pax_global_header00006660000000000000000000000064141331044300014503gustar00rootroot0000000000000052 comment=f8e8373ff7de9bdc1c5ba209e5d590f7ca06e1a4 gammaray-2.11.3/000077500000000000000000000000001413310443000133655ustar00rootroot00000000000000gammaray-2.11.3/.clang-tidy000066400000000000000000000031331413310443000154210ustar00rootroot00000000000000--- Checks: 'clang-diagnostic-*,clang-analyzer-*,-clang-analyzer-alpha*,*,-google-*,-cppcoreguidelines-*,-readability-else-after-return,-readability-implicit-bool-cast,-llvm-include-order,-readability-named-parameter,-readabilty-namespace-comments,-llvm-namespace-comment,-clang-analyzer-alpha.core.CastToStruct,-modernize-use-override,-modernize-use-bool-literals,-clang-analyzer-cplusplus.NewDeleteLeaks,-clang-analyzer-alpha.deadcode.UnreachableCode,-modernize-use-default,-clang-analyzer-core.CallAndMessage,-modernize-pass-by-value,-clang-analyzer-core.NonNullParamChecker,-modernize-raw-string-literal,-modernize-use-using,-modernize-loop-convert,-modernize-use-emplace,-modernize-return-braced-init-list,-modernize-use-default-member-init,-modernize-use-equals-default,-fuchsia-*,-hicpp-*,-readability-implicit-bool-conversion' WarningsAsErrors: '' HeaderFilterRegex: '' AnalyzeTemporaryDtors: false CheckOptions: - key: cert-oop11-cpp.UseCERTSemantics value: '1' - key: modernize-loop-convert.MaxCopySize value: '16' - key: modernize-loop-convert.MinConfidence value: reasonable - key: modernize-loop-convert.NamingStyle value: CamelCase - key: modernize-pass-by-value.IncludeStyle value: llvm - key: modernize-replace-auto-ptr.IncludeStyle value: llvm - key: modernize-use-nullptr.NullMacros value: 'NULL' - key: readability-braces-around-statements.ShortStatementLines value: '3' ... gammaray-2.11.3/.clazy000066400000000000000000000001771413310443000145150ustar00rootroot00000000000000#do not report clazy results for these subdirs SKIP /3rdparty/ #clazy checks specification CHECKS level2,no-qenums,no-qgetenv gammaray-2.11.3/.codespellrc000066400000000000000000000002641413310443000156670ustar00rootroot00000000000000[codespell] skip = *.ts,./build-*,.git,./3rdparty/*,./cmake/ECM*,format.config.uncrustify.4_spaces interactive = 3 ignore-words-list = ba,statics,resetable,processinf,nnumber,trun gammaray-2.11.3/.emacs-dirvars000066400000000000000000000005241413310443000161270ustar00rootroot00000000000000;; -*- emacs-lisp -*- ;; ;; This file is processed by the dirvars emacs package. Each variable ;; setting below is performed when this dirvars file is loaded. ;; indent-tabs-mode: nil tab-width: 8 c-basic-offset: 2 evaluate: (c-set-offset 'innamespace '0) evaluate: (c-set-offset 'inline-open '0) kdab-qt-version: 4 magic-parens-mode: nil gammaray-2.11.3/.kateconfig000066400000000000000000000001431413310443000154760ustar00rootroot00000000000000// kate: space-indent on; indent-width 4; remove-trailing-space on; remove-trailing-space-save on; gammaray-2.11.3/.krazy000066400000000000000000000022401413310443000145240ustar00rootroot00000000000000CHECKSETS qt5,c++,foss #KDAB-specific checks EXTRA kdabcopyright,kdabcommercial #additional checks #EXTRA defines,null #EXTRA style #exclude checks now being done by clazy or clang-tools EXCLUDE strings,explicit,normalize,passbyvalue,operators,nullstrcompare,nullstrassign,doublequote_chars,qobject,sigsandslots,staticobjects #exclude more checks EXCLUDE qminmax,captruefalse #coding style settings STYLE_CPPSTYLE qt STYLE_OFFSET 4 STYLE_LINEMAX 0 STYLE_CMAKESTYLE_OFFSET 2 #skip over known invalid desktop files SKIP /plugins/.*/.*\.desktop$ #skip generated cmake SKIP GammaRayConfig\.cmake\.in #skip generated qhcp SKIP .*\.qhcp\.cmake #no need to check 3rdparty stuff SKIP /3rdparty/ #nor Nokia's stuff SKIP /processlist_unix.cpp|/processlist_win.cpp|/processlist.h|/compat/qasconst.h #if you have a build subdir, skip it SKIP /build- SKIP Doxyfile.cmake #skip the borrowed code in the cmake subdir SKIP /cmake/Toolchain-QNX|/cmake/ECMQt4To5Porting.cmake|/cmake/ECMEnableSanitizers.cmake|/cmake/ECMGeneratePriFile.cmake|/cmake/ECMQueryQmake.cmake|/cmake/Toolchain-android.cmake|/cmake/FindWayland.cmake|/cmake/ECMFindModuleHelpers.cmake|/cmake/GammaRayQt4To5Porting.cmake gammaray-2.11.3/.qmake.conf000066400000000000000000000000301413310443000154010ustar00rootroot00000000000000MODULE_VERSION = 2.11.3 gammaray-2.11.3/.tag000066400000000000000000000000511413310443000141350ustar00rootroot00000000000000f8e8373ff7de9bdc1c5ba209e5d590f7ca06e1a4 gammaray-2.11.3/3rdparty/000077500000000000000000000000001413310443000151355ustar00rootroot00000000000000gammaray-2.11.3/3rdparty/StackWalker/000077500000000000000000000000001413310443000173505ustar00rootroot00000000000000gammaray-2.11.3/3rdparty/StackWalker/LICENSE000066400000000000000000000024531413310443000203610ustar00rootroot00000000000000BSD-2-Clause Copyright (c) 2005 - 2019, Jochen Kalmbach All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.gammaray-2.11.3/3rdparty/StackWalker/StackWalker.cpp000066400000000000000000001477131413310443000223040ustar00rootroot00000000000000/********************************************************************** * * StackWalker.cpp * https://github.com/JochenKalmbach/StackWalker * * Old location: http://stackwalker.codeplex.com/ * * * History: * 2005-07-27 v1 - First public release on http://www.codeproject.com/ * http://www.codeproject.com/threads/StackWalker.asp * 2005-07-28 v2 - Changed the params of the constructor and ShowCallstack * (to simplify the usage) * 2005-08-01 v3 - Changed to use 'CONTEXT_FULL' instead of CONTEXT_ALL * (should also be enough) * - Changed to compile correctly with the PSDK of VC7.0 * (GetFileVersionInfoSizeA and GetFileVersionInfoA is wrongly defined: * it uses LPSTR instead of LPCSTR as first parameter) * - Added declarations to support VC5/6 without using 'dbghelp.h' * - Added a 'pUserData' member to the ShowCallstack function and the * PReadProcessMemoryRoutine declaration (to pass some user-defined data, * which can be used in the readMemoryFunction-callback) * 2005-08-02 v4 - OnSymInit now also outputs the OS-Version by default * - Added example for doing an exception-callstack-walking in main.cpp * (thanks to owillebo: http://www.codeproject.com/script/profile/whos_who.asp?id=536268) * 2005-08-05 v5 - Removed most Lint (http://www.gimpel.com/) errors... thanks to Okko Willeboordse! * 2008-08-04 v6 - Fixed Bug: Missing LEAK-end-tag * http://www.codeproject.com/KB/applications/leakfinder.aspx?msg=2502890#xx2502890xx * Fixed Bug: Compiled with "WIN32_LEAN_AND_MEAN" * http://www.codeproject.com/KB/applications/leakfinder.aspx?msg=1824718#xx1824718xx * Fixed Bug: Compiling with "/Wall" * http://www.codeproject.com/KB/threads/StackWalker.aspx?msg=2638243#xx2638243xx * Fixed Bug: Now checking SymUseSymSrv * http://www.codeproject.com/KB/threads/StackWalker.aspx?msg=1388979#xx1388979xx * Fixed Bug: Support for recursive function calls * http://www.codeproject.com/KB/threads/StackWalker.aspx?msg=1434538#xx1434538xx * Fixed Bug: Missing FreeLibrary call in "GetModuleListTH32" * http://www.codeproject.com/KB/threads/StackWalker.aspx?msg=1326923#xx1326923xx * Fixed Bug: SymDia is number 7, not 9! * 2008-09-11 v7 For some (undocumented) reason, dbhelp.h is needing a packing of 8! * Thanks to Teajay which reported the bug... * http://www.codeproject.com/KB/applications/leakfinder.aspx?msg=2718933#xx2718933xx * 2008-11-27 v8 Debugging Tools for Windows are now stored in a different directory * Thanks to Luiz Salamon which reported this "bug"... * http://www.codeproject.com/KB/threads/StackWalker.aspx?msg=2822736#xx2822736xx * 2009-04-10 v9 License slightly corrected ( replaced) * 2009-11-01 v10 Moved to http://stackwalker.codeplex.com/ * 2009-11-02 v11 Now try to use IMAGEHLP_MODULE64_V3 if available * 2010-04-15 v12 Added support for VS2010 RTM * 2010-05-25 v13 Now using secure MyStrcCpy. Thanks to luke.simon: * http://www.codeproject.com/KB/applications/leakfinder.aspx?msg=3477467#xx3477467xx * 2013-01-07 v14 Runtime Check Error VS2010 Debug Builds fixed: * http://stackwalker.codeplex.com/workitem/10511 * * * LICENSE (http://www.opensource.org/licenses/bsd-license.php) * * Copyright (c) 2005-2013, Jochen Kalmbach * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * Neither the name of Jochen Kalmbach nor the names of its contributors may be * used to endorse or promote products derived from this software without * specific prior written permission. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * **********************************************************************/ #include "StackWalker.h" #include #include #include #include #pragma comment(lib, "version.lib") // for "VerQueryValue" #pragma warning(disable : 4826) // If VC7 and later, then use the shipped 'dbghelp.h'-file #pragma pack(push, 8) #if _MSC_VER >= 1300 #include #else // inline the important dbghelp.h-declarations... typedef enum { SymNone = 0, SymCoff, SymCv, SymPdb, SymExport, SymDeferred, SymSym, SymDia, SymVirtual, NumSymTypes } SYM_TYPE; typedef struct _IMAGEHLP_LINE64 { DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_LINE64) PVOID Key; // internal DWORD LineNumber; // line number in file PCHAR FileName; // full filename DWORD64 Address; // first instruction of line } IMAGEHLP_LINE64, *PIMAGEHLP_LINE64; typedef struct _IMAGEHLP_MODULE64 { DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_MODULE64) DWORD64 BaseOfImage; // base load address of module DWORD ImageSize; // virtual size of the loaded module DWORD TimeDateStamp; // date/time stamp from pe header DWORD CheckSum; // checksum from the pe header DWORD NumSyms; // number of symbols in the symbol table SYM_TYPE SymType; // type of symbols loaded CHAR ModuleName[32]; // module name CHAR ImageName[256]; // image name CHAR LoadedImageName[256]; // symbol file name } IMAGEHLP_MODULE64, *PIMAGEHLP_MODULE64; typedef struct _IMAGEHLP_SYMBOL64 { DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_SYMBOL64) DWORD64 Address; // virtual address including dll base address DWORD Size; // estimated size of symbol, can be zero DWORD Flags; // info about the symbols, see the SYMF defines DWORD MaxNameLength; // maximum size of symbol name in 'Name' CHAR Name[1]; // symbol name (null terminated string) } IMAGEHLP_SYMBOL64, *PIMAGEHLP_SYMBOL64; typedef enum { AddrMode1616, AddrMode1632, AddrModeReal, AddrModeFlat } ADDRESS_MODE; typedef struct _tagADDRESS64 { DWORD64 Offset; WORD Segment; ADDRESS_MODE Mode; } ADDRESS64, *LPADDRESS64; typedef struct _KDHELP64 { DWORD64 Thread; DWORD ThCallbackStack; DWORD ThCallbackBStore; DWORD NextCallback; DWORD FramePointer; DWORD64 KiCallUserMode; DWORD64 KeUserCallbackDispatcher; DWORD64 SystemRangeStart; DWORD64 Reserved[8]; } KDHELP64, *PKDHELP64; typedef struct _tagSTACKFRAME64 { ADDRESS64 AddrPC; // program counter ADDRESS64 AddrReturn; // return address ADDRESS64 AddrFrame; // frame pointer ADDRESS64 AddrStack; // stack pointer ADDRESS64 AddrBStore; // backing store pointer PVOID FuncTableEntry; // pointer to pdata/fpo or NULL DWORD64 Params[4]; // possible arguments to the function BOOL Far; // WOW far call BOOL Virtual; // is this a virtual frame? DWORD64 Reserved[3]; KDHELP64 KdHelp; } STACKFRAME64, *LPSTACKFRAME64; typedef BOOL(__stdcall* PREAD_PROCESS_MEMORY_ROUTINE64)(HANDLE hProcess, DWORD64 qwBaseAddress, PVOID lpBuffer, DWORD nSize, LPDWORD lpNumberOfBytesRead); typedef PVOID(__stdcall* PFUNCTION_TABLE_ACCESS_ROUTINE64)(HANDLE hProcess, DWORD64 AddrBase); typedef DWORD64(__stdcall* PGET_MODULE_BASE_ROUTINE64)(HANDLE hProcess, DWORD64 Address); typedef DWORD64(__stdcall* PTRANSLATE_ADDRESS_ROUTINE64)(HANDLE hProcess, HANDLE hThread, LPADDRESS64 lpaddr); // clang-format off #define SYMOPT_CASE_INSENSITIVE 0x00000001 #define SYMOPT_UNDNAME 0x00000002 #define SYMOPT_DEFERRED_LOADS 0x00000004 #define SYMOPT_NO_CPP 0x00000008 #define SYMOPT_LOAD_LINES 0x00000010 #define SYMOPT_OMAP_FIND_NEAREST 0x00000020 #define SYMOPT_LOAD_ANYTHING 0x00000040 #define SYMOPT_IGNORE_CVREC 0x00000080 #define SYMOPT_NO_UNQUALIFIED_LOADS 0x00000100 #define SYMOPT_FAIL_CRITICAL_ERRORS 0x00000200 #define SYMOPT_EXACT_SYMBOLS 0x00000400 #define SYMOPT_ALLOW_ABSOLUTE_SYMBOLS 0x00000800 #define SYMOPT_IGNORE_NT_SYMPATH 0x00001000 #define SYMOPT_INCLUDE_32BIT_MODULES 0x00002000 #define SYMOPT_PUBLICS_ONLY 0x00004000 #define SYMOPT_NO_PUBLICS 0x00008000 #define SYMOPT_AUTO_PUBLICS 0x00010000 #define SYMOPT_NO_IMAGE_SEARCH 0x00020000 #define SYMOPT_SECURE 0x00040000 #define SYMOPT_DEBUG 0x80000000 #define UNDNAME_COMPLETE (0x0000) // Enable full undecoration #define UNDNAME_NAME_ONLY (0x1000) // Crack only the name for primary declaration; // clang-format on #endif // _MSC_VER < 1300 #pragma pack(pop) // Some missing defines (for VC5/6): #ifndef INVALID_FILE_ATTRIBUTES #define INVALID_FILE_ATTRIBUTES ((DWORD)-1) #endif // secure-CRT_functions are only available starting with VC8 #if _MSC_VER < 1400 #define strcpy_s(dst, len, src) strcpy(dst, src) #define strncpy_s(dst, len, src, maxLen) strncpy(dst, len, src) #define strcat_s(dst, len, src) strcat(dst, src) #define _snprintf_s _snprintf #define _tcscat_s _tcscat #endif static void MyStrCpy(char* szDest, size_t nMaxDestSize, const char* szSrc) { if (nMaxDestSize <= 0) return; strncpy_s(szDest, nMaxDestSize, szSrc, _TRUNCATE); // INFO: _TRUNCATE will ensure that it is null-terminated; // but with older compilers (<1400) it uses "strncpy" and this does not!) szDest[nMaxDestSize - 1] = 0; } // MyStrCpy // Normally it should be enough to use 'CONTEXT_FULL' (better would be 'CONTEXT_ALL') #define USED_CONTEXT_FLAGS CONTEXT_FULL class StackWalkerInternal { public: StackWalkerInternal(StackWalker* parent, HANDLE hProcess) { m_parent = parent; m_hDbhHelp = NULL; pSC = NULL; m_hProcess = hProcess; m_szSymPath = NULL; pSFTA = NULL; pSGLFA = NULL; pSGMB = NULL; pSGMI = NULL; pSGO = NULL; pSGSFA = NULL; pSI = NULL; pSLM = NULL; pSSO = NULL; pSW = NULL; pUDSN = NULL; pSGSP = NULL; } ~StackWalkerInternal() { if (pSC != NULL) pSC(m_hProcess); // SymCleanup if (m_hDbhHelp != NULL) FreeLibrary(m_hDbhHelp); m_hDbhHelp = NULL; m_parent = NULL; if (m_szSymPath != NULL) free(m_szSymPath); m_szSymPath = NULL; } BOOL Init(LPCSTR szSymPath) { if (m_parent == NULL) return FALSE; // Dynamically load the Entry-Points for dbghelp.dll: // First try to load the newest one from TCHAR szTemp[4096]; // But before we do this, we first check if the ".local" file exists if (GetModuleFileName(NULL, szTemp, 4096) > 0) { _tcscat_s(szTemp, _T(".local")); if (GetFileAttributes(szTemp) == INVALID_FILE_ATTRIBUTES) { // ".local" file does not exist, so we can try to load the dbghelp.dll from the "Debugging Tools for Windows" // Ok, first try the new path according to the architecture: #ifdef _M_IX86 if ((m_hDbhHelp == NULL) && (GetEnvironmentVariable(_T("ProgramFiles"), szTemp, 4096) > 0)) { _tcscat_s(szTemp, _T("\\Debugging Tools for Windows (x86)\\dbghelp.dll")); // now check if the file exists: if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES) { m_hDbhHelp = LoadLibrary(szTemp); } } #elif _M_X64 if ((m_hDbhHelp == NULL) && (GetEnvironmentVariable(_T("ProgramFiles"), szTemp, 4096) > 0)) { _tcscat_s(szTemp, _T("\\Debugging Tools for Windows (x64)\\dbghelp.dll")); // now check if the file exists: if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES) { m_hDbhHelp = LoadLibrary(szTemp); } } #elif _M_IA64 if ((m_hDbhHelp == NULL) && (GetEnvironmentVariable(_T("ProgramFiles"), szTemp, 4096) > 0)) { _tcscat_s(szTemp, _T("\\Debugging Tools for Windows (ia64)\\dbghelp.dll")); // now check if the file exists: if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES) { m_hDbhHelp = LoadLibrary(szTemp); } } #endif // If still not found, try the old directories... if ((m_hDbhHelp == NULL) && (GetEnvironmentVariable(_T("ProgramFiles"), szTemp, 4096) > 0)) { _tcscat_s(szTemp, _T("\\Debugging Tools for Windows\\dbghelp.dll")); // now check if the file exists: if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES) { m_hDbhHelp = LoadLibrary(szTemp); } } #if defined _M_X64 || defined _M_IA64 // Still not found? Then try to load the (old) 64-Bit version: if ((m_hDbhHelp == NULL) && (GetEnvironmentVariable(_T("ProgramFiles"), szTemp, 4096) > 0)) { _tcscat_s(szTemp, _T("\\Debugging Tools for Windows 64-Bit\\dbghelp.dll")); if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES) { m_hDbhHelp = LoadLibrary(szTemp); } } #endif } } if (m_hDbhHelp == NULL) // if not already loaded, try to load a default-one m_hDbhHelp = LoadLibrary(_T("dbghelp.dll")); if (m_hDbhHelp == NULL) return FALSE; pSI = (tSI)GetProcAddress(m_hDbhHelp, "SymInitialize"); pSC = (tSC)GetProcAddress(m_hDbhHelp, "SymCleanup"); pSW = (tSW)GetProcAddress(m_hDbhHelp, "StackWalk64"); pSGO = (tSGO)GetProcAddress(m_hDbhHelp, "SymGetOptions"); pSSO = (tSSO)GetProcAddress(m_hDbhHelp, "SymSetOptions"); pSFTA = (tSFTA)GetProcAddress(m_hDbhHelp, "SymFunctionTableAccess64"); pSGLFA = (tSGLFA)GetProcAddress(m_hDbhHelp, "SymGetLineFromAddr64"); pSGMB = (tSGMB)GetProcAddress(m_hDbhHelp, "SymGetModuleBase64"); pSGMI = (tSGMI)GetProcAddress(m_hDbhHelp, "SymGetModuleInfo64"); pSGSFA = (tSGSFA)GetProcAddress(m_hDbhHelp, "SymGetSymFromAddr64"); pUDSN = (tUDSN)GetProcAddress(m_hDbhHelp, "UnDecorateSymbolName"); pSLM = (tSLM)GetProcAddress(m_hDbhHelp, "SymLoadModule64"); pSGSP = (tSGSP)GetProcAddress(m_hDbhHelp, "SymGetSearchPath"); if (pSC == NULL || pSFTA == NULL || pSGMB == NULL || pSGMI == NULL || pSGO == NULL || pSGSFA == NULL || pSI == NULL || pSSO == NULL || pSW == NULL || pUDSN == NULL || pSLM == NULL) { FreeLibrary(m_hDbhHelp); m_hDbhHelp = NULL; pSC = NULL; return FALSE; } // SymInitialize if (szSymPath != NULL) m_szSymPath = _strdup(szSymPath); if (this->pSI(m_hProcess, m_szSymPath, FALSE) == FALSE) this->m_parent->OnDbgHelpErr("SymInitialize", GetLastError(), 0); DWORD symOptions = this->pSGO(); // SymGetOptions symOptions |= SYMOPT_LOAD_LINES; symOptions |= SYMOPT_FAIL_CRITICAL_ERRORS; //symOptions |= SYMOPT_NO_PROMPTS; // SymSetOptions symOptions = this->pSSO(symOptions); char buf[StackWalker::STACKWALK_MAX_NAMELEN] = {0}; if (this->pSGSP != NULL) { if (this->pSGSP(m_hProcess, buf, StackWalker::STACKWALK_MAX_NAMELEN) == FALSE) this->m_parent->OnDbgHelpErr("SymGetSearchPath", GetLastError(), 0); } char szUserName[1024] = {0}; DWORD dwSize = 1024; GetUserNameA(szUserName, &dwSize); this->m_parent->OnSymInit(buf, symOptions, szUserName); return TRUE; } StackWalker* m_parent; HMODULE m_hDbhHelp; HANDLE m_hProcess; LPSTR m_szSymPath; #pragma pack(push, 8) typedef struct IMAGEHLP_MODULE64_V3 { DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_MODULE64) DWORD64 BaseOfImage; // base load address of module DWORD ImageSize; // virtual size of the loaded module DWORD TimeDateStamp; // date/time stamp from pe header DWORD CheckSum; // checksum from the pe header DWORD NumSyms; // number of symbols in the symbol table SYM_TYPE SymType; // type of symbols loaded CHAR ModuleName[32]; // module name CHAR ImageName[256]; // image name CHAR LoadedImageName[256]; // symbol file name // new elements: 07-Jun-2002 CHAR LoadedPdbName[256]; // pdb file name DWORD CVSig; // Signature of the CV record in the debug directories CHAR CVData[MAX_PATH * 3]; // Contents of the CV record DWORD PdbSig; // Signature of PDB GUID PdbSig70; // Signature of PDB (VC 7 and up) DWORD PdbAge; // DBI age of pdb BOOL PdbUnmatched; // loaded an unmatched pdb BOOL DbgUnmatched; // loaded an unmatched dbg BOOL LineNumbers; // we have line number information BOOL GlobalSymbols; // we have internal symbol information BOOL TypeInfo; // we have type information // new elements: 17-Dec-2003 BOOL SourceIndexed; // pdb supports source server BOOL Publics; // contains public symbols }; typedef struct IMAGEHLP_MODULE64_V2 { DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_MODULE64) DWORD64 BaseOfImage; // base load address of module DWORD ImageSize; // virtual size of the loaded module DWORD TimeDateStamp; // date/time stamp from pe header DWORD CheckSum; // checksum from the pe header DWORD NumSyms; // number of symbols in the symbol table SYM_TYPE SymType; // type of symbols loaded CHAR ModuleName[32]; // module name CHAR ImageName[256]; // image name CHAR LoadedImageName[256]; // symbol file name }; #pragma pack(pop) // SymCleanup() typedef BOOL(__stdcall* tSC)(IN HANDLE hProcess); tSC pSC; // SymFunctionTableAccess64() typedef PVOID(__stdcall* tSFTA)(HANDLE hProcess, DWORD64 AddrBase); tSFTA pSFTA; // SymGetLineFromAddr64() typedef BOOL(__stdcall* tSGLFA)(IN HANDLE hProcess, IN DWORD64 dwAddr, OUT PDWORD pdwDisplacement, OUT PIMAGEHLP_LINE64 Line); tSGLFA pSGLFA; // SymGetModuleBase64() typedef DWORD64(__stdcall* tSGMB)(IN HANDLE hProcess, IN DWORD64 dwAddr); tSGMB pSGMB; // SymGetModuleInfo64() typedef BOOL(__stdcall* tSGMI)(IN HANDLE hProcess, IN DWORD64 dwAddr, OUT IMAGEHLP_MODULE64_V3* ModuleInfo); tSGMI pSGMI; // SymGetOptions() typedef DWORD(__stdcall* tSGO)(VOID); tSGO pSGO; // SymGetSymFromAddr64() typedef BOOL(__stdcall* tSGSFA)(IN HANDLE hProcess, IN DWORD64 dwAddr, OUT PDWORD64 pdwDisplacement, OUT PIMAGEHLP_SYMBOL64 Symbol); tSGSFA pSGSFA; // SymInitialize() typedef BOOL(__stdcall* tSI)(IN HANDLE hProcess, IN PSTR UserSearchPath, IN BOOL fInvadeProcess); tSI pSI; // SymLoadModule64() typedef DWORD64(__stdcall* tSLM)(IN HANDLE hProcess, IN HANDLE hFile, IN PSTR ImageName, IN PSTR ModuleName, IN DWORD64 BaseOfDll, IN DWORD SizeOfDll); tSLM pSLM; // SymSetOptions() typedef DWORD(__stdcall* tSSO)(IN DWORD SymOptions); tSSO pSSO; // StackWalk64() typedef BOOL(__stdcall* tSW)(DWORD MachineType, HANDLE hProcess, HANDLE hThread, LPSTACKFRAME64 StackFrame, PVOID ContextRecord, PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine, PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine, PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine, PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress); tSW pSW; // UnDecorateSymbolName() typedef DWORD(__stdcall WINAPI* tUDSN)(PCSTR DecoratedName, PSTR UnDecoratedName, DWORD UndecoratedLength, DWORD Flags); tUDSN pUDSN; typedef BOOL(__stdcall WINAPI* tSGSP)(HANDLE hProcess, PSTR SearchPath, DWORD SearchPathLength); tSGSP pSGSP; private: // **************************************** ToolHelp32 ************************ #define MAX_MODULE_NAME32 255 #define TH32CS_SNAPMODULE 0x00000008 #pragma pack(push, 8) typedef struct tagMODULEENTRY32 { DWORD dwSize; DWORD th32ModuleID; // This module DWORD th32ProcessID; // owning process DWORD GlblcntUsage; // Global usage count on the module DWORD ProccntUsage; // Module usage count in th32ProcessID's context BYTE* modBaseAddr; // Base address of module in th32ProcessID's context DWORD modBaseSize; // Size in bytes of module starting at modBaseAddr HMODULE hModule; // The hModule of this module in th32ProcessID's context char szModule[MAX_MODULE_NAME32 + 1]; char szExePath[MAX_PATH]; } MODULEENTRY32; typedef MODULEENTRY32* PMODULEENTRY32; typedef MODULEENTRY32* LPMODULEENTRY32; #pragma pack(pop) BOOL GetModuleListTH32(HANDLE hProcess, DWORD pid) { // CreateToolhelp32Snapshot() typedef HANDLE(__stdcall * tCT32S)(DWORD dwFlags, DWORD th32ProcessID); // Module32First() typedef BOOL(__stdcall * tM32F)(HANDLE hSnapshot, LPMODULEENTRY32 lpme); // Module32Next() typedef BOOL(__stdcall * tM32N)(HANDLE hSnapshot, LPMODULEENTRY32 lpme); // try both dlls... const TCHAR* dllname[] = {_T("kernel32.dll"), _T("tlhelp32.dll")}; HINSTANCE hToolhelp = NULL; tCT32S pCT32S = NULL; tM32F pM32F = NULL; tM32N pM32N = NULL; HANDLE hSnap; MODULEENTRY32 me; me.dwSize = sizeof(me); BOOL keepGoing; size_t i; for (i = 0; i < (sizeof(dllname) / sizeof(dllname[0])); i++) { hToolhelp = LoadLibrary(dllname[i]); if (hToolhelp == NULL) continue; pCT32S = (tCT32S)GetProcAddress(hToolhelp, "CreateToolhelp32Snapshot"); pM32F = (tM32F)GetProcAddress(hToolhelp, "Module32First"); pM32N = (tM32N)GetProcAddress(hToolhelp, "Module32Next"); if ((pCT32S != NULL) && (pM32F != NULL) && (pM32N != NULL)) break; // found the functions! FreeLibrary(hToolhelp); hToolhelp = NULL; } if (hToolhelp == NULL) return FALSE; hSnap = pCT32S(TH32CS_SNAPMODULE, pid); if (hSnap == (HANDLE)-1) { FreeLibrary(hToolhelp); return FALSE; } keepGoing = !!pM32F(hSnap, &me); int cnt = 0; while (keepGoing) { this->LoadModule(hProcess, me.szExePath, me.szModule, (DWORD64)me.modBaseAddr, me.modBaseSize); cnt++; keepGoing = !!pM32N(hSnap, &me); } CloseHandle(hSnap); FreeLibrary(hToolhelp); if (cnt <= 0) return FALSE; return TRUE; } // GetModuleListTH32 // **************************************** PSAPI ************************ typedef struct _MODULEINFO { LPVOID lpBaseOfDll; DWORD SizeOfImage; LPVOID EntryPoint; } MODULEINFO, *LPMODULEINFO; BOOL GetModuleListPSAPI(HANDLE hProcess) { // EnumProcessModules() typedef BOOL(__stdcall * tEPM)(HANDLE hProcess, HMODULE * lphModule, DWORD cb, LPDWORD lpcbNeeded); // GetModuleFileNameEx() typedef DWORD(__stdcall * tGMFNE)(HANDLE hProcess, HMODULE hModule, LPSTR lpFilename, DWORD nSize); // GetModuleBaseName() typedef DWORD(__stdcall * tGMBN)(HANDLE hProcess, HMODULE hModule, LPSTR lpFilename, DWORD nSize); // GetModuleInformation() typedef BOOL(__stdcall * tGMI)(HANDLE hProcess, HMODULE hModule, LPMODULEINFO pmi, DWORD nSize); HINSTANCE hPsapi; tEPM pEPM; tGMFNE pGMFNE; tGMBN pGMBN; tGMI pGMI; DWORD i; //ModuleEntry e; DWORD cbNeeded; MODULEINFO mi; HMODULE* hMods = NULL; char* tt = NULL; char* tt2 = NULL; const SIZE_T TTBUFLEN = 8096; int cnt = 0; hPsapi = LoadLibrary(_T("psapi.dll")); if (hPsapi == NULL) return FALSE; pEPM = (tEPM)GetProcAddress(hPsapi, "EnumProcessModules"); pGMFNE = (tGMFNE)GetProcAddress(hPsapi, "GetModuleFileNameExA"); pGMBN = (tGMFNE)GetProcAddress(hPsapi, "GetModuleBaseNameA"); pGMI = (tGMI)GetProcAddress(hPsapi, "GetModuleInformation"); if ((pEPM == NULL) || (pGMFNE == NULL) || (pGMBN == NULL) || (pGMI == NULL)) { // we couldn't find all functions FreeLibrary(hPsapi); return FALSE; } hMods = (HMODULE*)malloc(sizeof(HMODULE) * (TTBUFLEN / sizeof(HMODULE))); tt = (char*)malloc(sizeof(char) * TTBUFLEN); tt2 = (char*)malloc(sizeof(char) * TTBUFLEN); if ((hMods == NULL) || (tt == NULL) || (tt2 == NULL)) goto cleanup; if (!pEPM(hProcess, hMods, TTBUFLEN, &cbNeeded)) { //_ftprintf(fLogFile, _T("%lu: EPM failed, GetLastError = %lu\n"), g_dwShowCount, gle ); goto cleanup; } if (cbNeeded > TTBUFLEN) { //_ftprintf(fLogFile, _T("%lu: More than %lu module handles. Huh?\n"), g_dwShowCount, lenof( hMods ) ); goto cleanup; } for (i = 0; i < cbNeeded / sizeof(hMods[0]); i++) { // base address, size pGMI(hProcess, hMods[i], &mi, sizeof(mi)); // image file name tt[0] = 0; pGMFNE(hProcess, hMods[i], tt, TTBUFLEN); // module name tt2[0] = 0; pGMBN(hProcess, hMods[i], tt2, TTBUFLEN); DWORD dwRes = this->LoadModule(hProcess, tt, tt2, (DWORD64)mi.lpBaseOfDll, mi.SizeOfImage); if (dwRes != ERROR_SUCCESS) this->m_parent->OnDbgHelpErr("LoadModule", dwRes, 0); cnt++; } cleanup: if (hPsapi != NULL) FreeLibrary(hPsapi); if (tt2 != NULL) free(tt2); if (tt != NULL) free(tt); if (hMods != NULL) free(hMods); return cnt != 0; } // GetModuleListPSAPI DWORD LoadModule(HANDLE hProcess, LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size) { CHAR* szImg = _strdup(img); CHAR* szMod = _strdup(mod); DWORD result = ERROR_SUCCESS; if ((szImg == NULL) || (szMod == NULL)) result = ERROR_NOT_ENOUGH_MEMORY; else { if (pSLM(hProcess, 0, szImg, szMod, baseAddr, size) == 0) result = GetLastError(); } ULONGLONG fileVersion = 0; if ((m_parent != NULL) && (szImg != NULL)) { // try to retrieve the file-version: if ((this->m_parent->m_options & StackWalker::RetrieveFileVersion) != 0) { VS_FIXEDFILEINFO* fInfo = NULL; DWORD dwHandle; DWORD dwSize = GetFileVersionInfoSizeA(szImg, &dwHandle); if (dwSize > 0) { LPVOID vData = malloc(dwSize); if (vData != NULL) { if (GetFileVersionInfoA(szImg, dwHandle, dwSize, vData) != 0) { UINT len; TCHAR szSubBlock[] = _T("\\"); if (VerQueryValue(vData, szSubBlock, (LPVOID*)&fInfo, &len) == 0) fInfo = NULL; else { fileVersion = ((ULONGLONG)fInfo->dwFileVersionLS) + ((ULONGLONG)fInfo->dwFileVersionMS << 32); } } free(vData); } } } // Retrieve some additional-infos about the module IMAGEHLP_MODULE64_V3 Module; const char* szSymType = "-unknown-"; if (this->GetModuleInfo(hProcess, baseAddr, &Module) != FALSE) { switch (Module.SymType) { case SymNone: szSymType = "-nosymbols-"; break; case SymCoff: // 1 szSymType = "COFF"; break; case SymCv: // 2 szSymType = "CV"; break; case SymPdb: // 3 szSymType = "PDB"; break; case SymExport: // 4 szSymType = "-exported-"; break; case SymDeferred: // 5 szSymType = "-deferred-"; break; case SymSym: // 6 szSymType = "SYM"; break; case 7: // SymDia: szSymType = "DIA"; break; case 8: //SymVirtual: szSymType = "Virtual"; break; } } LPCSTR pdbName = Module.LoadedImageName; if (Module.LoadedPdbName[0] != 0) pdbName = Module.LoadedPdbName; this->m_parent->OnLoadModule(img, mod, baseAddr, size, result, szSymType, pdbName, fileVersion); } if (szImg != NULL) free(szImg); if (szMod != NULL) free(szMod); return result; } public: BOOL LoadModules(HANDLE hProcess, DWORD dwProcessId) { // first try toolhelp32 if (GetModuleListTH32(hProcess, dwProcessId)) return true; // then try psapi return GetModuleListPSAPI(hProcess); } BOOL GetModuleInfo(HANDLE hProcess, DWORD64 baseAddr, IMAGEHLP_MODULE64_V3* pModuleInfo) { memset(pModuleInfo, 0, sizeof(IMAGEHLP_MODULE64_V3)); if (this->pSGMI == NULL) { SetLastError(ERROR_DLL_INIT_FAILED); return FALSE; } // First try to use the larger ModuleInfo-Structure pModuleInfo->SizeOfStruct = sizeof(IMAGEHLP_MODULE64_V3); void* pData = malloc( 4096); // reserve enough memory, so the bug in v6.3.5.1 does not lead to memory-overwrites... if (pData == NULL) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); return FALSE; } memcpy(pData, pModuleInfo, sizeof(IMAGEHLP_MODULE64_V3)); static bool s_useV3Version = true; if (s_useV3Version) { if (this->pSGMI(hProcess, baseAddr, (IMAGEHLP_MODULE64_V3*)pData) != FALSE) { // only copy as much memory as is reserved... memcpy(pModuleInfo, pData, sizeof(IMAGEHLP_MODULE64_V3)); pModuleInfo->SizeOfStruct = sizeof(IMAGEHLP_MODULE64_V3); free(pData); return TRUE; } s_useV3Version = false; // to prevent unnecessary calls with the larger struct... } // could not retrieve the bigger structure, try with the smaller one (as defined in VC7.1)... pModuleInfo->SizeOfStruct = sizeof(IMAGEHLP_MODULE64_V2); memcpy(pData, pModuleInfo, sizeof(IMAGEHLP_MODULE64_V2)); if (this->pSGMI(hProcess, baseAddr, (IMAGEHLP_MODULE64_V3*)pData) != FALSE) { // only copy as much memory as is reserved... memcpy(pModuleInfo, pData, sizeof(IMAGEHLP_MODULE64_V2)); pModuleInfo->SizeOfStruct = sizeof(IMAGEHLP_MODULE64_V2); free(pData); return TRUE; } free(pData); SetLastError(ERROR_DLL_INIT_FAILED); return FALSE; } }; // ############################################################# StackWalker::StackWalker(DWORD dwProcessId, HANDLE hProcess) { this->m_options = OptionsAll; this->m_modulesLoaded = FALSE; this->m_hProcess = hProcess; this->m_sw = new StackWalkerInternal(this, this->m_hProcess); this->m_dwProcessId = dwProcessId; this->m_szSymPath = NULL; this->m_MaxRecursionCount = 1000; } StackWalker::StackWalker(int options, LPCSTR szSymPath, DWORD dwProcessId, HANDLE hProcess) { this->m_options = options; this->m_modulesLoaded = FALSE; this->m_hProcess = hProcess; this->m_sw = new StackWalkerInternal(this, this->m_hProcess); this->m_dwProcessId = dwProcessId; if (szSymPath != NULL) { this->m_szSymPath = _strdup(szSymPath); this->m_options |= SymBuildPath; } else this->m_szSymPath = NULL; this->m_MaxRecursionCount = 1000; } StackWalker::~StackWalker() { if (m_szSymPath != NULL) free(m_szSymPath); m_szSymPath = NULL; if (this->m_sw != NULL) delete this->m_sw; this->m_sw = NULL; } BOOL StackWalker::LoadModules() { if (this->m_sw == NULL) { SetLastError(ERROR_DLL_INIT_FAILED); return FALSE; } if (m_modulesLoaded != FALSE) return TRUE; // Build the sym-path: char* szSymPath = NULL; if ((this->m_options & SymBuildPath) != 0) { const size_t nSymPathLen = 4096; szSymPath = (char*)malloc(nSymPathLen); if (szSymPath == NULL) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); return FALSE; } szSymPath[0] = 0; // Now first add the (optional) provided sympath: if (this->m_szSymPath != NULL) { strcat_s(szSymPath, nSymPathLen, this->m_szSymPath); strcat_s(szSymPath, nSymPathLen, ";"); } strcat_s(szSymPath, nSymPathLen, ".;"); const size_t nTempLen = 1024; char szTemp[nTempLen]; // Now add the current directory: if (GetCurrentDirectoryA(nTempLen, szTemp) > 0) { szTemp[nTempLen - 1] = 0; strcat_s(szSymPath, nSymPathLen, szTemp); strcat_s(szSymPath, nSymPathLen, ";"); } // Now add the path for the main-module: if (GetModuleFileNameA(NULL, szTemp, nTempLen) > 0) { szTemp[nTempLen - 1] = 0; for (char* p = (szTemp + strlen(szTemp) - 1); p >= szTemp; --p) { // locate the rightmost path separator if ((*p == '\\') || (*p == '/') || (*p == ':')) { *p = 0; break; } } // for (search for path separator...) if (strlen(szTemp) > 0) { strcat_s(szSymPath, nSymPathLen, szTemp); strcat_s(szSymPath, nSymPathLen, ";"); } } if (GetEnvironmentVariableA("_NT_SYMBOL_PATH", szTemp, nTempLen) > 0) { szTemp[nTempLen - 1] = 0; strcat_s(szSymPath, nSymPathLen, szTemp); strcat_s(szSymPath, nSymPathLen, ";"); } if (GetEnvironmentVariableA("_NT_ALTERNATE_SYMBOL_PATH", szTemp, nTempLen) > 0) { szTemp[nTempLen - 1] = 0; strcat_s(szSymPath, nSymPathLen, szTemp); strcat_s(szSymPath, nSymPathLen, ";"); } if (GetEnvironmentVariableA("SYSTEMROOT", szTemp, nTempLen) > 0) { szTemp[nTempLen - 1] = 0; strcat_s(szSymPath, nSymPathLen, szTemp); strcat_s(szSymPath, nSymPathLen, ";"); // also add the "system32"-directory: strcat_s(szTemp, nTempLen, "\\system32"); strcat_s(szSymPath, nSymPathLen, szTemp); strcat_s(szSymPath, nSymPathLen, ";"); } if ((this->m_options & SymUseSymSrv) != 0) { if (GetEnvironmentVariableA("SYSTEMDRIVE", szTemp, nTempLen) > 0) { szTemp[nTempLen - 1] = 0; strcat_s(szSymPath, nSymPathLen, "SRV*"); strcat_s(szSymPath, nSymPathLen, szTemp); strcat_s(szSymPath, nSymPathLen, "\\websymbols"); strcat_s(szSymPath, nSymPathLen, "*https://msdl.microsoft.com/download/symbols;"); } else strcat_s(szSymPath, nSymPathLen, "SRV*c:\\websymbols*https://msdl.microsoft.com/download/symbols;"); } } // if SymBuildPath // First Init the whole stuff... BOOL bRet = this->m_sw->Init(szSymPath); if (szSymPath != NULL) free(szSymPath); szSymPath = NULL; if (bRet == FALSE) { this->OnDbgHelpErr("Error while initializing dbghelp.dll", 0, 0); SetLastError(ERROR_DLL_INIT_FAILED); return FALSE; } bRet = this->m_sw->LoadModules(this->m_hProcess, this->m_dwProcessId); if (bRet != FALSE) m_modulesLoaded = TRUE; return bRet; } // The following is used to pass the "userData"-Pointer to the user-provided readMemoryFunction // This has to be done due to a problem with the "hProcess"-parameter in x64... // Because this class is in no case multi-threading-enabled (because of the limitations // of dbghelp.dll) it is "safe" to use a static-variable static StackWalker::PReadProcessMemoryRoutine s_readMemoryFunction = NULL; static LPVOID s_readMemoryFunction_UserData = NULL; BOOL StackWalker::ShowCallstack(HANDLE hThread, const CONTEXT* context, PReadProcessMemoryRoutine readMemoryFunction, LPVOID pUserData) { CONTEXT c; CallstackEntry csEntry; IMAGEHLP_SYMBOL64* pSym = NULL; StackWalkerInternal::IMAGEHLP_MODULE64_V3 Module; IMAGEHLP_LINE64 Line; int frameNum; bool bLastEntryCalled = true; int curRecursionCount = 0; if (m_modulesLoaded == FALSE) this->LoadModules(); // ignore the result... if (this->m_sw->m_hDbhHelp == NULL) { SetLastError(ERROR_DLL_INIT_FAILED); return FALSE; } s_readMemoryFunction = readMemoryFunction; s_readMemoryFunction_UserData = pUserData; if (context == NULL) { // If no context is provided, capture the context // See: https://stackwalker.codeplex.com/discussions/446958 #if _WIN32_WINNT <= 0x0501 // If we need to support XP, we need to use the "old way", because "GetThreadId" is not available! if (hThread == GetCurrentThread()) #else if (GetThreadId(hThread) == GetCurrentThreadId()) #endif { GET_CURRENT_CONTEXT_STACKWALKER_CODEPLEX(c, USED_CONTEXT_FLAGS); } else { SuspendThread(hThread); memset(&c, 0, sizeof(CONTEXT)); c.ContextFlags = USED_CONTEXT_FLAGS; // TODO: Detect if you want to get a thread context of a different process, which is running a different processor architecture... // This does only work if we are x64 and the target process is x64 or x86; // It cannot work, if this process is x64 and the target process is x64... this is not supported... // See also: http://www.howzatt.demon.co.uk/articles/DebuggingInWin64.html if (GetThreadContext(hThread, &c) == FALSE) { ResumeThread(hThread); return FALSE; } } } else c = *context; // init STACKFRAME for first call STACKFRAME64 s; // in/out stackframe memset(&s, 0, sizeof(s)); DWORD imageType; #ifdef _M_IX86 // normally, call ImageNtHeader() and use machine info from PE header imageType = IMAGE_FILE_MACHINE_I386; s.AddrPC.Offset = c.Eip; s.AddrPC.Mode = AddrModeFlat; s.AddrFrame.Offset = c.Ebp; s.AddrFrame.Mode = AddrModeFlat; s.AddrStack.Offset = c.Esp; s.AddrStack.Mode = AddrModeFlat; #elif _M_X64 imageType = IMAGE_FILE_MACHINE_AMD64; s.AddrPC.Offset = c.Rip; s.AddrPC.Mode = AddrModeFlat; s.AddrFrame.Offset = c.Rsp; s.AddrFrame.Mode = AddrModeFlat; s.AddrStack.Offset = c.Rsp; s.AddrStack.Mode = AddrModeFlat; #elif _M_IA64 imageType = IMAGE_FILE_MACHINE_IA64; s.AddrPC.Offset = c.StIIP; s.AddrPC.Mode = AddrModeFlat; s.AddrFrame.Offset = c.IntSp; s.AddrFrame.Mode = AddrModeFlat; s.AddrBStore.Offset = c.RsBSP; s.AddrBStore.Mode = AddrModeFlat; s.AddrStack.Offset = c.IntSp; s.AddrStack.Mode = AddrModeFlat; #else #error "Platform not supported!" #endif pSym = (IMAGEHLP_SYMBOL64*)malloc(sizeof(IMAGEHLP_SYMBOL64) + STACKWALK_MAX_NAMELEN); if (!pSym) goto cleanup; // not enough memory... memset(pSym, 0, sizeof(IMAGEHLP_SYMBOL64) + STACKWALK_MAX_NAMELEN); pSym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64); pSym->MaxNameLength = STACKWALK_MAX_NAMELEN; memset(&Line, 0, sizeof(Line)); Line.SizeOfStruct = sizeof(Line); memset(&Module, 0, sizeof(Module)); Module.SizeOfStruct = sizeof(Module); for (frameNum = 0;; ++frameNum) { // get next stack frame (StackWalk64(), SymFunctionTableAccess64(), SymGetModuleBase64()) // if this returns ERROR_INVALID_ADDRESS (487) or ERROR_NOACCESS (998), you can // assume that either you are done, or that the stack is so hosed that the next // deeper frame could not be found. // CONTEXT need not to be supplied if imageTyp is IMAGE_FILE_MACHINE_I386! if (!this->m_sw->pSW(imageType, this->m_hProcess, hThread, &s, &c, myReadProcMem, this->m_sw->pSFTA, this->m_sw->pSGMB, NULL)) { // INFO: "StackWalk64" does not set "GetLastError"... this->OnDbgHelpErr("StackWalk64", 0, s.AddrPC.Offset); break; } csEntry.offset = s.AddrPC.Offset; csEntry.name[0] = 0; csEntry.undName[0] = 0; csEntry.undFullName[0] = 0; csEntry.offsetFromSmybol = 0; csEntry.offsetFromLine = 0; csEntry.lineFileName[0] = 0; csEntry.lineNumber = 0; csEntry.loadedImageName[0] = 0; csEntry.moduleName[0] = 0; if (s.AddrPC.Offset == s.AddrReturn.Offset) { if ((this->m_MaxRecursionCount > 0) && (curRecursionCount > m_MaxRecursionCount)) { this->OnDbgHelpErr("StackWalk64-Endless-Callstack!", 0, s.AddrPC.Offset); break; } curRecursionCount++; } else curRecursionCount = 0; if (s.AddrPC.Offset != 0) { // we seem to have a valid PC // show procedure info (SymGetSymFromAddr64()) if (this->m_sw->pSGSFA(this->m_hProcess, s.AddrPC.Offset, &(csEntry.offsetFromSmybol), pSym) != FALSE) { MyStrCpy(csEntry.name, STACKWALK_MAX_NAMELEN, pSym->Name); // UnDecorateSymbolName() this->m_sw->pUDSN(pSym->Name, csEntry.undName, STACKWALK_MAX_NAMELEN, UNDNAME_NAME_ONLY); this->m_sw->pUDSN(pSym->Name, csEntry.undFullName, STACKWALK_MAX_NAMELEN, UNDNAME_COMPLETE); } else { this->OnDbgHelpErr("SymGetSymFromAddr64", GetLastError(), s.AddrPC.Offset); } // show line number info, NT5.0-method (SymGetLineFromAddr64()) if (this->m_sw->pSGLFA != NULL) { // yes, we have SymGetLineFromAddr64() if (this->m_sw->pSGLFA(this->m_hProcess, s.AddrPC.Offset, &(csEntry.offsetFromLine), &Line) != FALSE) { csEntry.lineNumber = Line.LineNumber; MyStrCpy(csEntry.lineFileName, STACKWALK_MAX_NAMELEN, Line.FileName); } else { this->OnDbgHelpErr("SymGetLineFromAddr64", GetLastError(), s.AddrPC.Offset); } } // yes, we have SymGetLineFromAddr64() // show module info (SymGetModuleInfo64()) if (this->m_sw->GetModuleInfo(this->m_hProcess, s.AddrPC.Offset, &Module) != FALSE) { // got module info OK switch (Module.SymType) { case SymNone: csEntry.symTypeString = "-nosymbols-"; break; case SymCoff: csEntry.symTypeString = "COFF"; break; case SymCv: csEntry.symTypeString = "CV"; break; case SymPdb: csEntry.symTypeString = "PDB"; break; case SymExport: csEntry.symTypeString = "-exported-"; break; case SymDeferred: csEntry.symTypeString = "-deferred-"; break; case SymSym: csEntry.symTypeString = "SYM"; break; #if API_VERSION_NUMBER >= 9 case SymDia: csEntry.symTypeString = "DIA"; break; #endif case 8: //SymVirtual: csEntry.symTypeString = "Virtual"; break; default: //_snprintf( ty, sizeof(ty), "symtype=%ld", (long) Module.SymType ); csEntry.symTypeString = NULL; break; } MyStrCpy(csEntry.moduleName, STACKWALK_MAX_NAMELEN, Module.ModuleName); csEntry.baseOfImage = Module.BaseOfImage; MyStrCpy(csEntry.loadedImageName, STACKWALK_MAX_NAMELEN, Module.LoadedImageName); } // got module info OK else { this->OnDbgHelpErr("SymGetModuleInfo64", GetLastError(), s.AddrPC.Offset); } } // we seem to have a valid PC CallstackEntryType et = nextEntry; if (frameNum == 0) et = firstEntry; bLastEntryCalled = false; this->OnCallstackEntry(et, csEntry); if (s.AddrReturn.Offset == 0) { bLastEntryCalled = true; this->OnCallstackEntry(lastEntry, csEntry); SetLastError(ERROR_SUCCESS); break; } } // for ( frameNum ) cleanup: if (pSym) free(pSym); if (bLastEntryCalled == false) this->OnCallstackEntry(lastEntry, csEntry); if (context == NULL) ResumeThread(hThread); return TRUE; } BOOL StackWalker::ShowObject(LPVOID pObject) { // Load modules if not done yet if (m_modulesLoaded == FALSE) this->LoadModules(); // ignore the result... // Verify that the DebugHelp.dll was actually found if (this->m_sw->m_hDbhHelp == NULL) { SetLastError(ERROR_DLL_INIT_FAILED); return FALSE; } // SymGetSymFromAddr64() is required if (this->m_sw->pSGSFA == NULL) return FALSE; // Show object info (SymGetSymFromAddr64()) DWORD64 dwAddress = DWORD64(pObject); DWORD64 dwDisplacement = 0; const SIZE_T symSize = sizeof(IMAGEHLP_SYMBOL64) + STACKWALK_MAX_NAMELEN; IMAGEHLP_SYMBOL64* pSym = (IMAGEHLP_SYMBOL64*) malloc(symSize); if (!pSym) return FALSE; memset(pSym, 0, symSize); pSym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64); pSym->MaxNameLength = STACKWALK_MAX_NAMELEN; if (this->m_sw->pSGSFA(this->m_hProcess, dwAddress, &dwDisplacement, pSym) == FALSE) { this->OnDbgHelpErr("SymGetSymFromAddr64", GetLastError(), dwAddress); return FALSE; } // Object name output this->OnOutput(pSym->Name); free(pSym); return TRUE; }; BOOL __stdcall StackWalker::myReadProcMem(HANDLE hProcess, DWORD64 qwBaseAddress, PVOID lpBuffer, DWORD nSize, LPDWORD lpNumberOfBytesRead) { if (s_readMemoryFunction == NULL) { SIZE_T st; BOOL bRet = ReadProcessMemory(hProcess, (LPVOID)qwBaseAddress, lpBuffer, nSize, &st); *lpNumberOfBytesRead = (DWORD)st; //printf("ReadMemory: hProcess: %p, baseAddr: %p, buffer: %p, size: %d, read: %d, result: %d\n", hProcess, (LPVOID) qwBaseAddress, lpBuffer, nSize, (DWORD) st, (DWORD) bRet); return bRet; } else { return s_readMemoryFunction(hProcess, qwBaseAddress, lpBuffer, nSize, lpNumberOfBytesRead, s_readMemoryFunction_UserData); } } void StackWalker::OnLoadModule(LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size, DWORD result, LPCSTR symType, LPCSTR pdbName, ULONGLONG fileVersion) { CHAR buffer[STACKWALK_MAX_NAMELEN]; size_t maxLen = STACKWALK_MAX_NAMELEN; #if _MSC_VER >= 1400 maxLen = _TRUNCATE; #endif if (fileVersion == 0) _snprintf_s(buffer, maxLen, "%s:%s (%p), size: %d (result: %d), SymType: '%s', PDB: '%s'\n", img, mod, (LPVOID)baseAddr, size, result, symType, pdbName); else { DWORD v4 = (DWORD)(fileVersion & 0xFFFF); DWORD v3 = (DWORD)((fileVersion >> 16) & 0xFFFF); DWORD v2 = (DWORD)((fileVersion >> 32) & 0xFFFF); DWORD v1 = (DWORD)((fileVersion >> 48) & 0xFFFF); _snprintf_s( buffer, maxLen, "%s:%s (%p), size: %d (result: %d), SymType: '%s', PDB: '%s', fileVersion: %d.%d.%d.%d\n", img, mod, (LPVOID)baseAddr, size, result, symType, pdbName, v1, v2, v3, v4); } buffer[STACKWALK_MAX_NAMELEN - 1] = 0; // be sure it is NULL terminated OnOutput(buffer); } void StackWalker::OnCallstackEntry(CallstackEntryType eType, CallstackEntry& entry) { CHAR buffer[STACKWALK_MAX_NAMELEN]; size_t maxLen = STACKWALK_MAX_NAMELEN; #if _MSC_VER >= 1400 maxLen = _TRUNCATE; #endif if ((eType != lastEntry) && (entry.offset != 0)) { if (entry.name[0] == 0) MyStrCpy(entry.name, STACKWALK_MAX_NAMELEN, "(function-name not available)"); if (entry.undName[0] != 0) MyStrCpy(entry.name, STACKWALK_MAX_NAMELEN, entry.undName); if (entry.undFullName[0] != 0) MyStrCpy(entry.name, STACKWALK_MAX_NAMELEN, entry.undFullName); if (entry.lineFileName[0] == 0) { MyStrCpy(entry.lineFileName, STACKWALK_MAX_NAMELEN, "(filename not available)"); if (entry.moduleName[0] == 0) MyStrCpy(entry.moduleName, STACKWALK_MAX_NAMELEN, "(module-name not available)"); _snprintf_s(buffer, maxLen, "%p (%s): %s: %s\n", (LPVOID)entry.offset, entry.moduleName, entry.lineFileName, entry.name); } else _snprintf_s(buffer, maxLen, "%s (%d): %s\n", entry.lineFileName, entry.lineNumber, entry.name); buffer[STACKWALK_MAX_NAMELEN - 1] = 0; OnOutput(buffer); } } void StackWalker::OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr) { CHAR buffer[STACKWALK_MAX_NAMELEN]; size_t maxLen = STACKWALK_MAX_NAMELEN; #if _MSC_VER >= 1400 maxLen = _TRUNCATE; #endif _snprintf_s(buffer, maxLen, "ERROR: %s, GetLastError: %d (Address: %p)\n", szFuncName, gle, (LPVOID)addr); buffer[STACKWALK_MAX_NAMELEN - 1] = 0; OnOutput(buffer); } void StackWalker::OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName) { CHAR buffer[STACKWALK_MAX_NAMELEN]; size_t maxLen = STACKWALK_MAX_NAMELEN; #if _MSC_VER >= 1400 maxLen = _TRUNCATE; #endif _snprintf_s(buffer, maxLen, "SymInit: Symbol-SearchPath: '%s', symOptions: %d, UserName: '%s'\n", szSearchPath, symOptions, szUserName); buffer[STACKWALK_MAX_NAMELEN - 1] = 0; OnOutput(buffer); // Also display the OS-version #if _MSC_VER <= 1200 OSVERSIONINFOA ver; ZeroMemory(&ver, sizeof(OSVERSIONINFOA)); ver.dwOSVersionInfoSize = sizeof(ver); if (GetVersionExA(&ver) != FALSE) { _snprintf_s(buffer, maxLen, "OS-Version: %d.%d.%d (%s)\n", ver.dwMajorVersion, ver.dwMinorVersion, ver.dwBuildNumber, ver.szCSDVersion); buffer[STACKWALK_MAX_NAMELEN - 1] = 0; OnOutput(buffer); } #else OSVERSIONINFOEXA ver; ZeroMemory(&ver, sizeof(OSVERSIONINFOEXA)); ver.dwOSVersionInfoSize = sizeof(ver); #if _MSC_VER >= 1900 #pragma warning(push) #pragma warning(disable : 4996) #endif if (GetVersionExA((OSVERSIONINFOA*)&ver) != FALSE) { _snprintf_s(buffer, maxLen, "OS-Version: %d.%d.%d (%s) 0x%x-0x%x\n", ver.dwMajorVersion, ver.dwMinorVersion, ver.dwBuildNumber, ver.szCSDVersion, ver.wSuiteMask, ver.wProductType); buffer[STACKWALK_MAX_NAMELEN - 1] = 0; OnOutput(buffer); } #if _MSC_VER >= 1900 #pragma warning(pop) #endif #endif } void StackWalker::OnOutput(LPCSTR buffer) { OutputDebugStringA(buffer); } gammaray-2.11.3/3rdparty/StackWalker/StackWalker.h000066400000000000000000000232451413310443000217420ustar00rootroot00000000000000#ifndef __STACKWALKER_H__ #define __STACKWALKER_H__ #if defined(_MSC_VER) /********************************************************************** * * StackWalker.h * * * * LICENSE (http://www.opensource.org/licenses/bsd-license.php) * * Copyright (c) 2005-2009, Jochen Kalmbach * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * Neither the name of Jochen Kalmbach nor the names of its contributors may be * used to endorse or promote products derived from this software without * specific prior written permission. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * **********************************************************************/ // #pragma once is supported starting with _MSC_VER 1000, // so we need not to check the version (because we only support _MSC_VER >= 1100)! #pragma once #include #if _MSC_VER >= 1900 #pragma warning(disable : 4091) #endif // special defines for VC5/6 (if no actual PSDK is installed): #if _MSC_VER < 1300 typedef unsigned __int64 DWORD64, *PDWORD64; #if defined(_WIN64) typedef unsigned __int64 SIZE_T, *PSIZE_T; #else typedef unsigned long SIZE_T, *PSIZE_T; #endif #endif // _MSC_VER < 1300 class StackWalkerInternal; // forward class StackWalker { public: typedef enum StackWalkOptions { // No addition info will be retrieved // (only the address is available) RetrieveNone = 0, // Try to get the symbol-name RetrieveSymbol = 1, // Try to get the line for this symbol RetrieveLine = 2, // Try to retrieve the module-infos RetrieveModuleInfo = 4, // Also retrieve the version for the DLL/EXE RetrieveFileVersion = 8, // Contains all the above RetrieveVerbose = 0xF, // Generate a "good" symbol-search-path SymBuildPath = 0x10, // Also use the public Microsoft-Symbol-Server SymUseSymSrv = 0x20, // Contains all the above "Sym"-options SymAll = 0x30, // Contains all options (default) OptionsAll = 0x3F } StackWalkOptions; StackWalker(int options = OptionsAll, // 'int' is by design, to combine the enum-flags LPCSTR szSymPath = NULL, DWORD dwProcessId = GetCurrentProcessId(), HANDLE hProcess = GetCurrentProcess()); StackWalker(DWORD dwProcessId, HANDLE hProcess); virtual ~StackWalker(); typedef BOOL(__stdcall* PReadProcessMemoryRoutine)( HANDLE hProcess, DWORD64 qwBaseAddress, PVOID lpBuffer, DWORD nSize, LPDWORD lpNumberOfBytesRead, LPVOID pUserData // optional data, which was passed in "ShowCallstack" ); BOOL LoadModules(); BOOL ShowCallstack( HANDLE hThread = GetCurrentThread(), const CONTEXT* context = NULL, PReadProcessMemoryRoutine readMemoryFunction = NULL, LPVOID pUserData = NULL // optional to identify some data in the 'readMemoryFunction'-callback ); BOOL ShowObject(LPVOID pObject); #if _MSC_VER >= 1300 // due to some reasons, the "STACKWALK_MAX_NAMELEN" must be declared as "public" // in older compilers in order to use it... starting with VC7 we can declare it as "protected" protected: #endif enum { STACKWALK_MAX_NAMELEN = 1024 }; // max name length for found symbols protected: // Entry for each Callstack-Entry typedef struct CallstackEntry { DWORD64 offset; // if 0, we have no valid entry CHAR name[STACKWALK_MAX_NAMELEN]; CHAR undName[STACKWALK_MAX_NAMELEN]; CHAR undFullName[STACKWALK_MAX_NAMELEN]; DWORD64 offsetFromSmybol; DWORD offsetFromLine; DWORD lineNumber; CHAR lineFileName[STACKWALK_MAX_NAMELEN]; DWORD symType; LPCSTR symTypeString; CHAR moduleName[STACKWALK_MAX_NAMELEN]; DWORD64 baseOfImage; CHAR loadedImageName[STACKWALK_MAX_NAMELEN]; } CallstackEntry; typedef enum CallstackEntryType { firstEntry, nextEntry, lastEntry } CallstackEntryType; virtual void OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName); virtual void OnLoadModule(LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size, DWORD result, LPCSTR symType, LPCSTR pdbName, ULONGLONG fileVersion); virtual void OnCallstackEntry(CallstackEntryType eType, CallstackEntry& entry); virtual void OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr); virtual void OnOutput(LPCSTR szText); StackWalkerInternal* m_sw; HANDLE m_hProcess; DWORD m_dwProcessId; BOOL m_modulesLoaded; LPSTR m_szSymPath; int m_options; int m_MaxRecursionCount; static BOOL __stdcall myReadProcMem(HANDLE hProcess, DWORD64 qwBaseAddress, PVOID lpBuffer, DWORD nSize, LPDWORD lpNumberOfBytesRead); friend StackWalkerInternal; }; // class StackWalker // The "ugly" assembler-implementation is needed for systems before XP // If you have a new PSDK and you only compile for XP and later, then you can use // the "RtlCaptureContext" // Currently there is no define which determines the PSDK-Version... // So we just use the compiler-version (and assumes that the PSDK is // the one which was installed by the VS-IDE) // INFO: If you want, you can use the RtlCaptureContext if you only target XP and later... // But I currently use it in x64/IA64 environments... //#if defined(_M_IX86) && (_WIN32_WINNT <= 0x0500) && (_MSC_VER < 1400) #if defined(_M_IX86) #ifdef CURRENT_THREAD_VIA_EXCEPTION // TODO: The following is not a "good" implementation, // because the callstack is only valid in the "__except" block... #define GET_CURRENT_CONTEXT_STACKWALKER_CODEPLEX(c, contextFlags) \ do \ { \ memset(&c, 0, sizeof(CONTEXT)); \ EXCEPTION_POINTERS* pExp = NULL; \ __try \ { \ throw 0; \ } \ __except (((pExp = GetExceptionInformation()) ? EXCEPTION_EXECUTE_HANDLER \ : EXCEPTION_EXECUTE_HANDLER)) \ { \ } \ if (pExp != NULL) \ memcpy(&c, pExp->ContextRecord, sizeof(CONTEXT)); \ c.ContextFlags = contextFlags; \ } while (0); #else // clang-format off // The following should be enough for walking the callstack... #define GET_CURRENT_CONTEXT_STACKWALKER_CODEPLEX(c, contextFlags) \ do \ { \ memset(&c, 0, sizeof(CONTEXT)); \ c.ContextFlags = contextFlags; \ __asm call x \ __asm x: pop eax \ __asm mov c.Eip, eax \ __asm mov c.Ebp, ebp \ __asm mov c.Esp, esp \ } while (0) // clang-format on #endif #else // The following is defined for x86 (XP and higher), x64 and IA64: #define GET_CURRENT_CONTEXT_STACKWALKER_CODEPLEX(c, contextFlags) \ do \ { \ memset(&c, 0, sizeof(CONTEXT)); \ c.ContextFlags = contextFlags; \ RtlCaptureContext(&c); \ } while (0); #endif #endif //defined(_MSC_VER) #endif // __STACKWALKER_H__ gammaray-2.11.3/3rdparty/StackWalker/qt_attribution.json000066400000000000000000000010101413310443000233030ustar00rootroot00000000000000{ "Id": "stackwalker", "Name": "StackWalker", "QDocModule": "gammaray-manual", "QtUsage": "Used by GammaRay for backtraces on Windows targets.", "Description": "Callstack access for Windows.", "Homepage": "https://github.com/JochenKalmbach/StackWalker", "Version": "4161d093d57ad6c905599b85861601146c3ef1e9", "License": "BSD 2-Clause \"Simplified\" License", "LicenseId": "BSD-2-Clause", "LicenseFile": "LICENSE", "Copyright": "Copyright (c) 2005 - 2019, Jochen Kalmbach" } gammaray-2.11.3/3rdparty/backward-cpp/000077500000000000000000000000001413310443000174735ustar00rootroot00000000000000gammaray-2.11.3/3rdparty/backward-cpp/.clang-format000066400000000000000000000000271413310443000220450ustar00rootroot00000000000000--- BasedOnStyle: LLVM gammaray-2.11.3/3rdparty/backward-cpp/.travis.yml000066400000000000000000000013301413310443000216010ustar00rootroot00000000000000language: cpp compiler: - gcc - clang addons: apt: packages: - valgrind install: - DEPS_DIR="${TRAVIS_BUILD_DIR}/deps" - mkdir ${DEPS_DIR} && cd ${DEPS_DIR} - CMAKE_URL="http://www.cmake.org/files/v3.3/cmake-3.3.2-Linux-x86_64.tar.gz" - mkdir cmake && travis_retry wget --no-check-certificate --quiet -O - ${CMAKE_URL} | tar --strip-components=1 -xz -C cmake - export PATH=${DEPS_DIR}/cmake/bin:${PATH} - pip install --user conan && export PATH=$PATH:$HOME/.local/bin - cd ${TRAVIS_BUILD_DIR} - mkdir build && cd build - cmake .. -DBACKWARD_TESTS=ON - cmake --build . script: - valgrind ctest .. --verbose - cd ${TRAVIS_BUILD_DIR} && conan create . Manu343726/testing --build=outdated gammaray-2.11.3/3rdparty/backward-cpp/BackwardConfig.cmake000066400000000000000000000173131413310443000233460ustar00rootroot00000000000000# # BackwardMacros.cmake # Copyright 2013 Google Inc. All Rights Reserved. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. ############################################################################### # OPTIONS ############################################################################### set(STACK_WALKING_UNWIND TRUE CACHE BOOL "Use compiler's unwind API") set(STACK_WALKING_BACKTRACE FALSE CACHE BOOL "Use backtrace from (e)glibc for stack walking") set(STACK_DETAILS_AUTO_DETECT TRUE CACHE BOOL "Auto detect backward's stack details dependencies") set(STACK_DETAILS_BACKTRACE_SYMBOL FALSE CACHE BOOL "Use backtrace from (e)glibc for symbols resolution") set(STACK_DETAILS_DW FALSE CACHE BOOL "Use libdw to read debug info") set(STACK_DETAILS_BFD FALSE CACHE BOOL "Use libbfd to read debug info") set(STACK_DETAILS_DWARF FALSE CACHE BOOL "Use libdwarf/libelf to read debug info") if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR AND NOT DEFINED BACKWARD_TESTS) # If this is a top level CMake project, we most lixely want the tests set(BACKWARD_TESTS ON CACHE BOOL "Enable tests") else() set(BACKWARD_TESTS OFF CACHE BOOL "Enable tests") endif() ############################################################################### # CONFIGS ############################################################################### if (${STACK_DETAILS_AUTO_DETECT}) include(FindPackageHandleStandardArgs) # find libdw find_path(LIBDW_INCLUDE_DIR NAMES "elfutils/libdw.h" "elfutils/libdwfl.h") find_library(LIBDW_LIBRARY dw) set(LIBDW_INCLUDE_DIRS ${LIBDW_INCLUDE_DIR} ) set(LIBDW_LIBRARIES ${LIBDW_LIBRARY} ) find_package_handle_standard_args(libdw DEFAULT_MSG LIBDW_LIBRARY LIBDW_INCLUDE_DIR) mark_as_advanced(LIBDW_INCLUDE_DIR LIBDW_LIBRARY) # find libbfd find_path(LIBBFD_INCLUDE_DIR NAMES "bfd.h") find_path(LIBDL_INCLUDE_DIR NAMES "dlfcn.h") find_library(LIBBFD_LIBRARY bfd) find_library(LIBDL_LIBRARY dl) set(LIBBFD_INCLUDE_DIRS ${LIBBFD_INCLUDE_DIR} ${LIBDL_INCLUDE_DIR}) set(LIBBFD_LIBRARIES ${LIBBFD_LIBRARY} ${LIBDL_LIBRARY}) find_package_handle_standard_args(libbfd DEFAULT_MSG LIBBFD_LIBRARY LIBBFD_INCLUDE_DIR LIBDL_LIBRARY LIBDL_INCLUDE_DIR) mark_as_advanced(LIBBFD_INCLUDE_DIR LIBBFD_LIBRARY LIBDL_INCLUDE_DIR LIBDL_LIBRARY) # find libdwarf find_path(LIBDWARF_INCLUDE_DIR NAMES "libdwarf.h" PATH_SUFFIXES libdwarf) find_path(LIBELF_INCLUDE_DIR NAMES "libelf.h") find_path(LIBDL_INCLUDE_DIR NAMES "dlfcn.h") find_library(LIBDWARF_LIBRARY dwarf) find_library(LIBELF_LIBRARY elf) find_library(LIBDL_LIBRARY dl) set(LIBDWARF_INCLUDE_DIRS ${LIBDWARF_INCLUDE_DIR} ${LIBELF_INCLUDE_DIR} ${LIBDL_INCLUDE_DIR}) set(LIBDWARF_LIBRARIES ${LIBDWARF_LIBRARY} ${LIBELF_LIBRARY} ${LIBDL_LIBRARY}) find_package_handle_standard_args(libdwarf DEFAULT_MSG LIBDWARF_LIBRARY LIBDWARF_INCLUDE_DIR LIBELF_LIBRARY LIBELF_INCLUDE_DIR LIBDL_LIBRARY LIBDL_INCLUDE_DIR) mark_as_advanced(LIBDWARF_INCLUDE_DIR LIBDWARF_LIBRARY LIBELF_INCLUDE_DIR LIBELF_LIBRARY LIBDL_INCLUDE_DIR LIBDL_LIBRARY) if (LIBDW_FOUND) LIST(APPEND _BACKWARD_INCLUDE_DIRS ${LIBDW_INCLUDE_DIRS}) LIST(APPEND _BACKWARD_LIBRARIES ${LIBDW_LIBRARIES}) set(STACK_DETAILS_DW TRUE) set(STACK_DETAILS_BFD FALSE) set(STACK_DETAILS_DWARF FALSE) set(STACK_DETAILS_BACKTRACE_SYMBOL FALSE) elseif(LIBBFD_FOUND) LIST(APPEND _BACKWARD_INCLUDE_DIRS ${LIBBFD_INCLUDE_DIRS}) LIST(APPEND _BACKWARD_LIBRARIES ${LIBBFD_LIBRARIES}) # If we attempt to link against static bfd, make sure to link its dependencies, too get_filename_component(bfd_lib_ext "${LIBBFD_LIBRARY}" EXT) if (bfd_lib_ext STREQUAL "${CMAKE_STATIC_LIBRARY_SUFFIX}") list(APPEND _BACKWARD_LIBRARIES iberty z) endif() set(STACK_DETAILS_DW FALSE) set(STACK_DETAILS_BFD TRUE) set(STACK_DETAILS_DWARF FALSE) set(STACK_DETAILS_BACKTRACE_SYMBOL FALSE) elseif(LIBDWARF_FOUND) LIST(APPEND _BACKWARD_INCLUDE_DIRS ${LIBDWARF_INCLUDE_DIRS}) LIST(APPEND _BACKWARD_LIBRARIES ${LIBDWARF_LIBRARIES}) set(STACK_DETAILS_DW FALSE) set(STACK_DETAILS_BFD FALSE) set(STACK_DETAILS_DWARF TRUE) set(STACK_DETAILS_BACKTRACE_SYMBOL FALSE) else() set(STACK_DETAILS_DW FALSE) set(STACK_DETAILS_BFD FALSE) set(STACK_DETAILS_DWARF FALSE) set(STACK_DETAILS_BACKTRACE_SYMBOL TRUE) endif() else() if (STACK_DETAILS_DW) LIST(APPEND _BACKWARD_LIBRARIES dw) endif() if (STACK_DETAILS_BFD) LIST(APPEND _BACKWARD_LIBRARIES bfd dl) endif() if (STACK_DETAILS_DWARF) LIST(APPEND _BACKWARD_LIBRARIES dwarf elf) endif() endif() macro(map_definitions var_prefix define_prefix) foreach(def ${ARGN}) if (${${var_prefix}${def}}) LIST(APPEND _BACKWARD_DEFINITIONS "${define_prefix}${def}=1") else() LIST(APPEND _BACKWARD_DEFINITIONS "${define_prefix}${def}=0") endif() endforeach() endmacro() if (NOT _BACKWARD_DEFINITIONS) map_definitions("STACK_WALKING_" "BACKWARD_HAS_" UNWIND BACKTRACE) map_definitions("STACK_DETAILS_" "BACKWARD_HAS_" BACKTRACE_SYMBOL DW BFD DWARF) endif() set(BACKWARD_INCLUDE_DIR "${CMAKE_CURRENT_LIST_DIR}") set(BACKWARD_HAS_EXTERNAL_LIBRARIES FALSE) set(FIND_PACKAGE_REQUIRED_VARS BACKWARD_INCLUDE_DIR) if(DEFINED _BACKWARD_LIBRARIES) set(BACKWARD_HAS_EXTERNAL_LIBRARIES TRUE) list(APPEND FIND_PACKAGE_REQUIRED_VARS _BACKWARD_LIBRARIES) endif() include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Backward REQUIRED_VARS ${FIND_PACKAGE_REQUIRED_VARS} ) list(APPEND _BACKWARD_INCLUDE_DIRS ${BACKWARD_INCLUDE_DIR}) macro(add_backward target) target_include_directories(${target} PRIVATE ${BACKWARD_INCLUDE_DIRS}) set_property(TARGET ${target} APPEND PROPERTY COMPILE_DEFINITIONS ${BACKWARD_DEFINITIONS}) set_property(TARGET ${target} APPEND PROPERTY LINK_LIBRARIES ${BACKWARD_LIBRARIES}) endmacro() set(BACKWARD_INCLUDE_DIRS ${_BACKWARD_INCLUDE_DIRS} CACHE INTERNAL "_BACKWARD_INCLUDE_DIRS") set(BACKWARD_DEFINITIONS ${_BACKWARD_DEFINITIONS} CACHE INTERNAL "BACKWARD_DEFINITIONS") set(BACKWARD_LIBRARIES ${_BACKWARD_LIBRARIES} CACHE INTERNAL "BACKWARD_LIBRARIES") mark_as_advanced(BACKWARD_INCLUDE_DIRS BACKWARD_DEFINITIONS BACKWARD_LIBRARIES) # Expand each definition in BACKWARD_DEFINITIONS to its own cmake var and export # to outer scope foreach(var ${BACKWARD_DEFINITIONS}) string(REPLACE "=" ";" var_as_list ${var}) list(GET var_as_list 0 var_name) list(GET var_as_list 1 var_value) set(${var_name} ${var_value}) mark_as_advanced(${var_name}) endforeach() if (NOT TARGET Backward::Backward) add_library(Backward::Backward INTERFACE IMPORTED) set_target_properties(Backward::Backward PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${BACKWARD_INCLUDE_DIRS}" INTERFACE_COMPILE_DEFINITIONS "${BACKWARD_DEFINITIONS}" ) if(BACKWARD_HAS_EXTERNAL_LIBRARIES) set_target_properties(Backward::Backward PROPERTIES INTERFACE_LINK_LIBRARIES "${BACKWARD_LIBRARIES}" ) endif() endif() gammaray-2.11.3/3rdparty/backward-cpp/CMakeLists.txt000066400000000000000000000106041413310443000222340ustar00rootroot00000000000000# # CMakeLists.txt # Copyright 2013 Google Inc. All Rights Reserved. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. cmake_minimum_required(VERSION 3.0) project(backward CXX) # Introduce variables: # * CMAKE_INSTALL_LIBDIR # * CMAKE_INSTALL_BINDIR # * CMAKE_INSTALL_INCLUDEDIR include(GNUInstallDirs) include(BackwardConfig.cmake) # check if compiler is nvcc or nvcc_wrapper set(COMPILER_IS_NVCC false) get_filename_component(COMPILER_NAME ${CMAKE_CXX_COMPILER} NAME) if (COMPILER_NAME MATCHES "^nvcc") set(COMPILER_IS_NVCC true) endif() if (DEFINED ENV{OMPI_CXX} OR DEFINED ENV{MPICH_CXX}) if ( ($ENV{OMPI_CXX} MATCHES "nvcc") OR ($ENV{MPICH_CXX} MATCHES "nvcc") ) set(COMPILER_IS_NVCC true) endif() endif() # set CXX standard set(CMAKE_CXX_STANDARD_REQUIRED True) set(CMAKE_CXX_STANDARD 11) if (${COMPILER_IS_NVCC}) # GNU CXX extensions are not supported by nvcc set(CMAKE_CXX_EXTENSIONS OFF) endif() ############################################################################### # COMPILER FLAGS ############################################################################### if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_COMPILER_IS_GNUCXX) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra") if (NOT ${COMPILER_IS_NVCC}) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pedantic-errors") endif() set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g") endif() ############################################################################### # BACKWARD OBJECT ############################################################################### add_library(backward_object OBJECT backward.cpp) target_compile_definitions(backward_object PRIVATE ${BACKWARD_DEFINITIONS}) target_include_directories(backward_object PRIVATE ${BACKWARD_INCLUDE_DIRS}) set(BACKWARD_ENABLE $ CACHE STRING "Link with this object to setup backward automatically") ############################################################################### # BACKWARD LIBRARY (Includes backward.cpp) ############################################################################### option(BACKWARD_SHARED "Build dynamic backward-cpp shared lib" OFF) if(BACKWARD_SHARED) set(libtype SHARED) endif() add_library(backward ${libtype} backward.cpp) target_compile_definitions(backward PUBLIC ${BACKWARD_DEFINITIONS}) target_include_directories(backward PUBLIC ${BACKWARD_INCLUDE_DIRS}) ############################################################################### # TESTS ############################################################################### if(BACKWARD_TESTS) enable_testing() add_library(test_main OBJECT test/_test_main.cpp) macro(backward_add_test src) get_filename_component(name ${src} NAME_WE) set(test_name "test_${name}") add_executable(${test_name} ${src} ${ARGN} $) target_link_libraries(${test_name} PRIVATE Backward::Backward) add_test(NAME ${name} COMMAND ${test_name}) endmacro() # Tests without backward.cpp set(TESTS test stacktrace rectrace select_signals ) foreach(test ${TESTS}) backward_add_test(test/${test}.cpp) endforeach() # Tests with backward.cpp set(TESTS suicide ) foreach(test ${TESTS}) backward_add_test(test/${test}.cpp ${BACKWARD_ENABLE}) endforeach() endif() install( FILES "backward.hpp" DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} ) install( FILES "BackwardConfig.cmake" DESTINATION ${CMAKE_INSTALL_LIBDIR}/backward ) gammaray-2.11.3/3rdparty/backward-cpp/LICENSE.txt000066400000000000000000000021071413310443000213160ustar00rootroot00000000000000Copyright 2013 Google Inc. All Rights Reserved. The MIT License (MIT) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. gammaray-2.11.3/3rdparty/backward-cpp/README.md000066400000000000000000000332561413310443000207630ustar00rootroot00000000000000Backward-cpp [![badge](https://img.shields.io/badge/conan.io-backward%2F1.3.0-green.svg?logo=data:image/png;base64%2CiVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAMAAAAolt3jAAAA1VBMVEUAAABhlctjlstkl8tlmMtlmMxlmcxmmcxnmsxpnMxpnM1qnc1sn85voM91oM11oc1xotB2oc56pNF6pNJ2ptJ8ptJ8ptN9ptN8p9N5qNJ9p9N9p9R8qtOBqdSAqtOAqtR%2BrNSCrNJ/rdWDrNWCsNWCsNaJs9eLs9iRvNuVvdyVv9yXwd2Zwt6axN6dxt%2Bfx%2BChyeGiyuGjyuCjyuGly%2BGlzOKmzOGozuKoz%2BKqz%2BOq0OOv1OWw1OWw1eWx1eWy1uay1%2Baz1%2Baz1%2Bez2Oe02Oe12ee22ujUGwH3AAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfgBQkREyOxFIh/AAAAiklEQVQI12NgAAMbOwY4sLZ2NtQ1coVKWNvoc/Eq8XDr2wB5Ig62ekza9vaOqpK2TpoMzOxaFtwqZua2Bm4makIM7OzMAjoaCqYuxooSUqJALjs7o4yVpbowvzSUy87KqSwmxQfnsrPISyFzWeWAXCkpMaBVIC4bmCsOdgiUKwh3JojLgAQ4ZCE0AMm2D29tZwe6AAAAAElFTkSuQmCC)](http://www.conan.io/source/backward/1.3.0/Manu343726/testing) [![Build Status](https://travis-ci.org/bombela/backward-cpp.svg?branch=master)](https://travis-ci.org/bombela/backward-cpp) ============ Backward is a beautiful stack trace pretty printer for C++. If you are bored to see this: ![default trace](doc/rude.png) Backward will spice it up for you: ![pretty stackstrace](doc/pretty.png) There is not much to say. Of course it will be able to display the code snippets only if the source files are accessible (else see trace #4 in the example). All "Source" lines and code snippet prefixed by a pipe "|" are frames inline the next frame. You can see that for the trace #1 in the example, the function `you_shall_not_pass()` was inlined in the function `...read2::do_test()` by the compiler. ## Installation #### Install backward.hpp Backward is a header only library. So installing Backward is easy, simply drop a copy of `backward.hpp` along with your other source files in your C++ project. You can also use a git submodule or really any other way that best fits your environment, as long as you can include `backward.hpp`. #### Install backward.cpp If you want Backward to automatically print a stack trace on most common fatal errors (segfault, abort, un-handled exception...), simply add a copy of `backward.cpp` to your project, and don't forget to tell your build system. The code in `backward.cpp` is trivial anyway, you can simply copy what it's doing at your convenience. ## Configuration & Dependencies ### Integration with CMake If you are using CMake and want to use its configuration abilities to save you the trouble, you can easily integrate Backward, depending on how you obtained the library. #### As a subdirectory: In this case you have a subdirectory containing the whole repository of Backward (eg.: using git-submodules), in this case you can do: ``` add_subdirectory(/path/to/backward-cpp) # This will add backward.cpp to your target add_executable(mytarget mysource.cpp ${BACKWARD_ENABLE}) # This will add libraries, definitions and include directories needed by backward # by setting each property on the target. add_backward(mytarget) ``` #### Modifying CMAKE_MODULE_PATH In this case you can have Backward installed as a subdirectory: ``` list(APPEND CMAKE_MODULE_PATH /path/to/backward-cpp) find_package(Backward) # This will add libraries, definitions and include directories needed by backward # through an IMPORTED target. target_link_libraries(mytarget PUBLIC Backward::Backward) ``` Notice that this is equivalent to using the the approach that uses `add_subdirectory()`, however it uses cmake's [imported target](https://cmake.org/Wiki/CMake/Tutorials/Exporting_and_Importing_Targets) mechanism. #### Installation through a regular package manager In this case you have obtained Backward through a package manager. Packages currently available: - [conda-forge](https://anaconda.org/conda-forge/backward-cpp) ``` find_package(Backward) # This will add libraries, definitions and include directories needed by backward # through an IMPORTED target. target_link_libraries(mytarget PUBLIC Backward::Backward) ``` ### Compile with debug info You need to compile your project with generation of debug symbols enabled, usually `-g` with clang++ and g++. Note that you can use `-g` with any level of optimization, with modern debug information encoding like DWARF, it only takes space in the binary (it's not loaded in memory until your debugger or Backward makes use of it, don't worry), and it doesn't impact the code generation (at least on GNU/Linux x86\_64 for what I know). If you are missing debug information, the stack trace will lack details about your sources. ### Libraries to read the debug info Backward support pretty printed stack traces on GNU/Linux only, it will compile fine under other platforms but will not do anything. **Pull requests are welcome :)** Also, by default you will get a really basic stack trace, based on the `backtrace_symbols` API: ![default trace](doc/nice.png) You will need to install some dependencies to get the ultimate stack trace. Two libraries are currently supported, the only difference is which one is the easiest for you to install, so pick your poison: #### libbfd from the [GNU/binutils](http://www.gnu.org/software/binutils/) apt-get install binutils-dev (or equivalent) And do not forget to link with the lib: `g++/clang++ -lbfd -ldl ...` This library requires dynamic loading. Which is provided by the library `dl`. Hence why we also link with `-ldl`. Then define the following before every inclusion of `backward.hpp` (don't forget to update `backward.cpp` as well): #define BACKWARD_HAS_BFD 1 #### libdw from the [elfutils](https://fedorahosted.org/elfutils/) apt-get install libdw-dev (or equivalent) And do not forget to link with the lib and inform Backward to use it: #define BACKWARD_HAS_DW 1 Of course you can simply add the define (`-DBACKWARD_HAS_...=1`) and the linkage details in your build system and even auto-detect which library is installed, it's up to you. #### [libdwarf](https://sourceforge.net/projects/libdwarf/) and [libelf](http://www.mr511.de/software/english.html) apt-get install libdwarf-dev (or equivalent) And do not forget to link with the lib and inform Backward to use it: #define BACKWARD_HAS_DWARF 1 There are several alternative implementations of libdwarf and libelf that are API compatible so it's possible, although it hasn't been tested, to replace the ones used when developing backward (in bold, below): * **_libelf_** by [Michael "Tired" Riepe](http://www.mr511.de/software/english.html) * **_libdwarf_** by [David Anderson](https://www.prevanders.net/dwarf.html) * libelf from [elfutils](https://fedorahosted.org/elfutils/) * libelf and libdwarf from FreeBSD's [ELF Tool Chain](https://sourceforge.net/p/elftoolchain/wiki/Home/) project Of course you can simply add the define (`-DBACKWARD_HAS_...=1`) and the linkage details in your build system and even auto-detect which library is installed, it's up to you. That's it, you are all set, you should be getting nice stack traces like the one at the beginning of this document. ## API If you don't want to limit yourself to the defaults offered by `backward.cpp`, and you want to take some random stack traces for whatever reason and pretty print them the way you love or you decide to send them all to your buddies over the Internet, you will appreciate the simplicity of Backward's API. ### Stacktrace The StackTrace class lets you take a "snapshot" of the current stack. You can use it like this: ```c++ using namespace backward; StackTrace st; st.load_here(32); Printer p; p.print(st); ``` The public methods are: ```c++ class StackTrace { public: // Take a snapshot of the current stack, with at most "trace_cnt_max" // traces in it. The first trace is the most recent (ie the current // frame). You can also provide a trace address to load_from() assuming // the address is a valid stack frame (useful for signal handling traces). // Both function return size(). size_t load_here(size_t trace_cnt_max) size_t load_from(void* address, size_t trace_cnt_max) // The number of traces loaded. This can be less than "trace_cnt_max". size_t size() const // A unique id for the thread in which the trace was taken. The value // 0 means the stack trace comes from the main thread. size_t thread_id() const // Retrieve a trace by index. 0 is the most recent trace, size()-1 is // the oldest one. Trace operator[](size_t trace_idx) }; ``` ### TraceResolver The `TraceResolver` does the heavy lifting, and intends to transform a simple `Trace` from its address into a fully detailed `ResolvedTrace` with the filename of the source, line numbers, inlined functions and so on. You can use it like this: ```c++ using namespace backward; StackTrace st; st.load_here(32); TraceResolver tr; tr.load_stacktrace(st); for (size_t i = 0; i < st.size(); ++i) { ResolvedTrace trace = tr.resolve(st[i]); std::cout << "#" << i << " " << trace.object_filename << " " << trace.object_function << " [" << trace.addr << "]" << std::endl; } ``` The public methods are: ```c++ class TraceResolver { public: // Pre-load whatever is necessary from the stack trace. template void load_stacktrace(ST&) // Resolve a trace. It takes a ResolvedTrace, because a `Trace` is // implicitly convertible to it. ResolvedTrace resolve(ResolvedTrace t) }; ``` ### SnippetFactory The SnippetFactory is a simple helper class to automatically load and cache source files in order to extract code snippets. ```c++ class SnippetFactory { public: // A snippet is a list of line numbers and line contents. typedef std::vector > lines_t; // Return a snippet starting at line_start with up to context_size lines. lines_t get_snippet(const std::string& filename, size_t line_start, size_t context_size) // Return a combined snippet from two different locations and combine them. // context_size / 2 lines will be extracted from each location. lines_t get_combined_snippet( const std::string& filename_a, size_t line_a, const std::string& filename_b, size_t line_b, size_t context_size) // Tries to return a unified snippet if the two locations from the same // file are close enough to fit inside one context_size, else returns // the equivalent of get_combined_snippet(). lines_t get_coalesced_snippet(const std::string& filename, size_t line_a, size_t line_b, size_t context_size) ``` ### Printer A simpler way to pretty print a stack trace to the terminal. It will automatically resolve the traces for you: ```c++ using namespace backward; StackTrace st; st.load_here(32); Printer p; p.object = true; p.color_mode = ColorMode::always; p.address = true; p.print(st, stderr); ``` You can set a few options: ```c++ class Printer { public: // Print a little snippet of code if possible. bool snippet = true; // Colorize the trace // - ColorMode::automatic: Activate colors if possible. For example, when using a TTY on linux. // - ColorMode::always: Always use colors. // - ColorMode::never: Never use colors. bool color_mode = ColorMode::automatic; // Add the addresses of every source location to the trace. bool address = false; // Even if there is a source location, also prints the object // from where the trace came from. bool object = false; // Resolve and print a stack trace to the given C FILE* object. // On linux, if the FILE* object is attached to a TTY, // color will be used if color_mode is set to automatic. template FILE* print(StackTrace& st, FILE* fp = stderr); // Resolve and print a stack trace to the given std::ostream object. // Color will only be used if color_mode is set to always. template std::ostream& print(ST& st, std::ostream& os); ``` ### SignalHandling A simple helper class that registers for you the most common signals and other callbacks to segfault, hardware exception, un-handled exception etc. `backward.cpp` simply uses it like that: ```c++ backward::SignalHandling sh; ``` Creating the object registers all the different signals and hooks. Destroying this object doesn't do anything. It exposes only one method: ```c++ bool loaded() const // true if loaded with success ``` ### Trace object To keep the memory footprint of a loaded `StackTrace` on the low-side, there a hierarchy of trace object, from a minimal `Trace `to a `ResolvedTrace`. #### Simple trace ```c++ struct Trace { void* addr; // address of the trace size_t idx; // its index (0 == most recent) }; ``` #### Resolved trace A `ResolvedTrace` should contains a maximum of details about the location of the trace in the source code. Note that not all fields might be set. ```c++ struct ResolvedTrace: public Trace { struct SourceLoc { std::string function; std::string filename; size_t line; size_t col; }; // In which binary object this trace is located. std::string object_filename; // The function in the object that contains the trace. This is not the same // as source.function which can be an function inlined in object_function. std::string object_function; // The source location of this trace. It is possible for filename to be // empty and for line/col to be invalid (value 0) if this information // couldn't be deduced, for example if there is no debug information in the // binary object. SourceLoc source; // An optional list of "inliners". All of these sources locations where // inlined in the source location of the trace (the attribute right above). // This is especially useful when you compile with optimizations turned on. typedef std::vector source_locs_t; source_locs_t inliners; }; ``` ## Contact and copyright François-Xavier Bourlet Copyright 2013-2017 Google Inc. All Rights Reserved. MIT License. ### Disclaimer Although this project is owned by Google Inc. this is not a Google supported or affiliated project. gammaray-2.11.3/3rdparty/backward-cpp/backward.cpp000066400000000000000000000013651413310443000217620ustar00rootroot00000000000000// Pick your poison. // // On GNU/Linux, you have few choices to get the most out of your stack trace. // // By default you get: // - object filename // - function name // // In order to add: // - source filename // - line and column numbers // - source code snippet (assuming the file is accessible) // Install one of the following library then uncomment one of the macro (or // better, add the detection of the lib and the macro definition in your build // system) // - apt-get install libdw-dev ... // - g++/clang++ -ldw ... // #define BACKWARD_HAS_DW 1 // - apt-get install binutils-dev ... // - g++/clang++ -lbfd ... // #define BACKWARD_HAS_BFD 1 #include "backward.hpp" namespace backward { backward::SignalHandling sh; } // namespace backward gammaray-2.11.3/3rdparty/backward-cpp/backward.hpp000066400000000000000000003743641413310443000220030ustar00rootroot00000000000000/* * backward.hpp * Copyright 2013 Google Inc. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef H_6B9572DA_A64B_49E6_B234_051480991C89 #define H_6B9572DA_A64B_49E6_B234_051480991C89 #ifndef __cplusplus #error "It's not going to compile without a C++ compiler..." #endif #if defined(BACKWARD_CXX11) #elif defined(BACKWARD_CXX98) #else #if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1800) #define BACKWARD_CXX11 #define BACKWARD_ATLEAST_CXX11 #define BACKWARD_ATLEAST_CXX98 #else #define BACKWARD_CXX98 #define BACKWARD_ATLEAST_CXX98 #endif #endif // You can define one of the following (or leave it to the auto-detection): // // #define BACKWARD_SYSTEM_LINUX // - specialization for linux // // #define BACKWARD_SYSTEM_DARWIN // - specialization for Mac OS X 10.5 and later. // // #define BACKWARD_SYSTEM_UNKNOWN // - placebo implementation, does nothing. // #if defined(BACKWARD_SYSTEM_LINUX) #elif defined(BACKWARD_SYSTEM_DARWIN) #elif defined(BACKWARD_SYSTEM_UNKNOWN) #elif defined(BACKWARD_SYSTEM_WINDOWS) #else #if defined(__linux) || defined(__linux__) #define BACKWARD_SYSTEM_LINUX #elif defined(__APPLE__) #define BACKWARD_SYSTEM_DARWIN #elif defined(_WIN32) #define BACKWARD_SYSTEM_WINDOWS #else #define BACKWARD_SYSTEM_UNKNOWN #endif #endif #define NOINLINE __attribute__((noinline)) #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(BACKWARD_SYSTEM_LINUX) // On linux, backtrace can back-trace or "walk" the stack using the following // libraries: // // #define BACKWARD_HAS_UNWIND 1 // - unwind comes from libgcc, but I saw an equivalent inside clang itself. // - with unwind, the stacktrace is as accurate as it can possibly be, since // this is used by the C++ runtine in gcc/clang for stack unwinding on // exception. // - normally libgcc is already linked to your program by default. // // #define BACKWARD_HAS_BACKTRACE == 1 // - backtrace seems to be a little bit more portable than libunwind, but on // linux, it uses unwind anyway, but abstract away a tiny information that is // sadly really important in order to get perfectly accurate stack traces. // - backtrace is part of the (e)glib library. // // The default is: // #define BACKWARD_HAS_UNWIND == 1 // // Note that only one of the define should be set to 1 at a time. // #if BACKWARD_HAS_UNWIND == 1 #elif BACKWARD_HAS_BACKTRACE == 1 #else #undef BACKWARD_HAS_UNWIND #define BACKWARD_HAS_UNWIND 1 #undef BACKWARD_HAS_BACKTRACE #define BACKWARD_HAS_BACKTRACE 0 #endif // On linux, backward can extract detailed information about a stack trace // using one of the following libraries: // // #define BACKWARD_HAS_DW 1 // - libdw gives you the most juicy details out of your stack traces: // - object filename // - function name // - source filename // - line and column numbers // - source code snippet (assuming the file is accessible) // - variables name and values (if not optimized out) // - You need to link with the lib "dw": // - apt-get install libdw-dev // - g++/clang++ -ldw ... // // #define BACKWARD_HAS_BFD 1 // - With libbfd, you get a fair amount of details: // - object filename // - function name // - source filename // - line numbers // - source code snippet (assuming the file is accessible) // - You need to link with the lib "bfd": // - apt-get install binutils-dev // - g++/clang++ -lbfd ... // // #define BACKWARD_HAS_DWARF 1 // - libdwarf gives you the most juicy details out of your stack traces: // - object filename // - function name // - source filename // - line and column numbers // - source code snippet (assuming the file is accessible) // - variables name and values (if not optimized out) // - You need to link with the lib "dwarf": // - apt-get install libdwarf-dev // - g++/clang++ -ldwarf ... // // #define BACKWARD_HAS_BACKTRACE_SYMBOL 1 // - backtrace provides minimal details for a stack trace: // - object filename // - function name // - backtrace is part of the (e)glib library. // // The default is: // #define BACKWARD_HAS_BACKTRACE_SYMBOL == 1 // // Note that only one of the define should be set to 1 at a time. // #if BACKWARD_HAS_DW == 1 #elif BACKWARD_HAS_BFD == 1 #elif BACKWARD_HAS_DWARF == 1 #elif BACKWARD_HAS_BACKTRACE_SYMBOL == 1 #else #undef BACKWARD_HAS_DW #define BACKWARD_HAS_DW 0 #undef BACKWARD_HAS_BFD #define BACKWARD_HAS_BFD 0 #undef BACKWARD_HAS_DWARF #define BACKWARD_HAS_DWARF 0 #undef BACKWARD_HAS_BACKTRACE_SYMBOL #define BACKWARD_HAS_BACKTRACE_SYMBOL 1 #endif #include #include #ifdef __ANDROID__ // Old Android API levels define _Unwind_Ptr in both link.h and // unwind.h Rename the one in link.h as we are not going to be using // it #define _Unwind_Ptr _Unwind_Ptr_Custom #include #undef _Unwind_Ptr #else #include #endif #include #include #include #include #if BACKWARD_HAS_BFD == 1 // NOTE: defining PACKAGE{,_VERSION} is required before including // bfd.h on some platforms, see also: // https://sourceware.org/bugzilla/show_bug.cgi?id=14243 #ifndef PACKAGE #define PACKAGE #endif #ifndef PACKAGE_VERSION #define PACKAGE_VERSION #endif #include #ifndef _GNU_SOURCE #define _GNU_SOURCE #include #undef _GNU_SOURCE #else #include #endif #endif #if BACKWARD_HAS_DW == 1 #include #include #include #endif #if BACKWARD_HAS_DWARF == 1 #include #include #include #include #include #ifndef _GNU_SOURCE #define _GNU_SOURCE #include #undef _GNU_SOURCE #else #include #endif #endif #if (BACKWARD_HAS_BACKTRACE == 1) || (BACKWARD_HAS_BACKTRACE_SYMBOL == 1) // then we shall rely on backtrace #include #endif #endif // defined(BACKWARD_SYSTEM_LINUX) #if defined(BACKWARD_SYSTEM_DARWIN) // On Darwin, backtrace can back-trace or "walk" the stack using the following // libraries: // // #define BACKWARD_HAS_UNWIND 1 // - unwind comes from libgcc, but I saw an equivalent inside clang itself. // - with unwind, the stacktrace is as accurate as it can possibly be, since // this is used by the C++ runtine in gcc/clang for stack unwinding on // exception. // - normally libgcc is already linked to your program by default. // // #define BACKWARD_HAS_BACKTRACE == 1 // - backtrace is available by default, though it does not produce as much // information as another library might. // // The default is: // #define BACKWARD_HAS_UNWIND == 1 // // Note that only one of the define should be set to 1 at a time. // #if BACKWARD_HAS_UNWIND == 1 #elif BACKWARD_HAS_BACKTRACE == 1 #else #undef BACKWARD_HAS_UNWIND #define BACKWARD_HAS_UNWIND 1 #undef BACKWARD_HAS_BACKTRACE #define BACKWARD_HAS_BACKTRACE 0 #endif // On Darwin, backward can extract detailed information about a stack trace // using one of the following libraries: // // #define BACKWARD_HAS_BACKTRACE_SYMBOL 1 // - backtrace provides minimal details for a stack trace: // - object filename // - function name // // The default is: // #define BACKWARD_HAS_BACKTRACE_SYMBOL == 1 // #if BACKWARD_HAS_BACKTRACE_SYMBOL == 1 #else #undef BACKWARD_HAS_BACKTRACE_SYMBOL #define BACKWARD_HAS_BACKTRACE_SYMBOL 1 #endif #include #include #include #include #include #include #if (BACKWARD_HAS_BACKTRACE == 1) || (BACKWARD_HAS_BACKTRACE_SYMBOL == 1) #include #endif #endif // defined(BACKWARD_SYSTEM_DARWIN) #if defined(BACKWARD_SYSTEM_WINDOWS) #include #include #include #include typedef SSIZE_T ssize_t; #define NOMINMAX #include #include #include #include #ifndef __clang__ #undef NOINLINE #define NOINLINE __declspec(noinline) #endif #pragma comment(lib, "psapi.lib") #pragma comment(lib, "dbghelp.lib") // Comment / packing is from stackoverflow: // https://stackoverflow.com/questions/6205981/windows-c-stack-trace-from-a-running-app/28276227#28276227 // Some versions of imagehlp.dll lack the proper packing directives themselves // so we need to do it. #pragma pack(push, before_imagehlp, 8) #include #pragma pack(pop, before_imagehlp) // TODO maybe these should be undefined somewhere else? #undef BACKWARD_HAS_UNWIND #undef BACKWARD_HAS_BACKTRACE #if BACKWARD_HAS_PDB_SYMBOL == 1 #else #undef BACKWARD_HAS_PDB_SYMBOL #define BACKWARD_HAS_PDB_SYMBOL 1 #endif #endif #if BACKWARD_HAS_UNWIND == 1 #include // while gcc's unwind.h defines something like that: // extern _Unwind_Ptr _Unwind_GetIP (struct _Unwind_Context *); // extern _Unwind_Ptr _Unwind_GetIPInfo (struct _Unwind_Context *, int *); // // clang's unwind.h defines something like this: // uintptr_t _Unwind_GetIP(struct _Unwind_Context* __context); // // Even if the _Unwind_GetIPInfo can be linked to, it is not declared, worse we // cannot just redeclare it because clang's unwind.h doesn't define _Unwind_Ptr // anyway. // // Luckily we can play on the fact that the guard macros have a different name: #ifdef __CLANG_UNWIND_H // In fact, this function still comes from libgcc (on my different linux boxes, // clang links against libgcc). #include extern "C" uintptr_t _Unwind_GetIPInfo(_Unwind_Context *, int *); #endif #endif // BACKWARD_HAS_UNWIND == 1 #ifdef BACKWARD_ATLEAST_CXX11 #include #include // for std::swap namespace backward { namespace details { template struct hashtable { typedef std::unordered_map type; }; using std::move; } // namespace details } // namespace backward #else // NOT BACKWARD_ATLEAST_CXX11 #define nullptr NULL #define override #include namespace backward { namespace details { template struct hashtable { typedef std::map type; }; template const T &move(const T &v) { return v; } template T &move(T &v) { return v; } } // namespace details } // namespace backward #endif // BACKWARD_ATLEAST_CXX11 namespace backward { namespace details { #if defined(BACKWARD_SYSTEM_WINDOWS) const char kBackwardPathDelimiter[] = ";"; #else const char kBackwardPathDelimiter[] = ":"; #endif } // namespace details } // namespace backward namespace backward { namespace system_tag { struct linux_tag; // seems that I cannot call that "linux" because the name // is already defined... so I am adding _tag everywhere. struct darwin_tag; struct windows_tag; struct unknown_tag; #if defined(BACKWARD_SYSTEM_LINUX) typedef linux_tag current_tag; #elif defined(BACKWARD_SYSTEM_DARWIN) typedef darwin_tag current_tag; #elif defined(BACKWARD_SYSTEM_WINDOWS) typedef windows_tag current_tag; #elif defined(BACKWARD_SYSTEM_UNKNOWN) typedef unknown_tag current_tag; #else #error "May I please get my system defines?" #endif } // namespace system_tag namespace trace_resolver_tag { #if defined(BACKWARD_SYSTEM_LINUX) struct libdw; struct libbfd; struct libdwarf; struct backtrace_symbol; #if BACKWARD_HAS_DW == 1 typedef libdw current; #elif BACKWARD_HAS_BFD == 1 typedef libbfd current; #elif BACKWARD_HAS_DWARF == 1 typedef libdwarf current; #elif BACKWARD_HAS_BACKTRACE_SYMBOL == 1 typedef backtrace_symbol current; #else #error "You shall not pass, until you know what you want." #endif #elif defined(BACKWARD_SYSTEM_DARWIN) struct backtrace_symbol; #if BACKWARD_HAS_BACKTRACE_SYMBOL == 1 typedef backtrace_symbol current; #else #error "You shall not pass, until you know what you want." #endif #elif defined(BACKWARD_SYSTEM_WINDOWS) struct pdb_symbol; #if BACKWARD_HAS_PDB_SYMBOL == 1 typedef pdb_symbol current; #else #error "You shall not pass, until you know what you want." #endif #endif } // namespace trace_resolver_tag namespace details { template struct rm_ptr { typedef T type; }; template struct rm_ptr { typedef T type; }; template struct rm_ptr { typedef const T type; }; template struct deleter { template void operator()(U &ptr) const { (*F)(ptr); } }; template struct default_delete { void operator()(T &ptr) const { delete ptr; } }; template > class handle { struct dummy; T _val; bool _empty; #ifdef BACKWARD_ATLEAST_CXX11 handle(const handle &) = delete; handle &operator=(const handle &) = delete; #endif public: ~handle() { if (!_empty) { Deleter()(_val); } } explicit handle() : _val(), _empty(true) {} explicit handle(T val) : _val(val), _empty(false) { if (!_val) _empty = true; } #ifdef BACKWARD_ATLEAST_CXX11 handle(handle &&from) : _empty(true) { swap(from); } handle &operator=(handle &&from) { swap(from); return *this; } #else explicit handle(const handle &from) : _empty(true) { // some sort of poor man's move semantic. swap(const_cast(from)); } handle &operator=(const handle &from) { // some sort of poor man's move semantic. swap(const_cast(from)); return *this; } #endif void reset(T new_val) { handle tmp(new_val); swap(tmp); } void update(T new_val) { _val = new_val; _empty = static_cast(new_val); } operator const dummy *() const { if (_empty) { return nullptr; } return reinterpret_cast(_val); } T get() { return _val; } T release() { _empty = true; return _val; } void swap(handle &b) { using std::swap; swap(b._val, _val); // can throw, we are safe here. swap(b._empty, _empty); // should not throw: if you cannot swap two // bools without throwing... It's a lost cause anyway! } T &operator->() { return _val; } const T &operator->() const { return _val; } typedef typename rm_ptr::type &ref_t; typedef const typename rm_ptr::type &const_ref_t; ref_t operator*() { return *_val; } const_ref_t operator*() const { return *_val; } ref_t operator[](size_t idx) { return _val[idx]; } // Watch out, we've got a badass over here T *operator&() { _empty = false; return &_val; } }; // Default demangler implementation (do nothing). template struct demangler_impl { static std::string demangle(const char *funcname) { return funcname; } }; #if defined(BACKWARD_SYSTEM_LINUX) || defined(BACKWARD_SYSTEM_DARWIN) template <> struct demangler_impl { demangler_impl() : _demangle_buffer_length(0) {} std::string demangle(const char *funcname) { using namespace details; char *result = abi::__cxa_demangle(funcname, _demangle_buffer.get(), &_demangle_buffer_length, nullptr); if (result) { _demangle_buffer.update(result); return result; } return funcname; } private: details::handle _demangle_buffer; size_t _demangle_buffer_length; }; #endif // BACKWARD_SYSTEM_LINUX || BACKWARD_SYSTEM_DARWIN struct demangler : public demangler_impl {}; // Split a string on the platform's PATH delimiter. Example: if delimiter // is ":" then: // "" --> [] // ":" --> ["",""] // "::" --> ["","",""] // "/a/b/c" --> ["/a/b/c"] // "/a/b/c:/d/e/f" --> ["/a/b/c","/d/e/f"] // etc. inline std::vector split_source_prefixes(const std::string &s) { std::vector out; size_t last = 0; size_t next = 0; size_t delimiter_size = sizeof(kBackwardPathDelimiter)-1; while ((next = s.find(kBackwardPathDelimiter, last)) != std::string::npos) { out.push_back(s.substr(last, next-last)); last = next + delimiter_size; } if (last <= s.length()) { out.push_back(s.substr(last)); } return out; } } // namespace details /*************** A TRACE ***************/ struct Trace { void *addr; size_t idx; Trace() : addr(nullptr), idx(0) {} explicit Trace(void *_addr, size_t _idx) : addr(_addr), idx(_idx) {} }; struct ResolvedTrace : public Trace { struct SourceLoc { std::string function; std::string filename; unsigned line; unsigned col; SourceLoc() : line(0), col(0) {} bool operator==(const SourceLoc &b) const { return function == b.function && filename == b.filename && line == b.line && col == b.col; } bool operator!=(const SourceLoc &b) const { return !(*this == b); } }; // In which binary object this trace is located. std::string object_filename; // The function in the object that contain the trace. This is not the same // as source.function which can be an function inlined in object_function. std::string object_function; // The source location of this trace. It is possible for filename to be // empty and for line/col to be invalid (value 0) if this information // couldn't be deduced, for example if there is no debug information in the // binary object. SourceLoc source; // An optionals list of "inliners". All the successive sources location // from where the source location of the trace (the attribute right above) // is inlined. It is especially useful when you compiled with optimization. typedef std::vector source_locs_t; source_locs_t inliners; ResolvedTrace() : Trace() {} ResolvedTrace(const Trace &mini_trace) : Trace(mini_trace) {} }; /*************** STACK TRACE ***************/ // default implemention. template class StackTraceImpl { public: size_t size() const { return 0; } Trace operator[](size_t) const { return Trace(); } size_t load_here(size_t = 0) { return 0; } size_t load_from(void *, size_t = 0) { return 0; } size_t thread_id() const { return 0; } void skip_n_firsts(size_t) {} }; class StackTraceImplBase { public: StackTraceImplBase() : _thread_id(0), _skip(0) {} size_t thread_id() const { return _thread_id; } void skip_n_firsts(size_t n) { _skip = n; } protected: void load_thread_info() { #ifdef BACKWARD_SYSTEM_LINUX #ifndef __ANDROID__ _thread_id = static_cast(syscall(SYS_gettid)); #else _thread_id = static_cast(gettid()); #endif if (_thread_id == static_cast(getpid())) { // If the thread is the main one, let's hide that. // I like to keep little secret sometimes. _thread_id = 0; } #elif defined(BACKWARD_SYSTEM_DARWIN) _thread_id = reinterpret_cast(pthread_self()); if (pthread_main_np() == 1) { // If the thread is the main one, let's hide that. _thread_id = 0; } #endif } size_t skip_n_firsts() const { return _skip; } private: size_t _thread_id; size_t _skip; }; class StackTraceImplHolder : public StackTraceImplBase { public: size_t size() const { return _stacktrace.size() ? _stacktrace.size() - skip_n_firsts() : 0; } Trace operator[](size_t idx) const { if (idx >= size()) { return Trace(); } return Trace(_stacktrace[idx + skip_n_firsts()], idx); } void *const *begin() const { if (size()) { return &_stacktrace[skip_n_firsts()]; } return nullptr; } protected: std::vector _stacktrace; }; #if BACKWARD_HAS_UNWIND == 1 namespace details { template class Unwinder { public: size_t operator()(F &f, size_t depth) { _f = &f; _index = -1; _depth = depth; _Unwind_Backtrace(&this->backtrace_trampoline, this); return static_cast(_index); } private: F *_f; ssize_t _index; size_t _depth; static _Unwind_Reason_Code backtrace_trampoline(_Unwind_Context *ctx, void *self) { return (static_cast(self))->backtrace(ctx); } _Unwind_Reason_Code backtrace(_Unwind_Context *ctx) { if (_index >= 0 && static_cast(_index) >= _depth) return _URC_END_OF_STACK; int ip_before_instruction = 0; uintptr_t ip = _Unwind_GetIPInfo(ctx, &ip_before_instruction); if (!ip_before_instruction) { // calculating 0-1 for unsigned, looks like a possible bug to sanitiziers, // so let's do it explicitly: if (ip == 0) { ip = std::numeric_limits::max(); // set it to 0xffff... (as // from casting 0-1) } else { ip -= 1; // else just normally decrement it (no overflow/underflow will // happen) } } if (_index >= 0) { // ignore first frame. (*_f)(static_cast(_index), reinterpret_cast(ip)); } _index += 1; return _URC_NO_REASON; } }; template size_t unwind(F f, size_t depth) { Unwinder unwinder; return unwinder(f, depth); } } // namespace details template <> class StackTraceImpl : public StackTraceImplHolder { public: NOINLINE size_t load_here(size_t depth = 32) { load_thread_info(); if (depth == 0) { return 0; } _stacktrace.resize(depth); size_t trace_cnt = details::unwind(callback(*this), depth); _stacktrace.resize(trace_cnt); skip_n_firsts(0); return size(); } size_t load_from(void *addr, size_t depth = 32) { load_here(depth + 8); for (size_t i = 0; i < _stacktrace.size(); ++i) { if (_stacktrace[i] == addr) { skip_n_firsts(i); break; } } _stacktrace.resize(std::min(_stacktrace.size(), skip_n_firsts() + depth)); return size(); } private: struct callback { StackTraceImpl &self; callback(StackTraceImpl &_self) : self(_self) {} void operator()(size_t idx, void *addr) { self._stacktrace[idx] = addr; } }; }; #elif defined(BACKWARD_HAS_BACKTRACE) template <> class StackTraceImpl : public StackTraceImplHolder { public: NOINLINE size_t load_here(size_t depth = 32) { load_thread_info(); if (depth == 0) { return 0; } _stacktrace.resize(depth + 1); size_t trace_cnt = backtrace(&_stacktrace[0], _stacktrace.size()); _stacktrace.resize(trace_cnt); skip_n_firsts(1); return size(); } size_t load_from(void *addr, size_t depth = 32) { load_here(depth + 8); for (size_t i = 0; i < _stacktrace.size(); ++i) { if (_stacktrace[i] == addr) { skip_n_firsts(i); _stacktrace[i] = (void *)((uintptr_t)_stacktrace[i] + 1); break; } } _stacktrace.resize(std::min(_stacktrace.size(), skip_n_firsts() + depth)); return size(); } }; #elif defined(BACKWARD_SYSTEM_WINDOWS) template <> class StackTraceImpl : public StackTraceImplHolder { public: // We have to load the machine type from the image info // So we first initialize the resolver, and it tells us this info void set_machine_type(DWORD machine_type) { machine_type_ = machine_type; } void set_context(CONTEXT *ctx) { ctx_ = ctx; } void set_thread_handle(HANDLE handle) { thd_ = handle; } NOINLINE size_t load_here(size_t depth = 32) { CONTEXT localCtx; // used when no context is provided if (depth == 0) { return 0; } if (!ctx_) { ctx_ = &localCtx; RtlCaptureContext(ctx_); } if (!thd_) { thd_ = GetCurrentThread(); } HANDLE process = GetCurrentProcess(); STACKFRAME64 s; memset(&s, 0, sizeof(STACKFRAME64)); // TODO: 32 bit context capture s.AddrStack.Mode = AddrModeFlat; s.AddrFrame.Mode = AddrModeFlat; s.AddrPC.Mode = AddrModeFlat; #ifdef _M_X64 s.AddrPC.Offset = ctx_->Rip; s.AddrStack.Offset = ctx_->Rsp; s.AddrFrame.Offset = ctx_->Rbp; #else s.AddrPC.Offset = ctx_->Eip; s.AddrStack.Offset = ctx_->Esp; s.AddrFrame.Offset = ctx_->Ebp; #endif if (!machine_type_) { #ifdef _M_X64 machine_type_ = IMAGE_FILE_MACHINE_AMD64; #else machine_type_ = IMAGE_FILE_MACHINE_I386; #endif } for (;;) { // NOTE: this only works if PDBs are already loaded! SetLastError(0); if (!StackWalk64(machine_type_, process, thd_, &s, ctx_, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL)) break; if (s.AddrReturn.Offset == 0) break; _stacktrace.push_back(reinterpret_cast(s.AddrPC.Offset)); if (size() >= depth) break; } return size(); } size_t load_from(void *addr, size_t depth = 32) { load_here(depth + 8); for (size_t i = 0; i < _stacktrace.size(); ++i) { if (_stacktrace[i] == addr) { skip_n_firsts(i); break; } } _stacktrace.resize(std::min(_stacktrace.size(), skip_n_firsts() + depth)); return size(); } private: DWORD machine_type_ = 0; HANDLE thd_ = 0; CONTEXT *ctx_ = nullptr; }; #endif class StackTrace : public StackTraceImpl {}; /*************** TRACE RESOLVER ***************/ template class TraceResolverImpl; #ifdef BACKWARD_SYSTEM_UNKNOWN template <> class TraceResolverImpl { public: template void load_stacktrace(ST &) {} ResolvedTrace resolve(ResolvedTrace t) { return t; } }; #endif class TraceResolverImplBase { protected: std::string demangle(const char *funcname) { return _demangler.demangle(funcname); } private: details::demangler _demangler; }; #ifdef BACKWARD_SYSTEM_LINUX class TraceResolverLinuxBase : public TraceResolverImplBase { public: TraceResolverLinuxBase() : argv0_(get_argv0()), exec_path_(read_symlink("/proc/self/exe")) { } std::string resolve_exec_path(Dl_info &symbol_info) const { // mutates symbol_info.dli_fname to be filename to open and returns filename to display if(symbol_info.dli_fname == argv0_) { // dladdr returns argv[0] in dli_fname for symbols contained in // the main executable, which is not a valid path if the // executable was found by a search of the PATH environment // variable; In that case, we actually open /proc/self/exe, which // is always the actual executable (even if it was deleted/replaced!) // but display the path that /proc/self/exe links to. symbol_info.dli_fname = "/proc/self/exe"; return exec_path_; } else { return symbol_info.dli_fname; } } private: std::string argv0_; std::string exec_path_; static std::string get_argv0() { std::string argv0; std::ifstream ifs("/proc/self/cmdline"); std::getline(ifs, argv0, '\0'); return argv0; } static std::string read_symlink(std::string const &symlink_path) { std::string path; path.resize(100); while (true) { ssize_t len = ::readlink(symlink_path.c_str(), &*path.begin(), path.size()); if (len < 0) { return ""; } if (static_cast(len) == path.size()) { path.resize(path.size() * 2); } else { path.resize(static_cast(len)); break; } } return path; } }; template class TraceResolverLinuxImpl; #if BACKWARD_HAS_BACKTRACE_SYMBOL == 1 template <> class TraceResolverLinuxImpl : public TraceResolverLinuxBase { public: template void load_stacktrace(ST &st) { using namespace details; if (st.size() == 0) { return; } _symbols.reset(backtrace_symbols(st.begin(), (int)st.size())); } ResolvedTrace resolve(ResolvedTrace trace) { char *filename = _symbols[trace.idx]; char *funcname = filename; while (*funcname && *funcname != '(') { funcname += 1; } trace.object_filename.assign(filename, funcname); // ok even if funcname is the ending // \0 (then we assign entire string) if (*funcname) { // if it's not end of string (e.g. from last frame ip==0) funcname += 1; char *funcname_end = funcname; while (*funcname_end && *funcname_end != ')' && *funcname_end != '+') { funcname_end += 1; } *funcname_end = '\0'; trace.object_function = this->demangle(funcname); trace.source.function = trace.object_function; // we cannot do better. } return trace; } private: details::handle _symbols; }; #endif // BACKWARD_HAS_BACKTRACE_SYMBOL == 1 #if BACKWARD_HAS_BFD == 1 template <> class TraceResolverLinuxImpl : public TraceResolverLinuxBase { public: TraceResolverLinuxImpl() : _bfd_loaded(false) {} template void load_stacktrace(ST &) {} ResolvedTrace resolve(ResolvedTrace trace) { Dl_info symbol_info; // trace.addr is a virtual address in memory pointing to some code. // Let's try to find from which loaded object it comes from. // The loaded object can be yourself btw. if (!dladdr(trace.addr, &symbol_info)) { return trace; // dat broken trace... } // Now we get in symbol_info: // .dli_fname: // pathname of the shared object that contains the address. // .dli_fbase: // where the object is loaded in memory. // .dli_sname: // the name of the nearest symbol to trace.addr, we expect a // function name. // .dli_saddr: // the exact address corresponding to .dli_sname. if (symbol_info.dli_sname) { trace.object_function = demangle(symbol_info.dli_sname); } if (!symbol_info.dli_fname) { return trace; } trace.object_filename = resolve_exec_path(symbol_info); bfd_fileobject &fobj = load_object_with_bfd(symbol_info.dli_fname); if (!fobj.handle) { return trace; // sad, we couldn't load the object :( } find_sym_result *details_selected; // to be filled. // trace.addr is the next instruction to be executed after returning // from the nested stack frame. In C++ this usually relate to the next // statement right after the function call that leaded to a new stack // frame. This is not usually what you want to see when printing out a // stacktrace... find_sym_result details_call_site = find_symbol_details(fobj, trace.addr, symbol_info.dli_fbase); details_selected = &details_call_site; #if BACKWARD_HAS_UNWIND == 0 // ...this is why we also try to resolve the symbol that is right // before the return address. If we are lucky enough, we will get the // line of the function that was called. But if the code is optimized, // we might get something absolutely not related since the compiler // can reschedule the return address with inline functions and // tail-call optimisation (among other things that I don't even know // or cannot even dream about with my tiny limited brain). find_sym_result details_adjusted_call_site = find_symbol_details( fobj, (void *)(uintptr_t(trace.addr) - 1), symbol_info.dli_fbase); // In debug mode, we should always get the right thing(TM). if (details_call_site.found && details_adjusted_call_site.found) { // Ok, we assume that details_adjusted_call_site is a better estimation. details_selected = &details_adjusted_call_site; trace.addr = (void *)(uintptr_t(trace.addr) - 1); } if (details_selected == &details_call_site && details_call_site.found) { // we have to re-resolve the symbol in order to reset some // internal state in BFD... so we can call backtrace_inliners // thereafter... details_call_site = find_symbol_details(fobj, trace.addr, symbol_info.dli_fbase); } #endif // BACKWARD_HAS_UNWIND if (details_selected->found) { if (details_selected->filename) { trace.source.filename = details_selected->filename; } trace.source.line = details_selected->line; if (details_selected->funcname) { // this time we get the name of the function where the code is // located, instead of the function were the address is // located. In short, if the code was inlined, we get the // function correspoding to the code. Else we already got in // trace.function. trace.source.function = demangle(details_selected->funcname); if (!symbol_info.dli_sname) { // for the case dladdr failed to find the symbol name of // the function, we might as well try to put something // here. trace.object_function = trace.source.function; } } // Maybe the source of the trace got inlined inside the function // (trace.source.function). Let's see if we can get all the inlined // calls along the way up to the initial call site. trace.inliners = backtrace_inliners(fobj, *details_selected); #if 0 if (trace.inliners.size() == 0) { // Maybe the trace was not inlined... or maybe it was and we // are lacking the debug information. Let's try to make the // world better and see if we can get the line number of the // function (trace.source.function) now. // // We will get the location of where the function start (to be // exact: the first instruction that really start the // function), not where the name of the function is defined. // This can be quite far away from the name of the function // btw. // // If the source of the function is the same as the source of // the trace, we cannot say if the trace was really inlined or // not. However, if the filename of the source is different // between the function and the trace... we can declare it as // an inliner. This is not 100% accurate, but better than // nothing. if (symbol_info.dli_saddr) { find_sym_result details = find_symbol_details(fobj, symbol_info.dli_saddr, symbol_info.dli_fbase); if (details.found) { ResolvedTrace::SourceLoc diy_inliner; diy_inliner.line = details.line; if (details.filename) { diy_inliner.filename = details.filename; } if (details.funcname) { diy_inliner.function = demangle(details.funcname); } else { diy_inliner.function = trace.source.function; } if (diy_inliner != trace.source) { trace.inliners.push_back(diy_inliner); } } } } #endif } return trace; } private: bool _bfd_loaded; typedef details::handle> bfd_handle_t; typedef details::handle bfd_symtab_t; struct bfd_fileobject { bfd_handle_t handle; bfd_vma base_addr; bfd_symtab_t symtab; bfd_symtab_t dynamic_symtab; }; typedef details::hashtable::type fobj_bfd_map_t; fobj_bfd_map_t _fobj_bfd_map; bfd_fileobject &load_object_with_bfd(const std::string &filename_object) { using namespace details; if (!_bfd_loaded) { using namespace details; bfd_init(); _bfd_loaded = true; } fobj_bfd_map_t::iterator it = _fobj_bfd_map.find(filename_object); if (it != _fobj_bfd_map.end()) { return it->second; } // this new object is empty for now. bfd_fileobject &r = _fobj_bfd_map[filename_object]; // we do the work temporary in this one; bfd_handle_t bfd_handle; int fd = open(filename_object.c_str(), O_RDONLY); bfd_handle.reset(bfd_fdopenr(filename_object.c_str(), "default", fd)); if (!bfd_handle) { close(fd); return r; } if (!bfd_check_format(bfd_handle.get(), bfd_object)) { return r; // not an object? You lose. } if ((bfd_get_file_flags(bfd_handle.get()) & HAS_SYMS) == 0) { return r; // that's what happen when you forget to compile in debug. } ssize_t symtab_storage_size = bfd_get_symtab_upper_bound(bfd_handle.get()); ssize_t dyn_symtab_storage_size = bfd_get_dynamic_symtab_upper_bound(bfd_handle.get()); if (symtab_storage_size <= 0 && dyn_symtab_storage_size <= 0) { return r; // weird, is the file is corrupted? } bfd_symtab_t symtab, dynamic_symtab; ssize_t symcount = 0, dyn_symcount = 0; if (symtab_storage_size > 0) { symtab.reset(static_cast( malloc(static_cast(symtab_storage_size)))); symcount = bfd_canonicalize_symtab(bfd_handle.get(), symtab.get()); } if (dyn_symtab_storage_size > 0) { dynamic_symtab.reset(static_cast( malloc(static_cast(dyn_symtab_storage_size)))); dyn_symcount = bfd_canonicalize_dynamic_symtab(bfd_handle.get(), dynamic_symtab.get()); } if (symcount <= 0 && dyn_symcount <= 0) { return r; // damned, that's a stripped file that you got there! } r.handle = move(bfd_handle); r.symtab = move(symtab); r.dynamic_symtab = move(dynamic_symtab); return r; } struct find_sym_result { bool found; const char *filename; const char *funcname; unsigned int line; }; struct find_sym_context { TraceResolverLinuxImpl *self; bfd_fileobject *fobj; void *addr; void *base_addr; find_sym_result result; }; find_sym_result find_symbol_details(bfd_fileobject &fobj, void *addr, void *base_addr) { find_sym_context context; context.self = this; context.fobj = &fobj; context.addr = addr; context.base_addr = base_addr; context.result.found = false; bfd_map_over_sections(fobj.handle.get(), &find_in_section_trampoline, static_cast(&context)); return context.result; } static void find_in_section_trampoline(bfd *, asection *section, void *data) { find_sym_context *context = static_cast(data); context->self->find_in_section( reinterpret_cast(context->addr), reinterpret_cast(context->base_addr), *context->fobj, section, context->result); } void find_in_section(bfd_vma addr, bfd_vma base_addr, bfd_fileobject &fobj, asection *section, find_sym_result &result) { if (result.found) return; #ifdef bfd_get_section_flags if ((bfd_get_section_flags(fobj.handle.get(), section) & SEC_ALLOC) == 0) #else if ((bfd_section_flags(section) & SEC_ALLOC) == 0) #endif return; // a debug section is never loaded automatically. #ifdef bfd_get_section_vma bfd_vma sec_addr = bfd_get_section_vma(fobj.handle.get(), section); #else bfd_vma sec_addr = bfd_section_vma(section); #endif #ifdef bfd_get_section_size bfd_size_type size = bfd_get_section_size(section); #else bfd_size_type size = bfd_section_size(section); #endif // are we in the boundaries of the section? if (addr < sec_addr || addr >= sec_addr + size) { addr -= base_addr; // oups, a relocated object, lets try again... if (addr < sec_addr || addr >= sec_addr + size) { return; } } #if defined(__clang__) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" #endif if (!result.found && fobj.symtab) { result.found = bfd_find_nearest_line( fobj.handle.get(), section, fobj.symtab.get(), addr - sec_addr, &result.filename, &result.funcname, &result.line); } if (!result.found && fobj.dynamic_symtab) { result.found = bfd_find_nearest_line( fobj.handle.get(), section, fobj.dynamic_symtab.get(), addr - sec_addr, &result.filename, &result.funcname, &result.line); } #if defined(__clang__) #pragma clang diagnostic pop #endif } ResolvedTrace::source_locs_t backtrace_inliners(bfd_fileobject &fobj, find_sym_result previous_result) { // This function can be called ONLY after a SUCCESSFUL call to // find_symbol_details. The state is global to the bfd_handle. ResolvedTrace::source_locs_t results; while (previous_result.found) { find_sym_result result; result.found = bfd_find_inliner_info(fobj.handle.get(), &result.filename, &result.funcname, &result.line); if (result .found) /* and not ( cstrings_eq(previous_result.filename, result.filename) and cstrings_eq(previous_result.funcname, result.funcname) and result.line == previous_result.line )) */ { ResolvedTrace::SourceLoc src_loc; src_loc.line = result.line; if (result.filename) { src_loc.filename = result.filename; } if (result.funcname) { src_loc.function = demangle(result.funcname); } results.push_back(src_loc); } previous_result = result; } return results; } bool cstrings_eq(const char *a, const char *b) { if (!a || !b) { return false; } return strcmp(a, b) == 0; } }; #endif // BACKWARD_HAS_BFD == 1 #if BACKWARD_HAS_DW == 1 template <> class TraceResolverLinuxImpl : public TraceResolverLinuxBase { public: TraceResolverLinuxImpl() : _dwfl_handle_initialized(false) {} template void load_stacktrace(ST &) {} ResolvedTrace resolve(ResolvedTrace trace) { using namespace details; Dwarf_Addr trace_addr = (Dwarf_Addr)trace.addr; if (!_dwfl_handle_initialized) { // initialize dwfl... _dwfl_cb.reset(new Dwfl_Callbacks); _dwfl_cb->find_elf = &dwfl_linux_proc_find_elf; _dwfl_cb->find_debuginfo = &dwfl_standard_find_debuginfo; _dwfl_cb->debuginfo_path = 0; _dwfl_handle.reset(dwfl_begin(_dwfl_cb.get())); _dwfl_handle_initialized = true; if (!_dwfl_handle) { return trace; } // ...from the current process. dwfl_report_begin(_dwfl_handle.get()); int r = dwfl_linux_proc_report(_dwfl_handle.get(), getpid()); dwfl_report_end(_dwfl_handle.get(), NULL, NULL); if (r < 0) { return trace; } } if (!_dwfl_handle) { return trace; } // find the module (binary object) that contains the trace's address. // This is not using any debug information, but the addresses ranges of // all the currently loaded binary object. Dwfl_Module *mod = dwfl_addrmodule(_dwfl_handle.get(), trace_addr); if (mod) { // now that we found it, lets get the name of it, this will be the // full path to the running binary or one of the loaded library. const char *module_name = dwfl_module_info(mod, 0, 0, 0, 0, 0, 0, 0); if (module_name) { trace.object_filename = module_name; } // We also look after the name of the symbol, equal or before this // address. This is found by walking the symtab. We should get the // symbol corresponding to the function (mangled) containing the // address. If the code corresponding to the address was inlined, // this is the name of the out-most inliner function. const char *sym_name = dwfl_module_addrname(mod, trace_addr); if (sym_name) { trace.object_function = demangle(sym_name); } } // now let's get serious, and find out the source location (file and // line number) of the address. // This function will look in .debug_aranges for the address and map it // to the location of the compilation unit DIE in .debug_info and // return it. Dwarf_Addr mod_bias = 0; Dwarf_Die *cudie = dwfl_module_addrdie(mod, trace_addr, &mod_bias); #if 1 if (!cudie) { // Sadly clang does not generate the section .debug_aranges, thus // dwfl_module_addrdie will fail early. Clang doesn't either set // the lowpc/highpc/range info for every compilation unit. // // So in order to save the world: // for every compilation unit, we will iterate over every single // DIEs. Normally functions should have a lowpc/highpc/range, which // we will use to infer the compilation unit. // note that this is probably badly inefficient. while ((cudie = dwfl_module_nextcu(mod, cudie, &mod_bias))) { Dwarf_Die die_mem; Dwarf_Die *fundie = find_fundie_by_pc(cudie, trace_addr - mod_bias, &die_mem); if (fundie) { break; } } } #endif //#define BACKWARD_I_DO_NOT_RECOMMEND_TO_ENABLE_THIS_HORRIBLE_PIECE_OF_CODE #ifdef BACKWARD_I_DO_NOT_RECOMMEND_TO_ENABLE_THIS_HORRIBLE_PIECE_OF_CODE if (!cudie) { // If it's still not enough, lets dive deeper in the shit, and try // to save the world again: for every compilation unit, we will // load the corresponding .debug_line section, and see if we can // find our address in it. Dwarf_Addr cfi_bias; Dwarf_CFI *cfi_cache = dwfl_module_eh_cfi(mod, &cfi_bias); Dwarf_Addr bias; while ((cudie = dwfl_module_nextcu(mod, cudie, &bias))) { if (dwarf_getsrc_die(cudie, trace_addr - bias)) { // ...but if we get a match, it might be a false positive // because our (address - bias) might as well be valid in a // different compilation unit. So we throw our last card on // the table and lookup for the address into the .eh_frame // section. handle frame; dwarf_cfi_addrframe(cfi_cache, trace_addr - cfi_bias, &frame); if (frame) { break; } } } } #endif if (!cudie) { return trace; // this time we lost the game :/ } // Now that we have a compilation unit DIE, this function will be able // to load the corresponding section in .debug_line (if not already // loaded) and hopefully find the source location mapped to our // address. Dwarf_Line *srcloc = dwarf_getsrc_die(cudie, trace_addr - mod_bias); if (srcloc) { const char *srcfile = dwarf_linesrc(srcloc, 0, 0); if (srcfile) { trace.source.filename = srcfile; } int line = 0, col = 0; dwarf_lineno(srcloc, &line); dwarf_linecol(srcloc, &col); trace.source.line = line; trace.source.col = col; } deep_first_search_by_pc(cudie, trace_addr - mod_bias, inliners_search_cb(trace)); if (trace.source.function.size() == 0) { // fallback. trace.source.function = trace.object_function; } return trace; } private: typedef details::handle> dwfl_handle_t; details::handle> _dwfl_cb; dwfl_handle_t _dwfl_handle; bool _dwfl_handle_initialized; // defined here because in C++98, template function cannot take locally // defined types... grrr. struct inliners_search_cb { void operator()(Dwarf_Die *die) { switch (dwarf_tag(die)) { const char *name; case DW_TAG_subprogram: if ((name = dwarf_diename(die))) { trace.source.function = name; } break; case DW_TAG_inlined_subroutine: ResolvedTrace::SourceLoc sloc; Dwarf_Attribute attr_mem; if ((name = dwarf_diename(die))) { sloc.function = name; } if ((name = die_call_file(die))) { sloc.filename = name; } Dwarf_Word line = 0, col = 0; dwarf_formudata(dwarf_attr(die, DW_AT_call_line, &attr_mem), &line); dwarf_formudata(dwarf_attr(die, DW_AT_call_column, &attr_mem), &col); sloc.line = (unsigned)line; sloc.col = (unsigned)col; trace.inliners.push_back(sloc); break; }; } ResolvedTrace &trace; inliners_search_cb(ResolvedTrace &t) : trace(t) {} }; static bool die_has_pc(Dwarf_Die *die, Dwarf_Addr pc) { Dwarf_Addr low, high; // continuous range if (dwarf_hasattr(die, DW_AT_low_pc) && dwarf_hasattr(die, DW_AT_high_pc)) { if (dwarf_lowpc(die, &low) != 0) { return false; } if (dwarf_highpc(die, &high) != 0) { Dwarf_Attribute attr_mem; Dwarf_Attribute *attr = dwarf_attr(die, DW_AT_high_pc, &attr_mem); Dwarf_Word value; if (dwarf_formudata(attr, &value) != 0) { return false; } high = low + value; } return pc >= low && pc < high; } // non-continuous range. Dwarf_Addr base; ptrdiff_t offset = 0; while ((offset = dwarf_ranges(die, offset, &base, &low, &high)) > 0) { if (pc >= low && pc < high) { return true; } } return false; } static Dwarf_Die *find_fundie_by_pc(Dwarf_Die *parent_die, Dwarf_Addr pc, Dwarf_Die *result) { if (dwarf_child(parent_die, result) != 0) { return 0; } Dwarf_Die *die = result; do { switch (dwarf_tag(die)) { case DW_TAG_subprogram: case DW_TAG_inlined_subroutine: if (die_has_pc(die, pc)) { return result; } }; bool declaration = false; Dwarf_Attribute attr_mem; dwarf_formflag(dwarf_attr(die, DW_AT_declaration, &attr_mem), &declaration); if (!declaration) { // let's be curious and look deeper in the tree, // function are not necessarily at the first level, but // might be nested inside a namespace, structure etc. Dwarf_Die die_mem; Dwarf_Die *indie = find_fundie_by_pc(die, pc, &die_mem); if (indie) { *result = die_mem; return result; } } } while (dwarf_siblingof(die, result) == 0); return 0; } template static bool deep_first_search_by_pc(Dwarf_Die *parent_die, Dwarf_Addr pc, CB cb) { Dwarf_Die die_mem; if (dwarf_child(parent_die, &die_mem) != 0) { return false; } bool branch_has_pc = false; Dwarf_Die *die = &die_mem; do { bool declaration = false; Dwarf_Attribute attr_mem; dwarf_formflag(dwarf_attr(die, DW_AT_declaration, &attr_mem), &declaration); if (!declaration) { // let's be curious and look deeper in the tree, function are // not necessarily at the first level, but might be nested // inside a namespace, structure, a function, an inlined // function etc. branch_has_pc = deep_first_search_by_pc(die, pc, cb); } if (!branch_has_pc) { branch_has_pc = die_has_pc(die, pc); } if (branch_has_pc) { cb(die); } } while (dwarf_siblingof(die, &die_mem) == 0); return branch_has_pc; } static const char *die_call_file(Dwarf_Die *die) { Dwarf_Attribute attr_mem; Dwarf_Sword file_idx = 0; dwarf_formsdata(dwarf_attr(die, DW_AT_call_file, &attr_mem), &file_idx); if (file_idx == 0) { return 0; } Dwarf_Die die_mem; Dwarf_Die *cudie = dwarf_diecu(die, &die_mem, 0, 0); if (!cudie) { return 0; } Dwarf_Files *files = 0; size_t nfiles; dwarf_getsrcfiles(cudie, &files, &nfiles); if (!files) { return 0; } return dwarf_filesrc(files, file_idx, 0, 0); } }; #endif // BACKWARD_HAS_DW == 1 #if BACKWARD_HAS_DWARF == 1 template <> class TraceResolverLinuxImpl : public TraceResolverLinuxBase { public: TraceResolverLinuxImpl() : _dwarf_loaded(false) {} template void load_stacktrace(ST &) {} ResolvedTrace resolve(ResolvedTrace trace) { // trace.addr is a virtual address in memory pointing to some code. // Let's try to find from which loaded object it comes from. // The loaded object can be yourself btw. Dl_info symbol_info; int dladdr_result = 0; #if defined(__GLIBC__) link_map *link_map; // We request the link map so we can get information about offsets dladdr_result = dladdr1(trace.addr, &symbol_info, reinterpret_cast(&link_map), RTLD_DL_LINKMAP); #else // Android doesn't have dladdr1. Don't use the linker map. dladdr_result = dladdr(trace.addr, &symbol_info); #endif if (!dladdr_result) { return trace; // dat broken trace... } // Now we get in symbol_info: // .dli_fname: // pathname of the shared object that contains the address. // .dli_fbase: // where the object is loaded in memory. // .dli_sname: // the name of the nearest symbol to trace.addr, we expect a // function name. // .dli_saddr: // the exact address corresponding to .dli_sname. // // And in link_map: // .l_addr: // difference between the address in the ELF file and the address // in memory // l_name: // absolute pathname where the object was found if (symbol_info.dli_sname) { trace.object_function = demangle(symbol_info.dli_sname); } if (!symbol_info.dli_fname) { return trace; } trace.object_filename = resolve_exec_path(symbol_info); dwarf_fileobject &fobj = load_object_with_dwarf(symbol_info.dli_fname); if (!fobj.dwarf_handle) { return trace; // sad, we couldn't load the object :( } #if defined(__GLIBC__) // Convert the address to a module relative one by looking at // the module's loading address in the link map Dwarf_Addr address = reinterpret_cast(trace.addr) - reinterpret_cast(link_map->l_addr); #else Dwarf_Addr address = reinterpret_cast(trace.addr); #endif if (trace.object_function.empty()) { symbol_cache_t::iterator it = fobj.symbol_cache.lower_bound(address); if (it != fobj.symbol_cache.end()) { if (it->first != address) { if (it != fobj.symbol_cache.begin()) { --it; } } trace.object_function = demangle(it->second.c_str()); } } // Get the Compilation Unit DIE for the address Dwarf_Die die = find_die(fobj, address); if (!die) { return trace; // this time we lost the game :/ } // libdwarf doesn't give us direct access to its objects, it always // allocates a copy for the caller. We keep that copy alive in a cache // and we deallocate it later when it's no longer required. die_cache_entry &die_object = get_die_cache(fobj, die); if (die_object.isEmpty()) return trace; // We have no line section for this DIE die_linemap_t::iterator it = die_object.line_section.lower_bound(address); if (it != die_object.line_section.end()) { if (it->first != address) { if (it == die_object.line_section.begin()) { // If we are on the first item of the line section // but the address does not match it means that // the address is below the range of the DIE. Give up. return trace; } else { --it; } } } else { return trace; // We didn't find the address. } // Get the Dwarf_Line that the address points to and call libdwarf // to get source file, line and column info. Dwarf_Line line = die_object.line_buffer[it->second]; Dwarf_Error error = DW_DLE_NE; char *filename; if (dwarf_linesrc(line, &filename, &error) == DW_DLV_OK) { trace.source.filename = std::string(filename); dwarf_dealloc(fobj.dwarf_handle.get(), filename, DW_DLA_STRING); } Dwarf_Unsigned number = 0; if (dwarf_lineno(line, &number, &error) == DW_DLV_OK) { trace.source.line = number; } else { trace.source.line = 0; } if (dwarf_lineoff_b(line, &number, &error) == DW_DLV_OK) { trace.source.col = number; } else { trace.source.col = 0; } std::vector namespace_stack; deep_first_search_by_pc(fobj, die, address, namespace_stack, inliners_search_cb(trace, fobj, die)); dwarf_dealloc(fobj.dwarf_handle.get(), die, DW_DLA_DIE); return trace; } public: static int close_dwarf(Dwarf_Debug dwarf) { return dwarf_finish(dwarf, NULL); } private: bool _dwarf_loaded; typedef details::handle> dwarf_file_t; typedef details::handle> dwarf_elf_t; typedef details::handle> dwarf_handle_t; typedef std::map die_linemap_t; typedef std::map die_specmap_t; struct die_cache_entry { die_specmap_t spec_section; die_linemap_t line_section; Dwarf_Line *line_buffer; Dwarf_Signed line_count; Dwarf_Line_Context line_context; inline bool isEmpty() { return line_buffer == NULL || line_count == 0 || line_context == NULL || line_section.empty(); } die_cache_entry() : line_buffer(0), line_count(0), line_context(0) {} ~die_cache_entry() { if (line_context) { dwarf_srclines_dealloc_b(line_context); } } }; typedef std::map die_cache_t; typedef std::map symbol_cache_t; struct dwarf_fileobject { dwarf_file_t file_handle; dwarf_elf_t elf_handle; dwarf_handle_t dwarf_handle; symbol_cache_t symbol_cache; // Die cache die_cache_t die_cache; die_cache_entry *current_cu; }; typedef details::hashtable::type fobj_dwarf_map_t; fobj_dwarf_map_t _fobj_dwarf_map; static bool cstrings_eq(const char *a, const char *b) { if (!a || !b) { return false; } return strcmp(a, b) == 0; } dwarf_fileobject &load_object_with_dwarf(const std::string &filename_object) { if (!_dwarf_loaded) { // Set the ELF library operating version // If that fails there's nothing we can do _dwarf_loaded = elf_version(EV_CURRENT) != EV_NONE; } fobj_dwarf_map_t::iterator it = _fobj_dwarf_map.find(filename_object); if (it != _fobj_dwarf_map.end()) { return it->second; } // this new object is empty for now dwarf_fileobject &r = _fobj_dwarf_map[filename_object]; dwarf_file_t file_handle; file_handle.reset(open(filename_object.c_str(), O_RDONLY)); if (file_handle.get() < 0) { return r; } // Try to get an ELF handle. We need to read the ELF sections // because we want to see if there is a .gnu_debuglink section // that points to a split debug file dwarf_elf_t elf_handle; elf_handle.reset(elf_begin(file_handle.get(), ELF_C_READ, NULL)); if (!elf_handle) { return r; } const char *e_ident = elf_getident(elf_handle.get(), 0); if (!e_ident) { return r; } // Get the number of sections // We use the new APIs as elf_getshnum is deprecated size_t shdrnum = 0; if (elf_getshdrnum(elf_handle.get(), &shdrnum) == -1) { return r; } // Get the index to the string section size_t shdrstrndx = 0; if (elf_getshdrstrndx(elf_handle.get(), &shdrstrndx) == -1) { return r; } std::string debuglink; // Iterate through the ELF sections to try to get a gnu_debuglink // note and also to cache the symbol table. // We go the preprocessor way to avoid having to create templated // classes or using gelf (which might throw a compiler error if 64 bit // is not supported #define ELF_GET_DATA(ARCH) \ Elf_Scn *elf_section = 0; \ Elf_Data *elf_data = 0; \ Elf##ARCH##_Shdr *section_header = 0; \ Elf_Scn *symbol_section = 0; \ size_t symbol_count = 0; \ size_t symbol_strings = 0; \ Elf##ARCH##_Sym *symbol = 0; \ const char *section_name = 0; \ \ while ((elf_section = elf_nextscn(elf_handle.get(), elf_section)) != NULL) { \ section_header = elf##ARCH##_getshdr(elf_section); \ if (section_header == NULL) { \ return r; \ } \ \ if ((section_name = elf_strptr(elf_handle.get(), shdrstrndx, \ section_header->sh_name)) == NULL) { \ return r; \ } \ \ if (cstrings_eq(section_name, ".gnu_debuglink")) { \ elf_data = elf_getdata(elf_section, NULL); \ if (elf_data && elf_data->d_size > 0) { \ debuglink = \ std::string(reinterpret_cast(elf_data->d_buf)); \ } \ } \ \ switch (section_header->sh_type) { \ case SHT_SYMTAB: \ symbol_section = elf_section; \ symbol_count = section_header->sh_size / section_header->sh_entsize; \ symbol_strings = section_header->sh_link; \ break; \ \ /* We use .dynsyms as a last resort, we prefer .symtab */ \ case SHT_DYNSYM: \ if (!symbol_section) { \ symbol_section = elf_section; \ symbol_count = section_header->sh_size / section_header->sh_entsize; \ symbol_strings = section_header->sh_link; \ } \ break; \ } \ } \ \ if (symbol_section && symbol_count && symbol_strings) { \ elf_data = elf_getdata(symbol_section, NULL); \ symbol = reinterpret_cast(elf_data->d_buf); \ for (size_t i = 0; i < symbol_count; ++i) { \ int type = ELF##ARCH##_ST_TYPE(symbol->st_info); \ if (type == STT_FUNC && symbol->st_value > 0) { \ r.symbol_cache[symbol->st_value] = std::string( \ elf_strptr(elf_handle.get(), symbol_strings, symbol->st_name)); \ } \ ++symbol; \ } \ } if (e_ident[EI_CLASS] == ELFCLASS32) { ELF_GET_DATA(32) } else if (e_ident[EI_CLASS] == ELFCLASS64) { // libelf might have been built without 64 bit support #if __LIBELF64 ELF_GET_DATA(64) #endif } if (!debuglink.empty()) { // We have a debuglink section! Open an elf instance on that // file instead. If we can't open the file, then return // the elf handle we had already opened. dwarf_file_t debuglink_file; debuglink_file.reset(open(debuglink.c_str(), O_RDONLY)); if (debuglink_file.get() > 0) { dwarf_elf_t debuglink_elf; debuglink_elf.reset(elf_begin(debuglink_file.get(), ELF_C_READ, NULL)); // If we have a valid elf handle, return the new elf handle // and file handle and discard the original ones if (debuglink_elf) { elf_handle = move(debuglink_elf); file_handle = move(debuglink_file); } } } // Ok, we have a valid ELF handle, let's try to get debug symbols Dwarf_Debug dwarf_debug; Dwarf_Error error = DW_DLE_NE; dwarf_handle_t dwarf_handle; int dwarf_result = dwarf_elf_init(elf_handle.get(), DW_DLC_READ, NULL, NULL, &dwarf_debug, &error); // We don't do any special handling for DW_DLV_NO_ENTRY specially. // If we get an error, or the file doesn't have debug information // we just return. if (dwarf_result != DW_DLV_OK) { return r; } dwarf_handle.reset(dwarf_debug); r.file_handle = move(file_handle); r.elf_handle = move(elf_handle); r.dwarf_handle = move(dwarf_handle); return r; } die_cache_entry &get_die_cache(dwarf_fileobject &fobj, Dwarf_Die die) { Dwarf_Error error = DW_DLE_NE; // Get the die offset, we use it as the cache key Dwarf_Off die_offset; if (dwarf_dieoffset(die, &die_offset, &error) != DW_DLV_OK) { die_offset = 0; } die_cache_t::iterator it = fobj.die_cache.find(die_offset); if (it != fobj.die_cache.end()) { fobj.current_cu = &it->second; return it->second; } die_cache_entry &de = fobj.die_cache[die_offset]; fobj.current_cu = &de; Dwarf_Addr line_addr; Dwarf_Small table_count; // The addresses in the line section are not fully sorted (they might // be sorted by block of code belonging to the same file), which makes // it necessary to do so before searching is possible. // // As libdwarf allocates a copy of everything, let's get the contents // of the line section and keep it around. We also create a map of // program counter to line table indices so we can search by address // and get the line buffer index. // // To make things more difficult, the same address can span more than // one line, so we need to keep the index pointing to the first line // by using insert instead of the map's [ operator. // Get the line context for the DIE if (dwarf_srclines_b(die, 0, &table_count, &de.line_context, &error) == DW_DLV_OK) { // Get the source lines for this line context, to be deallocated // later if (dwarf_srclines_from_linecontext(de.line_context, &de.line_buffer, &de.line_count, &error) == DW_DLV_OK) { // Add all the addresses to our map for (int i = 0; i < de.line_count; i++) { if (dwarf_lineaddr(de.line_buffer[i], &line_addr, &error) != DW_DLV_OK) { line_addr = 0; } de.line_section.insert(std::pair(line_addr, i)); } } } // For each CU, cache the function DIEs that contain the // DW_AT_specification attribute. When building with -g3 the function // DIEs are separated in declaration and specification, with the // declaration containing only the name and parameters and the // specification the low/high pc and other compiler attributes. // // We cache those specifications so we don't skip over the declarations, // because they have no pc, and we can do namespace resolution for // DWARF function names. Dwarf_Debug dwarf = fobj.dwarf_handle.get(); Dwarf_Die current_die = 0; if (dwarf_child(die, ¤t_die, &error) == DW_DLV_OK) { for (;;) { Dwarf_Die sibling_die = 0; Dwarf_Half tag_value; dwarf_tag(current_die, &tag_value, &error); if (tag_value == DW_TAG_subprogram || tag_value == DW_TAG_inlined_subroutine) { Dwarf_Bool has_attr = 0; if (dwarf_hasattr(current_die, DW_AT_specification, &has_attr, &error) == DW_DLV_OK) { if (has_attr) { Dwarf_Attribute attr_mem; if (dwarf_attr(current_die, DW_AT_specification, &attr_mem, &error) == DW_DLV_OK) { Dwarf_Off spec_offset = 0; if (dwarf_formref(attr_mem, &spec_offset, &error) == DW_DLV_OK) { Dwarf_Off spec_die_offset; if (dwarf_dieoffset(current_die, &spec_die_offset, &error) == DW_DLV_OK) { de.spec_section[spec_offset] = spec_die_offset; } } } dwarf_dealloc(dwarf, attr_mem, DW_DLA_ATTR); } } } int result = dwarf_siblingof(dwarf, current_die, &sibling_die, &error); if (result == DW_DLV_ERROR) { break; } else if (result == DW_DLV_NO_ENTRY) { break; } if (current_die != die) { dwarf_dealloc(dwarf, current_die, DW_DLA_DIE); current_die = 0; } current_die = sibling_die; } } return de; } static Dwarf_Die get_referenced_die(Dwarf_Debug dwarf, Dwarf_Die die, Dwarf_Half attr, bool global) { Dwarf_Error error = DW_DLE_NE; Dwarf_Attribute attr_mem; Dwarf_Die found_die = NULL; if (dwarf_attr(die, attr, &attr_mem, &error) == DW_DLV_OK) { Dwarf_Off offset; int result = 0; if (global) { result = dwarf_global_formref(attr_mem, &offset, &error); } else { result = dwarf_formref(attr_mem, &offset, &error); } if (result == DW_DLV_OK) { if (dwarf_offdie(dwarf, offset, &found_die, &error) != DW_DLV_OK) { found_die = NULL; } } dwarf_dealloc(dwarf, attr_mem, DW_DLA_ATTR); } return found_die; } static std::string get_referenced_die_name(Dwarf_Debug dwarf, Dwarf_Die die, Dwarf_Half attr, bool global) { Dwarf_Error error = DW_DLE_NE; std::string value; Dwarf_Die found_die = get_referenced_die(dwarf, die, attr, global); if (found_die) { char *name; if (dwarf_diename(found_die, &name, &error) == DW_DLV_OK) { if (name) { value = std::string(name); } dwarf_dealloc(dwarf, name, DW_DLA_STRING); } dwarf_dealloc(dwarf, found_die, DW_DLA_DIE); } return value; } // Returns a spec DIE linked to the passed one. The caller should // deallocate the DIE static Dwarf_Die get_spec_die(dwarf_fileobject &fobj, Dwarf_Die die) { Dwarf_Debug dwarf = fobj.dwarf_handle.get(); Dwarf_Error error = DW_DLE_NE; Dwarf_Off die_offset; if (fobj.current_cu && dwarf_die_CU_offset(die, &die_offset, &error) == DW_DLV_OK) { die_specmap_t::iterator it = fobj.current_cu->spec_section.find(die_offset); // If we have a DIE that completes the current one, check if // that one has the pc we are looking for if (it != fobj.current_cu->spec_section.end()) { Dwarf_Die spec_die = 0; if (dwarf_offdie(dwarf, it->second, &spec_die, &error) == DW_DLV_OK) { return spec_die; } } } // Maybe we have an abstract origin DIE with the function information? return get_referenced_die(fobj.dwarf_handle.get(), die, DW_AT_abstract_origin, true); } static bool die_has_pc(dwarf_fileobject &fobj, Dwarf_Die die, Dwarf_Addr pc) { Dwarf_Addr low_pc = 0, high_pc = 0; Dwarf_Half high_pc_form = 0; Dwarf_Form_Class return_class; Dwarf_Error error = DW_DLE_NE; Dwarf_Debug dwarf = fobj.dwarf_handle.get(); bool has_lowpc = false; bool has_highpc = false; bool has_ranges = false; if (dwarf_lowpc(die, &low_pc, &error) == DW_DLV_OK) { // If we have a low_pc check if there is a high pc. // If we don't have a high pc this might mean we have a base // address for the ranges list or just an address. has_lowpc = true; if (dwarf_highpc_b(die, &high_pc, &high_pc_form, &return_class, &error) == DW_DLV_OK) { // We do have a high pc. In DWARF 4+ this is an offset from the // low pc, but in earlier versions it's an absolute address. has_highpc = true; // In DWARF 2/3 this would be a DW_FORM_CLASS_ADDRESS if (return_class == DW_FORM_CLASS_CONSTANT) { high_pc = low_pc + high_pc; } // We have low and high pc, check if our address // is in that range return pc >= low_pc && pc < high_pc; } } else { // Reset the low_pc, in case dwarf_lowpc failing set it to some // undefined value. low_pc = 0; } // Check if DW_AT_ranges is present and search for the PC in the // returned ranges list. We always add the low_pc, as it not set it will // be 0, in case we had a DW_AT_low_pc and DW_AT_ranges pair bool result = false; Dwarf_Attribute attr; if (dwarf_attr(die, DW_AT_ranges, &attr, &error) == DW_DLV_OK) { Dwarf_Off offset; if (dwarf_global_formref(attr, &offset, &error) == DW_DLV_OK) { Dwarf_Ranges *ranges; Dwarf_Signed ranges_count = 0; Dwarf_Unsigned byte_count = 0; if (dwarf_get_ranges_a(dwarf, offset, die, &ranges, &ranges_count, &byte_count, &error) == DW_DLV_OK) { has_ranges = ranges_count != 0; for (int i = 0; i < ranges_count; i++) { if (ranges[i].dwr_addr1 != 0 && pc >= ranges[i].dwr_addr1 + low_pc && pc < ranges[i].dwr_addr2 + low_pc) { result = true; break; } } dwarf_ranges_dealloc(dwarf, ranges, ranges_count); } } } // Last attempt. We might have a single address set as low_pc. if (!result && low_pc != 0 && pc == low_pc) { result = true; } // If we don't have lowpc, highpc and ranges maybe this DIE is a // declaration that relies on a DW_AT_specification DIE that happens // later. Use the specification cache we filled when we loaded this CU. if (!result && (!has_lowpc && !has_highpc && !has_ranges)) { Dwarf_Die spec_die = get_spec_die(fobj, die); if (spec_die) { result = die_has_pc(fobj, spec_die, pc); dwarf_dealloc(dwarf, spec_die, DW_DLA_DIE); } } return result; } static void get_type(Dwarf_Debug dwarf, Dwarf_Die die, std::string &type) { Dwarf_Error error = DW_DLE_NE; Dwarf_Die child = 0; if (dwarf_child(die, &child, &error) == DW_DLV_OK) { get_type(dwarf, child, type); } if (child) { type.insert(0, "::"); dwarf_dealloc(dwarf, child, DW_DLA_DIE); } char *name; if (dwarf_diename(die, &name, &error) == DW_DLV_OK) { type.insert(0, std::string(name)); dwarf_dealloc(dwarf, name, DW_DLA_STRING); } else { type.insert(0, ""); } } static std::string get_type_by_signature(Dwarf_Debug dwarf, Dwarf_Die die) { Dwarf_Error error = DW_DLE_NE; Dwarf_Sig8 signature; Dwarf_Bool has_attr = 0; if (dwarf_hasattr(die, DW_AT_signature, &has_attr, &error) == DW_DLV_OK) { if (has_attr) { Dwarf_Attribute attr_mem; if (dwarf_attr(die, DW_AT_signature, &attr_mem, &error) == DW_DLV_OK) { if (dwarf_formsig8(attr_mem, &signature, &error) != DW_DLV_OK) { return std::string(""); } } dwarf_dealloc(dwarf, attr_mem, DW_DLA_ATTR); } } Dwarf_Unsigned next_cu_header; Dwarf_Sig8 tu_signature; std::string result; bool found = false; while (dwarf_next_cu_header_d(dwarf, 0, 0, 0, 0, 0, 0, 0, &tu_signature, 0, &next_cu_header, 0, &error) == DW_DLV_OK) { if (strncmp(signature.signature, tu_signature.signature, 8) == 0) { Dwarf_Die type_cu_die = 0; if (dwarf_siblingof_b(dwarf, 0, 0, &type_cu_die, &error) == DW_DLV_OK) { Dwarf_Die child_die = 0; if (dwarf_child(type_cu_die, &child_die, &error) == DW_DLV_OK) { get_type(dwarf, child_die, result); found = !result.empty(); dwarf_dealloc(dwarf, child_die, DW_DLA_DIE); } dwarf_dealloc(dwarf, type_cu_die, DW_DLA_DIE); } } } if (found) { while (dwarf_next_cu_header_d(dwarf, 0, 0, 0, 0, 0, 0, 0, 0, 0, &next_cu_header, 0, &error) == DW_DLV_OK) { // Reset the cu header state. Unfortunately, libdwarf's // next_cu_header API keeps its own iterator per Dwarf_Debug // that can't be reset. We need to keep fetching elements until // the end. } } else { // If we couldn't resolve the type just print out the signature std::ostringstream string_stream; string_stream << "<0x" << std::hex << std::setfill('0'); for (int i = 0; i < 8; ++i) { string_stream << std::setw(2) << std::hex << (int)(unsigned char)(signature.signature[i]); } string_stream << ">"; result = string_stream.str(); } return result; } struct type_context_t { bool is_const; bool is_typedef; bool has_type; bool has_name; std::string text; type_context_t() : is_const(false), is_typedef(false), has_type(false), has_name(false) { } }; // Types are resolved from right to left: we get the variable name first // and then all specifiers (like const or pointer) in a chain of DW_AT_type // DIEs. Call this function recursively until we get a complete type // string. static void set_parameter_string(dwarf_fileobject &fobj, Dwarf_Die die, type_context_t &context) { char *name; Dwarf_Error error = DW_DLE_NE; // typedefs contain also the base type, so we skip it and only // print the typedef name if (!context.is_typedef) { if (dwarf_diename(die, &name, &error) == DW_DLV_OK) { if (!context.text.empty()) { context.text.insert(0, " "); } context.text.insert(0, std::string(name)); dwarf_dealloc(fobj.dwarf_handle.get(), name, DW_DLA_STRING); } } else { context.is_typedef = false; context.has_type = true; if (context.is_const) { context.text.insert(0, "const "); context.is_const = false; } } bool next_type_is_const = false; bool is_keyword = true; Dwarf_Half tag = 0; Dwarf_Bool has_attr = 0; if (dwarf_tag(die, &tag, &error) == DW_DLV_OK) { switch (tag) { case DW_TAG_structure_type: case DW_TAG_union_type: case DW_TAG_class_type: case DW_TAG_enumeration_type: context.has_type = true; if (dwarf_hasattr(die, DW_AT_signature, &has_attr, &error) == DW_DLV_OK) { // If we have a signature it means the type is defined // in .debug_types, so we need to load the DIE pointed // at by the signature and resolve it if (has_attr) { std::string type = get_type_by_signature(fobj.dwarf_handle.get(), die); if (context.is_const) type.insert(0, "const "); if (!context.text.empty()) context.text.insert(0, " "); context.text.insert(0, type); } // Treat enums like typedefs, and skip printing its // base type context.is_typedef = (tag == DW_TAG_enumeration_type); } break; case DW_TAG_const_type: next_type_is_const = true; break; case DW_TAG_pointer_type: context.text.insert(0, "*"); break; case DW_TAG_reference_type: context.text.insert(0, "&"); break; case DW_TAG_restrict_type: context.text.insert(0, "restrict "); break; case DW_TAG_rvalue_reference_type: context.text.insert(0, "&&"); break; case DW_TAG_volatile_type: context.text.insert(0, "volatile "); break; case DW_TAG_typedef: // Propagate the const-ness to the next type // as typedefs are linked to its base type next_type_is_const = context.is_const; context.is_typedef = true; context.has_type = true; break; case DW_TAG_base_type: context.has_type = true; break; case DW_TAG_formal_parameter: context.has_name = true; break; default: is_keyword = false; break; } } if (!is_keyword && context.is_const) { context.text.insert(0, "const "); } context.is_const = next_type_is_const; Dwarf_Die ref = get_referenced_die(fobj.dwarf_handle.get(), die, DW_AT_type, true); if (ref) { set_parameter_string(fobj, ref, context); dwarf_dealloc(fobj.dwarf_handle.get(), ref, DW_DLA_DIE); } if (!context.has_type && context.has_name) { context.text.insert(0, "void "); context.has_type = true; } } // Resolve the function return type and parameters static void set_function_parameters(std::string &function_name, std::vector &ns, dwarf_fileobject &fobj, Dwarf_Die die) { Dwarf_Debug dwarf = fobj.dwarf_handle.get(); Dwarf_Error error = DW_DLE_NE; Dwarf_Die current_die = 0; std::string parameters; bool has_spec = true; // Check if we have a spec DIE. If we do we use it as it contains // more information, like parameter names. Dwarf_Die spec_die = get_spec_die(fobj, die); if (!spec_die) { has_spec = false; spec_die = die; } std::vector::const_iterator it = ns.begin(); std::string ns_name; for (it = ns.begin(); it < ns.end(); ++it) { ns_name.append(*it).append("::"); } if (!ns_name.empty()) { function_name.insert(0, ns_name); } // See if we have a function return type. It can be either on the // current die or in its spec one (usually true for inlined functions) std::string return_type = get_referenced_die_name(dwarf, die, DW_AT_type, true); if (return_type.empty()) { return_type = get_referenced_die_name(dwarf, spec_die, DW_AT_type, true); } if (!return_type.empty()) { return_type.append(" "); function_name.insert(0, return_type); } if (dwarf_child(spec_die, ¤t_die, &error) == DW_DLV_OK) { for (;;) { Dwarf_Die sibling_die = 0; Dwarf_Half tag_value; dwarf_tag(current_die, &tag_value, &error); if (tag_value == DW_TAG_formal_parameter) { // Ignore artificial (ie, compiler generated) parameters bool is_artificial = false; Dwarf_Attribute attr_mem; if (dwarf_attr(current_die, DW_AT_artificial, &attr_mem, &error) == DW_DLV_OK) { Dwarf_Bool flag = 0; if (dwarf_formflag(attr_mem, &flag, &error) == DW_DLV_OK) { is_artificial = flag != 0; } dwarf_dealloc(dwarf, attr_mem, DW_DLA_ATTR); } if (!is_artificial) { type_context_t context; set_parameter_string(fobj, current_die, context); if (parameters.empty()) { parameters.append("("); } else { parameters.append(", "); } parameters.append(context.text); } } int result = dwarf_siblingof(dwarf, current_die, &sibling_die, &error); if (result == DW_DLV_ERROR) { break; } else if (result == DW_DLV_NO_ENTRY) { break; } if (current_die != die) { dwarf_dealloc(dwarf, current_die, DW_DLA_DIE); current_die = 0; } current_die = sibling_die; } } if (parameters.empty()) parameters = "("; parameters.append(")"); // If we got a spec DIE we need to deallocate it if (has_spec) dwarf_dealloc(dwarf, spec_die, DW_DLA_DIE); function_name.append(parameters); } // defined here because in C++98, template function cannot take locally // defined types... grrr. struct inliners_search_cb { void operator()(Dwarf_Die die, std::vector &ns) { Dwarf_Error error = DW_DLE_NE; Dwarf_Half tag_value; Dwarf_Attribute attr_mem; Dwarf_Debug dwarf = fobj.dwarf_handle.get(); dwarf_tag(die, &tag_value, &error); switch (tag_value) { char *name; case DW_TAG_subprogram: if (!trace.source.function.empty()) break; if (dwarf_diename(die, &name, &error) == DW_DLV_OK) { trace.source.function = std::string(name); dwarf_dealloc(dwarf, name, DW_DLA_STRING); } else { // We don't have a function name in this DIE. // Check if there is a referenced non-defining // declaration. trace.source.function = get_referenced_die_name(dwarf, die, DW_AT_abstract_origin, true); if (trace.source.function.empty()) { trace.source.function = get_referenced_die_name(dwarf, die, DW_AT_specification, true); } } // Append the function parameters, if available set_function_parameters(trace.source.function, ns, fobj, die); // If the object function name is empty, it's possible that // there is no dynamic symbol table (maybe the executable // was stripped or not built with -rdynamic). See if we have // a DWARF linkage name to use instead. We try both // linkage_name and MIPS_linkage_name because the MIPS tag // was the unofficial one until it was adopted in DWARF4. // Old gcc versions generate MIPS_linkage_name if (trace.object_function.empty()) { details::demangler demangler; if (dwarf_attr(die, DW_AT_linkage_name, &attr_mem, &error) != DW_DLV_OK) { if (dwarf_attr(die, DW_AT_MIPS_linkage_name, &attr_mem, &error) != DW_DLV_OK) { break; } } char *linkage; if (dwarf_formstring(attr_mem, &linkage, &error) == DW_DLV_OK) { trace.object_function = demangler.demangle(linkage); dwarf_dealloc(dwarf, linkage, DW_DLA_STRING); } dwarf_dealloc(dwarf, name, DW_DLA_ATTR); } break; case DW_TAG_inlined_subroutine: ResolvedTrace::SourceLoc sloc; if (dwarf_diename(die, &name, &error) == DW_DLV_OK) { sloc.function = std::string(name); dwarf_dealloc(dwarf, name, DW_DLA_STRING); } else { // We don't have a name for this inlined DIE, it could // be that there is an abstract origin instead. // Get the DW_AT_abstract_origin value, which is a // reference to the source DIE and try to get its name sloc.function = get_referenced_die_name(dwarf, die, DW_AT_abstract_origin, true); } set_function_parameters(sloc.function, ns, fobj, die); std::string file = die_call_file(dwarf, die, cu_die); if (!file.empty()) sloc.filename = file; Dwarf_Unsigned number = 0; if (dwarf_attr(die, DW_AT_call_line, &attr_mem, &error) == DW_DLV_OK) { if (dwarf_formudata(attr_mem, &number, &error) == DW_DLV_OK) { sloc.line = number; } dwarf_dealloc(dwarf, attr_mem, DW_DLA_ATTR); } if (dwarf_attr(die, DW_AT_call_column, &attr_mem, &error) == DW_DLV_OK) { if (dwarf_formudata(attr_mem, &number, &error) == DW_DLV_OK) { sloc.col = number; } dwarf_dealloc(dwarf, attr_mem, DW_DLA_ATTR); } trace.inliners.push_back(sloc); break; }; } ResolvedTrace &trace; dwarf_fileobject &fobj; Dwarf_Die cu_die; inliners_search_cb(ResolvedTrace &t, dwarf_fileobject &f, Dwarf_Die c) : trace(t), fobj(f), cu_die(c) {} }; static Dwarf_Die find_fundie_by_pc(dwarf_fileobject &fobj, Dwarf_Die parent_die, Dwarf_Addr pc, Dwarf_Die result) { Dwarf_Die current_die = 0; Dwarf_Error error = DW_DLE_NE; Dwarf_Debug dwarf = fobj.dwarf_handle.get(); if (dwarf_child(parent_die, ¤t_die, &error) != DW_DLV_OK) { return NULL; } for (;;) { Dwarf_Die sibling_die = 0; Dwarf_Half tag_value; dwarf_tag(current_die, &tag_value, &error); switch (tag_value) { case DW_TAG_subprogram: case DW_TAG_inlined_subroutine: if (die_has_pc(fobj, current_die, pc)) { return current_die; } }; bool declaration = false; Dwarf_Attribute attr_mem; if (dwarf_attr(current_die, DW_AT_declaration, &attr_mem, &error) == DW_DLV_OK) { Dwarf_Bool flag = 0; if (dwarf_formflag(attr_mem, &flag, &error) == DW_DLV_OK) { declaration = flag != 0; } dwarf_dealloc(dwarf, attr_mem, DW_DLA_ATTR); } if (!declaration) { // let's be curious and look deeper in the tree, functions are // not necessarily at the first level, but might be nested // inside a namespace, structure, a function, an inlined // function etc. Dwarf_Die die_mem = 0; Dwarf_Die indie = find_fundie_by_pc(fobj, current_die, pc, die_mem); if (indie) { result = die_mem; return result; } } int res = dwarf_siblingof(dwarf, current_die, &sibling_die, &error); if (res == DW_DLV_ERROR) { return NULL; } else if (res == DW_DLV_NO_ENTRY) { break; } if (current_die != parent_die) { dwarf_dealloc(dwarf, current_die, DW_DLA_DIE); current_die = 0; } current_die = sibling_die; } return NULL; } template static bool deep_first_search_by_pc(dwarf_fileobject &fobj, Dwarf_Die parent_die, Dwarf_Addr pc, std::vector &ns, CB cb) { Dwarf_Die current_die = 0; Dwarf_Debug dwarf = fobj.dwarf_handle.get(); Dwarf_Error error = DW_DLE_NE; if (dwarf_child(parent_die, ¤t_die, &error) != DW_DLV_OK) { return false; } bool branch_has_pc = false; bool has_namespace = false; for (;;) { Dwarf_Die sibling_die = 0; Dwarf_Half tag; if (dwarf_tag(current_die, &tag, &error) == DW_DLV_OK) { if (tag == DW_TAG_namespace || tag == DW_TAG_class_type) { char *ns_name = NULL; if (dwarf_diename(current_die, &ns_name, &error) == DW_DLV_OK) { if (ns_name) { ns.push_back(std::string(ns_name)); } else { ns.push_back(""); } dwarf_dealloc(dwarf, ns_name, DW_DLA_STRING); } else { ns.push_back(""); } has_namespace = true; } } bool declaration = false; Dwarf_Attribute attr_mem; if (tag != DW_TAG_class_type && dwarf_attr(current_die, DW_AT_declaration, &attr_mem, &error) == DW_DLV_OK) { Dwarf_Bool flag = 0; if (dwarf_formflag(attr_mem, &flag, &error) == DW_DLV_OK) { declaration = flag != 0; } dwarf_dealloc(dwarf, attr_mem, DW_DLA_ATTR); } if (!declaration) { // let's be curious and look deeper in the tree, function are // not necessarily at the first level, but might be nested // inside a namespace, structure, a function, an inlined // function etc. branch_has_pc = deep_first_search_by_pc(fobj, current_die, pc, ns, cb); } if (!branch_has_pc) { branch_has_pc = die_has_pc(fobj, current_die, pc); } if (branch_has_pc) { cb(current_die, ns); } int result = dwarf_siblingof(dwarf, current_die, &sibling_die, &error); if (result == DW_DLV_ERROR) { return false; } else if (result == DW_DLV_NO_ENTRY) { break; } if (current_die != parent_die) { dwarf_dealloc(dwarf, current_die, DW_DLA_DIE); current_die = 0; } if (has_namespace) { has_namespace = false; ns.pop_back(); } current_die = sibling_die; } if (has_namespace) { ns.pop_back(); } return branch_has_pc; } static std::string die_call_file(Dwarf_Debug dwarf, Dwarf_Die die, Dwarf_Die cu_die) { Dwarf_Attribute attr_mem; Dwarf_Error error = DW_DLE_NE; Dwarf_Signed file_index; std::string file; if (dwarf_attr(die, DW_AT_call_file, &attr_mem, &error) == DW_DLV_OK) { if (dwarf_formsdata(attr_mem, &file_index, &error) != DW_DLV_OK) { file_index = 0; } dwarf_dealloc(dwarf, attr_mem, DW_DLA_ATTR); if (file_index == 0) { return file; } char **srcfiles = 0; Dwarf_Signed file_count = 0; if (dwarf_srcfiles(cu_die, &srcfiles, &file_count, &error) == DW_DLV_OK) { if (file_index <= file_count) file = std::string(srcfiles[file_index - 1]); // Deallocate all strings! for (int i = 0; i < file_count; ++i) { dwarf_dealloc(dwarf, srcfiles[i], DW_DLA_STRING); } dwarf_dealloc(dwarf, srcfiles, DW_DLA_LIST); } } return file; } Dwarf_Die find_die(dwarf_fileobject &fobj, Dwarf_Addr addr) { // Let's get to work! First see if we have a debug_aranges section so // we can speed up the search Dwarf_Debug dwarf = fobj.dwarf_handle.get(); Dwarf_Error error = DW_DLE_NE; Dwarf_Arange *aranges; Dwarf_Signed arange_count; Dwarf_Die returnDie; bool found = false; if (dwarf_get_aranges(dwarf, &aranges, &arange_count, &error) != DW_DLV_OK) { aranges = NULL; } if (aranges) { // We have aranges. Get the one where our address is. Dwarf_Arange arange; if (dwarf_get_arange(aranges, arange_count, addr, &arange, &error) == DW_DLV_OK) { // We found our address. Get the compilation-unit DIE offset // represented by the given address range. Dwarf_Off cu_die_offset; if (dwarf_get_cu_die_offset(arange, &cu_die_offset, &error) == DW_DLV_OK) { // Get the DIE at the offset returned by the aranges search. // We set is_info to 1 to specify that the offset is from // the .debug_info section (and not .debug_types) int dwarf_result = dwarf_offdie_b(dwarf, cu_die_offset, 1, &returnDie, &error); found = dwarf_result == DW_DLV_OK; } dwarf_dealloc(dwarf, arange, DW_DLA_ARANGE); } } if (found) return returnDie; // The caller is responsible for freeing the die // The search for aranges failed. Try to find our address by scanning // all compilation units. Dwarf_Unsigned next_cu_header; Dwarf_Half tag = 0; returnDie = 0; while (!found && dwarf_next_cu_header_d(dwarf, 1, 0, 0, 0, 0, 0, 0, 0, 0, &next_cu_header, 0, &error) == DW_DLV_OK) { if (returnDie) dwarf_dealloc(dwarf, returnDie, DW_DLA_DIE); if (dwarf_siblingof(dwarf, 0, &returnDie, &error) == DW_DLV_OK) { if ((dwarf_tag(returnDie, &tag, &error) == DW_DLV_OK) && tag == DW_TAG_compile_unit) { if (die_has_pc(fobj, returnDie, addr)) { found = true; } } } } if (found) { while (dwarf_next_cu_header_d(dwarf, 1, 0, 0, 0, 0, 0, 0, 0, 0, &next_cu_header, 0, &error) == DW_DLV_OK) { // Reset the cu header state. Libdwarf's next_cu_header API // keeps its own iterator per Dwarf_Debug that can't be reset. // We need to keep fetching elements until the end. } } if (found) return returnDie; // We couldn't find any compilation units with ranges or a high/low pc. // Try again by looking at all DIEs in all compilation units. Dwarf_Die cudie; while (dwarf_next_cu_header_d(dwarf, 1, 0, 0, 0, 0, 0, 0, 0, 0, &next_cu_header, 0, &error) == DW_DLV_OK) { if (dwarf_siblingof(dwarf, 0, &cudie, &error) == DW_DLV_OK) { Dwarf_Die die_mem = 0; Dwarf_Die resultDie = find_fundie_by_pc(fobj, cudie, addr, die_mem); if (resultDie) { found = true; break; } } } if (found) { while (dwarf_next_cu_header_d(dwarf, 1, 0, 0, 0, 0, 0, 0, 0, 0, &next_cu_header, 0, &error) == DW_DLV_OK) { // Reset the cu header state. Libdwarf's next_cu_header API // keeps its own iterator per Dwarf_Debug that can't be reset. // We need to keep fetching elements until the end. } } if (found) return cudie; // We failed. return NULL; } }; #endif // BACKWARD_HAS_DWARF == 1 template <> class TraceResolverImpl : public TraceResolverLinuxImpl {}; #endif // BACKWARD_SYSTEM_LINUX #ifdef BACKWARD_SYSTEM_DARWIN template class TraceResolverDarwinImpl; template <> class TraceResolverDarwinImpl : public TraceResolverImplBase { public: template void load_stacktrace(ST &st) { using namespace details; if (st.size() == 0) { return; } _symbols.reset(backtrace_symbols(st.begin(), st.size())); } ResolvedTrace resolve(ResolvedTrace trace) { // parse: // + char *filename = _symbols[trace.idx]; // skip " " while (*filename && *filename != ' ') filename++; while (*filename == ' ') filename++; // find start of from end ( may contain a space) char *p = filename + strlen(filename) - 1; // skip to start of " + " while (p > filename && *p != ' ') p--; while (p > filename && *p == ' ') p--; while (p > filename && *p != ' ') p--; while (p > filename && *p == ' ') p--; char *funcname_end = p + 1; // skip to start of "" while (p > filename && *p != ' ') p--; char *funcname = p + 1; // skip to start of " " while (p > filename && *p == ' ') p--; while (p > filename && *p != ' ') p--; while (p > filename && *p == ' ') p--; // skip "", handling the case where it contains a char *filename_end = p + 1; if (p == filename) { // something went wrong, give up filename_end = filename + strlen(filename); funcname = filename_end; } trace.object_filename.assign( filename, filename_end); // ok even if filename_end is the ending \0 // (then we assign entire string) if (*funcname) { // if it's not end of string *funcname_end = '\0'; trace.object_function = this->demangle(funcname); trace.object_function += " "; trace.object_function += (funcname_end + 1); trace.source.function = trace.object_function; // we cannot do better. } return trace; } private: details::handle _symbols; }; template <> class TraceResolverImpl : public TraceResolverDarwinImpl {}; #endif // BACKWARD_SYSTEM_DARWIN #ifdef BACKWARD_SYSTEM_WINDOWS // Load all symbol info // Based on: // https://stackoverflow.com/questions/6205981/windows-c-stack-trace-from-a-running-app/28276227#28276227 struct module_data { std::string image_name; std::string module_name; void *base_address; DWORD load_size; }; class get_mod_info { HANDLE process; static const int buffer_length = 4096; public: get_mod_info(HANDLE h) : process(h) {} module_data operator()(HMODULE module) { module_data ret; char temp[buffer_length]; MODULEINFO mi; GetModuleInformation(process, module, &mi, sizeof(mi)); ret.base_address = mi.lpBaseOfDll; ret.load_size = mi.SizeOfImage; GetModuleFileNameEx(process, module, temp, sizeof(temp)); ret.image_name = temp; GetModuleBaseName(process, module, temp, sizeof(temp)); ret.module_name = temp; std::vector img(ret.image_name.begin(), ret.image_name.end()); std::vector mod(ret.module_name.begin(), ret.module_name.end()); SymLoadModule64(process, 0, &img[0], &mod[0], (DWORD64)ret.base_address, ret.load_size); return ret; } }; template <> class TraceResolverImpl { public: TraceResolverImpl() { HANDLE process = GetCurrentProcess(); std::vector modules; DWORD cbNeeded; std::vector module_handles(1); SymInitialize(process, NULL, false); DWORD symOptions = SymGetOptions(); symOptions |= SYMOPT_LOAD_LINES | SYMOPT_UNDNAME; SymSetOptions(symOptions); EnumProcessModules(process, &module_handles[0], module_handles.size() * sizeof(HMODULE), &cbNeeded); module_handles.resize(cbNeeded / sizeof(HMODULE)); EnumProcessModules(process, &module_handles[0], module_handles.size() * sizeof(HMODULE), &cbNeeded); std::transform(module_handles.begin(), module_handles.end(), std::back_inserter(modules), get_mod_info(process)); void *base = modules[0].base_address; IMAGE_NT_HEADERS *h = ImageNtHeader(base); image_type = h->FileHeader.Machine; } template void load_stacktrace(ST &) {} static const int max_sym_len = 255; struct symbol_t { SYMBOL_INFO sym; char buffer[max_sym_len]; } sym; DWORD64 displacement; ResolvedTrace resolve(ResolvedTrace t) { HANDLE process = GetCurrentProcess(); char name[256]; memset(&sym, sizeof(sym), 0); sym.sym.SizeOfStruct = sizeof(SYMBOL_INFO); sym.sym.MaxNameLen = max_sym_len; if (!SymFromAddr(process, (ULONG64)t.addr, &displacement, &sym.sym)) { // TODO: error handling everywhere LPTSTR lpMsgBuf; DWORD dw = GetLastError(); FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpMsgBuf, 0, NULL); printf(lpMsgBuf); // abort(); } UnDecorateSymbolName(sym.sym.Name, (PSTR)name, 256, UNDNAME_COMPLETE); DWORD offset = 0; IMAGEHLP_LINE line; if (SymGetLineFromAddr(process, (ULONG64)t.addr, &offset, &line)) { t.object_filename = line.FileName; t.source.filename = line.FileName; t.source.line = line.LineNumber; t.source.col = offset; } t.source.function = name; t.object_filename = ""; t.object_function = name; return t; } DWORD machine_type() const { return image_type; } private: DWORD image_type; }; #endif class TraceResolver : public TraceResolverImpl {}; /*************** CODE SNIPPET ***************/ class SourceFile { public: typedef std::vector> lines_t; SourceFile() {} SourceFile(const std::string &path) { // 1. If BACKWARD_CXX_SOURCE_PREFIXES is set then assume it contains // a colon-separated list of path prefixes. Try prepending each // to the given path until a valid file is found. const std::vector& prefixes = get_paths_from_env_variable(); for (size_t i = 0; i < prefixes.size(); ++i) { // Double slashes (//) should not be a problem. std::string new_path = prefixes[i] + '/' + path; _file.reset(new std::ifstream(new_path.c_str())); if (is_open()) break; } // 2. If no valid file found then fallback to opening the path as-is. if (!_file || !is_open()) { _file.reset(new std::ifstream(path.c_str())); } } bool is_open() const { return _file->is_open(); } lines_t &get_lines(unsigned line_start, unsigned line_count, lines_t &lines) { using namespace std; // This function make uses of the dumbest algo ever: // 1) seek(0) // 2) read lines one by one and discard until line_start // 3) read line one by one until line_start + line_count // // If you are getting snippets many time from the same file, it is // somewhat a waste of CPU, feel free to benchmark and propose a // better solution ;) _file->clear(); _file->seekg(0); string line; unsigned line_idx; for (line_idx = 1; line_idx < line_start; ++line_idx) { std::getline(*_file, line); if (!*_file) { return lines; } } // think of it like a lambda in C++98 ;) // but look, I will reuse it two times! // What a good boy am I. struct isspace { bool operator()(char c) { return std::isspace(c); } }; bool started = false; for (; line_idx < line_start + line_count; ++line_idx) { getline(*_file, line); if (!*_file) { return lines; } if (!started) { if (std::find_if(line.begin(), line.end(), not_isspace()) == line.end()) continue; started = true; } lines.push_back(make_pair(line_idx, line)); } lines.erase( std::find_if(lines.rbegin(), lines.rend(), not_isempty()).base(), lines.end()); return lines; } lines_t get_lines(unsigned line_start, unsigned line_count) { lines_t lines; return get_lines(line_start, line_count, lines); } // there is no find_if_not in C++98, lets do something crappy to // workaround. struct not_isspace { bool operator()(char c) { return !std::isspace(c); } }; // and define this one here because C++98 is not happy with local defined // struct passed to template functions, fuuuu. struct not_isempty { bool operator()(const lines_t::value_type &p) { return !(std::find_if(p.second.begin(), p.second.end(), not_isspace()) == p.second.end()); } }; void swap(SourceFile &b) { _file.swap(b._file); } #ifdef BACKWARD_ATLEAST_CXX11 SourceFile(SourceFile &&from) : _file(nullptr) { swap(from); } SourceFile &operator=(SourceFile &&from) { swap(from); return *this; } #else explicit SourceFile(const SourceFile &from) { // some sort of poor man's move semantic. swap(const_cast(from)); } SourceFile &operator=(const SourceFile &from) { // some sort of poor man's move semantic. swap(const_cast(from)); return *this; } #endif private: details::handle> _file; std::vector get_paths_from_env_variable_impl() { std::vector paths; const char* prefixes_str = std::getenv("BACKWARD_CXX_SOURCE_PREFIXES"); if (prefixes_str && prefixes_str[0]) { paths = details::split_source_prefixes(prefixes_str); } return paths; } const std::vector& get_paths_from_env_variable() { static std::vector paths = get_paths_from_env_variable_impl(); return paths; } #ifdef BACKWARD_ATLEAST_CXX11 SourceFile(const SourceFile &) = delete; SourceFile &operator=(const SourceFile &) = delete; #endif }; class SnippetFactory { public: typedef SourceFile::lines_t lines_t; lines_t get_snippet(const std::string &filename, unsigned line_start, unsigned context_size) { SourceFile &src_file = get_src_file(filename); unsigned start = line_start - context_size / 2; return src_file.get_lines(start, context_size); } lines_t get_combined_snippet(const std::string &filename_a, unsigned line_a, const std::string &filename_b, unsigned line_b, unsigned context_size) { SourceFile &src_file_a = get_src_file(filename_a); SourceFile &src_file_b = get_src_file(filename_b); lines_t lines = src_file_a.get_lines(line_a - context_size / 4, context_size / 2); src_file_b.get_lines(line_b - context_size / 4, context_size / 2, lines); return lines; } lines_t get_coalesced_snippet(const std::string &filename, unsigned line_a, unsigned line_b, unsigned context_size) { SourceFile &src_file = get_src_file(filename); using std::max; using std::min; unsigned a = min(line_a, line_b); unsigned b = max(line_a, line_b); if ((b - a) < (context_size / 3)) { return src_file.get_lines((a + b - context_size + 1) / 2, context_size); } lines_t lines = src_file.get_lines(a - context_size / 4, context_size / 2); src_file.get_lines(b - context_size / 4, context_size / 2, lines); return lines; } private: typedef details::hashtable::type src_files_t; src_files_t _src_files; SourceFile &get_src_file(const std::string &filename) { src_files_t::iterator it = _src_files.find(filename); if (it != _src_files.end()) { return it->second; } SourceFile &new_src_file = _src_files[filename]; new_src_file = SourceFile(filename); return new_src_file; } }; /*************** PRINTER ***************/ namespace ColorMode { enum type { automatic, never, always }; } class cfile_streambuf : public std::streambuf { public: cfile_streambuf(FILE *_sink) : sink(_sink) {} int_type underflow() override { return traits_type::eof(); } int_type overflow(int_type ch) override { if (traits_type::not_eof(ch) && fwrite(&ch, sizeof ch, 1, sink) == 1) { return ch; } return traits_type::eof(); } std::streamsize xsputn(const char_type *s, std::streamsize count) override { return static_cast( fwrite(s, sizeof *s, static_cast(count), sink)); } #ifdef BACKWARD_ATLEAST_CXX11 public: cfile_streambuf(const cfile_streambuf &) = delete; cfile_streambuf &operator=(const cfile_streambuf &) = delete; #else private: cfile_streambuf(const cfile_streambuf &); cfile_streambuf &operator=(const cfile_streambuf &); #endif private: FILE *sink; std::vector buffer; }; #ifdef BACKWARD_SYSTEM_LINUX namespace Color { enum type { yellow = 33, purple = 35, reset = 39 }; } // namespace Color class Colorize { public: Colorize(std::ostream &os) : _os(os), _reset(false), _enabled(false) {} void activate(ColorMode::type mode) { _enabled = mode == ColorMode::always; } void activate(ColorMode::type mode, FILE *fp) { activate(mode, fileno(fp)); } void set_color(Color::type ccode) { if (!_enabled) return; // I assume that the terminal can handle basic colors. Seriously I // don't want to deal with all the termcap shit. _os << "\033[" << static_cast(ccode) << "m"; _reset = (ccode != Color::reset); } ~Colorize() { if (_reset) { set_color(Color::reset); } } private: void activate(ColorMode::type mode, int fd) { activate(mode == ColorMode::automatic && isatty(fd) ? ColorMode::always : mode); } std::ostream &_os; bool _reset; bool _enabled; }; #else // ndef BACKWARD_SYSTEM_LINUX namespace Color { enum type { yellow = 0, purple = 0, reset = 0 }; } // namespace Color class Colorize { public: Colorize(std::ostream &) {} void activate(ColorMode::type) {} void activate(ColorMode::type, FILE *) {} void set_color(Color::type) {} }; #endif // BACKWARD_SYSTEM_LINUX class Printer { public: bool snippet; ColorMode::type color_mode; bool address; bool object; int inliner_context_size; int trace_context_size; Printer() : snippet(true), color_mode(ColorMode::automatic), address(false), object(false), inliner_context_size(5), trace_context_size(7) {} template FILE *print(ST &st, FILE *fp = stderr) { cfile_streambuf obuf(fp); std::ostream os(&obuf); Colorize colorize(os); colorize.activate(color_mode, fp); print_stacktrace(st, os, colorize); return fp; } template std::ostream &print(ST &st, std::ostream &os) { Colorize colorize(os); colorize.activate(color_mode); print_stacktrace(st, os, colorize); return os; } template FILE *print(IT begin, IT end, FILE *fp = stderr, size_t thread_id = 0) { cfile_streambuf obuf(fp); std::ostream os(&obuf); Colorize colorize(os); colorize.activate(color_mode, fp); print_stacktrace(begin, end, os, thread_id, colorize); return fp; } template std::ostream &print(IT begin, IT end, std::ostream &os, size_t thread_id = 0) { Colorize colorize(os); colorize.activate(color_mode); print_stacktrace(begin, end, os, thread_id, colorize); return os; } TraceResolver const &resolver() const { return _resolver; } private: TraceResolver _resolver; SnippetFactory _snippets; template void print_stacktrace(ST &st, std::ostream &os, Colorize &colorize) { print_header(os, st.thread_id()); _resolver.load_stacktrace(st); for (size_t trace_idx = st.size(); trace_idx > 0; --trace_idx) { print_trace(os, _resolver.resolve(st[trace_idx - 1]), colorize); } } template void print_stacktrace(IT begin, IT end, std::ostream &os, size_t thread_id, Colorize &colorize) { print_header(os, thread_id); for (; begin != end; ++begin) { print_trace(os, *begin, colorize); } } void print_header(std::ostream &os, size_t thread_id) { os << "Stack trace (most recent call last)"; if (thread_id) { os << " in thread " << thread_id; } os << ":\n"; } void print_trace(std::ostream &os, const ResolvedTrace &trace, Colorize &colorize) { os << "#" << std::left << std::setw(2) << trace.idx << std::right; bool already_indented = true; if (!trace.source.filename.size() || object) { os << " Object \"" << trace.object_filename << "\", at " << trace.addr << ", in " << trace.object_function << "\n"; already_indented = false; } for (size_t inliner_idx = trace.inliners.size(); inliner_idx > 0; --inliner_idx) { if (!already_indented) { os << " "; } const ResolvedTrace::SourceLoc &inliner_loc = trace.inliners[inliner_idx - 1]; print_source_loc(os, " | ", inliner_loc); if (snippet) { print_snippet(os, " | ", inliner_loc, colorize, Color::purple, inliner_context_size); } already_indented = false; } if (trace.source.filename.size()) { if (!already_indented) { os << " "; } print_source_loc(os, " ", trace.source, trace.addr); if (snippet) { print_snippet(os, " ", trace.source, colorize, Color::yellow, trace_context_size); } } } void print_snippet(std::ostream &os, const char *indent, const ResolvedTrace::SourceLoc &source_loc, Colorize &colorize, Color::type color_code, int context_size) { using namespace std; typedef SnippetFactory::lines_t lines_t; lines_t lines = _snippets.get_snippet(source_loc.filename, source_loc.line, static_cast(context_size)); for (lines_t::const_iterator it = lines.begin(); it != lines.end(); ++it) { if (it->first == source_loc.line) { colorize.set_color(color_code); os << indent << ">"; } else { os << indent << " "; } os << std::setw(4) << it->first << ": " << it->second << "\n"; if (it->first == source_loc.line) { colorize.set_color(Color::reset); } } } void print_source_loc(std::ostream &os, const char *indent, const ResolvedTrace::SourceLoc &source_loc, void *addr = nullptr) { os << indent << "Source \"" << source_loc.filename << "\", line " << source_loc.line << ", in " << source_loc.function; if (address && addr != nullptr) { os << " [" << addr << "]"; } os << "\n"; } }; /*************** SIGNALS HANDLING ***************/ #if defined(BACKWARD_SYSTEM_LINUX) || defined(BACKWARD_SYSTEM_DARWIN) class SignalHandling { public: static std::vector make_default_signals() { const int posix_signals[] = { // Signals for which the default action is "Core". SIGABRT, // Abort signal from abort(3) SIGBUS, // Bus error (bad memory access) SIGFPE, // Floating point exception SIGILL, // Illegal Instruction SIGIOT, // IOT trap. A synonym for SIGABRT SIGQUIT, // Quit from keyboard SIGSEGV, // Invalid memory reference SIGSYS, // Bad argument to routine (SVr4) SIGTRAP, // Trace/breakpoint trap SIGXCPU, // CPU time limit exceeded (4.2BSD) SIGXFSZ, // File size limit exceeded (4.2BSD) #if defined(BACKWARD_SYSTEM_DARWIN) SIGEMT, // emulation instruction executed #endif }; return std::vector(posix_signals, posix_signals + sizeof posix_signals / sizeof posix_signals[0]); } SignalHandling(const std::vector &posix_signals = make_default_signals()) : _loaded(false) { bool success = true; const size_t stack_size = 1024 * 1024 * 8; _stack_content.reset(static_cast(malloc(stack_size))); if (_stack_content) { stack_t ss; ss.ss_sp = _stack_content.get(); ss.ss_size = stack_size; ss.ss_flags = 0; if (sigaltstack(&ss, nullptr) < 0) { success = false; } } else { success = false; } for (size_t i = 0; i < posix_signals.size(); ++i) { struct sigaction action; memset(&action, 0, sizeof action); action.sa_flags = static_cast(SA_SIGINFO | SA_ONSTACK | SA_NODEFER | SA_RESETHAND); sigfillset(&action.sa_mask); sigdelset(&action.sa_mask, posix_signals[i]); #if defined(__clang__) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdisabled-macro-expansion" #endif action.sa_sigaction = &sig_handler; #if defined(__clang__) #pragma clang diagnostic pop #endif int r = sigaction(posix_signals[i], &action, nullptr); if (r < 0) success = false; } _loaded = success; } bool loaded() const { return _loaded; } static void handleSignal(int, siginfo_t *info, void *_ctx) { ucontext_t *uctx = static_cast(_ctx); StackTrace st; void *error_addr = nullptr; #ifdef REG_RIP // x86_64 error_addr = reinterpret_cast(uctx->uc_mcontext.gregs[REG_RIP]); #elif defined(REG_EIP) // x86_32 error_addr = reinterpret_cast(uctx->uc_mcontext.gregs[REG_EIP]); #elif defined(__arm__) error_addr = reinterpret_cast(uctx->uc_mcontext.arm_pc); #elif defined(__aarch64__) error_addr = reinterpret_cast(uctx->uc_mcontext.pc); #elif defined(__mips__) error_addr = reinterpret_cast(reinterpret_cast(&uctx->uc_mcontext)->sc_pc); #elif defined(__ppc__) || defined(__powerpc) || defined(__powerpc__) || \ defined(__POWERPC__) error_addr = reinterpret_cast(uctx->uc_mcontext.regs->nip); #elif defined(__s390x__) error_addr = reinterpret_cast(uctx->uc_mcontext.psw.addr); #elif defined(__APPLE__) && defined(__x86_64__) error_addr = reinterpret_cast(uctx->uc_mcontext->__ss.__rip); #elif defined(__APPLE__) error_addr = reinterpret_cast(uctx->uc_mcontext->__ss.__eip); #else #warning ":/ sorry, ain't know no nothing none not of your architecture!" #endif if (error_addr) { st.load_from(error_addr, 32); } else { st.load_here(32); } Printer printer; printer.address = true; printer.print(st, stderr); #if _XOPEN_SOURCE >= 700 || _POSIX_C_SOURCE >= 200809L psiginfo(info, nullptr); #else (void)info; #endif } private: details::handle _stack_content; bool _loaded; #ifdef __GNUC__ __attribute__((noreturn)) #endif static void sig_handler(int signo, siginfo_t *info, void *_ctx) { handleSignal(signo, info, _ctx); // try to forward the signal. raise(info->si_signo); // terminate the process immediately. puts("watf? exit"); _exit(EXIT_FAILURE); } }; #endif // BACKWARD_SYSTEM_LINUX || BACKWARD_SYSTEM_DARWIN #ifdef BACKWARD_SYSTEM_WINDOWS class SignalHandling { public: SignalHandling(const std::vector & = std::vector()) : reporter_thread_([]() { /* We handle crashes in a utility thread: backward structures and some Windows functions called here need stack space, which we do not have when we encounter a stack overflow. To support reporting stack traces during a stack overflow, we create a utility thread at startup, which waits until a crash happens or the program exits normally. */ { std::unique_lock lk(mtx()); cv().wait(lk, [] { return crashed() != crash_status::running; }); } if (crashed() == crash_status::crashed) { handle_stacktrace(skip_recs()); } { std::unique_lock lk(mtx()); crashed() = crash_status::ending; } cv().notify_one(); }) { SetUnhandledExceptionFilter(crash_handler); signal(SIGABRT, signal_handler); _set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT); set_terminate(&terminator); set_unexpected(&terminator); _set_purecall_handler(&terminator); _set_invalid_parameter_handler(&invalid_parameter_handler); } bool loaded() const { return true; } ~SignalHandling() { { std::unique_lock lk(mtx()); crashed() = crash_status::normal_exit; } cv().notify_one(); reporter_thread_.join(); } private: static CONTEXT *ctx() { static CONTEXT data; return &data; } enum class crash_status { running, crashed, normal_exit, ending }; static crash_status &crashed() { static crash_status data; return data; } static std::mutex &mtx() { static std::mutex data; return data; } static std::condition_variable &cv() { static std::condition_variable data; return data; } static HANDLE &thread_handle() { static HANDLE handle; return handle; } std::thread reporter_thread_; // TODO: how not to hardcode these? static const constexpr int signal_skip_recs = #ifdef __clang__ // With clang, RtlCaptureContext also captures the stack frame of the // current function Below that, there ar 3 internal Windows functions 4 #else // With MSVC cl, RtlCaptureContext misses the stack frame of the current // function The first entries during StackWalk are the 3 internal Windows // functions 3 #endif ; static int &skip_recs() { static int data; return data; } static inline void terminator() { crash_handler(signal_skip_recs); abort(); } static inline void signal_handler(int) { crash_handler(signal_skip_recs); abort(); } static inline void __cdecl invalid_parameter_handler(const wchar_t *, const wchar_t *, const wchar_t *, unsigned int, uintptr_t) { crash_handler(signal_skip_recs); abort(); } NOINLINE static LONG WINAPI crash_handler(EXCEPTION_POINTERS *info) { // The exception info supplies a trace from exactly where the issue was, // no need to skip records crash_handler(0, info->ContextRecord); return EXCEPTION_CONTINUE_SEARCH; } NOINLINE static void crash_handler(int skip, CONTEXT *ct = nullptr) { if (ct == nullptr) { RtlCaptureContext(ctx()); } else { memcpy(ctx(), ct, sizeof(CONTEXT)); } DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &thread_handle(), 0, FALSE, DUPLICATE_SAME_ACCESS); skip_recs() = skip; { std::unique_lock lk(mtx()); crashed() = crash_status::crashed; } cv().notify_one(); { std::unique_lock lk(mtx()); cv().wait(lk, [] { return crashed() != crash_status::crashed; }); } } static void handle_stacktrace(int skip_frames = 0) { // printer creates the TraceResolver, which can supply us a machine type // for stack walking. Without this, StackTrace can only guess using some // macros. // StackTrace also requires that the PDBs are already loaded, which is done // in the constructor of TraceResolver Printer printer; StackTrace st; st.set_machine_type(printer.resolver().machine_type()); st.set_context(ctx()); st.set_thread_handle(thread_handle()); st.load_here(32 + skip_frames); st.skip_n_firsts(skip_frames); printer.address = true; printer.print(st, std::cerr); } }; #endif // BACKWARD_SYSTEM_WINDOWS #ifdef BACKWARD_SYSTEM_UNKNOWN class SignalHandling { public: SignalHandling(const std::vector & = std::vector()) {} bool init() { return false; } bool loaded() { return false; } }; #endif // BACKWARD_SYSTEM_UNKNOWN } // namespace backward #endif /* H_GUARD */ gammaray-2.11.3/3rdparty/backward-cpp/builds.sh000077500000000000000000000025171413310443000213210ustar00rootroot00000000000000#!/bin/bash COMPILERS_CXX98=`cat</dev/null ( cd "$builddir" cmake -DCMAKE_BUILD_TYPE=$buildtype -DBACKWARD_TESTS=ON .. ) } function build() { local builddir=$1 shift make -C "$builddir" $@ } function dotest() { local builddir=$1 shift make -C "$builddir" test $@ return 0 } function do_action() { local lang=$1 local action=$2 shift 2 for compiler in $COMPILERS; do local builddir="build_${lang}_${compiler}" if [[ $action == "cmake" ]]; then buildtype=$1 mkbuild $compiler $lang "$buildtype" "$builddir" [[ $? != 0 ]] && exit elif [[ $action == "make" ]]; then build "$builddir" $@ [[ $? != 0 ]] && exit elif [[ $action == "test" ]]; then dotest "$builddir" $@ [[ $? != 0 ]] && exit elif [[ $action == "clean" ]]; then rm -r "$builddir" else echo "usage: $0 cmake [debug|release|relwithdbg]|make|test|clean" exit 255 fi done } COMPILERS=$COMPILERS_CXX98 do_action c++98 $@ COMPILERS=$COMPILERS_CXX11 do_action c++11 $@ gammaray-2.11.3/3rdparty/backward-cpp/conanfile.py000066400000000000000000000027451413310443000220130ustar00rootroot00000000000000from conans import ConanFile, CMake import os class BackwardCpp(ConanFile): settings = 'os', 'compiler', 'build_type', 'arch' name = 'backward' url = 'https://github.com/bombela/backward-cpp' license = 'MIT' version = '1.3.0' options = { 'stack_walking_unwind': [True, False], 'stack_walking_backtrace': [True, False], 'stack_details_auto_detect': [True, False], 'stack_details_backtrace_symbol': [True, False], 'stack_details_dw': [True, False], 'stack_details_bfd': [True, False], 'shared': [True, False] } default_options = ( 'stack_walking_unwind=True', 'stack_walking_backtrace=False', 'stack_details_auto_detect=True', 'stack_details_backtrace_symbol=False', 'stack_details_dw=False', 'stack_details_bfd=False', 'shared=False' ) exports = 'backward.cpp', 'backward.hpp', 'test/*', 'CMakeLists.txt', 'BackwardConfig.cmake' generators = 'cmake' def build(self): cmake = CMake(self) cmake.configure(defs={'BACKWARD_' + name.upper(): value for name, value in self.options.values.as_list()}) cmake.build() def package(self): self.copy('backward.hpp', os.path.join('include', 'backward')) self.copy('*.a', dst='lib') self.copy('*.so', dst='lib') self.copy('*.lib', dst='lib') self.copy('*.dll', dst='lib') def package_info(self): self.cpp_info.libs = ['backward'] gammaray-2.11.3/3rdparty/backward-cpp/doc/000077500000000000000000000000001413310443000202405ustar00rootroot00000000000000gammaray-2.11.3/3rdparty/backward-cpp/doc/nice.png000066400000000000000000001177101413310443000216730ustar00rootroot00000000000000PNG  IHDRe)sRGBbKGD pHYs  tIME4 j IDATxyXU?.D@P L},4M,KnYf▦),4RT,Ͳ\s4LE\p}_+"yd;3{=3gFj׶_@ :ڶD% @ {\,"xvpYoykĠBBB YLZ2ʧ9"깢'@ !}Gɗ}fC:x:-+g}C /(G;%YBL'p@ ͓We,/WWnhiȂ VSTv >Wta@ "`TFG)+y Ng; 4K^g~CD3Y $P.Mv=gŬf}|7$K+9u7bLH1x{ b~v&n!ޠhט7:{I-ndn N, PbєZwHz2cOu6G"^2'mآMԞ@&٠ F ZC:6ͪm\Ȗ+ *J_jtXCZZ.RXWYW1E4GOgoJQ"ɮ3'& /|jcIφcbkѿţ/Q[|yYp(")j͌qX8p@ "`4 6-G+xX8V4rC6iu66  1~NLip3/2ɖO{Y0i; /X4 Zc~s(XV7`:cϰzR,yןQӉXnf(MCx^q15Sه)cg9KΐfFVNX!٠Bgc bb֟'̃3eX,#,r7AMz̜LB/SHF7:=S}"#aӠ}t>`P2ʧYe &⚋cj+J+sCFh@ *+RdnVxBrȩTgdlb<G]4%Fȡ8@'ܴ"Y7·_dשG'h=jtPclK$_?3ޭYz?gIt_" u B\"W#Xx5I|{`EN ɚz<Mߦ]9f#G'gIG:dE&m#aUeE:v$ Z)ׯ6{IGыNOslv&տoWȾۗG$ Υ %֯46a$_T[?euX"^@ _2EL\ +4i)I |W c}eʖ,(VOf5=hܴ eԞ]9;W0x <.bPb3 l>#b %gJ>%o~͛B'/kWxQy&ֿnh0>@u]C"dUS}z1DTW @PqcnHnRw-ce\Hn!h,o57}J4Iru0+9u^>و,ܑP|8C k[ӣ8g3}Ĭm΄4PK,նxbb2 b_d|X;`o>??թ1:6x.單, #$mqxcf[n+_~'YxkOo!U|(@_ #r.qgӰo֒Zjg8*Z.M{9]qJnn쌕x~.$d4^-3l\x oS5<Ѿ5n;~+S@\p|qovīNMw30f]! a֙/9ݒdW&>3tfO\~yDq@5rI Mpfm3lXg) lFBU~T?E45:aod1XתMڵp$@ AeX+?Vʮ?"w, \9T,[?!$*8|?S'[vQYCfYY?{˙NQg 1xF*Leϸ&ۘ 1dft0Y/d͐l2ؾ%gPP)1o1 #TwFn1|M`^nV/!_-}_8|{!SgT)6!gp܌ߙ6k"@ HڶD#<.dG ~|)@ vm[V%?ƲN'o⎝:њ{#@ @ *(EhAw\,=QF @ IF&wL}; EƋ^> سq;yBU@ 0 O1 \% @ xjЂ@ @ nCVWQKE'=t4xk L]c&BHbҖ~Y$hq;>8{Q 0|! !C;޶ns(_OeŬTEϤqHV=3*qrO0g}ID/kDĦX @_5&˰3gW3:VAM"zG)bUA˜//aPZPD ҢQ\al=ߧ EZ]_c|?ꐢ\X%U~#|P!Cf6.8Pګ;CM[ԹȒ$NeOl8 >^ſ%Oh2eV"otVl5*ց}f`gq27rf'kq(fmMZ'2o9QG[I˳l|otzp!OFy{*R6ndيm\)Hy҈wy;U$='غh Lԟ%7~ʠ_U5W#7ڕ^sW|c}Y+[6$.WŒr1?,@PaQhԚl26<Hrc`yeq+ "N'*QG}ճ'b3N̔MĚ8E+Uͳ39yjK_plH_Ƨ.Ϊi0x*i 8-wH0e Q,:,5Rj)qJ)~Z#zXV&TG3. #W*EI賤7d]/Dz̜Lh1)@PqF y|WHnvH.V }H>1f3;F=zWmy佑2CU;nј~(Yǖ$0xWX7 604A|{ba'tNqj0+PIc=gUe}>zYF*SG@cAe팓Y:q95LN%4޸TQs<=H9s,EeQ~}y 5m]Ar;5 4ZP.KkcOJQݳ>b\@WS~~~ )ύ 1 wW {U.%k*VV7@ *Sk4St[qL>Ω[8R%7Z7vLV#,WH(t~54HufKNW@\p|qovīNMwՌװPU^}ϗČO9*rI6ɢ;iA '7ZtgظxB~9/Y: .H  s]gjxvdFŹ\31>T fohMzz|P)KQ?F^Sb9lOV*fJOze>2;&>3tfO\ @ T7W9iFFѻ\ şyˈј|[?jss[Y/d͐l2ؾ%g2،!C0[Uԅɜ}-MN9`<a&-M| +j/r&p:sYBaz n"4u>Mq)xSلߍx.ŠboOP4n),s&%*9kmOY>e< ~%g~]&0 pi#fJ_zE>ym5v$@ hj+Vڨ@HVyo0RfOpeRPyкc·NXLY(D 3ba<.pĶ3fB.Xߎ3@ *={*v E*m_B@ Q@ @ @S@ @ xRQQ @ 5OHv,x`ч9x+\,"xvaL !$$-|j^C^8?3{ O?@ g7-f6UShn`Iiׯ5}ƢTHpY! 2%$65Nڇe~ _ !d7|:39Wz@H|զ寲,\w-%`XG\%7gG !$$ߤCy7P%o@ hT5w)9zt7d^z2+H,S7ԣϘL'.͐(# ۭoQ>N K2 n 54BrELF\ض08cq|dʄKT@5Șιlb"G ޼9IiC + W"]#B14;l$Ǧ6bs+W ix^ѝng ۷@ B}ơO/+Y©s-D-ŒX.]"F4/#j4~=H _ pHh3rNw:[or$Z?'b/ T^@o3۳8H95K8x}(a՝!{Ҧ-DN dIaҝ_c|?9quH?.,HKT}ΗvA>Mh\MO΃Fkmܩ"Ɍ=E ]`Z X6&=pi#&X|SU.Mv=gŬf@1E4GOgoJQ"ɮ3'& 86wAkH'fY ٦5N;v$=ڣZn?d Fe 8Eꅭ*ٶx%_(fe離536a%K/h՝kMd˱h ۟;3y 3b2ƣG }p4<OZ'yłnxȧ S#pNݚ*CqrFٶ=^R]S>cY@ 0rØЍ+;3:wvA`W-qլ`<]Kc$3kpo6b9gLrBgOKy|Qm=m,+Gw#LUMN>:[|N'*Y~ou Ideʍzf7ώg2Z;yB?[ߩyqx+w^rT;^|S{UKrI˽9cM#K,oPx,#ɒ5x! v{ؕd,yiZ]EA鸤-~&6N{x&nd32z+iRƇ?X6ux=WLn7qEN DRdHͩGj2YG6rF9?=b # BxSlE:I޿!DoȖF8iˆ,,dM#6shFDd练Rvc$聜3R /Y(+S/u_<|qt|<\tvXå|[:LY`U O!O-Ր'T0KaA8;|8I">r>'g5ft1cmy%/erl,L `ڡG0sA(rjٻ@ hna 3INI]t@MrbW߀G|s@q$/ٵ#.~Otae  ] Բ@Rɯ5XI ]nA$ jֱ%+4Iyp$Է M5n,J=H9s,p:QAt>fG/2CU;nl~m9Eu)ۙG$ w!'2d}io(Q~U& j맬4͘FONjȾeÙuKؿ ^oVYŕ׸̻=ÿH6䏁~Dja"mP}W#&wԢ|K0 IDATn<Eq?@ %5yZX?zucw8V|]Cn焍x!7tn.:[t䐚SR`dpX*v$JN6Ha-x^@nD Gq&4jҖS{vxfʣY,,ܰ7N^ք;R8sot }^Wҿ/YգԱx^G!&--dcE? h KI1? Z²lsצxVuuKz8aZ\κ Qˆ[p2O~aKe׌Wh;`=Ѭ!^eٖhxO A% 5jTjNc1eLOW29}Fu5fgu2*7 \γN甩T] .qcO}m',0fT#)A}y 5m}!˕iX{51YRTce=knq8=WO=Y=#fͥnt% ԘF`<f'rBdvD+ݩB hЖn O&hu`f Wߔ% zM?m ըfg*2Uj?rv <бxRCzuL=ѯcsfm3ojO%X̸_|M^7ӧ ; 3w߿xe4b̰M>@ ЀQFVk@. ZUH8t,k<\&&Y(#U}AoRϫ!{ǸnUc?qa9j9S~Ci\F/1䍺dL=ן3tfO\8Mmo[CBjɧ\31>T fohMzێdx/<8{ A 7fYYWH(t~54HufKN:9lOV*f&͡f=rf4G+BdOY2{f \;f}z7͜_YMIs989y?s,0=?7ݘocvĐ!ѭ*d÷oL>ͭyjhٗ); Q/Re \`J$Zn փ>0eGpD+\Sb߅z3eղ5CHc~wM7y]yZAT~1o3['k]Oy?@  Z ^*SI\ŗW^r7^+*zHT<}]3>%.q_^*%U¼ f<չ=5aNmeEoH*F؂s ag}@ Tc5x4tdj9ٶd#;3$󏢙SykЃh@ Q#86cģGƋ^~pıg6@ F0 *1 \% @ T n!@ mՓS[}̂^(ͩ}倄{w&- .4(Cq^C^8 + #G (tGm\s>?[~u'_2ߛo Q.ޥ_E_!|"sKwȡ1/fCYʷ'<6sX>Ư#^&m".s!ٴc沾%"}xT;7NM{gݞuœ|MW [zGU7Ðɞ?c?ihjGJ1gEuTP&uѼ ;)W嚿jjGf/7 'b^>I4ЏQ_3ã3ѿ5PuTua2gvU3қr)SWZȀo4NF`CqolDtPXj"7p*o5"h 3f(S1vIK2ذ+eq11sDys?.3žfH9Ig1`SDž[*+U$8M|z@HɿzڧxmH1k$4߸|?PRzETWo@EphSƋJp\?v3E;I&yQD Dmjֆs4@B؞s݂m|0#E8C$~I6=3;Y4C^2'mآMԞ@&5ùW2p/LsyY6&=pi#&x{%鯤%ƌ>nLbƯ3/z_e%ͯNݚ*CqrFٶ=^R]Dה|3tMK0De֗0׮ 7$&*mg9>H~3 LU~cF3>&LB\6" +}޿&0nt4]^kNIOf .Z_>vOQE,[9r|86wAkH'fY 22/sl9M!q|sgwyZ?ᒙfr%*f[XySWa2F.X?w䔬_ژcGҳ=ؿ-E@]Y9$gvN@.l|%;%HBQHl @E?]<ɔZAp [U'WOcɶ|^壭Y6oF7:Q=GBVۏ@P_k_0 yZ|?#hd| ˬX4v2x)"`$3oK_IYa-ѭM|ÁZͿ2qD-j!!Ь3Ya-ʃ8ӆV}*|Gc[Ӿ1)CoejqlSoq|ٚbii(WQA|}zU!/FYR4%ro6ک1rtɒ2a,OØ5Ca%}Q1e1ǽ+9ҟ)a)IVnԫOӓ|"9^ԲJi6^ q: ~1B"I???ɩ<h;}zV9Ȃc,ZuyH* *hCuqL+$c׏)Uxߣ"Yrش7ry0cI.i7%3䦑%v7.<$5~ v_OK֍eW+u* Zϥhpč,Xq$\:ʯFtA9Up3DZ(,𥌟= dzls}9ݻ|S,oK10~ŚE>9c+fE? dȠr܁ ?\M/v;`+-ud.a,˒OnD Gq&4jҖS{vxfZ heF׀ypgYei!Q-cn*XbgE,’lRn K_-5;vV,7|%-Y~dو75̴a?`cgJ,{9lbf?W7v͹~+"MMo\Np!>s8vޥH6Y߅ss_=WXfNzį,\i[˹ Μ9~PCH-N!C0[Uԅɜ}-oߘ|[?jSjhٗ); "āۯ7Quj.W_E)ƿ?Ysl\3yEDXݏѳ{aF!u\/ #< v7-g11<~_ryJ?Ϭ>DHMonɫ4/cGɖqz`@%ӴY(,s&%*9kRрM&n; tVsh-}{e)'̱6,E:9_V-; Y3$0owm"{LK?)#L_ _n@vBVT~1o3['kknzAy,_hB \dے/XZ> !`j4{O;3(+j D'@ x"qCnEy@A{6n'2OJ ( ʈXA%@ @*@ @ ܋''`T;`rV<\ÜyRCjjt̢>.h*y=t,G?J{w&- . ih0o(_ dK j/0k[*;UERu\0,:sVm!d÷ ]3z ?s(_O |q3K,]{s9ťbF\ic IDAT;=~%*ldoXBH j#8O#ߣ n 1-֑6C?u!ODՎ> Bk݇4ubM5wl}bCB d7ihKvO.~v-#`HWUY{(bS's0s!8y$aL;͎o!&)ɱ)&zV(fO!P>c2upc$4]2# *wZԥg%)ߓ6mpgگ3}z/o.gM۸bB'>ʭ} g^kIL/Lfkɯ_+hF[[7SG]Yy~'wlŘxJ~I޷m$l21_vgոAY0{/&~<-lȏçK7q"Bxm,^l nVLC_ftЃxX9 +7s,Zh0qcнLĊ|EC*qy5/ u:[$иS0=|y}Y*v<; 1 Wcn{q"!=A(&vVClmtLjD|m.cUٗ&e ~>O/]|v%^cܝ6?O?;.2B.~&۟cEqKj akb*)r3oٷ#Yot}+^e[& ůjWqZ^+ZKDc3g4L;0|DsrZjY`K$kdW l}x|«<ӽ”^:y[$Zҿ,SOKz@Aߥ\/FbڶZo hݩD~,(wm Ϊ14 h4e,(JB .'rfUY//%'TE=LD6{~ghi,D6Jwe/8?Hl,3:H eŰx}x,5C}׽ HZgŖm|Y\)z16A'[fى~sbMG b+N4Qp\4\Ss.o.`jap'meRH#__Oc[Z?7Z&= M}59g-b#:_w )_s~$WdžBȬj E%9YDD]Y9ƓٻZVBV>!Wu4rLyIq(fo Cd۶lX]˼14uv.QJ^)icilw##ӟ`xғOsïO~MQ{i9j?M&#-w_oVz,Y ǝ/ QWGJ3qER7"^6~o\l9|ړ?ƏnݼH+7[ISV4g∎ɡ4/.&^ *g+ȓlV>#Бz˩d绝 h?J]\ fag_ɴOdFt>cFKHƹ6ye7Zlhօ rUA~}s=Wsl.& ho؆HL#V6EVg#SQ,u^+ƻ_ (;` 䭕3yQe-/,)P2E$e@B,ɴfh{ geFci.%d{ûԶNRBn⬌Jc`*ϾaUtV"LwtwK"- r%|R˔,l2v.cHR u -7$r nPQz*ߖ0-Z-|=="d,g]O:/=39ׇg8ʁ+Qd=O<>S[4Jc&W"2}^-&Dݾ}%R$FutRBV] r lbtܑ{0zz{y20)Uħ:/Dl|%E֖+\٦5ӉlvÎbrn k\Zj% -M<+OTw or -_WS<;{ߢfGFEZz`>Y&L ~Kmzm ȈSCtdlO8z5SyS+bRػlՙTZ8c$9ҷq偂*YD%5.Td#$hȾD>}Ub)WbhЖ&&DA^N]Wi*J#Cfv zdJtG|ۧIo%?&=F4ho?d~ hqu֯Yп,/*lPx8IӡO|=zrK'qw69IyQgalq8ʊ{<{9 L2࿹dr>we~4!<R=hltQW5g?꯭JWmO_)ſw!~kji 7*mÜu+eƮ:ۣn#'m+Ww4n]Em+jΓqcͰaZVu AR񢰠ZuɄI/ IQ~g* ;|}+P*J+寙}w(Qԑ[_ҏ!>W8v&, "j8 }Mtg^b,ȏG?uŧOp)nVF1^  ?WXtJ(Ow>o)Бw7ݟe}hG 5V7wȩ <{+-:jSg{6j+e߉=7~ׇѣS~'YWH#O>SK#?ivu'#MEWH׻ xq#Gn[w/$ه9~gfB cy`ZZL%jxsRN%Wu.,zyZςhph={C5ؓUь}ޝ6Vs&_U^+KIKin#v~AU~ٌ}OV1SmW*J?1{+n>]ծWQXK mS]yC) (rլ^4LhOOǓӼ` $u$Ӎb˟޶VC}?I?Vw@1'>]6Oq=f/ Cһ#mZw﨩LǶFQ_)kh_7+PPȓ@blG4fy\x]O2액ڪ@å_0oQrM @sի {)&"w g51߳t!yR4^dkbd \`ܸ> ?oMqG7B_[ m^`3XLńW2)!F>4"β׈9YjZ'Yt^K,_-mɢ8޺O>[})/.eøb ң,}.EJ7z$o93ުCMlPIH̕גI {r||[IJNyH*\$8dQ-g]a/xKg_}仈~~:ˊY7rLTޣh'i:~| O16Payɵ&z>Ȝ}K'3_-!Uy||o1FLY7;Tb! G7p{-ߒ-~OBA5t0)k>Rw2P\KwwZF0[~~o+JDD.}> ٓS(8nnxu2'ؼ=|„ z؈6wH.l{)PG (eQpٷ3(P@U@P@ (P@ FAEdejA|'S?jiW/`r5o߶S7oc۶ml۶͵X44{z[ƣAx(RTCĂjcOvfwW`[ fڊ _XJ;o5O)bli ַ$>PSaN_D=SxbW!v~etvUYg-7lZE$ƪO_ZKLe)D$Ts^`y7^ 珒V3./45UwC1Mwdm KOs!=zxoql7jJZ e^<ڛŎγ`z3R{eo람OƔf.q ۴#tz]|nPҗcQFuf״[=v8tcsh݌@<$`[_$ZO:ؙѳ'_; w`ϨY% fmla/0kl*ל:! DVc< 4-ܮ?`,s Ŵs>42 G5u?`GD}nPݳP.͞JXR4@3iyIryKs%.`soLƨ~͉{ϑ_|uv{XęZ4-&OI't^^vAwb<35%.R.%~1i?w;2b$&3?5eq:-#G6Bױ`Gbߢ´rſnD ;`Ms |M444bK=Ceߔ.!26Szt0^ [8s*)O~ǘٻ)}pkX~ל ;3s ?wg<pWB7¶{:u6ߟH<ٝUzc4'+g4`z!?f.ĉ슑/ g Aam$>6̱k 4 ̸C ui&glb{>â!xV~smu>;sSq`z G0TxwcA; +>kdW l}x|«<ӽ”^:y[$Zҿ,SOKz@Aߥ\/ƀ?mЂ?LSI]XQ@U&,| Mci9h. `+8YPȕ\2O̪(k _xq[_J~#O8+6m{8l14% YOm7Կ Y}tѲ[6șTĆ폲bX_D^0+>+?WG4)$ř,ߵD}WÉOjlRr ULh' IDAT$ݺSW\\!%%dSZV V'?⛼&!Rmiڦ<݅ԝ1V".I);ͶƓPVYe_.z/׾o࣡x gNbm5 (6CG bya%rDs=hzNιír^I!!|}h[Z?7Z&= M}59g-b#:_w )_s~$WdžBȬj E%9YDD]Y9Ɠٻ>xݡL_TyaT;;ڎv)|9;70Y_זּS<̶ {o/& ŏ|ڧI_ ͸3gL3F1s|{۴VZEv}SM%*{ڌ].jy53)-8`v+rUп]-/boRʙ,ƻ_-iO[7/ʘ1\(_뎳JnymG%o|VEßYk+y%㫤k z&qL4;_O"~[O-͏It2?7/ e ,/'jԶ}{oZ6[c)q Xp:AMhđnj=9!~ *˙?#aDt"#BB g]*OpglftyēdVBg'ujLι  * *L7Qg@ݤN˩q?M۶mtY]w`⓶)O[#8wB4u gKoeT vWz&cilw'=;7Jq:K'4jOp?-W㧳dp`VAO%r!5|$r1MaYT)+ycή*Wvm3ť#rɱ'ص'k5A9y.vW6o>bƓwſWÏO _~Vu6ڴ)]RXxrUq R`FCqƻ{{s|erudoߚԑӂQ~QE Fc)Xw2t,ŏf6H M[:Qss{DG?$2tGPKΩ|DYa9+T<':=EJAM 1"΀̀(AcJkE̷J|Rkt2 CJY*c>sn>R=6=ZԈ7H2a]ZEP!d-'ʲL u)yv}!m6GUMS# ?d/FJc6ܾ9M,~ϨsKY=ϠJg 7)܆$rU]*+(?ȩ2}7>o"KTt N:;r|kߖ)(L>Dted*muF$7oY% RGQTmݻ1ȯcjL 3f3$oƴG*BeO99bh!ǎ'lhsGrj,O锩z ?v檗PG$^o%>aހ>4KM?5/F|'=45-7Fqy-F¾-?_#75U'ue-t }A-6nE&m[+gP Elmb^}kϚL弳-2Z4R-N7z[b8j_ `LZE}MAٗ9~J鎎Ix o~dg}j%T27 ۶1˘1?B=v]gvpo*"\{#T޶h$%LAo1j mO@HyvY&KY12 α}LaǙ}+Qd=O<>S[4Jc&W"2}^-&TǓ]*MjTH'U^;*d u/g$~1:0:w}5`޾ ucNKQ4ߥ~kI54bp)"rt"ݰV9/h*}i be *.<][9|HdΞli23eT $Ӛ㟢%\f4Q_(]PV'&G_G5vs7׽ØxEMCv+go_5l\~"K۟2id3 6Y1*;ǧ'3g{ ѫ+ZeKDCg r9I*soU8.Kj\|=WIȑ/j@FU+y4 hKK+ QPS)mb nMa5Ʒ]~82R(z)~dg}j+TYhcDs?d~ hqu֯Yп,/*lPx8IӡO|=zrK'qw69IyQgalq8ʊ{<{9 L2࿹dr>we~4!<R=hltQW5g?꯭JWmO_)ſw!~kji 7*mÜu+eƮ:ۣn#'m+W'4n]Em+jΓqcͰaZVu AR񢰠Zud$Xaw]I $~ JkS\S9h^]Of_tn쮓̾ޘO_q=Wd jӢi/W⯮oCNFgZl.b ˜@ԑ[_ҏ!>W8v&, "hcst'Ө/xb"?3n?GMˠVF1^  ?WH[,:ÎEt~%p7 fQXHܻ2>4uãEGL 6\G:xboE[QMpLpcFR`e;QBߓ0zt{O$ i~g}j)Tr䯘L| #ޮ89Ti*Bލ6hs{o1?"v}ǿCo~i$6w>#<6SVkSgr.QKǛr*/ v)gע}D ݇?%>hOvhN7ԓ?gh(O7:-rzJZ !i[ z:'HZ5fGg<þxteؘ>4>$S?kOc?&t\3u Cһ#mZw﨩LǶFFӬ1Z&޾4V@h܉1/_=Hv<2u5&v[jh_wmaOf_fy󿺐_ʥ)^^޴=@m20HϗuqOƯM3j\Z`zOܨʓ@blG4fy|6ӣ-bҼ{~Ĕ͞(pn|>zo/`4$_Ds?3,&?{.?$TJqًzCB@Lo^_i5aAq171j}6~7een/Bk544LۜLf0? 0eSB|iEn?r>O~X|[LyEqv|0S^\ʆqGw1<0X~]2nHrD%9fUÛؠN7+%ɳ7x=3ē8TNIqQɔ[8s9j -^f(ϾW1T@OG{zxY~7KWF{-?$^Ǐ2 r#P<>:2F/'f?['pA[?\j ʋ㫔u4b*Ϻ١ I8ocⷤ}K_/_?keM#k ?,L%q32B(|]-:̖ʅl_sCyt6ǔ" 08~ۯ l96/`҂X';WVü[O)L< d+CqU>KG`2'ELOz>?Tw}w$s{a_Sg lbcd.F H<b 7r (P@oP (Cc#! TlQ QG 0j(P>}>SxP؏ (PPw(P@ (P@?|v# S>#}jB44{z z7׶S7oc۶ml۶͵OX<Ͽ+POz|[ھ|8E|A(P`DE3/v彏bGQ,`j(G4VvWC6-?UF"lcէ{/މ@"G\ZXpoֽ%mf/\_fi k1-$k!z]>ǘ6[R u _}HI (qB{2o'_#g:}G;n,3dcdѺ}$ϥdÑɮNj-bo_nTʙχ2gk:AwlcCg7gj2KdːNP-8HgLimj^~^bw8'vHK {AG~yMU~w)Ƨ{ UaTlvMK<Гo M?NWR@u3=8XW nYXm);Qm9IDY% fmla/0kl*ל:)QA_6<1ȃ !p/ n$725|u 'pygLٓ/NSon-7C]'֯M1 eZ^3[&Qx z•~#Q `;GrV1{gFg)@u,ؑXQh0gH[۷XLԪḭSִ?DS J3)0i]:2ZJ(AɠjyI,3 PYJQ?ZWNy)O~ǘٻ)B 5nmOSva'yF¨4 \ әT&F79Y qďӻ 1{t&NdW̤i``<bUC_ftЃxX9 +7s,ZXh0qcнLĊ|EC*Q:]ӸS0=|Yq_K| ><>U@aiv/[ŽD%`]@Z&O>OJTDy W d5jD3eiIs{3w(CYwш{1LNfhrƱ(j܁'2 #r[:\kEk(~pRpfl>ؓ"I->lՏ Yu`·4~ Ѱ # ^y48OvN~1v8/r- RH܌Rf,Dqzq>иȄhNB\܍>b8Tx8n?pTpnM<{+.W c\~#[''(!/k̗K#؋ y?B1IGzbKۧڦ<݅ԝ1TyX#8tx n0ZϢ +o[YɂWgIVُ˯L&\"ת+M4LPףwN2$'a)LF'meRVX_e$~naYMrx 8צ<[R 9>ɳs7!;kBݖ@IN:єGVN1 45 nG\,AJ>I5xmX=cl/͐Y DcxfO/⣮UϞwoMɬKw%yaaN.$(' @c~=c۾r [G#1-g4 |J(3ǧ0iy2r[ q%SjA5Ez{-*7w޽ QeϿXYj0dQlc~{oZ6r@XJBhn/x^S&t'&>F*^u⭺zv_䜺X9!WSlTNߜ&Wn iծ# 6>UgU;:ʥh;v۝ῳpNlM5.UQHJ*8`B.%^(y.wTmRd :`ocN~./,dџyuD~s+m؊\XG)v8x_*G)a_WxS(4jOp?-_O1g<챋T-/s;o?X9qu1MGVFtͪ l>p GqtB̊L\&9"dg$rZHщo5 i8sCqDQKHdGȧQ6t$~ueK |=(k9*8g@{oDb)$meS3!#g?9E(9ω 8<$e>2eb_ը_USB\g`joo͡?\Ԑkl+~̌1zb>ʵE¯x|L~կԸgxrfƘ2@X[IDATYǿbݾ$A3Yн;S1H[o:Y1=nS ]Oli6m>ͻ>dDGEyg~9u2Q?ƧƏnt.#=j_DW-gDVunP+og d+}(PpۀQ|oLcܲr"67>ɎM@^&4w@q񣙍3m-%6R`CӖNŜ#!(89'̕?{(?:J,Y܋S1;y F 3 +WEteb%t$%Q۪S * ҭ3 ̿NC87v&~) :ǐm.F>wd`Tg}Tw9,R|dX-+8$A_(*4ȫOG6uMHΑż@ ޫU' ٗ9~j-Klbtܑ{0zz{y2;s"k(O<̟e3ЈhCZbaBPm;c ~G1c~$)zf6Eф&LFS'b(w)mI/}kϚL弳-FV:A=o*ͥ;m\3Hm7(&dO ?G@U-Q Wgo^ޱ+6W{Z^Ƣ 2 !dZzSڽ3G9"Q w>qky|̽ W'q;sDNȩ_v0> ]/9di'.ʰ Jie)Q(PpdetdlO8z5SyS+bRػlm 9I*so{U8.K~j\|=Vn~MB!mTu!r%6Fmi+zIn.U7iq-%NE0R`{Wзtqf~$퓬IMg_ dr\qvB#K/Cg ޝ^Hn슫=ޙUU Ȩ "!*fy-͒O-Z*ob7Ӝrh2DN]2M4'PA@D&s (ZjKVTOI;{bc{p}5\!h]%k]I1\Dvz֤>Mu+u&y^x b>៳Sׇ^I ^%9)v\DVe;CxC+5?IP7E!m kג-_YI.V+󨠶ngmzzz[>5},kGn4j 1g=*{*_jڿ/fWD>-[g{ue0nD.?8ΤCn_$jV5vMH :t@ )|ΙV 8A+\Nr鎍z!e/NIfIlg|ή׺wlG¯2 'H/UOv6- Sr|@ (N$NCvfKeFϺ38w5G*_q՛4z1'^A4)QUpUO)2Eui3x8Xs;yo#`9.e$FR~b~TQW:[zse?b摓z[xҿc4?ݹu|Z!}b!cb7q#=L591ffs2} L].٩Ļc y\8m.Yœ-w0#9{? |/S3xB UTEeoꋽ qз\u 7˧_͐е|ΖMJt>/9* H˒É2wWOU6| 9a/ƒ|oNr/㪇޾dplw4p؜Lʝ>@]6s+Aa]LF>ʂƥON̛o\-Ob+DT}ag|DAGXcV|&x*ɴE+~֒Е"P6 UP־>ݺP?q9MjKKRv~êLVvB߰ !\ؙ(@!Erܾ*]c3[έb9@ iúPy -dp ?K?eLĪ @ @wdúZ;$ǐq(4uZ!E[B*@ ȿП@ /@ @ Ću=@ @ +bxhpݱ(ړл3yi(ᄯ Y-Dkˤ8×0M`N7( NËs ]g7Yi( :\c/ΧwG0c~|V'~t|Ӈ>h>KI @ km+pCrvq|6:^gEb7raxA~VZ΁t#/3@˒:iUx?)kp2;̥Zc67۹'7Lܐ N3YpƮ!Vd~L/6CG;7GQ' eu^>27UPחAަO ; I5RKŕeJr0мUWM)[2rGw%I'ٺx!L+oQ}մ|[)Ouv"z!#O:G/ xQYnQ*ߚo8vƢK9sފL,ECX fh:~Mf`G\b#F@ Q?--\0j(ɜC?dls=S,x'e8m#IF.};Hu|sAz+ s<0y鱙KU_űcS⧏T{z~0pl)؁E:K]`~ϿGy#Q[v1\=F1$,1y?:A5 ϧOݩ o8͊ 9+7y/0/NBJ$=SGL߹=i3*|aT_ `~LT[{Ҳ^.r+:ϒ1DcӸh)Tt.}wpn]z4WWSv䙭+d[zXd =ūX\"S UzICɾj|dKClԡٌO$_bjާ*WiLbRԽ#'?AP ]Gؠ$C30 c/f/|4νa} ^z,+~흰RCF-*oJSĞR@ƹhzf)y0wy|;K~[0?r\5zs ƜMo&eǟ'·&uTe^@  asO&$j\Ƌm`V6/Yп_|3D/$/px:ĥ_B~}9j$cbFA .ğU=N:PHIQ y4֤MEo?RbeE̼}pMf^loəw!?4f{ĜZHC_-uVrN寪j6MPT.%mnbB0qê I/ g2)Q?5umFr: iv=Tؙ>LN TW?M#9!#Rм>u$E)Ŵ}%P,=ѝVʧg)G=_z+c?_'4$XO 5߼lo\#K,ԀLޙPa 3_݉\6Ӻ`pBlԴdfBr rT@ LAU]G0#< + CEiiup-r=ԿyzxyBJ%;^< 1-vTdyҊbQ~ sgOl W?)O.4f7 Qc`U|bh}OܲҌ>W!Y7{6Na„ L0) ډu$ dFS>ajS>$WLgȎؾM~*IGA~rSS̒IGs2v 1ˠ(W/5,_%Դ;O[wž jnrnN=ͽƓ yDei±ȼ@ :7zy:{0k~AWyw4 _N> haWFL8[ {0 z-Ѩ3 ~njfD~M7*OyPv1>o=9qRE.~R&Hɔ\댥NV#u.Z@fhлvc N&?Ha4"i1t8ݚ8S߫+Æp#b7!'|> Of&mI.;7R8aϵkms)Xp$AWz:V]*S$%%DɃ\06KSkȉd3:iFBܙoū hpti=mŠ%Jb}մ|hbѵƐwNt(W/5+_EԴM{԰}wnNdԂ}s+_a]n{kz{Aq"v+޾nי={I(*@},]'?gAJК8!6X-+7O<==pG/MZî7D@ PV8ӱ:<~( -r/K>'sk>dnFag'ypcLőahޝOQAIƭѴeM8eb^̷A:6>^v#]`9ر%'M|^s]dYHnG7Q x1xx5|sPV_1 [bǓAZՋkGK,;~ۖoNCԏlrqlw4p؜LƝM\E|œ ∈W3/>-z8R5?⢺*A$ $G.Byи}AO dף[wuBVeZe?kIؚ" "T<_6 U7hm펭:W+X#cL$a!iNC TmoO.ϻD\Σx(Ld B"GIثv f[='sE$ETG`8uDE'#s;B7پt=n6bR`0oMuٮ'>KY3!撨r@ .aTZ7`r U#"h6*R]>*Ʈ=[jqo$y~χ~9gY<>V[_>ѿHBxrA yHU )Æ5M$BSn8EnTr0 _^ .5A0@ 3JDu{QC+t~?kE] _vz|q(su#f"EVav}ZQ.crfEƐ=hʜ `uq4MsȧLlY7I|4љz~]ۓHʯE j䋿eM 0lVVՈF|ϦSX?Ƃw]b׮DZ@jdʛFۦU, {]Xd[GN+HZ2bӊ3~(;qhڧUyJ.0=Gu_iO5W!!grh|[=ȇ\_fd8F T*Q)UEj(^ 16;~wf}_VJFtЏiCc*lTL_}TcgB œdB++ɢaݕӲ J; r+)ufE6/HH1zRvcTō3Z+\!kfx%%Oghs|1s9:L!1 !KVՋdńfG#l?}`_I}e5v5soBS]ǪKVk@mK&D]ʻ]L()@PyF qݣ _q r ;v_ثB7ū; ;0`dƈ74s=4l47@EN$^)RtTsxF*,!p;W55,cT[ZJFzkB9*,3\z1L\ji/pr27 چVugX:X!'r.O;WqMApV&O@ jx;"zFuAO^mЌ^Sߦo1u, ]y|ٚ;{4@(LklEfJ+eI}oNȎCXUq4:w%X %2A<ëZYFӒE@eBaွQVLv\8*O(9G߹9w$˗thNٛܒ}G2)} QL^ԦUĎzWfgaDyٯG3u{8*Tmmn>rE/IdT%>f5bJxKeGwNXQ4hܚ{z_`_}Lg_EMdDPQ8L,0"\i@ ser5dI&XݳD" K TLi去i4{w|dTs"ÍkXxԣdt7Oϥٲ(qkrʯ\!VnJq*[G|^U͊/a6{&"9up~C9niO^Oܹ}>:Tůp]zP*?jQ pMqKJ̫b[q@ 06`̱y_IzA74g8|oK07RVIE nY=#jO\{ǣ-. XUeKOU>s:IܕhэQp_7ƽnuʔ2^0xɽ=6։\0VAn[{l]Ѷiӫ^<>~˧EM5^{16XU1?w]%^|J,׊V]9kX3Nj @ T;0;ecCcռ`"1huWKFk?n/a2$!!W3،!C0ǫ*| ~w8 /G>'A('j/p1`6sB| ?-?wR 4MK3!lm峯R8nϫJx Aѭ,~_B/~< G3 ,, o`LTu}{U(Ӥ N$@ xQ)HÇ 7l,QѲ$/ˤAҗ392da@ gi!g=V0<;"vuΊdQ GY.ʀ#_FBLOu|! !@$ H@ @ҏ@ R$@ @ (9aT EA,p7K"|p콂Q<ۚ6bR`0oMu9Sbu1AzTi_P<.ݘ:6QDğ@ B{QMz4f4mрg ^<ą.`~ojZ rc8y$9ӣ|4f2Vsf53ì #~ġbਪ dGȩ8n=ɍ8sβx/}$,W>ctfu3qS/K k}~Do;a+i>d6oVvh{h$  K/KESGFmHWUo΀OVLppkfGKۊߕBUK;iʹlY2wZQ9$@\Zwn[wU\ Ʋ1}ǏaJF4S&_Yc'| .xqo7s ܿ@ <Jc5TUp 5ɽ{1Dsic ]X}&?N@G_}ӊC5SE*\܍v.&8)TP]/˖`@4~{WҢz5XG_ٲ&c 2^BP3$~8E>|cGuDnԈK>sZT8xaQx>S7P;ya86%sj׎ v{Zy c[ak> Wrɺ!l5\sy-;:W~ BQ5D$ аgo=զb_ }Q%sYXio@ dx9Ư ePl/ FXuC@5drl,)t.Q(NWH1jGub [qv[ÏøHAݎΛxpBy &lƫ>~FhAʹ1>I5viK{,a>?dQIYKvriy2rA*IYherO}MrN៴ӫ x^$/&4Ӕ<ሁ@ɹU{)`תQ3DZ ^,G8yٱG9EW)HX[)aj#UlG+]>cƬ ![.| kZU|~[U샥(lifN\dW.d4Х'`+=$sg"|BʠkdzP\ZJ`:=}ϖ-ߢd#DtB2<~]бIUO_.p`/\V{_sgQP;iQVG3XMךtxѲ#^w 3d1<\ / ˯[ g5ac^D0YtdNl9䒚߲׫\O 1L͛ݱ<f0#ԜK:Tr0 ~4Lvo7;i}I8-QyV{*p{&l>\".sdz5L}bLjwo\oWb`ʹeGqzL扝~=8q8=jc !;/NfFzX4,G[SV?B W_>$tiʁ h݁'N$`/r"*-ؿt# ߹ }rdiyWZcN6ᷬh7m5؞L6T3SBA>camXt&EG5[fF{%^Y3똳5\j.p)z릓Jn L a?K`.?I\ DyCEEu &8*)M:ʉk7/Zau7 IV+ۥ=?CT͵ 95UT4;&x~@kɄ/[vNdES6HGr2pgѧ?yDsG|Y[Nqa7БQ,)F-9E'[( <-N/,zZ¼vW'-_V!i7PWn*T;\n^IBaheT ?(6fd?csH&O4ZSH$9Vsx?ފCw~Eʫ2-0~ё2>B՚-<=#]GsZ5Obm'$NnJSefujqpc{W j_ O GRUJ05haQԳ/ ݢ/x6\zpHscr¨I!SLJS+L&%$.M~SٗXLy3Q*Kfߔimngz7yLU|fУcAN YamS8 kY$gI_xة)UzSՁfsV.3yhnxӻX@zH3~Rf ykIY(V*ΑK(}]5ٓ{W޲վ_%ğ@ /j¨RP*{/cB1i?odpznj;m׊[}3ν:RM+;\ϵeE,*R6y 䧤=&E##[wl:YB%<,9ܸG=J K J̫b[ͼ&Mݽ+vrL"9up~C9nտnE<<:4AzYi去i4{w|dTsC-=h|t0Qm MBS`mPal뱿> dJϩ_&+0j92P?YфiU߮huIM1Kۛ1by`!˯nEKgW1~&},=o2+IGgHH(&e[A-EFCZu:5ƿDHj3LP av~* ھ_z)_@ Ѣ!uލpmC&/\gĨItɓcvi?gR?)gcL~G殼dGn c#)_bp|MXf%HUw7O,mOCUԳ\͸gZ{ F;uꍩrF'ܠR!gsa|6ό $}C;%1{ǟUƤ@cJŀ,F 5hByg%FC `WU._9]-x۲чzVè%˧O4;wmPDʒ]b!oW>;ǟ$8!yi8WAM0~lEƫN<~^'3wPf G.em[?P.z/sZnd{1szd1v2PjG-?-fE5?L[B_)+V3__/S~t;ed.c4ǻ1˗RI7^T\95s7ŰWe Ty(\КYL++?Va2/cĈ|4av6*TT/_bh|xj@ OR X#S$}T>,lϏ^u3Z Q>Rm g~G|G|G|lb|gT*Qc[^ʺHdzsܜ}_ˉ De NMpJ>"g~@ <9Ľjrgz~%E5 /WhaQ?@ Q%{+Dqxy8 G^,w5ps@ _ AERFAe ?_6 ;"@ T"!@ } 0ËR۶x- byOʷ± t^1mĤ` ^Eߚj}au1Az|/~`Kп+hbw IDAT'Tn`֔On?ʔ0vA2l39`}'зcܟ/b ĻRi9K$T"][,Y#XR0C™9aQC,iɓw[adG"[s|,0w] i0m˨_y`K'{SSjۙȍ܁oذufdh4LU-o=f45S{zGoe?1rUlԾ`iHJ&8wﶧNu#t36Z;VR[)ʯMa1|u5h5p uZJʁͬAΛY sɂFR7 8Ej\C.U> c ٗQkx G?dODM{nycG n ܃V/[IVGԶ3^ 7֑|W XBEUF:“&khbBZ<A5ד_EДaXQ}ƍGjhcP5s$ ;Z#I?g@_R[tT. lp#x9Ma=܀>o2m[oέGL]yk(iBIKFY~Z]Qy_W9e8}w3"k;>hZFı]|iWnOܾ.>ךoXX7o'b'!N]hQ baz$ j?mJZTV=\+TL=~93Į]ׁQ\^Ņ_&(;s'jpF _1̜Т{z՜JFdݺ4̹!ɷ_ _ֲ}'|Q._MCj8|/b v?[vtbu8@Da_ZPIOc!UR%Ư evE+9|F`Y0j;=M4L'$ODEF%wZ96@Oghs|1s9:L!1 !G60kh(i4ETL_}TcgB S Xϋl^0d3^c4$ƐUYKvriy2rA*IYhkhUtO)\ι;9]3/qsh+\p코EX? kZU|~[Us"yΊ>U|^nڽGQ4\b_.sT.= kXuC@5drl,):tT8x}{"Y7}1GjuK\믍ߕșSKa?b82\ӆ0fUrk 1 m_W]:Dƛ6x}!dKп)>.Uq]*[1aEWU_|TӷO%-_*\!kfxEY|O$sg"|BQEsپ4B?X<#Na_AgFe 7, Ҙ3, PT_(x 1m'٬t*AiQVG3HـW?edWnߚى-\Rs8E׌oOK9,g5d!Gɿ~ʀ;^-9^NC&[jg|) ?M-}FL|h$Ë\J|:9?*޿ 9)s $ˠ#HZW+yD;NW:{V`Kj_ˋ~ l_b/G扝~=8q8=jc !Ǵ2XS7t147"_~FI[J ʹpTa؝rT4ՓiomD=QZcN6ᷬh7m5؞L6T3SBA>camXt&OQ&^%<² i6˧\RFuM'I5n݀X(P(ſ'󇲌ao*t|'r~vHXv6('$^*~g+*޳G]hēW4ԷgLp, . @pRU8VQ!aԑD2Vd] EUGkP3'\ӠЄjJB/_EJjcOA<6dR18yD! IV+ۥ=?sw㒒Kҧ_Qܑ,_֖ӡa\8ugomR2_y0ݕI˗d r_ёՂ#b}YuIB~ġ*|ݔvtU˃J}-_id-%/W,kӁXUZSH$ሔm58燐YL(Zѭȩ$rZ5Obm'$Nn )>ڧxR 'F XĻE_XUm{պ0xu Ŭ)TMiQ>C?ϱv0Pָ~#/H6Me_bPKfߔ6&r/G[)ޯ@Wݟe˥P>*gqfD&Z]!たKu3}VIp=2UˉmT7_wGhԴ1 f8ƣ3Oʮ}cA]'ǬL6 #/2?V,zAN*ȊN ڴ8qƼpY *y_YﳋT&䤐&\?IErWp$7^&$hE?Xyl_> fKk ^c]4 (PƎnI?Go*z& g.qCV:&g N1cgZgW1~&},=o2+IL&, #qUѬ 0upJ JZ9ÍkXxԣDVsXKxY<.̄ݻb{i'oA+fOx#[wl-P&ԦD)Jug]]˫>&G$WKFE1e+I~Jo],KLL 111&dJϩ_E2WŶy975ν:RMnVh"9up~C9n)r_UYRzVF'GFM57;ʬf א.Ӹ 6We/⳴rV R԰cw= kJ=5dB#NW|/ S] :/cr+5cB1_/\3vsG{&,{%(EFCZu:5ƿDz__gJ!aHKLԭ 7#&*78v.$2e$b111gHAbv%x#Դ#f-rMjkGa9d^0xɽ=6։O9^JZרi"nwg2tyDv2x԰ޥ1br9v̤~nKx{%8$%=fԤ?}ZbIA13⸨ko4*FHʯMhգ* C|^_^~g>ؗζ86E&opU}%LC?l9nlk=g kI:5]puZ2nTUKHU0hDhѐ:yGʵ}7ĿgDݶ>.6غ6m} " 1 J[3 %ErFQe7og]TH;wd[0-|S^oִuKSro/S=wѽ3-˿z3_eGoU|h{o7IӁq=pص-zۿ ^>v3`Zۋ69W\_b?zSbӴ'=5nFt?qX$ǿS)^m*xιK9ŹijxzЃA0;ecCcռ`"3!lm峯R86U?o&9$@۾ aK 29^UQkV f8wG/ Ӣ sg +-e})a%=%1{ǟUƤ(JjWXwv3κÁPϏ;;~"K;s)]#Xni.sD":D?T_ʏFnbPw*PI+蟨Ͼz[&h73O ̀k?-E:i]e۸IN\:T9#_no,߿JO&?*MN][3rGd3$w%?([wo i-CHx[Z˗><+g1vGi~ j}G#O[Je>~a߸\GyjO?ZbZ̊j~ R:WbG< ^v.e\f_i>ǏwOM!0,YBa~-㏰TS| Sr{PEA""dZӅoTzJ~*x۠ϳ1+ذUZV_Բ5rrIݿ_T<{[|OJ_!aҍɫ eG}7ƸBk+/Þ̻ܜO`gcMߦ5(!/*~AJ|0NKyE_/ 8PCN+km9d`Ix gmD? v蹀%1yNb+ݖtWٷ9HmOP`Y;CMK(S{'s kw-?}V;ɮ^~U\[DžLv (T~>%) H&8wﶧNu#t36Z;VvyiS;g _]ͻj;Z {]<Ҹr`3+Ef c ٗQkx G7P>=7jh1H<wb>(is>R En<|Æmگ5]"&r=V|&"a4v>sQ9ybt\@(HGtR]S>Y2&JLn"*f4$a, &mϥCi|kʧ|0v{ٳ{Rp+>h K%0C,#dO K%:rI緙hf*Z(SW66"eqmõl+yL>-UZe}|26|SO eI.X)9y&CG7 AP$p`j[Z@H+[r,QjHGYxUO]a#:11DSv%Ң_G}0Ws2W|*\܍v.&8_Qav}ZQ.cr>tPb1KV4߃WVIO?x#^I+>}EaO0TY=%gsKaeג\Kς3n$=WC?W%=~wU{/Ϸs:UJ=ԏ.š6uqYJ5|r%#,?Xή<7})1:*}Sfz>m?; qB[j{ _yo mJZTV=\?] jg:vq!Ip:|bǎ,܉?n!ܨ×|&38pâ^|>q53q7=.snmim1װld+9'iӐf.˹<ϖXݫN?G>=_S=D썠TW#!/ܵ<^gNFn'VS~*Grw>㕡7KɦA,}<&fԡ T%q6K^iq@Ɍz3~ [! 0f2Gڟ>ZʾȀir-5'mP)ufl~\01c#'/ZH˓RIJB[\,S}?q1c-琢Q)8ʟzz+3VoxMSa߹7q][eȧ֏SKa?b3}#3mh$7qWD9prUD`+nߙLhvsXŨpo6)vw(,7njSaMӣQOˬ2iUi IDATLOmK'BbРqftbVX+ ^rhcc/%%<dHp]g9fv_Xz7Ji+%PzOΥ1۸fصCԗxoxxn[1Q.UDZF2w]*[1af )4RV.uܙ:68Pto-ȮAMs 93PGxPz #e4 .d^T_|%(&/GO|B.O+8>4@xX>}ؽ' ؑf;rbcS$t70< c^D0Nb]H׃g3v&²"ď1s>ZHpv@/5pOs3;ċ8#\㞰s)6_ɢ>^磉 `v_jkhxϊrB0až;dRvyC bۑ+D_;{0v0hB9NhX29nK4B$@0Pf)(DU?)*M@Qjt|I/AJ IH}H@J]ggٝ;̙rfk\HIX?ݹ6 %&&ٮ4uR<&/yh u fTٱ־xɯ ubߤ|E}~m*S^ϗs*rq灵ƣ;kw&r-~+7[#=l&(FJk%9ҢsH'r&aCOJDy #9j4(L'eIGGh R1W"g V 9 6& TJ_-iHQ}h[韕;OsX:W`݋y>+x?;1@$芖τߓszJ=Lvܮfs/u"_^uaCg~ *T#6t-s#$߾[@rdEi_c}UǷr#sOY6b10OB/?3_1}^Ǥ1hҢu`'#fP*KITW>/vQ*/,ZY6E诔7S@fD=[ӧF7.vJH?Yr6f'-NYbK&IYg Hx @PSSwj&eiX-sΟLڅ> 2>ϲ$dMT ^36rZ.ΆU 85ڸoq@e'bLlfL/3>X4y~χLIeL?w9d"  $ƒ)YcQ`̈&V@,a^ G}w ցAJ*ڛx͝[)֧JKDѴ}V kk[9x:01,LOs-{ۈ5>HX3#uPa~[JG|l"س+t2jkU!m&8`t;Jd]jԽRuL%ԿILYkrW*a\NuU6\:ʱ thMմ+oI$pTݛ+st4FSPD"7FdIJYEكX=:Mؘ~I &JhWgr? V.8١S%(.ɭd.++Ue7rXJ'rPn]f#3D+iP5JK&_m*ˬ߬ߗZV%dEnDg0#&7$^⯸Jt:mj݊;x zPUǟ3$kf-jժ VM/ۏt&Iiō7Fyn߷}KBݫ蒸qr3W$.r1TC^_OY5f,þ\YDZΓ7iϬ=I ?;=`(7̐9r(ww@Kk9msH9o7n.mA[)IQV܏I{Op16E@&3'zbgӊ_Yh%̟,].J 3+/?.;Ƅfd&i1س#/ٌ6i+%/2<(CܱuQ_Bτ?b/]=|! <۹g3trٌ1-k;_69wĶLАzu7 'H=EěY'Y-ʟ0e2:Ob- 9kfsW闰}y/Cb [4WmD2JaLY'+쭀ҋo~*K7ZBfMRG1[?`?nߦ_ٟSaƍ#}k_lZu6/Gp?F*ѠGnt1(?1y! @xCK?cݙV̟ualr1 9}Р#10}[3thbD>d~qk"}beS "T`SO~gZӎNBO/_A&&@,N">X6%O};kBj#%aLޟvTgDurMW7gq{Wb/<6p6IL(ЄЏ@ |StdL=F2[M Ϳ] 7HzS\4Z̗ v3~.b +qtĖ,RR1XBYSggo7/.{JR&Onfي߸uwֆfSW0.c/1/4.ٿE7?bY4O簪<^g?xg!e@JA0{cܬ $]ǚ9mK6޼<=^oE%IOzyv/ZpyĄ&phx3ڈcXDg d~r,%s#sjǡ_t"g"U2_GE4ǹpkOQR =~Y/U1OCnP}"^g{5u%ԏ…Э\FG_g:ŠY9o0?ݧe:dkL00@ X*[6M2 clV~{Fyh_q՞o%Cd/ʦtvs__^˭ll#xnrqK_=TȠ6OrUe7/嗼4oV(B~SvY#o7BnL5٫p}dwP5/t]U *M&ȫVO[KfȤ~M0~LٷF<&OwyGƲd˧~YfQʮ= WpD`q(nZ([Le04JuNfܽԐ{\/l n#wsQADeS,>J\bhoO/_{[;.Sah^wꬺY*!+#Fd]%(Ǩj$q'^%Fȗ+8nF:-ODF)p!q$ɐ_-P~R"ш95{ò!مifWXq@$}g z;|4F>kr7o7i΅!^q~Cᦺ@2e09غerJ5^}Н `P2dؾ0]ftA6*cZFVH4AG"ڻN dwHy԰v@LQJ@K|;rR0Wb~P$4^T##[|*UƄgysY3U?ʨ HQ}h[ AeGCDVFrO楝x+hoH+j:PiZ?yOM&*у^5j/@ Έ[DPe_ c^|EƼ^e9b9i` IC?Oao|yv:&9FhԬ>M1s_l)KR&.sKGϐx}!{u _~:*S~l3bp|N.bʬ3Dm)+HHT,(q}yKGz0;K >TA.'?KO=rH qF  u++svَt1#Xjw6pq'Sb Nx92!%OAMk9֧q6V/eS76f_bLON\:eE.m#e*1s{oPC\ܠU8r:_W\H$,I_n)qKHX9dJ(twRԫK/ڋG 1OoR/ex+rn/(Cӹ;|:1lX#rsN߶§^Q$Kk⎝F:ו3.`-ӿ7?|dCЯ#x c'.e/яҩ\ρkX@ 嚪 IDAThʉV# ~)3FwU=ڃ>s65=*(N+[KM߆fS7+/kdƒ8v6\i\ɪk=ao.udW84qۢdm]>sNv_BYZV.xt3DaŬ7 >| G`%5QW]{gSU{rOD8=56+_C d-kfطIBdP h(qoN$N^_Aw_"O>AMu^9mLp )0~/h|Fx'+ߞYl;t:^edK~JԭDUw7뇱쓯aC5&07ĵ*06*`<wD988+ ߽F_"·FUsMHpS%R:3AU?Z=U|_}C1}ԶDltt%M;7ھ.~w.qvNt7ޕ`~k|uTQ=R-WIFXz!LF5RQDEc&cNmW)u(aTv$^kߡP=zobڟ}O}#uVs˕[[NKm'QJ*;"'\bHI2$Ɔs,2#Ah &[^&71ī#r9b@UAԺEe4&FNlvam_Wp!"н=;ۥ1\3cowVrvF :upR] ۾95k3Ap{#:=8IiI+|AzTIZQKM?thdOxʇ@ R1tb>+P5Ff|0F{%-NhCflTهN4b]I#^ h ?x`~G] #9SSfi5jp1DdI%_ d4^T#=[vKKƫ+xoz\(c+A-B8@ F v3b a.FƳz,}Ē[I[hIC?Oao|uLs&-ѨY}қ+?bR|WHNzنF%/lKjő_FSa&.sKGBϲ/d`ϵ4ԝ᫛٥S> {@~he-EY6b/h0>OcN9XaoBѐCZNiew >W!Kx3VѾ\Ű_3Ɍx\ N@ B%jk : Y~h^߸%vؒERR8F`YFdIS72ti-{~>58{7NO{c/ 雏u?JqK_sMON܅:4 XQ 2ϘĹ7F>Sܠل&v}i߻5* skWep>2۪Ĺ/QA,NteFPGo&c z# I4@PnF31jSH{cb]7Nb$O'd[*;?u[@[ХFpV y}ց'1Nɵ|9e?eዯU2| xLONypBy8z7*wòcOfWL'_f߅\:ғd'sx&7:1x?b  -pEi+м aΐPs%+쭀ҋDOܱuQƠ/FoS1cR$d˦9ADԲʕ$ ʗU~iVtPIvMV<,/\1AnQI*::Ɋݙҏ頔_'oJD]AD(eS,&G¶ '+*?<:sP0qm;,49_ı;*USW7j% /7; -ϬkQG[6 MS05m,^wϓb 'oj vO_1-ű@  3Fs$)W."21hN.z2Odζ#e=ѹBw* $ `YA+Dfp4{IFQ]>'6;zT_w}¨3Vt<ϳbIfە +l![D`ކ^$,#Ɣsk$ûdP:E'a,836wp(u:e_׉uiP[HI 4k(5w!=pV{Yu,_sXA@K 拣"?eohZ~ƪ fCV'H;Suַ  QGX1k9 <^g}7δC$K.t1wl| :Όr*:;=t¬oy|% =:du;~gJ%(C#k FcZm|' DE6Y2vwֽ1nVcvJ6޼<=^oE%IOzyv/Zpyi_evc$TT81SFeԆTBOmfEE/6F9/ȠwFaE%+=ن''svIMOM8dQ>b!ۘ•?b't=ρ, q`{jl %,JKS/و&LOk?,l^c۶xZy*[1#9ykR> 0P`ޜH:w{̔8vuԐfŖ$_9mLp1iI^#m===s|8Tn^g/jC9@֙ Fy|ժU%YBe1.@V/ Q,ʷgg}?90e  Jh\%ad=YI jedc I;ϜEόHpS%֛*_ɆC3UV I4ϸIDLJ^{y~>K6TmB s73p~X\Ό%WIE[7앐hP`S@lMƛnc::_ K.:+A(zMaXaWf1nT'EȾ=_ѷ;F\+Ux ?O"}_oxhseFs#TVh.#!s}?JV!WM)~e@iW387r~/Ɨ'#ٯ)D//q(bhoO/-I8nr;40EӶ4X"Yp8"xNo?n4|)=r=uxsS9&HC{IuR2SM40wme `)]z2-OR2KC>ۮ/71rj&NeC/ B _Ck u ǎ71n ΟiJRdr#xE~:F?dNfbʴw8 *+ُYˡ~Z1}m`{Eduv1'-Sx: Qkfa,Lw o'!CBP']oGP%NN$zf'a|0tb>+P5Ff|0J.ZmC A։L.,iIt }m-餂= &Q_1MxYs=KgpYCOŀk폌.)9Z !c]Zc~A碗e$mY֣5J\-R> 0{\ƌXngB-"˲ѯ\y\G~΍JoW$J%%G(]Y~䙥1'Rc!K>#<ɹi*<?IzX;WR})!7`g?;xIf4jցaʏ/߅ψ)9)öէsEJRo횼 9|t^Kc ,;'95O97[_<d6"ǔ9$ٟdmrҾ&;wi]߳h<.{_)r^Z[|@Pqjh'P[ߛ| U} !%vή:!޶4?^ASՇ*$Re#@UL'kp˹8sAF$22VXLŗDn:PYpJ*[L' WN7YQ篐' PtAa=ˬCiMi؏͝[)֧Jt&=H!ƹVU^D),WQ&J&Oϖ1bQLeCpzo˼MtV.8١ʦ~>'e,l%m/V3䣹>x-3ywG];WMoa̶ߢ+y p-ݕpwRԫK/ڋG ɡ9D֍ۀv}gBwn=B|)KgC-jihcH{oyhbj O"Y]w4jԏ93C8} zU*9k)8lCMk Sczv䴋9݈tF nnSj+g\d Z+oyۺ'k7+߱^n.zc{RC%|nnV7;[7f녗wѹҴ *$<;3e&ȊʟiJ>\t݉< tZ(U($ eqv[T=‰Slha%O86*[涫 *H}αz; 3]6m&n/O %GBݫ蒸qr3W$> ?6m?V8 'zIl[ Wwpq̺PͰEsya1@'+L;K@8Ec*l _Usp XŹ,ɇ*&T'JNL8H~%tqQvUi9y7Pʗ'èAvɾc/82}kPE ˯A6|H쀤T5E:7r௣ 5}F n٫Zvހg0,/w/!m~z6.ufݝ>_@ז/'$߄I<_9"俫!yǡ lL:c2^_Vȟbwna!~0¨7U o .dh*LΨ IDAT7߽+lZp\fy΃R^rNSF>*l}hʾq7=~%Tc {e'o|p9Qx>|;#` Mff<ǽ l9 ـ5P7VC0]5Ĉ˽-]~ 9o ),?skכyťhhN̔vUTj '™0!.*e Ha=:LMė*cI/|LKfG3_& t awL$O8 #o@N<+9?n 붂R zH0ɮ1}$2hߓaNp4_('pm2 {w@aVg47=a矊DhQ ~M;? ۳,$Y n@L+HP}c?@ |۸u">Yp 7v r#.&@,P1o\T4o|Lū5 3/%ɿ.B ` Hʼwrvp: l@ l] o4c.!t_: [ƾ[͔O@ h''J]}&nC(m84 r HYTHڒAP}JӃ"ϙ7`xhZ5qC}I63e}/I\x?&/%)?.%ՐridfF>e^%~. Mu)z/{\Ks@8Oj0?mHغfq{IVj 4s8;A% >Yz`1p;*7j4ŘQ:uE{aՠng]< ݗ#*ɖπ:4U ؕ o T,B(-d2y + ҏ9qW#]3+ʢ}7e_ WO@ tk5f/称X:JghtހimhLq5O/xs0X݄iOK% В;\3@jP  yxGY#{IIC;%hh-oP[(ZUQjU%RDE_kծ],Y$d|$r2s=,<39h0/dsLK8'Vo?x6 %x>ؘY)mļ onX_?RMIЮxۃH!87o PB]b3a6wgh >V>_r:"BD[R,8ujAY|"c:u \z UQ/1 xm uŽ@ xJxf\#0K_΃+8-\9cMtq0R W`<h` *-\8 ο``558O2&˃6`#(dq >ېw%V#0ttDvkF)=|"mcbP~\ 48{03;8;| Ql[J$Uߋpln̾*D'AU3 RE [" =HS!|dW{ ͳc"/]5Bز" OAؾ5H~f 9 WypϦ 7(@8~Dgwn9$Y;ubu O_Z7HpZbE))f+ V?gH9>V2»ldcKl*:1vp% hkчY=kGD#T2}b+WVm/kArqs%;&x6<"H:Ϯ wO7ۣ!Q V_1i$l|1dpw^E僁,t ބx- q1דtuodָ|& gpx$}pFx=F  =@ aKצthZsI?:ڠ MW2\PWyG c7ڬ,dA>*zE4ԇJWz̝&הix1EV_m"~ĚX:6G9YuOaX|3u!W7)CXq̨rlIU[7Sr)Ò|V\2y?W1}UR=h%hF 01~\H~FMH%rt3U~k7fUһ}JY@ (xdSY,@|1G4V3 JA!ЮvRwҊZ[8Cs0Ñ ^;>tneo7pz*2l~v U~ۮjx$ɠɿ7óocD5ԴxJzuPsxi E6&Rxة@SH%Ҧ @ a< Rj5Zk0gaz;O'6"$l 6%5:t"+=.`L^ԖIq,KY+[2Y1VEYܜGCm9w)+gs|lч<1K`KFuWfaG/@L6J+#q~Hjr[ڵToיIGX+&xCN9ccG`A~ػq ȄGiԬ aY3|V*P&I}8k&7m%g f䐖S2ˉ ~\ N@ gqcƸ Z4cb&Ѥk{'Q %Ӿts5fUIXzcNz!wH6 K Te4.]j8gdϘ{65dkAe.x*N6mXέl|XT֌47?Gcob31@m<8B5R;չ8v!Ψ9 @ Іa#ZԩK!`/yV%F˚y6PpT]_,g93SX[;EPӕ|1p`rOzva)dɃ֍\6SVho wzx8՜lH Kȿ+R&Z _Ү!];rػ0vVwų ]if<^θ΃4 -xߖ6_s3ƾ/VѧUu: 1 @ TĖT1䥐f&=?聃I8O|wgTz]^V ąOj/.Ûw1[FL *OoߵN?Fcjr&u#>`XEɍ|y = 6lEŬ jhѴqB!~b#8\ZnZÞZߕ3?]!G*f7K 2/:WCpқ/gs5pss?j}@ɥc;H֛"#M8~ e*R bM [Q"$dHL g?yr*RPʮoΗ~N%KB" " 3o $@3/eElb&Q\ǎ|zYl {1Y7B@ a[}&@ @p[k8@ @ (0 @ @8@ @ @ GP{1A;XZ snL\Dpp03%u["oqSlĄMi/_^ĩ\U)JtE5}ÆX?>(Pb4ݜ?(شf~S9`gK &88ޥZN/B&+4GƐ[H|M4ZX7р~ aȇp-硡s;[iyOw0(ϛ[aS6.폧nTO-[徚֨~*y3a~d;}/!D[ F}Mrh+JVH(U$ *m0R_Z"L|Y%x'Q IR̘:/9G(i?foɒ2r"[Gc_Y zje2ǐ{IfV.nlduwY KQpg~a~,}W5Z/S:b&'<PtVԏbŽ}fQ0Q`"o{J?RbϭҩT?YF%T`8!;Fvv̬狝:*GjVW1a0z j/F%J,mm"RVDŽ҅ns5bAjΥܵRKL^ͨeFN*Ҹ~*_笛X?>t5#@sw̦p% hkчY=kGf>|}?nO'Yr㬕.ldcK|wH]~DBZ9'kM~4"coGN>;7\O}lXK^<"H:Ϯ gZ=l|1dpw^E僁,t ^sAGɷkws3S6*_~ٖEcc\>$_dtg.8.X+K!!A2(N+̩b#=[_+yb!+λSxwŇL+Q3~<>~m #ܤj%ˈ:a7Ffb(K*߲'mCXusg-1b;ӧs3G6~͟ [d@RlJ~oy0_d*rӀo.װd]4evl Tj%rZRJ.ryrp4,7|VSdrRַZt#g'+便z/fwW8g>;װfnru5 a3T&hª89] 9dE}yŮ/4-돒L. ūdÿ5QՏd)LZ S+bc9r:Vәq(!Xx4YhNtuj;"sIԏщUq$+ k#Vd]txd9HtY Z`Ɲ"q1=,gܥuw}zMj^ŕ'/YlЫ3װ?$M[h7/LTȉO .Wfxv~m[h@<ލ# D~љ @Y8 ئ=\+ldr#t+ymDebܸ}~+li>czZQnZ‚N 3+CN9ccs]۳'w•13{njy=Jc): ބ@&8JfMhФ t͚qI9IŪTVkor򙒿+-Ȳ}!2 T} 0ٌؖ;GC,q_ߣLrՏO@&Eq*sIJW$ !-o?65hڡ3w@jq~>j%O)_R+B)gqJj jHj+< $3ISbm5TAB+̜}pDCdo)%2V]R(qz;<:#N8ΛJ{h":!ce׏Nshs4)qK ~Ϧ׃^Ur6wn`["T2=&2]{og^d(N>SWf7Q>ţEc#lq<vE薥ɺl,ϠNՈ|%N2>U?LWsx .Km<8BQ\!a^ݏyn_39tsjۿpCkS/!]g"1\MpöY F.~[⤒PDUmuh $)CףӂRB2>{PvJVLM2r~)Uine*Z-u9Ogy~ V~boz4r@ x|-6y)YIz`m7Nc$ IDATC8f.~3hBbfn t?U,q7#.-;Ixo:VnD0_MglيQY@)u f8MLZp0'\O#ZWyڡbi9mj$(8CFW& 9-myC Pj5\m# ڕG;|d{=?P۽!g_0*1-BɯvPgH7oHĊxy#WsS{U l%8v<] eq 2&c96@j,2Ur~)iƁgH4Yv= 0c X?导/Z D!Z y.x:dHˬHd)ԕ-} R6͛OymPP{+7"=vc,Q" VClIr%lm]WʼIN̆cxX x"ܽ)I>៬jquO2(@xo {(]6g]OL@ Ag @ q:ޟ_&@ [R@ @ F@ @ Q  snL\Dpp0gQ߫p뽘q(ܺX6b¦4үFy/!Ue.A z*@ xlwMvhn/զz%%p`Vo:Bt?voh_Vt8Pimh4u¿agI)M2Y9!Ax;R՗?=3,gӗ~e~IVz0[ @ HKR_LDrn#F91Nyޞ4MM7v4GQT^A+Ĥfhs=gXuFR J srBÅ("p1Iݹ0kcf=_IplHs' btDgöWXֳ=?vcTbikYd Qt㬕.<|[Yǘ.pn;V>;vnnf/%Z5u#fhJqh}vnذ|'+Z#~4Eg%uLif>|>8+yI?_'U$Qٵx!;#LK(62;~/آ̎@m:Fv% hkчY=kG|3^ K* 30 [8Ы_qh@ 1ҵ)V#uR )T(0H<JZYI_]7fN&LXzT L+=owk 0*'䧥lC>`(F/@ Ygfз E)/YR{44s!Ɋ5!;2fxW IpJꨢMK^cPMcשp_ޭ)sasp35}Sc׮LUL_vsrF gDL5tlCrv*ţĩl,26:h N@ FSY,@|1.ٷ )7p[ rLn x~]4Eu zِlЫ3װ?$]+~-͋.?ـN tp{ m3ɵbrgQJ"'^bhI:!UV%cLpFnNAK~ x ?>J7F-ۄ2O50thZ`ǽ3NSU>Y@:>FY D+SrFcχ<""vH.h˹K!\9{c<ܧ굤 d\!QW|^'%fRy^ WtKdV z23> @ ƧëvF"ڬv-b ALUl2ɫ.$fwSe]OωLut-V-zTKY0 Ȳ|51 y>@vH FQ&4h҆Sf8f{B ˏ8ЪZATc<6pjpx}˞ĖT@ a|$ C.i\lhJC,l'< O=Vd)Y>4z@3j)t.A4a_B#.9-*HTyؖ ȒCw7{牢s`8geLr_F7.W6wn`Ӹ.k[RS;!5&<4ޡ7'%Uy5;l9dEF!T`١ F֍SӗC_.!,'thփ/7NFGmd1 {tb9gt]-*x:0d!VD:0]JPn W5j<찭by0fлϹx۲kr%]{KTjחպJK&Yu#WԨUϿ1m–Wsֳ!5,<7N{wqƾNx67<ʟKCxh(0S&J˭jGoܨVx@aG+Y905D2 VM0祐f&=?聃I8Ol1\y @?'L*b\#ڕG;|d{=?P݉/.ÛnK-#.hF1;RlNcnpG͡hS#tlz̉ > ֿG:N'*e=֜`VZ̺ ];ahѴqB!~b#>0 NYnfO2d_tRo94 X*sL-LIv)xd9K~!A@ھ5H_hB lT nh:,N_f %U s' p@ ,-@ \^{@ @ Tj^m{  @ Q {F/pO 1 a@ p%@A,V9Nt:=\QV-1aS0/_gǭT^ypQV e.A z̓h*9&?,hvG=ɒ-c7ŠƳ|^kՌ϶#H_,aٍ˂eX@= Q2>S:}tZdD-s'Kȱշ9͜qKEMµM%?,_^>;LÁ[T}f~TUR]O}&H>Ī3O |yEU]O?UZz.ԥg,9 +&2JS{ V1}%CvlV(WIRbϭ! TaTܰ@!m_Oാ1~ĔL!sIGY8ƙ !n8PXb ~bR3Kqulb?TmB$=O~DkT>#+i?foZfj|46{7?2<;#-HɭTèr[HtAεI+~/ۚ/5iK jm*mf: $L `Iɫ[EO1萰Ɛ{ev<l)4z@D˾3sC\$]dž8_ހDxI:3gRw>GY87Ɵj a>ԵΝ LjL+N> s[F˞ET|Z闯|T8Ĉ^~qZJɝ|v773eliߝ7$! `1Bx91_V?#*| /p1Z̜He2|~I,'` Z{~P}?Iz袎B9q~w Xh R4o|To3A\ 1a}FW'U$Qٵx!;#LKOY`sMoӡ9{+l;P_OB>H Ēw!/l `@~/%ƾXbE))<*yQ2c -h#ec  %_1_`yIw` _)p4ڿ|,x7%&ڟ:149#ۖ0+jC'O7E%~P 5bļx2/~A ׮_u8}i OCX7Hֿ5|[Yǘ._%АN.<φmg{ $LkD>#%9Rݜ cPMU{1iӢ -vVI:浆1zg V/Mb+enBHfe/uR~dnESV"L]6ƍdʐ(V\x42Zxyj>vTk8oPve4b{Ь 6J,=`c_e쏹dE^JDp:I͓ )$&e+QQҾW=~g\:tj&nq䵀.eD?@i97d:f構ye`:{k W|*\:q~ܶG3`(|׍_Q`YUOOKr#t3/:}aQ^z,H-R³J|0; bŽ6ӽ~NO[WTG]˟V!_T1hm>hxC壋1}~(~˗d~Uv/ g唹\ʰ9Fab_(:~kbٜB?ڋ)\c.4rwa/'P+Ɔžyc|Qd";/l]hR-%|Lr미¤ǘIUi΍bVd2"b߀Uk*]#ڃ]/uh"/5%n%_`Ӳ#̝_: WڣUt JFvcp@J &IV%P^+ӡtDX\|Fͩib3%RvykW2.$GJl;~A`'5dEsYGoy4?ܫ&v*KP.$}oG\B9.vf_IiDq!2kuIGįv<7 ;O?·+q0ב~ݴL7䅷&3},kXI,".i㎅ƽ|5TkOp2dS[t4Tm@:NnNn!Li{S9KԏȮۉU%㍕OQUR9CEpȫP D_E?Kqzn`ȋH _OaDUs'^UMҋIY_w%-{+mEмm%?BWUj`'q'uwH˗kHεk&5VJ@&rw ӱl;O4V{ 49(5&NskNܥ1j m} Xak0*[=aƭl \ _L۹KH<5 f/Yk/=Io>xH['(kѤVk*6$jGOpV,qٮZObL_Ȩ$/,{6#OxҠic7hNIoo_1i92m\q1KRtV2Y1VE&_'+,/ؒr" *{,he I#F dK^/!Ùd#FT𾩁\d%f('ud)OG^Y֑ 3SW|z4b)0hhA ; |K_W򑰮مC{;az)~ɩl)Wt8b0N'Q[rxGD$*Ew.5n۱.YަkhHgS]OњbʮR" pbo$w5}xt$N>.6ɊK7-}Ҿq3FpA[] >[A*|F:ڿFʯR'=.`p+$$ ~j|/ dR'eHa[YaBC7Οr&Fo[Ō*$jtJh[uyQc*:I /gqu2/NW߬ dQJVt[dܯ6~(VDX @"|wG+.}E5ZDOX!nSCFɇ1L\t$}y<"$ǐ$.aFv=c'3?liiS`x 7u[t]S>|VJ.5sGpLx0P%IEFF-,Igm$&<5$k{zMb'׋2 8:eM#31%W\g,e]OωLut-V-z˿!+\N6(~hQOL @dE&WG{Y&xx_1)&m߯{qVVygK VPTn)_|ez~Ao. </K޾d2aQ5kB&m8;m֌cƾ8Ҿڿ(ʍ \tSBOfdʽҥƐJHUW?c% G-\].Oj|UߣGgI#slPV% !-iN&+,28u[aE&IYB HrTs!aGEMvtOP> ALUl2ɫ.$fg,}]sS- Dy]=N`w\b' BU% IDAT:`s9W-K+0f:?Iܲ9 ,1i2o,rv982% l2b˫UAtVϝt=Z{*N6)gsV 6uq,bNshs4yC jk;8xtQ4)SbrohOҕ[>cZ>A#{J[|,#i"ӵȨvqSK/tw %44<>Gj˩3b\3y6:A՞y3¹D txMKӮf0Q%L$%G1n7Wfak|&O}9{+MYRR}ѥsNYgS K:ױNe8gZk^;kTek=ZIΌVU]U[?XQl/U:Y{q7~=~Pvx3G&aHB4<_Ϲ`uWCJJjwz`v3'%dI.t =_#፷7^WݲOCW[R<_e g\NC-΢ހAts>8 g}-/RBBiאJyw7agw 䌋m克{cz jյb(|7|rcW`m# w:Ѹ*$=:0uGNϠ]yj6zGq1ɜ6d5o/^0\PDM<׹9VQך3.[׽%N* Kj]3-fMϋx?4VGrf:{7~?~븄r9>uTb_ʬ%m$Guo6AÕq;9Ȳq>x9{ͺ9!eμdwcJը|JMS'Q8|x I__Uh;m^vի=u&G%R z/lHmz_vƭRM=[RQUo[yn>y4DUFx~co q1̌o% Kgɥb݈8s1dyr#_}wz5]I8#aڕ3C Pj5\m# i9mj$Z;n^> =v`Y4"P"F95/T:3)"e049ܿ?6agFm~>}]uy=ւf;ve5)3s/yo, 7֚[oO 2LVI'Q~6:X~~άg2 FuBcH_2\ٻܐ ^/|EK W,xs8#Eqh4H&B6!Λq4,@`' |dd'@x&t}_ulo+RNna}\3e!-~͉Y1Cf<|/r*Рp^wCq~*n} ݂/@?KHL h9k |[Rw.A5pfͺ9yh7|e|c~G_[z)$翄ǟJ1L`z-FT޿*,q19,Aq&;][8WhnY5J*waʷo-~U_r s~#Df*j gWs>^YT:/} z8j0$d׼OY}(֨`WDl0yor'0q6p(:>*MJ6jj[Դh%?\a!7Tū1]T_MZOu˞~[ϟILlu\R?$hgu6owuajrYo%u]NU!c6ߍCT \m+“0I}H)u}maRR?B܃Ac8Yz⻍r|R~i~Js?DAG$yw@ XT3`bPWȜ`C܅q}ڥ[TѤ/SK6rbٹGBhٸ>DF8C ?^* Bq B!B E!"x(Ae0 C[-5efP:6yi]Ŭ cZHB!LIcQB& dfD`w.h ܱko#_,t+ݱGK'݈1b~+Syl|? hhޝ3y2J2JB!0F,a⢢ݪq+rdz}զ*VGyQH(B!$`,_ 9w{";gȱ sx(í;2HNTJh/;׆ȭ|5s5ቹGbt}1t.[9Zl?j1C/&$Eh&c+0i[M+GX(86 S|2t5Q&7ZO6qԻ \!v'.doBL~=:Rs6Jh ůc4PMΘB!(bsVbuKF3(CŔcD[ )$/[RMs?M?Pcֺ1C:zQOS7iOֻv!‘:=MkckӚvP-sըS1 TGMkm;iP &]B!0FXϒtxf}W';͝QQs~/Wp);l3Q\oV&sр?ms|Jv#n֬:MT^r9@fI'Y8Rr=|/daΉ#|O V~q7ήwʥԂs4\"|\;@`VfH< ӔB!D'SR-CO\u͸6-Z" - g~+n/ nI2İ!+JeM$LV$!TuѡQJ!"6#<*qHգ·az`ǘI>\dAoUPEg#9B&s0F_Skg|+GHI[V!B!c9S z fR088\O"`KjzY~ZV[TxCc̲e1r19+,q@㖊Vde.V{\؏_7~"$hB!LIoVxWDIm٤&&cGVu3)Z]qw8jPVkFϩ@*vy!W5Wf*r͐lsđ2 _Cx&|] .Ex{S%j^ǭzB!RvFuP&˰8m-[uy.h87q%@L1o\RjD~xzףS͐u3գWKzMvvNeR83/gG32fw2O?CUKڷRs` ~Z#pΦkkF3B!ɔT{٨[ЫUlPӢ _?pq&΋gz '5SVJ)Co4ؽ69>֤D~9F:&| zN=Iˎ7=!na25pn1?Abge ?>}DalYl̉~KܝRi̍;qTlE+!Bo5+I$@U-YݵM]xgZiE.:U$I$I$ITJ! rq5=5pm[xՎMƊE9lB[6QǯFO%kU ʼA?=ȉ]_eJƋ/PO9i)4. !BҊwڝkΌx9ܵu i1j)F?K52N+X.k;XO.gL8I$LqM=/A=_4SrI$IiB!B2CQP+[#vlSNʭSd$]%!11İ!+]ߕŲDFT$ƻ~5|;ny Z6S[5f(ڪ;YU{B!0d20q5&sƔ8Rq^93tpΡƒYbG:2ICŘuј9g=sʴ|u["RǘKC^[b|)Gq9^́ɾc0(r:B!ĿLޒjyHIsxQH8-_25>]I9LVE+K̉Y1Cf<;հ dL;OG_MA'f3(Z6G!B딍C.݂&x8~&N|k-^|̗O1yRJ2B!_q} b`hwtgǮY6qe_(ۮ2bZTB!ȔT!ȕf+#kKʔteLjU!BH(D9`e]$5!B!*B!BF!B!0 !Jȫ5rV0k _GYS!BhVk{ opӑoxJ u5v 4Vw}Ƕpts%B!c1ܷ!i7 .c:֐˿R]BJ~-h5m]+*!BTf0"ƂA@ /m{rݹ+=p-E)q'p BOܾ]W߂gwfy+I q:D5nԢ *[C)L.3O `uo±U r !Br!uu`LOW`t0 ׀Iu .PN_W!|7|Ȋ9ӡ0wZ3`+o_vlm;BzC%4$Qo🚰s-aޟ~^}\ߟga^(= lJj?.oN¥zy:W5+ ~S蝡11j.B!t@9Imlf} 51GXGn^B!O0#,IF ʛ+P 52~dlzjόwl/1LN_RqksM.dj`W!Z{hOW_Y#sdiIdÎQ*4 ,PWPL+駡PhXօ! _ߦl 96M!BѦ6ELL 8 @w$w @B1GW\HHf WԼiy[EokMt(]%h y%YӸ ?$|!n'r&A ]{ !Z'p@R Ti;B!(3O@}`xh?}p"9> _x;XyU < OTU \t]G†W' P4|ZJ: B|sww5 2Yǐ Wh8XU5ê_i_xx{@@7X%x){/X.ܾbAߌ'3`Ex[>NfiԔu7B(7!_`/w;`-\>c@\P֏46Q#Fy 5!+ !U!:] ie3_y[ۖ8 apk 9p7w}@з"hT8}Fo=`,,u z˨pR!$F!埲q}ڥ[ԄѸ' X}6/ cga ޫB!D)q}H*PZ#6U ˴I Bh˿{eB!Ba= z|B!$`B4녽H$d\~6 Z:o]:f*ABQ X07խ] !)GDm}Dal+Eb5r4YSC!eq}ڥ[ԄB<nf|p01ioB!JCdJBS}ma, !l)B:>CBQvB!B B!BH(B!BF!k-!,l-'7mh=9Y]`bKsT Y Q,B&-ݶ.ׄFXW-"Z}INxiK6y j]Ŭ cZHBQ{r >-Q Sj;MbdLг5쨅N c_VJl"ڼ5i4[lXQX͜>,?gZ5N)eg巭ch=A_GzY)nәOwX dDWm#|٣+ӳ[!]x~"߹4|žvPh⍭NO_d 1?}Ǖoq\2x4NۊO$y E|m(6`GSdK*3kZƇOwx_koakߦT5=A3?[,[H˜/'FS \*NXo"P ;O%LX{ΜI|Zi44Oq=?+PRw?lzʶQ#`tpwt(g$XB!#FIˮd昼Dd~5opQdVoɄZhOqj-ؑArr&&R4MfЁ>;qgW8s5s Ѥãy/tnʓUЛRm3_-ʙtkLǙ $zZ>:uE7!_+WISWb~|a/LgQI ,Xj؉O&zy+aq+~4^kꋣb$̚ _+*"Ʒݴ*7ov46mimɟX9o ]VRb\_LH&gM*Vc͘bATC2eV]X{Sx b|'PD4tlsebJDԹsdsϤqn*j{?x$s2)jG݊&ncŢI3`ՀTڟm2/ hmic67fyggAX\ع'I!'ZH4iu2BOǑfGA{% uS2 -ΈЊ>ọJqFi>vWv1}kcјPk|";SGDy%hɔ|]ުyIu[*ԥJ޾NjF[qYlz*Hrou-5z|z'Y1C^ B̨M\4޺I-o)P~T84n]uT2/WD.4_::ng$TiOb{uݻ~Ԣ+E?$^xz*ף&ٵ6՝4O,~اiP &]B!Fc;kEC~"A-m'2ImWmیZ ;~V}+`Ş)W܍9D蛿o9MLjR1{I=Mnk4/(yޥ*.J*RM֟9JvJA2kV=KwR}Qlob,'o_359Ϝֽ\bFW.8uJ8ct~^Ϟ K;fyeM(-vxs`?l2NP+L ybQڴm䷉΄ n#nfY]Q+8Sv:">uɛy)*J1qxZٜ9GfޅnLD -qAbq+XդYm$  !Q((-x0gX<)JR~#P{eh|uԭgz 1lRH܎h5!ױ u_\UU**W=>/R|*ƻaTs-r*~a* LysT"F~K,9c/kg|+GHI[c$`B!ƜL&co>whL#?쵐C1]x8jxb 0c$*C fjmMzYxia f]m8+,q@1U,ٿ) ]=?H%0?˫qyYz?} _vLY>>{IiFM'ݘxXS%Us(Yv峸{&twU>cR4fgu7XqqǾq,YS1dqj߄چkӗ_ΐc9Wab;zf*BGLv~\/FtSHQѺ57xI=@$O9~!*ʴ|u[zi09DoVK*[AK\6SI7t(XžQ\x/udLӼ#J/|zw^'pP0]QӇF;,bU>O83/gG+ ʧFqvlNA,8:VƯZ5H~޸X=1lw#Noz4;{/V-_%ֵ.67Nlϗf !LI-F@q%h4?sJN2Njp3n~eF{f%ŸLЧO>o[!^it^>^>ʏFT;ܘ!Xm.̉~KɲGM'b$D 'V1iّ7T;V1TAnFMULp̖ǟ>fsw@Ov)'`ڱӟu,oGON,2 e 3zNf#)D6mzhyIUmE4sC\yy|+ظ>D+'IңUWW+rZNžޢjsI%owuajryhUW TTET$I$q}*_ +~1MwK6 wf$tx4ŀMyzS Qm櫥[9~iӻ╗a8w^UXGBǺnbhB&ke4iJ7ܽ\a/LgQI ,Xj؉O&zy+aq+~4^kꋣb$̚ _D0Mt{0kb+["(ž~ ٮwQl[OkNykš.aDWxf~8éj称zaLGBDg-wڝkΌx9ܵ;ڥg2p*5Q4Zel`Ɗ={!l}3W:ߜ&6#!{|5k_R%KwM."6<GݬYu mn{ktz&v_œGn_3Bb|4S%_qMF~[8~ e֪t~^ϞK~R7>7i-yjƔ&fLY4r7F0`j{Ww]7J#=8j&&PF쏿m ?_81iTVAՌhiorUWϩ@*vyW5WfygL9᝛Y1&ʤzSۗN(9cR4fgu78pq7jE]nL:k뗸lpޓnPy/y#%II]/uE[9\ɊwCEu3գWKzMvvNeZg+{] wi^ˁ IDATւ,755дc#u zgFoؼ:ЏJ>4@aAXX).;cUGxϫ=y|yW3NXZ/Q&z>RGJX)*9qb|bXrע-5 lf4zB!J7Z j$9KШii $抬Q(hgYґ }U0KeXNm$Ou=s~VmnLY,e bDo?KP@ܿ%dl YؓAo+>եǐS݇5W1uùP7' |dd'@x&ԬLN/4d ]CľM$W ~ՖLJw)6XY)<3Lulo+RNna}_s^x7 t咵쳡af@,`$=#C٦B 820x" q~!};oڷo[B<:dY In$EU`:ʪ.c_W}ol5Jj!괗=U#\z@G>Ъ^LS/wI$I$I&`Wc$ x崤jzk\ +9②IN[ˑ}+Bv5ֲ|h-lbBs2%U[.|j"aW̓.)b\^=IY6 ˤBBH(\0^jB!$SRB!BH(B!LT￀Eϴ'0K9YN/B mi߶.ׄFXW-gh>GG.X7RQ`}INxisָ],>-=}H|W?)[j̠jwPoi{ʿuuuδ;c©'q0]N}9ujPA{cҳ֥>}7J)1'W{|RWS'ԚL5cas`ц{_P^&3oXsv|j)#|٣+ӳ[ߘS͗W֐Ĺ71g6b x?r>maIx{<{dDWGZJǍzйU =dslZxC)9b}*o׎1v\& dfDOBG]ʰl?cE si =j}4,*ܟ/GK'݈! b~+SylbF*LXtVpYy&/|}&oQ5X/4N,7,{&-W ={.⌔;O|Y';1髳d9=F`SoH:CbK@E`ml]7bz9לc(7ϯK9j$-.V?=?v}?>}GWQu -! B/tE|}A,HgAT)A}-4Q@APBI! : 4f$s3s>gfξERXRɵ%iZDJZLM|fپo쟮U =oؗyYk??=ay :"azM u{5:-%ߌC_] ь. sm**q#8pvG#ܕXſrP-dg4>3 ]h ^3^\̄Ŭ,kf2v`4B[ʑ-x.2,ޟNßdTuyl#_-XnvCC{i[˯0/ĭ|"Ylǹ ,FLQ]vm_̞b+| :@7t"'W>`93|&˯Zm4O B@bvTjG/fJ$c1C&/p`{~eoOlimx?EQ*]Y?23]hY׉U϶| zF@'.(om_9x4]1pcmѾ?._i-Kb/m&1k8ԏna>ť}(~ 'ݝY$~$T`rhlro{)wGY2腢֙Y*3^UUM 6:*ݚ?UeRKt6oEuL&6:]˻$;KLt4ŗ^<:.vGaGY}wח!_Qobs٬IVCl.B1s3L|i GcpQ쨿SC:ߢ'~+j~KBJ3xI>%r8AGpMBpH:Y-cxu쾃yS'w޿~]?-q=YV ];1'|axdΘa 6a/ll_3,{O|;? |;O9 |T7ztiFgS:)qsCҳtޓЂ}l3Tl*C峻ѿlF+?8:S`%S J3ZbyDK°6xko_u4g! _Y|[_PZq26EwlV4 _1&Pg"f2yDİIL#E;_T|AF0c(n_sũ owȊr"banOS ,s3KOrc>D[㉩$G![+ҳ%l:OJR[V"^wnkn9S<7ǫ~t g͖W\[tp%l?Ik!Msz$ʤNP#u*TgڿU;bHϺT|6_1W SIN֊j׸h&+5ݴRYE_3g-bgB"3n'H>zG]<׿R2a5>=S1?xlFDj-ΥG5P)Nn8PLnAw#Gg:ȅLH8# 7 $RF9 /s*g,(2܍ZxZٸw3挓$Z6ԭ;Mݍ5l9[36cɮ*к/Sqo/f\>[˷}T +z5s<<*z|Bpʩ>e&6\әMtt FL9O1a1_bmF??H˞բx7K޶r$ǟ[܆s6rථ{\-狤82^yWebvOo ?2?&|;_Պhi+1IJoZ o^D.!8۱_cDzmmh#mDUd*{a+oLީ_0=dY]}quM}CGO_|]U@XjUu%[Uύ:>n 1g|x`Ό&MC[^k z-U5s;;FYv'%5X+g0r4NO b׿#. GH5rgn$8ep'u ;ٽѳ~GPw]q8Q|{ǜHL6:ї*m_ Zа/!Ky^o'DXڎbtG8vgM[U~գxk~ǭA(xq0k 5qxŅ,|o M*3\V}g{F?9>Z,{)=9aW"5~j|3{}BBKfhh(!Ax;(gOϷj^+Zͮ34iWƗZ|ᒸE@۠AW-9uم厡1{V7\K{M"<;g80 댆"R"7"1[|Szc>so}q8ַěKMb0@gKƎ}Y=њ282vYJcgN㌜6\Ok&MYP37BT%P ۬=3߸<0ɉ>V*lS_)Wc M>h9)>-_/b쨇xSN˥uHݾeUS*fl+K~ʮyL&CI>g ԽZQP<ŀjeb'h8I_:C vNRMwi69|@α?ɴRr۹Z3vlL0/'qcm_ѓ# 5xw*{6ʧa`'m2N8ua=vYۧF?lfmOFtmB&sa1c§Dw[+Gd*sܫlVe+~P,ƒ?U6>մU+IZcvӞeeY?7Jo\oiFD.hh՗{I߳|s7|ՒQm:nSac*W-O.^j|RuՁTTWVfl2If#L2t>:Ց!W7~x]ەj-x\TRWW6|YX|R?q jOPf"I|WP "Y0^\zU:ͽ9SKt<7K^~^'K#j#1nA#(hKXpM$_ȧױd߮T>K%0!;U胹sĝ c"[V'XB%K]vemLB!BԆQaB!BqU0at鸅|T>L`d(Wx|V]m""">bD}|Z~!@[2p{,x5tc!Ba<2A y=Fa3%(8dl֔dRLUM.I+xsdxcĔ SQ-'Әi,W2{Q$E(\P1Z_{RE䩠 ` bs>"ؕtVM|HP7-d]ɢj{*-ʉS~κt,w0)bM^k2_6,$B!Axw+Yx|څyZt`˼N4B&ZݛIˉ9?2 dga9+ɱDg1h }D.I|Rѥa.XŶS%CH.oO.>$Wt|/D^6q.7f -ElOoaę^*ݙHgOb u扡]hޒCε|*Ta4mx?Eؕδ{q1N&Kg֯It$cj v[,}BQ` ̬m_Mܗ+a]\CdB!Лe '_eْѡ|*-($\,de7g8W5=~yͻ…{0|mRgҌϞ呇u^gݹ!lY)x$` g Q?|)0 IDAT%ɋ[>7]';7u2~W C5Hy=6)FUiL&ߚŏsgYNȄv*\>-:,\J~q}ZC6!B! cue xeٞ+%sQt^-8(pZYLE{KԼ,_qot;̘8ǩɣ޻J.gs+sJ5q1[qi0&|B{z6m8ȩTRbvú8n5'W1yz_ +W>8@)S7@:{~NZP(M45R+aTY$ :;Ҫ}[Z}aoo1}+./-39njlD,^b,6|g4w.k=o1nli]\ͣޜ?9s}5:)RzZLt,caL˖ӄ@'oھ%M Xʭ_EʧlǘiCKsEӚX㤗G !BIcPx60x<ͫ[=}J~[& ׬ex>WȉcqG9>@=ӾZK19jeTA|=Cv)~anD{twcKkΘic>KB [g J{TՊY-V* UsSlHJpna~Oqm𗞥яx%"«T@qrÁbra !BICC0fLlȦ7e w (n[t].Y{JqF J0n'hR,h裃~-!mLنVp? 4j^Mh&> ^IVVt>{fΊ#ڍ /^ն3NhOP76^٪=SB2Ez~˟!4ǚBAB!hozUNbB SbJ>fHvY -Cܽ͛ȩ<vFIw}}Z9k7Zzn AV<9c2tE4 Έqq#'~jA48Ҥ_ |PLXڎbtG8E$i xWI:8m}덗]g H6qNw0{ìݒO&q!ԫLX<6e$ag|z{~y`5(i`_/ fa$t*B!(a.;y52WY7k7,Wph=٭/[?L{ )q` EDGoDc*t:.s>Qs|Y>iKoE8dqBtۙ|5C!/[)y/^Q,v|20d`4,!=8|&? o3L9D,(S#qFߘ1SԪ}W_b7%"[B!oW@-զ>:֪rtnR\ۨ?P &}L2$L2$LNRKREODjapp@iͱo`\*B!lKRE%XH $7B!Bܸ _3x>6s; .t1-S,B!00)?8!BQ!B!č0:tB>}* Gizʣo,NnD!BQ7vsÌ/yoA/bǜ' 5}#Vr8y)~)jMm1/8eBw)B!3Sk saP%·2pT&*bMe-^?3~ӛ&|^z9T h@`B!BHX[h;D, ])45s$lc) E3hB~/AJBA5YB!F%v3[l<řlk=ts.W 6xŏY˫2) >.Z2WC|ʍiQB!0F6%ܯQ'I9z$j0+4 jhga {^i֯0 !B!j\ZiOC ֢, p)^E{s2 -ղE3٤͘K#!B!$a%/\ De8ҨEDNoƸdLwjqǵQ1G||֑Q!B! cmxgө5ZgߓhLlGcM!$n3ƋπY X0gpzc6[(,B!B$0ڝPwgtqV@͋c7䗳*ILb2BxPdB!BIkgN`yy2WyyCoαSkx@Y^!B!rc!UlHO-p!B!jaSƬ=|P!B!] _3x>B!B! *9.m#B!ו(B!FIo3ׅ߰<׳CM<懶2 {^f/3 tVlGN]¡ÿ<>DT%B<]gYi%w[:toxK㪤Ǧ%)`T1DqTɬ#uy?])Zͺ-OvH7AѸG.}_.!'>v cٱiKڊS+FM_)fO7i=3Ws",MFPWCATu5dφhm{0{xЗ[=A/nZ4"4$uZ|Þ3(m_A.ѧaN%WBsN;ߕ|T\ZStm93Qh]s3,drߖ#DwE}o&|hD 4h5lضx5Ν@4>O`z~w x5l5|uԃ\QptrImmlouϾ:?lů\?Ϟ?>fm ;>|3|V|lߞ!΅$񥕊!7F8(^UB!$aClw ",#ۍH{TcUG^rneSY ߉^Fu1+N$7?kD%il&CÛα"Z7hKQ Tgǘ͎sVUUl*ʱ}ƚ4}rV'7 Ե$]}:`3 P4k߁b\\]Q c&_T\)c'b =)=37z>H!xjc;YCc#~tِ'y&Tr8q2GmVkSܔB2 , V9iH.JB!$ה8#8! PmacɤEڹ_3sCZ{wR]56_7dtQlm&bv1t Vꖛiۋho%zw"@q[[@\#c~28h!a4z0YXD'|#)YBAn^}oVFEWp>no !ne3Uǩ4 4?G-[lViP4zX0[˾@Ad!BHx8!Aڇc6ODVf{㬁F5x;)'q!ڊ K4rz/I@v:OF[ZN&Ŝϊ+ƌ|akLú-ٹ3.ۘ eWh]zޗ,y Lh[DP(EkҠQc+\ =NZ`(:g(4!BHx¹=3mC+51)D\Zw5t؊d[ſt2^s5bU^;NɥB!47K5}[il[cۙM&j׃!4$ u%9BE灿Wu] Gu=}q8_:v{'ߎPCau|pU(0z&K>qI]1$R4T К.uL8s$5ܲ/-}|nM!Ck4N!tl[j:4Kqӫ)d1l!~hG[zz_As& Ҩ ~:.-e3~@W݂єZ@qՎ؊cJD_Oe|qȏ"j[q'DݏBq4OI͍븚[j5mwTNM3Ο{;ZX{О okm=z}[aO Ǧ%)3NG=9ڏg_p.e>D͕cDf} qO3u#Ŝ9g8a =q3.=d&z a[9Ɂd, a-4n ]]QBR73b?)&* N&РJ{EЭ3W>J?b6ud[}\N$ѣqvFwXy7sY0uEDD|ĈziB!B:t@tng[:i7t"}UD,;S&794yOWVkut `9vt,!o勛w):;cevB!n& 1Sk saP%\^@VNsŦc QOk ϯ(B!BHxs3&GY2ߏ` ̬m_M'Hjq…BM/oPC뉧yKC3y i<={#SFpj!O;Y*h<0 n9/~ԣ\[/o[@ƎL^hR9e9Ew>>aw4])̎,\+ Ug4>3 ]h ^W1g47\V[+F23=Z-x$'ׯۧo?z: Q[SB汍|`;SM総{cp(| q+\W>[jO!0V-^}g R,?ÒN4=Ԭeo3՞ eKL65'|aQ>̒qSxڛ`M̛L\]#/g.`/Eَt.nZȰ>%kqOgڌύ祱 L6jo~v!{һ;7vc.V{G5SNQEfY45%`wnzmY(x$`_Ǚmx&HRw˯O-~g=5_q~c/򣎒~kCdbaiU ֵ%][ :2_e=d+2:8}vYyscK.O&V j]Gӄ@'oھ%cX-^&b,6峥B! c3|" xh҄Qqm𗞥яx%"BFJQr>6Ѫ]WF4KcFSU+B+ײJQ=XWc!sg,̠;q#lݸ [kc~5ȸS"=܈4ZkZ^WW˷EK,OΆ4վqTP2} !BuErnL80dN?S潍>>PHffA{9'}[bٷWv]BpޘBQUѠp82AufeW8{:_k~)FOB]PbrPQp˜< &Ls:-n\2g3%9+ovϠAy? glUgU|bٶȴmhe zJ,{.IJ|-_fjOǜqDKچ1-}Vt8*F7nbr+qB!9y~r8}:YX : S{rzVMC^;XsC2|O;}rwtxŅ,|o M"B ~P,loѵ3 }8υ>%P+b2z~TwAc﮾֌,[{;&I\x-5̣'y8 'ص; KH56Aү̝0v֔ߖD~_>ޘ+\[_@0q%o97 3n/`q:ʬ_3*39d?+9rq؃;4;T`jW._-~"-;ŏ3r sSN<)J[>S%%DD|76_D!BQ7vsÌ/yů.bǜ' 4$FьZ0qF:^n j82#ʾ%B!BI730wm&L^R"kQ6|? Pt'&*ILD5ORŜ}fn$aB!BHX[r2do GPԴo[{d˹Zp",H+"m!B!YЛ w#"X:[{:W{O2 P_As{ WV^B!B$gf&}׷,x3z |xyݹ{ k!EQ\0v2]iwҍ8B!BI'#qwר{}Ȝ~=kL2zF٠C}<7qm=qB^ooUc$ bʾ}Exв-|ƈ3|`aJS:<ߤr;l0cCy\i_DgxFh鎹zq0cpNJ=0S6k?!cs^yo$cS.1Ǩ?hmm Ǐ!yŚTL)JDqk o Cb9r-#SCOIwgœY3v4K6DY|3ǾՇ|?k1 [ ;I>3}.|=!]i 1{Y0vdvH(.O0e^ QSG䜱W>@c}f77T8y~mҲVcL(K+{?oue2#c>-0r+c+W3?kǰzcQ~8e>2zX( _MB\-tuh֕j~9-uWQ^:/:uO/cX$zbNҙz?@u3}7)c1`>{>hZmf*!k&}kлm 1h8 :q?\b)s3ǜ0dnr&u+(P $F1j21ap^/їx"s=+odۥTß1f9PhdF=3cĨ)ڊcҴ]5@jBBR&v:+:nBҶM˾{._[6h5;y,N_5ۺd+rpҖS vj2jd'jia.HOs(&,mVxt{W_bل:b<6! oA:2~/1zMlg7umH7ˣYF94o:>X=?IRpj̣-UbQD23_ cZJ~-} eiS$ "Уa-]<8_fh+I+к6iI+'~*܎>![\e]+Ec.(|xf:#1~KFYR\5r ?u50x"Y~sPP3BX1oަ[V2ZROۿ-߲ΣZT'9n+ F58Wf,]9F {:^ l}ځS+~`׹XbaŲضF,i'KJ3:kwյ|t/gcr%(WBd(MIޯTqZO𘻅H8N͜lO?zfٶAβ~;.>Ī牺xDb+cH8ɉ0N&}oE%$w bpDc{| ]4e>3/{9΅ą䧍k_/԰1|5*fW|/pϸWvBH4ED\yb&ouB-ur* ҵ?~XˡXX#%9oYcOT.!#qW:)7W?E mZ3FI1N1hg/ۥ1.SIb#4c$◫-oK=j* p|FK+Kg Rҍ )Hִt3Seh/yDÕiCrV{ ѽ=~d(]k?˥G#ɏrlFD{)-nE/7%i3hlo4>fiHjejZVu3g.&N l ;}z篘ֵu7$Roow gxV'<@F8r9l=X^Ѐj-x)85f!i!<14q=Szv].و|OˈkRD" e>s˷~T#Vw:k"*]W7nX1Ϲğ x= B!2aa>5cRW3otxWpNϏ΅J#m>XXGqK;MXs")9D3V7Εx9[~̢iq3~n$#[&TEt!56tLߝMY/ɍ`߱<:6iƘ#,$WgEs7_׸tS9L6ZZvݕk!Mpq?߲֟9Y2wy77O2ecpZ!kָDI­W->ʔımqjם.O7&.r,<~(WQ-j??)j4>ov~RqFݳ\GXbƨQ&p_S+j4s\#z<3/N;?e<>6F3/y`  Y?;akʛFGir=k-oN%!"etjAm ДC;=A&l !-G6q/__ZԷ5veyě^=ڃFWNNJsKzScJ|!-(%g"~qlN?SLe>s˷~rc9cO>W ]?Ҕq+Z4oT+l:׏M\yUz>Ѡ"~w}+~2'XGu{7zDD S\[bgIRJA8T+A,+u֛^-y~`;OD̊oIRcHVhkJךzO&wA-VoHjx-d(je_,너7 B^1iqVt0sovV^t>Q8o*.]ggk /`A4.Ioy~bΟb20y^&>ˈRǓ>DC&ymFwm{o}eRYS'SCqƩ#}rpOͧVeW Y9,=)||߲Q38xF58ŵ1XOGkG$nyY=5^Ky6xI][ǃYFV1[mMy]::Ŗ^>xץNA[ }4Q?w|;˖_98nͳ/ vh&8YDO;εvҦ%l}p$ӗy1t(ٯ-̘0xLV>.Zyfd>y`(N3%:z8i .oA1{l,>q SeCjffݗ2n[X?(6/\O1 7_.gLҮPay\Xf3xLFL؟?2\ݿWeiwT.]/އ*Xh¡KG0v-$@`1$s/?Mf-Bn9]Nw~bw4&&f$,f)6f9|?U7~Uyi }k٣QӉ8/oft )x,XcUb<=.w?fJۇ~s>5CIFL{z~eEkY+m+w{_eE;N5=C˒_n{pTiSNf~!~(eW]dٲ"_1 ǔ oI1Ƶ5iѮ;PO7s渎d/诤q~\>;mG9rȔ=Vf+~ySc>;1zg SHsxE ; 8{-=PA'on[m> IDATMdʓ]#I]Tl_w' ufOwU[TۯR:y um-AU?gmܡ:bWjtV[VSJ|+ZK1Y.v,֭ R0hK%cK}Hg uKwc]3Ƭ&88+|a9ŎC1;玛aHB!$at& G:8es4 WdEbXrK#kWY4Go}__/PZNCKO%:̈́B!$a"oE|culj˅-L_h>w(JYI>µP6D:3bG y)/^M1\!B{O^Q]OˊeF^{sFiL=}kÔͨ:Ob=Ydc,[}cD[ ;I~̭A_\zB<bxB|qaoNMSڰ1l^5}͌i`S%drZ,,=sx,וֹBvXxF-'܂Q;Λ mSM1}MsdCq*M&61f/VҨ:c*lop!.dfz_Y;=>N.Aw[٫9bY|ߒAW6m]JZR38{m:yٗB!=%#%+1bQ-6vV,s9fiH(֎ `ml\Ur Z)e'AKx NxMyL@聻u*'c˫FUsVw_G=\6/gPNW <@JTRTL(Cj/Zril~ɗn.\?Uo;4?=dӨ޾;6&e{3ĹaaRIQJyc,_)Ώ0`L&tuG[&Sk̠q񽰔q/ @RICA-P^E%4rѦGuĚrL!Bc2hs퍃mk&ώ`4T@׸t9LrBZ\]'Lw U`U8<_N{GMUǧ=Jx** v>8r9l=XcZ-GJ-:ImGi}bntS=F27Ư<-5|=UbP͗$b8>ov~RqƮw+~}K4H#BqyVN1DO1qW Yߺ旖Ku֛^-y~`;ODjA|߲Q38xF58[ŵ1)B!dGfv(<ɈiO{گL2._̀9LxX>r,jbLX5#XZ>w)`&>7bsp ?wǹvܯҦ%l}p$ӗy1tސ:մϙ,^|V(3$B!0VlO7\)4۾Q (n-y|H,#=Gn9&1^ $B!BI+Q[cYc}o0&qtڛ 'A>n⇋W7>;gg"%%c@T#&!B!D{K-?)WR5yS$>GJPW+fh4z. !B!*0Z-?kx8oMgk$\ek4]SGzIB!BHxU;zn$OI}VzG=5F`7aGvdkK65uɖ(IB!J.I4]lo& ` Օ :\O3qV1giy.|>e3D'"B!@2hs;I#/q> EAEwKZ{HJ,CoTrN[HCFB!0sjN17ocA#%2YF51(Q!*!5ȏs/xSfW1WY`0io$B!B! c%fK!54O L&Pr=B!B(b+ƥb?̦H >aN65B!B#W[Ͷ]G5V"B!2XS|70I(B!HFB!BH(B!BF!B!0RjeFsԕ_+bG!udp !BH׌5EqM`R'*"}EAyľF@ց։5*U?zՖ/][GýGѼ&x\& lN!BIVtQ./|QxKtUֵol==$SS_lX]+eS[.lϧNfGA !Br'Wpӟ:Dzbx>hsvƞ,RR1V)8`gi{3쫜ڵy+ ՚w<۬Ƙ,F yw[uBgL% |.d; |II| +xҦ&aoͮX$;=>N.Aw[٫9bf #c_Dž9q7ɪpWXڴu%fc(j5sGЖÉ[f7*<6LiٌZÈ1TG!B{ЧH\9~%6 J4 iɁZygHekwhީ Kf1vuM`d$>K7]ע17ɏG_M𵓤:-ظѕc^EsoL,gR' X'5w_O#~UNbɐ*lEmv 4(QPtGQQ?*gIqmJ' bjcg1-LcB!$awgx{&N[5Z]7D*ndbd["y{\B2"o,*?mn蔫%ޔK{SBh끭C n$QMr}j]jH #ؕVpX%Gz8 ST@׸t9\el!5%$B!J^ai>@Lt4SL\5gοy4])?Y<+;ܶ n}J½50e\抾 N]/u֛^-y~`;OD 1$+^i恃Uѐ~0‰I|R %|ռlNU- 9Lt}o /:`(ヷW l Vƅ` 6B!*0IOfLU\s !BҒKRtUnl] \CΩ .-D!BF!Jcű@6K$B!(7rIB!BIB!BH(gap!B!$t8~[ }ksVpڨ/# 9xХUn_P~!B!ޔ}]y%[Ga穐Wʗp fUldM!^^/B!$aX?iCͺ{^ s~^bPT0T:> [FǗa@$ OY=tu`t' >D3aX؜Jgsf^›K5ܧ/M?BBTd\ T] Nઁ#+aW!B!$a" 11dߐ^#C:2 kG Ȇӟ7^+b%2AAaEx.| _\p`d\]<-_w^,K`ˁk 4̂EQ־\0h+# VlGBM6|;"o=PP:%$-66$9@QۇB!ʉ` t/ Tز e_}O _`s#0s\L&09<|s'Gk ξaxwieMl`1x=8ICCq8k a;I 4x Avգ0i$\O*/#{7ЉвlK-AYamvba_tDB!Bƻ"/m?A`zhdl8: ~e|.x?!:| o WnUՄ1fZ)eC3Gc_sv^π&0&c/.I*@B{W@ ƌH+1V!B!$aԴж=yZU߷~/m]h? }y &/[MU+n9Qp 7x"-ǓpIjR~"۵ג̴ڗ6OtOR5Z !B! c U4 WZ̓8f`S  NP!Lba-wsa[xZxBs,j/I-hyn>`^ZSe'p@r!B! c9?s>_@۔ _[Gb\ B "j6k pksjO:?<䕠=x瀥|zŗϦ(,M+򣁧ޅ`pMW~p U(B!1eݚ O@cխw)_} %>Q!B!Y&TBTۺP?%ɢB!$BTg%B!BF!# !B/" !B!Q!B!$B!B!$atwbW!LzZ)CqWUM{i&B!BTuЛgxI2rc[ 8k>:NׅB!0;EOˊeF^{s֝-I WB{HIɦrv<<~13/vYƉdK,]-&=3a f #Ic?Oޜd]3޺ (Tk7`rfֿ-ӿxv+e7E֎k\ IDATo8T\`ʼ@dɹ*P?B!BHXE>E eM d9iVH\6.Zi=ͤfcF"R_ їxbb>|$4uAUhdF5?dσG2|L*Q#~UNbɐ*2 ímC"'Ò+5C2T}uٳ/<*T?B!B@X-?kXv8tM-ݐetw:RK[ŚDN;ƕ,=g8t*cSzwұ%8E\l(V&? RS21B!Ŀh!5'h󧘸 j)2B%vޏ{@kldjCW)?Ys^'p؃>5'@LX5#|Ҧ%l}p$ӗy1tGH^ w~@HBSC!B)}J$pP _ɘ$$B!_jݚ $Ul<[C, !B! Q@Ω .qB!7B!B!$aB!B! B!BI=l{k +YX8~jDόyg9Y֕p/n+L[8!ԆB!BJ-&] IrYloxjp!֭bH1Z9IB!F.I-i[cY|, C:# 6WR ~-v5lG.*U1M(ԅB!Bʔ-?)WAyv:[>Ci Xԕn3LB!0VlOatO_òé+-zGVH:|6J!B!$awe7.`҈ vOԧK'wbw%D-ؓERR&o5:'%YB!BHx9 D}5gLtqKl=W^?e&}53tj4FIB!Ja,OJ5t 9ojP%[B!BT0a,s?| bCo֖2Y~?Zq*TB!BF65:;zq'K~ɫB!BHoK X(B!BrB!BIB!BH(B!Bƻ͊}4 5Il` ^:&XfY|ms_OWEy Rꅇ4EVW[ne/m͟σfLJX0|:}TZ;dpPdB!J0j]Y|&|3s"ۻU'Xʢ9~E{퀸ykyw$M4ch ;n{ϔ͑GV+g l2o&}tn}# {^ӕ6>#H""u*A;Z#-6t3BԌWVZ bT22Tq rOM.sH$ѐIL7wNZG:+"ڴM3{D7L93v{9ԽߓGzU$@ R j=7}LX=w9e6xt&-5^ y6o!{%2 1OOCh7'6R'křRar*Yw)|y*{D:00T*-+%у}sS".,=l0Ǜ=%!_|v(ٷ&اz99>e; &n^"9]y4 $txΉԣpOfNg[Kic}[spIBx&qOCw4C'H?y8cBU-ţS#6U~wrUî$*xNj.^lF`'ҷQT']c@-F{c;*hҞ!ѿƴ#VOeJrՌ /z4\G20*طdKcW}gDz%lM.W%W>jhXuPKNȂKO_0-)OV-lIYj~Q߆oW%tn=0g},)ŗK[~P2wRY62M_ at@[ŃYyF^C?l2Xy_K=c>y1ggFtxX:e /&RMy)ƜF%nTTG㷸TBJ$ID@ (Q՟y'V %Y1e"O!51~j3#3cc@C,L$Ot5Eh58.>25۸~ESf=nI=5}o ;1}o1p/)Kȱ7}9Q2(2gl3Ncr5\~i3ropIn͠5*c(FJ=eePk=w1s_n; kUGSmS.E\K*{wTA 6r੏#8}{ǒGf4_A^KO]ȳ;V׷c)'Э Yɾ:<7$rBboY4=RrU^Cɾeŵksv>ΰL¤$oVۿiJUIT9Rxn{L Kc䘠"% Qh1 6AяULA]ݱBn#6ƋηI]uHxJB𶑈ϋ!rŏ,gsoƽOp^baTxbMu(s+ Nאk^@  T?(g}y\- bBfO;5òj _ dP/=G>_þdeq02>ڛ9 '2IȟNh˅bnRrWaA}wN)\AT pTe9;ײygxۮa{cg7u<Գl[{'zQ,рOߗ̒#\I&34;O +Gl(!;oߤL 8#xcP\}sE+{ah2~wq7c/@ Ժb&u|!SvfWxGX}2'<-zzUD!Zt\Y^]^v|.&Z6FYF*7R^fD@gàP~%&0U9eF[aP/M¦ytmHY oN5 M/rdJ2uN˹ֿ3=',c}-uKfdp"Ӆ:%5w}eUٗ\F5(I#W䃇ɜ;%{r66Kp|fGJr5>\T/3ϛStQEufEm>_+77>*{(ϹN.OWFQ}J4kK-8 Ͻ%\޸#K9΋=ƆIGFNfLJDܪg#rLԋR@ xZZCɜ==ێMөy9I*l{rn>O!Pk]YP(me^Yu?jՆ3F|3"gzDc 4}hsY!eX`k(V@`ZlZ1S8 e3Z3wݝPcYE+Pc>#3|VܽYFF{OU%[ҢU/QG_e S\G18ZԪ,tŚbn uNOoH~<'@Yi͐Ypi9ojڈdi2 vT@ Ԡϑ)͢X֠{tSS:m مVMYΨ=B|YҠkJeѤU ύ:ɯ0 ~U/Eb J+@gy-vuG6W`BwO$er Izptt&Oɓ(^ic/aK&@s]ZC\^*X[X>*?C־?]YR5~8{Toc7B.FEUOCǯZk3$46>R~6 .oS˵E[:[:*)w5xʄװ߿y,6?ځ/f gL1@'s o@ ,` BڶEgrq E0?KSw/k62\ N]eU$I^tkA^'U9tYJ'!aՋ7^rW_xFjAS>5'7&et:ϗ3G۹W9Vhs^۟LEy: kh"w$oh+?m_F2z\>mn8M3TʮG44N #Y~p{YP$81c%Fz9}ʁu4=z#;)Q3?aTȵY-6ЊDqDF*W9avz:- 0 = T ;7L@[p灥FNzwgv$o$/>>$ya"U' y?>jYG~!]ߛ rWۺ2Pyw ;IؚH"$H"'O:mfiSºxVطxArPb1DE^`a瀣s3{iu CYd̒#/͋$jq>N*tϼDY{=|( @F}dKﱣh,Xgg D/L(B =` C%ZzaްSz}BW6@ ,yaB@ @_N?&1@l IENDB`gammaray-2.11.3/3rdparty/backward-cpp/doc/rude.png000066400000000000000000000140471413310443000217130ustar00rootroot00000000000000PNG  IHDR% 1gsRGBbKGD pHYs  tIME! aYIDATxy|Mw]"HCLlԾL `PEk2h_1v-U!̘h--CQ{b-$IDֻIrn$y|}s_gSڷkBX2Ԧq70g)uH+T{?邟> ؾz3*!N۵BQ!B!Ľ F;IB!B{z|_irYƷ86f̊H"##OrƧ=>!B!Qɍbϡd Guc X9NOf2w}C=x1w=эᬊ$r|&oA>dB!d_-d\ɢ22_1,؁sb],>7xZS21B!`H~}1+sf \q<|"~_Hǩx籌& 3 MaQ>AŃNS5љg!i.be i,c\X;n-͆2b8lNa;^Wd$[4]uf 1E?[vOZJp}iE^.b&;(fm|mkRp=h[]^'G`-zϐ^mE|&娚GWW%33KfWyЏV7WVRܞ$Lcf`W1GCdC+bڔkod7C9KaP>jUH2ˇB!6åxvbV4Y GX^m?q$KRz,׮cZ!B'ݯɫXaRrM]>Eԉ $\Z΋X ڕ1nz[?xK0/6?K!>kVWaYQ]/aRcؾb=K+o=J4/ω߲%.3eV3;|HpE($~2V<ʹ4Rc~Y֣vRGa"?NqQře7qL`3&*>8XPaU3:ӧi>{&KZ!B'iї_TG1d[Bnl tR0 G}?U+RRv>_ { n48N-F~NRYp8:vhJ<'p\M=Hb YFtB!x|qnJޕ$ILL$r2-/Ѳ<J3+W㹢VN5c_QT+bgITI-R sV%|LݑG8оv\y\:sP}Kk l.l*kyaPlJxWzc_ÅulLpXB^ZMX`([iΈ'J Wr\+&ckϖzib?!. !B _GLܛέ:>{];vB ?O|J~DG>T20]?i6,ZPۏykT(AFz?=o<Lgb/ʂ7p FA=oJz)&O@s}KO g|65;}K~ov.UɜJw MpõѦ9^f h^Ӟ^m*WȿV4Wxu7&\j"{E>hB!T{M\ܰ Øލٺ;s j@yjp |uĝɁE3vpޟٝĽߖDF2ecք 0nyE"&1#0ç)>Sb*}vj6qշP&obdQ*La9,fW 0j Z16a *=>[,nv~vP#L6.ZCQىiėg?b(ߦ .c /dJ˿f9>AiUi%_,d\ɢ*nG'Iyjݩ]>j_|~ ;XL!Bq̿uG,K0*.ui&b>c}OZJp}iEvU 1?6t0aūv2zz2Ƣ i֮L7;R5&O\Zzvt:?ƮX<IXǯz{D8J/q(>E89>_ &ǰ}*.T{Vz`^lle9B|׬9J4/\ :*yp&4Ǯflnv^ZLԮup&B7.cΣKJ#5ם!(I!+qb2I&NV,?CqWA n5qSqڝgUw}4R.bG|-*%&%DSDj*u sVLUD,([]/R7L(@5_Lbgk|KdᆟUG!BR0v#$l; ~ WxO0::5uTUSJΧc)7f -chl,Wa mzB:/1hPOZv3@.a9m":)8 d[YǑ_YI0`|Ǚ-ő]s+oɍ!6SzE;x~6K92.^ sE2SMު!>s! 8QB!c`=7%9#;e:Wa%/c3YURh;uԻm0?2nAM8! uجN9S ۺͬgWŪb7ޫ/-gX)';-+͆gs-_.GB!FTrvϒdFZ<6_Z:JެgWml}xI}ۏꭨ;6s $1EmBŀ{-/:r5 ɤVSq >p5q)j"z|V?9O.jq!<.9>U_s99#t+5\Kz36Wcq+i⻵t wk*B!ւ1G4]?K~W-׏vu T#ewJ=׎mfiGJ"CMc* Jqq'MyG9mϳ%*}\v_` r&fC6g8uK@[$o݇kSWH֩ YUjį_^_y W,!(B!Oӡ <@]$!4o D?{JԘzgel$@Mx~_!=߇1zdB!w[?M^=e.pݑhSҫ3w W[9ѬZq>%znϿ9*ŢB!РSRU2:i`0#fپ Yv.$ B!B!l-V0_ʄK%BNelL!BDŽ0 !퉌,uyHH$I! xB!Bs`IENDB`gammaray-2.11.3/3rdparty/backward-cpp/qt_attribution.json000066400000000000000000000007231413310443000234400ustar00rootroot00000000000000{ "Id": "backward-cpp", "Name": "Backward-cpp", "QDocModule": "gammaray-manual", "QtUsage": "Used by GammaRay for backtraces on Linux targets.", "Description": "Backward is a beautiful stack trace pretty printer for C++.", "Homepage": "https://github.com/bombela/backward-cpp", "Version": "1.5", "License": "MIT License", "LicenseId": "MIT", "LicenseFile": "LICENSE.txt", "Copyright": "Copyright 2013-2017 Google Inc." } gammaray-2.11.3/3rdparty/backward-cpp/test/000077500000000000000000000000001413310443000204525ustar00rootroot00000000000000gammaray-2.11.3/3rdparty/backward-cpp/test/_test_main.cpp000066400000000000000000000142251413310443000233040ustar00rootroot00000000000000/* * _test_main.cpp * Copyright 2013 Google Inc. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "test.hpp" #include #include #ifdef _WIN32 #include #define strcasecmp _stricmp #else #include #include #endif #if defined(__has_include) && __has_include() #include #else #include #ifdef _WIN32 char argv0[MAX_PATH]; inline const char *getprogname() { return GetModuleFileName(NULL, argv0, sizeof(argv0)) ? argv0 : NULL; } #elif !defined(__APPLE__) // N.B. getprogname() is an Apple/BSD-ism. // program_invocation_name is a GLIBC-ism, but it's also // supported by libmusl. #define getprogname() program_invocation_name #endif void error(int status, int errnum, const char *format, ...) { fflush(stdout); fprintf(stderr, "%s: ", getprogname()); va_list args; va_start(args, format); vfprintf(stderr, format, args); va_end(args); if (errnum != 0) { fprintf(stderr, ": %s\n", strerror(errnum)); } else { fprintf(stderr, "\n"); } if (status != 0) { exit(status); } } #endif using namespace test; bool run_test(TestBase &test, bool use_child_process = true) { if (!use_child_process) { exit(static_cast(test.run())); } printf("-- running test case: %s\n", test.name); fflush(stdout); test::TestStatus status = test::SUCCESS; #ifdef _WIN32 char filename[256]; GetModuleFileName(NULL, filename, 256); // TODO: check for error std::string cmd_line = filename; cmd_line += " --nofork "; cmd_line += test.name; STARTUPINFO si; PROCESS_INFORMATION pi; ZeroMemory(&si, sizeof(si)); si.cb = sizeof(si); ZeroMemory(&pi, sizeof(pi)); if (!CreateProcessA(nullptr, const_cast(cmd_line.c_str()), nullptr, nullptr, FALSE, 0, nullptr, nullptr, &si, &pi)) { printf("unable to create process\n"); exit(-1); } WaitForSingleObject(pi.hProcess, INFINITE); DWORD exit_code; GetExitCodeProcess(pi.hProcess, &exit_code); switch (exit_code) { case 3: status = test::SIGNAL_ABORT; break; case 5: status = test::EXCEPTION_UNCAUGHT; break; case EXCEPTION_ACCESS_VIOLATION: status = test::SIGNAL_SEGFAULT; break; case EXCEPTION_STACK_OVERFLOW: status = test::SIGNAL_SEGFAULT; break; case EXCEPTION_INT_DIVIDE_BY_ZERO: status = test::SIGNAL_DIVZERO; break; } printf("Exit code: %lu\n", exit_code); CloseHandle(pi.hProcess); CloseHandle(pi.hThread); if (test.expected_status == test::ASSERT_FAIL) { // assert calls abort on windows return (status & test::SIGNAL_ABORT); } #else pid_t child_pid = fork(); if (child_pid == 0) { exit(static_cast(test.run())); } if (child_pid == -1) { error(EXIT_FAILURE, 0, "unable to fork"); } int child_status = 0; waitpid(child_pid, &child_status, 0); if (WIFEXITED(child_status)) { int exit_status = WEXITSTATUS(child_status); if (exit_status & ~test::STATUS_MASK) { status = test::FAILED; } else { status = static_cast(exit_status); } } else if (WIFSIGNALED(child_status)) { const int signum = WTERMSIG(child_status); printf("!! signal (%d) %s\n", signum, strsignal(signum)); switch (signum) { case SIGABRT: status = test::SIGNAL_ABORT; break; case SIGSEGV: case SIGBUS: status = test::SIGNAL_SEGFAULT; break; case SIGFPE: status = test::SIGNAL_DIVZERO; break; default: status = test::SIGNAL_UNCAUGHT; } } #endif if (test.expected_status == test::FAILED) { return (status & test::FAILED); } if (test.expected_status == test::SIGNAL_UNCAUGHT) { return (status & test::SIGNAL_UNCAUGHT); } return status == test.expected_status; } int main(int argc, const char *const argv[]) { #ifdef _WIN32 _set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT); #endif if (argc == 3 && strcmp("--nofork", argv[1]) == 0) { // Windows has no fork, so we simulate it // we only execute one test, without forking for (test_registry_t::iterator it = test_registry().begin(); it != test_registry().end(); ++it) { TestBase &test = **it; if (strcasecmp(argv[2], test.name) == 0) { run_test(test, false); return 0; } } return -1; } size_t success_cnt = 0; size_t total_cnt = 0; for (test_registry_t::iterator it = test_registry().begin(); it != test_registry().end(); ++it) { TestBase &test = **it; bool consider_test = (argc <= 1); for (int i = 1; i < argc; ++i) { if (strcasecmp(argv[i], test.name) == 0) { consider_test = true; break; } } if (!consider_test) { continue; } total_cnt += 1; if (run_test(test)) { printf("-- test case success: %s\n", test.name); success_cnt += 1; } else { printf("** test case FAILED : %s\n", test.name); } } printf("-- tests passing: %zu/%zu", success_cnt, total_cnt); if (total_cnt) { printf(" (%zu%%)\n", success_cnt * 100 / total_cnt); } else { printf("\n"); } return (success_cnt == total_cnt) ? EXIT_SUCCESS : EXIT_FAILURE; } gammaray-2.11.3/3rdparty/backward-cpp/test/rectrace.cpp000066400000000000000000000046231413310443000227530ustar00rootroot00000000000000/* * test/rectrace.cpp * Copyright 2013 Google Inc. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "backward.hpp" #include "test/test.hpp" #include using namespace backward; typedef StackTrace stacktrace_t; void end_of_our_journey(stacktrace_t &st) { if (!st.size()) { st.load_here(); } } int rec(stacktrace_t &st, int level) { if (level <= 1) { end_of_our_journey(st); return 0; } return rec(st, level - 1); } namespace toto { namespace titi { struct foo { union bar { NOINLINE static int trampoline(stacktrace_t &st, int level) { return rec(st, level); } }; }; } // namespace titi } // namespace toto TEST(recursion) { { // lexical scope. stacktrace_t st; const int input = 3; int r = toto::titi::foo::bar::trampoline(st, input); std::cout << "rec(" << input << ") == " << r << std::endl; Printer printer; // printer.address = true; printer.object = true; printer.print(st, stdout); } } int fib(StackTrace &st, int level) { if (level == 2) { return 1; } if (level <= 1) { end_of_our_journey(st); return 0; } return fib(st, level - 1) + fib(st, level - 2); } TEST(fibrecursive) { StackTrace st; const int input = 6; int r = fib(st, input); std::cout << "fib(" << input << ") == " << r << std::endl; Printer printer; printer.print(st, stdout); } gammaray-2.11.3/3rdparty/backward-cpp/test/select_signals.cpp000066400000000000000000000033661413310443000241650ustar00rootroot00000000000000/* * test/segfault.cpp * Copyright 2013 Google Inc. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "backward.hpp" #include "test/test.hpp" #include #include using namespace backward; void badass_function() { char *ptr = (char *)42; *ptr = 42; } TEST_SEGFAULT(pprint_sigsev) { std::vector signals; signals.push_back(SIGSEGV); SignalHandling sh(signals); std::cout << std::boolalpha << "sh.loaded() == " << sh.loaded() << std::endl; badass_function(); } TEST_SEGFAULT(wont_pprint) { std::vector signals; signals.push_back(SIGABRT); SignalHandling sh(signals); std::cout << std::boolalpha << "sh.loaded() == " << sh.loaded() << std::endl; badass_function(); } gammaray-2.11.3/3rdparty/backward-cpp/test/stacktrace.cpp000066400000000000000000000032671413310443000233120ustar00rootroot00000000000000/* * test/stacktrace.cpp * Copyright 2013 Google Inc. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "backward.hpp" #include "test/test.hpp" #include #include using namespace backward; void collect_trace(StackTrace &st) { st.load_here(); } TEST(minitrace) { Printer printer; StackTrace st; collect_trace(st); printer.print(st, std::cout); } void d(StackTrace &st) { st.load_here(); } void c(StackTrace &st) { return d(st); } void b(StackTrace &st) { return c(st); } NOINLINE void a(StackTrace &st) { return b(st); } TEST(smalltrace) { Printer printer; StackTrace st; a(st); printer.print(st, std::cout); } gammaray-2.11.3/3rdparty/backward-cpp/test/suicide.cpp000066400000000000000000000047101413310443000226050ustar00rootroot00000000000000/* * test/suicide.cpp * Copyright 2013 Google Inc. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "backward.hpp" #include "test/test.hpp" #include #ifndef _WIN32 #include #endif using namespace backward; void badass_function() { char *ptr = (char *)42; *ptr = 42; } TEST_SEGFAULT(invalid_write) { badass_function(); } int you_shall_not_pass() { char *ptr = (char *)42; int v = *ptr; return v; } TEST_SEGFAULT(invalid_read) { int v = you_shall_not_pass(); std::cout << "v=" << v << std::endl; } void abort_abort_I_repeat_abort_abort() { std::cout << "Jumping off the boat!" << std::endl; abort(); } TEST_ABORT(calling_abort) { abort_abort_I_repeat_abort_abort(); } // aarch64 and mips does not trap Division by zero #if !defined(__aarch64__) || !defined(__mips__) volatile int zero = 0; int divide_by_zero() { std::cout << "And the wild black hole appears..." << std::endl; int v = 42 / zero; return v; } TEST_DIVZERO(divide_by_zero) { int v = divide_by_zero(); std::cout << "v=" << v << std::endl; } #endif // Darwin does not allow RLIMIT_STACK to be reduced #ifndef __APPLE__ int bye_bye_stack(int i) { return bye_bye_stack(i + 1) + bye_bye_stack(i * 2); } TEST_SEGFAULT(stackoverflow) { #ifndef _WIN32 struct rlimit limit; limit.rlim_max = 8096; setrlimit(RLIMIT_STACK, &limit); #endif int r = bye_bye_stack(42); std::cout << "r=" << r << std::endl; } #endif gammaray-2.11.3/3rdparty/backward-cpp/test/test.cpp000066400000000000000000000036701413310443000221430ustar00rootroot00000000000000/* * test/test.cpp * Copyright 2013 Google Inc. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "test/test.hpp" #include #include #include TEST(empty_test) {} TEST_FAIL_ASSERT(fail_assert) { ASSERT(1 == 2); } TEST_FAIL_ASSERT(fail_assert_ge) { ASSERT_GE(4, 5); } TEST_UNCAUGHT_EXCEPTION(uncaught_exception) { throw std::runtime_error("some random runtime error"); } TEST_UNCAUGHT_EXCEPTION(uncaught_exception_int) { throw 42; } TEST_SEGFAULT(segfault) { char *a = 0; char b = a[42]; std::cout << "result: " << b << std::endl; } TEST_ABORT(abort) { abort(); } TEST(catch_int) { ASSERT_THROW({ throw 42; }, int); } TEST_FAIL_ASSERT(fail_catch_int) { ASSERT_THROW({}, int); } TEST_FAIL_ASSERT(fail_no_throw) { ASSERT_NO_THROW({ throw 42; }); } TEST(any_throw) { ASSERT_ANY_THROW({ throw 42; }); } TEST_FAIL_ASSERT(fail_any_throw) { ASSERT_ANY_THROW({}); } gammaray-2.11.3/3rdparty/backward-cpp/test/test.hpp000066400000000000000000000160721413310443000221500ustar00rootroot00000000000000/* * test/test.hpp * Copyright 2013 Google Inc. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #pragma once #ifndef H_54E531F7_9154_454B_BEB9_257408429470 #define H_54E531F7_9154_454B_BEB9_257408429470 #include #include #include #include #include #include namespace test { struct AssertFailedError : std::exception { ~AssertFailedError() throw() {} AssertFailedError(const char *filename, int _line, const char *_errmsg) : basename(_basename(filename)), line(_line), errmsg(_errmsg) {} const char *what() const throw() { if (!_what.size()) { std::ostringstream ss; ss << "assertion failed (" << basename << ":" << line; ss << ") " << errmsg; _what = ss.str(); } return _what.c_str(); } const char *basename; int line; const char *errmsg; mutable std::string _what; static const char *_basename(const char *filename) { const char *basename = filename + strlen(filename); while (basename != filename && *basename != '/') { basename -= 1; } return basename + 1; } }; enum TestStatus { SUCCESS = 0 << 0, FAILED = 1 << 0, ASSERT_FAIL = FAILED | 1 << 1, EXCEPTION_UNCAUGHT = FAILED | 2 << 1, SIGNAL_UNCAUGHT = FAILED | 3 << 1, SIGNAL_SEGFAULT = SIGNAL_UNCAUGHT | 1 << 3, SIGNAL_ABORT = SIGNAL_UNCAUGHT | 2 << 3, SIGNAL_DIVZERO = SIGNAL_UNCAUGHT | 2 << 3, STATUS_MASK = 0x1F }; struct TestBase { const char *name; TestStatus expected_status; virtual ~TestBase() {} TestBase(const char *, TestStatus); virtual void do_test() = 0; TestStatus run() { try { do_test(); return SUCCESS; } catch (const AssertFailedError &e) { printf("!! %s\n", e.what()); return ASSERT_FAIL; } catch (const std::exception &e) { printf("!! exception: %s\n", e.what()); return EXCEPTION_UNCAUGHT; } catch (...) { printf("!! unknown exception\n"); return EXCEPTION_UNCAUGHT; } } }; typedef std::vector test_registry_t; inline test_registry_t &test_registry() { static test_registry_t reg; return reg; } inline TestBase::TestBase(const char *n, TestStatus s) : name(n), expected_status(s) { test_registry().push_back(this); } } // namespace test #define _TEST_STATUS(name, status) \ struct TEST_##name : ::test::TestBase { \ TEST_##name() : TestBase(#name, status) {} \ void do_test(); \ } TEST_##name; \ void TEST_##name::do_test() #define TEST(name) _TEST_STATUS(name, ::test::SUCCESS) #define TEST_FAIL(name) _TEST_STATUS(name, ::test::FAILED) #define TEST_FAIL_ASSERT(name) _TEST_STATUS(name, ::test::ASSERT_FAIL) #define TEST_UNCAUGHT_EXCEPTION(name) \ _TEST_STATUS(name, ::test::EXCEPTION_UNCAUGHT) #define TEST_UNCAUGHT_SIGNAL(name) _TEST_STATUS(name, ::test::SIGNAL_UNCAUGHT) #define TEST_SEGFAULT(name) _TEST_STATUS(name, ::test::SIGNAL_SEGFAULT) #define TEST_ABORT(name) _TEST_STATUS(name, ::test::SIGNAL_ABORT) #define TEST_DIVZERO(name) _TEST_STATUS(name, ::test::SIGNAL_DIVZERO) #define ASSERT(expr) \ (expr) ? static_cast(0) \ : throw ::test::AssertFailedError(__FILE__, __LINE__, #expr) #define _ASSERT_BINOP(a, b, cmp) \ (!(a cmp b)) ? static_cast(0) \ : throw ::test::AssertFailedError( \ __FILE__, __LINE__, "because " #a " " #cmp " " #b) #define ASSERT_EQ(a, b) _ASSERT_BINOP(a, b, !=) #define ASSERT_NE(a, b) _ASSERT_BINOP(a, b, ==) #define ASSERT_LT(a, b) _ASSERT_BINOP(a, b, >=) #define ASSERT_LE(a, b) _ASSERT_BINOP(a, b, >) #define ASSERT_GT(a, b) _ASSERT_BINOP(a, b, <=) #define ASSERT_GE(a, b) _ASSERT_BINOP(a, b, <) #define ASSERT_THROW(expr, e_type) \ do { \ try { \ expr \ } catch (const e_type &) { \ break; \ } \ throw ::test::AssertFailedError(__FILE__, __LINE__, \ "expected exception " #e_type); \ } while (0) #define ASSERT_ANY_THROW(expr) \ do { \ try { \ expr \ } catch (...) { \ break; \ } \ throw ::test::AssertFailedError(__FILE__, __LINE__, \ "expected any exception"); \ } while (0) #define ASSERT_NO_THROW(expr) \ try { \ expr \ } catch (...) { \ throw ::test::AssertFailedError(__FILE__, __LINE__, \ "no exception expected"); \ } #endif /* H_GUARD */ gammaray-2.11.3/3rdparty/backward-cpp/test_package/000077500000000000000000000000001413310443000221255ustar00rootroot00000000000000gammaray-2.11.3/3rdparty/backward-cpp/test_package/CMakeLists.txt000066400000000000000000000003531413310443000246660ustar00rootroot00000000000000project(backward-package-test) cmake_minimum_required(VERSION 2.8) include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake) conan_basic_setup(TARGETS) add_executable(example main.cpp) target_link_libraries(example PRIVATE ${CONAN_TARGETS}) gammaray-2.11.3/3rdparty/backward-cpp/test_package/conanfile.py000066400000000000000000000005571413310443000244440ustar00rootroot00000000000000from conans import ConanFile, CMake import os class TestBackward(ConanFile): settings = 'os', 'compiler', 'build_type', 'arch' generators = 'cmake' def build(self): cmake = CMake(self) cmake.configure(defs={'CMAKE_VERBOSE_MAKEFILE': 'ON'}) cmake.build() def test(self): self.run(os.path.join('.', 'bin', 'example')) gammaray-2.11.3/3rdparty/backward-cpp/test_package/main.cpp000066400000000000000000000015761413310443000235660ustar00rootroot00000000000000#include #include #include #include using namespace backward; class TracedException : public std::runtime_error { public: TracedException() : std::runtime_error(_get_trace()) {} private: std::string _get_trace() { std::ostringstream ss; StackTrace stackTrace; TraceResolver resolver; stackTrace.load_here(); resolver.load_stacktrace(stackTrace); for (std::size_t i = 0; i < stackTrace.size(); ++i) { const ResolvedTrace trace = resolver.resolve(stackTrace[i]); ss << "#" << i << " at " << trace.object_function << "\n"; } return ss.str(); } }; void f(int i) { if (i >= 42) { throw TracedException(); } else { std::cout << "i=" << i << "\n"; f(i + 1); } } int main() { try { f(0); } catch (const TracedException &ex) { std::cout << ex.what(); } } gammaray-2.11.3/3rdparty/kde/000077500000000000000000000000001413310443000157005ustar00rootroot00000000000000gammaray-2.11.3/3rdparty/kde/CMakeLists.txt000066400000000000000000000024421413310443000204420ustar00rootroot00000000000000set(gammaray_kitemmodels_srcs klinkitemselectionmodel.cpp kmodelindexproxymapper.cpp krecursivefilterproxymodel.cpp kitemmodels_debug.cpp ) add_library(gammaray_kitemmodels SHARED ${gammaray_kitemmodels_srcs}) target_link_libraries(gammaray_kitemmodels LINK_PUBLIC Qt5::Core) set_target_properties(gammaray_kitemmodels PROPERTIES ${GAMMARAY_DEFAULT_LIBRARY_PROPERTIES} OUTPUT_NAME gammaray_kitemmodels-${GAMMARAY_PROBE_ABI} DEFINE_SYMBOL MAKE_KITEMMODELS_LIB ) gammaray_set_rpath(gammaray_kitemmodels ${LIB_INSTALL_DIR}) if(NOT GAMMARAY_PROBE_ONLY_BUILD) install(TARGETS gammaray_kitemmodels EXPORT GammaRayTargets ${INSTALL_TARGETS_DEFAULT_ARGS}) if(MSVC) install(FILES "$/$" DESTINATION ${BIN_INSTALL_DIR} CONFIGURATIONS Debug RelWithDebInfo) endif() ecm_generate_pri_file(BASE_NAME GammaRayKItemModels LIB_NAME gammaray_kitemmodels-${GAMMARAY_PROBE_ABI} DEPS "core" FILENAME_VAR PRI_FILENAME INCLUDE_INSTALL_DIR ${INCLUDE_INSTALL_DIR} ) install(FILES ${PRI_FILENAME} DESTINATION ${ECM_MKSPECS_INSTALL_DIR}) else() install(TARGETS gammaray_kitemmodels ${INSTALL_TARGETS_DEFAULT_ARGS}) endif() gammaray-2.11.3/3rdparty/kde/COPYING.LIB000066400000000000000000000636371413310443000173570ustar00rootroot00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin St, 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. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, 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 and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, 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 library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete 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 distribute a copy of this License along with the Library. 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 Library or any portion of it, thus forming a work based on the Library, 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) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, 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 Library, 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 Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you 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. If distribution of 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 satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be 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. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library 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. 9. 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 Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library 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 with this License. 11. 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 Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library 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 Library. 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. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library 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. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser 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 Library 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 Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, 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 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "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 LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. 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 LIBRARY 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 LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. 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 library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! gammaray-2.11.3/3rdparty/kde/kitemmodels_debug.cpp000066400000000000000000000021731413310443000220720ustar00rootroot00000000000000/* Copyright (C) 2010 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.net, author Stephen Kelly Copyright (c) 2016 Ableton AG Author Stephen Kelly This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kitemmodels_debug.h" Q_LOGGING_CATEGORY(KITEMMODELS_LOG, "gammaray.kitemmodels", QtInfoMsg) gammaray-2.11.3/3rdparty/kde/kitemmodels_debug.h000066400000000000000000000022361413310443000215370ustar00rootroot00000000000000/* Copyright (C) 2010 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.net, author Stephen Kelly Copyright (c) 2016 Ableton AG Author Stephen Kelly This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KITEMMODELS_DEBUG_H #define KITEMMODELS_DEBUG_H #include Q_DECLARE_LOGGING_CATEGORY(KITEMMODELS_LOG) #endif gammaray-2.11.3/3rdparty/kde/kitemmodels_export.h000066400000000000000000000023011413310443000217630ustar00rootroot00000000000000/* This file is part of the KDE project Copyright (C) 2007 David Faure This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KDEITEMMODELS_EXPORT_H #define KDEITEMMODELS_EXPORT_H #include #ifdef KITEMMODELS_STATICLIB # undef KITEMMMODELS_SHAREDLIB # define KITEMMODELS_EXPORT #else # ifdef MAKE_KITEMMODELS_LIB # define KITEMMODELS_EXPORT Q_DECL_EXPORT # else # define KITEMMODELS_EXPORT Q_DECL_IMPORT # endif #endif #endif gammaray-2.11.3/3rdparty/kde/klinkitemselectionmodel.cpp000066400000000000000000000177251413310443000233360ustar00rootroot00000000000000/* Copyright (C) 2010 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.net, author Stephen Kelly Copyright (c) 2016 Ableton AG Author Stephen Kelly This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "klinkitemselectionmodel.h" #include "kitemmodels_debug.h" #include "kmodelindexproxymapper.h" #include class KLinkItemSelectionModelPrivate { public: KLinkItemSelectionModelPrivate(KLinkItemSelectionModel *proxySelectionModel) : q_ptr(proxySelectionModel) { QObject::connect(q_ptr, &QItemSelectionModel::currentChanged, q_ptr, [this](const QModelIndex& idx) { slotCurrentChanged(idx); } ); QObject::connect(q_ptr, &QItemSelectionModel::modelChanged, q_ptr, [this] { reinitializeIndexMapper(); }); } Q_DECLARE_PUBLIC(KLinkItemSelectionModel) KLinkItemSelectionModel *const q_ptr; bool assertSelectionValid(const QItemSelection &selection) const { Q_FOREACH (const QItemSelectionRange &range, selection) { if (!range.isValid()) { qCDebug(KITEMMODELS_LOG) << selection; } Q_ASSERT(range.isValid()); } return true; } void reinitializeIndexMapper() { delete m_indexMapper; m_indexMapper = nullptr; if (!q_ptr->model() || !m_linkedItemSelectionModel || !m_linkedItemSelectionModel->model()) { return; } m_indexMapper = new KModelIndexProxyMapper( q_ptr->model(), m_linkedItemSelectionModel->model(), q_ptr); const QItemSelection mappedSelection = m_indexMapper->mapSelectionRightToLeft(m_linkedItemSelectionModel->selection()); q_ptr->QItemSelectionModel::select(mappedSelection, QItemSelectionModel::ClearAndSelect); } void sourceSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected); void sourceCurrentChanged(const QModelIndex ¤t); void slotCurrentChanged(const QModelIndex ¤t); QItemSelectionModel *m_linkedItemSelectionModel = nullptr; bool m_ignoreCurrentChanged = false; KModelIndexProxyMapper * m_indexMapper = nullptr; }; KLinkItemSelectionModel::KLinkItemSelectionModel(QAbstractItemModel *model, QItemSelectionModel *proxySelector, QObject *parent) : QItemSelectionModel(model, parent), d_ptr(new KLinkItemSelectionModelPrivate(this)) { setLinkedItemSelectionModel(proxySelector); } KLinkItemSelectionModel::KLinkItemSelectionModel(QObject *parent) : QItemSelectionModel(nullptr, parent), d_ptr(new KLinkItemSelectionModelPrivate(this)) { } KLinkItemSelectionModel::~KLinkItemSelectionModel() { delete d_ptr; } QItemSelectionModel *KLinkItemSelectionModel::linkedItemSelectionModel() const { Q_D(const KLinkItemSelectionModel); return d->m_linkedItemSelectionModel; } void KLinkItemSelectionModel::setLinkedItemSelectionModel(QItemSelectionModel *selectionModel) { Q_D(KLinkItemSelectionModel); if (d->m_linkedItemSelectionModel != selectionModel) { if (d->m_linkedItemSelectionModel) { disconnect(d->m_linkedItemSelectionModel); } d->m_linkedItemSelectionModel = selectionModel; if (d->m_linkedItemSelectionModel) { connect(d->m_linkedItemSelectionModel, SIGNAL(selectionChanged(QItemSelection,QItemSelection)), SLOT(sourceSelectionChanged(QItemSelection,QItemSelection))); connect(d->m_linkedItemSelectionModel, SIGNAL(currentChanged(QModelIndex,QModelIndex)), SLOT(sourceCurrentChanged(QModelIndex))); connect(d->m_linkedItemSelectionModel, &QItemSelectionModel::modelChanged, this, [this] { d_ptr->reinitializeIndexMapper(); }); } d->reinitializeIndexMapper(); Q_EMIT linkedItemSelectionModelChanged(); } } void KLinkItemSelectionModel::select(const QModelIndex &index, QItemSelectionModel::SelectionFlags command) { Q_D(KLinkItemSelectionModel); // When an item is removed, the current index is set to the top index in the model. // That causes a selectionChanged signal with a selection which we do not want. if (d->m_ignoreCurrentChanged) { return; } // Do *not* replace next line with: QItemSelectionModel::select(index, command) // // Doing so would end up calling KLinkItemSelectionModel::select(QItemSelection, QItemSelectionModel::SelectionFlags) // // This is because the code for QItemSelectionModel::select(QModelIndex, QItemSelectionModel::SelectionFlags) looks like this: // { // QItemSelection selection(index, index); // select(selection, command); // } // So it calls KLinkItemSelectionModel overload of // select(QItemSelection, QItemSelectionModel::SelectionFlags) // // When this happens and the selection flags include Toggle, it causes the // selection to be toggled twice. QItemSelectionModel::select(QItemSelection(index, index), command); if (index.isValid()) { d->m_linkedItemSelectionModel->select(d->m_indexMapper->mapSelectionLeftToRight(QItemSelection(index, index)), command); } else { d->m_linkedItemSelectionModel->clearSelection(); } } void KLinkItemSelectionModel::select(const QItemSelection &selection, QItemSelectionModel::SelectionFlags command) { Q_D(KLinkItemSelectionModel); d->m_ignoreCurrentChanged = true; QItemSelection _selection = selection; QItemSelectionModel::select(_selection, command); Q_ASSERT(d->assertSelectionValid(_selection)); QItemSelection mappedSelection = d->m_indexMapper->mapSelectionLeftToRight(_selection); Q_ASSERT(d->assertSelectionValid(mappedSelection)); d->m_linkedItemSelectionModel->select(mappedSelection, command); d->m_ignoreCurrentChanged = false; } void KLinkItemSelectionModelPrivate::slotCurrentChanged(const QModelIndex ¤t) { const QModelIndex mappedCurrent = m_indexMapper->mapLeftToRight(current); if (!mappedCurrent.isValid()) { return; } m_linkedItemSelectionModel->setCurrentIndex(mappedCurrent, QItemSelectionModel::NoUpdate); } void KLinkItemSelectionModelPrivate::sourceSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected) { Q_Q(KLinkItemSelectionModel); QItemSelection _selected = selected; QItemSelection _deselected = deselected; Q_ASSERT(assertSelectionValid(_selected)); Q_ASSERT(assertSelectionValid(_deselected)); const QItemSelection mappedDeselection = m_indexMapper->mapSelectionRightToLeft(_deselected); const QItemSelection mappedSelection = m_indexMapper->mapSelectionRightToLeft(_selected); q->QItemSelectionModel::select(mappedDeselection, QItemSelectionModel::Deselect); q->QItemSelectionModel::select(mappedSelection, QItemSelectionModel::Select); } void KLinkItemSelectionModelPrivate::sourceCurrentChanged(const QModelIndex ¤t) { Q_Q(KLinkItemSelectionModel); const QModelIndex mappedCurrent = m_indexMapper->mapRightToLeft(current); if (!mappedCurrent.isValid()) { return; } q->setCurrentIndex(mappedCurrent, QItemSelectionModel::NoUpdate); } #include "moc_klinkitemselectionmodel.cpp" gammaray-2.11.3/3rdparty/kde/klinkitemselectionmodel.h000066400000000000000000000127361413310443000230000ustar00rootroot00000000000000/* Copyright (C) 2010 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.net, author Stephen Kelly Copyright (c) 2016 Ableton AG Author Stephen Kelly This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KLINKITEMSELECTIONMODEL_H #define KLINKITEMSELECTIONMODEL_H #include #include #include "kitemmodels_export.h" class KLinkItemSelectionModelPrivate; /** @class KLinkItemSelectionModel klinkitemselectionmodel.h KLinkItemSelectionModel @brief Makes it possible to share a selection in multiple views which do not have the same source model Although multiple views can share the same QItemSelectionModel, the views then need to have the same source model. If there is a proxy model between the model and one of the views, or different proxy models in each, this class makes it possible to share the selection between the views. @image html kproxyitemselectionmodel-simple.png "Sharing a QItemSelectionModel between views on the same model is trivial" @image html kproxyitemselectionmodel-error.png "If a proxy model is used, it is no longer possible to share the QItemSelectionModel directly" @image html kproxyitemselectionmodel-solution.png "A KLinkItemSelectionModel can be used to map the selection through the proxy model" @code QAbstractItemModel *model = getModel(); QSortFilterProxyModel *proxy = new QSortFilterProxyModel(); proxy->setSourceModel(model); QTreeView *view1 = new QTreeView(splitter); view1->setModel(model); KLinkItemSelectionModel *view2SelectionModel = new KLinkItemSelectionModel( proxy, view1->selectionModel()); QTreeView *view2 = new QTreeView(splitter); // Note that the QAbstractItemModel passed to KLinkItemSelectionModel must be the same as what is used in the view view2->setModel(proxy); view2->setSelectionModel( view2SelectionModel ); @endcode @image html kproxyitemselectionmodel-complex.png "Arbitrarily complex proxy configurations on the same root model can be used" @code QAbstractItemModel *model = getModel(); QSortFilterProxyModel *proxy1 = new QSortFilterProxyModel(); proxy1->setSourceModel(model); QSortFilterProxyModel *proxy2 = new QSortFilterProxyModel(); proxy2->setSourceModel(proxy1); QSortFilterProxyModel *proxy3 = new QSortFilterProxyModel(); proxy3->setSourceModel(proxy2); QTreeView *view1 = new QTreeView(splitter); view1->setModel(proxy3); QSortFilterProxyModel *proxy4 = new QSortFilterProxyModel(); proxy4->setSourceModel(model); QSortFilterProxyModel *proxy5 = new QSortFilterProxyModel(); proxy5->setSourceModel(proxy4); KLinkItemSelectionModel *view2SelectionModel = new KLinkItemSelectionModel( proxy5, view1->selectionModel()); QTreeView *view2 = new QTreeView(splitter); // Note that the QAbstractItemModel passed to KLinkItemSelectionModel must be the same as what is used in the view view2->setModel(proxy5); view2->setSelectionModel( view2SelectionModel ); @endcode See also kitemmodels: tests/proxymodeltestapp/proxyitemselectionwidget.cpp. @since 4.5 @author Stephen Kelly */ class KITEMMODELS_EXPORT KLinkItemSelectionModel : public QItemSelectionModel { Q_OBJECT Q_PROPERTY(QItemSelectionModel *linkedItemSelectionModel READ linkedItemSelectionModel WRITE setLinkedItemSelectionModel NOTIFY linkedItemSelectionModelChanged) public: /** Constructor. */ KLinkItemSelectionModel(QAbstractItemModel *targetModel, QItemSelectionModel *linkedItemSelectionModel, QObject *parent = nullptr); explicit KLinkItemSelectionModel(QObject *parent = nullptr); ~KLinkItemSelectionModel() override; QItemSelectionModel *linkedItemSelectionModel() const; void setLinkedItemSelectionModel(QItemSelectionModel *selectionModel); void select(const QModelIndex &index, QItemSelectionModel::SelectionFlags command) override; void select(const QItemSelection &selection, QItemSelectionModel::SelectionFlags command) override; Q_SIGNALS: void linkedItemSelectionModelChanged(); protected: KLinkItemSelectionModelPrivate *const d_ptr; private: Q_DECLARE_PRIVATE(KLinkItemSelectionModel) Q_PRIVATE_SLOT(d_func(), void sourceSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected)) Q_PRIVATE_SLOT(d_func(), void sourceCurrentChanged(const QModelIndex ¤t)) Q_PRIVATE_SLOT(d_func(), void slotCurrentChanged(const QModelIndex ¤t)) }; #endif gammaray-2.11.3/3rdparty/kde/kmodelindexproxymapper.cpp000066400000000000000000000233701413310443000232230ustar00rootroot00000000000000/* Copyright (C) 2010 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.net, author Stephen Kelly Copyright (c) 2016 Ableton AG Author Stephen Kelly This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kmodelindexproxymapper.h" #include "kitemmodels_debug.h" #include #include #include #include class KModelIndexProxyMapperPrivate { KModelIndexProxyMapperPrivate(const QAbstractItemModel *leftModel, const QAbstractItemModel *rightModel, KModelIndexProxyMapper *qq) : q_ptr(qq), m_leftModel(leftModel), m_rightModel(rightModel), mConnected(false) { createProxyChain(); } void createProxyChain(); void checkConnected(); void setConnected(bool connected); bool assertSelectionValid(const QItemSelection &selection) const { Q_FOREACH (const QItemSelectionRange &range, selection) { if (!range.isValid()) { qCDebug(KITEMMODELS_LOG) << selection << m_leftModel << m_rightModel << m_proxyChainDown << m_proxyChainUp; } Q_ASSERT(range.isValid()); } return true; } Q_DECLARE_PUBLIC(KModelIndexProxyMapper) KModelIndexProxyMapper *const q_ptr; QList > m_proxyChainUp; QList > m_proxyChainDown; QPointer m_leftModel; QPointer m_rightModel; bool mConnected; }; /* The idea here is that this selection model and proxySelectionModel might be in different parts of the proxy chain. We need to build up to two chains of proxy models to create mappings between them. Example 1: Root model | / \ Proxy 1 Proxy 3 | | Proxy 2 Proxy 4 Need Proxy 1 and Proxy 2 in one chain, and Proxy 3 and 4 in the other. Example 2: Root model | Proxy 1 | Proxy 2 / \ Proxy 3 Proxy 6 | | Proxy 4 Proxy 7 | Proxy 5 We first build the chain from 1 to 5, then start building the chain from 7 to 1. We stop when we find that proxy 2 is already in the first chain. Stephen Kelly, 30 March 2010. */ void KModelIndexProxyMapperPrivate::createProxyChain() { Q_FOREACH (auto p, m_proxyChainUp) { p->disconnect(q_ptr); } Q_FOREACH (auto p, m_proxyChainDown) { p->disconnect(q_ptr); } m_proxyChainUp.clear(); m_proxyChainDown.clear(); QPointer targetModel = m_rightModel; QList > proxyChainDown; QPointer selectionTargetProxyModel = qobject_cast(targetModel); while (selectionTargetProxyModel) { proxyChainDown.prepend(selectionTargetProxyModel); QObject::connect(selectionTargetProxyModel.data(), &QAbstractProxyModel::sourceModelChanged, q_ptr, [this]{ createProxyChain(); }); selectionTargetProxyModel = qobject_cast(selectionTargetProxyModel->sourceModel()); if (selectionTargetProxyModel == m_leftModel) { m_proxyChainDown = proxyChainDown; checkConnected(); return; } } QPointer sourceModel = m_leftModel; QPointer sourceProxyModel = qobject_cast(sourceModel); while (sourceProxyModel) { m_proxyChainUp.append(sourceProxyModel); QObject::connect(sourceProxyModel.data(), &QAbstractProxyModel::sourceModelChanged, q_ptr, [this]{ createProxyChain(); }); sourceProxyModel = qobject_cast(sourceProxyModel->sourceModel()); const int targetIndex = proxyChainDown.indexOf(sourceProxyModel); if (targetIndex != -1) { m_proxyChainDown = proxyChainDown.mid(targetIndex + 1, proxyChainDown.size()); checkConnected(); return; } } m_proxyChainDown = proxyChainDown; checkConnected(); } void KModelIndexProxyMapperPrivate::checkConnected() { auto konamiRight = m_proxyChainUp.isEmpty() ? m_leftModel : m_proxyChainUp.last()->sourceModel(); auto konamiLeft = m_proxyChainDown.isEmpty() ? m_rightModel : m_proxyChainDown.first()->sourceModel(); setConnected(konamiLeft && (konamiLeft == konamiRight)); } void KModelIndexProxyMapperPrivate::setConnected(bool connected) { if (mConnected != connected) { Q_Q(KModelIndexProxyMapper); mConnected = connected; Q_EMIT q->isConnectedChanged(); } } KModelIndexProxyMapper::KModelIndexProxyMapper(const QAbstractItemModel *leftModel, const QAbstractItemModel *rightModel, QObject *parent) : QObject(parent), d_ptr(new KModelIndexProxyMapperPrivate(leftModel, rightModel, this)) { } KModelIndexProxyMapper::~KModelIndexProxyMapper() { delete d_ptr; } QModelIndex KModelIndexProxyMapper::mapLeftToRight(const QModelIndex &index) const { const QItemSelection selection = mapSelectionLeftToRight(QItemSelection(index, index)); if (selection.isEmpty()) { return QModelIndex(); } return selection.indexes().first(); } QModelIndex KModelIndexProxyMapper::mapRightToLeft(const QModelIndex &index) const { const QItemSelection selection = mapSelectionRightToLeft(QItemSelection(index, index)); if (selection.isEmpty()) { return QModelIndex(); } return selection.indexes().first(); } QItemSelection KModelIndexProxyMapper::mapSelectionLeftToRight(const QItemSelection &selection) const { Q_D(const KModelIndexProxyMapper); if (selection.isEmpty() || !d->mConnected) { return QItemSelection(); } if (selection.first().model() != d->m_leftModel) { qCDebug(KITEMMODELS_LOG) << "FAIL" << selection.first().model() << d->m_leftModel << d->m_rightModel; } Q_ASSERT(selection.first().model() == d->m_leftModel); QItemSelection seekSelection = selection; Q_ASSERT(d->assertSelectionValid(seekSelection)); QListIterator > iUp(d->m_proxyChainUp); while (iUp.hasNext()) { const QPointer proxy = iUp.next(); if (!proxy) { return QItemSelection(); } Q_ASSERT(seekSelection.isEmpty() || seekSelection.first().model() == proxy); seekSelection = proxy->mapSelectionToSource(seekSelection); Q_ASSERT(seekSelection.isEmpty() || seekSelection.first().model() == proxy->sourceModel()); Q_ASSERT(d->assertSelectionValid(seekSelection)); } QListIterator > iDown(d->m_proxyChainDown); while (iDown.hasNext()) { const QPointer proxy = iDown.next(); if (!proxy) { return QItemSelection(); } Q_ASSERT(seekSelection.isEmpty() || seekSelection.first().model() == proxy->sourceModel()); seekSelection = proxy->mapSelectionFromSource(seekSelection); Q_ASSERT(seekSelection.isEmpty() || seekSelection.first().model() == proxy); Q_ASSERT(d->assertSelectionValid(seekSelection)); } Q_ASSERT((!seekSelection.isEmpty() && seekSelection.first().model() == d->m_rightModel) || true); return seekSelection; } QItemSelection KModelIndexProxyMapper::mapSelectionRightToLeft(const QItemSelection &selection) const { Q_D(const KModelIndexProxyMapper); if (selection.isEmpty() || !d->mConnected) { return QItemSelection(); } if (selection.first().model() != d->m_rightModel) { qCDebug(KITEMMODELS_LOG) << "FAIL" << selection.first().model() << d->m_leftModel << d->m_rightModel; } Q_ASSERT(selection.first().model() == d->m_rightModel); QItemSelection seekSelection = selection; Q_ASSERT(d->assertSelectionValid(seekSelection)); QListIterator > iDown(d->m_proxyChainDown); iDown.toBack(); while (iDown.hasPrevious()) { const QPointer proxy = iDown.previous(); if (!proxy) { return QItemSelection(); } seekSelection = proxy->mapSelectionToSource(seekSelection); Q_ASSERT(d->assertSelectionValid(seekSelection)); } QListIterator > iUp(d->m_proxyChainUp); iUp.toBack(); while (iUp.hasPrevious()) { const QPointer proxy = iUp.previous(); if (!proxy) { return QItemSelection(); } seekSelection = proxy->mapSelectionFromSource(seekSelection); Q_ASSERT(d->assertSelectionValid(seekSelection)); } Q_ASSERT((!seekSelection.isEmpty() && seekSelection.first().model() == d->m_leftModel) || true); return seekSelection; } bool KModelIndexProxyMapper::isConnected() const { Q_D(const KModelIndexProxyMapper); return d->mConnected; } gammaray-2.11.3/3rdparty/kde/kmodelindexproxymapper.h000066400000000000000000000101371413310443000226650ustar00rootroot00000000000000/* Copyright (C) 2010 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.net, author Stephen Kelly Copyright (c) 2016 Ableton AG Author Stephen Kelly This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KMODELINDEXPROXYMAPPER_H #define KMODELINDEXPROXYMAPPER_H #include #include "kitemmodels_export.h" QT_BEGIN_NAMESPACE class QAbstractItemModel; class QModelIndex; class QItemSelection; QT_END_NAMESPACE class KModelIndexProxyMapperPrivate; /** * @class KModelIndexProxyMapper kmodelindexproxymapper.h KModelIndexProxyMapper * * @brief This class facilitates easy mapping of indexes and selections through proxy models. * * In a complex system of proxy models there can be a need to map indexes and selections between them, * and sometimes to do so without knowledge of the path from one model to another. * * For example, * * @verbatim * Root model * | * / \ * Proxy 1 Proxy 3 * | | * Proxy 2 Proxy 4 * @endverbatim * * If there is a need to map indexes between proxy 2 and proxy 4, a KModelIndexProxyMapper can be created * to facilitate mapping of indexes between them. * * @code * m_indexMapper = new KModelIndexProxyMapper(proxy2, proxy4, this); * * ... * * const QModelIndex proxy4Index = m_mapLeftToRight(proxy2->index(0, 0)); * Q_ASSERT(proxy4Index.model() == proxy4); * @endcode * * Note that the aim is to achieve black box connections so that there is no need for application code to * know the structure of proxy models in the path between left and right and attempt to manually map them. * * @verbatim * Root model * | * --------------- * | Black Box | * --------------- * | | * Proxy 2 Proxy 4 * @endverbatim * * The isConnected property indicates whether there is a * path from the left side to the right side. * * @author Stephen Kelly * */ class KITEMMODELS_EXPORT KModelIndexProxyMapper : public QObject { Q_OBJECT /** * Indicates whether there is a chain that can be followed from leftModel to rightModel. * * This value can change if the sourceModel of an intermediate proxy is changed. */ Q_PROPERTY(bool isConnected READ isConnected NOTIFY isConnectedChanged) public: /** * Constructor */ KModelIndexProxyMapper(const QAbstractItemModel *leftModel, const QAbstractItemModel *rightModel, QObject *parent = nullptr); ~KModelIndexProxyMapper(); /** * Maps the @p index from the left model to the right model. */ QModelIndex mapLeftToRight(const QModelIndex &index) const; /** * Maps the @p index from the right model to the left model. */ QModelIndex mapRightToLeft(const QModelIndex &index) const; /** * Maps the @p selection from the left model to the right model. */ QItemSelection mapSelectionLeftToRight(const QItemSelection &selection) const; /** * Maps the @p selection from the right model to the left model. */ QItemSelection mapSelectionRightToLeft(const QItemSelection &selection) const; bool isConnected() const; Q_SIGNALS: void isConnectedChanged(); private: //@cond PRIVATE Q_DECLARE_PRIVATE(KModelIndexProxyMapper) KModelIndexProxyMapperPrivate *const d_ptr; //@endcond }; #endif gammaray-2.11.3/3rdparty/kde/krecursivefilterproxymodel.cpp000066400000000000000000000404471413310443000241300ustar00rootroot00000000000000/* Copyright (c) 2009 Stephen Kelly This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "krecursivefilterproxymodel.h" #include // Maintainability note: // This class invokes some Q_PRIVATE_SLOTs in QSortFilterProxyModel which are // private API and could be renamed or removed at any time. // If they are renamed, the invocations can be updated with an #if (QT_VERSION(...)) // If they are removed, then layout{AboutToBe}Changed Q_SIGNALS should be used when the source model // gets new rows or has rowsremoved or moved. The Q_PRIVATE_SLOT invocation is an optimization // because layout{AboutToBe}Changed is expensive and causes the entire mapping of the tree in QSFPM // to be cleared, even if only a part of it is dirty. // Stephen Kelly, 30 April 2010. // All this is temporary anyway, the long term solution is support in QSFPM: https://codereview.qt-project.org/151000 class KRecursiveFilterProxyModelPrivate { Q_DECLARE_PUBLIC(KRecursiveFilterProxyModel) KRecursiveFilterProxyModel *q_ptr; public: KRecursiveFilterProxyModelPrivate(KRecursiveFilterProxyModel *model) : q_ptr(model), completeInsert(false) { qRegisterMetaType("QModelIndex"); } inline QMetaMethod findMethod(const char *signature) const { Q_Q(const KRecursiveFilterProxyModel); const int idx = q->metaObject()->indexOfMethod(signature); Q_ASSERT(idx != -1); return q->metaObject()->method(idx); } // Convenience methods for invoking the QSFPM Q_SLOTS. Those slots must be invoked with invokeMethod // because they are Q_PRIVATE_SLOTs inline void invokeDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector &roles = QVector()) { Q_Q(KRecursiveFilterProxyModel); // required for Qt 5.5 and upwards, see commit f96baeb75fc in qtbase static const QMetaMethod m = findMethod("_q_sourceDataChanged(QModelIndex,QModelIndex,QVector)"); bool success = m.invoke(q, Qt::DirectConnection, Q_ARG(QModelIndex, topLeft), Q_ARG(QModelIndex, bottomRight), Q_ARG(QVector, roles)); Q_UNUSED(success); Q_ASSERT(success); } inline void invokeRowsInserted(const QModelIndex &source_parent, int start, int end) { Q_Q(KRecursiveFilterProxyModel); static const QMetaMethod m = findMethod("_q_sourceRowsInserted(QModelIndex,int,int)"); bool success = m.invoke(q, Qt::DirectConnection, Q_ARG(QModelIndex, source_parent), Q_ARG(int, start), Q_ARG(int, end)); Q_UNUSED(success); Q_ASSERT(success); } inline void invokeRowsAboutToBeInserted(const QModelIndex &source_parent, int start, int end) { Q_Q(KRecursiveFilterProxyModel); static const QMetaMethod m = findMethod("_q_sourceRowsAboutToBeInserted(QModelIndex,int,int)"); bool success = m.invoke(q, Qt::DirectConnection, Q_ARG(QModelIndex, source_parent), Q_ARG(int, start), Q_ARG(int, end)); Q_UNUSED(success); Q_ASSERT(success); } inline void invokeRowsRemoved(const QModelIndex &source_parent, int start, int end) { Q_Q(KRecursiveFilterProxyModel); static const QMetaMethod m = findMethod("_q_sourceRowsRemoved(QModelIndex,int,int)"); bool success = m.invoke(q, Qt::DirectConnection, Q_ARG(QModelIndex, source_parent), Q_ARG(int, start), Q_ARG(int, end)); Q_UNUSED(success); Q_ASSERT(success); } inline void invokeRowsAboutToBeRemoved(const QModelIndex &source_parent, int start, int end) { Q_Q(KRecursiveFilterProxyModel); static const QMetaMethod m = findMethod("_q_sourceRowsAboutToBeRemoved(QModelIndex,int,int)"); bool success = m.invoke(q, Qt::DirectConnection, Q_ARG(QModelIndex, source_parent), Q_ARG(int, start), Q_ARG(int, end)); Q_UNUSED(success); Q_ASSERT(success); } void sourceDataChanged(const QModelIndex &source_top_left, const QModelIndex &source_bottom_right, const QVector &roles = QVector()); void sourceRowsAboutToBeInserted(const QModelIndex &source_parent, int start, int end); void sourceRowsInserted(const QModelIndex &source_parent, int start, int end); void sourceRowsAboutToBeRemoved(const QModelIndex &source_parent, int start, int end); void sourceRowsRemoved(const QModelIndex &source_parent, int start, int end); /** Force QSortFilterProxyModel to re-evaluate whether to hide or show index and its parents. */ void refreshAscendantMapping(const QModelIndex &index); QModelIndex lastFilteredOutAscendant(const QModelIndex &index); bool completeInsert; QModelIndex lastHiddenAscendantForInsert; }; void KRecursiveFilterProxyModelPrivate::sourceDataChanged(const QModelIndex &source_top_left, const QModelIndex &source_bottom_right, const QVector &roles) { QModelIndex source_parent = source_top_left.parent(); Q_ASSERT(source_bottom_right.parent() == source_parent); // don't know how to handle different parents in this code... // Tell the world. invokeDataChanged(source_top_left, source_bottom_right, roles); // We can't find out if the change really matters to us or not, for a lack of a dataAboutToBeChanged signal (or a cache). // TODO: add a set of roles that we care for, so we can at least ignore the rest. // Even if we knew the visibility was just toggled, we also can't find out what // was the last filtered out ascendant (on show, like sourceRowsAboutToBeInserted does) // or the last to-be-filtered-out ascendant (on hide, like sourceRowsRemoved does) // So we have to refresh all parents. QModelIndex sourceParent = source_parent; while (sourceParent.isValid()) { invokeDataChanged(sourceParent, sourceParent, roles); sourceParent = sourceParent.parent(); } } QModelIndex KRecursiveFilterProxyModelPrivate::lastFilteredOutAscendant(const QModelIndex &idx) { Q_Q(KRecursiveFilterProxyModel); QModelIndex last = idx; QModelIndex index = idx.parent(); while (index.isValid() && !q->filterAcceptsRow(index.row(), index.parent())) { last = index; index = index.parent(); } return last; } void KRecursiveFilterProxyModelPrivate::sourceRowsAboutToBeInserted(const QModelIndex &source_parent, int start, int end) { Q_Q(KRecursiveFilterProxyModel); if (!source_parent.isValid() || q->filterAcceptsRow(source_parent.row(), source_parent.parent())) { // If the parent is already in the model (directly or indirectly), we can just pass on the signal. invokeRowsAboutToBeInserted(source_parent, start, end); completeInsert = true; } else { // OK, so parent is not in the model. // Maybe the grand parent neither.. Go up until the first one that is. lastHiddenAscendantForInsert = lastFilteredOutAscendant(source_parent); } } void KRecursiveFilterProxyModelPrivate::sourceRowsInserted(const QModelIndex &source_parent, int start, int end) { Q_Q(KRecursiveFilterProxyModel); if (completeInsert) { // If the parent is already in the model, we can just pass on the signal. completeInsert = false; invokeRowsInserted(source_parent, start, end); return; } bool requireRow = false; for (int row = start; row <= end; ++row) { if (q->filterAcceptsRow(row, source_parent)) { requireRow = true; break; } } if (!requireRow) { // The new rows doesn't have any descendants that match the filter. Filter them out. return; } // Make QSFPM realize that lastHiddenAscendantForInsert should be shown now invokeDataChanged(lastHiddenAscendantForInsert, lastHiddenAscendantForInsert); } void KRecursiveFilterProxyModelPrivate::sourceRowsAboutToBeRemoved(const QModelIndex &source_parent, int start, int end) { invokeRowsAboutToBeRemoved(source_parent, start, end); } void KRecursiveFilterProxyModelPrivate::sourceRowsRemoved(const QModelIndex &source_parent, int start, int end) { Q_Q(KRecursiveFilterProxyModel); invokeRowsRemoved(source_parent, start, end); // Find out if removing this visible row means that some ascendant // row can now be hidden. // We go up until we find a row that should still be visible // and then make QSFPM re-evaluate the last one we saw before that, to hide it. QModelIndex toHide; QModelIndex sourceAscendant = source_parent; while (sourceAscendant.isValid()) { if (q->filterAcceptsRow(sourceAscendant.row(), sourceAscendant.parent())) { break; } toHide = sourceAscendant; sourceAscendant = sourceAscendant.parent(); } if (toHide.isValid()) { invokeDataChanged(toHide, toHide); } } KRecursiveFilterProxyModel::KRecursiveFilterProxyModel(QObject *parent) : QSortFilterProxyModel(parent), d_ptr(new KRecursiveFilterProxyModelPrivate(this)) { setDynamicSortFilter(true); } KRecursiveFilterProxyModel::~KRecursiveFilterProxyModel() { delete d_ptr; } bool KRecursiveFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const { // TODO: Implement some caching so that if one match is found on the first pass, we can return early results // when the subtrees are checked by QSFPM. if (acceptRow(sourceRow, sourceParent)) { return true; } QModelIndex source_index = sourceModel()->index(sourceRow, 0, sourceParent); Q_ASSERT(source_index.isValid()); bool accepted = false; const int numChildren = sourceModel()->rowCount(source_index); for (int row = 0, rows = numChildren; row < rows; ++row) { if (filterAcceptsRow(row, source_index)) { accepted = true; break; } } return accepted; } QModelIndexList KRecursiveFilterProxyModel::match(const QModelIndex &start, int role, const QVariant &value, int hits, Qt::MatchFlags flags) const { if (role < Qt::UserRole) { return QSortFilterProxyModel::match(start, role, value, hits, flags); } QModelIndexList list; if (!sourceModel()) return list; QModelIndex proxyIndex; Q_FOREACH (const QModelIndex &idx, sourceModel()->match(mapToSource(start), role, value, hits, flags)) { proxyIndex = mapFromSource(idx); if (proxyIndex.isValid()) { list << proxyIndex; } } return list; } bool KRecursiveFilterProxyModel::acceptRow(int sourceRow, const QModelIndex &sourceParent) const { return QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent); } void KRecursiveFilterProxyModel::setSourceModel(QAbstractItemModel *model) { // Standard disconnect of the previous source model, if present if (sourceModel()) { disconnect(sourceModel(), SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector)), this, SLOT(sourceDataChanged(QModelIndex,QModelIndex,QVector))); disconnect(sourceModel(), SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)), this, SLOT(sourceRowsAboutToBeInserted(QModelIndex,int,int))); disconnect(sourceModel(), SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(sourceRowsInserted(QModelIndex,int,int))); disconnect(sourceModel(), SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), this, SLOT(sourceRowsAboutToBeRemoved(QModelIndex,int,int))); disconnect(sourceModel(), SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SLOT(sourceRowsRemoved(QModelIndex,int,int))); } QSortFilterProxyModel::setSourceModel(model); // Disconnect in the QSortFilterProxyModel. These methods will be invoked manually // in invokeDataChanged, invokeRowsInserted etc. // // The reason for that is that when the source model adds new rows for example, the new rows // May not match the filter, but maybe their child items do match. // // Source model before insert: // // - A // - B // - - C // - - D // - - - E // - - - F // - - - G // - H // - I // // If the A F and L (which doesn't exist in the source model yet) match the filter // the proxy will be: // // - A // - B // - - D // - - - F // // New rows are inserted in the source model below H: // // - A // - B // - - C // - - D // - - - E // - - - F // - - - G // - H // - - J // - - K // - - - L // - I // // As L matches the filter, it should be part of the KRecursiveFilterProxyModel. // // - A // - B // - - D // - - - F // - H // - - K // - - - L // // when the QSortFilterProxyModel gets a notification about new rows in H, it only checks // J and K to see if they match, ignoring L, and therefore not adding it to the proxy. // To work around that, we make sure that the QSFPM slot which handles that change in // the source model (_q_sourceRowsAboutToBeInserted) does not get called directly. // Instead we connect the sourceModel signal to our own slot in *this (sourceRowsAboutToBeInserted) // Inside that method, the entire new subtree is queried (J, K *and* L) to see if there is a match, // then the relevant Q_SLOTS in QSFPM are invoked. // In the example above, we need to tell the QSFPM that H should be queried again to see if // it matches the filter. It did not before, because L did not exist before. Now it does. That is // achieved by telling the QSFPM that the data changed for H, which causes it to requery this class // to see if H matches the filter (which it now does as L now exists). // That is done in sourceRowsInserted. if (!model) { return; } disconnect(model, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector)), this, SLOT(_q_sourceDataChanged(QModelIndex,QModelIndex,QVector))); disconnect(model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)), this, SLOT(_q_sourceRowsAboutToBeInserted(QModelIndex,int,int))); disconnect(model, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(_q_sourceRowsInserted(QModelIndex,int,int))); disconnect(model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), this, SLOT(_q_sourceRowsAboutToBeRemoved(QModelIndex,int,int))); disconnect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SLOT(_q_sourceRowsRemoved(QModelIndex,int,int))); // Slots for manual invoking of QSortFilterProxyModel methods. connect(model, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector)), this, SLOT(sourceDataChanged(QModelIndex,QModelIndex,QVector))); connect(model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)), this, SLOT(sourceRowsAboutToBeInserted(QModelIndex,int,int))); connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(sourceRowsInserted(QModelIndex,int,int))); connect(model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), this, SLOT(sourceRowsAboutToBeRemoved(QModelIndex,int,int))); connect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SLOT(sourceRowsRemoved(QModelIndex,int,int))); } #include "moc_krecursivefilterproxymodel.cpp" gammaray-2.11.3/3rdparty/kde/krecursivefilterproxymodel.h000066400000000000000000000106711413310443000235710ustar00rootroot00000000000000/* Copyright (c) 2009 Stephen Kelly This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KRECURSIVEFILTERPROXYMODEL_H #define KRECURSIVEFILTERPROXYMODEL_H #include #include "kitemmodels_export.h" class KRecursiveFilterProxyModelPrivate; /** @class KRecursiveFilterProxyModel krecursivefilterproxymodel.h KRecursiveFilterProxyModel @brief Implements recursive filtering of models Until Qt 5.10, QSortFilterProxyModel did not recurse when invoking a filtering stage, so that if a particular row is filtered out, its children are not even checked to see if they match the filter. If you can depend on Qt >= 5.10, then just use QSortFilterProxyModel::setRecursiveFilteringEnabled(true), and you don't need to use KRecursiveFilterProxyModel. For example, given a source model: @verbatim - A - B - - C - - - D - - - - E - - - F - - G - - H - I @endverbatim If a QSortFilterProxyModel is used with a filter matching A, D, G and I, the QSortFilterProxyModel will contain @verbatim - A - I @endverbatim That is, even though D and E match the filter, they are not represented in the proxy model because B does not match the filter and is filtered out. The KRecursiveFilterProxyModel checks child indexes for filter matching and ensures that all matching indexes are represented in the model. In the above example, the KRecursiveFilterProxyModel will contain @verbatim - A - B - - C - - - D - - G - I @endverbatim That is, the leaves in the model match the filter, but not necessarily the inner branches. QSortFilterProxyModel provides the virtual method filterAcceptsRow to allow custom filter implementations. Custom filter implementations can be written for KRecuriveFilterProxyModel using the acceptRow virtual method. Note that using this proxy model is additional overhead compared to QSortFilterProxyModel as every index in the model must be visited and queried. @author Stephen Kelly @since 4.5 */ class KITEMMODELS_EXPORT KRecursiveFilterProxyModel : public QSortFilterProxyModel { Q_OBJECT public: /** Constructor */ explicit KRecursiveFilterProxyModel(QObject *parent = nullptr); /** Destructor */ ~KRecursiveFilterProxyModel() override; /** @reimp */ void setSourceModel(QAbstractItemModel *model) override; /** * @reimplemented */ QModelIndexList match(const QModelIndex &start, int role, const QVariant &value, int hits = 1, Qt::MatchFlags flags = Qt::MatchFlags(Qt::MatchStartsWith | Qt::MatchWrap)) const override; protected: /** Reimplement this method for custom filtering strategies. */ virtual bool acceptRow(int sourceRow, const QModelIndex &sourceParent) const; /** @reimp */ bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override; KRecursiveFilterProxyModelPrivate *const d_ptr; private: //@cond PRIVATE Q_DECLARE_PRIVATE(KRecursiveFilterProxyModel) Q_PRIVATE_SLOT(d_func(), void sourceDataChanged(const QModelIndex &source_top_left, const QModelIndex &source_bottom_right, const QVector &roles = QVector())) Q_PRIVATE_SLOT(d_func(), void sourceRowsAboutToBeInserted(const QModelIndex &source_parent, int start, int end)) Q_PRIVATE_SLOT(d_func(), void sourceRowsInserted(const QModelIndex &source_parent, int start, int end)) Q_PRIVATE_SLOT(d_func(), void sourceRowsAboutToBeRemoved(const QModelIndex &source_parent, int start, int end)) Q_PRIVATE_SLOT(d_func(), void sourceRowsRemoved(const QModelIndex &source_parent, int start, int end)) //@endcond }; #endif gammaray-2.11.3/3rdparty/kde/qt_attribution.json000066400000000000000000000013051413310443000216420ustar00rootroot00000000000000{ "Id": "kitemmodels", "Name": "KItemModels", "QDocModule": "gammaray-manual", "QtUsage": "QAbstractItemModel extensions used in various places of GammaRay.", "Description": "Set of item models extending the Qt model-view framework.", "Homepage": "https://api.kde.org/frameworks/kitemmodels/html/index.html", "Version": "KF5", "License": "GNU Library General Public License v2 or later", "LicenseId": "LGPL-2.0", "LicenseFile": "COPYING.LIB", "Copyright": " Copyright (C) 2009 Stephen Kelly Copyright (C) 2010 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.net, author Stephen Kelly " } gammaray-2.11.3/3rdparty/kuserfeedback/000077500000000000000000000000001413310443000177335ustar00rootroot00000000000000gammaray-2.11.3/3rdparty/kuserfeedback/CMakeLists.txt000066400000000000000000000053531413310443000225010ustar00rootroot00000000000000set(gammaray_userfeedback_srcs common/surveytargetexpression.cpp common/surveytargetexpressionevaluator.cpp common/surveytargetexpressionlexer.cpp common/surveytargetexpressionparser.cpp common/surveytargetexpressionparser_p.cpp core/abstractdatasource.cpp core/applicationversionsource.cpp core/compilerinfosource.cpp core/feedbackconfiguicontroller.cpp core/openglinfosource.cpp core/platforminfosource.cpp core/provider.cpp core/qtversionsource.cpp core/selectionratiosource.cpp core/startcountsource.cpp core/surveyinfo.cpp core/usagetimesource.cpp widgets/feedbackconfigdialog.cpp widgets/feedbackconfigwidget.cpp widgets/notificationpopup.cpp ) list(APPEND gammaray_userfeedback_srcs core/auditloguicontroller.cpp widgets/auditlogbrowserdialog.cpp ) add_library(gammaray_kuserfeedback SHARED ${gammaray_userfeedback_srcs}) # Disable warnings for generated stuff if(MSVC) set_source_files_properties(common/surveytargetexpressionlexer.cpp PROPERTIES COMPILE_FLAGS "/wd4018 /wd4996") else() check_cxx_compiler_flag(-Wno-zero-as-null-pointer-constant HAVE_NO_CXX_ZERO_AS_NULL_POINTER_CON) check_cxx_compiler_flag(-Wno-unneeded-internal-declaration HAVE_NO_CXX_UNNEEDED_INTERNAL_DEC) check_cxx_compiler_flag(-Wno-null-conversion HAVE_NO_CXX_NULL_CONVERSION) set(CXX_FLAGS_QUIET_LEX_GEN_WARNINGS "-Wno-sign-compare") if(HAVE_NO_CXX_ZERO_AS_NULL_POINTER_CON) set(CXX_FLAGS_QUIET_LEX_GEN_WARNINGS "${CXX_FLAGS_QUIET_LEX_GEN_WARNINGS} -Wno-zero-as-null-pointer-constant") endif() if(HAVE_NO_CXX_UNNEEDED_INTERNAL_DEC) set(CXX_FLAGS_QUIET_LEX_GEN_WARNINGS "${CXX_FLAGS_QUIET_LEX_GEN_WARNINGS} -Wno-unneeded-internal-declaration") endif() if(HAVE_NO_CXX_NULL_CONVERSION) set(CXX_FLAGS_QUIET_LEX_GEN_WARNINGS "${CXX_FLAGS_QUIET_LEX_GEN_WARNINGS} -Wno-null-conversion") endif() set_source_files_properties(common/surveytargetexpressionlexer.cpp PROPERTIES COMPILE_FLAGS "${CXX_FLAGS_QUIET_LEX_GEN_WARNINGS}") endif() if(NOT HAVE_UNISTD_H) target_compile_definitions(gammaray_kuserfeedback PRIVATE YY_NO_UNISTD_H) endif() target_include_directories(gammaray_kuserfeedback PUBLIC "$" PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/widgets ) set_target_properties(gammaray_kuserfeedback PROPERTIES ${GAMMARAY_DEFAULT_LIBRARY_PROPERTIES}) gammaray_set_rpath(gammaray_kuserfeedback ${LIB_INSTALL_DIR}) generate_export_header(gammaray_kuserfeedback) target_link_libraries(gammaray_kuserfeedback PUBLIC Qt5::Widgets PRIVATE Qt5::Gui Qt5::Network) install(TARGETS gammaray_kuserfeedback EXPORT GammaRayTargets ${INSTALL_TARGETS_DEFAULT_ARGS}) gammaray-2.11.3/3rdparty/kuserfeedback/COPYING.LIB000066400000000000000000000636371413310443000214120ustar00rootroot00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin St, 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. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, 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 and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, 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 library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete 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 distribute a copy of this License along with the Library. 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 Library or any portion of it, thus forming a work based on the Library, 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) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, 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 Library, 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 Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you 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. If distribution of 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 satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be 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. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library 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. 9. 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 Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library 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 with this License. 11. 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 Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library 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 Library. 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. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library 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. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser 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 Library 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 Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, 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 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "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 LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. 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 LIBRARY 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 LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. 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 library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! gammaray-2.11.3/3rdparty/kuserfeedback/common/000077500000000000000000000000001413310443000212235ustar00rootroot00000000000000gammaray-2.11.3/3rdparty/kuserfeedback/common/surveytargetexpression.cpp000066400000000000000000000106711413310443000266200ustar00rootroot00000000000000/* Copyright (C) 2017 Volker Krause Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "surveytargetexpression.h" using namespace KUserFeedback; SurveyTargetExpression::SurveyTargetExpression(const QVariant &value) : m_type(Value) , m_value(value) { } SurveyTargetExpression::SurveyTargetExpression(const QString& source, const QVariant &index, const QString& elem) : m_value(index) , m_source(source) , m_sourceElement(elem) { if (index.type() == QVariant::Int) m_type = ListElement; else if (index.type() == QVariant::String) m_type = MapElement; else m_type = ScalarElement; } SurveyTargetExpression::SurveyTargetExpression(Type type, SurveyTargetExpression* left, SurveyTargetExpression* right) : m_type(type) , m_left(left) , m_right(right) { } SurveyTargetExpression::~SurveyTargetExpression() { } SurveyTargetExpression::Type SurveyTargetExpression::type() const { return m_type; } QVariant SurveyTargetExpression::value() const { return m_value; } QString SurveyTargetExpression::source() const { return m_source; } QString SurveyTargetExpression::sourceElement() const { return m_sourceElement; } SurveyTargetExpression* SurveyTargetExpression::left() const { return m_left.get(); } SurveyTargetExpression* SurveyTargetExpression::right() const { return m_right.get(); } QDebug operator<<(QDebug debug, SurveyTargetExpression* expr) { if (!expr) { debug << "(null)"; return debug; } switch (expr->type()) { case SurveyTargetExpression::Value: debug << expr->value().toString(); //toString() is needed for Qt4 support break; case SurveyTargetExpression::ScalarElement: debug.nospace() << expr->source() << "." << expr->sourceElement(); break; case SurveyTargetExpression::ListElement: debug.nospace() << expr->source() << "[" << expr->value().toInt() << "]." << expr->sourceElement(); break; case SurveyTargetExpression::MapElement: debug.nospace() << expr->source() << "[" << expr->value().toString() << "]." << expr->sourceElement(); break; case SurveyTargetExpression::OpLogicAnd: debug.nospace() << "(" << expr->left() << " && " << expr->right() << ")"; break; case SurveyTargetExpression::OpLogicOr: debug.nospace() << "(" << expr->left() << " || " << expr->right() << ")"; break; case SurveyTargetExpression::OpEqual: debug.nospace() << "[" << expr->left() << " == " << expr->right() << "]"; break; case SurveyTargetExpression::OpNotEqual: debug.nospace() << "[" << expr->left() << " != " << expr->right() << "]"; break; case SurveyTargetExpression::OpGreater: debug.nospace() << "[" << expr->left() << " > " << expr->right() << "]"; break; case SurveyTargetExpression::OpGreaterEqual: debug.nospace() << "[" << expr->left() << " >= " << expr->right() << "]"; break; case SurveyTargetExpression::OpLess: debug.nospace() << "[" << expr->left() << " < " << expr->right() << "]"; break; case SurveyTargetExpression::OpLessEqual: debug.nospace() << "[" << expr->left() << " <= " << expr->right() << "]"; break; } return debug; } gammaray-2.11.3/3rdparty/kuserfeedback/common/surveytargetexpression.h000066400000000000000000000046231413310443000262650ustar00rootroot00000000000000/* Copyright (C) 2017 Volker Krause Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef KUSERFEEDBACK_SURVEYTARGETEXPRESSION_H #define KUSERFEEDBACK_SURVEYTARGETEXPRESSION_H #include #include #include #include namespace KUserFeedback { class SurveyTargetExpression { public: enum Type { Value, ScalarElement, ListElement, MapElement, OpLogicAnd, OpLogicOr, OpEqual, OpNotEqual, OpLess, OpLessEqual, OpGreater, OpGreaterEqual }; explicit SurveyTargetExpression(const QVariant &value); explicit SurveyTargetExpression(const QString &source, const QVariant &index, const QString &elem); explicit SurveyTargetExpression(Type type, SurveyTargetExpression *left, SurveyTargetExpression *right); ~SurveyTargetExpression(); Type type() const; QVariant value() const; QString source() const; QString sourceElement() const; SurveyTargetExpression* left() const; SurveyTargetExpression* right() const; private: Type m_type; QVariant m_value; QString m_source; QString m_sourceElement; std::unique_ptr m_left; std::unique_ptr m_right; }; } QDebug operator<<(QDebug debug, KUserFeedback::SurveyTargetExpression *expr); #endif gammaray-2.11.3/3rdparty/kuserfeedback/common/surveytargetexpressionevaluator.cpp000066400000000000000000000117131413310443000305410ustar00rootroot00000000000000/* Copyright (C) 2017 Volker Krause Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "surveytargetexpressionevaluator.h" #include "surveytargetexpression.h" using namespace KUserFeedback; SurveyTargetExpressionDataProvider::SurveyTargetExpressionDataProvider() { } SurveyTargetExpressionDataProvider::~SurveyTargetExpressionDataProvider() { } SurveyTargetExpressionEvaluator::SurveyTargetExpressionEvaluator() : m_provider(nullptr) { } SurveyTargetExpressionEvaluator::~SurveyTargetExpressionEvaluator() { } void SurveyTargetExpressionEvaluator::setDataProvider(const SurveyTargetExpressionDataProvider* provider) { m_provider = provider; } bool SurveyTargetExpressionEvaluator::evaluate(SurveyTargetExpression* expression) { // logical operations switch (expression->type()) { case SurveyTargetExpression::OpLogicAnd: return evaluate(expression->left()) && evaluate(expression->right()); case SurveyTargetExpression::OpLogicOr: return evaluate(expression->left()) || evaluate(expression->right()); default: break; } // comparisson operations: const auto lhs = value(expression->left()); const auto rhs = value(expression->right()); if (lhs.type() == QVariant::Invalid || rhs.type() == QVariant::Invalid) // invalid element access can never succeed return false; if ((lhs.type() == QVariant::String && rhs.type() != QVariant::String) || (lhs.type() != QVariant::String && rhs.type() == QVariant::String)) return false; // strings can only be compared to strings switch (expression->type()) { case SurveyTargetExpression::OpEqual: return lhs == rhs; case SurveyTargetExpression::OpNotEqual: return lhs != rhs; case SurveyTargetExpression::OpGreater: return lhs > rhs; case SurveyTargetExpression::OpGreaterEqual: return lhs >= rhs; case SurveyTargetExpression::OpLess: return lhs < rhs; case SurveyTargetExpression::OpLessEqual: return lhs <= rhs; default: break; } return false; } QVariant SurveyTargetExpressionEvaluator::value(SurveyTargetExpression* expression) { switch (expression->type()) { case SurveyTargetExpression::Value: return expression->value(); case SurveyTargetExpression::ScalarElement: { const auto v = value(expression->source()); if (v.canConvert() && expression->sourceElement() == QLatin1String("size")) return v.value().size(); const auto m = v.toMap(); const auto it = m.find(expression->sourceElement()); if (it != m.end() && !it.value().canConvert()) return it.value(); if (expression->sourceElement() == QLatin1String("size")) return m.size(); return QVariant(); } case SurveyTargetExpression::ListElement: { const auto v = value(expression->source()).value().value(expression->value().toInt()); return v.toMap().value(expression->sourceElement()); } case SurveyTargetExpression::MapElement: { const auto v = value(expression->source()).toMap().value(expression->value().toString()); qDebug() << v << value(expression->source()).toMap() << expression->value().toString(); return v.toMap().value(expression->sourceElement()); } default: break; } Q_ASSERT(false); return QVariant(); } QVariant SurveyTargetExpressionEvaluator::value(const QString& source) { Q_ASSERT(m_provider); const auto it = m_dataCache.constFind(source); if (it != m_dataCache.constEnd()) return it.value(); const auto v = m_provider->sourceData(source); m_dataCache.insert(source, v); return v; } gammaray-2.11.3/3rdparty/kuserfeedback/common/surveytargetexpressionevaluator.h000066400000000000000000000042631413310443000302100ustar00rootroot00000000000000/* Copyright (C) 2017 Volker Krause Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef KUSERFEEDBACK_SURVEYTARGETEXPRESSIONEVALUATOR_H #define KUSERFEEDBACK_SURVEYTARGETEXPRESSIONEVALUATOR_H #include QT_BEGIN_NAMESPACE class QString; class QVariant; QT_END_NAMESPACE namespace KUserFeedback { class SurveyTargetExpression; class SurveyTargetExpressionDataProvider { public: SurveyTargetExpressionDataProvider(); virtual ~SurveyTargetExpressionDataProvider(); virtual QVariant sourceData(const QString &sourceName) const = 0; private: Q_DISABLE_COPY(SurveyTargetExpressionDataProvider) }; class SurveyTargetExpressionEvaluator { public: SurveyTargetExpressionEvaluator(); ~SurveyTargetExpressionEvaluator(); void setDataProvider(const SurveyTargetExpressionDataProvider *provider); bool evaluate(SurveyTargetExpression *expression); private: QVariant value(SurveyTargetExpression *expression); QVariant value(const QString &source); const SurveyTargetExpressionDataProvider *m_provider; QHash m_dataCache; }; } #endif // KUSERFEEDBACK_SURVEYTARGETEXPRESSIONEVALUATOR_H gammaray-2.11.3/3rdparty/kuserfeedback/common/surveytargetexpressionlexer.cpp000066400000000000000000001616521413310443000276660ustar00rootroot00000000000000#line 2 "surveytargetexpressionlexer.cpp" #line 4 "surveytargetexpressionlexer.cpp" #define YY_INT_ALIGNED short int /* A lexical scanner generated by flex */ #define FLEX_SCANNER #define YY_FLEX_MAJOR_VERSION 2 #define YY_FLEX_MINOR_VERSION 6 #define YY_FLEX_SUBMINOR_VERSION 1 #if YY_FLEX_SUBMINOR_VERSION > 0 #define FLEX_BETA #endif /* First, we deal with platform-specific or compiler-specific issues. */ /* begin standard C headers. */ #include #include #include #include /* end standard C headers. */ /* flex integer type definitions */ #ifndef FLEXINT_H #define FLEXINT_H /* C99 systems have . Non-C99 systems may or may not. */ #if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, * if you want the limit (max/min) macros for int types. */ #ifndef __STDC_LIMIT_MACROS #define __STDC_LIMIT_MACROS 1 #endif #include typedef int8_t flex_int8_t; typedef uint8_t flex_uint8_t; typedef int16_t flex_int16_t; typedef uint16_t flex_uint16_t; typedef int32_t flex_int32_t; typedef uint32_t flex_uint32_t; #else typedef signed char flex_int8_t; typedef short int flex_int16_t; typedef int flex_int32_t; typedef unsigned char flex_uint8_t; typedef unsigned short int flex_uint16_t; typedef unsigned int flex_uint32_t; /* Limits of integral types. */ #ifndef INT8_MIN #define INT8_MIN (-128) #endif #ifndef INT16_MIN #define INT16_MIN (-32767-1) #endif #ifndef INT32_MIN #define INT32_MIN (-2147483647-1) #endif #ifndef INT8_MAX #define INT8_MAX (127) #endif #ifndef INT16_MAX #define INT16_MAX (32767) #endif #ifndef INT32_MAX #define INT32_MAX (2147483647) #endif #ifndef UINT8_MAX #define UINT8_MAX (255U) #endif #ifndef UINT16_MAX #define UINT16_MAX (65535U) #endif #ifndef UINT32_MAX #define UINT32_MAX (4294967295U) #endif #endif /* ! C99 */ #endif /* ! FLEXINT_H */ /* TODO: this is always defined, so inline it */ #define yyconst const #if defined(__GNUC__) && __GNUC__ >= 3 #define yynoreturn __attribute__((__noreturn__)) #else #define yynoreturn #endif /* Returned upon end-of-file. */ #define YY_NULL 0 /* Promotes a possibly negative, possibly signed char to an unsigned * integer for use as an array index. If the signed char is negative, * we want to instead treat it as an 8-bit unsigned char, hence the * double cast. */ #define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c) /* An opaque pointer. */ #ifndef YY_TYPEDEF_YY_SCANNER_T #define YY_TYPEDEF_YY_SCANNER_T typedef void* yyscan_t; #endif /* For convenience, these vars (plus the bison vars far below) are macros in the reentrant scanner. */ #define yyin yyg->yyin_r #define yyout yyg->yyout_r #define yyextra yyg->yyextra_r #define yyleng yyg->yyleng_r #define yytext yyg->yytext_r #define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno) #define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column) #define yy_flex_debug yyg->yy_flex_debug_r /* Enter a start condition. This macro really ought to take a parameter, * but we do it the disgusting crufty way forced on us by the ()-less * definition of BEGIN. */ #define BEGIN yyg->yy_start = 1 + 2 * /* Translate the current start state into a value that can be later handed * to BEGIN to return to the state. The YYSTATE alias is for lex * compatibility. */ #define YY_START ((yyg->yy_start - 1) / 2) #define YYSTATE YY_START /* Action number for EOF rule of a given start state. */ #define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) /* Special action meaning "start processing a new file". */ #define YY_NEW_FILE yyrestart(yyin ,yyscanner ) #define YY_END_OF_BUFFER_CHAR 0 /* Size of default input buffer. */ #ifndef YY_BUF_SIZE #ifdef __ia64__ /* On IA-64, the buffer size is 16k, not 8k. * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case. * Ditto for the __ia64__ case accordingly. */ #define YY_BUF_SIZE 32768 #else #define YY_BUF_SIZE 16384 #endif /* __ia64__ */ #endif /* The state buf must be large enough to hold one state per character in the main buffer. */ #define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type)) #ifndef YY_TYPEDEF_YY_BUFFER_STATE #define YY_TYPEDEF_YY_BUFFER_STATE typedef struct yy_buffer_state *YY_BUFFER_STATE; #endif #ifndef YY_TYPEDEF_YY_SIZE_T #define YY_TYPEDEF_YY_SIZE_T typedef size_t yy_size_t; #endif #define EOB_ACT_CONTINUE_SCAN 0 #define EOB_ACT_END_OF_FILE 1 #define EOB_ACT_LAST_MATCH 2 #define YY_LESS_LINENO(n) #define YY_LINENO_REWIND_TO(ptr) /* Return all but the first "n" matched characters back to the input stream. */ #define yyless(n) \ do \ { \ /* Undo effects of setting up yytext. */ \ int yyless_macro_arg = (n); \ YY_LESS_LINENO(yyless_macro_arg);\ *yy_cp = yyg->yy_hold_char; \ YY_RESTORE_YY_MORE_OFFSET \ yyg->yy_c_buf_p = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ YY_DO_BEFORE_ACTION; /* set up yytext again */ \ } \ while ( 0 ) #define unput(c) yyunput( c, yyg->yytext_ptr , yyscanner ) #ifndef YY_STRUCT_YY_BUFFER_STATE #define YY_STRUCT_YY_BUFFER_STATE struct yy_buffer_state { FILE *yy_input_file; char *yy_ch_buf; /* input buffer */ char *yy_buf_pos; /* current position in input buffer */ /* Size of input buffer in bytes, not including room for EOB * characters. */ int yy_buf_size; /* Number of characters read into yy_ch_buf, not including EOB * characters. */ int yy_n_chars; /* Whether we "own" the buffer - i.e., we know we created it, * and can realloc() it to grow it, and should free() it to * delete it. */ int yy_is_our_buffer; /* Whether this is an "interactive" input source; if so, and * if we're using stdio for input, then we want to use getc() * instead of fread(), to make sure we stop fetching input after * each newline. */ int yy_is_interactive; /* Whether we're considered to be at the beginning of a line. * If so, '^' rules will be active on the next match, otherwise * not. */ int yy_at_bol; int yy_bs_lineno; /**< The line count. */ int yy_bs_column; /**< The column count. */ /* Whether to try to fill the input buffer when we reach the * end of it. */ int yy_fill_buffer; int yy_buffer_status; #define YY_BUFFER_NEW 0 #define YY_BUFFER_NORMAL 1 /* When an EOF's been seen but there's still some text to process * then we mark the buffer as YY_EOF_PENDING, to indicate that we * shouldn't try reading from the input source any more. We might * still have a bunch of tokens to match, though, because of * possible backing-up. * * When we actually see the EOF, we change the status to "new" * (via yyrestart()), so that the user can continue scanning by * just pointing yyin at a new input file. */ #define YY_BUFFER_EOF_PENDING 2 }; #endif /* !YY_STRUCT_YY_BUFFER_STATE */ /* We provide macros for accessing buffer states in case in the * future we want to put the buffer states in a more general * "scanner state". * * Returns the top of the stack, or NULL. */ #define YY_CURRENT_BUFFER ( yyg->yy_buffer_stack \ ? yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] \ : NULL) /* Same as previous macro, but useful when we know that the buffer stack is not * NULL or when we need an lvalue. For internal use only. */ #define YY_CURRENT_BUFFER_LVALUE yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] void yyrestart (FILE *input_file ,yyscan_t yyscanner ); void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner ); YY_BUFFER_STATE yy_create_buffer (FILE *file,int size ,yyscan_t yyscanner ); void yy_delete_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner ); void yy_flush_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner ); void yypush_buffer_state (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner ); void yypop_buffer_state (yyscan_t yyscanner ); static void yyensure_buffer_stack (yyscan_t yyscanner ); static void yy_load_buffer_state (yyscan_t yyscanner ); static void yy_init_buffer (YY_BUFFER_STATE b,FILE *file ,yyscan_t yyscanner ); #define YY_FLUSH_BUFFER yy_flush_buffer(YY_CURRENT_BUFFER ,yyscanner) YY_BUFFER_STATE yy_scan_buffer (char *base,yy_size_t size ,yyscan_t yyscanner ); YY_BUFFER_STATE yy_scan_string (yyconst char *yy_str ,yyscan_t yyscanner ); YY_BUFFER_STATE yy_scan_bytes (yyconst char *bytes,int len ,yyscan_t yyscanner ); void *yyalloc (yy_size_t ,yyscan_t yyscanner ); void *yyrealloc (void *,yy_size_t ,yyscan_t yyscanner ); void yyfree (void * ,yyscan_t yyscanner ); #define yy_new_buffer yy_create_buffer #define yy_set_interactive(is_interactive) \ { \ if ( ! YY_CURRENT_BUFFER ){ \ yyensure_buffer_stack (yyscanner); \ YY_CURRENT_BUFFER_LVALUE = \ yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); \ } \ YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ } #define yy_set_bol(at_bol) \ { \ if ( ! YY_CURRENT_BUFFER ){\ yyensure_buffer_stack (yyscanner); \ YY_CURRENT_BUFFER_LVALUE = \ yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); \ } \ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ } #define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) /* Begin user sect3 */ #define yywrap(yyscanner) (/*CONSTCOND*/1) #define YY_SKIP_YYWRAP typedef unsigned char YY_CHAR; typedef int yy_state_type; #define yytext_ptr yytext_r static yy_state_type yy_get_previous_state (yyscan_t yyscanner ); static yy_state_type yy_try_NUL_trans (yy_state_type current_state ,yyscan_t yyscanner); static int yy_get_next_buffer (yyscan_t yyscanner ); static void yynoreturn yy_fatal_error (yyconst char* msg ,yyscan_t yyscanner ); /* Done after the current pattern has been matched and before the * corresponding action - sets up yytext. */ #define YY_DO_BEFORE_ACTION \ yyg->yytext_ptr = yy_bp; \ yyleng = (int) (yy_cp - yy_bp); \ yyg->yy_hold_char = *yy_cp; \ *yy_cp = '\0'; \ yyg->yy_c_buf_p = yy_cp; #define YY_NUM_RULES 22 #define YY_END_OF_BUFFER 23 /* This struct is not used in this scanner, but its presence is necessary. */ struct yy_trans_info { flex_int32_t yy_verify; flex_int32_t yy_nxt; }; static yyconst flex_int16_t yy_accept[51] = { 0, 0, 0, 23, 21, 1, 1, 21, 21, 21, 2, 3, 21, 4, 8, 19, 21, 17, 12, 5, 6, 12, 12, 21, 1, 16, 0, 9, 0, 13, 0, 8, 7, 7, 20, 15, 18, 12, 12, 12, 14, 0, 7, 12, 12, 0, 7, 12, 10, 11, 0 } ; static yyconst YY_CHAR yy_ec[256] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 4, 5, 1, 1, 1, 6, 1, 7, 8, 1, 9, 1, 9, 10, 1, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 1, 1, 12, 13, 14, 1, 1, 15, 15, 15, 15, 16, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 17, 18, 19, 1, 20, 1, 21, 15, 15, 15, 22, 23, 15, 15, 15, 15, 15, 24, 15, 25, 15, 15, 15, 26, 27, 28, 29, 15, 15, 15, 15, 15, 1, 30, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 } ; static yyconst YY_CHAR yy_meta[31] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 2, 2, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1 } ; static yyconst flex_uint16_t yy_base[53] = { 0, 0, 0, 82, 83, 29, 31, 68, 30, 74, 83, 83, 26, 68, 28, 65, 64, 63, 0, 83, 83, 54, 48, 43, 38, 83, 37, 83, 38, 83, 61, 34, 35, 36, 83, 83, 83, 0, 47, 41, 83, 50, 38, 42, 43, 53, 51, 31, 0, 0, 83, 66, 48 } ; static yyconst flex_int16_t yy_def[53] = { 0, 50, 1, 50, 50, 50, 50, 50, 51, 50, 50, 50, 50, 50, 50, 50, 50, 50, 52, 50, 50, 52, 52, 50, 50, 50, 51, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 52, 52, 52, 50, 50, 50, 52, 52, 50, 50, 52, 52, 52, 0, 50, 50 } ; static yyconst flex_uint16_t yy_nxt[114] = { 0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 18, 19, 4, 20, 18, 18, 18, 21, 18, 18, 18, 18, 22, 18, 23, 24, 24, 24, 24, 27, 30, 31, 33, 31, 24, 24, 27, 26, 33, 31, 32, 42, 28, 42, 37, 41, 41, 49, 41, 28, 26, 41, 41, 45, 41, 46, 46, 26, 46, 48, 26, 26, 26, 47, 44, 43, 32, 40, 39, 38, 36, 35, 34, 32, 29, 25, 50, 3, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50 } ; static yyconst flex_int16_t yy_chk[114] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 5, 6, 6, 8, 12, 12, 14, 14, 24, 24, 26, 28, 31, 31, 32, 33, 8, 42, 52, 32, 33, 47, 42, 26, 28, 32, 33, 41, 42, 41, 46, 28, 45, 44, 28, 51, 51, 43, 39, 38, 30, 23, 22, 21, 17, 16, 15, 13, 9, 7, 3, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50 } ; /* The intent behind this definition is that it'll catch * any uses of REJECT which flex missed. */ #define REJECT reject_used_but_not_detected #define yymore() yymore_used_but_not_detected #define YY_MORE_ADJ 0 #define YY_RESTORE_YY_MORE_OFFSET #line 1 "surveytargetexpressionlexer.l" #line 2 "surveytargetexpressionlexer.l" /* Copyright (C) 2017 Volker Krause This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library 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 Library General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "surveytargetexpression.h" #include "surveytargetexpressionparser_p.h" extern char* unquoteString(const char *str); #line 493 "surveytargetexpressionlexer.cpp" #define INITIAL 0 #ifndef YY_NO_UNISTD_H /* Special case for "unistd.h", since it is non-ANSI. We include it way * down here because we want the user's section 1 to have been scanned first. * The user has a chance to override it with an option. */ #include #endif #ifndef YY_EXTRA_TYPE #define YY_EXTRA_TYPE void * #endif /* Holds the entire state of the reentrant scanner. */ struct yyguts_t { /* User-defined. Not touched by flex. */ YY_EXTRA_TYPE yyextra_r; /* The rest are the same as the globals declared in the non-reentrant scanner. */ FILE *yyin_r, *yyout_r; size_t yy_buffer_stack_top; /**< index of top of stack. */ size_t yy_buffer_stack_max; /**< capacity of stack. */ YY_BUFFER_STATE * yy_buffer_stack; /**< Stack as an array. */ char yy_hold_char; int yy_n_chars; int yyleng_r; char *yy_c_buf_p; int yy_init; int yy_start; int yy_did_buffer_switch_on_eof; int yy_start_stack_ptr; int yy_start_stack_depth; int *yy_start_stack; yy_state_type yy_last_accepting_state; char* yy_last_accepting_cpos; int yylineno_r; int yy_flex_debug_r; char *yytext_r; int yy_more_flag; int yy_more_len; YYSTYPE * yylval_r; }; /* end struct yyguts_t */ static int yy_init_globals (yyscan_t yyscanner ); /* This must go here because YYSTYPE and YYLTYPE are included * from bison output in section 1.*/ # define yylval yyg->yylval_r int yylex_init (yyscan_t* scanner); int yylex_init_extra (YY_EXTRA_TYPE user_defined,yyscan_t* scanner); /* Accessor methods to globals. These are made visible to non-reentrant scanners for convenience. */ int yylex_destroy (yyscan_t yyscanner ); int yyget_debug (yyscan_t yyscanner ); void yyset_debug (int debug_flag ,yyscan_t yyscanner ); YY_EXTRA_TYPE yyget_extra (yyscan_t yyscanner ); void yyset_extra (YY_EXTRA_TYPE user_defined ,yyscan_t yyscanner ); FILE *yyget_in (yyscan_t yyscanner ); void yyset_in (FILE * _in_str ,yyscan_t yyscanner ); FILE *yyget_out (yyscan_t yyscanner ); void yyset_out (FILE * _out_str ,yyscan_t yyscanner ); int yyget_leng (yyscan_t yyscanner ); char *yyget_text (yyscan_t yyscanner ); int yyget_lineno (yyscan_t yyscanner ); void yyset_lineno (int _line_number ,yyscan_t yyscanner ); int yyget_column (yyscan_t yyscanner ); void yyset_column (int _column_no ,yyscan_t yyscanner ); YYSTYPE * yyget_lval (yyscan_t yyscanner ); void yyset_lval (YYSTYPE * yylval_param ,yyscan_t yyscanner ); /* Macros after this point can all be overridden by user definitions in * section 1. */ #ifndef YY_SKIP_YYWRAP #ifdef __cplusplus extern "C" int yywrap (yyscan_t yyscanner ); #else extern int yywrap (yyscan_t yyscanner ); #endif #endif #ifndef YY_NO_UNPUT #endif #ifndef yytext_ptr static void yy_flex_strncpy (char *,yyconst char *,int ,yyscan_t yyscanner); #endif #ifdef YY_NEED_STRLEN static int yy_flex_strlen (yyconst char * ,yyscan_t yyscanner); #endif #ifndef YY_NO_INPUT #ifdef __cplusplus static int yyinput (yyscan_t yyscanner ); #else static int input (yyscan_t yyscanner ); #endif #endif /* Amount of stuff to slurp up with each read. */ #ifndef YY_READ_BUF_SIZE #ifdef __ia64__ /* On IA-64, the buffer size is 16k, not 8k */ #define YY_READ_BUF_SIZE 16384 #else #define YY_READ_BUF_SIZE 8192 #endif /* __ia64__ */ #endif /* Copy whatever the last rule matched to the standard output. */ #ifndef ECHO /* This used to be an fputs(), but since the string might contain NUL's, * we now use fwrite(). */ #define ECHO do { if (fwrite( yytext, (size_t) yyleng, 1, yyout )) {} } while (0) #endif /* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, * is returned in "result". */ #ifndef YY_INPUT #define YY_INPUT(buf,result,max_size) \ if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ { \ int c = '*'; \ size_t n; \ for ( n = 0; n < max_size && \ (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ buf[n] = (char) c; \ if ( c == '\n' ) \ buf[n++] = (char) c; \ if ( c == EOF && ferror( yyin ) ) \ YY_FATAL_ERROR( "input in flex scanner failed" ); \ result = n; \ } \ else \ { \ errno=0; \ while ( (result = (int) fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \ { \ if( errno != EINTR) \ { \ YY_FATAL_ERROR( "input in flex scanner failed" ); \ break; \ } \ errno=0; \ clearerr(yyin); \ } \ }\ \ #endif /* No semi-colon after return; correct usage is to write "yyterminate();" - * we don't want an extra ';' after the "return" because that will cause * some compilers to complain about unreachable statements. */ #ifndef yyterminate #define yyterminate() return YY_NULL #endif /* Number of entries by which start-condition stack grows. */ #ifndef YY_START_STACK_INCR #define YY_START_STACK_INCR 25 #endif /* Report a fatal error. */ #ifndef YY_FATAL_ERROR #define YY_FATAL_ERROR(msg) yy_fatal_error( msg , yyscanner) #endif /* end tables serialization structures and prototypes */ /* Default declaration of generated scanner - a define so the user can * easily add parameters. */ #ifndef YY_DECL #define YY_DECL_IS_OURS 1 extern int yylex \ (YYSTYPE * yylval_param ,yyscan_t yyscanner); #define YY_DECL int yylex \ (YYSTYPE * yylval_param , yyscan_t yyscanner) #endif /* !YY_DECL */ /* Code executed at the beginning of each rule, after yytext and yyleng * have been set up. */ #ifndef YY_USER_ACTION #define YY_USER_ACTION #endif /* Code executed at the end of each rule. */ #ifndef YY_BREAK #define YY_BREAK /*LINTED*/break; #endif #define YY_RULE_SETUP \ YY_USER_ACTION /** The main scanner function which does all the work. */ YY_DECL { yy_state_type yy_current_state; char *yy_cp, *yy_bp; int yy_act; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yylval = yylval_param; if ( !yyg->yy_init ) { yyg->yy_init = 1; #ifdef YY_USER_INIT YY_USER_INIT; #endif if ( ! yyg->yy_start ) yyg->yy_start = 1; /* first start state */ if ( ! yyin ) yyin = stdin; if ( ! yyout ) yyout = stdout; if ( ! YY_CURRENT_BUFFER ) { yyensure_buffer_stack (yyscanner); YY_CURRENT_BUFFER_LVALUE = yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); } yy_load_buffer_state(yyscanner ); } { #line 45 "surveytargetexpressionlexer.l" #line 769 "surveytargetexpressionlexer.cpp" while ( /*CONSTCOND*/1 ) /* loops until end-of-file is reached */ { yy_cp = yyg->yy_c_buf_p; /* Support of yytext. */ *yy_cp = yyg->yy_hold_char; /* yy_bp points to the position in yy_ch_buf of the start of * the current run. */ yy_bp = yy_cp; yy_current_state = yyg->yy_start; yy_match: do { YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)] ; if ( yy_accept[yy_current_state] ) { yyg->yy_last_accepting_state = yy_current_state; yyg->yy_last_accepting_cpos = yy_cp; } while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 51 ) yy_c = yy_meta[(unsigned int) yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + (flex_int16_t) yy_c]; ++yy_cp; } while ( yy_current_state != 50 ); yy_cp = yyg->yy_last_accepting_cpos; yy_current_state = yyg->yy_last_accepting_state; yy_find_action: yy_act = yy_accept[yy_current_state]; YY_DO_BEFORE_ACTION; do_action: /* This label is used only to access EOF actions. */ switch ( yy_act ) { /* beginning of action switch */ case 0: /* must back up */ /* undo the effects of YY_DO_BEFORE_ACTION */ *yy_cp = yyg->yy_hold_char; yy_cp = yyg->yy_last_accepting_cpos; yy_current_state = yyg->yy_last_accepting_state; goto yy_find_action; case 1: /* rule 1 can match eol */ YY_RULE_SETUP #line 47 "surveytargetexpressionlexer.l" {} YY_BREAK case 2: YY_RULE_SETUP #line 49 "surveytargetexpressionlexer.l" { return T_LPAREN; } YY_BREAK case 3: YY_RULE_SETUP #line 50 "surveytargetexpressionlexer.l" { return T_RPAREN; } YY_BREAK case 4: YY_RULE_SETUP #line 51 "surveytargetexpressionlexer.l" { return T_DOT; } YY_BREAK case 5: YY_RULE_SETUP #line 52 "surveytargetexpressionlexer.l" { return T_LBRACKET; } YY_BREAK case 6: YY_RULE_SETUP #line 53 "surveytargetexpressionlexer.l" { return T_RBRACKET; } YY_BREAK case 7: YY_RULE_SETUP #line 55 "surveytargetexpressionlexer.l" { yylval->doubleVal = atof(yytext); return T_DOUBLE; } YY_BREAK case 8: YY_RULE_SETUP #line 59 "surveytargetexpressionlexer.l" { yylval->intVal = atoi(yytext); return T_INTEGER; } YY_BREAK case 9: /* rule 9 can match eol */ YY_RULE_SETUP #line 63 "surveytargetexpressionlexer.l" { yylval->str = unquoteString(yytext); return T_STRING; } YY_BREAK case 10: YY_RULE_SETUP #line 67 "surveytargetexpressionlexer.l" { yylval->boolVal = true; return T_BOOLEAN; } YY_BREAK case 11: YY_RULE_SETUP #line 68 "surveytargetexpressionlexer.l" { yylval->boolVal = false; return T_BOOLEAN; } YY_BREAK case 12: YY_RULE_SETUP #line 70 "surveytargetexpressionlexer.l" { yylval->str = strdup(yytext); return T_IDENTIFIER; } YY_BREAK case 13: YY_RULE_SETUP #line 75 "surveytargetexpressionlexer.l" { return T_OP_AND; } YY_BREAK case 14: YY_RULE_SETUP #line 76 "surveytargetexpressionlexer.l" { return T_OP_OR; } YY_BREAK case 15: YY_RULE_SETUP #line 77 "surveytargetexpressionlexer.l" { return T_OP_EQ; } YY_BREAK case 16: YY_RULE_SETUP #line 78 "surveytargetexpressionlexer.l" { return T_OP_NEQ; } YY_BREAK case 17: YY_RULE_SETUP #line 79 "surveytargetexpressionlexer.l" { return T_OP_GT; } YY_BREAK case 18: YY_RULE_SETUP #line 80 "surveytargetexpressionlexer.l" { return T_OP_GE; } YY_BREAK case 19: YY_RULE_SETUP #line 81 "surveytargetexpressionlexer.l" { return T_OP_LT; } YY_BREAK case 20: YY_RULE_SETUP #line 82 "surveytargetexpressionlexer.l" { return T_OP_LE; } YY_BREAK case 21: YY_RULE_SETUP #line 84 "surveytargetexpressionlexer.l" { printf("unexpected character: %s\n", yytext); } YY_BREAK case 22: YY_RULE_SETUP #line 88 "surveytargetexpressionlexer.l" YY_FATAL_ERROR( "flex scanner jammed" ); YY_BREAK #line 948 "surveytargetexpressionlexer.cpp" case YY_STATE_EOF(INITIAL): yyterminate(); case YY_END_OF_BUFFER: { /* Amount of text matched not including the EOB char. */ int yy_amount_of_matched_text = (int) (yy_cp - yyg->yytext_ptr) - 1; /* Undo the effects of YY_DO_BEFORE_ACTION. */ *yy_cp = yyg->yy_hold_char; YY_RESTORE_YY_MORE_OFFSET if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) { /* We're scanning a new file or input source. It's * possible that this happened because the user * just pointed yyin at a new source and called * yylex(). If so, then we have to assure * consistency between YY_CURRENT_BUFFER and our * globals. Here is the right place to do so, because * this is the first action (other than possibly a * back-up) that will match for the new input source. */ yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin; YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; } /* Note that here we test for yy_c_buf_p "<=" to the position * of the first EOB in the buffer, since yy_c_buf_p will * already have been incremented past the NUL character * (since all states make transitions on EOB to the * end-of-buffer state). Contrast this with the test * in input(). */ if ( yyg->yy_c_buf_p <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) { /* This was really a NUL. */ yy_state_type yy_next_state; yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text; yy_current_state = yy_get_previous_state( yyscanner ); /* Okay, we're now positioned to make the NUL * transition. We couldn't have * yy_get_previous_state() go ahead and do it * for us because it doesn't know how to deal * with the possibility of jamming (and we don't * want to build jamming into it because then it * will run more slowly). */ yy_next_state = yy_try_NUL_trans( yy_current_state , yyscanner); yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; if ( yy_next_state ) { /* Consume the NUL. */ yy_cp = ++yyg->yy_c_buf_p; yy_current_state = yy_next_state; goto yy_match; } else { yy_cp = yyg->yy_last_accepting_cpos; yy_current_state = yyg->yy_last_accepting_state; goto yy_find_action; } } else switch ( yy_get_next_buffer( yyscanner ) ) { case EOB_ACT_END_OF_FILE: { yyg->yy_did_buffer_switch_on_eof = 0; if ( yywrap(yyscanner ) ) { /* Note: because we've taken care in * yy_get_next_buffer() to have set up * yytext, we can now set up * yy_c_buf_p so that if some total * hoser (like flex itself) wants to * call the scanner after we return the * YY_NULL, it'll still work - another * YY_NULL will get returned. */ yyg->yy_c_buf_p = yyg->yytext_ptr + YY_MORE_ADJ; yy_act = YY_STATE_EOF(YY_START); goto do_action; } else { if ( ! yyg->yy_did_buffer_switch_on_eof ) YY_NEW_FILE; } break; } case EOB_ACT_CONTINUE_SCAN: yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text; yy_current_state = yy_get_previous_state( yyscanner ); yy_cp = yyg->yy_c_buf_p; yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; goto yy_match; case EOB_ACT_LAST_MATCH: yyg->yy_c_buf_p = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars]; yy_current_state = yy_get_previous_state( yyscanner ); yy_cp = yyg->yy_c_buf_p; yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; goto yy_find_action; } break; } default: YY_FATAL_ERROR( "fatal flex scanner internal error--no action found" ); } /* end of action switch */ } /* end of scanning one token */ } /* end of user's declarations */ } /* end of yylex */ /* yy_get_next_buffer - try to read in a new buffer * * Returns a code representing an action: * EOB_ACT_LAST_MATCH - * EOB_ACT_CONTINUE_SCAN - continue scanning from current position * EOB_ACT_END_OF_FILE - end of file */ static int yy_get_next_buffer (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; char *source = yyg->yytext_ptr; yy_size_t number_to_move, i; int ret_val; if ( yyg->yy_c_buf_p > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] ) YY_FATAL_ERROR( "fatal flex scanner internal error--end of buffer missed" ); if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) { /* Don't try to fill the buffer, so this is an EOF. */ if ( yyg->yy_c_buf_p - yyg->yytext_ptr - YY_MORE_ADJ == 1 ) { /* We matched a single character, the EOB, so * treat this as a final EOF. */ return EOB_ACT_END_OF_FILE; } else { /* We matched some text prior to the EOB, first * process it. */ return EOB_ACT_LAST_MATCH; } } /* Try to read more data. */ /* First move last chars to start of buffer. */ number_to_move = (yy_size_t) (yyg->yy_c_buf_p - yyg->yytext_ptr) - 1; for ( i = 0; i < number_to_move; ++i ) *(dest++) = *(source++); if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) /* don't do the read, it's not guaranteed to return an EOF, * just force an EOF */ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars = 0; else { int num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; while ( num_to_read <= 0 ) { /* Not enough room in the buffer - grow it. */ /* just a shorter name for the current buffer */ YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE; int yy_c_buf_p_offset = (int) (yyg->yy_c_buf_p - b->yy_ch_buf); if ( b->yy_is_our_buffer ) { int new_size = b->yy_buf_size * 2; if ( new_size <= 0 ) b->yy_buf_size += b->yy_buf_size / 8; else b->yy_buf_size *= 2; b->yy_ch_buf = (char *) /* Include room in for 2 EOB chars. */ yyrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 ,yyscanner ); } else /* Can't grow it, we don't own it. */ b->yy_ch_buf = NULL; if ( ! b->yy_ch_buf ) YY_FATAL_ERROR( "fatal error - scanner input buffer overflow" ); yyg->yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset]; num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; } if ( num_to_read > YY_READ_BUF_SIZE ) num_to_read = YY_READ_BUF_SIZE; /* Read in more data. */ YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), yyg->yy_n_chars, num_to_read ); YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; } if ( yyg->yy_n_chars == 0 ) { if ( number_to_move == YY_MORE_ADJ ) { ret_val = EOB_ACT_END_OF_FILE; yyrestart(yyin ,yyscanner); } else { ret_val = EOB_ACT_LAST_MATCH; YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_EOF_PENDING; } } else ret_val = EOB_ACT_CONTINUE_SCAN; if ((int) (yyg->yy_n_chars + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { /* Extend the array by 50%, plus the number we really need. */ int new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1); YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size ,yyscanner ); if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" ); } yyg->yy_n_chars += number_to_move; YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] = YY_END_OF_BUFFER_CHAR; YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR; yyg->yytext_ptr = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; return ret_val; } /* yy_get_previous_state - get the state just before the EOB char was reached */ static yy_state_type yy_get_previous_state (yyscan_t yyscanner) { yy_state_type yy_current_state; char *yy_cp; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yy_current_state = yyg->yy_start; for ( yy_cp = yyg->yytext_ptr + YY_MORE_ADJ; yy_cp < yyg->yy_c_buf_p; ++yy_cp ) { YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); if ( yy_accept[yy_current_state] ) { yyg->yy_last_accepting_state = yy_current_state; yyg->yy_last_accepting_cpos = yy_cp; } while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 51 ) yy_c = yy_meta[(unsigned int) yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + (flex_int16_t) yy_c]; } return yy_current_state; } /* yy_try_NUL_trans - try to make a transition on the NUL character * * synopsis * next_state = yy_try_NUL_trans( current_state ); */ static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state , yyscan_t yyscanner) { int yy_is_jam; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* This var may be unused depending upon options. */ char *yy_cp = yyg->yy_c_buf_p; YY_CHAR yy_c = 1; if ( yy_accept[yy_current_state] ) { yyg->yy_last_accepting_state = yy_current_state; yyg->yy_last_accepting_cpos = yy_cp; } while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 51 ) yy_c = yy_meta[(unsigned int) yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + (flex_int16_t) yy_c]; yy_is_jam = (yy_current_state == 50); (void)yyg; return yy_is_jam ? 0 : yy_current_state; } #ifndef YY_NO_UNPUT #endif #ifndef YY_NO_INPUT #ifdef __cplusplus static int yyinput (yyscan_t yyscanner) #else static int input (yyscan_t yyscanner) #endif { int c; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; *yyg->yy_c_buf_p = yyg->yy_hold_char; if ( *yyg->yy_c_buf_p == YY_END_OF_BUFFER_CHAR ) { /* yy_c_buf_p now points to the character we want to return. * If this occurs *before* the EOB characters, then it's a * valid NUL; if not, then we've hit the end of the buffer. */ if ( yyg->yy_c_buf_p < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) /* This was really a NUL. */ *yyg->yy_c_buf_p = '\0'; else { /* need more input */ int offset = yyg->yy_c_buf_p - yyg->yytext_ptr; ++yyg->yy_c_buf_p; switch ( yy_get_next_buffer( yyscanner ) ) { case EOB_ACT_LAST_MATCH: /* This happens because yy_g_n_b() * sees that we've accumulated a * token and flags that we need to * try matching the token before * proceeding. But for input(), * there's no matching to consider. * So convert the EOB_ACT_LAST_MATCH * to EOB_ACT_END_OF_FILE. */ /* Reset buffer status. */ yyrestart(yyin ,yyscanner); /*FALLTHROUGH*/ case EOB_ACT_END_OF_FILE: { if ( yywrap(yyscanner ) ) return 0; if ( ! yyg->yy_did_buffer_switch_on_eof ) YY_NEW_FILE; #ifdef __cplusplus return yyinput(yyscanner); #else return input(yyscanner); #endif } case EOB_ACT_CONTINUE_SCAN: yyg->yy_c_buf_p = yyg->yytext_ptr + offset; break; } } } c = *(unsigned char *) yyg->yy_c_buf_p; /* cast for 8-bit char's */ *yyg->yy_c_buf_p = '\0'; /* preserve yytext */ yyg->yy_hold_char = *++yyg->yy_c_buf_p; return c; } #endif /* ifndef YY_NO_INPUT */ /** Immediately switch to a different input stream. * @param input_file A readable stream. * @param yyscanner The scanner object. * @note This function does not reset the start condition to @c INITIAL . */ void yyrestart (FILE * input_file , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if ( ! YY_CURRENT_BUFFER ){ yyensure_buffer_stack (yyscanner); YY_CURRENT_BUFFER_LVALUE = yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); } yy_init_buffer(YY_CURRENT_BUFFER,input_file ,yyscanner); yy_load_buffer_state(yyscanner ); } /** Switch to a different input buffer. * @param new_buffer The new input buffer. * @param yyscanner The scanner object. */ void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* TODO. We should be able to replace this entire function body * with * yypop_buffer_state(); * yypush_buffer_state(new_buffer); */ yyensure_buffer_stack (yyscanner); if ( YY_CURRENT_BUFFER == new_buffer ) return; if ( YY_CURRENT_BUFFER ) { /* Flush out information for old buffer. */ *yyg->yy_c_buf_p = yyg->yy_hold_char; YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; } YY_CURRENT_BUFFER_LVALUE = new_buffer; yy_load_buffer_state(yyscanner ); /* We don't actually know whether we did this switch during * EOF (yywrap()) processing, but the only time this flag * is looked at is after yywrap() is called, so it's safe * to go ahead and always set it. */ yyg->yy_did_buffer_switch_on_eof = 1; } static void yy_load_buffer_state (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; yyg->yytext_ptr = yyg->yy_c_buf_p = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; yyg->yy_hold_char = *yyg->yy_c_buf_p; } /** Allocate and initialize an input buffer state. * @param file A readable stream. * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. * @param yyscanner The scanner object. * @return the allocated buffer state. */ YY_BUFFER_STATE yy_create_buffer (FILE * file, int size , yyscan_t yyscanner) { YY_BUFFER_STATE b; b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state ) ,yyscanner ); if ( ! b ) YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); b->yy_buf_size = (yy_size_t)size; /* yy_ch_buf has to be 2 characters longer than the size given because * we need to put in 2 end-of-buffer characters. */ b->yy_ch_buf = (char *) yyalloc(b->yy_buf_size + 2 ,yyscanner ); if ( ! b->yy_ch_buf ) YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); b->yy_is_our_buffer = 1; yy_init_buffer(b,file ,yyscanner); return b; } /** Destroy the buffer. * @param b a buffer created with yy_create_buffer() * @param yyscanner The scanner object. */ void yy_delete_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if ( ! b ) return; if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; if ( b->yy_is_our_buffer ) yyfree((void *) b->yy_ch_buf ,yyscanner ); yyfree((void *) b ,yyscanner ); } /* Initializes or reinitializes a buffer. * This function is sometimes called more than once on the same buffer, * such as during a yyrestart() or at EOF. */ static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file , yyscan_t yyscanner) { int oerrno = errno; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yy_flush_buffer(b ,yyscanner); b->yy_input_file = file; b->yy_fill_buffer = 1; /* If b is the current buffer, then yy_init_buffer was _probably_ * called from yyrestart() or through yy_get_next_buffer. * In that case, we don't want to reset the lineno or column. */ if (b != YY_CURRENT_BUFFER){ b->yy_bs_lineno = 1; b->yy_bs_column = 0; } b->yy_is_interactive = 0; errno = oerrno; } /** Discard all buffered characters. On the next scan, YY_INPUT will be called. * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. * @param yyscanner The scanner object. */ void yy_flush_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if ( ! b ) return; b->yy_n_chars = 0; /* We always need two end-of-buffer characters. The first causes * a transition to the end-of-buffer state. The second causes * a jam in that state. */ b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; b->yy_buf_pos = &b->yy_ch_buf[0]; b->yy_at_bol = 1; b->yy_buffer_status = YY_BUFFER_NEW; if ( b == YY_CURRENT_BUFFER ) yy_load_buffer_state(yyscanner ); } /** Pushes the new state onto the stack. The new state becomes * the current state. This function will allocate the stack * if necessary. * @param new_buffer The new state. * @param yyscanner The scanner object. */ void yypush_buffer_state (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (new_buffer == NULL) return; yyensure_buffer_stack(yyscanner); /* This block is copied from yy_switch_to_buffer. */ if ( YY_CURRENT_BUFFER ) { /* Flush out information for old buffer. */ *yyg->yy_c_buf_p = yyg->yy_hold_char; YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; } /* Only push if top exists. Otherwise, replace top. */ if (YY_CURRENT_BUFFER) yyg->yy_buffer_stack_top++; YY_CURRENT_BUFFER_LVALUE = new_buffer; /* copied from yy_switch_to_buffer. */ yy_load_buffer_state(yyscanner ); yyg->yy_did_buffer_switch_on_eof = 1; } /** Removes and deletes the top of the stack, if present. * The next element becomes the new top. * @param yyscanner The scanner object. */ void yypop_buffer_state (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (!YY_CURRENT_BUFFER) return; yy_delete_buffer(YY_CURRENT_BUFFER ,yyscanner); YY_CURRENT_BUFFER_LVALUE = NULL; if (yyg->yy_buffer_stack_top > 0) --yyg->yy_buffer_stack_top; if (YY_CURRENT_BUFFER) { yy_load_buffer_state(yyscanner ); yyg->yy_did_buffer_switch_on_eof = 1; } } /* Allocates the stack if it does not exist. * Guarantees space for at least one push. */ static void yyensure_buffer_stack (yyscan_t yyscanner) { int num_to_alloc; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (!yyg->yy_buffer_stack) { /* First allocation is just for 2 elements, since we don't know if this * scanner will even need a stack. We use 2 instead of 1 to avoid an * immediate realloc on the next call. */ num_to_alloc = 1; /* After all that talk, this was set to 1 anyways... */ yyg->yy_buffer_stack = (struct yy_buffer_state**)yyalloc (num_to_alloc * sizeof(struct yy_buffer_state*) , yyscanner); if ( ! yyg->yy_buffer_stack ) YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); memset(yyg->yy_buffer_stack, 0, num_to_alloc * sizeof(struct yy_buffer_state*)); yyg->yy_buffer_stack_max = num_to_alloc; yyg->yy_buffer_stack_top = 0; return; } if (yyg->yy_buffer_stack_top >= (yyg->yy_buffer_stack_max) - 1){ /* Increase the buffer to prepare for a possible push. */ yy_size_t grow_size = 8 /* arbitrary grow size */; num_to_alloc = yyg->yy_buffer_stack_max + grow_size; yyg->yy_buffer_stack = (struct yy_buffer_state**)yyrealloc (yyg->yy_buffer_stack, num_to_alloc * sizeof(struct yy_buffer_state*) , yyscanner); if ( ! yyg->yy_buffer_stack ) YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); /* zero only the new slots.*/ memset(yyg->yy_buffer_stack + yyg->yy_buffer_stack_max, 0, grow_size * sizeof(struct yy_buffer_state*)); yyg->yy_buffer_stack_max = num_to_alloc; } } /** Setup the input buffer state to scan directly from a user-specified character buffer. * @param base the character buffer * @param size the size in bytes of the character buffer * @param yyscanner The scanner object. * @return the newly allocated buffer state object. */ YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size , yyscan_t yyscanner) { YY_BUFFER_STATE b; if ( size < 2 || base[size-2] != YY_END_OF_BUFFER_CHAR || base[size-1] != YY_END_OF_BUFFER_CHAR ) /* They forgot to leave room for the EOB's. */ return NULL; b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state ) ,yyscanner ); if ( ! b ) YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" ); b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */ b->yy_buf_pos = b->yy_ch_buf = base; b->yy_is_our_buffer = 0; b->yy_input_file = NULL; b->yy_n_chars = b->yy_buf_size; b->yy_is_interactive = 0; b->yy_at_bol = 1; b->yy_fill_buffer = 0; b->yy_buffer_status = YY_BUFFER_NEW; yy_switch_to_buffer(b ,yyscanner ); return b; } /** Setup the input buffer state to scan a string. The next call to yylex() will * scan from a @e copy of @a str. * @param yystr a NUL-terminated string to scan * @param yyscanner The scanner object. * @return the newly allocated buffer state object. * @note If you want to scan bytes that may contain NUL values, then use * yy_scan_bytes() instead. */ YY_BUFFER_STATE yy_scan_string (yyconst char * yystr , yyscan_t yyscanner) { return yy_scan_bytes(yystr,(int) strlen(yystr) ,yyscanner); } /** Setup the input buffer state to scan the given bytes. The next call to yylex() will * scan from a @e copy of @a bytes. * @param yybytes the byte buffer to scan * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes. * @param yyscanner The scanner object. * @return the newly allocated buffer state object. */ YY_BUFFER_STATE yy_scan_bytes (yyconst char * yybytes, int _yybytes_len , yyscan_t yyscanner) { YY_BUFFER_STATE b; char *buf; yy_size_t n; yy_size_t i; /* Get memory for full buffer, including space for trailing EOB's. */ n = (yy_size_t) _yybytes_len + 2; buf = (char *) yyalloc(n ,yyscanner ); if ( ! buf ) YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" ); for ( i = 0; i < _yybytes_len; ++i ) buf[i] = yybytes[i]; buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; b = yy_scan_buffer(buf,n ,yyscanner); if ( ! b ) YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" ); /* It's okay to grow etc. this buffer, and we should throw it * away when we're done. */ b->yy_is_our_buffer = 1; return b; } #ifndef YY_EXIT_FAILURE #define YY_EXIT_FAILURE 2 #endif static void yynoreturn yy_fatal_error (yyconst char* msg , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; (void)yyg; (void) fprintf( stderr, "%s\n", msg ); exit( YY_EXIT_FAILURE ); } /* Redefine yyless() so it works in section 3 code. */ #undef yyless #define yyless(n) \ do \ { \ /* Undo effects of setting up yytext. */ \ int yyless_macro_arg = (n); \ YY_LESS_LINENO(yyless_macro_arg);\ yytext[yyleng] = yyg->yy_hold_char; \ yyg->yy_c_buf_p = yytext + yyless_macro_arg; \ yyg->yy_hold_char = *yyg->yy_c_buf_p; \ *yyg->yy_c_buf_p = '\0'; \ yyleng = yyless_macro_arg; \ } \ while ( 0 ) /* Accessor methods (get/set functions) to struct members. */ /** Get the user-defined data for this scanner. * @param yyscanner The scanner object. */ YY_EXTRA_TYPE yyget_extra (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yyextra; } /** Get the current line number. * @param yyscanner The scanner object. */ int yyget_lineno (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (! YY_CURRENT_BUFFER) return 0; return yylineno; } /** Get the current column number. * @param yyscanner The scanner object. */ int yyget_column (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (! YY_CURRENT_BUFFER) return 0; return yycolumn; } /** Get the input stream. * @param yyscanner The scanner object. */ FILE *yyget_in (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yyin; } /** Get the output stream. * @param yyscanner The scanner object. */ FILE *yyget_out (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yyout; } /** Get the length of the current token. * @param yyscanner The scanner object. */ int yyget_leng (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yyleng; } /** Get the current token. * @param yyscanner The scanner object. */ char *yyget_text (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yytext; } /** Set the user-defined data. This data is never touched by the scanner. * @param user_defined The data to be associated with this scanner. * @param yyscanner The scanner object. */ void yyset_extra (YY_EXTRA_TYPE user_defined , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yyextra = user_defined ; } /** Set the current line number. * @param _line_number line number * @param yyscanner The scanner object. */ void yyset_lineno (int _line_number , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* lineno is only valid if an input buffer exists. */ if (! YY_CURRENT_BUFFER ) YY_FATAL_ERROR( "yyset_lineno called with no buffer" ); yylineno = _line_number; } /** Set the current column. * @param _column_no column number * @param yyscanner The scanner object. */ void yyset_column (int _column_no , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* column is only valid if an input buffer exists. */ if (! YY_CURRENT_BUFFER ) YY_FATAL_ERROR( "yyset_column called with no buffer" ); yycolumn = _column_no; } /** Set the input stream. This does not discard the current * input buffer. * @param _in_str A readable stream. * @param yyscanner The scanner object. * @see yy_switch_to_buffer */ void yyset_in (FILE * _in_str , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yyin = _in_str ; } void yyset_out (FILE * _out_str , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yyout = _out_str ; } int yyget_debug (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yy_flex_debug; } void yyset_debug (int _bdebug , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yy_flex_debug = _bdebug ; } /* Accessor methods for yylval and yylloc */ YYSTYPE * yyget_lval (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yylval; } void yyset_lval (YYSTYPE * yylval_param , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yylval = yylval_param; } /* User-visible API */ /* yylex_init is special because it creates the scanner itself, so it is * the ONLY reentrant function that doesn't take the scanner as the last argument. * That's why we explicitly handle the declaration, instead of using our macros. */ int yylex_init(yyscan_t* ptr_yy_globals) { if (ptr_yy_globals == NULL){ errno = EINVAL; return 1; } *ptr_yy_globals = (yyscan_t) yyalloc ( sizeof( struct yyguts_t ), NULL ); if (*ptr_yy_globals == NULL){ errno = ENOMEM; return 1; } /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */ memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t)); return yy_init_globals ( *ptr_yy_globals ); } /* yylex_init_extra has the same functionality as yylex_init, but follows the * convention of taking the scanner as the last argument. Note however, that * this is a *pointer* to a scanner, as it will be allocated by this call (and * is the reason, too, why this function also must handle its own declaration). * The user defined value in the first argument will be available to yyalloc in * the yyextra field. */ int yylex_init_extra(YY_EXTRA_TYPE yy_user_defined,yyscan_t* ptr_yy_globals ) { struct yyguts_t dummy_yyguts; yyset_extra (yy_user_defined, &dummy_yyguts); if (ptr_yy_globals == NULL){ errno = EINVAL; return 1; } *ptr_yy_globals = (yyscan_t) yyalloc ( sizeof( struct yyguts_t ), &dummy_yyguts ); if (*ptr_yy_globals == NULL){ errno = ENOMEM; return 1; } /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */ memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t)); yyset_extra (yy_user_defined, *ptr_yy_globals); return yy_init_globals ( *ptr_yy_globals ); } static int yy_init_globals (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* Initialization is the same as for the non-reentrant scanner. * This function is called from yylex_destroy(), so don't allocate here. */ yyg->yy_buffer_stack = NULL; yyg->yy_buffer_stack_top = 0; yyg->yy_buffer_stack_max = 0; yyg->yy_c_buf_p = NULL; yyg->yy_init = 0; yyg->yy_start = 0; yyg->yy_start_stack_ptr = 0; yyg->yy_start_stack_depth = 0; yyg->yy_start_stack = NULL; /* Defined in main.c */ #ifdef YY_STDINIT yyin = stdin; yyout = stdout; #else yyin = NULL; yyout = NULL; #endif /* For future reference: Set errno on error, since we are called by * yylex_init() */ return 0; } /* yylex_destroy is for both reentrant and non-reentrant scanners. */ int yylex_destroy (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* Pop the buffer stack, destroying each element. */ while(YY_CURRENT_BUFFER){ yy_delete_buffer(YY_CURRENT_BUFFER ,yyscanner ); YY_CURRENT_BUFFER_LVALUE = NULL; yypop_buffer_state(yyscanner); } /* Destroy the stack itself. */ yyfree(yyg->yy_buffer_stack ,yyscanner); yyg->yy_buffer_stack = NULL; /* Destroy the start condition stack. */ yyfree(yyg->yy_start_stack ,yyscanner ); yyg->yy_start_stack = NULL; /* Reset the globals. This is important in a non-reentrant scanner so the next time * yylex() is called, initialization will occur. */ yy_init_globals( yyscanner); /* Destroy the main struct (reentrant only). */ yyfree ( yyscanner , yyscanner ); yyscanner = NULL; return 0; } /* * Internal utility routines. */ #ifndef yytext_ptr static void yy_flex_strncpy (char* s1, yyconst char * s2, int n , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; (void)yyg; int i; for ( i = 0; i < n; ++i ) s1[i] = s2[i]; } #endif #ifdef YY_NEED_STRLEN static int yy_flex_strlen (yyconst char * s , yyscan_t yyscanner) { int n; for ( n = 0; s[n]; ++n ) ; return n; } #endif void *yyalloc (yy_size_t size , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; (void)yyg; return malloc(size); } void *yyrealloc (void * ptr, yy_size_t size , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; (void)yyg; /* The cast to (char *) in the following accommodates both * implementations that use char* generic pointers, and those * that use void* generic pointers. It works with the latter * because both ANSI C and C++ allow castless assignment from * any pointer type to void*, and deal with argument conversions * as though doing an assignment. */ return realloc(ptr, size); } void yyfree (void * ptr , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; (void)yyg; free( (char *) ptr ); /* see yyrealloc() for (char *) cast */ } #define YYTABLES_NAME "yytables" #line 88 "surveytargetexpressionlexer.l" void yyerror(char const* s) { printf("ERROR: %s\n", s); } gammaray-2.11.3/3rdparty/kuserfeedback/common/surveytargetexpressionlexer.l000066400000000000000000000050401413310443000273230ustar00rootroot00000000000000%{ /* Copyright (C) 2017 Volker Krause Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "surveytargetexpression.h" #include "surveytargetexpressionparser_p.h" extern char* unquoteString(const char *str); %} %option warn nodefault %option reentrant %option noyywrap %option nounput %option never-interactive %option bison-bridge SPACE [ \t\r\n]+ DOUBLE [-+]?([0-9]+[.][0-9]*|[0-9]*[.][0-9]+)([eE][-+]?[0-9]+)? INTEGER [-+]?[0-9]+ STRING_ELEMENT [^\\"]|"\\\""|"\\\\"|"\\n"|"\\t" STRING ["]{STRING_ELEMENT}*["] INITIAL [[:alpha:]]|"_" SUBSEQUENT {INITIAL}|[0-9] IDENTIFIER {INITIAL}{SUBSEQUENT}* %% {SPACE} {} "(" { return T_LPAREN; } ")" { return T_RPAREN; } "\." { return T_DOT; } "[" { return T_LBRACKET; } "]" { return T_RBRACKET; } {DOUBLE} { yylval->doubleVal = strtod(yytext, nullptr); return T_DOUBLE; } {INTEGER} { yylval->intVal = atoi(yytext); return T_INTEGER; } {STRING} { yylval->str = unquoteString(yytext); return T_STRING; } "true" { yylval->boolVal = true; return T_BOOLEAN; } "false" { yylval->boolVal = false; return T_BOOLEAN; } {IDENTIFIER} { yylval->str = strdup(yytext); return T_IDENTIFIER; } "&&" { return T_OP_AND; } "||" { return T_OP_OR; } "==" { return T_OP_EQ; } "!=" { return T_OP_NEQ; } ">" { return T_OP_GT; } ">=" { return T_OP_GE; } "<" { return T_OP_LT; } "<=" { return T_OP_LE; } . { printf("unexpected character: %s\n", yytext); } %% void yyerror(char const* s) { printf("ERROR: %s\n", s); } gammaray-2.11.3/3rdparty/kuserfeedback/common/surveytargetexpressionparser.cpp000066400000000000000000000060031413310443000300270ustar00rootroot00000000000000/* Copyright (C) 2017 Volker Krause Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include "surveytargetexpressionparser.h" #include "surveytargetexpression.h" #ifdef HAVE_SURVEY_TARGET_EXPRESSIONS #include "surveytargetexpressionparser_p.h" #include "surveytargetexpressionscanner.h" #endif #include #include char* unquoteString(const char *str) { const auto size = strlen(str) - 2; if (size <= 0) return nullptr; auto out = (char*)malloc(size + 1); memset(out, 0, size + 1); auto outIt = out; for (auto it = str + 1; it < str + size + 1; ++it, ++outIt) { if (*it == '\\') { ++it; switch (*it) { case '\\': case '"': *outIt = *it; break; case 'n': *outIt = '\n'; break; case 't': *outIt = '\t'; break; default: *outIt++ = '\\'; *outIt = *it; } } else { *outIt = *it; } } return out; } using namespace KUserFeedback; SurveyTargetExpressionParser::SurveyTargetExpressionParser() : m_expression(nullptr) { } SurveyTargetExpressionParser::~SurveyTargetExpressionParser() { delete m_expression; } bool SurveyTargetExpressionParser::parse(const QString &s) { delete m_expression; m_expression = nullptr; #ifdef HAVE_SURVEY_TARGET_EXPRESSIONS yyscan_t scanner; YY_BUFFER_STATE state; if (yylex_init(&scanner)) return false; QByteArray b = s.toUtf8(); state = yy_scan_string(b.constData(), scanner); if (yyparse(&m_expression, scanner)) { delete m_expression; m_expression = nullptr; } yy_delete_buffer(state, scanner); yylex_destroy(scanner); #endif return m_expression; } SurveyTargetExpression* SurveyTargetExpressionParser::expression() const { return m_expression; } gammaray-2.11.3/3rdparty/kuserfeedback/common/surveytargetexpressionparser.h000066400000000000000000000033311413310443000274750ustar00rootroot00000000000000/* Copyright (C) 2017 Volker Krause Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef KUSERFEEDBACK_SURVEYTARGETEXPRESSIONPARSER_H #define KUSERFEEDBACK_SURVEYTARGETEXPRESSIONPARSER_H #include QT_BEGIN_NAMESPACE class QString; QT_END_NAMESPACE namespace KUserFeedback { class SurveyTargetExpression; class SurveyTargetExpressionParser { public: SurveyTargetExpressionParser(); ~SurveyTargetExpressionParser(); bool parse(const QString &s); SurveyTargetExpression *expression() const; private: Q_DISABLE_COPY(SurveyTargetExpressionParser) SurveyTargetExpression *m_expression; }; } #endif // KUSERFEEDBACK_SURVEYTARGETEXPRESSIONPARSER_H gammaray-2.11.3/3rdparty/kuserfeedback/common/surveytargetexpressionparser.y000066400000000000000000000104521413310443000275200ustar00rootroot00000000000000%{ /* Copyright (C) 2017 Volker Krause Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "surveytargetexpression.h" #include "surveytargetexpressionparser_p.h" #include "surveytargetexpressionscanner.h" #include using namespace KUserFeedback; void yyerror(KUserFeedback::SurveyTargetExpression **expression, yyscan_t scanner, char const* msg) { Q_UNUSED(expression); Q_UNUSED(scanner); printf("PARSER ERROR: %s\n", msg); } %} %code requires { #ifndef YY_TYPEDEF_YY_SCANNER_T #define YY_TYPEDEF_YY_SCANNER_T typedef void* yyscan_t; #endif } %define api.pure %lex-param { yyscan_t scanner } %parse-param { KUserFeedback::SurveyTargetExpression **expression } %parse-param { yyscan_t scanner } %union { int intVal; double doubleVal; bool boolVal; char *str; KUserFeedback::SurveyTargetExpression *expression; } %left T_OP_OR %left T_OP_AND %token T_OP_EQ %token T_OP_NEQ %token T_OP_GT %token T_OP_GE %token T_OP_LT %token T_OP_LE %token T_DOT %token T_LPAREN %token T_RPAREN %token T_LBRACKET %token T_RBRACKET %token T_DOUBLE %token T_INTEGER %token T_BOOLEAN %token T_STRING %token T_IDENTIFIER %type Expr %type Term %type Value %destructor { free($$); } %destructor { delete $$; } %% Input: Expr { *expression = $1; } ; Expr: Term { printf("TERM "); $$ = $1; } | T_LPAREN Expr[E] T_RPAREN { $$ = $E; } | Expr[L] T_OP_AND Expr[R] %prec T_OP_AND { $$ = new SurveyTargetExpression(SurveyTargetExpression::OpLogicAnd, $L, $R); } | Expr[L] T_OP_OR Expr[R] %prec T_OP_OR { $$ = new SurveyTargetExpression(SurveyTargetExpression::OpLogicOr, $L, $R); } ; Term: Value[L] T_OP_EQ Value[R] { $$ = new SurveyTargetExpression(SurveyTargetExpression::OpEqual, $L, $R); } | Value[L] T_OP_NEQ Value[R] { $$ = new SurveyTargetExpression(SurveyTargetExpression::OpNotEqual, $L, $R); } | Value[L] T_OP_GT Value[R] { $$ = new SurveyTargetExpression(SurveyTargetExpression::OpGreater, $L, $R); } | Value[L] T_OP_GE Value[R] { $$ = new SurveyTargetExpression(SurveyTargetExpression::OpGreaterEqual, $L, $R); } | Value[L] T_OP_LT Value[R] { $$ = new SurveyTargetExpression(SurveyTargetExpression::OpLess, $L, $R); } | Value[L] T_OP_LE Value[R] { $$ = new SurveyTargetExpression(SurveyTargetExpression::OpLessEqual, $L, $R); } ; Value: T_IDENTIFIER[S] T_DOT T_IDENTIFIER[E] { $$ = new SurveyTargetExpression(QString::fromUtf8($S), QVariant(), QString::fromUtf8($E)); free($S); free($E); } | T_IDENTIFIER[S] T_LBRACKET T_INTEGER[I] T_RBRACKET T_DOT T_IDENTIFIER[E] { $$ = new SurveyTargetExpression(QString::fromUtf8($S), $I, QString::fromUtf8($E)); free($S); free($E); } | T_IDENTIFIER[S] T_LBRACKET T_STRING[K] T_RBRACKET T_DOT T_IDENTIFIER[E] { $$ = new SurveyTargetExpression(QString::fromUtf8($S), QString::fromUtf8($K), QString::fromUtf8($E)); free($S); free($K); free($E); } | T_DOUBLE { $$ = new SurveyTargetExpression($1); } | T_INTEGER { $$ = new SurveyTargetExpression($1); } | T_BOOLEAN { $$ = new SurveyTargetExpression($1); } | T_STRING { $$ = new SurveyTargetExpression(QString::fromUtf8($1)); free($1); } ; %% gammaray-2.11.3/3rdparty/kuserfeedback/common/surveytargetexpressionparser_p.cpp000066400000000000000000001507121413310443000303550ustar00rootroot00000000000000/* A Bison parser, made by GNU Bison 3.4.1. */ /* Bison implementation for Yacc-like parsers in C Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2019 Free Software Foundation, Inc. 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 3 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, see . */ /* As a special exception, you may create a larger work that contains part or all of the Bison parser skeleton and distribute that work under terms of your choice, so long as that work isn't itself a parser generator using the skeleton or a modified version thereof as a parser skeleton. Alternatively, if you modify or redistribute the parser skeleton itself, you may (at your option) remove this special exception, which will cause the skeleton and the resulting Bison output files to be licensed under the GNU General Public License without this special exception. This special exception was added by the Free Software Foundation in version 2.2 of Bison. */ /* C LALR(1) parser skeleton written by Richard Stallman, by simplifying the original so-called "semantic" parser. */ /* All symbols defined below should begin with yy or YY, to avoid infringing on user name space. This should be done even for local variables, as they might otherwise be expanded by user macros. There are some unavoidable exceptions within include files to define necessary library symbols; they are noted "INFRINGES ON USER NAME SPACE" below. */ /* Undocumented macros, especially those whose name start with YY_, are private implementation details. Do not rely on them. */ /* Identify Bison output. */ #define YYBISON 1 /* Bison version. */ #define YYBISON_VERSION "3.4.1" /* Skeleton name. */ #define YYSKELETON_NAME "yacc.c" /* Pure parsers. */ #define YYPURE 1 /* Push parsers. */ #define YYPUSH 0 /* Pull parsers. */ #define YYPULL 1 /* First part of user prologue. */ #line 1 "surveytargetexpressionparser.y" /* Copyright (C) 2017 Volker Krause Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "surveytargetexpression.h" #include "surveytargetexpressionparser_p.h" #include "surveytargetexpressionscanner.h" #include using namespace KUserFeedback; void yyerror(KUserFeedback::SurveyTargetExpression **expression, yyscan_t scanner, char const* msg) { Q_UNUSED(expression); Q_UNUSED(scanner); printf("PARSER ERROR: %s\n", msg); } #line 111 "surveytargetexpressionparser_p.cpp" # ifndef YY_NULLPTR # if defined __cplusplus # if 201103L <= __cplusplus # define YY_NULLPTR nullptr # else # define YY_NULLPTR 0 # endif # else # define YY_NULLPTR ((void*)0) # endif # endif /* Enabling verbose error messages. */ #ifdef YYERROR_VERBOSE # undef YYERROR_VERBOSE # define YYERROR_VERBOSE 1 #else # define YYERROR_VERBOSE 0 #endif /* Use api.header.include to #include this header instead of duplicating it here. */ #ifndef YY_YY_SURVEYTARGETEXPRESSIONPARSER_P_H_INCLUDED # define YY_YY_SURVEYTARGETEXPRESSIONPARSER_P_H_INCLUDED /* Debug traces. */ #ifndef YYDEBUG # define YYDEBUG 0 #endif #if YYDEBUG extern int yydebug; #endif /* "%code requires" blocks. */ #line 42 "surveytargetexpressionparser.y" #ifndef YY_TYPEDEF_YY_SCANNER_T #define YY_TYPEDEF_YY_SCANNER_T typedef void* yyscan_t; #endif #line 154 "surveytargetexpressionparser_p.cpp" /* Token type. */ #ifndef YYTOKENTYPE # define YYTOKENTYPE enum yytokentype { T_OP_OR = 258, T_OP_AND = 259, T_OP_EQ = 260, T_OP_NEQ = 261, T_OP_GT = 262, T_OP_GE = 263, T_OP_LT = 264, T_OP_LE = 265, T_DOT = 266, T_LPAREN = 267, T_RPAREN = 268, T_LBRACKET = 269, T_RBRACKET = 270, T_DOUBLE = 271, T_INTEGER = 272, T_BOOLEAN = 273, T_STRING = 274, T_IDENTIFIER = 275 }; #endif /* Value type. */ #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED union YYSTYPE { #line 56 "surveytargetexpressionparser.y" int intVal; double doubleVal; bool boolVal; char *str; KUserFeedback::SurveyTargetExpression *expression; #line 194 "surveytargetexpressionparser_p.cpp" }; typedef union YYSTYPE YYSTYPE; # define YYSTYPE_IS_TRIVIAL 1 # define YYSTYPE_IS_DECLARED 1 #endif int yyparse (KUserFeedback::SurveyTargetExpression **expression, yyscan_t scanner); #endif /* !YY_YY_SURVEYTARGETEXPRESSIONPARSER_P_H_INCLUDED */ #ifdef short # undef short #endif #ifdef YYTYPE_UINT8 typedef YYTYPE_UINT8 yytype_uint8; #else typedef unsigned char yytype_uint8; #endif #ifdef YYTYPE_INT8 typedef YYTYPE_INT8 yytype_int8; #else typedef signed char yytype_int8; #endif #ifdef YYTYPE_UINT16 typedef YYTYPE_UINT16 yytype_uint16; #else typedef unsigned short yytype_uint16; #endif #ifdef YYTYPE_INT16 typedef YYTYPE_INT16 yytype_int16; #else typedef short yytype_int16; #endif #ifndef YYSIZE_T # ifdef __SIZE_TYPE__ # define YYSIZE_T __SIZE_TYPE__ # elif defined size_t # define YYSIZE_T size_t # elif ! defined YYSIZE_T # include /* INFRINGES ON USER NAME SPACE */ # define YYSIZE_T size_t # else # define YYSIZE_T unsigned # endif #endif #define YYSIZE_MAXIMUM ((YYSIZE_T) -1) #ifndef YY_ # if defined YYENABLE_NLS && YYENABLE_NLS # if ENABLE_NLS # include /* INFRINGES ON USER NAME SPACE */ # define YY_(Msgid) dgettext ("bison-runtime", Msgid) # endif # endif # ifndef YY_ # define YY_(Msgid) Msgid # endif #endif #ifndef YY_ATTRIBUTE # if (defined __GNUC__ \ && (2 < __GNUC__ || (__GNUC__ == 2 && 96 <= __GNUC_MINOR__))) \ || defined __SUNPRO_C && 0x5110 <= __SUNPRO_C # define YY_ATTRIBUTE(Spec) __attribute__(Spec) # else # define YY_ATTRIBUTE(Spec) /* empty */ # endif #endif #ifndef YY_ATTRIBUTE_PURE # define YY_ATTRIBUTE_PURE YY_ATTRIBUTE ((__pure__)) #endif #ifndef YY_ATTRIBUTE_UNUSED # define YY_ATTRIBUTE_UNUSED YY_ATTRIBUTE ((__unused__)) #endif /* Suppress unused-variable warnings by "using" E. */ #if ! defined lint || defined __GNUC__ # define YYUSE(E) ((void) (E)) #else # define YYUSE(E) /* empty */ #endif #if defined __GNUC__ && ! defined __ICC && 407 <= __GNUC__ * 100 + __GNUC_MINOR__ /* Suppress an incorrect diagnostic about yylval being uninitialized. */ # define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ _Pragma ("GCC diagnostic push") \ _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"")\ _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") # define YY_IGNORE_MAYBE_UNINITIALIZED_END \ _Pragma ("GCC diagnostic pop") #else # define YY_INITIAL_VALUE(Value) Value #endif #ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN # define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN # define YY_IGNORE_MAYBE_UNINITIALIZED_END #endif #ifndef YY_INITIAL_VALUE # define YY_INITIAL_VALUE(Value) /* Nothing. */ #endif #define YY_ASSERT(E) ((void) (0 && (E))) #if ! defined yyoverflow || YYERROR_VERBOSE /* The parser invokes alloca or malloc; define the necessary symbols. */ # ifdef YYSTACK_USE_ALLOCA # if YYSTACK_USE_ALLOCA # ifdef __GNUC__ # define YYSTACK_ALLOC __builtin_alloca # elif defined __BUILTIN_VA_ARG_INCR # include /* INFRINGES ON USER NAME SPACE */ # elif defined _AIX # define YYSTACK_ALLOC __alloca # elif defined _MSC_VER # include /* INFRINGES ON USER NAME SPACE */ # define alloca _alloca # else # define YYSTACK_ALLOC alloca # if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS # include /* INFRINGES ON USER NAME SPACE */ /* Use EXIT_SUCCESS as a witness for stdlib.h. */ # ifndef EXIT_SUCCESS # define EXIT_SUCCESS 0 # endif # endif # endif # endif # endif # ifdef YYSTACK_ALLOC /* Pacify GCC's 'empty if-body' warning. */ # define YYSTACK_FREE(Ptr) do { /* empty */; } while (0) # ifndef YYSTACK_ALLOC_MAXIMUM /* The OS might guarantee only one guard page at the bottom of the stack, and a page size can be as small as 4096 bytes. So we cannot safely invoke alloca (N) if N exceeds 4096. Use a slightly smaller number to allow for a few compiler-allocated temporary stack slots. */ # define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ # endif # else # define YYSTACK_ALLOC YYMALLOC # define YYSTACK_FREE YYFREE # ifndef YYSTACK_ALLOC_MAXIMUM # define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM # endif # if (defined __cplusplus && ! defined EXIT_SUCCESS \ && ! ((defined YYMALLOC || defined malloc) \ && (defined YYFREE || defined free))) # include /* INFRINGES ON USER NAME SPACE */ # ifndef EXIT_SUCCESS # define EXIT_SUCCESS 0 # endif # endif # ifndef YYMALLOC # define YYMALLOC malloc # if ! defined malloc && ! defined EXIT_SUCCESS void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ # endif # endif # ifndef YYFREE # define YYFREE free # if ! defined free && ! defined EXIT_SUCCESS void free (void *); /* INFRINGES ON USER NAME SPACE */ # endif # endif # endif #endif /* ! defined yyoverflow || YYERROR_VERBOSE */ #if (! defined yyoverflow \ && (! defined __cplusplus \ || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) /* A type that is properly aligned for any stack member. */ union yyalloc { yytype_int16 yyss_alloc; YYSTYPE yyvs_alloc; }; /* The size of the maximum gap between one aligned stack and the next. */ # define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) /* The size of an array large to enough to hold all stacks, each with N elements. */ # define YYSTACK_BYTES(N) \ ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \ + YYSTACK_GAP_MAXIMUM) # define YYCOPY_NEEDED 1 /* Relocate STACK from its old location to the new one. The local variables YYSIZE and YYSTACKSIZE give the old and new number of elements in the stack, and YYPTR gives the new location of the stack. Advance YYPTR to a properly aligned location for the next stack. */ # define YYSTACK_RELOCATE(Stack_alloc, Stack) \ do \ { \ YYSIZE_T yynewbytes; \ YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ Stack = &yyptr->Stack_alloc; \ yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ yyptr += yynewbytes / sizeof (*yyptr); \ } \ while (0) #endif #if defined YYCOPY_NEEDED && YYCOPY_NEEDED /* Copy COUNT objects from SRC to DST. The source and destination do not overlap. */ # ifndef YYCOPY # if defined __GNUC__ && 1 < __GNUC__ # define YYCOPY(Dst, Src, Count) \ __builtin_memcpy (Dst, Src, (Count) * sizeof (*(Src))) # else # define YYCOPY(Dst, Src, Count) \ do \ { \ YYSIZE_T yyi; \ for (yyi = 0; yyi < (Count); yyi++) \ (Dst)[yyi] = (Src)[yyi]; \ } \ while (0) # endif # endif #endif /* !YYCOPY_NEEDED */ /* YYFINAL -- State number of the termination state. */ #define YYFINAL 14 /* YYLAST -- Last index in YYTABLE. */ #define YYLAST 43 /* YYNTOKENS -- Number of terminals. */ #define YYNTOKENS 21 /* YYNNTS -- Number of nonterminals. */ #define YYNNTS 5 /* YYNRULES -- Number of rules. */ #define YYNRULES 19 /* YYNSTATES -- Number of states. */ #define YYNSTATES 41 #define YYUNDEFTOK 2 #define YYMAXUTOK 275 /* YYTRANSLATE(TOKEN-NUM) -- Symbol number corresponding to TOKEN-NUM as returned by yylex, with out-of-bounds checking. */ #define YYTRANSLATE(YYX) \ ((unsigned) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) /* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM as returned by yylex. */ static const yytype_uint8 yytranslate[] = { 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 }; #if YYDEBUG /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ static const yytype_uint8 yyrline[] = { 0, 96, 96, 99, 100, 101, 102, 105, 106, 107, 108, 109, 110, 113, 118, 123, 129, 130, 131, 132 }; #endif #if YYDEBUG || YYERROR_VERBOSE || 0 /* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. First, the terminals, then, starting at YYNTOKENS, nonterminals. */ static const char *const yytname[] = { "$end", "error", "$undefined", "T_OP_OR", "T_OP_AND", "T_OP_EQ", "T_OP_NEQ", "T_OP_GT", "T_OP_GE", "T_OP_LT", "T_OP_LE", "T_DOT", "T_LPAREN", "T_RPAREN", "T_LBRACKET", "T_RBRACKET", "T_DOUBLE", "T_INTEGER", "T_BOOLEAN", "T_STRING", "T_IDENTIFIER", "$accept", "Input", "Expr", "Term", "Value", YY_NULLPTR }; #endif # ifdef YYPRINT /* YYTOKNUM[NUM] -- (External) token number corresponding to the (internal) symbol number NUM (which must be that of a token). */ static const yytype_uint16 yytoknum[] = { 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275 }; # endif #define YYPACT_NINF -15 #define yypact_value_is_default(Yystate) \ (!!((Yystate) == (-15))) #define YYTABLE_NINF -1 #define yytable_value_is_error(Yytable_value) \ 0 /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing STATE-NUM. */ static const yytype_int8 yypact[] = { 0, 0, -15, -15, -15, -15, 21, 13, 6, -15, 16, -2, 18, 17, -15, 0, 0, 11, 11, 11, 11, 11, 11, -15, -15, 22, 24, 29, -15, -15, -15, -15, -15, -15, -15, 30, 31, 20, 23, -15, -15 }; /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. Performed when YYTABLE does not specify something else to do. Zero means the default is an error. */ static const yytype_uint8 yydefact[] = { 0, 0, 16, 17, 18, 19, 0, 0, 2, 3, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 4, 13, 0, 0, 6, 5, 7, 8, 9, 10, 11, 12, 0, 0, 0, 0, 14, 15 }; /* YYPGOTO[NTERM-NUM]. */ static const yytype_int8 yypgoto[] = { -15, -15, -1, -15, -14 }; /* YYDEFGOTO[NTERM-NUM]. */ static const yytype_int8 yydefgoto[] = { -1, 7, 8, 9, 10 }; /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If positive, shift that token. If negative, reduce the rule whose number is the opposite. If YYTABLE_NINF, syntax error. */ static const yytype_uint8 yytable[] = { 11, 15, 16, 29, 30, 31, 32, 33, 34, 15, 16, 23, 1, 14, 27, 28, 2, 3, 4, 5, 6, 17, 18, 19, 20, 21, 22, 2, 3, 4, 5, 6, 12, 16, 25, 13, 26, 35, 24, 36, 39, 37, 38, 40 }; static const yytype_uint8 yycheck[] = { 1, 3, 4, 17, 18, 19, 20, 21, 22, 3, 4, 13, 12, 0, 15, 16, 16, 17, 18, 19, 20, 5, 6, 7, 8, 9, 10, 16, 17, 18, 19, 20, 11, 4, 17, 14, 19, 15, 20, 15, 20, 11, 11, 20 }; /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing symbol of state STATE-NUM. */ static const yytype_uint8 yystos[] = { 0, 12, 16, 17, 18, 19, 20, 22, 23, 24, 25, 23, 11, 14, 0, 3, 4, 5, 6, 7, 8, 9, 10, 13, 20, 17, 19, 23, 23, 25, 25, 25, 25, 25, 25, 15, 15, 11, 11, 20, 20 }; /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ static const yytype_uint8 yyr1[] = { 0, 21, 22, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25 }; /* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN. */ static const yytype_uint8 yyr2[] = { 0, 2, 1, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 6, 6, 1, 1, 1, 1 }; #define yyerrok (yyerrstatus = 0) #define yyclearin (yychar = YYEMPTY) #define YYEMPTY (-2) #define YYEOF 0 #define YYACCEPT goto yyacceptlab #define YYABORT goto yyabortlab #define YYERROR goto yyerrorlab #define YYRECOVERING() (!!yyerrstatus) #define YYBACKUP(Token, Value) \ do \ if (yychar == YYEMPTY) \ { \ yychar = (Token); \ yylval = (Value); \ YYPOPSTACK (yylen); \ yystate = *yyssp; \ goto yybackup; \ } \ else \ { \ yyerror (expression, scanner, YY_("syntax error: cannot back up")); \ YYERROR; \ } \ while (0) /* Error token number */ #define YYTERROR 1 #define YYERRCODE 256 /* Enable debugging if requested. */ #if YYDEBUG # ifndef YYFPRINTF # include /* INFRINGES ON USER NAME SPACE */ # define YYFPRINTF fprintf # endif # define YYDPRINTF(Args) \ do { \ if (yydebug) \ YYFPRINTF Args; \ } while (0) /* This macro is provided for backward compatibility. */ #ifndef YY_LOCATION_PRINT # define YY_LOCATION_PRINT(File, Loc) ((void) 0) #endif # define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ do { \ if (yydebug) \ { \ YYFPRINTF (stderr, "%s ", Title); \ yy_symbol_print (stderr, \ Type, Value, expression, scanner); \ YYFPRINTF (stderr, "\n"); \ } \ } while (0) /*-----------------------------------. | Print this symbol's value on YYO. | `-----------------------------------*/ static void yy_symbol_value_print (FILE *yyo, int yytype, YYSTYPE const * const yyvaluep, KUserFeedback::SurveyTargetExpression **expression, yyscan_t scanner) { FILE *yyoutput = yyo; YYUSE (yyoutput); YYUSE (expression); YYUSE (scanner); if (!yyvaluep) return; # ifdef YYPRINT if (yytype < YYNTOKENS) YYPRINT (yyo, yytoknum[yytype], *yyvaluep); # endif YYUSE (yytype); } /*---------------------------. | Print this symbol on YYO. | `---------------------------*/ static void yy_symbol_print (FILE *yyo, int yytype, YYSTYPE const * const yyvaluep, KUserFeedback::SurveyTargetExpression **expression, yyscan_t scanner) { YYFPRINTF (yyo, "%s %s (", yytype < YYNTOKENS ? "token" : "nterm", yytname[yytype]); yy_symbol_value_print (yyo, yytype, yyvaluep, expression, scanner); YYFPRINTF (yyo, ")"); } /*------------------------------------------------------------------. | yy_stack_print -- Print the state stack from its BOTTOM up to its | | TOP (included). | `------------------------------------------------------------------*/ static void yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop) { YYFPRINTF (stderr, "Stack now"); for (; yybottom <= yytop; yybottom++) { int yybot = *yybottom; YYFPRINTF (stderr, " %d", yybot); } YYFPRINTF (stderr, "\n"); } # define YY_STACK_PRINT(Bottom, Top) \ do { \ if (yydebug) \ yy_stack_print ((Bottom), (Top)); \ } while (0) /*------------------------------------------------. | Report that the YYRULE is going to be reduced. | `------------------------------------------------*/ static void yy_reduce_print (yytype_int16 *yyssp, YYSTYPE *yyvsp, int yyrule, KUserFeedback::SurveyTargetExpression **expression, yyscan_t scanner) { unsigned long yylno = yyrline[yyrule]; int yynrhs = yyr2[yyrule]; int yyi; YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n", yyrule - 1, yylno); /* The symbols being reduced. */ for (yyi = 0; yyi < yynrhs; yyi++) { YYFPRINTF (stderr, " $%d = ", yyi + 1); yy_symbol_print (stderr, yystos[yyssp[yyi + 1 - yynrhs]], &yyvsp[(yyi + 1) - (yynrhs)] , expression, scanner); YYFPRINTF (stderr, "\n"); } } # define YY_REDUCE_PRINT(Rule) \ do { \ if (yydebug) \ yy_reduce_print (yyssp, yyvsp, Rule, expression, scanner); \ } while (0) /* Nonzero means print parse trace. It is left uninitialized so that multiple parsers can coexist. */ int yydebug; #else /* !YYDEBUG */ # define YYDPRINTF(Args) # define YY_SYMBOL_PRINT(Title, Type, Value, Location) # define YY_STACK_PRINT(Bottom, Top) # define YY_REDUCE_PRINT(Rule) #endif /* !YYDEBUG */ /* YYINITDEPTH -- initial size of the parser's stacks. */ #ifndef YYINITDEPTH # define YYINITDEPTH 200 #endif /* YYMAXDEPTH -- maximum size the stacks can grow to (effective only if the built-in stack extension method is used). Do not make this value too large; the results are undefined if YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) evaluated with infinite-precision integer arithmetic. */ #ifndef YYMAXDEPTH # define YYMAXDEPTH 10000 #endif #if YYERROR_VERBOSE # ifndef yystrlen # if defined __GLIBC__ && defined _STRING_H # define yystrlen strlen # else /* Return the length of YYSTR. */ static YYSIZE_T yystrlen (const char *yystr) { YYSIZE_T yylen; for (yylen = 0; yystr[yylen]; yylen++) continue; return yylen; } # endif # endif # ifndef yystpcpy # if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE # define yystpcpy stpcpy # else /* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in YYDEST. */ static char * yystpcpy (char *yydest, const char *yysrc) { char *yyd = yydest; const char *yys = yysrc; while ((*yyd++ = *yys++) != '\0') continue; return yyd - 1; } # endif # endif # ifndef yytnamerr /* Copy to YYRES the contents of YYSTR after stripping away unnecessary quotes and backslashes, so that it's suitable for yyerror. The heuristic is that double-quoting is unnecessary unless the string contains an apostrophe, a comma, or backslash (other than backslash-backslash). YYSTR is taken from yytname. If YYRES is null, do not copy; instead, return the length of what the result would have been. */ static YYSIZE_T yytnamerr (char *yyres, const char *yystr) { if (*yystr == '"') { YYSIZE_T yyn = 0; char const *yyp = yystr; for (;;) switch (*++yyp) { case '\'': case ',': goto do_not_strip_quotes; case '\\': if (*++yyp != '\\') goto do_not_strip_quotes; else goto append; append: default: if (yyres) yyres[yyn] = *yyp; yyn++; break; case '"': if (yyres) yyres[yyn] = '\0'; return yyn; } do_not_strip_quotes: ; } if (! yyres) return yystrlen (yystr); return (YYSIZE_T) (yystpcpy (yyres, yystr) - yyres); } # endif /* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message about the unexpected token YYTOKEN for the state stack whose top is YYSSP. Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is not large enough to hold the message. In that case, also set *YYMSG_ALLOC to the required number of bytes. Return 2 if the required number of bytes is too large to store. */ static int yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg, yytype_int16 *yyssp, int yytoken) { YYSIZE_T yysize0 = yytnamerr (YY_NULLPTR, yytname[yytoken]); YYSIZE_T yysize = yysize0; enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; /* Internationalized format string. */ const char *yyformat = YY_NULLPTR; /* Arguments of yyformat. */ char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; /* Number of reported tokens (one for the "unexpected", one per "expected"). */ int yycount = 0; /* There are many possibilities here to consider: - If this state is a consistent state with a default action, then the only way this function was invoked is if the default action is an error action. In that case, don't check for expected tokens because there are none. - The only way there can be no lookahead present (in yychar) is if this state is a consistent state with a default action. Thus, detecting the absence of a lookahead is sufficient to determine that there is no unexpected or expected token to report. In that case, just report a simple "syntax error". - Don't assume there isn't a lookahead just because this state is a consistent state with a default action. There might have been a previous inconsistent state, consistent state with a non-default action, or user semantic action that manipulated yychar. - Of course, the expected token list depends on states to have correct lookahead information, and it depends on the parser not to perform extra reductions after fetching a lookahead from the scanner and before detecting a syntax error. Thus, state merging (from LALR or IELR) and default reductions corrupt the expected token list. However, the list is correct for canonical LR with one exception: it will still contain any token that will not be accepted due to an error action in a later state. */ if (yytoken != YYEMPTY) { int yyn = yypact[*yyssp]; yyarg[yycount++] = yytname[yytoken]; if (!yypact_value_is_default (yyn)) { /* Start YYX at -YYN if negative to avoid negative indexes in YYCHECK. In other words, skip the first -YYN actions for this state because they are default actions. */ int yyxbegin = yyn < 0 ? -yyn : 0; /* Stay within bounds of both yycheck and yytname. */ int yychecklim = YYLAST - yyn + 1; int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; int yyx; for (yyx = yyxbegin; yyx < yyxend; ++yyx) if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR && !yytable_value_is_error (yytable[yyx + yyn])) { if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) { yycount = 1; yysize = yysize0; break; } yyarg[yycount++] = yytname[yyx]; { YYSIZE_T yysize1 = yysize + yytnamerr (YY_NULLPTR, yytname[yyx]); if (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM) yysize = yysize1; else return 2; } } } } switch (yycount) { # define YYCASE_(N, S) \ case N: \ yyformat = S; \ break default: /* Avoid compiler warnings. */ YYCASE_(0, YY_("syntax error")); YYCASE_(1, YY_("syntax error, unexpected %s")); YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s")); YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s")); YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s")); YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s")); # undef YYCASE_ } { YYSIZE_T yysize1 = yysize + yystrlen (yyformat); if (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM) yysize = yysize1; else return 2; } if (*yymsg_alloc < yysize) { *yymsg_alloc = 2 * yysize; if (! (yysize <= *yymsg_alloc && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM)) *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM; return 1; } /* Avoid sprintf, as that infringes on the user's name space. Don't have undefined behavior even if the translation produced a string with the wrong number of "%s"s. */ { char *yyp = *yymsg; int yyi = 0; while ((*yyp = *yyformat) != '\0') if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount) { yyp += yytnamerr (yyp, yyarg[yyi++]); yyformat += 2; } else { yyp++; yyformat++; } } return 0; } #endif /* YYERROR_VERBOSE */ /*-----------------------------------------------. | Release the memory associated to this symbol. | `-----------------------------------------------*/ static void yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, KUserFeedback::SurveyTargetExpression **expression, yyscan_t scanner) { YYUSE (yyvaluep); YYUSE (expression); YYUSE (scanner); if (!yymsg) yymsg = "Deleting"; YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN switch (yytype) { case 19: /* T_STRING */ #line 90 "surveytargetexpressionparser.y" { free(((*yyvaluep).str)); } #line 1052 "surveytargetexpressionparser_p.cpp" break; case 20: /* T_IDENTIFIER */ #line 90 "surveytargetexpressionparser.y" { free(((*yyvaluep).str)); } #line 1058 "surveytargetexpressionparser_p.cpp" break; case 23: /* Expr */ #line 91 "surveytargetexpressionparser.y" { delete ((*yyvaluep).expression); } #line 1064 "surveytargetexpressionparser_p.cpp" break; case 24: /* Term */ #line 91 "surveytargetexpressionparser.y" { delete ((*yyvaluep).expression); } #line 1070 "surveytargetexpressionparser_p.cpp" break; case 25: /* Value */ #line 91 "surveytargetexpressionparser.y" { delete ((*yyvaluep).expression); } #line 1076 "surveytargetexpressionparser_p.cpp" break; default: break; } YY_IGNORE_MAYBE_UNINITIALIZED_END } /*----------. | yyparse. | `----------*/ int yyparse (KUserFeedback::SurveyTargetExpression **expression, yyscan_t scanner) { /* The lookahead symbol. */ int yychar; /* The semantic value of the lookahead symbol. */ /* Default value used for initialization, for pacifying older GCCs or non-GCC compilers. */ YY_INITIAL_VALUE (static YYSTYPE yyval_default;) YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); /* Number of syntax errors so far. */ int yynerrs; int yystate; /* Number of tokens to shift before error messages enabled. */ int yyerrstatus; /* The stacks and their tools: 'yyss': related to states. 'yyvs': related to semantic values. Refer to the stacks through separate pointers, to allow yyoverflow to reallocate them elsewhere. */ /* The state stack. */ yytype_int16 yyssa[YYINITDEPTH]; yytype_int16 *yyss; yytype_int16 *yyssp; /* The semantic value stack. */ YYSTYPE yyvsa[YYINITDEPTH]; YYSTYPE *yyvs; YYSTYPE *yyvsp; YYSIZE_T yystacksize; int yyn; int yyresult; /* Lookahead token as an internal (translated) token number. */ int yytoken = 0; /* The variables used to return semantic value and location from the action routines. */ YYSTYPE yyval; #if YYERROR_VERBOSE /* Buffer for error messages, and its allocated size. */ char yymsgbuf[128]; char *yymsg = yymsgbuf; YYSIZE_T yymsg_alloc = sizeof yymsgbuf; #endif #define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) /* The number of symbols on the RHS of the reduced rule. Keep to zero when no symbol should be popped. */ int yylen = 0; yyssp = yyss = yyssa; yyvsp = yyvs = yyvsa; yystacksize = YYINITDEPTH; YYDPRINTF ((stderr, "Starting parse\n")); yystate = 0; yyerrstatus = 0; yynerrs = 0; yychar = YYEMPTY; /* Cause a token to be read. */ goto yysetstate; /*------------------------------------------------------------. | yynewstate -- push a new state, which is found in yystate. | `------------------------------------------------------------*/ yynewstate: /* In all cases, when you get here, the value and location stacks have just been pushed. So pushing a state here evens the stacks. */ yyssp++; /*--------------------------------------------------------------------. | yynewstate -- set current state (the top of the stack) to yystate. | `--------------------------------------------------------------------*/ yysetstate: YYDPRINTF ((stderr, "Entering state %d\n", yystate)); YY_ASSERT (0 <= yystate && yystate < YYNSTATES); *yyssp = (yytype_int16) yystate; if (yyss + yystacksize - 1 <= yyssp) #if !defined yyoverflow && !defined YYSTACK_RELOCATE goto yyexhaustedlab; #else { /* Get the current used size of the three stacks, in elements. */ YYSIZE_T yysize = (YYSIZE_T) (yyssp - yyss + 1); # if defined yyoverflow { /* Give user a chance to reallocate the stack. Use copies of these so that the &'s don't force the real ones into memory. */ YYSTYPE *yyvs1 = yyvs; yytype_int16 *yyss1 = yyss; /* Each stack pointer address is followed by the size of the data in use in that stack, in bytes. This used to be a conditional around just the two extra args, but that might be undefined if yyoverflow is a macro. */ yyoverflow (YY_("memory exhausted"), &yyss1, yysize * sizeof (*yyssp), &yyvs1, yysize * sizeof (*yyvsp), &yystacksize); yyss = yyss1; yyvs = yyvs1; } # else /* defined YYSTACK_RELOCATE */ /* Extend the stack our own way. */ if (YYMAXDEPTH <= yystacksize) goto yyexhaustedlab; yystacksize *= 2; if (YYMAXDEPTH < yystacksize) yystacksize = YYMAXDEPTH; { yytype_int16 *yyss1 = yyss; union yyalloc *yyptr = (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); if (! yyptr) goto yyexhaustedlab; YYSTACK_RELOCATE (yyss_alloc, yyss); YYSTACK_RELOCATE (yyvs_alloc, yyvs); # undef YYSTACK_RELOCATE if (yyss1 != yyssa) YYSTACK_FREE (yyss1); } # endif yyssp = yyss + yysize - 1; yyvsp = yyvs + yysize - 1; YYDPRINTF ((stderr, "Stack size increased to %lu\n", (unsigned long) yystacksize)); if (yyss + yystacksize - 1 <= yyssp) YYABORT; } #endif /* !defined yyoverflow && !defined YYSTACK_RELOCATE */ if (yystate == YYFINAL) YYACCEPT; goto yybackup; /*-----------. | yybackup. | `-----------*/ yybackup: /* Do appropriate processing given the current state. Read a lookahead token if we need one and don't already have one. */ /* First try to decide what to do without reference to lookahead token. */ yyn = yypact[yystate]; if (yypact_value_is_default (yyn)) goto yydefault; /* Not known => get a lookahead token if don't already have one. */ /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */ if (yychar == YYEMPTY) { YYDPRINTF ((stderr, "Reading a token: ")); yychar = yylex (&yylval, scanner); } if (yychar <= YYEOF) { yychar = yytoken = YYEOF; YYDPRINTF ((stderr, "Now at end of input.\n")); } else { yytoken = YYTRANSLATE (yychar); YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); } /* If the proper action on seeing token YYTOKEN is to reduce or to detect an error, take that action. */ yyn += yytoken; if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) goto yydefault; yyn = yytable[yyn]; if (yyn <= 0) { if (yytable_value_is_error (yyn)) goto yyerrlab; yyn = -yyn; goto yyreduce; } /* Count tokens shifted since error; after three, turn off error status. */ if (yyerrstatus) yyerrstatus--; /* Shift the lookahead token. */ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); /* Discard the shifted token. */ yychar = YYEMPTY; yystate = yyn; YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN *++yyvsp = yylval; YY_IGNORE_MAYBE_UNINITIALIZED_END goto yynewstate; /*-----------------------------------------------------------. | yydefault -- do the default action for the current state. | `-----------------------------------------------------------*/ yydefault: yyn = yydefact[yystate]; if (yyn == 0) goto yyerrlab; goto yyreduce; /*-----------------------------. | yyreduce -- do a reduction. | `-----------------------------*/ yyreduce: /* yyn is the number of a rule to reduce with. */ yylen = yyr2[yyn]; /* If YYLEN is nonzero, implement the default value of the action: '$$ = $1'. Otherwise, the following line sets YYVAL to garbage. This behavior is undocumented and Bison users should not rely upon it. Assigning to YYVAL unconditionally makes the parser a bit smaller, and it avoids a GCC warning that YYVAL may be used uninitialized. */ yyval = yyvsp[1-yylen]; YY_REDUCE_PRINT (yyn); switch (yyn) { case 2: #line 96 "surveytargetexpressionparser.y" { *expression = (yyvsp[0].expression); } #line 1346 "surveytargetexpressionparser_p.cpp" break; case 3: #line 99 "surveytargetexpressionparser.y" { printf("TERM "); (yyval.expression) = (yyvsp[0].expression); } #line 1352 "surveytargetexpressionparser_p.cpp" break; case 4: #line 100 "surveytargetexpressionparser.y" { (yyval.expression) = (yyvsp[-1].expression); } #line 1358 "surveytargetexpressionparser_p.cpp" break; case 5: #line 101 "surveytargetexpressionparser.y" { (yyval.expression) = new SurveyTargetExpression(SurveyTargetExpression::OpLogicAnd, (yyvsp[-2].expression), (yyvsp[0].expression)); } #line 1364 "surveytargetexpressionparser_p.cpp" break; case 6: #line 102 "surveytargetexpressionparser.y" { (yyval.expression) = new SurveyTargetExpression(SurveyTargetExpression::OpLogicOr, (yyvsp[-2].expression), (yyvsp[0].expression)); } #line 1370 "surveytargetexpressionparser_p.cpp" break; case 7: #line 105 "surveytargetexpressionparser.y" { (yyval.expression) = new SurveyTargetExpression(SurveyTargetExpression::OpEqual, (yyvsp[-2].expression), (yyvsp[0].expression)); } #line 1376 "surveytargetexpressionparser_p.cpp" break; case 8: #line 106 "surveytargetexpressionparser.y" { (yyval.expression) = new SurveyTargetExpression(SurveyTargetExpression::OpNotEqual, (yyvsp[-2].expression), (yyvsp[0].expression)); } #line 1382 "surveytargetexpressionparser_p.cpp" break; case 9: #line 107 "surveytargetexpressionparser.y" { (yyval.expression) = new SurveyTargetExpression(SurveyTargetExpression::OpGreater, (yyvsp[-2].expression), (yyvsp[0].expression)); } #line 1388 "surveytargetexpressionparser_p.cpp" break; case 10: #line 108 "surveytargetexpressionparser.y" { (yyval.expression) = new SurveyTargetExpression(SurveyTargetExpression::OpGreaterEqual, (yyvsp[-2].expression), (yyvsp[0].expression)); } #line 1394 "surveytargetexpressionparser_p.cpp" break; case 11: #line 109 "surveytargetexpressionparser.y" { (yyval.expression) = new SurveyTargetExpression(SurveyTargetExpression::OpLess, (yyvsp[-2].expression), (yyvsp[0].expression)); } #line 1400 "surveytargetexpressionparser_p.cpp" break; case 12: #line 110 "surveytargetexpressionparser.y" { (yyval.expression) = new SurveyTargetExpression(SurveyTargetExpression::OpLessEqual, (yyvsp[-2].expression), (yyvsp[0].expression)); } #line 1406 "surveytargetexpressionparser_p.cpp" break; case 13: #line 113 "surveytargetexpressionparser.y" { (yyval.expression) = new SurveyTargetExpression(QString::fromUtf8((yyvsp[-2].str)), QVariant(), QString::fromUtf8((yyvsp[0].str))); free((yyvsp[-2].str)); free((yyvsp[0].str)); } #line 1416 "surveytargetexpressionparser_p.cpp" break; case 14: #line 118 "surveytargetexpressionparser.y" { (yyval.expression) = new SurveyTargetExpression(QString::fromUtf8((yyvsp[-5].str)), (yyvsp[-3].intVal), QString::fromUtf8((yyvsp[0].str))); free((yyvsp[-5].str)); free((yyvsp[0].str)); } #line 1426 "surveytargetexpressionparser_p.cpp" break; case 15: #line 123 "surveytargetexpressionparser.y" { (yyval.expression) = new SurveyTargetExpression(QString::fromUtf8((yyvsp[-5].str)), QString::fromUtf8((yyvsp[-3].str)), QString::fromUtf8((yyvsp[0].str))); free((yyvsp[-5].str)); free((yyvsp[-3].str)); free((yyvsp[0].str)); } #line 1437 "surveytargetexpressionparser_p.cpp" break; case 16: #line 129 "surveytargetexpressionparser.y" { (yyval.expression) = new SurveyTargetExpression((yyvsp[0].doubleVal)); } #line 1443 "surveytargetexpressionparser_p.cpp" break; case 17: #line 130 "surveytargetexpressionparser.y" { (yyval.expression) = new SurveyTargetExpression((yyvsp[0].intVal)); } #line 1449 "surveytargetexpressionparser_p.cpp" break; case 18: #line 131 "surveytargetexpressionparser.y" { (yyval.expression) = new SurveyTargetExpression((yyvsp[0].boolVal)); } #line 1455 "surveytargetexpressionparser_p.cpp" break; case 19: #line 132 "surveytargetexpressionparser.y" { (yyval.expression) = new SurveyTargetExpression(QString::fromUtf8((yyvsp[0].str))); free((yyvsp[0].str)); } #line 1461 "surveytargetexpressionparser_p.cpp" break; #line 1465 "surveytargetexpressionparser_p.cpp" default: break; } /* User semantic actions sometimes alter yychar, and that requires that yytoken be updated with the new translation. We take the approach of translating immediately before every use of yytoken. One alternative is translating here after every semantic action, but that translation would be missed if the semantic action invokes YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an incorrect destructor might then be invoked immediately. In the case of YYERROR or YYBACKUP, subsequent parser actions might lead to an incorrect destructor call or verbose syntax error message before the lookahead is translated. */ YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); YYPOPSTACK (yylen); yylen = 0; YY_STACK_PRINT (yyss, yyssp); *++yyvsp = yyval; /* Now 'shift' the result of the reduction. Determine what state that goes to, based on the state we popped back to and the rule number reduced by. */ { const int yylhs = yyr1[yyn] - YYNTOKENS; const int yyi = yypgoto[yylhs] + *yyssp; yystate = (0 <= yyi && yyi <= YYLAST && yycheck[yyi] == *yyssp ? yytable[yyi] : yydefgoto[yylhs]); } goto yynewstate; /*--------------------------------------. | yyerrlab -- here on detecting error. | `--------------------------------------*/ yyerrlab: /* Make sure we have latest lookahead translation. See comments at user semantic actions for why this is necessary. */ yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar); /* If not already recovering from an error, report this error. */ if (!yyerrstatus) { ++yynerrs; #if ! YYERROR_VERBOSE yyerror (expression, scanner, YY_("syntax error")); #else # define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \ yyssp, yytoken) { char const *yymsgp = YY_("syntax error"); int yysyntax_error_status; yysyntax_error_status = YYSYNTAX_ERROR; if (yysyntax_error_status == 0) yymsgp = yymsg; else if (yysyntax_error_status == 1) { if (yymsg != yymsgbuf) YYSTACK_FREE (yymsg); yymsg = (char *) YYSTACK_ALLOC (yymsg_alloc); if (!yymsg) { yymsg = yymsgbuf; yymsg_alloc = sizeof yymsgbuf; yysyntax_error_status = 2; } else { yysyntax_error_status = YYSYNTAX_ERROR; yymsgp = yymsg; } } yyerror (expression, scanner, yymsgp); if (yysyntax_error_status == 2) goto yyexhaustedlab; } # undef YYSYNTAX_ERROR #endif } if (yyerrstatus == 3) { /* If just tried and failed to reuse lookahead token after an error, discard it. */ if (yychar <= YYEOF) { /* Return failure if at end of input. */ if (yychar == YYEOF) YYABORT; } else { yydestruct ("Error: discarding", yytoken, &yylval, expression, scanner); yychar = YYEMPTY; } } /* Else will try to reuse lookahead token after shifting the error token. */ goto yyerrlab1; /*---------------------------------------------------. | yyerrorlab -- error raised explicitly by YYERROR. | `---------------------------------------------------*/ yyerrorlab: /* Pacify compilers when the user code never invokes YYERROR and the label yyerrorlab therefore never appears in user code. */ if (0) YYERROR; /* Do not reclaim the symbols of the rule whose action triggered this YYERROR. */ YYPOPSTACK (yylen); yylen = 0; YY_STACK_PRINT (yyss, yyssp); yystate = *yyssp; goto yyerrlab1; /*-------------------------------------------------------------. | yyerrlab1 -- common code for both syntax error and YYERROR. | `-------------------------------------------------------------*/ yyerrlab1: yyerrstatus = 3; /* Each real token shifted decrements this. */ for (;;) { yyn = yypact[yystate]; if (!yypact_value_is_default (yyn)) { yyn += YYTERROR; if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) { yyn = yytable[yyn]; if (0 < yyn) break; } } /* Pop the current state because it cannot handle the error token. */ if (yyssp == yyss) YYABORT; yydestruct ("Error: popping", yystos[yystate], yyvsp, expression, scanner); YYPOPSTACK (1); yystate = *yyssp; YY_STACK_PRINT (yyss, yyssp); } YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN *++yyvsp = yylval; YY_IGNORE_MAYBE_UNINITIALIZED_END /* Shift the error token. */ YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); yystate = yyn; goto yynewstate; /*-------------------------------------. | yyacceptlab -- YYACCEPT comes here. | `-------------------------------------*/ yyacceptlab: yyresult = 0; goto yyreturn; /*-----------------------------------. | yyabortlab -- YYABORT comes here. | `-----------------------------------*/ yyabortlab: yyresult = 1; goto yyreturn; #if !defined yyoverflow || YYERROR_VERBOSE /*-------------------------------------------------. | yyexhaustedlab -- memory exhaustion comes here. | `-------------------------------------------------*/ yyexhaustedlab: yyerror (expression, scanner, YY_("memory exhausted")); yyresult = 2; /* Fall through. */ #endif /*-----------------------------------------------------. | yyreturn -- parsing is finished, return the result. | `-----------------------------------------------------*/ yyreturn: if (yychar != YYEMPTY) { /* Make sure we have latest lookahead translation. See comments at user semantic actions for why this is necessary. */ yytoken = YYTRANSLATE (yychar); yydestruct ("Cleanup: discarding lookahead", yytoken, &yylval, expression, scanner); } /* Do not reclaim the symbols of the rule whose action triggered this YYABORT or YYACCEPT. */ YYPOPSTACK (yylen); YY_STACK_PRINT (yyss, yyssp); while (yyssp != yyss) { yydestruct ("Cleanup: popping", yystos[*yyssp], yyvsp, expression, scanner); YYPOPSTACK (1); } #ifndef yyoverflow if (yyss != yyssa) YYSTACK_FREE (yyss); #endif #if YYERROR_VERBOSE if (yymsg != yymsgbuf) YYSTACK_FREE (yymsg); #endif return yyresult; } #line 135 "surveytargetexpressionparser.y" gammaray-2.11.3/3rdparty/kuserfeedback/common/surveytargetexpressionparser_p.h000066400000000000000000000060451413310443000300210ustar00rootroot00000000000000/* A Bison parser, made by GNU Bison 3.4.1. */ /* Bison interface for Yacc-like parsers in C Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2019 Free Software Foundation, Inc. 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 3 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, see . */ /* As a special exception, you may create a larger work that contains part or all of the Bison parser skeleton and distribute that work under terms of your choice, so long as that work isn't itself a parser generator using the skeleton or a modified version thereof as a parser skeleton. Alternatively, if you modify or redistribute the parser skeleton itself, you may (at your option) remove this special exception, which will cause the skeleton and the resulting Bison output files to be licensed under the GNU General Public License without this special exception. This special exception was added by the Free Software Foundation in version 2.2 of Bison. */ /* Undocumented macros, especially those whose name start with YY_, are private implementation details. Do not rely on them. */ #ifndef YY_YY_SURVEYTARGETEXPRESSIONPARSER_P_H_INCLUDED # define YY_YY_SURVEYTARGETEXPRESSIONPARSER_P_H_INCLUDED /* Debug traces. */ #ifndef YYDEBUG # define YYDEBUG 0 #endif #if YYDEBUG extern int yydebug; #endif /* "%code requires" blocks. */ #line 42 "surveytargetexpressionparser.y" #ifndef YY_TYPEDEF_YY_SCANNER_T #define YY_TYPEDEF_YY_SCANNER_T typedef void* yyscan_t; #endif #line 57 "surveytargetexpressionparser_p.h" /* Token type. */ #ifndef YYTOKENTYPE # define YYTOKENTYPE enum yytokentype { T_OP_OR = 258, T_OP_AND = 259, T_OP_EQ = 260, T_OP_NEQ = 261, T_OP_GT = 262, T_OP_GE = 263, T_OP_LT = 264, T_OP_LE = 265, T_DOT = 266, T_LPAREN = 267, T_RPAREN = 268, T_LBRACKET = 269, T_RBRACKET = 270, T_DOUBLE = 271, T_INTEGER = 272, T_BOOLEAN = 273, T_STRING = 274, T_IDENTIFIER = 275 }; #endif /* Value type. */ #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED union YYSTYPE { #line 56 "surveytargetexpressionparser.y" int intVal; double doubleVal; bool boolVal; char *str; KUserFeedback::SurveyTargetExpression *expression; #line 97 "surveytargetexpressionparser_p.h" }; typedef union YYSTYPE YYSTYPE; # define YYSTYPE_IS_TRIVIAL 1 # define YYSTYPE_IS_DECLARED 1 #endif int yyparse (KUserFeedback::SurveyTargetExpression **expression, yyscan_t scanner); #endif /* !YY_YY_SURVEYTARGETEXPRESSIONPARSER_P_H_INCLUDED */ gammaray-2.11.3/3rdparty/kuserfeedback/common/surveytargetexpressionscanner.h000066400000000000000000000273451413310443000276450ustar00rootroot00000000000000#ifndef yyHEADER_H #define yyHEADER_H 1 #define yyIN_HEADER 1 #line 5 "surveytargetexpressionscanner.h" #line 7 "surveytargetexpressionscanner.h" #define YY_INT_ALIGNED short int /* A lexical scanner generated by flex */ #define FLEX_SCANNER #define YY_FLEX_MAJOR_VERSION 2 #define YY_FLEX_MINOR_VERSION 6 #define YY_FLEX_SUBMINOR_VERSION 4 #if YY_FLEX_SUBMINOR_VERSION > 0 #define FLEX_BETA #endif #ifdef yyget_lval #define yyget_lval_ALREADY_DEFINED #else #define yyget_lval yyget_lval #endif #ifdef yyset_lval #define yyset_lval_ALREADY_DEFINED #else #define yyset_lval yyset_lval #endif /* First, we deal with platform-specific or compiler-specific issues. */ /* begin standard C headers. */ #include #include #include #include /* end standard C headers. */ /* flex integer type definitions */ #ifndef FLEXINT_H #define FLEXINT_H /* C99 systems have . Non-C99 systems may or may not. */ #if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, * if you want the limit (max/min) macros for int types. */ #ifndef __STDC_LIMIT_MACROS #define __STDC_LIMIT_MACROS 1 #endif #include typedef int8_t flex_int8_t; typedef uint8_t flex_uint8_t; typedef int16_t flex_int16_t; typedef uint16_t flex_uint16_t; typedef int32_t flex_int32_t; typedef uint32_t flex_uint32_t; #else typedef signed char flex_int8_t; typedef short int flex_int16_t; typedef int flex_int32_t; typedef unsigned char flex_uint8_t; typedef unsigned short int flex_uint16_t; typedef unsigned int flex_uint32_t; /* Limits of integral types. */ #ifndef INT8_MIN #define INT8_MIN (-128) #endif #ifndef INT16_MIN #define INT16_MIN (-32767-1) #endif #ifndef INT32_MIN #define INT32_MIN (-2147483647-1) #endif #ifndef INT8_MAX #define INT8_MAX (127) #endif #ifndef INT16_MAX #define INT16_MAX (32767) #endif #ifndef INT32_MAX #define INT32_MAX (2147483647) #endif #ifndef UINT8_MAX #define UINT8_MAX (255U) #endif #ifndef UINT16_MAX #define UINT16_MAX (65535U) #endif #ifndef UINT32_MAX #define UINT32_MAX (4294967295U) #endif #ifndef SIZE_MAX #define SIZE_MAX (~(size_t)0) #endif #endif /* ! C99 */ #endif /* ! FLEXINT_H */ /* begin standard C++ headers. */ /* TODO: this is always defined, so inline it */ #define yyconst const #if defined(__GNUC__) && __GNUC__ >= 3 #define yynoreturn __attribute__((__noreturn__)) #else #define yynoreturn #endif /* An opaque pointer. */ #ifndef YY_TYPEDEF_YY_SCANNER_T #define YY_TYPEDEF_YY_SCANNER_T typedef void* yyscan_t; #endif /* For convenience, these vars (plus the bison vars far below) are macros in the reentrant scanner. */ #define yyin yyg->yyin_r #define yyout yyg->yyout_r #define yyextra yyg->yyextra_r #define yyleng yyg->yyleng_r #define yytext yyg->yytext_r #define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno) #define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column) #define yy_flex_debug yyg->yy_flex_debug_r /* Size of default input buffer. */ #ifndef YY_BUF_SIZE #ifdef __ia64__ /* On IA-64, the buffer size is 16k, not 8k. * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case. * Ditto for the __ia64__ case accordingly. */ #define YY_BUF_SIZE 32768 #else #define YY_BUF_SIZE 16384 #endif /* __ia64__ */ #endif #ifndef YY_TYPEDEF_YY_BUFFER_STATE #define YY_TYPEDEF_YY_BUFFER_STATE typedef struct yy_buffer_state *YY_BUFFER_STATE; #endif #ifndef YY_TYPEDEF_YY_SIZE_T #define YY_TYPEDEF_YY_SIZE_T typedef size_t yy_size_t; #endif #ifndef YY_STRUCT_YY_BUFFER_STATE #define YY_STRUCT_YY_BUFFER_STATE struct yy_buffer_state { FILE *yy_input_file; char *yy_ch_buf; /* input buffer */ char *yy_buf_pos; /* current position in input buffer */ /* Size of input buffer in bytes, not including room for EOB * characters. */ int yy_buf_size; /* Number of characters read into yy_ch_buf, not including EOB * characters. */ int yy_n_chars; /* Whether we "own" the buffer - i.e., we know we created it, * and can realloc() it to grow it, and should free() it to * delete it. */ int yy_is_our_buffer; /* Whether this is an "interactive" input source; if so, and * if we're using stdio for input, then we want to use getc() * instead of fread(), to make sure we stop fetching input after * each newline. */ int yy_is_interactive; /* Whether we're considered to be at the beginning of a line. * If so, '^' rules will be active on the next match, otherwise * not. */ int yy_at_bol; int yy_bs_lineno; /**< The line count. */ int yy_bs_column; /**< The column count. */ /* Whether to try to fill the input buffer when we reach the * end of it. */ int yy_fill_buffer; int yy_buffer_status; }; #endif /* !YY_STRUCT_YY_BUFFER_STATE */ void yyrestart ( FILE *input_file , yyscan_t yyscanner ); void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size , yyscan_t yyscanner ); void yy_delete_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); void yy_flush_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); void yypush_buffer_state ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); void yypop_buffer_state ( yyscan_t yyscanner ); YY_BUFFER_STATE yy_scan_buffer ( char *base, yy_size_t size , yyscan_t yyscanner ); YY_BUFFER_STATE yy_scan_string ( const char *yy_str , yyscan_t yyscanner ); YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, int len , yyscan_t yyscanner ); void *yyalloc ( yy_size_t , yyscan_t yyscanner ); void *yyrealloc ( void *, yy_size_t , yyscan_t yyscanner ); void yyfree ( void * , yyscan_t yyscanner ); /* Begin user sect3 */ #define yywrap(yyscanner) (/*CONSTCOND*/1) #define YY_SKIP_YYWRAP #define yytext_ptr yytext_r #ifdef YY_HEADER_EXPORT_START_CONDITIONS #define INITIAL 0 #endif #ifndef YY_NO_UNISTD_H /* Special case for "unistd.h", since it is non-ANSI. We include it way * down here because we want the user's section 1 to have been scanned first. * The user has a chance to override it with an option. */ #include #endif #ifndef YY_EXTRA_TYPE #define YY_EXTRA_TYPE void * #endif int yylex_init (yyscan_t* scanner); int yylex_init_extra ( YY_EXTRA_TYPE user_defined, yyscan_t* scanner); /* Accessor methods to globals. These are made visible to non-reentrant scanners for convenience. */ int yylex_destroy ( yyscan_t yyscanner ); int yyget_debug ( yyscan_t yyscanner ); void yyset_debug ( int debug_flag , yyscan_t yyscanner ); YY_EXTRA_TYPE yyget_extra ( yyscan_t yyscanner ); void yyset_extra ( YY_EXTRA_TYPE user_defined , yyscan_t yyscanner ); FILE *yyget_in ( yyscan_t yyscanner ); void yyset_in ( FILE * _in_str , yyscan_t yyscanner ); FILE *yyget_out ( yyscan_t yyscanner ); void yyset_out ( FILE * _out_str , yyscan_t yyscanner ); int yyget_leng ( yyscan_t yyscanner ); char *yyget_text ( yyscan_t yyscanner ); int yyget_lineno ( yyscan_t yyscanner ); void yyset_lineno ( int _line_number , yyscan_t yyscanner ); int yyget_column ( yyscan_t yyscanner ); void yyset_column ( int _column_no , yyscan_t yyscanner ); YYSTYPE * yyget_lval ( yyscan_t yyscanner ); void yyset_lval ( YYSTYPE * yylval_param , yyscan_t yyscanner ); /* Macros after this point can all be overridden by user definitions in * section 1. */ #ifndef YY_SKIP_YYWRAP #ifdef __cplusplus extern "C" int yywrap ( yyscan_t yyscanner ); #else extern int yywrap ( yyscan_t yyscanner ); #endif #endif #ifndef yytext_ptr static void yy_flex_strncpy ( char *, const char *, int , yyscan_t yyscanner); #endif #ifdef YY_NEED_STRLEN static int yy_flex_strlen ( const char * , yyscan_t yyscanner); #endif #ifndef YY_NO_INPUT #endif /* Amount of stuff to slurp up with each read. */ #ifndef YY_READ_BUF_SIZE #ifdef __ia64__ /* On IA-64, the buffer size is 16k, not 8k */ #define YY_READ_BUF_SIZE 16384 #else #define YY_READ_BUF_SIZE 8192 #endif /* __ia64__ */ #endif /* Number of entries by which start-condition stack grows. */ #ifndef YY_START_STACK_INCR #define YY_START_STACK_INCR 25 #endif /* Default declaration of generated scanner - a define so the user can * easily add parameters. */ #ifndef YY_DECL #define YY_DECL_IS_OURS 1 extern int yylex \ (YYSTYPE * yylval_param , yyscan_t yyscanner); #define YY_DECL int yylex \ (YYSTYPE * yylval_param , yyscan_t yyscanner) #endif /* !YY_DECL */ /* yy_get_previous_state - get the state just before the EOB char was reached */ #undef YY_NEW_FILE #undef YY_FLUSH_BUFFER #undef yy_set_bol #undef yy_new_buffer #undef yy_set_interactive #undef YY_DO_BEFORE_ACTION #ifdef YY_DECL_IS_OURS #undef YY_DECL_IS_OURS #undef YY_DECL #endif #ifndef yy_create_buffer_ALREADY_DEFINED #undef yy_create_buffer #endif #ifndef yy_delete_buffer_ALREADY_DEFINED #undef yy_delete_buffer #endif #ifndef yy_scan_buffer_ALREADY_DEFINED #undef yy_scan_buffer #endif #ifndef yy_scan_string_ALREADY_DEFINED #undef yy_scan_string #endif #ifndef yy_scan_bytes_ALREADY_DEFINED #undef yy_scan_bytes #endif #ifndef yy_init_buffer_ALREADY_DEFINED #undef yy_init_buffer #endif #ifndef yy_flush_buffer_ALREADY_DEFINED #undef yy_flush_buffer #endif #ifndef yy_load_buffer_state_ALREADY_DEFINED #undef yy_load_buffer_state #endif #ifndef yy_switch_to_buffer_ALREADY_DEFINED #undef yy_switch_to_buffer #endif #ifndef yypush_buffer_state_ALREADY_DEFINED #undef yypush_buffer_state #endif #ifndef yypop_buffer_state_ALREADY_DEFINED #undef yypop_buffer_state #endif #ifndef yyensure_buffer_stack_ALREADY_DEFINED #undef yyensure_buffer_stack #endif #ifndef yylex_ALREADY_DEFINED #undef yylex #endif #ifndef yyrestart_ALREADY_DEFINED #undef yyrestart #endif #ifndef yylex_init_ALREADY_DEFINED #undef yylex_init #endif #ifndef yylex_init_extra_ALREADY_DEFINED #undef yylex_init_extra #endif #ifndef yylex_destroy_ALREADY_DEFINED #undef yylex_destroy #endif #ifndef yyget_debug_ALREADY_DEFINED #undef yyget_debug #endif #ifndef yyset_debug_ALREADY_DEFINED #undef yyset_debug #endif #ifndef yyget_extra_ALREADY_DEFINED #undef yyget_extra #endif #ifndef yyset_extra_ALREADY_DEFINED #undef yyset_extra #endif #ifndef yyget_in_ALREADY_DEFINED #undef yyget_in #endif #ifndef yyset_in_ALREADY_DEFINED #undef yyset_in #endif #ifndef yyget_out_ALREADY_DEFINED #undef yyget_out #endif #ifndef yyset_out_ALREADY_DEFINED #undef yyset_out #endif #ifndef yyget_leng_ALREADY_DEFINED #undef yyget_leng #endif #ifndef yyget_text_ALREADY_DEFINED #undef yyget_text #endif #ifndef yyget_lineno_ALREADY_DEFINED #undef yyget_lineno #endif #ifndef yyset_lineno_ALREADY_DEFINED #undef yyset_lineno #endif #ifndef yyget_column_ALREADY_DEFINED #undef yyget_column #endif #ifndef yyset_column_ALREADY_DEFINED #undef yyset_column #endif #ifndef yywrap_ALREADY_DEFINED #undef yywrap #endif #ifndef yyget_lval_ALREADY_DEFINED #undef yyget_lval #endif #ifndef yyset_lval_ALREADY_DEFINED #undef yyset_lval #endif #ifndef yyget_lloc_ALREADY_DEFINED #undef yyget_lloc #endif #ifndef yyset_lloc_ALREADY_DEFINED #undef yyset_lloc #endif #ifndef yyalloc_ALREADY_DEFINED #undef yyalloc #endif #ifndef yyrealloc_ALREADY_DEFINED #undef yyrealloc #endif #ifndef yyfree_ALREADY_DEFINED #undef yyfree #endif #ifndef yytext_ALREADY_DEFINED #undef yytext #endif #ifndef yyleng_ALREADY_DEFINED #undef yyleng #endif #ifndef yyin_ALREADY_DEFINED #undef yyin #endif #ifndef yyout_ALREADY_DEFINED #undef yyout #endif #ifndef yy_flex_debug_ALREADY_DEFINED #undef yy_flex_debug #endif #ifndef yylineno_ALREADY_DEFINED #undef yylineno #endif #ifndef yytables_fload_ALREADY_DEFINED #undef yytables_fload #endif #ifndef yytables_destroy_ALREADY_DEFINED #undef yytables_destroy #endif #ifndef yyTABLES_NAME_ALREADY_DEFINED #undef yyTABLES_NAME #endif #line 94 "surveytargetexpressionlexer.l" #line 508 "surveytargetexpressionscanner.h" #undef yyIN_HEADER #endif /* yyHEADER_H */ gammaray-2.11.3/3rdparty/kuserfeedback/config-userfeedback.h000066400000000000000000000000471413310443000237730ustar00rootroot00000000000000#define HAVE_SURVEY_TARGET_EXPRESSIONS gammaray-2.11.3/3rdparty/kuserfeedback/core/000077500000000000000000000000001413310443000206635ustar00rootroot00000000000000gammaray-2.11.3/3rdparty/kuserfeedback/core/abstractdatasource.cpp000066400000000000000000000105441413310443000252510ustar00rootroot00000000000000/* Copyright (C) 2016 Volker Krause Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include "abstractdatasource.h" #include "abstractdatasource_p.h" using namespace KUserFeedback; static bool defaultActiveState() { return true; } static QString activeStateKey() { return QStringLiteral("activeState"); } static QString commonSettingsGroupName() { return QStringLiteral("dataSourceCommonSettings"); } AbstractDataSourcePrivate::AbstractDataSourcePrivate() : mode(Provider::DetailedUsageStatistics) , active(defaultActiveState()) { } AbstractDataSourcePrivate::~AbstractDataSourcePrivate() { } void AbstractDataSourcePrivate::storeCommonSettings(QSettings *settings) { settings->beginGroup(commonSettingsGroupName()); settings->setValue(activeStateKey(), active); settings->endGroup(); } void AbstractDataSourcePrivate::loadCommonSettings(QSettings *settings) { settings->beginGroup(commonSettingsGroupName()); active = settings->value(activeStateKey(), defaultActiveState()).toBool(); settings->endGroup(); } // Please note that this function is supposed to be invoked in between // data submissions, so be careful to don't reset flags that might be // changed by a user via UI (e.g., active state) void AbstractDataSourcePrivate::resetCommonSettings(QSettings *settings) { Q_UNUSED(settings); } AbstractDataSource::AbstractDataSource(const QString &id, Provider::TelemetryMode mode) : AbstractDataSource(id, mode, nullptr) { } AbstractDataSource::AbstractDataSource(const QString &id, Provider::TelemetryMode mode, AbstractDataSourcePrivate* dd) : d_ptr(dd ? dd : new AbstractDataSourcePrivate) { d_ptr->id = id; d_ptr->mode = mode; } AbstractDataSource::~AbstractDataSource() { delete d_ptr; } QString AbstractDataSource::id() const { return d_ptr->id; } QString AbstractDataSource::name() const { return {}; } void AbstractDataSource::setId(const QString& id) { d_ptr->id = id; } void AbstractDataSource::loadImpl(QSettings *settings) { Q_UNUSED(settings); } void AbstractDataSource::storeImpl(QSettings *settings) { Q_UNUSED(settings); } void AbstractDataSource::resetImpl(QSettings *settings) { Q_UNUSED(settings); } void AbstractDataSource::load(QSettings *settings) { Q_D(AbstractDataSource); d->loadCommonSettings(settings); loadImpl(settings); } void AbstractDataSource::store(QSettings *settings) { Q_D(AbstractDataSource); d->storeCommonSettings(settings); storeImpl(settings); } void AbstractDataSource::reset(QSettings *settings) { Q_D(AbstractDataSource); d->resetCommonSettings(settings); resetImpl(settings); } Provider::TelemetryMode AbstractDataSource::telemetryMode() const { Q_D(const AbstractDataSource); Q_ASSERT(d->mode != Provider::NoTelemetry); if (d->mode == Provider::NoTelemetry) return Provider::DetailedUsageStatistics; return d->mode; } void AbstractDataSource::setTelemetryMode(Provider::TelemetryMode mode) { Q_D(AbstractDataSource); Q_ASSERT(mode != Provider::NoTelemetry); d->mode = mode; } bool AbstractDataSource::isActive() const { Q_D(const AbstractDataSource); return d->active; } void AbstractDataSource::setActive(bool active) { Q_D(AbstractDataSource); d->active = active; } gammaray-2.11.3/3rdparty/kuserfeedback/core/abstractdatasource.h000066400000000000000000000152311413310443000247140ustar00rootroot00000000000000/* Copyright (C) 2016 Volker Krause Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef KUSERFEEDBACK_ABSTRACTDATASOURCE_H #define KUSERFEEDBACK_ABSTRACTDATASOURCE_H #include "kuserfeedbackcore_export.h" #include "provider.h" #include QT_BEGIN_NAMESPACE class QSettings; QT_END_NAMESPACE namespace KUserFeedback { class AbstractDataSourcePrivate; /*! Base class for data sources for telemetry data. */ class KUSERFEEDBACKCORE_EXPORT AbstractDataSource { public: virtual ~AbstractDataSource(); /*! Returns the ID of this data source. * This is used as identifier towards the server and should * not be shown to the user. * @see description() * @returns The data source identifier configured on the feedback server. */ QString id() const; /*! Returns a short name of this data source. * Can be empty if short name is meaningless for this data source. * @returns A translated, human-readable string. */ virtual QString name() const; /*! Returns a human-readable, translated description of what * this source provides. * @see id() * @returns A translated, human-readable string. */ virtual QString description() const = 0; /*! * Returns the data gathered by this source. * * Implement this to return the data provided by this source. * One of the three following formats are expected: * - scalar entries: QAssociativeIterable * - list entries: QSequentialIterable containing QAssociativeIterable * - map entries: QAssociativeIterable containing QAssociativeIterable * * The innermost QAssociativeIterable must only contain one of the following * base types (which has to match the corresponding schema entry element): * - QString * - int * - double * - bool * * All keys must be strings. * * @returns A variant complying with the above requirements. */ virtual QVariant data() = 0; /*! Load persistent state for this data source. * @param settings A QSettings object opened in a dedicated group for loading * persistent data. */ void load(QSettings *settings); /*! Store persistent state for this data source. * @param settings A QSettings object opened in a dedicated group for storing * persistent data. */ void store(QSettings *settings); /*! Reset the persistent state of this data source. * This is called after a successful submission of data, and can be used * by sources that track differential rather than absolute data to reset * their counters. * @param settings A QSettings object opened in the dedicated group of this * data source. */ void reset(QSettings *settings); /*! Returns which telemetry colleciton mode this data source belongs to. * @return The telemetry collection category this source belongs to. */ Provider::TelemetryMode telemetryMode() const; /*! Sets which telemetry colleciton mode this data source belongs to. * @param mode The data collection mode of this source. * Provider::NoTelemetry is not allowed here. */ void setTelemetryMode(Provider::TelemetryMode mode); /*! Checks whether this data source is active or not * If the data source is not active, then collected * data neither will be sent to a server nor appeared * in the audit log. * Data source is active by default. * @return true if active, false otherwise */ bool isActive() const; /*! Changes active state of the data source * @param active The new active state for this data source */ void setActive(bool active); protected: /*! Create a new data source named @p name. * The name of the data source must match the corresponding * product schema entry. * @param name Must not be empty. * @param mode The default telemetry mode. */ explicit AbstractDataSource(const QString &id, Provider::TelemetryMode mode = Provider::DetailedUsageStatistics); ///@cond internal explicit AbstractDataSource(const QString &id, Provider::TelemetryMode mode, AbstractDataSourcePrivate *dd); ///@endcond /*! Set the ID of this data source. * The ID should not change at runtime, this is only provided * for enabling QML API for generic sources. * @see id() */ void setId(const QString &id); /*! Invoked by @p load() in order to load individual settings of this data source. * @see load() description for further details. * @param settings A QSettings object opened in a dedicated group for loading * persistent data. */ virtual void loadImpl(QSettings *settings); /*! Invoked by @p store() in order to store individual settings of this data source. * @see store() description for further details. * @param settings A QSettings object opened in a dedicated group for loading * persistent data. */ virtual void storeImpl(QSettings *settings); /*! Invoked by @p reset() in order to reset individual settings of this data source. * @see reset() description for further details. * @param settings A QSettings object opened in a dedicated group for loading * persistent data. */ virtual void resetImpl(QSettings *settings); ///@cond internal class AbstractDataSourcePrivate* const d_ptr; ///@endcond private: Q_DECLARE_PRIVATE(AbstractDataSource) Q_DISABLE_COPY(AbstractDataSource) }; } #endif // KUSERFEEDBACK_ABSTRACTDATASOURCE_H gammaray-2.11.3/3rdparty/kuserfeedback/core/abstractdatasource_p.h000066400000000000000000000033461413310443000252370ustar00rootroot00000000000000/* Copyright (C) 2016 Volker Krause Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef ABSTRACTDATASOURCEPRIVATE_H #define ABSTRACTDATASOURCEPRIVATE_H #include "provider.h" QT_BEGIN_NAMESPACE class QSettings; QT_END_NAMESPACE namespace KUserFeedback { class AbstractDataSourcePrivate { public: AbstractDataSourcePrivate(); virtual ~AbstractDataSourcePrivate(); QString id; Provider::TelemetryMode mode; bool active; void storeCommonSettings(QSettings *settings); void loadCommonSettings(QSettings *settings); void resetCommonSettings(QSettings *settings); private: Q_DISABLE_COPY(AbstractDataSourcePrivate) }; } #endif // ABSTRACTDATASOURCEPRIVATE_H gammaray-2.11.3/3rdparty/kuserfeedback/core/applicationversionsource.cpp000066400000000000000000000035141413310443000265240ustar00rootroot00000000000000/* Copyright (C) 2016 Volker Krause Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "applicationversionsource.h" #include #include using namespace KUserFeedback; ApplicationVersionSource::ApplicationVersionSource() : AbstractDataSource(QStringLiteral("applicationVersion"), Provider::BasicSystemInformation) { } QString ApplicationVersionSource::description() const { return tr("The version of the application."); } QVariant ApplicationVersionSource::data() { if (QCoreApplication::applicationVersion().isEmpty()) return QVariant(); QVariantMap m; m.insert(QStringLiteral("value"), QCoreApplication::applicationVersion()); return m; } QString ApplicationVersionSource::name() const { return tr("Application version"); } gammaray-2.11.3/3rdparty/kuserfeedback/core/applicationversionsource.h000066400000000000000000000036731413310443000261770ustar00rootroot00000000000000/* Copyright (C) 2016 Volker Krause Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef KUSERFEEDBACK_APPLICATIONVERSIONSOURCE_H #define KUSERFEEDBACK_APPLICATIONVERSIONSOURCE_H #include "kuserfeedbackcore_export.h" #include "abstractdatasource.h" namespace KUserFeedback { /*! Data source for the application version. * The application version is retrieved via QCoreApplication::applicationVersion. * * The default telemetry mode for this source is Provider::BasicSystemInformation. */ class KUSERFEEDBACKCORE_EXPORT ApplicationVersionSource : public AbstractDataSource { Q_DECLARE_TR_FUNCTIONS(KUserFeedback::ApplicationVersionSource) public: /*! Create a new application version source. */ ApplicationVersionSource(); QString name() const override; QString description() const override; QVariant data() override; }; } #endif // KUSERFEEDBACK_APPLICATIONVERSIONSOURCE_H gammaray-2.11.3/3rdparty/kuserfeedback/core/auditloguicontroller.cpp000066400000000000000000000160511413310443000256440ustar00rootroot00000000000000/* Copyright (C) 2017 Volker Krause Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "auditloguicontroller.h" #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace KUserFeedback; namespace KUserFeedback { class AuditLogEntryModel : public QAbstractListModel { Q_OBJECT public: explicit AuditLogEntryModel(const QString &path, QObject *parent); void reload(); int rowCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role) const override; QHash roleNames() const override; private: QString m_path; std::vector m_entries; }; class AuditLogUiControllerPrivate { public: QString path; AuditLogEntryModel *logEntryModel; }; } AuditLogEntryModel::AuditLogEntryModel(const QString &path, QObject *parent) : QAbstractListModel(parent) , m_path(path) { reload(); } void AuditLogEntryModel::reload() { beginResetModel(); m_entries.clear(); foreach (auto e, QDir(m_path).entryList(QDir::Files | QDir::Readable)) { if (!e.endsWith(QLatin1String(".log"))) continue; e.chop(4); const auto dt = QDateTime::fromString(e, QStringLiteral("yyyyMMdd-hhmmss")); if (dt.isValid()) m_entries.push_back(dt); } std::sort(m_entries.begin(), m_entries.end(), [](const QDateTime &lhs, const QDateTime &rhs) { return lhs > rhs; }); endResetModel(); } int AuditLogEntryModel::rowCount(const QModelIndex &parent) const { if (parent.isValid()) return 0; return m_entries.size(); } QVariant AuditLogEntryModel::data(const QModelIndex &index, int role) const { switch (role) { case Qt::DisplayRole: return QLocale().toString(m_entries[index.row()]); case Qt::UserRole: return m_entries[index.row()]; } return QVariant(); } QHash AuditLogEntryModel::roleNames() const { QHash roles; roles.insert(Qt::DisplayRole, "display"); roles.insert(Qt::UserRole, "data"); return roles; } AuditLogUiController::AuditLogUiController(QObject* parent) : QObject(parent) , d(new AuditLogUiControllerPrivate) { d->path = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + QStringLiteral("/kuserfeedback/audit/"); d->logEntryModel = new AuditLogEntryModel(d->path, this); connect(d->logEntryModel, &QAbstractItemModel::modelReset, this, &AuditLogUiController::logEntryCountChanged); } AuditLogUiController::~AuditLogUiController() { } bool AuditLogUiController::hasLogEntries() const { return d->logEntryModel->rowCount() != 0; } QAbstractItemModel* AuditLogUiController::logEntryModel() const { return d->logEntryModel; } static QString telemetryModeString(Provider::TelemetryMode mode) { switch (mode) { case Provider::NoTelemetry: Q_ASSERT(false); return QString(); case Provider::BasicSystemInformation: return AuditLogUiController::tr("Basic System Information"); case Provider::BasicUsageStatistics: return AuditLogUiController::tr("Basic Usage Statistics"); case Provider::DetailedSystemInformation: return AuditLogUiController::tr("Detailed System Information"); case Provider::DetailedUsageStatistics: return AuditLogUiController::tr("Detailed Usage Statistics"); } Q_UNREACHABLE(); } QString AuditLogUiController::logEntry(const QDateTime &dt) const { const QString fn = d->path + dt.toString(QStringLiteral("yyyyMMdd-hhmmss")) + QStringLiteral(".log"); QFile file(fn); if (!file.open(QFile::ReadOnly)) return tr("Unable to open file %1: %2.").arg(fn, file.errorString()); const auto doc = QJsonDocument::fromJson(file.readAll()); const auto topObj = doc.object(); struct Entry { QString key; QString desc; QString rawData; Provider::TelemetryMode mode; }; std::vector entries; entries.reserve(topObj.size()); const auto idx = Provider::staticMetaObject.indexOfEnumerator("TelemetryMode"); Q_ASSERT(idx >= 0); const auto modeEnum = Provider::staticMetaObject.enumerator(idx); for (auto it = topObj.begin(); it != topObj.end(); ++it) { Entry e; e.key = it.key(); const auto obj = it.value().toObject(); e.desc = obj.value(QLatin1String("description")).toString(); const auto data = obj.value(QLatin1String("data")); if (data.isObject()) e.rawData = QString::fromUtf8(QJsonDocument(data.toObject()).toJson()); else if (data.isArray()) e.rawData = QString::fromUtf8(QJsonDocument(data.toArray()).toJson()); e.mode = static_cast(modeEnum.keyToValue(obj.value(QLatin1String("telemetryMode")).toString().toUtf8().constData())); entries.push_back(e); } std::sort(entries.begin(), entries.end(), [](const Entry &lhs, const Entry &rhs) -> bool { if (lhs.mode == rhs.mode) return lhs.key < rhs.key; return lhs.mode < rhs.mode; }); QString res; for (auto it = entries.begin(); it != entries.end(); ++it) { res += QStringLiteral("") + (*it).desc + QStringLiteral("
"); res += tr("Category: %1
").arg(telemetryModeString((*it).mode)); res += tr("Key: %1
").arg((*it).key); res += tr("Submitted data: %1

").arg((*it).rawData); } return res; } void AuditLogUiController::clear() { QDir dir(d->path); foreach (auto e, dir.entryList(QDir::Files | QDir::Readable)) { if (!e.endsWith(QLatin1String(".log"))) continue; dir.remove(e); } d->logEntryModel->reload(); } #include "auditloguicontroller.moc" gammaray-2.11.3/3rdparty/kuserfeedback/core/auditloguicontroller.h000066400000000000000000000053261413310443000253140ustar00rootroot00000000000000/* Copyright (C) 2017 Volker Krause Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef KUSERFEEDBACK_AUDITLOGUICONTROLLER_H #define KUSERFEEDBACK_AUDITLOGUICONTROLLER_H #include "kuserfeedbackcore_export.h" #include #include #include QT_BEGIN_NAMESPACE class QDateTime; QT_END_NAMESPACE namespace KUserFeedback { class AuditLogUiControllerPrivate; /** Widget/QtQuick-independent logic and data for the audit log browser UI. */ class KUSERFEEDBACKCORE_EXPORT AuditLogUiController : public QObject { Q_OBJECT /** Returns a model listing all log entries. * @see logEntryModel() */ Q_PROPERTY(QAbstractItemModel* logEntryModel READ logEntryModel CONSTANT) /** Returns @c true if there are log entires to display. */ Q_PROPERTY(bool hasLogEntries READ hasLogEntries NOTIFY logEntryCountChanged) public: AuditLogUiController(QObject *parent = nullptr); ~AuditLogUiController(); /** Returns @c true if there are log entires to display. */ bool hasLogEntries() const; /** Returns a model listing all telemetry data submission events. * Qt::UserRole contains the timestamp of the log entry for use in logEntry(). */ QAbstractItemModel* logEntryModel() const; /** Returns a formatted text for display of the specified log entry. */ Q_INVOKABLE QString logEntry(const QDateTime &dt) const; public Q_SLOTS: /** Delete all audit log entries. */ void clear(); Q_SIGNALS: /** Change notification for the hasLogEntries property. */ void logEntryCountChanged(); private: std::unique_ptr d; }; } #endif // KUSERFEEDBACK_AUDITLOGUICONTROLLER_H gammaray-2.11.3/3rdparty/kuserfeedback/core/compilerinfosource.cpp000066400000000000000000000045451413310443000253060ustar00rootroot00000000000000/* Copyright (C) 2017 Volker Krause Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "compilerinfosource.h" #include using namespace KUserFeedback; CompilerInfoSource::CompilerInfoSource() : AbstractDataSource(QStringLiteral("compiler"), Provider::BasicSystemInformation) { } QString CompilerInfoSource::description() const { return tr("The compiler used to build this application."); } #define STRINGIFY(x) #x #define INT2STR(x) STRINGIFY(x) QVariant CompilerInfoSource::data() { QVariantMap m; #ifdef Q_CC_GNU m.insert(QStringLiteral("type"), QStringLiteral("GCC")); m.insert(QStringLiteral("version"), QString::fromLatin1( "" INT2STR(__GNUC__) "." INT2STR(__GNUC_MINOR__))); #endif #ifdef Q_CC_CLANG m.insert(QStringLiteral("type"), QStringLiteral("Clang")); m.insert(QStringLiteral("version"), QString::fromLatin1( "" INT2STR(__clang_major__) "." INT2STR(__clang_minor__))); #endif #ifdef Q_CC_MSVC m.insert(QStringLiteral("type"), QStringLiteral("MSVC")); m.insert(QStringLiteral("version"), QString::fromLatin1( "" INT2STR(_MSC_VER))); #endif if (m.isEmpty()) m.insert(QStringLiteral("type"), QStringLiteral("unknown")); return m; } QString CompilerInfoSource::name() const { return tr("Compiler information"); } #undef STRINGIFY #undef INT2STR gammaray-2.11.3/3rdparty/kuserfeedback/core/compilerinfosource.h000066400000000000000000000041561413310443000247510ustar00rootroot00000000000000/* Copyright (C) 2017 Volker Krause Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef KUSERFEEDBACK_COMPILERINFOSOURCE_H #define KUSERFEEDBACK_COMPILERINFOSOURCE_H #include "abstractdatasource.h" namespace KUserFeedback { /*! Data source reporting which compiler was used to build this code. * @note This will report which compiler was used to build the feedback * library, which technically does not need to be the same as used for * the application code. This is particularly true for compilers that * don't break ABI regularly (such as Clang and GCC), so this information * is most reliable when this is not the case, e.g. with MSVC. * * The default telemetry mode for this source is Provider::BasicSystemInformation. */ class KUSERFEEDBACKCORE_EXPORT CompilerInfoSource : public AbstractDataSource { public: Q_DECLARE_TR_FUNCTIONS(KUserFeedback::CompilerInfoSource) public: CompilerInfoSource(); QString name() const override; QString description() const override; QVariant data() override; }; } #endif // KUSERFEEDBACK_COMPILERINFOSOURCE_H gammaray-2.11.3/3rdparty/kuserfeedback/core/feedbackconfiguicontroller.cpp000066400000000000000000000255011413310443000267460ustar00rootroot00000000000000/* Copyright (C) 2017 Volker Krause Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "feedbackconfiguicontroller.h" #include "abstractdatasource.h" #include #include #include #include #include using namespace KUserFeedback; namespace KUserFeedback { class FeedbackConfigUiControllerPrivate { public: FeedbackConfigUiControllerPrivate(); static QString appName(); Provider *provider; std::vector telemetryModeMap; }; } FeedbackConfigUiControllerPrivate::FeedbackConfigUiControllerPrivate() : provider(nullptr) { } QString FeedbackConfigUiControllerPrivate::appName() { return QGuiApplication::applicationDisplayName(); } FeedbackConfigUiController::FeedbackConfigUiController(QObject* parent) : QObject(parent) , d(new FeedbackConfigUiControllerPrivate) { } FeedbackConfigUiController::~FeedbackConfigUiController() { } Provider* FeedbackConfigUiController::feedbackProvider() const { return d->provider; } void FeedbackConfigUiController::setFeedbackProvider(Provider* provider) { if (d->provider == provider) return; d->provider = provider; d->telemetryModeMap.clear(); d->telemetryModeMap.reserve(5); d->telemetryModeMap.push_back(Provider::NoTelemetry); d->telemetryModeMap.push_back(Provider::BasicSystemInformation); d->telemetryModeMap.push_back(Provider::BasicUsageStatistics); d->telemetryModeMap.push_back(Provider::DetailedSystemInformation); d->telemetryModeMap.push_back(Provider::DetailedUsageStatistics); QSet supportedModes; supportedModes.reserve(d->telemetryModeMap.size()); supportedModes.insert(Provider::NoTelemetry); foreach (const auto &src, provider->dataSources()) supportedModes.insert(src->telemetryMode()); for (auto it = d->telemetryModeMap.begin(); it != d->telemetryModeMap.end();) { if (!supportedModes.contains(*it)) it = d->telemetryModeMap.erase(it); else ++it; } emit providerChanged(); } int FeedbackConfigUiController::telemetryModeCount() const { return d->telemetryModeMap.size(); } int FeedbackConfigUiController::surveyModeCount() const { return 3; } Provider::TelemetryMode FeedbackConfigUiController::telemetryIndexToMode(int index) const { if (index < 0 || index >= telemetryModeCount()) return Provider::NoTelemetry; return d->telemetryModeMap[index]; } int FeedbackConfigUiController::telemetryModeToIndex(Provider::TelemetryMode mode) const { const auto it = std::lower_bound(d->telemetryModeMap.begin(), d->telemetryModeMap.end(), mode); if (it == d->telemetryModeMap.end()) return 0; return std::distance(d->telemetryModeMap.begin(), it); } QString FeedbackConfigUiController::telemetryModeName(int telemetryIndex) const { switch (telemetryIndexToMode(telemetryIndex)) { case Provider::NoTelemetry: return tr("No telemetry"); case Provider::BasicSystemInformation: return tr("Basic system information"); case Provider::BasicUsageStatistics: return tr("Basic usage statistics"); case Provider::DetailedSystemInformation: return tr("Detailed system information"); case Provider::DetailedUsageStatistics: return tr("Detailed usage statistics"); } return {}; } QString FeedbackConfigUiController::telemetryModeDescription(int telemetryIndex) const { const auto name = d->appName(); if (name.isEmpty()) { switch (telemetryIndexToMode(telemetryIndex)) { case Provider::NoTelemetry: return tr( "We make this application for you. You can help us improve it by contributing information on how you use it. " "This allows us to make sure we focus on things that matter to you.\n" "Contributing statistics is of course entirely anonymous, will not use any kind of unique identifier and " "will not cover any data you process with this application." ); case Provider::BasicSystemInformation: return tr( "Share basic system information. " "No unique identification is included, nor data processed with the application." ); case Provider::BasicUsageStatistics: return tr( "Share basic system information and basic statistics on how often you use the application. " "No unique identification is included, nor data processed with the application." ); case Provider::DetailedSystemInformation: return tr( "Share basic statistics on how often you use the application, as well as detailed information about your system. " "No unique identification is included, nor data processed with the application." ); case Provider::DetailedUsageStatistics: return tr( "Share detailed system information and statistics on how often individual features of the application are used. " "No unique identification is included, nor data processed with the application." ); } } else { switch (telemetryIndexToMode(telemetryIndex)) { case Provider::NoTelemetry: return tr( "We make %1 for you. You can help us improve it by contributing information on how you use it. " "This allows us to make sure we focus on things that matter to you.\n" "Contributing statistics is of course entirely anonymous, will not use any kind of unique identifier and " "will not cover any data you process with %1." ).arg(name); case Provider::BasicSystemInformation: return tr( "Share basic system information. " "No unique identification is included, nor data processed with %1." ).arg(name); case Provider::BasicUsageStatistics: return tr( "Share basic system information and basic statistics on how often you use %1. " "No unique identification is included, nor data processed with %1." ).arg(name); case Provider::DetailedSystemInformation: return tr( "Share basic statistics on how often you use %1, as well as detailed information about your system. " "No unique identification is included, nor data processed with %1." ).arg(name); case Provider::DetailedUsageStatistics: return tr( "Share detailed system information and statistics on how often individual features of %1 are used. " "No unique identification is included, nor data processed with %1." ).arg(name); } } return QString(); } QString FeedbackConfigUiController::telemetryModeDetails(int telemetryIndex) const { if (telemetryIndex <= 0 || telemetryIndex >= telemetryModeCount()) return QString(); auto srcs = d->provider->dataSources(); std::stable_sort(srcs.begin(), srcs.end(), [](AbstractDataSource *lhs, AbstractDataSource *rhs) { return lhs->telemetryMode() < rhs->telemetryMode(); }); auto detailsStr = QStringLiteral("
    "); foreach (const auto *src, srcs) { if (telemetryIndex >= telemetryModeToIndex(src->telemetryMode()) && !src->description().isEmpty()) detailsStr += QStringLiteral("
  • ") + src->description() + QStringLiteral("
  • "); } return detailsStr + QStringLiteral("
"); } int FeedbackConfigUiController::surveyIndexToInterval(int index) const { switch (index) { case 0: return -1; case 1: return 90; case 2: return 0; } return -1; } int FeedbackConfigUiController::surveyIntervalToIndex(int interval) const { if (interval < 0) return 0; else if (interval >= 90) return 1; else return 2; } QString FeedbackConfigUiController::surveyModeDescription(int surveyIndex) const { const auto name = d->appName(); if (name.isEmpty()) { switch (surveyIndex) { case 0: return tr( "We make this application for you. In order to ensure it actually does what you need it to do we " "would like to ask you about your use cases and your feedback, in the form of a web survey." ); case 1: return tr( "I will occasionally participate in web surveys about the application, not more than four times a year though." ); case 2: return tr( "I will participate in web surveys whenever one is available. Surveys can of course be deferred or skipped." ); } } else { switch (surveyIndex) { case 0: return tr( "We make %1 for you. In order to ensure it actually does what you need it to do we " "would like to ask you about your use cases and your feedback, in the form of a web survey." ).arg(name); case 1: return tr( "I will occasionally participate in web surveys about %1, not more than four times a year though." ).arg(name); case 2: return tr( "I will participate in web surveys about %1 whenever one is available. Surveys can of course be deferred or skipped." ).arg(name); } } return QString(); } gammaray-2.11.3/3rdparty/kuserfeedback/core/feedbackconfiguicontroller.h000066400000000000000000000075021413310443000264140ustar00rootroot00000000000000/* Copyright (C) 2017 Volker Krause Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef KUSERFEEDBACK_FEEDBACKCONFIGUICONTROLLER_H #define KUSERFEEDBACK_FEEDBACKCONFIGUICONTROLLER_H #include "kuserfeedbackcore_export.h" #include "provider.h" #include #include namespace KUserFeedback { class FeedbackConfigUiControllerPrivate; class Provider; /*! Logic/behavior of the feedback configuration UI. * This is available for use in e.g. QtQuick-based UIs. * @see FeedbackConfigWidget */ class KUSERFEEDBACKCORE_EXPORT FeedbackConfigUiController : public QObject { Q_OBJECT /*! The Provider instance we are configuring. */ Q_PROPERTY(KUserFeedback::Provider* feedbackProvider READ feedbackProvider WRITE setFeedbackProvider NOTIFY providerChanged) /*! Amount of telemetry modes supported by the provider. */ Q_PROPERTY(int telemetryModeCount READ telemetryModeCount NOTIFY providerChanged) /*! Amount of supported survey modes. */ Q_PROPERTY(int surveyModeCount READ surveyModeCount CONSTANT) public: explicit FeedbackConfigUiController(QObject *parent = nullptr); ~FeedbackConfigUiController(); /*! Returns the feedback provider to be configured. */ Provider* feedbackProvider() const; /*! Set the feedback provider to configure. */ void setFeedbackProvider(Provider *provider); /*! Amount of supported telemetry modes. * This depends on what type of sources the provider actually has. */ int telemetryModeCount() const; /*! Amount of supported survey modes. */ int surveyModeCount() const; /*! Convert slider index to telemetry mode. */ Q_INVOKABLE KUserFeedback::Provider::TelemetryMode telemetryIndexToMode(int index) const; /*! Convert telemetry mode to slider index. */ Q_INVOKABLE int telemetryModeToIndex(KUserFeedback::Provider::TelemetryMode mode) const; /*! Telemetry mode short name. */ Q_INVOKABLE QString telemetryModeName(int telemetryIndex) const; /*! Telemetry mode explanation text. */ Q_INVOKABLE QString telemetryModeDescription(int telemetryIndex) const; /*! Detailed information about the data sources of the given telemetry mode index. */ Q_INVOKABLE QString telemetryModeDetails(int telemetryIndex) const; /*! Convert slider index to survey interval. */ Q_INVOKABLE int surveyIndexToInterval(int index) const; /*! Convert survey interval to slider index. */ Q_INVOKABLE int surveyIntervalToIndex(int interval) const; /*! Survey mode explanation text. */ Q_INVOKABLE QString surveyModeDescription(int surveyIndex) const; Q_SIGNALS: /*! A provider-related setting has changed. */ void providerChanged(); private: std::unique_ptr d; }; } #endif // KUSERFEEDBACK_FEEDBACKCONFIGUICONTROLLER_H gammaray-2.11.3/3rdparty/kuserfeedback/core/logging_p.h000066400000000000000000000025001413310443000227760ustar00rootroot00000000000000/* Copyright (C) 2017 Volker Krause Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef KUSERFEEDBACK_CORE_LOGGING_H #define KUSERFEEDBACK_CORE_LOGGING_H #include #include namespace KUserFeedback { Q_DECLARE_LOGGING_CATEGORY(Log) } #endif gammaray-2.11.3/3rdparty/kuserfeedback/core/openglinfosource.cpp000066400000000000000000000103301413310443000247450ustar00rootroot00000000000000/* Copyright (C) 2017 Volker Krause Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "openglinfosource.h" #include "openglinfosource_p.h" #include #ifndef QT_NO_OPENGL #include #include #include #include #endif using namespace KUserFeedback; OpenGLInfoSource::OpenGLInfoSource() : AbstractDataSource(QStringLiteral("opengl"), Provider::DetailedSystemInformation) { } QString OpenGLInfoSource::description() const { return tr("Information about type, version and vendor of the OpenGL stack."); } QVariant OpenGLInfoSource::data() { QVariantMap m; #ifndef QT_NO_OPENGL QOpenGLContext context; if (context.create()) { QWindow window; window.setSurfaceType(QSurface::OpenGLSurface); window.create(); context.makeCurrent(&window); QOpenGLFunctions functions(&context); m.insert(QStringLiteral("vendor"), OpenGLInfoSourcePrivate::normalizeVendor(reinterpret_cast(functions.glGetString(GL_VENDOR)))); m.insert(QStringLiteral("renderer"), OpenGLInfoSourcePrivate::normalizeRenderer(reinterpret_cast(functions.glGetString(GL_RENDERER)))); switch (context.openGLModuleType()) { case QOpenGLContext::LibGL: { m.insert(QStringLiteral("type"), QStringLiteral("GL")); #if defined(GL_MAJOR_VERSION) && defined(GL_MINOR_VERSION) int major = 0, minor = 0; functions.glGetIntegerv(GL_MAJOR_VERSION, &major); functions.glGetIntegerv(GL_MINOR_VERSION, &minor); // e.g. macOS legacy profiles return 0.0 here... if (major > 0) m.insert(QStringLiteral("version"), QString(QString::number(major) + QLatin1Char('.') + QString::number(minor))); #endif OpenGLInfoSourcePrivate::parseGLVersion(reinterpret_cast(functions.glGetString(GL_VERSION)), m); OpenGLInfoSourcePrivate::parseGLSLVersion(reinterpret_cast(functions.glGetString(GL_SHADING_LANGUAGE_VERSION)), m); break; } case QOpenGLContext::LibGLES: { m.insert(QStringLiteral("type"), QStringLiteral("GLES")); OpenGLInfoSourcePrivate::parseGLESVersion(reinterpret_cast(functions.glGetString(GL_VERSION)), m); OpenGLInfoSourcePrivate::parseESGLSLVersion(reinterpret_cast(functions.glGetString(GL_SHADING_LANGUAGE_VERSION)), m); break; } } switch (context.format().profile()) { case QSurfaceFormat::NoProfile: break; case QSurfaceFormat::CoreProfile: m.insert(QStringLiteral("profile"), QStringLiteral("core")); break; case QSurfaceFormat::CompatibilityProfile: m.insert(QStringLiteral("profile"), QStringLiteral("compat")); break; } return m; } #endif m.insert(QStringLiteral("type"), QStringLiteral("none")); return m; } QString OpenGLInfoSource::name() const { return tr("OpenGL information"); } gammaray-2.11.3/3rdparty/kuserfeedback/core/openglinfosource.h000066400000000000000000000034041413310443000244160ustar00rootroot00000000000000/* Copyright (C) 2017 Volker Krause Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef KUSERFEEDBACK_OPENGLINFOSOURCE_H #define KUSERFEEDBACK_OPENGLINFOSOURCE_H #include "abstractdatasource.h" namespace KUserFeedback { /*! Data source reporting type, version and vendor of the OpenGL stack. * * The default telemetry mode for this source is Provider::DetailedSystemInformation. */ class KUSERFEEDBACKCORE_EXPORT OpenGLInfoSource : public AbstractDataSource { public: Q_DECLARE_TR_FUNCTIONS(KUserFeedback::OpenGLInfoSource) public: OpenGLInfoSource(); QString name() const override; QString description() const override; QVariant data() override; }; } #endif // KUSERFEEDBACK_OPENGLINFOSOURCE_H gammaray-2.11.3/3rdparty/kuserfeedback/core/openglinfosource_p.h000066400000000000000000000141251413310443000247370ustar00rootroot00000000000000/* Copyright (C) 2017 Volker Krause Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef KUSERFEEDBACK_OPENGLINFOSOURCE_P_H #define KUSERFEEDBACK_OPENGLINFOSOURCE_P_H #include namespace KUserFeedback { // Internal functions made accessible for unit tests class OpenGLInfoSourcePrivate { public: static inline void parseGLVersion(const char *version, QVariantMap &m) { auto vendorVersion = QString::fromLocal8Bit(version); const auto idx = vendorVersion.indexOf(QLatin1Char(' ')); QString glVersion; if (idx > 0) { glVersion = vendorVersion.left(idx); vendorVersion = vendorVersion.mid(idx + 1); if (!vendorVersion.isEmpty()) m.insert(QStringLiteral("vendorVersion"), normalizeVendorVersionString(vendorVersion)); } else { glVersion = vendorVersion; } // in case glGetIntegerv(GL_MAJOR_VERSION) failed... if (!m.contains(QLatin1String("version")) && !glVersion.isEmpty()) m.insert(QStringLiteral("version"), glVersion); } static inline void parseGLESVersion(const char *version, QVariantMap &m) { auto rawVersion = QString::fromLocal8Bit(version); if (!rawVersion.startsWith(QLatin1String("OpenGL ES "))) { m.insert(QStringLiteral("version"), QStringLiteral("unknown")); m.insert(QStringLiteral("vendorVersion"), rawVersion); } else { rawVersion = rawVersion.mid(10); const auto idx = rawVersion.indexOf(QLatin1Char(' ')); if (idx > 0) { const auto vendorVersion = rawVersion.mid(idx + 1); if (!vendorVersion.isEmpty()) m.insert(QStringLiteral("vendorVersion"), normalizeVendorVersionString(vendorVersion)); m.insert(QStringLiteral("version"), rawVersion.left(idx)); } else { m.insert(QStringLiteral("version"), rawVersion); } } } static inline void parseGLSLVersion(const char *version, QVariantMap &m) { auto v = QString::fromLocal8Bit(version); const auto idx = v.indexOf(QLatin1Char(' ')); if (idx > 0) { const auto glslVersion = v.left(idx); if (!glslVersion.isEmpty()) m.insert(QStringLiteral("glslVersion"), glslVersion); else m.insert(QStringLiteral("glslVersion"), v); } else { m.insert(QStringLiteral("glslVersion"), v); } } static inline void parseESGLSLVersion(const char *version, QVariantMap &m) { auto v = QString::fromLocal8Bit(version); if (!v.startsWith(QLatin1String("OpenGL ES GLSL ES "))) { m.insert(QStringLiteral("glslVersion"), v); } else { v = v.mid(18); const auto idx = v.indexOf(QLatin1Char(' ')); if (idx > 0) { const auto glslVersion = v.left(idx); if (!glslVersion.isEmpty()) m.insert(QStringLiteral("glslVersion"), glslVersion); else m.insert(QStringLiteral("glslVersion"), v); } else { m.insert(QStringLiteral("glslVersion"), v); } } } static inline QString normalizeVendor(const char *vendor) { const auto v = QString::fromLocal8Bit(vendor); if (v.startsWith(QLatin1String("Intel "))) return QStringLiteral("Intel"); return v; } static inline QString normalizeRenderer(const char *renderer) { auto r = QString::fromLocal8Bit(renderer); // remove trademark indicators r.remove(QLatin1String("(R)"), Qt::CaseInsensitive); r.remove(QLatin1String("(TM)"), Qt::CaseInsensitive); // remove vendor prefixes, we already have that in the vendor field if (r.startsWith(QLatin1String("Mesa DRI "))) r = r.mid(9); if (r.startsWith(QLatin1String("Intel "))) r = r.mid(6); if (r.startsWith(QLatin1String("NVIDIA "))) r = r.mid(7); // remove excessive details that could enable fingerprinting if (r.startsWith(QLatin1String("ANGLE ")) || r.startsWith(QLatin1String("Gallium ")) || r.startsWith(QLatin1String("AMD "))) r = stripDetails(r); // strip macOS adding " OpenGL Engine" at the end if (r.endsWith(QLatin1String(" OpenGL Engine"))) r = r.left(r.size() - 14); return r.simplified(); } private: static inline QString normalizeVendorVersionString(const QString &s) { if (s.startsWith(QLatin1Char('(')) && s.endsWith(QLatin1Char(')'))) return s.mid(1, s.size() - 2); if (s.startsWith(QLatin1String("- "))) return s.mid(2); return s; } static inline QString stripDetails(const QString &s) { auto idx = s.indexOf(QLatin1String(" (")); if (idx < 1 || !s.endsWith(QLatin1Char(')'))) return s; return s.left(idx); } }; } #endif // KUSERFEEDBACK_OPENGLINFOSOURCE_P_H gammaray-2.11.3/3rdparty/kuserfeedback/core/platforminfosource.cpp000066400000000000000000000061141413310443000253120ustar00rootroot00000000000000/* Copyright (C) 2016 Volker Krause Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "platforminfosource.h" #include #include using namespace KUserFeedback; PlatformInfoSource::PlatformInfoSource() : AbstractDataSource(QStringLiteral("platform"), Provider::BasicSystemInformation) { } QString PlatformInfoSource::description() const { return tr("Type and version of the operating system."); } QVariant PlatformInfoSource::data() { QVariantMap m; #if (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)) // on Linux productType() is the distro name m.insert(QStringLiteral("os"), QStringLiteral("linux")); // openSUSE Tumbleweed has the current date as version number, that is a bit too precise for us if (QSysInfo::productType() == QLatin1String("opensuse") && QSysInfo::productVersion().startsWith(QLatin1String("201"))) { // old form in use until early 2018 m.insert(QStringLiteral("version"), QString(QSysInfo::productType() + QLatin1String("-tumbleweed"))); } else if (QSysInfo::productType() == QLatin1String("opensuse-tumbleweed")) { // new form in use since early 2018 m.insert(QStringLiteral("version"), QSysInfo::productType()); } // Arch and other rolling-release distros set productVersion to "unknown" else if (QSysInfo::productVersion() == QLatin1String("unknown")) { m.insert(QStringLiteral("version"), QSysInfo::productType()); } else { m.insert(QStringLiteral("version"), QString(QSysInfo::productType() + QLatin1Char('-') + QSysInfo::productVersion())); } #elif defined(Q_OS_MAC) // QSysInfo::productType() on macOS alternates between "osx" and "macos"... m.insert(QStringLiteral("os"), QStringLiteral("macos")); m.insert(QStringLiteral("version"), QSysInfo::productVersion()); #else m.insert(QStringLiteral("os"), QSysInfo::productType()); m.insert(QStringLiteral("version"), QSysInfo::productVersion()); #endif return m; } QString PlatformInfoSource::name() const { return tr("Platform information"); } gammaray-2.11.3/3rdparty/kuserfeedback/core/platforminfosource.h000066400000000000000000000037311413310443000247610ustar00rootroot00000000000000/* Copyright (C) 2016 Volker Krause Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef KUSERFEEDBACK_PLATFORMINFOSOURCE_H #define KUSERFEEDBACK_PLATFORMINFOSOURCE_H #include "abstractdatasource.h" namespace KUserFeedback { /*! Reports information about the platform the application is running on. * This includes two string data fields: * - platform.os: the operating system name * - platform.version: the operating system or distribution version. * * The default telemetry mode for this source is Provider::BasicSystemInformation. */ class KUSERFEEDBACKCORE_EXPORT PlatformInfoSource : public AbstractDataSource { Q_DECLARE_TR_FUNCTIONS(KUserFeedback::PlatformInfoSource) public: /*! Create a new platform information source. */ PlatformInfoSource(); QString name() const override; QString description() const override; QVariant data() override; }; } #endif // KUSERFEEDBACK_PLATFORMINFOSOURCE_H gammaray-2.11.3/3rdparty/kuserfeedback/core/provider.cpp000066400000000000000000000550001413310443000232210ustar00rootroot00000000000000/* Copyright (C) 2016 Volker Krause Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include "logging_p.h" #include "provider.h" #include "provider_p.h" #include "abstractdatasource.h" #include "startcountsource.h" #include "surveyinfo.h" #include "usagetimesource.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace KUserFeedback { #if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0) Q_LOGGING_CATEGORY(Log, "org.kde.UserFeedback", QtInfoMsg) #else Q_LOGGING_CATEGORY(Log, "org.kde.UserFeedback") #endif } using namespace KUserFeedback; ProviderPrivate::ProviderPrivate(Provider *qq) : q(qq) , networkAccessManager(nullptr) , redirectCount(0) , submissionInterval(-1) , telemetryMode(Provider::NoTelemetry) , surveyInterval(-1) , startCount(0) , usageTime(0) , encouragementStarts(-1) , encouragementTime(-1) , encouragementDelay(300) , encouragementInterval(-1) , backoffIntervalMinutes(-1) { submissionTimer.setSingleShot(true); QObject::connect(&submissionTimer, &QTimer::timeout, q, &Provider::submit); startTime.start(); encouragementTimer.setSingleShot(true); QObject::connect(&encouragementTimer, &QTimer::timeout, q, [this]() { emitShowEncouragementMessage(); }); } ProviderPrivate::~ProviderPrivate() { qDeleteAll(dataSources); } int ProviderPrivate::currentApplicationTime() const { return usageTime + (startTime.elapsed() / 1000); } static QMetaEnum telemetryModeEnum() { const auto idx = Provider::staticMetaObject.indexOfEnumerator("TelemetryMode"); Q_ASSERT(idx >= 0); return Provider::staticMetaObject.enumerator(idx); } std::unique_ptr ProviderPrivate::makeSettings() const { // attempt to put our settings next to the application ones, // so replicate how QSettings handles this auto org = #ifdef Q_OS_MAC QCoreApplication::organizationDomain().isEmpty() ? QCoreApplication::organizationName() : QCoreApplication::organizationDomain(); #else QCoreApplication::organizationName().isEmpty() ? QCoreApplication::organizationDomain() : QCoreApplication::organizationName(); #endif if (org.isEmpty()) org = QLatin1String("Unknown Organization"); std::unique_ptr s(new QSettings(org, QStringLiteral("UserFeedback.") + productId)); return s; } std::unique_ptr ProviderPrivate::makeGlobalSettings() const { const auto org = #ifdef Q_OS_MAC QStringLiteral("kde.org"); #else QStringLiteral("KDE"); #endif std::unique_ptr s(new QSettings(org, QStringLiteral("UserFeedback"))); return s; } void ProviderPrivate::load() { auto s = makeSettings(); s->beginGroup(QStringLiteral("UserFeedback")); lastSubmitTime = s->value(QStringLiteral("LastSubmission")).toDateTime(); const auto modeStr = s->value(QStringLiteral("StatisticsCollectionMode")).toByteArray(); telemetryMode = static_cast(std::max(telemetryModeEnum().keyToValue(modeStr.constData()), 0)); surveyInterval = s->value(QStringLiteral("SurveyInterval"), -1).toInt(); lastSurveyTime = s->value(QStringLiteral("LastSurvey")).toDateTime(); completedSurveys = s->value(QStringLiteral("CompletedSurveys"), QStringList()).toStringList(); startCount = std::max(s->value(QStringLiteral("ApplicationStartCount"), 0).toInt(), 0); usageTime = std::max(s->value(QStringLiteral("ApplicationTime"), 0).toInt(), 0); lastEncouragementTime = s->value(QStringLiteral("LastEncouragement")).toDateTime(); s->endGroup(); foreach (auto source, dataSources) { s->beginGroup(QStringLiteral("Source-") + source->id()); source->load(s.get()); s->endGroup(); } auto g = makeGlobalSettings(); g->beginGroup(QStringLiteral("UserFeedback")); lastSurveyTime = std::max(g->value(QStringLiteral("LastSurvey")).toDateTime(), lastSurveyTime); lastEncouragementTime = std::max(g->value(QStringLiteral("LastEncouragement")).toDateTime(), lastEncouragementTime); } void ProviderPrivate::store() { auto s = makeSettings(); s->beginGroup(QStringLiteral("UserFeedback")); // another process might have changed this, so read the base value first before writing usageTime = std::max(s->value(QStringLiteral("ApplicationTime"), 0).toInt(), usageTime); s->setValue(QStringLiteral("ApplicationTime"), currentApplicationTime()); usageTime = currentApplicationTime(); startTime.restart(); s->endGroup(); foreach (auto source, dataSources) { s->beginGroup(QStringLiteral("Source-") + source->id()); source->store(s.get()); s->endGroup(); } } void ProviderPrivate::storeOne(const QString &key, const QVariant &value) { auto s = makeSettings(); s->beginGroup(QStringLiteral("UserFeedback")); s->setValue(key, value); } void ProviderPrivate::storeOneGlobal(const QString &key, const QVariant &value) { auto s = makeGlobalSettings(); s->beginGroup(QStringLiteral("UserFeedback")); s->setValue(key, value); } void ProviderPrivate::aboutToQuit() { store(); } bool ProviderPrivate::isValidSource(AbstractDataSource *source) const { if (source->id().isEmpty()) { qCWarning(Log) << "Skipping data source with empty name!"; return false; } if (source->telemetryMode() == Provider::NoTelemetry) { qCWarning(Log) << "Source" << source->id() << "attempts to report data unconditionally, ignoring!"; return false; } if (source->description().isEmpty()) { qCWarning(Log) << "Source" << source->id() << "has no description, ignoring!"; return false; } Q_ASSERT(!source->id().isEmpty()); Q_ASSERT(source->telemetryMode() != Provider::NoTelemetry); Q_ASSERT(!source->description().isEmpty()); return true; } QByteArray ProviderPrivate::jsonData(Provider::TelemetryMode mode) const { QJsonObject obj; if (mode != Provider::NoTelemetry) { foreach (auto source, dataSources) { if (!isValidSource(source) || !source->isActive()) continue; if (mode < source->telemetryMode()) continue; const auto data = source->data(); if (data.canConvert()) obj.insert(source->id(), QJsonObject::fromVariantMap(data.toMap())); else if (data.canConvert()) obj.insert(source->id(), QJsonArray::fromVariantList(data.value())); else qCWarning(Log) << "wrong type for" << source->id() << data; } } QJsonDocument doc(obj); return doc.toJson(); } void ProviderPrivate::scheduleNextSubmission(qint64 minTime) { submissionTimer.stop(); if (!q->isEnabled()) return; if (submissionInterval <= 0 || (telemetryMode == Provider::NoTelemetry && surveyInterval < 0)) return; if (minTime == 0) { // If this is a regularly scheduled submission reset the backoff backoffIntervalMinutes = -1; } Q_ASSERT(submissionInterval > 0); const auto nextSubmission = lastSubmitTime.addDays(submissionInterval); const auto now = QDateTime::currentDateTime(); submissionTimer.start(std::max(minTime, now.msecsTo(nextSubmission))); } void ProviderPrivate::submitFinished(QNetworkReply *reply) { reply->deleteLater(); if (reply->error() != QNetworkReply::NoError) { if (backoffIntervalMinutes == -1) { backoffIntervalMinutes = 2; } else { backoffIntervalMinutes = backoffIntervalMinutes * 2; } qCWarning(Log) << "failed to submit user feedback:" << reply->errorString() << reply->readAll() << ". Calling scheduleNextSubmission with minTime" << backoffIntervalMinutes << "minutes"; scheduleNextSubmission(backoffIntervalMinutes * 60000ll); return; } const auto redirectTarget = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl(); if (redirectTarget.isValid()) { if (++redirectCount >= 20) { qCWarning(Log) << "Redirect loop on" << reply->url().resolved(redirectTarget).toString(); return; } submit(reply->url().resolved(redirectTarget)); return; } lastSubmitTime = QDateTime::currentDateTime(); auto s = makeSettings(); s->beginGroup(QStringLiteral("UserFeedback")); s->setValue(QStringLiteral("LastSubmission"), lastSubmitTime); s->endGroup(); writeAuditLog(lastSubmitTime); // reset source counters foreach (auto source, dataSources) { s->beginGroup(QStringLiteral("Source-") + source->id()); source->reset(s.get()); s->endGroup(); } const auto obj = QJsonDocument::fromJson(reply->readAll()).object(); const auto it = obj.find(QLatin1String("surveys")); if (it != obj.end() && surveyInterval >= 0) { const auto a = it.value().toArray(); qCDebug(Log) << "received" << a.size() << "surveys"; foreach(const auto &s, a) { const auto survey = SurveyInfo::fromJson(s.toObject()); if (selectSurvey(survey)) break; } } scheduleNextSubmission(); } QVariant ProviderPrivate::sourceData(const QString& sourceId) const { foreach (auto src, dataSources) { if (src->id() == sourceId) return src->data(); } return QVariant(); } bool ProviderPrivate::selectSurvey(const SurveyInfo &survey) const { qCDebug(Log) << "got survey:" << survey.url() << survey.target(); if (!q->isEnabled() || !survey.isValid() || completedSurveys.contains(survey.uuid().toString())) return false; if (surveyInterval != 0 && lastSurveyTime.addDays(surveyInterval) > QDateTime::currentDateTime()) return false; if (!survey.target().isEmpty()) { SurveyTargetExpressionParser parser; if (!parser.parse(survey.target())) { qCDebug(Log) << "failed to parse target expression"; return false; } SurveyTargetExpressionEvaluator eval; eval.setDataProvider(this); if (!eval.evaluate(parser.expression())) return false; } qCDebug(Log) << "picked survey:" << survey.url(); emit q->surveyAvailable(survey); return true; } Provider::TelemetryMode ProviderPrivate::highestTelemetryMode() const { auto mode = Provider::NoTelemetry; foreach (auto src, dataSources) mode = std::max(mode, src->telemetryMode()); return mode; } void ProviderPrivate::scheduleEncouragement() { encouragementTimer.stop(); if (!q->isEnabled()) return; // already done, not repetition if (lastEncouragementTime.isValid() && encouragementInterval <= 0) return; if (encouragementStarts < 0 && encouragementTime < 0) // encouragement disabled return; if (encouragementStarts > startCount) // we need more starts return; if (telemetryMode >= highestTelemetryMode() && surveyInterval == 0) // already everything enabled return; // no repetition if some feedback is enabled if (lastEncouragementTime.isValid() && (telemetryMode > Provider::NoTelemetry || surveyInterval >= 0)) return; Q_ASSERT(encouragementDelay >= 0); int timeToEncouragement = encouragementDelay; if (encouragementTime > 0) timeToEncouragement = std::max(timeToEncouragement, encouragementTime - currentApplicationTime()); if (lastEncouragementTime.isValid()) { Q_ASSERT(encouragementInterval > 0); const auto targetTime = lastEncouragementTime.addDays(encouragementInterval); timeToEncouragement = std::max(timeToEncouragement, (int)QDateTime::currentDateTime().secsTo(targetTime)); } encouragementTimer.start(timeToEncouragement * 1000); } void ProviderPrivate::emitShowEncouragementMessage() { lastEncouragementTime = QDateTime::currentDateTime(); // TODO make this explicit, in case the host application decides to delay? storeOne(QStringLiteral("LastEncouragement"), lastEncouragementTime); storeOneGlobal(QStringLiteral("LastEncouragement"), lastEncouragementTime); emit q->showEncouragementMessage(); } Provider::Provider(QObject *parent) : QObject(parent), d(new ProviderPrivate(this)) { qCDebug(Log); connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, this, [this]() { d->aboutToQuit(); }); auto domain = QCoreApplication::organizationDomain().split(QLatin1Char('.')); std::reverse(domain.begin(), domain.end()); auto id = domain.join(QLatin1String(".")); if (!id.isEmpty()) id += QLatin1Char('.'); id += QCoreApplication::applicationName(); setProductIdentifier(id); } Provider::~Provider() { delete d; } bool Provider::isEnabled() const { auto s = d->makeGlobalSettings(); s->beginGroup(QStringLiteral("UserFeedback")); return s->value(QStringLiteral("Enabled"), true).toBool(); } void Provider::setEnabled(bool enabled) { if (enabled == isEnabled()) return; d->storeOneGlobal(QStringLiteral("Enabled"), enabled); emit enabledChanged(); } QString Provider::productIdentifier() const { return d->productId; } void Provider::setProductIdentifier(const QString &productId) { Q_ASSERT(!productId.isEmpty()); if (productId == d->productId) return; d->productId = productId; d->load(); d->startCount++; d->storeOne(QStringLiteral("ApplicationStartCount"), d->startCount); emit providerSettingsChanged(); d->scheduleEncouragement(); d->scheduleNextSubmission(); } QUrl Provider::feedbackServer() const { return d->serverUrl; } void Provider::setFeedbackServer(const QUrl &url) { if (d->serverUrl == url) return; d->serverUrl = url; emit providerSettingsChanged(); } int Provider::submissionInterval() const { return d->submissionInterval; } void Provider::setSubmissionInterval(int days) { if (d->submissionInterval == days) return; d->submissionInterval = days; emit providerSettingsChanged(); d->scheduleNextSubmission(); } Provider::TelemetryMode Provider::telemetryMode() const { return d->telemetryMode; } void Provider::setTelemetryMode(TelemetryMode mode) { if (d->telemetryMode == mode) return; d->telemetryMode = mode; d->storeOne(QStringLiteral("StatisticsCollectionMode"), QString::fromLatin1(telemetryModeEnum().valueToKey(d->telemetryMode))); d->scheduleNextSubmission(); d->scheduleEncouragement(); emit telemetryModeChanged(); } void Provider::addDataSource(AbstractDataSource *source) { // special cases for sources where we track the data here, as it's needed even if we don't report it if (auto countSrc = dynamic_cast(source)) countSrc->setProvider(d); if (auto timeSrc = dynamic_cast(source)) timeSrc->setProvider(d); d->dataSources.push_back(source); d->dataSourcesById[source->id()] = source; auto s = d->makeSettings(); s->beginGroup(QStringLiteral("Source-") + source->id()); source->load(s.get()); } QVector Provider::dataSources() const { return d->dataSources; } AbstractDataSource *Provider::dataSource(const QString &id) const { auto it = d->dataSourcesById.find(id); return it != std::end(d->dataSourcesById) ? *it : nullptr; } int Provider::surveyInterval() const { return d->surveyInterval; } void Provider::setSurveyInterval(int days) { if (d->surveyInterval == days) return; d->surveyInterval = days; d->storeOne(QStringLiteral("SurveyInterval"), d->surveyInterval); d->scheduleNextSubmission(); d->scheduleEncouragement(); emit surveyIntervalChanged(); } int Provider::applicationStartsUntilEncouragement() const { return d->encouragementStarts; } void Provider::setApplicationStartsUntilEncouragement(int starts) { if (d->encouragementStarts == starts) return; d->encouragementStarts = starts; emit providerSettingsChanged(); d->scheduleEncouragement(); } int Provider::applicationUsageTimeUntilEncouragement() const { return d->encouragementTime; } void Provider::setApplicationUsageTimeUntilEncouragement(int secs) { if (d->encouragementTime == secs) return; d->encouragementTime = secs; emit providerSettingsChanged(); d->scheduleEncouragement(); } int Provider::encouragementDelay() const { return d->encouragementDelay; } void Provider::setEncouragementDelay(int secs) { if (d->encouragementDelay == secs) return; d->encouragementDelay = std::max(0, secs); emit providerSettingsChanged(); d->scheduleEncouragement(); } int Provider::encouragementInterval() const { return d->encouragementInterval; } void Provider::setEncouragementInterval(int days) { if (d->encouragementInterval == days) return; d->encouragementInterval = days; emit providerSettingsChanged(); d->scheduleEncouragement(); } void Provider::surveyCompleted(const SurveyInfo &info) { d->completedSurveys.push_back(info.uuid().toString()); d->lastSurveyTime = QDateTime::currentDateTime(); auto s = d->makeSettings(); s->beginGroup(QStringLiteral("UserFeedback")); s->setValue(QStringLiteral("LastSurvey"), d->lastSurveyTime); s->setValue(QStringLiteral("CompletedSurveys"), d->completedSurveys); d->storeOneGlobal(QStringLiteral("LastSurvey"), d->lastSurveyTime); } void Provider::load() { d->load(); } void Provider::store() { d->store(); } void Provider::submit() { if (!isEnabled()) { qCWarning(Log) << "Global kill switch is enabled"; return; } if (d->productId.isEmpty()) { qCWarning(Log) << "No productId specified!"; return; } if (!d->serverUrl.isValid()) { qCWarning(Log) << "No feedback server URL specified!"; return; } if (!d->networkAccessManager) d->networkAccessManager = new QNetworkAccessManager(this); auto url = d->serverUrl; auto path = d->serverUrl.path(); if (!path.endsWith(QLatin1Char('/'))) path += QLatin1Char('/'); path += QStringLiteral("receiver/submit/") + d->productId; url.setPath(path); d->submitProbe(url); } void ProviderPrivate::submit(const QUrl &url) { QNetworkRequest request(url); request.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("application/json")); request.setHeader(QNetworkRequest::UserAgentHeader, QString(QStringLiteral("KUserFeedback/") + QStringLiteral(KUSERFEEDBACK_VERSION_STRING))); auto reply = networkAccessManager->post(request, jsonData(telemetryMode)); QObject::connect(reply, &QNetworkReply::finished, q, [this, reply]() { submitFinished(reply); }); } void ProviderPrivate::submitProbe(const QUrl &url) { QNetworkRequest request(url); request.setHeader(QNetworkRequest::UserAgentHeader, QString(QStringLiteral("KUserFeedback/") + QStringLiteral(KUSERFEEDBACK_VERSION_STRING))); auto reply = networkAccessManager->get(request); QObject::connect(reply, &QNetworkReply::finished, q, [this, reply]() { submitProbeFinished(reply); }); } void ProviderPrivate::submitProbeFinished(QNetworkReply *reply) { reply->deleteLater(); if (reply->error() != QNetworkReply::NoError) { qCWarning(Log) << "failed to probe user feedback submission interface:" << reply->errorString() << reply->readAll(); return; } const auto redirectTarget = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl(); if (redirectTarget.isValid()) { if (++redirectCount >= 20) { qCWarning(Log) << "Redirect loop on" << reply->url().resolved(redirectTarget).toString(); return; } submitProbe(reply->url().resolved(redirectTarget)); return; } submit(reply->url()); } void ProviderPrivate::writeAuditLog(const QDateTime &dt) { const QString path = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + QStringLiteral("/kuserfeedback/audit"); QDir().mkpath(path); QJsonObject docObj; foreach (auto source, dataSources) { if (!isValidSource(source) || !source->isActive() || telemetryMode < source->telemetryMode()) continue; QJsonObject obj; const auto data = source->data(); if (data.canConvert()) obj.insert(QLatin1String("data"), QJsonObject::fromVariantMap(data.toMap())); else if (data.canConvert()) obj.insert(QLatin1String("data"), QJsonArray::fromVariantList(data.value())); if (obj.isEmpty()) continue; obj.insert(QLatin1String("telemetryMode"), QString::fromLatin1(telemetryModeEnum().valueToKey(source->telemetryMode()))); obj.insert(QLatin1String("description"), source->description()); docObj.insert(source->id(), obj); } QFile file(path + QLatin1Char('/') + dt.toString(QStringLiteral("yyyyMMdd-hhmmss")) + QStringLiteral(".log")); if (!file.open(QFile::WriteOnly)) { qCWarning(Log) << "Unable to open audit log file:" << file.fileName() << file.errorString(); return; } QJsonDocument doc(docObj); file.write(doc.toJson()); qCDebug(Log) << "Audit log written:" << file.fileName(); } #include "moc_provider.cpp" gammaray-2.11.3/3rdparty/kuserfeedback/core/provider.h000066400000000000000000000302671413310443000226760ustar00rootroot00000000000000/* Copyright (C) 2016 Volker Krause Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef KUSERFEEDBACK_PROVIDER_H #define KUSERFEEDBACK_PROVIDER_H #include "kuserfeedbackcore_export.h" #include #include #include namespace KUserFeedback { class AbstractDataSource; class ProviderPrivate; class SurveyInfo; /*! The central object managing data sources and transmitting feedback to the server. * * The defaults for this class are very defensive, so in order to make it actually * operational and submit data, there is a number of settings you need to set in * code, namely submission intervals, encouragement settings and adding data sources. * The settings about what data to submit (telemetryMode) and how often * to bother the user with surveys (surveyInterval) should not be set to hardcoded * values in code, but left as choices to the user. */ class KUSERFEEDBACKCORE_EXPORT Provider : public QObject { Q_OBJECT /*! The global enabled state of the feedback functionality. * If this is @c false, all feedback functionality has to be disabled completely. */ Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY enabledChanged) /*! The interval in which the user accepts surveys. * This should be configurable for the user. * @c -1 indicates surveys are disabled. * @see surveyInterval(), setSurveyInterval() */ Q_PROPERTY(int surveyInterval READ surveyInterval WRITE setSurveyInterval NOTIFY surveyIntervalChanged) /*! The telemetry mode the user has configured. * This should be configurable for the user. * @see telemetryMode(), setTelemetryMode() */ Q_PROPERTY(TelemetryMode telemetryMode READ telemetryMode WRITE setTelemetryMode NOTIFY telemetryModeChanged) /*! Unique product id as set on the feedback server. * @see setProductIdentifier */ Q_PROPERTY(QString productIdentifier READ productIdentifier WRITE setProductIdentifier NOTIFY providerSettingsChanged) /*! URL of the feedback server. * @see setFeedbackServer */ Q_PROPERTY(QUrl feedbackServer READ feedbackServer WRITE setFeedbackServer NOTIFY providerSettingsChanged) /*! Submission interval in days. * @see setSubmissionInterval */ Q_PROPERTY(int submissionInterval READ submissionInterval WRITE setSubmissionInterval NOTIFY providerSettingsChanged) /*! Times the application has to be started before an encouragement message is shown. * @see setApplicationStartsUntilEncouragement */ Q_PROPERTY(int applicationStartsUntilEncouragement READ applicationStartsUntilEncouragement WRITE setApplicationStartsUntilEncouragement NOTIFY providerSettingsChanged) /*! Application usage time in seconds before an encouragement message is shown. * @see setApplicationUsageTimeUntilEncouragement */ Q_PROPERTY(int applicationUsageTimeUntilEncouragement READ applicationUsageTimeUntilEncouragement WRITE setApplicationUsageTimeUntilEncouragement NOTIFY providerSettingsChanged) /*! Encouragement delay after application start in seconds. * @see setEncouragementDelay */ Q_PROPERTY(int encouragementDelay READ encouragementDelay WRITE setEncouragementDelay NOTIFY providerSettingsChanged) /*! Encouragement interval. * @see setEncouragementInterval */ Q_PROPERTY(int encouragementInterval READ encouragementInterval WRITE setEncouragementInterval NOTIFY providerSettingsChanged) public: /*! Telemetry collection modes. * Colleciton modes are inclusive, ie. higher modes always imply data from * lower modes too. */ enum TelemetryMode { NoTelemetry, ///< Transmit no data at all. BasicSystemInformation = 0x10, ///< Transmit basic information about the system. BasicUsageStatistics = 0x20, ///< Transmit basic usage statistics. DetailedSystemInformation = 0x30, ///< Transmit detailed system information. DetailedUsageStatistics = 0x40, ///< Transmit detailed usage statistics. }; #if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0) Q_ENUM(TelemetryMode) #else Q_ENUMS(TelemetryMode) #endif /*! Create a new feedback provider. * @param parent The parent object. */ explicit Provider(QObject *parent = nullptr); ~Provider(); /*! Returns whether feedback functionality is enabled on this system. * This should be checked everywhere showing feedback UI to the user * to respect the global "kill switch" for this. Provider does check * this internally for encouragements, surveys and telemetry submission. */ bool isEnabled() const; /*! Set the global (user-wide) activation state for feedback functionality. * @see isEnabled */ void setEnabled(bool enabled); /*! Returns the current product identifier. */ QString productIdentifier() const; /*! Set the product identifier. * This is used to distinguish independent products on the same server. * If this is not specified, the product identifier is dervied from the application name * organisation domain specified in QCoreApplication. * @param productId Unique product identifier, as configured on the feedback server. */ void setProductIdentifier(const QString &productId); /*! Returns the current feedback server URL. */ QUrl feedbackServer() const; /*! Set the feedback server URL. * This must be called with an appropriate URL for this class to be operational. * @param url The URL of the feedback server. */ void setFeedbackServer(const QUrl &url); /*! Returns the current submission interval. * @return Days between telemetry submissions, or -1 if submission is off. */ int submissionInterval() const; /*! Set the automatic submission interval in days. * This must be called with a positive number for this class to be operational, * as the default is -1 (no submission ever). */ void setSubmissionInterval(int days); /*! Returns the current telemetry collection mode. * The default is NoTelemetry. */ TelemetryMode telemetryMode() const; /*! Set which telemetry data should be submitted. */ void setTelemetryMode(TelemetryMode mode); /*! Adds a data source for telemetry data collection. * @param source The data source to add. The Provider takes ownership of @p source. */ void addDataSource(AbstractDataSource *source); /*! Returns all data sources that have been added to this provider. * @see addDataSource */ QVector dataSources() const; /*! Returns a data source with matched @p id * @param id data source unique identifier * @return pointer to found data source or nullptr if data source is not found */ AbstractDataSource *dataSource(const QString &id) const; /*! Returns the minimum time between two surveys in days. * The default is -1 (no surveys enabled). */ int surveyInterval() const; /*! Sets the minimum time in days between two surveys. * @c -1 indicates no surveys should be requested. * @c 0 indicates no minimum time between surveys at all (i.e. bother the user as often as you want). */ void setSurveyInterval(int days); /*! Returns the amount of application starts before an encouragement message is shown. */ int applicationStartsUntilEncouragement() const; /*! Set the amount of application starts until the encouragement message should be shown. * The default is -1, ie. no encouragement based on application starts. * @param starts The amount of application starts after which an encouragement * message should be displayed. */ void setApplicationStartsUntilEncouragement(int starts); /*! Returns the amount of application usage time before an encouragement message is shown. */ int applicationUsageTimeUntilEncouragement() const; /*! Set the amount of usage time until the encouragement message should be shown. * The default is -1, ie. no encouragement based on application usage time. * @param secs Amount of seconds until the encouragement should be shown. */ void setApplicationUsageTimeUntilEncouragement(int secs); /*! Returns the current encouragement delay in seconds. */ int encouragementDelay() const; /*! Set the delay after application start for the earliest display of the encouragement message. * The default is 300, ie. 5 minutes after the application start. * @note This only adds an additional contraint on usage time and startup count based * encouragement messages, it does not actually trigger encouragement messages itself. * * @param secs Amount of seconds after the application start for the earliest display * of an encouragement message. * * @see setApplicationStartsUntilEncouragement, setApplicationUsageTimeUntilEncouragement */ void setEncouragementDelay(int secs); /*! Returns the current encouragement interval. */ int encouragementInterval() const; /*! Sets the interval after the encouragement should be repeated. * Encouragement messages are only repeated if no feedback options have been enabled. * The default is -1, that is no repeated encouragement at all. * @param days Days between encouragement messages, 0 disables repeated encouragements. */ void setEncouragementInterval(int days); public Q_SLOTS: /*! Manually submit currently recorded data. */ void submit(); /*! Marks the given survey as completed. This avoids getting further notification * about the same survey. */ void surveyCompleted(const KUserFeedback::SurveyInfo &info); /*! Manually load settings of the provider and all added data sources. * Automatically invoked after object construction and changing product ID. * @note Potentially long operation. */ void load(); /*! Manually store settings of the provider and all added data sources. * Will be autromatically invoked upon @p QCoreApplication::aboutToQuit signal. * @note Potentially long operation. */ void store(); Q_SIGNALS: /*! Emitted whenever there is a new survey available that can be presented * to the user. */ void surveyAvailable(const KUserFeedback::SurveyInfo &survey); /*! Indicate that the encouragement notice should be shown. */ void showEncouragementMessage(); /*! Emitted when the survey interval changed. */ void surveyIntervalChanged(); /*! Emitted when the telemetry collection mode has changed. */ void telemetryModeChanged(); /*! Emitted when any provider setting changed. */ void providerSettingsChanged(); /*! Emitted when the global enabled state changed. */ void enabledChanged(); private: friend class ProviderPrivate; ProviderPrivate * const d; // for UI Q_PRIVATE_SLOT(d, QByteArray jsonData(KUserFeedback::Provider::TelemetryMode)) // for testing Q_PRIVATE_SLOT(d, bool selectSurvey(const KUserFeedback::SurveyInfo&)) }; } Q_DECLARE_METATYPE(KUserFeedback::Provider::TelemetryMode) #endif // KUSERFEEDBACK_PROVIDER_H gammaray-2.11.3/3rdparty/kuserfeedback/core/provider_p.h000066400000000000000000000065441413310443000232160ustar00rootroot00000000000000/* Copyright (C) 2017 Volker Krause Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef KUSERFEEDBACK_PROVIDER_P_H #define KUSERFEEDBACK_PROVIDER_P_H #include "provider.h" #include #include #include #include #include #include #include QT_BEGIN_NAMESPACE class QNetworkAccessManager; class QNetworkReply; class QSettings; QT_END_NAMESPACE namespace KUserFeedback { class ProviderPrivate : public SurveyTargetExpressionDataProvider { public: explicit ProviderPrivate(Provider *qq); ~ProviderPrivate(); int currentApplicationTime() const; std::unique_ptr makeSettings() const; std::unique_ptr makeGlobalSettings() const; void load(); void store(); void storeOne(const QString &key, const QVariant &value); void storeOneGlobal(const QString &key, const QVariant &value); void aboutToQuit(); bool isValidSource(AbstractDataSource *source) const; QByteArray jsonData(Provider::TelemetryMode mode) const; void scheduleNextSubmission(qint64 minTime = 0); void submitProbe(const QUrl &url); void submitProbeFinished(QNetworkReply *reply); void submit(const QUrl &url); void submitFinished(QNetworkReply *reply); bool selectSurvey(const SurveyInfo &survey) const; Provider::TelemetryMode highestTelemetryMode() const; void scheduleEncouragement(); void emitShowEncouragementMessage(); void writeAuditLog(const QDateTime &dt); QVariant sourceData(const QString &sourceId) const override; Provider *q; QString productId; QTimer submissionTimer; QNetworkAccessManager *networkAccessManager; QUrl serverUrl; QDateTime lastSubmitTime; int redirectCount; int submissionInterval; Provider::TelemetryMode telemetryMode; int surveyInterval; QDateTime lastSurveyTime; QStringList completedSurveys; QTime startTime; int startCount; int usageTime; QTimer encouragementTimer; QDateTime lastEncouragementTime; int encouragementStarts; int encouragementTime; int encouragementDelay; int encouragementInterval; int backoffIntervalMinutes; QVector dataSources; QHash dataSourcesById; }; } #endif gammaray-2.11.3/3rdparty/kuserfeedback/core/qtversionsource.cpp000066400000000000000000000032401413310443000246410ustar00rootroot00000000000000/* Copyright (C) 2016 Volker Krause Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "qtversionsource.h" #include using namespace KUserFeedback; QtVersionSource::QtVersionSource() : AbstractDataSource(QStringLiteral("qtVersion"), Provider::BasicSystemInformation) { } QString QtVersionSource::description() const { return tr("The Qt version used by this application."); } QVariant QtVersionSource::data() { QVariantMap m; m.insert(QStringLiteral("value"), QString::fromLatin1(qVersion())); return m; } QString QtVersionSource::name() const { return tr("Qt version information"); } gammaray-2.11.3/3rdparty/kuserfeedback/core/qtversionsource.h000066400000000000000000000034121413310443000243070ustar00rootroot00000000000000/* Copyright (C) 2016 Volker Krause Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef KUSERFEEDBACK_QTVERSIONSOURCE_H #define KUSERFEEDBACK_QTVERSIONSOURCE_H #include "kuserfeedbackcore_export.h" #include "abstractdatasource.h" namespace KUserFeedback { /*! Data source reporting the Qt version used at runtime. * * The default telemetry mode for this source is Provider::BasicSystemInformation. */ class KUSERFEEDBACKCORE_EXPORT QtVersionSource : public AbstractDataSource { Q_DECLARE_TR_FUNCTIONS(KUserFeedback::QtVersionSource) public: QtVersionSource(); QString name() const override; QString description() const override; QVariant data() override; }; } #endif // KUSERFEEDBACK_QTVERSIONSOURCE_H gammaray-2.11.3/3rdparty/kuserfeedback/core/selectionratiosource.cpp000066400000000000000000000124371413310443000256430ustar00rootroot00000000000000/* Copyright (C) 2017 Volker Krause Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "selectionratiosource.h" #include "abstractdatasource_p.h" #include "logging_p.h" #include #include #include #include #include #include #include using namespace KUserFeedback; namespace KUserFeedback { class SelectionRatioSourcePrivate : public AbstractDataSourcePrivate { public: SelectionRatioSourcePrivate(); ~SelectionRatioSourcePrivate(); void selectionChanged(); QString selectedValue() const; QItemSelectionModel *model; QMetaObject::Connection monitorConnection; QString description; QString previousValue; QTime lastChangeTime; QHash ratioSet; // data we are currently tracking QHash baseRatioSet; // data loaded from storage int role; }; } SelectionRatioSourcePrivate::SelectionRatioSourcePrivate() : model(nullptr) , role(Qt::DisplayRole) { } SelectionRatioSourcePrivate::~SelectionRatioSourcePrivate() { QObject::disconnect(monitorConnection); } void SelectionRatioSourcePrivate::selectionChanged() { if (!previousValue.isEmpty() && lastChangeTime.elapsed() > 1000) { ratioSet[previousValue] += lastChangeTime.elapsed() / 1000; } lastChangeTime.start(); previousValue = selectedValue(); } QString SelectionRatioSourcePrivate::selectedValue() const { const auto idxs = model->selectedIndexes(); if (!model->hasSelection() || idxs.isEmpty()) return QString(); Q_ASSERT(!idxs.isEmpty()); const auto idx = idxs.at(0); return idx.data(role).toString(); } SelectionRatioSource::SelectionRatioSource(QItemSelectionModel* selectionModel, const QString& sampleName) : AbstractDataSource(sampleName, Provider::DetailedUsageStatistics, new SelectionRatioSourcePrivate) { Q_D(SelectionRatioSource); d->model = selectionModel; Q_ASSERT(selectionModel); d->monitorConnection = QObject::connect(selectionModel, &QItemSelectionModel::selectionChanged, [this]() { Q_D(SelectionRatioSource); d->selectionChanged(); }); d->lastChangeTime.start(); d->selectionChanged(); } void SelectionRatioSource::setRole(int role) { Q_D(SelectionRatioSource); d->role = role; } QString SelectionRatioSource::description() const { Q_D(const SelectionRatioSource); return d->description; } void SelectionRatioSource::setDescription(const QString& desc) { Q_D(SelectionRatioSource); d->description = desc; } QVariant SelectionRatioSource::data() { Q_D(SelectionRatioSource); d->selectionChanged(); QVariantMap m; int total = 0; for (auto it = d->ratioSet.constBegin(); it != d->ratioSet.constEnd(); ++it) total += it.value() + d->baseRatioSet.value(it.key()); if (total <= 0) return m; for (auto it = d->ratioSet.constBegin(); it != d->ratioSet.constEnd(); ++it) { double currentValue = it.value() + d->baseRatioSet.value(it.key()); QVariantMap v; v.insert(QStringLiteral("property"), currentValue / (double)(total)); m.insert(it.key(), v); } return m; } void SelectionRatioSource::loadImpl(QSettings *settings) { Q_D(SelectionRatioSource); foreach (const auto &value, settings->childKeys()) { const auto amount = std::max(settings->value(value, 0).toInt(), 0); d->baseRatioSet.insert(value, amount); if (!d->ratioSet.contains(value)) d->ratioSet.insert(value, 0); } } void SelectionRatioSource::storeImpl(QSettings *settings) { Q_D(SelectionRatioSource); d->selectionChanged(); // note that a second process can have updated the data meanwhile! for (auto it = d->ratioSet.begin(); it != d->ratioSet.end(); ++it) { if (it.value() == 0) continue; const auto oldValue = std::max(settings->value(it.key(), 0).toInt(), 0); const auto newValue = oldValue + it.value(); settings->setValue(it.key(), newValue); *it = 0; d->baseRatioSet.insert(it.key(), newValue); } } void SelectionRatioSource::resetImpl(QSettings* settings) { Q_D(SelectionRatioSource); d->baseRatioSet.clear(); d->ratioSet.clear(); settings->remove(QString()); } gammaray-2.11.3/3rdparty/kuserfeedback/core/selectionratiosource.h000066400000000000000000000056111413310443000253040ustar00rootroot00000000000000/* Copyright (C) 2017 Volker Krause Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef KUSERFEEDBACK_SELECTIONRATIOSOURCE_H #define KUSERFEEDBACK_SELECTIONRATIOSOURCE_H #include "kuserfeedbackcore_export.h" #include "abstractdatasource.h" QT_BEGIN_NAMESPACE class QItemSelectionModel; QT_END_NAMESPACE namespace KUserFeedback { class SelectionRatioSourcePrivate; /*! Records the time ratio a given entry is selected via a QItemSelectionModel. * * An example use-case would be the usage ratio of a applications * views/modes selected using a model-based view sidebar (such as * used in e.g. Kontact). * * The default telemetry mode for this source is Provider::DetailedUsageStatistics. */ class KUSERFEEDBACKCORE_EXPORT SelectionRatioSource : public AbstractDataSource { public: /*! Create a new selection ratio data source. * @param selectionModel The selection to monitor. * @param sampleName This is the name of the database field this data source is * associated with. */ explicit SelectionRatioSource(QItemSelectionModel *selectionModel, const QString &sampleName); /*! Determine which role to consider for the reported value. * By default this is Qt::DisplayRole. */ void setRole(int role); QString description() const override; /*! Set human-readable and translated description of the data provided by this source. * @note This must be set before adding this source, sources without description are * discarded. * @param desc The description. */ void setDescription(const QString &desc); QVariant data() override; void loadImpl(QSettings *settings) override; void storeImpl(QSettings *settings) override; void resetImpl(QSettings *settings) override; private: Q_DECLARE_PRIVATE(SelectionRatioSource) }; } #endif // KUSERFEEDBACK_SELECTIONRATIOSOURCE_H gammaray-2.11.3/3rdparty/kuserfeedback/core/startcountsource.cpp000066400000000000000000000041211413310443000250140ustar00rootroot00000000000000/* Copyright (C) 2017 Volker Krause Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "startcountsource.h" #include "abstractdatasource_p.h" #include "provider_p.h" #include using namespace KUserFeedback; namespace KUserFeedback { class StartCountSourcePrivate : public AbstractDataSourcePrivate { public: StartCountSourcePrivate() : provider(nullptr) {} ProviderPrivate *provider; }; } StartCountSource::StartCountSource() : AbstractDataSource(QStringLiteral("startCount"), Provider::BasicUsageStatistics, new StartCountSourcePrivate) { } QString StartCountSource::description() const { return tr("How often the application has been started."); } QVariant StartCountSource::data() { Q_D(StartCountSource); Q_ASSERT(d->provider); QVariantMap m; m.insert(QStringLiteral("value"), d->provider->startCount); return m; } QString StartCountSource::name() const { return tr("Launches count"); } void StartCountSource::setProvider(ProviderPrivate *p) { Q_D(StartCountSource); d->provider = p; } gammaray-2.11.3/3rdparty/kuserfeedback/core/startcountsource.h000066400000000000000000000040101413310443000244560ustar00rootroot00000000000000/* Copyright (C) 2017 Volker Krause Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef KUSERFEEDBACK_STARTCOUNTSOURCE_H #define KUSERFEEDBACK_STARTCOUNTSOURCE_H #include "kuserfeedbackcore_export.h" #include "abstractdatasource.h" namespace KUserFeedback { class Provider; class ProviderPrivate; class StartCountSourcePrivate; /*! Data source reporting the total amount of applications starts. * * The default telemetry mode for this source is Provider::BasicUsageStatistics. */ class KUSERFEEDBACKCORE_EXPORT StartCountSource : public AbstractDataSource { Q_DECLARE_TR_FUNCTIONS(KUserFeedback::StartCountSource) public: /*! Create a new start count data source. */ StartCountSource(); QString name() const override; QString description() const override; QVariant data() override; private: Q_DECLARE_PRIVATE(StartCountSource) friend class Provider; void setProvider(ProviderPrivate *p); }; } #endif // KUSERFEEDBACK_STARTCOUNTSOURCE_H gammaray-2.11.3/3rdparty/kuserfeedback/core/surveyinfo.cpp000066400000000000000000000045531413310443000236070ustar00rootroot00000000000000/* Copyright (C) 2016 Volker Krause Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "surveyinfo.h" #include #include #include #include using namespace KUserFeedback; class KUserFeedback::SurveyInfoData : public QSharedData { public: QUuid uuid; QUrl url; QString target; }; SurveyInfo::SurveyInfo() : d (new SurveyInfoData) { } SurveyInfo::SurveyInfo(const SurveyInfo &other) : d(other.d) { } SurveyInfo::~SurveyInfo() { } SurveyInfo& SurveyInfo::operator=(const SurveyInfo &other) { d = other.d; return *this; } bool SurveyInfo::isValid() const { return !d->uuid.isNull() && d->url.isValid(); } QUuid SurveyInfo::uuid() const { return d->uuid; } void SurveyInfo::setUuid(const QUuid &id) { d->uuid = id; } QUrl SurveyInfo::url() const { return d->url; } void SurveyInfo::setUrl(const QUrl& url) { d->url = url; } QString SurveyInfo::target() const { return d->target; } void SurveyInfo::setTarget(const QString &target) { d->target = target; } SurveyInfo SurveyInfo::fromJson(const QJsonObject& obj) { SurveyInfo s; s.setUuid(obj.value(QLatin1String("uuid")).toString()); s.setUrl(QUrl(obj.value(QLatin1String("url")).toString())); s.setTarget(obj.value(QLatin1String("target")).toString()); return s; } gammaray-2.11.3/3rdparty/kuserfeedback/core/surveyinfo.h000066400000000000000000000053611413310443000232520ustar00rootroot00000000000000/* Copyright (C) 2016 Volker Krause Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef KUSERFEEDBACK_SURVEYINFO_H #define KUSERFEEDBACK_SURVEYINFO_H #include "kuserfeedbackcore_export.h" #include #include #include #include #include QT_BEGIN_NAMESPACE class QJsonObject; class QUrl; class QUuid; QT_END_NAMESPACE namespace KUserFeedback { class SurveyInfoData; /*! Information about a survey request. * This class is implicitly shared. */ class KUSERFEEDBACKCORE_EXPORT SurveyInfo { Q_GADGET Q_PROPERTY(bool isValid READ isValid) Q_PROPERTY(QUrl url READ url) Q_PROPERTY(QString target READ target) Q_PROPERTY(QUuid uuid READ uuid) public: /*! Create an empty, invalid survey request. */ SurveyInfo(); /*! Copy constructor. */ SurveyInfo(const SurveyInfo&); ~SurveyInfo(); /*! Assignment operator. */ SurveyInfo& operator=(const SurveyInfo&); /*! Returns @c true if this survey has all necessary information to actually execute it. */ bool isValid() const; /*! Internal global unique id of the survey. * Used to locally check if a user has completed the survey already. */ QUuid uuid() const; /*! The URL to the survey website. */ QUrl url() const; /*! The survey targeting expression. */ QString target() const; ///@cond internal void setUuid(const QUuid &id); void setUrl(const QUrl &url); void setTarget(const QString &target); static SurveyInfo fromJson(const QJsonObject &obj); ///@endcond private: QSharedDataPointer d; }; } Q_DECLARE_METATYPE(KUserFeedback::SurveyInfo) #endif // KUSERFEEDBACK_SURVEYINFO_H gammaray-2.11.3/3rdparty/kuserfeedback/core/usagetimesource.cpp000066400000000000000000000041321413310443000245730ustar00rootroot00000000000000/* Copyright (C) 2017 Volker Krause Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "usagetimesource.h" #include "abstractdatasource_p.h" #include "provider_p.h" #include using namespace KUserFeedback; namespace KUserFeedback { class UsageTimeSourcePrivate : public AbstractDataSourcePrivate { public: UsageTimeSourcePrivate() : provider(nullptr) {} ProviderPrivate *provider; }; } UsageTimeSource::UsageTimeSource() : AbstractDataSource(QStringLiteral("usageTime"), Provider::BasicUsageStatistics, new UsageTimeSourcePrivate) { } QString UsageTimeSource::description() const { return tr("The total amount of time the application has been used."); } QVariant UsageTimeSource::data() { Q_D(UsageTimeSource); Q_ASSERT(d->provider); QVariantMap m; m.insert(QStringLiteral("value"), d->provider->currentApplicationTime()); return m; } QString UsageTimeSource::name() const { return tr("Usage time"); } void UsageTimeSource::setProvider(ProviderPrivate* p) { Q_D(UsageTimeSource); d->provider = p; } gammaray-2.11.3/3rdparty/kuserfeedback/core/usagetimesource.h000066400000000000000000000040071413310443000242410ustar00rootroot00000000000000/* Copyright (C) 2017 Volker Krause Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef KUSERFEEDBACK_USAGETIMESOURCE_H #define KUSERFEEDBACK_USAGETIMESOURCE_H #include "kuserfeedbackcore_export.h" #include "abstractdatasource.h" namespace KUserFeedback { class Provider; class ProviderPrivate; class UsageTimeSourcePrivate; /*! Data source reporting the total usage time of the application. * * The default telemetry mode for this source is Provider::BasicUsageStatistics. */ class KUSERFEEDBACKCORE_EXPORT UsageTimeSource : public AbstractDataSource { public: Q_DECLARE_TR_FUNCTIONS(KUserFeedback::UsageTimeSource) public: /*! Create a new usage time data source. */ UsageTimeSource(); QString name() const override; QString description() const override; QVariant data() override; private: Q_DECLARE_PRIVATE(UsageTimeSource) friend class Provider; void setProvider(ProviderPrivate *p); }; } #endif // KUSERFEEDBACK_USAGETIMESOURCE_H gammaray-2.11.3/3rdparty/kuserfeedback/kuserfeedback-update.sh000077500000000000000000000013251413310443000243510ustar00rootroot00000000000000#!/bin/sh # # Update the subset of KUserFeedback relevant for us from upstream # upstream=$1 if ! [ -d "$upstream" ]; then echo "Usage $0 " exit 1 fi for i in `ls common`; do if [ -f $upstream/src/common/$i ]; then cp $upstream/src/common/$i common/$i fi done for i in `ls core`; do cp $upstream/src/provider/core/$i core/$i done for i in `ls widgets`; do cp $upstream/src/provider/widgets/$i widgets/$i done pushd common flex --header-file=surveytargetexpressionscanner.h -o surveytargetexpressionlexer.cpp surveytargetexpressionlexer.l bison --defines=surveytargetexpressionparser_p.h -d -o surveytargetexpressionparser_p.cpp surveytargetexpressionparser.y popd gammaray-2.11.3/3rdparty/kuserfeedback/kuserfeedback_version.h000066400000000000000000000017711413310443000244550ustar00rootroot00000000000000/* Copyright (C) 2016 Volker Krause This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library 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 Library General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef KUSERFEEDBACK_VERSION_H #define KUSERFEEDBACK_VERSION_H #define KUSERFEEDBACK_VERSION_STRING "0.9.84" #define KUSERFEEDBACK_VERSION_MAJOR 0 #define KUSERFEEDBACK_VERSION_MINOR 9 #define KUSERFEEDBACK_VERSION_PATCH 84 #define KUSERFEEDBACK_VERSION ((0<<16)|(9<<8)|(84)) #endif gammaray-2.11.3/3rdparty/kuserfeedback/kuserfeedbackcore_export.h000066400000000000000000000002701413310443000251530ustar00rootroot00000000000000#ifndef USERFEEDBACKCORE_EXPORT_H #define USERFEEDBACKCORE_EXPORT_H #include "gammaray_kuserfeedback_export.h" #define KUSERFEEDBACKCORE_EXPORT GAMMARAY_KUSERFEEDBACK_EXPORT #endif gammaray-2.11.3/3rdparty/kuserfeedback/kuserfeedbackwidgets_export.h000066400000000000000000000003011413310443000256640ustar00rootroot00000000000000#ifndef USERFEEDBACKWIDGETS_EXPORT_H #define USERFEEDBACKWIDGETS_EXPORT_H #include "gammaray_kuserfeedback_export.h" #define KUSERFEEDBACKWIDGETS_EXPORT GAMMARAY_KUSERFEEDBACK_EXPORT #endif gammaray-2.11.3/3rdparty/kuserfeedback/qt_attribution.json000066400000000000000000000010361413310443000236760ustar00rootroot00000000000000{ "Id": "kuserfeedback", "Name": "KUserFeedback", "QDocModule": "gammaray-manual", "QtUsage": "Optionally used in the GammaRay client to gather usage telemetry. Disable using -DGAMMARAY_DISABLE_FEEDBACK=ON.", "Description": "Telemetry and targeted survey framework.", "Homepage": "https://api.kde.org/frameworks/kuserfeedback/html/index.html", "Version": "0.9.84", "License": "MIT License", "LicenseId": "MIT", "LicenseFile": "COPYING.LIB", "Copyright": "Copyright (C) 2016-2017 Volker Krause" } gammaray-2.11.3/3rdparty/kuserfeedback/widgets/000077500000000000000000000000001413310443000214015ustar00rootroot00000000000000gammaray-2.11.3/3rdparty/kuserfeedback/widgets/auditlogbrowserdialog.cpp000066400000000000000000000047701413310443000265110ustar00rootroot00000000000000/* Copyright (C) 2017 Volker Krause Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "auditlogbrowserdialog.h" #include "ui_auditlogbrowserdialog.h" #include #include #include using namespace KUserFeedback; AuditLogBrowserDialog::AuditLogBrowserDialog(QWidget *parent) : QDialog(parent) , ui(new Ui::AuditLogBrowserDialog) , m_controller(nullptr) { ui->setupUi(this); connect(ui->logEntryBox, static_cast(&QComboBox::activated), this, &AuditLogBrowserDialog::logEntrySelected); auto clearButton = ui->buttonBox->button(QDialogButtonBox::Discard); Q_ASSERT(clearButton); clearButton->setText(tr("Delete Log")); connect(clearButton, &QPushButton::clicked, this, &AuditLogBrowserDialog::close); setEnabled(false); } AuditLogBrowserDialog::~AuditLogBrowserDialog() { } void AuditLogBrowserDialog::setUiController(AuditLogUiController *controller) { Q_ASSERT(controller); m_controller = controller; ui->logEntryBox->setModel(controller->logEntryModel()); logEntrySelected(); auto clearButton = ui->buttonBox->button(QDialogButtonBox::Discard); connect(clearButton, &QPushButton::clicked, controller, &AuditLogUiController::clear); setEnabled(true); } void AuditLogBrowserDialog::logEntrySelected() { const auto dt = ui->logEntryBox->currentData().toDateTime(); ui->logEntryView->setText(m_controller->logEntry(dt)); } gammaray-2.11.3/3rdparty/kuserfeedback/widgets/auditlogbrowserdialog.h000066400000000000000000000034621413310443000261530ustar00rootroot00000000000000/* Copyright (C) 2017 Volker Krause Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef KUSERFEEDBACK_AUDITLOGBROWSERDIALOG_H #define KUSERFEEDBACK_AUDITLOGBROWSERDIALOG_H #include #include namespace KUserFeedback { namespace Ui { class AuditLogBrowserDialog; } class AuditLogUiController; /** Dialog to browse audit log entries. */ class AuditLogBrowserDialog : public QDialog { Q_OBJECT public: explicit AuditLogBrowserDialog(QWidget *parent); ~AuditLogBrowserDialog(); void setUiController(AuditLogUiController *controller); private Q_SLOTS: void logEntrySelected(); private: std::unique_ptr ui; AuditLogUiController *m_controller; }; } #endif // KUSERFEEDBACK_AUDITLOGBROWSERDIALOG_H gammaray-2.11.3/3rdparty/kuserfeedback/widgets/auditlogbrowserdialog.ui000066400000000000000000000043411413310443000263360ustar00rootroot00000000000000 KUserFeedback::AuditLogBrowserDialog 0 0 560 720 Dialog Da&ta Submission: logEntryBox 0 0 Qt::Horizontal QDialogButtonBox::Close|QDialogButtonBox::Discard buttonBox accepted() KUserFeedback::AuditLogBrowserDialog accept() 248 254 157 274 buttonBox rejected() KUserFeedback::AuditLogBrowserDialog reject() 316 260 286 274 gammaray-2.11.3/3rdparty/kuserfeedback/widgets/feedbackconfigdialog.cpp000066400000000000000000000053641413310443000262070ustar00rootroot00000000000000/* Copyright (C) 2016 Volker Krause Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "feedbackconfigdialog.h" #include "ui_feedbackconfigdialog.h" #include #include #include using namespace KUserFeedback; namespace KUserFeedback { class FeedbackConfigDialogPrivate { public: void updateButtonState(); std::unique_ptr ui; }; } FeedbackConfigDialog::FeedbackConfigDialog(QWidget *parent) : QDialog(parent), d(new FeedbackConfigDialogPrivate) { d->ui.reset(new Ui::FeedbackConfigDialog); d->ui->setupUi(this); d->ui->buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Contribute!")); d->ui->buttonBox->button(QDialogButtonBox::Close)->setText(tr("No, I do not want to contribute.")); } FeedbackConfigDialog::~FeedbackConfigDialog() { } void FeedbackConfigDialog::setFeedbackProvider(Provider* provider) { d->ui->configWidget->setFeedbackProvider(provider); connect(d->ui->configWidget, &FeedbackConfigWidget::configurationChanged, this, [this]() { d->updateButtonState(); }); d->updateButtonState(); } void FeedbackConfigDialog::accept() { auto p = d->ui->configWidget->feedbackProvider(); p->setTelemetryMode(d->ui->configWidget->telemetryMode()); p->setSurveyInterval(d->ui->configWidget->surveyInterval()); QDialog::accept(); } void FeedbackConfigDialogPrivate::updateButtonState() { const auto any = ui->configWidget->surveyInterval() >= 0 || ui->configWidget->telemetryMode() != Provider::NoTelemetry; ui->buttonBox->button(QDialogButtonBox::Ok)->setVisible(any); ui->buttonBox->button(QDialogButtonBox::Close)->setVisible(!any); } #include "moc_feedbackconfigdialog.cpp" gammaray-2.11.3/3rdparty/kuserfeedback/widgets/feedbackconfigdialog.h000066400000000000000000000042061413310443000256460ustar00rootroot00000000000000/* Copyright (C) 2016 Volker Krause Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef KUSERFEEDBACK_CONSOLE_FEEDBACKCONFIGDIALOG_H #define KUSERFEEDBACK_CONSOLE_FEEDBACKCONFIGDIALOG_H #include "kuserfeedbackwidgets_export.h" #include #include namespace KUserFeedback { class FeedbackConfigDialogPrivate; class Provider; /*! Configure which feedback a user wants to provide. * * @see FeedbackConfigWidget */ class KUSERFEEDBACKWIDGETS_EXPORT FeedbackConfigDialog : public QDialog { Q_OBJECT public: /*! Create a new feedback configuration dialog. * @param parent The parent widget. */ explicit FeedbackConfigDialog(QWidget *parent = nullptr); ~FeedbackConfigDialog(); /*! Set the feedback provider that this dialog configures. */ void setFeedbackProvider(KUserFeedback::Provider *provider); /*! Accepts the dialog and write changes made by the user to * the feedback provider. */ void accept() override; private: std::unique_ptr d; }; } #endif // KUSERFEEDBACK_CONSOLE_FEEDBACKCONFIGDIALOG_H gammaray-2.11.3/3rdparty/kuserfeedback/widgets/feedbackconfigdialog.ui000066400000000000000000000035121413310443000260330ustar00rootroot00000000000000 KUserFeedback::FeedbackConfigDialog 0 0 584 536 Feedback Settings Qt::Horizontal QDialogButtonBox::Close|QDialogButtonBox::Ok KUserFeedback::FeedbackConfigWidget QWidget
feedbackconfigwidget.h
1
buttonBox accepted() KUserFeedback::FeedbackConfigDialog accept() 224 510 157 274 buttonBox rejected() KUserFeedback::FeedbackConfigDialog accept() 292 516 286 274
gammaray-2.11.3/3rdparty/kuserfeedback/widgets/feedbackconfigwidget.cpp000066400000000000000000000163151413310443000262310ustar00rootroot00000000000000/* Copyright (C) 2017 Volker Krause Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "feedbackconfigwidget.h" #include "ui_feedbackconfigwidget.h" #include "auditlogbrowserdialog.h" #include #include #include #include #include #include #include #include #include #include using namespace KUserFeedback; namespace KUserFeedback { class FeedbackConfigWidgetPrivate { public: void telemetrySliderChanged(); void surveySliderChanged(); void applyPalette(QSlider *slider); FeedbackConfigUiController *controller; std::unique_ptr ui; AuditLogUiController *auditLogController; }; } void FeedbackConfigWidgetPrivate::telemetrySliderChanged() { if (ui->telemetrySlider->value() == 0) { ui->telemetryStack->setCurrentWidget(ui->noTelemetryPage); } else { ui->telemetryStack->setCurrentWidget(ui->telemetryPage); ui->telemetryLabel->setText(controller->telemetryModeDescription(ui->telemetrySlider->value())); } applyPalette(ui->telemetrySlider); if (!controller->feedbackProvider()) return; if (!ui->rawTelemetryButton->isChecked()) { ui->telemetryDetails->setHtml(controller->telemetryModeDetails(ui->telemetrySlider->value())); } else { QByteArray jsonData; QMetaObject::invokeMethod(controller->feedbackProvider(), "jsonData", Q_RETURN_ARG(QByteArray, jsonData), Q_ARG(KUserFeedback::Provider::TelemetryMode, controller->telemetryIndexToMode(ui->telemetrySlider->value()))); ui->telemetryDetails->setPlainText(QString::fromUtf8(jsonData.constData())); } } void FeedbackConfigWidgetPrivate::surveySliderChanged() { if (!controller->feedbackProvider()) return; ui->surveyLabel->setText(controller->surveyModeDescription(ui->surveySlider->value())); applyPalette(ui->surveySlider); } void FeedbackConfigWidgetPrivate::applyPalette(QSlider* slider) { const auto ratio = (double)slider->value() / (double)slider->maximum(); const auto red = qBound(0.0, 2.0 - ratio * 2.0, 1.0); const auto green = qBound(0.0, ratio * 2.0, 1.0); auto color = QColor(255 * red, 255 * green, 0); if (QApplication::palette().color(QPalette::Base).lightness() > 128) color = color.lighter(150); else color = color.darker(150); auto pal = slider->palette(); pal.setColor(QPalette::Highlight, color); slider->setPalette(pal); } FeedbackConfigWidget::FeedbackConfigWidget(QWidget* parent) : QWidget(parent) , d(new FeedbackConfigWidgetPrivate) { d->controller = new FeedbackConfigUiController(this); d->ui.reset(new Ui::FeedbackConfigWidget); d->ui->setupUi(this); d->ui->noTelemetryLabel->setText(d->controller->telemetryModeDescription(Provider::NoTelemetry)); connect(d->ui->telemetrySlider, &QSlider::valueChanged, this, [this]() { d->telemetrySliderChanged(); }); connect(d->ui->telemetrySlider, &QSlider::valueChanged, this, &FeedbackConfigWidget::configurationChanged); connect(d->ui->surveySlider, &QSlider::valueChanged, this, [this]() { d->surveySliderChanged(); }); connect(d->ui->surveySlider, &QSlider::valueChanged, this, &FeedbackConfigWidget::configurationChanged); d->ui->rawTelemetryButton->setParent(d->ui->telemetryDetails); d->ui->rawTelemetryButton->setIcon(style()->standardPixmap(QStyle::SP_DialogHelpButton)); d->ui->telemetryDetails->installEventFilter(this); connect(d->ui->rawTelemetryButton, &QAbstractButton::toggled, this, [this]() { d->telemetrySliderChanged(); }); d->auditLogController = new AuditLogUiController(this); d->ui->auditLogLabel->setVisible(d->auditLogController->hasLogEntries()); connect(d->auditLogController, &AuditLogUiController::logEntryCountChanged, this, [this]() { d->ui->auditLogLabel->setVisible(d->auditLogController->hasLogEntries()); }); connect(d->ui->auditLogLabel, &QLabel::linkActivated, this, [this](){ AuditLogBrowserDialog dlg(this); dlg.setUiController(d->auditLogController); dlg.exec(); }); setEnabled(false); // see setFeedbackProvider } FeedbackConfigWidget::~FeedbackConfigWidget() { } Provider* FeedbackConfigWidget::feedbackProvider() const { return d->controller->feedbackProvider(); } void FeedbackConfigWidget::setFeedbackProvider(Provider* provider) { d->controller->setFeedbackProvider(provider); if (!provider) { setEnabled(false); return; } const auto hasTelemetry = d->controller->telemetryModeCount() > 1; d->ui->telemetrySlider->setEnabled(hasTelemetry); d->ui->telemetryStack->setEnabled(hasTelemetry); if (hasTelemetry) d->ui->telemetrySlider->setMaximum(d->controller->telemetryModeCount() - 1); d->ui->telemetrySlider->setValue(d->controller->telemetryModeToIndex(provider->telemetryMode())); d->ui->surveySlider->setValue(d->controller->surveyIntervalToIndex(provider->surveyInterval())); d->surveySliderChanged(); // update the description even if nothing changed initially setEnabled(provider); } bool FeedbackConfigWidget::eventFilter(QObject* receiver, QEvent* event) { if (receiver == d->ui->telemetryDetails) { d->ui->rawTelemetryButton->move( d->ui->telemetryDetails->width() - d->ui->rawTelemetryButton->width() - style()->pixelMetric(QStyle::PM_LayoutRightMargin) - (d->ui->telemetryDetails->verticalScrollBar()->isVisible() ? d->ui->telemetryDetails->verticalScrollBar()->width() : 0), d->ui->telemetryDetails->height() - d->ui->rawTelemetryButton->height() - style()->pixelMetric(QStyle::PM_LayoutBottomMargin) ); } return QWidget::eventFilter(receiver, event); } Provider::TelemetryMode FeedbackConfigWidget::telemetryMode() const { return d->controller->telemetryIndexToMode(d->ui->telemetrySlider->value()); } int FeedbackConfigWidget::surveyInterval() const { return d->controller->surveyIndexToInterval(d->ui->surveySlider->value()); } #include "moc_feedbackconfigwidget.cpp" gammaray-2.11.3/3rdparty/kuserfeedback/widgets/feedbackconfigwidget.h000066400000000000000000000054161413310443000256760ustar00rootroot00000000000000/* Copyright (C) 2017 Volker Krause Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef KUSERFEEDBACK_FEEDBACKCONFIGWIDGET_H #define KUSERFEEDBACK_FEEDBACKCONFIGWIDGET_H #include "kuserfeedbackwidgets_export.h" #include #include #include namespace KUserFeedback { class FeedbackConfigWidgetPrivate; class Provider; /*! * Configuration widget for telemetry and survey contributions. * * Use this rather than FeedbackConfigDialog if you want to embed the * feedback configuration for example into an existing configuration * dialog. * @see FeedbackConfigDialog */ class KUSERFEEDBACKWIDGETS_EXPORT FeedbackConfigWidget : public QWidget { Q_OBJECT public: /*! Create a new feedback provider configuration widget. * @param parent The parent widget. */ explicit FeedbackConfigWidget(QWidget *parent = nullptr); ~FeedbackConfigWidget(); /*! Returns the feedback provider configured by this widget. */ Provider* feedbackProvider() const; /*! Set the feedback provider that should be configured with this widget. */ void setFeedbackProvider(Provider *provider); /*! Returns the telemetry level currently selected in the widget. */ Provider::TelemetryMode telemetryMode() const; /*! Returns the survey interval currently selected in this widget. */ int surveyInterval() const; Q_SIGNALS: /*! Emitted when any changes are made to the configuration represented * in this widget. */ void configurationChanged(); protected: ///@cond internal bool eventFilter(QObject *receiver, QEvent *event) override; ///@endcond private: std::unique_ptr d; }; } #endif // KUSERFEEDBACK_FEEDBACKCONFIGWIDGET_H gammaray-2.11.3/3rdparty/kuserfeedback/widgets/feedbackconfigwidget.ui000066400000000000000000000110041413310443000260520ustar00rootroot00000000000000 KUserFeedback::FeedbackConfigWidget 0 0 400 300 75 true Contribute Statistics Qt::AlignCenter 3 1 Qt::Horizontal QSlider::TicksBelow 0 0 0 0 0 0 Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop true 0 0 0 0 TextLabel true Show the raw data that is going to be shared. true true <a href="auditLog">View previously submitted data...</a> 75 true Participate in Surveys Qt::AlignCenter 2 1 Qt::Horizontal QSlider::TicksBelow true gammaray-2.11.3/3rdparty/kuserfeedback/widgets/notificationpopup.cpp000066400000000000000000000144661413310443000256720ustar00rootroot00000000000000/* Copyright (C) 2017 Volker Krause Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "notificationpopup.h" #include "ui_notificationpopup.h" #include "feedbackconfigdialog.h" #include #include #include #include #include #include #include #include using namespace KUserFeedback; namespace KUserFeedback { namespace Ui { class NotificationPopup; } class NotificationPopupPrivate { public: NotificationPopupPrivate(QWidget *qq); void showEncouragement(); void surveyAvailable(const SurveyInfo &info); void showPopup(); void hidePopup(); void action(); void reposition(); int xPosition() const; static QString appName(); Provider *provider; SurveyInfo survey; QPropertyAnimation *animation; std::unique_ptr ui; QWidget *q; }; } NotificationPopupPrivate::NotificationPopupPrivate(QWidget *qq) : provider(nullptr), animation(nullptr), q(qq) { } void NotificationPopupPrivate::showEncouragement() { if (q->isVisible()) return; survey = SurveyInfo(); const auto name = appName(); if (name.isEmpty()) { ui->title->setText(NotificationPopup::tr("Help us make this application better!")); ui->message->setText(NotificationPopup::tr("You can help us improving this application by sharing statistics and participate in surveys.")); } else { ui->title->setText(NotificationPopup::tr("Help us make %1 better!").arg(name)); ui->message->setText(NotificationPopup::tr("You can help us improving %1 by sharing statistics and participate in surveys.").arg(name)); } ui->actionButton->setText(NotificationPopup::tr("Contribute...")); showPopup(); } void NotificationPopupPrivate::surveyAvailable(const SurveyInfo &info) { if (q->isVisible()) return; survey = info; const auto name = appName(); ui->title->setText(NotificationPopup::tr("We are looking for your feedback!")); if (name.isEmpty()) ui->message->setText(NotificationPopup::tr("We would like a few minutes of your time to provide feedback about this application in a survey.")); else ui->message->setText(NotificationPopup::tr("We would like a few minutes of your time to provide feedback about %1 in a survey.").arg(name)); ui->actionButton->setText(NotificationPopup::tr("Participate")); showPopup(); } void NotificationPopupPrivate::showPopup() { q->show(); q->resize(q->sizeHint()); const auto startPos = QPoint(xPosition(), q->parentWidget()->height()); q->move(startPos); if (!animation) animation = new QPropertyAnimation(q, "pos", q); animation->setStartValue(startPos); animation->setEndValue(QPoint(xPosition(), q->parentWidget()->height() - q->height())); animation->setDuration(100); animation->setEasingCurve(QEasingCurve::InQuad); animation->start(); ui->actionButton->setFocus(); } void NotificationPopupPrivate::hidePopup() { if (animation) animation->stop(); q->hide(); } void NotificationPopupPrivate::action() { if (survey.isValid()) { QDesktopServices::openUrl(survey.url()); provider->surveyCompleted(survey); } else { FeedbackConfigDialog dlg(q); dlg.setFeedbackProvider(provider); dlg.exec(); } hidePopup(); } void NotificationPopupPrivate::reposition() { const auto pos = QPoint(xPosition(), q->parentWidget()->height() - q->height()); if (animation->state() == QAbstractAnimation::Running) animation->setEndValue(pos); else q->move(pos); } int NotificationPopupPrivate::xPosition() const { if (QApplication::layoutDirection() == Qt::LeftToRight) { return q->parentWidget()->width() - q->width(); } return 0; } QString NotificationPopupPrivate::appName() { return QGuiApplication::applicationDisplayName(); } NotificationPopup::NotificationPopup(QWidget* parent) : QWidget(parent) , d(new NotificationPopupPrivate(this)) { Q_ASSERT(parent); d->ui.reset(new Ui::NotificationPopup); d->ui->setupUi(this); d->ui->frame->setAutoFillBackground(true); d->ui->closeButton->setIcon(style()->standardIcon(QStyle::SP_DialogCloseButton)); connect(d->ui->actionButton, &QPushButton::clicked, this, [this]() { d->action(); }); connect(d->ui->closeButton, &QPushButton::clicked, this, [this]() { d->hidePopup(); }); parent->installEventFilter(this); setVisible(false); } NotificationPopup::~NotificationPopup() { } void NotificationPopup::setFeedbackProvider(Provider* provider) { Q_ASSERT(provider); d->provider = provider; connect(provider, &Provider::showEncouragementMessage, this, [this]() { d->showEncouragement(); }); connect(provider, &Provider::surveyAvailable, this, [this](const SurveyInfo &info) { d->surveyAvailable(info); }); } void NotificationPopup::keyReleaseEvent(QKeyEvent* event) { if (isVisible() && event->key() == Qt::Key_Escape) d->hidePopup(); } bool NotificationPopup::eventFilter(QObject* receiver, QEvent* event) { if (receiver == parentWidget() && isVisible()) { d->reposition(); } return QWidget::eventFilter(receiver, event); } #include "moc_notificationpopup.cpp" gammaray-2.11.3/3rdparty/kuserfeedback/widgets/notificationpopup.h000066400000000000000000000043611413310443000253300ustar00rootroot00000000000000/* Copyright (C) 2017 Volker Krause Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef KUSERFEEDBACK_NOTIFICATIONPOPUP_H #define KUSERFEEDBACK_NOTIFICATIONPOPUP_H #include "kuserfeedbackwidgets_export.h" #include #include namespace KUserFeedback { class NotificationPopupPrivate; class Provider; /*! * Notification popup that overlays a small part of the application for * encouraging contributions or inform about surveys. */ class KUSERFEEDBACKWIDGETS_EXPORT NotificationPopup : public QWidget { Q_OBJECT public: /*! * Create a new notification popup. * Do not put this widget into a layout. * @param parent The parent widget. This must not be @c nullptr. */ explicit NotificationPopup(QWidget *parent); ~NotificationPopup(); /*! * Set the feedback provider that is going to drive this notification popup. */ void setFeedbackProvider(Provider *provider); protected: ///@cond internal void keyReleaseEvent(QKeyEvent *event) override; bool eventFilter(QObject *receiver, QEvent *event) override; ///@endcond private: std::unique_ptr d; }; } #endif // KUSERFEEDBACK_NOTIFICATIONPOPUP_H gammaray-2.11.3/3rdparty/kuserfeedback/widgets/notificationpopup.ui000066400000000000000000000043071413310443000255160ustar00rootroot00000000000000 KUserFeedback::NotificationPopup 0 0 102 130 QFrame::StyledPanel QFrame::Raised 75 true 0 0 true true Qt::Horizontal 40 20 gammaray-2.11.3/3rdparty/lz4/000077500000000000000000000000001413310443000156465ustar00rootroot00000000000000gammaray-2.11.3/3rdparty/lz4/LICENSE000066400000000000000000000024371413310443000166610ustar00rootroot00000000000000LZ4 Library Copyright (c) 2011-2016, Yann Collet All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. gammaray-2.11.3/3rdparty/lz4/lz4.c000066400000000000000000001531321413310443000165300ustar00rootroot00000000000000/* LZ4 - Fast LZ compression algorithm Copyright (C) 2011-2015, Yann Collet. BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. You can contact the author at : - LZ4 source repository : https://github.com/Cyan4973/lz4 - LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c */ /************************************** * Tuning parameters **************************************/ /* * HEAPMODE : * Select how default compression functions will allocate memory for their hash table, * in memory stack (0:default, fastest), or in memory heap (1:requires malloc()). */ #define HEAPMODE 0 /* * ACCELERATION_DEFAULT : * Select "acceleration" for LZ4_compress_fast() when parameter value <= 0 */ #define ACCELERATION_DEFAULT 1 /************************************** * CPU Feature Detection **************************************/ /* * LZ4_FORCE_SW_BITCOUNT * Define this parameter if your target system or compiler does not support hardware bit count */ #if defined(_MSC_VER) && defined(_WIN32_WCE) /* Visual Studio for Windows CE does not support Hardware bit count */ # define LZ4_FORCE_SW_BITCOUNT #endif /************************************** * Includes **************************************/ #include "lz4.h" /************************************** * Compiler Options **************************************/ #ifdef _MSC_VER /* Visual Studio */ # define FORCE_INLINE static __forceinline # include # pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ # pragma warning(disable : 4293) /* disable: C4293: too large shift (32-bits) */ #else # if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */ # if defined(__GNUC__) || defined(__clang__) # define FORCE_INLINE static inline __attribute__((always_inline)) # else # define FORCE_INLINE static inline # endif # else # define FORCE_INLINE static # endif /* __STDC_VERSION__ */ #endif /* _MSC_VER */ /* LZ4_GCC_VERSION is defined into lz4.h */ #if (LZ4_GCC_VERSION >= 302) || (__INTEL_COMPILER >= 800) || defined(__clang__) # define expect(expr,value) (__builtin_expect ((expr),(value)) ) #else # define expect(expr,value) (expr) #endif #define likely(expr) expect((expr) != 0, 1) #define unlikely(expr) expect((expr) != 0, 0) /************************************** * Memory routines **************************************/ #include /* malloc, calloc, free */ #define ALLOCATOR(n,s) calloc(n,s) #define FREEMEM free #include /* memset, memcpy */ #define MEM_INIT memset /************************************** * Basic Types **************************************/ #if defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */ # include typedef uint8_t BYTE; typedef uint16_t U16; typedef uint32_t U32; typedef int32_t S32; typedef uint64_t U64; #else typedef unsigned char BYTE; typedef unsigned short U16; typedef unsigned int U32; typedef signed int S32; typedef unsigned long long U64; #endif /************************************** * Reading and writing into memory **************************************/ #define STEPSIZE sizeof(size_t) static unsigned LZ4_64bits(void) { return sizeof(void*)==8; } static unsigned LZ4_isLittleEndian(void) { const union { U32 i; BYTE c[4]; } one = { 1 }; /* don't use static : performance detrimental */ return one.c[0]; } static U16 LZ4_read16(const void* memPtr) { U16 val16; memcpy(&val16, memPtr, 2); return val16; } static U16 LZ4_readLE16(const void* memPtr) { if (LZ4_isLittleEndian()) { return LZ4_read16(memPtr); } else { const BYTE* p = (const BYTE*)memPtr; return (U16)((U16)p[0] + (p[1]<<8)); } } static void LZ4_writeLE16(void* memPtr, U16 value) { if (LZ4_isLittleEndian()) { memcpy(memPtr, &value, 2); } else { BYTE* p = (BYTE*)memPtr; p[0] = (BYTE) value; p[1] = (BYTE)(value>>8); } } static U32 LZ4_read32(const void* memPtr) { U32 val32; memcpy(&val32, memPtr, 4); return val32; } static U64 LZ4_read64(const void* memPtr) { U64 val64; memcpy(&val64, memPtr, 8); return val64; } static size_t LZ4_read_ARCH(const void* p) { if (LZ4_64bits()) return (size_t)LZ4_read64(p); else return (size_t)LZ4_read32(p); } static void LZ4_copy4(void* dstPtr, const void* srcPtr) { memcpy(dstPtr, srcPtr, 4); } static void LZ4_copy8(void* dstPtr, const void* srcPtr) { memcpy(dstPtr, srcPtr, 8); } /* customized version of memcpy, which may overwrite up to 7 bytes beyond dstEnd */ static void LZ4_wildCopy(void* dstPtr, const void* srcPtr, void* dstEnd) { BYTE* d = (BYTE*)dstPtr; const BYTE* s = (const BYTE*)srcPtr; BYTE* e = (BYTE*)dstEnd; do { LZ4_copy8(d,s); d+=8; s+=8; } while (d>3); # elif (defined(__clang__) || (LZ4_GCC_VERSION >= 304)) && !defined(LZ4_FORCE_SW_BITCOUNT) return (__builtin_ctzll((U64)val) >> 3); # else static const int DeBruijnBytePos[64] = { 0, 0, 0, 0, 0, 1, 1, 2, 0, 3, 1, 3, 1, 4, 2, 7, 0, 2, 3, 6, 1, 5, 3, 5, 1, 3, 4, 4, 2, 5, 6, 7, 7, 0, 1, 2, 3, 3, 4, 6, 2, 6, 5, 5, 3, 4, 5, 6, 7, 1, 2, 4, 6, 4, 4, 5, 7, 2, 6, 5, 7, 6, 7, 7 }; return DeBruijnBytePos[((U64)((val & -(long long)val) * 0x0218A392CDABBD3FULL)) >> 58]; # endif } else /* 32 bits */ { # if defined(_MSC_VER) && !defined(LZ4_FORCE_SW_BITCOUNT) unsigned long r; _BitScanForward( &r, (U32)val ); return (int)(r>>3); # elif (defined(__clang__) || (LZ4_GCC_VERSION >= 304)) && !defined(LZ4_FORCE_SW_BITCOUNT) return (__builtin_ctz((U32)val) >> 3); # else static const int DeBruijnBytePos[32] = { 0, 0, 3, 0, 3, 1, 3, 0, 3, 2, 2, 1, 3, 2, 0, 1, 3, 3, 1, 2, 2, 2, 2, 0, 3, 1, 2, 0, 1, 0, 1, 1 }; return DeBruijnBytePos[((U32)((val & -(S32)val) * 0x077CB531U)) >> 27]; # endif } } else /* Big Endian CPU */ { if (LZ4_64bits()) { # if defined(_MSC_VER) && defined(_WIN64) && !defined(LZ4_FORCE_SW_BITCOUNT) unsigned long r = 0; _BitScanReverse64( &r, val ); return (unsigned)(r>>3); # elif (defined(__clang__) || (LZ4_GCC_VERSION >= 304)) && !defined(LZ4_FORCE_SW_BITCOUNT) return (__builtin_clzll((U64)val) >> 3); # else unsigned r; if (!(val>>32)) { r=4; } else { r=0; val>>=32; } if (!(val>>16)) { r+=2; val>>=8; } else { val>>=24; } r += (!val); return r; # endif } else /* 32 bits */ { # if defined(_MSC_VER) && !defined(LZ4_FORCE_SW_BITCOUNT) unsigned long r = 0; _BitScanReverse( &r, (unsigned long)val ); return (unsigned)(r>>3); # elif (defined(__clang__) || (LZ4_GCC_VERSION >= 304)) && !defined(LZ4_FORCE_SW_BITCOUNT) return (__builtin_clz((U32)val) >> 3); # else unsigned r; if (!(val>>16)) { r=2; val>>=8; } else { r=0; val>>=24; } r += (!val); return r; # endif } } } static unsigned LZ4_count(const BYTE* pIn, const BYTE* pMatch, const BYTE* pInLimit) { const BYTE* const pStart = pIn; while (likely(pIn compression run slower on incompressible data */ /************************************** * Local Structures and types **************************************/ typedef struct { U32 hashTable[HASH_SIZE_U32]; U32 currentOffset; U32 initCheck; const BYTE* dictionary; BYTE* bufferStart; /* obsolete, used for slideInputBuffer */ U32 dictSize; } LZ4_stream_t_internal; typedef enum { notLimited = 0, limitedOutput = 1 } limitedOutput_directive; typedef enum { byPtr, byU32, byU16 } tableType_t; typedef enum { noDict = 0, withPrefix64k, usingExtDict } dict_directive; typedef enum { noDictIssue = 0, dictSmall } dictIssue_directive; typedef enum { endOnOutputSize = 0, endOnInputSize = 1 } endCondition_directive; typedef enum { full = 0, partial = 1 } earlyEnd_directive; /************************************** * Local Utils **************************************/ int LZ4_versionNumber (void) { return LZ4_VERSION_NUMBER; } int LZ4_compressBound(int isize) { return LZ4_COMPRESSBOUND(isize); } int LZ4_sizeofState() { return LZ4_STREAMSIZE; } /******************************** * Compression functions ********************************/ static U32 LZ4_hashSequence(U32 sequence, tableType_t const tableType) { if (tableType == byU16) return (((sequence) * 2654435761U) >> ((MINMATCH*8)-(LZ4_HASHLOG+1))); else return (((sequence) * 2654435761U) >> ((MINMATCH*8)-LZ4_HASHLOG)); } static const U64 prime5bytes = 889523592379ULL; static U32 LZ4_hashSequence64(size_t sequence, tableType_t const tableType) { const U32 hashLog = (tableType == byU16) ? LZ4_HASHLOG+1 : LZ4_HASHLOG; const U32 hashMask = (1<> (40 - hashLog)) & hashMask; } static U32 LZ4_hashSequenceT(size_t sequence, tableType_t const tableType) { if (LZ4_64bits()) return LZ4_hashSequence64(sequence, tableType); return LZ4_hashSequence((U32)sequence, tableType); } static U32 LZ4_hashPosition(const void* p, tableType_t tableType) { return LZ4_hashSequenceT(LZ4_read_ARCH(p), tableType); } static void LZ4_putPositionOnHash(const BYTE* p, U32 h, void* tableBase, tableType_t const tableType, const BYTE* srcBase) { switch (tableType) { case byPtr: { const BYTE** hashTable = (const BYTE**)tableBase; hashTable[h] = p; return; } case byU32: { U32* hashTable = (U32*) tableBase; hashTable[h] = (U32)(p-srcBase); return; } case byU16: { U16* hashTable = (U16*) tableBase; hashTable[h] = (U16)(p-srcBase); return; } } } static void LZ4_putPosition(const BYTE* p, void* tableBase, tableType_t tableType, const BYTE* srcBase) { U32 h = LZ4_hashPosition(p, tableType); LZ4_putPositionOnHash(p, h, tableBase, tableType, srcBase); } static const BYTE* LZ4_getPositionOnHash(U32 h, void* tableBase, tableType_t tableType, const BYTE* srcBase) { if (tableType == byPtr) { const BYTE** hashTable = (const BYTE**) tableBase; return hashTable[h]; } if (tableType == byU32) { U32* hashTable = (U32*) tableBase; return hashTable[h] + srcBase; } { U16* hashTable = (U16*) tableBase; return hashTable[h] + srcBase; } /* default, to ensure a return */ } static const BYTE* LZ4_getPosition(const BYTE* p, void* tableBase, tableType_t tableType, const BYTE* srcBase) { U32 h = LZ4_hashPosition(p, tableType); return LZ4_getPositionOnHash(h, tableBase, tableType, srcBase); } FORCE_INLINE int LZ4_compress_generic( void* const ctx, const char* const source, char* const dest, const int inputSize, const int maxOutputSize, const limitedOutput_directive outputLimited, const tableType_t tableType, const dict_directive dict, const dictIssue_directive dictIssue, const U32 acceleration) { LZ4_stream_t_internal* const dictPtr = (LZ4_stream_t_internal*)ctx; const BYTE* ip = (const BYTE*) source; const BYTE* base; const BYTE* lowLimit; const BYTE* const lowRefLimit = ip - dictPtr->dictSize; const BYTE* const dictionary = dictPtr->dictionary; const BYTE* const dictEnd = dictionary + dictPtr->dictSize; const size_t dictDelta = dictEnd - (const BYTE*)source; const BYTE* anchor = (const BYTE*) source; const BYTE* const iend = ip + inputSize; const BYTE* const mflimit = iend - MFLIMIT; const BYTE* const matchlimit = iend - LASTLITERALS; BYTE* op = (BYTE*) dest; BYTE* const olimit = op + maxOutputSize; U32 forwardH; size_t refDelta=0; /* Init conditions */ if ((U32)inputSize > (U32)LZ4_MAX_INPUT_SIZE) return 0; /* Unsupported input size, too large (or negative) */ switch(dict) { case noDict: default: base = (const BYTE*)source; lowLimit = (const BYTE*)source; break; case withPrefix64k: base = (const BYTE*)source - dictPtr->currentOffset; lowLimit = (const BYTE*)source - dictPtr->dictSize; break; case usingExtDict: base = (const BYTE*)source - dictPtr->currentOffset; lowLimit = (const BYTE*)source; break; } if ((tableType == byU16) && (inputSize>=LZ4_64Klimit)) return 0; /* Size too large (not within 64K limit) */ if (inputSize> LZ4_skipTrigger); if (unlikely(forwardIp > mflimit)) goto _last_literals; match = LZ4_getPositionOnHash(h, ctx, tableType, base); if (dict==usingExtDict) { if (match<(const BYTE*)source) { refDelta = dictDelta; lowLimit = dictionary; } else { refDelta = 0; lowLimit = (const BYTE*)source; } } forwardH = LZ4_hashPosition(forwardIp, tableType); LZ4_putPositionOnHash(ip, h, ctx, tableType, base); } while ( ((dictIssue==dictSmall) ? (match < lowRefLimit) : 0) || ((tableType==byU16) ? 0 : (match + MAX_DISTANCE < ip)) || (LZ4_read32(match+refDelta) != LZ4_read32(ip)) ); } /* Catch up */ while ((ip>anchor) && (match+refDelta > lowLimit) && (unlikely(ip[-1]==match[refDelta-1]))) { ip--; match--; } { /* Encode Literal length */ unsigned litLength = (unsigned)(ip - anchor); token = op++; if ((outputLimited) && (unlikely(op + litLength + (2 + 1 + LASTLITERALS) + (litLength/255) > olimit))) return 0; /* Check output limit */ if (litLength>=RUN_MASK) { int len = (int)litLength-RUN_MASK; *token=(RUN_MASK<= 255 ; len-=255) *op++ = 255; *op++ = (BYTE)len; } else *token = (BYTE)(litLength< matchlimit) limit = matchlimit; matchLength = LZ4_count(ip+MINMATCH, match+MINMATCH, limit); ip += MINMATCH + matchLength; if (ip==limit) { unsigned more = LZ4_count(ip, (const BYTE*)source, matchlimit); matchLength += more; ip += more; } } else { matchLength = LZ4_count(ip+MINMATCH, match+MINMATCH, matchlimit); ip += MINMATCH + matchLength; } if ((outputLimited) && (unlikely(op + (1 + LASTLITERALS) + (matchLength>>8) > olimit))) return 0; /* Check output limit */ if (matchLength>=ML_MASK) { *token += ML_MASK; matchLength -= ML_MASK; for (; matchLength >= 510 ; matchLength-=510) { *op++ = 255; *op++ = 255; } if (matchLength >= 255) { matchLength-=255; *op++ = 255; } *op++ = (BYTE)matchLength; } else *token += (BYTE)(matchLength); } anchor = ip; /* Test end of chunk */ if (ip > mflimit) break; /* Fill table */ LZ4_putPosition(ip-2, ctx, tableType, base); /* Test next position */ match = LZ4_getPosition(ip, ctx, tableType, base); if (dict==usingExtDict) { if (match<(const BYTE*)source) { refDelta = dictDelta; lowLimit = dictionary; } else { refDelta = 0; lowLimit = (const BYTE*)source; } } LZ4_putPosition(ip, ctx, tableType, base); if ( ((dictIssue==dictSmall) ? (match>=lowRefLimit) : 1) && (match+MAX_DISTANCE>=ip) && (LZ4_read32(match+refDelta)==LZ4_read32(ip)) ) { token=op++; *token=0; goto _next_match; } /* Prepare next loop */ forwardH = LZ4_hashPosition(++ip, tableType); } _last_literals: /* Encode Last Literals */ { const size_t lastRun = (size_t)(iend - anchor); if ((outputLimited) && ((op - (BYTE*)dest) + lastRun + 1 + ((lastRun+255-RUN_MASK)/255) > (U32)maxOutputSize)) return 0; /* Check output limit */ if (lastRun >= RUN_MASK) { size_t accumulator = lastRun - RUN_MASK; *op++ = RUN_MASK << ML_BITS; for(; accumulator >= 255 ; accumulator-=255) *op++ = 255; *op++ = (BYTE) accumulator; } else { *op++ = (BYTE)(lastRun<= LZ4_compressBound(inputSize)) { if (inputSize < LZ4_64Klimit) return LZ4_compress_generic(state, source, dest, inputSize, 0, notLimited, byU16, noDict, noDictIssue, acceleration); else return LZ4_compress_generic(state, source, dest, inputSize, 0, notLimited, LZ4_64bits() ? byU32 : byPtr, noDict, noDictIssue, acceleration); } else { if (inputSize < LZ4_64Klimit) return LZ4_compress_generic(state, source, dest, inputSize, maxOutputSize, limitedOutput, byU16, noDict, noDictIssue, acceleration); else return LZ4_compress_generic(state, source, dest, inputSize, maxOutputSize, limitedOutput, LZ4_64bits() ? byU32 : byPtr, noDict, noDictIssue, acceleration); } } int LZ4_compress_fast(const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration) { #if (HEAPMODE) void* ctxPtr = ALLOCATOR(1, sizeof(LZ4_stream_t)); /* malloc-calloc always properly aligned */ #else LZ4_stream_t ctx; void* ctxPtr = &ctx; #endif int result = LZ4_compress_fast_extState(ctxPtr, source, dest, inputSize, maxOutputSize, acceleration); #if (HEAPMODE) FREEMEM(ctxPtr); #endif return result; } int LZ4_compress_default(const char* source, char* dest, int inputSize, int maxOutputSize) { return LZ4_compress_fast(source, dest, inputSize, maxOutputSize, 1); } /* hidden debug function */ /* strangely enough, gcc generates faster code when this function is uncommented, even if unused */ int LZ4_compress_fast_force(const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration) { LZ4_stream_t ctx; LZ4_resetStream(&ctx); if (inputSize < LZ4_64Klimit) return LZ4_compress_generic(&ctx, source, dest, inputSize, maxOutputSize, limitedOutput, byU16, noDict, noDictIssue, acceleration); else return LZ4_compress_generic(&ctx, source, dest, inputSize, maxOutputSize, limitedOutput, LZ4_64bits() ? byU32 : byPtr, noDict, noDictIssue, acceleration); } /******************************** * destSize variant ********************************/ static int LZ4_compress_destSize_generic( void* const ctx, const char* const src, char* const dst, int* const srcSizePtr, const int targetDstSize, const tableType_t tableType) { const BYTE* ip = (const BYTE*) src; const BYTE* base = (const BYTE*) src; const BYTE* lowLimit = (const BYTE*) src; const BYTE* anchor = ip; const BYTE* const iend = ip + *srcSizePtr; const BYTE* const mflimit = iend - MFLIMIT; const BYTE* const matchlimit = iend - LASTLITERALS; BYTE* op = (BYTE*) dst; BYTE* const oend = op + targetDstSize; BYTE* const oMaxLit = op + targetDstSize - 2 /* offset */ - 8 /* because 8+MINMATCH==MFLIMIT */ - 1 /* token */; BYTE* const oMaxMatch = op + targetDstSize - (LASTLITERALS + 1 /* token */); BYTE* const oMaxSeq = oMaxLit - 1 /* token */; U32 forwardH; /* Init conditions */ if (targetDstSize < 1) return 0; /* Impossible to store anything */ if ((U32)*srcSizePtr > (U32)LZ4_MAX_INPUT_SIZE) return 0; /* Unsupported input size, too large (or negative) */ if ((tableType == byU16) && (*srcSizePtr>=LZ4_64Klimit)) return 0; /* Size too large (not within 64K limit) */ if (*srcSizePtr> LZ4_skipTrigger); if (unlikely(forwardIp > mflimit)) goto _last_literals; match = LZ4_getPositionOnHash(h, ctx, tableType, base); forwardH = LZ4_hashPosition(forwardIp, tableType); LZ4_putPositionOnHash(ip, h, ctx, tableType, base); } while ( ((tableType==byU16) ? 0 : (match + MAX_DISTANCE < ip)) || (LZ4_read32(match) != LZ4_read32(ip)) ); } /* Catch up */ while ((ip>anchor) && (match > lowLimit) && (unlikely(ip[-1]==match[-1]))) { ip--; match--; } { /* Encode Literal length */ unsigned litLength = (unsigned)(ip - anchor); token = op++; if (op + ((litLength+240)/255) + litLength > oMaxLit) { /* Not enough space for a last match */ op--; goto _last_literals; } if (litLength>=RUN_MASK) { unsigned len = litLength - RUN_MASK; *token=(RUN_MASK<= 255 ; len-=255) *op++ = 255; *op++ = (BYTE)len; } else *token = (BYTE)(litLength< oMaxMatch) { /* Match description too long : reduce it */ matchLength = (15-1) + (oMaxMatch-op) * 255; } //printf("offset %5i, matchLength%5i \n", (int)(ip-match), matchLength + MINMATCH); ip += MINMATCH + matchLength; if (matchLength>=ML_MASK) { *token += ML_MASK; matchLength -= ML_MASK; while (matchLength >= 255) { matchLength-=255; *op++ = 255; } *op++ = (BYTE)matchLength; } else *token += (BYTE)(matchLength); } anchor = ip; /* Test end of block */ if (ip > mflimit) break; if (op > oMaxSeq) break; /* Fill table */ LZ4_putPosition(ip-2, ctx, tableType, base); /* Test next position */ match = LZ4_getPosition(ip, ctx, tableType, base); LZ4_putPosition(ip, ctx, tableType, base); if ( (match+MAX_DISTANCE>=ip) && (LZ4_read32(match)==LZ4_read32(ip)) ) { token=op++; *token=0; goto _next_match; } /* Prepare next loop */ forwardH = LZ4_hashPosition(++ip, tableType); } _last_literals: /* Encode Last Literals */ { size_t lastRunSize = (size_t)(iend - anchor); if (op + 1 /* token */ + ((lastRunSize+240)/255) /* litLength */ + lastRunSize /* literals */ > oend) { /* adapt lastRunSize to fill 'dst' */ lastRunSize = (oend-op) - 1; lastRunSize -= (lastRunSize+240)/255; } ip = anchor + lastRunSize; if (lastRunSize >= RUN_MASK) { size_t accumulator = lastRunSize - RUN_MASK; *op++ = RUN_MASK << ML_BITS; for(; accumulator >= 255 ; accumulator-=255) *op++ = 255; *op++ = (BYTE) accumulator; } else { *op++ = (BYTE)(lastRunSize<= LZ4_compressBound(*srcSizePtr)) /* compression success is guaranteed */ { return LZ4_compress_fast_extState(state, src, dst, *srcSizePtr, targetDstSize, 1); } else { if (*srcSizePtr < LZ4_64Klimit) return LZ4_compress_destSize_generic(state, src, dst, srcSizePtr, targetDstSize, byU16); else return LZ4_compress_destSize_generic(state, src, dst, srcSizePtr, targetDstSize, LZ4_64bits() ? byU32 : byPtr); } } int LZ4_compress_destSize(const char* src, char* dst, int* srcSizePtr, int targetDstSize) { #if (HEAPMODE) void* ctx = ALLOCATOR(1, sizeof(LZ4_stream_t)); /* malloc-calloc always properly aligned */ #else LZ4_stream_t ctxBody; void* ctx = &ctxBody; #endif int result = LZ4_compress_destSize_extState(ctx, src, dst, srcSizePtr, targetDstSize); #if (HEAPMODE) FREEMEM(ctx); #endif return result; } /******************************** * Streaming functions ********************************/ LZ4_stream_t* LZ4_createStream(void) { LZ4_stream_t* lz4s = (LZ4_stream_t*)ALLOCATOR(8, LZ4_STREAMSIZE_U64); LZ4_STATIC_ASSERT(LZ4_STREAMSIZE >= sizeof(LZ4_stream_t_internal)); /* A compilation error here means LZ4_STREAMSIZE is not large enough */ LZ4_resetStream(lz4s); return lz4s; } void LZ4_resetStream (LZ4_stream_t* LZ4_stream) { MEM_INIT(LZ4_stream, 0, sizeof(LZ4_stream_t)); } int LZ4_freeStream (LZ4_stream_t* LZ4_stream) { FREEMEM(LZ4_stream); return (0); } #define HASH_UNIT sizeof(size_t) int LZ4_loadDict (LZ4_stream_t* LZ4_dict, const char* dictionary, int dictSize) { LZ4_stream_t_internal* dict = (LZ4_stream_t_internal*) LZ4_dict; const BYTE* p = (const BYTE*)dictionary; const BYTE* const dictEnd = p + dictSize; const BYTE* base; if ((dict->initCheck) || (dict->currentOffset > 1 GB)) /* Uninitialized structure, or reuse overflow */ LZ4_resetStream(LZ4_dict); if (dictSize < (int)HASH_UNIT) { dict->dictionary = NULL; dict->dictSize = 0; return 0; } if ((dictEnd - p) > 64 KB) p = dictEnd - 64 KB; dict->currentOffset += 64 KB; base = p - dict->currentOffset; dict->dictionary = p; dict->dictSize = (U32)(dictEnd - p); dict->currentOffset += dict->dictSize; while (p <= dictEnd-HASH_UNIT) { LZ4_putPosition(p, dict->hashTable, byU32, base); p+=3; } return dict->dictSize; } static void LZ4_renormDictT(LZ4_stream_t_internal* LZ4_dict, const BYTE* src) { if ((LZ4_dict->currentOffset > 0x80000000) || ((size_t)LZ4_dict->currentOffset > (size_t)src)) /* address space overflow */ { /* rescale hash table */ U32 delta = LZ4_dict->currentOffset - 64 KB; const BYTE* dictEnd = LZ4_dict->dictionary + LZ4_dict->dictSize; int i; for (i=0; ihashTable[i] < delta) LZ4_dict->hashTable[i]=0; else LZ4_dict->hashTable[i] -= delta; } LZ4_dict->currentOffset = 64 KB; if (LZ4_dict->dictSize > 64 KB) LZ4_dict->dictSize = 64 KB; LZ4_dict->dictionary = dictEnd - LZ4_dict->dictSize; } } int LZ4_compress_fast_continue (LZ4_stream_t* LZ4_stream, const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration) { LZ4_stream_t_internal* streamPtr = (LZ4_stream_t_internal*)LZ4_stream; const BYTE* const dictEnd = streamPtr->dictionary + streamPtr->dictSize; const BYTE* smallest = (const BYTE*) source; if (streamPtr->initCheck) return 0; /* Uninitialized structure detected */ if ((streamPtr->dictSize>0) && (smallest>dictEnd)) smallest = dictEnd; LZ4_renormDictT(streamPtr, smallest); if (acceleration < 1) acceleration = ACCELERATION_DEFAULT; /* Check overlapping input/dictionary space */ { const BYTE* sourceEnd = (const BYTE*) source + inputSize; if ((sourceEnd > streamPtr->dictionary) && (sourceEnd < dictEnd)) { streamPtr->dictSize = (U32)(dictEnd - sourceEnd); if (streamPtr->dictSize > 64 KB) streamPtr->dictSize = 64 KB; if (streamPtr->dictSize < 4) streamPtr->dictSize = 0; streamPtr->dictionary = dictEnd - streamPtr->dictSize; } } /* prefix mode : source data follows dictionary */ if (dictEnd == (const BYTE*)source) { int result; if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset)) result = LZ4_compress_generic(LZ4_stream, source, dest, inputSize, maxOutputSize, limitedOutput, byU32, withPrefix64k, dictSmall, acceleration); else result = LZ4_compress_generic(LZ4_stream, source, dest, inputSize, maxOutputSize, limitedOutput, byU32, withPrefix64k, noDictIssue, acceleration); streamPtr->dictSize += (U32)inputSize; streamPtr->currentOffset += (U32)inputSize; return result; } /* external dictionary mode */ { int result; if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset)) result = LZ4_compress_generic(LZ4_stream, source, dest, inputSize, maxOutputSize, limitedOutput, byU32, usingExtDict, dictSmall, acceleration); else result = LZ4_compress_generic(LZ4_stream, source, dest, inputSize, maxOutputSize, limitedOutput, byU32, usingExtDict, noDictIssue, acceleration); streamPtr->dictionary = (const BYTE*)source; streamPtr->dictSize = (U32)inputSize; streamPtr->currentOffset += (U32)inputSize; return result; } } /* Hidden debug function, to force external dictionary mode */ int LZ4_compress_forceExtDict (LZ4_stream_t* LZ4_dict, const char* source, char* dest, int inputSize) { LZ4_stream_t_internal* streamPtr = (LZ4_stream_t_internal*)LZ4_dict; int result; const BYTE* const dictEnd = streamPtr->dictionary + streamPtr->dictSize; const BYTE* smallest = dictEnd; if (smallest > (const BYTE*) source) smallest = (const BYTE*) source; LZ4_renormDictT((LZ4_stream_t_internal*)LZ4_dict, smallest); result = LZ4_compress_generic(LZ4_dict, source, dest, inputSize, 0, notLimited, byU32, usingExtDict, noDictIssue, 1); streamPtr->dictionary = (const BYTE*)source; streamPtr->dictSize = (U32)inputSize; streamPtr->currentOffset += (U32)inputSize; return result; } int LZ4_saveDict (LZ4_stream_t* LZ4_dict, char* safeBuffer, int dictSize) { LZ4_stream_t_internal* dict = (LZ4_stream_t_internal*) LZ4_dict; const BYTE* previousDictEnd = dict->dictionary + dict->dictSize; if ((U32)dictSize > 64 KB) dictSize = 64 KB; /* useless to define a dictionary > 64 KB */ if ((U32)dictSize > dict->dictSize) dictSize = dict->dictSize; memmove(safeBuffer, previousDictEnd - dictSize, dictSize); dict->dictionary = (const BYTE*)safeBuffer; dict->dictSize = (U32)dictSize; return dictSize; } /******************************* * Decompression functions *******************************/ /* * This generic decompression function cover all use cases. * It shall be instantiated several times, using different sets of directives * Note that it is essential this generic function is really inlined, * in order to remove useless branches during compilation optimization. */ FORCE_INLINE int LZ4_decompress_generic( const char* const source, char* const dest, int inputSize, int outputSize, /* If endOnInput==endOnInputSize, this value is the max size of Output Buffer. */ int endOnInput, /* endOnOutputSize, endOnInputSize */ int partialDecoding, /* full, partial */ int targetOutputSize, /* only used if partialDecoding==partial */ int dict, /* noDict, withPrefix64k, usingExtDict */ const BYTE* const lowPrefix, /* == dest if dict == noDict */ const BYTE* const dictStart, /* only if dict==usingExtDict */ const size_t dictSize /* note : = 0 if noDict */ ) { /* Local Variables */ const BYTE* ip = (const BYTE*) source; const BYTE* const iend = ip + inputSize; BYTE* op = (BYTE*) dest; BYTE* const oend = op + outputSize; BYTE* cpy; BYTE* oexit = op + targetOutputSize; const BYTE* const lowLimit = lowPrefix - dictSize; const BYTE* const dictEnd = (const BYTE*)dictStart + dictSize; const size_t dec32table[] = {4, 1, 2, 1, 4, 4, 4, 4}; const size_t dec64table[] = {0, 0, 0, (size_t)-1, 0, 1, 2, 3}; const int safeDecode = (endOnInput==endOnInputSize); const int checkOffset = ((safeDecode) && (dictSize < (int)(64 KB))); /* Special cases */ if ((partialDecoding) && (oexit> oend-MFLIMIT)) oexit = oend-MFLIMIT; /* targetOutputSize too high => decode everything */ if ((endOnInput) && (unlikely(outputSize==0))) return ((inputSize==1) && (*ip==0)) ? 0 : -1; /* Empty output buffer */ if ((!endOnInput) && (unlikely(outputSize==0))) return (*ip==0?1:-1); /* Main Loop */ while (1) { unsigned token; size_t length; const BYTE* match; /* get literal length */ token = *ip++; if ((length=(token>>ML_BITS)) == RUN_MASK) { unsigned s; do { s = *ip++; length += s; } while (likely((endOnInput)?ip(partialDecoding?oexit:oend-MFLIMIT)) || (ip+length>iend-(2+1+LASTLITERALS))) ) || ((!endOnInput) && (cpy>oend-COPYLENGTH))) { if (partialDecoding) { if (cpy > oend) goto _output_error; /* Error : write attempt beyond end of output buffer */ if ((endOnInput) && (ip+length > iend)) goto _output_error; /* Error : read attempt beyond end of input buffer */ } else { if ((!endOnInput) && (cpy != oend)) goto _output_error; /* Error : block decoding must stop exactly there */ if ((endOnInput) && ((ip+length != iend) || (cpy > oend))) goto _output_error; /* Error : input must be consumed */ } memcpy(op, ip, length); ip += length; op += length; break; /* Necessarily EOF, due to parsing restrictions */ } LZ4_wildCopy(op, ip, cpy); ip += length; op = cpy; /* get offset */ match = cpy - LZ4_readLE16(ip); ip+=2; if ((checkOffset) && (unlikely(match < lowLimit))) goto _output_error; /* Error : offset outside destination buffer */ /* get matchlength */ length = token & ML_MASK; if (length == ML_MASK) { unsigned s; do { if ((endOnInput) && (ip > iend-LASTLITERALS)) goto _output_error; s = *ip++; length += s; } while (s==255); if ((safeDecode) && unlikely((size_t)(op+length)<(size_t)op)) goto _output_error; /* overflow detection */ } length += MINMATCH; /* check external dictionary */ if ((dict==usingExtDict) && (match < lowPrefix)) { if (unlikely(op+length > oend-LASTLITERALS)) goto _output_error; /* doesn't respect parsing restriction */ if (length <= (size_t)(lowPrefix-match)) { /* match can be copied as a single segment from external dictionary */ match = dictEnd - (lowPrefix-match); memmove(op, match, length); op += length; } else { /* match encompass external dictionary and current segment */ size_t copySize = (size_t)(lowPrefix-match); memcpy(op, dictEnd - copySize, copySize); op += copySize; copySize = length - copySize; if (copySize > (size_t)(op-lowPrefix)) /* overlap within current segment */ { BYTE* const endOfMatch = op + copySize; const BYTE* copyFrom = lowPrefix; while (op < endOfMatch) *op++ = *copyFrom++; } else { memcpy(op, lowPrefix, copySize); op += copySize; } } continue; } /* copy repeated sequence */ cpy = op + length; if (unlikely((op-match)<8)) { const size_t dec64 = dec64table[op-match]; op[0] = match[0]; op[1] = match[1]; op[2] = match[2]; op[3] = match[3]; match += dec32table[op-match]; LZ4_copy4(op+4, match); op += 8; match -= dec64; } else { LZ4_copy8(op, match); op+=8; match+=8; } if (unlikely(cpy>oend-12)) { if (cpy > oend-LASTLITERALS) goto _output_error; /* Error : last LASTLITERALS bytes must be literals */ if (op < oend-8) { LZ4_wildCopy(op, match, oend-8); match += (oend-8) - op; op = oend-8; } while (opprefixSize = (size_t) dictSize; lz4sd->prefixEnd = (const BYTE*) dictionary + dictSize; lz4sd->externalDict = NULL; lz4sd->extDictSize = 0; return 1; } /* *_continue() : These decoding functions allow decompression of multiple blocks in "streaming" mode. Previously decoded blocks must still be available at the memory position where they were decoded. If it's not possible, save the relevant part of decoded data into a safe buffer, and indicate where it stands using LZ4_setStreamDecode() */ int LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int compressedSize, int maxOutputSize) { LZ4_streamDecode_t_internal* lz4sd = (LZ4_streamDecode_t_internal*) LZ4_streamDecode; int result; if (lz4sd->prefixEnd == (BYTE*)dest) { result = LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, endOnInputSize, full, 0, usingExtDict, lz4sd->prefixEnd - lz4sd->prefixSize, lz4sd->externalDict, lz4sd->extDictSize); if (result <= 0) return result; lz4sd->prefixSize += result; lz4sd->prefixEnd += result; } else { lz4sd->extDictSize = lz4sd->prefixSize; lz4sd->externalDict = lz4sd->prefixEnd - lz4sd->extDictSize; result = LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, endOnInputSize, full, 0, usingExtDict, (BYTE*)dest, lz4sd->externalDict, lz4sd->extDictSize); if (result <= 0) return result; lz4sd->prefixSize = result; lz4sd->prefixEnd = (BYTE*)dest + result; } return result; } int LZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int originalSize) { LZ4_streamDecode_t_internal* lz4sd = (LZ4_streamDecode_t_internal*) LZ4_streamDecode; int result; if (lz4sd->prefixEnd == (BYTE*)dest) { result = LZ4_decompress_generic(source, dest, 0, originalSize, endOnOutputSize, full, 0, usingExtDict, lz4sd->prefixEnd - lz4sd->prefixSize, lz4sd->externalDict, lz4sd->extDictSize); if (result <= 0) return result; lz4sd->prefixSize += originalSize; lz4sd->prefixEnd += originalSize; } else { lz4sd->extDictSize = lz4sd->prefixSize; lz4sd->externalDict = (BYTE*)dest - lz4sd->extDictSize; result = LZ4_decompress_generic(source, dest, 0, originalSize, endOnOutputSize, full, 0, usingExtDict, (BYTE*)dest, lz4sd->externalDict, lz4sd->extDictSize); if (result <= 0) return result; lz4sd->prefixSize = originalSize; lz4sd->prefixEnd = (BYTE*)dest + originalSize; } return result; } /* Advanced decoding functions : *_usingDict() : These decoding functions work the same as "_continue" ones, the dictionary must be explicitly provided within parameters */ FORCE_INLINE int LZ4_decompress_usingDict_generic(const char* source, char* dest, int compressedSize, int maxOutputSize, int safe, const char* dictStart, int dictSize) { if (dictSize==0) return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, safe, full, 0, noDict, (BYTE*)dest, NULL, 0); if (dictStart+dictSize == dest) { if (dictSize >= (int)(64 KB - 1)) return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, safe, full, 0, withPrefix64k, (BYTE*)dest-64 KB, NULL, 0); return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, safe, full, 0, noDict, (BYTE*)dest-dictSize, NULL, 0); } return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, safe, full, 0, usingExtDict, (BYTE*)dest, (const BYTE*)dictStart, dictSize); } int LZ4_decompress_safe_usingDict(const char* source, char* dest, int compressedSize, int maxOutputSize, const char* dictStart, int dictSize) { return LZ4_decompress_usingDict_generic(source, dest, compressedSize, maxOutputSize, 1, dictStart, dictSize); } int LZ4_decompress_fast_usingDict(const char* source, char* dest, int originalSize, const char* dictStart, int dictSize) { return LZ4_decompress_usingDict_generic(source, dest, 0, originalSize, 0, dictStart, dictSize); } /* debug function */ int LZ4_decompress_safe_forceExtDict(const char* source, char* dest, int compressedSize, int maxOutputSize, const char* dictStart, int dictSize) { return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, endOnInputSize, full, 0, usingExtDict, (BYTE*)dest, (const BYTE*)dictStart, dictSize); } /*************************************************** * Obsolete Functions ***************************************************/ /* obsolete compression functions */ int LZ4_compress_limitedOutput(const char* source, char* dest, int inputSize, int maxOutputSize) { return LZ4_compress_default(source, dest, inputSize, maxOutputSize); } int LZ4_compress(const char* source, char* dest, int inputSize) { return LZ4_compress_default(source, dest, inputSize, LZ4_compressBound(inputSize)); } int LZ4_compress_limitedOutput_withState (void* state, const char* src, char* dst, int srcSize, int dstSize) { return LZ4_compress_fast_extState(state, src, dst, srcSize, dstSize, 1); } int LZ4_compress_withState (void* state, const char* src, char* dst, int srcSize) { return LZ4_compress_fast_extState(state, src, dst, srcSize, LZ4_compressBound(srcSize), 1); } int LZ4_compress_limitedOutput_continue (LZ4_stream_t* LZ4_stream, const char* src, char* dst, int srcSize, int maxDstSize) { return LZ4_compress_fast_continue(LZ4_stream, src, dst, srcSize, maxDstSize, 1); } int LZ4_compress_continue (LZ4_stream_t* LZ4_stream, const char* source, char* dest, int inputSize) { return LZ4_compress_fast_continue(LZ4_stream, source, dest, inputSize, LZ4_compressBound(inputSize), 1); } /* These function names are deprecated and should no longer be used. They are only provided here for compatibility with older user programs. - LZ4_uncompress is totally equivalent to LZ4_decompress_fast - LZ4_uncompress_unknownOutputSize is totally equivalent to LZ4_decompress_safe */ int LZ4_uncompress (const char* source, char* dest, int outputSize) { return LZ4_decompress_fast(source, dest, outputSize); } int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize) { return LZ4_decompress_safe(source, dest, isize, maxOutputSize); } /* Obsolete Streaming functions */ int LZ4_sizeofStreamState() { return LZ4_STREAMSIZE; } static void LZ4_init(LZ4_stream_t_internal* lz4ds, BYTE* base) { MEM_INIT(lz4ds, 0, LZ4_STREAMSIZE); lz4ds->bufferStart = base; } int LZ4_resetStreamState(void* state, char* inputBuffer) { if ((((size_t)state) & 3) != 0) return 1; /* Error : pointer is not aligned on 4-bytes boundary */ LZ4_init((LZ4_stream_t_internal*)state, (BYTE*)inputBuffer); return 0; } void* LZ4_create (char* inputBuffer) { void* lz4ds = ALLOCATOR(8, LZ4_STREAMSIZE_U64); LZ4_init ((LZ4_stream_t_internal*)lz4ds, (BYTE*)inputBuffer); return lz4ds; } char* LZ4_slideInputBuffer (void* LZ4_Data) { LZ4_stream_t_internal* ctx = (LZ4_stream_t_internal*)LZ4_Data; int dictSize = LZ4_saveDict((LZ4_stream_t*)LZ4_Data, (char*)ctx->bufferStart, 64 KB); return (char*)(ctx->bufferStart + dictSize); } /* Obsolete streaming decompression functions */ int LZ4_decompress_safe_withPrefix64k(const char* source, char* dest, int compressedSize, int maxOutputSize) { return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, endOnInputSize, full, 0, withPrefix64k, (BYTE*)dest - 64 KB, NULL, 64 KB); } int LZ4_decompress_fast_withPrefix64k(const char* source, char* dest, int originalSize) { return LZ4_decompress_generic(source, dest, 0, originalSize, endOnOutputSize, full, 0, withPrefix64k, (BYTE*)dest - 64 KB, NULL, 64 KB); } #endif /* LZ4_COMMONDEFS_ONLY */ gammaray-2.11.3/3rdparty/lz4/lz4.h000066400000000000000000000446161413310443000165430ustar00rootroot00000000000000/* LZ4 - Fast LZ compression algorithm Header File Copyright (C) 2011-2015, Yann Collet. BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. You can contact the author at : - LZ4 source repository : https://github.com/Cyan4973/lz4 - LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c */ #pragma once #if defined (__cplusplus) extern "C" { #endif /* * lz4.h provides block compression functions, and gives full buffer control to programmer. * If you need to generate inter-operable compressed data (respecting LZ4 frame specification), * and can let the library handle its own memory, please use lz4frame.h instead. */ /************************************** * Version **************************************/ #define LZ4_VERSION_MAJOR 1 /* for breaking interface changes */ #define LZ4_VERSION_MINOR 7 /* for new (non-breaking) interface capabilities */ #define LZ4_VERSION_RELEASE 0 /* for tweaks, bug-fixes, or development */ #define LZ4_VERSION_NUMBER (LZ4_VERSION_MAJOR *100*100 + LZ4_VERSION_MINOR *100 + LZ4_VERSION_RELEASE) int LZ4_versionNumber (void); /************************************** * Tuning parameter **************************************/ /* * LZ4_MEMORY_USAGE : * Memory usage formula : N->2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB; etc.) * Increasing memory usage improves compression ratio * Reduced memory usage can improve speed, due to cache effect * Default value is 14, for 16KB, which nicely fits into Intel x86 L1 cache */ #define LZ4_MEMORY_USAGE 14 /************************************** * Simple Functions **************************************/ int LZ4_compress_default(const char* source, char* dest, int sourceSize, int maxDestSize); int LZ4_decompress_safe (const char* source, char* dest, int compressedSize, int maxDecompressedSize); /* LZ4_compress_default() : Compresses 'sourceSize' bytes from buffer 'source' into already allocated 'dest' buffer of size 'maxDestSize'. Compression is guaranteed to succeed if 'maxDestSize' >= LZ4_compressBound(sourceSize). It also runs faster, so it's a recommended setting. If the function cannot compress 'source' into a more limited 'dest' budget, compression stops *immediately*, and the function result is zero. As a consequence, 'dest' content is not valid. This function never writes outside 'dest' buffer, nor read outside 'source' buffer. sourceSize : Max supported value is LZ4_MAX_INPUT_VALUE maxDestSize : full or partial size of buffer 'dest' (which must be already allocated) return : the number of bytes written into buffer 'dest' (necessarily <= maxOutputSize) or 0 if compression fails LZ4_decompress_safe() : compressedSize : is the precise full size of the compressed block. maxDecompressedSize : is the size of destination buffer, which must be already allocated. return : the number of bytes decompressed into destination buffer (necessarily <= maxDecompressedSize) If destination buffer is not large enough, decoding will stop and output an error code (<0). If the source stream is detected malformed, the function will stop decoding and return a negative result. This function is protected against buffer overflow exploits, including malicious data packets. It never writes outside output buffer, nor reads outside input buffer. */ /************************************** * Advanced Functions **************************************/ #define LZ4_MAX_INPUT_SIZE 0x7E000000 /* 2 113 929 216 bytes */ #define LZ4_COMPRESSBOUND(isize) ((unsigned)(isize) > (unsigned)LZ4_MAX_INPUT_SIZE ? 0 : (isize) + ((isize)/255) + 16) /* LZ4_compressBound() : Provides the maximum size that LZ4 compression may output in a "worst case" scenario (input data not compressible) This function is primarily useful for memory allocation purposes (destination buffer size). Macro LZ4_COMPRESSBOUND() is also provided for compilation-time evaluation (stack memory allocation for example). Note that LZ4_compress_default() compress faster when dest buffer size is >= LZ4_compressBound(srcSize) inputSize : max supported value is LZ4_MAX_INPUT_SIZE return : maximum output size in a "worst case" scenario or 0, if input size is too large ( > LZ4_MAX_INPUT_SIZE) */ int LZ4_compressBound(int inputSize); /* LZ4_compress_fast() : Same as LZ4_compress_default(), but allows to select an "acceleration" factor. The larger the acceleration value, the faster the algorithm, but also the lesser the compression. It's a trade-off. It can be fine tuned, with each successive value providing roughly +~3% to speed. An acceleration value of "1" is the same as regular LZ4_compress_default() Values <= 0 will be replaced by ACCELERATION_DEFAULT (see lz4.c), which is 1. */ int LZ4_compress_fast (const char* source, char* dest, int sourceSize, int maxDestSize, int acceleration); /* LZ4_compress_fast_extState() : Same compression function, just using an externally allocated memory space to store compression state. Use LZ4_sizeofState() to know how much memory must be allocated, and allocate it on 8-bytes boundaries (using malloc() typically). Then, provide it as 'void* state' to compression function. */ int LZ4_sizeofState(void); int LZ4_compress_fast_extState (void* state, const char* source, char* dest, int inputSize, int maxDestSize, int acceleration); /* LZ4_compress_destSize() : Reverse the logic, by compressing as much data as possible from 'source' buffer into already allocated buffer 'dest' of size 'targetDestSize'. This function either compresses the entire 'source' content into 'dest' if it's large enough, or fill 'dest' buffer completely with as much data as possible from 'source'. *sourceSizePtr : will be modified to indicate how many bytes where read from 'source' to fill 'dest'. New value is necessarily <= old value. return : Nb bytes written into 'dest' (necessarily <= targetDestSize) or 0 if compression fails */ int LZ4_compress_destSize (const char* source, char* dest, int* sourceSizePtr, int targetDestSize); /* LZ4_decompress_fast() : originalSize : is the original and therefore uncompressed size return : the number of bytes read from the source buffer (in other words, the compressed size) If the source stream is detected malformed, the function will stop decoding and return a negative result. Destination buffer must be already allocated. Its size must be a minimum of 'originalSize' bytes. note : This function fully respect memory boundaries for properly formed compressed data. It is a bit faster than LZ4_decompress_safe(). However, it does not provide any protection against intentionally modified data stream (malicious input). Use this function in trusted environment only (data to decode comes from a trusted source). */ int LZ4_decompress_fast (const char* source, char* dest, int originalSize); /* LZ4_decompress_safe_partial() : This function decompress a compressed block of size 'compressedSize' at position 'source' into destination buffer 'dest' of size 'maxDecompressedSize'. The function tries to stop decompressing operation as soon as 'targetOutputSize' has been reached, reducing decompression time. return : the number of bytes decoded in the destination buffer (necessarily <= maxDecompressedSize) Note : this number can be < 'targetOutputSize' should the compressed block to decode be smaller. Always control how many bytes were decoded. If the source stream is detected malformed, the function will stop decoding and return a negative result. This function never writes outside of output buffer, and never reads outside of input buffer. It is therefore protected against malicious data packets */ int LZ4_decompress_safe_partial (const char* source, char* dest, int compressedSize, int targetOutputSize, int maxDecompressedSize); /*********************************************** * Streaming Compression Functions ***********************************************/ #define LZ4_STREAMSIZE_U64 ((1 << (LZ4_MEMORY_USAGE-3)) + 4) #define LZ4_STREAMSIZE (LZ4_STREAMSIZE_U64 * sizeof(long long)) /* * LZ4_stream_t * information structure to track an LZ4 stream. * important : init this structure content before first use ! * note : only allocated directly the structure if you are statically linking LZ4 * If you are using liblz4 as a DLL, please use below construction methods instead. */ typedef struct { long long table[LZ4_STREAMSIZE_U64]; } LZ4_stream_t; /* * LZ4_resetStream * Use this function to init an allocated LZ4_stream_t structure */ void LZ4_resetStream (LZ4_stream_t* streamPtr); /* * LZ4_createStream will allocate and initialize an LZ4_stream_t structure * LZ4_freeStream releases its memory. * In the context of a DLL (liblz4), please use these methods rather than the static struct. * They are more future proof, in case of a change of LZ4_stream_t size. */ LZ4_stream_t* LZ4_createStream(void); int LZ4_freeStream (LZ4_stream_t* streamPtr); /* * LZ4_loadDict * Use this function to load a static dictionary into LZ4_stream. * Any previous data will be forgotten, only 'dictionary' will remain in memory. * Loading a size of 0 is allowed. * Return : dictionary size, in bytes (necessarily <= 64 KB) */ int LZ4_loadDict (LZ4_stream_t* streamPtr, const char* dictionary, int dictSize); /* * LZ4_compress_fast_continue * Compress buffer content 'src', using data from previously compressed blocks as dictionary to improve compression ratio. * Important : Previous data blocks are assumed to still be present and unmodified ! * 'dst' buffer must be already allocated. * If maxDstSize >= LZ4_compressBound(srcSize), compression is guaranteed to succeed, and runs faster. * If not, and if compressed data cannot fit into 'dst' buffer size, compression stops, and function returns a zero. */ int LZ4_compress_fast_continue (LZ4_stream_t* streamPtr, const char* src, char* dst, int srcSize, int maxDstSize, int acceleration); /* * LZ4_saveDict * If previously compressed data block is not guaranteed to remain available at its memory location * save it into a safer place (char* safeBuffer) * Note : you don't need to call LZ4_loadDict() afterwards, * dictionary is immediately usable, you can therefore call LZ4_compress_fast_continue() * Return : saved dictionary size in bytes (necessarily <= dictSize), or 0 if error */ int LZ4_saveDict (LZ4_stream_t* streamPtr, char* safeBuffer, int dictSize); /************************************************ * Streaming Decompression Functions ************************************************/ #define LZ4_STREAMDECODESIZE_U64 4 #define LZ4_STREAMDECODESIZE (LZ4_STREAMDECODESIZE_U64 * sizeof(unsigned long long)) typedef struct { unsigned long long table[LZ4_STREAMDECODESIZE_U64]; } LZ4_streamDecode_t; /* * LZ4_streamDecode_t * information structure to track an LZ4 stream. * init this structure content using LZ4_setStreamDecode or memset() before first use ! * * In the context of a DLL (liblz4) please prefer usage of construction methods below. * They are more future proof, in case of a change of LZ4_streamDecode_t size in the future. * LZ4_createStreamDecode will allocate and initialize an LZ4_streamDecode_t structure * LZ4_freeStreamDecode releases its memory. */ LZ4_streamDecode_t* LZ4_createStreamDecode(void); int LZ4_freeStreamDecode (LZ4_streamDecode_t* LZ4_stream); /* * LZ4_setStreamDecode * Use this function to instruct where to find the dictionary. * Setting a size of 0 is allowed (same effect as reset). * Return : 1 if OK, 0 if error */ int LZ4_setStreamDecode (LZ4_streamDecode_t* LZ4_streamDecode, const char* dictionary, int dictSize); /* *_continue() : These decoding functions allow decompression of multiple blocks in "streaming" mode. Previously decoded blocks *must* remain available at the memory position where they were decoded (up to 64 KB) In the case of a ring buffers, decoding buffer must be either : - Exactly same size as encoding buffer, with same update rule (block boundaries at same positions) In which case, the decoding & encoding ring buffer can have any size, including very small ones ( < 64 KB). - Larger than encoding buffer, by a minimum of maxBlockSize more bytes. maxBlockSize is implementation dependent. It's the maximum size you intend to compress into a single block. In which case, encoding and decoding buffers do not need to be synchronized, and encoding ring buffer can have any size, including small ones ( < 64 KB). - _At least_ 64 KB + 8 bytes + maxBlockSize. In which case, encoding and decoding buffers do not need to be synchronized, and encoding ring buffer can have any size, including larger than decoding buffer. Whenever these conditions are not possible, save the last 64KB of decoded data into a safe buffer, and indicate where it is saved using LZ4_setStreamDecode() */ int LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int compressedSize, int maxDecompressedSize); int LZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int originalSize); /* Advanced decoding functions : *_usingDict() : These decoding functions work the same as a combination of LZ4_setStreamDecode() followed by LZ4_decompress_x_continue() They are stand-alone. They don't need nor update an LZ4_streamDecode_t structure. */ int LZ4_decompress_safe_usingDict (const char* source, char* dest, int compressedSize, int maxDecompressedSize, const char* dictStart, int dictSize); int LZ4_decompress_fast_usingDict (const char* source, char* dest, int originalSize, const char* dictStart, int dictSize); /************************************** * Obsolete Functions **************************************/ /* Deprecate Warnings */ /* Should these warnings messages be a problem, it is generally possible to disable them, with -Wno-deprecated-declarations for gcc or _CRT_SECURE_NO_WARNINGS in Visual for example. You can also define LZ4_DEPRECATE_WARNING_DEFBLOCK. */ #ifndef LZ4_DEPRECATE_WARNING_DEFBLOCK # define LZ4_DEPRECATE_WARNING_DEFBLOCK # define LZ4_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) # if (LZ4_GCC_VERSION >= 405) || defined(__clang__) # define LZ4_DEPRECATED(message) __attribute__((deprecated(message))) # elif (LZ4_GCC_VERSION >= 301) # define LZ4_DEPRECATED(message) __attribute__((deprecated)) # elif defined(_MSC_VER) # define LZ4_DEPRECATED(message) __declspec(deprecated(message)) # else # pragma message("WARNING: You need to implement LZ4_DEPRECATED for this compiler") # define LZ4_DEPRECATED(message) # endif #endif /* LZ4_DEPRECATE_WARNING_DEFBLOCK */ /* Obsolete compression functions */ /* These functions are planned to start generate warnings by r131 approximately */ int LZ4_compress (const char* source, char* dest, int sourceSize); int LZ4_compress_limitedOutput (const char* source, char* dest, int sourceSize, int maxOutputSize); int LZ4_compress_withState (void* state, const char* source, char* dest, int inputSize); int LZ4_compress_limitedOutput_withState (void* state, const char* source, char* dest, int inputSize, int maxOutputSize); int LZ4_compress_continue (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize); int LZ4_compress_limitedOutput_continue (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize, int maxOutputSize); /* Obsolete decompression functions */ /* These function names are completely deprecated and must no longer be used. They are only provided here for compatibility with older programs. - LZ4_uncompress is the same as LZ4_decompress_fast - LZ4_uncompress_unknownOutputSize is the same as LZ4_decompress_safe These function prototypes are now disabled; uncomment them only if you really need them. It is highly recommended to stop using these prototypes and migrate to maintained ones */ /* int LZ4_uncompress (const char* source, char* dest, int outputSize); */ /* int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize); */ /* Obsolete streaming functions; use new streaming interface whenever possible */ LZ4_DEPRECATED("use LZ4_createStream() instead") void* LZ4_create (char* inputBuffer); LZ4_DEPRECATED("use LZ4_createStream() instead") int LZ4_sizeofStreamState(void); LZ4_DEPRECATED("use LZ4_resetStream() instead") int LZ4_resetStreamState(void* state, char* inputBuffer); LZ4_DEPRECATED("use LZ4_saveDict() instead") char* LZ4_slideInputBuffer (void* state); /* Obsolete streaming decoding functions */ LZ4_DEPRECATED("use LZ4_decompress_safe_usingDict() instead") int LZ4_decompress_safe_withPrefix64k (const char* src, char* dst, int compressedSize, int maxDstSize); LZ4_DEPRECATED("use LZ4_decompress_fast_usingDict() instead") int LZ4_decompress_fast_withPrefix64k (const char* src, char* dst, int originalSize); #if defined (__cplusplus) } #endif gammaray-2.11.3/3rdparty/lz4/qt_attribution.json000066400000000000000000000006641413310443000216170ustar00rootroot00000000000000{ "Id": "lz4", "Name": "LZ4", "QDocModule": "gammaray-manual", "QtUsage": "Used for compressing the GammaRay client/server communication.", "Description": "Fast compression algorithm.", "Homepage": "https://github.com/lz4/lz4", "Version": "1.7", "License": "BSD 2-clause License", "LicenseId": "BSD-2-Clause", "LicenseFile": "LICENSE", "Copyright": "Copyright (c) 2011-2016, Yann Collet" } gammaray-2.11.3/3rdparty/qt/000077500000000000000000000000001413310443000155615ustar00rootroot00000000000000gammaray-2.11.3/3rdparty/qt/5.5/000077500000000000000000000000001413310443000160705ustar00rootroot00000000000000gammaray-2.11.3/3rdparty/qt/5.5/private/000077500000000000000000000000001413310443000175425ustar00rootroot00000000000000gammaray-2.11.3/3rdparty/qt/5.5/private/qpaintbuffer.cpp000066400000000000000000002324271413310443000227460ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtGui module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL21$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 or version 3 as published by the Free ** Software Foundation and appearing in the file LICENSE.LGPLv21 and ** LICENSE.LGPLv3 included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include //#include #include #include #include #include #include #include // #define QPAINTBUFFER_DEBUG_DRAW QT_BEGIN_NAMESPACE extern void qt_format_text(const QFont &font, const QRectF &_r, int tf, const QTextOption *option, const QString& str, QRectF *brect, int tabstops, int* tabarray, int tabarraylen, QPainter *painter); QTextItemIntCopy::QTextItemIntCopy(const QTextItem &item) : m_item(static_cast(item)) { auto *chars = new QChar[m_item.num_chars]; auto *logClusters = new unsigned short[m_item.num_chars]; memcpy(chars, m_item.chars, m_item.num_chars * sizeof(QChar)); memcpy(logClusters, m_item.logClusters, m_item.num_chars * sizeof(unsigned short)); m_item.chars = chars; m_item.logClusters = logClusters; auto *glyphLayoutData = new char[m_item.glyphs.numGlyphs * QGlyphLayout::SpaceNeeded]; QGlyphLayout glyphs(glyphLayoutData, m_item.glyphs.numGlyphs); memcpy(glyphs.offsets, m_item.glyphs.offsets, m_item.glyphs.numGlyphs * sizeof(QFixedPoint)); memcpy(glyphs.glyphs, m_item.glyphs.glyphs, m_item.glyphs.numGlyphs * sizeof(glyph_t)); memcpy(glyphs.advances, m_item.glyphs.advances, m_item.glyphs.numGlyphs * sizeof(QFixed)); memcpy(glyphs.justifications, m_item.glyphs.justifications, m_item.glyphs.numGlyphs * sizeof(QGlyphJustification)); memcpy(glyphs.attributes, m_item.glyphs.attributes, m_item.glyphs.numGlyphs * sizeof(QGlyphAttributes)); m_item.glyphs = glyphs; m_font = *m_item.f; m_item.f = &m_font; m_item.fontEngine->ref.ref(); // Increment reference count. } QTextItemIntCopy::~QTextItemIntCopy() { delete [] m_item.chars; delete [] m_item.logClusters; delete [] m_item.glyphs.data(); if (!m_item.fontEngine->ref.deref()) delete m_item.fontEngine; } /************************************************************************ * * QPaintBufferPrivate * ************************************************************************/ QPaintBufferPrivate::QPaintBufferPrivate() : ref(1), engine(nullptr), penWidthAdjustment(0) , calculateBoundingRect(true) , cache(nullptr) { } QPaintBufferPrivate::~QPaintBufferPrivate() { for (int i = 0; i < commands.size(); ++i) { const QPaintBufferCommand &cmd = commands.at(i); if (cmd.id == QPaintBufferPrivate::Cmd_DrawTextItem) delete reinterpret_cast(qvariant_cast(variants.at(cmd.offset))); } } inline void QPaintBufferPrivate::updateBoundingRect(const QRectF &br) { // transform to device coords and adjust for pen width Q_ASSERT(engine && engine->painter()); QPainter *painter = engine->painter(); const QTransform transform = painter->transform(); QRectF devRect = transform.mapRect(br); if (penWidthAdjustment > 0) { devRect = devRect.adjusted(-penWidthAdjustment, -penWidthAdjustment, penWidthAdjustment, penWidthAdjustment); } if (boundingRect.isEmpty()) { boundingRect = devRect; } else { qreal min_x = qMin(devRect.left(), boundingRect.left()); qreal min_y = qMin(devRect.top(), boundingRect.top()); qreal max_x = qMax(devRect.right(), boundingRect.right()); qreal max_y = qMax(devRect.bottom(), boundingRect.bottom()); boundingRect = QRectF(min_x, min_y, max_x - min_x, max_y - min_y); } if (painter->hasClipping()) boundingRect &= transform.mapRect(painter->clipRegion().boundingRect()); } /************************************************************************ * * QPaintBuffer * ************************************************************************/ QPaintBuffer::QPaintBuffer() : d_ptr(new QPaintBufferPrivate) { } QPaintBuffer::~QPaintBuffer() { if (!d_ptr->ref.deref()) delete d_ptr; } QPaintBuffer::QPaintBuffer(const QPaintBuffer &other) : QPaintDevice(), d_ptr(other.d_ptr) { d_ptr->ref.ref(); } QPaintEngine *QPaintBuffer::paintEngine() const { QPaintBufferPrivate *d = const_cast(this)->d_ptr; if (!d->engine) d->engine = new QPaintBufferEngine(d); return d->engine; } int QPaintBuffer::metric(PaintDeviceMetric metric) const { int val = 0; switch (metric) { case PdmWidth: val = qCeil(d_ptr->boundingRect.width()); break; case PdmHeight: val = qCeil(d_ptr->boundingRect.height()); break; case PdmDpiX: case PdmPhysicalDpiX: val = qt_defaultDpiX(); break; case PdmDpiY: case PdmPhysicalDpiY: val = qt_defaultDpiY(); break; // here & below: use values from QPaintDevice::metric to avoid runtime warnings case PdmNumColors: val = 256; break; case PdmDevicePixelRatio: val = 1; break; default: val = QPaintDevice::metric(metric); break; } return val; } int QPaintBuffer::devType() const { return QInternal::PaintBuffer; } QPaintBuffer &QPaintBuffer::operator=(const QPaintBuffer &other) { if (other.d_ptr != d_ptr) { QPaintBufferPrivate *data = other.d_ptr; data->ref.ref(); if (d_ptr->ref.deref()) delete d_ptr; d_ptr = data; } return *this; } bool QPaintBuffer::isEmpty() const { return d_ptr->commands.isEmpty(); } void QPaintBuffer::draw(QPainter *painter, int frame) const { #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << "QPaintBuffer::draw() --------------------------------"; Q_D(const QPaintBuffer); printf("Float buffer:"); for (int i=0; ifloats.size(); i++) { if ((i % 10) == 0) { printf("\n%4d-%4d: ", i, i+9); } printf("%4.2f ", d->floats[i]); } printf("\n"); printf("Int Buffer:"); for (int i=0; iints.size(); i++) { if ((i % 10) == 0) { printf("\n%4d-%4d: ", i, i+10); } printf("%5d", d->ints[i]); } printf("\n"); #endif processCommands(painter, frameStartIndex(frame), frameEndIndex(frame)); #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << "QPaintBuffer::draw() -------------------------------- DONE!"; #endif } int QPaintBuffer::frameStartIndex(int frame) const { return (frame == 0) ? 0 : d_ptr->frames.at(frame - 1); } int QPaintBuffer::frameEndIndex(int frame) const { return (frame == d_ptr->frames.size()) ? d_ptr->commands.size() : d_ptr->frames.at(frame); } int QPaintBuffer::processCommands(QPainter *painter, int begin, int end) const { if (!painter || !painter->isActive()) return 0; QPaintEngineEx *xengine = painter->paintEngine()->isExtended() ? (QPaintEngineEx *) painter->paintEngine() : nullptr; if (xengine) { QPaintEngineExReplayer player; player.processCommands(*this, painter, begin, end); } else { QPainterReplayer player; player.processCommands(*this, painter, begin, end); } int depth = 0; for (int i = begin; i < end; ++i) { const QPaintBufferCommand &cmd = d_ptr->commands.at(i); if (cmd.id == QPaintBufferPrivate::Cmd_Save) ++depth; else if (cmd.id == QPaintBufferPrivate::Cmd_Restore) --depth; } return depth; } #ifndef QT_NO_DEBUG_STREAM QString QPaintBuffer::commandDescription(int command) const { QString desc; QDebug debug(&desc); const QPaintBufferCommand &cmd = d_ptr->commands.at(command); switch (cmd.id) { case QPaintBufferPrivate::Cmd_Save: { debug << "Cmd_Save"; break; } case QPaintBufferPrivate::Cmd_Restore: { debug << "Cmd_Restore"; break; } case QPaintBufferPrivate::Cmd_SetBrush: { QBrush brush = qvariant_cast(d_ptr->variants.at(cmd.offset)); debug << "Cmd_SetBrush: " << brush; break; } case QPaintBufferPrivate::Cmd_SetBrushOrigin: { debug << "Cmd_SetBrushOrigin: " << d_ptr->variants.at(cmd.offset).toPointF(); break; } case QPaintBufferPrivate::Cmd_SetCompositionMode: { QPainter::CompositionMode mode = (QPainter::CompositionMode) cmd.extra; debug << "ExCmd_SetCompositionMode, mode: " << mode; break; } case QPaintBufferPrivate::Cmd_SetOpacity: { debug << "ExCmd_SetOpacity: " << d_ptr->variants.at(cmd.offset).toDouble(); break; } case QPaintBufferPrivate::Cmd_DrawVectorPath: { debug << "ExCmd_DrawVectorPath: size: " << cmd.size // << ", hints:" << d->ints[cmd.offset2+cmd.size] << "pts/elms:" << cmd.offset << cmd.offset2; break; } case QPaintBufferPrivate::Cmd_StrokeVectorPath: { QPen pen = qvariant_cast(d_ptr->variants.at(cmd.extra)); debug << "ExCmd_StrokeVectorPath: size: " << cmd.size // << ", hints:" << d->ints[cmd.offset2+cmd.size] << "pts/elms:" << cmd.offset << cmd.offset2 << pen; break; } case QPaintBufferPrivate::Cmd_FillVectorPath: { QBrush brush = qvariant_cast(d_ptr->variants.at(cmd.extra)); debug << "ExCmd_FillVectorPath: size: " << cmd.size // << ", hints:" << d->ints[cmd.offset2+cmd.size] << "pts/elms:" << cmd.offset << cmd.offset2 << brush; break; } case QPaintBufferPrivate::Cmd_FillRectBrush: { QBrush brush = qvariant_cast(d_ptr->variants.at(cmd.extra)); QRectF *rect = (QRectF *)(d_ptr->floats.constData() + cmd.offset); debug << "ExCmd_FillRectBrush, offset: " << cmd.offset << " rect: " << *rect << " brush: " << brush; break; } case QPaintBufferPrivate::Cmd_FillRectColor: { QColor color = qvariant_cast(d_ptr->variants.at(cmd.extra)); QRectF *rect = (QRectF *)(d_ptr->floats.constData() + cmd.offset); debug << "ExCmd_FillRectBrush, offset: " << cmd.offset << " rect: " << *rect << " color: " << color; break; } case QPaintBufferPrivate::Cmd_DrawPolygonF: { debug << "ExCmd_DrawPolygonF, offset: " << cmd.offset << " size: " << cmd.size << " mode: " << cmd.extra << d_ptr->floats.at(cmd.offset) << d_ptr->floats.at(cmd.offset+1); break; } case QPaintBufferPrivate::Cmd_DrawPolygonI: { debug << "ExCmd_DrawPolygonI, offset: " << cmd.offset << " size: " << cmd.size << " mode: " << cmd.extra << d_ptr->ints.at(cmd.offset) << d_ptr->ints.at(cmd.offset+1); break; } case QPaintBufferPrivate::Cmd_DrawEllipseF: { debug << "ExCmd_DrawEllipseF, offset: " << cmd.offset; break; } case QPaintBufferPrivate::Cmd_DrawLineF: { debug << "ExCmd_DrawLineF, offset: " << cmd.offset << " size: " << cmd.size; break; } case QPaintBufferPrivate::Cmd_DrawLineI: { debug << "ExCmd_DrawLineI, offset: " << cmd.offset << " size: " << cmd.size; break; } case QPaintBufferPrivate::Cmd_DrawPointsF: { debug << "ExCmd_DrawPointsF, offset: " << cmd.offset << " size: " << cmd.size; break; } case QPaintBufferPrivate::Cmd_DrawPointsI: { debug << "ExCmd_DrawPointsI, offset: " << cmd.offset << " size: " << cmd.size; break; } case QPaintBufferPrivate::Cmd_DrawPolylineF: { debug << "ExCmd_DrawPolylineF, offset: " << cmd.offset << " size: " << cmd.size; break; } case QPaintBufferPrivate::Cmd_DrawPolylineI: { debug << "ExCmd_DrawPolylineI, offset: " << cmd.offset << " size: " << cmd.size; break; } case QPaintBufferPrivate::Cmd_DrawRectF: { debug << "ExCmd_DrawRectF, offset: " << cmd.offset << " size: " << cmd.size; break; } case QPaintBufferPrivate::Cmd_DrawRectI: { debug << "ExCmd_DrawRectI, offset: " << cmd.offset << " size: " << cmd.size; break; } case QPaintBufferPrivate::Cmd_SetClipEnabled: { bool clipEnabled = d_ptr->variants.at(cmd.offset).toBool(); debug << "ExCmd_SetClipEnabled:" << clipEnabled; break; } case QPaintBufferPrivate::Cmd_ClipVectorPath: { QVectorPathCmd path(d_ptr, cmd); debug << "ExCmd_ClipVectorPath:" << path().elementCount(); break; } case QPaintBufferPrivate::Cmd_ClipRect: { QRect rect(QPoint(d_ptr->ints.at(cmd.offset), d_ptr->ints.at(cmd.offset + 1)), QPoint(d_ptr->ints.at(cmd.offset + 2), d_ptr->ints.at(cmd.offset + 3))); debug << "ExCmd_ClipRect:" << rect << cmd.extra; break; } case QPaintBufferPrivate::Cmd_ClipRegion: { QRegion region(d_ptr->variants.at(cmd.offset).value()); debug << "ExCmd_ClipRegion:" << region.boundingRect() << cmd.extra; break; } case QPaintBufferPrivate::Cmd_SetPen: { QPen pen = qvariant_cast(d_ptr->variants.at(cmd.offset)); debug << "Cmd_SetPen: " << pen; break; } case QPaintBufferPrivate::Cmd_SetTransform: { QTransform xform = qvariant_cast(d_ptr->variants.at(cmd.offset)); debug << "Cmd_SetTransform, offset: " << cmd.offset << xform; break; } case QPaintBufferPrivate::Cmd_SetRenderHints: { debug << "Cmd_SetRenderHints, hints: " << cmd.extra; break; } case QPaintBufferPrivate::Cmd_SetBackgroundMode: { debug << "Cmd_SetBackgroundMode: " << cmd.extra; break; } case QPaintBufferPrivate::Cmd_DrawConvexPolygonF: { debug << "Cmd_DrawConvexPolygonF, offset: " << cmd.offset << " size: " << cmd.size; break; } case QPaintBufferPrivate::Cmd_DrawConvexPolygonI: { debug << "Cmd_DrawConvexPolygonI, offset: " << cmd.offset << " size: " << cmd.size; break; } case QPaintBufferPrivate::Cmd_DrawEllipseI: { debug << "Cmd_DrawEllipseI, offset: " << cmd.offset; break; } case QPaintBufferPrivate::Cmd_DrawPixmapRect: { QPixmap pm(d_ptr->variants.at(cmd.offset).value()); QRectF r(d_ptr->floats.at(cmd.extra), d_ptr->floats.at(cmd.extra+1), d_ptr->floats.at(cmd.extra+2), d_ptr->floats.at(cmd.extra+3)); QRectF sr(d_ptr->floats.at(cmd.extra+4), d_ptr->floats.at(cmd.extra+5), d_ptr->floats.at(cmd.extra+6), d_ptr->floats.at(cmd.extra+7)); debug << "Cmd_DrawPixmapRect:" << r << sr << pm.size(); break; } case QPaintBufferPrivate::Cmd_DrawPixmapPos: { QPixmap pm(d_ptr->variants.at(cmd.offset).value()); QPointF pos(d_ptr->floats.at(cmd.extra), d_ptr->floats.at(cmd.extra+1)); debug << "Cmd_DrawPixmapPos:" << pos << pm.size(); break; } case QPaintBufferPrivate::Cmd_DrawTiledPixmap: { QPixmap pm(d_ptr->variants.at(cmd.offset).value()); QRectF r(d_ptr->floats.at(cmd.extra), d_ptr->floats.at(cmd.extra+1), d_ptr->floats.at(cmd.extra+2), d_ptr->floats.at(cmd.extra+3)); QPointF offset(d_ptr->floats.at(cmd.extra+4), d_ptr->floats.at(cmd.extra+5)); debug << "Cmd_DrawTiledPixmap:" << r << offset << pm.size(); break; } case QPaintBufferPrivate::Cmd_DrawImageRect: { QImage image(d_ptr->variants.at(cmd.offset).value()); QRectF r(d_ptr->floats.at(cmd.extra), d_ptr->floats.at(cmd.extra+1), d_ptr->floats.at(cmd.extra+2), d_ptr->floats.at(cmd.extra+3)); QRectF sr(d_ptr->floats.at(cmd.extra+4), d_ptr->floats.at(cmd.extra+5), d_ptr->floats.at(cmd.extra+6), d_ptr->floats.at(cmd.extra+7)); debug << "Cmd_DrawImageRect:" << r << sr << image.size(); break; } case QPaintBufferPrivate::Cmd_DrawImagePos: { QImage image(d_ptr->variants.at(cmd.offset).value()); QPointF pos(d_ptr->floats.at(cmd.extra), d_ptr->floats.at(cmd.extra+1)); debug << "Cmd_DrawImagePos:" << pos << image.size(); break; } case QPaintBufferPrivate::Cmd_DrawText: { QPointF pos(d_ptr->floats.at(cmd.extra), d_ptr->floats.at(cmd.extra+1)); QList variants(d_ptr->variants.at(cmd.offset).value >()); QFont font(variants.at(0).value()); QString text(variants.at(1).value()); debug << "Cmd_DrawText:" << pos << text << font.family(); break; } case QPaintBufferPrivate::Cmd_DrawTextItem: { QPointF pos(d_ptr->floats.at(cmd.extra), d_ptr->floats.at(cmd.extra+1)); QTextItemIntCopy *tiCopy = reinterpret_cast(qvariant_cast(d_ptr->variants.at(cmd.offset))); QTextItemInt &ti = (*tiCopy)(); QString text(ti.text()); debug << "Cmd_DrawTextItem:" << pos << " " << text; break; } case QPaintBufferPrivate::Cmd_SystemStateChanged: { QRegion systemClip(d_ptr->variants.at(cmd.offset).value()); debug << "Cmd_SystemStateChanged:" << systemClip; break; } case QPaintBufferPrivate::Cmd_Translate: { QPointF delta(d_ptr->floats.at(cmd.extra), d_ptr->floats.at(cmd.extra+1)); debug << "Cmd_Translate:" << delta; break; } case QPaintBufferPrivate::Cmd_DrawStaticText: { debug << "Cmd_DrawStaticText"; break; } } return desc; } #endif QRectF QPaintBuffer::boundingRect() const { return d_ptr->boundingRect; } void QPaintBuffer::setBoundingRect(const QRectF &rect) { d_ptr->boundingRect = rect; d_ptr->calculateBoundingRect = false; } class QPaintBufferEnginePrivate : public QPaintEngineExPrivate { Q_DECLARE_PUBLIC(QPaintBufferEngine) public: void systemStateChanged() { Q_Q(QPaintBufferEngine); q->buffer->addCommand(QPaintBufferPrivate::Cmd_SystemStateChanged, QVariant(systemClip)); } QTransform last; }; /************************************************************************ * * QPaintBufferEngine * ************************************************************************/ QPaintBufferEngine::QPaintBufferEngine(QPaintBufferPrivate *b) : QPaintEngineEx(*(new QPaintBufferEnginePrivate)) , buffer(b) , m_begin_detected(false) , m_save_detected(false) , m_stream_raw_text_items(false) { } bool QPaintBufferEngine::begin(QPaintDevice *) { Q_D(QPaintBufferEngine); painter()->save(); d->systemStateChanged(); return true; } bool QPaintBufferEngine::end() { painter()->restore(); m_created_state = nullptr; return true; } QPainterState *QPaintBufferEngine::createState(QPainterState *orig) const { #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << "QPaintBufferEngine: createState, orig=" << orig << ", current=" << state(); #endif Q_ASSERT(!m_begin_detected); Q_ASSERT(!m_save_detected); if (orig == nullptr) { m_begin_detected = true; return new QPainterState(); } else { m_save_detected = true; return new QPainterState(orig); } } void QPaintBufferEngine::clip(const QVectorPath &path, Qt::ClipOperation op) { #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << "QPaintBufferEngine: clip vpath:" << path.elementCount() << "op:" << op; #endif QPaintBufferCommand *cmd = buffer->addCommand(QPaintBufferPrivate::Cmd_ClipVectorPath, path); cmd->extra = op; } void QPaintBufferEngine::clip(const QRect &rect, Qt::ClipOperation op) { #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << "QPaintBufferEngine: clip rect:" << rect << "op:" << op; #endif QPaintBufferCommand *cmd = buffer->addCommand(QPaintBufferPrivate::Cmd_ClipRect, (int *) &rect, 4, 1); cmd->extra = op; } void QPaintBufferEngine::clip(const QRegion ®ion, Qt::ClipOperation op) { #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << "QPaintBufferEngine: clip region br:" << region.boundingRect() << "op:" << op; #endif QPaintBufferCommand *cmd = buffer->addCommand(QPaintBufferPrivate::Cmd_ClipRegion, QVariant(region)); cmd->extra = op; } void QPaintBufferEngine::clip(const QPainterPath &path, Qt::ClipOperation op) { // ### TODO // QPaintBufferCommand *cmd = // buffer->addCommand(QPaintBufferPrivate::Cmd_ClipPath, QVariant(path)); // cmd->extra = op; QPaintEngineEx::clip(path, op); } void QPaintBufferEngine::clipEnabledChanged() { #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << "QPaintBufferEngine: clip enable change" << state()->clipEnabled; #endif buffer->addCommand(QPaintBufferPrivate::Cmd_SetClipEnabled, state()->clipEnabled); } void QPaintBufferEngine::penChanged() { const QPen &pen = state()->pen; if (!buffer->commands.isEmpty() && buffer->commands.last().id == QPaintBufferPrivate::Cmd_SetPen) { #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << "QPaintBufferEngine: penChanged (compressed)" << state()->pen; #endif buffer->variants[buffer->commands.last().offset] = pen; return; } if (buffer->calculateBoundingRect) { if (pen.style() == Qt::NoPen) { buffer->penWidthAdjustment = 0; } else { qreal penWidth = (pen.widthF() == 0) ? 1 : pen.widthF(); QPointF transformedWidth(penWidth, penWidth); if (!qt_pen_is_cosmetic(pen, state()->renderHints)) transformedWidth = painter()->transform().map(transformedWidth); buffer->penWidthAdjustment = transformedWidth.x() / 2.0; } } #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << "QPaintBufferEngine: penChanged" << state()->pen; #endif buffer->addCommand(QPaintBufferPrivate::Cmd_SetPen, pen); } void QPaintBufferEngine::brushChanged() { const QBrush &brush = state()->brush; if (!buffer->commands.isEmpty() && buffer->commands.last().id == QPaintBufferPrivate::Cmd_SetBrush) { #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << "QPaintBufferEngine: brushChanged (compressed)" << state()->brush; #endif buffer->variants[buffer->commands.last().offset] = brush; return; } #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << "QPaintBufferEngine: brushChanged" << state()->brush; #endif buffer->addCommand(QPaintBufferPrivate::Cmd_SetBrush, brush); } void QPaintBufferEngine::brushOriginChanged() { #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << "QPaintBufferEngine: brush origin changed" << state()->brushOrigin; #endif buffer->addCommand(QPaintBufferPrivate::Cmd_SetBrushOrigin, state()->brushOrigin); } void QPaintBufferEngine::opacityChanged() { #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << "QPaintBufferEngine: opacity changed" << state()->opacity; #endif buffer->addCommand(QPaintBufferPrivate::Cmd_SetOpacity, state()->opacity); } void QPaintBufferEngine::compositionModeChanged() { #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << "QPaintBufferEngine: composition mode" << state()->composition_mode; #endif QPaintBufferCommand *cmd = buffer->addCommand(QPaintBufferPrivate::Cmd_SetCompositionMode); cmd->extra = state()->composition_mode; } void QPaintBufferEngine::renderHintsChanged() { #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << "QPaintBufferEngine: render hints changed" << state()->renderHints; #endif QPaintBufferCommand *cmd = buffer->addCommand(QPaintBufferPrivate::Cmd_SetRenderHints); cmd->extra = state()->renderHints; } void QPaintBufferEngine::transformChanged() { Q_D(QPaintBufferEngine); const QTransform &transform = state()->matrix; QTransform delta; bool invertible = false; if (transform.type() <= QTransform::TxScale && transform.type() == d->last.type()) delta = transform * d->last.inverted(&invertible); d->last = transform; if (invertible && delta.type() == QTransform::TxNone) return; if (invertible && delta.type() == QTransform::TxTranslate) { #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << "QPaintBufferEngine: transformChanged (translate only) " << state()->matrix; #endif QPaintBufferCommand *cmd = buffer->addCommand(QPaintBufferPrivate::Cmd_Translate); qreal data[] = { delta.dx(), delta.dy() }; cmd->extra = buffer->addData((qreal *) data, 2); return; } // ### accumulate, like in QBrush case... if (!buffer->commands.isEmpty() && buffer->commands.last().id == QPaintBufferPrivate::Cmd_SetTransform) { #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << "QPaintBufferEngine: transformChanged (compressing) " << state()->matrix; #endif buffer->variants[buffer->commands.last().offset] = state()->matrix; return; } #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << "QPaintBufferEngine: transformChanged:" << state()->matrix; #endif buffer->addCommand(QPaintBufferPrivate::Cmd_SetTransform, state()->matrix); } void QPaintBufferEngine::backgroundModeChanged() { #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << "QPaintEngineBuffer: background mode changed" << state()->bgMode; #endif QPaintBufferCommand *cmd = buffer->addCommand(QPaintBufferPrivate::Cmd_SetBackgroundMode); cmd->extra = state()->bgMode; } void QPaintBufferEngine::draw(const QVectorPath &path) { #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << "QPaintBufferEngine: draw vpath:" << path.elementCount(); #endif bool hasBrush = qbrush_style(state()->brush) != Qt::NoBrush; bool hasPen = qpen_style(state()->pen) != Qt::NoPen && qbrush_style(qpen_brush(state()->pen)) != Qt::NoBrush; if (hasPen || hasBrush) buffer->addCommand(QPaintBufferPrivate::Cmd_DrawVectorPath, path); #ifdef QPAINTBUFFER_DEBUG_DRAW else qDebug() << " - no pen or brush active, discarded...\n"; #endif // if (buffer->calculateBoundingRect) { // QRealRect r = path.controlPointRect(); // buffer->updateBoundingRect(QRectF(r.x1, r.y1, r.x2 - r.x1, r.y2 - r.y1)); // } } void QPaintBufferEngine::fill(const QVectorPath &path, const QBrush &brush) { #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << "QPaintBufferEngine: fill vpath:" << path.elementCount() << brush; #endif QPaintBufferCommand *cmd = buffer->addCommand(QPaintBufferPrivate::Cmd_FillVectorPath, path); cmd->extra = buffer->addData(QVariant(brush)); // if (buffer->calculateBoundingRect) { // QRealRect r = path.controlPointRect(); // buffer->updateBoundingRect(QRectF(r.x1, r.y1, r.x2 - r.x1, r.y2 - r.y1)); // } } void QPaintBufferEngine::stroke(const QVectorPath &path, const QPen &pen) { #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << "QPaintBufferEngine: stroke vpath:" << path.elementCount() << pen; #endif QPaintBufferCommand *cmd = buffer->addCommand(QPaintBufferPrivate::Cmd_StrokeVectorPath, path); cmd->extra = buffer->addData(QVariant(pen)); // if (buffer->calculateBoundingRect) { // QRealRect r = path.controlPointRect(); // buffer->updateBoundingRect(QRectF(r.x1, r.y1, r.x2 - r.x1, r.y2 - r.y1)); // } } void QPaintBufferEngine::fillRect(const QRectF &rect, const QBrush &brush) { #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << "QPaintBufferEngine: fillRect brush:" << rect << brush; #endif QPaintBufferCommand *cmd = buffer->addCommand(QPaintBufferPrivate::Cmd_FillRectBrush, (qreal *) &rect, 4, 1); cmd->extra = buffer->addData(brush); if (buffer->calculateBoundingRect) buffer->updateBoundingRect(rect); } void QPaintBufferEngine::fillRect(const QRectF &rect, const QColor &color) { #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << "QPaintBufferEngine: fillRect color:" << rect << color; #endif QPaintBufferCommand *cmd = buffer->addCommand(QPaintBufferPrivate::Cmd_FillRectColor, (qreal *) &rect, 4, 1); cmd->extra = buffer->addData(color); if (buffer->calculateBoundingRect) buffer->updateBoundingRect(rect); } void QPaintBufferEngine::drawRects(const QRect *rects, int rectCount) { #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << "QPaintBufferEngine: drawRectsI:" << rectCount; #endif QPaintBufferCommand *cmd = buffer->addCommand(QPaintBufferPrivate::Cmd_DrawRectI, (int *) rects, 4 * rectCount, rectCount); cmd->extra = rectCount; if (buffer->calculateBoundingRect) { if (rectCount == 1) { buffer->updateBoundingRect(rects[0]); } else { int min_x = rects[0].left(); int min_y = rects[0].top(); int max_x = rects[0].left() + rects[0].width(); int max_y = rects[0].top() + rects[0].height(); for (int i=1; i< rectCount; ++i) { if (rects[i].left() < min_x) min_x = rects[i].left(); if (rects[i].top() < min_y) min_y = rects[i].top(); if (rects[i].right() > max_x) max_x = rects[i].left() + rects[i].width(); if (rects[i].bottom() > max_y) max_y = rects[i].top() + rects[i].height(); } buffer->updateBoundingRect(QRectF(min_x, min_y, max_x - min_x, max_y - min_y)); } } } void QPaintBufferEngine::drawRects(const QRectF *rects, int rectCount) { #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << "QPaintBufferEngine: drawRectsF:" << rectCount; #endif QPaintBufferCommand *cmd = buffer->addCommand(QPaintBufferPrivate::Cmd_DrawRectF, (qreal *) rects, 4 * rectCount, rectCount); cmd->extra = rectCount; if (buffer->calculateBoundingRect) { if (rectCount == 1) { buffer->updateBoundingRect(rects[0]); } else { qreal min_x = rects[0].left(); qreal min_y = rects[0].top(); qreal max_x = rects[0].right(); qreal max_y = rects[0].bottom(); for (int i=1; i< rectCount; ++i) { if (rects[i].left() < min_x) min_x = rects[i].left(); if (rects[i].top() < min_y) min_y = rects[i].top(); if (rects[i].right() > max_x) max_x = rects[i].right(); if (rects[i].bottom() > max_y) max_y = rects[i].bottom(); } buffer->updateBoundingRect(QRectF(min_x, min_y, max_x - min_x, max_y - min_y)); } } } void QPaintBufferEngine::drawLines(const QLine *lines, int lineCount) { #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << "QPaintBufferEngine: drawLinesI:" << lineCount; #endif QPaintBufferCommand *cmd = buffer->addCommand(QPaintBufferPrivate::Cmd_DrawLineI, (int *) lines, 4 * lineCount, lineCount); cmd->extra = lineCount; if (buffer->calculateBoundingRect) { int min_x = lines[0].p1().x(); int min_y = lines[0].p1().y(); int max_x = lines[0].p2().x(); int max_y = lines[0].p2().y(); if (min_x > max_x) qSwap(min_x, max_x); if (min_y > max_y) qSwap(min_y, max_y); for (int i=1; i < lineCount; ++i) { int p1_x = lines[i].p1().x(); int p1_y = lines[i].p1().y(); int p2_x = lines[i].p2().x(); int p2_y = lines[i].p2().y(); if (p1_x > p2_x) { min_x = qMin(p2_x, min_x); max_x = qMax(p1_x, max_x); } else { min_x = qMin(p1_x, min_x); max_x = qMax(p2_x, max_x); } if (p1_y > p2_y) { min_y = qMin(p2_y, min_y); max_y = qMax(p1_y, max_y); } else { min_y = qMin(p1_y, min_y); max_y = qMax(p2_y, max_y); } } buffer->updateBoundingRect(QRectF(min_x, min_y, max_x - min_x, max_y - min_y)); } } void QPaintBufferEngine::drawLines(const QLineF *lines, int lineCount) { #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << "QPaintBufferEngine: drawLinesF:" << lineCount; #endif QPaintBufferCommand *cmd = buffer->addCommand(QPaintBufferPrivate::Cmd_DrawLineF, (qreal *) lines, 4 * lineCount, lineCount); cmd->extra = lineCount; if (buffer->calculateBoundingRect) { qreal min_x = lines[0].p1().x(); qreal min_y = lines[0].p1().y(); qreal max_x = lines[0].p2().x(); qreal max_y = lines[0].p2().y(); if (min_x > max_x) qSwap(min_x, max_x); if (min_y > max_y) qSwap(min_y, max_y); for (int i=1; i < lineCount; ++i) { qreal p1_x = lines[i].p1().x(); qreal p1_y = lines[i].p1().y(); qreal p2_x = lines[i].p2().x(); qreal p2_y = lines[i].p2().y(); if (p1_x > p2_x) { min_x = qMin(p2_x, min_x); max_x = qMax(p1_x, max_x); } else { min_x = qMin(p1_x, min_x); max_x = qMax(p2_x, max_x); } if (p1_y > p2_y) { min_y = qMin(p2_y, min_y); max_y = qMax(p1_y, max_y); } else { min_y = qMin(p1_y, min_y); max_y = qMax(p2_y, max_y); } } buffer->updateBoundingRect(QRectF(min_x, min_y, max_x - min_x, max_y - min_y)); } } void QPaintBufferEngine::drawEllipse(const QRectF &r) { #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << "QPaintBufferEngine: drawEllipseF:" << r; #endif buffer->addCommand(QPaintBufferPrivate::Cmd_DrawEllipseF, (qreal *) &r, 4, 1); if (buffer->calculateBoundingRect) buffer->updateBoundingRect(r); } void QPaintBufferEngine::drawEllipse(const QRect &r) { #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << "QPaintBufferEngine: drawEllipseI:" << r; #endif buffer->addCommand(QPaintBufferPrivate::Cmd_DrawEllipseI, (int *) &r, 4, 1); if (buffer->calculateBoundingRect) buffer->updateBoundingRect(r); } void QPaintBufferEngine::drawPath(const QPainterPath &path) { // #ifdef QPAINTBUFFER_DEBUG_DRAW // qDebug() << "QPaintBufferEngine: drawPath: element count:" << path.elementCount(); // #endif // // ### Path -> QVariant // // buffer->addCommand(QPaintBufferPrivate::Cmd_DrawPath, QVariant(path)); QPaintEngineEx::drawPath(path); // if (buffer->calculateBoundingRect) // buffer->updateBoundingRect(path.boundingRect()); } void QPaintBufferEngine::drawPoints(const QPoint *points, int pointCount) { #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << "QPaintBufferEngine: drawPointsI: " << pointCount; #endif buffer->addCommand(QPaintBufferPrivate::Cmd_DrawPointsI, (int *) points, 2 * pointCount, pointCount); if (buffer->calculateBoundingRect) { int min_x = points[0].x(); int min_y = points[0].y(); int max_x = points[0].x()+1; int max_y = points[0].y()+1; for (int i=1; iupdateBoundingRect(QRectF(min_x, min_y, max_x - min_x, max_y - min_y)); } } void QPaintBufferEngine::drawPoints(const QPointF *points, int pointCount) { #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << "QPaintBufferEngine: drawPointsF: " << pointCount; #endif buffer->addCommand(QPaintBufferPrivate::Cmd_DrawPointsF, (qreal *) points, 2 * pointCount, pointCount); if (buffer->calculateBoundingRect) { qreal min_x = points[0].x(); qreal min_y = points[0].y(); qreal max_x = points[0].x()+1; qreal max_y = points[0].y()+1; for (int i=1; iupdateBoundingRect(QRectF(min_x, min_y, max_x - min_x, max_y - min_y)); } } void QPaintBufferEngine::drawPolygon(const QPoint *pts, int count, PolygonDrawMode mode) { #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << "QPaintBufferEngine: drawPolygonI: size:" << count << ", mode:" << mode; #endif if (mode == QPaintEngine::OddEvenMode || mode == QPaintEngine::WindingMode) { QPaintBufferCommand *cmd = buffer->addCommand(QPaintBufferPrivate::Cmd_DrawPolygonI, (int *) pts, 2 * count, count); cmd->extra = mode; } else if (mode == QPaintEngine::PolylineMode) { buffer->addCommand(QPaintBufferPrivate::Cmd_DrawPolylineI, (int *) pts, 2 * count, count); } else { buffer->addCommand(QPaintBufferPrivate::Cmd_DrawConvexPolygonI, (int *) pts, 2 * count, count); } if (buffer->calculateBoundingRect) { int min_x = pts[0].x(); int min_y = pts[0].y(); int max_x = pts[0].x(); int max_y = pts[0].y(); for (int i=1; iupdateBoundingRect(QRectF(min_x, min_y, max_x - min_x, max_y - min_y)); } } void QPaintBufferEngine::drawPolygon(const QPointF *pts, int count, PolygonDrawMode mode) { #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << "QPaintBufferEngine: drawPolygonF: size:" << count << ", mode:" << mode; #endif if (mode == QPaintEngine::OddEvenMode || mode == QPaintEngine::WindingMode) { QPaintBufferCommand *cmd = buffer->addCommand(QPaintBufferPrivate::Cmd_DrawPolygonF, (qreal *) pts, 2 * count, count); cmd->extra = mode; } else if (mode == QPaintEngine::PolylineMode) { buffer->addCommand(QPaintBufferPrivate::Cmd_DrawPolylineF, (qreal *) pts, 2 * count, count); } else { buffer->addCommand(QPaintBufferPrivate::Cmd_DrawConvexPolygonF, (qreal *) pts, 2 * count, count); } if (buffer->calculateBoundingRect) { qreal min_x = pts[0].x(); qreal min_y = pts[0].y(); qreal max_x = pts[0].x(); qreal max_y = pts[0].y(); for (int i=1; iupdateBoundingRect(QRectF(min_x, min_y, max_x - min_x, max_y - min_y)); } } void QPaintBufferEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr) { #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << "QPaintBufferEngine: drawPixmap: src/dest rects " << r << sr; #endif QPaintBufferCommand *cmd = buffer->addCommand(QPaintBufferPrivate::Cmd_DrawPixmapRect, QVariant(pm)); cmd->extra = buffer->addData((qreal *) &r, 4); buffer->addData((qreal *) &sr, 4); if (buffer->calculateBoundingRect) buffer->updateBoundingRect(r); } void QPaintBufferEngine::drawPixmap(const QPointF &pos, const QPixmap &pm) { #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << "QPaintBufferEngine: drawPixmap: pos:" << pos; #endif QPaintBufferCommand *cmd = buffer->addCommand(QPaintBufferPrivate::Cmd_DrawPixmapPos, QVariant(pm)); cmd->extra = buffer->addData((qreal *) &pos, 2); if (buffer->calculateBoundingRect) buffer->updateBoundingRect(QRectF(pos, pm.size())); } static inline QImage qpaintbuffer_storable_image(const QImage &src) { QImageData *d = const_cast(src).data_ptr(); return d->own_data ? src : src.copy(); } void QPaintBufferEngine::drawImage(const QRectF &r, const QImage &image, const QRectF &sr, Qt::ImageConversionFlags /*flags */) { #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << "QPaintBufferEngine: drawImage: src/dest rects " << r << sr; #endif QPaintBufferCommand *cmd = buffer->addCommand(QPaintBufferPrivate::Cmd_DrawImageRect, QVariant(qpaintbuffer_storable_image(image))); cmd->extra = buffer->addData((qreal *) &r, 4); buffer->addData((qreal *) &sr, 4); // ### flags... if (buffer->calculateBoundingRect) buffer->updateBoundingRect(r); } void QPaintBufferEngine::drawImage(const QPointF &pos, const QImage &image) { #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << "QPaintBufferEngine: drawImage: pos:" << pos; #endif QPaintBufferCommand *cmd = buffer->addCommand(QPaintBufferPrivate::Cmd_DrawImagePos, QVariant(qpaintbuffer_storable_image(image))); cmd->extra = buffer->addData((qreal *) &pos, 2); if (buffer->calculateBoundingRect) buffer->updateBoundingRect(QRectF(pos, image.size())); } void QPaintBufferEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pm, const QPointF &s) { #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << "QPaintBufferEngine: drawTiledPixmap: src rect/offset:" << r << s; #endif QPaintBufferCommand *cmd = buffer->addCommand(QPaintBufferPrivate::Cmd_DrawTiledPixmap, QVariant(pm)); cmd->extra = buffer->addData((qreal *) &r, 4); buffer->addData((qreal *) &s, 2); if (buffer->calculateBoundingRect) buffer->updateBoundingRect(r); } void QPaintBufferEngine::drawStaticTextItem(QStaticTextItem *staticTextItem) { if (staticTextItem->usesRawFont) { QPaintEngineEx::drawStaticTextItem(staticTextItem); // Draw as path return; } QVariantList variants; variants << QVariant(staticTextItem->font); for (int i=0; inumGlyphs; ++i) { variants.append(staticTextItem->glyphs[i]); variants.append(staticTextItem->glyphPositions[i].toPointF()); } buffer->addCommand(QPaintBufferPrivate::Cmd_DrawStaticText, QVariant(variants)); } void QPaintBufferEngine::drawTextItem(const QPointF &pos, const QTextItem &ti) { #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << "QPaintBufferEngine: drawTextItem: pos:" << pos << ti.text(); #endif if (m_stream_raw_text_items) { QPaintBufferCommand *cmd = buffer->addCommand(QPaintBufferPrivate::Cmd_DrawTextItem, QVariant::fromValue(new QTextItemIntCopy(ti))); QFont font(ti.font()); font.setUnderline(false); font.setStrikeOut(false); font.setOverline(false); const QTextItemInt &si = static_cast(ti); qreal justificationWidth = 0; if (si.justified) justificationWidth = si.width.toReal(); int renderFlags = ti.renderFlags(); qreal scaleFactor = font.d->dpi/qreal(qt_defaultDpiY()); buffer->addData(QVariant(font)); cmd->extra = buffer->addData((qreal *) &pos, 2); buffer->addData((qreal *) &justificationWidth, 1); buffer->addData((qreal *) &scaleFactor, 1); cmd->offset2 = buffer->addData((int *) &renderFlags, 1); } else { QList variants; variants << QVariant(ti.font()) << QVariant(ti.text()); QPaintBufferCommand *cmd = buffer->addCommand(QPaintBufferPrivate::Cmd_DrawText, QVariant(variants)); cmd->extra = buffer->addData((qreal *) &pos, 2); } if (buffer->calculateBoundingRect) buffer->updateBoundingRect(QRectF(pos, QSize(ti.width(), ti.ascent() + ti.descent() + 1))); } void QPaintBufferEngine::setState(QPainterState *s) { Q_D(QPaintBufferEngine); if (m_begin_detected) { #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << "QPaintBufferEngine: setState: begin, ignoring."; #endif m_begin_detected = false; } else if (m_save_detected) { #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << "QPaintBufferEngine: setState: save."; #endif m_save_detected = false; buffer->addCommand(QPaintBufferPrivate::Cmd_Save); } else { #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << "QPaintBufferEngine: setState: restore."; #endif buffer->addCommand(QPaintBufferPrivate::Cmd_Restore); } d->last = s->matrix; QPaintEngineEx::setState(s); } /*********************************************************************** * * class QPaintBufferPlayback_Painter * */ // QFakeDevice is used to create fonts with a custom DPI // class QFakeDevice : public QPaintDevice { public: QFakeDevice() { dpi_x = qt_defaultDpiX(); dpi_y = qt_defaultDpiY(); } void setDpiX(int dpi) { dpi_x = dpi; } void setDpiY(int dpi) { dpi_y = dpi; } QPaintEngine *paintEngine() const { return nullptr; } int metric(PaintDeviceMetric m) const { switch(m) { case PdmPhysicalDpiX: case PdmDpiX: return dpi_x; case PdmPhysicalDpiY: case PdmDpiY: return dpi_y; default: return QPaintDevice::metric(m); } } private: int dpi_x; int dpi_y; }; void QPainterReplayer::setupTransform(QPainter *_painter) { painter = _painter; m_world_matrix = painter->transform(); m_world_matrix.scale(qreal(painter->device()->logicalDpiX()) / qreal(qt_defaultDpiX()), qreal(painter->device()->logicalDpiY()) / qreal(qt_defaultDpiY())); painter->setTransform(m_world_matrix); } void QPainterReplayer::processCommands(const QPaintBuffer &buffer, QPainter *p, int begin, int end) { d = buffer.d_ptr; painter = p; for (int cmdIndex = begin; cmdIndex < end; ++cmdIndex) { const QPaintBufferCommand &cmd = d->commands.at(cmdIndex); process(cmd); } } void QPaintBuffer::beginNewFrame() { if (!d_ptr->commands.isEmpty()) d_ptr->frames << d_ptr->commands.size(); } int QPaintBuffer::numFrames() const { return d_ptr->frames.size() + 1; } void QPainterReplayer::process(const QPaintBufferCommand &cmd) { switch (cmd.id) { case QPaintBufferPrivate::Cmd_Save: { #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << " -> Cmd_Save"; #endif painter->save(); break; } case QPaintBufferPrivate::Cmd_Restore: { #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << " -> Cmd_Restore"; #endif painter->restore(); break; } case QPaintBufferPrivate::Cmd_SetPen: { QPen pen = qvariant_cast(d->variants.at(cmd.offset)); #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << " -> Cmd_SetPen: " << pen; #endif painter->setPen(pen); break; } case QPaintBufferPrivate::Cmd_SetBrush: { QBrush brush = qvariant_cast(d->variants.at(cmd.offset)); #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << " -> Cmd_SetBrush: " << brush; #endif painter->setBrush(brush); break; } case QPaintBufferPrivate::Cmd_SetBrushOrigin: { #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << " -> Cmd_SetBrushOrigin: " << d->variants.at(cmd.offset).toPointF(); #endif painter->setBrushOrigin(d->variants.at(cmd.offset).toPointF()); break; } case QPaintBufferPrivate::Cmd_SetTransform: { QTransform xform = qvariant_cast(d->variants.at(cmd.offset)); #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << " -> Cmd_SetTransform, offset: " << cmd.offset << xform; #endif painter->setTransform(xform * m_world_matrix); break; } case QPaintBufferPrivate::Cmd_Translate: { QPointF delta(d->floats.at(cmd.extra), d->floats.at(cmd.extra+1)); #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << " -> Cmd_Translate, offset: " << cmd.offset << delta; #endif painter->translate(delta.x(), delta.y()); return; } case QPaintBufferPrivate::Cmd_SetCompositionMode: { QPainter::CompositionMode mode = (QPainter::CompositionMode) cmd.extra; #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << " -> Cmd_SetCompositionMode, mode: " << mode; #endif painter->setCompositionMode(mode); break; } case QPaintBufferPrivate::Cmd_SetRenderHints: { #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << " -> Cmd_SetRenderHints, hints: " << cmd.extra; #endif QPainter::RenderHints ph = painter->renderHints(); QPainter::RenderHints nh = (QPainter::RenderHints) cmd.extra; QPainter::RenderHints xored = ph ^ nh; if (xored & QPainter::Antialiasing) painter->setRenderHint(QPainter::Antialiasing, nh & QPainter::Antialiasing); if (xored & QPainter::HighQualityAntialiasing) painter->setRenderHint(QPainter::HighQualityAntialiasing, nh & QPainter::HighQualityAntialiasing); if (xored & QPainter::TextAntialiasing) painter->setRenderHint(QPainter::TextAntialiasing, nh & QPainter::TextAntialiasing); if (xored & QPainter::SmoothPixmapTransform) painter->setRenderHint(QPainter::SmoothPixmapTransform, nh & QPainter::SmoothPixmapTransform); if (xored & QPainter::NonCosmeticDefaultPen) painter->setRenderHint(QPainter::NonCosmeticDefaultPen, nh & QPainter::NonCosmeticDefaultPen); if (xored & QPainter::Qt4CompatiblePainting) painter->setRenderHint(QPainter::Qt4CompatiblePainting, nh & QPainter::Qt4CompatiblePainting); break; } case QPaintBufferPrivate::Cmd_SetOpacity: { #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << " -> Cmd_SetOpacity: " << d->variants.at(cmd.offset).toDouble(); #endif painter->setOpacity(d->variants.at(cmd.offset).toDouble()); break; } case QPaintBufferPrivate::Cmd_SetBackgroundMode: { #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << " -> Cmd_SetBackgroundMode: " << cmd.extra; #endif painter->setBackgroundMode((Qt::BGMode)cmd.extra); break; } case QPaintBufferPrivate::Cmd_DrawVectorPath: { #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << " -> Cmd_DrawVectorPath: size: " << cmd.size // << ", hints:" << d->ints[cmd.offset2+cmd.size] << "pts/elms:" << cmd.offset << cmd.offset2; #endif QVectorPathCmd path(d, cmd); painter->drawPath(path().convertToPainterPath()); break; } case QPaintBufferPrivate::Cmd_StrokeVectorPath: { QPen pen = qvariant_cast(d->variants.at(cmd.extra)); #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << " -> Cmd_StrokeVectorPath: size: " << cmd.size // << ", hints:" << d->ints[cmd.offset2+cmd.size] << "pts/elms:" << cmd.offset << cmd.offset2; #endif QVectorPathCmd path(d, cmd); painter->strokePath(path().convertToPainterPath(), pen); break; } case QPaintBufferPrivate::Cmd_FillVectorPath: { QBrush brush = qvariant_cast(d->variants.at(cmd.extra)); #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << " -> Cmd_FillVectorPath: size: " << cmd.size // << ", hints:" << d->ints[cmd.offset2+cmd.size] << "pts/elms:" << cmd.offset << cmd.offset2 << brush; #endif QVectorPathCmd path(d, cmd); painter->fillPath(path().convertToPainterPath(), brush); break; } case QPaintBufferPrivate::Cmd_DrawPolygonF: { #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << " -> Cmd_DrawPolygonF, offset: " << cmd.offset << " size: " << cmd.size << " mode: " << cmd.extra << d->floats.at(cmd.offset) << d->floats.at(cmd.offset+1); #endif Qt::FillRule fill = (QPaintEngine::PolygonDrawMode) cmd.extra == QPaintEngine::OddEvenMode ? Qt::OddEvenFill : Qt::WindingFill; painter->drawPolygon((QPointF *) (d->floats.constData() + cmd.offset), cmd.size, fill); break; } case QPaintBufferPrivate::Cmd_DrawPolygonI: { #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << " -> Cmd_DrawPolygonI, offset: " << cmd.offset << " size: " << cmd.size << " mode: " << cmd.extra << d->ints.at(cmd.offset) << d->ints.at(cmd.offset+1); #endif Qt::FillRule fill = (QPaintEngine::PolygonDrawMode) cmd.extra == QPaintEngine::OddEvenMode ? Qt::OddEvenFill : Qt::WindingFill; painter->drawPolygon((QPoint *) (d->ints.constData() + cmd.offset), cmd.size, fill); break; } case QPaintBufferPrivate::Cmd_DrawPolylineF: { #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << " -> Cmd_DrawPolylineF, offset: " << cmd.offset << " size: " << cmd.size; #endif painter->drawPolyline((QPointF *) (d->floats.constData() + cmd.offset), cmd.size); break; } case QPaintBufferPrivate::Cmd_DrawPolylineI: { #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << " -> Cmd_DrawPolylineI, offset: " << cmd.offset << " size: " << cmd.size; #endif painter->drawPolyline((QPoint *) (d->ints.constData() + cmd.offset), cmd.size); break; } case QPaintBufferPrivate::Cmd_DrawConvexPolygonF: { #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << " -> Cmd_DrawConvexPolygonF, offset: " << cmd.offset << " size: " << cmd.size; #endif painter->drawConvexPolygon((QPointF *) (d->floats.constData() + cmd.offset), cmd.size); break; } case QPaintBufferPrivate::Cmd_DrawConvexPolygonI: { #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << " -> Cmd_DrawConvexPolygonI, offset: " << cmd.offset << " size: " << cmd.size; #endif painter->drawConvexPolygon((QPoint *) (d->ints.constData() + cmd.offset), cmd.size); break; } case QPaintBufferPrivate::Cmd_DrawEllipseF: { #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << " -> Cmd_DrawEllipseF, offset: " << cmd.offset; #endif painter->drawEllipse(*(QRectF *)(d->floats.constData() + cmd.offset)); break; } case QPaintBufferPrivate::Cmd_DrawEllipseI: { #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << " -> Cmd_DrawEllipseI, offset: " << cmd.offset; #endif painter->drawEllipse(*(QRect *)(d->ints.constData() + cmd.offset)); break; } case QPaintBufferPrivate::Cmd_DrawLineF: { #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << " -> Cmd_DrawLineF, offset: " << cmd.offset << " size: " << cmd.size; #endif painter->drawLines((QLineF *)(d->floats.constData() + cmd.offset), cmd.size); break; } case QPaintBufferPrivate::Cmd_DrawLineI: { #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << " -> Cmd_DrawLineI, offset: " << cmd.offset << " size: " << cmd.size; #endif painter->drawLines((QLine *)(d->ints.constData() + cmd.offset), cmd.size); break; } case QPaintBufferPrivate::Cmd_DrawPointsF: { #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << " -> Cmd_DrawPointsF, offset: " << cmd.offset << " size: " << cmd.size; #endif painter->drawPoints((QPointF *)(d->floats.constData() + cmd.offset), cmd.size); break; } case QPaintBufferPrivate::Cmd_DrawPointsI: { #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << " -> Cmd_DrawPointsI, offset: " << cmd.offset << " size: " << cmd.size; #endif painter->drawPoints((QPoint *)(d->ints.constData() + cmd.offset), cmd.size); break; } case QPaintBufferPrivate::Cmd_DrawPixmapRect: { QPixmap pm(d->variants.at(cmd.offset).value()); QRectF r(d->floats.at(cmd.extra), d->floats.at(cmd.extra+1), d->floats.at(cmd.extra+2), d->floats.at(cmd.extra+3)); QRectF sr(d->floats.at(cmd.extra+4), d->floats.at(cmd.extra+5), d->floats.at(cmd.extra+6), d->floats.at(cmd.extra+7)); #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << " -> Cmd_DrawPixmapRect:" << r << sr; #endif painter->drawPixmap(r, pm, sr); break; } case QPaintBufferPrivate::Cmd_DrawPixmapPos: { QPixmap pm(d->variants.at(cmd.offset).value()); QPointF pos(d->floats.at(cmd.extra), d->floats.at(cmd.extra+1)); #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << " -> Cmd_DrawPixmapPos:" << pos; #endif painter->drawPixmap(pos, pm); break; } case QPaintBufferPrivate::Cmd_DrawTiledPixmap: { QPixmap pm(d->variants.at(cmd.offset).value()); QRectF r(d->floats.at(cmd.extra), d->floats.at(cmd.extra+1), d->floats.at(cmd.extra+2), d->floats.at(cmd.extra+3)); QPointF offset(d->floats.at(cmd.extra+4), d->floats.at(cmd.extra+5)); #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << " -> Cmd_DrawTiledPixmap:" << r << offset; #endif painter->drawTiledPixmap(r, pm, offset); break; } case QPaintBufferPrivate::Cmd_DrawImageRect: { QImage image(d->variants.at(cmd.offset).value()); QRectF r(d->floats.at(cmd.extra), d->floats.at(cmd.extra+1), d->floats.at(cmd.extra+2), d->floats.at(cmd.extra+3)); QRectF sr(d->floats.at(cmd.extra+4), d->floats.at(cmd.extra+5), d->floats.at(cmd.extra+6), d->floats.at(cmd.extra+7)); #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << " -> Cmd_DrawImageRect:" << r << sr; #endif painter->drawImage(r, image, sr); break; } case QPaintBufferPrivate::Cmd_DrawImagePos: { QImage image(d->variants.at(cmd.offset).value()); QPointF pos(d->floats.at(cmd.extra), d->floats.at(cmd.extra+1)); #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << " -> Cmd_DrawImagePos:" << pos; #endif painter->drawImage(pos, image); break; } case QPaintBufferPrivate::Cmd_DrawRectF: { #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << " -> Cmd_DrawRectF, offset: " << cmd.offset; #endif painter->drawRects((QRectF *)(d->floats.constData() + cmd.offset), cmd.size); break; } case QPaintBufferPrivate::Cmd_DrawRectI: { #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << " -> Cmd_DrawRectI, offset: " << cmd.offset; #endif painter->drawRects((QRect *)(d->ints.constData() + cmd.offset), cmd.size); break; } case QPaintBufferPrivate::Cmd_FillRectBrush: { QBrush brush = qvariant_cast(d->variants.at(cmd.extra)); QRectF *rect = (QRectF *)(d->floats.constData() + cmd.offset); #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << " -> Cmd_FillRectBrush, offset: " << cmd.offset << " rect: " << *rect << " brush: " << brush; #endif painter->fillRect(*rect, brush); break; } case QPaintBufferPrivate::Cmd_FillRectColor: { QColor color = qvariant_cast(d->variants.at(cmd.extra)); QRectF *rect = (QRectF *)(d->floats.constData() + cmd.offset); #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << " -> Cmd_FillRectBrush, offset: " << cmd.offset << " rect: " << *rect << " color: " << color; #endif painter->fillRect(*rect, color); break; } case QPaintBufferPrivate::Cmd_SetClipEnabled: { bool clipEnabled = d->variants.at(cmd.offset).toBool(); #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << " -> Cmd_SetClipEnabled:" << clipEnabled; #endif painter->setClipping(clipEnabled); break; } case QPaintBufferPrivate::Cmd_ClipVectorPath: { QVectorPathCmd path(d, cmd); #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << " -> Cmd_ClipVectorPath:" << path().elementCount(); #endif painter->setClipPath(path().convertToPainterPath(), Qt::ClipOperation(cmd.extra)); break; } case QPaintBufferPrivate::Cmd_ClipRect: { QRect rect(QPoint(d->ints.at(cmd.offset), d->ints.at(cmd.offset + 1)), QPoint(d->ints.at(cmd.offset + 2), d->ints.at(cmd.offset + 3))); #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << " -> Cmd_ClipRect:" << rect << cmd.extra; #endif painter->setClipRect(rect, Qt::ClipOperation(cmd.extra)); break; } case QPaintBufferPrivate::Cmd_ClipRegion: { QRegion region(d->variants.at(cmd.offset).value()); #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << " -> Cmd_ClipRegion:" << region.boundingRect() << cmd.extra; #endif painter->setClipRegion(region, Qt::ClipOperation(cmd.extra)); break; } #if !defined(QT_NO_RAWFONT) case QPaintBufferPrivate::Cmd_DrawStaticText: { QVariantList variants(d->variants.at(cmd.offset).value()); QFont font = variants.at(0).value(); QVector glyphIndexes; QVector positions; for (int i=0; i<(variants.size() - 1) / 2; ++i) { glyphIndexes.append(variants.at(i*2 + 1).toUInt()); positions.append(variants.at(i*2 + 2).toPointF()); } painter->setFont(font); QGlyphRun glyphs; glyphs.setRawFont(QRawFont::fromFont(font, QFontDatabase::Any)); glyphs.setGlyphIndexes(glyphIndexes); glyphs.setPositions(positions); painter->drawGlyphRun(QPointF(), glyphs); break; } #endif case QPaintBufferPrivate::Cmd_DrawText: { QPointF pos(d->floats.at(cmd.extra), d->floats.at(cmd.extra+1)); QList variants(d->variants.at(cmd.offset).value >()); QFont font(variants.at(0).value()); QString text(variants.at(1).value()); painter->setFont(font); painter->drawText(pos, text); break; } case QPaintBufferPrivate::Cmd_DrawTextItem: { QPointF pos(d->floats.at(cmd.extra), d->floats.at(cmd.extra+1)); QTextItemIntCopy *tiCopy = reinterpret_cast(qvariant_cast(d->variants.at(cmd.offset))); QTextItemInt &ti = (*tiCopy)(); QString text(ti.text()); QFont font(ti.font()); font.setUnderline(false); font.setStrikeOut(false); font.setOverline(false); const QTextItemInt &si = static_cast(ti); qreal justificationWidth = 0; if (si.justified) justificationWidth = si.width.toReal(); qreal scaleFactor = font.d->dpi/qreal(qt_defaultDpiY()); #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << " -> Cmd_DrawTextItem:" << pos << " " << text << " " << scaleFactor; #endif if (scaleFactor != 1.0) { QFont fnt(font); QFakeDevice fake; fake.setDpiX(qRound(scaleFactor*qt_defaultDpiX())); fake.setDpiY(qRound(scaleFactor*qt_defaultDpiY())); font = QFont(fnt, &fake); } int flags = Qt::TextSingleLine | Qt::TextDontClip | Qt::TextForceLeftToRight; QSizeF size(1, 1); if (justificationWidth > 0) { size.setWidth(justificationWidth); flags |= Qt::TextJustificationForced; flags |= Qt::AlignJustify; } QFontMetrics fm(font); QPointF pt(pos.x(), pos.y() - fm.ascent()); // ### this does not work outside of Qt code, so we need to emulate it with the following QPainter calls // qt_format_text(font, QRectF(pt, size), flags, /*opt*/0, // text, /*brect=*/0, /*tabstops=*/0, /*...*/0, /*tabarraylen=*/0, painter); painter->save(); painter->drawText(QRectF(pt, size), flags, text); painter->restore(); break; } case QPaintBufferPrivate::Cmd_SystemStateChanged: { QRegion systemClip(d->variants.at(cmd.offset).value()); #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << " -> Cmd_SystemStateChanged:" << systemClip; #endif painter->paintEngine()->setSystemClip(systemClip); painter->paintEngine()->d_ptr->systemStateChanged(); break; } } } void QPaintEngineExReplayer::process(const QPaintBufferCommand &cmd) { Q_ASSERT(painter->paintEngine()->isExtended()); QPaintEngineEx *xengine = static_cast(painter->paintEngine()); switch (cmd.id) { case QPaintBufferPrivate::Cmd_SetBrushOrigin: { #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << " -> ExCmd_SetBrushOrigin: " << d->variants.at(cmd.offset).toPointF(); #endif xengine->state()->brushOrigin = d->variants.at(cmd.offset).toPointF(); xengine->brushOriginChanged(); break; } case QPaintBufferPrivate::Cmd_SetCompositionMode: { QPainter::CompositionMode mode = (QPainter::CompositionMode) cmd.extra; #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << " -> ExCmd_SetCompositionMode, mode: " << mode; #endif xengine->state()->composition_mode = mode; xengine->compositionModeChanged(); break; } case QPaintBufferPrivate::Cmd_SetOpacity: { #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << " -> ExCmd_SetOpacity: " << d->variants.at(cmd.offset).toDouble(); #endif xengine->state()->opacity = d->variants.at(cmd.offset).toDouble(); xengine->opacityChanged(); break; } case QPaintBufferPrivate::Cmd_DrawVectorPath: { #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << " -> ExCmd_DrawVectorPath: size: " << cmd.size // << ", hints:" << d->ints[cmd.offset2+cmd.size] << "pts/elms:" << cmd.offset << cmd.offset2; #endif QVectorPathCmd path(d, cmd); xengine->draw(path()); break; } case QPaintBufferPrivate::Cmd_StrokeVectorPath: { QPen pen = qvariant_cast(d->variants.at(cmd.extra)); #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << " -> ExCmd_StrokeVectorPath: size: " << cmd.size // << ", hints:" << d->ints[cmd.offset2+cmd.size] << "pts/elms:" << cmd.offset << cmd.offset2; #endif QVectorPathCmd path(d, cmd); xengine->stroke(path(), pen); break; } case QPaintBufferPrivate::Cmd_FillVectorPath: { QBrush brush = qvariant_cast(d->variants.at(cmd.extra)); #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << " -> ExCmd_FillVectorPath: size: " << cmd.size // << ", hints:" << d->ints[cmd.offset2+cmd.size] << "pts/elms:" << cmd.offset << cmd.offset2 << brush; #endif QVectorPathCmd path(d, cmd); xengine->fill(path(), brush); break; } case QPaintBufferPrivate::Cmd_FillRectBrush: { QBrush brush = qvariant_cast(d->variants.at(cmd.extra)); QRectF *rect = (QRectF *)(d->floats.constData() + cmd.offset); #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << " -> ExCmd_FillRectBrush, offset: " << cmd.offset << " rect: " << *rect << " brush: " << brush; #endif xengine->fillRect(*rect, brush); break; } case QPaintBufferPrivate::Cmd_FillRectColor: { QColor color = qvariant_cast(d->variants.at(cmd.extra)); QRectF *rect = (QRectF *)(d->floats.constData() + cmd.offset); #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << " -> ExCmd_FillRectBrush, offset: " << cmd.offset << " rect: " << *rect << " color: " << color; #endif xengine->fillRect(*rect, color); break; } case QPaintBufferPrivate::Cmd_DrawPolygonF: { #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << " -> ExCmd_DrawPolygonF, offset: " << cmd.offset << " size: " << cmd.size << " mode: " << cmd.extra << d->floats.at(cmd.offset) << d->floats.at(cmd.offset+1); #endif xengine->drawPolygon((QPointF *) (d->floats.constData() + cmd.offset), cmd.size, (QPaintEngine::PolygonDrawMode) cmd.extra); break; } case QPaintBufferPrivate::Cmd_DrawPolygonI: { #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << " -> ExCmd_DrawPolygonI, offset: " << cmd.offset << " size: " << cmd.size << " mode: " << cmd.extra << d->ints.at(cmd.offset) << d->ints.at(cmd.offset+1); #endif xengine->drawPolygon((QPoint *) (d->ints.constData() + cmd.offset), cmd.size, (QPaintEngine::PolygonDrawMode) cmd.extra); break; } case QPaintBufferPrivate::Cmd_DrawEllipseF: { #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << " -> ExCmd_DrawEllipseF, offset: " << cmd.offset; #endif xengine->drawEllipse(*(QRectF *)(d->floats.constData() + cmd.offset)); break; } case QPaintBufferPrivate::Cmd_DrawEllipseI: { #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << " -> ExCmd_DrawEllipseI, offset: " << cmd.offset; #endif xengine->drawEllipse(*(QRect *)(d->ints.constData() + cmd.offset)); break; } case QPaintBufferPrivate::Cmd_DrawLineF: { #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << " -> ExCmd_DrawLineF, offset: " << cmd.offset << " size: " << cmd.size; #endif xengine->drawLines((QLineF *)(d->floats.constData() + cmd.offset), cmd.size); break; } case QPaintBufferPrivate::Cmd_DrawLineI: { #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << " -> ExCmd_DrawLineI, offset: " << cmd.offset << " size: " << cmd.size; #endif xengine->drawLines((QLine *)(d->ints.constData() + cmd.offset), cmd.size); break; } case QPaintBufferPrivate::Cmd_DrawPointsF: { #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << " -> ExCmd_DrawPointsF, offset: " << cmd.offset << " size: " << cmd.size; #endif xengine->drawPoints((QPointF *)(d->floats.constData() + cmd.offset), cmd.size); break; } case QPaintBufferPrivate::Cmd_DrawPointsI: { #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << " -> ExCmd_DrawPointsI, offset: " << cmd.offset << " size: " << cmd.size; #endif xengine->drawPoints((QPoint *)(d->ints.constData() + cmd.offset), cmd.size); break; } case QPaintBufferPrivate::Cmd_DrawPolylineF: { #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << " -> ExCmd_DrawPolylineF, offset: " << cmd.offset << " size: " << cmd.size; #endif xengine->drawPolygon((QPointF *) (d->floats.constData() + cmd.offset), cmd.size, QPaintEngine::PolylineMode); break; } case QPaintBufferPrivate::Cmd_DrawPolylineI: { #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << " -> ExCmd_DrawPolylineI, offset: " << cmd.offset << " size: " << cmd.size; #endif xengine->drawPolygon((QPoint *) (d->ints.constData() + cmd.offset), cmd.size, QPaintEngine::PolylineMode); break; } case QPaintBufferPrivate::Cmd_DrawRectF: { #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << " -> ExCmd_DrawRectF, offset: " << cmd.offset << " size: " << cmd.size; #endif xengine->drawRects((QRectF *) (d->floats.constData() + cmd.offset), cmd.size); break; } case QPaintBufferPrivate::Cmd_DrawRectI: { #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << " -> ExCmd_DrawRectI, offset: " << cmd.offset << " size: " << cmd.size; #endif xengine->drawRects((QRect *) (d->ints.constData() + cmd.offset), cmd.size); break; } case QPaintBufferPrivate::Cmd_SetClipEnabled: { bool clipEnabled = d->variants.at(cmd.offset).toBool(); #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << " -> ExCmd_SetClipEnabled:" << clipEnabled; #endif xengine->state()->clipEnabled = clipEnabled; xengine->clipEnabledChanged(); break; } case QPaintBufferPrivate::Cmd_ClipVectorPath: { QVectorPathCmd path(d, cmd); #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << " -> ExCmd_ClipVectorPath:" << path().elementCount(); #endif xengine->clip(path(), Qt::ClipOperation(cmd.extra)); break; } case QPaintBufferPrivate::Cmd_ClipRect: { QRect rect(QPoint(d->ints.at(cmd.offset), d->ints.at(cmd.offset + 1)), QPoint(d->ints.at(cmd.offset + 2), d->ints.at(cmd.offset + 3))); #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << " -> ExCmd_ClipRect:" << rect << cmd.extra; #endif xengine->clip(rect, Qt::ClipOperation(cmd.extra)); break; } case QPaintBufferPrivate::Cmd_ClipRegion: { QRegion region(d->variants.at(cmd.offset).value()); #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << " -> ExCmd_ClipRegion:" << region.boundingRect() << cmd.extra; #endif xengine->clip(region, Qt::ClipOperation(cmd.extra)); break; } default: QPainterReplayer::process(cmd); break; } } QDataStream &operator<<(QDataStream &stream, const QPaintBufferCommand &command) { quint32 id = command.id; quint32 size = command.size; stream << id << size; stream << command.offset << command.offset2 << command.extra; return stream; } QDataStream &operator>>(QDataStream &stream, QPaintBufferCommand &command) { quint32 id; quint32 size; stream >> id >> size; stream >> command.offset >> command.offset2 >> command.extra; command.id = id; command.size = size; return stream; } struct QPaintBufferCacheEntry { QVariant::Type type; quint64 cacheKey; }; struct QPaintBufferCacheEntryV2 { enum Type { ImageKey, PixmapKey }; struct Flags { uint type : 8; uint key : 24; }; union { Flags flags; uint bits; }; }; QT_END_NAMESPACE Q_DECLARE_METATYPE(QPaintBufferCacheEntry) Q_DECLARE_METATYPE(QPaintBufferCacheEntryV2) QT_BEGIN_NAMESPACE QDataStream &operator<<(QDataStream &stream, const QPaintBufferCacheEntry &entry) { return stream << entry.type << entry.cacheKey; } QDataStream &operator>>(QDataStream &stream, QPaintBufferCacheEntry &entry) { return stream >> entry.type >> entry.cacheKey; } QDataStream &operator<<(QDataStream &stream, const QPaintBufferCacheEntryV2 &entry) { return stream << entry.bits; } QDataStream &operator>>(QDataStream &stream, QPaintBufferCacheEntryV2 &entry) { return stream >> entry.bits; } static void qRegisterPaintBufferMetaTypes() { qRegisterMetaTypeStreamOperators(); qRegisterMetaTypeStreamOperators(); } Q_CONSTRUCTOR_FUNCTION(qRegisterPaintBufferMetaTypes) QDataStream &operator<<(QDataStream &stream, const QPaintBuffer &buffer) { QHash pixmapKeys; QHash imageKeys; QHash pixmaps; QHash images; QVector variants = buffer.d_ptr->variants; for (int i = 0; i < variants.size(); ++i) { const QVariant &v = variants.at(i); if (v.type() == QVariant::Image) { const QImage image(v.value()); QPaintBufferCacheEntryV2 entry; entry.flags.type = QPaintBufferCacheEntryV2::ImageKey; QHash::iterator it = imageKeys.find(image.cacheKey()); if (it != imageKeys.end()) { entry.flags.key = *it; } else { imageKeys[image.cacheKey()] = entry.flags.key = images.size(); images[images.size()] = image; } variants[i] = QVariant::fromValue(entry); } else if (v.type() == QVariant::Pixmap) { const QPixmap pixmap(v.value()); QPaintBufferCacheEntryV2 entry; entry.flags.type = QPaintBufferCacheEntryV2::PixmapKey; QHash::iterator it = pixmapKeys.find(pixmap.cacheKey()); if (it != pixmapKeys.end()) { entry.flags.key = *it; } else { pixmapKeys[pixmap.cacheKey()] = entry.flags.key = pixmaps.size(); pixmaps[pixmaps.size()] = pixmap; } variants[i] = QVariant::fromValue(entry); } } stream << pixmaps; stream << images; stream << buffer.d_ptr->ints; stream << buffer.d_ptr->floats; stream << variants; stream << buffer.d_ptr->commands; stream << buffer.d_ptr->boundingRect; stream << buffer.d_ptr->frames; return stream; } QDataStream &operator>>(QDataStream &stream, QPaintBuffer &buffer) { QHash pixmaps; QHash images; stream >> pixmaps; stream >> images; stream >> buffer.d_ptr->ints; stream >> buffer.d_ptr->floats; stream >> buffer.d_ptr->variants; stream >> buffer.d_ptr->commands; stream >> buffer.d_ptr->boundingRect; stream >> buffer.d_ptr->frames; QVector &variants = buffer.d_ptr->variants; for (int i = 0; i < variants.size(); ++i) { const QVariant &v = variants.at(i); if (v.canConvert()) { QPaintBufferCacheEntry entry = v.value(); if (entry.type == QVariant::Image) variants[i] = QVariant(images.value(entry.cacheKey)); else variants[i] = QVariant(pixmaps.value(entry.cacheKey)); } else if (v.canConvert()) { QPaintBufferCacheEntryV2 entry = v.value(); if (entry.flags.type == QPaintBufferCacheEntryV2::ImageKey) variants[i] = QVariant(images.value(entry.flags.key)); else if (entry.flags.type == QPaintBufferCacheEntryV2::PixmapKey) variants[i] = QVariant(pixmaps.value(entry.flags.key)); else qWarning() << "operator<<(QDataStream &stream, QPaintBuffer &buffer): unrecognized cache entry type:" << entry.flags.type; } } return stream; } QT_END_NAMESPACE gammaray-2.11.3/3rdparty/qt/5.5/private/qpaintbuffer_p.h000066400000000000000000000302341413310443000227220ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtGui module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL21$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 or version 3 as published by the Free ** Software Foundation and appearing in the file LICENSE.LGPLv21 and ** LICENSE.LGPLv3 included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QPAINTBUFFER_P_H #define QPAINTBUFFER_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include #include #include QT_BEGIN_NAMESPACE class QPaintBufferPrivate; class QPaintBufferPlayback; class QPaintBuffer : public QPaintDevice { Q_DECLARE_PRIVATE(QPaintBuffer) public: QPaintBuffer(); QPaintBuffer(const QPaintBuffer &other); ~QPaintBuffer(); bool isEmpty() const; void beginNewFrame(); int numFrames() const; void draw(QPainter *painter, int frame = 0) const; int frameStartIndex(int frame) const; int frameEndIndex(int frame) const; int processCommands(QPainter *painter, int begin, int end) const; #ifndef QT_NO_DEBUG_STREAM QString commandDescription(int command) const; #endif void setBoundingRect(const QRectF &rect); QRectF boundingRect() const; virtual QPaintEngine *paintEngine() const; virtual int metric(PaintDeviceMetric m) const; virtual int devType() const; QPaintBuffer &operator=(const QPaintBuffer &other); private: friend class QPainterReplayer; friend class QOpenGLReplayer; friend QDataStream &operator<<(QDataStream &stream, const QPaintBuffer &buffer); friend QDataStream &operator>>(QDataStream &stream, QPaintBuffer &buffer); QPaintBufferPrivate *d_ptr; }; QDataStream &operator<<(QDataStream &stream, const QPaintBuffer &buffer); QDataStream &operator>>(QDataStream &stream, QPaintBuffer &buffer); class QPaintBufferEngine; class QTextItemIntCopy { public: QTextItemIntCopy(const QTextItem &item); ~QTextItemIntCopy(); QTextItemInt &operator () () {return m_item;} private: QTextItemInt m_item; QFont m_font; }; struct QPaintBufferCommand { uint id : 8; uint size : 24; int offset; int offset2; int extra; }; QDataStream &operator<<(QDataStream &stream, const QPaintBufferCommand &command); QDataStream &operator>>(QDataStream &stream, QPaintBufferCommand &command); Q_DECLARE_TYPEINFO(QPaintBufferCommand, Q_MOVABLE_TYPE); class QPaintBufferPrivate { public: enum Command { Cmd_Save, Cmd_Restore, Cmd_SetBrush, Cmd_SetBrushOrigin, Cmd_SetClipEnabled, Cmd_SetCompositionMode, Cmd_SetOpacity, Cmd_SetPen, Cmd_SetRenderHints, Cmd_SetTransform, Cmd_SetBackgroundMode, Cmd_ClipPath, Cmd_ClipRect, Cmd_ClipRegion, Cmd_ClipVectorPath, Cmd_DrawVectorPath, Cmd_FillVectorPath, Cmd_StrokeVectorPath, Cmd_DrawConvexPolygonF, Cmd_DrawConvexPolygonI, Cmd_DrawEllipseF, Cmd_DrawEllipseI, Cmd_DrawLineF, Cmd_DrawLineI, Cmd_DrawPath, Cmd_DrawPointsF, Cmd_DrawPointsI, Cmd_DrawPolygonF, Cmd_DrawPolygonI, Cmd_DrawPolylineF, Cmd_DrawPolylineI, Cmd_DrawRectF, Cmd_DrawRectI, Cmd_FillRectBrush, Cmd_FillRectColor, Cmd_DrawText, Cmd_DrawTextItem, Cmd_DrawImagePos, Cmd_DrawImageRect, Cmd_DrawPixmapPos, Cmd_DrawPixmapRect, Cmd_DrawTiledPixmap, Cmd_SystemStateChanged, Cmd_Translate, Cmd_DrawStaticText, // new commands must be added above this line Cmd_LastCommand }; QPaintBufferPrivate(); ~QPaintBufferPrivate(); int addData(const int *data, int count) { if (count <= 0) return 0; int pos = ints.size(); ints.resize(pos + count); memcpy(ints.data() + pos, data, count * sizeof(int)); return pos; } int addData(const qreal *data, int count) { if (count <= 0) return 0; int pos = floats.size(); floats.resize(pos + count); memcpy(floats.data() + pos, data, count * sizeof(qreal)); return pos; } int addData(const QVariant &var) { variants << var; return variants.size() - 1; } QPaintBufferCommand *addCommand(Command command) { QPaintBufferCommand cmd; cmd.id = command; cmd.size = cmd.offset = cmd.offset2 = cmd.extra = 0; commands << cmd; return &commands.last(); } QPaintBufferCommand *addCommand(Command command, const QVariant &var) { QPaintBufferCommand cmd; cmd.id = command; cmd.offset = addData(var); cmd.size = cmd.offset2 = cmd.extra = 0; commands << cmd; return &commands.last(); } QPaintBufferCommand *addCommand(Command command, const QVectorPath &path) { QPaintBufferCommand cmd; cmd.id = command; cmd.offset = addData(path.points(), path.elementCount() * 2); cmd.offset2 = ints.size(); ints << path.hints(); // The absence of path elements is indicated by setting the highest bit in 'cmd.offset2'. if (path.elements()) addData((const int *) path.elements(), path.elementCount()); else cmd.offset2 |= 0x80000000; cmd.size = path.elementCount(); cmd.extra = 0; commands << cmd; return &commands.last(); } QPaintBufferCommand *addCommand(Command command , const qreal *pts, int arrayLength, int elementCount) { QPaintBufferCommand cmd; cmd.id = command; cmd.offset = addData(pts, arrayLength); cmd.size = elementCount; cmd.offset2 = cmd.extra = 0; commands << cmd; return &commands.last(); } QPaintBufferCommand *addCommand(Command command , const int *pts, int arrayLength, int elementCount) { QPaintBufferCommand cmd; cmd.id = command; cmd.offset = addData(pts, arrayLength); cmd.size = elementCount; cmd.offset2 = cmd.extra = 0; commands << cmd; return &commands.last(); } inline void updateBoundingRect(const QRectF &rect); QAtomicInt ref; QVector ints; QVector floats; QVector variants; QVector commands; QList frames; QPaintBufferEngine *engine; QRectF boundingRect; qreal penWidthAdjustment; uint calculateBoundingRect : 1; void *cache; }; struct QVectorPathCmd { // The absence of path elements is indicated by setting the highest bit in 'cmd.offset2'. QVectorPathCmd(QPaintBufferPrivate *d, const QPaintBufferCommand &cmd) : vectorPath(d->floats.constData() + cmd.offset, cmd.size, cmd.offset2 & 0x80000000 ? nullptr : (const QPainterPath::ElementType *) (d->ints.constData() + cmd.offset2 + 1), *(d->ints.constData() + (cmd.offset2 & 0x7fffffff))) {} inline const QVectorPath &operator()() const { return vectorPath; } QVectorPath vectorPath; }; class QPainterReplayer { public: QPainterReplayer() { } virtual ~QPainterReplayer() { } void setupTransform(QPainter *painter); virtual void process(const QPaintBufferCommand &cmd); void processCommands(const QPaintBuffer &buffer, QPainter *painter, int begin, int end); protected: QPaintBufferPrivate *d; QTransform m_world_matrix; QPainter *painter; private: Q_DISABLE_COPY(QPainterReplayer) }; class QPaintEngineExReplayer : public QPainterReplayer { public: QPaintEngineExReplayer() { } virtual void process(const QPaintBufferCommand &cmd); }; class QPaintBufferEnginePrivate; class QPaintBufferEngine : public QPaintEngineEx { Q_DECLARE_PRIVATE(QPaintBufferEngine) public: QPaintBufferEngine(QPaintBufferPrivate *buffer); virtual bool begin(QPaintDevice *device); virtual bool end(); virtual Type type() const { return QPaintEngine::PaintBuffer; } virtual QPainterState *createState(QPainterState *orig) const; virtual void draw(const QVectorPath &path); virtual void fill(const QVectorPath &path, const QBrush &brush); virtual void stroke(const QVectorPath &path, const QPen &pen); virtual void clip(const QVectorPath &path, Qt::ClipOperation op); virtual void clip(const QRect &rect, Qt::ClipOperation op); virtual void clip(const QRegion ®ion, Qt::ClipOperation op); virtual void clip(const QPainterPath &path, Qt::ClipOperation op); virtual void clipEnabledChanged(); virtual void penChanged(); virtual void brushChanged(); virtual void brushOriginChanged(); virtual void opacityChanged(); virtual void compositionModeChanged(); virtual void renderHintsChanged(); virtual void transformChanged(); virtual void backgroundModeChanged(); virtual void fillRect(const QRectF &rect, const QBrush &brush); virtual void fillRect(const QRectF &rect, const QColor &color); virtual void drawRects(const QRect *rects, int rectCount); virtual void drawRects(const QRectF *rects, int rectCount); virtual void drawLines(const QLine *lines, int lineCount); virtual void drawLines(const QLineF *lines, int lineCount); virtual void drawEllipse(const QRectF &r); virtual void drawEllipse(const QRect &r); virtual void drawPath(const QPainterPath &path); virtual void drawPoints(const QPointF *points, int pointCount); virtual void drawPoints(const QPoint *points, int pointCount); virtual void drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode); virtual void drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode); virtual void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr); virtual void drawPixmap(const QPointF &pos, const QPixmap &pm); virtual void drawImage(const QRectF &r, const QImage &pm, const QRectF &sr, Qt::ImageConversionFlags flags = Qt::AutoColor); virtual void drawImage(const QPointF &pos, const QImage &image); virtual void drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &s); virtual void drawTextItem(const QPointF &pos, const QTextItem &ti); virtual void drawStaticTextItem(QStaticTextItem *staticTextItem); virtual void setState(QPainterState *s); virtual uint flags() const {return QPaintEngineEx::DoNotEmulate;} QPaintBufferPrivate *buffer; mutable uint m_begin_detected : 1; mutable uint m_save_detected : 1; mutable uint m_stream_raw_text_items : 1; mutable uint m_unused : 29; mutable QPainterState *m_created_state; }; QT_END_NAMESPACE #endif // QPAINTBUFFER_P_H gammaray-2.11.3/3rdparty/qt/modeltest.cpp000066400000000000000000000527561413310443000203040ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "modeltest.h" #include #include // NOTE: Patch to make this file compilable with older Qt versions #if QT_VERSION < QT_VERSION_CHECK(5, 5, 0) Q_DECLARE_METATYPE(Qt::Alignment) #endif /*! Connect to all of the models signals. Whenever anything happens recheck everything. */ ModelTest::ModelTest ( QAbstractItemModel *_model, QObject *parent ) : QObject ( parent ), model ( _model ), fetchingMore ( false ) { if (!model) qFatal("%s: model must not be null", Q_FUNC_INFO); connect(model, SIGNAL(columnsAboutToBeInserted(QModelIndex,int,int)), this, SLOT(runAllTests()) ); connect(model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)), this, SLOT(runAllTests()) ); connect(model, SIGNAL(columnsInserted(QModelIndex,int,int)), this, SLOT(runAllTests()) ); connect(model, SIGNAL(columnsRemoved(QModelIndex,int,int)), this, SLOT(runAllTests()) ); connect(model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(runAllTests()) ); connect(model, SIGNAL(headerDataChanged(Qt::Orientation,int,int)), this, SLOT(runAllTests()) ); connect(model, SIGNAL(layoutAboutToBeChanged()), this, SLOT(runAllTests()) ); connect(model, SIGNAL(layoutChanged()), this, SLOT(runAllTests()) ); connect(model, SIGNAL(modelReset()), this, SLOT(runAllTests()) ); connect(model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)), this, SLOT(runAllTests()) ); connect(model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), this, SLOT(runAllTests()) ); connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(runAllTests()) ); connect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SLOT(runAllTests()) ); // Special checks for changes connect(model, SIGNAL(layoutAboutToBeChanged()), this, SLOT(layoutAboutToBeChanged()) ); connect(model, SIGNAL(layoutChanged()), this, SLOT(layoutChanged()) ); connect(model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)), this, SLOT(rowsAboutToBeInserted(QModelIndex,int,int)) ); connect(model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), this, SLOT(rowsAboutToBeRemoved(QModelIndex,int,int)) ); connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(rowsInserted(QModelIndex,int,int)) ); connect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SLOT(rowsRemoved(QModelIndex,int,int)) ); connect(model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(dataChanged(QModelIndex,QModelIndex)) ); connect(model, SIGNAL(headerDataChanged(Qt::Orientation,int,int)), this, SLOT(headerDataChanged(Qt::Orientation,int,int)) ); runAllTests(); } void ModelTest::runAllTests() { if ( fetchingMore ) return; nonDestructiveBasicTest(); rowCount(); columnCount(); hasIndex(); index(); parent(); data(); } /*! nonDestructiveBasicTest tries to call a number of the basic functions (not all) to make sure the model doesn't outright segfault, testing the functions that makes sense. */ void ModelTest::nonDestructiveBasicTest() { QVERIFY(!model->buddy(QModelIndex()).isValid()); model->canFetchMore ( QModelIndex() ); QVERIFY( model->columnCount ( QModelIndex() ) >= 0 ); QCOMPARE(model->data(QModelIndex()), QVariant()); fetchingMore = true; model->fetchMore ( QModelIndex() ); fetchingMore = false; Qt::ItemFlags flags = model->flags ( QModelIndex() ); QVERIFY( flags == Qt::ItemIsDropEnabled || flags == 0 ); model->hasChildren ( QModelIndex() ); model->hasIndex ( 0, 0 ); model->headerData ( 0, Qt::Horizontal ); model->index ( 0, 0 ); model->itemData ( QModelIndex() ); QVariant cache; model->match ( QModelIndex(), -1, cache ); model->mimeTypes(); QVERIFY(!model->parent(QModelIndex()).isValid()); QVERIFY( model->rowCount() >= 0 ); QVariant variant; model->setData ( QModelIndex(), variant, -1 ); model->setHeaderData ( -1, Qt::Horizontal, QVariant() ); model->setHeaderData ( 999999, Qt::Horizontal, QVariant() ); QMap roles; model->sibling ( 0, 0, QModelIndex() ); model->span ( QModelIndex() ); model->supportedDropActions(); } /*! Tests model's implementation of QAbstractItemModel::rowCount() and hasChildren() Models that are dynamically populated are not as fully tested here. */ void ModelTest::rowCount() { // qDebug() << "rc"; // check top row QModelIndex topIndex = model->index ( 0, 0, QModelIndex() ); int rows = model->rowCount ( topIndex ); QVERIFY( rows >= 0 ); if ( rows > 0 ) QVERIFY( model->hasChildren ( topIndex ) ); QModelIndex secondLevelIndex = model->index ( 0, 0, topIndex ); if ( secondLevelIndex.isValid() ) { // not the top level // check a row count where parent is valid rows = model->rowCount ( secondLevelIndex ); QVERIFY( rows >= 0 ); if ( rows > 0 ) QVERIFY( model->hasChildren ( secondLevelIndex ) ); } // The models rowCount() is tested more extensively in checkChildren(), // but this catches the big mistakes } /*! Tests model's implementation of QAbstractItemModel::columnCount() and hasChildren() */ void ModelTest::columnCount() { // check top row QModelIndex topIndex = model->index ( 0, 0, QModelIndex() ); QVERIFY( model->columnCount ( topIndex ) >= 0 ); // check a column count where parent is valid QModelIndex childIndex = model->index ( 0, 0, topIndex ); if ( childIndex.isValid() ) QVERIFY( model->columnCount ( childIndex ) >= 0 ); // columnCount() is tested more extensively in checkChildren(), // but this catches the big mistakes } /*! Tests model's implementation of QAbstractItemModel::hasIndex() */ void ModelTest::hasIndex() { // qDebug() << "hi"; // Make sure that invalid values returns an invalid index QVERIFY( !model->hasIndex ( -2, -2 ) ); QVERIFY( !model->hasIndex ( -2, 0 ) ); QVERIFY( !model->hasIndex ( 0, -2 ) ); int rows = model->rowCount(); int columns = model->columnCount(); // check out of bounds QVERIFY( !model->hasIndex ( rows, columns ) ); QVERIFY( !model->hasIndex ( rows + 1, columns + 1 ) ); if ( rows > 0 ) QVERIFY( model->hasIndex ( 0, 0 ) ); // hasIndex() is tested more extensively in checkChildren(), // but this catches the big mistakes } /*! Tests model's implementation of QAbstractItemModel::index() */ void ModelTest::index() { // qDebug() << "i"; // Make sure that invalid values returns an invalid index QVERIFY(!model->index(-2, -2).isValid()); QVERIFY(!model->index(-2, 0).isValid()); QVERIFY(!model->index(0, -2).isValid()); int rows = model->rowCount(); int columns = model->columnCount(); if ( rows == 0 ) return; // Catch off by one errors QVERIFY(!model->index(rows, columns).isValid()); QVERIFY(model->index(0, 0).isValid()); // Make sure that the same index is *always* returned QModelIndex a = model->index ( 0, 0 ); QModelIndex b = model->index ( 0, 0 ); QCOMPARE(a, b); // index() is tested more extensively in checkChildren(), // but this catches the big mistakes } /*! Tests model's implementation of QAbstractItemModel::parent() */ void ModelTest::parent() { // qDebug() << "p"; // Make sure the model won't crash and will return an invalid QModelIndex // when asked for the parent of an invalid index. QVERIFY(!model->parent(QModelIndex()).isValid()); if ( model->rowCount() == 0 ) return; // Column 0 | Column 1 | // QModelIndex() | | // \- topIndex | topIndex1 | // \- childIndex | childIndex1 | // Common error test #1, make sure that a top level index has a parent // that is a invalid QModelIndex. QModelIndex topIndex = model->index ( 0, 0, QModelIndex() ); QVERIFY(!model->parent(topIndex).isValid()); // Common error test #2, make sure that a second level index has a parent // that is the first level index. if ( model->rowCount ( topIndex ) > 0 ) { QModelIndex childIndex = model->index ( 0, 0, topIndex ); QCOMPARE(model->parent(childIndex), topIndex); } // Common error test #3, the second column should NOT have the same children // as the first column in a row. // Usually the second column shouldn't have children. QModelIndex topIndex1 = model->index ( 0, 1, QModelIndex() ); if ( model->rowCount ( topIndex1 ) > 0 ) { QModelIndex childIndex = model->index ( 0, 0, topIndex ); QModelIndex childIndex1 = model->index ( 0, 0, topIndex1 ); QVERIFY( childIndex != childIndex1 ); } // Full test, walk n levels deep through the model making sure that all // parent's children correctly specify their parent. checkChildren ( QModelIndex() ); } /*! Called from the parent() test. A model that returns an index of parent X should also return X when asking for the parent of the index. This recursive function does pretty extensive testing on the whole model in an effort to catch edge cases. This function assumes that rowCount(), columnCount() and index() already work. If they have a bug it will point it out, but the above tests should have already found the basic bugs because it is easier to figure out the problem in those tests then this one. */ void ModelTest::checkChildren ( const QModelIndex &parent, int currentDepth ) { // First just try walking back up the tree. QModelIndex p = parent; while ( p.isValid() ) p = p.parent(); // For models that are dynamically populated if ( model->canFetchMore ( parent ) ) { fetchingMore = true; model->fetchMore ( parent ); fetchingMore = false; } int rows = model->rowCount ( parent ); int columns = model->columnCount ( parent ); if ( rows > 0 ) QVERIFY( model->hasChildren ( parent ) ); // Some further testing against rows(), columns(), and hasChildren() QVERIFY( rows >= 0 ); QVERIFY( columns >= 0 ); if ( rows > 0 ) QVERIFY( model->hasChildren ( parent ) ); //qDebug() << "parent:" << model->data(parent).toString() << "rows:" << rows // << "columns:" << columns << "parent column:" << parent.column(); const QModelIndex topLeftChild = model->index( 0, 0, parent ); QVERIFY( !model->hasIndex ( rows + 1, 0, parent ) ); for ( int r = 0; r < rows; ++r ) { if ( model->canFetchMore ( parent ) ) { fetchingMore = true; model->fetchMore ( parent ); fetchingMore = false; } QVERIFY( !model->hasIndex ( r, columns + 1, parent ) ); for ( int c = 0; c < columns; ++c ) { QVERIFY( model->hasIndex ( r, c, parent ) ); QModelIndex index = model->index ( r, c, parent ); // rowCount() and columnCount() said that it existed... QVERIFY(index.isValid()); // index() should always return the same index when called twice in a row QModelIndex modifiedIndex = model->index ( r, c, parent ); QCOMPARE(index, modifiedIndex); // Make sure we get the same index if we request it twice in a row QModelIndex a = model->index ( r, c, parent ); QModelIndex b = model->index ( r, c, parent ); QCOMPARE(a, b); { const QModelIndex sibling = model->sibling( r, c, topLeftChild ); QCOMPARE(index, sibling); } { const QModelIndex sibling = topLeftChild.sibling( r, c ); QCOMPARE(index, sibling); } // Some basic checking on the index that is returned QCOMPARE(index.model(), model); QCOMPARE( index.row(), r ); QCOMPARE( index.column(), c ); // While you can technically return a QVariant usually this is a sign // of a bug in data(). Disable if this really is ok in your model. // QVERIFY( model->data ( index, Qt::DisplayRole ).isValid() ); // If the next test fails here is some somewhat useful debug you play with. if (model->parent(index) != parent) { qDebug() << r << c << currentDepth << model->data(index).toString() << model->data(parent).toString(); qDebug() << index << parent << model->parent(index); // And a view that you can even use to show the model. // QTreeView view; // view.setModel(model); // view.show(); } // Check that we can get back our real parent. QCOMPARE( model->parent ( index ), parent ); // recursively go down the children if ( model->hasChildren ( index ) && currentDepth < 10 ) { //qDebug() << r << c << "has children" << model->rowCount(index); checkChildren ( index, ++currentDepth ); }/* else { if (currentDepth >= 10) qDebug() << "checked 10 deep"; };*/ // make sure that after testing the children that the index doesn't change. QModelIndex newerIndex = model->index ( r, c, parent ); QCOMPARE(index, newerIndex); } } } /*! Tests model's implementation of QAbstractItemModel::data() */ void ModelTest::data() { // Invalid index should return an invalid qvariant QVERIFY( !model->data ( QModelIndex() ).isValid() ); if ( model->rowCount() == 0 ) return; // A valid index should have a valid QVariant data QVERIFY( model->index ( 0, 0 ).isValid() ); // shouldn't be able to set data on an invalid index QVERIFY( !model->setData ( QModelIndex(), QLatin1String ( "foo" ), Qt::DisplayRole ) ); // General Purpose roles that should return a QString QVariant variant = model->data ( model->index ( 0, 0 ), Qt::ToolTipRole ); if ( variant.isValid() ) { QVERIFY( variant.canConvert() ); } variant = model->data ( model->index ( 0, 0 ), Qt::StatusTipRole ); if ( variant.isValid() ) { QVERIFY( variant.canConvert() ); } variant = model->data ( model->index ( 0, 0 ), Qt::WhatsThisRole ); if ( variant.isValid() ) { QVERIFY( variant.canConvert() ); } // General Purpose roles that should return a QSize variant = model->data ( model->index ( 0, 0 ), Qt::SizeHintRole ); if ( variant.isValid() ) { QVERIFY( variant.canConvert() ); } // General Purpose roles that should return a QFont QVariant fontVariant = model->data ( model->index ( 0, 0 ), Qt::FontRole ); if ( fontVariant.isValid() ) { QVERIFY( fontVariant.canConvert() ); } // Check that the alignment is one we know about QVariant textAlignmentVariant = model->data ( model->index ( 0, 0 ), Qt::TextAlignmentRole ); if ( textAlignmentVariant.isValid() ) { Qt::Alignment alignment = textAlignmentVariant.value(); QCOMPARE( alignment, ( alignment & ( Qt::AlignHorizontal_Mask | Qt::AlignVertical_Mask ) ) ); } // General Purpose roles that should return a QColor QVariant colorVariant = model->data ( model->index ( 0, 0 ), Qt::BackgroundColorRole ); if ( colorVariant.isValid() ) { QVERIFY( colorVariant.canConvert() ); } colorVariant = model->data ( model->index ( 0, 0 ), Qt::TextColorRole ); if ( colorVariant.isValid() ) { QVERIFY( colorVariant.canConvert() ); } // Check that the "check state" is one we know about. QVariant checkStateVariant = model->data ( model->index ( 0, 0 ), Qt::CheckStateRole ); if ( checkStateVariant.isValid() ) { int state = checkStateVariant.toInt(); QVERIFY( state == Qt::Unchecked || state == Qt::PartiallyChecked || state == Qt::Checked ); } } /*! Store what is about to be inserted to make sure it actually happens \sa rowsInserted() */ void ModelTest::rowsAboutToBeInserted ( const QModelIndex &parent, int start, int /* end */) { // Q_UNUSED(end); // qDebug() << "rowsAboutToBeInserted" << "start=" << start << "end=" << end << "parent=" << model->data ( parent ).toString() // << "current count of parent=" << model->rowCount ( parent ); // << "display of last=" << model->data( model->index(start-1, 0, parent) ); // qDebug() << model->index(start-1, 0, parent) << model->data( model->index(start-1, 0, parent) ); Changing c; c.parent = parent; c.oldSize = model->rowCount ( parent ); c.last = model->data ( model->index ( start - 1, 0, parent ) ); c.next = model->data ( model->index ( start, 0, parent ) ); insert.push ( c ); } /*! Confirm that what was said was going to happen actually did \sa rowsAboutToBeInserted() */ void ModelTest::rowsInserted ( const QModelIndex & parent, int start, int end ) { Changing c = insert.pop(); QCOMPARE(c.parent, parent); // qDebug() << "rowsInserted" << "start=" << start << "end=" << end << "oldsize=" << c.oldSize // << "parent=" << model->data ( parent ).toString() << "current rowcount of parent=" << model->rowCount ( parent ); // for (int ii=start; ii <= end; ii++) // { // qDebug() << "itemWasInserted:" << ii << model->data ( model->index ( ii, 0, parent )); // } // qDebug(); QCOMPARE(c.oldSize + (end - start + 1), model->rowCount(parent)); QCOMPARE(c.last, model->data(model->index(start - 1, 0, c.parent))); if (c.next != model->data(model->index(end + 1, 0, c.parent))) { qDebug() << start << end; for (int i=0; i < model->rowCount(); ++i) qDebug() << model->index(i, 0).data().toString(); qDebug() << c.next << model->data(model->index(end + 1, 0, c.parent)); } QCOMPARE(c.next, model->data(model->index(end + 1, 0, c.parent))); } void ModelTest::layoutAboutToBeChanged() { for ( int i = 0; i < qBound ( 0, model->rowCount(), 100 ); ++i ) changing.append ( QPersistentModelIndex ( model->index ( i, 0 ) ) ); } void ModelTest::layoutChanged() { for ( int i = 0; i < changing.count(); ++i ) { QPersistentModelIndex p = changing[i]; QCOMPARE(QModelIndex(p), model->index(p.row(), p.column(), p.parent())); } changing.clear(); } /*! Store what is about to be inserted to make sure it actually happens \sa rowsRemoved() */ void ModelTest::rowsAboutToBeRemoved ( const QModelIndex &parent, int start, int end ) { qDebug() << "ratbr" << parent << start << end; Changing c; c.parent = parent; c.oldSize = model->rowCount ( parent ); c.last = model->data ( model->index ( start - 1, 0, parent ) ); c.next = model->data ( model->index ( end + 1, 0, parent ) ); remove.push ( c ); } /*! Confirm that what was said was going to happen actually did \sa rowsAboutToBeRemoved() */ void ModelTest::rowsRemoved ( const QModelIndex & parent, int start, int end ) { qDebug() << "rr" << parent << start << end; Changing c = remove.pop(); QCOMPARE(c.parent, parent); QCOMPARE(c.oldSize - (end - start + 1), model->rowCount(parent)); QCOMPARE(c.last, model->data(model->index(start - 1, 0, c.parent))); QCOMPARE(c.next, model->data(model->index(start, 0, c.parent))); } void ModelTest::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) { QVERIFY(topLeft.isValid()); QVERIFY(bottomRight.isValid()); QModelIndex commonParent = bottomRight.parent(); QCOMPARE(topLeft.parent(), commonParent); QVERIFY(topLeft.row() <= bottomRight.row()); QVERIFY(topLeft.column() <= bottomRight.column()); int rowCount = model->rowCount(commonParent); int columnCount = model->columnCount(commonParent); QVERIFY(bottomRight.row() < rowCount); QVERIFY(bottomRight.column() < columnCount); } void ModelTest::headerDataChanged(Qt::Orientation orientation, int start, int end) { QVERIFY(start >= 0); QVERIFY(end >= 0); QVERIFY(start <= end); int itemCount = orientation == Qt::Vertical ? model->rowCount() : model->columnCount(); QVERIFY(start < itemCount); QVERIFY(end < itemCount); } gammaray-2.11.3/3rdparty/qt/modeltest.h000066400000000000000000000055141413310443000177370ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2015 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL21$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see http://www.qt.io/terms-conditions. For further ** information use the contact form at http://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 or version 3 as published by the Free ** Software Foundation and appearing in the file LICENSE.LGPLv21 and ** LICENSE.LGPLv3 included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** As a special exception, The Qt Company gives you certain additional ** rights. These rights are described in The Qt Company LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef MODELTEST_H #define MODELTEST_H #include #include #include class ModelTest : public QObject { Q_OBJECT public: ModelTest( QAbstractItemModel *model, QObject *parent = nullptr ); private Q_SLOTS: void nonDestructiveBasicTest(); void rowCount(); void columnCount(); void hasIndex(); void index(); void parent(); void data(); protected Q_SLOTS: void runAllTests(); void layoutAboutToBeChanged(); void layoutChanged(); void rowsAboutToBeInserted( const QModelIndex &parent, int start, int end ); void rowsInserted( const QModelIndex & parent, int start, int end ); void rowsAboutToBeRemoved( const QModelIndex &parent, int start, int end ); void rowsRemoved( const QModelIndex & parent, int start, int end ); void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight); void headerDataChanged(Qt::Orientation orientation, int start, int end); private: void checkChildren( const QModelIndex &parent, int currentDepth = 0 ); QAbstractItemModel *model; struct Changing { QModelIndex parent; int oldSize; QVariant last; QVariant next; }; QStack insert; QStack remove; bool fetchingMore; QList changing; }; #endif gammaray-2.11.3/3rdparty/qt/qt_attribution.json000066400000000000000000000006531413310443000215300ustar00rootroot00000000000000{ "Id": "qt", "Name": "Qt", "QDocModule": "gammaray-manual", "QtUsage": "Several bits of Qt and Qt Creator code are copied into GammaRay: ModelTest, ResourceModel, Qt Designer type icons, QPaintBuffer.", "Description": "The Qt toolkit.", "Homepage": "https://qt.io/", "Version": "4.8 - 5.10", "License": "GPLv3, LGPLv3, commercial", "Copyright": "Copyright (C) 2015 The Qt Company Ltd." } gammaray-2.11.3/3rdparty/qt/resourcemodel.cpp000066400000000000000000001157451413310443000211520ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). ** All rights reserved. ** Contact: Nokia Corporation (qt-info@nokia.com) ** ** This file is part of the QtGui module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** No Commercial Usage ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions ** contained in the Technology Preview License Agreement accompanying ** this package. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional ** rights. These rights are described in the Nokia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** If you have questions regarding the use of this file, please contact ** Nokia at qt-info@nokia.com. ** ** ** ** ** ** ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "resourcemodel.h" #include #include #include #include #include #include #include #include #include #include #include /*! \enum ResourceModel::Roles \value FilePathRole \value FileNameRole */ using namespace GammaRay; class GammaRay::ResourceModelPrivate { Q_DECLARE_PUBLIC(ResourceModel) ResourceModel * const q_ptr; public: struct QDirNode { QDirNode() : parent(nullptr), populated(false), stat(false) {} ~QDirNode() { children.clear(); } QDirNode *parent; QFileInfo info; mutable QVector children; mutable bool populated; // have we read the children mutable bool stat; }; ResourceModelPrivate(ResourceModel *qq) : q_ptr(qq), resolveSymlinks(true), readOnly(true), lazyChildCount(false), allowAppendChild(true), shouldStat(true) // ### This is set to false by QFileDialog { } bool indexValid(const QModelIndex &index) const { return index.isValid(); } void init(); QDirNode *node(int row, QDirNode *parent) const; QVector children(QDirNode *parent, bool stat) const; void _q_refresh(); void savePersistentIndexes(); void restorePersistentIndexes(); QFileInfoList entryInfoList(const QString &path) const; QStringList entryList(const QString &path) const; QString name(const QModelIndex &index) const; QString size(const QModelIndex &index) const; QString type(const QModelIndex &index) const; QString time(const QModelIndex &index) const; void appendChild(ResourceModelPrivate::QDirNode *parent, const QString &path) const; static QFileInfo resolvedInfo(QFileInfo info); inline QDirNode *node(const QModelIndex &index) const; inline void populate(QDirNode *parent) const; inline void clear(QDirNode *parent) const; void invalidate(); mutable QDirNode root; bool resolveSymlinks; bool readOnly; bool lazyChildCount; bool allowAppendChild; QDir::Filters filters; QDir::SortFlags sort; QStringList nameFilters; struct SavedPersistent { QString path; int column; QPersistentModelIndexData *data; QPersistentModelIndex index; }; QList savedPersistent; QPersistentModelIndex toBeRefreshed; bool shouldStat; // use the "carefull not to stat directories" mode }; void qt_setDirModelShouldNotStat(ResourceModelPrivate *modelPrivate) { modelPrivate->shouldStat = false; } ResourceModelPrivate::QDirNode *ResourceModelPrivate::node(const QModelIndex &index) const { ResourceModelPrivate::QDirNode *n = static_cast(index.internalPointer()); Q_ASSERT(n); return n; } void ResourceModelPrivate::populate(QDirNode *parent) const { Q_ASSERT(parent); parent->children = children(parent, parent->stat); parent->populated = true; } void ResourceModelPrivate::clear(QDirNode *parent) const { Q_ASSERT(parent); parent->children.clear(); parent->populated = false; } void ResourceModelPrivate::invalidate() { QStack nodes; nodes.push(&root); while (!nodes.empty()) { const QDirNode *current = nodes.pop(); current->stat = false; const QVector children = current->children; for (int i = 0; i < children.count(); ++i) nodes.push(&children.at(i)); } } /*! \class ResourceModel \obsolete \brief The ResourceModel class provides a data model for the local filesystem. \ingroup model-view The usage of ResourceModel is not recommended anymore. The QFileSystemModel class is a more performant alternative. This class provides access to the local filesystem, providing functions for renaming and removing files and directories, and for creating new directories. In the simplest case, it can be used with a suitable display widget as part of a browser or filer. ResourceModel keeps a cache with file information. The cache needs to be updated with refresh(). ResourceModel can be accessed using the standard interface provided by QAbstractItemModel, but it also provides some convenience functions that are specific to a directory model. The fileInfo() and isDir() functions provide information about the underlying files and directories related to items in the model. Directories can be created and removed using mkdir(), rmdir(), and the model will be automatically updated to take the changes into account. \note ResourceModel requires an instance of a GUI application. \sa nameFilters(), setFilter(), filter(), QListView, QTreeView, QFileSystemModel, {Dir View Example}, {Model Classes} */ /*! Constructs a new directory model with the given \a parent. Only those files matching the \a nameFilters and the \a filters are included in the model. The sort order is given by the \a sort flags. */ ResourceModel::ResourceModel(const QStringList &nameFilters, QDir::Filters filters, QDir::SortFlags sort, QObject *parent) : QAbstractItemModel(parent), d_ptr(new ResourceModelPrivate(this)) { Q_D(ResourceModel); // we always start with QDir::drives() d->nameFilters = nameFilters.isEmpty() ? QStringList(QLatin1String("*")) : nameFilters; d->filters = filters; d->sort = sort; d->root.parent = nullptr; d->root.info = QFileInfo(); d->clear(&d->root); } /*! Constructs a directory model with the given \a parent. */ ResourceModel::ResourceModel(QObject *parent) : QAbstractItemModel(parent), d_ptr(new ResourceModelPrivate(this)) { Q_D(ResourceModel); d->init(); } /*! Destroys this directory model. */ ResourceModel::~ResourceModel() { delete d_ptr; } /*! Returns the model item index for the item in the \a parent with the given \a row and \a column. */ QModelIndex ResourceModel::index(int row, int column, const QModelIndex &parent) const { Q_D(const ResourceModel); // note that rowCount does lazy population if (column < 0 || column >= columnCount(parent) || row < 0 || parent.column() > 0) return QModelIndex(); // make sure the list of children is up to date ResourceModelPrivate::QDirNode *p = (d->indexValid(parent) ? d->node(parent) : &d->root); Q_ASSERT(p); if (!p->populated) d->populate(p); // populate without stat'ing if (row >= p->children.count()) return QModelIndex(); // now get the internal pointer for the index ResourceModelPrivate::QDirNode *n = d->node(row, d->indexValid(parent) ? p : nullptr); Q_ASSERT(n); return createIndex(row, column, n); } /*! Return the parent of the given \a child model item. */ QModelIndex ResourceModel::parent(const QModelIndex &child) const { Q_D(const ResourceModel); if (!d->indexValid(child)) return QModelIndex(); ResourceModelPrivate::QDirNode *node = d->node(child); ResourceModelPrivate::QDirNode *par = (node ? node->parent : nullptr); if (par == nullptr) // parent is the root node return QModelIndex(); // get the parent's row const QVector children = par->parent ? par->parent->children : d->root.children; Q_ASSERT(children.count() > 0); int row = (par - &(children.at(0))); Q_ASSERT(row >= 0); return createIndex(row, 0, par); } /*! Returns the number of rows in the \a parent model item. */ int ResourceModel::rowCount(const QModelIndex &parent) const { Q_D(const ResourceModel); if (parent.column() > 0) return 0; if (!parent.isValid()) { // qDebug() << "Root" << d->root; if (!d->root.populated) // lazy population d->populate(&d->root); return d->root.children.count(); } if (parent.model() != this) return 0; ResourceModelPrivate::QDirNode *p = d->node(parent); if (p->info.isDir() && !p->populated) // lazy population d->populate(p); return p->children.count(); } /*! Returns the number of columns in the \a parent model item. */ int ResourceModel::columnCount(const QModelIndex &parent) const { if (parent.column() > 0) return 0; return 4; } /*! Returns the data for the model item \a index with the given \a role. */ QVariant ResourceModel::data(const QModelIndex &index, int role) const { Q_D(const ResourceModel); if (!d->indexValid(index)) return QVariant(); if (role == Qt::DisplayRole || role == Qt::EditRole) { switch (index.column()) { case 0: return d->name(index); case 1: return d->size(index); case 2: return d->type(index); case 3: return d->time(index); default: qWarning("data: invalid display value column %d", index.column()); return QVariant(); } } if (index.column() == 0) { if (role == FilePathRole) return filePath(index); if (role == FileNameRole) return fileName(index); } if (index.column() == 1 && Qt::TextAlignmentRole == role) { return QVariant::fromValue(Qt::AlignRight | Qt::AlignVCenter); } return QVariant(); } /*! Sets the data for the model item \a index with the given \a role to the data referenced by the \a value. Returns true if successful; otherwise returns false. \sa Qt::ItemDataRole */ bool ResourceModel::setData(const QModelIndex &index, const QVariant &value, int role) { Q_D(ResourceModel); if (!d->indexValid(index) || index.column() != 0 || (flags(index) & Qt::ItemIsEditable) == 0 || role != Qt::EditRole) return false; ResourceModelPrivate::QDirNode *node = d->node(index); QDir dir = node->info.dir(); QString name = value.toString(); if (dir.rename(node->info.fileName(), name)) { node->info = QFileInfo(dir, name); QModelIndex sibling = index.sibling(index.row(), 3); emit dataChanged(index, sibling); d->toBeRefreshed = index.parent(); QMetaObject::invokeMethod(this, "_q_refresh", Qt::QueuedConnection); return true; } return false; } /*! Returns the data stored under the given \a role for the specified \a section of the header with the given \a orientation. */ QVariant ResourceModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal) { if (role != Qt::DisplayRole) return QVariant(); switch (section) { case 0: return tr("Name"); case 1: return tr("Size"); case 2: return #ifdef Q_OS_MAC tr("Kind", "Match OS X Finder"); #else tr("Type", "All other platforms"); #endif // Windows - Type // OS X - Kind // Konqueror - File Type // Nautilus - Type case 3: return tr("Date Modified"); default: return QVariant(); } } return QAbstractItemModel::headerData(section, orientation, role); } QMap ResourceModel::itemData(const QModelIndex &index) const { QMap map = QAbstractItemModel::itemData(index); map.insert(ResourceModel::FilePathRole, data(index, ResourceModel::FilePathRole)); map.insert(ResourceModel::FileNameRole, data(index, ResourceModel::FileNameRole)); return map; } /*! Returns true if the \a parent model item has children; otherwise returns false. */ bool ResourceModel::hasChildren(const QModelIndex &parent) const { Q_D(const ResourceModel); if (parent.column() > 0) return false; if (!parent.isValid()) // the invalid index is the "My Computer" item return true; // the drives ResourceModelPrivate::QDirNode *p = d->node(parent); Q_ASSERT(p); if (d->lazyChildCount) // optimization that only checks for children if the node has been populated return p->info.isDir(); return p->info.isDir() && rowCount(parent) > 0; } /*! Returns the item flags for the given \a index in the model. \sa Qt::ItemFlags */ Qt::ItemFlags ResourceModel::flags(const QModelIndex &index) const { Q_D(const ResourceModel); Qt::ItemFlags flags = QAbstractItemModel::flags(index); if (!d->indexValid(index)) return flags; flags |= Qt::ItemIsDragEnabled; if (d->readOnly) return flags; ResourceModelPrivate::QDirNode *node = d->node(index); if ((index.column() == 0) && node->info.isWritable()) { flags |= Qt::ItemIsEditable; if (fileInfo(index).isDir()) // is directory and is editable flags |= Qt::ItemIsDropEnabled; } return flags; } /*! Sort the model items in the \a column using the \a order given. The order is a value defined in \l Qt::SortOrder. */ void ResourceModel::sort(int column, Qt::SortOrder order) { QDir::SortFlags sort = QDir::DirsFirst | QDir::IgnoreCase; if (order == Qt::DescendingOrder) sort |= QDir::Reversed; switch (column) { case 0: sort |= QDir::Name; break; case 1: sort |= QDir::Size; break; case 2: sort |= QDir::Type; break; case 3: sort |= QDir::Time; break; default: break; } setSorting(sort); } /*! Returns a list of MIME types that can be used to describe a list of items in the model. */ QStringList ResourceModel::mimeTypes() const { return QStringList(QLatin1String("text/uri-list")); } /*! Returns an object that contains a serialized description of the specified \a indexes. The format used to describe the items corresponding to the indexes is obtained from the mimeTypes() function. If the list of indexes is empty, 0 is returned rather than a serialized empty list. */ QMimeData *ResourceModel::mimeData(const QModelIndexList &indexes) const { QList urls; QList::const_iterator it = indexes.begin(); for (; it != indexes.end(); ++it) if ((*it).column() == 0) urls << QUrl::fromLocalFile(filePath(*it)); auto *data = new QMimeData(); data->setUrls(urls); return data; } /*! Handles the \a data supplied by a drag and drop operation that ended with the given \a action over the row in the model specified by the \a row and \a column and by the \a parent index. \sa supportedDropActions() */ bool ResourceModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int /* row */, int /* column */, const QModelIndex &parent) { Q_D(ResourceModel); if (!d->indexValid(parent) || isReadOnly()) return false; bool success = true; QString to = filePath(parent) + QDir::separator(); QModelIndex _parent = parent; QList urls = data->urls(); auto it = urls.constBegin(); switch (action) { case Qt::CopyAction: for (; it != urls.constEnd(); ++it) { QString path = (*it).toLocalFile(); success = QFile::copy(path, to + QFileInfo(path).fileName()) && success; } break; case Qt::LinkAction: for (; it != urls.constEnd(); ++it) { QString path = (*it).toLocalFile(); success = QFile::link(path, to + QFileInfo(path).fileName()) && success; } break; case Qt::MoveAction: for (; it != urls.constEnd(); ++it) { QString path = (*it).toLocalFile(); if (QFile::copy(path, to + QFileInfo(path).fileName()) && QFile::remove(path)) { QModelIndex idx=index(QFileInfo(path).path()); if (idx.isValid()) { refresh(idx); //the previous call to refresh may invalidate the _parent. so recreate a new QModelIndex _parent = index(to); } } else { success = false; } } break; default: return false; } if (success) refresh(_parent); return success; } /*! Returns the drop actions supported by this model. \sa Qt::DropActions */ Qt::DropActions ResourceModel::supportedDropActions() const { return Qt::CopyAction | Qt::MoveAction; // FIXME: LinkAction is not supported yet } /*! Sets the name \a filters for the directory model. */ void ResourceModel::setNameFilters(const QStringList &filters) { Q_D(ResourceModel); d->nameFilters = filters; emit layoutAboutToBeChanged(); if (d->shouldStat) refresh(QModelIndex()); else d->invalidate(); emit layoutChanged(); } /*! Returns a list of filters applied to the names in the model. */ QStringList ResourceModel::nameFilters() const { Q_D(const ResourceModel); return d->nameFilters; } /*! Sets the directory model's filter to that specified by \a filters. Note that the filter you set should always include the QDir::AllDirs enum value, otherwise ResourceModel won't be able to read the directory structure. \sa QDir::Filters */ void ResourceModel::setFilter(QDir::Filters filters) { Q_D(ResourceModel); d->filters = filters; emit layoutAboutToBeChanged(); if (d->shouldStat) refresh(QModelIndex()); else d->invalidate(); emit layoutChanged(); } /*! Returns the filter specification for the directory model. \sa QDir::Filters */ QDir::Filters ResourceModel::filter() const { Q_D(const ResourceModel); return d->filters; } /*! Sets the directory model's sorting order to that specified by \a sort. \sa QDir::SortFlags */ void ResourceModel::setSorting(QDir::SortFlags sort) { Q_D(ResourceModel); d->sort = sort; emit layoutAboutToBeChanged(); if (d->shouldStat) refresh(QModelIndex()); else d->invalidate(); emit layoutChanged(); } /*! Returns the sorting method used for the directory model. \sa QDir::SortFlags */ QDir::SortFlags ResourceModel::sorting() const { Q_D(const ResourceModel); return d->sort; } /*! \property ResourceModel::resolveSymlinks \brief Whether the directory model should resolve symbolic links This is only relevant on operating systems that support symbolic links. */ void ResourceModel::setResolveSymlinks(bool enable) { Q_D(ResourceModel); d->resolveSymlinks = enable; } bool ResourceModel::resolveSymlinks() const { Q_D(const ResourceModel); return d->resolveSymlinks; } /*! \property ResourceModel::readOnly \brief Whether the directory model allows writing to the file system If this property is set to false, the directory model will allow renaming, copying and deleting of files and directories. This property is true by default */ void ResourceModel::setReadOnly(bool enable) { Q_D(ResourceModel); d->readOnly = enable; } bool ResourceModel::isReadOnly() const { Q_D(const ResourceModel); return d->readOnly; } /*! \property ResourceModel::lazyChildCount \brief Whether the directory model optimizes the hasChildren function to only check if the item is a directory. If this property is set to false, the directory model will make sure that a directory actually containes any files before reporting that it has children. Otherwise the directory model will report that an item has children if the item is a directory. This property is false by default */ void ResourceModel::setLazyChildCount(bool enable) { Q_D(ResourceModel); d->lazyChildCount = enable; } bool ResourceModel::lazyChildCount() const { Q_D(const ResourceModel); return d->lazyChildCount; } /*! ResourceModel caches file information. This function updates the cache. The \a parent parameter is the directory from which the model is updated; the default value will update the model from root directory of the file system (the entire model). */ void ResourceModel::refresh(const QModelIndex &parent) { Q_D(ResourceModel); ResourceModelPrivate::QDirNode *n = d->indexValid(parent) ? d->node(parent) : &(d->root); int rows = n->children.count(); if (rows == 0) { emit layoutAboutToBeChanged(); n->stat = true; // make sure that next time we read all the info n->populated = false; emit layoutChanged(); return; } beginResetModel(); // emit layoutAboutToBeChanged(); d->savePersistentIndexes(); // d->rowsAboutToBeRemoved(parent, 0, rows - 1); n->stat = true; // make sure that next time we read all the info d->clear(n); // d->rowsRemoved(parent, 0, rows - 1); d->restorePersistentIndexes(); // emit layoutChanged(); endResetModel(); } /*! \overload Returns the model item index for the given \a path. */ QModelIndex ResourceModel::index(const QString &path, int column) const { Q_D(const ResourceModel); if (path.isEmpty() || path == QCoreApplication::translate("QFileDialog", "My Computer")) return QModelIndex(); QString absolutePath = QDir(path).absolutePath(); #if (defined(Q_OS_WIN) && !defined(Q_OS_WINCE)) || defined(Q_OS_SYMBIAN) absolutePath = absolutePath.toLower(); #endif #if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) // On Windows, "filename......." and "filename" are equivalent if (absolutePath.endsWith(QLatin1Char('.'))) { int i; for (i = absolutePath.count() - 1; i >= 0; --i) { if (absolutePath.at(i) != QLatin1Char('.')) break; } absolutePath = absolutePath.left(i+1); } #endif QStringList pathElements = absolutePath.split(QLatin1Char('/'), QString::SkipEmptyParts); if ((pathElements.isEmpty() || !QFileInfo(path).exists()) #if !defined(Q_OS_WIN) || defined(Q_OS_WINCE) && path != QLatin1String("/") #endif ) return QModelIndex(); QModelIndex idx; // start with "My Computer" if (!d->root.populated) // make sure the root is populated d->populate(&d->root); #if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) if (absolutePath.startsWith(QLatin1String("//"))) { // UNC path QString host = pathElements.first(); int r = 0; for (; r < d->root.children.count(); ++r) if (d->root.children.at(r).info.fileName() == host) break; bool childAppended = false; if (r >= d->root.children.count() && d->allowAppendChild) { d->appendChild(&d->root, QLatin1String("//") + host); childAppended = true; } idx = index(r, 0, QModelIndex()); pathElements.pop_front(); if (childAppended) emit const_cast(this)->layoutChanged(); } else #endif #if (defined(Q_OS_WIN) && !defined(Q_OS_WINCE)) || defined(Q_OS_SYMBIAN) if (pathElements.at(0).endsWith(QLatin1Char(':'))) { pathElements[0] += QLatin1Char('/'); } #else // add the "/" item, since it is a valid path element on unix pathElements.prepend(QLatin1String("/")); #endif for (int i = 0; i < pathElements.count(); ++i) { Q_ASSERT(!pathElements.at(i).isEmpty()); QString element = pathElements.at(i); ResourceModelPrivate::QDirNode *parent = (idx.isValid() ? d->node(idx) : &d->root); Q_ASSERT(parent); if (!parent->populated) d->populate(parent); // search for the element in the child nodes first int row = -1; for (int j = parent->children.count() - 1; j >= 0; --j) { const QFileInfo& fi = parent->children.at(j).info; QString childFileName; childFileName = idx.isValid() ? fi.fileName() : fi.absoluteFilePath(); #if (defined(Q_OS_WIN) && !defined(Q_OS_WINCE)) || defined(Q_OS_SYMBIAN) childFileName = childFileName.toLower(); #endif if (childFileName == element) { if (i == pathElements.count() - 1) parent->children[j].stat = true; row = j; break; } } // we couldn't find the path element, we create a new node since we _know_ that the path is valid if (row == -1) { #if defined(Q_OS_WINCE) QString newPath; if (parent->info.isRoot()) newPath = parent->info.absoluteFilePath() + element; else newPath = parent->info.absoluteFilePath() + QLatin1Char('/') + element; #else QString newPath = parent->info.absoluteFilePath() + QLatin1Char('/') + element; #endif if (!d->allowAppendChild || !QFileInfo(newPath).isDir()) return QModelIndex(); d->appendChild(parent, newPath); row = parent->children.count() - 1; if (i == pathElements.count() - 1) // always stat children of the last element parent->children[row].stat = true; emit const_cast(this)->layoutChanged(); } Q_ASSERT(row >= 0); idx = createIndex(row, 0, static_cast(&parent->children[row])); Q_ASSERT(idx.isValid()); } if (column != 0) return idx.sibling(idx.row(), column); return idx; } /*! Returns true if the model item \a index represents a directory; otherwise returns false. */ bool ResourceModel::isDir(const QModelIndex &index) const { Q_D(const ResourceModel); Q_ASSERT(d->indexValid(index)); ResourceModelPrivate::QDirNode *node = d->node(index); return node->info.isDir(); } /*! Create a directory with the \a name in the \a parent model item. */ QModelIndex ResourceModel::mkdir(const QModelIndex &parent, const QString &name) { Q_D(ResourceModel); if (!d->indexValid(parent) || isReadOnly()) return QModelIndex(); ResourceModelPrivate::QDirNode *p = d->node(parent); QString path = p->info.absoluteFilePath(); // For the indexOf() method to work, the new directory has to be a direct child of // the parent directory. QDir newDir(name); QDir dir(path); if (newDir.isRelative()) newDir = QDir(path + QLatin1Char('/') + name); QString childName = newDir.dirName(); // Get the singular name of the directory newDir.cdUp(); if (newDir.absolutePath() != dir.absolutePath() || !dir.mkdir(name)) return QModelIndex(); // nothing happened refresh(parent); QStringList entryList = d->entryList(path); int r = entryList.indexOf(childName); QModelIndex i = index(r, 0, parent); // return an invalid index return i; } /*! Removes the directory corresponding to the model item \a index in the directory model and \bold{deletes the corresponding directory from the file system}, returning true if successful. If the directory cannot be removed, false is returned. \warning This function deletes directories from the file system; it does \bold{not} move them to a location where they can be recovered. \sa remove() */ bool ResourceModel::rmdir(const QModelIndex &index) { Q_D(ResourceModel); if (!d->indexValid(index) || isReadOnly()) return false; ResourceModelPrivate::QDirNode *n = d_func()->node(index); if (!n->info.isDir()) { qWarning("rmdir: the node is not a directory"); return false; } QModelIndex par = parent(index); ResourceModelPrivate::QDirNode *p = d_func()->node(par); QDir dir = p->info.dir(); // parent dir QString path = n->info.absoluteFilePath(); if (!dir.rmdir(path)) return false; refresh(par); return true; } /*! Removes the model item \a index from the directory model and \bold{deletes the corresponding file from the file system}, returning true if successful. If the item cannot be removed, false is returned. \warning This function deletes files from the file system; it does \bold{not} move them to a location where they can be recovered. \sa rmdir() */ bool ResourceModel::remove(const QModelIndex &index) { Q_D(ResourceModel); if (!d->indexValid(index) || isReadOnly()) return false; ResourceModelPrivate::QDirNode *n = d_func()->node(index); if (n->info.isDir()) return false; QModelIndex par = parent(index); ResourceModelPrivate::QDirNode *p = d_func()->node(par); QDir dir = p->info.dir(); // parent dir QString path = n->info.absoluteFilePath(); if (!dir.remove(path)) return false; refresh(par); return true; } /*! Returns the path of the item stored in the model under the \a index given. */ QString ResourceModel::filePath(const QModelIndex &index) const { Q_D(const ResourceModel); if (d->indexValid(index)) { QFileInfo fi = fileInfo(index); if (d->resolveSymlinks && fi.isSymLink()) fi = d->resolvedInfo(fi); return QDir::cleanPath(fi.absoluteFilePath()); } return QString(); // root path } /*! Returns the name of the item stored in the model under the \a index given. */ QString ResourceModel::fileName(const QModelIndex &index) const { Q_D(const ResourceModel); if (!d->indexValid(index)) return QString(); QFileInfo info = fileInfo(index); if (info.isRoot()) return info.absoluteFilePath(); if (d->resolveSymlinks && info.isSymLink()) info = d->resolvedInfo(info); return info.fileName(); } /*! Returns the file information for the specified model \a index. \bold{Note:} If the model index represents a symbolic link in the underlying filing system, the file information returned will contain information about the symbolic link itself, regardless of whether resolveSymlinks is enabled or not. \sa QFileInfo::symLinkTarget() */ QFileInfo ResourceModel::fileInfo(const QModelIndex &index) const { Q_D(const ResourceModel); Q_ASSERT(d->indexValid(index)); ResourceModelPrivate::QDirNode *node = d->node(index); return node->info; } /*! \fn QObject *ResourceModel::parent() const \internal */ /* The root node is never seen outside the model. */ void ResourceModelPrivate::init() { Q_Q(ResourceModel); filters = QDir::AllEntries | QDir::NoDotAndDotDot; sort = QDir::Name; nameFilters << QLatin1String("*"); root.parent = nullptr; root.info = QFileInfo(":"); clear(&root); } QHash ResourceModel::roleNames() const { QHash roles = QAbstractItemModel::roleNames(); roles.insert(ResourceModel::FilePathRole, "filePath"); roles.insert(ResourceModel::FileNameRole, "fileName"); return roles; } ResourceModelPrivate::QDirNode *ResourceModelPrivate::node(int row, QDirNode *parent) const { if (row < 0) return nullptr; bool isDir = !parent || parent->info.isDir(); QDirNode *p = (parent ? parent : &root); if (isDir && !p->populated) populate(p); // will also resolve symlinks if (row >= p->children.count()) { qWarning("node: the row does not exist"); return nullptr; } return const_cast(&p->children.at(row)); } QVector ResourceModelPrivate::children(QDirNode *parent, bool stat) const { Q_ASSERT(parent); QFileInfoList infoList; if (parent == &root) { parent = nullptr; infoList.append(root.info); } else if (parent->info.isDir()) { //resolve directory links only if requested. if (parent->info.isSymLink() && resolveSymlinks) { QString link = parent->info.symLinkTarget(); if (link.size() > 1 && link.at(link.size() - 1) == QDir::separator()) link.chop(1); if (stat) infoList = entryInfoList(link); else infoList = QDir(link).entryInfoList(nameFilters, QDir::AllEntries | QDir::System); } else { if (stat) infoList = entryInfoList(parent->info.absoluteFilePath()); else infoList = QDir(parent->info.absoluteFilePath()).entryInfoList(nameFilters, QDir::AllEntries | QDir::System); } } QVector nodes(infoList.count()); for (int i = 0; i < infoList.count(); ++i) { QDirNode &node = nodes[i]; node.parent = parent; node.info = infoList.at(i); node.populated = false; node.stat = shouldStat; } return nodes; } void ResourceModelPrivate::_q_refresh() { Q_Q(ResourceModel); q->refresh(toBeRefreshed); toBeRefreshed = QModelIndex(); } void ResourceModelPrivate::savePersistentIndexes() { // Q_Q(ResourceModel); savedPersistent.clear(); // foreach (QPersistentModelIndexData *data, q->persistentIndexes()) { // SavedPersistent saved; // QModelIndex index = data->index; // saved.path = q->filePath(index); // saved.column = index.column(); // saved.data = data; // saved.index = index; // savedPersistent.append(saved); // } } void ResourceModelPrivate::restorePersistentIndexes() { // Q_Q(ResourceModel); // bool allow = allowAppendChild; // allowAppendChild = false; // for (int i = 0; i < savedPersistent.count(); ++i) { // QPersistentModelIndexData *data = savedPersistent.at(i).data; // QString path = savedPersistent.at(i).path; // int column = savedPersistent.at(i).column; // QModelIndex idx = q->index(path, column); // if (idx != data->index || data->model == 0) { // //data->model may be equal to 0 if the model is getting destroyed // persistent.indexes.remove(data->index); // data->index = idx; // data->model = q; // if (idx.isValid()) // persistent.indexes.insert(idx, data); // } // } savedPersistent.clear(); // allowAppendChild = allow; } QFileInfoList ResourceModelPrivate::entryInfoList(const QString &path) const { const QDir dir(path); return dir.entryInfoList(nameFilters, filters, sort); } QStringList ResourceModelPrivate::entryList(const QString &path) const { const QDir dir(path); return dir.entryList(nameFilters, filters, sort); } QString ResourceModelPrivate::name(const QModelIndex &index) const { const QDirNode *n = node(index); const QFileInfo info = n->info; if (info.isRoot()) { QString name = info.absoluteFilePath(); #if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) if (name.startsWith(QLatin1Char('/'))) // UNC host return info.fileName(); #endif #if (defined(Q_OS_WIN) && !defined(Q_OS_WINCE)) || defined(Q_OS_SYMBIAN) if (name.endsWith(QLatin1Char('/'))) name.chop(1); #endif return name; } return info.fileName(); } QString ResourceModelPrivate::size(const QModelIndex &index) const { const QDirNode *n = node(index); if (n->info.isDir()) { #ifdef Q_OS_MAC return QLatin1String("--"); #else return QLatin1String(""); #endif // Windows - "" // OS X - "--" // Konqueror - "4 KB" // Nautilus - "9 items" (the number of children) } // According to the Si standard KB is 1000 bytes, KiB is 1024 // but on windows sizes are calulated by dividing by 1024 so we do what they do. const quint64 kb = 1024; const quint64 mb = 1024 * kb; const quint64 gb = 1024 * mb; const quint64 tb = 1024 * gb; quint64 bytes = n->info.size(); if (bytes >= tb) return ResourceModel::tr("%1 TB").arg(QLocale().toString(qreal(bytes) / tb, 'f', 3)); if (bytes >= gb) return ResourceModel::tr("%1 GB").arg(QLocale().toString(qreal(bytes) / gb, 'f', 2)); if (bytes >= mb) return ResourceModel::tr("%1 MB").arg(QLocale().toString(qreal(bytes) / mb, 'f', 1)); if (bytes >= kb) return ResourceModel::tr("%1 KB").arg(QLocale().toString(bytes / kb)); return ResourceModel::tr("%1 byte(s)").arg(QLocale().toString(bytes)); } QString ResourceModelPrivate::type(const QModelIndex &index) const { // poor interpolation of what QFileIconProvider::type() did // TODO: for Qt5 QMimeType might actually be a better choice here if (!index.parent().isValid()) return ResourceModel::tr("Root"); if (node(index)->info.isDir()) return ResourceModel::tr("Folder"); return ResourceModel::tr("%1 File").arg(node(index)->info.suffix()); } QString ResourceModelPrivate::time(const QModelIndex &index) const { #ifndef QT_NO_DATESTRING return node(index)->info.lastModified().toString(Qt::LocalDate); #else Q_UNUSED(index); return QString(); #endif } void ResourceModelPrivate::appendChild(ResourceModelPrivate::QDirNode *parent, const QString &path) const { ResourceModelPrivate::QDirNode node; node.populated = false; node.stat = shouldStat; node.parent = (parent == &root ? nullptr : parent); node.info = QFileInfo(path); node.info.setCaching(true); // The following append(node) may reallocate the vector, thus // we need to update the pointers to the childnodes parent. ResourceModelPrivate *that = const_cast(this); that->savePersistentIndexes(); parent->children.append(node); for (int i = 0; i < parent->children.count(); ++i) { QDirNode *childNode = &parent->children[i]; for (int j = 0; j < childNode->children.count(); ++j) childNode->children[j].parent = childNode; } that->restorePersistentIndexes(); } QFileInfo ResourceModelPrivate::resolvedInfo(QFileInfo info) { #ifdef Q_OS_WIN // On windows, we cannot create a shortcut to a shortcut. return QFileInfo(info.symLinkTarget()); #else QStringList paths; do { QFileInfo link(info.symLinkTarget()); if (link.isRelative()) info.setFile(info.absolutePath(), link.filePath()); else info = link; if (paths.contains(info.absoluteFilePath())) return QFileInfo(); paths.append(info.absoluteFilePath()); } while (info.isSymLink()); return info; #endif } #include "moc_resourcemodel.cpp" gammaray-2.11.3/3rdparty/qt/resourcemodel.h000066400000000000000000000120621413310443000206030ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). ** All rights reserved. ** Contact: Nokia Corporation (qt-info@nokia.com) ** ** This file is part of the QtGui module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** No Commercial Usage ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions ** contained in the Technology Preview License Agreement accompanying ** this package. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional ** rights. These rights are described in the Nokia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** If you have questions regarding the use of this file, please contact ** Nokia at qt-info@nokia.com. ** ** ** ** ** ** ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef RESOURCEMODELMODEL_H #define RESOURCEMODELMODEL_H #include #include QT_MODULE(Gui) namespace GammaRay { class ResourceModelPrivate; class ResourceModel : public QAbstractItemModel { Q_OBJECT Q_PROPERTY(bool resolveSymlinks READ resolveSymlinks WRITE setResolveSymlinks) Q_PROPERTY(bool readOnly READ isReadOnly WRITE setReadOnly) Q_PROPERTY(bool lazyChildCount READ lazyChildCount WRITE setLazyChildCount) public: enum Roles { // Qt4 uses 32, Qt5 256, for Qt::UserRole - use the latter globaly to allow combining Qt4/5 client/servers. FilePathRole = 256 + 1, FileNameRole }; ResourceModel(const QStringList &nameFilters, QDir::Filters filters, QDir::SortFlags sort, QObject *parent = nullptr); explicit ResourceModel(QObject *parent = nullptr); ~ResourceModel() override; QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; QModelIndex parent(const QModelIndex &child) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; QMap itemData(const QModelIndex &index) const override; bool hasChildren(const QModelIndex &index = QModelIndex()) const override; Qt::ItemFlags flags(const QModelIndex &index) const override; void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override; QStringList mimeTypes() const override; QMimeData *mimeData(const QModelIndexList &indexes) const override; bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override; Qt::DropActions supportedDropActions() const override; QHash roleNames() const override; // ResourceModel specific API void setNameFilters(const QStringList &filters); QStringList nameFilters() const; void setFilter(QDir::Filters filters); QDir::Filters filter() const; void setSorting(QDir::SortFlags sort); QDir::SortFlags sorting() const; void setResolveSymlinks(bool enable); bool resolveSymlinks() const; void setReadOnly(bool enable); bool isReadOnly() const; void setLazyChildCount(bool enable); bool lazyChildCount() const; QModelIndex index(const QString &path, int column = 0) const; bool isDir(const QModelIndex &index) const; QModelIndex mkdir(const QModelIndex &parent, const QString &name); bool rmdir(const QModelIndex &index); bool remove(const QModelIndex &index); QString filePath(const QModelIndex &index) const; QString fileName(const QModelIndex &index) const; QFileInfo fileInfo(const QModelIndex &index) const; #ifdef Q_NO_USING_KEYWORD inline QObject *parent() const { return QObject::parent(); } #else using QObject::parent; #endif public Q_SLOTS: void refresh(const QModelIndex &parent = QModelIndex()); protected: ResourceModel(ResourceModelPrivate &, QObject *parent = nullptr); friend class QFileDialogPrivate; private: Q_DECLARE_PRIVATE(ResourceModel) ResourceModelPrivate * const d_ptr; Q_DISABLE_COPY(ResourceModel) Q_PRIVATE_SLOT(d_func(), void _q_refresh()) }; } #endif // QDIRMODEL_H gammaray-2.11.3/CHANGES000066400000000000000000000630711413310443000143670ustar00rootroot00000000000000Release Highlights ================== Version 2.11.3 -------------- * Increase CMake requirement to version 3.4.0 (reflecting reality). * Update 3rdparty/StackWalker. * Update 3rdparty/backward-cpp. * Don't fail self-test for lldb version 10.x. * Support for CMAKE_SYSTEM_PROCESSOR amd64. * Fix crash when editing the margins property of a QtCharts/ChartView. * Fix crash when attaching to an ASAN-enabled application. * Register stream operators for Qt::CaseSensitivity. * Clean some runtime, compile , CMake warnings, etc. Version 2.11.2 -------------- * Prefer picking QtQuick items in fully visible parent-child chains. * Fix deadlock with Qt 5.15 on high DPI screens. * Fix QtQuick detection on older macOS versions. * Support for Qt 5.15. Version 2.11.1 -------------- * Add OpenGL ES2 fallback for the Qt3D geometry inspector. * Fix assert on empty text document selections. * Fix side-effects of using the widget layout overlay on a QSplitter. * Fix merging of events in the event monitor. * Fix event order in the event monitor. * Add event propagation detection for QtQuick pointer events. * Fix compilation with QT_NO_OPENGL and QT_NO_SSL. * Adapt QtQuick software renderer inspector to changes in Qt 5.14. * Fix Android support with Qt 5.14. * Fix memory leak in event recording when Qt Quick remote view is active. * Fix possible crash during probe destruction. * Fix crash due to signal spy changes in Qt 5.14. * Fix possible crash in the outbound connections view. Version 2.11.0 -------------- * Drop support for Qt 4 and Qt <= 5.4. * Drop support for MSVC 2010 and MSVC 2012, as well as GCC < 4.8. * Add support for more QtNetwork properties. * Add new network operations monitoring tool. * Fix inspection of QJson types. * Add thread affinity check to the problem reporter. * Add new event monitoring tool. * Initial forward compatibility with Qt6 build system. * Improved performance of the Qt Quick 2 inspector and the signal monitor. Version 2.10.1 -------------- * Fix detection of Qt infix builds. * Fix QMake integration for Qt 5.12.1. * Fix QtQuick inspector crash when reparenting to an unknown destination item. * Fix scene graph model going out of sync on moves. * Fix inconsistent transient state in QtQuick item model that can corrupt proxy model state. * Fix Qt version detection for infix builds. * Fix Qt version detection from executing Qt5Core.so on Linux. * Fix infinite recursion if the target has its own message handler. * Adapt to Qt 5.14 changes in signal spy and connection handling. * Adapt to Qt 5.13 changes in QQmlType and QML attached property handling. * Adapt to Qt 5.13 changes in QtIvi. * Fix ABI incompatibility with MSVC builds due to QT_STRICT_ITERATOR usage. * Fix enum to string conversion producing flag-like results sometimes. * Fix relative search path logic for absolute install path overrides. Version 2.10.0 -------------- * Increase CMake requirement to version 3.1 on all platforms. * Add object navigation to the paint analyzer. * Reduce network load by moving more property model features to the client side. * Port object hierarchy visualizer plugin to Qt 5. * Add new system information plugin, which supersedes the standard paths plugin. * Use application palette rather than style palette in the style inspector. * Improve gdb injector performance. * Reduce performance impact of the widget paint analyzer in Windows debug builds. * Improve Qt Quick item picking in case of zero-sized parent elements. * Provide access to QAbstractItemModel role name mapping. * Add QtPositioning plugin allowing to monitor and simulate geo localization. * Add NMEA geo localization playback. * Add support for generic functor property accessors. * Add property access for mime data and clipboard types. * Add infrastructure to override generic property access, such as Qt Quick anchor properties. * Add custom property access for Qt Quick anchors properties to no longer trigger their on-demand creation. * Add generic problem reporter infrastructure, and port some existing checks to it. Version 2.9.1 ------------- * Fix crash in in-process mode with networking disabled. * Fix compilation with Qt 5.12. * Update Android toolchain file. * Allow to disable QtScript dependency. * Fix crash on zero-sized paint operations. * Fix plugin deployment on Android. * Fix crash in Qt Quick inspector when encountering invalid bounding rects. * Fix activating the Qt3D plugin when attaching to a Qt3D application at runtime. * Fix out-of-bounds framebuffer read in Qt Quick remote view for some non-integer scaling factors. Version 2.9.0 ------------- * Improve Qt Quick Controls 2 tracer coloring. * Fix issues with QSG diagnostic render modes in combination with QQuickWidget. * Improve input validation in the connect dialog. * Fix Qt Quick remote view on targets without GL_BGRA support. * Add texture and texture atlas inspection for QSGMaterial using objects. * Only read and send the actually visible screen content in the Qt Quick remote view. * Support dynamic shaders in the QSGMaterial shader view. * Show vertex and fragment uniforms for shader effects in the QSGMaterial tab. * Fix crashes during changing/restoring QSG diagnostic render modes. * Batch row/column count requests for better remote model performance. * Drop support for CMake 2.8, at least 3.0 is required now. * Improved performance in the Qt Quick inspector. * Add support for QSGRenderNode. * Consider compiler version as part of the probe ABI on Windows. * Fix multithreading issues in the timer inspector. * Add search line to timer inspector. * Add screenshot support in Qt Quick inspector. * Improve item picking support in Qt Quick inspector. * Add texture waste indicator to texture inspection tab. * Add color picker for remote views. * Add object navigation and source navigation to the timer inspector. * Add support for cross-architecture injection on Windows. * Add support for editing enum/flag properties not registered with the Qt meta object system. * Add network configuration view. * Improved indication that a target process is being inspected by GammaRay. * Add widget tab focus chain visualization. * Fix re-attaching to a target application on Windows. * Add QML binding inspector (requires Qt 5.10 or newer). * Add support for QtWebEngine to the web inspector tool. * Improved argument inspection in the QPainter analyzer. * Support paint analysis with the Qt Quick software renderer (requires Qt 5.9.3 or newer). * Improved Qt Quick anchor property display. * Fix property editing of gadget value types. * Fix interaction issues in string type property editor. * Add action to slow down Qt Quick animation timers. * Add support for visualizing clip areas in the Qt Quick software renderer. * Clear Qt Quick target overlay on GammaRay client disconnect. * Add automatic down-cast support to GammaRay metatype system. * Add QObject creation stack trace view. * Add source code navigation to the message browser stack trace view. * Add stack trace view for painter operations in the paint analyzer view. * Fix rpath support in probe ABI detection on macOS. * Fix user manual access on macOS. * Add profiling support to the paint analyzer. * Use relative RPATHs on Linux, to make installations fully relocatable. * Add mouse wheel zoom in the Qt 3D geometry inspector. * Improve performance of the Qt Quick scene graph model. * Improve editing of boolean properties. * Add diagnostic shading modes to the Qt 3D geometry inspector. * Support vertex picking in the Qt 3D geometry inspector. * Support Qt 3D painted textures in the paint analyzer. * Fix Qt 3D buffer decoding for tightly packed attributes. * Fix server-side decoration state going out-of-sync in the Qt Quick inspector. * Fix crash when validating dynamic meta objects. * Fix support of namespaced and semi-qualified enums/flags. Version 2.8.2 ------------- * Fix high-dpi icon installation. * Fix documentation build with spaces in the path. * Fix build of the QtIvi inspector with Qt 5.10. * Fix detection of libdl. * Fix incomplete installation of multi-probe builds on second make install run. * Fix build issues in some Windows and cross-compilation setups. * Fix widget export action plugin loading on Windows. Version 2.8.1 ------------- * Fix compilation with interestingly packaged qmllint on Ubuntu. * Fix error handling in LLDB injector when LLDB scripting support is not available. * Fix tree view corruption when searching. * Fix linking with backward-cpp in BFD mode using a static libbdf. * Fix assert when launching a Windows target using a Qt debug build. * Fix crash during attaching on Windows. * Fix build with MSVC2010 and Qt4.8. * Fix attach dialog not seeing 32bit processes on 64bit Windows systems. * Add support for Qt 5.9.2. Version 2.8.0 ------------- * Fix possible deadlock in WinDLL injector. * Add code navigation for C++-created objects (Linux-only, requires a debug build). * Add legend for the QtQuick inspector overlay. * Add QtQuickControls2 layout support in the QtQuick inspector. * Add layouting grid overlay to the QtQuick inspector. * Add support for non-QtIviProperty properties in QtIvi modules. * Improved QtQuick2 remote view performance. * Basic support for the QtQuick2 software renderer. * Add QtQuick Control 2 tracing support. * Improved lifetime tracking of dynamic meta objects. * Fix alive instance counts not updating correcting in the meta object browser. * Add instance count statistics for QML-defined types in the meta object browser. * Fix crash when debugging dynamically created QtSCXML state machines. * Improved object navigation to the QtIvi inspector. * Improved icon visuals with light/dark color schemes. * Add QTimeZone database browser to the locale inspector. * Allow to specify a working directory in the graphical launcher. * Fix rendering corruption caused by runtime switching of QSG render modes. * Dynamically negotiate QDataStream serialization format, fixes corruption of large QDateTime values. * Fix QSG scene graph expansion on item picking. * Expand user manual to cover object inspection views. * Improved network auto-discovery if target device has multiple interfaces. * More robust ABI detection for Yocto-based cross-compilation to x86 and aarch64 platforms. * Add support for QtIvi 5.10 property overrides. * Fix launching/attaching to targets not having GammaRay in their DLL search path on Windows. * Improved diagnostics for injector failures. * Also find additional probes installed inside the Qt installation itself. * Fix crash in QtQuick inspector. Version 2.7.0 ------------- * Improved enum and flag editor in the property view. * Add style hint view to style inspector. * Fix runtime QSG visualization mode switching for newer Qt versions. * Initial support for statically compiling GammaRay into a target. * Support for QtWayland 5.8. * Add object navigation for the translation inspector. * Automatically re-scan meta types to captures types registered later. * Improved Wayland log and timeline view. * Add object navigation for Wayland clients. * Show alive object statistics in the meta object browser. * Add Qt3D geometry inspector (requires Qt >= 5.7 and Qt3D) * Allow to hide inactive tools. * Fix handling of partially out-of-view QQuickItems in the QQ2 inspector. * Improve error handling in the GDB injector. * Fix QML support not being activated when attaching to a QtQuick application at runtime. * Fix widget layout overlay for nested QLayouts. * Fix crash on client disconnect. * Syntax highlighting and code folding for textual content (requires KF5::SyntaxHighlighting) * Add 3D widget layout visualization. * Allow to hide non-visible elements in the full-depth picking dialog. * Add QtIvi inspection tool (requires Qt5IviCore) * Fix QSG geometry inspector not showing geometry correctly on first selection. * Build both release and debug probes by default when using MSVC. * Add more editor defaults for code navigation on Linux. * Add QtSCXML support to the state machine debugger (requires Qt >= 5.8). * Show QtQuick overlays also in the target application. * Avoid leaking GammaRay translated strings into the translation inspector. * Fix launching targets on macOS >= 10.9. Version 2.6.0 ------------- * Reworked tool view handling, enabling deeper IDE integration of individual tools. * Merged model and selection model inspector. * Fixed crash when quickly changing selection in model inspector. * Add QMetaObject validation, identifying common problems with signal/slot declarations. * Support object navigation to and from the model and text document inspectors as well as the signal plotter. * Visualize model selections. * Allow to inspect model content also for disabled cells. * Show model cell flags in model inspector. * Fix sorting in object method view. * Fix handling of source model changes for proxy and selection models. * Add in-app picking for selection models and actions. * Extended the user manual with example problems diagnosed with GammaRay. * Allow to rescan the meta type database. * Add object navigation from the meta type view to the meta object browser. * Show registered comparison and stream operators in the meta type browser. * Add object navigation support to the action and timer inspectors. * Visualize enabled state in the action inspector too. * Support editing of nested properties of value types. * Fix various enum/flag display issues in the property view. * Fix AArch64 detection on ELF platforms. * Support ABI detection on systems without ldd. * Add Wayland compositor inspector. * Add advanced element picking mode for widget and Qt Quick inspectors. * Fix crash when encountering an empty translation context. * Extend user manual content. * Fix a number of memory leaks. * Fix crash when selecting a dangling top-level layout in the widget inspector. * Fix missing standard icon types in style inspector for some Qt versions. * Fix table layout in style inspector. * Fix invalid memory access in palette model. * Fix selecting entries in the resource browser. Version 2.5.1: -------------- * Fix build of external plugins when using a non-standard GammaRay install prefix. * Fix race when receiving probe settings. * Fix tool selector showing empty entries with Qt4. * Fix widget layout overlay been shown incorrectly on floating dock widgets. * Fix tool navigation context menu showing internal ids rather than tool names. * Fix crash when target closes the connection first. * Extended user manual content. * Fix codec browser showing random values when changing selection. Version 2.5.0: -------------- * Added paint analyzer for QQuickPaintedItems. * Added element picking in QtQuick and widget remote view. * Added input redirection to the widget remote view. * Improved measuring tool in QtQuick remote view, and added measuring support to widget and paint analyzer views. * Added zoom support to the widget remote view. * Widget remote view now always shows the selected window, not only the selected widget. * Fix widget remote view overloading slow targets. * Added support for QQuickWidget. * Added instance counts to meta object browser. * Improved object navigation allowing to select which tool to go to if multiple apply. * Support for object navigation in more plug-ins. * Added edit support for QMatrix4x4, QVector[2,3,4]D, QQaternion and QTransform in the property editor. * Added basic type support for Qt3D. * Added Qt3D scene graph and frame graph inspector (Qt >= 5.7). * Fix SHM resource leaks on UNIX-like systems when target crashes on startup. * Removed non-KDSME fallback QSM visualizer. * Fix QNX 6.6 build. * Fix LLDB injector on OSX. * Added IPv6 support for client/probe communication. * Fixed depth-limiting in the state machine debugger. * Support non-QObject objects as composite types in the property editor. * Added QPainter analyzer for individual QGraphicsItems. * Added source navigation to declaration location for QML items. * Added the ability to browser and configure logging categories. * Added support for browsing QML context properties. * Fix crash on nested QVariant/QJSValue structures. * Improved injector self-tests to detect the Yama security extension blocking injection. * Added source navigation to QRC data. * Support more image formats in the resource browser. * Save and restore window and tool layouts. * Improved display in the model inspector for empty data. * Support block layout elements in the text document inspector (Qt >= 5.5). * Support inspection of QQmlScriptString properties and callable QJSValues. * Fix MSVC2015 runtime detection. * Fix various runtime attaching issues. * Fix display of multi-line string values in the property editor. * Initial support for namespaced Qt. * Add network interface browser. * Add cookie jar browser. * Add recursive access to nested value type properties (read-only). * Add access to SSL cipher and certificate information for QSslSocket. Version 2.4.1: -------------- * Fix exponential resource leak in recursive property editor. * Fix QtQuick item tree view readability with dark color schemes. * Fix rendering glitch in QtQuick item tree view. * Fix selection not working on item picking if the corresponding tree model branch wasn't loaded yet. * Improve QtQuick element picking on item views. * Improve runtime attaching on Windows. * Fix current state configuration not being updated in state machine debugger. * Workaround QtDesigner >= 5.6 incompatibility with CMake 2.8.12. * Fix crash when encountering non-serializable types inside a associative iterable variant. * Fix setting properties on multi-inheriting types. Version 2.4.0: -------------- * Recursive property editor for accessing composite property types. * Support for QML attached properties. * Server-side object search for considerably speeding up as-you-type searches. * Code navigation for QtQuick 2 items and Qt5 debug/warning messages. * Support for Qt5 logging categories in the message view. * Improved backtrace display for warning/error messages in the message view. * Support ABI detection on PE executables. * On-demand population of the font browser for improved performance. * Optimized performance of the QtQuick 2 inspector on item change storms. * Add support for QtPositioning data types. * Add OSX backtrace support in the message handler plug-in. * Improved integration of KDSME in the state machine plug-in. * Improved QMake compatibility when embedding the GammaRay client into a host application. * Fix object construction/destruction reordering issue in multi-threaded targets. * Fix QQ2 preview of QQuickWidget content. * Support rpath resolution for OSX probe ABI detection. * Fix crash on widget reparenting. * Fix crash in QStyle inspector with Windows Vista style. * Improve performance of the translation inspector. * Fix 32bit OSX support. Version 2.3.0: -------------- * Add QtBluetooth support. * Support local sockets next to TCP for connecting to the probe. * Change probe plugin loading to be compatible with Android .apk restrictions. * Don't generate backtraces for GammaRay-caused qWarnings. * Also show properties defined in a meta object in the meta object browser. * Add non-QObject meta object found via QMetaType to the meta object browser (finds Q_GADGETs with Qt 5.5). * Performance improvements in object tracking, remote model, property view, signal spy and several other places. * Ongoing refactoring work to enable embedding the GammaRay client and launcher components into other host applications. * Add copy backtrace feature in the message browser. * Improved UI-less build option for embedded targets. * Fix various crashes, including when interacting with QOpenGLContext, QDBusAdapter, QML ListModels or Qt containers containing types that cannot be serialized. * Make paint analyzing also work with Qt 5.5 despite the necessary functionality being removed in Qt. * Add support for static properties in the property view. * Qt 5.5 compatibility. * Initial support for QNX 6.6. * Improved QtQuick2 in-app item picking. * Fixed/improved tracking of object reparenting in the object tree model. * Fix widget layout overlay widget leaking into object/widget tree. * Optional compression support for the client/server communication, speeds up debugging over slow USB connections. * Fix initial selection in the model browser having no effect. Version 2.2.1: ------------- * Fix spawning a large number of threads in the process list. * Add GAMMARAY_BUILD_UI build option to only build the non-UI parts, useful for embedded targets. * Add workaround for QSortFilterProxyModel regression introduced in qtbase commit bec1854cc0. * Follow kdstatemachineeditor KDSME API changes. * Use the preload injector by default on Mac for Qt >= 5.4. * Fix probe ABI detection on Mac bundles. * Fix asserts caused by icon loading in QCoreApplication-only targets. * Fix crash when target deletes a signal sender in a slot. Version 2.2.0: ------------- * New translation inspector plug-in. * New signal monitor and object lifetime plotter plug-in. * Support displaying of QQmlListProperty contents. * Expose signal/slot spy callback API to plug-ins. * Improved state machine visualization using KDSME. * Support for QQmlTimer in the timertop plug-in. * Support exporting of QResource data. * Improved QSGMaterial viewer. * Various performance improvements for the QtQuick2 remote preview. * Improved Qt 5.4 compatibility. * Fix handling of large images in the resource browser. * Support for manually emitting signals, and improved method display. Version 2.1.2: ------------- * Fix crash when selecting the scene graph root node. * Fix two asserts in Qt 5.4 when dealing with connections to slot objects. * Fix build with Vivante OpenGL ES2 stack. * Build fixes for Android. * Fix build issues with Qt5.4 and Clang. * Fix minor compiler warnings and Coverity Scan issues. Version 2.1.1: ------------- * Fix invoking non-slot methods with arguments. * Fix QtQuick2 preview not showing any content with Qt 5.2 or older. * Fix crash when encountering tooltips in Qt5 targets. * Fix deadlock in object list model. * Fix QGraphicsView scene selection in in-process mode. * Fix debug message handler for Qt5. * A missing relocatable startup hook is no longer fatal with Qt 5.4. * Fix Qt 5.4 compatibility of the QtQuick2 preview. Version 2.1.0: ------------- * Aggregated property view combining static, dynamic and non-QObject property views. * Qt5Qml/Qt5Quick support (see https://github.com/KDAB/GammaRay/wiki/QtQuick2-Support-Status) * Probe ABI auto-detection on Linux, Mac and Windows. * Support to navigate to objects that are property values. * Plug-ins can now add specialized tabs to the property view. * Support adding and deleting dynamic QObject properties. * Support for "hidden" plug-ins, i.e. plug-ins that only provide type support but no own tool view. * Support KF5 in the KJob tracker plug-in. * Support for Qt 5.4 object creation/destruction callbacks. * Improved connection view, and support for viewing connections in Qt5 applications. Version 2.0.2: ------------- * Restore compatibility with Qt4.7 * Avoid leaking shared-memory in case of crashes of gammaray-client * Fix various crashes * Fix various crashes with heavily multithreaded debuggees * Fix unnecessary value limits in property editor for QPointF, QSizeF, etc * Improve property editor value loading performance Version 2.0.1: ------------- * LLDB injector (enables Mac OSX 10.9 support) * Fix various cases where client and server states get out of sync Version 2.0.0: ------------- * Out-of-process UI * Remote Debugging * Multi-probe support * Probe usable on targets without QtWidget available * Fix preload check on MIPS/MIPSel (issue #63) * Support for displaying arguments of monitored signals Version 1.3.2: ------------- * Support more Qt5-specific data types * Fix crash on some QtQuick2 applications * Support VTK6 * Fix compilation with Mac OSX 10.6 * Fix GDB injector on Mac OSX * Fix launching of bundles on Mac OSX * Fix crash in selection model inspector * Add fallback if function overwriting fails in MSVC release builds * Fix WinDLL injector with Qt5 * Fix Qt version detection in the process list on Windows and Mac OSX * Fix probe re-injections * Fix plugin loading on MSVC release builds Version 1.3.1: ------------- * Fix state machine viewer on Windows * Fix crash in model cell viewer * Fix crash in meta object browser caused by deleted dynamic meta objects * Improve performance of the mime type browser * Improvements in the state machine viewer image export * Compile fixes for some Qt5 configurations Version 1.3: ----------- * New KJob tracker plugin (requires KDE4) * Qt5 support * New Meta Object Browser tool * New QStandardPaths and QMimeTypeDatabase browsers (Qt5 only) Version 1.2: ----------- * Published API for writing your own plugin * New QStyle inspector * New QAction inspector with shortcut conflict finder * New QPalette editor * QWidget paint operation introspection using QPaintBuffer (requires private headers) Version 1.1: ----------- * Plug-in system for tools * Graphical state machine viewer * Object hierarchy visualization plug-in * New timer profiler plug-in * New locale inspector plug-in * New property view/editor for non-QObject types (e.g. QGraphicsItem and many of its sub-classes) * New widget export actions (image, PDF, SVG, Designer UI file) * New launcher UI with support for starting new processes, self-testing, and a much faster process list on Mac OSX * Support more types in the property editor * Improved widget tree model * Fix unreliable attaching with MSVC * MingW support Version 1.0: ----------- * Initial Release gammaray-2.11.3/CMakeLists.txt000066400000000000000000000772031413310443000161360ustar00rootroot00000000000000# This is the top-level CMakeLists.txt file for the GammaRay project. # # Pass the following variables to cmake to control the build: # (See Install.txt for more information) # # -DGAMMARAY_BUILD_UI=[true|false] # Build the client and in-process UI. # Default=true (except on QNX and Android) # # -DGAMMARAY_PROBE_ONLY_BUILD=[true|false] # Build only an additional probe configuration for an already existing launcher. # Default=false # # -DGAMMARAY_CLIENT_ONLY_BUILD=[true|false] # Build the client part only. # Default=false # # -DGAMMARAY_ENABLE_GPL_ONLY_FEATURES=[true|false] # Enable features only available under GPL license. # Default=false # # -DGAMMARAY_INSTALL_QT_LAYOUT=[true|false] # Install GammaRay in Qt folder using Qt layout folders: # - plugins will go in /plugins/gammaray # - all the other libs, including probe library will go in /lib # - qmake mkspecs file # Default=false # Only use for Android or when you know what you are doing. # # -DGAMMARAY_BUILD_CLI_INJECTOR=[true|false] # Build the cli injector on Windows # Default=true # # -DGAMMARAY_MULTI_BUILD=[true|false] # Build multiple applicable probe configurations. # Default=true # # -DGAMMARAY_STATIC_PROBE=[true|false] # Build the probe as static library for compile-time injection # Default=false # # -DGAMMARAY_BUILD_DOCS=[true|false] # Build the API documentation and User Manual. # Default=true (except when building the probe only) # -DGAMMARAY_ENFORCE_QT_ASSERTS=[true|false] # Force QT_ASSERT in all builds. # Default=false # # To build the man page from POD, run 'make man' after CMake (assumes perl is available) # To install the resulting man page, run 'make install' # The man page is not available on Windows. # cmake_minimum_required(VERSION 3.4.0) cmake_policy(SET CMP0020 NEW) cmake_policy(SET CMP0043 NEW) if(POLICY CMP0053) cmake_policy(SET CMP0053 NEW) endif() if(POLICY CMP0063) cmake_policy(SET CMP0063 NEW) endif() if(POLICY CMP0068) cmake_policy(SET CMP0068 NEW) endif() if(POLICY CMP0076) cmake_policy(SET CMP0076 NEW) endif() project(GammaRay CXX C) set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/ ${CMAKE_MODULE_PATH}) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOUIC ON) set(CMAKE_AUTORCC ON) set(CMAKE_INCLUDE_CURRENT_DIR ON) if(NOT DEFINED CMAKE_SKIP_BUILD_RPATH) set(CMAKE_SKIP_BUILD_RPATH ON) endif() if(NOT DEFINED CMAKE_BUILD_WITH_INSTALL_RPATH) set(CMAKE_BUILD_WITH_INSTALL_RPATH ON) endif() if(NOT DEFINED CMAKE_INSTALL_RPATH_USE_LINK_PATH) set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) endif() if(NOT DEFINED CMAKE_MACOSX_RPATH) set(CMAKE_MACOSX_RPATH TRUE) endif() if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE "Release" CACHE STRING "" FORCE) endif() include(CheckCCompilerFlag) include(CheckCXXCompilerFlag) include(CheckCXXSymbolExists) include(CheckIncludeFiles) include(CheckLibraryExists) include(CheckSymbolExists) include(CMakeParseArguments) include(CMakePushCheckState) include(CTest) include(GammaRayMacros) include(GammaRayMacrosInternal) include(FeatureSummary) set(KDE_INSTALL_USE_QT_SYS_PATHS OFF) include(ECMGeneratePriFile) include(ECMEnableSanitizers) include(GenerateExportHeader) include("3rdparty/backward-cpp/BackwardConfig.cmake") # Exit for blacklisted compilers (those that don't support C++11 very well) # MSVC++ < 2013 aka 1800 # Clang 3.1 set(BAD_CXX_MESSAGE "") if(MSVC) if(MSVC_VERSION LESS 1800) set(BAD_CXX_MESSAGE "MSVC 2013 or higher") endif() endif() if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.1.0) set(BAD_CXX_MESSAGE "Clang v3.1.0 or higher") endif() endif() if(BAD_CXX_MESSAGE) message(FATAL_ERROR "\nSorry, ${BAD_CXX_MESSAGE} is required to build this software. Please retry using a modern compiler that supports C++11.") endif() if(MSVC AND MSVC_VERSION VERSION_EQUAL 1800) # MSVC 2013 implements some C++11 features only partially, which is why cmake # doesn't list these features as supported, still support is good enough for us. set(GAMMARAY_REQUIRED_CXX_FEATURES cxx_lambdas cxx_nullptr cxx_range_for cxx_raw_string_literals cxx_uniform_initialization cxx_variadic_templates cxx_rvalue_references cxx_defaulted_functions cxx_override cxx_final ) else() set(GAMMARAY_REQUIRED_CXX_FEATURES cxx_lambdas cxx_nullptr cxx_range_for cxx_raw_string_literals cxx_uniform_initialization cxx_variadic_templates cxx_rvalue_references cxx_defaulted_functions cxx_deleted_functions cxx_override cxx_final cxx_nonstatic_member_init ) endif() # Version setup set(GAMMARAY_VERSION_MAJOR "2") set(GAMMARAY_VERSION_MINOR "11") set(GAMMARAY_VERSION_PATCH "3") set(GAMMARAY_VERSION "${GAMMARAY_VERSION_MAJOR}.${GAMMARAY_VERSION_MINOR}.${GAMMARAY_VERSION_PATCH}") set(GAMMARAY_VERSION_STRING "${GAMMARAY_VERSION}") set(GAMMARAY_SOVERSION "2.11.3") set(GAMMARAY_PLUGIN_VERSION "2.11") set(PROJECT_VERSION_STRING "${GAMMARAY_VERSION_STRING}") if(ANDROID) # non-rooted Android doesn't like .so versions and requires the lib prefix set(GAMMARAY_DEFAULT_LIBRARY_PROPERTIES PREFIX "lib") else() set(GAMMARAY_DEFAULT_LIBRARY_PROPERTIES SOVERSION ${GAMMARAY_SOVERSION} VERSION ${GAMMARAY_SOVERSION}) endif() if(EXISTS "${CMAKE_SOURCE_DIR}/.git") find_package(Git) set_package_properties(Git PROPERTIES TYPE OPTIONAL PURPOSE "Determine exact build version.") if(GIT_FOUND) execute_process( COMMAND ${GIT_EXECUTABLE} rev-parse --short HEAD WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} OUTPUT_VARIABLE _git_revision ) string(REGEX REPLACE "\n" "" _git_revision "${_git_revision}") set(GAMMARAY_VERSION_STRING "${GAMMARAY_VERSION_STRING} (revision: ${_git_revision})") endif() endif() configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/config-gammaray-version.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-gammaray-version.h ) message(STATUS "Building GammaRay ${GAMMARAY_VERSION_STRING} in ${CMAKE_BUILD_TYPE} mode") # # Build options # set(GAMMARAY_BUILD_UI_DEFAULT ON) if(QNXNTO OR ANDROID) set(GAMMARAY_BUILD_UI_DEFAULT OFF) endif() gammaray_option( GAMMARAY_PROBE_ONLY_BUILD "Build only an additional probe configuration for an already existing launcher." OFF ) gammaray_option( GAMMARAY_CLIENT_ONLY_BUILD "Build the client part only." OFF ) if(GAMMARAY_CLIENT_ONLY_BUILD) set(GAMMARAY_BUILD_UI_DEFAULT ON) endif() gammaray_option(GAMMARAY_BUILD_UI "Build the GammaRay client and in-process UI." ${GAMMARAY_BUILD_UI_DEFAULT}) if(GAMMARAY_PROBE_ONLY_BUILD AND GAMMARAY_CLIENT_ONLY_BUILD) message(FATAL_ERROR "You can only use one of the *ONLY* option.") endif() gammaray_option(GAMMARAY_ENABLE_GPL_ONLY_FEATURES "Enable features only available under GPL license." OFF) gammaray_option(GAMMARAY_INSTALL_QT_LAYOUT "Install into Qt directory layout." OFF) gammaray_option(GAMMARAY_MULTI_BUILD "Build multiple applicable probe configurations." ON) gammaray_option(GAMMARAY_BUILD_CLI_INJECTOR "Build command line injector on Windows." ON) set(GAMMARAY_BUILD_DOCS_DEFAULT ON) set(GAMMARAY_DISABLE_FEEDBACK_DEFAULT OFF) if(GAMMARAY_PROBE_ONLY_BUILD OR CMAKE_CROSSCOMPILING) set(GAMMARAY_BUILD_DOCS_DEFAULT OFF) set(GAMMARAY_DISABLE_FEEDBACK_DEFAULT ON) endif() gammaray_option(GAMMARAY_BUILD_DOCS "Build GammaRay documentation." ${GAMMARAY_BUILD_DOCS_DEFAULT}) gammaray_option( GAMMARAY_STATIC_PROBE "Build the probe as static library for compile-time injection." OFF ) gammaray_option( GAMMARAY_ENFORCE_QT_ASSERTS "Force QT_ASSERT in all builds." OFF ) gammaray_option(GAMMARAY_DISABLE_FEEDBACK "Disable user feedback support." ${GAMMARAY_DISABLE_FEEDBACK_DEFAULT}) # # Static probe setup # if(GAMMARAY_STATIC_PROBE) set(GAMMARAY_BUILD_UI OFF) set(GAMMARAY_PROBE_ONLY_BUILD ON) set(GAMMARAY_LIBRARY_TYPE STATIC) set(GAMMARAY_PLUGIN_TYPE STATIC) else() set(GAMMARAY_LIBRARY_TYPE SHARED) set(GAMMARAY_PLUGIN_TYPE MODULE) endif() # # Compiler & linker settings # function(append_if condition value) if(${condition}) foreach(variable ${ARGN}) set(${variable} "${${variable}} ${value}" PARENT_SCOPE) endforeach() endif() endfunction() macro(add_flag_if_supported flag name) check_c_compiler_flag("-Werror ${flag}" "C_SUPPORTS_${name}") append_if("C_SUPPORTS_${name}" "${flag}" CMAKE_C_FLAGS) check_cxx_compiler_flag("-Werror ${flag}" "CXX_SUPPORTS_${name}") append_if("CXX_SUPPORTS_${name}" "${flag}" CMAKE_CXX_FLAGS) endmacro() function(check_private_headers_exist module) message(STATUS "Checking whether private include directories for module ${module} exist") # backward compatibility with Qt < 5.9 if(TARGET Qt5::${module}Private) get_target_property(private_includes Qt5::${module}Private INTERFACE_INCLUDE_DIRECTORIES) set(Qt5${module}_PRIVATE_INCLUDE_DIRS "${private_includes}" PARENT_SCOPE) endif() # HACK to work around broken Qt cmake configurations in older Qt version (up to 5.7 at least) if(NOT "${Qt5${module}_PRIVATE_INCLUDE_DIRS}" MATCHES "/Qt${module}/") string(REPLACE "/QtCore" "/Qt${module}" replaceme "${Qt5Core_PRIVATE_INCLUDE_DIRS}") list(APPEND Qt5${module}_PRIVATE_INCLUDE_DIRS ${replaceme}) list(REMOVE_DUPLICATES Qt5${module}_PRIVATE_INCLUDE_DIRS) set(Qt5${module}_PRIVATE_INCLUDE_DIRS ${Qt5${module}_PRIVATE_INCLUDE_DIRS} PARENT_SCOPE) endif() foreach(dir ${Qt5${module}_PRIVATE_INCLUDE_DIRS}) if(NOT EXISTS "${dir}") message(FATAL_ERROR "The private include directory ${dir} for module ${module} do not exist! Please make sure your Qt5 installation contains private headers.\nThe required directories:\n ${Qt5${module}_PRIVATE_INCLUDE_DIRS}") endif() endforeach() endfunction() function(check_private_header_exists module file) set(found FALSE) foreach(dir ${Qt5${module}_PRIVATE_INCLUDE_DIRS}) if(EXISTS "${dir}/${file}") set(found TRUE) endif() endforeach() if(NOT found) message(FATAL_ERROR "The private include file ${file} was not found in directory ${Qt5${module}_PRIVATE_INCLUDE_DIRS}! Please make sure your Qt5 installation contains private headers.") endif() endfunction() set(CMAKE_CXX_VISIBILITY_PRESET hidden) set(CMAKE_VISIBILITY_INLINES_HIDDEN ON) if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") add_flag_if_supported(-Wunused-but-set-variable UNUSED_BUT_SET) add_flag_if_supported(-Wlogical-op LOGICAL_OP) add_flag_if_supported(-Wsizeof-pointer-memaccess POINTER_MEMACCESS) add_flag_if_supported(-Wreorder REORDER) add_flag_if_supported(-Wformat-security FORMAT_SECURITY) check_cxx_compiler_flag(-std=gnu++0x HAVE_GXX_GNUXX11) check_cxx_compiler_flag(-std=c++0x HAVE_GXX_CXX11) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic -Woverloaded-virtual -Winit-self -Wmissing-include-dirs -Wunused -Wundef -Wpointer-arith -Wmissing-noreturn -Werror=return-type -Wswitch") if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") # -Wgnu-zero-variadic-macro-arguments (part of -pedantic) is triggered by every qCDebug() call and therefore results # in a lot of noise. This warning is only notifying us that clang is emulating the GCC behaviour # instead of the exact standard wording so we can safely ignore it set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-gnu-zero-variadic-macro-arguments") endif() if(HAVE_GXX_GNUXX11) # QNX needs gnu++0x rather than c++0x for compiling QML V4 private headers set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++0x") elseif(HAVE_GXX_CXX11) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x") endif() if(MINGW) # mingw will error out on the crazy casts in probe.cpp without this set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fpermissive") endif() endif() if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Qunused-arguments -Wdocumentation") endif() if(CMAKE_C_COMPILER_ID MATCHES "Clang") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Qunused-arguments -Wdocumentation") endif() # Do not treat the operator name keywords and, bitand, bitor, compl, not, or and xor as synonyms as keywords. # They're not supported under Visual Studio out of the box thus using them limits the portability of code if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_C_COMPILER_ID MATCHES "Clang" OR (CMAKE_C_COMPILER_ID STREQUAL "Intel" AND NOT WIN32)) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-operator-names") endif() if(MSVC) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4244") #conversion from __int64 to int possible loss of data set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4267") #conversion from size_t to int possible loss of data endif() if(WIN32) add_definitions(-DUNICODE -D_UNICODE -D_USING_V110_SDK71_=1) endif() if(QNXNTO) add_definitions(-D_QNX_SOURCE) endif() if(CYGWIN) add_definitions(-D_GNU_SOURCE) endif() # linker flags if((CMAKE_SYSTEM_NAME MATCHES Linux OR CMAKE_SYSTEM_NAME STREQUAL GNU) AND (CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")) set(_linker_flags "-Wl,-z,origin") if(NOT ECM_ENABLE_SANITIZERS) set(_linker_flags "-Wl,--fatal-warnings -Wl,--no-undefined -lc ${_linker_flags}") endif() set(CMAKE_SHARED_LINKER_FLAGS "${_linker_flags} ${CMAKE_SHARED_LINKER_FLAGS}") set(CMAKE_MODULE_LINKER_FLAGS "${_linker_flags} ${CMAKE_MODULE_LINKER_FLAGS}") set(CMAKE_EXE_LINKER_FLAGS "${_linker_flags} ${CMAKE_EXE_LINKER_FLAGS}") endif() if(GAMMARAY_ENFORCE_QT_ASSERTS) add_definitions(-DQT_FORCE_ASSERTS) endif() # # Finding Qt # find_package(Qt5Core 5.5 NO_MODULE REQUIRED) set_package_properties(Qt5Core PROPERTIES TYPE REQUIRED) find_package(Qt5 NO_MODULE REQUIRED COMPONENTS Gui Network) find_package(Qt5 NO_MODULE QUIET OPTIONAL_COMPONENTS 3DAnimation 3DExtras 3DInput 3DLogic 3DRender 3DQuick Bluetooth Concurrent Designer Location Positioning PrintSupport Qml Quick QuickWidgets Svg Script ScriptTools Test WebEngineWidgets Widgets WaylandCompositor ) set(MACOS_DISABLE_UNSUPPORTED_TESTS FALSE) if(APPLE) cmake_host_system_information(RESULT MACOS_RELEASE QUERY OS_RELEASE) message(STATUS "MACOS_RELEASE: ${MACOS_RELEASE}") message(STATUS "Qt5Quick_VERSION: ${Qt5Quick_VERSION}") if(MACOS_RELEASE VERSION_GREATER_EQUAL 10.15.0 AND Qt5Quick_VERSION VERSION_LESS 5.12.0) # Qt Quick is only supported on macOS Catalina using Qt 5.12 and above set(MACOS_DISABLE_UNSUPPORTED_TESTS TRUE) message(STATUS "WARNING: Your version of MacOS is too old (less then 10.15) to use with QtQuick 5.12 or higher. You can solve this by upgrading your MacOS or using older Qt versions. Disabling QtQuick capabilities.") endif() endif() if(MACOS_DISABLE_UNSUPPORTED_TESTS) set(Qt5Quick_FOUND FALSE) endif() # Find these 'exotic' Qt modules without using find_package(... COMPONENTS) # so we can retrieve those packages even when installed into a different prefix find_package(Qt5IviCore 1.1 NO_MODULE QUIET) # 1.1 is based on 5.8 find_package(Qt5IviVehicleFunctions 1.1 NO_MODULE QUIET) find_package(Qt5IviMedia 1.1 NO_MODULE QUIET) find_package(Qt5Scxml 5.8 NO_MODULE QUIET) if(CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND Qt5Core_VERSION VERSION_EQUAL 5.9.1) # hide -Wc++1z-extensions warnings in this Qt version -- see: https://bugreports.qt.io/browse/QTBUG-61840 add_flag_if_supported(-Wno-c++1z-extensions NO_CPP1Z_EXTENSIONS) endif() if(Qt5Core_VERSION VERSION_GREATER 5.7) # Qt 5.8 is the first minor version to use nullptr more broadly # With versions below Qt might lots of warnings for -Wzero-as-null-pointer-constant, e.g.: # .../examples/widget-layouting/ui_contactform.h:606:85: warning: zero as null pointer constant [-Wzero-as-null-pointer-constant] add_flag_if_supported(-Wzero-as-null-pointer-constant ZERO_AS_NULL_POINTER_CONSTANT) endif() # if translation/doc host tools are missing, the Qt5 cmake config files throw errors... if(GAMMARAY_BUILD_DOCS) find_package(Qt5 NO_MODULE QUIET OPTIONAL_COMPONENTS Help LinguistTools) endif() if(GAMMARAY_BUILD_UI) # widgets are required for the UI find_package(Qt5 NO_MODULE REQUIRED COMPONENTS Widgets) # 3D Widget inspector requires Qt3D 5.7 if(Qt5Core_VERSION VERSION_EQUAL 5.7 OR Qt5Core_VERSION VERSION_GREATER 5.7) if(${Qt53DRender_FOUND} AND ${Qt53DInput_FOUND} AND ${Qt53DLogic_FOUND} AND ${Qt53DQuick_FOUND}) set(GAMMARAY_WITH_WIDGET3D TRUE) else() message(STATUS "Not building GammaRay Widget 3D") endif() endif() endif() # Sanity checking, we need private includes for the following modules, and < Qt 5.9 backward compatibility check_private_headers_exist(Core) check_private_header_exists(Core "private/qobject_p.h") check_private_headers_exist(Gui) list(APPEND Qt5Gui_PRIVATE_INCLUDE_DIRS ${Qt5Core_PRIVATE_INCLUDE_DIRS}) if(Qt5Widgets_FOUND) check_private_headers_exist(Widgets) list(APPEND Qt5Widgets_PRIVATE_INCLUDE_DIRS ${Qt5Gui_PRIVATE_INCLUDE_DIRS}) endif() if(Qt5Qml_FOUND) check_private_headers_exist(Qml) check_private_header_exists(Qml "private/qqmlabstractbinding_p.h") list(APPEND Qt5Qml_PRIVATE_INCLUDE_DIRS ${Qt5Gui_PRIVATE_INCLUDE_DIRS}) endif() if(Qt5Quick_FOUND) check_private_headers_exist(Quick) check_private_header_exists(Quick "private/qquickitem_p.h") list(APPEND Qt5Quick_PRIVATE_INCLUDE_DIRS ${Qt5Qml_PRIVATE_INCLUDE_DIRS}) endif() if(Qt5Scxml_FOUND) check_private_headers_exist(Scxml) endif() set(HAVE_QT_CONCURRENT ${Qt5Concurrent_FOUND}) set(HAVE_QT_WIDGETS ${Qt5Widgets_FOUND}) set(HAVE_QT_SVG ${Qt5Svg_FOUND}) set(HAVE_QT_SCXML ${Qt5Scxml_FOUND}) set(HAVE_QT_DESIGNER ${Qt5Designer_FOUND}) if(Qt5Gui_FOUND) cmake_push_check_state() set(CMAKE_REQUIRED_LIBRARIES Qt5::Gui) check_cxx_symbol_exists(QT_NO_OPENGL "QOpenGLContext" QT_NO_OPENGL) cmake_pop_check_state() endif() if(Qt5PrintSupport_FOUND) cmake_push_check_state() set(CMAKE_REQUIRED_LIBRARIES Qt5::PrintSupport) check_cxx_symbol_exists(QT_NO_PRINTER "QPrinter" QT_NO_PRINTER) cmake_pop_check_state() if(NOT QT_NO_PRINTER) set(HAVE_QT_PRINTSUPPORT ON) endif() endif() add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x050500) #add_definitions(-DQT_DEPRECATED_WARNINGS) set_package_properties(Qt5 PROPERTIES URL "https://qt.io/") set_package_properties(Qt5Concurrent PROPERTIES TYPE RECOMMENDED PURPOSE "Required for the GammaRay launcher process list.") set_package_properties(Qt5Widget PROPERTIES TYPE RECOMMENDED PURPOSE "Required for the GammaRay client UI and widget-related tools.") set_package_properties(Qt5Svg PROPERTIES TYPE OPTIONAL PURPOSE "Required for widget SVG export.") set_package_properties(Qt5PrintSupport PROPERTIES TYPE OPTIONAL PURPOSE "Required for widget PDF export.") # debug suffixes for qmake compatibility if(WIN32) set(CMAKE_DEBUG_POSTFIX "d") elseif(APPLE) set(CMAKE_DEBUG_POSTFIX "_debug") endif() string(TOUPPER "${CMAKE_BUILD_TYPE}" UPPERCASE_CMAKE_BUILD_TYPE) if(NOT UPPERCASE_CMAKE_BUILD_TYPE MATCHES "^REL") set(GAMMARAY_PROBE_ABI_POSTFIX "${CMAKE_DEBUG_POSTFIX}") endif() add_definitions( -DQT_USE_FAST_CONCATENATION -DQT_USE_FAST_OPERATOR_PLUS -DQT_NO_CAST_TO_ASCII -DQT_NO_URL_CAST_FROM_STRING ) if(NOT WIN32) # BIC on MSVC at least (see e.g. https://bugreports.qt.io/browse/AUTOSUITE-946) add_definitions(-DQT_STRICT_ITERATORS) endif() if(UPPERCASE_CMAKE_BUILD_TYPE MATCHES "^RELEASE$") add_definitions(-DQT_NO_DEBUG_OUTPUT) endif() if(HAVE_QT_WIDGETS) gammaray_option(GAMMARAY_CORE_ONLY_LAUNCHER "Only use QtCore in the CLI launcher (breaks style injector, but is needed for Boot2Qt compatibility)" FALSE) else() set(GAMMARAY_CORE_ONLY_LAUNCHER TRUE) endif() add_feature_info("QtScript debugger" Qt5ScriptTools_FOUND "Requires QtScript and QtScriptTools.") add_feature_info("Widget .ui file export" HAVE_QT_DESIGNER "Requires QtDesigner library.") add_feature_info("3D Widget Inspector" GAMMARAY_WITH_WIDGET3D "Requires Qt5 >= 5.7, Qt3D and QtQuick Controls") # # Additional dependencies # check_include_files(stdint.h HAVE_STDINT_H) check_include_files(unistd.h HAVE_UNISTD_H) check_symbol_exists(backtrace execinfo.h HAVE_BACKTRACE) check_cxx_symbol_exists(abi::__cxa_demangle cxxabi.h HAVE_CXA_DEMANGLE) # ELF header for ABI detection find_file(HAVE_ELF_H elf.h) find_file(HAVE_SYS_ELF_H sys/elf.h) if(HAVE_ELF_H OR HAVE_SYS_ELF_H) set(HAVE_ELF TRUE) endif() add_feature_info("ELF ABI detection" HAVE_ELF "Automatic probe ABI detection on ELF-based systems. Requires elf.h.") find_package(Glslang) set_package_properties(Glslang PROPERTIES URL "https://github.com/KhronosGroup/glslang" PURPOSE "Validate GL shader code.") find_package(QmlLint) set_package_properties(QmlLint PROPERTIES URL "https://qt.io" PURPOSE "Validate QML code.") find_package(KF5SyntaxHighlighting 5.28 NO_MODULE QUIET) set_package_properties(KF5SyntaxHighlighting PROPERTIES TYPE RECOMMENDED URL "https://www.kde.org/" PURPOSE "Syntax highlighting for code editor.") if(TARGET KF5::SyntaxHighlighting) set(HAVE_SYNTAX_HIGHLIGHTING TRUE) endif() # # Determine probe ABI # this needs to be run after we know exactly what we are building, but is needed for that installation settings include(GammaRayProbeABI) # # Installation settings # if(ANDROID) set(GAMMARAY_INSTALL_QT_LAYOUT ON) endif() if(APPLE) set(BUNDLE_APP_NAME "GammaRay.app") if(GAMMARAY_INSTALL_QT_LAYOUT) set(BUNDLE_INSTALL_DIR "bin") set(RESOURCES_INSTALL_DIR "${BUNDLE_INSTALL_DIR}/${BUNDLE_APP_NAME}/Contents/Resources") set(BIN_INSTALL_DIR "bin") set(INCLUDE_INSTALL_DIR "include/gammaray") set(LIB_INSTALL_DIR "lib") set(LIBEXEC_INSTALL_DIR "libexec") set(CMAKECONFIG_INSTALL_DIR "${LIB_INSTALL_DIR}/cmake/GammaRay") set(PLUGIN_INSTALL_DIR "${LIB_INSTALL_DIR}/gammaray") set(MAN_INSTALL_DIR "man/man1") set(DOC_INSTALL_DIR "doc/gammaray") set(QCH_INSTALL_DIR "doc") set(TRANSLATION_INSTALL_DIR "translations") else() # Make sure default prefix on mac is /Applications, dunnow why but it does not default to it # probably because we do not enabled built in bundle support in the main project string(COMPARE EQUAL "${CMAKE_INSTALL_PREFIX}" "/usr/local" CMP_RESULT) if(CMP_RESULT) set(CMAKE_INSTALL_PREFIX "/Applications") endif() set(BUNDLE_INSTALL_DIR ".") set(RESOURCES_INSTALL_DIR "${BUNDLE_INSTALL_DIR}/${BUNDLE_APP_NAME}/Contents/Resources") set(BIN_INSTALL_DIR "${BUNDLE_INSTALL_DIR}/${BUNDLE_APP_NAME}/Contents/MacOS") set(LIB_INSTALL_DIR "${BUNDLE_INSTALL_DIR}/${BUNDLE_APP_NAME}/Contents/Frameworks") set(PLUGIN_INSTALL_DIR "${BUNDLE_INSTALL_DIR}/${BUNDLE_APP_NAME}/Contents/PlugIns/gammaray") set(LIBEXEC_INSTALL_DIR "${BIN_INSTALL_DIR}") set(MAN_INSTALL_DIR "${RESOURCES_INSTALL_DIR}/man/man1") set(DOC_INSTALL_DIR "${RESOURCES_INSTALL_DIR}/docs") set(QCH_INSTALL_DIR "${RESOURCES_INSTALL_DIR}/docs") set(TRANSLATION_INSTALL_DIR "${RESOURCES_INSTALL_DIR}/translations") set(INCLUDE_INSTALL_DIR "${RESOURCES_INSTALL_DIR}/include/gammaray") set(CMAKECONFIG_INSTALL_DIR "${RESOURCES_INSTALL_DIR}/cmake/GammaRay") set(ECM_MKSPECS_INSTALL_DIR "${RESOURCES_INSTALL_DIR}/${ECM_MKSPECS_INSTALL_DIR}") endif() else() # Set installation paths # This takes care of installing into "lib64" on distros that use that, for instance, # by setting CMAKE_INSTALL_FULL_LIBDIR. include(GNUInstallDirs) set(BIN_INSTALL_DIR "${CMAKE_INSTALL_BINDIR}") # relative, usually "bin" gammaray_convert_to_relative_path(BIN_INSTALL_DIR) if(GAMMARAY_INSTALL_QT_LAYOUT) set(LIB_INSTALL_DIR "lib") # Qt always uses "lib" else() set(LIB_INSTALL_DIR "${CMAKE_INSTALL_LIBDIR}") # "lib" or "lib64" gammaray_convert_to_relative_path(LIB_INSTALL_DIR) endif() set(INCLUDE_INSTALL_DIR "${CMAKE_INSTALL_INCLUDEDIR}/gammaray") set(CMAKECONFIG_INSTALL_DIR ${LIB_INSTALL_DIR}/cmake/GammaRay) set(DATAROOTDIR "${CMAKE_INSTALL_DATAROOTDIR}" CACHE PATH "Define install directory for read-only architecture-independent data") gammaray_convert_to_relative_path(DATAROOTDIR) set(XDG_APPS_INSTALL_DIR "${DATAROOTDIR}/applications") set(APPDATA_INSTALL_DIR "${DATAROOTDIR}/metainfo") set(ICON_INSTALL_DIR "${DATAROOTDIR}/icons") set(MAN_INSTALL_DIR "${DATAROOTDIR}/man/man1") set(QCH_INSTALL_DIR "${CMAKE_INSTALL_DOCDIR}" CACHE PATH "Install location of Qt Assistant help files.") gammaray_convert_to_relative_path(QCH_INSTALL_DIR) if(WIN32) set(PLUGIN_INSTALL_DIR "plugins/gammaray") set(LIBEXEC_INSTALL_DIR "${BIN_INSTALL_DIR}") set(DOC_INSTALL_DIR .) set(TRANSLATION_INSTALL_DIR "translations") else() set(PLUGIN_INSTALL_DIR "${LIB_INSTALL_DIR}/gammaray") set(LIBEXEC_INSTALL_DIR "${LIB_INSTALL_DIR}/gammaray/libexec") set(DOC_INSTALL_DIR "${DATAROOTDIR}/doc/gammaray/") set(TRANSLATION_INSTALL_DIR "${DATAROOTDIR}/gammaray/translations") endif() endif() if(NOT GAMMARAY_INSTALL_QT_LAYOUT) set(PROBE_BASENAME "gammaray_probe") else() set(PROBE_BASENAME "${CMAKE_SHARED_LIBRARY_PREFIX}gammaray_probe") endif() if(GAMMARAY_INSTALL_QT_LAYOUT) if(WIN32) set(PROBE_INSTALL_DIR ${BIN_INSTALL_DIR}) else() set(PROBE_INSTALL_DIR ${LIB_INSTALL_DIR}) endif() set(PROBE_PLUGIN_INSTALL_DIR "plugins/gammaray") set(PLUGIN_INSTALL_DIR ${PROBE_PLUGIN_INSTALL_DIR}) set(TARGET_PLUGIN_INSTALL_DIR "plugins/gammaray-target") else() set(PROBE_INSTALL_DIR "${PLUGIN_INSTALL_DIR}/${GAMMARAY_PLUGIN_VERSION}/${GAMMARAY_PROBE_ABI}${GAMMARAY_PROBE_ABI_POSTFIX}") set(PROBE_PLUGIN_INSTALL_DIR ${PROBE_INSTALL_DIR}) set(TARGET_PLUGIN_INSTALL_DIR "${PROBE_INSTALL_DIR}/target") endif() if(NOT GAMMARAY_OUTPUT_PREFIX) # set by multibuild set(GAMMARAY_OUTPUT_PREFIX ${PROJECT_BINARY_DIR}) endif() set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${GAMMARAY_OUTPUT_PREFIX}/${BIN_INSTALL_DIR}) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${GAMMARAY_OUTPUT_PREFIX}/${LIB_INSTALL_DIR}) set( INSTALL_TARGETS_DEFAULT_ARGS RUNTIME DESTINATION ${BIN_INSTALL_DIR} LIBRARY DESTINATION ${LIB_INSTALL_DIR} ARCHIVE DESTINATION ${LIB_INSTALL_DIR} COMPONENT Devel BUNDLE DESTINATION ${BUNDLE_INSTALL_DIR} ) # "inverse" install dirs, to find the base location again if(APPLE) set(BUNDLE_INSTALL_DIR_PRIVATE "${BUNDLE_INSTALL_DIR}/${BUNDLE_APP_NAME}/Contents/MacOS") gammaray_inverse_dir(GAMMARAY_INVERSE_BUNDLE_DIR "${BUNDLE_INSTALL_DIR_PRIVATE}") endif() gammaray_inverse_dir(GAMMARAY_INVERSE_BIN_DIR "${BIN_INSTALL_DIR}") gammaray_inverse_dir(GAMMARAY_INVERSE_LIB_DIR "${LIB_INSTALL_DIR}") gammaray_inverse_dir(GAMMARAY_INVERSE_PROBE_DIR "${PROBE_INSTALL_DIR}") gammaray_inverse_dir(GAMMARAY_INVERSE_LIBEXEC_DIR "${LIBEXEC_INSTALL_DIR}") # # actually build the stuff # configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/config-gammaray.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-gammaray.h ) include_directories( ${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/3rdparty ${CMAKE_BINARY_DIR} ) include(QtInstallPaths) #to set QT_INSTALL_FOO variables add_subdirectory(cmake) add_subdirectory(3rdparty/kde) add_subdirectory(common) add_subdirectory(core) add_subdirectory(probe) add_subdirectory(launcher) if(GAMMARAY_BUILD_UI AND Qt5Widgets_FOUND) if(NOT GAMMARAY_DISABLE_FEEDBACK) add_subdirectory(3rdparty/kuserfeedback) endif() add_subdirectory(ui) if(NOT GAMMARAY_CLIENT_ONLY_BUILD) add_subdirectory(inprocessui) endif() if(NOT GAMMARAY_PROBE_ONLY_BUILD) add_subdirectory(client) add_subdirectory(app) endif() endif() if(BUILD_TESTING AND Qt5Test_FOUND AND NOT CMAKE_CROSSCOMPILING) add_subdirectory(tests) endif() add_subdirectory(plugins) if(Qt5LinguistTools_FOUND) add_subdirectory(translations) endif() if(GAMMARAY_MULTI_BUILD) add_subdirectory(multibuild) endif() if(GAMMARAY_BUILD_DOCS) add_subdirectory(examples) add_subdirectory(docs) # needs to go last, so see all installed headers for the API docs endif() set(LICENSE_FILE "LICENSE.GPL.txt") set(README_FILE "README.txt") list(APPEND DOCS ${LICENSE_FILE} ${README_FILE} "LICENSE.txt") if(NOT APPLE AND NOT GAMMARAY_PROBE_ONLY_BUILD) if(UNIX AND GAMMARAY_BUILD_UI) install(FILES GammaRay.desktop DESTINATION ${XDG_APPS_INSTALL_DIR}) install(FILES com.kdab.GammaRay.metainfo.xml DESTINATION ${APPDATA_INSTALL_DIR}) endif() install(FILES ${DOCS} DESTINATION ${DOC_INSTALL_DIR}) endif() # # cppcheck # find_program(CPPCHECK_EXECUTABLE cppcheck) if(CPPCHECK_EXECUTABLE) set(_cppcheck_flags "-I${CMAKE_CURRENT_BINARY_DIR}") get_directory_property(_inc_dirs INCLUDE_DIRECTORIES) foreach(_current ${_inc_dirs}) set(_cppcheck_flags ${_cppcheck_flags} "-I${_current}") endforeach() get_directory_property(_defs COMPILE_DEFINITIONS) foreach(_current ${_defs}) set(_cppcheck_flags ${_cppcheck_flags} "-D${_current}") endforeach() add_custom_target(cppcheck COMMAND ${CPPCHECK_EXECUTABLE} --enable=all --inconclusive -f --suppress=*:${QT_INCLUDE_DIR}* ${_cppcheck_flags} -i${CMAKE_CURRENT_SOURCE_DIR}/3rdparty -i${CMAKE_CURRENT_SOURCE_DIR}/tests ${CMAKE_CURRENT_SOURCE_DIR} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMENT "Running the cppcheck static code checker" ) endif() # # CMake package config file generation # if(NOT GAMMARAY_PROBE_ONLY_BUILD) include(CMakePackageConfigHelpers) configure_package_config_file( ${CMAKE_CURRENT_SOURCE_DIR}/GammaRayConfig.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/GammaRayConfig.cmake INSTALL_DESTINATION ${CMAKECONFIG_INSTALL_DIR} PATH_VARS INCLUDE_INSTALL_DIR ) write_basic_package_version_file( ${CMAKE_CURRENT_BINARY_DIR}/GammaRayConfigVersion.cmake VERSION ${GAMMARAY_VERSION} COMPATIBILITY SameMajorVersion ) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/GammaRayConfig.cmake ${CMAKE_CURRENT_BINARY_DIR}/GammaRayConfigVersion.cmake DESTINATION ${CMAKECONFIG_INSTALL_DIR} ) install( EXPORT GammaRayTargets DESTINATION "${CMAKECONFIG_INSTALL_DIR}" FILE GammaRayTarget.cmake # NAMESPACE GammaRay:: ) endif() #CPACK: General Settings set(CPACK_GENERATOR "TBZ2") set(CPACK_PACKAGE_NAME "gammaray") set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "An introspection tool for Qt applications") #TODO: shorten lines in the Readme.txt to make rpmlint happy set(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_SOURCE_DIR}/${README_FILE}") set(CPACK_PACKAGE_VENDOR "Klaralvdalens Datakonsult AB (KDAB)") set(CPACK_PACKAGE_CONTACT "gammaray-devel@kdab.com") set(CPACK_PACKAGE_VERSION_MAJOR "${GAMMARAY_VERSION_MAJOR}") set(CPACK_PACKAGE_VERSION_MINOR "${GAMMARAY_VERSION_MINOR}") set(CPACK_PACKAGE_VERSION_PATCH "${GAMMARAY_VERSION_PATCH}") set(CPACK_PACKAGE_VERSION "${GAMMARAY_VERSION}") #CPACK: RPM Specific Settings set(CPACK_RPM_PACKAGE_LICENSE "GPLv2+") set(CPACK_RPM_PACKAGE_GROUP "Development/Tools") #CPACK: DEB Specific Settings set(CPACK_DEBIAN_PACKAGE_SECTION "Development") set(CPACK_RESOURCE_FILE_README "${CMAKE_SOURCE_DIR}/${README_FILE}") if(WIN32) set(ICONS_DIR "${CMAKE_SOURCE_DIR}/resources") set(CPACK_GENERATOR "NSIS" "ZIP") set(CPACK_PACKAGE_EXECUTABLES "GammaRay" "GammaRay") set(CPACK_PACKAGE_INSTALL_DIRECTORY "GammaRay") set(CPACK_PACKAGE_FILE_NAME "GammaRay ${GAMMARAY_VERSION}") set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/${LICENSE_FILE}") set(CPACK_NSIS_EXECUTABLES_DIRECTORY "${BIN_INSTALL_DIR}") set(CPACK_NSIS_MUI_ICON "${ICONS_DIR}/GammaRay.ico") #set(CPACK_PACKAGE_ICON "${ICONS_DIR}\\\\CharmNSISHeader.bmp") set(CPACK_NSIS_URL_INFO_ABOUT "https://www.kdab.com/") set(CPACK_NSIS_INSTALLED_ICON_NAME "GammaRay${CMAKE_EXECUTABLE_SUFFIX}") set(CPACK_NSIS_MENU_LINKS "${LICENSE_FILE}" "License" "${README_FILE}" "Readme" ) set(CPACK_NSIS_MUI_FINISHPAGE_RUN "${CPACK_NSIS_INSTALLED_ICON_NAME}") elseif(APPLE) set(CPACK_GENERATOR "DragNDrop") set(CPACK_DMG_FORMAT "UDBZ") set(CPACK_DMG_VOLUME_NAME "GammaRay") set(CPACK_SYSTEM_NAME "OSX") set(CPACK_PACKAGE_FILE_NAME "GammaRay-${GAMMARAY_VERSION}") set(CPACK_PACKAGE_ICON "${ICONS_DIR}/CharmDMG.icns") set(CPACK_DMG_DS_STORE "${ICONS_DIR}/CharmDSStore") set(CPACK_DMG_BACKGROUND_IMAGE "${ICONS_DIR}/CharmDMGBackground.png") elseif(UNIX) set(CPACK_SYSTEM_NAME "${CMAKE_SYSTEM_NAME}-${CMAKE_SYSTEM_PROCESSOR}") endif() include(CPack) include(PackageIFW) addPackageTarget(GammaRay gammaray_probe install lib) feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES) gammaray-2.11.3/CONTRIBUTORS.txt000066400000000000000000000003431413310443000160630ustar00rootroot00000000000000Alejandro Exojo Christian Gagneraud Gábor Angyal Jan Kundrát Peter Kuemmel CESNET, z.s.p.o. gammaray-2.11.3/GammaRay.desktop000066400000000000000000000006001413310443000164520ustar00rootroot00000000000000[Desktop Entry] Name=GammaRay Name[x-test]=xxGammaRayxx Comment=Qt-application inspection and manipulation tool Comment[x-test]=xxQt-application inspection and manipulation toolxx GenericName=Qt Inspection Tool GenericName[x-test]=xxQt Inspection Toolxx Exec=gammaray Icon=GammaRay Type=Application Categories=Qt;Development; Keywords=Qt;development;runtime; X-KDE-StartupNotify=true gammaray-2.11.3/GammaRayConfig.cmake.in000066400000000000000000000020251413310443000176170ustar00rootroot00000000000000@PACKAGE_INIT@ find_package(Qt5 @Qt5Core_VERSION_MAJOR@.@Qt5Core_VERSION_MINOR@ NO_MODULE REQUIRED COMPONENTS Core Network) find_package(Qt5 @Qt5Core_VERSION_MAJOR@.@Qt5Core_VERSION_MINOR@ NO_MODULE COMPONENTS Widgets) set_and_check(GammaRay_INCLUDE_DIRS "@PACKAGE_INCLUDE_INSTALL_DIR@") list(APPEND GammaRay_INCLUDE_DIRS "@PACKAGE_INCLUDE_INSTALL_DIR@/..") set(GAMMARAY_PLUGIN_INSTALL_DIR @PLUGIN_INSTALL_DIR@) set(GAMMARAY_PLUGIN_VERSION @GAMMARAY_PLUGIN_VERSION@) set(GAMMARAY_PROBE_ABI @GAMMARAY_PROBE_ABI@) set(GAMMARAY_PROBE_PLUGIN_INSTALL_DIR @PROBE_PLUGIN_INSTALL_DIR@) set(GAMMARAY_BUILD_UI @GAMMARAY_BUILD_UI@) set(GAMMARAY_PROBE_ONLY_BUILD @GAMMARAY_PROBE_ONLY_BUILD@) set(GAMMARAY_STATIC_PROBE @GAMMARAY_STATIC_PROBE@) set(GAMMARAY_LIBRARY_TYPE @GAMMARAY_LIBRARY_TYPE@) set(GAMMARAY_PLUGIN_TYPE @GAMMARAY_PLUGIN_TYPE@) if(NOT GAMMARAY_OUTPUT_PREFIX) set(GAMMARAY_OUTPUT_PREFIX ${PROJECT_BINARY_DIR}) endif() include("${CMAKE_CURRENT_LIST_DIR}/GammaRayTarget.cmake") include("${CMAKE_CURRENT_LIST_DIR}/GammaRayMacros.cmake") gammaray-2.11.3/Install.txt000066400000000000000000000116321413310443000155370ustar00rootroot00000000000000GammaRay uses the CMake buildsystem. Please see the comments at the top of CMakeLists.txt for the available configuration options you can pass to cmake. The installation directory defaults to /usr/local on UNIX c:/Program Files on Windows and /Applications on MacOS. You can change this location by passing the option -DCMAKE_INSTALL_PREFIX=/install/path to cmake. To build a debug version pass -DCMAKE_BUILD_TYPE=Debug to cmake. To build GammaRay you will need *at least*: - CMake 3.4.0 - a C++ compiler with C++11 support - Qt 5.5 or higher Optional FOSS packages (eg. KDSME, etc) provide extra functionality. See the "Optional Dependencies" section below for more details. Building on Unix with gcc or clang: % mkdir build % cd build % cmake .. % make % make install Building on Windows with Microsoft Visual Studio: From a command prompt for the version of MSVC you want to use % mkdir build % cd build % cmake -G "NMake Makefiles" .. % nmake % nmake install Building on Windows with MinGW: Make sure you have the path to the MinGW programs in %PATH% first, for example: % set "PATH=c:\MinGW\mingw64\bin;%PATH%" Now build: % mkdir build % cd build % cmake -G "MinGW Makefiles" .. % mingw32-make % mingw32-make install Build on Android: $ mkdir android-build $ cd android-build $ export ANDROID_NDK=/path/to/android-ndk $ cmake -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake \ -DCMAKE_FIND_ROOT_PATH=/android/qt5/install/path \ -DCMAKE_INSTALL_PREFIX=/install/path .. $ make [-j CPU_NUMBER+2] $ make install For more information about building CMake projects on Android see https://developer.android.com/ndk/guides/cmake Using GammaRay on Android: - add GammaRay probe to your android .pro file myproject.pro .... android: QT += GammaRayProbe ... - build & deploy and run your project - forward GammaRay's socket $ adb forward tcp:11732 localfilesystem:/data/data/YOUR_ANDROID_PACKAGE_NAME(e.g. com.kdab.example)/files/+gammaray_socket - run GammaRay GUI and connect to localhost:11732 - after you've finished, remove the forward: $ adb forward --remove tcp:11732 or $ adb forward --remove-all to remove all forwards == Cross-compiling GammaRay == You'll find more information on this in the wiki: https://github.com/KDAB/GammaRay/wiki/Cross-compiling-GammaRay == Force a probe only build == If you already built GammaRay in the past and that you want to support more probes, you don't need to rebuild entirely GammaRay for this specific Qt version. You can instead just build the GammaRay probe for the new Qt version and install it in you previous GammaRay installation. You can make probe only builds that way: % cmake \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_PREFIX_PATH=/path/to/Qt/version/ \ -DGAMMARAY_PROBE_ONLY_BUILD=true \ -DGAMMARAY_BUILD_UI=false \ -DCMAKE_INSTALL_PREFIX=/path/to/your/previous/gammaray/prefix \ /path/to/gammaray/sources You can still use any cmake flags you want like CMAKE_DISABLE_FIND_PACKAGE_ etc. == Optional Dependencies == GammaRay relies on optional (FOSS) dependencies to help provide some of its functionality, most prominently KDSME (https://github.com/KDAB/KDStateMachineEditor). When you run cmake it will inform you about these missing dependencies. You can also force CMake to ignore any or all of the optional dependencies by passing the option -DCMAKE_DISABLE_FIND_PACKAGE_=True. For instance: # tell cmake to ignore VTK % cmake -DCMAKE_DISABLE_FIND_PACKAGE_VTK=True == RPATH Settings (Linux only) == By default GammaRay will be build with RPATHs set to the absolute install location of its dependencies (such as Qt). This is useful for easily using a self-built GammaRay, but it might not be what you want when building installers or packages. You can therefore change this via the following CMake options: - CMAKE_INSTALL_RPATH_USE_LINK_PATH=OFF will disable settings RPATH to the location of dependencies. It will however still set relative RPATHs between the various GammaRay components. This is typically desired for Linux distros, where GammaRay's dependencies are all in the default search path anyway. - CMAKE_INSTALL_RPATH= will add the specified absolute paths to RPATH, additionally to the relative RPATHs between GammaRay's components. == Warning! (Qt <= 5.4 only) == If your Qt is linked with the "-Bsymbolic-function" option preloading will be broken. When this is enabled, references to global functions will be bound to the shared object internally; therefore, the definition of the function will be fixed and cannot be overwritten by preloading. So, be sure that your distro-provided packages or your self-compiled packages are not linked with this flag (check with `echo $LDFLAGS` before compiling). For more info see: "man ld; search for "-Bsymbolic-function". Known affected distros: Ubuntu 10.10, 11.04 If you are affected by this, try the gdb injector instead by using the "-i gdb" command line argument. gammaray-2.11.3/LICENSE.BSD2.txt000066400000000000000000000027011413310443000157010ustar00rootroot00000000000000BSD-2-Clause Some 3rdparty code included with this software is licensed as follows: StackWalker: Copyright (c) 2005 - 2019, Jochen Kalmbach All rights reserved. lz4: Copyright (c) 2011-2016, Yann Collet All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. gammaray-2.11.3/LICENSE.GPL.txt000066400000000000000000001507571413310443000156500ustar00rootroot00000000000000 The GammaRay software is Copyright (C) 2010-2021 Klaralvdalens Datakonsult AB. You may use, distribute and copy the GammaRay software under the terms of the GNU General Public License version 2 or under the terms of GNU General Public License version 3 both of which are displayed below. ------------------------------------------------------------------------- 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. ------------------------------------------------------------------------- GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. 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 them 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 prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. 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. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey 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; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If 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 convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU 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 that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. 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. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 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. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. 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 state 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 3 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, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program 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, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU 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. But first, please read . ------------------------------------------------------------------------- gammaray-2.11.3/LICENSE.txt000066400000000000000000000115651413310443000152200ustar00rootroot00000000000000GammaRay END-USER LICENSE AGREEMENT (EULA) FOR COMMERCIAL VERSIONS November 1, 2019 IMPORTANT-READ CAREFULLY: This Klarälvdalens Datakonsult AB End-User License Agreement ("EULA") is a legal agreement between you (either an individual or a legal entity) and Klarälvdalens Datakonsult AB ("KDAB") for the Klarälvdalens Datakonsult AB software product “GammaRay” ("Licensed Product"). By installing, copying, or downloading the Licensed Product, you agree to be bound by the terms of this EULA. If you do not agree to the terms of this EULA, do not install, copy, or download the Licensed Product; you may, however, return it to your place of purchase for a full refund. Upon your acceptance of the terms and conditions of this EULA, KDAB grants you the right to use the Licensed Product as provided below. GRANT OF RIGHTS Under the condition of Your payment of all license fees KDAB grants to you a nonexclusive, nontransferable, royalty-bearing, perpetual and worldwide license to copy the Licensed Product for the purpose of designing, developing, and testing your software product(s) ("Applications"). If you are an individual, you may install copies of the Licensed Product on all of your computers provided that you are the only individual using the Licensed Product. If you are a legal entity, you may provide as many staff members ("Designated User") with copies of the Licensed Product as you have licensed. Any Designated User is permitted to install copies of the Licensed Product on all of the Designated User’s computers provided that the respective Designated User is the only individual using the Licensed Product. If you are a legal entity, You may at any time designate another individual to replace the current Designated User, so long as there is no more than one Designated User per license at any given time. To the extent that the Licensed Product includes software code ("Included Code") in your Applications for the intended purpose of designing, developing, and testing your Applications you are permitted to create a derivative or combined work consisting of your Applications and the Included Code and to use the derivative or combined work internally for the intended purpose. However, KDAB does not grant the right to redistribute the combination of your Application with the Included Code to third parties without prior written consent of KDAB. Nothing in this License is intended to reduce, limit, or restrict your rights arising from limitations or exceptions that are provided for in connection with the copyright protection under copyright law or other applicable laws. WARRANTY DISCLAIMER THE LICENSED PRODUCT IS LICENSED TO YOU "AS IS". TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, KDAB ON BEHALF OF ITSELF AND ITS SUPPLIERS, DISCLAIMS ALL WARRANTIES AND CONDITIONS, EITHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT WITH REGARD TO THE LICENSED PRODUCT. THIS WARRANTY DISCLAIMER NOTWITHSTANDING, YOU MAY HAVE SPECIFIC LEGAL RIGHTS WHICH MAY VARY FROM STATE/JURISDICTION TO STATE/JURISDICTION. LIMITATION OF LIABILITY IF, KDAB'S WARRANTY DISCLAIMER NOTWITHSTANDING, KDAB IS HELD LIABLE TO YOU, WHETHER IN CONTRACT, TORT OR ANY OTHER LEGAL THEORY, BASED ON THE LICENSED PRODUCT, KDAB'S ENTIRE LIABILITY TO YOU AND YOUR EXCLUSIVE REMEDY SHALL BE, AT KDAB'S OPTION, EITHER (A) RETURN OF THE PRICE YOU PAID FOR THE LICENSED PRODUCT, OR (B) REPAIR OR REPLACEMENT OF THE LICENSED PRODUCT, PROVIDED YOU RETURN TO KDAB ALL COPIES OF THE LICENSED PRODUCT AS ORIGINALLY DELIVERED TO YOU. KDAB SHALL NOT UNDER ANY CIRCUMSTANCES BE LIABLE TO YOU BASED ON FAILURE OF THE LICENSED PRODUCT IF THE FAILURE RESULTED FROM ACCIDENT, ABUSE OR MISAPPLICATION, NOR SHALL KDAB UNDER ANY CIRCUMSTANCES BE LIABLE FOR SPECIAL DAMAGES, PUNITIVE OR EXEMPLARY DAMAGES, DAMAGES FOR LOSS OF PROFITS OR INTERRUPTION OF BUSINESS OR FOR LOSS OR CORRUPTION OF DATA. ANY AWARD OF DAMAGES FROM KDAB TO YOU SHALL NOT EXCEED THE TOTAL AMOUNT YOU HAVE PAID TO KDAB IN CONNECTION WITH THIS EULA. SUPPORT AND UPDATES You will receive software developer support and access to Updates to the Licensed Product in accordance with KDAB support policies and procedures as agreed upon in your support agreement with KDAB. GENERAL PROVISIONS This EULA may only be modified in writing signed by you and an authorized officer of KDAB. All terms of any purchase order or other ordering document shall be superseded by this EULA. If any provision of the EULA is found void or unenforceable, the remainder will remain valid and enforceable according to its terms. If any remedy provided is determined to have failed for its essential purpose, all limitations of liability and exclusions of damages set forth in this EULA shall remain in effect. This EULA shall be construed, interpreted and governed by the laws of Sweden, the venue to be Sunne Tingsratt. gammaray-2.11.3/README.md000077700000000000000000000000001413310443000163352README.txtustar00rootroot00000000000000gammaray-2.11.3/README.txt000066400000000000000000000117671413310443000150770ustar00rootroot00000000000000# [![Logo](ui/resources/gammaray/ui/light/pixmaps/gammaray-trademark.png)](https://www.kdab.com/gammaray) Introduction [![Packaging status](https://repology.org/badge/tiny-repos/gammaray.svg)](https://repology.org/metapackage/gammaray) [![Windows Build status](https://ci.appveyor.com/api/projects/status/bff5abu59vm3i73x/branch/master?svg=true)](https://ci.appveyor.com/project/KDAB/gammaray/branch/master) ======= GammaRay is a software introspection tool for Qt applications developed by KDAB. Leveraging the QObject introspection mechanism it allows you to observe and manipulate your application at runtime. This works both locally on your workstation and remotely on an embedded target. Augmenting your instruction-level debugger, GammaRay allows you to work on a much higher level, with the same concepts as the frameworks you use. This is especially useful for the more complex Qt frameworks such as model/view, state machines or scene graphs. Among other things GammaRay can: * Browse the QObject tree with live updates. * View and edit object properties. * View and invoke slots of a QObject, and monitor its signals. * List all QObject inbound and outbound signal/slot connections. * Provide a layout information overlay for QWidget and QtQuick2 applications. * Inspect all QPainter operations used to draw a specific widget. * Browse the QtQuick2 item tree and scenegraph. * Inspect shaders and geometry data of QtQuick2 items. * Plot object lifetime and emitted signals. * Browse the QAbstractProxyModel hierarchy and inspect intermediate results in a proxy model chain. * Visual live inspection of QStateMachines. * Browse the item tree of any QGraphicsView scene. * Show a live preview of QGraphicsView items, including showing their coordinate system, transformation origin, rotate/zoom/pan, etc. * Intercept translations and change them at runtime. * Inspect all building blocks of a QStyle. * Show all QTimers and their statistics (number of wakeups, wakeup time, ...) * Browse all QTextDocuments, along with the ability to edit them and view their internal structures. * Act as a complete java script debugger, attachable to any QScriptEngine (including the usually not accessible one used by QtQuick1 internally). * Perform HTML/CSS/DOM/JS introspection/editing/profiling on any QWebPage, thanks to QWebInspector. * Browse the QResource tree and its content. * Show all registered meta types. * Show all installed fonts. * Show all available codecs. Contact ======= * See our official home page: https://www.kdab.com/gammaray * Visit us on GitHub: https://github.com/KDAB/GammaRay * Email info@kdab.com for questions about copyright, licensing or commercial support. Stay up-to-date with KDAB product announcements: * [KDAB Newsletter](https://news.kdab.com) * [KDAB Blogs](https://www.kdab.com/category/blogs) * [KDAB on Twitter](https://twitter.com/KDABQt) Get Involved ============ If you want to contribute, please check out: https://github.com/KDAB/GammaRay/wiki/Get-Involved KDAB will happily accept external contributions, but substantial contributions require a signed [Copyright Assignment Agreement](docs/GammaRay-CopyrightAssignmentForm.pdf). Contact info@kdab.com for more information about the Copyright Assignment Agreement. Please submit your contributions or issue reports from our GitHub space at https://github.com/KDAB/GammaRay Thanks to our [contributors](CONTRIBUTORS.txt). License ======= The GammaRay Software is (C) 2010-2021 Klarälvdalens Datakonsult AB (KDAB), and is available under the terms of the GPL version 2 (or any later version, at your option). See [LICENSE.GPL.txt](LICENSE.GPL.txt) for license details. Commercial licensing terms are available in the included file [LICENSE.txt](LICENSE.txt). For terms of redistribution, refer to the corresponding license agreement. About KDAB ========== GammaRay is supported and maintained by Klarälvdalens Datakonsult AB (KDAB). The KDAB Group is the global No.1 software consultancy for Qt, C++ and OpenGL applications across desktop, embedded and mobile platforms. The KDAB Group provides consulting and mentoring for developing Qt applications from scratch and in porting from all popular and legacy frameworks to Qt. We continue to help develop parts of Qt and are one of the major contributors to the Qt Project. We can give advanced or standard trainings anywhere around the globe on Qt as well as C++, OpenGL, 3D and more. If you would like to have a custom plugin for GammaRay to visualize, profile or debug your own specific components or applications, get in touch with us via https://www.kdab.com/contact. KDAB engineers know how to write GammaRay plugins and can be contracted to help you get yours working and improve your development efficiency. GammaRay and the GammaRay logo are registered trademarks of Klarälvdalens Datakonsult AB in the European Union, the United States and/or other countries. Other product and company names and logos may be trademarks or registered trademarks of their respective companies. gammaray-2.11.3/app/000077500000000000000000000000001413310443000141455ustar00rootroot00000000000000gammaray-2.11.3/app/CMakeLists.txt000066400000000000000000000037331413310443000167130ustar00rootroot00000000000000# Integrated launcher/client for Mac app bundles if(APPLE) if(NOT GAMMARAY_INSTALL_QT_LAYOUT) # CMake does not allow to have a bundle executable having a name different then the bundle folder name... # So we are disabling the bundle behavior here and generate a standard binary # The bundle structure will be auto created from the different INSTALL_XXX variables and we only manually generate # the Info.plist file set(gammaray_app_srcs main.cpp) add_executable(gammaray-app ${gammaray_app_srcs}) target_link_libraries(gammaray-app Qt5::Gui Qt5::Widgets gammaray_common gammaray_launcher gammaray_launcher_ui gammaray_client ) set_target_properties(gammaray-app PROPERTIES OUTPUT_NAME "GammaRayUI" INSTALL_RPATH "@executable_path/../Frameworks" ) # Let generate our Info.plist file... set(GAMMARAY_BUNDLE_INFO_PLIST_IN "${CMAKE_CURRENT_SOURCE_DIR}/Info.plist.in") set(GAMMARAY_BUNDLE_INFO_PLIST_OUT "${PROJECT_BINARY_DIR}/${BUNDLE_INSTALL_DIR}/${BUNDLE_APP_NAME}/Contents/Info.plist") set(GAMMARAY_BUNDLE_GUI_IDENTIFIER "com.kdab.gammaray") set(GAMMARAY_BUNDLE_EXECUTABLE "GammaRayUI") set(GAMMARAY_BUNDLE_BUNDLE_NAME "GammaRay") set(GAMMARAY_BUNDLE_ICON_FILE "GammaRay.icns") set(GAMMARAY_BUNDLE_BUNDLE_VERSION "${GAMMARAY_VERSION}") set(GAMMARAY_BUNDLE_SHORT_VERSION_STRING "${GAMMARAY_VERSION}") set(GAMMARAY_BUNDLE_LONG_VERSION_STRING "${GAMMARAY_VERSION_STRING}") set(GAMMARAY_BUNDLE_INFO_STRING "GammaRay ${GAMMARAY_VERSION}") set(GAMMARAY_BUNDLE_COPYRIGHT "Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com") configure_file("${GAMMARAY_BUNDLE_INFO_PLIST_IN}" "${GAMMARAY_BUNDLE_INFO_PLIST_OUT}") # Install stuff install(TARGETS gammaray-app ${INSTALL_TARGETS_DEFAULT_ARGS}) install(FILES "${GAMMARAY_BUNDLE_INFO_PLIST_OUT}" DESTINATION "${BUNDLE_INSTALL_DIR}/${BUNDLE_APP_NAME}/Contents") endif() endif() gammaray-2.11.3/app/Info.plist.in000066400000000000000000000026171413310443000165300ustar00rootroot00000000000000 NSPrincipalClass NSApplication CFBundleDevelopmentRegion English CFBundleExecutable ${GAMMARAY_BUNDLE_EXECUTABLE} CFBundleGetInfoString ${GAMMARAY_BUNDLE_INFO_STRING} CFBundleIconFile ${GAMMARAY_BUNDLE_ICON_FILE} CFBundleIdentifier ${GAMMARAY_BUNDLE_GUI_IDENTIFIER} CFBundleInfoDictionaryVersion 6.0 CFBundleLongVersionString ${GAMMARAY_BUNDLE_LONG_VERSION_STRING} CFBundleName ${GAMMARAY_BUNDLE_BUNDLE_NAME} CFBundlePackageType APPL CFBundleShortVersionString ${GAMMARAY_BUNDLE_SHORT_VERSION_STRING} CFBundleSignature ???? CFBundleVersion ${GAMMARAY_BUNDLE_BUNDLE_VERSION} CSResourcesFileMapped LSRequiresCarbon NSHumanReadableCopyright ${GAMMARAY_BUNDLE_COPYRIGHT} gammaray-2.11.3/app/main.cpp000066400000000000000000000104761413310443000156050ustar00rootroot00000000000000/* main.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include #include #include #include #include #include #include #include using namespace GammaRay; class InternalLauncher : public Launcher { Q_OBJECT public: explicit InternalLauncher(const LaunchOptions &options, QObject *parent = nullptr) : Launcher(options, parent) {} signals: void launchClient(const QUrl &serverAddress); protected: void startClient(const QUrl &serverAddress) override { emit launchClient(serverAddress); } }; class Orchestrator : public QObject { Q_OBJECT public: explicit Orchestrator(QObject *parent = nullptr) : QObject(parent) { m_launcherWindow = new LauncherWindow; // For some reason, Qt4 on OSX does not respect setQuitOnLastWindowClosed(false) m_launcherWindow->setAttribute(Qt::WA_QuitOnClose, false); connect(m_launcherWindow, SIGNAL(accepted()), this, SLOT(launcherWindowAccepted())); connect(m_launcherWindow, SIGNAL(rejected()), QCoreApplication::instance(), SLOT(quit())); m_launcherWindow->show(); } public slots: void launcherWindowAccepted() { Q_ASSERT(m_launcherWindow); Q_ASSERT(m_launcherWindow->result() == QDialog::Accepted); const LaunchOptions opts = m_launcherWindow->launchOptions(); if (!opts.isValid()) { QCoreApplication::exit(1); return; } m_launcher = new InternalLauncher(opts, this); connect(m_launcher, SIGNAL(launchClient(QUrl)), this, SLOT(startClient(QUrl))); connect(m_launcher, SIGNAL(finished()), this, SLOT(launcherFinished())); m_launcher->start(); } void startClient(const QUrl &serverAddress) { auto *conMan = new ClientConnectionManager(this); connect(conMan, SIGNAL(ready()), conMan, SLOT(createMainWindow())); connect(conMan, SIGNAL(disconnected()), QApplication::instance(), SLOT(quit())); connect(conMan, SIGNAL(persistentConnectionError(QString)), conMan, SLOT(handlePersistentConnectionError(QString))); conMan->connectToHost(serverAddress); } void launcherFinished() { m_launcher->deleteLater(); } private: QPointer m_launcherWindow; QPointer m_launcher; }; int main(int argc, char **argv) { QCoreApplication::setOrganizationName("KDAB"); QCoreApplication::setOrganizationDomain("kdab.com"); QCoreApplication::setApplicationName("GammaRay"); QCoreApplication::setApplicationVersion(GAMMARAY_COMPACT_VERSION_STRING); #if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0) QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); #endif QApplication::setQuitOnLastWindowClosed(false); QApplication app(argc, argv); Paths::setRelativeRootPath( #if defined(Q_OS_MACX) && defined(GAMMARAY_INSTALL_QT_LAYOUT) GAMMARAY_INVERSE_BUNDLE_DIR #else GAMMARAY_INVERSE_BIN_DIR #endif ); ClientConnectionManager::init(); Orchestrator o; return app.exec(); } #include "main.moc" gammaray-2.11.3/appveyor.ini000066400000000000000000000046271413310443000157440ustar00rootroot00000000000000[General] Branch = master ShallowClone = True # Variables defined here override the default value # The variable names are case sensitive [Variables] #Root = D:\qt-sdk #Values need to be overwritten to create a cache UseCache = True CreateCache = True Msys = C:\msys64\ APPVEYOR_BUILD_FOLDER = ${Variables:Root} # Settings applicable for all Crafts matrices # Settings are Category/key=value # Category is case sensitive [GeneralSettings] Paths/Python = C:\Python37-x64 Paths/Python27 = C:\Python27 Paths/Msys = ${Variables:Msys} Paths/DownloadDir = ${Variables:Root}/downloads ShortPath/Enabled = False ShortPath/EnableJunctions = True ShortPath/JunctionDir = ${Variables:Root}/csp Packager/Destination =${Variables:APPVEYOR_BUILD_FOLDER}/binaries Packager/UseCache = ${Variables:UseCache} Packager/CreateCache = ${Variables:CreateCache} Packager/CacheDir = ${Variables:APPVEYOR_BUILD_FOLDER}/cache Packager/RepositoryUrl = https://downloads.kdab.com/ci/cache/gammaray/cache ContinuousIntegration/RepositoryUrl = https://downloads.kdab.com/ci/cache/gammaray/binaries ContinuousIntegration/Enabled=True ContinuousIntegration/UpdateRepository = True Compile/BuildType = Release QtSDK/Version=5.12.5 QtSDK/Path=C:\Qt QtSDK/Enabled=True Blueprints/BlueprintRoot = ${Variables:Root}/blueprints # the version of the config Version/ConfigVersion = 6 [BlueprintSettings] /.buildTests = False qt-apps/gammaray.version = master qt-apps/charm.version = master libs/openssl.version = 1.0.2o libs/icu.ignored = True binary/mysql.ignored = True libs/dbus.ignored = True # since Qt 5.11 qttools (qtdoc) depend on clang, we don't need qtdoc libs/llvm-meta.ignored = True [windows-msvc2015_32-cl-BlueprintSettings] qt-apps/gammaray.gammarayProbeOnly = True [windows-msvc2015_32-cl] Packager/PackageType = SevenZipPackager QtSDK/Compiler = msvc2015 General/ABI = windows-msvc2015_32-cl [windows-msvc2017_32-cl-BlueprintSettings] qt-apps/gammaray.gammarayProbeOnly = True [windows-msvc2017_32-cl] Packager/PackageType = SevenZipPackager QtSDK/Compiler = msvc2017 General/ABI = windows-msvc2017_32-cl [windows-mingw_32-gcc-BlueprintSettings] qt-apps/gammaray.gammarayProbeOnly = True qt-apps/gammaray.disableGammarayBuildCliInjector = True [windows-mingw_32-gcc] Packager/PackageType = SevenZipPackager QtSDK/Compiler = mingw73_32 General/ABI = windows-mingw_32-gcc [windows-msvc2017_64-cl] QtSDK/Compiler = msvc2017_64 General/ABI = windows-msvc2017_64-cl gammaray-2.11.3/appveyor.yml000066400000000000000000000025751413310443000157660ustar00rootroot00000000000000version: '{build}-{branch}' image: Visual Studio 2017 branches: # whitelist only: - master clone_depth: 50 init: - ps: | function craft() { & "C:\Python37-x64\python.exe" "C:\CraftMaster\CraftMaster\CraftMaster.py" --config "$env:APPVEYOR_BUILD_FOLDER\appveyor.ini" --variables "APPVEYOR_BUILD_FOLDER=$env:APPVEYOR_BUILD_FOLDER" --target $env:TARGET -c $args if($LASTEXITCODE -ne 0) {exit $LASTEXITCODE} } install: - ps: | #use cmd to silence powershell behaviour for stderr & cmd /C "git clone -q --depth=1 git://anongit.kde.org/craftmaster.git C:\CraftMaster\CraftMaster 2>&1" craft craft craft --install-deps gammaray build_script: - ps: | craft --no-cache --src-dir $env:APPVEYOR_BUILD_FOLDER gammaray after_build: - ps: | if ($env:TARGET -eq "windows-msvc2017_64-cl") { craft --add-blueprint-repository [git]https://github.com/KDAB/craft-blueprints-kdab.git craft --no-cache gammaray-binary-probes craft nsis } craft --no-cache --src-dir $env:APPVEYOR_BUILD_FOLDER --package gammaray #test_script: #- ps: | #craft --no-cache --src-dir $env:APPVEYOR_BUILD_FOLDER --test gammaray test: off environment: matrix: - TARGET: windows-msvc2017_32-cl - TARGET: windows-msvc2017_64-cl artifacts: - path: cache\**\.* - path: binaries\* deploy: - provider: Environment name: GammaRay gammaray-2.11.3/client/000077500000000000000000000000001413310443000146435ustar00rootroot00000000000000gammaray-2.11.3/client/CMakeLists.txt000066400000000000000000000053471413310443000174140ustar00rootroot00000000000000set(gammaray_clientlib_srcs client.cpp remotemodel.cpp selectionmodelclient.cpp clientconnectionmanager.cpp propertycontrollerclient.cpp probecontrollerclient.cpp processtracker.cpp toolmanagerclient.cpp clientdevice.cpp tcpclientdevice.cpp localclientdevice.cpp messagestatisticsmodel.cpp paintanalyzerclient.cpp remoteviewclient.cpp enumrepositoryclient.cpp classesiconsrepositoryclient.cpp ${CMAKE_SOURCE_DIR}/resources/gammaray.qrc ) if (APPLE) list(APPEND gammaray_clientlib_srcs processtracker_macos.cpp) elseif(UNIX) list(APPEND gammaray_clientlib_srcs processtracker_linux.cpp) elseif(WIN32) list(APPEND gammaray_clientlib_srcs processtracker_windows.cpp) endif() add_library(gammaray_client SHARED ${gammaray_clientlib_srcs}) generate_export_header(gammaray_client) set_target_properties(gammaray_client PROPERTIES ${GAMMARAY_DEFAULT_LIBRARY_PROPERTIES} ) gammaray_set_rpath(gammaray_client ${LIB_INSTALL_DIR}) target_compile_features(gammaray_client PUBLIC ${GAMMARAY_REQUIRED_CXX_FEATURES}) gammaray_target_relocatable_interfaces(gammaray_client_ipaths) target_include_directories(gammaray_client PUBLIC $ $) target_link_libraries(gammaray_client LINK_PUBLIC Qt5::Core LINK_PRIVATE gammaray_ui gammaray_ui_internal gammaray_common Qt5::Gui Qt5::Widgets Qt5::Network ) set(gammaray_client_srcs main.cpp) gammaray_add_win_icon(gammaray_client_srcs) add_executable(gammaray-client WIN32 ${gammaray_client_srcs}) target_link_libraries(gammaray-client gammaray_client gammaray_common Qt5::Gui Qt5::Widgets ) gammaray_embed_info_plist(gammaray-client ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist.in) set_target_properties(gammaray-client PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/${LIBEXEC_INSTALL_DIR}" ) gammaray_set_rpath(gammaray-client ${LIBEXEC_INSTALL_DIR}) install(TARGETS gammaray_client EXPORT GammaRayTargets ${INSTALL_TARGETS_DEFAULT_ARGS}) gammaray_install_headers( ${CMAKE_CURRENT_BINARY_DIR}/gammaray_client_export.h clientconnectionmanager.h processtracker.h ) ecm_generate_pri_file(BASE_NAME GammaRayClient LIB_NAME gammaray_client DEPS "core gui widgets network GammaRayCommon GammaRayUi" FILENAME_VAR PRI_FILENAME INCLUDE_INSTALL_DIR ${INCLUDE_INSTALL_DIR} ) install(FILES ${PRI_FILENAME} DESTINATION ${ECM_MKSPECS_INSTALL_DIR}) if(MSVC) install(FILES "$/$" DESTINATION ${BIN_INSTALL_DIR} CONFIGURATIONS Debug RelWithDebInfo) endif() install(TARGETS gammaray-client DESTINATION ${LIBEXEC_INSTALL_DIR}) gammaray-2.11.3/client/Info.plist.in000066400000000000000000000007331413310443000172230ustar00rootroot00000000000000 NSPrincipalClass NSApplication CFBundleIdentifier com.kdab.GammaRay.client CFBundleInfoDictionaryVersion 6.0 CFBundleName GammaRay CFBundleVersion @GAMMARAY_VERSION_STRING@ CFBundleShortVersion @GAMMARAY_VERSION@ gammaray-2.11.3/client/classesiconsrepositoryclient.cpp000066400000000000000000000041621413310443000234020ustar00rootroot00000000000000/* classesiconsrepositoryclient.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Filipe Azevedo Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "classesiconsrepositoryclient.h" #include using namespace GammaRay; ClassesIconsRepositoryClient::ClassesIconsRepositoryClient(QObject *parent) : ClassesIconsRepository(parent) { connect(this, &ClassesIconsRepository::indexResponse, this, &ClassesIconsRepositoryClient::indexReceived); } ClassesIconsRepositoryClient::~ClassesIconsRepositoryClient() = default; QString ClassesIconsRepositoryClient::filePath(int id) const { const QString filePath(ClassesIconsRepository::filePath(id)); if (filePath.isEmpty() && !m_ready) { const_cast(this)->requestIndex(); } return filePath; } void ClassesIconsRepositoryClient::indexReceived(const QVector &index) { setIndex(index); emit indexChanged(); } void ClassesIconsRepositoryClient::requestIndex() { m_ready = true; Endpoint::instance()->invokeObject(qobject_interface_iid(), "requestIndex"); } gammaray-2.11.3/client/classesiconsrepositoryclient.h000066400000000000000000000034371413310443000230530ustar00rootroot00000000000000/* classesiconsrepositoryclient.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Filipe Azevedo Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_CLASSESICONSREPOSITORYCLIENT_H #define GAMMARAY_CLASSESICONSREPOSITORYCLIENT_H #include namespace GammaRay { class ClassesIconsRepositoryClient : public ClassesIconsRepository { Q_OBJECT Q_INTERFACES(GammaRay::ClassesIconsRepository) public: explicit ClassesIconsRepositoryClient(QObject *parent = nullptr); ~ClassesIconsRepositoryClient() override; QString filePath(int id) const override; protected slots: void indexReceived(const QVector &index); void requestIndex() override; private: bool m_ready = false; }; } #endif // GAMMARAY_CLASSESICONSREPOSITORYCLIENT_H gammaray-2.11.3/client/client.cpp000066400000000000000000000215671413310443000166400ustar00rootroot00000000000000/* client.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "client.h" #include "clientdevice.h" #include "messagestatisticsmodel.h" #include #include #include #include #include #include #include using namespace GammaRay; Client::Client(QObject *parent) : Endpoint(parent) , m_clientDevice(nullptr) , m_statModel(new MessageStatisticsModel(this)) , m_initState(0) { Message::resetNegotiatedDataVersion(); connect(this, &Endpoint::disconnected, this, &Client::socketDisconnected); m_propertySyncer->setRequestInitialSync(true); ObjectBroker::registerModelInternal(QStringLiteral( "com.kdab.GammaRay.MessageStatisticsModel"), m_statModel); } Client::~Client() = default; Client *Client::instance() { return static_cast(s_instance); } bool Client::isRemoteClient() const { return true; } QUrl Client::serverAddress() const { return m_serverAddress; } void Client::connectToHost(const QUrl &url, int tryAgain) { m_serverAddress = url; m_initState = 0; m_statModel->clear(); m_clientDevice = ClientDevice::create(m_serverAddress, this); if (!m_clientDevice) { emit persisitentConnectionError(tr("Unsupported transport protocol.")); return; } connect(m_clientDevice, &ClientDevice::connected, this, &Client::socketConnected); connect(m_clientDevice, &ClientDevice::transientError, this, &Client::transientConnectionError); connect(m_clientDevice, &ClientDevice::persistentError, this, &Client::persisitentConnectionError); connect(m_clientDevice, &ClientDevice::transientError, this, &Client::resetClientDevice); connect(m_clientDevice, &ClientDevice::persistentError, this, &Client::resetClientDevice); m_clientDevice->setTryAgain(tryAgain); m_clientDevice->connectToHost(); } void Client::disconnectFromHost() { if (m_clientDevice) { m_clientDevice->disconnectFromHost(); } } void Client::socketConnected() { Q_ASSERT(m_clientDevice->device()); setDevice(m_clientDevice->device()); } void Client::resetClientDevice() { if (m_clientDevice) { m_clientDevice->deleteLater(); m_clientDevice = nullptr; } } void Client::socketDisconnected() { foreach (const auto &objInfo, objectAddresses()) removeObjectNameAddressMapping(objInfo.second); ObjectBroker::clear(); resetClientDevice(); } void Client::messageReceived(const Message &msg) { m_statModel->addMessage(msg.address(), msg.type(), msg.size()); // server version must be the very first message we get if (!(m_initState & VersionChecked)) { if (msg.address() != endpointAddress() || msg.type() != Protocol::ServerVersion) { emit persisitentConnectionError(tr( "Protocol violation, first message is not the server version.")); disconnectFromHost(); } qint32 serverVersion; msg >> serverVersion; if (serverVersion != Protocol::version()) { emit persisitentConnectionError(tr("Gammaray Protocol Mismatch.\n" \ "Probe version is %1, was expecting %2.").arg( serverVersion).arg(Protocol::version())); disconnectFromHost(); } m_initState |= VersionChecked; return; } if (msg.address() == endpointAddress()) { switch (msg.type()) { case Protocol::ObjectAdded: { QString name; Protocol::ObjectAddress addr; msg >> name >> addr; addObjectNameAddressMapping(name, addr); m_statModel->addObject(addr, name); break; } case Protocol::ObjectRemoved: { QString name; msg >> name; removeObjectNameAddressMapping(name); break; } case Protocol::ObjectMapReply: { QVector > objects; msg >> objects; for (auto it = objects.constBegin(); it != objects.constEnd(); ++it) { if (it->first != endpointAddress()) addObjectNameAddressMapping(it->second, it->first); m_statModel->addObject(it->first, it->second); } m_propertySyncer->setAddress(objectAddress(QStringLiteral( "com.kdab.GammaRay.PropertySyncer"))); Q_ASSERT(m_propertySyncer->address() != Protocol::InvalidObjectAddress); Endpoint::registerMessageHandler( m_propertySyncer->address(), m_propertySyncer, "handleMessage"); m_initState |= ObjectMapReceived; break; } case Protocol::ServerInfo: { QString label; QString key; qint64 pid; quint8 dataVersion; msg >> label >> key >> pid >> dataVersion; setLabel(label); setKey(key); setPid(pid); { const quint8 version = qMin(dataVersion, Message::highestSupportedDataVersion()); Message msg(endpointAddress(), Protocol::ClientDataVersionNegotiated); msg << version; send(msg); } m_initState |= ServerInfoReceived; break; } case Protocol::ServerDataVersionNegotiated: { quint8 version; msg >> version; Message::setNegotiatedDataVersion(version); m_initState |= ServerDataVersionNegotiated; break; } default: qWarning() << Q_FUNC_INFO << "Got unhandled message:" << msg.type(); return; } if (m_initState == InitComplete) { m_initState |= ConnectionEstablished; emit connectionEstablished(); } } else { dispatchMessage(msg); } } Protocol::ObjectAddress Client::registerObject(const QString &name, QObject *object) { Q_ASSERT(isConnected()); Protocol::ObjectAddress address = Endpoint::registerObject(name, object); m_propertySyncer->addObject(address, object); m_propertySyncer->setObjectEnabled(address, true); monitorObject(address); return address; } void Client::registerMessageHandler(Protocol::ObjectAddress objectAddress, QObject *receiver, const char *messageHandlerName) { Q_ASSERT(isConnected()); Endpoint::registerMessageHandler(objectAddress, receiver, messageHandlerName); monitorObject(objectAddress); } void Client::unregisterMessageHandler(Protocol::ObjectAddress objectAddress) { Endpoint::unregisterMessageHandler(objectAddress); unmonitorObject(objectAddress); } void Client::objectDestroyed(Protocol::ObjectAddress objectAddress, const QString & /*objectName*/, QObject * /*object*/) { unmonitorObject(objectAddress); } void Client::handlerDestroyed(Protocol::ObjectAddress objectAddress, const QString & /*objectName*/) { unmonitorObject(objectAddress); } void Client::monitorObject(Protocol::ObjectAddress objectAddress) { if (!isConnected()) return; Message msg(endpointAddress(), Protocol::ObjectMonitored); msg << objectAddress; send(msg); } void Client::unmonitorObject(Protocol::ObjectAddress objectAddress) { if (!isConnected()) return; Message msg(endpointAddress(), Protocol::ObjectUnmonitored); msg << objectAddress; send(msg); } void Client::doSendMessage(const GammaRay::Message &msg) { m_statModel->addMessage(msg.address(), msg.type(), msg.size()); Endpoint::doSendMessage(msg); } gammaray-2.11.3/client/client.h000066400000000000000000000072101413310443000162720ustar00rootroot00000000000000/* client.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_CLIENT_H #define GAMMARAY_CLIENT_H #include #include #include namespace GammaRay { class ClientDevice; class MessageStatisticsModel; /** Client-side connection endpoint. */ class Client : public Endpoint { Q_OBJECT public: explicit Client(QObject *parent = nullptr); ~Client() override; /** Connect to a server reachable on @p url. */ void connectToHost(const QUrl &url, int tryAgain = 0); void disconnectFromHost(); /** * Register a client-side QObject to send/receive messages to/from the server side. */ Protocol::ObjectAddress registerObject(const QString &name, QObject *object) override; /** Singleton accessor. */ static Client *instance(); bool isRemoteClient() const override; QUrl serverAddress() const override; void registerMessageHandler(Protocol::ObjectAddress objectAddress, QObject *receiver, const char *messageHandlerName) override; void unregisterMessageHandler(Protocol::ObjectAddress objectAddress) override; signals: /** Emitted on transient connection errors. * That is, on errors it's worth re-trying, e.g. because the target wasn't up yet. */ void transientConnectionError(); /** Emitted on persistent connection errors. * That is, any error that is not a transient one. */ void persisitentConnectionError(const QString &msg); protected: void messageReceived(const Message &msg) override; void objectDestroyed(Protocol::ObjectAddress objectAddress, const QString &objectName, QObject *object) override; void handlerDestroyed(Protocol::ObjectAddress objectAddress, const QString &objectName) override; void doSendMessage(const GammaRay::Message &msg) override; private: void monitorObject(Protocol::ObjectAddress objectAddress); void unmonitorObject(Protocol::ObjectAddress objectAddress); private slots: void socketConnected(); void resetClientDevice(); void socketDisconnected(); private: enum InitState { VersionChecked = 0x1, ObjectMapReceived = 0x2, ServerInfoReceived = 0x4, ServerDataVersionNegotiated = 0x8, ConnectionEstablished = 0x10, InitComplete = VersionChecked | ObjectMapReceived | ServerInfoReceived | ServerDataVersionNegotiated }; QUrl m_serverAddress; ClientDevice *m_clientDevice; MessageStatisticsModel *m_statModel; int m_initState; }; } #endif // GAMMARAY_CLIENT_H gammaray-2.11.3/client/clientconnectionmanager.cpp000066400000000000000000000223111413310443000222370ustar00rootroot00000000000000/* clientconnectionmanager.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "clientconnectionmanager.h" #include "client.h" #include "enumrepositoryclient.h" #include "classesiconsrepositoryclient.h" #include "remotemodel.h" #include "selectionmodelclient.h" #include "propertycontrollerclient.h" #include "probecontrollerclient.h" #include "processtracker.h" #include "paintanalyzerclient.h" #include "remoteviewclient.h" #include #include #include #include #include #include #include #include #include using namespace GammaRay; static QAbstractItemModel *modelFactory(const QString &name) { return new RemoteModel(name, qApp); } static QItemSelectionModel *selectionModelFactory(QAbstractItemModel *model) { return new SelectionModelClient(model->objectName() + ".selection", model, qApp); } static QObject *createPropertyController(const QString &name, QObject *parent) { return new PropertyControllerClient(name, parent); } static QObject *createProbeController(const QString &name, QObject *parent) { QObject *o = new ProbeControllerClient(parent); ObjectBroker::registerObject(name, o); return o; } static QObject *createToolManager(const QString &name, QObject *parent) { QObject *o = new ToolManagerClient(parent); ObjectBroker::registerObject(name, o); return o; } static QObject *createPaintAnalyzerClient(const QString &name, QObject *parent) { return new PaintAnalyzerClient(name, parent); } static QObject *createRemoteViewClient(const QString &name, QObject *parent) { return new RemoteViewClient(name, parent); } static QObject *createEnumRepositoryClient(const QString &, QObject *parent) { return new EnumRepositoryClient(parent); } static QObject *createClassesIconsRepositoryClient(const QString &, QObject *parent) { return new ClassesIconsRepositoryClient(parent); } void ClientConnectionManager::init() { StreamOperators::registerOperators(); ObjectBroker::registerClientObjectFactoryCallback( createPropertyController); ObjectBroker::registerClientObjectFactoryCallback( createProbeController); ObjectBroker::registerClientObjectFactoryCallback(createToolManager); ObjectBroker::registerClientObjectFactoryCallback( createPaintAnalyzerClient); ObjectBroker::registerClientObjectFactoryCallback(createRemoteViewClient); ObjectBroker::registerClientObjectFactoryCallback(createEnumRepositoryClient); ObjectBroker::registerClientObjectFactoryCallback(createClassesIconsRepositoryClient); ObjectBroker::setModelFactoryCallback(modelFactory); ObjectBroker::setSelectionModelFactoryCallback(selectionModelFactory); } ClientConnectionManager::ClientConnectionManager(QObject *parent, bool showSplashScreenOnStartUp) : QObject(parent) , m_client(new Client(this)) , m_processTracker(new GammaRay::ProcessTracker(this)) , m_toolManager(new ClientToolManager(this)) , m_mainWindow(nullptr) , m_ignorePersistentError(false) , m_tries(0) { if (showSplashScreenOnStartUp) showSplashScreen(); connect(m_processTracker, &ProcessTracker::backendChanged, this, &ClientConnectionManager::processTrackerBackendChanged); connect(m_processTracker, &ProcessTracker::infoChanged, this, &ClientConnectionManager::processTrackerInfoChanged); connect(this, &ClientConnectionManager::ready, this, &ClientConnectionManager::clientConnected); connect(this, &ClientConnectionManager::disconnected, this, &ClientConnectionManager::clientDisconnected); connect(m_client, &Endpoint::disconnected, this, &ClientConnectionManager::disconnected); connect(m_client, &Client::transientConnectionError, this, &ClientConnectionManager::transientConnectionError); connect(m_client, &Client::persisitentConnectionError, this, &ClientConnectionManager::persistentConnectionError); connect(this, &ClientConnectionManager::persistentConnectionError, this, &ClientConnectionManager::delayedHideSplashScreen); connect(this, &ClientConnectionManager::ready, this, &ClientConnectionManager::delayedHideSplashScreen); connect(m_toolManager, &ClientToolManager::toolListAvailable, this, &ClientConnectionManager::ready); } ClientConnectionManager::~ClientConnectionManager() { delete m_mainWindow; } ClientToolManager *ClientConnectionManager::toolManager() const { return m_toolManager; } QMainWindow *ClientConnectionManager::mainWindow() const { return m_mainWindow; } void ClientConnectionManager::connectToHost(const QUrl &url, int tryAgain) { m_serverUrl = url; m_connectionTimeout.start(); m_tries = tryAgain; doConnectToHost(); } void ClientConnectionManager::showSplashScreen() { ::showSplashScreen(); } GammaRay::ProcessTrackerBackend *ClientConnectionManager::processTrackerBackend() const { return m_processTracker->backend(); } void ClientConnectionManager::setProcessTrackerBackend(GammaRay::ProcessTrackerBackend *backend) { m_processTracker->setBackend(backend); updateProcessTrackerState(); } qint64 ClientConnectionManager::processTrackerPid() const { return m_processTracker->pid(); } void ClientConnectionManager::setProcessTrackerPid(qint64 pid) { m_processTracker->setPid(pid); updateProcessTrackerState(); } QString ClientConnectionManager::endPointLabel() const { return m_client->label(); } QString ClientConnectionManager::endPointKey() const { return m_client->key(); } qint64 ClientConnectionManager::endPointPid() const { return m_client->pid(); } void ClientConnectionManager::disconnectFromHost() { targetQuitRequested(); m_client->disconnectFromHost(); } void ClientConnectionManager::doConnectToHost() { m_client->connectToHost(m_serverUrl, m_tries ? m_tries-- : 0); } QMainWindow *ClientConnectionManager::createMainWindow() { delete m_mainWindow; m_mainWindow = new MainWindow; m_mainWindow->setupFeedbackProvider(); connect(m_mainWindow.data(), &MainWindow::targetQuitRequested, this, &ClientConnectionManager::targetQuitRequested); m_ignorePersistentError = false; m_mainWindow->show(); return m_mainWindow; } void ClientConnectionManager::transientConnectionError() { if (m_connectionTimeout.elapsed() < 60 * 1000) { // client wasn't up yet, keep trying QTimer::singleShot(1000, this, &ClientConnectionManager::doConnectToHost); } else { emit persistentConnectionError(tr("Connection refused.")); } } void ClientConnectionManager::handlePersistentConnectionError(const QString &msg) { if (m_ignorePersistentError) return; QString errorMsg; if (m_mainWindow) errorMsg = tr("Lost connection to remote host: %1").arg(msg); else errorMsg = tr("Could not establish connection to remote host: %1").arg(msg); QMessageBox::critical(m_mainWindow, tr("GammaRay - Connection Error"), errorMsg); QApplication::exit(1); } void ClientConnectionManager::delayedHideSplashScreen() { QTimer::singleShot(0, this, &ClientConnectionManager::hideSplashScreen); } void ClientConnectionManager::hideSplashScreen() { ::hideSplashScreen(); } void ClientConnectionManager::targetQuitRequested() { m_ignorePersistentError = true; } void ClientConnectionManager::updateProcessTrackerState() { if (!m_client->isConnected()) { m_processTracker->stop(); } else if (m_processTracker->isActive()) { if (!m_processTracker->backend() || m_processTracker->pid() < 0) { m_processTracker->stop(); } } else { if (m_processTracker->backend() && m_processTracker->pid() >= 0) { m_processTracker->start(); } } } void ClientConnectionManager::clientConnected() { setProcessTrackerPid(m_client->pid()); } void ClientConnectionManager::clientDisconnected() { setProcessTrackerPid(-1); emit processTrackerInfoChanged(ProcessTrackerInfo()); } gammaray-2.11.3/client/clientconnectionmanager.h000066400000000000000000000107531413310443000217130ustar00rootroot00000000000000/* clientconnectionmanager.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_CLIENTCONNECTIONMANAGER_H #define GAMMARAY_CLIENTCONNECTIONMANAGER_H #include "gammaray_client_export.h" #include #include #include #include QT_BEGIN_NAMESPACE class QAbstractItemModel; class QMainWindow; QT_END_NAMESPACE namespace GammaRay { class Client; class MainWindow; class ProcessTracker; class ProcessTrackerBackend; class ProcessTrackerInfo; class ClientToolManager; /*! Pre-MainWindow connection setup logic. * * This is useful for embedding the GammaRay client into another application * * @since 2.3 */ class GAMMARAY_CLIENT_EXPORT ClientConnectionManager : public QObject { Q_OBJECT public: explicit ClientConnectionManager(QObject *parent = nullptr, bool showSplashScreenOnStartUp = true); ~ClientConnectionManager() override; ClientToolManager *toolManager() const; QMainWindow *mainWindow() const; /*! Connect to a GammaRay probe at @p url. */ void connectToHost(const QUrl &url, int tryAgain = 0); /*! Manually show the splash screen. */ void showSplashScreen(); GammaRay::ProcessTrackerBackend *processTrackerBackend() const; void setProcessTrackerBackend(GammaRay::ProcessTrackerBackend *backend); qint64 processTrackerPid() const; void setProcessTrackerPid(qint64 pid); QString endPointLabel() const; QString endPointKey() const; qint64 endPointPid() const; /*! One-time initialization of stream operators and factory callbacks. */ static void init(); signals: /*! Emitted when the connection is established and the tool model is populated. * If you want to bring up the standard main window, connect this to createMainWindow(), * otherwise use this to show your own UI at this point. */ void ready(); /*! Emitted when there has been a persistent connection error. * You can connect this to handlePersistentConnectionError() for a standard * message box and application exit handling. */ void persistentConnectionError(const QString &msg); /*! Emitted when the connection to the target has been closed, for whatever reason. * For a stand-alone client you probably want to connect this to QApplication::quit(). */ void disconnected(); void processTrackerBackendChanged(GammaRay::ProcessTrackerBackend *backend); void processTrackerInfoChanged(const GammaRay::ProcessTrackerInfo &info); public slots: /*! Disconnect GammaRay. */ void disconnectFromHost(); /*! Brings up a client main window for the current connection. * If you want to use this, connect this slot to ready(). */ QMainWindow *createMainWindow(); /*! Standard persistent connection error handler. * @see persistentConnectionError() */ void handlePersistentConnectionError(const QString &msg); private slots: void doConnectToHost(); void transientConnectionError(); void delayedHideSplashScreen(); void hideSplashScreen(); void targetQuitRequested(); void updateProcessTrackerState(); void clientConnected(); void clientDisconnected(); private: QUrl m_serverUrl; Client *m_client; GammaRay::ProcessTracker *m_processTracker; ClientToolManager *m_toolManager; QPointer m_mainWindow; QTime m_connectionTimeout; bool m_ignorePersistentError; int m_tries; }; } #endif // GAMMARAY_CLIENTCONNECTIONMANAGER_H gammaray-2.11.3/client/clientdevice.cpp000066400000000000000000000036411413310443000200110ustar00rootroot00000000000000/* clientdevice.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "clientdevice.h" #include "tcpclientdevice.h" #include "localclientdevice.h" #include using namespace GammaRay; ClientDevice::ClientDevice(QObject *parent) : QObject(parent) , m_tries(0) { } ClientDevice::~ClientDevice() = default; ClientDevice *ClientDevice::create(const QUrl &url, QObject *parent) { ClientDevice *device = nullptr; if (url.scheme() == QLatin1String("tcp")) device = new TcpClientDevice(parent); else if (url.scheme() == QLatin1String("local")) device = new LocalClientDevice(parent); if (!device) { qWarning() << "Unsupported transport protocol:" << url.toString(); return nullptr; } device->m_serverAddress = url; return device; } void ClientDevice::setTryAgain(int tries) { m_tries = tries; } gammaray-2.11.3/client/clientdevice.h000066400000000000000000000045011413310443000174520ustar00rootroot00000000000000/* clientdevice.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_CLIENTDEVICE_H #define GAMMARAY_CLIENTDEVICE_H #include #include QT_BEGIN_NAMESPACE class QIODevice; QT_END_NAMESPACE namespace GammaRay { /** Adapter for the various different client socket classes (TCP, local, etc). */ class ClientDevice : public QObject { Q_OBJECT public: explicit ClientDevice(QObject *parent = nullptr); ~ClientDevice(); static ClientDevice *create(const QUrl &url, QObject *parent); void setTryAgain(int tries); virtual void connectToHost() = 0; virtual void disconnectFromHost() = 0; virtual QIODevice *device() const = 0; signals: void connected(); /** Server is not up yet. */ void transientError(); /** Anything not being a transient error. */ void persistentError(const QString &errorMsg); protected: QUrl m_serverAddress; int m_tries; }; template class ClientDeviceImpl : public ClientDevice { public: explicit ClientDeviceImpl(QObject *parent = nullptr) : ClientDevice(parent) , m_socket(nullptr) { } QIODevice *device() const override { return m_socket; } protected: ClientT *m_socket; }; } #endif // GAMMARAY_CLIENTDEVICE_H gammaray-2.11.3/client/enumrepositoryclient.cpp000066400000000000000000000041611413310443000216540ustar00rootroot00000000000000/* enumrepositoryclient.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "enumrepositoryclient.h" #include #include using namespace GammaRay; EnumRepositoryClient::EnumRepositoryClient(QObject *parent) : EnumRepository(parent) { ObjectBroker::registerObject(this); connect(this, &EnumRepository::definitionResponse, this, &EnumRepositoryClient::definitionReceived); } EnumRepositoryClient::~EnumRepositoryClient() = default; EnumDefinition EnumRepositoryClient::definition(EnumId id) const { const auto def = EnumRepository::definition(id); if (!def.isValid()) const_cast(this)->requestDefinition(id); return def; } void EnumRepositoryClient::definitionReceived(const EnumDefinition &def) { addDefinition(def); emit definitionChanged(def.id()); } void EnumRepositoryClient::requestDefinition(EnumId id) { Endpoint::instance()->invokeObject(qobject_interface_iid(), "requestDefinition", QVariantList() << id); } gammaray-2.11.3/client/enumrepositoryclient.h000066400000000000000000000033361413310443000213240ustar00rootroot00000000000000/* enumrepositoryclient.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_ENUMREPOSITORYCLIENT_H #define GAMMARAY_ENUMREPOSITORYCLIENT_H #include namespace GammaRay { class EnumRepositoryClient : public GammaRay::EnumRepository { Q_OBJECT Q_INTERFACES(GammaRay::EnumRepository) public: explicit EnumRepositoryClient(QObject *parent = nullptr); ~EnumRepositoryClient() override; EnumDefinition definition(EnumId id) const override; private slots: void definitionReceived(const GammaRay::EnumDefinition &def); private: void requestDefinition(EnumId id) override; }; } #endif // GAMMARAY_ENUMREPOSITORYCLIENT_H gammaray-2.11.3/client/localclientdevice.cpp000066400000000000000000000044711413310443000210260ustar00rootroot00000000000000/* localclientdevice.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "localclientdevice.h" using namespace GammaRay; LocalClientDevice::LocalClientDevice(QObject *parent) : ClientDeviceImpl(parent) { m_socket = new QLocalSocket(this); connect(m_socket, &QLocalSocket::connected, this, &ClientDevice::connected); connect(m_socket, static_cast(&QLocalSocket::error), this, &LocalClientDevice::socketError); } void LocalClientDevice::connectToHost() { m_socket->connectToServer(m_serverAddress.path()); } void LocalClientDevice::disconnectFromHost() { m_socket->disconnectFromServer(); } void LocalClientDevice::socketError() { switch (m_socket->error()) { case QLocalSocket::ConnectionRefusedError: case QLocalSocket::ServerNotFoundError: case QLocalSocket::SocketAccessError: case QLocalSocket::SocketTimeoutError: case QLocalSocket::ConnectionError: case QLocalSocket::UnknownSocketError: emit transientError(); break; default: if (m_tries) { --m_tries; emit transientError(); } else { emit persistentError(m_socket->errorString()); } break; } } gammaray-2.11.3/client/localclientdevice.h000066400000000000000000000030751413310443000204720ustar00rootroot00000000000000/* localclientdevice.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_LOCALCLIENTDEVICE_H #define GAMMARAY_LOCALCLIENTDEVICE_H #include "clientdevice.h" #include namespace GammaRay { class LocalClientDevice : public ClientDeviceImpl { Q_OBJECT public: explicit LocalClientDevice(QObject *parent = nullptr); void connectToHost() override; void disconnectFromHost() override; private slots: void socketError(); }; } #endif // GAMMARAY_LOCALCLIENTDEVICE_H gammaray-2.11.3/client/main.cpp000066400000000000000000000057301413310443000163000ustar00rootroot00000000000000/* main.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include #include "client.h" #include "clientconnectionmanager.h" #include #include #include #include #include using namespace GammaRay; int main(int argc, char **argv) { QCoreApplication::setOrganizationName("KDAB"); QCoreApplication::setOrganizationDomain("kdab.com"); QCoreApplication::setApplicationName("GammaRay"); QCoreApplication::setApplicationVersion(GAMMARAY_COMPACT_VERSION_STRING); #if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0) QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts); // for QWebEngine #endif #if QT_VERSION >= QT_VERSION_CHECK(5, 7, 0) QGuiApplication::setDesktopFileName(QStringLiteral("GammaRay.desktop")); #endif QApplication app(argc, argv); Paths::setRelativeRootPath(GAMMARAY_INVERSE_LIBEXEC_DIR); Translator::loadStandAloneTranslations(); ClientConnectionManager::init(); QUrl serverUrl; if (app.arguments().size() == 2) { serverUrl = QUrl::fromUserInput(app.arguments().at(1)); } else { serverUrl.setScheme(QStringLiteral("tcp")); serverUrl.setHost(QStringLiteral(GAMMARAY_DEFAULT_LOCAL_ADDRESS)); serverUrl.setPort(Client::defaultPort()); } ClientConnectionManager conMan; QObject::connect(&conMan, &ClientConnectionManager::ready, &conMan, &ClientConnectionManager::createMainWindow); QObject::connect(&conMan, &ClientConnectionManager::disconnected, QApplication::instance(), &QCoreApplication::quit); QObject::connect(&conMan, &ClientConnectionManager::persistentConnectionError, &conMan, &ClientConnectionManager::handlePersistentConnectionError); conMan.connectToHost(serverUrl); return app.exec(); } gammaray-2.11.3/client/messagestatisticsmodel.cpp000066400000000000000000000225301413310443000221310ustar00rootroot00000000000000/* messagestatisticsmodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "messagestatisticsmodel.h" #include #include #include #include using namespace GammaRay; #define M(x) { Protocol:: x, #x } static const MetaEnum::Value message_type_table[] = { M(ObjectMonitored), M(ObjectUnmonitored), M(ServerVersion), M(ServerDataVersionNegotiated), M(ObjectMapReply), M(ObjectAdded), M(ObjectRemoved), M(ClientDataVersionNegotiated), M(ModelRowColumnCountRequest), M(ModelContentRequest), M(ModelHeaderRequest), M(ModelSetDataRequest), M(ModelSortRequest), M(ModelSyncBarrier), M(SelectionModelStateRequest), M(ModelRowColumnCountReply), M(ModelContentReply), M(ModelContentChanged), M(ModelHeaderReply), M(ModelHeaderChanged), M(ModelRowsAdded), M(ModelRowsMoved), M(ModelRowsRemoved), M(ModelColumnsAdded), M(ModelColumnsMoved), M(ModelColumnsRemoved), M(ModelReset), M(ModelLayoutChanged), M(SelectionModelSelect), M(SelectionModelCurrent), M(MethodCall), M(PropertySyncRequest), M(PropertyValuesChanged), M(ServerInfo), M(ProbeSettings), M(ServerAddress), M(ServerLaunchError) }; #undef M Q_STATIC_ASSERT(Protocol::MESSAGE_TYPE_COUNT - 1 == (sizeof(message_type_table) / sizeof(MetaEnum::Value))); MessageStatisticsModel::Info::Info() { messageCount.resize(Protocol::MESSAGE_TYPE_COUNT); messageSize.resize(Protocol::MESSAGE_TYPE_COUNT); } int MessageStatisticsModel::Info::totalCount() const { return std::accumulate(messageCount.begin(), messageCount.end(), 0); } quint64 MessageStatisticsModel::Info::totalSize() const { return std::accumulate(messageSize.begin(), messageSize.end(), 0ull); } MessageStatisticsModel::MessageStatisticsModel(QObject *parent) : QAbstractTableModel(parent) , m_totalCount(0) , m_totalSize(0) { } MessageStatisticsModel::~MessageStatisticsModel() = default; void MessageStatisticsModel::clear() { beginResetModel(); m_data.clear(); m_totalCount = 0; m_totalSize = 0; endResetModel(); } void MessageStatisticsModel::addObject(Protocol::ObjectAddress addr, const QString &name) { addr -= 1; if (addr < m_data.size()) { m_data[addr].name = name; emit dataChanged(index(addr, 0), index(addr, 0)); } else { beginInsertRows(QModelIndex(), m_data.size(), addr); m_data.resize(addr + 1); m_data[addr].name = name; endInsertRows(); } } void MessageStatisticsModel::addMessage(Protocol::ObjectAddress addr, Protocol::MessageType msgType, int size) { addr -= 1; msgType -= 1; ++m_totalCount; m_totalSize += size; if (addr < m_data.size()) { m_data[addr].messageCount[msgType]++; m_data[addr].messageSize[msgType] += size; emit dataChanged(index(addr, msgType + 1), index(addr, msgType + 1)); } else { beginInsertRows(QModelIndex(), m_data.size(), addr); m_data.resize(addr + 1); m_data[addr].messageCount[msgType] = 1; m_data[addr].messageSize[msgType] = size; endInsertRows(); } } int MessageStatisticsModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); return Protocol::MESSAGE_TYPE_COUNT - 1; } int MessageStatisticsModel::rowCount(const QModelIndex &parent) const { if (parent.isValid()) { return 0; } return m_data.size(); } // 1 / GRADIENT_SCALE_FACTOR is yellow, 2 / GRADIENT_SCALE_FACTOR and beyond is red static const int GRADIENT_SCALE_FACTOR = 4; static QColor colorForRatio(double ratio) { const auto red = qBound(0.0, ratio * GRADIENT_SCALE_FACTOR, 0.5); const auto green = qBound(0.0, 1 - ratio * GRADIENT_SCALE_FACTOR, 0.5); auto color = QColor(255 * red, 255 * green, 0); if (!UiIntegration::hasDarkUI()) { return color.lighter(300); } return color; } QVariant MessageStatisticsModel::data(const QModelIndex &index, int role) const { if (!index.isValid() || m_data.isEmpty()) return QVariant(); const auto &info = m_data.at(index.row()); const auto msgType = index.column(); if (role == Qt::DisplayRole) { return QString(QString::number(info.messageCount[msgType]) + QStringLiteral(" / ") + QString::number(info.messageSize[msgType])); } if (role == Qt::BackgroundRole && m_totalCount > 0 && m_totalSize > 0) { if (info.messageCount[msgType] == 0) { return QVariant(); } const auto countRatio = (double)info.messageCount[msgType] / (double)m_totalCount; const auto sizeRatio = (double)info.messageSize[msgType] / (double)m_totalSize; const auto ratio = std::max(countRatio, sizeRatio); return colorForRatio(ratio); } if (role == Qt::ToolTipRole) { return tr( "Object: %1\nMessage Type: %2\nMessage Count: %3 of %4 (%5%)\nMessage Size: %6 of %7 (%8%)"). arg(info.name). arg(MetaEnum::enumToString(static_cast(index.column() + 1), message_type_table)). arg(info.messageCount[msgType]). arg(m_totalCount). arg(100.0 * (double)info.messageCount[msgType] / (double)m_totalCount, 0, 'f', 2). arg(info.messageSize[msgType]). arg(m_totalSize). arg(100.0 * (double)info.messageSize[msgType] / (double)m_totalSize, 0, 'f', 2); } return QVariant(); } QVariant MessageStatisticsModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal) { if (role == Qt::DisplayRole) return MetaEnum::enumToString(static_cast(section + 1), message_type_table); if (role == Qt::BackgroundRole) { const auto countRatio = (double)countPerType(section) / (double)m_totalCount; const auto sizeRatio = (double)sizePerType(section) / (double)m_totalSize; const auto ratio = std::max(countRatio, sizeRatio); if (ratio > 0.0) { return colorForRatio(ratio); } } if (role == Qt::ToolTipRole) { const auto count = countPerType(section); const auto size = sizePerType(section); return tr("Message Count: %1 of %2 (%3%)\nMessage Size: %4 of %5 (%6%)"). arg(count). arg(m_totalCount). arg(100.0 * (double)count / (double)m_totalCount, 0, 'f', 2). arg(size). arg(m_totalSize). arg(100.0 * (double)size / (double)m_totalSize, 0, 'f', 2); } } else if (orientation == Qt::Vertical) { const auto &info = m_data.at(section); if (role == Qt::DisplayRole) { return info.name; } if (role == Qt::BackgroundRole) { const auto countRatio = (double)info.totalCount() / (double)m_totalCount; const auto sizeRatio = (double)info.totalSize() / (double)m_totalSize; const auto ratio = std::max(countRatio, sizeRatio); if (ratio > 0.0) { return colorForRatio(ratio); } } if (role == Qt::ToolTipRole) { const auto count = info.totalCount(); const auto size = info.totalSize(); return tr("Message Count: %1 of %2 (%3%)\nMessage Size: %4 of %5 (%6%)\nObject Address: %7"). arg(count). arg(m_totalCount). arg(100.0 * (double)count / (double)m_totalCount, 0, 'f', 2). arg(size). arg(m_totalSize). arg(100.0 * (double)size / (double)m_totalSize, 0, 'f', 2). arg(section + 1); } } return QAbstractTableModel::headerData(section, orientation, role); } int MessageStatisticsModel::countPerType(int msgType) const { int c = 0; for (const auto &info : m_data) { c += info.messageCount.at(msgType); } return c; } quint64 MessageStatisticsModel::sizePerType(int msgType) const { int c = 0; for (const auto &info : m_data) { c += info.messageSize.at(msgType); } return c; } gammaray-2.11.3/client/messagestatisticsmodel.h000066400000000000000000000046761413310443000216110ustar00rootroot00000000000000/* messagestatisticsmodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_MESSAGESTATISTICSMODEL_H #define GAMMARAY_MESSAGESTATISTICSMODEL_H #include #include #include #include namespace GammaRay { /** Diagnostics for GammaRay-internal communication. */ class MessageStatisticsModel : public QAbstractTableModel { Q_OBJECT public: explicit MessageStatisticsModel(QObject *parent = nullptr); ~MessageStatisticsModel() override; void clear(); void addObject(Protocol::ObjectAddress addr, const QString &name); void addMessage(Protocol::ObjectAddress addr, Protocol::MessageType msgType, int size); int columnCount(const QModelIndex &parent) const override; int rowCount(const QModelIndex &parent) const override; QVariant data(const QModelIndex &index, int role) const override; QVariant headerData(int section, Qt::Orientation orientation, int role) const override; private: int countPerType(int msgType) const; quint64 sizePerType(int msgType) const; struct Info { Info(); int totalCount() const; quint64 totalSize() const; QString name; QVector messageCount; QVector messageSize; }; QVector m_data; int m_totalCount; quint64 m_totalSize; }; } #endif // GAMMARAY_MESSAGESTATISTICSMODEL_H gammaray-2.11.3/client/paintanalyzerclient.cpp000066400000000000000000000025241413310443000214320ustar00rootroot00000000000000/* paintanalyzerclient.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "paintanalyzerclient.h" #include using namespace GammaRay; PaintAnalyzerClient::PaintAnalyzerClient(const QString &name, QObject *parent) : PaintAnalyzerInterface(name, parent) { } gammaray-2.11.3/client/paintanalyzerclient.h000066400000000000000000000030161413310443000210740ustar00rootroot00000000000000/* paintanalyzerclient.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_PAINTANALYZERCLIENT_H #define GAMMARAY_PAINTANALYZERCLIENT_H #include namespace GammaRay { class PaintAnalyzerClient : public PaintAnalyzerInterface { Q_OBJECT Q_INTERFACES(GammaRay::PaintAnalyzerInterface) public: explicit PaintAnalyzerClient(const QString &name, QObject *parent = nullptr); }; } #endif // GAMMARAY_PAINTANALYZERCLIENT_H gammaray-2.11.3/client/probecontrollerclient.cpp000066400000000000000000000030451413310443000217630ustar00rootroot00000000000000/* probecontrollerclient.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "probecontrollerclient.h" #include using namespace GammaRay; ProbeControllerClient::ProbeControllerClient(QObject *parent) : ProbeControllerInterface(parent) { } void ProbeControllerClient::detachProbe() { Endpoint::instance()->invokeObject(objectName(), "detachProbe"); } void ProbeControllerClient::quitHost() { Endpoint::instance()->invokeObject(objectName(), "quitHost"); } gammaray-2.11.3/client/probecontrollerclient.h000066400000000000000000000031131413310443000214240ustar00rootroot00000000000000/* probecontrollerclient.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_PROBECONTROLLERCLIENT_H #define GAMMARAY_PROBECONTROLLERCLIENT_H #include namespace GammaRay { class ProbeControllerClient : public ProbeControllerInterface { Q_OBJECT Q_INTERFACES(GammaRay::ProbeControllerInterface) public: explicit ProbeControllerClient(QObject *parent = nullptr); void detachProbe() override; void quitHost() override; }; } #endif // GAMMARAY_PROBECONTROLLERCLIENT_H gammaray-2.11.3/client/processtracker.cpp000066400000000000000000000077711413310443000204150ustar00rootroot00000000000000/* processtracker.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Filipe Azevedo Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "processtracker.h" #include #include using namespace GammaRay; class ProcessTracker::D : public QObject { Q_OBJECT public: GammaRay::ProcessTracker *tracker; GammaRay::ProcessTrackerBackend *backend; QTimer *ticker; GammaRay::ProcessTrackerInfo previousInfo; qint64 pid; explicit D(GammaRay::ProcessTracker *tracker) : QObject(tracker) , tracker(tracker) , backend(nullptr) , ticker(new QTimer(this)) , pid(-1) { ticker->setSingleShot(false); connect(ticker, &QTimer::timeout, this, &D::requestUpdate); } public slots: void requestUpdate() { if (!backend) { qWarning("%s: Backend not set", Q_FUNC_INFO); return; } if (pid < 0) { qWarning("%s: Pid not set", Q_FUNC_INFO); return; } backend->checkProcess(pid); } void processChecked(const GammaRay::ProcessTrackerInfo &info) { if (pid != info.pid) { return; } if (info != previousInfo) { previousInfo = info; emit tracker->infoChanged(info); } } }; ProcessTracker::ProcessTracker(QObject *parent) : QObject(parent) , d(new D(this)) { qRegisterMetaType("GammaRay::ProcessTrackerInfo"); } ProcessTracker::~ProcessTracker() { stop(); } GammaRay::ProcessTrackerBackend *ProcessTracker::backend() const { return d->backend; } void ProcessTracker::setBackend(GammaRay::ProcessTrackerBackend *backend) { if (backend == d->backend) { return; } if (d->backend) { disconnect(d->backend, &ProcessTrackerBackend::processChecked, d.data(), &D::processChecked); } d->backend = backend; if (d->backend) { connect(d->backend, &ProcessTrackerBackend::processChecked, d.data(), &D::processChecked, Qt::QueuedConnection); } emit backendChanged(d->backend); } qint64 ProcessTracker::pid() const { return d->pid; } bool ProcessTracker::isActive() const { return d->ticker->isActive(); } void ProcessTracker::setPid(qint64 pid) { d->previousInfo = ProcessTrackerInfo(); d->pid = pid; } void ProcessTracker::start(int msecs) { d->previousInfo = ProcessTrackerInfo(); d->ticker->start(msecs); } void ProcessTracker::stop() { d->previousInfo = ProcessTrackerInfo(); d->ticker->stop(); } bool ProcessTrackerInfo::operator==(const GammaRay::ProcessTrackerInfo &other) const { return pid == other.pid && traced == other.traced && state == other.state ; } bool ProcessTrackerInfo::operator!=(const GammaRay::ProcessTrackerInfo &other) const { return !operator==(other); } ProcessTrackerBackend::ProcessTrackerBackend(QObject *parent) : QObject(parent) { } #include "processtracker.moc" gammaray-2.11.3/client/processtracker.h000066400000000000000000000057131413310443000200540ustar00rootroot00000000000000/* processtracker.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Filipe Azevedo Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_PROCESSTRACKER_H #define GAMMARAY_PROCESSTRACKER_H #include "gammaray_client_export.h" #include #include #include namespace GammaRay { class ProcessTrackerBackend; class ProcessTrackerInfo; class GAMMARAY_CLIENT_EXPORT ProcessTracker : public QObject { Q_OBJECT public: enum State { Unknown = -1, Running, Suspended }; explicit ProcessTracker(QObject *parent = nullptr); ~ProcessTracker(); GammaRay::ProcessTrackerBackend *backend() const; void setBackend(GammaRay::ProcessTrackerBackend *backend); qint64 pid() const; bool isActive() const; public slots: void setPid(qint64 pid); void start(int msecs = 3000); void stop(); signals: void backendChanged(GammaRay::ProcessTrackerBackend *backend); void infoChanged(const GammaRay::ProcessTrackerInfo &info); private: class D; QSharedPointer d; }; class GAMMARAY_CLIENT_EXPORT ProcessTrackerInfo { public: explicit ProcessTrackerInfo(qint64 pid = -1, bool traced = false, GammaRay::ProcessTracker::State state = GammaRay::ProcessTracker::Unknown) : pid(pid) , traced(traced) , state(state) { } qint64 pid; bool traced; GammaRay::ProcessTracker::State state; bool operator==(const GammaRay::ProcessTrackerInfo &other) const; bool operator!=(const GammaRay::ProcessTrackerInfo &other) const; }; class GAMMARAY_CLIENT_EXPORT ProcessTrackerBackend : public QObject { Q_OBJECT public: explicit ProcessTrackerBackend(QObject *parent = nullptr); public slots: virtual void checkProcess(qint64 pid) = 0; signals: void processChecked(const GammaRay::ProcessTrackerInfo &info); }; } Q_DECLARE_METATYPE(GammaRay::ProcessTrackerInfo) #endif // GAMMARAY_PROCESSTRACKER_H gammaray-2.11.3/client/processtracker_linux.cpp000066400000000000000000000066471413310443000216350ustar00rootroot00000000000000/* processtracker_linux.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Filipe Azevedo Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ // A said crossplatform way (using ptrace) is available at // https://forum.juce.com/t/detecting-if-a-process-is-being-run-under-a-debugger/2098 #include "processtracker_linux.h" #include #include #include namespace { static QString readFile(const QString &filePath, const QByteArray &codec = QByteArrayLiteral("UTF-8")) { QFile file(filePath); if (!file.exists()) { qWarning("%s: File does not exists", Q_FUNC_INFO); return QString(); } if (!file.open(QIODevice::ReadOnly)) { qWarning("%s: File can not be opened", Q_FUNC_INFO); return QString(); } QTextCodec *tc = QTextCodec::codecForName(codec); if (!tc) { tc = QTextCodec::codecForLocale(); } return tc->toUnicode(file.readAll()); } } using namespace GammaRay; ProcessTrackerBackendLinux::ProcessTrackerBackendLinux(QObject *parent) : GammaRay::ProcessTrackerBackend(parent) { } void ProcessTrackerBackendLinux::checkProcess(qint64 pid) { GammaRay::ProcessTrackerInfo pinfo(pid); QString buffer(readFile(QString::fromLatin1("/proc/%1/status").arg(pid))); if (!buffer.isEmpty()) { const QStringList values = buffer.remove(QLatin1Char('\t')).split(QLatin1Char('\n')); for (const QString &value : values) { if (value.startsWith(QStringLiteral("TracerPid:"))) { pinfo.traced = value.split(QLatin1Char(':')).value(1).simplified().toLongLong(); } else if (value.startsWith(QStringLiteral("State:"))) { const char status(value.split(QLatin1Char(':')).value(1).simplified()[0].toUpper().toLatin1()); // status decoding as taken from fs/proc/array.c switch (status) { case 'T': { pinfo.state = GammaRay::ProcessTracker::Suspended; break; } case 'S': // Sleeping case 'R': { pinfo.state = GammaRay::ProcessTracker::Running; break; } //case 'Z': // Zombie //case 'D': // Disk Sleep //case 'W': // Paging } } } } emit processChecked(pinfo); } gammaray-2.11.3/client/processtracker_linux.h000066400000000000000000000030371413310443000212700ustar00rootroot00000000000000/* processtracker_linux.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Filipe Azevedo Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_PROCESSTRACKER_LINUX_H #define GAMMARAY_PROCESSTRACKER_LINUX_H #include "processtracker.h" namespace GammaRay { class GAMMARAY_CLIENT_EXPORT ProcessTrackerBackendLinux : public ProcessTrackerBackend { Q_OBJECT public: explicit ProcessTrackerBackendLinux(QObject *parent = nullptr); public slots: void checkProcess(qint64 pid) override; }; } #endif // GAMMARAY_PROCESSTRACKER_LINUX_H gammaray-2.11.3/client/processtracker_macos.cpp000066400000000000000000000055471413310443000215760ustar00rootroot00000000000000/* processtracker_macos.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Filipe Azevedo Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ // A said crossplatform way (using ptrace) is available at // https://forum.juce.com/t/detecting-if-a-process-is-being-run-under-a-debugger/2098 #include "processtracker_macos.h" #include #include #include #include using namespace GammaRay; ProcessTrackerBackendMacOS::ProcessTrackerBackendMacOS(QObject *parent) : GammaRay::ProcessTrackerBackend(parent) { } void ProcessTrackerBackendMacOS::checkProcess(qint64 pid) { GammaRay::ProcessTrackerInfo pinfo(pid); int status; int mib[4]; struct kinfo_proc info; size_t size; // Initialize the flags so that, if sysctl fails for some bizarre // reason, we get a predictable result. info.kp_proc.p_flag = 0; // Initialize mib, which tells sysctl the info we want, in this case // we're looking for information about a specific process ID. mib[0] = CTL_KERN; mib[1] = KERN_PROC; mib[2] = KERN_PROC_PID; mib[3] = pid; // Call sysctl. size = sizeof(info); status = sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0); //krazy:exclude=null if (status == 0) { // We're being debugged if the P_TRACED flag is set. pinfo.traced = (info.kp_proc.p_flag & P_TRACED) != 0; switch (info.kp_proc.p_stat) { case SRUN: pinfo.state = GammaRay::ProcessTracker::Running; break; case SSTOP: case SSLEEP: pinfo.state = GammaRay::ProcessTracker::Suspended; break; default: break; } } else { qWarning("%s: Can not sysctl.", Q_FUNC_INFO); } emit processChecked(pinfo); } gammaray-2.11.3/client/processtracker_macos.h000066400000000000000000000030371413310443000212330ustar00rootroot00000000000000/* processtracker_macos.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Filipe Azevedo Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_PROCESSTRACKER_MACOS_H #define GAMMARAY_PROCESSTRACKER_MACOS_H #include "processtracker.h" namespace GammaRay { class GAMMARAY_CLIENT_EXPORT ProcessTrackerBackendMacOS : public ProcessTrackerBackend { Q_OBJECT public: explicit ProcessTrackerBackendMacOS(QObject *parent = nullptr); public slots: void checkProcess(qint64 pid) override; }; } #endif // GAMMARAY_PROCESSTRACKER_MACOS_H gammaray-2.11.3/client/processtracker_windows.cpp000066400000000000000000000277511413310443000221670ustar00rootroot00000000000000/* processtracker_windows.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Filipe Azevedo Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ // A said crossplatform way (using ptrace) is available at // https://forum.juce.com/t/detecting-if-a-process-is-being-run-under-a-debugger/2098 #include "processtracker_windows.h" #include "common/commonutils.h" #include #include #include #include // Processthreadsapi.h on Windows 8 and Windows Server 2012 //#include #include #include namespace { // C++ wrappers / helpers struct LocalHandleDeleter { void operator()(HANDLE handle) { if (handle) { CloseHandle(handle); handle = INVALID_HANDLE_VALUE; } } }; // Non pointer HANDLE helper typedef std::remove_pointer::type LHANDLE; // HANDLE holder helper typedef std::unique_ptr LocalHandlePtr; class LocalBuffer { private: BYTE *m_data; DWORD m_size; LocalBuffer(const LocalBuffer &other) : m_data(nullptr) , m_size(0) { Q_UNUSED(other); Q_ASSERT(false); } // cppcheck-suppress operatorEqVarError; m_data and m_size LocalBuffer &operator=(const LocalBuffer &other) { Q_UNUSED(other); Q_ASSERT(false); return *this; } void allocate(DWORD size) { m_data = (BYTE*)LocalAlloc(LMEM_FIXED, size); if (m_data != nullptr) { m_size = size; } } void free() { if (m_data != nullptr) { LocalFree(m_data); m_data = nullptr; m_size = 0; } } public: LocalBuffer(DWORD size) : m_data(nullptr) , m_size(0) { allocate(size); } ~LocalBuffer() { free(); } bool isValid() const { return m_data != nullptr; } DWORD size() const { return m_size; } BYTE *data() { return m_data; } template T *data() { return reinterpret_cast(m_data); } const BYTE *const data() const { return m_data; } template const T *const data() const { return reinterpret_cast(m_data); } void resize(DWORD size) { free(); allocate(size); } void clear() { free(); } }; // Private windows stuff... // I used the WINE declarations as they was the only one working on my windows 10 Pro // https://github.com/wine-mirror/wine/blob/master/include/winternl.h typedef LONG KPRIORITY; //typedef LONG NTSTATUS; #define STATUS_SUCCESS ((NTSTATUS) 0x00000000) #define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS) 0xC0000004) #ifndef NT_SUCCESS #define NT_SUCCESS(Status) ((NTSTATUS)(Status) >= 0) #endif #ifndef NT_ERROR #define NT_ERROR(Status) ((ULONG)(Status) >> 30 == 3) #endif enum SYSTEM_INFORMATION_CLASS { SystemBasicInformation = 0, SystemPerformanceInformation = 2, SystemTimeOfDayInformation = 3, SystemProcessInformation = 5, SystemProcessorPerformanceInformation = 8, SystemInterruptInformation = 23, SystemExceptionInformation = 33, SystemRegistryQuotaInformation = 37, SystemLookasideInformation = 45, SystemPolicyInformation = 134, }; enum KWAIT_REASON { Executive, FreePage, PageIn, PoolAllocation, DelayExecution, Suspended, UserRequest, WrExecutive, WrFreePage, WrPageIn, WrPoolAllocation, WrDelayExecution, WrSuspended, WrUserRequest, WrEventPair, WrQueue, WrLpcReceive, WrLpcReply, WrVirtualMemory, WrPageOut, WrRendezvous, Spare2, Spare3, Spare4, Spare5, Spare6, WrKernel, MaximumWaitReason }; enum THREAD_STATE { Running = 2, Waiting = 5, }; struct CLIENT_ID { HANDLE UniqueProcess; // Process ID HANDLE UniqueThread; // Thread ID }; struct SYSTEM_THREAD { LARGE_INTEGER KernelTime; LARGE_INTEGER UserTime; LARGE_INTEGER CreateTime; DWORD WaitTime; LPVOID StartAddress; CLIENT_ID ClientId; DWORD Priority; DWORD BasePriority; DWORD ContextSwitches; DWORD ThreadState; DWORD WaitReason; DWORD Unknown1; }; struct VM_COUNTERS // virtual memory of process { SIZE_T PeakVirtualSize; SIZE_T VirtualSize; ULONG PageFaultCount; SIZE_T PeakWorkingSetSize; SIZE_T WorkingSetSize; SIZE_T QuotaPeakPagedPoolUsage; SIZE_T QuotaPagedPoolUsage; SIZE_T QuotaPeakNonPagedPoolUsage; SIZE_T QuotaNonPagedPoolUsage; SIZE_T PagefileUsage; SIZE_T PeakPagefileUsage; SIZE_T PrivatePageCount; }; struct UNICODE_STRING { USHORT Length; USHORT MaximumLength; PWSTR Buffer; }; struct SYSTEM_PROCESS { ULONG NextEntryOffset; DWORD NumberOfThreads; DWORD Unknown1[6]; LARGE_INTEGER CreateTime; LARGE_INTEGER UserTime; LARGE_INTEGER KernelTime; UNICODE_STRING ImageName; DWORD BasePriority; HANDLE UniqueProcessId; HANDLE InheritedFromUniqueProcessId; ULONG HandleCount; ULONG SessionId; DWORD Unknown2; VM_COUNTERS VirtualMemoryCounters; IO_COUNTERS IoCounters; SYSTEM_THREAD Threads[1]; }; static QString toQString(const UNICODE_STRING &string) { #if defined(UNICODE) return QString::fromWCharArray(string.Buffer, string.Length); #else Q_ASSERT(false); return QString(); #endif } // Resolve the dll only exported symbol // Though the api is available with recent Windows version, we prefer to dynamically resolve it typedef NTSTATUS(*NtQuerySystemInformationFunc)( SYSTEM_INFORMATION_CLASS SystemInformationClass, PVOID SystemInformation, ULONG SystemInformationLength, PULONG ReturnLength ); static inline NtQuerySystemInformationFunc qt_NtQuerySystemInformation() { static auto symbol = (NtQuerySystemInformationFunc)QLibrary::resolve("NtDll", "NtQuerySystemInformation"); return symbol; } } using namespace GammaRay; ProcessTrackerBackendWindows::ProcessTrackerBackendWindows(QObject *parent) : GammaRay::ProcessTrackerBackend(parent) { } void ProcessTrackerBackendWindows::checkProcess(qint64 pid) { GammaRay::ProcessTrackerInfo pinfo(pid); if (qt_NtQuerySystemInformation()) { BOOL traced = false; if (pid == QCoreApplication::applicationPid()) { traced = IsDebuggerPresent(); } else { LocalHandlePtr processHandle(OpenProcess(READ_CONTROL | PROCESS_QUERY_INFORMATION | PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid)); //krazy:exclude=captruefalse if (!CheckRemoteDebuggerPresent(processHandle.get(), &traced)) { qWarning("%s: Can not check remote debugger presence."); } } pinfo.traced = traced; // There is no way getting a thread state using public api as i'm aware of... /*{ // Get threads status HANDLE_UNIQUE_PTR threadsHandle(CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, pid)); if (threadsHandle.get() == INVALID_HANDLE_VALUE) { Q_ASSERT(false); return false; } QVector threadsId; THREADENTRY32 te32; // Fill in the size of the structure before using it. te32.dwSize = sizeof(THREADENTRY32); // Retrieve information about the first thread, and exit if unsuccessful if (!Thread32First(threadsHandle.get(), &te32)) { qWarning("Thread32First"); // Show cause of failure return false; } // Now walk the thread list of the system, and display information about each thread // associated with the specified process do { if (te32.th32OwnerProcessID == pid) { threadsId << te32.th32ThreadID; } } while (Thread32Next(threadsHandle.get(), &te32)); }*/ // The only way to get threads state is using private stuff... // Get SYSTEM_PROCESS array // This must run in a loop because in the mean time a new process may have started and we need // more buffer than bufferNeededSize !! LocalBuffer buffer(1000); while (true) { WIN_ERROR_ASSERT(buffer.isValid(), break); ULONG bufferNeededSize = 0; NTSTATUS status = qt_NtQuerySystemInformation()(SystemProcessInformation, buffer.data(), buffer.size(), &bufferNeededSize); if (status == STATUS_INFO_LENGTH_MISMATCH) { // The buffer was too small buffer.resize(bufferNeededSize); continue; } WIN_ERROR_ASSERT(NT_SUCCESS(status), buffer.clear(); break;); break; } if (buffer.isValid()) { // Get the SYSTEM_PROCESS by its pid SYSTEM_PROCESS *processes = buffer.data(); SYSTEM_PROCESS *process = nullptr; while (true) { if ((uintptr_t)processes->UniqueProcessId == pid) { //qWarning() << "Found process:" << pid << toQString(processes->ImageName); process = processes; break; } else { //qWarning("No matching process: %lli", (uintptr_t)processes->UniqueProcessId); } if (!processes->NextEntryOffset) { qWarning("no process found matching pid: %lli", pid); break; } processes = (SYSTEM_PROCESS *)((BYTE *)processes + processes->NextEntryOffset); } if (process) { // Get all SYSTEM_THREAD state to generate the process state // Looks like when debugging a windows process that spawn lots of new threads // All are suspended except the last one bool suspended = true; for (DWORD i = 0; i < process->NumberOfThreads; ++i) { SYSTEM_THREAD *thread = &(process->Threads[i]); if (!thread) { Q_ASSERT(false); continue; } const bool debuggingThread = process->NumberOfThreads > 1 && i == process->NumberOfThreads - 1; if (debuggingThread) { continue; } const bool running = !(thread->ThreadState == Waiting && thread->WaitReason == Suspended); //qWarning() << "Thread#" << i << thread->Priority << thread->BasePriority << thread->ContextSwitches << thread->ThreadState << thread->WaitReason; if (running) { suspended = false; break; } } pinfo.state = suspended ? GammaRay::ProcessTracker::Suspended : GammaRay::ProcessTracker::Running; } } } emit processChecked(pinfo); } gammaray-2.11.3/client/processtracker_windows.h000066400000000000000000000030531413310443000216210ustar00rootroot00000000000000/* processtracker_windows.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Filipe Azevedo Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_PROCESSTRACKER_WINDOWS_H #define GAMMARAY_PROCESSTRACKER_WINDOWS_H #include "processtracker.h" namespace GammaRay { class GAMMARAY_CLIENT_EXPORT ProcessTrackerBackendWindows : public ProcessTrackerBackend { Q_OBJECT public: explicit ProcessTrackerBackendWindows(QObject *parent = nullptr); public slots: void checkProcess(qint64 pid) override; }; } #endif // GAMMARAY_PROCESSTRACKER_WINDOWS_H gammaray-2.11.3/client/propertycontrollerclient.cpp000066400000000000000000000027741413310443000225500ustar00rootroot00000000000000/* propertycontrollerinterface.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "propertycontrollerclient.h" #include #include #include using namespace GammaRay; PropertyControllerClient::PropertyControllerClient(const QString &name, QObject *parent) : PropertyControllerInterface(name, parent) { } PropertyControllerClient::~PropertyControllerClient() = default; gammaray-2.11.3/client/propertycontrollerclient.h000066400000000000000000000031461413310443000222070ustar00rootroot00000000000000/* propertycontrollerinterface.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_PROPERTYCONTROLLERCLIENT_H #define GAMMARAY_PROPERTYCONTROLLERCLIENT_H #include namespace GammaRay { class PropertyControllerClient : public PropertyControllerInterface { Q_OBJECT Q_INTERFACES(GammaRay::PropertyControllerInterface) public: explicit PropertyControllerClient(const QString &name, QObject *parent = nullptr); ~PropertyControllerClient() override; }; } #endif // GAMMARAY_PROPERTYCONTROLLERCLIENT_H gammaray-2.11.3/client/remotemodel.cpp000066400000000000000000001055221413310443000176700ustar00rootroot00000000000000/* remotemodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "remotemodel.h" #include "client.h" #include #include #include #include #include #include #include #include #include using namespace GammaRay; void(*RemoteModel::s_registerClientCallback)() = nullptr; RemoteModel::Node::~Node() { qDeleteAll(children); } void RemoteModel::Node::clearChildrenData() { foreach (auto child, children) { child->clearChildrenStructure(); child->data.clear(); child->flags.clear(); child->state.clear(); } } void RemoteModel::Node::clearChildrenStructure() { qDeleteAll(children); children.clear(); rowCount = -1; columnCount = -1; } void RemoteModel::Node::allocateColumns() { if (hasColumnData() || !parent || parent->columnCount < 0) return; data.resize(parent->columnCount); flags.resize(parent->columnCount); flags.fill(Qt::ItemIsSelectable | Qt::ItemIsEnabled); state.resize(parent->columnCount, RemoteModelNodeState::Empty | RemoteModelNodeState::Outdated); } bool RemoteModel::Node::hasColumnData() const { if (!parent) return false; Q_ASSERT(data.size() == flags.size()); Q_ASSERT(data.size() == (int)state.size()); Q_ASSERT(data.isEmpty() || data.size() == parent->columnCount || parent->columnCount < 0); return data.size() == parent->columnCount && parent->columnCount > 0; } QVariant RemoteModel::s_emptyDisplayValue; QVariant RemoteModel::s_emptySizeHintValue; RemoteModel::RemoteModel(const QString &serverObject, QObject *parent) : QAbstractItemModel(parent) , m_pendingRequestsTimer(new QTimer(this)) , m_serverObject(serverObject) , m_myAddress(Protocol::InvalidObjectAddress) , m_currentSyncBarrier(0) , m_targetSyncBarrier(0) , m_proxyDynamicSortFilter(false) , m_proxyCaseSensitivity(Qt::CaseSensitive) , m_proxyKeyColumn(0) { if (s_emptyDisplayValue.isNull()) { s_emptyDisplayValue = tr("Loading..."); QStyleOptionViewItem opt; opt.features |= QStyleOptionViewItem::HasDisplay; opt.text = s_emptyDisplayValue.toString(); s_emptySizeHintValue = QApplication::style()->sizeFromContents(QStyle::CT_ItemViewItem, &opt, QSize(), nullptr); } m_root = new Node; m_pendingRequestsTimer->setInterval(0); m_pendingRequestsTimer->setSingleShot(true); connect(m_pendingRequestsTimer, &QTimer::timeout, this, &RemoteModel::doRequests); registerClient(serverObject); connectToServer(); } RemoteModel::~RemoteModel() { delete m_root; } bool RemoteModel::isConnected() const { return m_myAddress != Protocol::InvalidObjectAddress; } QModelIndex RemoteModel::index(int row, int column, const QModelIndex &parent) const { if (!isConnected() || row < 0 || column < 0) return {}; Node *parentNode = nodeForIndex(parent); Q_ASSERT(parentNode->children.size() >= parentNode->rowCount); if (parentNode->rowCount == -1) requestRowColumnCount(parent); // trying to traverse into a branch we haven't loaded yet if (parentNode->rowCount <= row || parentNode->columnCount <= column) return QModelIndex(); return createIndex(row, column, parentNode->children.at(row)); } QModelIndex RemoteModel::parent(const QModelIndex &index) const { Node *currentNode = nodeForIndex(index); Q_ASSERT(currentNode); if (currentNode == m_root || currentNode->parent == m_root) return {}; Q_ASSERT(currentNode->parent && currentNode->parent->parent); Q_ASSERT(currentNode->parent->children.contains(currentNode)); Q_ASSERT(currentNode->parent->parent->children.contains(currentNode->parent)); return modelIndexForNode(currentNode->parent, 0); } int RemoteModel::rowCount(const QModelIndex &index) const { if (!isConnected() || index.column() > 0) return 0; Node *node = nodeForIndex(index); Q_ASSERT(node); if (node->rowCount < 0) { if (node->columnCount < 0) // not yet requested vs. in the middle of insertion requestRowColumnCount(index); } return qMax(0, node->rowCount); // if requestRowColumnCount is synchronoous, ie. changes rowCount (as in simple unit test), returning 0 above would cause ModelTest to see inconsistent data } int RemoteModel::columnCount(const QModelIndex &index) const { if (!isConnected()) return 0; Node *node = nodeForIndex(index); Q_ASSERT(node); if (node->columnCount < 0) { requestRowColumnCount(index); return 0; } return node->columnCount; } QVariant RemoteModel::data(const QModelIndex &index, int role) const { if (!isConnected() || !index.isValid()) return QVariant(); Node *node = nodeForIndex(index); Q_ASSERT(node); const auto state = stateForColumn(node, index.column()); if (role == RemoteModelRole::LoadingState) return QVariant::fromValue(state); // for size hint we don't want to trigger loading, as that's largely used for item view layouting if (state & RemoteModelNodeState::Empty) { if (role == Qt::SizeHintRole) return s_emptySizeHintValue; } if ((state & RemoteModelNodeState::Outdated) && ((state & RemoteModelNodeState::Loading) == 0)) requestDataAndFlags(index); if (state & RemoteModelNodeState::Empty) { // still waiting for data if (role == Qt::DisplayRole) return s_emptyDisplayValue; return QVariant(); } // note .value returns good defaults otherwise Q_ASSERT(node->data.size() > index.column()); return node->data.at(index.column()).value(role); } bool RemoteModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (!isConnected()) return false; Message msg(m_myAddress, Protocol::ModelSetDataRequest); msg << Protocol::fromQModelIndex(index) << role << value; sendMessage(msg); return false; } Qt::ItemFlags RemoteModel::flags(const QModelIndex &index) const { if (!index.isValid()) return Qt::NoItemFlags; Node *node = nodeForIndex(index); Q_ASSERT(node); if (!node->hasColumnData()) return Qt::ItemIsSelectable | Qt::ItemIsEnabled; Q_ASSERT(node->flags.size() > index.column()); return node->flags.at(index.column()); } QVariant RemoteModel::headerData(int section, Qt::Orientation orientation, int role) const { if (!isConnected() || section < 0) return QVariant(); if (section >= (orientation == Qt::Horizontal ? m_root->columnCount : m_root->rowCount)) return QVariant(); auto &headers = orientation == Qt::Horizontal ? m_horizontalHeaders : m_verticalHeaders; if (headers.isEmpty()) { // allocate on demand const auto count = orientation == Qt::Horizontal ? m_root->columnCount : m_root->rowCount; if (count <= 0) return QVariant(); headers.resize(count); } Q_ASSERT(headers.size() > section); if (headers.at(section).isEmpty()) requestHeaderData(orientation, section); return headers.at(section).value(role); } void RemoteModel::sort(int column, Qt::SortOrder order) { Message msg(m_myAddress, Protocol::ModelSortRequest); msg << (quint32)column << (quint32)order; sendMessage(msg); } void RemoteModel::newMessage(const GammaRay::Message &msg) { if (!checkSyncBarrier(msg)) return; switch (msg.type()) { case Protocol::ModelRowColumnCountReply: { quint32 size; msg >> size; Q_ASSERT(size > 0); for (quint32 i = 0; i < size; ++i) { // We now need to read the complete entries because of the break -> continue change Protocol::ModelIndex index; msg >> index; qint32 rowCount, columnCount; msg >> rowCount >> columnCount; Node *node = nodeForIndex(index); if (!node) { // This can happen e.g. when we called a blocking operation from the remote client // via the method invocation with a direct connection. Then when the blocking // operation creates e.g. a QObject it is directly added/removed to the ObjectTree // and we get signals for that. When we then though ask for column counts we will // only get responses once the blocking operation has finished, at which point // the object may already have been invalidated. continue; } // we get -1/-1 if we requested for an invalid index, e.g. due to not having processed // all structure changes yet. This will automatically trigger a retry. Q_ASSERT((rowCount >= 0 && columnCount >= 0) || (rowCount == -1 && columnCount == -1)); if (node->rowCount >= 0 || node->columnCount >= 0) { // This can happen in similar racy conditions as below, when we request the row/col count // for two different Node* at the same index (one was deleted in-between and then the other // was created). We ignore the new data as the node it is intended for will request it again // after processing all structure changes. continue; } if (node->rowCount == -1) continue; // we didn't ask for this, probably outdated response for a moved node Q_ASSERT(node->rowCount < -1 && node->columnCount == -1); const QModelIndex qmi = modelIndexForNode(node, 0); if (columnCount > 0) { beginInsertColumns(qmi, 0, columnCount - 1); node->columnCount = columnCount; endInsertColumns(); } else { node->columnCount = columnCount; } if (rowCount > 0) { beginInsertRows(qmi, 0, rowCount - 1); node->children.reserve(rowCount); for (int i = 0; i < rowCount; ++i) { auto *child = new Node; child->parent = node; node->children.push_back(child); } node->rowCount = rowCount; endInsertRows(); } else { node->rowCount = rowCount; } } break; } case Protocol::ModelContentReply: { quint32 size; msg >> size; Q_ASSERT(size > 0); QHash > dataChangedIndexes; for (quint32 i = 0; i < size; ++i) { Protocol::ModelIndex index; msg >> index; Node *node = nodeForIndex(index); const auto column = index.last().column; const auto state = node ? stateForColumn(node, column) : RemoteModelNodeState::NoState; typedef QHash ItemData; ItemData itemData; qint32 flags; msg >> itemData >> flags; if ((state & RemoteModelNodeState::Loading) == 0) continue; // we didn't ask for this, probably outdated response for a moved cell if (node) { node->allocateColumns(); Q_ASSERT(node->data.size() > column); node->data[column] = itemData; node->flags[column] = static_cast(flags); node->state[column] = state & ~(RemoteModelNodeState::Loading | RemoteModelNodeState::Empty | RemoteModelNodeState::Outdated); if ((flags & Qt::ItemNeverHasChildren) && column == 0) { node->rowCount = 0; node->columnCount = node->data.size(); } // group by parent, and emit dataChange for the bounding rect per hierarchy level // as an approximiation of perfect range batching const QModelIndex qmi = modelIndexForNode(node, column); dataChangedIndexes[qmi.parent()].push_back(qmi); } } for (auto it = dataChangedIndexes.constBegin(); it != dataChangedIndexes.constEnd(); ++it) { const auto &indexes = it.value(); Q_ASSERT(!indexes.isEmpty()); int r1 = std::numeric_limits::max(), r2 = 0, c1 = std::numeric_limits::max(), c2 = 0; for (const auto &index : indexes) { r1 = std::min(r1, index.row()); r2 = std::max(r2, index.row()); c1 = std::min(c1, index.column()); c2 = std::max(c2, index.column()); } const auto qmi = indexes.at(0); emit dataChanged(qmi.sibling(r1, c1), qmi.sibling(r2, c2)); } break; } case Protocol::ModelHeaderReply: { qint8 orientation; qint32 section; QHash data; msg >> orientation >> section >> data; Q_ASSERT(orientation == Qt::Horizontal || orientation == Qt::Vertical); Q_ASSERT(section >= 0); auto &headers = orientation == Qt::Horizontal ? m_horizontalHeaders : m_verticalHeaders; if (headers.isEmpty()) break; Q_ASSERT(headers.size() > section); headers[section] = data; if ((orientation == Qt::Horizontal && m_root->columnCount > section) || (orientation == Qt::Vertical && m_root->rowCount > section)) emit headerDataChanged(static_cast(orientation), section, section); break; } case Protocol::ModelContentChanged: { Protocol::ModelIndex beginIndex, endIndex; QVector roles; msg >> beginIndex >> endIndex >> roles; Node *node = nodeForIndex(beginIndex); if (!node || node == m_root) break; Q_ASSERT(beginIndex.last().row <= endIndex.last().row); Q_ASSERT(beginIndex.last().column <= endIndex.last().column); // mark content as outdated (will be refetched on next request) for (int row = beginIndex.last().row; row <= endIndex.last().row; ++row) { Node *currentRow = node->parent->children.at(row); if (!currentRow->hasColumnData()) continue; for (int col = beginIndex.last().column; col <= endIndex.last().column; ++col) { const auto state = stateForColumn(currentRow, col); if ((state & RemoteModelNodeState::Outdated) == 0) { Q_ASSERT((int)currentRow->state.size() > col); currentRow->state[col] = state | RemoteModelNodeState::Outdated; } } } const QModelIndex qmiBegin = modelIndexForNode(node, beginIndex.last().column); const QModelIndex qmiEnd = qmiBegin.sibling(endIndex.last().row, endIndex.last().column); emit dataChanged(qmiBegin, qmiEnd, roles); break; } case Protocol::ModelHeaderChanged: { qint8 ori; int first, last; msg >> ori >> first >> last; const Qt::Orientation orientation = static_cast(ori); auto &headers = orientation == Qt::Horizontal ? m_horizontalHeaders : m_verticalHeaders; for (int i = first; i < last && i < headers.size(); ++i) headers[i].clear(); emit headerDataChanged(orientation, first, last); break; } case Protocol::ModelRowsAdded: { Protocol::ModelIndex parentIndex; int first, last; msg >> parentIndex >> first >> last; Q_ASSERT(last >= first); Node *parentNode = nodeForIndex(parentIndex); if (!parentNode || parentNode->rowCount < 0) return; // we don't know the parent yet, so we don't care about changes to it either Q_ASSERT(first <= parentNode->rowCount); doInsertRows(parentNode, first, last); break; } case Protocol::ModelRowsRemoved: { Protocol::ModelIndex parentIndex; int first, last; msg >> parentIndex >> first >> last; Q_ASSERT(last >= first); Node *parentNode = nodeForIndex(parentIndex); if (!parentNode || parentNode->rowCount < 0) return; // we don't know the parent yet, so we don't care about changes to it either Q_ASSERT(first < parentNode->rowCount); doRemoveRows(parentNode, first, last); break; } case Protocol::ModelRowsMoved: { Protocol::ModelIndex sourceParentIndex, destParentIndex; int sourceFirst, sourceLast, destChild; msg >> sourceParentIndex >> sourceFirst >> sourceLast >> destParentIndex >> destChild; Q_ASSERT(sourceLast >= sourceFirst); Node *sourceParent = nodeForIndex(sourceParentIndex); Node *destParent = nodeForIndex(destParentIndex); const bool sourceKnown = sourceParent && sourceParent->rowCount >= 0; const bool destKnown = destParent && destParent->rowCount >= 0; // case 1: source and destination not locally cached -> nothing to do if (!sourceKnown && !destKnown) break; // case 2: only source is locally known -> remove if (sourceKnown && !destKnown) { doRemoveRows(sourceParent, sourceFirst, sourceLast); break; } // case 3: only destination is locally known -> added if (!sourceKnown && destKnown) { doInsertRows(destParent, destChild, destChild + sourceLast - sourceFirst); break; } // case 4: source and destination are locally known -> move if (sourceKnown && destKnown) { doMoveRows(sourceParent, sourceFirst, sourceLast, destParent, destChild); break; } break; } case Protocol::ModelColumnsAdded: { Protocol::ModelIndex parentIndex; int first, last; msg >> parentIndex >> first >> last; Q_ASSERT(last >= first); Node *parentNode = nodeForIndex(parentIndex); if (!parentNode || parentNode->rowCount < 0) return; // we don't know the parent yet, so we don't care about changes to it either doInsertColumns(parentNode, first, last); break; } case Protocol::ModelColumnsRemoved: { Protocol::ModelIndex parentIndex; int first, last; msg >> parentIndex >> first >> last; Q_ASSERT(last >= first); Node *parentNode = nodeForIndex(parentIndex); if (!parentNode || parentNode->rowCount < 0) return; // we don't know the parent yet, so we don't care about changes to it either doRemoveColumns(parentNode, first, last); break; } case Protocol::ModelColumnsMoved: // TODO qWarning() << Q_FUNC_INFO << "not implemented yet" << msg.type() << m_serverObject; clear(); break; case Protocol::ModelLayoutChanged: { QVector parents; quint32 hint; msg >> parents >> hint; if (parents.isEmpty()) { // everything changed (or Qt4) emit layoutAboutToBeChanged(); foreach (const auto &persistentIndex, persistentIndexList()) changePersistentIndex(persistentIndex, QModelIndex()); if (hint == 0) m_root->clearChildrenStructure(); else m_root->clearChildrenData(); emit layoutChanged(); break; } QVector parentNodes; parentNodes.reserve(parents.size()); for (const auto &p : qAsConst(parents)) { auto node = nodeForIndex(p); if (!node) continue; parentNodes.push_back(node); } if (parentNodes.isEmpty()) break; // no currently loaded node changed, nothing to do emit layoutAboutToBeChanged(); // TODO Qt5 support with exact sub-trees foreach (const auto &persistentIndex, persistentIndexList()) { auto persistentNode = nodeForIndex(persistentIndex); Q_ASSERT(persistentNode); for (auto node : qAsConst(parentNodes)) { if (!isAncestor(node, persistentNode)) continue; changePersistentIndex(persistentIndex, QModelIndex()); break; } } for (auto node : qAsConst(parentNodes)) { if (hint == 0) node->clearChildrenStructure(); else node->clearChildrenData(); } emit layoutChanged(); // TODO Qt5 support with exact sub-trees break; } case Protocol::ModelReset: clear(); break; } } void RemoteModel::serverRegistered(const QString &objectName, Protocol::ObjectAddress objectAddress) { if (m_serverObject == objectName) { m_myAddress = objectAddress; connectToServer(); } } void RemoteModel::serverUnregistered(const QString &objectName, Protocol::ObjectAddress objectAddress) { Q_UNUSED(objectName); if (m_myAddress == objectAddress) { m_myAddress = Protocol::InvalidObjectAddress; clear(); } } RemoteModel::Node *RemoteModel::nodeForIndex(const QModelIndex &index) const { if (!index.isValid()) return m_root; return reinterpret_cast(index.internalPointer()); } RemoteModel::Node *RemoteModel::nodeForIndex(const Protocol::ModelIndex &index) const { Node *node = m_root; for (auto i : index) { if (node->children.size() <= i.row) return nullptr; node = node->children.at(i.row); node->rowHint = i.row; } return node; } QModelIndex RemoteModel::modelIndexForNode(Node *node, int column) const { Q_ASSERT(node); if (node == m_root) return {}; int row = -1; if (node->rowHint > 0 && node->rowHint < node->parent->children.size()) { if (node->parent->children.at(node->rowHint) == node) row = node->rowHint; } if (row < 0) { row = node->parent->children.indexOf(node); } return createIndex(row, column, node); } bool RemoteModel::isAncestor(RemoteModel::Node *ancestor, RemoteModel::Node *child) const { Q_ASSERT(ancestor); Q_ASSERT(child); Q_ASSERT(m_root); if (child == m_root) return false; Q_ASSERT(child->parent); if (child->parent == ancestor) return true; return isAncestor(ancestor, child->parent); } RemoteModelNodeState::NodeStates RemoteModel::stateForColumn(RemoteModel::Node *node, int columnIndex) const { Q_ASSERT(node); if (!node->hasColumnData()) return RemoteModelNodeState::Empty | RemoteModelNodeState::Outdated; Q_ASSERT((int)node->state.size() > columnIndex); return node->state[columnIndex]; } void RemoteModel::requestRowColumnCount(const QModelIndex &index) const { Node *node = nodeForIndex(index); Q_ASSERT(node); Q_ASSERT(node->rowCount < 0 && node->columnCount < 0); if (node->rowCount < -1) // already requesting return; node->rowCount = -2; auto &indexes = m_pendingRequests[RowColumnCount]; indexes.push_back(Protocol::fromQModelIndex(index)); if (indexes.size() > 100) { m_pendingRequestsTimer->stop(); doRequests(); } else { m_pendingRequestsTimer->start(); } } void RemoteModel::requestDataAndFlags(const QModelIndex &index) const { Node *node = nodeForIndex(index); Q_ASSERT(node); const auto state = stateForColumn(node, index.column()); Q_ASSERT((state & RemoteModelNodeState::Loading) == 0); node->allocateColumns(); Q_ASSERT((int)node->state.size() > index.column()); node->state[index.column()] = state | RemoteModelNodeState::Loading; // mark pending request auto &indexes = m_pendingRequests[DataAndFlags]; indexes.push_back(Protocol::fromQModelIndex(index)); if (indexes.size() > 100) { m_pendingRequestsTimer->stop(); doRequests(); } else { m_pendingRequestsTimer->start(); } } void RemoteModel::doRequests() const { QMutableMapIterator> it(m_pendingRequests); while (it.hasNext()) { it.next(); Q_ASSERT(!it.value().isEmpty()); const auto &indexes = it.value(); switch (it.key()) { case RowColumnCount: { Message msg(m_myAddress, Protocol::ModelRowColumnCountRequest); msg << quint32(indexes.size()); for (const auto &index : indexes) msg << index; sendMessage(msg); break; } case DataAndFlags: { Message msg(m_myAddress, Protocol::ModelContentRequest); msg << quint32(indexes.size()); for (const auto &index : indexes) msg << index; sendMessage(msg); break; } } it.remove(); } } void RemoteModel::requestHeaderData(Qt::Orientation orientation, int section) const { Q_ASSERT(section >= 0); auto &headers = orientation == Qt::Horizontal ? m_horizontalHeaders : m_verticalHeaders; Q_ASSERT(!headers.isEmpty()); Q_ASSERT(headers.at(section).isEmpty()); headers[section][Qt::DisplayRole] = s_emptyDisplayValue; Message msg(m_myAddress, Protocol::ModelHeaderRequest); msg << qint8(orientation) << qint32(section); sendMessage(msg); } void RemoteModel::clear() { beginResetModel(); if (isConnected()) { Message msg(m_myAddress, Protocol::ModelSyncBarrier); msg << ++m_targetSyncBarrier; sendMessage(msg); } delete m_root; m_root = new Node; m_horizontalHeaders.clear(); m_verticalHeaders.clear(); endResetModel(); } void RemoteModel::connectToServer() { if (m_myAddress == Protocol::InvalidObjectAddress) return; beginResetModel(); Client::instance()->registerObject(m_serverObject, this); Client::instance()->registerMessageHandler(m_myAddress, this, "newMessage"); endResetModel(); } bool RemoteModel::checkSyncBarrier(const Message &msg) { if (msg.type() == Protocol::ModelSyncBarrier) msg >> m_currentSyncBarrier; return m_currentSyncBarrier == m_targetSyncBarrier; } void RemoteModel::resetLoadingState(RemoteModel::Node *node, int startRow) const { if (node->rowCount < 0) { node->rowCount = -1; // reset row count loading state return; } Q_ASSERT(node->children.size() == node->rowCount); for (int row = startRow; row < node->rowCount; ++row) { Node *child = node->children.at(row); for (auto it = child->state.begin(); it != child->state.end(); ++it) { if ((*it) & RemoteModelNodeState::Loading) (*it) = (*it) & ~RemoteModelNodeState::Loading; } resetLoadingState(child, 0); } } void RemoteModel::doInsertRows(RemoteModel::Node *parentNode, int first, int last) { Q_ASSERT(parentNode->rowCount == parentNode->children.size()); const QModelIndex qmiParent = modelIndexForNode(parentNode, 0); beginInsertRows(qmiParent, first, last); // if necessary, update vertical headers if (parentNode == m_root && !m_verticalHeaders.isEmpty()) m_verticalHeaders.insert(first, last - first + 1, QHash()); // allocate rows in the right spot parentNode->children.insert(first, last - first + 1, nullptr); // create nodes for the new rows for (int i = first; i <= last; ++i) { auto *child = new Node; child->parent = parentNode; parentNode->children[i] = child; } // adjust row count parentNode->rowCount += last - first + 1; Q_ASSERT(parentNode->rowCount == parentNode->children.size()); endInsertRows(); resetLoadingState(parentNode, last); } void RemoteModel::doRemoveRows(RemoteModel::Node *parentNode, int first, int last) { Q_ASSERT(parentNode->rowCount == parentNode->children.size()); const QModelIndex qmiParent = modelIndexForNode(parentNode, 0); beginRemoveRows(qmiParent, first, last); // if necessary update vertical headers if (parentNode == m_root && !m_verticalHeaders.isEmpty()) m_verticalHeaders.remove(first, last - first + 1); // delete nodes for (int i = first; i <= last; ++i) delete parentNode->children.at(i); parentNode->children.remove(first, last - first + 1); // adjust row count parentNode->rowCount -= last - first + 1; Q_ASSERT(parentNode->rowCount == parentNode->children.size()); endRemoveRows(); resetLoadingState(parentNode, first); } void RemoteModel::doMoveRows(RemoteModel::Node *sourceParentNode, int sourceStart, int sourceEnd, RemoteModel::Node *destParentNode, int destStart) { Q_ASSERT(sourceParentNode->rowCount == sourceParentNode->children.size()); Q_ASSERT(destParentNode->rowCount == destParentNode->children.size()); Q_ASSERT(sourceEnd >= sourceStart); Q_ASSERT(sourceParentNode->rowCount > sourceEnd); const int destEnd = destStart + sourceEnd - sourceStart; const int amount = sourceEnd - sourceStart + 1; const QModelIndex qmiSourceParent = modelIndexForNode(sourceParentNode, 0); const QModelIndex qmiDestParent = modelIndexForNode(destParentNode, 0); beginMoveRows(qmiSourceParent, sourceStart, sourceEnd, qmiDestParent, destStart); // make room in the destination destParentNode->children.insert(destStart, amount, nullptr); // move nodes for (int i = 0; i < amount; ++i) { Node *node = sourceParentNode->children.at(sourceStart + i); node->parent = destParentNode; destParentNode->children[destStart + i] = node; } // shrink source sourceParentNode->children.remove(sourceStart, amount); // adjust row count sourceParentNode->rowCount -= amount; destParentNode->rowCount += amount; Q_ASSERT(sourceParentNode->rowCount == sourceParentNode->children.size()); Q_ASSERT(destParentNode->rowCount == destParentNode->children.size()); // FIXME: we could insert/remove just the affected rows, but this is currently not hit anyway // update vertical headers if we move to/from top-level if (sourceParentNode == m_root || destParentNode == m_root) m_verticalHeaders.clear(); endMoveRows(); resetLoadingState(sourceParentNode, sourceStart); resetLoadingState(destParentNode, destEnd); } void RemoteModel::doInsertColumns(RemoteModel::Node *parentNode, int first, int last) { const auto newColCount = last - first + 1; const QModelIndex qmiParent = modelIndexForNode(parentNode, 0); beginInsertColumns(qmiParent, first, last); // if necessary, update horizontal headers if (parentNode == m_root && !m_horizontalHeaders.isEmpty()) m_horizontalHeaders.insert(first, newColCount, QHash()); // adjust column data in all child nodes, if available for (auto node : qAsConst(parentNode->children)) { if (!node->hasColumnData()) continue; // allocate new columns node->data.insert(first, newColCount, QHash()); node->flags.insert(first, newColCount, Qt::ItemIsSelectable | Qt::ItemIsEnabled); node->state.insert(node->state.begin() + first, newColCount, RemoteModelNodeState::Empty | RemoteModelNodeState::Outdated); } // adjust column count parentNode->columnCount += newColCount; endInsertColumns(); } void RemoteModel::doRemoveColumns(RemoteModel::Node *parentNode, int first, int last) { const auto delColCount = last - first + 1; const QModelIndex qmiParent = modelIndexForNode(parentNode, 0); beginRemoveColumns(qmiParent, first, last); // if necessary update vertical headers if (parentNode == m_root && !m_horizontalHeaders.isEmpty()) m_horizontalHeaders.remove(first, delColCount); // adjust column data in all child nodes, if available for (auto node : qAsConst(parentNode->children)) { if (!node->hasColumnData()) continue; node->data.remove(first, delColCount); node->flags.remove(first, delColCount); node->state.erase(node->state.begin() + first, node->state.begin() + last); } // adjust column count parentNode->columnCount -= delColCount; endRemoveColumns(); } void RemoteModel::registerClient(const QString &serverObject) { if (Q_UNLIKELY(s_registerClientCallback)) { // called from ctor, so we can't use virtuals here s_registerClientCallback(); return; } m_myAddress = Endpoint::instance()->objectAddress(serverObject); connect(Endpoint::instance(), &Endpoint::objectRegistered, this, &RemoteModel::serverRegistered); connect(Endpoint::instance(), &Endpoint::objectUnregistered, this, &RemoteModel::serverUnregistered); } void RemoteModel::sendMessage(const Message &msg) const { Endpoint::send(msg); } bool RemoteModel::proxyDynamicSortFilter() const { return m_proxyDynamicSortFilter; } void RemoteModel::setProxyDynamicSortFilter(bool dynamicSortFilter) { if (m_proxyDynamicSortFilter == dynamicSortFilter) return; m_proxyDynamicSortFilter = dynamicSortFilter; emit proxyDynamicSortFilterChanged(); } Qt::CaseSensitivity RemoteModel::proxyFilterCaseSensitivity() const { return m_proxyCaseSensitivity; } void RemoteModel::setProxyFilterCaseSensitivity(Qt::CaseSensitivity caseSensitivity) { if (m_proxyCaseSensitivity == caseSensitivity) return; m_proxyCaseSensitivity = caseSensitivity; emit proxyFilterCaseSensitivityChanged(); } int RemoteModel::proxyFilterKeyColumn() const { return m_proxyKeyColumn; } void RemoteModel::setProxyFilterKeyColumn(int column) { if (m_proxyKeyColumn == column) return; m_proxyKeyColumn = column; emit proxyFilterKeyColumnChanged(); } QRegExp RemoteModel::proxyFilterRegExp() const { return m_proxyFilterRegExp; } void RemoteModel::setProxyFilterRegExp(const QRegExp ®Exp) { if (m_proxyFilterRegExp == regExp) return; m_proxyFilterRegExp = regExp; emit proxyFilterRegExpChanged(); } gammaray-2.11.3/client/remotemodel.h000066400000000000000000000171311413310443000173330ustar00rootroot00000000000000/* remotemodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_REMOTEMODEL_H #define GAMMARAY_REMOTEMODEL_H #include "gammaray_client_export.h" #include #include #include #include #include #include #include #include namespace GammaRay { class Message; /** @internal Exported for unit test use only. */ class GAMMARAY_CLIENT_EXPORT RemoteModel : public QAbstractItemModel { Q_OBJECT Q_PROPERTY( bool dynamicSortFilter READ proxyDynamicSortFilter WRITE setProxyDynamicSortFilter NOTIFY proxyDynamicSortFilterChanged) Q_PROPERTY( Qt::CaseSensitivity filterCaseSensitivity READ proxyFilterCaseSensitivity WRITE setProxyFilterCaseSensitivity NOTIFY proxyFilterCaseSensitivityChanged) Q_PROPERTY( int filterKeyColumn READ proxyFilterKeyColumn WRITE setProxyFilterKeyColumn NOTIFY proxyFilterKeyColumnChanged) Q_PROPERTY( QRegExp filterRegExp READ proxyFilterRegExp WRITE setProxyFilterRegExp NOTIFY proxyFilterRegExpChanged) public: explicit RemoteModel(const QString &serverObject, QObject *parent = nullptr); ~RemoteModel() override; bool isConnected() const; QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; QModelIndex parent(const QModelIndex &index) const override; int rowCount(const QModelIndex &index = QModelIndex()) const override; int columnCount(const QModelIndex &index = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; Qt::ItemFlags flags(const QModelIndex &index) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override; public slots: void newMessage(const GammaRay::Message &msg); void serverRegistered(const QString &objectName, Protocol::ObjectAddress objectAddress); void serverUnregistered(const QString &objectName, Protocol::ObjectAddress objectAddress); signals: void proxyDynamicSortFilterChanged(); void proxyFilterCaseSensitivityChanged(); void proxyFilterKeyColumnChanged(); void proxyFilterRegExpChanged(); private: struct Node { // represents one row Node() = default; ~Node(); Q_DISABLE_COPY(Node) // delete all cached children data, but assume row/column count on this level is still accurate void clearChildrenData(); // forget everything we know about our children, including row/column counts void clearChildrenStructure(); // resize the initialize the column vectors void allocateColumns(); // returns whether columns are allocated bool hasColumnData() const; Node *parent = nullptr; QVector children; qint32 rowCount = -1; qint32 columnCount = -1; QVector > data; // column -> role -> data QVector flags; // column -> flags std::vector state; // column -> state (cache outdated, waiting for data, etc) int rowHint = -1; // for internal use by modelIndexForNode }; void clear(); void connectToServer(); bool checkSyncBarrier(const Message &msg); Node *nodeForIndex(const QModelIndex &index) const; Node *nodeForIndex(const Protocol::ModelIndex &index) const; QModelIndex modelIndexForNode(GammaRay::RemoteModel::Node *node, int column) const; /** Checks if @p ancestor is a (grand)parent of @p child. */ bool isAncestor(Node *ancestor, Node *child) const; RemoteModelNodeState::NodeStates stateForColumn(Node *node, int columnIndex) const; void requestRowColumnCount(const QModelIndex &index) const; void requestDataAndFlags(const QModelIndex &index) const; void requestHeaderData(Qt::Orientation orientation, int section) const; /// Reset the loading state for all rows at @p startRow or later. /// This is needed when rows have been added or removed before @p startRow, since /// pending replies might have a wrong index. void resetLoadingState(Node *node, int startRow) const; /// execute a insertRows() operation void doInsertRows(Node *parentNode, int first, int last); /// execute a removeRows() operation void doRemoveRows(Node *parentNode, int first, int last); /// execute a rowsMoved() operation void doMoveRows(Node *sourceParentNode, int sourceStart, int sourceEnd, Node *destParentNode, int destStart); /// execute a insertColumns() operation void doInsertColumns(Node *parentNode, int first, int last); /// execute a removeColumns() operation void doRemoveColumns(Node *parentNode, int first, int last); // sort/filter proxy model settings bool proxyDynamicSortFilter() const; void setProxyDynamicSortFilter(bool dynamicSortFilter); Qt::CaseSensitivity proxyFilterCaseSensitivity() const; void setProxyFilterCaseSensitivity(Qt::CaseSensitivity caseSensitivity); int proxyFilterKeyColumn() const; void setProxyFilterKeyColumn(int column); QRegExp proxyFilterRegExp() const; void setProxyFilterRegExp(const QRegExp ®Exp); private slots: void doRequests() const; private: Node *m_root; mutable QVector > m_horizontalHeaders; // section -> role -> data mutable QVector > m_verticalHeaders; // section -> role -> data enum RequestType { RowColumnCount, DataAndFlags }; mutable QMap> m_pendingRequests; QTimer *m_pendingRequestsTimer; QString m_serverObject; Protocol::ObjectAddress m_myAddress; qint32 m_currentSyncBarrier, m_targetSyncBarrier; // default data() values for empty cells static QVariant s_emptyDisplayValue; static QVariant s_emptySizeHintValue; // proxy model properties bool m_proxyDynamicSortFilter; Qt::CaseSensitivity m_proxyCaseSensitivity; int m_proxyKeyColumn; QRegExp m_proxyFilterRegExp; // hooks for unit tests static void (*s_registerClientCallback)(); void registerClient(const QString &serverObject); virtual void sendMessage(const Message &msg) const; friend class FakeRemoteModel; }; } #endif gammaray-2.11.3/client/remoteviewclient.cpp000066400000000000000000000117171413310443000207430ustar00rootroot00000000000000/* remoteviewclient.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "remoteviewclient.h" #include using namespace GammaRay; RemoteViewClient::RemoteViewClient(const QString &name, QObject *parent) : RemoteViewInterface(name, parent) { } void RemoteViewClient::requestElementsAt(const QPoint &pos, GammaRay::RemoteViewInterface::RequestMode mode) { Endpoint::instance()->invokeObject(name(), "requestElementsAt", QVariantList() << pos << QVariant::fromValue(mode)); } void RemoteViewClient::pickElementId(const GammaRay::ObjectId &id) { Endpoint::instance()->invokeObject(name(), "pickElementId", QVariantList() << QVariant::fromValue(id)); } void RemoteViewClient::sendKeyEvent(int type, int key, int modifiers, const QString &text, bool autorep, ushort count) { Endpoint::instance()->invokeObject(name(), "sendKeyEvent", QVariantList() << QVariant::fromValue(type) << QVariant::fromValue(key) << QVariant::fromValue(modifiers) << QVariant::fromValue(text) << QVariant::fromValue(autorep) << QVariant::fromValue(count)); } void RemoteViewClient::sendMouseEvent(int type, const QPoint &localPos, int button, int buttons, int modifiers) { Endpoint::instance()->invokeObject(name(), "sendMouseEvent", QVariantList() << QVariant::fromValue(type) << QVariant::fromValue(localPos) << QVariant::fromValue(button) << QVariant::fromValue(buttons) << QVariant::fromValue(modifiers)); } void RemoteViewClient::sendWheelEvent(const QPoint &localPos, QPoint pixelDelta, QPoint angleDelta, int buttons, int modifiers) { Endpoint::instance()->invokeObject(name(), "sendWheelEvent", QVariantList() << QVariant::fromValue(localPos) << QVariant::fromValue(pixelDelta) << QVariant::fromValue(angleDelta) << QVariant::fromValue(buttons) << QVariant::fromValue(modifiers)); } void RemoteViewClient::sendTouchEvent(int type, int touchDeviceType, int deviceCaps, int touchDeviceMaxTouchPoints, int modifiers, Qt::TouchPointStates touchPointStates, const QList &touchPoints) { Endpoint::instance()->invokeObject(name(), "sendTouchEvent", QVariantList() << QVariant::fromValue(type) << QVariant::fromValue(touchDeviceType) << QVariant::fromValue(deviceCaps) << QVariant::fromValue(touchDeviceMaxTouchPoints) << QVariant::fromValue(modifiers) << QVariant::fromValue(touchPointStates) << QVariant::fromValue(touchPoints)); } void RemoteViewClient::setViewActive(bool active) { Endpoint::instance()->invokeObject(name(), "setViewActive", QVariantList() << active); } void RemoteViewClient::sendUserViewport(const QRectF &userViewport) { Endpoint::instance()->invokeObject(name(), "sendUserViewport", QVariantList() << userViewport); } void RemoteViewClient::clientViewUpdated() { Endpoint::instance()->invokeObject(name(), "clientViewUpdated"); } void RemoteViewClient::requestCompleteFrame() { Endpoint::instance()->invokeObject(name(), "requestCompleteFrame"); } gammaray-2.11.3/client/remoteviewclient.h000066400000000000000000000050621413310443000204040ustar00rootroot00000000000000/* remoteviewclient.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_REMOTEVIEWCLIENT_H #define GAMMARAY_REMOTEVIEWCLIENT_H #include namespace GammaRay { class RemoteViewClient : public RemoteViewInterface { Q_OBJECT Q_INTERFACES(GammaRay::RemoteViewInterface) public: explicit RemoteViewClient(const QString &name, QObject *parent = nullptr); void requestElementsAt(const QPoint &pos, GammaRay::RemoteViewInterface::RequestMode mode) override; void pickElementId(const GammaRay::ObjectId &id) override; void sendKeyEvent(int type, int key, int modifiers, const QString &text = QString(), bool autorep = false, ushort count = 1) override; void sendMouseEvent(int type, const QPoint &localPos, int button, int buttons, int modifiers) override; void sendWheelEvent(const QPoint &localPos, QPoint pixelDelta, QPoint angleDelta, int buttons, int modifiers) override; void sendTouchEvent(int type, int touchDeviceType, int deviceCaps, int touchDeviceMaxTouchPoints, int modifiers, Qt::TouchPointStates touchPointStates, const QList &touchPoints) override; void setViewActive(bool active) override; void sendUserViewport(const QRectF &userViewport) override; void clientViewUpdated() override; void requestCompleteFrame() override; }; } #endif // GAMMARAY_REMOTEVIEWCLIENT_H gammaray-2.11.3/client/selectionmodelclient.cpp000066400000000000000000000076101413310443000215600ustar00rootroot00000000000000/* selectionmodelclient.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "selectionmodelclient.h" #include "client.h" #include using namespace GammaRay; SelectionModelClient::SelectionModelClient(const QString &objectName, QAbstractItemModel *model, QObject *parent) : NetworkSelectionModel(objectName, model, parent) , m_timer(new QTimer(this)) { m_timer->setSingleShot(true); m_timer->setInterval(125); Q_ASSERT(model); // We do use a timer to group requests to avoid network overhead auto startTimer = [this](){ m_timer->start(); }; connect(model, &QAbstractItemModel::modelAboutToBeReset, this, &SelectionModelClient::clearPendingSelection); connect(model, &QAbstractItemModel::rowsInserted, m_timer, startTimer); connect(model, &QAbstractItemModel::rowsMoved, m_timer, startTimer); connect(model, &QAbstractItemModel::columnsInserted, m_timer, startTimer); connect(model, &QAbstractItemModel::columnsMoved, m_timer, startTimer); connect(model, &QAbstractItemModel::layoutChanged, m_timer, startTimer); connect(m_timer, &QTimer::timeout, this, &SelectionModelClient::timeout); m_myAddress = Client::instance()->objectAddress(objectName); connect(Client::instance(), &Endpoint::objectRegistered, this, &SelectionModelClient::serverRegistered); connect(Client::instance(), &Endpoint::objectUnregistered, this, &SelectionModelClient::serverUnregistered); connectToServer(); } SelectionModelClient::~SelectionModelClient() { m_timer->stop(); } void SelectionModelClient::connectToServer() { if (m_myAddress == Protocol::InvalidObjectAddress) return; Client::instance()->registerMessageHandler(m_myAddress, this, "newMessage"); // There can be some delay between the selection model is created and its connections went done in the UI part. // So, let delay the request a bit, not perfect but can help. // Probably a better way would be to consider some GammaRay::Message to be pendable and put them in a pool // to be handled again later. // connectionEstablished does not seems to help here (bader). QTimer::singleShot(125, this, &SelectionModelClient::requestSelection); } void SelectionModelClient::timeout() { applyPendingSelection(); } void SelectionModelClient::serverRegistered(const QString &objectName, Protocol::ObjectAddress objectAddress) { if (objectName == m_objectName) { m_myAddress = objectAddress; connectToServer(); } } void SelectionModelClient::serverUnregistered(const QString &objectName, Protocol::ObjectAddress objectAddress) { Q_UNUSED(objectAddress); if (objectName == m_objectName) m_myAddress = Protocol::InvalidObjectAddress; } gammaray-2.11.3/client/selectionmodelclient.h000066400000000000000000000036451413310443000212310ustar00rootroot00000000000000/* selectionmodelclient.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_SELECTIONMODELCLIENT_H #define GAMMARAY_SELECTIONMODELCLIENT_H #include QT_BEGIN_NAMESPACE class QTimer; QT_END_NAMESPACE namespace GammaRay { /** Client side of the network transparent QItemSelectionModel. */ class SelectionModelClient : public NetworkSelectionModel { Q_OBJECT public: SelectionModelClient(const QString &objectName, QAbstractItemModel *model, QObject *parent); ~SelectionModelClient() override; private slots: void timeout(); void serverRegistered(const QString &objectName, Protocol::ObjectAddress objectAddress); void serverUnregistered(const QString &objectName, Protocol::ObjectAddress objectAddress); private: void connectToServer(); QTimer *m_timer; }; } #endif // GAMMARAY_SELECTIONMODELCLIENT_H gammaray-2.11.3/client/tcpclientdevice.cpp000066400000000000000000000037641413310443000205260ustar00rootroot00000000000000/* tcpclientdevice.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "tcpclientdevice.h" #include "client.h" using namespace GammaRay; TcpClientDevice::TcpClientDevice(QObject *parent) : ClientDeviceImpl(parent) { m_socket = new QTcpSocket(this); connect(m_socket, &QAbstractSocket::connected, this, &ClientDevice::connected); connect(m_socket, static_cast(&QAbstractSocket::error), this, &TcpClientDevice::socketError); } void TcpClientDevice::connectToHost() { m_socket->connectToHost(m_serverAddress.host(), m_serverAddress.port(Client::defaultPort())); } void TcpClientDevice::disconnectFromHost() { m_socket->disconnectFromHost(); } void TcpClientDevice::socketError() { if (m_socket->error() == QAbstractSocket::ConnectionRefusedError) emit transientError(); else emit persistentError(m_socket->errorString()); } gammaray-2.11.3/client/tcpclientdevice.h000066400000000000000000000030551413310443000201640ustar00rootroot00000000000000/* tcpclientdevice.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_TCPCLIENTDEVICE_H #define GAMMARAY_TCPCLIENTDEVICE_H #include "clientdevice.h" #include namespace GammaRay { class TcpClientDevice : public ClientDeviceImpl { Q_OBJECT public: explicit TcpClientDevice(QObject *parent = nullptr); void connectToHost() override; void disconnectFromHost() override; private slots: void socketError(); }; } #endif // GAMMARAY_TCPCLIENTDEVICE_H gammaray-2.11.3/client/toolmanagerclient.cpp000066400000000000000000000036261413310443000210650ustar00rootroot00000000000000/* toolmanagerclient.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "toolmanagerclient.h" #include using namespace GammaRay; ToolManagerClient::ToolManagerClient(QObject *parent) : ToolManagerInterface(parent) { } void ToolManagerClient::selectObject(const ObjectId &id, const QString &toolId) { Endpoint::instance()->invokeObject(objectName(), "selectObject", QVariantList() << QVariant::fromValue(id) << toolId); } void ToolManagerClient::requestToolsForObject(const ObjectId &id) { Endpoint::instance()->invokeObject(objectName(), "requestToolsForObject", QVariantList() << QVariant::fromValue(id)); } void ToolManagerClient::requestAvailableTools() { Endpoint::instance()->invokeObject(objectName(), "requestAvailableTools"); } gammaray-2.11.3/client/toolmanagerclient.h000066400000000000000000000033001413310443000205170ustar00rootroot00000000000000/* toolmanagerclient.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_TOOLMANAGERCLIENT_H #define GAMMARAY_TOOLMANAGERCLIENT_H #include "gammaray_ui_export.h" #include namespace GammaRay { class ToolManagerClient : public ToolManagerInterface { Q_OBJECT Q_INTERFACES(GammaRay::ToolManagerInterface) public: explicit ToolManagerClient(QObject *parent = nullptr); void selectObject(const ObjectId &id, const QString &toolId) override; void requestToolsForObject(const ObjectId &id) override; void requestAvailableTools() override; }; } #endif // GAMMARAY_TOOLMANAGERCLIENT_H gammaray-2.11.3/cmake/000077500000000000000000000000001413310443000144455ustar00rootroot00000000000000gammaray-2.11.3/cmake/CMakeLists.txt000066400000000000000000000001671413310443000172110ustar00rootroot00000000000000if(NOT GAMMARAY_PROBE_ONLY_BUILD) install(FILES GammaRayMacros.cmake DESTINATION ${CMAKECONFIG_INSTALL_DIR}) endif() gammaray-2.11.3/cmake/ECMEnableSanitizers.cmake000066400000000000000000000134231413310443000212410ustar00rootroot00000000000000# SPDX-FileCopyrightText: 2014 Mathieu Tarral # # SPDX-License-Identifier: BSD-3-Clause #[=======================================================================[.rst: ECMEnableSanitizers ------------------- Enable compiler sanitizer flags. The following sanitizers are supported: - Address Sanitizer - Memory Sanitizer - Thread Sanitizer - Leak Sanitizer - Undefined Behaviour Sanitizer All of them are implemented in Clang, depending on your version, and there is an work in progress in GCC, where some of them are currently implemented. This module will check your current compiler version to see if it supports the sanitizers that you want to enable Usage ===== Simply add:: include(ECMEnableSanitizers) to your ``CMakeLists.txt``. Note that this module is included in KDECompilerSettings, so projects using that module do not need to also include this one. The sanitizers are not enabled by default. Instead, you must set ``ECM_ENABLE_SANITIZERS`` (either in your ``CMakeLists.txt`` or on the command line) to a semicolon-separated list of sanitizers you wish to enable. The options are: - address - memory - thread - leak - undefined - fuzzer The sanitizers "address", "memory" and "thread" are mutually exclusive. You cannot enable two of them in the same build. "leak" requires the "address" sanitizer. .. note:: To reduce the overhead induced by the instrumentation of the sanitizers, it is advised to enable compiler optimizations (``-O1`` or higher). Example ======= This is an example of usage:: mkdir build cd build cmake -DECM_ENABLE_SANITIZERS='address;leak;undefined' .. .. note:: Most of the sanitizers will require Clang. To enable it, use:: -DCMAKE_CXX_COMPILER=clang++ Since 1.3.0. #]=======================================================================] # MACRO check_compiler_version #----------------------------- macro (check_compiler_version gcc_required_version clang_required_version msvc_required_version) if ( ( CMAKE_CXX_COMPILER_ID MATCHES "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS ${gcc_required_version} ) OR ( CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS ${clang_required_version} ) OR ( CMAKE_CXX_COMPILER_ID MATCHES "MSVC" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS ${msvc_required_version} ) ) # error ! message(FATAL_ERROR "You ask to enable the sanitizer ${CUR_SANITIZER}, but your compiler ${CMAKE_CXX_COMPILER_ID} version ${CMAKE_CXX_COMPILER_VERSION} does not support it ! You should use at least GCC ${gcc_required_version}, Clang ${clang_required_version} or MSVC ${msvc_required_version} (99.99 means not implemented yet)") endif () endmacro () # MACRO check_compiler_support #------------------------------ macro (enable_sanitizer_flags sanitize_option) if (${sanitize_option} MATCHES "address") check_compiler_version("4.8" "3.1" "19.28") if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC") set(XSAN_COMPILE_FLAGS "-fsanitize=address") else() set(XSAN_COMPILE_FLAGS "-fsanitize=address -fno-omit-frame-pointer -fno-optimize-sibling-calls") set(XSAN_LINKER_FLAGS "asan") endif() elseif (${sanitize_option} MATCHES "thread") check_compiler_version("4.8" "3.1" "99.99") set(XSAN_COMPILE_FLAGS "-fsanitize=thread") set(XSAN_LINKER_FLAGS "tsan") elseif (${sanitize_option} MATCHES "memory") check_compiler_version("99.99" "3.1" "99.99") set(XSAN_COMPILE_FLAGS "-fsanitize=memory") elseif (${sanitize_option} MATCHES "leak") check_compiler_version("4.9" "3.4" "99.99") set(XSAN_COMPILE_FLAGS "-fsanitize=leak") set(XSAN_LINKER_FLAGS "lsan") elseif (${sanitize_option} MATCHES "undefined") check_compiler_version("4.9" "3.1" "99.99") set(XSAN_COMPILE_FLAGS "-fsanitize=undefined -fno-omit-frame-pointer -fno-optimize-sibling-calls") elseif (${sanitize_option} MATCHES "fuzzer") check_compiler_version("99.99" "6.0" "99.99") set(XSAN_COMPILE_FLAGS "-fsanitize=fuzzer") else () message(FATAL_ERROR "Compiler sanitizer option \"${sanitize_option}\" not supported.") endif () endmacro () if (ECM_ENABLE_SANITIZERS) if (CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID MATCHES "MSVC") # for each element of the ECM_ENABLE_SANITIZERS list foreach ( CUR_SANITIZER ${ECM_ENABLE_SANITIZERS} ) # lowercase filter string(TOLOWER ${CUR_SANITIZER} CUR_SANITIZER) # check option and enable appropriate flags enable_sanitizer_flags ( ${CUR_SANITIZER} ) # TODO: GCC will not link pthread library if enabled ASan if(CMAKE_C_COMPILER_ID MATCHES "Clang") set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${XSAN_COMPILE_FLAGS}" ) endif() set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${XSAN_COMPILE_FLAGS}" ) if(CMAKE_CXX_COMPILER_ID MATCHES "GNU") link_libraries(${XSAN_LINKER_FLAGS}) endif() if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") string(REPLACE "-Wl,--no-undefined" "" CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS}") string(REPLACE "-Wl,--no-undefined" "" CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS}") endif () endforeach() else() message(STATUS "Tried to enable sanitizers (-DECM_ENABLE_SANITIZERS=${ECM_ENABLE_SANITIZERS}), \ but compiler (${CMAKE_CXX_COMPILER_ID}) does not have sanitizer support") endif() endif() gammaray-2.11.3/cmake/ECMFindModuleHelpers.cmake000066400000000000000000000323441413310443000213530ustar00rootroot00000000000000#.rst: # ECMFindModuleHelpers # -------------------- # # Helper macros for find modules: ecm_find_package_version_check(), # ecm_find_package_parse_components() and # ecm_find_package_handle_library_components(). # # :: # # ecm_find_package_version_check() # # Prints warnings if the CMake version or the project's required CMake version # is older than that required by extra-cmake-modules. # # :: # # ecm_find_package_parse_components( # RESULT_VAR # KNOWN_COMPONENTS [ [...]] # [SKIP_DEPENDENCY_HANDLING]) # # This macro will populate with a list of components found in # _FIND_COMPONENTS, after checking that all those components are in the # list of KNOWN_COMPONENTS; if there are any unknown components, it will print # an error or warning (depending on the value of _FIND_REQUIRED) and call # return(). # # The order of components in is guaranteed to match the order they # are listed in the KNOWN_COMPONENTS argument. # # If SKIP_DEPENDENCY_HANDLING is not set, for each component the variable # __component_deps will be checked for dependent components. # If is listed in _FIND_COMPONENTS, then all its (transitive) # dependencies will also be added to . # # :: # # ecm_find_package_handle_library_components( # COMPONENTS [ [...]] # [SKIP_DEPENDENCY_HANDLING]) # [SKIP_PKG_CONFIG]) # # Creates an imported library target for each component. The operation of this # macro depends on the presence of a number of CMake variables. # # The __lib variable should contain the name of this library, # and __header variable should contain the name of a header # file associated with it (whatever relative path is normally passed to # '#include'). __header_subdir variable can be used to specify # which subdirectory of the include path the headers will be found in. # ecm_find_package_components() will then search for the library # and include directory (creating appropriate cache variables) and create an # imported library target named ::. # # Additional variables can be used to provide additional information: # # If SKIP_PKG_CONFIG, the __pkg_config variable is set, and # pkg-config is found, the pkg-config module given by # __pkg_config will be searched for and used to help locate the # library and header file. It will also be used to set # __VERSION. # # Note that if version information is found via pkg-config, # __FIND_VERSION can be set to require a particular version # for each component. # # If SKIP_DEPENDENCY_HANDLING is not set, the INTERFACE_LINK_LIBRARIES property # of the imported target for will be set to contain the imported # targets for the components listed in __component_deps. # _FOUND will also be set to false if any of the compoments in # __component_deps are not found. This requires the components # in __component_deps to be listed before in the # COMPONENTS argument. # # The following variables will be set: # # ``_TARGETS`` # the imported targets # ``_LIBRARIES`` # the found libraries # ``_INCLUDE_DIRS`` # the combined required include directories for the components # ``_DEFINITIONS`` # the "other" CFLAGS provided by pkg-config, if any # ``_VERSION`` # the value of ``__VERSION`` for the first component that # has this variable set (note that components are searched for in the order # they are passed to the macro), although if it is already set, it will not # be altered # # Note that these variables are never cleared, so if # ecm_find_package_handle_library_components() is called multiple times with # different components (typically because of multiple find_package() calls) then # ``_TARGETS``, for example, will contain all the targets found in any # call (although no duplicates). # # Since pre-1.0.0. #============================================================================= # Copyright 2014 Alex Merry # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of source code must retain the copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. The name of the author may not be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. include(CMakeParseArguments) macro(ecm_find_package_version_check module_name) if(CMAKE_VERSION VERSION_LESS 2.8.12) message(FATAL_ERROR "CMake 2.8.12 is required by Find${module_name}.cmake") endif() if(CMAKE_MINIMUM_REQUIRED_VERSION VERSION_LESS 2.8.12) message(AUTHOR_WARNING "Your project should require at least CMake 2.8.12 to use Find${module_name}.cmake") endif() endmacro() macro(ecm_find_package_parse_components module_name) set(ecm_fppc_options SKIP_DEPENDENCY_HANDLING) set(ecm_fppc_oneValueArgs RESULT_VAR) set(ecm_fppc_multiValueArgs KNOWN_COMPONENTS DEFAULT_COMPONENTS) cmake_parse_arguments(ECM_FPPC "${ecm_fppc_options}" "${ecm_fppc_oneValueArgs}" "${ecm_fppc_multiValueArgs}" ${ARGN}) if(ECM_FPPC_UNPARSED_ARGUMENTS) message(FATAL_ERROR "Unexpected arguments to ecm_find_package_parse_components: ${ECM_FPPC_UNPARSED_ARGUMENTS}") endif() if(NOT ECM_FPPC_RESULT_VAR) message(FATAL_ERROR "Missing RESULT_VAR argument to ecm_find_package_parse_components") endif() if(NOT ECM_FPPC_KNOWN_COMPONENTS) message(FATAL_ERROR "Missing KNOWN_COMPONENTS argument to ecm_find_package_parse_components") endif() if(NOT ECM_FPPC_DEFAULT_COMPONENTS) set(ECM_FPPC_DEFAULT_COMPONENTS ${ECM_FPPC_KNOWN_COMPONENTS}) endif() if(${module_name}_FIND_COMPONENTS) set(ecm_fppc_requestedComps ${${module_name}_FIND_COMPONENTS}) if(NOT ECM_FPPC_SKIP_DEPENDENCY_HANDLING) # Make sure deps are included foreach(ecm_fppc_comp ${ecm_fppc_requestedComps}) foreach(ecm_fppc_dep_comp ${${module_name}_${ecm_fppc_comp}_component_deps}) list(FIND ecm_fppc_requestedComps "${ecm_fppc_dep_comp}" ecm_fppc_index) if("${ecm_fppc_index}" STREQUAL "-1") if(NOT ${module_name}_FIND_QUIETLY) message(STATUS "${module_name}: ${ecm_fppc_comp} requires ${${module_name}_${ecm_fppc_comp}_component_deps}") endif() list(APPEND ecm_fppc_requestedComps "${ecm_fppc_dep_comp}") endif() endforeach() endforeach() else() message(STATUS "Skipping dependency handling for ${module_name}") endif() list(REMOVE_DUPLICATES ecm_fppc_requestedComps) # This makes sure components are listed in the same order as # KNOWN_COMPONENTS (potentially important for inter-dependencies) set(${ECM_FPPC_RESULT_VAR}) foreach(ecm_fppc_comp ${ECM_FPPC_KNOWN_COMPONENTS}) list(FIND ecm_fppc_requestedComps "${ecm_fppc_comp}" ecm_fppc_index) if(NOT "${ecm_fppc_index}" STREQUAL "-1") list(APPEND ${ECM_FPPC_RESULT_VAR} "${ecm_fppc_comp}") list(REMOVE_AT ecm_fppc_requestedComps ${ecm_fppc_index}) endif() endforeach() # if there are any left, they are unknown components if(ecm_fppc_requestedComps) set(ecm_fppc_msgType STATUS) if(${module_name}_FIND_REQUIRED) set(ecm_fppc_msgType FATAL_ERROR) endif() if(NOT ${module_name}_FIND_QUIETLY) message(${ecm_fppc_msgType} "${module_name}: requested unknown components ${ecm_fppc_requestedComps}") endif() return() endif() else() set(${ECM_FPPC_RESULT_VAR} ${ECM_FPPC_DEFAULT_COMPONENTS}) endif() endmacro() macro(ecm_find_package_handle_library_components module_name) set(ecm_fpwc_options SKIP_PKG_CONFIG SKIP_DEPENDENCY_HANDLING) set(ecm_fpwc_oneValueArgs) set(ecm_fpwc_multiValueArgs COMPONENTS) cmake_parse_arguments(ECM_FPWC "${ecm_fpwc_options}" "${ecm_fpwc_oneValueArgs}" "${ecm_fpwc_multiValueArgs}" ${ARGN}) if(ECM_FPWC_UNPARSED_ARGUMENTS) message(FATAL_ERROR "Unexpected arguments to ecm_find_package_handle_components: ${ECM_FPWC_UNPARSED_ARGUMENTS}") endif() if(NOT ECM_FPWC_COMPONENTS) message(FATAL_ERROR "Missing COMPONENTS argument to ecm_find_package_handle_components") endif() include(FindPackageHandleStandardArgs) find_package(PkgConfig) foreach(ecm_fpwc_comp ${ECM_FPWC_COMPONENTS}) set(ecm_fpwc_dep_vars) set(ecm_fpwc_dep_targets) if(NOT SKIP_DEPENDENCY_HANDLING) foreach(ecm_fpwc_dep ${${module_name}_${ecm_fpwc_comp}_component_deps}) list(APPEND ecm_fpwc_dep_vars "${module_name}_${ecm_fpwc_dep}_FOUND") list(APPEND ecm_fpwc_dep_targets "${module_name}::${ecm_fpwc_dep}") endforeach() endif() if(NOT ECM_FPWC_SKIP_PKG_CONFIG AND ${module_name}_${ecm_fpwc_comp}_pkg_config) pkg_check_modules(PKG_${module_name}_${ecm_fpwc_comp} QUIET ${${module_name}_${ecm_fpwc_comp}_pkg_config}) endif() find_path(${module_name}_${ecm_fpwc_comp}_INCLUDE_DIR NAMES ${${module_name}_${ecm_fpwc_comp}_header} HINTS ${PKG_${module_name}_${ecm_fpwc_comp}_INCLUDE_DIRS} PATH_SUFFIXES ${${module_name}_${ecm_fpwc_comp}_header_subdir} ) find_library(${module_name}_${ecm_fpwc_comp}_LIBRARY NAMES ${${module_name}_${ecm_fpwc_comp}_lib} HINTS ${PKG_${module_name}_${ecm_fpwc_comp}_LIBRARY_DIRS} ) set(${module_name}_${ecm_fpwc_comp}_VERSION "${PKG_${module_name}_${ecm_fpwc_comp}_VERSION}") if(NOT ${module_name}_VERSION) set(${module_name}_VERSION ${${module_name}_${ecm_fpwc_comp}_VERSION}) endif() find_package_handle_standard_args(${module_name}_${ecm_fpwc_comp} FOUND_VAR ${module_name}_${ecm_fpwc_comp}_FOUND REQUIRED_VARS ${module_name}_${ecm_fpwc_comp}_LIBRARY ${module_name}_${ecm_fpwc_comp}_INCLUDE_DIR ${ecm_fpwc_dep_vars} VERSION_VAR ${module_name}_${ecm_fpwc_comp}_VERSION ) mark_as_advanced( ${module_name}_${ecm_fpwc_comp}_LIBRARY ${module_name}_${ecm_fpwc_comp}_INCLUDE_DIR ) if(${module_name}_${ecm_fpwc_comp}_FOUND) list(APPEND ${module_name}_LIBRARIES "${${module_name}_${ecm_fpwc_comp}_LIBRARY}") list(APPEND ${module_name}_INCLUDE_DIRS "${${module_name}_${ecm_fpwc_comp}_INCLUDE_DIR}") set(${module_name}_DEFINITIONS ${${module_name}_DEFINITIONS} ${PKG_${module_name}_${ecm_fpwc_comp}_DEFINITIONS}) if(NOT TARGET ${module_name}::${ecm_fpwc_comp}) add_library(${module_name}::${ecm_fpwc_comp} UNKNOWN IMPORTED) set_target_properties(${module_name}::${ecm_fpwc_comp} PROPERTIES IMPORTED_LOCATION "${${module_name}_${ecm_fpwc_comp}_LIBRARY}" INTERFACE_COMPILE_OPTIONS "${PKG_${module_name}_${ecm_fpwc_comp}_DEFINITIONS}" INTERFACE_INCLUDE_DIRECTORIES "${${module_name}_${ecm_fpwc_comp}_INCLUDE_DIR}" INTERFACE_LINK_LIBRARIES "${ecm_fpwc_dep_targets}" ) endif() list(APPEND ${module_name}_TARGETS "${module_name}::${ecm_fpwc_comp}") endif() endforeach() if(${module_name}_LIBRARIES) list(REMOVE_DUPLICATES ${module_name}_LIBRARIES) endif() if(${module_name}_INCLUDE_DIRS) list(REMOVE_DUPLICATES ${module_name}_INCLUDE_DIRS) endif() if(${module_name}_DEFINITIONS) list(REMOVE_DUPLICATES ${module_name}_DEFINITIONS) endif() if(${module_name}_TARGETS) list(REMOVE_DUPLICATES ${module_name}_TARGETS) endif() endmacro() gammaray-2.11.3/cmake/ECMGeneratePriFile.cmake000066400000000000000000000210311413310443000207760ustar00rootroot00000000000000#.rst: # ECMGeneratePriFile # ------------------ # # Generate a ``.pri`` file for the benefit of qmake-based projects. # # As well as the function below, this module creates the cache variable # ``ECM_MKSPECS_INSTALL_DIR`` and sets the default value to ``mkspecs/modules``. # This assumes Qt and the current project are both installed to the same # non-system prefix. Packagers who use ``-DCMAKE_INSTALL_PREFIX=/usr`` will # certainly want to set ``ECM_MKSPECS_INSTALL_DIR`` to something like # ``share/qt5/mkspecs/modules``. # # The main thing is that this should be the ``modules`` subdirectory of either # the default qmake ``mkspecs`` directory or of a directory that will be in the # ``$QMAKEPATH`` environment variable when qmake is run. # # :: # # ecm_generate_pri_file(BASE_NAME # LIB_NAME # [DEPS " [ [...]]"] # [FILENAME_VAR ] # [INCLUDE_INSTALL_DIR ] # [LIB_INSTALL_DIR ]) # # If your CMake project produces a Qt-based library, you may expect there to be # applications that wish to use it that use a qmake-based build system, rather # than a CMake-based one. Creating a ``.pri`` file will make use of your # library convenient for them, in much the same way that CMake config files make # things convenient for CMake-based applications. # # ecm_generate_pri_file() generates just such a file. It requires the # ``PROJECT_VERSION_STRING`` variable to be set. This is typically set by # :module:`ECMSetupVersion`, although the project() command in CMake 3.0.0 and # later can also set this. # # BASE_NAME specifies the name qmake project (.pro) files should use to refer to # the library (eg: KArchive). LIB_NAME is the name of the actual library to # link to (ie: the first argument to add_library()). DEPS is a space-separated # list of the base names of other libraries (for Qt libraries, use the same # names you use with the ``QT`` variable in a qmake project file, such as "core" # for QtCore). FILENAME_VAR specifies the name of a variable to store the path # to the generated file in. # # INCLUDE_INSTALL_DIR is the path (relative to ``CMAKE_INSTALL_PREFIX``) that # include files will be installed to. It defaults to # ``${INCLUDE_INSTALL_DIR}/`` if the ``INCLUDE_INSTALL_DIR`` variable # is set. If that variable is not set, the ``CMAKE_INSTALL_INCLUDEDIR`` variable # is used instead, and if neither are set ``include`` is used. LIB_INSTALL_DIR # operates similarly for the installation location for libraries; it defaults to # ``${LIB_INSTALL_DIR}``, ``${CMAKE_INSTALL_LIBDIR}`` or ``lib``, in that order. # # Example usage: # # .. code-block:: cmake # # ecm_generate_pri_file( # BASE_NAME KArchive # LIB_NAME KF5KArchive # DEPS "core" # FILENAME_VAR pri_filename # ) # install(FILES ${pri_filename} DESTINATION ${ECM_MKSPECS_INSTALL_DIR}) # # A qmake-based project that wished to use this would then do:: # # QT += KArchive # # in their ``.pro`` file. # # Since pre-1.0.0. #============================================================================= # Copyright 2014 David Faure # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of source code must retain the copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. The name of the author may not be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # Replicate the logic from KDEInstallDirs.cmake as we can't depend on it # Ask qmake if we're using the same prefix as Qt set(_askqmake OFF) if(NOT DEFINED KDE_INSTALL_USE_QT_SYS_PATHS) include(ECMQueryQmake) query_qmake(qt_install_prefix_dir QT_INSTALL_PREFIX) if(qt_install_prefix_dir STREQUAL "${CMAKE_INSTALL_PREFIX}") set(_askqmake ON) endif() endif() if(KDE_INSTALL_USE_QT_SYS_PATHS OR _askqmake) include(ECMQueryQmake) query_qmake(qt_host_data_dir QT_HOST_DATA) set(ECM_MKSPECS_INSTALL_DIR ${qt_host_data_dir}/mkspecs/modules CACHE PATH "The directory where mkspecs will be installed to.") else() set(ECM_MKSPECS_INSTALL_DIR mkspecs/modules CACHE PATH "The directory where mkspecs will be installed to.") endif() function(ECM_GENERATE_PRI_FILE) set(options ) set(oneValueArgs BASE_NAME LIB_NAME DEPS FILENAME_VAR INCLUDE_INSTALL_DIR LIB_INSTALL_DIR) set(multiValueArgs ) cmake_parse_arguments(EGPF "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) if(EGPF_UNPARSED_ARGUMENTS) message(FATAL_ERROR "Unknown keywords given to ECM_GENERATE_PRI_FILE(): \"${EGPF_UNPARSED_ARGUMENTS}\"") endif() if(NOT EGPF_BASE_NAME) message(FATAL_ERROR "Required argument BASE_NAME missing in ECM_GENERATE_PRI_FILE() call") endif() if(NOT EGPF_LIB_NAME) message(FATAL_ERROR "Required argument LIB_NAME missing in ECM_GENERATE_PRI_FILE() call") endif() if(NOT PROJECT_VERSION_STRING) message(FATAL_ERROR "Required variable PROJECT_VERSION_STRING not set before ECM_GENERATE_PRI_FILE() call. Did you call ecm_setup_version?") endif() if(NOT EGPF_INCLUDE_INSTALL_DIR) if(INCLUDE_INSTALL_DIR) set(EGPF_INCLUDE_INSTALL_DIR "${INCLUDE_INSTALL_DIR}/${EGPF_BASE_NAME}") elseif(CMAKE_INSTALL_INCLUDEDIR) set(EGPF_INCLUDE_INSTALL_DIR "${CMAKE_INSTALL_INCLUDEDIR}/${EGPF_BASE_NAME}") else() set(EGPF_INCLUDE_INSTALL_DIR "include/${EGPF_BASE_NAME}") endif() endif() if(NOT EGPF_LIB_INSTALL_DIR) if(LIB_INSTALL_DIR) set(EGPF_LIB_INSTALL_DIR "${LIB_INSTALL_DIR}") elseif(CMAKE_INSTALL_LIBDIR) set(EGPF_LIB_INSTALL_DIR "${CMAKE_INSTALL_LIBDIR}") else() set(EGPF_LIB_INSTALL_DIR "lib") endif() endif() string(REGEX REPLACE "^([0-9]+)\\.[0-9]+\\.[0-9]+.*" "\\1" PROJECT_VERSION_MAJOR "${PROJECT_VERSION_STRING}") string(REGEX REPLACE "^[0-9]+\\.([0-9]+)\\.[0-9]+.*" "\\1" PROJECT_VERSION_MINOR "${PROJECT_VERSION_STRING}") string(REGEX REPLACE "^[0-9]+\\.[0-9]+\\.([0-9]+).*" "\\1" PROJECT_VERSION_PATCH "${PROJECT_VERSION_STRING}") set(PRI_TARGET_BASENAME ${EGPF_BASE_NAME}) set(PRI_TARGET_LIBNAME ${EGPF_LIB_NAME}) set(PRI_TARGET_QTDEPS ${EGPF_DEPS}) if(IS_ABSOLUTE "${EGPF_INCLUDE_INSTALL_DIR}") set(PRI_TARGET_INCLUDES "${EGPF_INCLUDE_INSTALL_DIR}") else() set(PRI_TARGET_INCLUDES "${CMAKE_INSTALL_PREFIX}/${EGPF_INCLUDE_INSTALL_DIR}") endif() if(IS_ABSOLUTE "${EGPF_LIB_INSTALL_DIR}") set(PRI_TARGET_LIBS "${EGPF_LIB_INSTALL_DIR}") else() set(PRI_TARGET_LIBS "${CMAKE_INSTALL_PREFIX}/${EGPF_LIB_INSTALL_DIR}") endif() set(PRI_TARGET_DEFINES "") set(PRI_FILENAME ${CMAKE_CURRENT_BINARY_DIR}/qt_${PRI_TARGET_BASENAME}.pri) if (EGPF_FILENAME_VAR) set(${EGPF_FILENAME_VAR} ${PRI_FILENAME} PARENT_SCOPE) endif() file(GENERATE OUTPUT ${PRI_FILENAME} CONTENT "QT.${PRI_TARGET_BASENAME}.VERSION = ${PROJECT_VERSION_STRING} QT.${PRI_TARGET_BASENAME}.MAJOR_VERSION = ${PROJECT_VERSION_MAJOR} QT.${PRI_TARGET_BASENAME}.MINOR_VERSION = ${PROJECT_VERSION_MINOR} QT.${PRI_TARGET_BASENAME}.PATCH_VERSION = ${PROJECT_VERSION_PATCH} QT.${PRI_TARGET_BASENAME}.name = ${PRI_TARGET_LIBNAME} QT.${PRI_TARGET_BASENAME}.module = ${PRI_TARGET_LIBNAME} QT.${PRI_TARGET_BASENAME}.defines = ${PRI_TARGET_DEFINES} QT.${PRI_TARGET_BASENAME}.includes = ${PRI_TARGET_INCLUDES} ${PRI_TARGET_INCLUDES}/.. QT.${PRI_TARGET_BASENAME}.private_includes = QT.${PRI_TARGET_BASENAME}.libs = ${PRI_TARGET_LIBS} QT.${PRI_TARGET_BASENAME}.depends = ${PRI_TARGET_QTDEPS} " ) endfunction() gammaray-2.11.3/cmake/ECMQueryQmake.cmake000066400000000000000000000046711413310443000200700ustar00rootroot00000000000000# # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of source code must retain the copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. The name of the author may not be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. find_package(Qt5Core QUIET) if (Qt5Core_FOUND) set(_qmake_executable_default "qmake-qt5") endif () if (TARGET Qt5::qmake) get_target_property(_qmake_executable_default Qt5::qmake LOCATION) endif() set(QMAKE_EXECUTABLE ${_qmake_executable_default} CACHE FILEPATH "Location of the Qt5 qmake executable") # This is not public API (yet)! function(query_qmake result_variable qt_variable) if(NOT QMAKE_EXECUTABLE) set(${result_variable} "" PARENT_SCOPE) message(WARNING "Should specify a qmake Qt5 binary. Can't check ${qt_variable}") return() endif() execute_process( COMMAND ${QMAKE_EXECUTABLE} -query "${qt_variable}" RESULT_VARIABLE return_code OUTPUT_VARIABLE output ) if(return_code EQUAL 0) string(STRIP "${output}" output) file(TO_CMAKE_PATH "${output}" output_path) set(${result_variable} "${output_path}" PARENT_SCOPE) else() message(WARNING "Failed call: ${QMAKE_EXECUTABLE} -query \"${qt_variable}\"") message(FATAL_ERROR "QMake call failed: ${return_code}") endif() endfunction() gammaray-2.11.3/cmake/FindGlslang.cmake000066400000000000000000000072411413310443000176430ustar00rootroot00000000000000# Copyright (c) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company # All rights reserved. # # Author: Volker Krause # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of source code must retain the copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. The name of the author may not be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. find_program(GLSLANG_VALIDATOR_EXECUTABLE glslangValidator) if (GLSLANG_VALIDATOR_EXECUTABLE) set(Glslang_FOUND TRUE) endif() # Copied from ECMPoQmTools which copied it from FindGettext.cmake function(_glslang_get_unique_target_name _name _unique_name) set(propertyName "_GLSLANG_UNIQUE_COUNTER_${_name}") get_property(currentCounter GLOBAL PROPERTY "${propertyName}") if(NOT currentCounter) set(currentCounter 1) endif() set(${_unique_name} "${_name}_${currentCounter}" PARENT_SCOPE) math(EXPR currentCounter "${currentCounter} + 1") set_property(GLOBAL PROPERTY ${propertyName} ${currentCounter} ) endfunction() # validate individual shaders function(glslang_validate_shader) if (NOT GLSLANG_VALIDATOR_EXECUTABLE) return() endif() _glslang_get_unique_target_name(_validate_shader _uid) foreach(_shader ${ARGN}) get_filename_component(_shader_abs ${_shader} ABSOLUTE) add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${_shader}.validate_shader COMMAND ${GLSLANG_VALIDATOR_EXECUTABLE} ${_shader_abs} COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_BINARY_DIR}/${_shader}.validate_shader MAIN_DEPENDENCY ${_shader_abs} ) add_custom_target(${_shader}${_uid} ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${_shader}.validate_shader) endforeach() endfunction() # validate an entire shader program, ie. also check for linker errors function(glslang_validate_program) if (NOT GLSLANG_VALIDATOR_EXECUTABLE) return() endif() _glslang_get_unique_target_name(validate_program _uid) foreach(_shader ${ARGN}) get_filename_component(_shader_abs ${_shader} ABSOLUTE) get_filename_component(_shader_name ${_shader} NAME) set(_target "${_target}${_shader_name}_") list(APPEND _shaders ${_shader_abs}) endforeach() set(_target "${_target}${_uid}") add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${_target}.stamp COMMAND ${GLSLANG_VALIDATOR_EXECUTABLE} -l ${_shaders} COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_BINARY_DIR}/${_target}.stamp DEPENDS ${_shaders} ) add_custom_target(${_target} ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${_target}.stamp) endfunction() gammaray-2.11.3/cmake/FindQmlLint.cmake000066400000000000000000000055421413310443000176360ustar00rootroot00000000000000# Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company # All rights reserved. # # Author: Volker Krause # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of source code must retain the copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. The name of the author may not be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. find_program(QMLLINT_EXECUTABLE qmllint) if (QMLLINT_EXECUTABLE) if(NOT QMLLINT_IS_WORKING) # Try to fix common problems on Debian-based distros -- they provide /usr/bin/qmllint, which is a symlink to # /usr/lib/x86_64-linux-gnu/qt4/bin/qmllint (or the Qt5 version of it). The actual executable is part of different # package, so might not even be installed => double-check whether qmllint is working by executing it execute_process(COMMAND ${QMLLINT_EXECUTABLE} --version RESULT_VARIABLE _qmllint_result OUTPUT_QUIET ERROR_QUIET) if (_qmllint_result EQUAL 0) set(QMLLINT_IS_WORKING TRUE CACHE BOOL "Whether the found qmllint executable is actually usable" FORCE) endif() endif() if(QMLLINT_IS_WORKING) set(QmlLint_FOUND TRUE) endif() endif() # validate a list of qml files function(qml_lint) if (NOT QMLLINT_EXECUTABLE OR NOT QmlLint_FOUND) return() endif() foreach(_file ${ARGN}) get_filename_component(_file_abs ${_file} ABSOLUTE) add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${_file}.qmllint COMMAND ${QMLLINT_EXECUTABLE} ${_file_abs} COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_BINARY_DIR}/${_file}.qmllint MAIN_DEPENDENCY ${_file_abs} ) add_custom_target(${_file}_qmllint ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${_file}.qmllint) endforeach() endfunction() gammaray-2.11.3/cmake/FindWayland.cmake000066400000000000000000000126131413310443000176520ustar00rootroot00000000000000#.rst: # FindWayland # ----------- # # Try to find Wayland. # # This is a component-based find module, which makes use of the COMPONENTS # and OPTIONAL_COMPONENTS arguments to find_module. The following components # are available:: # # Client Server Cursor Egl # # If no components are specified, this module will act as though all components # were passed to OPTIONAL_COMPONENTS. # # This module will define the following variables, independently of the # components searched for or found: # # ``Wayland_FOUND`` # TRUE if (the requested version of) Wayland is available # ``Wayland_VERSION`` # Found Wayland version # ``Wayland_TARGETS`` # A list of all targets imported by this module (note that there may be more # than the components that were requested) # ``Wayland_LIBRARIES`` # This can be passed to target_link_libraries() instead of the imported # targets # ``Wayland_INCLUDE_DIRS`` # This should be passed to target_include_directories() if the targets are # not used for linking # ``Wayland_DEFINITIONS`` # This should be passed to target_compile_options() if the targets are not # used for linking # # For each searched-for components, ``Wayland__FOUND`` will be set to # TRUE if the corresponding Wayland library was found, and FALSE otherwise. If # ``Wayland__FOUND`` is TRUE, the imported target # ``Wayland::`` will be defined. This module will also attempt to # determine ``Wayland_*_VERSION`` variables for each imported target, although # ``Wayland_VERSION`` should normally be sufficient. # # In general we recommend using the imported targets, as they are easier to use # and provide more control. Bear in mind, however, that if any target is in the # link interface of an exported library, it must be made available by the # package config file. # # Since pre-1.0.0. #============================================================================= # Copyright 2014 Alex Merry # Copyright 2014 Martin Gräßlin # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of source code must retain the copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. The name of the author may not be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #============================================================================= include(${CMAKE_CURRENT_LIST_DIR}/ECMFindModuleHelpers.cmake) ecm_find_package_version_check(Wayland) set(Wayland_known_components Client Server Cursor Egl ) foreach(_comp ${Wayland_known_components}) string(TOLOWER "${_comp}" _lc_comp) set(Wayland_${_comp}_component_deps) set(Wayland_${_comp}_pkg_config "wayland-${_lc_comp}") set(Wayland_${_comp}_lib "wayland-${_lc_comp}") set(Wayland_${_comp}_header "wayland-${_lc_comp}.h") endforeach() set(Wayland_Egl_component_deps Client) ecm_find_package_parse_components(Wayland RESULT_VAR Wayland_components KNOWN_COMPONENTS ${Wayland_known_components} ) ecm_find_package_handle_library_components(Wayland COMPONENTS ${Wayland_components} ) # If pkg-config didn't provide us with version information, # try to extract it from wayland-version.h # (Note that the version from wayland-egl.pc will probably be # the Mesa version, rather than the Wayland version, but that # version will be ignored as we always find wayland-client.pc # first). if(NOT Wayland_VERSION) find_file(Wayland_VERSION_HEADER NAMES wayland-version.h HINTS ${Wayland_INCLUDE_DIRS} ) mark_as_advanced(Wayland_VERSION_HEADER) if(Wayland_VERSION_HEADER) file(READ ${Wayland_VERSION_HEADER} _wayland_version_header_contents) string(REGEX REPLACE "^.*[ \t]+WAYLAND_VERSION[ \t]+\"([0-9.]*)\".*$" "\\1" Wayland_VERSION "${_wayland_version_header_contents}" ) unset(_wayland_version_header_contents) endif() endif() find_package_handle_standard_args(Wayland FOUND_VAR Wayland_FOUND REQUIRED_VARS Wayland_LIBRARIES VERSION_VAR Wayland_VERSION HANDLE_COMPONENTS ) include(FeatureSummary) set_package_properties(Wayland PROPERTIES URL "https://wayland.freedesktop.org" DESCRIPTION "C library implementation of the Wayland protocol: a protocol for a compositor to talk to its clients" ) gammaray-2.11.3/cmake/GammaRayMacros.cmake000066400000000000000000000123151413310443000203140ustar00rootroot00000000000000# Copyright (c) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company # All rights reserved. # # Author: Volker Krause # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of source code must retain the copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. The name of the author may not be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. include(CMakeParseArguments) # # GAMMARAY_ADD_PLUGIN: create a gammaray plugin, install at the right place, etc # arguments: # - JSON - the plugin .json file # - SOURCES - the plugin sources # macro(gammaray_add_plugin _target_name) set(oneValueArgs JSON) set(multiValueArgs SOURCES) cmake_parse_arguments(_gammaray_add_plugin "" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) if(NOT PROBE_PLUGIN_INSTALL_DIR) # HACK for external plugins that don't set PLUGIN_INSTALL_DIR set(PROBE_PLUGIN_INSTALL_DIR ${GAMMARAY_PROBE_PLUGIN_INSTALL_DIR}) endif() set(_build_target_dir "${GAMMARAY_OUTPUT_PREFIX}/${PROBE_PLUGIN_INSTALL_DIR}") # Work-around for KDEND-44 (also see https://cmake.org/Bug/bug_relationship_graph.php?bug_id=15419) # Re-generates moc file in case the JSON file changes if (Qt5Core_FOUND AND NOT Qt5Core_VERSION VERSION_LESS 5.6.0) # DEPENDS argument for qt5_wrap_cpp was added in 5.6.0 list(GET _gammaray_add_plugin_SOURCES 0 mainSourceFile) string(REPLACE ".cpp" ".h" mainHeaderFile ${mainSourceFile}) # sanity check file(READ ${mainHeaderFile} mainHeaderFileContents) string(FIND "${mainHeaderFileContents}" "Q_PLUGIN_METADATA" hasPluginMetadataMacroMatchRes) if (hasPluginMetadataMacroMatchRes EQUAL -1) message(FATAL_ERROR "First file passed to SOURCES must be the .cpp file which includes the header using the Q_PLUGIN_METADATA macro") endif() qt5_wrap_cpp(_gammaray_add_plugin_SOURCES ${mainHeaderFile} DEPENDS ${_gammaray_add_plugin_JSON} TARGET ${_target_name}) set_source_files_properties("${mainHeaderFile}" PROPERTIES SKIP_AUTOMOC TRUE) set_source_files_properties("${mainSourceFile}" PROPERTIES SKIP_AUTOMOC TRUE) set_property(SOURCE ${CMAKE_CURRENT_BINARY_DIR}/moc_${mainSourceFile} PROPERTY SKIP_AUTOGEN ON) # workaround AUTOUIC failing on files with SKIP_AUTOMOC enabled string(REPLACE ".cpp" ".ui" mainUiFile ${mainSourceFile}) if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${mainUiFile}") qt5_wrap_ui(_gammaray_add_plugin_SOURCES ${mainUiFile}) set_property(SOURCE ${CMAKE_CURRENT_BINARY_DIR}/ui_${mainHeaderFile} PROPERTY SKIP_AUTOGEN ON) endif() endif() add_library(${_target_name} ${GAMMARAY_PLUGIN_TYPE} ${_gammaray_add_plugin_SOURCES}) set_target_properties(${_target_name} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${_build_target_dir} ) set_target_properties(${_target_name} PROPERTIES PREFIX "") if(GAMMARAY_STATIC_PROBE) set_target_properties(${_target_name} PROPERTIES COMPILE_DEFINITIONS QT_STATICPLUGIN) endif() if(GAMMARAY_INSTALL_QT_LAYOUT) set_target_properties(${_target_name} PROPERTIES OUTPUT_NAME ${_target_name}-${GAMMARAY_PROBE_ABI}) endif() gammaray_set_rpath(${_target_name} ${PROBE_PLUGIN_INSTALL_DIR}) install(TARGETS ${_target_name} DESTINATION ${PROBE_PLUGIN_INSTALL_DIR}) if(MSVC) install(FILES "$/$" DESTINATION ${PROBE_PLUGIN_INSTALL_DIR} CONFIGURATIONS Debug RelWithDebInfo) endif() endmacro() # Common RPATH setup # @internal function(gammaray_set_rpath _target_name _install_prefix) get_filename_component(_clean_prefix "${CMAKE_INSTALL_PREFIX}/${_install_prefix}" ABSOLUTE) file(RELATIVE_PATH _relative_rpath ${_clean_prefix} "${CMAKE_INSTALL_PREFIX}/${LIB_INSTALL_DIR}") if (UNIX AND NOT APPLE) set(_rpath ${CMAKE_INSTALL_RPATH}) list(INSERT _rpath 0 "\$ORIGIN/${_relative_rpath}") set_target_properties(${_target_name} PROPERTIES INSTALL_RPATH "${_rpath}") endif() if (APPLE) set_target_properties(${_target_name} PROPERTIES INSTALL_RPATH "@loader_path/${_relative_rpath}") endif() endfunction() gammaray-2.11.3/cmake/GammaRayMacrosInternal.cmake000066400000000000000000000130251413310443000220100ustar00rootroot00000000000000# GammaRay-specific CMake macros that don't make sense outside of the GammaRay source tree. # Copyright (c) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company # All rights reserved. # # Author: Volker Krause # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of source code must retain the copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. The name of the author may not be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # Drop-in replacement for CMake's option() # This version takes care of adding feature info to FeatureSummary for this option # # Usage: # gammaray_option(GAMMARAY_MULTI_BUILD "Build multiple applicable probe configurations." ON) # set(_gammaray_macros_internal_location ${CMAKE_CURRENT_LIST_DIR}) function(gammaray_option option description) set(extra_option_arguments ${ARGN}) option(${option} "${description}" ${extra_option_arguments}) add_feature_info("Option ${option}" ${option} "${description}") endfunction() macro(gammaray_target_relocatable_interfaces _paths) # See https://cmake.org/cmake/help/v3.3/manual/cmake-packages.7.html#creating-relocatable-packages get_filename_component(_dir ${CMAKE_CURRENT_SOURCE_DIR} NAME) # Allow include like: # #include # #include # #include set(${_paths} "$/${INCLUDE_INSTALL_DIR}/${_dir}" "$/${INCLUDE_INSTALL_DIR}" "$/${INCLUDE_INSTALL_DIR}/.." ) endmacro() function(gammaray_install_headers) file(RELATIVE_PATH _dir ${CMAKE_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}) cmake_parse_arguments(HEADER_INSTALL "" "DESTINATION" "" ${ARGN}) if(NOT HEADER_INSTALL_DESTINATION) set(HEADER_INSTALL_DESTINATION ${_dir}) endif() if(NOT GAMMARAY_PROBE_ONLY_BUILD) install(FILES ${HEADER_INSTALL_UNPARSED_ARGUMENTS} DESTINATION ${INCLUDE_INSTALL_DIR}/${HEADER_INSTALL_DESTINATION}) endif() set_directory_properties(PROPERTIES GAMMARAY_INSTALLED_HEADERS "${HEADER_INSTALL_UNPARSED_ARGUMENTS}") get_property(_include_dirs GLOBAL PROPERTY GAMMARAY_HEADER_DIRS) list(APPEND _include_dirs "${_dir}") set_property(GLOBAL PROPERTY GAMMARAY_HEADER_DIRS "${_include_dirs}") endfunction() macro(gammaray_all_installed_headers _var) set(${_var} "") get_property(_include_dirs GLOBAL PROPERTY GAMMARAY_HEADER_DIRS) foreach(_dir ${_include_dirs}) get_directory_property(_hdrs DIRECTORY ${CMAKE_SOURCE_DIR}/${_dir} GAMMARAY_INSTALLED_HEADERS) foreach(_hdr ${_hdrs}) if(IS_ABSOLUTE ${_hdr}) list(APPEND ${_var} ${_hdr}) else() list(APPEND ${_var} "${CMAKE_SOURCE_DIR}/${_dir}/${_hdr}") endif() endforeach() endforeach() endmacro() macro(gammaray_join_list _var _sep) set(${_var} "") foreach(_element ${ARGN}) set(${_var} "${${_var}}${_sep}\"${_element}\"") endforeach() endmacro() macro(gammaray_convert_to_relative_path _var) # Make sure _var is a relative path if(IS_ABSOLUTE "${${_var}}") file(RELATIVE_PATH ${_var} "${CMAKE_INSTALL_PREFIX}" "${${_var}}") endif() endmacro() macro(gammaray_inverse_dir _var _prefix) # strip out relative components, those break the following on OSX get_filename_component(_clean_prefix "${CMAKE_INSTALL_PREFIX}/${_prefix}" ABSOLUTE) file(RELATIVE_PATH ${_var} ${_clean_prefix} "${CMAKE_INSTALL_PREFIX}") endmacro() # embed an Info.plist file into a non-bundled Mac executable macro(gammaray_embed_info_plist _target _plist) configure_file(${_plist} ${CMAKE_CURRENT_BINARY_DIR}/${_target}_Info.plist) if(APPLE) set_target_properties(${_target} PROPERTIES LINK_FLAGS "-sectcreate __TEXT __info_plist ${CMAKE_CURRENT_BINARY_DIR}/${_target}_Info.plist") endif() endmacro() macro(gammaray_add_win_icon _sources) if (WIN32) configure_file(${_gammaray_macros_internal_location}/gammaray.rc.cmake gammaray.rc) LIST(APPEND ${_sources} ${CMAKE_CURRENT_BINARY_DIR}/gammaray.rc) endif() endmacro() # allow to use CMake FeatureSummary with "packages" that consist only of a minor inline check # rather than a fully-featured find module macro(gammaray_add_dummy_package _package _found) if(${_found}) set(_property_name "PACKAGES_FOUND") else() set(_property_name "PACKAGES_NOT_FOUND") endif() get_property(_packages GLOBAL PROPERTY ${_property_name}) list(APPEND _packages ${_package}) set_property(GLOBAL PROPERTY ${_property_name} "${_packages}") endmacro() gammaray-2.11.3/cmake/GammaRayProbeABI.cmake000066400000000000000000000131771413310443000204620ustar00rootroot00000000000000# Determine probe ABI identifier # # Copyright (c) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company # All rights reserved. # # Author: Volker Krause # Author: Filipe Azevedo # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of source code must retain the copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. The name of the author may not be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # This contains all properties that define ABI compatibility of a probe with a target # Qt version set(GAMMARAY_PROBE_ABI "qt${Qt5Core_VERSION_MAJOR}_${Qt5Core_VERSION_MINOR}") # on Windows, the compiler also matters if(WIN32) set(GAMMARAY_PROBE_ABI "${GAMMARAY_PROBE_ABI}-${CMAKE_CXX_COMPILER_ID}") if(MSVC) # based on FindBoost.cmake if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 20) message(FATAL_ERROR "Unsupported compiler version detected ${CMAKE_CXX_COMPILER_VERSION}") elseif (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19) set(GAMMARAY_COMPILER_VERSION "140") elseif(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 18) set(GAMMARAY_COMPILER_VERSION "120") elseif(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 17) set(GAMMARAY_COMPILER_VERSION "110") elseif(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 16) set(GAMMARAY_COMPILER_VERSION "100") else() message(FATAL_ERROR "Unsupported compiler version detected, please use at least MSVC2010") endif() set(GAMMARAY_PROBE_ABI "${GAMMARAY_PROBE_ABI}-${GAMMARAY_COMPILER_VERSION}") endif() endif() # processor architecture # on Windows our best bet is CMAKE_SIZEOF_VOID_P and assuming a x86 host==target build if(WIN32) if(CMAKE_SIZEOF_VOID_P EQUAL 8) set(GAMMARAY_PROBE_ABI "${GAMMARAY_PROBE_ABI}-x86_64") else() set(GAMMARAY_PROBE_ABI "${GAMMARAY_PROBE_ABI}-i686") endif() # on Mac we apparently always get i386 on x86 elseif(APPLE) if(NOT CMAKE_SYSTEM_PROCESSOR) message(FATAL_ERROR "Unknown target architecture. Make sure to specify CMAKE_SYSTEM_PROCESSOR in your toolchain file!") endif() if(CMAKE_SYSTEM_PROCESSOR MATCHES "i386" AND CMAKE_SIZEOF_VOID_P EQUAL 8) set(GAMMARAY_PROBE_ABI "${GAMMARAY_PROBE_ABI}-x86_64") elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64" AND CMAKE_SIZEOF_VOID_P EQUAL 4) set(GAMMARAY_PROBE_ABI "${GAMMARAY_PROBE_ABI}-i686") else() set(GAMMARAY_PROBE_ABI "${GAMMARAY_PROBE_ABI}-${CMAKE_SYSTEM_PROCESSOR}") endif() # on Android we derive this from ANDROID_ABI elseif(ANDROID OR CMAKE_SYSTEM_NAME MATCHES "Android") if(NOT ANDROID_ABI) message(FATAL_ERROR "Unknown target ABI. Make sure ANDROID_ABI is set!") endif() if(ANDROID_ABI MATCHES "arm64") set(GAMMARAY_PROBE_ABI "${GAMMARAY_PROBE_ABI}-aarch64") elseif(ANDROID_ABI MATCHES "arm" ) set(GAMMARAY_PROBE_ABI "${GAMMARAY_PROBE_ABI}-arm") elseif(ANDROID_ABI STREQUAL "x86_64" ) set(GAMMARAY_PROBE_ABI "${GAMMARAY_PROBE_ABI}-x86_64") elseif(ANDROID_ABI STREQUAL "x86" ) set(GAMMARAY_PROBE_ABI "${GAMMARAY_PROBE_ABI}-i686") else() message(FATAL_ERROR "Unsupported Android ABI ${ANDROID_ABI}.") endif() else() if(NOT CMAKE_SYSTEM_PROCESSOR) message(FATAL_ERROR "Unknown target architecture. Make sure to specify CMAKE_SYSTEM_PROCESSOR in your toolchain file!") endif() # uname reports different ARM versions, unlike ELF, so map all this to "arm" # also, there are Yocto supplied toolchain files out there, reporting "cortexa9[hf]-neon-..." or similar here if(CMAKE_SYSTEM_PROCESSOR MATCHES "arm" OR CMAKE_SYSTEM_PROCESSOR MATCHES "cortex" OR CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64") if(CMAKE_SIZEOF_VOID_P EQUAL 8) set(GAMMARAY_PROBE_ABI "${GAMMARAY_PROBE_ABI}-aarch64") else() set(GAMMARAY_PROBE_ABI "${GAMMARAY_PROBE_ABI}-arm") endif() else() if(CMAKE_SYSTEM_PROCESSOR MATCHES "i[3456]86" OR CMAKE_SYSTEM_PROCESSOR MATCHES "x86" OR CMAKE_SYSTEM_PROCESSOR MATCHES "corei" OR CMAKE_SYSTEM_PROCESSOR MATCHES "amd64") if(CMAKE_SIZEOF_VOID_P EQUAL 8) set(GAMMARAY_PROBE_ABI "${GAMMARAY_PROBE_ABI}-x86_64") else() set(GAMMARAY_PROBE_ABI "${GAMMARAY_PROBE_ABI}-i686") endif() else() # subsequent code assumes there is no '-' in the architecture identifier string(REPLACE "-" "_" _clean_processor ${CMAKE_SYSTEM_PROCESSOR}) set(GAMMARAY_PROBE_ABI "${GAMMARAY_PROBE_ABI}-${_clean_processor}") endif() endif() endif() message(STATUS "Building probe for ABI: ${GAMMARAY_PROBE_ABI} (${CMAKE_BUILD_TYPE})") gammaray-2.11.3/cmake/PackageIFW.cmake000066400000000000000000000073351413310443000173600ustar00rootroot00000000000000# # Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company # All rights reserved. # # Author: Andras Mantia # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of source code must retain the copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. The name of the author may not be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # Creates targets for IFW packages. # # Usage: # addPackageTarget(package_name build_target install_target package_type) # # Where: # - package_name is the name of the package (application), like GammaRay # - build_target is the name of the make target used to build the required code (eg. src, sub-src, lib, docs, etc) # - install_target is the name of the make target used to install the required code (eg. install, install_docs, etc) # - package_type is the type of the package. Should be one of lib, doc, examples, tools # # # For each type there will be a target created, eg. packagelib, packagedoc, etc. and there will be # a master target called packages. Running # # make packages # # will create the 7z packages for all added targets. get_filename_component(PACKAGE_PREFIX ${QT_INSTALL_PREFIX} NAME) set(INSTALL_PATH "${CMAKE_BINARY_DIR}/install/${Qt5Core_VERSION}/${PACKAGE_PREFIX}") set(PACKAGE_PATH "${CMAKE_BINARY_DIR}/install/") add_custom_target(packages) # Used to serialize the package creation, otherwise the packaging for different types will # conflict due to installing to the same place set(LAST_TARGET "") macro(addPackageTarget packageName dependsTarget installTarget type) set(PACKAGE_LIB_FILE "${CMAKE_BINARY_DIR}/${packageName}-${type}-${Qt5Core_VERSION}-${PACKAGE_PREFIX}.7z") set(PACKAGE_TARGET "${type}_package") add_custom_target(${PACKAGE_TARGET} DEPENDS "${LAST_TARGET}" COMMAND echo "Creating IFW package for ${packageName} of type ${type}: ${PACKAGE_LIB_FILE}" COMMAND ${CMAKE_COMMAND} -E remove_directory "${CMAKE_BINARY_DIR}/${installTarget}" COMMAND ${CMAKE_COMMAND} -E remove -f ${PACKAGE_LIB_FILE} COMMAND ${CMAKE_COMMAND} ${CMAKE_SOURCE_DIR} -DCMAKE_INSTALL_PREFIX="${INSTALL_PATH}" -DBUILD_TESTING=Off COMMAND ${CMAKE_COMMAND} -E chdir "${CMAKE_BINARY_DIR}" "${CMAKE_MAKE_PROGRAM}" ${installTarget} COMMAND ${CMAKE_COMMAND} -E chdir "${PACKAGE_PATH}" 7z a -t7z -m0=lzma -mx=9 -mfb=64 -md=32m -ms=on ${PACKAGE_LIB_FILE} . COMMAND ${CMAKE_COMMAND} -E remove_directory "${CMAKE_BINARY_DIR}/${installTarget}" COMMAND echo "Generated package file: ${PACKAGE_LIB_FILE}" ) add_dependencies(packages ${PACKAGE_TARGET}) set(LAST_TARGET ${PACKAGE_TARGET}) endmacro() gammaray-2.11.3/cmake/QtInstallPaths.cmake000066400000000000000000000054341413310443000203700ustar00rootroot00000000000000# # Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company # All rights reserved. # # Author: Allen Winter # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of source code must retain the copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. The name of the author may not be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # Create variables for all the various install paths for the Qt version in use # Make sure to have found Qt5 before using this. # sets variables like QT_INSTALL_PREFIX, QT_INSTALL_DATA, QT_INSTALL_DOCS, etc. # run qmake -query to see a full list if(TARGET Qt5::qmake) get_target_property(QT_QMAKE_EXECUTABLE Qt5::qmake LOCATION) else() message(FATAL_ERROR "No supported Qt version found. Make sure you find Qt before calling this") endif() execute_process( COMMAND ${QT_QMAKE_EXECUTABLE} -query RESULT_VARIABLE return_code OUTPUT_VARIABLE ALL_VARS ) if(NOT return_code EQUAL 0) message(WARNING "Failed call: ${QMAKE_EXECUTABLE} -query") message(FATAL_ERROR "QMake call failed: ${return_code}") endif() string(REPLACE "\n" ";" VARS_LIST ${ALL_VARS}) foreach(QVAL ${VARS_LIST}) if(QVAL MATCHES "QT_INSTALL_") string(REPLACE ":" ";" QVAL_LIST ${QVAL}) list(LENGTH QVAL_LIST listlen) list(GET QVAL_LIST 0 var) if(WIN32 AND ${listlen} GREATER 2) list(GET QVAL_LIST 2 path) list(GET QVAL_LIST 1 drive) set(path "${drive}:${path}") else() list(GET QVAL_LIST 1 path) endif() if(NOT ${var}) #if set already on the command line for example set(${var} ${path} CACHE PATH "Qt install path for ${var}") endif() endif() endforeach() gammaray-2.11.3/cmake/Toolchain-Linux-GCC-i686.cmake000066400000000000000000000034151413310443000215530ustar00rootroot00000000000000# Toolchain file for 32bit builds on 64bit Linux hosts # # Copyright (c) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company # All rights reserved. # # Author: Volker Krause # Author: Filipe Azevedo # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of source code must retain the copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. The name of the author may not be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. set(CMAKE_SYSTEM_NAME "Linux") set(CMAKE_SYSTEM_PROCESSOR "i686") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -m32") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -m32") gammaray-2.11.3/cmake/Toolchain-OSX-GCC-i686.cmake000066400000000000000000000034141413310443000211240ustar00rootroot00000000000000# Toolchain file for 32bit builds on 64bit OSX hosts # # Copyright (c) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company # All rights reserved. # # Author: Volker Krause # Author: Filipe Azevedo # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of source code must retain the copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. The name of the author may not be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. set(CMAKE_SYSTEM_NAME "Darwin") set(CMAKE_SYSTEM_PROCESSOR "i686") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -m32") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -m32") gammaray-2.11.3/cmake/Toolchain-QNX-common.cmake000066400000000000000000000065641413310443000213340ustar00rootroot00000000000000# # (C) Copyright 2009 Johns Hopkins University (JHU), All Rights # Reserved. # # --- begin cisst license - do not edit --- # # This software is provided "as is" under an open source license, with # no warranty. The complete license can be found in license.txt and # http://www.cisst.org/cisst/license.txt. # # --- end cisst license --- SET(CMAKE_SYSTEM_NAME QNX) SET(CMAKE_SYSTEM_VERSION ${QNX_VERSION}) SET(CMAKE_SYSTEM_PROCESSOR ${QNX_PROCESSOR}) SET(TOOLCHAIN QNX) #SET(CMAKE_IMPORT_LIBRARY_SUFFIX ".a") SET(CMAKE_SHARED_LIBRARY_PREFIX "lib") SET(CMAKE_SHARED_LIBRARY_SUFFIX ".so") SET(CMAKE_STATIC_LIBRARY_PREFIX "lib") SET(CMAKE_STATIC_LIBRARY_SUFFIX ".a") IF(CMAKE_HOST_WIN32) SET(HOST_EXECUTABLE_SUFFIX ".exe") ENDIF(CMAKE_HOST_WIN32) FIND_PATH(QNX_HOST NAME usr/bin/qcc${HOST_EXECUTABLE_SUFFIX} PATHS $ENV{QNX_HOST} ${QNX_HOST_HINT} NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH ) FIND_PATH(QNX_TARGET NAME usr/include/qconfig.mk PATHS $ENV{QNX_TARGET} ${QNX_TARGET_HINT} NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH ) IF(CMAKE_HOST_WIN32) FIND_PATH(QNX_CONFIGURATION NAME bin/qnxactivate.exe PATHS $ENV{QNX_CONFIGURATION} "C:/Program Files/QNX Software Systems/qconfig" NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH ) ENDIF(CMAKE_HOST_WIN32) SET(ENV{QNX_HOST} ${QNX_HOST}) SET(ENV{QNX_TARGET} ${QNX_TARGET}) IF(CMAKE_HOST_WIN32) SET(ENV{QNX_CONFIGURATION} ${QNX_CONFIGURATION}) SET(ENV{PATH} "$ENV{PATH};${QNX_HOST}/usr/bin") ENDIF(CMAKE_HOST_WIN32) if(CMAKE_GENERATOR MATCHES "Makefiles") SET(CMAKE_MAKE_PROGRAM "${QNX_HOST}/usr/bin/make${HOST_EXECUTABLE_SUFFIX}" CACHE PATH "QNX Make Program") endif() SET(CMAKE_SH "${QNX_HOST}/usr/bin/sh${HOST_EXECUTABLE_SUFFIX}" CACHE PATH "QNX shell Program") SET(CMAKE_AR "${QNX_HOST}/usr/bin/nto${CMAKE_SYSTEM_PROCESSOR}-ar${HOST_EXECUTABLE_SUFFIX}" CACHE PATH "QNX ar Program") SET(CMAKE_RANLIB "${QNX_HOST}/usr/bin/nto${CMAKE_SYSTEM_PROCESSOR}-ranlib${HOST_EXECUTABLE_SUFFIX}" CACHE PATH "QNX ranlib Program") SET(CMAKE_NM "${QNX_HOST}/usr/bin/nto${CMAKE_SYSTEM_PROCESSOR}-nm${HOST_EXECUTABLE_SUFFIX}" CACHE PATH "QNX nm Program") SET(CMAKE_OBJCOPY "${QNX_HOST}/usr/bin/nto${CMAKE_SYSTEM_PROCESSOR}-objcopy${HOST_EXECUTABLE_SUFFIX}" CACHE PATH "QNX objcopy Program") SET(CMAKE_OBJDUMP "${QNX_HOST}/usr/bin/nto${CMAKE_SYSTEM_PROCESSOR}-objdump${HOST_EXECUTABLE_SUFFIX}" CACHE PATH "QNX objdump Program") SET(CMAKE_LINKER "${QNX_HOST}/usr/bin/nto${CMAKE_SYSTEM_PROCESSOR}-ld${HOST_EXECUTABLE_SUFFIX}" CACHE PATH "QNX Linker Program") SET(CMAKE_STRIP "${QNX_HOST}/usr/bin/nto${CMAKE_SYSTEM_PROCESSOR}-strip${HOST_EXECUTABLE_SUFFIX}" CACHE PATH "QNX Strip Program") SET(CMAKE_C_COMPILER ${QNX_HOST}/usr/bin/qcc${HOST_EXECUTABLE_SUFFIX}) SET(CMAKE_C_FLAGS_DEBUG "-g") SET(CMAKE_C_FLAGS_MINSIZEREL "-Os -DNDEBUG") SET(CMAKE_C_FLAGS_RELEASE "-O3 -DNDEBUG") SET(CMAKE_C_FLAGS_RELWITHDEBINFO "-O2 -g") SET(CMAKE_CXX_COMPILER ${QNX_HOST}/usr/bin/qcc${HOST_EXECUTABLE_SUFFIX}) SET(CMAKE_CXX_FLAGS_DEBUG "-g") SET(CMAKE_CXX_FLAGS_MINSIZEREL "-Os -DNDEBUG") SET(CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG") SET(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g") LIST(APPEND CMAKE_FIND_ROOT_PATH ${QNX_TARGET}) SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) gammaray-2.11.3/cmake/Toolchain-QNX650-armv7.cmake000066400000000000000000000066651413310443000213350ustar00rootroot00000000000000# # (C) Copyright 2009 Johns Hopkins University (JHU), All Rights # Reserved. # # --- begin cisst license - do not edit --- # # This software is provided "as is" under an open source license, with # no warranty. The complete license can be found in license.txt and # http://www.cisst.org/cisst/license.txt. # # --- end cisst license --- SET(CMAKE_SYSTEM_NAME QNX) SET(CMAKE_SYSTEM_VERSION 6.5.0) SET(CMAKE_SYSTEM_PROCESSOR armv7) SET(TOOLCHAIN QNX) #SET(CMAKE_IMPORT_LIBRARY_SUFFIX ".a") SET(CMAKE_SHARED_LIBRARY_PREFIX "lib") SET(CMAKE_SHARED_LIBRARY_SUFFIX ".so") SET(CMAKE_STATIC_LIBRARY_PREFIX "lib") SET(CMAKE_STATIC_LIBRARY_SUFFIX ".a") IF(CMAKE_HOST_WIN32) SET(HOST_EXECUTABLE_SUFFIX ".exe") ENDIF(CMAKE_HOST_WIN32) FIND_PATH(QNX_HOST NAME usr/bin/qcc${HOST_EXECUTABLE_SUFFIX} PATHS $ENV{QNX_HOST} C:/QNX650/host/win32/x86 NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH ) FIND_PATH(QNX_TARGET NAME usr/include/qnx_errno.h PATHS $ENV{QNX_TARGET} C:/QNX650/target/qnx6 NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH ) IF(CMAKE_HOST_WIN32) FIND_PATH(QNX_CONFIGURATION NAME bin/qnxactivate.exe PATHS $ENV{QNX_CONFIGURATION} "C:/Program Files/QNX Software Systems/qconfig" NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH ) ENDIF(CMAKE_HOST_WIN32) SET(ENV{QNX_HOST} ${QNX_HOST}) SET(ENV{QNX_TARGET} ${QNX_TARGET}) IF(CMAKE_HOST_WIN32) SET(ENV{QNX_CONFIGURATION} ${QNX_CONFIGURATION}) SET(ENV{PATH} "$ENV{PATH};${QNX_HOST}/usr/bin") ENDIF(CMAKE_HOST_WIN32) SET(CMAKE_MAKE_PROGRAM "${QNX_HOST}/usr/bin/make${HOST_EXECUTABLE_SUFFIX}" CACHE PATH "QNX Make Program") SET(CMAKE_SH "${QNX_HOST}/usr/bin/sh${HOST_EXECUTABLE_SUFFIX}" CACHE PATH "QNX shell Program") SET(CMAKE_AR "${QNX_HOST}/usr/bin/nto${CMAKE_SYSTEM_PROCESSOR}-ar${HOST_EXECUTABLE_SUFFIX}" CACHE PATH "QNX ar Program") SET(CMAKE_RANLIB "${QNX_HOST}/usr/bin/nto${CMAKE_SYSTEM_PROCESSOR}-ranlib${HOST_EXECUTABLE_SUFFIX}" CACHE PATH "QNX ranlib Program") SET(CMAKE_NM "${QNX_HOST}/usr/bin/nto${CMAKE_SYSTEM_PROCESSOR}-nm${HOST_EXECUTABLE_SUFFIX}" CACHE PATH "QNX nm Program") SET(CMAKE_OBJCOPY "${QNX_HOST}/usr/bin/nto${CMAKE_SYSTEM_PROCESSOR}-objcopy${HOST_EXECUTABLE_SUFFIX}" CACHE PATH "QNX objcopy Program") SET(CMAKE_OBJDUMP "${QNX_HOST}/usr/bin/nto${CMAKE_SYSTEM_PROCESSOR}-objdump${HOST_EXECUTABLE_SUFFIX}" CACHE PATH "QNX objdump Program") SET(CMAKE_LINKER "${QNX_HOST}/usr/bin/nto${CMAKE_SYSTEM_PROCESSOR}-ld" CACHE PATH "QNX Linker Program") SET(CMAKE_STRIP "${QNX_HOST}/usr/bin/nto${CMAKE_SYSTEM_PROCESSOR}-strip${HOST_EXECUTABLE_SUFFIX}" CACHE PATH "QNX Strip Program") SET(CMAKE_C_COMPILER ${QNX_HOST}/usr/bin/qcc${HOST_EXECUTABLE_SUFFIX}) SET(CMAKE_C_FLAGS_DEBUG "-g") SET(CMAKE_C_FLAGS_MINSIZEREL "-Os -DNDEBUG") SET(CMAKE_C_FLAGS_RELEASE "-O3 -DNDEBUG") SET(CMAKE_C_FLAGS_RELWITHDEBINFO "-O2 -g") SET(CMAKE_CXX_COMPILER ${QNX_HOST}/usr/bin/qcc${HOST_EXECUTABLE_SUFFIX}) SET(CMAKE_CXX_FLAGS_DEBUG "-g") SET(CMAKE_CXX_FLAGS_MINSIZEREL "-Os -DNDEBUG") SET(CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG") SET(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g") SET(CMAKE_FIND_ROOT_PATH ${QNX_TARGET}) set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) SET(CMAKE_C_FLAGS "-Vgcc_ntoarmv7le" CACHE STRING "qcc c flags" FORCE) SET(CMAKE_CXX_FLAGS "-Vgcc_ntoarmv7le -lang-c++" CACHE STRING "qcc cxx flags" FORCE) gammaray-2.11.3/cmake/Toolchain-QNX650-x86.cmake000066400000000000000000000066531413310443000207230ustar00rootroot00000000000000# # (C) Copyright 2009 Johns Hopkins University (JHU), All Rights # Reserved. # # --- begin cisst license - do not edit --- # # This software is provided "as is" under an open source license, with # no warranty. The complete license can be found in license.txt and # http://www.cisst.org/cisst/license.txt. # # --- end cisst license --- SET(CMAKE_SYSTEM_NAME QNX) SET(CMAKE_SYSTEM_VERSION 6.5.0) SET(CMAKE_SYSTEM_PROCESSOR x86) SET(TOOLCHAIN QNX) #SET(CMAKE_IMPORT_LIBRARY_SUFFIX ".a") SET(CMAKE_SHARED_LIBRARY_PREFIX "lib") SET(CMAKE_SHARED_LIBRARY_SUFFIX ".so") SET(CMAKE_STATIC_LIBRARY_PREFIX "lib") SET(CMAKE_STATIC_LIBRARY_SUFFIX ".a") IF(CMAKE_HOST_WIN32) SET(HOST_EXECUTABLE_SUFFIX ".exe") ENDIF(CMAKE_HOST_WIN32) FIND_PATH(QNX_HOST NAME usr/bin/qcc${HOST_EXECUTABLE_SUFFIX} PATHS $ENV{QNX_HOST} C:/QNX650/host/win32/x86 NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH ) FIND_PATH(QNX_TARGET NAME usr/include/qnx_errno.h PATHS $ENV{QNX_TARGET} C:/QNX650/target/qnx6 NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH ) IF(CMAKE_HOST_WIN32) FIND_PATH(QNX_CONFIGURATION NAME bin/qnxactivate.exe PATHS $ENV{QNX_CONFIGURATION} "C:/Program Files/QNX Software Systems/qconfig" NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH ) ENDIF(CMAKE_HOST_WIN32) SET(ENV{QNX_HOST} ${QNX_HOST}) SET(ENV{QNX_TARGET} ${QNX_TARGET}) IF(CMAKE_HOST_WIN32) SET(ENV{QNX_CONFIGURATION} ${QNX_CONFIGURATION}) SET(ENV{PATH} "$ENV{PATH};${QNX_HOST}/usr/bin") ENDIF(CMAKE_HOST_WIN32) SET(CMAKE_MAKE_PROGRAM "${QNX_HOST}/usr/bin/make${HOST_EXECUTABLE_SUFFIX}" CACHE PATH "QNX Make Program") SET(CMAKE_SH "${QNX_HOST}/usr/bin/sh${HOST_EXECUTABLE_SUFFIX}" CACHE PATH "QNX shell Program") SET(CMAKE_AR "${QNX_HOST}/usr/bin/nto${CMAKE_SYSTEM_PROCESSOR}-ar${HOST_EXECUTABLE_SUFFIX}" CACHE PATH "QNX ar Program") SET(CMAKE_RANLIB "${QNX_HOST}/usr/bin/nto${CMAKE_SYSTEM_PROCESSOR}-ranlib${HOST_EXECUTABLE_SUFFIX}" CACHE PATH "QNX ranlib Program") SET(CMAKE_NM "${QNX_HOST}/usr/bin/nto${CMAKE_SYSTEM_PROCESSOR}-nm${HOST_EXECUTABLE_SUFFIX}" CACHE PATH "QNX nm Program") SET(CMAKE_OBJCOPY "${QNX_HOST}/usr/bin/nto${CMAKE_SYSTEM_PROCESSOR}-objcopy${HOST_EXECUTABLE_SUFFIX}" CACHE PATH "QNX objcopy Program") SET(CMAKE_OBJDUMP "${QNX_HOST}/usr/bin/nto${CMAKE_SYSTEM_PROCESSOR}-objdump${HOST_EXECUTABLE_SUFFIX}" CACHE PATH "QNX objdump Program") SET(CMAKE_LINKER "${QNX_HOST}/usr/bin/nto${CMAKE_SYSTEM_PROCESSOR}-ld" CACHE PATH "QNX Linker Program") SET(CMAKE_STRIP "${QNX_HOST}/usr/bin/nto${CMAKE_SYSTEM_PROCESSOR}-strip${HOST_EXECUTABLE_SUFFIX}" CACHE PATH "QNX Strip Program") SET(CMAKE_C_COMPILER ${QNX_HOST}/usr/bin/qcc${HOST_EXECUTABLE_SUFFIX}) SET(CMAKE_C_FLAGS_DEBUG "-g") SET(CMAKE_C_FLAGS_MINSIZEREL "-Os -DNDEBUG") SET(CMAKE_C_FLAGS_RELEASE "-O3 -DNDEBUG") SET(CMAKE_C_FLAGS_RELWITHDEBINFO "-O2 -g") SET(CMAKE_CXX_COMPILER ${QNX_HOST}/usr/bin/qcc${HOST_EXECUTABLE_SUFFIX}) SET(CMAKE_CXX_FLAGS_DEBUG "-g") SET(CMAKE_CXX_FLAGS_MINSIZEREL "-Os -DNDEBUG") SET(CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG") SET(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g") SET(CMAKE_FIND_ROOT_PATH ${QNX_TARGET}) set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) SET(CMAKE_C_FLAGS "-Vgcc_ntox86" CACHE STRING "qcc c flags" FORCE) SET(CMAKE_CXX_FLAGS "-Vgcc_ntox86 -lang-c++" CACHE STRING "qcc cxx flags" FORCE) gammaray-2.11.3/cmake/Toolchain-QNX660-armv7.cmake000066400000000000000000000013001413310443000213130ustar00rootroot00000000000000# # (C) Copyright 2009 Johns Hopkins University (JHU), All Rights # Reserved. # # --- begin cisst license - do not edit --- # # This software is provided "as is" under an open source license, with # no warranty. The complete license can be found in license.txt and # http://www.cisst.org/cisst/license.txt. # # --- end cisst license --- set(QNX_VERSION 6.6.0) set(QNX_PROCESSOR armv7) set(QNX_HOST_HINT "C:/qnx660/host/win32/x86") set(QNX_TARGET_HINT "C:/qnx660/target/qnx6") set(CMAKE_C_FLAGS "-Vgcc_ntoarmv7le" CACHE STRING "qcc c flags" FORCE) set(CMAKE_CXX_FLAGS "-Vgcc_ntoarmv7le -lang-c++" CACHE STRING "qcc cxx flags" FORCE) include("${CMAKE_CURRENT_LIST_DIR}/Toolchain-QNX660-common.cmake") gammaray-2.11.3/cmake/Toolchain-QNX660-common.cmake000066400000000000000000000041711413310443000215600ustar00rootroot00000000000000include("${CMAKE_CURRENT_LIST_DIR}/Toolchain-QNX-common.cmake") # QNX 6.6.0 ships with a QCC 4.7.3 (based on the same GCC version) # # HACK: CMake does not support getting compile features from QCC # work-around by telling CMake the available compile features manually set(CMAKE_CXX_COMPILE_FEATURES "cxx_std_98;cxx_template_template_parameters;cxx_std_11;cxx_alias_templates;cxx_auto_type;cxx_constexpr;cxx_decltype;cxx_default_function_template_args;cxx_defaulted_functions;cxx_defaulted_move_initializers;cxx_delegating_constructors;cxx_deleted_functions;cxx_enum_forward_declarations;cxx_explicit_conversions;cxx_extended_friend_declarations;cxx_extern_templates;cxx_final;cxx_func_identifier;cxx_generalized_initializers;cxx_inline_namespaces;cxx_lambdas;cxx_local_type_template_args;cxx_long_long_type;cxx_noexcept;cxx_nonstatic_member_init;cxx_nullptr;cxx_override;cxx_range_for;cxx_raw_string_literals;cxx_right_angle_brackets;cxx_rvalue_references;cxx_sizeof_member;cxx_static_assert;cxx_strong_enums;cxx_trailing_return_types;cxx_unicode_literals;cxx_uniform_initialization;cxx_unrestricted_unions;cxx_user_literals;cxx_variadic_macros;cxx_variadic_templates") set(CMAKE_CXX98_COMPILE_FEATURES "cxx_std_98;cxx_template_template_parameters") set(CMAKE_CXX11_COMPILE_FEATURES "cxx_std_11;cxx_alias_templates;cxx_auto_type;cxx_constexpr;cxx_decltype;cxx_default_function_template_args;cxx_defaulted_functions;cxx_defaulted_move_initializers;cxx_delegating_constructors;cxx_deleted_functions;cxx_enum_forward_declarations;cxx_explicit_conversions;cxx_extended_friend_declarations;cxx_extern_templates;cxx_final;cxx_func_identifier;cxx_generalized_initializers;cxx_inline_namespaces;cxx_lambdas;cxx_local_type_template_args;cxx_long_long_type;cxx_noexcept;cxx_nonstatic_member_init;cxx_nullptr;cxx_override;cxx_range_for;cxx_raw_string_literals;cxx_right_angle_brackets;cxx_rvalue_references;cxx_sizeof_member;cxx_static_assert;cxx_strong_enums;cxx_trailing_return_types;cxx_unicode_literals;cxx_uniform_initialization;cxx_unrestricted_unions;cxx_user_literals;cxx_variadic_macros;cxx_variadic_templates") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") gammaray-2.11.3/cmake/Toolchain-QNX660-x86.cmake000066400000000000000000000012661413310443000207170ustar00rootroot00000000000000# # (C) Copyright 2009 Johns Hopkins University (JHU), All Rights # Reserved. # # --- begin cisst license - do not edit --- # # This software is provided "as is" under an open source license, with # no warranty. The complete license can be found in license.txt and # http://www.cisst.org/cisst/license.txt. # # --- end cisst license --- set(QNX_VERSION 6.6.0) set(QNX_PROCESSOR x86) set(QNX_HOST_HINT "C:/qnx660/host/win32/x86") set(QNX_TARGET_HINT "C:/qnx660/target/qnx6") set(CMAKE_C_FLAGS "-Vgcc_ntox86" CACHE STRING "qcc c flags" FORCE) set(CMAKE_CXX_FLAGS "-Vgcc_ntox86 -lang-c++" CACHE STRING "qcc cxx flags" FORCE) include("${CMAKE_CURRENT_LIST_DIR}/Toolchain-QNX660-common.cmake") gammaray-2.11.3/cmake/Toolchain-QNX700-aarch64.cmake000066400000000000000000000035621413310443000215160ustar00rootroot00000000000000# # Copyright (C) 2018-2021 Klarälvdalens Datakonsult AB, a KDAB Group company # All rights reserved. # # Author: Kevin Funk # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of source code must retain the copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. The name of the author may not be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. set(QNX_VERSION 7.0.0) set(QNX_PROCESSOR aarch64) set(QNX_HOST_HINT "C:/qnx700/host/win32/x86") set(QNX_TARGET_HINT "C:/qnx700/target/qnx6") set(CMAKE_C_FLAGS "-Vgcc_ntoaarch64le" CACHE STRING "qcc c flags" FORCE) set(CMAKE_CXX_FLAGS "-Vgcc_ntoaarch64le -lang-c++" CACHE STRING "qcc cxx flags" FORCE) include("${CMAKE_CURRENT_LIST_DIR}/Toolchain-QNX700-common.cmake") gammaray-2.11.3/cmake/Toolchain-QNX700-armv7.cmake000066400000000000000000000035541413310443000213230ustar00rootroot00000000000000# # Copyright (C) 2018-2021 Klarälvdalens Datakonsult AB, a KDAB Group company # All rights reserved. # # Author: Kevin Funk # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of source code must retain the copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. The name of the author may not be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. set(QNX_VERSION 7.0.0) set(QNX_PROCESSOR armv7) set(QNX_HOST_HINT "C:/qnx700/host/win32/x86") set(QNX_TARGET_HINT "C:/qnx700/target/qnx6") set(CMAKE_C_FLAGS "-Vgcc_ntoarmv7le" CACHE STRING "qcc c flags" FORCE) set(CMAKE_CXX_FLAGS "-Vgcc_ntoarmv7le -lang-c++" CACHE STRING "qcc cxx flags" FORCE) include("${CMAKE_CURRENT_LIST_DIR}/Toolchain-QNX700-common.cmake") gammaray-2.11.3/cmake/Toolchain-QNX700-common.cmake000066400000000000000000000063051413310443000215540ustar00rootroot00000000000000include("${CMAKE_CURRENT_LIST_DIR}/Toolchain-QNX-common.cmake") # QNX 7.0.0 ships with a QCC 5.4.0 (based on the same GCC version) # # HACK: CMake does not support getting compile features from QCC # work-around by telling CMake the available compile features manually # Easy to figure out by forcing GCC onto CMake, e.g. via -DCMAKE_CXX_COMPILER=/opt/qnx700/host/linux/x86_64/usr/bin/arm-unknown-nto-qnx7.0.0eabi-gcc # Then read out ./CMakeFiles/*/CMakeCXXCompiler.cmake set(CMAKE_CXX_COMPILE_FEATURES "cxx_std_98;cxx_template_template_parameters;cxx_std_11;cxx_alias_templates;cxx_alignas;cxx_alignof;cxx_attributes;cxx_auto_type;cxx_constexpr;cxx_decltype;cxx_decltype_incomplete_return_types;cxx_default_function_template_args;cxx_defaulted_functions;cxx_defaulted_move_initializers;cxx_delegating_constructors;cxx_deleted_functions;cxx_enum_forward_declarations;cxx_explicit_conversions;cxx_extended_friend_declarations;cxx_extern_templates;cxx_final;cxx_func_identifier;cxx_generalized_initializers;cxx_inheriting_constructors;cxx_inline_namespaces;cxx_lambdas;cxx_local_type_template_args;cxx_long_long_type;cxx_noexcept;cxx_nonstatic_member_init;cxx_nullptr;cxx_override;cxx_range_for;cxx_raw_string_literals;cxx_reference_qualified_functions;cxx_right_angle_brackets;cxx_rvalue_references;cxx_sizeof_member;cxx_static_assert;cxx_strong_enums;cxx_thread_local;cxx_trailing_return_types;cxx_unicode_literals;cxx_uniform_initialization;cxx_unrestricted_unions;cxx_user_literals;cxx_variadic_macros;cxx_variadic_templates;cxx_std_14;cxx_aggregate_default_initializers;cxx_attribute_deprecated;cxx_binary_literals;cxx_contextual_conversions;cxx_decltype_auto;cxx_digit_separators;cxx_generic_lambdas;cxx_lambda_init_captures;cxx_relaxed_constexpr;cxx_return_type_deduction;cxx_variable_templates;cxx_std_17") set(CMAKE_CXX98_COMPILE_FEATURES "cxx_std_98;cxx_template_template_parameters") set(CMAKE_CXX11_COMPILE_FEATURES "cxx_std_11;cxx_alias_templates;cxx_alignas;cxx_alignof;cxx_attributes;cxx_auto_type;cxx_constexpr;cxx_decltype;cxx_decltype_incomplete_return_types;cxx_default_function_template_args;cxx_defaulted_functions;cxx_defaulted_move_initializers;cxx_delegating_constructors;cxx_deleted_functions;cxx_enum_forward_declarations;cxx_explicit_conversions;cxx_extended_friend_declarations;cxx_extern_templates;cxx_final;cxx_func_identifier;cxx_generalized_initializers;cxx_inheriting_constructors;cxx_inline_namespaces;cxx_lambdas;cxx_local_type_template_args;cxx_long_long_type;cxx_noexcept;cxx_nonstatic_member_init;cxx_nullptr;cxx_override;cxx_range_for;cxx_raw_string_literals;cxx_reference_qualified_functions;cxx_right_angle_brackets;cxx_rvalue_references;cxx_sizeof_member;cxx_static_assert;cxx_strong_enums;cxx_thread_local;cxx_trailing_return_types;cxx_unicode_literals;cxx_uniform_initialization;cxx_unrestricted_unions;cxx_user_literals;cxx_variadic_macros;cxx_variadic_templates") set(CMAKE_CXX14_COMPILE_FEATURES "cxx_std_14;cxx_aggregate_default_initializers;cxx_attribute_deprecated;cxx_binary_literals;cxx_contextual_conversions;cxx_decltype_auto;cxx_digit_separators;cxx_generic_lambdas;cxx_lambda_init_captures;cxx_relaxed_constexpr;cxx_return_type_deduction;cxx_variable_templates") set(CMAKE_CXX17_COMPILE_FEATURES "cxx_std_17") gammaray-2.11.3/cmake/Toolchain-QNX700-x86.cmake000066400000000000000000000035421413310443000207110ustar00rootroot00000000000000# # Copyright (C) 2018-2021 Klarälvdalens Datakonsult AB, a KDAB Group company # All rights reserved. # # Author: Kevin Funk # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of source code must retain the copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. The name of the author may not be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. set(QNX_VERSION 7.0.0) set(QNX_PROCESSOR x86) set(QNX_HOST_HINT "C:/qnx700/host/win32/x86") set(QNX_TARGET_HINT "C:/qnx700/target/qnx6") set(CMAKE_C_FLAGS "-Vgcc_ntox86" CACHE STRING "qcc c flags" FORCE) set(CMAKE_CXX_FLAGS "-Vgcc_ntox86 -lang-c++" CACHE STRING "qcc cxx flags" FORCE) include("${CMAKE_CURRENT_LIST_DIR}/Toolchain-QNX700-common.cmake") gammaray-2.11.3/cmake/Toolchain-QNX700-x86_64.cmake000066400000000000000000000035531413310443000212240ustar00rootroot00000000000000# # Copyright (C) 2018-2021 Klarälvdalens Datakonsult AB, a KDAB Group company # All rights reserved. # # Author: Kevin Funk # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of source code must retain the copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. The name of the author may not be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. set(QNX_VERSION 7.0.0) set(QNX_PROCESSOR x86_64) set(QNX_HOST_HINT "C:/qnx700/host/win32/x86") set(QNX_TARGET_HINT "C:/qnx700/target/qnx6") set(CMAKE_C_FLAGS "-Vgcc_ntox86_64" CACHE STRING "qcc c flags" FORCE) set(CMAKE_CXX_FLAGS "-Vgcc_ntox86_64 -lang-c++" CACHE STRING "qcc cxx flags" FORCE) include("${CMAKE_CURRENT_LIST_DIR}/Toolchain-QNX700-common.cmake") gammaray-2.11.3/cmake/Toolchain-RPI.cmake000066400000000000000000000044061413310443000200230ustar00rootroot00000000000000# Basic cmake toolchain file for RaspberryPi # Assumptions: toolchain is in path, $SYSROOT points to the sysroot # # Copyright (c) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company # All rights reserved. # # Author: Volker Krause # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of source code must retain the copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. The name of the author may not be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. set(CMAKE_SYSTEM_NAME "Linux") set(CMAKE_SYSTEM_PROCESSOR "armv6l") set(CMAKE_C_COMPILER "arm-linux-gnueabihf-gcc") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --sysroot=$ENV{SYSROOT}") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --sysroot=$ENV{SYSROOT}") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} --sysroot=$ENV{SYSROOT}") set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} --sysroot=$ENV{SYSROOT}") set(CMAKE_FIND_ROOT_PATH "$ENV{SYSROOT}") set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) gammaray-2.11.3/cmake/Toolchain-Yocto.cmake000066400000000000000000000046631413310443000204730ustar00rootroot00000000000000# Basic cmake toolchain file for Qt for Yocto Environment # Assumptions: toolchain script is sourced # # Copyright (c) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company # All rights reserved. # # Author: Christoph Sterz # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of source code must retain the copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. The name of the author may not be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. set(CMAKE_SYSTEM_NAME "Linux") if(DEFINED ENV{ARCH}) #$ARCH is set through the yocto environment script, use this set(CMAKE_SYSTEM_PROCESSOR "$ENV{ARCH}") elseif(DEFINED ENV{CC}) #No $ARCH found, trying to deduce processor from -march= flag in CC string(REGEX MATCH "-march=([^\ ]+)" DUMMY "$ENV{CC}") set(CMAKE_SYSTEM_PROCESSOR ${CMAKE_MATCH_1}) else() message(FATAL_ERROR "Could not find processor architecture (no ARCH or CC found in environment).") endif() set(OE_QMAKE_PATH_EXTERNAL_HOST_BINS "$ENV{OE_QMAKE_PATH_HOST_BINS}") set(CMAKE_FIND_ROOT_PATH "$ENV{SDKTARGETSYSROOT}") set(CMAKE_SYSROOT "$ENV{SDKTARGETSYSROOT}") set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) gammaray-2.11.3/cmake/Toolchain-blackberry-armv7le.cmake000066400000000000000000000050111413310443000230550ustar00rootroot00000000000000# Basic cmake toolchain file for BlackBerry 10 # Copyright (c) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company # All rights reserved. # # Author: Rafael Roquetto # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of source code must retain the copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. The name of the author may not be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # the name of the target operating system set(CMAKE_SYSTEM_NAME QNX) set(CMAKE_SYSTEM_PROCESSOR "armv7le") # which compilers to use for C and C++ set(arch gcc_ntoarmv7le) set(CMAKE_C_COMPILER qcc -V${arch}) set(CMAKE_CXX_COMPILER QCC -V${arch}) # here is the target environment located set(CMAKE_FIND_ROOT_PATH $ENV{QNX_TARGET}/armle-v7 $ENV{QNX_TARGET}) if(CMAKE_HOST_WIN32) set(HOST_EXECUTABLE_SUFFIX ".exe") endif() set(CMAKE_AR "$ENV{QNX_HOST}/usr/bin/ntoarmv7-ar${HOST_EXECUTABLE_SUFFIX}" CACHE PATH "QNX ar Program") set(CMAKE_RANLIB "$ENV{QNX_HOST}/usr/bin/ntoarmv7-ranlib${HOST_EXECUTABLE_SUFFIX}" CACHE PATH "QNX ar Program") # adjust the default behaviour of the FIND_XXX() commands: # search headers and libraries in the target environment, search # programs in the host environment set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) gammaray-2.11.3/cmake/Toolchain-iMX6.cmake000066400000000000000000000046321413310443000201550ustar00rootroot00000000000000# Basic cmake toolchain file for Freescale iMX6 # Assumptions: toolchain is in path, $SYSROOT points to the sysroot # # Copyright (c) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company # All rights reserved. # # Author: Volker Krause # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of source code must retain the copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. The name of the author may not be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. set(CMAKE_SYSTEM_NAME "Linux") set(CMAKE_SYSTEM_PROCESSOR "armv7-a") set(CMAKE_C_COMPILER "arm-linux-gcc") set(CMAKE_CXX_COMPILER "arm-linux-g++") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --sysroot=$ENV{SYSROOT} -march=armv7-a -mfpu=neon") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --sysroot=$ENV{SYSROOT} -march=armv7-a -mfpu=neon") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --sysroot=$ENV{SYSROOT}") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} --sysroot=$ENV{SYSROOT}") set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} --sysroot=$ENV{SYSROOT}") set(CMAKE_FIND_ROOT_PATH "$ENV{SYSROOT}") set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) gammaray-2.11.3/cmake/Toolchain-jetson-tk1.cmake000066400000000000000000000045701413310443000213720ustar00rootroot00000000000000# Basic cmake toolchain file for Freescale iMX6 # Assumptions: toolchain is in path, $SYSROOT points to the sysroot # # Copyright (c) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company # All rights reserved. # # Author: Volker Krause # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of source code must retain the copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. The name of the author may not be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. set(CMAKE_SYSTEM_NAME "Linux") set(CMAKE_SYSTEM_PROCESSOR "armv7-a") set(CMAKE_C_COMPILER "arm-linux-gnueabihf-gcc") set(CMAKE_CXX_COMPILER "arm-linux-gnueabihf-g++") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mtune=cortex-a15 -march=armv7-a -mfpu=neon-vfpv4") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mtune=cortex-a15 -march=armv7-a -mfpu=neon-vfpv4") set(CMAKE_EXE_LINKER_FLAGS "--sysroot=$ENV{SYSROOT} -Wl,-rpath-link,$ENV{SYSROOT}/usr/lib/arm-linux-gnueabihf/tegra" CACHE STRING "executable linker flags" FORCE) set(CMAKE_SYSROOT "$ENV{SYSROOT}") set(CMAKE_FIND_ROOT_PATH "$ENV{SYSROOT}") set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) gammaray-2.11.3/cmake/gammaray.rc.cmake000066400000000000000000000001531413310443000176470ustar00rootroot00000000000000IDI_ICON1 ICON DISCARDABLE "${PROJECT_SOURCE_DIR}/ui/resources/gammaray/GammaRay.ico" gammaray-2.11.3/com.kdab.GammaRay.metainfo.xml000066400000000000000000000050261413310443000210660ustar00rootroot00000000000000 GammaRay.desktop GFDL-1.3 GPL-2.0+ GammaRay Klaralvdalens Datakonsult AB (KDAB) Qt-application inspection and manipulation tool

GammaRay is a tool to poke around in a Qt-application and also to manipulate the application to some extent. GammaRay uses various DLL injection techniques to hook into an application at runtime and provide access to a lot of interesting information.

GammaRay provides the following capabilities to help your Qt application development efforts:

  • Browse the QObject tree with live updates
  • View, and to some extent, edit QObject static and dynamic properties
  • View and call slots of a QObject (similar to qdbusviewer)
  • View other QObject elements such as signals, enums and class info introspectively
  • List all QObject inbound and outbound signal/slot connections
  • Provide live widget preview. Useful for finding layout issues
  • View the content of any QAbstractItemModel (QAIM)
  • Browse the QAbstractProxyModel (QAPM) hierarchy
  • Browse the QGraphicsView (QGV) item tree of any QGV scene
  • ... and much, much more
https://www.kdab.com/wp-content/uploads/stories/software/gammaray2.png https://www.kdab.com/wp-content/uploads/stories/software/gammaray1.png https://www.kdab.com/wp-content/uploads/stories/software/gammaray4_text.png https://www.kdab.com/wp-content/uploads/stories/software/gammaray-traffic-light.png https://www.kdab.com/gammaray/ https://github.com/KDAB/GammaRay/issues https://github.com/KDAB/GammaRay/wiki allen.winter@kdab.com HiDpiIcon ModernToolkit
gammaray-2.11.3/common/000077500000000000000000000000001413310443000146555ustar00rootroot00000000000000gammaray-2.11.3/common/CMakeLists.txt000066400000000000000000000100551413310443000174160ustar00rootroot00000000000000# generate the class icon index map file(READ "../ui/resources/classiconindex.map" _map_content) string(REPLACE "\n" ";" _map_content ${_map_content}) set(CLASS_ICON_INDEX_CODE "") foreach(_icon_data IN LISTS _map_content) if(_icon_data) string(REPLACE " " ";" _icon_data ${_icon_data}) list(GET _icon_data 0 _icon_name) set(CLASS_ICON_INDEX_CODE "${CLASS_ICON_INDEX_CODE}\n ADD_ICON(\"${_icon_name}\")") endif() endforeach() configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/classesiconsindex_data.cpp.in ${CMAKE_CURRENT_BINARY_DIR}/classesiconsindex_data.cpp ) # # Shared non-UI code between probe and client # set(gammaray_common_srcs ${CMAKE_SOURCE_DIR}/3rdparty/lz4/lz4.c methodargument.cpp objectbroker.cpp protocol.cpp message.cpp endpoint.cpp paths.cpp propertysyncer.cpp modelevent.cpp modelutils.cpp objectidfilterproxymodel.cpp paintanalyzerinterface.cpp selflocator.cpp sourcelocation.cpp translator.cpp enumdefinition.cpp enumrepository.cpp enumvalue.cpp classesiconsrepository.cpp remoteviewinterface.cpp remoteviewframe.cpp transferimage.cpp commonutils.cpp ) add_library(gammaray_common ${GAMMARAY_LIBRARY_TYPE} ${gammaray_common_srcs}) generate_export_header(gammaray_common) set_target_properties(gammaray_common PROPERTIES ${GAMMARAY_DEFAULT_LIBRARY_PROPERTIES} OUTPUT_NAME gammaray_common-${GAMMARAY_PROBE_ABI} ) target_compile_features(gammaray_common PUBLIC ${GAMMARAY_REQUIRED_CXX_FEATURES}) gammaray_target_relocatable_interfaces(gammaray_common_ipaths) target_include_directories(gammaray_common PUBLIC $ $) target_link_libraries(gammaray_common LINK_PUBLIC Qt5::Core LINK_PRIVATE Qt5::Network Qt5::Gui) target_link_libraries(gammaray_common LINK_PRIVATE gammaray_kitemmodels ${CMAKE_DL_LIBS}) gammaray_set_rpath(gammaray_common ${LIB_INSTALL_DIR}) set(gammaray_common_internal_srcs plugininfo.cpp pluginmanager.cpp proxyfactorybase.cpp propertycontrollerinterface.cpp probecontrollerinterface.cpp toolmanagerinterface.cpp networkselectionmodel.cpp streamoperators.cpp tools/objectinspector/propertiesextensioninterface.cpp tools/objectinspector/methodsextensioninterface.cpp tools/objectinspector/connectionsextensioninterface.cpp tools/messagehandler/messagehandlerinterface.cpp tools/metatypebrowser/metatypebrowserinterface.cpp tools/problemreporter/problemreporterinterface.cpp tools/resourcebrowser/resourcebrowserinterface.cpp ) add_library(gammaray_common_internal STATIC ${gammaray_common_internal_srcs}) set_target_properties(gammaray_common_internal PROPERTIES POSITION_INDEPENDENT_CODE ON) target_compile_features(gammaray_common_internal PUBLIC ${GAMMARAY_REQUIRED_CXX_FEATURES}) target_link_libraries(gammaray_common_internal gammaray_common) target_link_libraries(gammaray_common_internal Qt5::Core Qt5::Network) if(NOT GAMMARAY_PROBE_ONLY_BUILD) install(TARGETS gammaray_common EXPORT GammaRayTargets ${INSTALL_TARGETS_DEFAULT_ARGS}) gammaray_install_headers( ${CMAKE_CURRENT_BINARY_DIR}/gammaray_common_export.h endpoint.h enumdefinition.h enumrepository.h enumvalue.h metatypedeclarations.h modelroles.h objectbroker.h objectid.h objectmodel.h objectidfilterproxymodel.h paths.h probecontrollerinterface.h propertycontrollerinterface.h protocol.h sourcelocation.h translator.h commonutils.h ) ecm_generate_pri_file(BASE_NAME GammaRayCommon LIB_NAME gammaray_common-${GAMMARAY_PROBE_ABI} DEPS "core gui network" FILENAME_VAR PRI_FILENAME INCLUDE_INSTALL_DIR ${INCLUDE_INSTALL_DIR} ) install(FILES ${PRI_FILENAME} DESTINATION ${ECM_MKSPECS_INSTALL_DIR}) else() install(TARGETS gammaray_common ${INSTALL_TARGETS_DEFAULT_ARGS}) endif() if(MSVC) install(FILES "$/$" DESTINATION ${BIN_INSTALL_DIR} CONFIGURATIONS Debug RelWithDebInfo) endif() gammaray-2.11.3/common/classesiconsindex.cpp000066400000000000000000000037041413310443000211060ustar00rootroot00000000000000/* classesiconsindex.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Filipe Azevedo Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "classesiconsindex.h" #include "classesiconsindex_data.cpp" using namespace GammaRay; ClassesIconsIndex::ConstIterator ClassesIconsIndex::constBegin() { if (s_keyToNumber.isEmpty()) { initializeClassesIconsIndex(); } return s_keyToNumber.constBegin(); } ClassesIconsIndex::ConstIterator ClassesIconsIndex::constEnd() { if (s_keyToNumber.isEmpty()) { initializeClassesIconsIndex(); } return s_keyToNumber.constEnd(); } int ClassesIconsIndex::iconIdForName(const QString &name) { if (s_keyToNumber.isEmpty()) { initializeClassesIconsIndex(); } return s_keyToNumber.value(name, -1); } QString ClassesIconsIndex::iconNameForId(int id) { if (s_numberToKey.isEmpty()) { initializeClassesIconsIndex(); } return s_numberToKey.value(id, QString()); } gammaray-2.11.3/common/classesiconsindex.h000066400000000000000000000031671413310443000205560ustar00rootroot00000000000000/* classesiconsindex.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Filipe Azevedo Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_CLASSESICONSINDEX_H #define GAMMARAY_CLASSESICONSINDEX_H #include "gammaray_common_export.h" #include #include namespace GammaRay { namespace ClassesIconsIndex { typedef QHash::const_iterator ConstIterator; GAMMARAY_COMMON_EXPORT ConstIterator constBegin(); GAMMARAY_COMMON_EXPORT ConstIterator constEnd(); GAMMARAY_COMMON_EXPORT int iconIdForName(const QString &name); GAMMARAY_COMMON_EXPORT QString iconNameForId(int id); } } #endif gammaray-2.11.3/common/classesiconsindex_data.cpp.in000066400000000000000000000031411413310443000224770ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Filipe Azevedo Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include namespace GammaRay { namespace ClassesIconsIndex { static QHash s_numberToKey; static QHash s_keyToNumber; static void initializeClassesIconsIndex() { Q_ASSERT(s_numberToKey.isEmpty()); Q_ASSERT(s_keyToNumber.isEmpty()); int i = 0; #define ADD_ICON(x) \ s_numberToKey[i] = QLatin1String(":/gammaray/icons/ui/classes/" x); \ s_keyToNumber[s_numberToKey[i]] = i; \ ++i; @CLASS_ICON_INDEX_CODE@ #undef ADD_ICON } } } gammaray-2.11.3/common/classesiconsrepository.cpp000066400000000000000000000040241413310443000222120ustar00rootroot00000000000000/* classesiconsrepository.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Filipe Azevedo Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "classesiconsrepository.h" #include #include using namespace GammaRay; ClassesIconsRepository::ClassesIconsRepository(QObject *parent) : QObject(parent) { ObjectBroker::registerObject(this); } ClassesIconsRepository::~ClassesIconsRepository() = default; QString ClassesIconsRepository::filePath(int id) const { if (id >= m_iconsIndex.size() || id < 0) return QString(); return m_iconsIndex.at(id); } ClassesIconsRepository::ConstIterator ClassesIconsRepository::constBegin() { return m_iconsIndex.constBegin(); } ClassesIconsRepository::ConstIterator ClassesIconsRepository::constEnd() { return m_iconsIndex.constEnd(); } QVector ClassesIconsRepository::index() const { return m_iconsIndex; } void ClassesIconsRepository::setIndex(const QVector &index) { m_iconsIndex = index; } gammaray-2.11.3/common/classesiconsrepository.h000066400000000000000000000052061413310443000216620ustar00rootroot00000000000000/* classesiconsrepository.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Filipe Azevedo Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_CLASSESICONSREPOSITORY_H #define GAMMARAY_CLASSESICONSREPOSITORY_H #include "gammaray_common_export.h" #include #include namespace GammaRay { /*! Collection of classes icons id/file path and on-demand client/server transfer of those. */ class GAMMARAY_COMMON_EXPORT ClassesIconsRepository : public QObject { Q_OBJECT public: ~ClassesIconsRepository() override; /*! Request the icon file path for the given icon id. * On the client side the response can be invalid on first request, * as transfer from the server is asynchronous. Listen to the * indexReceived() signal to be notified for its availability * in that case. */ virtual QString filePath(int id) const; using ConstIterator = QVector::const_iterator; ConstIterator constBegin(); ConstIterator constEnd(); signals: /*! Notification of complete index availability. * @see filePath() */ void indexChanged(); //! @cond internal void indexResponse(const QVector &index); protected: explicit ClassesIconsRepository(QObject *parent = nullptr); QVector index() const; void setIndex(const QVector &index); protected slots: virtual void requestIndex() = 0; //! @endcond private: QVector m_iconsIndex; }; } QT_BEGIN_NAMESPACE Q_DECLARE_INTERFACE(GammaRay::ClassesIconsRepository, "com.kdab.GammaRay.ClassesIconsRepository/1.0") QT_END_NAMESPACE #endif // GAMMARAY_CLASSESICONSREPOSITORY_H gammaray-2.11.3/common/commonutils.cpp000066400000000000000000000022201413310443000177260ustar00rootroot00000000000000/* commonutils.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Hannah von Reth Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "commonutils.h" gammaray-2.11.3/common/commonutils.h000066400000000000000000000031201413310443000173730ustar00rootroot00000000000000/* commonutils.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Hannah von Reth Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_COMMON_UTILS_H #define GAMMARAY_COMMON_UTILS_H #include "gammaray_common_export.h" #include #define WIN_ERROR_ASSERT(condition, action) if (condition) {} else { qWarning("%s Error: %s failed: %s", Q_FUNC_INFO, #condition, qPrintable(qt_error_string())); action; } do {} while(false) #define WIN_ERROR_CHECK(condition) WIN_ERROR_ASSERT(condition, qt_noop();) namespace GammaRay { namespace CommonUtils { } } #endif // GAMMARAY_COMMON_UTILS_H gammaray-2.11.3/common/endpoint.cpp000066400000000000000000000311231413310443000172010ustar00rootroot00000000000000/* endpoint.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "endpoint.h" #include "message.h" #include "methodargument.h" #include "propertysyncer.h" #include #include //we use qCWarning, which we turn off by default, but which is not compiled out in releasebuilds Q_LOGGING_CATEGORY(networkstatistics, "gammaray.network.statistics", QtMsgType::QtCriticalMsg) using namespace GammaRay; using namespace std; Endpoint *Endpoint::s_instance = nullptr; Endpoint::Endpoint(QObject *parent) : QObject(parent) , m_propertySyncer(new PropertySyncer(this)) , m_socket(nullptr) , m_myAddress(Protocol::InvalidObjectAddress +1) , m_bytesRead(0) , m_bytesWritten(0) , m_pid(-1) { if (s_instance) { qCritical( "Found existing GammaRay::Endpoint instance - trying to attach to a GammaRay client?"); } Q_ASSERT(!s_instance); s_instance = this; auto *endpointObj = new ObjectInfo; endpointObj->address = m_myAddress; endpointObj->name = QStringLiteral("com.kdab.GammaRay.Server"); // TODO: we could set this as message handler here and use the same dispatch mechanism insertObjectInfo(endpointObj); m_bandwidthMeasurementTimer = new QTimer(this); connect(m_bandwidthMeasurementTimer, &QTimer::timeout, this, &Endpoint::doLogTransmissionRate); m_bandwidthMeasurementTimer->start(1000); connect(m_propertySyncer, &PropertySyncer::message, this, &Endpoint::sendMessage); } Endpoint::~Endpoint() { for (auto it = m_addressMap.constBegin(); it != m_addressMap.constEnd(); ++it) { delete it.value(); } s_instance = nullptr; } Endpoint *Endpoint::instance() { return s_instance; } void Endpoint::send(const Message &msg) { Q_ASSERT(s_instance); s_instance->doSendMessage(msg); } void Endpoint::sendMessage(const Message &msg) { if (!isConnected()) return; doSendMessage(msg); } void Endpoint::doSendMessage(const GammaRay::Message &msg) { Q_ASSERT(msg.address() != Protocol::InvalidObjectAddress); msg.write(m_socket); m_bytesWritten += msg.size(); } void Endpoint::waitForMessagesWritten() { m_socket->waitForBytesWritten(-1); } bool Endpoint::isConnected() { return s_instance && s_instance->m_socket; } quint16 Endpoint::defaultPort() { return 11732; } quint16 Endpoint::broadcastPort() { return 13325; } void Endpoint::doLogTransmissionRate() { emit logTransmissionRate(m_bytesRead, m_bytesWritten); if(!isRemoteClient()) { if(m_bytesRead != 0 || m_bytesWritten != 0) { const float transmissionRateRX = (m_bytesRead * 8 / 1024.0 / 1024.0); // in Mpbs const float transmissionRateTX = (m_bytesWritten * 8 / 1024.0 / 1024.0); // in Mpbs qCWarning(networkstatistics, "RX %7.3f Mbps | TX %7.3f Mbps", transmissionRateRX, transmissionRateTX); } } m_bytesRead = 0; m_bytesWritten = 0; } void Endpoint::setDevice(QIODevice *device) { Q_ASSERT(!m_socket); Q_ASSERT(device); m_socket = device; connect(m_socket.data(), &QIODevice::readyRead, this, &Endpoint::readyRead); // FIXME Use proper type for m_socket, instead of relying on runtime-connect // to a slot which doesn't exist in QIODevice connect(m_socket.data(), SIGNAL(disconnected()), SLOT(connectionClosed())); if (m_socket->bytesAvailable()) readyRead(); } Protocol::ObjectAddress Endpoint::endpointAddress() const { return m_myAddress; } void Endpoint::readyRead() { while (Message::canReadMessage(m_socket.data())) { const auto msg = Message::readMessage(m_socket.data()); m_bytesRead += msg.size(); messageReceived(msg); } } void Endpoint::connectionClosed() { disconnect(m_socket.data(), &QIODevice::readyRead, this, &Endpoint::readyRead); disconnect(m_socket.data(), SIGNAL(disconnected()), this, SLOT(connectionClosed())); m_socket = nullptr; emit disconnected(); } Protocol::ObjectAddress Endpoint::objectAddress(const QString &objectName) const { auto it = m_nameMap.constFind(objectName); if (it != m_nameMap.constEnd()) return it.value()->address; return Protocol::InvalidObjectAddress; } Protocol::ObjectAddress Endpoint::registerObject(const QString &name, QObject *object) { ObjectInfo *obj = m_nameMap.value(name, nullptr); #if !defined(NDEBUG) Q_ASSERT(obj); Q_ASSERT(!obj->object); Q_ASSERT(obj->address != Protocol::InvalidObjectAddress); #else if (!obj || obj->object || obj->address == Protocol::InvalidObjectAddress) return 0; #endif obj->object = object; Q_ASSERT(!m_objectMap.contains(object)); m_objectMap[object] = obj; connect(object, &QObject::destroyed, this, &Endpoint::slotObjectDestroyed); return obj->address; } void Endpoint::invokeObject(const QString &objectName, const char *method, const QVariantList &args) const { if (!isConnected()) return; ObjectInfo *obj = m_nameMap.value(objectName, nullptr); #if !defined(NDEBUG) Q_ASSERT(obj); Q_ASSERT(obj->address != Protocol::InvalidObjectAddress); #else if (!obj || obj->address == Protocol::InvalidObjectAddress) return; #endif Message msg(obj->address, Protocol::MethodCall); const QByteArray name(method); Q_ASSERT(!name.isEmpty()); msg << name << args; send(msg); } void Endpoint::invokeObjectLocal(QObject *object, const char *method, const QVariantList &args) const { Q_ASSERT(args.size() <= 10); QVector a(10); for (int i = 0; i < args.size(); ++i) { a[i] = MethodArgument(args.at(i)); } QMetaObject::invokeMethod(object, method, a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9]); } void Endpoint::addObjectNameAddressMapping(const QString &objectName, Protocol::ObjectAddress objectAddress) { Q_ASSERT(objectAddress != Protocol::InvalidObjectAddress); auto *obj = new ObjectInfo; obj->address = objectAddress; obj->name = objectName; insertObjectInfo(obj); emit objectRegistered(objectName, objectAddress); } void Endpoint::removeObjectNameAddressMapping(const QString &objectName) { Q_ASSERT(m_nameMap.contains(objectName)); ObjectInfo *obj = m_nameMap.value(objectName); emit objectUnregistered(objectName, obj->address); removeObjectInfo(obj); } void Endpoint::registerMessageHandler(Protocol::ObjectAddress objectAddress, QObject *receiver, const char *messageHandlerName) { Q_ASSERT(m_addressMap.contains(objectAddress)); ObjectInfo *obj = m_addressMap.value(objectAddress); Q_ASSERT(obj); Q_ASSERT(!obj->receiver); Q_ASSERT(!obj->messageHandler.isValid()); obj->receiver = receiver; QByteArray signature(messageHandlerName); signature += "(GammaRay::Message)"; auto idx = receiver->metaObject()->indexOfMethod(signature); Q_ASSERT(idx >= 0); obj->messageHandler = receiver->metaObject()->method(idx); Q_ASSERT(!m_handlerMap.contains(receiver, obj)); m_handlerMap.insert(receiver, obj); if (obj->receiver != obj->object) connect(receiver, &QObject::destroyed, this, &Endpoint::slotHandlerDestroyed); } void Endpoint::unregisterMessageHandler(Protocol::ObjectAddress objectAddress) { Q_ASSERT(m_addressMap.contains(objectAddress)); ObjectInfo *obj = m_addressMap.value(objectAddress); Q_ASSERT(obj); Q_ASSERT(obj->receiver); disconnect(obj->receiver, &QObject::destroyed, this, &Endpoint::slotHandlerDestroyed); m_handlerMap.remove(obj->receiver, obj); obj->receiver = nullptr; obj->messageHandler = QMetaMethod(); } void Endpoint::slotObjectDestroyed(QObject *obj) { ObjectInfo *info = m_objectMap.value(obj, nullptr); #if !defined(NDEBUG) Q_ASSERT(info); Q_ASSERT(info->object == obj); #else if (!info || info->object != obj) return; #endif info->object = nullptr; m_objectMap.remove(obj); // copy the name, in case unregisterMessageHandlerInternal() is called inside objectDestroyed(info->address, QString(info->name), obj); } void Endpoint::slotHandlerDestroyed(QObject *obj) { // copy, the virtual method below likely changes the maps. const QList objs = m_handlerMap.values(obj); m_handlerMap.remove(obj); for (ObjectInfo *obj : objs) { obj->receiver = nullptr; obj->messageHandler = QMetaMethod(); // copy the name, in case unregisterMessageHandlerInternal() is called inside handlerDestroyed(obj->address, QString(obj->name)); } } void Endpoint::dispatchMessage(const Message &msg) { auto it = m_addressMap.constFind(msg.address()); if (it == m_addressMap.constEnd()) { cerr << "message for unknown object address received: " << quint64(msg.address()) << endl; return; } ObjectInfo *obj = it.value(); if (msg.type() == Protocol::MethodCall) { QByteArray method; msg >> method; if (obj->object) { Q_ASSERT(!method.isEmpty()); QVariantList args; msg >> args; invokeObjectLocal(obj->object, method.constData(), args); } else { cerr << "cannot call method " << method.constData() << " on unknown object of name " << qPrintable(obj->name) << " with address " << quint64(obj->address) << " - did you forget to register it?" << endl; } } if (obj->receiver) obj->messageHandler.invoke(obj->receiver, Q_ARG(GammaRay::Message, msg)); if (!obj->receiver && (msg.type() != Protocol::MethodCall || !obj->object)) { cerr << "Cannot dispatch message " << quint64(msg.type()) << " - no handler registered." << " Receiver: " << qPrintable(obj->name) << ", address " << quint64(obj->address) << endl; } } QVector< QPair< Protocol::ObjectAddress, QString > > Endpoint::objectAddresses() const { QVector > addrs; addrs.reserve(m_addressMap.size()); for (auto it = m_addressMap.constBegin(); it != m_addressMap.constEnd(); ++it) { addrs.push_back(qMakePair(it.key(), it.value()->name)); } return addrs; } void Endpoint::insertObjectInfo(Endpoint::ObjectInfo *oi) { Q_ASSERT(!m_addressMap.contains(oi->address)); m_addressMap.insert(oi->address, oi); Q_ASSERT(!m_nameMap.contains(oi->name)); m_nameMap.insert(oi->name, oi); if (oi->receiver) m_handlerMap.insert(oi->receiver, oi); if (oi->object) m_objectMap.insert(oi->object, oi); } void Endpoint::removeObjectInfo(Endpoint::ObjectInfo *oi) { Q_ASSERT(m_addressMap.contains(oi->address)); m_addressMap.remove(oi->address); Q_ASSERT(m_nameMap.contains(oi->name)); m_nameMap.remove(oi->name); if (oi->receiver) { disconnect(oi->receiver, &QObject::destroyed, this, &Endpoint::slotHandlerDestroyed); m_handlerMap.remove(oi->receiver, oi); } if (oi->object) { disconnect(oi->object, &QObject::destroyed, this, &Endpoint::slotObjectDestroyed); m_objectMap.remove(oi->object); } delete oi; } QString Endpoint::label() const { return m_label; } void Endpoint::setLabel(const QString &label) { m_label = label; } QString Endpoint::key() const { return m_key; } void Endpoint::setKey(const QString &key) { m_key = key; } qint64 Endpoint::pid() const { return m_pid; } void Endpoint::setPid(qint64 pid) { m_pid = pid; } gammaray-2.11.3/common/endpoint.h000066400000000000000000000213741413310443000166550ustar00rootroot00000000000000/* endpoint.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_ENDPOINT_H #define GAMMARAY_ENDPOINT_H #include "gammaray_common_export.h" #include "protocol.h" #include #include #include #include #include Q_DECLARE_LOGGING_CATEGORY(networkstatistics) QT_BEGIN_NAMESPACE class QIODevice; class QUrl; QT_END_NAMESPACE namespace GammaRay { class Message; class PropertySyncer; /*! Network protocol endpoint. * * Contains: * - object address <-> object name mapping * - message handler registration and message dispatching */ class GAMMARAY_COMMON_EXPORT Endpoint : public QObject { Q_OBJECT public: ~Endpoint() override; /*! Send @p msg to the connected endpoint. */ static void send(const Message &msg); /*! Returns @c true if we are currently connected to another endpoint. */ static bool isConnected(); static quint16 defaultPort(); static quint16 broadcastPort(); /*! Returns the object address for @p objectName, or @c Protocol::InvalidObjectAddress if not known. */ Protocol::ObjectAddress objectAddress(const QString &objectName) const; /*! Singleton accessor. */ static Endpoint *instance(); /*! * Register an object of the given name for transparent server/client communication. */ virtual Protocol::ObjectAddress registerObject(const QString &name, QObject *object); /*! * Invoke @p method on the object called @p objectName with the given @p args. * * This also works with signals. * * The default implementation forwards the object calls to remote side when the * endpoint is connected. The Server implementation is furthermore expected to * call the method directly on the local object to support the in-process mode. */ virtual void invokeObject(const QString &objectName, const char *method, const QVariantList &args = QVariantList()) const; /*! * Write all pending data and block until this is done. * * This should only be used in very rare situations. */ void waitForMessagesWritten(); /*! * Returns a human-readable string describing the host program. */ QString label() const; /*! * Sets the human-readable label of this instance used e.g. when advertising on the network. */ void setLabel(const QString &label); /*! * Returns a fixed string uniquely describing the host program. */ QString key() const; /*! * Sets the fixed key of this instance used e.g. when saving settings. */ void setKey(const QString &key); /*! * Returns the process identifier (pid) of the host program. */ qint64 pid() const; /*! * Sets the process identifier of the host program of this instance used e.g. when querying system pid. */ void setPid(qint64 pid); /*! * Returns true for remote clients and false for the in-probe server endpoint. */ virtual bool isRemoteClient() const = 0; /*! * Returns the listening address of the server, in case you need to connect to a different service there * (such as the web inspector server). */ virtual QUrl serverAddress() const = 0; /*! Register the slot @p messageHandlerName on @p receiver as the handler for messages to/from @p objectAddress. * @see dispatchMessage() */ virtual void registerMessageHandler(Protocol::ObjectAddress objectAddress, QObject *receiver, const char *messageHandlerName); /*! Unregister the message handler for @p objectAddress. */ virtual void unregisterMessageHandler(Protocol::ObjectAddress objectAddress); public slots: /*! Convenience overload of send(), to directly send message delivered via signals. */ void sendMessage(const GammaRay::Message &msg); signals: /*! Emitted when a connection to another endpoint was successfully established and passed the protocol version handshake step. */ void connectionEstablished(); /*! Emitted when we lost the connection to the other endpoint. */ void disconnected(); /*! Emitted when a new object with name @p objectName has been registered at address @p objectAddress. */ void objectRegistered(const QString &objectName, Protocol::ObjectAddress objectAddress); void objectUnregistered(const QString &objectName, Protocol::ObjectAddress objectAddress); void logTransmissionRate(quint64 bytesRead, quint64 bytesWritten); protected: ///@cond internal Endpoint(QObject *parent = nullptr); /*! Call with the socket once you have established a connection to another endpoint, takes ownership of @p device. */ void setDevice(QIODevice *device); /*! The object address of the other endpoint. */ Protocol::ObjectAddress endpointAddress() const; /*! Called for every incoming message. * @see dispatchMessage(). */ virtual void messageReceived(const Message &msg) = 0; /*! Call this when learning about a new object <-> address mapping. */ void addObjectNameAddressMapping(const QString &objectName, Protocol::ObjectAddress objectAddress); /*! Call this when learning about a dissolved object <-> address mapping. */ void removeObjectNameAddressMapping(const QString &objectName); /*! Called when the current handler of the object identified by @p objectAddress has been destroyed. */ virtual void handlerDestroyed(Protocol::ObjectAddress objectAddress, const QString &objectName) = 0; /*! Called when a registered object identified by @p objectAddress has been destroyed. */ virtual void objectDestroyed(Protocol::ObjectAddress objectAddress, const QString &objectName, QObject *object) = 0; /*! Calls the message handler registered for the receiver of @p msg. */ void dispatchMessage(const GammaRay::Message &msg); /*! Sends a given message. */ virtual void doSendMessage(const Message &msg); /*! All current object name/address pairs. */ QVector > objectAddresses() const; /*! Singleton instance. */ static Endpoint *s_instance; /*! * Invoke @p method on @p object with the given @p args. * * This is invokes the method directly on the local object. */ void invokeObjectLocal(QObject *object, const char *method, const QVariantList &args) const; PropertySyncer *m_propertySyncer; ///@endcond private slots: void readyRead(); void doLogTransmissionRate(); void connectionClosed(); void slotHandlerDestroyed(QObject *obj); void slotObjectDestroyed(QObject *obj); private: struct ObjectInfo { ObjectInfo() : address(Protocol::InvalidObjectAddress) { } QString name; Protocol::ObjectAddress address; // the locally registered object QObject *object = nullptr; // custom message handling support QObject *receiver = nullptr; QMetaMethod messageHandler; }; /*! Inserts @p oi into all maps. */ void insertObjectInfo(ObjectInfo *oi); /*! Removes @p oi from all maps and destroys it. */ void removeObjectInfo(ObjectInfo *oi); QHash m_nameMap; QHash m_addressMap; QHash m_objectMap; QMultiHash m_handlerMap; QPointer m_socket; Protocol::ObjectAddress m_myAddress; quint64 m_bytesRead; quint64 m_bytesWritten; QTimer *m_bandwidthMeasurementTimer; QString m_label; QString m_key; qint64 m_pid; }; } #endif // GAMMARAY_ENDPOINT_H gammaray-2.11.3/common/enumdefinition.cpp000066400000000000000000000075001413310443000204000ustar00rootroot00000000000000/* enumdefinition.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "enumdefinition.h" using namespace GammaRay; EnumDefinitionElement::EnumDefinitionElement(int value, const char *name) : m_value(value) , m_name(name) { } QByteArray EnumDefinitionElement::name() const { return m_name; } int EnumDefinitionElement::value() const { return m_value; } namespace GammaRay { QDataStream& operator<<(QDataStream &out, const EnumDefinitionElement &elem) { out << elem.m_value << elem.m_name; return out; } QDataStream& operator>>(QDataStream &in, EnumDefinitionElement &elem) { in >> elem.m_value >> elem.m_name; return in; } } EnumDefinition::EnumDefinition(EnumId id, const QByteArray &name) : m_id(id) , m_name(name) { } bool EnumDefinition::isValid() const { return m_id != InvalidEnumId && !m_name.isEmpty() && !m_elements.isEmpty(); } EnumId EnumDefinition::id() const { return m_id; } QByteArray EnumDefinition::name() const { return m_name; } bool EnumDefinition::isFlag() const { return m_isFlag; } void EnumDefinition::setIsFlag(bool isFlag) { m_isFlag = isFlag; } QVector EnumDefinition::elements() const { return m_elements; } void EnumDefinition::setElements(const QVector& elements) { m_elements = elements; } QByteArray EnumDefinition::valueToString(const EnumValue& value) const { Q_ASSERT(value.id() == id()); if (isFlag()) { QByteArray r; int handledFlags = 0; for (const auto &elem : m_elements) { if ((elem.value() & value.value()) == elem.value() && elem.value() != 0) { r += elem.name() + '|'; handledFlags |= elem.value(); } } if (value.value() & ~handledFlags) r += "flag 0x" + QByteArray::number(value.value() & ~handledFlags, 16) + '|'; if (!r.isEmpty()) { r.chop(1); } else { // check for dedicated 0-values Q_ASSERT(value.value() == 0); for (const auto &elem : m_elements) { if (elem.value() == 0) return elem.name(); } return ""; } return r; } else { for (const auto &elem : m_elements) { if (elem.value() == value.value()) return elem.name(); } return "unknown (" + QByteArray::number(value.value()) + ')'; } } namespace GammaRay { QDataStream& operator<<(QDataStream &out, const EnumDefinition &def) { out << def.m_id << def.m_isFlag << def.m_name << def.m_elements; return out; } QDataStream& operator>>(QDataStream &in, EnumDefinition &def) { in >> def.m_id >> def.m_isFlag >> def.m_name >> def.m_elements; return in; } } gammaray-2.11.3/common/enumdefinition.h000066400000000000000000000100241413310443000200400ustar00rootroot00000000000000/* enumdefinition.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_ENUMDEFINITION_H #define GAMMARAY_ENUMDEFINITION_H #include "gammaray_common_export.h" #include "enumvalue.h" #include namespace GammaRay { /*! A single element of an EnumDefinition. */ class GAMMARAY_COMMON_EXPORT EnumDefinitionElement { public: EnumDefinitionElement() = default; /*! Create a new enum definition element with name @p name and value @p value. */ EnumDefinitionElement(int value, const char *name); /*! The numeric value represented of this enum definition element. */ int value() const; /*! The name of this enum definition element. */ QByteArray name() const; private: friend QDataStream &operator<<(QDataStream &out, const EnumDefinitionElement &elem); friend QDataStream &operator>>(QDataStream &in, EnumDefinitionElement &elem); int m_value = 0; QByteArray m_name; }; /*! Target-independent representation of an enum or flag definition. */ class GAMMARAY_COMMON_EXPORT EnumDefinition { public: EnumDefinition() = default; /*! Create a new definition for an enum named @p name and internal id @p id. */ explicit EnumDefinition(EnumId id, const QByteArray &name); /*! Returns whether this is a valid enum definition. * This means it's a enum definition registered with the EnumRepository * and there is at least one enum definition element present. */ bool isValid() const; /*! A unique identifier for this enum definition. * This is used to identify enums between the GammaRay client and server. */ EnumId id() const; /*! The name of the enum. */ QByteArray name() const; /*! Returns @c true if this enum is used as a flag. */ bool isFlag() const; /*! Returns the individual elements of this enum. * That is, the key/value pairs of its definition. */ QVector elements() const; //! @cond internal void setElements(const QVector &elements); void setIsFlag(bool isFlag); //! @endcond /*! Converts the given enum value into a string representation. */ QByteArray valueToString(const EnumValue &value) const; private: friend GAMMARAY_COMMON_EXPORT QDataStream &operator<<(QDataStream &out, const EnumDefinition &def); friend GAMMARAY_COMMON_EXPORT QDataStream &operator>>(QDataStream &in, EnumDefinition &def); EnumId m_id = InvalidEnumId; bool m_isFlag = false; QByteArray m_name; QVector m_elements; }; ///@cond internal GAMMARAY_COMMON_EXPORT QDataStream &operator<<(QDataStream &out, const EnumDefinition &def); GAMMARAY_COMMON_EXPORT QDataStream &operator>>(QDataStream &in, EnumDefinition &def); ///@endcond } Q_DECLARE_METATYPE(GammaRay::EnumDefinition) QT_BEGIN_NAMESPACE Q_DECLARE_TYPEINFO(GammaRay::EnumDefinitionElement, Q_MOVABLE_TYPE); Q_DECLARE_TYPEINFO(GammaRay::EnumDefinition, Q_MOVABLE_TYPE); QT_END_NAMESPACE #endif // GAMMARAY_ENUMDEFINITION_H gammaray-2.11.3/common/enumrepository.cpp000066400000000000000000000032401413310443000204640ustar00rootroot00000000000000/* enumrepository.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "enumrepository.h" using namespace GammaRay; EnumRepository::EnumRepository(QObject* parent) : QObject(parent) { } EnumRepository::~EnumRepository() = default; EnumDefinition EnumRepository::definition(EnumId id) const { if (id >= m_definitions.size() || id == InvalidEnumId) return EnumDefinition(); return m_definitions.at(id); } void EnumRepository::addDefinition(const EnumDefinition &def) { if (def.id() >= m_definitions.size()) m_definitions.resize(def.id() + 1); m_definitions[def.id()] = def; } gammaray-2.11.3/common/enumrepository.h000066400000000000000000000047071413310443000201420ustar00rootroot00000000000000/* enumrepository.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_ENUMREPOSITORY_H #define GAMMARAY_ENUMREPOSITORY_H #include "gammaray_common_export.h" #include "enumdefinition.h" #include namespace GammaRay { /*! Collection of EnumDefinition and on-demand client/server transfer of those. */ class GAMMARAY_COMMON_EXPORT EnumRepository : public QObject { Q_OBJECT public: ~EnumRepository() override; /*! Request the enum definition for the given enum id. * On the client side the response can be invalid on first request, * as transfer from the server is asynchronous. Listen to the * definitionChanged() signal to be notified for its availability * in that case. */ virtual EnumDefinition definition(EnumId id) const; signals: /*! Notification of EnumDefinition availability. * @see definition() */ void definitionChanged(int id); //! @cond internal void definitionResponse(const GammaRay::EnumDefinition &def); protected: EnumRepository(QObject *parent = nullptr); void addDefinition(const EnumDefinition &def); Q_INVOKABLE virtual void requestDefinition(int id) = 0; //! @endcond private: QVector m_definitions; }; } QT_BEGIN_NAMESPACE Q_DECLARE_INTERFACE(GammaRay::EnumRepository, "com.kdab.GammaRay.EnumRepository/1.0") QT_END_NAMESPACE #endif // GAMMARAY_ENUMREPOSITORY_H gammaray-2.11.3/common/enumvalue.cpp000066400000000000000000000033311413310443000173620ustar00rootroot00000000000000/* enumvalue.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "enumvalue.h" using namespace GammaRay; EnumValue::EnumValue(EnumId id, int value) : m_id(id) , m_value(value) { } EnumId EnumValue::id() const { return m_id; } int EnumValue::value() const { return m_value; } void EnumValue::setValue(int value) { m_value = value; } bool EnumValue::isValid() const { return m_id != InvalidEnumId; } namespace GammaRay { QDataStream& operator<<(QDataStream &out, const EnumValue &v) { out << v.m_id << v.m_value; return out; } QDataStream& operator>>(QDataStream &in, EnumValue &v) { in >> v.m_id >> v.m_value; return in; } } gammaray-2.11.3/common/enumvalue.h000066400000000000000000000053361413310443000170360ustar00rootroot00000000000000/* enumvalue.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_ENUMVALUE_H #define GAMMARAY_ENUMVALUE_H #include "gammaray_common_export.h" #include #include namespace GammaRay { /*! Unique identifier for EnumDefinition instances. */ typedef int EnumId; /*! Special value constants for GammaRay::EnumId. */ enum EnumIds { InvalidEnumId = -1, FirstValidEnumId = 0 }; /*! A target-independent representation of an enum or flag value. */ class GAMMARAY_COMMON_EXPORT EnumValue { public: EnumValue() = default; /*! Create a new enum value for EnumDefinition with @p id and value @p value. */ explicit EnumValue(EnumId id, int value); /*! The unique identifier of the definition of this enum. * @see EnumDefinition */ EnumId id() const; /*! Returns the numeric value of this enum value. */ int value() const; /*! Sets the numeric value of this enum value. */ void setValue(int value); /*! Returns @c true if this instance refers to a valid EnumDefinition. */ bool isValid() const; private: friend GAMMARAY_COMMON_EXPORT QDataStream &operator<<(QDataStream &out, const EnumValue &v); friend GAMMARAY_COMMON_EXPORT QDataStream &operator>>(QDataStream &in, EnumValue &v); EnumId m_id = InvalidEnumId; int m_value = 0; }; ///@cond internal GAMMARAY_COMMON_EXPORT QDataStream &operator<<(QDataStream &out, const EnumValue &v); GAMMARAY_COMMON_EXPORT QDataStream &operator>>(QDataStream &in, EnumValue &v); ///@endcond } Q_DECLARE_METATYPE(GammaRay::EnumValue) QT_BEGIN_NAMESPACE Q_DECLARE_TYPEINFO(GammaRay::EnumValue, Q_MOVABLE_TYPE); QT_END_NAMESPACE #endif // GAMMARAY_ENUMVALUE_H gammaray-2.11.3/common/message.cpp000066400000000000000000000172601413310443000170130ustar00rootroot00000000000000/* message.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "message.h" #include "sharedpool.h" #include "lz4/lz4.h" // 3rdparty #include #include #include inline void compress(const QByteArray &src, QByteArray &dst) { const qint32 srcSz = src.size(); dst.resize(LZ4_compressBound(srcSz + sizeof(srcSz))); *(qint32 *)dst.data() = srcSz; // save the source size const int sz = LZ4_compress_default(src.constData(), dst.data() + sizeof(int), srcSz, dst.size()); dst.resize(sz + sizeof(srcSz)); } inline void uncompress(const QByteArray &src, QByteArray &dst) { const qint32 dstSz = *(const qint32 *)src.constData(); // get the dest size dst.resize(dstSz); const int sz = LZ4_decompress_safe(src.constData() + sizeof(dstSz), dst.data(), src.size()- sizeof(dstSz), dstSz); if (sz <= 0) dst.resize(0); else dst.resize(sz); } static quint8 s_streamVersion = GammaRay::Message::lowestSupportedDataVersion(); static const int minimumUncompressedSize = 32; template static T readNumber(QIODevice *device) { T buffer; const int readSize = device->read((char *)&buffer, sizeof(T)); Q_UNUSED(readSize); Q_ASSERT(readSize == sizeof(T)); return qFromBigEndian(buffer); } template static void writeNumber(QIODevice *device, T value) { value = qToBigEndian(value); const int writeSize = device->write((char *)&value, sizeof(T)); Q_UNUSED(writeSize); Q_ASSERT(writeSize == sizeof(T)); } using namespace GammaRay; class MessageBuffer { public: MessageBuffer() : stream(&data) { data.open(QIODevice::ReadWrite); // explicitly reserve memory so a resize() won't shed it data.buffer().reserve(32); scratchSpace.reserve(32); } ~MessageBuffer() = default; void clear() { data.buffer().resize(0); resetStatus(); } void resetStatus() { data.seek(0); scratchSpace.resize(0); stream.resetStatus(); } QBuffer data; QByteArray scratchSpace; QDataStream stream; }; Q_GLOBAL_STATIC_WITH_ARGS(SharedPool, s_sharedMessageBufferPool, (5)) Message::Message() : m_objectAddress(Protocol::InvalidObjectAddress) , m_messageType(Protocol::InvalidMessageType) , m_buffer(s_sharedMessageBufferPool()->acquire()) { m_buffer->clear(); m_buffer->stream.setVersion(s_streamVersion); } Message::Message(Protocol::ObjectAddress objectAddress, Protocol::MessageType type) : m_objectAddress(objectAddress) , m_messageType(type) , m_buffer(s_sharedMessageBufferPool()->acquire()) { m_buffer->clear(); m_buffer->stream.setVersion(s_streamVersion); } Message::Message(Message &&other) Q_DECL_NOEXCEPT : m_objectAddress(other.m_objectAddress) , m_messageType(other.m_messageType) , m_buffer(std::move(other.m_buffer)) { } Message::~Message() = default; Protocol::ObjectAddress Message::address() const { return m_objectAddress; } Protocol::MessageType Message::type() const { return m_messageType; } QDataStream &Message::payload() const { return m_buffer->stream; } bool Message::canReadMessage(QIODevice *device) { if (!device) return false; static const int minimumSize = sizeof(Protocol::PayloadSize) + sizeof(Protocol::ObjectAddress) + sizeof(Protocol::MessageType); if (device->bytesAvailable() < minimumSize) return false; Protocol::PayloadSize payloadSize; const int peekSize = device->peek((char *)&payloadSize, sizeof(Protocol::PayloadSize)); if (peekSize < (int)sizeof(Protocol::PayloadSize)) return false; if (payloadSize == -1 && !device->isSequential()) // input end on shared memory return false; payloadSize = abs(qFromBigEndian(payloadSize)); return device->bytesAvailable() >= payloadSize + minimumSize; } Message Message::readMessage(QIODevice *device) { Message msg; Protocol::PayloadSize payloadSize = readNumber(device); msg.m_objectAddress = readNumber(device); msg.m_messageType = readNumber(device); Q_ASSERT(msg.m_messageType != Protocol::InvalidMessageType); Q_ASSERT(msg.m_objectAddress != Protocol::InvalidObjectAddress); if (payloadSize < 0) { payloadSize = abs(payloadSize); auto& uncompressedData = msg.m_buffer->scratchSpace; uncompressedData.resize(payloadSize); device->read(uncompressedData.data(), payloadSize); uncompress(uncompressedData, msg.m_buffer->data.buffer()); Q_ASSERT(payloadSize == uncompressedData.size()); } else { if (payloadSize > 0) { msg.m_buffer->data.buffer() = device->read(payloadSize); Q_ASSERT(payloadSize == msg.m_buffer->data.size()); } } msg.m_buffer->resetStatus(); return msg; } quint8 Message::lowestSupportedDataVersion() { return QDataStream::Qt_5_5; } quint8 Message::highestSupportedDataVersion() { return QDataStream::Qt_DefaultCompiledVersion; } quint8 Message::negotiatedDataVersion() { return s_streamVersion; } void Message::setNegotiatedDataVersion(quint8 version) { s_streamVersion = version; } void Message::resetNegotiatedDataVersion() { s_streamVersion = lowestSupportedDataVersion(); } void Message::write(QIODevice *device) const { Q_ASSERT(m_objectAddress != Protocol::InvalidObjectAddress); Q_ASSERT(m_messageType != Protocol::InvalidMessageType); static const bool compressionEnabled = qgetenv("GAMMARAY_DISABLE_LZ4") != "1"; const int buffSize = m_buffer->data.size(); auto& compressedData = m_buffer->scratchSpace; if (buffSize > minimumUncompressedSize && compressionEnabled) compress(m_buffer->data.buffer(), compressedData); const bool isCompressed = compressedData.size() && compressedData.size() < buffSize; if (isCompressed) writeNumber(device, -compressedData.size()); // send compressed Buffer else writeNumber(device, buffSize); // send uncompressed Buffer writeNumber(device, m_objectAddress); writeNumber(device, m_messageType); if (buffSize) { if (isCompressed) { const int s = device->write(compressedData); Q_ASSERT(s == compressedData.size()); Q_UNUSED(s); } else { const int s = device->write(m_buffer->data.buffer()); Q_ASSERT(s == m_buffer->data.size()); Q_UNUSED(s); } } } int Message::size() const { return m_buffer->data.size(); } gammaray-2.11.3/common/message.h000066400000000000000000000113641413310443000164570ustar00rootroot00000000000000/* message.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_MESSAGE_H #define GAMMARAY_MESSAGE_H #include "gammaray_common_export.h" #include "protocol.h" #include #include #include #include class MessageBuffer; namespace GammaRay { /** * Single message send between client and server. * Binary format: * - sizeof(Protocol::PayloadSize) byte size of the message payload (not including the size and other fixed fields itself) in netowork byte order (big endian) * - sizeof(Protocol::ObjectAddress) server object address (big endian) * - sizeof(Protocol::MessageType) command type (big endian) * - size bytes message payload (encoding is user defined, QDataStream provided for convenience) */ class GAMMARAY_COMMON_EXPORT Message { public: /** * Construct a new message to/from @p objectAddress and message type @p type. */ explicit Message(Protocol::ObjectAddress objectAddress, Protocol::MessageType type); Message(Message &&other) Q_DECL_NOEXCEPT; // krazy:exclude=explicit ~Message(); Protocol::ObjectAddress address() const; Protocol::MessageType type() const; /** Read value from the payload * This operator proxy over payload() allow to do: * - Run time check on the stream status */ template GammaRay::Message &operator>>(T &value) { if (Q_UNLIKELY(payload().status() != QDataStream::Ok)) { qWarning("%s: Attempting to read from a non valid stream: status: %i", Q_FUNC_INFO, int(payload().status())); } payload() >> value; if (Q_UNLIKELY(payload().status() != QDataStream::Ok)) { qWarning("%s: Read from a non valid stream: status: %i", Q_FUNC_INFO, int(payload().status())); } return *this; } /** Read value from the payload * This overload allow to read content from a const Message. */ template GammaRay::Message &operator>>(T &value) const { return const_cast(this)->operator>>(value); } /** Write value to the payload. * This operator proxy over payload() allow to do: * - Run time check on the stream status */ template GammaRay::Message &operator<<(const T &value) { if (Q_UNLIKELY(payload().status() != QDataStream::Ok)) { qWarning("%s: Attempting to write to a non valid stream: status: %i", Q_FUNC_INFO, int(payload().status())); } payload() << value; if (Q_UNLIKELY(payload().status() != QDataStream::Ok)) { qWarning("%s: Write to a non valid stream: status: %i", Q_FUNC_INFO, int(payload().status())); } return *this; } /** Checks if there is a full message waiting in @p device. */ static bool canReadMessage(QIODevice *device); /** Read the next message from @p device. */ static Message readMessage(QIODevice *device); static quint8 lowestSupportedDataVersion(); static quint8 highestSupportedDataVersion(); static quint8 negotiatedDataVersion(); static void setNegotiatedDataVersion(quint8 version); static void resetNegotiatedDataVersion(); /** Write this message to @p device. */ void write(QIODevice *device) const; /** Size of the uncompressed message payload. */ int size() const; private: Message(); /** Access to the message payload. This is read-only for received messages * and write-only for messages to be sent. */ QDataStream &payload() const; Protocol::ObjectAddress m_objectAddress; Protocol::MessageType m_messageType; std::unique_ptr> m_buffer; }; } #endif gammaray-2.11.3/common/metatypedeclarations.h000066400000000000000000000052071413310443000212530ustar00rootroot00000000000000/* metatypedeclarations.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ /** @file This file is part of the GammaRay Plugin API and declares the various metatypes. @brief Declares the various metatypes. @author Volker Krause \ */ #ifndef GAMMARAY_METATYPEDECLARATIONS_H #define GAMMARAY_METATYPEDECLARATIONS_H #include #include #ifdef QT_GUI_LIB #include #include #include #include #include #include #include #include #include #endif Q_DECLARE_METATYPE(Qt::ConnectionType) Q_DECLARE_METATYPE(QMetaMethod::Access) Q_DECLARE_METATYPE(QMetaMethod::MethodType) Q_DECLARE_METATYPE(const QMetaObject *) #include Q_DECLARE_METATYPE(QTimeZone) #ifdef QT_GUI_LIB // TODO move all this to the GUI support plug-in Q_DECLARE_METATYPE(Qt::FillRule) Q_DECLARE_METATYPE(Qt::InputMethodHints) Q_DECLARE_METATYPE(Qt::MouseButtons) Q_DECLARE_METATYPE(Qt::TransformationMode) Q_DECLARE_METATYPE(QPainterPath) Q_DECLARE_METATYPE(QPolygonF) Q_DECLARE_METATYPE(QMargins) Q_DECLARE_METATYPE(Qt::WindowType) Q_DECLARE_METATYPE(Qt::WindowState) Q_DECLARE_METATYPE(const QMatrix4x4*) Q_DECLARE_METATYPE(const QValidator*) Q_DECLARE_METATYPE(QPainter::CompositionMode) #if QT_VERSION < QT_VERSION_CHECK(5, 9, 0) Q_DECLARE_METATYPE(QPainter::RenderHints) #endif Q_DECLARE_METATYPE(QPaintEngine::PolygonDrawMode) #ifndef QT_NO_OPENGL Q_DECLARE_METATYPE(QOpenGLShader::ShaderType) #endif Q_DECLARE_METATYPE(QSurfaceFormat) #endif #endif gammaray-2.11.3/common/methodargument.cpp000066400000000000000000000054211413310443000204060ustar00rootroot00000000000000/* methodargument.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "methodargument.h" #include "variantwrapper.h" #include #include using namespace GammaRay; class GammaRay::MethodArgumentPrivate : public QSharedData { public: MethodArgumentPrivate() = default; MethodArgumentPrivate(const MethodArgumentPrivate &other) : QSharedData(other) { value = other.value; name = other.name; data = nullptr; unwrapVariant = other.unwrapVariant; } ~MethodArgumentPrivate() { if (data) QMetaType::destroy(value.userType(), data); } QVariant value; QByteArray name; void *data = nullptr; bool unwrapVariant = true; }; MethodArgument::MethodArgument() : d(new MethodArgumentPrivate) { } MethodArgument::MethodArgument(const QVariant &v) : d(new MethodArgumentPrivate) { if (v.userType() == qMetaTypeId()) { d->value = v.value().variant(); d->unwrapVariant = false; d->name = "QVariant"; } else { d->value = v; d->unwrapVariant = true; d->name = v.typeName(); } } MethodArgument::MethodArgument(const MethodArgument &) = default; MethodArgument::~MethodArgument() = default; MethodArgument &MethodArgument::operator=(const MethodArgument &) = default; MethodArgument::operator QGenericArgument() const { if (!d->unwrapVariant) return QGenericArgument(d->name.constData(), &d->value); if (d->value.isValid()) { d->data = QMetaType::create(d->value.userType(), d->value.constData()); Q_ASSERT(d->data); return QGenericArgument(d->name.constData(), d->data); } return {}; } gammaray-2.11.3/common/methodargument.h000066400000000000000000000034601413310443000200540ustar00rootroot00000000000000/* methodargument.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_METHODARGUMENT_H #define GAMMARAY_METHODARGUMENT_H #include "gammaray_common_export.h" #include #include namespace GammaRay { class MethodArgumentPrivate; /** Q[Generic]Argument that works on a QVariant, with some memory handling safety. */ class GAMMARAY_COMMON_EXPORT MethodArgument { public: MethodArgument(); explicit MethodArgument(const QVariant &v); MethodArgument(const MethodArgument &other); ~MethodArgument(); MethodArgument &operator=(const MethodArgument &other); operator QGenericArgument() const; private: QExplicitlySharedDataPointer d; }; } #endif // GAMMARAY_METHODARGUMENT_H gammaray-2.11.3/common/modelevent.cpp000066400000000000000000000037051413310443000175300ustar00rootroot00000000000000/* modelevent.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "modelevent.h" #include #include using namespace GammaRay; ModelEvent::ModelEvent(bool modelUsed) : QEvent(eventType()) , m_used(modelUsed) { } ModelEvent::~ModelEvent() = default; bool ModelEvent::used() const { return m_used; } QEvent::Type ModelEvent::eventType() { static int id = -1; if (id < 0) id = registerEventType(); return static_cast(id); } void Model::used(const QAbstractItemModel *model) { Q_ASSERT(model); ModelEvent ev(true); QCoreApplication::sendEvent(const_cast(model), &ev); // const_cast hack is needed since QItemSelectionModel gives us const*... } void Model::unused(QAbstractItemModel *model) { Q_ASSERT(model); ModelEvent ev(false); QCoreApplication::sendEvent(model, &ev); } gammaray-2.11.3/common/modelevent.h000066400000000000000000000041601413310443000171710ustar00rootroot00000000000000/* modelevent.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_MODELEVENT_H #define GAMMARAY_MODELEVENT_H #include "gammaray_common_export.h" #include QT_BEGIN_NAMESPACE class QAbstractItemModel; QT_END_NAMESPACE namespace GammaRay { /** Custom event to notify models about them being used/unused. * Useful for suspending expensive operations. */ class GAMMARAY_COMMON_EXPORT ModelEvent : public QEvent { public: explicit ModelEvent(bool modelUsed); ~ModelEvent(); /** Returns @c true if somebody is using the model. */ bool used() const; /** Returns the event id for this type. */ static QEvent::Type eventType(); private: Q_DISABLE_COPY(ModelEvent) bool m_used; }; namespace Model { /** Convenience function to send a ModelEvent indicating @p model is in use. */ void GAMMARAY_COMMON_EXPORT used(const QAbstractItemModel *model); /** Convenience function to send a ModelEvent indicating nobody is using @p model. */ void GAMMARAY_COMMON_EXPORT unused(QAbstractItemModel *model); } } #endif // GAMMARAY_MODELEVENT_H gammaray-2.11.3/common/modelroles.h000066400000000000000000000034561413310443000172030ustar00rootroot00000000000000/* modelroles.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_MODELROLES_H #define GAMMARAY_MODELROLES_H /** * @file modelroles.h * @brief A collection of custom model roles shared between client and server. */ namespace GammaRay { /*! Portable replacement for Qt::UserRole. * Qt4 uses 32, Qt5 256, use the latter globally to allow combining Qt4/5 client/servers. */ static const int UserRole = 256; /*! Custom roles for GammaRay::ToolModel. * @todo These can be split again, between core tool model and UI tool model. */ namespace ToolModelRole { /*! Tool model data roles. */ enum Role { ToolFactory = UserRole + 1, ToolWidget, ToolId, ToolWidgetParent, ToolEnabled, ToolHasUi, ToolFeedbackId }; } } #endif gammaray-2.11.3/common/modelutils.cpp000066400000000000000000000050471413310443000175500ustar00rootroot00000000000000/* modelutils.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Kevin Funk Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "modelutils.h" #include using namespace GammaRay; QModelIndexList ModelUtils::match(const QModelIndex &start, int role, MatchAcceptor accept, int hits, Qt::MatchFlags flags) { if (!start.isValid() || role < 0) return QModelIndexList(); const QAbstractItemModel *model = start.model(); const QModelIndex parentIndex = model->parent(start); bool recurse = flags & Qt::MatchRecursive; bool wrap = flags & Qt::MatchWrap; bool allHits = (hits == -1); int from = start.row(); int to = model->rowCount(parentIndex); QModelIndexList result; // iterates twice if wrapping for (int i = 0; (wrap && i < 2) || (!wrap && i < 1); ++i) { for (int r = from; (r < to) && (allHits || result.count() < hits); ++r) { QModelIndex idx = model->index(r, start.column(), parentIndex); if (!idx.isValid()) continue; const QVariant v = model->data(idx, role); if (accept(v)) result << idx; // search the hierarchy if (recurse && model->hasChildren(idx)) { result += match(model->index(0, idx.column(), idx), role, accept, (allHits ? -1 : hits - result.count()), flags); } } // prepare for the next iteration from = 0; to = start.row(); } return result; } gammaray-2.11.3/common/modelutils.h000066400000000000000000000040331413310443000172070ustar00rootroot00000000000000/* modelutils.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Kevin Funk Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_MODELUTILS_H #define GAMMARAY_MODELUTILS_H #include "gammaray_common_export.h" #include namespace GammaRay { namespace ModelUtils { typedef bool (*MatchAcceptor)(const QVariant &); /** * Return a list of items that are accepted by an acceptor function * This class extends the functionality provided in QAbstractItemModel::match(...) * * If (accept(v)) for an item v in the model returns true, it will be appended * to the result list * * @param accept Function in the form 'bool f(const QVariant&)' * * @see QAbstractItemModel::match(...) */ GAMMARAY_COMMON_EXPORT QModelIndexList match(const QModelIndex &start, int role, MatchAcceptor accept, int hits = 1, Qt::MatchFlags flags = Qt::MatchFlags(Qt::MatchWrap)); } } Q_DECLARE_METATYPE(GammaRay::ModelUtils::MatchAcceptor) #endif // GAMMARAY_MODELUTILS_H gammaray-2.11.3/common/networkselectionmodel.cpp000066400000000000000000000213241413310443000220030ustar00rootroot00000000000000/* networkselectionmodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "networkselectionmodel.h" #include "common/modelutils.h" #include "endpoint.h" #include "message.h" #include "settempvalue.h" #include #include using namespace GammaRay; static QDataStream &operator<<(QDataStream &out, QItemSelectionModel::SelectionFlags command) { out << (quint32)command; // Qt4 and Qt5 use the same enum layout, so this is fine for now return out; } QDataStream &operator>>(QDataStream &in, QItemSelectionModel::SelectionFlags &command) { quint32 v; in >> v; command = static_cast(v); return in; } static void writeSelection(Message *msg, const QItemSelection &selection) { *msg << qint32(selection.size()); for (const QItemSelectionRange &range : selection) *msg << Protocol::fromQModelIndex(range.topLeft()) << Protocol::fromQModelIndex( range.bottomRight()); } // find a model having a defaultSelectedItem method static QAbstractItemModel *findSourceModel(QAbstractItemModel *model) { if (model) { if (model->metaObject()->indexOfMethod(QMetaObject::normalizedSignature( "defaultSelectedItem()")) != -1) return model; else if (auto proxy = qobject_cast(model)) return findSourceModel(proxy->sourceModel()); } return nullptr; } NetworkSelectionModel::NetworkSelectionModel(const QString &objectName, QAbstractItemModel *model, QObject *parent) : QItemSelectionModel(model, parent) , m_objectName(objectName) , m_myAddress(Protocol::InvalidObjectAddress) , m_pendingCommand(NoUpdate) , m_handlingRemoteMessage(false) { setObjectName(m_objectName + QLatin1String("Network")); connect(this, &QItemSelectionModel::currentChanged, this, &NetworkSelectionModel::slotCurrentChanged); } NetworkSelectionModel::~NetworkSelectionModel() = default; void NetworkSelectionModel::requestSelection() { if (m_handlingRemoteMessage || !isConnected()) return; Message msg(m_myAddress, Protocol::SelectionModelStateRequest); Endpoint::send(msg); } void NetworkSelectionModel::sendSelection() { if (!isConnected()) return; clearPendingSelection(); if (!hasSelection()) { if (model()->rowCount() > 0) { const QItemSelectionModel::SelectionFlags selectionFlags = QItemSelectionModel::ClearAndSelect |QItemSelectionModel::Rows | QItemSelectionModel::Current; const Qt::MatchFlags matchFlags = Qt::MatchExactly | Qt::MatchRecursive | Qt::MatchWrap; QAbstractItemModel *sourceModel = findSourceModel(model()); QModelIndex index = model()->index(0, 0); // Query the model to get its default selected index if (sourceModel) { QPair result; QModelIndex defaultIndex; QMetaObject::invokeMethod(sourceModel, "defaultSelectedItem", Qt::DirectConnection, QReturnArgument >("QPair", result)); if (result.second.userType() == qMetaTypeId()) { defaultIndex = ModelUtils::match(index, result.first, result.second.value(), 1, matchFlags).value(0); } else { defaultIndex = model()->match(index, result.first, result.second, 1, matchFlags).value(0); } if (defaultIndex.isValid()) index = defaultIndex; } select(QItemSelection(index, index), selectionFlags); } } else { Message msg(m_myAddress, Protocol::SelectionModelSelect); writeSelection(&msg, selection()); msg << ClearAndSelect; Endpoint::send(msg); } } Protocol::ItemSelection GammaRay::NetworkSelectionModel::readSelection(const GammaRay::Message &msg) { Protocol::ItemSelection selection; qint32 size = 0; msg >> size; selection.reserve(size); for (int i = 0; i < size; ++i) { Protocol::ItemSelectionRange range; msg >> range.topLeft >> range.bottomRight; selection.push_back(range); } return selection; } bool GammaRay::NetworkSelectionModel::translateSelection(const Protocol::ItemSelection &selection, QItemSelection &qselection) const { qselection.clear(); for (const auto &range : selection) { const QModelIndex qmiTopLeft = Protocol::toQModelIndex(model(), range.topLeft); const QModelIndex qmiBottomRight = Protocol::toQModelIndex(model(), range.bottomRight); if (!qmiTopLeft.isValid() && !qmiBottomRight.isValid()) return false; qselection.push_back(QItemSelectionRange(qmiTopLeft, qmiBottomRight)); } return true; } void NetworkSelectionModel::newMessage(const Message &msg) { Q_ASSERT(msg.address() == m_myAddress); switch (msg.type()) { case Protocol::SelectionModelSelect: { Util::SetTempValue guard(m_handlingRemoteMessage, true); m_pendingSelection = readSelection(msg); msg >> m_pendingCommand; applyPendingSelection(); break; } case Protocol::SelectionModelCurrent: { SelectionFlags flags; Protocol::ModelIndex index; msg >> flags >> index; const QModelIndex qmi = Protocol::toQModelIndex(model(), index); if (!qmi.isValid()) break; Util::SetTempValue guard(m_handlingRemoteMessage, true); setCurrentIndex(qmi, flags); break; } case Protocol::SelectionModelStateRequest: sendSelection(); break; default: Q_ASSERT(false); } } void NetworkSelectionModel::slotCurrentChanged(const QModelIndex ¤t, const QModelIndex &previous) { Q_UNUSED(previous); if (m_handlingRemoteMessage || !isConnected()) return; clearPendingSelection(); Message msg(m_myAddress, Protocol::SelectionModelCurrent); msg << QItemSelectionModel::Current << Protocol::fromQModelIndex(current); Endpoint::send(msg); } void NetworkSelectionModel::select(const QItemSelection &selection, QItemSelectionModel::SelectionFlags command) { QItemSelectionModel::select(selection, command); if (m_handlingRemoteMessage || !isConnected()) return; clearPendingSelection(); Message msg(m_myAddress, Protocol::SelectionModelSelect); writeSelection(&msg, selection); msg << command; Endpoint::send(msg); } void GammaRay::NetworkSelectionModel::applyPendingSelection() { if (m_pendingSelection.isEmpty() && m_pendingCommand == NoUpdate) return; QItemSelection qmiSelection; if (translateSelection(m_pendingSelection, qmiSelection)) { if (!qmiSelection.isEmpty()) select(qmiSelection, m_pendingCommand); clearPendingSelection(); } } void GammaRay::NetworkSelectionModel::clearPendingSelection() { m_pendingSelection.clear(); m_pendingCommand = NoUpdate; } bool NetworkSelectionModel::isConnected() const { return Endpoint::isConnected() && m_myAddress != Protocol::InvalidObjectAddress; } gammaray-2.11.3/common/networkselectionmodel.h000066400000000000000000000053241413310443000214520ustar00rootroot00000000000000/* networkselectionmodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_NETWORKSELECTIONMODEL_H #define GAMMARAY_NETWORKSELECTIONMODEL_H #include #include "protocol.h" namespace GammaRay { class Message; /** Base class for network-transparent item selection models, do not use directly. */ class NetworkSelectionModel : public QItemSelectionModel { Q_OBJECT public: ~NetworkSelectionModel() override; void select(const QItemSelection &selection, QItemSelectionModel::SelectionFlags command) override; using QItemSelectionModel::select; protected: explicit NetworkSelectionModel(const QString &objectName, QAbstractItemModel *model, QObject *parent = nullptr); virtual bool isConnected() const; QString m_objectName; Protocol::ObjectAddress m_myAddress; protected slots: void requestSelection(); void sendSelection(); void applyPendingSelection(); private: static Protocol::ItemSelection readSelection(const Message &msg); bool translateSelection(const Protocol::ItemSelection &selection, QItemSelection &qselection) const; private slots: void newMessage(const GammaRay::Message &msg); void slotCurrentChanged(const QModelIndex ¤t, const QModelIndex &previous); protected slots: void clearPendingSelection(); private: Protocol::ItemSelection m_pendingSelection; SelectionFlags m_pendingCommand; bool m_handlingRemoteMessage; }; } Q_DECLARE_METATYPE(QItemSelectionModel::SelectionFlag) Q_DECLARE_METATYPE(QItemSelectionModel::SelectionFlags) #endif // GAMMARAY_NETWORKSELECTIONMODEL_H gammaray-2.11.3/common/objectbroker.cpp000066400000000000000000000165221413310443000200420ustar00rootroot00000000000000/* objectbroker.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "objectbroker.h" #include "endpoint.h" #include "modelevent.h" #include #include #include #include #include #include #include #include namespace GammaRay { struct ObjectlBrokerData { ObjectlBrokerData() = default; QHash objects; QHash models; QHash selectionModels; QHash clientObjectFactories; ObjectBroker::ModelFactoryCallback modelCallback = nullptr; ObjectBroker::selectionModelFactoryCallback selectionCallback = nullptr; QVector ownedObjects; }; Q_GLOBAL_STATIC(ObjectlBrokerData, s_objectBroker) void ObjectBroker::registerObject(const QString &name, QObject *object) { Q_ASSERT(!name.isEmpty()); Q_ASSERT(object->objectName().isEmpty()); object->setObjectName(name); Q_ASSERT(!s_objectBroker()->objects.contains(name)); s_objectBroker()->objects.insert(name, object); Q_ASSERT(Endpoint::instance()); Endpoint::instance()->registerObject(name, object); } bool ObjectBroker::hasObject(const QString &name) { return s_objectBroker()->objects.contains(name); } QObject *ObjectBroker::objectInternal(const QString &name, const QByteArray &type) { auto it = s_objectBroker()->objects.constFind(name); if (it != s_objectBroker()->objects.constEnd()) return it.value(); // Below here only valid for clients! // Remote/probe side should have registered the object directly QObject *obj = nullptr; if (!type.isEmpty()) { Q_ASSERT(s_objectBroker()->clientObjectFactories.contains(type)); obj = s_objectBroker()->clientObjectFactories.value(type)(name, qApp); } else { // fallback obj = new QObject(qApp); registerObject(name, obj); } s_objectBroker()->ownedObjects.push_back(obj); Q_ASSERT(obj); // ensure it was registered Q_ASSERT_X(s_objectBroker()->objects.value(name, nullptr) == obj, Q_FUNC_INFO, qPrintable(QStringLiteral("Object %1 was not registered in the broker.").arg(name))); return obj; } void ObjectBroker::registerClientObjectFactoryCallbackInternal(const QByteArray &type, ObjectBroker::ClientObjectFactoryCallback callback) { Q_ASSERT(!type.isEmpty()); s_objectBroker()->clientObjectFactories[type] = callback; } void ObjectBroker::registerModelInternal(const QString &name, QAbstractItemModel *model) { Q_ASSERT(!s_objectBroker()->models.contains(name)); model->setObjectName(name); s_objectBroker()->models.insert(name, model); } QAbstractItemModel *ObjectBroker::model(const QString &name) { ModelEvent event(true); auto it = s_objectBroker()->models.constFind(name); if (it != s_objectBroker()->models.constEnd()) { QCoreApplication::sendEvent(it.value(), &event); return it.value(); } if (s_objectBroker()->modelCallback) { QAbstractItemModel *model = s_objectBroker()->modelCallback(name); if (model) { model->setObjectName(name); s_objectBroker()->models.insert(name, model); s_objectBroker()->ownedObjects.push_back(model); QCoreApplication::sendEvent(model, &event); return model; } } return nullptr; } void ObjectBroker::setModelFactoryCallback(ObjectBroker::ModelFactoryCallback callback) { s_objectBroker()->modelCallback = callback; } void ObjectBroker::registerSelectionModel(QItemSelectionModel *selectionModel) { Q_ASSERT(!s_objectBroker()->selectionModels.contains(const_cast( selectionModel->model()))); s_objectBroker()->selectionModels.insert( const_cast(selectionModel->model()), selectionModel); } void ObjectBroker::unregisterSelectionModel(QItemSelectionModel *selectionModel) { Q_ASSERT(s_objectBroker()->selectionModels.contains(const_cast( selectionModel->model()))); s_objectBroker()->selectionModels.remove( const_cast(selectionModel->model())); } bool ObjectBroker::hasSelectionModel(QAbstractItemModel *model) { return s_objectBroker()->selectionModels.contains(model); } static QAbstractItemModel *sourceModelForProxy(QAbstractItemModel *model) { // stop once we found a registered model, this is what network communication is based on if (s_objectBroker()->models.values().contains(model)) return model; QAbstractProxyModel *proxy = qobject_cast(model); if (!proxy) return model; return sourceModelForProxy(proxy->sourceModel()); } QItemSelectionModel *ObjectBroker::selectionModel(QAbstractItemModel *model) { auto it = s_objectBroker()->selectionModels.constFind(model); if (it != s_objectBroker()->selectionModels.constEnd()) return it.value(); if (s_objectBroker()->selectionCallback) { QAbstractItemModel *sourceModel = sourceModelForProxy(model); QItemSelectionModel *selectionModel = nullptr; if (sourceModel == model) { selectionModel = s_objectBroker()->selectionCallback(sourceModel); s_objectBroker()->ownedObjects.push_back(selectionModel); } else { QItemSelectionModel *sourceSelectionModel = ObjectBroker::selectionModel(sourceModel); selectionModel = new KLinkItemSelectionModel(model, sourceSelectionModel, model); } if (selectionModel) { registerSelectionModel(selectionModel); return selectionModel; } } return nullptr; } void ObjectBroker::setSelectionModelFactoryCallback( ObjectBroker::selectionModelFactoryCallback callback) { s_objectBroker()->selectionCallback = callback; } void ObjectBroker::clear() { auto *ob = s_objectBroker(); qDeleteAll(ob->ownedObjects); ob->ownedObjects.clear(); ob->objects.clear(); ob->models.clear(); ob->selectionModels.clear(); } } gammaray-2.11.3/common/objectbroker.h000066400000000000000000000133621413310443000175060ustar00rootroot00000000000000/* objectbroker.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_OBJECTBROKER_H #define GAMMARAY_OBJECTBROKER_H #include "gammaray_common_export.h" #include QT_BEGIN_NAMESPACE class QItemSelectionModel; class QAbstractItemModel; QT_END_NAMESPACE namespace GammaRay { /*! Retrieve/expose objects independent of whether using in-process or out-of-process UI. */ namespace ObjectBroker { /*! Register a newly created QObject under the given name. */ GAMMARAY_COMMON_EXPORT void registerObject(const QString &name, QObject *object); template void registerObject(QObject *object) { const QString interfaceName = QString::fromUtf8(qobject_interface_iid()); registerObject(interfaceName, object); } /*! Checks whether an object with the given name is registered already. */ GAMMARAY_COMMON_EXPORT bool hasObject(const QString &name); /*! Retrieve object by name. */ GAMMARAY_COMMON_EXPORT QObject *objectInternal(const QString &name, const QByteArray &type = QByteArray()); /*! * Retrieve an object by name implementing interface @p T. * * Use this if multiple objects of the given type have been registered. * Otherwise the function below is simpler and more failsafe. * * @note The "T = nullptr" is just to ensure a pointer type is given. */ template T object(const QString &name, T = nullptr) { T ret = qobject_cast(objectInternal(name, QByteArray(qobject_interface_iid()))); Q_ASSERT(ret); return ret; } /*! * Retrieve object implementing interface @p T. * * This only works if a single type was registered implementing this interface * using qobject_interface_iid as object name. * * In most cases this is the simplest way for tools to get an object. * * @note The "T = nullptr" is just to ensure a pointer type is given. */ template T object(T = nullptr) { const QByteArray interfaceName(qobject_interface_iid()); T ret = qobject_cast(objectInternal(QString::fromUtf8(interfaceName), interfaceName)); Q_ASSERT(ret); return ret; } typedef QObject *(*ClientObjectFactoryCallback)(const QString &, QObject *parent); /*! Register a callback for a factory to create remote object stubs for the given type. */ GAMMARAY_COMMON_EXPORT void registerClientObjectFactoryCallbackInternal(const QByteArray &type, ClientObjectFactoryCallback callback); /*! * Register a callback for a factory of a given interface to create remote object stubs for the given type. * * @note The "T = nullptr" is just to ensure a pointer type is given. */ template void registerClientObjectFactoryCallback(ClientObjectFactoryCallback callback, T = nullptr) { registerClientObjectFactoryCallbackInternal(QByteArray(qobject_interface_iid()), callback); } /*! * Register a newly created model with the given name. * * @note This must not be called directly by anything but the probe/server side. * User code must use Probe::registerModel() instead! */ GAMMARAY_COMMON_EXPORT void registerModelInternal(const QString &name, QAbstractItemModel *model); /*! Retrieve a model by name. */ GAMMARAY_COMMON_EXPORT QAbstractItemModel *model(const QString &name); typedef QAbstractItemModel *(*ModelFactoryCallback)(const QString &); /*! Set a callback for the case that a model was requested but had not been registered before. */ GAMMARAY_COMMON_EXPORT void setModelFactoryCallback(ModelFactoryCallback callback); /*! Register a newly created selection model. */ GAMMARAY_COMMON_EXPORT void registerSelectionModel(QItemSelectionModel *selectionModel); /*! Unregisters a selection model. You have to take care of deletion yourself. */ GAMMARAY_COMMON_EXPORT void unregisterSelectionModel(QItemSelectionModel *selectionModel); /*! Checks whether a selection model for the given @p model is registered already. */ GAMMARAY_COMMON_EXPORT bool hasSelectionModel(QAbstractItemModel *model); /*! Retrieve the selection model for @p model. */ GAMMARAY_COMMON_EXPORT QItemSelectionModel *selectionModel(QAbstractItemModel *model); typedef QItemSelectionModel *(*selectionModelFactoryCallback)(QAbstractItemModel *); /*! Set a callback for the case that a selection model was requested but had not been registered before. */ GAMMARAY_COMMON_EXPORT void setSelectionModelFactoryCallback(selectionModelFactoryCallback callback); /*! Clear all registered objects. * * This also destroys all objects created by factory methods registered here, but not externally * created objects that have just been registered here. * Useful when the probe is deleted, or the client lost the connection. */ GAMMARAY_COMMON_EXPORT void clear(); } } #endif // GAMMARAY_OBJECTBROKER_H gammaray-2.11.3/common/objectid.h000066400000000000000000000075761413310443000166300ustar00rootroot00000000000000/* objectid.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Kevin Funk Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_OBJECTID_H #define GAMMARAY_OBJECTID_H #include #include #include #include #include #if QT_VERSION < QT_VERSION_CHECK(5, 6, 0) #include #endif namespace GammaRay { /** @brief Type-safe and cross-process object identifier vector. */ using ObjectIds = QVector; /** @brief Type-safe and cross-process object identifier. */ class ObjectId { public: enum Type { Invalid, QObjectType, VoidStarType }; explicit ObjectId(void *obj, const QByteArray &typeName) : m_type(VoidStarType) , m_id(reinterpret_cast(obj)) , m_typeName(typeName) {} explicit ObjectId(QObject *obj) : m_type(QObjectType) , m_id(reinterpret_cast(obj)) {} explicit ObjectId() = default; inline bool isNull() const { return m_id == 0; } inline quint64 id() const { return m_id; } inline Type type() const { return m_type; } inline QByteArray typeName() const { return m_typeName; } inline QObject *asQObject() const { Q_ASSERT(m_type == QObjectType); return reinterpret_cast(m_id); } template inline T asQObjectType() const { return qobject_cast(asQObject()); } inline void *asVoidStar() const { Q_ASSERT(m_type == VoidStarType); return reinterpret_cast(m_id); } inline operator quint64() const { return m_id; } private: friend QDataStream &operator<<(QDataStream &out, const ObjectId &id); friend QDataStream &operator>>(QDataStream &out, ObjectId &id); Type m_type = Invalid; quint64 m_id = 0; QByteArray m_typeName; }; ///@cond internal inline QDebug &operator<<(QDebug dbg, const ObjectId &id) { dbg.nospace() << "ObjectId(" << id.type() << ", " << id.id() << ", " << id.typeName() << ")"; return dbg.space(); } #if QT_VERSION < QT_VERSION_CHECK(5, 6, 0) inline bool operator<(const ObjectIds &lhs, const ObjectIds &rhs) { return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); } #endif inline QDataStream &operator<<(QDataStream &out, const ObjectId &id) { out << static_cast(id.m_type); out << id.m_id; out << id.m_typeName; return out; } inline QDataStream &operator>>(QDataStream &in, ObjectId &id) { quint8 u; in >> u; id.m_type = static_cast(u); in >> id.m_id; in >> id.m_typeName; return in; } ///@endcond } Q_DECLARE_METATYPE(GammaRay::ObjectId) Q_DECLARE_METATYPE(GammaRay::ObjectIds) QT_BEGIN_NAMESPACE Q_DECLARE_TYPEINFO(GammaRay::ObjectId, Q_MOVABLE_TYPE); Q_DECLARE_TYPEINFO(GammaRay::ObjectIds, Q_MOVABLE_TYPE); QT_END_NAMESPACE #endif // GAMMARAY_OBJECTID_H gammaray-2.11.3/common/objectidfilterproxymodel.cpp000066400000000000000000000050411413310443000224750ustar00rootroot00000000000000/* objectidfilterproxymodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Filipe Azevedo Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "objectidfilterproxymodel.h" #include "objectmodel.h" using namespace GammaRay; ObjectIdsFilterProxyModel::ObjectIdsFilterProxyModel(QObject *parent) : KRecursiveFilterProxyModel(parent) { } void ObjectIdsFilterProxyModel::sort(int column, Qt::SortOrder order) { Q_UNUSED(column); Q_UNUSED(order); } GammaRay::ObjectIds ObjectIdsFilterProxyModel::ids() const { return m_ids; } void ObjectIdsFilterProxyModel::setIds(const GammaRay::ObjectIds &ids) { if (m_ids == ids) return; m_ids = ids; invalidateFilter(); } bool ObjectIdsFilterProxyModel::acceptRow(int source_row, const QModelIndex &source_parent) const { // shortcut for the common case, the object id stuff below allocates memory and does expensive model lookups if (m_ids.isEmpty()) { return KRecursiveFilterProxyModel::acceptRow(source_row, source_parent); } const QModelIndex source_index = sourceModel()->index(source_row, 0, source_parent); if (!source_index.isValid()) { return false; } const GammaRay::ObjectId id = source_index.data(ObjectModel::ObjectIdRole).value(); if (id.isNull() || !filterAcceptsObjectId(id)) { return false; } return KRecursiveFilterProxyModel::acceptRow(source_row, source_parent); } bool ObjectIdsFilterProxyModel::filterAcceptsObjectId(const GammaRay::ObjectId &id) const { return m_ids.contains(id); } gammaray-2.11.3/common/objectidfilterproxymodel.h000066400000000000000000000053461413310443000221520ustar00rootroot00000000000000/* objectidfilterproxymodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Filipe Azevedo Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_OBJECTIDFILTERPROXYMODEL_H #define GAMMARAY_OBJECTIDFILTERPROXYMODEL_H #include "gammaray_common_export.h" #include "3rdparty/kde/krecursivefilterproxymodel.h" #include namespace GammaRay { /** * @brief A KRecursiveFilterProxyModel for ObjectIds. * * Filter in and sort according to the objects list. */ class GAMMARAY_COMMON_EXPORT ObjectIdsFilterProxyModel : public KRecursiveFilterProxyModel { Q_OBJECT public: /** * Constructor. * @param parent is the parent object for this instance. */ explicit ObjectIdsFilterProxyModel(QObject *parent = nullptr); void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override; GammaRay::ObjectIds ids() const; void setIds(const GammaRay::ObjectIds &ids); protected: /** * Determines if the item in the specified row can be included in the model. * @param source_row is a non-zero integer representing the row of the item. * @param source_parent is the parent QModelIndex for this model. * @return true if the item in the row can be included in the model; * otherwise returns false. */ bool acceptRow(int source_row, const QModelIndex &source_parent) const override; /** * Determines if the specified ObjectID can be included in the model. * @param id is a ref to the ObjectId to test. * @return true if the ObjectId can be included in the model; false otherwise. */ bool filterAcceptsObjectId(const GammaRay::ObjectId &id) const; private: GammaRay::ObjectIds m_ids; }; } #endif // GAMMARAY_OBJECIDFILTERPROXYMODEL_H gammaray-2.11.3/common/objectmodel.h000066400000000000000000000044251413310443000173220ustar00rootroot00000000000000/* objectmodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ /** @file This file is part of the GammaRay Plugin API and declares the public object model roles. @brief Declares the public object model roles. @author Volker Krause \ */ #ifndef GAMMARAY_OBJECTMODEL_H #define GAMMARAY_OBJECTMODEL_H #include "modelroles.h" namespace GammaRay { /*! * GammaRay Object Models. * * Public object model roles, for use by tool plugins without needing access * to the real object model classes. */ namespace ObjectModel { /*! Role enum, to be used with the object list and tree models. */ enum Role { ObjectRole = GammaRay::UserRole + 1, /**< Pointer to the represented object (available in the probe code only). */ ObjectIdRole, /**< ObjectId instance for represented object. */ CreationLocationRole, /**< Source location where this object was created, if known. */ DeclarationLocationRole,/**< Source location where the type for this object has been declared, if known. */ DecorationIdRole, /**< The classes icon id to display as Qt::DecorationRole (see ClientDecorationIdentityProxyModel). */ UserRole /**< UserRole, as defined by Qt. */ }; } } #endif gammaray-2.11.3/common/paintanalyzerinterface.cpp000066400000000000000000000047231413310443000221310ustar00rootroot00000000000000/* paintanalyzerinterface.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "paintanalyzerinterface.h" #include using namespace GammaRay; PaintAnalyzerInterface::PaintAnalyzerInterface(const QString &name, QObject *parent) : QObject(parent) , m_name(name) , m_hasArgumentDetails(false) , m_hasStackTrace(false) { ObjectBroker::registerObject(name, this); qRegisterMetaTypeStreamOperators(); } QString PaintAnalyzerInterface::name() const { return m_name; } bool PaintAnalyzerInterface::hasArgumentDetails() const { return m_hasArgumentDetails; } void PaintAnalyzerInterface::setHasArgumentDetails(bool hasDetails) { if (m_hasArgumentDetails == hasDetails) return; m_hasArgumentDetails = hasDetails; emit hasArgumentDetailsChanged(hasDetails); } bool PaintAnalyzerInterface::hasStackTrace() const { return m_hasStackTrace; } void PaintAnalyzerInterface::setHasStackTrace(bool hasStackTrace) { if (m_hasStackTrace == hasStackTrace) return; m_hasStackTrace = hasStackTrace; emit hasStackTraceChanged(hasStackTrace); } namespace GammaRay { QDataStream& operator<<(QDataStream &stream, const PaintAnalyzerFrameData &data) { stream << data.clipPath; return stream; } QDataStream& operator>>(QDataStream &stream, PaintAnalyzerFrameData &data) { stream >> data.clipPath; return stream; } } gammaray-2.11.3/common/paintanalyzerinterface.h000066400000000000000000000052701413310443000215740ustar00rootroot00000000000000/* paintanalyzerinterface.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_PAINTANALYZERINTERFACE_H #define GAMMARAY_PAINTANALYZERINTERFACE_H #include "gammaray_common_export.h" #include #include #include #include QT_BEGIN_NAMESPACE class QImage; QT_END_NAMESPACE namespace GammaRay { /** Communication interface for GammaRay::PaintAnalyzer. */ class GAMMARAY_COMMON_EXPORT PaintAnalyzerInterface : public QObject { Q_OBJECT Q_PROPERTY(bool hasArgumentDetails READ hasArgumentDetails WRITE setHasArgumentDetails NOTIFY hasArgumentDetailsChanged) Q_PROPERTY(bool hasStackTrace READ hasStackTrace WRITE setHasStackTrace NOTIFY hasStackTraceChanged) public: explicit PaintAnalyzerInterface(const QString &name, QObject *parent = nullptr); QString name() const; bool hasArgumentDetails() const; void setHasArgumentDetails(bool hasDetails); bool hasStackTrace() const; void setHasStackTrace(bool hasStackTrace); Q_SIGNALS: void hasArgumentDetailsChanged(bool); void hasStackTraceChanged(bool); private: QString m_name; bool m_hasArgumentDetails; bool m_hasStackTrace; }; struct PaintAnalyzerFrameData { QPainterPath clipPath; }; QDataStream &operator<<(QDataStream &stream, const GammaRay::PaintAnalyzerFrameData &data); QDataStream &operator>>(QDataStream &stream, GammaRay::PaintAnalyzerFrameData &data); } Q_DECLARE_METATYPE(GammaRay::PaintAnalyzerFrameData) QT_BEGIN_NAMESPACE Q_DECLARE_INTERFACE(GammaRay::PaintAnalyzerInterface, "com.kdab.GammaRay.PaintAnalyzer/1.0") QT_END_NAMESPACE #endif // GAMMARAY_PAINTANALYZERINTERFACE_H gammaray-2.11.3/common/paintbuffermodelroles.h000066400000000000000000000026261413310443000214270ustar00rootroot00000000000000/* paintbuffermodelroles.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_PAINTBUFFERMODELROLES_H #define GAMMARAY_PAINTBUFFERMODELROLES_H #include "modelroles.h" namespace GammaRay { namespace PaintBufferModelRoles { enum Role { ValueRole = GammaRay::UserRole + 1, ClipPathRole, MaxCostRole, ObjectIdRole, }; } } #endif gammaray-2.11.3/common/paths.cpp000066400000000000000000000140651413310443000165060ustar00rootroot00000000000000/* paths.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include "paths.h" #include "selflocator.h" #include #include #include #include #include #include namespace GammaRay { struct PathData { QString rootPath; QMutex mutex; // rootPath is set from the probe setting receiver thread }; Q_GLOBAL_STATIC(PathData, s_pathData) namespace Paths { QString rootPath() { QMutexLocker locker(&s_pathData()->mutex); if (s_pathData()->rootPath.isEmpty()) { QFileInfo fi(SelfLocator::findMe()); fi.setFile(fi.absolutePath() + QLatin1String("/" GAMMARAY_INVERSE_LIB_DIR)); if (fi.isDir()) s_pathData()->rootPath = fi.absoluteFilePath(); } Q_ASSERT(!s_pathData()->rootPath.isEmpty()); return s_pathData()->rootPath; } void setRootPath(const QString &rootPath) { Q_ASSERT(!rootPath.isEmpty()); Q_ASSERT(QDir(rootPath).exists()); Q_ASSERT(QDir(rootPath).isAbsolute()); QMutexLocker locker(&s_pathData()->mutex); s_pathData()->rootPath = rootPath; } void setRelativeRootPath(const char *relativeRootPath) { Q_ASSERT(relativeRootPath); setRootPath(QCoreApplication::applicationDirPath() + QDir::separator() + QLatin1String(relativeRootPath)); } QString probePath(const QString &probeABI, const QString &rootPath) { #ifndef GAMMARAY_INSTALL_QT_LAYOUT return rootPath + QDir::separator() + QLatin1String(GAMMARAY_PLUGIN_INSTALL_DIR) + QDir::separator() + QLatin1String(GAMMARAY_PLUGIN_VERSION) + QDir::separator() + probeABI; #else Q_UNUSED(probeABI); return rootPath + QDir::separator() + QLatin1String(GAMMARAY_PROBE_INSTALL_DIR); #endif } QString binPath() { return rootPath() + QDir::separator() + QLatin1String(GAMMARAY_BIN_INSTALL_DIR); } QString libexecPath() { return rootPath() + QDir::separator() + QLatin1String(GAMMARAY_LIBEXEC_INSTALL_DIR); } QString currentProbePath() { return probePath(QStringLiteral(GAMMARAY_PROBE_ABI)); } static void addPluginPath(QStringList &list, const QString &path) { QFileInfo fi(path); if (!fi.isDir()) return; list.push_back(fi.canonicalFilePath()); } QStringList pluginPaths(const QString &probeABI) { QStringList l; // TODO based on environment variable for custom plugins // based on rootPath() addPluginPath(l, rootPath() + QLatin1String("/" GAMMARAY_PLUGIN_INSTALL_DIR "/" GAMMARAY_PLUGIN_VERSION "/") + probeABI); addPluginPath(l, rootPath() + QLatin1String("/" GAMMARAY_PLUGIN_INSTALL_DIR)); // based on Qt plugin search paths foreach (const auto &path, QCoreApplication::libraryPaths()) { addPluginPath(l, path + QLatin1String("/gammaray/" GAMMARAY_PLUGIN_VERSION "/") + probeABI); addPluginPath(l, path + QLatin1String("/gammaray")); #if defined(Q_OS_ANDROID) && QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) addPluginPath(l, path); #endif } // based on Qt's own install layout and/or qt.conf const auto path = QLibraryInfo::location(QLibraryInfo::PluginsPath); addPluginPath(l, path + QLatin1String("/gammaray/" GAMMARAY_PLUGIN_VERSION "/") + probeABI); addPluginPath(l, path + QLatin1String("/gammaray")); return l; } QStringList targetPluginPaths(const QString& probeABI) { QStringList l; // based on rootPath() addPluginPath(l, rootPath() + QLatin1String("/" GAMMARAY_TARGET_PLUGIN_INSTALL_DIR "/" GAMMARAY_PLUGIN_VERSION "/") + probeABI); addPluginPath(l, rootPath() + QLatin1String("/" GAMMARAY_TARGET_PLUGIN_INSTALL_DIR)); // based on Qt plugin search paths foreach (const auto &path, QCoreApplication::libraryPaths()) { addPluginPath(l, path + QLatin1String("/gammaray/" GAMMARAY_PLUGIN_VERSION "/") + probeABI + QLatin1String("/target")); addPluginPath(l, path + QLatin1String("/gammaray-target")); } // based on Qt's own install layout and/or qt.conf const auto path = QLibraryInfo::location(QLibraryInfo::PluginsPath); addPluginPath(l, path + QLatin1String("/gammaray/" GAMMARAY_PLUGIN_VERSION "/") + probeABI + QLatin1String("/target")); addPluginPath(l, path + QLatin1String("/gammaray-target")); return l; } QString currentPluginsPath() { #ifndef GAMMARAY_INSTALL_QT_LAYOUT return probePath(QStringLiteral(GAMMARAY_PROBE_ABI)); #else return rootPath() + QDir::separator() + QStringLiteral(GAMMARAY_PLUGIN_INSTALL_DIR); #endif } QString libraryExtension() { #ifdef Q_OS_WIN return QStringLiteral(".dll"); #elif defined(Q_OS_MAC) return QStringLiteral(".dylib"); #elif defined(Q_OS_ANDROID) && QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) return QLatin1Char('_') + QLatin1String(ANDROID_ABI) + QLatin1String(".so"); #else return QStringLiteral(".so"); #endif } QString pluginExtension() { #ifdef Q_OS_MAC return QStringLiteral(".so"); #else return libraryExtension(); #endif } QString documentationPath() { return rootPath() + QLatin1Char('/') + QLatin1String(GAMMARAY_QCH_INSTALL_DIR); } } } gammaray-2.11.3/common/paths.h000066400000000000000000000104261413310443000161500ustar00rootroot00000000000000/* paths.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_PATHS_H #define GAMMARAY_PATHS_H #include "gammaray_common_export.h" #include namespace GammaRay { /*! Methods to determine install locations of the various bits of GammaRay. */ namespace Paths { /*! Absolute path to the root of the GammaRay installation, anything else * is relative to this. */ GAMMARAY_COMMON_EXPORT QString rootPath(); /*! Call this to set the root of the GammaRay install location, as early as * possible. The install location is usually determined by using * QCoreApplication::applicationDirPath() or the probe path, and the known relative * install location of the corresponding component. */ GAMMARAY_COMMON_EXPORT void setRootPath(const QString &rootPath); /*! Convenience version of the above, for root paths relative to * QCoreApplication::applicationDirPath(). */ GAMMARAY_COMMON_EXPORT void setRelativeRootPath(const char *relativeRootPath); /*! Returns the probe and plugin base path for the given probe ABI. * If @p probeABI is empty, the path containing the probes is returned. */ GAMMARAY_COMMON_EXPORT QString probePath(const QString &probeABI, const QString &rootPath = Paths::rootPath()); /*! Returns the path containing the GammaRay injector executable. */ GAMMARAY_COMMON_EXPORT QString binPath(); /*! Returns the path containing helper executables. */ GAMMARAY_COMMON_EXPORT QString libexecPath(); /*! Returns the path to the current probe location, probePath(GAMMARAY_PROBE_ABI). * @note Must not be used outside of probe code! * @deprecated Use Paths::probePath instead. */ GAMMARAY_COMMON_DEPRECATED_EXPORT QString currentProbePath(); /*! Returns a list of probe plugin search paths for the given ABI. * This includes candidates based on Qt's plugin search paths, and GammaRay's * install layout. * @param probeABI The ABI identifier we are looking for plugins for. If you * are actually loading plugins, this must be GAMMARAY_PROBE_ABI. */ GAMMARAY_COMMON_EXPORT QStringList pluginPaths(const QString &probeABI); /*! Returns a list of target plugin search paths for the given ABI. * This includes candidates based on Qt's plugin search paths, and GammaRay's * install layout. * @param probeABI The ABI identifier we are looking for plugins for. If you * are actually loading plugins, this must be GAMMARAY_PROBE_ABI. */ GAMMARAY_COMMON_EXPORT QStringList targetPluginPaths(const QString &probeABI); /*! Returns the path to the current plugins location. * This is the same as currentProbePath() when using the GammaRay installation layout, * but differs with e.g. the Android installation layout. * @note Must not be used outside of probe or client/ui code! * @deprecated Use Paths::pluginsPaths instead. */ GAMMARAY_COMMON_DEPRECATED_EXPORT QString currentPluginsPath(); /*! Returns the file extension used on the current platform for libraries. */ GAMMARAY_COMMON_EXPORT QString libraryExtension(); /*! Returns the file extension used on the current platform for plugins. */ GAMMARAY_COMMON_EXPORT QString pluginExtension(); /*! Path to the installed .qch/.qhc documentation files. */ GAMMARAY_COMMON_EXPORT QString documentationPath(); } } #endif // GAMMARAY_PATHS_H gammaray-2.11.3/common/plugininfo.cpp000066400000000000000000000122351413310443000175360ustar00rootroot00000000000000/* plugininfo.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "plugininfo.h" #include "paths.h" #include #include #include #include #include #include #include #include #include #include #include using namespace GammaRay; PluginInfo::PluginInfo() { init(); } PluginInfo::PluginInfo(const QString& path) { init(); // OSX has broken QLibrary::isLibrary() - QTBUG-50446 if (QLibrary::isLibrary(path) || path.endsWith(Paths::pluginExtension(), Qt::CaseInsensitive)) initFromJSON(path); } PluginInfo::PluginInfo(const QStaticPlugin &staticPlugin) { init(); m_staticPlugin = staticPlugin; initFromJSON(staticPlugin.metaData()); } void PluginInfo::init() { m_remoteSupport = true; m_hidden = false; m_staticPlugin.instance = nullptr; m_staticPlugin.rawMetaData = nullptr; } QString PluginInfo::path() const { return m_path; } QString PluginInfo::id() const { return m_id; } QString PluginInfo::interfaceId() const { return m_interface; } QStringList PluginInfo::supportedTypes() const { return m_supportedTypes; } bool PluginInfo::remoteSupport() const { return m_remoteSupport; } QString PluginInfo::name() const { return m_name; } bool PluginInfo::isHidden() const { return m_hidden; } QVector PluginInfo::selectableTypes() const { return m_selectableTypes; } bool PluginInfo::isValid() const { return !m_id.isEmpty() && (isStatic() || !m_path.isEmpty()) && !m_interface.isEmpty(); } static QString readLocalized(const QLocale &locale, const QJsonObject &obj, const QString &baseKey) { const QString qtcLanguage = qApp->property("qtc_locale").toString(); QStringList names = locale.uiLanguages(); if (!qtcLanguage.isEmpty()) names.prepend(qtcLanguage); for (auto name : qAsConst(names)) { const QLocale uiLocale(name); // We are natively English, skip... if (uiLocale.language() == QLocale::English || uiLocale.language() == QLocale::C) { return obj.value(baseKey).toString(); } // Check against name QString key = baseKey + '[' + name + ']'; auto it = obj.find(key); // Check against language if (it == obj.end()) { name.replace('-', '_'); name = name.section(QLatin1Char('_'), 0, -2); if (!name.isEmpty()) { key = baseKey + '[' + name + ']'; it = obj.find(key); } } if (it != obj.end()) return it.value().toString(); } return obj.value(baseKey).toString(); } bool PluginInfo::isStatic() const { return m_staticPlugin.instance && m_staticPlugin.rawMetaData; } QObject* PluginInfo::staticInstance() const { Q_ASSERT(isStatic()); return m_staticPlugin.instance(); } void PluginInfo::initFromJSON(const QString &path) { const QPluginLoader loader(path); const QJsonObject metaData = loader.metaData(); initFromJSON(metaData); m_path = path; } void PluginInfo::initFromJSON(const QJsonObject &metaData) { m_interface = metaData.value(QStringLiteral("IID")).toString(); const QJsonObject customData = metaData.value(QStringLiteral("MetaData")).toObject(); m_id = customData.value(QStringLiteral("id")).toString(); m_name = readLocalized(QLocale(), customData, QStringLiteral("name")); m_remoteSupport = customData.value(QStringLiteral("remoteSupport")).toBool(true); m_hidden = customData.value(QStringLiteral("hidden")).toBool(false); const QJsonArray types = customData.value(QStringLiteral("types")).toArray(); m_supportedTypes.reserve(types.size()); for (auto it = types.constBegin(); it != types.constEnd(); ++it) m_supportedTypes.push_back((*it).toString()); const auto selectableTypes = customData.value(QStringLiteral("selectableTypes")).toArray(); m_selectableTypes.reserve(selectableTypes.size()); for (auto &&selectable : selectableTypes) m_selectableTypes.push_back(selectable.toString().toUtf8()); } gammaray-2.11.3/common/plugininfo.h000066400000000000000000000045131413310443000172030ustar00rootroot00000000000000/* plugininfo.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_PLUGININFO_H #define GAMMARAY_PLUGININFO_H #include #include #include #include QT_BEGIN_NAMESPACE class QJsonObject; QT_END_NAMESPACE namespace GammaRay { /** Meta-data about a specific plugin. * This abstracts Qt5-style embedded JSON data and Qt4-style desktop files. */ class PluginInfo { public: PluginInfo(); explicit PluginInfo(const QString &path); explicit PluginInfo(const QStaticPlugin &staticPlugin); QString path() const; QString id() const; QString interfaceId() const; QStringList supportedTypes() const; QString name() const; bool remoteSupport() const; bool isHidden() const; QVector selectableTypes() const; bool isValid() const; bool isStatic() const; QObject* staticInstance() const; private: void init(); void initFromJSON(const QString &path); void initFromJSON(const QJsonObject& metaData); QString m_path; QStaticPlugin m_staticPlugin; QString m_id; QString m_interface; QStringList m_supportedTypes; QString m_name; QVector m_selectableTypes; bool m_remoteSupport; bool m_hidden; }; } #endif // GAMMARAY_PLUGININFO_H gammaray-2.11.3/common/pluginmanager.cpp000066400000000000000000000071141413310443000202150ustar00rootroot00000000000000/* pluginmanager.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Kevin Funk Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include "pluginmanager.h" #include "paths.h" #include #include #include #include #include #include #define IF_DEBUG(x) using namespace GammaRay; using namespace std; PluginManagerBase::PluginManagerBase(QObject *parent) : m_parent(parent) { } PluginManagerBase::~PluginManagerBase() = default; QStringList PluginManagerBase::pluginPaths() const { #ifndef GAMMARAY_STATIC_PROBE return Paths::pluginPaths(GAMMARAY_PROBE_ABI); #else return QStringList(); #endif } QStringList PluginManagerBase::pluginFilter() const { QStringList filter; #if defined(GAMMARAY_INSTALL_QT_LAYOUT) filter.push_back(QStringLiteral("*") + QStringLiteral( GAMMARAY_PROBE_ABI) + Paths::pluginExtension()); #else filter.push_back(QStringLiteral("*") + Paths::pluginExtension()); #endif return filter; } void PluginManagerBase::scan(const QString &serviceType) { m_errors.clear(); QStringList loadedPluginNames; foreach (const auto &staticPlugin, QPluginLoader::staticPlugins()) { PluginInfo pluginInfo(staticPlugin); if (!pluginInfo.isValid() || loadedPluginNames.contains(pluginInfo.id()) || pluginInfo.interfaceId() != serviceType) { qDebug() << "skipping static plugin " << pluginInfo.id() << pluginInfo.interfaceId(); continue; } if (createProxyFactory(pluginInfo, m_parent)) loadedPluginNames.push_back(pluginInfo.id()); } foreach (const QString &pluginPath, pluginPaths()) { const QDir dir(pluginPath); IF_DEBUG(cout << "checking plugin path: " << qPrintable(dir.absolutePath()) << endl); foreach (const QString &plugin, dir.entryList(pluginFilter(), QDir::Files)) { const QString pluginFile = dir.absoluteFilePath(plugin); const PluginInfo pluginInfo(pluginFile); if (!pluginInfo.isValid() || loadedPluginNames.contains(pluginInfo.id())) continue; if (pluginInfo.interfaceId() != serviceType) { IF_DEBUG( qDebug() << Q_FUNC_INFO << "skipping" << pluginFile << "not supporting service type" << serviceType << "service types are: " << pluginInfo.interfaceId(); ) continue; } if (createProxyFactory(pluginInfo, m_parent)) loadedPluginNames.push_back(pluginInfo.id()); } } } gammaray-2.11.3/common/pluginmanager.h000066400000000000000000000071371413310443000176670ustar00rootroot00000000000000/* pluginmanager.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Kevin Funk Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_PLUGINMANAGER_H #define GAMMARAY_PLUGINMANAGER_H #include "plugininfo.h" #include #include #include #include #include #include namespace GammaRay { class PluginLoadError; typedef QList PluginLoadErrors; class PluginLoadError { public: PluginLoadError(const QString &_pluginFile, const QString &_errorString) : pluginFile(_pluginFile) , errorString(_errorString) { } QString pluginName() const { return QFileInfo(pluginFile).baseName(); } public: QString pluginFile; QString errorString; }; class PluginManagerBase { public: /** * @param parent This is the parent object for all objects created by the plugins */ explicit PluginManagerBase(QObject *parent = nullptr); virtual ~PluginManagerBase(); QList errors() const { return m_errors; } protected: virtual bool createProxyFactory(const PluginInfo &pluginInfo, QObject *parent) = 0; void scan(const QString &serviceType); QStringList pluginPaths() const; QStringList pluginFilter() const; QList m_errors; QObject *m_parent; private: Q_DISABLE_COPY(PluginManagerBase) }; template class PluginManager : public PluginManagerBase { public: explicit inline PluginManager(QObject *parent = nullptr) : PluginManagerBase(parent) { const QString iid = QString::fromLatin1(qobject_interface_iid()); Q_ASSERT(!iid.isEmpty()); const QString serviceType = iid.split(QLatin1Char('/')).first(); scan(serviceType); } inline ~PluginManager() {} inline QVector plugins() { return m_plugins; } protected: bool createProxyFactory(const PluginInfo &pluginInfo, QObject *parent) override { auto *proxy = new Proxy(pluginInfo, parent); if (!proxy->isValid()) { m_errors << PluginLoadError(pluginInfo.path(), qApp->translate("GammaRay::PluginManager", "Failed to load plugin: %1").arg(proxy->errorString())); std::cerr << "invalid plugin " << qPrintable(pluginInfo.path()) << std::endl; delete proxy; } else { m_plugins.push_back(proxy); return true; } return false; } private: QVector m_plugins; }; } #endif // GAMMARAY_PLUGINMANAGER_H gammaray-2.11.3/common/probecontrollerinterface.cpp000066400000000000000000000025421413310443000224600ustar00rootroot00000000000000/* probecontrollerinterface.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "probecontrollerinterface.h" using namespace GammaRay; ProbeControllerInterface::ProbeControllerInterface(QObject *parent) : QObject(parent) { } ProbeControllerInterface::~ProbeControllerInterface() = default; gammaray-2.11.3/common/probecontrollerinterface.h000066400000000000000000000037631413310443000221330ustar00rootroot00000000000000/* probecontrollerinterface.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_PROBECONTROLLERINTERFACE_H #define GAMMARAY_PROBECONTROLLERINTERFACE_H #include #include #include #include #include namespace GammaRay { /*! Probe and host process remote control functions. */ class ProbeControllerInterface : public QObject { Q_OBJECT public: explicit ProbeControllerInterface(QObject *parent = nullptr); ~ProbeControllerInterface() override; /*! Terminate host application. */ virtual void quitHost() = 0; /*! Detach GammaRay but keep host application running. */ virtual void detachProbe() = 0; private: Q_DISABLE_COPY(ProbeControllerInterface) }; } QT_BEGIN_NAMESPACE Q_DECLARE_INTERFACE(GammaRay::ProbeControllerInterface, "com.kdab.GammaRay.ProbeControllerInterface") QT_END_NAMESPACE #endif // GAMMARAY_PROBECONTROLLERINTERFACE_H gammaray-2.11.3/common/problem.h000066400000000000000000000045101413310443000164660ustar00rootroot00000000000000/* problem.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2018-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_PROBLEM_H #define GAMMARAY_PROBLEM_H // Own #include "objectid.h" #include "common/sourcelocation.h" // Qt #include // Std #include #include namespace GammaRay { struct Problem { enum Severity { Info = 1, Warning, Error }; enum FindingCategory { Unknown, Live, ///< is added and removed as the problem arises/vanishes by the reporting tool Scan, ///< is added in response to a scan request and removed by the problem collector in advance to the next scan Permanent ///< is valid during the whole application lifetime and not meant to be removed at all }; Problem() : severity(Error) , findingCategory(Unknown) {} Severity severity; ObjectId object; QString description; QVector locations; QString reportingTool; ///< Tool which reported the issue QString problemId; ///< tool-specific unique id for the problem FindingCategory findingCategory; bool operator==(const Problem &other) const { return problemId == other.problemId; } }; } Q_DECLARE_METATYPE(GammaRay::Problem::Severity) #endif // GAMMARAY_PROBLEM_H gammaray-2.11.3/common/propertycontrollerinterface.cpp000066400000000000000000000036341413310443000232400ustar00rootroot00000000000000/* propertycontrollerinterface.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "propertycontrollerinterface.h" #include "objectbroker.h" using namespace GammaRay; PropertyControllerInterface::PropertyControllerInterface(const QString &name, QObject *parent) : QObject(parent) , m_name(name) { ObjectBroker::registerObject(name, this); } PropertyControllerInterface::~PropertyControllerInterface() = default; QString PropertyControllerInterface::name() const { return m_name; } QStringList PropertyControllerInterface::availableExtensions() const { return m_availableExtensions; } void PropertyControllerInterface::setAvailableExtensions(const QStringList &availableExtensions) { if (m_availableExtensions == availableExtensions) return; m_availableExtensions = availableExtensions; emit availableExtensionsChanged(); } gammaray-2.11.3/common/propertycontrollerinterface.h000066400000000000000000000042421413310443000227010ustar00rootroot00000000000000/* propertycontrollerinterface.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_PROPERTYCONTROLLERINTERFACE_H #define GAMMARAY_PROPERTYCONTROLLERINTERFACE_H #include #include namespace GammaRay { /** @brief Client/Server interface of the property editor. */ class PropertyControllerInterface : public QObject { Q_OBJECT Q_PROPERTY( QStringList availableExtensions READ availableExtensions WRITE setAvailableExtensions NOTIFY availableExtensionsChanged) public: explicit PropertyControllerInterface(const QString &name, QObject *parent = nullptr); ~PropertyControllerInterface() override; QString name() const; QStringList availableExtensions() const; void setAvailableExtensions(const QStringList &availableExtensions); signals: void availableExtensionsChanged(); private: QString m_name; QStringList m_availableExtensions; }; } QT_BEGIN_NAMESPACE Q_DECLARE_INTERFACE(GammaRay::PropertyControllerInterface, "com.kdab.GammaRay.PropertyControllerInterface") QT_END_NAMESPACE #endif // GAMMARAY_PROPERTYCONTROLLERINTERFACE_H gammaray-2.11.3/common/propertymodel.h000066400000000000000000000046131413310443000177370ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_PROPERTYMODEL_H #define GAMMARAY_PROPERTYMODEL_H #include "modelroles.h" #include #include namespace GammaRay { /** * @brief GammaRay property model roles. * * Public property model roles, for use by tool plugins without needing access * to the real property model classes. */ namespace PropertyModel { /** Role enum, to be used with the property models. */ enum Role { ActionRole = GammaRay::UserRole + 1, /**< the property action role */ DeprecatedRole, /**< @deprecated do not use */ ObjectIdRole, ResetActionRole, PropertyFlagsRole, PropertyRevisionRole, NotifySignalRole }; /** Available property actions. */ enum Action { NoAction = 0, Delete = 1, Reset = 2, NavigateTo = 4 }; /** Available columns. */ enum Column { PropertyColumn = 0, ValueColumn, TypeColumn, ClassColumn }; /** Property flags. * @see QMetaProperty */ enum PropertyFlag { None = 0, Constant = 1, Designable = 2, Final = 4, Resetable = 8, Scriptable = 16, Stored = 32, User = 64, Writable = 128 }; Q_DECLARE_FLAGS(PropertyFlags, PropertyFlag) } } Q_DECLARE_OPERATORS_FOR_FLAGS(GammaRay::PropertyModel::PropertyFlags) Q_DECLARE_METATYPE(GammaRay::PropertyModel::PropertyFlags) #endif gammaray-2.11.3/common/propertysyncer.cpp000066400000000000000000000155721413310443000205030ustar00rootroot00000000000000/* propertysyncer.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "propertysyncer.h" #include "message.h" #include #include #include #include using namespace GammaRay; static int qobjectPropertyOffset() { return QObject::staticMetaObject.propertyCount(); } PropertySyncer::PropertySyncer(QObject *parent) : QObject(parent) , m_address(Protocol::InvalidObjectAddress) , m_initialSync(false) { } PropertySyncer::~PropertySyncer() = default; void PropertySyncer::setRequestInitialSync(bool initialSync) { m_initialSync = initialSync; } void PropertySyncer::addObject(Protocol::ObjectAddress addr, QObject *obj) { Q_ASSERT(addr != Protocol::InvalidObjectAddress); Q_ASSERT(obj); if (qobjectPropertyOffset() == obj->metaObject()->propertyCount()) return; // no properties we could sync for (int i = qobjectPropertyOffset(); i < obj->metaObject()->propertyCount(); ++i) { const auto prop = obj->metaObject()->property(i); if (!prop.hasNotifySignal()) continue; connect(obj, QByteArray("2") + prop.notifySignal().methodSignature(), this, SLOT(propertyChanged())); } connect(obj, &QObject::destroyed, this, &PropertySyncer::objectDestroyed); ObjectInfo info; info.addr = addr; info.obj = obj; info.recursionLock = false; info.enabled = false; m_objects.push_back(info); } void PropertySyncer::setObjectEnabled(Protocol::ObjectAddress addr, bool enabled) { const auto it = std::find_if(m_objects.begin(), m_objects.end(), [addr]( const ObjectInfo &info) { return info.addr == addr; }); if (it == m_objects.end() || (*it).enabled == enabled) return; (*it).enabled = enabled; if (enabled && m_initialSync) { Message msg(m_address, Protocol::PropertySyncRequest); msg << addr; emit message(msg); } } Protocol::ObjectAddress PropertySyncer::address() const { return m_address; } void PropertySyncer::setAddress(Protocol::ObjectAddress addr) { m_address = addr; } void PropertySyncer::handleMessage(const GammaRay::Message &msg) { Q_ASSERT(msg.address() == m_address); switch (msg.type()) { case Protocol::PropertySyncRequest: { Protocol::ObjectAddress addr; msg >> addr; Q_ASSERT(addr != Protocol::InvalidObjectAddress); const auto it = std::find_if(m_objects.constBegin(), m_objects.constEnd(), [addr](const ObjectInfo &info) { return info.addr == addr; }); if (it == m_objects.constEnd()) break; QVector > values; const auto propCount = (*it).obj->metaObject()->propertyCount(); values.reserve(propCount); for (int i = qobjectPropertyOffset(); i < propCount; ++i) { const auto prop = (*it).obj->metaObject()->property(i); values.push_back(qMakePair(QByteArray(prop.name()), prop.read((*it).obj))); } Q_ASSERT(!values.isEmpty()); Message msg(m_address, Protocol::PropertyValuesChanged); msg << addr << (quint32)values.size(); for (const auto &value : qAsConst(values)) msg << value.first << value.second; emit message(msg); break; } case Protocol::PropertyValuesChanged: { Protocol::ObjectAddress addr; quint32 changeSize; msg >> addr >> changeSize; Q_ASSERT(addr != Protocol::InvalidObjectAddress); Q_ASSERT(changeSize > 0); auto it = std::find_if(m_objects.begin(), m_objects.end(), [addr](const ObjectInfo &info) { return info.addr == addr; }); if (it == m_objects.end()) break; for (quint32 i = 0; i < changeSize; ++i) { QByteArray propName; QVariant propValue; msg >> propName >> propValue; (*it).recursionLock = true; (*it).obj->setProperty(propName, propValue); // it can be invalid if as a result of the above call new objects have been registered for example it = std::find_if(m_objects.begin(), m_objects.end(), [addr](const ObjectInfo &info) { return info.addr == addr; }); Q_ASSERT(it != m_objects.end()); (*it).recursionLock = false; } break; } default: Q_ASSERT_X(false, "PropertySyncer::handleMessage", "Unexpected Gammaray::Message type encountered"); } } void PropertySyncer::propertyChanged() { const auto *obj = sender(); Q_ASSERT(obj); const auto it = std::find_if(m_objects.constBegin(), m_objects.constEnd(), [obj](const ObjectInfo &info) { return info.obj == obj; }); Q_ASSERT(it != m_objects.constEnd()); if ((*it).recursionLock || !(*it).enabled) return; const auto sigIndex = senderSignalIndex(); QVector > changes; for (int i = qobjectPropertyOffset(); i < obj->metaObject()->propertyCount(); ++i) { const auto prop = obj->metaObject()->property(i); if (prop.notifySignalIndex() != sigIndex) continue; changes.push_back(qMakePair(QByteArray(prop.name()), prop.read(obj))); } Q_ASSERT(!changes.isEmpty()); Message msg(m_address, Protocol::PropertyValuesChanged); msg << (*it).addr << (quint32)changes.size(); for (const auto &change : qAsConst(changes)) msg << change.first << change.second; emit message(msg); } void PropertySyncer::objectDestroyed(QObject *obj) { const auto it = std::find_if(m_objects.begin(), m_objects.end(), [obj](const ObjectInfo &info) { return info.obj == obj; }); Q_ASSERT(it != m_objects.end()); m_objects.erase(it); } gammaray-2.11.3/common/propertysyncer.h000066400000000000000000000057301413310443000201430ustar00rootroot00000000000000/* propertysyncer.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_PROPERTYSYNCER_H #define GAMMARAY_PROPERTYSYNCER_H #include "gammaray_common_export.h" #include #include #include namespace GammaRay { class Message; /** Infrastructure for syncing property values between a local and a remote object. */ class GAMMARAY_COMMON_EXPORT PropertySyncer : public QObject { Q_OBJECT public: explicit PropertySyncer(QObject *parent = nullptr); ~PropertySyncer() override; /** Add an object that should be monitored for to be synced property changes. */ void addObject(Protocol::ObjectAddress addr, QObject *obj); /** Enable property syncing for the object with address @p addr. * Use this to suspend property syncing for objects that aren't used on the client. * Property syncing is disabled by default. */ void setObjectEnabled(Protocol::ObjectAddress addr, bool enabled); /** Object address of the property syncer, for communicating with the other side. */ Protocol::ObjectAddress address() const; void setAddress(Protocol::ObjectAddress addr); /** Request the initial property states when adding new objects. * This is typically enabled on the client side. */ void setRequestInitialSync(bool initialSync); public slots: /** Feed in incoming network messages here. */ void handleMessage(const GammaRay::Message &msg); signals: /** Outgoing network messages, send those via Endpoint. */ void message(const GammaRay::Message &msg); private slots: void propertyChanged(); void objectDestroyed(QObject *obj); private: struct ObjectInfo { Protocol::ObjectAddress addr; QObject *obj; bool recursionLock; bool enabled; }; QVector m_objects; Protocol::ObjectAddress m_address; bool m_initialSync; }; } #endif // GAMMARAY_PROPERTYSYNCER_H gammaray-2.11.3/common/protocol.cpp000066400000000000000000000036501413310443000172260ustar00rootroot00000000000000/* protocol.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "protocol.h" namespace GammaRay { namespace Protocol { Protocol::ModelIndex fromQModelIndex(const QModelIndex &index) { if (!index.isValid()) return ModelIndex(); ModelIndex result = fromQModelIndex(index.parent()); result.push_back(ModelIndexData(index.row(), index.column())); return result; } QModelIndex toQModelIndex(const QAbstractItemModel *model, const Protocol::ModelIndex &index) { QModelIndex qmi; for (auto it = index.constBegin(), end = index.constEnd(); it != end; ++it) { qmi = model->index(it->row, it->column, qmi); if (!qmi.isValid()) return {}; // model isn't loaded to the full depth, so don't restart from the top } return qmi; } qint32 version() { return 36; } qint32 broadcastFormatVersion() { return 2; } } } gammaray-2.11.3/common/protocol.h000066400000000000000000000117471413310443000167010ustar00rootroot00000000000000/* protocol.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_PROTOCOL_H #define GAMMARAY_PROTOCOL_H #include "gammaray_common_export.h" #include #include #include #include #include #include namespace GammaRay { /*! Helper functions and constants defining the communication protocol between client and server. */ namespace Protocol { /*! Message payload size type. */ using PayloadSize = qint32; /*! Remote object address type. */ using ObjectAddress = quint16; /*! Message type type. */ using MessageType = quint8; /*! Invalid object address. */ static const ObjectAddress InvalidObjectAddress = 0; /*! Address of the launcher remote object for probe <-> launcher communication. */ static const ObjectAddress LauncherAddress = std::numeric_limits::max(); /*! Invalid message type. */ static const MessageType InvalidMessageType = 0; /*! Protocol message types. */ enum BuildInMessageType { // object management // client -> server ObjectMonitored = InvalidMessageType + 1, ObjectUnmonitored, // server -> client ServerVersion, ServerDataVersionNegotiated, ObjectMapReply, ObjectAdded, ObjectRemoved, // remote model messages // client -> server ClientDataVersionNegotiated, ModelRowColumnCountRequest, ModelContentRequest, ModelHeaderRequest, ModelSetDataRequest, ModelSortRequest, ModelSyncBarrier, SelectionModelStateRequest, // server -> client ModelRowColumnCountReply, ModelContentReply, ModelContentChanged, ModelHeaderReply, ModelHeaderChanged, ModelRowsAdded, ModelRowsMoved, ModelRowsRemoved, ModelColumnsAdded, ModelColumnsMoved, ModelColumnsRemoved, ModelReset, ModelLayoutChanged, // server <-> client SelectionModelSelect, SelectionModelCurrent, MethodCall, PropertySyncRequest, PropertyValuesChanged, ServerInfo, // probe settings provided by the launcher ProbeSettings, ServerAddress, ServerLaunchError, MESSAGE_TYPE_COUNT // NOTE when changing this enum, also update MessageStatisticsModel! }; ///@cond internal /*! Transport protocol representation of a model index element. */ class ModelIndexData { public: explicit ModelIndexData(qint32 row_ = 0, qint32 column_ = 0) : row(row_), column(column_) {} qint32 row; qint32 column; }; /*! Transport protocol representation of a QModelIndex. */ using ModelIndex = QVector; /*! Protocol representation of an QItemSelectionRange. */ struct ItemSelectionRange { ModelIndex topLeft; ModelIndex bottomRight; }; /*! Protocol representation of an QItemSelection. */ using ItemSelection = QVector; /*! Serializes a QModelIndex. */ GAMMARAY_COMMON_EXPORT ModelIndex fromQModelIndex(const QModelIndex &index); /*! Deserializes a QModelIndex. */ GAMMARAY_COMMON_EXPORT QModelIndex toQModelIndex(const QAbstractItemModel *model, const ModelIndex &index); ///@endcond /*! Protocol version, must match exactly between client and server. */ GAMMARAY_COMMON_EXPORT qint32 version(); /*! Broadcast format version. */ GAMMARAY_COMMON_EXPORT qint32 broadcastFormatVersion(); } } ///@cond internal QT_BEGIN_NAMESPACE inline QDataStream& operator>>(QDataStream& s, GammaRay::Protocol::ModelIndexData& data) { s >> data.row >> data.column; return s; } inline QDataStream& operator<<(QDataStream& s, const GammaRay::Protocol::ModelIndexData& data) { s << data.row << data.column; return s; } inline QDebug& operator<<(QDebug &s, const GammaRay::Protocol::ModelIndexData &data) { s << '(' << data.row << ',' << data.column << ')'; return s; } ///@endcond Q_DECLARE_TYPEINFO(GammaRay::Protocol::ModelIndexData, Q_MOVABLE_TYPE); Q_DECLARE_TYPEINFO(GammaRay::Protocol::ItemSelectionRange, Q_MOVABLE_TYPE); QT_END_NAMESPACE #endif gammaray-2.11.3/common/proxyfactorybase.cpp000066400000000000000000000042031413310443000207640ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "proxyfactorybase.h" #include #include using namespace GammaRay; ProxyFactoryBase::ProxyFactoryBase(const PluginInfo &pluginInfo, QObject *parent) : QObject(parent) , m_factory(nullptr) , m_pluginInfo(pluginInfo) { } ProxyFactoryBase::~ProxyFactoryBase() = default; PluginInfo ProxyFactoryBase::pluginInfo() const { return m_pluginInfo; } QString ProxyFactoryBase::errorString() const { return m_errorString; } void ProxyFactoryBase::loadPlugin() { if (m_factory) return; if (!pluginInfo().isStatic()) { QPluginLoader loader(pluginInfo().path(), this); m_factory = loader.instance(); if (!m_factory) { m_errorString = loader.errorString(); std::cerr << "error loading plugin " << qPrintable(pluginInfo().path()) << ": " << qPrintable(loader.errorString()) << std::endl; } } else { m_factory = pluginInfo().staticInstance(); } if (m_factory) m_factory->setParent(this); } gammaray-2.11.3/common/proxyfactorybase.h000066400000000000000000000053401413310443000204340ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_PROXYFACTORYBASE_H #define GAMMARAY_PROXYFACTORYBASE_H #include "plugininfo.h" #include #include #include #include namespace GammaRay { /** Base class for wrappers for potentially not yet loaded plugins. */ class ProxyFactoryBase : public QObject { Q_OBJECT public: explicit ProxyFactoryBase(const PluginInfo &pluginInfo, QObject *parent = nullptr); ~ProxyFactoryBase() override; PluginInfo pluginInfo() const; QString errorString() const; protected: void loadPlugin(); QObject *m_factory; QString m_errorString; private: PluginInfo m_pluginInfo; }; template class ProxyFactory : public ProxyFactoryBase, public IFace { public: explicit inline ProxyFactory(const PluginInfo &pluginInfo, QObject *parent = nullptr) : ProxyFactoryBase(pluginInfo, parent) {} inline ~ProxyFactory() override = default; QString id() const override { return pluginInfo().id(); } protected: IFace *factory() { loadPlugin(); IFace *iface = qobject_cast(m_factory); if (!iface) { m_errorString = qApp->translate("GammaRay::ProxyFactory", "Plugin does not provide an instance of %1.") .arg(qobject_interface_iid()); std::cerr << "Failed to cast object from " << qPrintable(pluginInfo().path()) << " to " << qobject_interface_iid() << std::endl; } return iface; } }; } #endif // GAMMARAY_PROXYFACTORYBASE_H gammaray-2.11.3/common/qmetaobjectvalidatorresult.h000066400000000000000000000033041413310443000224710ustar00rootroot00000000000000/* qmetaobjectvalidatorresult.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_QMETAOBJECTVALIDATOR_RESULT_H #define GAMMARAY_QMETAOBJECTVALIDATOR_RESULT_H #include #include namespace GammaRay { /*! Results of QMetaObjectValidator. */ namespace QMetaObjectValidatorResult { enum Result { NoIssue = 0, SignalOverride = 1, UnknownMethodParameterType = 2, PropertyOverride = 4, UnknownPropertyType = 8 }; Q_DECLARE_FLAGS(Results, Result) } } Q_DECLARE_METATYPE(GammaRay::QMetaObjectValidatorResult::Results) Q_DECLARE_OPERATORS_FOR_FLAGS(GammaRay::QMetaObjectValidatorResult::Results) #endif gammaray-2.11.3/common/remotemodelroles.h000066400000000000000000000035501413310443000204120ustar00rootroot00000000000000/* remotemodelroles.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_REMOTEMODELROLES_H #define GAMMARAY_REMOTEMODELROLES_H #include #include namespace GammaRay { /*! Starting point for remoting-related roles. */ static const int RemoteModelUserRole = 0xFFFFFF; /*! Custom roles for RemoteModel. */ namespace RemoteModelRole { enum Roles { LoadingState = RemoteModelUserRole + 1 }; } /*! Cell loading state of RemoteModel. */ namespace RemoteModelNodeState { enum NodeState { NoState = 0, Empty = 1, Loading = 2, Outdated = 4 }; Q_DECLARE_FLAGS(NodeStates, NodeState) } } Q_DECLARE_OPERATORS_FOR_FLAGS(GammaRay::RemoteModelNodeState::NodeStates) Q_DECLARE_METATYPE(GammaRay::RemoteModelNodeState::NodeStates) #endif gammaray-2.11.3/common/remoteviewframe.cpp000066400000000000000000000054201413310443000205630ustar00rootroot00000000000000/* remoteviewframe.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "remoteviewframe.h" #include namespace GammaRay { RemoteViewFrame::~RemoteViewFrame() = default; bool RemoteViewFrame::isValid() const { return !m_image.image().isNull(); } QRectF RemoteViewFrame::viewRect() const { if (m_viewRect.isValid()) return m_viewRect; qreal pxRatio = 1.0; pxRatio = m_image.image().devicePixelRatio(); return QRect(QPoint(), m_image.image().size() / pxRatio); } void RemoteViewFrame::setViewRect(const QRectF &viewRect) { m_viewRect = viewRect; } QRectF RemoteViewFrame::sceneRect() const { if (m_sceneRect.isValid()) return m_sceneRect; return viewRect(); } void RemoteViewFrame::setSceneRect(const QRectF &sceneRect) { m_sceneRect = sceneRect; } QImage RemoteViewFrame::image() const { return m_image.image(); } QTransform RemoteViewFrame::transform() const { return m_image.transform(); } void RemoteViewFrame::setImage(const QImage &image) { m_image.setImage(image); } void RemoteViewFrame::setImage(const QImage &image, const QTransform &transform) { m_image.setImage(image); m_image.setTransform(transform); } QVariant RemoteViewFrame::data() const { return m_data; } void RemoteViewFrame::setData(const QVariant &data) { m_data = data; } QDataStream &operator<<(QDataStream &stream, const RemoteViewFrame &frame) { stream << frame.m_image << frame.m_data << frame.m_viewRect << frame.m_sceneRect; return stream; } QDataStream &operator>>(QDataStream &stream, RemoteViewFrame &frame) { stream >> frame.m_image; stream >> frame.m_data; stream >> frame.m_viewRect; stream >> frame.m_sceneRect; return stream; } } gammaray-2.11.3/common/remoteviewframe.h000066400000000000000000000051531413310443000202330ustar00rootroot00000000000000/* remoteviewframe.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_REMOTEVIEWFRAME_H #define GAMMARAY_REMOTEVIEWFRAME_H #include "gammaray_common_export.h" #include "transferimage.h" #include #include #include #include namespace GammaRay { class RemoteViewFrame; QDataStream &operator<<(QDataStream &stream, const GammaRay::RemoteViewFrame &frame); QDataStream &operator>>(QDataStream &stream, GammaRay::RemoteViewFrame &frame); /** Data of a single frame displayed in the RemoteViewWidget. */ class GAMMARAY_COMMON_EXPORT RemoteViewFrame { public: RemoteViewFrame() = default; ~RemoteViewFrame(); bool isValid() const; /// the visible area on screen QRectF viewRect() const; void setViewRect(const QRectF &viewRect); /// the internal scene might expand beyond the visible view area QRectF sceneRect() const; void setSceneRect(const QRectF &sceneRect); QImage image() const; QTransform transform() const; void setImage(const QImage &image); void setImage(const QImage &image, const QTransform &transform); /// tool specific frame data QVariant data() const; void setData(const QVariant &data); private: friend QDataStream &operator<<(QDataStream &stream, const RemoteViewFrame &frame); friend QDataStream &operator>>(QDataStream &stream, RemoteViewFrame &frame); TransferImage m_image; QVariant m_data; QRectF m_viewRect; QRectF m_sceneRect; }; } Q_DECLARE_METATYPE(GammaRay::RemoteViewFrame) #endif // GAMMARAY_REMOTEVIEWFRAME_H gammaray-2.11.3/common/remoteviewinterface.cpp000066400000000000000000000116341413310443000214350ustar00rootroot00000000000000/* remoteviewinterface.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "remoteviewinterface.h" #include "streamoperators.h" #include #include using namespace GammaRay; QT_BEGIN_NAMESPACE GAMMARAY_ENUM_STREAM_OPERATORS(RemoteViewInterface::RequestMode) QDataStream &operator<<(QDataStream &s, Qt::TouchPointStates states) { return s << (int)states; } QDataStream &operator>>(QDataStream &s, Qt::TouchPointStates &states) { int st; s >> st; states = Qt::TouchPointStates(st); return s; } QDataStream &operator<<(QDataStream &s, QTouchEvent::TouchPoint::InfoFlags flags) { return s << (int)flags; } QDataStream &operator>>(QDataStream &s, QTouchEvent::TouchPoint::InfoFlags &flags) { int f; s >> f; flags = QTouchEvent::TouchPoint::InfoFlags(f); return s; } QDataStream &operator<<(QDataStream &s, const QList &points) { s << points.count(); for (const auto &p : points) { s << p.id(); s << p.state(); s << p.rect() << p.sceneRect() << p.screenRect(); s << p.normalizedPos(); s << p.startPos() << p.startScenePos() << p.startScreenPos() << p.startNormalizedPos(); s << p.lastPos() << p.lastScenePos() << p.lastScreenPos() << p.lastNormalizedPos(); s << p.pressure(); s << p.velocity(); s << p.flags(); s << p.rawScreenPositions(); } return s; } template void setPointValue(QDataStream &s, QTouchEvent::TouchPoint &p, void (QTouchEvent::TouchPoint::*func)(T)) { typename std::decay::type value; s >> value; (p.*func)(value); } QDataStream &operator>>(QDataStream &s, QList &points) { int count; s >> count; points.reserve(count); for (int i = 0; i < count; ++i) { QTouchEvent::TouchPoint p; setPointValue(s, p, &QTouchEvent::TouchPoint::setId); setPointValue(s, p, &QTouchEvent::TouchPoint::setState); setPointValue(s, p, &QTouchEvent::TouchPoint::setRect); setPointValue(s, p, &QTouchEvent::TouchPoint::setSceneRect); setPointValue(s, p, &QTouchEvent::TouchPoint::setScreenRect); setPointValue(s, p, &QTouchEvent::TouchPoint::setNormalizedPos); setPointValue(s, p, &QTouchEvent::TouchPoint::setStartPos); setPointValue(s, p, &QTouchEvent::TouchPoint::setStartScenePos); setPointValue(s, p, &QTouchEvent::TouchPoint::setStartScreenPos); setPointValue(s, p, &QTouchEvent::TouchPoint::setStartNormalizedPos); setPointValue(s, p, &QTouchEvent::TouchPoint::setLastPos); setPointValue(s, p, &QTouchEvent::TouchPoint::setLastScenePos); setPointValue(s, p, &QTouchEvent::TouchPoint::setLastScreenPos); setPointValue(s, p, &QTouchEvent::TouchPoint::setLastNormalizedPos); setPointValue(s, p, &QTouchEvent::TouchPoint::setPressure); setPointValue(s, p, &QTouchEvent::TouchPoint::setVelocity); setPointValue(s, p, &QTouchEvent::TouchPoint::setFlags); setPointValue(s, p, &QTouchEvent::TouchPoint::setRawScreenPositions); points.append(p); } return s; } QT_END_NAMESPACE RemoteViewInterface::RemoteViewInterface(const QString &name, QObject *parent) : QObject(parent) , m_name(name) { ObjectBroker::registerObject(name, this); qRegisterMetaType(); qRegisterMetaType>(); qRegisterMetaType(); qRegisterMetaTypeStreamOperators(); qRegisterMetaTypeStreamOperators(); qRegisterMetaTypeStreamOperators(); qRegisterMetaTypeStreamOperators>(); qRegisterMetaTypeStreamOperators(); } QString RemoteViewInterface::name() const { return m_name; } gammaray-2.11.3/common/remoteviewinterface.h000066400000000000000000000070171413310443000211020ustar00rootroot00000000000000/* remoteviewinterface.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_REMOTEVIEWINTERFACE_H #define GAMMARAY_REMOTEVIEWINTERFACE_H #include "gammaray_common_export.h" #include "objectid.h" #include #include #include namespace GammaRay { class RemoteViewFrame; /** Communication interface for the remote view widget. */ class GAMMARAY_COMMON_EXPORT RemoteViewInterface : public QObject { Q_OBJECT public: enum RequestMode { RequestBest, RequestAll }; explicit RemoteViewInterface(const QString &name, QObject *parent = nullptr); QString name() const; public slots: virtual void requestElementsAt(const QPoint &pos, GammaRay::RemoteViewInterface::RequestMode mode) = 0; virtual void pickElementId(const GammaRay::ObjectId &id) = 0; virtual void sendKeyEvent(int type, int key, int modifiers, const QString &text = QString(), bool autorep = false, ushort count = 1) = 0; virtual void sendMouseEvent(int type, const QPoint &localPos, int button, int buttons, int modifiers) = 0; virtual void sendWheelEvent(const QPoint &localPos, QPoint pixelDelta, QPoint angleDelta, int buttons, int modifiers) = 0; virtual void sendTouchEvent(int type, int touchDeviceType, int deviceCaps, int touchDeviceMaxTouchPoints, int modifiers, Qt::TouchPointStates touchPointStates, const QList &touchPoints) = 0; virtual void sendUserViewport(const QRectF &userViewport) = 0; virtual void setViewActive(bool active) = 0; /// Tell the server we are ready for the next frame. virtual void clientViewUpdated() = 0; virtual void requestCompleteFrame() = 0; signals: void reset(); void elementsAtReceived(const GammaRay::ObjectIds &ids, int bestCandidate); void frameUpdated(const GammaRay::RemoteViewFrame &frame); private: QString m_name; }; } Q_DECLARE_METATYPE(QTouchEvent::TouchPoint) Q_DECLARE_METATYPE(Qt::TouchPointStates) Q_DECLARE_METATYPE(QTouchEvent::TouchPoint::InfoFlags) Q_DECLARE_METATYPE(QList) Q_DECLARE_METATYPE(GammaRay::RemoteViewInterface::RequestMode) QT_BEGIN_NAMESPACE Q_DECLARE_INTERFACE(GammaRay::RemoteViewInterface, "com.kdab.GammaRay.RemoteViewInterface/1.0") QT_END_NAMESPACE #endif // GAMMARAY_REMOTEVIEWINTERFACE_H gammaray-2.11.3/common/selflocator.cpp000066400000000000000000000041651413310443000177040ustar00rootroot00000000000000/* selflocator.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "selflocator.h" #include #include #include #include using namespace GammaRay; #ifndef Q_OS_WIN #include static QString findMeInternal() { Dl_info info; if (dladdr(reinterpret_cast(&SelfLocator::findMe), &info) == 0) return QString(); if (!info.dli_fname) return QString(); return QString::fromLocal8Bit(info.dli_fname); } #else #include static QString findMeInternal() { WCHAR path[MAX_PATH]; HMODULE handle; if (GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, reinterpret_cast(&SelfLocator::findMe), &handle)) { GetModuleFileNameW(handle, path, sizeof(path)); return QDir::fromNativeSeparators(QString::fromUtf16(reinterpret_cast(path))); } return QString(); } #endif QString SelfLocator::findMe() { const QFileInfo fi(findMeInternal()); return fi.canonicalFilePath(); } gammaray-2.11.3/common/selflocator.h000066400000000000000000000026331413310443000173470ustar00rootroot00000000000000/* selflocator.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_SELFLOCATOR_H #define GAMMARAY_SELFLOCATOR_H #include QT_BEGIN_NAMESPACE class QString; QT_END_NAMESPACE namespace GammaRay { /*! Determines the absolute file path of this DLL. */ namespace SelfLocator { QString findMe(); } } #endif // GAMMARAY_SELFLOCATOR_H gammaray-2.11.3/common/settempvalue.h000066400000000000000000000032601413310443000175450ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_SETTEMPVALUE_H #define GAMMARAY_SETTEMPVALUE_H #include namespace GammaRay { /** * @brief GammaRay utilities. */ namespace Util { /** * Temporarily overwrite a given object with a new value and reset the value * when the scope is exited. */ template struct SetTempValue { SetTempValue(T &obj, T newValue) : obj(obj) , oldValue(obj) { obj = newValue; } ~SetTempValue() { obj = oldValue; } Q_DISABLE_COPY(SetTempValue) T& obj; T oldValue; }; } } #endif // GAMMARAY_SETTEMPVALUE_H gammaray-2.11.3/common/sharedpool.h000066400000000000000000000056421413310443000171750ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Kevin Funk Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_SHAREDPOOL_H #define GAMMARAY_SHAREDPOOL_H #include #include #include #include #include #include #define IF_DEBUG(x) namespace GammaRay { template class SharedPool { public: // no `using a = b;` for MSVC2010 :( typedef std::unique_ptr> PtrType; SharedPool(size_t prealloc = 0) : m_capacity(0) { while (prealloc--) { add(std::unique_ptr(new T)); } } ~SharedPool() { assert(m_capacity == size() && "Some objects are still acquired"); IF_DEBUG(std::cout << "Acquired objects left in pool on destruction: " << (m_capacity - size()) << std::endl); } void add(std::unique_ptr t) { m_pool.push(std::move(t)); m_capacity++; IF_DEBUG(std::cout << "Adding object to pool: " << m_pool.top().get() << " - current capacity:" << m_capacity << std::endl); } PtrType acquire() { // insert more if necessary if (m_pool.empty()) { IF_DEBUG(std::cout << "Growing pool by one" << std::endl); add(std::unique_ptr(new T)); } auto ptr = m_pool.top().release(); IF_DEBUG(std::cout << "Acquire: " << ptr << std::endl); PtrType tmp(ptr, [this](T *ptr) { IF_DEBUG(std::cout << "Release: " << ptr << std::endl); m_pool.push(std::unique_ptr(ptr)); }); m_pool.pop(); return tmp; } bool empty() const { return m_pool.empty(); } size_t capacity() const { return m_capacity; } size_t size() const { return m_pool.size(); } private: size_t m_capacity; std::stack, std::vector>> m_pool; }; } #endif gammaray-2.11.3/common/sourcelocation.cpp000066400000000000000000000065031413310443000204160ustar00rootroot00000000000000/* sourcelocation.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "sourcelocation.h" using namespace GammaRay; SourceLocation::SourceLocation(const QUrl &url) : m_url(url) , m_line(0) , m_column(0) { } SourceLocation::SourceLocation(const QUrl &url, int line, int column) : m_url(url) , m_line(line) , m_column(column) { } SourceLocation::~SourceLocation() = default; GammaRay::SourceLocation GammaRay::SourceLocation::fromZeroBased(const QUrl& url, int line, int column) { return SourceLocation(url, line, column); } GammaRay::SourceLocation GammaRay::SourceLocation::fromOneBased(const QUrl& url, int line, int column) { return SourceLocation(url, line - 1, column - 1); } bool SourceLocation::operator==(const SourceLocation &other) const { return m_url == other.m_url && m_line == other.m_line && m_column == other.m_column; } bool SourceLocation::isValid() const { return m_url.isValid(); } QUrl SourceLocation::url() const { return m_url; } void SourceLocation::setUrl(const QUrl &url) { m_url = url; } int SourceLocation::line() const { return m_line; } void SourceLocation::setZeroBasedLine(int line) { m_line = line; } void SourceLocation::setOneBasedLine(int line) { m_line = line - 1; } int SourceLocation::column() const { return m_column; } void SourceLocation::setZeroBasedColumn(int column) { m_column = column; } void SourceLocation::setOneBasedColumn(int column) { m_column = column - 1; } QString SourceLocation::displayString() const { if (m_url.isEmpty()) return QString(); QString result; if (m_url.isLocalFile()) result = m_url.toLocalFile(); else result = m_url.toString(); if (m_line < 0) return result; result += QString::fromLatin1(":%1").arg(m_line + 1); if (m_column >= 0) result += QString::fromLatin1(":%1").arg(m_column + 1); return result; } QDataStream &GammaRay::operator<<(QDataStream &out, const SourceLocation &location) { out << location.m_url; out << location.m_line; out << location.m_column; return out; } QDataStream &GammaRay::operator>>(QDataStream &in, SourceLocation &location) { in >> location.m_url; in >> location.m_line; in >> location.m_column; return in; } gammaray-2.11.3/common/sourcelocation.h000066400000000000000000000076651413310443000200750ustar00rootroot00000000000000/* sourcelocation.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_SOURCELOCATION_H #define GAMMARAY_SOURCELOCATION_H #include "gammaray_common_export.h" #include #include #include #include namespace GammaRay { /*! * Specifies a source code location. * * A source location consists of a document and cursor position * * A Cursor represents a position in a Document through a tuple * of two ints, namely the @ref line() and @ref column(). * * @note Lines and columns start a 0 (=> zero-based numbering). */ class GAMMARAY_COMMON_EXPORT SourceLocation { public: /*! * The default constructor creates a (invalid) cursor at position (-1, -1) with an invalid url */ SourceLocation() = default; /*! * This constructor creates a (valid) cursor at position (0, 0) with @p url */ explicit SourceLocation(const QUrl &url); ~SourceLocation(); static SourceLocation fromZeroBased(const QUrl &url, int line, int column = 0); static SourceLocation fromOneBased(const QUrl &url, int line, int column = 1); bool operator==(const SourceLocation &other) const; bool isValid() const; QUrl url() const; void setUrl(const QUrl &url); /*! * Returns the zero-based line number. */ int line() const; void setZeroBasedLine(int line); void setOneBasedLine(int line); /*! * Returns the zero-based column number. */ int column() const; void setZeroBasedColumn(int column); void setOneBasedColumn(int column); /*! * Returns a human-readable version of this source location * * @code * SourceLocation loc(QUrl::fromLocalFile("file.cpp", 0, 0); * qDebug() << loc.displayString(); * * => Prints: file.cpp:1:1 * @endcode * * @note This will use one-based numbering (file.cpp:1:1 instead of file.cpp:0:0) */ QString displayString() const; private: /*! * This constructor creates a (valid) cursor at given @p line and @p column with @p url. * Expects line and column to be zero-based. */ explicit SourceLocation(const QUrl &url, int line, int column); friend GAMMARAY_COMMON_EXPORT QDataStream &operator<<(QDataStream &out, const SourceLocation &location); friend GAMMARAY_COMMON_EXPORT QDataStream &operator>>(QDataStream &in, SourceLocation &location); QUrl m_url; int m_line = -1; int m_column = -1; }; ///@cond internal GAMMARAY_COMMON_EXPORT QDataStream &operator<<(QDataStream &out, const SourceLocation &location); GAMMARAY_COMMON_EXPORT QDataStream &operator>>(QDataStream &in, SourceLocation &location); ///@endcond } Q_DECLARE_METATYPE(GammaRay::SourceLocation) Q_DECLARE_METATYPE(QVector) #endif // GAMMARAY_SOURCELOCATION_H gammaray-2.11.3/common/streamoperators.cpp000066400000000000000000000060371413310443000206210ustar00rootroot00000000000000/* streamoperators.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "streamoperators.h" #include "metatypedeclarations.h" #include "variantwrapper.h" #include "sourcelocation.h" #include "qmetaobjectvalidatorresult.h" #include "objectid.h" #include "enumdefinition.h" #include "enumvalue.h" #include "propertymodel.h" #include using namespace GammaRay; QT_BEGIN_NAMESPACE GAMMARAY_ENUM_STREAM_OPERATORS(QMetaMethod::Access) GAMMARAY_ENUM_STREAM_OPERATORS(QMetaMethod::MethodType) GAMMARAY_ENUM_STREAM_OPERATORS(Qt::ConnectionType) GAMMARAY_ENUM_STREAM_OPERATORS(Qt::CaseSensitivity) GAMMARAY_ENUM_STREAM_OPERATORS(GammaRay::QMetaObjectValidatorResult::Results) GAMMARAY_ENUM_STREAM_OPERATORS(GammaRay::PropertyModel::PropertyFlags) QT_END_NAMESPACE void StreamOperators::registerOperators() { qRegisterMetaTypeStreamOperators(); qRegisterMetaTypeStreamOperators(); qRegisterMetaTypeStreamOperators(); qRegisterMetaTypeStreamOperators(); qRegisterMetaTypeStreamOperators >(); qRegisterMetaType(); qRegisterMetaTypeStreamOperators(); // This is needed so QVariant based comparison works (ie: QAIM::match) QMetaType::registerComparators(); qRegisterMetaType(); qRegisterMetaTypeStreamOperators(); // This is needed so QVariant based comparison works (ie: QAIM::match) QMetaType::registerComparators(); qRegisterMetaTypeStreamOperators(); qRegisterMetaTypeStreamOperators(); qRegisterMetaTypeStreamOperators>(); qRegisterMetaTypeStreamOperators(); qRegisterMetaTypeStreamOperators(); qRegisterMetaTypeStreamOperators(); qRegisterMetaTypeStreamOperators(); } gammaray-2.11.3/common/streamoperators.h000066400000000000000000000040271413310443000202630ustar00rootroot00000000000000/* streamoperators.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_STREAMOPERATORS_H #define GAMMARAY_STREAMOPERATORS_H #include namespace GammaRay { /** Custom QDataStream streaming operators. */ namespace StreamOperators { /** Call once early during startup. */ void registerOperators(); namespace Internal { template QDataStream &writeEnum(QDataStream &out, T value) { out << static_cast(value); return out; } template QDataStream &readEnum(QDataStream &in, T &value) { qint32 v; in >> v; value = static_cast(v); return in; } } } #define GAMMARAY_ENUM_STREAM_OPERATORS(enumType) \ QDataStream &operator<<(QDataStream &out, enumType value) \ { return GammaRay::StreamOperators::Internal::writeEnum(out, value); } \ QDataStream &operator>>(QDataStream &in, enumType &value) \ { return GammaRay::StreamOperators::Internal::readEnum(in, value); } } #endif // GAMMARAY_STREAMOPERATORS_H gammaray-2.11.3/common/toolmanagerinterface.cpp000066400000000000000000000031631413310443000215550ustar00rootroot00000000000000/* toolmanagerinterface.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "toolmanagerinterface.h" using namespace GammaRay; ToolManagerInterface::ToolManagerInterface(QObject *parent) : QObject(parent) { qRegisterMetaType(); qRegisterMetaTypeStreamOperators(); qRegisterMetaType >(); qRegisterMetaTypeStreamOperators >(); qRegisterMetaType >(); qRegisterMetaTypeStreamOperators >(); } ToolManagerInterface::~ToolManagerInterface() = default; gammaray-2.11.3/common/toolmanagerinterface.h000066400000000000000000000054361413310443000212270ustar00rootroot00000000000000/* toolmanagerinterface.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_TOOLMANAGERINTERFACE_H #define GAMMARAY_TOOLMANAGERINTERFACE_H #include "objectid.h" #include #include #include #include #include namespace GammaRay { /** @brief GammaRay tool identifier. */ struct ToolData { QString id; bool hasUi; bool enabled; }; /** @brief Probe and host process remote control functions. */ class ToolManagerInterface : public QObject { Q_OBJECT public: explicit ToolManagerInterface(QObject *parent = nullptr); ~ToolManagerInterface() override; virtual void selectObject(const ObjectId &id, const QString &toolId) = 0; virtual void requestToolsForObject(const ObjectId &id) = 0; virtual void requestAvailableTools() = 0; Q_SIGNALS: void toolsForObjectResponse(const GammaRay::ObjectId &id, const QVector &toolInfos); void availableToolsResponse(const QVector &toolInfos); void toolEnabled(const QString &toolId); void toolSelected(const QString &toolId); private: Q_DISABLE_COPY(ToolManagerInterface) }; inline QDataStream &operator<<(QDataStream &out, const ToolData &toolInfo) { out << toolInfo.id; out << toolInfo.hasUi; out << toolInfo.enabled; return out; } inline QDataStream &operator>>(QDataStream &in, ToolData &toolInfo) { in >> toolInfo.id; in >> toolInfo.hasUi; in >> toolInfo.enabled; return in; } } QT_BEGIN_NAMESPACE Q_DECLARE_INTERFACE(GammaRay::ToolManagerInterface, "com.kdab.GammaRay.ToolManagerInterface") QT_END_NAMESPACE Q_DECLARE_METATYPE(GammaRay::ToolData) Q_DECLARE_METATYPE(QVector) #endif // GAMMARAY_TOOLMANAGERINTERFACE_H gammaray-2.11.3/common/tools/000077500000000000000000000000001413310443000160155ustar00rootroot00000000000000gammaray-2.11.3/common/tools/messagehandler/000077500000000000000000000000001413310443000207775ustar00rootroot00000000000000gammaray-2.11.3/common/tools/messagehandler/messagehandlerinterface.cpp000066400000000000000000000034371413310443000263550ustar00rootroot00000000000000/* messagehandlerinterface.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "messagehandlerinterface.h" #include "common/objectbroker.h" using namespace GammaRay; MessageHandlerInterface::MessageHandlerInterface(QObject *parent) : QObject(parent) , m_stackTraceAvailable(false) { ObjectBroker::registerObject(this); } MessageHandlerInterface::~MessageHandlerInterface() = default; bool MessageHandlerInterface::stackTraceAvailable() const { return m_stackTraceAvailable; } void MessageHandlerInterface::setStackTraceAvailable(bool available) { if (m_stackTraceAvailable == available) return; m_stackTraceAvailable = available; emit stackTraceAvailableChanged(available); } gammaray-2.11.3/common/tools/messagehandler/messagehandlerinterface.h000066400000000000000000000042121413310443000260120ustar00rootroot00000000000000/* messagehandlerinterface.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_MESSAGEHANDLER_MESSAGEHANDLERINTERFACE_H #define GAMMARAY_MESSAGEHANDLER_MESSAGEHANDLERINTERFACE_H #include QT_BEGIN_NAMESPACE class QTime; QT_END_NAMESPACE namespace GammaRay { class MessageHandlerInterface : public QObject { Q_OBJECT Q_PROPERTY(bool stackTraceAvailable READ stackTraceAvailable WRITE setStackTraceAvailable NOTIFY stackTraceAvailableChanged) public: explicit MessageHandlerInterface(QObject *parent = nullptr); ~MessageHandlerInterface() override; bool stackTraceAvailable() const; void setStackTraceAvailable(bool available); signals: void fatalMessageReceived(const QString &app, const QString &message, const QTime &time, const QStringList &backtrace); void stackTraceAvailableChanged(bool available); private: bool m_stackTraceAvailable; }; } QT_BEGIN_NAMESPACE Q_DECLARE_INTERFACE(GammaRay::MessageHandlerInterface, "com.kdab.GammaRay.MessageHandler") QT_END_NAMESPACE #endif // GAMMARAY_MESSAGEHANDLERINTERFACE_H gammaray-2.11.3/common/tools/messagehandler/messagemodelroles.h000066400000000000000000000031241413310443000246620ustar00rootroot00000000000000/* messagemodelroles.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_MESSAGEMODELROLES #define GAMMARAY_MESSAGEMODELROLES #include namespace GammaRay { /** Additional roles for MessageModel. */ namespace MessageModelRole { enum Role { Sort = UserRole + 1, // not for remoting Type, File, Line, Backtrace }; } /** Column indexes of MessageModel. */ namespace MessageModelColumn { enum Columns { Time, Message, Category, Function, File, COUNT }; } } #endif gammaray-2.11.3/common/tools/metaobjectbrowser/000077500000000000000000000000001413310443000215365ustar00rootroot00000000000000gammaray-2.11.3/common/tools/metaobjectbrowser/qmetaobjectmodel.h000066400000000000000000000033061413310443000252300ustar00rootroot00000000000000/* qmetaobjectmodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_METAOBJECTBROWSER_QMETAOBJECTMODEL_H #define GAMMARAY_METAOBJECTBROWSER_QMETAOBJECTMODEL_H #include namespace GammaRay { /*! Column and role definitions for the QMetaObject tree model. */ namespace QMetaObjectModel { enum Role { MetaObjectRole = UserRole + 1, MetaObjectIssues, MetaObjectInvalid }; enum Column { ObjectColumn, ObjectSelfCountColumn, ObjectInclusiveCountColumn, ObjectSelfAliveCountColumn, ObjectInclusiveAliveCountColumn, _Last }; } } #endif gammaray-2.11.3/common/tools/metatypebrowser/000077500000000000000000000000001413310443000212515ustar00rootroot00000000000000gammaray-2.11.3/common/tools/metatypebrowser/metatypebrowserinterface.cpp000066400000000000000000000027071413310443000271000ustar00rootroot00000000000000/* metatypebrowserinterface.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "metatypebrowserinterface.h" #include using namespace GammaRay; MetaTypeBrowserInterface::MetaTypeBrowserInterface(QObject* parent) : QObject(parent) { ObjectBroker::registerObject(this); } MetaTypeBrowserInterface::~MetaTypeBrowserInterface() = default; gammaray-2.11.3/common/tools/metatypebrowser/metatypebrowserinterface.h000066400000000000000000000033451413310443000265440ustar00rootroot00000000000000/* metatypebrowserinterface.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_METATYPEBROWSERINTERFACE_H #define GAMMARAY_METATYPEBROWSERINTERFACE_H #include namespace GammaRay { /*! communication interface for the meta type browser tool. */ class MetaTypeBrowserInterface : public QObject { Q_OBJECT public: explicit MetaTypeBrowserInterface(QObject *parent = nullptr); ~MetaTypeBrowserInterface() override; public slots: virtual void rescanTypes() = 0; }; } QT_BEGIN_NAMESPACE Q_DECLARE_INTERFACE(GammaRay::MetaTypeBrowserInterface, "com.kdab.GammaRay.MetaTypeBrowserInterface") QT_END_NAMESPACE #endif // GAMMARAY_METATYPEBROWSERINTERFACE_H gammaray-2.11.3/common/tools/metatypebrowser/metatyperoles.h000066400000000000000000000025071413310443000243230ustar00rootroot00000000000000/* metatyperoles.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_METATYPEROLES_H #define GAMMARAY_METATYPEROLES_H #include namespace GammaRay { namespace MetaTypeRoles { enum Roles { MetaObjectIdRole = UserRole + 1 }; } } #endif gammaray-2.11.3/common/tools/objectinspector/000077500000000000000000000000001413310443000212125ustar00rootroot00000000000000gammaray-2.11.3/common/tools/objectinspector/connectionsextensioninterface.cpp000066400000000000000000000031041413310443000300540ustar00rootroot00000000000000/* connectionsextensioninterface.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "connectionsextensioninterface.h" #include using namespace GammaRay; ConnectionsExtensionInterface::ConnectionsExtensionInterface(const QString &name, QObject *parent) : QObject(parent) , m_name(name) { ObjectBroker::registerObject(name, this); } ConnectionsExtensionInterface::~ConnectionsExtensionInterface() = default; QString ConnectionsExtensionInterface::name() const { return m_name; } gammaray-2.11.3/common/tools/objectinspector/connectionsextensioninterface.h000066400000000000000000000037111413310443000275250ustar00rootroot00000000000000/* connectionsextensioninterface.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_CONNECTIONSEXTENSIONINTERFACE_H #define GAMMARAY_CONNECTIONSEXTENSIONINTERFACE_H #include namespace GammaRay { /** Communication interface for the connections tab in the property view. */ class ConnectionsExtensionInterface : public QObject { Q_OBJECT public: explicit ConnectionsExtensionInterface(const QString &name, QObject *parent = nullptr); ~ConnectionsExtensionInterface() override; QString name() const; public slots: virtual void navigateToSender(int modelRow) = 0; virtual void navigateToReceiver(int modelRow) = 0; private: QString m_name; }; } QT_BEGIN_NAMESPACE Q_DECLARE_INTERFACE(GammaRay::ConnectionsExtensionInterface, "com.kdab.GammaRay.ConnectionsExtensionInterface") QT_END_NAMESPACE #endif // GAMMARAY_CONNECTIONSEXTENSIONINTERFACE_H gammaray-2.11.3/common/tools/objectinspector/connectionsmodelroles.h000066400000000000000000000031721413310443000257760ustar00rootroot00000000000000/* modelroles.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_CONNECTIONSMODELROLES_H #define GAMMARAY_CONNECTIONSMODELROLES_H #include namespace GammaRay { /** @brief Roles for the object inspector connections models. */ namespace ConnectionsModelRoles { enum Role { WarningFlagRole = UserRole + 1, EndpointRole, ActionRole }; } /** @brief Connection actions. * Returns via ActionRole from the connections models. */ namespace ConnectionsModelActions { enum Action { NoAction = 0, NavigateToEndpoint = 1 }; } } #endif gammaray-2.11.3/common/tools/objectinspector/methodmodel.h000066400000000000000000000027731413310443000236750ustar00rootroot00000000000000/* methodmodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_METHODMODEL_H #define GAMMARAY_METHODMODEL_H #include namespace GammaRay { /*! Custom roles for GammaRay::ObjectMethodModel. */ namespace ObjectMethodModelRole { enum Role { MetaMethod = UserRole + 1, MetaMethodType, MethodSignature, MethodTag, MethodRevision, MethodAccess, MethodSortRole, MethodIssues }; } } #endif gammaray-2.11.3/common/tools/objectinspector/methodsextensioninterface.cpp000066400000000000000000000035011413310443000271760ustar00rootroot00000000000000/* methodsextensioninterface.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "methodsextensioninterface.h" #include "objectbroker.h" using namespace GammaRay; MethodsExtensionInterface::MethodsExtensionInterface(const QString &name, QObject *parent) : QObject(parent) , m_name(name) , m_hasObject(false) { ObjectBroker::registerObject(name, this); } MethodsExtensionInterface::~MethodsExtensionInterface() = default; const QString &MethodsExtensionInterface::name() const { return m_name; } bool MethodsExtensionInterface::hasObject() const { return m_hasObject; } void MethodsExtensionInterface::setHasObject(bool hasObject) { if (m_hasObject == hasObject) return; m_hasObject = hasObject; emit hasObjectChanged(); } gammaray-2.11.3/common/tools/objectinspector/methodsextensioninterface.h000066400000000000000000000042311413310443000266440ustar00rootroot00000000000000/* methodsextensioninterface.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_METHODSEXTENSIONINTERFACE_H #define GAMMARAY_METHODSEXTENSIONINTERFACE_H #include namespace GammaRay { /** @brief Client/Server interface of the methods viewer. */ class MethodsExtensionInterface : public QObject { Q_OBJECT Q_PROPERTY(bool hasObject READ hasObject WRITE setHasObject NOTIFY hasObjectChanged) public: explicit MethodsExtensionInterface(const QString &name, QObject *parent = nullptr); ~MethodsExtensionInterface() override; const QString &name() const; bool hasObject() const; void setHasObject(bool hasObject); signals: void hasObjectChanged(); public slots: virtual void activateMethod() = 0; virtual void invokeMethod(Qt::ConnectionType type) = 0; virtual void connectToSignal() = 0; private: QString m_name; bool m_hasObject; }; } QT_BEGIN_NAMESPACE Q_DECLARE_INTERFACE(GammaRay::MethodsExtensionInterface, "com.kdab.GammaRay.MethodsExtensionInterface") QT_END_NAMESPACE #endif // GAMMARAY_METHODSEXTENSIONINTERFACE_H gammaray-2.11.3/common/tools/objectinspector/propertiesextensioninterface.cpp000066400000000000000000000043101413310443000277260ustar00rootroot00000000000000/* propertycontrollerinterface.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "propertiesextensioninterface.h" #include "objectbroker.h" using namespace GammaRay; PropertiesExtensionInterface::PropertiesExtensionInterface(const QString &name, QObject *parent) : QObject(parent) , m_name(name) , m_canAddProperty(false) , m_hasPropertyValues(true) { ObjectBroker::registerObject(name, this); } PropertiesExtensionInterface::~PropertiesExtensionInterface() = default; const QString &PropertiesExtensionInterface::name() const { return m_name; } bool PropertiesExtensionInterface::canAddProperty() const { return m_canAddProperty; } void PropertiesExtensionInterface::setCanAddProperty(bool canAdd) { if (m_canAddProperty == canAdd) return; m_canAddProperty = canAdd; emit canAddPropertyChanged(); } bool PropertiesExtensionInterface::hasPropertyValues() const { return m_hasPropertyValues; } void PropertiesExtensionInterface::setHasPropertyValues(bool hasValues) { if (m_hasPropertyValues == hasValues) return; m_hasPropertyValues = hasValues; emit hasPropertyValuesChanged(); } gammaray-2.11.3/common/tools/objectinspector/propertiesextensioninterface.h000066400000000000000000000046761413310443000274120ustar00rootroot00000000000000/* propertycontrollerinterface.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_PROPERTIESEXTENSIONINTERFACE_H #define GAMMARAY_PROPERTIESEXTENSIONINTERFACE_H #include namespace GammaRay { /** @brief Client/Server interface of the property editor. */ class PropertiesExtensionInterface : public QObject { Q_OBJECT Q_PROPERTY( bool canAddProperty READ canAddProperty WRITE setCanAddProperty NOTIFY canAddPropertyChanged) Q_PROPERTY( bool hasPropertyValues READ hasPropertyValues WRITE setHasPropertyValues NOTIFY hasPropertyValuesChanged) public: explicit PropertiesExtensionInterface(const QString &name, QObject *parent = nullptr); ~PropertiesExtensionInterface() override; const QString &name() const; bool canAddProperty() const; void setCanAddProperty(bool canAdd); bool hasPropertyValues() const; void setHasPropertyValues(bool hasValues); public slots: virtual void setProperty(const QString &name, const QVariant &value) = 0; signals: void canAddPropertyChanged(); void hasPropertyValuesChanged(); private: QString m_name; bool m_canAddProperty; bool m_hasPropertyValues; }; } QT_BEGIN_NAMESPACE Q_DECLARE_INTERFACE(GammaRay::PropertiesExtensionInterface, "com.kdab.GammaRay.PropertiesExtensionInterface") QT_END_NAMESPACE #endif // GAMMARAY_PROPERTIESEXTENSIONINTERFACE_H gammaray-2.11.3/common/tools/problemreporter/000077500000000000000000000000001413310443000212405ustar00rootroot00000000000000gammaray-2.11.3/common/tools/problemreporter/problemmodelroles.h000066400000000000000000000026201413310443000251370ustar00rootroot00000000000000/* problemmodelroles.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2018-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_PROBLEMMODELROLES_H #define GAMMARAY_PROBLEMMODELROLES_H #include namespace GammaRay { namespace ProblemModelRoles { enum Roles { SeverityRole = ObjectModel::UserRole + 1, SourceLocationRole, ProblemIdRole }; } } #endif gammaray-2.11.3/common/tools/problemreporter/problemreporterinterface.cpp000066400000000000000000000027131413310443000270530ustar00rootroot00000000000000/* problemreporterinterface.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2018-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "problemreporterinterface.h" #include using namespace GammaRay; ProblemReporterInterface::ProblemReporterInterface(QObject* parent) : QObject(parent) { ObjectBroker::registerObject(this); } ProblemReporterInterface::~ProblemReporterInterface() = default; gammaray-2.11.3/common/tools/problemreporter/problemreporterinterface.h000066400000000000000000000034231413310443000265170ustar00rootroot00000000000000/* problemreporterinterface.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2018-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_PROBLEMREPORTERINTERFACE_H #define GAMMARAY_PROBLEMREPORTERINTERFACE_H #include namespace GammaRay { /*! communication interface for the problem reporter tool. */ class ProblemReporterInterface : public QObject { Q_OBJECT public: explicit ProblemReporterInterface(QObject *parent = nullptr); ~ProblemReporterInterface() override; signals: void problemScansFinished(); public slots: virtual void requestScan() = 0; }; } QT_BEGIN_NAMESPACE Q_DECLARE_INTERFACE(GammaRay::ProblemReporterInterface, "com.kdab.GammaRay.ProblemReporterInterface") QT_END_NAMESPACE #endif // GAMMARAY_PROBLEMREPORTERINTERFACE_H gammaray-2.11.3/common/tools/resourcebrowser/000077500000000000000000000000001413310443000212505ustar00rootroot00000000000000gammaray-2.11.3/common/tools/resourcebrowser/resourcebrowserinterface.cpp000066400000000000000000000026761413310443000271030ustar00rootroot00000000000000/* resourcebrowserinterface.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "resourcebrowserinterface.h" #include "objectbroker.h" using namespace GammaRay; ResourceBrowserInterface::ResourceBrowserInterface(QObject *parent) : QObject(parent) { ObjectBroker::registerObject(this); } ResourceBrowserInterface::~ResourceBrowserInterface() = default; gammaray-2.11.3/common/tools/resourcebrowser/resourcebrowserinterface.h000066400000000000000000000040531413310443000265370ustar00rootroot00000000000000/* resourcebrowserinterface.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_RESOURCEBROWSERINTERFACE_H #define GAMMARAY_RESOURCEBROWSERINTERFACE_H #include namespace GammaRay { class ResourceBrowserInterface : public QObject { Q_OBJECT public: explicit ResourceBrowserInterface(QObject *parent = nullptr); ~ResourceBrowserInterface() override; public slots: virtual void downloadResource(const QString &sourceFilePath, const QString &targetFilePath) = 0; virtual void selectResource(const QString &sourceFilePath, int line = -1, int column = -1) = 0; signals: void resourceDeselected(); void resourceSelected(const QByteArray &contents, int line, int column); void resourceDownloaded(const QString &targetFilePath, const QByteArray &contents); }; } QT_BEGIN_NAMESPACE Q_DECLARE_INTERFACE(GammaRay::ResourceBrowserInterface, "com.kdab.GammaRay.ResourceBrowserInterface") QT_END_NAMESPACE #endif // GAMMARAY_RESOURCEBROWSERINTERFACE_H gammaray-2.11.3/common/transferimage.cpp000066400000000000000000000061561413310443000202200ustar00rootroot00000000000000/* transferimage.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "transferimage.h" #include namespace GammaRay { TransferImage::TransferImage(const QImage &image) : m_image(image) { } const QImage &TransferImage::image() const { return m_image; } void TransferImage::setImage(const QImage &image) { m_image = image; } QTransform TransferImage::transform() const { return m_transform; } void TransferImage::setTransform(const QTransform &transform) { m_transform = transform; } QDataStream &operator<<(QDataStream &stream, const GammaRay::TransferImage &image) { static const TransferImage::Format format = TransferImage::RawFormat; const QImage &img = image.image(); stream << (quint32)(format); switch (format) { case TransferImage::QImageFormat: stream << img; break; case TransferImage::RawFormat: stream << (double)img.devicePixelRatio(); stream << (quint32)img.format() << (quint32)img.width() << (quint32)img.height() << image.transform(); stream.device()->write((const char*)img.constBits(), img.byteCount()); break; } return stream; } QDataStream &operator>>(QDataStream &stream, TransferImage &image) { quint32 i; stream >> i; const TransferImage::Format format = static_cast(i); switch (format) { case TransferImage::QImageFormat: { QImage img; stream >> img; image.setImage(img); break; } case TransferImage::RawFormat: { double r; quint32 f, w, h; QTransform transform; stream >> r >> f >> w >> h >> transform; QImage img(w, h, static_cast(f)); img.setDevicePixelRatio(r); for (int i = 0; i < img.height(); ++i) { const QByteArray buffer = stream.device()->read(img.bytesPerLine()); memcpy(img.scanLine(i), buffer.constData(), img.bytesPerLine()); } image.setImage(img); image.setTransform(transform); break; } } return stream; } } gammaray-2.11.3/common/transferimage.h000066400000000000000000000040011413310443000176500ustar00rootroot00000000000000/* transferimage.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_TRANSFERIMAGE_H #define GAMMARAY_TRANSFERIMAGE_H #include #include #include namespace GammaRay { /** Wrapper class for a QImage to allow raw data transfer over a QDataStream, bypassing the usuale PNG encoding. */ class TransferImage { public: TransferImage() = default; explicit TransferImage(const QImage &image); const QImage &image() const; void setImage(const QImage &image); QTransform transform() const; void setTransform(const QTransform &transform); enum Format { QImageFormat, RawFormat }; private: QImage m_image; QTransform m_transform; }; QDataStream &operator<<(QDataStream &stream, const GammaRay::TransferImage &image); QDataStream &operator>>(QDataStream &stream, GammaRay::TransferImage &image); } Q_DECLARE_METATYPE(GammaRay::TransferImage) #endif // GAMMARAY_TRANSFERIMAGE_H gammaray-2.11.3/common/translator.cpp000066400000000000000000000062601413310443000175560ustar00rootroot00000000000000/* translator.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include "translator.h" #include "paths.h" #include #include #include #include #include #include #include using namespace GammaRay; static QString rootTranslationsPath() { return Paths::rootPath() + QLatin1Char('/') + GAMMARAY_TRANSLATION_INSTALL_DIR; } static QString qtTranslationsPath() { return QLibraryInfo::location(QLibraryInfo::TranslationsPath); } void Translator::loadTranslations(const QString &catalog, const QString &path, const QString &overrideLanguage) { const QDir dir(path); const QLocale locale; QStringList names = locale.uiLanguages(); if (!overrideLanguage.isEmpty()) names.prepend(overrideLanguage); for (const QString &name : qAsConst(names)) { const QLocale uiLocale(name); auto translator = new QTranslator(QCoreApplication::instance()); if (translator->load(uiLocale, catalog, QStringLiteral("_"), path)) { QCoreApplication::instance()->installTranslator(translator); return; } else { delete translator; foreach (const QString &name, uiLocale.uiLanguages()) { const QString fileName = QString("%1_%2.qm").arg(catalog, name); const QString filePath = dir.filePath(fileName); if (QFile::exists(filePath)) { return; } } } } if (locale.language() != QLocale::C && locale.language() != QLocale::English) qDebug() << "did not find a translation for" << catalog << "in" << path << "for language" << locale.name(); } void Translator::loadGammaRayTranslations(const QString &overrideLanguage) { loadTranslations(QStringLiteral("gammaray"), rootTranslationsPath(), overrideLanguage); } void Translator::loadStandAloneTranslations(const QString &overrideLanguage) { loadGammaRayTranslations(overrideLanguage); loadTranslations(QStringLiteral("qt"), qtTranslationsPath(), overrideLanguage); } gammaray-2.11.3/common/translator.h000066400000000000000000000033511413310443000172210ustar00rootroot00000000000000/* translator.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_TRANSLATOR_H #define GAMMARAY_TRANSLATOR_H #include "gammaray_common_export.h" #include namespace GammaRay { namespace Translator { /** Load translation catalogs for current locale. */ void GAMMARAY_COMMON_EXPORT loadTranslations(const QString &catalog, const QString &path, const QString &overrideLanguage = QString()); void GAMMARAY_COMMON_EXPORT loadGammaRayTranslations(const QString &overrideLanguage = QString()); void GAMMARAY_COMMON_EXPORT loadStandAloneTranslations(const QString &overrideLanguage = QString()); } } #endif // GAMMARAY_TRANSLATOR_H gammaray-2.11.3/common/variantwrapper.h000066400000000000000000000040531413310443000200750ustar00rootroot00000000000000/* variantwrapper.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_VARIANTWRAPPER_H #define GAMMARAY_VARIANTWRAPPER_H #include #include namespace GammaRay { /** Transport wrapper for variants that shall not be unpacked by GammaRay::MethodArgument. */ class VariantWrapper { public: inline VariantWrapper() {} explicit inline VariantWrapper(const QVariant &variant) : m_variant(variant) {} inline QVariant variant() const { return m_variant; } inline void setVariant(const QVariant &v) { m_variant = v; } inline operator QVariant() const { return QVariant::fromValue(*this); } private: QVariant m_variant; }; inline QDataStream &operator<<(QDataStream &out, const VariantWrapper &wrapper) { out << wrapper.variant(); return out; } inline QDataStream &operator>>(QDataStream &in, VariantWrapper &value) { QVariant v; in >> v; value.setVariant(v); return in; } } Q_DECLARE_METATYPE(GammaRay::VariantWrapper) #endif gammaray-2.11.3/compat/000077500000000000000000000000001413310443000146505ustar00rootroot00000000000000gammaray-2.11.3/compat/qasconst.h000066400000000000000000000045041413310443000166570ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Copyright (C) 2016 Intel Corporation. ** Contact: https://www.qt.io/licensing/ ** ** This code is taken from the QtCore module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 3 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL3 included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 3 requirements ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 2.0 or (at your option) the GNU General ** Public license version 3 or any later version approved by the KDE Free ** Qt Foundation. The licenses are as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-2.0.html and ** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QASCONST_H #define QASCONST_H #if QT_VERSION < QT_VERSION_CHECK(5, 7, 0) // this adds const to non-const objects (like std::as_const) template Q_DECL_CONSTEXPR typename std::add_const::type &qAsConst(T &t) Q_DECL_NOTHROW { return t; } // prevent rvalue arguments: template void qAsConst(const T &&) Q_DECL_EQ_DELETE; #endif // QT_VERSION < 5.7 #endif // QASCONST_H gammaray-2.11.3/conan/000077500000000000000000000000001413310443000144635ustar00rootroot00000000000000gammaray-2.11.3/conan/ECM/000077500000000000000000000000001413310443000150675ustar00rootroot00000000000000gammaray-2.11.3/conan/ECM/conanfile.py000066400000000000000000000037061413310443000174050ustar00rootroot00000000000000############################################################################### ## This file is part of GammaRay, the Qt application inspection and ## manipulation tool. ## ## Copyright (C) 2019-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com ## Author: Renato Araujo Oliveira Filho ## ## Licensees holding valid commercial KDAB GammaRay licenses may use this file in ## accordance with GammaRay Commercial License Agreement provided with the Software. ## ## Contact info@kdab.com if any conditions of this licensing are not clear to you. ## ## 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, see . ############################################################################### from conans import ConanFile, CMake, tools class ECMConan(ConanFile): name = "ECM" version = "5.64.0" license = "" url = "git://anongit.kde.org/extra-cmake-modules.git" description = "The Extra CMake Modules package,." generators = "cmake" def source(self): git = tools.Git(folder="") git.clone(self.url) git.checkout("v%s"%self.version) def build(self): self.cmake = CMake(self) self.cmake.configure() self.cmake.build() def package(self): self.cmake.install() def package_info(self): self.env_info.CMAKE_PREFIX_PATH.append(self.package_folder) gammaray-2.11.3/conan/GammaRay/000077500000000000000000000000001413310443000161615ustar00rootroot00000000000000gammaray-2.11.3/conan/GammaRay/conanfile.py000066400000000000000000000111561413310443000204750ustar00rootroot00000000000000############################################################################### ## This file is part of GammaRay, the Qt application inspection and ## manipulation tool. ## ## Copyright (C) 2019-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com ## Author: Renato Araujo Oliveira Filho ## ## Licensees holding valid commercial KDAB GammaRay licenses may use this file in ## accordance with GammaRay Commercial License Agreement provided with the Software. ## ## Contact info@kdab.com if any conditions of this licensing are not clear to you. ## ## 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, see . ############################################################################### from conans import ConanFile, CMake, tools class GammaRayConan(ConanFile): name = "GammaRay" version = "2.11.3" license = ("https://raw.githubusercontent.com/KDAB/GammaRay/{0}/LICENSE.txt," "https://raw.githubusercontent.com/KDAB/GammaRay/{0}/LICENSE.GPL.txt," "https://raw.githubusercontent.com/KDAB/GammaRay/{0}/LICENSE.BSD2.txt").format(version) author = "Klaralvdalens Datakonsult AB (KDAB) info@kdab.com" url = "https://github.com/KDAB/GammaRay.git" description = "GammaRay is a software introspection tool for Qt applications developed by KDAB." generators = "cmake" options = dict({ "build_ui": [True, False], "probe_only_build": [True, False], "client_only_build": [True, False], "enable_gpl_only_features": [True, False], "install_qt_layout": [True, False], "build_cli_injector": [True, False], "multi_build": [True, False], "static_probe": [True, False], "build_docs": [True, False], "enforce_qt_asserts": [True, False], }) default_options = dict({ "build_ui": True, "probe_only_build": False, "client_only_build": False, "enable_gpl_only_features": False, "install_qt_layout": False, "build_cli_injector": True, "multi_build": True, "static_probe": False, "build_docs": True, "enforce_qt_asserts": False, }) def requirements(self): self.requires("qt/5.13.2@kdab/stable") self.requires("KDStateMachineEditor/1.2@kdab/stable") self.requires("KSyntaxHighlighting/5.64.0@kdab/stable") def source(self): git = tools.Git(folder="") git.clone(self.url) git.checkout("%s"%self.version) def configure(self): # Use kdab flags to match qt package hash # ~$ conan create -ks -o qt:qttools=True -o qt:qtsvg=True -o qt:qtdeclarative=True -o qt:qtremoteobjects=True -o qt:qtscxml=True . 5.13.2@kdab/stable self.options["qt"].qtsvg = True self.options["qt"].qtdeclarative = True self.options["qt"].qtremoteobjects = True self.options["qt"].qtscxml = True self.options["qt"].qttools = True def build(self): self.cmake = CMake(self) self.cmake.definitions["WITH_INTERNAL_GRAPHVIZ"] = True self.cmake.definitions["GAMMARAY_BUILD_UI"] = self.options.build_ui self.cmake.definitions["GAMMARAY_PROBE_ONLY_BUILD"] = self.options.probe_only_build self.cmake.definitions["GAMMARAY_CLIENT_ONLY_BUILD"] = self.options.client_only_build self.cmake.definitions["GAMMARAY_ENABLE_GPL_ONLY_FEATURES"] = self.options.enable_gpl_only_features self.cmake.definitions["GAMMARAY_INSTALL_QT_LAYOUT"] = self.options.install_qt_layout self.cmake.definitions["GAMMARAY_BUILD_CLI_INJECTOR"] = self.options.build_cli_injector self.cmake.definitions["GAMMARAY_MULTI_BUILD"] = self.options.multi_build self.cmake.definitions["GAMMARAY_BUILD_DOCS"] = self.options.build_docs self.cmake.definitions["GAMMARAY_ENFORCE_QT_ASSERTS"] = self.options.enforce_qt_asserts self.cmake.configure() self.cmake.build() def package(self): self.cmake.install() def package_info(self): self.env_info.CMAKE_PREFIX_PATH.append(self.package_folder) gammaray-2.11.3/conan/KSyntaxHighlighting/000077500000000000000000000000001413310443000204125ustar00rootroot00000000000000gammaray-2.11.3/conan/KSyntaxHighlighting/conanfile.py000066400000000000000000000052171413310443000227270ustar00rootroot00000000000000############################################################################### ## This file is part of GammaRay, the Qt application inspection and ## manipulation tool. ## ## Copyright (C) 2019-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com ## Author: Renato Araujo Oliveira Filho ## ## Licensees holding valid commercial KDAB GammaRay licenses may use this file in ## accordance with GammaRay Commercial License Agreement provided with the Software. ## ## Contact info@kdab.com if any conditions of this licensing are not clear to you. ## ## 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, see . ############################################################################### from conans import ConanFile, CMake, tools class KSyntaxHighlightingConan(ConanFile): name = "KSyntaxHighlighting" version = "5.64.0" license = "https://cgit.kde.org/syntax-highlighting.git/plain/COPYING" url = "git://anongit.kde.org/syntax-highlighting.git" description = "This is a stand-alone implementation of the Kate syntax highlighting engine." generators = "cmake" def requirements(self): self.requires("qt/5.13.2@kdab/stable") self.requires("ECM/5.64.0@kdab/stable") def source(self): git = tools.Git(folder="") git.clone(self.url) git.checkout("v%s"%self.version) def configure(self): # Use kdab flags to match qt package hash # ~$ conan create -ks -o qt:qttools=True -o qt:qtsvg=True -o qt:qtdeclarative=True -o qt:qtremoteobjects=True -o qt:qtscxml=True . 5.13.2@kdab/stable self.options["qt"].qtsvg = True self.options["qt"].qtdeclarative = True self.options["qt"].qtremoteobjects = True self.options["qt"].qtscxml = True self.options["qt"].qttools = True def build(self): self.cmake = CMake(self) self.cmake.configure() self.cmake.build() def package(self): self.cmake.install() def package_info(self): self.env_info.CMAKE_PREFIX_PATH.append(self.package_folder) gammaray-2.11.3/config-gammaray-version.h.cmake000066400000000000000000000006551413310443000213470ustar00rootroot00000000000000#ifndef CONFIG_GAMMARAY_VERSION_H #define CONFIG_GAMMARAY_VERSION_H #define GAMMARAY_VERSION_MAJOR ${GAMMARAY_VERSION_MAJOR} #define GAMMARAY_VERSION_MINOR ${GAMMARAY_VERSION_MINOR} #define GAMMARAY_VERSION_PATCH ${GAMMARAY_VERSION_PATCH} #define GAMMARAY_VERSION_STRING "${GAMMARAY_VERSION_STRING}" #define GAMMARAY_COMPACT_VERSION_STRING "${GAMMARAY_VERSION_MAJOR}.${GAMMARAY_VERSION_MINOR}.${GAMMARAY_VERSION_PATCH}" #endif gammaray-2.11.3/config-gammaray.h.cmake000066400000000000000000000042561413310443000176650ustar00rootroot00000000000000#if !defined(CONFIG_GAMMARAY_H) #define CONFIG_GAMMARAY_H #cmakedefine GAMMARAY_INSTALL_QT_LAYOUT #define GAMMARAY_DEBUG_POSTFIX "${CMAKE_DEBUG_POSTFIX}" // relative install dirs #define GAMMARAY_PLUGIN_INSTALL_DIR "${PLUGIN_INSTALL_DIR}" #define GAMMARAY_LIBEXEC_INSTALL_DIR "${LIBEXEC_INSTALL_DIR}" #define GAMMARAY_BUNDLE_INSTALL_DIR "${BUNDLE_INSTALL_DIR_PRIVATE}" #define GAMMARAY_BIN_INSTALL_DIR "${BIN_INSTALL_DIR}" #define GAMMARAY_PROBE_INSTALL_DIR "${PROBE_INSTALL_DIR}" #define GAMMARAY_TRANSLATION_INSTALL_DIR "${TRANSLATION_INSTALL_DIR}" #define GAMMARAY_QCH_INSTALL_DIR "${QCH_INSTALL_DIR}" #define GAMMARAY_TARGET_PLUGIN_INSTALL_DIR "${TARGET_PLUGIN_INSTALL_DIR}" #define GAMMARAY_PLUGIN_VERSION "${GAMMARAY_PLUGIN_VERSION}" #define GAMMARAY_PROBE_ABI "${GAMMARAY_PROBE_ABI}${GAMMARAY_PROBE_ABI_POSTFIX}" // relative inverse install dirs #define GAMMARAY_INVERSE_BUNDLE_DIR "${GAMMARAY_INVERSE_BUNDLE_DIR}" #define GAMMARAY_INVERSE_BIN_DIR "${GAMMARAY_INVERSE_BIN_DIR}" #define GAMMARAY_INVERSE_LIB_DIR "${GAMMARAY_INVERSE_LIB_DIR}" #define GAMMARAY_INVERSE_PROBE_DIR "${GAMMARAY_INVERSE_PROBE_DIR}" #define GAMMARAY_INVERSE_LIBEXEC_DIR "${GAMMARAY_INVERSE_LIBEXEC_DIR}" // probe name #define GAMMARAY_PROBE_BASENAME "${PROBE_BASENAME}" // library ABI suffix, as used with >= Qt 5.14 on Android #define ANDROID_ABI "${ANDROID_ABI}" // default network properties #define GAMMARAY_DEFAULT_LOCAL_ADDRESS "127.0.0.1" #define GAMMARAY_DEFAULT_LOCAL_TCP_URL "tcp://127.0.0.1" #define GAMMARAY_DEFAULT_ANY_ADDRESS "0.0.0.0" #define GAMMARAY_DEFAULT_ANY_TCP_URL "tcp://0.0.0.0" // build options #cmakedefine HAVE_STDINT_H #cmakedefine HAVE_BACKTRACE #cmakedefine HAVE_CXA_DEMANGLE #cmakedefine HAVE_QT_WIDGETS #cmakedefine HAVE_QT_SCXML #cmakedefine HAVE_QT_SVG #cmakedefine HAVE_QT_DESIGNER #cmakedefine HAVE_QT_PRINTSUPPORT #cmakedefine HAVE_SYNTAX_HIGHLIGHTING #cmakedefine HAVE_ELF_H #cmakedefine HAVE_SYS_ELF_H #cmakedefine HAVE_ELF #cmakedefine GAMMARAY_ENABLE_GPL_ONLY_FEATURES #cmakedefine GAMMARAY_CORE_ONLY_LAUNCHER #cmakedefine GAMMARAY_STATIC_PROBE #cmakedefine GAMMARAY_DISABLE_FEEDBACK #cmakedefine GAMMARAY_WITH_WIDGET3D #define QT_LIBINFIX "@QT_LIBINFIX@" #endif // CONFIG_GAMMARAY_H gammaray-2.11.3/core/000077500000000000000000000000001413310443000143155ustar00rootroot00000000000000gammaray-2.11.3/core/CMakeLists.txt000066400000000000000000000126101413310443000170550ustar00rootroot00000000000000set(gammaray_srcs ${CMAKE_SOURCE_DIR}/3rdparty/qt/resourcemodel.cpp abstractbindingprovider.cpp aggregatedpropertymodel.cpp bindingaggregator.cpp bindingnode.cpp metaobject.cpp metaobjectregistry.cpp metaobjectrepository.cpp metaproperty.cpp probe.cpp probeguard.cpp probesettings.cpp probecontroller.cpp objectlistmodel.cpp objectclassinfomodel.cpp objectmethodmodel.cpp objectenummodel.cpp objecttreemodel.cpp objecttypefilterproxymodel.cpp problemcollector.cpp methodargumentmodel.cpp multisignalmapper.cpp signalspycallbackset.cpp singlecolumnobjectproxymodel.cpp stacktracemodel.cpp toolfactory.cpp toolmanager.cpp toolpluginmodel.cpp toolpluginerrormodel.cpp propertycontroller.cpp propertycontrollerextension.cpp proxytoolfactory.cpp util.cpp varianthandler.cpp objectdataprovider.cpp attributemodel.cpp qmetaobjectvalidator.cpp enumrepositoryserver.cpp enumutil.cpp execution.cpp classesiconsrepositoryserver.cpp propertyadaptor.cpp propertyaggregator.cpp propertydata.cpp propertyfilter.cpp dynamicpropertyadaptor.cpp qmetapropertyadaptor.cpp metapropertyadaptor.cpp associativepropertyadaptor.cpp sequentialpropertyadaptor.cpp objectinstance.cpp propertyadaptorfactory.cpp jsonpropertyadaptor.cpp paintbuffer.cpp paintbuffermodel.cpp paintanalyzer.cpp painterprofilingreplayer.cpp remoteviewserver.cpp tools/metatypebrowser/metatypesmodel.cpp tools/messagehandler/messagehandler.cpp tools/messagehandler/messagemodel.cpp tools/metaobjectbrowser/metaobjectbrowser.cpp tools/metaobjectbrowser/metaobjecttreemodel.cpp tools/metatypebrowser/metatypebrowser.cpp tools/objectinspector/objectinspector.cpp tools/objectinspector/propertiesextension.cpp tools/objectinspector/methodsextension.cpp tools/objectinspector/connectionsextension.cpp tools/objectinspector/abstractconnectionsmodel.cpp tools/objectinspector/inboundconnectionsmodel.cpp tools/objectinspector/outboundconnectionsmodel.cpp tools/objectinspector/enumsextension.cpp tools/objectinspector/classinfoextension.cpp tools/objectinspector/applicationattributeextension.cpp tools/objectinspector/bindingextension.cpp tools/objectinspector/bindingmodel.cpp tools/objectinspector/stacktraceextension.cpp tools/problemreporter/availablecheckersmodel.cpp tools/problemreporter/problemmodel.cpp tools/problemreporter/problemreporter.cpp tools/resourcebrowser/resourcebrowser.cpp tools/resourcebrowser/resourcefiltermodel.cpp remote/server.cpp remote/remotemodelserver.cpp remote/selectionmodelserver.cpp remote/serverdevice.cpp remote/tcpserverdevice.cpp remote/localserverdevice.cpp remote/serverproxymodel.cpp ${CMAKE_SOURCE_DIR}/resources/gammaray.qrc ) set(gammaray_srcs ${gammaray_srcs} tools/messagehandler/loggingcategorymodel.cpp ) include_directories(${CMAKE_SOURCE_DIR}/3rdparty/qt/5.5/) list(APPEND gammaray_srcs ${CMAKE_SOURCE_DIR}/3rdparty/qt/5.5/private/qpaintbuffer.cpp) if(WIN32 AND NOT MINGW) list(APPEND gammaray_srcs ${CMAKE_SOURCE_DIR}/3rdparty/StackWalker/StackWalker.cpp) endif() # core lib add_library(gammaray_core ${GAMMARAY_LIBRARY_TYPE} ${gammaray_srcs} ) generate_export_header(gammaray_core) target_compile_features(gammaray_core PUBLIC ${GAMMARAY_REQUIRED_CXX_FEATURES}) gammaray_target_relocatable_interfaces(gammaray_core_ipaths) target_include_directories(gammaray_core PUBLIC $ $) target_include_directories(gammaray_core SYSTEM PRIVATE ${CMAKE_SOURCE_DIR}/3rdparty/backward-cpp ${Qt5Core_PRIVATE_INCLUDE_DIRS} ${Qt5Gui_PRIVATE_INCLUDE_DIRS} ) target_link_libraries(gammaray_core LINK_PUBLIC gammaray_common LINK_PRIVATE gammaray_common_internal gammaray_kitemmodels ${CMAKE_DL_LIBS} ) set_target_properties(gammaray_core PROPERTIES ${GAMMARAY_DEFAULT_LIBRARY_PROPERTIES} OUTPUT_NAME gammaray_core-${GAMMARAY_PROBE_ABI} ) gammaray_set_rpath(gammaray_core ${LIB_INSTALL_DIR}) target_link_libraries(gammaray_core LINK_PUBLIC Qt5::Core LINK_PRIVATE Qt5::Gui) add_backward(gammaray_core) if(NOT GAMMARAY_PROBE_ONLY_BUILD) install(TARGETS gammaray_core EXPORT GammaRayTargets ${INSTALL_TARGETS_DEFAULT_ARGS}) gammaray_install_headers( ${CMAKE_CURRENT_BINARY_DIR}/gammaray_core_export.h enumrepositoryserver.h enumutil.h metaobject.h metaobjectrepository.h metaproperty.h objectmodelbase.h objectdataprovider.h objecttypefilterproxymodel.h probe.h probecontroller.h propertycontroller.h propertycontrollerextension.h signalspycallbackset.h singlecolumnobjectproxymodel.h toolfactory.h typetraits.h util.h varianthandler.h ) ecm_generate_pri_file(BASE_NAME GammaRayCore LIB_NAME gammaray_core-${GAMMARAY_PROBE_ABI} DEPS "core gui GammaRayCommon" FILENAME_VAR PRI_FILENAME INCLUDE_INSTALL_DIR ${INCLUDE_INSTALL_DIR} ) install(FILES ${PRI_FILENAME} DESTINATION ${ECM_MKSPECS_INSTALL_DIR}) if(GAMMARAY_STATIC_PROBE) gammaray_install_headers(staticprobe.h) endif() else() install(TARGETS gammaray_core ${INSTALL_TARGETS_DEFAULT_ARGS}) endif() if(MSVC) install(FILES "$/$" DESTINATION ${BIN_INSTALL_DIR} CONFIGURATIONS Debug RelWithDebInfo) endif() gammaray-2.11.3/core/abstractbindingprovider.cpp000066400000000000000000000025111413310443000217310ustar00rootroot00000000000000/* abstractbindingprovider.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ // Own #include "abstractbindingprovider.h" using namespace GammaRay; AbstractBindingProvider::AbstractBindingProvider() = default; AbstractBindingProvider::~AbstractBindingProvider() = default; gammaray-2.11.3/core/abstractbindingprovider.h000066400000000000000000000036421413310443000214040ustar00rootroot00000000000000/* abstractbindingprovider.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_ABSTRACTBINDINGPROVIDER_H #define GAMMARAY_ABSTRACTBINDINGPROVIDER_H // Own #include "gammaray_core_export.h" // Qt #include // Std #include #include QT_BEGIN_NAMESPACE class QObject; QT_END_NAMESPACE namespace GammaRay { class BindingNode; class GAMMARAY_CORE_EXPORT AbstractBindingProvider { public: AbstractBindingProvider(); virtual ~AbstractBindingProvider(); virtual std::vector> findDependenciesFor(BindingNode *binding) const = 0; virtual std::vector> findBindingsFor(QObject *obj) const = 0; virtual bool canProvideBindingsFor (QObject *object) const = 0; private: Q_DISABLE_COPY(AbstractBindingProvider) }; } #endif // GAMMARAY_ABSTRACTBINDINGPROVIDER_H gammaray-2.11.3/core/aggregatedpropertymodel.cpp000066400000000000000000000455051413310443000217520ustar00rootroot00000000000000/* aggregatedpropertymodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "aggregatedpropertymodel.h" #include "metaobjectrepository.h" #include "objectinstance.h" #include "probe.h" #include "propertyaggregator.h" #include "propertydata.h" #include "propertyadaptorfactory.h" #include "toolfactory.h" #include "varianthandler.h" #include "enumrepositoryserver.h" #include "enumutil.h" #include "util.h" #include #include #include #include using namespace GammaRay; Q_DECLARE_METATYPE(const QObject*) /*! * Checks if it is dangerous to unpack a QVariant because it stores an invalid pointer. * Returns true if the value is an invalid pointer to a QObject. * Returns false either if it is a valid pointer or not a QObject*. */ bool isInvalidPointer(const QVariant& value) { if (!value.canConvert() && !value.canConvert()) return false; return !Probe::instance()->isValidObject(Util::uncheckedQObjectCast(value)); } AggregatedPropertyModel::AggregatedPropertyModel(QObject *parent) : QAbstractItemModel(parent) { qRegisterMetaType(); } AggregatedPropertyModel::~AggregatedPropertyModel() = default; void AggregatedPropertyModel::setObject(const ObjectInstance &oi) { clear(); if (!oi.isValid()) return; auto adaptor = PropertyAdaptorFactory::create(oi, this); if (!adaptor) return; auto count = adaptor->count(); if (count) beginInsertRows(QModelIndex(), 0, count -1); m_rootAdaptor = adaptor; addPropertyAdaptor(m_rootAdaptor); if (count) endInsertRows(); } void AggregatedPropertyModel::setReadOnly(bool readOnly) { m_readOnly = readOnly; } void AggregatedPropertyModel::clear() { if (!m_rootAdaptor) return; const auto count = m_parentChildrenMap.value(m_rootAdaptor).size(); if (count) beginRemoveRows(QModelIndex(), 0, count - 1); m_parentChildrenMap.clear(); delete m_rootAdaptor; m_rootAdaptor = nullptr; if (count) endRemoveRows(); } QVariant AggregatedPropertyModel::data(const QModelIndex &index, int role) const { if (!index.isValid() || !m_rootAdaptor) return QVariant(); const auto adaptor = adaptorForIndex(index); if (!adaptor->object().isValid()) { QMetaObject::invokeMethod(const_cast(this), "objectInvalidated", Qt::QueuedConnection, Q_ARG(GammaRay::PropertyAdaptor*, adaptor)); return QVariant(); } const auto d = adaptor->propertyData(index.row()); return data(adaptor, d, index.column(), role); } QMap AggregatedPropertyModel::itemData(const QModelIndex &index) const { QMap res; if (!index.isValid() || !m_rootAdaptor) return res; const auto adaptor = adaptorForIndex(index); if (!adaptor->object().isValid()) { QMetaObject::invokeMethod(const_cast(this), "objectInvalidated", Qt::QueuedConnection, Q_ARG(GammaRay::PropertyAdaptor*, adaptor)); return res; } const auto d = adaptor->propertyData(index.row()); res.insert(Qt::DisplayRole, data(adaptor, d, index.column(), Qt::DisplayRole)); res.insert(PropertyModel::ActionRole, data(adaptor, d, index.column(), PropertyModel::ActionRole)); res.insert(PropertyModel::ObjectIdRole, data(adaptor, d, index.column(), PropertyModel::ObjectIdRole)); if (index.column() == 0) { auto v = data(adaptor, d, index.column(), PropertyModel::PropertyFlagsRole); if (!v.isNull()) res.insert(PropertyModel::PropertyFlagsRole, v); v = data(adaptor, d, index.column(), PropertyModel::PropertyRevisionRole); if (!v.isNull()) res.insert(PropertyModel::PropertyRevisionRole, v); v = data(adaptor, d, index.column(), PropertyModel::NotifySignalRole); if (!v.isNull()) res.insert(PropertyModel::NotifySignalRole, v); } else if (index.column() == 1) { res.insert(Qt::EditRole, data(adaptor, d, index.column(), Qt::EditRole)); res.insert(Qt::DecorationRole, data(adaptor, d, index.column(), Qt::DecorationRole)); if (d.value().type() == QVariant::Bool) res.insert(Qt::CheckStateRole, data(adaptor, d, index.column(), Qt::CheckStateRole)); } return res; } QVariant AggregatedPropertyModel::data(PropertyAdaptor *adaptor, const PropertyData &d, int column, int role) const { switch (role) { case Qt::DisplayRole: switch (column) { case 0: return d.name(); case 1: { // QMetaProperty::read sets QVariant::typeName to int for enums, // so we need to handle that separately here const QString enumStr = EnumUtil::enumToString(d.value(), d.typeName().toLatin1(), adaptor->object().metaObject()); if (!enumStr.isEmpty()) return enumStr; if (d.value().type() == QVariant::Bool && (d.accessFlags() & PropertyData::Writable)) return QVariant(); if (isInvalidPointer(d.value()) && Util::uncheckedQObjectCast(d.value()) != nullptr) return "[invalid]"; return VariantHandler::displayString(d.value()); } case 2: return d.typeName(); case 3: return d.className(); } break; case Qt::EditRole: if (column == 1) { const auto me = EnumUtil::metaEnum(d.value(), d.typeName().toLatin1(), adaptor->object().metaObject()); if (me.isValid()) { const auto num = EnumUtil::enumToInt(d.value(), me); return QVariant::fromValue(EnumRepositoryServer::valueFromMetaEnum(num, me)); } return VariantHandler::serializableVariant(d.value()); } break; case Qt::DecorationRole: if (column == 1) return VariantHandler::decoration(d.value()); break; case Qt::CheckStateRole: if (column == 1 && d.value().type() == QVariant::Bool && (d.accessFlags() & PropertyData::Writable)) return d.value().toBool() ? Qt::Checked : Qt::Unchecked; break; case PropertyModel::ActionRole: { int actions = PropertyModel::NoAction; if (d.accessFlags() & PropertyData::Resettable) actions |= PropertyModel::Reset; if (d.accessFlags() & PropertyData::Deletable) actions |= PropertyModel::Delete; if ((MetaObjectRepository::instance()->metaObject(d.typeName()) && *reinterpret_cast(d.value().data())) || d.value().value()) actions |= PropertyModel::NavigateTo; return actions; } case PropertyModel::ObjectIdRole: if (d.value().canConvert()) { if (isInvalidPointer(d.value())) return QVariant(); return QVariant::fromValue(ObjectId(d.value().value())); } else if (d.value().isValid()) { const auto &v = d.value(); return QVariant::fromValue(ObjectId(*reinterpret_cast(v.data()), v.typeName())); } return QVariant(); case PropertyModel::PropertyFlagsRole: if (column == 0 && d.propertyFlags() != PropertyModel::None) return QVariant::fromValue(d.propertyFlags()); return QVariant(); case PropertyModel::PropertyRevisionRole: if (column == 0 && d.revision() >= 0) return d.revision(); return QVariant(); case PropertyModel::NotifySignalRole: if (column == 0 && !d.notifySignal().isEmpty()) return d.notifySignal(); return QVariant(); } return QVariant(); } bool AggregatedPropertyModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (!index.isValid() || !m_rootAdaptor) return false; const auto adaptor = adaptorForIndex(index); switch (role) { case Qt::EditRole: { QPointer guard(adaptor); if (value.userType() == qMetaTypeId()) { const auto d = adaptor->propertyData(index.row()); if (d.value().type() == QVariant::Int) { adaptor->writeProperty(index.row(), value.value().value()); } else { auto v = d.value(); *(static_cast(v.data())) = value.value().value(); adaptor->writeProperty(index.row(), v); } } else { adaptor->writeProperty(index.row(), value); } if (guard) { propagateWrite(adaptor); } return true; } case Qt::CheckStateRole: adaptor->writeProperty(index.row(), value.toInt() == Qt::Checked); propagateWrite(adaptor); return true; case PropertyModel::ResetActionRole: adaptor->resetProperty(index.row()); return true; } return false; } int AggregatedPropertyModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); return 4; } int AggregatedPropertyModel::rowCount(const QModelIndex &parent) const { if (!m_rootAdaptor || parent.column() > 0) return 0; if (!parent.isValid()) return m_parentChildrenMap.value(m_rootAdaptor).size(); auto adaptor = adaptorForIndex(parent); auto &siblings = m_parentChildrenMap[adaptor]; if (!m_inhibitAdaptorCreation && !siblings.at(parent.row())) { // TODO: remember we tried any of this auto pd = adaptor->propertyData(parent.row()); if (!isInvalidPointer(pd.value()) && !hasLoop(adaptor, pd.value())) { auto a = PropertyAdaptorFactory::create(pd.value(), adaptor); siblings[parent.row()] = a; addPropertyAdaptor(a); } } auto childAdaptor = siblings.at(parent.row()); if (!childAdaptor) return 0; return m_parentChildrenMap.value(childAdaptor).size(); // childAdaptor->count() might already be updated in insert/removeRows } Qt::ItemFlags AggregatedPropertyModel::flags(const QModelIndex &index) const { const auto baseFlags = QAbstractItemModel::flags(index); if (!index.isValid() || index.column() != 1 || m_readOnly) return baseFlags; auto adaptor = adaptorForIndex(index); auto data = adaptor->propertyData(index.row()); const auto editable = (data.accessFlags() & PropertyData::Writable) && isParentEditable(adaptor); const auto booleanEditable = editable && data.value().type() == QVariant::Bool; if (booleanEditable) return baseFlags | Qt::ItemIsUserCheckable; return editable ? (baseFlags | Qt::ItemIsEditable) : baseFlags; } QModelIndex AggregatedPropertyModel::parent(const QModelIndex &child) const { auto childAdaptor = adaptorForIndex(child); if (childAdaptor == m_rootAdaptor) return {}; auto parentAdaptor = childAdaptor->parentAdaptor(); return createIndex(m_parentChildrenMap.value(parentAdaptor).indexOf( childAdaptor), 0, parentAdaptor); } QModelIndex AggregatedPropertyModel::index(int row, int column, const QModelIndex &parent) const { if (!hasIndex(row, column, parent) || !m_rootAdaptor) return {}; if (!parent.isValid()) return createIndex(row, column, m_rootAdaptor); auto adaptor = adaptorForIndex(parent); return createIndex(row, column, m_parentChildrenMap.value(adaptor).at(parent.row())); } PropertyAdaptor *AggregatedPropertyModel::adaptorForIndex(const QModelIndex &index) const { if (!index.isValid()) return m_rootAdaptor; return static_cast(index.internalPointer()); } void AggregatedPropertyModel::addPropertyAdaptor(PropertyAdaptor *adaptor) const { if (!adaptor) return; QVector children; children.resize(adaptor->count()); m_parentChildrenMap.insert(adaptor, children); connect(adaptor, &PropertyAdaptor::propertyChanged, this, &AggregatedPropertyModel::propertyChanged); connect(adaptor, &PropertyAdaptor::propertyAdded, this, &AggregatedPropertyModel::propertyAdded); connect(adaptor, &PropertyAdaptor::propertyRemoved, this, &AggregatedPropertyModel::propertyRemoved); } void AggregatedPropertyModel::propertyChanged(int first, int last) { auto adaptor = qobject_cast(sender()); Q_ASSERT(adaptor); Q_ASSERT(m_parentChildrenMap.contains(adaptor)); Q_ASSERT(first <= last); Q_ASSERT(first >= 0); Q_ASSERT(last < adaptor->count()); emit dataChanged(createIndex(first, 0, adaptor), createIndex(last, columnCount() - 1, adaptor)); for (int i = first; i <= last; ++i) reloadSubTree(adaptor, i); } void AggregatedPropertyModel::propertyAdded(int first, int last) { auto adaptor = qobject_cast(sender()); Q_ASSERT(adaptor); Q_ASSERT(m_parentChildrenMap.contains(adaptor)); Q_ASSERT(first <= last); Q_ASSERT(first >= 0); Q_ASSERT(last < adaptor->count()); auto idx = createIndex(first, 0, adaptor); beginInsertRows(idx.parent(), first, last); auto &children = m_parentChildrenMap[adaptor]; if (first >= children.size()) children.resize(last + 1); else children.insert(first, last - first + 1, nullptr); endInsertRows(); } void AggregatedPropertyModel::propertyRemoved(int first, int last) { auto adaptor = qobject_cast(sender()); Q_ASSERT(adaptor); Q_ASSERT(m_parentChildrenMap.contains(adaptor)); Q_ASSERT(first <= last); Q_ASSERT(first >= 0); Q_ASSERT(last < adaptor->count()); auto idx = createIndex(first, 0, adaptor); beginRemoveRows(idx.parent(), first, last); auto &children = m_parentChildrenMap[adaptor]; children.remove(first, last - first + 1); endRemoveRows(); } void AggregatedPropertyModel::objectInvalidated() { auto adaptor = qobject_cast(sender()); objectInvalidated(adaptor); } void AggregatedPropertyModel::objectInvalidated(PropertyAdaptor *adaptor) { Q_ASSERT(adaptor); if (!m_parentChildrenMap.contains(adaptor)) // already handled return; if (adaptor == m_rootAdaptor) { clear(); return; } auto parentAdaptor = adaptor->parentAdaptor(); Q_ASSERT(parentAdaptor); Q_ASSERT(m_parentChildrenMap.contains(parentAdaptor)); reloadSubTree(parentAdaptor, m_parentChildrenMap.value(parentAdaptor).indexOf(adaptor)); } bool AggregatedPropertyModel::hasLoop(PropertyAdaptor *adaptor, const QVariant &v) const { const ObjectInstance newOi(v); if (newOi.type() != ObjectInstance::QtObject && newOi.type() != ObjectInstance::Object) return false; if (!newOi.object()) return false; while (adaptor) { if (adaptor->object() == newOi) return true; adaptor = adaptor->parentAdaptor(); } return false; } void AggregatedPropertyModel::reloadSubTree(PropertyAdaptor *parentAdaptor, int index) { Q_ASSERT(parentAdaptor); Q_ASSERT(m_parentChildrenMap.contains(parentAdaptor)); Q_ASSERT(index >= 0); Q_ASSERT(index < m_parentChildrenMap.value(parentAdaptor).size()); // prevent rowCount calls as a result of the change notification to re-create // the adaptor m_inhibitAdaptorCreation = true; // remove the old sub-tree, if present auto oldAdaptor = m_parentChildrenMap.value(parentAdaptor).at(index); if (oldAdaptor) { auto oldRowCount = m_parentChildrenMap.value(oldAdaptor).size(); if (oldRowCount > 0) beginRemoveRows(createIndex(index, 0, parentAdaptor), 0, oldRowCount - 1); m_parentChildrenMap[parentAdaptor][index] = nullptr; m_parentChildrenMap.remove(oldAdaptor); delete oldAdaptor; if (oldRowCount) endRemoveRows(); } // re-add the sub-tree // TODO consolidate with code in rowCount() auto pd = parentAdaptor->propertyData(index); if (isInvalidPointer(pd.value()) || hasLoop(parentAdaptor, pd.value())) { m_inhibitAdaptorCreation = false; return; } auto newAdaptor = PropertyAdaptorFactory::create(pd.value(), parentAdaptor); if (!newAdaptor) { m_inhibitAdaptorCreation = false; return; } auto newRowCount = newAdaptor->count(); if (newRowCount > 0) beginInsertRows(createIndex(index, 0, parentAdaptor), 0, newRowCount - 1); m_parentChildrenMap[parentAdaptor][index] = newAdaptor; addPropertyAdaptor(newAdaptor); if (newRowCount > 0) endInsertRows(); m_inhibitAdaptorCreation = false; } bool AggregatedPropertyModel::isParentEditable(PropertyAdaptor *adaptor) const { const auto parentAdaptor = adaptor->parentAdaptor(); if (!parentAdaptor) return true; // we need all value types along the way to be writable if (adaptor->object().isValueType()) { const auto row = m_parentChildrenMap.value(parentAdaptor).indexOf(adaptor); Q_ASSERT(row >= 0); const auto pd = parentAdaptor->propertyData(row); if ((pd.accessFlags() & PropertyData::Writable) == 0) return false; } return isParentEditable(parentAdaptor); } void AggregatedPropertyModel::propagateWrite(GammaRay::PropertyAdaptor* adaptor) { const auto parentAdaptor = adaptor->parentAdaptor(); if (!parentAdaptor) return; if (adaptor->object().isValueType()) { const auto row = m_parentChildrenMap.value(parentAdaptor).indexOf(adaptor); Q_ASSERT(row >= 0); parentAdaptor->writeProperty(row, adaptor->object().variant()); } propagateWrite(parentAdaptor); } gammaray-2.11.3/core/aggregatedpropertymodel.h000066400000000000000000000065561413310443000214220ustar00rootroot00000000000000/* aggregatedpropertymodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_AGGREGATEDPROPERTYMODEL_H #define GAMMARAY_AGGREGATEDPROPERTYMODEL_H #include "gammaray_core_export.h" #include #include #include namespace GammaRay { class PropertyAdaptor; class PropertyData; class ObjectInstance; /** Generic property model. */ class GAMMARAY_CORE_EXPORT AggregatedPropertyModel : public QAbstractItemModel { Q_OBJECT public: explicit AggregatedPropertyModel(QObject *parent = nullptr); ~AggregatedPropertyModel() override; void setObject(const ObjectInstance &oi); void setReadOnly(bool readOnly); QVariant data(const QModelIndex &index, int role) const override; bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; QModelIndex parent(const QModelIndex &child) const override; QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; Qt::ItemFlags flags(const QModelIndex &index) const override; QMap itemData(const QModelIndex &index) const override; private: void clear(); PropertyAdaptor *adaptorForIndex(const QModelIndex &index) const; void addPropertyAdaptor(PropertyAdaptor *adaptor) const; QVariant data(PropertyAdaptor *adaptor, const PropertyData &d, int column, int role) const; bool hasLoop(PropertyAdaptor *adaptor, const QVariant &v) const; void reloadSubTree(PropertyAdaptor *parentAdaptor, int index); bool isParentEditable(PropertyAdaptor *adaptor) const; void propagateWrite(PropertyAdaptor *adaptor); private slots: void propertyChanged(int first, int last); void propertyAdded(int first, int last); void propertyRemoved(int first, int last); void objectInvalidated(); void objectInvalidated(GammaRay::PropertyAdaptor *adaptor); private: PropertyAdaptor *m_rootAdaptor = nullptr; mutable QHash > m_parentChildrenMap; bool m_inhibitAdaptorCreation = false; bool m_readOnly = false; }; } #endif // GAMMARAY_AGGREGATEDPROPERTYMODEL_H gammaray-2.11.3/core/associativepropertyadaptor.cpp000066400000000000000000000043341413310443000225170ustar00rootroot00000000000000/* associativepropertyadaptor.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "associativepropertyadaptor.h" #include "objectinstance.h" #include "propertydata.h" #include "varianthandler.h" #include using namespace GammaRay; AssociativePropertyAdaptor::AssociativePropertyAdaptor(QObject *parent) : PropertyAdaptor(parent) { } AssociativePropertyAdaptor::~AssociativePropertyAdaptor() = default; void AssociativePropertyAdaptor::doSetObject(const ObjectInstance &oi) { if (oi.type() == ObjectInstance::QtVariant) m_value = oi.variant(); } int AssociativePropertyAdaptor::count() const { if (!m_value.canConvert()) return 0; auto it = m_value.value(); return it.size(); } PropertyData AssociativePropertyAdaptor::propertyData(int index) const { Q_ASSERT(m_value.canConvert()); auto iterable = m_value.value(); auto it = iterable.begin(); it += index; PropertyData data; data.setName(VariantHandler::displayString(it.key())); data.setValue(it.value()); data.setClassName(m_value.typeName()); return data; } gammaray-2.11.3/core/associativepropertyadaptor.h000066400000000000000000000034441413310443000221650ustar00rootroot00000000000000/* associativepropertyadaptor.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_ASSOCIATIVEPROPERTYADAPTOR_H #define GAMMARAY_ASSOCIATIVEPROPERTYADAPTOR_H #include "propertyadaptor.h" #include namespace GammaRay { /** Adaptor for recursing into associative container property values. */ class AssociativePropertyAdaptor : public PropertyAdaptor { Q_OBJECT public: explicit AssociativePropertyAdaptor(QObject *parent = nullptr); ~AssociativePropertyAdaptor() override; int count() const override; PropertyData propertyData(int index) const override; protected: void doSetObject(const ObjectInstance &oi) override; private: QVariant m_value; }; } #endif // GAMMARAY_ASSOCIATIVEPROPERTYADAPTOR_H gammaray-2.11.3/core/attributemodel.cpp000066400000000000000000000063041413310443000200500ustar00rootroot00000000000000/* attributemodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "attributemodel.h" using namespace GammaRay; AbstractAttributeModel::AbstractAttributeModel(QObject *parent) : QAbstractTableModel(parent) { } AbstractAttributeModel::~AbstractAttributeModel() = default; void AbstractAttributeModel::setAttributeType(const char *name) { beginResetModel(); const auto idx = staticQtMetaObject.indexOfEnumerator(name); Q_ASSERT(idx >= 0); m_attrs = staticQtMetaObject.enumerator(idx); endResetModel(); } int AbstractAttributeModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); return 1; } int AbstractAttributeModel::rowCount(const QModelIndex &parent) const { if (parent.isValid() || !m_attrs.isValid()) return 0; return m_attrs.keyCount() - 1; // skip AttributeCount } QVariant AbstractAttributeModel::data(const QModelIndex &index, int role) const { if (!index.isValid() || !m_attrs.isValid()) return QVariant(); switch (role) { case Qt::DisplayRole: return QString::fromLatin1(m_attrs.valueToKey(m_attrs.value(index.row()))).mid(3); case Qt::CheckStateRole: return testAttribute(m_attrs.value(index.row())) ? Qt::Checked : Qt::Unchecked; } return QVariant(); } QVariant AbstractAttributeModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) return tr("Attribute"); return QAbstractTableModel::headerData(section, orientation, role); } Qt::ItemFlags AbstractAttributeModel::flags(const QModelIndex &index) const { const auto baseFlags = QAbstractTableModel::flags(index); if (!index.isValid()) return baseFlags; return baseFlags | Qt::ItemIsUserCheckable; } bool AbstractAttributeModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (!index.isValid() || !m_attrs.isValid() || role != Qt::CheckStateRole) return false; setAttribute(m_attrs.value(index.row()), value.toInt() == Qt::Checked); emit dataChanged(index, index); return false; } gammaray-2.11.3/core/attributemodel.h000066400000000000000000000060001413310443000175060ustar00rootroot00000000000000/* attributemodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_ATTRIBUTEMODEL_H #define GAMMARAY_ATTRIBUTEMODEL_H #include "gammaray_core_export.h" #include #include namespace GammaRay { class GAMMARAY_CORE_EXPORT AbstractAttributeModel : public QAbstractTableModel { Q_OBJECT public: explicit AbstractAttributeModel(QObject *parent = nullptr); ~AbstractAttributeModel() override; void setAttributeType(const char *name); int columnCount(const QModelIndex &parent) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role) const override; QVariant headerData(int section, Qt::Orientation orientation, int role) const override; Qt::ItemFlags flags(const QModelIndex &index) const override; bool setData(const QModelIndex &index, const QVariant &value, int role) override; protected: virtual bool testAttribute(int attr) const = 0; virtual void setAttribute(int attr, bool on) = 0; private: QMetaEnum m_attrs; }; template class AttributeModel : public AbstractAttributeModel { public: explicit AttributeModel(QObject *parent = nullptr) : AbstractAttributeModel(parent) { } ~AttributeModel() override = default; void setObject(Class *obj) { if (m_obj == obj) return; m_obj = obj; // cppcheck-suppress nullPointer emit dataChanged(index(0, 0), index(rowCount() - 1, 0)); } protected: bool testAttribute(int attr) const override { if (!m_obj) return false; return m_obj->testAttribute(static_cast(attr)); } void setAttribute(int attr, bool on) override { if (!m_obj) return; m_obj->setAttribute(static_cast(attr), on); } private: Class *m_obj = nullptr; }; } #endif // GAMMARAY_ATTRIBUTEMODEL_H gammaray-2.11.3/core/bindingaggregator.cpp000066400000000000000000000121511413310443000204760ustar00rootroot00000000000000/* bindingaggregator.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Authors: Volker Krause Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ // Own #include "bindingaggregator.h" #include #include #include #include #include #include #include // Qt #include #include #include using namespace GammaRay; Q_GLOBAL_STATIC(std::vector>, s_providers) void BindingAggregator::registerBindingProvider(std::unique_ptr provider) { s_providers()->push_back(std::move(provider)); } bool GammaRay::BindingAggregator::providerAvailableFor(QObject* object) { return std::find_if(s_providers()->begin(), s_providers()->end(), [object](const std::unique_ptr& provider) { return provider->canProvideBindingsFor(object); }) != s_providers()->end(); } std::vector> BindingAggregator::findDependenciesFor(BindingNode* node) { std::vector> allDependencies; if (node->isPartOfBindingLoop()) return allDependencies; for (const auto &provider : *s_providers()) { auto providerDependencies = provider->findDependenciesFor(node); for (auto &&providerDependency : providerDependencies) { providerDependency->dependencies() = findDependenciesFor(providerDependency.get()); allDependencies.push_back(std::move(providerDependency)); } } std::sort( allDependencies.begin(), allDependencies.end(), [](const std::unique_ptr &a, const std::unique_ptr &b) { return a->object() < b->object() || (a->object() == b->object() && a->propertyIndex() < b->propertyIndex()); } ); return allDependencies; } std::vector> BindingAggregator::bindingTreeForObject(QObject* obj) { std::vector> bindings; if (obj) { for (auto providerIt = s_providers()->begin(); providerIt != s_providers()->cend(); ++providerIt) { auto newBindings = (*providerIt)->findBindingsFor(obj); for (auto &&newBinding : newBindings) { BindingNode *node = newBinding.get(); if (std::find_if(bindings.begin(), bindings.end(), [node](const std::unique_ptr &other){ return *node == *other; }) != bindings.end()) { continue; // apparently this is a duplicate. } node->dependencies() = findDependenciesFor(node); bindings.push_back(std::move(newBinding)); } } } return bindings; } void BindingAggregator::scanForBindingLoops() { const QVector &allObjects = Probe::instance()->allQObjects(); QMutexLocker lock(Probe::objectLock()); for (QObject *obj : allObjects) { if (!Probe::instance()->isValidObject(obj)) continue; auto bindings = bindingTreeForObject(obj); for (auto &&bindingNode : bindings) { if (bindingNode->isPartOfBindingLoop()) { Problem p; p.severity = Problem::Error; p.description = QStringLiteral("Object %1 / Property %2 has a binding loop.").arg(ObjectDataProvider::typeName(bindingNode->object())).arg(bindingNode->canonicalName()); p.object = ObjectId(bindingNode->object()); p.locations.push_back(bindingNode->sourceLocation()); p.problemId = QString("com.kdab.GammaRay.ObjectInspector.BindingLoopScan:%1.%2").arg(reinterpret_cast(bindingNode->object())).arg(bindingNode->propertyIndex()); p.findingCategory = Problem::Scan; ProblemCollector::addProblem(p); } } } } gammaray-2.11.3/core/bindingaggregator.h000066400000000000000000000037641413310443000201550ustar00rootroot00000000000000/* bindingaggregator.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Authors: Volker Krause Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_BINDINGAGGREGATOR_H #define GAMMARAY_BINDINGAGGREGATOR_H // Own #include #include "gammaray_core_export.h" // Qt #include #include // Std #include #include namespace GammaRay { class AbstractBindingProvider; class BindingNode; namespace BindingAggregator { GAMMARAY_CORE_EXPORT bool providerAvailableFor(QObject *object); GAMMARAY_CORE_EXPORT std::vector> findDependenciesFor(BindingNode* node); GAMMARAY_CORE_EXPORT std::vector> bindingTreeForObject(QObject* obj); GAMMARAY_CORE_EXPORT void scanForBindingLoops(); GAMMARAY_CORE_EXPORT void registerBindingProvider(std::unique_ptr provider); } } #endif // GAMMARAY_BINDINGAGGREGATOR_H gammaray-2.11.3/core/bindingnode.cpp000066400000000000000000000104401413310443000173000ustar00rootroot00000000000000/* bindingnode.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ // Own #include "bindingnode.h" #include // Qt #include #include #include using namespace GammaRay; BindingNode::BindingNode(QObject *obj, int propIndex, BindingNode *parent) : m_parent(parent) , m_object(obj) , m_propertyIndex(propIndex) { Q_ASSERT(obj); m_canonicalName = m_object->metaObject() ? m_object->metaObject()->property(m_propertyIndex).name() : ":("; refreshValue(); checkForLoops(); } bool BindingNode::operator==(const BindingNode& other) const { return m_object == other.m_object && m_propertyIndex == other.m_propertyIndex; } void BindingNode::checkForLoops() { BindingNode *ancestor = m_parent; while (ancestor) { if (ancestor->m_object == m_object && ancestor->m_propertyIndex == m_propertyIndex) { m_foundBindingLoop = true; return; } ancestor = ancestor->m_parent; } m_foundBindingLoop = false; } QMetaProperty BindingNode::property() const { return m_object->metaObject()->property(m_propertyIndex); } QVariant BindingNode::cachedValue() const { return m_value; } QVariant BindingNode::readValue() const { return m_object->metaObject()->property(m_propertyIndex).read(m_object); } void BindingNode::refreshValue() { m_value = m_object->metaObject()->property(m_propertyIndex).read(m_object); } void BindingNode::setParent(BindingNode *newParent) { m_parent = newParent; checkForLoops(); } GammaRay::BindingNode * GammaRay::BindingNode::parent() const { return m_parent; } QObject * GammaRay::BindingNode::object() const { return m_object; } int GammaRay::BindingNode::propertyIndex() const { return m_propertyIndex; } const QString & GammaRay::BindingNode::canonicalName() const { return m_canonicalName; } bool GammaRay::BindingNode::hasFoundBindingLoop() const { return m_foundBindingLoop; } bool GammaRay::BindingNode::isPartOfBindingLoop() const { if (m_foundBindingLoop) { return true; } for (const auto &dependency : m_dependencies) { if (dependency->isPartOfBindingLoop()) { return true; } } return false; } GammaRay::SourceLocation GammaRay::BindingNode::sourceLocation() const { return m_sourceLocation; } std::vector > & GammaRay::BindingNode::dependencies() { return m_dependencies; } const std::vector > & GammaRay::BindingNode::dependencies() const { return m_dependencies; } void BindingNode::setCanonicalName(const QString& name) { m_canonicalName = name; } void BindingNode::setSourceLocation(const SourceLocation &location) { m_sourceLocation = location; } uint BindingNode::depth() const { uint depth = 0; if (m_foundBindingLoop) { return std::numeric_limits::max(); // to be considered as infinity. } for (const auto &dependency : m_dependencies) { uint depDepth = dependency->depth(); if (depDepth == std::numeric_limits::max()) { depth = depDepth; break; } else if (depDepth + 1 > depth) { depth = depDepth + 1; } } return depth; } gammaray-2.11.3/core/bindingnode.h000066400000000000000000000055171413310443000167560ustar00rootroot00000000000000/* bindingnode.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_BINDINGNODE_H #define GAMMARAY_BINDINGNODE_H // Own #include "gammaray_core_export.h" #include // Qt #include // Std #include #include class MockBindingProvider; namespace GammaRay { class GAMMARAY_CORE_EXPORT BindingNode { public: BindingNode (QObject *object, int propertyIndex, BindingNode *parent = nullptr); bool operator==(const BindingNode &other) const; BindingNode *parent() const; QObject *object() const; int propertyIndex() const; QMetaProperty property() const; const QString &canonicalName() const; /** * This function returns true, if checkForLoops() found a loop. * It usually only returns true for one node in a binding loop. * * \sa isPartOfBindingLoop() */ bool hasFoundBindingLoop() const; bool isPartOfBindingLoop() const; SourceLocation sourceLocation() const; uint depth() const; QVariant cachedValue() const; QVariant readValue() const; std::vector> &dependencies(); const std::vector> &dependencies() const; void refreshValue(); void checkForLoops(); void setParent(BindingNode *newParent); void setSourceLocation(const SourceLocation &location); void setCanonicalName(const QString &name); private: Q_DISABLE_COPY(BindingNode) BindingNode *m_parent; QObject *m_object; int m_propertyIndex; QString m_canonicalName; QVariant m_value; bool m_foundBindingLoop = false; SourceLocation m_sourceLocation; std::vector> m_dependencies; friend class ::MockBindingProvider; }; } #endif // GAMMARAY_BINDINGNODE_H gammaray-2.11.3/core/classesiconsrepositoryserver.cpp000066400000000000000000000043001413310443000230760ustar00rootroot00000000000000/* classesiconsrepositoryserver.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Filipe Azevedo Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "classesiconsrepositoryserver.h" #include #include "common/classesiconsindex_data.cpp" using namespace GammaRay; ClassesIconsRepositoryServer* ClassesIconsRepositoryServer::s_instance = nullptr; ClassesIconsRepositoryServer::ClassesIconsRepositoryServer(QObject *parent) : ClassesIconsRepository(parent) { if (ClassesIconsIndex::s_keyToNumber.isEmpty()) { ClassesIconsIndex::initializeClassesIconsIndex(); } QVector index; index.resize(ClassesIconsIndex::s_numberToKey.size()); for (auto it = ClassesIconsIndex::s_numberToKey.constBegin(), end = ClassesIconsIndex::s_numberToKey.constEnd(); it != end; ++it) { index[it.key()] = it.value(); } setIndex(index); } ClassesIconsRepositoryServer::~ClassesIconsRepositoryServer() { s_instance = nullptr; } void ClassesIconsRepositoryServer::create(QObject *parent) { Q_ASSERT(!s_instance); s_instance = new ClassesIconsRepositoryServer(parent); } void ClassesIconsRepositoryServer::requestIndex() { emit indexResponse(index()); } gammaray-2.11.3/core/classesiconsrepositoryserver.h000066400000000000000000000036571413310443000225610ustar00rootroot00000000000000/* classesiconsrepositoryserver.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Filipe Azevedo Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_CLASSESICONSREPOSITORYSERVER_H #define GAMMARAY_CLASSESICONSREPOSITORYSERVER_H #include namespace GammaRay { /*! Probe-side classes icons index management. * Use this to obtain a class icon file path that can be safely used in the client. */ class ClassesIconsRepositoryServer : public ClassesIconsRepository { Q_OBJECT Q_INTERFACES(GammaRay::ClassesIconsRepository) public: ~ClassesIconsRepositoryServer() override; //! @cond internal static void create(QObject *parent); //! @endcond protected slots: void requestIndex() override; private: explicit ClassesIconsRepositoryServer(QObject *parent = nullptr); static ClassesIconsRepositoryServer *s_instance; }; } #endif // GAMMARAY_CLASSESICONSREPOSITORYSERVER_H gammaray-2.11.3/core/dynamicpropertyadaptor.cpp000066400000000000000000000076661413310443000216440ustar00rootroot00000000000000/* dynamicpropertyadaptor.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "dynamicpropertyadaptor.h" #include "propertydata.h" #include "objectinstance.h" #include #include using namespace GammaRay; DynamicPropertyAdaptor::DynamicPropertyAdaptor(QObject *parent) : PropertyAdaptor(parent) { } DynamicPropertyAdaptor::~DynamicPropertyAdaptor() = default; void DynamicPropertyAdaptor::doSetObject(const ObjectInstance &oi) { auto obj = oi.qtObject(); if (obj) { m_propNames = obj->dynamicPropertyNames(); obj->installEventFilter(this); connect(obj, &QObject::destroyed, this, &PropertyAdaptor::objectInvalidated); } } int DynamicPropertyAdaptor::count() const { if (!object().isValid()) return 0; Q_ASSERT(m_propNames.size() == object().qtObject()->dynamicPropertyNames().size()); return m_propNames.size(); } PropertyData DynamicPropertyAdaptor::propertyData(int index) const { PropertyData data; if (!object().isValid()) return data; Q_ASSERT(m_propNames.size() == object().qtObject()->dynamicPropertyNames().size()); data.setName(m_propNames.at(index)); data.setValue(object().qtObject()->property(m_propNames.at(index))); data.setClassName(tr("")); data.setAccessFlags(PropertyData::Writable | PropertyData::Deletable); return data; } void DynamicPropertyAdaptor::writeProperty(int index, const QVariant &value) { if (!object().isValid()) return; Q_ASSERT(index < m_propNames.size()); const auto propName = m_propNames.at(index); object().qtObject()->setProperty(propName, value); } bool DynamicPropertyAdaptor::canAddProperty() const { return object().qtObject(); } void DynamicPropertyAdaptor::addProperty(const PropertyData &data) { if (!object().isValid()) return; Q_ASSERT(!m_propNames.contains(data.name().toUtf8())); object().qtObject()->setProperty(data.name().toUtf8(), data.value()); } bool DynamicPropertyAdaptor::eventFilter(QObject *receiver, QEvent *event) { auto obj = object().qtObject(); if (receiver == obj && event->type() == QEvent::DynamicPropertyChange) { const auto changeEvent = static_cast(event); const auto oldIdx = m_propNames.indexOf(changeEvent->propertyName()); const auto newIdx = obj->dynamicPropertyNames().indexOf(changeEvent->propertyName()); if (oldIdx >= 0 && newIdx >= 0) { Q_ASSERT(oldIdx == newIdx); emit propertyChanged(oldIdx, oldIdx); } else if (newIdx >= 0) { m_propNames = obj->dynamicPropertyNames(); emit propertyAdded(newIdx, newIdx); } else { Q_ASSERT(oldIdx >= 0); m_propNames = obj->dynamicPropertyNames(); emit propertyRemoved(oldIdx, oldIdx); } } return PropertyAdaptor::eventFilter(receiver, event); } gammaray-2.11.3/core/dynamicpropertyadaptor.h000066400000000000000000000037131413310443000212760ustar00rootroot00000000000000/* dynamicpropertyadaptor.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_DYNAMICPROPERTYADAPTOR_H #define GAMMARAY_DYNAMICPROPERTYADAPTOR_H #include "propertyadaptor.h" namespace GammaRay { /** Access to dynamic QObject properties. */ class DynamicPropertyAdaptor : public PropertyAdaptor { Q_OBJECT public: explicit DynamicPropertyAdaptor(QObject *parent = nullptr); ~DynamicPropertyAdaptor() override; int count() const override; PropertyData propertyData(int index) const override; void writeProperty(int index, const QVariant &value) override; bool canAddProperty() const override; void addProperty(const PropertyData &data) override; protected: void doSetObject(const ObjectInstance &oi) override; bool eventFilter(QObject *receiver, QEvent *event) override; private: QList m_propNames; }; } #endif // GAMMARAY_DYNAMICPROPERTYADAPTOR_H gammaray-2.11.3/core/enumrepositoryserver.cpp000066400000000000000000000072141413310443000213600ustar00rootroot00000000000000/* enumrepositoryserver.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "enumrepositoryserver.h" #include using namespace GammaRay; EnumRepositoryServer* EnumRepositoryServer::s_instance = nullptr; EnumRepositoryServer::EnumRepositoryServer(QObject *parent) : EnumRepository(parent) , m_nextId(FirstValidEnumId) { } EnumRepositoryServer::~EnumRepositoryServer() { s_instance = nullptr; } bool EnumRepositoryServer::isEnum(int metaTypeId) { if (!s_instance) return false; return s_instance->m_typeIdToIdMap.contains(metaTypeId); } EnumRepository* EnumRepositoryServer::create(QObject *parent) { Q_ASSERT(!s_instance); s_instance = new EnumRepositoryServer(parent); return s_instance; } void EnumRepositoryServer::requestDefinition(EnumId id) { const auto def = definition(id); if (def.isValid()) emit definitionResponse(definition(id)); } EnumValue EnumRepositoryServer::valueFromMetaEnum(int value, const QMetaEnum &me) { Q_ASSERT(s_instance); Q_ASSERT(me.isValid()); const auto typeName = QByteArray(me.scope()) + "::" + me.name(); const auto it = s_instance->m_nameToIdMap.constFind(typeName); if (it != s_instance->m_nameToIdMap.constEnd()) return EnumValue(it.value(), value); EnumDefinition def(s_instance->m_nextId++, typeName); def.setIsFlag(me.isFlag()); QVector elements; elements.reserve(me.keyCount()); for (int i = 0; i < me.keyCount(); ++i) elements.push_back(EnumDefinitionElement(me.value(i), me.key(i))); def.setElements(elements); s_instance->addDefinition(def); s_instance->m_nameToIdMap.insert(typeName, def.id()); return EnumValue(def.id(), value); } EnumValue EnumRepositoryServer::valueFromVariant(const QVariant& value) { Q_ASSERT(s_instance); const auto it = s_instance->m_typeIdToIdMap.constFind(value.userType()); Q_ASSERT(it != s_instance->m_typeIdToIdMap.constEnd()); const auto def = definitionForId(it.value()); if (def.isFlag()) return EnumValue(it.value(), *static_cast(value.constData())); // see EnumUtil return EnumValue(it.value(), value.toInt()); } void EnumRepositoryServer::registerEnum(int metaTypeId, const char* name, const QVector& elems, bool flag) { Q_ASSERT(s_instance); Q_ASSERT(name); Q_ASSERT(!elems.isEmpty()); EnumDefinition def(s_instance->m_nextId++, name); def.setIsFlag(flag); def.setElements(elems); s_instance->addDefinition(def); s_instance->m_typeIdToIdMap.insert(metaTypeId, def.id()); } gammaray-2.11.3/core/enumrepositoryserver.h000066400000000000000000000077241413310443000210330ustar00rootroot00000000000000/* enumrepositoryserver.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_ENUMREPOSITORYSERVER_H #define GAMMARAY_ENUMREPOSITORYSERVER_H #include "gammaray_core_export.h" #include "metaenum.h" #include #include QT_BEGIN_NAMESPACE class QMetaEnum; QT_END_NAMESPACE namespace GammaRay { /*! Probe-side enum definition management. * Use this to obtain enum value representations that can be safely transferred to the client. */ class EnumRepositoryServer : public EnumRepository { Q_OBJECT public: ~EnumRepositoryServer() override; /*! Creates an EnumValue instance for the given numeric value and QMetaEnum. * If not yet present, the enum definition with be registered * with the EnumRepositoryServer, so that the client has access * to the key/value mappings of the enum if needed. */ static GAMMARAY_CORE_EXPORT EnumValue valueFromMetaEnum(int value, const QMetaEnum &me); /*! Creates an EnumValue instance for a given QVariant containing a registered * GammaRay::MetaEnum value. */ static GAMMARAY_CORE_EXPORT EnumValue valueFromVariant(const QVariant &value); /*! Check if the given meta type id is a known enum. */ static GAMMARAY_CORE_EXPORT bool isEnum(int metaTypeId); //! @cond internal static GAMMARAY_CORE_EXPORT EnumRepository* create(QObject *parent); // only exported for unit tests template static void registerEnum(const MetaEnum::Value(&lookup_table)[N], const char* name, bool flag) { if (isEnum(qMetaTypeId())) return; QVector elements; elements.reserve(N); for (std::size_t i = 0; i < N; ++i) elements.push_back(EnumDefinitionElement(lookup_table[i].value, lookup_table[i].name)); registerEnum(qMetaTypeId(), name, elements, flag); } static GAMMARAY_CORE_EXPORT void registerEnum(int metaTypeId, const char *name, const QVector &elems, bool flag); static inline EnumDefinition definitionForId(EnumId id) { return s_instance->definition(id); } //! @endcond private: explicit EnumRepositoryServer(QObject *parent = nullptr); void requestDefinition(EnumId id) override; EnumId m_nextId; static EnumRepositoryServer *s_instance; QHash m_nameToIdMap; QHash m_typeIdToIdMap; }; } /*! Register a MetaEnum lookup table with the EnumRepository. */ #define ER_REGISTER_ENUM(Class, Name, LookupTable) { \ EnumRepositoryServer::registerEnum(LookupTable, #Class "::" #Name, false); \ } /*! Register a MetaEnum lookup table with the EnumRepository. */ #define ER_REGISTER_FLAGS(Class, Name, LookupTable) { \ EnumRepositoryServer::registerEnum(LookupTable, #Class "::" #Name, true); \ } #endif // GAMMARAY_ENUMREPOSITORYSERVER_H gammaray-2.11.3/core/enumutil.cpp000066400000000000000000000101301413310443000166560ustar00rootroot00000000000000/* enumutil.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "enumutil.h" #include "enumrepositoryserver.h" #include #include using namespace GammaRay; namespace GammaRay { class ProtectedExposer : public QObject { public: using QObject::staticQtMetaObject; }; } static const QMetaObject *metaObjectForClass(const QByteArray &name) { if (name.isEmpty()) return nullptr; auto mo = QMetaType::metaObjectForType(QMetaType::type(name)); if (mo) return mo; mo = QMetaType::metaObjectForType(QMetaType::type(name + '*')); // try pointer version, more likely for QObjects return mo; } QMetaEnum EnumUtil::metaEnum(const QVariant &value, const char *typeName, const QMetaObject *metaObject) { QByteArray fullTypeName(typeName); if (fullTypeName.isEmpty()) fullTypeName = value.typeName(); // split class name and enum name QByteArray className; QByteArray enumTypeName(fullTypeName); const int pos = enumTypeName.lastIndexOf("::"); if (pos >= 0) { className = enumTypeName.left(pos); enumTypeName = enumTypeName.mid(pos + 2); } const QMetaObject *mo = &ProtectedExposer::staticQtMetaObject; int enumIndex = mo->indexOfEnumerator(enumTypeName); if (enumIndex < 0 && metaObject) { mo = metaObject; enumIndex = mo->indexOfEnumerator(enumTypeName); } if (enumIndex < 0 && (mo = QMetaType::metaObjectForType(QMetaType::type(fullTypeName)))) { enumIndex = mo->indexOfEnumerator(enumTypeName); } if (enumIndex < 0 && (mo = metaObjectForClass(className))) { enumIndex = mo->indexOfEnumerator(enumTypeName); } // attempt to recover namespaces from semi-qualified type names if (enumIndex < 0 && metaObject) { QByteArray n(metaObject->className()); const int pos = n.lastIndexOf("::"); if (pos > 0) { n = n.left(pos + 2) + fullTypeName; return metaEnum(value, n, nullptr); } } if (enumIndex < 0) return {}; return mo->enumerator(enumIndex); } int EnumUtil::enumToInt(const QVariant &value, const QMetaEnum &metaEnum) { // QVariant has no implicit QFlag to int conversion as of Qt 5.7 if (metaEnum.isFlag() && QMetaType::sizeOf(value.userType()) == sizeof(int)) // int should be enough, QFlag has that hardcoded return value.constData() ? *static_cast(value.constData()) : 0; return value.toInt(); } QString EnumUtil::enumToString(const QVariant &value, const char *typeName, const QMetaObject *metaObject) { const auto me = metaEnum(value, typeName, metaObject); if (me.isValid()) { return me.isFlag() ? QString::fromUtf8(me.valueToKeys(enumToInt(value, me))) : QString::fromUtf8(me.valueToKey(enumToInt(value, me))); } if (EnumRepositoryServer::isEnum(value.userType())) { const auto ev = EnumRepositoryServer::valueFromVariant(value); const auto def = EnumRepositoryServer::definitionForId(ev.id()); return def.valueToString(ev); } return QString(); } gammaray-2.11.3/core/enumutil.h000066400000000000000000000056001413310443000163310ustar00rootroot00000000000000/* enumutil.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_ENUMUTIL_H #define GAMMARAY_ENUMUTIL_H #include "gammaray_core_export.h" #include QT_BEGIN_NAMESPACE class QMetaEnum; struct QMetaObject; class QString; class QVariant; QT_END_NAMESPACE namespace GammaRay { /*! Enum conversion utility functions. */ namespace EnumUtil { /*! * Determine the QMetaEnum for a given enum/flag value. * @param value A QVariant holding the enum/flag value, numerical or * as actual enum/flag. * @param typeName A type name hint, in case @p value holds a numeric value. * @param metaObject A meta object hint holding the enum definition. * @since 2.6 */ GAMMARAY_CORE_EXPORT QMetaEnum metaEnum(const QVariant &value, const char *typeName = nullptr, const QMetaObject *metaObject = nullptr); /*! * Converts the given enum or flag value into its numerical representation. * @param value A QVariant holding the enum/flag value, numerical or * as actual enum/flag. * @param metaEnum The QMetaEnum defining the enum/flag. * @since 2.6 */ GAMMARAY_CORE_EXPORT int enumToInt(const QVariant &value, const QMetaEnum &metaEnum); /*! * Translates an enum or flag value into a human readable text. * @param value The numerical value. Type information from the QVariant * are used to find the corresponding QMetaEnum. * @param typeName Use this if the @p value has type int * (e.g. the case for QMetaProperty::read). * @param metaObject QMetaObject possibly containing the definition of the enum. * * @return a QString containing the string version of the specified @p value. * @since 2.6 */ GAMMARAY_CORE_EXPORT QString enumToString(const QVariant &value, const char *typeName = nullptr, const QMetaObject *metaObject = nullptr); } } #endif // GAMMARAY_ENUMUTIL_H gammaray-2.11.3/core/execution.cpp000066400000000000000000000240721413310443000170310ustar00rootroot00000000000000/* execution.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include "execution.h" #include #if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID) && defined(HAVE_BACKTRACE) #include #define USE_BACKWARD_CPP #else #undef USE_BACKWARD_CPP #endif #if defined(Q_CC_MSVC) #define USE_STACKWALKER #include #else #undef USE_STACKWALKER #endif #ifdef HAVE_BACKTRACE #include #endif #ifdef HAVE_CXA_DEMANGLE #include #endif using namespace GammaRay; namespace GammaRay { namespace Execution { #ifdef USE_BACKWARD_CPP class TraceData : public backward::StackTrace { public: using backward::StackTrace::skip_n_firsts; }; #elif defined(Q_OS_WIN) typedef QVector TraceData; #else typedef QVector TraceData; #endif class TracePrivate { public: static TraceData& get(const Trace &trace) { return trace.d->data; } TraceData data; }; }} #ifndef Q_OS_WIN //BEGIN UNIX specific code #include bool Execution::isReadOnlyData(const void* data) { Dl_info info; // ### technically we would also need to check if we are in a read-only section, but this close enough for our purpose return dladdr(const_cast(data), &info) != 0; } static bool stackTracingAvailableImpl() { #if defined(USE_BACKWARD_CPP) || defined(HAVE_BACKTRACE) return true; #else return false; #endif } static bool hasFastStackTraceImpl() { return true; } Execution::Trace Execution::stackTrace(int maxDepth, int skip) { Trace t; auto &data = TracePrivate::get(t); #ifdef USE_BACKWARD_CPP data.load_here(maxDepth); // skip 3: 2 calls in backward-cpp, plus this method // however, don't skip more frames than we actually got, as that confuses backward-cpp massively // (this can happen in release builds on ARM apparently) data.skip_n_firsts(std::min(data.size(), skip + data.skip_n_firsts() + 3)); #elif defined(HAVE_BACKTRACE) data.resize(maxDepth); const auto size = backtrace(data.data(), maxDepth); if (size <= 0) data.clear(); else data.resize(size); data.remove(0, skip + 1); // skip 1: this method #else Q_UNUSED(maxDepth); Q_UNUSED(skip); #endif return t; } #ifdef USE_BACKWARD_CPP static backward::TraceResolver* resolver() { static backward::TraceResolver s_traceResolver; return &s_traceResolver; } static Execution::ResolvedFrame toResolvedFrame(const backward::ResolvedTrace &resolvedTrace, void *addr) { Execution::ResolvedFrame frame; // there's also resolvedTrace.location.name, but that doesn't give us fully qualified C++ names if (!resolvedTrace.object_function.empty()) frame.name = QString::fromStdString(resolvedTrace.object_function); else if (!resolvedTrace.object_filename.empty()) frame.name = QString::fromStdString(resolvedTrace.object_filename); else frame.name = QString::number(reinterpret_cast(addr), 16); frame.location.setUrl(QUrl::fromLocalFile(QString::fromStdString(resolvedTrace.source.filename))); frame.location.setOneBasedLine(resolvedTrace.source.line); frame.location.setOneBasedColumn(resolvedTrace.source.col); return frame; } #endif #if !defined(USE_BACKWARD_CPP) && defined(HAVE_BACKTRACE) static QString maybeDemangleName(char *name) { #ifdef HAVE_CXA_DEMANGLE #if defined(Q_OS_OSX) const char MANGLED_START[] = " _Z"; const char MANGLED_END[] = " "; #else const char MANGLED_START[] = "(_Z"; const char MANGLED_END[] = "+"; #endif auto mangledNameStart = strstr(name, MANGLED_START); if (mangledNameStart) { ++mangledNameStart; const auto mangledNameEnd = strstr(mangledNameStart, MANGLED_END); if (mangledNameEnd) { int status; *mangledNameEnd = 0; auto demangled = abi::__cxa_demangle(mangledNameStart, nullptr, nullptr, &status); *mangledNameEnd = MANGLED_END[0]; if (status == 0 && demangled) { QString ret = QString::fromLatin1(name, mangledNameStart - name) +QString::fromLatin1(demangled) +QString::fromLatin1(mangledNameEnd); free(demangled); return ret; } } } #endif return QString::fromLatin1(name); } #endif Execution::ResolvedFrame Execution::resolveOne(const Execution::Trace &trace, int index) { ResolvedFrame frame; if (index >= trace.size()) return frame; #ifdef USE_BACKWARD_CPP auto &st = TracePrivate::get(trace); resolver()->load_stacktrace(st); frame = toResolvedFrame(resolver()->resolve(st[index]), st[index].addr); #elif defined(HAVE_BACKTRACE) const auto &v = TracePrivate::get(trace); char **strings = backtrace_symbols(v.data() + index, 1); frame.name = maybeDemangleName(strings[0]); free(strings); #else Q_UNUSED(trace); Q_UNUSED(index); #endif return frame; } QVector Execution::resolveAll(const Execution::Trace &trace) { QVector frames; frames.reserve(trace.size()); #ifdef USE_BACKWARD_CPP auto &st = TracePrivate::get(trace); resolver()->load_stacktrace(st); for (int i = 0; i < trace.size(); ++i) frames.push_back(toResolvedFrame(resolver()->resolve(st[i]), st[i].addr)); #elif defined(HAVE_BACKTRACE) const auto &v = TracePrivate::get(trace); char **strings = backtrace_symbols(v.data(), v.size()); for (int i = 0; i < v.size(); ++i) { ResolvedFrame frame; frame.name = maybeDemangleName(strings[i]); frames.push_back(frame); } free(strings); #endif return frames; } //END Unix specific code #else //BEGIN Windows specific code #include bool Execution::isReadOnlyData(const void* data) { HMODULE handle; // ### technically we would also need to check if we are in a read-only section, but this close enough for our purpose return GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, reinterpret_cast(data), &handle); } static bool stackTracingAvailableImpl() { #ifdef USE_STACKWALKER return true; #else return false; #endif } static bool hasFastStackTraceImpl() { return false; } #ifdef USE_STACKWALKER class ResolvingStackWalker : public StackWalker { public: void stackTrace(QVector *frames, int maxDepth, int skip) { m_frames = frames; m_maxDepth = maxDepth; m_skip = skip; ShowCallstack(); } protected: void OnSymInit(LPCSTR, DWORD, LPCSTR) override {} void OnLoadModule(LPCSTR, LPCSTR, DWORD64, DWORD, DWORD, LPCSTR, LPCSTR, ULONGLONG) override {} void OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry) override { if (eType == lastEntry || entry.offset == 0) return; if (m_skip > 0) { --m_skip; return; } if (m_maxDepth > 0 && m_frames->size() >= m_maxDepth) return; Execution::ResolvedFrame frame; frame.name = entry.name; if (frame.name.isEmpty()) frame.name = entry.moduleName; if (frame.name.isEmpty()) frame.name = QString::number(entry.offset, 16); frame.location.setUrl(QUrl::fromLocalFile(entry.lineFileName)); frame.location.setOneBasedLine(entry.lineNumber); m_frames->push_back(frame); } private: QVector *m_frames; int m_maxDepth; int m_skip; }; #endif Execution::Trace Execution::stackTrace(int maxDepth, int skip) { Trace t; #ifdef USE_STACKWALKER static ResolvingStackWalker s_stackWalker; auto &v = TracePrivate::get(t); s_stackWalker.stackTrace(&v, maxDepth, skip + 2); // skip 2: this and StackWalker::stackTrace #else Q_UNUSED(maxDepth); Q_UNUSED(skip); #endif return t; } Execution::ResolvedFrame Execution::resolveOne(const Execution::Trace &trace, int index) { return TracePrivate::get(trace).at(index); } QVector Execution::resolveAll(const Execution::Trace &trace) { QVector frames; frames.reserve(trace.size()); for (int i = 0; i < trace.size(); ++i) frames.push_back(resolveOne(trace, i)); return frames; } //END Windows specific Code #endif //BEGIN generic code applicable for all platforms namespace GammaRay { namespace Execution { bool stackTracingAvailable() { static const bool disableStackTracing = qgetenv("GAMMARAY_DISABLE_STACKTRACE") == "1"; if (disableStackTracing) return false; return stackTracingAvailableImpl(); } bool hasFastStackTrace() { return stackTracingAvailable() && hasFastStackTraceImpl(); } Trace::Trace() : d(new TracePrivate) { } Trace::Trace(const Trace &) = default; Trace::~Trace() = default; Trace& Trace::operator=(const Trace &) = default; bool Trace::empty() const { return d->data.size() == 0; } int Trace::size() const { return d->data.size(); } }} //END generic code gammaray-2.11.3/core/execution.h000066400000000000000000000062501413310443000164740ustar00rootroot00000000000000/* execution.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_EXECUTION_H #define GAMMARAY_EXECUTION_H #include "gammaray_core_export.h" #include #include #include #include namespace GammaRay { /*! Functions to inspect the current program execution. */ namespace Execution { /*! Checks whether the given memory address is inside a read-only data section. */ GAMMARAY_CORE_EXPORT bool isReadOnlyData(const void *data); /*! Returns @c true if we have the capability to generate stack traces. */ GAMMARAY_CORE_EXPORT bool stackTracingAvailable(); /*! Returns @c true if we have a way to obtain fast stack traces. * That is, if we can defer stack frame resolution to later point in time. */ GAMMARAY_CORE_EXPORT bool hasFastStackTrace(); class TracePrivate; /*! An instance of a backtrace. * This is essentially just a list of addresses, and needs to * be resolved to symbol names and source location for display. */ class GAMMARAY_CORE_EXPORT Trace { public: Trace(); Trace(const Trace &other); ~Trace(); Trace& operator=(const Trace &other); bool empty() const; int size() const; private: friend class TracePrivate; std::shared_ptr d; }; /*! Create a backtrace. * @param maxDepth The maximum amount of frames to trace * @param skip The amount of frames to skip from the beginning. This is useful to * exclude the code triggering the trace for example. Traces returned by this function * already start at the calling frame, so you do not need to account for the internals * of this here. */ GAMMARAY_CORE_EXPORT Trace stackTrace(int maxDepth, int skip = 0); /*! A resolved frame in a stack trace. */ class GAMMARAY_CORE_EXPORT ResolvedFrame { public: QString name; SourceLocation location; }; /*! Resolve a single backtrace frame. */ GAMMARAY_CORE_EXPORT ResolvedFrame resolveOne(const Trace &trace, int index); /*! Resolve an entire backtrace. */ GAMMARAY_CORE_EXPORT QVector resolveAll(const Trace &trace); } } Q_DECLARE_METATYPE(GammaRay::Execution::Trace) #endif // GAMMARAY_EXECUTION_H gammaray-2.11.3/core/jsonpropertyadaptor.cpp000066400000000000000000000050171413310443000211550ustar00rootroot00000000000000/* jsonobjectpropertyadaptor.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "jsonpropertyadaptor.h" #include "objectinstance.h" #include "propertydata.h" #include "varianthandler.h" using namespace GammaRay; JsonPropertyAdaptor::JsonPropertyAdaptor(QObject *parent) : PropertyAdaptor(parent) , m_isObject(false) { } JsonPropertyAdaptor::~JsonPropertyAdaptor() = default; void JsonPropertyAdaptor::doSetObject(const ObjectInstance &oi) { if(oi.variant().userType() == qMetaTypeId()) { m_array = oi.variant().toJsonArray(); m_isObject = false; } else if(oi.variant().toJsonValue().isArray()) { m_array = oi.variant().toJsonValue().toArray(); m_isObject = false; } else { m_object = oi.variant().toJsonObject(); m_isObject = true; } } int JsonPropertyAdaptor::count() const { if (m_isObject) { return m_object.size(); } else { return m_array.size(); } } PropertyData JsonPropertyAdaptor::propertyData(int index) const { PropertyData data; if (m_isObject) { auto it = m_object.begin(); it += index; data.setName(VariantHandler::displayString(it.key())); data.setValue(it.value()); data.setClassName(QStringLiteral("QJsonObject")); } else { data.setName(VariantHandler::displayString(index)); data.setValue(m_array[index]); data.setClassName(QStringLiteral("QJsonArray")); } return data; } gammaray-2.11.3/core/jsonpropertyadaptor.h000066400000000000000000000034751413310443000206300ustar00rootroot00000000000000/* jsonpropertyadaptor.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Nicolas Fella Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_JSONPROPERTYADAPTOR_H #define GAMMARAY_JSONPROPERTYADAPTOR_H #include "propertyadaptor.h" #include #include namespace GammaRay { /** Adaptor for recursing into jsonobject container property values. */ class JsonPropertyAdaptor : public PropertyAdaptor { Q_OBJECT public: explicit JsonPropertyAdaptor(QObject *parent = nullptr); ~JsonPropertyAdaptor() override; int count() const override; PropertyData propertyData(int index) const override; protected: void doSetObject(const ObjectInstance &oi) override; private: QJsonObject m_object; QJsonArray m_array; bool m_isObject; }; } #endif // GAMMARAY_JSONPROPERTYADAPTOR_H gammaray-2.11.3/core/metaenum.h000066400000000000000000000103051413310443000163000ustar00rootroot00000000000000/* metaenum.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_METAENUM_H #define GAMMARAY_METAENUM_H #include #include namespace GammaRay { /** Enum/flag stringification utilities. */ namespace MetaEnum { template struct Value { T value; const char * const name; }; template std::size_t count(const Value(&)[N]) { return N; } template QString enumToString(T value, const Value(&lookupTable)[N]) { for (std::size_t i = 0; i < N; ++i) { if (lookupTable[i].value == value) return QString::fromUtf8(lookupTable[i].name); } return QStringLiteral("unknown (") + QString::number(value) + ')'; } template QString flagsToString(T flags, const Value(&lookupTable)[N]) { QStringList l; T handledFlags = T(); for (std::size_t i = 0; i < N; ++i) { if (flags & lookupTable[i].value) { l.push_back(QString::fromUtf8(lookupTable[i].name)); } handledFlags |= lookupTable[i].value; } if (flags & ~handledFlags) { l.push_back(QStringLiteral("flag 0x") + QString::number(qulonglong(flags & ~handledFlags), 16)); } if (l.isEmpty()) { // check if we have a special 0-value for (std::size_t i = 0; i < N; ++i) { if (lookupTable[i].value == 0) return QString::fromUtf8(lookupTable[i].name); } return QStringLiteral(""); } return l.join(QStringLiteral("|")); } // functors for easy use with VariantHandler::registerStringConverter namespace detail { template class enum_to_string_functor { public: explicit enum_to_string_functor(const MetaEnum::Value(&lookupTable)[N]) : m_lookupTable(lookupTable) {} QString operator()(T value) { return MetaEnum::enumToString(value, m_lookupTable); } private: const MetaEnum::Value (&m_lookupTable)[N]; }; template class flags_to_string_functor { public: explicit flags_to_string_functor(const MetaEnum::Value(&lookupTable)[N]) : m_lookupTable(lookupTable) {} #if !defined(Q_CC_MSVC) || _MSC_VER >= 1900 //krazy:exclude=cpp to deal with older MS compilers QString operator()(typename std::underlying_type::type value) #else QString operator()(unsigned int value) #endif { return MetaEnum::flagsToString(value, m_lookupTable); } private: const MetaEnum::Value (&m_lookupTable)[N]; }; } /** Creates a functor for MetaEnum::enumToString and a specific lookup table. */ template detail::enum_to_string_functor enumToString_fn(const Value(&lookupTable)[N]) { return detail::enum_to_string_functor(lookupTable); } /** Creates a functor for MetaEnum::flagsToString and a specific lookup table. */ template detail::flags_to_string_functor flagsToString_fn(const Value(&lookupTable)[N]) { return detail::flags_to_string_functor(lookupTable); } } // MetaEnum } // GammaRay #endif gammaray-2.11.3/core/metaobject.cpp000066400000000000000000000075241413310443000171460ustar00rootroot00000000000000/* metaobject.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "metaobject.h" using namespace GammaRay; MetaObject::MetaObject() = default; MetaObject::~MetaObject() { qDeleteAll(m_properties); } int MetaObject::propertyCount() const { int count = 0; for (MetaObject *mo : m_baseClasses) count += mo->propertyCount(); return count + m_properties.size(); } MetaProperty *MetaObject::propertyAt(int index) const { for (MetaObject *mo : m_baseClasses) { if (index >= mo->propertyCount()) index -= mo->propertyCount(); else return mo->propertyAt(index); } Q_ASSERT(index >= 0 && index < m_properties.size()); return m_properties.at(index); } void MetaObject::addBaseClass(MetaObject *baseClass) { Q_ASSERT(baseClass); m_baseClasses.push_back(baseClass); } void MetaObject::addProperty(MetaProperty *property) { Q_ASSERT(property); // TODO: sort property->setMetaObject(this); m_properties.push_back(property); } QString MetaObject::className() const { return m_className; } void MetaObject::setClassName(const QString &className) { m_className = className; } void *MetaObject::castForPropertyAt(void *object, int index) const { for (int i = 0; i < m_baseClasses.size(); ++i) { const MetaObject *base = m_baseClasses.at(i); if (index >= base->propertyCount()) index -= base->propertyCount(); else return base->castForPropertyAt(castToBaseClass(object, i), index); } return object; // our own property } void *MetaObject::castTo(void *object, const QString &baseClass) const { if (className() == baseClass) return object; for (int i = 0; i < m_baseClasses.size(); ++i) { const MetaObject *base = m_baseClasses.at(i); const auto result = base->castTo(castToBaseClass(object, i), baseClass); if (result) return result; } return nullptr; } bool MetaObject::isPolymorphic() const { if (isClassPolymorphic()) return true; for (const auto &baseClass : m_baseClasses) { if (baseClass->isPolymorphic()) return true; } return false; } void* MetaObject::castFrom(void *object, MetaObject *baseClass) const { const auto idx = m_baseClasses.indexOf(baseClass); if (idx < 0) return nullptr; return castFromBaseClass(object, idx); } MetaObject *MetaObject::superClass(int index) const { if (m_baseClasses.size() <= index) return nullptr; return m_baseClasses[index]; } bool MetaObject::inherits(const QString &className) const { if (className == m_className) return true; for (MetaObject *metaObject : m_baseClasses) { if (metaObject->inherits(className)) return true; } return false; } gammaray-2.11.3/core/metaobject.h000066400000000000000000000131151413310443000166040ustar00rootroot00000000000000/* metaobject.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_METAOBJECT_H #define GAMMARAY_METAOBJECT_H #include "gammaray_core_export.h" #include "metaproperty.h" #include "typetraits.h" #include namespace GammaRay { /*! Compile-time introspection adaptor for non-QObject classes. */ class GAMMARAY_CORE_EXPORT MetaObject { public: MetaObject(); virtual ~MetaObject(); /*! * Returns the amount of properties available in this class (including base classes). */ int propertyCount() const; /*! * Returns the property adaptor for index @p index. */ MetaProperty *propertyAt(int index) const; /*! Add a base class meta object. */ void addBaseClass(MetaObject *baseClass); /*! Add a property for this class. This transfers ownership. */ void addProperty(MetaProperty *property); /*! Returns the name of the class represented by this object. */ QString className() const; /*! Casts a void pointer for an instance of this type to one appropriate * for use with the property at index @p index. * Make sure to use this when dealing with multi-inheritance. */ void *castForPropertyAt(void *object, int index) const; /*! Casts to a void pointer for an instance of this type to one referring * to the given base class type. If @p baseClass is not a base class * of this type, @c nullptr is returned. */ void *castTo(void *object, const QString &baseClass) const; /*! Sets the name of this class to @p className. */ void setClassName(const QString &className); /*! Returns the MetaObject for the @p index base class. */ MetaObject *superClass(int index = 0) const; /*! Returns @c true if this class inherits (directly or indirectly) a class named @p className. */ bool inherits(const QString &className) const; /*! Returns @c true if this is a polymorphic type. * @see std::is_polymorphic */ bool isPolymorphic() const; /*! Casts to a void pointer for an instance of this type * coming from the given base class. This performs the equivalent * of a dynamic_cast and thus can return @p nullptr. */ void* castFrom(void *object, MetaObject *baseClass) const; protected: /*! Casts up to base class @p baseClassIndex. * This is important when traversing multi-inheritance trees. */ virtual void *castToBaseClass(void *object, int baseClassIndex) const = 0; /*! Casts down from base class @p baseClassIndex. * This performs a dynamic cast on polymorphic types, and is undefined for non-polymorphic types. */ virtual void *castFromBaseClass(void *object, int baseClassIndex) const = 0; /*! Returns if this type is polymorphic. This can but does not require * to return @c true for types derives from a polymorphic type. */ virtual bool isClassPolymorphic() const = 0; protected: ///@cond internal QVector m_baseClasses; ///@endcond private: Q_DISABLE_COPY(MetaObject) QVector m_properties; QString m_className; }; ///@cond internal /*! Template implementation of MetaObject. */ template class MetaObjectImpl : public MetaObject { protected: void *castToBaseClass(void *object, int baseClassIndex) const override { Q_ASSERT(baseClassIndex >= 0 && baseClassIndex < m_baseClasses.size()); switch (baseClassIndex) { case 0: return static_cast(static_cast(object)); case 1: return static_cast(static_cast(object)); case 2: return static_cast(static_cast(object)); } Q_ASSERT_X(false, "MetaObjectImpl::castToBaseClass", "Unexpected baseClassIndex encountered"); return nullptr; } void *castFromBaseClass(void *object, int baseClassIndex) const override { Q_ASSERT(baseClassIndex >= 0 && baseClassIndex < m_baseClasses.size()); Q_ASSERT(isPolymorphic()); switch (baseClassIndex) { case 0: return DynamicCast(static_cast(object)); case 1: return DynamicCast(static_cast(object)); case 2: return DynamicCast(static_cast(object)); } return nullptr; } bool isClassPolymorphic() const override { return IsPolymorphic(); } }; ///@endcond } #endif // GAMMARAY_METAOBJECT_H gammaray-2.11.3/core/metaobjectmodel.h000066400000000000000000000120241413310443000176230ustar00rootroot00000000000000/* metaobjectmodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_METAOBJECTMODEL_H #define GAMMARAY_METAOBJECTMODEL_H #include #include #include #include "metaobjectregistry.h" #include "probe.h" namespace GammaRay { template class MetaObjectModel : public QAbstractItemModel { Q_DECLARE_TR_FUNCTIONS(GammaRay::MetaObjectModel) public: explicit MetaObjectModel(QObject *parent = nullptr) : QAbstractItemModel(parent) { } virtual void setMetaObject(const QMetaObject *metaObject) { if (m_rowCount) { beginRemoveRows(QModelIndex(), 0, m_rowCount - 1); m_metaObject = nullptr; m_rowCount = 0; endRemoveRows(); } else { m_metaObject = nullptr; } if (!metaObject) return; if (!Probe::instance()->metaObjectRegistry()->isValid(metaObject)) return; const auto newRowCount = (metaObject->*MetaCount)(); if (newRowCount) { beginInsertRows(QModelIndex(), 0, newRowCount - 1); m_metaObject = metaObject; m_rowCount = newRowCount; endInsertRows(); } else { m_metaObject = metaObject; } } QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override { if (!index.isValid() || !m_metaObject || index.row() < 0 || index.row() >= rowCount(index.parent())) return QVariant(); if (!Probe::instance()->metaObjectRegistry()->isValid(m_metaObject)) return QVariant(); const MetaThing metaThing = (m_metaObject->*MetaAccessor)(index.row()); if (index.column() == columnCount(index) - 1 && role == Qt::DisplayRole) { const QMetaObject *mo = m_metaObject; while ((mo->*MetaOffset)() > index.row()) mo = mo->superClass(); return mo->className(); } return metaData(index, metaThing, role); } QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { if (section == columnCount() - 1) return tr("Class"); return columnHeader(section); } return QAbstractItemModel::headerData(section, orientation, role); } int rowCount(const QModelIndex &parent = QModelIndex()) const override { if (!m_metaObject || parent.isValid()) return 0; return m_rowCount; } QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override { if (row >= 0 && row < rowCount(parent) && column >= 0 && column < columnCount(parent) && !parent.isValid()) return createIndex(row, column, -1); return QModelIndex(); } QModelIndex parent(const QModelIndex &child) const override { Q_UNUSED(child); return QModelIndex(); } protected: virtual QVariant metaData(const QModelIndex &index, const MetaThing &metaThing, int role) const = 0; virtual QString columnHeader(int index) const { Q_UNUSED(index); return QString(); }; typedef MetaObjectModel super; protected: // let's assume that meta objects never get destroyed // very funny, never heard of QML? ;) const QMetaObject *m_metaObject = nullptr; // cached row count, so we don't need to dereference a potentially stale m_metaObject // in setMetaObject again, as that might have been destroyed meanwhile int m_rowCount = 0; }; } #endif // GAMMARAY_METAOBJECTMODEL_H gammaray-2.11.3/core/metaobjectregistry.cpp000066400000000000000000000257041413310443000207370ustar00rootroot00000000000000/* metaobjecttreemodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2012-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Kevin Funk Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "metaobjectregistry.h" #include #include #include #include #include #include #include #include #include #include using namespace GammaRay; namespace GammaRay { /** * Open QObject for access to protected data members */ class UnprotectedQObject : public QObject { public: inline QObjectData *data() const { return d_ptr.data(); } }; } /** * Return true in case the object has a dynamic meta object * * If you look at the code generated by moc you'll see this: * @code * const QMetaObject *GammaRay::MessageModel::metaObject() const * { * return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject; * } * @endcode * * QtQuick uses dynamic meta objects (subclasses of QAbstractDynamicMetaObject, defined in qobject_h.p) * for QML types. It's possible that these meta objects get destroyed * at runtime, so we need to protect against this. * * @note We cannot say if a specific QMetaObject* is dynamic or not * (QMetaObject is non-polymorphic, so we cannot dynamic_cast to QAbstractDynamicMetaObject*), * we can just judge by looking at QObjectData of QObject* * -- hence the QObject* parameter in hasDynamicMetaObject. * * @return Return true in case metaObject() does not point to staticMetaObject. */ static inline bool hasDynamicMetaObject(const QObject *object) { return reinterpret_cast(object)->data()->metaObject != nullptr; } MetaObjectRegistry::MetaObjectRegistry(QObject *parent) : QObject(parent) { qRegisterMetaType(); scanMetaTypes(); } MetaObjectRegistry::~MetaObjectRegistry() = default; QVariant MetaObjectRegistry::data(const QMetaObject *metaObject, MetaObjectData type) const { switch (type) { case ClassName: return m_metaObjectInfoMap.value(metaObject).className; case Valid: return isValid(metaObject); case SelfCount: if (inheritsQObject(metaObject)) return m_metaObjectInfoMap.value(metaObject).selfCount; return QStringLiteral("-"); case InclusiveCount: if (inheritsQObject(metaObject)) return m_metaObjectInfoMap.value(metaObject).inclusiveCount; return QStringLiteral("-"); case SelfAliveCount: if (inheritsQObject(metaObject)) return m_metaObjectInfoMap.value(metaObject).selfAliveCount; return QStringLiteral("-"); case InclusiveAliveCount: if (inheritsQObject(metaObject)) return m_metaObjectInfoMap.value(metaObject).inclusiveAliveCount; return QStringLiteral("-"); } return QVariant(); } bool MetaObjectRegistry::isValid(const QMetaObject *metaObject) const { const auto it = m_metaObjectInfoMap.constFind(metaObject); if (it == m_metaObjectInfoMap.constEnd()) return false; return !(*it).invalid; } bool MetaObjectRegistry::isStatic(const QMetaObject *metaObject) const { const auto it = m_metaObjectInfoMap.constFind(metaObject); if (it == m_metaObjectInfoMap.constEnd()) return false; return (*it).isStatic; } const QMetaObject *MetaObjectRegistry::parentOf(const QMetaObject *metaObject) const { return m_childParentMap.value(metaObject); } QVector MetaObjectRegistry::childrenOf(const QMetaObject *metaObject) const { return m_parentChildMap.value(metaObject); } bool MetaObjectRegistry::inheritsQObject(const QMetaObject *metaObject) const { while (metaObject) { if (metaObject == &QObject::staticMetaObject) return true; metaObject = m_childParentMap.value(metaObject); } return false; } void MetaObjectRegistry::objectAdded(QObject *obj) { // Probe::objectFullyConstructed calls us and ensures this already Q_ASSERT(thread() == QThread::currentThread()); Q_ASSERT(Probe::instance()->isValidObject(obj)); Q_ASSERT(!obj->parent() || Probe::instance()->isValidObject(obj->parent())); const QMetaObject *metaObject = obj->metaObject(); metaObject = addMetaObject(metaObject, hasDynamicMetaObject(obj)); /* * This will increase these values: * - selfCount for that particular @p metaObject * - inclusiveCount for @p metaObject and *all* ancestors * * Complexity-wise the inclusive count calculation should be okay, * since the number of ancestors should be rather small * (QMetaObject class hierarchy is rather a broad than a deep tree structure) * * If this yields some performance issues, we might need to remove the inclusive * costs calculation altogether (a calculate-on-request pattern should be even slower) */ m_metaObjectMap.insert(obj, metaObject); auto &info = m_metaObjectInfoMap[metaObject]; ++info.selfCount; ++info.selfAliveCount; if (info.isDynamic) addAliveInstance(obj, metaObject); // increase inclusive counts const QMetaObject *current = metaObject; while (current) { auto &info = m_metaObjectInfoMap[current]; ++info.inclusiveCount; ++info.inclusiveAliveCount; info.invalid = false; emit dataChanged(current); current = parentOf(current); } } void MetaObjectRegistry::scanMetaTypes() { for (int mtId = 0; mtId <= QMetaType::User || QMetaType::isRegistered(mtId); ++mtId) { if (!QMetaType::isRegistered(mtId)) continue; const auto *mt = QMetaType::metaObjectForType(mtId); if (mt) addMetaObject(mt); } addMetaObject(&staticQtMetaObject); } const QMetaObject *MetaObjectRegistry::addMetaObject(const QMetaObject *metaObject, bool mergeDynamic) { if (isKnownMetaObject(metaObject)) return metaObject; const QMetaObject *parentMetaObject = metaObject->superClass(); if (parentMetaObject && !isKnownMetaObject(parentMetaObject)) { // add parent first parentMetaObject = addMetaObject(metaObject->superClass(), mergeDynamic); } const auto isStatic = Execution::isReadOnlyData(metaObject); if (!isStatic && mergeDynamic) { const QByteArray name(metaObject->className()); const auto it = m_metaObjectNameMap.constFind(name); if (it != m_metaObjectNameMap.constEnd()) return *it; // ### we could do some sanity checking here if the QMO content is really identical, in case they just happen to have the same name m_metaObjectNameMap.insert(name, metaObject); } auto &info = m_metaObjectInfoMap[metaObject]; info.className = metaObject->className(); info.isStatic = isStatic; info.isDynamic = !isStatic && mergeDynamic; // make the parent immediately retrieveable, so that slots connected to // beforeMetaObjectAdded() can use parentOf(). m_childParentMap.insert(metaObject, parentMetaObject); QVector &children = m_parentChildMap[ parentMetaObject ]; emit beforeMetaObjectAdded(metaObject); children.push_back(metaObject); emit afterMetaObjectAdded(metaObject); return metaObject; } void MetaObjectRegistry::objectRemoved(QObject *obj) { Q_ASSERT(thread() == QThread::currentThread()); // decrease counter const QMetaObject *metaObject = m_metaObjectMap.take(obj); if (!metaObject) return; auto &info = m_metaObjectInfoMap[metaObject]; assert(!info.className.isEmpty()); // ie. we found the entry if (info.selfAliveCount == 0) { // something went wrong, but let's just ignore this event in case of assert return; } --info.selfAliveCount; assert(info.selfAliveCount >= 0); if (info.isDynamic) removeAliveInstance(obj, metaObject); // decrease inclusive counts const QMetaObject *current = metaObject; while (current) { MetaObjectInfo &info = m_metaObjectInfoMap[current]; --info.inclusiveAliveCount; assert(info.inclusiveAliveCount >= 0); emit dataChanged(current); const QMetaObject *parent = m_childParentMap.value(current); // there is no way to detect when a QMetaObject is getting actually destroyed, // so mark them as invalid when there are no objects if that type alive anymore. if (info.inclusiveAliveCount == 0 && !info.isStatic) { info.invalid = true; } current = parent; } } bool MetaObjectRegistry::isKnownMetaObject(const QMetaObject *metaObject) const { return m_childParentMap.contains(metaObject); } const QMetaObject *MetaObjectRegistry::aliveInstance(const QMetaObject *metaObject) const { const auto it = m_aliveInstances.find(metaObject); if (it == m_aliveInstances.end()) return metaObject; // static QMO if (it.value().isEmpty()) return nullptr; return it.value().at(0); } void MetaObjectRegistry::addAliveInstance(QObject *obj, const QMetaObject *canonicalMO) { auto aliveMO = obj->metaObject(); m_dynamicMetaObjectMap.insert(obj, aliveMO); m_canonicalMetaObjectMap.insert(aliveMO, canonicalMO); auto &alivePool = m_aliveInstances[canonicalMO]; auto it = std::lower_bound(alivePool.begin(), alivePool.end(), aliveMO); alivePool.insert(it, aliveMO); } void MetaObjectRegistry::removeAliveInstance(QObject *obj, const QMetaObject *canonicalMO) { auto aliveMO = m_dynamicMetaObjectMap.take(obj); auto &alivePool = m_aliveInstances[canonicalMO]; auto it = std::lower_bound(alivePool.begin(), alivePool.end(), aliveMO); if (it != alivePool.end() && *it == aliveMO) alivePool.erase(it); m_canonicalMetaObjectMap.remove(aliveMO); } const QMetaObject *MetaObjectRegistry::canonicalMetaObject(const QMetaObject *metaObject) const { const auto it = m_canonicalMetaObjectMap.find(metaObject); if (it != m_canonicalMetaObjectMap.end()) return *it; return metaObject; } gammaray-2.11.3/core/metaobjectregistry.h000066400000000000000000000116241413310443000204000ustar00rootroot00000000000000/* metaobjecttreemodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2012-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Kevin Funk Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_METAOBJECTREGISTRY_H #define GAMMARAY_METAOBJECTREGISTRY_H #include #include #include namespace GammaRay { class MetaObjectRegistry : public QObject { Q_OBJECT public: enum MetaObjectData { ClassName, Valid, SelfCount, SelfAliveCount, InclusiveCount, InclusiveAliveCount, }; explicit MetaObjectRegistry(QObject *parent = nullptr); ~MetaObjectRegistry() override; void scanMetaTypes(); QVariant data(const QMetaObject *metaObject, MetaObjectData type) const; bool isValid(const QMetaObject *metaObject) const; bool isStatic(const QMetaObject *metaObject) const; const QMetaObject *aliveInstance(const QMetaObject *metaObject) const; const QMetaObject *parentOf(const QMetaObject *metaObject) const; QVector childrenOf(const QMetaObject *metaObject) const; const QMetaObject *canonicalMetaObject(const QMetaObject *metaObject) const; public slots: void objectAdded(QObject *obj); void objectRemoved(QObject *obj); signals: void beforeMetaObjectAdded(const QMetaObject *metaObject); void afterMetaObjectAdded(const QMetaObject *metaObject); void dataChanged(const QMetaObject *metaObject); private: const QMetaObject *addMetaObject(const QMetaObject *metaObject, bool mergeDynamic = false); bool inheritsQObject(const QMetaObject *metaObject) const; bool isKnownMetaObject(const QMetaObject *metaObject) const; void addAliveInstance(QObject *obj, const QMetaObject *canonicalMO); void removeAliveInstance(QObject *obj, const QMetaObject *canonicalMO); private: QHash m_childParentMap; QHash > m_parentChildMap; struct MetaObjectInfo { MetaObjectInfo() = default; /// @c true if this is a static meta object that can only become invalid by DLL unloading. bool isStatic = false; /// @c true if this is a merged dynamic meta object, as e.g. in use by QML bool isDynamic = false; /** * True if the meta object is suspected invalid. We can't know when one is destroyed, * so we mark this as true when all of the objects with this type are destroyed. */ bool invalid = false; /// Number of objects of a particular meta object type ever created int selfCount = 0; /// Number of instances of a meta object currently alive int selfAliveCount = 0; /** * Number of objects of the exact meta object type * + number of objects of type that inherit from this meta type */ int inclusiveCount = 0; /// Inclusive instance count currently alive int inclusiveAliveCount = 0; /// A copy of QMetaObject::className() QByteArray className; }; QHash m_metaObjectInfoMap; /// canonical meta objects at creation time, so we can correctly decrement instance counts /// after destruction QHash m_metaObjectMap; /// name to canonical QMO map, for merging dynamic meta objects as produced by QML QHash m_metaObjectNameMap; /// alive instances for canonical dynamic meta objects QHash > m_aliveInstances; /// mapping from QObject* to its owned QMetaObject (for dynamic ones only) /// this is needed to clean up m_aliveInstances on deletion QHash m_dynamicMetaObjectMap; /// QMO instance to canonical QMO mapping (for dynamic ones only) QHash m_canonicalMetaObjectMap; }; } #endif // GAMMARAY_METAOBJECTTREEMODEL_H gammaray-2.11.3/core/metaobjectrepository.cpp000066400000000000000000000223011413310443000212740ustar00rootroot00000000000000/* metaobjectrepository.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "metaobjectrepository.h" #include "metaobject.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace GammaRay; namespace GammaRay { class StaticMetaObjectRepository : public MetaObjectRepository { public: StaticMetaObjectRepository() = default; }; } Q_GLOBAL_STATIC(StaticMetaObjectRepository, s_instance) MetaObjectRepository::~MetaObjectRepository() { qDeleteAll(m_metaObjects); } void MetaObjectRepository::initBuiltInTypes() { m_initialized = true; initQObjectTypes(); initIOTypes(); initQEventTypes(); } Q_DECLARE_METATYPE(QThread::Priority) void MetaObjectRepository::initQObjectTypes() { MetaObject *mo = nullptr; MO_ADD_METAOBJECT0(QMetaObject); MO_ADD_PROPERTY_RO(QMetaObject, classInfoCount); MO_ADD_PROPERTY_RO(QMetaObject, classInfoOffset); // MO_ADD_PROPERTY_RO(QMetaObject, className); MO_ADD_PROPERTY_RO(QMetaObject, constructorCount); MO_ADD_PROPERTY_RO(QMetaObject, enumeratorCount); MO_ADD_PROPERTY_RO(QMetaObject, enumeratorOffset); MO_ADD_PROPERTY_RO(QMetaObject, methodCount); MO_ADD_PROPERTY_RO(QMetaObject, methodOffset); MO_ADD_PROPERTY_RO(QMetaObject, propertyCount); MO_ADD_PROPERTY_RO(QMetaObject, propertyOffset); MO_ADD_PROPERTY_RO(QMetaObject, superClass); MO_ADD_METAOBJECT0(QObject); MO_ADD_PROPERTY_RO(QObject, metaObject); MO_ADD_PROPERTY_RO(QObject, parent); MO_ADD_PROPERTY_RO(QObject, signalsBlocked); // TODO setter has non-void return type MO_ADD_PROPERTY_RO(QObject, thread); MO_ADD_METAOBJECT1(QThread, QObject) MO_ADD_PROPERTY_RO(QThread, isFinished); MO_ADD_PROPERTY_RO(QThread, isInterruptionRequested); MO_ADD_PROPERTY_RO(QThread, isRunning); MO_ADD_PROPERTY(QThread, priority, setPriority); MO_ADD_PROPERTY(QThread, stackSize, setStackSize); MO_ADD_METAOBJECT1(QTimer, QObject); MO_ADD_PROPERTY_RO(QTimer, timerId); MO_ADD_METAOBJECT1(QCoreApplication, QObject); MO_ADD_PROPERTY_ST(QCoreApplication, applicationDirPath); MO_ADD_PROPERTY_ST(QCoreApplication, applicationFilePath); MO_ADD_PROPERTY_ST(QCoreApplication, applicationPid); MO_ADD_PROPERTY_ST(QCoreApplication, arguments); MO_ADD_PROPERTY_ST(QCoreApplication, closingDown); MO_ADD_PROPERTY_ST(QCoreApplication, isQuitLockEnabled); MO_ADD_PROPERTY_ST(QCoreApplication, isSetuidAllowed); MO_ADD_PROPERTY_ST(QCoreApplication, libraryPaths); MO_ADD_PROPERTY_ST(QCoreApplication, startingUp); MO_ADD_METAOBJECT1(QAbstractItemModel, QObject); MO_ADD_PROPERTY_RO(QAbstractItemModel, mimeTypes); MO_ADD_PROPERTY_RO(QAbstractItemModel, roleNames); MO_ADD_PROPERTY_RO(QAbstractItemModel, supportedDragActions); MO_ADD_PROPERTY_RO(QAbstractItemModel, supportedDropActions); MO_ADD_METAOBJECT1(QAbstractProxyModel, QAbstractItemModel); MO_ADD_METAOBJECT1(QSortFilterProxyModel, QAbstractProxyModel); MO_ADD_PROPERTY_RO(QSortFilterProxyModel, sortOrder); MO_ADD_METAOBJECT0(QDateTime); MO_ADD_PROPERTY_RO(QDateTime, isDaylightTime); MO_ADD_PROPERTY_RO(QDateTime, isNull); MO_ADD_PROPERTY_RO(QDateTime, isValid); MO_ADD_PROPERTY_RO(QDateTime, offsetFromUtc); MO_ADD_PROPERTY(QDateTime, timeZone, setTimeZone); MO_ADD_METAOBJECT0(QTimeZone); MO_ADD_PROPERTY_RO(QTimeZone, comment); MO_ADD_PROPERTY_RO(QTimeZone, country); MO_ADD_PROPERTY_RO(QTimeZone, hasDaylightTime); MO_ADD_PROPERTY_RO(QTimeZone, hasTransitions); MO_ADD_PROPERTY_RO(QTimeZone, id); MO_ADD_PROPERTY_RO(QTimeZone, isValid); MO_ADD_METAOBJECT0(QEasingCurve); MO_ADD_PROPERTY(QEasingCurve, amplitude, setAmplitude); MO_ADD_PROPERTY(QEasingCurve, overshoot, setOvershoot); MO_ADD_PROPERTY(QEasingCurve, period, setPeriod); MO_ADD_PROPERTY(QEasingCurve, type, setType); } Q_DECLARE_METATYPE(QIODevice::OpenMode) Q_DECLARE_METATYPE(QFileDevice::FileError) Q_DECLARE_METATYPE(QFileDevice::Permissions) void MetaObjectRepository::initIOTypes() { MetaObject *mo = nullptr; MO_ADD_METAOBJECT1(QIODevice, QObject); MO_ADD_PROPERTY_RO(QIODevice, openMode); MO_ADD_PROPERTY(QIODevice, isTextModeEnabled, setTextModeEnabled); MO_ADD_PROPERTY_RO(QIODevice, isOpen); MO_ADD_PROPERTY_RO(QIODevice, isReadable); MO_ADD_PROPERTY_RO(QIODevice, isWritable); MO_ADD_PROPERTY_RO(QIODevice, isSequential); MO_ADD_PROPERTY_RO(QIODevice, pos); MO_ADD_PROPERTY_RO(QIODevice, size); MO_ADD_PROPERTY_RO(QIODevice, atEnd); MO_ADD_PROPERTY_RO(QIODevice, bytesAvailable); MO_ADD_PROPERTY_RO(QIODevice, bytesToWrite); MO_ADD_PROPERTY_RO(QIODevice, canReadLine); MO_ADD_PROPERTY_RO(QIODevice, errorString); // FIXME: QIODevice::readAll() would be nice to have MO_ADD_METAOBJECT1(QFileDevice, QIODevice); MO_ADD_PROPERTY_RO(QFileDevice, error); MO_ADD_PROPERTY_RO(QFileDevice, fileName); MO_ADD_PROPERTY_RO(QFileDevice, handle); MO_ADD_PROPERTY_RO(QFileDevice, permissions); MO_ADD_METAOBJECT1(QFile, QFileDevice); MO_ADD_PROPERTY_RO(QFile, exists); MO_ADD_PROPERTY_RO(QFile, symLinkTarget); MO_ADD_METAOBJECT1(QSaveFile, QFileDevice); } Q_DECLARE_METATYPE(const QObject*) void MetaObjectRepository::initQEventTypes() { MetaObject *mo = nullptr; MO_ADD_METAOBJECT0(QEvent); MO_ADD_PROPERTY_RO(QEvent, isAccepted); MO_ADD_PROPERTY_RO(QEvent, spontaneous); MO_ADD_PROPERTY_RO(QEvent, type); MO_ADD_METAOBJECT1(QTimerEvent, QEvent); MO_ADD_PROPERTY_RO(QTimerEvent, timerId); MO_ADD_METAOBJECT1(QChildEvent, QEvent); MO_ADD_PROPERTY_RO(QChildEvent, child); MO_ADD_PROPERTY_RO(QChildEvent, added); MO_ADD_PROPERTY_RO(QChildEvent, polished); MO_ADD_PROPERTY_RO(QChildEvent, removed); MO_ADD_METAOBJECT1(QDynamicPropertyChangeEvent, QEvent); MO_ADD_PROPERTY_RO(QDynamicPropertyChangeEvent, propertyName); MO_ADD_METAOBJECT1(QDeferredDeleteEvent, QEvent); MO_ADD_PROPERTY_RO(QDeferredDeleteEvent, loopLevel); MO_ADD_METAOBJECT1(QMetaCallEvent, QEvent); MO_ADD_PROPERTY_RO(QMetaCallEvent, id); MO_ADD_PROPERTY_RO(QMetaCallEvent, sender); // problematic because type is const QObject* MO_ADD_PROPERTY_RO(QMetaCallEvent, signalId); } MetaObjectRepository *MetaObjectRepository::instance() { if (!s_instance()->m_initialized) s_instance()->initBuiltInTypes(); return s_instance(); } void MetaObjectRepository::addMetaObject(MetaObject *mo) { Q_ASSERT(!mo->className().isEmpty()); Q_ASSERT(!m_metaObjects.contains(mo->className())); m_metaObjects.insert(mo->className(), mo); int idx = 0; while (auto super = mo->superClass(idx++)) m_derivedTypes[super].push_back(mo); } MetaObject *MetaObjectRepository::metaObject(const QString &typeName) const { QString typeName_ = typeName; typeName_.remove('*'); typeName_.remove('&'); typeName_.remove(QStringLiteral("const ")); typeName_.remove(QStringLiteral(" const")); typeName_.remove(' '); return m_metaObjects.value(typeName_); } MetaObject* MetaObjectRepository::metaObject(const QString& typeName, void *&obj) const { auto mo = metaObject(typeName); return metaObject(mo, obj); } MetaObject* MetaObjectRepository::metaObject(MetaObject *mo, void *&obj) const { if (!mo || !mo->isPolymorphic()) return mo; const auto derivedIt = m_derivedTypes.find(mo); if (derivedIt == m_derivedTypes.end()) return mo; for (auto derivedMo : (*derivedIt).second) { auto childObj = derivedMo->castFrom(obj, mo); if (!childObj) continue; obj = childObj; return metaObject(derivedMo, obj); } return mo; } bool MetaObjectRepository::hasMetaObject(const QString &typeName) const { return m_metaObjects.contains(typeName); } void MetaObjectRepository::clear() { qDeleteAll(m_metaObjects); m_metaObjects.clear(); m_initialized = false; } gammaray-2.11.3/core/metaobjectrepository.h000066400000000000000000000153341413310443000207510ustar00rootroot00000000000000/* metaobjectrepository.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ /** @file metaobjectrepository.h * @brief MetaObject repository macros. */ #ifndef GAMMARAY_METAOBJECTREPOSITORY_H #define GAMMARAY_METAOBJECTREPOSITORY_H #include "gammaray_core_export.h" #include #include #include QT_BEGIN_NAMESPACE class QString; QT_END_NAMESPACE namespace GammaRay { class MetaObject; /** * @brief MetaObject repository. * * Repository of compile-time introspection information for stuff * not covered by the Qt meta object system. */ class GAMMARAY_CORE_EXPORT MetaObjectRepository { public: ~MetaObjectRepository(); /** Singleton accessor. */ static MetaObjectRepository *instance(); /** * Adds object type information to the repository. */ void addMetaObject(MetaObject *mo); /** * Returns the introspection information for the type with the given name. */ MetaObject *metaObject(const QString &typeName) const; /** * Returns the introspection information for the given object instance. * This behaves as the above function for non-polymorphic types, for polymorphic * types it tries to find the most specific derived type for the given instance. * @param typeName The name of the (base) type of @p obj. * @param obj The object pointer is modified if necessary for the corresponding type. * This is necessary to support multiple inheritance. */ MetaObject *metaObject(const QString &typeName, void *&obj) const; /** Same as the above method, just using an already looked-up MetaObject. */ MetaObject *metaObject(MetaObject *mo, void *&obj) const; /** * Returns whether a meta object is known for the given type name. */ bool hasMetaObject(const QString &typeName) const; /*! * Clears the content of the meta object repository. * Call this when removing the probe from a target. * \internal */ void clear(); protected: MetaObjectRepository() = default; private: Q_DISABLE_COPY(MetaObjectRepository) void initBuiltInTypes(); void initQObjectTypes(); void initIOTypes(); void initQEventTypes(); private: QHash m_metaObjects; std::unordered_map > m_derivedTypes; bool m_initialized = false; }; } ///@cond internal #define MO_ADD_BASECLASS(Base) \ Q_ASSERT(GammaRay::MetaObjectRepository::instance()->hasMetaObject(QStringLiteral(#Base))); \ mo->addBaseClass(GammaRay::MetaObjectRepository::instance()->metaObject(QStringLiteral(#Base))); ///@endcond /** Register @p Class with the MetaObjectRepository. * Use this if @p Class has no base class. */ #define MO_ADD_METAOBJECT0(Class) \ mo = new GammaRay::MetaObjectImpl; \ mo->setClassName(QStringLiteral(#Class)); \ GammaRay::MetaObjectRepository::instance()->addMetaObject(mo); /** Register @p Class with the MetaObjectRepository. * Use this if @p Class has one base class. */ #define MO_ADD_METAOBJECT1(Class, Base1) \ mo = new GammaRay::MetaObjectImpl; \ mo->setClassName(QStringLiteral(#Class)); \ MO_ADD_BASECLASS(Base1) \ GammaRay::MetaObjectRepository::instance()->addMetaObject(mo); /** Register @p Class with the MetaObjectRepository. * Use this if @p Class has two base classes. */ #define MO_ADD_METAOBJECT2(Class, Base1, Base2) \ mo = new GammaRay::MetaObjectImpl; \ mo->setClassName(QStringLiteral(#Class)); \ MO_ADD_BASECLASS(Base1) \ MO_ADD_BASECLASS(Base2) \ GammaRay::MetaObjectRepository::instance()->addMetaObject(mo); /** Register a read/write property for class @p Class. */ #define MO_ADD_PROPERTY(Class, Getter, Setter) \ mo->addProperty(GammaRay::MetaPropertyFactory::makeProperty(#Getter, &Class::Getter, &Class::Setter)); /** Register a read-only property for class @p Class. */ #define MO_ADD_PROPERTY_RO(Class, Getter) \ mo->addProperty(GammaRay::MetaPropertyFactory::makeProperty(#Getter, &Class::Getter)); /** Register a non-const read-only property for class @p Class. */ #define MO_ADD_PROPERTY_NC(Class, Getter) \ mo->addProperty(GammaRay::MetaPropertyFactory::makePropertyNonConst(#Getter, &Class::Getter)); #if !defined(Q_CC_MSVC) || _MSC_VER >= 1900 //krazy:exclude=cpp /** Register a lamda property getter for class @p Class. */ #define MO_ADD_PROPERTY_LD(Class, Name, Func) \ { \ const auto ld = Func; \ mo->addProperty(GammaRay::MetaPropertyFactory::makeProperty()))>(#Name, ld)); \ } /** Register a read/write property for class @p Class. * Use this for overloaded getters or setters that would confuse older MSVC versions. */ #define MO_ADD_PROPERTY_O2(Class, Getter, Setter) \ mo->addProperty(GammaRay::MetaPropertyFactory::makeProperty(#Getter, &Class::Getter, &Class::Setter)); /** Register a read-only property for class @p Class. * Use this for overloaded getters or setters that would confuse older MSVC versions. */ #define MO_ADD_PROPERTY_O1(Class, Getter) \ mo->addProperty(GammaRay::MetaPropertyFactory::makeProperty(#Getter, &Class::Getter)); #else #define MO_ADD_PROPERTY_LD(Class, Name, Func) #define MO_ADD_PROPERTY_O2(Class, Getter, Setter) #define MO_ADD_PROPERTY_O1(Class, Getter) #endif /** Register a static property for class @p Class. */ #define MO_ADD_PROPERTY_ST(Class, Getter) \ mo->addProperty(GammaRay::MetaPropertyFactory::makeProperty(#Getter, &Class::Getter)); /** Register a member property for class @p Class. */ #define MO_ADD_PROPERTY_MEM(Class, Member) \ mo->addProperty(GammaRay::MetaPropertyFactory::makeProperty(#Member, &Class::Member)); #endif // GAMMARAY_METAOBJECTREPOSITORY_H gammaray-2.11.3/core/metaproperty.cpp000066400000000000000000000034021413310443000175530ustar00rootroot00000000000000/* metaproperty.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "metaproperty.h" #include "metaobject.h" #include using namespace GammaRay; MetaProperty::MetaProperty(const char *name) : m_class(nullptr) , m_name(name) { } MetaProperty::~MetaProperty() = default; const char *MetaProperty::name() const { return m_name; } void MetaProperty::setValue(void *object, const QVariant &value) { Q_UNUSED(object); Q_UNUSED(value); Q_ASSERT(isReadOnly()); // otherwise sub-class should have implement this... } MetaObject *MetaProperty::metaObject() const { Q_ASSERT(m_class); return m_class; } void MetaProperty::setMetaObject(MetaObject *om) { m_class = om; } gammaray-2.11.3/core/metaproperty.h000066400000000000000000000214041413310443000172220ustar00rootroot00000000000000/* metaproperty.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_METAPROPERTY_H #define GAMMARAY_METAPROPERTY_H #include "gammaray_core_export.h" #include #include namespace GammaRay { class MetaObject; /*! Introspectable adaptor to non-QObject properties. */ class GAMMARAY_CORE_EXPORT MetaProperty { public: /*! Create a new MetaProperty instance for a property named @p name. */ explicit MetaProperty(const char *name); virtual ~MetaProperty(); /// User-readable name of that property const char *name() const; /// Current value of the property for object @p object. virtual QVariant value(void *object) const = 0; /// Returns @c true if this property is read-only. virtual bool isReadOnly() const = 0; /// Allows changing the property value, assuming it's not read-only, for the instance @p object. virtual void setValue(void *object, const QVariant &value); /// Returns the name of the data type of this property. virtual const char *typeName() const = 0; /// Returns the class this property belongs to. MetaObject *metaObject() const; private: Q_DISABLE_COPY(MetaProperty) friend class MetaObject; void setMetaObject(MetaObject *om); MetaObject *m_class; const char *m_name; }; ///@cond internal namespace detail { template struct strip_const_ref { typedef T type; }; template struct strip_const_ref { typedef T type; }; template struct add_const_ref { typedef const typename strip_const_ref::type & type; }; } /*! Template-ed implementation of MetaProperty for member properties. */ template class MetaPropertyImpl : public MetaProperty { private: typedef typename detail::strip_const_ref::type ValueType; public: inline MetaPropertyImpl( const char *name, GetterSignature getter, void(Class::*setter)(SetterArgType) = nullptr) : MetaProperty(name) , m_getter(getter) , m_setter(setter) { } bool isReadOnly() const override { return m_setter == nullptr; } QVariant value(void *object) const override { Q_ASSERT(object); Q_ASSERT(m_getter); const ValueType v = (static_cast(object)->*(m_getter))(); return QVariant::fromValue(v); } void setValue(void *object, const QVariant &value) override { if (isReadOnly()) return; Q_ASSERT(object); Q_ASSERT(m_setter); (static_cast(object)->*(m_setter))(value.value()); } const char *typeName() const override { return QMetaType::typeName(qMetaTypeId()); } private: GetterSignature m_getter; void (Class::*m_setter)(SetterArgType); }; /*! Template-ed implementation of MetaProperty for static properties. */ template class MetaStaticPropertyImpl : public MetaProperty { private: typedef typename detail::strip_const_ref::type ValueType; public: inline MetaStaticPropertyImpl(const char *name, GetterReturnType(*getter)()) : MetaProperty(name) , m_getter(getter) { } bool isReadOnly() const override { return true; } QVariant value(void *object) const override { Q_UNUSED(object); Q_ASSERT(m_getter); const ValueType v = m_getter(); return QVariant::fromValue(v); } const char *typeName() const override { return QMetaType::typeName(qMetaTypeId()); } private: GetterReturnType (*m_getter)(); }; /*! Template-ed implementation of MetaProperty for member variable "properties". */ template class MetaMemberPropertyImpl : public MetaProperty { public: inline MetaMemberPropertyImpl(const char *name, ValueType Class::*member) : MetaProperty(name) , m_member(member) { } bool isReadOnly() const override { return true; // we could technically implement write access too, if needed... } QVariant value(void *object) const override { Q_ASSERT(object); Q_ASSERT(m_member); return QVariant::fromValue(reinterpret_cast(object)->*m_member); } const char *typeName() const override { return QMetaType::typeName(qMetaTypeId()); } private: ValueType Class::*m_member; }; /*! Lambda "property" implementation of MetaProperty. */ template class MetaLambdaPropertyImpl : public MetaProperty { public: inline explicit MetaLambdaPropertyImpl(const char *name, const std::function &func) : MetaProperty(name) , m_func(func) { } bool isReadOnly() const override { return true; // we could extend this to setters too, eventually } QVariant value(void *object) const override { return QVariant::fromValue(m_func(reinterpret_cast(object))); } const char *typeName() const override { return QMetaType::typeName(qMetaTypeId()); } private: const std::function m_func; }; /*! Template argument deduction factory methods for the MetaXPropertyImpl classes. */ namespace MetaPropertyFactory { // explicitly handle value and const ref setters, to deal with overloaded setters for arbitrary types template inline MetaProperty* makeProperty(const char *name, GetterReturnType(Class::*getter)() const, void(Class::*setter)(typename detail::strip_const_ref::type)) { return new MetaPropertyImpl::type>(name, getter, setter); } template inline MetaProperty* makeProperty(const char *name, GetterReturnType(Class::*getter)() const, void(Class::*setter)(typename detail::add_const_ref::type)) { return new MetaPropertyImpl::type>(name, getter, setter); } // can't merge with the above function, since MSVC2010 can't do default template arguments for template functions... template inline MetaProperty* makeProperty(const char *name, GetterReturnType(Class::*getter)() const) { return new MetaPropertyImpl(name, getter, nullptr); } // non-const getters... template inline MetaProperty* makePropertyNonConst(const char *name, GetterReturnType(Class::*getter)()) { return new MetaPropertyImpl(name, getter, nullptr); } template inline MetaProperty* makeProperty(const char *name, GetterReturnType(*getter)()) { return new MetaStaticPropertyImpl(name, getter); } template inline MetaProperty* makeProperty(const char *name, ValueType Class::*member) { return new MetaMemberPropertyImpl(name, member); } // lamda getters template inline MetaProperty* makeProperty(const char *name, const std::function &func) { return new MetaLambdaPropertyImpl(name, func); } } ///@endcond } #endif gammaray-2.11.3/core/metapropertyadaptor.cpp000066400000000000000000000065511413310443000211360ustar00rootroot00000000000000/* metapropertyadaptor.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "metapropertyadaptor.h" #include "objectinstance.h" #include "metaobjectrepository.h" #include "metaobject.h" #include "propertydata.h" #include using namespace GammaRay; MetaPropertyAdaptor::MetaPropertyAdaptor(QObject *parent) : PropertyAdaptor(parent) , m_metaObj(nullptr) , m_obj(nullptr) { } MetaPropertyAdaptor::~MetaPropertyAdaptor() = default; void MetaPropertyAdaptor::doSetObject(const ObjectInstance &oi) { Q_ASSERT(m_metaObj == nullptr); Q_ASSERT(m_obj == nullptr); switch (oi.type()) { case ObjectInstance::Object: case ObjectInstance::Value: m_obj = oi.object(); m_metaObj = MetaObjectRepository::instance()->metaObject(oi.typeName(), m_obj); break; case ObjectInstance::QtObject: case ObjectInstance::QtGadgetPointer: case ObjectInstance::QtGadgetValue: { const QMetaObject *mo = oi.metaObject(); while (mo && !m_metaObj) { m_metaObj = MetaObjectRepository::instance()->metaObject(mo->className()); mo = mo->superClass(); } if (m_metaObj) m_obj = oi.object(); break; } default: break; } } int MetaPropertyAdaptor::count() const { if (!m_metaObj || !object().isValid()) return 0; return m_metaObj->propertyCount(); } PropertyData MetaPropertyAdaptor::propertyData(int index) const { Q_ASSERT(m_metaObj); PropertyData data; if (!object().isValid()) return data; const auto property = m_metaObj->propertyAt(index); data.setName(property->name()); data.setTypeName(property->typeName()); data.setClassName(property->metaObject()->className()); data.setAccessFlags(property->isReadOnly() ? PropertyData::Readable : PropertyData::Writable); if (m_obj) { const auto value = property->value(m_metaObj->castForPropertyAt(m_obj, index)); data.setValue(value); } return data; } void MetaPropertyAdaptor::writeProperty(int index, const QVariant &value) { if (!object().isValid()) return; Q_ASSERT(m_metaObj && m_obj); const auto prop = m_metaObj->propertyAt(index); prop->setValue(m_metaObj->castForPropertyAt(m_obj, index), value); emit propertyChanged(index, index); } gammaray-2.11.3/core/metapropertyadaptor.h000066400000000000000000000035061413310443000206000ustar00rootroot00000000000000/* metapropertyadaptor.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_METAPROPERTYADAPTOR_H #define GAMMARAY_METAPROPERTYADAPTOR_H #include "propertyadaptor.h" namespace GammaRay { class MetaObject; /** Property access for GammaRay meta-type based property-access. */ class MetaPropertyAdaptor : public PropertyAdaptor { Q_OBJECT public: explicit MetaPropertyAdaptor(QObject *parent = nullptr); ~MetaPropertyAdaptor() override; int count() const override; PropertyData propertyData(int index) const override; void writeProperty(int index, const QVariant &value) override; protected: void doSetObject(const ObjectInstance &oi) override; private: MetaObject *m_metaObj; void *m_obj; }; } #endif // GAMMARAY_METAPROPERTYADAPTOR_H gammaray-2.11.3/core/methodargumentmodel.cpp000066400000000000000000000100711413310443000210640ustar00rootroot00000000000000/* methodargumentmodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "methodargumentmodel.h" using namespace GammaRay; MethodArgumentModel::MethodArgumentModel(QObject *parent) : QAbstractTableModel(parent) { } void MethodArgumentModel::setMethod(const QMetaMethod &method) { beginResetModel(); m_method = method; m_arguments.clear(); m_arguments.resize(method.parameterTypes().size()); for (int i = 0; i < m_arguments.size(); ++i) { const QByteArray typeName = method.parameterTypes().at(i); const QVariant::Type variantType = QVariant::nameToType(typeName); m_arguments[i] = QVariant(variantType); } endResetModel(); } QVariant MethodArgumentModel::data(const QModelIndex &index, int role) const { if (m_method.methodSignature().isEmpty() || m_arguments.isEmpty() || index.row() < 0 || index.row() >= m_arguments.size()) return QVariant(); if (role == Qt::DisplayRole || role == Qt::EditRole) { const QVariant value = m_arguments.at(index.row()); const QByteArray parameterName = m_method.parameterNames().at(index.row()); const QByteArray parameterType = m_method.parameterTypes().at(index.row()); switch (index.column()) { case 0: if (parameterName.isEmpty()) return tr(" (%1)").arg(QString::fromLatin1(parameterType)); return parameterName; case 1: return value; case 2: return parameterType; } } return QVariant(); } int MethodArgumentModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); return 3; } int MethodArgumentModel::rowCount(const QModelIndex &parent) const { if (parent.isValid()) return 0; return m_arguments.size(); } bool MethodArgumentModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (index.row() >= 0 && index.row() < m_arguments.size() && role == Qt::EditRole) { m_arguments[index.row()] = value; emit dataChanged(index, index); return true; } return QAbstractItemModel::setData(index, value, role); } QVariant MethodArgumentModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { switch (section) { case 0: return tr("Argument"); case 1: return tr("Value"); case 2: return tr("Type"); } } return QAbstractItemModel::headerData(section, orientation, role); } Qt::ItemFlags MethodArgumentModel::flags(const QModelIndex &index) const { const Qt::ItemFlags flags = QAbstractTableModel::flags(index); if (index.column() == 1) return flags | Qt::ItemIsEditable; return flags; } QVector MethodArgumentModel::arguments() const { QVector args(10); for (int i = 0; i < rowCount(); ++i) args[i] = MethodArgument(m_arguments.at(i)); return args; } gammaray-2.11.3/core/methodargumentmodel.h000066400000000000000000000043341413310443000205360ustar00rootroot00000000000000/* methodargumentmodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_METHODARGUMENTMODEL_H #define GAMMARAY_METHODARGUMENTMODEL_H #include #include #include #include namespace GammaRay { class MethodArgumentModel : public QAbstractTableModel { Q_OBJECT public: explicit MethodArgumentModel(QObject *parent = nullptr); void setMethod(const QMetaMethod &method); QVector arguments() const; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; Qt::ItemFlags flags(const QModelIndex &index) const override; private: QMetaMethod m_method; QVector m_arguments; }; } #endif // GAMMARAY_METHODARGUMENTMODEL_H gammaray-2.11.3/core/multisignalmapper.cpp000066400000000000000000000065561413310443000205720ustar00rootroot00000000000000/* multisignalmapper.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "multisignalmapper.h" #include #include #include #include namespace GammaRay { class MultiSignalMapperPrivate : public QObject { public: explicit MultiSignalMapperPrivate(MultiSignalMapper *parent) : QObject(parent) , q(parent) {} ~MultiSignalMapperPrivate() override = default; int qt_metacall(QMetaObject::Call call, int methodId, void **args) override { methodId = QObject::qt_metacall(call, methodId, args); if (methodId < 0) return methodId; if (call == QMetaObject::InvokeMetaMethod) { Q_ASSERT(sender()); const QVector v = convertArguments(sender(), methodId, args); emit q->signalEmitted(sender(), methodId, v); return -1; // indicates we handled the call } return methodId; } QVector convertArguments(QObject *sender, int signalIndex, void **args) { Q_ASSERT(sender); Q_ASSERT(signalIndex >= 0); const QMetaMethod signal = sender->metaObject()->method(signalIndex); Q_ASSERT(signal.methodType() == QMetaMethod::Signal); QVector v; const QList paramTypes = signal.parameterTypes(); for (int i = 0; i < paramTypes.size(); ++i) { int type = QMetaType::type(paramTypes[i]); if (type == QMetaType::Void || type == QMetaType::UnknownType) { qWarning() << Q_FUNC_INFO << "unknown metatype for signal argument type" << paramTypes[i]; continue; } v.push_back(QVariant(type, args[i + 1])); } return v; } private: MultiSignalMapper *q; }; } using namespace GammaRay; MultiSignalMapper::MultiSignalMapper(QObject *parent) : QObject(parent) , d(new MultiSignalMapperPrivate(this)) { } MultiSignalMapper::~MultiSignalMapper() = default; void MultiSignalMapper::connectToSignal(QObject *sender, const QMetaMethod &signal) { QMetaObject::connect(sender, signal.methodIndex(), d, QObject::metaObject()->methodCount() + signal.methodIndex(), Qt::AutoConnection | Qt::UniqueConnection, nullptr); } gammaray-2.11.3/core/multisignalmapper.h000066400000000000000000000035141413310443000202260ustar00rootroot00000000000000/* multisignalmapper.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_MULTISIGNALMAPPER_H #define GAMMARAY_MULTISIGNALMAPPER_H #include #include namespace GammaRay { class MultiSignalMapperPrivate; /** * A signal mapper that can deal with multiple signals from the same sender. */ class MultiSignalMapper : public QObject { Q_OBJECT public: explicit MultiSignalMapper(QObject *parent = nullptr); ~MultiSignalMapper() override; void connectToSignal(QObject *sender, const QMetaMethod &signal); signals: void signalEmitted(QObject *sender, int signalIndex, const QVector &arguments); private: friend class MultiSignalMapperPrivate; MultiSignalMapperPrivate * const d; }; } #endif // GAMMARAY_MULTISIGNALMAPPER_H gammaray-2.11.3/core/objectclassinfomodel.cpp000066400000000000000000000041501413310443000212120ustar00rootroot00000000000000/* objectclassinfomodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "objectclassinfomodel.h" using namespace GammaRay; ObjectClassInfoModel::ObjectClassInfoModel(QObject *parent) : MetaObjectModel(parent) { } QVariant ObjectClassInfoModel::metaData(const QModelIndex &index, const QMetaClassInfo &classInfo, int role) const { if (role == Qt::DisplayRole) { if (index.column() == 0) return classInfo.name(); if (index.column() == 1) return classInfo.value(); } return QVariant(); } int ObjectClassInfoModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); return 3; } QString GammaRay::ObjectClassInfoModel::columnHeader(int index) const { switch (index) { case 0: return tr("Name"); case 1: return tr("Value"); } return QString(); } gammaray-2.11.3/core/objectclassinfomodel.h000066400000000000000000000037271413310443000206700ustar00rootroot00000000000000/* objectclassinfomodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_OBJECTCLASSINFOMODEL_H #define GAMMARAY_OBJECTCLASSINFOMODEL_H #include "metaobjectmodel.h" #include namespace GammaRay { class ObjectClassInfoModel : public MetaObjectModel { Q_OBJECT public: explicit ObjectClassInfoModel(QObject *parent = nullptr); int columnCount(const QModelIndex &parent = QModelIndex()) const override; QVariant metaData(const QModelIndex &index, const QMetaClassInfo &classInfo, int role) const override; QString columnHeader(int index) const override; }; } #endif // GAMMARAY_OBJECTCLASSINFOMODEL_H gammaray-2.11.3/core/objectdataprovider.cpp000066400000000000000000000063371413310443000207050ustar00rootroot00000000000000/* objectdataprovider.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "objectdataprovider.h" #include "probe.h" #include #include #include namespace GammaRay { AbstractObjectDataProvider::AbstractObjectDataProvider() = default; AbstractObjectDataProvider::~AbstractObjectDataProvider() = default; Q_GLOBAL_STATIC(QVector, s_providers) void ObjectDataProvider::registerProvider(AbstractObjectDataProvider *provider) { if (!s_providers()->contains(provider)) s_providers()->push_back(provider); } QString ObjectDataProvider::name(const QObject *obj) { if (!obj) return QStringLiteral("0x0"); auto name = obj->objectName(); if (!name.isEmpty()) return name; foreach (auto provider, *s_providers()) { name = provider->name(obj); if (!name.isEmpty()) return name; } return name; } QString ObjectDataProvider::typeName(QObject *obj) { if (!obj) return QString(); foreach (auto provider, *s_providers()) { const auto name = provider->typeName(obj); if (!name.isEmpty()) return name; } return obj->metaObject()->className(); } QString ObjectDataProvider::shortTypeName(QObject *obj) { if (!obj) return QString(); foreach (auto provider, *s_providers()) { const auto name = provider->shortTypeName(obj); if (!name.isEmpty()) return name; } return obj->metaObject()->className(); } SourceLocation ObjectDataProvider::creationLocation(QObject *obj) { SourceLocation loc; if (!obj) return loc; foreach (auto provider, *s_providers()) { loc = provider->creationLocation(obj); if (loc.isValid()) return loc; } loc = Probe::instance()->objectCreationSourceLocation(obj); return loc; } SourceLocation ObjectDataProvider::declarationLocation(QObject *obj) { SourceLocation loc; if (!obj) return loc; foreach (auto provider, *s_providers()) { loc = provider->declarationLocation(obj); if (loc.isValid()) return loc; } return loc; } } gammaray-2.11.3/core/objectdataprovider.h000066400000000000000000000065161413310443000203510ustar00rootroot00000000000000/* objectdataprovider.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_OBJECTDATAPROVIDER_H #define GAMMARAY_OBJECTDATAPROVIDER_H #include "gammaray_core_export.h" #include QT_BEGIN_NAMESPACE class QObject; class QString; QT_END_NAMESPACE namespace GammaRay { class SourceLocation; /*! Base class for custom object data providers. * Inherit from this and register at ObjectDataProvider, to support basic QObject information * retrieval from dynamic language bindings, such as QML. */ class GAMMARAY_CORE_EXPORT AbstractObjectDataProvider { public: AbstractObjectDataProvider(); virtual ~AbstractObjectDataProvider(); /*! Returns a name or identifier for @p obj. */ virtual QString name(const QObject *obj) const = 0; /*! Returns the full name of the type of @p obj. */ virtual QString typeName(QObject *obj) const = 0; /*! Returns a shortened type name (e.g. excluding namespaces) for @p obj. */ virtual QString shortTypeName(QObject *obj) const = 0; /*! Returns the source location where @p obj has been created. */ virtual SourceLocation creationLocation(QObject *obj) const = 0; /*! Returns the source location where the type of @p obj has been declared. */ virtual SourceLocation declarationLocation(QObject *obj) const = 0; private: Q_DISABLE_COPY(AbstractObjectDataProvider) }; /*! Retrieve basic information about QObject instances. */ namespace ObjectDataProvider { /*! Register an additional object data provider. */ GAMMARAY_CORE_EXPORT void registerProvider(AbstractObjectDataProvider *provider); /*! Returns the object name, considering possibly available information from * dynamic language runtimes, such as QML ids. */ GAMMARAY_CORE_EXPORT QString name(const QObject *obj); /*! Returns the type name of @p obj. */ GAMMARAY_CORE_EXPORT QString typeName(QObject *obj); /*! Returns the short type name of @p obj. */ GAMMARAY_CORE_EXPORT QString shortTypeName(QObject *obj); /*! Returns the source location where this object was created, if known. */ GAMMARAY_CORE_EXPORT SourceLocation creationLocation(QObject *obj); /*! Returns the source location where the type of this object was declared, if known. */ GAMMARAY_CORE_EXPORT SourceLocation declarationLocation(QObject *obj); } } #endif // GAMMARAY_OBJECTDATAPROVIDER_H gammaray-2.11.3/core/objectenummodel.cpp000066400000000000000000000066601413310443000202050ustar00rootroot00000000000000/* objectenummodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "objectenummodel.h" #include #include using namespace GammaRay; typedef MetaObjectModel SuperClass; GammaRay::ObjectEnumModel::ObjectEnumModel(QObject *parent) : SuperClass(parent) { } int ObjectEnumModel::rowCount(const QModelIndex &parent) const { if (!parent.isValid()) return SuperClass::rowCount(parent); if (parent.parent().isValid()) return 0; const QMetaEnum e = m_metaObject->enumerator(parent.row()); return e.keyCount(); } int GammaRay::ObjectEnumModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); return 3; } QVariant ObjectEnumModel::data(const QModelIndex &index, int role) const { if (!index.parent().isValid()) return SuperClass::data(index, role); if (role == Qt::DisplayRole) { const QMetaEnum e = m_metaObject->enumerator(index.parent().row()); if (index.column() == 0) return e.key(index.row()); if (index.column() == 1) return e.value(index.row()); } return QVariant(); } QVariant ObjectEnumModel::metaData(const QModelIndex &index, const QMetaEnum &enumerator, int role) const { if (role == Qt::DisplayRole) { if (index.column() == 0) return QString::fromLatin1(enumerator.name()); if (index.column() == 1) return tr("%n element(s)", "", enumerator.keyCount()); } return QVariant(); } QString ObjectEnumModel::columnHeader(int index) const { switch (index) { case 0: return tr("Name"); case 1: return tr("Value"); } return QString(); } QModelIndex GammaRay::ObjectEnumModel::index(int row, int column, const QModelIndex &parent) const { if (!parent.isValid()) return SuperClass::index(row, column, parent); return createIndex(row, column, parent.row()); } QModelIndex GammaRay::ObjectEnumModel::parent(const QModelIndex &child) const { // note: Qt4 doesn't have qintptr if (static_cast(child.internalId()) == -1) return SuperClass::parent(child); return SuperClass::index(child.internalId(), 0, QModelIndex()); } gammaray-2.11.3/core/objectenummodel.h000066400000000000000000000042751413310443000176520ustar00rootroot00000000000000/* objectenummodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_OBJECTENUMMODEL_H #define GAMMARAY_OBJECTENUMMODEL_H #include "metaobjectmodel.h" namespace GammaRay { class ObjectEnumModel : public MetaObjectModel { Q_OBJECT public: explicit ObjectEnumModel(QObject *parent = nullptr); int rowCount(const QModelIndex &parent = QModelIndex()) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; QString columnHeader(int index) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; QVariant metaData(const QModelIndex &index, const QMetaEnum &enumerator, int role) const override; QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; QModelIndex parent(const QModelIndex &child) const override; }; } #endif // GAMMARAY_OBJECTENUMMODEL_H gammaray-2.11.3/core/objectinstance.cpp000066400000000000000000000142611413310443000200200ustar00rootroot00000000000000/* objectinstance.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "objectinstance.h" #include "metaobjectrepository.h" #include "metaobject.h" #include "util.h" using namespace GammaRay; ObjectInstance::ObjectInstance(QObject *obj) : m_qtObj(obj) , m_type(QtObject) { m_metaObj = obj ? obj->metaObject() : nullptr; } ObjectInstance::ObjectInstance(void *obj, const QMetaObject *metaObj) : m_obj(obj) , m_metaObj(metaObj) , m_type(obj ? QtGadgetPointer : QtMetaObject) { } ObjectInstance::ObjectInstance(void *obj, const char *typeName) : m_obj(obj) , m_typeName(typeName) , m_type(Object) { } ObjectInstance::ObjectInstance(const QVariant &value) : m_type(QtVariant) { m_variant = value; if (value.canConvert()) { m_qtObj = value.value(); if (m_qtObj) { m_metaObj = m_qtObj->metaObject(); m_type = QtObject; } } else { if (QMetaType::typeFlags(value.userType()) & QMetaType::IsGadget) { m_metaObj = QMetaType::metaObjectForType(value.userType()); if (m_metaObj) m_type = QtGadgetValue; } else { unpackVariant(); } } } ObjectInstance::ObjectInstance(const ObjectInstance &other) { copy(other); } ObjectInstance &ObjectInstance::operator=(const ObjectInstance &other) { copy(other); return *this; } bool ObjectInstance::operator==(const ObjectInstance &rhs) const { if (type() != rhs.type()) return false; switch (type()) { case Invalid: return false; case QtObject: case QtGadgetPointer: case Object: return object() == rhs.object(); case QtMetaObject: return metaObject() == rhs.metaObject(); case Value: case QtVariant: case QtGadgetValue: return variant() == rhs.variant(); } Q_ASSERT(false); return false; } ObjectInstance::Type ObjectInstance::type() const { return m_type; } bool ObjectInstance::isValueType() const { return m_type == Value || m_type == QtGadgetValue; // ### || m_type == QtVariant; ?? } QObject *ObjectInstance::qtObject() const { return m_qtObj; } void *ObjectInstance::object() const { Q_ASSERT(m_type == QtObject || m_type == QtGadgetPointer || m_type == QtGadgetValue || m_type == Object || m_type == Value); switch (m_type) { case QtObject: return m_qtObj; case QtGadgetPointer: case QtGadgetValue: return m_obj ? m_obj : const_cast(m_variant.constData()); default: return m_obj; } Q_ASSERT(false); return nullptr; } const QVariant &ObjectInstance::variant() const { Q_ASSERT(m_type == QtVariant || isValueType()); return m_variant; } const QMetaObject *ObjectInstance::metaObject() const { return m_metaObj; } QByteArray ObjectInstance::typeName() const { if (m_metaObj) return m_metaObj->className(); if (m_variant.isValid() && m_typeName.isEmpty()) return m_variant.typeName(); return m_typeName; } bool ObjectInstance::isValid() const { switch (m_type) { case Invalid: return false; case QtObject: return m_qtObj; case QtMetaObject: return m_metaObj; default: break; } return true; } void ObjectInstance::copy(const ObjectInstance &other) { m_obj = other.m_obj; m_qtObj = other.m_qtObj.data(); m_variant = other.m_variant; m_metaObj = other.m_metaObj; m_typeName = other.m_typeName; m_type = other.m_type; if (m_type == Value || m_type == QtGadgetPointer) unpackVariant(); // pointer changes when copying the variant } void ObjectInstance::unpackVariant() { const auto mo = MetaObjectRepository::instance()->metaObject(m_variant.typeName()); if (mo && strstr(m_variant.typeName(), "*") != nullptr) { // pointer types QMetaType::construct(m_variant.userType(), &m_obj, m_variant.constData()); if (!Util::isNullish(m_obj)) { m_type = Object; m_typeName = m_variant.typeName(); } } else if (mo) { // value types m_obj = const_cast(m_variant.constData()); m_type = Value; m_typeName = m_variant.typeName(); } if (!m_variant.isNull() && strstr(m_variant.typeName(), "*") != nullptr) { // pointer to gadget QByteArray normalizedTypeName = m_variant.typeName(); //krazy:cond=doublequote_chars normalizedTypeName.replace('*', ""); normalizedTypeName.replace('&', ""); normalizedTypeName.replace("const ", ""); normalizedTypeName.replace(" const", ""); normalizedTypeName.replace(' ', ""); //krazy:endcond=doublequote_chars const auto typeId = QMetaType::type(normalizedTypeName); if (typeId != QMetaType::UnknownType && (QMetaType::typeFlags(typeId) & QMetaType::IsGadget)) { QMetaType::construct(m_variant.userType(), &m_obj, m_variant.constData()); m_metaObj = QMetaType::metaObjectForType(typeId); if (m_obj && m_metaObj) { m_type = QtGadgetPointer; m_typeName = m_variant.typeName(); } } } } gammaray-2.11.3/core/objectinstance.h000066400000000000000000000064341413310443000174700ustar00rootroot00000000000000/* objectinstance.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_OBJECTINSTANCE_H #define GAMMARAY_OBJECTINSTANCE_H #include "gammaray_core_export.h" #include #include #include QT_BEGIN_NAMESPACE class QObject; struct QMetaObject; QT_END_NAMESPACE namespace GammaRay { /** Represents some form of object the property adaptor/model code can handle. */ class GAMMARAY_CORE_EXPORT ObjectInstance { public: enum Type { Invalid, QtObject, QtMetaObject, QtGadgetPointer, ///< a pointer to a gadget QtVariant, Object, Value, QtGadgetValue ///< a gadget value type stored in the QVariant }; ObjectInstance() = default; ObjectInstance(QObject *obj); // krazy:exclude=explicit /// use this for Q_GADGETs ObjectInstance(void *obj, const QMetaObject *metaObj); /// use for things that only exist as GammaRay meta objects ObjectInstance(void *obj, const char *typeName); ObjectInstance(const QVariant &value); // krazy:exclude=explicit ObjectInstance(const ObjectInstance &other); ObjectInstance &operator=(const ObjectInstance &other); bool operator==(const ObjectInstance &rhs) const; Type type() const; /**! * Returns @c true if this instance holds a value type. * This is important to treat differently when writing to it, as this will be a copy * of the value you want to change. */ bool isValueType() const; /// only valid for QtObject QObject *qtObject() const; /// only valid for QtObject, QtGadget and Object void *object() const; /// only valid for QtVariant const QVariant &variant() const; /// only valid for QtObject and QtGadget const QMetaObject *metaObject() const; /// only valid for [Qt]Object and QtGadget QByteArray typeName() const; /// Returns @c false if this instance is known to be invalid. bool isValid() const; private: void copy(const ObjectInstance &other); void unpackVariant(); void *m_obj = nullptr; QPointer m_qtObj; QVariant m_variant; const QMetaObject *m_metaObj = nullptr; QByteArray m_typeName; Type m_type = Invalid; }; } #endif // GAMMARAY_OBJECTINSTANCE_H gammaray-2.11.3/core/objectlistmodel.cpp000066400000000000000000000072741413310443000202160ustar00rootroot00000000000000/* objectlistmodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "objectlistmodel.h" #include "probe.h" #include #include #include #include using namespace GammaRay; using namespace std; ObjectListModel::ObjectListModel(Probe *probe) : ObjectModelBase< QAbstractTableModel >(probe) { connect(probe, &Probe::objectCreated, this, &ObjectListModel::objectAdded); connect(probe, &Probe::objectDestroyed, this, &ObjectListModel::objectRemoved); } QPair ObjectListModel::defaultSelectedItem() const { // select the qApp object (if any) in the object model return QPair(ObjectModel::ObjectRole, QVariant::fromValue(qApp)); } QVariant ObjectListModel::data(const QModelIndex &index, int role) const { QMutexLocker lock(Probe::objectLock()); if (index.row() >= 0 && index.row() < m_objects.size()) { QObject *obj = m_objects.at(index.row()); if (Probe::instance()->isValidObject(obj)) return dataForObject(obj, index, role); } return QVariant(); } int ObjectListModel::columnCount(const QModelIndex &parent) const { if (parent.isValid()) return 0; return ObjectModelBase::columnCount(parent); } int ObjectListModel::rowCount(const QModelIndex &parent) const { if (parent.isValid()) return 0; return m_objects.size(); } void ObjectListModel::objectAdded(QObject *obj) { // see Probe::objectCreated, that promises a valid object in the main thread Q_ASSERT(QThread::currentThread() == thread()); Q_ASSERT(obj); Q_ASSERT(Probe::instance()->isValidObject(obj)); auto it = std::lower_bound(m_objects.begin(), m_objects.end(), obj); Q_ASSERT(it == m_objects.end() || *it != obj); const int row = std::distance(m_objects.begin(), it); Q_ASSERT(row >= 0 && row <= m_objects.size()); beginInsertRows(QModelIndex(), row, row); m_objects.insert(it, obj); Q_ASSERT(m_objects.at(row) == obj); endInsertRows(); } void ObjectListModel::objectRemoved(QObject *obj) { Q_ASSERT(thread() == QThread::currentThread()); auto it = std::lower_bound(m_objects.begin(), m_objects.end(), obj); if (it == m_objects.end() || *it != obj) { // not found return; } const int row = std::distance(m_objects.begin(), it); Q_ASSERT(row >= 0 && row < m_objects.size()); Q_ASSERT(m_objects.at(row) == obj); beginRemoveRows(QModelIndex(), row, row); m_objects.erase(it); endRemoveRows(); } const QVector &ObjectListModel::objects() const { return m_objects; } gammaray-2.11.3/core/objectlistmodel.h000066400000000000000000000054321413310443000176550ustar00rootroot00000000000000/* objectlistmodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_OBJECTLISTMODEL_H #define GAMMARAY_OBJECTLISTMODEL_H #include "objectmodelbase.h" #include #include #include namespace GammaRay { class Probe; /** * NOTE: Making the model itself threadsafe works in theory, * but as soon as we put a proxymodel on top everything breaks. * Esp. the {begin,end}{InsertRemove}Rows() calls trigger * signals which apparently must be delivered directly to the proxy, * otherwise it's internal state may be messed up and assertions * start flying around... * So the solution: only call these methods in the main thread * and on remove. when called from a background thread, invalidate * the data first. */ class ObjectListModel : public ObjectModelBase { Q_OBJECT public: explicit ObjectListModel(Probe *probe); QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; Q_INVOKABLE QPair defaultSelectedItem() const; /*! * Returns a list of all objects. * * FIXME: This is a dirty hack. Instead of offering a getter to the internal data * here, we should move it out and only give the model a view of the data. */ const QVector &objects() const; private slots: void objectAdded(QObject *obj); void objectRemoved(QObject *obj); private: void removeObject(QObject *obj); // sorted vector for stable iterators/indexes, esp. for the model methods QVector m_objects; }; } #endif // GAMMARAY_OBJECTLISTMODEL_H gammaray-2.11.3/core/objectmethodmodel.cpp000066400000000000000000000074011413310443000205130ustar00rootroot00000000000000/* objectmethodmodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "objectmethodmodel.h" #include "util.h" #include #include using namespace GammaRay; ObjectMethodModel::ObjectMethodModel(QObject *parent) : MetaObjectModel(parent) { } int GammaRay::ObjectMethodModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); return 4; } QVariant ObjectMethodModel::metaData(const QModelIndex &index, const QMetaMethod &method, int role) const { if (role == Qt::DisplayRole && index.column() == 0) { return Util::prettyMethodSignature(method); } else if (role == ObjectMethodModelRole::MetaMethod) { return QVariant::fromValue(method); } else if (role == ObjectMethodModelRole::MetaMethodType && index.column() == 1) { return QVariant::fromValue(method.methodType()); } else if (role == ObjectMethodModelRole::MethodAccess && index.column() == 2) { return QVariant::fromValue(method.access()); } else if (role == ObjectMethodModelRole::MethodSignature && index.column() == 0) { return method.methodSignature(); } else if (role == ObjectMethodModelRole::MethodTag && index.column() == 0 && qstrlen(method.tag())) { return method.tag(); } else if (role == ObjectMethodModelRole::MethodRevision && index.column() == 0) { return method.revision(); } else if (role == ObjectMethodModelRole::MethodIssues && index.column() == 0) { const QMetaObject *mo = m_metaObject; while (mo->methodOffset() > index.row()) mo = mo->superClass(); const auto r = QMetaObjectValidator::checkMethod(mo, method); return r == QMetaObjectValidatorResult::NoIssue ? QVariant() : QVariant::fromValue(r); } return QVariant(); } QMap< int, QVariant > ObjectMethodModel::itemData(const QModelIndex &index) const { QMap m = super::itemData(index); m.insert(ObjectMethodModelRole::MetaMethodType, data(index, ObjectMethodModelRole::MetaMethodType)); m.insert(ObjectMethodModelRole::MethodAccess, data(index, ObjectMethodModelRole::MethodAccess)); m.insert(ObjectMethodModelRole::MethodSignature, data(index, ObjectMethodModelRole::MethodSignature)); m.insert(ObjectMethodModelRole::MethodTag, data(index, ObjectMethodModelRole::MethodTag)); m.insert(ObjectMethodModelRole::MethodRevision, data(index, ObjectMethodModelRole::MethodRevision)); m.insert(ObjectMethodModelRole::MethodIssues, data(index, ObjectMethodModelRole::MethodIssues)); return m; } gammaray-2.11.3/core/objectmethodmodel.h000066400000000000000000000037711413310443000201660ustar00rootroot00000000000000/* objectmethodmodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_OBJECTMETHODMODEL_H #define GAMMARAY_OBJECTMETHODMODEL_H #include "metaobjectmodel.h" #include #include namespace GammaRay { class ObjectMethodModel : public MetaObjectModel { Q_OBJECT public: explicit ObjectMethodModel(QObject *parent = nullptr); int columnCount(const QModelIndex &parent = QModelIndex()) const override; QMap< int, QVariant > itemData(const QModelIndex &index) const override; protected: QVariant metaData(const QModelIndex &index, const QMetaMethod &method, int role = Qt::DisplayRole) const override; }; } Q_DECLARE_METATYPE(QMetaMethod) #endif // GAMMARAY_OBJECTMETHODMODEL_H gammaray-2.11.3/core/objectmodelbase.h000066400000000000000000000130051413310443000176070ustar00rootroot00000000000000/* objectmodelbase.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ /** @file This file is part of the GammaRay Plugin API and declares a template for an ObjectModelBase class. @brief Declares a template for an ObjectModelBase class. @author Volker Krause \ */ #ifndef GAMMARAY_OBJECTMODELBASE_H #define GAMMARAY_OBJECTMODELBASE_H #include "util.h" #include "objectdataprovider.h" #include #include #include #include #include #include namespace GammaRay { /*! A container for a generic Object Model derived from some Base. */ template class ObjectModelBase : public Base { public: /*! * Constructor. * @param parent is the parent object for this instance. */ explicit ObjectModelBase(QObject *parent) : Base(parent) { } /*! * Returns the number of columns in the specified model (currently this is * always 2). * @param parent is the model QModelIndex. * @return the column count for specified model. */ int columnCount(const QModelIndex &parent = QModelIndex()) const override { Q_UNUSED(parent); return 2; } /*! * Returns the data for the specified object. * @param object is a pointer to a QObject. * @param index is the model QModelIndex. * @param role is the Qt role. * * @return on success, a QVariant containing the data for the specified QObject; * QVariant() if some anamoly occurs. */ QVariant dataForObject(QObject *object, const QModelIndex &index, int role) const { if (role == Qt::DisplayRole) { if (index.column() == 0) return Util::shortDisplayString(object); else if (index.column() == 1) return ObjectDataProvider::typeName(object); } else if (role == ObjectModel::ObjectRole) { return QVariant::fromValue(object); } else if (role == ObjectModel::ObjectIdRole) { return QVariant::fromValue(ObjectId(object)); } else if (role == Qt::ToolTipRole) { return Util::tooltipForObject(object); } else if (role == ObjectModel::DecorationIdRole && index.column() == 0) { const int id = Util::iconIdForObject(object); return id >= 0 ? QVariant(id) : QVariant(); } else if (role == ObjectModel::CreationLocationRole) { const auto loc = ObjectDataProvider::creationLocation(object); if (loc.isValid()) return QVariant::fromValue(loc); } else if (role == ObjectModel::DeclarationLocationRole) { const auto loc = ObjectDataProvider::declarationLocation(object); if (loc.isValid()) return QVariant::fromValue(loc); } return QVariant(); } QMap itemData(const QModelIndex &index) const override { QMap map = Base::itemData(index); map.insert(ObjectModel::ObjectIdRole, this->data(index, ObjectModel::ObjectIdRole)); map.insert(ObjectModel::DecorationIdRole, this->data(index, ObjectModel::DecorationIdRole)); auto loc = this->data(index, ObjectModel::CreationLocationRole); if (loc.isValid()) map.insert(ObjectModel::CreationLocationRole, loc); loc = this->data(index, ObjectModel::DeclarationLocationRole); if (loc.isValid()) map.insert(ObjectModel::DeclarationLocationRole, loc); return map; } /*! * Returns the header data for the Object, given a section (column), * orientation and role. * @param section an integer (either 0 or 1) corresponding to the section (column). * @param orientation is the Qt::Orientation. * @param role is the Qt role. * * @return on success, a QVariant containing the header data; * QVariant() if some anamoly occurs. * */ QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override { if (role == Qt::DisplayRole && orientation == Qt::Horizontal) { switch (section) { case 0: return qApp->translate("GammaRay::ObjectModelBase", "Object"); case 1: return qApp->translate("GammaRay::ObjectModelBase", "Type"); } } return Base::headerData(section, orientation, role); } }; } #endif gammaray-2.11.3/core/objecttreemodel.cpp000066400000000000000000000213511413310443000201720ustar00rootroot00000000000000/* objecttreemodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "objecttreemodel.h" #include "probe.h" #include #include #include #include #include #include #define IF_DEBUG(x) extern void dumpObject(QObject *); using namespace std; using namespace GammaRay; ObjectTreeModel::ObjectTreeModel(Probe *probe) : ObjectModelBase< QAbstractItemModel >(probe) { connect(probe, &Probe::objectCreated, this, &ObjectTreeModel::objectAdded); connect(probe, &Probe::objectDestroyed, this, &ObjectTreeModel::objectRemoved); connect(probe, &Probe::objectReparented, this, &ObjectTreeModel::objectReparented); } QPair ObjectTreeModel::defaultSelectedItem() const { // select the qApp object (if any) in the object model return QPair(ObjectModel::ObjectRole, QVariant::fromValue(qApp)); } static inline QObject *parentObject(QObject *obj) { return obj->parent(); } void ObjectTreeModel::objectAdded(QObject *obj) { // see Probe::objectCreated, that promises a valid object in the main thread here Q_ASSERT(thread() == QThread::currentThread()); Q_ASSERT(Probe::instance()->isValidObject(obj)); IF_DEBUG(cout << "tree obj added: " << hex << obj << " p: " << parentObject(obj) << endl; ) Q_ASSERT(!obj->parent() || Probe::instance()->isValidObject(parentObject(obj))); if (indexForObject(obj).isValid()) { IF_DEBUG(cout << "tree double obj added: " << hex << obj << endl; ) return; } // this is ugly, but apparently it can happen // that an object gets created without parent // then later the delayed signal comes in // so catch this gracefully by first adding the // parent if required if (parentObject(obj)) { const QModelIndex index = indexForObject(parentObject(obj)); if (!index.isValid()) { IF_DEBUG(cout << "tree: handle parent first" << endl; ) objectAdded(parentObject(obj)); } } const QModelIndex index = indexForObject(parentObject(obj)); // either we get a proper parent and hence valid index or there is no parent Q_ASSERT(index.isValid() || !parentObject(obj)); QVector &children = m_parentChildMap[ parentObject(obj) ]; auto it = std::lower_bound(children.begin(), children.end(), obj); const int row = std::distance(children.begin(), it); beginInsertRows(index, row, row); children.insert(it, obj); m_childParentMap.insert(obj, parentObject(obj)); endInsertRows(); } void ObjectTreeModel::objectRemoved(QObject *obj) { // slot, hence should always land in main thread due to auto connection Q_ASSERT(thread() == QThread::currentThread()); IF_DEBUG(cout << "tree removed: " << hex << obj << " " << hex << obj->parent() << dec << " " << m_parentChildMap.value(obj->parent()).size() << " " << m_parentChildMap.contains(obj) << endl; ) if (!m_childParentMap.contains(obj)) { Q_ASSERT(!m_parentChildMap.contains(obj)); return; } QObject *parentObj = m_childParentMap[ obj ]; const QModelIndex parentIndex = indexForObject(parentObj); if (parentObj && !parentIndex.isValid()) return; QVector &siblings = m_parentChildMap[ parentObj ]; auto it = std::lower_bound(siblings.begin(), siblings.end(), obj); if (it == siblings.end() || *it != obj) return; const int row = std::distance(siblings.begin(), it); beginRemoveRows(parentIndex, row, row); siblings.erase(it); m_childParentMap.remove(obj); m_parentChildMap.remove(obj); endRemoveRows(); } void ObjectTreeModel::objectReparented(QObject *obj) { // slot, hence should always land in main thread due to auto connection Q_ASSERT(thread() == QThread::currentThread()); IF_DEBUG(cout << "object reparented: " << hex << obj << dec << endl; ) QMutexLocker objectLock(Probe::objectLock()); if (!Probe::instance()->isValidObject(obj)) { objectRemoved(obj); return; } // we didn't know obj yet if (!m_childParentMap.contains(obj)) { Q_ASSERT(!m_parentChildMap.contains(obj)); objectAdded(obj); return; } QObject *oldParent = m_childParentMap.value(obj); const auto sourceParent = indexForObject(oldParent); if ((oldParent && !sourceParent.isValid()) || (oldParent == parentObject(obj))) return; QVector &oldSiblings = m_parentChildMap[oldParent]; auto oldIt = std::lower_bound(oldSiblings.begin(), oldSiblings.end(), obj); if (oldIt == oldSiblings.end() || *oldIt != obj) return; const int sourceRow = std::distance(oldSiblings.begin(), oldIt); IF_DEBUG(cout << "actually reparenting! " << hex << obj << " old parent: " << oldParent << " new parent: " << parentObject( obj) << dec << endl; ) const auto destParent = indexForObject(parentObject(obj)); Q_ASSERT(destParent.isValid() || !parentObject(obj)); QVector &newSiblings = m_parentChildMap[parentObject(obj)]; auto newIt = std::lower_bound(newSiblings.begin(), newSiblings.end(), obj); const int destRow = std::distance(newSiblings.begin(), newIt); beginMoveRows(sourceParent, sourceRow, sourceRow, destParent, destRow); oldSiblings.erase(oldIt); newSiblings.insert(newIt, obj); m_childParentMap.insert(obj, parentObject(obj)); endMoveRows(); } QVariant ObjectTreeModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); QObject *obj = reinterpret_cast(index.internalPointer()); QMutexLocker lock(Probe::objectLock()); if (Probe::instance()->isValidObject(obj)) { return dataForObject(obj, index, role); } else if (role == Qt::DisplayRole) { if (index.column() == 0) return Util::addressToString(obj); else return tr(""); } return QVariant(); } int ObjectTreeModel::rowCount(const QModelIndex &parent) const { if (parent.column() == 1) return 0; QObject *parentObj = reinterpret_cast(parent.internalPointer()); return m_parentChildMap.value(parentObj).size(); } QModelIndex ObjectTreeModel::parent(const QModelIndex &child) const { QObject *childObj = reinterpret_cast(child.internalPointer()); return indexForObject(m_childParentMap.value(childObj)); } QModelIndex ObjectTreeModel::index(int row, int column, const QModelIndex &parent) const { QObject *parentObj = reinterpret_cast(parent.internalPointer()); const QVector children = m_parentChildMap.value(parentObj); if (row < 0 || column < 0 || row >= children.size() || column >= columnCount()) return {}; return createIndex(row, column, children.at(row)); } QModelIndex ObjectTreeModel::indexForObject(QObject *object) const { if (!object) return {}; QObject *parent = m_childParentMap.value(object); const QModelIndex parentIndex = indexForObject(parent); if (!parentIndex.isValid() && parent) return QModelIndex(); const QVector &siblings = m_parentChildMap[ parent ]; auto it = std::lower_bound(siblings.constBegin(), siblings.constEnd(), object); if (it == siblings.constEnd() || *it != object) return QModelIndex(); const int row = std::distance(siblings.constBegin(), it); return index(row, 0, parentIndex); } gammaray-2.11.3/core/objecttreemodel.h000066400000000000000000000042431413310443000176400ustar00rootroot00000000000000/* objecttreemodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_OBJECTTREEMODEL_H #define GAMMARAY_OBJECTTREEMODEL_H #include "objectmodelbase.h" #include namespace GammaRay { class Probe; class ObjectTreeModel : public ObjectModelBase { Q_OBJECT public: explicit ObjectTreeModel(Probe *probe); QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; QModelIndex parent(const QModelIndex &child) const override; QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; Q_INVOKABLE QPair defaultSelectedItem() const; private slots: void objectAdded(QObject *obj); void objectRemoved(QObject *obj); void objectReparented(QObject *obj); private: QModelIndex indexForObject(QObject *object) const; private: QHash m_childParentMap; QHash > m_parentChildMap; }; } #endif // GAMMARAY_OBJECTTREEMODEL_H gammaray-2.11.3/core/objecttypefilterproxymodel.cpp000066400000000000000000000037771413310443000225400ustar00rootroot00000000000000/* objecttypefilterproxymodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "objecttypefilterproxymodel.h" using namespace GammaRay; ObjectFilterProxyModelBase::ObjectFilterProxyModelBase(QObject *parent) : QSortFilterProxyModel(parent) { setDynamicSortFilter(true); } QMap ObjectFilterProxyModelBase::itemData(const QModelIndex& index) const { return sourceModel()->itemData(mapToSource(index)); } bool ObjectFilterProxyModelBase::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const { const QModelIndex source_index = sourceModel()->index(source_row, 0, source_parent); if (!source_index.isValid()) return false; QObject *obj = source_index.data(ObjectModel::ObjectRole).value(); if (!obj || !filterAcceptsObject(obj)) return false; return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent); } gammaray-2.11.3/core/objecttypefilterproxymodel.h000066400000000000000000000065501413310443000221750ustar00rootroot00000000000000/* objecttypefilterproxymodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ /** @file This file is part of the GammaRay Plugin API and declares a template for an ObjectTypeFilterProxyModel class. @brief Declares a template for an ObjectTypeFilterProxyModel class. @author Volker Krause \ */ #ifndef GAMMARAY_OBJECTTYPEFILTERPROXYMODEL_H #define GAMMARAY_OBJECTTYPEFILTERPROXYMODEL_H #include "gammaray_core_export.h" #include "objectmodelbase.h" #include namespace GammaRay { /** * @brief A QSortFilterProxyModel for generic Objects. */ class GAMMARAY_CORE_EXPORT ObjectFilterProxyModelBase : public QSortFilterProxyModel { Q_OBJECT public: /** * Constructor. * @param parent is the parent object for this instance. */ explicit ObjectFilterProxyModelBase(QObject *parent = nullptr); QMap itemData(const QModelIndex& index) const override; protected: /** * Determines if the item in the specified row can be included in the model. * @param source_row is a non-zero integer representing the row of the item. * @param source_parent is the parent QModelIndex for this model. * @return true if the item in the row can be included in the model; * otherwise returns false. */ bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override; /** * Determines if the specified QObject can be included in the model. * @param object is a pointer to the QObject to test. * @return true if the QObject can be included in the model; false otherwise. */ virtual bool filterAcceptsObject(QObject *object) const = 0; }; /** * @brief A templated generic ObjectFilterProxyModelBase for some data type. */ template class ObjectTypeFilterProxyModel : public ObjectFilterProxyModelBase { public: /** * Constructor. * @param parent is the parent object for this instance. */ explicit ObjectTypeFilterProxyModel(QObject *parent = nullptr) : ObjectFilterProxyModelBase(parent) { } protected: bool filterAcceptsObject(QObject *object) const override { return qobject_cast(object) || qobject_cast(object); } }; } #endif // GAMMARAY_OBJECTTYPEFILTERPROXYMODEL_H gammaray-2.11.3/core/paintanalyzer.cpp000066400000000000000000000152201413310443000177020ustar00rootroot00000000000000/* paintanalyzer.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include "paintanalyzer.h" #include "paintbuffer.h" #include "paintbuffermodel.h" #include "painterprofilingreplayer.h" #include #include #include #include #include #include #include #include #include #include #include using namespace GammaRay; class PaintBufferModelFilterProxy : public QSortFilterProxyModel { Q_OBJECT public: explicit PaintBufferModelFilterProxy(QObject *parent = nullptr) : QSortFilterProxyModel(parent) {} void sort(int, Qt::SortOrder) override {} // never sort, that has no semantics here }; PaintAnalyzer::PaintAnalyzer(const QString &name, QObject *parent) : PaintAnalyzerInterface(name, parent) , m_paintBufferModel(nullptr) , m_paintBufferFilter(nullptr) , m_selectionModel(nullptr) , m_paintBuffer(nullptr) , m_remoteView(new RemoteViewServer(name + QStringLiteral(".remoteView"), this)) , m_argumentModel(new AggregatedPropertyModel(this)) , m_stackTraceModel(new StackTraceModel(this)) { m_paintBufferModel = new PaintBufferModel(this); auto proxy = new ServerProxyModel(this); proxy->addRole(PaintBufferModelRoles::MaxCostRole); m_paintBufferFilter = proxy; m_paintBufferFilter->setSourceModel(m_paintBufferModel); Probe::instance()->registerModel(name + QStringLiteral(".paintBufferModel"), m_paintBufferFilter); m_selectionModel = ObjectBroker::selectionModel(m_paintBufferFilter); connect(m_selectionModel, &QItemSelectionModel::currentChanged, m_remoteView, &RemoteViewServer::sourceChanged); m_argumentModel->setReadOnly(true); Probe::instance()->registerModel(name + QStringLiteral(".argumentProperties"), m_argumentModel); Probe::instance()->registerModel(name + QStringLiteral(".stackTrace"), m_stackTraceModel); connect(m_remoteView, &RemoteViewServer::requestUpdate, this, &PaintAnalyzer::repaint); } PaintAnalyzer::~PaintAnalyzer() = default; void PaintAnalyzer::reset() { m_remoteView->sourceChanged(); m_paintBufferModel->setPaintBuffer(PaintBuffer()); } void PaintAnalyzer::repaint() { if (!m_remoteView->isActive()) return; if (m_paintBufferModel->rowCount() == 0) { emit requestUpdate(); return; } const QSize sourceSize = m_paintBufferModel->buffer().boundingRect().size().toSize(); #if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0) const qreal ratio = m_paintBufferModel->buffer().devicePixelRatioF(); #else const qreal ratio = m_paintBufferModel->buffer().devicePixelRatio(); #endif QImage image(sourceSize * ratio, QImage::Format_ARGB32); image.setDevicePixelRatio(ratio); image.fill(Qt::transparent); QPainter painter(&image); const auto start = m_paintBufferModel->buffer().frameStartIndex(0); auto index = m_paintBufferFilter->mapToSource(m_selectionModel->currentIndex()); m_currentArgument = index.data(PaintBufferModelRoles::ValueRole); m_argumentModel->setObject(m_currentArgument); setHasArgumentDetails(m_argumentModel->rowCount()); // include selected row or paint all if nothing is selected if (index.parent().isValid()) { index = index.parent(); } const auto end = index.isValid() ? index.row() + 1 : m_paintBufferModel->rowCount(); auto depth = m_paintBufferModel->buffer().processCommands(&painter, start, start + end); for (; depth > 0; --depth) { painter.restore(); } painter.end(); PaintAnalyzerFrameData data; if (index.isValid()) { data.clipPath = index.data(PaintBufferModelRoles::ClipPathRole).value(); } RemoteViewFrame frame; frame.setImage(image); frame.setData(QVariant::fromValue(data)); m_remoteView->sendFrame(frame); if (index.isValid()) { m_stackTraceModel->setStackTrace(m_paintBufferModel->buffer().stackTrace(index.row())); setHasStackTrace(m_stackTraceModel->rowCount() > 0); } else { setHasStackTrace(false); } } void PaintAnalyzer::beginAnalyzePainting() { Q_ASSERT(!m_paintBuffer); m_paintBuffer = new PaintBuffer; } void PaintAnalyzer::setBoundingRect(const QRectF &boundingBox) { Q_ASSERT(m_paintBuffer); m_paintBuffer->setBoundingRect(boundingBox); } QPaintDevice *PaintAnalyzer::paintDevice() const { Q_ASSERT(m_paintBuffer); return m_paintBuffer; } void PaintAnalyzer::endAnalyzePainting() { Q_ASSERT(m_paintBuffer); Q_ASSERT(m_paintBufferModel); m_paintBufferModel->setPaintBuffer(*m_paintBuffer); delete m_paintBuffer; m_paintBuffer = nullptr; m_remoteView->resetView(); m_remoteView->sourceChanged(); if (auto rowCount = m_paintBufferFilter->rowCount()) { const auto idx = m_paintBufferFilter->index(rowCount - 1, 0); m_selectionModel->select(idx, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows | QItemSelectionModel::Current); } PainterProfilingReplayer profiler; profiler.profile(m_paintBufferModel->buffer()); m_paintBufferModel->setCosts(profiler.costs()); } void GammaRay::PaintAnalyzer::setOrigin(const ObjectId &obj) { m_paintBuffer->setOrigin(obj); } bool PaintAnalyzer::isAvailable() { return true; } #include "paintanalyzer.moc" gammaray-2.11.3/core/paintanalyzer.h000066400000000000000000000060271413310443000173540ustar00rootroot00000000000000/* paintanalyzer.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_PAINTANALYZER_H #define GAMMARAY_PAINTANALYZER_H #include "gammaray_core_export.h" #include #include #include QT_BEGIN_NAMESPACE class QItemSelectionModel; class QPaintDevice; class QRectF; class QSortFilterProxyModel; QT_END_NAMESPACE namespace GammaRay { class AggregatedPropertyModel; class PaintBuffer; class PaintBufferModel; class RemoteViewServer; class StackTraceModel; /** Inspects individual operations on a QPainter. */ class GAMMARAY_CORE_EXPORT PaintAnalyzer : public PaintAnalyzerInterface { Q_OBJECT Q_INTERFACES(GammaRay::PaintAnalyzerInterface) public: explicit PaintAnalyzer(const QString &name, QObject *parent = nullptr); ~PaintAnalyzer() override; /** Reset the paint analyzer state for providing a new analysis asynchronously. */ void reset(); // call the following 4 methods in this order to trigger a paint analysis void beginAnalyzePainting(); void setBoundingRect(const QRectF &boundingBox); QPaintDevice *paintDevice() const; void endAnalyzePainting(); /** Returns @c true if paint analysis is available (needs access to Qt private headers at compile time). */ static bool isAvailable(); /** * Marks all following paint operations to origin from the given QWidget/QQuickItem * until this is called with another object. */ void setOrigin(const ObjectId &obj); signals: /** Polling for updated analysis. */ void requestUpdate(); private slots: void repaint(); private: PaintBufferModel *m_paintBufferModel; QSortFilterProxyModel *m_paintBufferFilter; QItemSelectionModel *m_selectionModel; PaintBuffer *m_paintBuffer; RemoteViewServer *m_remoteView; AggregatedPropertyModel *m_argumentModel; ObjectInstance m_currentArgument; StackTraceModel *m_stackTraceModel; }; } #endif // GAMMARAY_PAINTANALYZER_H gammaray-2.11.3/core/paintbuffer.cpp000066400000000000000000000223201413310443000173250ustar00rootroot00000000000000/* paintbuffer.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "paintbuffer.h" #include "execution.h" using namespace GammaRay; PaintBufferEngine::PaintBufferEngine(GammaRay::PaintBuffer *buffer) : QPaintBufferEngine(buffer->data()) , m_buffer(buffer) { } PaintBufferEngine::~PaintBufferEngine() = default; void PaintBufferEngine::clip(const QVectorPath& path, Qt::ClipOperation op) { QPaintBufferEngine::clip(path, op); createStackTrace(); pushOrigin(); } void PaintBufferEngine::clip(const QRect& rect, Qt::ClipOperation op) { QPaintBufferEngine::clip(rect, op); createStackTrace(); pushOrigin(); } void PaintBufferEngine::clip(const QRegion& region, Qt::ClipOperation op) { QPaintBufferEngine::clip(region, op); createStackTrace(); pushOrigin(); } void PaintBufferEngine::clip(const QPainterPath& path, Qt::ClipOperation op) { QPaintBufferEngine::clip(path, op); // TODO disabled in QPaintBuffer // createStackTrace(); // pushOrigin(); } void PaintBufferEngine::backgroundModeChanged() { QPaintBufferEngine::backgroundModeChanged(); createStackTrace(); pushOrigin(); } void PaintBufferEngine::brushChanged() { QPaintBufferEngine::brushChanged(); createStackTrace(); pushOrigin(); } void PaintBufferEngine::brushOriginChanged() { QPaintBufferEngine::brushOriginChanged(); createStackTrace(); pushOrigin(); } void PaintBufferEngine::clipEnabledChanged() { QPaintBufferEngine::clipEnabledChanged(); createStackTrace(); pushOrigin(); } void PaintBufferEngine::compositionModeChanged() { QPaintBufferEngine::compositionModeChanged(); createStackTrace(); pushOrigin(); } void PaintBufferEngine::opacityChanged() { QPaintBufferEngine::opacityChanged(); createStackTrace(); pushOrigin(); } void PaintBufferEngine::penChanged() { QPaintBufferEngine::penChanged(); createStackTrace(); pushOrigin(); } void PaintBufferEngine::renderHintsChanged() { QPaintBufferEngine::renderHintsChanged(); createStackTrace(); pushOrigin(); } void PaintBufferEngine::transformChanged() { QPaintBufferEngine::transformChanged(); createStackTrace(); pushOrigin(); } void PaintBufferEngine::fillRect(const QRectF& rect, const QBrush& brush) { QPaintBufferEngine::fillRect(rect, brush); createStackTrace(); pushOrigin(); } void PaintBufferEngine::fillRect(const QRectF& rect, const QColor& color) { QPaintBufferEngine::fillRect(rect, color); createStackTrace(); pushOrigin(); } void PaintBufferEngine::drawRects(const QRect* rects, int rectCount) { QPaintBufferEngine::drawRects(rects, rectCount); createStackTrace(); pushOrigin(); } void PaintBufferEngine::drawRects(const QRectF* rects, int rectCount) { QPaintBufferEngine::drawRects(rects, rectCount); createStackTrace(); pushOrigin(); } void PaintBufferEngine::drawLines(const QLine* lines, int lineCount) { QPaintBufferEngine::drawLines(lines, lineCount); createStackTrace(); pushOrigin(); } void PaintBufferEngine::drawLines(const QLineF* lines, int lineCount) { QPaintBufferEngine::drawLines(lines, lineCount); createStackTrace(); pushOrigin(); } void PaintBufferEngine::drawEllipse(const QRect& r) { QPaintBufferEngine::drawEllipse(r); createStackTrace(); pushOrigin(); } void PaintBufferEngine::drawEllipse(const QRectF& r) { QPaintBufferEngine::drawEllipse(r); createStackTrace(); pushOrigin(); } void PaintBufferEngine::drawPath(const QPainterPath& path) { QPaintBufferEngine::drawPath(path); // TODO this is not handled in QPaintBuffer // createStackTrace(); // pushOrigin(); } void PaintBufferEngine::drawPoints(const QPoint* points, int pointCount) { QPaintBufferEngine::drawPoints(points, pointCount); createStackTrace(); pushOrigin(); } void PaintBufferEngine::drawPoints(const QPointF* points, int pointCount) { QPaintBufferEngine::drawPoints(points, pointCount); createStackTrace(); pushOrigin(); } void PaintBufferEngine::drawPolygon(const QPoint* points, int pointCount, QPaintEngine::PolygonDrawMode mode) { QPaintBufferEngine::drawPolygon(points, pointCount, mode); createStackTrace(); pushOrigin(); } void PaintBufferEngine::drawPolygon(const QPointF* points, int pointCount, QPaintEngine::PolygonDrawMode mode) { QPaintBufferEngine::drawPolygon(points, pointCount, mode); createStackTrace(); pushOrigin(); } void PaintBufferEngine::drawPixmap(const QRectF& r, const QPixmap& pm, const QRectF& sr) { QPaintBufferEngine::drawPixmap(r, pm, sr); createStackTrace(); pushOrigin(); } void PaintBufferEngine::drawPixmap(const QPointF& pos, const QPixmap& pm) { QPaintBufferEngine::drawPixmap(pos, pm); createStackTrace(); pushOrigin(); } void PaintBufferEngine::drawImage(const QPointF& pos, const QImage& image) { QPaintBufferEngine::drawImage(pos, image); createStackTrace(); pushOrigin(); } void PaintBufferEngine::drawImage(const QRectF& r, const QImage& pm, const QRectF& sr, Qt::ImageConversionFlags flags) { QPaintBufferEngine::drawImage(r, pm, sr, flags); createStackTrace(); pushOrigin(); } void PaintBufferEngine::drawTiledPixmap(const QRectF& r, const QPixmap& pixmap, const QPointF& s) { QPaintBufferEngine::drawTiledPixmap(r, pixmap, s); createStackTrace(); pushOrigin(); } void PaintBufferEngine::drawTextItem(const QPointF& pos, const QTextItem& ti) { QPaintBufferEngine::drawTextItem(pos, ti); createStackTrace(); pushOrigin(); } void PaintBufferEngine::drawStaticTextItem(QStaticTextItem* staticTextItem) { QPaintBufferEngine::drawStaticTextItem(staticTextItem); createStackTrace(); pushOrigin(); } void PaintBufferEngine::setState(QPainterState* s) { const auto prevSize = m_buffer->data()->commands.size(); QPaintBufferEngine::setState(s); const auto newSize = m_buffer->data()->commands.size(); if (prevSize != newSize) { createStackTrace(); pushOrigin(); } } void PaintBufferEngine::createStackTrace() { if (!Execution::stackTracingAvailable()) return; const auto size = m_buffer->data()->commands.size(); m_buffer->m_stackTraces.resize(size); // TODO find a way to stop this at the analyzer call site, we don't want to see the gammaray call chain m_buffer->m_stackTraces.back() = Execution::stackTrace(16, 2); } void PaintBufferEngine::pushOrigin() { const auto size = m_buffer->data()->commands.size(); m_buffer->m_origins.reserve(size); while (m_buffer->m_origins.size() < size) { m_buffer->m_origins.push_back(m_buffer->m_currentOrigin); } } class PaintBufferPrivacyViolater : public QPainterReplayer { public: static QPaintBufferPrivate* get(QPaintBuffer *buffer) { PaintBufferPrivacyViolater p; p.processCommands(*buffer, nullptr, 0, -1); // end < begin -> no processing return p.extract(); } private: QPaintBufferPrivate *extract() const { return d; } }; PaintBuffer::PaintBuffer() { d = PaintBufferPrivacyViolater::get(this); } PaintBuffer::PaintBuffer(const PaintBuffer& other) : QPaintBuffer(other) , m_stackTraces(other.m_stackTraces) , m_origins(other.m_origins) { d = PaintBufferPrivacyViolater::get(this); } PaintBuffer::~PaintBuffer() = default; PaintBuffer& PaintBuffer::operator=(const PaintBuffer& other) { QPaintBuffer::operator=(other); d = PaintBufferPrivacyViolater::get(this); m_stackTraces = other.m_stackTraces; m_origins = other.m_origins; return *this; } QPaintEngine* PaintBuffer::paintEngine() const { if (!d->engine) d->engine = new PaintBufferEngine(const_cast(this)); return d->engine; } Execution::Trace PaintBuffer::stackTrace(int index) const { if (index < 0 || index >= m_stackTraces.size()) return Execution::Trace(); return m_stackTraces.at(index); } ObjectId PaintBuffer::origin(int index) const { if (index < 0 || index >= m_origins.size()) { return ObjectId(); } return m_origins.at(index); } void PaintBuffer::setOrigin(const ObjectId &obj) { m_currentOrigin = obj; } QPaintBufferPrivate* PaintBuffer::data() const { return d; } gammaray-2.11.3/core/paintbuffer.h000066400000000000000000000112441413310443000167750ustar00rootroot00000000000000/* paintbuffer.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_PAINTBUFFER_H #define GAMMARAY_PAINTBUFFER_H #include #include #include #include namespace GammaRay { namespace Execution { class Trace; } class PaintBuffer; class PaintBufferEngine : public QPaintBufferEngine { public: explicit PaintBufferEngine(GammaRay::PaintBuffer *buffer); ~PaintBufferEngine(); void clip(const QVectorPath &path, Qt::ClipOperation op) override; void clip(const QRect &rect, Qt::ClipOperation op) override; void clip(const QRegion ®ion, Qt::ClipOperation op) override; void clip(const QPainterPath &path, Qt::ClipOperation op) override; void backgroundModeChanged() override; void brushChanged() override; void brushOriginChanged() override; void clipEnabledChanged() override; void compositionModeChanged() override; void opacityChanged() override; void penChanged() override; void renderHintsChanged() override; void transformChanged() override; void fillRect(const QRectF &rect, const QBrush &brush) override; void fillRect(const QRectF &rect, const QColor &color) override; void drawRects(const QRect *rects, int rectCount) override; void drawRects(const QRectF *rects, int rectCount) override; void drawLines(const QLine *lines, int lineCount) override; void drawLines(const QLineF *lines, int lineCount) override; void drawEllipse(const QRectF &r) override; void drawEllipse(const QRect &r) override; void drawPath(const QPainterPath &path) override; void drawPoints(const QPoint *points, int pointCount) override; void drawPoints(const QPointF *points, int pointCount) override; void drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode) override; void drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode) override; void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr) override; void drawPixmap(const QPointF &pos, const QPixmap &pm) override; void drawImage(const QPointF &pos, const QImage &image) override; void drawImage(const QRectF &r, const QImage &pm, const QRectF &sr, Qt::ImageConversionFlags flags = Qt::AutoColor) override; void drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &s) override; void drawTextItem(const QPointF &pos, const QTextItem &ti) override; void drawStaticTextItem(QStaticTextItem *staticTextItem) override; void setState(QPainterState *s) override; private: void createStackTrace(); void pushOrigin(); GammaRay::PaintBuffer *m_buffer; }; class PaintBuffer : public QPaintBuffer { public: PaintBuffer(); PaintBuffer(const PaintBuffer &other); ~PaintBuffer(); PaintBuffer& operator=(const PaintBuffer &other); QPaintEngine *paintEngine() const override; /** * Marks all following paint operations to origin from the given QWidget/QQuickItem * until this is called with another object. */ void setOrigin(const ObjectId &obj); /** Returns the stack trace of command at @p index. */ Execution::Trace stackTrace(int index) const; /** Returns the origin of command at @p index. */ ObjectId origin(int index) const; QPaintBufferPrivate* data() const; private: friend class PaintBufferEngine; QPaintBufferPrivate *d; // not protected in the base class, somewhat nasty to get to QVector m_stackTraces; public: QVector m_origins; ObjectId m_currentOrigin; }; } #endif // GAMMARAY_PAINTBUFFER_H gammaray-2.11.3/core/paintbuffermodel.cpp000066400000000000000000000564221413310443000203600ustar00rootroot00000000000000/* paintbuffermodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2012-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include "paintbuffermodel.h" #include #include #include #include #include #include #include #include #include using namespace GammaRay; static const char* cmd_argument_names[] = { "position", // DrawText "text", "font", "position", // DrawImagePos "image", "target", // DrawImageRect "image", "source", "position", // DrawPixmapPos "pixmap", "target", // DrawPixmapRect "pixmap", "source", "rectangle", // DrawTiledPixmap "pixmap", "position", }; struct cmd_t { QPaintBufferPrivate::Command cmd; const char *name; int argumentCount; int argumentName; }; #define CMD(cmd) QPaintBufferPrivate::Cmd_ ## cmd, #cmd static const cmd_t cmdTypes[] = { { CMD(Save), 0, 0 }, { CMD(Restore), 0, 0 }, { CMD(SetBrush), 0, 0 }, { CMD(SetBrushOrigin), 0, 0 }, { CMD(SetClipEnabled), 0, 0 }, { CMD(SetCompositionMode), 0, 0 }, { CMD(SetOpacity), 0, 0 }, { CMD(SetPen), 0, 0 }, { CMD(SetRenderHints), 0, 0 }, { CMD(SetTransform), 0, 0 }, { CMD(SetBackgroundMode), 0, 0 }, { CMD(ClipPath), 0, 0 }, { CMD(ClipRect), 0, 0 }, { CMD(ClipRegion), 0, 0 }, { CMD(ClipVectorPath), 0, 0 }, { CMD(DrawVectorPath), 0, 0 }, { CMD(FillVectorPath), 0, 0 }, { CMD(StrokeVectorPath), 0, 0 }, { CMD(DrawConvexPolygonF), 0, 0 }, { CMD(DrawConvexPolygonI), 0, 0 }, { CMD(DrawEllipseF), 0, 0 }, { CMD(DrawEllipseI), 0, 0 }, { CMD(DrawLineF), 0, 0 }, { CMD(DrawLineI), 0, 0 }, { CMD(DrawPath), 0, 0 }, { CMD(DrawPointsF), 0, 0 }, { CMD(DrawPointsI), 0, 0 }, { CMD(DrawPolygonF), 0, 0 }, { CMD(DrawPolygonI), 0, 0 }, { CMD(DrawPolylineF), 0, 0 }, { CMD(DrawPolylineI), 0, 0 }, { CMD(DrawRectF), 0, 0 }, { CMD(DrawRectI), 0, 0 }, { CMD(FillRectBrush), 0, 0 }, { CMD(FillRectColor), 0, 0 }, { CMD(DrawText), 3, 0 }, { CMD(DrawTextItem), 0, 0 }, { CMD(DrawImagePos), 2, 3 }, { CMD(DrawImageRect), 3, 5 }, { CMD(DrawPixmapPos), 2, 8 }, { CMD(DrawPixmapRect), 3, 10 }, { CMD(DrawTiledPixmap), 3, 13 }, { CMD(SystemStateChanged), 0, 0 }, { CMD(Translate), 0, 0 }, { CMD(DrawStaticText), 0, 0 } }; #undef CMD static const int TopLevelId = std::numeric_limits::max(); PaintBufferModel::PaintBufferModel(QObject *parent) : QAbstractItemModel(parent) , m_privateBuffer(nullptr) , m_maxCost(0.0) { } void PaintBufferModel::setPaintBuffer(const PaintBuffer &buffer) { beginResetModel(); m_buffer = buffer; m_privateBuffer = buffer.data(); m_costs.clear(); m_maxCost = 0.0; endResetModel(); } PaintBuffer PaintBufferModel::buffer() const { return m_buffer; } void PaintBufferModel::setCosts(const QVector& costs) { m_costs = costs; if (rowCount() > 0) { m_maxCost = *std::max_element(m_costs.constBegin(), m_costs.constEnd()); emit dataChanged(index(0, 2, QModelIndex()), index(rowCount() - 1, 2, QModelIndex())); } } template static QString geometryListToString(const Data *data, int offset, int size) { QStringList l; l.reserve(size); auto *elem = reinterpret_cast(data + offset); for (int i = 0; i < size; ++i) l.push_back(VariantHandler::displayString(*elem++)); return l.join(QLatin1String("; ")); } static QString vectorPathToString(QPaintBufferPrivate *data, const QPaintBufferCommand &cmd) { QVectorPath path( data->floats.constData() + cmd.offset, cmd.size, cmd.offset2 & 0x80000000 ? nullptr : reinterpret_cast(data->ints.constData() + cmd.offset2 + 1), *(data->ints.constData() + (cmd.offset2 & 0x7FFFFFFF)) ); if (path.isEmpty()) return PaintBufferModel::tr(""); return PaintBufferModel::tr("control rect: %1, elements: %2").arg( VariantHandler::displayString(path.controlPointRect()), QString::number(path.elementCount())); } QVariant PaintBufferModel::argumentAt(const QPaintBufferCommand& cmd, int index) const { switch (cmd.id) { // single argument commands case QPaintBufferPrivate::Cmd_SetBrush: case QPaintBufferPrivate::Cmd_SetPen: return m_privateBuffer->variants.at(cmd.offset); case QPaintBufferPrivate::Cmd_FillRectBrush: return m_privateBuffer->variants.at(cmd.extra); // multi argument commands case QPaintBufferPrivate::Cmd_DrawText: switch (index) { case 0: return QPointF(m_privateBuffer->floats.at(cmd.extra), m_privateBuffer->floats.at(cmd.extra + 1)); case 1: return m_privateBuffer->variants.at(cmd.offset).value >().at(1); case 2: return m_privateBuffer->variants.at(cmd.offset).value >().at(0); } break; case QPaintBufferPrivate::Cmd_DrawImagePos: case QPaintBufferPrivate::Cmd_DrawPixmapPos: switch (index) { case 0: return QPointF(m_privateBuffer->floats.at(cmd.extra), m_privateBuffer->floats.at(cmd.extra+1)); case 1: return m_privateBuffer->variants.at(cmd.offset); } break; case QPaintBufferPrivate::Cmd_DrawImageRect: case QPaintBufferPrivate::Cmd_DrawPixmapRect: switch (index) { case 0: return QRectF(m_privateBuffer->floats.at(cmd.extra), m_privateBuffer->floats.at(cmd.extra + 1), m_privateBuffer->floats.at(cmd.extra + 2), m_privateBuffer->floats.at(cmd.extra + 3)); case 1: return m_privateBuffer->variants.at(cmd.offset); case 2: return QRectF(m_privateBuffer->floats.at(cmd.extra + 4), m_privateBuffer->floats.at(cmd.extra + 5), m_privateBuffer->floats.at(cmd.extra + 6), m_privateBuffer->floats.at(cmd.extra + 7)); } break; case QPaintBufferPrivate::Cmd_DrawTiledPixmap: switch (index) { case 0: return QRectF(m_privateBuffer->floats.at(cmd.extra), m_privateBuffer->floats.at(cmd.extra + 1), m_privateBuffer->floats.at(cmd.extra + 2), m_privateBuffer->floats.at(cmd.extra + 3)); case 1: return m_privateBuffer->variants.at(cmd.offset); case 2: return QPointF(m_privateBuffer->floats.at(cmd.extra + 4), m_privateBuffer->floats.at(cmd.extra + 5)); } break; default: break; } return QVariant(); } QString PaintBufferModel::argumentDisplayString(const QPaintBufferCommand &cmd) const { switch (cmd.id) { case QPaintBufferPrivate::Cmd_Save: case QPaintBufferPrivate::Cmd_Restore: break; case QPaintBufferPrivate::Cmd_SetBrush: case QPaintBufferPrivate::Cmd_SetBrushOrigin: case QPaintBufferPrivate::Cmd_SetClipEnabled: return VariantHandler::displayString(m_privateBuffer->variants.at(cmd.offset)); case QPaintBufferPrivate::Cmd_SetCompositionMode: return EnumUtil::enumToString(QVariant::fromValue(static_cast(cmd.extra))); case QPaintBufferPrivate::Cmd_SetOpacity: return QString::number(m_privateBuffer->variants.at(cmd.offset).toDouble()); case QPaintBufferPrivate::Cmd_SetPen: return VariantHandler::displayString(m_privateBuffer->variants.at(cmd.offset)); case QPaintBufferPrivate::Cmd_SetRenderHints: return EnumUtil::enumToString(QVariant::fromValue(static_cast(cmd.extra))); case QPaintBufferPrivate::Cmd_SetTransform: return VariantHandler::displayString(m_privateBuffer->variants.at(cmd.offset)); case QPaintBufferPrivate::Cmd_SetBackgroundMode: return EnumUtil::enumToString(QVariant::fromValue(static_cast(cmd.extra))); case QPaintBufferPrivate::Cmd_ClipRect: return EnumUtil::enumToString(QVariant::fromValue(static_cast(cmd.extra))) + QLatin1String(": ") + VariantHandler::displayString(QRect( QPoint(m_privateBuffer->ints.at(cmd.offset), m_privateBuffer->ints.at(cmd.offset + 1)), QPoint(m_privateBuffer->ints.at(cmd.offset + 2), m_privateBuffer->ints.at(cmd.offset + 3)) )); case QPaintBufferPrivate::Cmd_ClipPath: case QPaintBufferPrivate::Cmd_ClipRegion: return EnumUtil::enumToString(QVariant::fromValue(static_cast(cmd.extra))) + QLatin1String(": ") + VariantHandler::displayString(m_privateBuffer->variants.at(cmd.offset)); case QPaintBufferPrivate::Cmd_ClipVectorPath: return EnumUtil::enumToString(QVariant::fromValue(static_cast(cmd.extra))) + QLatin1String(": ") + vectorPathToString(m_privateBuffer, cmd); case QPaintBufferPrivate::Cmd_DrawVectorPath: return vectorPathToString(m_privateBuffer, cmd); case QPaintBufferPrivate::Cmd_FillVectorPath: return tr("%1, brush: %2").arg( vectorPathToString(m_privateBuffer, cmd), VariantHandler::displayString(m_privateBuffer->variants.at(cmd.extra))); case QPaintBufferPrivate::Cmd_StrokeVectorPath: return tr("%1, pen: %2").arg( vectorPathToString(m_privateBuffer, cmd), VariantHandler::displayString(m_privateBuffer->variants.at(cmd.extra))); case QPaintBufferPrivate::Cmd_DrawEllipseF: return VariantHandler::displayString(*reinterpret_cast(m_privateBuffer->floats.constData() + cmd.offset)); case QPaintBufferPrivate::Cmd_DrawEllipseI: return VariantHandler::displayString(*reinterpret_cast(m_privateBuffer->ints.constData() + cmd.offset)); case QPaintBufferPrivate::Cmd_DrawLineF: return geometryListToString(m_privateBuffer->floats.constData(), cmd.offset, cmd.size); case QPaintBufferPrivate::Cmd_DrawLineI: return geometryListToString(m_privateBuffer->ints.constData(), cmd.offset, cmd.size); case QPaintBufferPrivate::Cmd_DrawPath: return VariantHandler::displayString(m_privateBuffer->variants.at(cmd.offset)); case QPaintBufferPrivate::Cmd_DrawPolygonF: return EnumUtil::enumToString(QVariant::fromValue(static_cast(cmd.extra))) + QLatin1String(": ") + geometryListToString(m_privateBuffer->floats.constData(), cmd.offset, cmd.size); case QPaintBufferPrivate::Cmd_DrawPolygonI: return EnumUtil::enumToString(QVariant::fromValue(static_cast(cmd.extra))) + QLatin1String(": ") + geometryListToString(m_privateBuffer->ints.constData(), cmd.offset, cmd.size); case QPaintBufferPrivate::Cmd_DrawPointsF: case QPaintBufferPrivate::Cmd_DrawConvexPolygonF: case QPaintBufferPrivate::Cmd_DrawPolylineF: return geometryListToString(m_privateBuffer->floats.constData(), cmd.offset, cmd.size); case QPaintBufferPrivate::Cmd_DrawPointsI: case QPaintBufferPrivate::Cmd_DrawConvexPolygonI: case QPaintBufferPrivate::Cmd_DrawPolylineI: return geometryListToString(m_privateBuffer->ints.constData(), cmd.offset, cmd.size); case QPaintBufferPrivate::Cmd_DrawRectF: return geometryListToString(m_privateBuffer->floats.constData(), cmd.offset, cmd.size); case QPaintBufferPrivate::Cmd_DrawRectI: return geometryListToString(m_privateBuffer->ints.constData(), cmd.offset, cmd.size); case QPaintBufferPrivate::Cmd_FillRectBrush: case QPaintBufferPrivate::Cmd_FillRectColor: return VariantHandler::displayString(*reinterpret_cast(m_privateBuffer->floats.constData() + cmd.offset)) + QLatin1String(" - ") + VariantHandler::displayString(m_privateBuffer->variants.at(cmd.extra)); case QPaintBufferPrivate::Cmd_DrawText: return tr("position: %1, text: \"%2\", font: %3").arg( VariantHandler::displayString(argumentAt(cmd, 0)), VariantHandler::displayString(argumentAt(cmd, 1)), VariantHandler::displayString(argumentAt(cmd, 2))); case QPaintBufferPrivate::Cmd_DrawTextItem: { QPointF pos(m_privateBuffer->floats.at(cmd.extra), m_privateBuffer->floats.at(cmd.extra + 1)); auto textItem = reinterpret_cast(m_privateBuffer->variants.at(cmd.offset).value()); return tr("position: %1, text: \"%2\", font: %3").arg( VariantHandler::displayString(pos), (*textItem)().text(), VariantHandler::displayString((*textItem)().font()) ); } case QPaintBufferPrivate::Cmd_DrawImagePos: case QPaintBufferPrivate::Cmd_DrawPixmapPos: return VariantHandler::displayString(argumentAt(cmd, 0)); case QPaintBufferPrivate::Cmd_DrawImageRect: case QPaintBufferPrivate::Cmd_DrawPixmapRect: return tr("%1 source: %2").arg( VariantHandler::displayString(argumentAt(cmd, 0)), VariantHandler::displayString(argumentAt(cmd, 2))); case QPaintBufferPrivate::Cmd_DrawTiledPixmap: return tr("%1 offset: %2").arg( VariantHandler::displayString(argumentAt(cmd, 0)), VariantHandler::displayString(argumentAt(cmd, 2))); case QPaintBufferPrivate::Cmd_SystemStateChanged: return VariantHandler::displayString(m_privateBuffer->variants.at(cmd.offset)); case QPaintBufferPrivate::Cmd_Translate: return VariantHandler::displayString(QPointF(m_privateBuffer->floats.at(cmd.extra), m_privateBuffer->floats.at(cmd.extra + 1))); case QPaintBufferPrivate::Cmd_DrawStaticText: { const auto variants = m_privateBuffer->variants.at(cmd.offset).value(); return tr("glyphs: %1, font: %2").arg( QString::number((variants.size() - 1) / 2), VariantHandler::displayString(variants.at(0))); } } return QString(); } QVariant PaintBufferModel::argumentDecoration(const QPaintBufferCommand &cmd) const { switch (cmd.id) { case QPaintBufferPrivate::Cmd_SetBrush: case QPaintBufferPrivate::Cmd_SetPen: case QPaintBufferPrivate::Cmd_DrawPixmapRect: case QPaintBufferPrivate::Cmd_DrawPixmapPos: case QPaintBufferPrivate::Cmd_DrawTiledPixmap: case QPaintBufferPrivate::Cmd_DrawImageRect: case QPaintBufferPrivate::Cmd_DrawImagePos: return VariantHandler::decoration(m_privateBuffer->variants.at(cmd.offset)); case QPaintBufferPrivate::Cmd_FillRectBrush: case QPaintBufferPrivate::Cmd_FillVectorPath: case QPaintBufferPrivate::Cmd_StrokeVectorPath: case QPaintBufferPrivate::Cmd_FillRectColor: return VariantHandler::decoration(m_privateBuffer->variants.at(cmd.extra)); } return QVariant(); } QVariant PaintBufferModel::data(const QModelIndex &index, int role) const { if (!index.isValid() || !m_privateBuffer) return QVariant(); if (!index.parent().isValid()) { const auto cmd = m_privateBuffer->commands.at(index.row()); switch (role) { case Qt::DisplayRole: if (index.column() == 0) return cmdTypes[cmd.id].name; else if (index.column() == 1) return argumentDisplayString(cmd); else if (index.column() == 2 && m_costs.size() > index.row()) return m_costs.at(index.row()); break; case Qt::DecorationRole: if (index.column() == 1) return argumentDecoration(cmd); break; case Qt::EditRole: // for fancy matrix rendering in the client if (index.column() == 1 && cmd.id == QPaintBufferPrivate::Cmd_SetTransform) return m_privateBuffer->variants.at(cmd.offset); break; case PaintBufferModelRoles::ValueRole: return argumentAt(cmd, 0); case PaintBufferModelRoles::ClipPathRole: return QVariant::fromValue(clipPath(index.row())); case PaintBufferModelRoles::MaxCostRole: if (index.column() == 2 && index.row() == 0) return m_maxCost; break; case PaintBufferModelRoles::ObjectIdRole: return QVariant::fromValue(m_buffer.origin(index.row())); } } else { const auto cmd = m_privateBuffer->commands.at(index.internalId()); switch (role) { case Qt::DisplayRole: if (index.column() == 0) return cmd_argument_names[cmdTypes[cmd.id].argumentName + index.row()]; else if (index.column() == 1) return VariantHandler::displayString(argumentAt(cmd, index.row())); break; case PaintBufferModelRoles::ValueRole: return argumentAt(cmd, index.row()); } } return QVariant(); } QMap PaintBufferModel::itemData(const QModelIndex &index) const { QMap d = QAbstractItemModel::itemData(index); d.insert(PaintBufferModelRoles::MaxCostRole, data(index, PaintBufferModelRoles::MaxCostRole)); d.insert(PaintBufferModelRoles::ObjectIdRole, data(index, PaintBufferModelRoles::ObjectIdRole)); return d; } int PaintBufferModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); return 3; } int PaintBufferModel::rowCount(const QModelIndex &parent) const { if (!m_privateBuffer) return 0; if (parent.isValid()) { const auto cmd = m_privateBuffer->commands.at(parent.row()); return cmdTypes[cmd.id].argumentCount; } return m_privateBuffer->commands.size(); } QModelIndex PaintBufferModel::index(int row, int column, const QModelIndex& parent) const { if (!parent.isValid()) return createIndex(row, column, TopLevelId); return createIndex(row, column, parent.row()); } QModelIndex PaintBufferModel::parent(const QModelIndex& child) const { if (child.internalId() == TopLevelId) return {}; return createIndex(child.internalId(), 0, TopLevelId); } QPainterPath PaintBufferModel::clipPath(int row) const { QPainterPath clip; QTransform t; std::vector clipStack; std::vector transformStack; for (int i = 0; i <= row; ++i) { const auto cmd = m_privateBuffer->commands.at(i); QPainterPath p; Qt::ClipOperation op = Qt::NoClip; switch (cmd.id) { case QPaintBufferPrivate::Cmd_Save: clipStack.push_back(clip); transformStack.push_back(t); continue; case QPaintBufferPrivate::Cmd_Restore: if (clipStack.empty() || transformStack.empty()) return QPainterPath(); clip = clipStack.back(); clipStack.pop_back(); t = transformStack.back(); transformStack.pop_back(); continue; case QPaintBufferPrivate::Cmd_SetTransform: t = m_privateBuffer->variants.at(cmd.offset).value(); continue; case QPaintBufferPrivate::Cmd_Translate: t.translate(m_privateBuffer->floats.at(cmd.extra), m_privateBuffer->floats.at(cmd.extra + 1)); continue; case QPaintBufferPrivate::Cmd_ClipRect: p.addRect(QRect(QPoint(m_privateBuffer->ints.at(cmd.offset), m_privateBuffer->ints.at(cmd.offset + 1)), QPoint(m_privateBuffer->ints.at(cmd.offset + 2), m_privateBuffer->ints.at(cmd.offset + 3)))); p = t.map(p); op = static_cast(cmd.extra); break; case QPaintBufferPrivate::Cmd_ClipRegion: p.addRegion(m_privateBuffer->variants.at(cmd.offset).value()); p = t.map(p); op = static_cast(cmd.extra); break; case QPaintBufferPrivate::Cmd_ClipPath: p = m_privateBuffer->variants.at(cmd.offset).value(); p = t.map(p); op = static_cast(cmd.extra); break; case QPaintBufferPrivate::Cmd_ClipVectorPath: p = QVectorPath(m_privateBuffer->floats.constData() + cmd.offset, cmd.size, cmd.offset2 & 0x80000000 ? nullptr : reinterpret_cast(m_privateBuffer->ints.constData() + cmd.offset2 + 1), *(m_privateBuffer->ints.constData() + (cmd.offset2 & 0x7FFFFFFF))).convertToPainterPath(); p = t.map(p); op = static_cast(cmd.extra); break; case QPaintBufferPrivate::Cmd_SystemStateChanged: p.addRegion(m_privateBuffer->variants.at(cmd.offset).value()); op = Qt::ReplaceClip; break; default: continue; } switch (op) { case Qt::NoClip: clip = QPainterPath(); break; case Qt::ReplaceClip: clip = p; break; case Qt::IntersectClip: clip = clip.intersected(p); break; } } return clip; } gammaray-2.11.3/core/paintbuffermodel.h000066400000000000000000000052271413310443000200220ustar00rootroot00000000000000/* paintbuffermodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2012-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_WIDGETINSPECTOR_PAINTBUFFERMODEL_H #define GAMMARAY_WIDGETINSPECTOR_PAINTBUFFERMODEL_H #include #include "paintbuffer.h" #include #include QT_BEGIN_NAMESPACE struct QPaintBufferCommand; class QPainterPath; QT_END_NAMESPACE namespace GammaRay { /** * Model that shows commands stored in a QPaintBuffer. */ class PaintBufferModel : public QAbstractItemModel { Q_OBJECT public: explicit PaintBufferModel(QObject *parent = nullptr); void setPaintBuffer(const PaintBuffer &buffer); PaintBuffer buffer() const; void setCosts(const QVector &costs); QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; QMap itemData(const QModelIndex &index) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; QModelIndex index(int row, int column, const QModelIndex & parent) const override; QModelIndex parent(const QModelIndex & child) const override; private: QVariant argumentAt(const QPaintBufferCommand &cmd, int index) const; QString argumentDisplayString(const QPaintBufferCommand &cmd) const; QVariant argumentDecoration(const QPaintBufferCommand &cmd) const; QPainterPath clipPath(int row) const; PaintBuffer m_buffer; QPaintBufferPrivate *m_privateBuffer; QVector m_costs; double m_maxCost; }; } #endif // GAMMARAY_PAINTBUFFERMODEL_H gammaray-2.11.3/core/painterprofilingreplayer.cpp000066400000000000000000000066461413310443000221550ustar00rootroot00000000000000/* painterprofilingreplayer.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include "painterprofilingreplayer.h" #include #include #include using namespace GammaRay; namespace GammaRay { class Replayer : public QPaintEngineExReplayer { public: explicit Replayer(const PaintBuffer *buffer, QPainter *p) { d = buffer->data(); painter = p; } void process(const QPaintBufferCommand &cmd) override { if (painter->paintEngine()->isExtended()) QPaintEngineExReplayer::process(cmd); else QPainterReplayer::process(cmd); } }; } PainterProfilingReplayer::PainterProfilingReplayer() = default; PainterProfilingReplayer::~PainterProfilingReplayer() = default; void PainterProfilingReplayer::profile(const PaintBuffer& buffer) { const auto sourceSize = buffer.boundingRect().size().toSize(); if (sourceSize.width() <= 0 || sourceSize.height() <= 0) return; #if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0) const auto ratio = buffer.devicePixelRatioF(); #else const auto ratio = buffer.devicePixelRatio(); #endif QImage image(sourceSize * ratio, QImage::Format_ARGB32); // TODO use the right format, this has considerable impact on performance! image.setDevicePixelRatio(ratio); image.fill(Qt::transparent); QPainter p(&image); Replayer replayer(&buffer, &p); auto d = buffer.data(); const auto cmdSize = d->commands.size(); const auto runs = 5; std::unique_ptr samples(new double[cmdSize * runs]); for (int run = 0; run < runs; ++run) { for (int i = 0; i < cmdSize; ++i) { const auto &cmd = d->commands.at(i); QElapsedTimer t; t.start(); replayer.process(cmd); samples[i * runs + run] = t.nsecsElapsed(); } } m_costs.reserve(cmdSize); for (int i = 0; i < cmdSize; ++i) { std::nth_element(samples.get() + i * runs, samples.get() + i * runs + runs/2, samples.get() + i * runs + runs); m_costs.push_back(samples[i * runs + runs/2]); } const auto sum = std::accumulate(m_costs.constBegin(), m_costs.constEnd(), 0.0); std::for_each(m_costs.begin(), m_costs.end(), [sum](double &c) { c = 100.0 * c / sum; }); } QVector PainterProfilingReplayer::costs() const { return m_costs; } gammaray-2.11.3/core/painterprofilingreplayer.h000066400000000000000000000030371413310443000216110ustar00rootroot00000000000000/* painterprofilingreplayer.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_PAINTERPROFILINGREPLAYER_H #define GAMMARAY_PAINTERPROFILINGREPLAYER_H #include "paintbuffer.h" namespace GammaRay { class PainterProfilingReplayer { public: PainterProfilingReplayer(); ~PainterProfilingReplayer(); void profile(const PaintBuffer &buffer); QVector costs() const; private: QVector m_costs; }; } #endif // GAMMARAY_PAINTERPROFILINGREPLAYER_H gammaray-2.11.3/core/probe.cpp000066400000000000000000000775651413310443000161540ustar00rootroot00000000000000/* probe.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Author: Stephen Kelly Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ // krazy:excludeall=null,captruefalse,staticobjects #include #include "probe.h" #include "enumrepositoryserver.h" #include "execution.h" #include "classesiconsrepositoryserver.h" #include "metaobjectrepository.h" #include "objectlistmodel.h" #include "objecttreemodel.h" #include "probesettings.h" #include "probecontroller.h" #include "problemcollector.h" #include "toolmanager.h" #include "toolpluginmodel.h" #include "util.h" #include "varianthandler.h" #include "metaobjectregistry.h" #include "remote/server.h" #include "remote/remotemodelserver.h" #include "remote/serverproxymodel.h" #include "remote/selectionmodelserver.h" #include "toolpluginerrormodel.h" #include "probeguard.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define IF_DEBUG(x) #ifdef ENABLE_EXPENSIVE_ASSERTS #define EXPENSIVE_ASSERT(x) Q_ASSERT(x) #else #define EXPENSIVE_ASSERT(x) #endif using namespace GammaRay; using namespace std; QAtomicPointer Probe::s_instance = QAtomicPointer(nullptr); namespace GammaRay { static void signal_begin_callback(QObject *caller, int method_index, void **argv) { if (method_index == 0 || !Probe::instance() || Probe::instance()->filterObject(caller)) return; method_index = Util::signalIndexToMethodIndex(caller->metaObject(), method_index); Probe::executeSignalCallback([=](const SignalSpyCallbackSet &callbacks) { if (callbacks.signalBeginCallback) callbacks.signalBeginCallback(caller, method_index, argv); }); } static void signal_end_callback(QObject *caller, int method_index) { if (method_index == 0 || !Probe::instance()) return; QMutexLocker locker(Probe::objectLock()); if (!Probe::instance()->isValidObject(caller)) // implies filterObject() return; // deleted in the slot locker.unlock(); method_index = Util::signalIndexToMethodIndex(caller->metaObject(), method_index); Probe::executeSignalCallback([=](const SignalSpyCallbackSet &callbacks) { if (callbacks.signalEndCallback) callbacks.signalEndCallback(caller, method_index); }); } static void slot_begin_callback(QObject *caller, int method_index, void **argv) { if (method_index == 0 || !Probe::instance() || Probe::instance()->filterObject(caller)) return; Probe::executeSignalCallback([=](const SignalSpyCallbackSet &callbacks) { if (callbacks.slotBeginCallback) callbacks.slotBeginCallback(caller, method_index, argv); }); } static void slot_end_callback(QObject *caller, int method_index) { if (method_index == 0 || !Probe::instance()) return; QMutexLocker locker(Probe::objectLock()); if (!Probe::instance()->isValidObject(caller)) // implies filterObject() return; // deleted in the slot locker.unlock(); Probe::executeSignalCallback([=](const SignalSpyCallbackSet &callbacks) { if (callbacks.slotEndCallback) callbacks.slotEndCallback(caller, method_index); }); } static QItemSelectionModel *selectionModelFactory(QAbstractItemModel *model) { Q_ASSERT(!model->objectName().isEmpty()); return new SelectionModelServer(model->objectName() + ".selection", model, Probe::instance()); } } // useful for debugging, dumps the object and all it's parents // also usable from GDB! void dumpObject(QObject *obj) { if (!obj) { cout << "QObject(0x0)" << endl; return; } const std::ios::fmtflags oldFlags(cout.flags()); do { cout << obj->metaObject()->className() << "(" << hex << obj << ")"; obj = obj->parent(); if (obj) cout << " <- "; } while (obj); cout << endl; cout.flags(oldFlags); } struct Listener { Listener() = default; bool trackDestroyed = true; QVector addedBeforeProbeInstance; QHash constructionBacktracesForObjects; }; Q_GLOBAL_STATIC(Listener, s_listener) // ensures proper information is returned by isValidObject by // locking it in objectAdded/Removed Q_GLOBAL_STATIC_WITH_ARGS(QMutex, s_lock, (QMutex::Recursive)) Probe::Probe(QObject *parent) : QObject(parent) , m_objectListModel(new ObjectListModel(this)) , m_objectTreeModel(new ObjectTreeModel(this)) , m_window(nullptr) , m_metaObjectRegistry(new MetaObjectRegistry(this)) , m_queueTimer(new QTimer(this)) , m_server(nullptr) { Q_ASSERT(thread() == qApp->thread()); IF_DEBUG(cout << "attaching GammaRay probe" << endl;) StreamOperators::registerOperators(); ProbeSettings::receiveSettings(); m_server = new Server(this); ObjectBroker::setSelectionModelFactoryCallback(selectionModelFactory); ObjectBroker::registerObject(new ProbeController(this)); m_toolManager = new ToolManager(this); ObjectBroker::registerObject(m_toolManager); m_problemCollector = new ProblemCollector(this); ObjectBroker::registerObject(EnumRepositoryServer::create(this)); ClassesIconsRepositoryServer::create(this); registerModel(QStringLiteral("com.kdab.GammaRay.ObjectTree"), m_objectTreeModel); registerModel(QStringLiteral("com.kdab.GammaRay.ObjectList"), m_objectListModel); ToolPluginModel *toolPluginModel = new ToolPluginModel( m_toolManager->toolPluginManager()->plugins(), this); registerModel(QStringLiteral("com.kdab.GammaRay.ToolPluginModel"), toolPluginModel); ToolPluginErrorModel *toolPluginErrorModel = new ToolPluginErrorModel(m_toolManager->toolPluginManager()->errors(), this); registerModel(QStringLiteral("com.kdab.GammaRay.ToolPluginErrorModel"), toolPluginErrorModel); m_queueTimer->setSingleShot(true); m_queueTimer->setInterval(0); connect(m_queueTimer, &QTimer::timeout, this, &Probe::processQueuedObjectChanges); #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) m_previousSignalSpyCallbackSet = qt_signal_spy_callback_set.load(); #else const auto* signal_spy_set = &qt_signal_spy_callback_set; if (signal_spy_set) { m_previousSignalSpyCallbackSet.signalBeginCallback = signal_spy_set->signal_begin_callback; m_previousSignalSpyCallbackSet.signalEndCallback = signal_spy_set->signal_end_callback; m_previousSignalSpyCallbackSet.slotBeginCallback = signal_spy_set->slot_begin_callback; m_previousSignalSpyCallbackSet.slotEndCallback = signal_spy_set->slot_end_callback; registerSignalSpyCallbackSet(m_previousSignalSpyCallbackSet); // daisy-chain existing callbacks } #endif connect(this, &Probe::objectCreated, m_metaObjectRegistry, &MetaObjectRegistry::objectAdded); connect(this, &Probe::objectDestroyed, m_metaObjectRegistry, &MetaObjectRegistry::objectRemoved); } Probe::~Probe() { emit aboutToDetach(); IF_DEBUG(cerr << "detaching GammaRay probe" << endl; ) #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) qt_register_signal_spy_callbacks(m_previousSignalSpyCallbackSet); #else QSignalSpyCallbackSet prevCallbacks = { m_previousSignalSpyCallbackSet.signalBeginCallback, m_previousSignalSpyCallbackSet.slotBeginCallback, m_previousSignalSpyCallbackSet.signalEndCallback, m_previousSignalSpyCallbackSet.slotEndCallback }; qt_register_signal_spy_callbacks(prevCallbacks); #endif ObjectBroker::clear(); ProbeSettings::resetLauncherIdentifier(); MetaObjectRepository::instance()->clear(); VariantHandler::clear(); s_instance = QAtomicPointer(nullptr); } void Probe::setWindow(QObject *window) { m_window = window; } QObject *Probe::window() const { return m_window; } MetaObjectRegistry *Probe::metaObjectRegistry() const { return m_metaObjectRegistry; } Probe *GammaRay::Probe::instance() { return s_instance.load(); } bool Probe::isInitialized() { return instance(); } bool Probe::canShowWidgets() { return QCoreApplication::instance()->inherits("QApplication"); } void Probe::createProbe(bool findExisting) { Q_ASSERT(qApp); Q_ASSERT(!isInitialized()); // first create the probe and its children // we must not hold the object lock here as otherwise we can deadlock // with other QObject's we create and other threads are using. One // example are QAbstractSocketEngine. IF_DEBUG(cout << "setting up new probe instance" << endl; ) Probe *probe = nullptr; { ProbeGuard guard; probe = new Probe; } IF_DEBUG(cout << "done setting up new probe instance" << endl; ) connect(qApp, &QCoreApplication::aboutToQuit, probe, &Probe::shutdown); // Our safety net, if there's no call to QCoreApplication::exec() we'll never receive the aboutToQuit() signal // Make sure we still cleanup safely after the application instance got destroyed connect(qApp, &QObject::destroyed, probe, &Probe::shutdown); // now we can get the lock and add items which where added before this point in time { QMutexLocker lock(s_lock()); // now we set the instance while holding the lock, // all future calls to object{Added,Removed} will // act directly on the data structures there instead // of using addedBeforeProbeInstance // this will only happen _after_ the object lock above is released though Q_ASSERT(!instance()); s_instance = QAtomicPointer(probe); // add objects to the probe that were tracked before its creation foreach (QObject *obj, s_listener()->addedBeforeProbeInstance) { objectAdded(obj); } s_listener()->addedBeforeProbeInstance.clear(); // try to find existing objects by other means if (findExisting) probe->findExistingObjects(); } // eventually initialize the rest QMetaObject::invokeMethod(probe, "delayedInit", Qt::QueuedConnection); } void Probe::resendServerAddress() { Q_ASSERT(isInitialized()); Q_ASSERT(m_server); if (!m_server->isListening()) // already connected return; ProbeSettings::receiveSettings(); ProbeSettings::sendServerAddress(m_server->externalAddress()); } void Probe::startupHookReceived() { #ifdef Q_OS_ANDROID QDir root = QDir::home(); root.cdUp(); Paths::setRootPath(root.absolutePath()); #endif s_listener()->trackDestroyed = false; } void Probe::delayedInit() { QCoreApplication::instance()->installEventFilter(this); QString appName = qApp->applicationName(); if (appName.isEmpty() && !qApp->arguments().isEmpty()) { appName = qApp->arguments().first().remove(qApp->applicationDirPath()); if (appName.startsWith('.')) appName = appName.right(appName.length() - 1); if (appName.startsWith('/')) appName = appName.right(appName.length() - 1); } if (appName.isEmpty()) appName = tr("PID %1").arg(qApp->applicationPid()); m_server->setLabel(appName); // The applicationName might be translated, so let's go with the application file base name m_server->setKey(QFileInfo(qApp->applicationFilePath()).completeBaseName()); m_server->setPid(qApp->applicationPid()); if (ProbeSettings::value(QStringLiteral("RemoteAccessEnabled"), true).toBool()) { const auto serverStarted = m_server->listen(); if (serverStarted) { ProbeSettings::sendServerAddress(m_server->externalAddress()); } else { ProbeSettings::sendServerLaunchError(m_server->errorString()); } } if (ProbeSettings::value(QStringLiteral("InProcessUi"), false).toBool()) showInProcessUi(); } void Probe::shutdown() { delete this; } void Probe::showInProcessUi() { if (!canShowWidgets()) { cerr << "Unable to show in-process UI in a non-QWidget based application." << endl; return; } IF_DEBUG(cout << "creating GammaRay::MainWindow" << endl;) ProbeGuard guard; QLibrary lib; foreach (auto path, Paths::pluginPaths(GAMMARAY_PROBE_ABI)) { path += QStringLiteral("/gammaray_inprocessui"); #if defined(GAMMARAY_INSTALL_QT_LAYOUT) path += '-'; path += GAMMARAY_PROBE_ABI; #else #if !defined(Q_OS_MAC) #if defined(QT_DEBUG) path += QStringLiteral(GAMMARAY_DEBUG_POSTFIX); #endif #endif #endif lib.setFileName(path); if (lib.load()) break; } if (!lib.isLoaded()) { std::cerr << "Failed to load in-process UI module: " << qPrintable(lib.errorString()) << std::endl; } else { void (*factory)() = reinterpret_cast(lib.resolve("gammaray_create_inprocess_mainwindow")); if (!factory) std::cerr << Q_FUNC_INFO << ' ' << qPrintable(lib.errorString()) << endl; else factory(); } IF_DEBUG(cout << "creation done" << endl; ) } bool Probe::filterObject(QObject *obj) const { QSet visitedObjects; int iteration = 0; QObject *o = obj; do { if (iteration > 100) { // Probably we have a loop in the tree. Do loop detection. if (visitedObjects.contains(o)) { std::cerr << "We detected a loop in the object tree for object " << o; if (!o->objectName().isEmpty()) std::cerr << " \"" << qPrintable(o->objectName()) << "\""; std::cerr << " (" << o->metaObject()->className() << ")." << std::endl; return true; } visitedObjects << o; } ++iteration; if (o == this || o == window() || (qstrncmp(o->metaObject()->className(), "GammaRay::", 10) == 0)) { return true; } o = o->parent(); } while (o); return false; } void Probe::registerModel(const QString &objectName, QAbstractItemModel *model) { auto *ms = new RemoteModelServer(objectName, model); ms->setModel(model); ObjectBroker::registerModelInternal(objectName, model); } QAbstractItemModel *Probe::objectListModel() const { return m_objectListModel; } const QVector &Probe::allQObjects() const { return m_objectListModel->objects(); } QAbstractItemModel *Probe::objectTreeModel() const { return m_objectTreeModel; } ProblemCollector *Probe::problemCollector() const { return m_problemCollector; } bool Probe::isValidObject(const QObject *obj) const { ///TODO: can we somehow assert(s_lock().isLocked()) ?! /// -> Not with a recursive mutex. Make it non-recursive, and you can do Q_ASSERT(!s_lock().tryLock()); return m_validObjects.contains(obj); } QMutex *Probe::objectLock() { return s_lock(); } /* * We need to handle 4 different cases in here: * (1) our thread, from ctor: * - wait until next event-loop re-entry of our thread * - emit objectCreated if object still valid * (2) our thread, after ctor: * - emit objectCreated right away * (3) other thread, from ctor: * - wait until next event-loop re-entry in other thread (FIXME: we do not currently do this!!) * - post information to our thread * (4) other thread, after ctor: * - post information to our thread * - emit objectCreated there right away if object still valid * * Pre-conditions: lock may or may not be held already, arbitrary thread */ void Probe::objectAdded(QObject *obj, bool fromCtor) { QMutexLocker lock(s_lock()); // attempt to ignore objects created by GammaRay itself, especially short-lived ones if (fromCtor && ProbeGuard::insideProbe() && obj->thread() == QThread::currentThread()) return; // ignore objects created when global statics are already getting destroyed (on exit) if (s_listener.isDestroyed()) return; if (Execution::hasFastStackTrace() && fromCtor) { s_listener()->constructionBacktracesForObjects.insert(obj, Execution::stackTrace(32, 2)); // skip 2: this and the hook function calling us } if (!isInitialized()) { IF_DEBUG(cout << "objectAdded Before: " << hex << obj << (fromCtor ? " (from ctor)" : "") << endl; ) s_listener()->addedBeforeProbeInstance << obj; return; } if (instance()->filterObject(obj)) { IF_DEBUG(cout << "objectAdded Filter: " << hex << obj << (fromCtor ? " (from ctor)" : "") << endl; ) return; } if (instance()->m_validObjects.contains(obj)) { // this happens when we get a child event before the objectAdded call from the ctor // or when we add an item from addedBeforeProbeInstance who got added already // due to the add-parent-before-child logic IF_DEBUG(cout << "objectAdded Known: " << hex << obj << (fromCtor ? " (from ctor)" : "") << endl; ) return; } // make sure we already know the parent if (obj->parent() && !instance()->m_validObjects.contains(obj->parent())) objectAdded(obj->parent(), fromCtor); Q_ASSERT(!obj->parent() || instance()->m_validObjects.contains(obj->parent())); instance()->m_validObjects << obj; if (!fromCtor && obj->parent() && instance()->isObjectCreationQueued(obj->parent())) { // when a child event triggers a call to objectAdded while inside the ctor // the parent is already tracked but it's call to objectFullyConstructed // was delayed. hence we must do the same for the child for integrity fromCtor = true; } IF_DEBUG(cout << "objectAdded: " << hex << obj << (fromCtor ? " (from ctor)" : "") << ", p: " << obj->parent() << endl; ) if (fromCtor) instance()->queueCreatedObject(obj); else instance()->objectFullyConstructed(obj); } // pre-conditions: lock may or may not be held already, our thread void Probe::processQueuedObjectChanges() { QMutexLocker lock(s_lock()); IF_DEBUG(cout << Q_FUNC_INFO << " " << m_queuedObjectChanges.size() << endl; ) // must be called from the main thread via timeout Q_ASSERT(QThread::currentThread() == thread()); const auto queuedObjectChanges = m_queuedObjectChanges; // copy, in case this gets modified while we iterate (which can actually happen) for (const auto &change : queuedObjectChanges) { switch (change.type) { case ObjectChange::Create: objectFullyConstructed(change.obj); break; case ObjectChange::Destroy: emit objectDestroyed(change.obj); break; } } IF_DEBUG(cout << Q_FUNC_INFO << " done" << endl; ) m_queuedObjectChanges.clear(); for (QObject *obj : qAsConst(m_pendingReparents)) { if (!isValidObject(obj)) continue; if (filterObject(obj)) // the move might have put it under a hidden parent objectRemoved(obj); else emit objectReparented(obj); } m_pendingReparents.clear(); } // pre-condition: lock is held already, our thread void Probe::objectFullyConstructed(QObject *obj) { Q_ASSERT(thread() == QThread::currentThread()); if (!m_validObjects.contains(obj)) { // deleted already IF_DEBUG(cout << "stale fully constructed: " << hex << obj << endl; ) return; } if (filterObject(obj)) { // when the call was delayed from the ctor construction, // the parent might not have been set properly yet. hence // apply the filter again m_validObjects.remove(obj); IF_DEBUG(cout << "now filtered fully constructed: " << hex << obj << endl; ) return; } IF_DEBUG(cout << "fully constructed: " << hex << obj << endl; ) // ensure we know all our ancestors already for (QObject *parent = obj->parent(); parent; parent = parent->parent()) { if (!m_validObjects.contains(parent)) { objectAdded(parent); // will also handle any further ancestors break; } } Q_ASSERT(!obj->parent() || m_validObjects.contains(obj->parent())); m_toolManager->objectAdded(obj); emit objectCreated(obj); } /* * We have two cases to consider here: * (1) our thread: * - emit objectDestroyed() right away * (2) other thread: * - post information to our thread, emit objectDestroyed() there * * pre-conditions: arbitrary thread, lock may or may not be held already */ void Probe::objectRemoved(QObject *obj) { QMutexLocker lock(s_lock()); if (!isInitialized()) { IF_DEBUG(cout << "objectRemoved Before: " << hex << obj << " have statics: " << s_listener() << endl; ) if (!s_listener()) return; QVector &addedBefore = s_listener()->addedBeforeProbeInstance; for (auto it = addedBefore.begin(); it != addedBefore.end();) { if (*it == obj) it = addedBefore.erase(it); else ++it; } return; } IF_DEBUG(cout << "object removed:" << hex << obj << " " << obj->parent() << endl; ) bool success = instance()->m_validObjects.remove(obj); if (!success) { // object was not tracked by the probe, probably a gammaray object EXPENSIVE_ASSERT(!instance()->isObjectCreationQueued(obj)); return; } instance()->purgeChangesForObject(obj); EXPENSIVE_ASSERT(!instance()->isObjectCreationQueued(obj)); if (instance()->thread() == QThread::currentThread()) emit instance()->objectDestroyed(obj); else instance()->queueDestroyedObject(obj); } void Probe::handleObjectDestroyed(QObject *obj) { objectRemoved(obj); } // pre-condition: we have the lock, arbitrary thread void Probe::queueCreatedObject(QObject *obj) { EXPENSIVE_ASSERT(!isObjectCreationQueued(obj)); ObjectChange c; c.obj = obj; c.type = ObjectChange::Create; m_queuedObjectChanges.push_back(c); notifyQueuedObjectChanges(); } // pre-condition: we have the lock, arbitrary thread void Probe::queueDestroyedObject(QObject *obj) { ObjectChange c; c.obj = obj; c.type = ObjectChange::Destroy; m_queuedObjectChanges.push_back(c); notifyQueuedObjectChanges(); } // pre-condition: we have the lock, arbitrary thread bool Probe::isObjectCreationQueued(QObject *obj) const { return std::find_if(m_queuedObjectChanges.begin(), m_queuedObjectChanges.end(), [obj](const ObjectChange &c) { return c.obj == obj && c.type == Probe::ObjectChange::Create; }) != m_queuedObjectChanges.end(); } // pre-condition: we have the lock, arbitrary thread void Probe::purgeChangesForObject(QObject *obj) { for (int i = 0; i < m_queuedObjectChanges.size(); ++i) { if (m_queuedObjectChanges.at(i).obj == obj && m_queuedObjectChanges.at(i).type == ObjectChange::Create) { m_queuedObjectChanges.remove(i); return; } } } // pre-condition: we have the lock, arbitrary thread void Probe::notifyQueuedObjectChanges() { if (m_queueTimer->isActive()) return; if (thread() == QThread::currentThread()) { m_queueTimer->start(); } else { static QMetaMethod m; if (m.methodIndex() < 0) { const auto idx = QTimer::staticMetaObject.indexOfMethod("start()"); Q_ASSERT(idx >= 0); m = QTimer::staticMetaObject.method(idx); Q_ASSERT(m.methodIndex() >= 0); } m.invoke(m_queueTimer, Qt::QueuedConnection); } } bool Probe::eventFilter(QObject *receiver, QEvent *event) { if (ProbeGuard::insideProbe() && receiver->thread() == QThread::currentThread()) return QObject::eventFilter(receiver, event); if (event->type() == QEvent::ChildAdded || event->type() == QEvent::ChildRemoved) { QChildEvent *childEvent = static_cast(event); QObject *obj = childEvent->child(); QMutexLocker lock(s_lock()); const bool tracked = m_validObjects.contains(obj); const bool filtered = filterObject(obj); IF_DEBUG(cout << "child event: " << hex << obj << ", p: " << obj->parent() << dec << ", tracked: " << tracked << ", filtered: " << filtered << ", type: " << (childEvent->added() ? "added" : "removed") << endl; ) if (!filtered && childEvent->added()) { if (!tracked) { // was not tracked before, add to all models // child added events are sent before qt_addObject is called, // so we assumes this comes from the ctor objectAdded(obj, true); } else if (!isObjectCreationQueued(obj) && !isObjectCreationQueued(obj->parent())) { // object is known already, just update the position in the tree // BUT: only when we did not queue this item before IF_DEBUG(cout << "update pos: " << hex << obj << endl; ) m_pendingReparents.removeAll(obj); emit objectReparented(obj); } } else if (tracked) { // defer processing this until we know its final location m_pendingReparents.push_back(obj); notifyQueuedObjectChanges(); } } // widget only unfortunately, but more precise than ChildAdded/Removed... if (event->type() == QEvent::ParentChange) { QMutexLocker lock(s_lock()); const bool tracked = m_validObjects.contains(receiver); const bool filtered = filterObject(receiver); if (!filtered && tracked && !isObjectCreationQueued(receiver) && !isObjectCreationQueued(receiver->parent())) { m_pendingReparents.removeAll(receiver); emit objectReparented(receiver); } } // we have no preloading hooks, so recover all objects we see if (needsObjectDiscovery() && event->type() != QEvent::ChildAdded && event->type() != QEvent::ChildRemoved && event->type() != QEvent::ParentChange // already handled above && event->type() != QEvent::Destroy && event->type() != QEvent::WinIdChange // unsafe since emitted from dtors && !filterObject(receiver)) { QMutexLocker lock(s_lock()); const bool tracked = m_validObjects.contains(receiver); if (!tracked) discoverObject(receiver); } // filters provided by plugins if (!filterObject(receiver)) { for (QObject *filter : qAsConst(m_globalEventFilters)) { filter->eventFilter(receiver, event); } } return QObject::eventFilter(receiver, event); } void Probe::findExistingObjects() { discoverObject(QCoreApplication::instance()); if (auto guiApp = qobject_cast(QCoreApplication::instance())) { foreach (auto window, guiApp->allWindows()) { discoverObject(window); } } } void Probe::discoverObject(QObject *object) { if (!object) return; QMutexLocker lock(s_lock()); if (m_validObjects.contains(object)) return; objectAdded(object); foreach (QObject *child, object->children()) { discoverObject(child); } } void Probe::installGlobalEventFilter(QObject *filter) { Q_ASSERT(!m_globalEventFilters.contains(filter)); m_globalEventFilters.push_back(filter); } bool Probe::needsObjectDiscovery() const { return s_listener()->trackDestroyed; } bool Probe::hasReliableObjectTracking() const { return true; // qHooks available, which works independent of the injector used } void Probe::selectObject(QObject *object, const QPoint &pos) { const auto tools = m_toolManager->toolsForObject(object); m_toolManager->selectTool(tools.value(0)); emit objectSelected(object, pos); } void Probe::selectObject(QObject *object, const QString &toolId, const QPoint &pos) { if (!m_toolManager->hasTool(toolId)) { std::cerr << "Invalid tool id: " << qPrintable(toolId) << std::endl; return; } m_toolManager->selectTool(toolId); emit objectSelected(object, pos); } void Probe::selectObject(void *object, const QString &typeName) { const auto tools = m_toolManager->toolsForObject(object, typeName); const QString toolId = tools.value(0); if (!m_toolManager->hasTool(toolId)) { std::cerr << "Invalid tool id: " << qPrintable(toolId) << std::endl; return; } m_toolManager->selectTool(tools.value(0)); emit nonQObjectSelected(object, typeName); } void Probe::registerSignalSpyCallbackSet(const SignalSpyCallbackSet &callbacks) { if (callbacks.isNull()) return; m_signalSpyCallbacks.push_back(callbacks); setupSignalSpyCallbacks(); } void Probe::setupSignalSpyCallbacks() { // memory management is with us for Qt >= 5.14, therefore static here! static QSignalSpyCallbackSet cbs = { nullptr, nullptr, nullptr, nullptr }; foreach (const auto &it, m_signalSpyCallbacks) { if (it.signalBeginCallback) cbs.signal_begin_callback = signal_begin_callback; if (it.signalEndCallback) cbs.signal_end_callback = signal_end_callback; if (it.slotBeginCallback) cbs.slot_begin_callback = slot_begin_callback; if (it.slotEndCallback) cbs.slot_end_callback = slot_end_callback; } #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) qt_register_signal_spy_callbacks(&cbs); #else qt_register_signal_spy_callbacks(cbs); #endif } template void Probe::executeSignalCallback(const Func &func) { std::for_each(instance()->m_signalSpyCallbacks.constBegin(), instance()->m_signalSpyCallbacks.constEnd(), func); } SourceLocation Probe::objectCreationSourceLocation(QObject *object) const { if (!s_listener()->constructionBacktracesForObjects.contains(object)) { IF_DEBUG(std::cout << "No backtrace for object available" << object << "." << std::endl;) return SourceLocation(); } const auto &st = s_listener()->constructionBacktracesForObjects.value(object); int distanceToQObject = 0; const QMetaObject *metaObject = object->metaObject(); while (metaObject && metaObject != &QObject::staticMetaObject) { distanceToQObject++; metaObject = metaObject->superClass(); } const auto frame = Execution::resolveOne(st, distanceToQObject + 1); return frame.location; } Execution::Trace Probe::objectCreationStackTrace(QObject *object) const { return s_listener()->constructionBacktracesForObjects.value(object); } gammaray-2.11.3/core/probe.h000066400000000000000000000263521413310443000156050ustar00rootroot00000000000000/* probe.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_PROBE_H #define GAMMARAY_PROBE_H #include "gammaray_core_export.h" #include "signalspycallbackset.h" #include #include #include #include #include #include #include QT_BEGIN_NAMESPACE class QAbstractItemModel; class QItemSelectionModel; class QModelIndex; class QThread; class QTimer; class QMutex; struct QSignalSpyCallbackSet; QT_END_NAMESPACE namespace GammaRay { class ProbeCreator; class ObjectListModel; class ObjectTreeModel; class MainWindow; class BenchSuite; class Server; class ToolManager; class ProblemCollector; class MetaObjectRegistry; namespace Execution { class Trace; } /*! * Central entity of GammaRay: The probe is tracking the Qt application under test * * @note The Probe lifetime is strongly coupled with the QCoreApplication lifetime, if there's * no QCoreApplication instance, then there's no probe. * * To get a hold of the probe, call Probe::instance() */ class GAMMARAY_CORE_EXPORT Probe : public QObject { Q_OBJECT public: ~Probe() override; /*! * Returns the current instance of the probe. * * @note You must hold the object lock when using the probe's object tracking * functionality. * * @sa objectLock() */ static Probe *instance(); /*! * Returns true if the probe is initialized, false otherwise. */ static bool isInitialized(); ///@cond internal static void objectAdded(QObject *obj, bool fromCtor = false); static void objectRemoved(QObject *obj); ///@endcond /*! * Returns a list of all QObjects we know about. * * @note This getter can be used without the object lock. Do acquire the * object lock and check the pointer with @e isValidObject though, before * dereferencing any of the QObject pointers. */ const QVector &allQObjects() const; /*! * Returns the object list model. * @return a pointer to a QAbstractItemModel instance. */ QAbstractItemModel *objectListModel() const; /*! * Returns the object tree model. * @return a pointer to a QAbstractItemModel instance. */ QAbstractItemModel *objectTreeModel() const; /*! * Register a model for remote usage. * @param objectName Unique identifier for the model, typically in reverse domain notation. * @param model The model to register. */ void registerModel(const QString &objectName, QAbstractItemModel *model); /*! * Install a global event filter. * Use this rather than installing the filter manually on QCoreApplication, * this will filter out GammaRay-internal events and objects already for you. */ void installGlobalEventFilter(QObject *filter); /*! * Returns @c true if we haven't been able to track all objects from startup, ie. usually * when attaching at runtime. * If this is the case, we try to discover QObjects by walking the hierarchy, starting * from known singletons, and by watching out for unknown receivers of events. * This is far from complete obviously, and plug-ins can help finding more objects, using * specific knowledge about the types they are responsible for. * * Connect to the objectAdded(QObject*) signal on probe(), and call discoverObject(QObject*) * for "your" objects. * * @since 2.5 */ bool needsObjectDiscovery() const; /*! * Notify the probe about QObjects your plug-in can discover by using information about * the types it can handle. * Only use this if needsObjectDiscovery() returns @c true to maximise performance. * * @see needsObjectDiscovery() * @since 2.0 */ void discoverObject(QObject *object); /*! * Notify the probe about the user selecting one of "your" objects via in-app interaction. * If you know the exact position the user interacted with, pass that in as @p pos. * * @since 2.0 */ void selectObject(QObject *object, const QPoint &pos = QPoint()); void selectObject(QObject *object, const QString &toolId, const QPoint &pos = QPoint()); /*! * Notify the probe about the user selecting one of "your" objects. * * @since 2.1 */ void selectObject(void *object, const QString &typeName); /*! * Register a signal spy callback set. * Signal indexes provided as arguments are mapped to method indexes, ie. argument semantics * are the same with Qt4 and Qt5. * * @since 2.2 */ void registerSignalSpyCallbackSet(const SignalSpyCallbackSet &callbacks); /*! Returns the source code location @p object was created at. */ SourceLocation objectCreationSourceLocation(QObject *object) const; /*! Returns the entire stack trace for the creation of @p object. */ Execution::Trace objectCreationStackTrace(QObject *object) const; ///@cond internal QObject *window() const; void setWindow(QObject *window); ///@endcond MetaObjectRegistry *metaObjectRegistry() const; /*! * Lock this to check the validity of a QObject * and to access it safely afterwards. */ static QMutex *objectLock(); /*! * Check whether @p obj is still valid. * * @note The objectLock must be locked when this is called! */ bool isValidObject(const QObject *obj) const; /*! * Determines if the specified QObject belongs to the GammaRay Probe or Window. * * These objects should not be tracked or shown to the user, * hence must be explicitly filtered. * @param obj is a pointer to a QObject instance. * * @return true if the specified QObject belongs to the GammaRay Probe * or Window; false otherwise. */ bool filterObject(QObject *obj) const; ///@cond internal static void startupHookReceived(); template static void executeSignalCallback(const Func &func); ///@endcond ProblemCollector *problemCollector() const; signals: /*! * Emitted when the user selected @p object at position @p pos in the probed application. */ void objectSelected(QObject *object, const QPoint &pos); void nonQObjectSelected(void *object, const QString &typeName); /*! * Emitted for newly created QObjects. * * Note: * - This signal is always emitted from the thread the probe exists in. * - The signal is emitted delayed enough for the QObject to have been fully constructed, * i.e. on the next event loop re-entry. * - The signal is not emitted if the object has been destroyed completely again meanwhile, * e.g. for objects that only existed on the stack. * - For objects created and destroyed in other threads, this signal might be emitted after * its dtor has been entered (in case of short-lived objects), but before it has been finished. * At this point the dtor might have already emitted the destroyed() signal and informed smart * pointers about the destruction. This means you must not rely on any of this for object lifetime * tracking for objects from other threads. Use objectDestroyed() instead. * - Do not put @p obj into a QWeakPointer, even if it's exclusively handled in the same thread as * the Probe instance. Qt4 asserts if target code tries to put @p obj into a QSharedPointer afterwards. * - The objectLock() is locked. */ void objectCreated(QObject *obj); /*! * Emitted for destroyed objects. * * Note: * - This signal is emitted from the thread the probe exists in. * - The signal is emitted from the end of the QObject dtor, dereferencing @p obj is no longer * safe at this point. * - In a multi-threaded application, this signal might reach you way after @p obj has been * destroyed, see isValidObject() for a way to check if the object is still valid before accessing it. * - The objectLock() is locked. */ void objectDestroyed(QObject *obj); void objectReparented(QObject *obj); void aboutToDetach(); protected: ///@cond internal bool eventFilter(QObject *receiver, QEvent *event) override; ///@endcond private slots: void delayedInit(); void shutdown(); void processQueuedObjectChanges(); void handleObjectDestroyed(QObject *obj); private: friend class ProbeCreator; friend class BenchSuite; /* Returns @c true if we have working hooks in QtCore, that is we are notified reliably * about every QObject creation/destruction. * @since 2.0 */ QT_DEPRECATED bool hasReliableObjectTracking() const; void objectFullyConstructed(QObject *obj); void queueCreatedObject(QObject *obj); void queueDestroyedObject(QObject *obj); bool isObjectCreationQueued(QObject *obj) const; void purgeChangesForObject(QObject *obj); void notifyQueuedObjectChanges(); void findExistingObjects(); /*! Check if we are capable of showing widgets. */ static bool canShowWidgets(); void showInProcessUi(); static void createProbe(bool findExisting); void resendServerAddress(); explicit Probe(QObject *parent = nullptr); static QAtomicPointer s_instance; /*! Set up all needed signal spy callbacks. */ void setupSignalSpyCallbacks(); ObjectListModel *m_objectListModel; ObjectTreeModel *m_objectTreeModel; ProblemCollector *m_problemCollector; ToolManager *m_toolManager; QObject *m_window; QSet m_validObjects; MetaObjectRegistry *m_metaObjectRegistry; // all delayed object changes need to go through a single queue, as the order is crucial struct ObjectChange { QObject *obj; enum Type { Create, Destroy } type; }; QVector m_queuedObjectChanges; QList m_pendingReparents; QTimer *m_queueTimer; QVector m_globalEventFilters; QVector m_signalSpyCallbacks; #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) QSignalSpyCallbackSet *m_previousSignalSpyCallbackSet; #else SignalSpyCallbackSet m_previousSignalSpyCallbackSet; #endif Server *m_server; }; } #endif // GAMMARAY_PROBE_H gammaray-2.11.3/core/probecontroller.cpp000066400000000000000000000030071413310443000202340ustar00rootroot00000000000000/* probecontroller.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "probecontroller.h" #include "probe.h" #include #include #include using namespace GammaRay; ProbeController::ProbeController(QObject *parent) : ProbeControllerInterface(parent) { } void ProbeController::detachProbe() { Probe::instance()->deleteLater(); } void ProbeController::quitHost() { QCoreApplication::instance()->quit(); } gammaray-2.11.3/core/probecontroller.h000066400000000000000000000033201413310443000176770ustar00rootroot00000000000000/* probecontroller.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_PROBECONTROLLER_H #define GAMMARAY_PROBECONTROLLER_H #include namespace GammaRay { /** @brief Server-side part for the object property inspector. * Use this to integrate a property inspector like in the QObject view into your tool. */ class ProbeController : public ProbeControllerInterface { Q_OBJECT Q_INTERFACES(GammaRay::ProbeControllerInterface) public: explicit ProbeController(QObject *parent = nullptr); public slots: void detachProbe() override; void quitHost() override; }; } #endif // GAMMARAY_PROBECONTROLLER_H gammaray-2.11.3/core/probeguard.cpp000066400000000000000000000035321413310443000171560ustar00rootroot00000000000000/* probeguard.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "probeguard.h" #include static QThreadStorage s_probeGuards; using namespace GammaRay; ProbeGuard::ProbeGuard() : m_previousState(insideProbe()) { setInsideProbe(true); } ProbeGuard::ProbeGuard(bool newState) : m_previousState(insideProbe()) { setInsideProbe(newState); } ProbeGuard::~ProbeGuard() { setInsideProbe(m_previousState); } bool ProbeGuard::insideProbe() { if (!s_probeGuards.hasLocalData()) return false; return s_probeGuards.localData(); } void ProbeGuard::setInsideProbe(bool inside) { s_probeGuards.localData() = inside; } ProbeGuardSuspender::ProbeGuardSuspender() : ProbeGuard(false) {} ProbeGuardSuspender::~ProbeGuardSuspender() = default; gammaray-2.11.3/core/probeguard.h000066400000000000000000000045341413310443000166260ustar00rootroot00000000000000/* probeguard.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_PROBEGUARD_H #define GAMMARAY_PROBEGUARD_H #include "gammaray_core_export.h" #include namespace GammaRay { /** Use this inside probe code that might trigger expensive or otherwise problematic side-effects in * GammaRay itself, so the triggered probe part can skip reacting to that action. * * This works per-thread, and is supposed to be used RAII-style. * * Example: Creating a short-lived QObject inside a probe plugin. */ class GAMMARAY_CORE_EXPORT ProbeGuard { public: ProbeGuard(); ~ProbeGuard(); /** Use this inside your probe code to check if you have been called from other probe code. * In that case you might want to skip some operations, */ static bool insideProbe(); protected: ProbeGuard(bool newState); void setInsideProbe(bool inside); private: Q_DISABLE_COPY(ProbeGuard) bool m_previousState; }; /** This is the inverse of ProbeGuard, use this to temporarily disable the guard when doing * individual calls to target code that might create objects, while being in a code block * protected by ProbeGuard. */ class ProbeGuardSuspender : public ProbeGuard { public: ProbeGuardSuspender(); ~ProbeGuardSuspender(); }; } #endif // GAMMARAY_PROBEGUARD_H gammaray-2.11.3/core/probesettings.cpp000066400000000000000000000176301413310443000177200ustar00rootroot00000000000000/* probesettings.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include "probesettings.h" #include "common/message.h" #include "common/paths.h" #include #include #include #include #include #include #include #include #include using namespace GammaRay; namespace GammaRay { class ProbeSettingsReceiver; struct ProbeSettingsData { QHash settings; ProbeSettingsReceiver *receiver; }; Q_GLOBAL_STATIC(ProbeSettingsData, s_probeSettings) class ProbeSettingsReceiver : public QObject { Q_OBJECT public: explicit ProbeSettingsReceiver(QObject *parent = nullptr); ~ProbeSettingsReceiver() override; Q_INVOKABLE void sendServerAddress(const QUrl &address); Q_INVOKABLE void sendServerLaunchError(const QString &reason); void waitForSettingsReceived(); private slots: void readyRead(); void settingsReceivedFallback(); private: Q_INVOKABLE void run(); void setRootPathFromProbePath(const QString &probePath); QLocalSocket *m_socket = nullptr; QWaitCondition m_waitCondition; QMutex m_mutex; }; ProbeSettingsReceiver::ProbeSettingsReceiver(QObject *parent) : QObject(parent) { } ProbeSettingsReceiver::~ProbeSettingsReceiver() { delete m_socket; } void ProbeSettingsReceiver::run() { m_mutex.lock(); // we only need this for ordering run after waitForSettingsReceived m_mutex.unlock(); m_socket = new QLocalSocket; connect(m_socket, &QLocalSocket::disconnected, this, &ProbeSettingsReceiver::settingsReceivedFallback); connect(m_socket, static_cast(&QLocalSocket::error), this, &ProbeSettingsReceiver::settingsReceivedFallback); connect(m_socket, &QIODevice::readyRead, this, &ProbeSettingsReceiver::readyRead); m_socket->connectToServer(QStringLiteral("gammaray-") + QString::number(ProbeSettings::launcherIdentifier())); if (!m_socket->waitForConnected(10000)) { #ifndef Q_OS_ANDROID qWarning() << "Failed to connect to launcher, can't receive probe settings!" << m_socket->errorString(); #endif settingsReceivedFallback(); } } void ProbeSettingsReceiver::waitForSettingsReceived() { m_mutex.lock(); QMetaObject::invokeMethod(this, "run", Qt::QueuedConnection); m_waitCondition.wait(&m_mutex); m_mutex.unlock(); } void ProbeSettingsReceiver::readyRead() { while (Message::canReadMessage(m_socket)) { auto msg = Message::readMessage(m_socket); switch (msg.type()) { case Protocol::ServerVersion: { qint32 version; msg >> version; if (version != Protocol::version()) { qWarning() << "Unable to receive probe settings, mismatching protocol versions (expected:" << Protocol::version() << "got:" << version << ")"; qWarning() << "Continuing anyway, but this is likely going to fail."; settingsReceivedFallback(); return; } break; } case Protocol::ProbeSettings: { msg >> s_probeSettings()->settings; // qDebug() << Q_FUNC_INFO << s_probeSettings()->settings; const QString probePath = ProbeSettings::value(QStringLiteral("ProbePath")).toString(); setRootPathFromProbePath(probePath); m_waitCondition.wakeAll(); return; } default: continue; } } } void ProbeSettingsReceiver::sendServerAddress(const QUrl &address) { if (!m_socket || m_socket->state() != QLocalSocket::ConnectedState) return; Message msg(Protocol::LauncherAddress, Protocol::ServerAddress); msg << address; msg.write(m_socket); m_socket->waitForBytesWritten(); m_socket->close(); deleteLater(); s_probeSettings()->receiver = nullptr; thread()->quit(); } void ProbeSettingsReceiver::sendServerLaunchError(const QString &reason) { if (!m_socket || m_socket->state() != QLocalSocket::ConnectedState) return; Message msg(Protocol::LauncherAddress, Protocol::ServerLaunchError); msg << reason; msg.write(m_socket); m_socket->waitForBytesWritten(); m_socket->close(); deleteLater(); s_probeSettings()->receiver = nullptr; thread()->quit(); } void ProbeSettingsReceiver::settingsReceivedFallback() { // see if we got fallback data via environment variables const QString probePath = ProbeSettings::value(QStringLiteral("ProbePath")).toString(); setRootPathFromProbePath(probePath); m_waitCondition.wakeAll(); } void ProbeSettingsReceiver::setRootPathFromProbePath(const QString &probePath) { if (probePath.isEmpty()) return; QFileInfo fi(probePath); if (fi.isFile()) Paths::setRootPath(fi.absolutePath() + QDir::separator() + GAMMARAY_INVERSE_PROBE_DIR); else Paths::setRootPath(probePath + QDir::separator() + GAMMARAY_INVERSE_PROBE_DIR); } } QVariant ProbeSettings::value(const QString &key, const QVariant &defaultValue) { QByteArray v = s_probeSettings()->settings.value(key.toUtf8()); if (v.isEmpty()) v = qgetenv("GAMMARAY_" + key.toLocal8Bit()); if (v.isEmpty()) return defaultValue; switch (defaultValue.type()) { case QVariant::String: return QString::fromUtf8(v); case QVariant::Bool: return v == "true" || v == "1" || v == "TRUE"; case QVariant::Int: return v.toInt(); default: return v; } } void ProbeSettings::receiveSettings() { auto t = new QThread; QObject::connect(t, &QThread::finished, t, &QObject::deleteLater); t->start(); auto receiver = new ProbeSettingsReceiver; s_probeSettings()->receiver = receiver; receiver->moveToThread(t); receiver->waitForSettingsReceived(); } qint64 ProbeSettings::launcherIdentifier() { bool ok; const qint64 id = qgetenv("GAMMARAY_LAUNCHER_ID").toLongLong(&ok); if (ok && id > 0) return id; return QCoreApplication::applicationPid(); } void ProbeSettings::resetLauncherIdentifier() { // if we were launch by GammaRay, and we later try to re-attach, we need to make sure // to not end up with an outdated launcher id qputenv("GAMMARAY_LAUNCHER_ID", ""); } void ProbeSettings::sendServerAddress(const QUrl &addr) { Q_ASSERT(s_probeSettings()->receiver); QMetaObject::invokeMethod(s_probeSettings()->receiver, "sendServerAddress", Q_ARG(QUrl, addr)); } void ProbeSettings::sendServerLaunchError(const QString &reason) { Q_ASSERT(s_probeSettings()->receiver); QMetaObject::invokeMethod(s_probeSettings()->receiver, "sendServerLaunchError", Q_ARG(QString, reason)); } #include "probesettings.moc" gammaray-2.11.3/core/probesettings.h000066400000000000000000000043001413310443000173530ustar00rootroot00000000000000/* probesettings.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_PROBESETTINGS_H #define GAMMARAY_PROBESETTINGS_H #include "gammaray_core_export.h" #include QT_BEGIN_NAMESPACE class QUrl; QT_END_NAMESPACE namespace GammaRay { /** General purpose settings of the probe provided by the launcher. */ namespace ProbeSettings { GAMMARAY_CORE_EXPORT QVariant value(const QString &key, const QVariant &defaultValue = QString()); /** Call if using runtime attaching to obtain settings provided via shared memory. * This method blocks until communication with the launcher is complete. */ void receiveSettings(); /** Identifier used for finding the communication channels to the launcher. */ qint64 launcherIdentifier(); /** Reset the launcher Identifier. Call this when detaching the probe. */ void resetLauncherIdentifier(); /** Sends the server address used for communication with the client back to the launcher. */ void sendServerAddress(const QUrl &addr); /** Sends an error message if no server could be launched back to the launcher */ void sendServerLaunchError(const QString &reason); } } #endif // GAMMARAY_PROBESETTINGS_H gammaray-2.11.3/core/problemcollector.cpp000066400000000000000000000111421413310443000203670ustar00rootroot00000000000000/* problemcollector.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2018-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ // Own #include "problemcollector.h" #include "probe.h" #include using namespace GammaRay; ProblemCollector::ProblemCollector(QObject *parent) : QObject(parent) { } ProblemCollector * ProblemCollector::instance() { return Probe::instance()->problemCollector(); } void ProblemCollector::registerProblemChecker(const QString& id, const QString& name, const QString& description, const std::function& callback, bool enabled) { Checker c = {id, name, description, callback, enabled}; instance()->m_availableCheckers.push_back(c); } void GammaRay::ProblemCollector::requestScan() { clearScans(); for (const auto &checker : qAsConst(m_availableCheckers)) { if (checker.enabled) checker.callback(); } emit problemScansFinished(); } void ProblemCollector::addProblem(const Problem& problem) { auto self = instance(); auto i = std::find(self->m_problems.begin(), self->m_problems.end(), problem); if (i != self->m_problems.end()) { // if an already reported problem is reported a second time, but with a different source location, // then the problem involves multiple source locations. So let's keep all of them. std::remove_copy_if(problem.locations.begin(), problem.locations.end(), std::back_inserter(i->locations), [&](const SourceLocation &loc) { return i->locations.contains(loc); }); return; } emit self->aboutToAddProblem(self->m_problems.size()); self->m_problems.push_back(problem); emit self->problemAdded(); } void ProblemCollector::removeProblem(const QString& problemId) { auto self = instance(); auto it = std::find_if(self->m_problems.begin(), self->m_problems.end(), [&](const Problem &problem) { return problem.problemId == problemId; }); if (it == self->m_problems.end()) return; auto row = std::distance(self->m_problems.begin(), it); emit self->aboutToRemoveProblems(row); self->m_problems.erase(it); emit self->problemsRemoved(); } void ProblemCollector::clearScans() { // Remove all elements which originate from a previous scan, before doing a new scan // and do so, properly informing the model about all changes. auto firstToDeleteIt = m_problems.begin(); auto it = firstToDeleteIt; while (true) { if (it != m_problems.end() && it->findingCategory == Problem::Scan) { ++it; } else if (firstToDeleteIt != it) { // this is supposed to be called also if `it == m_problems.end()` auto firstRow = std::distance(m_problems.begin(), firstToDeleteIt); auto count = std::distance(m_problems.begin(), it) - firstRow; emit aboutToRemoveProblems(firstRow, count); firstToDeleteIt = it = m_problems.erase(firstToDeleteIt, it); emit problemsRemoved(); } else if (it != m_problems.end()) { ++it; ++firstToDeleteIt; } else { break; } } } const QVector & ProblemCollector::problems() { return m_problems; } QVector &ProblemCollector::availableCheckers() { return m_availableCheckers; } bool ProblemCollector::isCheckerRegistered(const QString& id) const { return std::any_of(m_availableCheckers.begin(), m_availableCheckers.end(), [&id](const Checker &c){ return c.id == id; } ); } gammaray-2.11.3/core/problemcollector.h000066400000000000000000000077241413310443000200470ustar00rootroot00000000000000/* problemcollector.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2018-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_PROBLEMCOLLECTOR_H #define GAMMARAY_PROBLEMCOLLECTOR_H // Own #include "gammaray_core_export.h" #include #include #include // Qt #include // Std #include #include #include namespace GammaRay { class ProblemModel; class GAMMARAY_CORE_EXPORT ProblemCollector : public QObject { Q_OBJECT public: static void addProblem(const Problem &problem); /** * Removes the problem with the \p problemId. It's ok to call this method * with a problemId that is not registered, in this case nothing happens. * * It's not required to call this for problems with findingCategory Scan, * as those are removed automatically in advance to a new scan. */ static void removeProblem(const QString &problemId); static ProblemCollector *instance(); const QVector &problems(); /** * Use this method from a tool to register a scan, which the tool is able * to do. * * \p name and \p description are user-readable strings, \p id is some * internal string that will be used to identify the checkers. This can be * chosen at will, but must be unique. * \p callback will be called to start the scan */ static void registerProblemChecker(const QString &id, const QString &name, const QString &description, const std::function &callback, bool enabled = true); /// Meant to be used in unit tests bool isCheckerRegistered(const QString &id) const; private: struct Checker { QString id; QString name; QString description; std::function callback; bool enabled; }; QVector &availableCheckers(); signals: /** * These signals are directed at the problem model to inform about changes * in the result set. */ void aboutToAddProblem(int row); void problemAdded(); void aboutToRemoveProblems(int first, int count = 1); void problemsRemoved(); /** * This signal is directed at the Problem Reporter tool to inform that * the problem providing tools have started scanning for problems. */ void problemScansFinished(); /** * These signals are directed at the available checkers model to inform newly * available checkers */ void aboutToAddChecker(); void checkerAdded(); public slots: void requestScan(); private: explicit ProblemCollector(QObject *parent); void clearScans(); QVector m_availableCheckers; QVector m_problems; friend class Probe; friend class AvailableCheckersModel; friend class ProblemReporterTest; }; } #endif // GAMMARAY_PROBLEMCOLLECTOR_H gammaray-2.11.3/core/propertyadaptor.cpp000066400000000000000000000041101413310443000202540ustar00rootroot00000000000000/* propertyadaptor.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "propertyadaptor.h" #include "propertydata.h" using namespace GammaRay; PropertyAdaptor::PropertyAdaptor(QObject *parent) : QObject(parent) { } PropertyAdaptor::~PropertyAdaptor() = default; const ObjectInstance &PropertyAdaptor::object() const { return m_oi; } void PropertyAdaptor::setObject(const ObjectInstance &oi) { m_oi = oi; doSetObject(m_oi); } void PropertyAdaptor::writeProperty(int index, const QVariant &value) { Q_UNUSED(index); Q_UNUSED(value); } bool PropertyAdaptor::canAddProperty() const { return false; } void PropertyAdaptor::addProperty(const PropertyData &data) { Q_UNUSED(data); Q_ASSERT(false); } void PropertyAdaptor::resetProperty(int index) { Q_UNUSED(index); Q_ASSERT(false); } void PropertyAdaptor::doSetObject(const ObjectInstance &oi) { Q_UNUSED(oi); } PropertyAdaptor *GammaRay::PropertyAdaptor::parentAdaptor() const { return qobject_cast(parent()); } gammaray-2.11.3/core/propertyadaptor.h000066400000000000000000000057551413310443000177410ustar00rootroot00000000000000/* propertyadaptor.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_PROPERTYADAPTOR_H #define GAMMARAY_PROPERTYADAPTOR_H #include "gammaray_core_export.h" #include "objectinstance.h" #include #include namespace GammaRay { class PropertyData; /** Generic interface for accessing properties from various sources of an object. */ class GAMMARAY_CORE_EXPORT PropertyAdaptor : public QObject { Q_OBJECT public: explicit PropertyAdaptor(QObject *parent = nullptr); ~PropertyAdaptor() override; /** Returns the object instance who's properties this accesses. */ const ObjectInstance &object() const; /** Set the object instance who's properties we want to access. */ void setObject(const ObjectInstance &oi); /** Number of properties. */ virtual int count() const = 0; /** Property data for all properties. */ virtual PropertyData propertyData(int index) const = 0; /** Write a single property value. */ virtual void writeProperty(int index, const QVariant &value); /** Returns true if this adaptor allows adding new properties. */ virtual bool canAddProperty() const; /** Adds a new property. */ virtual void addProperty(const PropertyData &data); /** Resets the specified property. */ virtual void resetProperty(int index); /** Property adaptor of parent object instance, if any. */ PropertyAdaptor *parentAdaptor() const; signals: void propertyAdded(int first, int last); void propertyRemoved(int first, int last); void propertyChanged(int first, int last); /** Emit this when you noticed that the object who's properties we are * looking at became invalid. */ void objectInvalidated(); protected: virtual void doSetObject(const ObjectInstance &oi); private: friend class PropertyAggregator; ObjectInstance m_oi; }; } Q_DECLARE_METATYPE(GammaRay::PropertyAdaptor *) #endif // GAMMARAY_PROPERTYADAPTOR_H gammaray-2.11.3/core/propertyadaptorfactory.cpp000066400000000000000000000075261413310443000216620ustar00rootroot00000000000000/* propertyadaptorfactory.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "propertyadaptorfactory.h" #include "propertyaggregator.h" #include "objectinstance.h" #include "qmetapropertyadaptor.h" #include "dynamicpropertyadaptor.h" #include "sequentialpropertyadaptor.h" #include "associativepropertyadaptor.h" #include "metapropertyadaptor.h" #include "jsonpropertyadaptor.h" #include #include using namespace GammaRay; Q_GLOBAL_STATIC(QVector, s_propertyAdaptorFactories) AbstractPropertyAdaptorFactory::AbstractPropertyAdaptorFactory() = default; AbstractPropertyAdaptorFactory::~AbstractPropertyAdaptorFactory() = default; PropertyAdaptor *PropertyAdaptorFactory::create(const ObjectInstance &oi, QObject *parent) { QVector adaptors; if (oi.metaObject()) adaptors.push_back(new QMetaPropertyAdaptor(parent)); if (oi.type() == ObjectInstance::QtObject) adaptors.push_back(new DynamicPropertyAdaptor(parent)); if (oi.type() == ObjectInstance::QtObject || oi.type() == ObjectInstance::Object || oi.type() == ObjectInstance::Value || oi.type() == ObjectInstance::QtGadgetPointer || oi.type() == ObjectInstance::QtGadgetValue) adaptors.push_back(new MetaPropertyAdaptor(parent)); if (oi.type() == ObjectInstance::QtVariant) { if (oi.typeName() == "QJsonObject" || oi.typeName() == "QJsonArray") adaptors.push_back(new JsonPropertyAdaptor(parent)); else if (oi.typeName() == "QJsonValue") { if (oi.variant().toJsonValue().isObject() || oi.variant().toJsonValue().isArray()) { adaptors.push_back(new JsonPropertyAdaptor(parent)); } } else if(oi.typeName() == "QJSValue") { } else { const auto v = oi.variant(); if (v.canConvert()) adaptors.push_back(new SequentialPropertyAdaptor(parent)); else if (v.canConvert()) adaptors.push_back(new AssociativePropertyAdaptor(parent)); } } foreach (auto factory, *s_propertyAdaptorFactories()) { auto a = factory->create(oi, parent); if (a) adaptors.push_back(a); } if (adaptors.isEmpty()) return nullptr; if (adaptors.size() == 1) { adaptors.first()->setObject(oi); return adaptors.first(); } auto aggregator = new PropertyAggregator(parent); std::for_each(adaptors.constBegin(), adaptors.constEnd(), [&aggregator](PropertyAdaptor *pa) { aggregator->addPropertyAdaptor(pa); }); aggregator->setObject(oi); return aggregator; } void PropertyAdaptorFactory::registerFactory(AbstractPropertyAdaptorFactory *factory) { s_propertyAdaptorFactories()->push_back(factory); } gammaray-2.11.3/core/propertyadaptorfactory.h000066400000000000000000000044561413310443000213260ustar00rootroot00000000000000/* propertyadaptorfactory.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_PROPERTYADAPTORFACTORY_H #define GAMMARAY_PROPERTYADAPTORFACTORY_H #include "gammaray_core_export.h" #include QT_BEGIN_NAMESPACE class QObject; QT_END_NAMESPACE namespace GammaRay { class ObjectInstance; class PropertyAdaptor; /** Base class for custom property adaptor factories. */ class GAMMARAY_CORE_EXPORT AbstractPropertyAdaptorFactory { public: AbstractPropertyAdaptorFactory(); virtual ~AbstractPropertyAdaptorFactory(); /** Create a custom property adaptor if it can handle @p oi, return @c nullptr otherwise. */ virtual PropertyAdaptor *create(const ObjectInstance &oi, QObject *parent = nullptr) const = 0; private: Q_DISABLE_COPY(AbstractPropertyAdaptorFactory) }; /** Factory for property adaptors. */ namespace PropertyAdaptorFactory { /** Create a property adaptor for @p oi, if necessary wrapped in an aggregator. * Can return @c nullptr. */ GAMMARAY_CORE_EXPORT PropertyAdaptor *create(const ObjectInstance &oi, QObject *parent = nullptr); /** Register a custom property adaptor factory. */ GAMMARAY_CORE_EXPORT void registerFactory(AbstractPropertyAdaptorFactory *factory); } } #endif // GAMMARAY_PROPERTYADAPTORFACTORY_H gammaray-2.11.3/core/propertyaggregator.cpp000066400000000000000000000130171413310443000207520ustar00rootroot00000000000000/* propertyaggregator.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "propertyaggregator.h" #include "propertydata.h" #include "objectinstance.h" #include #include #include #include using namespace GammaRay; PropertyAggregator::PropertyAggregator(QObject *parent) : PropertyAdaptor(parent) { } PropertyAggregator::~PropertyAggregator() = default; void PropertyAggregator::doSetObject(const ObjectInstance &oi) { std::for_each(m_propertyAdaptors.begin(), m_propertyAdaptors.end(), [&oi](PropertyAdaptor *pa) { pa->setObject(oi); }); } int PropertyAggregator::count() const { if (!object().isValid()) return 0; return std::accumulate(m_propertyAdaptors.constBegin(), m_propertyAdaptors.constEnd(), 0, [](int lhs, PropertyAdaptor *rhs) { return lhs + rhs->count(); }); } PropertyData PropertyAggregator::propertyData(int index) const { if (!object().isValid()) return PropertyData(); int offset = 0; for (const auto adaptor : m_propertyAdaptors) { if (index < offset + adaptor->count()) return adaptor->propertyData(index - offset); offset += adaptor->count(); } Q_ASSERT(false); return PropertyData(); } void PropertyAggregator::writeProperty(int index, const QVariant &value) { if (!object().isValid()) return; int offset = 0; for (const auto adaptor : qAsConst(m_propertyAdaptors)) { if (index < offset + adaptor->count()) { QPointer guard(this); adaptor->writeProperty(index - offset, value); if (guard) { m_oi = adaptor->object(); // propagate changes back to us, particularly matters for value types } return; } offset += adaptor->count(); } Q_ASSERT(false); } bool PropertyAggregator::canAddProperty() const { auto count = std::count_if(m_propertyAdaptors.constBegin(), m_propertyAdaptors.constEnd(), [](PropertyAdaptor *pa) { return pa->canAddProperty(); }); return count == 1; } void PropertyAggregator::addProperty(const PropertyData &data) { if (!object().isValid()) return; Q_ASSERT(canAddProperty()); for (const auto adaptor : qAsConst(m_propertyAdaptors)) { if (adaptor->canAddProperty()) { adaptor->addProperty(data); return; } } Q_ASSERT(false); } void PropertyAggregator::resetProperty(int index) { if (!object().isValid()) return; int offset = 0; for (const auto adaptor : qAsConst(m_propertyAdaptors)) { if (index < offset + adaptor->count()) { adaptor->resetProperty(index - offset); return; } offset += adaptor->count(); } Q_ASSERT(false); } void PropertyAggregator::addPropertyAdaptor(PropertyAdaptor *adaptor) { m_propertyAdaptors.push_back(adaptor); connect(adaptor, &PropertyAdaptor::propertyChanged, this, &PropertyAggregator::slotPropertyChanged); connect(adaptor, &PropertyAdaptor::propertyAdded, this, &PropertyAggregator::slotPropertyAdded); connect(adaptor, &PropertyAdaptor::propertyRemoved, this, &PropertyAggregator::slotPropertyRemoved); connect(adaptor, &PropertyAdaptor::objectInvalidated, this, &PropertyAdaptor::objectInvalidated); } void PropertyAggregator::slotPropertyChanged(int first, int last) { auto source = sender(); Q_ASSERT(source); int offset = 0; for (auto pa : qAsConst(m_propertyAdaptors)) { if (pa == source) { emit propertyChanged(first + offset, last + offset); return; } else { offset += pa->count(); } } } void PropertyAggregator::slotPropertyAdded(int first, int last) { auto source = sender(); Q_ASSERT(source); int offset = 0; for (auto pa : qAsConst(m_propertyAdaptors)) { if (pa == source) { emit propertyAdded(first + offset, last + offset); return; } else { offset += pa->count(); } } } void PropertyAggregator::slotPropertyRemoved(int first, int last) { auto source = sender(); Q_ASSERT(source); int offset = 0; for (auto pa : qAsConst(m_propertyAdaptors)) { if (pa == source) { emit propertyRemoved(first + offset, last + offset); return; } else { offset += pa->count(); } } } gammaray-2.11.3/core/propertyaggregator.h000066400000000000000000000043571413310443000204260ustar00rootroot00000000000000/* propertyaggregator.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_PROPERTYAGGREGATOR_H #define GAMMARAY_PROPERTYAGGREGATOR_H #include "gammaray_core_export.h" #include "propertyadaptor.h" #include namespace GammaRay { /** Aggregates the results of a bunch of PropertyAdaptor instances. */ class GAMMARAY_CORE_EXPORT PropertyAggregator : public PropertyAdaptor { Q_OBJECT public: explicit PropertyAggregator(QObject *parent = nullptr); ~PropertyAggregator() override; int count() const override; PropertyData propertyData(int index) const override; void writeProperty(int index, const QVariant &value) override; bool canAddProperty() const override; void addProperty(const PropertyData &data) override; void resetProperty(int index) override; void addPropertyAdaptor(PropertyAdaptor *adaptor); protected: void doSetObject(const ObjectInstance &oi) override; private slots: void slotPropertyChanged(int first, int last); void slotPropertyAdded(int first, int last); void slotPropertyRemoved(int first, int last); private: QVector m_propertyAdaptors; }; } #endif // GAMMARAY_PROPERTYAGGREGATOR_H gammaray-2.11.3/core/propertycontroller.cpp000066400000000000000000000101661413310443000210150ustar00rootroot00000000000000/* propertycontroller.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "propertycontroller.h" #include "probe.h" #include #include using namespace GammaRay; QVector PropertyController::s_extensionFactories = QVector(); QVector PropertyController::s_instances = QVector(); PropertyController::PropertyController(const QString &baseName, QObject *parent) : PropertyControllerInterface(baseName + ".controller", parent) , m_objectBaseName(baseName) { s_instances << this; m_extensions.reserve(s_extensionFactories.size()); for (PropertyControllerExtensionFactoryBase *factory : qAsConst(s_extensionFactories)) m_extensions << factory->create(this); } PropertyController::~PropertyController() { const auto i = s_instances.indexOf(this); if (i >= 0) s_instances.remove(i); qDeleteAll(m_extensions); } void PropertyController::loadExtension(PropertyControllerExtensionFactoryBase *factory) { m_extensions << factory->create(this); } void PropertyController::registerExtension(PropertyControllerExtensionFactoryBase* factory) { if (s_extensionFactories.indexOf(factory) >= 0) return; s_extensionFactories << factory; for (PropertyController *instance : qAsConst(s_instances)) instance->loadExtension(factory); } const QString &PropertyController::objectBaseName() { return m_objectBaseName; } void PropertyController::registerModel(QAbstractItemModel *model, const QString &nameSuffix) { Probe::instance()->registerModel(m_objectBaseName + '.' + nameSuffix, model); } void PropertyController::setObject(QObject *object) { if (m_object) disconnect(m_object.data(), &QObject::destroyed, this, &PropertyController::objectDestroyed); if (object) connect(object, &QObject::destroyed, this, &PropertyController::objectDestroyed); m_object = object; QStringList availableExtensions; for (PropertyControllerExtension *extension : qAsConst(m_extensions)) { if (extension->setQObject(object)) availableExtensions << extension->name(); } setAvailableExtensions(availableExtensions); } void PropertyController::setObject(void *object, const QString &className) { setObject(nullptr); QStringList availableExtensions; for (PropertyControllerExtension *extension : qAsConst(m_extensions)) { if (extension->setObject(object, className)) availableExtensions << extension->name(); } setAvailableExtensions(availableExtensions); } void PropertyController::setMetaObject(const QMetaObject *metaObject) { setObject(nullptr); QStringList availableExtensions; for (PropertyControllerExtension *extension : qAsConst(m_extensions)) { if (extension->setMetaObject(metaObject)) availableExtensions << extension->name(); } setAvailableExtensions(availableExtensions); } void PropertyController::objectDestroyed() { setObject(nullptr); } gammaray-2.11.3/core/propertycontroller.h000066400000000000000000000052641413310443000204650ustar00rootroot00000000000000/* propertycontroller.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_PROPERTYCONTROLLER_H #define GAMMARAY_PROPERTYCONTROLLER_H #include "gammaray_core_export.h" #include "propertycontrollerextension.h" #include #include #include QT_BEGIN_NAMESPACE class QAbstractItemModel; QT_END_NAMESPACE namespace GammaRay { /** @brief Non-UI part of the property widget. */ class GAMMARAY_CORE_EXPORT PropertyController : public PropertyControllerInterface { Q_OBJECT Q_INTERFACES(GammaRay::PropertyControllerInterface) public: explicit PropertyController(const QString &baseName, QObject *parent); ~PropertyController() override; const QString &objectBaseName(); void setObject(QObject *object); void setObject(void *object, const QString &className); void setMetaObject(const QMetaObject *metaObject); void registerModel(QAbstractItemModel *model, const QString &nameSuffix); template static void registerExtension() { registerExtension(PropertyControllerExtensionFactory::instance()); } private slots: void objectDestroyed(); private: void loadExtension(PropertyControllerExtensionFactoryBase *factory); static void registerExtension(PropertyControllerExtensionFactoryBase *factory); private: QString m_objectBaseName; QPointer m_object; QVector m_extensions; static QVector s_extensionFactories; static QVector s_instances; }; } #endif // GAMMARAY_PROPERTYCONTROLLER_H gammaray-2.11.3/core/propertycontrollerextension.cpp000066400000000000000000000035061413310443000227520ustar00rootroot00000000000000/* propertycontrollerextension.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "propertycontrollerextension.h" using namespace GammaRay; PropertyControllerExtension::PropertyControllerExtension(const QString &name) : m_name(name) { } PropertyControllerExtension::~PropertyControllerExtension() = default; QString PropertyControllerExtension::name() const { return m_name; } bool PropertyControllerExtension::setObject(void *object, const QString &typeName) { Q_UNUSED(object); Q_UNUSED(typeName); return false; } bool PropertyControllerExtension::setQObject(QObject *object) { Q_UNUSED(object); return false; } bool PropertyControllerExtension::setMetaObject(const QMetaObject *metaObject) { Q_UNUSED(metaObject); return false; } gammaray-2.11.3/core/propertycontrollerextension.h000066400000000000000000000101341413310443000224120ustar00rootroot00000000000000/* propertycontrollerextension.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef PROPERTYCONTROLLEREXTENSION_H #define PROPERTYCONTROLLEREXTENSION_H #include "gammaray_core_export.h" #include QT_BEGIN_NAMESPACE class QObject; struct QMetaObject; QT_END_NAMESPACE namespace GammaRay { class PropertyController; /** * @brief Base-class for server-side property editor extensions. * * This can be used to add your own tabs to the property widget. * Re-implement the corresponding variant of setObject/setMetaObject * you can handle, the default implementations do nothing and return * @c false. * * @since 2.1 */ class GAMMARAY_CORE_EXPORT PropertyControllerExtension { public: /** @brief Create a new property extension. * @param name The extension identifier used for client/server communication. */ explicit PropertyControllerExtension(const QString &name); virtual ~PropertyControllerExtension(); /** @brief Sets the object that should be represented by this extension. * This variant is used for non-QObject types using Gammaray::MetaObjectRepository. * @return @c true if the extension can handle @p object, @c false otherwise. */ virtual bool setObject(void *object, const QString &typeName); /** @brief Sets the QObject that should be represented by this extension. * This variant is used for QObject-derived types. * @return @c true if the extension can handle @p object, @c false otherwise. */ virtual bool setQObject(QObject *object); /** @brief Sets the meta object that should be represented by this extension. * This variant is used for QMetaObjects without a specific object instance. * @return @c true if the extension can handle @p object, @c false otherwise. */ virtual bool setMetaObject(const QMetaObject *metaObject); /** @brief Returns the identifier of this extension, used for client/server communication. */ QString name() const; private: Q_DISABLE_COPY(PropertyControllerExtension) QString m_name; }; ///@cond internal class PropertyControllerExtensionFactoryBase { public: explicit PropertyControllerExtensionFactoryBase() {} virtual PropertyControllerExtension *create(PropertyController *controller) = 0; private: Q_DISABLE_COPY(PropertyControllerExtensionFactoryBase) }; template class PropertyControllerExtensionFactory : public PropertyControllerExtensionFactoryBase { public: static PropertyControllerExtensionFactoryBase *instance() { if (!s_instance) s_instance = new PropertyControllerExtensionFactory(); return s_instance; } PropertyControllerExtension *create(PropertyController *controller) override { return new T(controller); } private: explicit PropertyControllerExtensionFactory() {} static PropertyControllerExtensionFactory *s_instance; }; template PropertyControllerExtensionFactory *PropertyControllerExtensionFactory::s_instance = nullptr; ///@endcond } #endif // PROPERTYCONTROLLEREXTENSION_H gammaray-2.11.3/core/propertydata.cpp000066400000000000000000000047631413310443000175510ustar00rootroot00000000000000/* propertydata.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "propertydata.h" using namespace GammaRay; QString PropertyData::name() const { return m_name; } void PropertyData::setName(const QString &name) { m_name = name; } QVariant PropertyData::value() const { return m_value; } void PropertyData::setValue(const QVariant &value) { m_value = value; } QString PropertyData::typeName() const { if (m_typeName.isEmpty()) return m_value.typeName(); return m_typeName; } void PropertyData::setTypeName(const QString &typeName) { m_typeName = typeName; } QString PropertyData::className() const { return m_className; } void PropertyData::setClassName(const QString &className) { m_className = className; } PropertyData::AccessFlags PropertyData::accessFlags() const { return m_accessFlags; } void PropertyData::setAccessFlags(PropertyData::AccessFlags flags) { m_accessFlags = flags; } PropertyModel::PropertyFlags PropertyData::propertyFlags() const { return m_propertyFlags; } void PropertyData::setPropertyFlags(PropertyModel::PropertyFlags flags) { m_propertyFlags = flags; } int PropertyData::revision() const { return m_revision; } void PropertyData::setRevision(int rev) { m_revision = rev; } QString PropertyData::notifySignal() const { return m_notifySignal; } void PropertyData::setNotifySignal(const QString& notifySignal) { m_notifySignal = notifySignal; } gammaray-2.11.3/core/propertydata.h000066400000000000000000000052621413310443000172110ustar00rootroot00000000000000/* propertydata.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_PROPERTYDATA_H #define GAMMARAY_PROPERTYDATA_H #include "gammaray_core_export.h" #include #include #include namespace GammaRay { /** Represents a single property value. */ class GAMMARAY_CORE_EXPORT PropertyData { public: enum AccessFlag { Readable = 0, Writable = 1, Deletable = 2, Resettable = 4 }; Q_DECLARE_FLAGS(AccessFlags, AccessFlag) PropertyData() = default; QString name() const; void setName(const QString &name); QVariant value() const; void setValue(const QVariant &value); QString typeName() const; void setTypeName(const QString &typeName); QString className() const; void setClassName(const QString &className); AccessFlags accessFlags() const; void setAccessFlags(AccessFlags flags); PropertyModel::PropertyFlags propertyFlags() const; void setPropertyFlags(PropertyModel::PropertyFlags flags); int revision() const; void setRevision(int rev); QString notifySignal() const; void setNotifySignal(const QString ¬ifySignal); private: QString m_name; QVariant m_value; QString m_typeName; QString m_className; QString m_notifySignal; AccessFlags m_accessFlags = Readable; PropertyModel::PropertyFlags m_propertyFlags = PropertyModel::None; int m_revision = -1; }; } Q_DECLARE_OPERATORS_FOR_FLAGS(GammaRay::PropertyData::AccessFlags) QT_BEGIN_NAMESPACE Q_DECLARE_TYPEINFO(GammaRay::PropertyData, Q_MOVABLE_TYPE); QT_END_NAMESPACE #endif // GAMMARAY_PROPERTYDATA_H gammaray-2.11.3/core/propertyfilter.cpp000066400000000000000000000056431413310443000201230ustar00rootroot00000000000000/* propertyfilter.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "propertyfilter.h" #include using namespace GammaRay; Q_GLOBAL_STATIC(QVector, s_propertyFilters) GammaRay::PropertyFilter::PropertyFilter( const QString &className, const QString &name, const QString &typeName, PropertyData::AccessFlags accessFlags, PropertyModel::PropertyFlags propertyFlags ) : m_name(name) , m_typeName(typeName) , m_className(className) , m_accessFlags(accessFlags) , m_propertyFlags(propertyFlags) { } GammaRay::PropertyFilter GammaRay::PropertyFilter::classAndPropertyName(const QString &className, const QString &propertyName) { return PropertyFilter(className, propertyName); } bool GammaRay::PropertyFilter::matches(const GammaRay::PropertyData &prop) const { if (!m_className.isEmpty() && prop.className() != m_className) { return false; } if (!m_name.isEmpty() && prop.name() != m_name) { return false; } if (!m_typeName.isEmpty() && prop.typeName() != m_typeName) { return false; } if (m_accessFlags && (prop.accessFlags() & m_accessFlags) == m_accessFlags) { return false; } if (m_propertyFlags && (prop.propertyFlags() & m_propertyFlags) == m_propertyFlags) { return false; } return true; } bool GammaRay::PropertyFilters::matches(const GammaRay::PropertyData &prop) { return std::any_of(s_propertyFilters()->begin(), s_propertyFilters()->end(), [&prop](const PropertyFilter &filter) { return filter.matches(prop); }); } void PropertyFilters::registerFilter(const PropertyFilter &filter) { s_propertyFilters()->push_back(filter); } gammaray-2.11.3/core/propertyfilter.h000066400000000000000000000052661413310443000175710ustar00rootroot00000000000000/* propertyfilter.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_PROPERTYFILTER_H #define GAMMARAY_PROPERTYFILTER_H #include "gammaray_core_export.h" #include "propertydata.h" #include #include #include namespace GammaRay { class GAMMARAY_CORE_EXPORT PropertyFilter { public: explicit PropertyFilter() {} explicit PropertyFilter( const QString &className, const QString &name, const QString &typeName = QString(), PropertyData::AccessFlags accessFlags = nullptr, PropertyModel::PropertyFlags propertyFlags = nullptr ); static PropertyFilter classAndPropertyName(const QString &className, const QString &propertyName); bool matches(const PropertyData &prop) const; private: QString m_name; QString m_typeName; QString m_className; PropertyData::AccessFlags m_accessFlags; PropertyModel::PropertyFlags m_propertyFlags; }; namespace PropertyFilters { GAMMARAY_CORE_EXPORT bool matches(const PropertyData &prop); /** * Register a filter to remove properties from the property view. Property * adaptors are free to consider or ignore filters. * * Use cases are e.g.: * * Removing a QMetaProperty in order to re-add it from a custom adaptor * * Prohibiting a property getter to be called, if it's known to crash the * application under certain circumstances (e.g. if a getter requires an * OpenGL context to be valid) */ GAMMARAY_CORE_EXPORT void registerFilter(const PropertyFilter &filter); } } #endif // GAMMARAY_PROPERTYFILTER_H gammaray-2.11.3/core/proxytoolfactory.cpp000066400000000000000000000041631413310443000204740ustar00rootroot00000000000000/* proxytoolfactory.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "proxytoolfactory.h" using namespace GammaRay; ProxyToolFactory::ProxyToolFactory(const PluginInfo &pluginInfo, QObject *parent) : ProxyFactory(pluginInfo, parent) { const QStringList typesList = pluginInfo.supportedTypes(); QVector typesVector; typesVector.reserve(typesList.count()); for (auto it = typesList.constBegin(), end = typesList.constEnd(); it != end; ++it) typesVector << (*it).toLatin1(); setSupportedTypes(typesVector); } bool ProxyToolFactory::isValid() const { return pluginInfo().isValid() && !id().isEmpty() && !supportedTypes().isEmpty(); } void ProxyToolFactory::init(Probe *probe) { loadPlugin(); ToolFactory *fac = factory(); if (!fac) return; Q_ASSERT(fac); fac->init(probe); } bool ProxyToolFactory::isHidden() const { return pluginInfo().isHidden(); } QVector ProxyToolFactory::selectableTypes() const { return pluginInfo().selectableTypes(); } gammaray-2.11.3/core/proxytoolfactory.h000066400000000000000000000037571413310443000201510ustar00rootroot00000000000000/* proxytoolfactory.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_PROXYTOOLFACTORY_H #define GAMMARAY_PROXYTOOLFACTORY_H #include #include "toolfactory.h" namespace GammaRay { /** * A wrapper around a plugin ToolFactory that only loads the actual plugin * once initialized. * Until then, meta-data is provided based on a plugin spec file. * * TODO: Improve error reporting */ class ProxyToolFactory : public ProxyFactory { Q_OBJECT public: /** * @param pluginInfo Plugin spec file */ explicit ProxyToolFactory(const PluginInfo &pluginInfo, QObject *parent = nullptr); /** Returns @c true if the plugin seems valid from all the information we have so far. */ bool isValid() const; bool isHidden() const override; QVector selectableTypes() const override; void init(Probe *probe) override; }; } #endif // GAMMARAY_PROXYTOOLFACTORY_H gammaray-2.11.3/core/qmetaobjectvalidator.cpp000066400000000000000000000075231413310443000212340ustar00rootroot00000000000000/* qmetaobjectvalidator.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include "qmetaobjectvalidator.h" #include "execution.h" #include "metaobjectregistry.h" #include "probe.h" #include "problemcollector.h" #include #include #include #include #include #include using namespace GammaRay; QMetaObjectValidatorResult::Results QMetaObjectValidator::checkProperty(const QMetaObject *mo, const QMetaProperty &property) { QMetaObjectValidatorResult::Results r = QMetaObjectValidatorResult::NoIssue; // check if property overrides base-class declaration if (mo->superClass()) { const auto baseIdx = mo->superClass()->indexOfProperty(property.name()); if (baseIdx >= 0) r |= QMetaObjectValidatorResult::PropertyOverride; } // check if property uses a known type if (property.userType() == QMetaType::UnknownType) r |= QMetaObjectValidatorResult::UnknownPropertyType; return r; } QMetaObjectValidatorResult::Results QMetaObjectValidator::checkMethod(const QMetaObject *mo, const QMetaMethod &method) { QMetaObjectValidatorResult::Results r = QMetaObjectValidatorResult::NoIssue; // check for parameters with unknown type // don't check internal methods such as _q_createJSWrapper() from QQuickItem if (!method.name().startsWith("_q")) { //krazy:exclude=strings for (int j = 0; j < method.parameterCount(); ++j) { if (method.parameterType(j) == QMetaType::UnknownType) r |= QMetaObjectValidatorResult::UnknownMethodParameterType; } } // check for signal overrides if (method.methodType() == QMetaMethod::Signal && mo->superClass()) { const auto baseIdx = mo->superClass()->indexOfMethod(method.methodSignature()); if (baseIdx >= 0) r |= QMetaObjectValidatorResult::SignalOverride; } return r; } static bool isDynamicMetaObject(const QMetaObject *mo) { Q_ASSERT(reinterpret_cast(mo->d.data)->revision >= 3); return reinterpret_cast(mo->d.data)->flags & DynamicMetaObject; } QMetaObjectValidatorResult::Results QMetaObjectValidator::check(const QMetaObject *mo) { QMetaObjectValidatorResult::Results r = QMetaObjectValidatorResult::NoIssue; if (isDynamicMetaObject(mo)) // those may dynamically add properties we query... return r; for (int i = mo->propertyOffset(); i < mo->propertyCount(); ++i) { const auto prop = mo->property(i); r |= checkProperty(mo, prop); } for (int i = mo->methodOffset(); i < mo->methodCount(); ++i) { const auto method = mo->method(i); r |= checkMethod(mo, method); } return r; } gammaray-2.11.3/core/qmetaobjectvalidator.h000066400000000000000000000034151413310443000206750ustar00rootroot00000000000000/* qmetaobjectvalidator.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_QMETAOBJECTVALIDATOR_H #define GAMMARAY_QMETAOBJECTVALIDATOR_H #include QT_BEGIN_NAMESPACE class QMetaMethod; struct QMetaObject; class QMetaProperty; QT_END_NAMESPACE namespace GammaRay { /*! Checks QMetaObject instances for common problems. */ namespace QMetaObjectValidator { QMetaObjectValidatorResult::Results checkProperty(const QMetaObject *mo, const QMetaProperty &property); QMetaObjectValidatorResult::Results checkMethod(const QMetaObject *mo, const QMetaMethod &method); QMetaObjectValidatorResult::Results check(const QMetaObject *mo); } } #endif // GAMMARAY_QMETAOBJECTVALIDATOR_H gammaray-2.11.3/core/qmetapropertyadaptor.cpp000066400000000000000000000156431413310443000213210ustar00rootroot00000000000000/* qmetapropertyadaptor.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "qmetapropertyadaptor.h" #include "propertyfilter.h" #include "objectinstance.h" #include "propertydata.h" #include "util.h" #include "probeguard.h" #include #include using namespace GammaRay; QMetaPropertyAdaptor::QMetaPropertyAdaptor(QObject *parent) : PropertyAdaptor(parent) , m_notifyGuard(false) { } QMetaPropertyAdaptor::~QMetaPropertyAdaptor() = default; void QMetaPropertyAdaptor::doSetObject(const ObjectInstance &oi) { auto mo = oi.metaObject(); if (!mo) return; if (oi.type() == ObjectInstance::QtObject && oi.qtObject()) connect(oi.qtObject(), &QObject::destroyed, this, &PropertyAdaptor::objectInvalidated); for (int i = 0; i < mo->propertyCount(); ++i) { const QMetaProperty prop = mo->property(i); if (!PropertyFilters::matches(propertyMetaData(i))) { if (oi.type() == ObjectInstance::QtObject && oi.qtObject() && prop.hasNotifySignal()) { connect(oi.qtObject(), QByteArray("2") + prop.notifySignal().methodSignature(), this, SLOT(propertyUpdated())); m_notifyToRowMap.insert(prop.notifySignalIndex(), m_rowToPropertyIndex.size()); } m_rowToPropertyIndex.push_back(i); } } } int QMetaPropertyAdaptor::count() const { if (!object().isValid()) return 0; return m_rowToPropertyIndex.count(); } PropertyData QMetaPropertyAdaptor::propertyMetaData(int propertyIndex) const { PropertyData data; if (!object().isValid()) return data; const auto mo = object().metaObject(); Q_ASSERT(mo); const auto prop = mo->property(propertyIndex); data.setName(prop.name()); data.setTypeName(prop.typeName()); auto pmo = mo; while (pmo->propertyOffset() > propertyIndex) pmo = pmo->superClass(); data.setClassName(pmo->className()); PropertyModel::PropertyFlags f(PropertyModel::None); if (prop.isConstant()) f |= PropertyModel::Constant; if (prop.isDesignable(object().qtObject())) f |= PropertyModel::Designable; if (prop.isFinal()) f |= PropertyModel::Final; if (prop.isResettable()) f |= PropertyModel::Resetable; if (prop.isScriptable(object().qtObject())) f |= PropertyModel::Scriptable; if (prop.isStored(object().qtObject())) f |= PropertyModel::Stored; if (prop.isUser(object().qtObject())) f |= PropertyModel::User; if (prop.isWritable()) f |= PropertyModel::Writable; data.setPropertyFlags(f); data.setRevision(prop.revision()); if (prop.hasNotifySignal()) data.setNotifySignal(Util::prettyMethodSignature(prop.notifySignal())); PropertyData::AccessFlags flags = PropertyData::Readable; if (prop.isWritable()) flags |= PropertyData::Writable; if (prop.isResettable()) flags |= PropertyData::Resettable; data.setAccessFlags(flags); return data; } PropertyData QMetaPropertyAdaptor::propertyData(int row) const { int propertyIndex = m_rowToPropertyIndex[row]; PropertyData data = propertyMetaData(propertyIndex); if (!object().isValid()) return data; m_notifyGuard = true; const auto mo = object().metaObject(); Q_ASSERT(mo); const auto prop = mo->property(propertyIndex); // we call out to the target here, so suspend the probe guard, otherwise we'll miss on-demand created object (e.g. in QQ2) { ProbeGuardSuspender g; switch (object().type()) { case ObjectInstance::QtObject: if (object().qtObject()) data.setValue(prop.read(object().qtObject())); break; case ObjectInstance::QtGadgetPointer: case ObjectInstance::QtGadgetValue: if (object().object()) data.setValue(prop.readOnGadget(object().object())); break; default: break; } } m_notifyGuard = false; return data; } void QMetaPropertyAdaptor::writeProperty(int row, const QVariant &value) { int propertyIndex = m_rowToPropertyIndex[row]; const auto mo = object().metaObject(); Q_ASSERT(mo); const auto prop = mo->property(propertyIndex); switch (object().type()) { case ObjectInstance::QtObject: if (object().qtObject()) { prop.write(object().qtObject(), value); if (!prop.hasNotifySignal()) emit propertyChanged(row, row); } break; case ObjectInstance::QtGadgetPointer: case ObjectInstance::QtGadgetValue: if (object().object()) { prop.writeOnGadget(object().object(), value); emit propertyChanged(row, row); } break; default: break; } } void QMetaPropertyAdaptor::resetProperty(int row) { int propertyIndex = m_rowToPropertyIndex[row]; const auto mo = object().metaObject(); Q_ASSERT(mo); const auto prop = mo->property(propertyIndex); switch (object().type()) { case ObjectInstance::QtObject: if (object().qtObject()) { prop.reset(object().qtObject()); if (!prop.hasNotifySignal()) emit propertyChanged(row, row); } break; case ObjectInstance::QtGadgetValue: case ObjectInstance::QtGadgetPointer: if (object().object()) { prop.resetOnGadget(object().object()); emit propertyChanged(row, row); } break; default: break; } } void QMetaPropertyAdaptor::propertyUpdated() { Q_ASSERT(senderSignalIndex() >= 0); if (m_notifyGuard) // do not emit change notifications during reading (happens for eg. lazy computed properties like QQItem::childrenRect, that confuses the hell out of QSFPM) return; const int row = m_notifyToRowMap.value(senderSignalIndex()); emit propertyChanged(row, row); } gammaray-2.11.3/core/qmetapropertyadaptor.h000066400000000000000000000041531413310443000207600ustar00rootroot00000000000000/* qmetapropertyadaptor.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_QMETAPROPERTYADAPTOR_H #define GAMMARAY_QMETAPROPERTYADAPTOR_H #include "propertyadaptor.h" #include "objectinstance.h" namespace GammaRay { /** Property adaptor for QMetaProperty/Object-based property access. */ class QMetaPropertyAdaptor : public PropertyAdaptor { Q_OBJECT public: explicit QMetaPropertyAdaptor(QObject *parent = nullptr); ~QMetaPropertyAdaptor() override; int count() const override; PropertyData propertyData(int index) const override; void writeProperty(int index, const QVariant &value) override; void resetProperty(int index) override; protected: void doSetObject(const ObjectInstance &oi) override; private: QString detailString(const QMetaProperty &prop) const; PropertyData propertyMetaData(int index) const; private slots: void propertyUpdated(); private: QHash m_notifyToRowMap; QVector m_rowToPropertyIndex; mutable bool m_notifyGuard; }; } #endif // GAMMARAY_QMETAPROPERTYADAPTOR_H gammaray-2.11.3/core/remote/000077500000000000000000000000001413310443000156105ustar00rootroot00000000000000gammaray-2.11.3/core/remote/localserverdevice.cpp000066400000000000000000000034641413310443000220240ustar00rootroot00000000000000/* localserverdevice.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "localserverdevice.h" #include #include using namespace GammaRay; LocalServerDevice::LocalServerDevice(QObject *parent) : ServerDeviceImpl(parent) { m_server = new QLocalServer(this); m_server->setSocketOptions(QLocalServer::WorldAccessOption); connect(m_server, &QLocalServer::newConnection, this, &ServerDevice::newConnection); } bool LocalServerDevice::listen() { QLocalServer::removeServer(m_address.path()); return m_server->listen(m_address.path()); } bool LocalServerDevice::isListening() const { return m_server->isListening(); } QUrl LocalServerDevice::externalAddress() const { return m_address; } gammaray-2.11.3/core/remote/localserverdevice.h000066400000000000000000000031211413310443000214570ustar00rootroot00000000000000/* localserverdevice.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_LOCALSERVERDEVICE_H #define GAMMARAY_LOCALSERVERDEVICE_H #include "serverdevice.h" #include #include namespace GammaRay { class LocalServerDevice : public ServerDeviceImpl { Q_OBJECT public: explicit LocalServerDevice(QObject *parent = nullptr); bool listen() override; bool isListening() const override; QUrl externalAddress() const override; }; } #endif // GAMMARAY_LOCALSERVERDEVICE_H gammaray-2.11.3/core/remote/remotemodelserver.cpp000066400000000000000000000443421413310443000220660ustar00rootroot00000000000000/* remotemodelserver.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "remotemodelserver.h" #include "server.h" #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace GammaRay; using namespace std; void(*RemoteModelServer::s_registerServerCallback)() = nullptr; RemoteModelServer::RemoteModelServer(const QString &objectName, QObject *parent) : QObject(parent) , m_model(nullptr) , m_dummyBuffer(new QBuffer(&m_dummyData, this)) , m_monitored(false) { setObjectName(objectName); m_dummyBuffer->open(QIODevice::WriteOnly); registerServer(); } RemoteModelServer::~RemoteModelServer() = default; QAbstractItemModel *RemoteModelServer::model() const { return m_model; } void RemoteModelServer::setModel(QAbstractItemModel *model) { if (model == m_model) return; if (m_model) disconnectModel(); m_model = model; if (m_model && m_monitored) connectModel(); if (m_monitored) modelReset(); } void RemoteModelServer::connectModel() { Q_ASSERT(m_model); Model::used(m_model); connect(m_model.data(), &QAbstractItemModel::headerDataChanged, this, &RemoteModelServer::headerDataChanged); connect(m_model.data(), &QAbstractItemModel::rowsInserted, this, &RemoteModelServer::rowsInserted); connect(m_model.data(), &QAbstractItemModel::rowsAboutToBeMoved, this, &RemoteModelServer::rowsAboutToBeMoved); connect(m_model.data(), &QAbstractItemModel::rowsMoved, this, &RemoteModelServer::rowsMoved); connect(m_model.data(), &QAbstractItemModel::rowsRemoved, this, &RemoteModelServer::rowsRemoved); connect(m_model.data(), &QAbstractItemModel::columnsInserted, this, &RemoteModelServer::columnsInserted); connect(m_model.data(), &QAbstractItemModel::columnsMoved, this, &RemoteModelServer::columnsMoved); connect(m_model.data(), &QAbstractItemModel::columnsRemoved, this, &RemoteModelServer::columnsRemoved); connect(m_model.data(), &QAbstractItemModel::dataChanged, this, &RemoteModelServer::dataChanged); connect(m_model.data(), &QAbstractItemModel::layoutChanged, this, &RemoteModelServer::layoutChanged); connect(m_model.data(), &QAbstractItemModel::modelReset, this, &RemoteModelServer::modelReset); connect(m_model.data(), &QObject::destroyed, this, &RemoteModelServer::modelDeleted); } void RemoteModelServer::disconnectModel() { Q_ASSERT(m_model); Model::unused(m_model); disconnect(m_model.data(), &QAbstractItemModel::headerDataChanged, this, &RemoteModelServer::headerDataChanged); disconnect(m_model.data(), &QAbstractItemModel::rowsInserted, this, &RemoteModelServer::rowsInserted); disconnect(m_model.data(), &QAbstractItemModel::rowsAboutToBeMoved, this, &RemoteModelServer::rowsAboutToBeMoved); disconnect(m_model.data(), &QAbstractItemModel::rowsMoved, this, &RemoteModelServer::rowsMoved); disconnect(m_model.data(), &QAbstractItemModel::rowsRemoved, this, &RemoteModelServer::rowsRemoved); disconnect(m_model.data(), &QAbstractItemModel::columnsInserted, this, &RemoteModelServer::columnsInserted); disconnect(m_model.data(), &QAbstractItemModel::columnsMoved, this, &RemoteModelServer::columnsMoved); disconnect(m_model.data(), &QAbstractItemModel::columnsRemoved, this, &RemoteModelServer::columnsRemoved); disconnect(m_model.data(), &QAbstractItemModel::dataChanged, this, &RemoteModelServer::dataChanged); disconnect(m_model.data(), &QAbstractItemModel::layoutChanged, this, &RemoteModelServer::layoutChanged); disconnect(m_model.data(), &QAbstractItemModel::modelReset, this, &RemoteModelServer::modelReset); disconnect(m_model.data(), &QObject::destroyed, this, &RemoteModelServer::modelDeleted); } void RemoteModelServer::newRequest(const GammaRay::Message &msg) { if (!m_model && msg.type() != Protocol::ModelSyncBarrier) return; ProbeGuard g; switch (msg.type()) { case Protocol::ModelRowColumnCountRequest: { quint32 size; msg >> size; Q_ASSERT(size > 0); Message reply(m_myAddress, Protocol::ModelRowColumnCountReply); reply << size; for (quint32 i = 0; i < size; ++i) { Protocol::ModelIndex index; msg >> index; const QModelIndex qmIndex = Protocol::toQModelIndex(m_model, index); qint32 rowCount = -1, columnCount = -1; if (index.isEmpty() || qmIndex.isValid()) { rowCount = m_model->rowCount(qmIndex); columnCount = m_model->columnCount(qmIndex); } reply << index << rowCount << columnCount; } sendMessage(reply); break; } case Protocol::ModelContentRequest: { quint32 size; msg >> size; Q_ASSERT(size > 0); QVector indexes; indexes.reserve(size); for (quint32 i = 0; i < size; ++i) { Protocol::ModelIndex index; msg >> index; const QModelIndex qmIndex = Protocol::toQModelIndex(m_model, index); if (!qmIndex.isValid()) continue; indexes.push_back(qmIndex); } if (indexes.isEmpty()) break; Message msg(m_myAddress, Protocol::ModelContentReply); msg << quint32(indexes.size()); for (const auto &qmIndex : qAsConst(indexes)) msg << Protocol::fromQModelIndex(qmIndex) << filterItemData(m_model->itemData(qmIndex)) << qint32(m_model->flags(qmIndex)); sendMessage(msg); break; } case Protocol::ModelHeaderRequest: { qint8 orientation; qint32 section; msg >> orientation >> section; Q_ASSERT(orientation == Qt::Horizontal || orientation == Qt::Vertical); Q_ASSERT(section >= 0); QHash data; // TODO: add all roles data.insert(Qt::DisplayRole, m_model->headerData(section, static_cast(orientation), Qt::DisplayRole)); data.insert(Qt::ToolTipRole, m_model->headerData(section, static_cast(orientation), Qt::ToolTipRole)); Message msg(m_myAddress, Protocol::ModelHeaderReply); msg << orientation << section << data; sendMessage(msg); break; } case Protocol::ModelSetDataRequest: { Protocol::ModelIndex index; int role; QVariant value; msg >> index >> role >> value; m_model->setData(Protocol::toQModelIndex(m_model, index), value, role); break; } case Protocol::ModelSortRequest: { quint32 column, order; msg >> column >> order; m_model->sort(column, (Qt::SortOrder)order); break; } case Protocol::ModelSyncBarrier: { qint32 barrierId; msg >> barrierId; Message reply(m_myAddress, Protocol::ModelSyncBarrier); reply << barrierId; sendMessage(reply); break; } } } QMap RemoteModelServer::filterItemData(QMap &&itemData) const { for (auto it = itemData.begin(); it != itemData.end();) { if (!it.value().isValid()) { it = itemData.erase(it); } else if (it.value().userType() == qMetaTypeId()) { // see also: https://bugreports.qt-project.org/browse/QTBUG-33321 const QIcon icon = it.value().value(); ///TODO: what size to use? icon.availableSizes is empty... if (!icon.isNull()) it.value() = icon.pixmap(QSize(16, 16)); ++it; } else if (canSerialize(it.value())) { ++it; } else { // qWarning() << "Cannot serialize QVariant of type" << it.value().typeName(); it = itemData.erase(it); } } return std::move(itemData); } bool RemoteModelServer::canSerialize(const QVariant &value) const { if (qstrcmp(value.typeName(), "QJSValue") == 0 || qstrcmp(value.typeName(), "QJsonObject") == 0 || qstrcmp(value.typeName(), "QJsonValue") == 0 || qstrcmp(value.typeName(), "QJsonArray") == 0) { // QJSValue tries to serialize nested elements and asserts if that fails // too bad it can contain QObject* as nested element, which obviously can't be serialized... // QJsonObject serialization fails due to QTBUG-73437 return false; } // recurse into containers if (value.canConvert()) { QSequentialIterable it = value.value(); for (const QVariant &v : it) { if (!canSerialize(v)) return false; } // note: do not return true here, the fact we can write every single element // does not mean we can write the entire thing, or vice vesa... } else if (value.canConvert()) { auto iterable = value.value(); for (auto it = iterable.begin(); it != iterable.end(); ++it) { if (!canSerialize(it.value()) || !canSerialize(it.key())) return false; } // see above } // whitelist a few expensive to encode types we know we can serialize if (value.userType() == qMetaTypeId() || value.userType() == qMetaTypeId()) return true; // ugly, but there doesn't seem to be a better way atm to find out without trying m_dummyBuffer->seek(0); QDataStream stream(m_dummyBuffer); return QMetaType::save(stream, value.userType(), value.constData()); } void RemoteModelServer::modelMonitored(bool monitored) { if (m_monitored == monitored) return; m_monitored = monitored; if (m_model) { if (m_monitored) connectModel(); else disconnectModel(); } } void RemoteModelServer::dataChanged(const QModelIndex &begin, const QModelIndex &end, const QVector &roles) { if (!isConnected()) return; Message msg(m_myAddress, Protocol::ModelContentChanged); msg << Protocol::fromQModelIndex(begin) << Protocol::fromQModelIndex(end) << roles; sendMessage(msg); } void RemoteModelServer::headerDataChanged(Qt::Orientation orientation, int first, int last) { if (!isConnected()) return; Message msg(m_myAddress, Protocol::ModelHeaderChanged); msg << qint8(orientation) << first << last; sendMessage(msg); } void RemoteModelServer::rowsInserted(const QModelIndex &parent, int start, int end) { sendAddRemoveMessage(Protocol::ModelRowsAdded, parent, start, end); } void RemoteModelServer::rowsAboutToBeMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destinationParent, int destinationRow) { Q_UNUSED(sourceStart); Q_UNUSED(sourceEnd); Q_UNUSED(destinationRow); m_preOpIndexes.push_back(Protocol::fromQModelIndex(sourceParent)); m_preOpIndexes.push_back(Protocol::fromQModelIndex(destinationParent)); } void RemoteModelServer::rowsMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destinationParent, int destinationRow) { Q_UNUSED(sourceParent); Q_UNUSED(destinationParent); Q_ASSERT(m_preOpIndexes.size() >= 2); const auto destParentIdx = m_preOpIndexes.takeLast(); const auto sourceParentIdx = m_preOpIndexes.takeLast(); sendMoveMessage(Protocol::ModelRowsMoved, sourceParentIdx, sourceStart, sourceEnd, destParentIdx, destinationRow); } void RemoteModelServer::rowsRemoved(const QModelIndex &parent, int start, int end) { sendAddRemoveMessage(Protocol::ModelRowsRemoved, parent, start, end); } void RemoteModelServer::columnsInserted(const QModelIndex &parent, int start, int end) { sendAddRemoveMessage(Protocol::ModelColumnsAdded, parent, start, end); } void RemoteModelServer::columnsMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destinationParent, int destinationColumn) { sendMoveMessage(Protocol::ModelColumnsMoved, Protocol::fromQModelIndex(sourceParent), sourceStart, sourceEnd, Protocol::fromQModelIndex(destinationParent), destinationColumn); } void RemoteModelServer::columnsRemoved(const QModelIndex &parent, int start, int end) { sendAddRemoveMessage(Protocol::ModelColumnsRemoved, parent, start, end); } void RemoteModelServer::layoutChanged(const QList &parents, QAbstractItemModel::LayoutChangeHint hint) { QVector indexes; indexes.reserve(parents.size()); for (const auto &index : parents) indexes.push_back(Protocol::fromQModelIndex(index)); sendLayoutChanged(indexes, hint); } void RemoteModelServer::sendLayoutChanged(const QVector< Protocol::ModelIndex > &parents, quint32 hint) { if (!isConnected()) return; Message msg(m_myAddress, Protocol::ModelLayoutChanged); msg << parents << hint; sendMessage(msg); } void RemoteModelServer::modelReset() { if (!isConnected()) return; sendMessage(Message(m_myAddress, Protocol::ModelReset)); } void RemoteModelServer::sendAddRemoveMessage(Protocol::MessageType type, const QModelIndex &parent, int start, int end) { if (!isConnected()) return; Message msg(m_myAddress, type); msg << Protocol::fromQModelIndex(parent) << start << end; sendMessage(msg); } void RemoteModelServer::sendMoveMessage(Protocol::MessageType type, const Protocol::ModelIndex &sourceParent, int sourceStart, int sourceEnd, const Protocol::ModelIndex &destinationParent, int destinationIndex) { if (!isConnected()) return; Message msg(m_myAddress, type); msg << sourceParent << qint32(sourceStart) << qint32(sourceEnd) << destinationParent << qint32(destinationIndex); sendMessage(msg); } void RemoteModelServer::modelDeleted() { m_model = nullptr; if (m_monitored) modelReset(); } void RemoteModelServer::registerServer() { if (Q_UNLIKELY(s_registerServerCallback)) { // called from the ctor, so we can't rely on virtuals s_registerServerCallback(); return; } m_myAddress = Server::instance()->registerObject(objectName(), this, Server::ExportProperties); Server::instance()->registerMessageHandler(m_myAddress, this, "newRequest"); Server::instance()->registerMonitorNotifier(m_myAddress, this, "modelMonitored"); connect(Endpoint::instance(), &Endpoint::disconnected, this, [this] { modelMonitored(); }); } bool RemoteModelServer::isConnected() const { return Endpoint::isConnected(); } void RemoteModelServer::sendMessage(const Message &msg) const { Endpoint::send(msg); } bool RemoteModelServer::proxyDynamicSortFilter() const { if (auto proxy = qobject_cast(m_model)) return proxy->dynamicSortFilter(); return false; } void RemoteModelServer::setProxyDynamicSortFilter(bool dynamicSortFilter) { if (auto proxy = qobject_cast(m_model)) proxy->setDynamicSortFilter(dynamicSortFilter); } Qt::CaseSensitivity RemoteModelServer::proxyFilterCaseSensitivity() const { if (auto proxy = qobject_cast(m_model)) return proxy->filterCaseSensitivity(); return Qt::CaseSensitive; } void RemoteModelServer::setProxyFilterCaseSensitivity(Qt::CaseSensitivity caseSensitivity) { if (auto proxy = qobject_cast(m_model)) proxy->setFilterCaseSensitivity(caseSensitivity); } int RemoteModelServer::proxyFilterKeyColumn() const { if (auto proxy = qobject_cast(m_model)) return proxy->filterKeyColumn(); return 0; } void RemoteModelServer::setProxyFilterKeyColumn(int column) { if (auto proxy = qobject_cast(m_model)) proxy->setFilterKeyColumn(column); } QRegExp RemoteModelServer::proxyFilterRegExp() const { if (auto proxy = qobject_cast(m_model)) return proxy->filterRegExp(); return QRegExp(); } void RemoteModelServer::setProxyFilterRegExp(const QRegExp ®Exp) { if (auto proxy = qobject_cast(m_model)) proxy->setFilterRegExp(regExp); } gammaray-2.11.3/core/remote/remotemodelserver.h000066400000000000000000000137761413310443000215420ustar00rootroot00000000000000/* remotemodelserver.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_REMOTEMODELSERVER_H #define GAMMARAY_REMOTEMODELSERVER_H #include #include #include #include QT_BEGIN_NAMESPACE class QBuffer; class QAbstractItemModel; QT_END_NAMESPACE namespace GammaRay { class Message; /** Provides the server-side interface for a QAbstractItemModel to be used from a separate process. * If the source model is a QSortFilterProxyModel, this also forwards properties for configuring * the proxy behavior, enabling server-side searching and sorting. */ class RemoteModelServer : public QObject { Q_OBJECT Q_PROPERTY(bool dynamicSortFilter READ proxyDynamicSortFilter WRITE setProxyDynamicSortFilter) Q_PROPERTY( Qt::CaseSensitivity filterCaseSensitivity READ proxyFilterCaseSensitivity WRITE setProxyFilterCaseSensitivity) Q_PROPERTY(int filterKeyColumn READ proxyFilterKeyColumn WRITE setProxyFilterKeyColumn) Q_PROPERTY(QRegExp filterRegExp READ proxyFilterRegExp WRITE setProxyFilterRegExp) public: /** Registers a new model server object with name @p objectName (must be unique). */ explicit RemoteModelServer(const QString &objectName, QObject *parent = nullptr); ~RemoteModelServer() override; /** Returns the source model. */ QAbstractItemModel *model() const; /** Set the source model for this model server instance. */ void setModel(QAbstractItemModel *model); public slots: void newRequest(const GammaRay::Message &msg); /** Notifications about an object on the client side (un)monitoring this object. * If no one is watching, we don't send out any change notification to reduce network traffice. */ void modelMonitored(bool monitored = false); private: void connectModel(); void disconnectModel(); void sendAddRemoveMessage(Protocol::MessageType type, const QModelIndex &parent, int start, int end); void sendMoveMessage(Protocol::MessageType type, const Protocol::ModelIndex &sourceParent, int sourceStart, int sourceEnd, const Protocol::ModelIndex &destinationParent, int destinationIndex); QMap< int, QVariant > filterItemData(QMap &&itemData) const; void sendLayoutChanged( const QVector &parents = QVector(), quint32 hint = 0); bool canSerialize(const QVariant &value) const; // proxy model settings bool proxyDynamicSortFilter() const; void setProxyDynamicSortFilter(bool dynamicSortFilter); Qt::CaseSensitivity proxyFilterCaseSensitivity() const; void setProxyFilterCaseSensitivity(Qt::CaseSensitivity caseSensitivity); int proxyFilterKeyColumn() const; void setProxyFilterKeyColumn(int column); QRegExp proxyFilterRegExp() const; void setProxyFilterRegExp(const QRegExp ®Exp); // unit test hooks static void (*s_registerServerCallback)(); void registerServer(); virtual bool isConnected() const; virtual void sendMessage(const Message &msg) const; friend class FakeRemoteModelServer; private slots: void dataChanged(const QModelIndex &begin, const QModelIndex &end, const QVector &roles = QVector()); void headerDataChanged(Qt::Orientation orientation, int first, int last); void rowsInserted(const QModelIndex &parent, int start, int end); void rowsAboutToBeMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destinationParent, int destinationRow); void rowsMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destinationParent, int destinationRow); void rowsRemoved(const QModelIndex &parent, int start, int end); void columnsInserted(const QModelIndex &parent, int start, int end); void columnsMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destinationParent, int destinationColumn); void columnsRemoved(const QModelIndex &parent, int start, int end); void layoutChanged(const QList &parents, QAbstractItemModel::LayoutChangeHint hint); void modelReset(); void modelDeleted(); private: QPointer m_model; // those two are used for canSerialize, since recreating the QBuffer is somewhat expensive, // especially since being a QObject triggers all kind of GammaRay internals QByteArray m_dummyData; QBuffer *m_dummyBuffer; // converted model indexes from aboutToBeX signals, needed in cases where the operation changes // the serialized index (move to sub-tree of source parent for example) // as operations can occur nested, we need to have a stack for this QList m_preOpIndexes; Protocol::ObjectAddress m_myAddress; bool m_monitored; }; } #endif gammaray-2.11.3/core/remote/selectionmodelserver.cpp000066400000000000000000000071731413310443000225610ustar00rootroot00000000000000/* selectionmodelserver.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "selectionmodelserver.h" #include "server.h" #include using namespace GammaRay; SelectionModelServer::SelectionModelServer(const QString &objectName, QAbstractItemModel *model, QObject *parent) : NetworkSelectionModel(objectName, model, parent) , m_timer(new QTimer(this)) , m_monitored(false) { // We do use a timer to group requests to avoid network overhead m_timer->setSingleShot(true); m_timer->setInterval(125); connect(m_timer, &QTimer::timeout, this, &SelectionModelServer::timeout); m_myAddress = Server::instance()->registerObject(objectName, this, Server::ExportNothing); Server::instance()->registerMessageHandler(m_myAddress, this, "newMessage"); Server::instance()->registerMonitorNotifier(m_myAddress, this, "modelMonitored"); connect(Endpoint::instance(), &Endpoint::disconnected, this, [this]{ modelMonitored(); }); } SelectionModelServer::~SelectionModelServer() = default; bool SelectionModelServer::isConnected() const { return NetworkSelectionModel::isConnected() && m_monitored; } void SelectionModelServer::timeout() { sendSelection(); } void SelectionModelServer::modelMonitored(bool monitored) { if (m_monitored == monitored) return; if (m_monitored) disconnectModel(); m_monitored = monitored; if (m_monitored) connectModel(); } void SelectionModelServer::connectModel() { Q_ASSERT(model()); auto startTimer = [this](){ m_timer->start(); }; connect(model(), &QAbstractItemModel::modelReset, m_timer, startTimer); connect(model(), &QAbstractItemModel::rowsInserted, m_timer, startTimer); connect(model(), &QAbstractItemModel::rowsMoved, m_timer, startTimer); connect(model(), &QAbstractItemModel::columnsInserted, m_timer, startTimer); connect(model(), &QAbstractItemModel::columnsMoved, m_timer, startTimer); connect(model(), &QAbstractItemModel::layoutChanged, m_timer, startTimer); } void SelectionModelServer::disconnectModel() { if (!model()) return; disconnect(model(), &QAbstractItemModel::modelReset, m_timer, nullptr); disconnect(model(), &QAbstractItemModel::rowsInserted, m_timer, nullptr); disconnect(model(), &QAbstractItemModel::rowsMoved, m_timer, nullptr); disconnect(model(), &QAbstractItemModel::columnsInserted, m_timer, nullptr); disconnect(model(), &QAbstractItemModel::columnsMoved, m_timer, nullptr); disconnect(model(), &QAbstractItemModel::layoutChanged, m_timer, nullptr); } gammaray-2.11.3/core/remote/selectionmodelserver.h000066400000000000000000000036501413310443000222220ustar00rootroot00000000000000/* selectionmodelserver.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_SELECTIONMODELSERVER_H #define GAMMARAY_SELECTIONMODELSERVER_H #include QT_BEGIN_NAMESPACE class QTimer; QT_END_NAMESPACE namespace GammaRay { /** Server-side of the network transparent QItemSelection model. */ class SelectionModelServer : public NetworkSelectionModel { Q_OBJECT public: explicit SelectionModelServer(const QString &objectName, QAbstractItemModel *model, QObject *parent); ~SelectionModelServer() override; protected: bool isConnected() const override; private slots: void timeout(); void modelMonitored(bool monitored = false); private: void connectModel(); void disconnectModel(); QTimer *m_timer; bool m_monitored; }; } #endif // GAMMARAY_SELECTIONMODELSERVER_H gammaray-2.11.3/core/remote/server.cpp000066400000000000000000000252701413310443000176300ustar00rootroot00000000000000/* server.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include "server.h" #include "serverdevice.h" #include "probe.h" #include "probesettings.h" #include "multisignalmapper.h" #include #include #include #ifdef Q_OS_ANDROID # include #endif #include #include #include #include using namespace GammaRay; using namespace std; Server::Server(QObject *parent) : Endpoint(parent) , m_serverDevice(nullptr) , m_nextAddress(endpointAddress()) , m_broadcastTimer(new QTimer(this)) , m_signalMapper(new MultiSignalMapper(this)) { Message::resetNegotiatedDataVersion(); if (!ProbeSettings::value(QStringLiteral("RemoteAccessEnabled"), true).toBool()) return; m_serverDevice = ServerDevice::create(serverAddress(), this); if (!m_serverDevice) return; connect(m_serverDevice, &ServerDevice::newConnection, this, &Server::newConnection); m_broadcastTimer->setInterval(5 * 1000); m_broadcastTimer->setSingleShot(false); #ifndef Q_OS_ANDROID m_broadcastTimer->start(); #endif connect(m_broadcastTimer, &QTimer::timeout, this, &Server::broadcast); connect(this, &Server::disconnected, m_broadcastTimer, [this]{ m_broadcastTimer->start(); }); connect(m_signalMapper, &MultiSignalMapper::signalEmitted, this, &Server::forwardSignal); Endpoint::addObjectNameAddressMapping(QStringLiteral( "com.kdab.GammaRay.PropertySyncer"), ++m_nextAddress); m_propertySyncer->setAddress(m_nextAddress); Endpoint::registerObject(QStringLiteral("com.kdab.GammaRay.PropertySyncer"), m_propertySyncer); registerMessageHandler(m_nextAddress, m_propertySyncer, "handleMessage"); } Server::~Server() = default; bool Server::listen() { Q_ASSERT(!m_serverDevice->isListening()); if (!m_serverDevice->listen()) { return false; } return true; } bool Server::isListening() const { return m_serverDevice->isListening(); } Server *Server::instance() { Q_ASSERT(s_instance); return static_cast(s_instance); } bool Server::isRemoteClient() const { return false; } QUrl Server::serverAddress() const { #ifdef Q_OS_ANDROID QUrl url(QString(QLatin1String("local://%1/+gammaray_socket")).arg(QDir::homePath())); #else QUrl url(ProbeSettings::value(QStringLiteral( "ServerAddress"), GAMMARAY_DEFAULT_ANY_TCP_URL).toString()); if (url.scheme().isEmpty()) url.setScheme(QStringLiteral("tcp")); if (url.port() <= 0) url.setPort(defaultPort()); #endif return url; } void Server::newConnection() { if (isConnected()) { cerr << Q_FUNC_INFO << " connected already, refusing incoming connection." << endl; auto con = m_serverDevice->nextPendingConnection(); con->close(); con->deleteLater(); return; } m_broadcastTimer->stop(); auto con = m_serverDevice->nextPendingConnection(); // FIXME Use proper type for m_serverDevice->nextPendingConnection, instead // of relying on runtime-connect to a slot which doesn't exist in QIODevice connect(con, SIGNAL(disconnected()), con, SLOT(deleteLater())); setDevice(con); sendServerGreeting(); emit connectionEstablished(); } void Server::sendServerGreeting() { // send greeting message for protocol version check { Message msg(endpointAddress(), Protocol::ServerVersion); msg << Protocol::version(); send(msg); } { Message msg(endpointAddress(), Protocol::ServerInfo); msg << label() << key() << pid() << Message::highestSupportedDataVersion(); // TODO: expand with anything else needed here: Qt/GammaRay version, hostname, that kind of stuff send(msg); } { Message msg(endpointAddress(), Protocol::ObjectMapReply); msg << objectAddresses(); send(msg); } } void Server::messageReceived(const Message &msg) { if (msg.address() == endpointAddress()) { switch (msg.type()) { case Protocol::ClientDataVersionNegotiated: { quint8 version; msg >> version; { Message msg(endpointAddress(), Protocol::ServerDataVersionNegotiated); msg << version; send(msg); } Message::setNegotiatedDataVersion(version); break; } case Protocol::ObjectMonitored: case Protocol::ObjectUnmonitored: { Protocol::ObjectAddress addr; msg >> addr; Q_ASSERT(addr > Protocol::InvalidObjectAddress); m_propertySyncer->setObjectEnabled(addr, msg.type() == Protocol::ObjectMonitored); auto it = m_monitorNotifiers.constFind(addr); if (it == m_monitorNotifiers.constEnd()) break; // cout << Q_FUNC_INFO << " un/monitor " << (int)addr << endl; QMetaObject::invokeMethod(it.value().first, it.value().second, Q_ARG(bool, msg.type() == Protocol::ObjectMonitored)); break; } } } else { dispatchMessage(msg); } } void Server::invokeObject(const QString &objectName, const char *method, const QVariantList &args) const { Endpoint::invokeObject(objectName, method, args); QObject *object = ObjectBroker::objectInternal(objectName); Q_ASSERT(object); // also invoke locally for in-process mode invokeObjectLocal(object, method, args); } Protocol::ObjectAddress Server::registerObject(const QString &name, QObject *object) { return registerObject(name, object, ExportEverything); } static bool isNotifySignal(const QMetaObject *mo, const QMetaMethod &method) { for (int i = 0; i < mo->propertyCount(); ++i) { const auto prop = mo->property(i); if (!prop.hasNotifySignal()) continue; if (prop.notifySignal().methodIndex() == method.methodIndex()) return true; } return false; } Protocol::ObjectAddress Server::registerObject(const QString &name, QObject *object, Server::ObjectExportOptions exportOptions) { addObjectNameAddressMapping(name, ++m_nextAddress); Protocol::ObjectAddress address = Endpoint::registerObject(name, object); Q_ASSERT(m_nextAddress); Q_ASSERT(m_nextAddress == address); if (isConnected()) { Message msg(endpointAddress(), Protocol::ObjectAdded); msg << name << m_nextAddress; send(msg); } if (exportOptions & ExportSignals) { const QMetaObject *meta = object->metaObject(); for (int i = 0; i < meta->methodCount(); ++i) { const QMetaMethod method = meta->method(i); if (method.methodType() != QMetaMethod::Signal) continue; if ((exportOptions & ExportProperties) && isNotifySignal(meta, method)) continue; // no need to forward property change signals if we forward the property already m_signalMapper->connectToSignal(object, method); } } if (exportOptions & ExportProperties) m_propertySyncer->addObject(address, object); return address; } void Server::forwardSignal(QObject *sender, int signalIndex, const QVector< QVariant > &args) { if (!isConnected()) return; Q_ASSERT(sender); Q_ASSERT(signalIndex >= 0); const QMetaMethod signal = sender->metaObject()->method(signalIndex); Q_ASSERT(signal.methodType() == QMetaMethod::Signal); QByteArray name = signal.methodSignature(); // get the name of the function to invoke, excluding the parens and function arguments. name = name.mid(0, name.indexOf('(')); QVariantList v; v.reserve(args.size()); foreach (const QVariant &arg, args) v.push_back(arg); Endpoint::invokeObject(sender->objectName(), name, v); } void Server::registerMonitorNotifier(Protocol::ObjectAddress address, QObject *receiver, const char *monitorNotifier) { Q_ASSERT(address != Protocol::InvalidObjectAddress); Q_ASSERT(receiver); Q_ASSERT(monitorNotifier); m_monitorNotifiers.insert(address, qMakePair(receiver, monitorNotifier)); } void Server::handlerDestroyed(Protocol::ObjectAddress objectAddress, const QString &objectName) { removeObjectNameAddressMapping(objectName); m_monitorNotifiers.remove(objectAddress); if (isConnected()) { Message msg(endpointAddress(), Protocol::ObjectRemoved); msg << objectName; send(msg); } } void Server::objectDestroyed(Protocol::ObjectAddress /*objectAddress*/, const QString &objectName, QObject *object) { Q_UNUSED(object); removeObjectNameAddressMapping(objectName); if (isConnected()) { Message msg(endpointAddress(), Protocol::ObjectRemoved); msg << objectName; send(msg); } } void Server::broadcast() { if (!Server::instance()->isListening()) return; QByteArray datagram; QDataStream stream(&datagram, QIODevice::WriteOnly); stream << Protocol::broadcastFormatVersion(); stream << Protocol::version(); stream << externalAddress(); stream << label(); // TODO integrate hostname m_serverDevice->broadcast(datagram); } QUrl Server::externalAddress() const { if (!m_serverDevice) return QUrl(); return m_serverDevice->externalAddress(); } QString Server::errorString() const { if (!m_serverDevice) return QString(); return m_serverDevice->errorString(); } gammaray-2.11.3/core/remote/server.h000066400000000000000000000112011413310443000172620ustar00rootroot00000000000000/* server.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_SERVER_H #define GAMMARAY_SERVER_H #include "gammaray_core_export.h" #include #include QT_BEGIN_NAMESPACE class QTcpServer; class QUdpSocket; class QTimer; QT_END_NAMESPACE namespace GammaRay { class MultiSignalMapper; class ServerDevice; /** Server side connection endpoint. */ class GAMMARAY_CORE_EXPORT Server : public Endpoint { Q_OBJECT public: explicit Server(QObject *parent = nullptr); ~Server() override; /** Indicates which parts of a QObject should be exported to the client. */ enum ObjectExportOption { ExportNothing = 0x0, ExportSignals = 0x1, ExportProperties = 0x2, ExportEverything = ExportProperties | ExportSignals }; Q_DECLARE_FLAGS(ObjectExportOptions, ObjectExportOption) bool listen(); bool isListening() const; /** * Register a server-side QObject to send/receive messages to/from the client side. * This is equivalent to registerObject(name, object, ExportEverything); */ Protocol::ObjectAddress registerObject(const QString &name, QObject *object) override; /** * Register a server-side QObject for exporting to the client. */ Protocol::ObjectAddress registerObject(const QString &name, QObject *object, ObjectExportOptions exportOptions); /** * Register a callback slot @p monitorNotifier on object @p receiver that is called if the usage * of an object with address @p address changes on the client side. * * This is useful for example to disable expensive operations like sending large amounts of * data if nobody is interested anyway. */ void registerMonitorNotifier(Protocol::ObjectAddress address, QObject *receiver, const char *monitorNotifier); /** Singleton accessor. */ static Server *instance(); /** * Call @p method on the remote client and also directly on the local object identified by @p objectName. */ void invokeObject(const QString &objectName, const char *method, const QVariantList &args = QVariantList()) const override; bool isRemoteClient() const override; QUrl serverAddress() const override; /** * Returns an address suitable to connect to this server. * In contrast to serverAddress(), which returns the listening address, which might not * be identical for all protocols (such as TCP). */ QUrl externalAddress() const; /** * Returns the current error of the device to be displayed to the user. */ QString errorString() const; protected: void messageReceived(const Message &msg) override; void handlerDestroyed(Protocol::ObjectAddress objectAddress, const QString &objectName) override; void objectDestroyed(Protocol::ObjectAddress objectAddress, const QString &objectName, QObject *object) override; private slots: void newConnection(); void broadcast(); /** * Forward the signal that triggered the call to this slot to the remote client if connected. */ void forwardSignal(QObject *sender, int signalIndex, const QVector &args); private: void sendServerGreeting(); private: ServerDevice *m_serverDevice; QHash > m_monitorNotifiers; Protocol::ObjectAddress m_nextAddress; QString m_label; QTimer *m_broadcastTimer; MultiSignalMapper *m_signalMapper; }; } #endif gammaray-2.11.3/core/remote/serverdevice.cpp000066400000000000000000000040771413310443000210120ustar00rootroot00000000000000/* serverdevice.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "serverdevice.h" #include "tcpserverdevice.h" #include "localserverdevice.h" #include #include using namespace GammaRay; ServerDevice::ServerDevice(QObject *parent) : QObject(parent) { } ServerDevice::~ServerDevice() = default; void ServerDevice::setServerAddress(const QUrl &serverAddress) { m_address = serverAddress; } void ServerDevice::broadcast(const QByteArray &data) { Q_UNUSED(data); } ServerDevice *ServerDevice::create(const QUrl &serverAddress, QObject *parent) { ServerDevice *device = nullptr; if (serverAddress.scheme() == QLatin1String("tcp")) device = new TcpServerDevice(parent); else if (serverAddress.scheme() == QLatin1String("local")) device = new LocalServerDevice(parent); if (!device) { qWarning() << "Unsupported transport protocol:" << serverAddress.toString(); return nullptr; } device->setServerAddress(serverAddress); return device; } gammaray-2.11.3/core/remote/serverdevice.h000066400000000000000000000054431413310443000204550ustar00rootroot00000000000000/* serverdevice.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_SERVERDEVICE_H #define GAMMARAY_SERVERDEVICE_H #include #include QT_BEGIN_NAMESPACE class QIODevice; QT_END_NAMESPACE namespace GammaRay { /** Abstract base class for the actual transport implementation. */ class ServerDevice : public QObject { Q_OBJECT public: explicit ServerDevice(QObject *parent = nullptr); ~ServerDevice() override; void setServerAddress(const QUrl &serverAddress); virtual bool listen() = 0; virtual bool isListening() const = 0; virtual QString errorString() const = 0; virtual QIODevice *nextPendingConnection() = 0; /** An externally usable address of this server. * This might be different from @p serverAddress as passed in the constructor. */ virtual QUrl externalAddress() const = 0; static ServerDevice *create(const QUrl &serverAddress, QObject *parent = nullptr); /** Broadcast the given message on an appropriate channel, if backend supports broadcasting. */ virtual void broadcast(const QByteArray &data); signals: void newConnection(); protected: QUrl m_address; }; template class ServerDeviceImpl : public ServerDevice { public: explicit inline ServerDeviceImpl(QObject *parent = nullptr) : ServerDevice(parent) , m_server(nullptr) { } ~ServerDeviceImpl() override = default; QString errorString() const override { return m_server->errorString(); } QIODevice *nextPendingConnection() override { Q_ASSERT(m_server->hasPendingConnections()); return m_server->nextPendingConnection(); } protected: ServerT *m_server; }; } #endif // GAMMARAY_SERVERDEVICE_H gammaray-2.11.3/core/remote/serverproxymodel.cpp000066400000000000000000000022261413310443000217470ustar00rootroot00000000000000/* serverproxymodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "serverproxymodel.h" gammaray-2.11.3/core/remote/serverproxymodel.h000066400000000000000000000075521413310443000214230ustar00rootroot00000000000000/* serverproxymodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_SERVERPROXYMODEL_H #define GAMMARAY_SERVERPROXYMODEL_H #include #include #include #include #include namespace GammaRay { /** Sort/filter proxy model for server-side use to pass through extra roles in itemData(). * Every remoted proxy model should be wrapped into this template, unless you already have * a special implementation for itemData() handling this. */ template class ServerProxyModel : public BaseProxy { public: explicit ServerProxyModel(QObject *parent = nullptr) : BaseProxy(parent) , m_sourceModel(nullptr) , m_active(false) { } /** Additional roles used from the source model for transfer to the client. */ void addRole(int role) { m_extraRoles.push_back(role); } /** Additional roles used from the proxy model itself for transfer to the client. * This is useful if BaseProxy overrides data(). */ void addProxyRole(int role) { m_extraProxyRoles.push_back(role); } QMap itemData(const QModelIndex &index) const override { const QModelIndex sourceIndex = BaseProxy::mapToSource(index); auto d = BaseProxy::sourceModel()->itemData(sourceIndex); for (int role : m_extraRoles) d.insert(role, sourceIndex.data(role)); for (int role : m_extraProxyRoles) d.insert(role, index.data(role)); return d; } void setSourceModel(QAbstractItemModel *sourceModel) override { m_sourceModel = sourceModel; if (m_active && sourceModel) { Model::used(sourceModel); BaseProxy::setSourceModel(sourceModel); } } QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override { Model::used(this); return BaseProxy::index(row, column, parent); } protected: void customEvent(QEvent *event) override { if (event->type() == ModelEvent::eventType()) { auto mev = static_cast(event); m_active = mev->used(); if (m_sourceModel) { QCoreApplication::sendEvent(m_sourceModel, event); if (mev->used() && BaseProxy::sourceModel() != m_sourceModel) BaseProxy::setSourceModel(m_sourceModel); else if (!mev->used()) BaseProxy::setSourceModel(nullptr); } } BaseProxy::customEvent(event); } private: QVector m_extraRoles; QVector m_extraProxyRoles; QPointer m_sourceModel; bool m_active; }; } #endif // GAMMARAY_SERVERPROXYMODEL_H gammaray-2.11.3/core/remote/tcpserverdevice.cpp000066400000000000000000000107041413310443000215130ustar00rootroot00000000000000/* tcpserverdevice.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "tcpserverdevice.h" #include "server.h" #include #include #include using namespace GammaRay; TcpServerDevice::TcpServerDevice(QObject *parent) : ServerDeviceImpl(parent) , m_broadcastSocket(new QUdpSocket(this)) { m_server = new QTcpServer(this); connect(m_server, &QTcpServer::newConnection, this, &ServerDevice::newConnection); } TcpServerDevice::~TcpServerDevice() = default; bool TcpServerDevice::listen() { const QHostAddress address(m_address.host()); // try the requested port first, and fall back to a random port otherwise if (m_server->listen(address, m_address.port())) return true; return m_server->listen(address, 0); } bool TcpServerDevice::isListening() const { return m_server->isListening(); } QString TcpServerDevice::bestAvailableIP(const QHostAddress &address) const { QString firstHostFound; foreach (const QNetworkInterface &inter, QNetworkInterface::allInterfaces()) { if (!(inter.flags() & QNetworkInterface::IsUp) || !(inter.flags() & QNetworkInterface::IsRunning) || (inter.flags() & QNetworkInterface::IsLoopBack)) continue; foreach (const QNetworkAddressEntry &addrEntry, inter.addressEntries()) { const QHostAddress addr = addrEntry.ip(); // Return the ip according to the listening server protocol. if (addr.protocol() != m_server->serverAddress().protocol() || !addr.scopeId().isEmpty()) continue; //If its our desired IP (e.g. from --listen) return early if (addr == address) return addr.toString(); if (firstHostFound.isEmpty()) firstHostFound = addr.toString(); } } return firstHostFound; } QUrl TcpServerDevice::externalAddress() const { const QHostAddress address(m_server->serverAddress()); QString myHost; if (address.isLoopback()) { myHost = address.toString(); } else { //scan Interfaces for available IPs, use requested address if we can find it. myHost = bestAvailableIP(address); } // if localhost is all we got, use that rather than nothing if (myHost.isEmpty()) { switch (m_server->serverAddress().protocol()) { case QAbstractSocket::IPv4Protocol: case QAbstractSocket::AnyIPProtocol: myHost = QHostAddress(QHostAddress::LocalHost).toString(); break; case QAbstractSocket::IPv6Protocol: myHost = QHostAddress(QHostAddress::LocalHostIPv6).toString(); break; case QAbstractSocket::UnknownNetworkLayerProtocol: Q_ASSERT_X(false, "TcpServerDevice::externalAddress", "unknown TCP protocol"); break; } } QUrl url; url.setScheme(QStringLiteral("tcp")); url.setHost(myHost); url.setPort(m_server->serverPort()); return url; } void TcpServerDevice::broadcast(const QByteArray &data) { const QHostAddress address = m_server->serverAddress(); // broadcast announcement only if we are actually listinging to remote connections if (address.isLoopback()) return; m_broadcastSocket->writeDatagram(data.data(), data.size(), QHostAddress::Broadcast, Server::broadcastPort()); } gammaray-2.11.3/core/remote/tcpserverdevice.h000066400000000000000000000034711413310443000211630ustar00rootroot00000000000000/* tcpserverdevice.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_TCPSERVERDEVICE_H #define GAMMARAY_TCPSERVERDEVICE_H #include "serverdevice.h" #include #include QT_BEGIN_NAMESPACE class QUdpSocket; QT_END_NAMESPACE namespace GammaRay { class TcpServerDevice : public ServerDeviceImpl { Q_OBJECT public: explicit TcpServerDevice(QObject *parent = nullptr); ~TcpServerDevice() override; bool listen() override; bool isListening() const override; QUrl externalAddress() const override; void broadcast(const QByteArray &data) override; private: QUdpSocket *m_broadcastSocket; QString bestAvailableIP(const QHostAddress &address) const; }; } #endif // GAMMARAY_TCPSERVERDEVICE_H gammaray-2.11.3/core/remoteviewserver.cpp000066400000000000000000000160721413310443000204440ustar00rootroot00000000000000/* remoteviewserver.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "remoteviewserver.h" #include #include #include #include #include #include #include using namespace GammaRay; RemoteViewServer::RemoteViewServer(const QString &name, QObject *parent) : RemoteViewInterface(name, parent) , m_eventReceiver(nullptr) , m_updateTimer(new QTimer(this)) , m_clientActive(false) , m_sourceChanged(false) , m_clientReady(true) , m_grabberReady(true) , m_pendingReset(false) , m_pendingCompleteFrame(false) { Server::instance()->registerMonitorNotifier(Endpoint::instance()->objectAddress( name), this, "clientConnectedChanged"); m_updateTimer->setSingleShot(true); m_updateTimer->setInterval(10); connect(m_updateTimer, &QTimer::timeout, this, &RemoteViewServer::requestUpdateTimeout); } void RemoteViewServer::setEventReceiver(EventReceiver *receiver) { m_eventReceiver = receiver; } void RemoteViewServer::requestElementsAt(const QPoint &pos, GammaRay::RemoteViewInterface::RequestMode mode) { emit elementsAtRequested(pos, mode); } void RemoteViewServer::pickElementId(const GammaRay::ObjectId &id) { emit doPickElementId(id); } void RemoteViewServer::resetView() { if (isActive()) emit reset(); else m_pendingReset = true; } bool RemoteViewServer::isActive() const { return m_clientActive; } void RemoteViewServer::setGrabberReady(bool ready) { if (ready == m_grabberReady) return; m_grabberReady = ready; checkRequestUpdate(); } void RemoteViewServer::sendFrame(const RemoteViewFrame &frame) { m_clientReady = false; const QSize frameImageSize = frame.image().size() / frame.image().devicePixelRatio(); m_lastTransmittedViewRect = frame.viewRect(); m_lastTransmittedImageRect = frame.transform().mapRect(QRect(QPoint(), frameImageSize)); if (m_pendingCompleteFrame && frameImageSize == frame.viewRect().size()) m_pendingCompleteFrame = false; emit frameUpdated(frame); } QRectF RemoteViewServer::userViewport() const { return m_pendingCompleteFrame ? QRectF() : m_userViewport; } void RemoteViewServer::sourceChanged() { m_sourceChanged = true; checkRequestUpdate(); } void RemoteViewServer::requestCompleteFrame() { if (m_pendingCompleteFrame) return; m_pendingCompleteFrame = true; sourceChanged(); } void RemoteViewServer::clientViewUpdated() { m_clientReady = true; m_sourceChanged = m_sourceChanged || m_pendingCompleteFrame; checkRequestUpdate(); } void RemoteViewServer::checkRequestUpdate() { if (isActive() && !m_updateTimer->isActive() && m_clientReady && m_grabberReady && m_sourceChanged) m_updateTimer->start(); } void RemoteViewServer::sendKeyEvent(int type, int key, int modifiers, const QString &text, bool autorep, ushort count) { if (!m_eventReceiver) return; auto event = new QKeyEvent((QEvent::Type)type, key, (Qt::KeyboardModifiers)modifiers, text, autorep, count); QCoreApplication::postEvent(m_eventReceiver, event); } void RemoteViewServer::sendMouseEvent(int type, const QPoint &localPos, int button, int buttons, int modifiers) { if (!m_eventReceiver) return; auto event = new QMouseEvent((QEvent::Type)type, localPos, (Qt::MouseButton)button, (Qt::MouseButtons)buttons, (Qt::KeyboardModifiers)modifiers); QCoreApplication::postEvent(m_eventReceiver, event); } void RemoteViewServer::sendWheelEvent(const QPoint &localPos, QPoint pixelDelta, QPoint angleDelta, int buttons, int modifiers) { if (!m_eventReceiver) return; auto event = new QWheelEvent(localPos, m_eventReceiver->mapToGlobal( localPos), pixelDelta, angleDelta, 0, /*not used*/ Qt::Vertical, /*not used*/ (Qt::MouseButtons)buttons, (Qt::KeyboardModifiers)modifiers); QCoreApplication::postEvent(m_eventReceiver, event); } void RemoteViewServer::sendTouchEvent(int type, int touchDeviceType, int deviceCaps, int touchDeviceMaxTouchPoints, int modifiers, Qt::TouchPointStates touchPointStates, const QList &touchPoints) { if (!m_eventReceiver) return; if (!m_touchDevice) { //create our own touch device, the system may not have one already, or it may not have //the properties we want m_touchDevice.reset(new QTouchDevice); } m_touchDevice->setType(QTouchDevice::DeviceType(touchDeviceType)); m_touchDevice->setCapabilities(QTouchDevice::CapabilityFlag(deviceCaps)); m_touchDevice->setMaximumTouchPoints(touchDeviceMaxTouchPoints); auto event = new QTouchEvent(QEvent::Type(type), m_touchDevice.get(), Qt::KeyboardModifiers(modifiers), touchPointStates, touchPoints); event->setWindow(m_eventReceiver); QCoreApplication::sendEvent(m_eventReceiver, event); } void RemoteViewServer::setViewActive(bool active) { if (m_pendingReset) { emit reset(); m_pendingReset = false; } m_clientActive = active; m_clientReady = active; m_pendingCompleteFrame = false; if (active) sourceChanged(); else m_updateTimer->stop(); } void RemoteViewServer::sendUserViewport(const QRectF &userViewport) { m_userViewport = userViewport; auto newlyRequestedRect = userViewport.intersected(m_lastTransmittedViewRect); if (!m_lastTransmittedImageRect.contains(newlyRequestedRect)) sourceChanged(); } void RemoteViewServer::clientConnectedChanged(bool connected) { if (!connected) setViewActive(false); } void RemoteViewServer::requestUpdateTimeout() { m_sourceChanged = false; emit requestUpdate(); } gammaray-2.11.3/core/remoteviewserver.h000066400000000000000000000102201413310443000200760ustar00rootroot00000000000000/* remoteviewserver.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_REMOTEVIEWSERVER_H #define GAMMARAY_REMOTEVIEWSERVER_H #include #include "gammaray_core_export.h" #include #include QT_BEGIN_NAMESPACE class QTimer; class QWindow; class QTouchDevice; QT_END_NAMESPACE namespace GammaRay { /** Server part of the remote view widget. */ class GAMMARAY_CORE_EXPORT RemoteViewServer : public RemoteViewInterface { Q_OBJECT Q_INTERFACES(GammaRay::RemoteViewInterface) public: explicit RemoteViewServer(const QString &name, QObject *parent = nullptr); using EventReceiver = QWindow; /// event receiver for input redirection void setEventReceiver(EventReceiver *receiver); /// resets the client view if the window selection changed void resetView(); /// returns @c true if there is a client displaying our content bool isActive() const; /// set the grabber ready state void setGrabberReady(bool ready); /// sends a new frame to the client void sendFrame(const RemoteViewFrame &frame); QRectF userViewport() const; public slots: /// call this to indicate the source has changed and the client requires an update void sourceChanged(); void requestCompleteFrame() override; signals: void elementsAtRequested(const QPoint &pos, GammaRay::RemoteViewInterface::RequestMode mode); void doPickElementId(const GammaRay::ObjectId &id); /// when receiving this signal, obtain a new frame and send it to the client void requestUpdate(); private: void requestElementsAt(const QPoint &pos, GammaRay::RemoteViewInterface::RequestMode mode) override; void pickElementId(const GammaRay::ObjectId &id) override; void sendKeyEvent(int type, int key, int modifiers, const QString &text, bool autorep, ushort count) override; void sendMouseEvent(int type, const QPoint &localPos, int button, int buttons, int modifiers) override; void sendWheelEvent(const QPoint &localPos, QPoint pixelDelta, QPoint angleDelta, int buttons, int modifiers) override; void sendTouchEvent(int type, int touchDeviceType, int deviceCaps, int touchDeviceMaxTouchPoints, int modifiers, Qt::TouchPointStates touchPointStates, const QList &touchPoints) override; void setViewActive(bool active) override; void sendUserViewport(const QRectF &userViewport) override; void clientViewUpdated() override; void checkRequestUpdate(); private slots: void clientConnectedChanged(bool connected); void requestUpdateTimeout(); private: QPointer m_eventReceiver; QTimer *m_updateTimer; QRectF m_lastTransmittedViewRect; QRectF m_lastTransmittedImageRect; QRectF m_userViewport; bool m_clientActive; bool m_sourceChanged; bool m_clientReady; bool m_grabberReady; bool m_pendingReset; bool m_pendingCompleteFrame; std::unique_ptr m_touchDevice; }; } #endif // GAMMARAY_REMOTEVIEWSERVER_H gammaray-2.11.3/core/sequentialpropertyadaptor.cpp000066400000000000000000000042221413310443000223530ustar00rootroot00000000000000/* sequentialpropertyadaptor.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "sequentialpropertyadaptor.h" #include "objectinstance.h" #include "propertydata.h" #include using namespace GammaRay; SequentialPropertyAdaptor::SequentialPropertyAdaptor(QObject *parent) : PropertyAdaptor(parent) { } SequentialPropertyAdaptor::~SequentialPropertyAdaptor() = default; void SequentialPropertyAdaptor::doSetObject(const ObjectInstance &oi) { if (oi.type() == ObjectInstance::QtVariant) m_value = oi.variant(); } int SequentialPropertyAdaptor::count() const { if (!m_value.canConvert()) return 0; auto it = m_value.value(); return it.size(); } PropertyData SequentialPropertyAdaptor::propertyData(int index) const { Q_ASSERT(m_value.canConvert()); auto seq = m_value.value(); auto it = seq.begin(); it += index; PropertyData data; data.setName(QString::number(index)); data.setValue(*it); data.setClassName(m_value.typeName()); return data; } gammaray-2.11.3/core/sequentialpropertyadaptor.h000066400000000000000000000034261413310443000220250ustar00rootroot00000000000000/* sequentialpropertyadaptor.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_SEQUENTIALPROPERTYADAPTOR_H #define GAMMARAY_SEQUENTIALPROPERTYADAPTOR_H #include "propertyadaptor.h" #include namespace GammaRay { /** Adaptor for recursing into QSequentialIterable properties. */ class SequentialPropertyAdaptor : public PropertyAdaptor { Q_OBJECT public: explicit SequentialPropertyAdaptor(QObject *parent = nullptr); ~SequentialPropertyAdaptor() override; int count() const override; PropertyData propertyData(int index) const override; protected: void doSetObject(const ObjectInstance &oi) override; private: QVariant m_value; }; } #endif // GAMMARAY_SEQUENTIALPROPERTYADAPTOR_H gammaray-2.11.3/core/signalspycallbackset.cpp000066400000000000000000000025741413310443000212330ustar00rootroot00000000000000/* signalspycallbackset.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "signalspycallbackset.h" using namespace GammaRay; bool SignalSpyCallbackSet::isNull() const { return signalBeginCallback == nullptr && signalEndCallback == nullptr && slotBeginCallback == nullptr && slotEndCallback == nullptr; } gammaray-2.11.3/core/signalspycallbackset.h000066400000000000000000000035221413310443000206720ustar00rootroot00000000000000/* signalspycallbackset.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_SIGNALSPYCALLBACKSET_H #define GAMMARAY_SIGNALSPYCALLBACKSET_H #include "gammaray_core_export.h" #include QT_BEGIN_NAMESPACE class QObject; QT_END_NAMESPACE namespace GammaRay { /** @brief Callbacks for tracing signal emissions and slot invocation. * * @since 2.3 */ struct GAMMARAY_CORE_EXPORT SignalSpyCallbackSet { SignalSpyCallbackSet() = default; bool isNull() const; using BeginCallback = void (*)(QObject *, int, void **); using EndCallback = void (*)(QObject *, int); BeginCallback signalBeginCallback = nullptr; EndCallback signalEndCallback = nullptr; BeginCallback slotBeginCallback = nullptr; EndCallback slotEndCallback = nullptr; }; } #endif gammaray-2.11.3/core/singlecolumnobjectproxymodel.cpp000066400000000000000000000036261413310443000230410ustar00rootroot00000000000000/* singlecolumnobjectproxymodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "singlecolumnobjectproxymodel.h" #include #include using namespace GammaRay; SingleColumnObjectProxyModel::SingleColumnObjectProxyModel(QObject *parent) : QIdentityProxyModel(parent) { } int SingleColumnObjectProxyModel::columnCount(const QModelIndex &parent) const { return std::min(QIdentityProxyModel::columnCount(parent), 1); } QVariant SingleColumnObjectProxyModel::data(const QModelIndex &proxyIndex, int role) const { if (proxyIndex.isValid() && role == Qt::DisplayRole && proxyIndex.column() == 0) { const QObject *obj = proxyIndex.data(ObjectModel::ObjectRole).value(); if (obj) return Util::displayString(obj); } return QIdentityProxyModel::data(proxyIndex, role); } gammaray-2.11.3/core/singlecolumnobjectproxymodel.h000066400000000000000000000045261413310443000225060ustar00rootroot00000000000000/* singlecolumnobjectproxymodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ /** @file This file is part of the GammaRay Plugin API and declares the SingleColumnObjectProxyModel class. @brief Declares the SingleColumnObjectProxyModel class. @author Volker Krause \ */ #ifndef GAMMARAY_SINGLECOLUMNOBJECTPROXYMODEL_H #define GAMMARAY_SINGLECOLUMNOBJECTPROXYMODEL_H #include "gammaray_core_export.h" #include namespace GammaRay { /** * @brief A QIdentityProxyModel for generic Objects. */ class GAMMARAY_CORE_EXPORT SingleColumnObjectProxyModel : public QIdentityProxyModel { Q_OBJECT public: /** * Constructor. * @param parent is the parent object for this instance. */ explicit SingleColumnObjectProxyModel(QObject *parent = nullptr); int columnCount(const QModelIndex &parent = QModelIndex()) const override; /** * Returns the data for the specified model. * @param proxyIndex is a QModelIndex. * @param role is a Qt role. * * @return on success, a QVariant containing the data; * QVariant() if some anamoly occurs. */ QVariant data(const QModelIndex &proxyIndex, int role = Qt::DisplayRole) const override; }; } #endif // GAMMARAY_SINGLECOLUMNOBJECTPROXYMODEL_H gammaray-2.11.3/core/stacktracemodel.cpp000066400000000000000000000055041413310443000201720ustar00rootroot00000000000000/* stacktracemodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "stacktracemodel.h" #include using namespace GammaRay; StackTraceModel::StackTraceModel(QObject* parent) : QAbstractTableModel(parent) { } StackTraceModel::~StackTraceModel() = default; void StackTraceModel::setStackTrace(const Execution::Trace& trace) { if (!m_trace.empty()) { beginRemoveRows(QModelIndex(), 0, m_trace.size() - 1); m_frames.clear(); m_trace = Execution::Trace(); endRemoveRows(); } if (!trace.empty()) { beginInsertRows(QModelIndex(), 0, trace.size() - 1); m_trace = trace; m_frames.clear(); endInsertRows(); } } int StackTraceModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); return 2; } int StackTraceModel::rowCount(const QModelIndex &parent) const { if (parent.isValid()) return 0; return m_trace.size(); } QVariant StackTraceModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); if (m_trace.size() && !m_frames.size()) { m_frames = Execution::resolveAll(m_trace); } if (role == Qt::DisplayRole) { switch (index.column()) { case 0: return m_frames.at(index.row()).name; case 1: return QVariant::fromValue(m_frames.at(index.row()).location); } } return QVariant(); } QVariant StackTraceModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { switch (section) { case 0: return tr("Function"); case 1: return tr("Location"); } } return QAbstractTableModel::headerData(section, orientation, role); } gammaray-2.11.3/core/stacktracemodel.h000066400000000000000000000041031413310443000176310ustar00rootroot00000000000000/* stacktracemodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_STACKTRACEMODEL_H #define GAMMARAY_STACKTRACEMODEL_H #include "gammaray_core_export.h" #include "execution.h" #include #include #include namespace GammaRay { /*! A table model for displaying a single stack trace. */ class GAMMARAY_CORE_EXPORT StackTraceModel : public QAbstractTableModel { Q_OBJECT public: explicit StackTraceModel(QObject *parent = nullptr); ~StackTraceModel() override; void setStackTrace(const Execution::Trace &trace); int columnCount(const QModelIndex &parent) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role) const override; QVariant headerData(int section, Qt::Orientation orientation, int role) const override; private: mutable QVector m_frames; Execution::Trace m_trace; }; } #endif // GAMMARAY_STACKTRACEMODEL_H gammaray-2.11.3/core/staticprobe.h000066400000000000000000000051741413310443000170140ustar00rootroot00000000000000/* staticprobe.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_STATIC_PROBE_H #define GAMMARAY_STATIC_PROBE_H #include /* Determine which plugins to include. * This isn't entirely correct, as it ignores dlopened DSOs for example. * However the main motivation for static injection is iOS where we have * to statically link everything. If you use dlopen, dynamic injection is * the better option anyway. */ #ifdef QT_CORE_LIB Q_IMPORT_PLUGIN(CodecBrowserFactory) Q_IMPORT_PLUGIN(EventMonitorFactory) Q_IMPORT_PLUGIN(SignalMonitorFactory) Q_IMPORT_PLUGIN(StateMachineViewerFactory) Q_IMPORT_PLUGIN(TimerTopFactory) Q_IMPORT_PLUGIN(TranslatorInspectorFactory) #endif #ifdef QT_GUI_LIB Q_IMPORT_PLUGIN(FontBrowserFactory) Q_IMPORT_PLUGIN(GuiSupportFactory) #endif #ifdef QT_WIDGETS_LIB Q_IMPORT_PLUGIN(ActionInspectorFactory) Q_IMPORT_PLUGIN(SceneInspectorFactory) Q_IMPORT_PLUGIN(StyleInspectorFactory) Q_IMPORT_PLUGIN(WidgetInspectorFactory) #endif #ifdef QT_QML_LIB Q_IMPORT_PLUGIN(QmlSupportFactory) #endif #ifdef QT_QUICK_LIB Q_IMPORT_PLUGIN(QuickInspectorFactory) #endif #ifdef QT_BLUETOOTH_LIB Q_IMPORT_PLUGIN(BluetoothFactory) #endif #ifdef QT_WEBKIT_LIB Q_IMPORT_PLUGIN(WebInspectorFactory) #endif /** Trigger static injection of the GammaRay probe. * Put this into your main.cpp in global scope. */ #define GAMMARAY_STATIC_INJECT \ extern "C" { \ extern void gammaray_install_hooks(); \ } \ \ namespace GammaRay { \ class StaticInjector { \ public: \ StaticInjector() { gammaray_install_hooks(); } \ }; \ static StaticInjector staticInjector; \ } #endif gammaray-2.11.3/core/toolfactory.cpp000066400000000000000000000035161413310443000173730ustar00rootroot00000000000000/* toolfactory.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "toolfactory.h" using namespace GammaRay; ToolFactory::ToolFactory() = default; ToolFactory::~ToolFactory() = default; const QVector &ToolFactory::supportedTypes() const { return m_types; } void ToolFactory::setSupportedTypes(const QVector &types) { m_types = types; } QString ToolFactory::supportedTypesString() const { QStringList typesString; for (auto it = m_types.constBegin(), end = m_types.constEnd(); it != end; ++it) typesString << QLatin1String(*it); return typesString.join(QStringLiteral(", ")); } QVector ToolFactory::selectableTypes() const { return QVector(); } bool ToolFactory::isHidden() const { return false; } gammaray-2.11.3/core/toolfactory.h000066400000000000000000000104341413310443000170350ustar00rootroot00000000000000/* toolfactory.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ /** @file This file is part of the GammaRay Plugin API and declares the ToolFactory abstract base class. @brief Declares the ToolFactory abstract base class. @author Volker Krause \ */ #ifndef GAMMARAY_TOOLFACTORY_H #define GAMMARAY_TOOLFACTORY_H #include "gammaray_core_export.h" #include "probe.h" #include #include #include #include namespace GammaRay { /*! * An abstract interface for probe tools. * * The ToolFactory class is an abstract base class for creating probe tools * for GammaRay. Each tool must have a unique identifier. */ class GAMMARAY_CORE_EXPORT ToolFactory { public: ToolFactory(); virtual ~ToolFactory(); /*! * Unique id of this tool * @return a QString containing the tool id. */ virtual QString id() const = 0; /*! * Class names of types this tool can handle. * The tool will only be activated if an object of one of these types * is seen in the probed application. * @return a QVector of class names of types this tool supports. */ const QVector &supportedTypes() const; /*! * Set names of supported classes. * @see supportedTypes() * @since 2.5 */ void setSupportedTypes(const QVector &types); /*! * Class names of types this tool can handle as a string. * @return a comma separated QString of class names of types this tool supports. */ QString supportedTypesString() const; /*! * Initialize the tool. * Implement this method to do non-GUI initialization, such as creating * object tracking models etc. * @param probe The probe interface allowing access to the object models. */ virtual void init(Probe *probe) = 0; /*! * Allows to hide a plug-in from the UI. * This is useful for plug-ins that only provide support for additional * data types. The value is usually filled in by the plug-in loader * @return @c true if the plug-in has no tool view. * @since 2.1 */ virtual bool isHidden() const; /*! * Class names of types this tool can select. * This must be a subset of supportedTypes(), and is used to check if this * tool is a viable candidate for object navigation. * When returning an non-empty result here, you must handle the Probe::objectSelected() * signal. */ virtual QVector selectableTypes() const; private: Q_DISABLE_COPY(ToolFactory) QVector m_types; }; /** * @brief A templated generic ToolFactory for some data type and tool. */ template class StandardToolFactory : public ToolFactory { public: StandardToolFactory() { setSupportedTypes(QVector() << Type::staticMetaObject.className()); } QString id() const override { return Tool::staticMetaObject.className(); } void init(Probe *probe) override { new Tool(probe, probe); } }; } QT_BEGIN_NAMESPACE Q_DECLARE_INTERFACE(GammaRay::ToolFactory, "com.kdab.GammaRay.ToolFactory/1.0") QT_END_NAMESPACE Q_DECLARE_METATYPE(GammaRay::ToolFactory *) #endif gammaray-2.11.3/core/toolmanager.cpp000066400000000000000000000156151413310443000173410ustar00rootroot00000000000000/* toolmanager.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "toolmanager.h" #include "metaobject.h" #include "metaobjectrepository.h" #include "probe.h" #include "proxytoolfactory.h" #include "toolfactory.h" #include "tools/metatypebrowser/metatypebrowser.h" #include "tools/objectinspector/objectinspector.h" #include "tools/problemreporter/problemreporter.h" #include "tools/resourcebrowser/resourcebrowser.h" #include "tools/messagehandler/messagehandler.h" #include "tools/metaobjectbrowser/metaobjectbrowser.h" #include #include #include #include #include #include using namespace GammaRay; ToolManager::ToolManager(QObject *parent) : ToolManagerInterface(parent) , m_toolPluginManager(new ToolPluginManager(this)) { // built-in tools addToolFactory(new ObjectInspectorFactory(this)); addToolFactory(new ResourceBrowserFactory(this)); addToolFactory(new MetaObjectBrowserFactory(this)); addToolFactory(new MetaTypeBrowserFactory(this)); addToolFactory(new MessageHandlerFactory(this)); addToolFactory(new ProblemReporterFactory(this)); Q_FOREACH (ToolFactory *factory, m_toolPluginManager->plugins()) addToolFactory(factory); } void ToolManager::selectObject(const ObjectId &id, const QString &toolId) { switch (id.type()) { case ObjectId::Invalid: return; case ObjectId::QObjectType: { QMutexLocker lock(Probe::objectLock()); if (!Probe::instance()->isValidObject(id.asQObject())) return; Probe::instance()->selectObject(id.asQObject(), toolId); break; } case ObjectId::VoidStarType: Probe::instance()->selectObject(id.asVoidStar(), id.typeName()); break; } } void ToolManager::selectTool(const QString &toolId) { emit toolSelected(toolId); } void ToolManager::requestToolsForObject(const ObjectId &id) { QVector toolInfos; switch (id.type()) { case ObjectId::Invalid: return; case ObjectId::QObjectType: { QMutexLocker lock(Probe::objectLock()); if (!Probe::instance()->isValidObject(id.asQObject())) return; toolInfos = toolsForObject(id.asQObject()); break; } case ObjectId::VoidStarType: const auto asVoidStar = reinterpret_cast(id.id()); toolInfos = toolsForObject(asVoidStar, id.typeName()); break; } emit toolsForObjectResponse(id, toolInfos); } void ToolManager::requestAvailableTools() { QVector toolInfos; toolInfos.reserve(m_tools.size()); for (ToolFactory *factory : qAsConst(m_tools)) toolInfos.push_back(toolInfoForFactory(factory)); emit availableToolsResponse(toolInfos); } ToolData ToolManager::toolInfoForFactory(ToolFactory *factory) const { ToolData info; info.id = factory->id(); info.hasUi = !factory->isHidden(); info.enabled = !m_disabledTools.contains(factory); return info; } bool ToolManager::hasTool(const QString &id) const { for (ToolFactory *factory : m_tools) { if (factory->id() == id) return true; } return false; } QVector ToolManager::toolsForObject(QObject *object) const { if (!object) return QVector(); QVector ret; QSet knownTools; const QMetaObject *metaObject = object->metaObject(); while (metaObject) { for (auto factory : m_tools) { if (factory && !factory->isHidden() && factory->selectableTypes().contains(metaObject->className()) && !knownTools.contains(factory)) { knownTools << factory; ret.append(factory->id()); } } metaObject = metaObject->superClass(); } return ret; } QVector ToolManager::toolsForObject(const void *object, const QString &typeName) const { if (!object) return QVector(); QVector ret; const MetaObject *metaObject = MetaObjectRepository::instance()->metaObject(typeName); while (metaObject) { for (auto factory : m_tools) { if (factory && !factory->isHidden() && factory->selectableTypes().contains(metaObject->className().toUtf8())) ret.append(factory->id()); } metaObject = metaObject->superClass(); } return ret; } void ToolManager::objectAdded(QObject *obj) { // note: hot path, don't do expensive operations here Q_ASSERT(QThread::currentThread() == thread()); Q_ASSERT(Probe::instance()->isValidObject(obj)); // m_knownMetaObjects allows us to skip the expensive recursive search for matching tools if (!m_knownMetaObjects.contains(obj->metaObject())) { objectAdded(obj->metaObject()); m_knownMetaObjects.insert(obj->metaObject()); } } void ToolManager::objectAdded(const QMetaObject *mo) { // note: hot path, don't do expensive operations here Q_ASSERT(thread() == QThread::currentThread()); // as plugins can depend on each other, start from the base classes if (mo->superClass()) objectAdded(mo->superClass()); // operate on copy to ensure potential recursion isn't invalidating the iterators const auto disabledToolsCopy = m_disabledTools; for (auto *factory : disabledToolsCopy) { const auto begin = factory->supportedTypes().constBegin(); const auto end = factory->supportedTypes().constEnd(); if (std::find(begin, end, mo->className()) != end && m_disabledTools.remove(factory)) { factory->init(Probe::instance()); emit toolEnabled(factory->id()); } } } void ToolManager::addToolFactory(ToolFactory *tool) { m_tools.push_back(tool); m_disabledTools.insert(tool); } ToolPluginManager *ToolManager::toolPluginManager() const { return m_toolPluginManager.data(); } gammaray-2.11.3/core/toolmanager.h000066400000000000000000000060621413310443000170020ustar00rootroot00000000000000/* toolmanager.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_TOOLMANAGER_H #define GAMMARAY_TOOLMANAGER_H #include "proxytoolfactory.h" #include #include namespace GammaRay { class ToolFactory; class ProxyToolFactory; typedef PluginManager ToolPluginManager; /** @brief Server-sided tool manager. Provides information to the client * about which tools exists and especially which tools to enable. Also * calls the ToolPluginManager to load tool plugins. */ class ToolManager : public ToolManagerInterface { Q_OBJECT Q_INTERFACES(GammaRay::ToolManagerInterface) public: explicit ToolManager(QObject *parent = nullptr); /** returns the tools that are best suited to show information about \p object. */ QVector toolsForObject(QObject *object) const; /** returns the tools that are best suited to show information about \p object. */ QVector toolsForObject(const void *object, const QString &typeName) const; bool hasTool(const QString &id) const; ToolPluginManager *toolPluginManager() const; /** Check if we have to activate tools for this type */ void objectAdded(QObject *obj); void selectTool(const QString &toolId); private: /** * Check if we have to activate tools for this type * * NOTE: must be called from the GUI thread */ void objectAdded(const QMetaObject *mo); public slots: void selectObject(const GammaRay::ObjectId &id, const QString &toolId) override; void requestToolsForObject(const GammaRay::ObjectId &id) override; void requestAvailableTools() override; private: void addToolFactory(ToolFactory *tool); ToolData toolInfoForFactory(ToolFactory *factory) const; QVector m_tools; QSet m_disabledTools; QSet m_knownMetaObjects; QScopedPointer m_toolPluginManager; }; } #endif // GAMMARAY_TOOLMANAGER_H gammaray-2.11.3/core/toolpluginerrormodel.cpp000066400000000000000000000050111413310443000213050ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2012-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Kevin Funk Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "toolpluginerrormodel.h" using namespace GammaRay; ToolPluginErrorModel::ToolPluginErrorModel(const PluginLoadErrors &errors, QObject *parent) : QAbstractTableModel(parent) , m_errors(errors) { } ToolPluginErrorModel::~ToolPluginErrorModel() = default; int ToolPluginErrorModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); return 3; } int ToolPluginErrorModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent); return m_errors.size(); } QVariant ToolPluginErrorModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); const int row = index.row(); const int column = index.column(); if (role == Qt::DisplayRole) { switch (column) { case 0: return m_errors[row].pluginName(); case 1: return m_errors[row].pluginFile; case 2: return m_errors[row].errorString; } } return QVariant(); } QVariant ToolPluginErrorModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role == Qt::DisplayRole && orientation == Qt::Horizontal) { switch (section) { case 0: return tr("Plugin Name"); case 1: return tr("Plugin File"); case 2: return tr("Error Message"); } } return QAbstractTableModel::headerData(section, orientation, role); } gammaray-2.11.3/core/toolpluginerrormodel.h000066400000000000000000000036531413310443000207640ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2012-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Kevin Funk Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_TOOLPLUGINERRORMODEL_H #define GAMMARAY_TOOLPLUGINERRORMODEL_H #include #include namespace GammaRay { /** List of plugin loading errors on the target. */ class ToolPluginErrorModel : public QAbstractTableModel { Q_OBJECT public: explicit ToolPluginErrorModel(const PluginLoadErrors &errors, QObject *parent = nullptr); ~ToolPluginErrorModel() override; QVariant data(const QModelIndex &index, int role) const override; int columnCount(const QModelIndex &parent) const override; int rowCount(const QModelIndex &parent) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; private: PluginLoadErrors m_errors; }; } #endif // GAMMARAY_TOOLPLUGINERRORMODEL_H gammaray-2.11.3/core/toolpluginmodel.cpp000066400000000000000000000046471413310443000202510ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2012-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Kevin Funk Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "toolpluginmodel.h" #include "toolfactory.h" using namespace GammaRay; ToolPluginModel::ToolPluginModel(const QVector &plugins, QObject *parent) : QAbstractTableModel(parent) , m_tools(plugins) { } ToolPluginModel::~ToolPluginModel() = default; int ToolPluginModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); return 2; } int ToolPluginModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent); return m_tools.size(); } QVariant ToolPluginModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); const int row = index.row(); const int column = index.column(); if (role == Qt::DisplayRole) { ToolFactory *factory = m_tools[row]; switch (column) { case 0: return factory->id(); case 1: return factory->supportedTypesString(); } } return QVariant(); } QVariant ToolPluginModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role == Qt::DisplayRole && orientation == Qt::Horizontal) { switch (section) { case 0: return tr("Id"); case 1: return tr("Supported types"); } } return QAbstractTableModel::headerData(section, orientation, role); } gammaray-2.11.3/core/toolpluginmodel.h000066400000000000000000000036631413310443000177130ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2012-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Kevin Funk Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_TOOLPLUGINMODEL_H #define GAMMARAY_TOOLPLUGINMODEL_H #include #include namespace GammaRay { class ToolFactory; /** Information about loaded plugins, for display in the about dialog. */ class ToolPluginModel : public QAbstractTableModel { Q_OBJECT public: explicit ToolPluginModel(const QVector &plugins, QObject *parent = nullptr); ~ToolPluginModel() override; QVariant data(const QModelIndex &index, int role) const override; int columnCount(const QModelIndex &parent) const override; int rowCount(const QModelIndex &parent) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; private: QVector m_tools; }; } #endif // GAMMARAY_TOOLPLUGINMODEL_H gammaray-2.11.3/core/tools/000077500000000000000000000000001413310443000154555ustar00rootroot00000000000000gammaray-2.11.3/core/tools/messagehandler/000077500000000000000000000000001413310443000204375ustar00rootroot00000000000000gammaray-2.11.3/core/tools/messagehandler/loggingcategorymodel.cpp000066400000000000000000000111211413310443000253440ustar00rootroot00000000000000/* loggingcategorymodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "loggingcategorymodel.h" using namespace GammaRay; namespace GammaRay { // FIXME: can be called from different threads! void categoryFilter(QLoggingCategory *category) { Q_ASSERT(LoggingCategoryModel::m_instance); LoggingCategoryModel::m_instance->addCategory(category); if (LoggingCategoryModel::m_instance->m_previousFilter) LoggingCategoryModel::m_instance->m_previousFilter(category); } } LoggingCategoryModel *LoggingCategoryModel::m_instance = nullptr; LoggingCategoryModel::LoggingCategoryModel(QObject *parent) : QAbstractTableModel(parent) , m_previousFilter(nullptr) { Q_ASSERT(m_instance == nullptr); m_instance = this; m_previousFilter = QLoggingCategory::installFilter(categoryFilter); } LoggingCategoryModel::~LoggingCategoryModel() { m_instance = nullptr; QLoggingCategory::installFilter(m_previousFilter); } void LoggingCategoryModel::addCategory(QLoggingCategory *category) { beginInsertRows(QModelIndex(), m_categories.size(), m_categories.size()); m_categories.push_back(category); endInsertRows(); } int LoggingCategoryModel::rowCount(const QModelIndex &parent) const { if (parent.isValid()) return 0; return m_categories.size(); } int LoggingCategoryModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); return 5; } QVariant LoggingCategoryModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); if (role == Qt::DisplayRole && index.column() == 0) return QString::fromUtf8(m_categories.at(index.row())->categoryName()); if (role == Qt::CheckStateRole) { auto cat = m_categories.at(index.row()); switch (index.column()) { case 1: return cat->isDebugEnabled() ? Qt::Checked : Qt::Unchecked; case 2: return cat->isInfoEnabled() ? Qt::Checked : Qt::Unchecked; case 3: return cat->isWarningEnabled() ? Qt::Checked : Qt::Unchecked; case 4: return cat->isCriticalEnabled() ? Qt::Checked : Qt::Unchecked; } } return QVariant(); } Qt::ItemFlags LoggingCategoryModel::flags(const QModelIndex &index) const { const auto baseFlags = QAbstractTableModel::flags(index); if (index.column() == 2) // info not available in Qt < 5.5 return baseFlags; if (index.column() > 0) return baseFlags | Qt::ItemIsUserCheckable; return baseFlags; } bool LoggingCategoryModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (!index.isValid() || index.column() == 0 || role != Qt::CheckStateRole) return false; static const QtMsgType type_map[] = { QtDebugMsg, QtDebugMsg, QtInfoMsg, QtWarningMsg, QtCriticalMsg }; const auto enabled = value.toInt() == Qt::Checked; auto cat = m_categories.at(index.row()); cat->setEnabled(type_map[index.column()], enabled); emit dataChanged(index, index); return true; } QVariant LoggingCategoryModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { switch (section) { case 0: return tr("Category"); case 1: return tr("Debug"); case 2: return tr("Info"); case 3: return tr("Warning"); case 4: return tr("Critical"); } } return QAbstractTableModel::headerData(section, orientation, role); } gammaray-2.11.3/core/tools/messagehandler/loggingcategorymodel.h000066400000000000000000000045311413310443000250200ustar00rootroot00000000000000/* loggingcategorymodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_LOGGINGCATEGORYMODEL_H #define GAMMARAY_LOGGINGCATEGORYMODEL_H #include #include #include namespace GammaRay { void categoryFilter(QLoggingCategory *category); class LoggingCategoryModel : public QAbstractTableModel { Q_OBJECT public: explicit LoggingCategoryModel(QObject *parent = nullptr); ~LoggingCategoryModel() override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; Qt::ItemFlags flags(const QModelIndex &index) const override; bool setData(const QModelIndex &index, const QVariant &value, int role) override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; private: void addCategory(QLoggingCategory *category); QVector m_categories; QLoggingCategory::CategoryFilter m_previousFilter; friend void categoryFilter(QLoggingCategory *); static LoggingCategoryModel *m_instance; }; } #endif // GAMMARAY_LOGGINGCATEGORYMODEL_H gammaray-2.11.3/core/tools/messagehandler/messagehandler.cpp000066400000000000000000000176231413310443000241360ustar00rootroot00000000000000/* messagehandler.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "messagehandler.h" #include "messagemodel.h" #include "loggingcategorymodel.h" #include #include #include #include #include "common/objectbroker.h" #include "common/endpoint.h" #include #include #include #include #include #include #include using namespace GammaRay; using MessageHandlerCallback = QtMessageHandler; static MessageHandlerCallback(*const installMessageHandler)(MessageHandlerCallback) = qInstallMessageHandler; static MessageModel *s_model = nullptr; static MessageHandlerCallback s_handler = nullptr; static bool s_handlerDisabled = false; static QMutex s_mutex(QMutex::Recursive); static void handleMessage(QtMsgType type, const QMessageLogContext &context, const QString &msg) { ///WARNING: do not trigger *any* kind of debug output here /// this would trigger an infinite loop and hence crash! if (s_handlerDisabled) // recursion detected return; DebugMessage message; message.type = type; message.message = msg; message.time = QTime::currentTime(); message.category = QString::fromUtf8(context.category); message.file = QString::fromUtf8(context.file); message.function = QString::fromUtf8(context.function); message.line = context.line; if (type == QtCriticalMsg || type == QtFatalMsg || (type == QtWarningMsg && !ProbeGuard::insideProbe())) { // TODO: go even higher until qWarning/qFatal/qDebug/... ? message.backtrace = Execution::stackTrace(50, 1); // skip this, ie. start at our caller } if (!message.backtrace.empty() && (qgetenv("GAMMARAY_UNITTEST") == "1" || type == QtFatalMsg)) { if (type == QtFatalMsg) std::cerr << "QFatal in " << qPrintable(qApp->applicationName()) << " (" << qPrintable( qApp->applicationFilePath()) << ')' << std::endl; std::cerr << "START BACKTRACE:" << std::endl; int i = 0; foreach (const auto &frame, Execution::resolveAll(message.backtrace)) std::cerr << (++i) << "\t" << qPrintable(frame.name) << " (" << qPrintable(frame.location.displayString()) << ")" << std::endl; std::cerr << "END BACKTRACE" << std::endl; } if (type == QtFatalMsg && qgetenv("GAMMARAY_GDB") != "1" && qgetenv("GAMMARAY_UNITTEST") != "1") { // Enforce handling on the GUI thread and block until we are done. QMetaObject::invokeMethod(static_cast(s_model)->parent(), "handleFatalMessage", qApp->thread() == QThread::currentThread() ? Qt::DirectConnection : Qt::BlockingQueuedConnection, Q_ARG(GammaRay::DebugMessage, message)); } // reset msg handler so the app still works as usual // but make sure we don't let other threads bypass our // handler during that time QMutexLocker lock(&s_mutex); s_handlerDisabled = true; if (s_handler) { // try a direct call to the previous handler first, that avoids triggering the recursion detection in Qt5 s_handler(type, context, msg); } else { installMessageHandler(s_handler); qt_message_output(type, context, msg); installMessageHandler(handleMessage); } s_handlerDisabled = false; lock.unlock(); if (s_model) { // add directly from foreground thread, delay from background thread QMetaObject::invokeMethod(s_model, "addMessage", Qt::AutoConnection, Q_ARG(GammaRay::DebugMessage, message)); } } MessageHandler::MessageHandler(Probe *probe, QObject *parent) : MessageHandlerInterface(parent) , m_messageModel(new MessageModel(this)) , m_stackTraceModel(new StackTraceModel(this)) { Q_ASSERT(s_model == nullptr); s_model = m_messageModel; auto proxy = new ServerProxyModel(this); proxy->addRole(MessageModelRole::Type); proxy->addRole(MessageModelRole::Line); proxy->setSourceModel(m_messageModel); proxy->setSortRole(MessageModelRole::Sort); probe->registerModel(QStringLiteral("com.kdab.GammaRay.MessageModel"), proxy); auto selModel = ObjectBroker::selectionModel(proxy); connect(selModel, &QItemSelectionModel::selectionChanged, this, &MessageHandler::messageSelected); probe->registerModel(QStringLiteral("com.kdab.GammaRay.MessageStackTraceModel"), m_stackTraceModel); // install handler directly, catches most cases, // i.e. user has no special handler or the handler // is created before the QApplication ensureHandlerInstalled(); // recheck when eventloop is entered, if the user // installs a handler after QApp but before .exec() QMetaObject::invokeMethod(this, "ensureHandlerInstalled", Qt::QueuedConnection); auto catModel = new LoggingCategoryModel(this); probe->registerModel(QStringLiteral("com.kdab.GammaRay.LoggingCategoryModel"), catModel); } MessageHandler::~MessageHandler() { QMutexLocker lock(&s_mutex); s_model = nullptr; MessageHandlerCallback oldHandler = installMessageHandler(s_handler); if (oldHandler != handleMessage) { // ups, the app installed it's own handler after ours... installMessageHandler(oldHandler); } s_handler = nullptr; } void MessageHandler::ensureHandlerInstalled() { QMutexLocker lock(&s_mutex); if (s_handlerDisabled) return; MessageHandlerCallback prevHandler = installMessageHandler(handleMessage); if (prevHandler != handleMessage) s_handler = prevHandler; } void MessageHandler::handleFatalMessage(const DebugMessage &message) { const QString app = qApp->applicationName().isEmpty() ? qApp->applicationFilePath() : qApp->applicationName(); QStringList bt; bt.reserve(message.backtrace.size()); foreach (const auto &frame, Execution::resolveAll(message.backtrace)) { if (frame.location.isValid()) bt.push_back(frame.name + QLatin1String(" (") + frame.location.displayString() + QLatin1Char(')')); else bt.push_back(frame.name); } emit fatalMessageReceived(app, message.message, message.time, bt); if (Endpoint::isConnected()) Endpoint::instance()->waitForMessagesWritten(); } void MessageHandler::messageSelected(const QItemSelection& selection) { if (selection.isEmpty()) { setStackTraceAvailable(false); return; } const auto idx = selection.at(0).topLeft(); m_stackTraceModel->setStackTrace(idx.data(MessageModelRole::Backtrace).value()); setStackTraceAvailable(m_stackTraceModel->rowCount() > 0); } MessageHandlerFactory::MessageHandlerFactory(QObject *parent) : QObject(parent) { } gammaray-2.11.3/core/tools/messagehandler/messagehandler.h000066400000000000000000000043231413310443000235740ustar00rootroot00000000000000/* messagehandler.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_MESSAGEHANDLER_MESSAGEHANDLER_H #define GAMMARAY_MESSAGEHANDLER_MESSAGEHANDLER_H #include "toolfactory.h" #include QT_BEGIN_NAMESPACE class QItemSelection; QT_END_NAMESPACE namespace GammaRay { struct DebugMessage; class MessageModel; class StackTraceModel; namespace Ui { class MessageHandler; } class MessageHandler : public MessageHandlerInterface { Q_OBJECT Q_INTERFACES(GammaRay::MessageHandlerInterface) public: explicit MessageHandler(Probe *probe, QObject *parent = nullptr); ~MessageHandler() override; private slots: void ensureHandlerInstalled(); void handleFatalMessage(const GammaRay::DebugMessage &message); void messageSelected(const QItemSelection &selection); private: MessageModel *m_messageModel; StackTraceModel *m_stackTraceModel; }; class MessageHandlerFactory : public QObject, public StandardToolFactory { Q_OBJECT Q_INTERFACES(GammaRay::ToolFactory) public: explicit MessageHandlerFactory(QObject *parent); }; } #endif // MESSAGEHANDLER_H gammaray-2.11.3/core/tools/messagehandler/messagemodel.cpp000066400000000000000000000101551413310443000236120ustar00rootroot00000000000000/* messagemodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "messagemodel.h" #include #include using namespace GammaRay; MessageModel::MessageModel(QObject *parent) : QAbstractTableModel(parent) { qRegisterMetaType(); } MessageModel::~MessageModel() = default; void MessageModel::addMessage(const DebugMessage &message) { ///WARNING: do not trigger *any* kind of debug output here /// this would trigger an infinite loop and hence crash! beginInsertRows(QModelIndex(), m_messages.count(), m_messages.count()); m_messages << message; endInsertRows(); } int MessageModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); return MessageModelColumn::COUNT; } int MessageModel::rowCount(const QModelIndex &parent) const { if (parent.isValid()) return 0; return m_messages.count(); } QVariant MessageModel::data(const QModelIndex &index, int role) const { if (!index.isValid() || index.row() > rowCount() || index.column() > columnCount()) return QVariant(); const DebugMessage &msg = m_messages.at(index.row()); if (role == Qt::DisplayRole) { switch (index.column()) { case MessageModelColumn::Message: return msg.message; case MessageModelColumn::Time: return msg.time.toString(); case MessageModelColumn::Category: return msg.category; case MessageModelColumn::Function: return msg.function; case MessageModelColumn::File: return msg.file; } } else if (role == MessageModelRole::Sort) { switch (index.column()) { case MessageModelColumn::Time: return msg.time; case MessageModelColumn::Message: return msg.message; case MessageModelColumn::Category: return msg.category; case MessageModelColumn::Function: return msg.function; case MessageModelColumn::File: return QString::fromLatin1("%1:%2").arg(msg.file).arg(msg.line); } } else if (role == MessageModelRole::Type && index.column() == 0) { return static_cast(msg.type); } else if (role == MessageModelRole::Line && index.column() == MessageModelColumn::File) { return msg.line; } else if (role == MessageModelRole::Backtrace) { return QVariant::fromValue(msg.backtrace); } return QVariant(); } QVariant MessageModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role == Qt::DisplayRole && orientation == Qt::Horizontal) { switch (section) { case MessageModelColumn::Message: return tr("Message"); case MessageModelColumn::Time: return tr("Time"); case MessageModelColumn::Category: return tr("Category"); case MessageModelColumn::Function: return tr("Function"); case MessageModelColumn::File: return tr("Source"); } } return QVariant(); } gammaray-2.11.3/core/tools/messagehandler/messagemodel.h000066400000000000000000000046531413310443000232650ustar00rootroot00000000000000/* messagemodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_MESSAGEHANDLER_MESSAGEMODEL_H #define GAMMARAY_MESSAGEHANDLER_MESSAGEMODEL_H #include #include #include #include #include #include namespace GammaRay { struct DebugMessage { QtMsgType type; QString message; QTime time; Execution::Trace backtrace; QString category; QString file; QString function; int line; }; } Q_DECLARE_METATYPE(GammaRay::DebugMessage) QT_BEGIN_NAMESPACE Q_DECLARE_TYPEINFO(GammaRay::DebugMessage, Q_MOVABLE_TYPE); QT_END_NAMESPACE namespace GammaRay { class MessageModel : public QAbstractTableModel { Q_OBJECT public: explicit MessageModel(QObject *parent = nullptr); ~MessageModel() override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; public slots: void addMessage(const GammaRay::DebugMessage &message); private: QVector m_messages; }; } #endif // MESSAGEMODEL_H gammaray-2.11.3/core/tools/metaobjectbrowser/000077500000000000000000000000001413310443000211765ustar00rootroot00000000000000gammaray-2.11.3/core/tools/metaobjectbrowser/metaobjectbrowser.cpp000066400000000000000000000157771413310443000254440ustar00rootroot00000000000000/* metaobjectbrowser.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Kevin Funk Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "metaobjectbrowser.h" #include "metaobjecttreemodel.h" #include #include #include #include #include #include #include #include #include #include <3rdparty/kde/krecursivefilterproxymodel.h> #include #include using namespace GammaRay; MetaObjectBrowser::MetaObjectBrowser(Probe *probe, QObject *parent) : QObject(parent) , m_propertyController(new PropertyController(QStringLiteral("com.kdab.GammaRay.MetaObjectBrowser"), this)) , m_motm(new MetaObjectTreeModel(this)) , m_model(nullptr) { auto model = new ServerProxyModel(this); model->addRole(QMetaObjectModel::MetaObjectIssues); model->addRole(QMetaObjectModel::MetaObjectInvalid); model->setSourceModel(m_motm); m_model = model; probe->registerModel(QStringLiteral("com.kdab.GammaRay.MetaObjectBrowserTreeModel"), m_model); QItemSelectionModel *selectionModel = ObjectBroker::selectionModel(m_model); connect(selectionModel, &QItemSelectionModel::selectionChanged, this, &MetaObjectBrowser::objectSelectionChanged); m_propertyController->setMetaObject(nullptr); // init connect(probe, &Probe::objectSelected, this, &MetaObjectBrowser::qobjectSelected); connect(probe, &Probe::nonQObjectSelected, this, &MetaObjectBrowser::voidPtrObjectSelected); ObjectBroker::registerObject(QStringLiteral("com.kdab.GammaRay.MetaObjectBrowser"), this); ProblemCollector::registerProblemChecker("com.kdab.GammaRay.MetaObjectBrowser.QMetaObjectValidator", "QMetaObject Validator", "Checks for common errors with meta objects, like invocable functions with unregistered parameter types.", &MetaObjectBrowser::scanForMetaObjectProblems, /*enabled=*/ false ); } void MetaObjectBrowser::rescanMetaTypes() { Probe::instance()->metaObjectRegistry()->scanMetaTypes(); } void MetaObjectBrowser::objectSelectionChanged(const QItemSelection &selection) { QModelIndex index; if (selection.size() == 1) index = selection.first().topLeft(); if (index.isValid()) { const QMetaObject *metaObject = index.data(QMetaObjectModel::MetaObjectRole).value(); m_propertyController->setMetaObject(metaObject); } else { m_propertyController->setMetaObject(nullptr); } } void MetaObjectBrowser::qobjectSelected(QObject *obj) { if (!obj) return; metaObjectSelected(obj->metaObject()); } void MetaObjectBrowser::voidPtrObjectSelected(void *obj, const QString &typeName) { if (typeName != QLatin1String("const QMetaObject*")) return; metaObjectSelected(static_cast(obj)); } void MetaObjectBrowser::metaObjectSelected(const QMetaObject *mo) { if (!mo) return; mo = Probe::instance()->metaObjectRegistry()->canonicalMetaObject(mo); const auto indexes = m_model->match(m_model->index(0, 0), QMetaObjectModel::MetaObjectRole, QVariant::fromValue(mo), Qt::MatchExactly | Qt::MatchRecursive | Qt::MatchWrap); if (indexes.isEmpty()) { metaObjectSelected(mo->superClass()); return; } ObjectBroker::selectionModel(m_model)->select( indexes.first(), QItemSelectionModel::Rows | QItemSelectionModel::ClearAndSelect); } QVector MetaObjectBrowserFactory::selectableTypes() const { return QVector() << QObject::staticMetaObject.className() << "QMetaObject"; } void MetaObjectBrowser::scanForMetaObjectProblems() { doProblemScan(nullptr); } void MetaObjectBrowser::doProblemScan(const QMetaObject *parent) { auto registry = Probe::instance()->metaObjectRegistry(); const QVector metaObjects = registry->childrenOf(parent); for (const QMetaObject *mo : metaObjects) { if (!registry->isValid(mo) || !registry->isStatic(mo)) continue; auto results = QMetaObjectValidator::check(mo); if (results != QMetaObjectValidatorResult::NoIssue) { //TODO do we want the Problem descriptions have more detail, i.e. have one problem listed // for each method/property that has issues instead of one for each metaobject? Problem p; p.severity = Problem::Warning; QStringList issueList; if (results & QMetaObjectValidatorResult::SignalOverride) issueList.push_back(QStringLiteral("overrides base class signal")); if (results & QMetaObjectValidatorResult::UnknownMethodParameterType) issueList.push_back(QStringLiteral("uses a parameter type not registered with the meta type system")); if (results & QMetaObjectValidatorResult::PropertyOverride) issueList.push_back(QStringLiteral("overrides base class property")); if (results & QMetaObjectValidatorResult::UnknownPropertyType) issueList.push_back(QStringLiteral("has a property with a type not registered with the meta type system")); p.description = QStringLiteral("%1 %2.").arg(mo->className(), issueList.join(", ")); p.object = ObjectId(const_cast(mo), "const QMetaObject*"); p.problemId = QString("com.kdab.GammaRay.MetaObjectBrowser.QMetaObjectValidator:%1").arg(reinterpret_cast(mo)); p.findingCategory = Problem::Scan; ProblemCollector::addProblem(p); } doProblemScan(mo); } } gammaray-2.11.3/core/tools/metaobjectbrowser/metaobjectbrowser.h000066400000000000000000000046531413310443000251000ustar00rootroot00000000000000/* metaobjectbrowser.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Kevin Funk Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_METAOBJECTBROWSER_METATYPEBROWSER_H #define GAMMARAY_METAOBJECTBROWSER_METATYPEBROWSER_H #include "toolfactory.h" QT_BEGIN_NAMESPACE class QAbstractProxyModel; class QItemSelection; QT_END_NAMESPACE namespace GammaRay { class MetaObjectTreeModel; class PropertyController; class MetaObjectBrowser : public QObject { Q_OBJECT public: explicit MetaObjectBrowser(Probe *probe, QObject *parent = nullptr); public Q_SLOTS: void rescanMetaTypes(); private Q_SLOTS: void objectSelectionChanged(const QItemSelection &selection); void qobjectSelected(QObject *obj); void voidPtrObjectSelected(void *obj, const QString &typeName); private: void metaObjectSelected(const QMetaObject *mo); static void scanForMetaObjectProblems(); static void doProblemScan(const QMetaObject *parent); PropertyController *m_propertyController; MetaObjectTreeModel *m_motm; QAbstractProxyModel *m_model; }; class MetaObjectBrowserFactory : public QObject, public StandardToolFactory { Q_OBJECT Q_INTERFACES(GammaRay::ToolFactory) public: explicit MetaObjectBrowserFactory(QObject *parent) : QObject(parent) { } QVector selectableTypes() const override; }; } #endif // GAMMARAY_METAOBJECTBROWSER_METATYPEBROWSER_H gammaray-2.11.3/core/tools/metaobjectbrowser/metaobjecttreemodel.cpp000066400000000000000000000175221413310443000257270ustar00rootroot00000000000000/* metaobjecttreemodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2012-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Kevin Funk Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "metaobjecttreemodel.h" #include #include #include #include #include #include #include #include #include #include using namespace GammaRay; static inline MetaObjectRegistry *registry() { return Probe::instance()->metaObjectRegistry(); } MetaObjectTreeModel::MetaObjectTreeModel(QObject *parent) : QAbstractItemModel(parent) , m_pendingDataChangedTimer(new QTimer(this)) { connect(registry(), &MetaObjectRegistry::beforeMetaObjectAdded, this, &MetaObjectTreeModel::addMetaObject); connect(registry(), &MetaObjectRegistry::afterMetaObjectAdded, this, &MetaObjectTreeModel::endAddMetaObject); connect(registry(), &MetaObjectRegistry::dataChanged, this, &MetaObjectTreeModel::scheduleDataChange); m_pendingDataChangedTimer->setInterval(100); m_pendingDataChangedTimer->setSingleShot(true); connect(m_pendingDataChangedTimer, &QTimer::timeout, this, &MetaObjectTreeModel::emitPendingDataChanged); } MetaObjectTreeModel::~MetaObjectTreeModel() = default; QVariant MetaObjectTreeModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); const int column = index.column(); const QMetaObject *object = metaObjectForIndex(index); if (role == Qt::DisplayRole) { switch (column) { case QMetaObjectModel::ObjectColumn: return registry()->data(object, MetaObjectRegistry::ClassName); case QMetaObjectModel::ObjectSelfCountColumn: return registry()->data(object, MetaObjectRegistry::SelfCount); case QMetaObjectModel::ObjectInclusiveCountColumn: return registry()->data(object, MetaObjectRegistry::InclusiveCount); case QMetaObjectModel::ObjectSelfAliveCountColumn: return registry()->data(object, MetaObjectRegistry::SelfAliveCount); case QMetaObjectModel::ObjectInclusiveAliveCountColumn: return registry()->data(object, MetaObjectRegistry::InclusiveAliveCount); default: break; } } else if (role == QMetaObjectModel::MetaObjectRole) { if (!registry()->isValid(object)) return QVariant(); return QVariant::fromValue(registry()->aliveInstance(object)); } else if (role == QMetaObjectModel::MetaObjectIssues && index.column() == QMetaObjectModel::ObjectColumn) { if (!registry()->isStatic(object)) return QVariant(); const auto r = QMetaObjectValidator::check(registry()->aliveInstance(object)); return r == QMetaObjectValidatorResult::NoIssue ? QVariant() : QVariant::fromValue(r); } else if (role == QMetaObjectModel::MetaObjectInvalid && index.column() == QMetaObjectModel::ObjectInclusiveAliveCountColumn) { if (!registry()->isValid(object)) return true; return QVariant(); } return QVariant(); } int MetaObjectTreeModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); return QMetaObjectModel::_Last; } int MetaObjectTreeModel::rowCount(const QModelIndex &parent) const { if (parent.column() > 0) return 0; const QMetaObject *metaObject = metaObjectForIndex(parent); return registry()->childrenOf(metaObject).size(); } QModelIndex MetaObjectTreeModel::parent(const QModelIndex &child) const { if (!child.isValid()) return {}; const QMetaObject *object = metaObjectForIndex(child); Q_ASSERT(object); const QMetaObject *parentObject = registry()->parentOf(object); return indexForMetaObject(parentObject); } QModelIndex MetaObjectTreeModel::index(int row, int column, const QModelIndex &parent) const { const QMetaObject *parentObject = metaObjectForIndex(parent); const QVector &children = registry()->childrenOf(parentObject); if (row < 0 || column < 0 || row >= children.size() || column >= columnCount()) return {}; const QMetaObject *object = children.at(row); return createIndex(row, column, const_cast(object)); } QModelIndexList MetaObjectTreeModel::match(const QModelIndex &start, int role, const QVariant &value, int hits, Qt::MatchFlags flags) const { if (role == QMetaObjectModel::MetaObjectRole) { const auto mo = value.value(); return QModelIndexList() << indexForMetaObject(mo); } return QAbstractItemModel::match(start, role, value, hits, flags); } void MetaObjectTreeModel::addMetaObject(const QMetaObject *metaObject) { const QMetaObject *parentMetaObject = registry()->parentOf(metaObject); const QModelIndex parentIndex = indexForMetaObject(parentMetaObject); // either we get a proper parent and hence valid index or there is no parent assert(parentIndex.isValid() || !parentMetaObject); int size = registry()->childrenOf(parentMetaObject).size(); beginInsertRows(parentIndex, size, size); } void MetaObjectTreeModel::endAddMetaObject(const QMetaObject *metaObject) { Q_UNUSED(metaObject) endInsertRows(); } QModelIndex MetaObjectTreeModel::indexForMetaObject(const QMetaObject *metaObject) const { if (!metaObject) return {}; const QMetaObject *parentObject = registry()->parentOf(metaObject); Q_ASSERT(parentObject != metaObject); const QModelIndex parentIndex = indexForMetaObject(parentObject); if (!parentIndex.isValid() && parentObject) return QModelIndex(); const int row = registry()->childrenOf(parentObject).indexOf(metaObject); if (row < 0) return QModelIndex(); return index(row, 0, parentIndex); } const QMetaObject *MetaObjectTreeModel::metaObjectForIndex(const QModelIndex &index) const { if (!index.isValid()) return nullptr; void *internalPointer = index.internalPointer(); const QMetaObject *metaObject = reinterpret_cast(internalPointer); return metaObject; } void GammaRay::MetaObjectTreeModel::scheduleDataChange(const QMetaObject *mo) { m_pendingDataChanged.insert(mo); if (!m_pendingDataChangedTimer->isActive()) m_pendingDataChangedTimer->start(); } void GammaRay::MetaObjectTreeModel::emitPendingDataChanged() { for (auto mo : qAsConst(m_pendingDataChanged)) { auto index = indexForMetaObject(mo); if (!index.isValid()) continue; emit dataChanged(index.sibling(index.row(), QMetaObjectModel::ObjectSelfCountColumn), index.sibling(index.row(), QMetaObjectModel::ObjectInclusiveAliveCountColumn)); } m_pendingDataChanged.clear(); } gammaray-2.11.3/core/tools/metaobjectbrowser/metaobjecttreemodel.h000066400000000000000000000051431413310443000253700ustar00rootroot00000000000000/* metaobjecttreemodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2012-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Kevin Funk Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_METAOBJECTTREEMODEL_H #define GAMMARAY_METAOBJECTTREEMODEL_H #include #include #include QT_BEGIN_NAMESPACE class QTimer; QT_END_NAMESPACE namespace GammaRay { class Probe; class MetaObjectTreeModel : public QAbstractItemModel { Q_OBJECT public: explicit MetaObjectTreeModel(QObject *parent = nullptr); ~MetaObjectTreeModel() override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; QModelIndex parent(const QModelIndex &child) const override; QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; QModelIndexList match(const QModelIndex &start, int role, const QVariant &value, int hits, Qt::MatchFlags flags) const override; private: QModelIndex indexForMetaObject(const QMetaObject *metaObject) const; const QMetaObject *metaObjectForIndex(const QModelIndex &index) const; private slots: void addMetaObject(const QMetaObject *metaObject); void endAddMetaObject(const QMetaObject *metaObject); void scheduleDataChange(const QMetaObject *mo); void emitPendingDataChanged(); private: QSet m_pendingDataChanged; QTimer *m_pendingDataChangedTimer; }; } #endif // GAMMARAY_METAOBJECTTREEMODEL_H gammaray-2.11.3/core/tools/metatypebrowser/000077500000000000000000000000001413310443000207115ustar00rootroot00000000000000gammaray-2.11.3/core/tools/metatypebrowser/metatypebrowser.cpp000066400000000000000000000034701413310443000246550ustar00rootroot00000000000000/* metatypebrowser.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "metatypebrowser.h" #include "metatypesmodel.h" #include #include #include #include using namespace GammaRay; MetaTypeBrowser::MetaTypeBrowser(Probe *probe, QObject *parent) : MetaTypeBrowserInterface(parent) , m_mtm(new MetaTypesModel(this)) { auto proxy = new ServerProxyModel(this); proxy->setSourceModel(m_mtm); proxy->addRole(MetaTypeRoles::MetaObjectIdRole); probe->registerModel(QStringLiteral("com.kdab.GammaRay.MetaTypeModel"), proxy); } void MetaTypeBrowser::rescanTypes() { m_mtm->scanMetaTypes(); } gammaray-2.11.3/core/tools/metatypebrowser/metatypebrowser.h000066400000000000000000000036711413310443000243250ustar00rootroot00000000000000/* metatypebrowser.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_METATYPEBROWSER_METATYPEBROWSER_H #define GAMMARAY_METATYPEBROWSER_METATYPEBROWSER_H #include #include namespace GammaRay { class MetaTypesModel; class MetaTypeBrowser : public MetaTypeBrowserInterface { Q_OBJECT Q_INTERFACES(GammaRay::MetaTypeBrowserInterface) public: explicit MetaTypeBrowser(Probe *probe, QObject *parent = nullptr); public slots: void rescanTypes() override; private: MetaTypesModel *m_mtm; }; class MetaTypeBrowserFactory : public QObject, public StandardToolFactory { Q_OBJECT Q_INTERFACES(GammaRay::ToolFactory) public: explicit MetaTypeBrowserFactory(QObject *parent) : QObject(parent) { } }; } #endif // GAMMARAY_METATYPEBROWSER_H gammaray-2.11.3/core/tools/metatypebrowser/metatypesmodel.cpp000066400000000000000000000112761413310443000244600ustar00rootroot00000000000000/* metatypesmodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Stephen Kelly Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "metatypesmodel.h" #include #include #include #include #include #include #include using namespace GammaRay; MetaTypesModel::MetaTypesModel(QObject *parent) : QAbstractTableModel(parent) { scanMetaTypes(); // TODO do we need to re-run this when new types are registered at runtime? } QVariant MetaTypesModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); const auto metaTypeId = m_metaTypes.at(index.row()); if (role == Qt::DisplayRole) { switch (index.column()) { case 0: { QString name(QMetaType::typeName(metaTypeId)); if (name.isEmpty()) return tr("N/A"); return name; } case 1: return metaTypeId; case 2: return QMetaType::sizeOf(metaTypeId); case 3: return Util::addressToString(QMetaType::metaObjectForType(metaTypeId)); case 4: { const QMetaType::TypeFlags flags = QMetaType::typeFlags(metaTypeId); QStringList l; #define F(x) if (flags & QMetaType:: x) l.push_back(QStringLiteral(#x)) F(NeedsConstruction); F(NeedsDestruction); F(MovableType); F(PointerToQObject); F(IsEnumeration); F(SharedPointerToQObject); F(WeakPointerToQObject); F(TrackingPointerToQObject); F(WasDeclaredAsMetaType); F(IsGadget); #undef F return l.join(QStringLiteral(", ")); } case 5: return QMetaType::hasRegisteredComparators(metaTypeId); case 6: return QMetaType::hasRegisteredDebugStreamOperator(metaTypeId); } } else if (role == MetaTypeRoles::MetaObjectIdRole && index.column() == 0) { if (auto mo = QMetaType::metaObjectForType(metaTypeId)) return QVariant::fromValue(ObjectId(const_cast(mo), "const QMetaObject*")); } return QVariant(); } int MetaTypesModel::rowCount(const QModelIndex &parent) const { if (parent.isValid()) return 0; return m_metaTypes.size(); } int MetaTypesModel::columnCount(const QModelIndex &parent) const { if (parent.isValid()) return 0; return 7; } void MetaTypesModel::scanMetaTypes() { QVector metaTypes; for (int mtId = 0; mtId <= QMetaType::User || QMetaType::isRegistered(mtId); ++mtId) { if (!QMetaType::isRegistered(mtId)) continue; const auto name = QMetaType::typeName(mtId); if (strstr(name, "GammaRay::") != name) metaTypes.push_back(mtId); } auto itOld = m_metaTypes.constBegin(); auto itNew = metaTypes.constBegin(); for (; itOld != m_metaTypes.constEnd() && itNew != metaTypes.constEnd(); ++itOld, ++itNew) { if (*itOld != *itNew) break; } if (itOld != m_metaTypes.constEnd()) { const auto row = std::distance(m_metaTypes.constBegin(), itOld); beginRemoveRows(QModelIndex(), row, m_metaTypes.size() - 1); m_metaTypes.remove(row, m_metaTypes.size() - row); endRemoveRows(); } if (itNew != metaTypes.constEnd()) { const auto count = std::distance(itNew, metaTypes.constEnd()); beginInsertRows(QModelIndex(), m_metaTypes.size(), m_metaTypes.size() + count - 1); std::copy(itNew, metaTypes.constEnd(), std::back_inserter(m_metaTypes)); endInsertRows(); } } gammaray-2.11.3/core/tools/metatypebrowser/metatypesmodel.h000066400000000000000000000033471413310443000241250ustar00rootroot00000000000000/* metatypesmodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Stephen Kelly Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_METATYPEBROWSER_METATYPESMODEL_H #define GAMMARAY_METATYPEBROWSER_METATYPESMODEL_H #include #include namespace GammaRay { class MetaTypesModel : public QAbstractTableModel { Q_OBJECT public: explicit MetaTypesModel(QObject *parent = nullptr); QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; void scanMetaTypes(); private: QVector m_metaTypes; }; } #endif gammaray-2.11.3/core/tools/objectinspector/000077500000000000000000000000001413310443000206525ustar00rootroot00000000000000gammaray-2.11.3/core/tools/objectinspector/abstractconnectionsmodel.cpp000066400000000000000000000150621413310443000264510ustar00rootroot00000000000000/* abstractconnectionsmodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include "abstractconnectionsmodel.h" #include "common/tools/objectinspector/connectionsmodelroles.h" #include "core/util.h" #include #include #include using namespace GammaRay; AbstractConnectionsModel::AbstractConnectionsModel(QObject *parent) : QAbstractTableModel(parent) { } AbstractConnectionsModel::~AbstractConnectionsModel() = default; int AbstractConnectionsModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); return 4; } int AbstractConnectionsModel::rowCount(const QModelIndex &parent) const { if (parent.isValid()) return 0; return m_connections.size(); } QVariant AbstractConnectionsModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); const Connection &conn = m_connections.at(index.row()); if (role == Qt::DisplayRole && index.column() == 3) { switch (conn.type) { // see qobject_p.h case 0: if (!conn.endpoint || !m_object) return tr("Auto"); return tr("Auto (%1)").arg(conn.endpoint->thread() == m_object->thread() ? tr( "Direct") : tr("Queued")); case 1: return tr("Direct"); case 2: return tr("Queued"); case 3: // Qt5 case 4: // Qt4 return tr("Blocking"); default: return tr("Unknown: %1").arg(conn.type); } } if (role == ConnectionsModelRoles::WarningFlagRole && index.column() == 0) return isDuplicate(conn) || isDirectCrossThreadConnection(conn); if (role == Qt::ToolTipRole) { QStringList tips; if (isDuplicate(conn)) tips << tr( "Connections exists multiple times.\nThe connected slot is called multiple times when the signal is emitted."); if (isDirectCrossThreadConnection(conn)) tips << tr( "Direct cross-thread connection.\nThe connected slot is called in the context of the emitting thread."); if (!tips.isEmpty()) return tips.join(QStringLiteral("\n\n")); } if (role == ConnectionsModelRoles::EndpointRole) return QVariant::fromValue(conn.endpoint.data()); if (role == ConnectionsModelRoles::ActionRole) { if (conn.endpoint && conn.endpoint != m_object) return ConnectionsModelActions::NavigateToEndpoint; return ConnectionsModelActions::NoAction; } return QVariant(); } QVariant AbstractConnectionsModel::headerData(int section, Qt::Orientation orientation, int role) const { if (section == 3 && orientation == Qt::Horizontal && role == Qt::DisplayRole) return tr("Type"); return QAbstractItemModel::headerData(section, orientation, role); } QString AbstractConnectionsModel::displayString(QObject *object, int methodIndex) { if (!object) return tr(""); if (methodIndex < 0) return tr(""); const QMetaMethod method = object->metaObject()->method(methodIndex); return Util::prettyMethodSignature(method); } QString AbstractConnectionsModel::displayString(QObject *object) { if (!object) return tr(""); return Util::displayString(object); } int AbstractConnectionsModel::signalIndexToMethodIndex(QObject *object, int signalIndex) { if (signalIndex < 0) return signalIndex; Q_ASSERT(object); return Util::signalIndexToMethodIndex(object->metaObject(), signalIndex); } QMap< int, QVariant > AbstractConnectionsModel::itemData(const QModelIndex &index) const { QMap d = QAbstractTableModel::itemData(index); d.insert(ConnectionsModelRoles::WarningFlagRole, data(index, ConnectionsModelRoles::WarningFlagRole)); d.insert(ConnectionsModelRoles::ActionRole, data(index, ConnectionsModelRoles::ActionRole)); return d; } bool AbstractConnectionsModel::isDuplicate(const QVector &connections, const AbstractConnectionsModel::Connection& conn) { for (const Connection &c : qAsConst(connections)) { if (&c == &conn) continue; if (c.endpoint == conn.endpoint && c.slotIndex >= 0 && c.slotIndex == conn.slotIndex && c.signalIndex >= 0 && c.signalIndex == conn.signalIndex) return true; } return false; } bool AbstractConnectionsModel::isDuplicate(const Connection &conn) const { return isDuplicate(m_connections, conn); } bool AbstractConnectionsModel::isDirectCrossThreadConnection(QObject *object, const AbstractConnectionsModel::Connection &conn) { if (!conn.endpoint || !object || conn.endpoint->thread() == object->thread()) return false; return conn.type == 1; // direct } bool AbstractConnectionsModel::isDirectCrossThreadConnection(const Connection &conn) const { return isDirectCrossThreadConnection(m_object, conn); } void AbstractConnectionsModel::clear() { if (m_connections.isEmpty()) return; beginRemoveRows(QModelIndex(), 0, m_connections.size() - 1); m_connections.clear(); endRemoveRows(); } void AbstractConnectionsModel::setConnections(const QVector &connections) { Q_ASSERT(m_connections.isEmpty()); if (connections.isEmpty()) return; beginInsertRows(QModelIndex(), 0, connections.size() - 1); m_connections = connections; endInsertRows(); } gammaray-2.11.3/core/tools/objectinspector/abstractconnectionsmodel.h000066400000000000000000000056501413310443000261200ustar00rootroot00000000000000/* abstractconnectionsmodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_OBJECTINSPECTOR_ABSTRACTCONNECTIONSMODEL_H #define GAMMARAY_OBJECTINSPECTOR_ABSTRACTCONNECTIONSMODEL_H #include #include #include namespace GammaRay { /** Common base class for the inbound and outbound connections models. */ class AbstractConnectionsModel : public QAbstractTableModel { Q_OBJECT public: explicit AbstractConnectionsModel(QObject *parent = nullptr); ~AbstractConnectionsModel() override; virtual void setObject(QObject *object) = 0; int columnCount(const QModelIndex &parent) const override; int rowCount(const QModelIndex &parent) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; QMap< int, QVariant > itemData(const QModelIndex &index) const override; struct Connection { QPointer endpoint; int signalIndex; int slotIndex; int type; }; protected: static QString displayString(QObject *object, int methodIndex); static QString displayString(QObject *object); static int signalIndexToMethodIndex(QObject *object, int signalIndex); void clear(); void setConnections(const QVector &connections); public: static bool isDuplicate(const QVector &connections, const Connection &conn); static bool isDirectCrossThreadConnection(QObject *object, const Connection &conn); protected: QPointer m_object; QVector m_connections; private: bool isDuplicate(const Connection &conn) const; bool isDirectCrossThreadConnection(const Connection &conn) const; }; } #endif // GAMMARAY_ABSTRACTCONNECTIONSMODEL_H gammaray-2.11.3/core/tools/objectinspector/applicationattributeextension.cpp000066400000000000000000000041161413310443000275440ustar00rootroot00000000000000/* applicationattributeextension.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "applicationattributeextension.h" #include #include #include using namespace GammaRay; ApplicationAttributeExtension::ApplicationAttributeExtension( GammaRay::PropertyController *controller) : PropertyControllerExtension(controller->objectBaseName() + ".applicationAttributes") , m_attributeModel(new AttributeModel(controller)) { m_attributeModel->setAttributeType("ApplicationAttribute"); controller->registerModel(m_attributeModel, QStringLiteral("applicationAttributeModel")); } ApplicationAttributeExtension::~ApplicationAttributeExtension() = default; bool ApplicationAttributeExtension::setQObject(QObject *object) { if (auto app = qobject_cast(object)) { m_attributeModel->setObject(app); return true; } m_attributeModel->setObject(nullptr); return false; } gammaray-2.11.3/core/tools/objectinspector/applicationattributeextension.h000066400000000000000000000034451413310443000272150ustar00rootroot00000000000000/* applicationattributeextension.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_APPLICATIONATTRIBUTEEXTENSION_H #define GAMMARAY_APPLICATIONATTRIBUTEEXTENSION_H #include QT_BEGIN_NAMESPACE class QCoreApplication; QT_END_NAMESPACE namespace GammaRay { template class AttributeModel; class ApplicationAttributeExtension : public PropertyControllerExtension { public: explicit ApplicationAttributeExtension(PropertyController *controller); ~ApplicationAttributeExtension(); bool setQObject(QObject *object) override; private: AttributeModel *m_attributeModel; }; } #endif // GAMMARAY_APPLICATIONATTRIBUTEEXTENSION_H gammaray-2.11.3/core/tools/objectinspector/bindingextension.cpp000066400000000000000000000073501413310443000247320ustar00rootroot00000000000000/* bindingextension.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Authors: Volker Krause Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ // Own #include "bindingextension.h" #include "bindingmodel.h" #include #include #include #include #include #include #include #include // Qt #include #include using namespace GammaRay; BindingExtension::BindingExtension(PropertyController* controller) : QObject(controller) , PropertyControllerExtension(controller->objectBaseName() + ".bindings") , m_object(nullptr) , m_bindingModel(new BindingModel(this)) { ObjectBroker::registerObject(controller->objectBaseName() + ".bindingsExtension", this); controller->registerModel(m_bindingModel, QStringLiteral("bindingModel")); } BindingExtension::~BindingExtension() = default; void BindingExtension::clear() { if (m_object) disconnect(m_object, nullptr, this, nullptr); m_bindingModel->aboutToClear(); m_bindings.clear(); m_object = nullptr; m_bindingModel->cleared(); } bool BindingExtension::setQObject(QObject* object) { if (m_object) disconnect(m_object, nullptr, this, nullptr); if (object) { if (!BindingAggregator::providerAvailableFor(object)) { m_bindings.clear(); m_bindingModel->setObject(nullptr, m_bindings); m_object = nullptr; return false; } m_bindings = BindingAggregator::bindingTreeForObject(object); for (const auto &node : m_bindings) { int signalIndex = node->property().notifySignalIndex(); if (signalIndex != -1) { QMetaObject::connect(object, signalIndex, this, metaObject()->indexOfMethod("propertyChanged()"), Qt::UniqueConnection); } } connect(object, &QObject::destroyed, this, &BindingExtension::clear); } m_bindingModel->setObject(object, m_bindings); m_object = object; return true; } void BindingExtension::propertyChanged() { Q_ASSERT(sender() == m_object); for (size_t i = 0; i < m_bindings.size(); ++i) { const auto &bindingNode = m_bindings[i].get(); if (bindingNode->property().notifySignalIndex() == senderSignalIndex()) { m_bindingModel->refresh(i, BindingAggregator::findDependenciesFor(bindingNode)); // There can be more than one property with the same notify signal, // so no break here... } } } BindingModel *BindingExtension::model() const { return m_bindingModel; } gammaray-2.11.3/core/tools/objectinspector/bindingextension.h000066400000000000000000000040021413310443000243660ustar00rootroot00000000000000/* bindingextension.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Authors: Volker Krause Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_BINDINGEXTENSION_H #define GAMMARAY_BINDINGEXTENSION_H // Own #include #include "gammaray_core_export.h" // Qt #include #include // Std #include #include namespace GammaRay { class BindingModel; class BindingNode; class GAMMARAY_CORE_EXPORT BindingExtension : public QObject, public PropertyControllerExtension { Q_OBJECT public: explicit BindingExtension(PropertyController *controller); ~BindingExtension() override; bool setQObject(QObject *object) override; BindingModel *model() const; private slots: void propertyChanged(); void clear(); private: QPointer m_object; std::vector> m_bindings; BindingModel *m_bindingModel; }; } #endif // GAMMARAY_BINDINGEXTENSION_H gammaray-2.11.3/core/tools/objectinspector/bindingmodel.cpp000066400000000000000000000222721413310443000240160ustar00rootroot00000000000000/* bindingmodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Authors: Volker Krause Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ // Own #include "bindingmodel.h" #include #include #include #include #include #include #include // Qt #include #include using namespace GammaRay; BindingModel::BindingModel(QObject* parent) : QAbstractItemModel(parent) , m_obj(nullptr) , m_bindings(nullptr) { } BindingModel::~BindingModel() = default; void BindingModel::aboutToClear() { beginResetModel(); } void BindingModel::cleared() { m_obj = nullptr; endResetModel(); } void BindingModel::setObject(QObject* obj, std::vector> &bindings) { if (m_obj == obj) return; // TODO use removerows/insertrows instead of reset here beginResetModel(); m_bindings = &bindings; m_obj = obj; endResetModel(); } void GammaRay::BindingModel::refresh(int row, std::vector> &&newDependencies) { Q_ASSERT(m_bindings); refresh((*m_bindings)[row].get(), std::move(newDependencies), createIndex(row, 0, (*m_bindings)[row].get())); } bool BindingModel::lessThan(const std::unique_ptr &a, const std::unique_ptr &b) { return a->object() < b->object() || (a->object() == b->object() && a->propertyIndex() < b->propertyIndex()); } void BindingModel::refresh(BindingNode *oldBindingNode, std::vector> &&newDependencies, const QModelIndex &index) { if (oldBindingNode->cachedValue() != oldBindingNode->readValue()) { oldBindingNode->refreshValue(); emit dataChanged(createIndex(index.row(), ValueColumn, oldBindingNode), createIndex(index.row(), ValueColumn, oldBindingNode)); } uint oldDepth = oldBindingNode->depth(); // Refresh dependencies auto &oldDependencies = oldBindingNode->dependencies(); std::sort(newDependencies.begin(), newDependencies.end(), &BindingModel::lessThan); oldDependencies.reserve(newDependencies.size()); auto oldIt = oldDependencies.begin(); auto newIt = newDependencies.begin(); while (oldIt != oldDependencies.end() && newIt != newDependencies.end()) { const auto idx = std::distance(oldDependencies.begin(), oldIt); if (lessThan(*oldIt, *newIt)) { // handle deleted node const auto firstToRemove = oldIt; while (oldIt != oldDependencies.end() && lessThan(*oldIt, *newIt)) { ++oldIt; } // if more than one was removed, find all const auto count = std::distance(firstToRemove, oldIt); beginRemoveRows(index, idx, idx + count - 1); oldIt = oldDependencies.erase(firstToRemove, oldIt); endRemoveRows(); } else if (lessThan(*newIt, *oldIt)) { // handle added node int count = 0; for (auto addIt = newIt; addIt != newDependencies.end() && lessThan(*addIt, *oldIt); ++addIt) { ++count; // count, how many additions we have } beginInsertRows(index, idx, idx + count - 1); for (int i = 0; i < count; ++i) { (*newIt)->setParent(oldBindingNode); oldIt = oldDependencies.insert(oldIt, std::move(*newIt)); ++oldIt; ++newIt; } endInsertRows(); } else { // already known node, no change refresh(oldIt->get(), std::move(newIt->get()->dependencies()), createIndex(idx, 0, oldIt->get())); ++oldIt; ++newIt; } } if (oldIt == oldDependencies.end() && newIt != newDependencies.end()) { // Add remaining new items to list and inform the client const auto idx = std::distance(oldDependencies.begin(), oldIt); const auto count = std::distance(newIt, newDependencies.end()); beginInsertRows(index, idx, idx + count - 1); while (newIt != newDependencies.end()) { (*newIt)->setParent(oldBindingNode); oldDependencies.push_back(std::move(*newIt)); ++newIt; } endInsertRows(); } else if (oldIt != oldDependencies.end()) { // Inform the client about the removed rows const auto idx = std::distance(oldDependencies.begin(), oldIt); const auto count = std::distance(oldIt, oldDependencies.end()); beginRemoveRows(index, idx, idx + count - 1); oldIt = oldDependencies.erase(oldIt, oldDependencies.end()); endRemoveRows(); } if (oldBindingNode->depth() != oldDepth) { emit dataChanged(createIndex(index.row(), DepthColumn, oldBindingNode), createIndex(index.row(), DepthColumn, oldBindingNode)); } } int BindingModel::columnCount(const QModelIndex& parent) const { Q_UNUSED(parent); return 4; } int BindingModel::rowCount(const QModelIndex& parent) const { if (!m_bindings) return 0; if (!parent.isValid()) return m_bindings->size(); if (parent.column() != 0) return 0; return static_cast(parent.internalPointer())->dependencies().size(); } QVariant BindingModel::data(const QModelIndex& index, int role) const { if (!index.isValid()) return QVariant(); BindingNode *binding = static_cast(index.internalPointer()); if (!binding) return QVariant(); if (role == Qt::DisplayRole) { switch (index.column()) { case NameColumn: { return binding->canonicalName(); } case ValueColumn: return binding->cachedValue(); case LocationColumn: return binding->sourceLocation().displayString(); case DepthColumn: { uint depth = binding->depth(); return depth == std::numeric_limits::max() ? QStringLiteral("\u221E") : QString::number(depth); // Unicode infinity sign } } } else if (role == ObjectModel::DeclarationLocationRole) { return QVariant::fromValue(binding->sourceLocation()); } return QVariant(); } QMap BindingModel::itemData(const QModelIndex &index) const { QMap d = QAbstractItemModel::itemData(index); d.insert(ObjectModel::DeclarationLocationRole, data(index, ObjectModel::DeclarationLocationRole)); return d; } QVariant BindingModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { switch (section) { case NameColumn: return tr("Property"); case ValueColumn: return tr("Value"); case LocationColumn: return tr("Source"); case DepthColumn: return tr("Depth"); } } return QAbstractItemModel::headerData(section, orientation, role); } QModelIndex GammaRay::BindingModel::index(int row, int column, const QModelIndex& parent) const { if (!m_bindings || !hasIndex(row, column, parent)) { return {}; } QModelIndex index; if (parent.isValid()) { index = createIndex(row, column, static_cast(parent.internalPointer())->dependencies()[row].get()); } else { index = createIndex(row, column, (*m_bindings)[row].get()); } return index; } QModelIndex BindingModel::findEquivalent(const std::vector> &container, BindingNode *bindingNode) const { for (size_t i = 0; i < container.size(); i++) { if (bindingNode->object() == container[i]->object() && bindingNode->propertyIndex() == container[i]->propertyIndex()) { return createIndex(i, 0, container[i].get()); } } return {}; } QModelIndex GammaRay::BindingModel::parent(const QModelIndex& child) const { if (!m_bindings || !child.isValid()) return {}; BindingNode *parent = static_cast(child.internalPointer())->parent(); if (!parent) return QModelIndex(); BindingNode *grandparent = parent->parent(); if (!grandparent) return findEquivalent(*m_bindings, parent); return findEquivalent(grandparent->dependencies(), parent); } gammaray-2.11.3/core/tools/objectinspector/bindingmodel.h000066400000000000000000000061271413310443000234640ustar00rootroot00000000000000/* bindingmodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Authors: Volker Krause Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_BINDINGMODEL_H #define GAMMARAY_BINDINGMODEL_H // Own // Qt #include #include // Std #include #include class BindingInspectorTest; namespace GammaRay { class BindingNode; class AbstractBindingProvider; class BindingModel : public QAbstractItemModel { Q_OBJECT public: enum { NameColumn = 0, ValueColumn = 1, DepthColumn = 2, LocationColumn = 3 }; explicit BindingModel (QObject *parent = nullptr); ~BindingModel() override; void setObject(QObject *obj, std::vector> &bindings); void refresh(int row, std::vector> &&newDependencies); int rowCount(const QModelIndex &parent = QModelIndex()) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; QModelIndex index(int row, int column, const QModelIndex & parent = QModelIndex()) const override; QModelIndex parent(const QModelIndex &child) const override; QMap itemData(const QModelIndex &index) const override; void aboutToClear(); void cleared(); private: QModelIndex findEquivalent(const std::vector> &container, BindingNode *bindingNode) const; void refresh(BindingNode *oldBindingNode, std::vector> &&newDependencies, const QModelIndex &index); void bindingChanged(BindingNode *node); void findDependenciesFor(BindingNode *node); static bool lessThan(const std::unique_ptr &a, const std::unique_ptr &b); QPointer m_obj; std::vector> *m_bindings; }; } #endif // GAMMARAY_BINDINGMODEL_H gammaray-2.11.3/core/tools/objectinspector/classinfoextension.cpp000066400000000000000000000036531413310443000253030ustar00rootroot00000000000000/* classinfoextension.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "classinfoextension.h" #include "propertycontroller.h" #include "probe.h" #include "objectclassinfomodel.h" #include using namespace GammaRay; ClassInfoExtension::ClassInfoExtension(PropertyController *controller) : PropertyControllerExtension(controller->objectBaseName() + ".classInfo") , m_model(new ObjectClassInfoModel(controller)) { controller->registerModel(m_model, QStringLiteral("classInfo")); } ClassInfoExtension::~ClassInfoExtension() = default; bool ClassInfoExtension::setQObject(QObject *object) { m_model->setMetaObject(object ? object->metaObject() : nullptr); return m_model->rowCount() > 0; } bool ClassInfoExtension::setMetaObject(const QMetaObject *metaObject) { m_model->setMetaObject(metaObject); return m_model->rowCount() > 0; } gammaray-2.11.3/core/tools/objectinspector/classinfoextension.h000066400000000000000000000033051413310443000247420ustar00rootroot00000000000000/* classinfoextension.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_OBJECTINSPECTOR_CLASSINFOEXTENSION_H #define GAMMARAY_OBJECTINSPECTOR_CLASSINFOEXTENSION_H #include "propertycontrollerextension.h" namespace GammaRay { class PropertyController; class ObjectClassInfoModel; class ClassInfoExtension : public PropertyControllerExtension { public: explicit ClassInfoExtension(PropertyController *controller); ~ClassInfoExtension(); bool setQObject(QObject *object) override; bool setMetaObject(const QMetaObject *metaObject) override; private: ObjectClassInfoModel *m_model; }; } #endif // CLASSINFOEXTENSION_H gammaray-2.11.3/core/tools/objectinspector/connectionsextension.cpp000066400000000000000000000054201413310443000256360ustar00rootroot00000000000000/* connectionsextension.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include "connectionsextension.h" #include "inboundconnectionsmodel.h" #include "outboundconnectionsmodel.h" #include #include #include using namespace GammaRay; ConnectionsExtension::ConnectionsExtension(PropertyController *controller) : ConnectionsExtensionInterface( controller->objectBaseName() + ".connectionsExtension", controller) , PropertyControllerExtension(controller->objectBaseName() + ".connections") { m_inboundModel = new InboundConnectionsModel(controller); m_outboundModel = new OutboundConnectionsModel(controller); controller->registerModel(m_inboundModel, QStringLiteral("inboundConnections")); controller->registerModel(m_outboundModel, QStringLiteral("outboundConnections")); } ConnectionsExtension::~ConnectionsExtension() = default; bool ConnectionsExtension::setQObject(QObject *object) { m_inboundModel->setObject(object); m_outboundModel->setObject(object); return true; } void ConnectionsExtension::navigateToSender(int modelRow) { const QModelIndex index = m_inboundModel->index(modelRow, 0); QObject *sender = index.data(ConnectionsModelRoles::EndpointRole).value(); if (!sender) return; Probe::instance()->selectObject(sender); } void ConnectionsExtension::navigateToReceiver(int modelRow) { const QModelIndex index = m_outboundModel->index(modelRow, 0); QObject *receiver = index.data(ConnectionsModelRoles::EndpointRole).value(); if (!receiver) return; Probe::instance()->selectObject(receiver); } gammaray-2.11.3/core/tools/objectinspector/connectionsextension.h000066400000000000000000000040261413310443000253040ustar00rootroot00000000000000/* connectionsextension.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_OBJECTINSPECTOR_CONNECTIONSEXTENSION_H #define GAMMARAY_OBJECTINSPECTOR_CONNECTIONSEXTENSION_H #include "common/tools/objectinspector/connectionsextensioninterface.h" #include "core/propertycontrollerextension.h" namespace GammaRay { class InboundConnectionsModel; class OutboundConnectionsModel; class ConnectionsExtension : public ConnectionsExtensionInterface, public PropertyControllerExtension { Q_OBJECT Q_INTERFACES(GammaRay::ConnectionsExtensionInterface) public: explicit ConnectionsExtension(PropertyController *controller); ~ConnectionsExtension() override; bool setQObject(QObject *object) override; public slots: void navigateToReceiver(int modelRow) override; void navigateToSender(int modelRow) override; private: InboundConnectionsModel *m_inboundModel; OutboundConnectionsModel *m_outboundModel; }; } #endif // GAMMARAY_CONNECTIONSEXTENSION_H gammaray-2.11.3/core/tools/objectinspector/enumsextension.cpp000066400000000000000000000035711413310443000244500ustar00rootroot00000000000000/* enumsextension.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "enumsextension.h" #include "propertycontroller.h" #include "probe.h" #include #include using namespace GammaRay; EnumsExtension::EnumsExtension(PropertyController *controller) : PropertyControllerExtension(controller->objectBaseName() + ".enums") , m_model(new ObjectEnumModel(controller)) { controller->registerModel(m_model, QStringLiteral("enums")); } EnumsExtension::~EnumsExtension() = default; bool EnumsExtension::setQObject(QObject *object) { m_model->setMetaObject(object ? object->metaObject() : nullptr); return m_model->rowCount() > 0; } bool EnumsExtension::setMetaObject(const QMetaObject *metaObject) { m_model->setMetaObject(metaObject); return m_model->rowCount() > 0; } gammaray-2.11.3/core/tools/objectinspector/enumsextension.h000066400000000000000000000032371413310443000241140ustar00rootroot00000000000000/* enumsextension.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_OBJECTINSPECTOR_ENUMSEXTENSION_H #define GAMMARAY_OBJECTINSPECTOR_ENUMSEXTENSION_H #include "propertycontrollerextension.h" namespace GammaRay { class PropertyController; class ObjectEnumModel; class EnumsExtension : public PropertyControllerExtension { public: explicit EnumsExtension(PropertyController *controller); ~EnumsExtension(); bool setQObject(QObject *object) override; bool setMetaObject(const QMetaObject *metaObject) override; private: ObjectEnumModel *m_model; }; } #endif // ENUMSEXTENSION_H gammaray-2.11.3/core/tools/objectinspector/inboundconnectionsmodel.cpp000066400000000000000000000074711413310443000263110ustar00rootroot00000000000000/* inboundconnectionsmodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include "inboundconnectionsmodel.h" #include "core/probe.h" #include using namespace GammaRay; InboundConnectionsModel::InboundConnectionsModel(QObject *parent) : AbstractConnectionsModel(parent) { } InboundConnectionsModel::~InboundConnectionsModel() = default; void InboundConnectionsModel::setObject(QObject *object) { clear(); m_object = object; if (!object) return; setConnections(inboundConnectionsForObject(object)); } QVector InboundConnectionsModel::inboundConnectionsForObject(QObject *object) { QVector connections; QObjectPrivate *d = QObjectPrivate::get(object); #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) QObjectPrivate::ConnectionData *cd = d->connections.load(); if (cd && cd->senders) { auto *senders = cd->senders; #else if (d->senders) { auto *senders = d->senders; #endif for (QObjectPrivate::Connection *s = senders; s; s = s->next) { if (!s->sender || Probe::instance()->filterObject(s->sender)) continue; Connection conn; conn.endpoint = s->sender; conn.signalIndex = signalIndexToMethodIndex(s->sender, s->signal_index); if (s->isSlotObject) { conn.slotIndex = -1; } else { conn.slotIndex = s->method(); } conn.type = s->connectionType; connections.push_back(conn); } } return connections; } QVariant InboundConnectionsModel::data(const QModelIndex &index, int role) const { if (!index.isValid() || !m_object) return QVariant(); if (role == Qt::DisplayRole) { const Connection &conn = m_connections.at(index.row()); switch (index.column()) { case 0: return displayString(conn.endpoint); case 1: return displayString(conn.endpoint, conn.signalIndex); case 2: if (conn.slotIndex < 0) { return tr(""); } return displayString(m_object, conn.slotIndex); } } return AbstractConnectionsModel::data(index, role); } QVariant InboundConnectionsModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { switch (section) { case 0: return tr("Sender"); case 1: return tr("Signal"); case 2: return tr("Slot"); } } return AbstractConnectionsModel::headerData(section, orientation, role); } gammaray-2.11.3/core/tools/objectinspector/inboundconnectionsmodel.h000066400000000000000000000036451413310443000257550ustar00rootroot00000000000000/* inboundconnectionsmodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_OBJECTINSPECTOR_INBOUNDCONNECTIONSMODEL_H #define GAMMARAY_OBJECTINSPECTOR_INBOUNDCONNECTIONSMODEL_H #include "abstractconnectionsmodel.h" namespace GammaRay { /** List of inbound connections on a given QObject. */ class InboundConnectionsModel : public AbstractConnectionsModel { Q_OBJECT public: explicit InboundConnectionsModel(QObject *parent = nullptr); ~InboundConnectionsModel() override; void setObject(QObject *object) override; static QVector inboundConnectionsForObject(QObject *object); QVariant data(const QModelIndex &index, int role) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; }; } #endif // GAMMARAY_INBOUNDCONNECTIONSMODEL_H gammaray-2.11.3/core/tools/objectinspector/methodsextension.cpp000066400000000000000000000142601413310443000247610ustar00rootroot00000000000000/* methodsextension.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "methodsextension.h" #include "objectmethodmodel.h" #include "propertycontroller.h" #include "varianthandler.h" #include "methodargumentmodel.h" #include "multisignalmapper.h" #include "common/objectbroker.h" #include #include #include #include #include using namespace GammaRay; MethodsExtension::MethodsExtension(PropertyController *controller) : MethodsExtensionInterface(controller->objectBaseName() + ".methodsExtension", controller) , PropertyControllerExtension(controller->objectBaseName() + ".methods") , m_model(new ObjectMethodModel(controller)) , m_methodLogModel(new QStandardItemModel(this)) , m_methodArgumentModel(new MethodArgumentModel(this)) , m_signalMapper(nullptr) { controller->registerModel(m_model, QStringLiteral("methods")); controller->registerModel(m_methodLogModel, QStringLiteral("methodLog")); controller->registerModel(m_methodArgumentModel, QStringLiteral("methodArguments")); ObjectBroker::selectionModel(m_model); // trigger creation } MethodsExtension::~MethodsExtension() = default; bool MethodsExtension::setQObject(QObject *object) { if (m_object == object) return true; m_object = object; m_model->setMetaObject(object ? object->metaObject() : nullptr); delete m_signalMapper; m_signalMapper = new MultiSignalMapper(this); connect(m_signalMapper, &MultiSignalMapper::signalEmitted, this, &MethodsExtension::signalEmitted); if (m_methodLogModel->rowCount() > 0) m_methodLogModel->clear(); setHasObject(true); return true; } bool MethodsExtension::setMetaObject(const QMetaObject *metaObject) { m_object = nullptr; m_model->setMetaObject(metaObject); setHasObject(false); return true; } void MethodsExtension::signalEmitted(QObject *sender, int signalIndex, const QVector &args) { Q_ASSERT(m_object == sender); QStringList prettyArgs; prettyArgs.reserve(args.size()); for (const QVariant &v : args) prettyArgs.push_back(VariantHandler::displayString(v)); m_methodLogModel->appendRow( new QStandardItem(tr("%1: Signal %2 emitted, arguments: %3").arg( QTime::currentTime().toString(QStringLiteral("HH:mm:ss.zzz")), QString(sender->metaObject()->method(signalIndex).methodSignature()), prettyArgs.join(QStringLiteral(", "))))); } void MethodsExtension::activateMethod() { QItemSelectionModel *selectionModel = ObjectBroker::selectionModel(m_model); if (selectionModel->selectedRows().size() != 1) return; const QModelIndex index = selectionModel->selectedRows().at(0); const QMetaMethod method = index.data(ObjectMethodModelRole::MetaMethod).value(); m_methodArgumentModel->setMethod(method); } void MethodsExtension::connectToSignal() { QItemSelectionModel *selectionModel = ObjectBroker::selectionModel(m_model); if (selectionModel->selectedRows().size() != 1) return; const QModelIndex index = selectionModel->selectedRows().at(0); const QMetaMethod method = index.data(ObjectMethodModelRole::MetaMethod).value(); if (method.methodType() == QMetaMethod::Signal) m_signalMapper->connectToSignal(m_object, method); } void MethodsExtension::invokeMethod(Qt::ConnectionType connectionType) { if (!m_object) { m_methodLogModel->appendRow( new QStandardItem( tr("%1: Invocation failed: Invalid object, probably got deleted in the meantime."). arg(QTime::currentTime().toString(QStringLiteral("HH:mm:ss.zzz"))))); return; } QMetaMethod method; QItemSelectionModel *selectionModel = ObjectBroker::selectionModel(m_model); if (selectionModel->selectedRows().size() == 1) { const QModelIndex index = selectionModel->selectedRows().at(0); method = index.data(ObjectMethodModelRole::MetaMethod).value(); } if (method.methodType() == QMetaMethod::Constructor) { m_methodLogModel->appendRow( new QStandardItem( tr("%1: Invocation failed: Can't invoke constructors."). arg(QTime::currentTime().toString(QStringLiteral("HH:mm:ss.zzz"))))); return; } const QVector args = m_methodArgumentModel->arguments(); // TODO retrieve return value and add it to the log in case of success // TODO measure execution time and that to the log const bool result = method.invoke( m_object.data(), connectionType, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9]); if (!result) { m_methodLogModel->appendRow( new QStandardItem( tr("%1: Invocation failed.."). arg(QTime::currentTime().toString(QStringLiteral("HH:mm:ss.zzz"))))); return; } m_methodArgumentModel->setMethod(QMetaMethod()); } gammaray-2.11.3/core/tools/objectinspector/methodsextension.h000066400000000000000000000046261413310443000244330ustar00rootroot00000000000000/* methodsextension.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_OBJECTINSPECTOR_METHODSEXTENSION_H #define GAMMARAY_OBJECTINSPECTOR_METHODSEXTENSION_H #include "common/tools/objectinspector/methodsextensioninterface.h" #include "core/propertycontrollerextension.h" #include QT_BEGIN_NAMESPACE class QStandardItemModel; QT_END_NAMESPACE namespace GammaRay { class PropertyController; class ObjectMethodModel; class MethodArgumentModel; class MultiSignalMapper; class MethodsExtension : public MethodsExtensionInterface, public PropertyControllerExtension { Q_OBJECT Q_INTERFACES(GammaRay::MethodsExtensionInterface) public: explicit MethodsExtension(PropertyController *controller); ~MethodsExtension() override; bool setQObject(QObject *object) override; bool setMetaObject(const QMetaObject *metaObject) override; public slots: void activateMethod() override; void invokeMethod(Qt::ConnectionType connectionType) override; void connectToSignal() override; private slots: void signalEmitted(QObject *sender, int signalIndex, const QVector &args); private: ObjectMethodModel *m_model; QStandardItemModel *m_methodLogModel; MethodArgumentModel *m_methodArgumentModel; MultiSignalMapper *m_signalMapper; QPointer m_object; }; } #endif // METHODSEXTENSION_H gammaray-2.11.3/core/tools/objectinspector/objectinspector.cpp000066400000000000000000000277461413310443000245730ustar00rootroot00000000000000/* objectinspector.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "objectinspector.h" #include "propertycontroller.h" #include "methodsextension.h" #include "classinfoextension.h" #include "enumsextension.h" #include "propertiesextension.h" #include "connectionsextension.h" #include "applicationattributeextension.h" #include "bindingextension.h" #include "stacktraceextension.h" #include "inboundconnectionsmodel.h" #include "outboundconnectionsmodel.h" #include "objectdataprovider.h" #include #include #include #include #include #include #include <3rdparty/kde/krecursivefilterproxymodel.h> #include #include #include #include #include using namespace GammaRay; ObjectInspector::ObjectInspector(Probe *probe, QObject *parent) : QObject(parent) { registerPCExtensions(); m_propertyController = new PropertyController(QStringLiteral( "com.kdab.GammaRay.ObjectInspector"), this); auto proxy = new ServerProxyModel(this); proxy->setSourceModel(probe->objectTreeModel()); probe->registerModel(QStringLiteral("com.kdab.GammaRay.ObjectInspectorTree"), proxy); m_selectionModel = ObjectBroker::selectionModel(proxy); connect(m_selectionModel, &QItemSelectionModel::selectionChanged, this, &ObjectInspector::objectSelectionChanged); connect(probe, &Probe::objectSelected, this, &ObjectInspector::objectSelected); ProblemCollector::registerProblemChecker("com.kdab.GammaRay.ObjectInspector.BindingLoopScan", "Binding Loops", "Scans all QObjects for binding loops", &BindingAggregator::scanForBindingLoops); ProblemCollector::registerProblemChecker("com.kdab.GammaRay.ObjectInspector.ConnectionsCheck", "Connection issues", "Scans all QObjects for direct cross-thread and duplicate connections", &ObjectInspector::scanForConnectionIssues); ProblemCollector::registerProblemChecker("com.kdab.GammaRay.ObjectInspector.ThreadAffinityCheck", "Threading issues", "Scans all QObjects for thread affinity issues", &ObjectInspector::scanForThreadAffinityIssues); } void ObjectInspector::objectSelectionChanged(const QItemSelection &selection) { if (selection.isEmpty()) modelIndexSelected(QModelIndex()); else modelIndexSelected(selection.first().topLeft()); } void ObjectInspector::modelIndexSelected(const QModelIndex &index) { if (index.isValid()) { QObject *obj = index.data(ObjectModel::ObjectRole).value(); m_propertyController->setObject(obj); } else { m_propertyController->setObject(nullptr); } } void ObjectInspector::objectSelected(QObject *object) { const QAbstractItemModel *model = m_selectionModel->model(); const QModelIndexList indexList = model->match(model->index(0, 0), ObjectModel::ObjectRole, QVariant::fromValue(object), 1, Qt::MatchExactly | Qt::MatchRecursive | Qt::MatchWrap); if (indexList.isEmpty()) return; const QModelIndex index = indexList.first(); m_selectionModel->select( index, QItemSelectionModel::Select | QItemSelectionModel::Clear |QItemSelectionModel::Rows | QItemSelectionModel::Current); // TODO: move this to the client side! // ui->objectTreeView->scrollTo(index); modelIndexSelected(index); } void ObjectInspector::registerPCExtensions() { PropertyController::registerExtension(); PropertyController::registerExtension(); PropertyController::registerExtension(); PropertyController::registerExtension(); PropertyController::registerExtension(); PropertyController::registerExtension(); PropertyController::registerExtension(); PropertyController::registerExtension(); } QVector GammaRay::ObjectInspectorFactory::selectableTypes() const { return QVector() << QObject::staticMetaObject.className(); } void ObjectInspector::scanForConnectionIssues() { const QVector &allObjects = Probe::instance()->allQObjects(); QMutexLocker lock(Probe::objectLock()); for (QObject *obj : allObjects) { if (!Probe::instance()->isValidObject(obj)) continue; auto reportProblem = [obj](const AbstractConnectionsModel::Connection &connection, const QString &descriptionTemplate, const QString &problemType, bool isOutbound) { QObject *sender = isOutbound ? obj : connection.endpoint.data(); QObject *receiver = isOutbound ? connection.endpoint.data() : obj; if (!sender || !receiver) { return; } QString signalName = sender->metaObject()->method(connection.signalIndex).name(); QString slotName = connection.slotIndex < 0 ? QStringLiteral("") : receiver->metaObject()->method(connection.slotIndex).name(); QString senderName = Util::displayString(sender); QString receiverName = Util::displayString(receiver); Problem p; p.severity = Problem::Warning; p.description = descriptionTemplate.arg(receiverName, slotName, senderName, signalName); p.object = ObjectId(receiver); // p.location = bindingNode->sourceLocation(); //TODO can we get source locations of connect-statements? p.problemId = QString("com.kdab.GammaRay.ObjectInspector.ConnectionsCheck.%1:%2.%3-%4.%5") .arg(problemType, QString::number(reinterpret_cast(sender)), QString::number(connection.signalIndex), QString::number(reinterpret_cast(receiver)), QString::number(connection.slotIndex)); p.findingCategory = Problem::Scan; ProblemCollector::addProblem(p); }; auto connections = InboundConnectionsModel::inboundConnectionsForObject(obj); for (auto it = connections.begin(); it != connections.end(); ++it) { auto &&connection = *it; if (AbstractConnectionsModel::isDuplicate(connections, connection)) { reportProblem(connection, QStringLiteral("The slot %1->%2 is connected to the signal %3->%4 multiple times."), QStringLiteral("Duplicate"), false); } if (AbstractConnectionsModel::isDirectCrossThreadConnection(obj, connection)) { reportProblem(connection, QStringLiteral("The connection of slot %1->%2 to the signal %3->%4 is a direct cross-thread connection."), QStringLiteral("CrossTread"), false); } } connections = OutboundConnectionsModel::outboundConnectionsForObject(obj); for (auto it = connections.begin(); it != connections.end(); ++it) { auto &&connection = *it; if (AbstractConnectionsModel::isDuplicate(connections, connection)) { reportProblem(connection, QStringLiteral("The slot %1->%2 is connected to the signal %3->%4 multiple times."), QStringLiteral("Duplicate"), true); } if (AbstractConnectionsModel::isDirectCrossThreadConnection(obj, connection)) { reportProblem(connection, QStringLiteral("The connection of slot %1->%2 to the signal %3->%4 is a direct cross-thread connection."), QStringLiteral("CrossTread"), true); } } } } void ObjectInspector::scanForThreadAffinityIssues() { const auto probe = Probe::instance(); const auto &objects = probe->allQObjects(); QMutexLocker lock(Probe::objectLock()); for (const auto object : objects) { if (!probe->isValidObject(object)) { continue; } const auto objectName = Util::displayString(object); if (object == object->thread()) { Problem problem; problem.severity = Problem::Warning; problem.description = QStringLiteral("The thread %1 has affinity with itself.").arg(objectName); problem.object = ObjectId(object); problem.locations.append(probe->objectCreationSourceLocation(object)); problem.problemId = QStringLiteral("com.kdab.GammaRay.ObjectInspector.ThreadAffinityCheck.Self.%1") .arg(QString::number(reinterpret_cast(object))); problem.findingCategory = Problem::Scan; ProblemCollector::addProblem(problem); } const auto parent = object->parent(); if (parent == nullptr) { continue; } const auto parentName = Util::displayString(parent); if (object->thread() != parent->thread()) { Problem problem; problem.severity = Problem::Warning; problem.description = QStringLiteral("The object %1 doesn't have the same thread affinity as its parent %2.").arg(objectName, parentName); problem.object = ObjectId(object); problem.locations.append(probe->objectCreationSourceLocation(object)); problem.problemId = QStringLiteral("com.kdab.GammaRay.ObjectInspector.ThreadAffinityCheck.%1:%2") .arg(QString::number(reinterpret_cast(object)), QString::number(reinterpret_cast(parent))); problem.findingCategory = Problem::Scan; ProblemCollector::addProblem(problem); } if (qobject_cast(parent) && object->thread() != object->parent()) { Problem problem; problem.severity = Problem::Warning; problem.description = QStringLiteral("The object %1 has thread %2 as parent, but doesn't have affinity with it.").arg(objectName, parentName); problem.object = ObjectId(object); problem.locations.append(probe->objectCreationSourceLocation(object)); problem.problemId = QStringLiteral("com.kdab.GammaRay.ObjectInspector.ThreadAffinityCheck.Parent.%1") .arg(QString::number(reinterpret_cast(object)), QString::number(reinterpret_cast(parent))); problem.findingCategory = Problem::Scan; ProblemCollector::addProblem(problem); } } } gammaray-2.11.3/core/tools/objectinspector/objectinspector.h000066400000000000000000000044431413310443000242250ustar00rootroot00000000000000/* objectinspector.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_OBJECTINSPECTOR_OBJECTINSPECTOR_H #define GAMMARAY_OBJECTINSPECTOR_OBJECTINSPECTOR_H #include "toolfactory.h" #include QT_BEGIN_NAMESPACE class QItemSelection; class QItemSelectionModel; class QModelIndex; QT_END_NAMESPACE namespace GammaRay { class PropertyController; class ObjectInspector : public QObject { Q_OBJECT public: explicit ObjectInspector(Probe *probe, QObject *parent = nullptr); private slots: void modelIndexSelected(const QModelIndex &index); void objectSelectionChanged(const QItemSelection &selection); void objectSelected(QObject *object); private: void registerPCExtensions(); static void scanForConnectionIssues(); static void scanForThreadAffinityIssues(); PropertyController *m_propertyController; QItemSelectionModel *m_selectionModel; }; class ObjectInspectorFactory : public QObject, public StandardToolFactory { Q_OBJECT Q_INTERFACES(GammaRay::ToolFactory) public: explicit ObjectInspectorFactory(QObject *parent) : QObject(parent) { } QVector selectableTypes() const override; }; } #endif // GAMMARAY_OBJECTINSPECTOR_H gammaray-2.11.3/core/tools/objectinspector/outboundconnectionsmodel.cpp000066400000000000000000000103341413310443000265020ustar00rootroot00000000000000/* outboundconnectionmodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include "outboundconnectionsmodel.h" #include "core/probe.h" #include using namespace GammaRay; OutboundConnectionsModel::OutboundConnectionsModel(QObject *parent) : AbstractConnectionsModel(parent) { } OutboundConnectionsModel::~OutboundConnectionsModel() = default; void OutboundConnectionsModel::setObject(QObject *object) { clear(); m_object = object; if (!object) { return; } setConnections(outboundConnectionsForObject(object)); } QVector OutboundConnectionsModel::outboundConnectionsForObject(QObject *object) { QVector connections; QObjectPrivate *d = QObjectPrivate::get(object); #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) QObjectPrivate::ConnectionData *cd = d->connections.load(); if (!cd) return connections; auto cl = cd->signalVector.load(); #else if (!d->connectionLists) return connections; // HACK: the declaration of d->connectionsLists is not accessible for us... const auto cl = reinterpret_cast *>(d->connectionLists); #endif if (!cl) return connections; for (int signalIndex = 0; signalIndex < cl->count(); ++signalIndex) { const QObjectPrivate::Connection *c = cl->at(signalIndex).first; while (c) { if (!c->receiver || Probe::instance()->filterObject(c->receiver)) { c = c->nextConnectionList; continue; } Connection conn; conn.endpoint = c->receiver; conn.signalIndex = signalIndexToMethodIndex(object, signalIndex); if (c->isSlotObject) conn.slotIndex = -1; else conn.slotIndex = c->method(); conn.type = c->connectionType; c = c->nextConnectionList; connections.push_back(conn); } } return connections; } QVariant OutboundConnectionsModel::data(const QModelIndex &index, int role) const { if (!index.isValid() || !m_object) return QVariant(); if (role == Qt::DisplayRole) { const Connection &conn = m_connections.at(index.row()); switch (index.column()) { case 0: return displayString(m_object, conn.signalIndex); case 1: return displayString(conn.endpoint); case 2: if (conn.slotIndex < 0) { return tr(""); } return displayString(conn.endpoint, conn.slotIndex); } } return AbstractConnectionsModel::data(index, role); } QVariant OutboundConnectionsModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { switch (section) { case 0: return tr("Signal"); case 1: return tr("Receiver"); case 2: return tr("Slot"); } } return AbstractConnectionsModel::headerData(section, orientation, role); } gammaray-2.11.3/core/tools/objectinspector/outboundconnectionsmodel.h000066400000000000000000000036551413310443000261570ustar00rootroot00000000000000/* outboundconnectionmodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_OBJECTINSPECTOR_OUTBOUNDCONNECTIONSMODEL_H #define GAMMARAY_OBJECTINSPECTOR_OUTBOUNDCONNECTIONSMODEL_H #include "abstractconnectionsmodel.h" namespace GammaRay { /** Lists outgoing connections from a given QObject. */ class OutboundConnectionsModel : public AbstractConnectionsModel { Q_OBJECT public: explicit OutboundConnectionsModel(QObject *parent = nullptr); ~OutboundConnectionsModel() override; void setObject(QObject *object) override; static QVector outboundConnectionsForObject(QObject *object); QVariant data(const QModelIndex &index, int role) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; }; } #endif // GAMMARAY_OUTBOUNDCONNECTIONMODEL_H gammaray-2.11.3/core/tools/objectinspector/propertiesextension.cpp000066400000000000000000000054421413310443000255140ustar00rootroot00000000000000/* propertiesextension.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "propertiesextension.h" #include "aggregatedpropertymodel.h" #include "propertycontroller.h" #include "objectinstance.h" #include #include #include using namespace GammaRay; PropertiesExtension::PropertiesExtension(PropertyController *controller) : PropertiesExtensionInterface(controller->objectBaseName() + ".propertiesExtension", controller) , PropertyControllerExtension(controller->objectBaseName() + ".properties") , m_aggregatedPropertyModel(new AggregatedPropertyModel(this)) { controller->registerModel(m_aggregatedPropertyModel, QStringLiteral("properties")); } PropertiesExtension::~PropertiesExtension() = default; bool PropertiesExtension::setQObject(QObject *object) { if (m_object == object) return true; m_object = object; m_aggregatedPropertyModel->setObject(object); setCanAddProperty(true); setHasPropertyValues(true); return true; } bool PropertiesExtension::setObject(void *object, const QString &typeName) { m_object = nullptr; m_aggregatedPropertyModel->setObject(ObjectInstance(object, typeName.toUtf8())); setCanAddProperty(false); setHasPropertyValues(true); return true; } bool PropertiesExtension::setMetaObject(const QMetaObject *metaObject) { m_object = nullptr; m_aggregatedPropertyModel->setObject(ObjectInstance(nullptr, metaObject)); setCanAddProperty(false); setHasPropertyValues(false); return true; } void PropertiesExtension::setProperty(const QString &name, const QVariant &value) { if (!m_object) return; m_object->setProperty(name.toUtf8(), value); } gammaray-2.11.3/core/tools/objectinspector/propertiesextension.h000066400000000000000000000042561413310443000251630ustar00rootroot00000000000000/* propertiesextension.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_OBJECTINSPECTOR_PROPERTIESEXTENSION_H #define GAMMARAY_OBJECTINSPECTOR_PROPERTIESEXTENSION_H #include "common/tools/objectinspector/propertiesextensioninterface.h" #include "core/propertycontrollerextension.h" #include namespace GammaRay { class PropertyController; class ObjectDynamicPropertyModel; class ObjectStaticPropertyModel; class AggregatedPropertyModel; class PropertiesExtension : public PropertiesExtensionInterface, public PropertyControllerExtension { Q_OBJECT Q_INTERFACES(GammaRay::PropertiesExtensionInterface) public: explicit PropertiesExtension(PropertyController *controller); ~PropertiesExtension() override; void setProperty(const QString &name, const QVariant &value) override; bool setObject(void *object, const QString &typeName) override; bool setQObject(QObject *object) override; bool setMetaObject(const QMetaObject *metaObject) override; private: AggregatedPropertyModel *m_aggregatedPropertyModel; QPointer m_object; }; } #endif // PROPERTIESEXTENSION_H gammaray-2.11.3/core/tools/objectinspector/stacktraceextension.cpp000066400000000000000000000034721413310443000254450ustar00rootroot00000000000000/* stacktraceextension.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "stacktraceextension.h" #include "stacktracemodel.h" #include #include #include using namespace GammaRay; StackTraceExtension::StackTraceExtension(PropertyController* controller) : PropertyControllerExtension(controller->objectBaseName() + ".stackTrace") , m_model(new StackTraceModel(controller)) { controller->registerModel(m_model, QStringLiteral("stackTraceModel")); } StackTraceExtension::~StackTraceExtension() = default; bool StackTraceExtension::setQObject(QObject *object) { const auto trace = Probe::instance()->objectCreationStackTrace(object); m_model->setStackTrace(trace); return !trace.empty(); } gammaray-2.11.3/core/tools/objectinspector/stacktraceextension.h000066400000000000000000000031221413310443000251020ustar00rootroot00000000000000/* stacktraceextension.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_STACKTRACEEXTENSION_H #define GAMMARAY_STACKTRACEEXTENSION_H #include namespace GammaRay { class StackTraceModel; class StackTraceExtension : public PropertyControllerExtension { public: explicit StackTraceExtension(PropertyController *controller); ~StackTraceExtension(); bool setQObject(QObject *object) override; private: StackTraceModel *m_model; }; } #endif // GAMMARAY_STACKTRACEEXTENSION_H gammaray-2.11.3/core/tools/problemreporter/000077500000000000000000000000001413310443000207005ustar00rootroot00000000000000gammaray-2.11.3/core/tools/problemreporter/availablecheckersmodel.cpp000066400000000000000000000065361413310443000260670ustar00rootroot00000000000000/* availablecheckers.cpp This file is part of GammaRay, the Qt application inspection and manipulation problem. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "availablecheckersmodel.h" using namespace GammaRay; AvailableCheckersModel::AvailableCheckersModel(QObject *parent) : QAbstractListModel(parent) , m_availableCheckers(&ProblemCollector::instance()->availableCheckers()) { connect(ProblemCollector::instance(), &ProblemCollector::aboutToAddChecker, this, &AvailableCheckersModel::aboutToAddChecker); connect(ProblemCollector::instance(), &ProblemCollector::checkerAdded, this, &AvailableCheckersModel::checkerAdded); } QVariant AvailableCheckersModel::data(const QModelIndex & index, int role) const { if (!index.isValid() || index.column() != 0 || index.row() < 0 || index.row() >= m_availableCheckers->size()) { return QVariant(); } const ProblemCollector::Checker &checker = m_availableCheckers->at(index.row()); if (role == Qt::DisplayRole) { return checker.name; } if (role == Qt::ToolTipRole) { return checker.description; } if (role == Qt::EditRole) { return checker.id; } if (role == Qt::CheckStateRole) { return checker.enabled ? Qt::Checked : Qt::Unchecked; } return QVariant(); } bool GammaRay::AvailableCheckersModel::setData(const QModelIndex& index, const QVariant& value, int role) { if (!index.isValid() || index.column() != 0 || index.row() < 0 || index.row() >= m_availableCheckers->size() || role != Qt::CheckStateRole || !value.canConvert()) { return false; } (*m_availableCheckers)[index.row()].enabled = value.toBool(); emit dataChanged(index, index); return true; } Qt::ItemFlags AvailableCheckersModel::flags(const QModelIndex &index) const { const Qt::ItemFlags flags = QAbstractListModel::flags(index); if (index.column() == 0) return flags | Qt::ItemIsUserCheckable; return flags; } int AvailableCheckersModel::rowCount(const QModelIndex & parent) const { if (parent.isValid()) { return 0; } return m_availableCheckers->size(); } void GammaRay::AvailableCheckersModel::aboutToAddChecker() { beginInsertRows(QModelIndex(), m_availableCheckers->size(), m_availableCheckers->size()); } void GammaRay::AvailableCheckersModel::checkerAdded() { endInsertRows(); } gammaray-2.11.3/core/tools/problemreporter/availablecheckersmodel.h000066400000000000000000000036671413310443000255360ustar00rootroot00000000000000/* availablecheckersmodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2018-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_AVAILABLECHECKERSMODEL_H #define GAMMARAY_AVAILABLECHECKERSMODEL_H #include #include #include namespace GammaRay { class AvailableCheckersModel : public QAbstractListModel { Q_OBJECT public: explicit AvailableCheckersModel(QObject *parent); QVariant data(const QModelIndex & index, int role) const override; int rowCount(const QModelIndex & parent) const override; bool setData(const QModelIndex & index, const QVariant & value, int role) override; Qt::ItemFlags flags(const QModelIndex & index) const override; private slots: void aboutToAddChecker(); void checkerAdded(); private: QVector *m_availableCheckers; }; } #endif // GAMMARAY_AVAILABLECHECKERSMODEL_H gammaray-2.11.3/core/tools/problemreporter/problemmodel.cpp000066400000000000000000000114701413310443000240700ustar00rootroot00000000000000/* problemmodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation problem. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "problemmodel.h" #include "../../problemcollector.h" #include #include using namespace GammaRay; ProblemModel::ProblemModel(QObject *parent) : QAbstractListModel(parent) , m_problemCollector(ProblemCollector::instance()) { connect(m_problemCollector, &ProblemCollector::aboutToAddProblem, this, &ProblemModel::aboutToAddProblem); connect(m_problemCollector, &ProblemCollector::problemAdded, this, &ProblemModel::problemAdded); connect(m_problemCollector, &ProblemCollector::aboutToRemoveProblems, this, &ProblemModel::aboutToRemoveProblems); connect(m_problemCollector, &ProblemCollector::problemsRemoved, this, &ProblemModel::problemsRemoved); } ProblemModel::~ProblemModel() = default; QVariant ProblemModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); const Problem &problem = m_problemCollector->problems().at(index.row()); switch (role) { case Qt::DisplayRole: switch (index.column()) { case 0: return problem.description; case 1: return problem.locations.size() ? problem.locations.front().displayString() : QString(); } break; case ObjectModel::ObjectIdRole: return QVariant::fromValue(problem.object); case ProblemModelRoles::SourceLocationRole: return QVariant::fromValue(problem.locations); case ProblemModelRoles::SeverityRole: return static_cast(problem.severity); case ProblemModelRoles::ProblemIdRole: return problem.problemId; } return QVariant(); } QMap ProblemModel::itemData(const QModelIndex &index) const { QMap d = QAbstractItemModel::itemData(index); d.insert(ObjectModel::ObjectIdRole, data(index, ObjectModel::ObjectIdRole)); d.insert(ProblemModelRoles::SourceLocationRole, data(index, ProblemModelRoles::SourceLocationRole)); d.insert(ProblemModelRoles::SeverityRole, data(index, ProblemModelRoles::SeverityRole)); d.insert(ProblemModelRoles::ProblemIdRole, data(index, ProblemModelRoles::ProblemIdRole)); return d; } int ProblemModel::rowCount(const QModelIndex &parent) const { if (parent.isValid()) return 0; return m_problemCollector->problems().count(); } int ProblemModel::columnCount(const QModelIndex &parent) const { if (parent.isValid()) return 0; return 2; } void GammaRay::ProblemModel::aboutToAddProblem(int row) { beginInsertRows(QModelIndex(), row, row); } void GammaRay::ProblemModel::aboutToRemoveProblems(int row, int count) { beginRemoveRows(QModelIndex(), row, row + count - 1); } void GammaRay::ProblemModel::problemAdded() { endInsertRows(); } void GammaRay::ProblemModel::problemsRemoved() { endRemoveRows(); } /* ClientProblemSelectionModel::ClientProblemSelectionModel(ProblemCollector *manager) : QItemSelectionModel(manager->model()) , m_problemCollector(manager) { connect(manager, SIGNAL(problemSelectedByIndex(int)), this, SLOT(selectProblem(int))); connect(manager, SIGNAL(problemListAvailable()), this, SLOT(selectDefaultProblem())); } ClientProblemSelectionModel::~ClientProblemSelectionModel() { } void ClientProblemSelectionModel::selectProblem(int index) { select(model()->index(index, 0), QItemSelectionModel::Select | QItemSelectionModel::Clear | QItemSelectionModel::Rows | QItemSelectionModel::Current); } void ClientProblemSelectionModel::selectDefaultProblem() { selectProblem(m_problemCollector->problemIndexForProblemId(QStringLiteral("GammaRay::ObjectInspector"))); }*/ gammaray-2.11.3/core/tools/problemreporter/problemmodel.h000066400000000000000000000047271413310443000235440ustar00rootroot00000000000000/* problemmodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2018-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_PROBLEMMODEL_H #define GAMMARAY_PROBLEMMODEL_H #include #include #include namespace GammaRay { class ProblemCollector; /*! Model of all selectable client tools. */ class ProblemModel : public QAbstractListModel { Q_OBJECT public: explicit ProblemModel(QObject *parent); ~ProblemModel() override; QVariant data(const QModelIndex &index, int role) const override; QMap itemData(const QModelIndex &index) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; int columnCount(const QModelIndex &parent) const override; private slots: void aboutToAddProblem(int row); void problemAdded(); void aboutToRemoveProblems(int row, int count = 1); void problemsRemoved(); private: ProblemCollector *m_problemCollector; }; /*! Selection model that automatically syncs ClientToolModel with ClientToolManager. */ // class ClientToolSelectionModel : public QItemSelectionModel // { // Q_OBJECT // public: // explicit ClientToolSelectionModel(ClientToolManager *manager); // ~ClientToolSelectionModel(); // // private slots: // void selectTool(int index); // void selectDefaultTool(); // // private: // ClientToolManager *m_toolManager; // }; } #endif // GAMMARAY_PROBLEMMODEL_H gammaray-2.11.3/core/tools/problemreporter/problemreporter.cpp000066400000000000000000000036511413310443000246340ustar00rootroot00000000000000/* problemreporter.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2018-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "problemreporter.h" #include "problemmodel.h" #include "availablecheckersmodel.h" #include using namespace GammaRay; ProblemReporter::ProblemReporter(Probe *probe, QObject *parent) : ProblemReporterInterface(parent) , m_problemModel(new ProblemModel(this)) { probe->registerModel(QStringLiteral("com.kdab.GammaRay.ProblemModel"), m_problemModel); probe->registerModel(QStringLiteral("com.kdab.GammaRay.AvailableProblemCheckersModel"), new AvailableCheckersModel(this)); connect(ProblemCollector::instance(), &ProblemCollector::problemScansFinished, this, &ProblemReporterInterface::problemScansFinished); } ProblemReporter::~ProblemReporter() = default; void GammaRay::ProblemReporter::requestScan() { ProblemCollector::instance()->requestScan(); } gammaray-2.11.3/core/tools/problemreporter/problemreporter.h000066400000000000000000000037321413310443000243010ustar00rootroot00000000000000/* problemreporter.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2018-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_PROBLEMREPORTER_PROBLEMREPORTER_H #define GAMMARAY_PROBLEMREPORTER_PROBLEMREPORTER_H #include #include namespace GammaRay { class ProblemModel; class ProblemReporter : public ProblemReporterInterface { Q_OBJECT Q_INTERFACES(GammaRay::ProblemReporterInterface) public: explicit ProblemReporter(Probe *probe, QObject *parent = nullptr); ~ProblemReporter() override; public slots: void requestScan() override; private: ProblemModel *m_problemModel; }; class ProblemReporterFactory : public QObject, public StandardToolFactory { Q_OBJECT Q_INTERFACES(GammaRay::ToolFactory) public: explicit ProblemReporterFactory(QObject *parent) : QObject(parent) { } }; } #endif // PROBLEMREPORTER_H gammaray-2.11.3/core/tools/resourcebrowser/000077500000000000000000000000001413310443000207105ustar00rootroot00000000000000gammaray-2.11.3/core/tools/resourcebrowser/resourcebrowser.cpp000066400000000000000000000077541413310443000246640ustar00rootroot00000000000000/* resourcebrowser.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Stephen Kelly Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "resourcebrowser.h" #include "resourcefiltermodel.h" #include "qt/resourcemodel.h" #include "common/objectbroker.h" #include #include #include #include using namespace GammaRay; ResourceBrowser::ResourceBrowser(Probe *probe, QObject *parent) : ResourceBrowserInterface(parent) { auto *resourceModel = new ResourceModel(this); auto proxy = new ServerProxyModel(this); proxy->setSourceModel(resourceModel); probe->registerModel(QStringLiteral("com.kdab.GammaRay.ResourceModel"), proxy); QItemSelectionModel *selectionModel = ObjectBroker::selectionModel(proxy); connect(selectionModel, &QItemSelectionModel::currentChanged, this, [this](const QModelIndex &index) { currentChanged(index); }); } void ResourceBrowser::downloadResource(const QString &sourceFilePath, const QString &targetFilePath) { const QFileInfo fi(sourceFilePath); if (fi.isFile()) { QFile f(fi.absoluteFilePath()); if (f.open(QFile::ReadOnly)) emit resourceDownloaded(targetFilePath, f.readAll()); else qWarning() << "Failed to open" << fi.absoluteFilePath(); } } void ResourceBrowser::selectResource(const QString &sourceFilePath, int line, int column) { const bool locked = blockSignals(true); const QItemSelectionModel::SelectionFlags selectionFlags = QItemSelectionModel::ClearAndSelect |QItemSelectionModel::Rows | QItemSelectionModel::Current; const Qt::MatchFlags matchFlags = Qt::MatchExactly | Qt::MatchRecursive | Qt::MatchWrap; auto model = ObjectBroker::model(QStringLiteral("com.kdab.GammaRay.ResourceModel")); auto selectionModel = ObjectBroker::selectionModel(model); const QString filePath = QLatin1Char(':') + QUrl(sourceFilePath).path(); const QModelIndex index = model->match(model->index(0, 0), ResourceModel::FilePathRole, filePath, 1, matchFlags).value(0); selectionModel->setCurrentIndex(index, selectionFlags); blockSignals(locked); currentChanged(index, line, column); } void ResourceBrowser::currentChanged(const QModelIndex ¤t, int line, int column) { if (!current.isValid()) return; const auto idx = current.sibling(current.row(), 0); const QFileInfo fi(idx.data(ResourceModel::FilePathRole).toString()); if (!fi.isFile()) { emit resourceDeselected(); return; } QFile f(fi.absoluteFilePath()); if (f.open(QFile::ReadOnly)) { emit resourceSelected(f.readAll(), line, column); } else { qWarning() << "Failed to open" << fi.absoluteFilePath(); emit resourceDeselected(); } } gammaray-2.11.3/core/tools/resourcebrowser/resourcebrowser.h000066400000000000000000000043471413310443000243240ustar00rootroot00000000000000/* resourcebrowser.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Stephen Kelly Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_RESOURCEBROWSER_RESOURCEBROWSER_H #define GAMMARAY_RESOURCEBROWSER_RESOURCEBROWSER_H #include "toolfactory.h" #include QT_BEGIN_NAMESPACE class QModelIndex; QT_END_NAMESPACE namespace GammaRay { class ResourceBrowser : public ResourceBrowserInterface { Q_OBJECT Q_INTERFACES(GammaRay::ResourceBrowserInterface) public: explicit ResourceBrowser(Probe *probe, QObject *parent = nullptr); public slots: void downloadResource(const QString &sourceFilePath, const QString &targetFilePath) override; void selectResource(const QString &sourceFilePath, int line = -1, int column = -1) override; private slots: void currentChanged(const QModelIndex ¤t, int line = -1, int column = -1); }; class ResourceBrowserFactory : public QObject, public StandardToolFactory { Q_OBJECT Q_INTERFACES(GammaRay::ToolFactory) public: explicit ResourceBrowserFactory(QObject *parent) : QObject(parent) { } }; } #endif // GAMMARAY_RESOURCEBROWSER_H gammaray-2.11.3/core/tools/resourcebrowser/resourcefiltermodel.cpp000066400000000000000000000034361413310443000255000ustar00rootroot00000000000000/* resourcefiltermodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "resourcefiltermodel.h" #include "qt/resourcemodel.h" #include using namespace GammaRay; ResourceFilterModel::ResourceFilterModel(QObject *parent) : KRecursiveFilterProxyModel(parent) { } bool ResourceFilterModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const { const QModelIndex index = sourceModel()->index(source_row, 0, source_parent); const QString path = index.data(ResourceModel::FilePathRole).toString(); if (path == QLatin1String(":/gammaray") || path.startsWith(QLatin1String(":/gammaray/"))) return false; return KRecursiveFilterProxyModel::filterAcceptsRow(source_row, source_parent); } gammaray-2.11.3/core/tools/resourcebrowser/resourcefiltermodel.h000066400000000000000000000031071413310443000251400ustar00rootroot00000000000000/* resourcefiltermodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_RESOURCEBROWSER_RESOURCEFILTERMODEL_H #define GAMMARAY_RESOURCEBROWSER_RESOURCEFILTERMODEL_H #include <3rdparty/kde/krecursivefilterproxymodel.h> namespace GammaRay { class ResourceFilterModel : public KRecursiveFilterProxyModel { Q_OBJECT public: explicit ResourceFilterModel(QObject *parent = nullptr); bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override; }; } #endif // RESOURCEFILTERMODEL_H gammaray-2.11.3/core/typetraits.h000066400000000000000000000034741413310443000167060ustar00rootroot00000000000000/* typetraits.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_TYPETRAITS_H #define GAMMARAY_TYPETRAITS_H #include namespace GammaRay { /** Dynamic casting trait for supporting dynamic_cast-like functions for pseudo-polymorphic Qt types. * The generic implementation in here just does dynamic_cast, this needs to be specialized where needed. */ template Out DynamicCast(In *in) { return dynamic_cast(in); } ///@cond internal template Out DynamicCast(void*) { return nullptr; } ///@endcond /** Specializable variant of std::is_polymorphic, for use with pseudo-polymorphic Qt types. */ template bool IsPolymorphic() { return std::is_polymorphic::value; } } #endif gammaray-2.11.3/core/util.cpp000066400000000000000000000215471413310443000160070ustar00rootroot00000000000000/* util.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include "util.h" #include "common/classesiconsrepository.h" #include "common/metatypedeclarations.h" #include "common/objectbroker.h" #include "varianthandler.h" #include "objectdataprovider.h" #include "enumutil.h" #include #include #include #include #include #include #include #include #include #include #include using namespace GammaRay; using namespace std; QString Util::displayString(const QObject *object) { if (!object) return QStringLiteral("QObject(0x0)"); const auto name = ObjectDataProvider::name(object); if (name.isEmpty()) return QStringLiteral("%1[this=%2]").arg(object->metaObject()->className(), addressToString(object)); return name; } QString Util::shortDisplayString(const QObject *object) { if (!object) return QStringLiteral("0x0"); const auto name = ObjectDataProvider::name(object); if (name.isEmpty()) return addressToString(object); return name; } QString Util::addressToString(const void *p) { char buf[20]; qsnprintf(buf, sizeof(buf), "0x%llx", reinterpret_cast(p)); return QString::fromLatin1(buf); } QString Util::enumToString(const QVariant &value, const char *typeName, const QObject *object) { return EnumUtil::enumToString(value, typeName, object ? object->metaObject() : nullptr); } QString Util::prettyMethodSignature(const QMetaMethod &method) { QString signature = method.typeName(); signature += ' ' + method.name() + '('; QStringList args; args.reserve(method.parameterCount()); const QList paramTypes = method.parameterTypes(); const QList paramNames = method.parameterNames(); for (int i = 0; i < method.parameterCount(); ++i) { QString arg = paramTypes.at(i); if (!paramNames.at(i).isEmpty()) arg += ' ' + paramNames.at(i); args.push_back(arg); } signature += args.join(QStringLiteral(", ")) + ')'; return signature; } bool Util::descendantOf(const QObject *ascendant, const QObject *object) { QObject *parent = object->parent(); if (!parent) return false; if (parent == ascendant) return true; return descendantOf(ascendant, parent); } namespace GammaRay { static QString stringifyProperty(const QObject *obj, const QString &propName) { const QVariant value = obj->property(propName.toLatin1()); const QMetaProperty mp = obj->metaObject()->property( obj->metaObject()->indexOfProperty(propName.toLatin1())); if (mp.isValid()) { const QString enumStr = EnumUtil::enumToString(value, mp.typeName(), obj->metaObject()); if (!enumStr.isEmpty()) return enumStr; } return VariantHandler::displayString(value); } struct IconCacheEntry { explicit IconCacheEntry(const QByteArray &className_ = QByteArray()) : className(className_) {} /// note: this member's data is being referenced by the database's key QByteArray className; int defaultIcon = -1; // pair of property name and expected string value typedef QPair PropertyPair; // a list of property pairs using PropertyMap = QVector; // pair of icon and property map, for which this icon is valid typedef QPair PropertyIcon; using PropertyIcons = QVector; PropertyIcons propertyIcons; }; /// maps latin1 class name to list of icons valid for a given property map /// for performance reasons, the key is just a view of the contents of IconCacheEntry::className typedef QHash IconDatabase; static IconDatabase readIconData() { auto classesIconsRepository = ObjectBroker::object(); IconDatabase data; int id = 0; for (auto it = classesIconsRepository->constBegin(), end = classesIconsRepository->constEnd(); it != end; ++id, ++it) { const QFileInfo fileInfo((*it)); const QDir dir(fileInfo.path()); const QString className = dir.dirName(); const QString iconName = fileInfo.fileName(); const QByteArray classNameAsByteArray = className.toLatin1(); const QLatin1String classNameKey(classNameAsByteArray.data(), classNameAsByteArray.size()); auto dataIt = data.find(classNameKey); if (dataIt == data.end()) { dataIt = data.insert(classNameKey, IconCacheEntry(classNameAsByteArray)); } IconCacheEntry &perClassData = *dataIt; if (iconName == QLatin1String("default.png")) { perClassData.defaultIcon = id; } else { // special property-specific icons with file name format prop1=val;prop2=val.png QString propString(iconName); propString.chop(4); const QStringList props = propString.split(';'); IconCacheEntry::PropertyMap propertyMap; for (const QString &prop : qAsConst(props)) { const QStringList keyValue = prop.split(QLatin1Char('=')); if (keyValue.size() != 2) continue; propertyMap << qMakePair(keyValue.at(0), keyValue.at(1)); } Q_ASSERT(!propertyMap.isEmpty()); perClassData.propertyIcons << qMakePair(id, propertyMap); } } return data; } static int iconIdForObject(const QMetaObject *mo, const QObject *obj) { static const IconDatabase iconDataBase = readIconData(); auto it = iconDataBase.constFind(QLatin1String(mo->className())); if (it != iconDataBase.end()) { for (const auto &propertyIcon : qAsConst(it->propertyIcons)) { bool allMatch = true; Q_ASSERT(!propertyIcon.second.isEmpty()); for (const IconCacheEntry::PropertyPair &keyValue : qAsConst(propertyIcon.second)) { if (stringifyProperty(obj, keyValue.first) != keyValue.second) { allMatch = false; break; } } if (allMatch) return propertyIcon.first; } return it->defaultIcon; } if (mo->superClass()) return iconIdForObject(mo->superClass(), obj); return -1; } } int Util::iconIdForObject(const QObject *object) { if (object) return GammaRay::iconIdForObject(object->metaObject(), object); return -1; } QString Util::tooltipForObject(const QObject *object) { return qApp->translate("GammaRay::Util", "

Object name: %1 (Address: %2)\nType: %3\nParent: %4 (Address: %5)\nNumber of children: %6

") .arg( object->objectName().isEmpty() ? QStringLiteral("<Not set>") : object->objectName(), Util::addressToString(object), object->metaObject()->className(), object->parent() ? object->parent()->metaObject()->className() : QStringLiteral( ""), Util::addressToString(object->parent()), QString::number(object->children().size()) ); } void Util::drawTransparencyPattern(QPainter *painter, const QRect &rect, int squareSize) { QPixmap bgPattern(2 * squareSize, 2 * squareSize); bgPattern.fill(Qt::lightGray); QPainter bgPainter(&bgPattern); bgPainter.fillRect(squareSize, 0, squareSize, squareSize, Qt::gray); bgPainter.fillRect(0, squareSize, squareSize, squareSize, Qt::gray); QBrush bgBrush; bgBrush.setTexture(bgPattern); painter->fillRect(rect, bgBrush); } int Util::signalIndexToMethodIndex(const QMetaObject *metaObject, int signalIndex) { return QMetaObjectPrivate::signal(metaObject, signalIndex).methodIndex(); } gammaray-2.11.3/core/util.h000066400000000000000000000145251413310443000154520ustar00rootroot00000000000000/* util.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ /** @file This file is part of the GammaRay Plugin API and declares various utility methods needed when writing a plugin. @brief Declares various utility methods needed when writing a GammaRay plugin. @author Volker Krause \ */ #ifndef GAMMARAY_UTIL_H #define GAMMARAY_UTIL_H #include "gammaray_core_export.h" #include #include QT_BEGIN_NAMESPACE class QRect; class QPainter; class QObject; QT_END_NAMESPACE namespace GammaRay { /*! * GammaRay probe utilities. */ namespace Util { /*! * Returns a human readable string name of the specified QObject. * This does include the type name. * @param object is a pointer to a valid or null QObject. * * @return a QString containing the human readable display string. */ GAMMARAY_CORE_EXPORT QString displayString(const QObject *object); /*! * Short display string for a QObject, either the object name or the address. * This does not include the type name. * @param object valid or 0 QObject */ GAMMARAY_CORE_EXPORT QString shortDisplayString(const QObject *object); /*! * Returns a string version (as a hex number starting with "0x") of the * memory address @p p. * @param p is a pointer to an address in memory. * * @return a QString containing the human readable address string. */ GAMMARAY_CORE_EXPORT QString addressToString(const void *p); /*! * Translates an enum or flag value into a human readable text. * @param value The numerical value. Type information from the QVariant * are used to find the corresponding QMetaEnum. * @param typeName Use this if the @p value has type int * (e.g. the case for QMetaProperty::read). * @param object Additional QObject to search for QMetaEnums. * * @return a QString containing the string version of the specified @p value. * @deprecated Use EnumUtils::enumToString. */ GAMMARAY_CORE_DEPRECATED_EXPORT QString enumToString(const QVariant &value, const char *typeName, const QObject *object); /*! * Returns a display string for @p method. * This includes return types and argument names, if available. */ GAMMARAY_CORE_EXPORT QString prettyMethodSignature(const QMetaMethod &method); /*! * Determines if the QObject @p obj is a descendant of the QObject @p ascendant. * @param ascendant is a pointer to a QObject. * @param object is a pointer to a QObject. * * @return true if @p obj is a descendant of @p ascendant; false otherwise. */ GAMMARAY_CORE_EXPORT bool descendantOf(const QObject *ascendant, const QObject *object); /*! * Finds the parent QObject of the specified type T, if such exists. * @param object is a pointer to a QObject. * * @return zero on failure; else a pointer to a data type T. * */ template T *findParentOfType(QObject *object) { if (!object) return nullptr; if (qobject_cast(object)) return qobject_cast(object); return findParentOfType(object->parent()); } /*! * Returns a class icon id for the given object. In normal operation a positive integer * is returned containing the icon most closely associated with the data type * pointed to by @p object * @param object is a pointer to a QObject. * * @return on failure -1 is returned; else a positive integer. */ GAMMARAY_CORE_EXPORT int iconIdForObject(const QObject *object); /*! * Returns a suitable rich text tooltip string for @p object. * @param object a pointer to a valid or null QObject. */ GAMMARAY_CORE_EXPORT QString tooltipForObject(const QObject *object); /*! * Draws a transparency pattern, i.e. the common checkerboard pattern into @p rect. * * @param painter The QPainter to draw with. * @param rect The area to draw into. * @param squareSize The size of the individual checkerboard squares. */ GAMMARAY_CORE_EXPORT void drawTransparencyPattern(QPainter *painter, const QRect &rect, int squareSize = 8); /*! * Turns a signal index into a method index. * Signals indexes are used internally by QObject as an optimization and are * usually not exposed in public API. If you get them nevertheless, by using * internals of QObject this method turns them into method indexes that work * with public QMetaObject API. * * @param metaObject The meta object the signal belongs so * @param signalIndex The signal index to convert. * @since 2.2 */ GAMMARAY_CORE_EXPORT int signalIndexToMethodIndex(const QMetaObject *metaObject, int signalIndex); /*! * Checks if the given pointer should be considered a nullptr. * One would assume this to be trivial, but there are some interesting hacks * in for example the QSG software renderer that use near-null values to not * trigger normal nullptr checks but are still effectively null as far as we * are concerned (as in: we will crash for sure when dereferencing them). */ inline bool isNullish(void *ptr) { #if QT_POINTER_SIZE == 4 return quintptr(ptr) < 0x4; #else return quintptr(ptr) < 0x8; #endif } /*! * QVariant::value() dereferences the object internally, this method * converts it to a QObject without dereferencing (e.g. to check then if it still exists). */ inline const QObject* uncheckedQObjectCast(const QVariant& v) { return *reinterpret_cast(v.constData()); } } } #endif // GAMMARAY_UTIL_H gammaray-2.11.3/core/varianthandler.cpp000066400000000000000000000375461413310443000200420ustar00rootroot00000000000000/* varianthandler.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "varianthandler.h" #include "util.h" #include "enumutil.h" #include "enumrepositoryserver.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include Q_DECLARE_METATYPE(const QObject*) using namespace GammaRay; namespace GammaRay { class VariantHandlerRepository { public: VariantHandlerRepository() = default; ~VariantHandlerRepository(); void clear(); QHash *> stringConverters; QVector genericStringConverters; private: Q_DISABLE_COPY(VariantHandlerRepository) }; VariantHandlerRepository::~VariantHandlerRepository() { qDeleteAll(stringConverters); } void VariantHandlerRepository::clear() { qDeleteAll(stringConverters); stringConverters.clear(); genericStringConverters.clear(); } static QString displayMatrix4x4(const QMatrix4x4 &matrix) { QStringList rows; rows.reserve(4); for (int i = 0; i < 4; ++i) { QStringList cols; cols.reserve(4); for (int j = 0; j < 4; ++j) { cols.push_back(QString::number(matrix(i, j))); } rows.push_back(cols.join(QStringLiteral(" "))); } return '[' + rows.join(QStringLiteral(", ")) + ']'; } static QString displayMatrix4x4(const QMatrix4x4 *matrix) { if (matrix) { return displayMatrix4x4(*matrix); } return QStringLiteral(""); } template static QString displayVector(const T &vector) { QStringList v; for (int i = 0; i < Dim; ++i) { v.push_back(QString::number(vector[i])); } return '[' + v.join(QStringLiteral(", ")) + ']'; } } Q_GLOBAL_STATIC(VariantHandlerRepository, s_variantHandlerRepository) QString VariantHandler::displayString(const QVariant &value) { switch (value.type()) { #ifndef QT_NO_CURSOR case QVariant::Cursor: { const QCursor cursor = value.value(); return EnumUtil::enumToString(QVariant::fromValue(cursor.shape()), "Qt::CursorShape"); } #endif case QVariant::Icon: { const QIcon icon = value.value(); if (icon.isNull()) { return qApp->translate("GammaRay::VariantHandler", ""); } const auto sizes = icon.availableSizes(); QStringList l; l.reserve(sizes.size()); for (QSize size : sizes) { l.push_back(displayString(size)); } return l.join(QStringLiteral(", ")); } case QVariant::Line: { const auto line = value.toLine(); return QStringLiteral("%1, %2 → %3, %4"). arg(line.x1()).arg(line.y1()). arg(line.x2()).arg(line.y2()); } case QVariant::LineF: { const auto line = value.toLineF(); return QStringLiteral("%1, %2 → %3, %4"). arg(line.x1()).arg(line.y1()). arg(line.x2()).arg(line.y2()); } case QVariant::Locale: return value.toLocale().name(); case QVariant::Point: { const auto point = value.toPoint(); return QStringLiteral("%1, %2"). arg(point.x()). arg(point.y()); } case QVariant::PointF: { const auto point = value.toPointF(); return QStringLiteral("%1, %2"). arg(point.x()). arg(point.y()); } case QVariant::Rect: { const auto rect = value.toRect(); return QStringLiteral("%1, %2 %3 x %4"). arg(rect.x()). arg(rect.y()). arg(rect.width()). arg(rect.height()); } case QVariant::RectF: { const auto rect = value.toRectF(); return QStringLiteral("%1, %2 %3 x %4"). arg(rect.x()). arg(rect.y()). arg(rect.width()). arg(rect.height()); } case QVariant::Palette: { const QPalette pal = value.value(); if (pal == qApp->palette()) { return QStringLiteral(""); } return QStringLiteral(""); } case QVariant::Size: { const auto size = value.toSize(); return QStringLiteral("%1 x %2"). arg(size.width()). arg(size.height()); } case QVariant::SizeF: { const auto size = value.toSizeF(); return QStringLiteral("%1 x %2"). arg(size.width()). arg(size.height()); } case QVariant::StringList: { const auto l = value.toStringList(); if (l.isEmpty()) { return QStringLiteral(""); } if (l.size() == 1) { return l.at(0); } return QStringLiteral("<%1 entries>").arg(l.size()); } case QVariant::Transform: { const QTransform t = value.value(); return QStringLiteral("[%1 %2 %3, %4 %5 %6, %7 %8 %9]"). arg(t.m11()).arg(t.m12()).arg(t.m13()). arg(t.m21()).arg(t.m22()).arg(t.m23()). arg(t.m31()).arg(t.m32()).arg(t.m33()); } default: break; } // types with dynamic type ids if (value.userType() == qMetaTypeId()) { const auto v = value.value(); return QString::number(v) + QLatin1String(" '") + QChar(v) + QLatin1Char('\''); } if (value.userType() == qMetaTypeId()) { const QMargins margins = value.value(); return qApp->translate("GammaRay::VariantHandler", "left: %1, top: %2, right: %3, bottom: %4") .arg(margins.left()).arg(margins.top()) .arg(margins.right()).arg(margins.bottom()); } if (value.userType() == qMetaTypeId()) { const auto mo = value.value(); if (!mo) { return QStringLiteral("0x0"); } return mo->className(); } if (value.userType() == qMetaTypeId()) return displayMatrix4x4(value.value()); if (value.userType() == qMetaTypeId()) return displayMatrix4x4(value.value()); if (value.userType() == qMetaTypeId()) { return displayVector<2>(value.value()); } if (value.userType() == qMetaTypeId()) { return displayVector<3>(value.value()); } if (value.userType() == qMetaTypeId()) { return displayVector<4>(value.value()); } if (value.userType() == qMetaTypeId()) { return value.value().id(); } if (value.userType() == qMetaTypeId >()) { const QSet set = value.value >(); QStringList l; l.reserve(set.size()); for (const QByteArray &b : set) { l.push_back(QString::fromUtf8(b)); } return l.join(QStringLiteral(", ")); } if (value.userType() == qMetaTypeId()) { const auto ec = value.toEasingCurve(); return EnumUtil::enumToString(QVariant::fromValue(ec.type())); } // enums const QString enumStr = EnumUtil::enumToString(value); if (!enumStr.isEmpty()) { return enumStr; } // custom converters auto it = s_variantHandlerRepository()->stringConverters.constFind(value.userType()); if (it != s_variantHandlerRepository()->stringConverters.constEnd()) { return (*it.value())(value); } // Work around QTBUG-73437 if (value.userType() == qMetaTypeId()) { int size = value.value().size(); if (size == 0) { return QStringLiteral(""); } else { return QStringLiteral("<%1 entries>").arg(size); } } if (value.userType() == qMetaTypeId()) { int size = value.value().size(); if (size == 0) { return QStringLiteral(""); } else { return QStringLiteral("<%1 entries>").arg(size); } } if (value.userType() == qMetaTypeId()) { QJsonValue v = value.value(); if (v.isBool()) { return v.toBool() ? QStringLiteral("true") : QStringLiteral("false"); } else if (v.isDouble()) { return QString::number(v.toDouble()); } else if(v.isNull()) { return QStringLiteral("null"); } else if (v.isArray()) { int size = v.toArray().size(); if (size == 0) { return QStringLiteral(""); } else { return QStringLiteral("<%1 entries>").arg(size); } } else if (v.isObject()) { int size = v.toObject().size(); if (size == 0) { return QStringLiteral(""); } else { return QStringLiteral("<%1 entries>").arg(size); } } else if (v.isString()) { return v.toString(); } else { return QStringLiteral("undefined"); } } if (value.canConvert()) { QSequentialIterable it = value.value(); if (it.size() == 0) { return QStringLiteral(""); } else { return QStringLiteral("<%1 entries>").arg(it.size()); } } if (value.canConvert()) { auto it = value.value(); if (it.size() == 0) { return QStringLiteral(""); } else { return QStringLiteral("<%1 entries>").arg(it.size()); } } // generic converters const QVector genStrConverters = s_variantHandlerRepository()->genericStringConverters; for (auto converter : genStrConverters) { bool ok = false; const QString s = converter(value, &ok); if (ok) return s; } // catch-all QObject handler // search the entire hierarchy for custom converters, so we can override this // for entire sub-trees if (value.canConvert() || value.canConvert()) { bool isConst = value.canConvert(); const auto obj = isConst ? value.value() : value.value(); if (!obj || obj->metaObject() == &QObject::staticMetaObject) return Util::displayString(obj); auto mo = obj->metaObject(); while (mo) { auto type = QMetaType::type(QByteArray(mo->className()) + '*'); if (type > 0) { auto it = s_variantHandlerRepository()->stringConverters.constFind(type); if (it != s_variantHandlerRepository()->stringConverters.constEnd()) return (*it.value())(value); } mo = mo->superClass(); } return Util::displayString(obj); } return value.toString(); } QVariant VariantHandler::decoration(const QVariant &value) { switch (value.type()) { case QVariant::Brush: { const QBrush b = value.value(); if (b.style() != Qt::NoBrush) { QPixmap p(16, 16); p.fill(QColor(0, 0, 0, 0)); QPainter painter(&p); painter.setBrush(b); painter.drawRect(0, 0, p.width() - 1, p.height() - 1); return p; } break; } case QVariant::Color: { const QColor c = value.value(); if (c.isValid()) { QPixmap p(16, 16); QPainter painter(&p); Util::drawTransparencyPattern(&painter, p.rect(), 4); painter.setBrush(QBrush(c)); painter.drawRect(0, 0, p.width() - 1, p.height() - 1); return p; } break; } #ifndef QT_NO_CURSOR case QVariant::Cursor: { const QCursor c = value.value(); if (!c.pixmap().isNull()) { return c.pixmap().scaled(16, 16, Qt::KeepAspectRatio, Qt::FastTransformation); } break; } #endif case QVariant::Icon: return value; case QVariant::Pen: { const QPen pen = value.value(); if (pen.style() != Qt::NoPen) { QPixmap p(16, 16); QPainter painter(&p); Util::drawTransparencyPattern(&painter, p.rect(), 4); painter.save(); painter.setPen(pen); painter.translate(0, 8 - pen.width() / 2); painter.drawLine(0, 0, p.width(), 0); painter.restore(); painter.drawRect(0, 0, p.width() - 1, p.height() - 1); return p; } break; } case QVariant::Pixmap: { const QPixmap p = value.value(); if (p.isNull()) { break; } QPixmap deco(16, 16); QPainter painter(&deco); Util::drawTransparencyPattern(&painter, deco.rect(), 4); QPixmap scaled(p); if (p.width() > deco.width() || p.height() > deco.height()) { scaled = p.scaled(deco.width(), deco.height(), Qt::KeepAspectRatio, Qt::SmoothTransformation); } painter.drawPixmap((deco.width() - scaled.width()) / 2, (deco.height() - scaled.height()) / 2, scaled); painter.drawRect(0, 0, deco.width() - 1, deco.height() - 1); return deco; } default: break; } return QVariant(); } void VariantHandler::registerStringConverter(int type, Converter *converter) { Q_ASSERT(!s_variantHandlerRepository()->stringConverters.contains(type)); s_variantHandlerRepository()->stringConverters.insert(type, converter); } void VariantHandler::registerGenericStringConverter( VariantHandler::GenericStringConverter converter) { s_variantHandlerRepository()->genericStringConverters.push_back(converter); } QVariant VariantHandler::serializableVariant(const QVariant &value) { if (value.userType() == qMetaTypeId()) { const QMatrix4x4 *m = value.value(); if (!m) { return QVariant(); } return QVariant::fromValue(QMatrix4x4(*m)); } if (EnumRepositoryServer::isEnum(value.userType())) { return QVariant::fromValue(EnumRepositoryServer::valueFromVariant(value)); } return value; } void VariantHandler::clear() { s_variantHandlerRepository()->clear(); } gammaray-2.11.3/core/varianthandler.h000066400000000000000000000103111413310443000174640ustar00rootroot00000000000000/* varianthandler.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_VARIANTHANDLER_H #define GAMMARAY_VARIANTHANDLER_H #include "gammaray_core_export.h" #include namespace GammaRay { /*! Variant conversion functions, extendable by plugins. */ namespace VariantHandler { ///@cond internal template struct Converter { virtual ~Converter() {} virtual RetT operator()(const QVariant &v) = 0; }; template struct ConverterImpl : public Converter { explicit inline ConverterImpl(FuncT converter) : f(converter) { } RetT operator()(const QVariant &v) override { return f(v.value()); } FuncT f; }; ///@endcond /*! * Returns a human readable string version of the QVariant value. * Converts to the variant type and prints the string value accordingly. * @param value is a QVariant. * * @return a QString containing the human readable string. */ GAMMARAY_CORE_EXPORT QString displayString(const QVariant &value); /*! * Returns a human readable string version of the given value. * Thihs is a convenience overload of the QVariant-based version above. * * @return a QString containing the human readable string. */ template inline QString displayString(T value) { return displayString(QVariant::fromValue(value)); } /*! * Returns a value representing @p value in a itemview decoration role. * @param value is a QVariant. * * @return a QVariant itemview decoration role. */ GAMMARAY_CORE_EXPORT QVariant decoration(const QVariant &value); /*! * Register a string conversion functions for a variant type. * @internal */ GAMMARAY_CORE_EXPORT void registerStringConverter(int type, Converter *converter); /*! * Register a string conversion function for a variant type. * @tparam T The type for which to use this converter function. */ template inline void registerStringConverter(FuncT f) { Converter *converter = new ConverterImpl(f); registerStringConverter(qMetaTypeId(), converter); } /*! Callback type for generic string converters. */ typedef QString (*GenericStringConverter)(const QVariant &value, bool *ok); /*! * Register a generic string conversion function for various variant types. * This can be used when you have a converter that can dynamically check if * it can handle a given variant, and the types it can handle aren't known * at compile time (example: QQmlListProperty). * @param converter The converter function. It's second parameter is used to * indicate if the value could be handled. */ GAMMARAY_CORE_EXPORT void registerGenericStringConverter(GenericStringConverter converter); /*! * Converts the given variant into an variant of a different type that can be transferred * to another process. * This is most prominently needed for pointer types, e.g. const QMatrix4x4* -> QMatrix4x4, * primarily for the fancy display delegate on the client side. */ GAMMARAY_CORE_EXPORT QVariant serializableVariant(const QVariant &value); ///@cond internal void clear(); ///@endcond } } #endif // GAMMARAY_VARIANTHANDLER_H gammaray-2.11.3/debian.changelog000066400000000000000000000116041413310443000164620ustar00rootroot00000000000000gammaray (2.11.3) final; urgency=low * 2.11.3 patchlevel release -- Allen Winter Fri, 15 Oct 2021 09:50:00 -0500 gammaray (2.11.2) final; urgency=low * 2.11.2 patchlevel release -- Allen Winter Fri, 18 Sep 2020 08:30:00 -0500 gammaray (2.11.1) final; urgency=low * 2.11.1 patchlevel release -- Allen Winter Tue, 03 Mar 2020 16:15:00 -0500 gammaray (2.11.0) final; urgency=low * 2.11.0 final release -- Allen Winter Wed, 03 Jul 2019 17:30:00 -0500 gammaray (2.10.0) final; urgency=low * 2.10.0 final release -- Allen Winter Sun, 16 Dec 2018 09:00:00 -0500 gammaray (2.9.1) final; urgency=low * 2.9.1 final release -- Allen Winter Wed, 01 Aug 2018 12:00:00 -0500 gammaray (2.9.0) final; urgency=low * 2.9.0 final release -- Allen Winter Wed, 07 Feb 2018 12:00:00 -0500 gammaray (2.8.1) final; urgency=low * 2.8.1 bugfix release -- Allen Winter Wed, 05 Sep 2017 08:00:00 -0500 gammaray (2.8.0) final; urgency=low * 2.8.0 final release -- Allen Winter Wed, 07 Jun 2017 16:00:00 -0500 gammaray (2.7.0) final; urgency=low * 2.7.0 final release -- Allen Winter Tue, 14 Feb 2017 12:00:00 -0500 gammaray (2.6.0) final; urgency=low * 2.6.0 final release -- Allen Winter Thu, 29 Sep 2016 12:00:00 -0500 gammaray (2.5.1) final; urgency=low * 2.5.1 bugfix release -- Allen Winter Fri, 27 Jul 2016 12:00:00 -0500 gammaray (2.5.0) final; urgency=low * 2.5.0 final release -- Allen Winter Fri, 08 Jul 2016 09:30:00 -0500 gammaray (2.4.1) final; urgency=low * 2.4.1 final release -- Allen Winter Fri, 11 Mar 2016 09:30:00 -0500 gammaray (2.4.0) final; urgency=low * 2.4.0 final release -- Allen Winter Tue, 22 Dec 2015 09:30:00 -0500 gammaray (2.3.99) final; urgency=low * 2.4.0 rc1 release -- Allen Winter Sun, 20 Dec 2015 12:30:00 -0500 gammaray (2.3.0) final; urgency=low * 2.3.0 final release -- Allen Winter Fri, 10 Jul 2015 15:30:00 -0500 gammaray (2.2.1) final; urgency=low * 2.2.1 first 2.2 bugfix release -- Allen Winter Tue, 27 Jan 2015 18:30:00 -0500 gammaray (2.2.0) final; urgency=low * 2.2.0 final release -- Allen Winter Fri, 31 Oct 2014 09:30:00 -0500 gammaray (2.1.2) final; urgency=low * 2.1.2 second 2.1 bugfix release -- Allen Winter Thu, 23 Oct 2014 10:00:00 -0500 gammaray (2.1.1) final; urgency=low * 2.1.1 first 2.1 bugfix release -- Allen Winter Sat, 30 Aug 2014 12:00:00 -0500 gammaray (2.1.0) final; urgency=low * 2.1.0 final release -- Allen Winter Fri, 27 Jun 2014 09:30:00 -0500 gammaray (2.0.98) final; urgency=low * 2.1.0 rc1 release -- Allen Winter Thu, 26 Jun 2014 15:30:00 -0500 gammaray (2.0.2) final; urgency=low * 2.0.2 final release -- Allen Winter Fri, 18 Apr 2014 11:30:00 -0500 gammaray (2.0.1) final; urgency=low * 2.0.1 final release -- Allen Winter Fri, 28 Feb 2014 11:30:00 -0500 gammaray (2.0.0) final; urgency=low * 2.0.0 final release -- Allen Winter Fri, 17 Jan 2014 11:00:00 -0500 gammaray (1.9.96) final; urgency=low * 2.0.0 beta2 release -- Allen Winter Sat, 11 Jan 2014 11:00:00 -0500 gammaray (1.9.95) final; urgency=low * 2.0.0 beta1 release -- Allen Winter Fri, 20 Dec 2013 11:00:00 -0500 gammaray (1.3.2) final; urgency=low * 1.3.2 patch release -- Allen Winter Thu, 03 Oct 2013 11:00:00 -0500 gammaray (1.3.1) final; urgency=low * 1.3.1 patch release -- Allen Winter Tues, 30 Apr 2013 16:30:00 -0500 gammaray (1.3.0) final; urgency=low * 1.3.0 final release -- Allen Winter Sun, 27 Jan 2013 18:00:00 -0500 gammaray (1.2.2) patch; urgency=low * 1.2.2 patch release -- Allen Winter Fri, 21 Dec 2012 14:33:00 -0500 gammaray (1.2.1) patch; urgency=low * 1.2.1 patch release -- Allen Winter Thu, 16 Aug 2012 10:17:14 -0500 gammaray (1.2.0) final; urgency=low * 1.2.0 final release -- Allen Winter Thu, 05 Jul 2012 10:19:14 -0500 gammaray (1.1.99) beta2; urgency=low * 1.1.99 beta2 release -- Allen Winter Tue, 03 Jul 2012 14:10:14 +0000 gammaray (1.1.98) beta; urgency=low * 1.1.98 beta release -- Allen Winter Wed, 27 Jun 2012 14:10:14 +0000 gammaray-2.11.3/debian.compat000066400000000000000000000000021413310443000160040ustar00rootroot000000000000009 gammaray-2.11.3/debian.control000066400000000000000000000017371413310443000162210ustar00rootroot00000000000000Source: gammaray Section: Miscellaneous Priority: optional Maintainer: Allen Winter Build-Depends: debhelper (>=9), cdbs, cmake, qtbase5-private-dev, qtdeclarative5-dev, qtdeclarative5-private-dev, qtdeclarative5-dev-tools, qtscript5-dev, qttools5-dev, libqt5svg5-dev, libqt5webkit5-dev, kdstatemachineeditor Standards-Version: 3.9.6 Homepage: https://github.com/KDAB/GammaRay Package: gammaray Architecture: any Depends: ${misc:Depends}, ${shlibs:Depends}, kdstatemachineeditor Description: A tool to poke around in a Qt-application GammaRay is a tool for examining the internals of a Qt application and to some extent also manipulate it. GammaRay uses injection methods to hook into an application at runtime and provide access to a wide variety of interesting information. It provides easy ways of navigating through the complex internal structures you find in some Qt frameworks, such as QGraphicsView, model/view, QTextDocument, state machines and more. gammaray-2.11.3/debian.rules000066400000000000000000000001511413310443000156600ustar00rootroot00000000000000#!/usr/bin/make -f include /usr/share/cdbs/1/rules/debhelper.mk include /usr/share/cdbs/1/class/cmake.mk gammaray-2.11.3/docs/000077500000000000000000000000001413310443000143155ustar00rootroot00000000000000gammaray-2.11.3/docs/CMakeLists.txt000066400000000000000000000044651413310443000170660ustar00rootroot00000000000000# # find doc generation tools needed by multiple sub-directories # # qdoc toolchain # TODO: some of this probably should be upstreamed to Qt's cmake files... # TODO replace with Qt5::qhelpgenerator for Qt >= 5.7.1 find_program(QHELPGEN_EXECUTABLE qhelpgenerator HINTS ${QT_INSTALL_BINS} ) find_program(QDOC_EXECUTABLE qdoc HINTS ${QT_INSTALL_BINS} ) find_program(QTATTRIBUTIONSSCANNER_EXECUTABLE qtattributionsscanner HINTS ${QT_INSTALL_BINS} ) find_file(QDOC_TEMPLATE global/qt-html-templates-offline.qdocconf HINTS ${QT_INSTALL_DOCS} HINTS ${QT_INSTALL_DATA} HINTS ${QT_INSTALL_DATA}/doc ) find_file(QDOC_QTCORE_INDEX qtcore.index HINTS ${QT_INSTALL_DOCS}/qtcore HINTS ${QT_INSTALL_DATA}/doc/qtcore ) if(NOT QDOC_EXECUTABLE OR NOT QHELPGEN_EXECUTABLE OR NOT QDOC_TEMPLATE OR NOT QDOC_QTCORE_INDEX OR NOT QTATTRIBUTIONSSCANNER_EXECUTABLE) message(STATUS "Unable to build user manual in qch format.") message(STATUS "qdoc executable: ${QDOC_EXECUTABLE}") message(STATUS "qhelpgenerator executable: ${QHELPGEN_EXECUTABLE}") message(STATUS "qtattributionsscanner executable: ${QTATTRIBUTIONSSCANNER_EXECUTABLE}") message(STATUS "qdoc template (qt-html-templates-offline.qdocconf): ${QDOC_TEMPLATE}") message(STATUS "qdoc qtcore index (qtcore.index): ${QDOC_QTCORE_INDEX}") set(GAMMARAY_USER_MANUAL_BUILD False) else() set(GAMMARAY_USER_MANUAL_BUILD True) endif() add_feature_info("User Manual creation in qch format" GAMMARAY_USER_MANUAL_BUILD "Requires qdoc, qhelpgenerator, qtattributionsscanner, the qdoc templates and index files") # Doxygen find_package(Doxygen) set_package_properties(Doxygen PROPERTIES TYPE OPTIONAL DESCRIPTION "API Documentation system" URL "https://www.doxygen.org" PURPOSE "Needed to build the API documentation." ) if(DOXYGEN_DOT_EXECUTABLE) set(HAVE_DOT "YES") else() set(HAVE_DOT "NO") message(STATUS "Unable to provide inheritance diagrams for the API documentation. To fix, install the graphviz project from https://www.graphviz.org") endif() if(DOXYGEN_FOUND AND QHELPGEN_EXECUTABLE) set(GAMMARAY_API_DOCS_BUILD True) else() set(GAMMARAY_API_DOCS_BUILD False) endif() # # generate docs # if(GAMMARAY_API_DOCS_BUILD) add_subdirectory(api) endif() if(GAMMARAY_USER_MANUAL_BUILD) add_subdirectory(manual) add_subdirectory(collection) endif() add_subdirectory(man) gammaray-2.11.3/docs/GammaRay-CopyrightAssignmentForm.pdf000066400000000000000000014533261413310443000233470ustar00rootroot00000000000000%PDF-1.6 % 468 0 obj <> endobj 488 0 obj <>/Filter/FlateDecode/ID[<6ED954BC0EF0F143936766DC11EA27D0><9AA4E427859B3C4A949FCC15EEF282C7>]/Index[468 46]/Info 467 0 R/Length 93/Prev 89373/Root 469 0 R/Size 514/Type/XRef/W[1 2 1]>>stream hbbd``b`NAD+`A"@LAD5(WHo {X,#c2Hg?K4 ] endstream endobj startxref 0 %%EOF 513 0 obj <>stream hb``` l    L-z30p21Zj:m65MgLސalQ.Iu441c&: 30rb ⋙"3-fJdfg0%c;[FXM@Ű!HGy`:a3Be0(pi20jn`{/``[gtUZ{tXDmiT`%6Z -%4u..!]%z n2 P˜1H3<@wIA endstream endobj 469 0 obj <>/Metadata 49 0 R/PageLayout/OneColumn/Pages 466 0 R/StructTreeRoot 109 0 R/Type/Catalog>> endobj 470 0 obj <>/Font<>/ProcSet[/PDF/Text]>>/Rotate 0/StructParents 0/Tabs/S/Type/Page>> endobj 471 0 obj <>stream h[s\WVI(Kؖe9&!gXh_kIIٵ @h HL# rRrTtF<#g4nEҏd_\Y|Jz;B*P]!JS ++MWH2,]YW @QY))'+ s5" wT aWGogE<+\Ћ._<^^#g7Ϟ_.+zbn$?Mm9g7Kz/wts_|Fɏ|v}y?7# ncDG!iRF! ·RR2U||:zy=g&XX4-u3̟)}/.gw㷨o&@G_gohxSoH[T(=R>nN/;Z,g2BV=6dHԊ6z`c4x|z?4=ɣK?_}|L^Ww].S׳4//pC $/NOg+j1(@g'ZqZ:GfYLӻvg`'7߽?p^p_ Vǯw7k~6!il$h6g7HSO1jy75͘'ۻ|6O]2Y:9>[~srv\}|D d:e].zCrv"&w M|0 fCO﹮|S1g+EDvq.k@uqz7%n:|7 㗯ѦTҚO{7? ~-4vĖL_ h;&^ *W8h/jPSdsդwz'eZXfhA4o̹aR褔fZhqi!vy yq-{Fc,Smv)[z-t֖θ:~הEg:ױF<\c(^oЯ~CQh ֙};[Is-A23\NqGD+:PvԄw -j*I3Nu ވ[$(ׅT-XfrBi-XDn!é.U2waَH7ފzX#kIb'YܬHԯߴ՗H:p.DB¼21Rk 8%e ˑڵ'eV17p.q:4@TyOtuBtl$0o52U:(:&'ި0]IZ1 a&8id!s&4 +1P6 ]Q<4ē6Rm平raUlb[+;,c--ᱭo!@a~@`)Z R ?ぇI`<&zeVe-͍<9jڧ}#ؕ=`\ lY \{x %] 2BRi%6rh> kkYVgx ve c~@EIm~xֻsE &NIfXѹ {Jp@fŹd *Q UZP=e"'G:չFL3 ptUVؾplh mJ>%doت*ba m'zK?/ 3ATyű?^Dsz5z(G ҅쓳vw|vuk ߏ` *^ WfMEohh;;gI V1YiU&%}Iѳ/\;q6ɑsUcg pP`al\wR=3o) *vc|a[bJI(0doGq8CVe":  b]IzWwZE}zfT"cY`DZ(eXy*D|hzR⼨hWߊQ euK;?됨F, cGVl l u䉭jREr耹Ă3P4b[vm.2T *2ҥ2 Uh7}+)s"Ͱƥ8NS€39DH<$1sėݞmضlap{'PORo*9ܷ,TQ6@ܖ7Ow[HumEbULP- oC&0D;-TUy n+; -ժ*" ʍ!w@˜6w@1#5@nT_/@_j;G&m|LZmUtB=} b[uo< [4r6D d31MHi[Z"*(2 MW2=\!"* dd]^ÊXRTA6i=;B dQ9. P_=%]+K&-#2@I`G R:,2-lJ1. ] Z9g0ATi5 JXg`5*z20uGܣ= 4[ͦ&M$f=0uk٨%K[N“S|Xm%vR/;"Q;.ܤ$?RNQEew.ǨQΞ 1k89u{9 tŧWzJo,-1t 8淩Q*I ?P%,}vìҮQ}_b7te.%.{[pc?8&`I* .U,'1G&'Oy1G '{K|^W%ܣ )+yK~{[= PƚaRl8׀;rlM<ΦOf%)Wm5ak槉p48X55ORO|kq?P#tk^ր-ccm5[@R(֟MIb>'ap|>[~ 0O9i )Ȱ@]Xf=|*L<(v&p"> §T)9R*?x\y/p/L$NnF4XFE|EL#i)cza rÎ*kߑ-0/xĪ t,a>XPB#2]P w׬dm˙ޗ#= EqzFQELG,œFv ɩBoTm9dK^On27f3DgRs}K)YwAq:f`5s^H5L9`#/V sɩV `f>%iX,e) S|g,v.p4ᦣU@4C+,C ˦.)cFupޢp ֫g2y;儺r:4N 0)/yeڬƨBe@:1clRr8Z,K1Ȼj0ikb"6FvZHdh m JcW9^ f-oo+qdeuyzL)DhV޳ߵE2 0689~KOi.[h^$ (mkpxוsJQ*ϑx0%A !3r :s.zUUĢ+9a1c EKIZuN76/B:1;0Ar4&g!`倢( Zi_FLy??lL1zsWb5W}o7dzrX{6r\oǧޭ>g#g˟shF(c4?AHO@ě? aY=}( $~$AH p$|"A}t1~1v6zMr-Y'6ޑdžf~bD0WBlmxxu.ޏ_-nmB1>YMGHOW_mvcwnk1GXgw:}o狣޿ݯ^Qg٥i~Pvs~avV&kRӗ?/#:vr Q.4yt[TPsO0XTʲzhI/{GXfwOڛd,M^1n qavwV>Y$'f]&eÖq%1S!3h|m>GWg]f1_::^ ݐQ"62ߺ[ob6j[k ox(U 5cV(<~h_~mHC+zxH)u#7"f*u8NῙR*tu=f:^-n{zw]by<ߔ8_-n=Ofr ^VKjcs KW endstream endobj 472 0 obj <>stream HTMk0WQ:XїJ!=Ba^²di䐤-;3v ͛rt;6 WW[&U2:~o4aqSڕEHFUx=T nd9T HqvrUb(ALl<՟!Yݽ7ƇfM8҉ԻZ+a|O0)2j4 }َ6NOɕՖ~6æ B̳'te4c!Ica,̂MUt +s.BܲgB+r.Tn{R42-Ek')ģU}O>RҝsH/XuTV+,'Mg_z]ohQ us@v{鐑ǎD!Tw5bK4R:,i$L#8#X.sDZhn_px8)y]2, n`,"0Umwū!20V$e<{6< l~BwE}+W2|%~!E(t䜓Fw}D*:&t nR5ʉv.Z:GZSy}vXF[2 qr|ɋpRve{}vYث=slSb26$SJdTb`e屼 WcY endstream endobj 473 0 obj <>stream H|TKo@ WY+HUanm$Ux(V$Tos=/ ^d96#81~'Hw 0鯙 f% ( 5@V́BIzvaFas t''1pǩoMLؒ˜㞼XV89BK?YtZd3{9`_S^uFF FV?ڑI2yavlőt' '"ڨfO7ݾ<\C5Y-g/18"J@Ts&]lCq~_N y]!;Xy1Xb jTXd2\ -4;[tb@lNy m( endstream endobj 474 0 obj <>stream HTn0+H%#JA8Ρ-栛уr`Ic5wl$=X)"E1K0scc}Nu:U`>stream HtTKo@ +|9@$8J=p@$aG=i3cQ P`lIZ ş"zP no蜌w_z2i21><-X%9mwJEyGgVw\'ך6YBJ}%8=Xq*(=9U$|(0Cᶒ7D3c5ɖD@2ւ]%_LMX{?0ɮLs+Q.JtEMij!3,uJՠ؀nũ"[ǺYb/5emĺX9Lԃxz;c\L]KNjV\TRDQ%IIG ) z{4d33 tPPOuUv(#P)?٠~V4.9S&_CiZs*@O,Tpk'03!Oit.C0YdE~/aOa^ad]T! o 1 м^W\YѮC/F#g< zxJxonM?I3v*w l8ZVy@5O88F̒<_wV)\>PUsOr^ endstream endobj 476 0 obj <>stream HTN@}WLv%xmK*Ixh`ń@E9^P=; ~') %Pu.e)M \z]~<껯Aδ(Us%d TUHZЪ1i$}!H0֊\A& ۝`C#hY[omcS#Qh, ú,"*mwlSof v}=CeP]2_Ĉ ((YVSD3G$KzS*31}oIJoQ]<Q(yʗ;[7ϫ(%7F+aP(F 4m" I:3PSmU#9C2tr9lAFUGbtc:: $2FU\Xrú(׬_tx)h:Nr}IUnbθqwy7.b z}iAxOTQ8:W#@3 $iJ:!wN>/#ZR;N4;<A cs@Zx K%ƌ-^;x= uk2TsKu6XGچn1n%4@3w<57Mj:grLdb<`Lx :<la7(Q85ޞVoH,v?TIuw_NY8 endstream endobj 477 0 obj <>stream H|Tn@}+qe/ږRD-P%STU8Hi36Taa{M<y+|)X v{a} VhU{4iV`b%me#C(fU-AL.Z"< PP |-$Kuf?6Te;3/V*V%{77M{0p4!?mDu" OSRA\JHc \@HaC٪*OYQ/R(nt&y $YXlʷ:-%6/ 6}LD Lm4.9Zҷ[.ҧ[|^a\9;U"K ߶Ed=}711G]L~-o֠kxyKϸc&svHqm:.ntǁ\I6TCڻQ1~߀-5avo@t"Q'w"IE'yyKQGyWrнҚr~+W5U endstream endobj 478 0 obj <>stream H\j0ໞb!x! Ѕ}GZsWR#G1zݍzmi:t6"IIi9r謈|pL3GQU}iv =x捈ޜbͅ톢jlfIqtm_1;>˔'8O:saQ~To=)vwi\cVe, OPT$#C{P !gqˉz ^z9YyP +^]W|%N=ֆ- endstream endobj 479 0 obj <>stream HTj1}߯У`uFw ,C গ+F+y ,Zl9gdtF`2ah` r2ԭ@MA:+ gO_KSZcM5i',aOOp5R\JF[vl[ru9*ei(6#-(!~5ٕΨ ;<64б!RFa>C PGw1W(X BR, *k[TJ-iѝ cB'jUT>Wȵdվ㍌&.Ss4%ϫ~=$!H +~ &`X Ơ\H>[m"7$M~?O7j^ƖOP~x幟>stream HSA0 |m BP=TO8Npf3vl{VO3j;nZJ87?9gm?'] p9308]cX"0GJq1D~V>}^&EJ"tTSCZtzE,Jp?tP^[s9Ҿf_DtJpCt]OJͳfxY P^N;E\O*>]33y+mۘ>e=P ,VyF]٢^>Pmq?y>stream HyTSwoɞc [5laQIBHADED2mtFOE.c}08׎8GNg9w߽'0 ֠Jb  2y.-;!KZ ^i"L0- @8(r;q7Ly&Qq4j|9 V)gB0iW8#8wթ8_٥ʨQQj@&A)/g>'Kt;\ ӥ$պFZUn(4T%)뫔0C&Zi8bxEB;Pӓ̹A om?W= x-[0}y)7ta>jT7@tܛ`q2ʀ&6ZLĄ?_yxg)˔zçLU*uSkSeO4?׸c. R ߁-25 S>ӣVd`rn~Y&+`;A4 A9=-tl`;~p Gp| [`L`< "A YA+Cb(R,*T2B- ꇆnQt}MA0alSx k&^>0|>_',G!"F$H:R!zFQd?r 9\A&G rQ hE]a4zBgE#H *B=0HIpp0MxJ$D1D, VĭKĻYdE"EI2EBGt4MzNr!YK ?%_&#(0J:EAiQ(()ӔWT6U@P+!~mD eԴ!hӦh/']B/ҏӿ?a0nhF!X8܌kc&5S6lIa2cKMA!E#ƒdV(kel }}Cq9 N')].uJr  wG xR^[oƜchg`>b$*~ :Eb~,m,-ݖ,Y¬*6X[ݱF=3뭷Y~dó ti zf6~`{v.Ng#{}}jc1X6fm;'_9 r:8q:˜O:ϸ8uJqnv=MmR 4 n3ܣkGݯz=[==<=GTB(/S,]6*-W:#7*e^YDY}UjAyT`#D="b{ų+ʯ:!kJ4Gmt}uC%K7YVfFY .=b?SƕƩȺy چ k5%4m7lqlioZlG+Zz͹mzy]?uuw|"űNwW&e֥ﺱ*|j5kyݭǯg^ykEklD_p߶7Dmo꿻1ml{Mś nLl<9O[$h՛BdҞ@iءG&vVǥ8nRĩ7u\ЭD-u`ֲK³8%yhYѹJº;.! zpg_XQKFAǿ=ȼ:ɹ8ʷ6˶5̵5͵6ζ7ϸ9к<Ѿ?DINU\dlvۀ܊ݖޢ)߯6DScs 2F[p(@Xr4Pm8Ww)Km endstream endobj 482 0 obj <>stream HX}⻆/` cA endstream endobj 483 0 obj <>stream HV}l_lg;|\ ;cppفt _)|M UBm1hT, U4MtJ;vbb$f9u05J}Xoǎ}CZN1Go0pi6}幞];O;X_u8<;Y~=}C/m>ulښw>9 J+:_P]5_ξ] %c)80k|Gke`_C/B^o x $\{Bh4."& P\DMQptew[^_'鱺v Ǥs^YQd`cMPTd|ǯecU@7C%K IP4][ #R`33Պ<i4e||NVAX _U^d3;cxːʸZd\xN= C90i(L˸X4t#w7|v;aF4V\"d~]P(zdbӰ_Kˉ@"ĊoKVaUE%B gbӅyoarԺkb!@ayҏF̞?VWZtje+njq˙ |{a,-ښd)dv|ķu@5Tx*XYRk!rj7˜y'WffXY̜x1XICեls88-cYU_.x*YP`+_"Fŵ⛢k۸mVӐ}qϚϿpVnCBN.5_U ݢeT*!G܊QNΚ]7ڻsW<׍?\]5z<7RGGz1 z~\ڸPj9EYXn$.YCC .;QjYJ,`PPDVIAne֫tc|??̏O9u᱅I&:?5F?| . _]̲y>xkDhFXVGytč kIFsZPK2'qLA:Xp6&Q]RԺoRA t_~~ec}K`*Em}{=<=~ŝ/߬[iѾڻs=@s:h8~Wo78oŏK x]ЏkiFlZb٘8啧]ML7T1 K$b$` $w#n`tS!nd=4.HK nf YTՆ}{ī[pKFvC?y+R8eV313j/U3]r!Xjckjv%zMW`s}nv}[A|=qǥIvOJ؋E}x3}/ޗv ءCDI{ ϸq]wX 4p rݮSYd$H40!KnfhAf}g9 OJ@٬= /2uG 24ͮAҰ.cԜjyՋ2oZ{/4."ށMĊ"@ٺ,3,AyS7Fn6,i1W9YRLNx\J $b #8q- }N x#:ۧgmT8z[<8z1k8l)M_61ܟ{!-@!p!cHH ,r ٻ|;91yhd>xwkwWi? {ӷ>6r :A:ZbnLM6០u7,\Sx^%rGiR&S2oS'Gv-{s@GpdVqAC:3l`0jz*Ա %'$7 _NU=sE} Aqsjb'%Έ B |!^fS4GQQC:j`Ri, Q0؆>TW2e2`0P4"0bh &Bn'GA,9_<`d`ygMIGMC+sWJ>\D[a$8bɦ%OgSoFf:Lf̋JCXU%Hg]fx2P LyQ?I55fYïd&If4M3cz)53+6rs}\C^j}_j0jHaЄBjPdȄ:rIP q5`6F\͕r s9Y("6+iÍ,`>&2K6SIReUIy,b詔W4E[~_HS j&`1Xnw 0ʡ(/* 9zE` vǗ`}zD 56$,.ѕl~ÊWfa=K77zνzdl=3}ع;ι9NCҺ* %XA)a(Z1 lºJm$ƺ X ?EQ7Q~IDzAWg&0r|cʢu6}7z+)`1GaGqbG[S_=< w}g;gM( )M B会/L}JL5DPXe,d6)Cf6k:wQss9OM/=~|ƏtP1b8#!>IIH@9 jqecg},"z)9.G5&^e XJᐭY8,`81`0,dE,@@2HTn_< p=A٥O MN)g)ė~c# p{S*)ْ lZVT?>aJIg4kkȋ|J#4I7Ԕ"zF@ yE% *Oz裪! {xn?ƏY]љώg!I&:Ӄ>sޫscN_AX(lYiQFDIeg6k ;>=&!K[VZ*9%S?f1d@)d!ka/Ԡ΅=_ZMxRqxN\7`Pss9\BYfvd.~0i̺+@Iͺ5V5x %RVDx,Ό˲-f_}J:.4€J|fnH_|g']1Sna @eӷ5שh{%s\n?S۾j4ܶͅv2k޻ao޿\ Pw{%,Ø=mgIsCo5  %"Dd$) 1$#*I1vwq@0$p5H$-:+r;@z ]s`n"GrQ1]w ;-7RFܙh%$dk@-TNr% b  3|0)};/O<_x}~~Zlv5HC2ZKE+x,6bOP"G'ri\u%)ζtMdd(Rs+Jάd[V&Ҍ44:/Db ،U5#S E0' 5ld7})\%ED+ǯVwToVU_rɗұ32WnoExGvzn܈v7&'p>Js*@*~D[U٢mI|,$߮}YSbf9a;oe Åg'rGe{i(_j`l%LCT z?gP2Ԙ;3=!X#lh1y Ÿwqo0v G x+4af0s]Y1lCͅQ1WW,⚯k47k&g+rrB1m+'[!؊M-ֵj+փe:(LMAӱSKRFVn9DGSϤΣ7[+۹OO4ˠ jR/ jxoLt1)sIt|@$SZ, P- 7PNG׸yT boS_zDK̎ mukxQ./gIٻ"eSˊY5T qddM&Զ*lе}xwKB-rBfsx` O7.#cO;g]݁ZwT=>>f~lg*K;>m<y6KD֮u޸[n/Gl<9 Exm-sUrǽ3ǥqu\;LN= 9S.E b8sdLIlЉ:q5QFƖ^vYn[vT˽pom0X, xl, |6ƪkZu?8:EROk3~_jGt;Oa a!tۺ w.1śQL1|&juy.Gqx cM$ZDmI9E QTmC1=W7ˢ77Hgva~lP2=VVSvC)X__e˳MLC|Np_-@r/[vrrJrme DkzJCgU XẂ{=ٖ,Vf([J=<,1;] Y!/n\l[!5oy-@:ƺ(v[PmA gG<`!{j@2 GG2~*+x%O3LT,Lv(]Jk)_#(NGN[Sm^zE{S$PxBԮzcUǶh*~h’J@tY$ $ZS@x : <i5SEEl.`L'OR$!aKx0H!&O BDFf,e,{e,eM"^1A8$I& tFˋWcB  P3WgKfYoB<:,^bKl >@a ?15_ǢRN-\Y}Ȃ@2c,-3b Ͱ&)1$7QξyTuR1FQ'D_TlϘx]gP{64f,0O~҄vAtuqwaCFq)d(SsT('`6f00gf X' #PVVv9[fDnv#~g/ҧ/8F0?R$'Ԥs/ϵ/^RCCa8u=8:9ş_SOj3E"{12ÿ-\^PRWz]c1$8iXݩ ǤȝNzu3rJϪ[5j =Hp jt28iD *1V,)tُA]n񎹭R [E&' ,/]f17WvR_ʳX}yj4sl i'5J M j;(ʼ"h:,u:ib־8CfnJjMF&5nчup_?gXZcD` ]uz5>r8_aA[\FY&cڞW=S0M[QM<4|(LՍY(lnP]94Zau@0 #XP4˴2o/Uu߻;'sugl'Kl'c+:V2 45 uY"MP5Ԫ_jJ@0`Ħe[Hlb&)ecidwm{^( d#j^VUj@`F5.aN&E"w2іN w.ՃTHc)<;pwQb@!Lþ v(ħ\ ?mrԩg{njfJl;0AbY]:wEC% Lppr:i  t/Mβ,6ɸK%;SjV`C}>#5vO5\LqSZwMM&~-.NhQAܢhBFQ*D-H`3! dxJpK,%%[S\2!Q#FP}׉@n& 2E.:$O(I=i$Zr&%w/3Jd*@+j&SLf@ۂ.G՞@jߢz9QNRhAX38`Jqm0\}^0qHgtD16L욠l"N_ǿLeJ(L9Gh饕 *ۼfGl.}XS޸]U]Kt/a\K" & NN>@UĻc8FRN3QT׃L|iVHYԋVӟ!W U䲪IV%*i 墶}d NA@K7Kr,lKyazA0l%4o2IcQɜ!j ,7JsMELe9$0BP[/mvKItN:&QQ(RbbT(XD2Exn zd\gTwB!e[*އ:!/UkX$*58 ψsh+0Whw8!;rL)74ƗzV}uX,]2#3]ѕډ0o&cu,ߢ ?pH؀Hl@Ja w Ch;= y(xe'ř9zSkUojsO ׵%H +:r 6² I@DmEOn_ur]&CP&ȗߛ'?GLa1߲\\>2lb1''BbNҧwZ)wijJ O'ΰ[cϲ4CH1p89hC㺃s\c7Hʯ$\4t->Jd2q] !+EE>BaKPF-=$e ǡdJ@Uw ^"JiXq1^}^TN'f#7~{]Bm؝/={̞b1Eݿ_V\/~o]h~[8P1Y+an,fJc6nLPΗ.)aЕCm/. $ hڢ kE8!ݙ;TȽnlvw}!jJ=Z<"m 펏IcCdE+~:?> z+|Kjkk::-wS_^@2|@K(뀝1PHYVȺfוsʜr]YP(c{w{w9پK:؉c;bHAiY)!uj7)qڢD6PhdLJ5vcҚlBaDL6_2 MPʾ9 j{w<®KFT&io՝E:"WNwrδ0@Sn!vpsUUx Wr/h%Hab~\.0%4 ?  Z[[q+.)/fZdmŴR)W˴}}?rɑ/E̐L`Q L+`lqb5g&\2{F[&K+pxȯ$ׇ4Gu}}؞~ś XKPמ7:lyaFKEq+244Fg:),Mkb6+3p+ R`MPXxQ^s23D0 ^q=7< nLӡ A.v8 Ɗ@)f\.8si7lq.QL70K2QK$(r@I(}t3 )ѐL,l;*KJ96..ig Bl;_)["MJgȌ\p2on3ͧgCK$4OYr<΍C`G9v-dw#4qƨ 3ķ03u w6 ˌ gy$߶F?P.RU"!VTIq%8ՑmIFLV nݪ*\ßr| Mnvc>wnO޻q3U.ㅎl7>''{)EA~I(PE aEo6p8T82ipFQ Gm:0ΆxWӬ8nQy0CG 9dǗ'Q~DbP$SGdL "cOi"41 {7uKX-]-7KQ¡/<y"\a4-,{2DkgbȆ])L'̠Xa= {=O+eȒӦ?.qGGq<#zqAiV7&|Z"d+/|!Z-\p#ܘgwsJ>e/Jg)%-]W'yh{76ED Nκ~StYа`mug?􍐫QϺ 8hqp4!qҸtSUɒ`%ҔtKӊ.1+>iKٮHQSЙȰJ{}3Am@ S!ajTb ui=Ayڊ y;#"q>V&f9N+]Du.Kʁrɴﭴ喽ܒYR䬮>SˮQƗ*>84CovgfH#(uAI+)[[n#ЈxwdTX[_TޖC?ޱ`L~z]oM]!%k(m|))ZʢO`UıD SM[q3`"Td\~gtuc{ko^H#x<{mVǾ*q/SZs;£?ázA)v_Bl\xrCǟ~W. ˙Oo/LImmJ]-9?2HݚRt7q]Zi%%$,iwm${IlظT )3I8tھO2LCmi\Cg>Fi&LQˤlzΕ{ܟݻ{}jNt,ģמ}3\%OKiE_@gyqc tc_ϊj1!52)-* ʓ>]t0$Ƒ!#5t|:@&KKO `pO0{6LS3d1s.a32yƅP)viL܀r0**vɻS m9$wD+/V1UwӃ&:5g/;P+;z12&⠱^OȥsSq~ ?}Fr[" ;Z&Y°"kaUYZì,;q7N|Dzal";-jԍ'qz^Ud hʹ*d& m}TU*P5UCg`;Tb@@O+L'ggGspLl1wwocb);vL `$Č&Tf)[)L&'BcJ%XLgBLx2/U2^Mˈ :"ӇCѮ*m$P |f/nHxiJ&!Fs졻@H'_;i T!`䐃$ a/0큑S@g?#@CzɹAsO t5EL7vCAFw,%t]sxp_un/2ZY Ʒ ?JeB--R>5dW /߽tfڝ=grn8g[q*K|ORVtW~͙,/+JIu潙}3+'=}}q={@ﷂalr9LL"/y9r/dتRФ[ ǐtĴl.$Lkt-)ڳUlH G@%/I`:̃Ù3dA9 csy&.{vO:]o&x-FCu0E ;{Y?GKc< R‧K.dO4֨[n V40>_0^ǶdTR4 zD Pi7;`7 Bl\^*X#B]f+"X:]xN+f3O g#Q ]%(?Iu.O¯wx+PO2Z4$' JҺ\NZ.% br-y7iM.l(Q/o5jP wXːT,>?%Oӄ lNol;0H T>2sR /o <]AʡnZ|.޾Vڽ|޻Ye4jyWql{`OOt+zc_3UExs2=:RQ/_8遠Ya`[2tXL)qSp5^Wfj_xHa= 0>nyx ·;wbSe : *]u༠ll̉h :Fn*EFKgu|i 7 3Mao0a6t:nA5#ch࢝!7JstON`9JrbqԦF'(x__օF4Nf*wW Ո6 ˰"t^$"+#9LcrlE}@vY&2Y+|Q~,+W~F]GĀ7$7X؄]9Rd`8Gu.y}ަ.눈^n~4;=8=4@HiCV⳽ C݃ږz&_G  Đ0Vv?RBRuaHJ$?1i4h#O)IV RH2Bʏas-8ۦH]"ko z#19'O#.h.xל.7EeN1FV5Xvi|BQ)Zt3,@C!{(P]nlC*;rtH{DLy” ?U}SDbj/VчLGD w@H r[`0ED4ܕMd 5'1389i 7133Aiq.6"eYCGI.*[ Wa*J0.GX$ g \^曝b:,f,<`%MB0 Y )\VhYV@6R_H#F=FٯCRL`6І0%ZUz #Ԁ5\Xa 89"l 1gvCC!<a hQ馺ta.%*?""P]]|k38gXevo,55c6^b'#NPL1_9 [Vؚl++w~ԺR:ƠcbWÜO}zzIs!:]T@2 k+GDM:.b&W@݀F6%88jp#p7Uzg->[{'bhZ0;<-jClA6nD Wi؉0{ى⮧jf'pODk^MbP$Y{}B}TR0bZJMaW-uXf2_F\;|9_A.aԠ߇Ʌo[~M"EQrXڟIZJj #Ý!49 J+d!꘥o+ER7s2$%Rdu %SSDx=nPbG=r1ܥ6Sz4Xg.82$PvN;FE&N8_?--uf1Yruut|/.-] ,(N׹8-S8 a{RE2h#"JyH*NV} X5)&xBB8 :$֣"h;x@9g :>D6@_DbcE$u]:t]Lsq'G{ c# v>><jJ wD9ζ;e.[pw[5p9 uףNX'FD;1p"i7.ekDi~KON[鴪Hn "*n?^qx^ ۻeo;5QLk {aR9*Hfc_WPVh k*]Q #Z@\=J * jRbjԘbK?9SKOtf5_x^ -dr?wiebgaʎ /m_PuvcT<Ç2U.9T DIS*$(&pyp 7N/MQ2\)oRev.,yC!E# @M(J2HG݅0^>;޷ ~#t'oIHIv#^w)d}l;g|~;sr'v 6ЄUڒTy:hbP`/J%&**6bbiK*\lDvS4 :hsNBE=;F1&tFv# l8i^z֧I뮥'vҧn:ћm,>GҮ᥶oׄBQEq1~[XWnA-RϒD0ـ҉(=_QMՉ 6 !;# wmJ7ۓ8Oz+JIBl̎;`/u{W- q 1LvwD[&0ąC{w~{k[_zC߾9#__뇮>tKأ-;h _]L,Dz^t315NTG!gWIrCwY3[Q\@3|0NN\r"Qb3w!ѫa}(]4afeq`vA'uzsUamiS97a2^<<LBUlf-jf0f/XC\/"n[p.zC.EE7KAл H"䀡IQt؃LH)&N;‘#\(N;$YO"")&O$ȆԭD'g'pZۚ D+JR=!^/v~w+vdْ~ؔ==:6Dd}LOeX(f`qRru5V%YUs5#4{EHdo_ܓ+E+zl )UΠ.o{=QLS;ťSamy񞲚ɠs7kC槖_Kmr?Ml%˛io!,uJ։l80X9#w1!1!8'a?Owa@ȶ0*22, ư"( ֜Cx#9nbn&fV"ڜ`i~;Fw(ɪmNwXC 2&;1q+9f>*N(SDrzR\1+/[g"o >X_X޶//7|>|hDn>F+ʫ˂Q)%m2-22|if+BmzTDi6Ni8E[y5z8e-YHp^3EDuTD SB ALf"5MQ]?.'9G F x*"d.kor taXAaVuDw;t[a)zu[ksSfp(L|A_z(; PtZk&"v_30I﹁)0UAHƌ 36|V#My :Y!$ir.4BL!dTtԌe\!YUk5"0`AbM..A`i3J.dI.teLvvUI5#ygrIqN73$<Fj7yZ ZU*p&{ )IRy+rhط-{TIű\ˏ2#H>?ϧ3I}k})u)XJB`ci4MNSp]gڦHWx)wD%^՟M=J?W\(,?p55Su)˻;U," :Emo ;WMAÓ'cg#d$S~/-RI*b1Af"E`%ɿ'?MRƅ#%4ض:|>?vujN8ٍمiht+( RT*&Ƞd31BtsBc{,Z-gL.0d '.,^_#o4O_G[dA%?=e @ j/#Ƽ9_I _@3^`XN3/˜+zDy|^QMYh&؜`][Ex/%jdlӴi jEMuRB l] Pl}djJ hLSx6,[2hiOvx|]S0ƝՎKPd,9Ŏʹ<@\z" zO񅽟]?'>=Tώ~f/54> K?ʁ'=cO-$Oڣ_4 jP›dwdL6e5*_Fʚ/#*tRTAQ*U RCFb(v/! /pdJuÎSjzO.Jȶ1ŻDYhZDbDa?v50-Cm#ާKRfR&⏙pkz}YZnց 5&'$V ~#r#}:cIdߚǫE4KsE#nDz믐CG߉q ݫ1r` Bn'>91^T[Tz43[Mj.eSmRZbfN6YbVM CͩPmH+LuU} /w SOVznOȎqL<ٓ1˩}暫:d#jg\J8O.#M>\T7lЉ'-`*+d!f#FT8yŠws 8)V;㞃ؕ ˫Z9'XԂl Vu;:^ (C~nrePg䞡p\Sb/qN/RhTP*T&=t \n@ pb4 a ់{PaASY+Yh/b6-њ*0,P(b d."BӨAnľ^*QCAwQux@QT@2U-rGjI2!92$R" |K@b $Ni`b7bAM?2*1OWJb*cT nb|x=Q-|)sۍykc/̡&ʶz}ber <"3 ah(ֽ{20S^TS;579Np zeme;P10`JB5wyuEZW>%>j+݄ TzHBJJF:6Fx[QlaTǹq ύFW_+~QXԮm4v:1ı1'Zi'2 udf,;9n~5w9{^8]>UpReLdANB|V k aL8vs U5tQ[B@x? oK5oeGS~¸L3<7 zf /[B7n4< EbHLRKzCnhFY(^vdiOIQf MuE@ݫ v0-5A9 r r8*ixQ尋qA5o[~d T0 ESIEPrVΒѫq'rNQa0o\ٸwNmx Y.PY9* yUtU.lqri;$!r9fK[Yޖ]ט0RI85Ag9L."*2'(+ȣ^,Rގ#{UպZ'o^RJj%ja Y &Kf?ɄZ6SRϗe[%rŜLBv!lj̰uyscx~yߜ4'm<Npi78q1(IHCnB<۱BؘiXWUJAC[KHA՘I1:jIm:wNH!I4y{߽oӁȫzvg+k&+jKsps}s@勮iqwZ8I/8L5d>Y[ĂةC!MzlMӄoF x>*ZD 8](adԷȪCUoKKKq\JU\NwCn4[-qv{S 0'0D *t`[ZUM+A zg7 (BQ~\G}NK AjS:۴U%bԣT嶽> > KGmEC9B M`) 9$i0|3x9x-h >z)bu'4o$NTDr}RK]ZOխmZ7*ieĝl )e]j8>.W^W_]k7^TP(*UgHUv`p(̡V*d1_h?G+ju8n2.ߖgg$vJ{%tXk1} 0i 8|P?}ssw[ix|qmo~.d3 $.O!fqj:$5ⳋ.$mϊ\f|`UT#صbQvW7_ZkWHpI7]ٳe[bw7_O:]Wn-JۣO&4Ej>6]6J,{ub!r > rlJ U,~RIT51W%y}FPkfՙghGFo7Vc|g,D2-Ũc]v-!uf7,$/nH~M_h])/>|,sB/b~>,U0aXY}QnƓMW VP't b}{ : aS3gzO<`W`7es@b !~/ ph8]p8p8p8p8p8p8p8p8p ?« ͮm#zHR6]OʶeM7$MO-͏ml6em3ڿ)8K5 Qh5Љi ` g3 hkiDl ) o?/AQijX ^Szo>j0zYw^l} ۞v(6ӐD;vKoZ\'1$_'nAL`rc;NZv^_Kh0}NWZbYa_zpiYUBkLC5thT}l4\!+$J\6B7e2t0TZQ+RZSMW&μbuzS4OXʚoQK:t0ɏml2<'sYm"Ud= 4\>ޗI'ZSZM&#DAٔV%6C7ԲEm#-jiKSZ1YH>RZ)#;߱54ӊ`n,7~ACus 6(Yd/TBЋj1p|CSߏ3yi-y *A'U1/. r*^xPQ0@ZF]#Db#ZcKQcLp͠cbI+޻5)ydw+U~7ufrEkBXVZHH]L}|hgz;[@1^Ayr '2sN%46PZK#=t!} 3-v d tƛH5nqrco*-5 G:EO1P$d1E,ֈ⢸#;7d,*^yERn@.Uzk5Ś#g>&w;CfC)c-'ĽK^]``! 6_W!cx,9s /R~^m1>n[| PIĈ>4Q"1(3P- 2[ r<,)TF)TRgeTi%Βn)|jcyfX[s 筦Y(װ{rXN,V ȋYp:@ETP︖*,^iD f ҇2]fH-q0Yϒc;nCFtBMsn@Pz*g[eX/)qȠ#RXO L OB) (}Q-mЭA-]COۑYu<,SNT,r`ǧڲGal#Rvw[;{ߎAUEAk;*sVlt~L,m<y>\50w`܉G)߬DSjijƁ4Q]ԓL1ZG8أ%ou;rvBK2sx( 0[$K0V@iiҪQ{J(PW"vUɟcv,/T!54FhMo6a;qD3p8p^3'lb0x0`$+ XNbLGW>"0%@XlD!m[E- Gi| wŎIgz 5jj?0fY3 gLsԂ)sƧ64aݫgݺt),#$۽`oۦu/_ͬ`r:쮰, sD{>;"/\vJ˞Z2LY2ޏEGٝD^c20_ȴ{CcdwqJWJ$:'DGQ/l `D؜5Rv$eGG ufOr Op&Όrq9^Pw%encXCs^Q]Gxc;y.kׄ`(Iu EmW5i ;TmVXh1EKbdD,;rQWIpYL99{2k>˽*Wz?g2hV1usE/?l1ӝlvg"s!)Mh4xAmg/b8i&L{ *џmH]S9SH)gqߞT-)̞2R2<1xB] jiRJT(/dHQ!ɋ> | 聮^a49H5jcSoVG l]׼XRo?֝<䭘?,8` ڥg>Yс} | }f`\Ħ* uWQc..ln*q#&~Z,}s3 UO}Us0mwY_Wqwc3ҌMZQZ ;Bọr :}B)ȒW7Ԫn`wO&Յ-ڃ"޴;{ȅ5Gibb ,cwچ%hc/8|>d'#+e9s-m-ΙB^_|NΩ j޲9*:}ZJa:M;8gXTzhקP(-4E^" }#=?q7V Yߣ[)R#Ԉ}mȈ(G`oׯkΨ%#WҹP}SF?Po.8 x>CW"w勀P'<M*GbdbțO5RUYwY?!Mz3|z^d}5ATݭ9yZ-Xsimk[q' *dNA] 0ߎP9oHpΟOkBMhSo))tVQp]$Ͽ6I7;i/iK#GzXGi'N:Z {PZ{@D c-b/VhBՒ}"8rݛ J &)!B"HZ&  } llX%F3r'+n RB͒FƹsumgM ?<2 oL}0z0CU!^uTjT9':c2~.A Z#{S-,'Kf'i7ww16^y>DZۤ~K{Nj EP-b[κ(un9\,2l ޝ _CAr65>qM?8dNǡȷ*yvy0"PZvAP Kyl˲ {/vUAFrԭ5v.T==w5;Q흥R|NGD'Kw=s-ޅ|RqǫE]k5bǥƇ ?C{GER+D΢w gWKN8* 1#r`55i[e0et{pU^eC䇬?U* ~ ^ݓOZ=י}4@$a+,j3wH38=Fcܴ]}└:|YkLXX /I|.ܛ]ѢrR뼇Iay<;}$_C#ƏLrؙ$%0\}IO,2~lXR2t>O*&*N8ߓʴr30 jbڌ L[hNѯ5̻7uް_ghқ96f]Zklr6h=#yYۥ2.FUU |8K8gr!qJٳR1ʣt, t6nF8;e,kn<Qs EϗO*9drʌ/87[TxI k=lDLSX/G==kgOṯcb8v`0#83o~CHNŞ.s|ⅿ>['~x5YN&7Iwv??y%]-7V1[ɇb%v=,qb{Y,}2\PbQ΂8x&/1`c iS>NylEyy=7ΝĨjKP(JPz#P? ?kמB}kG{F BN:B}+1! ~ s/{8_uI7'(ڭlA޷`ϛIY.Mn!xw\|şi]|fnmvN.7鷿φ;x_6L| E8ؿ{0P]GxK\cNLm(Snbu)Z5&ω_  g=%GXEZAH58aM]g+_5rTmw_|~$?@ڈH(|"%_!=_D&$ݔG[rCeu%ڴ;荛+ӣwJWr-kS^ 4jn9}@ b R?%e*OI;xF2F6RI2sj`/,ڿK1Ļ丬C5)|#\9%NP}ky5Z#ǣyMG'cͣ6ɽ$EY_Ϻ`ɜ/e,ZLMBNHghO5:$y;ސ{eP[=jawc^&DUAG`Us6ڷs$OrͲ:󽷯ն@jJ)cLhA n^6Щ%!Y6-DdjNG\(NED9y _/?]Pԩz^ߟ?OhÜƄd?:H.~ʍ36g{6?WϚ>=~4gf;uvAtwއͳd,0ՠ\ %~nuONYbj -{|(;f{!O2=/XGG㏾nHf+KGf\f̙v'W 3gu]Ns!=.'Oϟh}.u}v2 /z;J2^AD` 1w&WYj'cT/n\s;>*$&O&]eDǰ'q+g$lzĦ7Q~v0R}r@7Owg;@|9śM.U?*]C&̄&)7R>45gk Oj_̥k\wK(6I䡋YB\GaB;me}/{ Q1x A;䝎]``D`-l#}i0 K݆}w|= k.k7'\vLzg^^˹nMjYNfOFCKQ%dn7ZM-|ZoZL 7:z껮m{vOVh]u9WƏC68I5Ysn!>Hj/_QfZY tk5hP;04cnCQϬy h=GMbwaU&9`C"8;qO˞6R[[ޚjɜN bvZ`l0Qsx/qN* orjsg_SXoXOy7p)cϑ&޳w$O-i;%aGώt2T۶1lr^Ǚ8.z{^hw2\oG{;69eZp1Cl.]{SshcX AgtϓX x3L}Wf+Y$ *z`2.Zkܕe"tߒLwWH[Jɗo{\ll&==3 {ua=ek5^9ĽJJ%}=@Y{e;.t(e.)Fr&Fʼ"!ɘzH#zyDzQgE붑(߷IWO3GmwxHºDkx\wY$f1Kh#9^0Fײ;IzϢ;}5=1}3|3g-b>x݃2ԥqi?){n'SO)k0Žy5 zn{>..:ьO~Z|mϐ?ڬ+Sڈ_9g:vm3! KBO)O_:Xe> e|`"dd(vԟFyk9nH |UVo*g$~e 0 NFtFs[svn? ]k{9tʟR{}U0bJ;_?iUqD/@{<|ӗMoMN~R_*ޤ*?_gG& ٟz}](Y!KywFx ݻHYS$R;x3o aoƯ_J^P$+´EWǻ& k@y ] R]~ddoY^+-02&5%md 'ά HfTqjK{Im*78w)7rkEp ,B{aj;'c;%Ȕnhcet9k3De~bZWS /&+5`ro!Mk鏥%2TiƗ,"qIu r$|y'"W8CxMK,t`r z;8InoT'Τ`JjY+pU[,HW;c%mj:R/7a*WI:yz)IAB:|Y2xeEz_cKܝwq)/ਮ2{w7d7!BH$,("P $35v$i)L"`&*JyMiyO;:B,2Gܽ m1{9CgfB/#=6$z-Epf'dMdeua><#ot+}Eums|ӷ%G7793y2&2s7طTP{NE0V^3.՚yc=j+]1}کqjzɀwSy̵OacZ[\zBiԤq{H2d|@%C7DۥٛMxwYk+c:V ̹;WflnK+aaHh/|ގRުo:[`Ic Ef(_#A_L1 dslRamu7U)L5ޝ8.Y跉WLYLT<겜 R)ߔ'#ЛlGd5g*qMm4!u[θŔ=Q3tI?|2ᾛ 1}{lTYRf3m`[s㥪x(,(Z+ԫ"ĽiRF팟aXoweV;[o$Ed&c +OxqWwHf`dE2jop낁LNwIkKNq^==dBB>߭z/o=KǸ7#Ed˕) s}Xg<ڄ>5?7kv4qNwź}%[|yM?e=˖B}wQ?\{q[ ox)!區8w4q;\ٵe[GY_jI/_7A/v ?<-!9)%3eN`2_ݸkϸGϸ8z}q#AfپS o__} ]:78Ky>.#^ pO㝛ֳA5Jws;U3fBpDQ>샼@"=?OcU:α)giх uOwL38NS]KхzOm R*8玽uT;nTj;~ÞI]hKz?5z5cny I=-u"rC(/cvvk3N¯^ÿ^\ffAbJHʄG3* qɕCOq5*\VJEݐk`IR'U3gFN"G26 +00nZor56}ⓉL|ȵh*_1ꪺ&## #wiTaHXZ՟R:Zx"u~?x _DzOX^8ͣ}">Tj[rcBm!miEP64 mdD>iO#Z)V ^&! 7w],\v_& Xիj]ldC6>,RzZա2U"2ݓAwۺ&[׮MHz &hF& TMlhQ'Rn2Td[33*"U\0X<,y;\]bLݡV{Hz_[M }sɶ2=ּ_6fDZ`)0JJ$;v)TnUo.o*WO:gWe{`t A|&-p I6enEP P 6#;Kt}ۦs\&V`Ǫs;ksOv^V>L1v,0B5ԃ-=?i ,%jyceb(^%nMK,]v-IzQQiL4B Fb"S1V>  Fy#o*h‹~~}33{{i+t)qP`f)'kEp<0ixF(+3 D%-a(P?0i7'ߓʥsRy-I*'ie-Lap-SEnd|CvexES"r !Fx>2@F?(Е:LN1Y!Z?^4niԁ}To"U7Hy#|Ls@?n!x7JqT[D:GGi!ι{mtX:hlˑB,Ct /Q6x:@QuNQiv~{]9gְ~|"jx{@Κ5 g? U>h9|3w5 zj#h;K:X˳ɮ7&v=XΙ)y9('G 8ȐEfJeHųL`9uG`}DRb P|+Xw¨3X#Q,ڈXeTTu:TuTn枹p<<:XiZX N󻊹Q-Wr`{Gԑ⽔GڨҜDuxw5jua{8 ;D\ꙸN vv{ri.暶Y.wK^:rrlBK.g{d]2x/F-+`ՖiPlRZWuɖW kYdx>T*K*\2loؼ#[a<@o҆֡И6M~*z5=jшNZTŠדcgs7fMXI77Qϩܐ:NI*P ۚTT D\sp+rNK"㑽>ID:"vd 'C|Q :$.Y 9 g/opBla@(^9$<.;d[DVRic!<琈zϠo89۱sM x:s:[8pjDᨎI X;YJ}z%T20L ALdr05]a=*TB@ZK[`(g endstream endobj 484 0 obj <>stream H\j@E^&G?8N ^LƙGD[^$0U֥:_m^6~Un+rϡೲR7Jݱ<o/YBiuǝg{% *ߞKgUQc_Eaa<ĚITL7rNB-5j_;7ݾۆt]EQM5JT+MBDNSWDGBOVS"]@/ h"§ŧƧŧFݢk@KPfp`iii1a>|=y!MtZp$aI‘% G$jqmq]ƵY3/KfX>u{P[ܝC M^4N*V]?2Nܡ endstream endobj 485 0 obj <>stream H\j0EY& ? c./n?@Ʈ,K!Hp3:V`Fy\K'mX^2)rqphY]  6~934 (^Ō)H7:"rVD/̄ΨT CI5[8]tYQJt>stream H\]k0{Ź\/mL "tn/~M0c4o`CΑ$6LU3uюӉϽҌtW b,Cm1* ?4z<*ߜfכ3=|U͊blfJ,Isv`Cۺ~\,S"5OUZsHSRqOֳ mN}.o|ydIt$mvPm T$=CHB=d9z2a"rJHb"RKH-Z ævﺽ9l᪄To~hw]W endstream endobj 487 0 obj <>stream H\ۊ0}\cDhm ^8v5h/|K6|Ʉeut7Bk;XUĮtt' W}m%_q9 ?8ٙpuنlolU^,܍"VR׺'Mոn7./s61QCCZ(# *Ϳ4AڵUߵ $*>/Font<>/ProcSet[/PDF/Text]>>/Rotate 0/StructParents 3/Tabs/S/Type/Page>> endobj 2 0 obj <>stream HWKoHWԭ% VTz0X [J2_$G9Ţ\"Yɏ7O}~ǧ럱_aE.ۻ6RV۽ظw1iq*r-DJ.N(RK+[!D*% ]Rb hh%M+n?V5ԍD KӖ'qm^ںһ UdT\>n[*>|߈mɷJԞr>˕YU 6lu1B3˱èSIu˫:H H压۫&LMe֊?E%EERU.T% g hpIȰ(}}whq>TԤa1!(!dF ?BB(ƶ`N@` 9jQ3F#](v pncneuBkJqJ>J> _xܣ+:ٰ,/B{b>4ˆrhk6GD'),Cc *d*[=A&{!yVPHyF*.b `g]Қ+@}#W6lj/~'#&?V/X'[r|7iHMݘS/7];1̫Ǯ; N7zDaUq@ X?dex7uTms\z ^ ɣ{.fk8+z?oq;C)[#Ʉ204KNAaJv6 ܦ_^LEm g/¤(دe֋PЊӫ#7?/ĶIl:J5#5;b£ % #%*oi|Zmd00MG,I@0bIНl e>t{RM@|Ϗ0t@QINBĎK3!DnaJU,$ȶ{w|l[h08NPڹ~T{)E 7r^U[@"ݱ1Eb-|oٺ_j.L#$A2&Vi)lı+6htƇv0l.Ef\kqFmCILIQġ.0 W@gbHS+Te0́+e掔]Pw _}!Z#͂dq:H¼G> IJ*,Q>:;2x, iAB~ Dִ3dXU Anq(xs8 J MLjlO`Gwё8djGfN$#NB&d W:!%t&ӊu<Ҝ0Qg@Lw"/솩u'[}C-DV( !<v,FX0|ڲ/!$쥦d4TAa(fYⴈث(+#{}RTw{LĖsIJ 8ң&ߩKzmy!U L6V8a˶>/Font<>/ProcSet[/PDF/Text]>>/Rotate 0/StructParents 4/Tabs/S/Type/Page>> endobj 4 0 obj <>stream HVێF }WqXΌn"]-XEȲׅW2l}Indx<$1w?=2 ?Ο`r!;)Y1O X^O(8B:YҊ.(YxN: x"4byd6kF xu :%llP|he"Bee%TEYŐ,QAHlSl69{VDM|ڭi|`LJ}YYKv[l觧ʰ[~=%v)`]8/T~y^ =zΈ+~MHЭ8H/"̫>-{閎rYI"v9L҂DbwMK[{YlюYaf|5@SyMi&P=+`{(w&yQKr/la#gS>Q6zmdPp֚Ɵf Ǵ5.QFxZ4j8_pϊ7ME\ N攂pbKãcb=isBDŷ}i+vd`؊:QiEYpJ:|,%=_:Avxr2emCS֭TTuIT:sc/qP (&>)薕W2͞x !Z`SA3%5Q9f҆*nWvأ}tp缀'&6lw-2Θbd`w35aʜ\i8j!8rN,X,:u8\pD(06#%.6q~F ܵF+X5‡޶ ,K( Z{gDsjVc3!W72IMc N#GVEh0gj%]< pxF\1BAю3Gt/90RZ" _nzCMp@tգyi3$~UЃGG0'%beଜ;+/ye_7D35`Rg,+8aT^*!`[w ]z"*UT>mI Җ H07lН _h?= M endstream endobj 5 0 obj <>stream h25V0P63A fBYB(3 ` endstream endobj 6 0 obj <>/Font<>/ProcSet[/PDF/Text]>>/Rotate 0/StructParents 5/Tabs/S/Type/Page>> endobj 7 0 obj <>stream HW[o:~ `1DݰE9 tC8e9VkK^[NN΅.vl ;4g8|sC|t"?~~n>\ 4eiW_:3a\qWww!0ݭ?h'IeER݇{ڏssLK#_$U*%c8STHqR񷉊ǖa$*abDԠJ8e^'J$"2Tu:+^|{_yG?6H@{G_U퐡#v$ ~\hH/Œ<`L'4rE̯r|SjvS$좩7\Zi;w( oLB_AwxH1(;BУ_][vwՒ5L#oӱwhkwk?H eOL[E>6DC o~Q(nl;Z 8"#7Spq9sq\Iʿ 3%@44 $ \#2Nr޽1,Tn"QBZ s7BR۲;W2c /J?Hh?Zie6GE<XPDQngJEn"97Xio] @ q="K 3'#*܉bz J@gT&8o_="q/F72MggL匡]9vׇ؋iiմ#v+"n+F"S'Ny3sېQb`jHEf=_uyڔc}aR y4 a1r8m&;ڸe@hW Ke%|>CqZ9gdJI"84_A(lD3\usDZt!Q/jFbo2f2ӆ , zحP:f|=x ?Wz%"1EgJTi8?'t]KQ! m9 -,m,ݦ>f2L~-!),!M!6I5ˠJBvMP<gwԛeZSm*fdc7chtJ&7U8J% sF."w8p148Fl6bS֧65~i6[͞Wf)L328ֈI'O׀$LGo\.׽ @ Xg7=UjVT1Ջ|}=o {90T [r{jcY~-4 ҄kvv5<Ҡ$JC`d\pO-r+Td0<=Amt"u7|,8tG5С,S nBB0MP0y(3bU(MaP>&)v [Il+f"\;jyyZ xJ ht@<':5pv0t0?}X(vNv-K!MmL!i^ ֖9/&)})uf{/FwboB@ā P:d%qYZvjWc#eC?Ȣ,{l!,Ya[}x\j?Ea7ΡiqW8]*4g` uuul?-@-Ks{CW2ZaCjCu=*FΏVLQ]j͂)ίhRho,|N!WaC ,0!6Grwc7A2;rqiI11NbYV,15BI*mZa<}ٿ\4-ud ]{!_%b5N bܸm f! k:$mmaL7 e1FUP& SxF).>] #dx&WÞ0ֹ[_18r%9_R#7iRlfkb9N\`0qsm,14\h9+x!Mk<`B.gURd-5iiW"׮,thVa< ٷ yEᙦϴ3$l0^&պi2OhTI΋hpUfr!09kipXq(Ua3D2fݶ|4g TjQJ}.kxp~<@1.E76X V`O;;`^mXݶa +Uդ%Y͍[@YF ^l6/ ɡi-9˛7oAE{ن{ E͋?[m{NEgq2Y3U?ت!T|bˈX:s(_ Iڗq`yFAd5|asEldD 7)Q犃|SrG:@gיJ3r h\nY 7v$tb;.[';fZvP0!uU5ջgіQ"=Ҏ" c(iIb.~〝c28V VH;i65/ykgy9K=D͝bX$IHgUs`WBÀ8e֋}E8kr+^E' pS`rF-X61FȤFї>GtIBEx3tgxAnn)1ٰ;igZ5p)~{\ TCL*GcM>stream h25Q0P67A &BXq endstream endobj 9 0 obj <>/Font<>/ProcSet[/PDF/Text]>>/Rotate 0/StructParents 6/Tabs/S/Type/Page>> endobj 10 0 obj <>stream HWmo6n`IE萗j`PdyHFl~^l΀"K<]EmNFs<V<ȱr*: ,Yh87u*NRf('VVP7rзfxebbeD Ս{ȜƝjJ`>hv>UxRSj&g@+54>w?j`!v?^EIMg ^')%X |Out{HT@JIkOj8 ^aY'Mʕiwd5!|f'TS $d'1㬀=qsA%\ v"#qE?37\;qQ3y f (ֵ4EЦ:9Z(oJTYy_gK!f 2vbtùJqWqkR(I}֧#`k=F&Rn/ı$گh3KD0FZΙ3MIqC 'qC3{ ;06M?1s BabΩz- KRY՛`*m e`±$`TlbyF-3A#g؝w{g;ߠ/P< JħTjnA=J!&!H endstream endobj 11 0 obj <>stream h25U0P0A fL[@xFBB *AT,@Ȼ 4 endstream endobj 12 0 obj <>/Font<>/ProcSet[/PDF/Text]>>/Rotate 0/StructParents 7/Tabs/S/Type/Page>> endobj 13 0 obj <>stream HUmk0nҘIJG^aBЍaڱ:Nmw8i^tKBNNswp\> Ae830*~C(:IPঁ7B[)AI\~Ӿ}. nWCpz\r \h %h_ꚠ7tTMGPad }Hl mŧac.b K4*aW )fg^Wt"%e^{1sSqOHa%v$ F=yYLjʒ {zXXd3}){FwzQ"#MV{+Vl]fynXU+}wF-+/@UTwFhgm}%SU6t? 9Ƙc]+\,4) endstream endobj 14 0 obj <>stream h޼Tmo0S@+q켭T6!`}i i\9]\oJ^|>?wE DI"r )PL`d(QA1MF#5qlFwѐ(ț0\3:պnrS벆lt *0n;eAѯ+RWgeխӦ3ZARo֝3p:%gV>\:59erҌ |*^QjJ01`YzRI9,Ac@wʕu~aV6'ԝe7mިʝ`K:`J'UjNvʒU%aXlj4V)[)1e̘_F\uZ%SrW{u6ml{a@b2^.'d(0vC,]7w< D}Zm%A_K' 'C5+ANsA йCވ{xDқ}?0`Vɭny߻㦍bsqnv_U[1aǔ[y++fd_bJT>4((8ӀGx`_aҐuO޴7;4>"YNdn7xJxh['&td_G3bcbSOt)_oMkxCNY+7炮|)#| hL1dS4  gM0D,.^7Xظ endstream endobj 15 0 obj <>/ProcSet[/PDF]>>>>stream H4˻0D~bJhoH,!2myH$$TA#`tprcQfUs3MV*˭\i9s: ֐'9TJ)vqO㐏]l% +" endstream endobj 16 0 obj <>/ProcSet[/PDF]>>>>stream H4˻0D~bJhoH,!2myH$$TA#`tprcQfUs3MV*˭\i9s: ֐'9TJ)vqO㐏]l%  " endstream endobj 17 0 obj <>/Subtype/Form/Type/XObject>>stream /Tx BMC EMC endstream endobj 18 0 obj <>/ProcSet[/PDF]>>>>stream H, 0<,u3ͭ"$7P Z||SsN_t#8xjD˦#TjwMs¸G~$S YsC`ƹ50 >o# endstream endobj 19 0 obj <>/Subtype/Form/Type/XObject>>stream /Tx BMC EMC endstream endobj 20 0 obj <>/ProcSet[/PDF/Text]>>/Subtype/Form/Type/XObject>>stream H, @}8B)^ "ZDs2 񆡡ƪg/Ԃ8>Tn=;,ta(Wm:^<҅&cie];hrU͂MoA~7mbl# endstream endobj 21 0 obj <>/Subtype/Form/Type/XObject>>stream /Tx BMC EMC endstream endobj 22 0 obj <>/Subtype/Form/Type/XObject>>stream /Tx BMC EMC endstream endobj 23 0 obj <>/ProcSet[/PDF]>>>>stream HL˽ 0\7rX[( ;$x [.zPUj+*ŵi'4,fg!7C"Bd;t3sI6iAղ}NRw\B`a$ endstream endobj 24 0 obj <>/Subtype/Form/Type/XObject>>stream /Tx BMC EMC endstream endobj 25 0 obj <>/Subtype/Form/Type/XObject>>stream /Tx BMC EMC endstream endobj 26 0 obj <>/Subtype/Form/Type/XObject>>stream /Tx BMC EMC endstream endobj 27 0 obj <>/Subtype/Form/Type/XObject>>stream /Tx BMC EMC endstream endobj 28 0 obj <>/ProcSet[/PDF]>>>>stream H, !}6A)FPno0Ds9SyrZ~@jQ-F4RLÔ29+a+bg#frӢ}t %sK=kd)Or$@ endstream endobj 29 0 obj <>/Subtype/Form/Type/XObject>>stream /Tx BMC EMC endstream endobj 30 0 obj <>/Subtype/Form/Type/XObject>>stream /Tx BMC EMC endstream endobj 31 0 obj <>/Subtype/Form/Type/XObject>>stream /Tx BMC EMC endstream endobj 32 0 obj <>/Subtype/Form/Type/XObject>>stream /Tx BMC EMC endstream endobj 33 0 obj <>/ProcSet[/PDF/Text]>>/Subtype/Form/Type/XObject>>stream Hj0z ۋjXNBŐ+;FvMz+agf+-57΋ZY.${#U +{iS37.0c!?i#|pu:䩽@L=ba8v})h,Z ^Z#]1R;pG2M0  9cXTtkVT7c:۹NZ{XâThӜMym`!) endstream endobj 34 0 obj <>/Subtype/Form/Type/XObject>>stream /Tx BMC EMC endstream endobj 35 0 obj <>/Subtype/Form/Type/XObject>>stream /Tx BMC EMC endstream endobj 36 0 obj <>/Subtype/Form/Type/XObject>>stream /Tx BMC EMC endstream endobj 37 0 obj <>/Subtype/Form/Type/XObject>>stream /Tx BMC EMC endstream endobj 38 0 obj <>/Subtype/Form/Type/XObject>>stream H2P(250׳44U0113C=C#T>/Subtype/Form/Type/XObject>>stream /Tx BMC EMC endstream endobj 40 0 obj <>stream hZmo6+1DEָ];p+b?(2m +[^vE[QlnM{"5C3Cá#)  $ʱ!s 2  5HT3 2F)P@1@qZ%1@ W8%ΫBq 5h)("YV$yC"TA+m>}Д @4նZhph}%v!( fHY@Q+쌼Ks+dM&1+4u}W=2|+{~~X1Z',]/}9 ?Fs~N.'`{:Ǐ)vx|ȓ_7꒺N \o h[T?e"0L?모jAY t01X@-`kXMqO]}ES+gC$I^3 U$8 YE\ԫ,+/D D4':L]GE٠BrWg0 Slfw1YޗiIjKb>/Subtype/Form/Type/XObject>>stream % DSBlank endstream endobj 42 0 obj <>stream hތUmo6 +똗6qCq!u]?(6m ^~hus04MzE)သ1١3x6";ў8!{&ى3L,滹ӑ #YI2ȾgU)63]Wwb5N!=v/ r-UZfP'>Ql+tѧ&d1X_6¦h^߿b6[oZ[Ґcw܏iXS/f!=;\_,QAytHC'v|eEMzsMǏ8ӷez%f5[W?T1WҡY󬨖RlRrv^V޴ުm|R4cE͘ ag}0,HWYYGe^D-Qtn,3'ѵKOUc ~/-&-mn VbJ6'&l~k1 4}՝>V6c\+c\lPeIGN{3B.i!W6M;<#uh+iCe`*XJ ^i ёKM]EAkPȪ)Pks6t5`M ʼk!1ҿjZ_hcYRqA Gg ҽH"]N:.]{p*uSjNk`-`B[`R ' K-J:6ʥnU` +庫FQVv:81.PkG2 b!nc Oz򠣠$&i}Rp6yl sAc+_<6<˷)S&g-d1a _3|}_zVW;5o-Soz{@9w>/Subtype/Form/Type/XObject>>stream /Tx BMC EMC endstream endobj 44 0 obj <>/Subtype/Form/Type/XObject>>stream /Tx BMC EMC endstream endobj 45 0 obj <>stream hެZ]sO{ni{ 0 aPb,.>ə3+s"ޅizWgzzOO-\'>Y" BG!v})u;P`<E Bjg`!Y E't˄!$ +{_s]!s+Ĵ`Pt|=BE 8:,V aqqqċċċċz>zv8hv8q8B;xBA<>rlיS3Tc8fJ{*UګOi.]Jq! B}2qT8a9?IOxRƓ2񤌧R<~Lc9?2-sW_~5՚_|W'_)WCƼ51oyk[e*^u_"mO#\WkG Ԟo`^a^8mf7oB X~p|,I#Ljc <+g#B6x#o B^{z"VMY6Nm~+NՄjwXjhs}qXz~=޾>WՓalfYq]>z6*/W۫5._ʽanT7/o}o7wE~~~>W/n>Q8lD~ ;7p8a pa,4aQrJaE&Z7`b.xq;Rswwdx9yގTlo1HHVP[@Qd!8nn FXD=U6r ǒX5X;0Zi,%>cA)SlTT rRtHge!$ш޸T*^Yˑ &Wp8#rv-CY$@xixVG z*ڭV@n RCf\=j<6_/\cO__?|(_W~z?=x>t`-@xn0y22{E~5^0~{~Igi5~5FƮݿwYfSmV8kwЗ:͊[ekZ-^Qh6㻒Ei%i]O34J_JՑ_Y![ߺJ^w&?biSN[o}ica ̴/K`ɾt̤ձFg'a[fȧ`B?E> g'aҬ$L" Z0~V|fȷ`'0 endstream endobj 46 0 obj <>stream h޼XoG 2܌=?%((}@^^,|jO3}L{br3|mvYzj,ZЕ_9U3\)nHr4\wgtRla0 arh2,iqOQ7^>?ڛ9=0LJgoǧ5nf[.ջK8zޗ @^ 5m;9o1ܾΣ(8;y7vGŒGŒGŒGŒGŒGŒ%o}sKhmo.?B(2 J-}=z -? `w^^^^lj` =>{mB@ - 0{qzGx=:x<cau/pq,PP|.PAd` `.}n;o8B/}K&ئH‡ ɸ` ,O9|0(QgЗ/xaAPPGXva+`1c1c1nU%.)kYkRr;,&~KaGDArmbK 妭%Ϙגb~ 8S(ã͓ǵFcDYfn76i26MƦͦ&/R &>=my G_7EsiiPJ  >A8,%?2(ro׈ak-JnMoi4o=kv+t[vnKmF{{avo-ڽ0yT&u`60)m`mvdd1oŶżMN6Yl;Y̛g񦗟QWSu5QWSu5QWSu5QWSu5%+* endstream endobj 47 0 obj <>stream hXNGͧ#ED^1"YJ@_AD/?6SN{fZLLj'|@;BmCar]/bu]ڽ+soN~NP'GGǓ5'Kexixr(]GLlq=b9P>TyZ]v2'u5<'? u%)jv[<#]~p vU*Gxkr {j|Pw_-8|c2?\Gorh57oB: 6q&nz\U? 1 endstream endobj 48 0 obj <>stream hޔ=A JJ-$ [bEKJR,'MNֲHUMeun&Z+w]ƽ`5.3x`ExVL3> ^уej)Kxx^DK(AizFjюQ~|DCA\ZJ=U*hRGWdj*GU} |[g(3;qQe353|#yI9Sїi}9ǖїn} їa}9ї}d} ї}كїu}vї}ўїU}ߞѷmuϣhqIgKMZߥCF?e\aw>stream 2021-01-23T11:04:39-06:00 2020-04-28T08:58:29-05:00 2021-01-23T11:04:39-06:00 Acrobat PDFMaker 11 for Word uuid:a1e4fd9a-c258-4ee1-b04f-de6ae41fa0ba uuid:9e93e6c6-903f-44db-a1fe-e1c54e1c1122 3 application/pdf Ann Goodson Adobe PDF Library 11.0 D:20200428135805 1 1.1 endstream endobj 50 0 obj <>stream h213S0Pw/+Q0L)617)Ic0i&-!F *V? U? 1= @ endstream endobj 51 0 obj <>stream hL 0WvVBFAPp6#KFtۛ8x"@IqT9'VDA.Nt==Ũ%WkQ=S2U@j:|0V25ڋm RŁɆ̏ U*%di`fdc^k۰gF';أEGyy挭_1%G endstream endobj 52 0 obj <>/Filter/FlateDecode/ID[<6ED954BC0EF0F143936766DC11EA27D0><9AA4E427859B3C4A949FCC15EEF282C7>]/Info 467 0 R/Length 202/Root 469 0 R/Size 468/Type/XRef/W[1 3 1]>>stream hbb&F{" L o@$W D u`vɿ,R$? D2[E"`aXDLB)H]B0 #DPd$>YF /z0{ / A X|]pXd`NJ';g?8w4Ft5JK2-@ * endstream endobj startxref 116 %%EOF 6 0 obj <>/Font<>/ProcSet[/PDF/Text]>>/Rotate 0/StructParents 5/Tabs/S/Type/Page>> endobj 49 0 obj <>stream 2021-01-23T11:06:45-06:00 2020-04-28T08:58:29-05:00 2021-01-23T11:06:45-06:00 Acrobat PDFMaker 11 for Word uuid:a1e4fd9a-c258-4ee1-b04f-de6ae41fa0ba uuid:2c14456f-690d-49a1-8212-08e371794a38 3 application/pdf Ann Goodson Adobe PDF Library 11.0 D:20200428135805 1 1.1 endstream endobj 514 0 obj [70 0 R 74 0 R 72 0 R 73 0 R 515 0 R] endobj 515 0 obj <>/DA(/Helv 12 Tf 0 g)/F 4/FT/Tx/MK<<>>/P 6 0 R/Rect[138.76 626.781 270.009 644.093]/Subtype/Widget/T(Text1)/Type/Annot/V()>> endobj 516 0 obj <>/ProcSet[/PDF/Text]>>/Subtype/Form/Type/XObject>>stream /Tx BMC q 1 1 129.2486 15.3122 re W n BT /Helv 12 Tf 0 g 2 4.204 Td () Tj ET Q EMC endstream endobj 517 0 obj <>stream h4N 0=)ImJ@ś ҀU~$^vvfv(9 Y}ǡaMBc?u7$Ki<;%)B{gO`ԩGnhpG.l!{>[B̼akp] 2fY0>{9a.F@ 0B endstream endobj 518 0 obj <>stream hL 0WvĦVA,Q\͈1]*,~>N^4D̽+" f ЇGwb4L(P*ԥAzߪsAR#84ې1J EP|-m;E]͆@9%]bf/ԬG endstream endobj 519 0 obj <>/Filter/FlateDecode/ID[<6ED954BC0EF0F143936766DC11EA27D0><0F7B244551C1B242969ECA2810123148>]/Index[6 1 49 1 467 1 489 1 514 6]/Info 467 0 R/Length 50/Prev 116/Root 469 0 R/Size 520/Type/XRef/W[1 3 0]>>stream hbbd{?c e"6 0,"@  endstream endobj startxref 95369 %%EOF 9 0 obj <>/Font<>/ProcSet[/PDF/Text]>>/Rotate 0/StructParents 6/Tabs/S/Type/Page>> endobj 49 0 obj <>stream 2021-01-28T10:38:02-06:00 2020-04-28T08:58:29-05:00 2021-01-28T10:38:02-06:00 Acrobat PDFMaker 11 for Word uuid:a1e4fd9a-c258-4ee1-b04f-de6ae41fa0ba uuid:39ab19ca-6f7c-4092-a5e2-7e7f74b8ca0d 3 application/pdf Ann Goodson Adobe PDF Library 11.0 D:20200428135805 1 1.1 endstream endobj 520 0 obj <> endobj 521 0 obj <>stream HkoF;R~ܭj}xmG ]"R?*`P殿3`H@~qnoXzݐu/`a3v#&#$*TlLx_(.|%ſ@F)wgq`- 4` Klf̿JЀ$& u~3ͦl&L Th6[PBZ qP^/嬏zD[ÛkDZGG/ >"Ih&;ogt\M,LRPk؃R>E Pʨ6p[ _-`-U!ZaAµC۾ )=w"9;"wv- \n_=Og/i^*JMя(/*5)=mH pI0v6^[o!,5zu+S7/l ×6~K!8lJF!wS'q+NRc1ʆ:2:ij-*c(,ۉ~?U>xS1"߁? y>2,OUsWolN)O`]RŗwG#YV! 8=W`.d|ɸfUF@ L?,SȰ=[,#XC.䀕pֈzaG%tBA>4RD[VG(vP}sVevuF D ]-5Dwn+,]|3:%1L0U c(x؃ЅXb&hF@fPRuWꁆL RI( R0{4#Yx`zIbN_HZERdSw{J#OMGpW;!y!Қq'٥P#&*@j/-n471l>W#om;&6UVTboPW * 5$f~m6^Զ+\u;h~fJ+lז3 tj>stream H\ˊ0OqxD\̅]L@#9&P+ݎ^^x߭dAjˮ6A/0rW9vNW^Ul[}Ფr7;#ETq?zk1lU)ߎ Sc1W> endobj 525 0 obj <> endobj 526 0 obj <> endobj 527 0 obj <>stream H B endstream endobj 528 0 obj <>stream HWwW~{Yf:E $"RAAԖ.(؎(eRL MNĩQ!;OL|w]>y!j#!q>9#>Of H#i :Z iY3SO->֤$&Vt~7A2GXo=eƨvŇX3>yz7Y8#^5vOwbbvS!Rrwؕ%;0adJQʥcv*֖a;& ڗf'JڻLe|o§.BCs]O??O>?Va㡴!0Ea?-&PU$BRF.,{٪"$Kh$9r.zܒsD6 suceՎ 2[)ơ s ](1|J]b#e #bZe4莁HU't Z~-#Z}ݴ5b,&znԖ^g偹Bb0Rl^i'픏|m9J8NI29KL}z;VA 7 x,!lt3=iL1qX38K;OLZ75[-RM19z_yji簙eڕm lDZ K;U_ !&e쑫rUTSqΑјa8ҹs\Q҆+OUW6YVyiz*QwDZ&6uv'8V =4Y9cٙmЖ.k%''pDa?FCwLe$(IN̕|'2_ޗ_a V"n G\r-b+hW*XuJ#Ui*U4PکKmtkv}H_9N7g8ibztSl\Lקn{;=}mkpݻ%uZ&7碾5$s:K/L<ԾrM =nQΑa+ipUPK{謐/TTIy)0w-])Su.G)rSשPPSHfS*Cbf^ ~̠{J_pqKCYE8)j"[ɸ?KrCD~^@d.7ܭo\еPeP^2H=T !K,o`"O@ &Pjcρ*6WL!l`:p[,D(1#Dl/O=`Mmsy_xf±Y?F`d!Щeӛ4[H$c4kҵ˜GQ7@77 ZKfcuhޏ(K{rɼl&W;?ģȰɽb l!MvgE&A 30rQѷRHގ5}b;acs.E4xD6oѺ =6J*`[jH-,2 'M cIU!%%_G Eu.եTR]7CU*!sؖTT`j0jNܺPtNӋ'j?uV,G3J(b41+)*&!Df~yCṣBZD,a6 :>a36,`/d.tUx*c8N-vy-uS]UPݎ`Iy9&df$;0gݺwܩcB۶ jݪe`@ ~fjb8.).d-)Q$52W$RdJXVUz)JȎA^x1z(z-e ѡ\( *(PY (y?߼y潙.ty8`IFJ0YsIw0!kWB\$,LwӅyG谶"lQ:6ͼ cQMOwMak'b͇NPΩəF\1N@:0R!MaMCYB^e`s0b˰7Xzj*ςJ ;*)K I%DL>$T/~J)mBuoai ZҶk:Lrf@ꁦ1ܥ|͡$SEmo-n_?9K'lRN|PCOa_g"5A!fl.'hh{J |d[$ǧ$M 6,sƎq[Ir=@ &%։9w>Jt/ͣ{1OYirn"Gt%8r cKpp%W8K+p]K饚t=wpC W:Yv8;_A{¸pxelB5,rSg75 HUI癜_ uVyCW(.KA~xaX ۘ6s0dqBi : d_UЕY`#ĺ^^X?tY!0h 泬);;x6LZ^![ȊC":ܚVAF-EׄHĽG{ًPl{.0QI.kA&8!68Ƌ'nŐR"7McSx4%_<: YфIs'?~6Q:U:9} | B2 uv״kT 5 ge qc2JSsKHƯ0~M#GOG.?A@Ϡj(P:!k{^z܀gDyfѿ 8e;op,-rzru0>=p.6M=EѿF1Ts4R0V$ ߒs~ݽWƿFKid5(=%*g ζ'B/Szz;S?2p^~9>7n ^q$'v9r33rP Т$oV,ȷ$;դVZԒI܂H-^~FwJD<87Oऋ%(zsܿAx%KBMkp(5m_|Tks-~xmAv;}-~STU׭fޞҩIzWSK]N 4ܩuKVU(--LWxC*OyO}ʔ*5eGyJÔS{|:VdCOtO]]RO;S"sqsP$PϙގE0L w.fh:o6m ߑ)DQMKEY(,Q*rMlxkaVKkeIW$Zoؐd+8쇆&˚@]61x@MCޣ{O;ݽw/UR_ݸcN<:.cijesi2 K4-A4K؀IՖupglnzSS Y{GN쿎M(Ef(OURs)ZKQN S:Uql&p3>5a1[xK岾~ 䲞[sBo}}|nI:$O4=U"[8f lV0x=>$ڴ6L ՐP ޤfaOVYNT۠ar "|r:֡} \%T¥2 "2$}) reApU9Ս{ <1+Ǚƙp+QQu5"ˮV>[e]Vj3p]UOL/U=ޅE|ak Bv*  d4?UJx $E a"ahƾip7[T;e>Y}fwyko7}NjkKd>7LᓷOnMs`;ג.ۧoXA;TQ W=(5`x xbb6A%Wl$Tb(LMBU'j vF:/ AWLɢdln璉^ׄ{P $c7www}2,E\VΈ/sԥe;|gU@eggJr-5ۍ  \PBHh& +DS^nI)/ ]Xâ Nr?yxC@BE4<% zHd8Zż%111ŸB[}\U˥Z*8zwUlMOGTXa\KٌꯆL .4)o"A{*⌗ժGYt(5E(lhoobzs$#"d%]Gd1C>_cאQ<653٢9WŸd 2* ;O'={s,Ś1 [3tJӐxbni'CY<:^c͵{Bbkh4vH*hqtٵxq#.k$T.It\(]{XL7BpilCfN.Hep9l ?qM]wy:R. .(:ٔi !h]?qÄ.lrP0sT}Axq# eAA ;RٝfON*Tϭ|ֱMM8HH {8۶㝷ayC,_oY ~Sg7vcTďjWrʘsyir:}7!K&L{SrCp/gs q׬qw80?7@$M@4ۢD`bb qHd(QC"sOD"~ E0ʁN H(TUv)ۄPN34".lE#eB4=N#,26FH,䷱1b9ވFpS`!p5@FDs"<'HY_"j+H2MHi {@:6R.1qR\WEVcXњ3T%@w fXՋ^{BPP0$wg&aH&Lƒp<Ґ:yFWb(ߖ`zA#14ې"5c;2/T$NiTmIz@:z >%N9ZLBk_`HK`d@'M\*ݯYԱEV k'zoǎp sDܖlK1`Ek< KOBTʇ漟pxzƽ~ކLmvBtw0`ӏl4 Q;~ ԩ{t!Y⿿fHJ\B>\ 8y@H'=0qpUp1 (ϣVyd)(ݼ0Nb]1$d lVƞ]^S "N7u bB&dt dBaAt1{O_yā_"Ú=OĂj?ۭ~쯓XJ!gpOjhɯٚ8|dɦaf- 훜r31fKCnx>Mf OQd`C"$֤ ҟ$p %lgWkYl#_VN֟])wZOP&^~ՠ0R*=yJⳄ:GĎ؏R2A +7;$~120SrPr@Vj@EӒE&mE%굎'ڍbz^iZ)3ߚ.4KEugo_6{l;=(!?#W8{1m[&}=SkKPW_g9cw8 ># 1~ wL;,W[k M&N4HW"0U@H *(ۻ@4G˞l{%xFd7a!&JsСQzkB'\x|)Y}82Sa0 3<&ڠ8; 1܆ohd- kvþh?=o.6XՓ{:]LjN\2<OLS JTTFK .ʍT}ߦ4lm͔tlM-4t2}MoGz@ D ȁOr-)L678c !*jABmJl1MgnGHyh㱾N 呑f♵Xpoﱌ1pBl}#ŐTXIX -n` lq,nƣcaF!c)i3fтh2kb%-60^R78kD4(4 kA|/ )LVePb"m@0 lmݍH,]LR#Ԡ#(1/HR(N'[j;&&۾ۑȎYWGEsҪf""9~D\iLb\' ?%'#ȏWMR@ fi_!%c?4cX`I^PTc܄гF?0#M)w,' (VaqJ/26$Fr#^.GtlyK]D:WOLs9.ʲmQ j!O(!<8T?G$Y֥"inM$b0t)g(;iN.D%R\sd璳q94Y,m_I]PG>_X.F~Dx "__UN8\.tL[g;Z"oyY|vK`}66ח Un[|6Q(~]H\*RJ z}ĒI;dR"KúQ Vb=]yHcvy1Xw.`z<-2}f/y[MGю 9wƫC3E:XBNwQ-wDSmҭjo6,MĶ J s>n0Re² aنPlpU&X4]14;H+p R,DRVԟ] 42FTalj a0OGyH )z$"+.7-Ӑ*3ݽ.11WV5]"sI2ҩh:\Ui$Q8bi e6>UhP2O ] z>zgyRR~Icp#']bc/^,DW9ef4ؐBV'5Lv1f_sI[bg%wQSئ+Z!yjXlq# -δ{ʹ{a^3*We!Te!TEmZjm`|ط&Z ϰ"l†C7}ȎZصȳ<(*̇|(*an>F aWo= ~#7v kvb\O}EavT{x³2(g[k$-@ HȰ6S({B}QGg3jʰVeUvWewS :Q*6S+3Ş'jfOk-H)/J]0yRDq})|h﹙p?-:پxg%`lhNP @@" 2xJQn*B9!g&dL |iZOqo<r#>XƬikZZv&%bq+ֺEXh&,&"(fT10hb_`F3]G ;۩xD8K[ ( ~Y@+9pW`6KDF3isdesd(S6i`œ@ l2H{ ñ hG-j?O26rjt`;Dv9UbۈF\221,xȲQKb)=X](*B9>|༟hcrzqj (D+4weL]FA,Z1vccl8i.C<WB1]ee%}2̑kk#Sk /nBLb6x_zPětd{JLr)ԦSґ‑*( T a :E};'s󾬜RO S َvS_5g4 F5_/jg NlXEPp;8M_8qs_Aӗ/x;#շ~x_,]o/<:34??9Mя1 LܦEƜ.&rXj R:c}BL"z u,Vb0t˺{@E`I7)u z 7‰55н&T爢Eq (bͪx(};kOotos;LLW9A6ĻJE 8Ū c5R't> g=g;_G|Di{QN`Ҧ.x&ψSFlԈR;io6Ͱ]r;y.4qY%Ȉ鴯a]brl%[Ħ>lƭpvaG xO,o2q؇9o)W*rp(9ztTpv%m@5?{\OMrzc C$ݍe%?v[X<ѭ4Q%Iki lihJt4t1P`Dw< D>` ?Ţ_QM&,0ߘ̫'}uT/8VM~c|N x bb UCAq{ҚGk: k̃اm{[ b1i$2Ք1x w5HZ*]5wnP!F7~f|WlWww|g;ws_&n*ukDl$:J3`ĴZ *!!MVb-GلzSUi{{y֯Yڣ=O>3c}&} *G)6h<L̩{ж6DcgbĝOy;[]$HdѸ>Ӻ,PjԱBha~Sz͡w4-KE,,K>$z,$;1DGZ5ŠDg1L,"۳Y~<>B0gO矶:>U\7yn I-IԍJF TS pI[Gn“ÃB r&H}bR & S"hYVg[hܚjcZ,z 2_۽mM"F{j UG8*T v؂/Im<&(DAP:"-tV:֭V;'iDg?*I1rnS {}ᔁy΀g8ͣyz^ Dg1|S[eB)]Оlu۩pNs;48:cjSS6i~' %Jz!As2uIC9űU=ܞKa ۔=#a W:A&*?x6 Mu=Vi|]?3瓓'T:s\8I `WMvDD2U qXqCϤzڶc?/O]Tţ#Y[ܳÒ`Ns]'NM:_tF8.1EVEoE}̗M20_Ŵ&TX H" Tb. {X@:˗bG;,}մohM H(z(vFYBLP+ LwJ'SIpk)-VA QA3h joFߌ5o>^ѾX`,oQRXTt.1u2hyCxӱKG@7]Y.tBa3 )R$rA|xq/Ye-p%-`MTey Ԃ%LA8-8nCIO:.5 n4A2jz{ hի\jsJ׶h筕?m[|щTЎ\3 N +$9|hdfS, )Ytr.:y ZɭB9VԄԬW0C7aq5OT6b.\+\nLA e]0k{Jqh(C~=H'6O9]^`~JP*cpBP*VoaC'5t0d+Wb6b!kdش<&s>\Ӥ%V_j @#S,HB BL:<h=SaSΨfQv!mʏ#N3ns|ϯ/'Y9z^{]}B6[^巃I, c2ߎ۷ec:ݎ0Şi4Mj>ޛ>e汻$w!a+HH B)OZѱ*GXRNXLdZ-a:m-@#P;5Hnt'L|9{K>m 7o0ڭh>K} ۷S:B=Q9Ԇ)<ݕ5X2V4m74çkj4 PnʢjK X^K6q&{Kog3N7Rd^dc !禰8Z>|K?=/F<qgCwp\Gߊ7CeD/>OpZ(jBqP]L8~l%+:> i6nG % $%@*8.jDIcd0?;|/ߪɉJ*00 *)uU:ؤ)Τ3ALS]r# F70>SJ'ZGtbZRjvGWmjK4~U>2[_%9w4+pF$_,dUjN~λl9[Xk:GsR'Q~?i#jo+p Dsw>=QkrˁyKV(^%۽z L'gBeγMr}RکOi[t43lӉi1hiД==bww>l#ϋ!wyR> d@<<,ؗoҷ>ꁹ Ԭn:=) j^&_cOŸe@L<!C0m 9Oy˰ gYѾ@̐Ѓoݘ! ظ<<נּX٤],RPDUyޞ~S0+E>Um<w)m⮞,$WCo#Z] / o^)x6= Q}7xfʓ1B =Es_7e`m=pkVD6.88zp oƹ_1Ƚ5nf`ck_KF0=lGb@ _|&~G( 80x( οW:~n8H[q sg7lc|7m"@V'zf6/-'џ.]'sD6hYṋgt=4t#sp֞6t?OxҳS|v|bTkou2-W^WB dEEy ɾ.w#0|2AIw>ߵAU雜#|Z'~xh6iN-x3ܯv ZurTߥоkp׃H]YJ>jWqx|bGy4s璐_?ATxRT{^d)H$bs(/Ci~i ?c^`,/bns9E|\&I9Ə{_u$Ik`2Bϋqq͹cR]uWbGﻯhNMQl/9i'ihMbU_B/Ѱȟm9a/!` ^yD EܧT1w.mR^t1[>8&8mHncuXЃgܕa2=^ה{A}yk 291)1Ky E175 Ŝ6wQxJyJk+*=4-x9 P.#f,r"Urzt՛hCm@:W亢>8GA~7gd/ż_D1Ay74so?Т ~ RBjuOo2 \ u\*pNusqVir z=}iUqy{"K E ¢@I3j"mT2(Q”98 Z8@j%S!`}syesϏs|Z\2Y]A-ك( ދr }W5 ΃&zX⠞<ގ,/4'}-WG!ES},qM?N{&UܟMԃV?6u`Gfކ},ઠ'qԭ{G)T'3 ڮ\íJ3_nU!٫7K3se'| ~uux4x跫XwW;ƇR!k9WKHn=>QOgEt͓rr_*q9ȟ;Q'K<ϳ;_vEh5gpXr0])!Z} _~ Ev&r1䟏%Cl]u]5s?n_;LPlv-fg:YbI6oؘO/s h2%#?VZȫVcWb 5-9C]WEVc䮈k1Z9eyԿv`[Qf-AMqAJsώj;p+L{#rRx]H{1Gk=wGe5we]5`btzqS-]Ruj Yc꽭 ;eU;~͜fy750Ŵ9]g' sr5ϗ_F{F`wP~rbl9X#?ENcXKJ2wrErt753q꿌P%n{O=׿( إ+W#(#_-CX+sbY냛M&5y]+|_.јpΆ9s5hۇ\4{GVj]xUE:a΍dQS k{^}'Y?]ӅێCu0Ϟyj^:}_h*t~]AǴ KSv&77J̩78OAoc04)%} m-?M~,<T }>g!bސp_n@ՌW~d0e]g r05&oK~>:2,XFF'DJ&22[+[Ci)%oJ}r{&s9&)9wJ?,ZjAsn{LFx(ӋA2j U+{gEFL`4{윪[@]~:dYYn tY"^hj-oU,{騘PtýO1^o4xr@љ;HOMxp,c^/m[NѶ.۶юYFWٹJ#r{#Nem|)`^߿+ԟE-0gdzLGz Kt\Htt;<Í36m}5Iۦ٢5;oI}y}].蠿YtrNrY(SOI{2. :?Ssb(dhw:# c*%' ᣖвK! c}ExÝa|ц?:#X<=b=n:uquq?>~}9Vݙq&߂7[`PAY{fǙY^1h[n]Os zWU]ax>r DC" ) H"(Fk2T RQ(-Z H; `SԎ8`vhQ`mU[/7igzgY{>k/FuråݟWPtA%cf#^oEq]+蕈 [b8{w>VF|',pTHNxCb`f̸'d+GwXouǷRj9؈1{Zzr+n"gߏG} ҷ13AS;x6) C 4Ђ~ ʓ-R7J71dEuY܋֭X?-ki];h?Ρ У`;Qqh]"7c|{[fΊ9`Ÿ@- Z1:zܿ9ϵ_I,ԍhU eM/U~ə{?u1TmKZ2bS镁ؚ@Zr1yeErr#E*!?q2I|@0_ /iF NSfd[3SYZgGG[tǘO&Z'SSG} /)4ﷁ{u+رWG9ixg߆3fLV8N$)Ws1N9נM*KhFOnұ9UL򍡧%:$]+]gMuuUY]n!Vw(GpobVnH1Q+ƒhxF33Rj 8cDqh?b͓TZR-++9\d,(fSnshk\4iζshZ~szί¶ M`nC㱊

>]\%I9:M59(s.lkrʹ\$|8{ d+MijZ Ӹ>ϰ-L&q6Rݠ~y|/<'5/7KNqcVg!ZFm~ռ{ |Jy`Tq7'-6}J˰Awηd1 H=Ob VޖIԺobR뭔Jﰔzߗ o.grDn<ۿXnrǠgfacl~ݡtdT)v,}0YJ_=_K?1}8m=üMh޽ix\̷o\XtO~o,2>=+;R/3]L}ȧRUa M $"W4j+odx~{rLR9dy[ָ#ᕑr/ 0 72'9~`rP9𰵪O`Z#AS~`Ǯ1ä+~}ԽV8/hLW/M2h'÷owHyC*If\xÞ|///-Կ(!K;#+e.ĽkȉTOi*y) 3W]qHj53 %'qA\fj)8OEEKvmth YeE6DWmY`|Z׋uK׌&k_mҷ]';bb1(+br|g3O+e:3ùV6E/S^aoo>tkA&9u9\пB&QSY*3L']$ 58(KG<9ꊬW?vqd.ܻdBR~G}q{S$!" /4\1Uz%9].PLB'S_Rv: *Z_KgHA(`+-w~Id*ig6o>gu|,COoy~[ |T}(88i?.JO.q?}pM`1C'oZve}8Ƚ-.ڬٍBǽi>#KmT:X#,+Hž^11nٝ4Vl(r]wZ5|1^qb Vy6(}Se bMa<rOuGxZ|w;Q9eu6r HsCF1n5J6/;/"RI|}aQZ 0罍rڭQ,ȹ;\@[7faOmra=O3-韉k1b"~t ڌL d]㭱+lO=0sZ/l6q0 P)9I|!){.\_)=/\I+݊R~7\S5:%;Cnd f+1_SFE#|~a|gx wr;DgW>Y^+~s{Ce˜g`‡j`9%?za1mE Q.UZoaբ5 D;z?YL-ߐj&Xja'K6+Q;9N ~17Fa))Yǜ}5j)--GClkrǑ:nmfsLNI!|-L42YP5f앚j~zewoBUHYd~l䜤uª_TٶNT }SZ:~M{FmMUd_|j!SE=RcbvS\W@R\vwsL_urkh,+nkcx,Hc*!1=n%+Te4Y] k9nEOK`W?eFb=w%dS5dy{ͦ@4 ngis7j6r$o8iFKH8lf2֟ZLS; 쏆7[A+̤eV_ Y!3)5CV2!+GU U}Vr=uo\,1UsL%!/nƺon?d+V6>T\12b[j{8Zɭ~O<^C6 2'qqEe|ωm ~A#>xiGf4jN2i+ :ӓ}w=&*P?S"[}:iԅR? pE'Y^et SXS-L4 Au)>86%=?;;Bp<)4Č f1#}//U~ 0{.x%ƾulĨWM֌ů}Xj+.^;Z]Qށg[ZVD}+Q,_*BYGQ6u:Zn7aMp`ꃕ*^QOxVZY%5 5 5 Q1B?8tLGPy3E[s3bZt7Rӈ'bfTSS3vsFNGssHKLj(,{sV_b2F8Cs<ݚ8'Z\g+>a>Q~K~9\׭J|ϱ068 ˥iw?) mijcW9r$7N jA흠v(}-(-Дi^k;ʵFQd'mIL?K$F^R6&ȿm38ɯO:տy+bl~3Z5ah ǍhIY,X^3.S:F?oQu1 r3w6wpmHfVuZהy$҂>_#rCWBUe5qтu vֻV\RwvsWnp+n^Pzxnj_e"$ u+8=3,{ڨU&mVk$]r=f-w738D 0Y5+7ꄎҴ5O{փ̤j.˩ՒE nL>z+yrFx.mN{2_ږb!ikSO`wrHL%(CB+UPjWa$ !BDXD Ѣ aدTr6*}H7 er_H LYFe_2Qꓝ4#$!ă[.'K2OfeJ4c*115?b=ի^G_ fŖIh߀u[HZ{d`)~ӓ?=3o$>/%r})ESi"WZ6e6̺I[}wY Ǣ.m[ `kuW_H֚Y \Mp W5̾| jS/'p^Pjq7ؓ6nݕ HYfo2K@`jNυs )Ǡ>lrt]ᮆ)G\ )GnnhrpyrmϘMC4FƄ1c8!% N&B3!U8vg.D!VNøRI9_Q.1^ϐ稇=Ot{l`fg],ǜx]VA̍UI:-uNQ΀ڢjګGquR.;=e~o5^'wQFBwIJr$ǝbB=~S7gXNk> 5TIوxֲXeulM ڷ=ʚRV#UU ^) 'gXLv^(l#" Gi*p>!(P6#j.f`,.e(.f. T-i+0 `Х@ endstream endobj 529 0 obj <>stream hL 0WvDQP,Q\͈1]*,~>h< lÉ8)+" f ЇGwb4L)P#AZMzߪsAR#84ې1TfdcNkWgf;j踣CGs y M.\1I%ՍG endstream endobj 530 0 obj <>/Filter/FlateDecode/ID[<6ED954BC0EF0F143936766DC11EA27D0><4E73512BA8F02F498C2AA7C30F6F39DE>]/Index[9 1 49 1 467 1 520 11]/Info 467 0 R/Length 64/Prev 95369/Root 469 0 R/Size 531/Type/XRef/W[1 3 0]>>stream hbbd,}G?c+;$X@,i 4Hxn 1X&|nK b endstream endobj startxref 150392 %%EOF 6 0 obj <>/Font<>/ProcSet[/PDF/Text]>>/Rotate 0/StructParents 5/Tabs/S/Type/Page>> endobj 9 0 obj <>/Font<>/ProcSet[/PDF/Text]>>/Rotate 0/StructParents 6/Tabs/S/Type/Page>> endobj 49 0 obj <>stream 2021-01-28T11:09-06:00 2020-04-28T08:58:29-05:00 2021-01-28T11:09-06:00 Acrobat PDFMaker 11 for Word uuid:a1e4fd9a-c258-4ee1-b04f-de6ae41fa0ba uuid:95b9735f-e618-4a7a-90b1-41cffc83f3d5 3 application/pdf Ann Goodson Adobe PDF Library 11.0 D:20200428135805 1 1.1 endstream endobj 532 0 obj <> endobj 533 0 obj <> endobj 534 0 obj <> endobj 535 0 obj <>stream HWmk#7nG-tIb;1\A ೝ:%Y8iwf$틽kf4Ҽϣx68fg'.drg/-v0!ݢf)6(M4xVXòL fO>4nW!5Jr#G~\}W-*&ڸEӁ.,yc"€&F e*\Qb\'~[T*X m-dHY q^,_#%-g#4|ũsJx3 'ʽHGV$DʠJZj"YD88s%ŁvL+n Bz$6]o3MDH( Κ9ǶQ)3|J8ɉUk+RyW{6z~y/^leeUw{??k }WAug!+5Y`zkhvp >UH#)e{5[3o^K0V8smz}̝%GEp"IO?2hq RUyP kQR/!"2hmhbQB JH7E9x|t?ʼ a;H +H m$@]Qe'\0zaHU@%y‰lA"-Ta=^0~;=qws8u7Z }G) ]mIӮ֤ paUH))S W# fhX"7&\uW.<+@NfT˱g SG0'<7&M3LGzp{*S- ~2ckFު17< ( zQ?1sW0u«և`g !뮴#!DIl!MO`﫹Vm{"ܛ#RMP衼H26C |%8U4f5DP9dD (PipH~>BQ>iT$g@+@P֝YӏCW;Fd_@S)\ćK㈳_!8T;IZ'RHm蜎píc] muL:2{|=Ǐ+,Alݸo[?eտӼ5]vw'>;"}VDD~<ߝD7[榡rxXBi> (+&%)e;nG3 Rn#Uۍ c V endstream endobj 536 0 obj <> endobj 537 0 obj [539 0 R] endobj 538 0 obj <>stream H\n0E|,E#'BJFbчJRb! 7JZ0313<(|x5߬b5Ant'V]=+q4M9.8NvA^l[sױZSX݆;6ETqk1lSjoyj2>)CFǡVlks *(?UlxҨK$*.=, P@[H tIhtxfuk V:f7=蹺YGypk1C?ZWGg endstream endobj 539 0 obj <> endobj 540 0 obj <> endobj 541 0 obj <> endobj 542 0 obj <>stream H endstream endobj 543 0 obj <>stream HWSWν~.m5U+Ȣ(/QDQQ" 5j\F%.EC1ո5AGkщ{kftmg/t̓w=|.C#8fq8@/lEB!>bW1 q7x\No}R ۱_r$aSҊ32}lO+|ڀh?GZ`V>q̾L}489N؍ϻeލ">Ra]|&K Djc&z9c RqUH[m[؞vߖjn }1`3q TJ{ YyhжxLlz ۰OJ[|-D[Rο g$^BRoY/{{p#q'>ANSgxW8:$yvjoӖZE' Ha:fcaBfNmUms oqvb/q'~\E'(C$CVj+Wrf@=;qۅ_FS̥Wq[Q%ͥ5WtQ]U/b:zҼ-Qև~O7mh)2E~7:@o:Y/o3)w&ŔS<z\[|"{/d?qQ<NJA49HPa3W5PyDs.O] tnSt^~(' s9Ü gs9\q^_dsJreֺ>sst܃ܩ9 n[+l ׽\ey˙x.|D1sr}Τ#'פPgvUt S_5]t:J U%Y)_<\Փ󎗳[wBUz+2%,x5tJe"p" dwTBu:<#ds-Z;mL (V-i&% |sS8۬C1 mbG3"\;_ȟ {%aRlC1_x+rX/1Cx*TMfG-"0OʵۜG0 ;~FwÜono$6;ڏ\=q̏Ѵ(K{rȼm6W՟9(p؅cf; H[ɿNtVN$9a>VqF> x@l=:ɝQv/ßJe\c 6],bd-tuXJ*c8N-vXy-uTQ]ݠ>=|) &LH::ihݻuҹS#E k:UH̓bmRR%%JVf5]&R?3r,J֢XV[Vǖ]Ŗ\ Hr)/Fܵ@=hYi)H"^ 0 ) ԶfV-Xμ{潙%:~SȤZ[ 5Js,Q !Y(WeBYz\iC 0 1rT!iV]dHp^h: *fuH)6NvI]J8MEZ\m!) ` N7|~ @j/| )%<& K㚮;=9t!:AE5YX!Wjv5-flT.L8G'(Os5+zއz>8`IF.pr/c3| `u)5"ty\ؘk{1(oΦ:ըS[7E2G9=9_Mi_j' !Ρəz^1N@90L šNa4O<-qEr}`x >PAUR/44ى@EQ{r$F7TMAn+N|{,NNВ>a9;;PdRګ/>ИOi·gm!r{ޒTt[ BJʉ!AziKRm!Tܐ |)4kR@SGYBpB:M h@}i8*<@pk 9jz*XiTpP0FQ`sFk[ڶ4/YINi9;d c%q»@p]Ky_z3wV@tp)ʫ‡'{1@ؕZ-\Fo PϢ6uvMcY B-sb qhe @ ;`++=Px\K_N!.4 Q k\VxL뙂 7 6XN˕Uۛ Mt4 ~GZc3GO Bψj@B$غ dHNWmr<¡P\I&X4TRՔV+~).3_&mBL/'!H EΔX2h/CP-K,7'RׯlFᨑl(LOrO4GZ#@G<=7>EܝjV* FKȧ)r+v͛cLmF Dag^}.Wn4[||m0W+.Mһ]ZRtiX.-dBH--|Fa qLwdC*yOʔ*%eGyJ)\L>uV*dCu_]]RO7է&<7BE2:i|ԶZjMg KZHy^>? s;?tPOMJ%Z{?˦rRi`6in"fayI ە" - j_fdTqKLi^CFy#ƩĊ&|< c88{N78$eƩ`wpͭ;aWQVݵUؚ>ںqpP8<:.4  R Upd+yĵK$soJIHe(jWEFL˕%iS~AoG%LfܓΦ)tWJUdzFvᮂܢL;]old*XH'*֪Q q0 V/mT~/=z#RYS3UPrHi~̓.Z1S+拆"} üv1O`vdGSJ RrP֪R\JT 5F n IW V')m` 3:;;=KXBٌܾ'q@%s0SPҬdW ր sݚIKV NE\vA B&ayYf"bߚŞWsS{nG`sv!3A Kc`;?J44D<~0H0 mDoE:Ͻ3z2yO +^WS$zF&wzG9P#ɮcƉذwόDڲ2}}|!;.FXЀHv!aS$B%M!HIQC9}ǟuB7tMR< 8n5İ7=I̲qx qd aٸd7.SDu͑{e$qc#Q%W߷Xϙ)q <;ʰ;߯ƿAf^_bަu<rܛ|$"&DIoa[fϳ4DH {=q3e(.iܪ.ݼq9r_oC_zJBo5_oZxOkOjoVitD-.YAͲ *2\~r:!c= !QǥХin❡ _r+__2Ntڦyu桑eEQU7mɶu9Dj8h#0(t>U$Tz/k XUy wᨗPe ԸϏtD]a '^w=7Tm)/zaR&C0Ά a*1BЦb!7Z %EfwǞ7qfEKh@7L X ȄH̵ѤI1;Y0ccO1eY5Y6)d;8;c-HG3A*Iz;Iˣ).ם{N\p><#0dI7Gwt`huw  ".1QTpc[ቊ}Lƶuzۺhbc)O7@\yۙ{/u߻|wmƉiCWuHVNX*Md Rmmmu4Nk* khe0uh&!A)ҳ{wK`%뾤[B][>2{g;ʆ rX^ ݀rZljLkw" C8B2\`Cӂ( UD$"'t0ʽKl.El1Mxi_ߘ ^ˠ1}}SX'b"b7k s_p,8}";aq&-:nbͳo$@<[-]NKopJܤs;|B,sqo[]dYH\qjMæ͉] tNթf!6]L \Z2Qy沱K=/"~D/~Ty5ApL.E5 2G&i&4Io0ԔdH}$Y I- Kt&K#б }YifrWTBQ HmځπЀpmތƋc, qYbCME"Ͷ!k78 xV%jWVVkjjdj7 )8d|%OBm*F4ͦ6YQDB#/y~JTmr1J2vÇO8 j'̲Pk1nGO>yi0&Z`d=! (=*DA$`"aBXoOC- 2 J? pD7B:GUʢ[ZWH Z,r1uBD0 `,,`ӸlXv_a$%SUz/-QMxm&w]q%nJ O ,O)sI, %8?rr=. Ol\ ('d,TE UoL%qݙ xV|o mk\j#?0 !iLZ%A6 XsŪ3ӄg%`j=dFj2'8w"~nXF"9RfbA3?BEʵI77>ڢ3ਭeC]2-Wrj-~ֿz.;??0`[:nF9{Չ H w`WG:/m.YlA*_L+r{~ Cܦ#”i|g*ܶc;ELot\v= ˅[ߺ"g\$ߚ!o }ۡ*C۬鲐(V{U(.{Zærף|O- && F Ά_ 105-.PeXk 83uKOqo|,_ n)T@_J&~$THiss4.%A]!;1c ʽ ۩ \s>#^9n6,<ޢk8Px?zfғHܡܱRZeD醜*eĸ|4eHFBQJ+%*P5%JR m^BKZH}{_ێH_`s,PsKsG~LOmǹjQ_Ǐ&B_߸ϏW-hws>Vl3ךQݞ#Xr{Nz zR8R(fd5qFeY qU;Ft\v^s7$Ah7 "^[zB;+ MK+ {b)-R&7ncwhVIC.ٺcLzc ֠8to/yG;Ͽҹ!N8}xMɈCDkÝwm#s7d{b3vgu&N8?mnOEJ`@JuBeE PY @VBxiPo>> uUBժj-u9猝fVf3~9i~<ڼON=鐣s~Vhp2$_W&%yՁ@;o\K:w7WYؕSp2Aۇ*w$ʴDAV[ b-@Ǡ/2LsQ -`O m|i3G$0Anp+A V-Lp)yFGK ْ00l){w,g؞}c/"#滑L!(R@APȥK!M[#Fn vrl 0Lh4IRvt_"UzѢ5T FѵUS[tT(eU1i鷤x&K-ąt,ұ]g5(FXa v OBD;_ o \dRf#SϸfVA]͸2/(F)dT2#MN~|JHhI#}F~H>\ĭ3H*\8_տ@DxS& r@H5D@Ң| !OҖBw̜h;TU/||VX5&8f.L5&B 3źמYjvEUt=58ft~4w~d@׃BqygDdn<;.;ũ#r4q)-8;Q}{D0p gwv-r>hM %.hW@/7l/.r6izjqSS8=peG{|$؃;٣` g&82/SO"N 7ݭK›E|l,Ocq3Y{L$iF"<)c@TƴY1"I|[htI7Np5QE|b S–ԕr]y ~9ؖܠ`kuhKbKw87c`(lR12b6OOџF$Wn81Ǣ}GX} _ M7$=:Sc_uQ~s2,!;$z\5DL90ާJ +"`w׈ېp\` QAhaC=$|Bx2[ pbD 7QßJ%@|G~";ej8ZSy&mN,̒ .701~!1O*JXh8VS7D$ .6-fK3/!0Bv潄 : 67Kh׀="1}?b?<4IB 5 OQХZ%ۮYw%.]yc{QzNao & r8/( H{f,E_7H}8WƗ\>ahs&8ɂ7\xpL5sN6|fL$؜.E,d8m6?ԛ70(59#VT\$+YfDNkT /߸u8(eѽ'0S_O{)y)x)xV {5\?-@?M]U4Rפr85Neb۶0CR$E,J$$Sd˒,m.bQ%^cK&6]1"P`-yY΍1daA.d(}J< j9!{_+aSq:ݍ]rxe߯p7sNK2h@" U2'*ՑpPlU^"mRs~)-j,}*Tҥ KU=Tܙ  2 {X]! *,U:ZNU],rHtuJOW(tVں \ ߖJzJVn}^R yt*%I~1)M HƅMpٕD:g;Q{f=m3-۴}di]J$pl&ʄE\ZkޯO/f*Hk32ͷ>&^Ibձ.ë;Z L)mX :]=~u-AZٹ ~c݋KvQ!tKKy?څ-ٙ4;Uؙ;W8Q)Pzє=8k,mx7 dml8#x լ(Tn> 9)6hW&R&%~7YVz HJj#0IiO_E7snNTD[ܛ]Wk3o8*k t=tCmZQXI^,Z(;N,=諒`$]|2bOշ^2ح_=|.R,,.ÿ4LTS'ߋUhd^P2:3\G@j3L&1p y&uD(O;^ ;ax\9 :UKFK< c[b0l^a>u"A][D)zAA. '֠ؔY:HuTyw1e3MQ[ #\fUĈi4,(b`OP_od@@9XXeOu WFzzX&k+AwA0yVCNm|a^WBxMш\2)E?3 zƉxn$Bҕ]JZ'\đkk2+efLO;S&*Tq2) ujes3BfO{QX,Ln snk;WSUWgg{oo͖õ%3 ɠ;;?gfj{j[NΥ{{+z&TOi?͑ۇRk&GpH>,*9B>g11V@+Aqu2Zh93$$OD/a3D?dNY#^iyz˱T=rrn J M7pB1MOF9@*eɡ%wrh_]FN,\ߡ[)ѓ 2b# 7rd/0 C!C\C9*6 D sʤ)S1k..sɳD$;ub=L 51xIe5%%?"f3BLiB,!\A4S-R[ڣǞ;fKO:+: ,m._J3,fAaOĝ?W⸸#jT=)B8n(Ab`yqfJ%ǥ/XRU.t̅\ȕ)"r|􈎞֑154kxzcpcߚo55Ad lNՕ.+iʠ t]QG괞Q<_JY[ XVsTz\_Sj0xB@+JZmĻKŬhV+ 'Uf #\z.'ۅG/ v(EgL#FL.$Zd^..>lڦDԎp!3_O(y_0TW9j-,u7nk %F@EDL]AE赘5^S`S>+Y郞bDQj'wqU3<1̾f]&7XQwq^%$Db`ۨAFB$!S"`HHEH4YgӨ[EDDcUțd6޵+{ܹw]ZZ8ZK ܶߵ?ńkd&C!&B;kcqņƝIga,gC;!Gg6P{zNǚ6ePu.B{a ,uwWijR!xZ5Qj>j +% RQB)|o%ɬG8{K'g`k}㼧>)g7Pn0 ld֘5gs}~N4Eab9]x!s譏b9%-jKC3A\6S*ƻ~)T@<4Mʹ7U75~0[\JwK(-a-a!NOaM :MI g3JRZ-@%Èvta{69ljuC'q)bdGH4-`RNebD z}K{?oO~nŽs#G]k_߅o<4 Kyu?}/K ?|yv=:X(A y2̗M|Nk_ü菨ȫW%*U1qOЏQE=-d6)Gv,۷6q aYM1EIW;ƦS coRDbDat r50-KĤ% > Sf?׈T'ZkΝz}AZnՁ u*wMWs~3r3F3a,ojјhD .l2i tv&||[hVKĥxͨR%Qx76d]R}TI.YƤ,2b)A8%⼉ !O˷re!͉v]א n"i*rxywo o?-;=c~rU\pU^*,·R+].YX*lrG}5nN-Q f1;8 4$`!?d|egp\"XRX9JQ#*@{E lJ hE [KgP췼5}8uҜvK&'((.f%Bh5mA-ZVэQgUzK*im{;׊@uT)YBwZjxH8%ɦS$6T5͌!ym"L8XPDKҥ(MW6 W7@ WN9=NpA7N*Ƈݣמqm驷f>1 ol̑}e,[?`y)Caodh4e bR-3Cm`j&&)QHi4"Y,ەug%8wm$-l큣zT&$D htU_g@CjIST@.AC]âHޠ-9P Ys'JK#] XP4;Bņ(sr^;bDp:vwz4#39+}O%|Hf=rr0ޝe( Ults94Γ!DZ.\s~ [:ރ3'E顲P⺽=TL"*R'(+̣؝XR߆kUպT'ɤN"o^VJj%X%g4V:OϾ}jm:{m{׎k׎}m8k)1MG)H k`B6h;L]KNPQV4E`bib 贉?nUZ q;Iawwν};LLMw5\ޖׅj^ST.UJ=׎DњlM!hc,4ݙ4j10G3ecT`\&ոyx?3#6ad8Rn6kE.::syXꃉc ՟JjS[LGj2uTUcx4F\('h﬘I#<(F2&:Dɑ43~$t(EQBfe#L6u0dxswnQzlU"UU)A e%SdɌ3P&xz"f&FX ]:=w??J6cAU:sZ*IUzROq)$gҁtkTN pM`h昃QHˤ&%j̲yʼhs/2>=twr״i@S 10Z`b\{KڦUnf|!8%(O*mnpnv2VI~W;JEY%Rf~V; V@dد18>yѸX^aA^,l*ثLfR普_%7G`/űKtƟ6+0oW?G淁u_lm5KBυ//B{syCiHv5ݶ'l^VYxsJPcB.sCC 9:_LL4GfnH"Jt2%ߓ.cKk #J*;ܓ ӋMF6]H97>9Rw%H;'dzU?ca8˟Peh,O-RgWÈ;3T/XTjeM-łf%4ey ̝udi Jz嵖_ʪx)#G%V+q]Պt$Rt#Ͽ28W_Ul.,$  >Q C{0"84־E6)]>,heN%)iKP^csuK6ejl.bUhMS97{f%SU]M 6۰u EI uy47eFˮ(Tir ܂r!,-妍{wvmkpk-c_v WOn3sd&$L\bGZ㲛NW l#;;]vNqNesU`$>Ls%Zm8V{&,d< "!tD{e6Ko#!_90JA{3[ǐyRu1Uh_I1m\i9^Obo]sEunaw G[ rZ@ֺ[7[ٜ̚l~Q6Wܾ/Zg鵾]#cS@Nd b}wKtGd#qm4.A oW RF_~vKp<9 ~4澙l! ^n2lCkX@/L;1=K lعӍ;{`#:5ˆO߳B9ղAP9kOX4`={k/\Uu܀Ą$5ZHnhBԘDH %-P 100aF3TVbX50c"ZGc;ȴPiUyZ )&>g_; I(f<뮽Z@3Og6c@=0s}sd.[3Fb<;?٧B2߭ VH?:T>HC%GF}~;)j)mnE\np$9fxy/ӨGtYCNF,ndm| 5Öٲ6h ݮ>t;+}sXD1Lx7sN/U)J9GD)w`xB^!6/:7cӽm?{8VfZ'fo;8G9J0?䒸>ٗ4څ;hSo^;Y:|Iqn:OX&;:|l;9'F)zS;|AjUcTYL2\uxq~h}9|2lG%y ~(I&Bq 1Z3b;TE_ͨNr@Cry&D~NKejf `mЕOB_kXOֱ W)) vz|SO&]}lSaU&GWj< i#x͐=|*0x3$A ?;HD.=_"H9x(-D`v!,ArO: Bv,-|w9?ߪzSZpZ˔jȚQ別h{sBuu~bv>מnA8Fa> o8;g=05F >ӹMɂwۇW/]ك\ cAȮElW7c>7J/AfC@> Yۧ>Kcݍw-pWA25j UzcQsrj SpڐW# ϯ yD@6tDu{t,׶_O{蓿,׷=pm`'h?ړ~Gl~x;\tzKew;N}%}kIo.C8FA-5%\˺~Kn25$΁\;sF兾+Mj`P k1 u!$!^{A;$ן\ρlA;i&v=[kZ0ߢ~g\Wz7˻suQV أKh'X>ee!.08?A̞ȱ[T`B8EҡrZ1xDUs&0w rHq>qz*UUEؿy`4%uE.{ң"a449=g2C\9@ al.1bbFH~C~)3ZO(rש4:WL|*DPmrg&`nS~Y݀\  jI^\:: 5&\#\;? ;/djۮjKuN\9@ƶ.]ɳ7tmRe1'Y/iGK@CTY K# C1"p7(eOg/` |@r~úT-|| 6s[|^#^<Ŷc.EuHCm'.? t^pA*#j**{A,AzT&kwR;Ҽi\@1ԱiȎSqfwkH3s΂&-.36Ì`E?^{!0O}$>wO4<="~a5p]~;u=le Ylto]ݼ?ٷ@ʹA AuY6ϺSl?5|<8ߧ٥ }k2:@`I"BR%ZRZhZuGZEōPJm*TJi;``;Z)-P- »}rHgsJj.l9oxEF2MOfY#j]Էiƾn)wQo#,u^n<+hݖFBR,m0 GbC%Ѽd o7I{܍ӥ>obp$.IeR{qmxsEPs_Ċ?2#0+bld0e޻&wk(;x5y蓫h7MJ ZC6‡OkoA;Ͻ);rQ<.F6EMI*cߙAپrޯMv^wn}{߆R?o4"2) vpOIU^dۧͤǀ0iA bz#>9Rff馾em.sukb.c,gifu5?h9쑺d+6_5:5M,4v4VF]}&{Fi`{β-W_جnlXr:[gEv%4Nr¶o?Z]aձ%/ wJ0V7?E 3s6nL%k˘>k -{b(2f[ OcKu{<=w#Y]d>bTHNxm,vuN5eeJ7m&"u4K_AHNڥ> 7z3wZKOCԛDo }ٗ#Í2̛!籦m!#[2/SC_q_.5,7`HHFy~.ʽzHF[ n)NƗ4i1zyndMMޙg-Y}SYqБUF;f:F#S|*e?Y~)8{~1rWV>84nt&j`^Q~^w%aLI7*uٛO0jxi^f-[kD+@O.׶:VBqGwyڶfU4/31Jvܭ۰~>%\m9_cjkM {jeחddP&zGw?Rc7h~:oiLO|V?g&MCq7>B'EqŚU}?abl.b5Yr=_>&#Sj9R^O؆nL.o@ua󆶉|/LnX|BݡP̉(6fV>eZ0ol;byn=~pzAgd/~+)<{+yDa.K@V"c?Ls9+efJO[֝/ݽ_>[mMgL7zf+mnm0u9ސa5~t Va/8^ɼT 7bqAw%~'q'i5kU#^ CzwKV I҉.LA @]m|-OƟƟ}i՟3Ihnm*}Jb|o$ect.sѹMl|$f1C@m]ڛ1kgA-utЦ̣Z/bx?vf kJνixDߌzN[tOhbfZ6#Fqwwf! b}PoX0͠}:S (Ph<ޕ{\^#ay\ a-ߐ~Y 7P -%dlBAƶUorFArS^L6`b?k=Z)7$iͯf5ju;xzc܃:8==rA(HD \LՈ">Pc͠[bX6!)I;@vK$ԴcФTkH$ vZ'1NsiZݳ緻y]zQJsC;G\]JΛłGň/X2~gcTFaN$P RQ$8-{WG6_/=8Hp"ߟ9*~s%cWa4> UQw"Ǯzrڽ>?}﷾OǣĭLgɈ\t"94VEdw#Uy r9ͻs|<هY}@>50?m:J'ۧ}X.š^YJ >AWz9deSHa\~,k5c~ŶٷlK}b1}Zr q&b<|F;ʀ:*#_@>lYw wJr䭏/Q8̩|B|m~ڡi$v|WU]hu (3/2b_CQJ0Cg tasoAMy8~usYeN;~%š-_f!42V#C ڔf""e͔;FJHI\OyߖZpt&p ٖ-~۹eJp,-,#x%zd.\,Cq}UG} knq:$>?CògKVb3_Y迫K!;, r ?Ӿ襭$Q6=φ]Q]泱vՃXͼo:2I>[x;z\O][@?sl"rjsُ.ڐ%C<ut=綗ziFu$Η=]-[L=zUsԙkh?߃ŢAL5 kQr:{d?serSgITRߕ7MXS ܳ>۽GXϰw֞Eзj`>v/2WCœy< LY@;p_7WҮ眃r}w~B_JP|2mWDc|8U3i\>,⺾9%ԀT1üŝyPiF\2W0X^'{<0ȧ (:L.d:0)6RmZ콓5~DW[Y~EywD_hT}@ENBDv y:P&ҩZjM4iS|OmKISTIN$tgL!i;)Bd FkfU!k`b5>b c ™DSB#՘w#ىqnh椚eޠRȍ1w_<-I3bZSByOxQ.Bxm#Rlw{ ;=Dl0r1g)VIC7I5֗9'XCr#{kTWg67Hmaw;{d\Vj&W/+1kaJub?gMHiKX$ǻ9ϵTR'& L `"Q=2 &9P4 g5[@8 ًfC_gg2iBWA_} e̅f.c2<г Vf~ ȼۼk׀Jat3hK,"wuB{2 [CK!tn"&cb6A "f1}%_Bm0= {<ܑ/OcbƎe8;du{BɇE9ͨUҫ-V=pNeA@!@ `bA^ ae L54sF G2m$. ]jd;:ě4ĀS m]cK;zv&ـ;OC0"Ί8+VX4Xƛ37\r<#F@=f*f*f*(BX&mxWF<"WX7UFet*FN'v?gU٩Q^Z\*rݡLv(p`+7Lv@d!΂y''F¯|pK[{pϦDhBړiioďI;0#hb@/s L&v~2~/)ãn^-o)KPU$\ɭWԫ?WR@Yw&\YUzZUc2ق%/ZJłYzs'\*zUzbwR-y+YtGj5\e<)tpy!7\ 3\ 5\M->.u&1?:2vAK\zN0[DˤQ:i)mс]cq]7a=m8Ci8"F(Ki)QDl*fX95\k8DV Mw͐uʛNvܰC\b5ˢ H\jūXŰ?Uq'VMou8ho;Ҵ:qI3#m#k DB GlQ6R (nsmvlMB)Vb#.!",EE))a\UDg2ɴۭ&*H⦻] !%E+b6nA&ZRH4ڞRجǦI<hAnE7/I7/ D"+ z;T~q @}ҰU i֍M{'r=9%X0Wx ]DzFUr?Lva18mQck7 ~*y T.C Q=?aqks9vpr] mh7wgnyFʹ)~ ׫JU&pKid.)%,uY%fn8t2 KJIva*F~ZzGԷz$p#ȡܒ;-9W- H0,_ .m n&  aR0idLbie%Ve8FL1Ԗ#z[[y$'˼;\[C&n,&&IbD$ 0lJ}jJN& 8S8˧"_?a槀gzX{`kMg̍`;Xv`\&v7;5Q=>~",2~p7A!Fܴqr6i;FخPO .&q.0 E{Q_ \, endstream endobj 544 0 obj [546 0 R] endobj 545 0 obj <>stream H\ˊ0OqxD\̅]L@#9&P+ݎ^^x߭dAjˮ6A/0rW9vNW^Ul[}Ფr7;#ETq?zk1lU)ߎ Sc1W> endobj 547 0 obj <> endobj 548 0 obj <> endobj 549 0 obj <>stream H B endstream endobj 550 0 obj <>stream HWwW~{Yf:E $"RAAԖ.(؎(eRL MNĩQ!;OL|w]>y!j#!q>9#>Of H#i :Z iY3SO->֤$&Vt~7A2GXo=eƨvŇX3>yz7Y8#^5vOwbbvS!Rrwؕ%;0adJQʥcv*֖a;& ڗf'JڻLe|o§.BCs]O??O>?Va㡴!0Ea?-&PU$BRF.,{٪"$Kh$9r.zܒsD6 suceՎ 2[)ơ s ](1|J]b#e #bZe4莁HU't Z~-#Z}ݴ5b,&znԖ^g偹Bb0Rl^i'픏|m9J8NI29KL}z;VA 7 x,!lt3=iL1qX38K;OLZ75[-RM19z_yji簙eڕm lDZ K;U_ !&e쑫rUTSqΑјa8ҹs\Q҆+OUW6YVyiz*QwDZ&6uv'8V =4Y9cٙmЖ.k%''pDa?FCwLe$(IN̕|'2_ޗ_a V"n G\r-b+hW*XuJ#Ui*U4PکKmtkv}H_9N7g8ibztSl\Lקn{;=}mkpݻ%uZ&7碾5$s:K/L<ԾrM =nQΑa+ipUPK{謐/TTIy)0w-])Su.G)rSשPPSHfS*Cbf^ ~̠{J_pqKCYE8)j"[ɸ?KrCD~^@d.7ܭo\еPeP^2H=T !K,o`"O@ &Pjcρ*6WL!l`:p[,D(1#Dl/O=`Mmsy_xf±Y?F`d!Щeӛ4[H$c4kҵ˜GQ7@77 ZKfcuhޏ(K{rɼl&W;?ģȰɽb l!MvgE&A 30rQѷRHގ5}b;acs.E4xD6oѺ =6J*`[jH-,2 'M cIU!%%_G Eu.եTR]7CU*!sؖTT`j0jNܺPtNӋ'j?uV,G3J(b41+)*&!Df~yCṣBZD,a6 :>a36,`/d.tUx*c8N-vy-uS]UPݎ`Iy9&df$;0gݺwܩcB۶ jݪe`@ ~fjb8.).d-)Q$52W$RdJXVUz)JȎA^x1z(z-e ѡ\( *(PY (y?߼y潙.ty8`IFJ0YsIw0!kWB\$,LwӅyG谶"lQ:6ͼ cQMOwMak'b͇NPΩəF\1N@:0R!MaMCYB^e`s0b˰7Xzj*ςJ ;*)K I%DL>$T/~J)mBuoai ZҶk:Lrf@ꁦ1ܥ|͡$SEmo-n_?9K'lRN|PCOa_g"5A!fl.'hh{J |d[$ǧ$M 6,sƎq[Ir=@ &%։9w>Jt/ͣ{1OYirn"Gt%8r cKpp%W8K+p]K饚t=wpC W:Yv8;_A{¸pxelB5,rSg75 HUI癜_ uVyCW(.KA~xaX ۘ6s0dqBi : d_UЕY`#ĺ^^X?tY!0h 泬);;x6LZ^![ȊC":ܚVAF-EׄHĽG{ًPl{.0QI.kA&8!68Ƌ'nŐR"7McSx4%_<: YфIs'?~6Q:U:9} | B2 uv״kT 5 ge qc2JSsKHƯ0~M#GOG.?A@Ϡj(P:!k{^z܀gDyfѿ 8e;op,-rzru0>=p.6M=EѿF1Ts4R0V$ ߒs~ݽWƿFKid5(=%*g ζ'B/Szz;S?2p^~9>7n ^q$'v9r33rP Т$oV,ȷ$;դVZԒI܂H-^~FwJD<87Oऋ%(zsܿAx%KBMkp(5m_|Tks-~xmAv;}-~STU׭fޞҩIzWSK]N 4ܩuKVU(--LWxC*OyO}ʔ*5eGyJÔS{|:VdCOtO]]RO;S"sqsP$PϙގE0L w.fh:o6m ߑ)DQMKEY(,Q*rMlxkaVKkeIW$Zoؐd+8쇆&˚@]61x@MCޣ{O;ݽw/UR_ݸcN<:.cijesi2 K4-A4K؀IՖupglnzSS Y{GN쿎M(Ef(OURs)ZKQN S:Uql&p3>5a1[xK岾~ 䲞[sBo}}|nI:$O4=U"[8f lV0x=>$ڴ6L ՐP ޤfaOVYNT۠ar "|r:֡} \%T¥2 "2$}) reApU9Ս{ <1+Ǚƙp+QQu5"ˮV>[e]Vj3p]UOL/U=ޅE|ak Bv*  d4?UJx $E a"ahƾip7[T;e>Y}fwyko7}NjkKd>7LᓷOnMs`;ג.ۧoXA;TQ W=(5`x xbb6A%Wl$Tb(LMBU'j vF:/ AWLɢdln璉^ׄ{P $c7www}2,E\VΈ/sԥe;|gU@eggJr-5ۍ  \PBHh& +DS^nI)/ ]Xâ Nr?yxC@BE4<% zHd8Zż%111ŸB[}\U˥Z*8zwUlMOGTXa\KٌꯆL .4)o"A{*⌗ժGYt(5E(lhoobzs$#"d%]Gd1C>_cאQ<653٢9WŸd 2* ;O'={s,Ś1 [3tJӐxbni'CY<:^c͵{Bbkh4vH*hqtٵxq#.k$T.It\(]{XL7BpilCfN.Hep9l ?qM]wy:R. .(:ٔi !h]?qÄ.lrP0sT}Axq# eAA ;RٝfON*Tϭ|ֱMM8HH {8۶㝷ayC,_oY ~Sg7vcTďjWrʘsyir:}7!K&L{SrCp/gs q׬qw80?7@$M@4ۢD`bb qHd(QC"sOD"~ E0ʁN H(TUv)ۄPN34".lE#eB4=N#,26FH,䷱1b9ވFpS`!p5@FDs"<'HY_"j+H2MHi {@:6R.1qR\WEVcXњ3T%@w fXՋ^{BPP0$wg&aH&Lƒp<Ґ:yFWb(ߖ`zA#14ې"5c;2/T$NiTmIz@:z >%N9ZLBk_`HK`d@'M\*ݯYԱEV k'zoǎp sDܖlK1`Ek< KOBTʇ漟pxzƽ~ކLmvBtw0`ӏl4 Q;~ ԩ{t!Y⿿fHJ\B>\ 8y@H'=0qpUp1 (ϣVyd)(ݼ0Nb]1$d lVƞ]^S "N7u bB&dt dBaAt1{O_yā_"Ú=OĂj?ۭ~쯓XJ!gpOjhɯٚ8|dɦaf- 훜r31fKCnx>Mf OQd`C"$֤ ҟ$p %lgWkYl#_VN֟])wZOP&^~ՠ0R*=yJⳄ:GĎ؏R2A +7;$~120SrPr@Vj@EӒE&mE%굎'ڍbz^iZ)3ߚ.4KEugo_6{l;=(!?#W8{1m[&}=SkKPW_g9cw8 ># 1~ wL;,W[k M&N4HW"0U@H *(ۻ@4G˞l{%xFd7a!&JsСQzkB'\x|)Y}82Sa0 3<&ڠ8; 1܆ohd- kvþh?=o.6XՓ{:]LjN\2<OLS JTTFK .ʍT}ߦ4lm͔tlM-4t2}MoGz@ D ȁOr-)L678c !*jABmJl1MgnGHyh㱾N 呑f♵Xpoﱌ1pBl}#ŐTXIX -n` lq,nƣcaF!c)i3fтh2kb%-60^R78kD4(4 kA|/ )LVePb"m@0 lmݍH,]LR#Ԡ#(1/HR(N'[j;&&۾ۑȎYWGEsҪf""9~D\iLb\' ?%'#ȏWMR@ fi_!%c?4cX`I^PTc܄гF?0#M)w,' (VaqJ/26$Fr#^.GtlyK]D:WOLs9.ʲmQ j!O(!<8T?G$Y֥"inM$b0t)g(;iN.D%R\sd璳q94Y,m_I]PG>_X.F~Dx "__UN8\.tL[g;Z"oyY|vK`}66ח Un[|6Q(~]H\*RJ z}ĒI;dR"KúQ Vb=]yHcvy1Xw.`z<-2}f/y[MGю 9wƫC3E:XBNwQ-wDSmҭjo6,MĶ J s>n0Re² aنPlpU&X4]14;H+p R,DRVԟ] 42FTalj a0OGyH )z$"+.7-Ӑ*3ݽ.11WV5]"sI2ҩh:\Ui$Q8bi e6>UhP2O ] z>zgyRR~Icp#']bc/^,DW9ef4ؐBV'5Lv1f_sI[bg%wQSئ+Z!yjXlq# -δ{ʹ{a^3*We!Te!TEmZjm`|ط&Z ϰ"l†C7}ȎZصȳ<(*̇|(*an>F aWo= ~#7v kvb\O}EavT{x³2(g[k$-@ HȰ6S({B}QGg3jʰVeUvWewS :Q*6S+3Ş'jfOk-H)/J]0yRDq})|h﹙p?-:پxg%`lhNP @@" 2xJQn*B9!g&dL |iZOqo<r#>XƬikZZv&%bq+ֺEXh&,&"(fT10hb_`F3]G ;۩xD8K[ ( ~Y@+9pW`6KDF3isdesd(S6i`œ@ l2H{ ñ hG-j?O26rjt`;Dv9UbۈF\221,xȲQKb)=X](*B9>|༟hcrzqj (D+4weL]FA,Z1vccl8i.C<WB1]ee%}2̑kk#Sk /nBLb6x_zPětd{JLr)ԦSґ‑*( T a :E};'s󾬜RO S َvS_5g4 F5_/jg NlXEPp;8M_8qs_Aӗ/x;#շ~x_,]o/<:34??9Mя1 LܦEƜ.&rXj R:c}BL"z u,Vb0t˺{@E`I7)u z 7‰55н&T爢Eq (bͪx(};kOotos;LLW9A6ĻJE 8Ū c5R't> g=g;_G|Di{QN`Ҧ.x&ψSFlԈR;io6Ͱ]r;y.4qY%Ȉ鴯a]brl%[Ħ>lƭpvaG xO,o2q؇9o)W*rp(9ztTpv%m@5?{\OMrzc C$ݍe%?v[X<ѭ4Q%Iki lihJt4t1P`Dw< D>` ?Ţ_QM&,0ߘ̫'}uT/8VM~c|N x bb UCAq{ҚGk: k̃اm{[ b1i$2Ք1x w5HZ*]5wnP!F7~f|WlWww|g;ws_&n*ukDl$:J3`ĴZ *!!MVb-GلzSUi{{y֯Yڣ=O>3c}&} *G)6h<L̩{ж6DcgbĝOy;[]$HdѸ>Ӻ,PjԱBha~Sz͡w4-KE,,K>$z,$;1DGZ5ŠDg1L,"۳Y~<>B0gO矶:>U\7yn I-IԍJF TS pI[Gn“ÃB r&H}bR & S"hYVg[hܚjcZ,z 2_۽mM"F{j UG8*T v؂/Im<&(DAP:"-tV:֭V;'iDg?*I1rnS {}ᔁy΀g8ͣyz^ Dg1|S[eB)]Оlu۩pNs;48:cjSS6i~' %Jz!As2uIC9űU=ܞKa ۔=#a W:A&*?x6 Mu=Vi|]?3瓓'T:s\8I `WMvDD2U qXqCϤzڶc?/O]Tţ#Y[ܳÒ`Ns]'NM:_tF8.1EVEoE}̗M20_Ŵ&TX H" Tb. {X@:˗bG;,}մohM H(z(vFYBLP+ LwJ'SIpk)-VA QA3h joFߌ5o>^ѾX`,oQRXTt.1u2hyCxӱKG@7]Y.tBa3 )R$rA|xq/Ye-p%-`MTey Ԃ%LA8-8nCIO:.5 n4A2jz{ hի\jsJ׶h筕?m[|щTЎ\3 N +$9|hdfS, )Ytr.:y ZɭB9VԄԬW0C7aq5OT6b.\+\nLA e]0k{Jqh(C~=H'6O9]^`~JP*cpBP*VoaC'5t0d+Wb6b!kdش<&s>\Ӥ%V_j @#S,HB BL:<h=SaSΨfQv!mʏ#N3ns|ϯ/'Y9z^{]}B6[^巃I, c2ߎ۷ec:ݎ0Şi4Mj>ޛ>e汻$w!a+HH B)OZѱ*GXRNXLdZ-a:m-@#P;5Hnt'L|9{K>m 7o0ڭh>K} ۷S:B=Q9Ԇ)<ݕ5X2V4m74çkj4 PnʢjK X^K6q&{Kog3N7Rd^dc !禰8Z>|K?=/F<qgCwp\Gߊ7CeD/>OpZ(jBqP]L8~l%+:> i6nG % $%@*8.jDIcd0?;|/ߪɉJ*00 *)uU:ؤ)Τ3ALS]r# F70>SJ'ZGtbZRjvGWmjK4~U>2[_%9w4+pF$_,dUjN~λl9[Xk:GsR'Q~?i#jo+p Dsw>=QkrˁyKV(^%۽z L'gBeγMr}RکOi[t43lӉi1hiД==bww>l#ϋ!wyR> d@<<,ؗoҷ>ꁹ Ԭn:=) j^&_cOŸe@L<!C0m 9Oy˰ gYѾ@̐Ѓoݘ! ظ<<נּX٤],RPDUyޞ~S0+E>Um<w)m⮞,$WCo#Z] / o^)x6= Q}7xfʓ1B =Es_7e`m=pkVD6.88zp oƹ_1Ƚ5nf`ck_KF0=lGb@ _|&~G( 80x( οW:~n8H[q sg7lc|7m"@V'zf6/-'џ.]'sD6hYṋgt=4t#sp֞6t?OxҳS|v|bTkou2-W^WB dEEy ɾ.w#0|2AIw>ߵAU雜#|Z'~xh6iN-x3ܯv ZurTߥоkp׃H]YJ>jWqx|bGy4s璐_?ATxRT{^d)H$bs(/Ci~i ?c^`,/bns9E|\&I9Ə{_u$Ik`2Bϋqq͹cR]uWbGﻯhNMQl/9i'ihMbU_B/Ѱȟm9a/!` ^yD EܧT1w.mR^t1[>8&8mHncuXЃgܕa2=^ה{A}yk 291)1Ky E175 Ŝ6wQxJyJk+*=4-x9 P.#f,r"Urzt՛hCm@:W亢>8GA~7gd/ż_D1Ay74so?Т ~ RBjuOo2 \ u\*pNusqVir z=}iUqy{"K E ¢@I3j"mT2(Q”98 Z8@j%S!`}syesϏs|Z\2Y]A-ك( ދr }W5 ΃&zX⠞<ގ,/4'}-WG!ES},qM?N{&UܟMԃV?6u`Gfކ},ઠ'qԭ{G)T'3 ڮ\íJ3_nU!٫7K3se'| ~uux4x跫XwW;ƇR!k9WKHn=>QOgEt͓rr_*q9ȟ;Q'K<ϳ;_vEh5gpXr0])!Z} _~ Ev&r1䟏%Cl]u]5s?n_;LPlv-fg:YbI6oؘO/s h2%#?VZȫVcWb 5-9C]WEVc䮈k1Z9eyԿv`[Qf-AMqAJsώj;p+L{#rRx]H{1Gk=wGe5we]5`btzqS-]Ruj Yc꽭 ;eU;~͜fy750Ŵ9]g' sr5ϗ_F{F`wP~rbl9X#?ENcXKJ2wrErt753q꿌P%n{O=׿( إ+W#(#_-CX+sbY냛M&5y]+|_.јpΆ9s5hۇ\4{GVj]xUE:a΍dQS k{^}'Y?]ӅێCu0Ϟyj^:}_h*t~]AǴ KSv&77J̩78OAoc04)%} m-?M~,<T }>g!bސp_n@ՌW~d0e]g r05&oK~>:2,XFF'DJ&22[+[Ci)%oJ}r{&s9&)9wJ?,ZjAsn{LFx(ӋA2j U+{gEFL`4{윪[@]~:dYYn tY"^hj-oU,{騘PtýO1^o4xr@љ;HOMxp,c^/m[NѶ.۶юYFWٹJ#r{#Nem|)`^߿+ԟE-0gdzLGz Kt\Htt;<Í36m}5Iۦ٢5;oI}y}].蠿YtrNrY(SOI{2. :?Ssb(dhw:# c*%' ᣖвK! c}ExÝa|ц?:#X<=b=n:uquq?>~}9Vݙq&߂7[`PAY{fǙY^1h[n]Os zWU]ax>r DC" ) H"(Fk2T RQ(-Z H; `SԎ8`vhQ`mU[/7igzgY{>k/FuråݟWPtA%cf#^oEq]+蕈 [b8{w>VF|',pTHNxCb`f̸'d+GwXouǷRj9؈1{Zzr+n"gߏG} ҷ13AS;x6) C 4Ђ~ ʓ-R7J71dEuY܋֭X?-ki];h?Ρ У`;Qqh]"7c|{[fΊ9`Ÿ@- Z1:zܿ9ϵ_I,ԍhU eM/U~ə{?u1TmKZ2bS镁ؚ@Zr1yeErr#E*!?q2I|@0_ /iF NSfd[3SYZgGG[tǘO&Z'SSG} /)4ﷁ{u+رWG9ixg߆3fLV8N$)Ws1N9נM*KhFOnұ9UL򍡧%:$]+]gMuuUY]n!Vw(GpobVnH1Q+ƒhxF33Rj 8cDqh?b͓TZR-++9\d,(fSnshk\4iζshZ~szί¶ M`nC㱊

>]\%I9:M59(s.lkrʹ\$|8{ d+MijZ Ӹ>ϰ-L&q6Rݠ~y|/<'5/7KNqcVg!ZFm~ռ{ |Jy`Tq7'-6}J˰Awηd1 H=Ob VޖIԺobR뭔Jﰔzߗ o.grDn<ۿXnrǠgfacl~ݡtdT)v,}0YJ_=_K?1}8m=üMh޽ix\̷o\XtO~o,2>=+;R/3]L}ȧRUa M $"W4j+odx~{rLR9dy[ָ#ᕑr/ 0 72'9~`rP9𰵪O`Z#AS~`Ǯ1ä+~}ԽV8/hLW/M2h'÷owHyC*If\xÞ|///-Կ(!K;#+e.ĽkȉTOi*y) 3W]qHj53 %'qA\fj)8OEEKvmth YeE6DWmY`|Z׋uK׌&k_mҷ]';bb1(+br|g3O+e:3ùV6E/S^aoo>tkA&9u9\пB&QSY*3L']$ 58(KG<9ꊬW?vqd.ܻdBR~G}q{S$!" /4\1Uz%9].PLB'S_Rv: *Z_KgHA(`+-w~Id*ig6o>gu|,COoy~[ |T}(88i?.JO.q?}pM`1C'oZve}8Ƚ-.ڬٍBǽi>#KmT:X#,+Hž^11nٝ4Vl(r]wZ5|1^qb Vy6(}Se bMa<rOuGxZ|w;Q9eu6r HsCF1n5J6/;/"RI|}aQZ 0罍rڭQ,ȹ;\@[7faOmra=O3-韉k1b"~t ڌL d]㭱+lO=0sZ/l6q0 P)9I|!){.\_)=/\I+݊R~7\S5:%;Cnd f+1_SFE#|~a|gx wr;DgW>Y^+~s{Ce˜g`‡j`9%?za1mE Q.UZoaբ5 D;z?YL-ߐj&Xja'K6+Q;9N ~17Fa))Yǜ}5j)--GClkrǑ:nmfsLNI!|-L42YP5f앚j~zewoBUHYd~l䜤uª_TٶNT }SZ:~M{FmMUd_|j!SE=RcbvS\W@R\vwsL_urkh,+nkcx,Hc*!1=n%+Te4Y] k9nEOK`W?eFb=w%dS5dy{ͦ@4 ngis7j6r$o8iFKH8lf2֟ZLS; 쏆7[A+̤eV_ Y!3)5CV2!+GU U}Vr=uo\,1UsL%!/nƺon?d+V6>T\12b[j{8Zɭ~O<^C6 2'qqEe|ωm ~A#>xiGf4jN2i+ :ӓ}w=&*P?S"[}:iԅR? pE'Y^et SXS-L4 Au)>86%=?;;Bp<)4Č f1#}//U~ 0{.x%ƾulĨWM֌ů}Xj+.^;Z]Qށg[ZVD}+Q,_*BYGQ6u:Zn7aMp`ꃕ*^QOxVZY%5 5 5 Q1B?8tLGPy3E[s3bZt7Rӈ'bfTSS3vsFNGssHKLj(,{sV_b2F8Cs<ݚ8'Z\g+>a>Q~K~9\׭J|ϱ068 ˥iw?) mijcW9r$7N jA흠v(}-(-Дi^k;ʵFQd'mIL?K$F^R6&ȿm38ɯO:տy+bl~3Z5ah ǍhIY,X^3.S:F?oQu1 r3w6wpmHfVuZהy$҂>_#rCWBUe5qтu vֻV\RwvsWnp+n^Pzxnj_e"$ u+8=3,{ڨU&mVk$]r=f-w738D 0Y5+7ꄎҴ5O{փ̤j.˩ՒE nL>z+yrFx.mN{2_ږb!ikSO`wrHL%(CB+UPjWa$ !BDXD Ѣ aدTr6*}H7 er_H LYFe_2Qꓝ4#$!ă[.'K2OfeJ4c*115?b=ի^G_ fŖIh߀u[HZ{d`)~ӓ?=3o$>/%r})ESi"WZ6e6̺I[}wY Ǣ.m[ `kuW_H֚Y \Mp W5̾| jS/'p^Pjq7ؓ6nݕ HYfo2K@`jNυs )Ǡ>lrt]ᮆ)G\ )GnnhrpyrmϘMC4FƄ1c8!% N&B3!U8vg.D!VNøRI9_Q.1^ϐ稇=Ot{l`fg],ǜx]VA̍UI:-uNQ΀ڢjګGquR.;=e~o5^'wQFBwIJr$ǝbB=~S7gXNk> 5TIوxֲXeulM ڷ=ʚRV#UU ^) 'gXLv^(l#" Gi*p>!(P6#j.f`,.e(.f. T-i+0 `Х@ endstream endobj 551 0 obj [/ICCBased 552 0 R] endobj 552 0 obj <>stream HyTSwoɞc [5laQIBHADED2mtFOE.c}08׎8GNg9w߽'0 ֠Jb  2y.-;!KZ ^i"L0- @8(r;q7Ly&Qq4j|9 V)gB0iW8#8wթ8_٥ʨQQj@&A)/g>'Kt;\ ӥ$պFZUn(4T%)뫔0C&Zi8bxEB;Pӓ̹A om?W= x-[0}y)7ta>jT7@tܛ`q2ʀ&6ZLĄ?_yxg)˔zçLU*uSkSeO4?׸c. R ߁-25 S>ӣVd`rn~Y&+`;A4 A9=-tl`;~p Gp| [`L`< "A YA+Cb(R,*T2B- ꇆnQt}MA0alSx k&^>0|>_',G!"F$H:R!zFQd?r 9\A&G rQ hE]a4zBgE#H *B=0HIpp0MxJ$D1D, VĭKĻYdE"EI2EBGt4MzNr!YK ?%_&#(0J:EAiQ(()ӔWT6U@P+!~mD eԴ!hӦh/']B/ҏӿ?a0nhF!X8܌kc&5S6lIa2cKMA!E#ƒdV(kel }}Cq9 N')].uJr  wG xR^[oƜchg`>b$*~ :Eb~,m,-ݖ,Y¬*6X[ݱF=3뭷Y~dó ti zf6~`{v.Ng#{}}jc1X6fm;'_9 r:8q:˜O:ϸ8uJqnv=MmR 4 n3ܣkGݯz=[==<=GTB(/S,]6*-W:#7*e^YDY}UjAyT`#D="b{ų+ʯ:!kJ4Gmt}uC%K7YVfFY .=b?SƕƩȺy چ k5%4m7lqlioZlG+Zz͹mzy]?uuw|"űNwW&e֥ﺱ*|j5kyݭǯg^ykEklD_p߶7Dmo꿻1ml{Mś nLl<9O[$h՛BdҞ@iءG&vVǥ8nRĩ7u\ЭD-u`ֲK³8%yhYѹJº;.! zpg_XQKFAǿ=ȼ:ɹ8ʷ6˶5̵5͵6ζ7ϸ9к<Ѿ?DINU\dlvۀ܊ݖޢ)߯6DScs 2F[p(@Xr4Pm8Ww)Km endstream endobj 553 0 obj <>stream hLα 0Wf;ho(". s\1js b>ϊ6{&iA{ލvG4r@k5C}Ϋ*Į\mYH)NHllKHJK S>?̎ɶ5FR"6b rq'\a./mG endstream endobj 554 0 obj <>/Filter/FlateDecode/ID[<6ED954BC0EF0F143936766DC11EA27D0>]/Index[6 1 9 1 49 1 467 1 531 24]/Info 467 0 R/Length 98/Prev 150392/Root 469 0 R/Size 555/Type/XRef/W[1 3 0]>>stream hbbdy$ĘmVe:#SPH.ؚA  *1210”0N)aDQ b$@ L]r endstream endobj startxref 259392 %%EOF 9 0 obj <>/Font<>/ProcSet[/PDF/Text]>>/Rotate 0/StructParents 6/Tabs/S/Type/Page>> endobj 49 0 obj <>stream 2021-01-28T11:11:15-06:00 2020-04-28T08:58:29-05:00 2021-01-28T11:11:15-06:00 Acrobat PDFMaker 11 for Word uuid:a1e4fd9a-c258-4ee1-b04f-de6ae41fa0ba uuid:39643a73-d135-40a7-b0f3-21db191f7cfc 3 application/pdf Ann Goodson Adobe PDF Library 11.0 D:20200428135805 1 1.1 endstream endobj 555 0 obj <> endobj 556 0 obj <>stream HWmk82UIe.IޱP}hMKfFvl[3L,| [*ə7{ȸhVh[4]BE7/VDeVHS/*Ҫk~ЎűOSPYZ9kYAnɂ[qPc֡ [!8Q#+~g;Y2=P%N, DH2R{ȝ_KAяE Y "_ Q%{8?2ʛ)0%\1P/Z>8bW0(S P qiZ|ӵ(F\*հ ̑z{ gFV* rȯ'vTdPp>b2e zM!^$<)AG`k#hV܎ԞvCc^eǚwcз0?Dj[fb-LF i 0RUh]Ypp3@JLTBIy:t;RbQrk稖{mJ-/vCkۧ7xlx'YwIOi5=G\^ImRȇ'~Ë&li0Ab.LIPe"S lya1/PD G0>H̼"QH Px_] V4(6I6zuIne?9zFLmmGk RWQOaS6uu,]t9붛4N>&`Rl*ڮȻjv6Qod5}M8t@@ GG )R(Ee~3?n=?43w]3E9#[< E5ߗ;Z)Y`DrlJ֊4VnW3VYBT$\aD=i ]S3)E;qRnB O endstream endobj 557 0 obj [559 0 R] endobj 558 0 obj <>stream H\]k0{Źl/5:`~M0D{_[:X@!9MN²:V(|:DMk屿Ytkk8!ݪ.W]=+>]e>s ?8ٙV_xoVm͕V_yM6 ?ܱ( ͍^)eJv7o<0%1¨^8Ԋmm'77(4껶~-$*1앤^iB]J BJ$=AHBO*=tJ/AG3 tp> H-Z"@j%R H-Z"@N~ G_ZR|/./mrU 0ed endstream endobj 559 0 obj <> endobj 560 0 obj <> endobj 561 0 obj <> endobj 562 0 obj <>stream H  endstream endobj 563 0 obj <>stream HW SG=\f2Q8*"n⎨7]1N)j,Mt4nqL"Xq1IR_T_*T۷υh ;oN2sR@//J$6l%gI0rE$`Ԥﳣ@|4Ma{ۭ3rf^rJΝdvg[Nl}1)@0O~gwĚ=hYޅA'Ӊ,_oFj(F)6QکFG[ٯuY1jH?oRFC]dks]fzwNE7VFV ؄x(8 Ÿ*p; 8k؆@BzTbǪiϙWqU6▴ OAh,jv(yr-1NNE%1WA6Gm_U>s,#]PuLA/ E#*hd|[GRɣ=bZ2n'/e䁹Jnqxsi2l~ p|i-_E[Rοg%NBRc_>ƒ&%}8ng8-&\a 6,Αu'B.#.s ğ&Ce*)}R)ʥZuom :#.%R#z^|#QbM$]#d 1DF G$Id@BY$/k+e/ .IܖHщ7 s1jF*|5;TvnuzC/rBP3Iw:gsU3L)1G\\WkkNvs_t:V_q{%uFW٪碉@(W&Mj&ujΕsiLN X)W7cuQ;Scfsq(Tz^h(S"UDSzZAU*X'2\=T ,a8%kbO@ bLhrA"k{Dls϶D&ٸED}p:̳¸?Sad!T3Z|U+I qF8yYϓU`N?FɌD &k\{ _˿D9qI~ !kF<M'DMrs>܉e-Y\a&!ӮE n]h 1n㑎xw݅.Xjhމd=&_>Z cq(P>{X\acWK=ZBɼEo"hX]Nޡ_{xCUa-~R6J݆-L)}ISa%oǛMԖR[jKm-1KՄ*sضT혙PjHjܺSEӗ?`aD<+ Dj$jT L7Ә1fQEEV\gRb9ٕ(&|Tk۩rv3؇yQf_Q j8<.0ͮꪚv3yrdOHOKM0j>{ս[.#;EvҾ]6Z7EQٽ۷so{Y]$M?795ƿkrI uJ%rNѼ^a9[YΩs]=PB-ǧ9=w/3G0agDj̀ԛ9Z0W2W Pj0GReUr|HRea e؍V XG #wb*w?Zbx&6`G0@^^ ++^[7(1Mٖmoo}]v`x[!97c3Ka)u%1ؼi膽NjQq I|w&k3=go{]R=Ծ<{"d ĩ4֥W7-/1> ӈ(/YHNXE|Z՗Ҟt8s#5`O ۊAkX " ;0j lN rWk),MIVAӪ49;d }9{c}уy&q«@ p]k@UkҫFq$ 7DJ2xvB?q cx^;.%… Yήl1D*:O=HBeP/B8@;`+*xb43ZuBI FȄ 8`xӠ`#ĺRY2xy3~"C6C0\ |c|j%r,7$LWD[SAFn9eׄH$f{e(@~.0%DVWT cs/qA-ϾashpDXw  I AVA@_z x-F: 5"R5d:Quܹ_ Y:ʠ4^g8IZ'hԙZFj} ?F)xV*"e4Yd(X8H;[~GT??Ç^FDbZ4;ڔNU5[>>lO >}rZ)A^o=rIiEDƴK|̶55$ZSZ;{==mS$?OojV>$P D{<|uEqUj ujZ&d&&#RX%Zնoi Dd%Hkb5ߏx]VIҲ"߷#DےH$߭:һq+Qo\ҰVXqj'u}#SnOmR"Z[Z9no=4d?5rPxډFeGtvF,ƕl6T+h'zル.)p5jN9}I?l_lvƿ7G?Nꪏm,{?\⯻8|\Kj;%|,5uڈXcj%m+* siU mچ@Lm @YTPGJQjR^R>u{~y۞c_ ^4l98û]VekA+{*p jގw ;o/N:ON}A*RG2]8D$U&"{`ZGEʂ Ҋ1#WnX킹GiC~Vo$Lfܓ2L{Eua*C][S)X`\2>ZFXg"-*kXvҮ|Ȋ L<-xǣ<~RցRI]SӥU*zfU=y;yVJ58;E5+]=GP8(p9(NmV).- Fa{b0nf 8R N_X fHLW`:!q1\ETp~uTC\/4+Bٯ+kfE-Y`ʊ`6&hoLle] wB4qgh^'E'*AinpMLpN2Dqtrw~>=}}T" Tp_ A``0 z&jŖߌ{Z2qo<<ݙWLcg6ܱvomľw\$l@D . 4"w{QC97`9,PBSP)Dzh#=^' 3+Џ!},А Dd 9R?/&QLh^R:E9"G>-p&>%5<6#?"7 7ΰT-XDHnE=rϸ2$4rKnŪw\̤W;mW$ /쑖g۹Z UpKFYUB.GS:qc(;.^ǥлR+Iw@f'J aTUKz}C'0<^t{_ =ڶ&)>ڶoqsvFT3A٬UjDN$8lႚ)"<]~@t+KH앚 EY] YJ8~ 1{ٽ>@@?a˨΅~\SXg?cr.</ȅ>{?㝑gsw/Uj,s~tlZ)WW?[[B 1%A{;RHTEUɼf7jU.+[=Ѓ=Wݘ $|&LRPe AQ-!'iO7oFME˷aIpr× |ZT\-旋6t=[mXmko+LX8300 6"B $!`JI*^i8Uv+I'OG*$F;d"ĉ9"NFޚHg`,ڍxvEB<yc܇7p˻FH,㱷>J-k C]TsR°9نc64 Bt^>E$TlkhԲȤ~ <@-2Hm(p7ވznBS q}Bz!Ei5<V"1^#mV 2a+)E(nؓrgt<ӥ^]uB@7l (<2122C=G44{)猪[F٠slαOX1]#1׫̏:4S= ykF٣|Y9u߻?r};Q|ۉCC%*JHRuH(Ci+%BjTa[m)RKF(IF3 IUʾs{޻ ͒i\-΀[ihT Af3%Ԃ HY[2NgT6Nzô(֒F{v{˷J) U]F %C]F-*4Hh9a@-liD8-c`&AE7<-*V]9[0ͩ5ըD55'R uO[F,9$sff~T˵ÅMv+ 8jGF^ilHw -Wr* }ͯmXꝟZj3hݵ#H$8G;]'lϐYl$} }LKRy4s-ܞBc)Ӕn;e!mVr[ :O*N= ?۞CۜܬvF;Z\O@Z ]: 1!aOhAւM\MU*讃="@An&OUM{ V/Nx AK㽻D~ =KzٔR`j*5bS);R*ma'  *(|R.P[ ` IOh_l,[b"Gw+PD SEx \ġ~=]^=i ԡT[qjG!kTۺk&Gsm)eYJXeX.MY*ʧHI9i \,a T(|#/.XcvC2  /H=soH੧樶-r[}96?ٙ 4"9?Oɞ`vc{:}ݷj+BCg!ͦ.?Zn?h"{X[\wxC͋\̭2H*\_ "\?y)wrI9GAI5D@Ң|jC l9r $tlNkTtMgG^;|$R}v+3~%UTѹ齞sC} #?; $j%Sp]m\ܱ_oq:^Ľ BU &`{ G{}J &9y&qS ֵ 5ʱ+|fw5pƮ}A7v-TbFWud(lÊ&_x?T-.qyO @=2= bxm!s*dMD:[kz]ПnU_^t,⣣9L־'t")8c@Tƴi)"바 KBC{wuyYbIE|b su-(lM)ueCy ~9ؖܠ`i&uhIp1Ve`[ٶKr0D6t11P'ZOLN LM~32R;~|+Ģ=Y}_L Li {tʾA5k#m?` ð3kǴ$L90ޣJ +!`&Αܷ!{ -&F{*H i'4?sQ(&ň nT.lmwO0\9['&s,|wa|!̬Bx-\ 0P1a>1>|`,4 _IE>F"'su-HlMIuiCz"]cK.ɹ!Ch"nBGrs{POIiK~2G)q2Y薖\z!Bza] ˴rmU,n5n,z,?~֠Qƈ95KQ+JR9Vn+D-lt&6m08 l<$cpaCxЎfq .caPF[ #6D):*3'mV!'$pc&.07;UJ8 ȺUYMTj-2Bd5st,rPq\+8*[\emy8.n>ri*ڷS3_"W*RZ+K~_2ѯKZkۓU.]Zap=(ݾ8̉I7/(\26 3 ,^GK/sϾ[8 >U…̗'?sIm:0I%dQHY?%Ų$ˁf+fI4JX[lwI4MK׮a. 4E6MWy΍1dbu/CBYa@ <:<<>|+>$>bDz⾪Vl kR2C&9q9?\v|!]PadU5jԾ%'hE#d2i>Ny_2o_򯛆"=EYM HS"4E 2RDUTTAmA PA +U&J҉tISI[1qPF(&&?~V,GRNp*hR ]y;Y7PGA(p6 *}jMPl/'EZEFTYRi*bVz(Qz3J_{dLD0J Y`* R2\LV*MW ;_!cбXᢢjf2Xroh{Q3h+n`2}d"!n)&9̋D'+m1pEr4as̜4YsΜ79TLD_Lc=Е qv>f­{>$^]XSi)ˍ YJo=(~\Ugnܺ;wC[7fk!!rKh]5NquwL ]&L60StQ8p&6,vtHN%4+0Uz*v8.GPajݥPPSM\Z HKj#0;'P|X~{?nUAJ^^-a/2yC ^y'9~,iX`lW=ZGWY?~+yo"á-b J ϴs{ʽȆPF_dKޔ;ͦ:Rʞ:%ahhD88 DN'tJG,it}z:xC֙Qt(j?|םUiD}Qܘ`1 |քDKqĕ||1ޯǭ- *FUxiO3s+èBEhS=iW;UJ:)I٥3izT6 bƄ]pw-3vBaBf"vTُn u+P-UV7? v&UK]?z&ۛ)lJ"L;}5]Hr<5.M{ʱT8bSߺA/86 gTv-11Ys?Z˹h1 _OVGy$ TCd qʸhpFF82UHO^[-ҢzKó&aTp0o".ӴtLa`ykDS[-֒/%dzDC4r9atHά]WuGUq(1vbىK˒,IjY}Ng]6}w)$SӨ-jL U%cB@o-A)Z}o2M*Dd4-^[ړ}lݓL{}mmvv$[["s>h0oh753'UΟ~,_4UOlДQ*[XYD jȑh߈ kE^F\DCޜȄRBHVc$d,<f^'3TCu,.ueNTˆg՘fBsf#'mX[*+lbl,u\?0*J˥覎0ʥuV.5KP]u< XtCy|Y _S~I̱dyl D^~{?|klWc;}k׳ϱƌu7UB@$*dmTD4DTĐJ*RI޶rVQHP>"dYyۂLs]Issof's=̟ +JAj,EgL=R7f:wH Y|oӬDÊ=fa2v O#_vL(&4|;=S&Hƫ dx5!a_ DTUP%6CTŞJ*1٤X\G^ckàƣI "h `[0%KWJ'ƭΚkڢ$]u̗ ,`WCc4]ʳȵhKqt@c5OhD#n0waEDmF(EGoDމa"Oͷ(VlX_q !Ǜ VQ]E*EWէ[88Gjk@%m.;26e 3MpHq#✉ !O˿zeDr qSkHKD4 Vjr._BnN>&;}/yr' ߗ{ Go,3T>N 'n|hdvbvYd1ȍb-a*[U ֮VQҲ>ix!$YWqQTN~ =KEjv'aNQ;⯲$˰` Z(S;x2Dlà y lQGJ;Q_REqr*q[Ē\ .a.#glv\v 3tAk!01I|osy/3إ"2{'9iUdR6zEe$68] ]R 9QLTKZ c][aohkFi)ȞtN{HZ|ajLɣiଫ V ݝeۡhZ!Tyem%hqjp >kN`o Rè*DSNF|P4圜#Ku; q^1N\g=3{xx_3YzĎ`9 e6 ʥ4[%*4J$JZUX*R%ER),Qi J?ڱ~z<LϜNX@a&S^aNQϿnQؕ#Nؕ8raY%(BY|5uȓg /;\,w' n=nq2N88Pp' $3]-٤ d~cUOGftrT>ovt=MkyI5cckBCMd%|o 3?Zjcj/10NX̟6N[|y$Y )/X^+4WX۷6|9m$[ߝ1F|h3|- zprG Z2'ę_mz<,R)ldOV| ڶ0eޜQL=a6z8RU sG!jZ"L2*9s sdFoLƪKoV <<:6\.5m+tOo/?i#K h;115ԗԶ&/æ`"9Ug?!H$u,&#]aa3[v>dgmi۵!=rmrϴi/* #[ \G]_ '*B:&\W}'e|=9*/S( (P ~$kivK+Nʼn6$g& 5!ty#~;9#.FMEHSHš%*'<CSĆ~p vlj2_[ĕSyO`XgsW[Su[!m{d'L/_gs?hMuK032gWPO%J)1ssxwSWtA}'''mǢO8^Wi;yٛVVtLgSNH{˹9׊.EReT%؈fҽ1g(Ɇϩz]6,W6* lc);Or|nۜ#8z77!.cťE$(X_k>_ (Lyj>pwH:HG+,U nTu3خBGM^{ZC=?<{vҙO㭆VgKzS_s?k>OulIk@ 12i-Xp4Ok+Elc(Dc?6+QT O=_j>x{L* q!1f{^h[ Q ^=*^&.:$m` `%d?1{}طq~Mk],?=cyKP@ ߎ SqAż QŘxGvXZ͂OF7'Z?k0W\Z/_fp=*+> ~cW^iXX"(BY.hN+-}z3nCCW3,5ͲXz=Br,.]f%t=*X_ :0\9Do3{kB ڶ[#:B`NrLsu :*p 4 m|xttt蓫t[q6cA2suL@cnT)iJ$i V I%w0n'uk'{$z>]b3ޫz}@E@߆ ~h ~(&L3wIʗ^,M {ťY9,_/WڈjyAP@XK0P,(`L}LKiKq("BU@[|gmg("} דK|~kY+nb,soUs2}sd-=yH(nsm^;zr"iTޫW{'kL:r6>}#{m˟D}4>k'nqƖ2b5l9~Ͳi6X?"Ӣ{ à7B?5v4=f;L/A32R{L-/+bSER)gO5 \w,tW]w2խ3=$dLdRT>4P"g(KjsiRǗK\ 1s~3"ͣuNkvs&+OjjfRVfȐd9j<.Un bT9AHc}V6/wRnRUWL2]<®E_''8qoPc7 m ҘZ9y! BFd3Yqe^{>(imZb2eԯJ+N|Ƶ0*ڕGVܷf#`or˸29-sjbrm8LdžX]#:^rNf:vՃy8xO{4IN<O;{{ܛXc6h,{߁QJkbP<ܛ| s n%,Lt3I0>U^OqIj֫&1/us˨/b\[/%St.Ue]ji2-q6Dny٘x_ RΏ3:'ꟴVD<^/ϵNffA= Zڣ%~>7!_BAV!o VfٸM"_Yݨj';ovz`lZ+Q䜍BLmHk?d>qJ\m{; -G.EjZ∋\,Hƶ{9^! Ff2PϘh)߅|u\|%0~GA۳Y}G:y^P|eΈd7DOdyO5NFoN݇P?Csq"dgB.]9њ&ɟ5lnk2?'5w599xUdGq#ӷ&JB9>ySOU(k\Cj\bBy"iocib1bJ<bg]8ߖ8=Z+fK;ݔs{Z=.C{|@ ډݻo o?!~[XfgUc|F!*#^ᔉF߱c`!M8x-Ysn5?!{fUZȃ`ntdoDM3'.xr.e|'{˥P ~~ϾV}SO[{qP*=Gn!/'ebʗѷ/h_.j/U ̿80yŜ1H(G'ٸ9գ7uW,0Fuipe y#$ BRjz@e25;W6jLSa,PE9~ԘgKXz\Ό]6N\ޤ; U317X>}{jd5Vk~'=.ZsB67UrfqzVjK֞|ٝ.Xe@?<.-}EuCQrʤY CB8쐾@ vz͵||{~OS/GqkBԽ{:Kf[ص-{ 8fv:#[ vk#9g}ؾ.aZ&&o >(~7yRC͟"H*T)rr/Y9GY~ [`#R!l!ViN ~+pX̿s^SX{2CǛfv1wh^_ƻia8ԫ,ġ^5q=ו]C GWǡMC>ġzz.=܇*3%~KϢ?33Û"fY[`Ly ;W~vlXbXgV7!aEgC@rAC㶞Ÿp 탐7oí|_ށ+( % ˓IISU[+ĺ^j>4}R{? GzyD|ޡxQA &7RX|j/gzK5_dbSur (1: X4k*yUA)v;fcظ5g0f[uTBSN8#x Yo4)'ST ?g0 B2˻X~JOFzRkC( <% X\+N~.>ɖE_}mQTF~w}Ft3"$u*knlF{;iIHO` YScP Z^gM}E=OeO_# lYe,]w'KNg߳uil]'c-TT+'lNS=Yb<}Qrs`}PHO 9CJN1y!|Z*PQ|{wC<ǖa:$`@S5=^x]yy勚ƞ;yjm_q zë,z+-}93~xA %t9r!HxhW_hng (:1j#(+y[ro,zo~sG^Iv|THNy[m4K=} [>6`ۃ;t.^2`_|('d{3^K,@/DUf~'*.l\|n[}( h"b밹^'/HیE?چ7o"wu碄y+2#FS@;m|@:ox}2hcwe5aoWDxMl ܣ0 K݆ſtzacz˶D,IWcGr.';SGe7OdO{EC Q,c6sviU˅{Ýn%Xꭑ)޻}&)'Wߖ)~ VK$~cwS[K`}JS66fG3ٖl{kܠ8(tdNgYhlbByjwa/%:S|*\Y\+s_4L\j7Gmx!T+ J}E{@u]ZK} $2y{n=-R8飴rVz9y-فs*3F%3{y>%jn>h5IsTju29 g؏Tu%~ok_~D[*1~ h|ڏh$Vk\g}#97~EmJI]a(Mb}037rQ(_L>\o}U?bRs/̴~hVY |#wEIjv/Ϫk&dMf&gu=iDw+Y9F'ɹqŞ xo4!Xo\O8sd6_"X=mPt!2Xml6Ou |/L~/7J#Nh|cf=g}2DZ.[}C^;υg! =*茎|CtBʂ5L'CRߒ uQXH0NsZ|.w5KW9"K?JJj2|o'jgx.szf?un=aF-k$Cg`+~ `Jzv*}of]*cV޷~N˷/~)mnlD?me+G۾/0-J^0SJ\ؔ_Ƿ>ُoѺm|ۤD-Ɍ'OD c邎#Iq׶;u<)f-.ڄ{yx;;W3֘h#9^0F2]$]guOb>d` tOL 辻yy1pIq`RFuJ̸սF5<4ϓ)kƽy5 z{>..:ьOAv^>^e앩smD/Iݜ3]ːO+DH'/71X%2h9 0 ` pԟF{jNxO ˗V5_"CPepeo}ρ 6HSapgcU{`Z# )S/أ[2eg_h$=&32lG/+ކ-|K-1-a|krgOZ7w5E!w*ˤoĽD4^Lbq)3o +ca^/8(ڴEVGjM!rYK?F{Wo97[nk ;L{HM LVj\8;WՅO?&W#N⤶J-4Eߨ7em>}{yOv&אXD; @/918hyk5gX?}sM4{Ygmhp^X"rl/} /{}չ}"M_i^3"dm},axv:ywޟ/eURr^9}{!tk鯶9XzQ_0JLD6cgQjf-c}}"㫨b<8bN琝x|邵}_ߠ'W"6O5k,xP˶0-:Ad!N-Jf>|ߢ z9o[_W4 |fԂ`3A{.vVs~BN"W+^_WK'*`ROMHBCX۸1Ⱦ=V~|O?D>ILG2_bIe2qr&~ 6sd"yRKyN`Y͌7苗;y"$W!yA}sݻ-ݶ!c!F0O=:e`zcڧ d7z:/-'77D&q3dg<63q/p3~E|0I_$PSc,w VW_h4Fh4Fh4Fh4Fh4Fh4Fh4Fh4Fh4'(ůT<Xf$0_%5×l|˓MdzEyؓ{r++3r<هB'od<٢OS\O CpPBa:4#ʲh'=؏Nղ.f{Ϟ A-ۚ8ݪe=fQeFύ?g=^q:[@r'&L%OԖ!O!L a>Gt4`7vkl^{-=Z?v=*Yg%>1=ޛ:)mP+F˹]"rk봨yʶ+E)_[Q|ڻ"3i:Jmj ޻rldjr&"j 6~4-sꚣNMG{GΨ+ўT:-M=Nm;ڵ'w]ѽh{Yv448 d# KR :ܳ+rٕ"A]UEtSm]vD6XQpEtVZh#$MhFt{W2i;t۱JZt4$i>Ù~Lg <9sRPzwB{nD;ux=1yvDToOgoO8ԫ+EQ57@v  D}{" G]E6HDuF"]jo*vEbၞ~AQ+2鍭j475T4Eb["EB{67?j\TDTkH_h`?|?d _#ZjV$v"eQU9e/3D!D$fJ.w]e?Gg]./rbŸ\l"^rR EN≱x_f9@d8Lb+@G$:x3 GN 9.RdbK7Ko ^ }}c)(ϟL_E?JZ_Fz}I}b`ZYL/V,bU`5:qqE+*šNh_Jq\ú&>cK8a0Nn'7L H {9Iee"vN& Qݶ#vK;Lгўfg Q3fD g?ͼ;xXl9,xzdddKb 4Lf|~໹|)ss7cOݎ3T+h9ujDuW:<뮍Ӑc -YLD qZ?ud:NC]{7H_'b㾫;:Q2$QeqcۺrD]+"MJ% WҐ 3B7uVૼBmQStd)Xt! ͡fx5鶃%eqgjO+ZO*ǥ+IjvYqiʌ#iB#) J+ыʔ[9gٷlsU*lەWA ҇ t<4&Iۅb, ۀuIZT֔&RVcɋJT\nR+ky>8h46n0>n4"2c)d1嘲MY&)d0I&bKϺ+y.ѶHA"I·G H&/r$ժ+I6m]E@35~-N鷂i74B-mPZ5z`0 tڝ&$Z[@>hSPG+T>stream hL 0WDQD^ibʚ|{StNs>f0 i1o8 VDA!/ta]6>/Filter/FlateDecode/ID[<6ED954BC0EF0F143936766DC11EA27D0>]/Index[9 1 49 1 467 1 555 11]/Info 467 0 R/Length 62/Prev 259392/Root 469 0 R/Size 566/Type/XRef/W[1 3 0]>>stream hbbdvQ/'@.C`kAӀ*"@b @ĕ0 endstream endobj startxref 315106 %%EOF 9 0 obj <>/Font<>/ProcSet[/PDF/Text]>>/Rotate 0/StructParents 6/Tabs/S/Type/Page>> endobj 49 0 obj <>stream 2021-01-28T11:11:52-06:00 2020-04-28T08:58:29-05:00 2021-01-28T11:11:52-06:00 Acrobat PDFMaker 11 for Word uuid:a1e4fd9a-c258-4ee1-b04f-de6ae41fa0ba uuid:7a053d35-315a-4e4a-a31d-63999e398647 3 application/pdf Ann Goodson Adobe PDF Library 11.0 D:20200428135805 1 1.1 endstream endobj 555 0 obj <> endobj 559 0 obj <> endobj 561 0 obj <> endobj 566 0 obj <> endobj 567 0 obj <>stream HWmk82]Kd.IޱP}hMKfF-;N NlIgf >M>^]\/'lp6Yn^ ΦӔ 6}IsMbsH6}Q$v([3o'qR-j椟̙ye!4KTcMr4SJ/+—o$sbŷ_ l#aI'2|p6BwQO;qѲy)rZd9xk-d^Y qEe֏(. 5/!DmEm7Պ[0.&7F7WD4kwSY?!n<\sf [@%+u/6vp+8 '&7l^!W46}+ʽܪe32 ѷ3kM?|`Hd t+5,vK99o}~kݓ qbJ!Dƫn |;b'ADKXcN6B#"Qf:A'P jϐ>D`MʢNpY3ȮgEfoy3/k>ٲ5pu["bRE%?[1N3>C(A=!P:$g SZ&hPifpXpM0'n{ W?pP$Z#'9VeZqےy](Ώ]J H Ryr8%)N9ae6@CţAœ2t41Y_+V^H@d<('* D(]:[&2B9 "5!ŗbIi*Qq!~p9*_HGFq$IhYTpLLeE6.uI6yBȝDnTO׵Cp&M~T:밉h/=0u)2CtV1"a^?gpn>'gJOU`8}d}j;~8E›)5 G@B>> ILV $(z:T-lavQ x*e:_]CjaVU Kyo#B~˝,=TK8cWff&Zi?3LLof,gj38sř-B-k8$s*|FLcj=q."]/n endstream endobj 568 0 obj [570 0 R] endobj 569 0 obj <>stream H\Kk0 8> t`~@b+aqXldlWhA8O7+:fqR wW8}qZ+ >8;,YV6_U܌$ּ#eZre|.! 1b8Vm*U2_> endobj 571 0 obj <> endobj 572 0 obj <> endobj 573 0 obj <>stream H? *x endstream endobj 574 0 obj <>stream HWwNWݿs`S$CH$!W*ED<%^PVIYhch5mX~VzOkh.|gvZ/Z9{{>;@=C#Vd'*>7-9;eQxN+@J H"pp; JulCB_z\XLm@q];} t7~>U؁%8*!y {^Fܖ#  y͌`eGKLDک=(<J=[[W*mA x*U{k63bm_P"t z-cnAL;h\-$l <4W-obr#W/|/勶HoPwKTa]f=l#mr.1 70x"|=Vzܙi"_,`,y\gS &nje股lY=/=.,>GY_8.2XI'Wr*O=*/9(s0k8m8c-mGt׉괺UsgnfY?M|(C|ȡd U֯W_#t^_)gS\3Mp'{yx+BgO*i*1P̗5، sE|9޳uKe%^a9*\ՊRT/4(Vg%uO7ӓBOq$L)wUqI^'"Eҋxxx{ڑv.ړ2\,#>b&ŗ8/>%X ^Od(H1DLtbb)%82PTy Xifs Ta+G5HS9Dfqn_] unu^w#QNtwF:iNs9\qI7戫+ʕtsts=wCܓ+jǽ\eiQ\uY&KYz>gRkR3TUUOuTK+DT uS=QwoKZ>U9:Vj:1oDRѦTM: ?jjej-_:2TF;Q2ƻZ.S_SI U pZTPq_Hs yȕ#[m\_q>VܠNrAER Giez!3B8y%4bHMC59/h'C5m"a8UhhH`.GZ6_PQ?I&¤җrQSq~ҭmb3B_֍GYi|EwUb'p9gq _ӛ]G5 ;1Μ1=/7gZv̌ԔIG$ ճGn]t1CdDxXv!my3;1a1bKk(K*b5;eWBH6 ,RK*b WO1 ŵHU6_uSUrI*C/E^ RT{_=iOtJ'S&[Mj"*pH0+ӊKXBҥ,i#UcqGZH+@Z $H}L {1AI/@5Av}U_ L H9>AvIU{ewx>@&b;Ʊ rޗP &:D|L6U6=c'L1\Z TðH)brmo{\вktzfrc0,8O/ju+nza>qU`©9cI0 k VF\",F/u 14 imu7o +ԩNw7Igko O ֐9#/X,,O- .0 ^,] :] UM/˴ bf!GQ|+sO8Cp[?P@ Enf-vjă$L Ba_d*q( q!W<~ͯT,}ls ɏahai:ąb36*aauiti8zs``#)pb]Q^ތollМ͐ychT {Yww5iu\_!k 28 Kd2oV</鑜E] L4R!b:pI9( {_b«!D/&:FAyZX/tl9P8FHF /ҷ(-եo(pF߁^B2>bW(ej? h W sBu/On#@(5`LKŀC wXa/DB;p*]KOJ3h55I EUZhӕ~#HH ʥRڰɤ0! +Eb`?N V VWo[@_Z8HUCpJHSҌSJ/vߛR27`b`%˽4_`i~|qn4uN" Iɹptj?ƾI3 dh39JbDʤs%oO(Ldbrd|)?7c?O4Ϸ].~T.?:RM,ȩ|MV% W!:}(u'%>MIvkx`$Xl-+eG nub;Yu 4Ԥ7: >2WT3q\*A"luuJȝS9L+5EJ VJ㥏S&$ƽ{׻gĉz%mw{ooilqiqQ*FK.߮ső4 }oTĢ)2՚F4c\F N$Z&)0 ٳg?3|o q-Mu6usuÏ{{m|N\|KRJY&KV:"J&CC.q dmhP몁'E\3:wι}wy.E 1$aˢ)0z͖mYM;N$"Ok SUI=AL}6W9Ge2a_ؐ7?g8ZaMt;NbXFu[ӣzTEU;ӭ 0 &עl#y-FuĴCpY=NѮ䦞M7[>{}{65w7Ǿtd:r=os։qݤG%$Fv9UV hS&J20JX$□I@C6&Y%d4QI?{W\>KǒPxP,kWJ FFeBK&K0Eg$FȆ%`zkaOX~e)[h2 [#s8r($*ɘ!$dhVVaŋŢiV 7|qo7&+t/ dU# UYR\Q̐x/,ePń,Ŀ̲6IAU˓bUVeRbm**O f<nASNU"-1*~˿|ֿϼqtf^x<7O?C)4IΡWN=*hm م_Ǣ`Nhc?  leB 7 +Ղ*] :q%Zkf+EH_`DM\363$r1E\͌bgI փL~u5 mOw oYpG<3.rIƋ x˸N8N4JҜ\ ^RA)y> )@%1k!0s@UnU,x3v9%ӉOOLL[|]F4@$5/`”" k=O34L_w9_o rR.aK"1Mij<uECO6`P;ol7ʓhg 9dpQ,]J$ IK]&*&2ҥwڶ}sck wgZxSԾُӑ䏮l з;@>=\𛱅 bˋ#jaڙ'^ [CbS9\Q\mG={s O{)uiJappVĴ 2y?RLd|}.It ns4]4r=O$߶vA*>GvA&+Ǐ:?Axf 0BbO%tK(@6 =;<{^du4ڠ8m?QȬ="051sg~k}l{?UҡڇG6_C(ݘ._?N6pGa~gϰAj;Q|oSy檝[C#;Ro7zW_񋯽}7WbiإY6srz1wH{҃GiAR-[DŽ< LNDԎPP{1ѧds$S,NP %)aw0 H }M%lq0p<g˂-p6x67Ş Iw5oK@kXqC=ZIŃ} P6Ry?'a7.Yz)#df!Fp|6"$ž٨ۍh.>XʆsLp4Kp~ڶ%m{" ƒ&Zr94m'bVvҒ\qI<&_~ڸk@\ g!uCV-B4@ d.\P~_ҳkDXxwY s-k@EkC-/v6CH+c|..cH0N3/vNǎF;%wo+:W_,K!UyΦ'롳K?9DklK p].b|w9ĩR$\8P T&!@ Z*_w; 2/RT.5lmFw%7-b󂳽B湻0~ pEp 1 ]\3(u[հ1vf8Y^##OQeB=8z$ Bdn6F!}wk'k'unWՇUVTE|h(KXL7ݍ@0 pF\p((aepz`*qa/6Û.̯8K mT\pd,%K5iK*=ҵ赟xk?@Āxа`j&thJp0E@ƻ͞iEo'A4DP/6yV?-xk' dJ6> ig+Q+OQXB}r` gKo^)cv{~X-[;,1cq5e֨( .͔h\ɂ#J6ԐR?zҔr[LY5z\n:0{1hpsz $h8T7Q k"(>ר!q oԐh8plj?1XGRCS'b1*;Yݬ^Kp+*rp*hiCH:=.? k.@ybu23"AQ-x54r^VZ2c£,h 07P0Yn[.4,\=)p4b2`5[n=n gB8VP2**b[ r",횞DD8ሔوA#wZ"5vDxXI{B#v,-p f3әVGTjlwYpAMͧuVӷu9_e˽reb*dqӗμ75mSM*OQcָ槾^ ݬS3po\uK4 컵A#w¡HUU< 3׻ΌtjxB/a0d3tm}Q$AlE{ԄN,b'``A'0^mg瑔HQH%RIY)JlIQ6Lw7KvMT-[֠h̀! w ZCy[M>t>YX1n;ىaC w;)ߣJ/W48M6J-.kC_Kekt# Jv?@ 筆~-!n/`KvK4q>2pv?xuvf?4TX!}͓*T2xHU뫨4Y&i2&_&b +\6`ç|F}{}>VzY~ >MS[uzF`jIkHv]ͻFxK6zWš~.Ee5lVB\ M vo,;vt#˶ we(Wvi#ElYo%1~61>}9!{CUNh͋@4-;z2* 6?bxCo:4j`e§z`ntC{)r>g_8xKСN-jbs^t4$;̏ 2%*MTTEh  YgqI KsT'/AqY<$VFF%(]5D4IrGn{V }O}C%M? hS`#ut&t;ə^Κ I*v]=m旟FaLJp`Ey;;Žg=IDy\ Mmte|d7aJ\JpWqSJgKE=^rGBK_d\Υz'"E-*t %8ud %8łq|p"[| aX^ }T8>V&pj> ?JJ4p_6N ˔|ex(ߌthA( A2jB#b>&߁(lb:,2,\|'6;bJ+s ݚ euPۃ%gG07[%Ϧ:;o@SrjDS5{X#(5;"w2R{$UU4C\Bi @Fш>ӺfMp>hie`+ν3'Ca֯f$g }tuє!;}@{S?<+JjU*p8NJ5W0&><4` (e/(働DtXsA8!/#ZjmdyHongﲿoww{2Pj=^:gVKg! IDJ(]뾞SURjZs\T|iKi[mw{bK)+5CD}FmTfm{BfnH-xsxRQdP kWP~ MK ^T/~KAHC&f^pLB#Ҙ4!1R4G&ҤF10ΏEUuyh9Ώ' j7ut7`CKH-ء`4 p߿?GNt;"RUTq$T5-9/~OA)R"0qF>ʳa(l aPD$3Vh"6O$'R绿!y@CA~elr}k[Z#4IHBCyVe*#eC[g: ְۋMw5F据F)K%)p n4J$X&59 >'kHDAabi^˺+&|mЙU2`dA:xHՖpI֬AGOdz= ꍙfbP&;^x±[kYK =yqw7m<ʣs_Q]e;{wkMn^RB؄< 0@BhX@x !GG txt:vZ 8RN`*"I(p.Z@k} TAy(VK*?n;څ߾72LY|~X /IG pXKIUϠԀ% KJVwJ-ΩJ-ABR% !Hȑ';K/ώD}~:egWLHq#QY["RAXSB-Wc픠03POCWÊ=LeʬPzre%5mk+W2:es鞦&ϵ,YY2##w([+4MltƉXE)BM呥!-DB1%Y,׊Z',Z4}3?[k[ED<^zrR]DYL@:":=ڈ_#~9(ۄp`0]Vx܍Dr|偼4bd9+**Ljiy&[e̪'W]8|%ڗs3/*{&kl7o/Xvno{w?\}4G7V_oMO3#bk6:+'( 0ۨΌThDi (τܢEH P4 `s8-4h@=< (z^ Qh+t1PկV:WZ(,`]l3Tyվ }-d: rCx0f@O>{=Ÿ<=q)(kz sS"()zQE~nnf~30KD>Um@K2Yj}[:ia_g:E}]!lb @RoS213~ }0oO2g?8 {̏hJLq>G}fn1g8/w2dQV>J}F}Cq^;RP>HO#,;>wG:{/wPmӌw>}͑Gٮ|]:m %$pu`(2_來JviC.ay]{2GOXKFbۿ[@6!2l[g9G,ʀ!D>`P.fM>*^C54Fx=.٘\oYwQ,gN>/pXK릧q;WC/&i,*?i_y=ʗ~Va/2[eUGɋ}zd ځڻEz\c''.W'zC/9'Vm{oо>Cn; %>F>K5jf$S%֫هE~ZƸD{I?M %c͝|㝂}k;bwK )э@yʖ9;>=OO.'~jJÃ~>o~&@CAΌLdN'9d>IQYs?p x pZEf M?ϻ苁bQ fm ]h9Vrr#x%5-جui'K7r\w%Q9N{/3CdHsWn>ٮ#2s4#hrV)< 3 !|CVAS&z K?i5A+)ww-Kz}CR ZݓM>'\wA 1 ث=U (>޽WFVg+3565WȹxO*ܤs!/~T&>S>Ecwx\Ujx医\9G w ㈵SL^>Ɵjȥns 0N1r?q<*%Ғc\)NDTisTeF'JW&jNSl.kok~ÓK3Ǎ#^''1?3syw&4\;i WMr=yΩۛ)&k֜qܗh,&w ֬"8{r%֐#]\,3aI9?|ge[uVG()Jq*02=Pi|9ľQrFF3H ֲ!1MaŹ>Ν?%lņό:w`a2,t=[ߤR|#5p ^cK -e7Nlw|-R::.#S&,S0X rkr4ɟ` <D }]^9_I6%>M >{o^{.߈sxf?"}kQ]>X{!1#q諶"jK!qW;=ӻv]8ԏ< ~t8_]Gw<"#z>q?OK?M; nKvIE`ov_mX ц;o經}q=v}φc옦oXy<gV7c~i;؛hmm'p3gljc27RsK_h6rAER?es*qޓ",=bINdUoDsc0f(NhbF(DZy+k")T'͊ƈXgިI2k"[HJŬճ2Kuג3д>#ǚ4G{wwI.k&*6k\YWb2Ǟٔض?g/bYFڱCwL8܅mٸr#3p'{A ѩ`h;c^X;ۭ~ 5=gm-cM_wmw,w:[l'c[ U YSa :ܟVdbX rɅ=NNx-PHMwcz'c*Y'v[KB sƬ}DߓRuL;z FƁa=h^Ə{xr'3L Y;Ho݃~f<2=) [v4nW컌>AZ.2DkY4Hk&^ @zu%}Y+a,8IcK3^n[4|% ԘKHJ ~̽IlYk_X=]&N9?uVo2ַ_FyF:rp~Ɲ\$ǻRmrsi;><)87mg;M΢}Z aqR[f%ϩEib}{:u"g1}q߭\q2,q义/x=|~LuA'+)+++^ Cސ|=!_b5Aʶ[ ^r L[%e/hsI{rfN{}yF9Lvִ_xH -S;j(#Q)r`3 |Y䮔jS}wyȫ{;Y+D2~OXjgK|)~J,6G֙gn[hN=gI,7کGzWU]a|s>r !$  JyZA-iZQvM8PA:@G Ufl*ڗVjNksˍmw|[O&Z+ &qN[xgN}sAB3s}~1Vɜ4n2 nbQQ ئHkS|m1zU6wY{[d«fbzrMsWm(b%RtvmT8wWkKzk_wJB9j5q~&jU6דRJ+K~Rk)<כ\K9=m+[\dI' Ԏtt^*pv's?E:K7 Ep9eW+ct{a Lo|!N{>;{o@m\{f yyw"\;`NKz@{󴟱sq6}{C֧Os{ϲQDk}Cpkg/>weLdyw)u N/7?b<{a_UJ"̖ xP(& q]?❼';~wl4m A(K( c`HmRףrPpY[kt2FJdC<y=XHgʍ&_*EmҀƪMGWvuD/[@;e$x\ ¼!Ĥ +|>^^ higWZ} M,b4rb o 2Sd qo9)Y cfbJt]95Tr_&Zfj"G&gmL^#4.k6WWOg\$>B*xgʗ^'_*@]y3Ki}S²4,Sm=A~DWFK,y/q)2L7[GMEKM_eI ' ivIV/KA|-{# $Ho-Voջ]ۇ_@G;#Ϻ䦧",T{8ٙI"7뽤9eZ$\ŀQSWn2 Ts+Vi Hکos,isIdNt1zin_f/|T̻DjO&bll]4rO=r:XsqvCx2b>kpKndSAi[Ek$ux$@qG-<$T?$2sKl$G4F #髊N/FttvhkE ]*܇E{i4Gd6gdvdazkmzM?+c+/"$@)xL>{iɉ-$%}ٱŒoۤnH*a~_)ݨھ|Y~, Ns\MdeRo~avK'rmC8va@J| $m5qx4d!KbWmHm{DAA1mvP[iY bZ[?*mbԡ+J:9"4uCn={D d>s$Sr¼d\z uxkBa)?fƨY{Xx~+1೘4oS&Ϝ xߧ laB1%9^_9~5_ dP)yOl)E~n}s3}Sp>; ?A8hSljq<:AE.gWWqww:b߅߹7p#>I W1,ǧs?c73rx'R(Gi':OCΆ|mۑo˽ 8͌c^c; WnqD;?7q,̽ L+Z7@\Q8r/Σk]]PҎ8tqo=h!P51| UϓgΈ7*G<7IFUxoz3wc4O@Ҡf@E߳' eeCdMzl8'i8C!uؼ7_]Tbo}#=R tQ5:B7|^wO\+iS;1^^&\s/M%#uXsFh4Fh4Fh4Fh4Fh4Fh4Fh4Fh4FhM؈쓴~IEd'5!"u@~R/RE5e?lo[}ҝ_"Z'f`*CZ[ʜ1K[YKwBhXO,:N|TOZ Fq4l@- aQ?E/>OSJLH{Q -j,j-ӊcA#1nc)==!ŀorK_oʶx,ޟ0x2OxV#Od2{PtZ̞9$f2 e0؟>_#hX);Dl Ă[12JL]ᔌ9No<)=p0֌cR&&Doz(4`,d&eKf,e)Ӕf ̐2dp7@8}d涚pLm3:@c{[+E*9 @Ȍ2{xR#_Gp3i'm5wbROiɍ/.9ZH^GqyW8zOY:E0 s;+d7m5U2n,xSq,-#/ SRR>Ѽ]7/-`-.`m&oSuvR3h8q> xNH Hr-ٲ|O12gQ^-`P\F88 r:xQց8"{.ŏZ_G+J\=ث :ˣVes!nɯ^ ,jA/m Ty=*KP#Ў tG8KDiED<2Hd?07 #"7B >yUb>0"R B~ YpAGyLpM !XŞr/Y_vmeыP7ZI)D2KTfd[삅y `9(̒:KbE;jƨèh+?#|QL8b/m OL=I8 v)vnĥ;$<ːs V7[:nZ->[2Z]^Fy55j.\`+e Po)B[۴rj۩jDpi}״Eyy'[ϴjt[G[Elާ_kpObe() ȋ480'QzqՁF=wjUy>s9l,;M{Ps%ǚ@(eHqb2کSWZ55ih@cv%l]hNۏoZX[6VڲB3igs>~_1<øH\9\ v ϱ tfP8[~7~G}1D [ :_ z?HeMt0{Me/ S _MF@f)FT}dx=d0ȼt60p_Yn#=Njp$ _\2u:;Q^:XIn%:O|༁Uqd.wٿk8qO .@/t0Xf\[:5?ouz$`73u72u"9ytQ(ةu^vZf:mA1ֿɞ:\]f;uJXžue ;IVLd.299tE-vMVEeIɂL$ۥ8B r <\[w/|X|{uJat6P\#v~ۧ_<7ĝJGa&-O/eٶ<JM4 Gbk-;ygk{:pB)8k,W\/ ' O_v2 #V2U.;e^SӴ=:"XGqF4\#H 4ϕ F#YZ dV“mAQ0@1PR7@P3yxaX7u(}lh`145 yL1]}\ %0 V T2Y;vI_^*?<9֧$kDŽԝ)ke{ &ɂ>xK#<50dlk- ^Z.r֒k)d6xUPZ`[:R^~s|v/#{RW:~)y_PW+!ؤX3xeO*}{?a ΃oR T&\5sySxѷ|%ǣx?@X8O}ƘY pb(glgv1ԭwݝ݋!3uP@ю߰nYŒ5o+pni„6kgmE0pxŶkbV"V͹*NKѓQ1MF{vt$ųn1+b^> {rQJؗnJKwpRliD*IItQE2TĸTz[Q,B ̭רZaJDaQax>stream H\]k0{Źl/5:`~M0D{_[:X@!9MN²:V(|:DMk屿Ytkk8!ݪ.W]=+>]e>s ?8ٙV_xoVm͕V_yM6 ?ܱ( ͍^)eJv7o<0%1¨^8Ԋmm'77(4껶~-$*1앤^iB]J BJ$=AHBO*=tJ/AG3 tp> H-Z"@j%R H-Z"@N~ G_ZR|/./mrU 0ed endstream endobj 576 0 obj <>stream H  endstream endobj 577 0 obj <>stream HWwW~{Yf:ElB-T*B[$mTR4 DRj6jw i3=g9'97}{Qa>yA OSH3i鹹IM 6sf,h+oQFjr9 ;eEMRvi3DŽf GMJ6m ;%7yfIOlOOIj%xZww`Pt ˠQʥcvպ>02OtpˁhK̀e|emK7Xolzu݈ %Q+^`/Na^,@-_(G ` n:kҐ|xǿXbW=`;K C(T+L2[qKZ؏ǧhV(J4Dg5;9rb'Yj qA4庻QG*mA*4Kx'*U{c6b-_+Hume{۵|[GRɣ-a<&Z"ng/el%sf/z؆}.GZ>hDNK$I&˾be" 89Kp9͝i&[\a 4ΐ5g<6 5WڑvE.u.t?SU=O9GlЮm[7'0~{\e* *BJe\+ʥU?U16_Fs̥Wq[pGKZJ;"׈ꔺ3sOSnzY_ |H+KɡdTQ:Pwԯ:I/%3\5LNLvm!pW+ ?il'`R,Fl?Q\;'LΞK[(ˉM9*7"XuRTUj!QNn'B]D=q LpUqu?yIϯyiyS9cGYvhOr-}_8.Hz|c 7pzI_G !D"1RF2Q2B)y2_ C|* R-uwKL583U5EcoK"-Ru^Ϛ4y,ՙ:nֱΓs о&JalU7cu;+Scfsr曻(T#zo(S&զLs]5B5ObNLUQNyL3H})-~RʨqB;-8U2>A /!Zȷ"[t Pn}~/y r^CR GidzAi3(qEK}dՊ19@cf9XeSL?ۤC0aųqE~5c$q ^Bg!B*poj W!0D0);i*|[[Bv٠/|ԖR[jKm-x>[b2mMцI̭+K7T1{b&Pg%ÈDXIC%Qc%S;RdP7d2̯_M7zh.YsyTHfXMc>6Smb/3B^Gi|Ew UbqYcq_S]C5u#N: ?ornNvVfFzZıE٣{]:w!"<,}mZj"0ߌ]lGyfmξ=fv4q|8V®R'1CD+EaSPB<1gDj<@Q$ިPԇ"bT1ڎ 7;3|j5ўJ4'ojlE#P0 KuKYeqB7Ǎ *.@*abWM%ILEGIe~E `]Zl_ J7; j QXuwZXX0*$iI1Zd(s";] yL1*7,T`a^Awik{Nмk6.6+0(Kqs7\UW)G=oNڇ:l0)Wr8~B8>Mt۱ )__%)5ʢycٻƤ=iks(]ތmlD5h֊77:,qU# ?46x:+32 ]0ۀ5Y-1x Zl*\ p}z#cQJeʑy$afy 0auwݬKX%V^[< :i8E:zMI.lsf99Pw Cm,?h IN6'lZܽ؎|}r1Rojh\(^u/@YKU!I)9b7r[\Hb=LKމTkޚ؀y<^'Äyi|fbGx%8%FG/di'yM H˩&+.2jkהȲvTkqOY#kd)Me}ھs|sqI.$K?׌i#fbPŮ(l̥6T1&i*1Y+e+RA+a*eFAI]{Ia|={~m{3xM=gG#1jvS- o_ѧspLOJr\\}@4jHLBe >K :'IvU@ɶM]Zt'QiI^*ZIH]Hge,ۭm!KZt2ȨekWt=O ~*,y]_TeVobo$ۇ}J,g\p> z&\, bwq[ͻ쩈U+y;.lOܾypH8<:!xXv"KItw.V]pb`dMiI) H+vvDdČ\Ya yB~J Y0qOP0rՕuY^FvᮂnQL`?rjAbVf4V4aM"FejH>fW!+2^ު3~/~`IYK%}wNMVuU恻.Z1[+拺$6n\젎1ʏ`v FC888Yi`*ø9>Ěa4H)8|a)!2a_AXzGGDZcp QK(?|`6qQ s0S@Ҭdg ufsݚId )+wI0]m$wA DayYfbߚŞI=7#09;upSx3PA}=Jnj6)&`z>/[6O|3j Hh7PjHb}f` ^-cNIT[BxY1 H0y(FwmcOqm䢾NzuQ 5JLKȀ#H  A il3oeBF^ϱ9>cw\2?xƊN) R7Peek?TWlξ;cG9Nl'8jHVT*e"J!Md Rim]?HSJ-ŠmL&N6&U)~aN{ޏ~?7t84KfCsf[Tp5ڷ8nQ4ta͔PS &h"dmAb C(zʈ:Q٠:2~ӢXK.ߒ?RC\P>+dTvW2HwM4n"qb  㴌킛mDU0?\ΐ5y51-}y??Qx>5:*'a՗}q::87Z|gG~~'jX[\z.@vI[W%aDE%aj 0tYDl>M / q4o`6MWPtfp< T*BCРLSrl<ڌNG%>,G@M OM&akQt{;}bv ၻ#$WCһR|VX)uח#d}5{C~A\5_H A$኱<ܒq^ːg}q OY [Yyd^۟5uǎk]?9cIowxO9`B%a/P#a la6ڬ]OfbYfK}lfլŨ%Hެ7$g2 +> ]U veTm=)([YeH'2v'8X2\oW.4KG>gݓ۔Ρ{Y!a$l*hI:ZRG@ũ$rTuo݉-: 0HP,yV!<8Uwri-/Jr1xW ߠe1P&j00r ˸ی(6ڡ~D1x@q_Jx e&vc1mM Os0k9)|n3~AJ\ 09yh_R)so 0tSI2isJ%.DY:p:##ivvl.W6?)ư8&.K"'zo\̽Ptut ,[)wVl 7VԘ4#~jHe.p=*n dh̙ Q..27 >ڮ3ym]#5\MA+5y`zSlw~~hat^-3vk$"X]NndvڞuP¦/z?Cfmj^T?W53W<;Kś;Eߚ.eB##*.7%XgY-m&4Ԫ?84n{nsrjUzra(>i%t밯s6|&|>̆W^?]Z :4r~7uW ^#dYpX.xgTedt 4e HJB+"%2p3R0T b1- Z2n@*(Z7"ν"ںtmE[cڠ7dg7D\l2?'{!]_o맏mAwF; ɇ6kdiwl`,+{pUFVgƞcOc'qfq\gIӤOI6:?inh D hO6!t+QQVѾeR U+eZ)眱Ӭ`9sc_;;%Hx˫X\/Q)ġ`4@@H'PCxj%/T=촇=lgL ӹ+3c{ g_܏N{Q͟]ɪr.qEI7җ8W|+o=[cɬ~\W d eTނ! "iQY*1J;RAZއ~-Oe$GUZFƋ -|i1G$0AK"8p3@ǹ;<jG`oI31lϮ@sH<]S9!H@$A hr)Rȥ(iȭ[<[g.)MMAiU<.ߕ@nk` tZ^pm kS=J:&7~Kgұ|\L -o8ۭD>5TLMsJP~#lSd~&!It NӇ2 L-㺖gu1s's7ʬ(Q/?ﰖ)yb؇SB E{>0zQ<@j p2 EpaEWo#p !AA&[$ՀI)L9x)ҩZw]Z{G;Q7ymsbvDHE;YW,~TGSSlGsz zP,,:D4qЖLEvqqK~Pz>"OS<Ђ U &`#-)*?|"罛M~/+|Z[׮4xƓ*Ǯdq>U[XyADxS1]1ɓMT +b|oPS=zp׫'|I#l(&mٶ '0]wԓ5lMu}CU}AwXxѱ^8l3YHDs@|ܗuRӦR&8.. e 7fyW$eL(苎5ԕ Vn$nȃ_12c[rԡ%Xeoqgdۂ/!Q"cdhS/Cm j?=099005Hinjg!u|1500=W'ѩ*Ո_NۃCτfB1>0"x*Ĝכ8Gr߆S`~G@ ; 3kqE]Ԃd#2lIRS H] ? ;rṉ ߅0 p-\o@AJDŽ8KT"򕂱4|%9f% 5&ե ntfS/!0BV'< :C>%zHf'B/m4g[Zry  w1,ӦrUٳոYF#lklkz, G͖J4D*J~Z5RV0>"8,bA nd 4xՆ l;0)S bjC;1C g^vEGgN`RVT-3\eիUguQmԠU :;x & Pw@;}NLAx&3EA ›j1g%z*S xh& At"$}zke`,G\^YnRJ޿ Khͻ"EM3f_AvvB=B $O6y>ri1"-y(GyײַBc)T";W/G*UxmzE#YK#{j<$u xya~yb]Z0_r:j]em>s6?؞##e\k vp7L+1<Ε)">B]!]/zS4HK){#Lu:kStwt&d:?P:^)ZgGҡx8_wVqQuFYqc[-@&F%J@ojʅgn_}trrەKO]./]{٫?g~O1^=ТTh[钔ŋdGIA_9GUj7Z\GQer^m`(ԕrr\g r"ZB݋-@u*LiwP% 5Ħ$ꨮ}>YU츨/5:CBnXW @% (M֒&+M/P~TsU٣YyƊp|2 WJ{(ӫVᵦ=Xέ ML]TE(W'Eg0 蝚3SuD.vݵ |΄i Re?-ԭ@ukLTY0ٚVY7[, wh蕣S?|soozh+X0EzHw!U:ċwV_75+R):7G7O%F柧3oR۵Ǡ&jd=(nUZh-碡<(|>Z):(\P 6CIC)!@˘V!={QnK . Ϟr\t hR{h>5_üƻxOӲC3NA8KlIXK V Ka[1c;#9v]!|pAUE\7ۉe'.-K'iNf9etǢ1ߥp NM~(ӫuO3)T M1CkA}4?LtPP)."Uc.- iBޚ=ocm=FcߋIp¨(Y.¢:bË(YԼ.AuPʒ 7`Q(/ӑYrVg5>:~MI& RF2ʓٲ]./z aۇ]q\U;y1]>w3V@W X6DAQ l>QC*`HHAH$]{ʍ[EPDM"ABem 2"r̹w&B{ϝνҷ3.xV+Rwp2HݨuLbPD"3\g7SM +V4Yn?oKJ>]8P8406L3gCӗ2+VݞB%D7b UWK f2dE}!/% !@_M]Z̞j8Z/~KAH|'^IpUѸ8)ΈB;c.ƝI0͡WM#Ƿ6ǎ676ꖀ8 ef|Y-Wx`LPP1:)](iu]FR{aGDW6R.[}.9i!_k6OXk:9Iݞ(`;Iti/g"3ƌ9cͦ~nhۄRn||[8%wۺu.. lS"*ƹ)TB6KfZw9-hjYJYS(0l(mHA'~U |af=2 7INt#/*Uք}1QW%CXR{*K7hBfB>KMwtXpVclpQGW{j&$&aDanD^^.y^)qVw:k1ojҒtEVb0_]w t%>+"ע-o  Ƃּ>-9h ܅ٶZfM#_y'^<6ߢZlb)~9N)<o&|XEuQ^V՛_U:nJS!*ؔ6\ZF`42:!ais&܆<- 5'M!-DҀXr|y}{;뻋G{?վXسz<60vb2z|_)̬3SY#:U3HXVHىe 7SlU90XoT2[EMH;⅐g!G\E S;%.QR;GYʎ,-hL6O2<>g(,Fm)y_D fT| JmT=tnKr%0Lqas C]}`CIJd4]qF*Oe`y^6.fBeD4P*QJס2X@A7aVN)JGmhI2-QDâ$ٔGNp#HUh1PPM$KP̛.o_Nn2sLLFЃwvbTڇvV_~,^KNOQsu4D;>N?1DgA2h Z<Q ho/QJ jSS53YK%wB8uV}r/) M@ưY?Ss,IwOG dͷB9H94⓲d4[aW+v?Fq,9uwA`\4_crs6bj.ni{}!<˯IoDO'ۉӉt'2ժn 4''SogS0;jkbmvZg}jJvRx9j3t[m84 ˆ h*ox$IbҋaWIA[/0(S"v-tmNJz6.Du0S.Sk5WumEmv"{;-jdk#i< -2![$v4X$ww"nhPUa!H3[39z'$KMF: ,M9Aņ(sr.YNO+yHQݝٻ۝]ݙڃ]÷H≭@P \k#Z0&KSRӣ`В&hibÔ B &*m-n~?_/u&GɓV")htǤWhkTu!o|"zz5Ef8'jLs|9S̰,YFA>m,:LDIGLIMEb;v;IuOZܪ83R<*;=葦Yel+hTvJPdFzKF5j3 uL KUq-/KpJkD^iD2́9<e%Ub3.yÿ^+1Nrm_:S v8I5\:m]}15fھ%sL$gǕvp2R95,>d] tȅD|]cYg?4fvZn`P\3mp * 4#H)D(+Sz?+/ChC7&k)ߥG@:L%!A"W "-nHKW189n,Oװ( vTq5ފɒc*Xq< |~~|a<(=j{/όfIi gCa!-l_I6hx¢7DaQNFsw#.N*vFړ\cOc|2>HQR}N:8"i,{BN},<TnTݨf]=ΐrӵz>2cF3<= +n7v,5 s l,;oi[MJ]n8XN[?{k±D>]Փ3clf_ 凱?}hQT O=_jr[[[[5-i Ub\Sbb }l1dvG3x128+@%= ƨB=Ⱦ94*6 |ZIY B*ccmৣ {|!7ݕ[~g\4(hF} Xy y,־t :.:{F,/sDfcNнU#fÇ؟?湓\H**?20 HB>uq0D1kocO\oNk0筱L1 f: Y甙cj }YAKu-2=M?+x!q4Ϗ1ۆ[}O3^sKF&i%dr>hnw%IG&e"cΧ}Z2ge={@YTKZ?\JtEJUHHEاԎK`alsX͵cF3YyR$WK5ڵ6F$QWɍvrdʉ GpBmҜ~罓r'͗"\Rbjvu/Wצ0?<双{SSlQ}V`o;e*y LZg4"kɲ+"s4tLYEN#~o#6'W)/~%MPr\p39VѮ<˜ȵR%t7A{3^%9iWkfe@86љu5SOձk$} LOr9xCM6FCf\卒 W"_aތ\TPkg$u+a9etVIrzb $MJT^Wg6)?!/~[F}bz)qŜNs*cǞ<_j|P/Ni+$?!r{ER`sZ"lbjEZ! ܼVj;| |on9r)WӚG\J]d)0Gz~0$'Yo} 862֗a zD ڞk>;ʮ+wF$;!z"kK~w2zKt">I';rb)5\6Icw[<;"=8>5q\VB99[g'zB9_Rϝ|^>ʅQL|kMLŠU}CWⱸt?s,"Fg2\I5x^!]rĬrX3 !V L\5 Yh¹4mk}Ξv3K%Bsַ'kp~#\o9Xح%w s)'>[.%R|mzm܋R9rUy~8)?S}ECrT}ajHfā+洷u]?Y2݊ ?ǮotS0#jpK_ɩ8{ܝHv'J0 7yKA!c"mARqJ;uxͲ9kᔒhe xa *Nsb0G0F^s g~ړ:7}3Dj7O3.cUVơ^ei *C5U~H\=8ob'@$%87xwQ>SAѷ-%}ofvX[z`I޼1Βcu#0WMm ״c#]`= Y +>uz#  ,ƅ[hdyn\4R\AyT/ݰ_=^LrOؘH\!ʈUĤ)g=˛'ċ OW4ѿ2$3W9a-_Of [ojܵ~['k \jc*6k/}Oʸ)RzMʒsd(}(5 K.'w$? pUϻFA0&B(ðJHBB@2 SJeZEE 8ڊC)LRqU:v:*X(wK ͹o_oݑs9ߗc2.9;ěTswHsYr~+|Os' j4w7vꍰ~ҏ g<%U&?6HATKE0]"{l[@'^:YVɋx Ha4 [m96߲uMBuq|%~OzY&H߆?Qm혂O_Y=,aXYeVz2֓gXӟh:E)`oa ,= 22^qLwI썰(zl2Z|+70 SY[=vc3^ߓ֟aHKBGzKt\HN~mglx7"=k+I]}*{Ji`{2-[_gٌ.袿;Yr:Oc:KnQ4PZ9a{wʟɸ0X덒N;%BzbgOG@Trw ÖrPc?D3a't+ҟZL:;oϻ/_ԼO7$#Wk*ݍ[`^f\i˙y{ g0.Q9 pF“G珵Bw>H@ɏT{Gq]}_΋ Tߒ#xd{{{{>BEN8BrwdenYPIjY?dߡw2C>!#܃Zb9wz~':r7;V6w egSm4$w"Cah@yS>-XͅR?}1FEf(6|C 88%[.5RynǬV95*yދe/Vw,F޷O*r|Vs6p_9 W~|+h~^3է'nWjUacD~F'ƷZ}V?k-IE+%mS-7Oz CiG܆룆1YsZGbzʝ{aCR+eOTc{)xVX3Q k2'Dg5A>I&]yLI5:M5O΍s.lk5 \j|?zjM9Ƨd#g)ָ:큿$lk(F Rm`#yt6o(L{qg wL/xQiw\oG{;69!eZp9"b߁x.?{p \QAgtrRܭg=PsZDq( s+X]*YQURK7}=9$W<'pvt3ӰuS ^7j9]%<[ eP;PгS{3Sݼ8}wr^-~Kis3gc%i+{U'^9}1nPRzŦX:~|m<&%:naLf< x"Ox,KtI O1ku& i@1&2?{3!k{bg@'͜γ΋KwS2zSbƥ,6yL|L=NYmM0۬ixLxޣq9uщf|Y?*fL h#IrصxZ|*X)% G?<}1,At](P`46-GSs8'}J^$ɭ(,CC{_A ; o~ޓ.Nٝz9lI-{78zxw%@c5&i\]4џ-`;W| _6oyoKXjI~oOXŰLm8{҂Y}-O,F9S]&~ $''R=:?gKAyK؞^C)A?_%զ-rLjsoڧ:hDj#_c;} khg~uG}^oJ{b>!!k d ˽әc\~)E2Wy [KbK*QGWjerN&ӇD?R3~n #_E]DZFw"M@>|H}%7L'nǻ/V,7PwԒ>/V+yaS(؋,x\~s;c³*aۋ;n rwb)i[dǛ p7sqSO/߆tޟ[3u?|;q8G9 ܇QyGpޠ د V0w-9O{~>}Hn8NouxeB1Tofb_FI'b<0>y&vQb ta2WdD?\ }iaО!p(:)Fb .6)wmhDo 汜EVEf#~)s!"y I31_cֈwlK򿀥X{g}(6[ٶ2m!|dDƘV2Oj_39. wpV=N832=ލ9 zOc@ݛrorE{ ʹ?j cS2/7k݅ZY,lVޡ_#w,8uz!?3X>~ž!+, b HWЧy( Byi0ʭ-%XQQ,ֱ-Wio/ޥ,`V0(稶, Ƌ7]%9.;+ Tڗ u{4vSq _?b@)_𹰮ѿ^cdz5Zo|_ " oujiU5lwq3RbYi 3|c% ½.?WwsK}srPeJ]^)ʾ Xb>GPsxbmBRX@ƍ饬EMθXCw)$xaMb:0?H*c3[Wxη{ y̓*\sȢlmf$ݼE_!$0 ) حXmxm1 v 3~2q&/8c>]$`>+ԑdouioyg>پɾ&6ǰy&8tٷֈ{㩇vfN7o96r~ُQ 3U\r^s;F#,u+l359ɜsZ.v<$>1ҏyu(IA>-!1> U?"/&2A:cO z?"2g3.Z> F)~ ~z6L&q*9=Td[l"|֓-Þ|ޓXa^H@ٖ>瞅]ˮ xﲬ,Z4nDaQem&,WDEuBҩZjM4]dөo*iiӐNZGfg129={r[=};Ս=hd7ѝ{ԍ:cR<;;QቢhoO֯kQ#ڞ^ivhZړZ,/P=PƦ^-Y;%ԼQ""_sXwjjt?%3FDI'F꼸Z&]{ĕ<rH @:G{72$CP} m~GFp 0"6>瀌i^MBW 5JS$v"QҕYe/+D!cD$eJ,s^e?Gg]l!/pcŸ\\E29 c.3 D0pL VJG:x+Gv 9.RdBK7o :\GSP?~>x!\gĸT"qcD0e/Q)uC{S2q-raKCع!1!5x3TAxg*þǰ<"5Q`cVu2J(`ն!6KLгўfg Q+VD gv/ͼ;gyXl9,op]> m`h໹U|;)s7cOeT/cehz}no1(77wHdՆk hpJM'UnNEUVzi+kFp%P D4 VpU@#d`9+o<55xѷ X0*l*wZ"gD1q|w cٱ= P }@ƶ+@0 vmcۤs;+e.zB_[gqSeW\-ěS/ߩT/^^Tw&l}rO.ɘ1b'Pp&g{yv].f/%ZR y+Oy"W)O)ू 9ӏ[V22?D O:gx| Ӑc Qr[-u&8:2vAlj:o=?&1n։kq5\zp}UOẍ1um9b8wo"%FS+iHW܈{Rk p,竼BIS "S,!BCb2)j11Kym/ɟ8 o+uƄ)]r͕΋/$VCv%-H\u%%zAcux%zQtu)"YQOrʱ]yՉt] H\O* )g"7KyѯCxm%&%IUc⢲9ʚg$!f2;[Oכ7WUsy9ϒkYr,ٖ,Œi1Y$ %füLLgh$ ~K"ۣ`AlUyn<[[!tIJZZH[:E(9\upp(Dt;TN3$z_@WT>`SP{ Vc͙3E9f)i LI}\BSyioBlXꃍxp[s) ak6<ŵxqq&8Y6V15ɖpSxJ'rB)8iHԡ4ÐHxfjvG/YCU7xBٲNaMz#Ñ#<v$Rxk@(1>\LCj ?da>Y 5|H:V ZqQ+OMq jMiBE{=Ϸ=)#/^2~/.>stream hL 0WDQD^ibʚ|{StNs>f0 i1o8 VDA!/ta]6^1geG endstream endobj 579 0 obj <>/Filter/FlateDecode/ID[<6ED954BC0EF0F143936766DC11EA27D0><7DAE5CED5CF27545A822800B3A0BCFBB>]/Index[9 1 49 1 467 1 555 1 559 1 561 1 566 14]/Info 467 0 R/Length 79/Prev 315106/Root 469 0 R/Size 580/Type/XRef/W[1 3 0]>>stream hbbd! n*% $\ ˔$ np%@"$; čP(*! endstream endobj startxref 414236 %%EOF 515 0 obj <> /P 6 0 R /Rect [138.76 626.781 270.009 644.093 ] /Subtype /Widget /T (Text1) /Type /Annot /V (GammaRay) /AP <> >> endobj 516 1 obj <> /Font <> /XObject <> >> >> stream /Tx BMC q BT /Helv 12 Tf 0 g 1 0 0 1 2.00 3.86 Tm (GammaRay) Tj ET Q EMC endstream endobj 580 0 obj <> stream STU endstream endobj startxref 415188 %%EOF gammaray-2.11.3/docs/api/000077500000000000000000000000001413310443000150665ustar00rootroot00000000000000gammaray-2.11.3/docs/api/CMakeLists.txt000066400000000000000000000020551413310443000176300ustar00rootroot00000000000000find_file(QDOC_QTCORE_TAG qtcore.tags HINTS ${QT_INSTALL_DOCS}/qtcore HINTS ${QT_INSTALL_DATA}/doc/qtcore ) if(QDOC_QTCORE_TAG) get_filename_component(QDOC_TAG_DIR ${QDOC_QTCORE_TAG} DIRECTORY) get_filename_component(QDOC_TAG_DIR ${QDOC_TAG_DIR} DIRECTORY) endif() file(GLOB _dox_deps *.dox *.html) gammaray_all_installed_headers(_all_hdrs) list(APPEND _all_hdrs ${_dox_deps}) gammaray_join_list(DOXYGEN_INPUT " \\\\\n" ${_all_hdrs}) set(DOXYGEN_OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}) #apidox generation using doxygen configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.cmake ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile ) add_custom_command( OUTPUT ${DOXYGEN_OUTPUT_DIR}/qch/gammaray-api.qch COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile DEPENDS ${_all_hdrs} ${_dox_deps} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile WORKING_DIRECTORY ${CMAKE_BINARY_DIR} ) add_custom_target(gammaray-api.qch ALL DEPENDS ${DOXYGEN_OUTPUT_DIR}/qch/gammaray-api.qch) install(FILES ${DOXYGEN_OUTPUT_DIR}/qch/gammaray-api.qch DESTINATION ${QCH_INSTALL_DIR}) gammaray-2.11.3/docs/api/Doxyfile.cmake000066400000000000000000003372251413310443000176670ustar00rootroot00000000000000# Doxyfile 1.8.20 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. # # All text after a double hash (##) is considered a comment and is placed in # front of the TAG it is preceding. # # All text after a single hash (#) is considered a comment and will be ignored. # The format is: # TAG = value [value, ...] # For lists, items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (\" \"). #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the configuration # file that follow. The default is UTF-8 which is also the encoding used for all # text before the first occurrence of this tag. Doxygen uses libiconv (or the # iconv built into libc) for the transcoding. See # https://www.gnu.org/software/libiconv/ for the list of possible encodings. # The default value is: UTF-8. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded by # double-quotes, unless you are using Doxywizard) that should identify the # project for which the documentation is generated. This name is used in the # title of most generated pages and in a few other places. # The default value is: My Project. PROJECT_NAME = "GammaRay API Documentation" # The PROJECT_NUMBER tag can be used to enter a project or revision number. This # could be handy for archiving the generated documentation or if some version # control system is used. PROJECT_NUMBER = @GAMMARAY_VERSION@ # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a # quick idea about the purpose of the project. Keep the description short. PROJECT_BRIEF = # With the PROJECT_LOGO tag one can specify a logo or an icon that is included # in the documentation. The maximum height of the logo should not exceed 55 # pixels and the maximum width should not exceed 200 pixels. Doxygen will copy # the logo to the output directory. PROJECT_LOGO = # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path # into which the generated documentation will be written. If a relative path is # entered, it will be relative to the location where doxygen was started. If # left blank the current directory will be used. OUTPUT_DIRECTORY = "@DOXYGEN_OUTPUT_DIR@" # If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- # directories (in 2 levels) under the output directory of each output format and # will distribute the generated files over these directories. Enabling this # option can be useful when feeding doxygen a huge amount of source files, where # putting all generated files in the same directory would otherwise causes # performance problems for the file system. # The default value is: NO. CREATE_SUBDIRS = NO # If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII # characters to appear in the names of generated files. If set to NO, non-ASCII # characters will be escaped, for example _xE3_x81_x84 will be used for Unicode # U+3044. # The default value is: NO. ALLOW_UNICODE_NAMES = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, # Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), # Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, # Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), # Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, # Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, # Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, # Ukrainian and Vietnamese. # The default value is: English. OUTPUT_LANGUAGE = English # The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all generated output in the proper direction. # Possible values are: None, LTR, RTL and Context. # The default value is: None. OUTPUT_TEXT_DIRECTION = None # If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member # descriptions after the members that are listed in the file and class # documentation (similar to Javadoc). Set to NO to disable this. # The default value is: YES. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief # description of a member or function before the detailed description # # Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. # The default value is: YES. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator that is # used to form the text in various listings. Each string in this list, if found # as the leading text of the brief description, will be stripped from the text # and the result, after processing the whole list, is used as the annotated # text. Otherwise, the brief description is used as-is. If left blank, the # following values are used ($name is automatically replaced with the name of # the entity):The $name class, The $name widget, The $name file, is, provides, # specifies, contains, represents, a, an and the. ABBREVIATE_BRIEF = "The \$name class" \ "The \$name widget" \ "The \$name file" \ is \ provides \ specifies \ contains \ represents \ a \ an \ the # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # doxygen will generate a detailed section even if there is only a brief # description. # The default value is: NO. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. # The default value is: NO. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path # before files name in the file list and in the header files. If set to NO the # shortest path that makes the file name unique will be used # The default value is: YES. FULL_PATH_NAMES = NO # The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. # Stripping is only done if one of the specified strings matches the left-hand # part of the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the path to # strip. # # Note that you can specify absolute paths here, but also relative paths, which # will be relative from the directory where doxygen is started. # This tag requires that the tag FULL_PATH_NAMES is set to YES. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the # path mentioned in the documentation of a class, which tells the reader which # header file to include in order to use a class. If left blank only the name of # the header file containing the class definition is used. Otherwise one should # specify the list of include paths that are normally passed to the compiler # using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but # less readable) file names. This can be useful is your file systems doesn't # support long names like on DOS, Mac, or CD-ROM. # The default value is: NO. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the # first line (until the first dot) of a Javadoc-style comment as the brief # description. If set to NO, the Javadoc-style will behave just like regular Qt- # style comments (thus requiring an explicit @brief command for a brief # description.) # The default value is: NO. JAVADOC_AUTOBRIEF = NO # If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line # such as # /*************** # as being the beginning of a Javadoc-style comment "banner". If set to NO, the # Javadoc-style will behave just like regular comments and it will not be # interpreted by doxygen. # The default value is: NO. JAVADOC_BANNER = NO # If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first # line (until the first dot) of a Qt-style comment as the brief description. If # set to NO, the Qt-style will behave just like regular Qt-style comments (thus # requiring an explicit \brief command for a brief description.) # The default value is: NO. QT_AUTOBRIEF = YES # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a # multi-line C++ special comment block (i.e. a block of //! or /// comments) as # a brief description. This used to be the default behavior. The new default is # to treat a multi-line C++ comment block as a detailed description. Set this # tag to YES if you prefer the old behavior instead. # # Note that setting this tag to YES also means that rational rose comments are # not recognized any more. # The default value is: NO. MULTILINE_CPP_IS_BRIEF = NO # By default Python docstrings are displayed as preformatted text and doxygen's # special commands cannot be used. By setting PYTHON_DOCSTRING to NO the # doxygen's special commands can be used and the contents of the docstring # documentation blocks is shown as doxygen documentation. # The default value is: YES. PYTHON_DOCSTRING = YES # If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the # documentation from any documented member that it re-implements. # The default value is: YES. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new # page for each member. If set to NO, the documentation of a member will be part # of the file/class/namespace that contains it. # The default value is: NO. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen # uses this value to replace tabs by spaces in code fragments. # Minimum value: 1, maximum value: 16, default value: 4. TAB_SIZE = 8 # This tag can be used to specify a number of aliases that act as commands in # the documentation. An alias has the form: # name=value # For example adding # "sideeffect=@par Side Effects:\n" # will allow you to put the command \sideeffect (or @sideeffect) in the # documentation, which will result in a user-defined paragraph with heading # "Side Effects:". You can put \n's in the value part of an alias to insert # newlines (in the resulting output). You can put ^^ in the value part of an # alias to insert a newline as if a physical newline was in the original file. # When you need a literal { or } or , in the value part of an alias you have to # escape them by means of a backslash (\), this can lead to conflicts with the # commands \{ and \} for these it is advised to use the version @{ and @} or use # a double escape (\\{ and \\}) ALIASES = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources # only. Doxygen will then generate output that is more tailored for C. For # instance, some of the names that are used will be different. The list of all # members will be omitted, etc. # The default value is: NO. OPTIMIZE_OUTPUT_FOR_C = NO # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or # Python sources only. Doxygen will then generate output that is more tailored # for that language. For instance, namespaces will be presented as packages, # qualified scopes will look different, etc. # The default value is: NO. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources. Doxygen will then generate output that is tailored for Fortran. # The default value is: NO. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for VHDL. # The default value is: NO. OPTIMIZE_OUTPUT_VHDL = NO # Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice # sources only. Doxygen will then generate output that is more tailored for that # language. For instance, namespaces will be presented as modules, types will be # separated into more groups, etc. # The default value is: NO. OPTIMIZE_OUTPUT_SLICE = NO # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, and # language is one of the parsers supported by doxygen: IDL, Java, JavaScript, # Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, VHDL, # Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: # FortranFree, unknown formatted Fortran: Fortran. In the later case the parser # tries to guess whether the code is fixed or free formatted code, this is the # default for Fortran type files). For instance to make doxygen treat .inc files # as Fortran files (default is PHP), and .f files as C (default is Fortran), # use: inc=Fortran f=C. # # Note: For files without extension you can use no_extension as a placeholder. # # Note that for custom extensions you also need to set FILE_PATTERNS otherwise # the files are not read by doxygen. EXTENSION_MAPPING = # If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments # according to the Markdown format, which allows for more readable # documentation. See https://daringfireball.net/projects/markdown/ for details. # The output of markdown processing is further processed by doxygen, so you can # mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in # case of backward compatibilities issues. # The default value is: YES. MARKDOWN_SUPPORT = YES # When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up # to that level are automatically included in the table of contents, even if # they do not have an id attribute. # Note: This feature currently applies only to Markdown headings. # Minimum value: 0, maximum value: 99, default value: 5. # This tag requires that the tag MARKDOWN_SUPPORT is set to YES. TOC_INCLUDE_HEADINGS = 5 # When enabled doxygen tries to link words that correspond to documented # classes, or namespaces to their corresponding documentation. Such a link can # be prevented in individual cases by putting a % sign in front of the word or # globally by setting AUTOLINK_SUPPORT to NO. # The default value is: YES. AUTOLINK_SUPPORT = YES # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should set this # tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); # versus func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. # The default value is: NO. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. # The default value is: NO. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip (see: # https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen # will parse them like normal C++ but will assume all classes use public instead # of private inheritance when no explicit protection keyword is present. # The default value is: NO. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate # getter and setter methods for a property. Setting this option to YES will make # doxygen to replace the get and set methods by a property in the documentation. # This will only work if the methods are indeed getting or setting a simple # type. If this is not the case, or you want to show the methods anyway, you # should set this option to NO. # The default value is: YES. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. # The default value is: NO. DISTRIBUTE_GROUP_DOC = NO # If one adds a struct or class to a group and this option is enabled, then also # any nested class or struct is added to the same group. By default this option # is disabled and one has to add nested compounds explicitly via \ingroup. # The default value is: NO. GROUP_NESTED_COMPOUNDS = NO # Set the SUBGROUPING tag to YES to allow class member groups of the same type # (for instance a group of public functions) to be put as a subgroup of that # type (e.g. under the Public Functions section). Set it to NO to prevent # subgrouping. Alternatively, this can be done per class using the # \nosubgrouping command. # The default value is: YES. SUBGROUPING = YES # When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions # are shown inside the group in which they are included (e.g. using \ingroup) # instead of on a separate page (for HTML and Man pages) or section (for LaTeX # and RTF). # # Note that this feature does not work in combination with # SEPARATE_MEMBER_PAGES. # The default value is: NO. INLINE_GROUPED_CLASSES = NO # When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions # with only public data fields or simple typedef fields will be shown inline in # the documentation of the scope in which they are defined (i.e. file, # namespace, or group documentation), provided this scope is documented. If set # to NO, structs, classes, and unions are shown on a separate page (for HTML and # Man pages) or section (for LaTeX and RTF). # The default value is: NO. INLINE_SIMPLE_STRUCTS = NO # When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or # enum is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically be # useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. # The default value is: NO. TYPEDEF_HIDES_STRUCT = NO # The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This # cache is used to resolve symbols given their name and scope. Since this can be # an expensive process and often the same symbol appears multiple times in the # code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small # doxygen will become slower. If the cache is too large, memory is wasted. The # cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range # is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 # symbols. At the end of a run doxygen will report the cache usage and suggest # the optimal cache size from a speed point of view. # Minimum value: 0, maximum value: 9, default value: 0. LOOKUP_CACHE_SIZE = 0 # The NUM_PROC_THREADS specifies the number threads doxygen is allowed to use # during processing. When set to 0 doxygen will based this on the number of # cores available in the system. You can set it explicitly to a value larger # than 0 to get more control over the balance between CPU load and processing # speed. At this moment only the input processing can be done using multiple # threads. Since this is still an experimental feature the default is set to 1, # which efficively disables parallel processing. Please report any issues you # encounter. Generating dot graphs in parallel is controlled by the # DOT_NUM_THREADS setting. # Minimum value: 0, maximum value: 32, default value: 1. NUM_PROC_THREADS = 1 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in # documentation are documented, even if no documentation was available. Private # class members and static file members will be hidden unless the # EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. # Note: This will also disable the warnings about undocumented members that are # normally produced when WARNINGS is set to YES. # The default value is: NO. EXTRACT_ALL = NO # If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will # be included in the documentation. # The default value is: NO. EXTRACT_PRIVATE = NO # If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual # methods of a class will be included in the documentation. # The default value is: NO. EXTRACT_PRIV_VIRTUAL = NO # If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal # scope will be included in the documentation. # The default value is: NO. EXTRACT_PACKAGE = NO # If the EXTRACT_STATIC tag is set to YES, all static members of a file will be # included in the documentation. # The default value is: NO. EXTRACT_STATIC = YES # If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined # locally in source files will be included in the documentation. If set to NO, # only classes defined in header files are included. Does not have any effect # for Java sources. # The default value is: YES. EXTRACT_LOCAL_CLASSES = NO # This flag is only useful for Objective-C code. If set to YES, local methods, # which are defined in the implementation section but not in the interface are # included in the documentation. If set to NO, only methods in the interface are # included. # The default value is: NO. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base name of # the file that contains the anonymous namespace. By default anonymous namespace # are hidden. # The default value is: NO. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all # undocumented members inside documented classes or files. If set to NO these # members will be included in the various overviews, but no documentation # section is generated. This option has no effect if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. If set # to NO, these classes will be included in the various overviews. This option # has no effect if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend # declarations. If set to NO, these declarations will be included in the # documentation. # The default value is: NO. HIDE_FRIEND_COMPOUNDS = YES # If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any # documentation blocks found inside the body of a function. If set to NO, these # blocks will be appended to the function's detailed documentation block. # The default value is: NO. HIDE_IN_BODY_DOCS = YES # The INTERNAL_DOCS tag determines if documentation that is typed after a # \internal command is included. If the tag is set to NO then the documentation # will be excluded. Set it to YES to include the internal documentation. # The default value is: NO. INTERNAL_DOCS = YES # If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file # names in lower-case letters. If set to YES, upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # (including Cygwin) and Mac users are advised to set this option to NO. # The default value is: system dependent. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with # their full class and namespace scopes in the documentation. If set to YES, the # scope will be hidden. # The default value is: NO. HIDE_SCOPE_NAMES = NO # If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will # append additional text to a page's title, such as Class Reference. If set to # YES the compound reference will be hidden. # The default value is: NO. HIDE_COMPOUND_REFERENCE= NO # If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of # the files that are included by a file in the documentation of that file. # The default value is: YES. SHOW_INCLUDE_FILES = YES # If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each # grouped member an include statement to the documentation, telling the reader # which file to include in order to use the member. # The default value is: NO. SHOW_GROUPED_MEMB_INC = NO # If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include # files with double quotes in the documentation rather than with sharp brackets. # The default value is: NO. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the # documentation for inline members. # The default value is: YES. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the # (detailed) documentation of file and class members alphabetically by member # name. If set to NO, the members will appear in declaration order. # The default value is: YES. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief # descriptions of file, namespace and class members alphabetically by member # name. If set to NO, the members will appear in declaration order. Note that # this will also influence the order of the classes in the class list. # The default value is: NO. SORT_BRIEF_DOCS = YES # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the # (brief and detailed) documentation of class members so that constructors and # destructors are listed first. If set to NO the constructors will appear in the # respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. # Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief # member documentation. # Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting # detailed member documentation. # The default value is: NO. SORT_MEMBERS_CTORS_1ST = YES # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy # of group names into alphabetical order. If set to NO the group names will # appear in their defined order. # The default value is: NO. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by # fully-qualified names, including namespaces. If set to NO, the class list will # be sorted only by class name, not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the alphabetical # list. # The default value is: NO. SORT_BY_SCOPE_NAME = NO # If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper # type resolution of all parameters of a function it will reject a match between # the prototype and the implementation of a member function even if there is # only one candidate or it is obvious which candidate to choose by doing a # simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still # accept a match between prototype and implementation in such cases. # The default value is: NO. STRICT_PROTO_MATCHING = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo # list. This list is created by putting \todo commands in the documentation. # The default value is: YES. GENERATE_TODOLIST = NO # The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test # list. This list is created by putting \test commands in the documentation. # The default value is: YES. GENERATE_TESTLIST = NO # The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug # list. This list is created by putting \bug commands in the documentation. # The default value is: YES. GENERATE_BUGLIST = NO # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) # the deprecated list. This list is created by putting \deprecated commands in # the documentation. # The default value is: YES. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional documentation # sections, marked by \if ... \endif and \cond # ... \endcond blocks. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the # initial value of a variable or macro / define can have for it to appear in the # documentation. If the initializer consists of more lines than specified here # it will be hidden. Use a value of 0 to hide initializers completely. The # appearance of the value of individual variables and macros / defines can be # controlled using \showinitializer or \hideinitializer command in the # documentation regardless of this setting. # Minimum value: 0, maximum value: 10000, default value: 30. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated at # the bottom of the documentation of classes and structs. If set to YES, the # list will mention the files that were used to generate the documentation. # The default value is: YES. SHOW_USED_FILES = NO # Set the SHOW_FILES tag to NO to disable the generation of the Files page. This # will remove the Files entry from the Quick Index and from the Folder Tree View # (if specified). # The default value is: YES. SHOW_FILES = NO # Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces # page. This will remove the Namespaces entry from the Quick Index and from the # Folder Tree View (if specified). # The default value is: YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command command input-file, where command is the value of the # FILE_VERSION_FILTER tag, and input-file is the name of an input file provided # by doxygen. Whatever the program writes to standard output is used as the file # version. For an example see the documentation. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. To create the layout file # that represents doxygen's defaults, run doxygen with the -l option. You can # optionally specify a file name after the option, if omitted DoxygenLayout.xml # will be used as the name of the layout file. # # Note that if you run doxygen from a directory containing a file called # DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE # tag is left empty. LAYOUT_FILE = # The CITE_BIB_FILES tag can be used to specify one or more bib files containing # the reference definitions. This must be a list of .bib files. The .bib # extension is automatically appended if omitted. This requires the bibtex tool # to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info. # For LaTeX the style of the bibliography can be controlled using # LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the # search path. See also \cite for info how to create references. CITE_BIB_FILES = #--------------------------------------------------------------------------- # Configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated to # standard output by doxygen. If QUIET is set to YES this implies that the # messages are off. # The default value is: NO. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated to standard error (stderr) by doxygen. If WARNINGS is set to YES # this implies that the warnings are on. # # Tip: Turn warnings on while writing the documentation. # The default value is: YES. WARNINGS = YES # If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate # warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag # will automatically be disabled. # The default value is: YES. WARN_IF_UNDOCUMENTED = YES # If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some parameters # in a documented function, or documenting parameters that don't exist or using # markup commands wrongly. # The default value is: YES. WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that # are documented, but have no documentation for their parameters or return # value. If set to NO, doxygen will only warn about wrong or incomplete # parameter documentation, but not about the absence of documentation. If # EXTRACT_ALL is set to YES then this flag will automatically be disabled. # The default value is: NO. WARN_NO_PARAMDOC = YES # If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when # a warning is encountered. # The default value is: NO. WARN_AS_ERROR = NO # The WARN_FORMAT tag determines the format of the warning messages that doxygen # can produce. The string should contain the $file, $line, and $text tags, which # will be replaced by the file and line number from which the warning originated # and the warning text. Optionally the format may contain $version, which will # be replaced by the version of the file (if it could be obtained via # FILE_VERSION_FILTER) # The default value is: $file:$line: $text. WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning and error # messages should be written. If left blank the output is written to standard # error (stderr). WARN_LOGFILE = doxygen.log #--------------------------------------------------------------------------- # Configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag is used to specify the files and/or directories that contain # documented source files. You may enter file names like myfile.cpp or # directories like /usr/src/myproject. Separate the files or directories with # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. INPUT = @DOXYGEN_INPUT@ # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses # libiconv (or the iconv built into libc) for the transcoding. See the libiconv # documentation (see: https://www.gnu.org/software/libiconv/) for the list of # possible encodings. # The default value is: UTF-8. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and # *.h) to filter out the source-files in the directories. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # read by doxygen. # # If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, # *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, # *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, # *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C comment), # *.doc (to be provided as doxygen C comment), *.txt (to be provided as doxygen # C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd, # *.vhdl, *.ucf, *.qsf and *.ice. FILE_PATTERNS = *.cpp \ *.cc \ *.cxx \ *.h \ *.hh \ *.hxx \ *.hpp \ *.dox # The RECURSIVE tag can be used to specify whether or not subdirectories should # be searched for input files as well. # The default value is: NO. RECURSIVE = YES # The EXCLUDE tag can be used to specify files and/or directories that should be # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. # # Note that relative paths are relative to the directory from which doxygen is # run. EXCLUDE = "@CMAKE_SOURCE_DIR@/common/gammaray_common_export.h" \ "@CMAKE_SOURCE_DIR@/core/gammaray_core_export.h" \ "@CMAKE_SOURCE_DIR@/ui/gammaray_ui_export.h" # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded # from the input. # The default value is: NO. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. # # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories for example use the pattern */test/* EXCLUDE_PATTERNS = */.svn/* \ */.git/* \ */cmake/* \ *.moc.* \ moc* \ *.all_cpp.* \ *unload.* \ */test/* \ */tests/* \ *_p.cpp \ *_export.h # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test # # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories use the pattern */test/* EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or directories # that contain example code fragments that are included (see the \include # command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and # *.h) to filter out the source-files in the directories. If left blank all # files are included. EXAMPLE_PATTERNS = * # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude commands # irrespective of the value of the RECURSIVE tag. # The default value is: NO. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or directories # that contain images that are to be included in the documentation (see the # \image command). IMAGE_PATH = "@CMAKE_CURRENT_SOURCE_DIR@" # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command: # # # # where is the value of the INPUT_FILTER tag, and is the # name of an input file. Doxygen will then use the output that the filter # program writes to standard output. If FILTER_PATTERNS is specified, this tag # will be ignored. # # Note that the filter must not add or remove lines; it is applied before the # code is scanned, but not when the output code is generated. If lines are added # or removed, the anchors will not be placed correctly. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # properly processed by doxygen. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. Doxygen will compare the file name with each pattern and apply the # filter if there is a match. The filters are a list of the form: pattern=filter # (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how # filters are used. If the FILTER_PATTERNS tag is empty or if none of the # patterns match the file name, INPUT_FILTER is applied. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # properly processed by doxygen. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will also be used to filter the input files that are used for # producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). # The default value is: NO. FILTER_SOURCE_FILES = NO # The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file # pattern. A pattern will override the setting for FILTER_PATTERN (if any) and # it is also possible to disable source filtering for a specific pattern using # *.ext= (so without naming a filter). # This tag requires that the tag FILTER_SOURCE_FILES is set to YES. FILTER_SOURCE_PATTERNS = # If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that # is part of the input, its contents will be placed on the main page # (index.html). This can be useful if you have a project on for instance GitHub # and want to reuse the introduction page also for the doxygen output. USE_MDFILE_AS_MAINPAGE = #--------------------------------------------------------------------------- # Configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will be # generated. Documented entities will be cross-referenced with these sources. # # Note: To get rid of all source code in the generated output, make sure that # also VERBATIM_HEADERS is set to NO. # The default value is: NO. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body of functions, # classes and enums directly into the documentation. # The default value is: NO. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any # special comment blocks from generated source code fragments. Normal C, C++ and # Fortran comments will always remain visible. # The default value is: YES. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES then for each documented # entity all documented functions referencing it will be listed. # The default value is: NO. REFERENCED_BY_RELATION = NO # If the REFERENCES_RELATION tag is set to YES then for each documented function # all documented entities called/used by that function will be listed. # The default value is: NO. REFERENCES_RELATION = NO # If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set # to YES then the hyperlinks from functions in REFERENCES_RELATION and # REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will # link to the documentation. # The default value is: YES. REFERENCES_LINK_SOURCE = YES # If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the # source code will show a tooltip with additional information such as prototype, # brief description and links to the definition and documentation. Since this # will make the HTML file larger and loading of large files a bit slower, you # can opt to disable this feature. # The default value is: YES. # This tag requires that the tag SOURCE_BROWSER is set to YES. SOURCE_TOOLTIPS = YES # If the USE_HTAGS tag is set to YES then the references to source code will # point to the HTML generated by the htags(1) tool instead of doxygen built-in # source browser. The htags tool is part of GNU's global source tagging system # (see https://www.gnu.org/software/global/global.html). You will need version # 4.8.6 or higher. # # To use it do the following: # - Install the latest version of global # - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file # - Make sure the INPUT points to the root of the source tree # - Run doxygen as normal # # Doxygen will invoke htags (and that will in turn invoke gtags), so these # tools must be available from the command line (i.e. in the search path). # # The result: instead of the source browser generated by doxygen, the links to # source code will now point to the output of htags. # The default value is: NO. # This tag requires that the tag SOURCE_BROWSER is set to YES. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a # verbatim copy of the header file for each class for which an include is # specified. Set to NO to disable this. # See also: Section \class. # The default value is: YES. VERBATIM_HEADERS = NO # If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the # clang parser (see: http://clang.llvm.org/) for more accurate parsing at the # cost of reduced performance. This can be particularly helpful with template # rich C++ code for which doxygen's built-in parser lacks the necessary type # information. # Note: The availability of this option depends on whether or not doxygen was # generated with the -Duse_libclang=ON option for CMake. # The default value is: NO. CLANG_ASSISTED_PARSING = NO # If clang assisted parsing is enabled you can provide the compiler with command # line options that you would normally use when invoking the compiler. Note that # the include paths will already be set by doxygen for the files and directories # specified with INPUT and INCLUDE_PATH. # This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. CLANG_OPTIONS = # If clang assisted parsing is enabled you can provide the clang parser with the # path to the directory containing a file called compile_commands.json. This # file is the compilation database (see: # http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) containing the # options used when the source files were built. This is equivalent to # specifying the "-p" option to a clang tool, such as clang-check. These options # will then be passed to the parser. Any options specified with CLANG_OPTIONS # will be added as well. # Note: The availability of this option depends on whether or not doxygen was # generated with the -Duse_libclang=ON option for CMake. CLANG_DATABASE_PATH = #--------------------------------------------------------------------------- # Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all # compounds will be generated. Enable this if the project contains a lot of # classes, structs, unions or interfaces. # The default value is: YES. ALPHABETICAL_INDEX = NO # The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in # which the alphabetical index list will be split. # Minimum value: 1, maximum value: 20, default value: 5. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all classes will # be put under the same header in the alphabetical index. The IGNORE_PREFIX tag # can be used to specify a prefix (or a list of prefixes) that should be ignored # while generating the index headers. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. IGNORE_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output # The default value is: YES. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a # relative path is entered the value of OUTPUT_DIRECTORY will be put in front of # it. # The default directory is: html. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for each # generated HTML page (for example: .htm, .php, .asp). # The default value is: .html. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a user-defined HTML header file for # each generated HTML page. If the tag is left blank doxygen will generate a # standard header. # # To get valid HTML the header file that includes any scripts and style sheets # that doxygen needs, which is dependent on the configuration options used (e.g. # the setting GENERATE_TREEVIEW). It is highly recommended to start with a # default header using # doxygen -w html new_header.html new_footer.html new_stylesheet.css # YourConfigFile # and then modify the file new_header.html. See also section "Doxygen usage" # for information on how to generate the default header that doxygen normally # uses. # Note: The header is subject to change so you typically have to regenerate the # default header when upgrading to a newer version of doxygen. For a description # of the possible markers and block names see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each # generated HTML page. If the tag is left blank doxygen will generate a standard # footer. See HTML_HEADER for more information on how to generate a default # footer and what special commands can be used inside the footer. See also # section "Doxygen usage" for information on how to generate the default footer # that doxygen normally uses. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FOOTER = "@CMAKE_CURRENT_SOURCE_DIR@/footer.html" # The HTML_STYLESHEET tag can be used to specify a user-defined cascading style # sheet that is used by each HTML page. It can be used to fine-tune the look of # the HTML output. If left blank doxygen will generate a default style sheet. # See also section "Doxygen usage" for information on how to generate the style # sheet that doxygen normally uses. # Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as # it is more robust and this tag (HTML_STYLESHEET) will in the future become # obsolete. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_STYLESHEET = # The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined # cascading style sheets that are included after the standard style sheets # created by doxygen. Using this option one can overrule certain style aspects. # This is preferred over using HTML_STYLESHEET since it does not replace the # standard style sheet and is therefore more robust against future updates. # Doxygen will copy the style sheet files to the output directory. # Note: The order of the extra style sheet files is of importance (e.g. the last # style sheet in the list overrules the setting of the previous ones in the # list). For an example see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_STYLESHEET = # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the HTML output directory. Note # that these files will be copied to the base HTML output directory. Use the # $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these # files. In the HTML_STYLESHEET file, use the file name only. Also note that the # files will be copied as-is; there are no commands or markers available. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_FILES = "@CMAKE_CURRENT_SOURCE_DIR@/kdab-logo-16x16.png" \ "@CMAKE_CURRENT_SOURCE_DIR@/kdab-gammaray-logo-16x16.png" # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen # will adjust the colors in the style sheet and background images according to # this color. Hue is specified as an angle on a colorwheel, see # https://en.wikipedia.org/wiki/Hue for more information. For instance the value # 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 # purple, and 360 is red again. # Minimum value: 0, maximum value: 359, default value: 220. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors # in the HTML output. For a value of 0 the output will use grayscales only. A # value of 255 will produce the most vivid colors. # Minimum value: 0, maximum value: 255, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_SAT = 100 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the # luminance component of the colors in the HTML output. Values below 100 # gradually make the output lighter, whereas values above 100 make the output # darker. The value divided by 100 is the actual gamma applied, so 80 represents # a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not # change the gamma. # Minimum value: 40, maximum value: 240, default value: 80. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting this # to YES can help to show when doxygen was last run and thus if the # documentation is up to date. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_TIMESTAMP = YES # If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML # documentation will contain a main index with vertical navigation menus that # are dynamically created via JavaScript. If disabled, the navigation index will # consists of multiple levels of tabs that are statically embedded in every HTML # page. Disable this option to support browsers that do not have JavaScript, # like the Qt help browser. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_DYNAMIC_MENUS = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_DYNAMIC_SECTIONS = NO # With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries # shown in the various tree structured indices initially; the user can expand # and collapse entries dynamically later on. Doxygen will expand the tree to # such a level that at most the specified number of entries are visible (unless # a fully collapsed tree already exceeds this amount). So setting the number of # entries 1 will produce a full collapsed tree by default. 0 is a special value # representing an infinite number of entries and will result in a full expanded # tree by default. # Minimum value: 0, maximum value: 9999, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_INDEX_NUM_ENTRIES = 100 # If the GENERATE_DOCSET tag is set to YES, additional index files will be # generated that can be used as input for Apple's Xcode 3 integrated development # environment (see: https://developer.apple.com/xcode/), introduced with OSX # 10.5 (Leopard). To create a documentation set, doxygen will generate a # Makefile in the HTML output directory. Running make will produce the docset in # that directory and running make install will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at # startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy # genXcode/_index.html for more information. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_DOCSET = NO # This tag determines the name of the docset feed. A documentation feed provides # an umbrella under which multiple documentation sets from a single provider # (such as a company or product suite) can be grouped. # The default value is: Doxygen generated docs. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_FEEDNAME = "Doxygen generated docs" # This tag specifies a string that should uniquely identify the documentation # set bundle. This should be a reverse domain-name style string, e.g. # com.mycompany.MyDocSet. Doxygen will append .docset to the name. # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_BUNDLE_ID = org.doxygen.Project # The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify # the documentation publisher. This should be a reverse domain-name style # string, e.g. com.mycompany.MyDocSet.documentation. # The default value is: org.doxygen.Publisher. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_PUBLISHER_ID = org.doxygen.Publisher # The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. # The default value is: Publisher. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three # additional HTML index files: index.hhp, index.hhc, and index.hhk. The # index.hhp is a project file that can be read by Microsoft's HTML Help Workshop # (see: https://www.microsoft.com/en-us/download/details.aspx?id=21138) on # Windows. # # The HTML Help Workshop contains a compiler that can convert all HTML output # generated by doxygen into a single compiled HTML file (.chm). Compiled HTML # files are now used as the Windows 98 help format, and will replace the old # Windows help format (.hlp) on all Windows platforms in the future. Compressed # HTML files also contain an index, a table of contents, and you can search for # words in the documentation. The HTML workshop also contains a viewer for # compressed HTML files. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_HTMLHELP = NO # The CHM_FILE tag can be used to specify the file name of the resulting .chm # file. You can add a path in front of the file if the result should not be # written to the html output directory. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_FILE = # The HHC_LOCATION tag can be used to specify the location (absolute path # including file name) of the HTML help compiler (hhc.exe). If non-empty, # doxygen will try to run the HTML help compiler on the generated index.hhp. # The file has to be specified with full path. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. HHC_LOCATION = # The GENERATE_CHI flag controls if a separate .chi index file is generated # (YES) or that it should be included in the main .chm file (NO). # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. GENERATE_CHI = NO # The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) # and project file content. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_INDEX_ENCODING = # The BINARY_TOC flag controls whether a binary table of contents is generated # (YES) or a normal table of contents (NO) in the .chm file. Furthermore it # enables the Previous and Next buttons. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members to # the table of contents of the HTML help documentation and to the tree view. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that # can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help # (.qch) of the generated HTML documentation. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_QHP = YES # If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify # the file name of the resulting .qch file. The path specified is relative to # the HTML output folder. # This tag requires that the tag GENERATE_QHP is set to YES. QCH_FILE = @DOXYGEN_OUTPUT_DIR@/qch/gammaray-api.qch # The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help # Project output. For more information please see Qt Help Project / Namespace # (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_QHP is set to YES. QHP_NAMESPACE = com.kdab.GammaRay.api.@GAMMARAY_PLUGIN_VERSION@ # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt # Help Project output. For more information please see Qt Help Project / Virtual # Folders (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual- # folders). # The default value is: doc. # This tag requires that the tag GENERATE_QHP is set to YES. QHP_VIRTUAL_FOLDER = GammaRay-@GAMMARAY_VERSION@ # If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom # filter to add. For more information please see Qt Help Project / Custom # Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom- # filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_NAME = # The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see Qt Help Project / Custom # Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom- # filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's filter section matches. Qt Help Project / Filter Attributes (see: # https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_SECT_FILTER_ATTRS = # The QHG_LOCATION tag can be used to specify the location of Qt's # qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the # generated .qhp file. # This tag requires that the tag GENERATE_QHP is set to YES. QHG_LOCATION = @QHELPGEN_EXECUTABLE@ # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be # generated, together with the HTML files, they form an Eclipse help plugin. To # install this plugin and make it available under the help contents menu in # Eclipse, the contents of the directory containing the HTML and XML files needs # to be copied into the plugins directory of eclipse. The name of the directory # within the plugins directory should be the same as the ECLIPSE_DOC_ID value. # After copying Eclipse needs to be restarted before the help appears. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_ECLIPSEHELP = NO # A unique identifier for the Eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have this # name. Each documentation set should have its own identifier. # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. ECLIPSE_DOC_ID = org.doxygen.Project # If you want full control over the layout of the generated HTML pages it might # be necessary to disable the index and replace it with your own. The # DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top # of each HTML page. A value of NO enables the index and the value YES disables # it. Since the tabs in the index contain the same information as the navigation # tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. DISABLE_INDEX = NO # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. If the tag # value is set to YES, a side panel will be generated containing a tree-like # index structure (just like the one that is generated for HTML Help). For this # to work a browser that supports JavaScript, DHTML, CSS and frames is required # (i.e. any modern browser). Windows users are probably better off using the # HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can # further fine-tune the look of the index. As an example, the default style # sheet generated by doxygen has an example that shows how to put an image at # the root of the tree instead of the PROJECT_NAME. Since the tree basically has # the same information as the tab index, you could consider setting # DISABLE_INDEX to YES when enabling this option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_TREEVIEW = NO # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that # doxygen will group on one line in the generated HTML documentation. # # Note that a value of 0 will completely suppress the enum values from appearing # in the overview section. # Minimum value: 0, maximum value: 20, default value: 4. # This tag requires that the tag GENERATE_HTML is set to YES. ENUM_VALUES_PER_LINE = 4 # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used # to set the initial width (in pixels) of the frame in which the tree is shown. # Minimum value: 0, maximum value: 1500, default value: 250. # This tag requires that the tag GENERATE_HTML is set to YES. TREEVIEW_WIDTH = 250 # If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to # external symbols imported via tag files in a separate window. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. EXT_LINKS_IN_WINDOW = NO # If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg # tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see # https://inkscape.org) to generate formulas as SVG images instead of PNGs for # the HTML output. These images will generally look nicer at scaled resolutions. # Possible values are: png (the default) and svg (looks nicer but requires the # pdf2svg or inkscape tool). # The default value is: png. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FORMULA_FORMAT = png # Use this tag to change the font size of LaTeX formulas included as images in # the HTML documentation. When you change the font size after a successful # doxygen run you need to manually remove any form_*.png images from the HTML # output directory to force them to be regenerated. # Minimum value: 8, maximum value: 50, default value: 10. # This tag requires that the tag GENERATE_HTML is set to YES. FORMULA_FONTSIZE = 10 # Use the FORMULA_TRANSPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are not # supported properly for IE 6.0, but are supported on all modern browsers. # # Note that when changing this option you need to delete any form_*.png files in # the HTML output directory before the changes have effect. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. FORMULA_TRANSPARENT = YES # The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands # to create new LaTeX commands to be used in formulas as building blocks. See # the section "Including formulas" for details. FORMULA_MACROFILE = # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see # https://www.mathjax.org) which uses client side JavaScript for the rendering # instead of using pre-rendered bitmaps. Use this if you do not have LaTeX # installed or if you want to formulas look prettier in the HTML output. When # enabled you may also need to install MathJax separately and configure the path # to it using the MATHJAX_RELPATH option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. USE_MATHJAX = NO # When MathJax is enabled you can set the default output format to be used for # the MathJax output. See the MathJax site (see: # http://docs.mathjax.org/en/latest/output.html) for more details. # Possible values are: HTML-CSS (which is slower, but has the best # compatibility), NativeMML (i.e. MathML) and SVG. # The default value is: HTML-CSS. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_FORMAT = HTML-CSS # When MathJax is enabled you need to specify the location relative to the HTML # output directory using the MATHJAX_RELPATH option. The destination directory # should contain the MathJax.js script. For instance, if the mathjax directory # is located at the same level as the HTML output directory, then # MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax # Content Delivery Network so you can quickly see the result without installing # MathJax. However, it is strongly recommended to install a local copy of # MathJax from https://www.mathjax.org before deployment. # The default value is: https://cdn.jsdelivr.net/npm/mathjax@2. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_RELPATH = https://cdn.jsdelivr.net/npm/mathjax@2 # The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax # extension names that should be enabled during MathJax rendering. For example # MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_EXTENSIONS = # The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces # of code that will be used on startup of the MathJax code. See the MathJax site # (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an # example see the documentation. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_CODEFILE = # When the SEARCHENGINE tag is enabled doxygen will generate a search box for # the HTML output. The underlying search engine uses javascript and DHTML and # should work on any modern browser. Note that when using HTML help # (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) # there is already a search function so this one should typically be disabled. # For large projects the javascript based search engine can be slow, then # enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to # search using the keyboard; to jump to the search box use + S # (what the is depends on the OS and browser, but it is typically # , /


GammaRay Qt-application inspection and manipulation tool
https://www.kdab.com/development-resources/qt-tools/gammaray/
$generatedby doxygen $doxygenversion
gammaray-2.11.3/docs/api/kdab-gammaray-logo-16x16.png000066400000000000000000000011001413310443000220020ustar00rootroot00000000000000PNG  IHDRabKGD pHYs;;̶tIME&( E|IDAT8˕AHAٙݨP4V<zlMHEz(`5x*X &PZCAA-{TtӃΎi1}ȳ H]ٞbQp+P -P?߃c-Qh[qo)A0'F2B+cٷ=>ntnt\P=0cPF)ӊ(S_P5=\ߙ;7s~']2r}=<پnhn᫥< wvے5Z2(t0 ,gL oTogWj~X2cWCU>~ZU4 )M͜cR[ 8P+|QRfTPsD^ ^BJ qr)PVLa o@ճ]Aa9>$Oh!O _IENDB`gammaray-2.11.3/docs/api/kdab-logo-16x16.png000066400000000000000000000022351413310443000202200ustar00rootroot00000000000000PNG  IHDR gAMA a cHRMz&u0`:pQ<1PLTEuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuttuttttuututttuuuuu ~" ?3 { {/u+D'uuuuPQSY8WOBX {uux |w}~# z zs |$~uuuutttuuutttuuuutttuu@1m1tRNS{||}}}͍SbKGDG pHYs+tIME 2gammaray-api.qch
") else() set(apidocs_qch_file ${manual_qch_file}) # trick to make the copy command valid below set(apidocs_qch_target ${manual_qch_target}) set(API_DOCS_QCH_FILE_TAG "") endif() configure_file(${CMAKE_CURRENT_SOURCE_DIR}/gammaray.qhcp.cmake ${CMAKE_CURRENT_BINARY_DIR}/gammaray.qhcp) add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/gammaray.qhc COMMAND ${CMAKE_COMMAND} -E copy ${manual_qch_file} ${CMAKE_CURRENT_BINARY_DIR} COMMAND ${CMAKE_COMMAND} -E copy ${apidocs_qch_file} ${CMAKE_CURRENT_BINARY_DIR} COMMAND Qt5::qcollectiongenerator ${CMAKE_CURRENT_BINARY_DIR}/gammaray.qhcp -o ${CMAKE_CURRENT_BINARY_DIR}/gammaray.qhc DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/gammaray.qhcp.cmake ${manual_qch_target} ${apidocs_qch_target} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} ) add_custom_target(gammaray_qhc ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/gammaray.qhc) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/gammaray.qhc DESTINATION ${QCH_INSTALL_DIR}) gammaray-2.11.3/docs/collection/about.txt000066400000000000000000000011101413310443000203140ustar00rootroot00000000000000GammaRay

The Qt application inspection and manipulation tool. Learn more at https://www.kdab.com/gammaray/.

Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com

GammaRay and the GammaRay logo are registered trademarks of Klarälvdalens Datakonsult AB in the European Union, the United States and/or other countries. Other product and company names and logos may be trademarks or registered trademarks of their respective companies.

gammaray-2.11.3/docs/collection/gammaray.qhcp.cmake000066400000000000000000000024371413310443000222100ustar00rootroot00000000000000 GammaRay Help @CMAKE_SOURCE_DIR@/ui/resources/gammaray/GammaRay-32x32.png KDAB/GammaRay qthelp://com.kdab.gammaray.@GAMMARAY_PLUGIN_VERSION@/doc/index.html qthelp://com.kdab.gammaray.@GAMMARAY_PLUGIN_VERSION@/doc/index.html About GammaRay... Über Gammaray... @CMAKE_CURRENT_SOURCE_DIR@/about.txt @CMAKE_SOURCE_DIR@/ui/resources/gammaray/GammaRay-128x128.png false false false gammaray-manual.qch @API_DOCS_QCH_FILE_TAG@ gammaray-2.11.3/docs/man/000077500000000000000000000000001413310443000150705ustar00rootroot00000000000000gammaray-2.11.3/docs/man/CMakeLists.txt000066400000000000000000000013771413310443000176400ustar00rootroot00000000000000if(UNIX) find_program(POD2MAN_EXECUTABLE pod2man) gammaray_add_dummy_package(pod2man POD2MAN_EXECUTABLE) set_package_properties(pod2man PROPERTIES TYPE RECOMMENDED DESCRIPTION "Man page generator" PURPOSE "Generate GammaRay man pages." ) if(POD2MAN_EXECUTABLE) add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/gammaray.1 COMMAND ${POD2MAN_EXECUTABLE} -c "KDAB" -r "\"${GAMMARAY_VERSION}\"" -s 1 ${CMAKE_CURRENT_SOURCE_DIR}/gammaray.pod ${CMAKE_CURRENT_BINARY_DIR}/gammaray.1 DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/gammaray.pod ) add_custom_target(man ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/gammaray.1) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/gammaray.1 DESTINATION ${MAN_INSTALL_DIR}) endif() endif() gammaray-2.11.3/docs/man/gammaray.pod000066400000000000000000000124721413310443000174000ustar00rootroot00000000000000=head1 NAME GammaRay - Qt-application inspection and manipulation tool. =head1 SYNOPSIS gammaray [--pid | | --connect [:] =head1 DESCRIPTION GammaRay inspects runtime internals of a Qt-application, such as: Object tree, properties, signal/slots, widgets, models, graphics views, javascript debugger, resources, state machines, meta types, fonts, codecs, text documents When run without any options, gammaray will present a list of running Qt-applications from which you can attach the selected injector. Else, you can attach to a running process by specifying its pid, or you can start a new Qt-application by specifying its name (and optional arguments). =head1 OPTIONS =over 4 =item B<--help> Print help message and exit. =item B<--version> Print version information and exit. =item B<-p, --pid > Attach to running Qt application with the specified pid. =item B<-i, --injector > Set the specified injection type. Supported injectors are: preload (Linux, Mac OS) gdb (Linux. requires gdb to be installed) lldb (Linux. Mac OS, requires lldb to be installed) style windll (Windows) =item B<--inprocess> Use the Gammaray 1.x in-process UI. This is not necessary in most cases, apart from using tools that do not work remotely. =item B<--inject-only> This will only inject the GammaRay probe into a process, but not start the GammaRay UI. This is useful for example when doing remote debugging on embedded devices. =item B<--listen
> Specify on which network address the GammaRay server should listen, default is GAMMARAY_DEFAULT_ANY_TCP_URL (ie. tcp://0.0.0.0, all of ipv4, use tcp://[::] for all ipv6). This can be used for example on Windows to avoid firewall warnings by setting the address to 127.0.0.1 if you don't need remote access. =item B<--no-listen> Disables the GammaRay server. This implies --inprocess as there is no other way to connect to the GammaRay probe in this case. =item B<--list-probes> List all installed probes. =item B<--probe > Explicitly specify which probe to use. You should use this if you have more than one probe installed (e.g. one for Qt4 and one for Qt5), until probe ABI auto-detection is implemented. =item B<--connect [:port]> Connect to a target with an already injected GammaRay probe. Useful for example for remote debugging. =item B<--self-test [injector]> Run GammaRay self-tests, if an injector is specified only that specific one will be tested. The exit code is 0 on success, and 1 otherwise. =back =head1 EXAMPLES =over 4 =item Run gammaray on the Qt-application C with command line arguments 'arg1' and 'arg2': % gammaray /path/to/qtfoo arg1 arg2 =item Invoke gammaray on the Qt-application running with pid 1234: % gammaray --pid 1234 =item Invoke gammaray on with injector gdb on Qt-application running with pid 1234: % gammaray --injector gdb --pid 1234 =back =head1 EXIT STATUS Exits with 0 under normal operation; otherwise, exists with a positive integer indicating a problem occurred while launching or attaching an injector. =head1 COPYRIGHT Copyright (C) 2010-2021 Klaralvdalens Datakonsult AB, a KDAB Group company, Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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. StackWalker code Copyright (c) 2005-2019, Jochen Kalmbach, All rights reserved. lz4 fast LZ compression code Copyright (C) 2011-2015, Yann Collet, All rights reserved. backward-cpp code Copyright 2013-2017 Google Inc. All rights reserved. =head1 AUTHORS Allen Winter Andreas Holzammer Anton Kreuzkamp BogDan Vatra Dan Vratil David Faure Filipe Azevedo Frank Osterfeld James Turner Jan Dalheimer Kevin Funk Laurent Montel Mathias Hasselmann Milian Wolff Nicolas Arnaud-Cormos Patrick Spendrin Peter Kuemmel Rafael Roquetto Sergio Martins Stephen Kelly Thomas McGuire Till Adam Tobias Koenig Volker Krause =cut gammaray-2.11.3/docs/manual/000077500000000000000000000000001413310443000155725ustar00rootroot00000000000000gammaray-2.11.3/docs/manual/CMakeLists.txt000066400000000000000000000077221413310443000203420ustar00rootroot00000000000000macro(qt5_build_doc _qdocconf_name) #compute the qdoc template dir from where the qt-html-templates-offline.qdocconf was found get_filename_component(QDOC_TEMPLATE_DIR ${QDOC_TEMPLATE} DIRECTORY) get_filename_component(QDOC_TEMPLATE_DIR ${QDOC_TEMPLATE_DIR} DIRECTORY) #compute the qdoc index dir from where the qtcore.index was found get_filename_component(QDOC_INDEX_DIR ${QDOC_QTCORE_INDEX} DIRECTORY) get_filename_component(QDOC_INDEX_DIR ${QDOC_INDEX_DIR} DIRECTORY) # run the attribution scanner to collect 3rdparty license information file(GLOB_RECURSE _qt_attributions "${CMAKE_SOURCE_DIR}/3rdparty/*/qt_attribution.json") add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/codecontributions.qdoc COMMAND ${QTATTRIBUTIONSSCANNER_EXECUTABLE} --output ${CMAKE_CURRENT_BINARY_DIR}/codecontributions.qdoc --filter QDocModule=gammaray-manual ${CMAKE_SOURCE_DIR} DEPENDS ${_qt_attributions} ${CMAKE_CURRENT_SOURCE_DIR}/${_qdocconf_name}-offline.qdocconf ) # offline docs # run qdoc to get qhp file set(_qdoc_output_dir ${CMAKE_CURRENT_BINARY_DIR}/${_qdocconf_name}) # FIXME: this should rather be taken from the qdocconf file? file(GLOB_RECURSE _qdoc_srcs ${CMAKE_CURRENT_SOURCE_DIR} "*.qdoc") file(GLOB _qdoc_imgs ${CMAKE_CURRENT_SOURCE_DIR} "images/*.png") list(APPEND _qdoc_srcs "${CMAKE_CURRENT_BINARY_DIR}/codecontributions.qdoc") add_custom_command( OUTPUT ${_qdoc_output_dir}/${_qdocconf_name}.qhp COMMAND ${CMAKE_COMMAND} -E env QT_INSTALL_DOCS=${QDOC_TEMPLATE_DIR} QT_VERSION_TAG=${GAMMARAY_PLUGIN_VERSION} QT_VERSION=${GAMMARAY_VERSION} BUILDDIR=${CMAKE_CURRENT_BINARY_DIR} ${QDOC_EXECUTABLE} --indexdir ${QDOC_INDEX_DIR} --outputdir ${_qdoc_output_dir} ${CMAKE_CURRENT_SOURCE_DIR}/${_qdocconf_name}-offline.qdocconf DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${_qdocconf_name}-offline.qdocconf ${_qdoc_srcs} ${_qdoc_imgs} ) # generate qch file from qhp add_custom_command( OUTPUT ${_qdoc_output_dir}/${_qdocconf_name}.qch COMMAND ${QHELPGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/${_qdocconf_name}/${_qdocconf_name}.qhp DEPENDS ${_qdoc_output_dir}/${_qdocconf_name}.qhp ) add_custom_target(${_qdocconf_name}.qch ALL DEPENDS ${_qdoc_output_dir}/${_qdocconf_name}.qch) # install qch install(FILES ${_qdoc_output_dir}/${_qdocconf_name}.qch DESTINATION ${QCH_INSTALL_DIR}) # online docs file(GLOB _qdoc_styles ${CMAKE_CURRENT_SOURCE_DIR} "style/*") # Qt style set(_qdoc_output_dir ${CMAKE_CURRENT_BINARY_DIR}/${_qdocconf_name}-online) add_custom_command( OUTPUT ${_qdoc_output_dir}/index.html COMMAND ${CMAKE_COMMAND} -E env QT_INSTALL_DOCS=${QDOC_TEMPLATE_DIR} QT_VERSION_TAG=${GAMMARAY_PLUGIN_VERSION} QT_VERSION=${GAMMARAY_VERSION} BUILDDIR=${CMAKE_CURRENT_BINARY_DIR} ${QDOC_EXECUTABLE} --indexdir ${QDOC_INDEX_DIR} --outputdir ${_qdoc_output_dir} ${CMAKE_CURRENT_SOURCE_DIR}/${_qdocconf_name}-online.qdocconf DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${_qdocconf_name}-online.qdocconf ${_qdoc_srcs} ${_qdoc_imgs} ${_qdoc_styles} ) add_custom_target(online-docs DEPENDS ${_qdoc_output_dir}/index.html) # KDAB style set(_qdoc_output_dir ${CMAKE_CURRENT_BINARY_DIR}/${_qdocconf_name}-online-kdab) add_custom_command( OUTPUT ${_qdoc_output_dir}/index.html COMMAND ${CMAKE_COMMAND} -E env QT_INSTALL_DOCS=${QDOC_TEMPLATE_DIR} QT_VERSION_TAG=${GAMMARAY_PLUGIN_VERSION} QT_VERSION=${GAMMARAY_VERSION} BUILDDIR=${CMAKE_CURRENT_BINARY_DIR} ${QDOC_EXECUTABLE} --indexdir ${QDOC_INDEX_DIR} --outputdir ${_qdoc_output_dir} ${CMAKE_CURRENT_SOURCE_DIR}/${_qdocconf_name}-online-kdab.qdocconf DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${_qdocconf_name}-online-kdab.qdocconf ${_qdoc_srcs} ${_qdoc_imgs} ${_qdoc_styles} ) add_custom_target(online-docs-kdab DEPENDS ${_qdoc_output_dir}/index.html) endmacro() qt5_build_doc(gammaray-manual) gammaray-2.11.3/docs/manual/examples/000077500000000000000000000000001413310443000174105ustar00rootroot00000000000000gammaray-2.11.3/docs/manual/examples/qt3d-geometry.qdoc000066400000000000000000000062011413310443000227630ustar00rootroot00000000000000/* qt3d-geometry.qdoc This file is part of the GammaRay documentation. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. This work is also licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. See . */ /*! \example qt3d-geometry \title Qt3D Geometry \brief Geometry data inspection. \ingroup examples-gammaray This examples shows GammaRay's capabilities for inspecting \l{Qt 3D} vertex and index attribute data. \section1 Problem The example shows a three dimensional cylinder using \l{Qt 3D}. It's implemented as a custom Qt3DRender::QGeometry sub-class and computes the cylinder geometry, surface normals and vertex indexes. Looking at the rendered result we notice that one side of the cylinder is not rendered correctly, and on closer look we also see that the phong shading doesn't match the expected light reflections on a cylinder \section1 Investigation GammaRay provides several features supporting the analysis of issues regarding vertex data provided via Qt3DRender::QAttribute and Qt3DRender::QBuffer. They are all available by opening the \l{Qt3D Inspector} view, selecting a geometry entity in the entity tree view on the left side and then opening the \l{Qt3D Geometry Inspector} tab on the right side. \section2 Backface culling A common problem for seemingly "missing" parts of the geometry is a wrong vertex order. The \l{Qt3D Geometry Inspector} in GammaRay allows you to disable the normally active backface culling to easily spot such cases. You find the action for that in the toolbar above the geometry view. Back faces of triangles are rendered in red with culling disabled. Rotating the cylinder in our example using the middle mouse button so you can see the bottom disc will show that the triangles are indeed the wrong way around, and thus aren't rendred. \snippet qt3d-geometry/mycylinder.cpp Wrong bottom disc indexes \section2 Normal vector visualization With the geometry fixed, we can look into the shading issues. Light reflection in the phong model largely depends on surface normal vectors, which are also provided via a Qt3DRender::QAttribute in our example. Normals can be visualized with the \l{Qt3D Geometry Inspector} by choosing the corresponding action in the toolbar above the geometry view. The resulting blue lines show the vertex normals, the yellow lines the corresponding face normals. In our case we can easily see that the normals for the side faces are bent towards the center line of the cylinder, that is their y value isn't 0 for all vertices as it is supposed to be. \snippet qt3d-geometry/mycylinder.cpp Wrong side normals */ gammaray-2.11.3/docs/manual/examples/quick-batching.qdoc000066400000000000000000000131631413310443000231550ustar00rootroot00000000000000/* quick-batching.qdoc This file is part of the GammaRay documentation. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. This work is also licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. See . */ /*! \example quick-batching \title Qt Quick Batching \brief Investiagte render performance issues due to unmerged render batches. \ingroup examples-gammaray This examples shows GammaRay's capabilities for analyzing \l{Qt Quick} batch rendering issues. \section1 Problem The example application shows two custom Qt Quick sliders consisting of flat colored rectangles forming a gradient from green to blue. Inactive rectangles (that is rectangles above the handle) are drawn smaller and slightly grayed out. \snippet quick-batching/quick-batching.qml Slider setup Observed in a OpenGL tracing tool or profiler, it can be seen however that the two sliders take several hundred OpenGL calls to be rendered. Closer investigation shows that each rectangle results in a separate OpenGL draw call. \section1 Investigation There are several aspects of this problem that can be analyzed with GammaRay. \section2 Scene graph render diagnostic messages The \l{Qt Quick} renderer has some built-in diagnostic messages that can sometimes be helpful in such a scenario. Turning them on unconditionally can be inconvenient though, as they are usually triggered by frame. It is therefore useful to only enable them for a short amount of time while triggering the relevant operation in the target application. This can be done using the GammaRay \l{Messages} tool. Open the Logging tab, find debug categories you are interested in (qt.scenegraph.* or qt.quick.* for example), and enable the checkbox in the Debug column. \section2 Scene graph render visualziation The \l{Qt Quick} renderer also has a number of built-in diagnostic visualizations. Those can be usually only enabled at application startup. This is inconvenient however if you first need to navigate to the relevant part of your application to investigate a specific issue. The GammaRay \l{Qt Quick 2 Inspector} allows to enable these diagnostic render modes at runtime too (only available in some Qt versions). The toolbar above the scene view in the lower left part of the Qt Quick inspector view has toggle actions for them. In our example, the batch rendering visualization is particularly interesting. Areas of the same color indicate batches of elements that are rendered in a single draw call. This is the optimal scenario we are trying to achieve. Areas with diagonal lines represent sets of items that have some Common state but are rendered by individual draw calls. The scene graph renderer calls this case "unmerged batch". In our example one observes a colorful output with all inactive elements in the sliders in their own colors. \section2 Scene graph inspection After having established that our problem comes from rectangles not being batched in the scene graph renderer, we need to investigate what prevents batching in our case. Items for example can't be batched when they use clipping or differ in their opacity. For Qt versions < 5.8 batching is also disabled if non-opaque items overlap, or when Items have a non-trivial transformation matrix. For this we use the scene graph view of the \l{Qt Quick 2 Inspector} view in GammaRay. It shows the internal scene graph items, which is what the renderer uses as input for the batching. Comparing the subtrees belonging to active and inactive rectangles scenegraph-node tree view, we notice that the inactive ones have an Opacity node, while the active ones don't. The opacity node indicates transparency. Now we can check the opacity property in the \l {Properties} tab of the Item view, so we switch back to "Items". The Item corresponding to the previously selected Opacity node is already pre-selected, so we only need to look for the property. In fact we find it to have value < 1. In order to match this to the actual code causing the problem, use the context menu action "Go to declaration", which will open a code editor around the following piece of code: \snippet quick-batching/Slider.qml Slider delegate In fact we see that the opacity value is set to a value depending on the index. That means that all rectangles get a different opacity value and that rules out batching. In order to verify that this is actually our problem you don't need to restart the application, you can also use the editing capabilities of the \l{Properties} tab in the item tree view to adjust the opacity live. If you still have the batch rendering visualization activated, after setting the opacity to 1, you'll observe how the selected rectangle gets the same color as all the active rectangles. Just removing the opacity is of course not a proper fix, it merely verifies that it's these opacities causing our performance problem. To retain the same visual appearance as with opacity set, you could e.g. use the alpha component of the color property to obtain the transparency, without impairing the batching. */ gammaray-2.11.3/docs/manual/examples/quick-event-handling.qdoc000066400000000000000000000051121413310443000242740ustar00rootroot00000000000000/* quick-event-handling.qdoc This file is part of the GammaRay documentation. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. This work is also licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. See . */ /*! \example quick-event-handling \title Qt Quick Event Handling \brief Investigate issues regarding Qt Quick input events. \ingroup examples-gammaray This examples shows GammaRay's capabilities for analyzing \l{Qt Quick} event handling issues. \section1 Problem The example application shows a simple Qt Quick button. Clicking the button is supposed to emit a debug message acknowledging the click. \snippet quick-event-handling/quick-event-handling.qml Button setup However, you can observe that this only works on the left side of the button, on the right side clicks have no effect. \section1 Investigation There are several aspects of this problem that can be analyzed with GammaRay. \section2 Qt Quick element picking Open the \l{Qt Quick 2 Inspector} and select the element picking tool above the scene view on the lower left. Clicking on to the view will select the element at that location. You can notice that picking on the left side of the button will select the button as expected (or one of its internal elements), but picking on the right side selects a second, invisible button. \snippet quick-event-handling/quick-event-handling.qml Hidden button This hidden button consumes the events and thus breaks our example. \section2 Live property editing Selecting the hidden button in the \l{Qt Quick 2 Inspector} or \l{Object Browser} tools will show the \l{Properties} view on the right. In there we can investigate why the hidden button consumes events, and test our theory by changing properties in the running application. In our case we will notice that the button is not actually hidden (the visible property is still true), it is merely set to be fully transparent (the opacity property is 0). If we change its visible property to false, our example works as expected, that is clicks on the right half of the button work as well. */ gammaray-2.11.3/docs/manual/examples/signal-slot.qdoc000066400000000000000000000056121413310443000225200ustar00rootroot00000000000000/* signal-slot.qdoc This file is part of the GammaRay documentation. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. This work is also licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. See . */ /*! \example signal-slot \title Signal/Slot Connections \brief Investiagte issues regarding signal/slot connection handling. \ingroup examples-gammaray This examples shows GammaRay's capabilities in analyzing signal/slot connection problems. \section1 Problem The application shows two buttons and a LCD widget. The LCD widget is connected to the clicked() signal of the first button and increments every time the clicked() signal is emitted. The second button recreates the connection. Clicking the reconnect button multiple times however breaks things, so that clicking the emit button will result in more than on increment of the LCD widget. \snippet signal-slot/signal-slot.cpp Widget setup \section1 Investigation There are several aspects of this problem that can be analyzed with GammaRay. \section2 Watch a signal emission Select the emit button in the \l{Object Browser} or the \l{Widget Inspector} tool, and open the \l{Methods} tab on the right. Find the clicked() signal in the method list, and chose \uicontrol{Connect to} in the context menu. Clicking on the emit button will now show each emitted clicked() signal in the method log at the bottom half on the right side. This allows you to verify that the signal is emitted correctly, and here in particular is only emitted once per click. Alternatively, you can also observe this in the \l{Signal Plotter} view. \section2 Check existing signal/slot connections Select the emit button in the \l{Object Browser} or the \l{Widget Inspector} tool, and open the \l{Connections} tab on the right. The list of outbound connections at the bottom half will show all existing connections to the LCD widget incrementing code. You will notice that multiple connections to the same slot exist, and that GammaRay shows a warning icon for this. Looking at the reconnection slot we then see that the code mixes string based and function pointer based connect()/disconnect() calls, which is not supported by QObject: \snippet signal-slot/signal-slot.cpp Mismatching reconnect This code therefore adds another connection each time its called, resulting in multiple increments in the LCD widget per emit button click. */ gammaray-2.11.3/docs/manual/examples/state-machine.qdoc000066400000000000000000000034001413310443000227770ustar00rootroot00000000000000/* state-machine.qdoc This file is part of the GammaRay documentation. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. This work is also licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. See . */ /*! \example state-machine \title State Machines \brief Debug state machine runtime behavior. \ingroup examples-gammaray This examples shows GammaRay's capabilities in analyzing state machine behavior at runtime. \section1 Problem The example application shows a simple traffic light with an on/off switch that is driven by a declarative StateMachine. We can observe that during its normal cycle it transitions from green directly to red, rather than going via yellow as one would expect. \section1 Investigation While the target application is running, open the \l{State Machine Debugger} view in GammaRay to observe the state machine layout, active configuration and configuration changes. It is easily visible that the green state is not transitioning to the greenToRed state as it is supposed to, but the red state directly. In order to fix this, right click on the green state, and select "Go to creation", which will open your code editor at the right spot in the corresponding source file. \snippet state-machine/state-machine.qml Wrong transition */ gammaray-2.11.3/docs/manual/examples/timer.qdoc000066400000000000000000000062051413310443000214030ustar00rootroot00000000000000/* timer.qdoc This file is part of the GammaRay documentation. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. This work is also licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. See . */ /*! \example timer \title Timer \brief Analyze out of control timers. \ingroup examples-gammaray This examples shows GammaRay's capabilities in analyzing timer activity. \section1 Problem The example application shows two buttons, clicking any one of them is supposed to trigger a deferred repaint of the application using two variations of a single-shot timer. In both cases we however observe 100% CPU load afterwards. Analyzing the CPU load with a debugger or profiler points to frequently firing timers, but doesn't support us in finding where that timer actually came from. \section1 Investigation While the target application is running, open the \l{Timers} view in GammaRay to obtain a list of all active timers. Sorting by \uicontrol{Total Wakeups} or \uicontrol{Wakeups/Sec} allows to quickly identify timers running amok, such as the recurring 0-interval timers in our example. When working with a debug build, the context menu on the \l{Timers} view allows you directly to navigate to the source code of the created timer object, or timer event receiver. \section2 QTimer and QML Timer When using timer objects such as QTimer or a QML Timer, you can further inspect those by switching to the \l{Object Browser} via the context menu in the \l{Timers} view. In the object browser the following inspection views are particularly useful for timer objects: \list \li The \l{Connections} view allows you to inspect to which action the timeout() signal is connected to, which can help to identify the timer if source navigation is not available. \li The \l{Methods} view allows to manually stop timers, to verify a specific timer is the source of a problem, or to observe if it gets erroneously restarted. \endlist In the above example, we notice that the QTimer::singleShot property was accidentally left on false, causing the timer to fire constantly. \snippet timer/timer.cpp Missing setSingleShot The \l{Signal Plotter} view of GammaRay can provide additional help in finding too frequently triggering timer object, by means of observing their timeout signals. \section2 QTimerEvent Raw timer events are less commonly used than timer object and provide fewer options for runtime diagnostics unfortunately. In our example the source of the problem is a missing QObject::killTimer call in the QTimerEvent handler. \snippet timer/timer.cpp Missing killTimer */ gammaray-2.11.3/docs/manual/examples/widget-layouting.qdoc000066400000000000000000000056651413310443000235700ustar00rootroot00000000000000/* widget-layouting.qdoc This file is part of the GammaRay documentation. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. This work is also licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. See . */ // TODO: expand this example to also include size policy issues, but for that we need to // first be able to actually edit the size policies... /*! \example widget-layouting \title Widget Layouting \brief Analyze widget layouting behavior. \ingroup examples-gammaray This examples shows GammaRay's capabilities in analyzing widget layouting issues. \section1 Problem The widget layouting example application shows a complex dialog window that cannot be resized as small as one would expect. \section1 Investigation GammaRay supports several approaches of analyzing widget layouting issues. All of them revolve around the \l{Widget Inspector}. \section2 In-app widget picking You can select the widget occupying a specific screen pixel by clicking on the area you are interested in the target applicationi while holding Ctrl+Shift. This will select the top-most QWidget occupying this space. This is particularly useful for identifying the "owner" of seemingly empty space. In our example this can be used to identify the QLabel on the lower left that has an unnecessarily high minimum size set. \section2 Property inspection For the selected QWidget or QLayout instance you can look at and edit many layouting-relevant properties in the \l{Properties} view on the right. Tweaking these values at runtime avoids lengthy recompile and restart cycles just to verify a specific change. In our example, we can change the minimum size of the offending QLabel in the lower left to something more reasonable and immediately verify that the window resizing has improved. \section2 Bisection layouting problems A useful approach for identifying more complicated layouting issues is bisecting them, that is, take our parts of the UI and verify if the problem persists. You can do this at runtime with GammaRay too, without needing to recompile or restart the target application, by setting the relevant QWidget sub-tree to invisible in the \l{Properties} view. In our example, we can use this to exclude the QGroupBox instances at the right side as a possible source for the window resizing issue. Hiding one of them makes it a lot more apparent that the actual source of the problem is the QLabel on the lower left. */ gammaray-2.11.3/docs/manual/external-resources.qdoc000066400000000000000000000016751413310443000223050ustar00rootroot00000000000000/* external-resources.qdoc This file is part of the GammaRay documentation. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. This work is also licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. See . */ /*! \externalpage https://doc.qt.io/QtAutomotiveSuite \title Qt Automotive Suite */ /*! \externalpage https://www.kdab.com/ \title KDAB */ /*! \externalpage https://www.cmake.org/ \title CMake */ /*! \externalpage https://doc.qt.io/qtcreator \title Qt Creator */ gammaray-2.11.3/docs/manual/gammaray-action-inspector.qdoc000066400000000000000000000025351413310443000235240ustar00rootroot00000000000000/* gammaray-action-inspector.qdoc This file is part of the GammaRay documentation. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. This work is also licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. See . */ /*! \contentspage {Tools} \nextpage {Models} \previouspage {Object Browser} \page gammaray-action-inspector.html \title Actions \section1 Overview The action inspector shows all QAction instances that exist in the target application. Information about the display name, checkable states, and shortcuts with their priorities are displayed. \image gammaray-action-inspector.png The action inspector warns about shortcut conflicts (that is, two QAction instances using the same shortcut sequence and thus making it ambiguous) with a warning icon in its shortcut column. Actions can also be activated remotely by double-clicking the corresponding entry. */ gammaray-2.11.3/docs/manual/gammaray-advanced-usage.qdoc000066400000000000000000000064561413310443000231200ustar00rootroot00000000000000/* gammaray-advanced-usage.qdoc This file is part of the GammaRay documentation. Copyright (C) 2018-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. This work is also licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. See . */ /*! \contentspage {GammaRay User Manual} \previouspage {Examples} \nextpage {Licenses and Attributions} \page gammaray-advanced-usage.html \title Advanced Usage \section1 Integrating the GammaRay Probe into an Application In some scenarios it might be desirable to not use GammaRay's dynamic injection mechanism, but to build the GammaRay probe into the target application at compile time already. Static linking is one such use-case, as dynamic injection doesn't work then. There are two ways available to integrate the probe into a target application: \list \li Dynamic linking to the GammaRay probe DLL. This is useful for example for use with application bundles on platforms that allow dynamic linking (e.g. on Android). GammaRay provides the probe DLL as link tarket for qmake and CMake based build systems. You need to ensure to deploy all GammaRay DLLs and probe plugins too in this scenario. \li Static linking to the GammaRay probe and all relevant plugins. This is a more complex setup but your only option when using a statically linked Qt. Compile GammaRay with the \c -DGAMMARAY_STATIC_PROBE=ON CMake option, add the \c GAMMARAY_STATIC_INJECT macro to your application code and link the application against the GammaRay probe and all relevant plugins. See \c tests/manual/staticinjectiontest in the GammaRay source code for an example. \endlist Be sure to review the licensing terms before distributing an application with GammaRay integrated permanently, in particular when using the open source version of GammaRay. \section1 Creating Custom GammaRay Plug-ins GammaRay can be extended with custom plugins. The main extension points for plugins are: \list \li Add entirely new \l{Tools}, for example for additional frameworks. \li Add additional tabs to the \l{Object Inspection} view. This is most useful for inspecting aspects of objects shown in multiple tools. \li Extend the \l{Properties} view by additional means to retrieve object properties. \endlist For more details on implementing GammaRay plugins, please refer to the \l{https://docs.kdab.com/gammaray/latest/}{API documentation}. \section1 Integrating the GammaRay Client into an IDE The \l{GammaRay Client} can be integrated into integrated development environments (IDE). Such an integration is available for \l{Qt Creator} as part of the \l{Qt Automotive Suite}. For more details on how to integrate the GammaRay client with and IDE, please see the \l{https://docs.kdab.com/gammaray/latest/}{API documentation}. */ gammaray-2.11.3/docs/manual/gammaray-application-attributes.qdoc000066400000000000000000000022341413310443000247260ustar00rootroot00000000000000/* gammaray-application-attributes.qdoc This file is part of the GammaRay documentation. Copyright (C) 2018-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. This work is also licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. See . */ /*! \contentspage {GammaRay User Manual} \previouspage {Qt3D Geometry Inspector} \nextpage {HTTP Cookies} \page gammaray-application-attributes.html \title Application Attributes \section1 Overview The application attribute inspection tab allows you to see and toggle QCoreApplication attributes (see Qt::ApplicationAttribute). This is available when selecting a QCoreApplication instance in e.g. the \l{Object Browser}. \image gammaray-application-attributes.png */ gammaray-2.11.3/docs/manual/gammaray-basic-operations.qdoc000066400000000000000000000134761413310443000235130ustar00rootroot00000000000000/* gammaray-basic-operations.qdoc This file is part of the GammaRay documentation. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. This work is also licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. See . */ /*! \contentspage {Getting Started} \nextpage {Graphical Launcher} \previouspage {Installation} \page gammaray-basic-operations.html \title Basic Operations \section1 Launching a New Application \section2 On the command line Launching an application with GammaRay on the command line is similar to tools like valgrind: \code gammaray \endcode This will launch the application with GammaRay attached to it right from the start. The GammaRay arguments allow you to select a different injection method. While the default is suitable for most target applications, the simultaneous use of other tools using the same entry points as GammaRay might require using alternative injection methods for example. \l{Command Line Interface}{Read more...} \section2 With a graphical user interface Launching applications is also available in the graphical user interface. Launch GammaRay without command line arguments (or use the menu shortcut if you have one), select the \uicontrol Launch tab. There you can select the executable to launch as well as optional command line arguments to pass to it. \l{Graphical Launcher}{Read more...} \section2 With Qt Creator You can launch both local and remote applications from within \l{Qt Creator} with GammaRay via \uicontrol Analyze > \uicontrol {Start with GammaRay}. All environment and application settings are taken from your regular run configuration in Qt Creator. \l{Qt Creator Integration}{Read more...} \l{Qt Creator} integration is only available in the \l{Qt Automotive Suite}. \section2 On an embedded target If you want to run the GammaRay UI on a different machine, which is commonly the case for embedded targets, you can proceed as described in the command line section above, with the extra \c --inject-only argument passed to GammaRay. Then follow the instructions below on how to connect to a already running instance. \section1 Attaching to a Running Application \section2 On the command line Attaching to a running application on the command line is similar to gdb: \code gammaray --pid \endcode This will attach GammaRay to the application running with process id \c . Keep in mind that this process has to be a Qt application with matching build settings and Qt version for GammaRay to work. \l{Command Line Interface}{Read more...} \section2 With a graphical user interface Launch GammaRay without command line arguments (or use the menu shortcut if you have one) to bring up the GammaRay UI. It will show you a list of processes GammaRay can attach to, that is, those with the same build settings and Qt version as your GammaRay instance. You'll find the process list on the \uicontrol Attach tab. \l{Graphical Launcher}{Read more...} \section2 With Qt Creator You can attach GammaRay to already locally running applications from within \l{Qt Creator} using \uicontrol Analyze > \uicontrol {Attach GammaRay to a local running process}. Just as when attaching a regular debugger, Qt Creator will open a process selection dialog allowing you to pick which process to attach to. \l{Qt Creator Integration}{Read more...} \l{Qt Creator} integration is only available in the \l{Qt Automotive Suite}. \section2 On an embedded target If you want to run the GammaRay UI on a different machine, which is commonly the case for embedded targets, you can proceed as described in the command line section above, with the extra \c --inject-only argument passed to GammaRay. Then follow the instructions below on how to connect to a already running instance. \section1 Connecting to a Running Application \section2 On the command line Run the following command to connect to a running remote instance: \code gammaray --connect tcp://: \endcode Insert the IP address or host name of the target device, and if needed the port number accordingly. The port number can usually be omitted if there's only a single GammaRay instance active on the target. The port number to use is printed on the command line after injection as described above. \l{Command Line Interface}{Read more...} \section2 With a graphical user interface Launch GammaRay without any command line arguments to get to the launcher UI. Select the \uicontrol Connect tab. Depending on your network setup active targets will be already listed here and can simply be selected from the list. Manual entry of host name or IP address, and if necessary the port number is also possible. \l{Graphical Launcher}{Read more...} \section2 With Qt Creator You can connect to remotely running applications with GammaRay already injected from within \l{Qt Creator} using \uicontrol Analyze > \uicontrol{Connect GammaRay to a remote running process}. Qt Creator will then show all running instances discovered on the local network, as well as an option to manually specify a network address to connect to. \l{Qt Creator Integration}{Read more...} \l{Qt Creator} integration is only available in the \l{Qt Automotive Suite}. */ gammaray-2.11.3/docs/manual/gammaray-classinfo.qdoc000066400000000000000000000024461413310443000222250ustar00rootroot00000000000000/* gammaray-classinfo.qdoc This file is part of the GammaRay documentation. Copyright (C) 2018-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. This work is also licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. See . */ /*! \contentspage {GammaRay User Manual} \previouspage {Enums and Flags} \nextpage {Stack Trace} \page gammaray-classinfo.html \title Class Info \section1 Overview The class info view is available for all QObject and QMetaObject instances and shows per-class information defined in the corresponding QMetaClassInfo instance. \image gammaray-classinfo.png The class info view shows the following information: \list \li The name of the class information. \li The value of the class information. \li The class or namespace in which the class information has been declared. \endlist */ gammaray-2.11.3/docs/manual/gammaray-client.qdoc000066400000000000000000000106041413310443000215150ustar00rootroot00000000000000/* gammaray-client.qdoc This file is part of the GammaRay documentation. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. This work is also licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. See . */ /*! \contentspage {Getting Started} \nextpage {Qt Creator Integration} \previouspage {Graphical Launcher} \page gammaray-client.html \title GammaRay Client \section1 Overview After launcher a target application with GammaRay, or attaching or connecting to a running one, you are presented with the GammaRay client. This is the primary user interface for exploring what is going on inside the target application. The GammaRay client is available in two forms: \list \li as a standalone application as depicted in the following screenshot \li as \l{Qt Creator} plugin (for \l{Qt Automotive Suite} only) \endlist \image gammaray-object-inspector.png \section2 Tools GammaRay contains a number of different tools, each providing a different view on the activities and data of the target application. A tool typically focuses on a specific Qt framework such at \l{Qt Quick} or state machines, or model/view. Switching between tools is done via the tool list on the left side of the GammaRay client window. Only tools applicable to the current target applications are active. Inactive tools can be hidden via the context menu. An overview of all available tools can be found in the \l{Tools} section of this manual. \section2 Object Inspection Many tools in GammaRay deal with a set of objects of the target application, most commonly QObject sub-classes. To explore the properties of a selected object, those tools typically show the \l{Object Inspection} view on the right hand side (as seen in the above screenshot). The object inspection view contains a number of different tabs, depending on the type of the object. An overview of all available object inspection tabs can be found in the \l{Object Inspection} section of this manual. \section2 Object and Source Code Navigation Different tools might show different aspects of the same object, so it's useful to be able to navigate to the current object in other tools. This is offered in most places via the context menu. Equally, it is useful to navigate to the code that declared or instantiated an object, this is offered also via the context menu. This information is available under the following conditions: \list \li For objects created via QML you can navigate to the QML source line triggering the creation. \li For type declared in QML you can navigate to the QML source file containing the type. \li For objects created via C++ you can navigate to the C++ source code line creating the object given you have a debug build of the target code. \endlist If you are using GammaRay stand-alone (ie. not inside Qt Creator), you can configure the text editor to use for source code navigation via \uicontrol{Settings} > \uicontrol{Code Navigation}. Depending on your platform, a number of common pre-configured editors and IDEs can be selected. A custom editor command line can also be specified, using the following placeholders: \table \header \li Placeholder \li Content \row \li \c %f \li The full path to the source file. \row \li \c %l \li The line number. \row \li \c %c \li The column number. \endtable \section2 Finding Objects Finding the right objects in the many thousands existing in a target application typically can be challenging. GammaRay supports this by the following means: \list \li Search fields for most object tree or list views, to quickly filter by type, object name or other displayed properties. \li In-application object picking for visual elements, using Ctrl+Shift+left click on the target application. \endlist */ gammaray-2.11.3/docs/manual/gammaray-codec-browser.qdoc000066400000000000000000000026561413310443000230050ustar00rootroot00000000000000/* gammaray-codec-browser.qdoc This file is part of the GammaRay documentation. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. This work is also licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. See . */ /*! \contentspage {Tools} \previouspage {Network} \nextpage {System Information} \page gammaray-codec-browser.html \title Text Codecs \section1 Overview The text codec browser allows you to inspect QTextCodec instances available on the target system, and to see how a specific code converts a given QString. This information is usually independent of the target application, but rather depend on the system. \image gammaray-codec-browser.png The list on the left side contains all codecs known by QTextCodec, as well as their aliases. You can select one or more of these codecs for looking at their encoding results on the test string at the upper-right side. Encoding results are shown for all selected codecs on the right side. */ gammaray-2.11.3/docs/manual/gammaray-command-line.qdoc000066400000000000000000000111651413310443000226050ustar00rootroot00000000000000/* gammaray-command-line.qdoc This file is part of the GammaRay documentation. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. This work is also licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. See . */ /*! \contentspage {Gettings Started} \nextpage {Tools} \previouspage {Qt Creator Integration} \page gammaray-command-line.html \title Command Line Interface \section1 Overview All basic GammaRay operations such as launching a target application or attaching to a running one are also available from the command line interface. This is particularly useful when working with embedded devices. Additionally some specialized options to control GammaRay's injection behavior are only available via the command line interface. Launching a target application with GammaRay is done as follows: \code gammaray [gammaray args] [application args] \endcode When run without any arguments, \c gammaray will bring up the \l{Graphical Launcher}. Valid arguments for the \c gammaray command are described in the next section. \section1 Command Line Options \table \header \li Option \li Description \row \li \c --help \li Print command line options. \row \li \c --version \li Print the GammaRay version and the GammaRay protocol version. \row \li \c -p, \c --pid \c \li Attach to a running application with process id \c . \row \li \c -i, \c --injector \c \li Specify injector type to use (see below). \row \li \c --inprocess \li Use the Gammaray 1.x in-process UI. This is not necessary in most cases, apart from using tools that do not work remotely. \row \li \c --inject-only \li This will only inject the GammaRay probe into a process, but not start the \l{GammaRay Client}. This is useful for example when doing remote debugging on embedded devices. \row \li \c{--listen
} \li Specify on which network address the GammaRay server should listen on. This is useful when GammaRay is selecting the wrong network interface by default, or for restricting remote access in untrusted networks. \row \li \c --no-listen \li Disables the GammaRay server. This implies \c --inprocess as there is no other way to connect to the GammaRay probe in this case. \row \li \c --list-probes \li List all installed probes. \row \li \c{--probe } \li Explicitly specify which probe to use. This should only be needed when the automatic probe ABI detection fails, or on platforms that don't have probe ABI detection implemented yet. \row \li \c{--connect tcp://[:port]} \li Connect to a target with an already injected GammaRay probe. Useful for example for remote debugging. \row \li \c{--self-test [injector]} \li Runs the GammaRay self-tests, if an injector is specified only that specific one will be tested. The exit code is 0 on success, and 1 otherwise. \endtable \section2 Injector Types GammaRay contains various methods of injecting itself into a target process. By default it picks the most suitable method on its own, based on the current platform and properties of the target application. It can however be necessary to override the default, in particular if other tools are used at the same time that already occupy specific ways, or if the target application is incompatible with a specific injection method. The following injection methods are supported: \table \header \li Method \li Launch \li Attach \li Supported Platforms \row \li preload \li X \li \li Linux, macOS, QNX \row \li gdb \li X \li X \li Linux, QNX \row \li lldb \li X \li X \li Linux, macOS \row \li windll \li X \li X \li Windows \row \li style \li X \li \li all \endtable */ gammaray-2.11.3/docs/manual/gammaray-connections.qdoc000066400000000000000000000050631413310443000225640ustar00rootroot00000000000000/* gammaray-connections.qdoc This file is part of the GammaRay documentation. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. This work is also licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. See . */ /*! \contentspage {GammaRay User Manual} \previouspage {Methods} \nextpage {Enums and Flags} \page gammaray-connections.html \title Connections \section1 Overview The connections view is available for all QObject instances and shows inbound and outbound signal/slot connections for the currently selected object. \image gammaray-connections.png The connections view consists of two identical parts. The upper half is showing inbound connections, that is signals connected to slots of the current objects. The lower half is showing outbound connections, that is signals of the current object connected to other slots or arbitrary functions or lambda expressions. The latter is displayed as "slot object". Each of the two connection views has four columns: \list \li The sender object (inbound only), or the receiver object (outbound only). \li The name of the signal that is connected. \li The name of the slot the signal is connected to, if available. \li The type of the connection (automatic, direct or queued). \endlist \section2 Navigation The context menu on both the inbound and outbound connections view allows you to navigate to the sender or receiver object respectively. \section2 Warnings A warning icon is displayed in the first column of the inbound and outbound connections view if GammaRay detected a possible issue with the connection, such as: \list \li The connection exists multiple times. \li The connection is explicitly set to direct mode, but the sender and receiver are in different threads. \endlist Note that these are merely warnings about common problems, but can in some cases actually be valid usage. \section1 Examples The following examples make use of the connections view: \list \li \l{signal-slot} \li \l{timer} \endlist */ gammaray-2.11.3/docs/manual/gammaray-enums.qdoc000066400000000000000000000027771413310443000214020ustar00rootroot00000000000000/* gammaray-enums.qdoc This file is part of the GammaRay documentation. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. This work is also licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. See . */ /*! \contentspage {GammaRay User Manual} \previouspage {Connections} \nextpage {Class Info} \page gammaray-enums.html \title Enums and Flags \section1 Overview The enums view is available for all QObject and QMetaObject instances and shows enums and flags defined in the corresponding class, as far as introspectable via QMetaEnum. \image gammaray-enums.png The enums and flags view shows two levels of information. On the top-level you see the list of all enums and flags listed in the corresponding QMetaObject, in particular: \list \li The name of the enum or flag. \li The amount of elements defined in the enum. \li The class or namespace in which the enum has been declared. \endlist On the second level you find the names and values of each element of the corresponding enum or flag. */ gammaray-2.11.3/docs/manual/gammaray-event-monitor.qdoc000066400000000000000000000061051413310443000230460ustar00rootroot00000000000000/* gammaray-event-monitor.qdoc This file is part of the GammaRay documentation. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. This work is also licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. See . */ /*! \contentspage {Tools} \nextpage {Wayland Compositors} \previouspage {Timers} \page gammaray-event-monitor.html \title Events \section1 Overview The event monitor allows to observe all QEvent instances being processed by the target application. It allows looking at the event stream, event propagation, event properties as well as provides statistics on how often which event type occurs. \section1 Event Log The event log tab shows the history of all events that have gone through the event loop of the target application on the left hand side. For a selected event, the view on the right side shows the properties of the corresponding event. In case an event is propagated to other receivers (such as input events do along QWidget or QQuickItem hierarchies), those are shown as child entries of the corresponding event. \image gammaray-event-monitor-log.png The event history view shows the following information: \list \li The time the event was processed (not the time was put into the event queue). \li The type name of the event (see QEvent::Type), or the numeric value of the event type in case of custom events. \li The receiver of the event. If the receiver object has not been destroyed yet, it can be navigated to via the context menu. \endlist The toolbar above the event history allows to search for specific entries, to suspend the event recording, and to clear the event history. \section1 Event Types The event type view shows all known and observed event types, as well as the amount of corresponding events that have been registered. \image gammaray-event-monitor-types.png Two types of filters can be configured in the event type view by the checkboxes in the \uicontrol Record and \uicontrol Show columns: \list \li If an event type is not selected for recording, it is already discarded before being processed by GammaRay. This can be useful if a certain event type is occurring so frequently that it impacts performance. \li Events filtered for display are recorded but not shown in the event history, but are still recorded. This can be useful to improve visibility when investigating a specific type of event. \endlist The toolbar above the event type view allows to search for a specific event type and offers actions to quickly select or deselect each of the two filters. */ gammaray-2.11.3/docs/manual/gammaray-examples.qdoc000066400000000000000000000017421413310443000220600ustar00rootroot00000000000000/* gammaray-examples.qdoc This file is part of the GammaRay documentation. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. This work is also licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. See . */ /*! \contentspage{GammaRay User Manual} \previouspage {Problem Reporter} \nextpage {Advanced Usage} \group examples-gammaray \target Examples \title Examples The following examples demonstrate typical problems that can be found in Qt applications and how to investigate and fix those using GammaRay. */ gammaray-2.11.3/docs/manual/gammaray-font-browser.qdoc000066400000000000000000000026631413310443000226740ustar00rootroot00000000000000/* gammaray-font-browser.qdoc This file is part of the GammaRay documentation. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. This work is also licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. See . */ /*! \contentspage {Tools} \nextpage {Locales} \previouspage {Geo Positioning} \page gammaray-font-browser.html \title Font Browser \section1 Overview The font browser allows you to inspect the fonts accessible to QFontDatabase inside your target application. \image gammaray-font-browser.png The tree view on the left side shows all font families and their corresponding styles found on the target system, or manually made available to QFontDatabase, as well as their supported sizes. The right side shows a preview of the fonts selected in the font tree, for the given sample text and font properties. The preview is rendered on the target system, that is, you see the result from a remote embedded system here if that is your target. */ gammaray-2.11.3/docs/manual/gammaray-geo-positioning.qdoc000066400000000000000000000054101413310443000233500ustar00rootroot00000000000000/* gammaray-geo-positioning.qdoc This file is part of the GammaRay documentation. Copyright (C) 2018-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. This work is also licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. See . */ /*! \contentspage {Geo Positioning} \nextpage {Font Browser} \previouspage {Meta Type Browser} \page gammaray-geo-positioning.html \title Geo Positioning \section1 Overview The geo positioning view allows you to observe the QGeoPositionInfo data supplied by a QGeoPositionInfoSource to your application, in textual form as well as on a map view. It is also possible to override that positioning information manually or via a pre-recorded NMEA file, enabling you to easily test location-aware application. \image gammaray-geo-positioning.png By default the input widgets at the top of the view show the current positioning information provided by QtPositioning. The map view at the bottom highlights the current position with a blue marker, and the movement history with a blue line. You can center the map on the current position using the corresponding menu action or the keyboard shortcut \uicontrol F3. \section2 Manual Override You can switch the geo positioning view into override mode using the checkbox at the top left. This will enabling editing on all input widget on the top, and show a second position marker and movement track on the map, in red. For the application the positing information are replaced with what you enter here now, independent of what the QtPositioning backend reports. Besides changing individual values of QGeoPositionInfo in the widgets at the top, you can also set a position on the map view directly: \list \li Left-click will change latitude and longitude. \li Ctrl + mouse wheel will change the direction. \endlist Note that the override mode is only available when the target application has been started with GammaRay, not when attaching to it at runtime. Observing the data is possible in both cases. \section2 NMEA Replay Besides manual override it is also possible to replay a recorded NMEA track from a satellite positioning source. For using this switch the to manual override mode and select \uicontrol Positioning > \uicontrol{Load NMEA file...} from the menu. */ gammaray-2.11.3/docs/manual/gammaray-getting-started.qdoc000066400000000000000000000037071413310443000233520ustar00rootroot00000000000000/* gammaray-getting-started.qdoc This file is part of the GammaRay documentation. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. This work is also licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. See . */ /*! \contentspage {GammaRay User Manual} \nextpage {Installation} \previouspage {Gammaray User Manual} \page gammaray-getting-started.html \title Getting Started \section1 Overview \section2 Installation Installing GammaRay is described \l{Installation}{here}. If you are a user of the \l{Qt Automotive Suite}, you can skip this step as GammaRay comes pre-installed with your SDK in that case. \section2 Using GammaRay In order to start using GammaRay, familiarize yourself with its \l{Basic Operations} first. Based on your preferred workflow and user interface you can dig deeper in the following chapters: \list \li \l{GammaRay Client} \li \l{Qt Creator Integration} (\l{Qt Automotive Suite} only) \li \l{Graphical Launcher} \li \l{Command Line Interface} \endlist \section2 Learning GammaRay's Capabilities Once you can operate GammaRay and use it to interact with a target application, there are additional resources to learn about the capabilities GammaRay provides you: \list \li Documentation of all available \l{Tools}. \li Documentation of all available \l{Object Inspection} views. \li \l{Examples} of real-world problems, and how to analyze them with GammaRay. \endlist */ gammaray-2.11.3/docs/manual/gammaray-graphicsscene.qdoc000066400000000000000000000051461413310443000230620ustar00rootroot00000000000000/* gammaray-graphicsscene.qdoc This file is part of the GammaRay documentation. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. This work is also licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. See . */ /*! \contentspage {Tools} \contentspage {Graphics Scene Inspector} \nextpage {Qt3D Inspector} \previouspage {Widget Inspector} \page gammaray-graphicsscene-inspector.html \title Graphics Scene Inspector \section1 Overview The graphics scene inspector allows you to inspect the structure of a QGraphicsScene, and to view and edit the properties of the QGraphicsItem instances contained in it. It enables: \list \li Inspecting the graphics item hierarchy. \li Remote view of the rendered content with diagnostic overlays. \li Inspecting individual QPainter commands for each graphics item. \endlist If your target has multiple QGraphicsScene instances, the combo box at the top can be used to switch between them. \section1 Item Tree The item tree is shown to the top left, and contains the visual hierarchy of QGraphicsItem instances in the currently selected graphics scene. The right side contains the \l{Object Inspection} view for the selected item. \image gammaray-graphicsscene-inspector.png You can navigate to the object browser (and other applicable tools) using the context menu on the item tree. This feature is available for QGraphicsObject sub-classes only. \section1 Remote View The remote view is shown to the lower left of graphics view inspector. Besides giving you a zoomable view of the rendered content, it provides a number of additional features: \list \li Element picking. \li Diagnostic overlays with bounding shape, bounding box, and transformation origin information. \endlist \section1 Paint Analyzer When selecting a graphics item in the item tree, the \l{Paint Analyzer} tab is activated in the property view on the right. \image gammaray-graphicsitem-paint-analyzer.png The paint analyzer allows you to inspect every single QPainter command that is executed, and look at the visual result after each step. */ gammaray-2.11.3/docs/manual/gammaray-http-cookies.qdoc000066400000000000000000000023671413310443000226570ustar00rootroot00000000000000/* gammaray-http-cookies.qdoc This file is part of the GammaRay documentation. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. This work is also licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. See . */ /*! \contentspage {GammaRay User Manual} \nextpage{Problem Reporter} \previouspage{Application Attributes} \page gammaray-http-cookies.html \title HTTP Cookies \section1 Overview The HTTP cookie view shows a list of cookies held by a cookie store associated with the currently selected object. In particular these can be: \list \li A QNetworkCookieJar instance. \li A QNetworkAccessManager instance. \endlist \image gammaray-http-cookies.png The information shown in the HTTP cookie view correspond to the properties of QNetworkCookie. */ gammaray-2.11.3/docs/manual/gammaray-install.qdoc000066400000000000000000000147401413310443000217120ustar00rootroot00000000000000/* gammaray-install.qdoc This file is part of the GammaRay documentation. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. This work is also licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. See . */ /*! \contentspage {Getting Started} \nextpage {Basic Operations} \previouspage {Getting Started} \page gammaray-install.html \title Installation \section1 Overview GammaRay consists of two parts, the client and the probe. The client is the graphical user interface you are going to interact with. The probe interacts very closely with the Qt application it analyzes, using a number of internal APIs of Qt. This makes it necessary that the probe is built for exactly your Qt. All of the following affect the compatibility of the GammaRay probe with the target application: \list \li Qt version \li Qt configuration \li system architecture (most prominently 32bit vs 64bit) \li compiler vendor (particularly on Windows) \li compiler settings (particularly debug or release mode, on Windows) \endlist This tight coupling to Qt makes it unfortunately somewhat difficult to provide pre-compiled binaries for the GammaRay probe in all cases, the following instructions therefore also cover how to build GammaRay from source code, should no pre-built probe be available for your configuration. \section1 Local Development \section2 Pre-built packages Pre-built GammaRay packages are available for the following systems: \list \li \l{Qt Automotive Suite} contains everything needed as part of the standard SDK already. \li For some Linux distributions \l{KDAB} provides packages via the Open Build Service \l{https://download.opensuse.org/repositories/isv:/KDAB/}{here}. \endlist \section2 Build recipes Build recipes for the following source-based software distribution systems are available: \list \li \l{https://github.com/KDAB/homebrew-tap}{Homebrew} (macOS) \li \l{https://cgit.kde.org/craft.git/tree/portage/qt-apps/gammaray}{KDE Craft} (Windows) \endlist \section2 Building manually You can obtain the source code of GammaRay on \l{https://github.com/KDAB/GammaRay}{Github}. To build it you need the following requirements: \list \li Qt 5.5 or newer \li CMake 3.4 or newer \li a C++11 compatible compiler \endlist GammaRay is build using the normal CMake build process, you can find more information in the Install.txt file in the GammaRay source tree. \section1 Embedded Targets The \l{Qt Automotive Suite} contains pre-built GammaRay probes are included in the device images. For the following source-based embedded Linux distributions, there are example build recipes included below. Note that these likely need to be adjusted for your specific setup. \list \li Yocto \li Buildroot \endlist In all other cases, including non-Linux target systems, refer to the cross-compilation instructions below. \section2 Yocto A Yocto recipe for \l{Qt Automotive Suite} is available as part of the meta-boot2qt Yocto layer \l{https://code.qt.io/cgit/yocto/meta-boot2qt.git/tree/recipes-qt/automotive/gammaray_git.bb}{here}. See the example below on how to customize this to your needs. Example gammaray_git.bb: \quotefile yocto/gammaray_git.bb The lines you probably want to adjust are: \list \li SRCREV: to use newer versions \li DEPENDS: to select the Qt modules you are using, in order to enable all relevant GammaRay features \endlist \section2 Buildroot Example Config.in: \code config BR2_PACKAGE_GAMMARAY bool "gammaray" depends on BR2_PACKAGE_QT5 help GammaRay Qt introspection probe. \endcode Example gammaray.mk: \code GAMMARAY_VERSION = 1728dff87163c370fa6903a6d739d1a132b4c62f GAMMARAY_SITE = git://github.com/KDAB/GammaRay.git GAMMARAY_INSTALL_STAGING = NO GAMMARAY_INSTALL_TARGET = YES GAMMARAY_LICENSE = GPLv2 GAMMARAY_LICENSE_FILES = LICENSE GAMMARAY_DEPENDENCIES = qt5base GAMMARAY_CONF_OPTS += -DGAMMARAY_BUILD_UI=OFF $(eval $(cmake-package)) \endcode The lines you probably want to adjust are: \list \li GAMMARAY_VERSION: to use newer versions \li GAMMARAY_DEPENDENCIES: to select the Qt modules you are using, in order to enable all relevant GammaRay features \endlist \section2 Manual cross-compilation GammaRay can be cross-compiled using the normal cross-compilation mechanism of \l{CMake}, ie. passing CMake a toolchain file. The GammaRay source code contains a number of example toolchain files for Linux (generic Yocto, RaspberryPI, iMX6), QNX (6.5, 6.6, 7.0) and Android you can use or adjust. The toolchain file is passed to CMake using the -DCMAKE_TOOLCHAIN_FILE command line option. Note that this only works on the very first CMake run, CMake doesn't like changing the toolchain file on an existing build directory. In that case, make sure to wipe the entire build directory first (just deleting CMakeCache.txt is not enough!). Additionally you might need to pass one or more -DCMAKE_PREFIX_PATH=... command line options to point CMake to the location your sysroot has Qt (or any other dependency) installed in, just as during desktop builds. A default build of GammaRay includes both the client and the probe, for cross-compiling you normally only want the probe, which can be achieved by setting the CMake option GAMMARAY_BUILD_UI to off. Here's an example CMake invocation for building for RaspberryPI: \code export SYSROOT=/where/your/rpi/sysroot/is/located export PATH=:$PATH cd cmake -DCMAKE_TOOLCHAIN_FILE=/cmake/Toolchain-RPI.cmake -DCMAKE_PREFIX_PATH=$SYSROOT/usr/local/Qt-5.5.1 -DCMAKE_INSTALL_PREFIX=$SYSROOT/usr/ -DGAMMARAY_BUILD_UI=OFF \endcode */ gammaray-2.11.3/docs/manual/gammaray-launcher-gui.qdoc000066400000000000000000000075421413310443000226310ustar00rootroot00000000000000/* gammaray-launcher-gui.qdoc This file is part of the GammaRay documentation. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. This work is also licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. See . */ /*! \contentspage {Getting Started} \nextpage {GammaRay Client} \previouspage {Basic Operations} \page gammaray-launcher-gui.html \title Graphical Launcher \section1 Overview The graphical launcher of allows you to connect GammaRay to a target in application in three different ways: \list \li Launch a new target application, with GammaRay injected right from the start. \li Attach to a locally running application that has already been started by other means. \li Connect to a remotely running target that has already been injected by GammaRay by other means. \endlist The graphical launcher can be started as follows: \list \li On Windows, by starting gammaray.exe without arguments, or by selecting "GammaRay" from the \uicontrol Start menu. \li On Mac OSX , by running the GammaRay app bundle. \li On Linux, by running "gammaray" without arguments on the command line, or by selecting "GammaRay" from the start menu. \endlist \section1 Launching Applications The \uicontrol Launch tab of the graphical launcher allows you to start new applications and inject GammaRay into them. You just have to select an application from the list at the top, and specify command line arguments in the field below. \image gammaray-launcher-launch.png GammaRay automatically selects the best matching probe for the target application. You can verify this in the combo box at the bottom of the page. If GammaRay could not find a compatible probe, launching fails. \section1 Attaching to an Application The \uicontrol Attach tab of the graphical launcher allows you to attach to an application that is already running, and inject GammaRay into it. The process list shows all local Qt-based processes that are potential targets. For each of them, GammaRay tries to determine the exact Qt version they are using and list only those that has a compatible probe available. A tooltip in the process list shows detailed information about the Qt version and used probe for each process. \image gammaray-launcher-attach.png \section1 Connecting to a Remote Target The \uicontrol Connect tab of the graphical launcher allows you to connect to a target on a remote device that has already been injected with GammaRay. This is mainly useful when working with embedded devices. GammaRay tries to automatically discover viable targets on your local network, these are displayed in the list view on the \uicontrol Connect page, and can be used by selecting the corresponding items. Discovered targets using an incompatible GammaRay version will be shown but will be disabled for selection. Should GammaRay not be able to discover the desired target automatically, the network address of the target can also be specified manually in the entry field on the top. This might be necessary depending on your local network setup. \image gammaray-launcher-connect.png \section1 Troubleshooting Opening the \uicontrol{Self Test} tab makes GammaRay verify its installation and identify problems (if any). The results are displayed on this page. \image gammaray-launcher-selftest.png */ gammaray-2.11.3/docs/manual/gammaray-licenses-and-attribtions.qdoc000066400000000000000000000021561413310443000251470ustar00rootroot00000000000000/* gammaray-licenses-and-attribtions.qdoc This file is part of the GammaRay documentation. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. This work is also licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. See . */ /*! \contentspage{GammaRay User Manual} \page gammaray-licenses-and-attributions.html \title Licenses and Attributions \section1 Overview GammaRay is available under commercial Qt Automotive Suite licenses. In addition, it is available under the GNU General Public License, version 2. Furthermore GammaRay contains third party modules under following licenses: \generatelist{groupsbymodule attributions-gammaray-manual} */ gammaray-2.11.3/docs/manual/gammaray-locales.qdoc000066400000000000000000000051451413310443000216650ustar00rootroot00000000000000/* gammaray-locales.qdoc This file is part of the GammaRay documentation. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. This work is also licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. See . */ /*! \contentspage {Tools} \nextpage {Mime Types} \previouspage {Font Browser} \page gammaray-locales.html \title Locales \section1 Overview The locale browser allows you to inspect static databases provided by Qt and/or the operating system around localization. This data is typically independent of your application, and includes: \list \li The locale database, accessible via QLocale. \li The timezone database, accessible via QTimeZone. \endlist Each database is represented as its own tab in the GammaRay locale browser. \section1 Locale Database The locale browser is allowing you to inspect the details of every QLocale configuration present on the target system. \image gammaray-locales.png The upper area allows to select the aspects of the locales you are interested in (for example, date/time settings, currency-related information, number formatting rules, and so on). This translates to columns in the table below. Each row in the lower section of the locale browser corresponds with a QLocale configuration present on the target system (that is. typically one per country/region). You can use the filter on top to quickly find the country/region you are interested in. \section1 Timezone Database The timezone tab allows you to inspect the timezone database as seen by your application. \image gammaray-timezones.png The timezone browser consists of two parts. The left side shows all known timezones including some of their properties, names and identifiers, The right side shows all known time zone transitions for currently selected timezone, including both regular DST transitions as well as irregular transitions that occurred for whatever reason. This view is limited to 30 transitions before and after the current date, as the list is potentially infinite for timezones with recurring DST transitions. The current system time zone is highlighted bold. */ gammaray-2.11.3/docs/manual/gammaray-manual-offline.qdocconf000066400000000000000000000015051413310443000240020ustar00rootroot00000000000000include(gammaray-manual.qdocconf) include($QT_INSTALL_DOCS/global/qt-html-templates-offline.qdocconf) # adapted from qtbase template # TODO: add logo like in the doxygen case? HTML.footer = \ " \n" \ " \n" \ " \n" \ " \n" \ "\n" \ "
\n" \ "

\n" \ " © 2016-2021 Klarälvdalens Datakonsult AB (KDAB).\n" \ " Documentation contributions included herein are the copyrights of\n" \ " their respective owners.
" \ " This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License." \ "

" \ "
\n" \ gammaray-2.11.3/docs/manual/gammaray-manual-online-kdab.qdocconf000066400000000000000000000071051413310443000245450ustar00rootroot00000000000000include(gammaray-manual.qdocconf) HTML.stylesheets += \ style/qt5-sidebar.html \ style/kdab-header.html \ style/kdab-footer.html \ style/kdab-online.css \ style/gammaray-manual-splash-screen.png \ style/gammaray-logo-256.png \ HTML.headerstyles = \ " \n" \ " \n" \ " \n" \ " \n" \ " \n" HTML.headerscripts = \ " \n" \ " \n" \ " \n" \ " \n" \ HTML.endheader = \ "\n" # this goes before the breadcrumb HTML.postheader = \ "\n" \ "
\n" \ "
\n" \ "
\n" \ " \n" \ " \n" \ "
\n" \ "
\n" # this goes between sidebar and actual content HTML.prologue = \ "
\n" \ HTML.footer += \ "
\n" \ "
\n" \ "
\n" \ "
\n" \ "
\n" \ "
\n" \ "
\n" \ "
\n" \ "
\n" gammaray-2.11.3/docs/manual/gammaray-manual-online.qdocconf000066400000000000000000000013221413310443000236410ustar00rootroot00000000000000include(gammaray-manual.qdocconf) HTML.footer = \ " \n" \ "

\n" \ " © 2016-2021 Klarälvdalens Datakonsult AB (KDAB).\n" \ " Documentation contributions included herein are the copyrights of\n" \ " their respective owners.
" \ " This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License." \ "

\n" include($QT_INSTALL_DOCS/global/qt-html-templates-online.qdocconf) # Sidebar with quick links HTML.stylesheets += style/qt5-sidebar.html gammaray-2.11.3/docs/manual/gammaray-manual.qdoc000066400000000000000000000113751413310443000215220ustar00rootroot00000000000000/* gammaray-manual.qdoc This file is part of the GammaRay documentation. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. This work is also licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. See . */ /*! \page index.html \nextpage gammaray-getting-started.html \title GammaRay User Manual \section1 What is GammaRay? GammaRay is an introspection tool for Qt applications developed by \l{https://www.kdab.com/}{KDAB}. Leveraging the QObject introspection mechanism it allows you to observe and manipulate your application at runtime. This works both locally on your workstation and remotely on an embedded target. Augmenting your instruction-level debugger, GammaRay allows you to work on a much higher level, with the same concepts as the frameworks you use. This is especially useful for the more complex Qt frameworks such as model/view, state machines, or scene graphs. When trying to analyze bugs in your application, a instruction-level debugger is usually your first tool of choice, and rightfully so. Debuggers allow you to follow the program flow and allow you to inspect the state of objects or variables. When using a debugger with proper Qt integration, such as in QtCreator, it will also handle Qt's own data types correctly. However, when dealing with complex frameworks this level of detail is often too low-level. Keeping an overview in a large scene graph or following the complex interactions between models and views can quickly become a cumbersome task. GammaRay addresses this by providing domain-specific debugging aids on a much higher level. It provides easy ways of navigating through the complex internal structures you find in some Qt frameworks, such as the QtQuick scene graphs, model/view, QTextDocument, state machines, and more. Unlike the debugger, GammaRay understands those internal structures and can present them in a meaningful way. GammaRay is available for all major platforms and can inspect applications right from the start or attach to an already running one. It also supports remote debugging, which is especially valuable when working on embedded systems. \section1 Table Of Contents \list \li \l{Getting Started} \list \li \l{Installation} \li \l{Basic Operations} \li \l{Graphical Launcher} \li \l{GammaRay Client} \li \l{Qt Creator Integration} \li \l{Command Line Interface} \endlist \li \l{Tools} \list \li \l{Qt Quick 2 Inspector} \li \l{Widget Inspector} \li \l{Graphics Scene Inspector} \li \l{Qt3D Inspector} \li \l{Styles} \li \l{Web Inspector} \li \l{State Machine Debugger} \li \l{Messages} \li \l{Signal Plotter} \li \l{Timers} \li \l{Events} \li \l{Wayland Compositors} \li Script Engine Debugger \li \l{Object Browser} \li \l{Actions} \li \l{Models} \li \l{Text Documents} \li \l{Resources} \li \l{Translations} \li \l{Meta Object Browser} \li \l{Meta Type Browser} \li \l{Geo Positioning} \li QtIvi Inspector \li \l{Font Browser} \li \l{Locales} \li \l{Mime Types} \li \l{Network} \li \l{Text Codecs} \li \l{System Information} \endlist \li \l{Object Inspection} \list \li \l{Properties} \li \l{Methods} \li \l{Connections} \li \l{Enums and Flags} \li \l{Class Info} \li \l{Stack Trace} \li \l{Bindings} \li \l{QML Context} \li \l{QML Type} \li \l{Scene Graph Material} \li \l{Scene Graph Geometry} \li \l{Scene Graph Texture} \li \l{Paint Analyzer} \li \l{Widget Attributes} \li \l{Qt3D Geometry Inspector} \li \l{Application Attributes} \li \l{HTTP Cookies} \endlist \li \l{Problem Reporter} \li \l{Examples} \li \l{Advanced Usage} \li \l{Licenses and Attributions} \endlist */ gammaray-2.11.3/docs/manual/gammaray-manual.qdocconf000066400000000000000000000016471413310443000223710ustar00rootroot00000000000000project = "GammaRay" description = "GammaRay User Manual" url = https://doc.qt.io/GammaRay sourcedirs += . $$BUILDDIR sources.fileextensions = "*.qdoc" imagedirs = images exampledirs = ../../examples examples.fileextensions = "*.h *.cpp *.qml" depends += qtcore qtnetwork qtgui qtwidgets qtqml qtquick qt3d qtwaylandcompositor qtpositioning qhp.projects = GammaRay qhp.GammaRay.file = gammaray-manual.qhp qhp.GammaRay.namespace = com.kdab.GammaRay.$QT_VERSION_TAG qhp.GammaRay.virtualFolder = gammaray qhp.GammaRay.indexTitle = GammaRay User Manual qhp.GammaRay.indexRoot = qhp.GammaRay.subprojects = manual qhp.GammaRay.subprojects.manual.title = GammaRay User Manual qhp.GammaRay.subprojects.manual.indexTitle = GammaRay User Manual qhp.GammaRay.subprojects.manual.type = manual navigation.homepage = "GammaRay User Manual" navigation.landingpage = "GammaRay User Manual" buildversion = "GammaRay User Manual $QT_VERSION" gammaray-2.11.3/docs/manual/gammaray-messages.qdoc000066400000000000000000000037511413310443000220530ustar00rootroot00000000000000/* gammaray-messages.qdoc This file is part of the GammaRay documentation. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. This work is also licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. See . */ /*! \contentspage {Tools} \nextpage {Signal Plotter} \previouspage {State Machine Debugger} \page gammaray-messages.html \title Messages \section1 Overview The Messages tool allows you access to diagnostic messages sent via QDebug, as well as to configure QLoggingCategory settings dynamically at runtime. \section1 Message Browser The message browser shows you all diagnostic messages of the target using a QLoggingCategory that is active on the target. Here you can: \list \li search and filter messages, \li look at a backtrace of a selected critical or warning message, \li and navigate to the code location of a diagnostic message (availability depends on build settings). \endlist \section1 Logging Categegory Configuration The logging category configuration shows all QLoggingCategory instances detected on the running target, as well as their current configuration. \image gammaray-logging-categories.png Here you can enable or disable individual logging categories at runtime, which takes immediate effect. This is particularly useful to only enable output of high-volume diagnostics for a short period of time. \section1 Examples The following examples make use of the debug message browser: \list \li \l{quick-batching} \endlist */ gammaray-2.11.3/docs/manual/gammaray-metaobject-browser.qdoc000066400000000000000000000060311413310443000240340ustar00rootroot00000000000000/* gammaray-metaobject-browser.qdoc This file is part of the GammaRay documentation. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. This work is also licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. See . */ /*! \contentspage {Tools} \nextpage{Meta Type Browser} \previouspage {Translations} \page gammaray-metaobject-browser.html \title Meta-Object Browser \section1 Overview The meta-object browser shows the hierarchy of all QMetaObject instances found in the target in the tree view on the left side. The property view on the right side shows information known by the QMetaObject system, including data from: \list \li QMetaProperty, \li QMetaMethod, \li QMetaEnum, \li and QMetaClassInfo. \endlist GammaRay checks all QMetaObject instances for typical problems such as using unregistered or improperly qualified types on properties or method arguments. In case such issues are detected, a warning icon is shown, and a tooltip informs you about the details. These problems are usually harmless when working with C++ API, but tend to be relevant for dynamic invocation or language bindings, ie. most commonly when exposing the affected types to QML. \section1 Instance Statistics The meta-object tree view also contains columns with information about how many instances of a specific type have been created, including and excluding sub-classes. \image gammaray-metaobject-browser.png In particular, these numbers are: \list \li Self total: The total amount of instances of this type ever created, excluding instances of sub-classes. \li Inclusive total: The total amount of instances of this type ever created, including sub-classes. \li Self alive: The amount of instances created but not destroyed yet of this type, excluding sub-classes. \li Inclusive alive: The amount of instances created but not destroyed yet of this type, including sub-classes. \endlist Tooltips and colorization of these columns show the ratio of this type on the overall amount of QObject instances. This is particularly useful for identifying unnecessarily created instances, as QObject construction is not exactly cheap. For dynamically created QMetaObject instances (such as found in QML defined types or dynamic QtSCXML state charts), it might not be possible for GammaRay to determine the lifetime correctly, those types are grayed out in order to avoid access to already freed memory. For those types you will not be able to access information beyond the basic statistics. */ gammaray-2.11.3/docs/manual/gammaray-metatype-browser.qdoc000066400000000000000000000036371413310443000235600ustar00rootroot00000000000000/* gammaray-metatype-browser.qdoc This file is part of the GammaRay documentation. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. This work is also licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. See . */ /*! \contentspage {Meta Type Browser} \nextpage{Geo Positioning} \previouspage {Meta Object Browser} \page gammaray-metatype-browser.html \title Meta Type Browser \section1 Overview The meta type browser shows all types registered with QMetaType, and the information registered for them. \image gammaray-metatype-browser.png The meta type view can show the following information, some columns might only be available in newer Qt versions though. \list \li The type name. \li The type id. \li The storage size needed for an instance of the type. \li Address of the associated QMetaObject, if present. \li Flags of the type (see QMetaType::TypeFlag), possible values depend on the Qt version used by the target. \li Indication whether custom comparison or debug output functions have been registered for the type. \endlist On types with an associated QMetaObject, the context menu allows navigation to the corresponding entry int the \l{Meta Object Browser}. If the target registers types later during application runtime, it might become necessary to manually trigger a scan for new meta types, the Meta Type menu contains a corresponding action for doing this. */ gammaray-2.11.3/docs/manual/gammaray-methods.qdoc000066400000000000000000000054031413310443000217030ustar00rootroot00000000000000/* gammaray-methods.qdoc This file is part of the GammaRay documentation. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. This work is also licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. See . */ /*! \contentspage {GammaRay User Manual} \previouspage {Properties} \nextpage {Connections} \page gammaray-methods.html \title Methods \section1 Overview The methods view is available for all QObject and QMetaObject instances and shows methods, signals and slots declared in there. \image gammaray-methods.png The methods view contains four columns: \list \li The signature of the method, including its name and as far as the information is available, its return type and the types and names of its arguments. \li The method type, that is signal, slot or a Q_INVOKABLE method. \li The access type of the method. \li The source of the method, which normally is the class it is declared in. \endlist For some methods, additional information provided via QMetaMethod are available in a tooltip. A warning icon in the method name column indicates possible issues detected by GammaRay. \section2 Invoking Methods Methods can also be dynamically invoked, using the \uicontrol{Invoke} action from the context menu. Similarly, you can emit signals by using the \uicontrol{Emit} action from the context menu. In both cases the method invocation dialog shown below is displayed. \image gammaray-method-invocation.png In the method invocation dialog you can specify any argument the method or signal may take, the types as in the \l{Properties} view are supported here too. You can also specify whether to invoke the method directly or queued. \section2 Monitoring Signals It is also possible to monitor specific signals of the current QObject, by using the \uicontrol{Connect to} action in the context menu of the corresponding signal. Whenever the signal is emitted, the signal log at the bottom of the methods view shows the time stamp of the emission, as well as the values of the signal arguments, if any. This can also be seen in the above screenshot. \section1 Examples The following examples make use of the methods view: \list \li \l{signal-slot} \li \l{timer} \endlist */ gammaray-2.11.3/docs/manual/gammaray-mime-types.qdoc000066400000000000000000000027111413310443000223300ustar00rootroot00000000000000/* gammaray-mime-types.qdoc This file is part of the GammaRay documentation. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. This work is also licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. See . */ /*! \contentspage {Tools} \nextpage {Network} \previouspage {Locales} \page gammaray-mime-types.html \title Mime Types \section1 Overview The mime type browser allows you to inspect the details of every QMimeType instance available via QMimeDatabase on the target system. This information is usually independent of the target application, but rather depend on the system. \image gammaray-mime-types.png The tree view shows the entire hierarchy of all known mime types. The hierarchy matches the specified inheritance of a mime type. In the subsequent columns, additional information for each mime type is shown, including: \list \li file extensions, \li human readable description, \li icons, \li and alias mime types. \endlist */ gammaray-2.11.3/docs/manual/gammaray-model-inspector.qdoc000066400000000000000000000051501413310443000233430ustar00rootroot00000000000000/* gammaray-model-inspector.qdoc This file is part of the GammaRay documentation. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. This work is also licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. See . */ /*! \contentspage {Tools} \nextpage {Text Documents} \previouspage {Actions} \page gammaray-model-inspector.html \title Models \section1 Overview The model inspector allows you to look at QAbstractItemModel instances, chains of QAbstractProxyModel instances built on top of these, as well as QItemSelectionModel instances associated with any of them. \image gammaray-model-inspector.png \section2 Model Inspection The model view on the top left shows a list of all QAbstractItemModel instances in the target application. Selecting a model shows its content in the model content view at the center. In the model content view, you can select a single cell for inspecting its content in detail. The cell content view on the right will show you: \list \li information about the QModelIndex of that cell, \li cell flags (see Qt::ItemFlags), \li and values for all default (see Qt::ItemDataRole) and custom roles. \endlist If the source model allows changing values, you can edit the value column in the cell content view. The context menu on the model list allows navigation to and from the QObject browser for analyzing object properties. \section2 Proxy Model Inspection If the target application uses QAbstractProxyModel instances, those are shown in the tree view in the upper left, beneath there corresponding source model. This allows you to inspect intermediate results of proxy model chains. \section2 Selection Model Inspection For each selected model, the selection model list on the lower left shows all QItemSelectionModel instances for that model, as well as how many cells, rows, and columns are currently selected. Selecting a selection model shows its current selection in the model content view at the center. The context menu on the selection model list allows navigation to and from the QObject browser for analyzing object properties. */ gammaray-2.11.3/docs/manual/gammaray-network.qdoc000066400000000000000000000053671413310443000217420ustar00rootroot00000000000000/* gammaray-network.qdoc This file is part of the GammaRay documentation. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. This work is also licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. See . */ /*! \contentspage {Tools} \previouspage {Mime Types} \nextpage {Text Codecs} \page gammaray-network.html \title Network \section1 Overview The network view consists of three different sub-tools, each being on its own tab: \list \li Showing all network operations such as HTTP requests done via QNetworkAccessManager. \li Showing all network interfaces found on the target system. \li Showing all network configurations found on the target system. \endlist \section1 Network Operations The network operations view shows all QNetworkReply instances and their associated QNetworkAccessManager. \image gammaray-network-operations-monitor.png The following information are available: \list \li Success or failure of the operation is indicated by the icon in the first column. \li The row color indicates whether or not a QNetworkReply has been destroyed already, for still alive replies object navigation via the context menu is available. \li The HTTP operations performed and the corresponding URL are shown in columns two and five. \li The time the operation took, and the amount of transferred data are shown in columns three and four. This information might not be reliably available for short-lived operations in secondary threads though. \li Whether or not transport encryption was used is shown by the icon in the fifth column. \endlist \section1 Network Interfaces The network interface view shows you all network interfaces and their corresponding states as seen by Qt on the target system. \image gammaray-network-interfaces.png For each interface you see its: \list \li identifier, \li hardware address, \li and flags (see QNetworkInterface). \endlist Additionally, all IPv4 or IPv6 addresses configured for an interface are shown below the corresponding interface. \section1 Network Configurations The configurations tab shows the QNetworkConfiguration instances found on the system, such as all known Wifi configurations. */ gammaray-2.11.3/docs/manual/gammaray-object-inspection.qdoc000066400000000000000000000046151413310443000236630ustar00rootroot00000000000000/* gammaray-object-inspection.qdoc This file is part of the GammaRay documentation. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. This work is also licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. See . */ /*! \contentspage {GammaRay User Manual} \previouspage {System Information} \nextpage {Properties} \page gammaray-object-inspection.html \title Object Inspection \section1 Overview When selecting a specific object in one of the GammaRay \l{Tools}, you often find the object inspection view on the right side. This view consists of a numbers of tabs, showing various aspects of the selected object. Which tabs are available depends on the type of the object, and the context it is being used in. \section2 General QObject Inspection The following tabs are generally available on all QObject instances. \list \li \l{Properties} \li \l{Methods} \li \l{Connections} \li \l{Enums and Flags} \li \l{Class Info} \li \l{Stack Trace} \endlist \section2 QML The following views are available for objects used in a QML context, or types decalred in QML. This is not limited to \l{Qt Quick}, but also includes \l{Qt 3D} or even custom use of the QQmlEngine. \list \li \l{Bindings} \li \l{QML Context} \li \l{QML Type} \endlist \section2 Graphical Output The following views allow you to inspect details about the graphical output of QWidget, \l{Qt Quick} or \l{Qt 3D} based applications. \list \li \l{Scene Graph Material} \li \l{Scene Graph Geometry} \li \l{Scene Graph Texture} \li \l{Paint Analyzer} \li \l{Widget Attributes} \li \l{Qt3D Geometry Inspector} \endlist \section2 Specific Classes The following views are showing information about a specific class. \list \li \l{Application Attributes} \li \l{HTTP Cookies} \endlist */ gammaray-2.11.3/docs/manual/gammaray-paint-analyzer.qdoc000066400000000000000000000067671413310443000232140ustar00rootroot00000000000000/* gammaray-paint-analyzer.qdoc This file is part of the GammaRay documentation. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. This work is also licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. See . */ /*! \contentspage {GammaRay User Manual} \previouspage{Scene Graph Texture} \nextpage{Widget Attributes} \page gammaray-paint-analyzer.html \title Paint Analyzer \section1 Overview The paint analyzer allows you to inspect every single operation performed on a QPainter. It is available on objects using a QPainter for rendering their visual appearance, as well as on entire scenes. For individual objects, the paint analyzer is typically not applied recursively to child elements of the selected object, so you only see operations by the current object itself. Supported object types for this include: \list \li QWidget \li QQuickPaintedItem \li QGraphicsItem \li Qt3DRender::QPaintedTextureImage \endlist Alternatively you can apply the paint analyzer to full scenes recursively. This is available in the following tools: \list \li The \l{Widget Inspector}. \li The \l{Qt Quick 2 Inspector} for windows using the Qt Quick software renderer backend. \endlist \image gammaray-paint-analyzer.png The paint analyzer consists of three main elements, the command list on the top left, the command details view on the bottom left and the render preview on the right side. \section1 Command and Argument Inspection The command list shows all QPainter commands in chronological order. The following information are provided: \list \li The command type, in the first column. \li The command arguments. This is either shown directly in the second column if there is only one argument for a command, or as sub-entries in the list for multiple arguments. If an argument is of a complex type, the argument value is also shown in the \uicontrol Argument tab in the argument details view. \li The relative contribution of a command to the overall rendering cost, in the third column. \endlist The argument details view will also show a stack trace for the currently selected painter command, showing what call chain lead to the command being executed. If debug information are available for the corresponding code, the corresponding source location is also shown, and can be directly opened using the context menu. \section1 Render Preview Selecting a command in the command list view will cause the render preview to show the visual result up to the selected command, allowing you to inspect the visual output step by step. The render preview can be panned and zoomed using the corresponding actions in the toolbar at its top. Additionally, a measurement and color picking tool is available that way too. The render preview can also visualize the current clip area of the QPainter, at the selected command. This is shown with a red shaded overlay, areas covered by this will be clipped. */ gammaray-2.11.3/docs/manual/gammaray-problem-reporter.qdoc000066400000000000000000000033721413310443000235430ustar00rootroot00000000000000/* gammaray-problem-reporter.qdoc This file is part of the GammaRay documentation. Copyright (C) 2018-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. This work is also licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. See . */ /*! \contentspage {GammaRay User Manual} \previouspage {Object Inspection} \nextpage {Examples} \page gammaray-problem-reporter.html \title Problem Reporter \section1 Overview The problem reporter view can be found among the other inspection tools in the left sidebar. It is a central place to collect all issues in the target application that GammaRay observed while being attached. Additionally, it allows to scan the target application for a selected set of issues. \image gammaray-problem-reporter.png The detected issues are found in the list view on the right side, including a description, a severity level of the issue (shown as icon), and if available the source location the issues originates in. A context menu allows navigating to the source location, as well as to the associated object. The left panel in the problem reporter controls explicit scans for issues. At the top various scans are listed and can be selected, the button below executes the scan. Note that this can take quite some time and can block the target application. */ gammaray-2.11.3/docs/manual/gammaray-properties.qdoc000066400000000000000000000101541413310443000224330ustar00rootroot00000000000000/* gammaray-properties.qdoc This file is part of the GammaRay documentation. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. This work is also licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. See . */ /*! \contentspage {GammaRay User Manual} \previouspage {Object Inspection} \nextpage {Methods} \page gammaray-properties.html \title Properties \section1 Overview The properties view is the most general way of inspecting a selected objects, and is available for all types of objects. \image gammaray-properties.png The view consists of four columns: \list \li The name of the property. \li The current value of the property. If change notifications are available for a property, this value is updated live when the property value changes. When looking at a QMetaObject instead of an object instance (for example in the \l{Meta Object Browser}, this column is not available. \li The data type of the property value. \li The source of the property. This is typically a class name, but may also indicate dynamic QObject properties, QML attached or context properties. \endlist Depending on the source of the property, additional information are available in a tooltip, as seen in the above screenshot. Depending on the type of the property value, object navigation actions are available in the context menu, typically for properties containing references to other objects. Properties containing container or object values can often be expanded to inspect their content in more detail. \section2 Property Sources Properties are taken from the following sources, based on the type of the object you have selected: \list \li Static QObject properties, that is properties introspectable via QMetaProperty. \li Dynamic QObject properties. \li QML attached properties, if the selected object has been created via QML. \li QML context properties, if the selected object is a QQmlContext. \li A set of non-introspectable but well-known properties of Qt classes built into GammaRay. \endlist Additionally the following container types are supported, and exapanded depending on their content: \list \li QSequentialIterable, which covers most sequential containers like QVector and QList. \li QAssociativeIterable, which covers most associative containers like QHash and QMap. \li QQmlListProperty \li QVariant \li QJSValue \endlist \section2 Property Editing As far as supported by a specific property, property values can be edited, with immediate effect on the application. This can generally been done by double-clicking into the value column. For some complex types, an extended editing dialog is available, reachable via the "..." button on the right side of the value editing field. The following types can be edited: \list \li standard numeric and boolean types \li QString \li Qt's vector and matrix math types \li most of Qt's geometry types \li some of Qt's graphic types such as QFont and QColor \li enum and flag types introspectable via QMetaEnum \endlist Additionally it is possible to add dynamic properties to a QObject instance with the controls at the bottom of the property view. Existing dynamic properties can be edited as any other property, and can be removed via the context menu. \section1 Examples The following examples make use of the properties view: \list \li \l{quick-event-handling} \li \l{quick-batching} \li \l{widget-layouting} \endlist */ gammaray-2.11.3/docs/manual/gammaray-qmlbindings.qdoc000066400000000000000000000037401413310443000225510ustar00rootroot00000000000000/* gammaray-qmlbindings.qdoc This file is part of the GammaRay documentation. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. This work is also licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. See . */ /*! \contentspage {GammaRay User Manual} \nextpage {QML Context} \previouspage {Stack Trace} \page gammaray-qmlbindings.html \title Bindings \section1 Overview The bindings view is available for all QObject instances instantiated from QML and shows active property bindings for the selected object, including their dependencies on other properties. \image gammaray-bindings.png At the top level, the binding view shows all QML property bindings of the currently selected object. Below each bindings, its dependencies are shown, which themselves can be property bindings again. Dependencies can be explicit, that is they are specified in your code, or implicit. Implicit dependencies will not show a corresponding source code location and are mainly introduced by the Qt Quick layouting system. The displayed information include: \list \li The name of the property. \li The current value of the property. \li The depth of the dependency tree. The depth is shown as infinite on binding loops. \li The source code location of the binding. You can directly navigate to the source code using the context menu. Implicit dependencies caused for example by the Qt Quick layouting system will not show a source code location. \endlist */ gammaray-2.11.3/docs/manual/gammaray-qmlcontext.qdoc000066400000000000000000000036631413310443000224440ustar00rootroot00000000000000/* gammaray-qmlcontext.qdoc This file is part of the GammaRay documentation. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. This work is also licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. See . */ /*! \contentspage {GammaRay User Manual} \previouspage{Bindings} \nextpage{QML Type} \page gammaray-qmlcontext.html \title QML Context \section1 Overview The QML context view shows the chain of QQmlContext instances from the root context up to the context in which the current object has been created. The QML context view is available for any QObject instantiated from within QML. \image gammaray-qmlcontext.png The QML context views consists of two parts, the context chain view on the top, and the context property view at the bottom. The context property view shows the currently selected context from the context chain view. \section2 Context Chain The context chain view shows the entire chain of QQmlContext instances starting from the root context in the first line. For each context, the corresponding QML file is displayed, as far as that information is known. The context menu allows you to navigate to the source code, opening it in your configured code editor. \section2 Context Properties For the selected context in the context chain view, the context properties view shows the properties of the corresponding QQmlContext object. This view essentially works like the general \l{Properties} view. */ gammaray-2.11.3/docs/manual/gammaray-qmltype.qdoc000066400000000000000000000021051413310443000217270ustar00rootroot00000000000000/* gammaray-qmltype.qdoc This file is part of the GammaRay documentation. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. This work is also licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. See . */ /*! \contentspage {GammaRay User Manual} \nextpage{Scene Graph Material} \previouspage{QML Context} \page gammaray-qmltype.html \title QML Type \section1 Overview The QML type view shows additional type information that QQmlEngine holds internally for the type of the currently selected object. This is available both for types defined in C++ and in QML code. \image gammaray-qmltype.png */ gammaray-2.11.3/docs/manual/gammaray-qobject-browser.qdoc000066400000000000000000000050051413310443000233460ustar00rootroot00000000000000/* gammaray-qobject-browser.qdoc This file is part of the GammaRay documentation. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. This work is also licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. See . */ /*! \contentspage {Tools} \nextpage {Actions} \previouspage {Timers} \page gammaray-qobject-browser.html \title Object Browser \section1 Overview The object browser provides access to all QObject instances in a target. On the left side you have a tree view showing their parent/child relationship, on the right side you have access to properties of the selected object. \image gammaray-object-inspector.png Depending on the type of the object, the \l{Object Inspection} view provides additional tabs for accessing specific details of an object. The QObject browser provides the following information about the selected object: \list \li \l{Properties} that are accessible via the Qt introspection system. \li Qt signals, including the ability to monitor them for emission (see \l{Methods}). \li Qt slots and Q_INVOKABLE methods, including the ability to invoke them. \li Inbound and outbound signal/slot \l{Connections}. \li \l{Enums and Flags}, as well as class information registered at Qt's meta-object system. \li For objects created in a \l{QML Context}, access to context properties and the current context chain is provided. \endlist \section1 Finding and Navigating Objects Finding objects can be done using the search field on the top-left side of the object browser. Additionally, visual elements can be picked in the target application by holding the Ctrl+Shift+Left keys. If you have already selected an object and want to navigate to related objects, such as look at a model of a selected view, you can use the object navigation in the context menu by right-clicking on the corresponding element in the property view. \section1 Examples The following examples make use of the QObject browser: \list \li \l{signal-slot} \endlist */ gammaray-2.11.3/docs/manual/gammaray-qresource-browser.qdoc000066400000000000000000000031651413310443000237340ustar00rootroot00000000000000/* gammaray-qresource-browser.qdoc This file is part of the GammaRay documentation. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. This work is also licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. See . */ /*! \contentspage {Tools} \nextpage {Translations} \previouspage {Text Documents} \page gammaray-qresource-browser.html \title Resources \section1 Overview The resource browser view allows to inspect the content of assets compiled into the application binary using the QResource system. The tree view on the left side shows the file and folder hierarchy, the content view on the right allows to inspect the file content if it is a supported text or image format. \image gammaray-qrc-browser.png The context menu on the folder tree on the left side allows to save individual files or entire folders to disk. This also works for remote embedded targets. \section1 Source Code Navigation When using compiled-in QML code using the QResource system, source code navigation from the \l{Qt Quick 2 Inspector} automatically selects the QResource browser, navigate to the source file, and open it at the corresponding line. */ gammaray-2.11.3/docs/manual/gammaray-qsggeometry.qdoc000066400000000000000000000037711413310443000226140ustar00rootroot00000000000000/* gammaray-qsggeometry.qdoc This file is part of the GammaRay documentation. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. This work is also licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. See . */ /*! \contentspage {GammaRay User Manual} \previouspage{Scene Graph Material} \nextpage{Scene Graph Texture} \page gammaray-qsggeometry.html \title Scene Graph Geometry \section1 Overview When having a scene graph node with an attached QSGGeometry instance selected (such as a QSGBasicGeometryNode instance), the scene graph geometry view shows the vertex data associated with that node. This is primarily accessible via the scene graph browser in the \l{Qt Quick 2 Inspector} tool. \image gammaray-qsggeometry.png The scene graph geometry view consists of two parts, the raw vertex data table on the left side, and the vertex preview on the right side. The raw vertex data table shows one line per vertex, the available columns depend on the specific data layout of the corresponding scene graph node. Typically this includes at least the vertex position (named "vertex") and the vertex color. One or multiple vertices can be selected, the selection is also shown in the vertex preview on the right. The vertex preview uses the vertex position to visualize the layout of the vertices. Individual vertices can be selected by clicking on them, the selection can be extended by holding the Ctrl key while clicking on a vertex. The selection is synchronized with the vertex data table on the left. */ gammaray-2.11.3/docs/manual/gammaray-qsgmaterial.qdoc000066400000000000000000000027571413310443000225620ustar00rootroot00000000000000/* gammaray-qsgmaterial.qdoc This file is part of the GammaRay documentation. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. This work is also licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. See . */ /*! \contentspage {GammaRay User Manual} \previouspage{QML Type} \nextpage{Scene Graph Geometry} \page gammaray-qsgmaterial.html \title Scene Graph Material \section1 Overview When having a scene graph node with an attached material selected (such as a QSGGeometryNode instance), the scene graph material view shows details about the material property and the used shader code. This is primarily accessible via the scene graph browser in the \l{Qt Quick 2 Inspector} tool. \image gammaray-qsgmaterial.png The upper half of the material view shows the properties of the QSGMaterial instance. This works similarly to the generic \l{Properties} view. On the lower left a list of associated OpenGL shaders is displayed, the lower right side shows the source code of the currently selected shader. */ gammaray-2.11.3/docs/manual/gammaray-qsgtexture.qdoc000066400000000000000000000044151413310443000224550ustar00rootroot00000000000000/* gammaray-qsgtexture.qdoc This file is part of the GammaRay documentation. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. This work is also licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. See . */ /*! \contentspage {GammaRay User Manual} \nextpage{Paint Analyzer} \previouspage {Scene Graph Geometry} \page gammaray-qsgtexture.html \title Scene Graph Texture \section1 Overview When having a scene graph node or a QQuickItem with an attached QSGTexture selected selected, the scene graph texture view shows the texture content as sent to the GPU. This can be either an Individual texture or part of a texture atlas. In particular, the following elements are supported: \list \li QML Image elements. \li QML Text elements using distance field rendering. \li Texture-backed shader effect elements. \endlist This is primarily accessible via the scene graph browser in the \l{Qt Quick 2 Inspector} tool. \image gammaray-qsgtexture.png The texture view offers a number of generic interactions also available in other image inspection views in GammaRay, such as: \list \li Zooming \li Distance measurements in source coordinates \li Color picking \endlist Additionally, texture-specific diagnostics are available checking for common issues such as: \list \li Fully transparent borders, which can be optimized by cropping the texture. \li Repeating horizontal or vertical content, which can be optimized by using a BorderImage element. \endlist If any of these issues are detected, a summary text at the bottom of the view will list the amount of memory that can be saved, and a diagnostic overlay will show the affected regions of the texture. */ // TODO screenshot for the various texture issues gammaray-2.11.3/docs/manual/gammaray-qt3d-inspector.qdoc000066400000000000000000000072521413310443000231230ustar00rootroot00000000000000/* gammaray-qt3d-inspector.qdoc This file is part of the GammaRay documentation. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. This work is also licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. See . */ /*! \contentspage {Qt3D Inspector} \nextpage {Styles} \previouspage {Graphics Scene Inspector} \page gammaray-qt3d-inspector.html \title Qt3D Inspector \section1 Overview The \l{Qt 3D} inspector allows you to browse the entity tree and the frame graph of a \l{Qt 3D} scene. If the target application contains multiple \l{Qt 3D} engine, you can select the desired Qt3DCore::QAspectEngine in the combo box at the top. On the left side you can switch between two views with the tabs at the bottom: \list \li The entity tree of the scene. \li The frame graph composing the render settings of the scene. \endlist \section2 Entity Tree The entity tree shows the hierarchy of the Qt3DCore::QEntity instances of the scenes. The checkbox next to each entity shows the state of the entity enabled state (see Qt3DCore::QEntity::enabled), which can also be changed this way. The context menu on the entity tree allows navigation to other views of the entity object. \section2 Render Settings The frame graph tree shows the hierarchy of the Qt3DRender::QFrameGraphNode instances composing the Qt3DRender::QRenderSettings of a scene. The checkbox next to each node shows the state of the frame graph node enabled state (see Qt3DRender::QFrameGraphNode::enabled), which can also be changed this way. The context menu on the entity tree allows navigation to other views of the node object. \section1 Geometry Inspection When selecting an entity with a geometry component attached, or a geometry component directly in a different view, the \l{Qt3D Geometry Inspector} tab is shown on the right hand side. The geometry inspection tab has two views: \list \li A graphical wireframe view of the geometry with various diagnostic shading modes. \li A raw data view on its associated Qt3DRender::QBuffer instances. \endlist \section2 Wireframe View The wireframe view renders the selected geometry separately, so it can be inspected for defects easily. Common issues involve: \list \li Wrong triangle ordering. Toggle backface culling in the toolbar above the wireframe view to get culled back faces highlighted. \li Wrong normal vectors. Toggle the normal vector visualization to get vertex and face normals displayed. \endlist \image gammaray-qt3d-geometry.png You can move the camera in the wireframe view using mouse (left and middle mouse buttons) or keyboard (arrow keys, page up/page down, W/A/S/D). \section2 Buffer View You can switch to the raw buffer view in the toolbar of the \l{Qt3D Geometry Inspector} tab, and select the Qt3DRender::QBuffer instance to inspect in the combo box at the top right. \image gammaray-qt3d-buffers.png The buffer view then shows the buffer data decoded based on its associated Qt3DRender::QAttribute instances. \section1 Examples The following examples make use of the Qt3D inspector: \list \li \l{qt3d-geometry} \endlist */ gammaray-2.11.3/docs/manual/gammaray-qt3dgeometry.qdoc000066400000000000000000000113621413310443000226700ustar00rootroot00000000000000/* gammaray-qt3dgeometry.qdoc This file is part of the GammaRay documentation. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. This work is also licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. See . */ /*! \contentspage {GammaRay User Manual} \previouspage{Widget Attributes} \nextpage{Application Attributes} \page gammaray-qt3dgeometry-inspector.html \title Qt3D Geometry Inspector \section1 Overview The \l{Qt 3D} geometry inspector allows you to look at vertices and their associated attributes used by the \l{Qt 3D} renderer in various ways. The geometry inspector is available when one of the following objects has been selected: \list \li A Qt3DRender::QGeometry instance. \li A Qt3DRender::QBuffer or a Qt3DRender::QAttribute belonging to a Qt3DRender::QGeometry. \li A Qt3DCore::QEntity with a geometry component attached to it. \endlist \image gammaray-qt3d-geometry-wireframe.png The \l{Qt 3D} geometry inspector provides a wireframe view of the vertices, as well as a raw buffer view. It is most easily accessible from within the entity tree view of the \l{Qt3D Inspector}. The wireframe view supports a number of interactions: \list \li Left mouse button drag changes the view direction. \li Middle mouse button drag moves the camera in an orbit around the geometry. \li Arrow keys and W/A/S/D keys allow to move the camera in a first person view. \li Mouse wheel changes zoom. \li Left mouse click on a triangle face selects the corresponding elements in the raw buffer view. \endlist \section2 Backface Culling The wireframe view by default does backface culling as the renderer would do as well. This can be disabled via the corresponding action in the toolbar at the top of the wireframe view. The culled faces are then shown in red, like in the following screenshot. \image gammaray-qt3d-geometry-backface-culling.png \section2 Normal Vectors The \l{Qt 3D} geometry inspector can also visualize normal vectors, assuming there is a corresponding Qt3DRender::QAttribute present in the vertex buffer. Normal vector visualization is enabled using the corresponding toolbar action in the wireframe view. \image gammaray-qt3d-geometry-normals.png Vertex normals are shown in blue, face normals are shown in yellow. \section2 Diagnostic Shading Modes The 3D geometry view can use a number of different diagnostic shading modes to highlight specific aspects of the vertex data. Some shading modes depend on the availability of specific vertex data (such as normals, texture coordinates or tangents), and are only selectable if corresponding vertex attributes exist. \list \li \uicontrol{Wireframe}: No shading is applied to faces, allowing you to see through the model. \li \uicontrol{Flat}: Just colors faces with a solid color. \li \uicontrol{Phong}: Basic phong shading is applied, which can help to validate correctness of vertex normal data. \li \uicontrol{Texture Coordinates}: Uses the vertex texture coordinates for the green and blue color channels. \li \uicontrol{Normal}: Uses the vertex normal as the vertex color. This is useful to spot discontinuities in surface normals. \li \uicontrol{Tangent}: Uses the x, y and z component of the vertex tangent and vertex color. \li \uicontrol{Color}: Uses the vertex color attribute for shading. \endlist \image gammaray-qt3d-geometry-diagnostic-shading.png The diagnostic shading mode can be selected using the \uicontrol{Shading} combobox in the toolbar. \section2 Raw Buffer View You can switch to the raw buffer view in the toolbar on top of the \l{Qt 3D} geometry inspection view. A combobox as the top allows you to select the Qt3DRender::QBuffer instance to inspect, typically there is one or more vertex buffer and one index buffer present. The buffer view shows a tables with the buffer data decoded based on its associated Qt3DRender::QAttribute instances, resulting in one or more columns per attribute. \image gammaray-qt3d-geometry-buffers.png \section1 Examples The following examples make use of the \l{Qt 3D} geometry inspector: \list \li \l{qt3d-geometry} \endlist */ gammaray-2.11.3/docs/manual/gammaray-qtcreator.qdoc000066400000000000000000000066461413310443000222560ustar00rootroot00000000000000/* gammaray-qtcreator.qdoc This file is part of the GammaRay documentation. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. This work is also licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. See . */ /*! \contentspage {Tools} \nextpage {Command Line Interface} \previouspage {GammaRay Client} \page gammaray-qtcreator.html \title Qt Creator Integration \section1 Overview The GammaRay \l{Qt Creator} integration plug-in allows you to interact with a target application with GammaRay conveniently from within Qt Creator. The \l{GammaRay Client} is embedded inside Qt Creator in this case, and you can directly launch, attach to or connect to target applications using the currently active project configuration. Code navigation from GammaRay will directly open the corresponding source file inside Qt Creator. \image gammaray-qtcreator.png The GammaRay Qt Creator integration plug-in is only available in the \l{Qt Automotive Suite}. \section1 Launching Applications Launching a target application with GammaRay from within \l{Qt Creator} is as easy as starting it in the debugger. Just select \uicontrol Analyze > \uicontrol {Start with GammaRay}. Your target application will be started and the GammaRay client appears inside Qt Creator. All necessary configuration for GammaRay is automatically determined from the project settings in Qt Creator. The following remote targets are supported: \list \li Generic Linux targets using SSH \li QNX targets using SSH \li Linux Boot2Qt targets \endlist \section1 Attaching to an Application To attach to a locally running application select \uicontrol Analyze > \uicontrol {Attach GammaRay to a local running process}. This will show you the standard process selection dialog of \l{Qt Creator}. Select the process you want to attach to and press \uicontrol{Attach to Process}. After attaching the GammaRay client will be shown inside Qt Creator. \image gammaray-qtcreator-attach.png \section1 Connecting to a Running Application It is also possible to connect to a target on a remote device that has already been injected with GammaRay. This option is available under \uicontrol{Analyze} > \uicontrol{Connect GammaRay to a remote running process}. Selecting this option will show the connection dialog depicted in the following screenshot. \image gammaray-qtcreator-connect.png GammaRay tries to automatically discover viable targets on your local network, these are displayed in the list view in this dialog, and can be used by selecting the corresponding items. Discovered targets using an incompatible GammaRay version will be shown but will be disabled for selection. Should GammaRay not be able to discover the desired target automatically, the network address of the target can also be specified manually in the entry field on the top. This might be necessary depending on your local network setup. */ gammaray-2.11.3/docs/manual/gammaray-qtquick2-inspector.qdoc000066400000000000000000000123361413310443000240120ustar00rootroot00000000000000/* gammaray-qtquick2-inspector.qdoc This file is part of the GammaRay documentation. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. This work is also licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. See . */ /*! \contentspage {Tools} \nextpage {Widget Inspector} \previouspage {Tools} \page gammaray-qtquick2-inspector.html \title Qt Quick 2 Inspector \section1 Overview The Qt Quick 2 inspector is the tool of choice for investigating issues with a \l{Qt Quick} user interface. This includes: \list \li Inspecting the visual item hierarchy. \li Remote view of the rendered content with diagnostic overlays. \li Inspecting the scene graph used for rendering the user interface on the GPU or via the software renderer. \endlist If your target has multiple QQuickView instances, the combo box at the top can be used to switch between them. \section1 Item Tree The item tree is shown on the top left, and it contains the visual hierarchy of QQuickItem instances in the currently selected Qt Quick scene. This does not necessarily match the QObject parent/child hierarchy. The right side contains universal \l{Object Inspection} view. \image gammaray-qq2-inspector.png You can navigate to the object browser (and other applicable tools) using the context menu on the item tree. Similarly, you can navigate to the source code locations instantiating the current item, or the file declaring the type of the current item (if available). \section1 Remote View The remote view is shown to the lower left in the Qt Quick 2 inspector. Besides giving you a zoomable view of the rendered content, it provides a number of additional features: \list \li Element picking. \li Diagnostic overlays with layout information. \li Input event forwarding, for working with embedded targets. \li Color picking. \li A measurement tool. \li Taking screenshots of the target output, with or without diagnostic overlays. \endlist \image gammaray-qq2-qsg-visualize.png The diagnostic render modes include: \list \li Clipping visualization: Highlights items using clipping, which is a potentially expensive operations on the OpenGL render backend. \li Overdraw visualization: Allows to spot out-of-view elements that are drawn, as well as elements that are drawn on top of each other, both of which are potentially expensive operations. \li Batch visualization: Highlights items that are in the same render batch on the OpenGL renderer, as well as unmerged batches. Unmerged batches, and more batches in general, can have significant render performance impact with the OpenGL renderer. \li Change visualization: Highlights areas causing view updates. \li Control tracing: Highlights Qt Quick Controls 2 outlines, which can help to identify layouting issues. \li Showing a customized layouting grid. \li Slowing down the animation timer to diagnose motion design issues. \endlist Diagnostic render modes can be enabled individually, both on the target and the GammaRay client via the \uicontrol{Quick Scenes} > \uicontrol{Decorate Target} menu option, or the corresponding remote view toolbar action. \section1 Paint Analyzer When selecting a QQuickPaintedItem sub-class in the item tree, the paint analyzer tab is activated in the property view on the right. \image gammaray-qqpainteditem-paint-analyzer.png The \l{Paint Analyzer} allows you to inspect every single QPainter command that is executed, and look at the visual result after each step. When using the Qt Quick software renderer, a full-scene paint analysis is also available via the \uicontrol{Quick Scenes} menu (requires Qt 5.9.3 or newer). \section1 Scene Graph The scene graph view shows you the internal representation used by \l{Qt Quick} to render the current scene. This is particularly useful for debugging performance issues related to batching and rendering of custom QQuickItem sub-classes. \image gammaray-qq2-geometry.png For nodes in the scene graph you have access to: \list \li QSGNode properties, in particularly node flags and transformation matrices. \li Shader code, if the node has a material associated with it (see \l{Scene Graph Material}). \li Geometry and other per-vertex data, when looking at a geometry node (see \l{Scene Graph Geometry}). \li Texture data for image elements, shader effects or distance-field rendered text elements (see \l{Scene Graph Texture}). \endlist \section1 Examples The following examples make use of the Qt Quick inspector: \list \li \l{quick-event-handling} \li \l{quick-batching} \endlist */ gammaray-2.11.3/docs/manual/gammaray-signal-plotter.qdoc000066400000000000000000000025521413310443000232060ustar00rootroot00000000000000/* gammaray-signal-plotter.qdoc This file is part of the GammaRay documentation. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. This work is also licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. See . */ /*! \contentspage {Tools} \nextpage {Timers} \previouspage {Messages} \page gammaray-signal-plotter.html \title Signal Plotter \section1 Overview The signal plotter visualizes the lifetime of all QObject instances on the target (gray horizontal bars), and shows the emission of every signal on the target (vertical black bar on the emitting sender QObject). \image gammaray-signal-plotter.png Tooltips on each signal emission show information about the signal, including the signal name and the time of the emission. \section1 Examples The following examples make use of the signal plotter: \list \li \l{signal-slot} \li \l{timer} \endlist */ gammaray-2.11.3/docs/manual/gammaray-stack-trace.qdoc000066400000000000000000000030071413310443000224370ustar00rootroot00000000000000/* gammaray-stack-trace.qdoc This file is part of the GammaRay documentation. Copyright (C) 2018-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. This work is also licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. See . */ /*! \contentspage {GammaRay User Manual} \previouspage {Class Info} \nextpage {Bindings} \page gammaray-stack-trace.html \title Stack Trace \section1 Overview The stack trace view is available for all QObject instances and shows the call stack leading to the creation of this object. \image gammaray-stack-trace.png The stack trace view shows the function name and source code location for each call, as far as this information is available. You can navigate directly to the corresponding source code using the context menu. Source locations are usually only available if debug information are found for the corresponding executable or library. Object creation stack traces are only gathered on platforms where this is possible with limited runtime cost, and therefore are not available for all target platforms. */ gammaray-2.11.3/docs/manual/gammaray-standard-paths.qdoc000066400000000000000000000040621413310443000231550ustar00rootroot00000000000000/* gammaray-standard-paths.qdoc This file is part of the GammaRay documentation. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. This work is also licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. See . */ /*! \contentspage {System Information} \previouspage {Text Codecs} \nextpage {Object Inspection} \page gammaray-standard-paths.html \title System Information \section1 Overview The system information view shows a number of information about the environment the inspected application runs in, such as the used Qt build, the standard paths and the environment variables. \section2 Qt Installation The \uicontrol Qt tab shows information about the Qt build used by the inspected application. This includes the version as well as information about the build configuration and architecture, as well as the installation location of Qt. You can also find details about the platform the application runs on (OS, version, architecture, etc) here. All information here is retrieved via QLibraryInfo and QSysInfo and therefore reflects Qt's view. \section2 Standard Paths The standard paths view provides access to the information of QStandardPaths. For each standard location (see QStandardPaths::StandardLocation) it shows the readable and writable locations as seen by the target process. \image gammaray-standard-paths.png \section2 Environment Variables The \uicontrol Environment tab shows a table of all environment variables and their corresponding value, as seen by QProcessEnvironment::systemEnvironment() in the target process. */ gammaray-2.11.3/docs/manual/gammaray-state-machine-debugger.qdoc000066400000000000000000000027421413310443000245470ustar00rootroot00000000000000/* gammaray-state-machine-debugger.qdoc This file is part of the GammaRay documentation. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. This work is also licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. See . */ /*! \contentspage {Tools} \nextpage {Messages} \previouspage {Web Inspector} \page gammaray-state-machine-debugger.html \title State Machine Debugger \section1 Overview The state machine debugger allows you to graphically observe the configuration of state machines in realtime. \image gammaray-qsm-debugger.png The following state machine frameworks are supported: \list \li QStateMachine \li Declarative State Machine \li QtSCXML (depending on Qt version) \endlist If your target contains multiple state machine instances, the currently displayed one can be selected in the combo box on the top left. \section1 Examples The following examples make use of the state machine debugger: \list \li \l{state-machine} \endlist */ gammaray-2.11.3/docs/manual/gammaray-styles.qdoc000066400000000000000000000024211413310443000215600ustar00rootroot00000000000000/* gammaray-styles.qdoc This file is part of the GammaRay documentation. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. This work is also licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. See . */ /*! \contentspage {Tools} \nextpage {Web Inspector} \previouspage {Qt3D Inspector} \page gammaray-styles.html \title Styles \section1 Overview The Styles tool is used for working on and debugging custom QStyle sub-classes. \image gammaray-style-controls.png This tool allows you to look at: \list \li style primitives in all states, \li controls in all states, \li complex controls and their sub-element areas, \li built-in standard pixmaps, \li and the style-defined QPalette. \endlist It also lets you view and edit style pixel metrics. */ gammaray-2.11.3/docs/manual/gammaray-text-documents.qdoc000066400000000000000000000030011413310443000232130ustar00rootroot00000000000000/* gammaray-text-documents.qdoc This file is part of the GammaRay documentation. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. This work is also licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. See . */ /*! \contentspage {Tools} \nextpage {Resources} \previouspage {Models} \page gammaray-text-documents.html \title Text Documents \section1 Overview The text document inspector allows to introspect the document object tree of QTextDocument instances found in the target application. \image gammaray-text-documents.png The document list on the left side shows all QTextDocument instances, selecting one of those shows the document object tree in the top-right view. Navigation to other views is available using the context menu. The document object tree on the upper right shows the tree of elements composing the text document. This includes blocks (see QTextBlock), fragments (QTextFragment), tables (QTextTable), and so on. Selecting an element shows the corresponding formats in the format view at the lower right. */ gammaray-2.11.3/docs/manual/gammaray-timertop.qdoc000066400000000000000000000037771413310443000221170ustar00rootroot00000000000000/* gammaray-timertop.qdoc This file is part of the GammaRay documentation. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. This work is also licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. See . */ /*! \contentspage {Tools} \nextpage {Events} \previouspage {Signal Plotter} \page gammaray-timertop.html \title Timers \section1 Overview The timer view lists all active timers inside the target application, as well as their desired and actual firing intervals. This includes QTimer instances, but also any QObject that is receiving raw timer events (see QTimerEvent). This tool is particularly useful for finding timers that fire too often and thus cause unnecessary system wakeup on energy constrained devices. \image gammaray-timertop.png The list view shows the following information: \list \li The time object name/address. \li Whether or not a timer is active, and if so, does it fire in single shot or continuous mode, and at what interval. \li The amount of wakeups triggered by a timer, that is how often it has fired so far. \li The average time it took to process a timer's timeout signal. \li The maximum time it took to process a timer's timeout signal. \li The timer id, which is mainly relelvant for raw timer events rather than QTimer instances. \endlist The context menu allows to navigate to different views for the timer objects. \section1 Examples The following examples make use of the timer view: \list \li \l{timer} \endlist */ gammaray-2.11.3/docs/manual/gammaray-tools.qdoc000066400000000000000000000051141413310443000213770ustar00rootroot00000000000000/* gammaray-tools.qdoc This file is part of the GammaRay documentation. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. This work is also licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. See . */ /*! \contentspage {GammaRay User Manual} \nextpage {Qt Quick 2 Inspector} \previouspage {Command Line Interface} \page gammaray-tools.html \title Tools \section1 Overview GammaRay consists of a number of specialized tools to inspect various aspects of Qt. You can select these using the tool selector on the left of the GammaRay window. \section2 User Interface Inspection The following tools can be used for inspecting graphical user interface issues, such as layouting, rendering, or event handling. \list \li \l{Qt Quick 2 Inspector} \li \l{Widget Inspector} \li \l{Graphics Scene Inspector} \li \l{Qt3D Inspector} \li \l{Styles} \li \l{Web Inspector} \endlist \section2 Application Behavior Inspection The following tools can be used to observe application behavior at runtime. \list \li \l{State Machine Debugger} \li \l{Network} \li \l{Messages} \li \l{Signal Plotter} \li \l{Timers} \li \l{Events} \li \l{Wayland Compositors} \li Script Engine Debugger \endlist \section2 Model and Data Structure Inspection The following tools allow you to inspect and manipulate models and data structures of your application at runtime. \list \li \l{Object Browser} \li \l{Actions} \li \l{Models} \li \l{Text Documents} \li \l{Resources} \li \l{Translations} \li \l{Meta Object Browser} \li \l{Meta Type Browser} \li \l{Geo Positioning} \li QtIvi Inspector \endlist \section2 System Information The following tools allow you to inspect information of the Qt installation and the system it's deployed on. \list \li \l{Font Browser} \li \l{Locales} \li \l{Mime Types} \li \l{Network} \li \l{Text Codecs} \li \l{System Information} \endlist */ gammaray-2.11.3/docs/manual/gammaray-translator-inspector.qdoc000066400000000000000000000046411413310443000244400ustar00rootroot00000000000000/* gammaray-qresource-browser.qdoc This file is part of the GammaRay documentation. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. This work is also licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. See . */ /*! \contentspage {Translations} \nextpage {Meta Object Browser} \previouspage {Resources} \page gammaray-translator-inspector.html \title Translations \section1 Overview The translation inspector allows you to observe and override translation lookups on active QTranslator instances at runtime. This is useful for debugging translation issues as it also shows context and disambiguation information, as well as for verifying UI layouting as the length or format of translated strings change. \image gammaray-translations.png The translation inspector lists all active QTranslator instance on the left hand side. These are not necessarily all QTranslator instances that exist in the target application, but those registered with QCoreApplication::installTranslator, as only those are considered for translation lookup. Selecting a translator on in the left view shows the corresponding translations that have been looked up and found in its catalog in the translations view on the right. This view contains the following information: \list \li The message source text. \li The context (typically the class in which the message is used, unless manually specified). \li A disambiguation message, if present. \li The translated message. This is editable inline. \endlist Translated messages can be edited in the translations view. To revert changes made there, use the "Reset Selected Translations" action found in the translations menu or the context menu on the translations view. For changes to take effect, it's often needed to notify the target application via a QEvent::LanguageChange event, this can be triggered via a corresponding action in the translations menu. */ gammaray-2.11.3/docs/manual/gammaray-wayland-compositors.qdoc000066400000000000000000000034711413310443000242610ustar00rootroot00000000000000/* gammaray-wayland-compositors.qdoc This file is part of the GammaRay documentation. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. This work is also licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. See . */ /*! \contentspage {Wayland Compositors} \nextpage {Object Browser} \previouspage {Events} \page gammaray-wayland-compositors.html \title Wayland Compositors \section1 Overview The Wayland compositor inspector allows you to browser QWaylandClient instances and their associated resources, as well as observe Wayland events. For this tool to be available you need at least Wayland 1.12 on the target. \image gammaray-wayland-compositor.png Wayland clients are listed in the view on the top left, showing their process id and executable. These correspond to QWaylandClient instances, the context menu allows navigation to other views for these objects. Selecting a client object will show its associated Wayland resources in the resource tree on the right side. Selecting a resource will show further information for it, in particular there is a live preview of Wayland surface resources on the center left. At the bottom there is a log view and a timeline view of Wayland events for the selected client. Hovering over the timeline shows further information for the event under the mouse cursor in a tooltip. */ gammaray-2.11.3/docs/manual/gammaray-web-inspector.qdoc000066400000000000000000000024071413310443000230220ustar00rootroot00000000000000/* gammaray-web-inspector.qdoc This file is part of the GammaRay documentation. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. This work is also licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. See . */ /*! \contentspage {Tools} \nextpage {State Machine Debugger} \previouspage {Styles} \page gammaray-web-Inspector.html \title Web Inspector \section1 Overview The GammaRay web inspector tool allows you to inspect HTML5 content inside a QWebEngineView. \image gammaray-web-inspector.png The web inspector enables access to the built-in diagnostics tool, which allows you to: \list \li inspect and manipulate the DOM tree, \li debug JavaScript, \li observe network operations, \li and profile the web content for performance issues. \endlist */ gammaray-2.11.3/docs/manual/gammaray-widget-attributes.qdoc000066400000000000000000000021611413310443000237050ustar00rootroot00000000000000/* gammaray-widget-attributes.qdoc This file is part of the GammaRay documentation. Copyright (C) 2018-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. This work is also licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. See . */ /*! \contentspage {GammaRay User Manual} \previouspage {Paint Analyzer} \nextpage {Qt3D Geometry Inspector} \page gammaray-widget-attributes.html \title Widget Attributes \section1 Overview The widget attribute inspection tab allows you to see and toggle QWidget attributes (see Qt::WidgetAttribute). This is available when selecting a QWidget instance in e.g. the \l{Widget Inspector}. \image gammaray-widget-attributes.png */ gammaray-2.11.3/docs/manual/gammaray-widget-inspector.qdoc000066400000000000000000000035531413310443000235330ustar00rootroot00000000000000/* gammaray-widget-inspector.qdoc This file is part of the GammaRay documentation. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. This work is also licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. See . */ /*! \contentspage {Tools} \nextpage {Graphics Scene Inspector} \previouspage {Qt Quick 2 Inspector} \page gammaray-widget-Inspector.html \title Widget Inspector \section1 Overview The widget inspector allows you browse the QWidget and QLayout hierarchy of your target application. \image gammaray-widget-inspector.png \section1 Remote View Similar to the \l{Qt Quick 2 Inspector}, the widget inspector has a zoomable remote view of the selected window to the lower left. It offers: \list \li Widget picking. \li A diagnostic overlay with the layout information. \li Input event forwarding, for working with embedded targets. \li A measurement tool. \endlist \section1 Paint Analyzer When selecting a QWidget sub-class in the widget tree, the \l{Paint Analyzer} tab is activated in the property view on the right side. The paint analyzer allows you to inspect every single QPainter command that is executed, and look at the visual result after each step. \section1 Examples The following examples make use of the widget inspector: \list \li \l{signal-slot} \li \l{widget-layouting} \endlist */ gammaray-2.11.3/docs/manual/images/000077500000000000000000000000001413310443000170375ustar00rootroot00000000000000gammaray-2.11.3/docs/manual/images/gammaray-action-inspector.png000066400000000000000000004040421413310443000246260ustar00rootroot00000000000000PNG  IHDRE pHYs+ IDATxwpWo B z+Roڨ[m5}wknbwf6bn{4=ZHшh@O0@{PUyY"P"EyQi_fUe'9gfeAAGb   ["AAxĈP&&&  | 7 #6FFG;o,3<<(N |%ʲqSSw}_,atl ~Fz89?Kfo  |3 #'Oq[Z9r8Sӄan7nY x<^\.~?M$Y<" w~ֶ6>17kY|Ȳxdz:ɲL(" D!p-XȲL8^[> !r8U^>Is%Νo ȲL ˜:s@ @͊n<O3D"x<\nm}ײ,q܄Bؼ@ # P-5/$Ŧ?qiv۶nfllM0=m#55G]m ^/`MKXQM B$$x((癧={i ΎǷq{i*5{㇯Ryp:fw`Z RX̸\.GF(,(<P(hZrsrpTyyئm~$p|[/$IڽFCuU%}}465384(9z?{g+V27%j4n^}e:`[.R\ڽI0x8zK$['~AAݤ0V/;-Z$A`HsoO׿g?{ ś?2Vj5\R+~';+Jtx Μ959s<-VbvvY\.76t 0֙#b'j2),,l")$@d6I7xgٳw?z7\llbphQT* (/-7Q-6QXFQa!?|{lۺaΜ;ϛ? ?Q($$I")@Nvmto~nwhи`_|  ~AAXKj4=FFGc Ȳ̾XYWGF0ȹ˪HR@VĚL5<浵_fv*._ @m ~8{<\򕫬Y υFJK?qI:Y230ͤ[Ðd KJ{~`0kU(HD4 =M_&  w^cl|lټ!|6mXOUeł凇GHXP(3E  X2T|[p.t+h4jΝo b!77d bO/9///N9GA~^tzN$QYQN˥6V $'[FP`6?p8bFѠVl2111$'CYi)z}C4@&= ^=VVR 1o=@xbvRSSf j𑣬]::s#25Uw$ J9@0o=E8Nz,7dgbrǶl <%HF+/R->҂ g]`fvCb"۶n'aKAA/<0$)x" RIjj2IFeeluu5$l  ܹPAA;D}AAGAA1"AAxĈPAZ_⑐E PPYAĂpԣaő,T  _MՐ cTA$ zaAAe095CŦ; mAATJ%%S  熇G^b iyS#XsbdkV.S#HHg~}P~z{x<(JrsrX_{{HHD<- ݳd(2Oebd%%P0(*J͡Dp( %Ԍln`O'C]t6q3]mfbdv!G"z1ȲLs%zzzQ(!)$23DAS\T9PR\Du+00ծ`zڶG' `Q ,w^atH$(U706SF@Jz&9t\j`*O="̱=bam䕔珿I"da|dۣ45eH}jy,3pA ssDZ-:]<~ZRDZ^0~  ׵( ^mBr}'$0>F=Aʒ>+=7  ޒUWut:ݗnxjj IYiilZgW7GGRn}6tIAa-YϟMop8|k7'O&LSc|jll|+o'ddS_K=^cǙe=;^O$BE"6PMlv}};N(bvn{r "9v' cc.v2F>}LOnk~q~cS3Pv7iۭq-s@K~{[4W tҲK`>>Bk[;Zm[pӃYmmtv>;r>+U*۲}>8w]Oadtѱ1?@Mu59D"+PT\ll NMM]45,>99Ăi-UґcǙs|tvu/s͹8x{nw0<<ḭm|w[I HYa=@00ǭkQ(?⣏wss9 4\l;::n>ع/Rرq @L$I h4 BB!-ꜽv* h|IjkVRz}9$ JFF:k׬$s.?t0sfu)'N )/+%>>A+8N2334hDd&''fJZ ZY,˴_S[S$I\llBS]UZtyq ,ꢸM=^3O?㥵 YZMGg*keLls K]m ZAFFFB^C"N ^d FYrY*h\zs|33#;+ ł^($w̷;y'Qk4 ax ().F\+affi;9c[6p:i|Bⴄ#RFV'`LJšV,=Rk۲ayx>vCyY/<`K$&f BAZZ*>;©g().-s=Nehxi^[ 33?TTQZRLWw7ׯL$  ٸ~=yyx}{s,}dgvj={I2\nm!:DVf/4p: >stupr"Prj{z9P(#Gq:(|efd`2\@zzj9r8v={}`0øn kzKgUmP^VJVV&@baȲ={1M\x)N< }p8GFx<v$Վ?=xVCww/=}w.65 ϓnD\n7P` ȉS좫FgG2?aXVl6;;):LLNԟ<$gΞh4288t׏!..Lbb"@ 1$&rivtمPxY0+kk!''kl{1jdg yn|T CC$$fxsɲlvZ >M7 v004,_,P(R"9BUU%]4\۩YQ͉Sj(iY4j5o}ώ#_RHRnñ$՟vcYUWʺJKi`ӆuL&.\ld~~˗zOJxTHK׮3g!2M-~|KV D 쬻~t Il2vj^/>ރ秫 "K@Yi 8uo"9Y=v˅J&QpMUeEnjlj& ՚1)kVǎkxx2cM+)*,.$L GsY 0,sk(޳_z JK195SOvs1j57n$=J@vV@RAeE9$ʲL$"cLJ"` 55 @ خ(1 1):AfE՗'%#=}xJKcִE~0FFiiⴔMJEVfYY zFC i@!I޳a ]4\búdgfq|9L~p8LNN6P0DyY)cXؼi#'N2;7GB 맬-4jZ6_˞I2hmo'3#D=>;-Z-I(h,Q]] 뙚BKIN1R+z/ZP*c- $ស')@Fz: g:JTV3:6ƓO`hxɩiZkxkkVPZRRd2޴_5ػ?7nd6MyYj#I2@yy8f][vtLyY.|\.&Ktv%#V{-IJ2fffچስ}qeR[={?K74b/zޟhd-q81<2ZŅZfxdtk:PB0 tuwc4&@(ff#bH$c˦?E(dE/_.w!T̄WUKbIHOrss(-)f|bNGIQYMF`ffѱ1JK'BVzJt:W(.*$?/+T*RR],C0p MӑN`H$##ʊrFFǐ$E-3D aXPt:233HZсdjM{h=K|||쳬Ѩ)/`IYI $&9?/ήn4 YY099*݊>!kVHI(5+l2zHMIaEu:]< y )GC Cnf tvudH"3#^>'$$PZRL8a`pɰZs1MdfdPZRnv.Q IKKEgxdUudg}L?J@]/c'$(/+%rزyz&qZ-fd͡`brsy eyABIOommAӑc1jL& #))U+눏6jLF#=}䓛CWWdge LZ ‚|#5-5{P\D^^7P\T"b è0qo$wx@Mx#4;"͈i5t4 7-ApЏA/Wy؏Ox>h%Zj*▻  L|utvw߿=zog2/v ѢW;:_=>?K嫋݋C(" 2#{9m[)+-a/p'o;47_šF||ڵ?czچZBuC*Y9t[l&..:pώCr RIk[;u5+L&&VZrٲi#=lX5kV1pÝ8p_|DaA>)2~.*i(]]TWWb68|W\e`pu93N/RYQ`jjg3ʺlv;%EK684LcceNv~ʊr\n7/\d:p^B>wA0bݚw`~Jqq>>;J\\r$±z:e:N>CqqT14<ՎNFG輯onXpzzL@4ftt ZMZZ*6`(DJr2*Tjz=x^ě nw03;>AlbdtH8BJJ2CG!2yyyHDdz B HZQ(Ȥ[BLOO#Iq82cRd3;7DVuWJP0H8A0Lt>rE Ü9w)q\.7\EѠT*))֞ 힏]L#qZ-qLFTJ`IⶓkZŏ˲LVB0p8$IχZ!7;`6b<?qX=ʊ%BvV{[#Ad4 ͅ^hǶl j֮YͥKm{.gRN?1x&'rl1@Yi){g8gfimkg~Ig+ $//$)z=,*, ?Y֬^InN6))lٴI8rJMgg.\eAΏɦ(-)"7'hqzb;H?y9[9ںӡGO`5h7z}[6ozhw-l8-99|>FPiT4 9J n7sKs8"2pP0DJJnQT[p_kFCRdjjNÚFﵤSXҘgF/'zTUS))def24a:<^/ES$6_GUe^>+ǮRVV¼{ z~L[)/+mB$hktt%%ȕNL&#b]rb>W;bn7u5+8|֯%//9"?0]Q CP8*eULRVZr_HĶǶ;qϓBss j:vPHkB,Ip,cwx]zQo҂և/]ώ(c~޳YW$󌎍nE's" ƒB_ǿDG$&..$vG4$q8{QyIIXYF#sss,f3N9SRVy^ZNbFP`QU(UX633L Es3*p8Xf&'hTњɄB0SSSw%;$c *)50LhZJ%֭% ,j5C"}!#=trZx(+EѠQATT*((g|bMaI61XBLFVֱz23btNv6i))f 󙘜")@Vf&yy22)/-!77ٴ DT2;7ǚ+ٰ~-kZQTדn'%|kllv;Vkedc1'ꙜbuLF ǑJu튋b11IKMtzeYY[HIIغyS$1i\ItQ*[΢ ?Ʉbc[-l^Mqx}^6_GJLVVfKt/Wl1SR\l6zeDb6l!Q'c2|o%ItA0n{rrrQa>A3F B Q(I|p8(JY#..L),(ּͻqSXP$s.=GF ~q0224+qqZGeՊB!1:Msb4=d‚s͡P(HOOg!KP7y5/qAdDF}F4  zM߼)=x%rx("Aa}W5t_  J e"  @G 1s&:,B `rjEY7##v3>>劕[efff#2ss.&'?ww"2.I|~?vp8`n dYfvv <O<\jm̹tuuDg319l&5%p8̴͆fGe<^/iՕ8N~ennH$}9;%-%-:FC(yA{@Mv#׆:r8`ΐ |k7>VNG5SϱqظU>NF!x%;+"ؾ ~?`3ŖIHH`vvgvf1z{p?`úQQ^ᤧBDRsQRTD+1\(I ՃͲe2sf*&{ ȏ~}~?b&=݊sfM jv_TU|ߊejljg?yǜ:}'Go++(+-rY.65QW[#?'jܜl.QTX@{4[6oU֬^hpP(45019I^n.++H~-oC8fh4iJ%HBX9 hCol}l3`0 -7ٰn-e%K Gώ/&۱? :++?y >KE 1mG֮j} V~ך#E" á)Gw_{dӄ]#QU($iї!!AGbbRABB͊j %?##0>1$IU*T*F16C PχFA$BpZ-: ʊ. ܑوBr.dx}0aVKJr2kO938gD"2iia'tjӭK@%5%.  p#}w:áxI<#%,9 L$"3;;1)iUΙrsfNNN6^RI0$Q<|   3g|/]gPTP\TD_?'6]V1vj;rݱfaIp |M^~R]ߦhݼΟM?z+W;Dw_~yX6zMsx6jVT-ZOՎN?ElGoGFFٳo?P|v<8}9&o":ر}+c)4ٙ_*C$hYyG]] ׯpsn=t|Wcި'NIaA-On4>1ſMD>UTU.DRUQSO෿=owmH 0f^||(*,$ {7^}CfgHxسoc#2>o'ګSS|g/>׿pM['}"7X2iZ,f Sh4rsrb0&%-X7j?.44v*rsrسo?[΋ 8Vo}~[gxdp8R}9BꦮҒb{<ԓ<xN/_P(ķ_yKmm]ܜl>w*,Ƃir8/Zz^?]՟`TUVr'FVSYQ;z|ٻ. %?dga]4\d x|Cpi/cw_ D^|9 x_3`6addd^x=|#<^/[6m}>f_hf =? 9ػS(gcJiI-PUuhZv;7m ώcrj\}I zzg(/[_WҭVHKMȾ^}^[HNN&n݌:gfft:ϷMd?y֮]priBʑ`LAϼn֟`s'y7En$ RRREyX ,3^Ғb[HEy#voC >;Ba~>E|v%E(bMKEP`1! a10ذn-y<}Aֵrx%Jl/M|8>ڵBFJEUe%cx<^hPӆD'ܶbVFkKXf 5U9V[R*8t+tvu/Woɦy٧,';+Ƕlb|bhk׋`ӆ$[,zYQ<L {$@"e(ʻMΞuӳslltWuu9d)G'A w H!@(H/"n^ܿV 8{". Ʀ& ^ynJW^|êx՗zȒt/]c"-m~^y@N/-lٴSgb♝OC c$$SxZBC1ECcsBb2HIIfj$*}ˎ%y ~Za![_HoLVF:ZYfPքGz~2Pzzz AMs%KJ+Rp.AHiձ~rxȲL]}g]]OkLFEC!1!^UвElvtI _[gwjb/=.hhl";+ں:b5"nǃ~tAFTT+E%H  ,cزi#u8|p:]"Qyz>{ӲUaalڰں:[d>̠Y3'Xt1Ϝzt:- z" T zL&s r9d{\lWv5| UOJJD}ʲٳf7ߦ웧Sfx<^Z-J&8;lȖM7W^NsK+SPU]CR**5bn|^/k׬ v>`Χe啼+?緈e:}%3#/BRSRp8_3{& ?4[ZE0}Ŏ{EN $eW,_ʇAo@zYz%~N`_vd ( :r 1H7bATVVcth5DQZB5֭ NǬ3&oEE4 (" x%'NJUNeƌ\o IDAT&vr޺뛙Am]=n)SҸv?RSSƔӌmw'@tPL"҃N; *yD{ޏ]45qxI0Ԉ SPA ٖ4~,;D .O6 ؚ!3d}CotRUORQHJoRRZ8EVNJl)&2<s-c$ (Jt-k A~eZ=Piq׮zM̟OBlL !wс2#\LJ%Vi(p yaZ@MPoW^T^d:1̉HYAeDJ&v͉%<2:Dd|2,9}Ie 9 Mw-kqxD h{9"j9gnHA 34|cV'R/s,x,I /1#L$$P`wG1/Ld+s_ ʣ-0+e $E~ViЉpۏ^5TZ%2QzXA%# 2V 㑔kN '㒉1,iɔYnpj"(0+BdУr1dtm0>Py~kfX!S< 1(b8js#B>7_ۖ&XLÏ[vsCE2B Fٚ8 : jm2g^3iyn?X;r!7E2$l dEz**w,*:ĩ3gYT<5B_f`pNsE|ۍbxxs/Rp$1<3\~w$Raik.mZ6&hxғp׏W>LU@"VLSFP82'S3EhؖER"A?o|<_*d@T"x56̎P~֑h7o@+*q_"p; *fDAdP"kDS/XVP  E#'q"1NG~ 6 JD1N tmX轇i;&*l5*z BCBg638hL(..jldehnHRb" DFT"#"p\Kll,aapOGC^z_HT;nm^p!,@eX2HQ,^q}R"n\NW]3K zV vvL\~C}q=.D RX<ޔȆ`ЍqXPoG)rBMG)o3IJ~~eXq(ɮ?Nn(Π-Soюteڼ#㖔ͧԯFjL2M?pTYo׹W2ZQ(kw;e:J|lǭBCF'@S*P"@|h@bm#n':78|Qf:^LSQڔWR[JQ.Rکz>osۭtߥwУW;(| =N!GkoIC^%PKcFP"@crʃ H+L\ScQ3vQlEְG+2DU ήnbcHNN`;}V5uDDh @@DdUx^f"IeVh/vJ RQQQyy6MCCRd& piT7TPEEEEEEe",ՑVGe4P=v$H+r]]\>n{wwVu,zp|>Kikn,|Q,446RZVue IWSp:ux<^+*Ee"IU5 "2]޸I[{$p8()- lʣαn*;:'G[{յ;~Xt4462888Kk=Pp:7zWN._A$m** hnn 7r!#j'~Ad~=( 2U+_0:mlj/'{z9p0~?I[{;%446o S**!Z[p=457܂w;}x{p\465;:465dAj1vwPW_@efp8l'{r؉g!dYOCC#ihl`N3˅$IQW߀:4Z4451d=N}C#&p|>-#ICCCtttJVF]ݼGtwg#2}}H)ʷpsBk%eYx}o7S!w4+ضu3y8s$8u81{L|>?յ³:BfF:Wp<@IphH IgpBz=kW&>>w=qv8Z饭ӉOF2/ ϣR/_J፛[!).)erQ^{%`r"I"Dat#[drtx|@x^ z=:OzT<^?i:t1u:/<UhvSV^AhHa>4 nXzOOog7;TT~lŒ’E H̵e#"*RIpEZZhnimrOee3{ RIJJi@ZHNJfD"0ol;:,߾ ]y1eggm&BBCXf۷m&";PLF#M\^8&Oo@ilݼ`r{f'/> Wy.^@fF:SLa/Z_9EKIe~ F7Fi,*&##Pl6;}_R.Y4: 4Neu S! CYyrr0 tuuST\gyh4$?NLl fhȦؑ>аkTT~L?x t:9yT.^?˿z O'w }'.7+ JbVˆukj.ٺy#,\03gAtt4{됍jfϚEEe.\d9dgeDk~6mXGtt&}T23VLfZN6]]]?y3g;}qز&&-5(N>,I$''HXX(n#Oh|RHIN35 L~ DGxp2)ded "))(m#-5ugI$޾|t:-&:p8|-76ZDDx[˝F{G'E%L:]n ՞>)Y3ȿrmm[6!ILF\.7SR)ii445b&TTv}P))}HNaPEEEȱX,ݵF3樨]]446۾|J w]O8y ,xjjj******lve-x^NYv C!.\LXX[7o24 ۶lF9p$!I2?7!++%r:e WA+CEEEEIZZDrWP2wl ]y={Sh4>{D z=sBbrAEt:-kVk diӈÏ>OYy7m`C̚1Ҳ2VZIPPY********?;sfsr>ᄇ008HCcmL3HLLtvveF\.7GtRZVΥKaZDF`GOo7Chjny5We׋hSTTT,x|m_A:(gm֮YfcfEUu ^VKeU5mm ,ZĞ :*,C6Jf›'WzzEr׮cX**޾>F~k,||[=͒W%SSo1ϟ?T$IB!I XefϚ(jj5QFǶzRp||Hm>Z) -+o~5W2qe>9%&/7lݼ'OS]SVa5zHgْńhq# >h{wp2%-۶ܵ9shD**MWh3 }}ff#';kMGfMgy^WrI"##H?&ݘ;gv/^ڦ(pW/ΗĚ(Z^8fU ~ݚQ2Zs^/dꕤ$'w~:^i9Y<}-63O D33/LdYfŬ[..^gSOk/x@_)S ށVas%^z9DQ vuu_񎵗7/(d2'}_"##O=ATT&uu\^HxX֭~;ILH`{K/piZ'K465F}C#}}kǟK,Y wȲ@m]=|>}yAt6mXϿ?ORSSLL&DQh b* >>'$y˝>_`42N%66#ǐe]O?E}} o$--+Ș:^֭Y/³/U~Z7׭矝ZJLL4!.PYUo~Ο71%믽BDD8[oˬyzDQLf,]˖ٳЈ"5X-j/KJ #XV*+)*)DΞ^nt"1!sf7IVf&sgw$bcc/W2lv;v 5sMM LƲٶe3rHgޜ9ۧ:̙5ĄxJq:NlF#aj..ijj&""j2/NNNo`ٺy#|g,*Ơדɜ9QT "##hoOP`,0bn(O!*2݆:+y))-S @/_:Sbp8qDEEN{G'e{M]=kWdu>rN%:%s.au*,ZH)\tdWQyHDGEt:v;6a% 6oGn3x<l6;>CHH0[6m`64 @@eKOc0 Y]<<,tt IDATnD86&1GOo/׮2888aaa<DD}_ll8Yey445s) |VQydxZ') ハ>r֭]MEE%f1-'w՗_cXCDEEȍEUT"[lOIWw;ohTh(qcYr9~ƬVXg{ AAAp8X,_JP` @EDh0MzUnN'"<|ΦFSF͙5} 6Xh!%eMFN^G AjY5õv/ZȴlZ[())#qdMDl2oDQTR[ONNr:?z)kM&zf͜A^q УjX|)|d]O?I}}` Qr8w"kV$>.5 OU {Ss8կ UTT~a^|YuPTTJ̊LCX8QV?=r_E%NEG졼jܾ/&V8F~}#y&U=먨|fj1[V+AAAy].ݷ(w|>CC@hh(WxcLqI)9Y:v;;L^/biƕd d2JUVUs9_Qغe#^'OvYx1ɉw hdZN6֭ǎl2n-;6o@]*$q%%eiZ%)4&I9v72))-W_`oo 22iME}`F^.rk[5>r`-\yN(|c:A8w"׭KD~U+bu\R0-+;>s+*j-F#f1{L5]=y(^xYl6^yfϚCAr%F4+DtAq;HϨ(6o\,E\4IyJޫ!Hҽ#~kt:~?~V#W7(*&` oRN>Â󈊌$227s Z!:@rsY^yo^|Y$ NUu xgtt q t:oFUu-mmttt2k 5y'hko.eLooO=`\F]}kV$1!/j"99̌t>㴩U~q5BCXn 'NAe|2 =wԔd6_KgWΜ#9)˗Nilj&!>K͛e4eFRp֮ZIJJ446aZDm]ׯBkXjSl>Ndrhmkg DEF_pں:.^Dt16ͬYw53O?˰\bp466QW߀"6&ή.>34 Zysr~˖.Iii9m '{>_uŎaQŎW8'o؇9p0~QQ^Ab(z}4f]]=o~/455S\Rʱ'!Jǟzsp:yyTLg'[/qUC~}z~?ʪjk8p^?M>t/_ajhmmcG13/Ą8Ess3nz}5bZZZq\$_8D)_/nȑc\R3O?E>sfΟ8dc[Xx!-s1V\Frrdӆu l&3ಳ2K~^e2{i<e uFQ/G[hllfǶ|o?\DvO&2*m[6q1lCDq̹qג% YR^/`瓏zLCjJ ǎ'l᤬VzzqPp:Ox R~\/'2BQe:}o]n{_ܯ,`zE|w 8pN Ξ@hH(r%ϝKjJ2 p:t_)`Hw;fh4y╗^o/\DzT,+ey ArSOx03faۃN%((0JضuhD7^{Y9tdgt""##b<yvS|K /3tn $ v !!Hȝp/WTrqٶy#CC6NOY3#!.,Y Ŭ3 %>.v$R C!^x JޭLL&#QQx^@gʝX,V}o7~I'vpy,+9YTUʚ[*YqWS[WϒE dɢ̙5 O፱mi2AvV&W#Ƥ"" PSR `֌uKYd݅> `\Pl&4ђ+TyZ!!>rv 5%Xn7:ObZN6aaa\/1zX@d4p:X-1LڝN^@P`E)Ƞ Ahظq='O>0;{!d$#}*5uo~ 9'vpV_Kػo?&N.Y p:HB]}5ux^p.Goo߸Y,V*Mtt i:>xXt1!?yM֏ֺTVҼ8=77̮TQ>U БslHLt2bGq1 g&3#aQ8bcLdD`ÁN#&: ӈ]&,˼wxg&_OI XEEE_}tV,_:a*4E j0LoD2)@A5sD@SQQQQQQQ:****K~U+-+ Q,˔WTrjP\ΕEJ3dYs/(_]tR~=q7Z9]XRO}) ,ijnA&$ILm]Dhmkؘq]8?U)x?s.g6s_DyoKk+mxKW$jʣ> qʩg1t:ùJ)VHRR"TVWh0`8u,tOg{$,,.24dH[{._br.Yh4~EzRa~?&'o6$IbppRZ[>YMҪSRࠅ &'#I5DDs9U?0;寮Cm]=7Y֎,XVJJ@x2SSSˠł}x^n5jҞZY\fCe:::)-+fO Sx@؉ؒҲQ:J%tttBqIHDk[;7K0CMM-at^kj).-# А`^/U5T"I74PYUMjr2M-#[Zio!Im &**?VTQQYYz%흝lv=NYy7n#J{Q% p:p2MMx^V._FRR>EF +ڐ^AO^4$IbjrΞEbb*VXΚU+i-\.h5-]ө;z$CCC|w]\e߁ v:Bn*x)Ï?鷿l4AEsO#d/sRyeY~'\..c`߁n>b?!fM$IH&*.#N08hϿ؏fe#G'zȬ38{¨3"7" 7>xV?67)+n/\'5%y ~b:\7q78x^}"oִg2iЈ"$,˼Yn:ȫ/HsKH룩HqI4Z-v" %e(/*JJXb~VF#4sXzU5\+Akut W^c`$b7uʔc͙ÊeKIII>mTDFD=ʏH9#lb4QI"K˨oU줤$IB|RXT=H̉*8H,byJJ8~ɉ DQRZƊeKZ^AppgT.R{).{"*ݽ+H$b$qukއE"/["$b|!"כW:ֶvZZG%#-5?8O?Ϊl'0 %)Z ֒{̟3O=Ɂ}{f9[b1o\):cYyfCK* bRX墽㩨q=O_d׎m~Մ+%ݼHuaٰlDDchnm%9)3A_ïg;b&![Jpptvvu&ZHjJ2CCr6+כHO,bX&M9`Llt4sMmmvvz=UL'C$`^&}tuP(ilL ==\)##=˖ bcQ,X0XLbBDUa.Tի$%%EP` aa?@bB< qr&;DBLL4dCR.\jEǜX a7ۉbp) |#{>ƥR)>>*efp8R^[tKMzMs'9)!j뮑L|\pAXXzVsvqN##=*, [6oD$Z傈0$**ͬ^?/'|F3LvtH]¾[)zQ?Fՠ?7n= m\=Rp@ᑣ8N+t :\˨TJ3tvus)Tz]kpƩ3gi5lڴ\N]{IfF7E%l۲bEc۱X,vRH$tv Ck[;===̙@\ eͪh'xd. +eW 0<FT~i:@  7G@H5x7̝ @UU5 B"@?Q*|)*.adB,zʻnp, X޾>V\x#e232:tJx+@ G]ZիVH{{ITJ0NaphKKHOl6S^Q01N#<, Պ \N'aB֢;~AQT\yyF$z !yyuO!|#Km&"x#(r&M&RR>î8o.ɍbӆOAw_Pȸn#6:2<çJ|x(cضDBӍfjiiUm"Cy#?gZyE%kV 9)˅D"ZgϝGRs? ~?aZ9tp.#awmf9ݎNa0eKÙs9 8XAכ(z>!&:z vvӴhxOsE5 8t`ޟjچ^Z#c(r?taD"%k|$%&iúY~P*_$U6&'MDGE{v~JbB,mm~?ӗ~ "8/?{jPk$''Gee(W+9u,rݰP^pPPX~gz yq[f|-\5HOK 5 uuܾ\06_F``V?j nbܾ8}(wMtR)f󔻖ZB.q8(rD{}HRk N{Guux{z _EF89陾b29q4.b!y|}|m[6!8!///:-lڸ~E&yz`ّH$LY,lj;zyB.B>PQQ&󄿿 =yJSO<.P֨ٴq='N%PyoJ|K{wM :-~~h4j ]]TUM(++G&y= jTE%(rd2~~vʮV0nge̝;^ z0~~D"BCC dxdؘh5pw7/c|Jt ^JUyZM4gRH$Y'Np`n4xzcH$\..^fU$<Z NpoRp@΅=ƉSgt`qBN1/X>n'yojuyhь[6m$;ޣ83gٸoik[;L'buBj Yh`00::FpP =joo=-dfƍKJCV" *yݻv0>nTꦶ9,Y$smܾ)p\lX_>kʮVE\x QRZƂ<~p\tZ|j˅N=0gϡh8ϡШ5\.>{B~+g-"%d2Νcrr___RRRc>L&~  {>ˆuku:FKѨYv55YF~ai)䜿];Q*'_zS%WYr9.p8سkgk ʶimk|BBXf׾6\OKkAT֑ts˦ R~mm\Sw}6p87v ²KHNLot:INNѱ1btwi:\PDKk+ cLFGGٸ~-qsW^eqB^}M.'It8y:X];hiiZC#.܉[8r*E֢r.nͪm]-`bb>c rqyg=q I p:,۶l&44IwyyLEUUdftvk9/F#즨N\vlJwO/=w[=~~\`0GDD8u޵:#)1Trr/PSSZb+t@dD8DEFrz}j$b1aa>{oabؘh KxÞ}M>{B/Ϝwd2}6U+@Ww7ɉ8 _YK˿ ogn L&Χ'Not:br`n¨Z#V:^=KfF:I ^;^ȹ8;oe9{3ONwch:ISgؼq=Vk =Y .)1R3O}nwFGGxC"Kg8-8w>wweؾOpGZZ`]> @m]=ˋq?8EDEFgϝd6rA!';85uu$ \.5uw011w+زy#J Νy8jx~OgHiY_i|ro~DX, |{>7 %RRfdfJ"(0t:-bEӍf>>z̝s( RcGXXZf3 5MOO#G?au@R8^o B^{v(= H8t`dۇ^cDM57rC9za)BCQUhnAT2a4]o~bccw㋗pN09idd-ZȢ 0ϯHka07w.-mBFFFzIY8GXwO.V: 45cŤrtr)R7tbYz%oI+ArԷx{B~A!.{Kppz-^ 91Z/)a%]/;TJUu V 3ppbhn7tRZvug%W^SZOlLmSocVsMl /U~,_I~?󟽄RC(|nP,?!HZ^qo+JY|#!}L|\9ؽ=f#G111AfF:kVFD"1JAh| x/S;c_X<͖MxP匌lb126 gO>%88Ȉ,|x(r1||IL7d6Z.IP(HJLJ!""'tww7!$8.V9 %EP ˙0LM?L2^&3ֳaZjѨWf0LiqX,c5u3lX b1b {PNf3AQ4hhh[fpAmmmhϕ Hy|' "#xÏ[;.?jp: t̟yFgo ^{-v;׭X 4$Tƴu&^y q8_o#G{z_8>>D"q1~>?w]K t$,DbEQ3i2h0HR'bظ@^7㖶hDR!MU*jql6x"o7*bll:^_,+2gh4j܍@ f3:Jcb2LaKN7v022T* F3# _~ݎFwHIIaժU3u:aZjrNcc8NO|& a]TV3::JB.w{f뇗\ :, #er$12 D^PUZl6cb||gYV' }2tZ-&V8V??_R)F$I#1)Sx{1LLx{{g|. B1cqZ1l6R??_9E.C$^ss٩)D"RQD<5T"E&b|܀NedtXL.>bT|rra\^ba` $88nxwfpba|l˅^B@Y_ܬnmm#۷ĩ3ܾ"Erb"Y0C4gvKLL411ўE"Cw~jCUߏ*@ V @@ G|5^ob|{jBuM,F$i6htkWʮBDa?=x\.ʮVsF^Hteɑ8rťW\/nsq39n0p|莣 Fl6;qtb!eNgc`pe[Vjw3:6ᠦOOũ3g9r/f\/j؆` l.{|vߘvs+*):ʮV~^E%q1NΦ35;:ZE8יּ,< }rMMxɼ>w޾>&'ݍNl6)ɔWTp:hnieq"O首6"dΔBQI)U=FƸ_Hww.OOF1:6NMmԌԋs9{AQ1VwrF3.wDBhH=e?Sbbu>u,2 ?>9vήnȑOc۩eq"RIyϞr!H(.#w|}}8~W`9}۔\)cjBUu /сl|G\PHWw7.׷kA:xֶvO\/`ll EKkE%ڿr\bK\.)(*fd}j깔_@Kk a0Lp8h̥z=FCLt|x* PUXV+(p?@MmWH&. innft:򹕐 x44^g%,ʢ))-HD``隽b̉Z^be(JzzPOgoljl2s54V*9q4c'͙CtT$544^g%,Y)udr}}X,.t2>n`ْdf&ѱ1GFXp!&|N\.fۇ"X̉),.ar q͙Cmmߏ>BGR!GR˸kǛ2FFɠOeejj?9ZLvΌ2F7|wFFG9q4jN;(*.tSZbq"4zz{9w"ޟnאH%tvusS(J^{-n;8u&a:::h4^ 26[x^Z[(.X\^ϩh45:n4300HaqɷY 4n؈\ y#XR_/?>CCtvuЛw1֬Ze,Sv~s>Z9z NWW7ihl6 Wz=̙.[p<2Rɝ].\SSWV_XL[{̓.((pD\/S[RBqTJ%m446RV^1Dn \?09,Y}â P*X Yr9CT*),,j;G>9Ϋ&%]:.)1硘Nغy#uu5Vxz] '4$%QT\JAQ1- 7d2}5qs).)jy% a4ٳs%8N1tRX\ & jQLL^)+Z* ktvv100Z傝۷iqs)*.jy_=][cNl bT*%eW+hhNm]sbcw@LLL4Dj!JLY0"JXv6;"J/R` ȼt9x_@ q">}.qS*,_g9sիVHgW'G>/R5VR^QE{G'"{]j57Wx^ wT90LL`Z:}(0c8Ñ8N1&xMN A !!$vVIcooJ$&jXVwV˱OO2/3x2Fq܍badB*Ѩ?VT*.k2P(wi0N*hI'NBWOKgaP(TTVkt@6[3czNn!A$'J\CwO'OgeF"#g|'F#2ӓt?01X,FVсX,!">złn')1%qSeOއoJEeG>9NHPכn026-<|q{zP(rl-"w筸6|}}$?7˫OKi$'%zvl2j-TVאĄ}FppㅟyDq!2ad&3=TNd6VxOxaZ2ϗ s/E '((R3gq::MD_?iiWVr9V6oڀVwgp=(ϊ0h 6Too|G7Tʿcݚ՜ɥs/W -]hߞ]ijlrbph~N>0L8{72:JppЌi gyRY]홮xO%WEבw254}.bRC/(r!K &!>ZL&A}I3y͜91PZvɄ'/>%Y.Y̚U+=wQҏc捔]-駞? mh5ZR ZC㬍|scODxa\\pfwʩ /<%YlݼT $$ݕOfKqT`2 z`j؊DkՌ5[ڈ~DtsyG?$00~Z♧?{{IIN$4$.GP)] g/>,yyر}+z^PQYE҂wOBB~]=h4,L$%&s`u8zt:-6޾>֬Z3O}:0"Hcmtΰw.z!!465݃VS_<0'e2ٌ0BL&3::FdDjvh eтضJm[ TYTJ%!,?{v}a|Hĕ匍3wN,6X6`l5DabϏZK夷 q9]_`v>s^Odx8f-ZȊK9y $'yzbcg)گl6s:I xyyy*s,>J",4},9Q"Jٱm Ngcڐd )w-`RihU*b1^^Rj5^^^8N:::'1!sqϚ+yTC # y*h5]7$300rΟA 4 3gYb9M7nf x mLMYx:v__7ޢG[{GcŊ(J֮YE<[Z\ȻDOo/|1ťWqtuXݿTJ^{M`Z ^GV1}y }#T*%˖.fdtK;;dpRQYEr-OZj :ss#z4]~nNl,}WTJ"%9W^;D"EP^(eW DŪ9v],Z8`Ϗ@Xh(>>(Jr.D,^Mjj ewtJ gϝ'9)7RRz ZF#Ɉ#t: PNXZ`>ii)hΝe|ci6"'66?bcH$,[d|Z^vzH\\@xX"Pt:=11фx>>NVe^R~ׯmJ$ry/#H `Ų%BI#Ax ںzʮ! @<`_ @-aBO#o@ @ (lڰw㳎8 P*f>r 2^ݟuN ͆dy'y~222} }{ܩ}&?z"5zJ/kILg2Gsqk8y:o$V5O%2nƧ'OKr Ewmd2Mׅ78},a\)ʆukXj뙗ɥKٻw7/\ =-SS[ݻt108HyE%i߉rb"|RIDxE,]˅^grp2jE P)U44^g%DGFH$"kBCCy&022JQq R11QDEwO HRb;oCR/~@ccc8uT=xH,[Q*kHNLd~]$ S qshll"44-6GK(rvF~Aa/[JEeUU(JefP]SKUu-I#ɉZOOf׎mգR*)-+DžaRijnFѰ}fL&3o6v9"],]EB|/UcɴWɼt|1LdfsDt$= ƼKYb"ٿw}=75Qz >z,^ĥBlVwmSQYEɕ2Bؽk )(*&ߟMףLeK(׉螚ĩ3 ԔY~sD{G's@_?Vܼttth|ef? \NRBM71̈́.Yv5a\OGg'?{]Օ~nAW9@IHB䌍66PigM7o[ V5Uq6LNBDBs26>sw{]w*M&[6mٷeoT*HO#44@=NB$hk렾BJBpI;,+,@Trl [6m@~|46]D[465BdD8$&$/_r1::Fv"6[lX^R񓤧haOs?2CC$''yLbbgϝ=zT"!;k!ogqvrd355͹ ;q TJ\\,xO P( K__?$%öJ:::q8>V:::9]\Bn֭YX,'@\fǨd6YÕռʋ 395,!$$]RZVB6_w7 s:ݔDWw7 ;y] #iYA>y).֭Ysq{<+/? T]'T^A|\ӁARSIINbpVJBΒ4<34< ή.x| *;yއL.cc8~4NO̮;(ee׳;P^~ں:dk]$!>'Jyew(1m214<F{{yaN$1SSS)>XÙFGG 쎧bBv*TJ57jikΟϞo i:<RS_{OF/E%hkk`DaoZ?OyE%b`a?n7.b4hU+0 l\SgBGͳFӑ$ zjl4hͶݨ II*IKMp033CqyGhk+!Xu*ccz֧3f"#"xB,p:Y|yXܼByE6xv;|HaGsEFFFq8<)33#vVXΟyАLf˪xCk4Zn=Ml\ںY]a]j%sno]Xd>jOa Ӂ1Rq /155T*AP8+*}Qji AAAZكHxm8NN'8s۵k;֮D/s^/vN'6ێ̌WzH,ZDWCH$";kbjj8>7ol־np:ܸ~-9KQ [vyj6.bFFGLѲ#~GA pfQT\n$1ݎ\.bx|Av˅B.G&zj Db7N 7NÁB.G.e޶nwT*px<R)6 ˍ\H$YDTJ^/n͆L*EPy<\.rÁT{1[m6zQT8NrmgfE&9N'blv p8J$dr\.JofT*p|P.'>{'\\&p̙w7JTd2/+q8gRJ%XVy?|V D2~z<lv;r Wm6"ߖV|ϋT*wTB.}͆6~g|p8].\N_t88NJl2}yv> ՆF,RHoZb1V ˮ͆X$vyvS~]bG#'8?A I}C# l{qnxt'dg-$;k&}@A N@;0sCR@@º?_<  vWͷshj|!3-?xVx,e IDATc'NS |,z Mx开a RV^8kWD6G䶭n>SbXzn57hmkgOP^QN' ,ˡ񉯵^45ߤ%$'C\./100DbB<9qA[d-ZHGG'2a~;:XZ@@<6^VM^n}]Ͻ8yy,zDžxw%":N%hn"#"Az)T1^C$Ԍ`D,~x뉀f&&&:@ٍ&X:-L? Z)_Jv"P6 Zxӯ&&&'imk';kSӌ͖h0pe1:+y" :=84DtT$_>zWM7v<7o q̘g#,4xxh|yO>P&'8[r:玠kkv!\ƴ}zz/|ɂthkkGTr Lf3|9I ?qiN.)PMpZ8ߙ BCHOH2%,vlݙ#ih/Ht =*{I Nm]=!9v|VXj%N>+ }ψ`tlsKIOO?gdt 'gjz$=6mÏC]]=w/L͍:fHÈ[- RsVp~B$qz=D"BBQ*9~t%R)_*v>T*5w=Omy vxK8NnqL1}8NFFF)_ʪ_*;Qr<ʹ U0<2B֬ZVi_njoH K>@@V,+daTɿM.\^h0"'OSSS4yۋ^C8 c`pf.@ti.wJ$ F#*w^_`ho=PΒl%7g y9LMBшbFUtiULZN%<< Z \vmu/щj/@=!F̱k{kZ8s֧}G}CJwDR=/… X\.`)i)n*rs391B!gp}<}B^n.ii"aE2֮^IKKZ-ף0(ϧ2e,+GI-drp""pܳp%6oSStKDD395E@45d2NGgSSSXV\NŬX P^QȬi\*+q?Z;^3g?/jnVIhDEEr iHjX<8Y"[N.FV Hq465'򫊬(Zơ#~ !F47r[,ܳK$h>=^\Nhh(׮` or5:G&7g1Czkkfq̩;Ks\ CVK+۷=Ȩ݄Nm]KsYpY22::VXkX,ܨEG$sڭoЋL3fV\ACc2yj);bivdv\`(a!rM$XE@k].UW8]|i"#adt8oLGFF"44?ܹ0hp:X’,x: djjf,zz{)\("#DPb2J]]ݸ֭YZ"!>7[uDGE a!&trEѷnv?@lV./B"FwO#,YM|\,qq j(,'<v͍::cm38xukנh)_ʺ5Zs$DZ8+ oEpQp~/~Ŏ Ο/{:Dلrv}SzZ*:ڿkzD]}=ۏ@*`4H=]kP֮O`? ~s!̪9|IItw*.#JQQ (>{VC|\,==359E>)l6Ow~ DD"!kBbcbhHKM%@`6Im]=ˋ D,& ĄxR 11 Fj5aaJddbM ?7jOEar322F;>>Ao_?FxBf |O&&&h<'''q{<0 "nшR %%註=x^FFGwtv==ҙzzzQf3=g܂/\DOO}q/f3=  rwX211?]iv~{z1Lx<+SNs3 G`l|K;G0]wrήy?0 8~.FFFGX,t/X,ttv;,nzz1Kx{4~ |+AGu.C?%j"HH$l6r9m9[~&kV$2";:).9GHp0QQd.H/hdKDxvmETR^y>ww!*I ][m7LBCq=,/* n&DCIx<DR 33_@e&Ѭ҉BD,jp: @*`q8jdwRT`gXnrtP*\.n7ZӉq (Oo߆L&BX,(   .9ap04D@2=τתټiQ|^FY3%x}9%/~`XOdg-b匏Of -=} '1!̌ߩݷ RDrsx=HFz?D,!$$?/"2"ܧ(aXf,-+d`p5!RSiimRymmoþ_;;{'u \ZDfhimR9j!FXj?CG6!xW8~4##8v=7tcS3]]rj52K?-%%mitr%LMMg#Pռ{?#+VAqyR%"<MRbqY^SEoXz!d\Xm[褼;:ˣoqZZ-{?ǯ_g7S% p'"͆FBr03I䶭w)Ee2cPJWW7^=δiX6_/T*lG-9R9ˋ _VM@J⾧' ԭRxWlxRWTBCx՗x՗Q[GyEN \NiY9"DD"%##ںz&&&JW)՗^U~?~ogͯ_G+/T] OoFGG$&ē ǷlpPQY"fhhzo+/=?\oaq"FCiY?T*qg*]D,fbb[e}$5V #ܜňD"zzSmI*eAz:7ꘜoA$+ohOvtvddK/` 6exeKo~EKkF$b55sN.?@\0W^z@=/_/_qB)O='/hY>/sق'G|@DGGЄjC1c011AaRn$;kt㡳j<^i( j5.X.r>-7 ˰lXm68\zhI&Y(>{m[d2AdS-Պ!(Ms1~7+|kq ^g6BYT*#{"RYsg[l92.t_yY-_nFĒo$dzyz Jdpp^:\1../BRֻ8֬^Fv1XV<ū/@__+Jm} Ρ#GZT_[J[Ob1;ơ#ǰ}ӷmLfq`r_D"b_U͛ذn-תk攱ryًP)wjpeK_i{INJ<}sS"CGQr"7-6 v>M͜.>KFz1uz 02:i﾿$pxd@1Lnh4CTT}DGG100ak{>AѲ-̜h&&&6xO  )1uP_@u ^/;܎B!Бv?8Nt8v'NA$ئDDVK gϝG*e*D,&$8}'L]C񱱬_yNF"2"-bLL^ĄxN.fhhܜ"ngtl>BΩ e˿/R؄!00TW >.s %%Cjd[yWfPt:NDOo:FΜ-!<,E*8œ۶ j ?k9Rb6Q qxɩi._ RXj%I SVQIJr"<Z\3gAVQTXR%ĕ&RSRYۉr_ 7 b1sukVs<`X^T6 P6o\ ##OC|`pukV먭ob0.~K? R9?svN^//>ˀOݻkҼ\^z~7+II lXWl2 Դ_|\l6oXϚ+)>{)b |Y!KrYfF5jET ΟϞ{(NDb1ôwt`|N& I;: A"  iŋT*ET`4A*OXh(SSS":ݜ(Hh4LfM~ N?|jGRP(H$HeR&J/XL&b066NlL46pc$DZ~bcy=x=&'hlIxX8 /!Zmx8*.0 x=8E͛dRmvt:j HDrrϢ DrE=vֶv4X,Vb7jkh$,4\NZj E 2ܜ%^/}9תT*AZҨkh#88рZ/+vJl^}y AA~{ŵH$,+bWORb"jH4}jHOOE$q<&DgT*E"p88N۷DZj**5D"liezzx**o vm|f }ǜs˗)䷬ 7z]vW#Pʺ5DR A~Rp?A N|ᐎ?ݔ Kd2Rp</ \ARR|! QV-# Z* }~ j [ a_0( Cp~a#tq *x{aϞ=og**_{jz7F@/uya8x=6r}c$mtvv|z)>{O>=|ls~7ӀORyo>CÀyoχ\ZbE\QK\n\.< p?b^}U^}Uz=IIIKޕfq |.Ut:ilz]ͯ;umv;G}߿+A~^W^K9|w&&'GWW7JRp&Μ=G{GʯO=hljMyonljvS9R[ϱ'ZFW^򭎩O Q} Y 9zEz= M$'%g Jo~ϣvߏf2~r\VM]}=k;,e\fjzT*ly߲]ϵkט繽}A<6m\Om]=V v;ׯѱ1.7:̵IMI";ksz\&-5ůrZ5 ܜ%Mu:67|wk[;57j)#&:KgfH$G{iiie׳;PT?~<9sii l6/^B"v*zzbʕ YL&F-YV &P پ:|%p:E|q N>v xYOUL&;ZCuu5999]̌tFFF[6?JHY9LX,j@RR<޴YDEFfpЄR`oҾ^[> #\zLR|WDGGapDGG6 KQU {zGtJ%*@R){pf|_(G*2gvW\cr҆$+3.MQjQUqz-,vbǹ|}x)11фp5`dtxٽ))$1l63gv!1фha1G#ɹ~ ʩ<-e{aECwO}] ;}HbB<`sjjj5ϛG;wiYtg&hjI\.GVc?3ӈ׎XBC7R@}GFx1zN͛j8T*7ݮNnN'O!,4:2+;~֎4Dm]=i>z胂bE%DcήnHINbLʔ#ghxTBj24<\{}llZnw099:aG.cRp8 @"AP`1|˹KFANpp0 Jsq{%.6ZE`` ]Dv{̟7Ȉ"##Z3`Bf^G&b!3="##_rA~mzj)**Bղ}v\^'33sں1Nsoɤ\ƸBBB `0`4IvV&h4I1=-I uk":* cp0SPTDc6t"mP(I_11v.t㳐QQdg1HPP f`ГڀσZ&tw%*2 ΞM\l QQrx=,^ z=##$'%M7TJtt $&&bdfcْEh4z !!F4~CCCI#@rh|"#".c 1-!F#i)8NBCC(*GhDR:5O;A驭c{ uOHbc0 I;L14HpA+!, ##>@Ń\d,*<._k(=p-'¯~{냘]Tc0٧?~=Zhii@fzU+shefT*dl~}5bH$=FG~ݚϼ+݋XAvBiimd2q~yma0 ţ -7 V_=&*2bkxD@n}5f/BzrW^ӿںz&'mj]zƦfnn߭x$ xP;f24>1N{>sezmbt!*j8u f||8}v{##cYt1'O3OR+/$UtttbZYj9J[{9ٙ_x'8uBFJJpibhP*/0Nr*8b IDAT*k{JT_D__?5lԶYz%\1c'O1l6cz]=rʪj[Zz: w=bcbHKMfŲdaXZP_'00_ S 좵.b9F MMF32 a3)\aΞ˭H`94Ga;;`[9Y<أ }F#aa R^Q9mdnמ~+du&S՗.s q Y|)9ٙ7s);{ %D"qIH+$aXd\vysLzijn!>>Η+ %|qjS'jrsP(\.;:A&%tuuOa2:XSHIN bzV\koMww7_*~ߣV)'C6aiolT7#-˗p8|q]egϳ|NǷܡw@msV\БcY,[_w,\0,>(nټMNNrq~ʟ+r~J%l\ 0wleŲ%GTʶZ- Mt(ryQ!W%o /-tuu:mmz‚OTL;zZq8mzoJr2G2c苹9 .2"fZXf5}}0ֶv""#عc466QGͥ+>}ܜl23鑍( :țRD/qLs IMI&,,G3df㺵$DT;tHLzm.\$#-|8%9ɿ,T*M40GiimEPt"SgLfF18].-,fbBA~,݆ )Iz8$**ؘh =VPORb=v~R"ˉJ"<<s '3=;ukkkٰn-)ɉLC?yͱ&x j QΝ+ t:QՄnlgVN6]\BtT$yLLX&(0`w{':Yٜ.; 6 1>a!5%ή.M&ikkgyh&w, Nňe`D"p)zyꉭEerH$}EEF2P5OH$23vDoW~zD[ftcH$D"Θq088f'((шT:s_FڄFb Q?722/ Q /ӈD"H$>׌n$$#xM&ߨ M%x<|^ǃlw~^{o|U5?_H L׿[^^ -|q#ުk.&DG:-fm r9==hJ}zzzHOfv4K0LRn)000T&ETQ1L$%%ݍV`c8]Nl6z>`tt TT $!8NooN+R\q\ǿ[2IMIfú5LC))fxa1Jc'Naq/ڿ-A~>@"퉭_Ĺ F;=JZ&,̟GEe5Ass Gp8ǿ_i"ʟ/}n7k18h"o,rsny~o+rX!ο_ p` &!>G7=FͿϒOw*}[ !Pj9DGE!::_L؀ tvvjQ(: D.!Jz zjΝ!00T't:-^/_W*VjXV 5+*)?8"J^RS9qWm6?wI' 1% $';KXx<:rֶvPQYNETufR)gSO|ܽ ֠hxxhhhbǡz*2::;Q*\(r򃗾_\rQ1WkljZ<߽ F8͙MxxO?'OH6[sK"ϽL9Z1좧#ҁ>h'vE LIf kwL*fNRb6  ɊKo **4j.] *#,^XLJJ2Gd2kp\lzx9Y(oLLLiDDQzhnia].bb9}, qqf/[B@@{JJybcw"уh[###X,V/0h21h2V0a`pA2,-c=7ngT*0 a4./&0i A♪hM~%##DGGb4cLO~sH\&![4 A@@m\Ts\\#V.6~GbbE3 &:*j(^9yzzzX|Eu-, ]T@Qa?hp\\vY'KOK|ۃV?x7R>h:Xyshni?-ZXk&!> `4fgdtys  l6 9}.G&f*N.f hrʕ53AȠĊKyXQ\&'=-󩨪6 Nu%rD‹ymƩel0ׯX-Vr3ٶ1##,ZP9Y|)qTTPWHHQx3vJxn""҇VP|uDEF@NL&%@V%hDףRprPTO\.#2"TJD.9m z`u:<^/:BB@b؟a0`dtZM}JH٘;PHLx\.Y/XW@dD8zA|20p( &mLLXp:bgw?Nh$  à~E/O>L&C.c٘f#N Ytr8voАbZ׳nj:pǍjdvQ]n'4,YOxx( d29OOHԃёJt:-1p] sEq^ @XXȴmǍZBVr.bߙA oM.;˂?*}f,1-dٍ[*rA?m!166}'^7P(1/(hz|Zx9Nbe\zt߲={):[1V>(duu\ =i7˯Ū+gż ԶEbw8 E[I9Nxbںz-]r'HtZ#֬^?e4j5t:-ͦW>JRd2 Mݞ cʱNN2wNrikk{#LR!HHMIErRUU?8m=|cQTZZʤ29 ϛu~er92 yz|b M-ΟJB*26>ΉSgX|)Q~~Ht1 N$ cc|c'>$ڀer!hH$w`|HD"HO&D(qP$D"[FD"H$}ˈegD:f+=p.ync?sQp[Nxx<:°kf"`lv;/~9?ARek؉S^c71h2ulp`#eu>>Jdd8kV᧵sް?"ѝr|҃ZZ[Bn_IRRLJSs DӲx23#JQ|t:-g]`wm"B_?Fܯ,n! Irs8u ZL*EPs2y);ysq{<\g?!i)/jM@`ys cb'Oc4t:[qq;~0BB\#=- UՄ 8w"qq֯{uFc02 AvC{O -2<]WWVWp8B?"6.+׮EUu o\DEER$;+@NRfBA,߲$gOƄ >V=%ٺQ.W04<|[ϟڇV̄sO?I]} SYU͏bXv޾~VX}w>jGG;wJliim5WG;8r֬͏mV,cRFFF d8p-u}=#ctA #3.˗7@I~vncO29ic9w؊D7H$HR2rh4ϱt獷edd}]* JJ$7koP^Y 1)*(wcxx05رc<޽ ,,xA c  󧷤$' AD1wv11v \<- x{𣝬[ nmf23ϛEoo?K%Sϛ 8Œ b諘qbWGg'ga 6Xf-9CDŶǷ H u^gyw`U8N~GHcnk7CGbIJLB_.a󦇹^[GOO6Na%dbfqt*<{j>Keu y{zD.bǮɤVSS)F0A:##7Ѩ)=x'BdD.sIz0>>ur%|*V,D$*ZȤR*C:3 t `9aX+{fdb9r#p`ZINJ`sfK\LtH$,\Pުjl64j:7/N \6m[7o}J%9YS墨0ydȞD"al|RR 6 FHuq̛;˗FV@o$&$FB|<&}RHB|.kr9ٜ8yؘhJbE%Zşx+WARb3eZ,pU{C@`{r* @i.VTeKtCG2h2˥WޒR_,GVz{Oy^Dhh(` ddge18VקR#H7w6!!D#ɉbVnAOLo.\.'::N7չ<%0PG\l Yu"&:BNdDsAZJ ^x^]b !1!#.4 PILGRHPP}-bcH"jDh0`.Z͛oÏ~=n7o.YH%RRS))ϖ͛xO̞},,.&&&?sf||"q8\^Kl\,VYo?߿̣oy/[Cyr;lzxj訯t,`H$z0$%$r2Ut߹cY11<<4200mGHJH;22JKK+IbcXP* La0 ZRDz ?  Š`;&,,Պf`Уj0GV[>(@kVSUsW|,D_|U+?ѷƌs+^[D"E&Y|)/\D._wT\B.q{YpwB `#{OP7;v!Y@T/_pE[x^lD`` n tuuYP{RV&; q:_H$D"j[ &yeRAf\:\_OnO:Uy#mNk HJ%3^鉷E"H$mwD"A&!I̴\" *pcOn| )ϴۜ Lg-O=};}}}ėή[^o -A$G Lͥz]vUj.z^Ξ;soh/kkbE%Kco7$՗.I;t϶C$>rtƸuS%S_]4bڑcBn_Ss3f;#ǎcWVazGg']=sz]8_$zP;gp044ARR nGVk|k8{<VjXQ<3g}vSz  pz---v'O݅#!}hB"p8hni_gb>YSs Ӧ܋ZZp870hG}C#V$ 08hu.g8x-ݏ LC465a[;:iljfhh!U, IDATn4GCc6 K Cmm]=l6>ؾcj{446 lvN{{^1:imk#5%^|=zv#TQm v׮b֭YMggC8t&ΝG*s]Uu CCnN>$zzϛły4 g_ ?744 3K\z˗v"уnw`Z0\,*+S⻀zpu ?x޾/dbbѱ18v=}ٻk{dt>Nj}gQWr}t:);ﰙؽ& ?^ޤ*+Z>4ą ::piIOKx<\.'m\L]]=+);w'Oq5.WG;1ؾc I+?y.f<0.;wBR!x<$'%r^e(>BP(sOS_@ggN 0:B~^.'O7ަNue22p\x^rsx7رknۅJD!RRG$zDDǼs|J=ћj:t*uLo${qjJ2y9AccyU*tZ:ֶ6;t[#@QA>}2{zH #i[AA\ h cGABRLyE-Qp/Ub}cXjt:_1ׯmctHt1 NIGg'--mlܰ>ϝ3BGhX(;w}̄Jm]7piΙMzZ*1ќ*+<<£68 }hj‚| 1d#(*, ;+(:::iimch4}D;tODxyCd#ϟG\l q=vA(ox4 aaN.#))d2qT\l0 OLpIBj GwG.Jd"v;g_d z^m,lz{LzZ*Y8Nbb߂nٯ!00YP΄k6jq8$&$00hDZZ[YxX'Vt߹cQp/F(DX,=Lv#ݖ/SP,DoYbOw 5D"H$&DߨszfrbŗAoo/_{E"H$Dwތ#U45ba WjD*T*q\xij ZobۑJT*1M$f LvK(Ӊ~"諛Jެ\r!k|;G;_w$$aLz uOX &Dڙ4P(ؼAdH$}bpć;vdM{_'we;]HRbc:/x<0cpcrL+h4lcchZlg44JZiTJEe%11wF@Wdtt^ K7g-|}5_ϸ\.~"oV.)lXvރ`NR) 1k3@SS3fϾRE ILbRЎױ$dz鑍g?!;w`cRv+H$aZS ׮*K%fQT{oGJ?xԔBCx'hljfT*>D~ۗγOc0 n|6DCCͽw?1+7G?u.ΞG 1SY͝䍷M$4(7K݌NUX,N'}(\.G&z9~$!F#YwtLfF{J@Dx7}s.WzJbٱc L;Άukxj LLNv&Yclj'**lN+HIJÏv2ol?fll;Etti:s5WÎ]{p`{:2THSs / P>NXXAAvrhhh䗿-G!DdD%pFGǸP^N|\,U5:s<}΀iKJPPv B-ُlZ/]Ք?ɵknZ- BAxxyֻINJ$$H{Gʤd2~HK#8y^8w&>$ɉ 8pW{D4 x'xD}ǃK˰NZXWT2+'6摻/fb6_K^n?04n7+^f#;3r}2)V.X_. Mwh$7'{l6ٰ%.]QiUQJ*RBA9gc0ms~fN3ۻsv~{ff{:;bs"@sΡr%ݴ {} tQ(L:Y3(*.'W^Qɕא˽.@{Dp8~=^~KldgK0M,`X)y;D ΟSτ# ќ16_@`gp8WX;o9.oDx8Q`ZGם(\.Z-6}<:Zʪj&I `Xx E$Ƥ$l6]]]lXIHMI`ۄچH,B|Ѳz.^D`` fa0kᤥ {dz4 F XL[[;ɓ| yPT9~XbcyasFWk~RMC IIbr?$b *'O=bQgOݔ&a41`2᝵/FZwl>LfNZ k9DGE!PU,]1[}U+qN"y< 6s#W{8.']ȸ:d2)z=zg:mv-لxmJKFNnu w}xRFS]SÖ7?,0\vyXjw*5k&SL鳄qIfhhP=Qrx`T;@fNoaz,V+H$ rrio@QefɽqF7h7w6z]MHp{dUT*fϚ'AV0#4j5@r FMQm$1R VnV~̌tNIhZD"1#JHLvYiӦK'uvj*N'e*q?iiWT’ŋ8pQ"H\r˗FHH0JABS@RpO]]ݜ<}_!h 6Rpxͻɺ5OAxy +3 FL&ӟL0M!ObtzsHp88v$7nG;A̱r=tB2Hl!%.\LHH}gfv)#I :r/szbP?I[NhD\vsx.wi tj/\$}`_ƁCGp:ye_Ve)D,!";b1|N٦1<<̦:wfϜɔt\|˖PW_ϕ9̚59f"?\Ȉpl6%MFFs8\|V֯[CTT$7o`>+f F# jq\ʧK1)1bZIB{G'==L<Fkk׭!TBNnd,TY|ǟ1{ wBZZ*z=RgK$)8XV}& OvaZe왼f/\OL&e}|{A Q2#-8G d{OO/d@Lt=4O(.6{sLz=6-aa1%yݾ˯ɮ/2y`;ÇD"!*:='G"cq8TW>'"rbvi9mm^ٱ FYy핗(--1[`IM ;;ߤ)J*xN+f}Bزy)4467rInK$tvv+;paz6oZϩ3^epx.]z_].WG|Ioo/gΞ#-ud˦ζ[('g(-~xpnSPXDo_qqNKK(*.a]w곰fH_q 1T16}}P(0l6dRJ?:% :zq4ӎ/rh}xgX,wuG q e弸u j #4,-7>rcV<%)Ö6sI, :]mm>vW2udN~O)e2D"ˍtp>=N"쇀f~y##;%̢ > f͜!Ʀ&TJc.AAL:yx`4IP` Q8_{?=-J^٬8NΟdIcHJL@Q 1))MQq qHK܍IdGyk&N9KtTSHKK+{gxz-̠FbI ͆fcޜd2ܩh2bXxs6  66˅446`:]v?bYDEFRT\D"jr5jYr!̟`DR:-3gf?F*z J IDAT:;z]@a`RFw}DsЇ{A'11{Zw| |;|ߕNux$ɸ}gw;Sz={w?c~ώW@7cx;mjwj:(()q;wd=JMwzn'}IXƄҲrVaD"N'RNwX|AAxD"bػ!:*ۥ唕WӋb+Wjx$jj(]d&Ez{0*utttt:Zt\,)-vizZZ[)]dbpBGwWsq~o#t467qYBB)]Jޭ| 9p+--344`bz{x$TVdB-]LGG'&SRrJoHbTbw8v=anYũ3YH$t*uu 8N.^ČTBq9Tܩd,]o^, x3nǙsQYUnꉎ$uTN_c2yTUPr"J8=*w)$bh&sOp\H%b1oחYt үɲɤR{~uZimi-mb%de_`Y>j5oNUu =n+P^QɒŋpHR$b1t\|+S[WϭBnݢ݁ܫa,Jq]DGGDwzj^G RXTB]]=6HV._6.w!8(0RAUu5 M]@CC#c}97nRVqƦ&zFLtIIDR((*#+SZVN:*ܡ1[?R`0*&"99˖`zONNfɢT*.YȒ  MYz%]ݨjv;ŷ/(O Ï? 2ŋ2}qkjؼq=/<۷XbO4::ȈbI_07qA .KII*($!>@bӆu/`v'???ojE&2u FJn7%%bߩ$.6ÎT*%'7;U"bnGh?D*d2r2w~Bsܸyb f7#Hgƌ4^<-mm+HĂ##E/d2s='w<(0^}ʦ 9=kVt:}2dO:B{g'~wJR$1gLr9\%c|̞ŋ|KGb1m=1[,Z+H9~H%HD"_J [_xk .'J6CB(-+ffUiBDl61Q\N[x~GrO$&(`0<,>::;Yv5=,7i) ęln7 LOK[/ORbQc6[ظ~-bEsK ֯ATDAAK,&&&;f*̞5gבOww̚9%P(\%:*ŋRގ "ܼ29y!ZMHH0"RL&11?QQTWRW_s6n'7&eJ@FT*%((Fͬ3ࡸ֡h|DF! @;6Vc #ƕv1[,8c"N'WxӉ`q\\z\.n cB ɗ_wgG?*9}rIn̿xNnu?li?HWw} ޾~~8~׎{5Ǐ>e9qꮒo~G>snf?Wrry>GD"!3#u>K_?⅙Q q-<ϱz(e=(lܰ{i)SHLܦ|k+G?~븑w_/jqbcM,^O?V_/&jtU5\|WY?ŗ{/o(b^˯a4ǥ=x_'Q@L8ۋlZHR)r+包;^f oLD"F9]N$b 4 mmƿhVVR6500ʻϦ 눉}W`41=3YyMlmweJBz'M}P=!̙3gnT*sgb\axxV+;/rkǭ|6o\OtT$_;tXzJ$#J觲ɓoȱ>咜;:;LNDPP /7ёX;UtwwhE&ʪ*ZhmkcؽwN9g1szg+;8y,R_@Gg':ooD3s@W1=g͌i$Rxn^ޱں:rodr$IIB~A!S''ƺ5Pkwwv'ؗ–͔nF-&%%t UԒ2@.C&Ѩ"ܔW?0{?ډNa465#X:h`twlr!IJJvT"7@,?KMhx<D"/=x%bcq\d2qqŨU*QHt:}mTTWMݘL&tJ%vivɤRoX,Ξfӆtft /]`ӆuL2]_fL:':r޾>bǕk4`G7 艓]!N'"^s0LMy-xG⩪F 2"KJʵ>nOE2u֝.sLL4merNLTT$/\b Ğ}XffW_]Ǣh4t[ cO@Ҧw拯222BPP f=03,Y0=m &LAQ1+-%1!]_~驾r##"H:eܽݷw\vh21ud mm`o* KJyG;}''O⃟?L2~.gphe;&vxnxWH$|n #20-%WpDz]ZNgW*|GX0{̙=W_~nv;\vDLf3aL2K`>M-֑>ba RNa4b1==$윣ؤ$(-+jz]{"@XĊKj~F665ҥO} ffnj헿W(]d'uTpUPHsK+ 7k\l,.nrnޏvt99u,YETUײbRJʹqazN'ֶ6[V~! 3բ ::;YxU5;~FUդ/򥋹|:9 m܀X,W_FOv`>:̞>%_܂Fa#tuuST\¯~{~N@Mx< - 8&b1~_[7'J}ףD  WgtȍsV'*QJCCnJ5LlL4FD j2iR"1X,V@Tb0ڬޑLшf!@RЈBz0ɫv̚ITd$G`ph.7nfhZ F ˙1#cOPv*^Ϗؘh:"D8]NN'fT &^ZB*gͪ76rf>~~~F ƽbFрH$o `r(~+X-VZbc JlL .]!$(J5Z фbJWĘ-^yNfΘβ%0L:EZ4_   ۑIx<:Ϝ3Q*X,$b lX;h54j5RL6nV-S&}F T*A=t:eLfl6V %!1 A&zqysܳvKD ڢ Lf3ghZ"t?Xm6/~j<NZbuq%>$1[}L9D*bXpwVV!yDO.;rhkv)̾oIDDx8i|'z#Lhp~guq3C+))Sz=Ʀf~n7/\7^ItVzO7udN̝*;:YjL_˹tU5Dl욿X">۶wU.`kЇ0<<Ȉ M{Fx#͡O(323yרjwAZx<\ˇ|ROtT 328~4df tܩfCd/@<~gt:B!tuugKn_PȂysS,|ފeK)]G ВhD#kX?jU+q&+YXbAOpPW.g``9(˘ʧxZr^_߽sgK7ֶ IDAT[!*)IJLW$RP*,_SgrEVZFAqO[36{5dR)aϟK36RTLNftq8\GZPM&KPPXCGYONYBRp?<;:w/M^B) osx(OG@{n% <}|)'+, h=s6{CP= ?nsXٰvS̟7J+013J"))qϊeKO<֬z&pv}}vt?~%S&'0#οۿk7)|sX,lynn{3y$2Ŷ|{;-=n3 M fΘM|{S&Of^}}Wh̚1̌{p8"# @ݎ QPTDXX(\r ٌF!""?wί~;OMxr [k_"X|)q_XR$nrAaa,LXy>ɻ w&Ob9$OJ Hl  j9w=},_fN0azkj $:* NNn?=z{p\QVV+\NfF:Y粑dxߡs2{ TVq+Væ ~=ɄFab>E`2illBpif Ѭ^;w*dxdtPP }}de_j~N'W^j>iߛ,NMm-Njq:;j!-uR lV6k׬N%7n"5%RI!yHwt N'׭a`pzfϚgaDF?n}t" 9444N>̫;-e*MM *A9idͪDsm4@Y3?oCCCYɓG? HDjTBCCq9]:sX̖7)Pc'PT.GǥWYr}L8xX6#SV^H!)`4rN$b1 q\t'IVRZFGWp6]CV, sryW}˾eK|pvDcс`sAMm >>^x<̛7MqYo#Yf5YΣSPYU9IeDEGɮ/9u& VKgW7Uռhmk'(07o\yffFzٶu gePUU/ 1̼'!!$RR %e*bLtTz}>E1Gkq\s#&CLr߽_p!G2?>wrr Z\.\MΘo!ZPq>/\D"ǬY3ILH@V=l>)dDb+ ,[|~FGXn U5>z`bbQոnw,-Xس3VFoB qVÄG_?k׬Zf65 FFF0w;{,=FƵ,"QT[?RZ:~^'}Nj%.6А.]˯0m/g<;1={?aws6oL8pݸ.5##h5ZͺTJAY:Ag? 7D¿D"TJ /ӋNE*N>Rd2g1^۴9z$zt,^ZZx)bϾX֯Y3u'2Z%HH;CG300@LL4_8D}C㭇9 ]t:qj'^ܾſ[} ''{ql޸#J%/C&a49։и\?ituuȖ/{?cj哽svrrrD&(*.d4 },\029RAJ}C#m,D@B!| `1 v x_r: T(HO5pݾȞF=l6P*Y!lv;z=*Փ#Iy}f۱j@T9^/Jr¾CChq9]2r$ɄvHd1U"d4bۑ r?~ Jd|u0=ï-"mvvvjd.q ""O ""+w~>z݉G&#"""""""o|h%F/\ .yJyhpDfk$nVT?0e9|#?vvN#Am梨{q ;Iz擒zỖIpsnpefqrg/fJn7ҹt?O؃vda}:NGG'gR8}&4ںz6|Y|+0+;&%Eqz.D=E%/3Ai$'76G}ssimk#8LkDF[2qݔ=RDDmʰKS`QFiMnVTb FTBVd,_MJپа*94651wN"}}1<,35uMp>ή.'ͥ={?cYe2 NXh-74FSS3 fE%r@&ast-oݟbFI) %{TVUhS1s|Y9DFF2jA^OIiu+? |>:::I9A8p0.t:4j5שL͛n;:(YdD.QU]Í2J%:x\*6#N`2HI}S<)F,3:nBO#;'ETVUc;8 ͿO ˖.&%?ysKkQ*?x>ދZgR0ٗ74 H$I6M&?98&QxƒGhhhWMSs3YܸQBƵ,, ΞKd4b 0čR$;7&sȷDt^MvKXh/④,_2d2?5?{MijnF. ϱmfBŪYt1}}8 _'-* &&ǁWK/bEx<R]JukXr9mm1s /Zje^KJhniz] ~ ,3M?yFsg'8k&W.'>.:6[æyrmCp<8EYM2r?/9jj5ײ()-7ͻyzI֭]͡#ǑH%>G[דCiy9>֮YELt4ddf̎jf%$-ϸw! @O`?/3|j^/ޱ?Fb3hhlnw. Ɯ  _Z&!8NjkXf_^ER]SIj}a470::Bcc"/VKtT_IFщL.ЇB))-߱$J```LDUR X,fH1RIm]6CdC1IǞ)'aiiie^bcbPTDFF`4'8Μ17["z J%&jlh(n!BdͪSǷYЉEn!&agg"OL RV~3)"""""?B^2h \.UILt7֑q-StEDDDD 0cAL&An~aaLvnVTRW_׋n/FIz._IH}}^AnىpP~z lii^/(EuM-eXmи#P(:e2&S&OFΞKE"088ᠩ.Z[۰[zjo}L#;QRRJ/=CX8LeU5۷nȱL<*Y9LCR_xho@`0&|cF<""ߝz%.;'Zinieժ<|a͝ChHsf'NNxvY|QtwPSWǖٶe3USU]͛8A`%$͝D=Oz줣ӯzrnNziNᠻ <N^)(<}}tw~LjN(ߧNvZx^{0r,ɝR IDAT顷O̽'pOm>Нmwcn??Av;tuwO8<exx&"@/L6jq9]v*Z|ER^ǷX3+a&uuu$Κj\1uTL# LҜ9,]Ʀfd2CCCO^ϚU+imkyʷ/GP8+˖ HJnB._ItX|)Q,_Ʀ&N'`00)>ŋvx/~uRAz/7>ǃl:哕o/""ė8r!OxzaɢXm6> ~޻ܧn""?4&͝۶n&==Ū+Pt+XpCCCbRΦ ߾u+YٜN>CYHRv'Ӯ0/ia6¥4z0sn3)IR䙝IMŋh4rXv<ٹylۺȈ>rzzz{I;3)3>[JnEhh0^ IoA 9[6mdUOR)Ebc9XAzN~t $Jx6445+/t=/WrΤR^~sfۈoo`z_gR&$3;$44KvN.6X˖2;q*+w4U+W>ͯs9 kV%7/-f rpݼ.ӞO$&$0c48"&:۷>$M&E$CC|̫/ȯ~W,\0h.^BLL==Xf6f2{,Sa;P*[9ÇxWeɒEDFDlARq.})n:HrE Mn~gRXlwJ"?&OgD^z9::Xp>&Pnxx,\0;K-d$J@"޺{|ƖטAL6FFFZfJ$f >+ccߺӦjra6u ?(a_x7P(|#V\ijn&=^\rҲh4٧3޻@Vvzg9FTdƼxJYbA@^"$ٳDO+͊*[lVILH^tzz)1Ep<~TTVrFf%҆h|6Xm6fo Uq)Ҳ ^x縜Αc'0g?_ӅZM`` v`^x-N``ŲX-w0{Vuuw&1"5V HP|]#a,Z_xǍjL9NDRګVٱu 11$IAŲ%&sÉRQ\.*hjR)2FO74RRVڲY lڸ7!jՄ)pZMgW7m0ZvԮoJ˴)S"/@DD~<,đc'fuTUײvjbbp'MStvuOy筟PPxv&M'*2NǙ)\n^x*gqqoL'N%T(ټq E\I̘U+ٷq1DGE>aaֶv.\T*eͪH$^xYRΝp0 u^Sg)QBHp0ё aN&jr"(㽟1+a&2JN'%,,˗ĥW e( Bhmmd29u"X:– `8yANe;OPP 3n[V$ix( bII=`@R㥰>֭]MLt=="*2D9()'x}b t: [d2.]!:: DBTd$!w`0 ׭X3֢heA@Rq9-kVaXF.oJDq!J}D)8E7J(^k^𢛈EDDDD~ m& NY!`&#C@(@depJͿ##i^ GDOΤžwlϸEeUcLJCIi}'qo{')QP|mv?xSDb"|DrFFFٸaSܙhlj?r%""ɓٶe=Jook׬#!-*%%eL2 PxAƎm1?totvvrCC6mX4f_8ī%׶ž S&ųu˦{u8{~?ddymFERU+ɾq"#"w߹cfD[[;㘕0E>at/2< CC\/*F3O+%*`0t:9~2^xt8u --m lߺshn㽥m~ vn߂bE 2slsǶ;qBWw7/}n,3?{-Z=)G>"r O\\ /p8P)DEEq-eK+/A ض9˥+u:-^nzN/[BLt7JaCt_K/ٝe;:;IIEPz>/[stvu_TJ}C#gϥbyɥ'ųzJr_/kq*z-N\\ [6mzQ19yLpFFG9~4Q֭]MvvײSk׿`tt/_xĄNNfhh5WCNnE7JX|v9(8[7** 9&fL)QBNn>lXCG|NjjQRZBTb0|6@VKR9zm[6S $+'J{Ȱ}``_.h5߯VKWu&"#imkC"ǟ~T"amdfeN^~B.oή.yIsx/r3;V8x```Q]v;pGpG\vJ.\|Ƕ}>fTTV*O ^ɤIq2<2¤8.gg!'O}OiMx M--vYb.]rP*XVccp:œىwt0)>PG%WPGJIY s/99w L:Y 3II@bt:-/< a!K_Wl1K;o ;'xv]n~8s(ry n>G^ 4$dW32<)-7pR˗-aݚDDVeՊܬ$+;mi RT\;oLOo/#465zikov%zU |׭&Y|)/>,gSγ|xm}GN¼e.׭fp_&'/Q3)X ,e˗-a̜1SgAk[!ٺeZ%z .^xC.m&Τ#uXV^}%Ү\W_~ ZLӷޠY K\NKrˎ[6Z[m.d2A_U+Xx!/ӃK/>OcS˗.a] NKaQ155(WZVΪQ*˼wo.8r߽9yy?? p&{~N__?AmL>"X8l47_3 ?\nddVmĂyIderRIsff ("?. ~ӞOo"}}}\|իVR|s"/OxߥSg)q^0׮h4PQYENn.׭e``L&YOJKF^OEe%.dʔ47~33;KӰZm sV=b_cxd׈bͪDjZ( * OH$֯]T*%L ]]DEF3;p:eddف9(FFFe*j5`7j LdD8rqZnc~B`l޴3)nڴarp9$͝þa5DGuZ-_~uW_yJ>__O .6C;BpؾmKط 2sƦ|)n$O^}"  \.vӧǑǹ=ͬ[ήn)ϝ;!<̮_܉|7\nVG|B;:^׭ A|})AoxdƸ32:JVv..BىTU022ʓ!H8 õ}Y߬OC}#P^͆bdu׮f^:ŋpݼܳ~M)A*MFf+-%*2BalݽضeKӑ RvnJP~λ@_d T`/&TJ%a/޳kW~U|hjzʉŌT"a%L2xӞ8_3JKh蠯իVb/iٱ}+fNFTT$*'N%pNLll =>m*n_z#G󋟿1,YАIz!]ߕۥ^/x;$]^xeǞ}<( ^oT*oW*H$N'nBnVrRp{<8d22_pAGApP(|ΨÁT*Ewx^3%ՆA"EN'J/RBBrƦk|r<^/T:V:lQ(8$rnvA*E" JHx}'>Q<^nA:v)r;[б>.o\.:}+HQ*}=tR1vo["p8!^yyF˅D'Z}v>94/ƥq >VZz9xj:n2s*tm(b2Q*(JΦΛoPU]MYMjW3Xbn.3jϤhd4Kꅋ[ L&#M̀)?XT%Yd1&H$>hE֭LOBߝqnwck  _8].OA0!!R]}⽶}y\l} dwrP(~q>X~~8;*>d}gqp{J'\#Tz>$rC/>N x[0ƴ}կo۵|H;թR}|p`Ww7 KXh(CCCp:8u8].J%3Kk[NPr9CCtuuR ǃ\.'$$D5vuua6lV ł㡷NGKK 8N:;0 =5@GEDDDDDDD IDAT&xOP8ȏ y khmm{f<4bF γ۩1X<}RpW3EO;/O|:#mx"^/W38rwϬ~)? >G'vžtuw߱!/ϾGiY9N:ʩv^/iW9 vYWHrud㴴/Mt{Υ=>6͎NKJ˰ZKJz;nc{cs?J}C# `s%=&465{rA'|Jk[=)/M~ACrse>|OTKWxn~%#ȣCF< t;eߠ2s #JQ*TVV뉏,]MfV6o:11d22eU+%((2h2!|-!%Z-W32)+㡺Ÿ\n)^lFSRZFN^>* C@9yhj-̙=lb\͠&zV q5#f"""him&5ZZ}ۋh@*PRZ9(i9FHp0K.bphZEbz))+'2"'N'r9~4V+ӦN{q/Z˼PT?y4 Bee&AqoVOMm-EŨj F0}}wlq؍Ƭ5GDuM 'qVU*;Ki!DRϿL9V9466W߬N%APbRtZ-2A@Pl &'7R"eJL"!aaTUW_p@Hp0Wүbڐ\|_¥4<^^ -=ݼIXX(.0.Y5 1o\N'aʤIx[QNA}ϦU7z{HpӦrrW3|%̜1(yp 6kY9E@@)ȓG90f'΢&q1h.\%[y),?ܑc' eŴS070wv"9tEzzzx2ӧOJKˈlZͺk8|?<&C__?W1m?H_?aaaL>COO1atN>J!OW<)NpёQ_e̜1KiW"22Xjꨨ%ߘC522l` fBST*+tFTՌL_DCC/^L.C1e$V._JYM3/RYUMCc2er*bc9~"?P()n{,Y9Z)~oL+NOҌV]/aڸp9gc㸲<ݯ/ @h@I"EI(ʴj3=zf⽍};o6vFިԲEoD=i@x[CyKD( RQsof:{Ϗ̌ ;A ٴ,n# 0c >~?JeҎ o(vsqm j5y賲hmkGVR(-)&3ol ?/ul^l"''p8Ao` ^b';;+o~^R8bs ~?A\.R;|!++z[ݼ\cxxSguХd4V= fYYoA‘0`syL&#˖.fHU[OjH$0Mx^Igg+W4ޯ(/g}\ވ9Fk[#;(:Wvq Hr)--avuUS9Y3̒E̬('++U+WT*8zZ^_MSn`+,YY3YQ:04^(ߦe KRVBzzKX,)[Ѱ3p;p5Wa6YڰB(//Cӥ5˻幮'y{R+z%iniEq)j : SVZR$#iDuU%G /7cddGOoE3fprsrzMfTށ ?‚p{']]őcIq;w\" fM̯% 1x"]J惼qEB0m̮ZEDQZZN57Ҹ?؊"=J]<-tFr*}yZ:b7E GGG|J(Hhέ!jefE9`_xibs gW GhmHmb4,g@Rr(aۧD,ZW7s,-=L;.~!sjS3mFz;̤HJj'j. 7t%,o\J{ԅ1O=)8 ().b1*gD$gV(C!N'YYu: %EXVR92QzY,;]]Ŭ@R7Ag5\n7-mlxq*5{@STTHnnlO>luku8S--#s+9_fdQ*Rb˗-% S[;Ғb inF@ @Yi HQ,^DA~>yy80 ,[@fFZBL(FRx(c֬ c2h\$9 r'aXp8̮ffE9B!\.7Ul663+=jɻ{OIq1%\FYYi!`phJJ`IKK#339&/7JKKˡܜ#TFd2!IP26v{_7̌ 2v233PԤSU9Ap8̬(l6EwWeƌBF̤rEEvF#}},kXBNYi èwZFzZv;R4cYYɎ`իV``pp"l.BT*rss(cfE9Vq]}F1Nna2RۮJ7l+ IOK6EIg:=vY3xX,c2mu:B̰q ޳OZ r{KIq1S.**$nOgfPի nNU+lj5,khb6JrEJݖ12>@9@N-###7{l{;z̭ʴ/\lf&`MZ|{\rY35eK64~_/JoÒlٺ 9d2_WZ8o BvvrA`iÒ;~7̘1h&Wcp8ͼsL,GRqSdf؉D#;+O&"q2@`Mp\r|U tuwR hpph,AFAgeeh0dga49琐Dx"E PUX,FvU+366ƹrL;$p:$6/LIIĵnuuKEQd墧H$.e IOo@IM}> ""C Ύ]{n:pyGDne4Xq8Sۑ,e+`k] 8ND^:PFhZnR.+^z/7'tt\JOF Hs/?0xyFGFy?pǎDI$aDQ߿ D_\dxd6vZڸy2?g{zw{޾~\dmlݾ\Y22$(#fͬ xx)X,?=O$x<^Dw"#m6iV0.P([$.x<$IC!OJEgȶRo$I&Œv|=B0~?.`(D O$ qD"T=\ljf\ěoFE>>$F_;ǫA   !b|w}"#EU?Lyrݸ|`ݔ_ߞfo2/\RPҖfA]-a\$`04[[$I;pFC,v;vqÓ<$ ~|n*gk;>B`-)Eozz{9u zɸe }6oF0BPミ.=D6<8YYXOjg'}qytZ->?^y9P9k&j=2%H ,^Ǜo^GssϊFrssUQP(1v?Bh~ﻩ4,g޼vdȘC؉$~N>E aq==\|KYz)IediZ_HɈN#3 o?G^NF?{i;Ao_?>Y̊rmI]<^ Y$ v959v/^dT*pلh$|ŗ(.._ǼsPZ?GF=.oy\Y͛K<?%oT*QTHU,ZZwD'OMI XKSi{{=9: ;[K/>͖^|MFb/L۹{/+YrsI%r>{W*)#IZ֮u9VZIݼytI`$$3. $QQ^ƾ}5"#s!G$.O Бcw=~|1<^hy ?/'\^h\ơ#G9}޿B\|ֶvVݳbkBF櫧 HjG;v^fjy6'Zw @ZLzUVr0Lrs ]usjiMEy yQVZfpߚ{gKO'3ÞvqܵY( * /7ɘ׏g۱lo 'ϜukאTR9t(J ;kV2 d)/,'###ĩtuu h4.#s'y̷^C?o wX6AFkE|ː@o(r׿\e˶n~[jM\WfE߯o;V'e$#s [_@ oC$aͽS3}z=:uPVZJEyX;h|Yy@ ̯Sl޲DZZ6HkVS3Sm|u;'&#=-}`0Hllh0ŀWFfZyBW0?ܴ=a,p8TWU1םοTWUwb1Ν{ҩ(+FfMffywfvfM LL|jL}QfVTܴ=c0RlwgΝv\Jرk$R*)+-Zg7n~+$Ϊ),,@$}x=(XR_OSs+3+*dm̯qiݽIa/\lbS (J]m-E3ر{m9ϜfKG"VD"8NgM4Tq8\hjup:immBS36[:U0SBьqsvW^EJ!su: uf*?7AiI O?ƥ /ZU+&^h?o<7EU*x}^2( [ZzQ5afpj'6x,f# QTdeeɨÁfsY)PV322JFӉJBcXyL ee jQ*U}IXV\:=*UR3 b<P(_7Vň <ŲX\.7hJ=wp8 26>^Gytu (J[xq:xDQ{S͖NVfO' ,3: GP(]i`BY IDATbbOo_?fi8tRŒitXv zxn+ua6Xڰ#"xɄNM9lYhlQa!zBqq8zy  O9G_ zg~ ܜlڽX4jEؠhJ危 IXPWo~2Kš39‚0e4#"(q&1ٜQTF^\L&YYYh%??VKoO/V̌ GE4 HIɠ;%? !`\lj'{M?ٍزmZx?ҋ?E$h4PnI)uk:|eK8~'NbdtwQTFfKgzWիn/vϊF^}Ϝ8y*3A6W_h4T('$yL`݋IN2P^ ?Bzz?w܊Vr>h4tujSB0 :.]F֤4uTr@" 鼐,w8؉D"Q;o׮]̬i>vrWv)--ӴutPX+W*)P4|_U*PA׏a}M>6,xQxvStvvM9Rd|GwJ 1X,ddEYiJkטYQA$`Ғb:;%rƱmhZɰg0^JEqQ#H$`УP(0 `%饪r]W7m$ֶv{zyч/ s>22w_jM-JZ JzORa2&#RChZFiV+jkA,t[:C(Jf3 \n7yԢٌJ$O044DNv֗Sc2Q[ZYzt"###eVhi17|:߶̝Z7"(>%g7ot%s8qB@o>wuOEA@Gb8Itq;H4eVP Dp$B$'ᑘňDDG8&Dc.x\EeIpN9ˌB {42oAkΜ;ffE9[dχ'BAA>$SAݴ>Jw+a0F$;O/9},YY8ٶ}'gΞGQE(惭8s<AQKk;wFQ $6m…M8~{CN+qI{z;h4ʦ[r*e%B( dN?uΜ=Z&?/7NP\4cJ! ҸtMص%ƞӗ}vMVVzM;HOK&p:D"(CAVVNeƌ/6 xo^4%LGwL5#KEbp$@DT"TpP$B8& H z*js;/4x]0(UJz=Yǡ#GY ̬(rL/PXP0C#,\PLJ2?*ݎ`nQOk[;UpGj=Ƽs>cvu5|5{!̯epp~?lis[).1Q}7xgSnbTUbmv;. ,_ӧsjشy ˥%}gǯ O߷^~"J3 9r'OAP`4鴜=\Gعg/j ]1wSsl "YY9w sY?IvV&N`>4JJh4/~[|pD(fph27o%C!"WviV9vƩ/eLec8uY3+D 5,,7q *gdvu5{?)).fKrzj/㫯zI$zJuedd[(s3YfA;N&I9“N زmY'ql1wN SPX8M-8I)8͉X} <^/._uغ}4[`гټe[*voo--/\XxHͬ^M Ijٺm'nZŚիR7RSm;i\CCC!vz}rZ\S;J2pq!uuΟ7$DIDBB|.J"(@"'D$Q ! 1DA%1H~^4 h !@Tbcλ1QT3G?DӦ<.673oN ZM,OɳskdLOC&ȎSO<%';?.(p8FMq6>vpqC̙3' a2Xp>xIO~C*gUq ۏ^gppůo (o~c~x]W/FVdJk7݆B@I-qh,F{{o>cli1 h5ZpEt[:sjjx:45}nB;g^[~^.9945"fR%xQXPRb搕҆%TQT4郭ֻg墚loN'uX\A-:iizsY>eKhX,I A "J9b }dtPR(!  Dz@9spQ23(BK/>χCՍ1Fk[;ؽwvIKt {;TUb߁;i nL&#`Kdt95'Nn144`~MLuZD!V\(LQIMYSh!( V.ow7…&;JT {>0Kx<^‘(jimkrRo<̊ vK<g&&S:  @_E3 -@AAWT+22ww@Ap|8!&!<$QDRFA"$$q2B8ƢQ"0hx"1Z fFFsmf =""sPPhb63<2Jq $IJE(bq|lYIZh44,GSXf…dggFa)..E nzlә鴬XވAg̙qj+7fM}.77L8v.V@P9k&HNO%v22A7.#jESSCYi &155)/+:tw0VKA~XIYjwo9BAfb!''0/ZHNN6e%)u;@ff%% ;+X,LF Զ)*bFE7.%;;lf1xf *D,j瑞FƤ\ivv6v -Bbk]jܙ|9_H xBkЉDP(`~-<1=IBR\cB l!?/рhL?7 x`ZyWKQFDQ䐤`@Tr+nbJ-q233) 2-:<dPBQ&#yPbJ(B%O$b:BA2Z8 v~oMZT*nV+#rsQTD$Q1>(J$S~x>| j?ntt:BX لd"XZ&&bq0u$ZnCRrtX zu8*x($( FTV PH!DBR*&= $H_,#######spxZFRFE?jtVk8Nbf:.PU ''sN$+3J(w͞<: jAB12:$Lft:-ܜDQdxdL8$z&:'$17++QBdddddddd* 222p8bPĄߏJӚtҬV,+LpiAJ(Jdfe2<ZZ8x(@X,C0o> AM D&H$B s\0;w9$j9r8α1=F.;8|N3~9N>BS3#DQd`hxdC)# fphP(˯FwOo¼k "IP&&&D]ۼ@0e˶:$&522:8CC8NZ-ex}>cc 8Nxr$Ibl|!bXrikoŎ]{R人`0同:pߏe||T$M_?@ Umm41<4[$׏?0HZZvA`xdѤq8 0.nOc20I8~]1;:W^xlnI<O޹{/v{8x(Dx<0#J^/.B˯_86oI=w:8zDg!34l;%I71sl|: r388 [&V(MI=IᣈRIVT*n:h4hHEQBV1c( ҬV$$J%LS"K`0HH*F#pAQj5H !9$R(IQ_3(FMͭwyyysj8{>u=b*`(WYp> P3X,ƥWh\Ӹr^~Odfe2pp8x^;A0bS3O? i3 oHil6IvΞ>>K',kXBzz:w"ngvu.;.QR\B 0>5rywgNO j8׺F;wW;4.ml6cnҬVN9/@CYQ(4.[g=+'}^m֬^koEwSM, 5 sL&v Ȩ#:):feE2fw7.e]Xff3=x?o.6r0 ,\Poo`hu o~lۉBD q55թ}6oٚݸ;w# Y 9WOa`sX OɅweRR\Ķ{NRǧ`X(+-ĩ,_LCryla^z;y`}sTJ%iii/Z'j58^JgޜΩᗿ=G~шfC%ZH,__lb%4rϊ匍3<:JKk˗5f|/R:yⱇQX0鴺|*%Ea0N.~  G"x<^Z-VܚTlj5}?y ;p_8iW7yx(k$V'҃n'|rYwΝÙyfVGGz+Kd/NZJ}:j!JIg[ Z={}9W+)PTRpIb J JJB~@4`0hH$f͜I4Y)#HY?/^x.65?$ܜ45X ^FFfWUR]Y*=NǥKDQ =z3X‡W_g#x<>OJ{a\n ?N̮Gz0)΢5WE'‚cs{صC'fr&8y4mw5O?3fN)Æ'ٍO}4.kGN&ˎ|TVh4NF9l$=n[N>'-- IaAfxd>{{ۣ (/+!._g]Vjp8~2}:|Wӛ O>orfUe%O<i]x-yHo_?vIu3g$_ ]zSRoygy9y4{V,u=ysjhX\ϱ8w" jQ>:6[:V+7JXPWǮ={uP\\DYi  JKS3:p{t3O\\^/1M1cTf0503e$^} $IB{%t >@Su&M`N5jL{{qtw`4tA>T S:eMZ/羥dIRotܛ: тQB[V3j@ (/z=AuUxppߏ{Їh`4 #͆`d2c0 Y F""#$ ] @ŊhJ J%&&pZW^c1ҒbIHMrQSSK1=wC2#qt:r:/^bETjjgbcc>?q%7:|Ғb(vCQQ#BT"3#1Edesӕg}egS{ .Vx9}( %9ȀMFZ:$%&zIJJ/IKMd2!#=xOqdge궵Wrr ',, YIE#U=NCC&g$&&P Y`18vErR"y9\HKKL~?aa8вDvGG\DQ 8vSTXȑDZۣBY?O%$RSGExJKH'99N~~^("J"fx</G')1<dAP2+zj^|*(Υ+Wt2:?hUC (2}ObEףLh_%twvAa48,V >(I}޻~Ea^Cc._ 瓑FO5442سy(" )  ):A0E$I"S>Ϡ0:IA>)*3ʑ()*R3544~d1ixp߿!Yru:W0IAT96}0C %g(J^TMGlX`܄;!dՉHp=6l%d^=fea5[HLxpfܜ֞܇mwʃ f,~Y $+)6@QTh͑fp=JtC]pT/XOwt::a⣣է[Fo3p:Eli#!>yx_f0py'@1unܒ@I" UDi5aH~?xzŐzZͿϯGzc-w՗.?Ng'xoYnFK wxtvޱωSTT>hh,#HA5BpzXN ՒR邲p rP!D$%Kt:F A/`Ŝ[o_ᄋQ31J޶Άsb5wDQo?0jrsYh[.ƕIJ%C|>o3nV\A?EYd=E_/Y5'c_ʹ8tY,[u'jRxW/@np:_~f|+t8SD{=?ۿ‚,YX6ܵhRSXgR`~Ⱥ?Z~wDQ+Q zZpN̙GŽxla늢c(j<ݗL78}3#(nDLw_AIGl8D j `P d( XGEbg/щZY0שI$:r>i!| z=H:(+2^l&2, A .6ĄU pSL:4>۲ ^,+e߁C,7gV?h%dxbdYfJ?zn- nc4Yq#"OvHKMgTiN`)v܋ n""Egg's:w%bXذ/m=L:yXakᤲE }.&M['ngqTT3yDssشy+}}ÔIQ(Xhlv^$[nf M(¼9^VƠ_=+N/݌lZ},'1!TR xXDEEr E*oh 22G( G˩ }o{1d]߰KAΟ=M}s;o;v"@pXM)ƍ˄F咜Od Q_GnWݮn].:p:t8;pv:qv{%BBl1vfAfF7.ra;e;uǎ] vӘLF6oFGή.@TTmƬo@$bbo[r^:u2ǎq%{Ǘ.ᩕ9v$.\u՗j8UvR2Pׅ(و$F(ZdY$5oH#xE ^c4&l6"##Y-ZC}?L0w_ϡG3g/\`H Fb\[qR o=^|9>ݼ~v;))iC.]urK x] Y`] ym2@@ \z-[-VU|`0{LM{㳪NkjJ2aaa8;;Ymzlш(Ja6Ȓ̮{Yph Vln\oawIOKm('2oynE}C>NNWVhKسo>DJj&MdlI)HW̘ $Q`0%ýRp:IVE ( RS^WcS> }:}ܣɵkuv^(SCCEOO/?k` h|]$I^ճUjh<$wZtuw|M.YAc3ʘ2znQ P 2VAF+HHj &.4D.2qqw躓ɷ=Į9&kj);y3Mhhhܕ62u$232q,{>,[khh||kD) ys-ř=EУMY )xԹ@<7fNS%Yo N ֚$^ %f54444444 $4_fa ^O=LjhhhhhhhPvhnAa(A5LZ3&TA*zѿ nʙ3EEEE>}|>9z_ #^oO [~y]en7[w|Nѷlݾ3T΃tl;rp|  y9~44l6\ad~>;w+!'M } q$If:;:Ά֟ o+~k|7[7/;?e`_ap7jǖmCť V_|Ο,8x7zB;mʲrG;q4~T9ˊ'޶EC|{T {nej\嗿o/  ThkkG$:( '+xͷtVwrFI\l,|'ϭ]}OkprSR "ܵeuػ ̝='##=iS'?ҥ:B]})I(B٩ J0>#Iڵk,YDNWP[{sf ]^IVf˗;gp9gg[GIDATah:N`La&.^HLt4'Nqӧ2"?f͜Nvvo' F[eLQ!x^܅AXt1 \v7I9vmz-)hd9'Mb.VSSS =Cggo7pW~=ƒp\aʤ՗/3oo`6YRkS?&JƎ!''>RM|3$QM Nހ^} :nN󶶵yv*8AI\89Y&ǒMOo=ovq:lڴ)xmvvudg؄_#]&neJdž*`pǬ3غ}''Ngp=ҒbӸ#J]o`EL_ʶt6fΘ*M2ol\==FEFxyGQSsv:;߽+AVxG[[;۶$?7Dv~FzzXr*T 455s+Nbld1rD>%ż򳟨v?C>ʻ p#~q54a4ma6x~?@X NyE%οmdM+/( ۶FK #(?}q%_Q#FFB|ͥA˞炙8:Z)m(PM^n6E4898gh'f7\9e,alv3\i`sB.Vԑ':wX;_Ř'Qy@hιnm٬?u1ز IIjMdL)cm"Wv<}zsP^Oɀ)[B.&1%co[GV?=zZhrߦ7]W) ɵz,RN>ҧ-a]mc/MzI1~QD=^F~1 >8g՚fM8H8]`(L#0j8^kۤ՚±6%ѫ!]Ke z4c8͖ʱ6:_@&jSUIfoCV?EZ)\3bpj|a;~$O+VLWVk ':$8@gǑM W;W< -GX`>mD;|Yk tFfP=h #~6e>}!: A ǯB G#:9?0$ C&oIENDB`gammaray-2.11.3/docs/manual/images/gammaray-application-attributes.png000066400000000000000000001460051413310443000260360ustar00rootroot00000000000000PNG  IHDR4+ pHYs+ IDATxg\IIj((SzADĎ{?+ ٱF/"*JS@AwR$|Nfgfg<.hL&s+|+&+#;حu~ FV1tAn˿q<?m8".N$-ψ򊊦n NrhqQnȿJy/)A &T^Q!-%5m}G577v3LBbb }[1!~Cw 88pN8'&8%v+J]t%G}>&qk7nGv+~V6n^̜zp ``Gp8E\TOW״ !zQ<<Y?6JLH|y!SW+) rre((, EXH7}_jsss6SU]2c$[Vgqj||bhxU7VHܿsrߓHz_yC^B8 øcB/tvWKt[wtMp:{>PNV¯_DEB%%aܳ{jZ/U"~*-T'N)UYYB9)͝61($,))qӥ$BHKI","q!:~tӝH />~  U8f#x<~s_lڰ!Lw:z B䐰nn. 3Ss3 )$ !TRRZUU=f(Vu79< vqvRPI)777+**,?LD)u޷oE<&ƆV8!&Muu,!ee83zԢ~~n޺3udlq菨@:ݡlwt~L͙=*>rH?vʳQ1JaaU/_< 2پ.z]v//6V6q8ndmٴ~Ͼ9UFk6TP= x k-))u9B;?R^>۸΋FE;CÆsDePN{uƒ::܅܌ e8["j2u &칋1O[DWZ9qmieU/BhePQQ -E7mX6(8ǙX[Yt܏=WVOmǏLM4BT*N&y2LhX||gNwԨ?qtdS>SU}瞆Ik[[YYoZ]]y;~s a!GW^ߵeN:+/? r#RƎ|{݆S,++\BI)sgϘ5åvCGN0]{16V!a*#GcG[K#VZy@ZgλΙ˃@$>~gOx. \}ҷY,-B56o[(!o^ /y{~>_̠xG}5ڍ[P.p.,w!M 5QQvQBVVb_Jz-2u@ hiΝE$Μ1uBTYYC"kljD]Zz>|l۞/_xȏ*ef&\D"ˤI6d @4g29ٹXpjfdE%$224H{ˌϛ#Ϗ}vMFBh"YrXgطd2 u_$ h4ˤ$=]m~(6rlD"DcPFF&Pĭ,^"Rjkxm-MII IIԴ_H$9Yߴ:HǷ١&F"8NSCJ򊏟>;:x ؎~*YOW{e"(##3.Bxaee⒁Gv!;'{ugL26ؓȂx<~lɋD)))S#E\,%%".V$''+({Osbcmyν-v>}~dBC UP+Qeͺ0hU"cH$<?v *BKA g>>>>>^q1-_kNӥ$%Y+DDp8QZlQP@MDD!T]]x*G1L2Bq xMb[k[SSۼqmxdfN;PWSqnyEE~C:mL nK$t:B=0"jjj*Yy̚y|ӝ~Ꚏ_o١%S;WUW#F3 !T[[]oQW]\H$`]:P]TYY"eJWBMMM1O&"ęvJڻKk/[[ZdO|+m-- u}{8N_OG_OC :liRrjfa]d2xyy{B&7c{-_9a+D&WVVb'UL&R-SQA"ɋ2}(/BBdš ׯi8֝{g_ܰΫZO>1C;]XT(V&{׳J016262L|ta..,$(((--Ր١]헎Bd2?oMAAʪeokE]2P]ab)6ޮ]I RoHsKKmmBH;Ɗ?NTTdHߣ  cʪ;v_vc|>lagy`oc_+6_3dBhXzb06{ <ܗwsZžBQQkWHg㓒SBdAf,[W/2zڡ5t:=21׈_Z3 SKƏ~(JE74|VԱL"SPXy_VT"!N3¯--4O D"ɶv>}W\R2~XN/(#?WP+_&1򊊨1(Չ_2̴ Ug0عO:eg!aeeإnvhIJJ pf&YAbFC]5!L&վ2 ]`2C'R(؟eѷ"lz7}/~!d[moov+~gq`)_8\ss' ǁ@pq!&Q_0-uȂ人nůmoo v~H&UUפgdN!TIa=ETL ǁ@pq 88pN8NϏ̨l-~L[".𑹺spZe.Si8[;n~\}32r"f: upN8' ǁ@pq 88,s|:t؅˃gɀg2 VJMMmBK⳸ƮV_ub?mW N1DEE^%%=KH|f0N7tt4~ܚ jZ|n<}4P?84\G[ !$4-kC}T*c NM{n¥׌ zyΜ;DP}Cú[>|q[HGK!e ǹLh]PP@*Y\ӝNur0h~*8Mꤡp\Yybjb皚uk<B֓QűU^$lѮiWG+UHNIݱ{g%$(ۮЮ*) zYYJ!C45f }RRRkldٛpx5d27wxh+U@i)+)%]QQqֻ\ ^\>m 3аکg#\{v2ӷXĨԪyNn^A"-Z-hjq̓އ(?F޼m|*IWGk8ǡYJ C MM---XEED+\\]?U~>}>TAaQqq<84!$D&ϧhL&D"}D]KH|CL|G--А!rغT[SO˗.RReev޼2 (ʵgX:Z~>Gz'mJ9|8Bh_@Ȝ?]JcGU/ 7fK${}7vݦxXY7gfJHPT8s"_&!Ay!t'!7 -銞v]LTTy 3cl!E9_PųTFbbk7 u!82SɫkZ[[-LDMD⢥+oܺʳmŵK\d?k}eeN_ҳbr'rPSS׺MfV째< au+;Gs_&aN.aQ\[r]6 K/m*/SK;segwlF=qjC---Xz⫤s۬XUSS˾٣PbDc=m"/[Q.[Μ .>KZk.;y1_'اn38ɎuǞ/ĩS&>teX[ KJܸtd\t@ E]<t6vxALdȮ[y`Iɩ~'|xZz{ۂ@2}zr)S9Ĉ}>^^SpPN c2ػᑓlGpjaQ]m8@}AIi vw.t557AYYVT 6zBZZ!dKPRvzsd7"xy.YxἈ6m=v䀆$[GcF ^YH:BF}ՊHY\|DT4SS[kmeA&/]~Ģ] IDATY\뗿vtZ"n2~~50)i)Q]S#˳>Fn; &LgǶǗCxV!x5yݎ=-p}2lkQZJcXx3'/]m„V{HHPݗ/ad2@/<";x#+>gKlii1۱w0 C lbb~4altuX?I0#dLenڥhhezY/'':VeeՁ#G}!oho>x}rJKJIQq4BH$ ,{M&WXtB?m9ݍdg{RG[IW[ k*g;fEv;#`&UUWN|5~XJ>Vφ"v.ґAoLfKK۬wyy992{=9?Ə8xV\$BHAA>%5 !vaaMM---?zS];F9oܺS[[[W_LlܩUCDEDLfpHxdevEVSS{y'9M: !L1O:8v߁ElB#srLfrjZ|BBHA~H7 !tć̷ KipHfw`):Z/]bO4Naaa0f0&̩?+N"'r&)7oߵc q_7:*ʽp3:}ͭ-<u_j^v=ٳڻy\!2Y]MUQq(B`:s>$++e:1`= Z[^zΊ?q=}̗@ () >;wԼ?sNIPz6!A=e;6X/++۷< pT$fOT3'AJNMRRR}leeՙΚяϰނs.^|cN)IIkKS2ݖGl7P7 z+???LjXt7 AXXXSiq 88pN8' ǁG4j:azB|\=h?iZSuW*ꌱHegXC ^ȄrPԀG-~[E& j7=vw ̹)ao=1!዗l/oٱ!T\\kzlVx6 A2iEF6=ZBL ~˗.RRާU-m''󠆮Q܋jXϰ+zXKTTy 3cl!E=VX6{ O&o47C]q;9%C!Vzl(끺z C}]>~1?l0SuuMkk 77777Bhҕ3O,t[afbljltٗ\o7f̮[WWT,p[Nu!nSF[>>E \&a]R[[2r RN̤hC+*"mFM 5uϟ>i!XKsGXOG]vkK#.^VVV.-%b}]׺Mjx௡չNPV}ׯikjޱx\\_>:;9Λ3 Åg8@3S㭛w_BB_%?~!dnj~j,_аNyMzzSsP]۷te"WI'Ny*Zy\sg` ޸sACCpXꄫo>ZISC +à7nR(>GD"@zsscFb5!D u$;gEKm-gL6ǯe*M 52W,*.޼a͸c|N>{pjnI][NNv(;)`%N2Qp([QQvNB4+;΍ALƅK Q3Aocg]!nDھ吷OeUB()9dϑ#Gؼ}B{8.ѽGkWjtv793wU(=mii0(Fa)i޾%6*t{>u->~'-L"Cݻ5wn})A߽{ܹˣ]3BY9"=^+Uypʅt"heivƕȐbb`;2PaE]Fy/ Xƭ;$%%B 8qՄW\}qD&%:x2̶M>?vd BNwZx}CX;eog}+ ~hma^U]z93? ]mء~ñQFzy#B=[yyh,ʂL&_z @^_|[ E[ ϢJKF*+J;G,C7Ѷ]{V{n5cG,]H"񥤄L))Ije%ZNV}0 DZjd]amBH"n+/~`2555l *T))IVIIOٛQ^^r1[Ѵ4GV{EZ)-+?J;ft mw]p=fdCh0^*/9j]Zc*DtB`G&AN`gc4 5U ,dРN.tq^涨]VvNǚrrYoUVV8rȁ}|||8.1Oo_hIi)kDVV!D$ iS8;9ED{j~>>11ѿG]%R#AiRR߾JK(6t{@JJI_.."c/&*:CUWt@ G(ZN;H$ _;d~$;ߓ:ښMMZXkGV9INmwvsdaԴֶoƏ2BYڪP\$_pGveb2---o!>apCd2+6`yp=֞n a7v߁ElB#srLfrjZ|BBHA~H7 !t]V̖7Ze$=Vbd_T\`0]yam9'Y߼sE˦C>cFnP+TFjnnz}f=폟>?}d2?Ώy.ѣT7440,ҘG)`2O}ruW /Cm]Buz߯Yz`0:,EGK⥫6XH32)?84F򚚚)SXX; FG&!u[w;x 5.\i3Z:88ܼ}/{ 'f&ݚ;{&B()9dϑ#Gؼ}W ܴmpšQaNz[!a'}= 512ȻE5^J#cƏ}ν1EӿܺsOCM=]_W c2!T\R9ˣ7o_,]{;6*t(SueedvoCN-)-ү\"6*tۢMvhޝaA.1S`$?D.&nnnB!av6֊Cx.Y+g/\{!]iߊsr-"bbg;mQQ/]5IB^X[YxEšv6O>ҕF6+ }ˆvz xu,VY$IRRbH3SY<olhPPXeP8!4BYI\\uilLF(+HH$2ҷ>()-鲻 5'<d#8!y{m۵WP@`ʉ///婩PBfLs0** =]".VBH"]DTD{-"C JzffO·:3e!oc_"QG-D"Fa< mhhpUU a)ATTPx3'/]m„V{HHPjpA>sVZZFGiSܻysmssLȐH !'&&#WTT[eJK^%hkibe嬷U.^>?0lûY+m֟x<ųdA[e?#ٛD&WXȶ]{H&Y߼sE˦C>cFBgdp*# F0(mliiy( v%Q_pޯ[YYyJϵǎ1560/CM!?dinSYizR@P`PPB+*sB1>鴵ede0\\\JÇ@dWRR鏟H$YY-0XpU5ja$.&AԫjͲ?́2.oG!gU?c vCWLa]?zqAy5V2]~וU+nM3 ҟa=?=UqU>ﺻfyx#,ݏZ\F]{jkiXd[sCK'O?~X"`k8pN8' ǁ@pq#׊KJ{)A,?m'8^4{Kwt}rKt?zťꪽ׸zymSaOxjk=xc'Dxx5b^6U^n`j5cZBL>56:}sŻ.tC٘xk;c lU+{͛3ߨB_W'9YtأU+L{2>'{FRr*>'C*KIJI@R9TAx;cǵ.u򵲲ri)K"BB3F IDATSz|M? zViI)B9sd̷Y>~_%$(krFFF?B(>!ҕk*G(]];Ǿ!n@a.YMrrr߇GN2i놅Ga9qx-ѓlT'N HxR!=] 򕞑JÇ!5TxqcBCbbƲagc8Ϛ.!ֶq BHKC=)9N'$2II}ؘJ"|Ji0<oce)/?K~j0E<oog /}!i֌iSBBҲJwJʱr@*r唗W|o5{Q--?fjkxir1QQ,1]k7KJKq8ܷoESeB7Ѷ]{V{v-//-!~324|X⫤4=]veVPG%%$XFu!!2{=npN||bbE11ђҿ=QT\"++Ʊ])--c/GLLth]U4|#\a-ۺcO:1,"smLD0Dg8/s[ĞX\\Үyj[w쎎5R S32zj011rbuu BTT픨6/v/ ^;v69L&395->!ڪc6 SY FQQq]}O4NnnS^%4777441~+..IIM2gdp*# F"r@1L{;2f1̖Y: (Q1zϓ bbdahxd&Y\ǏXQhxdaWP|ByXzmhΎ=gϜPWp߶koee{:omblXZVenjURRqoYߣ>| pxyqMq+=׈ u2D=~~6<Oܵ}?B}C:pBKSsKJJxy ڍ.hkj)`)+ TWKAJ5M͖ltcC/=t&Nһj{;;IPSUۨX1ܶwp񂹲?LЂ%gϜnnj< `D {s$(W):=]JJO:,Tje,cӸ.??z Ϲx%"q9$%-Mdr9AG--->~' $.))1؍_?{uuu=ɍL&7309!@  lkāSN8' ǁ@pq 88nkkkk%=攠P̟ݏZ\SVV=ӥ;JNp% |dЍ j^ZԺ~kN}z FN.io'r]4wt+KV731}!^KH$YK ӹgrH?߼\"47559gd2YWGkbb=EE/_}*ZY)ϯ4| WgxǴϪK̚Q_.;ĩSRO:._aۿ`0WpvPTP(//{D>|JB `;*Mz(vDEAQ ;*(CmHĂz!@_C=|_dgď;=?wӱ$iW.qٿwJm]]qqg_ڻFoZ!d2tCWTVVj_DJJ@)2nd9uZd}waq1ENV_ɓϖ\kYngD9duJZ*bfĤrrI$RZz{͙n.--Kt^JJۦkfΝ*5OXpg?*TW 6tBhp{[!X})A;+ ͚?p0*|WXF5}AC7vZPL662AnWE^ᒯ^LO|Mi銐7:d?A:A X<ܻ?rS蚬@u6| +v=s!S"wp1#3 !W`ldE& MnJOcimn,,*< : N2n>yH$jJLNuuqӧz}":AJWGTYE>F`*RX\"e0jj22ݔT ("*pB22$ݟ.[pǞN/+88)+)}VYYEQP@)&L8>95?pyf5yy9Oɋzw_geeՃ5B]1mj6AjjF#B---ko:}2w׮\E?b|U}REK)(( lhlNxY,ͻ)5qosdeJJI[}UD؏z\^x_MMtzኊ!BE---8n.e9nc/]I(~\ZZZscib٧Q޽O$GƖgY'ICHII- B(nei>t`CG"PiS&atZW˂WGjߥիV̜;?6̜̚yێK۸FMUuǶ͚XCϞ8xX\B|CZx<>rߞǣW]_]͐!F?̿T7)cè}1q.\bXcg`n}69?[`dW9ƦܿxntօM[]bI)WEER?&fCLQ)lG=,,TRR:WK@i=3cR2z̩hK"h/Ki=ihhqGWIII "L&=g:N'B Ё@@p ty5&6VQcN*5w8a$8hqGy'b}#uHi=Ƞ7{ѫ %oN7N=kT'G~sΟrO< 61}g'Uw-)JBbYYyk["iY!1Ǐm|}k7G6dr"'=p5!**??}Ѝ _B$ >ڹ{_51s?< \xobeo9q,\68(ɡUX\LR~`g_ڻFoZ!d2tCWTVVBE<2k)XA;kn㖉~'!>xtL51HGg!&l瞇EDجӰI)r$3nDZ-97Ξ B uuv6D"H$Z#39lҒ$\<7k'B('7]a݈}SZjk?V;*+( !<?e҄'.L&//1yB ̺9p?"""Ç tŪg*((xpy11MnٶNLR)j┙u#N>|ŻjjjӫwWTbk [ZZp8`=].fs\^P㵴<.yU.\WW3n۹w8 F ˍ:=pzudX<Fn7&k+K}]]Z*+ghkkk?i!ںֶEŏK@{}|4bŲO{H356 ߍݻgY'ICHII- B(nei>t`=;E>vhij-]`on Q3h?6c ۺg_U?x;O۾koO :a3!o?~Ñ F E"޾{p<=\m]}aQȮrMge˷Oٴuu!l`f~:>#t*c#þ$0lHi=* W+*))}^GnW-:Ly͝cN%EEgG[$@+~Y'q"gWCCC;JJJHN׿4 ai5oA9IqZ "L&~~D?k:m>}k%»C"잉 Tžny 'B Ё@@p t 8:<O*5w8(@+~Yx= 7/DςyA 2GUfqSc#z5AVނ?SKKKu.n/:o>{֌.N3.?ƺ+'2HiYy~_UʪK<!>nw\Ao8yl[SCc)N? w-)JBbYYyk["iYe&Ɔ3O-c2+B8946 TJ7߾}7X_OTT!TV6&ON pu!D"ᣝU3?6Zlc{~%G(VVFxӳ/vCL7_JHLc2zzW*++շ:D"hk=+`_X[YKJJ:r,!1ܻY3O9V>уJJJ9ⷹf B7mKvÕ + 7OJJ2MupR<q쩘jX+C ~})A;+ ͚?p0*|WXF5}AC7"T]mC7ʐ]:p,a˶> ƺ88%\:?q{pRe%ńKeb!prrddd<}/062rrgMO|'Ogfa ݺάXP[WtSo'. Y[S[[S[fLYYs8;tUOwЪ5\.Bh+Ǣ"N*,\uvccw2F?**fTUU,Fɾ/mj464RRÇ qkVȐD;wmBf %''!$/'׾9</+K'O K&.Z]V^^^Eŏ-&**6VYnc *2e!FM-oJ]ܰ?X>q<)NR ӫKK˜ƍ[ZZY, BQSC&rudSd?SXBB\ZZAM6KgG`)~="l!D!15Xz'0_IYIÇö*BO4aɩ3SyzL^лӪSf͂:;5X,,h4<B/jojb\ѝPWݸy[JUVRlmk \,}XvSzǢ";3@K+3Zw 6;ӱ,@ µK:Ԇwrkldeێں:Y2YDDJ|jjt@8W.޽WSS3^cw"% rl6u+ ŏKx<^KK'oյ=,,*~\2|j uӧOsr:YFQSSS}21yCTLLKKǛ{j1Bb6|* :1Voޔ]KJaWMM%~>N=wАuv= %$ı.^f0j\nd4w$x4ZEEewEY[Y./VYY>i. IDATuOwNc,-V,t0ػD1Sc؝Azƍux$-1tѲ@-a;+**ĉVC f}t 2j/юm[v6Q\\,tx̘HƟ(t3~A T\;9Y{G޻@ hkܽ}=;E>vhij-]`/`?4H{uϾ .WEY)$8HIQk*"D5 #$0:t"lɎ]dZJm]'gԐHҦFC]KuuEŏ,ͿO=֗dY*,.R)R[$ZaÚ3o,XoaCT-%IK]BG{`sKˋԺ~p}=sB{wm믦FW[#B(gϟ bXYAp Nuuv6D"H$Z*+/?YYYzeаCB=~N<&Ϟڴ5`dn^~ƍW:;9$$&G1L==+jjjN79!D&̙9^yѨD?_1 +kj#?{*kڣ))^sƻCrEoq7"6NZZz?]ֿv*%-Dl|U]YIqK Mu8-'7:pb&l瞇EDج:tcggksֶ61QQйw4*BJR225E3K rs^pwFNV!ẃÆ \\NV~w"So+gob+xw䁽tXHmNur{U]}+֮tkh;1a;casfN-*~1y#B/f䖖w?|w ?T_>"/{?5e99YuiY9 OU`#:ehbr&>գS:ɔкe2IddQQE ޾sWYY!dinFPr>|ؐ!cCi)w#jjj?]`flEHW_W[H:=)%m.3620KNIXTxBobcmu6B(qiiy`J<7{UﯖugD"Q{ ?G/'!`ysR3Vݻ+!D(`DDE99=#$ryXF~t:֝ءx=Ia0:4`x~~T5AW96~^vHTDDElBHF``Nfҳd:*]mRevۣfb6(S4I;vBh/_]q qpW̬a h?v;9HG!6"yy9Oɋz/+'+И_Y &yw`XLyyEE~aOm]'N?2@\\!Ծ.<?e҄'.L>їzo߾/kmk{XXTs)iZ{Q:ps{JB BHNNvi7os^CccU=ԙ%~/\`pȨ#hceMٵ6p^|̛3kozuk[[SS3N~rΚ5_zLW?{!꒔s_}Xgnڕ!˼~Sf\zz{GX,GU`qn˶v+hq+H&xrE---8n. ~sb}t 2jhS2u}x)T `hN- B(nei=GII)w?:$*r`}=~A'E F͠A;،de# {~=p]OE;|+kjjŕN >~χ#9~Бc~Ă262\n㖚5U6wX-Xg[8wt[bٮ{EMp*J!AyB!˧Μc>zٜY7oۑ}GUF}ϳI ϖ֣R(rz·B%%o먯x} 25wftL\jZF9m+$8>zURRD"uvY$\:]D2Ll = _'B Ё@@p t 8:AmmmUTJx͝)N$ _ )&.^GG{G;>͋>`@{~ELh&Fk؈^hhh,s`?mְzD+go1.n?~uÀe66.n,+*~=̺>&$&OmS_'&"dIғΝ:1y"J*sƍOYAvų#F Z,X7\vXV6&`U!28շ:Dyy9[1"""!fŪvnctP̨ǯgp?PJddH{X?"ݟ=oۄbOф=m]hg_ kDܼ #lCNۻm۱g |:X_oK45֮tk˗׭^پWSS{ EA]?BHUEeS--/_͞VIQ?'7c"44>:tcÑ[;8ݷ-ݹ'۱ĉ.]',^~e}y"/{zcQE U֭۝:ja0}μw?'&*D VP*ILNuuqӧzrqt .kXO?v;9HG!6Cyy9OɋzOlmk"ӗnްioյ=,,*~\2|PudX<FDɐe^)e\. 55RR4B?`0j:dps{JB BHLT_~_Dx~F3~uں¢"C]roi Bz{RUUnI`tHDْlHi=* 779*))}^G z^sgFĥeSIQіDn9Ё IH㎮$ W. d2L&w% Ё@@p t 8:N'BGp[[[cbi=R(^sg%Hpў㎮rO,I AhJ#5njlDf444 _9}[0z{$&_ިrqM9AFfV﯇ɽ?|.B(`Y9C?߅sgiMΜ>2{BIƹ-_D\\\j]8:Κ)&*M#ːeϧN=2^-K&1|Pt0Zx O`/p?GHDDFK̜>鳈CsB?8~"6"| *zuYy`>Kx3r葜>}t.\UQ?<\WW>=x:)iK]>?unSq,y=yñ?rDiYų./-+SZ1[Qfc? D)ho[ZV%JJJxi)Æ~Hҕp8ʸ7nj_;wmBf %'';u,Ӹ?/^з?>}|Boyy#fʤ rr!{[k~'FP6**Nx<^KKեCWq1'xx^ݻo={byR7C)+ aC#L jgޘ;{D"xwS~)߷ J>bS'OVTVp>NRde) N.--Oǵ6~^=8}ڔ[Ւji"BmBQ_UT{(+ϻTJaqq̪**kV- ?7ws)II)/Gv5R鶈.MIQKBS𸎷tmm~˗֮?U`?WVѱ|r~""kdoL&N~AN^Ua?FSPP贔 /_Vod>}ԯ_?l7C)<#rr !Mp\糯2577X߆p4ZE^~Teeյ.Ĥ;XYySv-)fs8/_55 5UGrswUSS{ÇW\nY۫גmGϞ`X^Kxhjk?2SSSKKKiiY7C{v6ֱN P{)C͋>`h@ 2GUfqSc#z5AVނڹg߉ȦaQG>722wF06^s.UwɕU.n{_}&܀eثDEEUUTlgEHHHn?~䠾.vissJ#F !\-A'1p;;w_U:}tz,3Okld=y,ܼCxpښZ.e/LoNMJH3BhAXzsKSu5CQ?R) EQ>]ܣUTCvR(RUQ ]jYظ |/g0j:dj(:ER(6W]͐/R]ESeeՃ5:65m`55Uarv*yzLtIYFF^^ rr+g2xx7n޾'#MB#}MJJ儗_ʒ+ڟ-~\Pt#rrr,Vss3dx!oiڛ.>tUS/_¦HfFM-Mн~w>{ܽ+ rs{JBI{ha>7"w_ϸ˂\.VQy[/7u;Bi3ߔY~Շ =b>55] 47+-+OLJaL%5UGrsw[k"8tDLLLSC}M.W˂WGbׇgIP F6g^xQ%O)R)a[7*)*hYߖDq+KC ;mqhmhYZB_bme?51Б'ϞSa[">|EVVEEEtuI& qpخR!A棻^pؼm.2.N^Jujol:+}6mݾa]H/شu|YMyӧU+ V|0E*gr#?W$tCgKvr!A C*))}^GN=Ҳn"zoPw+~سY̸3#Gu:AΌKM1-D!eێֶ6Izʤ }ݣ-IH25^hA_dZ!pzURRD" 2'@&~ t 8:N'B Ё@nkkkLl|׊wJx͝)N$ _ )&.^GG{G;>͋>`@{~ELh&Fk؈^hhh1''oNE=sJ Xܷ}عg߉N3&vM??9}:xA)iSΘu*WΌ9fc]~HC=}ӷN!2nvqc16'{F:fTCߩ1|LO*R,^{~}KKmlc V"Ϝ?{>55ٌDN;ǎGM͚Cר%>5]tp$Bٳ?"!ͱ *|fϜq2.8lȐkd;|WS{TTTT\"%%9kxqh?|dinF./5wXpejR) #?qVQ9D_OAr싨g6ɳ6o+pLfʰCBj*j*h} EDDB۰9%R'cb63ll1|Mxs ?p&-Jsfx"rr zM_oЍ!55zko.+/,OwNP¥.Ύ ة̬[t3-]jI~ CvZ-&1)e|̴čkvE),~|LܵK߾;N;L "A8p|¢YƆXʭ_|̊ey&+=)(݆/_²ş]8+=iBu%rrgMO|'Ogf!?oώW>$'G g(GuC;#:t Iڥ 2lۉ**K>p&6%Ǎ>7woדׯ M#GY_l6<|4ĸ} աsg߼*Z3kNVzҒ>؅tw@Q?<\WWe9:IIIξMIMwqtB>ӿg/\pW/']]t D&zIxz̬O># S]]4x%Ox<>*rRo9y_W5ii[[[B)ƶ?{]ee%?f %''j,ͱ%CRR޽6w Y7ow᯻l>}ӦLYn76|Ve`ea>l`!MMt]K >}!4t5ܽ-dg}{v68N_ON;JQs~u^&E曷}ӯ[]M i?|Za}lhSdEEn.{OѦfM>{jd0f_iqg/\{f:zpEE¢▖7XO7ױ$?.x---K ޾}_P[Wq  LV` <}~T Bhś7eגRl6yUSSsWvmK?i P}=7N;,&*گ_?o]swuIJI{9/Ⱦwvv'O!Үg)-4b~> hyXN;1I)i؄U/^VOwXssszF&c鯦}5PB Z{A^);**6<9{PDEl(]D@ aW"ja5t{N4Ovvv'8dMMMee},G ջnڣ (-)}y N)< ׈t}=7oߥ섫_ӛ/3_XʪKxۜ93óUA:MZ ncGiZ`͆EEE*~ztz"x/3dmLk-x-㧬yt5 %zzltB$.iceɟ_P7wT*@_wy-|ϾIɩ `LmN)ZG!6dvz%%9eee1(8d&4M|HKoݼs |{4%ED;@PPPo];[ׯY 6l5cO5# /9wvg7#m"/'Pgf̞6qhSnSw?r2,:jw9$ Agh ޹Ĥ+((t=NOI x| >֟kׯFԁ)iڪy}*C{ᔗ1111֖\!~ 2  NaBu`<{;xxQaLb7nBݸydQatt舳TU5}l,]%8hCgWu,/odH| x< _ZVF&5q`J&f0eL&;sZ–ޡg.Rj}450i9snbiCD;}/-uNZ6{Z56=y`_A^ ;v@6/ X]M<'m!,,ϗSTI:䧳ˉG'"P*zoزʻw# 'J8lvԹBTA[(A&=&Nb -.E1q:B*qVڸOyHTL\[HAk,Rƿ=KmbVHI ]B?s:uh߇CTZZq&Μݦq9UZnOfo͛kl:sޔ}4lĊ%JiL ێ=o7LSk`b?$^[9oY6k8fn(*&޸cj3x4:JZڷ\9YJFCFlCO3OiIҒ5#ts|W.5!D&!: 4=coď%fN*kNǒB,YEj&w_i:nLF!&W5d6HIrn2[E3'F!}*.oKqAȷ J^xN:޿yӇwP'Bu)!/~/uYI9I=H,QqvԂ u55'-X?%!'A(eJVE'"&.*@&wiHVAqÎnB"8MR!E!!P !P !PӦkNMdG!%xBBBBiMlץVsIvI | B?=iIZ)&pXk +D)]Zj̠P0>!j .ثA]T^ަG#Bv^s""I$?~f1mF-!IS=NbD̚c<vtպk>VLh ^QshNq]iFG[]8''GKHLށozEh͘ڪkv֤E^ZFc'ھ朗1qSƻMOLMo˶%J4uرZ!UuHQ:se+V{/]lmi^QJȐ\n@2G IDATzKJr#ĘdX7n?]8DWo4h38q̽ve\Zx9>~ ;zΆͷ6/33 :4~JJJkkkLMs<3731656\4u[AlXJR6osQ@UUײO2Ϙ`Gy%pر2 kWPVVV 6ݣ{m[6K1ήkV.BgC4Yb٥QUOG#?rė/ =fײÇN4chfypߞ>_fھ#(;3@{u[L&5lef`PwEDE:;N|OtqsrsN΋VŋDFpyG)JpP 8Cu]3kFbܕ kW{v ܾj~}V=O"/ǒR FEF806y !?yؼyf>qY7-tK=f&qQ#ϟ6(--[d}BLQ1".o%|S˼%$;aSSn=ʅ_OA XZ=u,EiaLj̍?2(d2\} ы̋k\.}$[wzJ걓^v5->{83B Nd29$xg?,qs>65.d2=1 :1kT*JR dz4YYp%:ڪgd2yxg_vSQx,F4KsPc !]wb쬭i%HZ#4 uc⛬MU%1/{\vWi7--dVLZj$MXXՏF^Ni=B6d0ld!z:d2YG{T ?{tf>ڔD"icfbԠ9O2ΝNPL SZRz/52lmiѭj o=S& Lqx-)^WcOqIHP]4Yל%UUɩ7mm; 04?ONVy%RUU&!‘"a==siWXc-v6V.G /&>aɂr_H MO窕lɩ7btjiY9F ?~2 pw@ɩ3^2TTPRиb.ٰYB\|"aCP :9钒[e*(3*dƯ  @@Pa^d:25+--׀Y! //W}f Mv[{Hb5ٱ^- )H4WX仕%.+((d:[8ZO5:9%Gzs>oh}*9~PQ{ysg 5~a 21G4;q1{ F&.PKKKMt;w{t]Q:^\[EEyalؾGTTBC Jtɣ jo)++%+++9ictl$^nj1QQii<{Eϟsuuu_dedmh0L߸y=ABBB#[/-%?(3BBBߥKKqҞB&_P\b06_5B0g=ztO@Lh( >={lް,^H1Y3[oB)ރ%%uuҟd<}6x@MOo`+˖-!$((**5cokq123WSSsx B鯭ND{Z(16>[>AOw{2{[6VнG\.:s?IFMM DїٜV32͋rsrN}4l>{>ꂂB?@yy5Wr E} UWW_Ml9wﯧx?$$^k[74vX,G[S#è?xɩo޾mG{Ž`=v̴ሢuH<(--{%% EEEo$x<~MtLO,Z8H%߾{f\n^~>1`ЕkZR~IyĪ)+ᨪ(/miʵ֭^j! ? \Q#Gx-ܼe[^^Ujh7p@WoWR–z:ϞHu۷ѱq@LjZ[pLA^ٰHEYwSm }N"I޳g"k+֐)dĆZA߾#h[N)ɱdd̫rOȁlaaQZZ훌c"tI+&8lkcPpMiLuyo عɕ*)*^ /obl'K-Ml~=}qĚZ}]<8}{xV*蒒S'RgTy9 mmSbQ#wnK ]FKDMEsmHNIu07st[ѧHHPh3ПH%Ohj& ]?$ &?v+WBuO"42L"ޢڞ_ʽ$Ho/:w:2 @!~7ۥ'08yXyeJMϙǏc !~C NBPcFyyyJF B&9!BtB=*!Pׇ !P !P !P !P !P !P !PӞp+%ieK^>5 n=IQ@!hOpZVfJ !]#ߔ.L-5f,B!T_{^r[L.E*//oG-!~[|/Qx$<==UY)+#3mkg7GjiNԙs\]l,IlwozΛ3}f԰c'7K+ͭUurpzkلq.KΗbH}sdeۺLpc -މtI$R㧰18!䯿lrp< 2>aja;a{}\v)7n+24|2xeׯx&hfV6c\N>o w88r[#ǻv;13󕁩e[,Ke+ itȓ϶H Z?YsSR{tK?T{}d2p'Θ3teK6y NR Fݣc/T.--UbUThK&_CdRg{_ ,LF^R ƦF|7L&sq`T(]Q/^f.Zꭢ8y4mf ^as]UO  y>qpxꬬϪ*C _SSSYYgKBm/ Lf ٯKQuH<(--{?ɨ!H5r\6͛RPUQhmlz/ù\nEmos ߽p9*fs8WTUU(+RS;p8f>OEݻw!E\$uSOcGP0xyy-Tjnjr9:\.7'',G?26>K< :MtLO,Q;]RYTTTrdMBk3mн{}AHPPYYie?՛.T\Ra尉g@0d6HiI$fO5G88]x]un=gN=NXzmܕvEk7({ΝoWoi&CI$3ڢgX)Mi[rFg'QQQ1;qJsnQy8=%冫Sg7!kjϰ.=ɅŁ[zW%؎Z~O<ݰٷBTDD{_$dt!!Wן_B=zpnhF=!PuȰ^;'DP(:[BB{<-]lLBp<>eM9:biha3 k{۝4l1B:}%E^wAAAe%% s)nhEv'O )^<6ѵÚȹK#7{wlUUGOLLJϘjd"ĸKJBvYTDIa*C66JL=RKLL,&.~ƿ0}ۏy/]4ƞzA)SΜ?,R-8]]:JM Bm [[i+z05gנ{ `.;ysDD4KPUUl+;{-G[#ǻv;1++~_:ijIIiQQlXkdOK***Laja;ap~ݳkǐDEE Z*Ju鑣$$$꭮NRUUUtFe2-_mfeo3p- 1Es=Θ3ٵBP(2D"&Bhjfi˛X8877u\;wkR^d 1ko#ҏ?ejjau]ty%%/^f]{囷rpvܕ9uG]c'Miln3i{gDήw=xR9yή.ػw`+/B*홰UAkм};ð#Gj-X?`;yj V,*.47sr3mqf%%/3_53JT̜Y3lXKxzԱg?~ =tnܼ'$t WcvlU b{O:]Gÿ_$=im]dGN oIDAT|⹿/={~(.)Y_7{j%M 9lwl,L.}$[wuuLl r}L>D Dz~ЃG <~J@J1uʤ+Ξ?s Ip=%S[^2x ?{ `gq1wlң{^glmyvSU81kfLP(#=_?ot|dę8ܺ},`gT72SCd4,M%OP!ҎBVp9YْR1}C@@`.{/C}AH-M q'4PR\rrMVahGEiJt)n4 CK>lZϞ=LyΞ}Ҳ2ie9nxmUUuAAW?ٱݺ隣#(bxܼ|g1lj;w!AAQQWP~b d!G&vޫKgGՂϜvݿ{~!!ݻ90΅)=}:Ù4m&O#(`[?XY$Z:Z-_eh7NHKy-Z93óUA:Ma/md2v|~ǂ `'O6]0o?Ahz-WJ2Ҫ*DNcÏ>y.^Z]Umanjgcwr#4 RM#.F?ѷh3Wrr'ۃG½.ݐKJӟ<>6$oٺ~Mu֌)ߎspFR;nB]YYٓDFT}D2-ўK9YYzC:Uo ع{ F&ڧ=z3O>v,6j9,Li4Z;jA?*x .6sGg )//obbb4 B5^;'DP(:N*BYxNBBBBBBBBiMaGO巚SNVvT, B՞vDn][}{91{&,B!9bx[5>JkDA! ma-`?F-!۞2g׻^Q&>~j߾ \MLZܻή>rst8NBm0Um ?c74¬"""F? tС}6??I~:t$]G | Ve8'nj *B֗(驕\+!PǠRHGDś??j̉&f(BExB.B.B.B.B.B.B.>3IENDB`gammaray-2.11.3/docs/manual/images/gammaray-bindings.png000066400000000000000000001207111413310443000231400ustar00rootroot00000000000000PNG  IHDRL@k pHYs+ IDATxu\S_` -I+bw"݊""&v !*m(a\ݻ vv=gp2|g$y$y1tv$oJʝEַ%KJlKLTciG\soᄄdedDbg"8%>}te({Q^N/d2:;е|opBfYTWWwvˁ$#]V$$$$$ 4?Ύw[fcĵmH]ܥka/9Mtia^?/^jh/ |޽r57w5tQp8AIFVܬ#=z(#ziktآ1~o޼ۺy 0 vÇLpnjBȻ/_̪j=|Ў#5-OBB!KsSAAKWO>xn))~+ysfiijy͛wasl%Ea6f"Gݼ}wmmKJ6lڦe:@]vMͿUyyH>gϜZdeee֧zZvaܗڏkeu$[mqG H;M6022z'Lh0TꝻ?|d2 ѣߍ9{>}nKrs8? ((q?*3듻s~~|ZYYñ JJq I^||beοWIQqreeEBi99uq8ðcպj<\TT !DR)/..@ojIIo\e%XYYw?qcxᣐP h2!sy0p~9saΜ绋@ ޼}w}{vԎuǬ~UAWxn3u1,"M֐ܶ ~,i˦>}c+B cƎAgЯBBB?|>l頁g`N9 ׻FO^^NV`Ѕ-)+)UTTV״(+)c'%XZh&PTWc/_>zbx#/.)a?fz؊;9e N[\Rr(,lanedT˕>{tP!tA&6CR |*9ӦNnV3455}R$[7a-oz|| 45 /u_q))I0j"PZzn非@͛۳ɳOmn㸽Bw?x8q6OXq+ yy ZYKIJ={A?XvN.B~Ͼ'hhU1|JK˨FI}gX>g!-L$7 RRR-\W--cq [6|@_(;'YŜ9GZӦL>ts$$lp8cGjbbbOܺyxTCcӧLpܰyZvj45I?y|ޞ3Svl,$$D׷jܸь xvlی'2XHkjj{KX,K#p8\ee9y-PƋWFgϓ 2iɷ*Ms7^zf囶lOH|d"VK!d@gFvu3jzz$%%BZZjj OGڑdAA!2^5sPSw?seed _D量h411QY6iB+KDP}}$ sSgc2^HI! '.&Gs=^@@H$((b 41p22vÇ=ODRVRpfr?Z^QA!%5-''$ &B~Gf6`+ ꣫=wRħ-L{i$ Hj"̀B$-Mh"Ff7jy&F|Fe]glh=cf93D))Fݬ,Bj_~C=}p-={jp8K 3B[櫩K['p}KJBJUUU\jyy92=/}oc0 %%??GWƺhp!Va6-9V#fS5yyGRIc08NJJFc/Edn5q1rNxJ!6'2eeehN#/<mk!Gmeg;#/8ީ}'\ ﯌x<^HH8\voj\l6;"2w~/UFFz*BMpG$[gozDDDҲrZܗ8<.>Av\|⼟_~YȽ0:43+KA^Kl|lB>]ϷdI uSCѸ)($%ݖ,7wc'v۫@JjoxI@BØA&y~<ƺZs}_NTp8W[Z%>{nia[Z6W7wMm5茌˗m?>*=ΟK/-=~LxDQ3'6^)i6CpK>;ځ歞/^"jj'$&&7yrL͍MvZOgiavbݏ px*''}f#Z" >p3T[WTY,߁4ؐ{aEEŵTjaaajv-4$1ll)(HŚjG**.ӫ%555///u' leq5FUźp VXW_k6#$(Y]6wQlYYYUUUZzƅƎiC}jaQGVgf~jHH# <뷚Z:;>&.&w6pZ1Uӧ;ԙe?p?5yOXb[޼j\b$FP! qqY63v#~~bpaaQMMه׷OR)I^ځAp8Gxd=*))%3 na#fS<G{zfj+ܗr뛛 {/,;;GL\lyHH;BWT#>aӁAϞOv[`komYS[KH͘6ETL{߿DYӧ5FH ߯,rk7ݸpzPv[@PyO1^""+_|庨`++Ks^~P@MMm=~ ÑEsg|CznKEDޏzf%%%\&86A[P(qvr#Ma2{[f߸u挩}x&W[155гAWЎ?k^'H2>}ן=wA]M3VQQdW=K pws>YpOOPqqɱ DZ6wآuuuHJJٮx2Eiis/@h$oά4g13Ӂ_t3zӸZY   膈Cuv:@ot-8nNtCtCtCtCtCtCtCtCfޣUKfuX0Hy+8[CƢ^$EcfV/힝ڪ堭wʤo #7~ 貌EYoA A A A A A A A A*΁ł_kc{(o6e-)i8NTT5%"2~]-tM 8fdߡzֺ5+;1.% CNH"_yobdH&!Sg~!H=4׮򐗗wHys=x7nn۰:fY+>,#˺qvÇubH]yħnCȱ+b27l6nkOu3JZAo<.qqIǟpZX$(HZn &Oh#Nd|-?WXXJ]b8Ϟ'p_ dfe훜ےC=п/_=5}zXDD:}ހ9;oł9c۟7 X8uJJgd}edQUQYflex쥃>_^,+܅G1,+ E @W'**bhkb|?m,..qs_'!l歰𴩓G;w*Z fذdaa+f>fz^r s͌v܎r>k'Nu!4EJ*Ň1e=f\Má.+PVBa?]>Ν9QZZv((6aIII]]Ϟ>:q:!`0n4+fN߼uGyy9V::׃/9xyy A~F;?OJX0cbG  I #cD`s'o] P(/_i ^{cOf^|b|Fo9%X B B~dS޺qctziVPVV޽oM͚3o\$+#s)jjZ~n__jfw*0i⒒{vP]] =DDD ԗ^ϟ;D"sgG?xT[[M>fB@ Lp$.!!BfJJJxU0߸cDEE|ӕ34Џz!A_I)s p&F-[DdIDEnjvOHl銊J76finK!4? qo߿wHػ[Uk\j~o֔Ncp3MQRR$*=z:o?g/?EE""daaa쥼,))8(V.%%YVVb E+ IDAT|u> +g2γGSap.<nkC$gw17twY>RUQAhW>}Piid3p8?EE~G=bXnZHDͮxi^F ׷oOHHh֌iӧ>xeN];B|f & K︊dySqVd*z;*IKSht:B*?@RR@ <|p3DCG޼}8.=m.28>量xS顼."x/4QS d2kjjeedVXfdhaw¢4NfVÈcˆ /^ 7bV%P3)RREZܒ:K{w䙚ڪmotjMMmyyWX!LGDFs8̬O|ؕ*) e?؉SJJj.˲>I\lc%e22N/~x^#ΞX@"**+?gt t8:4>ugia]ƟS!!!aaᬬOg;kZzFEEE]]7o߼yӫWS{3yy6^$x)bef}BLtvn]!6 Ӭ?T@N>ж:n\(""&3]̬^=;;C[UN Wcf/=dAo=GA@'dM?4e ͚Dt3GE5L̟3 ;C~Ntx$4<n<n<n<nwזtL4E"Z =HSo"SO #6 Zn<n<n<nIu𷀑<nN?xKW޼}qc'O\~GhXļEnĦ#GgԴf&wtvIJNm\>e-cМܼ_ӑB1|}f&ojS>xa :q/,Mu.0cbMj1셅8B]Nn^Mtuzmۼ+[a`+г~T uV霈AaOEyr &*"K{媪*XЅKGD )+)Lpv]NqIImoXpnϞZ1a{E[54eՊeBBBm IߘisT꙳%TԨ,[ȐDF=|zvN.,)jS忇f?xj턽uuxXn^^.ܖ[HѝƎI[Z 襥6n]8nLTxkw9;{9]"BΟ=9aB'fin8*ے~Gқ|j_84`+fxǑ_q ,DEcnOQяGٷvB"h;l,22&F1?chtZW/ szZ_@]feLTX߼xJ 3f](,,W_{SPPg߁V둼C._d$)%+s#߿pe"Դt0m1YX!TRBkP.H"-q_YTT<{b99Yl+V0kTOnp7o߹X~]rrC]|-~j.rrr j_z-L;k1j/"GEF?qi $擸ힶ6q/^칍3> v@ٗ>KJ8kZTjW}=B>{ ޼a-Li'___{OJZzMM- -v2%ɓL4PQE" lۼfK >;ylJ&L1͕7Haa!Ks3-MO |Omۼ^!a7jܙGtuz w_˗ӧNp[XX:cvc;u4ѹ"B"JH**.8INVkULmEDDB"bċfWT8;k9A0BO L2uDrHhBLf>CO3޺{oϮjO2?x؈G{xzG޽w(Cf)śV۴ǫ_> ƫWo?XRBظ~ 9:lްnni rTWdV=p0.!Q(0EHP7KL𴬬LA^~ۖ 2PXX}~׎m޻|NMǾ=(RRG145TTz 'P{0&|¥OFb\|%5-oO~~IZ'Ѽ @YY%NԠeoD4v C" ;}<|wP67xj>úǝ0];T6$om>u mmz gN ))C߾!s{9t@KSD"[!$$Νhlm=Z#y/[rQ7#{yd7nw4:}>?Nr!t䱐[׸BtIjj:Bb=,)!{PrJ%\t~#}RE[װ !t/,|#CwnۼgAުTIIaVV2^8~ҁCLf5B(7:NO-O;4NUQYy)ϭx2¢kW8u^Z١EX&/\&8 6zGFZ:0V^XXd2O ~|ZzNoC\dn6(^%um^z#OPKKH14GQ(RӧNwڭk_y0FTT&$40汚N/Dm޶CZvQΜMNIݰf8$'+2"9?}<6}>G|<|*+`u;؏:}{a~N8:O>;C.;3b :敋wnM\^^bH;N md}f77BOO124Xq nPνе=! μ/ULf9>pyqqqN_nӼ3cǮ^'ވf ,Cݽ h7͛3+6:|-i+\w8[`0ya8Ce2 {ob+HYy1QaKGF o8}sv+o_9;sv6BniYZۧwAhb /-dX)i؏CIHJ 46D41b9Ml׿_S/xZUPPpXͦ fi3ix޼bk?bԸ3=u,-_yB"%c& E:JMˈzrnxyBV{奦2ja祣 l6oPWxxK 3C͙5W[6Y\Rҧ.l #+_>|NUUe޻uLГ'z(J|<"*isF   BEbb-ؽg?xBfft:B" b[^'{58N/~77'9 ˽}Rbۧ|8C}xhB7ul&5MgM2uTWZS]0 x` PRxۼ0N?wY_r\UUգ'.jи'228~!~Tj! #'UU$nY?ʒH$q1"wx㔖4'wZ%r rr6$AW88oL曐p[ZaP""wqŶ07^zv!QQpn +#RQ>+8eee۪%J w7KK>yNwf o幵JKsVuZ~w@c#oϭ!l`/nd0 vlA~;|Y\g'ڿGMMaLFZRTT}YZZ $.<+dAwR_W/LFiij \aa6Ebeikc04 Z{;@ ׭KKS454c%"QDq=趝ު**Gܼ}7#L:jW}=uLC] !4bzjindWm]e֮igBqq? ]A99Yw=ϫ0ghH,%+[ȳ;7f HIy/]`[wxcWח{PK{j!+) Hdf"8N?1hMM]l6Ȥm  pbbd`Jq F7Ht'ayy'9CYY~>$4fafّv~}[ba61uU=KY^Q+ZQY8Kݸu/TjaEyV*''K$i4zX F aZߧ\^xI)L&SIQ!Ǥn_}qB *8`0Bؕ\+//߰vբs7o󪫫kf-QXXt'$fgf}voF]]]l6[HHDn :d+*+<]AMU%#%v,vfx,-| /-- ;f޶|S7ohtxZm]T^QQWWtO07,[U1q1f5yRP~}y'|psb w!Ql%*+)P*& !$++tVO\J `_~ ?lu W0' l /^f0,W67nݽqN3Z&}fXq9-//O|/*.u;DO?BFo͆OܹwUU*&lU>^ܳ~r%͗/_d._po߾'l<?hӶӹCYGDF-Y`qCrr޾{lCy]x/p8)i}zegD1UII). dyƙ eߡ gݗ.Zn#7s4;A&m'<.n`=<|diLfu*+~j^8٬ N8:%5mo}> ƽ&wzwuW?vݽu{N1a`RSZ~g"V^+ol0]p>B<9%LZw~E3??t ʍs|9D1b??_ ;Zt6q74Ա^ ˡW/Ɨ:|fjNx&ٻvlk!Qwy/ Y @kzI| g^t O9;;<2Р#*>ff}auuu'ϜPoUt5襥" o@som]ؤ \EEGS^>^;;NkbpI@7I@7fI^*+F!H!H!H6K9]tC9:$%6.߳sBa5p&ƭZփ` IDAT1kZ> r#ycFܶ0}ku2].h'yyg{zn&CJJhgI][>{:6L3Qg4譫'Q5&#-mbdL6am CGnWPVnVfVh"۸u1QazY_]]ݱOG> KJI=thj%%[=Gg=܁nvNF2c΋BONn0ISgYwXt9w¥`;!&CsrB,zûxO6ẒxŎ.E!"p wpܽg?vm,B>qP޺ kjjzzpJ%h8ZTD43>Yĩ@s<t?J-_72˖:۴SXH awLE8ad0{7fsfDy/ݠ /C'={'"޾{/ @>ٓBn]EPWWwʅ'qP Y*))9 ZЯ/$7:eO-O;"ĮS[7eE׮>q Ct$7fx U)R_x(*c.㝤)6CsrZD\B8NW0{$o߽b22^̙9-.!!:Ƿ|V\ rcnjC}&]A|3͙[d2/EDUU-M EyISR̚ @G ,v;Ǽ}ͯy5Ahl!ӫ22ҕ FUUL-Z4(Ms_5c:$4ׯlH$cCq^ڒY y@qJ-` u%kij ~oʄvlqudЈ[6iu @fN/BHYI_އU1555[w]i֧1O8NvvØXFondb!V!+KTUd[2+aa!!Ӫ@rs^<{!tH;[+Kُbׇ,L;;կouWX#F mz}AUqP޺4:MQAG=Kikc- 0qhu爉}`S+n+V=xA 䥦g5xk"Bon))!qֵit}~XoBc!rgh:$55!b?O|=B(9%K0O.\rpotD~Rr*BHCC}|ȭk`5/7adm?Hۯ@S,L6oae%#u[1'8dV#rhhs)CcTGܺ /XM-,ڸvSg襥qI>_w'L064+(gp[D",[0"26?oyS4$Դ ee%+ DPRJ٠5<e?BKSǏ{w)43픲dK]"Kk_oDn%/,7kiEr(IiL2m&y9Lsa4$X[Lڵm/x/;q1(`Çv@ `0uLOd1iԙӧjkku3욛'B5qtM*3J ! +oAzڶ}ěݸy?khjH5ÑѨB} ""ɺfΘF$JJji>RQ&k~m2j)?}TTBFKIHT855v/[6?/L&q8X? w/5-fQ^W45dR:`YM{NHRdrB-VPb@',iˌzzr 1=%&%Y p_fan~ɔwh!CKItMUWW{8 E% HRu?^dCN =ZYGFgffI$YYD9**x%bP+)윜G -223%\ #y;fիge0tUFB<*//wt3~Ҵvz/k|6outM7wSl@ӌ,D5K ?KO?n0Ĥ[#ݻ:ێ8eq $ܛ4ePe (+<lexL؎mq !ЏT^Qغxgt1%5 KE!BT(FKC!B_<BV<BV<BV<BV<BV<BV<BV<BV<BV<BV<BV<BV&yc\M_m5kޢ;f &//H$~|o:0t+WE M<ԴͼSB}94N# hn[Vfia.<}FRtD2Wb*E^FI{GDmln?X'],xZ76o} Kٿp*B5,P!47O^d2L&|`ċʺdokM,I:thUiW6y"t|inhbgߋKVwB"bގ))55.#.^Y 54=݌/-j̒{]t2ЯW_fM=׭hQ7n)3YYoKK3pw#|\5{JP(ѱWD}B<]WVV*))ɖY"۷u*_za @d5W.;o{aaѪu'B:cQWW[<VPP~S'dWyna._c\Fb**" (-ۇund`1772#B!|<m^=323?iss/3nH$,#dwq1J hkigMnaC.GF=|X,~[^QD&FA|mf_Sswf=bm{k9<[uu5*DDF7x u;>z\,WTgc,wżbnrJxT0!ݻ羃Gjjj?ywq(B@o/uKT̕OVw[wL0(/-+RZVFEey= 9؉ 4pys<7mtʲ&*x;:?iZvm=ܗկSZZfkkjg2>{&Vꗩ_fe7liomo29&8εZXhKÜ=L0 H޿?|xQg+3c:ǹ {ϵMhkCGZ`lc#yGBˏwfKSRL?%ۢftka';B}ͥH6@ Cr|/+oKCO9B!%矇o\^^` yB!&y`3Ow!jr-B!P+IB!P+IB!P+IB!P+IB!P+IB!P+IB!P+IB!P+IB!P+IB!P+B<1w/߶cgP5oQ GXJ$}f#=4v%n+B!B8pw֗Hc!'Y:&[!//s(D063uʤ/oSof%)hors-Zja<5"*F jVTyiw3wgcvNα#KK /^x88˥)]M2dc>RӮ__z0p9ؑן'Mp<#ޯo+^}"@:޳Gw{;hLB|kSRddWb(*=qDaDTtWcllX<^ɊUf%>6m蘸uX,թ?OWS=O'ܰ:zhGM~&K صj[B\6D{iu;ic\ ;yj[7wҹNxeoxɝ;E]21sRtս;E]>&Xbҭݖ,Ըt>H@"8 wnt붞m--ub/oݳ`>KwgQZ[6 ;:qdcחZi7t䄟;{@,vd2#C!!jN͔䙚rw[ ]Ǻ,bkXˉI;ِHn]la2R(eeQ^yDy{m A`(33+"F 316R=5:NѬ,- //?5st:\<nTL\MM zX֡C{eeFyYڨ Olyܕk#v2'#G8JWijrA&lV_cG"8~ZB@]q蒅h4ZƥZݤj2pi4yzwlO|}6^/]wȨjTRV,~OabL*MDn׮-Ĩӈ7v~)1UUUE>Od!~O8v_YY$[NJf oߖ֩PS[;ˇ9VH$R\nAr鐞L&dS\̫xΝlh:s~Q#aXE2VZߡF`ӈ3ayC2?l|h~G=:rtM52MQssjjj/32;5< BU#Tt{R 3@ IDAT̈O!H23'<^z/b^UU@ Ϟ߹t\zX,~&'thkpC@>l6K[[K s/~p>eDT̫WFbҋMgXE<D,˖X[z}#dy%% EE*GF*a|SżbnrJxT0!r%ݻ羃Gjjj?ywq(B@?[rrqEƸ,7Up|}v{Kn]'ME:MN)-+5_pr5d2h҄M_L4pys<7mr 44wokbLkG1f.ZPNiiFb:ާwLϞI곶3[UU}M߽S4O?"-hC?yFL0toqN,-J?mCkW56?M7٦ʂyM!jN&F{ZJj M9gڔB!l.G:ԽZZԘbރBD"ջg6 B}q5:`מlٸA>- B}LZ.mm;B!> ^E!j0C!j0C!j0C!j0C!j0C!j0C!j0C!j>!\B!<BV'yc\Mv >DfV"kB+ 5oQsה{iMlIj_S|?Yde|08:jpGؼ%n+>>#Nx哲R |> qhcm)Pə4uiɅKͬ? 7w4aO>Sr5~뼼|_?'W3+{枓J^ 9I|8e >[;VT|80;w E<[SbY[u163uʤs>~CGw6sݔ{u*Uq1|>ZO1Ғ-]nߖ{R0H$In Em;w_NntjqW-Z^GG+q唔GXZ@۷k6̝k|lXQn+V?Rx9oK'>LSKSY {zlE s/H{m9\Y~CD&/ln}y}v>J;^*Lld|%yuK͘3B&޿G]]M̬]AnF}z@۷[GAAaIƌ/-j);vfhn>񓧛7zBJG7CU۵k:Ԣ(1 꿁^ OKSNּdإ5vz[gkmռS|aLM̚qDtT\n}}/z*%%akPe[z_?k\n7gqA|~%N\s'I>x88˥)]M{DP0@?eF_W^ z[]]]< î];}n]elAAi3}ϴv.'S4-<"*1m<^XT4~ک3aAG=MLfxDT**ʙYeÇ9:}{[ZJѦN8ix+@P= $6mʤ/oۼ*ӾS&O$[ =gWn''O={mlJw~޽zxߎżNkV R41V{ o彅b,***њlsݻލ(㕔hjh@uu/ns/&*dff_'п>NXr/UڬPkA)>07 wI3< ^[ϝښ ^ڶ|>m:)Jv_> VDTtWcllAf".ۻБ+JJ=ΚKB\E󃏅HWD{iuPb>hX"-㬽 AAd(ӹ˗\WTkZ2͝[PfCGJ޾whߥQ֖JJJei]Ǎi1ptHL}/B@ph_S'm cf~ {hkiy[~!\ٽ&&kb ,ԟ'^+|ة]WVf:^ $_ۡaW`VX=:Υ]-_K>}};8z鐊 jě;K#T0jkkltDԴ"__HvΫ?τ^8weFfVVGeee~swq!2C/3VZu3<y.5YfX,Sn'ܾg N99*-vD6mOpI#.boD/G<^fNO\hzz1Ϟ puXi9_8Ծ~)?6x[JVZ7{/nKF5RzppFf֟gB ; }lMtJJѽǃWRw@S"Z[ӫNvmڮo*KD"9~䧎xNl'3551P5iMMh\:d2ϟWSS`yuaY0ݼT.nys~P(:V'nr3o==]{;ԽaY3~t"+cQKtHplŠ> @vܛTw2Ԙ4a|rJSv͓whDr92iom/ߡ#G9x)l"L4'<tdgcM\36#;&: Kjj[gne?a{]~ϔB(+3"39/ިX!{}٘X Vn :thO$ݻtd2L6+knKUu㰫e0h4grU۵&*_ tHtZ/S'iӆNϞ9]"i٠Ĥ4]F9)((q8m۶! ._z.2uզM2<)99V-kij~vm_zM9}8j8 HʆOpt<|t#1v077v6tK5rw[ /V߯iSP[[ގD"hk^-nigC"vn{55Wdžt|̯-a˨.=w:b/ܖ歿=1k/[TU7zeTS&O2yb۷gM9u&5ᣒ<k ~F|>g2O&E۲ȤÍj$'1bKKKk\A4ۅGZ!C .\ еX FuQV-mc⮆GE_AOrrt:2׏&%(+2}zna._c\6Y\VuygC;Бu`]{|kjk/v[r0{ BA39*:l1^%{}J$]#GD8n.>` rMd9ݰчۧWOyҹKS**Bj@P6,-(璅kjl:uh۶mvթP{wduuuiY4j7X,¢q~p&NpV-[<~z&}£lXfd7|xARd<^I=r8 >/ece9u9 6ӻwwo7V'{iAaai,VaaQ8?!WTT&ۆ:v@޽>ifvk7}ꔣB޼[}#Tvm"Ómbl{ޖN7V`(*1ϞDzh_SC`Г:SHt rr^|6NsE!&ddt;CL ;gx,z% ߾-^ WSS4FOg+DGtr[kc!'kkk|ƒ;=N̸AJ; E#6xS"dff]O ʵ4$ɣO+r-u{R &&ےk6xL!sQb7v6y/3nԉcZ[=E"Qw؜; Mt˧6x7ޫWbH$JݻWqc\.;uGCy^ >NpH+lV:UcG>׶Nc.]lڼBܷܻ͛ɩb{N"TijhX.;̮lݼ10hON-*> <:|J 9ޮ}*q9|ض3~$9^L2S>ΝIn&216RgӀ=e\dvٽ;,]4oGHi='|d],]?jDeepG+KuX,iS'o<dkdѤ 㥣JJJV7s`+*?zpJ2*z[]|&/_=HDWPmn߁4*MGG{Y9^[Y npl_&)(ЧM, o<~mژwu;o Pbr_%XS:q/S&nE"̚=?۩8sZ0wc\'i)'v~kEkjjjtC \wW}|zvfgcW._TRRQQyznU,pl#Syn [gkXO=pH0eTc׮}{oޭ 㟿Oc̝mN4$qĻ/|lgDX|*i_/,,z|67K&)**s&w'9?_`0z'ПF}|T$ju ;70 ܽMށ PMfu1<оO{g揓[T>9veF&pV16NRqC-?^|u7owgdfFĚ5wD!"H7?7tѥ-gU|45aez'ˆ>'$k Hn-7}דl5)hs!|FtIN ܽN5U:iEOZKřeH(T5@{w4xՄEIጥv)~RᨙPwb)$։zLᘹܮ4:@]tVB`6O[*`N9a m~x3:SQfu6S0Wor/fBɚԚ:t2,A?BH2@Q.N* +)zQl@%R7sB?/J3]xlGdxK/P;Ua횙_KբlH|:@[5g2nTy!l8f{- ;38=jv̻]L"fZѓ×LI^{E.κU)$I.E(^*z?ҥv&45qX^tiϳڟ mi?P' ЏKGd IDAT;UWKLx5թ HZ`HBT$L`ܵfwyՈ$SUB 2EUrO^}e)lAJuV!wh!WH`@?S Ͱk%5%*AW8? [6\lםPT%6H*w7}%iwvD})K).FU%j(+',5(OkBU:I4eiGkdr edH.JF](j$4358]XRhkqyڂ*2V* $Y|ĎԐ !Brx[ʤ $p|# Rk*4 L"&E?LeeZXB?5m9]G($R቗0>љ[Zz+2 Th`"РmmBoG'UJf e~"f!-BsEL .Aբ~!LSfP%;rmY T$3-IHA,B?RyEEcٟݴͥV.s;Zށ B}HfV%[iC#Rřx!j rmս-٭;I#U$OB!bI^X0, ;B6?ZBV <BV<BVɓ}JJjE!B_EcÑ<BV<BV<BV<BV<BV<BV5'yc\Mv >DfV"kB!m$j|FF8X[1sr&MEZrRx3+OD'Mq:c/B!$}'wW~a;ݻvӕc#]h{{BeSRBcia%oz0wc]FXUUU%B"<1y܅V\nAq1o#Gw_)ꗋD f̙<ƵN˙Yٓΰ:ry@}j0'Q&;(޲a䔉S[;_[?l")Jt'WaNs/*.`Sb*أ{7B^ OKS/2 7 thG*3os,9pD" =ufo˯\KwP!I%y)i?uzyo?wVT_jk6xkOJJm /P(} K4s՘p;[-v<7٬ ?t$n{x%%kg%!.rɢBD"ѽԴ?|B`34P[[K,VTOqy s^wfU+*?a-L^-(\#%o;4B͡75st:\<nTL\MMM^^~Mu !6Y9M7oί EG[jpDMn&kogC"w3];k/t:NYYZdeg~A_!?|(`WCQZTw2Ԙ4a|rJSv͓wh!Un>PTb0E M X\XTTXX`jr8 F-( H?OIW Ovb u颺ZyE rRBӶ`0|NW|()1dJ?Pk{/o8Q#wϛ{BR<DPScW|>d@~^>LVprHDdRݡJuu5ttBXEEҲ$m9@lB:\.-7P ō"[Z] ^ڃ B!굈/dhkp鍕3J Ƴ/@"gv&gjD<$//?˕`n6EĤ[*ʵ><Q19x0,#3ZuPx)h#mVzp_fok Cy]el[1!j.-._rG1'Mk׮˗.^iVG1y{79U,njx˳f)Vr dתq8[7oܽ-[}0`>IN7i Kn+V; PQV9x8ԙ6++ BIvYc쯲Դ7[|wﭪpww !B T(FKɓěedfFĚ5wD!BxѢ\ܴykMmm6*ƍ%4!B}G0koyB!Er-B!P+IB!P+IB!P+IB!P+IB!P+IB!P+IB!P+IB!P+IB!P+IB!P+B<1w/߶cgP5oQ GXJ$}f \nӘ*yBOB8pw_>y 9ԙ0¼fl+**~yS_z/fVҟ yGr͝h׈yBY}gI^nt$wW~v /Gʖ\ѹS/ J|s]RB񣢬,Z6v74'O;"B͇*y;wVԸ8\xdfe *((Ppw3͟3Kvwv@~^L! 7\FGDHdfee--ݍL&@tL\ors8]~T*BD^H@У6RKWb0.]l˶T*{X;OUU55~앫a'+))jNB ke7,>uu5H{]:'K]j#tu 2:&$σ ,;w&DSC$p ‰$/m=]]Muu^<׭2>el!Ć 7 <{&DQAWRv;[Ϟ/Z )**E_?!MU<9Ǎ1|srΝ '5"Bͪ.ךH{ݖ,jd245rױ.l6 de4lb-zv6$aW;[uLf~ EYiԧWnnQ^Gl0zhLT~G=blfNi4+KlOMh\:d2ϟWSS5+u^YQ^V~rDeen6**MRDd]H}N0WpO^=&޼EK$ï\KKG;Dڦ~G=}}ҹ'//('xTUn&Ktcn]u57?_H𡰓'b#/Zl#1qWF'T1Md29ƃBr-[d*+3s8 ĝEZq2O<VXTD"srttr%CP"!p v\G]q蒅h4ZQPhصtQSC- ^f,']٣{lUs?xZtmܕk#I$RI[]^GBƢQ]]X,iwvv/L8x3i;D)1Eu BoM7^p8 >DAlHmi 5 0;wׁ=}eI$RҼxuQScWcd21V.;oc< >* ދAry܋[X(-wGQN#.^9t=]&Cݖ,"_6R-**.hHX[7Fut#eFf+i !Phk=0h_@P]]4[M DC$88}3**jkg 8\ Ƞ(uDEKEfA{E}ep` M6MA[ 6ɏYg={}(/MM^,ҧܦ}5 JJ=,x{6)O)DկkzyC6eldO!ͮ55 7n*2J-}~]xuHJI+.) sJⴌ;efm0Wd۹MMT I)iMMoz_UU2* 'D"Q¤Դ6'ji`0)2P(,*.̲kB_@ߣ!aK\DeK6X u߰zWX 57<*5tut6orH464\by댳Lfnrwͮg25Î2|4љB65޽s0QTPb0yPRZ*$x:L""çM5ھs?.b=JmQg/<-*bjha&OQAκTYwf/ZRIlޜ.CC#"}|Ǝѝ;ݟ}u-̓Ab>giS>~"},_@Ӆjmk50C!Пfqbuo| ˵d7?-+/OIϘB!OI,עOCteK-;#ByX3wH} B \E!CX!B!,B!y!Br<B!9EB!"!BHaB!$C!CX!BɡOsrvyXPؽ=؉ b )9uG.F߷ajٿa 'EzB!4>"';A%߼K^;g>{.ۘ6,4__n S3w=z]SeKrj`Bh@Ê<} G,@cOHtD8/͹{<ѹu;I%!1iرٛ)(+)j&MO =~"t3B!4pȃ=^XM(\p;WTE70; ۆuaAHIv}tU/^utt̜1gnJ4ZEEoi;D"g^FC]-2L"2:'hi7gnu531H7wf!Tjmmݦ-ۼwl >F&oF:s65=sƲ)((\y a_3wlhP\Rb(]yU+D"np#߬22\G{ÂGV[<ߛ89]uZƝF:}Ƕ-.ż [[YH2/*v}ì͔-N߾sLIpN.))dj^wS %z/]Hzjkֹy,_~x<0&Sz=mbUgN***Nome7aB!4@n&ُO{||ʍkopYTҘqXVzy`rM;w]d͛3a0TjeqqWR-iɵ0;}K<{ zr$$lN̔GY'?\IM'Hg˾2thb|܍hnSSI;]¥#Ϟ~;YqW.e%;|hcկkn݈õSgc뎥΋3SOF"Bq˜;9X[ގT=w =õUu{-⮝E"pk{[9:HIQ'2R\-9,?mԤ555?ϥWN;zpJA鈸t:bG<^++[}Ғ0v˲M !@"oQ:6?{m++h4aLI%jj )r^ѣ,ބVhӧH$%%55ڬ9JL<Sdѣ$s{nEEEEys**+=7***h4OB;1\I6|6!1Y 9=oV"CԯZyõYv6VD"QZVKz'pc@p%} ^0!1S3b) HnlSZiJeӧӕ B杻vctuDBQ#߿DeӧJ|]}ܕ茔]3>JR4M h|B _eh4%%Zvu u^66r8LPCC߭",*.H *$s`0}$RGgO drGG'h4z(01E"QCc#@Q#GHY,3B_9{{͛\WUQ5u7oxM i&S]_E)mK$"NK@GG4a^U>]-(şl%I55+d1/dIr=HEe䋱Gyl>1Jmmm| KBrf7^AvIAj FCC^v7OsLN9:UfS)=) w|tk]mHPW|ر~\,*.I\Z}C DPWcLMM s9KBLHLv"'ٟcb:?f{p(sGxw]G{;u;V:\.7-!y,>˲\X\^^!Y.i?mmmmiiai g pMbJ_D"Q^СbFDSgl-oc+@Mbi.DB>j͝S[W&Ik@RPb5˲NH|>h+**%kRܦ&CB7n4l^rjWˏM7'7O DGIimm1<dL:' K22Z!gdk hHsM>VC]=(7,"U+?{x y<]͛\{G$ WXg]djeN1(7h3Bmjý{`@#8`5TaqzƯ\oMML<6СGCŽ ]d[<&HQ ޷w՗|>h9tDmr˞vd?IDATTUU=6f~psUUnZ[[g2jl'rE 1|ϞӦ}xg Ktzc~!>e^fdb o7|KkKN!O9)]6\ ttt윲 I;)BI,"yv c/!B#b.^:| ?xɲU+.Bؽss嗗W\˲2)I= ƏaU"+Qm]gYBg萁\;i[?Si)Ф%%%T ;vt:H 1<`_sr>>{.lqLێ=w!3+ !LQQ˳wV;pCK**b6Hܶe#MF655nD=~RZʖݫ!i4Bݻ11} ?_|uovqqȨ̬Y35.ÔO6H ѩ-~pݫjkk_z}XnV ·/[GTUF$$&}Dǰdfeص~ U&/?>p:H}+j,xyD$LVaa{JK7lx9t:Kn^> NUj $55V_QY.x̠J RfQF6Դ'?~jCpF_o/WK647TVV6i 7QWWc1s:iS&M(.ah2-D6/JJ uܑc!j[6#r>E܌fUsϝ=Sk<o=B'SQQfތٰqK%%%B o3ee8,=㭸D?Z[!bboIϘ=s:)HϟtIHM{2&0ڍ))_v 1\YY !t<$TEY>t_o>B\.<cbdY٪oD7o!&=!#߽;^XXȠ3nzэb.NNG1|dP~~UڳgwAƕ9~o^z[KK@ |t>/\{n|uu`|s܅" C={t0nL[w|UC]Eh /ѱrFhwm!]Vz:,3+[LvuuvS'..)afN;u:Lx__z2.t E"5 B8`p :a^!fq+C k~aaѩ3a>}Z ZtŘ *)*dfeggP(X["jkk΅?yL&:;%O>eRg 1jjG_Hh x٪7 B_|~:x8٦=yڪoFKIRfLǕE܌aXrrCTWΙdld{g26yڋ/_|-%-560`+*b2~{zB:dH|XXn~7=PnnEKW0,V)F "b'ܾswт~Kx O=SQ#OB (ʄqn{k`Hc;)@[ Z}廅c V ߤ9vb`^~s艭/[gk7ne#ϝ64g^uSt}#DQo.^(%%{{֯[%F&#bn-7[]Mg[f7߭_H$޻}9 ^FLL Chڌ9K-d05)/صg`/8i r%iWTtJ(˩=~گ9B(>᾵eqqq >';]!fq1 ^;p8fa;v1ܷ9Ys> q{'Oᝦ(n|BBEsSg'ں¢7㧜1/ja޺3{t:vzD鳫W.Wi2#;n*.) ޴EYIw_xjд:] ͞nB)RGe7n`bl]$|zK7<|LOWgUUU{lب[sgikw}&}מF}zSԫ#r7]I ;w!77!p:cIOwNh:ڂ[xn^|$/굍e+.\;+HSSZ| u5TX| C˰w/S3aӦL:rj35g[{yT/^}.*bYĸKN ЕA_|uǮ΄<{KIJ~Fq8v]Rת-o7|z螚%˭;ےHCN?*++[Y39;;x$2˳-kne qq}nǰH3a7_Q?nJNu-MeeeKJ>PdO="ltHx艈QcGB?pXMMuuax6yjm[y@lpxn\3ঢ"=#sKW۶E|sO051B=xҷ9̣֝P$.^Б+64U7^|55h%M-~qr#H 8@FJ$\|~ <a&Fj_D%%?8URRR\\K@ Ǝ%-%6rrf!C^ :t"(/'w^II *݁70|e$ybe}&+d9H$Lc;/?! yy~}-0 SPwsqJLzJ{Բ__u5UЯReb'>|#[P!YvN047/_J"f^ <|deODRUUi᪫.B1 [k`t;p!0e̴g2s>2D"Q魭!ԣ$!Q7)))12? :iWVVh h2k>ome!p fKyElc,C}41 nǰw&DXsC;77}3'_yݸ<.gIga6jmm'Ϭ,EvT4D__PжJJ%, 0,]^J{`l6Br4H@x|'WPP3RVin^Oj몪0 [h~dtz#kp=LMSd~K-!de,N=$"JX\YYBfw%H5^|‘c!Ip_|JK#XƗ-PAǒ$?k*-E-_Up{*++|Ws]b=I$"ޥE89>my7ͦP(!FSAdt~lق&#4ȥlv୼\.Zii={xô)S|qzI KC]]VNNV>sr?v~fԟ9e '`N!H # :Dј̆_q3ar8j8uгL0L&߻l؅ISg5{trL"s8 ;HQ'KX ì[[>rsG<)'UUUc*+/y ð3g<!-c#"?}QEe~cQ(67FZZ ]VϝafmeMO7!1ibZ[ßefe)+) nPFb ]?c&;h22%%%x|~IIܷn1 B222Ǐoz_W|&MkE 9א/9vsڰL'{Ѿ5A*8h;MY\d;BPmÔw;pJ('?xϾZءuGRtQstx5$AwpKWV\\ ﮈ[fAdg r}O}2f58X[މ*e57#~ԡM'G-dJJł{QMVyyōH'.w7Яho <~ެ*Fc:0cs뎏Ws ;~/tϣLӻhi\l3aǎRN0~lnشgK |d |.?@0tum>}9mޟ =x< uK1[.]9vZa 8*+X8L(!$CVUUWWWKHHٚm@?ԙ_G!>I jVVZg%Dl<|eemg_UuuEe%F#mhlӶ.+r1ĺ}xB)Rswufq1BeH$ O99بϗܼI\.|p8x<LVWWk{ y\ BHZZӧϨ_\7c,.I~̘[&F |'ð.Z< 3tȝ{o,*b[ءإ |>TL{3_V5tF c݋OڵKk2+(ȫ^r+*+o߹ GEVWWx߾/_rO9WPXXSSCO B(/֭;6V xu4J'nECGUZSBʲ҃?~_߯o JKr|>r괅aj/+`l'>(+)}#Cu6jk߽.b2|3S TC]AȨ!ZPX?Hi*5uWyu~ (*iinM>BytBaPT93χ_<{.H$M|577D")QMu` {jpʈp;q24C?_]]&ˤRΞvٰTe}p_mZ sS_,(u߁C )%9alQÙӧތ]h2Ç(**CH./ZɎ !++kblTR:|8efu]Ԝ"m BB06y¸֬aO>;g"Mf3aZšC=waU$";c3eK|ڦ㷗 hD"0#~o/..|GXd+׷]YUJJJKSa>(++WT6e"gs,ka0bx©' FZw} { §ϥ+\n{n\]Zrw$EECv92w *Uz57 #Cmo.aɽzr>GYUEe[9|DB}"ȠGt:m߹k/AގGCϜdL @ijj\!XxΒe ~7T1}qq)'l{N&z={½{ B1mʙ.0{Hֶu@7{7:ZbxzbX<OMM֚@ /e=*//aJjqIʲ?^&] *Uڲb[ FFFFA^N~}-t .{ IDAT899ʫ"ddd45)>U8+׳߽A}|H$RׯѱZTWWHu8URid!l3DX'c q3ϟ ee&AJEÇ!11 @SRR,/V H99eddH$iii =kWp#z:&`؈ÈD ˽ݿont1 wO0k EqyZRl6Kn^UU/((TTTh Rjk|~yyEEE%é())bV^^hjXo3<^uu@MmmÔGeee<&#LC]!0 T+~JR24}& rsqz(5!1ILlmeI6 zqs?kF/n R H"n R HiUp#%%YXXUhV}Cq7=o:6Ԫ@ t7h7~+0"n R H"n RZ=7Ε5~gWZ\y_c!/M<xMOWQGC*^D6O"n R H"n R HGWw.+̈iZ,Zod`ee BM[]+cY׮Gl۱k>efe\{؈ƌxWk{ȱ sm]]ݽDW_X%^|5k }w\eKՕZÙ-=j'팍4Xv><2:*-4m)BhDرG;^Xՙ5c>{!%%9uD{;9z<$.gkm4mX; D#Ǿ檩"@UE؉ ՋN#/{07151FXY}*)a!cn>*--!!1x{BbR;+ Ds̭GgR}-i^~~k{1DUBN:v)!rG7I|f|eeedTת*u5UO?n9N ;!Qr9|>_YNV~D"NM""8\BY\D"!drT~}l]M PqII虰,>)˷Tķa,.F0 ˩㘛vnmh2!f1Ѥ&,gG*5|GpgǶaBϞbBbut:=1' 2cri ƒE L:n@Td{;۸Yق6lWRTرuw5XD"O?B%% YH$.ǡZLn)RsGYV1\ʵτ;zj>PAA[F|~Tt5PYYٻ:@ĸ8%&%etSXnzzbbb555qw3ù'wwׯ_|~~AAaaOkܤ .]vܼ_r gh~.CR¢_rBt:MUYQwnp ?i*k}'xyj}9cB3,dF ڥ DFdsS'۳7i4gǘ V4u=Q$%_~]b߁CJEd5UUWbdr >v°wSֶݎ\JJ ! /h?u-i7$IOWlYi)6V"8y:=-颥5rn.\ڤ)J+wDcF+ms3sqQ/ϟ3YsxܧwoX۶SYUBHph;~o2=1b0e%%`ZUݥs,tlff!֬xQ<ҕk윽|G|WF.-e ' ]oExj̄)ƦC=7,%%enjB ҆{mUUVv.=7m?׻~baL4p@w} Lm-oFmtu1 s릧{?)-Z0W]MUBBA5|EFxro[s@k|{5uD2,` 1e0BVrT]F '^VV֔;cLڕ@ %$%?w][.!>ᾊ#BRAA!%%_dkca 8H$jjjhw)]DW?3D"B㑛e3 0 %WGq>ݍR(kii^ ު(+|{Š LfqXa=K01M~͘bVIQrCB$?Hp8Ӛ<9htBD cJeee[^ڸSqqI=ܴu;ϓn EdMMmEe%Z^NA"$9I$n@u@pSPP0%uE!Ycefe = A-[Y H$.+,.n&>PN99ܼ|55uaS(=s .AU<#bbk4L+ @NNB@K3aqww"`|?Ok$`hwz;vWAb|}qqݻS(-,E'#kuuu<ء}{ZvÎF'vrr=z9q ~"?SK'͞oث-BhGeYR]]]YYǰ7ayyit-M͌e'Ϟӄɒ_s{z ~=NLJv/O??N5FXLshk>ve/\ӋKX Ͽ~#RFe{px<^syc rᗊ̲6uxZ<ρai\/_xyx}{LX㵄Ϯv}p\̬!Mj㝛=HLLאA|穓'CMTQYAF ^QJ1}Jyrrj=~}]Mh*n.N7:c[ftVtW+))QWSV7\?:=CQQ디[Sܘk7GDFϟ=s "hw/~h 6_VwOy9iS&GlQ4?T;\'yx8dX֖m>zъJMs\Z4`}GddLMvҠp{ۂ¥+3J/%)WM2Ȱwb҃{ D"`5um;vݺ}Hٺi=Z2UF={ĸL!o+Zu57*)*N< \fRIU Lo3͚9}.wu|'O' '릧vrƏ.w~@TJKƊY3sSwc۶ܬZCaW:(t?D7)@@p D 7)@@p D 7)FDĩ3~u-@Ckowӓze$99{Z ߕgGHԟa1 kDNNOIM{,)Iiy?:9ȽunzFUk2-Xl\***xfT)B 싔Դۆ9rwz@%&%/]z+/[w2Gv3*{Vm];w7o\'('xVw77sԀ~kn*7 \Tbkywӭ;↍[B\0w֜l,ZYT K]:c:6fHЩ3a֖z:?T %77h!(5Ͱw/I FDM[·;zlش!tYQn^xzE6"}J?^\\`2݇׬xfԪ]>oxNsqu|Z=KWys0}Dค;R­[n[L|QeeKz[L%n{ pv6y[kjj޽?oܼ|Oo?Sª ; \ݧϚ'袜_kA_o4g[/Qڈq.g;~i5̜yy~#S?=<!18z~%&%?y>N!co8|4Oo]{f@i)A\\\NNގD"0QQQJJ~سὄDK{9Bu*((l\КCBL5oh0h.nE5荺KW WٳP#SVRbNKUUno3r>s&$ޗ456ވ7 \fUkB^rt9 uuuƞ\Wmߵm)A<ss,lta^ᇪJ+Vۼuy<Gq^^?>kB BQj(=}>J4xxz݌9z^|ظ*J͙YUY\z"APw}%%[w67ka~wO='(mfV.mp o~#Gww/>q~]&57 95m;>?yk~7/灃? _|vtĕCv?"{wmC8|ڥ W|hO[NI}mjlymEvvxĕKW5Wgǫ#ݸY+􄫟GiOrX%,Cߤ#>^|ba Bu+|n޺CP >'&⪁~EKW NmM/ZRII1&ʑ{N<ϼgݻߊhdCdeG޸~™WގC%&%;pxwoEnѠS?پkKb"͞|̬[pgϏ'Ɗ+ыkPMO/3!%܋Olp\^~7oNE^<b_t1q13O9g]ص︘;[&pl9mD9Y%k.e-֖&8\TtNӄW;+H޼3]`!:{.|ޜwoEX͞/))y_2~nr3N8L&2|["]&x! ''[VVVVVB -D?_oIIIIII sO w;q޻k*-O߿__dcey7>>~_#`+.dB} e Uܼ<&|}2t2eGDF8>af6֖7#cg萨ZPdT<335rO>Kspf*Bw7ɽ`kcߋ651JKhQ2H$R`?KIM63k꤉d2Y9trr0@CںE 檫JHH'WQQvqvDXY*(("vڵ B\^^~[8p`f̙0%aJ*,QMJ~hcmim?5ׯ_>631 B$aXs0ޮқۭ½Ǯ]-R6BHN_` xlvYRCn!.rsg68GᏟ,MW.[,xk^ ,XZ͕c\WGܚ8nX-@ Z8L$OTxy&-!!!!!aokgNHLꢥaXw}|5뭠 a#bsqʤ2hk{Mny~wWG@HJ~Z!0Ά I$555t~"0Tii%EVHӊK$_ ҉$"g H$idc#cܼM[k}tJM}5xBjރ6Vd2 ED5 = p8A/( *UY\ܵk&I$BΩkٓd12B7g`K*? ު(+|{&(ܺ T%yզ k`9c>WXrpLfMx9<Oqۀ¿Hd리(؊bI *MJKKKiYo!JJ͜ܛL-]ST|௷jjj+*+B1n_QY0V 7(5$))O?&*ւ˶ohnjБcϞiZ!])i, O*#!E&v7**+k*.)x,V,B*x/^驪GXY6&͞1d~cF溺RQQjp#I-JŸ$2í?Q(p ꏅTQQ&ˠ BCxTS+Z-wpsGܦy|wk!>/H$`w?/⇃YYيʪj E!TZZZ{LMn0=1y8m;L2ī)<@ 0OIM{:u!s3;wZ  {YOEEJ6L^^t"@$_xB_/gkfϟ;+s;aV YFAAa L rTܢgdT YlmٟFٝ#drw})S UL &l~~.B*+)a6^i@BUA9DA"BQDE"SxU<֪ "譸{;SY"&zzmӓVbh4$+y%C׮Ƚ7ѣFqvٝ+JPZfB/E.³:(өcuꚛ]˷07#,,ޭ[!5UUWqVT="mBew#FN455a~$]8us0L673vut_l!&$fߟ!dT%yM&B}K<>fG?5] ]EEEQva;7Dnښ+9[7e$P>s O|w~@r3t5UUMMMb*K҂Y؄-Oj#z9ڧ---g @GtQVi6aXJZ9j+njjbc=`KR qRf>?vR][!wbν0 {-'v.\zX^n6y;!\<,\WNvCl#bz-2)m^,pEGVJbzLwWWfg7v'޼fapǬ"ǫkrJ#y1V3ڙŪ9w!G ʪGMMlGK[ܬ/Bo![:Ǭj@aXI -%ew޻o;xBm=WGӌvy%/N֐e!Tx:1RVuͨ;L,V1}LMj|B:k۷y1A*Bc{~ᵇ\]ٙ=q@ui̬SdzBKb70\x f@E?ojmmv\e?KPHcjZQY}!TRv늤߽J ˮ #?z"f}{j{&HajWNv^Ԥ8c'NJ\Z340Xê}v1ɭtttaUgnQRR 6i?͋ɪ!G%8+-٤w/ IOK߳80!y:o Ôi4Ⓒĭo\m-aCvCEnjr/(ry׮vNM6#\C++˸MkezBLmɞ3kVa,@Y|I^LO"}/^i}5(y[uvfrY9N^# 8Pw?pTO[%'jy|1oV.f362:r`[@(Ξ0ITUBQmOPbѱ۝`hh)")9uCd@ .G{QMIڼ5)%qKFH9)6k,#CCv'{xMps ~%ec8N)iOj B޽P!~ݻwwjSAI FF1Ntb;֠6*yqܽ3BWq Bv U5U'G]POO\;]=U\8%8y}3vh4E;vZ4R$.X]u]iN8ѳWnmYvv*FQSU^pYښ%GF[rA-EIGܜQ{*{5k.Nf?̬e8M B|yE4MWWwޜYΎs)t҉nUA BSS.*-vݥ</(e;fsMٖ8x@ysڌu?I6KKED0631::rߝ$,&1%׼wAtNrwfNXV\ZߪϴKv~ [BlZ6~1Q+BÔS:k7454k.\ ]-I)a+;٠ |Y>Kp3{u Ξ:!{JdФ}$74|@MM-bvʔ &j@$F ijj;D"Atͧټy_a-07?$Ih4joǻJaawfRDAl1c01C!H$$ZmjY4-s8].,f st:4 dee`1[`aq׋($$D"yr:]QAO]]-(R\XH4f[ޠOՓ^'3#_ķCƾii>.gӦjw%zA f;TEEEEEEEx20DϾʙS'8՚ƑÇ^͑1:U/ŞkqE!`4ۄ,˩ Nk(YEEEEEE8*********HEEEEEEECuUTTTTTTT1TPEEEEEEEW n]IVuuUH$zugE-A$ ;#2drƽH^O-r;\.}닩=b ,Mb1^5+y2K2 JK~ܩ$',/@_LEEE " p?$~j}3`" nطDq8~&xȲHֶא3z2o[x9R}FQ.Kv [PycXdrzήgl#2ssHssͧ#>4gylimHD ioC$AECQGhik'2?owMcvn% 2;7 `[XhEgW7$z24o?ʼn#h5$I)=lq{p=B!ZZX]]}EPypD#QZ KK/<.#pܼ}驗ӦVwr|5XP(_fphg_ #LM`2GͦM5$ &&hmh4++iimEQs8ϝCFDk_G?PYy20HMU}׋(yypV7nޢK:"rsc;u 011ɒ})x.@mvZ:0<uONKx.^8Nlpgf稬`-  h/EDٳ{3BWCAAC, %#CH=(A ==<uxHOO%d%\.VVVp\dfdRUY 55TUa뙘zPy0LB^(G?`ߞݔIYY):Q5z͛jƻxAIQfA_ҖTQQyذ2t:l dfd0<2ʮu QSSFؗٻg7Kv;EE,.-|쬫}c:w/NF|ʲPݻv燐v 8NignSS3ٳ+^aq8[1 ,..( (COo?zydRrssL~~YYYaz%qpZZ /O*E\LA*hTT^COLj32XEEEBVy'1i ]3ޞ|=t}!&xECˡ[Ή|{X*(ؓ-b  ss rEA/BKfG^^ &Nj$6]R-II 4%kimIl qHD' t:%j3ELE 0yn_  LI#$%YDV fB "n=|yY@/t@A K8,H`M#&Z`<1jՂ<d/ЪfVIlWZV$D c2SU lNng5ZC v 驡jpHCJ&Mޢ5pP/аh,FMPR% U#PEAQ23S&hFf2 X8S(!.'(k?A@'&?e:h!"rD"0K{d%qFb:fb* )<2#SlYLf :! ,-w$vd$mClSʚ5G*S/]@BDUD"͎s1S(_S1jڝ2u"@ˊQfd"L:I!&CUddU(pՖJm".{DbH:q|(6 d$L@U$"%8tdB2^t|9Ѐ#P)H,V`*^~u>WpD6I9gZ1<$8'>389'ðWD~ҦQ]. VI:D zTACf1 KPdLE8_)aLwH<@Ed[30T[Cf5z8/2KV Χ-cJ:mVt̑< Q%:3gRhJcUQv$ڝ25Ү=)u`}x#?wo.j?7v"3iinmCraNJn޾Cyi)-mGHKK Dץ۝faq{zY""== z^ cqɌ? fDd+eؕa/B0E239x!jw젩ɄXVtZ-C쬫%--޾~N'gN" r^TT^,/Щ|OX(M A׈dg!؇#bZ7؋b:C7}{b| nFLX>3ȡNgW7vrj]< zT`A~>{좣 Qh2d#"LMϰh3:x1(/_J ج;zRRfud^_/׆Űy]=ݳ,_^%8s$-maw>,`ӧNb2޵P8L~^.7o"w{'hkb6kNj *P8&VK"d4QWW(p9:K ˗^I>NٽbVVtZΞ9M[{OG[Gߗ\OpbZq=ySۂ `4dx<u;v}6:AѱqTQu-7n%{m";;9۷me:RiZnٌ5- ^OvV֚bAʊr,sjLUx{䕦C8384xmXYq|7/Zob%Nݜ7Լ8~? -]]lT`tGD233_ܳcAPPHOO`0`6j4ܻώxԄ^QR\DYi)Vk99.znp>%( V2zU}QWQ90m.&+.VΞ9`@eQD$t:]JatlO(';;!z=;ӇV.g_ "yY_⒝())fW]oܤ %~_@デwnm$33s^#oD"QZZ9{$&p8$u:fAH$|5t:ϞNC#xGK,g߾Tč[IZ%zp,W  r45re2d 16>/?n& q ʑCRYYPsgNQH4 h5Z dGѰ;O>0e{nF,eӦdY& a0ǏDHZ㈢rD~~.gNLF008קg_\%HPo/C##jwIvf&gN KKvvɦ{ÿBQa_VǗWØF44raii Z Hkoå翳]unpЎ;9UFE;47 @dF#l۶U~`zfgNq(/㋫)/+#ھN\~cG Ǐ_|̍[w~14E}{wH$8|6ݱ1?,'H$TAܼp$Bo_?HGE]N'9ۻJb8z2&&x2044>x<΁𮮲ʡ|y:( 8(H|@gw7gOvSSN>NEGz;~ Ih4\x .;``jzCKk;H$M=B$ Q8rGܰ_ eр?@䵞YѱqVj//3o[ ՚F\u_:[lf8NvTVTpA.7׮ MWwfnmۊ$ITUVX[@AD"A0B#I@{,< ZӨ۱ &(7o" 7h4N8R{|<\<4y=}`X,8|X4dT7aRvYrsYq8pݻv284zƞh$';^&3iUWUIHj\xǏݻvrJ|:Hwo3NN1*+ضu ZKΑnMh0Rڧ@@0(ϭoEIʻ  kχ?ߟwh4æf6o!򲲵eTWUR]U /cbbU kg]=lټiݻvw. xhjn@>?dTWqNUR\T,ˌd ._@,#q)d2%{?T^ L&SӜ>$j)).l6ÁH$oJuU% %g|;r$e;-GWާ,х]IΝ=M<Ka0$#Gpy<p~?=}defp!'&Iw^ӭ47QUU(jEqC//+yp9gfq\ q~?]lr3<:GZtt099EuuAYYzFCYYyy?{DB8LNNsA233I7`jzcG@>FFX\Z@~ef3Go|Zy67Ǐ^ワ՟H IDAT2FU/۷QQQNOo?eeeھ}+nߥ"ٻ;>1( 쬫e|b*uQhx3~S򡢢󑗓ǻ|tvud+# 4noM5qAQh/w[W1v^ B6pOYi+MM&dO9?۝ DZZR4ZQq=?vQL.tvu \N'Ǐt1<2ʉGims{ DIIi></ǎ5u~?`nކݾLyYy9 g.22"77۶l|wkWw&6UQyxf`,t dwH$lj/.?CIqxRpʊgTTF4߮DhDN}yAZh4\.Z$Nd2F$ !  1LzDQ$uV}""SӀCa q\8NA E 95}(l6!c}N7&MMp^#XP(̣l hZ ?k6CԬ(DBWMLPQ^&?/&#==}~9SSTUV8ܼu>x𨙶*RzlCwvuf44";%''>H$OؼLym ܺ}P(D"!q#ff0Mܽwɩ maEVVfJe%CALFc*t.WZ&GQ[_קu}uOHOI_cz=7nfffbށl"333un;ٺe36ozs-[6񨹅2r$Awp8R_~KK QeD$@1FFz:>.ٽk'LMPRRldefRVVʼmp8 PZ-( S33lټA`LMMzٲy-mx<^v؆n_f-1;;Ǯu  ػg7mB!*++NCEn=z\.=޵i'&).*L>#Q*+xחˡ(,(vl߆Ao`l|"E!(/w}yd`DEy9[HKKC5ONRZRBMMvDiIKo׉,N9ۍFLJ+t:VWWEaMdC,I~L&fϏbIM6 D4Z UҭV230͘-f(ZEN* FFX ?@<VهϾ`0H$P9UUb8Ν;?*+k5 fچ3[lD  bT&deE9|dslr%F۳ߏݠ9v0CC~>x"])5켫^Μ:I{G'@gOF4#//'%Nss+x~9ZmR9 c[Xp8̟W@3ܾ(.*@~4`;Fbl|x<^Yqx),pjhal ?{Htj5Z:-P  0=3Vi 6DAC4>(S8P9_@^nIFz:vֱkg:/?(ѕS۟|t%_|^og3QooWTX:PvnټܒG vwlLYEmeCWVHqqYhZIOOO=d33(**$##dPNNV5Bf zL~~^2!Qjb6!/74VKaa&^:>??|L&uRr?."I_|Օ|6aS3>?5z& eUg::Ȣ=Wp8k:q2ҭwta0c޶HȒDZZxlݦVP`=#2XF,Dc1sukЈhd-ܾӀF!A,#^ ٣ !O>BuU%|tŒ߷Vnĭ; ݓ%7 EEל aǩС\&//7gn=T6|'2t+h IJN4#vwi!O~,~h4[lfݩ:nEŐe%h4(ϭh4M A/3#^$/6=eGgnHQS?nCEtZ)**$p^#eȒ(”$piG//c5j飲⻒SyI[[ڻ0Ϟ&h(d4Ij?-e,팍O8~pʶl/^;ɓ'\xp$G|pb a_Yχ5Cbf3ǎaxxvlJsK{&#=B (..FI>ugfdf[@,%Kxڮmn')EE Z-TUVKQQijwp-?~2(ٳOT5[T@hW"q޳c\{xݏx:?A|1x8E(">:@Fփ@TT^%&[ur~FqZFDQAW?ޏu4AHܯi}@ff04Daa.iiX,dQ"AKk;*9ttu =ʍ[wX^YmBgW@ܜz|K[{.|m?!''ndO8;N::),,H9Ԋ0>1%ũ|HDwO/>&&h48Mgg7%Ŵ" K$ahd, ]=Ս[ ##c x,L&c*oKv{zS-m9** yVQQQy<~(--aSM5pw8r?H餎a2crrX,}y9BjjDO2_@>FG-,mTd7innetlI4Z-S3}|~|>?7 2s233ttvs=l u`4ܻOwO/],.jtuxHKe&[!Uw295͛0L{d99by/^'33D"G~@FFzriF:O?'==X<7nvsdfdHH_g7\c]Z HDRѢoHזtx^Qn8r SSmaיEד$22}yp8LVvVJ̩ 17oC@  d3oc83:6YY`iΦj?I=0 r If Rc;{vdSM5Nj" H'/7޾~E$h0019gϜBe,f3 8bgCE]q;u[ZkݯJF CON}籒$ܸyh4W7ox<ܺWo]^Y/}E$M(Bý|D"65P(:w`pQy Ok[y E[G'}q \>,-g_"_^{rg{w?}8p8zק}O?%_2;;?Sz@ @'|5m ƭ;paYzzQ9BOyXNz5Dx9ɓ!vl? P |5jjavvkZƵAHvVN@_tͿ bfv4KRvmۃhʆNE]sL~U<}]>6Tqn5Ut26>IIqX[rqisgN^WU^2Kv"-8]..?_o b49y8%DQdYIΟ=q 1==KA~rr{8a:|~?.{`=  t8{$]=|8Qn쩓wtʹik$ QQ^maaÚ=q4)dгgyeҒbxJ gN}jy>9>xwnP(L295,xW FVzw@r5#5FȊLUy^hs8TWU&u]^yXr],gzzOQӆ0a4Di7>d2ۏNeyeD hXUYAQa!(GWG^h6 N$a2ݑ}ydY`SXPztRphp8B0"fUQ94Ю咈'( KKvZZۙeSM5w5tTdYFeͱ#_`g>CJKJ8 v{J0bQ-,295H<'%FSs+X¾(c yS wK.2xR\唭DByYiJr]p=45`_^FE?dg]- +2Z$=yňbSUYN$deetMOo/3s3קdx"JZ ׿z;ٱ} yN=͟ʞ]hmx,HD<8۶na=$ `6Q jSu>tZDQl6c0!)e6oTIoS_oI}/کh`!Ndit=VPn4Яkxjd2jSlbh4L]Ʉ(h44kC]/){5:]1 sTOE`,rzh4\.7$  bqNvG:u{ &H$77<A 7'GQYY׻Fı#9*/لbaaq>lD~^Zܜl =V+\b1)..fMS[Yq8L6\xOh¶׻dܙ308ma^*~r>95R眦{'r292ܻZV*˖,vv\vmWj/y{:s7F9`iC㐗TMl{<|X̥ E*+;TWmi20ܼ}шˆP(l2[_W˫.rY][gkkDA/ V S\vhql2& Hx-֛B!2,o@!T*2P8LMM7n*d4rb1spश1I0ʫۧ>۷ }9zx7L&5OLa2SܽCcr9FF8L2e~ Fllnގ@  H&1LC!8U|>2,B+|S:e~^`,G;ߔt:M~GiTEb1}/M:uO6%iϐ,vQTߓeet:ZF (TuX,Ct%(k/C/nG!3>1l6DVTdY&  zfxj>PS]&v 03;G,`͑H-* ff F,.-a0}墮 Pq.TZ9嘛_ A4%FuU%@9*LNMbaqy$s sX=esk V޾M㌌<\nfZ pY76I$,,.a;sR?l6>B.'H0=3G 9 }L295lqb8V!<|Act|ejɤ젥p8@o"I\tB***O!ɉolrރ\&#J33;Ǔj}2?gOThJ ๏ lmol(R{٦|gdٗ6Ͽn.'P{bSS3L*EQ)HRt:-s8."(2$ gNbf =^768w zUvvvQKeEp, u,,.}R|>?o^yP y$JY]ߠQNL&ÁӅ?(uJ0P__2]G:dvwI)tlnmSS]M6͊xʔ}:J'ܽss 8||DWlqCR4?C!>x?#L12:(|'\qd2Y{V|x~a042ʵDLLNs-S3|r0t8{UW7o$q<^߸'^t>EBa>v>uȯ _aphOf󻏯tz&C_XGǿ/wD>|CA bx,N2p8\cTը'TZ-TTTH&q=d2"TThnj,o9Q"ʈc'BӢt)v*ʀ555DdYffE,j4ﳾ"?5_dY2B%;hlFEx^r Ea;\.QS]M"@$1? ҙ4mD"1)$R Vd2H,BV% )cgU ?3;W^Cie~~x<C~?-5d3Y$ _yJ+dD"IsS#\xgNdggU.](E1vw xt=ǎ˗361ɫ.5m6| c|~~ 'Ctwa}c֖1---J;4 Rχj1C>`jz+]q{u^9s qCD"ED,fyud2E4esk I&`zvWΜfxd3N38gIAwW'kqp`r<+kQne0 aZIg2S@F8PX'HZ-?wa}H$4 G%]h~AΟ{'L dQ ssZT&E&zHl2_l;oKj"FKh5҂dF7^'Lb멬H$1;[]RonX4j5l.VwI8` H:do"xʫdC)Osjzp$B.z_L&SvR]Uk/2e~l:9'mJL(:{q.?`ZxՋl` L(\A?ryeH%DCD%[HB J`mu dhjlG$ vpAE</W ZZ͇# K:'>Ratv JD$rd2Whjlsq$.jxc㬭o|nLVgfNdR)|ǁT_'`V?|D9]H_s$?,ll62>ux۷Q/O:yh|^G,#͡QZ@סJ:sn.XC f|PYL2?>R,CtC0jL&%ɀo/jI F7SR)D"y WlVM9MKu.n,m)!xY:Os}G|:tË\Ҋ.K+H4"(sȋyvnXJ|ﮬ17pٹy:K' yhBP(H%R( "(veb:ͭm"(Z=~?z݁`*F^X& kLF,s (2wvY[@ӑ羖rťe$VIR2|iBLJ&XFncf`0 ,/`0J| mBHApoׇ^8IE&&T**wiq=Dc1 ;;At:6;nӉfh񲵽]Щ Y[_l6tX[ߤ@2,hEVH,,,+5P*H$ m#O}x( B<lR˔)C"(Yrٹyjٷ:YQ;pP`6e=hwwboR= @Hlf`hV8\.w%4p@tD" ]Gx:b@0"hR*!L"JG,NFT@88`rjBT"epdƆzL&O"=WZ,^X,N ǁL&4|P( |8WF-܅^cD1 nh2VeqqHv)]XXpQ& T(&I#{2D,!L289E2$N39=@0bzvBK]]-ZBUU  mG:>1E Q€Jq4"::>Qih`cs5:K˫JBVY n3,XY$ nٹtZ-۬2v{qLMQTܺsшF.}~.cw2G:;0oK n'W3:6N0䓫Y][gzfn]n7ndl|-߼9y2e~dY6;e Z~A/(>y~b1VJbiyٹyvv}DZ;9|Wo$rC]GHA \n=Ǻ:yT NZD33;' 5 Oe>?z^/JGp8d>uݽ=$ {qtosO$¹|~GG6=ǐ::h8BHf<уD"aoƩS'd8]twAThin€秦J>?&F^NgQp~a'zxi|aX,ftZp{<( zzb;d:y}lvh@0ȉch0VT O@sS^JIWW'N'rSNW,;y『6 yI$tw!J :B>rX7"〓'#Hط8u8r MWg'*y?XXUPH$QVeVS[O~VXѣxޒh=Ǩ&O(BކK*H9v˅P(@L&S νBC}XJ+N=GsIR)=GPx@]-g@}]׋q@ bqi=Gq@R˿Xfߡ`0H*b63<<ʛo\ana˕.  E&af8]njpTUUu +k @@$`T:6HeR<"c2}Zt}]G:gvvVF~GV VgI|\'lnoSW[CߓWWV2:6RqupWzN'88pRUU8 K4137 mEWVihgrjYjkY^Yebrc2\=gJK_Dž|'! 9zv{- hSiW+yh4ʽI$X*"X~}uhx9f LNMxUjjcxd ^hKt~aϿ ZZ!?=8%QUixkTZ-Xf6FTh*l ĝ?Od4~'[;TZP(d3Y*+455NC(a0&R[SdVgdiinB,fbzfP(DwW'sA`qiGgG;knwRڞFT*d2rrD\"Wؚ'drrh TX-ph7XT*ECC=H$:s $ tv099E,SD"Qfbjp(Lwff t`~aOgg;d TN8 TWUph_`Nlg诎v76pܴq@SS#R0'4p`@%]+kx=^:qp@[k vZJhiirspDV!߷!Kh5lm娪dumd2IKss t299M,i"--A~?mm=.#[ۿVׇd2O$Jcq::X]]R677*oUe%PˍN+,&ru)9q8rmLN>hc]LLNt$Ib m*")RU*ϭ*GTZTZhW#;|D<`ET33;jWw"OD1&gdtwu22:X,FTrqff8}$O͇˿LF#9Gl'Otuu_ß/}LA(WMb1>t -Mx}>]T{`475QS]k7_' w$k? {{\ӹ77_"nzɩi<^gϜfqiE}zz}y RM0BX${8{4Ox7||:t7ngOJcollH* y)$2};OClݽ}Z rkN P4A$VNJd2HRX-BhJ"(`ښjb8^ښjR4NR]Uο*QhJ^zǃA),SQafSQa@TbijN E!F"`0x ш'`1 BDQD"#*+q:]dRz=nPX\J9ndB(&P]UE4(+Hr{&DH$b (HX,fxa* &+hzŹEMu\_,fc6lC>jq( 4¼QT ZmxښLpT:TWVꊫltB~xVV?Zoaq X\f;pG7ߣVikma~aRAMu5gϜ019EUe%뛛)?4X}_>|:oqWmmcw8.G_q-j>̓G|V\L 6:L&SS?}aeu[waXDB^|{.LN03;Ͻy~a?TCã456*DBTjbLL]lkk_oI wCry* eIb| =r9#㬮o r=D'WsC:l$SI/#LpyMiz*U*d2)׮*n) ԅJHJUFcB^} KptpLbq }b1^>T TFMuOQT( D"*y9/FKyRQ1b1{K>T;G*45٢0}Ohjl$H Ym-͇WTx<^42 cEc(U*T*"Q_'OkKe2eY zo}384@gg-M?|YSSb8|;z*vax"yn޺@eӟi9m:W˔)c+eeʙSGaz_EyT|]Wi},/^ߏb88`0岒rTiiboJ"(hVZC*R]UI0H̪ IDAT,ְQ+b;hjl8WxD46|۷r v"mab8ijjdgwh4F[k NP1wUb;|{ BRi hoo%OOEJm(r5żƆz/l9b1G&PSSE.[hԈ@ `csJ+g 6Ɔ$1;;456~/Lo˷)MuBa_2_@ (͗Kx*{*ܽOMuTZ,,P_JzOҤ)'9v{Yj5[,bx\P(ddtZz}r RRawR,hX-Vgf 76DS__K< H,PJt&f?@$d"HUJ%=fcs#MLQ[XYY]G"],uGTBE:0@ O2bcsX C(bp͂o -o>{ZKu9p:_ e~ddYn6;2h}KT*ل``{gϕW//\nĉFfm}X$T&Nw'0>9MבNvvwIҬG"ťek456pYPW[H$W;8þV PgNڂ@ l2e9s!76E"#JǎvH$xۇhwلNexdx,N<`wwP0DϱcDQ&SAȱ݄ajp8{6B0SӜ8󽜷2e ~6_\Z iϒdx/(XG0鿌\.CdYxq{@$i0'd2FFǙ#35=ÃGd2/,r}t ߽d2w= [wX}n?C!nݾ6~w13;O0OLzt}n]GO'ܹ{öa;Y&&y.b{}>W ;Xu^qhU^ Ns} *EGǨ:-x+~U* IӸ\n*r15T p6L&#*j5Lu@&ph7fD"A<N uHR POKs5d_! j LcTUVP(J2P|.FftlɩidRͭm78]@@@Mu5|2@Xb)O ikmATb4_;,f rl6[AޡP'n0+hkD"L*fNg9vL<'˱_eʼl~6[+kE"G9p:&98p242P뺹E{[ky C4{x^[67Y^]eqqJ+Jx<25AjP(lHEE 466qJE2B*rqZ2=ǎAmM8b135Us҅γk/15=.g|rv`na` x;M0196;%뾾~Ν}iiimf`h69p8Xf7l@(bhp`xxvhok+v{H8⾁`|GO8sK+llnqfܤz pKONiэb. bߺsvjkmͯf-t:{!r 6SӳlmmcvngdjNjl j5tuA(b2T d2b12\>OU3VˑvwQ]]E>ښdR)JUURa0 Jikmt&CcC=\{)+d477Q N H2*%m]C* H$,j@v 'OΎvZK=R)x<N`ZŴ`0 ڝMME}8] zD"1"N[d%Z[Q*|*R.Q崶 8Ccc=PŌlB$V)C[^06>oL*-456p9|^~_Bb82qb8'ӧX][_+4jϟckk7nL&dLGff8y\ǽ(ȅ\n* T` !H0?|uxMqC0f;H$$ׇX,wh|H&d`#{U4546ԣV& M\PAEB\n7z;wo}~($R)v;X%f`TD*A,j[yX+ԭ;4D*ewodj@"4 R)Ǐw_-XLtx?;oA,gxd֖fC!^t z=VvvhokҥܼúI/ Vn~e2eXR@XB?GT:]luX,Ve'r@A( Netwuq-6y+x}~-\je>R*9~r\-o+o=m~ߋDP*8]nDB!fT*d4Vs\vtZlB[J lby0M_)KJ#TTpHb** bnG"RS]EW㧯f(JZ[ZuO;2e~Lbz6g@ Iѣ^F#2)?Q~X,b}cmn/g~aD"ɱ]Lq 4b6s B&eeeD2AKS_ѕfhdͭmzU TUUfx?WNu.552866zc1M,.-#IlmocX}3gX][#`߷RxLNrVb8;{q's5ը 0 em3i8]N***U*9}=Zw2e~|F" 5M +e~orᧈ{&Xx<>eA$E,P9Z-P94j5!T*6nɄVabr  !J195H$> C -J,.-YX\l6:7 OOlmp#;ܾss"ֻ{ XaB4lBa{*h{1;7@7JERCaf?g_νb1WVs>PtH(tz_X,|gf~\N/Q mTN U$)::I&TU[ FtwuԎp$czv`0DwW'f\񞣘M&4G:;Xb1N?X_G4OCUU%C#46v{ל2^h,{/bske*+ g稬G0rvvvh42;7_(-M/,J/PzGWpY7 &dk66P] ]WIW )ظض0P0UCIn6{u2_YSy-皙_`ieKfüIuU%yytvus'(+-_+p*w@NNk@T^=AVrٴXfkk!xi>\z2mԔ*+r J x<SZRL,h4PXtJKIMI!PW[ɔ墲MqQ!EEq UX,[4Cjj*h$Fc;-sp"J%z$TB~^.RTXMR\\>STXFQa ZEEy9MTWW&ux=F#^Tds]T*%p`s2M^n-17Q+UdL݉HzzE"pB k|#Dh4filǦ{G{6-ZɔATB`jj ]/tt=Bγג*dw]l=md2)r2:w``^cӲgIeb1FDGܾyp$"z+BR@ HLYĢt_/z6K?DR:HO18<"z8<<{wx8NrsQ)_zߕlZ-/P]U)a|oL&2R)uTWU"ijTJcC(%[[SsɄN=veeTV#hl\؞KcC=RlI̺D"jr9[Α.&]\tׯ`|9r9y\8RNO_WXX@]m lIt]čW&hr9:&r9EETWdH$o0##O?ёfÌD"|YhY_ߠO>x\9Nb(J&Z\&#~>~BUUלE  0==KyyB_8|IKKE!Wd"?/ARSR \~?!%E+tv{x2‘0oilON̲\.'qrX,ƥ֋9jH$N$R h>RQ*TJd2F( .^`n~8].ӥ;'~"H4h_Hh DPT^@(bpxD<7@kYY)=b J-X BVdf# 1z!=#Lb2e%/JX[_Dj!JJ%'D) u7%=I$C!Hr; ;; {@M,àv2l'RKu0[[QӁ!ѠO( m&IWz-)?9 |LI!+'wE~~kղd\Nj~Wr#pRv#HHMM%xHMIE\xˍ\.Cv{H%h5r^'*RSDhqȤ2RRB"G @Zjd 0]O(Fxz.p$A PLΒJuU%>N]p: YMZZj0 ssbI$o+ I IZj*nյ 4 O;nw02:p2̛lZp݌E4ɳv$ Lw_`}$5  cw0,..?HVܴXαxL&E"23;`g82ҍXm{{nݼ/$I~P|e?g{g]ՊѓOC:Garjښjܿ^>.f|Op8棏weQM<=;p=i|\@`0;!8efc;IJ;&55mOV9<fjf]H4=Y[_nwkkeggF$PCBMuׯ".lTWUbcemښj)Z]BiI1,.`ڈFc\ԊɔEqQ!7_Cբӥ115-T y.ȟ'k'(s_Z.0I]?hlh`iiR\LэődbƙZ D"X|A*+ijl  !|@\ /..-3840q}8sOǙoh4]_$\+n>I![vxU8+kb޴/ẙk 7-t~]+]/BZ,ϻ{Q ]񬣋 3~.!;8!^.ǯg͛Ɀh4J^6_y~m}VB\ϲB{sBf3::|=GttuVbrvnI!O;ju͛/,Zw!`Yq\s|lG& =JK3FNj)'rR+YwqMuGGdL!~"22ҿ%M|~.?&5{{q.'yyZSN7-[ AwOfIfD;IKMɳv<^/>l򬳋=aüI^n=at)Á什9.~_s R%55RyBzz:LM͈@mF@P[SE_Utt>'+3 %%lmQx α,[[efv+W.308[[}&&Y\\flfooB{hgdl DYi ]ˋSV;; ryE,#qxt8Oʏ SXp 3Yx}knbzvEZ/^`l|MMdL(݈+֤RVVJIq{{{PUYEPVZ2:]UsMFV+uXm6 (/=qs| Y,:M\.7**67-\܊lpjۉDJ6%̌ 8pZ"0TYhOPR\:W.S2ebq@]M5皛(++?sI-:RF]mM‹+ AG?s45޻'n{{؛%?=3wA@^^.Gqdxd7o%%FLq]]rFtTa``p7ڇ$. @Yi ,- IyM^V.򥋔1ɽ;/~.^Lhoc֋/%񸐤ysZMqq}wvP ը[{zJ$( 1g wvq [[;<}Iss#[]/O!; vRb?b1(=}O?'wy@0ȵ,@N3oߺ!nHOwn%'ɸrUxz)r>orNδu\ڥ3qIq%w|A~'Hgkǟ>~-XNgUYG{zneeⶳO^uaݺhjqMm&I6=?!q8d&~:-IJaa]tԱή͆T*A.1; >jQ(b1z=/=~& KOb2Hj;Z Jh,Ƈ?z]Z@ii "nW3j)IKMerjEnnήnJupiyE6zD -tHOKR22b1ֳEQ^iJJ j VL(ѥqťx,ͶjCDIJt#2LzD' \N$XP0J*r O$!##X,&;V*U!d2rp8,N<9$ogOY,DU*(pZE49H4*&/7Oj.;;Ө ; ׯKyY)M|>-|MiI ׯ^aeu d2[[|쳇 \nedtuwյu|>TmbӲEyY)>CPr .7V'I歩}r MW-XO_/i;N))Z FZL&'~VBrյ5N+DQFFlj|~RRR0m Żw:Ux^,[۴k&''0`j( KQa!e%w]rsP*M9=yցRn8qgfi$IV SͱNwO/vE|D0Y{'V[7.wd=}_o<gye9#BPɳN'07lZ,LLNݵ2<2[\Zf~a]+C#b1"$\gXx X{! ?(fxN%+b k&l_^YeӼ׳l7 COo?@Qxމ|ۍpشlq||L˹fҍF`~>x7o-233شl?`n~ã<&V ;46 WLƕ˭ܾyx< uX6$LO'55mQ*RR7q8h5%`0ȮJZZ*uܺq`( +3. @ky*+8<b''HO';Dc0Mnomslw`¶Go ~$;;b,FF1 Ϳ '~9!gk 55U/03;OCC-S3lli#++Oiczeg]47P?8D<痿-jp8fczf#J%O|{@JIjj*C# zjk612:K̠ ?t jk(-)arj]ZUo<45-)** ?kEgdP_GQQ%%%X,[deeQ[S1J2gh H8<=}*7'sM,-pr+lP( É? ?/V/zi9*;]c|b Ig{&RW\r9QtzQ*H'ix>9OGW7o^cbQS]fc`p.a|J \wn{Rca0ϩJ{::IKM*s8V߻-';;*CĿκ^zn\*nknj_zE|\YQ.zJKJמuk |VTSqnʙϮ>~³Ngfp?0HI(֋()*wx ޾~_ ?f{g'OibumW3+M& z+kkdRG@*RR\tX,α݁@.d1qkf6-[tP(^ϭephDJRՔ=a*ѧSȇ?z^GaaFJJ@!gf(-JZ6_Kt:Kˬmz)))" Sj+Eys 3GP9LnP\p JTp4ed崲^GѠKKC.X[?ץ 7DĒj3s 33;'dJ$&L:rT*UsH$"!y #%ŌMϩUjJp=( JKvQ(deeʹJm98ܾ}MFF(+-H$F;x2.Fh4`0q{ ={ZFAVa;B.-Ep8B8^T^#RBTq8JZ-'N&$3#h9:jjft 0k-   zNӥ:Y'# C!zX(.*D.4Z-xJE DQD%K8? PHX\/45P`0HvH$"Vux*%ׇJB"0a@jw8t(YYX+uiyE&z}F`χ <  PNmo>o Oa$!c4X^YdR\.k As/ZDQQ ې׳$=I$0axxxD  ?ˮՆöOKK3345405=M4#?/O\35=CsS#633w._Ts 67\S-vn@zymr:6R_#ӊ2DIx<܂|rsuF+~ތF#e_7Grl7)o"~ }g$WDQ>3޿VK ڮ| ob$I$_7~ol_@4T*ŲMo`h4ն?GJ%B{lZD"hnwt P(x*vh4!c8NGH$b1$-ݡR( /OXZ^p4moK/PZRն˜o9FS>D IDATQ^Vޣh4H¹y<X\Z! ʘ6Y=Ie0=;G,gjebrJޅI ߷ :sb1̛&f<~AV։,,.kV+ KqOnͬoB,Xc;3Z<ѱ dZ,[Z;{ⱿX,kεťe7z]'b$ 13;GqLLNac86>I`/G<gfv[wp\Xzx>n7`Qv;M zN'"Jx23qh`hdp8w(-.~}|M NZ232H\mLVf&@!//}Z[؀ek DBqQׯ"p{?%7'&WGP(ɛ xӋ? {}++8NEy#.x!Fڤ+knfCבn4?HuU%?zD*!?Fv BA 5|1yZmQXPba||b'֗ZmK$Y,m78NQ;7^gfvFEO/yH<Gǘ8::DƐe8N&fk'H$,,,Q00=3{Wt|\V֘)/+#+3':(Oi 'TWW j=:Ad2_5p`;pLN|k@I}aRSSh4226FKs99`Y][t!%EKYI c5j++Y[ߠLtr'c456$}L͡ tZH$1>>\.h$>ηR0tj24V ӉZfn~˝Ԕr᫭%vvwkJO4W<1n޸NgW7iiBonѩ{wmE%HT*UC֍/| dܸ~߅szMqۅ-7^jk)J1 rE?'}:xs؜O~h: z$o+oVe~q KKdSYQ\!ӯph4j )%yZmlnZH7IMMh4jD#ܻ:]UU zwߒT*cye<oBZ//ulZٵx< RXX@  **ʹqX6vvqqEy9 sMV YZ kkxLbdFFi'}RrEF#EVi ;sFAt񷶾<3ՂEղx( 232y:P_WK4evn^W*@qqS3sz( p=!Q,_TX^>Qk!&VbфIMM!P*ܴ%n4T((/ qyjz9bk%I$j,p8>&~r rs8<jefffK0h0 p8%! 9:>F&m񐚒'x"xrE_v ǃ!/7G<<:ta2e 5nµyl G0| ̛JKP(na255ǃ.-o)dAR6N&/aӥ Drz\. 1" a~aIlv#988 5-ZE};WZj*VTNI !gIj`$ICw`2e}''&" j%ImtW98]=2Z7Erlmocl']Y^Yp%Lw`>!~LQq%8N>?iix<^2ÿAff2E>렺ϓ%  H'v$Imz޾;&֑H$W‚|Wk zj4<T}޻.ϻb\"xv_j{]0SwPTX395 ߥH$C_Ƹp3}:z\r|>?i]+ ^[cPOaA>vtut uPw'I%aEQT/,DB$fphMsMLÉ.-.oM ?x>[xMMXm6qǼɮFN J$I$)``0H,TB '?A4 X`0H(" b(Jq:]x<j5EEopII~HL2 [;b8@p$նںB4 \nC֠V #rr9>?(]Z^!S<^/eqi׋ttuM(K/ r؎kԟ2fxSRRFvL&Hٹ*P( @$kI /?0H˹fq[QQ/RPP˭Y0sppV8|9w]Lzz:Mv̛ZmN ~ %^/>%\.wrI~8NykXm{lmmX j8 2=3xyC8G77v7&";^k>zqVֱ Y][OYYZ^g  ì3=3+^Z^k&k}ťegf_/G<gm}R^VƇ&~@ ' >{I}]-:Eii)9(r~'kC0>1NGeEW*`VVpLMr$hJ%'/7]+/ 32ZT*%o v\JH7y]1efRQ^,VD"N4j57];lwm\n7m[ ap*J:jkmG"45֣VrBJ'QUܼqRT&"2)p8 v q6jSSc=.]@II1ҒB0H 7W F#|iIb1GѥN"|>?pX(VV|-,\&# 򴽓h4B!ftp:q\D"̛q}.f3;d#<ߏB!^"p̌ B0/z>'E`ph4h02;7<8<¶Ͷٹy=7x}>2&DZZ*`rssp{<tiHt:G"|wOn ߿ M w3;3 ]Hqqqz@"N:vw6lU,a9{d^=3=;Cv3?oXj`A;ᰳ;\.gfOfkn]s!>89kM&~?d2bdldqiѯ-0/ l2فbmmTWWҌferzM{[LFwQikmefvZZikmh4ESc^SZ꣧/u;`e5,475rUU$ΰ`}lq|bNgG{!:o܄KUY)#c;zť%F&8|TrvﰷF& t:^,X,7C;r0M CabyWy@Ub1S[S2[UYDZcG=} zւ묬r1gf))RW[Hj;~b s~٧=m:scGx>`XY'?@!O~]oj,<Ύ/md7P_X'?Ɔ—"Q߁vhdHK/<_Fx0LQl2ۃ\vWVa2u5@(bccLӉWtwvbXhmi`0PUU?t1]\d^"_Xob}}mxa*I\O]]-m-6ClnoO$hmi&NKsaVTU$I.#P0dZ/+t+)vilh磤:VWWP``mLLNeL5q6n~jɩm%Q,&^}CaL&S!&,zP9'ZL&vatzkb|u5\W`|bEQ*OhM$Dow*A𕔰m4Oł\+jR-tvp:HD&_I :nGEDQb6(+-j"x=.755Uvjkk`0ԈdB$C!\N'v N_FmM vijjx"76 I>_ 5@ww'Uņl+na4tRi_(BVT*lmoI.GV\.'ftyف"+߻"(-MTUV`Z)-a0uz'!NՊDimiVueern*+d2v# |nDQl2?ԛ q9mVt:=(j P,+|^f=455H$hjlfgWBUe%فlFڙc6Ifyc!`Fy8Ak4ߤ^8橪ʩ?}ߜo[`(!aDQdiy^yOCe>?sOA||1hIf/yyPsTE像>!HP@!J?ɞR4+k ܺ(/X a4q\_R/w l$.^BAh4ƙ > D .]$ g]`zfYͻ,-P^7< u|猍O`6/M2N}0u|inS[[;l6cZH&;b|bښ>cGhva0HR9~Q]UŻbbr:Ξ(6Kv>3B0&w?&zs/219EEE9~=gzzj;|OxWt8Tqu^0=]|LLNQYY{;FmM >1jkjϹp ^K04<>^\v2\Ce^x^|ĭw\GMu5cGv?SS]f'G=ZPh&_822J΂i^{e"]O6zh4JMu5\%NvI&LF4ٹ9jkj|mO߼,LMM`cs d9T7x[rgpL0q8TǏ177O^imiasgAѝa^/5U,/.񲱱IWgMy橓ONqUnm-->{mDId2t:H$(BSS#ۡ>CYi)--ͅpt:MCC=hEUR4WWRqT|66x핗47526>A.ZQ2\ł&/h4R]UEgG;`tl^}b8o.\dqiYY^^qc4p84$QUU q!VG[k JY qzNٯsۃ8vj38 ,靅bmne5@,uު‘K+(Rx--Y _ ˑNY^Y.񽴴®x<硪*DD"V=M 2pnd#^X&$rWRVVJow7ӳs$ $I]NN'] IDAT!7_ "h"b^ |I8v0Qj\ ˑH&12xH[k O?uۃȲ̱w zL6K2DEWg3H urzR4LN +[[Y$g2bȲL6壏?;ϩP^lPv:&IcOYY)K]\Q 9& Y.h0p{pQeҩۡZ,I‘ssUcb1VEEu&&fbZZ,<ҙ |W \d!|xtll6['Z,L LX]]#X R r">Wݠw{Ml6gΞg;B'IwV͇#a]DWgw YI\zEUD Sӻv4 -t:pI6'-j\>e,rF?= .H$\|~KY)l3:6N8q۵Z7N>j`Z;! 5o "Kqz8}<\:w#pWW<>>AYY\nSL&#P8tH$(c_[>kk?v@6EuuTĢ1<רwO$طYVVVX-p6vSQ'P_dċAoϵ}S腼zNKWg;&Ɔz`^^AuU%ܹ3㦳Yje{ طwdK#PUQN;^gxdV..2 X_ ygط\.'"$zDctuu( --\rV:[x M$)&nݾC6'1[twvpwqEVhimkdҙb[ɩi:qAcȪB[K3cM&׵@t*MwW'k -Mt:>cDQna2穓#8vn|A(-ds9W8r |I~$qG#eTUV<\rwq_ 47 PO$essɩ}$K+4q9N82 j3k o5X,D&;w9rPq.VVV8r ãcXfF$"܌X,fee ܺ``a.[TWWjdx핗8tuvw8xybMNMJ9p{`2p2o _};[p'b4il3ݍj-^[H{)^_{?Ç077Op}_|PYUIuU%NO}[o/~k2"(\Ru@ XS'gC\f yx*?4[[D"ã߰y܅K+=;;G:} |P_;,mѱqyS_ߐ<ӅlCJX QW[j` ]_d{x-;;9~B8all]|pA.^,~7~um[h_\pE}. 1:6 Ȳ̧fss\zP(\ };~?}T̯)/r: [IDJJ{:fffۍ`lbÎlL}]-l'LhlT*(fY-!O?4D^Gߏ)wr|^F#--r9*rr9Y 9zۻ].'?4h,FO"QX1Jw0"W*܅_S>YF**"pvmS2THr4FYyZ"->(1JR,0Uv|Yǹ5H2C!].d^H0VXMݟU5=S$0}{`6V8^cygQ (0Rh %eD UL:\jnH$H%8v@J'@]@/^j>oFv}Z8AAhtTUVRUY^}HDEr?>c'W1I**gdI#dp ‡IhNe3Ra -CRV|BxD%-ָWk(5 lg .3~\_MlfYL@BP*o̺BXD%yBY]i ` v2*JD%X6sJx}1=+q'aK%by >s~Z<6W|QUWd:AU#>Ύ:OI8wKjaFZ"'2]((H2^$$:\" q[Fh8OL @8rnMƢR]_` ~!XVš/!w ~S"jCSBQ( $B =qq~LBq83|}BORBe)b~PBE`3Ps0rzU&+Tb,Pf+HbFCCfj2'"  2/4>P1R gw(AE -4}&de^eTdC2/d7 9Л >Si*Pj.us*ί*1F|}?R ga1\] "1BOZ#ʨU*," 4Ӆ^waЩ8,yZj4'Y.'\tO@E'pThmF< @Eu54FhO"+׮?qM4e: A~_~\D"vQWChhelV+o1 lmo?<F7 2Wisg:ft◿rbYfIl6~wHTT?k!k \\>?َ( lln q:/W~*chx .33;G6O>ehd2w?5 YJŲN}1/^bee}y>S_8>'&q:]\stwO c\N|EYn#Xr߫|h$6.Wa^8|zޞn#nt:71 xfYXf%\'095E:OO37@6c}cs/2p5U>{֖fTUeeuH4F8֊X] `br>==u vTU,^1Eci4'($ LBtŮD"f|v ? ,( Žgu /F9s<>+ >4(`0r7ngpT;6FȲ㓓,]BIYn߼K<>>t~y8t,dd2CI*fffw* *h UUq8I׋9R4$݅f o{zU+8sk HO??j$|+W߼$Ic"+Iҩ 0;;q.\L4'LX vf5/g??[߼VR)WV96:Mgo%\gxd6;5FQ_[CgG{ѱ1VW,rE'&_$ uTUVrA&&e\@Y^^ayy\.>_w >IEadte %^/:%= QlHm667Qd2Y 452::NI`p}W~z }{0:6}{QɞA_~Cevw" D%PS]EuU%##Ñ]Av\-3[t:C}m-FqN.\N',{t)fERI*ijinH$U^RF|E%I֝;fd2I,qc4c464PQQ]l6LAzښ? XVPvP_GtI'aPQal| \.!\n'wzH4FOw'Vٹ9N?FmM5VAzb0:6N nSYgrzGSRexdcG04}ւ_mT*-h4ow~'{g38hKWb1ygYDA_`~aU~N'3OS| Zw~ulnndXȅKI0 456?p N ?@UyW&Λ?Wb'}^xG$G0ȲL*](SQ ץhܹocGs]e^rO??<ۡ~TumhjN}Q?+W[X  IDATssbjɩi~y׹wl& zfvp:3O FhofFhybzP8L$AUU.^BEJOcC=Db}Zr:/|)Sr9*+0 [9qCۡ9hw%$*I>"WG_MnWaw|GDQ=]wΎvΞWP{{{X][c}}Qb}}/3γ=$b G"FN`"}{u{BgGϞG$B0Kx{ә ã먩A ϭii4졭*YV?#^_۬olR]]E{[+ ւAY;Cւe,KXnsSB^xh#PRd2r9)h,F YlmoSSUEWg55FVVV$n&NUe%KKTWn{t:55;9^N?Yh!$+x<76p::KW*S$m 9yMMm6dEP_fctleN'%^/c8v0GTWU"I.[Y )-QUUbaxtGSC=r9<SH( UUX,,V %%%@`-2NCh4?L$ N':)LIVWUUx"A6tj4?-P<kdnB F<Ge"(( l6G(TJRcqV ld2I0$ Y__GQVWW vz3b1oBwnh4Fjfs9V fQI&S.xL:M*&pQUl¤lш$Ilnm(*P @  r.0+d2bPh4F4 ha(rY8vX<ƖEQd2N%H$Rtv0t:bwر8vv;t8\&ւkeثmQHg2wh4kNO,Gc0PL&#bِ$8YaPUpl6^_VF>GSZC$ f1t:,76widY&NS'[vd*,+RiZ[p\|_sF!֖o~?h48dYh4gyy wz_dW3`0\ȼ3xg8}<ګ/3>>v(+/~.:{3gϳp8d2061O~CS'3p6(S'X_`fv|-N}1Lg>Yu@(' v;|t&nID2,ǣcML|gϱ = +;zȲL<fcjf@Q_WbfmdY7_ƍ~TU%?r ǏA  NIF_Fy{"^".;d:ذfK% 2,^P(L*`0026^H3LF$]J kkA{)Ξ5CdYX ffgDsgp[Ӎ>966~N'etlxy[ҋ2\``oo.].4o37>MowP ͅ H̹s^#ͱgO?_юl"Idh(/>?D7H&@a;lnnQUY uDQ>t@G{33_%h4O'& ՃSUUA&[Չ9:ۉbwO.\DGSq7BU =U+yFœֶ^zrbX1PDdq܅KTc)zzŰc(|)ڍ>vCי}XvܿO??ÑÇPDMMCy͜9{PݝDQB0$qQWVw9,0 aԉD^O<(.؊Fc=zξ=iW^|. "Daf+>F6ca.sLFZZ _{8ywf{>Fy\s-3&#cDG{K/vHg2x_ :fcGs]eD,Ahinnxv/m r1f3o\맦Nj?@YY)\.'UUf 馱ko9~SLO 夥TWWa20PT:s x<.-Ph@7Fh4*ZFy\@Fh4wh4Fy<1 N{w+B,ȟ6_UϩG] FŸx*2z{S^^={^~ S1JI/d2 ͼ]X\bum}V;2lmms%*IJ Gr7r<+lY!??IHh40:6N:wft|W B.SiZZ{GOQW[µp˗.?V`2(+)onw똚I66(/+Y嬬q~ :0o^ ܔ?phljYmv~;d2/{T*Ń42<:c̙LLLL]m gN?Spfv}őK,-dKAƢ9:dS'OD׹':?h:f橪x&pH8h$n\89d$ .tw?0 ;IcC}M.]. WޡH$/{=9^ P(D1y %t$DQ[]EEA>Sk㤨("""(H0:>fnw AœyӧN; t88S[[C0F LLMsǑJtMMe2~?d_}([[ foXiAlu6~w96hTJ<#d}<ꧠ ZZFѐJ>)m(H(//chxF-NFCUU ef\g'dJ!OP=h_8籶V$`\kͭ:.z`:& bd@&&::Nfz=/߼RDB,# ~J4>L@S(J:t/tKDDDDۑ)-)XV RIQQ! Z5UrJ%#㔔ch4TVVѨecm?0{W/c^Xd}}AUNZ[Y[ߤB\.D"{U E@ɗ>~H$llR$H H$ R)2*n djI"J|f$L"ɞ~7<.y^Ï QPDDusO}fxK??]~Ʊ""?$s]8j/{툈|7dd`DDDDDDDDD2 L{<{z:&>G8u1 Ĺ%"""YZo219=C*|C#H$y &t;.J[k D"g{qi-~}9"_Nj f_pQ,d gfWy9l6;( ڏ1:6N+<W^f޼H4|v.tW^~)m݁B@.o*}Cv2;7O*Ƶ|_}=Mz]P<͗~:faqz5GFW^fcs _BYY闞 s|W[aVVĹ%"""qGFDZ\|}{*)).F" 7[02st:P~1-Mt疈'睷ߠcmF/]J0pܸB:'1J wj:׉RDҜ8ɛosq"p!I t~gdk{'LՌl<BG}hyi=}D"ry\ry63?sv,NQra "TƆ$ X Ty jV0JQW[ÕK$H,}jn>[F_J d ;@Hnn3sGuRA:t)|/1|;~2`[k3ݾ%j1 Oks|v.Cã\>O ⣏?em}L)߄hG*)--A!obqiE66\N`0 RZZD"Ml'+kܾ{?uܹ{NǛoʵ+1 HRR):ѱ l'^bo[w).)FQcYY@ueo`1 !J7cs8j.]9rRIAA>ܣ,7o\bJKKxûV*]Ν뤮OotGzebZ[^HNhD'ݵR[[s404칕od2v#H1BN驹+-otL8(l%إ@Q!#g= |\kFyDQT*sѿD_cs>9uEDD|`쨬Uii)T O0]|29V JE)JC}jP0GH$JKJp\D#l.*qJKJpAeeE{9:W߅"?xȋ'g XVp8C!B( 4 :D2Sͣp8hB< rO);Z ֨I&$IyZ x<@(BI'L&CA$ JA"VH$;:8h8DqJ$H$R)yyy{888@rL&=9!PȤ2"6'?dz < }&P^C.ѐL&BP`2ez=zNVE*?K$HF- ZSܶ7m*'<ڞt195墮Ȭ` (-)QB0:AA~>f)ceu0dj> hZ,+펜ZzÔ?r1,d2),.-#JYAf j13;Fex<^>3No϶Q~.U395MqQ}C,,,R\\D扶_<_hmiffv>eOLJ&bccE@Fj0<<.U9?<22~?Ͽlv;>BzzqQUY{ڢ!ϝ]B0c,,.e Ke{5j 019,%iXX\bdt^^ýAV唺|i7>bdty*+Q*Z̲Į&pJ tM:& eCxQh4\p89?[VW9~Ǐg_'|![:yF&pWvBΞ{?"?,A`nL2" B[k ;NlYxbh,a e% 199{߃{C t9:H4?q4ױzoNBԥ q98V0 llnJFfoMqq1ۅ%JJJXX\tό_*UtvA"yRbYYK.D@J F _,[Vܢ4/&)^鎓0:>kW8w`4 jIB&;νهV U?Dy:;<Á鎓Lp9|_qTpF௃\.G.;$RX \;oa  07gf{gw~{޾$BWD~8p\G}~Vd(@<kFUevb.twTD"ARQ*U*<^ovL7XX\ǟ(LF$S@cCoH&"? 0>1E4a|b AiAT*9AQi!3wrRp8;{pZmXVVα?q~N2 t:M87Xh4x IDATr%l6#R)T*D"=.NLQa[; `sSQQNff h"K\,!+AJ>EH"""?>r<H$ lYsc49q,~wh?v ,UJK8u2\RRL<G."ƍkW9uM=ڼ<.^8(K+T*H$磰J ]OP_$PZtvlolv475EmM /^@"B[k RAVWhnndm}bvqyI4G}4x1 \zJL,,.R)Y][G"D6S޼r͛dkmetlWvRg:NQQQ璘t:۽O׹NF#F.]Q_ҒbF'! "I& QUYAsS#w9EIq1#()))O$xÏhjlf13;GZH?86&FᑌҲT;`tlFeEyV `brGiI1>I\{{񙚞 ?vVd4jh4/*LMuS3i~h1 DDD@r=Ί| i K)/+fo?2\ch65՜:'/t!03;O[k PQ*ܽ_Yb^HN-!3T*B0S33\rJy H$ϝ88.p:] ǍkWP(͙:?믢V),,daarA``hd2ߋTNeskqJF679y:kkT*YkEVS^V.ZH i.-MTGBaA>C#c).*w'OdDFcə+WFkW3319M2Dӑo2<)Lhl`0~<^/EE8~90Xݜ=s93X:&H&)-.0 a^\$ c4jgq8:R)Z@ H<GVp¡ ?F:F?%x]ffJ0hj*Uȏ]/5o`0Ȝٌ۽^6OFסX[[Vx}k_;:α'0;7O0d2|ۿ # F)).fbr o~+Z1 [rGX'P^cqirzT Nǭ;2x9y}]gD"*:N`:V-i}5/,r% 295N;՟KTW̹gml0<2]~ FX,`liF&PAP)H$& :N>[w8΂NE.AYi ?-mz*+ayyU*4456lp1vVwv366pDh4Ξ9C8E"rB7 34!Gu }(r^}կ熨(ߨ΋z}mJ_K|^PyN;O ci=0AfTWWa^\B?:EmlRR5s\x}:(׭,.-gwA෿2HxK8)++`*+Q\ȷcjeqqJeKeE9SΓobsk=~vL@;&oOo6ri h4jvm dcc|ٹyLie0 zԘmxJ%t0845~:N(E(rke S3"|vw D^^WTT3>1ҲJ3'c:"""W>Ytsx`zf5V͙)7qb7r]Qa!ܺs2ۨdqyx'p:6fʹ4n&RcX<?uA`l|200<…455I:╗oQkXqvVgf5lvܸ~NcjٵZ>%%TWUy 3~ sڻ>d*Zfk{nER!Wڢ#iǬo X][gcsMlo0:>AkK;N'Y2n߹q0KK8,H$;^vwlmmgmF#ZMF`l|+.`6/037ω,YV7h}7_%dbpxS'OPUUp8h2gq:]|g2GNngssW~X][g{gsglmos93kkT*q892 @*BC&1>1ѐI,!:.e)>Ig8  ȬR) tgkC.,.vcphnlPVZʱV{e%N"PQ^{CAA>&޾~tGN k~t z~!b17峹E(bjj: ?0Z`/28< 6;ݾK(VG* ףQ9Ru}:Z[MMFI%x<^^q>׼<._AO/c1 )/+0D0HRS]Fagw ~AOkKƉ8<<$pYN8ya@0L@m^zE1MܽP'm%"""my.VGدJ:FP_&;;( .twf#R)Q=%Ip8i8uy3C#aZ[:IQa!ee#qGƈD( ._@}]-Vp8ZFVѨ) ~_?_`^\"V8vJFVv_oX[_GVsy6 G,[VHӨU;_kGE.c0虘B+7)).F{84ya`(2X[SCC}ze %%iQ*( ?pq{mΜ>?@p&^B\y7sZmUr9\t֖fL&ceQ"(2/T*Q(hZN>rp8 <['LV8~?):dP(X_ߤ?)).FRQYQ( gږQT47153K[kKe{T:FtT T.QPDDGG L4pd2IsS#?]g;Inݾ/QPP1=R1>9E?z7DٛRuuU4OǛu.$IL&#grd+\{{|m=Lcm=ɤcC0#c |_ LΝ0 rIM&# ssP_$Tdz^-vPdlpʶQZR©S'D|KuU% KKDQڏ1;;N畗_zۤ*.r kWMaa7=ىLb}P*yv|>hپ)6=qQ\'SW[K8A"*%%ż֛x=kH&T(xsy)-}r3ZX\daa!d2)'gc~wڢH$BJZtZ-*'ji\ _.yz^q""""ߎPLRC{{)J瓧C"PPXh{HKH¢BR)-ܾ{Z#W^IMOTFāߏ墨(sh>'S N9y8dΞ9eeFFH&tp dbiN˵Ǟ{h4 ݨՙ-2Év ~7o?8LEy u Q:wյulv;W/_W48NvgNc;3/P\T̙tȎ |sf 򩮪n޸ήFQaK<O?AְL&cjs盀/t#ޣ~a|r Tʹ3첲Υ ,r"#)d2Io׮^fskRI]mMvEbr JMǩ9ʌ]ؤ`0D<g}}<Ξ=oiقB~=~~!ãcu93^vkW.[b l)8ׇdjz}陌nŲ F ]4; ֝{R_WKeefsIwK$S_W[hZ'VGt_=J2<2ήh4G\Mee9[[~n޸L*bYqV`rjťewfsUryNŲ@aAgϜatl<{Lo {{9]bm}G(F&ǟ"/O ѰPUYɜy!vsk&U BX,+ LV‘([;Oqaacpx::Nrx2ڂkkYј3ONrs,.-ldorq*jaZj%#nݾ6O  3od2?'΁ko]~1D)76ٵ2P_ܼN8Ĥ(""' (D9$353K,|Y‘0ݾ˱dMbq z=*aLjsc^X^ahd޾|>?5ըTMYre=PVZJC}޺Sy8egJii)Eܽ>8 | G0P*XAG(,x"zt W.]`h(ҍkolrChN_ z=X,[l\&3u ;L&q\tbsՐߓ'(.)F@n35=B  T))//cq)s~K 475lRW[CeEyNJG}!tZ{zSDDOLӤSi׿^a}c\NN,+8]6D Oblr:t0;gfpxVBBI2NJ94j5j"‘n{cbY7oxAhkmadt<(ɐdX676OWaqɂL&nGlln!HHn<666ͯAGQը5j&&PTt9T"EVguF##˩A:|T*yG[g=Z[{rNG}};lo@Ѥ7y4r*E Ra::NoB*Ι%L"˹pѠR8uDfRbYiDB*² @kK3C-OZbkkȍW3ʊ f(++a`pTT"AVsZZmLLM3g^G&T*8qT twuEDD~tdbXy2""._9i&I&S:ڏ}jSUY>-݆:wr8d_)"27 (//;7A:"r9@"P+i9KuUi!-]M?Ȭ=.0-gP<ڼo""""?fDd2JKQRRWBVS~$rq^<'^x `$%ؼEDDDDDDDD{rd2ff?>HYT*AȱllnGyx;9m$IFF0/,`w8XZ 3sOdmb1>bqzkd =x(0o^@}n߽ܼAXp}rlA`iقә)s88< v"sܹ}'6 k%NlYaum{blcK:fjzYAnw#zslS#$I67@fN!2<2F0ʾH$j?0LNeLg?P(Ľ=݄aZv>5H- IDAT Skgqiwp 161x!V=DQR3߫8 _915bf/[V&NIo߀!""$LR4ڼx<^|(. kt:E*" H&IRq:]B!lv;n>]B0@ @ t a~.ټ@o547F<#?:6BRJaiҲ={z'2o^ H0=37Ƣ % d=Q_?<^/ #XSr.Fu{9qIGhkkelbe h>lw:ݵ25=4}TUUT(}r]H?ڧg昛|ο{C zDx3[$Iffnwp 랙yS47}AFDDD@P(().F*x0i4DQ$pAF! "iV DI$aH^G2F @fd4HOܢH$AGas@ۏ13;H$jqx;vVVh4ooi TWW119s.gm}27_S{<476Qs4fmm&cmZ SYYgrs$ -**+ʹ{>zC&SݽO0$J z' R8NC#f=}z.v3<:\|}$d&S>P| ^O(bmm*Sgt|r‘3ukx:EC2jsh4Noν#TUVҌ䳌t:#-GAO4%-Z:̃jcC5Ut876S.184Zo`1 """C&L|B!JP(JD.wZRRif W(PT4hP*j m^n\B!GTeh4HRjUyS4* vb-,X<Ƈ}B<ˑ8~,[⪲ZM v76[`C#(ѱq!3538:g~> 8Sb< daq߳ήȬ,67342ʧTxq8ܥ% ?7/7/ Hr"3D"QN p$n0dɲ_?`qz=rd255(8ssD"QtZ-eeTSXPՊNETb4H$ٱzil/hin`2(/gk{I<'N}a͛͜:yAXY]ciقh2cT2E(2$R Z3 L&# "NH$̶J%ٵZe#ay$M~齭򾪫}q;~oρ'(~ % A%@,Q"E wkfgg{{U*4U3w3o>?ɇ ?lgZpB!lV+gϞ&P:;X]]v+סܽ^‘pYXV Yj5޼zYӡaGeaמ`ZEP.htZ%PFI$S t:,.kt:ݱXhlZU>h4H2 ƺ0.q:V$I^RR?C_VN!UzG0Jǭqok4gVC*nWTt&Nr)?zd;+øY__CrGYZR??Р>uЗ#VY5ʫWVt0 +yCNwX _o{V~Azlώ:FhZ`0Է}8^2",zpdƁc娭ϟF9珶9P9cjv<߾(Ϸ1]>G5TQQQy]8 ߇zեem^hմMEEEuC9+"b/'2VRt>=ZdTTTT^52`2b{gr`(2}|ă$I?t "K~MWfum7^!kkZwDGX>pE'=~ӏcmcgwW`T*15=蠱O*!0Lܼuwa6h'ppdobjg:Npr`C<~2DoO7 F ;q7( oo׀׀VUquZ-#cDbH2nɧt_jXY]# ƻ\0 K#Μ>n5|wXY]#JsY66(| | |zLX;V3Hu$d"_((:B,g|rt&CZ=q,<{Zո}>^;5WYZYedtjzVG2v$ LfYi|r^DIRt5 Ä&=y~nݹ$Ilm=:>e0TNK$z@"`eu&2BQݓg^A--~ hx莌Uѱ b8z1"Z****_~%0Ͳv8:5`Bб@[k tHkK3=}=t˜NO!:;dVY] HԈN  (JLNM /m?aumX< 1(>RA^r=_/1fj4|ݷGD".D2y 'CGJ:.:VgeeIhiiΎvZhbJXTh2?G_OxO&TťeΞ9MYf{'@Zet|Fx\o0j E:1>5@2V"L!T*d2\.d>qΝ}&nXhf  Ng!8Vϝ{(UMWVY] JԄl456ir 'T;****t3,wh4Lff=Bϝ!dJjuA@ӑq8TU "VPXYpX,$nC & Q!N(hh5Pa0( fY/VCAn!ZJ$P*$(" C-#FFC$j XfRRI+cу| >,3lV)wF#bL(;v$I ;3kb4)dz( cN:Rr9eqv I_$t8XDJZ`@*DQ)eX(0Lry Fl6G:#wݮC}*^b "&S%L&# Ϣg2Y =f|nfY&fL6pjru\ajr8ѨU!W*lǪ B >V5hgz2V~(8vyfQj V:****PZ  :;:xtL6(O j{.RCC:5yA_U0ut DQ$giZ-%JUKwժ!:l6mلp b6PU{XὩh4cٲd"LQ.Wm}?\Nyljsw_Kt z=#wY5jxl4>>zi]|]w G]yRIMvmWd2*6Z?/]VaSQQQy9MkI.0mt|}vv1p֖.^d|bK/a4tqe%/ۄ/zeǃl~emSQQQy8jU( W.]bo~.'&@&'Nӫ********L&44x0x_. bZ`D?].\ォd)$Ie+($S)GFIpYEcvnD2ĤU.9RHj#c s*2l@Npesk%IFF)E(=,.-SՈ㈢#cd EE;p7~0# rY319M4z@Vcm} $cwj2Jp$B"!/A`|bť%gHpQrbα~L/ r<>D 2 (2:6a-ߏL|>nQJٹyVV^OL!Wd`~Ogn~A9YUPEEK/;メ  cłj!s-ڸqуܽOV\042$>F*by=6>;:I$S/4U~?x8(tvtP, E6etl\ST~XX\fsk:IRF=yʣOɤ3r9ɓaAdtl{t:0 Ivjs=1q[+C#LtX,S^/OdhfrjZAUg'`k{O"JdhZF4z3J?:Z̧?B)B_T* =8YEqyeaXIEEE5՞5s96[Rt:iootDllnH&d45EaT*G_'-hZ0j/XXXd?. 295n}. lc{{E,ϳ$x=.'OGJǝ^ӧOrc T$zŎe@Pd 3;;O"$DIRd9Gj5nݾ9ˌN97_XzAp$k@ױ$BsYjt:%v319:~#"k| ,<7*D{{CãyR4gϜ7sOTP, JgXߤZA,N&!g&wvwp8N=667q8vlV]X$ /;k*/@.GQ( iinPj4HJ%.yHk Ĕ"cXhjjb:sZd"ᰓff46y/5`fs$lVNd*$i($;j5XX,JH(fZ$Qx"f?YJB G:X**gNB%*faW(pj5vX,btTP \.'JEf1xnvZFPP$SDQ$`6q:$Ie5pq8DQ (Q.ijjD*=l 8\.2Yf$QBD| k>'H>_@cRd4*>[d) 46rL<@ryr?:_PEe24Tb!26] TeJ1$Q`wcT,n" HΦR%K I<,^ڇ#2,[j5R4$dn~|pmT"uD&G%Q* ǎB"V#Q2<"I!W>SpskjJ< }{(kJ,.- i],G )Y~f786^c+r}CIIXX\"+d̽}5ޏlj:;/|OiuLpk{PH1FYPUTTT s;4N0 IDAT9y7n tL2? nd덆v8k|.&CVf'zH,Q,~D:cf1 TJcnN&P$U~+mGv*_=?|̃G$On|b!zp@4z(Vd2ӧ<}z|&Rp#I*fjz-‘?B\>{Ie2k|H0v$I<}:̃GT*yb" CcnݹGXBhIx1C^j'OJOo%cdtD2y66!vH$8x VVהmA7) EVVO>࣏S,H-"Dwq#!# N'C#ܾ{d2ܣX, Xes> |ۋl&Icڰ9\rƟ!@$joc3BH\،N'I# %N7(7a08j߼]X\Z"Y/7bQU@?$Iwwr"}=4f`p8|K/03;KYp\X,fQ .r\<ϙӧj2,,R* ܹwJB:\Mb{d9rS3xcǼ}.=ܹ{K)N8}U$tҌagk{<ΞYE9K0j^1Q`ied1;/kub\z}]66 En\.'N = ՍMB~l6+&a~R!G` }@EEEu)ο6_S+rh%'74-g/_cgu3kl,{,׾:V^ZZFsh~ e4Z-3sb`2_*_=W??eqiV@?p.'XZ^QRbrjGe;@?6 \\)37@$xnA8VRqy>}ΎvBp)>FP4 NpD?J*_XdqiYTQQyryB0.B8 ^M*Jqt:$:P(LcC< E( $')|YPVd2E8@$AE:;:yDRpȱ5HStuvR(lt:[wcsRD_/M,/N\.OOws\GPjC<Nr $  MiTQQyUQuUT^/ۄ/DsS#l3>ڦ86} k1********oېcy󈢄P( Gxb8l6 y9rdU~]J%r<>_R`0LH)KzD"lj inn"/`6H&Sds9Z[Qi+"(bI$h4m **HX;7v]kl/88J*&/v,H6CaH3lV ~ *5meD:`4+o;C( '(Ԅ'vqp\.˲.Jh5v*Essf`0ޠX].'֊Nc?RD( @0pHCd2IPt:C:3TTTT^gt7ed+WD&~ab6n|zUΞ9Opj5<jytW2Ho;[XS||&vhԨ14<½V g0H\frrJIPږe߼ER!(ol%׶GqrH$">_>"Np8}zǟ@T:Mss6~Gޡ[DTʤ3ZtuvX2KK˸].B0+k}>V rOn$[.𣏹rOF(mlF)Jkh`hdZFۑUOFxrywv Gz=H|&z~_{>N`I3T' >( J(gN?Zjj5E| _OBcӝ*5B9˩TQQy9TUD(uXu:ouQzηx ^(ʫD/6s ÌMp:;_Erܻ'/c)KʫԠAׇ(X,* ZZXA*yIgWJq\475Fc8tz===܌dF"IeNck{WvUT^ol-e8W6׍xx0;8‘mtw1=;w4 S3;>fgT*57xڛLcǹxao9Z[鋋X,lnmڂFa9/,٨/<̤<>kJB&S,J$I8s$ ʼn>nܼAx"A$h00nln2 Whjj< Zu W' K4#0>1ISS#=P(1\\^!H g0j햿$IR,p\D"o_O) |jF!s}:ytdɩi?SH&' )b*_Adsk NG @8b( oDR/12*kY-ȕB HK_]+Vf HΎ$IbqijSYtd2 K$B6c޿{p)gfH4z50p8.:uKK:tlb1جV:dGR`ZX][aw(N u&b>7⒲]>gum]$Ibv~sg0=3n @IXX\d' LQ, GLNMC **}= zM R>****_2 u:7y9NѢ%Á^GrI|㪪%cm}3NqEfiywY\\EhTif0>1o) 9/.a4|ccXO:V*ywY][c?K&e7 ɒdX,)GR A0L "~+/rT*f$=`cs bxFP\.O/h~}. ACVcsk[9FgϜA&q:119u7\|Nҙ t%"(/ r"Ѩr:;ڹtwޣ V>~IDJ¹33áCAly?mo:***=_%`! xe:;:Wxd 3Akk NӁlk`{{,TNяfj+hokd4IwW'^vłb֖fZ --4˺-̈́ao&G+J477q) ZP([oI\skPfeGH iii`0"IgNnr!]ݝڊa@{[+V2twt:+f9Ν9CgG(ۧvJgNvD\>#=yNV׹0x8urQǃdpJ5z{l;{F3 5:9A7^A( 4776rq}jV6JC68M8ATpP*0 8vUPEE~PuU^T=ߜ/Cۣ8FQ$&ޞbocAo`|b}GgwwAORtrcn~n߹K(vH(ZT??{M_/\I?;o_clb%xe&fhkmƧt+v.,-H$et|A 8($I7 G4x/䃯Oo b6q:HZeum~X<.;2;;;;x^|>aޗo4)+-asn#w& 1a85Ux|~?vܺؾG%Al6Jolv{XZ^a}cH4!j&]^qRIγB:R ӧNrT}76!/&jS'۩,}9iiii7m'۹!l':SS VW(**%JKJ2YdcsK!g^GT[P(Ȳٰ٬hZT** 3l I l6qiNrrLLMq9 UU/>|~bMhh4qҒ$)]Q J^^. F&ZMÉzJJR)r\a6((gqi].buvq:iin%H̥ F\.Z[)++!77`wOpbViki>*W(imiFղvs))-)&RVZBeE9tZr]. Q(8 PTX@Ue& &J#-uQ^V痢ښjr),Ǟgum׋3&^vGiij33TJB8hXvvX^Y҅TWU27F93OѐL&46 0qY.vV,$I $H05=C $ITC3~?Y6>P`Zd@o tz_Sd:YPFFF#|:YPFFF[U XFFFFFFFF[(|~? (,~q+ Ezxfl>j?/q~Y~G####ć@~x Y\Zh4HY\ZjGfeZtr=TJy_.|I<|hij;wcqiBP6 GDZEuU%3s475211瀌7c`(bf&NX: .|]bX\Zf:M C!67$‚|?ÞCF2|W&GFK7pF,fl(>_Z`= &ښjF&j\r:{+IKVᡗWwxzOL|l&ו66ӧ:8Q_z>-,v Dn '& RXPbgg}~&~p8ᠸx,.ɾ}Ls 67tq 6˗.K=+/_HGGta0eqiXTSRqI*gskٹyimi,--vzuރG8sI'z:M~^. ' ]v>p̱̋!`J#$ Q*"HO򨞨ARQ͍MT*% >(O$Ģ14 nI{6jb6qpxH[k vKK1MXV$IC_BmTUVV>84LsS#UD"&&&)..d2!QۓTTp:TP*(*,`fv]Ν=di@vD",,-q `\Z),, ҒJ%]=~JKJX]_R$^/m-̀}7Odtl2Bv`ÉzssQ*SXP@"?@nJ:}fswof L۩(gMG{u58JKq:,,.uYlpj:>2vN061AEy(rR*1Mƹgd4$r-SDGG"455rdVkX\\Nܞrr8v,f hƆL3LG h8Q&{m-q8Jʲ:222(>Qp{{|p\$ H^booH(&;ہjF  AVap=*xʲqppab|dg>JPFF:2222OP(h4xPǖ)Jtz=)AȔ 5jR J/t|LEբTc2X,4"g|Su??LJ@+Y,G˕RDtl@B^/+ R a.Z$ Efd~TIŴx}k'qHJ*$d'PFFFFFFFy,oJg{ټRWd?[.x 8$e Dh@}=Vl%###_Qt4(z'"X(8r&J% Qy$"RZ yh k*SO)-)~‰:wurp/me^?^/{0>1{tmV+Y6+󋋘fN:ɉz77u9} ǃ|N&d& E"TUV CtLvIQaxE66Xdyem-͌OLT*ws(n2~jF'Z[/ju=llneˬT8{lmﰴ`3.gzvضZt<>ƞaښjqdpOudppp |ʇ% sR DV# BBy4OhXRJ$))3?Vz9NaAT5 aB/z:c`h6ܽ3OR}YZX\J?[,px":UTVV؀Ncl|X,ӧw' Y|[ۜ=} KqQKffp8-{|D"QBuR\Tg GXsturn^FQQ![[۬olP(xC#qp:a43}x<|%BnKiiaq[/]'%,/P}T F매$xQ)U@Éz=馺}7[;;8ٸN*%s !Ν=jFKә&lY6'ꩫafv.g819XY]шJ™+'&"(ӳsQnpZkk֖fù=J%v]5F!/ } (`5HDDJBBDBE %$$(ȲZ(+.j~nYPE@:2222OQ$ ԫRXJ2Z/HoD*Z eR@HR Pcȼ:YPFFF1qt:Hјݥ$|K_xS( HfJa F#(""Dd*JAEd*E*DQy822222222c`"E b.Wpmj4Ba45>ZEӉAѠ=))TXV"!?V>.(IH I Ia4Fq!ZK'eQI_8 L&D>edd^,lO|P6_gojUK$H&v6PH2CLF#X tt`0L& h5Z_ h8D8FVj0L$ r].<7 _0J6$ QQ5!$|>^l`0|E}%$Qk2H8J" FD1>VVV),'V3JQ$L]*3IwC#$ 666yPV1gOb4lnm#Rt򸫛/Ct ӛIJdqq~mt rL[O$gspxHIq1-:[Gܹ{%A`|b2c x v~ JrƟ8}#޽~++qS8yLa1ɲ1]x&%ܽp8LO_?s X,ffg(,,`l|`("P']=LRR\VE$|;;$?iffx `P(ģ]/PZRuVV2-%"Debr‚ؼ {SOqDq|'O{d2aYTJ xNG<Gģe:[Vz[P(`~Tj5pI@R!H"x @@’RH'<@A1~%޽w%~?e~ 8w?} '( PRR 'd:3s^;w B{]n'x6%%hmifbrX4ƅsg47cZ(O_ q멨('//ф$JجV,th4`2p:;$89dehqrqh(l77P P*3kZtzڣFR)$H$›oCMM5-M:7=$ 9}QY^^1==KP*ol284bZwdsg IJyz>^{MntQ|JcCd>kA^C#t=ù3{>& dp8I$A0pE6LbyKcJ>'^p$BG{``0n'&98Fh4Da8,rN~ɩlmeJ K{[+$q*>5IDAT>+VᕗoR]YɻѨݘͦzJS';(+-ecs9ۧ 19ض;ϜKQf%$Q#2QҒbz=lJFaeulYVfffx&###@hC()QXqB$(@Q!H鉇 H2N2DNSP")Gü ԪAAǢidI0u: Z-ǧ+.+kTWWh% !BT"bf~!, lVNu֝wYm0:::ѱqWV_%zxP)ܺy7zLu\$7nEnӧN~wgtz‚zP*TUVv{2`Nv18qT$pjj{!ޫqyNAOWW>zB]mM&vhdIo_?f|{<ꦶV6^{6.oyI B\|!nݼ+R刣̋˱+Գ" %HXu"ͧ$r r 1 $BJDɬ+"fk8wϞTK 4TWR]ZLEQ>.vr6rvV+.Ut6QWSg:^ T*^}eJέt,۟%%jܸ~m'&ijK}qpx(/E H 1aT*E@?2l زl\z^ϣ]ڕ$`0jMW))."Fe{gcul64j St:(gyeBI^^H$] ;;LO"LNMO$hkkIH$Pՙ@FHJHDe{{cu[[# ).JGT*%׮^δZonsCT*H@ ^\_ Z[),(d}}IWۧgN36>qlۗ.+c2(.*]ZR£'ݘf*{!f9p$B$X\TJ%J\GFFFE.h7߹CIQkD#QB kD]chtzWZ uRPhU/*l&Vhc2鱘جfYl s)+)<,:.MI$:‘z9˰D('pB6"S> ?O&2$ekk ڂ`0.VOks3 h,.-܈j%2>1AY\H}]-:1bZ} Βbizx> zfg(.*"HibwouTUۇ¹ΌFaoof\Œvb3tvI;,lVF#/.ŷA"(s QTX:63O1V¹Ngfcj)^/.G^L&de@Dck{̵DDn\O0tv$FZBpM'GyFTРtG.8R2/ųDr.~:( C_k9By:F aVQX2VHui||>?+#8 "qM ($ :}=YOgqU~־~:s ueddd8!ͧ3djk)wE2 W,Օ(Jgfijl#222/nŋw`2+ Gddd"G?OӒt;(/ۓK$G0>YHWO/ LNM3=3+'?( K,/2>9VcRAuUW/ RS]#,.-SQ^afv^4gl%I;w B~(J?x<(2 C t:cj;{=m-͙?xbeuҒb#;{z:MÁlq~LCC=CRXX@WO/x']=/`YY^^%/7;>1(x2h4gHw:Y][{I$q&%%mWLX^Y% 򤻗9(J޹,e%GՂǿѱ \99R?FFl2aY tڏuFF7߾iJKy >]!D͆f@Ny!`ȷF?@yY)(B/ \lnnqCTj5vSTWY|Eٹy z=~8{4Og.%rxpx>WENG>?@<ʊcf FpKϓmu\x\^͊`iy~:f O͆9f;6>IsS㑴D2lnmTG;:k8QܢK/ʼnDӒ({{Hl6 2gfy rp?0@,ng8!LKTWUR_W27] n0dpxh`%=ރGjSO?)H޹klo#L%TG;#>\H_<(+-}.+ D &CMJ@ҙFB)nlg>ڕˬJ&0e8}ݽ}XsxwnD !~r2/<{{bI=;B`2=5v5yg?0 X,֜<$1AkKlnm=@ 1PJAR%G\(J !:Hӝt"I*fQ wR^׀b4 X{38Yx)(ȧ#H*.dwoCTT[A&** &%bH*5ȝ'i-Dy moiG=D4czIz|gDP`4t&;;"Xc} $%帘ZZn J= ʫh8E8u%g.^h4=<1[R\9;]WR]dg;>~#,VED[-##Oď>W7PRb';m222_%9TH@(###-EP`X2N/(########sy,BFFFFFFF[A̬`IENDB`gammaray-2.11.3/docs/manual/images/gammaray-connections.png000066400000000000000000004502511413310443000236720ustar00rootroot00000000000000PNG  IHDR  pHYs+ IDATxw|?lEۤU/ 0\06N;q%%}w~_ߜqB! z}%mS, ~>0gޟkg>;_4 @<!I2x~i x #f I 烁#DԵ,X pFaRג0>O/u_N*J!I/ V~?0K[MB, %wJx}Y: tZB@@./u 0 2l ɥ2̝<<ϙ8~M2ggK] 9[*6sn=X  `$ؗ.` 0vP XeϻEžA`> @nq\hIX]Xp2 0L,i4jRyDTJ%=&;+&HHtzNƧhz~n^WvQH$V//_^zk Bo͛6$q}~?0HdU*U4`PpwbZ.-)k D"ˍ}MSX’ò@ !Ad"x=Bq~  DdUNA9wm)_>g`0kolVDAk$ͨ㸣GOl߶ e7cAx퍷ł ޳ѭ[^ޱzS ~Ǎ:}6 "A0/*̿+}_7E"њիn꺮 =}ϋOc;޾XFt_YazY^VUMUU_' nkvخѨ#S2fOLdg$ LȤܜd$I&ԴD,_g6q#Ikꢂ|FdN3 $1(:]ZZj,Fwuw{>J̠sBFxDYSwˠ+ދmmzesM Ng4J;~bp`h0x}&:sĄѠo>BbkgfFR@YR7=3jln6n|K$y}GPFq$7s9I,;3M άֶN^%M/~Dajh4H@C#Xնwtv";;2Jkjv{<ӧώ z?k*UJ@čU/활 IIFu:B!ub$cm.f;x ts(UW`+'OŻp-9=v{B{٬:V& JOKi5. XLSS3ǎ BNk['۬.>izxxSFdNͺ$ G)i8v$˲CCc8Zhimmomkh4cF)铧NGѹsMڠcx"8nbbRd2 jɆ8Dl9W-n0z}ǎ|'>~p8b6&'O:RH$F`ЇB3 ^N`7\3^|I*j4Ғ쬬p80tUUEZWUVNOd4SQ<%%l6w=j\j2%:\[\^UYn6\.D"('I!IJٳSv{1w. I/KK HNsJSR҂IF}iA0,˲ن؜,xiR<7oʪ(G '++/ZU]^Vx;1==h/?A%b>Aiq c$InqVC蚺99 d_F"U+c1zڲ֒dtnn~rj|߁Cη fy17iO<ˌ4AsRdkVP,F{]n x6=%_Yyvڲx۟CxjՕu:N}w>UdXfYVnyreίDW_M3W(pO4eV;Xf)) DɨD'|"X$"|~XRZR< ʊFT|qT#{}W^ߍ M 9/70eqz}H@ce0c0#f # èTJR!fsiP(J!^lVO~`hZ=5y0b6+-7ܽV&,u^Ϡ׍O.)0ihj|Z6 η aβ A,I$ɩ,0FQBKjZ5::,/rIRLB0񓯼:rY 4M/&bY^߽`&Hrsj?yjvb]1 "nH$WYGW=%JmVD!IJdRT6VjѨlHOS륨\._^Vj2%!Ax}?eo9 8 C^&Sh0%%!pSTiia24fdJiaܜ‚ȑ*2QG+@ zҢ?_ ibY'd rWywF]a IyźU$lV*K]N6K _Nn/XWW"H0X vůWyA,$ԅ$0K]­ D3!dRӝY#;ޥ4n ~Wy^&x'܎>:%-q5ⷳ\*Ā1X  `$,` @AH/X,!\޽ތoq}}K]D={ao86ܽG+Ʀg~f\^n]HQTŶSx>BkǶ򲛰?YZZx=f k,)7n!IA"d2a1*2 2L,aq\48 Dr qeXLR!8 #ie,R B*c4/gfXKKcgfZ-ðTx^,rP,yyD"B.cFQa /2 Dˤy$,0̋x) X- d4ʱ,Nr ID4M# dXr8aEtT*K p(lYw&Ehχ{G&0 [{ת{YP(;:(JV=xҒ}׿B/ݛ\SwBE8?7vZbaࡑ1\vE §Ny'"Z-B(k؅`]#`]! i_S1lkXsjel6s|Hm}VVf^nٱU+W>p6H4I=TVf5_P87?T&~d^7r011_HO;̓O(Ež'mVkoUww\(Ko^ؚλ{V)E~k{g˱.{ph?;!8{7Zr}wt<}nO0/{٧:_}6tfff/o'bD"Me%7bZUT* dDKWVETHBBUUT*-_v{(uy#e?gfddh_0 *b ytǏ LzUuggw|Va2łxvvf O^QV bD"$I/{'Opxffv]b\&pd|bb͚$3#8ߏR*UU"(%%aYSleeuǎ:R 1 Lʊ6\&C! aKIc9/! 0 G,80h'BH$BH,D"8!޴k78O^6y DpR{H&aKaXVVFnNvIq_ QC! YñX.HpI!Ð ޹ѵkVG‘.~'LQ8M##c  F~h4n2%) '&H2zLIWmP,$;p+Ҩe%{>;6>q\(j޾H$RxڽJ؟N.\ʊի *R̬;J3L01 'cn1)ʢ5E8a4(`rjaO5`Ͽ{1;߷N+B)J _^VT)K!o{ cٙڡi2=A֯K1 Z}Zbh0`up_ EʹzR7D"JG?^vvCү=yJhT*8ku.  =S_aeyܦLI?X)ɶT^ GP(/7;)h2%8.rsPw滑Si5U+Wd`Miv{<ϷcU*A\n04-KrX,ib-ra (!Cn X4<'Je2l0LTڬ:Vaxqw9]ZhG!WHF8A@a0X XPhdtp h"BX̦!! 3R)jH$+aJ2%688$JeRYR1AXJ7T*eraRDb&J3 Mi)J8Fd4D#rs0RTV^U%VZ)Q[FT*MN}rL&dWBzZjr !OAVT NF]ZRP(ApCC4Le%vt# !JFFBR4 8):ytVfοce'N>Ӏ?Ŷc:z/gbQ.p Jjhlyb[F38j'.D,6 2ƛYک>>ػFHVM N2B`djJJ,..q\.[QUxMmX$r{<]=׭],OdK$'񵓧N^٬nؐ(El^ᾱ Miv׷a:Juφ BİGGnA 5W Ck6{EUI6k_8#=bںU+ (-KX6Wذ~^y/?a*RVJK1 8qc! X\b6KRr=O(re>cn,i4Y#U*˽ |BE[+W|߁C gy8Hd4WU].wbj4/,8m.[8N8L$d2YʣN5W)JEQǔ4?kV~[ixlXFcn_"*ʗDdRxzZ*/j*;+ˊƦ*Ch|r"+3S$ݿig#$I89ٙ_g v]="! NTfgw)`^#]* v|uhz`` ( f'&VTV,UUUGj ;:*e2dF4Xˀ X,0=%%YFr+qOKOSxAj4EK]{"?/B$I8W80f2xXV!\.ͱX,bh|b!TUYaXR)vH$ub,B%ŷi_=)%㦧g'&nX,^sKAXVtYϟ<33sxO Z&';h4:͔//Mv]ey2l iZuٻ.{tllaa! U/_Y]8YZ&##923׭[UJ%㳳l!`'H/yZ'&m +WOLds,vgf@JJ))v(xuC"uRd|Br"l2}ro7/ΌtJU?T\IFI֟OG7Bpgd8K]eaV 5ЁGsIqBbk旹d~Pp)2)Bᚺ A Z~9K]Ru1M_{lM3!D|+'.BJ/(-)VuA쪏J^1E"\>+. ` @]{ @X,cYv _Q8vAxڡK]ȗ JOv0v}wjCjXpC@τv;E` @AHh|.$󗾒|8q{<.3X,}>{OXlH$ ?j$Iz( !ϑ+z{{fg,0^vŧ:,]nc9.>% nDh U3 hbh`(<˲.rE)k0Gwvu_>gY"™s?O~Kl ,~r{`|\nSSBp0 Kѱ|"EQko[&I2_uO@Qkq\(^ܡh:l  IFW t\.vA܁^S^p؉Z.F"DuRB#ckfffXS֬g:TFRs>pVWU>u B^}/\e wSYQ̓8u٧0,(Dw]g Ü:P[cYA̸̓ecXKk[ùbyOM#5SS8gfo{a{J0<  }o#oٵc[;~8Oaw_x~˃_gaӈ͛6^118}ڹm]qFaXvvVvv,+\f8rr יoAhkoo<޵ e/X鏥{{>ׯc ƭ=PVZ;X=u4q<E"{0--C`0nϵO?Rۼ55?>{}kDĞ_h5KznyYiWwݞlZ9 y9/\\QUP=^o` t*2?88 5lO*=&'OeE9qC#b$/'nO0,Y^ NOKHO|.!$lX_|^SU3 PdJ*)*$ Vxbx}O)ɘYWt#[+ʗ~O_|wdda‚|55\BT/8ILN108t칻׭6q x[:))2⧮陁!Usԙ];{?~/"gY^G55Om6c14<iazFCZ=wD\~A(Ew:UJlv݂ 5WF">Ry9Qj#=- IBQVZi9$QnNVZj*EQ~vMZީi23ξ4;q8ONMcUT,t#cQ áP8##=?/7)T2q\NNvfz:,uw/(er-f3`r<B$jڶny׿mww셼56OK?P  ,CѮP(d4 J̬17rEM}9D T*Ugs 8{}⢂7mDE [+/BH$%Ez~a{zTJe<ل;5=T* F/,ɤ(56>ˇFJJXOOT&Fwߣj2华OL`FQTgwb5L/岿)r/H&'P^na-4ZMIQa(Һ A3sn\._pt:myY\._p:{WT~9:6,/w)^;{^ zwBmm(_Vq͗B!_g11 #==^_! /[-U-us|¦ibD"d{ݚ)GFGs( 򫫪N>F>?Qq\k['o)BჇwD"?  ?0LYI1%㋷uX̖dpMBk߁~`brpMоA|B,Fut;qi(`(tHsMޞnkr=Pv,quM{-+-Q*+:{A?tvbrĩ3gt:R;08 myW*NNN5 .).JDP7O; GFgfHKK}lԏo=aJJU<Tĩg, kd4:77wPX `޾~*;z!tPM<,\M(Ewvt$)B0|߽76>wO:s!ӻa;=3sH4znxtȤRSQ*vvuw/eٳ55,.p_,;vԻ}0?vywOP$wБ@ 8:6SGj``m1ǃڷPù`(GFKJeYiICcRwCGjBE8/\ C6>}B?x8[^|ήn$nϋ/ |];2:/\|=W\Sw4w=!eϷ -B~}Cr,Bt.,8ݺefvr ,ð,baX(az$#rFGokwxMBl$sss8?vbwضf3gE?p03 ˖=s7Y,˛bYV&s[\qa!~Grr99s96U+-7wVL&z91W A`AYn AtNtp:茩Xۣ/V pP aAиZsܙ3aR(驩zfDG"jSk4 JKq7͒А`F;;rGFFU*Us5+_xyBy<.AmpH8QQYzeKR]M1Ņ8A`0y'IMI9!-%YZ=[s!G qЌY0 k+W=g+M-KbGw8-F9??h4vwd^D"QCCbxqNop:! E$贞{3v0[QHQy\NL:%5<(h ޗdsժIAd2I%!J%O?ʪG\4=5t>S_x`0RKO[X}b\9Y}DE%&eт RS),[ yn=08>+wb==w\O #@p S*GTY6`0>>ѻ ?i\NdD8zQVѦM}b^o5 v="< O%=L,RڐnC$\:r^m Ȅq>%)A` 2a0t& 7t gP{qlAd2JEHxS6.9 c i'OWk5ڵ=kモ|DyA`u 0y⥭;vK-ݜ`A4R0L.zzvJ]D684 zgLq~~h0J8W?_> Af2caa6fI;@ FdRIWOOhpkAA<`LG`vX,V7:6/*U Y,o~I!0-n՗;{a^a A,='gR(@ax</4ZmdDxbB\L&(?DePP`=&?09j:APj S &q.%]0 ar"2ȤLz1YL &&f3@?0\T_zXP,"<AATjT\gyd0lZ0rLf_[aB`@Q @lv&hl6ۍG @pC#b8b8N5Rh24*pvVƌn).hwK)ɉ&]L{b%ET*|clvWw/vX,EhՆF:nwtv)S_|(@6MP Fc80-53hi.3YL?aO(>,omk7on1Nkx : uL"4qZQU._h:HDQW$%ddR;zD||'q___6U^Y33':eqd_蛘p\killfHKD-;  eRQmL&eX6 Y,BO՞>PYu0?APt`0Ł=!!Ka^d$bqs&J]d#9Y C#8t83- @ X.ʣ"Ô -0 'yx(t/W\o \mik7d"$٬޾WL*a80 >1ۇ`ؘh>$~!T*5)1! pr8lHHR oZJ2(rÔ Ld2Z:ȯnd̦\b8aٲ2i4|P.dP p8aJbT`ð‚АoR2DV "02:X*FyT$ |}|zztzi)0 wp ctlltlG ÕJ/ ##"Ô>>@t}qJ5>4<7B\uWEa?AɄ (;3#00 ' dz33s2!P D0B ˃BCBbb@7L*xFzǍ axtl,/wfzj9w!3#MR9YQ0 896R  S*jHhhH<(.. ܮ${_n@ &.u|BjB 8lY3̛;'?FMM\j?8^~bV6+K/_{cC]|庐`y[{ukry>8nvb2}ZAgk{&;ce&bu_ou{rE:=#'K.S&"Gh~e'BU(Bt1p¼YrCCX^~ILHH'yIzwAվ|>pyO- N/țe6nwnnwv;`y ǭV+B!ݖI0 36F6(htù N}bb/?4gˋ/^0"ˡP(,} `c'Օ̋/^x, /^x};ŋ/^xx'X^xŋ/ˋ/^x>`yŋ/^g,/^xŋO0rk/)}ANڎήɁ(^j4qTO-)NGt?TS0-;w%o10܅'{ ήjnO-} F[warۍ^ox{GgM͗YN8feמ}88ّ@ ?Nv A]5/gdnw͹N޸WVRs8$w^{R}Cp\q`)0s':݁W,[ ٲu{FzZLtL>ޮ8DQ~~+/q.|ŨB$)T$z>:*r֍FS}g~~Zt:=-%9339[yT:& "bhxѲԔ̌4R16&zޜ/nŋ^0/Aa2nn8^VT~Abpͪ'w ZZfdg**O9윬LNw|{G'aёy|||jrxh!" HCݮt:aOMIp0t~즥Iegex4Ţ(ZHDjrήƦY3OF(56_ZrEi.]ol>>ؘB0 1JE_Zǹޙ9ʪSLsFvN;{B[{aQQy{͉aЂiK/?44Օ|Cc ]0{SYw@BD"piTj||\ AwZK|\lGGW@VF>{wO|޲S**O>r\.WYy%V;AȈЍUQ=|BCBIl  ٝb"]oHSRtIn۱b.^8oNI1pgj]Y Z[>W@xC &cvqKW֜ qܣccw/;pP'HMo gJ׮Œ8x8L {+*_8ب-f3a9˗-7$$$|_w'+//n۱_Dgk˞"A|OT| 8_Rw̌˗$'%L; { -?P&?:CaaJ‚E 捫ԟ}d06ϛ;;4$Jwgc'ʛ/nݾrpP=Ssֶ;qT5ߚZ_*XfՊ‚k~lP((}Lt~_AccSKk i cO+g2A@0t8&YQu֥$A984|ѕ˖~ѦY9Y.豲VK « W+BC)7]xQIahE"ᵖVÑxDESskٲ﫯k/]b0sJ v>8ys洶}qS^  >mlnydQ :Bؘ*44ѵ c}F ǒ֭}{j#6:5{zā‚C u<_~~.\bK|lLIqaGgצO0LaJN$ED><::pVXZk2K;wEY3q0McohjAF,ut֜phNH[aA;wu\#*xR_}}08Xz2&A FT cמg r/rܺSo0d=fF} 񱗯\Ed%%E89{#n-,;xt- ْ5\.W7+u|G `XAUTŊK=n"4 /^?0tɢ0A uE:rlFVfy-9zl f.^8bL]{]r̟S^yͬ3gQ\p92t=NkْE)Id+#qC#lkyfg]py3julyq&XѵA2Ҩ!޽ofS!}($$XT9c_z5;+cNI1;v0|e}wh4?:$X~LM͹4ZDB( y;XLSO,)1b9VvMMN>pj\m42uk”Zn瞽M~~ C"y v78tXՅ>lLbb'l)=z|-v͚ ANtV_ھjMEEUR_/ +/x1ד; _}}(wF/q\"o|ڋI ?^SB_Ooo{ o600( WXAPӵ}$bmhjzdEaa+Wby{;f&7a 1L6]8H!P(q䥛An˅qB=޷sJ>},Q IDAT ðή啳rgDEE_rðڋRRtN w/yj}CddxV F4l1_\A0f?z϶͟ h4'?>=Tz ӁZsB zo?jߴO0Yoٲu;hyu_|_$!'yy Dȡ#[m;Tj\lN߳﫫 ̽`oz-.*0--Y/5ڽ..[А/ > ڽw?AN?::3Ol6ȱ2}7=-N{ @bAl44<04^]bieթ?w_zOUq|&yûo=uW뛚>00HR7>? #23ٙ8~֜I%_GJRAzzoxomvU`쑑ѡa. PbP(),S{_}U'׭}1lמ}7SCdni0#I$CC#ALLL1hH}_d7~ίJ +h&y]y>= m?{l0bxddbb;q, qP)c~TE!PnUQ8Bպ\T `6M{p9ii F$75]JJJdt:[i4ZM׌&*2"\׏236ZolLKiRJFEF OOB|\Eɳλ\.TOx~.^ 0J$iiG(?k#T5 #M\.F}G?@WTy<)JbV[Ww%t:=2"\rN9^oнx`2#c:lMvv&JS |"fͺ|b 08XFMNJa2N79)ϏJpsF300jNU\f ÆGtz}Oo_4_k%3aJht(at:-Z0FIbp:zY7( ̜MRÔNðqBƲXY ðAUu4BAZڝ.PRbH藕l500 Co8YBDA' z`evuuܽPQVgں{{h46EQQ {`Jv{P \WW{p8- TuK[^oP̛]Ld2)Ju\ryв%f&{ΒTjbBd:|XgWt\}} q"Bp9\23• 4MoN:Up:qhu}Uanln!H h  z÷̦n6$ZJ=3'd O|L6-a P(I tϝ0::f4h)IA2)AbcuzHO񸁁)I @GTU1ol (.6Yc'*nFVfRbP$P(\񬬌04NWur546c></wFJ S*\.'ajᄸX7k&J0lphh4vvwr '& |36b Ar~f˖tuwܳ#juvwϙ]ri=znJ]ڋ.p\kic2U[ZCi4L"h. I/Y\\:K( ]sZ]Kkۘj&Yv)F~˟8AT4-:*b^ll@)ܨL\ @HHpVF:BZmUT޾~ͮ&SvV&Jq!|}}+8dC4 Ţ~>>S{ALA 'qne8d/<xv]aNh$@$3=홧hkmm04]Fb8 O>11VOyF֑0 8Fnp.6Dv߭߉{RYK)bc4ZgBCC|}|(=z5AKk+u' HOUk4&CBAƦTŊa/X_}}U|O"W:|"I h-b2-+R+BaTdD[[ٚ!r%P5L&0&l6*|K",N#]LO?_{1>.04f0Lgh4\tZZj@-YJExxXzjʔJ2ijrRdD0xL59V&&O6) g[Vꔮ PXk{GltKݨBAd2D,NOK S:N}NK$ֶ̌tD"!fXjuDxXHAΛCj]hK.lQ$irAUO/7tNNS(B Ev0&AR.YD, B%7G F80L0-/_Yh\ږ*3*8 QGyEՒE /b4_]=qq+-at~kl6{`pn[m |}|itBhJ"00twvE)OUMMI$ Ƙj3q`0:|f$'.’B&1b]mt:aZh4X,m {{#'+Eѭ;vi4fY5<|O{bpG>MA<  $p|Eh蜒b~~lK yw; }u-[2/z4_F SWdeY,bGFG?uGDD,_ O<ؖ.i0 =Ss"a3䫯˝ AP@@;_0\\zyvNb1-Y6'̬mhdEIG1/zbBł!ylM| (*2+$'&O>l<%gΘ]\sϾOB?@ %<,mFvfL|ɢ#ǎ?mАX-iwvg͜ SO߾vÔkVR96(;DEۍYᒢKq53gDBŋ;y6f>futTNoܨSͮ'L E.| W*!=sϾ{ x` ϋ0 /Y`ێ]oŁzP(ȭw//Ϻӛw0 /7wǮ=I2t쒃J7}f̙]|c˝.'FX0 .*῾2Afb"ђE /bV g=zeLtNoزmNg2˖,JO2sg`8}7|DӋ z|^|\_~-1!fi"$/"#IOJ7LmRsK-.,ؽwXa8B SȑJ$˗..=vf ?`V۹{_[G ;#'>.A|;=/M 7޲X+-MIN\jŁCIW*B֬\Ag|;0 ;W[[vF}}kVa d.K8VXp(JtT:vɢ(]kmcN FcOOɝ$ IOO=w¼99748dp9oZz ̜>p:CO?'ATRTغ}ңt:->6gI%`~ww1~`@_bׇlݾ+X?O=00~w]'&FA(ȴxƾ'8͖{;GzP(#A8ކ~I Bd81a0rÉ/qB ' d.$dʓ' 4E<`!nUz% MɓiBy ȧpDa Ϥ=FfR)p{eSAQt`phi 8yt}u熐Ǵim'z Lr< v7or֞&nG2&!iu&iYJySnv.}Ccӆ?Ƿ C0Q#?oW|'B͹ ~T(of}BlwVoI:8pkC#ģzZ >ظ|䤄oo1{~7D zA<F^x_+t/W~&Y&)Ť}[}&>.`x=xdJ:grSD8Um7uFSP۟Dd=9δF6mݹS> ȭ)Ŕ@pޞj||OGVFT2y& Yߪmfhn4$F)MRpVW0:66::Q^ l2h4=d.72~7#AqhT5]]]=j5;\ڿaxF|Fc.~h2++0!Я0LuX, =l)=p7}e[\LK8yTFݓn=Q^u4'+sǎ?1FX.u\U'OpB QwĹ MV-@$ZjϯiVK#Xp~hHOW=|p8+.]#yY,;[{Bk4DYyUs2'ߙ=XUO_ra3b&J׬Zq78^utJrT*AQ{q\gfwo6T {nY8v8Jblj~I^9N-[${ с ~μ?.䐲%p1;;{̙3̞ ”RT/.7 <~j0@#X̙L굋iP(+kV[m+/˥)7B5+{ff9>wEPTU.H$eWd=pO>br՟8uJ7/޽嚮]rI7d2rR Z:ҙ z=˪~;RO_xOlG~!H}Ñw;9rk,|bؿ|;wlkniq%JrͪVH/|3(_/ =vbJKK:{]3EEh?i9wU_Jltu\(GcK*N:eR9=3r5FՒJ lmo(/߶eӾ>[.MMOOMm\N}rIEQzk;B|YU(td&8s<fժʗϜ=ABnBCha֮`܅ ˖7!ښ33g_v3O}UK }7:y>W|Y6=r-5jx[{G"x\jkXiniݲyB&۱m #? m>7N>{> i4Dbv455==3~™R /\ԓX\ /7~KǞxRB[G&q:6N +5Wj4mO&SnwAe*2\4>1iY;ҒL&sԴNYQ<SSWi^/<ʊ x<[_ds=~3<52:z镵+6oqMN4M&aήѱ1\VtG !C#7n"ߊ N9s(]=o !RUrm#Bo_WwtIŋ~}B;x^XZ$H\z5Z-U*͛laB G> y?MMͰ,lEJW=q>1~}-"].Wk#*rʂg,?{bU~[vljim;}C{@ST2߼yPR\\&C.% ۽lRQϞt{,(). O5]Ng?`br=K8ukV,|wOowO/Bh[q+ }όO+kk\w-!C1sEǝf5ج֢BB́[cG\yhll#JyիjOAϜ;/˗U-mniOflzkꕵE>aիFFH(ٱu^X L3# ή /'X<@hޞFǎ 3 S\ľ~|Z-}Ϝkժl6xxᣔᑟ¦hIb4ښd2911tWw5>JGk߭ G%0n<|M8}zVeL)5fibb2nߺd4b z\!޾x"a6.]xnÙL豓qPȏ<}58~s?yzEӎ5=2+i=DfffHQQa:9{>lXu:7m8{(#իV* DRUUi6otv%js`CO"/# ^{艫ךF}d#JiZJK7mliIXzT:׮ކG@&]kh}aHG V~\.{s¥+GPUPwhzzl2r18fPJ7:]DH4ZwattL65];y[<38PW5ͱ'/_ 5R)D)pR|(<ֶAR*sq aTbZd23N>iCpcSHtppǎT*7yZsE˪$N*u:(z!poMy^EcoF>k-׍}$~`(JƾVsҕᏉHgf5NMڳ.x {zT*4]JiKk[iIRZ,j_A{*'}ҥ+T*͝=`!}Ϟ C)5 ijz`(h=ty*J7o܀ћ6R\fdJ$?J߼~?0`9oz<70r!dZLF㢲__ÃNgbas l.|<@:Z~Am`f:!f&9u\(㏚L&A,Ytzʚ O5+wD^=q Όlh4GrpWZR"vn'.]n*++ݾuL.O$׮׏0OM)ukVs˲Ri6 2 P(@DQe/Of[_Q(WOW>f30b6NDQ B2̠3 _|\.ZBͭSJy쑇Ϝ;g}jHd2f)Pw8cŔJa63NBSgj< 2ݻD$-,_%}Lst.Y-Z!2 o GۦJP*gfd2z1:d2@$OMoaHh*E Ba!J)!D8L*òhTP/*FH&eL6bYD1o;VafnB0:qt[{;B( >o& #Er c2whŪ%KT`+ {^l.8vRA)X,-m'vw=^ϥ+ML`;P8"agfBaQ]{JX*XVPmz;|=OL C\&3!uЫT*::;WU.Q*@ֶ?w0FHm[?OF¥+Rh40 Gy:N X~pZV_-fVx< ?tffl6qT)s2(XZR1JDJ'533˗-y1JSFaS* /^208QrH$j2TJ%B L&Q0,H$tɑA)m _ Ob%oo#Q$PH& za^ȋ/UOjw%`J@Io'P*c<^PdY"~p܇P(J\0 lFlhRh_ڼybך[j,q\< I#D*p:Bp`ݹ l(8jX+ƹ( l2 !ښ3gb&&&O9g2L7',$ǼfeͤJiYiɟ}/7]}g/yw3M,}7j"_izzoTV,6 6o>o06˲9 iDHJ%R.8uw[nw*JukppIh4cfAFFǼwOo_aޟ9C?y:NWWU,n#N744\t̕j5Rlxd0cәL 2 ݮu@0Hh5ARi&f,)2l{?|ÿ_:ntOLz.z%$3xallqOO(Uʼrh0|ܫ/T*5а'tWNkYo|^DZiJ*pNMOr9By>j5ʊG'Tr˗,r\[c<66O1?44b1ѨHJ8h4޵N%0XNNә\&DL~ cM,!ɲ^bzyTf3 q#JA&ڱm?yv`Y&Z2 UU.<08BRGA.w,ZvH%&Iom]^2Nt&qúu_Mju`(L&rH$T'!m>I"B́EezR4N]=%IAl>3!f1F.fq`p=/"M.3,d{{c5q]9?ͻBy8nKE3y?D"IR3Tm^|y߁:DGFG7oڠv^|%ȤѱqRiӆz[;/ {ܳb,*+=XP,g1(D#'Oq:tZ ?GRXݹ},Dz,ǖ/ZtkkW_[ZMYY鏞]YEZro9J(>}Z BNmc4͊xܮֶ۶<js83O|]NgIqq_C-f3uUK+kVTdoZ,W^{䡽 TU.9pA^}C*Ad\C ; W/r8'S̹>KgW%s5a6lXw#Gr90%^];Ϝ=L&u]KX|euW]pyjzF"K/~kB&!xipp"[N:'"8{чjXTV5u:ҥo8XVR<660&A$d&P*<~j433|)y<O9;<:2Z2HU+MWȨVzܼ E# ((p.X艓 GT*ht:[ݼqz(Y^Vp衆2cMϭ7][?әL'/ggnyX\_%EE!Ryu%E=kVK8ng_ D$~v(rDr5N <ri8C{gJ&59|RՂ۬jh,p:(^_UUy].QW`ٮ5<|ࡆ&QQ{MWwfú##,,*Xh0ͦ;޾`(N2l*+*8TR\tsV:@^aj{ͪU+!$2E>_6ⅅEee%ۃB K2R̯eښ5+0zd6E"x"aXj5H$J)ݴaf--)~",p:vn&U*e+B(˜]Z&NǦ ZhwO];,fAXB* **d3R$Jgs%=VDdcYϫV{~Rɘf5vk'J}^FFXYNr JE*]%EyGo ʤ3zpȤ2ǕNT>b"͛y&rgtWlߺ)'JnL" ™l|Q-U* d4MLLj8XcO?FY-x<P@יf^ n[TVJ$^;$ zA 6/_TQBv\.e2F)z;8֠/*c!TesX,^R\~ZB0hy=]Pjjem٬zjL&K$JE*ᰧRh4Qk*/j5hl18e%RT.iڥKܮT*u9l& x[-e6[˝v{AW*hf-\"ISZ*p8d2R\\IǣjzPVBu:ݦVCpfY󉱕>ޠW*QtH&jەJRbyU*aORpyh4z^DDQܸan/yEAxJmM?ҺkǶɞiQ-ȇ Dz5eRi8ɤ7s:>jM$ T-,p8q5իV̽\.wZuk t3xeM 2 J$4+ @( z.=w.Dr2aWUV&RPLXi#QaX&ooOp\|wXv΋H/J%կhE.wn+k|Nn?Ͼ/~3a7g|ԅWՇےI\&hԝ=/ʃ{v[TΜ;ws<<=L[B) "?m4Yolb=NLz6y.\hL&YUt3ODRw'`(d2oB)O_8}܎m[>O_5&ɼ뗛ATT|5V HAPJkJL@&OU2L:y! 2,#E1m0(J ,~̶^}%|6\>͘x;2~%Pn, *4]7~Ea(!qX`X`>~9O_o|< , ,0&yVo` ^قvX`X`MjapX`X=fZ`X`xY0X`X`c X`X``]-=(`)/ :Q2{ZC6}6XAJ^9T wZ`;dAup>.z`y (Q;^ 5bSGBͧBM<\ !, VC_,2-Zeb2)zvJҧU M"YCZXd&3o,zg,2TcKm3&):6)VR@H`yXsC\r`||]afεE0}?HD:;D@PD_+N#DB ABn7 6 OA@)8L!9)F^%:1)vEGژ#`K ):# R iH\YSb-dm>yw*7R<]oQo٫ m1uc?C ʐ;;ב{WomI X̵#BBZK8K;[+~[-/5ffDʸH.A10M#Am닸ܬpoط*%w"O[rǀ0!g@,&[mL{`1%i8 jګBUzTЩ4Q PNs#BF\: DsP?.Eio3reOX %#*s`?: qBN`ж5f(XMWOY*(oqꐣ=SH1tD(Xd𴗳!%)jdrjhPØފɼZ'E* / cbGKخ)`)!)d2T3)/gCJSbSxUE->ڈK4%maDy1|ˑ[OE'X9~Laue1pOO;:ˬ#pvJPh*MoDHVYeb33>-L.ΐ D<7 N2P$)ܬ{n%#>Os#?x󿩷L̿%mw6)vHR5 Fgu-~>$܌U&0FT"`Xm[lCgȕ5JQ?7igAJ~Z0 ѱ qaOΌwٙ$  O`L!R9]ZBb jK*1TOPAUzʐ0OyؽNQA)!-1)OzhfE,q dXeb^b<8]AfEX$Iubqi% { Qzl3@ "-!R_.aFW~' o,bboL=Qjl2L484%{t>TNŇ ~Q' "a\h6fyvؙƛdJPJT@f`e RO<܇y=z{(-+CBqzĤ"tìPdcB@a6W#b =N fܵcEZ 3p+p ɐ] t@ f!IZ$c@F, PPE,d,r)#,M (PP @nW]@  i{TH~ e̒gįq)x=;Ex ,M =b"cv99 4P $ moMgTЧ} fca(1W 5c z}:&97$ap)KRdܱnLq2JX͑@H@WDsh9$vYjr rtEȓƣB J0&j Ј`OƀCMi#O$E<%} %Rhz r*0rLR쐣>D8OelxGyh06,,b^aKӤ%Dr44%)rdJ=,R lnDLp.7b)>N p"Gl%zn IDATX fYp+Azb69*V#eDigtF+>&Ӵ7FE 7D1,5"䣈YZkZo : )(Y(hq2 (#*Jq~+-B$G #x XpnT:=;y^3p<0 AرS2T'dTL20px,``p(Bc4G!ེҘ~DP a^qb& JtbHJTӨHo 1$𠤀%L24~|Qppˣ"4y% A0LTT埶i߭E6HNlD  #B<}!x1r[f&Ei$JA"a -a!X!$tR:#`NG7 f`N뺄'rh8;*"c$Đxm#a{"zhXRL2ifɨ3#RLzVKlO/czø#(`7ۅS1$->G/s|H$TJϠă.m r}pyKaD mAG`y(IcXsd%1I:ɛ0A~ EqX0t1,mOtR=aic@8*b wL,"==!|%.pRfրtbp:u-N3GihpE`kǃ;{8ni_b"Ϧ@W c`8GCܛBybxu1dIx wk4x$%Ϧ::(nI=MD+6H1 x7*X0DHQNu]BTUcZ!2p85p`0|.cK-kM!Ǿb~GE)<;Ž>{iI $D^ iNJk 8I=6OSHɽ#5w0c#DHvm`FvJwBI ztn1QJ-*%#( !596R@H:`"MCHG,Πx%VQpo xqDƂd|Y߷h4NXT3iap jO:gRPӰ(h D` ; )3p:lNEj_L,&HADe՝|BJ,ƞDxa[xR'}bO 'ANE$Q)4ōq6ePwN*2:U< >5"y`I(7IrQi$V9ס J#G0 M t,P -^!DOx9&F8|b蓽A+gao:}[%¢$bP|xqD;9EE ()N&ɑ' $Xϔ`,2h Ƣ96!`$؈BXATX(ı8yv2_K E]A̴  t*T w[Y(I9 #1<0VfͶY2_̰ G& >'Hu0nJVQPgPGPoIq*$_5qk8"'y%/{đL1r,fYsoyg rDqPI ḇ9wq?F@X!)nV!HF¾M@BEz"$n1&F/$IHՉ$H ?,9 g?$4!aiUsd}U98 B<4C"=R|>W'(^DQX@O\g;JC!(#"GȧInd$w@# *Ӌ``h2"=iW A%#@$ЍK AoI BU~PIi$D`W$8&%A<PBKhr`W $~q[6%URk K|-OkZZd4M>( $HR7\aC.єJ}& σ nyvr s!,:C8[ s\-u_K AIH y,`/α!85zt %"NcR<;*M5dHP*A , '*NH@AAT!v A"te'nr\_a~m cĐ 'Q<^o~K- xM'zbA[ >!""w $HHX|o솗tpghL+ P$82xQ =WtE- )$B&⫪he꺪?$H C‚ A $H> PApC:F=%$@0A!$]ez$idtjRw"bW${px884$@<0+J9IXq$xF0Ɓ@GEqhxXK\[+XƸek:_S[xt@I49{TOOo\$i3t|sq\SϺ\M1/~nRWO@ 0##'N74\÷zq7nzB-u u ]=|/  11<⥓u[x04\nwu+buVYa6CSkNwtܼpWF̨oڟ 8YJd y3{FF wvvL]$I8YS[S[W[p-GUk E>Znkٴe0C3$Q5g挂p$|jE1';ᰋtţǎG" 0`:e`+P(Xi5}Zaa>Eݠ1 457k5ok K7JtѼܜ̌gw5sXt-{mܴehxi!;+h4b{z/]7Yq+V})o02HR?Ynx ˲_,``m>)-fE"5u_H@bI8t I7;n`;;.\j{7_ u MmVks˅N| RS?fD4\ XrI&wc|crIկuEz4=o웯ԙE|E鳵 )Ζ O;ppJEA8>7',C ,]O?7Cvw<#)Ɂ`pm;l>=k"(A{ێ3wiNJ`0D3t /Oɞ9cZC㩆SgϜQ>ooR'k88iS'k5 /ݽh4zŋ*E' /7gidMn_xc6{te[zzzA봷͟d2tƕW$y߻eX, EwOO]}ok?gJھcMKK2i"l߹+-5\S3`N 8x|:ybvVvFzZRsFFGwړr9$* oRQ>>3#:يmODѲqƗE8޻@iiIJr;p0Zh4 \x㸪%V6\}wthIR ޽o7"ƴԔ9Txm7;5SQ^ԗbM盗,[i$+)ޱkϔɓDQ5s[X"FϞHII&IRVUV|>RI#,+j5QoޜY6$I$Ze߮ Lɓ& {=(YrmݾyhZq%rVYfӱֶv }ǮE@H*ܜU.-[wURRT}#G9?QSeҒ};^R\Q[.ċں}géӥE;weW6>S\\V7wNjJ EQr\V$jήq%}~Uk<$EL&4qB߶cgC"a]ANty߁CϜmhY 7.|ZR|ߪ܇kk?N?upnϩ{+3ί;8K[cڈK.KO^Qsw{@ޑq~CIE9F7gkuUCg_{wt gEbH|x۶}V3q.m8aݦ8bKZT)jj͑hh4vX3F\K{1?22drfL&9)i*^uێgΖ c管aI,[1)))pGGrsY|ңW/<% DY(>pZ=wܵ'u?Q9| O5o\$I>ZX_\TV``qqEyn 7n|%%Em[wEAeEYwOo|;Mܜ{7y BH.tZ$].;J 5Z hgd8Ys`(væ Jۇ=#:ǗbIZbu$מon_Z*m4  8x8]{DI$SR5ͺ ŪOEq#ޒ~4e\IIIFa*Ѱcں" wxs {hx_]yr$vdEy9uu hѡe+V]ZR\j][XZ\cn +V.?.++a$1qBUnNG}C'`FPb1oZy^0_[~wh0x= 5pdA͙ۯ,Vŝ7C~ܳM鯯-ZxhY- mfLOziQ@hiemrco7pdt\SVDT($E$t8춱F#D:I_UU`P.߽__\!B P{ML؝J%%+%j¦ b NK2;v>{fʝf1SϘYyEppG+svݷ@6fJ…;YXf?)Έ"#::/L-ooVUvUm䝝cU(8v\]Ie΅P )+/=O٬jh:>R-,⹰;ٴ^X@IB.Ӏ%+iݹZDp]iI&Uc'DAf7ӟج(p:۶e=;ef\rY9[Wҋ/86JuaE1ԛe > n?Ψ/bffqk~mVR|c͉qɚڪ)'MMA$1<< 04#FAsM?x,$V._hBQRRRfNcDQlh8e4MBT59YY#i$IzUk9Vq/_? cRyyv$I9cZ$=288sZK)RUͱ۬9b^8:^SW ̜?\*،4$SS{CqƗ~nS)"32RSwCQTv*y߸yO} tlyo_^>~׿yӡj9 #V+dY#_0ͧZ1˲6a qo[lVjU(z`0h5Z\k{mi8\N0$ɴ5z^P$0 tZ.^MӬh4zU(rsrBaVkZN9W6~Fc6nnkkDb}}ƽ>B(d{5ִ\ B. \޻0B,H% >`1'a22`$E2PPfzQ$SAAGA0mX2LA z/c-C!L\.3Aחܰi˺ 3y}^\jB$I6\ ЕNzV T*q9VH&VU2e\nIt:헞||ѽy9RIm]/>8^d$Bшq0 L&agي[BPo_=7߼LS"}q2)DӔ$JUe4M py2G7$ITQ>~ʵ7yG~7.\ljԪmiU}}.ܶcgfFḲ3j5)[ZOFfۜNb+).5[F=8DDNn^%aۣ芕kfΘ}9ގR*+L&ymܜ A/M[{{Fz-O,-+V8(+(HE=t}nmk{U&;غ}sMr,77ͥˋ 'Me?3|0Ak/j*33"H$r96gLc(EB5udLFQaw:4ET*s3:sRLn:erNvEQdV.0vbN"RR$LFS1HNҒ4VB&旖gff*ÞbX2yyN$QӚMIVaR))):NզX9tlVb0$QZUUiFthjAe c6+`m6hH HBTmVwDʜPYѨqm)@bjI&#Iv-?7犊lV Nl8Niu:lTʖ EEvURI$A8̌t$B:.-5ՠ٬tQcEQ)9fPѨV0V*fLHxeete6[ZjJ\l6V1HO )* $Iͪ #=} I&I.Mw/|7:n(/3$IjjNB4 iNq%YY $I FCFZY$I4m6'efdrJ;JNMSbP4S\XR*0 i*2n'i ;+S 2Z`$Ia*Qd c1'Y3d2‚¬ cXJsw̟|K!DӴn)2$ɂʊr2-`$b6OH7 %IDEmٳfj5BpPIŜd0fYHJ2)dg<EQv-=-U, c ZMӡRXQ!a ]UEyffRt8,4MhZƏIm߱b)'N0 4ElT-##Mgfi5v-0HdHKrVۊ   b&2;+03#]R9dސT*REYz6d")*/7 ?3#C&%Hۭy9:fLZIOKd$E攔eed0 ֬0kƴ|A)JV#ҒS<Q[jʘ=k4A1de&--5RSSrsQVb_J3]QH'S'νօEM[͛;kڔK:#/7CC%EE"i{y6oQ*C6XWxV*?V?$+-W y$ԙj^ >ROIw7T,qg* s >tZ?V/!?t_1n%>$YV60cLĻ&Hb7CDIQaNvV|(0EQ ؏Ah>vD^Ւh?NxO1m7?V| PJ7s $H ' A $H>P$H A g V $H LBJ A $xI(X $H A3 +A $H}&`%H A |agŪ?}'kDQ|[..޳БD^o~kF+WID'k/]yo\>㝝]_1v;wH?9ͥ=^whtێ]O|&x1ۻ9w?FKo*dzuێ+ m߱3+[ڃC c w744Q7-mjn^fCwOV;{TJ u neӧNޅ$IrWxz}}pjJ»W^COo0sgϜ6uBau$5:ֶRSS5BgpRh0u 2CTZZ#?E[bߐήe+WuuuS=mʤC4^=sIT[07ΜkrWwOQaA4{W^Bn|p[# _y$.^_zT'Ḯ ֦jL&N[UYq}<ϝk:zy(wt)cͥ"DI3kfjJ{(q[n=MREL&#Ie*{(ɚI~ܚUUVKWs__?3F1 B [ݳ   }{sҥֶ\wBE1OSZ"I)ʔdF#EQ.)Jכd2(>?`~uJKP矋bl{W+/ac1. (j58cyQ0*HI`,^Ǒ$R*E3,UK/Gy駾F\.EoPRR|0(*"*AO|?C2 "^@ HQ׿z! 'j}5@8q(XVP`K`0q P*,ˆBp$MR8uǞx1/~R*˃ ˲ 8.h?5Poo7D‘_gGVoشGvm_ί|7߸\+WOgf޳ŗ^~/"[\.wEt9EQxw4]R\KCKc6m~o/YjARdFGrV.ɢX($*c؞? ~QC$Io3 F;ϳr9_zIK4u| b0b$E^Ƙ$HD"`H`i`0`ÑH8P(X$I@ 8ꫧN9}s?ᚵ;~d- 4^rBcB!R JI$wI`4DAD򊙧(P(\V`($ ":V@0(Cj H$Ƙx$5Z MQH$1ƬU*_z9/7oS@ (b)Ÿx 22FT1L$%BR2 qAD#QD J%Ɋ *8(/Ò1L8AJ˲Xko**,(j496BP4#I%IH4 %I Ei IDAT+Q` 8zK8:s\]] ?ɦ[:7J}0zU_z@ u6@!q\((:*a@]Ņd y$˯f򒌑i7-(X(?phʤS&Obh:++o۴eԩ(.K|s߶-~ETY-).8n݆M}Y5u;wE2痍w' _#~ɲ#G>1|ݰfo 5uu;;;p!"';kϾ?PxyNsi5ח,xt˲[4cŚ~OV{!>}`跿crko3sMOI'ty 5B Xu$I:{q] @45cJLv:4Myy 7z]iilz?߽ UgGڵ{3g$L/~e?oEw>dMH2uC×/3gLKKMyΜ>++}Euko] Nٽwg8E 콉SwYl˽sr%>'q\rN$K;ը.Ql^޶@iYd,yl=b1;;b02Fo '&XS[qG**󏝰l7bwzGhj8p??Ʀ;vYVLB9;p贩2Y\\+?}tB7o.QK/,.);yMbhά)ɉ/\:w»{u~L&45;غl2#RW._j N-)-s\!..˅E]oZxc Go`644d~~}E?OB j??PIu6YL*s YT*׿7r7yRBl|y[{{aZx!8 Ax[g/ 6mK7U+ן9w~ޜYd2;pfw,?7=-ųY[Zݻ۾X]]sHjmni ?eE i;wFTo8#SLI1g]j#Lsi ,_T9g~.?H._X lݾl6$oTn bqIգN8Bp:kˆ=kGlG_[mV427{aё @ ҫEk5Ë `f9m;v-^8s.:z,)113#}bxf~qD,~gNGok }'-$33Gϙ5sdnm3gpW[W_QYfʈ/l߳Z,<̓2Ͽ8s|rRC~~xՎήBa6nȘ3{FMm#Q|Ovu+/^+7lby^.,:{BdD8qF)+3}g̚=szeU a~߿HO;{fM]G##"bƏ۲mZ0f訨ں0PYUQ.;s.n)uvul㍦氰P˲7p {|J%̛`ޜj_lV*0om]j;v!SOe!Tzj&nw~}Dzn3 ,E) vI~F=j%[tt\xyC_"SRZvd21 &&n,H,s,k昳B*FWVd544t>^b~-/?+Ee-v0UTUnSB<1">nYgΝ,Hϛ-ɷu⥡!rsFh,{ƫ//]^0w}MBN᩵kBd2!X?1gN۰sf3amݾsٙ'N>陙ark/Ы׿7Tq==}}}/\P`qtՊe#lv{h?sNp̟7;*"#;zԥ+:]K/<+Josg/;$IQS[Ų y,'%%Kź5>RTTr!eSS{zk0w[ QQOeGEeUsK˪+Q,y 45^-5Lյ4C7omk/.-3ZZn0e.JǧO0LwOOSss<׭ 9SSUDž\^aۻ]R kq1!Z(}nkhh4*__H7n dzZmn4]rs|~LLc-+BH$$b;aaZRgA & !4qRadNnB8TbHDZJJ_]{BMp8JʮVT I$LCܲ &%(b֕˗FGGv†E7oND!vm6{lLRpݭmi_  MLHhkȍ+5z}D" ~= ^] 11Ϟ=ѣHjur4$8dg}uJt3`M^~>mEfg$?>`R\sfm߹'%99+3rwuwo߹C\k[;I6{9$IDG!fPBRv\y!1!F F@FZ@OJJ8t4徹vGwwys)J1"~ "#">QO?N& ˜( IssK}CҲAG2PDDxDXAq1{t-m0nw,DZ6&';rwtڳðmۻW\.4jgبl횛% 8{8uQ&Id27D>f740M&IC~Ak%TMM-Y|GhI$ܜԔ䢒6-[jutvƄ/Ԩ1 k輥ή.$$ GR"*3= 9&D%ƍҊG|5~ vf+܀&'?>nLLAF 83 q B*[x$aii2Ch11)BI$bzeU\lPp0v#|W"3,p)$I2Re2gA1gZmR00}G[lE M2EEE( OGцM_!A;u{Gjq:Gr==van{dR+r͊a%* $ ͠(($;/Jŕu>_p^Ss#G5%e&Ym߹{Ų%rlܘmiUF&D"}GZm!Jxm8 -/>g0,˰,Bv}Ϛz}gp3ñ[v-v?xtd4viǝ,8CSqq Ȉ4dz3355N8v*8 c9Cj'.8}F|}B4M8>'p o3azmE%6|x afvxr,#=M(pM43y6yXxXhfzAY*HDkWTde œ̈2O ?9Ώ>Yԣ}u%dhCp ǰ۞0 y#Æm펣#238rqgΝ+.&aX "B3 #8ϸaqݬ@!Dt}C-_8q$I8T9ƎCoΟq &䍻prqIt񓱱1>m4Mc8߯T*0  ֪r,6r]س`\ll`L&K?h4fr͠noxa9t^)) N"sslۉaXhHX,DTVffjrJmxEQΜ]]+*Wmm))YQ\8F<\x3h4(jD|ܡ#Gm6Rt躛K0\,-AP֞! B[;zԔI.uhRtO@b ۞ h8ĥDד$627_NvfHHpdDXggWK[{rQqzZ+/>;{mO</';#8F-R4E%bH$,VV8N+ؘ~r%ӦNRcFDn0{yZ]S˲Fv}?ĩZ&"$8h`pl6?Q-R$,4z vۡhun].+2<<-5m5׮WΛ=S*pbVkHpPNvֈqr\nwG{GUu BH.S!d4HO?EF\-bHTAAgZm˅WB.Ms.YVV[^~me ͖0">!, _pӴVSTaaN+#=-#-uU[,I1Qn[dg%&+r f͘VRZmnaiVԒjZ=9;tVQBBon3믑$T>3gϯs}_?GTfL# BQ!?sѣrB!q¶;_x и1V._cL&(-߽0B"Ţ_xO?isYQPD3gL۶}W񓏮ZIIJ% +Kʲ2w?D$-_03#G)BbaZy_y#2"<7'K&bK/lڼ7~tjJeKx<^@@ж?V*lݶ}" Κ1}ڔ_?^+oDFEXXQ5;$IHNJ(Z/٧׾s_nYPJJeKKˮbrezzʮO2y)ߎfX!x|llhHΙ5SQ{*>>"GAwc4*[o흎aXvVfLJXmfO0aaY~Z# >'7fӗyؘLTJ9.]himU*}}|~ˀa:=5EDQ11x7^}iMǏtJX,TXdێ]VhHȑ9r|nv>>/<4I1QK.سo'>KJHxWz|͎{w܃Ο'J|ð yx|'75%TĄl߹_vS˭6F}W(䏭^?7".691S=G ۿ+˖{^BCXW`KMI  9^ ZBF(jԩ[u]4{'T*P(x74VU54` 5iBz}fE*GVn9燍ɖd$I{D73 쬌wB!S|>bn޶7v)ɉ+-Dx |W~'{׿]fF< z 60hDz$ԷME)V;/^t?aO]j?]k?{BTdĔ#OVgW/vh]{_3O]t_=s:GzN#n~uH,O:9>6칧i y.^]Cj2y⃮:wL/!aDVV+pBBΟDG{jptEӃ%X e A2X^ `x,/e A2X^ `x,/e A2X^ `x,/e A2X^ `x,/e A2X^ `x,/e A2X^ `x,/e A2X^ `x,/enf^q4)_&U)EWt@~77 IDAT+l`ЈI%0.x_E;`Iث[-e ݯe2o4ݧinkO_ vvu\]$y;˲m-NC,Dj}2"a] #f۶}ܜ y&֮Je||B.KBtQYi$|FSm;ˍM[vNa/^.ljnA!?vقg,V{C"x<ļR~/;`ڽ?4,dbh2}'O\Bq'N@55;q2+3cEMdXvx#80pJa3.X,3;]3j_Nv˲UUյ:q@.UTU۬Ąe2Ųg߁N[o2iBjJ2s8<>t^TXz;&=-d6:|44$e̴G]{FG1ňt=vdw68(h>Jeo459{^y{eK)ֶNbg͜_kmkw\m*_ k4ꎎ΂g;:SS/Dߧ= zfsum픉R)JbܢRϨ.lF.,$$8(h O>'A#EQ+-1 !y$xllEKK9w6SN_}CZ~ 3O=kzEeeQQ BhǮ:]K5J4ҢP(F8D"I^TX\Z6kƴQǎ9˅E-[(o˅E5uvlk{eK=:;w8l"˹!a{z8cG x51QUcppp~~?βlk[3AkV?vtN>˰̪̙RB6rݏ} jiG94Gi4='a('$8CF7qȈ&,!4nY?qk)_!ðӧM v+K,Όi"H(J84r]-0">%9)4,ZBa F%'%)~BñE 慇eggܴf4_QTJRbQI;:NIya1>JP( H$z=Zݴ)fLRpB㸄F憅LW[@R٥U~A$IΘ>51a9d+BT"z0ϔNBC8ð^ qaF )ɲnH0LDQVaa8B.r+ 8Na$ a|/$)JzZ&I=n 8#IyJPbBXa8qq6UmA U~Bvy| 2F_q`E  d Ӧh5_,8}fYwpCX,LO?v`F]xyQADhZmN!DIVilnmkw@ T*jq\U1CveYAZnnnlFYhn|tߜj?/*dYb |vl6OVUS[7J10NMNzCgpW; ?mwh={~4!$ɸؘOoD. !$ccc:RSDurgfNaXTTDDD{)2˖,xI NxzXjx:}A(0 ;,66&;;j@ P&O0~cN߸ 0T~E*L:Ts.b >urhHH||7n4E>Y!%9Vd?xX,[̖sf޶paʯUTTV8n62Z~-"<+ &l`ЈI=qz}v \aNh~qFBIh4R94{hV*!!8|}}lV[hhHk[{F"0 Er[[۬VL*e9G0ޣFT* "IRHh*__}9:*4#$qC㍔$nim Db |}}93L=Z)JB::;FOe2#br\2,$8 ^=MA!iu:e)  zJDegs !A2X^ `x,/ew;˲ff6 a_$ 2ޝ0rxq`YN*x96F\7q}}FP,a ]| Hɲe A2X^ X' J%8#l`ЈI=GUuMvfƏ^1`o>44 A2X^ H?BUF,:o~77״w`8P*C2g9~ a!D$r瀅aHDZ w/e A;_Ehٚ[.XৌaY{8#p$ ҧb QwoI$Яe4.\tpkLS^)JLQo3d B~aY!^}y??]^dleKDGMFa1 (sVaY1 ! piqeB88>4'[.WuM$ȚںJ}racn6E8 H@y1 EQaCcr08cƲWmJ08,㸧eSg:V,_"erг}}^0 Xvא$ayd2y֧|!T*n n=-aX CC^ þݤ|Olr\JOaM'Oy׺0L\lѣ{rs^r23ϝPȫkj3000bDe˼nT*ϲ̕bՊoس@_.ϝ=3%%' FcHpгO?־BX0Έs.]Rxi>>ʆ{Z\*?pGF̙2yd~/;zdnkƎ=s v e2 B`<0LIic-Fd"$6~E_UuOMJLx|JR)%aS&Odo0"<,?`ǮQQM--GWEGE.xj wg(,nڼ%++㏿gq'N澾cO̚93O$uѣrwF=r~ >n(Gi~z٧+**B}߿ӳ+%=Zd6?xx̙;Ԩsf7f~rRCMVt:{3h6[,yyccvq\}CcYU+We=U+2;;:rd0v{GggnN@ z y<O'\.BGRTrrNi+pM-W˯]-H 4-u ZᣍMM7Ngr&'$)-K}CcWWG[Zi)-(0GxfWVWg1 sb9z¥˺^WbfYπwa|bl}uC ZԔ$Pzht$갰P_J# ssj{ʯ]?xlkhx+B äɸc23ڽoļ羞 ."y-!ݾqӗ)2GK5U+۬v$%8F@(T(䡡!r I <,,n9IiN  -II aS(J!Qc<3K$gаP^k.-[h >-qFD&^)IE!8eOe0 0c8EQJ$IB>*7AW{,0 I.K, ŔPrrjuu>Zr[mqcFGFF',p(̌7bqbBrŢg}|Rn/*.&BDQd6sЛ~2A\lLMmb \ VUW cccp 4a1"#tſ{:߰d2xࠠ'[#I) p_h;_B!cH,+ ÄBIJ"3#z/-5EV$㸧 1q<59488'޻/ 23җ-YZ`CGl3O}l>8dBI rs=jrOysۿw8aaΓHayx.R|%Y31,KQ2t;wݵgodDĊeKx<^\llaQ/>%AH?ð_FV0eF~Z=tP8ޜ! P߮U5qqw_0# w֣* ύB0[,Oz8cOOfl <৖ֶG}VT\R(Hbct],D%Ξ1=**ë㓑T(w{_,\n"rÏX:`x,/er;0n;f;pH{GkrV}zRf;xh[{nT{ܺ߰m.Cj~LNhqu⥲׆?uy{kY \㸞핢V;#uItE"$"&&ðM&O=طw8._  M&s^}~Cpx)>.6_wԔCGu:L&7vtnvEQ!|ҫv=88xy_*8Aw&S/**rqt:K̟p8N(IlhO>S[[0,=5eIJXvh3Oz(pj_̌N+EuXXm_kيK M&r9qqdY,655߸4u^+ٲc׬ꎋlO,;Ӥs?qv9vO2~ζsҫWBC L5+#ðήǚ[(J8*'gQbfmڼ5,4d왡!!7:?88;w Balxj>ژ(KO:m'M̻e-6k.^ثן8YPW@ܜqcGD"e˯W*8?q¸IAWw=:݈sgxw>uiSr3zDZ-m+*g|r˯]OKM-00xdc>Q"e5jB(/Jѹ xE>]EF ^PP``gWwqi)aB0+#cq2 (F,VTV\^љ7n\. ˲,r8aɓN<}mw8G߼?gf-ViVؚՏ^գmd{v / [Z;BCpvM|֖=xo9T[㖣lhbeUɂ3ƯXd箽+*=:=dڔIk/jC8}__gW)bcΝX[ȊeӧN9|XeUl3OprqI)q[kᎆ IDAT7<1J(]d2 e+_ҿyo6?Yak+o9?~a*_ߺ/n_أx]5"hļ' ڛ;6r?y B(!!olSԣAqWQUs}_gI 6"Κ:e_N4,7z+ƍ7fテW_' rܘQsg:zDgWדOJ$%eOzU%r2iy"fgА]o/Bp\T8sT@pQ3M%"<,M80+/lbPxXhKk#G/Gĩgܲr]pnO*>?h4kzzfsDDx}C'GΙ5C \tc,_3q*۽xX,喲k#Μ;?f(HT]S7^VQsמ^}_晧f/^r~~/6omhlDSL4a<_aSrcW\ѺϮ_d:66f4C/7o\xYk+}#>΃ʪ}>˗,JIN)*)}|ꔤDPHpBb?q9ٲlsg-' _d1[23[ZZBO>iX8ueݑrB}}}8{q$ڳjH_dx_۵n=8͞5cڔI>y괧(@0cT! 2ΞQ?]1''kbgK^}RP(8GVfzgW7_N8}D\\tT/,*;a!riB4M8U?xZN* ycJ|aYʪE)J y<M2lD2nNgf#X-)ZZV>g֌Prbbrb"q@&-6lra8$0VYU{k4S&Oܲu95jTv+*.ݣݸ˖6ġܜGW_mݱA,L;kυeՂ]1B"8̜4m_{צ6miSx0Qe13V2_8np~t5ܹ3s=332v{S}9.&kwW|K%?z8%9l7>1~ؘں{[:Ki4ڞCsܕ7zwiy9??oΎEfyftlE{:Q|4.&lٷ7;⒲@ޅ[SXpt=$.]۷BX,z퍷;{x?765KbN?uƦX$I $Eutvtz__-nxt/|*ed[GGlL1 h{}}C(OLB|Ei}.KZ\gWϗ{&<,) ?YGl8w=U??govm}⒰zYY#v@;W5Sϛ-F;sb_p8Xܑեji(:=3+ɼ?/,jfnJUoEB|ܟ_{s^斶Ajh4u}c ?wg0NNo(*(0p":8N(ҋ/ 7~+.6]feUSYU}׉ q.}=Q.%Wy [aD,#|mnj6$o>t /Dža(~/JIA@?_MMxU~;RFF6mXk~oG;G$ A_#r|‚}{ollB ySL&cߞ샅=}o#GFǾ/ Ʀfu(Uiy[LMNꙜW,WǿWV*%y{_R#_ko399qbtlAy\p8C߷]7{|Ʉ H&NMM+ \-eך˅u(##^^||xdO$⒲ HlcGxٹM+%)Io08N,]N ZZ?77^"bkkkaaCr8w?g0x\.xegfK*{ɞ}Z33677?3;j;دnA$KR/ɖNO$ +'ʤRꥵm 0_H< o~5;7JL*;~V>A|̌C;k}|)p߽;?lIYŕ⒰mE%vW?{Iu܅޾쬌W_+<,:Iڭݺg_Xoږv[ ÆFFsS/-D0 5 Oϯ^\YraSO>MDQ46&zsSaآZjm(.)l3t/ϳ;'(Rr8e-mm4r=sr8ERۭ ÐۻWLNLpxݬMMdeTsMMIVt:}( ?_N;I/\EXg~0?D" `0r}[_O߽ ( @0~`ݡ<o2#cl6 EBdDx7uG__Fb%BLMI3s4:M,8A$A~a!74$>hv}n~>#m7D++iXgfc0=Հ''&0ĄUǮ';i^ZQҺYZmS t:d2;/PIrfnhO'fj(jΐ#AjyNhpړ3o2NL&* Qln^).v8Zt&%&(vjlXl2a`hhJJ‚`u&EQ胯-%uۼr=R(o\l¢g0uSx<.ۖ|? HnֶErE)n*\L*=t`J@=!Fܢ(6- 淃:ǣ3fE?_$E%`h>円?!@Q-ղD,ra=FhteZVD,p@aA~( p P`Gp¯A|s`2"A/+#f_)._@QчHZ4C!i$@{=\G";]Ε;A UVd1? 7N0_YLrS,r Pzu",,thx̹ <ꞐB!q& &΄* _>xl'{nm |n,+"t: eD#=x<__Fr8ϗdFtz 6p6[b?iph`y fH #r;F#`0x<3p_h43O>n49wry{+޸Nn5KF~ v PEEDk8\X/%9qmѬwlm]Gn{x|B, nJjJĤv[i9,뉭 4do4[,[[://t:0ðۮ}{ACS>nSqF*1!^XАvͶ} rlLLM;֝r\NpP`UM嚞Y^^(U$ޟ[Xh"H23=-1!ntl H6eZ&F`0AC#[:Ʀvrj:<4R)Q-)L&36&څr,,H<.ϯrMNNDbEQ[:=FۛUpmxt0rM]J8<,t}crut sWd6on/,\/AL& ύAQgm}}vn 햎fY6t˴U͚CV70 GF[mk,lnhlr:Q*|{8vy9 {{5cf4itZDxXYe2[,@q1;XX&vVRb|Vf`lvV:* jlv{GgJ}GGX,{JIh BQT\!Id2Y֕U^#(/@P xQCNZ߆6a7+2AoJOӢTGp,,PFFj4ehni3L_|Eu{G[V~Ё[qqQZQ+E$-(\NL[j1Y,HDQq|tl.mW*RȈp}u:hlikOJL&'=̓Yɉ 2T.-, #n;pc:iE\. knmX,a!43١ @tpD"\אin y˛b668ziSb`^u\edDyUg2B-tlT)# <5=cZSS |}$IKE$I޲\.7.&Nlj iݽ8otL& ðM(Nd "?mfa|榯u9F3L ךg>A!!x=iNG}õǏBϜ;_QY5;7׉n90 G_**p LJH JZP?xijjcGT&70x%719}DZ#(B^^~:2rh( Ex<AS⒅EuHHX$ TT|rtNHG|^P`@?{'OWD?rNxXyǕx%ǽ|ZjrrA^媮?XX N+f1YQ*D"q\.\:wfK߽+>>>ފ/N.\8{ B[!ozs/C#&%ƣ( X$UFD$UT>{~xdTTJ=y\wO_pP7~JqRhp0Nݓ;zb2)@wݽI 1 rU@Qm]g_E)~ ob\NP`3.^.I -mա! w8.83.@ 0i&ZK'+ru¥3g/wu{^IK[{LtAg pb/,,`2Nk``K"iljؑߟ.^.ZYY}ⱇ}o8h H*wO))Huꃅq1|&2"㕖Wr8}Cl67\k;S/.4ړ?ϵwNIJLٻbTʪN:qHxX{j^D9{0 .+^TO=br!2F\-)?{"=pD@RBtPܯohǎ-,ťxkbj*7g])6say rJR" 0 GTe];z(",h4-,I|wߧP旖Wrs{/MNOH^QJeCSӻ'Owvd.tvB VE9duuwߞl\ F;c9{;:N9711APvVA'Ϝ8z C^ZVȃo/Aa!EWKu>p=rj546=|@2E7^{ԙ RPX\Rv܅ٹ̌40 j(}Wj2I>jṄ#`ssATFFR>| 8{׿D |٧\h<{a޻2GEj^yןSw捇Mm]CYEO?晾_ge%$e<|,|vun8WTϮ_FFxyPY_x S?zZpc r5}w?qEQ49)QᎿG ++Qɠ(JԬ?}`Q{HVV=ٓS.{Ąںv]q:WlАd=0EwLLN<|fDFۓ|snJMQF2+{GFCB3vAQX_`JsEtь޷77y ,ފCCFp,VW5ŵ ⳟlZM#c0 C 8)!>3#kN9{$pǎLAQ4㣍6f4$k+O<>qk /8v4=m]ܙAĄ.jH(t\Rb~7 ;:1 ˹\Ʀim}!R׶ZmͭmUյ g/}F(8ֶ̌I}ߔ[APhhHheD'&ǩh^7FbeuEane^}Gܦցa&M|lLV^Q537d0Vr> ZZYZmb(mwgj`$y?{yjZR~Ёϰo{PaU&( 'R32 #= *tΞYY]=~HXhmia` $6nuv$%DήIj2/?@33]ݽzXGg7a <#}\&++?53ktlpĄxApc2I.80pظhvJ1x^(m1[k9lV~~Nshdthh$}{GF[[VM&JsNoll;}{q`valTTWuDlZ8x_).YXX|чMfsҺk>>z?nwaAI..{wlL([P q]}P(HOA`hjnؔɤY"pfvnbrF-,9lv~^H( h~aQ7ttu$%xh\\.H߭G}}*IL  LOtwiedj _o~grj:&:ӏή])mͭm 2"O>X,nީGz`xdd6lllV{mR/(VxmuUqOS~+Qe4gcc\w{Gq;bbv$AI f5 Ur\\2261L j5)ˑ%=}}44>1t:e2iFnVVQi!Hr9s99{;:[:TH {;Bdõpڶ)de斶(UvfbNel 5 iN[Y,.iZVW5QQy$_|=ٙM-QJO?QQU?0_E%q1QaaN@)I:Uo0fes؜U\6;;tzP(AN/)+_^^y^^?0>> HA^T&p\[:ovfP(p8m3<wKSebI\Z^6~y(<\l} Q$ IrUFĽwXլwt?_ܜL& ٹ>ŪTFd0e746}'pwa^ {phA(juKkfJNJ-R/ AH (jfvñ䤾'CꞘb[[:m$&&wۛήFooEP``qI+q. sl6 `05hքArRpDxXo@LLTbBAI 1Q`819=pv$QFUJEtz܂j R/[^YԲX]JeaãN$%''%~AY[S ך~>~%<,,4$ ѱ &Q\Rwss]}6-2"";3}[I"+##5,,8Xb654dR??+EWŅE5AkkǏaZSF%HIEi;:-Dl0Qb}t:M|`nojnnHniך[5D3Ej-) IrUR\ PDxXOoNb7||cc^^\tutl<:JxiiyqIidDhEeD, ևryꚹrҕ(%.)-W/-[޾C r <.a huz}\LN3(9]wOq|phҕŎ{z"3zzM-uH&YQUpiff>J4- \CÉ . Nە H(ܝlEѶNR(@Ѭ8#JWVN>;RFr2fWVtz/^.Z^^Q)#W*N fs\lgY,f|l AOOm6# VOPFˤRw FGB (+TT;:6^^Y- Ckmյffvj^RxnkO@d0LFll4Ùlm]ǏI(=Y$INNM_j 럛_\R<>1'֞>P ˺{;։AA60=3[VQf^ٵbmqfv~`p A|`DVV5&9.&fccs8MLXW_K*ɤ0 XJZ{N ZF 57 Ps.^)ZY]U)#KU8o..߾[Hr!)a;JwKǏll^YJMa0ᡡb|nijVb0׹tzeDxuucs ]62dNLN$maqtƨϜ(@Cш0AE..)*ewOt%2"ግO씹Q\Rr:UȞk0 Q |;./:JIӻ{O=b2:jbU)#FFk etMlhljt\-mWe2ixxh[GgsKlim(x07d0WK5kk@2畔olln:sniy%6&fw,㚵+%"ªkꮖ[=ֶ P44<(}@Q j6b2 zLtP(i>ލM-+tM ໽/\ pGF{彽}Uu~>rv|bPxUICъr(JC/_)Ԇ+##ފAxa  ٛĤ p=t03#mkKw̹9oBHp\!k Fgۭ\!+Bta/?WRRV]S=wbzIo0< ??[a墫?wCC|ݍM6 ~>bOq!$R}{%O~paL*VlP5͛ڭ̌tG_[ذ9i(z/$'g/F[Johڕa6[VVW&&v8΂Pؖn ࠠd`M&\LiX-fn1rN].WYEK/nuP}cӗt? y9{K+|_-$Qjڦ֧xt "ELHpA "|~LtTJYY]㝛K"hS'+aC0 CPRBk y8R/ǏB@aAPPhl[$/8vDլdgg^\_~X5|_0/&ZwOt7eXV"y<.f\NmٟK?3 [޾ b{́|wa֞/+3}kK781rjJOەa0GFwT66{D"FisͦisԉcGr jkR΢h&ZLT=Y\gdttaQ?_/ԋb#OLR<\xb|\VF:F6=3d2AЁ‚!h::-V+E6[ˤ{8/-/ƧxL"$΀NMN鋍޻'fMVfm=4$ĭ熙`08lh_\.*~{ϝ'暺_GA$Ij4kVa$Ix9<"lov 4kkÑeU{OڮN_X "IbA^@`o_lˤ0ggfL'7q;""|,:w25=  IL > ILb |^R^]bolf1<bybNr,?7G*0//K_}:55oS$93;fw ?~a`xĄ@o}kvb(rY,0L! `4XLPðNQ 㕗~?))Ǐ~~n "alZsKm}YVd2['O$0a$HCQԖN0ˑJ r.s/OMX-A1ǻQt:KTN0PKd2ϯ;bLfaX$\j fad2t:I|:`QE$>6&>6f}cszb y<;m2F~ $AB`~z?9s7NrRcGN˯Nn5{0lv.r`F`LJdDx8 z~vn~!rRF1;_vX_/GF"h4 Lle1Y0p:ϨovA'IW^^^:$ l6jaf{%7I"*p:(tx~al6oPel$Iravbx+aY^^u%Z( H$,,/,ȟ^zL&\.h0q$InmoDQ`j!Pۛ`8b\`2 Ɔ; A0 3L@$I\wNX,kkg,&K!Cfsp#I`02,Pð/ ˥nzjQȥ4I2d29 < \guun_9Nw\&Ol?O>P`8ƌ fS[KJ!H:tbLW~N@ #~01L@PM۝ᡅ=x/F&=} lˤ>IEMMόvvu{X*APE">l pmx<ӡѬmj$qmvγNj9bH&zF /_$AB:E$^r8y{"~$k->ށK$92h2͖Vfmlne2wei4ڗ{fn~7ڻ'K OŒ`Ȥ^w?v׉<q$QI4u0667W6B $(񎣇\.Aקq~pA}6?ZŘLH$\YY;/\L&;>6 &TZdpy\H(T)=2f + >p8t+A%^նr_?C rh4ժx" IDATp:48sFܙ_|EoߟO('Œ$a02cGq Id}{ t i#hr;6>%4 X,A<.7!>{pf+%9`&ilv;Fq#|d\l INoWU_\ȃ|^^Ur"vةt:]&:tp+ qw[C<̥+ Co/ 0 Cv=V[`y' ^}ͮޣ׿Y$I_ݻSVVL&!IqP}`KR~_ p8L&C.a࿼qgl8'{؝3b1E"ziLr8+YB@. $'\.6NE])յ  nol*tLuFPT$.vꪆX,:7 y__dB|,As:!3#0fY,VRbBnlkjz&:J8A6dX29z 0l,LOBf @7__?%¢:%9db.b4P°@$E"!L5=3|c4h4ONMi4Hj_h0䞅&FF77^^ɩiw A`0=*6##c:!?/&Bo֝N'l$I.--0 EQ$pnۓ ,_oEeU} :St86NR (0 @*^\쭐oll?j(l/NcS3t:o0 w7^Zfh4TTFF(,ba qqЭ{ᄈ 4[ 8N446=Ԕ 0__E:ڪ3`bY >G\|u|bfWq"0/w.^)D&`0  ,&UMA^. AEWˤR vG|hzl| >{w*),+V!DY{eu INK$ȤR:~;**gft^ISSkJJӏUHedfkUALf|\,Khn~!8(c嘆 oh66>aFLtTxX{~Sg_bY:b﹀ (4Ġ:rT =MӃINJ,;{#Z"P u訨Ě;H2M qSS."{)`p8+/-?y内~))9\VpNrrRdDW^j4M$T!O7FFF0CdlL4Ņg*)yzfɤEN~ʹőOb =INJsV3|iXZH(:~tJrb|\lKkr\ѹz D"FVp8=dˉKðU ڽwvvQFYA`Z]i4Ar9*UQaACDRFGEB byّw.Cp~/s2bQ  $IECh!:*˃j$!zH$ V ׋0,t:H(TT1Q$IC":Xb00,BCArNk0ժؘh\&`NKI !v0FT* "!4j5 VZ`0Q驩QB`lHKHmjuf_0o.ǣivCpܘ".+z=A èTȈpVr3"~EZ p8A鴚`5zHZ)!DYqqF|>I"0$L&IXbЋE"ZR*q CL.3BHR,!I%{׭]s"BCCD"EmmSSSO{\&rYDxEQɆ%EiH$bZEӔ/.,Daa!2,<,B {D"BJDQT,9Y+ VhZM0E4M PA" X ryj2)1!Ġ&/]]aV/F  ;V=Sx<>_$fef$΅^EDEJm03ӂrD, ExX(V |lԔApO& : ^V)y˝k^(EE-41qV ᡡ@$'%dq\ktZNˤ~_=|ޠP R*|ÄȈD"I\^鴚`\jUnnvDxZVQBrBC2{q A|>/';+-5rUJ%nBCBC050S$rC "H,4fhT&HCb1 >bk4$66=xZMJjcp\hKNNM0!0 z0EQVҺfj;jBD܈`50kϛBB $ήni♧d2K$Z 5r)J$]( JRԠ V+ɼۛ@ZF _CurydD'm-|>a8.60TJJI~ :ZR*@ l ] %q|\,Wv;P*dJJEqaADxA7l{%:QIMI}!xCTqQɤyٹY\.vEG ^c&<,~0M (BtP<2"<4@ѴX,^ƴXMNfAl*s?JSBdXĄum@=.+?ql Au'O-Y`z Î]{q³g-#"љQ89{ҕگohkxYt'?Cq1e #x^uRT@غcoy{A\=uG Oa. |.] cq+255λyѠ-9|~|>ߡ#eA~w{aXQA]n+WoMx;w?Ժ RsglIZЊY4C &&/]< A(]^l2M?t&PZJraA^]}eKbnqON9r֮~trrtEe^zu.;>:4<"J̛;H(NNN7~ Hf]M7n%&aP$)}3]C܅t@T 8uV C9-_6Ψ⥓l*˲23 訨Fq]?g;  W,[zWzu# ?oْE2ð‚<yFt}cSnv M $Iz<^> }>U90\.0z= |>>$?e''%"nχC\A4M=C @a(0@ >aE@aXUu}_}==[H2;' EQv$IDS(I "*EQn$IAp;q 91@nw@ Ym6ߩ%ES4a֏!<֡I.iئ%R4aX$ a/^0-Up8wt9_7e2٥W0M7?شeci7aw74ʤ2.AQ |O$σat,(r:]_;YlItTiI E"!Ղ>!ADDT$~q aoʪe^'KW"\n7pK&I( BNG=^MEP;m\.W${Mv7NӴ|IQ*2L|]E)vyyfP[oi v*}>܋4rafu{zedH$A_()-x陧x_W_ s\~@`D cFQPsy\8H$BQ'(r3 >; lahr9|i˅aAl[v{hx^W1 'C4 4^i!޾Uo'7n*;v<,43㗮MƺƢ|n7r9l'$IM[}u:L*; +fM7n\z]#Xpl>p7^yY.-|Dv$"@(`?$I=q8ݯCBg+MQagvG8j.\jni}nS*kϾ_y9)0NVfE N޾[q?~L]}a:ffRFӁCG,K9zlhdEдԔK$}Ym6@XTwW^~)1!{cu{hiiiZѬzdedD}{4@vVtxvNA]=}" eR7lyh\n.;^iQa Vx*td/ĠtI41aF`ԙ^GCCBǍ6o&Jq99%E]=^75%V EsU+*yɃ{w9+>_$=SaafѲN.[\ajEEFɍFSgwkO,YlUO?YYU]WR\0D|'l b(թ)INWtu;:6vѱ1A33Җ,^(H:;x"kDxطjll3Mo۽x%EŅ߿e΁A/]RT#o'=} \@inmkPtj5`[wqeNIў}[?=2&9e'ƍ_{-[oꃿbcb^#nG.;V~"%% /wێ]=)#V ?bٿ{n?SQyV3I~zjk=ZZi~CkV0"ګ7n6*pdx䃍[}>#,.,عg/{zommiZ[v[:vUW]~V)srmXGG6m| -۶/\0?/'TYY<##}ttlppxŲv󇇅"æ#E~_x<Ϯ=W]gw[JmɩАOH_J$7nުR)}kΞ&~XzCqU7o5w`rrJ*XEtR8\T]sf~Ns >Ou ̛S޲斖/7K/(ϫp+>_$>n9 IDAT`50YQYU@Q"( V-Vg;HHOKr8U@Ggo֍ztEp8˖,;CQ9ibb߁Fl3"hqJ J [B zL?֮)=}lRRC+[R9n4"r޾~܇DE]Vر{6i}I?SZ9Yk[ \D146:БǏ\sDPOzxHY_,bk.\NKMJ$QWյwvegf| }FKk;_S+r]nywsJ<~LggWLLTGW_Аo|)V)/<^"aΟN&BC\v M))ɯƖ tZo~#en[xA^nM5/\`j~W?wNbb P_+Wj&L.kkT*:l#cGFu7p歗_z>2"|=3 1yɎή_h޸y"?m54--ٹ{odD0[,'N<>$0aZDGY4aغ't <|+/<ơ(jm.Z4ƙʘhHtpkW~iO?559IHSol|ykKk{hHRx4=ɉ 6-4$gwvv}q˜b]q6ĠIe~ϟPYU?o*7Kk][|R$\.n"HdZb="#U6{,Z8ɢEMq1sKK\NqQsI}Byq/aW^~Iռ,?盜C499:<2.8Na~^Uu͋?`тI U{*sUʋd$p<[odfM>5%|I9Y{J$i)^wCW=S]]sXybbbjx,VeKg{:l+*POr?dRD,~drj IJ J%^'qYheK&&ys|>БOLO;~o|;EQG*x}pp|Ddw~g;:oܼbc]nUk~[Fi߁CllYTr2ӆ/^G)۽zqiV AP͛/>LXhȦ-:|z7UV;5eK;@dsKBo}]PG4K;~7([og?rY[[{S‚KQTo˪k./Y.(DM[{z^կ{84s`ZZ+-8[?^kh υ 7o=t_ wu~+/ޕD,R(fefu痿~hŲ9sJj^qԙukde|ѡ?7o5\D3/=jА7oX 'X4j"htlh0BaTd-;}=B! :vhhq?zvvGylU㍛]==i) uQU 11 ٰCnѩkGFGnWGW!tff((rGaha Vr8T"J8z6?0@QCQMp0Al^'=-妥"&SOo_KW. ^ FQ ? }.+H.G Q, 1@`fed '=-rђU([}} .?1a,H"'%[r}  j c'N⸏i6ǫҌׇ{ aXNМb=8Η_z-bhxo``)FGGA ?y Eбqlyj<.h2utvd+:zz##?i#G"3559>hCcSWWwfFZJrj=uĄ60Ay%-^~A$  wtv7ݸ1eS$xbђE z]EL XɩiҒb/.,75%r}B뀙ak;޾QR&gzj MOK)?yj(zL ^b(2Lb822Avz&(&%$jnv.--E L ._VgZ;:>v<aa\.0/{ ѱF H1?({J=b}R.Lep8 >v"QbBm44:70E\(p`phf"Hss0t4MSeXiNJY(jl_YU]4f4NLLqctT"(x]nR p9O826nlikxVT~j̀I)2&DBaxX()ݰy_ST~?)oL0LDxXJR" m}Y,V SS̯\.w8wP:vDTdNMI!a !AYuODo'BN'Bq8F "軰g4M3 Cp,[(!>tE%IR9ٙJEP[[GaA`o_hnșB"$%|@~n^&PqNx-6 ̝p:g& G@jJr]}c]}㼹u:^wj.j4)II$'%2:2:ZW46nlhlq_iquW0e`gLI,_`ԙ$E&'' @ V<٬iNaPr}>&N `(*𓓒x<.0"TR)\nxXm_ٲgAG"a) 0 ̧vLp,Vq8\. p?Ien I>$E}V4`ViyR\u A4Mі{uJV^v0EQT>sZnEQgY' Eaa(fbۣpOMIh_hxgY'@dGGGNNN0 "V/+v{9`z񿽿 ..v5{0 w>s(R*SP MNNd`(a} z_h YG.]j0C404M(_`i$) s-uiXb>r0v($%<@A~nP69|34=`i.ĩLPb/\p=Cc0 .wk{UU?ixQ׮ѱH>Pvs4›-I 7^|hx$"<,7;`0Uoپ7na8?7LE qל⽽}ya3oxr\,]㾫v:FRrܜLN^q| bBJ  ;RVn4p|ι())x?A :}6>.V-kuu^t6&&3?xԔqcFzZA^nBBX$II&5 # ! Oq$IVx=55tC4p.Wttda~^rR\&}};].nͽ8uΏp|\㭨 ¸ز'|~zysJWXz\5k9ŧ+*~EȈpEjUa~^zZJDGuvvOqqZMظ$ "hop\E{y;4<0ĄY(dg2SrynNc]n\.#ࡣE%E.^6[,24>6Ƌ{sr"#i5 7zsSRd}?rB7LLO+?yr54ݐe6aE쬌kuSQ((AJ+nؘ-yȈqottu:f, Ay9\ðNjmo'I4a 699=eRidDDuE`h4j `]aXӹֶvutEA~aa!=C$I&2ظqbt:B Lf>w~Z ABtN|>?7;kdl,+3#?7'.69dP)-.zz >v%Wl;44\iq߇V*JVT(ZЄ˵^A"ĢܬtMp0 1;&qP(E q܇o p8R.^`܅ 鴚ֶXznj x<##c0:tNUT}8(J<ظq:ѱ{Aj~P295RW]#l|?A&&lxV4#a&鑃 ERW}_aoF ]==+-}x}}㲥kG{8qLAAq:[-˗. (&:jh|eK ҺaW쬪N qqr,*2|lrҕŋ(s޽vwy‚|@IA::wbxgٛJ"..63#[,?qI$SgwݓۧD_tym'OW8CGWwa~X$"I?(Hlؼ69 1ju}CC+|~_KK[ZjVb8|ؑrX<44vͣJBR8uz=V*A1QZMޱkOEeՂys].ל⩩ήD=2:mwvvuABCC9m箆‚˖rq/i6_zYXcJ L承}㑲Ȉ yVFzK[ol ݺc׍ #!HKznublp:ZAikXtqTdٺc׾CCCd2٥˵)I\g```jU:;ɤQ MM[ع{"x`H>sޭwVfsKQɤm;vW\78aZ8nwOZHKpaێMM7ZВE B zrrycӍ9E q+W,?xxמ}Wt^"\\ #NʰԮ^0&|HYŹ8۝pݍ7.]? "#w/&%&,^81 JKMxy^_5MƗ_|>2"D'$W\غ}׉Sg(JNJdjV*^y%sղcϜ={0?2<|hxxi<>?=-0hXElaR_ܶs7EO>֠3 g49? EJb`QNX"zvbhtlrƶoشuh WT]|ޭ#eQV˕ @ 8x}&''ϝ׿k>>gX̛nkHOMIFQT*8uzǮ=**䲈Hf }yvuz vwKƶsysfys.]7<|"_|EN7wt{/kyse'fslt47W,[mqjh2Y,Nq-w:sUoim[8!`Dݞm;wO&DKҦ+/\JV*>RV~RRjBJeǎؽ|Mp:"<ܠ[֭w^^XGRTA^:՝5n465,,SRqmniB0a /t{{sP);^C+D"}?OLY`Ouw8RV~>=5噧eohZhibfgvLI|;Ox<2Ԡ?yzמ%E.g]>oll\*dnGzr 49efEٴ-m9YUanwqsRbbM3\ywaλ{lήw{Uyt?)+~^0L'᷿ݔ/\Tv'HINy.<11}^TتUAA3ӷ~a? J]Ccv.KE^B0|m?n4~?sfK?{ettvmܲ?I ۺcײ%R?ɩ shhׇaQ~eoW}Py): ?nSP~sG%>.٧ ,lwOlas>ba;M}Z#`)J 0 nd&/'.H/_;w,?]b +էtɩxJ cbg:.bppIR&'%w_"(`iD"˾UGaN:R/(=}ugj|s;GhdҀZ0w`' X @];{ @}<ݖ' @L>b X4n, @>K*g8"  @eV , @f @0wN IDAT̾EӴux`0c6:g0俧߇GR,0 rؽ]$A;>(AYf_&IN ~2z៴?$)!}f0a|1eH✚H}`bAԔrL} AӴmBFgӄ=? n\`yt٪$vnnAJp?i4>:g6%f+b|x`_~]!uh3,1_pz0xݮN߇B䎟imlo2(/|{;IȂfO,~VV|$Iv{\. ;&;Z.q98^kmB}zS]]TbHCKOEZ=}c#n'b=\9ȱގPil82y?bdz8l$Ɔ#CƑ!B^K+5a>9{:Zq A <;f6 u=4Ȇ0 v䑡NY2H5>|to8c[?H"Hs9Ox\$ !᳘gx?#W ڥz鞳vYr6^9o$zۛ .wb|~Pr~_%' HLrdt۹14:nɚ=/A$Wʹ. XLc]Ms?z?]p/O d5}djlnόom:cZ ɕ{.6$҅nq9k*? H|-FzOۚ7o kLR0 3k3sJbnGrLٲ}!RBԺOF@WZkr5v/3Ct[baIbsj>wlf܁r6ӡizMesev8U[ypъVj6U?XheCIâhPH>~p_w9WO_u `uܰOZ\w%)#W"jCN{}=` A|xhh3O?~9ey:.K3*5:ljZ}NXB@W[KU$50}G{Ssbnd'<&7^{22ՖS0*!tEpTP,1Hv:*]\n#d>6a\ybcO߸z1H%Q l.^V$ƥf": NL۬+Oh -F& u,\K( vryؔI99 6%a`w{K}-'eiB5:;>:&mWr^OƦdĦdFZ.zI6/q Λ 0ħe$9S̢Ј+5=(9PƯVq;a1)xjZ}iRkqmAjt^ Ìvl}\>?d!M,% n]\p.Tw X lnrN:ǟ|Uo[3|(Ho=>ֹ1iQq.%emՎI[!4p_ l)\܇{/KIP|{UMDWKSk5N+i6NWXc*!paW([jAcmoz\"ijmq)jiRkC .GPԇ{v9I\y"X[4e5_k0gCPwq៭zwhAN"EwNǽvَljmY*)D!AߖGAT%\ f߾{͛Y"'{G.,*(A$ w^^%~[z;Zre^ڠ7;>lH{ҳ_ݼX$u!âOˤ(jre͍֥yZg&E"aSVNiupoGTJFL˫k7ƢR.uO,N/MC0䴮4}/ 7^rZERyIUD .]q:DRYz)fsy~nC@wizifjv|#zڛG+4WίĢсgreq%Af~1m*SjF[-) 'WT Œƣ4835p]cJG%MSE#7yMxa_9{q#vXiht٭~{~j<B0 ׮[Ǣ=Ms5 ws|q]8O$+ '.*Shtg05Ԛ;*s's7W.x䍦džrh͢5R A . WN\o:yB_{iUQ<ðƘ&Wik6=7Z.W7fdj*,)v[}㪥4#yfl(u7.5f̌ JKa)G@Gnt$$p(SizflvJoljt, 'fK e5 %"AQc%3hsinxt4)[!1ߖ#7g1,cщ^%Y=Uir6e|q,QYZ]f+]bKJõ4@gTEZoL_4֛K *~_y̼"Ã~_<nR^f.LOFABu?[㧎{nb$Rf]oiڶ4pq4K^IULĽn'_(fsȔjHBQT$bsyײ0X4HzB@$XJ=lcFvr;uFJ*Wg+˦LvXYb̰Wx_,EQRsVNf^չ>"0 K*)ݘq`Jd-KchlB(Й3,ř*q曺$jIm4fH2=6bm!`'KY,6E-gWu?;1}_~T #J I$|D.YH,Ii7֕YR,љ20@%!Si*[W?=6f&1W鍑`pnrr6%nzmP<*Oʵo[a xX*W `^y>=#Xl)+W %2=,\oS RXVfqvqiy26= ڭk79}HXkJ`pnrT7C0.M f0,+utcFI*i+Ҳ6lh mHϲlHr\՚T:Db=k72siPu4@$&Mx)=AoazB"WB\jSBzR-34]v]Zv~iM C~k74Kj굦t{ڛJke*mfnX45sV\p\)d*({Q؜ܢ~θs_3R_lSL̜'"XꥡH尕ې#˃ g " ɶ<2): ؖ> ÀAt4YlA_$PE&(00 cx? A8<ӺF8<Dq8\v;9YvaD {Z#MQ@|UE8M=E FX0 ޸ba,' 04 u<.Ţ~%HL$ g3 TAd2blmYkL{]H$I/ gs(b(z4MQ;)x,py02Xͳ2"7G{)$#"(qxŢ.o{k7;2Ovjiz;2A$,(tbH<xYlEQMNc1BupLrSC_K|6e|[?H?)gy|A8 ,6%p]8{E9c3 ZH-CqD$ZݾD/pCk M7[ v_nj}!A0 Nt4.' ƐAbWWN_>y0 ߵW,S,LO6ؗ's':VbшaKf(,(cP #`"E"Q_^E@( YahIi h 0sօh,܂"~-Jt$pg!!뿝D<0 bcoH&#>~$(`Mpmq2x=|$ž恮vZ+U)I 4퐂aX D#a*Uލ4MMD#>yܫd9fB޴'ţ͎F4Mq82;1Wf~j,&6UPzqîy tfbsP4p|flvxE&ؔC'eJM|H-AP,#kβ8\Q]}wO{@`X&/--w( (Un%Ȕ(Ҳg6lGQTk.G{F1ҼPh > 1 q٭Y%<(" ezӴDt0_$bKŹH8%{ܨ=5 ݼ69< d$˵v]>q(FAj`L}o< Űvl236bst_(XC}[`ȕɝYg^}~ϻ,6z.gaRM&TVPuWߵKsSpXcLSu7FGm}Ox-Euwy`$Lo= `RM_2[V!䖮x .xӵ0 ]raFXM4K9;>b,L6K.'Ki#yAjl--anZh &H `8kmg{M˶|୹ֺm*FF79w鹅E6nIereVovgƴ.5֘F&]-w%KsN;9XH IDAT8DR GJ_|/>q"!6e '1ْ1n^,JSE%̼".Wh͍ xv6_!A{R$i[ܰ-ܘ\0_^t#,6;3Xg(q:ab)"Sb㲳9\6'uP㲯,%"b̜ىacer#(\3fXZ/2gf˦5-g'Mݶ{U Õ)ՋӓɝF}ˋ$ǢEkk4Fs~(nثt\>[_pG_\I\߶4uo۝pL\<A__,3jH*Wi !Wkb)KJZgHϒu\.!Wk"DL2R7fd'ߔ(4:F T*UJg9\Bc0rAUZB r 閌BF jH*1]$syΔ_L(k W2Ӛx|!rJM N }ZfF^G,`*൘B 9+W&!|PTsx$RmrrGk\=2M>~@.[ PA.O=6'?? #).'NVAif~ wj"X)RH"E*o`fT)RH"Ew"E)RHq9X0T)RH"Ew$@÷<'1'"E)RHĠ?ÔZ"L"E)Rä)RH"E;LJ"E)Rä)RH"E;̝w/Lohj{ M.\~煗y<^w>l-RHlk644ra8~۫VD"=ҟE:oǝ?=F7l(&HpDQeR)Z\Zꁯ~ Yn'& %\&(f9Ţ6G&Bd;8l?aX"<^"Zm.'b*l 9]x,2a4MACL0Lwaq}{˹233^ p؉gO}c*Ɠg~1Sӿ|7InP)hft@0Y,$ID1%[InC_ڿsvEcXOoZd,% "IP Cp$D(.T*nFBd˦iD\Z0 P`6\n7MQ,6K*b,W(O?Zʗ>|񓧟|z55`|"O8]X4b2)a H%@ P(;*~g{|sEݞ@ 0L<Kִl?/9lO0 3}'O=vG?{z䯑ťeP@rAExt%q#JQD"hRtMD|U0@ 2 22 D"bըձX# Q aEp8T"^}Y@ XB4}kT@x2ApplSzEЙgZE"|~b'Ifwr (b.H2!l6G*b1զ)Z, P8aDl[TbگUWu _ziIJ0_= Ä¡۶}c'~/|NQ{> ǣ1,) e#aR$9F)aEuS*{pntuw=Jbl|Ճ?E#΄#Aii۷m#_7^}P(|I.}v{ZCGvv$I=q捛v0чhx2A4TWY&O8 x<.H r8{źښ_w}:+%Ņw84Mݳ[Q?{ހ?P-^+֦ 3 ծP|zg foݴ8yfjz,aHT*|^~kmwAHB8ٲqlٴ KOGdersڶ uםpqjKFPf{?x韽Ҿ];{~EYfѱ+JJKH/+-_X|u׹ܮ/~Gs:]g/4giyj[ܧT*zWٹ@QToJ`qq^n޸l6i+6`L}f.{ ? R}޾<^QQaOv:\sO0WnTZ\ޑ*,/[6o0::Nܜle4药}il6g;\2t.ybfvҕdh4:8P()lK/O޽c;o Aж 򛯶|_;|806{n~aSDjsKdH$?O{v{742CTWu584r)AEɠB,;tb4zg~;f HOs<.wyv{v饥x1@@5j~?=w~)X_vaaQռ[S]yfϙs :]ڵNU*YY\WQdgZ?0$ hVT"p^j>l4~O =~2';K,m.724sG~ CGWMLLMNO't:/P"  hO1*DrŢ v~۝mYf6-..a$I=⾽ﮯ(nwΜo,).qlKW j4p@͞問>R!?xqh Y,T٘\0l2~#dp,YW[_x76GZW]E34al닟D<1x, j+zP defF"cO>W[N=[ZRt[ycêdykcz.]i&P0y'xٹK>IR "h<)[7owl_Z=^-Sg>' _xԙdeN:{ş/nC ٷދ$x<~}%Ͻ`$+To۲iphԙsf fJK [:/])).y}33_?gef$HbQ5;;Pڱ ee7{-Yw>08477&MO>+x E/]AA_\Xf6ed]ij#KE& 108TTXh~^n~^O(IREoݼI8tБc9vbc | zj[-(󗕔@|ڼiƆѱ'OYtZG?#k*$iڮwv!,RhGMCo_{vťZ%b՚thv8<h4p49y[h띣cN㽕ժRI8A14;;+ 8H$~DskPxhh8){xibb1=}7{zJѨoV^V AJ\\Zb'&&*w,--PԠ8" sq de;q2g7^);B@8^Q^v%OAN80"pz&`}`@A 0̭%i00 'C4C֥Mɂ 5h D<cگ};3s3sN"%,$GFeA|Rr+VH5"`l||rr:##o&J d}\f:|RkWue@`0kv.;;;726~╖@ 808BRT$6oR*W;qrr\Ͽ/ےY]UƬ6u5aXnmC`L)(9ɥ%\R*O8qJ j5Or~sr,۶l}M7H pRx4cXxlL&ݹ}+<8JQEQ$]lZrQw ErM&O@ X'rPuSfpr 0 !b8nkjimmضuAJl26^v{UW `0<7۲}E fqqiZ/}_:]O$H^Z7f((!6lKDrؚMVY,\aslv^&8|0M,6K,ؾ5ȃs9OOp8Wpf6 P80L,gٱDՁ(x0 Mc(a 9(B$A N#KhBRd&a@<`dDQ`>س{f~s/>V#=rk*&ժRHuie~^NrʤT(dᰓu`VTj*'7nvWeed}᳟ G&c'޹A[IL$E#Q'm5M4  )MAM,.,ȿAqF໶m%aF7FOaA^$=Qfs쒢‹Vfȸ%u4r8ލ|a~?9uP IDAT2.RmF˭Z;42P(*%˅ hqqixd45 LAܼh /=LQL7B\ٱ'.͞I&H vjkR x@e!"zƋV梂Uf䜽p:''I\&tvt8-msOo;x<ڵ凎;=ij@4=wtu\STrgA 2qŒPWvMyڊMN9w{,S$$9950̚򲩙@hDB|p8<6>=^N¶kl6;l2#c2ㅂAH8ڇ177kf;u\ڵI@ ܟlZM,|sx6>N,y-Wjk| 'pÑ u9;?mɒɤ0 ܑW ‘:r-YHt|rr $I.,, zFS788x؉$IfefZmz܅k<y x,rq95< I2olJ$<.˹9bXQ_T]vCvcλr-~ҒWh*c6:o8vTkkDl6̹W[; upxdd,##УNnli5@ HOKzj}{YWSH(OٽiC?\zqiETޣOOLj5j]_[R*(_H3Y,\$NNԮI.G"ɩ7J{3C#ZFQ Ғ‚|AA+.,|{مa'.^jjv='BdYI1 NLNn[Y AW/k_\ڸrs{?Cн{vk`7u3sD"=--bsɖ6Fr-BPR6=Q<uwX-m/_YZ^YSQn4\.#KK|~ anw((/K*w)ۻ[R2 395mV#)Pq8]jr-ae[Ν(/ NW$K23:-*2)Pr0Xt䙖43bjO-OL6ԯ (+-.=tX8}h,G"+Qy{U*z}Ϟ_XXܳkgFz+͛7mHn HTRi09N(>~ [7oJ$xiqil2񉉆:AXɿTWnٴEQXqs˦ (-.-X,|>ye܅啹u)Z#Xh4:6>j"lKL^izjkimt'bGDD|jz$I[]-Q06>~ȉ+M-P03= xYn8~|qQD"9p\FPX( y>@Pdu  WYuPYZ?晟¹kG2}>_x1';k׿/"LApzc'YQ^\Ht8A}a04<{_33:݇Οnv'ok.)aG~V@QtW[ۿwn/<"dRs_Rzv}woE(ؾy(](-)&)fA2tϮ{{?D{i.>\nFJ3?lER#N@j[ 6mpODRHCe4m|WG x"*o`)RH"E?79Xib)RH"Eդ)RH"E;LJ"E)R;>ZsUwDo1{yNI~K^z۸` $$ ޵*[ڦmXPdyƁNpwܹ93sNL &L0a„y0a„ &LL &L0a„y0a„ &L̃w(^bAC?.!g?0anini}HijZmw\ ʪjCIVfKgW?"70j& (gʡ(;6&zEȁ?AV2GP=08zQJOK! EDx74=44a(`'.NY]AM3qq1b?XH3wunRIZJT*}x%̖˥e(0,23%_A^#Lc1CQTgWhlj>r}>_[{G$ T-]ڳo3gL?#.q8/ 0 )v׿X2a#E/S`0xzmxX*dfK$O0̐ɮ=|>o:x<=}u @ :["͙C/aRSb[ښ[I\r9 ǿ̍_0}I3[,{ DEeUeͯy_O+G4LQex}ZQiY]!a_| n躺ݞ&q|_岫nfwvB Ųs^>[x~hF pbB|Jrҗ[|BQq nVuaC}ṭӧNp8vw؉/j;Wxw9a;;p8_8z$t=p?C3c*  Vެ&r}ǮPAjj/$cO86?c0xvA@0PRZQ f͜! |0˂y`hh27y<J?iwof5!>uk)^1<<24$(r:;gV|\>IMMh$H[&l~FMҲgb}Þ>FqZ@PT\hK[/=LSs˹.Wfz+H,Rnۭ606:j#3Oyw&aη^DQd#~`o2V._*IGޓ:D"J?RT>wblnmμFah,Z>cɓr|>MӁ@Pjݹ{jMINZx]{%Ǖh5k7*`65Rg˖T͘6uY,g)sW.h}g&FeUqIi0H,Y0%)s̊lp{ff:q8L;p+##eWΟ;m }Fee^Ow[l^ /8;'? BbINJ 5EgWwW(\:nS1 7oz3[, CF]xq9R7,ּ9y9sCYEJn ttv>zt,̼@ᅋ@0R E +\z׮eӦ#;vnnm 7jEŗY`^g }>N]fm߱+t@RmXo49/Ŝ ٽ} /34E:r,%9w`xw׭]-Dpp`(!!nJȤ Fx|Wʯ$)J4MQ|bPP/9vMpzrzʯ]߸aP(<[x7AmE%l6;#-s)P_0L΄KgΝEK;phIiINJy,dz{~VYR(:*QQ]RTܬmii}bcA\Rymmmx8_.ܢj7oxbѕ0"5%yAttvصfuvV';woܰv,-"ʚ[Z7m\j8)ɖ,L d6;q`I`cτM7|2|>f(O*:sq[&<}#70 g0;q*gBSOlnjnc8f̞aֶR`B.׮Zը}d/>oX:Бc4M?y*>.nS[ G0s8lEI(*.4|X,yx9/|Vf:0})}Eڳ#O̚1 GHO g](R܉mv1$::. o+ߞ9}H(qmlj&)t/<pԙs Ü8yOe8uh44}JiSPnS7N>;Q  C40p5kƴuvTҲQwú %e/?ٵ"ֶں˖ժx;R[0onqIdH$eK ͝s% ѣOSsF aF99w04H+oV4RhA OӴd 9l A`JPDDk[ n-M@f>CǓΝ `?0pѸ$h6{ܜ l6A`BR[W?Ψ^TRxb87K MM@k%O>y)c--t7zzjՉSqoTxЪ)Z _~y$~y\!v .'2s[+ =e Kvf&:(}٫fFΟ=+99٭OEEq 2i6^^dH"">?t}CSOo߬y<ḸX >z36=!-J%vLtdwwma{z~ Eg=a=-UUOl'33`K|~2w„LrV%p|'NY6ۃa = AGma E*R&av748s[7oڸ`\\㛟y Jeӣ)ɟ;0Q`LVoO9{~rNݵ{b{vS1'O5$I~^z5W,w..WЗ>DE;|~EUU^DE+*͏%Jn4? 3bb/[|aJqEӅaX$0TGH"pDHr`BBuJeOho~?NcX/]̔ʤ äL2Y˟NTCSsB|܄,F&LȚ<)O̙5@dYIDIgN^r MOhӦNGo,X8_{M3gNHKr @fu͢󣣣2'dg\.mx866 HjH1t9].\,z慗lxlCCU*%84E/^ !>NV$%'%N20Ą)'̞JR "Hq{BϽO>[[[+߆a H*OZm%&dg)9ًc/<4xF1 UÐPzw CC?EgiXj۽gTddY8{< Ƃ!ǓhGg﫨˛cXk|;@`듏G>h6-J|^0z}g;;ZY8+>6A`Aq$Bϙjwvu p8zڍ өD"!0~iaph$)e3Ο<}6,VqParBQDV"pCW|>HBѥŋժJLMIt~qDDB|IQ 8lOX bY׮J 0 GDD4cSD@zEeQQ&dDN#J J.=Ȇӧvŋ̋\8/%LĕiutKś{4Rkj d2\&CQT,eed_a HJL$B!f>r|罷_u~IPf(4MQ$ $.bAۛD"!aE>ECr6=m"Bb--R)'dg1=C5A` Ap!BfdBU|~X`*[ZۜNT*)X`֌;>K4Imbh;!!>珎zabX(Ff 0 a|v o%%%W˯<}J`0?tdtyG=EE\NCcSumK<AbQ08 9i.J (T86 Xh42[8!$1lP*JX,VN΄ں5Yr@V4T]A~I==}0,k). Z) >&r@d4?xi2t\.#筙B9^>bN+B,|0na2@bH:vTss+M3== |aǟbXvpwO宮j4uu m?;vmh_}|>$!r{+y}~0w)EQ!8 'r̡ C4M斾^àq(;3cn:a$i2Cc8)>r8,%>/ l֭7å,ps/FUJ4UCcSZJ6<2s`ɤށAN<^;yo~w޿ Eq] (7oeftCRH( qWʯd2V)-JX? hۋR0vD>f<0 r gnԦގɅ A烚X[6m玲RhhjNIMq8z t,0z}0t@$1!3%$I  YU_|٧{v޾\&Ө՗ˮ$$4mX}>_^ؔVk em흝VbX,508oY;s&dJaX.y}>Cllwts[9vsJ$"JyluQ_m4MZ\zr6!ޑQOqI_[7C59/>u3gLrrC+NX|i^m~ԴrW_4 $Ǖ_?onwO/ 4M\. b R9~PΜ={qR)5u^otҺlI(C<aDE^TaNmmk s{} @Q7nXkXkjkRSRhV*m}rٮ{,^Jr5u]}crRfu:]8p8F9cڔ>C? |^ FGm)I] AoBCS2ll-PTQߨZ0/(v8] Ì8-11DoC0^zE`c5@ ZxєIy0 {}>6 y<^}cSnNv @Pt o"ݟ%ccK~W!BQtI퍷4jufzz0@"P w@Y,<+3Б㡹;f9{q rO: ̌! C0L5 1,9)dW4IV;QCwA7m\,$E}.6k>ﱳ Eт N:oY,pر1эMQzՆs0whﵶ#(0:#NijD|JܻV8ΊK4*ravާQ`8xiSy<0]VMTx)66F*,_z$IOӧUrldl(D&';xcJ"&:E9dҲ7yӦL3jA..8?`hF*3{nlv|\G5C3!gTQdR?m'Nl ؘ|ݰ@ 8)oRo}atϾ55_,v"{<6־a[p;+!3appÏwxrlaCc˝5szvf恃kjYf>#y^jXhF`dI$d(>nTTh5Ţ+-<L,-)XoN\p>z_*XVE=9obVbIL/Pe2 Ô X, DWWwh0Y, (rՖ<&d2[6 ǓJ%^?01JMf HK%CPh 0ըU, ήv\lbN60a  ` aERE* M1CF@ }}a  Zaقa\.crهHL*x t}"P\ldqyI0##'Irht:]4M_VT>jG7:qȆb7 BSI0Jfly\ P NKM(d2X,L qFHDlRbARf]OHuZ I&%!>@tgWL&rCC>_.چRS4kW8]."HH$CZE`0hF _.6s?VbBX,+ C 3u@0hnOmP=84r j 0?8d|l6[Ӎ8FlaHjz5pX,[oMnly >C?0RID{gׁCG,=sINDDgtt9)I7n鳅vȦ%ɽOݗ߄榖Gm}rKB|<ad~ݚU+p9~ԁCG{v۰DBC'O[-w ˿eko 74467!Ι5gݗf|C oo |ֻkW:yҿ.a4M/~ac7 Z0a &L0a„'su\ &L0a„Fv„ &L0a0a+L0a„ v„ &L0a0a+L0a„ v„ &L0a0a+L0a„ v„ &L0a0I/ #Nvؽ N:xI{~OU0තQw\ uofuWNt7y3)FE啫Dd݃z+ EQŗKDHwwϏ~W:z?V0anu}Y,s< q݂A?pk5ʛI ,'?FaD[jRįpZ0#$ϔ8oܰN(ĩ3vjNMM^yP3KJˮ]a ~yGZ:kW>ÉS`lLukRW[xG&OkצU콡(b|Iv*.'H y|^khjׯL0 U[_?B.p$Ivu6Z8};{oZj9vfMm0T*Omٜr8}r)`Y2N/|ȇ=4\.YcXֻbĩ3+-f;.D1 Sr,gBH$$I 6>A? ϷgNEBARR׬\: ?yzՊep,רUy!ZكCCa;ajJ [3GF(/uuAXjH].qӦ$ege=A/ "!Zm-(NLͿ ࣼ "(&:L|B>?2RfI|>AM ##ǵX,=bjd2 L&6eqCH3F2-C}CBo~9j>0cXmGEEi "Pu*fi:.6f =EQ:nx< \T"p8IP*~= ^,ܜ lFhV# j2tp{GgD`0(z"Nk#b@ (/_~^N(2M0K$ֶ̌t94d)\nА1RŠʪ?;zA"3jАZr8Fb"VR A=}! Cw AI*F&4M$Qky?iSbc``gW0,&&Mfmxx``H.i9NtT$'?-TrbJp8F)110LoA,\[[FF"a>+ uZ-öX'H(3S$I5_ #k]=97JrIyy>{4tz 0284422 HXJqhB;5jX$ 9=At􎎎r\V (q88NT#3>.id $EN' wFBFEeZZJ>ݬ(h^zSOlx<޶oH;ԁ6pbc\NOofYZF(|~gtb?٩j$o.݇r͛T*e[{ǡ#^zsp'IR&.[\ }I,NgqI׮V(-ފ*FFf RS.76DG8+A"( ,tY<:*ĩ3^6̚rYX{GgfF:̙B:.ٳhNJHw=ζfLLfslt[lNZm"k{w~& G*n\=0&[t:-AVr/׻vJLӂys&Q(0 ',)Xt)uXwvwU*m Y짞,JKJˆ8=ɱ @$BOd{GgMmdJ#z|ˁCGN4/k?Vg-w}h6[̖6IS'765_)&J~w۷^|.9)1>Wfo~Ns\b2V p/ nzdÈ100#S&7^r51>~Qz`pBA(EK/bhO}=/75-ŗ- _p8WyJɤ|>ʪvOzb3a+X@m WU׈E"sr+-+~rK]CcSs1 308TYu3=53@y<2AKd?6&ZV#YWߘ?{SP\.wsKkx3ڳO,BR*765- P(Ωoht{u{F_OJWӗ>ԔK%&DԬjr}ͺ5+Mf˾bwwsgHO+X8fn[֯]QUUbc.9v"-5%lIN900;|`0nJ@WwOKk[N9wW_?y׬޻k3MxDQ?a}o(ڷ-w>DtM<2hHjiI 7k[[z`٧PU{$e4"1G$:2A=0w@lJ%r PS[wC3ӧM3A,?qM3n#ɜ.W΄,zѻM5u ٷ`HěXYumֻ##\i7N`phw@pͪ0 0yRnjJP 8NٲvՊx֋I<}06&z>uʮ=Kc vMrkO-LINb@ti K M<)3 RuN}2ٞ},?aVS[G l泅|Pa8j+MɓB]_׬^9cڔG贺c'N Y$IMKŗ_K»_ɤM7ɼ`^>RIH$4-!imZDz23:2^Z[{3lԔ他0bmhlOPx<&%t=>>NфqB܉WɛCӴbz\xX$ Z,V봩YlvX p=s/]-iS aS&:1bqhV}^/ESVM(h4j &de; QN0"󡿋 HJȸx}b΄ή٬޾H@  W*䣣!Yz)r6q8%n ۻiI˯WGr'N v_.G,Va 3OcpB*+#=iAh[b;a EӴjes8zp艂5A(43~-(CF][WGPSKKss IQCFJ*egkR]SSRzexA"JfM IbZنrYLt4|`IDAT :q]gY/<ժ3O9}qɮ3@TΙ= @ `7kVmU'`g M&sPhiOѥ??{ؤ;06۰;g/*.QY5n> C C"hU pbbBsKk]}X$s?>o%11ѡ^h2/_EQL-V[|l2].dBQTggᡀ9 E>708rK=T*͝x۟  }u (&$=F 5a Q3IDzj vHps/>KFFF<OZZ* |Bdr^{cb)JOKiP(0$E96 A"  CC(Bx͍FGE;p莂 `  K=co1 EZBIyz~mi\'=-dpva @1 E>?C34c8FQ-¤H$VOn#0z@0 6MA(zaF$)!"sejh2DGTJHW\w1E@ pȆE w~w듏 7~/X L/ɤ+W}d=EQ]:HX~`U$AJHC@Rb ɞ޾( (a nf   Nxº q1 a>CJrή(A&Mܹ{olL4Ei^"++=" BXk=B.OB5g /EEFFGGI""F} ABP[ /7'11ĩ0 ϙ5'6u |;d4B0 cXjba *2r|U}>[n˟=sŲ%Nlh;ͮ&?kk@`xy,O[ BtVp(졂 E MyQa~+bhŲųfL+.-{m? %0P("Ip nwhq{X?܉9a0AQgiLi h "p8FR8\8"?jt:mM}} hQ/[{~hLLX,nln =}0lkBv֥ҖƦ:ZRbqɨ .+Y D$:*2]>sb2mxQ=hҌ D ` (';[T~}Gٕ޾b$IAIr и,!CFxժ˖wrA,vwOoAJrRzD*ilFH?xhuMm{GNBPVuVެ{ a8*J#޾ƦPVXpБaB!ϝs5]ZK 5gͬu8Anז!iPhZaZjLNJFDD446z}Վs\0 S)Z&P9Kvv\&S*s{{t5Jwuw]I$AID޳/DGGDnTV&'0608XYuq_,Acrl)?6_АBaq8S-qJIN " z |>\Z&JJ3z‹Eic5gΨs8np鞮.'߬5MRg~/eefDD®0hNgwXysfXLy N'I033=-xvCӯ-ȏa}>jۙA g-kWl@Lt;r,\Lh>}X$EU fL#)ji!9l6K(XDtB|\^j}>md E"auMmSsKYz]csKiƦf\G86&0:z}:?  y]}CٕVVVRn̨HReǝ-bNŢZl9c$IŚ ss&2gϜ! M qq]kWM-q(;~j]6Ck+Z"):X$*P Z =5##J"JbOgh*VɓHX)j*\vM&ɤҬ GGr'M@ H3-U7Q5s:چ93g0ZПc@镫gΝR)aj5eWRZh2Ϙ6U,ek@\\\ țH"tZmLTAKˮެx[z"A2nQQ](*p8"y\ސ4cA( c"#Wr\F Jy\9låeW׮^H$mljNKIa8$$LfiSCF]-vRqgWwFZBUĄ^C+{fUi ~=@[_B[4čLtjt׍1 E$&꜋c:6Ʀ2 ˜C[FyhB[swNޓo>y; z}(!Lee(KTJW_u* 0 ! A8n[_@&Z&z >/lNLf`3>ai~ swہ!dnS_>o_6Mz}yc/68;y3 zOwOs}Νճ?} (dBx}oG3e ·!ha œmAX^N65B*LD뫯C-N^xt‘V)|s`ِ}c4_{wq^{Zuۺ!'>|vޖT(K|,`(-1Mdu{:MTeQQ5qe۶[Tryż8(wG\Qqq΋}{AéjhoB_!80#Bc:B)!80#czUUA-_;U!n)7M/Xqɑd}yΘ#@]]}lb;mڰF( ZQUUz(DF]QϿj9/fUWW\?WABL' \.LTc˖.4"~ W={t'^?zSUU%VqBBIMMnj 5 =hu.VMUuͪD?{\YIyp';2_Б foTUV <臩STTP]]u.ɓ <ՙJ7KPBa%d Scu %5Z*^J;fm#Lܽ'9rovؽWʿݼuJ"סciSu G~򕆆1E=iP9/֬4mʤTWw^0ĤDQ1-Q1I*uG\d&p.6\L\`?BDVL%%ߏHas8i7ng55U;qRCC} տ< 8"NѺտ&:gjjj6[E"8yKf mB|K/uFbҲȳҹS .k2rɺ[NvP(l6w;3gL VBRFŜ8f XX֮훫kj8Llv Ѓ***!w{ʄQgV0#!.3B71p~wCݻw+)-!$''I.YdGtџ5ӛH ݧhj$I <~ΖBhhhX[ZܹsOTJYYYSS233 jjj⬪bi>>#af%vFZ]u u--7KKt͎:QEp}S#dw)gG{UUU2<|7o@A={h?Bh[Z-6ޅYt(q O5552lia?60L"* $a7@k%\pt .m3͜Oob/@[KxUZZ** (Ҳ/PWW״YեR(+Wtm-ݻu:٭C=|#Lܽgl4ٝiJGAPVR&H;h ElϧX,ⅲP(lZc>=URR&8***G=7>tǰj| t&D*--#qiiؽ1eee:::P\\ECD"mZNהծ m\c?qQ#C9豺:bNIL7!t(+/g@؉ 7#eee 6#+dqΜY>yړC[;-AF nP5\nk#LMD/' B.jRѰ@RGx  6Kg|VVU侨P(zzNS**ԡC89bxHy#ַO / B@r=(ߦ;*؁J\%gXQQBJw$- /,gSc#kK"J^nD"i oTUTǏx!---[TTTK['9[2ܡ,ٝF hXtF4o-Y`?wP{KSUUа{Y, Oͪ8?Jdr.kV F#ef=jّ9E{[۷D*Y4s9!tRӻtO P{tRBHq`LG!Ł1!2}DP[[锔>~sϏĭ}@!: B)!80#Bc:B)!80#BCu9U<ާ B!i}z&%ȥ6Bfssr#1W깗*:B}iXUUUr|:B)!80#Bc:B)!80#czqqɑ?w/B#Has82 hvBa1ʕ&}{@yyE}C}t !^Uū50'H%ʪ?Э[WS#eee()-}^Ii)Wee!>ݷo߾{?XYN w( /_=z˭ϙw-:x@C]цD]TDvcHǎL S@]]&hjeAujPT\pi;jj4EyBs/\nw캺:}7@YYwTWW@MMmII gfBbT*KFچ#j>l^]]}=nJt7v 5j̇bkTutt44440Efldyfǎu^:a]׮B[Y)1#a#>qoB5֝ÆJ Be0#Bc:B)!80#BCjLWWٜOBzfhrQ}}[!{"%ȥtB_<Bc:B)!80#Bc:B)!8]W"ޮ} jU>FJΕ?G.)UsPysr{0sr nYݝ) 5d :B}i 5HŨBc:B)!80#BC!>cI`OB&}P BKqL.xM=@2uR3ttU}r רDP/oa1%{5JЭ"Ԕ/]*}瞝>W.ϳs~YyT;'7OYo{>oܼ]Ak5ݺmiaxK062媨OAZ"d`7N(LRt 3b:M 9pH,(|ͭ<`f&:wmȁD!Ǐ;fA_/!)׉R-&' V*/xcn6NFSTo$Fl6{iҒN"xq'N O" fsDvDP($^{ TWW_K7Ӈ|[Kei18RVV0KtEВ 2`2ueizt] 8EE v}}i3riNjSn:;a]]CJJJIa uu– 9bgϟ׳}zwԉH6jhx@ZP\\7izUT>?o>gwϑLoY}!VTTlڲ}ʟnj37`dX,&D ?vnqO Rsݧ!ZBPYY"" -]]Ν:vsڔI H;Lg͜qk?zHZ"tM ޞGo &] f!/_KHXZv3;'lX"EءJ>G33W|>TTTr%! TjNDugXYN|5۷bMvsq"&9s畫Ir6h**ij4g'=LWmзӒEa#CX,tj?ClT 5lԯ())1W,_JP\V(<z!];\LZ"H]6*a' j'p]͖0GjP(広f ŗ-%@!Ł1!tRBHq`LG!Ł1!tRBHq?w#*tu8ZaQ˲bzיY iN jx!tRBHq`LG!!oL !y:B)czzFfzFf 59***-lA h!5O /OX߾k۴˺wm .ZAF ^f v8*?Gǜo9{KgHj'&F 6A4--׽VvR WgΦR Dǜ҇j4<;3gxI,(`CeevHzr|1-|*iP!<]t-uΎq Ŀs.o9uG4"=;'e{\EG,l=}/Q 67obnmo]6.v!V: h_Mmm}}.&*EM*1?:;OblZQ'R)86mD[c'sNB|}?J:;ZZ ;C=)ַy6df=z$^ 6.rEI~V--Ջ 9#J/xidDXj ܴu(˭L?Zɗ]w# l۱\dDLԩw ܲa-\O|=2ׇ;u1DbVyyŪuׯY!lZajbl6gמ_wmߜtP fD#MKcׯG!8ri,Y ?láCnes8Gh&\,VeeսfΠRZ?L,;{MML&XY8;dSc#P@&@ FSS6uҵԴfD_U60er~}%=-FCޭhiљblg(у8Ib\`QP:: .cE_;YْEiP^Vn5tM oOň{=񶃞x}:B t6())N'^ihh>×(5G554\=-FC455@EE%S3 ˭Kk@IIID%% 4vrt"/kbd4'&_ ="3d6*EE|sH7D$czz,s攎W6mPzKKvBCC Pkex1NT@h@ -UZZt` wpёD:Mb2;wu߼;w  81O )9O!~mm^)M aCCNVVV:WUUR}!<9MoD_1QN>qc 'hv|iqEb1i4˼<(/xP(R];2/ϠK94s| qVmm-?r=pcretK,b7؉INM ]bV [4PQoJn%M6ͥw 3uu==-TEGF>rM_%%R!-hh޻>tw_C#vߴn5Q>==c0PZA ݫgHݺ6$mRu=7 ab-%orz)S :dQ#ÎGHoV `me1u,{Kk]aSyy :t% i|[7}}|}$*j?>uL<'B3RBHq`LG!Ł1!tRBHq`LG!Ł1!1B<!\Ot+-E]QQ?oaC]gj䚧'oߵm?Nط%\lqFF}v~ͭ-l xFm;XrҟcηvSnx͜=vϳsu t#s  z\x<ގ]ExrnHƊhMb^Onmeq)>JBlǎVUUU,sqvv%R|LnݖsTFⓧ߼ug ɏH \or$19e3<^;vhy |8-kwvZ~o|74zonS)gSQZQy|l]m]#(*i,T)Ekjk H$*bh8XFN\Kbb `coz֝!vΓD oɨh4#9O$V86mD[c'sN B|}?J:;ZZ ;ѴK-)ַy6df=z$^ 6.rE((|]͙Ȉ/^>&;M[vxsW~]݀$8b2*']tY|R Q'B33$ ?yxA7m)m/OKe: ş<ze oǜ9{{״v.++?u;#G`NfVַ䔸As.i ʤK\~ZJ|9qwоf+6<>DHO0516kϯoNMtx3&w~=zđM3f@ !w7f9ѣFhfc.\eyOwP( mmoOiͦi7~Mdr@K>ہd2;xxSTmm-O.\"ʝ=ˇF&m,_5._@ IDATd wtMMiS]SJbrsh4MMmIR孫zsӦ8m#aFC 7{dpqv FP(tuv M (qTpݻu--:SW\lat50 TTpOapWD턼kbgd_dQ}%8@uu ]SӝҢ/XQi8|P(q=)&R~L.޷IPBa{!~N mv%įl6a _mC<ϟ\zE^~ނ%cnj\a"*] JJJ@Ӊ :%JcOE{п_.DuGk@II 1!jD@_0Ȱi.OLz(D4g߻"b̽}Nae#l(tWZq5fcso9 񒤎L&.c1EMJ }gII)񧨨 ee% nڦD"Ŝ9W{ sM׭-Q[MNnC!$IO7sAO.d\ʖ]oF@ee1ka wpёD:Mb2;wu(FƇU`?]F@>{WWW])=v<,T8Q-ih4ژ#Cy#{tVWWws) gO隚G8z,dݔKܐDa5\3g7T*5Z*G:Ӵ󪪪6V{C</' )iݸy-kwc_T___]]}jR>jЊ"Ťh/BJv˼<.]ls| qVmm-?r=R@2:%ZM}^iiJ>{MW|қZbivOv1m,<}L[ ޞ9Oa/˅BﰡC cEC}q&qC|;p@={NqO읚ꚕC;fn)n.DaKuu޾-'Nvwc}\S^V>w"> 3K[R{vnryRYY5+Wlpx~Pj#C|אW&mZ^&rD+ ˖ 77||BHݺBqB_ zIU!3ч>%;'k;7LGH1d=z QĜ7oN:ӊ+(PHP[Y%7J.[KlorB 2x̨a#$7w_t:wm}]!$_/c6ڨ^EG%tB Ͻ Bc:B)!80#Bc:B)!8BBHqtKK"*:VF5O /OX߾k۴?N?oUK"-mYغLq<ݨf>50޼máK.^st6Lj ǚ[Y_yv~vnbd `na] \OҢzݓ ۱q\f@+ ϧ&^ܽ}<!:7o?w/diq?y[wfΐ4E*++o$&l޶sG cǎ?o!z5vKy#aQ: _~s&",:2ŋNojӖ<\dD@WG7`+ .-ד.~V z!6dXhfDz~~'O##R/~7hভ;o::$DMovv.++?u;#G<\yyŪuׯY!@VZ.2Rd7VҢk%_wurيI3LMS.<j1hd{m!#G'n0K/Rx- 1;'5F7p({;Bahk{{\Ok6]/HMlF&;vZZA$ɦFo߱ݼuǛJjkkyzp5`,L|$)xNc6mn29؉޺@ FSS6uҵTɻׯZ)+@Vvg# d2yqAaaq%\nn@K=fstt] 2={ N .0t:: .Wjw̠˖,׷Oظrkjx{YZZtU\\""-]==xbҵo޼ 2Dz9Ed_ʏe9\=6)P($^S)ӦO{?z{ǖF:ihh>×xƑ G554\h.%%%k\ljHee%hjjJ"ݽg/pAA"WڒNbC(.)a0bsk!WL'&FMsy|~bC!_~? )so߾EXt6߹R^^j֘gϽ}$L&.c1E<(`?hx[cH3t;{Bаs"Z9%^h.@8DtDXsg;p8pي"W]&mdwt=w76R@etHJNӻw~}uuu{[ѕc#jjkKOEՒ.F=2a___?Gnuuu=`0yvę9zԈCǪKOFZ[7o>ZVV^IL6tpƛݺŞ .x<^Nf0uN8F>}xflVY,&F{? T*~gp_t"qPs| qVmm-?r=R@yq ]S&;1IEEoSݽcFMw;OVSS3v赫VxL=_Ŝ;{q➩?oM5,߲cMbdcyE]{ I@F~FGGFztd򥁇BzxVr+i)n.{lo:%%R612w?}jxG&+uij|zzS9Ç0PZA ݫgHݺ6'MM4$i'l;Hf֣}KvN̦IKKG諓ϫ&F36)^"juWw ѽ{[o F)qXeKu-MN!njvc߫W,74q Do7o`h6-x#Lm!5Ɖyyȃ}zK-j'._l] \OҢzݓX }пj%C>/6x>y[wfΐE*++o.%&l޶sG cǎ?o!uޢCkwvZ;zonS-\yR#B <}hau'O7o9QbEof> ? YP[[ #SC]bzF]/\"^O~N K ((|]͙Ȉ/^>FٶcwYYȈSޱ9*QC]NvΓ6oYYYը@\neҥn.?XEJ;h_?yxA7mb2eE 3LMS.<j1hd{mqׯG!8riìF:R0- 1;'5F7K, 5˗jjhgxxSTmm-O.\M?j`0ju07{-JbGtw tOJbrsh4MMmIRZv(Grqr!ȁ}o۵G?1Zѯ ?{0e$awlId=ztܹSaԴX7wꚚƢtNQ1߾}GJOE"bΜ4}^º:5Vo]@-X+6=_!F*bb1̝e+##\uu<߹ӭ\Ew76QoQ"eLJNӻw~}uuu{[ѕRi蚚G8z,dejb|51Y]]pSG 7 ޽{A'XN;QS[[ZV~*lXY rh}nݹlΑǭ,&6IЊ g444XL2/+=~* Tj/3Eޣ"s| qVmm-?r=ӣQo۴K#t7%ZM}^iif\(ػxx:kDыƎ!ގ=W&tpqrxǟmD)***}5^={&җ/ axx86ds~;Ѱ9~3hjGAޞ9OTWku0r5iӺ3d_xV+-9nnm7nݻum[ߦ@_S~IU$3WȗR&f{%QzUkbyQ:?Ww ѽ"j f ōRx˖[P8S?LAocF ;!1wwоs>Y@k+)ns//dE_ y{͘O~I.cnj}A/˗Jڱu GCqbzؑM'^BB{Avc:B)!80#Bc:B)!80#7{Av!8z~R6_e 6EH"A wur ~9vXeuo޾"N?oD.Skkkͭ1=\~n6]#aJ,x1?ARnx͜=vϳsDYzډcႀ͆u@r=MK~AFuOc18'T_V<;}C[@yrIr#'Oy(`CeemڥvHzr|1-$(l# sEa] vRQq ]Lt̙***TUU]|}srX5u[T1Gx*7oio7v̂yP{?ZPm8lȋWy5fp `?q?}{^xI μϘE<|瞠)Y>Ē0嫃Hp鲝[i9[ZZt'Q`;7~|?zƳf9Wrq*̬ i+zO ?Y^^aEْEzz,rbd9GĂMyNwtwh3~ 옓H؉Elۦ˫"E%'Mm;-3Qf#q Nvgd<,*.6kϯoNMtx3dAoDEGFx0"}ێee"#bN{>x(iw.2"J[6#8\h;]݀$8b2U\y+Ju2,43aHQDki555DCl56kѵ\ ;r[wD "=v~bf\neҥn.?XEJ;h_?yxA7mh@zzHMk9EE_9Bہ+Vݸy칷iutd2ݿ9=Sqcw5ZtMΝsreW$,**曎PV*D",qa2f$iåMWDH畕U4i[x,&Ss[ﹹ8ܽo4l(H5A Oӛ:ҧw?6q ? T*~g@ rxDMmmiYYHsqjce'8h4ژ#Cy`0yvNٷAɓ\wjhh=u隚G8z,dxjjcǎٰiDkti554 rڪXDwޯneel\6{Z|`?a4e^H/ t(?߃GBoܼU[[Ƨ\O)rO 6.?n޾{k [sk+W6[M䪨{<{1ǛH_4PW;rdw{__ZbivO2uu==-s$;3<߾{RzӦxȈ9AZ5, 6.޾ÆCTWOo(]ZŵVp8g̲M`;1{)W4*qk?&FDZaq1DΞeK[ۍ`s>>!$hwn]i7$픟mRu]̬Gȗ\w3e=z QČ7oN:I&& >ԣ{OEab-%79!C 3jd͝:X[YLqs;q~~'({ B_/cOkEǧ O Ps/!80#Bc:B)!80#Bc:B)ycz<!Py:B)Z۫🷰ZCVvNȱ͗Ck_}^'iO+oP;׳V:[ XԦkx~o7oypĒst)5cͭ,m/^<;Gըh>.lX)Ӵd^{Q.k Gx?y̜!9?TVVަ]JLNټm /.ǎ3BbޢCy~5ffQ' b4ɝ;umx ZZt86j`}@_,'MW~ChjjӦN&g2ljl$ ]dpSf+ΞCSS#6VuޭH={hk\`0ut\(Q;!:GA-Yԯoq e6.P]]Ct'jj\v5hFcOE{п_]d}8}˾DYl6aBx]t:x]WWlE=xAPm*++@SSdMM i :]JJJ:v J,.)a0n'WL'&FMsy|~bC!_~?9´4Dd7"^b. ;{߁ÁVDGJ^,qa2D==D9sJR9 H$i] h4TVViiAHRWIc1;ݺ}Qx}aC+rݟ.#@RrJ޽뫫CWJoje1aݻv5xG B*E3Ʌ/Nܹ{q $jce'8 H & 3/*jgc=b`h>s mㅆmX2bⅾZZZ@ԼݦJZ[%$&ԹgdZuh4i._ӵ֬~a+twq8-DM[.8[w9}ݻЗ#-=o ܳz_^^t+֬O21$tO?ȌHI߯ϖmA,&3pF+)ܽ{o(,9x֔'oI42|w6ƻoތ9Bpai zRϝ=x@-,y<`1q$IΥ\`icbV6mכD[҅޹\[xh4/'@EEҼfh4}}=!OHL_0`0tiSաiNY'[XU@& " ]daC_ϼ9 :L&X˓jq.; ##NffDL]3! e rZ ν{ia{W~W=G.%%Xϻv@Ν~;Ҩl l۸+Y^NH rBNHפf2 9PT\ mLXD BD"1*B} 2=^j]=<vzzzcHTgEԢR5REEfу'=Wb3|zzmH ߿744hQRN'z'@ȰgqWRe]?zS_OC{\ȐL&+tho  EE7ҏנLLX$)Ii~ڲmuYo~Z) ٖ TTT XgkDG[XLf~3QZx?|3HnUQt:xKXĜݳX,Wۣӵ,,Foڲ}loG8|CIIh |3hǪgZZZv6V>S@z7o X3 {޻S+@VvD"a y^e 4c<>gֱ*#^q@ xg]v=sPoi''k}qUIv 2@8ȁQ^+\*U@_lk)WU;C Y3׭ٲmGCsQjyG<<*+ =wSj oRi q*߀lw'Ud{]Π22w / ~"/Ltaˏwf o'Gj\q_գ{22j@4OO:h~j *e׫NK|AHVz6ƞ;M(WYOGg ەа]tiP#%G^t*Yy4xkm-MNӠF;2X­!{{7MB[+.~z4=@%|z3Z6PЕtja8!Z!>0#Bs:B!>0#Bs:BUs:'Rq:B~?&ܡ#- 5jT:N ۫$!Kk5{ąKo Fxߢel1VlgW_bSظ<޺r/-gkvp8~K^Anma˖>sMI1k8[Kke˳sjj%C/S/?mڮݺ77zqWΧ$ƅ4:\ݍ)jl/^yhbxIJ󡃓/?r4`*lXrAÛnϝ=KapR={~~׶n=#ʥ,F]8ODqi W;}_'O>s7b٧L6zD!J=1VYs}?y EEߍL&Ν̺u)*1ǻZ +xz>{NѬ>f;;og1\hNq-|;.d2bz͙U]0 ;'31َ.7o>~ӔۿQg qbϜ'Ou@,Qsn750߅K pa_o1ߍQtոCkB-H=hӬlHKHKϨVQQy9ʘ#scOW= 9~ޱ5%c\Z Hxpıĸ~}{o ڵfk.,&3pF绂Ӥ.^|J;CJJJFGŜ>]CGT|P#ioj:bo,+"8J/_Թ+k x*"ũWYecH:+׿_-ۂܽ{o}n7q^y3j‘Ç.7e#5Vϝ=x@-,y<`1QX-Zdv}}=s 9FF̤|} L~$)lw-;읧>zJAqo{-;'$&/`0t۴)WSR&F}Kl\5qս)ܕdCBL>lhuV7ANKY˻t n]~5r042-422,/W j%TZcK ۻ08B)))ú޵(Ta([SS3ȓs{+`ǥۘT*U(iT wn3CB?y3Pe---K{}JReKdr$Ɍ^S-/͐oE=HeԢӉ|`~' B xWX(b1w0`3xTq~~>VΓZ>l&D9u8 |-|_Ry+֜-1 $*ڌzWow&;;J oǐoAOj=T?]ń<˷kێF%]M⨓ҲBFuО۷U P^Q͠`hhY9n]tҹSh vr@---;]aersrPxbrCfyLZ箬Ԋӏb2yyP~gֱoC{{?t[=wTϙPZUIlP(q5t kW?xd{Ю6&ysٞz4:됨$RI$(U,5W.^M<6$X}Ȯ]4'%U&9-(}+9Ւui]oI2EqDMKi8yXPQ+,߽5W :KKzE=<YTFҎ g=DiI_*H ȩEB A]֛"ֿHn'3^W9'lyLXkCTV\M!e[ʖAiկ*%8E[:kZ']N6̫B!ɋL'C9;wwХBz!- N$I;G+&w9bb| ]u+RϽN餡K%g ;CmNj  ҫw 9C5/ oDOK%5S = BK-]j35s忇7މƴ?pw@̇|[j1K" IDAT5 kGAx XD*7)IoD1 V<.[RX{t8JZY<Ƒ\P_O72x_hMj=*.3-3ͪ"$Iת%VK~Eգߝ*߫X$.Pȓ<,Пϐ,fx0ezVMZ*YЋSx_%P~O/][eۏI;* EV#3H]tȩDzg@.ku2H:TǭhBUFՃZgE$?U3ދ ^qc]2$,{V,gH*&V 'eQJ)H%x/fiL P-$TK^s%I1m)r<Epp $2pGFjNA#xBH-iکr7 =&Z$lKU׺Y(jEK1m)KvH9+-T Hx-"f W^$H/Xs"2t4H*%PRܱFņPZVR :#jFJA`Kk^'Nij^݆bF14HŰjg3洜2kI"[#ùs/ ^=4z6=VEu8S寺L W,hK9'hKTP[dL)ϲm(kD}G^ӟ@z|wPV-Xs+|:d14*}U 1Ö  h$߷ː%xHѺtߋĒ罾Ys*[[JRIx$#K$iqP3M4;UEU ;U%յN%µi<^;V41 Wʕoh7 T2/175 Xc>N$04H Z}Ud19S7YԞ'z}SaB'x.t1E`H#RI eLq>~եz)$[D P%ќA uH`EO5-aXI|mv*UR U,DZX Ν4}媼q@.ak :?u>Ի~_%ቀ8'S3cT9Wo' =j c5ȠEoM%f5R{ swjdrg TJG2 !Ug(AnfdD'㙊}Y]4vӴ5'|Q^PJюN'{k}tJP-9p *O;I$$3\)UTEphYox>V$YsM/PPr{ #qmlQN$k>dM kNQJ+*n`'Ud{ZzO&t2K5>}^i0M߫>~FiKꡱꗕUe51,c㭵nj Ѐ5INf7YQ"ΊzЈz&h|ѦA4SniZ!vtR\":M}" !?Pk9s}Ϸ x!tR??]>C 'NNw:"uwb!>{A!9!tRBH}`NG!9!+++]DBjMӟٯzܴ" <^~Nt.+ɗ.'4j"Nqq&H=wo׶ <o8NSSI:3iX lG\/pG0y{.>_+uvu8y+9ޏ?6?ܡ#-8EE3xW lKkӬ#-`̹҃w9 *VE$]~c\Oަ).q{PT 3dIKJJ<ͲY'9!1dwrwckJ[$" )))=swQ8W?ȌHI߯ϖmAhI|]=)L:LOH$ٳVT-GwUUU;u"vٳ;$Z '-5ۇL&:]!Q46>{Ba!Ȱd Nw6jJjm7Ad;yykSFzۂw%b'V&T[[m!Ԃ5::)*joj*)oBDGFv^:r$Ɍ^D-Zs1aT*G*69JbL+ɫW~ZZ |~t:PKYLfk۸y,ؽag1_pY5`b"H1N'7Ro]O;驏T)R- @ڳ>|( 򩯥h۶ oy;5!PS\#:⮝;D9e 4c<>\jWXxYy9pܜg m*lDsoRߎ$ v6߉|-jL&X:rrNǜQp!XjX&xɝ! k9ׄ{)B^ɔN;lq=ª(`h3\';-+;gM0{oخz6?s%t^JP(lѿ,^+-~[ w\(۩.N="Z*Rm[TyXT&B5xkūo Bs:B!>0#Bs:B!>0#Bs:B!>0#XYY%"З)~?<:tXGhc?G?Z 022=Z8:IGGD"޽]6 oEl&;MM&|L̤nc'\=NrylG;{A|>WTT~7[2L&;w2ֵ\gW qbϜ::҂ST4cw@`iͶf?:Ϝ{!6Ng>{Bm:4iK-tAk!OZ^RRi&|b]Cv@a!'x;$]>qxK"rΐҳQ1O{Wh׮7XstX,۲xx*計ĸlS`1ZtĒҵ&'$~skqJEʵ751ܼG:h>x@M>|f-}}~}aC+$Jg](,v23>`>`nӦ\MI;bS=^UU$3oN'v6/jT9[kG-xW-,F*!@ONQQ{SSMIWE~@(J$Ԍlv}od>|T-@yEEBb7(N  +;&!JJN`gC~B5ŽV4i#O P(F?mX۷Oo7Wy&,ϼK  ~"/LtaˏV-8pWEyC:ٹGnee;w~MC?_$)il9w:N\/%P(_/~R!H 7*;Kg^D!\,c㭵n@!9!tRBH}`NG!9!tRBH}`NG!9!tRtq:BF2RW87RrYVb;bLHP-Eˬَcήѿ!/)XgjzOf\vtgkŋQAVl31`H)7fgki^lyVvtIYN{/4U3g-Z:bד.oۼo> <TWW/\`c5JĸC[I:Px[P`=~K|gk7nj)&7d>yٳ,߳@IIi)1Agy$_Q qZuKw聾 sws⤣M"ztޮmQ/^rp:Ɗ=k'Ox.#bg=h7mvtg3q?1/N>ߒd2ܹY]8EE3xW lKkX,~ⅷߢq6흦zf@mc@Ml q Q~#PR˙vlGn gk?i8Xg~I} L6zt~.5}|7FZ.NVE9F(C/MDB_GP3’'OljP-g<8uXJb\7$ʷ )))=sw!ʷrgbzKIzNkAVWtLbaIדdOrBc5o=h?A=U[WI/Nb:}=.CBY計ĸl$7{̇@a!'x;$]>qxKCs1(7F-9|X_5g{ }iٲi]͇  _/A&l_OHL_0`0tiSOIׇoۦ Ÿ f@"vYZyǫjׯo2"!1As$ ];wDq D;5ۿQVV.Ovk{޻S+`0@iiYLPH:vh<U}eCoܼ%m*Ϡ7oY8?MM_&v֯L߅K (7Wy&,ϼK5jjbw۴u;gÆ ݸ~\@{޻*!qwЀ}/dJop?46VT#6nztҥse: vʏTyX4-=!m=wQ۷Ο666j\IL ;ڵK5#xkm&'cq,J֐=~,dE^oNBtj0#Bs:BB!Ԍ8!tRBH}`NG!9!tR͖ӳsrh>w5ӬCGe>|${ɬiF_GFDI<`uBuj~f~t6o۷OZX]{m6#Bj0w7).Nݻ@yy9J=RNfW~׽ㅆmX2bⅾZZZPV^?VF6ME97@Nm;>|<ɡ ^8RTc[kf B-Zg>yekmUJ@ԉc)q(߾3ltT?t(垍JN]-m';b`M[.8[ꅣ޷= Z '- kx1 2lgc"/|~Bb AMr5%x<~J>D|6m~`Oh0TR{i^i4!r0#Bs:B!>0#Bs:B!>0#Bs:B|.mkvNБ>Ij5T ;B}\Ysut` fЂ5ͅظI@OOoߞ 2'O=@ 4665S'i t%ꟷo4Xj8zRCb ?X n<.q?oϟty}i.Чz"sFk 5#CC'G'O_[4f_o@vNTYSu(/+/ߴu;eu7vs3KJJTpg3ykŢiҖ.Z:ťP`9=-=C_XU_̇&J|b]Cv_\x_N ܴ9Ɨ]ZaE9.N/]&|*'UII铧YV5NKI׷D!%%gbN|p#D\.ltTrB_oi;IWߩ}%7lڪp\ġ޺{ڛnܣ@H4XNs 'iަ<3BRk0=gz.\2iU*J9Or|aj؟6p̈́Ic޾󕴰jyX Sg.Q~* )nl'0#Bs:B!>0#Be\MBMsz̈́))+/_v6&<ASB-H𮟫.+/+_Үm[{eLBkΜj톙n'JK:vX>аq_5rİn4*Kǣ+:u\_.o%bыjii)lAo97@Nm;>|<ɡޡVA$]~о0ΠӧNq9i!Te^ʿ 3:*BKS3$4l˶E-;d(--BZ Hxp1--а́;;CQ@"]CG/Pck'wHJoشilp_zmCKw着:wD<֥g/wH\ s>}rT3oN'v6/㧤X`0dr6mH_/Dg0 :mڔ)JZ^ZX=,SI?y{ͦhznS]B x|$jkkWr:"ZF?RsdD"[hbTP(&&,mL# 9dlZ-K::ڪv'!j :*:] *++tzs szmۂv%ok' +0PXTf D"Ŝ:iddJ {`O[vm#HU766bb%ڶm`0^_y;5j^F1w|yQIJ0ѣF?bq/kҲ^V^\.7'X,_<74k[͞|RsG.\xo.3mAqO/=ݸGܜysgR%%ꡒO VK:zHh'mԣ{vmHJ&g:MqL4v?nFhJk89Z[;q\y-.#bg=h'⧺Ϻig/^x-g3ij79EE3xW lKkӬlҵ~8iӔD"Ѡ8Xg~Im16V455)wF.@^3kogvtyq:+n9#Ou@RԙIZO`;>zڛ¥=RSSs: EQ)ַz iiĦ'OlUZC2oޞ8G"ST͛Q#GF>,t%`ΐҳQ1O{Wu O;|a062INuqv b27mIדei6*v<MzAftTDJb\~}l R}Ckuz_^^t+֬O21$tOvܜw 3]?ؚtıV-FxJ>5Jf8R{ك 0a1U9ÝJxtv=P6ȰLI->`>`nӦ\MI%UtutuDC& R𮰶qܛn{h3=_On7Ad;yy*X q,z{nRU pvr P(tqr Ç Ίwwq.; ##NffDL]@mrūQVBCŸB~izmNQQ{SSU#X@OO޿߶Ϳgl߿F*UXȑH$3fL Bi|&oBDR 9Ed~\4.,HX**|&Nrٻ|{vK7)uB===1qJMyE廻tuu@SS3ȓs{+`YGbkm6>5)Ӊ|[YLfJk"b#(ow&;;Jno)eb"H1NPENiiٺn`iݓY^DRiddH&rX,㒀ue${Z5wlK ***C gϾ+ל"jk3j e@ӕ$tшظx.EŲM=U-|()<=r Q8k7n\칋WSTPծrr9jvbqΝE"Q?R  +;G]OWwԷ#>ZUU$D4֦}ۿQVV.Ovk{YiPӧV3,`<ҲBFuО%yyf;.KO͵멞3=Zӕ't?I;yrgH(B124iZV3RSS=*ys 0ۑǢn 7߻:XNv[ի&}T!$Uk۴s k,9_+j=8g8RZ! RBH}`NG!9!tRBH}`NG!9!ԇ9!PctRj|.iQ"]!$~ӬCGe>|${ͱ?W݅ظI@OOoߞkҚ b|AKbeasrg̞`|kVYyT"ldhh *a#Oqqj!8r}gE~^\ fy̥WiZmmFg::0:pܝ?vL R [/EapR3Xk`xCJL3d+F.$.\r@x.곀Z:ν%2\XHh'mԣ{vm@II ?L4iJCھ9ήb'OcŞ9'Jf;;ogyM[]L\z95Қ4+[gIΚsLLvN_ĸLogm<_>36* /gbٳ]n޺;,|D)ΊrST$;bq鳎1N4m.\kwLj߁þ^cMRt-'wqM_ODY0g]ֽ"@ "((.RVT,P%(`*uP'Z aaHj4@24?g{s1bFhm>(rVz{uڤrrryn<^僇Gضkgdt Q^TT|n~LTDVz7_ܹgr}<^噘ؓ%%áLj31QiIkVyegfg0Zckm|xyQ&FL(~W"ND=}cF޼5.55o$[kPTT\]]q.qu6f&ZYlā`]{spwWVƹaLC}b'\[--}S N-hqy>-7fƊ dH\psP(jjN- KѨT2l2zVX(6-={RFR]6+2%4L&hk:F^~1g~7}l<.pTPP:;^Α6`cez"\is3Jo`cmA&'M/ m-dI_hzС8pC&|'ϞIvBUU@Wӫ[^mGJu`_^WRVnix9[ w +$(((B)i·D55Qr@KKSz Els_|̵kQr,++6@&`"W6׶7o;J?0jjj@YY ~4d뢯g**P^^.~6SV^Nuߤ׮^&G5d0 Ի4_\]N&K8܁zr[<ؼ\KKD"ŝ:S"Msލ$R v.XYxx062Sh3O 9|/466NMMM]aÆ >xT}CCy2@+Wp;b׶;;Qdh |PHP  w+tRáa._ihh g/f:;IDۭ1Mܟ4MG9yr_`:]mFUկ~ݽ/܆J. =VK| ;^S]COc3byo@PC}}ӷl|F%#"tU+[wn_~6bKuM͌iSJ{hϾUUsBU܌]c&wFoi6rYa!,7d@-ޜ2ib7Fǰ ""7m^[[7b!AÆi-Jms4$H]cgɶ;nƟ|!;y!`)q;ܺ}M[c%zllzs>=3 (HH'Æ̌'o or.4d|11mjkz-豄,[k{lR .|^Tlog3sގ}׶Voώ&y 5; "PF /^{A!ف9!dt]EBy:B!$;0#Bs:B!$;0w*nBݦrހ8ڵ֬ɶdsʼ1v(ZC>vUվ?44aZVFuBڋ9*7)%vf}'k)&8gǖQFvQht~OK ?z>=3\Z}B-顜q!kW_Z['$% L:SԀx--]QРo7b766x̬̬B@`PR4b:bOlR,̛d;yܻ@J9{ؖ&6Ծ[z.lţ}u(>}i"W56)\oǪrzޭ IVfFoA4 b͹T2m %9>6:",/*<|dll>Bos~y31Qq'KJ8C;wgb2Ӓ֬򖈶aeKJB_)hӧ> y.{WGd3q@pcZ!ݞ%f&_^SS7,LC/slZUfv|G_Phbn>vhoO̙m٘2d{xz@k;w:}\G7r| ͎GFG+7 #V.RRRb[:8͘6euoGNJT]Sb;Miޭ|^B 'o "Bs:B!$;0#Bs:B!$;0#Bs:B!$;0#赜.tTUUy._uqNl$9z\eR _~JaÆv***Y9NDZXY˖z|و<|~<{ B nL=FBRFGa'<|TWWk׬6lh7ͅ䳱lںg&mSz]8eONM9cUϞnT{X=eij12jȞ <.Fgc8g7n"$z FcIx̬̬BDyUu]lK[٦&ΝUZ<.xbcb9g,&Dcni*Lm9]]8t$}w3( T 44"/51Xި-]΀ef3w~7E}lHÇnm+l~ظͣA߈o~t(=k&iЯ_?99O+~BuYNϻu[655oΥwvQPc#!wDe%-ϸp1S!Ajj߹#wv{TUUΧ=v8qD!˗ӧNaIyޒJxgbbOp\{TUTӒ#Bv*QU>xب9[J[G|kY{xK|]쎆~/^47hGHqcFIg gJ*>U7W 4?95 ڬKNWVR;f o޼=/=<"2)%/UVV:xom:455k7&>!o~z(OUVVmDߏB}. ޞn+//om``lv<2ZԞFyn~{f߲ieZ:/7f4ذcf M&&:TEagxyc#GC>thODrY .61f>s0 go۲1!1ej5.*.:x$6CCs*3k;ѽ1]H͛l. [6oxH5-%xm!zwvo>YA6_<,Q~>=3 (HH'ÆJ`!NIeF[rN>[ 55ѿ??,[k{<@=io)xoʪ~ʘ~fE\\z86B}"ZZv!ĵ#0#Bs:B!$;0#Bs:B!$;0#͜^UU|U/kD}8=?.x2aL}#LcՏ=^DT:{"Gkd[bm9e^z3.y}_ شe3{;>n=tzt踺/. yN׊S&M<qnώ-F쒉'*Ԧg ]-P(B]F2O%%Cwoˢ?^z\d힋tF A\jScӬ3V.TTT_ȭ7vg Flx>%0(^#'x hb799O|79;:DDFWVV 4pG~ekЉ`?^h=_zeI) B.;Oϻu[OJٳfݒ(|Q".!1xa/ǫ<{s8l}|>LLTfZҚU޻oԪ1ϸm %9>6:",/ ߹#w/Q/0=])'vɂ.ƌҒh`^}CZe%%T[[l)FQ.d@PsiF&uGe |>n EMMi~rjvb7J&MXF w. !:xNWk^>POxp,t'65P(. ZZ-R=]]rk2WTv4b;{B 9|'HiI"NTW ኲ? Ơzr\:L&růk^޹3 ꐞK%/_ڽ7௿/^PJKEEE@pHUu5'OF͘>5Q@Xs:455{xDdRJ*_ Ҳ*eӦ "cƬN]BIC9]߈=dwcccd:-_t]룡Nwrumd:\|_yyyk;c Wg'̬p-YVlObW(83e|weT]BJ;MiޭR^B 'o /"Bs:B!$;0#Bs:B!$;0#Bs:B!$;0#[9q Sgv\@}zhK'~-,,GzeK=>lDL-ŃBÏuAݍi8HHJ0Cz]wȣjJBw_y>951~W={VSKqN׊S&M<qnώ-F0|ݽ1y.1.-)^GG{֝B野yznEAJU4bFfCğ{ؖ&6 u=p+}l㦨o`PR4bpb%r6]1!)rμYLǽKz.ly74=stbP F'OXuYNϻu[OT--}SO<)[U=YR9zsRUQMK=ەD!W#c#fqڣ@$FGC߹#w/xvDe%Y{HH_iaȌ[F@ce9}ܘjӫ[^3P[[l)FQ.dt}M͛?ny/P(*sɐ_r͕B:;ONM.YFRd Yajr.-\Bdvv. _O^ہ HUT\<j7668nSSӂӦ& h_ZVZDJJ--.-B Պ+((Ӽx\.h5 c:ߍ[7o1|xoǂǪs&1POX ^1~P(//%@KKD"ŝ:PҲtWY)j@ Y9.N۫dr ;PO8\q[HӜw€ 㣖G߶;n7+Gz";,p?v򕆆@p&>bv @P2.d@iYYSDEEE@pHUu5'O;^YIiGÏ74y&t(*OȤT@@VYY<}ڔ#auuu<^dt ۘՉZ<4m!txIԽXdeǮ}1#z","lĤaC7,LC/slD|hӝ\gut~#`˦ \.ej`bc&݉1p-YVlObW(83eNۯx6o𕗗s006;puv\NtoLQWɫZG߈!"U״]%;Miޭ|J^+|zf@P𑐠O h!@?,ըU=}e֘ŴcbMQQqOB2~@KϋB2O@> s:B!$;0#Bs:B!$;0#Bs:B!$;>Ĝ^UU|UoGDn L:SzCH<|fL,&vS慬.=R2?R|`ӖFmdjhbg/?{3+}={V #Aӻ;^+VO4llLvƹ=;50Kzu "Q~_,ĸx[w7oVBZҹĸO]ވQoOlR,̛d;yܻ{"--]!+ҤL_)_ ؽ7mikbnh`G{;[k%$itǫ跍efefmr8XR歪޾˟mik2[n R>=ݡ0devsYL*UQAA؈ &K"( ƍ!DA[٫os~[wVRVJK?v$L|ccxgbbOpGFڣ@$FG#=[vDe%Y{HH_*a=])'Jow֨_&aJ̋ I)ACGkr1%wb7J&MXF ūjjȽh!BQUUq>TmmmZz4F:̳ɐh/_JPT'Xjr.-\Bdvaw.>G:ۋx`O*:qcǏӻ!uDk:455jijBxҲ2֒h)Atp,t7*)+TOWW#[ eW f`[Vag}G|7nݼaظ|3gLp_nܲ}~0 t{N^0@(y $hh@iiїWYIP(($HqNd0 Ի4_\]N&K8܁zr[ yykin|h\#r۶sƾ嫂'O !HZZV@|G??nmeޞJJƎ>~͛7:(Jƅ,(-+:y(WTT4a1_'O*OȤT@@RQV>mʑ𺺺 /2:mklm^6cԐGAccc?t:(=qz{62]&K 絳M\.yђq.R~}44^m?Fw:}\G7rrm[6&$&Lf1EP(41qru;f˂chݼW^^xd4CCs*3k;ѽ1]WɫZSH߈  =.f3ٖfv<^V)B RuMM7ynwˢ?^0Bw'oO_E!9!dǔ?1/ SNG!$tBHv`NG!ف9!dtBHv`NG!ޜ?BMxB휞{3/f^"D aWUUy._|$GuKiGkd[bm9e^D4 I)%ޑx;644wm &"~j߶ IrO]qIE[oG*sCGZlf@HY9\-i`o^fNG>q^;^+VO4llLvƹ=;5}Iv-v˿[9kegfgFE666践;yƌ&dvFz;Ifw|ꢅ.-6Y}#<^eyq.NSgΘ|ճgͣE}GLAvJJI$҈hWo.p ;&$XΙ7vvwtG}Cqv `[ښ?@ YV<<^oľM`jJI=/ /\Xő[ }o#:nmicig[6m4Lˢ%Nǵg}***4Rl@341g[^rcs3knٱ3&)EAN0Ȕma~tu=S>t$}w3( T 4Dh5 AiwDZwvޭDW#c#f^vQPc#!ʋ͏JO;d0vo|d@LLT\ɒc݌i͘go`cc\L0^#.d@iYYQ~#ő[=[8:'۹{?21QyQ&-mU߯L̬vﯩy#ѠfꚌssج۰IUE53520`{spwWVƹaLC}b'\[--}S N }h:Ǎ-ė'WzR(55Ugɩi%hT*L6a=+,[[[l)FQ.dUkVz<}r: Et?1˿tc#CmF՜r݆Mc˦6_86+2%4L&hk3 _zMi*mɔ IDAT𱳱2HK=tyQZ6d2y҄BڂL&O4ŋ6;J?ʫ  * 3 W]>d`ͧ|B\&UU@Wӫ[^l{3/(߯s5ڕ[Z+^jCC[D-MM⁂P(655-XNmFcSjq\.4E%ls_|̵k1d2Y[[ h/ru^axf >GBMM (++@~"ÏFqt]՗_|DB_5q浚 Ơzr\ڽ:L&pt功7OΤ4I$Rܩ= QstqcGOY3eUz 9rQP(y%ka]}I%{KQK6Z<8QI AJSkCG}o8EOkm` ӽ96«n;F"Zԧt) JEYy)G*x1KDt:=.hjjRTT4a1TUW/xhmp횕Æ 9DGhT̙3751E-3Bd\"^WS7W_5F1}j|ɰaÆ ĸ\rZI 8n,xm;3MMF*+n  e@=p.,bv@}AL۶lLHLfZb=<G_Phbn>vh puv\ڎ/P;62rF.OxrO]BVk7;afb\[Wg8[_bGn 7,LC/slZx՛7[9& m,ݷ05is"yѢf{.0~RE _ڡìnqDa!l c;v{sʤ팤sXL ?i`l6lbRHPడCٻ[@Rko-['_z鸳 @4naӖXvF ^wr93O  >ɰa=3#EIeF[?"\ni-σ>cF;cԈQ-Zc YL9^+ؤ=x,Z۱ >\R{/#z; Bs:B!$;0#Bs:B!$;ڛ?"ЇBHv[[Ze:O ʹ5~`-f1ٶ2/duExO I)Ow?t4Ŗkp:lseђFk>z\Zh;O?n~N׊S&M<qnώ-F8Qp:W{; i|DOuE ]Zl/Gxn &=. ]2'&Ϝ1s*bak8QѮ6z`PR4b:Un22 9J\G,̛q{DŽ9f1n? -mMm 05}{&XĪ6GN-m/_Spk7(qXrKKy74؍1g,Me1N0Ȕma~tu:_>t$}w3( T 4D8)jwDZwvޭDW#c#f^vQPc#!_KH>vD߹#wO4U=YR9zHTVVmںcF%%v0٠&\966lRUQLM :fGRؽ} dgfg@nI 8s]Y碏1 ߮8ykۻ^pKK_|9}©'yK">-"trg3ZtwcEEhi2$WzR(55UgɩiDU}CZe%%ѲKѨT2l2zVXi˖h40BVNkGq~-6r0L4aP( ɓ'M(~͎KqeeUÆUUƙN2x0O>!.tj -r{3/(߯$[Z+^v$Cj&@AAA(mjjZН(ojv,"RYIyek  m6+;6?,/2hGE_}J-%TT\G[[TXV^NutcѮN$^Ǐk^` w!+ix:L&pDpE<$HqNLɞJJJ,3+ R-*=%AԠ̓Az)wl~XD捪 hj2|Vy{{-9x ci>4zW~.ok7Ə#'SuNpLJI PZVVYY<}ڔ#auuu<^dt ۘMX!U Z:ԑ|F=5q7r|MC d!ܺh{sʤb((ܴe{mm݈ំ:y4*b@Rko-4DOnƟ|AH­w~ش%)>Q«W\4'˝O  >ɰa=uIeF[›11mjkz-聄,[k{`:ty:B BHv`NG!ف9!dtBHv`NG!ف9!dtHBÇ!$;--{\dԙ͋Ps:O ʹ5~`-f1ٶ2/duExm 6 ]nw׊5FlYLcN Iݵ$|}gYގU{|CG[^|Nǝxr.,Z2X߈bG DUxpMmǭoZzʤgcc3ٱeԨ]g'z|tp|XL)gS8a<}?uD}ݻҒut7oLb򕫋g_xOz]8eONM9cUĒxpY:Oo3;:Z+)'H#-Xq.9YΙ7y:g5a)6,ўǫ跍efefmr8Xy_05aM|.Ռm,z88yO@{oZ(--y݌id2L&2OqKKy74خKbċtY8MȈ04RO-ѠͭbOlw:Ĝmi{՟C ͬ^fDŽ9f1n?㍍1g,M0z~uBr7,:>*U҈iqyд;R"?cȻu;mǫ|(~}<0p瀽2Ecmڣ@$FGӼ/QbjºS__eeE/FhDZ03IMK'ӿ/ǫ<{s8 3vhunyKd033R3RmUϟbosiF16b޻H=>s eڴLeKi4Jugw!+H$ROgع{ܰuuuikO#֒'62_O^%^֓ڂL&O0^(Z[ɓ&xf%hT*L6a=+,zС1 ~ q%;TUU].^NnyGkiȽWgUJJ--Օ2zo/2hGE_}Jq`]hpSRKKܻ孍:u陮Ύ3X=p8ܦ ݉MM   n a(**M}g32׮Y%^+ed$:ߍ[7o1|xy[Oz '&@AAee%h&j[[G(@yyNWG]9H+&׼V4PBV -/BP^^J8dZrQNDdr Ko+sY3eUz R1gr$toml4a<hiiHS'{#n_{{-Yn0𖍕 c#úɓ&Wg>.vƛl Ӽ['_t:l慬{;nݾæ-I^zmr66FCC9|$$az`:ԻRX79u.4d|11mjkz-虄,[k{fFapb{;E {;r{tz;k/!$C0#Bs:B!$;0#Bs:BɎtO!t֖`I,okエ&lVzJ'yQ׺{/c\b\ZR;bܘ|DOuE ]Zl/Gx.:=. ]2'&Ϝ1s*bUkPѮm`G{;[k%$it%>>.x2%;79;/S862e[؆ x̬̬BK---y݌id2L&2O.@QP8la3wh=Za23ebLk;dhbuc|`$C]a.i@**((1<***4RzwZlN-m/_Spk7옐b9g,&n Fl}#vcccLKsSb~%Б0OY͠((P֖FLЈͣGߑi@ޭynU<^僇E񋸄a'Oq8܀?ݕq.xsvQPc#iwcFp[n}CíwOEJOzW;v#55d%XvA/_|u**tLӧ=A *DŞ,)=׈:'֭Q#7o~ejefmMmWWdK;f݆M*VAxn~LTDVz7_ܹg&{ >L@nI@k]} --}S N-/dX'w1Ew7VTT&|(+)H$UUƙN2x0WzR(55UgɩiHϜs^sa'Dx.qhd2لe(TUUzH2kb e3*%e%JA8$EX:p{p 20`рnlZ~ӾJ/^/׶J4xwo~acrFnNHϝZ~[T5=2,@Qhb=f܅ENtHD53UfowyBĢ-efe pa8ʕR:0$ISfUX:pN @d:=@@d:=@Ѓk>ޟVZ`)npOWs?؄8K+F'uӧ93ߵϝ il,p}m_hlOqIU3~2;gOι/;iqQ Y9W؆Kkܚ̄ ٙ8 !MG[S׳}d2)ʎpP`> di|>f\m_jGU6-6]Мox>c.kkLi<ℬ51Fljd@+DsSG2(5`z L2tz Lǟ&1LIENDB`gammaray-2.11.3/docs/manual/images/gammaray-event-monitor-log.png000066400000000000000000002354071413310443000247410ustar00rootroot00000000000000PNG  IHDRH9 pHYs+ IDATxuX]𻻰,ҍ4k"" (v&vw(4 **J)ݰKm|Qb=<>;w̜.ٻgfp Ы  d``  ,2`0pYf0YY*O>^^5UU<---g466rTARTT0y_i3bUMM z] 8++[FVF\TO uM!##%.*QuGyyeL&?GWڌjDC)2ee稩u6ھQTHP(Jg6Rm)&&Dmjnny_i3bMt/``j; d2:.t^? Ȁ0+.@P f."~y_1 d``Gp_uVM_@䛶n{l#; ׵=Nk~ejuu H& N22h\^x~A!{ 77z6=̀i뎔W8OYIqC xOmm'%]Y[MaaTEEmF/ӭ `()*rppppp()کIOK888F 矡CBOWW0 i}]<SRR*,LNIMSR;ibqq)UUD"D 6kk=Yϧ=ABBgt'2̬ls~-"H˖.ўbamD$B3t;wQ۷n/(p?qK&22=ytL\C唇;6i=XC{ ^J<5?߲%&xvuuĖd3>~ q[SsNNӾ1aېQW.l;IqYYZ89N{e߲A k\WboӜ\vz1yzi&" ֬ws#ק,`_WWwM[wܹqD"6ĬEƱ2GI{vnۨدC7e .) &*0~X!!AV{LlN 0y9YnnÇIIJh{>}UUQF2 'G{ 3nrSS:3;|)wIO1ߙ'ޱkgBb@WT$?ym"D"CGV\[[@ DbYYyiiِꂂbO5hmIHp"2[0g/sZ[ZZ[iL D !K1@ {5F0{]MMM_)+)Z[Tqxܽ#>檩P&ƷlbU8?ו˸ $$$jjkDDD /7J!~/4@ LѾq·5Ա@1O9WSS~5VM& KJKYCĦKJJ0YC]ı}j ׵LT_9ɩ8'S#ȱbGD⃰8$ a { L&ɦWXM?NK@@kdu(.)F N?wv ~A[ZZxy]ן<}>~9YYasKDE644pqq 2z531zߵwa\+ 11Qkk^FP˗xi٪5FLlN4s܅"zp8;s?}FN"q?v:MN(&d._A9{ǩ3M:H\Ĺ sbd~򊊰HQPX0JOwB$4,Ç &ܒ1??*'OKx8~^="%%~u9QQN>bQqiQ#TA#CfVBBVS`ׂ=oъe]I;ve}"Jvw5wլ_MMͮ WZJx!_ X*+;GMUgg[8HAa˔^縸HFQѱGcf打t:7e?s^0 II5+ k;# `<?aTtTtlBc4ڒwƭ2֭cdȀlyk7lp;N+D-a(u~EXDdDdԙJ?_p1qqb#F}9z?՟{a?7ZnciieBxWEuuk/-[ަK~"{!d0;׼?^pEWN?=ACzƂk)t&u,`hȹe 2`@yEx<")![6yo'$(tzɦ&MMz?s~O |ǟ[FG0+<}`@))+~1A `X}jfVvVNND}|jkUV^.&*ڛ!uSyE7 eϑG wvUUY9ƾ< r$LN9f,(WFP:(p&+d!!uu~xVy_i?bYS^kq@o < kF 0X  d``  ,``1{A7}KK3s>u-yּ=u Բ% TTPSS812`}? 0ʀcV^/--ׁ \&ju!&.qd3Ȁ|_ii&&'O{!*+cfic4j#ʷ,e|g|40[9?1{Ō ߽Gyz8k;>8ss`dಶs|2!g˾W]׹XX:""c]gmyl_ z7ioeRHMP߀ڲcҠAZ[[w?t7ny?***rҕG^Ym;if=qD :Oy:7NhfbSS[}ƾȀ|Þ+((u/BD"j(X8NE^ՙ8lX-M~>/pC |GeU//{v[ZZERJ`ֶYd/a889tzO @wsprhh &OsC98~?2@PTEDDBaMx, pۊ G;x p˵}L^FNVXHO sTU:?mdehs|N~^}sW-'EEŊF ̹ k]Wtf&DNN|Jimm ƅ2L L~APH0UF+Oe0_IEEɯ(--35b:rp_mm4Yd!nkֺF;]bqMΙ5#1uTtog1ٔ`6 Ͼ\}CrꫴÇU4kvm0B(SѣQxkSi;;20Ȁ0X  d``"AH;aA cVThʷ~W(r'=[s.sis#`J=@ $~n/SRB||͙5b_ܦ0b..}!/??/p neL&B5Ba¸=@ u\E C"}5nn_ ޤ/S.N 0t[0gJ˜gݰvQS_'&spr̚w~?CmÃvF}}u%%$zPBLsr9᮪Rv3Nt97%5M/6.D"YYZtЀa~~~;[˾%JJUUNME>!zJ\|JUQV>ڏUIiY#x\)+/߹m ޗݵÇ.|ULTtحegݽ}糲spNЅv5G#F {mݚg'pwo XB\Í7v777_|EEEǚ)(piv33+*:;ami҂373ڥ$VP773y{_52msrr1l %%9IgbbR2B(6.a ii)"(/'7V)aY\R"""[Zjo;UL&W}D"{!! e%%VKcURZzlh⒒GI=#j'O-[@EEUՖ6nVNHBBí--zt=-c&j QwWds3а2LfssK^P?33+&6U*d0t':aɢ~WʨzHRLfIiiYY< IDAT9BH_OZ@`Yy9N3XM?2 ! yy#i4N2i6?_Ehhyzt? 0ӧbnm@{ilce$.. ~H$]]'ϰ/1#G ξC";MAxǏvwú{w:weΐXR\\ly.^ZlUss牣ɦX[:zާϞ'Jv H~ ַ#}|kjkINNBL&3;nߵ/c&*[gs>[mldPZZqe+5Vh4s#t5B"x) C&L7%7/oæmE%vΟp]wQ(SVq㤔W.\Zjy#M;%A>F6]:akBUO{󖋋8kt>?[ffֱ'++TTl\?HA!1̧Ϲ"(8df7PEe\W,\SS}bt۲]YY)$&ko@y_}vUN56]jRPdt]7u/Xrۺ5G{ܻu'^xI%A1w7k]WM8f/4,{IȳiNu5ըy )kO[Q]:w/7\aUf&E%oq밽ޫ?mlw^F@ nݹWRZJڃCSͬ--c81鉜T... qAAYW㓒D?;k??77:>1Mif&x<^QqGpݸSTx\ vl$((=)|-znAFZ@_7 cg;U@@ϛ㒗__DFZH8Oz [6m/]=oȑ7qz"%¥ƈh{۩URWVU7VTT=xd!Vqq7p݊!C4X}$ۯدtvP|Gyy9Bh Hk۲a2{@$+*+K_-UU 2KHP0mjN7 Ztl߽S!:I?J& K&(*(#L&ݵTLYy9qQq(vǙp]rukjjp{D6֍XW&n i4BMMMuu"""*..nY;Ajmm}`De妦&q/Tkqq1bE2>NAjk늋K)ȫ(+y9GmllnnCYYS\\WYYypH(/ P!$$(XQYUUU`0:[PO7,"2/d&NL.**޽܅/^b[Z[t=niAR'O>_2OTDDKsc&MԎx``0{P]>pıcOB] b2 5jDm]kQ6oknnF]_Pt҂CB^ (\2XH$n\v5{cvN kꮹy׬hXKL\s\T\bmcȨoeye㦬(L&FWT89NKx2qZN}+[GS ׵nUUߦN>2W|u2>f.^jȪX G1 }ut+*-^fmXVV x9gj1b,)+BuWߦ3aٹB(2*r77i꒞<5~j`-piΆS+..«ڰid+7oc-'<ϘY.X`2^~c~>N!I:Pdt9.""sf@߂ߨmvcs+Y:P\\G{9GWtV߿'$y++˷=j$w!q@OwX^gP3DEDV._2zH<9z$k7&6dMIO촹9,"V{¸d]hlq9w#񭭭>Zt銿[w70ɬy[LLT^^!4|?VS̅䛷qfOy:7NnnאVRh B(8T)%%?1ndc^L4Q;)Q#h4<*v%32n#6lvعuAfVǍ[fÆLKV3~֥+4$UĤ'g|PWS}-/pD}+ωaXL6{zzEcnj1emdtk_#Cۏg^~!$*"S嫎#CCG9wCs\f P29ﳧ_ܲcMvF[LA^^{¸s_ysᬸhQq ߇Ҳ;MEMM Ǝonn.)-UVRĞRQV/(֋`UUW[8[m;[O?V sB$IFZqu]MMwz|$aTiiYDH}:9`Q#~n7햔@%&=Y8{KͿ 6mvl݄eg]ܻsR$C3o k\BB(P[>n+׼{III"t֯]%""sΜo2c/_ϟ>ID1\LFLPT\~-]ÆA+,a &1+hmmbWmX-l>qb$m99YaWi?CyI$DםM&?y6HAi|XML~"#-enfee]?PUQ~!s\B))ii:oCB\Nj1 E\LNRd;NSS//k!z6q.3BOVTgLw|!ע.^@ d u=F=Y8o4H Ͼǫ Y|z&3ֺpMM!Vnii-ztU=! /<jRS^F淋KxܣI$~b!9߫3 ;V[+e% >~0n,,عK$$o3&Ncں:k}_s1UlݱG\\,9px߮>&jpboW>{Ah4Z'6P/36?~ע>*.-JII V ϹҲ2 JJJTVUpZcR^UVU 2XwԦzenfA`?jNN~W}|,̗-]cۖ}iiݵMHHJmd5Rn !D"폸{8z;GIܤںz4'J?n,BZJJSU]}AyV˰CD455%%?]tť^xfPGe|\ql$ko~~e+`U=QRRj5[P(b#C988BB׺Ngk7I$Pcc#LyvFߜUv5 0>)cdr~A4Y;:~%I$aHhx؟q&U^1HAՙx𨻔$h Y.)N>d֘Ȩ/_1G=NJ~~mmwĊQYYeK BA^ʪںAB55Mw*}xoEu#~>UUTkjX現x5b0kKm;*_Wi'<Ϝ:qLC]-%ՆM.]8iim}eAAѣ}uXSZZ|FSS[Nm]uM͚6316LL~y۽;d[ee̞(;;vg4;|ɢ&:qB=k樑*uEѱ ܾ !}MہW998ltʪK{4R\Kyn޶kV9_KTw=PZZ`ot~]:{cEHhw #8$` @>veJjDHuj/]zϹ3윜 Aݼ}5G;Gd^^^ !&02ZUELJܽy k+cC}s{g|ګ&LЈ($(XQYUUUt?//_G{{OBh8Ȩ֗Ç }Z ?}Mxd2sscv=]Q kQQ[֖5(>ƒI:ځ7oʈ2;;%Xu,$##tbrhOs{Ր;xhժ*J'OEYM Id677{!$//7d 3 d`e߫et:=;'WCvP0R^3 :(ܡC4Bfjk*+У[F nݹWRZJNDFNEY[ZDƳ24>^~>ܼ'O2tQkK BY9,m~^WBꪪE YId2#3;l۹EP@޶;vjF3gKh}h` q{OBhX-k:x<^[{7o\ bb{#oX*..VYY8)EJʅKBXB̮oeJIJcǏp?D !D&;D\\NWY`-  B(*&0 ᪫kk2()-p_K?۾YQQaݏ`WMc'VN*z9ZؗS_O"mhGʧL{Q_KzTs(E%/~gc---|w+~ײ׺~,㤴 IDATksr.\-U8k-Â`U;&Y4vZdՀ7o=#>VSS{~]{eedXOw;PVF!da?} {wW^} Bh=4G:w BOƪ ZZ[M-u]amiܫ xcMƎYp]XWq?qdDSSWޤ{+XH ^!E?tLOqrrHI1u?9Ϛ7k"֗HObjD"QEY` y`;Z\\ kчRRL&3{PeeUL\<6U=S""3>f2̗h^"kjjP(#p%)t谢Er.މ/s'(N\W YZggi4;  r󈜜22[7mx"`C"L Coٸe]{XYr51k]Wn߽JVFm&Ϟ53qctX9dÌVZ#3=<ϸ$**O0>zxQ [L6}8lѦ k{a<W._b:Q9Yk)m:tXѴx<Ђ%+( B]fbҠxpTSU=|`/bvG*@~ 'eCp5@7A Ǔ0X  d``  ,2`0@7c_Qk3f/H{7֭}|;PhX¥+۷hp~Ay{boP(szkohiUVVa55OfbaZ?0*&!DR_glnm?ƭ;m:oݹGK[BlæmvVv[睊E566yjg4yioxDDFL976>7"6>Btg83K-w~׵nZzY썡NL.**޽܅/^b[Z[}s!gg(+)rAj-fDm~>Ξ?N *%&?9"%u||""Z-3ivL'n'DO8uXd}aa'!ޮD~o5NCCNpn577#.z_/( uuWiA![~WPx_ʀyxy.xyH$7[c;՚1;'҆5uܼskVh4%&.g ^^^C1 iwsIɦd2[]x4BHEYIFZkQOWtlMJUTD 899UT[-u]!%)'חWTа=~rʹ˾WL&Bظ k k;AD_V`1b]{[[[t+*-^fmXVVNfb3>fZ`++6lf4b}ۿ2&&ƆQ1---b^~AiY:]nb6fӬ^`vc,j~vX:NQJ11AAD,T PJPiz?\\z3sgΜ{̙COG0spy-) @sKKu;Wٹwc7؃i.\wtvh:uz#׹ ̬'{ke+lgFǝhkI)zfU@Rmf'XXpG"'Mh7eH/)]C*yydG׹ <+yn0%xgr\('/ 5k-&ꓔrcٖ|V7|HÏ:L5h7JeMqVSUq9L{z{ =\>>^A}rP~/N\ n-PP~ ?$4_ s nݱm 痕ë3V",LFk3?X+!a2X,VWG{vV]}RijZz{Gh1 K<ܑ 7ma$͌];䙘VugiOu;$m-#|.?ׇd?MwvwO|%&M<fdnsD0D88VUk֝:˓斫ד @[{*uVX[ XxR?M[hܷÇ.u؊u[TF)g^khlX\VFf3E$%$dLkISb6Fk:O8}]JRB\BFk?z\HP@@ܣDž&Fwն27;K&XlQB K_ k]B$ݰyuV^{֬[ikc]W뿖sީ3d2%'R{r/^R~1 9PTn͔mm,7ܔ^g陷GsqqԼIO6ܞUB"8kN4 {GHPnj*uly䓧Ϣob?l766ݺu)1QOohlTTG|ŁݻA*6m _ysة/Tիl-7n μu@7W)3gR`viv416LNMO8WHt<jNsqtuj%oP^l>J2P~1_jDEEBm^~x;c斬eK^|m5!>Dccӑ![J';Xڞ^G9yVȮ3NZ1f&[0_$[,+//h?Nv?ђ\\\z:X,[kf]]Sn\0o/~SVv.=i2oC3k)6oX yb6֖`bd(,,QA]}W{ |=+yt07!FNqt@!H$RZz50vMgbSc]S Nܼ|cCii) P^bx<Ǜ ݷX[b0UQVy$%'NbRsgJNMSVR(-{[ByPPPd7מ"njF#",՝rGZZgr8\\«wZ,3ܸi?yuRZe{3bw)I 6669Y,|Iid;|V`0~@y((Լ_`iazR$k+ !ONV.|wg5cFsqq15{^Z)VÏ VEyz~?oh"E-e|>4nνA|B$?|$< 1Fْ$穎J `mi~ɲ/'#T*uKȎDEEy+-BxYy붎ΩN󜉍6Ul,""t{wrEضah@C@@@O/""QRI@Es  NrZZ!;wp8Rj*6#-U "77AarR677 ׹.D~֖aՕPS'Q(踳<"d26IJ  0 + L}=ݠ 6Sܼ|qt!BZD1 QQJhljb(+(*&jmߵh/,d 1UZZ*x阸XG=|Yac=@~~^<2}"onnɻ{3#0;mmLA[ڮ'߈:FPϹw7bOS"dXlOwӍb>(/nܻkz}Լ^R_Ow2okKٳf".03 6›޿ c?v"|3Tj\9C2c`5PSC]ZJJMUMXH^Ry9% |޿i.6 *-%ٳg%\=ܧ:9gގjhh?VsNOaaa2}VX,q59¡q1O$IXhпijn' @ss 2CW:@"yx;9a0-!}tBYl֜Ӧ:R&UOFaP󾶫$ Ŧlq8\ ?ʉ6VI)7J͚91)lbVv{߽:tXpB\99Y{h}bf&L.cdD)N1})8w󟕔o Zf_$۶u3aәe2y[Qlrr~L̬DlK1] ӓؤ4stuxE'䥥VT"ooVH 2 {C\>0d&M uQRӝ' pm]݋v 3CaCXTu%[QkV  !|tnbግ&0 @{{ǻj乭TѾ+#j~8[ׯߴwtlڰwkE*MMA!;fNwNr!!tB3&Z;9%xVJ^KJRjk*Ӓ G%ĜVs/^#]}߼/^FB82s;;;%**r3=h4Le%E,[{\7+Kޞ^ `?57w@TTDC]n: U'Z[iL|‡.tL,{h S_PPXXY999i4ar(dSGG盷C9yp035JNM# 7}R 'OG A%H}VWS~F7 `AQq0600c20w-)%&>Fpjc{[ddh4ZrJڷoqy׏M2;7FUVVe335oq3J՝=wנxYXqM _"<$~QF$C@_RR+`ݤ7VT;e7!ٶsDk5~J G{+גJhe0ɵ8DP^y}5 T*B{[Q vѱDbkDI_G!|>쭩g Ȓ#L-[Ҳv3pǹ({}^LݼucX>:ѱ KX# mLYpt|͔Y5ϖ$LCP>/7GTT8Pǫn\cwsK88T*XC]@4 777weeUjT`7{ x N~Vey]ٷkqg@EYy׎O ޱ+x.$\^~qLDDy@Oȁ}/]GsYYײ%{;Ϝʶx4ZZ@trWbҝn8yx"X̻o9H # ̛▖1vGpJJvmھu}6vNB´NUUȁk|vw3''礉J#vtvPbV67h4 cKsSxHTEE ' s53pKt׹|.ӝ?5z,kh8gCb>ЮG"BvjΟ3y ?rp{ OwxB`l8aǮsfbZ8I&i?g55<y\\S~`X?kф&Ɔ~B444&FHx4ӓb#G,X?2 ¥+ rchky'49Ym[7ٳjtbq&ZϛaF #w3bccst=9UC]--#s tk!Ky Ss2w3$!vdZ0F@P{.`0`#jf֓n q(y0 ; |7NJj!AO t埐xⓧ%w|9_CSSnEܽw_Wg,mѝi|i.^KODI9{zPx,Y~p. yME}-}-y{.573ںfe ֻQP1¥^fs9"ۊ{?}jDڰ)HP@`ZE6v^>lmkA0/_M-hvD䉶v4 ? ZJ++룂[.]ݺ̞1c } #,$4^G{LP4J ddG)+?b7uNGl!O5fˇPPT\UU=m#pxQ__AA?}=2yPԎCU{[QI&6(=~L8dƴ3MZACذnc&uc%ӝlؼu(_Y3.XL"t7\i,y:F ٩WΞspΘk6y*cH(/_Ov;ɗz5L!DxгGo>qe f˟]$66HHZJVI1uȓkךe:.K|p7s#~6LT~ӡh |lg`~F#?tF^ZKQNfޭ[cq8$uȓ\u~BGB:ڈ6`8ߐ$, r2@{E+0|ޢ}fTҙr],HO[t}u\W+`0ywRϝY8mXqR̺t֑B!' xOTRzwATy}Q f0jeŏ4MfO=ݚ:PS3p'+ك;7O'ȓ%7\kllZ<'\zFٵ}C`]W=TDDx?ط;CPvRRӮ%:vUlX1r2{ yܼ}uwĞ=g9XJ/F'p82CNxQͣEbs\KH~:=Ûgrjnz/8gUVDimntZSZpHG9?t1S h4ڭ&͘Θb)dۋ"r [vPԘ3a$vҴ?GDB GlyjqVbS=Dx]|?iC=2P4;{yIQRI0QI䄨Wn. Sf=}[F Ŀ,Edef͜dk$xY¢Í̬OPSUҲ~k6v֯P))-[dQt!FsV[Ws&'Ƿh?Q{273!\\J۟pt'QA!݋ tg]Y[Z8L?wkNlډ \7{6۷_YTU5n/v ?i)ɂ"".)(,fǗ cFc0VVV#C?s #ΗB5~Â֫Ǽ6bq8*BSdCk;dT{iʹ35TnjCRi4ZNs3Bx{hWOW+y2&Z:̠g/(}} -h`7}ZM&MV"X@!TDZ<&RwgH*Hȯv Jbƕ}C$,?_ 2qFh(9;Z'r%mNP +<8M m-MmU_L=Ē\Z:7.Tr"?:*0_hoR! ~2 -,/cC@LJv%a̓bz91 +[k5F9ZШJQƧw8q|lQ`ea6m6ysrE?KFZjŪ5U%xJ^NCc? y/""wdiQ F,{[wئO>HQ陷>|hԼvme%D ڴ5%5isyQVb/^=Ɗfae516LI>wpդۉ֯ :#vP|Vօ 汱![m;EKee4eӆuzd29"č binj%;;{}}BvbaayTZFf__zί V #x |wxsUt5f{ItB +}Wo_4\:=]O|ZɀdUʟ_>-hki2TS^+8 7/ߴCV̟㽖8ԯADB_}㡁j2xX\8V;>}=z/1N_Ι6MGLJv֨}!W'.AI>yG p,8z0tǫC' 2$`0_{$m= ,l1LL@??uc{@!1Ie1tbw7UIQeyPx;q;;C 24R3//vQ o(Q *dSTRޔ>x\{՘csWQ1 FHL]THHc0G ܺ-?3(? D$ˊяҞ`:ғNe6v'F~tֽ-}=HOp*1I?Ca!~^?yg_h=v︙-tݰy\ $im;L6l 3:|]]k.gmeyy'/ggg?MwvwO|%&Mܻ$D<=;7/.!1Ay[^47C#SCG<" =d*/Ï1MA^ЁϜA<ºjͺQgVzy"zW%R!Wtݳ5BhO"kP]IF=J䩬l. \wh4Z; e;cn]"R2JFh͍H՘zVݯ6?3UF# W~ m-M/$*6?\s8Y|'bnڵ6bOwӇw/ w@: /=J&ɒ@_oimn||Ko(7Xiڪi֮@%y[ZAnGxOqrЃh?ιX[Cў=ʯ{WjspzC]LS)f63koU;eϬe*K,\8%vlou5|hoVPfee/7\ +CGX!JZ7/ƑHޖgbEk7:y /;;x}Qʈ/eanC$74>ylelll\\\+/ꮭŬ._ںg%=.pY9ZZfNHI$(mEe]]1'ŵ//Mќ2ogդN4OG2~r26֖ FUeWj~:; k`Xa!O"۶"/n.sf 27/_\\L =*mKcDEK3؁F6vvqdU?tU^Eٵq4;1X68J1 ELJyr]OQk:FG:$=7#q#Yvua1h=ƺ#!kIcZ]ɤ6v}"k xNdђ!10n{t># k~|3IEÑn3mzT4xSUFgsN WQg3MVcX]cK+4w)\|z֏s2}KQG#HH-YŽO6vvzlx@?[/!2l\<|S.2  zW+ Ʀfn..dŨTj{GGK {P.*w_ss3p & 1YOlmxyyc^1?iʵ[@ qqs-Y1jd7+-{kG;1NM̍O$}'骨 RFbk+c%Jmkk}= B555ԑ*@@WwDVNgKwAR+@cc #&"e/Ξkhl`0u}k!?E;^`0NvדSj%1uzV%$D |}waK 1d.D7*$5:q:&.*:nr%l pa/~c&0D%0$ A՞`=1 0jHW҆'%syбw]@ρ]]]݈[__bEDi4ZWwwoo;ڿ: KrzHgȔh  ^,Y %%9NgGğ@___͌Ge>w}4BG657X3 # ()t+zPcCyjޛF IDATj0"++}ކM[Oqss'#q6mS͌$Z F546H޾~>Nv &pK@A ".O݌4553;^hnnAER6 ( lr۶n&[6l:l¯,Qmi,1_Cg{kի2$٣wr~\ W%Eʻy*onӒYW#T(Cy$xOOOı&%)p" L<iu5Gi4Z}}CCcl߼A饥xyy)dRP!""'_||-ֶ!ieUuS#hٹy4*T+es/^nni!+;7tO璅{??@RBv466]z{ϕ ٹ L+*Sd2By bkc]͌L'OSSӨTw5)iVdow&&Hl%qgcǎF[o((,b(K3Դw4-;'orBchou2BLKsӘ]]$Xj&GB";::߼=KJJs Zx㚬(aB"}64Ǫal8P`:; ;8q716]0̮[wgc$D ,_Yek=aGg *!.>`5/Wo6 m&\4WGɧN7|r1UUFY[Y̙/*"r6Ӹ+d[ CBv ٩:e/Rϯ1c aw;v2wփ#fBvNMK]oϑ`0!As,>wᒫˌ#aᔔ[YO0-2cZiً"3;[YE (B*u2oB_?t,M߽]7nϚa(w$`ѝD'}M W,Y9m*bޞK~^(2JYiO%}yQܻ.ӝR(W3Jnkow-8(PXX*P ϟzP~93ƟAc(((((((((:8` FAAAAAAAA@=` FAAAAAAAA@=` FAAAAAAAA@='*:v߭ ʷ~qqqz}t纹@GG+Ξ-\Ų Y[{.]= Ŀ,Edef͜dk22&S(X,V_WG{9E2:쯩Raww|DZ)i }k,̿E?s+-t;91 \?hblwO8j>u,?;3xDoo_ж?"_O)|mԙغ% @PYYG ^Zibluu>.(u'{󆵓lm3ĞmjjZ55|[YI&t6m`ee]kafj*ӝ<}''+uF1Q+V5x,]bONJq::SUUebpG5T6*))F8uԙ }H$R˖,D$Dbk螰湹Ξ5s2okKٳf".0DvYXXTUMM_Դ%NFE[Y"*yTZFf__zXm!]?#sM9۴u4)N\/;u:Ha 0$ozٳRnnSVbŜ:n ïI or!a%JO%K8TS^DDxJ}=]ǻ%CA=_g__uTtۊʝ!AM {^7mV䩰ТRn:U_?.,~cL*:`æ#8ƞ>wWVFE774"GJK4/(*~$xƬվ+6~3 ̟r gK^dĝ=;1Z'"%NrM@8ᎄIW.?z\x("[7TGmؼZĵ6/Z0?;ϊiH*[㗒sEjF "D ^.\ETT$#gb8y uu/^O)```鳒[wtuINW^L\ôEOg:}+-yoveeomϣŒQhjbDPJJJ.!6ʿf /\:ZC#"|8CM0(e{֬98𬬬i'`0q&Ɔ72>s8YWWOUTbeed:MΎb'7@2!MɓX,Ҳ-X[b0UQV)+)n>m}`Po2cB'Oc0DYsϥp8iVNy XZnfN>#3kNw9}ƴH!,fu||L[$",՝rGZZgSh  wt 3; 󳲲z.YTU]]]%̦F5@_OJ-6/@VZzƵp Bœ.Hܼ|qq1kK012~I_tQBFA/qNvO^!7 =zrbmel{W&D { ~B2n\ߵ#@X:F tuNH<q<)N'qs0gni!"۲tycc}W\L3;{IN"QMMSfadH:5FsssNݛ.H"Yٹ^aۃ7}[Es,pˌi0u`$x}666IדS522W𢫗{;}xR83-RRT:q:&.*:nr%RC$lԖ?[[B*7(_x"8Ē6gae!)+:(azy(EKnnnu|'bOD'XfDAcqtq|¹ q kW8u̘?wvz$ңV+lڰ MM۶"8VL#`?/-P*|#m洩w_bְÅEd2x dV_4}փ;ONJAP>ZrcG`) =99ٽ߿ QЁ}j6֖I)7|A6LlGB/^{eeZe)7@~~bh !A-._=~Ҳ ^oÎ `Cc#CIIqd{$  0dD6gccr-)=6R=lK1 53>y; =]5~>8҇ +#M  _7ZSCMU (ᒯ\`ccc9mǒv݆ >_omۺHl ذLLܲ KE?AyYي tan^;ϸ&.OB/қ "X齇^,("(bD7^i6Q E:zB@:/v̞L33*Co4R}<<<4X,Ֆ`P?\\\x Ɗv6nn.YY3Q acc[v+ŽEܻc_ %f\^=}/wm{u~Cޝ۶Pf07uYYXXdrFVfD)chWPT֍8duD"YL$ pQQ!+)!O ;p~D{V}g 7Z\zǏ###[6m$S=sۭ̔mmWz _O\w--͆퇆޼}5/:+$%)QZ(":6Xay9GK7mToCD~^<֬q5zb/,zZ[yJS]ul.K3Ӕںz2>x샃x<~<iii-)-CRje Kۅ5|RwvuKϜAOO?88蘙gd.vbUu Ly PWUy1/L&76ʹ*p]۷~]2o+_DDXVFHx'--Ȑ@DDxt1:;֨ iNN!!owg1W]Cn^~СPw7&&F$:;H$RD))I QZ JIJ=D"ݸص4^|F$GFF5Pwgg׌RX,`: sp!8+vSc/^89#C# <|0e8qE+z::!![7s@ ?TCKG72I5s')`0]$y,qi陖,dOU)*+#}7WEYi9wsKmmYYXPT;\mbjϐcJ޶-n޺s)͝0)`gwM<!2zR 7mʔF6ʂ31™M2%K8MLi_ʃ ڿ[;;njldj<uUuUqjPƒ`B?m:Ź?`x,9tH /'KUlnfjnfJi[3_DiacceP*g"=sяuSmQ].ͱkO8(QG2`1 k91cW(C]bdd斜yNEB(Vfoznlh@X77W?ߏG---X,\=U,`gcok)'= >MaievhihQ<(*ې?_֏UU]C$dr޽/^"4]]ݧ]pq^Mg$3()+E_ :UB\ P|aggbg`H$";Mv$$SO*}{dgH7wΙef匝mhDEE$@H8mŽEzEUqZWGWMee}>|gfbRQVZfկᨫd)Z;8*Λ>31_`lfmduF,f8/_?_䔪RR@OGgjl^B\BBZ222jjy{zƓM(2`٦Lٻk;@co`} vv/OY^OcF3M *'q |Ggטk^UTIM[YCW]))"c|8i_aa촠w_vg~[$%$PZ$%O?ETW:Ueb6)"JJdeps+)6 Mu촤Ev_.^rHHF..ΐУD%&<(\bee;wvo_q1N |v N9MB|l|Ҳۉ+/ W#/vٻ/HR\t1Z/CkAa1?0(95}O~u,@ZF:8'ɨf qVvF8{boaobt(0[wD"q~K[GC*w###kywt,wu|jmgd˧)G,60]~coobrGrW@,]7Y]p[9@XJ\|%E=>VVUvddfJIJ011qwWPWX|gyN?T?c;*`@8alnkzDb4{l +EvlmM{zY9˗.aggZ⌼M?"CT<26^@~,}2h1?4(Ν?k8{bgQ{'#¤$%ssjT~'W,uDKKSRV~$<ؑ3JJ6oqTtA IDAT)sqq8+Νp]*+'gbTX|Rb)iHLGGcѻoU1p:qȨ3'Dn"r/@ ܺ@FYb.y󖝃}*{p;1¬ M ?̔n߈UBtlܱrzoڢɩ`ǥjE͝}) Hbec-m5.68> 7o\olhi 3 D"--RRb5utt3gLxR܋fT?'vYj:Z{cG)S2omWM+"<,,o޾PS:??0hgTQTNxRy >^V,v[컹7b(=q||CCCmm%)Io&9 ==UZ2$y.ъ'6MhU_:w]%D6Mk/On޽1q-d@r^ JOl_շ!.48wΨzqi wy9#\t4KIa0=ӥ#9 옘R2MMdgb0jidgaaQRb*jnnde535bf""* X,VUEt3Sqq1,бim{{GQ1QC= #=sӥ$w/]cOQWTT a0]seUw7!AA]{z:5OkiYj\յ M-Y`goiھjxyɩئLkd9" iYf82&;[i u*o,`V7o(!EjӸKʑYrcYܕQGK N556bӄ8-LJI.%TvDz'+.)(*)+ͧ gibO (/7ǙIJIu/?'clӄhhh&ĹS/ExgfQz:/O@O'wOPx\r#"H$)#cwH,PRqiٶ-ZE||ϣ~Q OqSή. @i~IYygWzޒ2Sc6ei#yqXWWm'ee%=;88>Fȸ.pؑCAHv>1=u*?CWwKNy9{iEWYwd( C<l@~(kW^mni:uD}TX| ۱"G{%NY9Q GKuuׯuܹm[߿ڷ)X7o߆VU;[>>^ mmW,uAO+BĉSYxD'H+<88(&*wv!A\,}//'wv..޾4:E'h:;BÏ#:Z>ޞwAqqqZ[Asԙ۶̝sCCCn+#7>y Y_173JAӦUTVVVհ\|l|=?p)/'+#-'f&Wo@4+W"YTxlaa^,q f*WE⾾OL}A"n'fd t~i;E==AFVNJ6o#YT:{VYr23hhhGZY._aaj;I ..o.m;/\^rXWhZUt-+qyB89^_26;k䔒*&* 4:{@aD!AA]Ş5zEž~;n݈46?qiWXu jӣ/%v핔KJ'{8tp?L޶sϜG}ߴe;x<',88R5ɢ ?6O[9 EOkY9/_ieifQ[WO&Ǚ}ppV`0--%eHJ}]픴 d爽[z zzHOǦOec37 ((,~|劥/ C*F b(ĩu||!NLNLN=z?wzœڑ߈ͬՎW79es-]p G޼}g^Uu3'#fLXߣ8oNdt.ʥ%V;)..vNRjzƙ |c;m|b>ިTdxKKw䴌S᝝]8+G=;f n׎K r3/Z/:|{2>OFV666@brjjzʩ3X|wuusrp]=#ca!0>hϾcu||+8+}{vې?_ #ϯ~읝]U5Py x)O?.A{TRbln#W[e*$&tvuNaم9_l$zmM #""bm-/_%đg ?~̾};@xMNnlYm<:?م5 O0+"@3Ɔttӄ%%$N@${{DʡK]MPѡ :Zv6 ^:s,0# "DEE$@@sg׷! | 迋c}cS5ίWmэ p!*Y N|梏LYoݼ29 8|$FJJ@::ۉAB?~ehr9`fgiPnoM ;(6e88Vlx++{9stv6U5OԴ5tu "N"?_F_%5g t%5'&MJJrWVCOϜmt @c+e s(,q|###bb/57ۋ%H CCCzƑQvAsv@ [2ge 9c:s{gLYlZO^+_B&1@4!D" >rO3̖GݵoNxdd~ 51Qw4ɩ,,,uB8vDNV7޼e |8i_1?'H$6<cO32/GRBNRʖMP˝韋4&tɁfff@AQ1۔ JJdeNaed2o盷o}}6̒ TVU|{b+{TR`>QI) s^~:w%ek7@PiTd4~+KJcnM֣wsYX7zC"N  HN7億řqrr",k݄RBS57zX,?d䲳+WF {u3 HGKCNV[ vɮ^tvvm޺Cgew:m:a+q)--L-MS v]l@brZ/VD:mn``jhɲҲ@/#3{xx9}},94XEKͬ1Z:>z\  :1Wgnp;1 )!YZ^hhjz>/LEY r'F G]#F߮n$-Ym`v>ԫںzw61qvbv&D1TRzJ̼<<:X>]WX,.!ƺgnY[ Oբ I_PXlgm5l>~w gpȓg,zjz=22cy$0߶k/r|9&nf/ ?"CELe7Gt0# %uU$DTDXm ۼsِӰcZ eOfu\ʣbAg|sr[Z];HQ %NUSUZ 2|)&u%컹44^|ݸ]!v ٰ5nݱ/3.hgcu')M?<<*n2\Xt_C]UMU^~QI9 h,;qE:՘7➿x988$>1♓Y* <7l"---ճMc%zzz /=3, CK_7&ݼ{V1Y,߿oW^~ge] +6050mnnپ:oC\>+ΝCۋ?kxrRyWs[w goE鶶Qv5003>s ^TȻW0<43~rb'dy{_{1= _QQ944xxz V|=Κ, 5U.NNǏC,rF `1oeggc``HNM735b kֶwSM2sZ`$+ sd$@ 2,pfPWmn./' X8͛JH_P&,<0KNvl9+222222hi"ӷEDE 0Hʂb!FX,vI)iӥ$j|XVd%EE2eX,o̘.ÍLO{Z[ %)A2uҲgd2Yz M 5qIIݼ{]S~S3/s⤥uYD"=*)UQVz\Z/PWV^H$VTVR#%n^~Q$a IDATVg735/(7M431`0E"BÎ6%_-K Yw#ׯLvyPpyikj {2Rn#Ƙgcn#ho|+-1A?`wQ}:~|ޝ444v`cm!%)` t889աWI$׭d% 9x,2 ]Ņ1K {z{L?M*)[lۅkkg~k'z\'7K{{;~cskcs܁8$K̲4!eI ~;1)y &&޾>aiNE>[~00sZ0ө{]ss޽9add+x.[fT|a%--8Xqi*@KS=qiƎ|tZOttv ;ev׶)cYή. @i~IYde4-)+CP"3;gϲkF۶l22߹wRO*ZJ! FMEY{GGEE9ڭ 277Pmm>V^@ /TWUP/,yZ!""VulV]K[GJDN~ȓvk32[d2.%)*"~gj INM_]05-c:߷oN5<yv8+;#=q]61'Nٹm )C)a1.NNĈ~Ĩ M55TF!h#ںz]OKmȟ7= e߾}-jqrYv*i:(~쇇(ak+jj:z!I;HCC3KNVG[kKQ7Qkk(1΋ѧj߇6N$m077ׁ=ޛϒECC?j1r* sO򁂆nn.'G5n҈ۻw W\1}*`dd4KLN~Rܼca!\M_/rŌ5rqrJIJD^hi8tx?:3a17eӋ}BKK+4?-#wz:Z/,߽8wn]]\\TrdČR 1;{[LOM9Ic-*JQβ!S\,g+333#ēj7.It))Lnm{//#+'UVmMW111~!pVvCCbkϛ xR5|2͛KѱVf2ѱq>z\ix}'߿oMbB|\8 8q<ZYUM"n'&H$ii#KJgΘ䊽t~׮|񲱶H$srf]yM.8:Rj|%e^5ZYP'!ZZ62ԧ:7wKv/"",+#}$<Ǔ䖖V$1]*,<gb&&&Dȡ`DX,62<;:deO;N]U%K!Wq\tM 3N, mʔeg03332~Hjkj|XzUȑp'z:+bUnFFF H2ᡇ*J$y@i޺޾>[DEdQ-vcffdkIHCC`=m[luX 'Xe8,J ?ܒs7ܩH77`fj|a°:;Ҏ@v)&yo`vLgcc }H 231K Rcs/{,.&:I*hihQ\GG'4T_mW?.ixRv/)ӳMv$u<2aQ󴶪f@-%/y`Nj1\B^A:@H8m$=/\LWGW@ @]Ue O*yP--uȑqoiWP>olnhjy8n t&9~ w͋\Mv:on#Õb$%~oacٲ kF;tgIwv2X,]ZUEZT ?Sή+4ͭk#yWVN%VVSUƀNз!e唏W|(,8o.rZG +JU@~g0@ _A .?{?`@pXdoc eiȟ!bNG.@  @ 0@ ]@ w#`@ !@ @ 30@ bQI飒ҟ K[GaCխXZ/&\9֞@ DsO|6nze`ci8qޘثJjڝ]iOOf8a媮y8yBmSKWoJ}6+X䫦ףJjx%tC]}ëhdTy@S]m +)D ;Mu촤Evc/Q[IqG/_9v$$#gHRc'N*Oi?}|7 Kn'WBooߖV.wLq922^̓r 0;zsORw xQxPvZ/8@~O&l|*2\VFHOOg%scsg nMDĒ}7y:H$k{ řqrr",k݄RBS57zX,?d䲳+WF {u(==Æ \z:o&'zFfήnU5kncsk}}:K[ǧu+VyjH:}6+c~;u q:#7QZgdx<򔋳B~~N}]m[6̼ꈱ566|673`bb\vX5d2eic0q1s"(Q@FVKٸ8#HgY[333?@ ̓ki#-VX '+ch:eommM-l!J :}vP b,d̓pH|8D(/?6m9~ŽEjk)B?%'q* U7mniu]4vthh(vbOo2ՊK1G/fͥPWLiB2;vJrjzd)ښ#mMIJOyÇ}wݷ='#@/1!{ZﶝSX%݊_h?GP2cWwF=]m4Z###UU5f͛ _tlشu»)<}vݨ{_==?gܑ9cHWٺc7??_f3Q/\)Imy^V~F-􌬗͝ tuqNufY9 J3[ANR cQa!AI23 z&Is33Φ 89EDJE 4ԑY0 /GR^~Atl\Ifaݠ\8O^PTVD\L4=Vl{ )[#Yɷ)_{v^5Grps0yÇk7::;Gމ -ޙCq/Y#ZP}7YB6dHDYRIB-lZ!fqkcHv/}9>sw 422AjArDsK+FOWZR|z-nfRI0k۳.E ~450 t#{Vpssowa``t?(L=_0A&FꪟËںW^ `0.y]˄b|/7m\G^+'7a7X?ٻ;:EF>BYDES̹]]998(: tdTKS}=b6Tɴ P(TtqivYZqqq5mOV:;:1338:"̺od7KBB"-ܽgd/.>BZ[={KTX3Dd"-5) =VQVTVZ^ZVbKJ˗.YBKKK9yFz$P(b,7>kƆ({? QdxxOKKs\׾+`#$>F# J˗ӣPI!033!+;Åjxd澾)z07F[K ~:SO''EE!|;SLܸx)Շ8)tɘ-.h#3^ 7Ҳ{v퐚;XxDdx(5558o 7ffj"9K~ԙ55䑽@??65>~L\z\_Pߧ3&FF7m\w/#k# "c񎅙.0C@ #*R3ffRB"D"#;;Rd$}lll*Ji2w9:;M##u7}$122tuw# vnӉpB*.S;zhџ=r{G՛7o,]d* RJA~L3z:lllq a"'ܩ3VVqp#C$9KqqiYzWwgAZ@CKx`b*=Q;ߛ"1X #@\|%yGM++ b'APttt].V[ IDAT~dB9;:;+?ٕ̹X)*J`gbd-`HHA6yJ0աG}yZ MЊtvl-cc'Ogfa0عsf@P)w HI=c@[[;Qȓ߽ >DTLG ;w,? XvҲ>^'^#H.dSKCE &FF]sfU=? XDXIeee3M֍ n? [[_=7ݿ Ӿtء! 5\ 700xӪ*7SLper#1,,̈A~)t!khs}^{ټtƿ FWGkkn|)z/m;kk_rss8mE!||[6MpuX=vU={ pssXܺ~\;+u!,,tby9Yo_"TU⒲U;\UOKJL~0Gأ[jh@>ww̜)(+RRKCXxdGAP"¯{WDEP(`SQttt7o%gfD="}Р>ٕ'! }vN.`hh@ WD`0櫆FbSßI+5w"_wp]2R ]Ιzc&G1QQ 1g<>-`0]aص6gf"J3RBVu(NI}YɉUUA K΃o{׹@`G;K W'ƝOpp2"L_Wmlr@ JN!RRCG͝3;-&R+19]mEM-#u9edW@B`eiNOOORiyESS qR,hii-)+xZYUlHaΏ>522bbhQGd`ЈL1QYء&HyD&D9'Wx>'7/p{$%\A^K04EB%5U円;i8ǿ/c*->J$GGGJNKHpMڍ{Bd)Yb""2R#1 Hlkk'ONa TTTmme]KC-5-OG&F7o%WU?####Ϟ둘UDX `ceM*+e䲱M̚k75r y,)vQb(blrޫ3rqr0k^OՙY8^ń u=Rs #oHD8.9nOoɮ<7\IChmM3<vttl_DImEqѽM !i˩8$ijs+p8 S-\hmi[Ơ ?ә U%$\A^jE):_P(",dar6,\`n&_|/vhvH  #-y 0KrDؑC b4 J)xDsK+FX'}WVU=0Υ qf+ӢS~`-x#IAGB300(+9;R,f /_TWGkϮG=n`l,M9;Ǝmq+*+. :s9sv=-0 2׍KH-pqrFG=NMM-)9血q+mD"'<..TWzVg9s$C q&fVxApNm'74:VZ.(/t0p{{;=y2/wxx20(- rX&/ܲlY4U[Zv hg-A!GA81Sd=Ȃ6svs p~/@ȑcrKeT}v^`>E`mP(j}]m6'#on``` :p8 1QcUKWFFF䙷ǁ-}vvWg)K$>){#E$ZDqQOoEQLvE@4ףJL%&:o""h}.SLt&󿇎xy=3>!@rw~p/7/GR`6/z':`a󽌬)}ŝԻN?ۋ"7/tWA>zlz]Ah¢{հX2Z9-޾'O+z]ҪWWWw;q2f=^srKVvrUq8!ƿ{byWa5ZÝ}׸ء!e p{"nx?E*j+n&>U4t p^e*9=u%5W t y$ce9ܗePe ]d ޾]k7n|:l.(:| ۵n|'7sA{A ?i,K{pd-5n^~:iO&hix$RrXYUGS@A HJqwe*?YSuFd >9Ay_/ǽS*JM)i8yO<"7"gGȓۏJϝs::b2)9h#Cם~oo:b(zAI] x]gWqi@ijS08agg]W[kmkDT̳5|~&||!A'ccaf9c:;A!in.M~GoW(+w+SVR<1.Y|6䠹ImnwQذqreVsSKO@0?ķA~M @ _ (n̙-y88 xs_@{-'@@ 5+]0VrJxd, ]dm0@ 7# 8>/`nJU@ C @.` @ 0@ F@ C @.` @ / ܪg{`־ӯl$+;u˟+:v9E5<[玛FSs@ oF--6vɍ {?j պ{;F+u G'U2͌l9}mam|{}ND553)a0 viGū+ikluv{M*ms١kv{_9_9EڗuƔ#z^JjD3_jⲙb$ܽ{/ۗz @EE9 f38yZUfϭUUQʊE{}lIOO_^]klW}_, wRwo'YnI(MEIq"N`X&&&@B66¢:ښҲy2Ҭ,,,,<ߢӂH$zxk}c1XleU5//(jR2ffܼ|'-qK+O@ )_fbf:.#-En۽sيyz=cSRndd?} C,Ysv:!(}ӕ6o&s$.'''¼u @r7o_P(df簳-TTT׮ 89](ԏhisssi m6hٮ~{j !)V_l\UѧffG[!wu u GDvLHLrCUKe]}zfQlml-9984Խvǜ9Zq Ԭw|:{M]Uk֓}e5::qZ:u.?T .!b=#ӕ@>C]Vr `8'gY@gWWss*yHݒe rԴM[mmhs7oYiE =.^f6$^nqt366D$/&$4SA]EMmâҲGdxyx4ǝEk7n713D[\LHB5/LMhY WRZ/(0]-Y' :c6D[\yEŜA[Xkns܂u =G C>_LHڱBԐtWV|eHI(X"mElf؉( 5Յ W͎O(Jv"R7+;m[6a\###7o%+.W "v%94J -}M[K+>56aa!i)oՓ)wEr1z޹y5&wpK 33fͫn.^XXYo^U9E^QSCzfbdp^:blm}]WJ_Go563Rxxzs+B3<@6o%YabD~J,\0LtDz-K Ӑa[OLl!7^ϐa Kaâ%i>zByœerikxŵi7 D^>fuuvLM0>wd%DIM~2'Mْg/jhiiΙ]豂R~Gg; 9yL{G DFzWVP,}Y700@$ )+-W\ !l񢅤Xvq>u[ד_5 #ƗǞɼL^y;[dqYY9EM-+%HK>c@N^տ^P.C e/"C _oڸ@ggWNn捷o\޵ɟ݃"O60Q;s.vvWN>Tgv rLuͫwo_76M$SB#KKutnDf,-̸8ښHvJgG:::fffGy,-.Y8NZSͅe i0''G_VmK$鞖p D<[G&Ʒ3F䲙,(+-FaU̓ɲ(!p^CeEan.5d&iZuUd>)&IEE%5wǛB3 tQ(;i%g122>{;4Tuk>+-EP}}}||SL1q9%yx"9_KΒh1C@HH%H;GEY17/PRZLA~v?kSoXoICCcz@(.-[ WRVWVRx=\N鈐_]__*ԏ:B~Y~ {GGMMMnÍHΒTx^SCK |89Se9p"*8; Z tHnnjjjzzzSďI312:lޘr뚹)z7"xyy4ȃX@`@G6DE)<uHur#@`ggccSUQJ ebljll36E?4&s Ty۷IK ^ܴy{R󑅕448 /'^RV|%%1|44/eK:];޽#e{z喖W:C_MEbn..򘵯dG mmzҴׯh-]8ae3_o"K~|LLْ}3cf+MpvsϾwae\lllkl,,01*'ָqqvvv!O"$Q͖w"B\Ysz.nu'YWb[[_#+W`af>goRy 7Kb杛W>ᵀ>}BPѤ ;\*V"e.obdyڍa'MVw/N IDAT;:6lrU\@ᰱ&^zzz];w.fdo_փ:q]NIM{W066$N"P~΃1,,,gzz&^<]vhmkwsdia(&v _{׹@`G;K W'ƝOpp2"L_Wmlr@ JN!RRCG͝3;-&R+19]mEM-#u9edW@B`ei>WZ^l„dyEUѱWwvu~iii-)+xZYU\=aΏ>522bbhN,%illW1xyyGF?4OZjj/Ǐ9~88')ps 122"Be假&9s@PgW7==HSZ,sa /ŤlPF}}(j+^&&&vb?`#,mh6m\G3l_mm$2Rᡇ[e r޾UQZ픥KDOADDܩqb/8n݌L( FFVVnZZ:b-#Y]p8$A5ngg'`57CDE8BΟKH\aˢE wmw&,]HF`BcceX(~oA ZYœ/~ @ EK# 309^q= ~ww>6ͭ7[ab`0:W/XX--Vs2hiV kߘ[ٖ>lhQ{1!~. )CnKKƽp"H$XR}ˬ[c bj!""h؉W MO#=]NIMu'l1M]aGp?qwB զd𰚶 Oo{;[ɩiNEJNh8w?899GvwS$DE/"-@ |ITTDFZXx$!mm~2E"5Pw#ݼWt$8 x@!S2E@}.V%eݸEK8豟 @ k# F mn5u݄rj=f__PQ#ӕ:aժ~|fFV6Ɯ>V6@[X_z}Z,NCjZ᦭uSpᡠm`eGeU%%gM`roEIi6FZzk7nIUx:{E5mmno߶}#LESMv7o}Vu ]CM]#2۷m\vi雘YIMַo5t ׬D+Vn檪glzX8C]X`^vzWgR#;=VPS'j&;=H2ϔ LԴOY]@ 83뾓 rc]}Z{zz뾬ECGƶ`ddz).nvWCCBDM{vpp̙OVQrmj^=yW[Ke m rn\hBם'+2ESM-E{(+&Ɵ}:;۰Q <--r2R$Y4>lsx?3-xɒϪ~&&y?9ٷْwo  |8YIRRs'%dܵ=[*nse@ʷg݇v߼)%8td2? ~V!\*fbf:.#-En۽s 4^شe922݅Fʺ̴Չ@EE +m߼MjHqC}]NNNdy{!AA, !o޾gJjfDnC^CuUy2U\_<8_;; AzhjUvE2r;=u ,o%wzxmkG[m<ű~S"NX[ srphi{v9s;4?::AOOͥی 36Uеwp")i$]kx -=j:uC Ӝt:ZZ 3[ZZ8?mnc`[[miyz5yg3ͭ#N"Ʀf Fk!FҌԾwp3236 xYؔo߶}yzzzy2:ښ)w -N=x>`Vi,-j*ء޾>*J@yyx}yÒŋP(EoVv۶v{ ;HjZz_rq^y{s޸InH'm ~CyPD"75^^QQM 89\r4qUN]0O}C6@OooCc~r_*+#-{`L1QR3OV;um[; <+Z}iyűǎH͝SZVkS%%vU6g@Gǻg/<E^]OGn1c\\gM?%@؉(-UV+GFF3s59VXٔ%QPXB߷nUe)?yꌂR~>>r;H|ZUmaؑ`۵4ccc>~vHlimO\alhjnc@r]p JJ.%Ծq3l-t]E#O565311JΒ)(8ZUEbk&F?}!y-edyv"c*=Դtc}i*ʊw2\& {v@Zd7˗)@>K 3..N؋g.\0_FZJrd 3316Bn. b~[6󤧧/xr5MN7=AӪ*DSu?vF|:tw &~ 7ffj|kkN|nwe&$^OL^f bX,d`0 gcCSgyxMɂy2҆ mt6&j8+ʵܓ'&ދW<=w*jbHk7:\M>_Q JʣOus٦r|l\3fyEtq**{'O >?e񝍉@>@!Z(}~Vk5GnlTKCjνܼr? q˦Դ%,,Ysښ;#KIW/_PSHK͵0[ӆGGiunܲd۰mfa.qtts^V.)%00gbiy c#>W47 owwO))'7D.NcPTπSB\<;'ϓ&Y sr+.`ox7N@j mmk;)BMM=[RRMU)/`SQttt7o%gfS P(K we8g߻CQى`IFbȮs.4KIO*1 0)K4YpcA F4:"LB|qrp[}.7oED'Lթj.DTbbQ1|b8n_i75ՏG7OeA~_@S7N> }[.PW-(,y\|XjZC0msAwQ19l032CpɈ0a!9%hkΟ'<#-%8t䘖E%u߳mlqttt,jjWsrWZ\A`E+i\B###Ex<`!]-)CwO''HZ>1KǏMSCБc[7odddhii씜%gf-s$%yx' |F{}t)-((,;wꓞƦ<=m@wO 0O^Z^P(tfie92Rs d)^%9as!g_\~#!:X~D"1UC<)@鳏KCbL/z;36:|xZy2HܼE rpc/qqr Π'2M[{F3V:;xyy|69d1]aS2M80.gacSY lA\|wd;4qz|NdAFZ@`HgW,#BiY<ir!/ 3+ KccSV͍66^ xYWaddQR`z}@?݄ڏbv66+ Kjm- k.ŝNx9snpζ}Şni}E\CxGFF9=^Q-Tv>===BB!A&ũea-ȱ&sp륦/LuuY={IϐM@` ;󇆆}8 ^ep6&$!uЮ." ;OZjnggx9L:6VV(5oiihϗ ?Ԛ' ^Ƈq322 ϔ_$7衠 2_d"7 @CK]dNLeʊ۬xM@9/\]m57E!B||[7lGL d,F>f`rܤ]S_7nvط-ʔ?tbjjj_o@G azu&A_@pZq$5wlMOTh GIyr;$ջ >V@OO[%=̙-y88 x.FFFGĿԴt=c9hih/ppp t'&F7o%WU?####Ϟ?}̬LE ~r 2\/_vkܼá$M]IYyo_Xʪg ;Fe'ZjZzkk@Aa˺OA 9%s3?]58d4_uvBR\tv9 >@Ϡ4LȑcrKeT}v^`˂|\vQ]UuepU*Oh6lv\mcÎTA@ {1g s׿jX~u B@ W4u###a'>?#`@ Ol/ U@ C @.` @ :`Pe?g@ @ ]@ w#`@ 3lWV6@[X_z}Z,N^_9E5QSwCAYCAYCdˎʪ) ;:KJΚ䔻߀\ xQS[Wj\]~K*̺.(8XW_ށn/ed|x6W/eMvJvh@PI aҎ9s*BMCϣʊ˦ŷe゜/-ZuISn*J,,S4{1;B B"0v⥤WSE444Z?@~ _313 "7޹lXW_glZpKffN***@0]ismR;GBrrr",,[ $gI xD?SR0Ue%rDP(G2s~|>lv6 E\BŪ5zF+W=*FʐVƻP146hhlmnit['F6vzƦx<~[gW׆-hsw:'ֺiiccF[ nJ6^mҦu[[miyz5EMG-=cc3KD"q2p8 J VU?[gkjz|h-&fh I8G<PrJ:zFY9zƦZ&Bx[[;&fV;W nKx b'8k/fc ޵ J޿W[K.^;pޛoLɊw &"l qJoU--**VxwrH|8# xKL>jŪKh?u$FB{!H)GzD$^b8_z+;#UTT_}OˈD 7w9tԯr_[մ{WzdNBTTh9KRTjqA^bT4wDoZghS+%SͧZ,C}Yb_|X00o 99FKO%F TW\q+8hzqy~R`&[[LillxQE\.wU22t|:9wBgGDsJTl|l>$e=d Ǝ#Dʛ7o'e屑a$Npv  B 3sCgɢӦ3 dgWZUJp9%E4TVRtu1 GYxTN+([Z1׮ݘ2R]Txր2U-` uu{akmu]3f*J*w @&dEN3ݚãZfMJΗ~~/:O\JJRmG-`w-@ؠaXdZT .oQuNڑRZrd_/ZWW[r/5- k \\>6$_ ;7Z;N3SsiG|F%ń/lnem6Ŋ! mm=MmX^#\9FƣUTW9bickC#C鲷 sGOi"iϥ_)rmhgk/J؟4h1S\gNmj=-Y:s`M:tk]g0>Dpۛjjؒ 'r !7x<>1>:9ۼCz\`ȲٵzQUA/,`rڏ*f?)khh\ VW׼xrlniSgs ZZZq8\]m“lN:m5e2@`keLEB?> 74x`v;ȉDS#t԰ D-]AH$*.>s;#9qւmm%'Ols\gK?ʮaij]^>{QU K趍5̪s\d7DT{K.o4l:"Xz7EDD],%v3fS$2%45.]'&.^ͬYi?svVJrWgPQs=V)MTL|îK%Uv3\ @=}VU475q8:w` 2]Do谷_U4mފ9.MMII7֕Ka$D8h/+HI LW>k/7w{ 4uӲ y'+%-M:s=+9.NnYi*Yye@gGGrldߜX7pAaUЃ2{*55NtݾsWB@ccS痯^ zf^!h6ݸuWiBu@+^zi3;;!N-9WNzq2x}FC{}fE k[,2jZÆ&m uʊ:ˆ&J.>//#EPexq Y`ȍ`ጌGIbbFë޽Wqv!H_g̺~oB趍mXk1v!0" ONLIo99xouU`d4tC-&.Oײ/0MYizŮNq=sS ϷO͘%MiyxZ4֎&3Í65x ͼRzy&ERLqt\sgϒc<@#hTAmT]S;PLLA7o&D"BP^x'7 #ckm2ʟ>C-!:C\hGG)+TwfX>N~:prv h h1q,79p7n߲#(vov_D-Y3( >6uA?_$HJJk`x&i+-'LjiFF +dFut\*Uj¢2t-*<$,<*84@ hiin;zi;'aFq b;jju>$!32foP: A;wj󛨨ɸ1+J3 *(,Z9`-Tii7Yh8?&@$JQDp8..{9]#sUqAn cWdd uh>CP?-^*}a| xܞ$0tAt~Rբ=dKI s3 s3A<yP^J64O[vXޙ=t04/-/EDb)- nް^PDNCC*qoKա$ޚضIڽ=ʸG#: Co`gk͏bhuџǦHXcRiSw(nO{it<J"x<oicgaų|s v]a:i*pLpzYˮA=JJQ,'8&>lWRKDT M;r߀`U޼z^675*tmnj:[&=yp뤘412V{7Y,ISz=7#'RZ_- MTT54㱫2 /EձkЭmBwa)fV={KAY7hܥ+n#MQoOsS' ""ul-gAԴ+>Wf oxmN$@TLbrFsKbFY@MS+p[7N:ڴ79GOGc^<{;_m-D^oISr#))UId`Gg'Er }W|/uvuً̩Ӻ+7l;m r+eφmۄ%(։KHii?ay2;g/Jf?$"ݕj+oM{[>z0f/[/L $Q\BB\xVj s[LCGjܖx\3G{6k.]p:k>=96r}Jes8- V22FFDb-p7oߗ9*JJhZ[WwnSaGUSӧTmm5%?)5ĸo_}*ĆNg|j@ (**WwnSaGUS@*ͮ2NY>s+**jWwnSaGUB+70 k:V&.Z_jm :za$WDCWkVp]`GUͺÜZ?\KQP֪Fab|얎V +\ m\b՝{]teOzr{ m(Z~#~ķO<9EvT &+]jF|d}ɳ" HkNhbj<|34 !$//׵KgaGE#ƌ߶yC=L2}v/AYYbSdMwwij')s͞9ݬi3n5>D={Ǟ?~ڵ˚)Qk֑@ 4&LmF|z i FŬc 63,#+,S͘Q#O;"+'b;w bb[Fs昈۷y.8$14&N 6Ż?WOWקwՠC㬌kUluLMML׺///zzd2L&u9eeIrrrdrn !b<*,,Z֖D"SjZnng/:Y[Y>zdR(eX=))-8@]])+@V۸fh Ύ1B(1)%}z/Z0OJa eev66~ q8=~Ð۠ KI/_߳sƮ={߾ѳGZ㉎[8oN!aUV_0@ hkk /7 8ΞϞΚ1m@ϙ{JMMS6]e`;v\8.~6r+lߩ۱M룢c/ ,I:̌C^hh`P=$ ._v+ ر;dqc\]B_~ݵg_ַ:.Y;֖RTaBᣱq?{|߂ye+W֯Am۱gW QͼVG{V !VGl¸1cދJ[۾S&OgcmZr#F+.QTڎ|˫܉[TĪ~;.^FFzܘ/R^}ޭF Bϧg|3UP;t~l':фq}{jijy>r8~L']ĤU ڳ K"bzfՊk/`x!!s/!AG:r@ pԉ#=~uuc#+A&UT]taw߯ݴt"ԤyM[Vð9Iɽz@:zB\t~wfX`՚ ߸viǶ:m۹g7_vwr:p8gΝ߳sۺ5+|zkW^ :/57-+#3q؀9܉o]WKKIW5" MMk.lްcl6ð57r vn]nS[Z 8p{vNϙ__P={n߼EUTT|IKKWoޭz-ZGǏgN;{XWO>CVGPn]LM_T=Q Y2Je 8psf&6\|A^^Z T㷚@pwsQUU y+۷!Vqq^^~^D"Q^NNnf"I$)ZII k+ *J&w65!ɲTjUwKgSii)^¢zTi&UWO!$$$f͘JLJcnnG"=x!t?!i`mm-*{?aQ F s=@m-o/))*H|d2YSSƺcbBΎNJ\=}&Likcaؽ F 'Hzz_>GBҤP(1ljB \) ~|0lԈGOkh`@"F ZP9_ twQ1޿$!!1aܘ\.!$%%I$ TUTr? rAJMK[mw/-^ؽ[ׅYׯx{B&tzz⥉7otuoewZ3XBѽ۷XG@ =szDTׯYe2OwW"hldhi1 'ee%2l1QGjͺ0ճgcnqqv$ +ׂ |@GG!OzM KJUK#::NlͭHKKH$⟌k:;l҄qJHH``0?.ǻx2<*KTAA# k{ߥL_s)++[R5i˭ds؅E*ʨxAApQEYjWw "--<>RZ0o5,'%|EE/)-0kd2ǫd0(hjS=?"D=wؐch4sC9{,DbfgS{,=~@_{]׎*?{GYG%ŪH$'uơ8v}oٸO)((_RQVf2USo5oL{3sss w0ϏЯ!@'_RRZ_|kÆ4-5-ЀFZ[-^4zy)\RTj>6B^PZu BHJ*I0 554B%l񓧝t;.]/^3v橢(&ۼm$r0_UUEw̟h4Zzzp^P_ U Tpںթ3(h3$H4Z^^>F;}nKd>#3>Puݻv}ó/Ǝfs6oYV^ɎZΆ:V7#v\bi4ώ‹PWsمb})++99yyjj **>r+1 +))--exJ@())ή;}JM K3&+#3lSgBV<|-al6r:pt5+@ XTX7n:,_>W^xtzA_|ܾ`{NW6ݺI7 g9a_M0  &rg߽a>oblKyL@pF=+N099[ 6u fblD nܼx<ާ?nքf?x&?x*.y)W۽{O())upRR^޵-?yH""L"0 KMKON~hmeQ׺=~K#\nOLA^ttBN=x B}GFHK+țZã'O) Krr\4]B^!O=78''+;|nGA-rm̝=s=^ZZN FgϜ?`1>^RS_<{b”Φ&'[zT_T8~첕k\]fϘvȱ1'cqT†>v򴂂uk:-w.BfކH$/Nbz.bAͻ]JIIM:ix{;|zy w^sfͨxaӺ5>{NBBowQ$GWg|xW-|vNN͆0=0/?7W<ꠣӐU4Zkل4?Q}co׭޻3TU/U}h0 Ҭyf!xx"z{ s]l(7?_濗բ=[Z];jj.FF?~JK_@1`o!))|:]UE @ZZף|;vZ,?=1OiHKIvR5m]6~DQkUk@VO X:@ HJJ*((LSk]6~DQkUk@bjh :+|@ :+|@ :+rf9ojRE9@B!YIS~J魶C3)0ڬOiF-EmpVmf޳{$[etğ|=4˕_ :+|@"ן6 9ܤ\JR)[y1Wւ+߆w[a-E5n/Z: ZRs>NKK'$ZFjZz7orʔ<i?~J t'O!de:M0{-_g'NUZZZ_Ӓ 554Z:(Z^sccΦ&! F߼UC|Ϛ>UQk@h/_6@͜6wВ]t^JJ디Yq왎l6w**-;D"gfB OsJzz5?GFǜx_W%M/ƒz-3k4BkxR]֍Q&N0빹y13|Ĥ/R^:sn6V-Sɦ&''7399zLf^6toPee屓p8wظ;/] 4զL0?PTL۷:=r\ plm/(,V1˵HJR,_ee};|x>$MRRr;n2l1z|.׬\tګol\B4f·Mdt x/!QGG!t'Ywlz~BGڲaW 7lքXii.5ڰy\P%;wﭷk7mpՁ ؏=sӃϴB\v]KKCкUUU\ ؽBO"N>o^dBա#ǖ[7Ξ9}xISϟ99w^۲q}ȵt1k$LL7n5+뗯bbwn|bv6ԳGB?>}!^cK54r©[\\>~,(`4Us"BHMUoN2BHKKO2~b]`Oe`okg3.=}zfm9o߽nA&O>歰JJ!4a4<jͧ jp>411f5l&M7~;wWݰe㺞=#@NRۼבc'x<^RJJJ>xBZc \p`G^]}H#'7WYY !4g0V F!ӕl{yyAUU!D&Lfj,5歐а>I$RP 姧h  /\Ԭz+))*V?XUG"%$||$$$/bb&%%~yyyZ`oqկ #׬k+SXxw1 IM2)8$9߾gٱ+*:IHH :<Vxd+˧#IZ54cڔX,mcGq8 r![t>s;Sja/R^>|ѾfVcn}0,1)9gqW}E"7޿bֹ"v6^=/_f1`@04Яw?OoZƏ|PTkkˤ;=wzI*:jp|nyfNO8?~.oް+Ws\ee3iki_2HJR7c<93w'^JZw5wnqsfpk I~}MO<߂ kW<|t @CC}jjӧL:|Y***ճ7m^XXnJ|./,-}_teyyk8eeG;_Қ1uኺv""OZ߿oxD>@+E()-.ŰC _y˶jjS;1*&62*&`Φ &)5Ȱh mR{]~f@م2"*.^Iz˳zb[R@she"kYM+-|\>i j  :+|@n7 2e%Eڪᠭ@ۦش۱i7f;iCKj,im\vXAbt> Vy } SYzs[(HSHbm4¤b{ZoY9CX *H mpP,agSBPRR"NV#u ~ȁ~6 @x/eͦhm@]|޻{8LG2ldh/Z8|!ރoEENNLz`2n\C]Qii'>!nEf{yBB6o)\eMֱߙ IDATVeeGFKٺ-۳{v߻_RRzhy-SoF ߶94z\.7&6N]]-)܅ kˁB̉#MM ;e)ͱixF{vn頣C|&ͱbe1p ! :M C}//\.CjׂCߊ+-YlQ_*H m^=q&FC} {xx[9O1G  CBca=lTV77o'La>rOz 񍎍>jی9 2|E;x~➀^C|V@EYmЦkN;O ovf [ʃG{ a6{^`]9rgSBھАJv3__2pళ`gwk֬5kڬ߳B99^C|svh Y鳑c':͞wk鵶O^'ݓGWOg2TUH;LP'\KJR$څz:!ރ=S^9ɣD^H$&&?8xتeKގܳsVaQKǎq'&|άKdV/_v&KW116[074m0)(<~ZuYgۑmO |Gb#B3w⊊6"Ut܎s&##Pn.^Tz^'ݨOݽx~"BL&8;\  QQV>u&ϜA"Cs¢KWL4!>&|"ڧVRR_KK.]qp:beedHIy*v{M{1_@ 9;;RZJ.Vs{ :Z ]Ttk=$$$D$MM 'G{@UUǏHIHH̜6%˗/URT?mRRT I)))qcF aTUJK١aCy99PQaZUWSp#*=wO_RRH$/;;GT*JZ[;::Φ&x DO-[wQ`0w X|INiu>+ںRw3bZ:"hD -b\<BJYi'DfaO ?]̪?g"**l6YRRT99COS))UA_ׁ@ h wP'!T`<}T X,ڏpL&x|PAzFMMTt4Au3mO L߿o-](HTzk&tpR ?Tg磼<2*ء}\g̙ʂFS724fVTR^)++?\}t:fX***!Op8{UVVͭ ðܼ| uuqjdggLlޫGDT̈CkN2u`ƭGP(8 ~hW\\" i^%OZJ 7oǎJYnufW|l\^<]iNvԴ4ݭ7o 윒Rkˌ[<JMHz54!D>~JE:q '76m6ybq!/wg & pJٳ{]'**Ҵ+BǍ={uieetzD4YhjbLPcnljȉ{.}.," \]BY߾Ϝp2NYYeeN3<`O;8ð4:  ^=ĩ9ysg8ph)&ﷳ_f=.m-@" vmۄOtqr9w466ܾy>Z0a [GF/^0oBhԙ!iiSS5Z[)| YgӀ;(U57b^dR$(ݺ3iڙG=>g_qI`SZfޜ;\J) r Ѿ*;lܻк [ ,]jKyj#eee***=Ǎ:w߼ 6a蹳gV]t; C:Rט<@#B[ [f5w}z7VĹrQ6E |5k9MgU˗CƶU۶5B ?D؅2Λkkk7C\h'V<*?l'_WPFOh 0mHdNYBh^=O]<\ ,,"xr4VCrFdUf `z] ظe;R^^nPG{ۆ5l!M-^MUhY(K('e%8cyr;A[ ;W؆sۦ!M!U,))awgf{~ "7H$_mh+}mt> VXx|x5@$ qC0FrСS:n@#YiR4)|@. SYzs[(HSHbm4¤b{Zoz߂ƚPxVDjTh+D.Je= ?" 2؄1Dw@f=~={toB'ϜKe_gs3!|t谐f6 ?څzYk2f7@[PG>v=L !?GxKq ^=.,꩐_9w^he1PNV1vbvF'Κq;VgSd|LBl;s,Ҭ.lÖt+GРb^:B[$gLpн:}BؕKN6Hv_+'_X E [=ظxc>CB=\<-휧̘dYvNߩ3">~ ;fۨq:8o0a99o߾_JaGGh:{"y;o۹@yhXQ -}=o޽/v֯.-#}`YgӚܽmCZ|b~.'0")ATkSsOu g$)tu=>ի|<)/_ͭ,FD_Y[Ztb\NV6+B燆E By{y [vhO LMfQ܉bhO$]];v쀧EDD$G}=^=;SjiS%$$Gkl"//c֬$Tl&B꧉*e?&hPEʺWP'QwX.Jn̴D"PSS! 554g55AHy<>B^B">{>B`%%_vX`hhآZigPafe{ Q1U\\Zʖ%k6l7gz ZŻYSI$B4YJna[v9OC^Wg磼<2*ء}“9X[Yh yF?zP{!Jyy\myDb-.?_p L&^?|0ɳԵٯ^.o(.k1z0 936uxdt旯ݽ{~ð7o!t n}wʾ}} iÞ$k#=!>k/څ2~ e7/nZh+B?jn]8\ v3̫1'.7tl7C[ TsY^,h)c%%%lv=XogSh+D7H$_mh+}mt> VX$tH :+|@  SYzs[(HS:ZTlO-[[BX *H mpP,agSBPRR"NV,#OY7gMU`XxdȭG4U򅳝t;` 8ljGrl6ݞlm@]ؽw0@ :[hXS PaQ]>myݰð܂._hyu<%!)agXxdll6{ʵ^C|)96nqrsv;q'Bttgiy8k2K8Q>z'Lr_w:~-o#ġ#P3rcbՒZY+[旯gΝp,)U<~t#C=;tѡ egƕ&Voـ0l=P!5-m٪uu*.655Y4|xde+c"n\d%>G^bM  &r++ps2cNZzߒe9^C|Μ53mԸI Uϵ^i3:y  O ۵}!JAg@Wd2)agnڃx57{ ur6klPNNk7=}|/ZdΞg;fB;.x7o'La>rDa䜲[;yy BPD4@{Ayy ~ }{UOlj2rPy%H].J?jr$@_O!uB 2$b/\=PN)^㉇OyCwVXjhozh_e<}bOWDŽ-jԴ4AӦLfŲ䇏=\Zz^'ݨOݽx~"BL&8;\  QQV>u&ϜA"CsxUk :DrYYBEE굟Լ<܈Dr]?g~-$:ibbZ\UxACNǝojUϟ3`**l6B`j7CEYFĤSg9q䠁^DHr3B o4ϾMOcl}UȨc ̘3?1遵odW]YY)7/O&kYǼWÇ ܿ~V (JpHhtlJJJlvyy9JE 433=~x#CPIii4Jut;w!E+|*4ʳQ{vڪ|ˇ4Bu*v`wL`^^>p L&3kxd1 {ybA."8n+VKMK/++ >|$"?YhjbLPcnljȉ24?q@ (-e_VueFFH?#mϞXRZZYYy9х_dg>^Bz.fMX>C2s\Pf ț 7] <&)_d(@TGpH`O)n'N͛;kFCc&Nv61>vxjjΞҧsWd2uo zڙG=>g_qI`Sϛ3c瞀7Bi4A." '6oزm`M)-=!x `]{H$mgL۹'g(e ;vQouzN׭ljT?^eWo!䥧.xi@@%FwVmHU" by>ʅ.!Ք|4V-_jޫG ~A hʟm/XS}.C](yv3 @[ dʈpU.Ob^2Gx Oxx_mݱÛYXD$"r4VCrFdUf ǩ37CKJJp,Llw`~p 3n,O\n騭ٴ"Vh&O7y⸖JB:fXRRfE甑ogSh+Dh_"H4 ޿V+|@9kZ]~KGh`bt> VXS@CqxIũ[(HSHbm4¤b{Z/ IDAToz߂ƚPxVDjTh+D.Je= ?" 2؄1Dw@f=~={to[~v6C}t ljGrl6ݞlm@]ؽw0@ :[hXS ͙ }hce2rF"fRZ:z._OqoZ=a& ׷@ "]=-l<-JkM~2yP$ 53;5䮏|1@RˍSWWKJ~he9l_9w _3g,ߵًe+vثgF&r#oco 8xبzk6((ȓ[XX /"!An6m!ۻؼ 5܃0zx񔗯N=t':lKjj_9q@՟>'+c]vCjZUŠiS&ݍXb]+r++~ݫ'Bg߹vԭΦ 9y3ܼvʅV۸mpΦ,_NA^V뗊KJ6na1?OGQѱdҕkbCBw'^ ߵvԭvb1/=I,)- n: :s/D ^hOݟ*?]^0eiwMIT#=svv]ȭ񉨘S'M֢P(tE GGF{ljB 6emiѵBos9Y٬oxzG6Vvλah rw׉H${:?~h uu53S{;m--"hkm5+ !Y3(̼Y3p\]#bBee=ݾ}~SH$u;]] Dsǎ? z+Jkn,iAC0J,+t>;Xrtf g2%${ &Qs>CBX,oP%RFVfD"YXސM ytڏ $<^ږ1y,e%%#b'<Θ3?1遵odW88+++ srNDtC}'G}wnۄ3wSg~*TfeeeB999D"QMM"!ѩĤȘX|D|BRN.DlOTxRRT84B)K * J|6]=$Z4Fhp L&;r]#aؓg ri`dO}OBn!߼0w ,vn8|͟jF&}sDSRR?n.^`ƭ;rssTk+5[6ܽsJ`,<kKy99Х.NU.Ϯ ڬiTQ$ R@^˳( aÕ\]YmJ k*Rm@{q!OJW]]Y}⛌|L0toxNٰvݝ 29Rx=yeh+$i{#t$P(3>Qy/nܡ<*+73۬VQY7bm;E"QimiivЃ-g=;cı΁Ak+*B;1_EEKWwKHtGhؘjo޾;yxBYICy]~3[5s6dߞ]גFE8;pl}H$Zn/+=q,͔tЭn<}=WTG_Ur_bm&&+3_>&2ŅMM#ݽl|. ZS*3o?Bs;2s\=&orn͜Û9 CRӮyN Bq8Vw`x~"8}FYYyoN:]^^w^cFľJJN6Ƒ1eƜ̬0>!i!{92<!{ ‚ϳ}ƌgK>9'['7oߵE"Qm] !Dhk546"S\F ̮j|kEE/_͟;[NNv_eH tmeN3zMMBv$F(b.v 3ZD=rBjr֍ ^Ϝ>//ݰl715II-f[g1fTΟ< v޽'&_?.Px<~bu,w=>z݈៦hH?|ߏƏt7ohT%OSFY97% |.(,*bRTPpʯ͛3H4דwuvJ@ LM,Vq?|,}d_ck36MPeE%i!!)եY7bEbR &=ރ@ hԡyj?O8|PEp5yjj2223, o߽'l)**<bb\BD?w6/lOtO7߸ioldJIKp RФc&Q#BJiiGaKśZZR3ϩV fhpZ4ZyyEgZ]S3҅8;^q ӝBQWیEEVߜG}}}ՔOWb%YٷZ[R(*'&}NJItg ?y3xÑ&t捪KD"y4aC4ybIjlxU}MjşN$K4i=xHQQ!dB75c3>]_OuAV)r یF޽JKKy<HBC'Kjzi4*vho{6D #(**eϟpc$&<|h┗+)+ rncT(* |P(ݯپH'Xbo1{mܒW!O7ׄ/^D;gb8K9g)"<'Np|sm%z?=}!TWWFYS;%>G5bj@נjh :񓍍ϞHIMwuv'q#&9íySb"Ȩi}1>agLMܩߚdv rX+2BrA^ШTaC-ƌª1\ntvrr_jŞ#<}B֚@-MMMMc]XU]MVVntpl///參k(!)"Sg &ʶَzzׯ YYY7!4p@9f/[Y[W7xЀ׶ tk w񠨨/4p@wg?>\uMxnU^iIq{ kWu۱er+–9k^MSnA[ f$:kXۥx|Sc׆:cPgwJ<+z (})ߞM~TVH#m,ͬ(ZaS2~qv듾{h+>& ~HDBjuu5Ǔ^D"=j mt@P(j{@[ Х.t)z=ݓl? @t>Х.NU.Ϯ ڬiTQ$ R@^˳( aÕ\]YmJ k*Rm@{q!OJW]]Y}⛌|L0tnB|BRl\±Ñ_+ =v5er/z&VHG7IBPz5f|"?_:ݸCyTVroffIʐa>\<7o):k[sppdSlcu+*~Y։}qPGWq.~Kuk?.zWӟq㮧NyƔtMMZvΟVy鳞 lRbMIҮ]XkNGpU3,0۳w^ep8\}E֖!!x;tԁ}KWq,Dڱk7[S[0cڔXfBwc *TobT:k}by>&2ŅMM#ݽl|. ZS*3o?Bs;2s\=&Lorn͜Û9 CRӮyN G@555<|sONo޾âm|䙀 GXfd>,1>iN > ?!Xb >cXT^^sg82=qݦX'Og9<}6$73mZ!jY9|>?֟}gD55% \!4f>8IHxiPEe͙trEe򕫧OZJ’E ֬ǒiP$oQ(!庺:Xm_Qce}jN~|: X㬭lc>.\MI7gV/]"h'14t5!TWW#3Í24744@54Yb)9$\M3{̌i? BqofB#G++)!TYQIk5&C,|+3ZNNǏ=%?oLyyyyyyX̬}}{[׬VffimiARo߾+錆q3ݻ =_DYpI q8Eʶڅ߂H =%}~||2+\gw/PScI4l<_^Qٞn-q3@_!~%.ǫpB٠lvߝ Eht2dde|BBQ)--k5Ѭ,_Gq88eH$rcҨOe>/+4hPiiYaB 5<3a>lUk746:_JEmݼaoXפ)T u˻4 Dѝ Ob_?z)Q4- ʾ5ڒBQ)fԖ0@fVvRJ*6;=ijBGxx aNZqcDbv~.Æ$^Mi:u{摃D"1&.jY?OUUU>;iפ ^jIS6[ مU8Qx{c955fG$짦gПFb\iOaK(EES^^^47FbRÇN!NyB(_N9VM򺠐緈8q SϜ(//7t̞1Ǣu忮/--{ >D">%-]JM9?]Ǎ*(xz)qBU^n=6"t7sTɀ/'򑼺!N hy6Iepk^p=v/<2jl߆ƾ}LE1u6SF;w{9;ه7֊3:uP)4*|P1j 73ݼWZ!}l@0u\PدYxnԤ ľU; ALo29a5hh+lD-5StLfVNm]BMBRr;gf޾{b>:{#v2]*ce3۞Ll򣂶@ 蘠5>nlj"<Ξk1Q(XYQk{f)ָVOW>1m#da؞_wtw tE"!F'ar D"{ )@P`@ldF IDATkKA] :R08S^0.t)|KA] &ګ/\]YHaH$tAV'h+gWQ†+ 34"F@Th+. A[ EW|lLQ-O<ӡ :dWLO/ v5er/z&VHG7IBWx3Ӑ!fLҡ]*+73ś|tN'pfqo߽^-=x!N"BlvNnMff > uue8e势ϴY#,u4m=GcccJjZvΟ_~0 u:Kڵ7|X[Z(+)u:X._RG(Exf'rn=LB(/?;pY:@ s.9ѡ{[K(ww"ЃHMcj2{|L,S\uG382|ʑ^.4o?Z8:yg rn͜Û9 CRӮyN ByN}!)9g,GƔsp䙀 GXf,Vӧׯ%!)I\˜4q^yveees,SRRVYs(|ۥbC^k>iN > ? BDISXRH${gBъ%Zx<ٱLRe!2$C(ݝ mt>bb^%3.Xp Yl-q8Wpsx!]]?4$b6si#ܽ`_x֍2Roؼ-/?S/s=5qӺ5!{+*8gGc#Cc#k7¬[EoXt=-iߞ]zz#CB'1c.hT yel3uc#C$j?y@ pν Nt-R\_OoR*<|IRSIB]QWEZuAaQkml?6cLtgg3ԄsKzy Y9{N/''ISQ!# B&t u# I.}pͭ,> ZY!4rB8!!ȹ:lYYY#CC<^b$D"PRr+Vn9f3zTQ?<}LʩI~m<]bb\BD?w6vSm6+F}-\[KEaJZUW")%}LMm--vIh /@p,@7oq8r 1ѣ˭b9JJF[q8XMB>Y_O7ׄ/^D;gb8K:EEp/īvqIO> EEWy!&kfVNzu7V)0cJ$Sұ<1OD'O;H.Y>kT{;ںںff#1nK<ǎ2\"<{FޮitDRy 454dWD=H5AVcRexMBR͇ 3JLg7/'GիV#n"ÖtuCvm3דt.cƍn!R(]%%6mq45i*+)/Z+d/ Aч"14Ԥ%G}tߞ}\fR(* 焤dyQ#Xd{by9֖#vEE[wuXɬSu(UԈ7)<غ#XF>r F_+`IIפ;97Vv¶N?c`SM[&="ĕϚ~er@'gJ*R(((,_-_bʌ93WPfX綧fS2=+V~ػMx)vX,ͬ{&m)ָLOVHљ k;T1( 6m.]D"dBh+@ 믶@_] :R@t>Х.t)|KKU.Ϯ ڬiTQ$ R@^˳( aÕ\]YmJ k*Rm@{q!OJW]]Y}+F>ѨKK`'PSg 2+uOИ3kzw'7]MmkT>x qK_!T(*ڥXkKls/n܉CnjOtg`=\$Bl8WOBmߵ̘KX[p Ѩ"(.!bڬS{qMMff} h?w>V1S_w=BƔtu5?`#̇uhkofe7--:wt1ʸoϮ畖u.Tso޾;yxY}/GGb=~rr>x;x]HdA")gBg6oc}_K7-)n/:c*:ݝ mt>R3Ln|̹6 33r'$[}~(w6n^i[3.pfxN<}B(95PԴk|#B|n߹EHJN6Ƒ1eƜ9?y& 0Ƒ=9""hBڶY_O؉SX'Og9<}8 G:{Y:-]U9;;Er,9yU]Sc1!/._?щ1~mMMM-"(gw/W]!{D/˽{6/d8MM&mm^"֭^e߯w/݂7R"2$C(ݝ mveN3zMMV~Ñatc7lg4P,_:`BwKe%W^^!u:CCWy3.prsv˯r7E B(uc#C1Ǜ9c!ڙs槦g0\ZD8zO~pFVFvѿXxG%Ux(w- ++пzMs @Ix]PXTgm`g(X+:ٌ35gܒ^BfVN^ijTT!C}CC ]CC*[BRK_>8npsk+Ĥ쫱VCaV#Ш\lmm-G;J}|͛3H~{%9%U$!S=\B$i0<4h";'^=cYYYAA~gR"Swd} i#1J. !DR"͟;vlV6J-+4[Ry NV”+q <^-( ERrcKśZZOǥ9=Y>T)Ҳ7i"kx듮DGZ$ +IIE,ֈgI-R%%K?y3xÑ&t7VZVf7;V6o}BaBRQ#P=ٞ|(bv v G.NWStutZYƦ%VXp7mk5%5];ݾsoGp_w1665ݰYMUu/+%s$^R3 OQni7=JNM/(|cm9Fzy ֖o'$|.*攗+)+ rnc5U(* |P(l5!)"νY9.9a+WWTΙ7֪M\B%ho{6D #(**e"<\3r3{1B<o8*{>VEwCѿawyܪ92rםz 5jj+74I |#G>bb=ܚx0\?*f#.NQGxIQwoODTF u8x>%iMPA^ШTaC-ƌq3~#߰e{yyy/]ݐ]|5֟l &b뱛Dja{:x;,njn5E~Qf644c}(¬ϴ4ڹS5?&.=jBoɲ@ u޽t=nA-PTTfNF76~j-dKfKr}!tJV8Gu(2p55 F O=)֤.h9i@R9l462ZSb =%c ڮBmljiWYz]]obLNӿj}6r)((,_9C+tDה*ce3۞Ll򣂶@ X{Wѻnr|׊~x,UfV=X Ɣ_k\K&kwAV'h+Lc*hj'&D"{CUVH#  _mh+$6t>Х.s>@|KA] :R@ gWqm4REX( ] )hUvJm.ͬQ%z5 ) zWS']OVH#[whE 楥g0t3}Xbω?y ?$jj˔Xk:MGko\E BQ.OZ[b|tNsE7njى>/eddLN@  IEE,999S:W*/ydr.JE׻}޳/hTb_I*OHK8v8!QnxDT^k%)(pmVέc'JJK8>fN~(6˖.a#BI)'N+))\7_C^>~D7g;vtVSq>_`3*p?Db̜>ԙs5oٸNMM57޻ȱeKe3Z!EEE{[,,@HIK:|Sg3kkW._:fH =U#E$s%.1d6}=>~p/@ѱq㦔VB9zX9 B,MsSjz.JzU}C=IH$Re%Bmegw/PScI4llکjY4*tnI-p8ZZ- Sү%x8B(Iɍ.cBojkiKJ?Y@FV!Cm۴U$~NK[K(Vr<KS"4GIJJh@ 8yܭ?o|>ʹ;῏%++|:VYG<0*--EMy:Vo5RJ+!Ғ/7nέto7y{yЍBv6QG>{bL[q~HZ&SP|Ou@ߒ[ϒf" o"UU _3?y3xÑ&t7V.iFZ1{:Eb]]m3.DYZ5<^M ugXx{"%5٩đXx% cOasH.NQGxIQwo4:"+"p}J$Қ@+12&OQÆbS#7nf:y99گ^Bap`: IDATl///參= ѓٵ}O~W VI?kmBW.[x0<[w*-ku`}4yB&LVVR~E~ 4EFVf$999|O>PG[kMP`^B</twB;cOBm\fΐҲ2Q؞_{k w񠨨/4p@wg?>\uMxnO;5i4cm/T__?%9!V _{G'xOa1!LN{~E+5)}er@'_Z*BT:U^wB!Ϗ~=ojlrvڰy[;ݫO~D8f~J|`JƷgeRߩ%D -ًnlt#j'kYىArReifE3i`LƵdvd}@vXIF"d/m.0@P`@kKA] :R@t>Х.t)xjUy\A5q*DBd}@ |kyv!lR1K3+mT zM@ h<!IW)᪫ Rt4uтyia{:jLߠC=#6d?///Uf?aWS[ZSx qK_!T(*ڥXkKls/n܉Cw!k쳌m<|xB=%2lw1/-)Yb6g҄I-MAM3wKZRS7lfbBOIέaᑯ @OGjzFS{;XVuG;=Aj,]JRX|BRl\±Ñr#_+HAmrnE;QRZ4sCMMܵYt kWaJJN9q\IIbz077S%%Ҽ9<%G:rސX.(ط;) w/k̟D"=oId>zk"ees,"h͟phx!$>G5A'TUUٸmmg7nfݽwБc˖.gf9?3VsƎp/u8p@P%`5#μ~Ū5(**Ι5v]XoayZYYUR"]r \pَOj4[OgbHO+*b̾ "PTzՁrrrutc'XtN#1LvssqsW,HIXw͆ͬb8+W{^K?}O=/--kZ%=gIT `W-B!Im%6b>lII$ax<^YIiEE,&=ރ@ hԡaSӮ:x7W}==r)Ξhm<:Z&oUx5eԟB}C=IH$?_.1J. !DR"͟;BWm6+F}-\[zJZUW")%}LMm--vI6_'z5sT ]D" ܖW)HZaBKWW|FfՔTlSAA[UjJR_YY@&RZL3~#nҧϞ9wp?NFʲ|!Tk&.'i6?OsEe]c\KNeeΞhe1N{CQQgjSPjO Db磾>jJt_~Koa4H)oAU.)i^ӝ{45#\!sU,V1\IY!sӬ EuA! #x&$%?R$ݹw?+Y Eƿ555I:"pcldKw444<|с(+-?ypӮ}@UTT|*{8а.#&SHV !DPc5o"I9K9"_̱>|,%Ux p,V{d~MVέWyRNS73"uu'ϜUSUpm>I=Æ9zdcc/RRӛiHeׄ>㧰[ Ȩ#^Ѩkj~TF u8x>%iMPA^ШTaC-ƌq3~#߰e{yyy/]ݐ] jέ,mÆܿ#"{;qy d=]=&[[Y,_RR"em;d5uVт]!{Q>ee(aC8eBoeQ#Xd{by9֖ݴf͘mWHBReK(=g)Z[uQC]mE^V&++/[,PC]w/]Os?[kE+-nj6:DiJ ;0_d^"{̎mЪk w񠨨/4p@'\uMxnO;5iχ%a,$f9u͙hڔlڬj#}&MQvѨ_2:Ėiϯq_xV29Nv"JI!MMM#{0q8叟<"%p4'''w gw/_p I^2nH:XoALl򣂶@ X_"\7l% ;JCCC؁o޼EiiiغQ-vvfks@'TYYLb%S2~q-Y}3]=@ x D"_ oBW I .t)|KA] :R@t>Х%c gWqm4REX( ] )hUvJm.ͬQ%z5 g>ÚXOz HJobGlHG: ŮPPU]bbh"*b J!@PSEWpއfgw.a0zMBzrL\MMMDk@ذ9B!Ν};)/dg4gy}~8&h`3. ;P~J&ٝlc?'s^Tĕu萪*ƛئ<==Nt}p8 u5Iƹ:wLfV?$%:جqܽw՜\GCC}B!u:p1עco]|PRJ|bbb7n^b̚1>:O1?vءCn߹K>,;!5448 N>]tـE G\ױa:!|eFL%^3cܼ}aN?g88mc֬اO]>|X ?N;GbRuD$|lY.~<-/g|&te.ye45nĤCGYO=^쿠EgΗTƯËƫ7oޤ=s[$$$l-uj3_߂/;vjf 63]&HTTev+(B63 w_:pk7h3(25:ĩFFkV0oް kGO!JKTTTVmv4|]!<v^2VcoC");~j]Ϟls4T}#'?]tN\\|WȾܼ[r?%UUUe-B|8L~Bѱ\x ^22i*e !ǎ&ByB~KL3cd"(..>O`fLL;#}/[zcGؿByO~R^p i_,bx4Ib0<BM2sf*XM!˿.W ^Ҫ0vͨ&UVTr8\6UUTJh_mqa>g!!j]FJJKq8\aa[Oc#jlle2DhRJH6%%%rr4!d?ъ_7m~r:6/RT$P^N߸upZ]Q֮[\naQoںiڷ2֬;o)~K?\]]odJJJx|444J8zp_y =H},j z(VKZ}%xaz!'X9kEERIiiJUVSsK,ZV^z2:v=**͟ӲPA^YWx\'~|:I-e2 qj~?6p8 u1=6mَJLJ kK{)wc=4h4 AeĤd>M(2IOL˷0& ycX,Q]\\BB=|Nj}cXe 1qY\.ً89v7440~}p/ZUbl !yy9!#&**%%mr\wWHjw\. "i҄lHW097X9wf:sW|q Qc.=yЀ_ibbz< 4+#CFJJ?vTSSSFfVBbw#":ݵes%!'G2)M[PȻop=2ҫ07s7L68`!Oqttir^ fL,[~SEE:vsGG?uƭc.wr;w]h;:zh,^)xGIIyo^E~**ʧ]ؼm'BD"̇ ms47{Pq""^x`𙷰Y+/'7md=]6[`s8/_]nSSS"]Kf[,!!1qX+_lsr[۷Op5 &q{lL tK;H!h~@l9xB2:ĆJO9uu/ *=_CKՠ>b=~JKKS!$))1ƫC;yI'* VѕQ?uCPlA/̛MT0VohDm^/䁱7w;WgGW}r)!^V1 tD.clvMM {ΥD!o ̷]`A^^޿* +\] t)H>Х`vZ?Gt)H>Х @] UV0ԑ7#tAT&+ j[ƐA2RyZ@T+.a0zMBzrL\MMMDk@ذ9B!Ν};)/dg4gy}~蘸Ȩa?;ljCV)Lfw~ ?{~WqwֿߩS;tHU~J*own'.R(p>c>}~ IDAT#l}JK:wtV^rXI,l=y߸ūd2W^1jjkFޱ!{]~4.U4q1# v@-3ԔLI}bؙb}nVv9ia֮ߤs:B*OO9"|mrW)ey`@ocf]ݛdRB,̇oߺ!TW_]uk 22||j޽<|]rD"]j`W6h"/SY`AQHgvĤdC};[H*/ذ9XSg/22(d%]yZ^xM9>HK/\logᣣ'h4<?kܴ)I>{ngc5A+"N9_VFSUQ^0o/޼yNFFznҲYӧ LG-[cB ?&LtzE/_:i>qYee!ZY,-_~]РsڿBcϝ}¥5Uՠ}z K6V"+͙9}}ё6i_+a6CC=7/PLLlE`W[ǬeZ0l%"Id}`B֐ɭ~vHdoCLY=pXn֬v6r:=`E 绹8UU1$$$Bjj>ôYsF:P` ZѪi/۳e+W Cpq_Ȯ>&>{x K sްRp!m;vܸzkSV@?!&tl_͞=!aG+i/_89ފQQY9uƜX[8:ح^LTTtN:X ޡx4.=Zf]&b0x" 61Fiٳ|), ?yWGYP`"?5s󊊊,FHIJDFEvY[pVz .o%cGWVBZZ!}=%%Ż9;p8A#%`,G1lf*+#SP!oCBFt-"%,Ms}e*e 8)7<ؿ_OXx<~!EEż^ƐJ8\XT=do/fm2ix͞={KI6gಹ/6))c?Hjjnj3kG)XM!˿.W ^Ҫ0vͨ&UVTr8\6UUTJh_mXlpspsy:}s}fv47q jf 2!$//Gg˕",6 !TN?y:+;2S(d^e6*/= "8l7*ϟ#ڱ㧌 ;esy+&/f+.|KQTRZڢuuU{M(2IOL˷0& ycX,Q]\\BB=|Nj}cX.x:gfesg/<|444ܾssAR~}z#GzhOhr%XdkcEګZ7^Qidh &&Аp;I@MNyu_0F p*EibƺF~/_>wNp>vh S㥯%,1`7rXlvw`|g>""][88]\Rrqr;x$#+B![ʯ {##-jya}={SdӁx ']Oqttir^ fL,[~SEE4.brQD$5xz%EH{۹g" l&OSPN&w>ڋᨩZ>|Ć[itqq^Fy;^AsqrW!mٸuΑ.G~j36{/!>z:yP[HJJ466ZʞV xOP0!Z0AWS[pI`' +ѣOCrt5/^u:es r?j-7mtaD*3mPWWq`ౡcNۅ^ ʯ^VTT"//QO?wtr;.y'o teq aj4¼OcV7ooZSS+%)9tقs9jqc@"J] f>Х @] t)Xp VZ`[SG\NJQ`RmCɴ4JQkSa.`6Ey2q555]կ bc` ;wdjt쾐nj4偋ٱ{/YIi)B?~ܼ`#@;P~J&ٝlc?|޻swRUŸ磧~_3o!oSíy2¦ڲw_6c?}~$e>zm>}¤fώ6i*S˗ l]|:}|?=Oy?d|ݎ262,)-7&>F5BEYys`7}"#3BV_0ů':&.2*&pB7|(#-V^3޽`-BCP%.6ek}LL֯DR@=z4tRcs0YةKRJrS&=4S$5z˶W.p8nҲ]!HCyz/lnn>zT|횚c#Kc]TT\"Oo<\l~}}Òeޤ9 !TPeXͬfF=y\FF!{[x<!tKWXY`= !D=qluuڠ*~xߛ'4 RBvEEř7e<ޖd3I6՛7e/-Y 9!f\Uk .oNXN|G"6Vԍ|9>3&תB**+BCzBAꣁY[=|bq8{(.)}y8j9q >3M]Uf0tO 9t٫7oX,Bzz)"efqyYjW&:qiNl|ҕ;b,̷n߅~G͝=3)>zڿxcB")L:)WοLLJ*'&ߊq{)?@=x{woOٯoQ1X^;p(dg[QFAk׷@K\.ϧ^投2Wup0yƵv[^?}P22_e@%(WTTle1BJR&2*r2K}Hpy+)P;z2ENB)))-&.Ù 21<6.e9¼I/`3SY/!Ͱ~Jň=zh(F6mgNJ}XKH{[gdh`gc>TWSuvtjS'DJJJȬO{z))ӆ x>.~t?QVVVF<?sTIADDdX}qcd%`oVUYRP.K sl"t`ޘHKK22}PPFr' y@X:;hxwWg͞_OEruv&xȤ]da8nT5UqcF}-<~8GO$޾#﨡AZ7#HKKWU1k]vV1nfi9f`gM)dry[+TUZO'NdpʊJ+ 2C}=ަJik-N"",!4t-%^X0J ***ʼ ʔK1ijn^x鰡O;t!"H->{uu5/Hoqёx=]6c畗>uraQQaaQ]ͬf))v[.zРh>ikivMНM>ut6PؗsD${AjFfB(>1)7/|V,̇cX Fuqq BFVFG[ !:)'/17[ѲW瘸̬l.Eڃ\ԡCn[]۷ m717.ꀕ刢[ +ڋCedxbBbPWRTlղfO=]аCuyy!W,K޽;544|)RȯrCC׷+..y"MgǮ|oTJ۶l ;nVcifd"ڹ't$=CIQQE"))]1|ؖm;'{OZ[(+dؐvm۲c^g?o9>3l8N/c;DEgia{SgΟxIId_ 6+ee[8PIQǿ߲wђaCMʇ dM;JJJ$%,-\@yَ]{kjkEEDzKLL ]vcӖ.kV Wp0lsr[۷O.-\Mm-o%{ܞ6+S(Y<3N`okne+W637'DRcıG҅|δA]c E| _9o߱X,.{^ʇ}p$..~$gV{+R{~qqΆ ?]YP74¼OcV[ Ɛ}?!TT7oXZ+[6݉zhGw?TdzY ƀҭ#灱@\vt[l6w?D";+0^v@yyyx0`rmt)H>Х @5!lCmJ |KA.$R :' :n,n 5555:s{ `2y)]H> Ho $[Ш)Gn:{^__oWc'̛3ێ @X9 ^BCO.邨~Mť f ӻXn3ɇ^|[>iWB1q}[3A^Nwt߸)|}?ԆJū;ld<|@we9'O~ά#+);zʾ|t;>Z7t|2c6(k64xzНΜXTTES(>z򴮮zbc#CꚚguqI*-;gƤ +--SQQ Zا BcךL[6KJMZ͋DDRsscL|) ޾++=DΫ̯Nޱ7bS'M0+?s7LHvO[<B8!TWWLA565 |1oM.L@#}R#}YUe{F6QEBc5UUY=\{@; 16V[H9"hόiwcv2wwqpA \nКu::Z1GKLJ\ZoeeJBLCO>)g/\ ٵ~}{BF+iSf7qWHhSSB˗{NJcFZccYc׃'|ᴬXRF,(J|YY"!A{#S&UU!e ͽǎ&By|/Ǥ>|s-324튽?LQ4p@¢9ϙ-**JRP/EoΟC l-I: IDATGI쨧z!`h͹ygO;{XVvΉSgB$qcbvޫگ_X[{~ჳk?淬p27f3zc쵽p:o!'/K&_PDTġr:]EET&"豽{IqID$!T^ND+hq8y:yNJ_'477-?GTT(+;ngB/_ybko4s8x(OS ~YyUsJCMDxwIAA[K36.a޷{8U$hrfUMۚa*ILf}}BQ~$@"&&~IX^Ҫ6i766DPSSB(&.~JٳScbef8@~ =9KNT!}"7VO% 顮?wk'N),jhh((RC17.t+8Q(dȰ_>1qj::GdX F+ױ{hOhr%%![+˘O\{)>~"|666˯\@hk'ݽx8Eqdٍl/dl׳<&s:rA]i3t4U(ulv"$(0߽ciSf̶ut[8AgoJ۶l8x$z=&{#kjn߹ܲ(w7B61{Fc{q8\kjjG{Yt \TZZ8~ŁNO=ssqq}Y 5n B(prG{Mr5v_ء[7**Lfc.B#S<|6\IqbO q=4ESu3F$ž}IQ WS[pI`&{(h -=C|C~_v z .*izUD6o[fgϜm lX=𷛾775;yYױԙSg-h4 ;|? L~05_~ҝ tCttg0!V3uMl\fAu=UnNCcSg[GEYGvۙvХ @aձ89Iڭ@H> Ho $[Ш)GnzN@V!'tAT&+̇5A+*4gyEEFńkU3sn\y ͘= mԞ_tlt+ԆJPrl&ٝlc?$sO~:YWGVR?vdo/~} ٱ= /ZK&+{"iiǏlϟeldXU8xXMMM#-Y?qrY^t쭽O6y̮ tBTDY-K eSqsїzI)0 R11#FPueǏ _@R $- TaCҲ{۴=+"bogӡ^#?y^ ,Zb6\.!cރ2t/ssqrsq+6Zeos UuIiAFZ֪ů4eO0۽wB߹jjFFخOδup4';cg/Z5ӉSfX;/_`TWUU>+ʞo6zs|s-324jgB+{g%5{X PJCu5UgG<6{BTL\-5U+n*˫}FX5rr!Z9M^^۽š"E6**ʼMeeJmno8Ҳ鎊2͖xN0m. XD[bB4^ʃ[ XZpljf5tAZHjk+x̋ݓᨵBMM \SZZHgS+A^^޿* +\] t)H>Х`v~@] t)H>Х`)@Xu,N@junMY~s9)1BDk@H> Ho $[Ш)GnzN@V!'tAT&+̇5A+*4gyEEFńkU3sn  @WΦ6T:c3f+i'~~̺:'{{;GOOWȎͭ웚x-PUQ@?ò>.&&?dW-޾!U[w&sBm"DRt&sq}Lm1=BMNpFvriBM]5PVp5$ Q$O%AǫKM?6I,̇w3'hnؼpXh)|yFp\Y !xNCC\\6)tF&2ҌDr?1nZgq~/^]+(hj6&.5җ.,^&R.ܽwB߹jjFFخOδup4';cg/Z5ӉSfX;/_`TWQVvwu}7g)3xm|_+;|:;eOBfͽqty^GQc=y:.XX\\"LFN F el$p+"֘e8\oyb)ɏ5[EtDEfhkF~)B#zkEY+榟7~[BIغ}FAk}fL7&.:4ZQÇ: Xr; 1 }._륍 ߽ge1bRRR'rFFfBĩ/^:v81:t_FwW[_;-(H^;p(dg[QFAk jJKnI4(aCԄ<:?kdsJdԓgֵ7PEψ~{|*2cGH !{[|cR>9X3op+{QcUUU@OIUUUio0N&>}U0p6Vl6{Ig:ڟ? bnϜ1DR:y"yHEED rѱ\x ^22ir pE;k ,d} bskM%-a# _Hʈ6!^Ű}LFu>g 1k>Brܷ{dNj|SNXFLm?r0hͺF111VUhlle2 c, !TZZ5Up*JEE%H08{[ !h4ڽʒjSQYy3*,6BHVDձY"ZÉ>r) lv |'77 )(hki%,v/':;EF+mVU1`ބ3xz+*z2:v=!<3I%%ezz!.[RZrsu =0lC`5͟2 [/]53-Le jpVJ'61p!<SJC VSZ ۅ F( A9"ORXPPKaZ76cn)\.7//?1ηuƏsO(BrDnn~TLbs>7ٗg u% qaC՝<}#]]""oqƷ2 ZՈȩSKi`^V_jlԱٍlBM]Dn>t* BYEea}=esɥ5&rğ $V[K ;:sMMMJ%zh :mˆаC6n5166mFf͘:kR\|CXа]{B m%%Z7g͞4}el{(Bǻ89>wꂳX!-YpS q K & +"t4+_ʝRczYuoy♡fSzջ{HTkk" u[eiIKmg \Mm-o%{ֲb;(2Х @aձ89 v5udq]կ  + V1dLoA~`TV 9  @X9 ^gS\.WSSQ`33clfz;)/d'%c'$ I!oD@PR)Jk.X7'R4h}>=}*Z !.M"Gl-!ZI4|??s;/3k kJbtwsrt});'7A+6vP:Nǎ{yK#ùvvFFnn+ddf/ZM +3x3g{MHQů/ l_^PX<')\NiiiY~cőrл G)UGM2w݂usێ<_o8Eŋ.`2Ozn@+:M>j yj6$UKO;{pÊ򖖖%N;Wjjj ッڿRfvڅtBgNd[Pz݆9Vut쟓|hok]v5yammmG41y:qy 䣮bme~[ cv]Y>^~/#xh4{?U~>aX&Iƍ#̝3{aŠ{,^d``` QHTPrJ^fիphEs>Xl!D\%fYj 4OՄȈsi++H$ҲҲrSRKR=9m<)LD?qsBȔŠ˹mmmKkj\] D"Q{cG[ZZΝJEH.NKQ ZѲhK Æ~*]V$ZUɓ&Hڔz3d2Y\ltU300<'-O&(wsuquq dǏ po۹'in3{vp"Ym瞯.l'3;?uOZGF,ں}Wonnĸ !$aՊč[=},Vlt;Cw[sph^cSK){)LqN-_0zԈ|O{^{To;~"<'GHܿWJ,[ \AVǛd>r?XJ}vd2/6So=}^*DWTx#ݺ h]5&zbS*\7d4TWJ/|r/uzm֋AOillT.UcccS4E_z!t:@_t6u,(IDATh($QH>@|F!B4 [ږp˚M`bW* u-a[kI&OW*kP׽箟@gV بL+^e#pVU+Gڣ7iVQG96EQ=yg@g(*bjo S:s_0/"SRO!V(-(\h/\FȺ0aWG)0m0*&no@plm#bΙ<&ʌcn %:2"4d!!aM"ePTqg)(\gmc~9[zm!wmrAInN @ǩر{o?oid8:9>:67s͟ z|6{FTG3^d2<=>[D_o1`Iц 8R\1aAQ L栁ӽ34+,*8?s~r-7eѫ&޻ɓ'nڰ6/,%yaCmr˪Oשhd \Md2\{niY:WЙG41y:%ɱH>j!VV*Nuws5rĤ㳲st"B2.upO{7usP[S{=\./*w|x#J{={ظ^`YqfuVG$t<Ui4ZJc'OaС/o\S422ohP?PSS[7?xx=nSLnjjRtYf!EQFFZ40p]Yss Hy(VpSSӘ%ڻ , ߉u)R82zv̿@>b(+J:t ё.I9VV.H$ee+)(,~x>OYO3zdܲ6'UTT2 'O0xu˶? ڷK_kߕjEƭl+6:!=nqcF?Wȿl17ϷÇE&|hߋMxo\k񊄏WT X[ܻSQ}rBW/zSva2*,*Vo]77@Z/eJ]LilW*5{gK!I4ZckK(.kV95ho' C_(y𫳼;ט6Š'O466Ri~T@/Nl쿪@gkF!B4 h($Q&]/TP@lɐRWo͒VfdH;>I5M;$ s>@|F{A|IENDB`gammaray-2.11.3/docs/manual/images/gammaray-font-browser.png000066400000000000000000003377741413310443000240150ustar00rootroot00000000000000PNG  IHDRJ pHYs+ IDATxwpWoeyPރ{+R"EQ(2m}3qsw7q^VweHJ)A T mVQ@ `*^Vg~O3   i@AAxD( P:y=v$x≟WAGK7;]:}FCqqkWz'/ݎ^gxCÄCatz55ݷʊnLTÉ]nܼKhlg9~ٞyyKyAɚ2**Lb$zxwj{;`KH>{~l/:}-tu?# ³cl6s%JJ(rM,cڴҲF#IzV! w;qr**ʑ3*O~DmM5;;iΜٳPUƆz.\LSSZ+R]]EmM Zd"I Dg+)Ih4Po i(+- Yji`(Vj2.$`4).*"JL&q:QQY1v\m:.0eeu4סQ&#Cwˮբꖭם;]x^,f3V,֭ۤ*Gii ᄑwi0L+X|Yn;]]ttvb0踂  <[_ǃe񢅔t466PUUAӉht`2p8TVfNfδzz(-)"NJKKP2 B"ӧMaK/+N`@$$I`ГNpz=VD2lν6 #gd0f3x"7& B^^&H,d2cٱmB!LFf @ݮz;]]i.YDyyhQkq31stnAYY)yN'VAA$ ϣ(u89nw!|twSQ^Nqέ8N.^h|: ޹ G7oF  JQUχ?:6H284?J*&HGBUUL&d2FGciah UUzIhh4J&!Ζ%0:`hxp8;֭۷imil2d2b r%;kT*E"̕9xIӄF|>b1reap8BdGeK8q @ |~##(B:^*! H&ν_TaxZ {r3yY);~V㥠' CG;|TUVqQ k׬ʕ6"(iYf kN_?̧vb88$iXZA~^D7n2Zr ~le%%!۷m( {GxP %t":;e?~8uEQ睷~:_` kd |~hط,se>$?/BsSE#Ih%-##Lj t8AHR]&fϚIKs}b0Xl)OEPUYA.nwtPZRd|u{$@sLBC}=.]fhx֮&LPQQY3ih \X0$!֬^PRR̺k9q4'OaͪD"|~?/]f󦍜p֖f֭]CӉlfMtvvD(⣏w284L,v*JhhѣH;?_l6-M&܅a=zG|^{.^L8& si^" uh4)EQx<`Ɨ(,(p%QR\+/ob6#2aE {ؼi#Ni--:}A9JeE95l|iϮJ.GWw^Z kK}]-{̜1uh4,ie3V.gڴa-7tbtw8-7QZZLCC/^ƭlWGeٳfbR \4Ԉګl"<^/.];2wddhJUF#|֮YD"޳ŋ"I?~(YfJ[ml޴KgV\7~{DIXj(B"$#geUUsMANˤR)$I ZȲ%0L\xO[^^ᄑ˖jIRfv;& U{mΝMaKt ?!yN' u\܆dtbY)*rcHh$ 鴌,g42LKIhrc\|F#:ݙYflټ/e00[, σJlʼslʿ`0ϸ}Iv>ڱH$=L0dק12⣥R<;а&sJJx'!cKMEy9IB:´fv}PUU7?Ξ;O:}wM xV@> Yxoܤ)A>6 h>:k. ֬cTVVF=k&'Oatt5Wq-ϛ˰C*yKP\\|9 ̟7*a45Vr*ͥuj8t(6̚E]]S?:!Qv߸age_ysqe%fڮSUY'())atttv*șf*:::?o>s)"(3.tikJ$֖f=,ˬZ[;w0Q"Ġt?oy8磮zϢhKq&h4FX,C!2 yNg.IRB!v͟O B^G`XH$qNP8  JZ,L&8Y XfHJ,bŰZ2CsOѠh`HRffS_7OL0BUUzflټ I#p؉DcQN엯L&C d2O$pd2 ZAlj%iZ-`ހc}e(Hv˕_ښ94`V"olO,˽׮AdXvw => Ϟ|5Lӌj\K3{#9֓J8s<˗-yO˗ĉ xi ߺXAxt˗ۈRϣiR} eǃ?X߯TWQ&r-]1F  <@ ;+8crtZ-P ߅*|_SSiRdǃEFA$" Uh~șE0)tF" Sрcl\(+qov4EI< X3A$I0",)zbWEC2l>m  ?$y-Ͽ8f{][sEQ8z{}!ֈRUMK->fktfeY˃,]#25/G~Ë_:&fC]'NfOF :tP'&Q|,N  P&PP?F/Gp sGG<#tzܥ̖G_'yF Mtj+.l1c43{&ǎ]PdE4cKUTDyUXq gzk}SD1ڮ^r،nF 3霪 e 1H(@4"LPZY'VnxfO] b0wn3{r>Lf+uDBA\bRF"  iH&uޢNgVGEX<>!Xhjz {zq\lVl6+Wg{t"nݾjeH*ϽX,F2"?/[n``Tػo?V` ȲK |n޺ir";qT 8+'Kֽ~"-͓a>!E}(_hBTE&nܼŬӱX&7n?q8f͙=:a-jAxXSv|^t*.HkN:gVL} :oQZUœdd;Yen][( 8}ք< NJ*p?᝷ {ttt2MKs#>Gt:MKs˧/@"IV|{*(! pՋx20sav„dF$Z1WEg_4巿;:)(pzy3R4.W>&֖f. ^*7K<Ɔzh44Ξ;ʩhEz:Fƶ;?KWx픕x8u ?z-֯[Hi6=k&^XC~݆`3@6Ã,[o27mɰh~ycOwbK/p1T.]BZimiJP2 U蝷btӃVseRd5o_8=o>;xuwD/[&y-[MAxXV}{{(Л04h4HD"ilVmo_? ͥN. ꦣW@o00mZ+- ϮM|E̟d2JJZ_,OTQU% g(ri{KkV000D~~׮3@Iq1ߖN*475rs>Z;]LF*+*Hh0p }=$*ee\tKaA u2stZ-Fa҄^t:ŒӐEY;__M=_c FeJ ]JM:d#.0:ULMpt~^YQAww7˖.awt)..&pPP"-˘&2]Xp:X,b(#> MyYV,O8o<'/T:[N֬^lTWIDUU̞$Id2 Ht놆)(,E⢢xL&C?KSYQ0 up8hmiR\\DMuՔex+ IDAT,`EQ룢VFSSS0ӧ0M44SYYAQASc]ݔ{ vN9\yMM5DQ֬^bcXruPPP@mM MMa)rq88e%%8\.W)/+d4OMMo9D4 #|;Wb/2,qȗ~oooF(棏wby z Ow&l5s:ߎRq3?<#>]]̝3;tݳNz(Ee٘9c6AQI?+--d B\>9v;ӧO{+g{rA`0b6^Xw~`7p16o'yF^Zp ֬ZI_7ofO^ ‚oad)hR\\4]NX9v/Z8e)hn)pgW9uիVPvD#47a0x2?0 (% 32!;iI@)ZoY ӜWړ}T<|¢t뤿?;[/ x^zzzl:%dxG|lv ױltuhRKՒ-Y  4wMkm]X8a=~AJK맢b֭]GX?Б#_IJ EnjkNAxnFn1X"dO׫~]$AߧI DXAAx<CCJ$IF G"Hw댢}W &31+XxF(h7}ӤLUUIh$ ¯]dxoD|hZ- \ ϚH4JqQcIA4$I2r&Zx0h N+3 όd2f\~7oa0p9 iRzz{h*Ō^-srcqVknl] $??ݎ(R)iuTT?@ZNO<'P\R͝NW:BC`yG|E0*ƒӠ MjNӘL&TU<^/z*lV+ZEUuZYsQS]MZN(UF̈́e}JKKtuw ().t:M0DH( TTxAp$h: C_7np ?UU%'rcT e$H_ X]~NTUs[ o`y KRODAIG+Ih4ouzlV+JCSi$IB#M7'0͸&KFcf\.d D0" !IN 襱f#?/2c=p/+H$9{t:(߽_gd/_>z+Wۑe_=ntup 2 ̧yajc' < wT*5a( ?0@"+9F:|ю*chhxeْŴ<[ox UUzRUݚOcCd2E(,Z'NKW0 D"QOkwwΜq4AoZf㡵7n̵ב$DMuU%Ŝ=ŋ+5GQ㙳rRJ'lkcՊ67cZ+7Pvz :zW^HysڂFf[o礴 /cLA~XL&M&=Zb"MM,Z0VGQƆ mV+zb7nk̞=˖RZR’ŋI{3\v=x= cܹHg{?MKK355x^> == 0sh4^Gfˬ044Loo/7nkXh!55,_p$LGg'CC|~LjG2OvFMuL:aM僇,YF5ktܾ Dk&PϩgCH ,gkNZ-NE3ht:;vZPUEQr[&A3HNL$e#h4_-_8^h4:}N6әS&AQz}.P/z=, |-`S3ԯY4U{d( zUU1cc(ɠg-d-3g xښ6L c2A`0LLLhjO8W]gMu/̘>-wnČc}]9ZOxe{&xJ_YIc|v7݄A _cTfْO| O| <<| I&0Oii %SR畊:e^+Ev$(F$ld2D8gβz V C~?Z[}̞5;w1htw*`"70s FGܼuӦ =FZrMAx$;~_L,;p{TUV;XjG`%e/D/_`NL9J:fit g@N㝬X]?+WeYEAfRSAgW['D"ȲL:- 'm!)#l޴rtZ-P8fkUQfz-\@C};~T:eljyspDvA9FTڮOz㏧zo:6~9癳D,}R~:(\m6a۞1:Q_vX G"tu<>ߓT*@#>{&#E;2BUe%8F|>ҩŨ :>Q0H$vOjQqQ?ɏ&l[l ~w;qW~>˖.|̅$ ɈF!ןMοTWW3AfN I%vL&hNdEQD"h:߸*,7b!Jf}&I~ߣP mٌd&c1[$ DL&͖]3`0s+(}?hnj"njjFF tx"b!dDvswmN<͕WYj%DP(̪lBebzk*H&;WQL&d^O}]-F x<`K"l6d2Nd226d2hDUUdYΥQ2l6[.wd^\G:&cZP%v&(z,f3Ą2b1LD*B?( VIFh$ 2Q%HجVR&)[ΌBFɐN٬$S)2``0ݴ47QS]E,# bXt55J>EUU6XT*ޠGͦOc4PUX,*VD~eY&鰍-,򷲫֖ܴpxQQq8(>w}}6 :a6.~vOmk <6Fz8tn Jmk|N̚IOo>׭Б̞9>xÏO~hmi,\QXx!En7O?ۋ,gH&̜1;?b$ܼ~wOiI +/ƭی|100@"{Ba!?k_󤹩ښj>ȫ3'3>ڱlf́45504?ֲkg476r-j…l߶5SXP%8xy̚9 i4̟7.jr<^/Cäi#fNgA:;p+W0t:jk#bhxko?0#v /?ɲK줱Xz%vZ>B^E ZZikoN=74Z9{<6+WPVVJ_?pʊ _dBUa*-1~uup*` Dtm,k[6~ ¬3xt\n/~Yٶ5I8~/O&SR\.=e**ZIBӡ seS{IUU. sFC% cq>ni4N8fhx55tt3,_8;dqfe ю]GG1̈́!]`Uv(//CUUtڇk G"t2`Xg㠪lQv~֬zn-J8s._aʦS::X߿![_{5}M._ib1}VdYfά}Wۯa69{7ou G)pزy۷N܅dǴ6pZ9y4Iu c)))^T2ŜY3|>lp6###4֛줲۷;nG:m[)[µ׭EӲ`\|u8;:zGdzzz'jH$H_<'[_˗\y3pN7˗-ah4noPx7^BSӳnh4rAf%Jxqb2zGH2Kҩ\wUrX\x/lb̞IqqӧO27Z.r 0 8NǩgsM&={LFTJRa].LѠ]uή VEQ)*r/G1Ld}_a.Ӷmk:|?fH ɝΟGUU%Z[rZ[ٽg<l/s>U|>Ξ;O;v&9|8>&K]\Tk|{ TWW1PPBJt:6:/du̟a>&Ͽ8rǔ$iBگoNJFL&8bTWWbR>x'P_WKMuUvmc$gΑdTWSUYAMMc-{z32>M:f``*߸KrjeE9gϝgQU*v;]u?p۷;{_<-] . wdW~#][SY QWSM,ghhVKa J&aE$>ho:CFNȈ݆h$Hx1 \zTU%t8s2G0M8El61[ BmeL&%Ewtg gs&EQɖ-[r(rUu03;ۻ;ggfgfg{M3[۽]9AdeQHQ"%9@ lVɖЋ{@~jj48Y_ >>@0H( Q(~(P} N⢍%T+V,111h2ضe YdR)qqF*LNO DTDL&cf IJLn_"&Fw< ~\n7R$ܨ  {yyٹy|>J%@χJbdtѱq6_uv9;x| qD"ff,2 z}$]X\rP(bŠ?mv;vx.711_xN{>Ӥ$ڿNeaaARRbmqцZ<=T"Aq{p8TJ %|v_j$ Bh4jgfP(`29woy3:>A~6K:2]/J(_%*d}Q~ؗ0^233V_Ƶf֯[GclټiULUX,p]oyy}P u#DVB. M;(U'tGRb=l8}5eu?&B##AFV7.^b%ZZA*hDCpJsĩ3v"ׯu H[[;Y?̌  9ͭmSZRjgk|GWs~rW,e>;zv8zk*BLLNry 9+5K}p8VKo_? 3#|pĩ8."&4rZ1pyHOZ>8#P5J%17?SgX$x8W]D8unj.z9~4S&RS"ZS3/Vtwcc4I{G'5A>?q6T*%KKK9wCBBmr d2Ý11_y5.ʏLu IDATO&'%E)+zEJlmUXX\䕗^JUmS&&"fBiʧj^/G??}irw,V+ٰUvlO_~Zܱ s6U_=s>ܜlW2׮6~ ]ݽ?Ak[egd̮ۘZ#?>{M(lvNjD7kj6_< 9z"uk)-)j.^,+sBٳ{'gΞvq: 9[]ٖH$SORw2`ѱqz"Xv.݈":I}XQTTȺuk9s3γXLwOodZOpp:au<5"crsk@NG0wG(QD Xjezf&799or\5ii9[MK[; 088L&#w8277xj*MXmv^"䞾~23# ,} q:QY\\sER)*,d㘯KNvLzWٌL&#Km6s3%rLyh0$!>})fxd4FbbbZ™":`0H&6oڴjiiE"gfHMMaphuںˑ1Z{z),'/7?@AA>~pTrA~~^DT(λ; oYĹH$BT|Feˮssf" }yۚ6l6}pBOܶ ~'N'Ju`0Y&܏k@lL I$&$DSSS$KK+" Bҕ"S@`bl)V!(~ blT*6cmLM"~z{(*,r1 iI1Zߏ.u+oO{4r_S&ͭw r9Us6ff,wr{٫/x#n=Nvf&Yhi`+ Xl.#G?'7''=088Dm%>@ k.[ngذ86$k.Fꚋ,\x^Jj-ҕsx-TUPzK\SJ$/7EfJKVYS^rk226NnvVd7s/5m HG*rMll .29Wl|~{q  1L H$8N>;9iL[, ijnETe``AJCʇonAy 頧V6x:)-)&]jgdrDN1:} tOu#3gYS^ʲ˅uvuVH$zsKƹM0$ f>##$EYi tZ$b1ךx=|> =}XV$ W#J<=\.gbrV0zH|'(U*Ji⢍81Tj+j3z&&'i! TJo_?v \F }Z$1Mכ1+&;:h00cj1Bl6;MכdX\a4_=>s$$c6OچZBAjJ Y;;Q~.]n`ph.Z &4Ix)XNZuD"!FƉ3~g[k ȗu?Ĥ)x%ǽtb)caau(J =CCٽObú*r9=}q:OWWr6~ګ;_qqܼb(+- ?ƫM$%%R[w_n`  CӮj*W~J(wߧrM׮]${w\~̿'Nᕗwc a}lY3yÏkx!֯ _OEY)cc咟lR TO^n%Eb7`RQ^ƔLFFII|'HR֮`0/E&6o3՗8[]CJr.7wp+/s?Sx]vl+TV344BQaQ,V ""Ob#a]?ukOy핗8q Uk?#6щn&KLLh0sQUdr'&|<>>;o|EoJurs'<߽γOghhm[hz͛6r>a* WOd2xş}r?D"Ï>kכIOKdf#9u۷24<>VOZj i+4v߷p $߁}C(?B!:@ *GFiiiciYX٨@Kk62;:l\.篽 4=}\HWw8^Q^Fqq!K;o<=MSs˷:` #닋 (/gfƺjrCx+`KO__dLufc(J F=כ[܊j(4^mⱽ{0 dd17?>6Q@ wR\X"6rD"9JER1;7s\̌ $1BgHKMS'1!>M HHJL PTPR@ vp}Ep8QTXѽ0l\_E^nZ<qF# 5,--qfa*Z rM]eUUҒںKZ\."y j5(rrs)).BVs*KsSGcphvTbx8}_ȀP*+#Y2E( v;%d#hk`]h4j/qkn =NOF#Jd?4G(?tB!u.=B6u1vwH:}+Wٱm+{vmG("IHIAN imk@@ff:p*I=GjCAP*c@R`rr ÁBĘׯ[ooˆuUHb.^⍷au( DBRx][ZE8˼,,.RVZLBBכ[X+oJv l0>1xR$&$L[[H83caGp:沼Ljj .;R HV6n@̌g4U.N'jΜQ*U]nz=3de!ILf;ٶu3gkp8 (Fz=._aC!'yGJKz5lg,\iFѰm,䑍 H%Um2%DJnN{M' Α~S>`xx\kjwźdefqqߣRgϝQ0w8# p\$$s1~Ӹn_QA* ? p(Q~4N->+AFW[d*YjR=?y?S})|GM9y,嫺Ag\Մw79v/!,\ϾGZ!P~>/=<3T$|SƠRHMN85ut µ'N`0OH;wq># iu_=D 'XSQFVfw6(Q"}л ٳk}[6߱&WSQVJ\õ HKMR4҉ZfۖGVwA$|; ė vƑcq\lߺрdqaۍsyN祫z/J t)_ת7$'%~SxhI~\2n bqa!3q;k.Vbr1HKKԙ UUb%hkIR*KJeccY^vNa*vnC^NŅsq DNN6ךa}U4%J(QWn wlq+M*R@ g"X)}@(H$B  J9~n72XDLlLĎaaM_7k*HIJx}!ֵrŠ%D">}P(DFź˼def۶Fkć}X$|~?/J(5+r;7  ޵&n_bqq1RG~^nԍ)ۺR :NLRpWRb[TRV:]% TP@|:B!/<φH$btl77汷nNO>#7O:ٵwt  "s<> ?'LD~u_ߥ|^ n8(M9)1uGnN6nNQQiWuZ Eddih4jj5+J]H$vFrRnETjv(Q|w-iYmD6'H֯JU"!Nj%55Q)U3>6NRrW6syWp8ӇX"F!c/q\5/>,bBAGg7jc@3imk'6&'Np8IKMM<}%$?1]A z=_err9y,SS&RS8v$mlv㌜:s~RlyB!1چfg]U%.Bll,k'##=ήn 8y,=}J+B 9ɧR]s)%??ɌH,ӴwvBKki\ngy4pZIwD_əs1O0:>NZj*-.]";++Rk~l5ͭH%Μ&-5HJLDVRo҆RJI_ 9>0VWqJ#Gq9P[P(h4< VנjIJLfߒGiI1~phE MJO Dl^YҊ㡡]]=]鳘Lf.֑oǥKyw飨Dݾ?\XGRb"O"##O?;R?ݎF7rC#%E~Q]s.74bٹHʽr1$R )).]B0IYI q9N'j/Q\TȻ"F \;\mj>o~ꯐE{Go흨TVxH . Wr "uk+ٶu3%E  Μ=N,C,\d3;7}N0daq˗ضu C̦OR 11aphXL(s/H(dTO[GFoj"DJJ2Z8ںKڱa'\c  tvwc/1e29FVf&'Od6t:ٸ~ݪlvz}#Xw=mp[Z&';8cCYe-\@`xdںzvnʲsbll޾~ z=/I% 5~y?c_žпAΞ;Oz>9|Zkq8j\&C,ľxC0:6NSs r7KaA>>Lf]ciikaϐJc5rM-t#6R]sE "oRVgz .7_~e2>(ݨj+hKyY)#lܰ޾~N<͡?E(GDVGri#$9+#WsJ))XuGjj ->ry}p2'w'}S% KC52 ~s#Ȟ>gfX\\riz44b#5%@Áyz&M@ ^z9fr*#BNII&&F鱗>KX,l6BP4B$%%T*E" 11:®rN˦ imkgaaBk*%9Bu:-xU^{%,+`H|+`hCZ>TԔ IHt3qW5{'&ؾm ^zPҒBN'J}lxE[&[5<2BVfS&3nƫMrZ^H(/C,c/h##=Mףh9|$cXIKM!;;BwAՒƳOje!?G ` <^/ ds' ~ff,B#iX{>٧Iqq!_^E* LLLb4dFennFM vG<ڿ )[VvF7 nK Gk/14>iKNNRVZBͅZiJ zs 111_/#V\Φ)//+Tr' tuzySح)ۜ@~|uv}ll;,J!GGv~EKArN =}ľGXӍr9wRvo4|& qLf?# !V{o% /{B(6N: ;d2Yd5n_慟>>|$XTDʕd݃yzʣ{v6m\C 8~LLNٍ>6ǃH$F=HD%Jp[SgBXo]{N]E>YR)Nlv;av.P(JSP0;;ǔɌyz,?r۷mgA(\%CfM9[]΃tb21X,Vff, ],.ez۶o Bfgfrz,<3cұY{?7.}yFFGq./p*^͹5K޵JWwi)dsbr'?ι5,.(.*$;;'O I'/7 T*ۻ'OشqCX{T*mU)66[ H^F&%9 zǎI~^X6`phL\.' DW&ȖG615ec2ζOQ<::GCݖ5 م\&#//w ^/[/" q./T("5wAyQX'O ?A"t.T*")8xIBRHC {ÙadtylބFT qȤ҇nf޶u3N% g42<2(q\L\\B8e<B z=+ ,//Sv KpXBwO/Uk+>@m]=yttvxlnj/Փ<#ǎ3@nN6 ,/0Wd2>?qFrsqZ V"hik'1>["V:vV˳Oo,l߾O!&&8sL3~0r?Nkk;c$%%m>=|ǃku."+3df,V_'O! ),ȧ`0+/L&ƫML̰ǘ_ ;3IdeeimtcV%~~@0@fF:=ڱ d`A"#JZgٱm o1:ENv6&q|~4@s"ɨ\S@ Cvlgrj27 (U*?K%EJr2##X$&b19/p:PTD$؝zs+3 >}KWBf,Rf{),,w?@(`vn_/#w3&%%iٹ}NSfggkv*׬!3#[*11ݳֶv\.mQ*XT`|EGղufhim{pG|a#Q)(rP*l6;N۶ JILL`#hH1d|52N9:D凅H$"/'}~{zc 1!Dd2;KLL i.ֱgB?slgGqønRS rC#^ /~FWw/Orʷ0^LFgW"(b3^avnA֔!8_sScc+/z n(rΜ& Rw2&Ԕ^~yJWO{NlL,=sǹ5d2%,+~]=<}IJKx\.|~U~ګlߺL[{۶lr) 9$F)wnGRebr "&A ];c2))ɔzٰn-l\8<}19NNg>u{<(ҒbJ2O=ʛ QdlX}Q::HL PKK1ɚrم}Ɏ\.㕗_yW_z>v0?/RQ̌t2Ԅ>6RIss+;oCT Q*08<̖͛(/+111BgInff,lܰSa.=vP*XZrpEmI)pJ..yG\PS&3 Ql6;b\N Sa`46_K/DԙPL*e],--13cCT2c008V>   î& Z+/@B|8P՘Lf&&'DKOLӰ:1m$&ģQy<'&( dR)Z &ZFfyyۍ`BPhu0j12Ѩio$##tVel|B!RIk0drĔɄZ% 2<2r\ZL&3SSƠ"رm xUT*eaqߏD"Y *%l r,l=~۷gddB.XJh42!HL&TJ%b!f,_^w}6'ݪ`I\< 9*2bg#H&)1@@jJ 9$%evvݻvh =- PHbb9߫˜@Dev:@B` B|T&ٸR H$%&$33BVV IINܜl2VB4a+5大jP*U*Fmlڰ[1<2<9Ydgg108Ěr㌨jX*9Y|>T199ӹLzz:62`32:^K~^.YYw`0IOOCg4ޯ@i jzZ H$"%%]."+{aGqz3^<6n\B9R!+3‚|bccVJB#@ffiQkj5z:-j@F&7',2<شq=6;X|>]7,*(Q_&6'=^5bAEņ$bu{ùL0(]=$$c\Y24<"k+|i͒A_뾧jGZ RC#QGYi!#Miy9t:Zڱۗp墫\AQa>b84>6M+iN]ݔ7y_=m7J(_4}XRD0Tj9%Jͭ`Tl_np`2iJC#RgG?gnn+HKK%--?LJEi5*Fƈcb-x^q..Ek[;uWZf7S]s׋ݾ 4D!H{{ED @aA>9(5k|dQR\ daa+ _i (jmeD&&&TF9u,i>{MuE 8q ))TF,L{zǹA,VwyD%J( 9Wy* ͣR)INNG,x* pCbb9Ywxyپ؂ H]z87nnrرo|-$cYbob$ID'zۂc%ARV!ER~Gݙss~̟WBvv&KJPTXyla=ƥWp|„i|>S.A`{v;.1ʔXq})5sX,76qki-C e #9&CBB9C:4 OȽ=pK/D"j;1Q\T$En۶#+ͷὝH$P @vJQ  h5"H$BQdEb"}p&GUz=;ɛojc4XA233 FhLMU.g=XBt:Cl{bp8tx5z|>FF>);q3.L_Պbh2r0m;vIţREr1 f$FCQA>6‚|</C5`0%x< 311p8"{axdq }\isOo/##&D&&&08pL^WnՊpn{==}xJcゖߏ񆶟nJr8n3840>1~`( thx$XNkw8l6Gpܸn&&&0F0M\pUٴX L;wn}5O?fgxd>~0Qa.accwyu=@ juKn'(([Ko.66իa%s_27kxIUGGWvo`d{i„ s_Xf3BE%q1  cyys砟] 啔̝MOo;>_| TTUSTX@jJ ~Oh J  GP &Lf9FZj 3fs}KQL˂rbccP^/ȥ+FGb4")1.?X,F""Vc"@o0hhimE&Dm6>?XHRR+AѐJcS3b9\'-5#1=Fh"=-z ot<\#7'+WjY\dKt~ g_`RYˉH$jNBBm$چdΜPMך?k-$##2{v1hZzz{ȠIZZ*>ߍYD0z_ͥol";3&i<6Fq` y}~cdekh`ܹDӵϻqo!l4ln]ݤ`2 N,MMךIMI`0щT*k}±'y>K#q_f5?lAnF0a>Hu 366mQ*z1F.\n(09#xcvHVc[x:zQ3aD-+X'Ȉܜl<^oHL&]u{Xl yy9dgeѨy-$&$01aj8YDw+-6emȈPt$M>ϵ*C90啕~rć p0a<z˖R:Qdn]rr9`%s9{IX?He  &@DL[G1116!"HEtTCCwu{W}Xov3Q*$$d: ˸ Z-###wtb\ňb{z&0d.ׯwOtbZQ*EB߁@^^a#%%jHC Dk[;H-}dgnqDx^{BwSdڶ]=wtKVOӵfzz$--ꋗD* Q$'%NsT_T066&;EMAbbQQ$MFĄEX^ۙXtD\t~ׯwa67Gd\NKkÓtO2# 2j6!l4&LGF~{ksfYv36>X,&RETDbJ3O16>.ĨTr,KVkR)H-R`0ftRPUvN' JE D!c'&ШU|Od#jɂTa2/#|-JMDa!؉ї 1d2&,ϬA< br93T* IXA"{ebB y9A0&!|X6rjZ &0ӉDR!dR\:J\.RǃR mrۃB!دMJ$Vۤ\,^B+,,rP(eۃL.2<'jraىԆb`0itTJtT"].7v]kJ%H$оNTx<^rpN4ju/ǃL&rp:@*rE / ,YhZxGP(5# cv\}>jXb|MV;DGG'3夔WTR?˗1ajr6nX/tSO<\. E&1?>/BXJoLLUwd!JQ*wtfcnj׈-܅B )9ܬv1;5[ߏ,v%7F$6[ρ\.'..XNE$v]lZy[ۛN}McZ5 ̟[;l,85w;wc2;O?/7$DNPFfX\.7b$t::Ft9#1)DT*E*zJ.]A,*wA^NK/v8R#N Jٰn wmg%xC }Cx$t ={)(mizjT * odph8>Xm6s\ZU۱^\.7bϾT]ą !UHF#ڽw?rs^4زiCÂ\.gu ;fΜ=OEU5[ `0bt ! 284̅JZ[jAU%Μ=O}C{LFaA>`ܜ[Z$/7LF}vp Fzb1qwqԔ;J{fcllzBGF2`0HrRϜ%>>BNEEN'qqX&j啌!ill"'; ˡGPT284D,A.a8p0sfdl&&:^OEEj5>F]k!Ary|>/wQXq,YaFbcBi-Q76QXυJzHMI!>.@ $#=!F̈@Zj MC!/TT166ZbV$66K$%%}X,ͷ!1AǬŜV UU1Lt^")1 Lmni?֭%#=*IIIӨwdApׇX"!REo0044hN,V+u jJgDGo0hBE%$$b6qD|\,*HOKۤȹ LLL@yex}>bcc:rsH% Ft}>*.KJrRh6de߁)[j;:grNڙ1#>:;Hz;_NOO/Ϟgqjj ZM}cך[Xӊy> Kȑ VpwͲ%s]|sfn*zz!f׻0M?xE@W SyUֶ:ErR"zLoo5p0>>qt7z7_͕ZRozݶǶ;fמ}LLL.yyLXm6~_`j@ @[{IItuc7M$'&/}ؘh.pI++9u ?ןs|9&~D{w#%%sfATd5W8r8>?FR6N>˞ĩ376s^:8xMךftu_xyw;׻? & ǃ<6?}$s9N>˶vPO[,|dF^._Mv;i);NJJxa)=,. \(`N;:BNQa3r} RI\ ܳN~b!̚+?uoĮ{ѨUOB8y'_&LL `phؘbfMF:BX,VJJm"BZBP/tajk9t(MM²Edp:Xv5}}ٺOJ~~6o0N "#>y9dD|>ܶ\(JINJ$5U%_؀_r4 !HHJJdlF$dBI騪8ft~IhK);KB@VVcRWNL&D2Y\D[{b=+ʖgV+ kW"HZ`C~g"̹ ̙=tNX"!*dd"3#ՆngYxB[Gy̚Yt#lm##- IlL4d14V( ֭Y,8WVzŊO̝;H|>0L0a1J'Nζ4]kf%lXBADD P*&o( rdY7oõ!ht=wmw!&"BX"!>>\B!"Z&6zdDo:r5wA0DQղpA)lAm]=r&J)..7oG([Z9z bJd2i)){@{G;vE*v*Tr CFppU+&=tmhp̙='{K\l,oh-^&8HVf:ZBVĒ z޳ Ffiz~w{(t"J9y 6 JIJJ$--\~[ƲnjcúDDDOc)..vCMZN‚|RS),oETT$3o5CCØc}::S\\Httl#33XB\\,Dl6Ec[RV. sfbvrsHJLWyLV(c̙M+KvVblbINJD}CERR"m46^x`~\֭Y? A&uQsfrh4(rV,_DZlyy%sZ˗_u~My\ry%syG9_^_%K/]{t?)9v &̝8N ꋗy>~yw+_xxA{o0^ɴ@.ßM?f||{2:j95 R؄<̛;#@;?acxTa„yer֯]Py+3$$>|"o~9"@0 1#c2ef?wDYz%ٷ< N zka#N`4xx<7o1wl.^&Y<,n H4-'9Y3}ڽ 6&ؘD"1ӌ7pxh^{%{CzAϞCgbZYYv͟?lٴ7*r9mBKg:0L0a„yPVr& W zn lێ" J%m6۶-mĄ`0H vqv n` C^ Zh21t Uymܳ,\㋯Lff:#WTk/ȤJ0ay!w&G}=.6)y;vA\^q: qNJ|~*tpo@pһ531aaמ}ڽֶ6Zڨft̑c'س M 1{L^z99jmGo;vbǮ= CilݢRHJLųq:6o\0qq14651<<2NII60wՆj%`40a^~߿e74zpc@ @͕l۾Ӏww۷'KnfB|_0Λo~|]9?޳4Yjoqz]{#;l6_ͧ6{]{[z#s O߹'~ֶ{֭q xwt zt:==}njl6;2A;gmm-YgaZYpeKs{z xnFA=̙MCc  cA<t,[NFF:55W(,( #㳟0qy `DbP(yVa zK3Db8wV(`0hxBTU_jy ilFjj 11X6jx /`5x]]䓘 ~?˗-E,S^YEfz:e˗~c Ksr yTTUeK0:}=ltNg‚|rx9r`Wj5mNb1=/FVFVT&9aZ-ї_/ l mTU_$;;ygI)\d OE,(O]}Fy%s@D" DEFa<6d^ OFJ!"Bɵ.YgyBrH3|/}7~̚ŲLrr}}p7;+.(ںzΞ@jJ2E$t,.$1!5.`ӆu,_DЦRYxyD"2,^%Š)LȤR6mXǦ 릕 P*ZYFGg'OFVti^~?iM&&,b1f3U\QFӂ`-?*22ٽw?MEHeR8} {ʪIlO:N~6;AzZlZA6By%kV$--NLo~G?7bcywC3 >+sr N/h+e̝ԏ{o%xKqZj*[6m{oGuE֬Z8UlٸGj9S9q ?Btt$x ٹ{_]\NwOlL .#ɟRꈍ!%9GlB JGsK+vSo^/11I^=t:SjCS5ٰvM(O~˹v{t:O|[xERiztxʖ/EGFz:֬":**}أ[ip)., JɟG9$G^n.iiYcH0a>@RbZ->Ydefp⮎Epyl|~?@)B|Ldd$11~DX"&&&_}D"C)2 ׋? L( J&m! \.':: >l{E"ct!IV9U$-DKxjm/y} md2txd29:])W@ D,F" ߵOH/(iӈD"T JCX,&**ly"OrEߏ6RCvV&bHV!:JhKTxP؂Gʉ jgg9:L06i%&&P(f t:rB׻z`Yщ'7/,ZHSs 1K],(OTd$Z gΝ'"BIa~>JRmhRM iix'## %Q.#\s3C󟄆F!`u([$.WEVV&n%PX, g*" bh|>/Y|3h$"BInNvzFlH^nך[I(.*D*2˖RU} OJJJW_~kHKKE`ڐJ`AGrp,_,RS5EJr2q1D" Cj;HOcF^Rkͼ3X,Vxhko'7'L/bll,ZngтRV+d2"# ]y  a4YlN`qQ!/QeKIJJ`7ٳh@"0#/I9\f"#742cFv%bdErX,׋L&eA~@c4|  1>1A0p?0208ng|Bx<8&nc[m6FF '}  cZZ &L0a|%ށ@ GSg1o~?]=\&>>*vQ[F)2BQnwhnq\46]!Nfu󘪂N9mamV ~3B~S5Ь8xKZZ*?@Tt$RD)?dvO~^.yy9FG,(ZmDD( ;>rEvjFzvGNv9a„7rh"Jxl+2ۨiohD!WPXO БcHS?ֶv*/"X|e/xo19x0y-_:}͆L&#)1Iq\4]ke|?#RYn**H$~ccDFjqx>[jQTH$bb1} t"J%~`Rh4AD,& .*XL0ԙs U*|>ߤDLIYH*jo_ϏE P1vRH$|>?2͆RD,U*<SO<'xP*H7:׻pݨ*>_h@ X,A&"ZNӼv5ÁL&C*{E8|i`fF:? ""L"$b1 ӉF&5%N8DB!t~ 3HNNBD17o\ٺeqhj23ҙYTr y^x](~/p:]MKH$b2{bMaA>#66y%sD76G>1UVU !;\ZZg- eUصGK%q)r9>ogdf[yo.Tk244.>5kVc<4˻ l\۹l/xAP YewΚ+a<;>̞9ŋmuVgӆul۾+zΝ/g捜=ϳO?I\l,#z=bE0deT]fC*p)&;7l}d3b^::ʒ 9y WTJZZ*zt{j5/;v'77U+x װm.|>+/%.(`-6P[@ll,ivǹ\s*BzqYv5?LZj*11QX,Vj53 e||=zXj%/c[9q4 :q\̞53g̚YLK[;]tGH$%$284WD"Ao0ι 崵w[ini.BZ E Kq\l߹H IVz?|.?@-~"FF %FjjD"I"DD"!*2\>FF&T% QQ(R ?.^P>"(  Ňy A,_X::;Yp"R. BȈaR⫓"@qq""8wD\cxxܜ,^{%7i mJ""̝3׿ 7-n+*믲eF^_蜺\./_A"p]bdDORb9zsDGGoA*5"DeJfN# шFprY˯ʹw1IR _zܜlZZZy|#g'-z{%V- łV#V7=k۶dӆu0sh -5~R)籰W2JfF=ņt)T7]kf,[htRU}Ξ/G,rhkKʃlذ6yz\N7.T p8He2FGrHO#??lfhxu̞-tvu%CO?8f/*bgV**&/ vVc5|9~[{46]g~OQɹMT;L0= F#׻w}-(eF^.%sc-\($##D~?K,s( """P  Mv=FVp89wC2,fe`'v7Dj\ZK[{Ch45 K29RLXDкŕ:A:7ٳy$&& JCL*cELLX0Rl)[݂FaphƐ@A~OZ3*ׯwZ"=-5W<`NZZېd̙=+jT]=dgep:Ieri @tt4e˖eFFMTV_$*2OU%b-D,3$Z ##} b05#JIJJ$p:ihj"=-[ZYp F# :6!I چ&&=gӰ;7a&OXRhh )IRRG.a۩'!!Ma٧0OPHnN^LFF:)-[ʳ<嚦;a Jiil6Ԫӄ}id2N^n6R'Yd117P\THjQYUM||^Ʀk!ۆ& F#jg~}H]}M̙3ʪj;C0ȱG{g'mǙsq:LggeacESӅR.]Frr5WR]} *'&g>~@FPB0a~8N p8,Y$RS0MHt񴴶eFH'+3H-̙B 55 IKME&!q 1T ΟGzZQDDDE.]=,^XJ-yy 'YYdg14Ջxp:ğOyY>7ӷ|zz))--aSZZf dxع#ǎ?FϛkD|?7<K>[[ ft߼OnL3qKXZZn㋯Dyys爤923O|Xiq1Ib2(#V,_ng \~Á,\zwŋ^;wimmN;ȑБ̞̞ʉ|'Ni'Nn> c&\.'v,e9jjn?/w={%ד&ߛ":s⋗.s& xs.p-yHH!No_?|HD"{ټqͼsH$m6N9G"`y70kVmpEع{'d3-y8y`0@ ̮=H$ x2 }먚m10xTx QFM"~w jWD@OaapFW DQnlSx^FG(.*ʤKD"ȑÝ9u,ccc7^~ TUc b2W/k_,aWsL"l.]w,'J"hD3n+ul"\%2$144PIdNM&ƶ{R;{{>KO&&&uiu]{kkk]{>C.yef<^NxfϚO*#,_׋Du^/6 k%]ls3\.Μ=lg~3s9^N(W^~IWw|B$]XVz{gͪ[噧gl|Պl&koPxbf$$L CC $]qr6)'"^L&>.\L<Y P]oQS]nV4MYUtvv266$bQ'X`>A[ᕗ^`"+A)*. $I"L'hotDW}HlJg1qettٳ*IR74RSNJ$\~Lii =TDYi)tu0olTUc`pH$j8寿EmM5//EE̪dqM5eXV"()5żsXu]eϛeϾtpϟ_׿! p w7n{| eBw|N:KqQw|}\!:Μ;YPS)b8qYpݴx8x(]:T/mA,;m[_⼻}|*M޷3{VP $I~bp{'J}LJ\vYVFcܨGl|^/_ (BWW7oϞ'LD2ɵ78y;wcPVZJsK+>fUAxY|8uYIaZZ8y {~&&"|;ett0<<‡vx|>3hlj&Lfw^sGa2~sXz% 8u,dalib8Ջ'Lޠ w៸yjW{~Ι8{D457g׭Y[ F3^>|q|>Xl޸Mꦶ>bRx} OęUU p8ˣ.4]K166l}rK,I6;s5yj+--[*ZiaVe%_q%dYѱ1zz{Z,}݆$IhN",G1ZZZ~{Y8y Ux4_NJKwohimcӆlټ7)++aϾ!|t(YRs׾B05kVq6_w*?Q}X,N,רU+fCc/ܳOb2:;?o.?Ϩ]\@##vV+d%?WxfUU')/}'GجVΝ@4'?ߗ .]f` l_z-72o\ڵ3"M-,[Z'su^yyϛ˩gCw1LaaƲ}?mc.֯[ʕ˱mXĒO{g'H + SW[ÁR\$xuŊ|s7oԓH&0M45`3ˤ,g\9FFF1U{̞/^}7z&Jy7ag(++SꍷPS)VXfBvRֱdXTزcqVX墨@0[9{$7oщ`i]-Ĝٳ7g;w卷޹vkdE߸1E|Y"﷉ℂAߨDK&NG?[ihjb9]]̚UPkVl{׬]5tuL-,Z0 "߲gpPp!108H=ʗ_|-DUe?;!(HLDoho,_LfzuC,_ q ߼Y*)8v-0ee?]‘fLp{9kUe||M8"okΟKٺu cgΩ^C[ IkV?!/]fM_Őblp$m``NSHI2{4M#!!!I/H$(X,fL&X ٌ(Th4"+l\u8d EX% $&Eb:&_e2)DQ]muH$YFDXff3DBzF1EjJE1)Rhd2,Kg46DR\7t_poy$S)FFFzm>z` qYټqM-446tI%%|., [xg*2tNSYQκ58x](³OocOPSciǐϪ$Nko:\_Ǐu.]R).^LGG'=wuө:At8(,(!$ǎ`:ݸN<8.\#ϝ͆F.}?Buv7 B8p2ewtbR̞Ź8z8ٌ$6+Omf:VJH&^ϱ'Z^RPP5y=?yE 2g,Y~ B`2 ,D"q[G;apl{]*PCCTUVbr::;fp'Yv57y~lބ4zJZ[룪gJOOhIa|s7jJ%e6o\igXr$qeV\࠘1mhlbphlټ02<~ߡ-Y{vq ^Yysl} .xOlAUUFhimfvjH&tvuQ^^ukVl!o =kY|YCc0t f >L 'Fh```````9o,?0d*E[{}ᮜ@U0M$)'yp]-f>i Y&??7hܻ ̧ L,cϾ<ԓ3IFFFپR)EW40000000 ܨ]͎w1C$w cOp5&&&v:VF2]{W̍~^} կW__ }}^|a+&EAt~b1*+Ŧ.a6ؼiutPYQ6>2000000a,5QTTS\TH*|.2>H I2W^Eӄtqq1&Efϕר]L2Bud暳*Y0.mO*%EL NAUU^~ys/`ِ%‚?m۰~jkp 3x OOIq1E$99shmkGulO~Kjkj2~._k066Β?px"X:{z0dYFu?nwNTJ|vP_Xp(ߟϡGXd vjҋBTMҋr:F[A^w'U200</[¡CGZE¡#G\~|Zj4S} 455|_?/w'8je^GR:;QIp_60̐Ҍ/me`|b Y]s>b!J&xD]s/IXkcR`%AޓU|+}$nl̲(Gf>gMaʭsZy^ ,+"ʛ"C&aj"c&æ@,MqȷNGN5NnTɈI\_ʹLx(ɶ[D볈zYu>DgL?79}Ove+G[[tFƐedRk虠.qeXqL"q1vc)UnNO“O)ϕHHY3GuyŹfYYNV1;3xxmxT.ɋ|2FY JXFemPfGL$v0 [ hn`u@Jy2AήNZ&Yb0crJQ3?5y~++](wɜ ,2x-`/+I["%Hj*)r?:1\̆Lۄbė*̲&%~M YZ=U+SJynyn9gOrL]tPPPa02%by"5AS-K&&McUY}Ha[f4yޮr__RAcur$IJ?L29jɸdV'#M>(JVyrim,/:*({z(Ô!f>FvNv3M^J$ʤ49 ?S&e1gdM=۵c:! ]444|fnnFV500چ?ʤxHtr*>tn}@OopnvZaGs=S9:s&>F~iiTL:pyΝ~)M|:v\};&˜o?a```(c,|0@FxʹvdOUlr~3"2c!MHwOu|[:L~cDO"JN\Ćqh&(3gΓ:y[bU@x-Be=bNt(T(Og}-^/4%,iĥES) ٦eWD&.@hʀiY}Pʭh;* rZ%6Ȕ8έ掇C2^9t/ט$&(GYȹr˛'3#k"R3 O)^p~P©f aNOl:(x Dz\. jl +ȈwUPΔJ\pŽ!,3҉depM0l*PVhqE>$4+ (ԦjSy`N=Q*tf"NEk```sKιyzVΝ;O{{ccct2xqv|'Oim{4>btl_oG?z{!ǐF넞*%2!drB%gibfNLDŽf n.QxP!/ׄ\넞~J vvs% չ>aW`b&Q6["ȶ'?35Q%'0 ~GB -bxCx*`S{ \BBimcXgݟ: SX9ԫUڳTBp1bfb;C5Abc븸Z&)wJ,WtIwKTR`̶$xDYpI>BY%t$w97u~]Ƴ%J g*6P]XTۥqYTxHvEf*SE Mc߫<3aUb}XY7000xT DUU>BWW7EEɭBo{8kY?kJetIl*PM|\aX;x^ 2N)S>,5AEjZFdOIևef YC*iɜjDULwTD"KZ&ö.#)8ڧbSw&ur?5ubuA!S?qmDqAT|ة* S`hӹ8,ʫpJ,S-1x :U")MR |BR a$^(Up$&xDb/*<_"L|XEP:(g/* &oQY/4оPR]EE /(FUP~LcWڐ"AW2%ǡ%+e _0Qx,mYXHF`mPRߠOw/ۅ[Ә]x{*=Q! "Z$Lrp8'l=#:tPHh"ygy`܁8\2.GPs6﷫+Ύvq_ÚkI:y-" ɇSmykr٩3U.S*+ 5&wxa*`wYhWTv*rI$TLTd6(LMD,,+PIeٳCeO]Mevu,ʚnyU/&c:t[E ub;`4)|u$5]¢HloW)Km)]@,\e&vht.׉*Bbmӕ+ZkE`Rp\1q_c_ uSb0sW%Y8gDT0owTޯ1~Wf[GĽ>E[b*)hiАmQ)|M8ƫ!nhߡbN ÊO!,?@̰/0t >(g/" t:n v(N3Y$n#24M4O4,+tt$/.B-uSe\*uJm27DŽcqeN˄NO!AK[-:}fYb]H)sOy\%HXØp^dΨNKbNaQü)sW%@(!~i (4kH{.ʴEtncv/O}xUnC=SnIxf* Mآ-W3,+N#Qhei[Dڷ緊b_6-Q`%K޶є-ZD?7@Ep8?seD YשtI:$*\2u:ܧfʔEGDg *pfPc(!DK!pyX._=2!Q$X%V sT]ܟ P<q/^ѹ0<Itp 'eBcmzy0#IXe+ I. >Y9ڧg B\YP891u?:h gelǿ'K;ιV˃)ft(>w2<2#=y38pIRL&%7Supݙ:X,TUEUUEdllyN s{nj)G Qc@$X,%%躎b!Lb2lhN,Üt(Fl1ER)6]׉X-dY&H*6MjEUU8V3H~0?&DS~edd *% ݍ$H`Q"EEEiHnpivL&Ef3~->FFFcddz} `ZHRx=E3$LǗ ^?H4B8C89NB N*bX, FH&Ŧ~>pd2(DD2IJMhak```````p|^ikk׃dq.(DBb31AeL&D8>L&SֆD I׋I1aXͤɜeGER^]-K&rEQ(*,DUU PbdYƝ$I < uEQ$R4MCQ aR,#rcY)..BLf$|ڨpI $T4q}wf?ϢO*p}TM:;e~p3hRȌ\#c+]B # S <[j "$P&m)ҿCVjm*QE {-WH2H >TYNT$Q8){U&TjK+&R T  Si_4qO% E&$OkC2Y`ЙK׮DeB=:&(cv{ fSX&*V jUMOB"ILH -7ZD-xI8\с]*>L0=?ٟ 2?e $2?מt X%NkMBb6uFAoGYX,TUVf L 6=!#;@$%Iʹlvמ~$JbI̶V57-<ݶ~c"6KdIª9EAS&B엍)~o)-ڬQ'$@Rx@\&&GʜBv+06>ޯ2 "\2Nh9ȋ2o'S2-WGtx ĕaqaiػ<3s_<],ý_(}s_ Bphn4S~/hYy2mSh5 0.' Qx'_kN$_&h y7Zmۓ G4hQ `G{D_,S":,ʟ˫#*^]b_f$s#ރ Q +&XF]g |([h!Ū.>hiNjSO #400{ 9Pg=i!8sRkT6=+ fÆ[BoBe~vWf -R'KllRsۯ\D).d} vqB0RZ }!ۢƢqcG ^Pyy 3JQdlFfѕd&,Lw*]VceuK= FRNImu"z`*]zn+5·#Z<I5Ur\X9N<1g7@LePJ98ilHj3` MڥPv}|o'T{LeR.Z#;MRhZ=܉ūڳ9K8\p~jMeI!c UpZXqI2CПSvDf>g2R+ D<VN 3 Dє@4̓B'h4F@Fh4 ~(:Fh4^|I}Πo!] n҃+r-6{J|hWGqwn7vzZƭƺ=Rɛ7V_HG@vU7ި e%вkj GT+e1`ZQ2EwG=TKk)c$4՘2U|Fe.~k TSȤ >h4Ec~k}mNf[qIS,?2i*LtAd4jRW&Λ e meD/ʈyZcJ UnFLdZZ c)7, ϟǔu>kxDIxyD|٬D&)vҰ\' Pηɸ `{b^h1DI0x-Rg, Kbav!h4EN >?/>--$֮!J144m[I3>sH8'^GJ;kh&྘AJAL/8iJ&da$$-Þ@!c) 6TWi \:ބmEf'. Ʌj1~As@k݉;pٴI]lGU%yg&m1`0)A*ue֜ *DnJ9%NJr>i.Dg'Rvq 5[B{@pkҎb"CI۰# I8;1άjQF\x @;`FR$JM5N9 VW \eҧ&lL]AX+9;QsbNe XaLrf:r *-T^m4HLqo yD&26A94t]<5ƛoӟukpaLOM311A0$qx92 D)&'H$_Ν^~|| 8u TI"02o3>ߏYKƬÍYB`"0Q1xޡ3]g\R wjֻ4ms~g(s:,ĘV(G3)Tpt3n(tbSdIHGZ!~Tvchҕ-* TVwhLؼoYaܜs*6U&ܤA+._^+`ɨ xUKell3X^Q/Xʹc4Rcm IDATYVBv!7,+9:COz1K6Iebk:kO_,qqabUD2qX_m \g[ R= k_c D 8aIqMF<(,饾>ktuuq{Z (v*ϷN5ވU~=;&B*o6ůĩJCVZGG8m 5AC##S)@e ^l5xŠ/a^{E"l{Ἥn΋Ngnl/h V3z'U#< cUft꽂W qPٲrcH[ʒJ'ʭƪjAţ܂Õg0˔S&ǭrٕek*c9yW 6XZ exZenԸH!P06/f^HZ U.s(96 Qж|VrrrS!mA[0vXt*3syqqgZLVɊ2BAξ/zJڌ+L!-K+')ugFyxx(>2@ZgRMi)ef;x Pї'Z~a-ջ Ƨt5xŢ ~*oC#=GG$Q`M\dW$g;W׾0,c~k.\6LdN!aOC.Ȗ$ٸ$k mdO،gT(z8t%0eWWYx q_HTK|ǪxTdAO Xthh4 -%By؎s= 39QI;|>ep~r~v92jfVk$m,VKRyjqifPVUl".K?<`ҡ= xqޮ T 3t*2>oO2Z/>T81|lJ>tYt dȳ->Ak@Fqztzժ16uU67h ij"d2a6h7o"`fvϋ4Mgff2Iuu5t)x<$>t:,`q&crrT*E20:6F(>ZQ8##$I$) CCTW3<2L:"L22:Buucy&&&21>ib4F|I^vc bn׋`0i'~?~\.)+linp8in7c#v̋x|>^/ib6s8SSS]njjj,ёah4F<, 4  zdsY, K-ښZLdpp;==ΖʘGooT00 ~E]m ifd2x*ՋFh4$Unhin.XwzzXBY_EZjkKjHM%|0 cbuo26vrب؂}!m sxT|>esЯ[$~A[w$jWAȄ3viGh; &FʈZ)#|0h/.-&o( pRpVÉq5Ud*ɘ]Z'i (`V1_WgPd_^#T׵Y\~^j396ɷI}nj7l+>:ja%pxdw7H;}%8$$ybg#M8)6*-PA>h4/ Kۿ/ӌoہz-Ll5h ~fTлUnaQr|5|AUĸbXRwe!QxNiΕ;LdT0->^qHG[%벾û5w|cGHva ] IB1iqPAй;$ŠEw._RO[Piz"hF<|4͊0B_N >mϷ&TFCCaRg 5_a x6WD$7󙜌DB_a_:LecgrP"{l _Q{-^j3n>`pj"`xr.D0/f BWH0Qb+"ӕ\^AMm!5:Hfm5Fۢޮ/a|jY zq{j$!vcQLȫqi ,RQhd}@2 D+wzD3SbܙLdS?v2phb21uh VDR}S6"ʣŽRK $wM(i1vN ASId֡+{Øϑ[XVC*q,"vB)mQ]=hoq,"cR\&BߟF>'? \teN6dY )1MT*mdYLr}-$$\'Xpqڬ[*ʹ.|-jeQ( ; H(yb=FX_Y2 Dֆ/-&x|=.-^-:A 徭ꅦ5L6paJ9fsjG+m&ya ptT4$5+WteDLomCހUub :"xhKmY Q !yOSyk-j[jWP'&:?4$UnͩƤ߀?29>fKRW]\O^▂O,$jwo"FEpf PoEUccw {b_.194_UU|8dQ再Jզ7Qt !x7Ozk4WĢpttFVZEuudw}˲Yr\7z vhin_C"TE8w<׭W}<'--MLNNx^Z[x2S^˲x#˗D|\t.c6`XCO;l64 l[M$k%?Jƪ`MAfKJG~Z l.AOxquj<'&)’ZVŋmjRgF 3] ZlyXm˦4!?bpl托)aȥM\Vo(ܜGٰ5Swm<1nv)BJ;ieyXQrʌʈ-(0p;nB1Q4<~!ɨ:% +m %|qq+3골GT5ˮ=Yp.Ol_`J^1/,(ɳF 'bIW{,|(RW+Qd2peb}[J'ɬ:_6dm%*MтFkV)ydA@6rI8IXj SMnj4EKYOsS.^7bll>NOTWaY=}\zO> =%<أDu|~2'=_\>/Ie ,[׾`rjX,F._bxx7ogN^~Ϝ%395E<>Eȓ$LH$ʅcCkGpkNYeemq zo[$j$zWBH!Ӕ-=RLfU@ea9 v> a/7Pi{P ^_-q h{_uI9kszf[TI9RX'[VEMX_]/1xNy|bNJ@p59T{-)JC& ,$YxFyY4 22:F8"oYTWUttwlR</kVU+WΉ'ا.Afѣ|v!rdvطw0$aL&91"9}`455ȺrYUWUQS]ۭLS{bJ:zր`6T9f`{T7fm3pmf4`;TLRS˲:M_MƂE@o6O5lļC[@N.r7M8l3^&d^շ0)_p4M܆`u&Dܕj)a*Tq>n)/r/XVc""y|M,""1&n .xrMZv%Iީ &;l[@O;xW@-97NT J2|XVBނTg&L;<Sm,ewK]XL eh4W"!|>lz~?333LNN5Ng"366N4ZG0dxdPYAjkK!1455 1 l6K6% 055M$fn.1n7#؎#384X~CUrBh a JZ4xCXZes* gDj&\Z끠P9^6T_ƂC-~aXp'q]LΠ@2!_݉? 4xA-.w"a',[-aF\v B=jS_#+H;/`dT6,wJ(_P e&25E)|kNG,%(_.쫔qK/My?_S{%A:|Q:_?.UU~r~ZQ}SvAh4̓$$a4䵺j~r 6H Fs!8)&¢4tk$AK`c],MP ^ʠsW('bF+•D{@""c /b;JcKQ꟠ {$!G J˃2՟vn, G*;mEDR˔#Qv!W“f6c6HVJ$`V^;4%!uo6HxeZ֣Gk%%A F)H*`!`o̠P9"]!OL@[Ϸ n`M<$- KIDQɶΣD;ǭo492 IDAT5^g[T_qx(+U}^h4 'OLɨ~т՝:lQ &3Y}npx RO_| k; SL` < ®,=DoCTTb>e6ŗ5i Z l`a& fm)nC$4>݂*yƘOŬaeMjx M"g?0ese!8I1tJ-@I$bj{RprªfPx@0l\RdW^rdDuIĭD‹x 8jphm(quՒe {br/H:kU, 6>I%~^qKms+$YS%O/vY]%:@48?5Рh421, 6#eLQp39?ir+mʱG&A%V[L ˭+m&n)tBlr|f%gl^Gp޾ӂVƂD.AQt' vK3Wgl4nrf"g՚*+& v jҲ G%ce]AAȥƦ'1ʈ`yDD̠'᰺JAGٺkTˬ e*VϵR޵alxZ'YdD" /\ Oo4Yf5XǏF:aLMOsۿЏFh4WȑOoNmM5Sܼu}%8= o6Q s'ȤӴ"w}q1]aMZHOyЉ܏F|GXym݂?qy"U/pys8.$OPe i4FyXaEIgj ݸ̳Omׄma qqRrUN>C(bGinjXfܾeKK}~˖.`6W^cHYdYg]twu鱍t~X0׮`Ϯ_34FA }$ 6ozNbu} ܼy ˲8u wzضyL7o27311gNf*֬^0MΎvo/>49"0ɥ+Wz۶nuFh4_滰s6>>xGō4JClټ>z=Z[ÏIg2ܼux"U+t2Ha/\P/Jb r9^ϙ@+-像>jů>:ghmmXvtwvCx<jkkq:;r9\w244B"c^>>x\Fh48: ^<Sߣ=T:͵/昞a%KаTeWضC]]-eN/i/șd2?q1FGhokT:m[,h#'ض}Ͽ{nmyh4[Wyo<.Ow-wM*qFh46@qkLNNJucc|vo<Б#$6.re߸A&]t-$Sߺ-yO'N3>> Jh4f_?t o O|FwW's\| Bpmzz{ B\.._mS]Uͥ˗q\z¡/]NO+/0<2'1 [oʥW7GqGFezzKzEmwa7>Ce@odW188osK'>cɒnr+iFaYG>9Ƒ`&(Б|Xz^L6oͩgmX ^{-ho+-;glݲL&o͉NV^/ohk# ry~ѱq:;8qҽ#>rG֯`hh۷uwlǶm8CGc`xd%];q뙝^` oY;:s۲Bs44,|/G?0Ȓ.r%ZWxiinfbbw{׾`Lqy¡33\rf|^/o.'Oa.pY> ׭wg' 7ns'xnaY胅q+>khpi2Ow!JQ]UܾF2=3[+-z^fUjjjp\ܺ}*~Øs猌284 ;wD":|T*^{*>m TU+V0;;Go_?nQn޺E8ZOull9Μ=)%>'ǎ 36>HUdQ;K+/ sqRt阽2?>(Xl)MM:'&8y4Z[\B {w*eoݺ/wJDa<,ˉNrÿvj~G044m\|Ujk燯LG{7o旿[ >>=~j?Ϝٻ{MM۹c;|+WsgggK.$zxܸqt%K74b,]ͲKB&F(aŊeֻD"aSn n޸a=pyff8}<b2r<33~qU#_|>=Y]U+WD^/C#'?tܲ%KƯ>:X,duL~6a~@KSzݵM^XEP0'P;Ϟ];I$T Y)%`n|^//>`p.-.]†GU! 23;k_b2&&Xnm iٶu _^峓F  )9'Nэ`*g-h Bp7ocf>F<, ƛpQjj 9wI 4B`\lټ0 $c#!ptv? g](e<8/xht:M.G9U\.=V8DifƍQb egϝ;M-}/~?}}' իxعcv=ejkvWl(Jg2@ @Cc<~,nzd  q\F2QQQPʌ% y6+tzRSSffv$x lj]槿Ϟ*?XU]ǎY8 tmgxyܳ* h{䗿-ew8"eJYP1g`pW^z`0W*TUڎD&^~q6ΞN<!!!!" A?tJ`j+2[,$G~7r8q(`v<Gbnn,&'H^SSS=s))-CC._磨}ʆMLNN8\.$ܹg~/x7K8?x[SQU.oO&ߋ~?"3::ƿ?|vN'9r /~ٷw(J|>_ijz:?=YSV坷? |1 mEr1{,Yq\Ǹ\.xYE;zX,KP*{/ns6rgŌ>LZ ?rAtv~+bb } ilnabbӧNrr;o=VQBBBiA~E<n[^q`h_ǟb۹xUյ|z f3}rH8ssjs3/>ĹpbcÕ9 1z=;v  믑Ĥxx'[`2!M-b$]E.-)f˖u5~ӧNc0Dy=,ʦZZS;-)) GPQڄsgO߃ JJmpd3'+>*/xA^n?zmN<\sgyT*捔wd"VVlTVעVt{os6;ۊHOKZU|7ٽcnn @iI1 ͛7ΛIݜyˏl )(™'B!>E:2:F}c'O&Emظy(L><2my%|>7YX\@[{'e jx tuDizfZN;Zf~a *zbnn>AV3;7hkwl|LJ@232J  3!~w\hd"66u jd2yy\~p e[${tE7zdfp8WOFzx|eņ 3#cCpU\&++dI`ttVhK)x \J7bgttֶqk`xd>=8].|u p\$%%w qQ ͎Z"-5|>)))#ea!XfVI =5+- IDAT5MHHJLr%ɲ231\n711zFJ8VWZQ(defb;Xu,<bET|~?++arX?Áf'!!5?XF$!h '?X l  17?`Ӧ|~ 7mtPdef%Y,Y]nr_  VcffV,xN'Ɍ0 2jb4IJN&*HHHQY^^ ONF`k_Y=W A5JV\&gjl1CFF:ilp p,-Y a ͆L&C N- @ۍB )1L& 0-Ņ / G0h2 rkTJbcIx<\n7& `Zñk ;%eczHJJ2F^O\|KV_r$$$$$$$$*2Z-ac-33^V! #;; VN# C!fh4ꈏGѐV%.6LFjj qjrz^KNN&99J%$''c2t~j5@ @Zjꃷl=c:x<%ϕsOL&CբQ*: Z^GDz13@P VdQ+fjZ4MBlVb0N'^䍯 daoD ˅.r5{eyyY>'/8$cD$`~?]X$N(k>x,W0Jj]C^L:b`p1AB&&h0фUOWw/^W095(3$'%Yv6QTLLN122JFz' Bhdja;Y4INNMzzv=(f/ND=M͘LFZ-f>Tj,|~zY\4D ?N! =6&ʢϱ(lׇRnGP(zn:zp: `yyELx  13;GB|<`ήnl6;q{595zVpv /?VE "]]LOOj|FCT,/DKM Xl6nH.bwHIdVWTTVo`ph8DX}}|>? WVaCᾨ7T*PUlTTpL8ftl A~SC%flv;7oAш%-v*ܸyKl[]{AOov]LL 4bJx!&{3..:&&&u|qnqlu=ںz\ՏN [ժq+ Nt11z.]&,-zT؄l[87 25=\-Kx}>W@å+ChS 288LUu-KV+Kb{$SpUx>(111\c? RYU7oU:]|juZRf3>9VnyEӊd~E fܑZFskZM[{^C#P՗8~ $s 7HO㣏?t6DjcqB |Hifjj10RQTjU'ͭbuNNNb~~A<@llXdv~ۊ8I7:6C)v]eMyݻ(زB-NOKNsEk"!1eYZoMQU*YYwJESskrFz{!0LLNqQ#fRɉcGIJLDQ޲r\{ز)B%lCQ.ZM1>9%I}hZbbԔ' ?ǯIZr[mƚLlڲyKK(Z|Nӌd~EHJJDёKKK嵗_33=պ̎HKM YSZSZ۷RZٹ@Ml}` ==Rgwߊ @{G'%;yz=…QgNfU*<N+j밺Ï?噓'[ ˒ֶvF.W̩ݷ/t+ BT\uRZT-Ν=C(ͮ zv1/]xK}CA]AZj 9l޼ $`|bβc6, cx xǮye5}t: :flL&t'9vP ?sfXTV026{&##+׮r9o/=|?a6m'3--ɸ~&]ݽ;z @ee5};{22c(.$;Ha1}p5- bU5ojFQs)ߋȡb?C-djeOn{zo]:VW%FVX"]=ĚLؾ }'gY.'/7 ؿo/ DWVp{<|%ߵ:?037?{d䬊jjj aY'^;h:wn`zz엘L&?iTԲbgn=x*]]=defoo{vt:UZ0$''Q@:N: +/̖͛אb%$$d|Hw]׿=wKH|_%$$dw3Ϝ !!!!!!#OmKHHHHHHH|U8<27C`0>^e:NWwyyyoq4nhlB.s1,%:;(">.wo[{f;Fk[;鬬p]^iil_Wr57066NvVCك_ܺ#VevvN"!C-d2~UյTւj5`{4od2k k9߸ bvw{G'w**))I(miͼ'hm;?$ wVxIKKWQUCoo=LɆt{v2-h1U~ILL$6DwO/w+Au8Vu.dx8VWIOOC&|%'q][^E  55P(;jQlp[/r&&&ټ)kUtuϿ`jjLqWV%>>0:6NsK+7AUM-Jp呡a> ZZۈܾSA,` '?X `3c\v\ζBN?Mò rɩiGHII620|t?~JwrN9KKV>Ub G\[")۽+*3֛ƢV $NZl{}{ L`0 Im-[D]#k7~*C@X%9uU~,Ԃ.Z:Rnf JKla48x` aQ{sT։m~짴w+x'S, Ξ>*>?dm2Vᕗ.D(bl|1>ϞAV7)ny%A߿.K/S6mʧr 9WVp`^rss?u-\N=OLF[_?yGVb6AE_ÿb֭[P-됗Ê͆FdzzēdY^Ya`p/hNT9imkrIIN %+ r!?/bMF;)))ŲD\\,P5̌ @9ٌtFF$:zؾ-4^[lwJzwtRTXF_Lo߸fDnNB! AKHH|/xt{${䶒`0PVCGq  QTX(s=$%%oDՍ-*JjJ 2 )11/06>Arrҟ41kyYqA&@:-ewqf#x<^~x>ҕkn%7',KV+:}^b9|.}[QTC u x9 3s)bqUDĚ p~ >ϿERb"E[7^c5-x=Ǯ,M--b%`({ը)OLR^YųgO?BC11z=[ٱ}hZvˋGVB 77ۊ#>.۷Ƹ}Y 9ylke]nGeU5n-7E 1J|}LF#qqscQ*do0TT*^KBd f3mAQ1C,߻ '8IћhIm+KT%>%2Itvuc.3<:JaJ S>>Ğ](o=LLNoOf;*ur[ͳ)?V˽Fwn'+3ɩiZZ۰ZWeAUM-)l+*D.|3?f\A[{'V2 \'S^`ph]%=r\2{0;cv~x2(pkّm`BumU>JC S&n7}{ټ)}[wb2#~onaWi bgggx:,%Fٲ)_"!!+|>zd}Y% /R% '+(j#KHHHHHHHH_ZO't Q\.&LLNhHMI晓'he2CU5l/*"99 KumO& 7ovjy핗ވV7~%$$$$$$z$bPq`^WYxj?0㡻sgoh$.>ѱqΞyj|5GFpU^>x_{@_pϱ,-c[~N0sgx"VJl}W9r./HJL$#=]4J%ilvW hhl"rHGP?QvR˅ 86瑟ǟ^KWxTԂu x?{ݻJB풌? C-&&V P0J& bw8dƇ9蓋Ed>u{,Y1[ȅγȎ(۽ FV0 }&2 ~Cx+׮Hl+>C& {,KK\V2۷od2G|\9Y\rVUT*_| Vw>EBBBBBBB![^=LPT@XhjU$|>/2 pd2t:- v6>ߏR@z z\n7jBBR|+2XZ Cd?X%$$9:VRGnM b]ZbS^4B 3>9WH*J4œ"J}\.`NXKnШh6`Ƥ ˬ: ( bbbԸcaa#~#|z\\l р\.G^/>NrR*XVǃd4"`0HL^\@ `0P(x<"lv;ZF#WQVž.1w  t0χ!6$^vjw4 p8 x?:J?~?zׇE['χ]ǃF.O+7 K d?d1R˅N8VW¢L DzYX4VG _ 嫟16>#== N:{teKj8.~arjZnm렪QF* _'bǟM\\ ,_L '7oKݣؾM4Z;tg~Μ>g3<<"++oLFfFq]LOPY]Cwo/fn)ҕk319V^d29jHٮRVW? 11z232B[{ R{)H% IDATQvҒ[w? EbBqqa9O/]ebr޾>6o wM:~+좤xgx.ghhlnEz {ziLQVԏx7WWWY^^:F`!G?+6;zN.8].N'~͛INJnJm]=~ʊKW>O/]`vnKb}T0>1 >t{8|%jtvtl?OfF:SmE011Iݼ[m8}"g'/IKbb">ήnvG˶m,  H9q]DGzdgg^,Yob^|3Ϝr?x}{˰.[žΞ>K/߸ɾ=e|>X^YazfVl6 ql-؂hW^bph8jL44#?4>`0/"019%=<C#P(ܾ[A~^./Q6xZ ho^/ 8NtZ [6oⓋ%1)V˕k ϝoo6 23Xu,2NuϘ\&# hd4&zܜl;࣏F./FuM^!5w+gff߽]=6- E]ؿvC)S^Abb>[Fݧ墮Ʀfv;J%+ z0?/PTLLNp+~)$%'?`l|ADT295)_1&Q*$&&FNܫo⥫~̖%lQX !͆]KtyF 11QN. BI\\,N@ @lI*~q+?1!ۍFTFŁYID笍nj4bF_Verpx<bM& 4S|\\$QEL^Lrz  jh4j1!B,Ka&JN:g-ycHJLD&"[p\шBnw1mKVNfT(0✯:x^#jn\.7kr 'k<6_xy0 X2/)#!!"O2 /n14V< b#FF#7'AfllqWVV'-5Z(dgGBNll,f3-m퀌XzBIGgWؘd|~75:=3V6Y:V|Nn7յš阛Z-V|w03;KZj*@VqbEާI0suT*{zP*z8.[pD~A::XD-@?@Ww㓓$'% ijnayyDz#ctwb0u\'ތO :qfffiljfllN륥!"62" AKHH<|%!h 5u*)'?9vcGs#br9Y={ؽ3Ϝ>mms*)f  6~?/^xxv…ikk 2:_{E"EE~&KV+1ny%vtxܭAhjn"=TVhL(w! Νnv;7oebrJ4fWqKVT~k_06>.QT@[{Cr%O+++) #=%8qVV&۷26>!jl677om+匌37?/ONN;\)cvv.l6-B:bD/%9ƹeS>u6+3"YY4߫k[QO|\8NyԔW֍rn#\srjjA&F>?D' D߸ɒՊN# QU]+57@VV&׮  h[!,~4}-$6!V n߭@1=3K͓? RU]KEUudɵnmرe TaZC /5*%yy8~G35=C,,,Rli^1#& FbajE3?@iѨ54 @$vK/`02BC:Hffͭm? f:>t͛ټ)*Rc~~Ͽ=eܾS⢙^x=74"q\q­khtQY]͛p{<\q[mNWX.; js)-)&11˝7oE exd[w*x</[ ^!FÊΝJ6xn xzz6q*-!GNvU FC[Q\! &rn-k ,e"?/def PJ%%$$<,ٹBhoj`A&qٳy$* /oZ7n9}$;wPq8ggem@2OɑjA f" fdd`XZvWz*V AҒ**VKU YKR2Ef VJ";;FÊ JQTrZ-5z 򩭩Nsy]{V .\>&RN9G<gkfYY_87n{h5tF>DPSSfe}TVr reEc2ذ~-ttvH$hHySW[JJp8d+۷Hٝ۷r/oNZ[ٽks\xXrVx8:FiI [6on=Znefn޺MWW7/:s:{q֭]CwO/7o! O}r(C(++̹nfvkku::h5<`۶4FّrZ ٲy}ܺJC&Yى/C.rsYY_''ѤΪWT3*6odeerD|y ZZMAw#a'شa=?@aAyyrni%lF WIs455/>H4ךXزySguOڭ]7lMF[q{())Z>ʾɇpjŲ{y= fA?"·EZAAAAAAA{*(((((((|P@JdphXPVPPPPPPPlZ|ny;FEk32kM\̭ۭעOI\kjVL&lSx8u,NҒ*s.qzךKeE\pѧ(d2q qCǟ&PR\, !/|25׮u[x|z,{RcuK,rr#%>_})YdffEDQI맴P0ȗx(-)AH&Z@@܅KPVZ"_{rP(Dqq Qԙs:2S󱨇995MiI1@/pt*7 ruG0ԙs*-ܼF&7'g֝Vjku-re0Μшx /T+((|JN aN=ϯ>>D,J5>>t! N#>?h[r֭]g#7'}*PYP6{4j c7GH#G]-76y瓄7oO:޻o3X̼k XK(.\H맾V!q=4^kbUflf̥InpN&={vahjnakf99QmM/|7Hdgظa=W8dexۅ(u{^}mjkjO><ƫOL̓w%<vj~wR& e* `3Xj՟bI.b8i}}T/s3 8s|rhJ~~aa!](xETW 07?h7,ftlL_ckڹv:_}r}L YC]ܜٞNVSZRBTWIF;mmRYH"wپuK H$LLLRYQ*Ɍ h˺kcuY;r>0ZU+eg>>w0" MII1N Z9cyApOnrJ#4208rHVSxX,Jk{F7=VFiU VukְaZXaS˩ YYUXAmM :2Vg 6-Cܼ+/ n<`lb:MF֭]Ækq=,p$O?Ȓ}`cOiJ̴16>ܼSEKl߶@ cӰiEr_=ikW^BLqrc_o2eui6H/OywҰ9͹efv?xH$w:}Mv$?OdT/7^e= ?zs /|aYhO4LwmV+D12k?@iI H-qhuaL&#כ[0 جOb|{#|֮td۫Em[6rdRދaZ~߇ e| F%KγV];̴vim`TÇl4SؿO9od||+ev# Бc,,ʢ`N 33#avMbr?C pt- q{}}kpb2pݸ=^jkoɿΉ/Na3:>}Ϸ DAAa9'o3;,99ٔǪU+EcLLL2;;Cj UL&}ֶv.TPPPPPPPljC4 \fZPTj,3CVc0PTrB99O[lMζG}J1];Z!غe3[l XAAAAAAAACq?z?Ơtx Z-ļ}~=N8ALTW]_T$ _cnkײyOwq'̴9rs8uu޹#x,^{%n=ΞȿP(ıNx7eAc#~)--I9\/ͿJ>>DV^}kכ@oU ??`l|?}z{DQkLNMS_W ,׭/Oay7jhߏ9wYYR)G}F42^k"qpr#3 z{m"(}{~Z@n^}E ^[Ш DQv؆ 4DR+/ Hx$.;7Zn?g#˫/HN -t."#G鴼(W$Le IDATr&D"A W0 ;j*}CGCsh4y $/7p$?G?oK,3CCL&} =N8N ‘?n7_:˚ի8r386>GHAXƶ[P^^&[peV j5^:._~UV\ HN ]= <*h7_$Mȝ۷ q;Kvnƞݻ8zs߻ @^jEeZ?uTWRKeeur  4|r‚|~p=ܼE4ۭmVy >;%۷mGǼIk[>g7_DQ|"|Vq^7cwabrL@Wj913kx QLɡ#OۍA?=Fj=k];sSA%TPPΣ,Eq8a@BedPThZ:vOO^,U?& g?kQԄ!g?L(jجV]oFuc ѧ9|3V ]y h)Y=e |^ CaZ.56ukeF^ǬΑc%۸~Μ@4_8HɈj!wAy3ţߥx,NtR3Z[4pէOV#T}FÂlj8xy"& =T0+$=[,8y,//u*ˈtfEe%FarjA֮]M0xFV 9y >KYi)O|A{]rhkZ>9tJ~&VDED<7_sz4 w1~w%Q 7yyw295bAQfs3ӳttvZA]m NJJTjU*`M6jRz6l7^Cxד&LѨٻg7C8^Qdee2pN僋o y zffd vAvGQ4#+3{vUVs`^VP:G,Ex%H<33$ <ٴqNLNV5Is˜0?$++wQK?)l62lΦ {qfv9(D}!!>:֬_߳cVe4dbbT%pI"c㌎ ycDQF>dzzG= (J-6|rr|>fff t̥pX,::200HOe_'pÑ;vKջ3RعEy|~}>?ܒ.0tUFQfffe'g)vYoq@ /ۍN\z&= UG"9Y*Y|B!vG|M"tʟg~yLd‚tb1N'N+mr|x#㘛s\iGs$ ssݞ|~G/G) J,Pp8"˒s]7 A撗&HՋ䤪Pi:P(‚Ғl62L&搂<3hp:b1LF#x|Jom<fQYQN]m SddafN0$';LN'`1 L25= (.*# a /7A(8E!';"Ix)+-%H$x 9"(% O|AVf&&{q:m I&LNMIU ``6gPRR/GlV+@CGQP +$=::J N#riTe@0kY#>6ή\.7?zw'/;Or: ݇Ir7sD"N>` ?_ގbpkVuK>FQaS33?3t"PbO<%,/ 2Ȧ&S[SŦƭۭ#&&&3|>;?-  26>];LLNQy+%rrD"!U u ݻdQ싗`4zګο(\_9SKq4۾gyh4j$n--vj^~JD"4 0- rsrxWٸ~P鞸 |ױ̈́S$zܜs&n&)-)icڹ/xjkjxfN=2?9vG(**d߳{BVf*FI$et|p$LVV=vl6+6d28۷m008HY+ytV\TȺkt,,,02}imxڹsb2[ydE:;ذn|"QŒ>g7\n4}$S$FQN=Ϻkd1ijA<`3e$I6n\ֶ/cZ2=kv%Is{= cVȮ;Dܼ4cicrKwblQ#ut]#a3:m x9|#>>u|aEP rАÃ#:r m[hlVfU(xƢAvlߊ`фБcib A}}-/]l(eJKJhˡ#ljDMl6^N(bl|N/[dKf>0g]WN|qRZLtG"u*]xM(& ^RI$kIQL"&AVaѫT}Bo\( HZhF 7RT*xxDLc12F&#ZQT@Hk$FJJrA @j_\H8L4!HLJTOLʿ.ןl, $!.d`<@$Dx"N<'!$$DH,F$!$,!h—'OsyYkMﳳvp2;kiz|q4'O4nsҕ4h4զO` 64hIۋti|>IKffg92FUikִq]z-ѱ1N:#?wrgA8yZ㞜LL>D"4߸Ds084˥ˍx<^כ[hnv&'8ccH{4?pϥ+tBEb-7o!bnmjS3pp$•hmkO;^ /\W::x8:ƹ Yri:˽Xnݲg΢Q:}TaF8y:MaXz(++eUج6Z|9TPPP&]2때CldwlgNs[@n;qxh4F\mjQ.sSPR\:>=+E"?[6384?xM.P(̏>Pɽ^::H ~,-7ٹc tؿOBRPPP.wa ].F#f&˅햾 ԝ(&p8 B,xxxPzAzyxG5[Hbb ֌ t:ݲ:`1ORfaTC(JAFcۭ1::(--@M;ؽs_<4U+(,(zf{l^wyf6 -KAj*f*Sٵᑇ ?s{m,,Ұzzs셋wIDATvj{zikSW[#GWqLMP_[#_׮ӋJlݲQٶ-7oc;hhmK{9I'!ybjkU*՜9{^ϦPb1߿+ .Xs.LJG&36>Φ y>(('/*r6A+PYL@ZզAl$pxλ]Vfe l޴QYz333tuuSYQrw'q13;KMՊe~zz]q sVforx i7O8,,cx<^F#D H9@ e" rl6 YO]}@qQPP6{-3N  w2Z-$*nX+vbFJ"%)&q<$񸈘HNGvNbJ9DZM,Q1x,N"'HE Q^~'S O E-,l6f‚NbŷC \%Lbl`2AGE dfJ3nhB1QVZ‚w `2j8NDVC '?/oi'B$ ߓI䂏/bjl :-Y&'GJ  zTi $IyYAVt0 [ڤDBhRZbA21%GcRkdefN8⨴ZIIFCRH$E)hHRO(MwL&|lYE-JBEY1ճVhTI$k7HPƢE)CrI&APZ- b7iU_X,F!)oY ZtX,N" f=Fs"! *dP0D"+iQ huz:=hD=?^4^ru;Yz%._| [6 Gرm+d}\nJ|~?dff;\kA Dާ@ 'O1tQ^Vڿ+u.GIqGE벞^"_Ne'9sc܅W^V$yy?~nܼzopXB9uob4;i XAAUI :QnIۛŘPR+RvPաhQ5FF#N` +3lܬ,# JpKٸa=:F>0Ϟ'w?%z 8}<>Jpq@O8)b>;q: ɡ\mj/]CGp:3wND3|(,ȗ"rsrx7i" U~%`1)?FXԡVE/)f}~v>^72(Rhh4tZ-*p,F !'PUtZDQ$#_OH(D*egs6/<6QT|94Z W]'x]X 295MaA:.`t?՗_[f6nT*^ӽ|{zS[Hgh0`w8QSt%E45ߠ#Mb) >T170=3h N0/ j Btuw?NWJsZ, zo}V|%_*‚:vQ__jŠr[e% '77D"A{l]!pl+!fjSd4bZy6njN[9|}a4S翽.֮Ar?I&|!Lt)@ @$!33|f4Mw#uObzj2&P " ?qBy'SSOO313˜I\LIIqh1 aɶYȶ1u: `n-ǝ@ ?  2==V P5dfth4j5p @<d4RXP(+-%#ÄdBաVx>bZYQY_@FC!dZPSShi.1uĘC0y|{R4lb6SPrdef2>1I{ֶ^yl8ѱ_a2ښj| OX#~D19?#ٵsdee2pcN "g^@հa3>OcϮiY.{/J^.]Iswr|64g5q jkZQ]6lNsGG$}~n޺-;$?F/_ r^~ŲQAA{0hz@ '(c O Ă?VKBP$F$#OQ flX zz&VQƨQc6 X,z=V^G4atbPޞ|=jjkyE#)"&Ty8JՊJvñ1vV֡Qb|iؼt"dJVb~X,8s ssj1SqEgt|ܼ}m[PQ^&[$3l<333߿MJ;O^&'Ms9t8`Q\.}O8 ٱ}6n ?իV 1pFغe3~{=ڷ7h&(GZS-N@A&}a'/+q3jI< ,3ԑ> ߣH05Q 򀁄5h`pq{P,q9) z<Ȓ;#}ѽI<\/RZf} DN \> dkP_ f:LeC eMK S.I:x=pNP8Pq:&A2m~^S%2 cn0p9]8dUվ  \S@4X,F^^d )D6n{Asn`WC"NLkIׁaeF=4MEuEp:D…if@(VE5 UMj*aqx|^rr   {4nxk9--]$ LjiT-a΄`"2bTT2$I.\N.É(SUYA4m+qB))+"eR. ^ ɩS @u _𑣴xTTTp xM)*(SRɓ#a'( ecðYtMdI4DeYa*XxUUI*f"Z udD6L_N.C"at*)&/ן)2YVx޾>FGX0z=w8_f |>ԉ \;w6G}m-'N~LU ;4Mf75RS]$n.ZAnC9T(5w~:?YtW9 RiҚarU 4믪hknHCxy|^.u?d2m.1`J8=dkoՏ=rzÑ.+Ylظ0v}v'I~0H"W^ng:N\M{1ֿK<`r2㏮. O]CՌJ:彍KZ;vYxbrI~?o/q(?xʔXii X@?@.Z[[;ytJ yқĂys9~7߽I:ۗ/bMssSzgͤa>Sjjin\Ͻwd;I4eC̨䙧2<2CqyIhgph8\C2X~-Ćef64P_˯΂ys3'wH$$QYQNeEy} Wi'3O?!jjf %50FNQ<49N d0~&U uuj&qA,ieNee( Jf ?qMsD"Qrsx<GFشe+C,^'Ng ?Yt%6`9m bd"[*p<^\*ccVghJ o]`>}D"aJ^NU5JJl{Ǵ[g#I af>7| ۍG"Hަ-,q!$184̦-Xٽ#jQUG1%$ItlW7V={{xikˌٰi w/gv}N4Mce56m݆$}ߵ{5@u4Mc8_wCtZ"'R| \;z/`\nK(IP㑒8I㑒xℼ&+qq":Fš7U:uLeOݾi^"~Zg3p8Lii={O^懤vP8i.d^Q[1 p!>=}ūJiiI퉉 FG/R]5þO4r~Z[/uŋR[SMuե## CyY'?[3;3'Ms.k;OdhxH$ʦ-8vPVFG8ee~M=gxΜ$ En5@:\/}$I Ӡ95p״4311AMU޾~ 2'{K|^/⠿~+ZZtYi@Ww/ShjE2qgKᱱ1~穩"J366NkK3h&475(= u^0"K2mhZvm;vrelݾMSTZ:(p\?D(hhƈF"D"QѨ}4YH& B2N9,ZxU3*IR46Τ4"TR¬ FMu55v*.q4xshmiff}8ɬt<|VgsUuݠ"ع. Hܸhr1>:8v٣@̶ߣd 0pܔF Id맲@ CA~>* <4- ׯH$ʆX!bu){zD'A(@$Ir٣ ׺@ ۖ//z=f |%2 —TVZVp:A2~IPsd NsA>HDPճanrn o U =3$+[43].#F?5(\ (LTx4kzc22z | Z_) XAAzTAAAA3 =_+IENDB`gammaray-2.11.3/docs/manual/images/gammaray-geo-positioning.png000066400000000000000000007664651413310443000245030ustar00rootroot00000000000000PNG  IHDRfh pHYs+ IDATxwXSW {8@u=Q{]Eqks+n޸p֪uպ'CAq{<=997y=B!dnB!зB! >!2 BȀ`B!B秒fӾf9c~imLLa!BGILm]2,g#UgK) B8*O\do3"7 !BHu '<3:'[J*ܼ-+1&.H?#KB!TB7ы >Ka]:wۺ~bB!K}ykC >uX"~56*=w4£wBmg]v񵵵{4k:y؝{~5fS!M]O>+.>^xwAϞ=;ar> V:zqm;^ o٦=˲)}Ϟ?_H_Щk}9z}!*v(͚f-lҸr+9:8T(_~o{IKK;zP^{% U*q z?XBL /?7hcYť#Zz%IMھE([QW^ H7jX{ߎ ysfZsVC X~Ԃy~6mo rvǏ"B Rs~*oP!B_.x"UT4/Bo/_>* ;Nی/1vWcT6FXB!T+eU/d2SSS{B!T27A!*pz!BSBL}B!d@0A!!BSBL}B!d@0A!!BSBL}B!d@0A!!B.SnB,&| 9frdhTоPCPV>!2 x/軇TJħ!^BYݽM0~:jws /dEOjƌ_U%D,.&Fu[=Az[;99:WJ2CZ 0/{|~'!1qБ:DӅX |dGzo($DCԪ`oXKn ժe^>u,Z^M/1wHYc)~()h_K[V]X"P<XZX=R"d+/&Osxo拗/7nS~<ѫ'F]bեWm'$Ie7lr0 ԟ>uܻ`ccIlfb՚/^hL5O,k_W2qL&+!gNZ#G VEQ𸄄Бc'XĽ[ЮmYs^ ZOƏKw~D$V;uvC-[Cx{:iJ|4ss>= nݾo?tۼY+nݾmηQ2BrӦL, ,وa;.[\f]:Ϛ>Z*KQw c֯ɍPM[\Nj%.1Y,<zFxLt f@OHmFGnj4GágNn噭~QG;oϝ? q=Ϟ>s/Ϛ̉#A;E\r!4WYoeeyPɣ(ڰ9N>ckkvՊժpy1T靕eZ5%$q5\_66nB?ݳ/:5mr{tr'-\223~x9/]8o՚O>VIFFF=srq<lZų/ټu`5K\hгǏԱécwnhcmfǜy \/9tkֿzIX2oBwzRXWG;} &,ⲥ[:T0u)Eb,2Ξ4513zdzu7nz{صgԈazԖ;M:ɣ~:ѣp;'Wz~u{Q#֩q\zvµ_Ƚƌ9pP;h_g9 |h،_& ܭ+zjmeӧNj)!!JZrsG{FRj׮IQEQ_~ <sˎ7o}Q?ٝ sBܽgll|Uhl؟БcGl֤1Wrء gΞ_zeЊ{[V.'OT*Y/JnnS'O+\AQ+W;BӦs..%nܤqUkhBajU8~JeƲIZ&&ƣZûkլ<< A/h<[4HX7SջC NV(\+V4alYggP(RV]Gg·0̶BYk޴ɘ#%1ұzs. O=_zD"VW'O})pr.eyb_A\͚i9{R4<2-`jjy뎴5k16wYN ؾIO=_jML; azI=åW\+z4oz*l BV}gx%JZ+ 8r V/T*9qӦϞ2iiڳs#$$&߰S;oX62Re`ێ]oFk)rRrظcko{JU+O;-]**:ƹ,׊h^U+a n5"][y6 }Q# !5uy=u9yа_$kcjf2TWx}++!Gt1u/Qq@]+V煫{`fffbbI~9?e+W1 ӶMV6d}9o3. 2|:KHL1z܃գ~2sN?r`Պg/8"<qٵbnZ׮ܼ  ^s0!t^0իUsP~-[w,^Jn .}ڶYwз㗨}:d?|H^ ܽzjfcwZSV=~CjjF ` f?ÇNN/'jCn slllܷ{ܺ' ' Ƚc155C s3mdɿZO+DVrZȨ: 8Vvг'fMB"9~81)Iw!^'otXBԮG?JHL VziS&[۶2iBȉ#srtc \x H/411 ڵ?hwg_ݺ}GH". }u)ydl\'?w.q!j7n)Y!Sիrhؠ~\\[:;{z4'U7~{{;OШA}k+?iS&֭Soas` ' :IW][rٱCv~YTlXfYnO+U CN2l!gwYx TJeܹ'h歭Zx88X,=cZu5q󖃽WA8:: S66ڷ(ںF?Wx*_΅$ɶ^e}^ަu ˑ$ժe2NBа6[%[O=KJJPRrs[KjԚV-[9~ozwt֥S=,;/`Q^]ʖĤKWFrȁ>{N>;55Mٺ]f-n5ux q5[6lѼpjJnS7q.mN;i2Ttttt?2pEQ͚6z:pO&&RcWw,66sgϴ)P;3ՙƲILuƲu`ooۋ$ɦգ[L&׫# Z: SDRy{62..IFFF-<9)MX8::bab%6^5At9'ڳjƍqttGw_3zmڵ.\ ?Rg6zD#Y~;hD"|QÇz,?Դ4_/ZbJmmQlmSR>,Ѵ BHzMy/Ypwo?6$&%]FXD)J>~*лdo ~ʸsOP>=u׿rկO<2`ȃ{",;{ |gimgߎh׺e[>~Qp)e<} LQ @>a[8_Xenn.TJ&&& pÅrFW?"8Hw,#77WTMRid$V!$I*RR~#P(TvcWaBYM#"%XЧCMzv1,«uw]֥`me)eN]{Uw߇V ͸xݹ5SX Ǐ?5(8VPZK eΝ PcQZZڱ6oX[7o떋)ʼD |`wy}r.vy3\LRb-4M;W)^j 7oSEQmZ]z_')VTiiiVVVIRۮl,-,fNZ'[R8eJ0z|9\\C-[irIݻv̹ c'L ;wJ*潇RWrs+ϑZl?i?njܨ\nTSVrybbb֗6LtA#\nTT2##KԯfG\v}e9.%|e1W#dYV/eSRR89rϝU373{d9\XaP(RРޏo޾ ##"._ʛ6nm ð,kaRnٖ;T{ޱoӧ:9:۽ålY_lMJNVviڸEQ ,Ow JMMKNVܾm8N8,YPΥw=~._^@lY\++.nEGoM7q\llܡ#ڵm˛7oz!4,{歗^^'k9)嫏u5m|bXTt4׮;y{9jpw!VJ?tDswP]t3}\.22vdia萙9y +KIfժ Z >ڟ<}毇swiT'O1 /".UZGTDEw?j'O{ E qwV(_s2e+mRxOHH^;v&L~rX,_E6ujںcFy0a.7mb%aN>HиqCȥȏϴj=TATqðyp^x)$::ŀKWʹTZZm֧B@ A{Y}5hv'O<m&ǑsnP5Q@dvޛ98բ"߅ܺBdȕY;vmݱKx<߮]|.^6`p333_E{X ƌY]p-='Od2wo޾kk+Yn_l{^==y r6n޺nf"+/'H{^s_ui)IRVVлgtezAôZ~fLUF7o}o^$ImZXl>6a"Z8i,lY3-\{r¬ro޺|>hz׬·޼';qJxmjUwnTJe3,ZZKJ8ʧC=H/ݻw,:9:6޿7r}t L15v_SVBY tB4h IDAT"%..R%7~UT@E(wQB~={a om8yOVg} KY \ظEKWDEE4nX,-Ƀ!LM ?h %R9~=Y|ny:lj̚1MKzp01,9"CZ 0/{|{B!g}B!dp!BSBL}B!d@0A!!BSBL}B!d@0A!!BSBL}B!d@0A!!BSBL}B!d@0A!!B.SnB,& ) Oi Z'DRM@! >^xnBddEOC{`uj* JPIVǠݽKcG#G >!=*ɊlŪ|^vƽ m;DEG(NZ#G .}w+J:PIoaӖ}pg_=nݦYƟLQQpI/_N:ûW{Ћ}b>m4;BMVxY+;Z>ϧ=B qݺ}gmv5yڌظnQ޿y$Vm;vq*ngAM=8'Ba;}qvQ[87H^|>&,ⲥ͜EEG >۬Z(b SFGnj4GágNn ii+pwfVuGdTуsOϑ瓄B W{:v8uM>mQ93o3'.zWd}655Y~ҽki3, E(>zRXWG;} yq~/g\|͵GB6j So##?zVn鑭|jUxa{.kWtX!##c-M) l۱+4,eM=R">s6x=;RȖXeÚs.ļ٧cǟ A@U* %͚,N|h،_&}w{:J˴:h;j׬?ظn!+xE}{iƳEsD,7k"?hA{ ׊&M+V))׮#Dܳ[׮]|!nygswJ*_fY(͵b}g׮^)HUպg&pr.eZ_A\͚8y*cv$I>}|Ū511͟;~6zr8ZmT*kŊkW$TșC'$Z[[-[PTN:ɣK:ѣqӦϞ2iiڳs[Cg_HOW=~~Ob޶md_zvB|JߗGc266n^RӣkNKWq.7˵b;gf}N][O~r..ƌ m+XDFGٱu߼fS`Oо?ٵ=pЮ蘘ݿ}͚5HR?7i1[{ ӥ>C VVW3uX!2UJC֮z߬o]hXD!'&%}G5B߆{>_wZ~P´"un  }Q#^ vkjjZffj([[)εbVuV*bG!^'߷w8pl߹ÿ׭đӦL$⏛m5e҄G-D6 \x PZV=קÚ:/411 ڵ?hwg_ݺ}GH"."I?89eKGzdE987kKqΞ psuhTح3mmm Lu)[ccR?:kl,ZĎ",s'˲޺}aXe1Ebl,˶CMzv1,«uK! Y1 V--ǥiBa[ӣISqVF uurѳ\ea!OJN+u--,fNZ^zR1))Yxh(D*ngoUP޹LW_zRYHuI~}4rx|ڃ ORrJJKKHvzjA=eR-fjjZQ\&11QT #oʗsZ?t^nݲI(_z٢Idu=s SΝJy))K¾X<z츮ZMJOW2 /XWk߄So^>闯OǍ[&%'m;4m܈?{>8e/]jU+@޻%'+ n߶k}~7p`qw?j'OU"ƫO=Z&jew-PŋBHI)_ chݺ,]!MMMkתRV6tu74T$ ׻̿q*Jss=W(_..>ރ<>{vKW4Le׫;kTYפ$r..sھ{{۽IRm۴. utQK@Pedl;cGZzL6CpXo< 7c9{{cX[[ڼue++ɓgqϻ}z(hSgϜ`ѲVm;ǏCK|L*.[`XRyC: BΞub]Ʌf!)oWܖnn/^tssxGy #mqjۚU˛5mhiBakc|B-YLfdjjݦo?]|.^6`p333_O#oMz#:t>t0nj<_UV}F."ez+%Ht,t գwA&OW?W:*[ϠKwyOQc* -me諽 A)D%Wr#<{+}y06mipҭ#>NZO !¢z`jjZm)w9[Ç * )ȅG ʏi|U3>)h%ҭXBB!  BȀ`B!B! >!2 BȀ`B!B! >!2 BȀ`B!B! >!2 BȀ`B!B! >!2 BȀ݀b`jb\M(6ӕB_]i>rB! >^xnB!_E kocGשUB}AG.L} n?v4l >rP~BdE3f׮jT/>jl9vva$bWIhuqKv''G{bl!BY 4^|uS6V-RctJHHws vwhټgn"x)J:;` 55Ǎ56oܼUZ5.=ȨUv4o?)"%eU#H:w8| $}IB7W}{wn]|7kUuYGl!TPgΖ\+ χ^\bUn]FU,_ /_VnL ETTtZ5Rctt#·/|_G|]9lH]Jg{eIZ_h f&JnY .^nffzt1eFFCAB啫ܺI$:j1ڃ*Mʗs9{؟u%gΞP7xo<=r~㖍V WmO6 b̈́M{2r/ccuj/[KWvq >< ?~2zD.݇}=aZv=;n┰Kw퉸|wA@tC۰?PlŪ}gۭW߁!g K\dydƲ-TRYW0 :H,[ZZ5؉SFEQE @<<ŨNZ9&ȹ#Pxh~IQщIժTU>ph]z8dĭw¸gt>xW#?(bQc:v>jOr"A={qud6Y:QcƟ<=n'Yle:w5a8i gYvcOPbRҹ 'M&ҩK^}?('.>wA'O7cg|W_wW";[_Bw:xk*2gy5se \Cj9&N[s/ܻ{\.߾sÇ[U\||YgoޤM׾SWZS˨CSN0mlÇKхU*"{r\\޺'9׾]]ժǎ\~;`%ժTYhAjZڜy cK.ܵ'h՚u۶lEܗ8f$g2-=[zȒm @[6}:1޿zz4MȲlRrM"$I'G:z̉ڷ'$&VXAigg㬉3-)!a9+Wڪ}QBP䩐?qt[#PiҢy=}rJVX >p; ò,#\IV(L51iou,@Rr2 :\(gLў< SSƍ',j*͛5ͣfƍ޾ae_z -< NHLdY`nf[aOp1<߻R?yK8aN=vԀ~}'~j5⻰%p.:-PS;B"6<%%\.y^w뵣C 8NT wL'Jn6T*!1(z?Ued >պp򡒛X,V|`s/dFF/_iҸa\||pbc9֮m/Rԩ]#.Yӥsg6/_e9{>ԳM:ute~f55[֬2+ZE0k ݽW+ݽE]| >n ?Cܽq# )ӕ mBfImZm?l#G;{\$ʭWo]^ ˗,b3"c;hնú[ΙQC?iX~L,t? % @0nu9 ʏ4}ǰd ?_*I?˥lZ IDATY yZZK:;O_>SF:\^x nEmn*}OMOO722n1G~?mTZN#>%B!TʔN#׽Q!BD1YB,A!!BSBL}B!d@0A!!BSBL}B!d@0A!!BSB:YMFvq*CfF:hMIB4K3szj4D*K Ʋ,DzZ!B$$EDw0JR)XLD"ZDR>=9eLe)ED$D_1eFVk4-Dz KӴX&HD"AI3i)ϑ$imWʶp:9!XN"#)-baLFaY)cL,4c ye3ï 6<JLḼiqP~v&Ih(HaiH%u E,GQr $Y $I`Z$IJ<2PCVc%Ĉ0y^fG<˰<OH)Llb![D"4ܤ{"XVd0X*h̦qZvj6:CUgs<ϑIRTf"SI]H$aT5Z+(f?<Dz ְ '5J$XLiB/xgYNTT$EɌH$xa232#$O[.$v&3Cfljjf..9!0uVfZDJ|A"x'Hk EJeFLF6'Gje9XB8N8>ATV;J)-@dHKK8Bj)D1ÁI3IQ4-"Ix:S,1z(&D"j h 7u ˲hjFX HR9 aJ$HDbI(&Fe9eYЀHDg$99ce4MKDJQ_x7s0LZQkZF( xo4ZDZe@4-JȂUQXh1\E_u$IX̱:3cYe-cYa6jԙj% KS;))"hNx(P)%\".u$iĈyUF-6%A,f4,0Z|(&3CL31"in!e23TU),AXHŹ,@_ ALfDQǏ0Ѣ/$,j4FDfd$i$=jIiHBQ4(ZXhZ-"ajqH8gXcYhbD*= LIAp0IKRtIFy!DZjX#3TVkMfzZX*32.pϜk-2 Ct3p$ADT0LZ-L}qycaE4M$MRT,} /e?5"怐Ɩ2dB@ RcTd*$(B3=*A"s4Z@?e*M Z A$ <ϩ3U4Erk;c9b$Yvv9wsȥ{02J\LL@^fiQl h1kȌ$H@4j%1 0UK5յdfE.UtLWGxzF_?oQ.]O5wq)sًy}^j1 #:/} شN@8׊Ck;wUBzֺBZ0\SI~y] 9q}b;0yi s̛5b1l6fQH5$ފs~jU61V caMh+9L1> H<6jmG8y]z:W@N)|Xu]9VZ2גS,)ke}o]ˠ:b4b{+Tra5ysȐ6uژEW@"RJR` e(@DҤQdml/vvRR%g@Zk}'+bL9'z@m֛/ /_dҍ*z*K[cY,EPgٟ~Xxձ{$ Gn,=;*}hP[Z)ֳ@DmmI)绻8=>H#yק_[W_v<$dD2oNO,Λc~<4)PE s%֢DrMeVJg]DRoQu!x)(kXdimfZ ׂ]?Ӹ9kcHko|o]fUj7(_>ZׄD(QRJN1@:h@)곮c`ȑ=)-}|ɿZv8((PFcu]1qEdO#iCSNaͩpu7o̴Q] QByõR=h,`*WipD$bTApp;ayLƊps-M$H\9؆zjRsJ9R i9Jjyvvi."~1_LR=Rk3mܡ;!Zi}|P{$j9%^e`4:TE=>vVk)֤ɐvWBÁu]ɱӜs@ڴZG[Gڨ&TA>^hyR2"z:gD]ԾAݲ}9gPw 5'.)ysƚ-o7ee5nQ-)ljk|?90_t>"RԊDXm^!߈(>=b}/)%I)qej|vGAf??jfۑp9ׇy3Gh{$y)BoT ",Rs,98RQ{1$@s\R)ԚڶmIy҆A5R¶TK1'Qbۮ Qns=9[JQ"xPV}s4{zHQD软Nc:Y&:^Jq#8;aCJ -VS8i{ uyq 6/SJZE֚n#"\P塱lD6fLQJj/?"Cu^]Ȉ.q) | OB811@3~>m܉UrNjA޿8w?N7uwt30!"0mbjN떗Ka\7'9e$s]A"`Qr[ Z%gt\[`HHI""4q͏n %gQLks"oTqo̳ $ķ%opqp| 6o֧/*w5E kj[&t@zc֟/WѣχahZ%؋ۯjcq=씖Ӝ͸ q(mB_wהy]5RR qr.UiA !L3,n!gmgm7%EqxeJqܜ)@-_[H~qsHfc+uYH=/yf}?v蚋HI)Pr2cwX³y1}/ ی(\5!wk{SS)HNc>Dwy<'5}>}j\`\o̎=|{X]Uȹ. 󞹆$52o]Hxn]gGr+%u6I*ŵNyG=YZ-:I19̬%#"!6EZOPhi}296>ء笍3>[lOMsq53>Gonô%/r̵r5R9RPkm,* _9aPGHSoߡW*.S[LUJ9r)EDoЛ},Xs%ǜruB֮[89"}LO1̈͝:c !7-C+ (b)V%ޏ>Q hҚ¬{߶|_ekK)N()0Jξ?l*j iNSNc"K)jM Zk႓5!߯K\ks 3~麥s]Y)2WfHZkiQQ.DJ<ҋ7JG経Ϝv}?JaZK<.sM s<Ү7 G!+rVO~Kb8ZjaO w^iҦÌ߷Zs͵d溏 )1Z u8Bw=",̩9La:m1ig ")kNJ)c "!bKh~}&BmZ`$#k S79RK:"1 @!BsT%KNJA)%T6M1XsNЌ>y־_0Oc]?e&2=Ygw<4Y{ bu8,[ks \1$t`fQE)9?zݜ7/HR3H]@ҺyԒô)%2kRM-Y=ZKEXc./y\S9ዾ_t|RJ,nikrs,qRk*DΟDI9̛q}B.;u- fڼR NsLhqpKzS09/{u9?}Vk~:buNMEfs2b: uڗ4ᛟ$Z F;c_np25)%1:0Yvs/@Ω")R:RJ0VK#~"<-M ⵶H҇һ|Hq( IDAT] Q»^dNqYhSbIGq~apyv-i´ad賜9AKZ7lf "x[@icLM\fT4[cR}N/r DY@lI0=Iל H7qΟsyw 9ڇ>,dfZtfe%xv}qں6r7XGlnD b S SiH$JrJ/};7ˠXMWJ)2?'ךcE_؝mdS,)&ifJ49<ᗈTJ=9'O_K=E]4%R}/SQDRsFf=h9h|Zw1{ZUS S%GuMIk(tWc`ΥHRW)4OgPpsŴ/斨t^:)(醕 mi5Q_/N2Jﮘ 0\k[=!ؼZFo5Z{1 D_S=~}G{hmt\J8Dd\a:o3@Ьqg/g_>n| h/jIq^ɳ?Q~y71w92RS.5:LDD$?͜z^ 0YSc-e&!)>XaN1RZĺy熔2nA=qg@놾Y_-Jg/:m~2N|~q"A9{ }VI9yZ;9q;ZK-9Rb]V/.!RSrp:{M~;7s.̗(aι4UoMeaa|~@i9h-öۡm5QnZv݂n5n͝9ũl`|Oo(]w I)Řr."Jk}aE̵.!Dkf~_9>: 4=95(w9g-}.\*7r]Kt{ǭ]a?_;lS g'Osk7Dd穖rC6IĔZկRoT!6Fc-&G\1NZ)oɺ>9H",B1\cjȺag)uNasu|<ҧ{i!(un[ wHSjsU6 wX_yqRç=⾗QQ"AһsRW^:'"?4Fs!Kn"y<O@,}Cu\~ 0O9`?90nրH[cly6Gb1x33{o%@k-TJo}j1c q0C? }Xoo3o|gz|0l8iVir-Yĸ˒nP-n}z4[^ JF^13C0ouKp>陇4v~Øs,5_}pNsI1.G)e׵wG] n.ZPݤu²^;kAk єqZ-gV1K)""mn{fzBPNv+}RD.wR<sJ1NaaypY熹r-1lΕR^=kf14ZM. s+"332®ysmVH0݄?{Q)28o4~8x[C$Ā{1}y<%nOE7MӬBjeWǧqR1X֋Z uLߋ,rh9s1TrDMXk1FR4s@ᅨEjo֧!JNǿ/6 Iaa1suwt6?/%Ԯ_tm&*sVF3"m9ཿJ եZ+ӖDZʜsmSEJ)B9[9æĵ қ\x~/V]1*S9.WǾv&Ԓ*F-2к MuMK5(sӳaCjIԏя<Ǐj"c32cфDtq 4~|}P@s9޴+c][גkcDbHqN1WM?cowua:y:%!_"ր, ]eSZ1K֫/3D֒ZJ)%Drh#?E4ETc)9C҈ȵ0|~X=0w3眸: ;4bJQDޑ sRJJ9XJEBԨy;l%`Sޖ|?y?}Oe(c)sYښFf4g1F7TwKGbn_e6xbx⺕1ߝrRqk2sAm47ʂxG^Mib̹\Rֈukf 1%f~XރB0o6(Db)5Ƅ~8`ӯWOHM=/rff~Ut(jy٬id5NjքkD%%6vަhm)6MH*e3//AJ)ud^?.?3ktSLޙOhݬZV&ќ!D+K)ZY5svOsnn+z 2r%hzQ"YK.{^&2OsɥHkc-n[5Qר`[ZJi媢6s19N1:Y,оV(c|EY$~gQJaM:]yNI"SI[㝿lԭ:#]f=+GgmZ^NhMDZDDHJ& 3ܠAMEt~+گӞ|34SN1Ź4,cc\-~:ǰ<|37vtBl%k\^[{65I"ZkzmEDH$j6ƶ'1F䬵zkU 8]Zk/r߼Pܑtl;Cw@<.~y[oo)-wW]|R @݅DdDڍ )7Z$g𓧟 \}67ƴMu;G7nR%H8P*Yةп"9g$cvKPr%uâ{ٹj)NJZXjc*ywl=ƘRjj)ƴ_`{Ǐ/z<-s/}ێ&ꇁH9T[=S.)Fc6̑kf@|{D8&%L;x 37Gg鑙Srm?"ڿ-:Pbwt{.ruc*Bsl_>:^-Eh(ywHuJրKҦVAHl"Gh.9sJ)mVHA\j5q^aX|7܌~$4F+Jdm2yq!jccVkڙm`& :gR)6\J)3z5*ežA_*ٳ }vq-rk"8OaE]7>bgYֹ{kTX*R+!:眳3@ *0)\D!(uobflv??SO.ܯQ\K^hSbDe̎7R_3*' gϹᐌ.)|suNuCVֺIzDK1+6myN %Xwعި=s}pG ֚sbEn1/aE"פIv }b8oDr\ikH*h==1IAoGDjS)Ekru}dݣZVw::ph2F+yظYaZR̢LCں5)p#ط@sB$c떾[Z_29¶:z㼾 $g]%SGQyڿD!qӔunsxJRRb(Z\D)c :sM7vɵ>&,z _tKp[i|-|-%# Cv$8+RqmIJvx%TrV"ƚ~J9e&"IF>ﺟ$b98o5՜k~{+J''C6Hd[kHZ{T>{"4LfkC9};X/nٔҋؙߖY6O_|rSK S/mbSMqC%bKN|mGh6kRy:)HMyz{/AjcR֒seu䰇Чi0#kオ{*U2k"b8~$m:%|++՜KNH8,v[ւhWlQnvNO9r `*䔹19"F)D WA{vpEsrr2ﻻ@"cnZhm*i$-Jyjr[IBDbקϔmay$J_ l-j%84YgkED`<ϥa1Zg;sC\.O\.axVHQlg_<8!yƚ*"Mxm9;P7TZoΩLru5Yz;4`Y8reҤ[r4DNy܌Y\uC{K2UpD<`˗?|f}ݑ (?y~߂{JnO0gfc}8_ ^WnŜ~ӕO:99=X.KDwJDֹ8VhsIQ_ifN1b/_]n))x'^,^2'Mx͌CwxiOcy?{$Y?zƍѤaUsޜ>׹͵Ȣ8Oy}T5IfYvМ_ݞƍȌ,gVB~4`pI,0Cda/([-[ OXYRɪ3++#3ۜn79yX޸qsωFgk}k|s9F\U_byJ U%L>g<>jA*0*)Ɯ:W[ ̵z&Q 4R̴rJ*hŽ3L, Oc{8bG/<֝(# ⳻-/ )%-bf?v~ _L_r)b$b\3;Ц9)4~253Y|9'fq!+|Rhcx,粰N۾V׍e9[VJ;R Y7XZ+|VB*R)bW4Z]5o4u-#JJɺc;ckT5,Na\pńsN$!6VK!mrNU=Sڼ:~u!HĊb1Sd4j6??=sΥ ⍦o}ҶJ+?y[mb圑K)BWpwpu떔OFL3?) 0\:E~9)I_J>ŐSB*UjV#'eOc c1i匭ui˅zWxb?릙hn Jy`MN)!R(@0< 4J;lm@H^bRSVjT6lVi Ļ"GÐK1Bi۹1b q̹0Rv]0H ) s)\Q ܽX%Ezͮiϣ)#kkj|gMzmt{.)咥R5W/m5ERw)lǶ%8zu&دp/̑^׽tn_+FZKNLE30=zٍ#k``*K)TKQXorbLD)z*ۺn/g\r !(z5%(4uCeyw`93yKu=ggXԺS`mo\sR4sl^[#xL)'T? 9Zsӣ!B}J(]W~VuctSh;-91r}@ٔsSmxkgjr ʔ y,姛_vvi+"H%3?TZrs1 1tV̞fjP0nZm.lv)DhۦiKf3c:lKwb?mJ7mL1|嬫W?SimB4s9 @xnݹ3Q%1v T d;Fs>p6߸qC}ʉ>'9"R Tb)AC)J)z!T!q׻t̅(Ǯ,L[@ \C%n^69*:f"`x>(L]J__%uĘH)<_l6?9>~{w1b|a M_cx(Rj&?״(eĒЊ@\oOףקטL?PavRfUU/_030øo>*q?˦uS='T>W0퓴Ɣ\d Ns>]IynݏV2"hPRe|{ba5X/9.`\W`|R=m#m?,Wc#Q(yڹ;Bi:q3ow?=:G]w8S2LpRYc9jBiiНr)rm4gc\+ 1 RU>begW`gvv`]= C4]UB PB_~͕'JDA1n7֚K)Bsy9SJZk40 r 1s؝K1r^)}.v~iN)yv%S80_˱֮V)_0O6aRzrOi⏏ry*%1[ׯ9)unFTN0×ɾN8&TZu%}'XVvkm&r~ql>k֗UD)Fġ珘_!~8O51) J=SճVקWw~FW Όxs ` W^W€9Q9~AUVVJ)ҷ+nR !=Phi&(bHqc# ck"BJNQ8r9c].Q9BZkЮK`r;ԯ0ڧxfb~~9ǷZإî;pN&@!6rNWC5^/W>{,W`}\,?ᣏnwjq^ߺuk_/ WU)q"LlKa"&*.sзߞz^JiukSűO#ZW]ƽpKJ%~s\< ZW[I ûw}jg\ NyC!Zfyԗ>(v,or~m:W'ÿuėkҿ(%>!_me+)^NcoLVYkEqFsl ‹I~+\cژ>~ӣ4j_YZ,1asΈ8x)bYh!kRJ}#vBǏ9?9y?RUR?vaRZTk.^*eix}Ήf{JC@%z-08RҐ3? KQ4?#%7SYO6~˹lwm]UcVUG'ӕq|r*ԭK5wQHi]Pscf%DR1bB<9cL1%D&N) hyI7UqchSow dR{3Yt3[W\rbR㪻߿VUm{rte|YG}Pg𐈤}lw냃nܼwLX\-`BJN]99 (̙Vv?On_YC)$EolyS*(Α70nwh9y/$@` aW _Q+|RN)/F!hF]ݮ )aRp C>,hV V%(w1YrB|TSDx.awյvҗ>D%sԮ2&~@ĥa~Z)i}Cf18L1n?g晳ת咘XJf>2/ H)V!cYfG8nm6;y}>K]p1ȿwn{BH!s̬\.f֚) 'L1&b(꺞F."&b@DIM.h;OF)mߤs!~R9Vsc%g|SAI[%|UQUezV&" -APgg(BJ)R%$LW]NY*B8zOя>Fk[9ctNB]p6r()~()a/'s}buM G2Q4^WB1"PZ+& ʜ3L}S1 J)blJrZC;'IZR U4OUS)RF f{?r}_%~soPnZszBNWHXgk:31ŋF??b1墖"T2)1h]}o!MB>$ B T/Tʓ PʡԮ]F?vSji*K)!i|TJ 6gCcu %m@D1q}?ZgsT28=c3P3 CuJ 6V*hR”P Dg*q0_￿{`jmRq>(ras]Νv~o1o~F|E۴nbJNmUWf6說b)$ a*J;-j31Rjќ~~Wx{-P0309Q3[XW7;wvC!卪ƖibYE*:wWN fFbbgx\4vBp*^e0="'b>VFs.sZkkmJ뺪VbǑZk+Rk"Q !h]R隍ly-qJvb;nRic_aB)%bL11g|e֮̔iuնO'BdZJ%Tg 2ƔRqBFmu~{|.h)8!n7_}kܛ$)RL9JWJ+)Qawnި[^]m5/sn[U]]GwDc:>>\}ຳF/b1g CEw*Q(Jf))@gI"66U+Tr8Rn|b0ߟ fwZ]<ꃈBlwO>1kZ>7FMQWR \Q+ |8oJIDvw/l)sJ R9^;Zkf18՛q:9gyz *8N3_CHR"¨SI2c}U7V%G]3kkm.C}? 뚯[gƏya)'*,#RơR8BM4"0Z)bJ1)1x㟝-5 d33bvΤH11͎q1)3J釭3Cԁ5^_o7W[rNW6?9`#@ aivX}R~Gj\QuU]7咨RdsZV͡RֺϦ<#qtbJJ))Lr~- Ukc+mjcsݨJv1x"RنA Jӳ@A!CwBEUg`?@HY__ >+k{^R)ef:ZJ49'*eǔ1Fkup6c'Bīg&G*ں]*ۓ >I}ШQ)+ٔVJdSC3[ sn cRTJ_h=u0 !fVJɞ)VDR(v4MU98=}vX5J?T=iFX'121$?>999}(hR`9]m;٬Bqn^R9'%+R)+}JAJ%"bޙ~buҦPU1$9R5j5y(%c¸m!RJcC1Z/YDNhS.Jh4눡 B~ rKQrS&QuYzS5RD\SyqQJgK)$T$ER&n,J. @EUI]ӎWϘfa# ں 0 Q#R2 JkWV[CJaRJggzdH))iFcrN!xJy)CTZk Sԏc LJGàPR%? &RJI1*D呑$"KQ IDATC]h-1}JCT`b?/snH9i+kv8SJg3*!0kT4L9|'櫵8]J)S)ARW)qbW&/%)׬k͜c?vY J(R" mA)1±|,* .jqm%xH0xZ0ot]O x9{1){\)u*oԕFBB񺱅Ø f]ZkD?c.B)f߶WSɾߺf%̓,Di ʐs̗jU^UbݩuՋCR B4YPJ's^QJ)] y۵VUUO֗8U&RJ8\ B)}w֍7c*ӹ D9k(caUk2"*r{uɹJ9_nl67t8n޺uzz޻muݾnwӟNOOE۶‡nSoܸK>zjX^uBp|7nW5գLK_[uۣca-՗I}J)1q 㾪u6xfBQVuk=Fx‡js!0 )*Rn,gRw 7ĝkD}pC;ݿwmJ\ Qp=*9r5O~2ŋ?o{U|~w"uMJ+)&;v4?~y0ֶ]v=RȇTu-PR|()jF|v]InԻ#0H)EӴҘi֕kQUS>c|S#0qӼ6}g֕< ιv|c9/ZZ뜻}5w\4b!d"gzFi<`LE1Cf!O @Tcy_焜s!(فکB7Q adRrFL]RBԠ* Py{ORLV{87ow!c?d̓קtf7?Vo[as:-L1x(ZdB#K`zd45 A0g.B JХ+4R)y%uU-QaU]7<%wTZ_F+uIc 1RJi%z 1&113b?0Ƹ`Z3fK)9#&R9뜔rzR RL1\94vRʩֺBC0xv LT1V}|-sڈsnfsJVJ}?W^v7^kooo\~SB() crh jΏmV,مo}vPz\oP|iP9nvX⦫/nSI~N{gҴn}A:JkD)8D]-K {hw󟼓?Mm` PjqG@H _G%V߾yinzKڶ),= %T[yMB5Os&%|)g̜@Vn%bBݴJ9>$VJ}Uls0a!as1M{c1B<]ζ"x?~4C{́Ruu-KEN'G''>$:!!*T)ebZ GKCG"eebLI|k6,RÏ>zgJ]圼 )r8Ou׮mӓZόJc6X;7SpXZkyJa]׋dRWo4m[R߼ylnCR !H8q l?0 ڪҏo|77{ (18 ÇԽRQJ"NfSFAhS=i !sKUsismn'8 dfХ+SL)}jFkZ-awlpU*Tb6J)gv~io\vPw)2۝R|*N(nTsxܥޛBb\ ckc[u\!Ʊ.eal5.p(P)+9]d@t6\J,gBIdW3Z[ PG$B\ _uL&Jk)4XiQL1d;Uc}&J)RkmyySҾi+JݢЀ  PV6gK"b:#F+40S4ڸ'ȍj:'#ƮjU3WƗ"*G!R>|I ~R 4=QZk]u}lYwݟ[!\~iA&bZc3%\rSȢEgTrʏ9C8k ;P5 xxt iMs>:1cElu]n6zrrl3/U 0 S@BHf[gq?onۤsBEc90D?朔UpL'A¸۞\&" q!WVI y7 F\_re*'Ji]@`,A7 X04h!ŘBc>Ƙs m(RȗX(4NCA> ݻ\](R/̅91%BHefEW(Sb 0+ȓ) 9~JOPck!+%K)圄^k9Ř)%Wƚ*ŤZ|o\#D.{j<":8p0i1}Z RpG}Yя|63`?`6ݺuVu]/~槧D%Ƹky[.b@?lغY)J]uVATJ7kc{DIJu1n#Tڴj ^BߞaVksiWDhĦnaI~%~q$xg%B)PO s/+|0Tc>%ƍ)ast yqȿB(0378m:foj FtL1Zk4M4<@ +J116u%ݦ\ՋgΜ !B^ٜs>m%_cRBյzI +R>L[u\URi)UgoHR !SԴ ާARHmVRH!?2Sa")!(` x|1γ4k[)nZcj>k]rr\u0z/gk)1v]rzZjc{۪9Fel*]Z(D֏= i3tcvr1b8ڪ]\ff*%|mSʁ5/x&A8Q,=(%$@yr֗}}&NqR3qBɀWo&aW׮Z=ڡ 1"PRɳm.9XJI)i}n.Tdw4DğQʻwR-tߧJڕt*LȖc*ʽ@9 O)].$L G(ꢳg)mJ/n\b> Pig_y Ĕ)G(R*l-CSz#a&  !Ɣd;BJ))VSvNOdl~ Ŷ?9967J_垼Pi(c1kw޻}Jكkpq~,)mCD}?y[C߻RKfv JU|跻#e>xBΖR~{,$Z7X)%cබm5f9 C\uUȩY"@c2ŴuYf3`aӃ Qr(2P r)YsE} @||1̌ oN~GozE@<4ڐ#*Qy J FNmmR6ߕz?'?pJeD([_1 0cDE 3vfDDQK9iZ~U>9~B)&+){R) zW5`"!42%/#ZC%ǘB !PB R 3g™>aC|f.! >s kJ^}F]O>'lfB:0/]~6m:gv\,V81}׻nJY.>>IT&Ou$U?*-*}|!uRf0DYXkq AHsʥP)b9JF NөJ䉐y@\9@|02ٷݎӚ] RIk89RH O`R̜$ ӌD\ e<e}uP(Df^o1( 9'B)RnWOe.>˱ B*I@'ÂϏ0sɱXRBguԦR,iL!I)1T gzʲ1}JVKcG Oy b @%/("cqnGFl 궻su5#sCyɳI"?\gk1ѹv#DD" b̧t>[f0L|Kv OE0FKs޽F1fs);M5Mc/>ŧ\[p@iOׯ_sSkr<͊2(5LܒShD/l҃R !:֘澨ób(T7ܼsnN1}ouDP5u*vV-\wcN9ocܗPO<(|e0ƥRD+}&4KQju#ҹ_,ƚ' վۿ%2Ɯ9u8Zk!ֺHXk]u}31ƄBA Ѷ-(K!!Wi>c9y (Dj-,<˳>x}}S?\O"^t;oQהRBHNU >  s9bN1.ӧ @\;ZG)EqzQs׳_'7_ְ @k3y)g)(#" 6dĭ c׮vmL]o+6RfOr*JX-HeL0.d%""RvyҰJ"2ĭXlJ)8/WI{}@Z㽺)ZA>թq<߸q\r}=R,rX.SJm޾sGJ36M<{$ !x=6Zu)%spME Ņ/_r쪑YJaJ)!h4EybZ/z-u](;;ӮB])h3NRJmז>XVML&i](]Ķ]j,uc] IDATr\ȢWRmwk"BΫz\w&qze9%on=E.jtkͅ`Eq )Iv uyC@ 8z]`B&3I"qu˪*_H^G bk3SNX~!x9xAիCsq/Lr1cȘRc9_F<9dBߪc9bHUB_ w*Шgn3A/ęֶHDD)| .GhOFȋ(:4lЌ !8ob2gx8a ?88+V4 "b$.TA<ðKsXxYNsf*/{JD-[I(Ȧ[}ٿ~ƍ6s>@5um/7Jrv΅)gX3ht(e*vh֊ Bz0=0^"r}BJxM_8Ķ\|ns.>T4JW]Rѻ~UMi7ic;sJ${b1䌋G4ʕ1b!D91s!cs~. F?g)V a!c <41Nǣa9kl2w[SU٩zwg2=dN!D|hBD1YN6{"c1E S3cLHYUFb!&p3M~NR=O7{L 93Ί=y1.L1zۦ4}^Qv5"Jľ\͸1bBquG($4p0\kGQu_Q:@ecd7;o|*(S+%K97QN)x2 R!F ATD!gcI2rԸj`2#h4Zk]ltBu}xxsst{Fݝڜ32;wBlwԈk!xcF9Y72S}+)/Q}پ R&{HxF3Qp]-,L#w ΔLJ.?#r)Q;o ASq3N3.;R @3E?AO }>yhյf/F$ct۷cMkT"!0Ug&X)!*)6)] B"|Jݕn/D޹뺪)1瓟r!ΧeݺoW ͅ\7u9_}TB+IPN_EGҕ;tk剷]0ƫv}Ot~q$s%H2Z*/4DZc )fsJ*ERʧtc0 vz[VqTkef8zfxJiDJxeQ_,W1i .h]7 m4tI!4v,]˅Ro5 pG47moRgBk6x|@]U3p~Yқrpֻֻ6c"& %/q_s&*I(W:)U=Tb@BrN1|b<)\N TU&U3~LcyvsRiS)]]jb K'y>fg!rYN7 APre,OB)LG~"ԈgՑ``xG~+pJ6\N9!W~ ſ(ϙwfG1ھ'" u]}E䉪qDRb̨ܠYBrm >4qiR!ĮŪd\6.}k`4jOm68;*S\Ƙnj])ed VEF`2*e__-Oost`l(\VeJR?X !JJB̭s}G 8P)>S~tz] oԕf _c,u]9F1(3 H)(3^vMZ!$!sq6ۮ[-WɸUix%\GD43Am[1 Riٻkۭ@S(SoT])gzyϻq2fJ*ضZX/Ȱ3|+@DNOA&T[{GbihÃw']_Q??O# 9rNBpUi*$vϊ$ggif "rB )`@ʐG%e3w!;O)][|"aU:w';Bn^mv<95I+\E(?TN9.(SN޶qS J݅9ۭ[sƔ6TBjƞViy٬XGTk))hRR BK^[ۊCCX A)B2ONr(嵦9yA(|K;g~z)+eΪy: }~Gn:9dR[w\asNeȴi`<)Ǒ\Rlksӽ9PEnhzM?j'\/NoW` b]SU]K)/}(Ϙ(;9W9bU)%"G[O/D@o|7я>p?Ü_߸{VRw7:R*4 ھ{f`)a4MMS%d<ԦRO1\ ":+K&AJP#w] Fs/"\+~g [Z9@D)s{D,rN!~tPq:mDD&(4͸jFO?<ƙ6`q92 M)rtQ=r%ʱrh4b5i;Mc)%;ǖYS1Fq۶ղV ϖn`!B !DJp4:=9K)"K)T" ~6 ?{jmRa3)RS wӾ]%]7BB)J mϗ1`x=,߃D:S!04,3H.zp8:~wuӯ4j0?ptߨЧ:"2C`r~EDΘT*m M! "H98b2y󙨴kTB2vA$rStNj1[-NPJMͷUzPV$(2K')k6^3<_/;^͏ldkSʰYg "K9Q΂smT]uSbf2CD~V&!c uhΎ>obgBFz=ͅj.s^U|4L&2x)4v]W{:_L㲖UU}pp-g7l6s{Rx^߾}{XTUu`8 GJkuu9pD&ޥSt1vQHSԕR=!S޶g[Dt͛!zCH1 !nc̽uD  '1sJ 2L"PZB9N`~?w!~ z-`SZolp2h2ΎaR|_8gbJ:DyVJ)@$䝕Rf9\vl3Ѧ!BJ1ﺖs^rJw)F"q i84]w蝩Joe3^j%SZ!/s:CL1F^Us.L\ i).3y]U9gJ1DՄONNx281ƚڮm9|cܙ/1|8*<"r.\ȜS.L \!gڊ-6̅Vy  ,Z߯n3Kͅ!z(g@B )_3Fs^IUL  ]))cʌd02=W +ZVn%|=Bs3" 'ݻZUBԵNpU.F-mDޞ{ 1b &kmFi%tF)o2u3jU.OD*խWUc{s> c z4=S _~mh0J@DjJ\Ƙ҇F3^-OF". _Z*ݭGwue3đR{_ݚ6z;ҙ¡nF!d'+];ʻΘ1-Bs(l@'@%lМ%2M a[iiLj${!+ɗ=/ ?/͛nb7v_%@J1zgPm*Oh gq8gB<>N-3`(s9j:do&ʔ0`z 1F L5V㧍׫i rF';6~icbL</Z !T xJw|J.& ڬg؅z!yWu[{c<99). cp0B `[t61yHʻ%~!gJi9]uH S1\6 )GdD=Q9:)pΥ2RU $\>.qV%|@Pڏc KqN#@U \sχD̜"0osj)qNEdY%Pp <cqc y}?1]U *}\$RWU5^CDw=g$ҦV cLI,O9!LUQa9w!,C5ir,rY`DD`RʘnݾsCR߻WlUU !urgwp뺮L]5_)uLp.a B)]vfv2xe,6. )Q&zݦT\W6X'0A♴&sA^"zpBdB !T41jRt]׶-gy#\|7BzaPZfj.e圽s)% _C軵ҍu\chc!/U,,z];_Bƥ1Jy^>Jc*3:/|@)Oːk!" YpfX.gyL)LHJyʼw9'drնb9 @yl )h8B| YNѹ^6U>|{g:lgWή6\΅4ȅsYsfK% e}.A޻Jɪy/$ xƑ"*CM`egWsV7{]ߜu=B)-bDpqPFHXO73;K)~bR5q3o7tv6u 9%RJ1g[i0kS̅MDŃkW֮RNRcjg/xC"!q3G]?ώڶ`-Yons(-Ѩimuhw]e[_FC!z݆_Vtx7 Ku~}mc; ƥ\zqZ7;DBzqcKkzq,-ZIs~^~E9ggv45M# F QH(9)71O/ @bS~*ȡTJgaBEoݪSJUxv})kmf!C!:b1}m7!XĞe,r>E3R ً@p2Tmw Rv")"H1ھ@m*S ׆B9׷y=3ڼ ًg)ı"|T )!ħrX|3|BxN޽guD~@ AX:Eu|ytx86MS\aZ=N&4χhxׯ'U=|;e/[3Pe߭$Z=hu!2me+biLB0=cĆz^2PJ0f 0@Du^M0hi׷Ww??ZW wbRKGb\J}ߏLWהs޸^Եq!C1f!PlKz&C8ZQm3@d-bbx8CJ3EIε7)-2fF^* OQ !7_! z"xF*)/|ie| d촷]ocx|xđlNDuGw6;;;%BH8)bٵ-,[wb0xzuu.^o9B ëOPD*S6c!Owb7 Yj0J{[g{_յ`߀ 콏1p8,I](OzWe*,2~׏z_w?fܨZp4}y!|YxSU)}WV))%眵}ojRRʇƘ֦cu}%"*Bp[k^DD(D5(r0c*U΀; IDAT \.RWyҷHE!TQly4B>`#ls x2]!blqgp81L&c*%kv?_Ld}du۶ulŔqF!y?;g3RL)[B1E#ٔ!R!@+5<4'Lsr1,r/ȉic Tu{} ?=ɧ_y`'~G?cއD31W.Ⱦ.@ |߻^Z9뽇ް,B>kwNH)JpKiLOT!!:k 1Ư:2QBBAH%Rm\& [ӮgBo1CQ:<p.Ԍ˜BΩXe_u}>6CFWyŧR>.Ƒ]R1Fp "BvwNNJiml^s^d2|aޟΤ`4L&~}u!b9 ZD#kLJ.WZTU=N3au5~ ,x~=xgyd}Sx|!@DιR }6 h)Bt}_Q)!+0n RuM}YP YYQ6%Q"M)]yk")T3eum4UZqQk[.{.@iM$ws=}@p#C%{~Y˚fwWw2ɇbk.jBjS,9j1cOL1&\v33u;BT*sHXpGPDPHik9پM1(] .z}P/]}o{[pZ "4c`]i.,f i눬]yy{]Q ɅZrB  _:7\{UUIY+5On~blxhH%]Ls.@,d{{{ 8G8-K1TD>Rk͹*MDiAD!eim-3~&ɺQxtߟhm_bI!bB@WxVBgOJG9 Dy M!y0B7[dms% 4>JgsB !]/߻0M UCg(稫_dbv4?2Us.I,|q&ge{<$|<w4@i9?]ߜN_Ds dfivw7o?gPL1(Ԑ*cvwwEuqJk[ưV)=LRo'ÃÃO>'?p0|: 0d9klZjؙ ߹T}g^>-[J%f6c׫ryGP))x4r=3J9Cd|c9$RvZ?GPp iJkSNtE2P$j4) }qu&Dc)xHR)?hgqsN)~ᇌqq Ӽ6srJsd ""m{Md4&JYeYv֞t2M&l Hdð)UllȆ(?ٰaCOh2`Dju]s1|UY|H =wi \ozp.HM7N<+߻9 rdî'gg꛳@p:g$D) mۚgM.I3JC0GX_!@2Q*yx%5F{DB)}MחuaA%2J EB9WU&R~:b xӏBH(e!9\sتJJ)@01[g9%c)W}ֶu_zU%s }8XsvL| c1D]tuF^l6mW_^-ϛ麞1^j\.?я>s.cl<s_l^RJ)EBsЗeyzzyQd 9"CdHU ;W'E b0 w~oQƧ]^W2rCE1q·\Cpѐޔ>7nG#JiD^Td)# % J bc c7Mypx"r8•bb OONтk7rTYn6euW㜌;;]UU_Ycwo~skk+|t1et^o6evI̦,MUx/fSMhkk:CXknt6NNOΗK5%((Fň2˲,>MYrx<\|*iRt2(/TycL/W5"0NFQV քdP2M|4?tݹsgN]WeY}Ϲ(|6$i*U]ߵ]׵m[7^p1L !Ѻm㓦i)uSkm| 4ڜkc|2EeEH*< [k$8MƊI}sV{sh9ڃKǣu#Sʒ41^'.(asFgΏt %q‰>8u{!dqCQ. _ àH0IS.ă)D$)c{ `$ZrM̄U߉.rZLs3(>%"2μs@0Mf"~h\2&)s>%ɛ)>[/ s)8wny~Y)R.Q8IJH&)(\k٬ c4hćдr:>9szv<@|4@#!a4ʆa888h&Muݴ{^7e Z뺮vGEv\ )-^׵u]]Ѧ*5Xkϗ˕1:kl6%H(r^VeUw]7hM()ط{cL]}?8犼!M\.AT ٔw uCh<87]VeE)B@lN%vUm7Gu]-o_qXetL=[j}$H4XL12^ֺ GIz}zrzrrҶm۲,4$9Wj3F0X(3JC۔.)B$_H8Г@Re H)`-t,Wvvw% |OvdaYxVc !+BQ)] '(Ev!D2:з(˯"*D}ZcN*)TY$KVBH1F==x*T:NcΚahՈ, ysn11R>π`CWUz7KJK5oeEcT(xE<ARm{\(KӦ2zxx<?>>YVp*mwxtBZ,B>[n)Ã4>>>OYe]ם 7M5 CuyYc~]_ɸ(rBPg֫NNN{lZه_YV`}.8>{O(3y6OOOVgx'\}t37ͳٚ-9{u2?9f,˛e3ښI`g жrSnh4$N>:>;cd2UUn{f7n,W7_}Xdi)KDԎ \<>9M{υx0 xWwVwˣo~s!DЃ/FU2N!g} dBDJ28fL\sAcG$}BJ!>(a!fiQu̹Yx0+mHHH k IDAT{gzktVLqO`7Fk1B,AFzb7>q:"Q(Ɣ=-ZR3t}W9kB& 95ubJkm_BLk;6'PDF)rۅ(0w]/eIrw^]&v6ժW_}۶ ! !Bd9/jٜmomͦãXkWzͦUUu]4 c,4MT{ƍ!ż?itxt%4$ӟVuK+7od:Z~J)= ׿>޽{rv>h?8 nڶ(7^ye>ņj0 19==m|[ZlI4)˪֛jB8ڶ, k`(w]r۶gjZV+t+ÍW_.&8߽F93 f:m׭f)dzWr{gC NI "eNOOϖrd7EǕk,x=w +{ȑ8o5_i|}u] <qڮ̥0cX)sH""aMwB7|Y!ҘGҎ3 %BZսw2,/yuUZ!8g^s>8/8'x!ݵуmeŌqA#m;۷fyWI΄䌯7؅Lu {GD.&;Ҷ)!{> ?v/(\dY^M4eY @)/feY)s:BRulmJ)cy.-vТ(^qcXF|IKH!gh庅xZ}pd"Gn:yEdFD:+<B8 ~ӌypsOA}d 뜵N!fSb6b"JxB~ !8kF;OT2N=þ1Z "rTSDžPLP!"c6A/΅.9I,IJB0B"!䤮")S>BkPRl6kUCe%*qޕ&˲,ˆof޹m1THvժgk:M@'H|''U]3W"wT3P*O߿s|6ϲ 83pC0&WY2ٞKo%.};o!d6MvfuU󣟵VGx|t||Νons!{rBXwѲ8 !?V5{e/<#B4g x筡 pڻ}F#֚I p5ux ;qOw:mu3Og \[Zu}۶B ԃn&|v\З@$O>F)mz]RjzcH)4/}{)d+~sUV~)lom1g/FD@묵jg׳㓓grv&I8λifb>Lg~?g( )`?VGGu:RF^Eib]WwiZ iF~[pZݻ9'f9_w]{je?_}ԣ0eEOMYhblO{eiBJYQXkQ>*4̦\=\PJŨͳûw'W1Fͦ#!'eUp3}&d29== !)?899ITdDr !ܽ{oSz "gsκ]ng-1HN?luiʿRΈH9{uruܴi( bֺO$#H-w:k#ќ !.l]``yM bZ"0Xdb &l?mqOn3C[B/U{d+jAkm!TK^'c;VJ)Lu3t5Z&9TP)>v_wuVwRqԃg aZK)оt5VR>(l$IKYi}FȱZZk*k=^_W/.AĢ(BGǧE^ܸd 1u]&BBf3!DZڀ(ٱ޽w,:5&Ͳ%Q%ÅqBDX۹zVG}Te!2뵵R"QQ,x\ooG%?ÏBy)R4Ͳ<pжt2VJ )@!Φַ1?I2LRJ޻Vn,2J)0'C|vӓ bkt1.??)ˍduќL(97wƓd:kڃWJ}6 Wfi^yw~f! !eUGӳC4ۄKffZ˲,K+!V}ggCR $4]OO?_9)սnͿs|a3!7wʲ6&elxsos)%f0qJOFcRL))xL4 L$I.F6&4=?u]_h/dW)@ }TZpYlnSR>,!!HBXk뺉S4\+F֘~jH$p<&OLVLO 1 *XcpQ(|g.iOA$HRm'kim]E} 1+B`&/덵vMYGEeWR.,l6@"2JsuUK<@eR$Q٬z-MiPH$I $yWb%x7Pp4*^?* cL:D|׳4],\Jk?L4*c;r==h=FE!R|_Ϧ}?xe9t8%78/p{B>8rɈkCVLTOQxĨBkݶ]sUHBsƘiM[ CoKLr({2dduMT{ƅi|?FHBhn"ʘK.wO T  d,ժ]'\O/4!H)ֺZm̦, !I1N1wMTe1%㬍I(%J<˄R'Q2S)Rh\j:|>q2F(mYN&YK.qc1It|P*#23drpt4ц &%G ɍ"NOΪRS($I]7m+J~0<5f:0J"BXz6A*9azgzO )`rbѻ`eltKT$\1"]~(<ד$RrIA W,IT^t2N^ʺ{m8έB(za.sBu.I\Oxvf.gwOtd7;C5M^Ky9/-0!8B(! )i:O"Ha S1; )ZK~KJ:ksRJ1.\ p;1Ip:iGPXWl{u7-u_V,d]c~>R׈NȐ [Cu]Eg|܏;u?$&{B;kf]WK)S򓾊"@:>OQ_rL>Dm=uiB yVA['(/bT/+^hdYVZA몪$j2+ywF'RYj>h)":%pʲRJd2qƮ ̄#@A)u1BdRR99*I39y1¨;o b'fC躭 !~-wKvxBp!,3ƨ1164 !\9rOBRe! MJ)}uL0;0@`9vه}5w0t ͽֵG!G|k`kӳt'{(PW*HB<aLҾ6ϓ$8G1u_`8:J=4 >XkaQr("Pm?8z2+7o't;;R_-9:cLUy.>ǥ r)1àD !mDL2..0-R*!=Q}!88G!֚㢷׮=%[0Bz.gR~kx%/Zk_NG>/}.} )]=~?o휳Ɣr].2BD$ž Q>O(QL5q(PA(1gB>`Wo|a(.= J8DM_Vg\ rZ;8zm"\!:7@Pvs$HG!aH[pi(…X~FkV~j<ȯBO?yc5Gβvusz3eZi-I|eLr$_?)eyy#Of/I%!(J])'uz 1r7Qc¥0G&x$xs E$HTv1eUǕ~^߹KBc,ۮI,Ϸ(I:=ȉ&=nk;c }[.l:ey1p~=F?כB:)O#~ "`$)६tH4KSJIOJnmm;=x4H\. A2QU5ٞc_Iס4-liuw8\Ku/GֺME:TNaoqӧ.Usu#7E6ͭ78arAeob̅YnYZZmer[f:c5ھ|!Ra>jHh 04B0F q~,·ڽ=!Wؗ OB3hFcTcY5]f'T%Pc< ߵe[oPQBɂ ؓVR"zφ8e2I %$xR^_<^|ȹL&m-Wi6!@qa3YGE׵QQrrzd:ei$IyQJs]@O|p@ FwFw BYb!`C~wo$-k| ,?0{i}!G)Glp۵mP-=@$0PL01:o>C6vr`u{B\·/B>ٟ>8;_I]ؖz BCHWB/֛/U"Mι8|=? 9|S6jWRJRbk;)0O0Wptn_0f0oH2)AFj16xaŋ^wYk0hDR}J"0z:k J4\$?:-Fs1ƅ*}gAJhb&Ż xQ9Rrb /<~5 wr$-ⴗ@E !n^s—\t1#⦬IV+t:y.;5><>e\QQloͦh4*1V>w.A䌹 & T..Ck}~mctwC?8Y?uq=@۵%$R3֮_f -[AC5][r.'}䌉v]]mVG1I )F\]uR IDAT Kxa;I$I">f_lN`RFx1Sv_p|9b|(4f3.o.9zօyQL&ӲjڶuެWcl1kNy<^7*xjsHJ`m0!OdZݘĉY`EVy#T.Q .Gu]Ľg BfmZFAQ "6G/"AM ɵ,^ʐ<ϳ,wsT<{L&J )/:2,7(eltfZc ` rBa{!sq9RC>:=ƺ +fRR匉G)!y3}WAL*%d¸{?Bk-wཱིF @B0J\.zCH94*%(B^ĄD'M܅$aBPn޼y֭ӓc]t0 KtڴsMyvvXgwlj2fygמi16zs.U>n{h*rJ3DB){r e~{}IR+&H"`m(|!/@;gL=rH@tfExKI wν$Q4PVJ4˒ie><=X eAk,1/H) !4GƘ!kzK*{պ7x3!32*4:kʬ*]CD)e&J6?#)_bA'& w߻!iWo_Ie/_fCK!rymkLUUà'~@Ӊ;b$I`5Q<"糡뺱֥iڛT.ϥʹX|{3OT+6e|2!U9t1J'$$CF?W ,g&CS̓$y~⤆H~pAs=>U}m46es! ✃A9)$ b3?uTt{p 3}zLl(%JJ}N2µHֺZ:Dc2rHf롫žOsx((IB]6Q4^_SHI͕JΜ!s.2|"aL $5fZ޾zUUKleRGENϗD%nݤF?~RYG %g>4MvHJ8W*4Ւƹ"yֲOG9h 2BDI)k}kOGT &x|O^(tPDs6N [/=^@c ܽe)cV1]pB+|.|Rz3@Uݘ !Ƕ& AF0Z|c`u6'I6JұVϿB{u6Ф٨/% u Ĥ0 Yk,TkJJ#0JS)o';cKqJ+k1}3%O/$j1c!&Tpv}? Ww!b1Eד4=::F$ɴm%Jong({ɴ@זFkco}cBhdEWz4^L*I|V(x (eM}~BoLdx qB5eRY&UB?úb6o.ҨքXۑYM? OyúMMtgY;){j".DX~(oׄd'&*PO6ˣo)vVXse~޻m)%yM&xAǗal$\I72ʑKpΣ aHT%š4\lSJ?N:_,$DҶʪ > Fc+F)2uz cޜp o;dfѝ[d4D$!ژad=FhAՔ#$o\Wxy]wG;;?ֶC4Q20yNcx b@oޛ6IWbw}[, á4#ƴ gllHfh4HA#c`  ltuזkDnp#22t,xク^ω#EBjc63k2omo42[_b̲Upq:7& <<,Z~vwk^Hnhg}2,MEͮ[:fJRBʖu3.6+f bVmQSc+0sI, @ Zz2r41ww!N20s231=F% ?szW197Τ\U11G?DpLszfN&!Ľ}X|/w>̌BH!mޜ97B\w?)b)_*OQ" Bhc`܅n )Z Jī!Y[|?s)Tc'm Sd1lr$y' þMY !VL&leM)4ajbJJuEM(FQ_?rLă==Lnm~*EiO`!˃Ӣ^ {rܿ.^)}p=EomVl*mxa_>i;Bx}[!:][kRZ.(Sz+X#F8**,:!P\m!&y]Byf) +bb<(AL+,۶CDFRDͱ˟" |QJkOd浻10+! qx> RX so<5g!2% B(U ǧ0q_6Q\ kÃ`V.ZCJt<.˜[IL޲vOg4f~]JB(*݈C`ڑy aܻD(؞wœ򐦘aЯjR7ucRC)?k*~!HU='[ZCa5t l.;2BH!OnJ%Ɯ!Tqn;MSIy/~?!;OEQ @J8\(Oq!ֆ(kܾX,I#_bfN)+⺱Kq4JLUg~AQ)űoR N*}A~Qɜ(E#sDD VqנpF]usԬF9~O~_uեU5|)f ǿ';;nH5N3D0v0EIBBH\bЇ]1.ZE!pȯ]|徽:e(v9=Uc_l;u=q!R"}`h}T,VYO8:жv-vlLf[֖J]|1Bmbj]o5ͩQ)D䭲ԵpYF.T6Ϙawwwݪm~Qi=ͤRR 1x1ƪ*7778,c]1֦A0UEnhn]g"G'xfAwpVޕ(FVR)%T*"ZH! Ð"!ÂLU\mNs6nhh\OL;Ű'?~Η/qAEIJnS߮E܈D1EωL3w R*>^iM1@)0o5 (%-ޖJib"J(st@Rw6O{ 86M!8?DZ3CLR1¥2UE?H!d^Ts[N-ϰC΍@YQky#@R@<K+wg'$⺄>۷nV/'OWU؟L'w)ЮCDCCN>qv޷vt@k.')DYMT|Q͕L Jϴ?ԓi a HT]9T\:"*%X"c`p8|kpч*aՄ%Ohu'HP*Әww~?+ZרvAmֺʨZUm̿2ПqDc꺮:m6+Ow]Ziv)z)E)-=q 1ʖՋ{q&I40mَJiBa-z~b~Xa}:m%9L7&\ݿ|hUrVFJ8jz ̯Gdȫ8qt00RZz>GȶeZxn/,K-``D)|(I)*3#2Ks !@D9bp} cޘb2۪'6ROveWOee~qϧ=)Sȹd+PRx7dvlYKG&1aӓ( #Ɨ_MI]WFvrh\~ \|>;k'O?y&Ǐ66%GdTU94QHuSpJ>6 12s-|\lonSmV\Ӣ[B1":L)aC(˲,Qr%ŠaH)E1pp0(LKb"Eg$hwC_6S eR{mV:'ZkS.ڽN0\OEYm6/C)F7clU]՛]b,S61FZ߅-67f<]mQX,1j~GaM))"f X`)6]cp#J}{k4b}`@!?VqLĄ g?pmNNmp"$!RZWqٗ>,NU?  \ZkR.BhpVYJrGbLgCr޹9`Zi TJ1=F2Sێ]YɴPZ3sLb 6\S };Ji RRʒ~Jv03s!B[Un6ΨR nIQJy}QkRC d`? "rYW6>Rb0O0Q/((Āp 1Ba3QLi׃BHхlInN&1䅈EQqN33 0ڜj&(_zr_Q~lHhm(vy UD(P)2$A`)@R]VWSJ]xr"6h_;9Ί+4 1Ys+3s}߇f[99oLq*٬\ -"llݩ%1:))p檐S5RI"z2gXճfUskk)5ov\Jq,ʪ4V:RJcVZ*)L1QY{)<1@0M)cTZ٢2EJ+sZSc>=yIJ] Z<!1{}.{GXi1Z"?1&Ν)%)5&˓RR 1Lʢ쳔(UJz.@d8"CE? /GD䆖"^a$CG9A8B L*\  B9K<_b9.rx>" ! 1;3qdiK1X3[L6oYhaU>Ƅ{T')i9nڮ/Eλ!Ũm*.//x7X[٢n&<E;ɂ`&9wU=7RdMvCD dQ6EY_|O3sPZ#R6x#'(!{bV!V9cJDJOrL}0Ɣ TJޜM3+ ZL9bK%ҋ*y2'(SyJ[MS_:OJC-Z1hbg 3A8\b 0َ.@L1ЈW*c5@`RJ`Nc"!Cr;{r>֪(vT(q}o<.$`)̘ig,G{2xZim B&Ҽw͛I!R!@ cNJe˦̍LO|5T" r~10abptkqזMUZL;<8NJnfJk82g6QS >s'9+&hcB1%fRu5+ [Ug (P(WрZnFIԵ>Ѧ8CݜWof6u}{ JTDBjOS 18%eĞ|{3, i^"1ǘ$"9qwJ(E?eny2P<~˿hεᬘMi |OFjxF'<'TJ*mX_xg移{O6WsdN)QJYlb~GB&*%J^rOX QeJJ !0+GCߎuL˺Q/ٙ)ƱTO7/~ jBAoe! IDATb1n0b`mQ6d()5)l'fN)x8b Jy2{ fǑ̑V^\ë1:7|ΓA~X`JΤ b}J 5ǘbZSU,2(7 7.)eQs8SyuY1yfۑom;:GDJ(ݽ{֭DʢPBGʤnmmg%sɍdG?UnbnPʜrrxS)/o ks8B4C#)J%Bi݆/?/ߖVsWz`,7')(pRN%i9eu1~Z!{c1DtD~X !~2Zj9C(S׎!$% |vwf26=_9YOJH!P(%S 9!fDa&Vζ[-/ bɻoCe3iQׇ =DJIi[?`7OK~vpݽlnmgb$qt)E%PJ|kNF)h-˳.U5Z~SftxDg[o&wܙN'0cx!&ͽs.}Q޽Y(wRHcl]W+mx]L"8A)Un{wyZ@ X'XO18>"Ku "NdS@6!I)>x]η~Y5~g7o} CBHVBKxo> >"N[cpιR"[Xc 2qN+n[_<z_UfsumWokR R)[/3;7C"{^ū#}laxXc뢜lkvC֍} -j-n'% R+)0=7"`dnbb ڔRGRv?zB).{nbZ]ebyfaaັ@CX(Ÿ́x|wï?ڬx_JS !F\'L,5ҧ+df[ƙ)0jicөWl)FCu,=뚺Ʒo߾;}GֺUJ{t0{7(2 -qpneU׳esF3^g~ѭvR|]#e\VZ}lOI_,HF {vO>˺9)kvֵFJ()TK!D$]sI1 )=^UۿSL)9$b/m(6Dd.2\./yzptok- n.R ~|*ws-זrU;7RrF˺[VӢʹEQE\jҢo B֝")) ^XhuE]ub" QO&>BRI#L]!t:9&i6=~hG71Z+jmTʍw xJMɔ gJy&~ܰJnBXkVR*bl@.,,@!090OXJq!3RF]sqB3kED^9b 1hBJ%C._.?PKlzi 1j7B"f53"CI% wscYƔߞDmmmm< !fvt3cp@L10'cLF>D)\ѽ;ڲ(wZmiɝ;wʢ}@Dtp}6ևW#"`Z'LT0wc-b!'go޺YBX^D3Cg"%Uz|Vm e/.xqnq#|6_{=Ѽo'"#@(u~Xw0/&Vesk[_V* C܇OnӼ;~skKƻRJ)^*֪7묵S-^y+9[8%[zs*M1lxuzRSy&灈JY)zDPR+cBꬦ Z7vCnemYD1oW=LUTjn9 Dĉ-m"0X旚^2q]ēb^7xܘЧiaDl[.Wө8 }PJYU[uUWU~j޾%RCOI[2s@TRNN!TF"].AkbS3?Bzwm\(A V+G6{=Q Nd!]LQTDb !ᓧOvwWà12RIiUU_٘e6o>k _amHY%!$BȮWk`^ZBmUU)ʒx{9LU=Q/zRbJDĔ01 !O.0GF6Qn ]hSVM&J1zߏo i)ť˝-ֻwݾDV\Ľ-,"tC|a;3 u,us0bk{{tѣEQBknYU{{]MSc4S z'(T\RDquÔlQS_'BB-(LV=m0[T28d }A9 D Pqi!JRp1˙[%夘GG_=^>2ʾ;w_/?$J?/ `1.V;G{-|:5R 766Te"S^Jy P(IƘtVU5V#b&s}n.ۛ d"Nfl֓͢(mTCCзǻ!Ec^Ϟny lf3,67v6dD_&S6gsd~CL)P{~ Ph%=8PdA4y50309ff")Dc/m}>3 =/w#)[_(Y)%'ǁ kR2Y+q x%sx\y""^ډ P3% 1Zciu}dlx@DG RI."b"8;Oѧd"O?$#% bU(yw%WErГbȂRSe5ECƾV΍5D|q臢,~U"JD|MּHbzr}k)pğ[gfJ$>9g7)KT1bB>Zɺ8,Xc-Ĝ˗΍MSOjC 1>|Hk5N^P ЯgFB1x}ѣGVye>"Ld$lZKP "f }6F? ! Egx}~g]|dR#7s/e1@003qzŦ*Q_]Yhkz|7~o7|K%'?~XJiVJi1vwRvxݗtHD)D!U%(Q1bBXk1A?ćvIL713)S4Ӎ~!sl)=wGB*y'(eZ6-[T((4 2sLcc7wwc >E7㫫"?~DƖUQs)ݬasc2XcQ*ُEIym RVgϡJaԕ]u000;/YH*QpړD$q@fk6hJk?)J YV5*$RcQR]FBDwsr c R\(sVg%XleB&)Iwf&.XozK63UyD̳d,r`fb`Η>/>1{-ѺJ%U*{wߟ4+YŽ\\nt)EȦ:~TTB=ɣfUF)TJܸvnlEu龇U{gֱK@ Ɣ˪)i̫fj6jbl)ĔRwطC8t ޻Q9OhCVXnU3ϲd/:*F? mOS"eJ]TYFY3'(ŹݩG B0RJβ4(0D:y9W!D V,WwwkWv4N'>yb̻n]^Y#񿈙=1DDkl61{ZdRqF*pEQsÓݪi+~R,)vycSrB"jl9۸:]6ߧwmZiB!cL="P*)' g %wȳLP s[M&E2.)[]%()aѶևv`[loi|!Fwnze !"B>y%d~Q_G)Һ(˲(>13JZ+/AW7˝RkrqOfgWPR)ND)輋~Rp1EĔR*V*kѴ6[(򴐛jդDw](6ܼ8GwcFlER(T1ȔwNm?ξyŘ]˲H֒B!)FBn"rSDVZR)%0$E Ibo @L80 01%MOsCY6Oanj-D(s]?y"[90ͲNgJ 7zR!d!Ā)o6)w+47$:cBB0$X\UY@J@tGI]5q" RA TBI)>x CUJҪG1Vtb^M48tD7Fx)ONx82-*˲4Z?>B `( m˲47˝b nt}rmnVзnR $q-Z\R [TE9)Yf=*-*m 9cn6ҏCJ!OD)Яվlͼ&Z3 ؃WsC 禗2yR:SzenapΣd)N"|/j#`.%@|TQgG\JpTM=c !(|` n-}cY㨓(1S.ƔRxׂB\p٨%৭!nK)Dl|KƔ~Aϓ}؜N߾w7K/R*c svcL(U1Ff<wϊB+r?xS>'JXm[Nsbֻ5!cT18G]g/rz&ER11cXIR (1w)7- )Os'qW}{Tl.Je}s8ه] (W^|y4_QI]URMi:;9~k#d\J)qMyk}rb 1;;iMrt"sh4pӧO(FJE7b4Mk飏ղ:K.nJk'B)BРLSl!FyFrjNXtK542\P%qýWU)V$Dlղm;&O&yh?SsaBflCf%#s!xdPJJ{N?R{w:TeiԵEk&~dزZýGƖ| nlW֭BkHIS+V\BjB2kbKLGG=s)cE3߼cluy\!_1 +eM"JKclњh|,laOΞjJ*SL]ѫ][h)MdRLzD,BJ411)v ^M IDAT ,fTsRs7 Wf(wp.oD!DQJ)D&O$cIck\|8\m?׽躮776at=q>j;M+>!]/MS<(ePEٸa>"jc-_{2[Rʾ[Gk%6RHRއAm (*& <$,^cɢ3Ŗj;" ),<]=vg5s/ڝN 37B6Mr>@L44M]cwuBzsxǰ ڢ,ˉ6&h%.%D٨an0PMu)綁sJAB Pwe(Bb9G?EGLR,(~)ЎC;mJAZ˙ ]aGc̋&,MlmF礔)Q\nk%C\L);9^]kb&R sQ D }b!Db(yBH)TXa6s}O1æBh}xǣs)?y5Zݹs;%:<# PQ)R/t&<"_EX3mI-U_jß?q?{{6*w|~xZ|coX[nmm&ٴs*/uDQh'f~XQ np΅,l]Ә=LDȢT/D7LD;!-Y K?lǮ$Mg$c`FfVPBI>A]@!}讖!"c`^twzDX0"x=1۶k)]U1ی{ 9ۥbc 8%9\q(I qis؈/pj~ҷ+Ȳ=7~sEs1.~1S]F35s!x.Y/nr[CIv?۴Uʽ=/ҪmܞcwZkZg>Ii9qsNYi iFŔ]E@ sv']kzM)d]׹m1ƶk~c`,r!HD\jB"exÁtwPy ._uG"Db)SJHbm,߿σi+J>XW 28bS^UZz'E4Μ6g_}6Y{vڜ̚yk[OٳٟCׇ3d@ijfUp<ȷI@6/|S9 þ5wy(JHy$_qN#@JM;;# ^b)x۬x@g֘f57}/.Y]-lݞe sscN >^8۾黵׶o3޻}Xk?\3a&OW;gv6YxԕP1_< r qN[I!\Nb!|zDZ z~> 9xY 96sk>DιweܱsJh5}׵z}&@G!J )k'cmn5b!msڮ넔A-8rSBη-e%@,6^D~BPť/! Dl mQRJcLo%&ΥRّxfR>duÀ)6e1 )8kRJ!R@}&ze?gbxpٽY狢J)qso^bl謱B Km }1mۮM4os)^b}zg9-s\*-UtAY jXTz۷]X/YL4ZM'X Չ]>o3-t=RKcQ 1 b>!|[!I,91p0Te- o9:>9}\c`ѻY;\Ӵ{BR)8l':/tD.eH<\'uݩuB,˼=~;;Ƙ|>M-Vhⴎܾ4cھ[bB D&X1g_Mw)KDJUh{kApF22BuJlZEF 䕿%F{bxbl L> =*^szw>ܯѸѧߓ9|0? !Osjי.+g1zBBZ[hsY7OfR~WջZkwe-8@ BH+[k}fnEBθǨb1,& l w}p=!" " d &RљmmچT1JߥK(0$JŘWJ2%}C3`y} UTJL]GbpfOVi Rl܎듲rBʬpBzs5hg:1>b4u]0 "bI)wv&]=~_9Z״.3SoxYWkt2k"HLcPvׂs]VJ(o2o~8e=d@gG׋ω@ IB^gX.8ެVkZ,( K3F$@J,$qVj|s!HAa1ɵս_???wK,`?/ۿ:`ODeU '޴iml Bev]bph?}z4ÑTo~H)"-{,zK]ޞ뻵PU.vG1n9o{L;\WsDRInƶ떳zQVO)"bwڭ;ދg>yc !dJ):6t>g+kFZGNѢ#wg6\M1ZkP הz= k8q]kMBJ)x7߿3ݙN',jʼnK)ۨÇ< ޿wÏNcXIW=_oˏ&A!e)Gۯo7q2ys31,ʢ(tfŎ0t|1 dJ{6mol8ILbr0euS_;ۮũođ2'J6%rPTòJXޙM.YY5}9zobpEd_3ĥM\Οw͒Eu H&g-et9P(˲,wXg R:1qav ~UwyHۓKz9 N,5K eQ1ƆAUeY=~,Q~۶'gu]ڶ++Yu9Dߪ ! ]m"pڬΖ! .dQ&q$c@~z]͹,K5Iᜳrɾf"Ph-Jb,%S1jP?[\`R>L1q"5Pp9fkP;STBO'`xaoE/C&TVL˲]֫P0)8[v]s{KRJH靳 nH/sZ(֭5;]>xX M\LBŒb)Bg P81b1ݺo1{\HR'г/>gzuI)3ŲWQh`}pyRȢ(R%-\ &TGJBIzJ;72-q+|W'0diO1,~qrGqӶϏ!,kظ )x2L!b1 >Mg"h ۾@!/po 8}"WR}tc71&4gifGp*RIXk(^&{"QZ-|f)F0bB]}J)8W9ѳgٝz::F2S@˯vݫJJY rk]WBJN9 q7q OJXYBz? u4Ƙq!OɱzƸqc9#|rZw#S}6bɚR}c՚cLRB)y1Fk-wͧ#u]R.˥71V1Bwx J H!U^ clcqW)B#x^7˥ಪs Ήmձ!FCNZzB N~bBY  qq!1VHMF/#7*j~,,|V%3H] 19nbbS*R\)7}o:c"7hX`]WG36+yk[RZM&Z)yGE jdҺe}`e&r8,Z'΅ewiއ9R8uciâoq55ř:@R|p~1WBfq1^b,Ɛ=OC1}>2glp;c֙.@e=e}]B)sϽYxV)P9kBXAi]ו⚾#v~8D߅DV'&Bޕq)"rJoFz]bj4b^E @Ժ}?7{ƘRpAQLmXT+3X)%$!tBHU|] AM_~cD\JޙqyM"w{,!0ylTCl>OR(pLU144ƴMm  pDq@J /?} ` >v$ 9BJA XJ)ʲ\7XJֽ1]Qo )?}}I{<>~#,u>>9N'u]11yr:7_[ۃ#Q^Ve^DD@I oQČ1cYf݅|@Tcyۛᠪ+%ğm:H)zWzBdvֺZ6+]c툐)f;u7gRO>k쌈>}b;grx}^9'b4Ƚyczg]JLJQ%g`0H+]bڵZJ=[ӭ3@EyEO.ʃ9D@ɳ?rͼ%h 3ROyX71n# "?nO8_PB>?r„}ts[;wG)އmY ד5׶oK΅T8ctΥĘ"!@&J;S}_ʪܲs.=9kp~-x^o "|f)tcYBm|SV*gϞ=},(|H 1iL d4lo{??=;އ5Wsض1)쌍\Vi.׋c!*rcŲUֻoלX-N׋ي 8q-'daIU?fyuUptz䎎}}T4OҤbeZMCmJE_F"B$ `K)b1_O1X{H.9.~ʫ"w2R}[bd M'㯗_%`&GPNp2/m7_,NNNaY^b[c{FH93Į륒J)Od7}#&,t,ΞX&!P.Y?ȈPrjW6קbv"bH1K.ʟ~0ɓT!coEXL鋣?s/٫"3b0&F}pH0`Ȅ`(H)Tqy̝/'_(Yp'чAѧf8c5\Ҧ5Ytp1:kΆ!]-TE5l)%o8/aaQi[x|mz>BƄ(dV }Sb{DBLGyL1+v2Ʋڵ1&ą9qxξ:! #E^i1VYn:m_]-DBJ}/WY}/#8ZWD9Cp!X17ij/)!b5B8gvH/>\(XsaM!΅P٩'x׭jNsEm/+{I]wwYR ޹pj0{|R?~]\w_>u ` R)Fc 1KWfuU,"ma!"]\!*-w&wi{p6c٨՞_|qż/ D Qj0J~3 }cRLH]EYV)w\ߝODTRޤsss?Z$7s=A60ftί^f@CaceCB@|ܨ=ⲟ {sH>x׫!aqZIiBuW_ӝ)fEcƤ{)|nCU׃s2KƔsk;k8c9}R!Mb=o,*_& ՘ F;޹Gȴkw(?Z]L r8EicB-2a3@J̻h)"%1Y!>j% 1]06֯rVu6bخlq:ۖ?wXuYi~Y?#=*Frnff_~ſ$꽟LW~qwQ|!$DݯKoJ)t]I)ɍa1 !uQ޸vS"η~|pC뻦QZ#+S8l'b,RkyYBJ1BJQ '")$s ,%C:b% UUfח.Ȑϛ> @V;9Qm[nq(r2 zml6eUZ3ƈ`PWUuM1fF!}Y)By뜵6Ȁ*w2Xbq"N1J}X qqY6TQmzkK]0`{7Z3]Y eE]:<}%`հ',aDbypMM4Ư[>lp>|TNŘB`1$s36@!4]X2ƈdt:Y ?pgSIw<)C]+'ջ](ĿW@D)i8D,pY~̔Ry.q.@s!.ʹѷ[wT,`1FDZBc Z9ӍI9g=0Bh%RUsMN} 7o2<ƈ<%L )x.ĨkzeDceu;~c>cNb㜨ޮsu]Ji\~9dUY޿w轟9;==\()'׸l\Q!$!ۃ\9Z(*CcZƠNETs1}Pr=FrfMG\THz~8HH}JRsoHQ{'c;M 81-Vl<*%碱؛|P|Y-eYcz~r=_yƣѨ()s;.x{?5Xo $1"Ÿ8'EYNR`߯sVo&,4',{zĀq!MgVb̚t "wB#J%˪֚Gdh]^Ę\my"_`0@|_o1, 2)p9G$@)5Wq}snZ q:{%0}rNY__{w\Xߵ}( UT]2TnזH)`sQa4ݻBJO!bD߲^]:ܧAdYv5mwM6RD!JQU`YP؛A' F['gώáL7XcMk/OGd8NvM= R9"EX^&6j-^Xӛާ& )%116DV1 ÚkW*\BV(">xct6Ku&AnGCu!\۶Y nOmCZpC "cRz^6߃pݼS.xQ{mlsR.yS6?فvV"Wx?>=3z۶~gbv&PT(1&hw]Nȳ O[81F1ϗH !P 'BƯ4m׬t9>/sNDgY>YF%ĢEYh]i啧6;ԆV"O+R.I1*A@΅s;/=7V8% E Yobpmfy~|zp1ƦibS!֚HojX9颈)m=@)ԕk׌Q5P@?ąPZ4k 4 $Ul{h:\:uΥ:4|t@cH1%˛_ B>8LާQ дS\Ol*Qst6;].w*Tb4?|p6k\ݻ2{ާcs}ޘvJ!e)9'8$.i.zo5;ܣ<.)@)R:cz萰( )RRqk,ZcdR)@ $\9+8Ǧ8& ܚ#(ڻ2>w" b s{c5 ɥBpgϏBB7p4 q#0XUr\8s'BdwdV6R{s صmvR))*k0Y/aQMwE52}6kTJ3QC}=:T/:_cqPwGB. ( u۝O<߯}s1vn֒Įb ޙj(^pw}8]AJEčq9ƘR ym3۶ .PRᶿsCCQwg$co( .I~e}8WFy. !B 1M1DK)2\](\!h4wxO1 ʲZfg(L Q 1Vkkjz8%3*%s:5UBB!xofZүHj0M]9%EÈDY 6gNvFReHC`w0T*>?B)ª:}41FuY_YwoۣSo~=:O"􅱖14vdr6Ãx,^MnUGf͐;VxUEsmxDD_Hw= }bIUHp~5\U"HFR15BD,8H*9 D_N#` s bN+M[U 6YXN孌U!^[X8Qpxppj0H=}ZtQd]gFCo6,G!,ji:#'zmݔ1I92fBvW>$oXΚ^J(XҦ*hM9Jam ({)zoRbܝ=yLŲ<1_|9.ss|~?Bk>8<{dZkgeox8bg2 㺮joo%+R =@cԨբ5Uog+ re}3EYU R!D\Whh]ΙY2Ɣ*f@ĢJoh1ZzcMo5EQZP{뜱HJyV!Fuq)k[7x M!f$B$& ;%. }\F$T;=ק EӶ!)%!Q9Aw\\wByRkHý+%u5y`j~<O/RJԃn^4jqRNv~<7:Rz''ه!&߭~S )ƑV&糎3Z?dUra 6!k^sN{_짟?~nj1GDThP8{/O:^r䃏?<,d!`y>~_=J(S- Yv^) s3)F3\Jm4pV'u]9bPE R wIDH^QcƺM syf)5&")e >x,rBp.dnMvRWoL!ER4l&Ddl)өrnT*|u1`):爓*xbtݚ 9Lwvp6D($*xבּ_OP@&X˜5sJgk!\H!Ud6`bg"v&~R1BJ"NK.W@;4͖+7*P !dd>ww]_h>~dN5I9!1@7:? ūDQwӬ^轗J!D%a,6]՚kTQKUޤޔ.BD}p$ɂ[l$|.k9SJ9neo._c!DLɚBBr.rO| }"ʢΚl0Uck]Q>3H{cYW|8Ij坓/y-;R" ֦hOqvt8˳>X Q /}*]pM˚ ZvjY.u>x;w:|ч?_%Ѫ_?/gףq_=ۿ~׻դEGϫxt;``KO)x >|x٫RG$U %s" ~FJ{gi692#Y[ Ѧ+hWmvErx֟B1ޜ>bs N쭣Rr8m/J+w]Ř[*TUlBBǔN-+Bp}Ldc:UQ+*@~*qp]߮svw;Oc}ۤk/&~.~JAE1u8%T%6Zwy.ݝs 88wrLDcR" GGqfD)7ϏONcch(Ev{ŵi<|ɃW~yuv>6|8蓝O0+zmO&v߸%(UHJ= vܻ`VRREQ7Z#]2wq? d#オY3%EdSFD)%cL̴~Ę>5N(UZ+^|S.4 z݆>W3FRJj\VvFr25MZCRBLc2֘Bܟ/';;;J@4$JEfR9J"x 㼀պ( [袔*ϯ5 9;~ҷ+. ^_xP8L, OB'#Yw-˂u(J .s/`O>v;s49U/x5jMcλb.߿`/ob\'_r>_U* ϟΟmq!xo9 Ԉ/sR<3:OEQ~rΧ"R*ˢޝm?Clu)MvJ2&t@"Zy1H1YCD&]!DkZ벅V w2C+W@JC]T+9p?==z86m}821h.M2g\HU#r~Rtqy&L*]T1ZgM߮շGd"RjdYXR7G?(pW# !&>='.'bdƧ".כLho%~L:4s !6Int>;[$-?\Aش pg9H{&;Lso  ȬʚH5M(6떙L^/ʹZmZje/zOi#u[Ь"U"Y21Fċ7E$@ L_e"ܯs}]R*"k/ RզuV@z2,8wNܨ'$:g>BJciΙ\(9Ĥ$^4ԙH/%D)U]}B&D(*Hi_mYWzk(jF@js:- Iy&D1|[ıOnOn)AzE*)Wf)έV+fFJ)$"sx8x>{‰B6*%K+l NNDd!"ppmt?Y?\R9R|SܩC IRZ[,B勎{hxseٺ b J52S)%$RJ+י뗇HJ!סţ]/sR)e S1R0޹rɜKb]sl3VYປCl""(%1]ߺb/w777QYR*|8jy݆o2 IDAT61MӔسU\ !\k$5)|wsG&\Цf}1^I8vNQJ)~:h0@"q.^4JQ1JPLlTJ+(*ƔR*97wo"-WC dC'MItmdcyhnp$c>E4,A!х5S)ǘRN d9JQ.~2!-ψ w8\BJ4z((x<9@9ucܚa9%:SIcxqJX>MUɢH|re&)̼j[k1zRRJuw@Xlx;KTǜcVgxXv]_Ye>z[Wؾαawݽ{o1cd2>MR6uӃͭ׮M頩1ZfF;OCyK"g\i!HUg($'(] !H b*W"*>> | }HR }V7t)sE`yq[I3፷V[['ExyhnsoyoJB9y[7fPN`ψ2C T]TꚀN)b|_0ý{/>P͋`RƘ1\+8 ksQG3Ps:_#]m} I@)IkRj>R[D0"eʔ P8>GN1"6v"1MvV<<9g18 zb q0hRJa ?L&R ) ro%9VKiHE1པϖz@@I2dZkѭf;)^B hn!rZ>pmyYdW]{\WW?O9HR@)Y<6;b~R+uHmۅ P_ѣ}lWmRr΂h:; Fk~ RƍO~B;ۿ7K!D H\pJ?1"nRJ93  ,1J& Z"GTP`ڗ 2llLVp uZJc*e9;<~gW)SJژ#] ;8"j}߇བྷR^w@Fcp9"?9hF8#^pJs.G6G9rZ)}SUeM)Q`Y~XREX.NK,A6'յė}Jg{9)eE4jy811Y#*MDM4kR${˽/؍1xʷ w49Ŕ/ɫw7SJ) ( EHg\jKpĢ/0Z\RJQ!^BN)]9SB6癠r} KT_S>85 A@k~8nN,"9'`NS Ɯ/L/"B%ϤPHzM? _)CStw}u]۶]e Q_y@(%Si\G"?hiSBi5>۷oOrROFRRJ]uD(gx y=Ѻ!Ĝ (3{W|!VUv$XJiY1 {OƌJișS j.RڞeP>+ QS !}Z.Ve۶B1fſ?'Sµ`\|'wx&I+۷CW?yIImMƻ[vv6&cSyy-,@h6'on;کE9 \9g$8# km٥9g)icR;WfݻЧ褔U=7埊]"܌6czvBrιZ_9sq5N)94ƚ7z@"RJcZk%B1uTs<)efWOd}N }DRۄyXVf1 a:B!bVVa!Mc>N%m۶-RRRҟc=~ aD@2#DJ)R*qr)gΉsF"o)C_??7~7j?{tu]Y%#QJ1DzrOXDG1Fz04 9]޿'MdF呹Ib9vD$IMڐ& , Gj.s"Ă ŹykJY+D/w6U݌fMCX-"9m3_(wRf}aC~!1VB!% J+MsRʳ\>ΓNx@1DBz3;walƜloOPUUƘiMʚꎒccw{4Kx7YJuwsN)Yu=wb>#iF[}p KR`e}DXL19D>,trJg}fkko/Z{hrήkv9]JJ5Nۮ`0تڌH2qv9ĐSdO #k0=+IimӥT"E? !˗#MXְQ9^&s fC7zT-Fs= Dr5.e1-C$R1cəC}pD74;2(y!C~i8 R9o@8ʛzTRϗrX,Bww=+W\u9ّ0SbApԮfZoR=kkkseq.f8aL<})v__߽{w6murZV?pcqWƌ8lMvl>;pާ>pkksccRuF%VQU7֘2\g\$QO~?_R..Ű2SEׅ~V~pַ.bHRqF 3OǔRg ^;XZ9#E:^< _B6<Wl[x Na}C$cy)ɾ>Ԏu) x<~뭷6&"@{SJ9gc֊11ݢyEY])TZ扇b0EwK)Rks{"ꪮm IbF$%%Y[ ֖s.缽}O7>tk{fwwwObJ )z$g+gyu_޿˩j3Sp$m >ُ?ѭ~{|jBPj]!mq.{0 RS}۟|)%˝bJ!-֮: @@Um:}e Nw)y1_0xJ7YzDJS!zcZ*kKufz/}闳> :<>N>kgfE Ǔ]cZ_v])̏$>t}\.CRʦi)ŜRg~J+m}!+c+G+m炟i]"9`..R)Mf)j$@W,4GdcD~h4ڽrZzDц[}j!H)-:'`Տ$[wa!z|QM8J3ȼ/(T~=,.)A8-9YڤcRJE 0tTr1 !X+"ژc9p8O&(Z! IHx\eq Bs)2psZޅ]X9R=j.Yoȋt6BSIQnݚN!O?sw޾q_>l f|?J}2"fwtRI`" ՍoM޿33o_[iLꍛo7qmtmgC()t 7s 0}(OXΜs_G:iZhdZ3[`ySH#")2u8G1x!:G$22RJBZfN){Us.J)[c#PR !Ec$d}5s@H"FRZ\`sbƔoR)J8xn(e\8%u8=PDr1ţ.n3}@DZib8)JNyW4đz Pd9g$&&JUUF$cp[*]Imh(]Z+Jw;Xx8}٧p6=(֭_||>)QٟOtQI)ZqLJS1vy(z\_Hjmmrb NI+2B[Z#ikkg4aч XSj{͛@k%lzJfL5C_-w`0!"pB-![a5D" HPH8Rƨyޭ|V=loԛ[k,xh~E Ԭw"CdשN&V+94ʪR!qJ1 C)T$5KJhKK{⧭%01\`DtX12.笵RR9WvY)%3+C{!h2blZkcM8_/b.-)J !!pf9CazDf!1k%3˅j+=9?>JQ@*sW½x@]B4Sիjyp0ؘwRu흓DS}U٭-bG jjq\w0ƚfm(6\\I^D藇uSYcln_νeZpgd8m͖Kȼ\,P O2u$eMM[)|dmM`bNiP ku%e >`Dذַ_׿?w~;W? Rs7vwGq$@p I9{"e.EuШ"$I03s$겋o1`Ȃ9srj6FG$mm^ |(xB$"TSl$z{RIM}jR]>覮Ro#(u1&48+G:Bɥ8s7g)fѯWH1y狃 I9`!3{X.1?P6uT ժwTRJk#TR뺲IE!d(YBX'  IHU GknAۻc}PYjZ+@ܺl6ڵkWLݏGp􊪜CNQ w8{ޏΐs!9_G=$$IYUiR#;(nyM3 IDAT.VN+U[izPKF)ڒ)%Isc'>Qs ryaiRЮf}l&[DɀcH"r"! RkS[;P&9>|J/mۖDz!]2L @\Pyţ'1s)3  #^H7l10s>&LD(}㤈il RRJ)Лs%rN"zenK7qRnll,;wlmnmmm"c9o;1[[Ͽxw=ާu]u]\H,RIYn#0bDgx\RĔƐ69FJ- qm{?̇5p9VfԋNU~zګjx{5plm}gkshC `;X_з|pKǖiqwqR*圙Sԅ3Ly1&8.T0}ރ;!@j@~9;TFiLutEaA*;PfSJ9sNo]]2$ Y<}(m-zvXyjDr4P܂'%5uEҮKڥ#̈SDN9ŨU3{\ $zhiOy%[T(;QI9H^`rHke1>a]Yc+kIi*0χAeg};7Hܙ>3gb>MH1 JDĕRP%ffNi{c#x0=F`0PZ圕RJJd9BNs~\aV jˉWy'㑒||zH}IJR-żw5Ҡ|GwO{ydl9x٨b-=z {'gN!(ӟ]MepX,x("2?ۿjhs*FEìLg d)Y0__͂S$:e rM<%=}X~zWH%IR\Bh.+EA5eFW ҹ*_/,9S %X8ޯ#N }A)ɜ'A!"m 3;7Jonlܽ{`X]O&UUʖJ[-<޻giA3h\{i4jI?߉wҬ}A(\Q{1TꑨTy@J L9'DskׯU4Mm~?"pQ}Xu'u =%7#%b}veP/%3׷w~:>~ctE1^;[2!"Y6&*! E~!u RZ'>8h?hQۤh: rѵ =akWYn*S1sλaЦ֦֦QڜRcc r @պ*MaO9ZLɇc,2gF.5?hTJdCVO.|IծξHĔ}H3>3#Q!vmr&g2\V, 7'.Cn9 E b,3&U~agyhspN`SNJ7^) ͻ7ߎ1!bwUUis888u޾sǓk[tuCn>/lA1c/z!2)$Hi#IDHx}&"cR1vt|0hG~^BMrw<^pܛwU3K[@këq>yI/flo^~hgJY"(%*-3 p;G&ƘR)Ɯs~@@ITD)e6T? !on=XΥ[bpPy^)zZB*UZqnx K,5VCc4Ow+/=@L]׃nTYۼX9c #(@3hr1RN4O,rJ1D" `!Z.Sé*B1e:9)h(%ojL&ӮmR+y8׊a1b\g]Q]Ջ}4&|~q3ak$* ݏ'\˿O`sX qz? ꦶZj5@(z̍^XkUM׮s?mg߳6dzv.Xon/lԓzcޜT(ԈY\%9#΅)RE3XA 1%4 ԍ7lZuY7sJ!8@Us2;!z@",0s)vMxm9wTP5ETgNs94}/-TW!HzKsT*ƘK4!ggzsΥA)yefA6zS^  vb̙sNY@Dm͛s׷{{wJ裤FhhiGwrD`4te[OC }廻7"Y\"y<<D86ۛ㺮ǃzVCQFB[߻}ΧB@ņ>ņbޘԓo\F\۷ww?ѯ?dɏoՃu}v54 _s M oRJ1xL0.RJ),B)-zx/ejz{>6X<9,a4瓫!z3J#HIJak>)\)#k zol54v;Z>"e}3sF$g!gLZHg`9!1!^cpʒ㜋1Ŵv[8f(bLЖee0\ݽҭU{fJU`25 % O1.W۷ Eט{{!Vbc(nۻzs A1Ї,QTў‰"BR*Eu]Ykfl6_t}@4Q˄8rL)Vjb;?i:gwwtUmuS$lY2 Dxvc̷32ayu T W, Jm-m{w>S +ċ+((N6G]n?ٝ?z7~^ic!/#Ҭ{BC($#4ͤt Z%aJ=v+纥F;D䔐Je!r )v!YA5ج$3D֓+j1Q Q|1l (}NB1FfXRfbf̭%`"vp֯3 ʪ{_N }rRI$2v% 1Fvј7ع[>tsByն_ܾ͠)zhݽrssNuй9IBܩ=z}֗+,3 RHv p\pgΖ"S~9Ѝ (bQ~bHO;WYO{۳n{oV=[W9}JYa3R*Է-Q*:6!8#"I; )]wizJ1M&VB_0sJM՜OĹ ŗ)3jX"G+m*$2gk[ T*e$I|L+CbN)cmۆ63ke$['I3g1+9O5R A0ÞuZ*z*Oc!! }H)/ fr^3hvUEZ1Nqz0-o}gsˀV0Pa TB$WMX|mynF$]X[)BzY^ōl8R{w?]ΧT({;3'fqtuDT)w7o !2sWRQb,)%b!2l9 S|%!1Z)R pd)w.Ɇ X)\ON)۳2}6u];}\'@zhMHVP'園>3#kwF}MrsJQj֏oYsnX$V^s>g><4 `\`!}Kپr];= ,x?zJSx3NG#On]yc@DuU^S{>l+)ݻm6;`Xb 9 ~j{"`5m"TPJMR_| I6_-f9kH)?!f0~cGM1dۘrԏJ;MRj@sέ"h,%D⦔Vw=6T(5c9P@x8^s/l/)g}LQ `Ȑ2BKCENxNZǏv}(d+"efΉsoD~RZ.WJ׮ myBe-3ZjbS9[rsT)9eX";P4Ym.~C$6C!<9cS u0K)ωGmKȾ΋=G;\9P՚sʹ{8ue!QkE7:"%@m;ZVCgl9#8ҕ2)t]gXR!!М }|s @(Dɻne=PF9BG3q1DqjҜ3`!Xu3KdsC5۶f PC!JzKv&ܯVT0V+x9sL)}HgM,ɒ0w[D%ں{zf$IMfOQd&EQA0LLwuז{/"p22ce!e}J ȫu I()Ka]JJsY U)ٹx tRb&/~, e>::Ngф͛0>~I>_:Ń|qfG9\ÃKWBAr׮(%4?M <}.[!xE17;;=y{q)ZkU@,9` Y['"0M[wf1þm/~^Ϭ:އIzwTr)ekecJ,o+z"1i޷|f4Mػrɥv;aᤢ놚&2.6 -%%ѭ5(KIk$ 0)"b>:>Y^3FY7ߚ8qR1I¼Oc""0RM߭uwƍy !,BUkgN{VKg #a^FϜP}h'!43!Z\ᄷIПfCΦW5ITcL)e!ꬃHjKs=_c֭*<469"T-k~3p!&4Rqƅ˾MUN\^5F9,\D@Eqmh],loha]%ulޥfӘwPᥘ1d oyfTZ[l.̅ D=ݛOQl -\8LW=,sAc|Yk#T( sسYXk@D]7U.SN#c_ddMD%~89|<0$ܪZX~XVXZB:>)圉fb,7n[? TJEE<4U4ƅ%~i7PMseڦs~ %CD4*gmӔqU/"4ںvKmEoDŽ9_=fjTke)iB4WwD5vm.+RJJYr&um^jJzaaPЂ䒊<-[3d\TyU3̛sv=u<6Gj8:<)00PQC!8K gQQ|}w/$(%nwcڶebc\ι흝qO[[[DlƍKjlk. Y,*/]d(*RZ|>NqG鼿\-AU s%sDA}%R|J̜R6dwo7mkgsk$x-dʀVksR]<sIq %2k{ims jb)xf fwjMMdev_YrJ9ws(,dXsFj y]B[$0ωTv>/*,\;3XO+u! `Yk;\B&HISl#PX úϒ$Qk+Ƙ& ;[jP $h ^0B5DQhB躶ý|_Q۶!{ƘJ IDAT1UY֕YV̬޷wH^__<΅[09vnGDR0`m۶pg{!VUSJZ%[~zWWx-Xąκr˫EfPD>֚7J^tB})1 >Cuŀ43qw(Ŵ\:r}cWmQ\"1Hd?-pΜs.9},^ IAԁWјR*:R?1!x2Y:x& yYJ$3[c&y ѻjǀ@H93!!c屝/xf{X0Mug… 16Pe>":fr<8}εe}DJ*{B%3ADi<'J:\Jku̼.\ XkCss)dfvξ"Q2GHD˥:}%P܏@DDkf p⑀ #ͣ'ixU\/,L$ji TbIj,X K@**k> [۵&[khjumc5H( y?EEs<)UU͹H18i$\s|6>zhǰi5[Uipe˗f'"#rF-J)c"7߹vsDQ}v e4DUrtlݻ HΥW:B0B_db"$C簺>*H/룜rm[E=s>L|e.%g޿eQk\Rb1K.6Z/D%e6U`ƪgg։c\-WZBg;eAy[l U \ pR/ `J2LV7ƠEUۃ:kB'{,AGul\uDu`S)Zf6k,*ETfup a<WDOUQaj ởYt٧wsʿgt:E"g"Z)ڡ~1֜Nޖs=q])q"B&O0:(k%#_3W+{Ury1Hd"u&YS)aS!49. g uk~$2"Z>Ð{˟ѝ/غ#K)C|s3c,"3R*]}O.0).]P]a.Yu=ˈ2 r㘚 MsX?[g9R)%XBֿN!*XR5eA. ʠ jTВζ Djf2rd}>zˁ"&t 4ZSy %<ڗ\(Kᒋ 5r 3,bλj~J8qu!*u> Pu1h\Zkݻ'm.Ƃ7"㈀㞚xhBm,;s+ !M /ֹ~I1u[|ihI|Z!Vчpяn"1)圳1N&K%{"Zkۮr%"GjD4YR)z\`޿ \œb,)NX[z8*ZZ!\;e8gSʵIEJ-94!4Mm J.C,4MpΞ-5sL%g\Bӄ^3(}(軉{^"2P)%c]$s]$,"V5kaE ċ;)q$C>]Z^PɅ{ "m;眉h6loM& v 2˰J"٬k{ep,9q\"ӺDm'yX6*<˶\o[ .9Q}Mw2aHXc{0R$2Dtn>CZ+h)1q̬"{gy/C|iicw B֑2^%_J9 1̥RJ)"ʆsC U.,"Zc^|7@DH;猉PrdN)b躪yau%>c.݂R?j3p;^AZ[^ E5q6dq-P0RXzNj\`%EkMUX5뜳Y兗v|6qc/8?8:<||떆jHCʙR0}gݹsWf! <%L r޻4WN62pH:lc{bՊ{gK=ύ19Ymwv{M=9Q(M~*safcʇc.q\!l)‚!4gsRPZ/[cJ\J8 [㌯9R.b̨j֓9cڮQ#>ե}2"3J}4+J# .{ůWc2#U0 Vk;}}Ѽs/d*HO]\cRBM0Ѝ|RQ!̪MեpJi\?9888<WO)JMC6Pl=ȳ(/'?|/8'c,Fs.TM"9g8yehgt(C"-=<<|gD ! nņl飯)1êWд!  4cJ'_<kGf?]Lh3t懟=!H@YR*4m ծ*MUC@ڹ31:|&efD}SCDgi7CU.fiνALT嗵yb\-Wѥdc=\ގoq!&#Q-s5PFy!&NXӖ:s3z0lm~y*9aqN 1dYDYmQeã:ϒ!Um۶i[|+"H=\@@{Fjd,""@ʥ8OqjWqrEBLm7oIp9qTMhs%jZOk!$2@sU$R3u͕bbL90:ﭽt'ȋqS(z=k W"r\`J82orV'?G--e>Nf3C߯jv:~6Tffɶ"0SN%guҷtf^>sR&2!+_? BXgQTShʹ]>pͣ KYz ZpO?&J}gh6B5$ WufU.ŐX~''5F⹹kt:m˅q<:>cjv{{ܼyss"rxxRWDƺnc]l!t3@Jb;cv8FpAXUn2ߝLwu"{_?my:.:ypKl{^x?'cn*%GGT25:cZ qν "Qg_jQ>²3)V;DHC9:GCd: ߞ_Ziܧ޼wwWCD1\:csFDgmZ]N)ǔ0#@8mi[,W@Xkɼ-z.9Ŵnth;eUS;k>9]t]՞oTS)=vw QUQ@Tպoqq *;΁/U/sd6mmͧiU5&PETMZwV'k,')E cmtM3ii7ٚLw&ӝn;Nfd+4S^Djy8CӅv{͚[1l.rD,PUqXR4osa"Q*!.p mLZSU?{ET[wyX; fܝOnٚo;;^%)9)64Mn mTƺ}^\' @tν @*¶>(; Q9e,zWUEH| %QHɑAXd}3G=5*::!Ur1RR):״-ԟ:-iǜ|4!TʅbB!Cӻ0c)' gPze;]/v]9Sz聱5ֈ(%֝+ܕus iQciœS/u!d c蕲}q˥w>`3Qosjн>CI'jW}:>S,tw#p"_> !4mlIK֭iʙ=:!xU󨜝W5SuhZ}3 Q徃Vaxw~mP"e!I~&4FTr { @ AX*ۜ5wٝ30m>LHƔZTbF`1My_@wcR:n_?;*P[ޭm׶M 0麶mL.>>9YVUpcFuK>~kDNd,ZY897MsƍhJ&{ɓ?QaTBsoX.ay|HJڻմ}vw.RT|ۇsC:f1UYUhήz%C\5)#s 3Pߞ9Xqsyu!PӸݽNX"\kIAK@tŭq(95ܘMքB-۶%Cƍ%K.\J;mciz.T5%_7Ԫ,H._`j,YKvcRKHd=T"^) xC!!nu0MIne& 4SsJ9s9$"(rN$?mVӴyf!l>O1raD3c)%,h! }QG)vX?Ng)ED4TUvꅮpa2'R"(k fey|)4 .71:~·R8ձKgԮ\1FfvއмZ;mZ5n,OR:ϥ\Yi>,|j!  U4ƑVv ")qwfWXJ)*BH;KBx+{ %gɬW\*x(D4d69%T;0 Ch :PṨj5J(X%fQ?bXՅ|mWEXyT^z~{󩀋="nA/`k{/~4L7_C'wvv˅2cT5䬽뽟f04RR.;g-ԇg=0՜b988 }Ji>;>Ŭf Q?Vq\q W6&7,3O:;ΠpI\z_!KqM.׎W>w*\QPr&C歿8N**)U51W(1}xeMA8ik20mw9k (9’Cc)4ms:#C)` 9%&8j(\ Cڷ!Z] \J91~t3EJ)͟C|ŏ'4~3㽯~1{bqdҶ]a\<+uΥc*Pak{g:7m\9gv?W|-{GHmN3uލs)I08V.8g\>0Ra —5y$Ө*CDDCM xu~>DJ8,sw |Aߝ=CD޹;+YJmk@pr1g hVJ9ت,\ ԏo{~m[Ck^ve+"bm|H>m/am$*5X+9s } <4^-/ESΫհZ ) }r.Yl6qqhk~:60d R$i!v̩䔭M5֓K=3B4\ՠMoc6ۀ KLq\m'ꇺQxDD"1'$ d:/~m>9ضM)9Ɓooܼ_sERC|7g?٬&jypdc}ι۟w~'??E:3<' }1FD tu#,qHwu"^Ofֺf7ǮޭjWJ LgϞsdq@s])R L8R> В"sSV"XS#kƐ1Hfm cH* S)c kwDi۵GG"2 1iks?#suS[d6XRsݵEu<8JB$2c~XP%s.k`!R"* Vۭ Q04uxE IDATJSJ4!\LR"#CЇ ! Z] |u=ͅN]RjZ'.֠NcB>?|еm9;||H)5ɝwqމ0QNmO<lMfG1'|{',ri}w͛71?~7q88bMl2v. iZ>t: 2XJ.%<< !C{&R"¦i_XZR#D kut.Dg[ c?ZkUD5\Yƾ~e6¢բiﯡcw t~9_?Dd]EAYSUCӴ7W6Udoo`Qm[%hLkk\8 m^ih_?; ,ࢢU}DK@E ĉP2H!#*)cLbqLÐBpd @}=-`,!.qC?T Xk"0D\hA2Do}[?Y06Eo3|![VǠ"o*_?JПˣRu·L0 op M0a(X_Nfua2߃7" g}GD1&~9dmgBRSJ\^|'PM) Couw]4#w]n KmR+ȥpɹąrjډq >)21}8G1l;ME$2 byu kUW)) iwXscV#zZjjh #RL1* ~uK)|NW"K)ȅk5Nc1-=iQ,oo \k"9qc1(<J u !*6ݣV"HJ /=oZK^CT_v8:@1ѳ 8"vl ON3[󝶛XcROq'#E/no行!jli흝jy7\zfn~nmo7MCl]5M۴mhZD|_%tG'Eє%Kوfv7q" CtNgY4h*)ǞWUY$M N^5auv2/&' XhxEנ 9RJ1K)P7fUZ;X 9g"sAGGTpؗ: \q,5C U-YP*屁* cND DBbAOGfc]gݵAS1:Dlgk?7nZ7OR?_>mcimj&6}3޳|6݄w2jUuX "r>L;{?Pnzk@5H9Uh~RJ.D4ߚ} N3C7cu\8C !Wj"~K(Rƾ7nTsRL8嶗B"YB S/WsJy#R;Z2Ɖ*i6k}k#z)c/ԨJ9UN *d4cLΎ,B ?YXWE#֙ka)S]+:Q6m}Kaqay_- 1n ^O|2f:ݘv΍;?O~:/9Gn|s>,';2D՘ӬT#]ivnt0G1 8>?9:C'?Ldڏ,0:]eJrΪM{@DΪjcSBD_\N'OvCuSGͳRD'qkuΝwH'1 ƬKx'.)խsv:X>:e̢ks/T%̪B阗l.xb#9 $0 ȱVu̥rs _Zx eVeH{^"aBZ{3B,b ]~2sLq\Ru+b :WH$]y!`n#[7]׵֘zj!*liՑ&!*ʕOa1.\2K1^ίaCD"$c<޺wrt/y瓻!4*rp?[cLZ}{﫣Yu YDX'ӧy2}__q|ލ[`i&|xtx޽<~92c9" !ll.dO۵ܯVZ[ kmu*sTV .y)Z_e&Ӝ2ssh봶EΚ 9E zނ1攬պ{w3j("H gaRR$Zc|הKfиa> >ss6M{2Vv6Kykc S*YS o7xֿA@R Z$+G-Jڮ )В:x^`q֮Y5}@Q.,c K[kƚV@3[8&AED:QW1<Mm;nA'w|2oݹbq(9Rv4Zvk{g:"ˆ8Lڶ|rSG!)|gkqnѣGObL|kY8%\_2LqJ$?S([M;~yZ#c'-K͒~f2oU{2uʶz Pr6#7Nj,\XX9BӶsBEK)R sDdU}@K̬Z%S8FN8 *sV]^+Tw] "Q6Dk%|]RV=^0ACTmT:{йGD^U͚ߠlQR'Ig{ "S ށj}fńDT=wΙwPx ^` "RkΣ9 ~{FO7v_7zzsg'w?Muǿ;{ئi}9k'ޭyin޼ڴaZw?qnдmgßq7 Cr|4oOg{7o&c\ 8iiY{)Z:Wls2 }.;YQA)7m/K GMiXd&5)GsҒ2(7ةm9sutꜳ=uw'C?r&Cօ+| 8 Uka?B7L!a,֠XOt S*97R.1 3Y5 , ŨӈZ#ſa ꒹&G^^8  !|Z}`PhB vMh 7o~ ضcJaJ:ND3m?7mȲDK,Xꚞz̦'Ǟu^ZHB̈  RK3ʌ瞃&b, !Rc!RG}Dle>㣔s7'Zg1i[PWgðqmZc9{}P\Nb{ۻ}fU13GGaw5>lְ)E;uN/VC{,kc-[0[K퍈-5mx*tWWSjl 1iXojM/"9Ro8Ms)>{S\-9+HIu'90 5!"&**YArJİH)`Y BF,HSXPPW_R/DmV@Qw^f$27VZTc[c8KI1!&|#Vٰ\bn(URιĘƶm ETbܟ quH^+C!NGx*w&u`sU5ց>r81Zgdl)o߄)LhqtֶMQfMŝDD)L{1"J.%R}VڦA4 Op4o/>bSN8\7Bܨ?kq^<&Ft 9VůJN'pXkCm":>>1YnKxW =Ʀ~`Ԭ"',RrNۦݍsN)!+"b}i?i0bwHt!sN1Όͻ#U$<PU b0}DDHnKDljQTTb,YaG x߸R8L"nVR$1 C"d-Fƹ| iokoI# ~(<"c1*%0Vݵ9%X66Z뜯/?!QEժk$,bnVjw)EmɀB3:U#5i. 笧R@"Sʹ)3 _~|qxx8:xUYz˜Jy:V6wm!4,~vK[{8zӴ^5i{7 Lٍ)$m8.ٕ vg4[`:()bMޙGDڜRJQJ{4C7>4h*R fCT 2!Au.IKyIS$,ؐcg~"b"R4G,%;X"SBoxӭZPuw}]MWN/=un9oT!RDu2\$ak\}4i\u Q9VirJڶm"PwGϞ|Z.K^tx :[ؘiVZ{Mw-bzև^ }gojX-^jv2m!z3~T<#m۶}W^*|@#RR ^CHV7YSa:c˓B}ӝls9v*B'pA^rh_?{ho[Wq{9R̓19%hoF9RTXŨnf"HywOd-š!wqg Ib,1iW׏}(Ч3}7M!LAzr)T; BAښ U!Cy퓧>I959띯-J[Jњho%6;;|׷}ӫ/dm)Ũ k om꞉yJnROØ1sɚf¸aX.l7mwənC|?ظ0/| 4F6*h _?!\r\v]kO=Wq,H[7jXH/0Z@w+K)982}xoL䬛MRJ)8.gVqdf9Wn0)%m]V"%N{ϩP6]jM$o @-oLua6g:?b( ks\AmjLm TTESovwyR 7__w!DdhC!5UO_l{/gr1ÐbqkηE*ٰI)(3ծIll?AiXe)27$>zybXucf:R1:pWAz+YLDoD*H)yX@ RDDoQ7WT{H6 r)N3@딄~KSXkJ)S8iRT묹y7;(@$\1͋勯Jt#"siS-da D%_}r,X i@)!Tft+9%)ET1~>"kqۯ_O_FfcY,NGpVq:9Y~O/#_1bJuzS_w11|yg'״{ryF⾟+o^jOG/OÒl)R/˳&bD]9뜷-EB 0"ι:_4~U׷HN|ݹNHUԴ|зMD9,M4ߦY2B$H@pwf3{y",VsQJ=z{>u[rg2~D$DB,)䱼h)pC6Yrɂ.;1̍wH?j|X?mb)%b:\guv"si{޿j՟xSdCloڮu7-HqqVR a]BRB0ZX` R͜#)IDpN("J ˣaX&4tm!ٶayRr "6u+n9uKXkm4k.bu1):GL8MS9PEP?TZg_U1CDMC QuӲAOxou:e&gM%❵0&YE48 3|)CBuFBRݙT~;1YkU4xaogb%a[U!ÆP-Z*wބP$,!gU5KΧqlgA`c9 1z6W]㝵f X)ClO?[U=>|q8wW7_~\:`.e0y_:;h{ۯV`Xz9K)>uc IDATkV<Ϸ6Nj-i\|"~c:z $d6[0jݴo"%(RZCo9KpK"$ {y[Q 3;d1/V_B(({FgJ!;DhH9{E%jLof^q}H]jѢHM~oG>LS*\7bDjqZK)91\λmkWԻ5wvɘ'_q\/Or">/S&Ræ۶-P%ovkp~5WmevOGq+ } cjs٨mf^i5X\3mG2G߮O^uh3g/40 p{}^Z+ E3[[(6Pmϥ1LkڏHN5mӴwT4IRnRSĀ 5II~Z{o~N_\ B&!F  yJC߄u:UU"Rj' dMS&8_J  "{Ä>51mӬKN1K5uaQ`uy7`qqx^Ok wqb^>cFKu}vsi LWv0#3Ay{ˑa$ցεggyk[n/"VW&]"+V4"e{s5O%Q֠*\ -Dl޻Wn=VUʜI FT"]qQV-+TBETxTrIཿr6{6 Z4H67RViy!C D1u5* 0`-#< 2j]zEq@q`jAxeBDH)TQHCCfz=mx.iMFD48c-f}۶tXa:k^'|c;: \)Py ,SgZ]TN֖t*J)En]@x;B@D*3#g۶519ϰM1i*~aXӂW9O b٫>*dwfgo!cxI̷_>6]`|j9dff=Wmkp!HTRǯ "5M'[%{z>yQrmYM?Z!"'ooHSGnxvws{h]ED*l}CwB$dM;g6k|r\36ֺ}mCR)8եX^ (q*"d~~υqM8Fflէ.E.j#@V Y+8S.D\2Ϛ]H݂fnfP밄4Bezb?[@mQ>Y.44M\">RB,G=?Ys-%OvlF"uvs[fMߙ~lg_.njELa \ADa8zv5/Dt#QNYE_|[Jm윿6FDcz|?GH42*yB)*JXWvlj M 9jZqcmۃ4T=-K4L* K#R$c}ٰ͑U;}$jGD-L 7DPWL#rc6@`w8R-.~ _ d@ TM4VՍ#c>cWab4yxq,E@%tԥ4)Sh *_QKz~a^]Wz5D$rJ1UA?ľsVobgמz3[%)xrm]v_|r Ĩ!S/飏 A1:>8/qī0@7ݛWNKƘ퍱e6/9C # sCUJ)!&U%bt}bekK*9.M| z9KDBJkY&}a[STǬOWՌ X k B)6 35i™QQD9UQB$"k{CY`l[<g 2&pUTe$æ~>T(_TEC@eIUy?A!fKR$ ~a_ +;w=&xw"ַ]u}h4^@9(,"ڑs̔R 1圑򲗈Ncj5jO|kkn5wrVE_;U_C"[P)i8|-i}-JCDMhrq^.\XD,PTii@Df:"Rd4\()|^8?t_l6pVψAط} jӸ2M9'6Y62)Z9t̜nBj~9LS>l۴c=s z:5BۂDLGm z\/:d*"qcNyWIZH,O?\`d}Il(AhFUKdw1ƒkeJk)EE@%s5qYS2]}"YPQ H)+8dCD @^O @\YVoŇ}ۦk|uvO'>&׃g>q:NM:x>M|>;_} iE1S^ʩ\^+B1^sT%4 Y׶17Ͷ9^?U0ڽwXw"QJ1p|km]CRr$"̣Gݯ-a'˛ 5[[y[s"n%)6b7TU'OU@DUP!4"Zs9׼d9j}k}o|c_{wD$!u޷g=412X;)N)MqaiZnn7 ӸZvu[* #MWߝ΀ LTJ!RE5r%ϏB)QsÀ&jVcIb)\J&om["f EH yr ,KֲHE~|t쌵 [*/YT~t_00l-ekRI?Aj>^juĠ{=*iϞ.)nRX+)31n@;U9"n9gY ۭŋ?lٍs5fOPR.1%E/?IE.E%y;j%fӔr_7VgRC)S)@Ky{G RaZflD詳|GRdXHN5ogUEjUqضl˦avĆn:3w%0BUMͻ[LSW0qllD@4ǻijZm #ѥXËN?VE4i2!6f25M4|ӽDȓ.0ؐ͡ u}-bv-#6l4sNKTRY2(4 ! mXQʪ %t_vijSkX9w`qr=?VZE*m&nDv/aP|.:;;~o9RJ vy1wT:ocdqhCD;T7*c 3-94X@RK)cJ;CxJy.EƱ,yqB{ӧlfΎ j/ xZ2c8NڶU}Dl>`bxf<*5m7qJaursu3ƾZl,X1`ؔTUb %J:MZ$vMKd5ݜ]WX*[gl-CRJJE~7>ʬ¸RR(SJ"A,("@,p~nI3Xzxrx "R#¾!ŭUJJQJLʜOb*1Ʋq~$Qb,0JECպ(KF $wjYR,1ْU)I C )z !L[:FdUl7xxۯ~sRUb; %#ȋ٭gZXL'E8Ȉ "ѣ?$DmGǽo68!0Mm5mxgy:NrͭuGOW'Հm{>m!>[;M7\8o9H1Ξ>Q:8||Y |_ŧOÓ'(t; al8 dk/u DdDB&'o\8cSaSkڮݖǙ1lS- 1i5$ gwvm]gtUC qI[ݘ0k;bHNzv}JIy6뚶qn_z#`6k('Ȫ*@Rb -|Saqa\N!]o~6ousJ9hX8ck|ӷIQ  &DˎSu)BMo1T*%jơb:Ӕ$!0,Wf͜ueP,vbܤ)0N@qδ%h* OHgk{ۯgߨ oZѴcF"b8d8<|A6{{4|ًsMRbu;Go{v 3zo=>4\Dfyg s!`;iX:R3niq`dĔK)lؒec'PJY.ӧ4}1:<)}!}>:Jϟk)}x(wYȰ ضMCt"8YEDOɝR4Էsc)dc7%z s7~qmӝvl}\c'2pc*RT _MbDdRTKSuε׈b_:g)bIaZ3o\$pJYdX!ЩMƀH-fo[ߓ'%U򚩈{`h`4qa vlZ|Ufk"N SYJ9GӸrͬ|j"*L``dCLH%r9cNYTqXM|ˆw[W"@b51r@0%zx`:X7aqϠ;F6dn[ GAD̿9wݿE κfM7}IDdU)gϞ=yl6;:<4p"kmw8:>z?n%2Nzq)|>7>f.F{bc!^)~yƄ/Z.q9#bͷՂRF2 3?~\KUu“'W_gπYc+l [gK0a%>"d),:"Წ9s%By Fg@D*kM+c뷺~?oN+74c۾߾⬧}m0Ϛfpz<~mmA|JQ"Ǧ1U)l۷{rƶ]q "oڶm\@)ڠ0>)GXz 7.bF>r'4b1H|soc^'WKlo@iuX.^, C4;{}B\ 1d hJd5G) ^M"%v*D2h,LS8::I9Wgx@mٰ!%ûէfdKEtONtxKY}1%۰>Q׿˓8q-u}2xx1R u~Yr:NwϞYg.uLD,eS[ESJ0 9G? 9g;4,/D2"s7־si~wNa"wXBkiv?LBsQQۂ1W:UPo;߲ͻӞ RxM7a6_p^"6OPK.9UU^ bv(]%4 }ι h7IiqJaHqTN|t[DZccKq*9V'G5&BӰ:RnvW_UR9c06RD@!SX9u@zCcy Uý7O z5,F${[[M&suaX-K)Of[{] re[;DG!VCcPRbHHKFT;H :$˦dz ũjyj9:PI0%6lc)RiEDS0N5jfn~}ޏPP_׫r\0圆 )Nw~O?wv b|6 x3{ IDATHƐZ+"*Qa¶M۶7 "u]DZVXᆕ?ظv1Ӱ)dDo.wLW|#)QӸǏe-k{(.K0kӆ@ACDb 8VٿWFkVKu^TJKNp <b4 blTn,] RYaZO*'qZ!3jv{yݴR4z)LH\~%aɶi-<˒us)Z RU2C%jۊz\uVo\+w91" "(*kW+ȠIea\Emmlڽ#"ߴia{]ύgcFA.FHY1sآޜ4gs0$x]q-DlL% )L1t.1 d 77PDPAZDd1cNԡ)iνsJRĢ7}ދ(%itMvߊ̏0RJ H;;{6!6Mӵ͂)F !,vw.Ykg0 z`fcoA^QT9뜽E1tyk?gcon}hk/G":gU5=BkYs6f6GƋ1i+AZݲU("6V[78 qM65̈R2uƜY{ԅ b[coMzyZ@uMu]grcasB"b *@)RJ>9zJDضim3ɴ,ŐsHqZ>e:5i]r"қ)뷳i[6KD`e"""0 #:wzknb@. p"^ HJ1Wlw{kUc۝ؗ֋fOV/pS۰rLƽHȤz[;EU%',,1TMJg'V`pLg03s)*Dpv M/OI)Zl^20 :BDZ,+"mnm_s^-WϞ=g 3mSE$l16UӶm5x7kvrwa)bHdXv0a-@M"5=n=)Tr90qSJm㛚ӭ0 cret5~)R40:S%V ɡjSӺJ)Nдi\ѣoe]zK(00" B *gE$B(E^wu7o3\y^d?Y/kAW7*ZZ5o΅LtܧU*-Fֻ&^"a5wAֺ7f?A"j'B1Q_|Q"DJK)1眵v{~p8cq= ~1]ὛRGZmpk'8 ushiJN)~{?5X='mvI 'GN6Wi{mUdW_ZtmlϞ8Owr ιm6lԗI;"25'iDnvsa\!o:27m8IZjIIMGlr)b+9.*EE^;;hn~B$iZUR޵ِ UQz\.%8 욖mfi\㛮鷘jNkw&ÐVt#|z.ԫw*ϏSXg;gY8V @O`EUJ1Pq,$/Q|{m@iyu4PrPѢd1EձߑrhQ%e",Ӝ QA32fxNҊa笂Rlx"{D *rO}j'j^BXko0ak>{{1ãjZ,bk>7l۶PDRΥ*Qؾz4?{G ˈTeUy܇{9wKH.l< 0APu}CH@F'r&Sqo׻X7_==^V' eri:lO&0b],xYh=91zD͉@5Dٷ aդWh i\#^*fa&i 2dXE|u1pu''BEd\,fo^D4R~q=8kC >s~a "C7[u-i{y|GT^sg2)3KmaŋxS[6W4]W f!N8 QUYc1-t}؎k(zqW#Tf!"}6x$jY\*b<f?~fUK3tO1 E`U @+ \YR)A ;XXCHXyHdR%V+#b?*pRʙ1kvd,jn>G¨PRf>YibfBt΅8А1wY7'EjuihϞ=L/./E>"a|x&\Ҙ(yD`o4_ s?_Qo~`6_!ҰR(;"p"0(wzj;{\zշYP:@ܷ)g[ߵ֚S _v]QM/{k]i3ߍq"^x?Q?Yc3h؝" =}bfޙV.Xw( qaATfQll߻p]4)X YF:knVjͥ7gi18[("|KԴ1n̪qm@6l{n!Xkcոiq$DcL?!FkoE4Vn%RP D0ijРѣOoa2[ER&DkM~2V0^h(+u఼Rkg]V^\)ty*R 5O sM 3|ѿ%eWQQVջG3iZz\X~r1Įk>=YYC\y}#eSXIOf.xo~?!lqKglqW~}HX,bT:1 kM7 Ɲo@U@ ݃>\_Iaa"k`s8bbw۳awa ݢ.Hi?N[Qq> 30p@ xP39甉9cl$VRFUkƠ;@㼳E@ Qd*  T oЫ0$*YjEn"LZ3 !(: .Fc9KDUxU!H(DT2 p6Qސ1Dɕ&VT@^+A??JoHXg%q؇0gra9˜s7أ`9ME뜵>AD!QNcIٻ=GAιTQZ-eAyv8Ε#^>솼Yӟ?|1OW OqOjZK!<t1ƎErUĽ[2D]t+ֺYv~O/˾}c/hR+OH'p0-{RUh C;C&Y !>! 'x"-д]d@Q}kllŁ:g s,\ūQW1.9[ "ZEd%L Maoxp !-RѾ-V@FU!JAwKI)aJIȐ$U~}w.."`0qmcqGQR*+XD{ Z1oljm|>t!tG=՚4JT6\ݰ=8+P.ZW#GfA@sEVT{5\ki*9q-G\d R/nV[j}gy_z]uS}Wx?==9Y泙6tvv~qq1 {rkC1kJ"B4t׿6%%;Mj~X"|u(^jiY0r}?f11_)Y!X@ˎ@$:b%RQwZ1Ƈ@替1Zc#4f~q/ߜq-"ofm :i;m/P0MsMiyV~tjիefN(@7kqXi%aKPJR! {tRΙw^>41f},gXx=zG_zBJ^pn'Gy!u-'K-̼ߜd|{R])}!µ"x1p|U$muLk "1_! RK PfV˳묱h3_.Sֵ/]XJDT梢Ygݻ6׮-ZUK9u￐ZJ)ߜ[ˋ/O__^~U&1އn엏/=l/k{cC'O?W"m^ M)|4H)_;kGTk[OUm3i_>]>Ur꧋?i~~]^v)~t-Q)㐦TېAqOwl\_<~2ord}1nK%jZK/s9=H]i|%soڿh;?_,`v3Q=koR_]ұEP (qVg]UfBrf X IDAT@9J|pG}l!XZY+sqMheV5>=닗gb0Nw E>B -)x/"yǒwk/4|XBֺC҅NId Ku>9Mv}]<͎SJ))$mЅE"4FT*0^+b-!4UjEeD9C| >ͫYYDXTro}aS!Hu'ssѣGS/j̼n׮]W'Or `Z,_z!.Vןr=֐k1\N5'O "KqB1z2 (],wkTU*"tk牅s{j$V;\"5 wY,,\JM㨠Ddm~+융];nԒӸGqwݗp-) W~4'O0ob|8 %7[NEJRZ%9GΒ%zˆHhȶ|Tpσ܎9׹",*}!"QkR XWjZqb{Z-nZ.KyG=T _!*ˆ8y14c7[:LjTPK-#X.\8iJ9h^sDtD#(תt A|NZn˽RJ{v]^+ ~e{z"3Wf) 30sU5SZk)C [Z8l}Uݽ3bH~||b7R<7ð) i)ǯ@DƣqTADU]ki=V1DD ! ܅b`n<µVߩAG녵o!k1d-"Rs6j v9r'?[lSIDr.v>ߜNُ?<`q/볳ӓn͡P';PQ:c8o?zG c׵v}|%q*Aq=8OaI4i2ZJJئp)IDaF2D8/ժRT;(D  ۩~9D3c r%sRMd9wHGCR*dw?e[S{Zro/^\铓G*k9 ѣ&3h@1iܒ]w9獷8c|E:MirʹZ+w]bt:osdr*_pȕ\dQß!YQpd[j%s(EPU&j/?!FHuӧOSJb8=9ާy:c F[|PAH4! TQϪ,MӰtCn\ƱA*% u9~QEXk9#of}Zk.%TJcMju6 ")EdcȚUrK|^rkwD4ǶJ)cHE ׭cb B%=留md5!s-\+.vvsC:}:_p4 mR*=T˧yD$kW)9S)TfvYkr5΢STѪ*׈zcydm-2MyqJQr3Ɠq\y.kCAl F8k!yӿ7\&hL!'8Nud? ]SJ<~仡"dhgH@TU!]BT=c/ 2]?CD">s;@Zwl bC0^ePqqbf$ 1+=kiD罈Rι:(E` (}S{Dm4MVE0c]g}ݖ)\')唸ygm {44J!r1\Sq̡_W ׺>Y3(@q^&q)va"hΥ֊hh!b*XUH4SMSʩ B! Z$1>?SSJ㢪|00N= ܗDNj!{C1C{-ϟ-sci唓\Oa~~wrli.T2BF̆!A$I)y{y|4.ֱh`y"\jʼ|W}!i2DGT!mCn={ YqkM)*1 3k9 hX h=*Ui*+4JR,_!~{cD$\*[c5Zk?k-CDJԂKɵT`ֶ'=r9uCõ ql4}Οio/a*ӧթA`iu1U )dE9< SΥ D4qH!fc,`,!USJZ3J  XJ:'$C.ȿjO)_.$Z,b79Q{<$fOvGDel6~yYe:;sއGU lҮ(bg_/lv''.}^Ǎֵ5./Dև4,Z)àyGl)i0~h4kn"B!wRu2^ǽV,9.T2R@D@ uT' "V$Pm)xbRR%e2C'$  c"k8M9ep;ts5Rk)f]wF˰_ϗ~|l UumҴw.a, cDסjrT++G74Di*h  v`ۧ3]kJS)(nxm$,ÐuY$dEAtfAAw6mf>? |m^~>_t1J)V'''bKiۯRJ)T֍$Q?2pI*yʠ,vߥ p ᚶ/dqcIҰdJX&Z%jԟv}}w>SN)MTj%cZpc5CEah̏cz뎤9KR\H5Tn&Ƙ5M)x\%ZJAb5I6"csmTK11ykݵ]UfQuZ{2 ,oFOSɇYf>|kymwbBOGqcګ>G 44ZizbGHZ酀54K\NjDd'bѓABedپ.v]ч`+pq \VOO*|w?y0NXK!l"aJ+Mϵlj){hp#1b4֌8AД WCk"FƐuV75KֺnXNV'bc$"a)KJIEG2:m9k 9B 1Q.ewO)ZJՔ1|Z-VUJJ9r{=c͢0O㮖eϽ;PKI~]օ[:cګCGU7<c:M٪Z7n~mnr.9Qi?ܩzƘ$_sb>+@W^u]GHs.g_}?}mw]G JD&Œ-UcmߕHveNBJi|?La+rۋ4f]>!v:[4s9oƶ*fY,fݮFwNE"iƿd ^Q>>" ;T#Dk bpUU-3Vh|'!オYri)2޻@5ÜWeRJ'c:F]!lLE<7d̓g?WL{ָr(3`yLb`'_x"yDaTU]ߍSʹ\W})y썛w,}:u$"} I}gl64-sEs%³gO& ! 5J΄M86zB MM ʰ/U]W^x?LI0}$TҔi@tκYd3ƌ15so?-mEXvSUCεF JskB*< _/ŮW\K!D\Pf]l,+D4\|.>#nkYZCog*\c 9*{Ԕ Z m u]@$UZRr[̷~\i$cC1_E4n&ՑHnv#:üw}1=A]"R5!z,#oZoDC1*ʻOqS.#+~P4rJ!O6mv_kVkba1|>oʢP 3!w5,}vaX=ah"| ZETa{^AbED5ZO۵8Ǜ.159f/oQqSuaCʸ7#iJ" ε UTJMLXc5mMu>#QPRks 1xT10{ou+8N{8}C{c%"o ׮TN㨪5O@P)i*%~qLJi]YDcg)%6/kLXY R8GJp ߖnЕ SJsS~rpD+kOzQs؇XcA A˲}w*}<۾GsZ]J\'/s|u&cMT g|0YS]" Huƾkk[PQcޗpy_J˰[ z$&!t#=꤫ޏ%ĉ fR+\`~rj9.11wz>."kDʡِTUh6KHȕYߖ>pdZ$plѶa9'tz8D Tm(zQܭoDgJBy>3Ґ-a*hiY\8@ERlH; DW*q-Zgc P;ECBaP-zc0ף3ÕϪ!"C 9kD_,n]6!|݉1F$[ j]70Z={{2vTŋ9h*K;WR.Og|\%vƮ 4~b#/^n_T caOU)춻\ 9XD@ιZ~rnTwd̰ۏt}{Qfkc6;k7+3_d*su]X^7jܶkZbl@DtZ;kDn?\B ~Kk y笵}dPUi_pl:UP: Wƅ~ǣiU^eR0 ?CJ6n((0VcM ^Db@|&r@ˤP" FcZk"ڮ_>ރu}Z1zM7m?@f r}z 1v1\5@7m1BU a(S_!.]l.|E֜}7EE'оǕc/c|\sd0V,07\ qٻ!T:p?"Zdj|똔Rj.!nxxeaal -0ZZn܈} @ҼUTrΥ8G.=|AGh=xk-µF$hs-eoҸǫHN]CUj˱l(FótAYZK,Zkr,RjJ[,g( DXL bT|p-Zs.a6EBRZrxh];}*!x^Uk41gO7s@zJ&8J ]?}D#tKo>ӰovW%O\KJfw~Zk /~y a/,vS18G6ȍۃ2|H8V2i}("CDH G+4C6YK%OzE:xJBX)R}bS| ޹-\k) Gkѵ`UgRnRx\:M*OY V`ĪRX:b2Ӱ9DXΚ6"*h\$ OfJXV]?9ququr#R[|C~wXc[r]§14ϗe)n_٬lKG0Rr.>) 3a2,'_/?O/ԏ;,@`}}xtDH9Ґp'SNًѳ:n|ؒ.`5j_9nf8*<4Mctޙƻ{.9b,޻MؕSJ)31u\a@:$.uF!k42Be"cX$H[CsGWSׇ:1~cχiBǕWns_.X~ U~ejAMM"UkEsA'VJq_ dgi\A2V̻.΄DX2 TTC͝iΆtl}h-|; c|>Ky\.i*0<}g٬ϿO>CKʹ;G! n$DHn/rCoF ny_OE `K-iLNy]<}"iwWDX?#XÍֵݭqկg>G2@7EDa)8 l> 1m&y[Ji2fj)0!ZVɴ**9̹@s'P[ǕVd%QϪj{ogT^wB8fq~zd ZvͰ_wlyPK /#Ee9צCVa*bUbS]=N:epϹ*F㕱ȕ TJɜ*՗>HdPx6튅e+$}m94~1xa<...~?<1uqqۋSj-Oϥ8|~'_P]xr3ㅡͶ뻦m2 @!{ʽNJ$m/__8+<_ѳyyLxf߿Oƙ5֬홲*FVqc >rv\ոң /\9^9Ɣxs6˜Y)X*RJBaN¥sAĦ[.VˣˣyӭTd9'U%< HثUfD)iĹsvBz4N#uaRJs*)Kf(R@qǫfAFΓRX GCxeE5]0@%7ؾm㜻Uko^Z?GFR9U;`UXKDfOebhݟm۾|bo;cr?oԩs\tM{ߣh4!BsTw|#p?_t.BN"c8*SympHm۽=I?HdGvo\ǢVqs&cb!!8v{sO5Dm?'DP MpVO,9Ͼr<жI8NCkn,|M s  \<횮_8`GE2Ӷ[2%Ʊ_~eʨylY28L;ugeR !*K)BFQTl%"+P!U㩫V?;-į7:WiY$%P2oWd[.GG''ǻ.$)a~qZ-iri6Sw% **͠[%%o!g {^ (Z"sJi}F@f^؏DsRqh}4(PK;9ea6BMRJ)$XtU \T'$ćp_ H)Հ[8[uT_vc]j[ڪ;pܴ*}cԨS+Zt slWJiƜ"8 ʥpD&a).ka@_G)VqHQAYYR5 U1Qa%r}>"cK1DAJ-{pO$BCS-묱\ԓRl'<";wZW?,)m֐yd\|ݻwv]m}ԇgu (~!,0nE^}?@Xߌۋ7?I)_O;]ܥDyZ=.Ccv\{R9>b #]&19D1M&ܮُXH~.9l;ߟu9y^:烻\]f)4i; K3sca~奈:X1>¹@)Ő|O_r5\@5GX;9 "kҕʯҥM^kH檗߯A2y9r9Mc"]?6߼{gׂ/p 0c˅P'yZZ.Lޤ "wm<_ݺj`@# \R.O3[#`_\rJi֠+_k',̡ 4rsL)otw༇U@wU0!Ctx/ITK-{YMrcR",\ys ꛰-SnyB{0.ȵ=+9Nڴ 盃<du}wvu{ԇ(8޼XKM|6Ϭ/K۶׿2ܶmߵ_^@J̫͛W/ڶAj `ZDw[GE\ؓ[:2af%QlFʸf{_tq߶1ޘMx߽)4,`-i4୵1i8q)MyΧ-4Dns`>ZS%k?e"{ժO8n׾]t'İEDJ)1N;DFʥ bu>^<oiH0m/w 4V8"by?vMz4uZ nI}v;FUU8g,9Ť͈7c5}!' d s4je5Gj|09.o <.fDxG(.6αL~>ખ*v0(xc@L1o\~X* ?:h+2fyX uw.p0KӴ!xL8ǘb""4Ȉ:v2tta撙=V>,%8nմKawY04`e)c}g,zSӲ폏O3 9ͥޘC28c~!-%RDjP2!M~#(91sa,6S=>:Z-^sK)뺶m>XmA( ̺o3*=u9feႂQpBT~ׯk6g?_^uAx5?<'2XƷw S/ Lqh:7mC8l~-wۭci ֆ &A8gÜR!z3)M UZF"cIs0K})(R1yDf-~ *9. Bw^oSw*< X+UK*imS:27M4d(ܴ͋wgaaaxkEGIgXl@U$']ozϷtSO*c*5kM3+'sOJ"XKQN$B8B.2v[xHk=ǹ"8 )4a Ox\p 1v\0MZŸNOcJ9\ _>:R,P3m"PRVUݍނ߾bDh>>%[k?m$c7Mե%"u5Scl4)@d$ćWݼQhCAXr>(rXLn MN*œU:w`G8ޕ;y\c%TJ\mRyf("VGS -wjC|7yTP⼹|@M4֫B- 9L} R8욶}/|ٳ~xCQUw_DTE--;u7Ŏ0!vN%@E,+Pho@`htY3"uߨou?Du?mg73@H딒oZ,n99Hއrs:;{wrrltTEKB2wd_'Pw"噹R.t_r93kDtޑ1>fa88"BfLD*JGclX|)ͻbuGzQS*kaPiBv0\Ƙ(ZU,3\< ~ X#Щ.h}7sb//ފ!cnO\JI7>z&sqmo_M*78D-U,VQu}WZWS)dk]-ALd.KNdo:""3{8|_RӶ"GJ%sF"k";_ C&@DҴ "]n615Ïzۃ2 ~.AFs&^GvI6Sߓ+Rh)x>'r} d뺅rhꄌxݛTsӤƘmEa!djY)!zyWsBw&B>hoX>ZC>4޻fCDdw>)Fsa*4,Ѩ|[j1HBW @`a#bEF!WCDcmhZs9ONvXs{R 8cJy.AzZ]fM!8rNADkR%^uACr#kC 㙋閕͐[9n sv%>t@^UZ7'M)k- }Sf.7}u989a{Q~eݾ*9M4'>Gc9α$6* FP:Ǵ"֑kE Tȓ#54v)pn^@02 <'X4'$or/JQ#Sfs( _DX_~l6M)^|B¢ιb4~)ke<0Ymփ\l2)I}|a^)aɥօh+!:_vo^sjwV'i^ h.Hιm~_(6̗!C |^RU BZ<oVGs)]"G OnVT;2!"c)ŔSb2諲> 96ݲҕwsAj'eVǬsmMw[i97j7js8owaۻV%J([]Q۔,1a6%tKq&(h)+gfVšq&X9v8@xq,̚7(ӎմg¥iúOr0.i^8M1euEggϟo.k"uD+j˫o_\&]J(}#GlXXQ;>0dBܯD8tROs<ֺN^Z,Κ4~"Pmb;X@Nv q?thEbE}B!0D޹~uBy<wjp(~h]viX[kW "ji)#κ,H)…1''ǡiTT%zpw1\e.}8rDp@S4·=̼w1':*9%EƇp\.qDi!>fO4n;X~-KӰȊrQ9{_ tH,qjen;]9A._eڋy㜧1sUcfs։ ;O{uvϘUA?Hd}B+] ?=}>wj`^^^za0 <38M] : IDATI3rj* xQs"Ր}[/~Ӆq^$>_~s98 ;"1TIhnK䔹Qi\.>Or)"bȐB%\O..f*yЏ?7T!9Σi{shSJ ۘo:5R)9rI7Dmᩪb2eDd(SySqN5(B ZDƒ9O%]}u-\):g۶`DH@´Z dȖ{Z}v|1sDS-檗d hYUS[{R0WK]۶r*lP0[5֛G- Kh{b'E~ *\J.9#481XPzx5!2}6D&bn@_𚫀^ߪQ[Uڀjx7T|urvJ,u!Wy4mwn]^nz{Ni"X똋08zs"}P Њ,BUJ)"XWE福uiKo8#6׹I Տeu#Q%pNsi]- /*]J*ygPC\,w.BECݫuCT99CnTV9'2<Q;LH,(H)Syk,B* > R<Ŝ{``Jes@,qF/m5@R՜쬾~˾RdU6^I#"ڰ.BįWN $;n7UXhy߱/"Bg΅?}7ն_y¾TlBn7C=!+ǻnb)Fc~!x(*ͥqXw9m6|؜?⨈G$`=r'4^ж`jӰ19]K&cڔ"!I*!MP9UiZ #(i K@}@K9]\lƘ[Bka)Y|ɛ@@2 boH< 8sakMxK3gQ;dܪ#)RAi \S@D~7 {JU0!&D QzIcbqrr<3O<:^7p00uj.Xhhn7`"cBh\2ry *@͇"Zˣg%y.kzؐA >OWOIIU;LUlݩ`;PJ9E2|-Hm 9yurXf"\rciZ]<8Gc}hC:SαH2Qj @"eOk\."521""j=`@ *V9!H)xM""$*""4˙!!)k)\{Ya*r7uSWn .Oc2Π1 C%AXDDue'B"Z-OOO߼yRJʶmDv{KGU%4MS4m׵MxXAEojH.k`Š*%yYkҘRrKhZ#9<6g[\rJs*HzU""t1iC;0D0+_R:T *h)]֚81&h aߔt֖d,"]j@@Ds.Ţf̄蝫ef R BBCVJuA$SRY"\%'|HavCL;opGCb?Xcc~Jiޝ9xy S%꽩fynaRN4B`1F8_|tҶ|{}Y9~;i1{Z$t -94׋PR2"Z;z9GC,/S6vN06&~ۑ,%4/i VEy^cYI!js:40XcmHh =_[YXJ_* "LDJsق1}x "_(uuD Cp֚xȥjm斱u]N-"A2Zq:`n'/œbijD<]q,@yҵa7חm6M&,QDTmXeQ/*tL%ʇ߇9};#c?jQ Q4D!xB9^8=;;g 3ǔ޾{Bxvr-׵Y0DuM_Pu:06mŏ.4Ÿ];T8bRQc R4@X5<oURyU+8nۋ;%'P9dď("E" !X6֚ C2s7X"x(Ċj@4ι DEx$7T`+ɨ"`myQ2$γ_V)_@よ@UTlfs1ۮsޕR83CW5O-N)4Ux""T=UG&l$Gɿ/|+b U K⬁!ʑ'0cֽQ1D֢9kZRլeFc%P" HV}PizdT59ˍv}3xC4maKal}]3MvXq3auh?4qWRUei[=" sJqsmt"1N9i$W=r sJ:]pfD4MヿISUĺpۏOUE k#8j)O'|[pܡr>)v"7؛]`14m.A`)Cc!58Kz[lE5I$ZCPD!{$f)Ym\u_U~TY8|=)jeX9c }!PqĈ/"wf_zUmQDx ?Tu^H"3( g.X1ݝDֹ~ul[~槪Ko:0]G8Tgf%۶oUEȈ TP6ԊޘCQPy<)s M^iKNv:L)1&l |mQ ~mo}EUP [H 5 <HY%_=ܒ0c\o\K/ؗs.`1yyf%k֐HNYU}>k̭(}->h+ۂSRUrN"R-LTTT,z'|"K)h'yȐ~8y m[^|۷/N]YU QN' "r֮/O?f33܇Ldyh7ٛTա-Y.ymz`*FKkU0E03pBUS9rqxQ͹pW9OzPUe1>ѱUsJHB3ss35oiRZ_ESi.MaB}Lxꍕp)k&E뺽d"z"9Ug];'8VT-.pk-)L0H[eS|XG<4n9H)%B ڇu\HZ*"J*D"BjJ=N? 2y:hil6XsU)m{P)5 ~}6ͳVzsnby˯?}#.h0{ߝ}崋_|W Qޑ|t|0_nu)ca>͆.fS)"EZp-Z*Wu5"DHY@rsmfO0stdNatݨy^ZB؉H->@-")NӴ*K-yC?9꜔qkqFp"8K*(7#KQ!V pGa؇"HY(C'@ fK1ƌڱZ{ccvse0!FimSTBF+sƚ>D"rֲe)j:=}f2QD4#"!ޫMAsI2-+GOO.F3&0M9 t.ZkkuH6&0n#JH,AӖo=Ф PQ@-,@SkZRFrH)hSABuEv~T=$DkMaBUIe#œRm?;?YpM1Κsw$F}r8(T PY#GBDP "E93Ths.Ѯ?H$VƂPJk[^,g\JIR1m қ]8"ykw0h}7`}z4fyl\,qz$Z.ROO/.ى},}$J!&T]~%gHJΥV@'9apIX./@QivW~q){ufP)M{%ƌ,Y""r%nx_B-xNJ̹R1wsÔb@DmuhqK.qP\,ARҼd.bHFe(RXٗCZ%XVvِ6dIm-O4YRSTD;( MZi "*+,,k,R=C >" ҜfA I9ǘrN9)mJk4*1Qw]E_b\,24MUbΧ///;aMbe,Lk)-_?? "%c24XKV%Tsݛ#(ǜ[g"F4,jʜ۴nQA)!_h@T04^]lZPHny  430KO-FZsX?,!ic9ZKIXfI!ζcq}Yy1nnw|ΕRQuj^x~ IDAT?ːyGG˥Z݃aECA7׊ Tkm5)|VҴ[qI\os"imhu7" ݸ]0#~إ5"P0G,.13ʈJaטPԛig+aa(969E7 E众*KJ9Mu6I:dhԈ*<1S*"`{1XmC&EC,Ca!rhmMƢ!T<bL)Rk:ogԘ~cZk9TJ!7gbk  Wٓ"lzO8ADɳv 2Wb1<}Bfl^ǘ֛rmAD(ŘbZ4;O? sgo 5Aj-(RJe1mYHe\)e}r&`XEDں֒SH)N5(JF2uhmn`0af,ZCDr.r7\Յ ~Xd}FXJӦkM1qpIqEBDH#cJy .rbfԨ9Wrh[WHL-cniR3Uk]J Fkt p"|A7Qkf;SʥeιhG4 5VhF3\B)%ǡ2Rz^,Jk3v4{aεW^R/h=?[0 FC/}cs al7D҇G6;6h6??bӿ7_XxKf QC!cٜse.E)i>VJ.GON?Sk?*FZY~hZfFOD~0sl!Zr Zt~ => Ҝsn<`)"̥東w,f$\aA\K!)I+Jn^wͮuishiJ‰I}eD"R^of7MX$s.1Bdff{-/@41Wf\|a/@1&Dyor"9V{]6}K\>Мc 1%RTR'sJ)Rvݵt+3V|L+[.O>9n*0 8޻k/z{_ۖr)swFZJNyzAADTkI+޾xyd:}x|<=UZ4_-1y:Dy %gfIn|RW#4o;T?8,OkNy t eN@(aat`RKE$k]o/$lȥQg搖 3y1)捚SR=DەK .T5!-xL)!j?iADL)vV!)SZRcJr{Ю m}!i|H"@|B&RK 1XJW)/JQ1[BR}3B5B)WIZ뜌Rq!ZK sֺîYbqzzBnvzDmDF?dGiqF:ezK1Af.@K:Ly!̳FFw˓/6߮.QJۈn/v"YW, /Ҷ|CRJ3.X{3,\@>XKSRX>˵vXGJqל"y>^g.n.hKM)0q]Au"-iY^*;5K?N3biV^EJ~`tkJ%cHbBDZy6Xĭ--L:ah@8UJ9a臡Yom }[{/a?utz#qq Џ֬u_|˿f2춱%$P)' a,Q)RJkZs?APSYX>5:,Z:κd}w`uUR֥1(WAsV3*)1eh~T$9R 9?^\I ^tiJ9+]ߧӾLmn"0|bz)|yy^anq+ݶD~xD"1Fk]JEq%K?oׯ_oÜjhZ Er1ƔX猱7kklc$eS6כy|kCuGONvwsg_( 3B oo~ZkypZVJcL|m "%L#sRaRo/5j)1%RZwWs"}x彍[nDy1v8kn o"k qZb|vrrtt!jK)g^z^Ky}v;~r [g:8fS(ջ%J+6TK s'Aptq~ PJĕ[{,lQ(E舨vWcj:ƣ;})mNyyn`l?=!r-kfca]v@)D~[#> fRr)Iuv1TrDc6oJVSɅ6Y v tJ'?7 #+}y{-? \IQV2?\N8ơ7Z~"v95Vw7z$+"a|hnj=}h.)":?}✋1r{*כjb&b155fp;]HkHJ)PJy!易} ZDU wgmrNs-9s.2r 1攙5焄J6BZ*Uzu]y3.=}'n-._"V@ׄD.̙k?O5̤1C'D$9̓(T`֑kZ[m'ϕ tJ)) 7F[gVkC+J7FTN#", ƚ+ū#kt9T ^pB0bƷ:osH$"UѾxo95#Yob^~O%g`Y.ba| B+}abw] ٳ}3yMs>ɻ7GiZ{u[L4"jmfJ1R>ՒQ+e+c*(Mɿ~WG}xGfɥNiْR봾۔ֹnKsH_~96]};O[W{/KQmBzϚIkMĈ A8,R24 %gӼ[6ÓumDcR-) jJ+u!9P2ka) jCTJ{;:^q P@=sY8rZRW3^DRh""ɮkK-Ek"&Z5FIp+n?CRY)ueG/%7wN{s}Q3#xzz_ޮ?6xN9TiWw 4@EC Д?쑚ljm ;}̱/Fԫ]L9s.Ш k;c>vzs"jεYE>ҙ?#rva*&va9;[gל;cW1L)" Waqf˲lp8g&~yRjHh#D"buY޻tu}@4Z"e"u?[IQcڴa|ދJ' "ZRJH!611昆q709Bn*xzQ8/^3~b9||@䍝WCDr!0l3Zߝ6K 9E O#6}֙Ob7\#$?t l[IiY}\Y]|gm;fv⋯W^NkD ;XWZ h+h2Q5Ŧ %iqiym?,KΑkqw;ks.Q.ƌZcODb)ɽwJQez;G ,j1gr9N3r@[VEE@Յ}A^bVj-8o,:g}g­4ǁgR;R*Ԍk7.򵖔2ghf}J)"9!r`:78 G]w{[bqJf{X&Z s1@}GP1l*춻]P~2.ptMABrfB?llC.uu}dUݸ|zSWyQJhw:;u!BN:5t,G:Rw#ZKFFi9Zyi9c%t\K-+)msەbvZ2)mHSd#5gvs; kMq%NT.DwV)֔RJ.1&:#"KN52<;T*\(}iRr%/֙r@BfC%* +ac:\J1HaYD)Jpyp-'z|nh1sn]zSr wu'."Zk[s'_=;9qXR??o⯐R`i?~5_g0~f)u(\WuqdX}O f͹|kYT+Nj7_^5VٻYcN{P{fH1RDwsh Gusevw n:qJw͙[瀏r aJ9 P :0Oaڅ&&Ǯ&DL*(\Q;[+ϻy:gA?(S(H hEO1Sh&]bD5IpKǮgGv֪V:X!֪zgՃ=Tb8;?Tks\'GF1kfRyj;hlm- !9nRKqg^_ u7 cczu.DTDxjW w#\R:o7Y$R~rȕL;jT[\~aĭ""'ϔRH?|/_)ީ U 3.VǃR AwDSk->j f@%iCãjb6@d7*ҺMqG)\s &voHZhΆQ! H\+@RϦ TVoX)6WST"R+<[Ta\5),myKZA QsvZ[7R6̹#hzr/Rn9s>;}~~vXcDKH)uT٥z,]ح.v* )G6i@jkVa͖Hc6UaRlgq\ŠuK)Fޟ{z?|qgô~v$ JE7[gqt2,\^^L=7|\w+N ܴx,on׫KfԖRaֈ1sK5XjD'$I%.Eh"" 皒ϏxЦEYH*Hns]^EaH/EpN5%_ϸy4@Dq1yY;c+D @"|yZcK bHjΞZ)T߼(EQG}AS |ϫ<p::s/R"rq~vZ3kmbJ)!BH8suJ)fNK J0wiϞ-'\\qhOewVC2䜧R<">1҆/_~9tV~5Hs\9ƘRj~j^NJzvH@kc}ӫDtwȵ,/^'4P*~"SwfƵ~@$8ֺU0=Z6㳳oެih\,G缵F֫1B!Zy|Ur$yNM^!/IFk>rεTcB)S7Z-UjŜJ)p-}O/YkJ+HsgQ*~JBJN`hg/w_?~l!%vFm ]^\qw ̥R*kLkYkU~&`!̥LX7TR!ᩲiĘBL()xXj I9o Rj,U:I @ {*Vr֙VHE!Zp`6S; yг3GqZcɣƺ. R}]\vZy93i>Z.;S)zX<;99Xrnw0#8y1~YkZck(猈L,,Z**%lSZDV+I bR]F[3@qjM󔌱J)J {TK-j}!&58x=́_]2 c_mëz?ڌ=1pTsBpXXGFJ)Ƙk- "-(EWecCZe?בRجY4) IDATv}|_詈JIJCS28rJo' '.@R \SdTUjdaV"$ЧTP("H@ ¥B'-`P\J Ƽ0K?W"{||kb[iڕ_ic!}TFKOUhutܓ"Dn7)R+~Z!Rԅyb9'o"2*R8ghiA0,Kk'(y9?b{ħތsХ0MVC2, cG/(\zc)oRJD( ֚Z(g͂1|[,uA$;瘇vlr-b{L2VfXp)n7GO^txz"R+TvF{g jB"0dUش*\m~ L1^y%R*J+gYTIHRfN +ʵ g_^cgkK;<̜r^֗ɉRYJ!ԜI)}q"B |4YkDXK"!,"E':007"8]ϵ~!"|?\)G 9(Sj3wR9Sk T6'ȜKNsZk !֒Z}ޏ@evAV;TTEת;!nD>k0:V?0vUd^КM[sYZؚL}#EV'@DJ-$aVZn00(R"BJc5J-(t3rD-cҲ"@'2(VF54ʥr%fNB,$>q3 ts `!R Z \x,}qޟVWf\9qhj-zjI9SAi0Vyo *D< : p)EkUJLdkoZ9"!csֵ1Wm~YON*y-M;@h&w?nJiͮBD 34.8^|fX_|F>XCC '}fR2#sn6޻8mA{ר?ׄa}vykINcz+s5HVwyoyxП!D$sBuz}ci^)Pq70;fN)4s| d&c1J URsjJ5UPS5x9P7z@;=$KG|z(E޹b8=Ynwai]^.v7l6XǣDf D$F0Z,^R^ ̴Իk kw~M "_baIJ^ZS*Xc/ӭ``)GiZmX{gъ^OH ~ධ P)ثQ9ss;> 0 "+)U\ 9Wf&*fgdZJ W=-'4iVLs8::::Z^\^966+"|~{I_?s)KLݷ#3=y/%XK94O970.s_=;>A2s1Gv,"JiugH+zVKa\]ToGu~s7Ѹnu50W.37e_}0 Nz{\o^\l\,@)e RˣnÜK.M~@LR=(uYOt)eB $JWZ00S/ЖCq\nX\K.*1"gH9:SQ7`(-<*EԚs!w`?$qCH̥甃c{/K. S1np? UDR seb&+R-ڒU "!"T4R#x#>9KG<9?o9狋뎏;},pZRBzGkJ&V9k>{_?}pz կr;묱NDv] @7EZr){+4'QidZTf1 r)!DK!f;O" 04F)䔴־s=3ޏ6nu]_@z{,_ *n;F6Zvm:%W.^' Ve/ӆKaDʥnEDk%aky<(kLk |ܘϋVTDu}B΍88Z[- ӶnR1og\9v+k$-0FEb~T4̜K- TQ.d=y}Akb<>:v)ˮ~ƚ'O/+k돏5cnr.ӧfONw4̋ҋ/cgR2 rXkYJ.aDX,wg$ i)ZI}i ERJAF6Zc:"!Tcڼb,sw"*mlw9'.7y̰jdjZU!Uv6HZf  KPA]3CWsdRkJi.)NZ+W&K3<(L,Z!""f៉c3her-y}"q<>>̕9`Y,q\vZӕ>q05}bW64^+`"gsoRr7RL\ 0IvnyYk6^M` ӄTygMcmKӳw wnݴ N؉( )` $$@"J~D$( EQ"!)C6 41Fv=Tua Ƿϩ[w<Խ[眽Z{{}w#%D@s\F"Eʈ!wVDZ*"A>pF ""rD\sn/p=2/οPs9nE.v޶<| m?gOv BKj9WTkalt#ڡ f@蘽?;X9'!ƾn1MQu^կg@swbu:OMrp|}՝nG+X/nO6_iT#"0??Mp>Wo??)_;L<眼\EHU \ ""9FRLTEJ!0b(;=ץj^jD_79;M\ySZno~Qi͝R\ |p8s' /S'?_ Tff`b&*F8q}% Q؍4[,{ϨV X~.l (]Tk1q%16uߙسqgsN|mZ̴)~~"u)LiNs-JH*i92v ]]a˥s_~iwq+e`ZURHddB]M@" rI0w=xvaĹIDT5ƸYbʍݢ.is;$^=r׎??}`53z !@]q׏]Wզi),HT6u;}{dr"=+ژ"!1zoDFgLkrѻx|eĔ*Uߺ4bׇ#˩U5M*$RDEk|N"g].\B(D -1 9jq7C"@D"K={RjED}uYh [,"8zhPWy '/M?+W`o޼liu͑\M<Ϫ: %\gs'bv9ounXj?*V q=@[xS/Bz肿{fS.9P)4 |9_11h"%S q1bIWƃ!wD$jҍZtt5=2ZKJ)MRbMHy+$IAAs.x9Gi'&;֥T>F'!PSUTƚ!SȫSp3:Ls}{3I>`s0k*BXݴ^Jmyi*nl`tLαW,#j5h D!b |X.4mk" !}tx߉N 3^#i bhk"]w ܸ\CC|Vuk_r3?\|.6s-=p:yc,TqUΟsq f>xsޱiM!o|aF$3ct@++}O?WG#}o<,LUJ-Ҫރë "*&RUB9l!DpWWy*ĭyD"oJRBX.W;Lsp.yVwm)sM̳f 3}7Х>yk;5(JNu^L@55*uVʥ#USmȑgd5tr{SO{f=S+USznnvgKsV5bQZ.;F@@h=9}f !^z:Ms""v]wʕ#8M1S1x? =<1Fpi}lubyi|pyx3~s/:=rNiB5jZSnyhD|jPkmz\*7BC_W^W-}tKoHi"ru7~f0`e@\v ZK-;Ǯe=+߾XN)!bhO$'Qh8s䜦3KNρ 4Mc#RLE(GZ~ܧ?饗ptl瞗}+/m[v3rfer1#7 IDAT/{U-VevD\] `ӴnO9Ğ331ml'Mc (}eXHi26HEN{"08w=Q/"RZ](f""Jλq$Įb}LS+xk)8VrIDV u\L D -UZ$||Mnn +nͻ-,/On̥HDsλg~}m{kCo 8nG/?jU}]ϳ6k9Ao9=vb4"%ͻay:<g4ӸnqWw22 *0sއxGWQrƵ.=~ļ>a.""$KԴh˜kj  j#D޹fDc^L:]ޓgf|6BL ?D9YӴYEBDkɥyN1Fh31D3PI)sJ]׵YKڧ9f)˜kJH#gч@؍Ӝrp dt,[Ιk3_oĜ3i Xs)mDl>@fiJ?Gi}>k_:;5x~+|YğS?}CT f ޼mS4ٛŭdf7,.wEjɹ$"l|46\ 1133䙠GP_w Rz*e..RͪLHHLd:woBd~7]@"dw_=iTBbj-4g>8Ӌ+ƍK)""fU辡 $"4ҍݣ|\?/z;I)73޻_FRz}ztN97*u788=ݢ[ؿ暩V'&Mֳ`5\<V""瀈NH D1.SkSZo6Cl'38880v3/"2"!*}`6Sݣȱ] 12D"",%_jtC<70tq.;|LE߻nSJ'{o-rCk~Q$Y*4&33;f&*|rdszOZij3t}yώE ErߚbsUD4ཇS Rs""!9|9@Rsc?weεHN<ò:~miѴ'YSP!w]9SKvZ2i<7uM_>?b RT\~|0yvf])c(Uc&tDM"Y`ArMB;5QGLH\՞`h IJ"Wc ZAbbC|B}VMb4Z. ZkU3hV! G.1Prj r:~gx] "9fs&cuGrv;6;OnLӄԜ^j' WWPCQɥsz1^.McbF"§d`sOK'|v{+W7x: DORϦ|W1TiJc!Y<_6 G~_-??usB]ClC;fvMO @UEBПNSͥF 1!v] ЮsɠZ/inT9COh֓nQwpN+Pkw*7ٔL,fF/r-^^޾|rdG6k5p !8DQ?TEH\0LԮS &3U4'2c430Aaڛ1NsʩѯU˓b3:M :Ti#r-Yѣl>g؛S"Uj w9X,vqioݾ=3 CRZRizsJg!3H-֢"]ߵ̼X yn~@8my?0L> 4mַaqX)$jpSԟ~_GIR s{OL͘ߔ?~wp312n7h}ͱjTfncTE s+tÞAim˙gɊti6'ةNHPӟyrzsX=я[*jjb00jb[u|DZ0}Wé@c R@JU J)Vw޾љAev9k>b@F?.jZr}6ۓ +;S:8ɜ_;=fȎ}/ޥ*cHDUlfq2/ᕷKD蹴)H"EjHTK)a!.Wy΄}NU*\B^ "sRVc P5x"v'2PӪm!#˜rFDuݰ ^50;y޺O}Z+O`{F~9^ Cų ƀ 3SRSb\.b {77|\KKFo#ǩCgʕCOD;ZՙGJ) $ bbfs= >|/_T'Bdƒ,Wq;DH`v;Gc;J{S)Z+00,flpazߟ,RWauv(izه_{ EZƺNw|̔B&"5cUTi>Y#-WGOT9 @ЄUV'"Jqqv=ehfAIM'#>UXEaH0CEUT$2 w}1w $U+$31'{f dDCh >v=O+IQ$bT@C3/' x7?عbuיvB@{7" !)hc4ѽzaVt @Zi*!.= aZJ.?--U@cCt!;IyN ٹLjͥ9yN{ nOd{@c?MçM\ljjZH!1s̫{>9׊{ _Z b珏b-4ݮi%o쳗Z<I2[J8E.~]P35#b?bD1Ҭ<>-e+01IF)F0ZK5+ w5$X!G猿 hUQau\58)sQ(jTCƈPLeO7)"3u{H&&Yk@L2y>D#c2Ӫ5ɽs-QU :v"*cX.; 8MfX8ǫf9YAw3(%TrUDBP;>0?\f Wc(~΀^ȀfMBp>0"_Oԍo>C&} _KTp?w=V)~ꆔLPB=5!,!vZSe3NOOOEUC]?]j*+91;|PLFw2NZ]7WB#ƨ Ւ L+7@17 jy}ڸU4OYyRͺL4\Bm\ju. U?o р*hL̝k35VCjTUg  j4fm枍1osLӴlʵkaX.Cb<ݖh07uE4cG'lfPJiJSUQbz 8vZJrhC1 o.LUv&!bbJSIllj?g>X! i.o~O?E2RV6U(ښJ|Q'>qRa8^cVhZP YnܘNnpֲ~kt[PRaVK*yfB}s2u}W[fVe&wK@F!^b3 ZSsJ@ꆸ컥B<63D3ES4STꪲe 篢U}o PL&#`j#[(@1CFk]5Q> {E!#1h1+F!"SqX4M4כRJOyL@D}ߙuewv *+.k{Jԧa;} _Vu9Bhc1Z֚y+4GeRjufF$3Bʸ.=s vDؼ}H*UJEcOÌ~^I͵dz{@fp /;_|_/] HwyYmGL@E߷- /?gv;}_;_d^V ]lxDu/O۷]p\*Z{r@q_~,fLiܨ8O{30Ӄ/ 3Ӝ\]jL2c IkR~=/07[j͔6C!SlzSqg9oxد-RE&PzD3U_V!*B"1~軮O)Z9m7R^i~:6RKEEBʇCiwDhZǓwV~~\ʋͶ:}E}z[G>*foS55gO~;bBd"V}^j\w~~8cau@ןZrs3yUnzwn^XTʴB.=ݗ7֜mtAhO-乔ݮи4|Ŋy UIQ"k`UC_S+5{R_.S7p ;>Znss69wzRK-9;&cwG{᳟O_z#5M %]^|;7 =A@#4$T"roL""{KS/HTcuf#Z~>vKJNnK! EGDv<|>gjZL2M:) ]dxQ\{@3* A?6\8ϛ̳>zWkufTk7Jݟ3cCJ'ʕ+}*egUqv7̦ibh$~l]KΙCCڈ|EzON b}Mu#wp׏]?#ϻrŰC j#Zj?  '_5/qOs1o7o]׹w"1ZnSw+7-s-2RڏwRrʈ e/RsJ<\K%HHmnӮ|l ly+ۤI!2!Rm~X)!t%L[\Y=@J; ~I.E!676͹O6V< UcbBjjpb 9M$Dd=K@}[;6w{1f3-X0,bw8r.4u]6a-3"Bk)ŕw&C!t4*{jRKBܷ·N]Z@Wj8X[m<,hc;8="RDjgnބ}>a0!<|W/wi>S5+@c7^9lpt9qq5M}qyUT۶[&"Zs.@D{jfk 9%TNWT]gz{ ah he˚zn!#QFV.NjZJε* FIcrʘz T k?ytJ]:rpTDH,32{7]2ވ шHDVUg 79ĨRsڶ!] ț_җasF`4)e{nRcX-c2Ι4 H-&B@DŢZvkMv-4R #9G!b&޽s?s0s]8>Wo XnTźn3f6 nu{ 7DM !LESJ48REiice!%ͱcjg6"6l.1'|YRJi3SXZ˲3wG8Zmz$vf Z@V` bCA{;m}Է):""1z_,[/21@O7}|`&u{N)@@``"ҼE̊n4ӣ B!l~qZ}-gYy׷K|QT攆R&^o&Ƽj]i\6fVsw9n*e[sm"n6[JEk1b۵D$*L_w&RՌ]`pN7+HKU eHH@2g?L}~ ~^ `v VU)EVAxev4TBg[?1"̮SBTsɪcC.젳4TXlP#"ZҔ)*j >4D[l2)Z͊j~ ݯ뚲!be;h&#.YRCHC9qfsT;˱CDL)i"9d"-r":B,L))<4ؠVy6ܶ"\qg٩%1ժ~^m.Rqc{{[bE03-8N[mCE)83o۶i3E!R.0ݗ-"-ŢoqTJ.3"low}j_&ŸS[g̿~mem[4k#ǺC}Zi9 wW) д4N Ɩ_be-+i|W$RJ9eS3i#fbc.+ӶJ"Zb4v|7y/13PJ]Gv4X̞k;#GנY9<gL);ngbIva8r?d=-Rcއgs;+@}gj6֛W̌'f.Rjx۷oS^#!riyc!>CߔχHouN'v;Ӹ:@[/> m-9dTР_c?>9_"97'ü(~>`fZ60ov渧g@jNcfܿΧBJf}| %vZEc]3CWs򰮜`tqR?Va R(wsUM9O>W뜲ȩ V C $B8ƒrrg8Ձ_gn܇WLCɩ<I;Tu'R;f$d4xF6A{o)twC~=q:>9iB 16G8ճSW\KMSr1m6Uafs?=!]v16=;I ~; b"}WS4MH@odoMW ?%}ww^sJR+"8v!a 2Z}|~ =ct`:/_+3I)yXw˽~y+vjISYMNsNJQ0&Ɩwʯy(} :u/nC<|}5k.z*[OSUy6+!hu=3y]EE|Sb3@0*Rce@*Q[3kyJ@ĮmoK)U>>`yjVk%~'ZJR2 G҈c`SU5])Py`>ݥ5O%yr[{[x#¶ {w|2S5y%`ʌM׌)akb;7'^wUv03g;3DԵM6Ld;9j^=gVY3o'E GD4Uk.9&=⬛~m{vc>*"9Mi\;V)$v~G's?/~1;`^rNLs]̍H3T]stD\/7|3ؕl?RTrTrޅb|"(9mVGVsؙj2fILs9GD]u16a /0+%2); HUm/o?ҸVGMmyjmY`{0UFc1n+:y/|6L@733yfC;~df>c1 Vg$W#K%LD!0TtR>8fGt|txvxxڶcb 'Uw;U׫qrNĮ[zRjaRonϟpqH䔦<\JRS#8}99? //~yS:"GӭFUʸ6{]UiڞcMk'yngD$q'j藙ՒsvTc\Y:mc)y}zVh;6Wg7$D{n佛ݪ.FVȞDL̞ c=ﴍ}6ZtcJw}] S)d}df!ƦiNNN֛ifb_mRjɫ{9O횶MGqj??gg17ܮykh⫯{_/URUUM@(39v̎~:xEtxԜX4M˕ef%86}.y3M6%%É1Sm"!f хww/3Sʓ85wNH8`1ܷ]-> p4AٹH`thBl;M041ufHkۆfPUBd*1 Q3k؈8߶gtVY~.\k镧>p_cv( ԹEO3-.rwZmo|ۿEݭ[^o͙`D|dwJN6ߛ51:ԡ8o[-w>>Q=L@>[XQ㔦 "uBhݼ uxwзξkp׫3Ui|n6iV]4]%0ᠧb/k0`9ŪY&n׾Y % 9^>RS.!Uwl`XUiHjT_ Aao14M T:w1mƛUoYl-D41Ġs5>83rx\3cފζov>sU@/+iIJ2M0 wntK)nWxjVvxG#·w>}?F Bul9ǘ~;/z? 9.:smص ι.T=NNn^,e:?~9SU=-$_/L9Zhvq;1Z4s)s_WTʦL>b&>\ɵ*ZMt|T\kޘBRl0vauOi ߒ֒+"bх3U-E^Ic͎U+;l9nXK͹Lz$TX)ze _!K)5lfm^M8-+h7>f-ɟ첣bb\kf"2Mi, \׵]׭Rj=^]Rj&gj)؇s|J 7a@AZu#?0!6~u[X"5kpjiP~7}+~>K,%^iZ؅д!6v1zWRT e 0? cͩ 1,1QTcf-_ R$B8LfTmoIFA]@5Z<І":lF܀HMXgjZdIU5Sz:0,Z۶?F[$gMԞ \+3cϺ- łO:r''_m.~J))v;̆0{naӔrb*By(g/Ooύ' HVr@nﯵ#_GS|y-Y1gfHc&e߭ jɛա>46jeʣ/.&:r0x]p'KLcw^n/>̬iVtC|䄨hV>!j4y^j1-7!c~ LT*@_LnNV77ڮy9l6Blۦ{眪g*/TJq>j<2McIi+yC @~h=7F_o=˟o}ձ<44䒊d &?0{?D@Zw]o'Pf~DKnEF}̬4Un IDAT9ԧ&\'5x\lw%X@͑=H5@5)o-G4 Pף&zmg;IND>99@̲I4QNqAP"i *~~j=+"5KkTdV}ęl"_<@D&!]mjSeӶ]םxJ;7﷛HHD#.4,k;z_'3??VGOE׭aAvL>|`c*%'cb#YQsA3 [ 3+vGR(N-Ū\]9U!q}Y+, >v{3-CfYܬsc/ye$,9B/aSYhZ9f:kΝ;qi8ɪk'\)DHu>xfv]x@A,D]"#5ۿU>}O??ۋ_'#B (~>gM=pҩ^sb-RJ.3]>qiBDf\Ȳd4 l Plەͬiy'uAA8S5m}QZKdHҙz2D=0 }pf ߸bœe70?[Y/pxfJH]P4k6Axc{ל9m<T^8]M~SZ6b0|||{0EcKGU4REr @+ v1nN׿NaM}/_?N7};1b=A/ e?f Xս;wk''/~+o9M4mr{?-43S[>wp~y`v(9Mk۩TdNDtw=2&)XVRVC-洋'1oΞ(FI;mNIKx !91 @T K )"2!iv]]BqȈ22D^>\gYcVD5׋c.Ys{8Mp}\,4YAq޹]7xJH-$ffȻ< T""A dD{%3NzX9;-i$%C02 -v/5vZAU/tS:6p/3֚Um{N9"(9iH*ǤnD3dd|l.d)eו 8D0 \.,Bq[oØK[.֭[DGiyoo)&FB)fo] `Z6"i[;* ևtuJz+T]R88{Cܽ? MĹB,X&G!>~RjdptAݣ_"՚ծGeCȎ< % >$1:Z `άaks|AD>DQ;9YXKiDjmaw7ciqJJ͙bv0Z&tqljZpZ4PtxװS>4 dG\fZ4ef(Ɔ!9H)HT@5{_k-1$bBĞg?jnIWK̞\@<1V1yr]@ΝV}_o|~@ܦ^<k:!is)0l6\jQ69ã;o;<qQpݠ"$1< i6:8|{S;]~5oUMXje|lb̟'XM`@|Ƿ9vmAQ^E)eij*>Uߺ/n?Z4a:YՈٕY!x$O.٬UC~5W@/€1;<sc `g=Ɂ4 9WhڸX^հf ?s!lhv~7xV!*0)mc9瓓w~pT/z.ԜtMM!ȹp~fAM Q * ͝xNjJjPkIaTj6{v_4n3vx.I=".B(JPR(R#1{ a}-gTq}4 Gԋi#TƴZoT ]0*^qJ# Ш:jЄкtf"\pf<@J*͘GZ_7碘Mzf7"İ EDa3\izwK)P+TIeɋEj(yYfm G@!kv34d3jA~V5kd Y]%ɻol|#]9XS ;5 GJD9$bO!=o9p=N_Qa@ƩqV"bۇN@L"YA *+hƦ}IS z~|p,^tfȌM=:1s- z}CpΉ8N:v}۝Nq &MKn/7,Lk-Ђ99i4#3hռA֐6fkΗ̚ vGUEj-~omw8H912,z|1=GȎvP=9ǁ_d7QiڈV"՚c{n=J,q30%'ty#&zEmx|`G^ jE5NS>> ϩ >GUbHIP"cIBZW#ڎzj}#Г.P@iP2Ti;mҴ GU*#g9z3  C2&F[&+;#!9rӘU&d$m#AV-W}=y4"0R*z4x#`OAŇwscv̧)jA|ѣ{6͢: 0_. ;Q]oz#{S .9:9Wy j>[G#>"Yi' 碈*=\RPiK)M7M'a-iqjӘ֫MIՑk8 FG\Zj2sM{"{YVpT)2^L&CEMIf=\@+a'V'ag!3u!x5#z-ô>H5G#Hu~J|նvs3s4bJSyqK-]YUUd֛۷^?̴jf;ϗSwk-fDt+y9tw(" HFٿSC,fյXgSMPJnE-v@i}rKF i8 bZ%M9bTIq CQVq> yLEjbj$]*RsC)4%7Gv!z$Gl%>f&9kDbtJ V!1q2M}^ܟY&rJzʮ @䐙)VL:M";eE PgBtΙ\0.OBlb8>>68MӔcl900T}*S2æY*RbzU-f~m{A!fМSk#X~Ȇ,uOBMR֝[\vRf}b $r2>[KtHM*ǣ;X,TZkJӺ@1HhhWkd&rUީر]⇐_$D&26:s,ވޯsLxΩ!B,.3̀[~JLTk"6섶{{}\ܹi1.}q\μgak)f[ .UyD4J.E p2/[Yk=g[}Q{028N]̴-Ӏj-Dt>&Z*MFN+f-9Lj`9z[{di`0 %t;v,#sC{wFj `'y/{cy1w\LAoǴV@=1\jfo3|3 ~]kǘ mrI%G zzZvwZdzd3Infz$$!% nnjGϹ:M~ht("Rs*R~|ʞSGQYω"0 /l8}gŭ 3-eI8U!x 0c *R/b9|ǧp+ :-e'N bxmϧ{)SYxfYv<;HLFK*JHR"fF t9?L,S+ 4xj]|YKHD{ 7V7ןN\|. QQS"E7_u)ϸ{)M/.FbNĢ"z> 8fv<]H0@ ū7 SB@^sYDd"s@~N4/۳㠟PEBøm[bȉ8,DW{+Kiܺ)3a0g\|@W6j*nwqqyeRZ8+h"JDā  fΎC@)ĕ˜ IDATZ3u(aB|x߼AE1`g?AW榢M:&03RJUTg pJbp{V10,V!3\̴ۍfM^@XD㺲d9yDz_tfn&]ǃ9B`w1!$lR;>sk s7ЙZ0O@}EZkAbb(@)ʛ~y]bE*]q/6|ZjkYīLsӲR 1 Dz rJAoʹb"1)ys, Bl+!]™Ðc PjqrJ ?e$/c+9֕U`'C T-xqQSLÐYºDN߲Iؽ/N w]E1Lpyq1Ʈ/TR: ݛwV)#qh ubZk"RfFtc.!ƘTx`c="8:~'LuwH) imt7iEUBoe]1S\dƋ@LbfffjhcX@[}1C^/:izL()ӀY N)/x{%bR!YDI ǼvdR=>Ou{ Yd0ݽD!q޼y툨O{g< Tf*-HcHMU=p&8 D:[NT$imWk=H+|! n,>8() Ҥi`9"b}qw4Z/{XͤCmSÆ)qML]χ \M1Ro/"` LlhD ] (;0qΩyBdf--x_o(S':{:_%IΛ'XT(fK)90 _~+Zi5< cL[dDe'q1ǘziUUUDLNu*;I0UU{;c͚XcmHSi1awoTRRTfg@sLCs';TkۥN12}>iHCʻ3ѧI|ShԥZ@GPpu'wq2teumYx|vx!GxsoQ~>`x /[dC@dnΩ$ 1Y)E]; 귍s1ѝJK9.m9o=OD!:%MEHk!u~f@NB!!! /OUm1Me! X!duF$uRO!ܵU7<^~jis ՝#ǁI.b&O Ct o_=$LJ@`njM}9y܎:셽QLl= q:`Nֺ%b dnv٭q)4UV5Mp LDHj=]9,q󪖅}:LAHo9O2 ฿J)o/pz3G[q+ֈqכKoAgv 9mƥ"Ƌp.a.µ> =. -TOWVŞJchS8> Ddϯ3$므 K)14KDӮFD9^`2 gǃkvOEn[훘@A5ZݔQZHg| .u1)DMZKk̉(o5BKxDs? wO}-G){b)gʹIefK-]RR\ۢØ⹻)Hi[-|sw3,uѣoPq$^C0'}&Rju;U}bn"clRv@zy8jJJ9Fb Yf\H[)Qs4dfeY*e\5F{"߂,Śx)k4G1q ]UL,ǝEoKz(A_ÒX8>/m}BUvl1^w5s! RM DܝR(2OJw_#D=% d'uĕ JJk8!8^3T|tqwQ|}aA1<09Y1W庲*C$K ~خz1/9y8?㭃9?fֹP5S80lC"RKl`VN)唿F:LenZ)CGdEREGDd#";{7URR:jw3 jY/yvG|h}7y~>V Н̺*31!>э{xjfzb={"‰>MJݯDh5UE7is]#`/CsLy]Ҧԧ/LHb$ hZF|n eD8<|sQ] ?h嗷o;3AZ)evx)E"R8K a=j""*"%W8qt7S7u@YbƒX>*]Z=_lK1e>Z ȝm{ËS̛>)poh)Ǜdܜ!8n4PXkZŭ=mv(DJܼ?MKڞ\ЈUJ1R-AULcJøp22bFl9o_XkҚv%=fp7S35VkYL1C Ǔ&3md*"ѵ,>Ÿ1:Z6%UB"2!/2/JnRL9ݚÓ13DqYaӚe&8p9Ƹ<_q(Syq5[bq3mC}C6<!lD2,V*mxa 04;盛uypˋ]L9J3?cjҤh-y~l woeVw7r8H=޼۬#y|O3cޠ%JҦ[#t02K[kFEx1@WxTlZY|HWzrHǼ=Vu>ZE' KI)՞ yN}~zƉn9ϽFcbo;SH%f"@1I,ӑ8DvुaMS\UE*`̽o3U41U@ldl%O?hUS~]lGeāL|d#'c.'k?`}6wn* Zmx1u{>kl>>r&DdffEΧ"rMSNyH{c\i@êRKnO նD_3ReiاaSO'uZ;VܴHsTx eU6Dq xf< в.0Đw7v/T||_u=`i͌Ґq1n N ɣ)3aYJ Aw'Q+~cpwj!Ҽ7q8Ƒ{_zpj"!'"b@X[kbX^ cZle"ś>n*.,ML)>]=*jW:n[cf`^5w͔t'썳 RZe!RJ9l*LunPp0 /wsmvmcNBa1FPp{1[WWƐr1Uib㐙GjiuQicR=ƮZ&"L:RWIdהGz} 18]ui!<.MLigQ<1RnJl隃"& ѹ zNщ^$|&ֈ1p;3ݛ?Ex&"S`"^PD$^ke z ? Ҿs:>':­J;޼A0g..vD݂Wa33n}Lƃ!tuy11߄Mٸ!u3Z~w"܍80QRyH9'5 !c)? Jg趋 O8xgp=svWoފZfZl)Z QŃKؽD 3UTUz[^λ"Sc8$dr8\OӱZrGscps搆q:I=ìEQm.&LFCv۾[ 0*eqrG1"9!<-qߏp"mK: ]FZ4Yզxyk6'lCL) 0[km7 Zq5V:x#I-UQ=Ӹv3*~0Z]y.eEZ!Ęޣ[!aS IDATdO*y:śkp_ZcQ9["!?yND֬1 fjSUu1|\oO nIp Zcj"\L)7q )|zprp&.⠮`J8>.tjfz\~j~zY=zOJnj%aw& oҪ6DdĻE&f+KzWi_o7 [|sF2?LM:oj.`9Cohw/mե"OxuU3'%n[͵i V3ǧ OLrnKAps Ωϟ]oϣ]~ q^) re̤2_/ˍjxEC"1Fw!ͰC 1%怏ׯftN!u.^s/y(S?#3W5"<f1R"8tIpf/xpw=pyضQy1<ϩ-wm Se639m2׍ԉN1 ?|?H-m^tV#5g?憄1nL'19PJb cSkxH!p C p2|>?&E/9_ oՉTZȏY-Ӱ9qTi|vM]cVB|&>V2 )| Pb?HxwRi.,G&ژ9s)375Q7 p9wMT;q&!bƻ8Mvt~&]5N5[ک>F/'j1<0nzn"S.61z2x󗔷7ղL6#\b?.fe>*6K[ @&H| hȹk&;4fb/`ܛCO7H8XͿ^3!u{?iIp&aذn*J)SðJ'møz78H+|KCxkBˏ֡;Aw6aF5QwA|@sS7ƀ - S]VF$$UUWEcc|N}XqkZK@Ϟa!)ͺnp޽?1 % OjE;[ q3ˢu=b >ܚ0G~7k*sH)uZKYCGs:'m8,67V;~quE.B8-!<{1Ԙ?SYk7]|+^lD"J!0oDZx%g99RTfhfD; }t+*RMjCet8͋{bLx`NMEȿ-j9ѩc`tkIkxx7mor9YJdg4 "1s,9-(\ꊗSjҚ:[dIwomiu yL%n dulnyB<+<$:ͫ*n7vjYyYRcw>W?&;+t08W}n~b-&bZTZk]ZTܜbL!p{JctЅdO:"/-o\٧?ʞS3 O~4aOk憈1&RT_]ojbSc6MRγ] wkh .|,xڌ sFfc _";FZbs'2m203jB9uqW&)NԽ+__٧=+yeϩߍCD;I 0YUO4ghtORLbö :ק*u_Ŕ]ADZb. jeZjUB"đ/ZA LD5rctRww @@ C&!$jϖ_ZSH Kk#El"Gde92sf}e=+|ʞS36b5Hyz߿!T5ye omCkP)ykYEUbJ1sZ SH9<cv|`lFĈ(4M ./w1sCRj]@LTkq.vQ=S"BB kkj7\>_ie]Ypd&\O$`W^ї;Љ{@LHCs RT r$ժfp4_zj˛// ̇9D6ߌ3 R̜r/Đ^淹\.G@!sFZe#ɵC\ Wu]NruTJ@gj63b2ñL)`Һ:YV~iޕ}=PJܠzV}MU>we Su}o6_YշKZk%|yy!"wpH1o ;έNyܔVw|20x@J[ղ4 5"gwY@os>N[ڠnbF_s"wvđe)i )ĩxѹ:M0"iCL\=eu哕]:Vi]8}֕8BrN9AMſ'sswO]څ yA9EhV{n3fbqb>ֽsh9siDf*xyEzݥRݣ\2΢kM>͛@ȑCڢٍx|ub̄̏áU-sכ7o20hB!L!` Vxx7R Q,+++U?U$H4ue|:Ͳw޼˸meϩ߁^㹭*GR9b< FULctw7SDp?>7Zyof{+S[y6-~g|Z5"qd^f,2i1|A{"<루64=LG85'>{@E7#ؠ?Lr8ګ4ci7(5s}nRi;S!1d z^_YdU]߯fa q>dq6+KV=Oefs_JAH)91߼_??|`fUc~ "{HL C`fV[[,3!ޭ:P un8/!nt:$&uBJۭOϸJnB9 bf-+5 &HAivJ,䣷faLf(``FD7PMD;kA˲Տ5yZiSTWkO^JѐspTZjΑ()890"@uwU1/8#b Dbui6upiZds'"ĀĮؚbVe2!$OO++JҾ-DHL f]_=+߯ I[^?>=k̜r6,9ͪ^n66Θ`aB)e.,xyq{`盛 Z-tsqmܔ.as~ W5MD&]7kR!v"dt7@5~t&W)V㎐@u!2[wQ]slQ\6jJBzI _ ZH_~B76t1d[VYr~]i%˶X1z?93{9ːfs/\"tS[0G*eSSSKKK.}/ !\yD}_ErAqZBAXԝ_)KvR.qk- !=*Trf"+_6t/Fkc A"7mGrPqN =J!Î^?.8mWLo928 Y>ZRcL Xτ ɑa\l:Kdv\1mNE0pwUw@kYk: YQ }z>tlN@RLKf'ɚjkz0| m=[:וXRJD Ƙc] trG1ޙ0ΐq(i#( F0#^PHS 'Z:b:s-A*YY+Q1i\W, d) '#S"U g,Nr.8Zkc-jB d= ChIËqHhͶ2ɱn*A@E%kz3㨥%d;GDFLK,% OV-=[olScKԕgH50Jr!8hm nθs>ױcV"b6>s].uƜ#:<؈1[kD"UК7Mb΅U0|"^{T28ADZRaW[]]yP(8i"WʁБ"jd=ڸ0]sJ@qYۆW9Fkە :M0jTkxS#2cL+#%""XsX!c6 ]X)!G1Ȝl=Y8ϲd2u, WGLJ3L0X~o*iKd-qVC_&5ZpJ%n,5êQcF!:C_Z>'K&}XkRZ+Ɠ^y#Fl>C 7:&o$" cqL%ܦl.3B)E]0.uxYnwǗ1pdur`Cm G<[.pά*vCau ~ ) \3O+ItVz޹¥#X\FGBAѳBXW"% ŞQV,pڶЧ[b_d5Ô7Z|>jlkkKMj1 L=)҈BH/Ҵt$Z:n9[a=@cDKcTk!*ɢ0iY0$^@D1&=} ZJ'96cXƚB8Ff (9S{̵؂y bLiY2 71^I6-@ k/Xk8gAV8fDZRRMVb)m%MN ^Ό@dADۙʬ1ZOH/Lt'1ִ5)c܆(yp<|Dm{6X&e$maV u0tE|'e2 bpcJikcܱ#2űZp^%B0^|)s7ĺm'CRC2YzPp^ ic&cdVh[XCFY]t1DDfBA i%" fmyme!K(Vm |!;xzAgyr(v]A%yO/ |ִC5^pJDRqb1: æ0b>CM鷯Wrd@cL1FYu'ӕdDȔTط i@ؘ z2ݚHmVG3-* IDATKdl2 rDTf3M*i(`ݪ~_ hBh ZD"q )E"8gBpM% ȸ1L似 / 2' D˘B2VՈ8@ qߗǑZEV&*9'-"&DxV#+EBpD`qΉlDʶ[-6YB d:{XA=K ug}@}Obz7E/Ds)%c.%@GӚS [t?/C9u.+< ,I[ ))kWuPzSVur:?oSpJt[GudJdZWl*Ȅ9qB$q Pk#!՝T19xi(d}K!ydyV 2I!2YNߣl$dg 176C+z6׾rѳf2U֔Ч_e YaϿ %8JkSVM\Ji$k+av9o%I$S%|)֯ G ̊>DXk0P Il@8̈́:t46i*. c\ݮb)Zbn<t~ 'gj얦v@6zW,.@Ú }^*aQ.ȗdm YQHUM'Eƕ!dIrl! K$,t˜ey"q2Ʃ߃b2 ң,PL:6:n):b< }p|ϖ6g9} ckk6R"=*"֊sH]Yu#8޵Ll$Cd pXwTjyI,CrZ[ROXk%L$S_hak>ޮ Z6=8sBuh nqwRؚm/yϗ:[ږr|?HR))E" !x,Yb4ce l*(dJ9e1kM d\9C!KsϺJ*HyD"$LaH `e#'hRo1D"8[c( |> Mo(BQQ.KˈR-mK`Kۯo:N&oV$X6\ܣ@.YB P Â`+L C(F λ DBwJ  0鳦fUߠM.]DUa2N++sX:n4* )oK܊{ouZ+ ԝ^bc|`\ksy^eeeeEEuu^h.*mv/;o sS'-4^! c̱0,3.CEDck' (Ɂ}8_KM-zq}ljX=Y STll)YH &ѵhsp<2C2ZQC@⮭+&ؚOE= [yT7^7|`P̓TDRHNkt.Ӝ˷(aQrd78Pb%s' 8*91M<( #m(#@@92P (uZDBpiL2  !% hRFdxѳ<6oj#IHhΒV !DaUDZoOolҶl> Mӕբ\Ict>۔ 3:@{8a>((OT2Iߗ+[xSG"б:.cEDJk5#peқc<  X\60ֈJ&<hc0<<|RR HiI&dp3da~zCmn3ɞndZ5CjIk B.8g]4[\3.=Oz~y>D3ZfrWEU_ZR0AHVq>!NVE& lk6U- R s_5&"؞ 5)DjktyVǑ5 c1k-Z9rlk>ӚSJ)mDq8銤}kbkaϚ8"gB&L.+1Xc,Ys^SS!eJ0 Ċ&c#G\FJfW*@Jm7-zCm_tJYkBla>McL&Ӛ H!H$ b:LBJ!6AӍE}ƌmğ,7m1q ?Hd'!( sl. c9 W[5ҚϷXU\PXq\J\2H$|F=*ʐ\ ڪ(HkmD9lWL&DD0(A*5\(H$D& ^l1smcL765 Δ/0ɭe8G֦ZWO֚0ך˷X/ aaMSs_!ѨJOTȐ#f })P1S7n3 Xe|>{n@{B 1eoB-"RJkXas c8&R꺕L&\TyTqF| Q݇yƥHǤ17kV51}֜t*LWTI?AUUuLUؔRleD$)vڨw 4l$BDRD"c_{)A0=ƨZmF+{}HUT}g2ѧdm-y .GBk!PU]xRVVڐĉ l+((\!S+y<2 Q!D*|.4X글t䆈m+^j+dm-a,Vy/gׂ@!7i0raťwUqqΤ%>`zӆ9>yog[A=t Y 45 j_dP1!|pɠ""r&9eU1.ڞ hL:h(*8 y18,mX ! `UBL\JDŇYca.8H3!g4-僀M؄Us? yy=劎笠{lR<˙7۞>`щ2eB̩nϮ6&~`3Hxq-vX4H,e`9ғeaJe*6|/0g< ,D`:6ɱAD7;Ra cE8i?bfkVqFaNiYM2fqXaAM\pJC vv% Qґgy1,ddr@`n?s=!2f[]Wh|Qõ ݆`ntGds3Q&e0D .5׻ANDƸͤ𼠼;]uQyJ.eZk1ZkmbC@D 8@-YH@Br!xs?VhdaQeȐc,<=g^ \8cR 2+l"@# 3U!ι'FcDn`('\d.3ǐ#J&B%2ƈs&=θd3` gDēOqYz)?ܒf7ַF@a&5cضns{o/h<0Jz;]oq͚E^:Ltlr׿4{ O8en7z~UP, 0莌hDv}sڇ{m-Mt%Uy{ /Oq 4Ŷu.{BiO KI!0yɐ#Q`(Vq6¼5JΥRJ!8g+_?EK8c̓B w 9\JB cԏ~sQCv;Wh7YOJg{Rʫ{Oxяwl={甆mbtR dҹ@G yҕIvB}:fS7Ilx9x׎zeǷxϽ~;/ߔs;oرt:MD/u_rאC>|~C>ֶ2/KW9u77vo_fDZd∃FYTiENCS|T(4g+A^Y-GZ`tu%{}/k %cv;&ٹ=iΆgk/ƿyZswZk9ر6]~؈M݆VW% \*n߿̶^LJa^7\k.ҵ~gao-XQ8uAGBY'dW$ye{~Τ+s_ O_z܄m0gHD(. )")d.g 8֗\Q}} ŷ0iVkD9NopBDDրBߗnq[C<۷N=.,Ӽ~CLW;g,C3Bpk1 I.88N廗_^ߧ^y]71ƺ4K‚]!cLyᕗ;ﯭd _4S/ayl<>[eۋ-+n]"#h" CYc5Z"g ﮾5 _ߥk+6ꣻu[q{}7tc+{;9m[<;GRpn/tϞ¦˾2jU` rnJr~77~Yc^x_]\u`xN?Q}n;qPw6_#j{ %ꁎ\lKdevhfWsd'_9OBeѝ'9 YRAWw܈藒RU{gQ}>O蓋upHJ"1m }}(.c|!edt #rƶdSLcݗX1I?Ofϙ_b]ϙ3off˫ŋo׏7=sLyC.=yW>=1`/nVg=x]/XZnj1OXxu[`ڔ!i Vg6-!"Uf-jH` 3&VđXY"6?*`ϕ@u5?w횋 rzd1gȀaLb9͘>#_<3\}5H[+ n,FV( k~{9rmO?O!~]s/'t򥙙祏[זQv󀐬uRcTDdYC5'x-zݵ͜jCnn{o<#`Ԙտ+V.wء|晗_z=s?kSRԘ1cO>a„#ߚU> A VkxMN_~ۋ*?`pԟ'r[H IDATtfzC)퉗f#GN'Җ߾p@?_.m $?ht.yr򿼱9c:zAՉsXҢQq) ŹsW由 c$gO~Ʋ̨o,:|??r%-`lW|e4y~}&X;nkO'wn(j˚Ë#F1sE'ϛ* ՝i?׽w~9<.蝯/=hdLn^Ϗ}Ǵiic9upk :jL׶̪kcF'59.Oح$:f=){ ϲ-j/;t>U1wIZ}=77uB|O]n~ek]|ǴG+^hoޜy܅uxv{^2UO>qTV~\g҈ m{E.uC6ݤ3z Oܥω»ɏ*6~}?],=ӗڼ7c N(2lwhV}kߔC8hKُWiKP{a#}_%WoDͬCoN?!C|c/8A͝;G^6k֬8;лsܹ'|…vi&s?5.]1.'cWu?a){Щ{VƇЗ33[]%V;OF&`u6Źӗd|Ns]"/|\Gwd˛{ q.ر^/m*O}V0waI?m͔YMt XsVzI]5~${+? w&Iqk[3uNGWm֗W, 5 ?:o]oQW>Vx16ķMzK_߼[p'\yOTn=lsS=vp%6`^qgO_XM{LJvr|4WR7G?!);nsEe/E"?;=O;g\#Og[{nϴ41#:yڿzzԘ^u CO7Yn, /]>lİ {Ma# 1Lk]je?7?vGS0m]x3ѿ?znnlyzñ^eҋ=78pynEʣG=zSO?~{;mVw)LZdwK~x'l &t)$2^xų:+UtЁӧO/5`ʧ i{փ4i K_:w-S]uo^v'XL>o,sa@z{_dOVwyƜ9k~tg6duT~ڰpMͦooyN?X?O!t~S~My]Y@5 qC^Og7]9+O__*c|>;s׽"?Zǿ}۾vչk۵tz.Ŋq[s^]v%u~L4ge?/ X:ua_-Cӗ_|а>^¦vĂv*,64Owա؝kJr_۩1.yt{ z^3{̝pпܤ+ycO+}h 7̛nx;NTuR[͞FN6'OY/v;{.hzށ9u.o{urAy8䐃3y߿e^:gg|=ccǎ馛 :{Ǔ^~Fl_s~ŋ;Gr{, յfZKlȪy -}}eܯ{_Ny{4 }S]JfU/]ןNw$?;~Ǻط}yIW>6}ӗ"#~㠪N{;?N[:VI=Ng !n2@y}k3}\7iuKhݷ~w' 4wTjpuka<afҦ$N>}C8oh${BV=oU׳us\ޙWTUNWgL>x뵷9O<2-D=~:o?_r}'~}.ُޛNݠJ2WOZjWݰGqߣ#/<4un鎷 ɛzY:'i _^]oo~}ƧO>d 8M >>q%Ɔ.8[TTTLzק~!K_]u]s؋>A!aM~y }Kx^ Bkf~q'{谝R/}\_\-UT'qWs;i}ڧ9ph{ r92z;OƼu/.1dt_޷5h2N2s˚ԮC#Ƨ_onũ>CJw4`a/gDbwy}nnx97Xk={0|<<pQG-\>}ׯys̙'m""9063ېT➹fe_y~Co4z9N\_;1Zr6oKK3ߛg8&&Y4;~@p@e0ahռ\AU;cjNL׸S^{lj;?Zq}S },* pi˫q+9)=!TbAK/6*!}mZ8~`*DY:iڔ÷]&ƜT]>XQ~|!A k5 ׏!NkY~W9 +qCڮư_ګIzӗcə266!9tҍw7O2k;]]:hTuM{5#vGYXO}ê UzM_%I!KK@ a~fci@^u>YcqwMo 9hE=l]%`;HMl k檳R6O p~fx5Kcl 4*++9`yuu`Ⴕqwi'u]J(( D;Ps>TbMR6 IJw`M~ACvIS0! L#+__ `]C8ú`aVgqʄ>xӔ_u޵c<&82=*>%;z|ś+q$^ʶ0:ǭ(4vPPzv O Ccx]=Nܳ:̟;2lws/<;N?bv=#H;ݾ>MkWN|o@>_/<}7G01vwϞ/;29vo2"OG_WA%v0v[Us&5AQ9֦+UyB}K.m Xp9uU^-13FtwL=qϪ$ ۣX?lhN@n'?Wx…~WW>k^|Q).U׷PTJV,_>v+W,WJz!ǃ'x"c,t:=m맟~ZWruݮ6ȹ{,i ǚo:gY{v .^BC"E{(9jC3gRÏ eCĝʞ~B%Ióũ.!ys72/ÏmZF]%F%M 7Ck^h`mqjZQ;;pdͳ3W6oMn* s 6b6 7嵱fpkl*N5! |3̄չkڲV(CƒɄMا6Q(pQ+/ V]zHX5z{jGfs+<xiβ`U6b@:1ߝo,D, In߿Caf}KC XgNg(&"렊_]['=o/+ +31,1YK}Ve%n]fä+O!])UrN}Ч_[`Uk^>K uwl<P᷆u]cѢE{Kq`y? U:ZίMv 'Or衇444֮A+cor{KԘSU@Z,c %W[} En5w﾿(Er[JJK[Tn(Z׮UFF52ta~YJJJ܅$ɏ>\ G>K|jd M*I1E9snVwB"'w 02iiSrqz'ܸ?L [ܒiSpsmmU 'b40eCyYXbV\NpZ|Zcicsb(oI!I8F}ؽ`uke ӛ0jFx}'>96Zr8> R-)􄆆R$zJ;=LX94=|ѷ24N/ʌN.JT~x~k"el+]xWvǩ#WϤN.Gwt@ TBPf>arc$@r%YABrdXDO 7U diFQifhߺ+%`#aWQclblְNuAČاظ1#nR9oƺ֗wF3Ϸ:px~N3=o($Mx~..xnҒasI/'- 7M,`fnJaߴ:Znxx\b3fJ/T8_9#?;b8^nǟ0X֤XT%e\,_C׸VE/ʈR]t&%h/T0qv<yEU wܜ[/AT5.@5AWV d\;7o ХK//0qR^ޜ(i:wDŋ+ &l Q ;wLDBda96ێٳg?Ę)%iv.+)G,_T`1YXwJĝ;u !m !ar4ΰMdŒfC@|bjkӿEr11:`J²KߗJ;? yt0<,r8:9 ktJ񢜖F97>?P1_J'+J/ yѴgʙJ!&UB$t}vd(25B1laݛJ_>j@JF8XMLb @JW>X"B/]:25R+d]~~*:y bJ_ݹoϒB/31,wlKDjI^?ESDmYqSbB<:Ɔ+孥oF0;E jI$0D"`ףX!B?.Bm~O;ܠȜZ%x-_RlvI2+9lNV z0AI=/F[BFv`, G _rf%ߝ4Q/떠71 ={BѮmV:t(h4ǐ O+ܶM:4kl=]6}*::{kxff-[w)IeǏY4<}Ȋ}4' SK5Z !R)7Xv1Ytr.N%.~4R#U" +EM\7=H*)9}HNQz{+(ӉIbd;ݕmtHnPdT|ro2+T~-DBj)}A)")L<+bc|wIJğǍ9oƴYSyEjva۽w\׵gf9yϿb\.I{j!Lj@ jN^-;^EG|>t:%r-;Ř'T*xQ*?zyr9JRgi Z#JVxf BDQR~\$I,yu:36_\.799z1ImM5jk" >o|!}V6[ta2().:F"X,fN<9cG32:FUe%UUnJۃ餵JEwO/'O#H0616Ztv3J ۉ sɔrrzj5h4L&$t P(^&L.PN,`Mx$1<2B*b||"Z"~d2 t#'$k76]OG\rIR)nd2ٕ~jznH$2Yv``0ko1\}bZÇny|B$v?0H*% lTJ@_ y4jo>{V tJ&Z?|l6ٕ9RbtlEE$I3u#Cô47C}LLN"IPVlbnn&L&>`6P*ѩVe3j Ʀ3ڌ^Da*z{(,'(/+ІBsA456RQ>tBjn߶J&r09hZ m,//T(H27?O,'GC|kD iijjeOE9jZ=bqO:@*Cyc[޽/?#cc@\ΓJ%I>VG47a/*ڴoQQ!=RYʽ΢P*psIrZ[Z'Ox^TQ(=׍_IzAff2vKa;b𡃤R)**ʳ3@*J=Y ny'@E.Zbz<v TWW 8N Cj,& )* x8> O`2VvFG()IZ,yld2(6ZyK^o3Dj}$@)cGnVX,FgW7v{:mj0;7ǾVѯt:]*CC^־SSRX<[AV%ӗѱ&tuiGp|d2p=x\vfg'☌&rr􌌌bZP*V팍SS]Rdyٗu<Zy6f3To@s@x|×]n/\Yu^w fSI?˾d-``'T/{W2 uM^߱/6^wQS dL>>;'zC xhZ|>?%+GӾ.d(,LXn" . L2`<]O3d]W$I\|wx"Jz%VV_- =шFRI=(I4CQ?w{`o$IYwJ`1J%yert7;@``(@҆,秡RܻP(T*e[Fe`2Q(`\LR$ Jr?dヽT70Yd-MT`۵@ @^^ޛ 13;K^yɤI+[U-y1o˽(Jd _Ma0Q$1 5ZdO%YBZ R,LO +PT,6/?b+pp\hbjt t[pO'{F"Y6^ [!ICb1Z[^ݛpWfo$޾>^CAQa+`'v$TgG$I<=,%/f*{$Wo$|>*%99h]lrJ55Z pou&cT5PQS@g;xuM vHĈE>AaysA!H\-Op/P rrIOO0?5N!?ƗeJ8nfghkm>r1޿OIq1 v;7nWMhmi޶D7C0B2b>s;NK"^OR H,x8K'i=`ŌG폩`hxFGFimifnn~˨N>>d%"/7ϲ=BIO&L !ChF.83\tNODNnT\A<17Ml^"˙mJ*3),.%lEb.($א޺M6,7ow#,:L&Ţ ʝ% H& B"y/+(SSt'6mlP_=؋!?^Xp$g[wܾs`0~}qS(ee7NK\9ʦsluڎڴ2%5n;h-\/؀B&.^7>G9qW:v`#Lp>YBVT*8 vq-t=wﱧd"L&hI_.Sl#(٘n/fC.SThCTrJ vPw/M\U*z{\vZu]***w6FR m6gPTMwK$vSV^FLG \-wT*)-)!DzvРW0kut24^\UBRJmm zCgW7P#x t2j5:y<}%|,ଡ଼B!' t&GG.a))c4D}}-}E!J0HYi`Q(Yud?xBV{}}ݖuOmڎn+k7ō7GͼrܙMmkO v7999DH&؞' $qdH|\F=ITy9YCCn}5nܺEMUpN>NlWv'?F:^Lv\ǃ^C&eu(B]D^T$j ;XX\LsɈF9|%/~M0 ,/n/G184LScö'?ˍGA{vtZ-X>)-)ZYt8pܸ\n.]Fa%^g`V˧}! Xra6X?} PTX8CTTdקg+joΟ!Wȩܻ7$QP qI$z=X{*p:8&1ݯ,y!~E:ηzz:2&.]J"d# p06>LORW[˟P*TKKsS}N޿XY )(f~@Z5%>lƧ8 8. l^2nb 92Ғj_X[wbƙ@.v2ˑH43BTdnr++̲VAL@$I" 幹AaOi<äR<rj0.%IՐF2l6g#mrsrs| 0@C^X|VVVvXZRbD뵂Y IDATj䧟RTTH( O 6B[4 q=//PW˃G=|ϷX,_*ҕ$IE.J<|Rae%\.j1D!c2AT`ZXXXH$BYY)n3NrM).*ʜ,E@*" 295MQQaVhki~F(W1TbV'G_rFO~3 u[^w+]8YO SeC(6%69^YLNNF p2m{ {N788'|B8EՒFBLrDht=4'g=Bطod^"INB}XDQL&~R8,l/*׾Mwݴ\xe%~u+7p\D"QxRhe9vH$2OɯbU4R$=7aY$Id2ٖf;Z2FM'&Imž)JDQd2XFA2;IH؞kS#|_e_ƁH$ʃG ijh*Ov7BۮJw! uttusArrrv榇qMHDwO /S\lak~~b;w?GهGF5Ў, ݂PVۓCknwzMQQQi{rNv\.j d$I yְo0,k4*4lJIrssc82%\hF$V|_X`ᤩLJ">UeI$c1پdZEBڞnk5]"ؖ@*r:O >+רarrIZ3߆F;3;;$Iژ_XD"AEyyV{G6DuLd4iEVʽ67p:B!$TTs]JKJ,,.thin" a2u2, lJ0xl OT;JE^N5eyk#Ǚ|3@SDZt(p8<2񣇙&Msc xynGNK()}B&K?%uujb1JQ`YZ" qn߹R.pu$ *TH4dz^/.^H(h4P(2qϞ9;8 ݄a*;ϕY^^̙SܻVj-ڭV$SS ytuL&q]$I;oHFD2~WS/u,x@O,QPogOq S'Iw~Sg/bff gva;ETDkK3P|ksWo^sge,f3'Mjf+r7u*˸qU{I$d2ELOϠP޵ v:#[ cG"._N}m ;8~7oܾs={u`o޺ÙS's>~&r9vJ%wܣÙ"j졃|##crss)XD؋P\y{z1q{<2N kOMOʼx02QP@ Jj[\\uGDV'd{>fT*P,CgF $IʒIHvkBBRѪZ?~?3s=s9f'|AŨ%GG:,/ÇPW['?KK̼JbxFflU&JR\lvQVZvJe bA.319BQbZ_X%-SZ۳D(tfb!T%ۃ'HnNG9};+$)l jz)++EP򸣋ʽ{'L0::޽9y!KWr"\45U_`X8~c/RQQJ:wt2==޽ EE؋p8p{W/#3ۓolϴ}:ЖڈV>*]ӛl㷶Z}BPc_[+rj!R"kbm U*)򬶍d-Je {NX^^&LHh'2C$eU8+6%>u ppt\2AC}]HHCFccCfrNfyy!<JqF9s$yXVKZ,2:?YSxmQei9_RP F n<ofy'Ul Ejuht\H$F2vrj* E2Il:2ܔ>r ***s>m-0<IBp=1?.bXJ"  N I&[n\[<{Z(yTWU m> `{+~BͲMx&&&Ba<7g*ymk=܎]WVVXt8ܻZ[~&P=Tj\r1=3 ΟM˯TVՅBj/S8͍ ?G_V*g~dDR33;KyY.oTe`GG DQwPS]ŒK28]=47a2ft13;Kue%]20~D 7??ioLe^0Ɔzwvid@]]-c6)284ġb1SQ^*= ۍѸ^|yGY.yLo4u:jkKPG[K`fsd2Z[Q(pD145pF*^~0MJf)/+rGQ;UUV>yTW+?R)d2|296'3TUUCii 6HƆ=B奥ask ^ED WU x-,̺J@.O4RyR#&rj!!](fohhO> rFMQ=#i8x onl#bjziF# Y|>P8=Ѻ`(LuU%7nqG^3 vxw=S| \~i'&oPSAeaAQ| =8=}bQN?f~a|B_Ν{_u=}/^l2qat]LLN119hcן"̮6]&n8]$)Mvt`&*L&'Ѩ6d< {\+-n`*ηN284{?w?ȡLF#))N%]̣D#QL&=|LF#3d2N]ݨ2cff} ۞Ua*6{ޛ7?YBQɔO$T(Yþ`8Ѩ6in;+`F#h^<%>"KK^.;hjh Hpj?NN%e$gO?TUIgEΟ=MFjM NԣA݃R` Lؐș[X`~a_Rdz~w~^"(<r\!πJ\|#cc>Dxed2JO֝{9uٌV'Ql/BVsr+k^7qB{P_Go_? uMPhd2p:9{U,/p{< yӅgi))*q`>\I}T]^fdtYN?FWO/ñGޚ~~NeeX,fgH&8].N f{>cc<jj\rehd-j.OM1=s'&$-?~U(JYJ: IDATZqܼ fsGўep$GWrS_XZuu,.lQp]ԐH&ݺUS*\X,N:Wd -'(O557>q!}p8tႼ_ TX0 \_:șVY 799;C.CaPV s<̊F2Ɨdٹkok<,^ʳ<ŧ33Wη.i+W|pc+9?Hm,ܫZ<m`ٳ{,WqVsjh_-;wl`b.W種? xHcC}sh4~Ix?`#BRZEa#UlzBn)xd2E.l IJy0-\ZD" :L&^fq K oŢ > ~]to" 1>>Imm-SS31R)_$ Dc1T*%V:{pY#?9훘LJ! -̈́a~^OGuU e*ΞEVSSSs-|~0MrR hwk7z}l,67×NR0LT)j tBBP,CkX\.Cf9Y[37?!o~C[I4Cr{v.SIr{I0[laffGYV|>V%R9þGF1Ur'7KK^8-z`s]{_쬞Y- MM-^wSoNg`S[6ow I;oK=w=rܼMm^dbEjkk9=4$I46ɑ׸86ݚƹȻώXtxWp4@PX\`,WGc1Jjf(>@4ښꩯGZ)lGVsg\4F&f-CEr?}}huZ< `QWW'?秹A/MFB[<WfFcD"Wj477騪RIMMmYRJ5#;o†fh Dy!˧xy' C dsgp|p(LGG;j5tc1ijjU3|~ydJ fJH$M&b Fv]R.u$I"c2oi=`:&c62"#gumT*Fsmb` qܴkv>;~z`0H*B!F>`ڢ dkwfI_+WQThu`kZ[徕b19?zZmydfvV6Iܵ}.|9)]BϦ|>_V%I&IRYzYOVc4j fxI*ǓT(eu] znpgm`26>Vkff3$w?{n*t:W8x`?C#gٱl3MǦM^!R*IRlfS[M?~5H,8֊╗_a:;ۗm`D"|t>\>O.IbtfZt1owhjjrSSSM2gT*X)>Cggw9;@pFZ@*on_Kc LDz DfzjX?}}60 yYҌ`m`&PfU`ܹ~u] (Jހ`@Ѭ)v> {p|Xi̹shz/ԑ{ɴ2 fEc&٦%JJk"(ZM(d4T*DF|~?x][vP8\vm;.ƕm+WVj5eh4zޙ+ ݱP__w-7+tP(\'ux n6BQ&Fj$ T ހZF ~$@tp.'A-wmWf͜8uz=7z =45rwi7u:PTٳ{>շD"N'[:;9s;wl#H464ڼ8a=fffillYBQkv?s%-9yz^D:fk6V_gy ٷwq{x~N:M-rƢ0y`]_%1oW{i})ӟ3s'.67m ho}F1]=UU<3O=Y^\ WYYAߵbtk%[oZ}䠶gksZ[hkm=OVt?BO݊^E B"BB"ȍl`ҙ ٻطwd|~?}=pjhSQ xiSn܊ BO =I+BT*V.Q^cuUb5(yNO>ڲun۱yPpɲ؂?`!Y B \n7.g af䪟p Ǖ7S]װ|Ϣc"kڔ~􉼿ꄞлzSSyj5L&I$RrT*V ѕD>Vݻګy($܋N`lJhoZ}4ݽ\:sH({8s6upʛ̌]RQI索A>b{p|=@p}Y:暕@~?tŲlz]# ຸ5:f'.T*x0M!6uPQUCa?O$dO+/lxO(h4/QUYI2"cXd{p$B*χF!^ZbkwWR)Vh,F&l6vﺶY2BєUPX654omYo ņ.`0V }Rh4&fh46-o,TM  X,r9t:fjhhtAj5L D6#As6e,y?q'xG<ypj rs{6`yt#GTX ځed,sv;vC!8>k_ sGٱ}8a2,ny 9?[-[ܹ_!_}eN޹/!6]!ޢT*1 (IT*պ@ NR)oǚX"ƨTR2") SVU>xE=[y@<P(ejkk!OL%ILO"Ij m THۍs?ys330$̒`7D9q4ZO4CRQ]]B`@VSS]shl6N H$hin[يNCQSQaɈPVpT]+Vp87n'^8!mb$r\#ϗU( PHJ:60O gO-GĠ3y;t ?v؎j d2tvtT*dAΝMI2|qp8|~47RH$BMuՍ/bEa#*N^-}}\`m|xc^~N0<|VCoo6Vo?`{vb9^z /1yy nc'˔Dh0_ďQ Bp\.+" [iiib~!l466b4QH nBWAS:#TlTWU hnj$rK= eAB,ɬTX(m+BUb$IemBOmd=Z^CՐ(JI”ʽ(+WF8/u#梨by;Pb1 |,;ZVJ(JyȶL&C$j]TH4j%ʆl`G=ZdV{mvGL ùꄞлz.\G?dH]]ZZdʳ+D"Ŏ;7y+w=ۼHľGRW[!N35=ó<'GT(8~ %.sun7_'O 1ߋcZ9[6wʙ׻w}}LEv :=$<=CT\pXlfnNC}EvΎF/\CuudޭRYiin&NH@ FIR3_>DXV &?dW\`ГLQz{H&tc9KזͨjffPO6rund/Xʮu9ǼGqz<^'L^g+n?:L]m <5ͥqF05=#L^̞݃L^³TwLY&'/λqӣw @ lD"A2 ȳCShI畗^`nn9)v"ω_kvZ8q{w&N35=dw㱃^Vkll൯} ܱ_eYkE=Ғ.n~}@  6j'^< jh4(Jl-I\nEOVJ6L]m-~ @ zJ%Z.Kc̲^jj_7AW`fun,پmy%/pxO5z_OSC=t>;~W^zhGP25=C4%32rMxec qq`c1碋yΎv.\D}}nU$S)z{d}ZZFcd2a#}`% 8UVw5HD<_uK~zH$TUVb_X@BE(јL IDATaw8Xt֨1XVLF?fryjj^j Blfivչ9Μ=G*wk7DRּG?fc6#gb;XXpb2x~N:MHmk_ } F/c8a2,x"aaIi{i>#z}^MUwr(gΝGӊP l6j'^CCodL&C2@RDy$pi5XSbM8sV`0bazf',:ٵsD"lV6ѾZꁢH4_\g~t:Og._}xnc0Zl$3|UK/|ɉ+R0MhT*^D"u -l R,ˤ/s+;%ê &q8xQޏFcRiPaqFANhLD1bezM/ aϓ^ARŔ#hV2,;w⭯60H 5u|,hQՄ~& 3ع]E8h0uum* &3JՍ_w:$`VFȤS-V9ͻ3#Lj{=TTM) F dX,h\.$)e |>[Wj͗Ng˧8E~l>C>qu>\N|G"$ zv!:M{wK L375I>x Iر!LDc[;Μf1~n6J%ιY8Doi#6007= F;5u\x 4quj[U !L*EۖBdan&L޻@ &DH$Rd2T71t=hH$"M( F$M%_h:Geu-H..cAWUVsHlJ:d`>Ο=c4#ayn'>"z}& L&8w:}OGD~ڻz9sSr,^T2IYd✻®O2|0ZLbbKvf/0=vV+@@aZb1Ҷqt:#6'6^2F7P*:IJW3lXǚ0{v2?oe%uyz?q|lD =l, D!sS/v 343|cv{l&ؙt dv3wFԬɉP]p9iڴ+9~BAmS FghuDBAR*kE^D`ffŊ׽H>.h8FC7`,fh܍Z+_ nҵUbzwC/ j5bQrrA,`VM8 _ֶgֽ܆׾ym[^So5zKKfn!@pgQT&Z5D>YR4/QYQBl64Gaz=}/ܩB(g\Uil7_5`KK3--bb/ۃd^8ɖ͝3yƱ@]]jJJEEY@ 1--+3k+,]¢v BO_p8 H&A<o‡<+'e Q/o')r5N.Whl,<3O? @oVl^-a1v>aXPWV]בH$H$l6Pz ׇb;54̞]jވD2I8P(D*¶ƩJ$tnj!dNw@pfb1D܀fh ;o8͸^#ϳٌJ?w7_w{׻H4J0$yt#O04{ baSO<.k _'twmazfCFTGKsX'Nq4 5"ZښZΞEQQacd@?#giin" phjجV1 寈@ x(ʂ䕵Pq(xT*UYzY/ϣj5$B 8IC v؎& I(I 47_u9{fo!h_skXy#^xق)u,Ǝ^~uU\SU_@ G&!frL&44pD6|>YXXu$ CTUU޻',--aXAZoK+͡T*T[ƹ"R]]E$ 1"ç}B!^kDr boPwjfFf9.N*+v$Gn45&ʚi_QdXW[ߝ~[[t,yoyt:lV}ލH$(pBq  ,TUtuux<439y]ƛLD"I(3g= CmmӉ',P(DESYwNVl Wګ_}/hOni;ֿ^ `i)M"c;+|e2YYZt:aii F-.eͰa;8~v;>t:M"`l| I|~SS?7aP(Zl6H1O<^a ytxLbzH$pr9~?.WٺRNV.s@pc~\'H$uQ>#9ʙEwHa-ӹY>9rKcc\ϽrT*t4^nf& {3ٵs>ʙF#NW(>ՅhC8!H`׹ebl\rBBbAjk(ᑳgej tt:#.l6q..T*½yJ>A>=rښ4j W^ rފQ( cSiH ^uuit:m-ds I/JI&hf3dBne?ʫWu@IxC475ĉS dllhjZ[[8y4wrۃR  ;MhÏ辽X-_,f ?Obabb+h5‘?9Bw8?,G/RSŚGɑceEa0=?+@qq8V(TR]RamԚ׻Raymņx^@ B=?ʖ͛I&Բ( lx}nӽƆŰ{{޶os\[_g~R>~-ռmtmP|tٿ@0޾ rc%6wvhʬŠM@ijjiʼncsgQݜ]:Mp( `EED"u QfҶn sKp8JjؽFl6m)7(TX(R)VUU1 vJgBi6R@ y|lveU J6]WϵDb4owyoEtzϻy^NóDj"D@0HmM @d*IUeeY$_!};qz=.k ޓHRDtš\.'gor9rUɥ)BdHȃI*c,ա5Æ =|iyj qqH>Vf#_6,DD"E>^/Y;CFRaZimm& SYYɡk_QF/^;M# nf؏c6a;dϣRhii^&?V(K^/][<5+/SloGT253C<g#{9sMm,yTo y&&d4PH464\dnKcTTJiin&Ngر}ޱ)`p89;2"ryq$3d+B!QޝϳjĤB---`XQH䁨bszyEz(j!GTaS7EV 9%@y^*Dm}x<#F+ v7ߺ)L&#Ghx+\ QQa#ϳ#?q_;JW6mjF.^GV33;F&17?ϥqz{pG1eSk@ L_oFΜcqхh4؁?yR_QL&_׶YX$Lv{Z,\r}?^_W]]͞]9{Σ7E(6 ~HHnUs9ab~~|@$&Pq"yJyY=SmXumOj*jkWp8ߋؿ/L&DhB6p.K) t60.l6dİldd20k$FբP(P(8P*l69yBTJj$I$IF&LVd2ٲnF<"hZxؾ}&H$fjz>v腋TWULruχ룭.25=[ol yxk$~gk736|>Mq>?q^O0"Hl` 60N NK]mgύ멨tm3477  jk z6ώh4׋Z^_@ ꘘ'~q n"KF!C(vdReƀ*c"J=A2R(*AZ| , ,8b1t:CT⢋Ƃ5L.CRVYXpRQa# 尘͸4Br0@PCM]neX,eLcclSjs{? @h4mm/]kKoo{^Ѻf=\n7.46l쟓@ xبn[O#xx$y)GF!` : uڊ6 TWSQ$$n#e2,Oh4rBH1@U?Vy(Y*Yf2! lT@%IId& sr,ʚ@q%3HL{ }FZ-T,FuJV!a4亱 b1zRP-_jsi@ nCz肹&hs_ Oi"YP%Y>Ɇ* Tg (3{!_ }}Q|(u%l'>3<\rw.+N: L7z:}LMM3q{9w<,dS),Ѻ=E|*)W*ضJN q<%3PTP(mdiS C1ҺcђHYX<jNAvBF_n=]Q.WQUjmם`P-b~~nVQDFkO?~|<~ͥ{8~~_WUٻg' tuuOr#G=k?zx,&'o.s|Gej[ e`^ɛY 4a6>׿e~ uttqi>=sH8"@@2bFeM[xOU L$A x$FvX"`(P5 n=q k"  \~xc:zڇwS.W;zrw^|U-r ̕x'o:_FuB~Y¡=݋e`"aҩ5J#~ή20KR$PFe``3sZ1^!A E0L#$+L`.Jɶ~mĎL^>Z~%V+l5|JB=f1Gyc20"l3õ9v(g>vؿ/.^ѣt-Ƙm _Gpi̢bwr9z/nKydYcY/l6ntvvY8O=MmUU"-Tu`aYAR}%WXXn[:K Vm+ْ'AKI!?n=۶_-"D]Y5m7Fiϝa%:::8w\gO$JJ\fxxb3O=E$Ő%RHl&|yw7ޢLfs./~1>ď~h}2==i|+/ڏ3<ĥds9<'>9|BQ =sAbٖwe|bё,d;twuȡ\z 8q$׮_䙧bbBg._k?|>:C/ғջ:Xm8ݗZgxD"@{xt^x)v\+Lt]G$O_zWկW"/.ڽ{yxcĢQ}v QdHC`=d9u>h$BXmd2e`"t) ̉ON20OT& mt*ɻH^F  ,B4dY&%uh]7׿&20-HXE_*d|Ͷeߴm+YryYNMxRj$  hZ04eY[XBUk P ے/VRl؈=[ܝ>`hL{mxzvj5o0ƍiB8G6MjJ% mۦR4:@ L,Lwo]!$( , `߾}$~ٿߚVNMMNZ"+ض,+4pBYb~~h4,d9LˤR^I$ejPUU(Jtf BBF O*6lȞq=~<`ofln;֚M868palajjRLTu]H&S`?*UD"NR%Pbzz>fggZͯXR iTkUzYX8cS.imcR,VkM}7[Ok [ӨJ gPꆟ&mh>N,mL{bӬa&\/iTU% U SFvl/LyQ*4$Zdh8B.#N A4 UUQ˶(t/"Ba1 ϻ$15=沨@-Bv(~qʽ,GkpMr{ .yKttt4<\>kj_ٷ';BUv޵tuvD,.B$H$IBeE&HŰ,mxu-gfRm{ɍ)_,ɼdhokV.`rL<#0@Ow7$q'&ad9)Z*Hr9]N$T* m433y㭷yql>333Kwwi]tl׈D" ;OL0TUFGԥ>> ҩj`0A~7l}~lnϿ 37GQ>c^>~si|CJޞdYޒ&&ohok4M.^k@qc~HIw0#R=KTӂMm[aVp/D"ѮǶw3Y&`5q\LOUpXۨVp-\7p甁u\:ڹxiJ 7>184g>qL}0r|/ƛoH) \4G"=ܹ u gs5ҩ>4.SV(+x_w<Ǐ=իx'(+  I6#ge:::NLpcjNNRR\p'<3<)R/g>ϼ.JM>tP錏OЖ3'RR*spp7m4>=ͱǎq#[2\+N"4ͦZ^ݒeeYMm/>?o Mm[av62)DjL{KvԳ;tꇴ[/{\pO"P$xDz~7^Ve~~Y(\tiCI;+Zm1@@pܘϭذ{%ޗņ< J5ɋhL{bIt`a!X m;MɄh~&}o}mm_vJvܖK Yl{$Iڔ]UI k!ipuw33;K:F$YΟ#Yz{zEUT:; O15?L6fs9]F[:]ݻǡ\.o^T$],@ R>XVXhl[:~RzMm[aϒ,XNܼwm,Pw36q<#:6qt$yt=x/q9 Hnσ/Ξt'ap.j5X,J$!ЂAYK>_'{@'xD/_C9{i# șϾNGY$z'[w lPH$~Ezd▎h|mei^p6H`pi2Kߗ.ޭ!`Ҫ gJ%&6@﬿ܻVGضnriXM6cnn@@%]9q?UA,n1"lw" l@ hU6bc6D;2)w3>"Mfzzubvvn<ڵ %o@ W%4Lg F{{`Z8k 199I/Hv(g\ennC(D@pgrq0ؼض~ \m+Yrҷ|{KofGzwӹZm;ȲʨXe8c||]j~,KKP LMMs8o o=Xx9p@KJ nBay법6bVy˟Vʹg )=n8dIioogaa~Ξ=Ob,KJb]/=RD x(+k ooJ)`۳-5R(/T*qU\ejj5mZJA0 $}RP8L{[I9t?Ρ:8}~69}<ȷ `6UUN6QE6bϠĕ ?=bʲL.ro7ô|+m7W̢0ir- j2ne~~M i~2m;jFӽ/&ze`fff)TUn_˼/`&.^;;շ ܔW_}U8-aj5l6{G,/Ҧzw6"I,/_XW93 /\# ICCX(uLU9~8ϟFӴ/ Hn6+W25=GٳG /ay8.hX,e_xV9O<~~<448s - v@$FFFxg?ŝled2N:o߿P(|& 6m=[@H t]j5w{=CC؎M\]y\y}lHH|z4!-X,5994mPHV8bM e`ݳ^@ 6Ȳoo I[]Cpv’#oVS[^uMm[aZ4ᢞRfI!my.p Jo]L<ŋ(+躎y'46ƕW3< s՟ʋ}6&}HQV SO>n[?3M.Yzhooc|b>_[yT*TeL0L D%dy3+,,!!x^AJ g6DOR$IR f|xٶSK)g> ~-2Jnj M w1l0̺e`lG8@ n"[G9^ht4MÖjKcADZq]g a(K-kc}.Jh(S.b|q @ ni.vȲ\e9߭ԶL,m?ۨHt+fsEQPUT*E"Gu_@W΃5.K;Z;.㟼g30bǏeN:ȁznמT6k#ƮhKZH-.Za~7ӞeHL8V,*|(L(m,*@=un\ '$,`Y ,J~&eYM@  [d\mjk/+rM妶';wEʹy.F H$YW$IQ䶒@&''QUh4J.g׮* Piz{{p]ΝgdΝ%IBu*Cq_ٴzyA**Ѩ_#P [׷\6cBcۭoO^3(qeOނH<,mL{Br@T!2?EhZv夏h4\?us20$!I`0(+ry<<$Ib|b<ؿ0==*MՀJ4Y1B,KA:&tvvP* Ժc)`,LXZH0N$ndr 6bϸN$hL{,QDurt_CUex<H1 b%l6p[! DPkCjאy$#qBջ*2JGo=`;T *sW+_Q5cxϹ~}ё-#@ T*cAu IJWT5M<݃zjUdC`T"+H II#N^@siF"H+#:׳'I"*J{oC~yK%oQ3X0QezdRF[ɛFTZX lVUl؈=S2aP[RH+fs]MӐ$v?IDQ2Y ( ̲o mdYZG Tt?ǯ_3\p>R/H>ގ+O}pn_KHBJAb }xc{UrbzeWNը̛e @ JP$3ذ{Tڹe`b)0ʲTVʹW$r(-]q?UA,0W9;Z?~g޽ ?r]"^;o/t_~$hp {xWyܴNvJZ+򳺿lC@VUl؈=A,Ƒ(L'{J @ C<eQu&fUfe,CG"D䮵ZwN$ 5 KĒz_;q2ب hxӛ*0:2={ػgOG<O}s_WV]9Myg|D5?%]$$$T9|-d2^?n%ņނz<`['G0ѨR\n=EժIT" J0W j$ 2 4;B dy?9G7=v|m_۵|<'a'܄&A9f{P{xC@ ֤gfou,O7uضM\T*70MW- zdd2k+;K5Lxo~M{L2?8~_?%Z;;gś~Uۍ9K'fEo)ԶtqzMm[aϔLXwh ) ?\aw3yG4%,,,뵺yM Լ'y?[` ˯?uW,|ẖg ȫ6k~G,]ꏋS}p|X%D8H߾Mz:@ 𙙙wv޾ضrkm)* Uo[Uw?J7s@Mk+5T?w4?0iOk9B R _./ۨTjcÛLf?zcP0MBȟٟS(˿o6o6qjszB<.\$37 Ԛ6BH =Ƿx=<\9OҚxG*6lĞ)E@T7lF)f] {m(WN.[d߾k<:* P(177O z▷}-0W9>( ĢQ, uQ/} Ӳ90A,C\H:8Zg  N(W}iD'xtjj5l}V}ɛSE$I߹(ImғO\+tݴLHwF9T25Ĺw/V"`Ya-mfO8;(elA$,XL3X&U3 @XbFJe[8C6F"tӛ&@aT54G,.j;l"iA\A$\wyXQdBe`EܱxG;v &^ ,!ǢndJ j[$Ig׿r,,BdYv@dT/aH#B',ԪNS$&I;˲)BAjpwQ}gnm^ʺ}!tL =3_/k޼&Ӣ!$ذ{}Ehvi'JQ P+xoSڀhx[?~Zpё$ f'mo,| G_NW o?,K4(bFo%B4A+dH\6KZ#M$<イW.($q:;[6^.caa|$3?EhٶT0]uӺELf?d2n-,h-˒ _é'.~FYe  _.,9zIr$h #fcT^MUT*%$ 44U:$IffuO:*%d2/{K4X,( /<>=M:b|bё9y4G=¡Kp~m]c} `YlHo@p/ zQd+b$Z)r7=a4b>7U߾ E;ѻ5,h~vƲ,"n #]4MTUET FFMG4 xK/[o#!1yjH$L{[GGY昞` `Z˫g`䶮YYr-? j@ -Yt6Cq.WoBg+UX[ RD S(7JGp(o/;Qս]ˡ7Ҟm[(D.WBQ$񖺿g6Px<_\[V(‚zUUд0 \wG+s[2K˂ +lt6abk1R- z^X05==?{7B\;0QV f7panڹG20Pbjjuҗˑ/H$aw O?|lQ֗*XV ˲i6[ZwtTw-B Ţ B:`:{yS;^^0 jNl;r8|(WqDAZ5aBƭ;oskjYegfT{s|l'.qRDX^p0SQ Qk0aiYoH(E`:6ޥ};] JV]v Cd6fqneY'C;߄iQ5kFYf7pְ^sϾ0e \d%HP*i6Ѿ z?]Ay^ALPPYׁKlߦeߔR޹g \. +ɱA.*C Hŗιω'2::llP,M2y>zHtz/830ސT*qn'Mwӿiq$c*fwK9pWx*C&vP7X 9luI&_IX~ U|^D2m7.χ4M \׽`0LrIt%\Z#|`xx)KTb߾/K\7qhjųwbzj޹kl~@u8C57:}z h  ,7Xd2A&YJ&;[T*fgOiE*g3]Tm{v0(Rye`BhM$J30OZ7ƿ<# ;oo'$Jh40  =y9o0/=y!f9!,Md-}{rlM-y܂ BL>.%񞶱L@Fi=[zzp)fŌƟy=˾dв,ˡyj___52(j5r,}}@?a˼ôf1|__|!қOXTIsd8A6LBϠKܰ:aY K@z|>BMQ6yrVˢZhξy-n3w{nܰ3;S?EQ3:ES JYES \iz+ƪТz3ͱ\AEi,0/d_a0xmcwj$\46tٰalk#3Wɸ@`ġg)dSnM|mAMo\Th  Rh/Almmi&z4MAU0tb8O&Lbior9֯_+xA$FU6Ƙ.:2~Wq)w3jqTu@;weA ?y%ĒjqL-{ KgN\&'gDO.^XdjjV˦T*166F,f:ZfA6G*&0==E|l 9/W*,!\zoIE ?9λρca v{ ǘi[m! &2j?֝zAAZ1M}m|ߟ+8B>|z=`\eYOLj(T*\ץZQ.fRT5Zf3lh4rBȑq9m6j3N8?F  ExܹD ~/<#crxÚo0Yy'% cw"6 ?8:V؄őخp&6oXd:6-繤]\Dz­Zt!&#C4M4L&)K4(-94IRTUҙ4j biZh8S7PX,a/;I*S%~/ٛa.x(揓1ySy|=_AS>u[4-Wy:/3=qW;~yܹwlMdSS;89˝׽a  ,i aX|>O٘퇾at.غ檪FǫVDo|lYgi9PO歷kV|0gmɷ^s^|_fK?ˏ~gX] zzJIjj UUд0 O E[s.Ow|yxk^o9U]xU1~]u0:o¶֏o};OL590r쯩3g0(c X8;Pl_W zz288d2if3 g{OQJxi oؖMI^̻o~F=Vw~|ĂuA.&. +ɱADo9zhvB; 4M%sFow/V4M,gnԻh_C5iI' X,CCC@·cX#G,I(k/xEQ ]eh MU[M}Eg("5Aj1==5['J]^|ઑ"?ɳ} JSy( %{/A+]IdY^q8z(ffA0==.Ơ(ܰ1 3%7>ñ{~t%[QgX zzhԱFX,Ç(=I%m$sͺ8V:ʞ7:7@*㷨9EʭI&GuLx/Xˇn zzRZ-X,˺XʹGՙ,I:,RFQPqS6&7Wm1ev_`pQ@~~4b>" \8CR^]Z gDQ(d mz١43^qj3+ afi,E p h\'L3ZM*-% K=RA.( 34.5 : |1*L{DOVީS1 l6Kn#*4$t 1F@u'>ƍIq~0C+o?Vt.I2F*~زe yᇗy[;vW^yu  \YA&%( SSSضC2"Jl6jd2fsެ_7^<z>G>73,I[n韢_@AJul=[Je5ViV^iX:4MbvI4 >Ѝ’;63K[o9~/x\ \}T3M>Mg(b1p H$өXms\$g>8[l.>5@qx饗^! 8J%lb$Ibس(hz(ZIx:;v`ttjʳ>K<{%R('>qnVVZ뺼KLLLDmo~ Ͽg:6-^"JN3~T(]ZU ]8>O}իW:qi>4_d2<Q;虅;raj__''=ggat:kƍu}֚⡇#L-[D}yGشi~7B mjFz%96-^RP(j- I+|򓟌K .oI=lw9k[M6EƥRXhϞ=u'n$ z!( fVݻ;wiZo޽x;va  ap.%?_$y}_<|A>طo_o׮]`YVtsΞ=֚{?u} _{ r׮](w_t> @:뮛wݻ{۳Vn!:oY*Ǣ=jj 7ܣ//~h4~9xRABV^Kk[2i밐Jnz\W,1<u]gq& 7~穧:c_AA1 >Z- EQ,+*s>HxJ%9wϋ/}۷o0:?,ݚ5kX~}t'Xp7xcՃ,+w zz'O355adYsXe{5M#}G?Qz!|I~7~Q\sMOP׽4>gk}cǎdž <__Qׁp677KA+0'뛷W(GP@UU2Rg7|o~;3|3q\{e>OO}*ݷo_\>{ssüo;? _עZv-w~g,_җxGԧ>ś&֭[ǽ * _}Fwٚ|>'> ~믿aQVcrjuDGGף>ʣ>XLJ>! @4 qhF[P+_ _W t($Ы$i 8 h 2lfvH*\g4M0zC> /s|ߧjEs|ukȅ8΢  ,vpif{h0x l6Kdpph!9 4 D|_AA.? #pS9$x'c ARٸSzH&=msֆpUwJ֫T 4utL&G,Fvpt!s, (,VZQ%Uyz,AT{.OJ3MMX,v~ʼ>=Ǘ$ gn}| LV pub6BrLx4w>\*:u )Uk0;ç*j|0uիF4nPLUaŲRDOCٴdd xt:nԩ(JO@ju=5MYA࣠L?]F1 "a[d2*%rQS zN&Hb\A GsON&LQDoU*UffA^!=*a@띥auq \\\f} <uZ QA*#t#^(-5,^;6h*qݨhp@L>-Ʋ, K3( hVcr詪BT04 0M gEudZ- 6B>+*+ {f3 @f+-[63==3zӥ pYНɤfsUj"w(a6Kxq \ l%|P\_ pV)o$ :^˲.^0p z]Y*DvA|.~{~:.a^:D9~ 0 zz'Nf||ff>½tbI7K Qwu #~ |*fFJͮ5 3 ~X=f3IJe(y1p0ִ=j8B y(   z<cd/0@BҴsR! \8MX^78m;$IFTxu]E&ЖD:4] bT ?Pt4QT5 L0ql jy^8(W)kAΏNa6=ɴ HzDOV^4A`&tUUQU 1,b|ö[:^kp8]ٴ}A_ꎏ3wAjgBjh(.xL`V|ŵe Ng ,N:^OM58,tff:[dbBgފ mt:C*020fضMb͚uYP?0.De*Jo_Rl꠨Tk -G% U& - 1G.'cH)]#e'%d1JcYv4Z2k4=[-?/,:~Al۶~@uZH&ضM*\HFZ>k|ܹCחc߾0M{8t 8X,޽{I$ pA{4M8@կ=?#8p|;|{kʏ~{Ǟ# ?߼>>̆ fsd2EA5TUaux( fN!6ZVl<@ ZT(*^~fea&13iz8ima67M}LM \E֭[+ 'bF!i*ζ}iyk'gضgЛ{4]<,˞ϖ-;*|e4MFoȧK\088bLOOy>,70qn1]\JV 9?JtLTX*l4-)膁8hF6B! f6l4(Z/KTf p){7clf'&q]AjC 'q2 ֭[9|0?8\ |0>>klذI֬YpukX޶m7raپR6o\niG4>bzosmz܏qСHX,du jf=u,q8aD;!/2::JE:&fx={s\~K_*[8>f0pqb*jQo:X3~ `5]Z-ƶm,M Y/ •B;i?8D_ zm z϶n[^7}y;sbw1nw>KѻG'~Qz~\mֽLe>j(KZŢsYRy޾Z˧lJ CSi8k4; gUe? v>5Eٵ}qrm]!Ee9LOכguDhgyyno) z1i|yd|Uof[.R?w? ޏSNpI\cbb" |SiY=nz@Ug W@kxJQq}/ N i`:kKr3|pAAˇόάK{6^}댣s띙Z.wqzr?B^=S8 sSQ7; d6$˹;6v~4SJy_ͩӧ}4G).{*]5 "6hco ٻwr.jObǎ4MZ9vl[nEeEc58pM6Efm8qU9p7o²ޏT*MX"JغzO_8~D|G.^a-rZbRɨ**lְJr9FGGe>d۶m@3Wo vbΝnF&&&{>سgw~}ׅ!lQ~JEo6[c͚QAԨ8l߾bx>xD"yö-FF/~;(}?\ץl|q2e8(y[pIe`Vg*~]gJ1HӳSa ifyy_b% ds 8V1/SowJª \x4Db1neufSM󼰞+\4ѳ,;*ZT[Jлz3;J% {]Pˉ(^իC|A:*'~ \{0h9y(:vnT ~h]iz6mh^oz.AJ&{R۶MH?HԻA]IB(AA.nv  pNQ5IENDB`gammaray-2.11.3/docs/manual/images/gammaray-graphicsscene-inspector.png000066400000000000000000002405641413310443000261760ustar00rootroot00000000000000PNG  IHDRXHMsBIT|d pHYs+ IDATxgpcY':dzuuٞi!3(B4r b5Xv]ɤ7$A Ax _dE0{y}d@ 6ȷ@ efY}>hzWG3qѧ.S @gϾ/X\\Bp.HӌOLǹ{V+x<^Q(cq)_bO;t:Z׋^1DI4ET(JPVD.T*P*x^j5@L&Z& z KD"b8L`0\.'i~_e7zׇ /~Iuu'M&zH&LLNRZj}`/]P]UE_:)ɘf+E&= @ xıcq"K.W]G.S_WDžK9z0}DhiiLܼP($RYQsaA|~?nʊ:VR T`(HA~>HG4P\L0"LNI tZ-rs< "73i xzR^O*A,//J('?rp>mt߾saX,N07xd2I=wXvEVso|I󙜴6[ӏ@ ג 䳳,ݤR)&SzLYB$f ^GٌJbjzmp$B[[+ Y4v{4ZZP* P*8uԆl6\.' t܈L&PRb^K._%*6d !!_+N8^oCfIc='9}Zl6axxӧN(JZZbNXr-{wU녅Eݣ{A&.o륎@ J|69sqќC  O6[}S(/I x3oq .s4ee6樯>ȅHg2*J%Lш}jZvQS]h}@SS>P0Dss#| fǎ04? <^/X<Μ>ɧg?'077GNK,O+dFس{7NKK>xh,J_2 Itx V('KHcC:vmj{6%JH$ee62 LZZuAjVe[$x 3.@)+I^Y-}Dx}lt߾C62V+g8"5\vŷdˣs%vꢲAjkj0dҒ2yMuz2 m--hyاijlgG"tuvp.׿(eeeH}epu\e eeHSطcG>tIꨯ# v{P*\.]U*8v0Tɩ)څsa@0Ğݻ;]];\˸n@ Jw)\ DP,\qd29%ODtY^vJF}5 xx;*Y iXX\ aCeX3eS!HR*P,V@ w^Cq1KKK™@ U @x@BAYYV!@yeV*؉Lu\tչZݖe2ڮ2`4E.#DH,@PN˞l듒fXXX|i_p^Rڎ'ӽ[^VU*5/@|yeD"Ie,ؙ,,.L&1^N>kG"x)u ^mV MK _`hx/.-mx~%q=%lNIBַM 鼈`(Tg<>1-[&0P䳳\tErGGr/K  a`p> ky4055,lI4%0;7G&a~ 'DE\/I "NJIsLT*Ef5c22Jp=KϞ#͒Nf'`OIRi)d:fzzFa\.nuf>m[{\{5[s֞?vφJRRk,..? ٸ8ّ{L=swS{'Jq-z2==o.I~zL l6ENB֠汰Ry9Lj91ir1iB  SSx}>::EV#CF"@TA]m똞Ũa6f!NhD"TWW1>>Auu5CC#XJ,ا dBD"A^^oEEE SӒ#x0 (Jijl>5\.'N_"߿)وbاhgbN&l6}{9@Ho_Rɾ{gbr^t:M<h[3vzznh$!//рᤳ@ /~q8UҘl[*M+Nʊr._ƮlRV `ZH&STUV`ZP(9죥F{[+*Ź{Q(&l֨t*L.''//ՂGPJ&tq8$I:-*+%R p#Q^fVVL<g\&'A{[ 55ho>=P(D4]וP__d" R[SC"$LPS \xDd!|>?DZT6}{ri=\Jj5fQ@ѠQk6S]]CV;0LؿL&CEE9X01Ưk2X(/+rX߷w#$GuU]ړLp{<4c+-e=(Gu=EE8 x<ۿ۰VdBH޿/UL l6I)Ѿ  eaqT:-9Any9&SLFjkkP* I(B1;7hDIMLNb)1S>1i(..PTT>` JV㊊ >lȵ2ill Z!/OJ^܅EJfJ%3TWUJB $[?05[d2Xv{6=w9TCaC %e6v; (Jj4fțk@ ؾP ||ç~beX^tS3-*p+sSQr-#{\v >:| d/]v2LZp (:vKyyy|GZ-SA* tdS_WK8j@ yed`s17}?ff[OO/ͯs6~mʬ!74VJAA>X@*̆JKV$@ Grr0VηlogUrX@ */ߘp/+@ vSiй& wlDbbd LlUG$ ɶ]捁!:;ڙOa F f|?O|~?❷ޠOnuʇLo6G13;OEE>o~]oߡB DwhjlK*@   X-\.4qyfcN[N%JJJ̼R%8 nX,F&l[۫vsu,'˿?7lhݻXX\fN#Βn@ )do]e87;Gcc`pP(\.'rQXP"%%f_폍h}Ԋja>׿6.^fjzi~FfU *M9;TW!0yS:7Μӳ0?z>>&v&v 9zD#|YZ]tui 0KKLdyO~R$ QS]EAA>jܸu0`1)r?nmiaɵL{wg85{54j3ψǝO:&?_d"Ey@}@ 2D‚XXP@"mVa6[-M)- G$ 22:bj)adlv‘(ryvoߡ[{8t`?@ޢL$ qt:Me2AoATv{XY #1(r(r }( Lf QYYcxf:eR[gg)ZM&#d203Z7ضƮ>c"$IS^V/cXhWO8MIo~B ^>xbw0b?das_ϦW'Y@ d$fq~$I8A4hmn{({;2X+<84lx/юbo]].r184 T$v BXX\VZ-< "7A'C2hD@BiN<$iPɉKF-L&'NIyx<0(r Lfg.^[i)*w)1T*Hdi .ĩDZJIRR) Jx"JB^{9f7>e7)pq-r$ cbݻ:y> w(+T>ڵ ^s+L&R@&O> FCQQv;UUSQQŭy::-4s6tLΕ8}W^Ng^X9}.^f|rVKCTVwGFi>urM܇{ /so|9jt GnBS[lG:xI8!_y_OnL$d8Tl6K4}sa;}qIp:AEyꗃ~'RR˵L0bnnMLFs'9r3$Izhk88rC9G077OeEo~]nd`'B nܡ/92l L1VKͳ470K(B2yB\e8 P v IDATv\zWgc'D">R`=(**Dp:;zx^tv$d29x}{v%]4T#l'r`^<2픗T*7l/N8ee6:Ig2XHg2=?sv"lV{W>#F@ͶRXJBA\nnupuJ%]tuvD^awnN˙'9LF#GnnP\#ɴ|9<DX\taOc)1KIܽ?=o7qNm<k9uڼ ^^{t:j}Y0o6߳{׆Xb_vh4bw~S-B `;P(չsNkgN*27`~A~~>m-`*BܾZ:tkvA AN`~]/ͻ_{ٹFD&:N8/MM$HJ2020L2e*+*ܺ݃R c2LMϠP(0J20_΅Evuud`**p{D"\ 4LTSXX@&#,Gcyvf'.Xc\ν><l%,l-kk}2 `@ Ըd` l [Ob=}=@ XCVP(rZss#%fܽ{29z0}& 9ssWIS443:v]]X,ww”l*Rze!cn~m}Tkwĵl 8y#)Z[ #d284 nZM&!4ß߿&D$#tD"A8!͒H$'O_Hi"D 匎x~xL&#m%}`xo_?DD"A*"J~N$1E!JYѱ-m }W]'L'q,-F$ :ׁAbWRld2b\^_?o"JIX})0/(Xl*Sͯ%7nIW\<_g{CciEUUؿOx% TJK`bx(?"yD6d"=BPTM>R) M>RI,ؐ"NQTb1 .I;&t:BTh,J$sɤPT*\.go8x*[JL&+8PJZ&Zݿgqq KI wzs6 .^DQ Rɼ4 ㋋^*G&r-3<:I1jt|xd2z&'`=\. ܽ7Zƹ? HHh\@?@6% QWWemz]qo##!No#LF#ccsM|~2d\~Y"}^1x\X/B+~<^/eX!\?ϙop8d('_wc!RTTȍli?+[=~"̮1Mu:& sI4ͩ'p[oI8|#+΂+++,.`]]\|H$JMuUC!4y|y~}ٹyfgx̩J](JkOͰD:_2dg?`(FR37?OUe%n7&S71A("sƆz>tZ?8DgG;bN'V%2s4 L0"LQXTn/ܷb%fy K.@% 9}ϝgiE:Ύva ! fiS954M¡9 |~.~PS{1P:nib(xrY ijj`xdh'JI󱰨:m.`0D]m 6&SPTT8s$T3rGjqcw9q(.]Z=Ph4KQx~F;z ?~l /|# wGiyM^Ν~9r 0fg4SSJK G"LMݻ%cDr Iგ ̚k20{9_;;rQK.)1oQtvg.>,ƭn!+ٽ B"ir+{v  2LF*+xh4LMMgw%f`ƆMq</:^{vB&q-h5I)wjZ阊29uje7$N3BRW[BdncG KNo?H=z}RFQj5%f3m--d28@?nz=vIh@Rs+'O3 >T{yeχMQ;kY\\C$/~%2/];m bbp B&gj}B, &t*MgGdG0"R))17.8}WC(©'~ ,,,S*dz*onuFաP*TMO}(x/ vV=KNA^Vf>EuUkNI_+0BA~6Yv%>:@ <'q~sK$.v8.1'xV {d2E4T狌`^Phջx]?Bk'0OwOpX?faRxh,O%)d*ŵ7$J"?>"O=t:'O_K*")!LJ'.l6K2Pέ\~cDN<`e%,˗w8Fcx^2jTwfC;B~ L"4008ǟ~˵L,#o ?4<¯ ?O^< k2"WVeD>Z'#qҘߵ>d2Lȗ$7{W3{9r /^fvvneǰom6-/b1ׁ5-<P(DAAdD"No{vL&>RTR9H<LF,y T*E,GӭJͤj=L&QT,\X-l_=|Ļ_{|?U>yG 6RE$azv&)i'sg5);w(/֛gd%!:;{Yxq.,JOM}gN1=3s,Rt,..QYQ,*Ɔz>1)9Q==hy89} tZnh4FG{O"Vu۪*+q-/ͯ=IVr20=46Id2d2:"'V%s?HEE9KKX-bm-Ry I**|a܇9z䐤h2Ѭ\.ilhÏ?bU>|~<55<\sSj2:6F&jedtyJ׋^GPV0;6Hrt)kOjdd1 t:BX HTYVVGۗ2e6s6`40i"IQi4D1('p=0ih4B.\XidxdG6SaA!u55B  BVіjr# bbbRy9R[SCUeO[D<ivv`}ZH&L0==J8L2$ɲĂ}z7nTw#d4b)yF:IPd"B!g`h3N`&pV7% PW[ddjz:'H37`vv{vH${J\.7N388DMMckvn7Ϝfd.z'13;>5}j=p8(,,t?0 pu8r =,.|,.-1pbA17ı#\yDH$4a(.&RQQNEYzJ)٤ZilR 456HMF (J*+)+I}j+- P[S#e+iokZ 3evh0ޚ]ݮWTtzZhod Jц秽>CoH=*ScqO,e7h4l'G~~>]ښj0IR4 {vj% ,_{MsjNlɟk/'|5p844108DEyy> o=TWsk5oߏ룭Ph^Twz7i}>ať%\VMm©Ǚ$֛BMM5'eppM\N$IFC&EN;De`~ݧӫ2020HJE}ee>y%Zdr?b x9ٗKن*+:"(:78D$x<[_Zׂ1-I5l QT?vYllXv{Ţ[ rTU%20gNzi߫Nٴb[B ̓320OEnEF&>D&z[UqRDn_mo^[`':pX -ĉFqb$ŐL&%LJKFղrQUY,* :>S ililvO/{AvwE3s\r2p  C_ܾ΅E.^B,ʵ,-z}ܼWQ*GOe}B_n䃏>at.~)t7o|(C  ӟaTyS*+¥ˌqCyn7o1ppo|ᑱn9,4 .2@il6q=RPTTWSP>R+##c `+FGTP*( T*}i" LdR꯵>e=z>_*h0P^VF(&(+XUa|VV{[+^e6f h`bN&APh4D"Q"}9arrI0 LIJPșw8YYɭjFjNJ ܏ӵ8y4h3o~{6\VdR&D]mT֚E~A>D֖ 20%%fn:1td`TZM&EaXhoku `T*Mg{@>3*gfط *+ڂ˵LٌlbI4ףi1 تTJ0X ANrŁ}{7TOVv?< ̳p:%E^|20.\FͶ9yt'31a'q-t gNd|bh4JuU%HJũ/L&'nׇJ4]]c4Ȥ3tvOeEPHϼ*F)8u8G(d2,,.S*9l(Kh$V07 ۹@ xq< '_7fos`>R Cͭ]qP(Dcc#vCY^vSTTH6EGPܹC z *F}{wm-:BF ʓ8nAn!2=3ZՍ;+++[?{fh>ȑITbRn{c؞zw?{fO sffkNU*R)bs&0 g !J}@./\1k5z)..nZ'Lrq'V&&鲘grJAa|F*=Gq.-QXPb8/y"ܲ}';(nxgϋц!~/ߚX^Y`0D4CVS\\Ven~L`(2bLFaaazgU`(HAܜ,|Q*=i% 0dT >m"PH4J8!ܸyXV IDAT%DQ݇ w50v~ZDDLNN]N,˴n\R)b;|rBfGD|~&+G Bw40>ħ40  v"h`\a2 h0f?{xq2jDnܺ;q!rwZnO/`E+W ږCiO іFnA\޶twuH$2ϝ9ùęݬx(}>?eLOϠP)/+CEAA5UϵngUJx1b1"(YZmN{I&|D"PVH$:alooѨ۩>m埭}]ť> (*,أr~=$I|r@ICCsã?ș3 L ̴mP(DmM5#c TVsy LEgthwJTcw:qXGFQU\3?\.#77r._:lsx<QZ_h` h`r9^I']22j`̆`Fd_\! m,XL&CT\Z"'ǜ2m9|x{YWFU}}^~.8ñV EoMM5; OOĢ4 JD_ۿ`vn:f R] RYQL&c6Kyi)s ~ XJK,#rfD"X^Y5 }E,٠rlmIR$ 2_s#c,HRnjui4p8/FYZ^m eLNMc6\Zm0R* DQrrX%$sPWybp.=D͌md2\.#"GAgfLw\2G4gPQVFјIL&b2eH$N'@J&X3}@ʊ b9fe?&h[?g"y>"8u~r QJMu+X,d/z \rDg)tYY,rPT~ <PV΅KW0 ?30xEE^Gg)+ZM\iWH\Z"rhw`6e3>1[o΅Kh4X}jk=(P9ԀqI.Wp(99i(vwom=B,# -JeeEbܣ Ü;{X,+*d"Ik$8ZmD"`0>&S6Gd1QW[BX^-Ӻ3>2:G2yh(*,f !J119N"jX,J .D"}J¹3I$TW(n],,.RW[kys+$_Ξal|ZMyyMe%D#Qt,Fijl[Vcw88y+tQ'bT50ϓ'57nN~v50u?tw9B0v&qΞazƶ)& r D ]]Y|xP2f1 $;B2rL9V g?]ϋ``yeL>[ Lwn50{+*,dNk`TJyF<^iV;b|I.OyYxME_Yrq֣G,:F@@@@Iy" Wx"uE\l/g)w}8>y ̓hwT>\"rp.QTX@,C/g"+9]v'?dCvo ǏMI(J|~?jDd"D"!J!HF( rq Rzh\A2@n|5*˨G?)Qi=r$sJsmGt*F#2^;w])tgjz,'sUdr͍*r9h4ݯq m4QHeR C:yd2s ɈEcR)(u8tYeb?jՒI>wv/.:7opRVZ\ eJeH$bDH(T&txE߹.X~ n\aչH$@ҹ-=refqюՒOEyn'3SَL&F` ?/`0L,#KŒYٔT2D,&#Ap8())6;¾z"K#$SIgl/̉;_%R v Ւ,P_GlG׳hG&Q` %//; a#hgvn ~;;(x<ο78f67ޯ*f#W]Hs`H&1ilgdl)O`aafgft:yv8L%ڙ3(˙_^o?DbiaRlb)*%Ko`չє2[.[tXKQ8fE#-9p-ۑ)baz/7\RThJM^wzFXKʘ$ 6>LCI KXDP0=zSZRI祡$be'jmVf/"HQ Ri gg)4c~08"$mG)**DRRVV`Jh$'ǜC[,( jP`fY-/9f3n&Lljӷӧi(JDb<ꩯWOLF*?@]mpK9SRRùD4V n֣G0LXLr1 46ԓG %%lnna6HR`Z2}`QPdv^kԋ<=cEՒO(j0:6?[@&<> (-)A*177GSS#cF꟒G]ue|5ʪyBTqtM<ݥo0?5ќJa6>L01f'F/  FcI /w? *+Tg)>.Ӿz~y+9nU4Rh+bx[kFo҇x>45$I$qZ DP*ܾCׇܻ?,t:.T*J# %r [oO~ U tD"f# ( "[w~$.‚b8`0NN=bgҪxH$R Hp^/ee ~$Cã 99f>};}>_Fyy5S|葖Z)!|?ʤ_൳~_?;<fG#U;J/Nb1t:ݾhdRWv$Z(:Zd6#=}14A IľL&#5O@J|h4M:tbB.xqmlP[SqbC!:Px}ڥo`cG?3_[k.Cg QEq!&Z(*,tw'cbQ7l;$I *|>ΥeZ}->x5(,b;XY]%L~e ̾0Ν& RTPLf BT3:6NsS#3/,RQ^ƥ+Wy7p.-Eq:mх˜pL&#T㡰1TJ%`C!cBKSs |>rsӳ9:; 77;o$rtW攝Fd2~Iv']2lll 2Wx7OY12o`vb?}ⴶ梸-LCCWtwvW*2xmHHR,%L&#q{_X}x>sS*ЀFHKa7+Wr8p#MLMP` TJUeYYYb~g/,`D"#ɸGN~[o0xƆ:>#Doc%LFyi)Υ%;ܻGEE,`p8L}**t) bt<}(^pe126w;t8hԸN?&:DܣFBGZ_R>8Qfkg)._JQaj`(ȢݎD"E*a#z)Z c6,(XR(0 d2njrT~y++42UZ\ZirrrXZZc4QeiLnnR ҞIgZ2תױi` Z@0Hh~uJHhrmQPWˏ~3:#ڹ饼 (}hKs#S6|>f][|z"@r1R)?DQaAZ P1MLOۈD"XNwuC~?gOwJh0BKSS&C(P*2Nwur}ϖ5_xڗEnG@EiZ"y*G40_7,VxokpS__׿}GZ012:6H$b&9~&SYo@O zp>>:,gx@P_~c꙱2c)Z?/pM =%Ŵ47  q\댎3=c# >6B3N&XLO>`]2{iRs _αT/qe7޿_7~χ@ )**X`RI"$Lqxt/,y$IR'@4.K^"LV|iH$@׳4;wi/vWѰ1=cCQhD>hDJJp8ױX ^FP(Lnn^,`0D*arr DBcC=C24TQ^NaA,'~2s ڽdr"g S{~a_};{$sgSfv&YX?{OMp].^¢/;wY[s{x^07oayeѱqFahxo#`w8Lh IDAT>7nftl\gdt}ֹu.vɩiFFǞi <ѐdi40vk~>e%|>t;;;O"(,ڢ-;\R)mrr "GnN0jexd,3 C,,,"HJd6#1䘱Z,8K$)&XF#sfd⠌[!HYXXP`rhSkhDǾ֐ JC}mz T&֝=L&#+KK8&wr9yy9䘩wE]ma6Kó@aJ:~2:Oyh~UJj`#Qs$!H&iBAYi tv"?? pP(̢݁^рk݅㥲J!X;8{ѱqJK>̢3 Lgǩ EE-@32i =*+kUT*++^oxѐn\c_.`gi`e*~VKZUWW{:Qy" “{ٷWyHDyY?y6ﰰht׿ۿH M r1ܣ`0 ]_xK~*+ږ @Bh`,:~.Rg ͍|</]ͽFNPkgGx})FY\SUUzQ*ͫ9,|4Kt Z%<eYͭ-¡\饭@#h`'>C˷ua' T!vx}_p:)*,h4]-8^D׉r|ÈFclnnL&|lllv?2\ć]dz(׮K?R>[?4oa{;H$`г/]^n./H,zi4jD‘0^76v#L~3>8kx<^a]Ok`Ƣ~dphW/336|p#FƘ222?9ٹߑ\q1| objz."p[ `8n=Y\;ry~[R$'& r^҇]D$qqrsYsHl'܄dccO8I*ː%faS^^"hc2FGõٔM4. ަila@VL&#`6p8ƭۇXgQR\LnnKKK45qhd2 DQ|~?h`0a"!J_`4p9[wkkAr,]y99 phRf橯!zY_@Qc6GxBa2e#JKk`Z !z&S6DBpP(H$h5V J#I9 V "= J%dF!SQQƚ˅X,~gQ(>50M x^T*%[? /pX<\< U3==Cwgex}^P(ҷc8RTWhlRYQA8A!WR;_| j5]|JY[sD9q%%Er PZLXieHii1-7ee`oڎ"inl6c#i=zMNN5f40x}>&"qdj\ii’o!P"o,(-)l6!ZI%SBx90vz׋NcvnvR*GԔYR)+-͋YP0bovɴFEE9/\ǹ}Zcf 5UV 7n!dBR)~Ͽ.|XLIq1[gT*p=a299fb1K2rsr?4B ``ye&#ELsrҒνt:n߽F\Xoq|흷2nJjg1*JK3@eG"I{^VeW\T*ZZq}=stL*+ʸwӷEPd^OAËr (,x140ZR ~'Edg}40O@@yPnO]"aዄpK֖\F%HG^?Slsj`_ر޾;|t2"8 c6ˌmZZ40 x>B0ƭ;( a#!7~NW476"P)C!tYYq"(F#GD, rI>/]$IR$Rp8><!hD,欞&!*7=^/JLA14x,G&HןyDP*\];A&h?+M \D1Z7x(.Nk`ܽGрH$h4oq"mbvnwy.^&MCCv&r\'Әy}x}>W227~}twuh'_|I$e}cRr;|x99f}E]x^'(H%hHKGZ ^)C8%D"8u8\.$}36Zp.-ZGh?uGߌFe76$JXF;G)ZµNeE950(//C"c#  OyY MLh`,w508K166N^^.h`v}B8M_P> ZT"!ڹx><^/%%EfWhjl`hxd2 ED"3y$E*1;7?nnnqfGc42'?V?hA7~u5 Q^V"u5_Fm$f IѐP2;7VbGP l,i?y` ǹ|U#hf{˄B7˲@Ro7:glY磾:y99Fij''o>RY*'q:-M\ ǹ Z޻v#|5 ׹' 5576pU D66X^Yh0`7^¥+U}Mkm0C(JaxhY:u\f8I87_ۜU>NkprE;]lnmѤ9hJ('ܙngltokEKtw'KTgJkZ{"r>Aj52vzP(TV]DRQ\\DKs#"X,N4;_D }s`{[pRTOaܹ{sgcjj*P(-7eLf(˼Ymnݹˉcm;YYĔm$JQS]tS_R]]X,A,S__G"f嗿 pSlllRh^oUUϩ.L# ^xA!Ұ?+ãY_~\TTHQa}}45603;K^n.N2 uhZ,V M ܹCUe8ge[`sk *}dNC,evdr0HC,zuJK9txxv-HRJ%H)45lfflM$)\rxQTh4j>?XΞ&HLj5lu:ffl z@\.# hh5n޺CsS#TJd:ɩ)>mx^LC!D;=twu2BajߏFp`믝wϧyzpnp*bsk NOF~~^`wg1kV:]C#أcևEDQ>?&S6>X,hxhA*~x7; EPѱ 7'][\q밋"\~3_?xHkykkkLLLe{d?=2 <`f;#706K2V70"%Edih/`6p.-!HRdB$\rH$BmM5iiƹD4p[[[d2T*pg rI Ysh'JQYQNVVR.OFc\zoHAC}-`K0&q$S`;x~><&X_ߠ;w{p^/eeH$lsD"&OȎON@VˢݾYtRQV#cc|62*mgGbښj‘HZAiI1pz0x47p:YY]}99C*uExBDJ$iV $ > X-,d2Y~?FUm3<2b!2X^Y>$ 2ѿdjz:7^;˗D"( T*%JI~~>r|_'O$IzHRB?,B~H餫Odiy<?(OסץMiIZflb]yr1L|jǏre:M ?xVWj4tuuph@BtGйA\~𣟠R)X[+.^~{}=T_sXz@.0egjXY]~?x^OUe_Vjk"Aɔo~T*秢 #-1st73Y:p.-Q_[CN?o).*d~aJcmTUUH$1eg#8݉&f ^tL>DLɓTJXhD"ZEc!} PZZLiIЏ>07'oIC]ݎp8LMU>Yrssa6KSSÃ3b1Z8+;s: ~/bvW#PZR& q~ \.'lF,B!'7'wGLBsS#P5:r9 ^iH%_.²jk3/גC$QxQTQR]``nNzz/LϣhD":s{13*/+ҎݽiC@z@N1:J%u!2 eI50/2n`(@A# <H"<"h4?H$I&LNMsxzm#Ipt]Nwu㟾Gn7^?G0Bz1LlnnPjlnm"ZE(FTvSP`!7_P(LeE9s5j+8,M{@f[$ =AbߣjІBi:Ѓ!42Xf}uY" x&݇LV"~A'=y͓'~A4z5Z C$Ijkō]tΎ&Ju#^X<Z?j&'iO )9`N'dY6;j40{IR)t:j`6tm$v50?q j49 z5C!R2d%na$'hkkWA(rїF#w5"BAԈ399 9{4`Қ޾棾Ɍ^5"+^/ A p BZGܹ3xds9f\[wz0̬GZB0r*] ?Aqp+^/hmia|bɩipazxRI?@:_gbb32K[^fK &u(ދh4F<} mZr&¼[;|U.ۦu:G&J2O3|ɧܾmQSSMbD0hx":&O,.-fY\Z"Hrv7n$?%?O(7nr|#ϯ7~2ݾ7 BÏ|v+U|~?%H$y6K+  pdA/~$s dY|nC/pv}\~d2-]yfb4ؿNKXnֺDP$16>)ɈB Ncxh4j;$^1T*Sb1VVSWS#ޛ\N_0 IDATgXZ^L̊ ;cGo8q쨘K8J.jD"3|&\.FAP 'pT7dLMP[SM z!H295JDP`6DMfb1Kgψ‹ VC&EѠٿ*'C%^<?o2 zٹ9&9p`0aUYAKK3{{q<|86|>zm$I&[~ o0hX~KKؿV FKΟ;KeE3s 'H$/`4iint(BO1;7xlj/,rY'j9}$ D"k,--L8|ʲQ$ϞaddiintIz{a>eZ-.W%]_w\ƑCK]^['(-UytsAMu5ښ*F#u67Lh?@Mu5|&)nkmStP(ڂ\.-x1DH3t8?*;-vURSSMWgMhZܮJl6MfZ[Z,Tp|erjhۇB߆apjDÅsgik55h44 lHSS#&:}gOoi!b6ыFchQC&!<:% 0vEI!u%$$$$I5) [. AH|w6{ Ղ^i3g_ۭ?gGZAJZNCBb@D2I}]-`V#Z &nҮ8݉j1DcFFl2 ڬLMMcX@xNWB ٢)5"8~(o$2tǎ?#MNv{cmm7_ħR(C'-?V`Г+F+Ҿ!jazɋ?(i`4j lik-ωr'٦"XDbLJaP(LPD253˹sg^4Z -M,/ȕ7PtZ8TӧD~x"#Νadt-e_X¹3?*i`Nu50++, >qs ̊1Ξfdt{40}e L,hv} v_,pW'c|v6^*'xZ'UUT384Bܙe%"2KH6jY[_/8qP8LC}=t&]ֈfZ n FKs3ϺvO}]u5456t18NHgҴ45FBh@ףT*yA hrWVPXVq[B ͢hd2drjLJ  A/X-fh0'LMΑ] ?pd_  eS3zjkj 5&dG?6gfUJ%av.z(tFCӉ\.jpUFӉJ:;ѡ멬(E؜b֝ QG#xlNql6H< ֖fZ[f^^{ըiR/\6R)[}{Ɔ-mO| "jDUe-ͥsnڻgcf-OzdnVr<6p8\.SA!Fpfbj565r T*p_Z/yj`6µ}wdG7DH$v^{ǏQf$tH g40|( bQ`}}2奍 &J%hTLS, ( J%&F h4L&ķw06*+ۃ$%.fn~+4{|visKط˶)`C$`0!oҨDX,)-ܘh k<dN'T*(JqʳJHvFXk\pL6 |>R FeE`BݾFnܤ#tq$l6[RL͈,*JT?BQYQ tXv&EpT7\EсL&`űڬd3YR c6rZ 6htFYtZ-b^b!R* #TVVǑQx[juU*%!OkJdL%$OC6#R6-dT*a' T*mSxӁa#<7HҬrUdp:OVw=$zk`6Kn6k`~ӟsQ CRRW[K4s[6s bYm0 WTUyp$S)gHҨT*b8-M[GPϣ)eҽBlsH?HCC=`JE6E7i`f橮eqyFq:alV+\b~ Yjk'yڙxټp_k :B@4DP}/mŢN%/m˾ .s@( u:2 Ldbl| \.GӉјr<ΣVxD,~P_2Z[H&Gr]nn:\΁mL`6xD"b1iF2 h0 PTb'lHW  qcHQ8u$wz|@۾V1n[D2I E֩ԩ9|>YZZU~Asgsx?uݝmZ_XP??@ccL(}_@@ S__G]]-ǎ&Lh%>scZA9sD2lYܽ"&E =MMX& xHd}ZvKTݎZRnp8B:‰t>rT]GR㟖40X\l6꼡J_'q᝾/:l^Lh4dl|Μ>d2$xu1erb~z**+(J v3?c\a06s]|r*FfU,oqKG4Hmxw{;w؀B(i`%` ur, 0hΎv z=U*V^inl`l|N8jh?('W($E҂^#Qb8 |~C!/. BYҾ50nZ|MEZCxIMfFGe24j]fV(i`ރ^kY^^f_K L(NrsS(Шd240FAl6GXqse[T h A(C]ܹ{mȤ3R_eX]UEϽtutq*5Zv˨nDle1L[-/?l21:: Fy=L<huZJ%c:Nwwsg_YZ}|> z r.DYR~f CA& K|eZN''aQQ#stl6KÙ~H4s$6f#Xi=swlɯ?` ˡR*yמ5؊ XBBe"$^_g܅50UDsDJHHHH<+fcXv40RoO0;7Ex*^t??uZ +^ffj4":F<GRmZ4g Pdu;\'La  w%j'(-ZUy4bZ~badt !g|*G/, is~/^# /P2,oq޾@.W TjGG"rw^N:!26>`Fn χR$ŸsqIKx{ţ)B0Od7vf|Cb]cdRL{gX,xB.Nyh MģIlף,,.; z=kY%;D*BĮfb48 *z4@({6_nAdXf"}xXz/zfAc402f3GFd2TJrE\SAV3:6N,{iY^L"ۣu܄^-Km׆fzfF7HRmL&Cp%)s h~| )Nn|R)dP([F< oNkk fZM ra2X-T*4j5+^<;]ƾqUV娯caqmP*jkq8&vFK!.?K0lYcXY\\w85%ucGIu` ϲfKpI:Vlzާ!Fe؞zx7) kk8ƕk7x76d2"C^m٬hZR |>\@T鴬GZ-XVVW} zJ%Tl.n'NEOiQW'V؆aZhZ^].` X<_|"5<.`D"X(J0$),3x|!O*UK$Edc!J/ V+x G.%LoL IH5A"HHHH<7Ǩvy02bhd2Y)|8bƠDl( RFRDTJBRjdt:28,FV/GW JEl9A~ IDATKy$$M^t?kJU/PYܲcS*_R /jj5\xWq8RS]E0q4_nܤcG38 V"/N](HŽ?r/َ]D&!033+U* DGh5<733% LX1;Q9szX,b4dtd2f`pHPD׳tj'eA4GWQZH?3_[u>Z-fx\/P_GѐH$HRE"ryC(8dLNN?8H*j1 G3 "pZM0djjB< )M{T*>|*TT*J]EבJQJ#W4sMn|vl64z^t~/+^/@}TVVjI&d2< Whr*WNgXz }kA/C΍[(C;zmۇjAѐNr=vt)40?j132:\.G&q'PT?daqbHeˣy Maphh,9W&JH$b-F ǎpPܺut:b&n{`czf^B!50PX|:T] bMx ~8R}^vx>50JIg" >dXf@&C!W|&V˵c؊FfddX,FGzݧ50PV#eKS$AP(ȐAnf-6ÇWw𕿜4@VKcC=CCte2q$P&B^h*?WģGh:r9EqQ~h[@VE #Ty<6UY2>JE0dvnÇ)V37?2xNKh4ss4ױaِda~P*aJ?? O Uhc8ann&˅j/P(000@9I%$7f'S*>huZtZ-PۍwuF:!QS]E4X(219I" ۬x8rl. 짡nƒ$W fťOs/?TI:w[̩S\&_(`6Y[tIg2$I< b4wgH8VWx5)v"X,;MAJRO|_-qr҆薿?c}=;ox_X;͇Z|<Ž3LϏ?癙'L'Wxי/g}=mDQ׮n*!(i6ڳDF#P8`mm^Wx$I-X7ߍ 'pDPϥhRP(X7jMH-6 Y` _ ĻhDV4l2n!_ Ja4Mf;++^N|htݠ׳Ao@.MIo755>FX||9ˮ˲&[l% L2BӉY" L"QYHɽ>Io'$ s噻Ӓ[ByiD6k`Dm XD&!˩򸩬u 9UU?@MݎG?)-M(<FZE(ra1OcViCt9Qd0FvA;oRs!yf}]^V.M6_uu:D2IkK3tFjl2q]=Ty<[TחU&؞dd|{/]X=xDU*'x"G"pm,,.:~ Wbv۽Ff9ݎ`qӎkZ-[d2b2:~QWB__z'P.SW[K]m7ߛFyvڨYx:|y;OyH<;ۦϝ=M(bjzd2JrLxKYlG@ڽ1+!!!<鱗LF{{;O?ayݷj%!!:ߔ`b !H#[bd2L{d qvA766l4>Tr LF}}=6m{SsbTo)K~_?.ڞnuS;=sKӼle2(@$kLLV ZO?8D8FMu2dܽǏ+tuuߧo|ng=Ptp~ ϕm_‘ y_R{S& m9wϗ!!!rLQ Ü=,Tx"b;#c$ R2'<ݒ~#v$Μ"`Xhg_k n|l&b F+X'xB>y͗' ( աR3qP'vd*gNwcZĺh?Rl$&}{τKl;vɩi._?Cjj CD1;4}<_<uә pRɛ&կ{ #okchxC]X}$$v-ZuZ*NFCP7l2Lo7rPi3O<^S]% i~`s|vG*ݛ/믳 Y^YD"-u8550gObyeẸ;wqSlϔJd {E4D&$ ϗm333i?xd2E:N=f{GǕW T@dD MvIxFGj,]8d,ٖ]ZlKkfzٌ$@"CQ@ "l"9$޻{ޭ_ҹsܼu@ HFFz7%.^_ 033C^^eideexq8x>/8 9uDrǝu/}k? g7,񴻉5VRUhz~<ijh4KBL&~n ͸l$)>e]^<{Xݟ-c1Wxv'/ _X tѨj#afggW7"B2'J+ɘÜh@!x򾄄ēv x ։Im6\mfEm÷+@lSУ揿ijEَz5#/?[`h!/ MڕڿۭWi줺w:p8?̽{w.'Tqҕ7:&!!!ߐKHH ? ? DA1Қ9uwF%$$$ ~@-C+A+GP@%CP|5*H(b~ˆՇ{!HȷͰY;8FΜJdr=p>^_l1Z&&&8Q=x}>_fe UBa2r@(qTOQ *JP06gnQ/ AOhDĐYDv7?v cH}7V/,ȧpiIeye/_f9ziDr"&%Si,Z#.Q+Ieh8ΠJ-GedPPeZw9^WbA73,؂D1xN'˅^VՓ؄H$# QС,tZmS޻nzqώkM-dfg0 .*Zc3Gpx\nnnh406n xn IDAT/^̩Uy Ʀ-f9MW߉u Marj,ffx<>@GghtLF#m7^O x(ɩ _Ar+>rQ&)lZr166(n0ccB!~?ߕ#Nj! DnDQFqDҺ@qּ8^Yjzy Tr&T7o:~Ɵֿ..+dHHU!SHWfǽmvv׋VSiaw(Ǫx&};\$W5NZj*}et:-cr/kttu12:J]M ss9Ujk1<:FEy9.*$>fKG I&FFFFx^,4*V8b1;n9@ܶϖKoh}b"LNMQ[sNrzrcՕ$&&ğ9 nJ<[֌ƦT*"1`1@355\!_^Tq8d@$S,l BZZ*.R*D BzqFF};[!JRB"01%7d~̐q/>\s0aGL&.445:##$&6+^ǒn&##9232vόl~׿ظ;9I]m Xnk AHLL$!AGVf&VNh23;l)+-G+xvǟxD}W6QԨU杜"?//n[gJx4].AGnv6 C $!Aᠱe. inmxmmpW"p"hBh4r hii[QkdebfNOO#d(&>!!ZE $=tLOOPkԤ$'N[br1?~NגȒ:UGɤ]>)( } G̏xOڂD+|ɳ-r#z-}z{_XbӶ%G!Octm=7nގ)JHVDQd`p#ٽv'G;6sYj.9["hLHLDFQh4j5j:SMMMEPr`05ZH4ET$F0f;JDD8 @4*Fp-m>Y] HZRg+Ar0۵~8j#GcӮen!KߕW:tzV9iUr/+I~ 26Q6Q9GǑ Db;g~aNAi=zHa}'OHmg"rV ٢!eտq5iZZ_;.13;FC;vE]$>[N&08 4SS]˶ۨor:Ћ Ip U >}d1=ȵf-d29E__?Fʊr:#P>}/Xڽw5R]L`xx`(<. I(68Cmq6eܸy"]w9V]pvZ]ʙS (+ߋ>)߮ZS3Y\u51y;TJL&&C.1<<+"H<^@OmJf.&0::Fo_?5KPT||>?m7_pEm1q~o|GB^n6u.jkV- yޘiTVp8 N G UbQI,/0?^A9LBBVjkfjС,{eYGb 9$KiI&.AOJJ2ךZe[$x^  zFn^[0>\HSs i)hlnŠsTTUSǑE46!1,m"[b(r^_h4JcS GFU$㣏?tPSSWt*5Ǫ%9hZrˡ,:j'v\XBBgfgsZZxaA<կuJ_͍=F'{U_KJ^xE#9r!tZ-ڛs?pZIګ;UMmfN{M->wULBBbs,f3_{UΟILh^<k#yX3808Dm1\nzcUp{E"0Gxm-fEQH4W&Ȑ(rDDbQ !!!!!!'Y9<>Ғ*[WK__YL( DQ02gr.AeDqs*OƠc%CxNlI=z "6N'w{8sa =tQs ֶ=ݞVlD"<!bAX'&1`t:Xu gSy5qgffpѨ)'9)‚233q:KdD%!( sIߨB׾WA~Xp4ȴg;;ߌ?~8 \ml"#݂r#r.,rϩ(/Zc3Gv O%7o~E:d`FFd`v;\t3׽$S^w>Pb356}( /a4().r_X$SYYNku*6%%Iry8{=yXPUiϞ)e0gETiu$_mkş\MCb`9Er*l6;JĄ0 +/qeT*]=Fۍ[ǹ%مu4IpYgsrG20W5b4%pѨ9^WC{GW|g彫LaB0"Gh4u7oc4 GTt8'3na4Ɵ缼ܸt_ d=-m9rpyY\cB7OSK KBosnL5G3a1e MN@/KqqqcO=UIIqшl6VS'y46@SS 'OpU$6 nGAo|~*+ {Q©ܼu &5tF"2LA~L񐟛WtE1 Vb0Y;(r4 SQs?׾7XX!zAOg=*+ʙ%#=@ NôG݂`;DIJJNgϯ2 ?/URkw2~ݬ*SE;u";ξTSE>C"[8vZ@Ỏc &"D93>y;w;N6'*y뭷x .\\UUAYi S3ÑUkin,@ηIDQdp(Dbq߫M\ #nΦ+XXXhBPF}s""{W3o?3bW\7M;2 91X<, G&iѱxHYiHBBBb{ 9)pyo#9YpYR0 p2-m`eƠB.k <\vDDǨ61g7?/7>$xݎLjoQ${~"EtrOR1}7zu7=I<-HA\.(pxw#[K,+p8 ol: mp8uX5t:NLPZR&kD"GHMMd2Jv 'O|Qu}=Yzk[ѿx"Qyn w18y3j,4d͕\lA(P|urt>z]Gc_##Td2?hڝ3;p8c; *+{CYYdse DtZ55SQ^FףVⶮ?^GsK :->b'Yi?@YI {{2@8TWU~(.L}Q />+N#xø\ ~$$$T*H79 v FU*}$''aFfg瘞a`p?9[Znƹr;wvV+-m,,.rZ#6mG+#^7ξN'?& (S%"FnKO@x7)VPE?Jue͎s|Ta1q8^Khlj20cchq`_{? )*, !!b?}waaҒUnZf{$vH1V+׮5qNj1cyqNr.rd2PugXj4LF# -mx}~ffgel6eP<ʢ`j:aʢ=N'7@Zn>@0ME^@@on+TBL(ް Bf-`4.&A{L$.u&ǃB`fv݁>1CGUe9Zf_ /:,3II&>/`(&/a4vG Op88|>?j*~Z,fAXeGsݱ?𬴟`+465S[s ss9z0M-,.0 >`7N 9*߸IUEynfIIIFJ205 ^Q^Zcո\. 9siddBKK'NvfxiYD"[(/-EKj;fz!(.bL2O8su%gjsc{ɫX7 D@0$UiW^>+k7U)_~{?rp zbU`:rɾ;J=_X,5<XVkY2eK<F)-)dcy‚ 﫫*xWǾkwL0ht㕲3paO[FJu D# @fnt$A|$$/5{|zU G#~Bh񈣄idD#sH20I16y go+ό!Q1T52DwQ7t,rEI4ED1'fQeSNs8$Q1LD E|r3hvHHHHHHHXgvns~E< x|H^AڡmomW!0$qz"8}ni6έɫ@&H;+Wx$pxQ% r nܼEFF:ŒL.jcy|v)&#R^VJoo?>He-שv#6$9 PFFϲ+/Ŧ'zvIsszș |gdffP]K~^.S3>W_(} z0 $%%1npL3kğ͞HLH3pN٥+ejzC2_nΜj`:Zf`pS'Op˜=sA>"VZ'dsCYY9\B_~qZLFJb1K@&> 8=a‘pYVE>!D"""28iLm OKX)hq:]]H.`% O. "RXPubJ*h0PTX@0\l.,,PT[Cܞ;r `u ܸuq[TWUpt:e5y?T,f3o095'^&eL\HZEdlxi1P&mOhUcRnoG(v}"+e`^B,~}‚G2"eVjk /7G2"F!~ Z-\V18V]͍[(\FÚg̩妥L#!F=F!7'9&Qh#?/9]++1.__Gflʅ.qpճ)zc[ўt8ZŜF8pQѪrJmݼ˟n'))ڷ>'^n1V%I\6CyJJNgϯ< \Jqނ`mD}7k[e4w:hr{ު{c<(G7\zwȿy_ަeI? >ksxyLexBJAԢSk5Cwg?x6 ddςz',-+-_})7J#)I$(2p8>uu5}ryp\d7Ʋw9B&r.Kͅ7gӕ@,[`vvC ih8:AfF '^_F}!F8Y LMM; ,..D0$++Q-f<^/6i\x Jzy7ݐIGD֚UϽiZAOf*Ў30d|q ѥ]rA {@; y=Me$*lN^g|p8bNKC֠֨0?._>26A NΟ{9ƕju8]N?C.4j } DZBM $%loD)" EvUG @DՓ4e2d5,_k]u[^ޠh0#V cn|~?jE2\f'99n33;Kjj '&VQ搚LA~>P lFP<|& ɧ,mB1bx[fZZ'MN0nbw8{]M- rrd( 6508kccܽ׽g`pQ?D"w;(/+啗GO{yH$BfF:rλ8V]u"v-р%BnN6KWr nwK\y\DQvlN?rZS,+yn_2n) ɓtݽ(y>tw:>&qu`XiښsDށR$9Ʉh?r1TQY^fPM*rRQr4 :.~`0RDL(  &LFJr<# `2h4%%/]w?.I‘+VDܝBQ&*nl(e+Ar =&c1XZ&7'Ӆ\.C!i)岴LRrFV\PYYNee9TWVӐjUg-?gdt̸ԇ9ǟ^r8‚7WBRUYF&btb)>zkګ/c2{{~K嫍cvvqi`3aBNv6]v+"00۶R)O mSXj4ݷrt/_=*ꋬsA~#Tx knX8w{x3XU7ͯu+?yGi_{ꪘ7ApQ!E =ַ!@yY)e֣{on-_%ű~ڥrZS3I&ӦϢ@r%6aoDg*)&g(υV` Ɉmߋg;Hi}c)_|1]캷NDdl||}-ݿ^B7 65o!cwlڷI|1vHYi f"FH~4*~ }W?>bPtqVx2a^,/hnipifp!gO7 x8<3X\?0Sh\HaA/]!?/J4QQ^h4R*imAĉ:Zj5=}j[pYirLiI S3`jz&ttuDȈ(onmwHgbb{==|wIJJbrr(D$1F4F#Ͻ??O/Oɧq-~?J+{T%I6WE[ilP_LP`6l|nF۲.aiDqd}]U* =i[YXXd}=KHXgjln>ƭVNvs3R?p0 9%/UHIN:1FMINg%ew^ߐ\NMQ*'$S\fS\|kM͘0qG0qY ɈZc%Gd%$/c2"6ׇNK/LF-f(.>&l6NW~uǸ,[,>B0~UlKHiQ[XXXĠ1(*ffg 7'шV!+3JA/Ez֬|posduFF:H %.Mju[,m\w~ &ncz<-,Ib|+Ǐ[zIN~cnIVnAK ),(W-u=~j5Z*+$fj~cpN{܎Iy/1y\I˾88eV7H@ɭ3hZ- =6w"@Q1h .l}}ύlJFA /6Ov'?ihphvt[dt:EwPZ#!!!!!H@ wjhxE(b6n6"q$fAI]"=p8tvezff-g7?q+3I~6]`H NJ) L?$ 1R@nHq5=#^ܡmAXJ6 NL9{Fz\B.^h޾~>?s Ca*iim` Hk DQ$55Irbxt|>#k,,.RZ\̃>Pq) &'tZFFG\ڈlF084ӧbu1L4:yyO<xx=QQ^`` 'EPTiDzgF }Blx> @n >Ѩk 2rR6Mum486N!I$H~09$,jOMM)[yG{g'u.7o1nr \`e[>?% nw𠷏q+ 'NN70S'b%]T*,+4pǭT*B]=nz x@ͭmRE'czv L& pڻ|pi2A+20JrskMHR/˜Ád+47RW{cUfgSZ\LcSL&Ĕdrc40Lh4.72lX9Ǫh褢,^,y+h4$''np9^^rrfRQ-d’n!99ҒbT*59هvL&K@Օj--m'파P]Ue6NjXd21;;'Eمd~nK̓A%n(c@j*o2,"2bxK,[Djd$$MQa<Y3z +eBϞy do5d;l=+ǎ-}mTګ)+-X[H}cZ9 r()))LOOF4ef# cOFF:|zw~ hldZO 3gjb͗Ow F̺|myM }8>qť*R(x݃dêoJEbB"I8].JKqܺ$'395덍$ASVZٜNӹa>^''3U ˔}m Pi~ys(rJ䵊s5$9~D.wu'!!!!!!Yl1BFj5II$$$ 1~B0FJ& b#A$?#?VkpO ""%o2DQ{J\P*\ߦN`(ƓNa˨2}o[,,,vCe56`Lh-Qy̝N #c+_o4SRM$>vG,= Bklɉl(y󻕝ije rXW=_^V?*z'9A ~_GlG4ҐFHDj j?TV\o5^&+œ{oOf0r2A;% SJҍi$%YOp}`Fu5qͿEss+X'&pw;x󌌎100HffyyVTI o}6rshQοZD"ZFcR{m7nRGss+_{ QFF8Y_GskǪq6;`\#E\|ښcX'&(,rqZe%L̐tK8!3#^c{def011ɽ޻INJbrrH4rpWןՐ؜$L }]D؎>wu< А61 r閍i?ZkCB1׫6@zJlW;epfxr%U\&4~7tVtttp!6;imN0q-]A0bC,iiqIK .Ͽ! T0E26'U**|o, rI;8YɈNE:nij%MBBT17@wj5&` ȭwVE1鐕vt\xF+˰47ubS '|?]w9qGvSػ:%g@*h2D_m˧—r.}B,u|wO|eӼ nO}᜘pq$壮OՖ _<d`,f320։ .^BuU%jlDQ#c II&LJQTdb1T*IIE Ǫ*HX+tbNpYwgd?JK$O!3>HFƦfgg9t(t ~lI`XuodLUv'&HJ2RXu%9هH2hdefT*>+/'/7wJNh<̨ē#p6Kk cr6yy~D}wsE>7ݰ!Ͽwg_;h(+#wy_]el&H[d;a۹u!o?%?o>?O!ޯ pGn,@u?.w;vK!DQdxdMID֍^m"\qs6=6Ks ; } v^\JEл~DEn7(kziTE12:HL\ppp$IL,l56]9_yg؆p"{c2"RFd8bq,.6=^⋰l #>@   07?R ,.(jAE&xPTD1J(Vc BC! N 1e|܊dtP T*U9 'fRy{3779-RnѨjgҕa}>3;RUQ嫍32:w$&$0pŭ;[,@p˨jdds5ffftwt x8<81}gp=B!ZnPXX;wbz~Sdff033ojj W5ZMYI -m$&&pdA$wun[rrU:DsK+: e GOdt߿O0~ǟxQ*$$z g」#{Ou@Fa`09*Pu~ҳd[϶Wj{~Xe["Er$Z"'c$A rjhL&0fsVݺ}o[}?q&7;IZC#tkTVs,CC\rAxkDGEo? K*?Jԟ㥣'OMkk;}fgiLgW7])}ʝdGe *gb퉐(Π D%&&&#NgPQ$l6;:qV+^զ``ps2( -ht"`۱l$\agUD~g3/206t#?OF2TĄ3DZC۷ݍsm[_$z|n>t!~2rۇ7cVF#=088ąq&I*JW##SZZ(A1JZ}#[kPXR~?:]<>?OfjjZ9^ye%*#mŠ\03b6x_QY^3ds!ضʻ1rQ^VJttUw'+UHfbx^M}*܄ I$th0H쬛ez}8F@ lbzz&$>Bl&,LTPFggh zݜ!˗>LAom6{N E/cKG'ؘ"ΝLJ@$&h8&XJ;{>-;FecU%dgfx}UʿyS5ug/_fjL&#}JP[ZRstNRQVʕk$''dv (窪* *+ƆM 2"$'%zCvrRҜHlHݚF>/UUBbؠn3g3>18s6RWɬVbccчl;^'5e^j*IY@@ #IaaAI h4$IB$4 Vx$ YjBkZ%EyxAe~QƧ#yw~lߊAW2زe`* nbc,sZ6V-k>6._eǶZ֎4To_?/Gx>MSZRޠFJJ2V MM\o -5؉24'%;kuYmvJ1LdǶ؉2X{}QGUJ(?"2gjr 0;o%p!Q]yc۷r?gm|>C}*ETF11є22:ŋsklۺ/_]J PY0. ۭȍtuwt+]}n,]ݸf\߷R8zΞ ';+%%E3:)J"׌Ғlۺٿo/ M 395Eafgݸfg1 "# bcb|\zKW)ciض峍Aj!'; ;{nM߼{@ wIT6'T'Aލt? 6O2xyY,399E\lP5Be6DB|pŒHܛ.,UZAQaAF$.6plHuU%iiAsp8ƈefEtLmPUYAfJtojn!..шGʍٵ>uXIwquᤧ LNNάk\-5LL8ټsE6VEcS3Y8w]INJD qqh5ZZ[1"/qqA*%9)Dnee3gϓ|vrrKz8&NjkhhNuU%pA "-5aRihlvK gβo% IᆂGkTXYHI޻>MFw?7|eןymI_cuoZ20** Y''ѯ_7λX\'))}d^~?cIAd`B&%&RWټiGoh`||}$&)DQ=m`pp]8}dUy4h4b4|%/1뎝 hPe.`Ɵ#G5o<0ۼ׳2=?XrFAjj26(FFFR.rͰyc5-$==D ¸tjCeeF 2 :|t+xeI\kUX*Jijna||w*^{W|ӟ9\,132ClZ 5X]$8Uye@@ v}yߎQ? TrԙsfyΩ@:&[V*/** $3=clؐGY&8=:Dq]%nwNEEEE%Ty$Ȳ̬Kdυ{,Y&ݸ^n)-'q`Ʋ^s*-$ZHJL\< m7(.*dl|8Uo*J 0i260o~c}LY=ݢ:3VR맢AsK+IItbzz7K~jWhk xӟT7wSY7422jED2339{<{vBѐh0<2Bvv(3}g4-dg᜜ z CE*۽s'a2(+16R33OLPVZk t:&,NnNDEi.>OVۺ 'Ȳ uM,#IqL<{gi;,n_[F&&uDHIJ|~(fnHNJ"#-s.RKCcכ[HLPTXȱ8r\m]tsm7&fѧs)*JYmT~"nw~Aع}\LsK+;o##-bc9w"oU\H ^:A_n,#J 0e26uka 1/b@쩶ު3F |OЅZ{wi0 "AIVւw={v_|_bed{{SYlۺkׁvhA!yڳg?(;&i$ -n=Gן%!"1" bH֝;ۖ=aZ-)IDDD}kR$!h4|SDz ^Al@KmIxh#PeYls1Vޙ:1 ʱNC-c4iuߣ[9ZG\dD}SYddv8vх-iY(9VY~n?^[p=^vR[8Z}zA} I~mΎEQ2>;DžsЍ#3T& lݹ٫aQkh"uLICd|>)[,=.ƇܸƼfuVedjvn1DEEEE%\LϏ[q^Sz'NN>/vs7;YTQQQY䄹G"-Y_5U1\(@ww'O>4ov"6dfdSQQQYCy~a^5w*Joo/۷nΝ;PTTT#{O7:(*ܰDDu0gf8v$V CfF}Ư"ђ@Ue7;ͥFF '+y7HOK%**ARexdӜ<=FUE91Ν>6oHk[;^Vi\oh$::b1M\o ::JOUE9e4*w}Lzz:& Pr9&2qpIr3r29=c _x???rܹS i'66˥ĭcϞ=XVk;w җ~3}Y^z%%O'~!#W^e<쳼hZ$IO~!p> ?< /2/*--mQ+HB‚ƠV?jz{x"|uچW@ zX\x=帰KB{gؕt766NttPaCǐo|eZOr}jj*$XJ9yf'J֝65-,^#"#8w -S)n.wS 7t +! +xkP? w.CFY0^6s>7W(f7˓J299N9~$6 wNNB[X,*I~^7n`4W^}4栄Fzy9XGC;qPUY19 Mimmj6m!CⴴGRb.\\,w:AÞ;r@ Դ0@aFf\ F4 SS$%&B*+GCu:;Ngߞݼ}ǘNyŗX,hDIjlQY_ pRccȲGCGtt4ohddJzZ*>97Omehh8o&:* Q)z 8un'|X{(MS9Rl۷/h꼲 Hɑ{;t.?\[" 20ow*~˟k_󟜜1œ3V(`\jybFM[d`N0rKEyHhpRC巗p{pfɊIFz*adkmk8%%9|>8őC8Sw '=usMGؒpBB<} {zASg -KUn,˸\x=w-|NEQTڳFPRT+*+bxx,.]JeE9Z)FFFekm TWU'FFGٲyFBj5\éټFt:]=s0>z=9ڿ<ɨcd ߺ#HHի)ܩ)pxx8V!D$,Ywĕ!/+7#GxwbDVV PVZ(FF FqDDDpMlRSR E 76 "GaE%EPDjJ  |v{lIONNAGg۷m[johHl6;@ x>F@ kܱ ۍ`P*+}{e4 % hkm͒v AyY)m9_ot;1>1ALL v"[uCKrR._@nn \|[k+W:i /7g'|̘'%%\)$IY *ǃ 2W|>:pPPoaINN"99I oԷܴqaMjjJm?j]&'-1b礤*+K^Ϟ];Ȉ޶<<ܙw|k.M%<[kkؼq߽sǒss2S_^M ~H+_ aaal۶gbXk!0G233h4}˿ Iwr)?f?~<?&''9wn /(&vv;_װ<BJKKCvL|G oݺUB7b@.NiRA Q Eu.kgc(z-$r t%FFFIIIi+|;:]wI9je]ߨF`ϮKCP;YP- ]K낷$A5C^G|_ 7o#2~NtzIe^y^}UY|!~|?O5/w8299N9fǻHhN&ђ@uU"~H20iDGE70HƜ jE20Օc=w>6oDKk;>FFajZY&!!!$NK۝;%>>=vpYA`]\V(!Kn CЏddphY,&S We`MC>흓WigW7xe- hZDIfzPYQn2>>EEjlښ@Pnwoz3LMO#"11XmvDIbfz 'e%DGGVT/cv8rH7n)"vq!r IۍYGf3$%&RZR4' =TLrR b˖͌RYQNBBF fF "#YNo~{=w"c4SZñ$ A1JK}>rssؾqv Ξ-5!TE=$%QRTHll,56#u$::7; "Vj6oh4Aכ[[3Y?++)ŗ^TIP꿫JBUUɑCB#Tu `0Hx;*k fwd";+3藹k@`l6QGB|<wtP[uA8(Sx0>>]a]}%ŔRLT.`"Ғbn4LMNif'۷o-5446Q>74(#7MMMe`7e`v؎h4*a=2Zvv˒v mTZV;]w̌ rg?U\rJKVo_?Y$SUYAŢ MA)[GpNڊ_? 1sN$9ٙMNN9zxgxyWiiYXQ+HB‚Ơ&66NI(}x^ 7,zQQ0x5c*.]X;_XX%!3J.Wn#O===|Xl#T*+jSgPY\tYN>H󪲔'"'O3;ڶ}Nqs203338y Ն%3s^fo nvvJ^>:iDE?@FF:9Xmv:|pAAݜ Lͦ4Z͛im !qZڱ, LB|<{vTd`իHb(.*d2244̬ wUk S$%&B*+GcSIu 201vvv^b "IVu1,+W멪f#}:|c F6VWR(i>21o-oߊjzK x޽>( 髊 WHc>rinw, ?|fg}[-7:nb;B*+,Ks2"Ac l i$-9XjǪ/::A22ҹ~ 4 -5ڨ$55IZټHII}Jq+hs*eUF獿aCH,W}G<,2ȚS(/cdt4TPŒ@dD7:RY~bEFaAf/344BWw bR¦ Ÿݞ品h4pf';o574RQ>'#|^ɹSd`@ ޹YΨUVHB;a\N!(/~R᫕m9_ntܤ:vng|b)/+>jiRb"\H^n.3OLRY^ , ̬ۍsz4A@h{=xdIDdô$'%i}Te`TQ U>_EYO*ʣx}>*+0kCF%dd$IDhȒ 4u:Hh0"TTVUFePԟjEE%*ʬ܉GfJ$HsӾȈ?H 򜑨D`;XV;+Z>l6Sh6WG dfv ;Dc?@ z "QRGUThniVVUDe04sApwP$Ewc'D~QN:ͨz۴|/_>z8`cSyp`^GfJ<22  BI$Qv+FaPTD9 'Nj[$SX7~*Ŏm[9]wQr</ MνXrr9{;obΧJt#α8o/N'\cxdQ?ϵ58s6v.QQ^NcuϩuLMO#"1o۷q,, ̸\:sK餺_|,oJCcQQfzIKMzs V5\|&-A+WbIH`=?y ̄IYI1S!My_ R*@}c#Kk>a: grrb,"*q-<+ x0DF cjEevmJy3\|-"j#2Sʳ;DGGF8|#lk200Hll,&qh<_0-fHBTϋE,# :X,4QQf Ycjf>*IMMQd`6%jcM̺fIIN9e%% J#33 0g (VM1 reIw*_2J:!+IJ[†|%9)FfF:F4$(-)a|m5"I2VbcbfphH٤8992tԓWWV ,RVpȲȒR99,m7>gW]YAnvGQHP Wo^`kdI wo#R Ȳ(DQBeY33H%~~QDD$YF xvvvN,LJj**+LII 瘙q?0HXXՕ23Bd v ɺCv& Htaa员h!b!lb||/CAA> hmoظXf^/ 1CVf&Wzҕ+$&&RGFzFJn )Btt4&\ DxL2҂̸fRztT>Ғr J'Y,tPy#._e*JK|defdnw理D\-$$p8x<^\YvlJA~.NB))$&ZB㴔d&&TWWbCQa!t(&f YCU@XX1䐛MgW7YYYfg+5$Z,$%Z#++k󻭶fQQ$&ZI#6&T .,SpvP{rˤ]XIzv6@.e)8L=0DQN%1We`T20ōx פ J!IҚt-m~mBIǑ^*, LP^9^W 1giwJ :!8;H2H:xq?_ {gD2pΜͨUwSem3㗩݃}"3~:!_t VA|>?b@d@XDt-Q #u cFnkVQQQQY Ernȴ_Gt(JZ$ddd&Ɲ Ed!, Mx8F4Ñ"" AQU* ))LNN*******35܊'1)q%D)8r'f`d(ܺ@ -4@pX1DdNfl|KBcBl2yFdYfrjJ!1t_e&&k]tͦ5NMM?g$#ׇl @-kg<&:ZuD$"$ dinXx jj4HH3*Fȣ4`׶-aZgLNNaIS]qF Q`px}^z 3.2ReY>Lb̭!ڠ.HH 8'&z| p9M#zCYp"ʺgfƥs.>~e鶲***kTaeiLGpp#%DYBAqDIv1t2195crۄ ,)7.^7 ZNL"!&LdڲUT} ヌcllUb;g\.<rx! ԝ=^חNbZZmw=vYɩ/yw~F, ô/  caz 7I8v0`911A@ FV LCjD4""HMN$;”**|׹Ejj6~,LMOhʫcIHR]YARRVQ"z o ?iGHII,446QaC$:&‚IKKepp|q8X]~C2KeّDVFhdzz:>X%-5c I ***3uhmkcb&.6LbI$565h{ }V$Ï-嚥Օev܎:]w}{v?0ejjh /. ki~(:3RIKIBe</χwAH 4E\l,z}PlI[7r7oMռ{=dYo0lv]`0;x3g16kv>f3qL㞮d6122JttI!5 ,s IHNNb74rAҒb HO»$;-##!@cDrs%/7>l6?Eٷw76[ZpMtt4Iot~,k)x hhL $9t`XYI כ[Acmx 9)CsK I^0Pgfozڸ8?v^/`|mJH$M*ЇDyרTT%ZOU5mJi ,fmۘd~/M>93:x:7'cq9x<׃ߏX4T9A6Af9XY4Mi(c ٬I[cpۧ&rnL[.-Q0EHJ@?Rk iq"\. |ZQݻJwLLL|55,,v ͷad$QS"7RiL?.>t_~53l:dr|i"#TXs+X\ZիWק u1@UU,,,瓱4lacc}+) @>C4L\T*&+>Y$Iz<0OA2A@b2CC(S ]rAU:Ǟo[:A,sx43D0=3OnL`c1c1- J@Ͽh&~}[G sczkran~` Õbt}EQrCf- ק`um!gX᜷e}p4U9T;R]u{Mo;nq2x<=s減(B0{#㪌 '9Pٳв3j$!N?.CHUݛIDATߧ(Ft]G"MA' r8AF*@} " Cl'' w Jm6JqPp庴pB;pzDMXi .O`S  ڒGA?O K9IENDB`gammaray-2.11.3/docs/manual/images/gammaray-http-cookies.png000066400000000000000000000313261413310443000237570ustar00rootroot00000000000000PNG  IHDRR h pHYs+ IDATxw|SUMw)2QdɖQh@ٛ2AED{=e (([Vmiq1ƶI:|/Hwι'ܛ&(P򩋺ABtаĴ0_L٢gs-lʿO666+4.#8zgvvv7nr[QNPeS߯WgBw-]UK[،3266v-]biaY{Zw-XWãNUv5B[_>-JJh4jRzN7hp!OOr$uG?|X\|F_o PZ5~8skNpv횷U~XL||35;Yڳk/"ABDDFY-*)nڼէWO:/LEKx7 .6g;⋦3`o޺ҥ?_X1AH:Ge)-Xܱ}V-+ **j܅~;oeeثwf v޻uy~84w?z@03g~ԧ߯[g@6Bwlݶ#:&ں]_cf&L>.~֌8`ͿU6o{oܱ!?>bᢥ˗;PitݺtUT.keeԻ?5ެC&HNN>m[X[[{{5ܯ7i1ֵwwrr:~dDDVX3Obյ|r/.5ޫ*dFV׬Y=C!AKHH+-[YY9{7RPB8;;k7@[hnmmmS'))ܯTw3崌QBwt ϜٽFqrrԱݾ̴+ۢ%g|=?MJ:xh~}lmmm۷m}BuǮ=a..ϗ/_ʾAC+f~;hj&Ҡ~=J6xJz7}4%ePpXxx3n BwBC̴+5GwOд--M*^pM[{*rl2= ~7n$ Zaoo<)}oZJTIDDoHxx''YRM8Nu4.ؘYٹcmZnn&O=Ur卛v.b2TⓩAjBȨ(w77!D|SRwvj3SG++g`.(9VlcOv?sqQT-"44Lbά/]1y_}9Ѹ*j]r¿>x`}{-^׮Z/ʣ@yPjj8f{xjUz흻$?pV 'NP*f꛷nB$&&޻w?fB;wuc]jeeUGrRre`mn֪YøT1VA}⫻t#ǎ+PZٻ3IIIBظ84LMw޾cgRRRZZÇ2Qso; PV"#oӤT}ؐA!!!7n1ޅwF.B'ܸyK}?t:F)Wba1feeQQQnW3%!zv}amm]zfMXY=|&'TzmJ{Νv3qxkFop ||3su~5uSu۾ yPV{z׷Wˠv?|z*3-T^WY7tGGV-:XZU++olV;j txTm9e@u_\^=g:b9*@@5ͺ fϙoaalƬ9:4n԰Kǎ8w~>l+zallmRJne߾cV/_n/_٤̧X[kzcu68~fv__UDd[ bڳrl)JbZÇtDQWQ4߸۷ȍ[yɕgʳ+x*ycyYyR ȿЏ?2n9z08ץ6{{{^omm]Jݻ+W6w8id+!ӧW;мO!-} hܷe7jO>.ªaQZjjG}6b/|A\?1z}6Eq޽T}QeR1?.3w#?nggw-{.J<ބ٣y ;߷oY;vq3B7nvZؾs3g\xi6:vϿ(k%$$̚=}n:v=wARRR^j3յoU_z=y6:wcيU,8n*Ӕҽ_ʕNrvvmv5t ?ppJJ@;viz՞}j;`А+WU5>T~<}kjcL3g[%w@7n^qk`vޫo3+11QҳoNwޫoDVڵ놃b晛ͧ^Ƴ4kնL嫂zv[o֮ ݱsl߱'OjתRT*˿ :1_}n-[2mDƙj٥G۵M9X6"ϣoۺU ?{{{J/{z2A3ATTtsoN-O_T]]\cۦ~/\4޻ݖ -Mjwl;eoΏZ|Iʥ?^*(/~:_TO1Kceaͪ 枿pq,8nܻǥߵ}[o9c܂NAl޺JJKbw[vEVKھ7^f֜*եjw=x&T srPkլSs楥Mff*"$AÇ+,\|^f]CBx% lL3Êf9z ]V`\[ǎ0?.!a%mطWO3O7>UrZmhXؼ'Ok% 6lFc{EEE_zͫaeڠv'6m/zqSQTC \bU||qV}7T*U*?5Uu:Z*!!9֯w/;[[[[۶mN|*/'OH8s:k4''mmrʪ}6%;whoiiԩC3 ]JJ98+337GO= ˽`8>bD !\]\\c ? ѵׅSVUEERM:^Z̚ݱK듒#}{*R[\q::!"""?yaXEdkk&xP#ǎ8 )0VVVڵЮ\p+WLoȆ fd8K( 39J,7{Ӗ|ZŊ \6瑃".]tk}z;**?FGGO:}/թ}ڵ 5,i|K]RXlj|tjU!\\\T*ǡt sww)L7zi3tY4xީg~Rfii!tBǏ> yr\6ljeyZ?Q~;>z9DY3VsgsCO?;m 7o6d^$''+[ܼ5jaȺ !BC yY"|*٩V6p}{\bqW-_lQ瑛[2eSv6h~GjSrx*=X8:8l*Ĥ^?x8np1B7o彶t<|8kգkZ5k Zq&en\Yq\B'^ް~}Q+h^ɦ-Oo5l0x/xrCSRR"6n~figAƳA{/q3hm58v{ĉ];gs/ ϗ_dwcz!bc=V靷縝m5\v]Sgte4M2eghQ׮߸A]ODGGڠ <0$A7(=!w?<)Ѿm끃}\bblZKR?N::WKy{+MVRQlЕ̞;ý{.\WAOΒ?Wi;mڽ7ƎB Q~jWZjWz[f^43P|ƳA{_|qɚ 7,K ,-֮0etZ֛o̘>-G׼ԯ|S̞0Jxϟg~6lNNNSO:KjStVܾ}GV/Wvυ6a5V 6ϙyOZ ߑ#eccc<94!*"2ŋUT6]lLoqs)rr(8S:Ks\vsgN/нR %ڢuw{ۛZ\H#,,|Ӗ۶.BɟP(z~HV-ԮYԵCPP*2w>.*q HX$A Ik$P]G~qsu)A-9J^[ oȾ5V%ڢ"[ [Ŝ|`6@A`.r HX$A IPPRCNG_JrɗUs8i, )99yUкed)]m *SJV J!9'ͪ`ooo~]ףXM6֬{ ]:r~+>۫ǪdU[p JkQ4BnƩbbb GUQZhXxюUɪXjM>zzDQ9k'yk[-YRi+>۲WS uoS4sc7np'^M}[YzmZZs^;v1^l”iNϗbj^ˎ2C~#y7oȰ(PEx{5Qa}e\1bnX񓲿#^?dUz(xO9ewBf>KJJ:v|s&鱃ju;r:r?4ٻs۾]K|IDAT;aҞ' u-YSl]ȃMܜ=}Ʒ^6p@:k.*aaC}Ȼy];nx 5zDV~/Zjv N۲M3_}3+11111ʷe/1'k[wi˶}[9yoj3?(>z˔y_4֚?Юqcjjc',վ]]{ !5uByW }dwB<|Qmj{^=%ͼ\RHe'OiVzGϼ|ZϿ66t/v?\Hzz@NlkӱkfLKHHByvú=zq63;GCsޫ;ҩO/[[++M߸)LPab6mܱ]LWt?ң .]s gh1ͬ rkZjZ?w\._Lѽ!Cl\rˆ57nTn hs_wܽvF3&fvLu9SLy fL߾sM&^ 8o{ af:9zIntW4!/QQc&L8n?W3]xfVM5jzħ_$...kbwywh:wjǟqo&wFࡆ.(Pܷ֬d*b}oݡG,[塸i+<熻VM,[l֋hiF}zv+±*YƒM~P/& Z>kakmҹCQjoTݤZu>_2<<~4(3~ptE;V%bYNkׯ4%**Zn .ӳ;wuh׺~E] ޜ:NNF̺l /^jKmQ+VpKlHӢR-VśP Ԙ,?E^3$PƪdU[߄zV,,,(YcU->|FM~?Z;x6e+@M( b HX$A Ik$5@ b HX$A Ik$5@ b HX$A Ik$5@ b HX$A Ik$5@ b HX$A Ik$5@ b HX$A Ik$5@ b HX$A Ik$5@ b HX$A Ik$5@ b HX$A Ik$5@ b HX$A Ik$5@ b HX$A Ik$5@ b HX$A Ik$5@ b HX$A Ik$5@ b HX$A Ik$5@ b HX$A Ik$5@ b HX$A Ik$5@ b HX$A Ik$5@ b HX$A Ik$5@ b HX$A Ik$5@ b HX$A Ik$5@ b HX$A Ik$5@ b HX$A Ik$5@ b HX$A Ik$5@ b HX$A Ik$5@ b HX$A Ik$5@ b HX$A Ik$5@ b HX$A Ik$5@ b HX$A Ik$5@ b HX$A Ik$5@Yk ՁP wl,Tfr[\Uc6vf:h5bMHX$A Ik$<<&JZIENDB`gammaray-2.11.3/docs/manual/images/gammaray-launcher-attach.png000066400000000000000000002050441413310443000244110ustar00rootroot00000000000000PNG  IHDRhAZF5+ pHYs+ IDATxwt\W{rB%H"9E*֨=>>^zޱ{>iLխ#ŜE1D@P9W=Q` )`)W} „#!####!>ndddddnedddPd-###"+h'A-DQT*ɤZ~ܢ“x-(`ܜDY`Kĵ6bxԧ #H$t[[xRY"j'I9~e~zHRq #6h'Ǫ?y;Moor]->EyCvV&.]ٟƮ={ Cj,|HU+W0<2|P(+/l~ Ec>'O)).F$rsra@(&p|R dga\|mل HDWw/\DTf*>S2v"(6JKl޴#Ǿ!Cmu5 **ʹr*yyTO[.c͛6 皚"VD"_x< 9x0`| 8 4-e\zrb4553pچ#+3d2l޸P0$A2L,\0_V2?:{Xr]]\t NjZ5>N^^.֎J׏ޠGE J$I|>>#Nj8q4:?X ZbRyJDFF=&u*`‰H_ZA02t2s LEI/2(J˘?w.^AOOo/fl6DP(t_ntr 3tK)A`U͛3ϋFf9\miLFN(ysilή.CC>*x<hQ!q 366Fmu5nD"dd~$(/_|0 b2S5s* ш288DZZ(,nIcC%6:)-.b6cШ՘-f49r!E+UJ;:qiRP^V5$I9#QTVF`0 h4-fsDjo/ii x^ "'+tjɈ`l@bZZttva[F!* ''Zf܉DInF2lFNyyyz2vLFׯRb1ٍnÔA6‚J%vkv#PBJ%!mJNN6]]̝ӈdDR1::FEy9a2z(bnyPۥu8#-m$O㽫<с*O5hԔ?n1ddd?h'YAeB!.]R`FeJY&###8oGQQ!CW^# xp\ $0gd`4RTX3kLT*B@*LV QAլ̨``pVKNN6x}>$HA0o eLij *ꮹeddd~Nɫxֶv::)).l1irT*1[D"8ł hZL&m&_0 (UyCFFF:-5JJ&9o(ܰ)XVZ@nN"###䱻YAqDQ)###skZrܷ/q:(/c==N:Sx&&&|{$皚I& 9޳~ԙ$I"(ܹdgfW;x<5wߞ8$I8yd2ɵVؘ1N9ˮ=C\ւ$Ig~N!##ʵkq ܶ55$Vm0 m68\BZOEy?/w@0 zmm{r/űiRTIeeUf108tb)/+0<2E(u9t(CtuPS=}*N9C4˯wP_XZO?h,ƧNm;FMYi *BL t皚$O% QS]` OPXXb!RȞ ?|(є>|bOau$ $ϜCV308Ȍ fVS\TDqQyy&D[[j FP䐑qP^Z x(-.m; . `ȸ Z/ZQTjIqo.@H[k-\QQ"J"jȈZ @RFyxD"N~T*(~Z.\D"< (" A@RMV"V@*!##AKK/)+)y È9J~*tE鏷k|N22220?($ЈWiefkJd 9ξ5!ݏWyjy,WܲCDBE}s+h)$I$~<ܸ8 N])$RjtXR0[D v=^FFFfZh q%ozMjNq+hQDRne2222Cz~6 s:p-mN=>5=p[NN8ய?x 76]N;L6<V]|;|ی 3o#_AZ:vCw 콷pg2 ۃs؁$} s~Ѓ~Tu2N9vi7j?s)5hs7 9w^{q~Kh4XͲMK2hW*̃0mraq)% z c>d2ɢ+1ۿQ`ƭ|{FVۀ-3o`xpk72"0h4M~SF6L{h 020D#O~B!5VIʁT˗kx3GFG3Q(12}dX5V{&u tvWm @Aq JKP5m/@o7~RR$O`h:8H^OAI)u ް׃R18@m6x$5Kq=z;;Fdp(@4h:}-/̂i|Ar ef兗1LxC!"OoAK8+,"FcpaV+^|xk6m~s:vvrF~A,t ˯_T DIy%_y J͆S8ᡁp|Ǵs?x۶ef@_ RvPCwh)$j|ly,z:)SkÄ:0gb:[[|("cN8q WBJUM=B@`tAVn.iT*5 A%+WQ\Z΅3"paٹ F#D\%@0v@ai t=*O&]tw$zq `0 Ǎk̉FuCVETHꤴGRSHvnD1DQ>x<FEPRS! E*K,XNH5ZD1u˙2l9^=gX @?׮ "jQ P(FERH$ͥn|6x/Nyylx<18/"* ZBT %2_AL&ӔcMI**z vvh]OKQRQIYLfד_HN^>mW.lbVq*fUMQ?$1^̚Z W),-%3;=59GkٳSTZl̬$ftQ\ZNł1#ф=+ B͞@_/c>|>+(,)31{|C̨I+)ͱre^KaiϜR8>hV~fFT1KEU(*sef!"R25Xmv.o"'/’Rz:[՞I˥}>\hBՑaP*ge!*6W.rV5ťTjlTB|,EUа`!ͧOj),)Þ[0T̪R9L6;RBdyvu228Hi 4ZlAH3;/s'%3gRTjUjr(71سn fueol, p2um1 yϹ8~bOqFIEeZuwWPJ}ooGOgE%(nllJݟ}̆OiL&owHDcbP(w  x&&+,,H E巼vLOi.2)_??3uV L+ͮl)3@iE崔sό[|$d2Ep{vH\o0ޓrd))\d~zܳF+t6$"&KgP*~v߳22O=&բI-+A2DD8&¾{=GVFFgq>B8rlttni{' 9-mn` KJIs"T۱oͷ/w7fD1>S~Ƿ8zÝ4ط =SϺ{8}w)/3::6sM8oq̗;w>sCSk,޻h46N:]h4b2aʘ7 سo? Qp8F(߽n&&ܼcI&|sxL&9|D!0Μ2%x×}oorջʡTA:[|D"|{x<ΧxP0Hœl^r%n7[6oʕkbsO~/[d:'٭|XOnHJE0O>cgx2E 8ٳYPbnc#gfb2N:evm ̣ "$λ (T3aBć"'l1( }~d}RU5Ny\|geۖMQΔ*V&'6>K2v,V `6-\=٬Ih4\w~蓹FNl6+`gœav3oN#om?:z=~ҋ|i*E^nX'Nb2fVVW~{HUU;D"*W^~!XkY3),gޜFoۂEEEyY~) u M6-/_!L %E.\Dڞ;:2^ m'/77}LR18`u( S# ].~}mٌbnE6H$®={9|`0Hww|%$$"0_K&SvfD-\@SB:ٵXʔqafג'" 0y9zZ5YLWb۬>{Ab 9ipwP*DQ ˖.&7'Ǿ!;ȸGvv5gΜq14CyO*O*b!++!I3e;{u5?pH$"yOi+达~DQ/wrZ kVkddQ\\/^x__^gO?cӆg0LT]{xzG1t:΢jkV ?/'NRYQdd劧xz::x>  z,Y7Ob24j55?p OB ٬PS]Bdtl!-\E ٵgR]5 Rɵ6 +W,ϿB2vZ-O-[B"]O-KGUVV<) IDAT,.XIR1sF% ͽf75 TO>)܉3f4AFJnn5ߝf"kmM(ܩtɦ!I3eٯ_K zfQQQ~mxdi8id)Ǟ-qK-dɢK/bE=CcJF# u̟7+WQQ^V2VXXK/>{\oے{5NA~~ZQfMfMPmT͚KCP_ͷWKKJ(,b9 MFvGmuÎaJ͹sM8GXpVk-8y4.ʵktttR_7.nGBBfTV贩4&ٵ545_`tzddBij>fedz[9==,?B4oOd=SOK/|ٌ8TW͢ JK?~h4ZQw 5U\tJIq|ܵǾaEL&>B4eNc(nj'NBe̙جk9sgc:|9mV\kjfXV;himcl6zz{0 z>9#K1<<™sXd 6!O BEƆzJ9~*p8k|b123Zٷd2ɪsT͚IiI1Oddɢ ٬?p*lV+nF˗ ijj }3'u4N~d]cC}zuvښjKT_W;Ul}[Wؘfp=A`#Y @ee{` pISM9ĚU+P"w#*Ղ9ʢ󩮮b``R/ĥWXr9&3mvb0Mrz/zi2gJ45 Vx*mB184ĈӉg2%jvv6 ,$zzcZ-Vdق$L`0L&<pހ9Ф(gTUQk' QLfKNi~?CC&&&PTw9TU!I T*I&?\/^Y3g`YYj% dm$IT*F4z1FF 4|Dɘ˅^O5>J+WigU4a]\LLh4x>|>@BAOo/PIgW7cc.tZ-eUSNexxNjGV\AIq*T_R>,05? N^pwt0<2AǠ32JUbb(FK`dI2 F͚U+BR211pnrhkotb0y GחRT%//$ j'H} 1Vpe t>W+/pAJ6+zsر}NB۶lfNC=ZZ|װAX,F0Dױ-׏f緓LJ\'CjDc~dR˝H$RhJn7=̟;gn&~N>grrXqE\huZ‘ G/ɐWx0Evߟ$|7Mp>Mڬ7oO2ޜ)y]q45_Œ> xoIJ%Go{|ǽ˟y|UfFq,^H[[iYD[[>o}q3[6o{ ظa={7rL[ɢ su׶֬ #ae&a__?GZb5#OעR8},};eer'`gnl>S":ٕ$r|sjLt:CCFNr'}rD"6j+n'??cѱ12L&JKK&7 R*b1S46R*ɖLi+hûĚU+R~ }8X22gH!^}%$)>Yi׬6f͜@'P(VE'!F RmGfVP褋cxt$aFFZZ OH&8 r J$ REE 0<}P8痢p$*_.rij1mEIk[{:Ɋ1L{G'cc;9ĔG`0N3~xMd2Io_ N=v3R^\fǟc1Yhel޴M~ɪپm y,_˖mh4_0wN*}ٲ@*Q5X)bX&4R[Ssٴa=Exv /gTW͢Ma)--d~܄[jjn=|Ls#7~D2zz& VA$(t:$I`4EKk[:GD$OjL2Ovvqj hѱ1DADR2i\d3Q*H n =x<]ZVSTX@_@:goWwH"&&i7ѱQ  Q"&2@",]B-]AQW;][ng98GGDVs}A T*)).FQfotr}RXX@լl)?9Mk0Fz=$QXP@OO}e pϤLJI2LD"&&X-ff3]=bj2<222"L%7'Hm%ID<KI OI@QiSZRV&s5&$)//cNGb^ukVlb$'Ʃ(/TWݝ~/]B{G'N%W:]DA$._Mhii̪cZ}jks1:B!*,Y@޾>f͜IKk+CC\xIT)#N=ۏ  F#LH$(++M?t:LF#Ccc.2Lt{̚-f|~?PO801:6F  ?/Ngdd"~?FLL!0Jd"5`Уm@< 03$ b8%`6pj$ ZN5!$I2$Jy9LFzh4Je*2PTX?H+d2I0Bק@ǮEH$oOF٦f$}Ì\X{@himp8L4c9ޑ ct-D"sQ(..f׻S=vΧSzO##Ih$ft:J% @DL /0CH&HJɩ@PjQo^`У 8Ș|Q3m-I]DQn$?0V%#Ä5MWp8 Bd=!1`<(v#a4Q*!"nrS+> "\c.:-JƆ  $Jw Ģ126 }*Dr4Qn@h4U~`c2&SdJNg*Q`2/4@ dI$V^ojS/Oxr/s)vCw(L#EEh4 2!33QaFFB!FR+L{yFFn^R"3ӎfc9J^^.}HqQaZ)_ɦ!j5qrXA^VkaI/o8ΞFG0[̨ ӉfKS'vq:GO>o' ctEwO#LN9HƗ;*Pe?FR(<6mHjUƍy02dO΍5EjrDQZKNm[ۼi>ys`\TuO>-7';+g֯CEz9r۶nwV|׭!;+]ڭjg~wyl޼BAKK+ϳiL&#.^鶂 lJKcX,/=,j Q?L nl}8f+s K-䋯v1sYd1556& ^O8!33FyÏ0Nn t:-6ZTeI$Ȇg&#ĚU+(g9ʮ{(--nl GFnbYʵie'TD槁ZNY^YIgnj@fannSATR mm\xi]nv Db-60 P(̞}z=.8DNj1̅8{9wGVt"J%.^J6wN#/_0Rg?O?eK0sf%W^j訋O-ep82YM|%ߞH }(?)bZm,;t߽+_V۷oa\X*Hy&9r9ڭKOş7pk5v Ib\\Ƿ, qaHmꗿ~r [؎{%Y1/x1$ ,bqo?BC@$ !#`x ۶it6C( 8::lbQ,_0J|P%x_c6'vxtxDVSf[4u|D&X^ٳ[{`?^966 x.&aQi}kۄ>ĜD>:Oz.9b/Ǹw.-+?6~ڄe ᮪nz}4wN't!t8t{o | ` JZ''ڞk),F݂c8! CDq$Fy`2OyvwPwwvpxt Ӵp>&lV|>x0P(pxt e!r'(( tzCʍ;l60/t.f9O(y4SF8CEX`:cZR.c4awwNOOP1&) Q"bZ#~kg-˘d$Be4M G#蚎$I:ܹM|WY ѥg!xaKSRx~K!_vFcX6G#t6CǛ9!j*X%liۿ&i6F0 mmw8$U GX.W`v J2 9  {s^@],BuQT (&_O#nU$Djr,(2 ls2!(c4%Y&)S&.:gyYB>W]}'g=h$AΘBfg>t6t]N@,CEt]|j~a[8 U wױ.:Y;[b0 {wokVk9*@ݝ;1LQT uԪUs.']˲ Y1QHGao8i#tTT[{xxY/~( ??ohs, ğ?/ '?/ GqȈ@txWo}?{TG: CtW>ķ\z>_ŷ]__ۿo|*~񓟜]|ÿo|C|?=|s_H-b: l=;o|73H`Y̴*c94uj ޡ?H-c]<;ۭ4MBEzW]윁Oa%6R0n lm7!%ղQqe<O4M|{G /}T,<;wn_/!#2Zr_(t_4uw`#''V|߇i淿0k(Kx ->8(BEq~uПO~ãcɪdoዟ%M.!xp?[oA$gg=ܽs?}mn9x[{x\_o}~>9bV"cܿwwwwQ,qxtBor %M EQqJyJ|Ē>Xw_RG'&|?_"|kXai( zg0 z X IK|#sv seړ`&] | UqlZ`4C rEF\loQ׍jYZuЙ-O:JC_ꠓ$t:C>/X,4MxEQ(+*,*]X5MjB;huI/-ǛQQ`8e X MToLEI :7eFCŒIXV:ܿOk z}av]<G'̚BSd2ELXlZi $1,F^e'h4(JsUE%,P(`Xt硠t˲n;;}k Og3,WK"I:zZJ=O0v1j6`& ?|V1>Zx/9 )T*^R1M ٌ~f&&Z4rv 3=jQQ,0OШQ*1L:.vww8FNE^a۷1 rh6X* 鍢I`oG'x>fj*V˲ ;-'"(BN..%8$ $IV{ 3Ôs\t#a(BeAl,˦7KW*==$*"X˲)'vO@Uh[T2r?nDc":9|?uevgvw6J {OB0D|gfj/e|@u݀͏pR aQ./'*DQR,^;)ECuD9'hZq|ZH&Ry5L!@jǣm qk$*g e! C粜4'!Jܳ $62C??]gGZ\nr\'j^>u[<r e\?bs,P+](JϜyFF'Ġ#Ӷy`Y[['0KWA:,ˢ/ ٦LˢțI /uK0 aYm^sqč8:Sj:b4uOEA}C2N>3|XKe7$I0_,b֥O\#lt6a^|ym*Ⱥ)(t6b>~.0+_52'],T^86fOW׍/X})I`>>.rTǓ 0jp8qBR 1!5’$ CӳZ&g<BD?Խb0bg)OI1!zJ"{ώX2O?6!r$Qm&aR8iYUkPEDQl6:tVg=J4eH\ĺKAc:Azx*vC|LR wY/5h}s:gI {=C,\ !8=8f6q*. @eH螞h}Ӳ Z\.X*B]Or9 |8!t_%ra޽;`&hRF>z6rL^i|r= IJ͈/qQQEqh.6a&FQDZ A@ӈQzjU2k2DQD^kYN>qQbVҘݽ:fJ20B0111%\Q,iMl6.I:QUqtYGX){J^QB)`6ef @ؐr hH FHRS&' ue dfq"j*LgvAR C Y˲Up2J+2<1,r\JHaReBSj%|,`.aSU&-r챢O/7fl#hbr#AAH\u38? PKyҹRr   짎ՙ|Kwefً80 \3c' Clmy|mC pȘ{,{@x!SSlY])Ðm8<:Aӽ~v HBt@K82tjq N{)~\*849z|h6ppt`@^uMJҸ\ l޻`<(n,3=%Ƣ~mM/85c2sy08?yX"ۡ$ufr*a| !՞Χj^T|9OJ%/׃( Gr`p~oeR7*Hځe qh[8<:YG΀mE$1Q6-R|S' ?uw,YbT-2J łb^1 ;Sh@j/z=l#lmC?@xў!LGc]bW&~?> ü1$'*q.uw88 T< CxiJ^Dq$f(nL~f <"Ɍ0_6LˢS\NHBŽOH8.8AoLEц[i+Mb:h.WWAߏFSAQPLzMk{CT| +OUᕽt_n"(Jjy?>YsxxO>mۗ0{)Z! IDAT7cP9BW1(_ TtqicE%ћ/@zonHr]2v?Q`8J 咬r1c,*Lx|meY GtX4jA|Nktב$ V+^XVOdS]t:xدczwi5_p4p&eWvkj4f2MQ۵7 {_+h捘 aq UU1Nd2&I*p1Lp{{h cs<8iYytnec4:yVyrI4E|γ^y5yzj vweL3 X!l4C>OVvPUqk[[mL3DQD>w4 u$r9G]T,²,G]T@4r9FY+MjDDhXE^B].Q(((p](x&$z$I`|?@Q wn߾I2 !AHVSf^fϥ&\CQ'% DՄ}J>EQ$T 1SǐY0 _3^ ,j0 l4`Zɫ^]e,(B|,cZs=42|,T*B7 K%EQ0_,!ژDrPm+'1$\2;biV `6X4 DQ|۶QT SV ƽVN!Es@ 45p<|0D5},%jUblY@Duȹ4MR'>/oAKQVQmOܻ{^r+/7b֒BA`:AuhV:!ҜWFRnAD$nOOP*pvv$I0O(E5pSt==E CyT*D6\aL'S$INR^L;}9'Sdu螞AQtO5} t@P<;/tnP3\E+Ր`bvMQP $DQJL]VFtM&Li/ÔQ(P,i]|xr9l2 j:ܵTPRFP˱}"Og&)4L qFDVETBHR!l0aZ& e`PE1XEP@XLdJ*20/s+5Ѫc<З9lvZ& <σ뺨j;^'?gp <ǃX4u:Il2mz37bVbnj < Ҩ8VʨUtT>\+!fXwV8f ~#}T(JXd1 AQGRXVD80L2UD0P!%Z UU0 1 1_,`|CZEx>vШJYVcY2Bg֦3,C|#E1 R^g:F\BTeZXu_fIZ.7Ac64EA\qx|750 ~GrNv!&S2|  leC\j2_PT(prBNv$G7ꔽm;|wa6G<(B[-<:8$k7t sm"tj;-/q|ҹ2]Gbӳ"N!G8;:?N+M=7cF{7\مk ٓj ^$`m2+xqcie<|t@Ss,ѣ:bh6젔dMFqDψy<''rRaQW $/tJ[s-t fD#Nhx\@DJΤTf;x0d8m$bWQ|{gb>6g=SuL e=eY`Y'|3<>8?i{OUX2Z|QX`R:o7.E߹lsֳ3obA+tL%.3p5],-ptoc&Sy G id]tJ=em(ݿw:4az-uox[-c͖$ܾllRb=D<)fWAW.DT,t̫DzoU--t|j;h.-Zf9 ]/ ~P5gy}nI<\", qy?_T̂0X,}UE0!_r}?侼y,͛FUA+:tBM]V^,Ky8zƢ@4j8@DW=aRvXiu֠N똄x~58Lǹ1eLsIgr zpzL<y3 ?K:0LraFD\h+ b!%tOQVdBAǑz !r(( L0J"~ܾzt &(DR$J` AHl}lF:Riq%a&ʥXaR Buaje\h,+d2=σ2|CӉX\JD4umAR)Rݝ+˭R.#"<&DÐf)JJcfeRcuIl,P: srrDJEH]7AZ`is9jq#&ẝP>. e2 1] ] \x]J8t:l6RH˥L"  <==˱&x2VJ8%Txm2  ey>x(6>d94.&)F#yt@:OORqϓL x@2 N A 0q"'֤ Wi1dp8BtO>< ,~Ou)e#f& E0螞q\2"{\ y}̎O u뉣eXI:ֶHF!l۹ԉt☺gdpढCQS:sLXi:xI %.%ZjA8> l=0gzd4@g$c0LjgOV(O>qBJGۃ咎%tC߸/{zJ},3 ,%yr,NOʚ>zH0%=,x۷=$߻$I(v6_\˵ubҪ|Y.Q( Sh~K BH,Au8 I)X,^AȈja6_PObq!I lno<j2 @A].x2nshkH^QG^rjMӡ:ml] ~c:A].-´#jU%@@ـ$RڑX*;h6p\2 n[tj6q|a0 8z}PK#1kXenČ3[[ml=lCQ4uf V*f9VviZDc[\tdYF Ʈ:98ly>4MGEDTD [[mj~ER.l|=Lg3,TI@4RqnP.|FyYFGpS',u/_J-*(}zzU]B7 LSttr<qX.W(KoL~ ?tDh(}vwyf9yG'jЗ m1 )˘]$.ը "Az{[Yƾ$1lL2Ǐk1($|qwsN8>9rs0 *L[.zd9Ya2=s4MCjpkgfA0Q)qxt\mvzɱ I uVgf.Ư^$կ꺽Z-YΑmb I>N:]$IBg/$LM2SE̛0 śAT{#c C5(ײnyx>cBLl/K֞xxUljI_GvI3˼ܘ󽸯gc},KmL#^&Ոɞ=σIKkʳ$־ǯ(B`/;DZq~ǗbOkkY{sm.^#;g瞵̏u]:kcfu\ H{6F5a:,髖msm{c`5IP׷Y7c2 Cյֱ}hLsJ>OdͱD[u#Jy4ޫks__7y6(/3fdqU{rm/~vm"Xm  `[Us0p]w%x-6zҵ_y]eS$rx/YyU/.fk fT <)f T|FJ>}?˽L|j*ooo:>tq_P 𥲻g 8uຸ(\&}]7.z {o0:/QTϏ;nTš:e9]iBܲ,躾Ax&3CVԞi?c8_$tvnA@'2 c&OuLŒqn a>47ԍ.*4;_im6p}ۼ  dEh9}F9h01=tժfY!28F{ e;^gsm;E"N8XvLua<\J0"aWO?wxpXxwq]0y_ D@F\^{&Iջ(<;۴aUSl#뒇7+) j!I֠X(Up]RV{{ZJuN|Zm94]0nZO0,tj5E9-ˆeYݝk$1r0PTl6i:v) jb{{+2 jT*a\q>"vwwPPװ u ӄm;b`mô,V+4;ZTu + F|px(1Ng89}<:8ղ-:C8 c/(oQopPDuҎc ܲm,W+Je{֑tM)Qh$:}ͬsnzN1e2s[y{"e6o\R & V>߯88ƶfr6q>O)",R2l{FJquxRdBR׶ uEya^q˲r,VkcgS8rIc6Pz\}0 Z $BU{B~zt?+`7WF0m( >u'bs^30M H>8%2jBhIv I2xfcߛlT' <#6$a?#K; ́(9<}=ΓcKg\W74ss\Qݽ4J%\\^}03zmBE0 K\b4u6r:onPQV t]mww!c>7q,ƻSy IDATV+H]хaV*DQċ\:&)./Eʥ2d +G|?Tf k:ݮ͆YI "oK R‹K?vZu `1RGq`exRjY`F:I`h]ocr.ֶ7`ػ>P(pqq~`H=ʲ 0#~u]p{׳HB&FD<χ @JeQ"4MQ~y${eT--slx~v{8 28&1c0DYR֙GTPf<˂I$M%)cBDV비>X@0S2aiqDfW,KI:|$Y^E:8bys<(}&Iwe/\qqOܘ$e_|߇ |,AL(<(6ٗM{v.R d2q u @1($YFEwf.8rs6e90/;wVGY[f<`|/H1@eos,qx㤠 $>8* ߫ks ]Q*W@N8rk80 t>qޯy׸OZYjL@z0@P^7]]_#| " 8qsKH]#jgL=~ s^ߚ{k;=#{c$!˲?kvi?tm+[C|{{_|qʞSgI g(|:wzV:6du]JyOCbκ?0ŽKlLwontn[gv(`GIRV[ʜR޾;{q:T}hw775ݮk ~wgs5\ץ?=`ķhֳ6!(qW0HPVï/_X,@t%?coB@Ck]\^A}ߺX²m C qIu0 Q޽zN(iz  1Qi̷(nE,| u!,ڈem=eL ܫ쾲vKV,#ۼu=xlFl/.!"La lF?qt П/^>=9FAXMӟ^r/nIa]>.jOIT4Xl6 Ӳ>jP(`8\V( ~1jUZMa˶Pqt"|/8 ^~MS!֦ hSv!Hdߞ3l? ZC Q0 u:K$}nVaz7hr<"]f`Y{*V5:#TeEUj5aa8iN=ZCt;mfsqXEZX&tژgIQ}r0@h5Q*$ t%cZa\X(!yL8E|>{5mۤnlDQuQ*1it^Q,8nJi]cAlmVkO`0l6GVC\&讦eY qLm۝mPL?Mqd! CFcX0C}q2\׃9Yw:Eqc0=J϶GG^& nnH[)]BثV1_e T8J{p﹊lkTUp0$y$BH(I0LqzrjMf{|~m.!HxRl6OH si9]v6Emqi*TU`HryfkEGX,`rA23XV(߳),0j*X&, YC39K3dOptԢa&4 w$KBU5$IL|e|*8˒ ^'I͸e 繘/1=~m?KKM'Ֆ!"%XYmۈ]_W_^ ʴӬuՁ,ex5˲4UehG>S٦v<(|[~cskŒ:Us,]뛻nE8;~Q6˝>O;vsJncI`8a^߽$ޕtuuQ*c gOR~߼lÿt"wʺNgx]ץyD߼iZ1p_/..$-W܉Yko޾q8u//!$y0 9@hjE Dgg#ˣ<_,^8{dy+24ԐeY8/_1m`䢔sr`Fdǐ$=I(bvY^w?=(0M8o!X0(b2,$(FG44ahYY̮s6Hfde}EFskGQDS|\B߼EDZy~+ Ce_F`YZv54M} <=GwzrBYSb ,4zٳwGZ5LH$qoQ.._~Xhq\kȲj~N_k5acX Mpe;v7`w^)9Jd?P*!K2onRZwg6L%-wg^3Ɠ i I?MUSaAeK}h6ifG&nn1Mp60 )yѢR`8Ayp}sKM! < g<=9@xs9;@!ԟ8S#zvcwf;p_;W yx q~q\. `6$F_P8e&~xwvNahncIvmER% 5;(aK% >*2F1SV`YZ&޾;im(>WߐPV(XV֦ Qi9boߝAxxZmƏW*eA|$IptԢn>}~znu^A}tJVGG  жm:x z=ǎ[躎r͆`=2h&B׈D\SH> s9nFPQCUE6988Cyc,wH\FaȲLOf3b;Gס:{=Att^\=t *e1 ^!2t],I(K<Z!ԞHr jC׷UVj51OR#_(!I9Z-c<&!}dJ!2z.|^x|מ= o-yA^Ct(Zq|܃(h6,X(}D&?X.VVٸf ( ,q<9t;}w0tz]?T֞5Q,)% '=RUQdQDyB.GxP`comr$ AYq¦/u;my+a<М+ ~t=$\Mjkf=~>,\zm0|;A1L-X5c:bX^\*T,"ρeY  It!8bDz4p2u]:xxO1I㋻6onm:41<rFٜlFaLyӗ]'`9 u2h`ZCȒukA% ^NX$/T$|LzOIAI$tMp4fOE0U;˿3lFZA0@cݿ(` zLR뺘Lg(`\є>1f9dIB˰x GֳHh 9j7]q`6t]gg^JoߝՄdsH^\.]#z։H8r$Kȥ a" CT+eyǶ TUrXԑZX8)IW*8TUmo,8W yr&xdGQ@>#۞Qh&m˲!t|'%IiZc)2Xe",R#[s׷2ȆQbUQ1˲iX uRbT5͛⠐σXV+$ Ybb9,ST4Q(q, wڑf(cI6־1ǁ)q`rwzA g_ 4M'm fYGڑϔDï#@U?s|O𝾎rro0~j{6wzvN;N;;@o^e7  L$|Jfig CJJrdApj¶ITɻ3FT`''X&- F8stGTUFXį/_5rpQ 2ΑDl{H`jb\$A7I9AEjO&P5OP8N@N;i%a&Vm ?ӓ18b\^]A7tJlz 0Фy7/bIJ _T*Hd>Gt6} 巒ؓb$)AT%QB}N? L3@ZLPUǡi#AlrequU DVX2Y`9qc^mo0 =G^iFo! C6ݕea}raz !J#ϑ^@>CB{(c:!ITJʊ LB/NN<;# Mۛp%eYPUaZO<9INڑStϑ< wiO6zvGnivAo~mR`8dZq\~Y2`Z4GރS0lr0L7ɔH,˦!_{??u?,˺Gx>)fxwv}zlQdl6{GѳI}=^5gx =𿕮\V& I(/ѣS_)0[\!$uwo8G% IDATOI'o6ΓC8*qC`}M'Im.?ߧtu} u2:5Y/h?mROm}϶ aB4A$Auj," ]q߼E|D.g7I+BKF4|ʷs8=Ws2!XQ ˲!I.. f9Z&׿;<ϣW}Bǡ?yec6}./0LmV~{>UU$ IE!vNq;orQ$<1b @.gZ"50QѴQa<8y Gcj9xG\bD۫¶lXjm`"bQn,3 t:E\Om 0 ao6X,Ra@T_B%9L&SfsqIxpO\M5\=|MfPW$IYA\j1cpv,P/2(899Q{$avh68=9xB,42oB*~%ZM( qzr ސxJ/NOaHHGc CI!R.c۶qzr Kf<<$j3ӓcBK4}DМ "t]q Х$Iy%dIb~m+3f> yr܃(Xka0Ӂ?8l:L8^y uqyyhD=Ër9x,;#)Vʥ"}IBi#"؛ Kh31CTw/,/8mKhRzc0 NO)ͫkZO&0MBo8Tet:msҥlF^,ˢ? I)q{] y(5EGG(Kp]' ^{*lFƨV l Q CGE^CIZ'0M R vg.qlgo1!4eYArB!% [C5̗K8Ls9Գ.I\\\b6cq(. `A# bRoD<4$LLӄyjNHa<: .ryjZCǐ-)Ibw[oqrrLrap#3P.E1<BqU7,`Ap%2N{% V@<q(=e|z$I2n+&$i!0 z|MalE/A`(2-$V+u( MW0Dmolɏp\=2HR uZp8&a|!Q O8bo %M–ea Z5@liۛG¿s iz=3P({>Ɠ1j{{P,c4^wm{j2\*BJE ba@UUȊRgR)c20 |zUe E9ks\>I!"QHgĉ۴l8}Ȳl?1H:qp@ .eo#-o6].jBִ|j;-7G&Ǔ Z)c߼\RQ0 za2ys:m^0 <Wެa bTHLu ҃5-b0Pȃ8P0YytȒH_lW*:TUI==+eL&T+rNTfZ6J"08(&|ֺa6umɑrl6Q|.jB^Ζ|@|EQ k9à(@Dh ʥA G$񑣐$JNgi@ӵ6~b8Ce(2k Pdп5[C0 pi Ñ劮eZ4whZ0j9fZD^\^b@2cK ykh-KSglGp4jEEAjpo<( @ֱ|o0r}>VOU}M >t$aQ.Ǔx_[X}zmL{_8[2L8.I`]^مM&S,+}!q!uUq뚖ڟF4S- Mt?U>q,{oCSO9d:#?RF=8Γr: +so>Gߪ>G_LBqa_GB4ΟܥJ la!t]qpssKOg3&*2֦ E<#pEJt#"=cm z EU(qu} Q`okX.W0 >%חTR I,xpaY643/XJ!0 ޼(PuoGP@RF0sh5x D 47_`,C5k{eWVqaozoq84MpV"/^80LaYVJF ٗf} 9Kx%EAd\܂X% t(l>^J}A0ǒ<,AKG-z /jt:t#I"N{b,1TE(~|EfjFr89a6!n0 Z%n PBQ1>n C/-8ltJLп ASCTUyLyRqNTq d]}ضj>Eʃ0~v>T b҇W˿T,bX *{vQU(`8t]G}Q}nc:@QdXLqiј[L3pGGj4axI`6g|@Uf3$ Մa$M$ }/Wd)%Y88&N)Ň,KSS[T*Ls(BQ,cEQ0PJN,s;woʖ$ -K*Iu?1Q.h0$ p]ױ_VKDs ܫQ_AT+e7(JI:$e_#xWa޻4t]C^p8BT(XMwa@LX(}xQOIey|ߧ~QAQf3TUch ɺ-|YM #m[V6}0 R.ad[|*XV+}6I$ ޾;Cy =`o6 GV*4 z}σ4NR@JQaZA%2 <R "#a8r0PVP(,ˆao,VQZl!J"Ɠ 0#Ǹfa,R/;T 7}IS*0`288ا$IH|QDH8B0 =\e (a AY,(2XC-b AhE5]h@y@xea:$pA*d:٫V1Eq6U,W+8|>e-bD@b$ih6G1frA$@p0_X Ðg"ŘɀKQɊ`B.bA0Oi*dIA$(EX@ꡐσt !"$IlEqzMlJ T*b8\.snձm#ˡR)u]X\΀4NSUZB%hPd j{`RO9S0_,$AF]ױX,Ͳ{e\+MUӗ>A<σiZp\b>s<3Bry Yaolh ˶SR"d%/|B!Y3_,K[b<>">FC,Ky4<;~E:}+8=9m\ׅwϪ (%?돮gk^y[qx7o~~lt-4 Z ea:qm{Hwgfennofs, [5SZh58,rÐ͜t(qjbXbZA״nr2bAbG#bEN,`aN1/0L$ $I"!l{J  F1?O4K%$Ih %$qW!}t>A.CR-|'oUQY7 H__Yo5X.Xא$/ehs`u򥖂:Ab4Ae6pq\T*e 3 \]@ Y(Sr^ðԻyظWtJ1S?xCI;')0(-xup[3GF~}J(\(aT,b:n0@s}}EE$0 E!(B&tT YQHtv15M԰l; !2dE&)I[ (znGen,Ppp}| X!Q>ʥVkz] CFTUI>CVE3%IeAh:8;z ˲2dYxj!:ށQ$hpuhxrM^qpշ-oiCetq]`۲  `{q]{{U%,+t;m2i'l64>]`}[|>Rx ) "Ȳ( "NOusZޑ|I,2u8 E3KhFA˫ks8 Ië3HH>I* m2wM"IIyD ǁiZ$S++HqO_Ydmp'5 ,0@ϼmɲ A(&t:,t& BDQH=C(!2UZmJ,XE8p, u/UȦdc m'eY1[nm ݠ!v$zcيa RFfO!dYp(hIg, ǖ2,A(8OEԓy=rD<ϑ/Q)>c鋂M 7%,Mv(H2Rkm*%'?MAQ =Pay\8 {*{i<Pq\M%YdYal 49 bC70N!j{{(PUf ` QMU1_,.j{UdJt76,NMSG1t]C}BTX i4MERF`X$c4^,K|.F4zBQ-Kd*r:`qÐ+ a4k\Gq{^-W|rBZAP/TM%Cu$lfJim/ ξA@G% r:[er $a6C%(c!IM5 nKH40/ $Iw:<@DK[ވrhDjRKMVn9gY;wg̜ٝn%u)Q^(ZQwJ[D|>P! ihj # A׵FdZ'cPdn<'vsSid;0 }&_EۃE8Bx<;ㆬ(Ā# IDAT"p(` hHRt:] ͅ~_ώX׋VmM?+8JCRo4F{V`l؛͖-6M}mlyVۣqCEQhk+oOD"/p ĩ$Rimiyvo4ûz9NF1ʕ4sWt/^ 8JC^ƒEM?4l? Xn\vc._FEwTG^S'OF#SuE-^9\ 5{npyyS;73Yj_eRda7ΰ kn;,nnw NzT:藣nZ8(h+:q8򃢪ꑘWTkuhv]hulnmAUUTU,-oI+kT0 j +va\XZqϾ@ay5- G]) DG@$۽^Om&Nvrƺ\w1ul ðSvr塒A6v6 (+9Ta/^ ^w_JI8;;(^o H`+_qBU|.,-#S ]WIf)_@2')VnNh4IZE@$Jvcg|$q D$eX:.il\X)*87kX*h6f!K`>d+dV~|X,`~h$(P< 4\.6>( q6C(j?x~xڋF`VEQf27 K+pȤS(+p,ٌDQ(m`D6k`d2NM0;;^?um.X@4>ڝ6Lz$s\.Y4-Jp Q׉Vo`0XkhB vNuVG@ `hZŃj"Jam}L.J D(W$ ,CK[6^/z=HXZZ&Mt*El3 5r,lAp8"&1HiJ.l&kXZ^}rEQ@ݜC޲hg~(9cG9qdEXYY%4qZ2I}>_@ GXa7v: MU11?7k[w=Aݜ$,6#ytȆ!`...2kUUvsx=;5imm^^Vssc MXT^ QTQVRHLh~,ϡ^o`qa>eq?SQZ* iBQU,2ǹ Iz>|>.΅z`(d"rbFr dYryۯۂ(4MGmT,P*j*X%fO(JBQ!2:|>rNɦs w\L%o0" \!JMӐf@4$Y&mO0nt*QAB QZtaafs׵Fl%F\AۃPT39ju(b ѐKhfspqJ 8x,߿:Њb}cx<0LZmD|,If vvǠijyEOB ~Mdi(%x􇝛@Q($qP@1 -00rK=Mc˓p&cdxJn C7yqsɹGGX~aLͩ'U^}LlЍ]'o=)?<^]^ hm~(ӆV?e;ʺ6aiEzyXPYx<K9&vK C,6ڮ?eLKgd`[mUahVMbzD88CAb%~֔Q9${RVc@oZ9`. JXK&FU)4a"j2 BtE2@Pn@Hq;nN@6xn֛@<)8( AI/1x=n_ JA Eog YQ{6`sk x,fo4~T FNRNzpp4ݐRy@Dͤ*$I0dzȤHCÉ|((0M3ni H0%ccsco&E'F ?|>BATU[Y n7z>h$-!L&XL%I%;oP(UX*Cy;^4uq?759m#0xGrZ&,qyBD@V xS677*蒲duu ,վ27Z!O{6M]7AFv 6n;7Ǟ/jѪA<1N%aRW˵0L.{h%aӁ(Er7ٵEOl&ϡwdL3dw sގP~vn0Y8, yz7+.kEa 7;n/vzF1 ej[#qya.G5 [.I:N޾Zk1@enHv{OAU:;vw^Qom4MC[zPz^7:$\Z^AI|W/iP@Pakj5jj֙c Vk) iX*k>:wdz>TU?3@kpgeF] *wקX*YE#ivx|=\/YͭIx (‰lnE!Cba& vr{lŅypܵhT.rTF4A˺`XvHi͹mT,EG$Dq( ,O?a`sk "m)dgem s3h;) Z v lk0M`XvpRcI=LDRA(x4FE<͡\Buc1bTA"r!_(i۹\Vd; Ȋp(^߇x,zq("iMl&m |&mffP.WrIttP,a:fffl6J&!2F1DQjH$v\ᐘ1Cf/TMrlb0} źPՠ:8򅂭$v}0MH&0{d" 4EM/ Eht*v Yf:2x/oDDiibbF!hFA&c) 9΅t*50:24TUETn+Hh܂$Iz$OZiCh4X_z7*X[߀#>G@ײŪT+e3`Eͭ<6:VVnUQ1;3``D,k+ 'pn׋x}d0 IxS2fҖ<1 |F#TkC(z<{* yh< yͤ% `Ykt:rkդ! @S4h(KIHQL.+@khP00Q(m%w'cYRftѳ:iD4rr6̋.eًJBbY!c2oMӴeH4hՉ>A`Ҙ#H ]7b][Ge0 zH)úX0 =[(I28C&$K*"^xi I&rx`X2G(humBA Ť4 45OYɵUU X7>$ԚiBUUȲL GS Yd`&a/* ir|\.7QVEn VԝpmtMG"0(Z"!Aӌ}nAV{>|bAF[0 4]h9pbu1es%xcI8$R#Btr EBT$ID pt* IX*AT M(;mcQlnlBQU ٙ:.8eqםw`<CmU`%z<`ck&9 @54 3, ?ه R*Xhxxn$ juFcR*B&lTR(Ky\dINw114 7A 3@Fcdw( ʕ 0ƈ 0 ss$ hp^(K!fg0;K{β,24d04"0)Ld\6~G@OA׋@r S^!H@vx/x I\3VHީ%m$ d`w^/r({t*' =&Fa{R PT^?y86E!-3P(MSI+%a#+ LfqHpJRkΫV# CRVZTojh4rEj mS|r9v* 'u:@ 7Cq0$2Œ.{ٹDVtaŢD> p'm6{) ۜ`c@?L^s#gw88 M_QI3\oAdBS^t>.npyM`p|g^=nrVpsn^vDXކz\q2af';n8P7kK1mtT|~3E]sWΡ XTX*X"šass Zr}Wt7SZe"8#PuK* m%"Zv)F5MSKryG^q h{6B;q BN|.ζ\;FUUT*U"@:Qs<.Iґx-:LsEJH8^'A$3_(sudƕɑZ`v옌J^F1QՉ+h4F!>A@$,C HGH&l&4̇zNl6kKзH$p(d¶f}PVd.Eހ(H$[eYKAFC (f D  d0 4-h3& FMf,# AVdt IC4 N`^f X^8h`XFUè7d0 R ۍT2ARb1}x<uȲ90,t*nNT2y ki nYQ%Cdi#HL. H%A%u˥nF1r,ɦ%^Hxv6Ajp.Hu< (PUh:Idps**tǥX,F*?a8>h;[!2r :.dY}*R$TU2hAjNE!$ Dt"J> Nn#5λݮm6[Э D4M$=+W<YVttQ*$NZ c[0 n8C&ʰ`R T~BҒgK<5hYh4enwlwe]IiQ.ta|tnn*=d.A,AѲ*2r, "E!~,qQ|>C!eTU|K!2V P7XQa`x 2Q\.D"aet=xnJFnP ` X,ssXVh$|YQ V%IȤSD¨ꖑidnT.#A׉Znrn@7EZ/PZwIa&dY&˰l"JA4~w>\ qME8qbe9F1;CAX#T*^{sL<GPLnbabnvj T*ja[@uWUk>y (YS2ISIJ4Dh ā"=Jv>~zT bnܲձ킦/^wMS!ݷ~?z>F18L@\AŢl6f2!t{0Y##_(Zt8y,ŅynhX4Ņkv%zDI%jpݠ($,+"$&꺎NbVhlyxM|fs9T54M-C Dq{+~\?(psnhjFLSU0 e&EQPVl0HQ 04QS /u]GV'WrbzxlO:K|0\p[N.߻\Dݸ65b @Q$YF@4fgg,~hnP X;y m;'`$IBJoꍩkȲ,\.e%T0:$ xvsmX!X뺻\.4 M&4mؘܯz=Szr4'x,EӺ^x=h`(j3`YwYYK+ȤS&6]LLyf&FeME>Nl MSI?xLހ/x8p8 v-(:X .΅@ յ5CRIGc䰕/@e[YeTD5i̐ frh42'<4MM38P %K ÀOzCFZn%ԟG`h| krp"J 2npwm}L.k1Clhjv^XCC?|>`+!QN&[0IwTBJJ%Md9h벧iAXL$0P`AêȲ,ԋ!;]hN JTb9 4 j*?(66@4qVVٌ֠ݮX [y4 ekdS ]gjP IDAT܅K2~٦\ ^Q~Pbmòٙ),%IN..az O1L""ER`@󹊢`+_@6n&o9%!sN{6;DžQ(`4 ,KJnOq4 øxMB4=aH&ԋjawQrhp5}Y8->H6xq^a*:7lX/px;c!0X^euP$IRhgr{E^8PhY׍#H4"t覥+}`a' $شn&666QH0PPpD2%K}T 2ޝjw셳혦iү4t8hK+ ǒ493ƕ/(/Vx&F4M;~'.}<dBC]I;_ y}`0mch6ޞ)j"pGN(+Z ŅyPGT&Q*q5->t #(KwI(I*(|>ɏ4Mj:ƒχH$f("%Yа2鴽8W.W 2" Q70m @elL&0 6dYF2A^(f2`rTNѻz$qh GCY&PTXS)P4S'$'Z)(RO@ DфHRp,dEF&*?N:pft:e`DZ! RA$,,o&Z!ҩ8CހH$J&ZV{Q4@8?rM%\hM퇷$If3xVQ*VfE1h6 4Lv,#9FcI6xjh[H&) dDZLDTv\v:$IMQR"bJ=x>~4-T"#`iyPNp8DFf%,D"UU-5pJBaSIpZ]'iSIRA4~ S6fL.UUtA@PD4G0MJήu{&IB\F2@Nj~E$ &;cv0!J2Zq /ɤQ*a1X!d) K2Dqۍ|d@R&1>L"L|/NvjzumvnF+X^YpX h6*v5E> ٳdT˓:3 X^YEE@].>{xh,`4"JB ohz9} U㰼J>KQb3,sQMdT*յulln'SAaǾL0 1 R4?e!yh D,BzUxv?@Tq4Myyeh MѠh#:~x,kZY]ifd\zw4 0N: qIP`<QbumezmFa[m;=0HYA )ϋq3P0$,K gr I=t +kl٤ Pi ~K+v{5p"dE5uv͠@& >`\6k{={1.Er_Y] %4MS 67pss 33)%a\A$"gPD*_/J%+KcǮ.Fv(ӘvwdYahi$Y~}`0 &N(`Y~8N'߶x0(:^ ţ&;`#ΰ9& 3ǁi"Ȯf19,"=>˯罸Q*o3shDp^,X_m"t.G]p;h}~+  |s=| +Tqppp8.8çaxy/s3p .1i!Gۉ$ˇű(EQ0m!^|-"SUv9(x0ǽ41RAé[G>+y,wKdN$9FRrNE*MQQ\LUUk4[{v\dv,It$.%bu-RJBYwcѴ]QNKF?`@ҞAն`BJ_]nw6)pòj([nPF;)zVm[pRTE~]nXK*DP5- 4 .qJ ;04%4M=HRZ@Y, tHAXٴ |n(,t^x&KRI2ti&#d/0@8xěVA@r~4Z, q2!.8zg F97x <ܪ=76?FpxvƣZUy6 2zejύ 4 n&8^{ z= b3gN[y\.T*U¦VpbqǡP,a4ԩ( Fc5i[Yg"Cx?]G`>#XZ^˲#_?` ?w[{/C(x=wEhP(`sk b >23f\.;wJz K_?d{? Q_G?rm[^9xg0׋o!I7xv{ZE"Nj/h'~ȇ58^/>{o??#:u əvpnf>i|+_FziAKxG?=ٞy9??Y9}xHXtzFLo!}w8!+M!B[Q$q̠m9x,rpe1$?ʗriΝ=Yk}bqs3p8pcxN666ڣ˯Νse䃸Cᅗ$]2;jھ| "Ξ=qppܰ4yG0%s]w;Sۺg& ÀS^B)dB> #dPE'w:R88H\马 L4Z7 CQ`YYp6t{A˂uR3NvxWW0j=;\]]HpûρqˁxsQo8c5ܟ?߾`^>xSPvai骝9'CƛX][*TUSO?P(.,a+_}gřӧq ăUS3SsBM4MuxqIt,66ƛoH&**$IixWj0mCa^Tj<#F#hG+x~<|Km p{6t&]ی6s]ӡXAJ0 vXi3Q"8*3SO?IS I#R᭷/~n'?9x7L._qo}O<)6~o8qbyg?n6\XZƛoG,~ē?]w&󘟛=rO>4.nO>^|| %x|9qΪAah|O7sȽ/ 9~2?|'F SOz/}->N=szt:]B4| C0c?$KȤRHmTx1bQt,~_%v-PUls?|p4EC$D#QSIz:6^y5ĢQ3OMxeO "+aB~ۭxV] |ki:yv+.7 }ΞATL.W7/2G|j `Y:\.,"Ia˅d"uVVW}ko_ʗ/ XL.akھlϿ5y0 7x &>r/a}}cj;4_7/4 y|_ßxi̧?/5pF#R|껜 pAo+o/-a&ÉȊ^x@1kTmoƛoĉS)llnaqa_ΞE8©'ߛ^;axp)emx qi,-j}w߅/,!!760? rx7@4n&umAf`&"07߄ !ciy$oƫfpbq<; "8΅|C^DZŭ <AOY~ǵď~ ">O84 '޵:ᇏPgywBuw3Qę3Nv8nTxpܰ*0"D8&W>@ g% ûu#shi:88\g%9888\))vpppp8Zt]ǥV|B0 g8h PhpqFxT vIDATQp988 4vi>=|z*ޘދ`0+ Bu}(K60;3c ^s#0 Ar~w=)ppcCS5ehnL YRuzКᇏ_~_7pq7܍=j("JO݃ee" 5HQ z#*14ikN;鴝i3T'TMtҩiQ#@c9vUX؅]xw++ha~}x~CLt49/I㋬~1$xczhhs}imm{$eܸ#hY] Ip8ع=,y9_ 937dh4̀,ZMӉng˦|NW07sN 111$&*hZ*ݺELtx]Dve괩j>@vV&z23gc64,{/eNͩ^)c7|>z|, H1^ٌ.gif)))LII1A˲;;w#T!;;ؾmrY"g؁C$g/Ww7kHH0Xk Rc#%:|ŋb4@ x1޶6eeC-|%u l^WWX?7PO5C N|nDZ'7{$!1oH9"W.G=% ٯSvL&<%\CQZZ[9|XΩ2~r$ Ww7˗-ϊp82ldYv3'c6uBR)k(~|6 ~ ;͝B &C Dkk׮Y9~$s22E/rӤIW@ I~?,WU|aQ(c*##Z <* YwHƄV)@KKkmOW`qHqNDQ$I+CW 8nT <ཻJ5f0[@ x-ah@ S C!E"bIENDB`gammaray-2.11.3/docs/manual/images/gammaray-launcher-connect.png000066400000000000000000000272611413310443000246010ustar00rootroot00000000000000PNG  IHDRhAZF5+ pHYs+ IDATxwtgoUd4 "9%R9K<43w{g>wm9k8뽳ϝQF#QD%I9"3 4G !$HBH<+>[Uo}"":T ""S@ZDĥ"".h؎&qlOu)|>~T2&OS#32q dffNu)"t@?C]eưm.e1tχq%Ou) 'e,V@+L&7a gHd0q.APkMi@oڼe*7I 2Vd5ӹ;H$w=$ >ݴ0%ŔŶ2ǎQ\\ysߵZ}C%%Ŵλ|q:v YrM7Ï1LBsrR-"_nA' Gk[u'05k&+/c*nfRSS)/+0LjFG= ޻dp0|- TMQ]]8c,²ln. kV݇+N"ɡGc46}r"̞5}0xػw?O3g,~3,[K_U8ym޳w}v8a hkm`JnƱ?u'0M53LBA*+3k̯x}FMM-a}I$!edx۶~3XjYq%Y|-8Q Ez  g>n[zD";:HKM0]pͮqdtw2\EZZ ESx<8dggt3a=W4=֝ -- 0 pS,]4M ^> sۺ),Dz,"2z.YDk[;MzF:hh>JV,[`x˗qlfd$JZZUlkcŊ,]Soh`6a|>Dnb$n̛G?exR-"_'Ͽ7yb8쵳TzI?s]rA~]]]̨SL˟FN -̞5lZZZ),, +3@ Gffeo, q.yD=B>"TNr:==8CYY1MԴTfΨԔB\232HII!5%@ 9C~ _?oյ2s *DiI f #=P(Ԕrrrdg 咞F~~>8|x줼T)ϟp==8(SHjj* ̧ ?N#HO'/"+3s1?{<^/99 rΦirs$ߐnttԔKW(ex^222ƺz9cA ihhddeefFU%tuuEJz .;fL4hKK+wߨb6 ̟2.P]Smށ˾M?S]q+KMx0I}~n\{KuzzZ x0/:i p;D"̛;U+W˯x3{]]eKiko۶yW_L$@cc} 3*bNFF}wܰf5X'q^kjiiiŲ,z1W^J)"Lm~N?ͭ7D݉zxY`̨'# bYv!QWw^xYh! 7"9sHII{O=AOOYYY8Nvvָc񽧞ũܳϐ RXXhXl|aΙê+(--aq::;7SOߥP(۶ 2on_@ @aA>1Ʋ--]O>08snbͧcqȵ5 ٱsǫk8Q@iI1;vj1 a(qIK-dꕬ[{+tvurqmeffRw^!>zN:sfϺ` χ祫C֖ d׋yf:Nji>{=gFs  }|^ǢE a*nƱg[cӑ|8c7_`>wܶ@΅]ّ.]¡CIIIX,]Y`~5?_>eEymm,˚쯀|&.}֌U 2<<Œ***  QR\Urs  ' 9r]0CN @ ZZZ)**$? Q\\D^^0yEE477SQ^NII1}}p걏l&%%捍 dfddKSZRLNN))@Cn0H rCdff RZRL< <4D"ax6rx<NIqӦ :V?3ho (iii̚=~~?EcP$,Zٻx0L5c̨ht`l`B{zimm޻bm'dr_jk;p`oXCGgeym^ֈp"Szz:< ~eYl߱|ֶ6NwK/kow~lf]]?JcS2Ɖzz ?|Bdfdc>±!BAxQ>ݼ믜>;o_aX]uuO/pQ swn`p0%^%@***rr))l)//Pq 2'*eq!,^@<`9z/mG:jHP9pU>.,(uzz:ӦwYd1 deeI `=bimkc]qx5 ˗-;8ʧp4OE}C#5u_̝3_}ycd{}@={+VXNo_6oettvR_H_?[pW_{imk?L+YzAxw\@4222طmsСˮl7l_}FjN"//D(7}Q^~ JͷP$o5A"KKM?6|p՗NXTVh!9O5~DGGӧ oOְpOާO3K'/6.JK[T-"r|MI@pu#D- ]~ZZ\srJNNI@d纞,-"gy]B4''Uys% ) h0^QDD&M1Ԥ[фð厏""TTr&/ՏRmrEH2D[,a;EٓꁻW&~so>˲m06EDS@UeY>rt-Ɩ[_d&|]U@q^~u`ϰ,ZvE$2L[[;Gmmiiiqٱs7p̕ؼq_;CKk+;wfhh!<~;Q?eȷUwq,^'`Q233/8p'O5o~ E/[َ\'Oy465f此ǟDm <))ȟ7eȷU[nb֭8mƶLF{B0Vٯl!98\@'"",'+ M"6 cq.{P;aLuΝu㒡 ECAt q,˺\wElwdM:u'&"D[nȞSc&Н1*B)).N3ua0> "rWqx CϠ;j[ LJMǭI⠉ǰL^:Ϥuء:>;lyIM_lrgzaax͜`"?N5B,^-&i:OO6찵& k =x a1#dzp>j'ca&M0-+;갹ݢk.恷L& [`$ngf%KsMVL~] ;Z,n/Qm~xh:W[,+I^|uC6D2ToC~y i0jA`<OV#NŌ,Y&{lwYI5i؜;4 '~bIdr->Xb}EE܀.JDd8ݠnrTg‘>M6G4ErS }-= kSi }ġ"`qpMx;&QQ6$Z'IF|߄,_F?2j9d Y>8>ϲdL~:xC̀32s~!C$7!ǟ7MIH>2K1x'mC L>+ˠ9uE;mQaGʼ|j`"rm\q@W:8逮HeU$""I_DD\J-"R hR@ZDĥ"".q)K)ED\J-">z8WIDATR hR@ZDĥ"".q)K)ED\J-"R hR@ZDĥ"".q)K)ED\J-"R hR@ZDĥ"".q)K)ED\J-"R hR@ZDĥ"".q)K)ED\J-"R hR@ZDĥ"".q)K)ED\J-"R hR@ZDĥ"".q)K)ED\J-"R hR@ZDĥ"".q)K)ED\J-"R hR@ZDĥ"".q)K)ED\J-"R hR@ZDĥ"".q)K)ED\J-"R hR@ZDĥ"".q)K)ED\J-"R hR@ZDĥ"".q)K)ED\J-"R hR@ZDĥ"".q)K)ED\J-"R hR@ZDĥ"".q)K)ED\J-"R hR@ZDĥ"".q)K)ED\J-"R hR@ZDĥ"".q)K)ED\J-"R hR@ZDĥ"".q)K)ED\J-"R hR@ZDĥ"".q)K)ED\J-"R hR@ZDĥ"".q)K)ED\J-"R hR@ZDĥ"".q)K)ED\J-"R hR@ZDĥ"".q)K)ED\J-"R hR@ZDĥ"".q)K)ED\J-"R hR@ZDĥ"".q)K)ED\J-"R hR@ZDĥ"".q)K)ED\J-"R hR@ZDĥ"".q)K)ED\J-"R hR@ZDĥ"".q)K)ED\J-"R hR@ZDĥ"".q)KM:G-qg-""َCԚ|z';cy::a\U]""zI-UDDAZDĥ&h,K, .-=ar0'N[Wm\Oiə ZDĥ"".Eq/zb-23IKK>MaEs4ƗT@ 3ե\H4JɓM SZZrAN5۶ihl"##7 hq\"4q\<64-{3嶺 H>+D"Ga``_}ۿ ^g^مaQXXwAYiuV?[222Ʀoڼ7~0 JKQPe5FZ2/MZ&a$a"kֿqN```WB,_0oh~>CiI O7m wbR'g?"/[ĕ-"ҙ,qǽ& K/c'Ďݾn#|{|| :O``9/w:o ?,JKK//Co_-tYfvvc̚5UsT3_⅙\{ò͋DnX#MOqrqJK)+-eϾ}}X~R-x<+ o~.==O]w^p +`CPP-7H] a-oßXj%Y)[Ddsq>'26;wl"aۡ6~`6mwgp`Zo|hth͛{>l!<8?$/X\z8Fbv3by/Xc󴴶/Xp!c i>8jn?qػo?lŶKTp]mlMM'ǽ?0P$3Ƶ|/,[>CFFfd-?0H[{}}88x=u|88::;cضga16]y((gtto~fTUƏhhhE w~<^e|y n% Om]>U̙3z=8bH7DG[<Ӈ?6ϑGY{gaQr}8lڲ|O?fq:HiW1KrY39MUe%w/z&I'%+;--?0 -oXghhhL"r=8+9ΆK$waMx^ܰc\vlVhP.%gZ`044Dww7P{q!X^QAyEE>7n3{:O=^=‚|V,_6E ǖ?[=o0X{#vor(?>OSfu€LnBd°$eT`]ީT:fEQd Ť`Rs՚6$I6[֎p:pf `ϟi:nwY:Vrrrr|UU:ֆP(L^+{qIgnkh<&Sz_d2x&ff|2d B$3<2034< s!O¯}vv Pkz}72:=^o{rj+׮{;:;i$//[M|Y.\`ojbt]l B,.Eo_&EϿ8˱p2^:jSYQAyE9MMYU[C$ҕ\ڹ˦Mvwz'ؿo/7n( SSSK'_kL&,V _;K'ɱ?ֱ7$ MMSdeTWWJ~^[l#~jkkؿo-N?F$EUUnhSO*ʊ \khSυ8v33>:)-).*hqAvX}HG`v10pMٻGF[C0b-ttv1==M$Db9=vpjzq'lX ׳a:VZlBuZbO.ۀ߿|XL&3t׮31Au'Ąxa^n7btvz)v)vzX,f:@o_x/N$ -7QYYݏuzoA 0KWw>V ֯cںwx,FKk;hx,jeUmtu0 244D4{fhxq333B<+]O~O~#{HR|*3!?/]b:z'Xl t;wimkT*f̰bt:NN@nn.剦Nkk;OL0>1˕KNN6MӰlXsx**)-)~.\LLxtrcv3ӄVְo|_UU%eܱIee'F4׿K'Npwp .JJZtuuc'//6 >1XnOMO|C$LψD躆(:&99v\.~Ӊi8N(N;4MG>99E<ǑjO$p:l6f|>V\(`(8mJZ,X,>|`TUc#;G,|@{Á?l6G(FQp0;ԆŦhN<tiDX,McJ)(,@V<~?%%% l6Y!c೶X> "%!ބPAYph\zvl6c6چy.-|h<3NY:%Cpʊ씴s~##T+"- nbѯ[LQ%9 #c՜e2+D0]ڹhG?Nx7Rwtqzcq0bxdd6]7巿d2I"}}x˼tq jm!9Rp!𝻃 ĄJ]o$77#r@2ff|Lx<\t?ϸ,ǎatlw۶sasEf/?FSm^ؿџݍd]O[O|?r% ]]D14M sؾ}+XClܰ_o? p]׹r^9륳T*Eii f`dP8L*na(..wMӸz[{wDlTWU?@07ԩ|TVVp"RL_o tvvijjfbƒj$YS<,f3T7(,՗O5{axd^|>?;D㥢a::ٺe3^$H5Yn-%%Iq?C"嫌SVVz<tvuHӔR{'֯cl|*mžݻhm`bC(ĄV믽B0d՗_"LK'2֬ʕk\r B|vxSni]Tĭfn55( h4)H$r얕R[[=XpwO/7mdUm G]l6n7=}W3azzƞc'rht`Zur #3:6F*‘Cgw[Z3l6kWKk;vj+5 b6q8q8_UBv8 -ؾukװk {wOMM5[lf[[Gә; 񤞦w$##ܺWtڰm]  QBrrrH$X-fA." VU d2QP?P(i8Hqn$N'Y4n\P}喈@ sIӟʩ$Ir:0Mؾ OOOEE)'JYm4n#$I4Ur1r |~BIq1H 3_oI2pe^X&$&لh?D2IN4%//H$J(x<%ӀķJNBuT*Eudd 6d{[kn;;(?V=lxdU5BdC8Դfl6edֶ6[FMM5[ZpDx2VEȡaO= PZZbfaZX~b14U sB˲^jٽk4M)).^} !뙭N&DQ 3OV8Au\R EԞZCz BTK[X,0& l#5MЖpSgl6/:z\4TEBG;'kɄgJ|hNeE7nh:?\l^Bޞ([dpphm÷|fB=A.r-7nׇAu4.\d'+ܓi_vD"|?>%cph7p8rHRڹgxdEK$'b[Z}F i >I|miK{X^_i=̙$XPa^̽ebKQ. !3}?xM__?yyy\~U(+-epp~n޼7ZZ4- UYʮ a(sW53Y~JGc4bM+#qϮ [r߬34^䓴Iaٞ:K-"HOo_G:~1BcYme:PTXvK_3ni7^_:?g.}}z*.]`mni%//7Bo 4-[l2'e_GFشq^GSmlV+45d2195Śի(*,՗_VmEajz2!ķ(~-{f߽fW(-)7^gph=DSTXH*++|ccаg7-mT-RAriYlŲx.Zhw=6 !޻ʱql-8#|zfE.jRKPc%3m"$n *&in`b-T¥F|7I&SaVN )).f磪(%%Ť*DQ, ^$y.j/!%o!Nbq._FM,_ra>s9ą)(8t7p8q~ "xb1?J'[C0$fիVsvVְc6$A6mڈܾ+Wb6[ ܹC:~B,zelvwNrq),,]Y]UťKWXn-ee(ؾ ʕȱ<(xj_8<4>r%~lNWw\%J1::6/Tnj335=y|zsH/Ϟ'JgkFGG'7n*X05`?07|cLNN1p.d/,sH ux3  r :QUS/X<-ǕW_ xt5MQW? l̋ǎfq~4 C?vݖyNUUb> TWUFo_?[l&gOXT*E2&8-TfX<͛8t,w_\/ _܇\g.6P]UEUeN``?xQ;:1)J {{PYQªU;r8 x<^_ynoiy+x/ϝ17ޠIfggK,^֮l1gy#'իWquv>s:+w.+s:^=r łpbjz)oǮ;1LwtP\R 0ܹ3Mذa/^łnc DFz'QɩL-(PQQFYY33>6lX_b;w4P_SQUu޶5U\8V+ϝ73㣼,[YlV+6 ]֭&vNuuCCCl<7dX,h,6fNIq1L&@x< D qzv{-h;wY f\/$yF띤=﹕3Xy`4M#Jc?۩1ɧgWO"OH ,=*W<@)$`YUU{u^;Ȇ99v&<LoTǦ*EEEnb077U.0T6} j(.D!ZG!xB+'|'oUJJvSẘ h4Q F|`0H8[CYzH!#_xV?[Mttv-_FRb_ :SSS ?Te&L m?9,L i*۶nzM<ǎ6HWNKDQNt/:Ǐ3_p0>1A(#b1^{en1$pwp-Tq.\ıGv :##ni~6n@Mu5޽KGW7??zo;֭/߇B!{ܱ-L&**s} {HUٽk'ƞ$)nR{Hmp<0]~&^UK_d͛6qEmMu!秣=x&]# gYWWŋiykVx#SEF.]=ǾEb˕K0+\p5WIXL-}?ϸҊf33;;K*{=5WQRRLyyyLMMF)((Νy Nee%m~Դfz+j`UPL&lJ,f M=麎jj*UUXsdvfd2Nu*mB*ݻbl|k=b1wvS`0l4M+wp5UV:l% 6;=Ο VlZmV>5MX<^|C!ӊ <Xr#/υv3wЊ @EQv3+TBC=@lIyBhBUMud2)W݄ -@PwD,0 9(0{Xht`0F(PXP Yk:M;&PZZBNOeH$2Yk >yN$rp:uB333n`~=<\a0t:())ydbtEQ<+],p%`_??}Gn.\L{{-.^L͛TWU*~_۷m/+7O3!WXv;Slٲ A!sł8㡰ܾQǢ:]%.ihƇ?frrO3%^>@u&_P(D("u:0Lb7x/o|ՇzBB!TP(Hѽp+3==C_@{I"s0p'Xl|tiN'=lX2 _*pwO/o ӽ%2lE5+:x-[6s9MhmkM~[['w{Im i_|m)=w/3|~.^gtt t# |Q*^$7o6NڵFҩc=M"6%ŘK 6̼ggU چ p-R6|cW295`?FOo;ob6/~EQ())fzzt:KsxAΝAu'& t8]bjk!&&I&|gl߶_v||‚>=7o7o1>>(T޾~>,%F*•fSӃ}~_T$9BO&Q߷=#ZZPUΞf#OpO\~z{ҸG^U;`6o{|'36>>^yӿ̈́ؓڬ6nغe+COocccXVY&'uXR82w!SVS]UI$QCGlـc,G)*̼HĉDLNMTLUX,\.86O`6,QY05M\g.C##||ݿ/~+y6nŋ|79qN?ƉǸ|C.4T !ēV, 'NcTWW?t/j}> ۶m%Lk^UKqq%P^^FCnopP[Sfw.|v !FO${?Eqf^1.]ĄjJJJd||];wJ()vSvD)++]3_Z`H$/~kfALH(&?_Cfj^//#m;:9{%%ŠC"'#VO?GVSS?_$c55?#/ !易-{ł=M]T\ϖa֯[7G`:&p8'((6RNX,f˜DWŊÑbAuB0y.Wfjr]D#Q( 6\#Ͽ8y<^rss8ٽGv,KW?}z,# )(!ʞdݯi?бe)wb3",V˼SH֦f+@QQ?j,F 射%(0, @!aDzc<7_G(^_LJWw37O8)׶t]'>K;wzih{ p8{}AFFn27@UU%&Hl߇d6s:3TTVpϙtr&H%S6{D(v&P޻qjj6=\.#A{Wl6v;;:wښM r]j6mFFFϢϹYYY$I>>)PWWHX,OOxqy˔277ϝ޻&ddd֧.[UU|%%OF/[Kssͳ[VZ[hdRt۽}55l:pEUMds9z# :``(ݻuX X,ܱ۷zX^x}fӉW">> &;0T#ȝ}E|bmBuu8y.R H,  cXٳk;wld:\BleD"fcK[ǎyz#./399I"ڹ7d||rþyϟ' dBd9 #cx=tJ`qяlj~cdd_q lB>p8L*vp8ؘ݁l~v;ws..yq]v aZX,(BUU%e݃b~~IN'c/~ vmܱښHMm =AKrDﻼy/,pR, 6<255jcjj)WMy^@p8.3k(ɤnv 8N4MtH$p:O\STftB4nl6IRv8nb6f1̘͏?~R7\.G8b"d2}@wA8d2xVP#eJK}r**8Hǃ!B|%NEE9d{ܱA0"qq~*"¥˝LMSs`0ۉͳc6zQZ꣺޻HrkqoQRRIPVZJKK3;[r\܉dghx:GcGX^^fv,.uJ(*r%sIt]Gyj_oy:?:M6el|Ɔz&&É׎sb9Luu?J]1˝ Ҿg7`|vvuxr\_pdd21>>A2bu5E{lJǕngrt]AUadtN``pX ~MzuGGf6p$Bty"RPΜvLstaɍضu WIӼy$'8y5.ut9kgv~@ HKle Z[ZՍdpRAo'OPW[Ȩ12nܼWBIq1W{ػg7e>t]JUN:ɕ6F)+-AUUc`jzL&CMMup8v+wzȵ7x[ Ϟr\+]b+466ͩ7^́t\馸-۸}l6KUU%~o~k,/Ǩafv}{`XvMXVFiinbbr ʦMaYYXX3<2>m\褡UB0'Og3g9zfή4Ҝφ1;J dphcGS\T"1K} |ʕǾN9tt^ؑC\& :upƭ-nkt\r S?0HGgnq!n8͍ LNN'J^~_=I%R[l`n~SSD"9K@{ ee:xblnN]`pN/D4sz<  k:w۰L&3{GX]]p+J|NgW7;r$OEe#ܺ}M,?95}b2b``{Y ضe wz?08e>^N%%%MCC=#c`ؑttv70dN]BL&f es@8UiAehxoJ7Nj9|۽w9'{֯ݸ"H۶^]%ptu?@6egJJuZ9|> UU,,,u &Y/195$226uul6p.^;vVp9\Ŀ~OLkz`pp7ONGgxX ˙111Im&جnd8w"_@<<2FCC=33$ jkkoiϿ NGg˸uA.j죣Y;E9sCqY7Lc~Ȏ0LE0:6?}(vd4p8b~~D",,,b26\٬adL&K8[]UYa'dPUXlh4fW⣮۷mN8^G2n=׮?=~.j6mӧ((F4L,CQ$QYYAcC#C|47FWB,fS5H]y7./H[[ ln2򟶫D"n޺hѭfux<(>vNeedX ل(1j% L&󕰿}/^ d9,}yy^l6];QUQ2 T EQ6|p ØLuR)p f A(&g%$VD"IEyJnw~ "J)))j25=Cl(#RYYA.o`I"d˖͜xmJ$~s؏ba`pĖ6r3slj GHX,"Ԅn'YܡP_GqqdF,Nv0~n̖m Vln#HL&^_?456`-ѱqV2 QUYAQr^t2n7U\.t]7>h&koTUV2;;G$vrx6/RT*+PM*tp$n'LXx=JKK9z`Yݽ M@uc KFȀd{} e>+9voX56fc=rVimioǃ롱@۶R\T(@Ey9d*԰m&&&ٶus~pa,V GKuU~ZZT]ɤbZٵsMM LOԘo y^lncqO[k UUx5.Zp8X&ٹc;z8vBQZZ(񕰴 r o@:̏;x>RmmOLracVUY$w`ӦjvmT/-F10ܺ}{(/+c\zVٽsUvl4۶naͨJ6E,MMWl܊롲׃fg_^l6sssl6Z[zX_?2>>ɞ;ٴ)= ԮǏAUMDUUU~y䵦zB0--4zZ,6 ؽk'n#6ܦ*r9"87xw~qjMc@UFFǨ M2[7iT*}d||mQ>Dcc۷mqnBO=ƆxՏYKK kKcC^R"(M 095DZ922nMUx^82 {즤`(đCinjĿbRlV{좮ٹy>lfmȣP6Yb<83ox&Ï[Qr9񤑔_޻صsǫ.SJJ E"~}u:3wNE~`D"f{dr'IA,Dݎp:"Gq8I*bb~F㸜|=i\Ex<駖)Op֖fZ[^j}24j׷d @!x[빻hȆ׬V+_u^ VQ/ }}ٙț5ɒL&f_z6eYX-V_;Fd2ܺu LOOfWnl6˸74.\z/_F4_z\ g,µ7T({(&VWm{455qu؟F5c,b?}XVt_Oq:ܺˮ;BvE^/l6ǁLNNG8xp?W{i3@0[O1:68{vt:ҕ ƅI9 {pmjkja߾=d#-,0::Ʊ#YY}l|@0DCC=\_dܸy{vsme泥 OyQf昘ⵣG"]bvvodaqBt/-G1L r!y!.\92v۶_Z޽~=J7S_3{H$t(Hl6K(j2<2bAU ע(##c8V nJZo`RU(>7oQW[C&0<2ax[6STeh^en7#D"m>Dm޵}{ٲRݳյ())=o}{q33/r}Z$۷nv3<:ʾ=,VBlۺ{c6f3#cou@ V*[/tr`>ܽKII -ͼ)\+).杷NcX_kǎiS5c~ad2E0"FGǨEQU&'hnj"N395EcCL6 Xhin"PRRMդ3i2 dfs[+ L̒J(.*dRY/DFڵբ"j6U^52 Ny6bXr=+*&R_WK" <04SVqlVl9 IDAT|'8w"Ʒa+r _P+!xחco)רd2?36>Nyyd/_]g~a.\. \Ĥi9>39y8CC|yVSX,>L6Eՙ108D]m-]#c\~J6UW15=6 ]=|q5rYc477R\16z^?( wn**XXsgVW7y7ikkw{},,.7G;oDv&y-*Gn##VSƸass<7oߡ}nvNwOo~Ɔ~y$.wFIVlBƀ%.8K lnz<\I(wJVx\dB4.\0FiNl!,6O?5d˝Wvw~MևӧNFI%S{^\c6I&\xp$­wP߾I^c9)TZNilhxCBo}y~_s'>ra1[mBKSPZV՞k,,,51j56lh&\#QVZJWwO/7oaac-KJJ+ R]U; RUYEfffhkm׎ZSYYAEy9)jaff6Joݜxx ]=TVVpN/>_ P[[C_(+-p-[hjlTW}uF޽Gqq t&m[`>G?]JK ,/عc;m--ڹښ\.q =׮^#tGG--- (//ce%Ɛ !dDTUVwfyB|Q')6^T,[׾~oήncNNM7BW+ '3ح/_Z!^T: 97ɄZ>H2@G^`k)_=NjuP+$:6Vק3)Tj`{ l&yDS/,l /k$rϞY\fyLJS}ۋ7oݒvcnyO*"*eve1ΥRd]{cIeϤg̳٫P7k}=w Pu4<I޼Kl2ۊT:\\Q0֩HkDelE{5fZ-FEkUc%lNWhj:2*¹14] d5JpU'i\Yt ۊT:#[T:{ k2Ա8\(TޟrLfRc{U+ *L,u.}X=^RdQ0*NFS?j,eްFMΩ0)2rF+PeW8Y¥2n>gZ6 SqBZJKaxYc8}E5d57w=h֩l84{T> N~=DŽHg3,+= d3t6r-1pX"mE*܋ht*sx m ?]%*9"Nhrl-2=XHEaOJT :իVt6G e ܋hL't69>YXF9KTL (ZuUX]HU߄],3{[tn5* ˌSF[U0+wz >ƍFK}`c]2klg9Jm nE1۬sa1GUʡ\KZdVZAdR`K½B`UgU i i|6emՍ] +Ul?Pf¼>^¯rd5t[Ʊ:Щy(yLVK_DZ[U0l.>o>qF4Vu (*jpf6Gh垝1~p+' 1O%h:: eXVg,9YiΥ`5+Y7L$s:y̥ *L=tͫn˃u:qUFN\r(s-N*L,$5xUjit)$:Nk㊋uhZت]R*?[adZƃv"Јd_щgko&Q{}Ʈ{.h:nQ+6fu(*0v(V4&8YiX9ZabJFbkjz Ua$ F@rxY'^S˩,Mnѕy*QYJ鼽Ķ"#w-jJV1)1lXF"K~CǔE5ˢBMdDN0]`8Vk 9=uު6y+m|SU&b\oKj+z2x,,44i U^8Ue_":X*On0?K7#c[2N83M;bM7Z^6f% xk:9X{d9\=xb^[Mvm>tnS7*צkk?jSקikǙU]f2_ߩ1B8y` VM5.U1ʟ] LUY_9=?ZskP1ʔc><ٵߓݟo}OσuqmfQ2fG6( 5Nk SP܂a{4}.us5|tj=[/GZ3]} GZ*FXTcjxkqqf~zkǁd,Ǟݴ^FZܧ\3g`7V+Nz^b++PO(&wߦ?@4OZ,N]]-7njrEٽm[oz=:p4JuU%SS3Ac+)~}:O>%/~n7NÏPVVJ$14<»o>oQQQ? L9| WI&Sw姿x"3g(Ü9&}>>)ZKK'82||S\N'wlܸy+<_{TT0<2{vc6/r u\܁}|yjju2oue!5^wmIRinL(&Hs $G?d=gff=X]]?a~Dhǃ(456Ҿw7?0+ؾ =1͌O1ds9VS)2 TX?b2٬YH$fӉl7OJ]Y^^Pϝ޻\®Tjł,Y\\`8, H$B! ^|wHQJz<̦}NUUZ\X'70Hoʱ(L`ZlX,Vm*|?hkkٳFQL&=w( 5hlg%C#iJύVS)B$e9c~~V, nEU˯Pٹ9\.'壏Ϣ(F~d3"(UTST͗an~l6LMA(\.~_019ilڃ(.*?.Zgx\.G(a~a\N3 33sh_~[k+2/-uܻ9 0H&޷A4ٳxR_]]2޹#xѹ>]myܤ2==MeE ~=DyQ|>'GO_V4g?~8l_F8gn>G?|%p=π/t PpЁYTUo|co7RoOxEdz9EE^GK 'wJ ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! `I ! YX>YaV~B/-)>;M_I@ɞPj֟⷏\BGZfhtUVⷃ͏*Ht=]IrO[CV+aH$*fMK!^((XXH$߈W rPUH&S<:r:p^*bR_]2¼ HR]]ʣi:/qD"N'VEJI3APfQ\TxAVVVPAYY#+Ow߄6ʫ-?w'?|ĉhjl޽?ʥ\vMr/ܱ=?._+͆߿Ķm[~(!(ċЍ77}p>\xfN__?7lva]r~|?b:o&= bdD2@UURT{~>zB< ]I&8sLl6f_{YlvC?FExjO'u2 m7,w)t200H[k >KA!?__}h~K$)e}TrRG'ǎa۶\q3?i՞k◿)z-x^{~49DT*⢟d2I2$1._e2p4Hp8iR)B"H066F*ڰ/.ًSe9ѢgLNN144L.&'}]יu2 \~\.Dۿ{/֗i  k̦'p( eeA,.xQ_$]יgvnx_hrq8Kl6B&B0DT*E6%O0?iA̟>.?Qr~{ =EȭY<}ܾK.=Νj%Z3gIVyw b'4/&W?eph }E˘bvn|tvήm* tNvY+mhBj!T0IӤ"!Sv9 h5 @eIe&n2>\5q%P[>Gs7oQTE:/p^}βxJwP(02iWU# dY}AT%RMb:&ăm!RbbcL ]cc<[1A}987 ǯKK %a`h4x'yTU qw>Na2;;T2\. \! ]sA?r\摇LrS'&891ɉqffX}[ܸ .#}ECI)q(Goi7 85B @4ٿxo(p]0IJm&pZ1,gB6;>b|ffq.##yT*x81qIuBϹz3R׹z#R:A+Om%H:CL_]g1L}}2KضO%d2޹>?Be!ccO^ b/l6K6GP(w{{oG y\ZGU0蛚oj|~ϟJ 9ر}ѣq{A9:DZyh6а,*IDATi1uK50LbzV#8 UVc;6^&h4m{g]ZYO;i4 d3,+.n.qCc=5@0nl vgzݶ#q4M#ۖy,wBш*v@!R~(m l#XP>,bmm TJO(Ov:MTڶ:en30 b/eBT({% Şw-NFygIENDB`gammaray-2.11.3/docs/manual/images/gammaray-locales.png000066400000000000000000005357411413310443000230020ustar00rootroot00000000000000PNG  IHDR   _ pHYs+ IDATxw|G08=9)B 2ؘp^z{Ž<|=yŘ !$DP@9ьf&tz_[j]U]vx<[Z.$8^dx<=kx<ox<2CoknntBx<{ZT*eR*r8q110ix<q4Mj4 +xoEQGT<xˌx<z)stFC ̾+VcI;9=P8))4kt>>l0tIp'/ʼnq'YqfC qY~~|} ZgjCK2a8|/Cӵup;Y s{ZF*E|u=: =!+)i~k!]`Ri{b] Pu<@rxq:] nߺkvv_Q8X*raeB#4w`1v$I V(i/}7KK#ZaΝvBr}qv] 8 _oZUJv`1FݕZLP`csnmzy"N5}%I1  [6mXd>YhSek<<) n4ܤ(zQ[m6er2 {+.n7}ug 7n \i733$izvv.00QH.<+:^$bYV+obYV*{E<ϝŚaQ]^\Tu8$#m6BP^`.(?`\.éT*fffۺO  joniݾu` <D"&Wg*)i``'5%nI@ֶvoEt4JĘf|aaLLJGFJiX##"D"a_@dD8[m`UFa]݋6RMDk[;&SS*JeSsW_!A\.Wk{ #-47IEFDhǭEVe;^aJֱ,(7BCW=qFӥ+׿t$,ˎi5! hf9$޾~)쪬:gL~xeY\^?08/1 #HGgתt$cb5E=80flh@_߀T*]1=3377o4 EzZ* }FQ!WMҿ)4+3w+9MGFBCbc`phxD7/*,MLոV'9NXۇ ZtRR|jMSѨьoټ{MH%ՙfehE!X\y,C|S^ZrUb@aXX|ŅFsY‘ǕJEk{GwOwvv,sh2T;1Qwf\{bc'k/Yekj ˰VWWB.kj/ SǎW[6,hmf??\.+[V~Yqy<{~h2L]z!Ja}jQ9{"EQ/G$,EӌX,ӌ_XAGǵ:X|Td hu ("^eBQT=v2J2leyᬬ:7;;{xqG#B!108r񓁁ͷz::[n Ph3++ 00 ONNzTeթ*#RPPNINr^o8v`PV]:@1U:hAPTDSU,~a [F575tb ,y͹n-J}qNvVNfC#o? zz:0CKsp #Y?}dZXXljU^ ~tTx}fl~_i{Ǿ'J/^hb}IV]SZT,9&Ke%Œ=c{ yDӅܧ(PPq9c)K#g顖KQ7\N$P]cINJHKW:s͛)/ldžx#6?誻Vk/<bd҈g4O*2?/!:3 =-wWc \%1ݏpk׮zn_`(\޲OeYU>>Ӫ30.]w}+?ALmqG4.q˲ ܷ+<6|A?xA1x<2,xeX<xˌx<`x<-3>x<[fyd Hw 9sOֶހbKaԔȈp~{646!$s8(5MsGG.!) Ka=Z]{GK,:s_ ĄV[;X?/'k5_O\-l4N-tܽRܣA\ϚeY.=yߊarrA,9NK!Z8.VjEa<__%m˲uWU*ՃKӴ淳C3,zj! {Yga<K%ɒܦi܅{ې'j v'YO_R $' x_>Lp`U%<] \|E ;4"If-B"33Jli='< ).nvv#{vpm ;;:m$Z53;;4<"J3Rf43c0LN mے"]ң"~ƛ_ FŲԔb]oryB|0=E$% 7LF37DFDhƵ ̛~.lDkuAQsJG-Oom˦AB;ata$::iNNL(r6jMJGD7O{2;Q/ꡨ/w -5 044"RS41ё0x:YMIJQD"&I a7rcMCG?m㸙١L2;7777?=3#RS`vΕ+[m/2M8xFMӉ qz3{wIpo8kuZ.<,,2"|\(jnn>00tEGEiu1\A'/"09== "=DD<<22b=ujۖM%E 4M75ۿ^ڻ;:*v:3;#/ٵ*#&`;㺉 ͸64$$:*R =ٹ 13;X24((~Ra<\R\pػ{IbBV7Afguuw;]@օȈΈp!Aku+Հ?u;@P  mgΎi9Fz}|lkk/ku7{!:z$0,377oXf7o9vB&6jtfGn7yxniE뇆=R=u㸦[](>rl|vV729;rnБm ޾sfff<2?or 5v0utwtl6ܼX]c0LZ 'OU:}|<=3ò'?wG6MkjYs>veYEJEHHp{GbHY s6rmCC}T"8@ a&$AB+W7VݮNZ.\&l6[3XqRypB+פɹ{?&Iox<88=77x|jA6oPwE'8Vq0yȱ>?txwOX,T' յn7ƛwTN~(J%a;hFQڍaQ* <| FΎ.k IZqpGӳ,}:|xWwX]{e9r8Ų ra>;xpVkGgW_MM]7%b7e4df=2BadDxYI&$t:N<Ͱ,rRݕ1͸:^q @SܼbПJ$/Tku5uk.IKm33>$ݟ}~|Qg&Jbf@$ ӓSƺ+WG4>>o4L{1<2zUll0MѥRSX,,Dk8b',eҴX,H2Rny`Z&&v$=[l!=[6EGE6jݸpm91yw W*SFx<(Aq@c0Yf ESW?k ;8H]헿xwfg2xx!0}(EP@㺺{GF6m\OEQt:r~adl #9cI?__@ nevKv`Az"h@?ɏ/@;o;3;KѴbJۓv:}Ғ@`V~֗ Mn7yȱuAėwVRN&m6EAXX^yqd2fB*4Mp gBQrĢؘݻ{gNj\69e.,wUNoAQuvwk47aY0 ! oyy |zzaٹˤG8rڍՙ:ɍ8.6?;gm7;7O,H !pɃG .vUʗaHRðq aH l%;օWrWը[^'?Jwm 74=770ٹ]CGJKBvFI7x<ԁ~ *Rot:]A\ḻ'߾fkua!?R`]US@j XTT*aXVdR)!nߺ\cSsPP >/f;ƼBw BH$!A R*qOIN>j{vpIF6ㆇGrYJrdZH -ig}7|G*^DxD,rr?}샇0eFDHBd+*PY_^7QQ9rP*AWotvU*OTd6m\ w'@$ r}@{3!D7DQ$1>>=5E7ofϝX\Wؘ߿n`@`jJaBc* 5ё+ϪTJX\w^5WTe%LF?r$A%zJH;Jl??%m˲./..XmY$ ݁p-Ygf{JWǠotT*]EXOK= ==}e%dA dr_OX4 2îJ]2E$BaWV[{Gk{#iK)1(0pjahEȈܜٹȯOYmH}]x<kgff%qA~^XX2ϝ ^կ#[ {:΂(nh 9QL}&?/'0 `L!IORb%+KK~su/9)1>.izFI3Eo6NNNNN#‚RSBP2#ÌF\.hof8ǣ+! ǵFcDxXP`V7jUT$IZА !8(0<,lvnnddLT(۷mˤzz{YM#c4MĄxBaь,d.[3ۿF3~ˤҾ7I&%&T=ow86/QINJq|5uB=蘷! bD)V  mmo_W6<< q>08axRb<˲ 㳰tji:9)AᅅŔC#66VPi40anZ=a0LNMt? |{s۶ J& ?1͸7fYaP?0 HbB¼y X׿zUbqd]X8s\nN{wM|DXqzZ&D| :>$((44dtLÐV7cX6mXw}sE76?ĄO1jMOMݳk}m˦А!89)xfff##‡GF1 }O߾ /o 7nvvБ/'2"\35MO8.BT(Ƶ:3gg~u:nigvnnvn.4$dn~^Hq0 \RmD"QLt%kydtT$$%.,,N̘BH$'&(22:6?oQ*-\aX$ٲiCgWrBfA**LAHR 2H$t^ohX];==ξ-#GMmȨ4]Yu2q+N:Su2 Ϝm#EQNH(=㸚Kuãc陓8>h6OTizg\k xa(3Wei4 8W9No͉S+׮C$ɄBB(e2>?|p|f#mcKX]n7rX9yiz`fEdX00 Ug;].@* 0V&EۘFsBc' X]ь_zS>H*o۲hmx<#$MM=~tD"Ta\&#Vk{wwoeչΡ?ba*N1L##^4r?rg n;yl8 (vPH|vp\ǣcǣcEOW3LN^0\z}rj귿{shj4,WN~=omv{S˭.zGO,.ږ0YQY ȹ zA7=i[]z8ew8a;>Sm#c._AQT&8.e2)qTMMG4._8}Vk4͐$o&p e KߨgYvqf6[2ֹ#N0 аgnwL U:rjCRqFήECG$8tdfkn12:@0ruph hL3RY$X i `ZZV'JJEvꁁAT__h4,dff8;kzfWo6kudzy 媔8 8+ė;QҺcBo=P(e%%ESSFò%EEk,W.=B\.4m6b*U&Ɩ].Y[4͸VׯM-ujƵ6k(*\R*C֮-@QtӆY$gffgI𰰘訟uk."# JFFG S>>5yIݽ}Mee O _;fD&Ie-VV7i)M-CÝ]=ogh ?OT-t:].fԔizfS3@71q[mkkZi~~ָheKK%'%DVӌU*J:@ !>p`ddfEՍi4bCC"}}|lXZRۗ:<2W^Z2::xnIc&aV xIZHHz%oMLku:xdL3/YWw|[4E]jX hm ?0?OXz]8e4MM2 #6WY߉T*u4M- a\ZjL3><2*|*v IrfffzzFs,už W^_\\ho0~vb+ްf#qi)y9٫3W ̌ĕ˘'St?NIxpa`EQⵅAFni‚wA|Q8nA p݇W XcY`\j48A7 Htܳs˒KO8egfgǵ5y'**cbVhP!$!KUJ˲Q3Ͳ8TY%-"):wbJ$ ü0 A㸋5ñի2cB$}7,Z[!I[*T*UT}緟=?QK $B*T*!HP(eӆ 54EzЄ t];:S}}\fAB@`Q jJ,jEQHuuojv R E7m\Q֗BC1 e!ᡏ+*, (ZPP%x @`i‰ RɖM._ݹ9Ek/dgeElW IeRAQJxPݲyŚZss^9r ~?W?V EyMc2W[wOJ hUz@‚5 "00 k$Q(B n/8Suz}HH[` IDATΞQr;J}~C_ٻghD p\,gef;Qֻ,+ &'c n⊂34~t-pg~/qg6[r7NѷSYun{lWظLpG/4][we ??IسgaaVkY:uᑒuE- յksgq;A+! ^ߢ 0?~w'7|[+=XS}A_|A<{x<ox<2,xeX<xˌx<`x<gHH  A$V")R$奲]UgfvvnvvvnfMUWuS',mi4jQ].<~IDdd/_D"DS;޾~"XF$nX`|bRR>KLaĄPۧ`X{xJHҍ%Eϋ`0808bȦM%!%]Z]=$%&l_—N~  C񔷅RNLL\tz}x!(꺚j>Ik745~l|>Wcbƒ☘'tEэ-N+"9s0=jCX=޽Veq||b(4M{<H%q&&WU</n7dg}X2 z#Y3 z9|#||A'(gk>^ nZ @4A_׃i  VS^/D;S(XԮp8^#N;qzdt|ul?5 x]kiLSK+ 4o1(<7<.,w̹O^vfF~鞾~ӹj4s.8}|'b}~Znِ$%~#ܸy-mA4,.wzz/ihj}vO?zԭw}ت'P(U7nIoսa>+zӧ(jt|/M&(gc;~Ppw`nc'ϞP(?J-vԚgx;:[i6pYKW:?x10̧GkK>zB<{;UjǏ8M8#c]=?A?-VCQ؉Sg/]1WzvUĽ|0 ` G%`NC85=3PZ!0Fc0H645=~Eg ήO6V7{ ڥnV[[Z'RRnZ6.Ug4jT.x}ބxn;bbT۷mzrBB|TtQ$B($zs\ΒN7b]nC3i3oNNJ5UWUX,JJ" KNJh$I( @3LrR,0ii>HSYEW,Ie盙WJOg?D3s q<waa=q*2RZuKo_RbaS38gdL&p8 Kz}LttRb_X 8+3V[DxxJJ^{%> M>otl\Ǘ:],&1!A7A++۶%&I'ӗ M/\LKK---x+++o{ 6/V53#0r0Lј+v=L,3sAT$IrRZef''%bbv"~abX/)0++F&gYVaXE"aFz JbBoWV,Ξʓ4%ɔ"'H,iK4M__^DX֌4rArbZqVᤧ0<3;RR$eQx<<_$]m˒Ng0,'%&FH#jM }~ rEegftzI@Ѯ |>/\"YH<|D]MuTdd Pkɋ/(ܐ?;7ss}>Xb}~4#QS3$EeqREQHOHBr9Y*ðU='Ϝ~t:AB|lNMI:adt|t|<1!^"uTUV&F+F$%&(U`0A5WrrDByŢR#SSA-,T'AJ {%As^7--U$avSTX|!`lm[ Vb\5#-mIsqn鳇kji/[,g/|Wڥe)U.'H(H´KKBu5ccϜ{ (URT?8^TP0;;Rl83;R  Mg|yw3gs&U;2:p86FU;:gf{zL ìXFSgNHoX#cCC#]=jA\zM_q{qQz}^`Ի\nyYcC#>ӣGhnji?T^44ݞ[X_{'|q<**rciIT I::>33;t_LQԶ͛'M~w'ݿrfwoĽ|^o8z$0WXT(MM;x^ή)UPP*U+dCSd:sWh64_LJ$x2OWAbX֐$O`?Z,Zs5vwwܽݽ*UOLޟjlyov8]g]l9>>yl^9r$0WXT('Mux\^LttYVXm@f7_qh2YV,^ifw]흆&Cqgk4ڣO-w<}V703疍ƾRu- 譜Ϝ7'Ot89߸5;77=3?P'V,H4L,l`0;O6>:yh:z0gk4K3s>a$[a 8qfklj=|D ޺pjtcS;{tDJYu5խm4Mts/ڻ A:s> X@b_~c'N wP7\xyI0W]'ĩ6tAa޾0Eh5,O>t87okN~F0s垞񉑑{n}`X^ D %aqqq^v3 ê{[G;$7oߝonix=7lv{SK$[gK[VT-**K Ü)D 2H^|UT [ֳǟ V/TOA -k7F'~@pfvF .| @DJYw4 c6[zÙK; A>svߟ޻{W,f i%Ib3 eFzzPO_e셋Eih_Pܾ`hzpxuj!cTnɤVk98P-fmݢt:p'lfHiUeX,QySFJZ.2 .$EEENܻFNOÀCgfd04syr_KVWU-def􆁡e&&&&J'y` vnwh±uyB@(nٴX,++Bm[+˷EU^wtvqc*uDD_P>f-򺚝33*&.VV}[fF@0jMd\dx@FFW;""·n AYm a@vVfmes y۷;v2 ?8Ofߞ];*].v]x pNQX,+j+ؐ?>qb{CC#O<^Ѹl4y<@0XWS]aCay];*YG1Ll6; VQYKҢ[r!ݮRkg_4 xE>ErSi"GEFb+/]yt: ܜ jeN^70APZj*22>..V&[neXX(+ jSi¢b~aŰpI8EQ|>a(MQ'ǧa$˷o 2YLK[OtavW,VG/_/~v>1prr;{bqQ$, m[.]*D8|$IjtmKKx|xa( A0"(##mW]aܜ}=F,V?}|Qa!V=Ar` &r8qcoݹ+/-.*6[\\so:4MfZ{5.7Lzzl/..d,ذT$? 1 0ðXY̶-_y׿aoeO#Iua$m6P(00 cCpҏ~FX'?|3=-c4MӴH(\XHx<r8ы"(6*쩫~`rwܟߴq#(ͭmױ5 G M-^`Ã8w@ Aᒢ‚{DQfg_{nCT765SRi:z6m,qaw1 vMfD)=ΪֶHT,~)U{ʼn{4^X۶-ӳs(J6HP @*SAs\=aV+FS~u2[ CFoII)䁡X@'EGx>>=u-44y\.+a!BEwmhnIjci =r$EQٙ^KvE;`,RSF'zpIA~Rbnݾ A`C։EQTGg 7lJ#rrz署m[e.' Qxrr&&$8@ggUK}y9B DB!{v445$icIbbzDZ_8*\X,a)x;*{Qbm Bc\.`@aXk7o14]_JQTnN6 Wk7n{I I BP(0tgՎ#NŢ@0\z۰ltܿr_p"""[B a7mH@qQ a abqnN{~؛661Ɏ&0 ꍛ'Ϝ]W~AdiIt& !*,7^Cȶopͭm$IB 8?K<|ZOo||}{oܺc0,K$aB<9I+[^6Ţ@ 02:&0j9[lQA^}pO@yF HeEP(J~innPbbP pp64 CT܅q 0 KOOmln <./\0|;@`H(8r7UήEH$+/(+$1 J!f'"pci[wQDaabyp .(Cup{w6mݬ._A/R IDATܿ`09s[; 6V(\.77'w˟4,Ln_ͣA`>qڥk7nR$~}^DB<WKuz垘Z?_{\yp`ArDGG!0R/ b<9:}q/x=-m$I_[TPo]6b a%.#- 01yМWGgge>cz?wCrͺ'{]nwKkǮg EQgקȷn[5+++SD9IQMͭEkN0s`;R>_kGΪ_8K:|Uea^F9}qۖͫ]\AihQOy"ߌ|mܹ۴eFy"sDwOB}G_HLLxDV''<Ń?84u*M?LMNJ|[*; n_ncs %.s.(y7. > C닆OOMgef|UmXT*;:1}Ё?vP(b\AQO.E_6gjfŃ1lX(def;rX.TWUDG}mlټg HjCC<ŃiK!P|!!Irup2q AQT0\=q! A(G"x2D b }~U '3Av% q_AlZC#/HngO02::xS5>#/] ôwtvF p-f`pxtl;{xZ37>dA E}>ߣ{zjK|vv[ V۵~!é95J=`/<:y^xfʙs|~?KWiϮh44>2ْ`0ҦVkv}phd-|-m^hikw8_+v|iB~Vg׳0Gh|QJhg׳gΙWV>'F)Qz 64 `?5 x{z|>Y+Ÿ|0 a Ao9_?0qu؉? Gv.g׳ h>sOh4-1@yIM{ހ'iuu~;kaMH#"d1ч۷mpbjuI<9 L&s\,`b!dDxx^^NCcN߿wb,-I"%n'>.r9.IxQb:5%eu^kp^8h4ZZb  F9.V7 i٢"y<;}p> ~bdtL$%%%v.187mb4 ȘhF q nwdger\yeI&|>RRSJHP5;7OQ\r5-EQG.OH,VVNQTjK4;@vVfDxxkG݆_||RNMM2M.d/ܺsW*W(W.O jaә> A e):*2\~ĿO6[Oo_Q%bʓy<ޒN"$KtzZ*fKLLFD8NBahjJJcs 0enaAJfbe1^wQDQ45E hiIG~믽rnN7;NVVlJE-t|t]T(~fFs\VM(0 )JA?$I/P"s8FyXt:U-HOST/^..*HOw s ALINji۝p:=5Z- (T_'KKŬ Fp8 Lp:QMV(n79)Q" [XZ-IJJlmL|M#Ғnj5{):rQ _.OTyެ 8.ىT].k9AGjy2$v{өRkpHOh.\ܐ>';nggeDFJ\-.;)1|z iKADDKW/.g?1GrsnOKMEh4Ha85E IIjSkONN;2Y ͭm۶n^6 cLLttTvIG$ $I"H$(Uj RS+aBYY@$FQF,??r\҈a(j,MD?*X,h2".OJXR|9700$\k7nDѱ7o%&$t8ٺܘ[Vo*[I ׮ܴ[]?5m^@$Ţ_c~g23-|{4SR\xj:4}5HѕpeZr߸֞xGF[%a *;+SRUoW*U*E%Wp8\:h4G>{^&鋉ZXP`& l6;A2ꞘFGRWFxqQ~Lwo_5vBJNJ|R={1 H; qY7=3K ~ĄQ׫Rk@,K*j4ڹ8m^YIKM./|gWP hlj ڥ Z˶^rݼ,KTd{~) {ETaI''MNMj\.RڥgrE_HĝqI7nH;qL-_M敧&'Bf=lv\6`C~K[H(D[,^{RS{ڞՕ&ƭܜᑱ /fʑѱXo~WV v㵵ud9@IiDd Yq wA?Aܼub{٥fZXI9V&z}͉X(fIҦ1 361pfFzTtNΌwod6KWǵK Cskonm,ߎalwo?iOM{< zdaGO0 39yAQ x<ҒnDGE͏MLᑱoy\dJ}YY GԍwJcy<.X쬙ٹF8q/].ڻcjmWO/Aݽ}9Gq<J#E<a|>wOJ .\vEH$']LO^^^nm̐bfffO~|}bb|[GP 044D斶m[<龁AAFSrr~FXIQ㧂$yzi nmJ$J=<: RSp7[,}1ݽ}ٙ*tt‚8A` u93s S3<+|^WWott^oFP''+3(A4btl<';KѦ$' oߦP(UjƦ 7PpG &:FùxV ׿0LB|ܟ5,VkX!ת='&&_:tfutmXh2-,W,sl|bl|bM&V蓼GJ㱋VA\.N/ SS3Sr Y+0E,Oα|vv.3=2V3Hd]Mu >4BZYnݲyGeyNvSTXp8 `.Dښj,lۖs V3L敕Ąx݂'I\WSP½vtU+ͽ0ϧ.طgڮ^Y;*wT+u))ɱ2ٞ]{v)*H$ڲ p8+ IVl/YC$z~'~GW˕Fe11Wz`0H@ PR|6.b6ʊ,&"++sĄ9'gf"ү%&&ðٹy)4۷fgMr!:ooj!DGo۲`W]MՎ @zaڻ{аbP4EI׻P$%%T#0r`ߞuyec_1ͳ v##-MV(*kkK /\c. TWy^?ALMͼppڡQ@bbB]΢ 2YIqǺ@?1~rk1貭[ AA(Uj7m,QQGwVQ s:]&@d{Gk\[S=7E{v]ZyOy|_@dK[kT[STiUWc4e"aػjIMK/ܹtd XXT0 Q[S"uɉ+Kk[;EVudt<$S*Z ۀt޻agfrs*7lX}|K1Qeۂ A"PR4]R\|CzndtzGź\|>۽l4Hjnm՗kwV+T*auj w\EwvbIEQT,Q;tz} rUӴ'XZ0}*! (t@Kk`C0aښjAٕb6wv2>q'RS* 90L pܬ ߿gWRb$\oHByp= 6LNn/7hS?.'XMJL0,/#("edݸu`=Ǎ+ۺ׿WS]% S򲲿TW .ǒ$961O=% (/t:6iil,-qR)$]n7OuCءomvkaA E! ˶?غycVfҕanybd6{>0  H_ɉu|b23# "(Xm"9A00 D =js|˥.$y䤸X'!B`FEE]Q(,ᒌ anmx<9ٙ%erNMCgfd D'%& ,DNv֭; qH(-@ @$ qp Fh&I CF o۲ 0>>9>9~DQa{X$HO^obcl+/ߛ&bÆOVʊ@`{8jw8 $IW4MdΪ׿84C7n4r m6EQNO%&$$DZaPh^1{^E0X捥{W64`DF 0SE4x0 HaC[oXa^ `aEQ@0qNvVUe?, *_y酶y4M#(puy(vVxW'af: ޾w AGLKLH`U A=b6m,9ox<#-%%92"l^ 6pVK?ES}o/"GlYP(Eѻj˓xA\'"""98g}^[)fef  p8<7//{711Q(C(\HifF0{zk`pH @r#0.'--uzf|>ߞ]ROo_0HVWU|Ʀ{ce1;}ce[@rYr9>8q8Nj B!p-7~_:t0  qvR0p8ap8{56(ցca!<E]5-4l*- (*-5QTVlo0L" N'?RSC#ݻ"vg#33#L,njmH8Zxfz[w7wubV܅]9aabރx\P(D|)`0~$_p%++9>0Á>PAۯ^qRd=]=3sd=}c0 '''Ds95VUYqqEَe+ cwgC[_A\y[axB0L,~O2Fy\ޣhB\^QaPMJLhmp8pp31+۶Bx\.nܾfWm _`p]^a/E Clބ ˁ  8uq. kpoٴǰȡ|>8b8v|D,@ԞwKK8K#"8マam[6o*# ! rs'&M "ܼ}W0RmP J{I<CQ V\Tx{4Mc("'w (cg8u&yYwT_ IDATVܼfgAp_ݽ!V0=g/ԣRZRsǵX,FQ"˅!~JK:{88)q\ 0y{S11ihlV0 GEFr\dQ1)){\.a/ 葡ɾp^ٿw7IXX82Rp233zFZ21 {8}aK`e׿_M\~v'w^ھv~lbp_k+';3&:r6LCQA~?|8ꧭٯj;e6Vml%NCS #* @ ?HiIQiqS/siMw?w*W(ׄĄKTjuB|SQaxqa&xUwO㯾k+-)x𴪫N*ND{qIydD7U$́9/ ތ\E L)&[t(J%s&-6~rs4\3/o>-nX*nXBX]['$'}G&U*wiYEQ[6 Z\hZZ-0ۨ\n"[b@$\k))ɩKSsHsuPhyY,֒2aUQ>hm#I2"<<1!)a*nѼW¨ȈԔ%_46 gIZZillڸaIM͉q.ᢝrl֡Ą'Q68HDHpbrܜk@4aj<G]}ctˋXJV{Jn½Sc毭rl;_LUMWb|`xd4kӆHCGd +=)68bz_{'&'33Mfb9MbbM&Zqws+p|Z=9Rke ^ 8n|bBJ$,NO<=}||XaY6((j;qa];T꒲ ?ĄIJz{yOL0 3=H?ݱf[ֶHJ96]R~cjfbrR"H P^xvllS"Z##8(`pzz`,+ 2, %qf9(0mjzZsssXpUؽsjq`@Ѷwxv{PPWC s.V?fs};]ݽ]x=aN R$_V 0IJ'OMNJ[|N=Z)YDXynWUXbY0,s𳣻wmwwwZ:h2 tT0~nnnM-sdqGdS8VoZP$4:Hũ1QQ vfAbX,ra-;>>Ԕ%7o 0lphic'RS"$QT#rjU(B0$89,6 (*$8Hr0iD˲lY,W(CC+!AjfbbRg2GFB!\hHh< A.Sm;]/uXj^UWݽ$z;,^/+"P7JFH$8;RlI:ol|\ pwV*=84t9T~ccb]\.]yzFعccg/\ocg_hPFFFoo_S󃊛vN'OwuUwsRO>˲HKk۩ V\ZԱ/N*c/]}zThERbXRFq{zwtv,;44v9##rh:th2:tuX/N)-񧟵}]?S65=}pNN'&5Z-A*F"񈉎RԧϞޭ+#rNTM.߼}LGw}-mϜ .tN? ֯[fuƉSg>9x0֭uww0eϷ 7{®q77׼-׭0,c}/lHdC 7;;+iPV^F5?htFEFdg;-YYD"<6m [6%J鑑Qٙcpp"dY/)J8uvuF|}feٲ}5?7o@!  Ɏm7n0,<,,v ƍdm Tn޺ʮy9rSkǶU+RMڨ\顣EbcL&H$q ׭\DR߫.,%7O[{gXhw5#rR b,;886s5 88Q}Irҝ fS;5L^oX];ޫ93f9ne[6oJJLT~VfgV|} ϖ۲V:t8Bl2|}}֬p-MiimohLJLpZ۬6\UM\RjNNN2Gv֦MnnSw*|c}} t>VkOok{vgm v;9>Z޾pOYrNefz:Azn~q8Np? i];5Ȃ_AAAy9ٮ..5uAQw9KH!bkA^F;URV.6[]}#˰`[A~a~Dpp`_fF[8pC uw$'%OLh.M]# G;¨lo- W/V v{bB/l p ߸IS"I 04m1[X5 D|`ZgYd2x4/;;_8fu~K+/+.)1 z=˲F|>pp @(zǶ%VU791""̹$A8N0 B(}Ҵ){_}%}ؘb/\rEjb0W(dj93po|3`bH:ۀ0 qzH<t ߼=5uOF;18`9Y?[)Kqok=t斲`ҖKJUZ~`'FEF<q_LdPPX, Y|oK?_ߐ:a0 q4ӉBVao1 @nw VV}C'nYRJT1'||vsȈᑑδ)I9 W=c'^^ޞW#1 Bf[mV 9Z*jgE< 0Mfalڞ CG,*JaPAV۱/Nb00 @@&d2 Kєfs8 ]S[I$EQ v,o_۳;#}Utta+ /Ą`dd8(Ĝ-w]SWE$?y덤O!IBj5 i$ 6mў0셂a8"<,}势ߒI8MA `YaͶ~ݚFgW`VgrP$.KKݳkw $Izyz&oڰ:1HiVZu~}C#ظ~djE>>S$)xUޓ4ͣi IL]|?D"';$I0 *2buJ"%aӖAdb|\[{=6:94$I>?#8\0WDDXMm=EQM|(0$񙳯w{G+E /noXnWttvlۚ싓4^b9Ib0@Q$Ϗp'B(**c8ͣwl-q3$H$SgQGUV?(0O}˿yWFVk]\ׯ=rIQ qBQq,}MBapPP8F>p6$1 H:۬m<"80,:*-$6oxJo_+0kaŢzmO<ض|//OeGt>p@8gϯXxZj_ٵds8I?';'o/U@;L[Ĥ'IiKSKJ rѵo/I HYHH2?'#Q$=q #I ~ǣi:}mv^ ;k~$ܓΐ$I^]p'$P X" ++x48EaPI8"; z#"IXNuuxP p'8,4?ҕ"_PWU]c׭[Lг es$,40 ( VeX,hRr1Baxx0ag MfW`lڄ8A$ o-pJf?ߜ-Y$Iao IWWW>;T= a8$Аw êjZZX]k4Moܰ- kVS4M$IQ {_F`4:_@k[;M{<4MMO46oik?y|8g˦#{M&S9[6A6mB\Nv?&W !iVuƦM.Cʱ9z Wk4?aON/ {K~8ϮgfaKJ7mX'-)͓vNtwی?{ 06/"8Ns||ȱ/~_7}A_b +*2{v;TQ*UHܳY7ŔU0 v$S$?=Fؽc x.Pʪ;/tB-Sr5gBԅczfX9IK];`p.Ȃp|i:|oA0p(8?<  X  X  X  X  X  l&h2͞S"I[FV/ =}/"T_)gh6^_UU˲ul1?ߔ%Ih60LsKU+[9ohFWP5us6.Ģ3G50 Znu*jj exxO9WQEGWwO֦b&;9[DŽ+B7/`ǹt|ςh~ВXX-i0*m?0FIfg^T@knnYf8'}Ή_{u45?XXn04Լ:}D2(Uߵ}aKEdG,ðR*&ӣ[{{z1 suuyjGSsBel"?h$IΎ"j1 3`Y֊! IDATqquYL&L$9 !|[ ccV ''&>fJ[F^y Z;쌗웟 fcFmmmiKSǪm$=Ӄnw\VBP({rwdtCGt:vj )ㆆGF0qB" s՚ޱqF|w*f/r4<\Z~c﫯ozQRZ+k뽽<-{_ZmV5-5I_9xh|lLK[{tTfS(K757:'^hмz?_W0 r۷Tܼ!x<'  Xn Ӧ##{vؾƭ;%lpض];EGGTjaM#!iKSPybXnܺ.vʷ^ߛ`Y6miʎ8Vb6[X ,ˎOL:p|MϭVkGgk޾^useW,K^26>ؒ/,jM@@awouMjݾ03#Νʭy[ 4Zhp~)ZgΚzDEZ-qS=}i[a>qSS, <>ar7mXDzP  !IIa?ڳ{Z@HppaA^|u b87;~ffǰ[ wkZ^ݽs;A"wvvuw5t^ÑJ;/'٪>} }/l[w*B@u.0"" /GFqǶ֭[Yc8\oFeX۷t?ߜ,To~nڜ뎭U5<"-paa!0%&kI_6qSBDUMA/''U}yQiaAު lܰ.?7; _.d,Pg׎Ԕ%zj 8VN' GGGK6nXG$q,_jZ/[,N' |>)0( 㸋zzJRoVK"ϜGB^?2*_:00)xi9;x<0'`V,sss*E׿t&?8Ae%<6&^uͶmV!`ZbY$)AopVZV٬|>˲Rowk"#==bcbZ۴SSA+/swssfP(U@|}SS5m|0 ǜ|}}) >?pYq#$ a;HnqqQQ>>R >oZ!8;卿|t 7Lzod@80$I&'%^+.H<%X#Bhj׎mq.]P4mX83 |am/z;qaONLp)E+ð~}uU|~ʒ|u.É!6/a6oo/BCϞ(2V\˲Ӗ  .Ȥ^a!9[9T0 <@ppw0 p+3V 9[40a}ŝ۷&'%.L.|m޸Sg>"͞];O>GDdDn67O<3>1sSg;6;xyyvvŢUTޫJ=%w:q^w7@ǃ=斱j>q}kuq1]E%b(?/ђǣcOzJ<<ܿR6eh^Pi0 [bXpqq `>R-7tedSeX1L$>ߔ.fy`4:_@k[;Njwm]k\lsM#\Ze$&Vnf4lT&$Dzl۩K||OyVO_ܓe>ܿ{ElLt7_;{̦rlsuq~~V| ]Z,z|ın^Ziú16Y>;׺a~~>$H?o'a>>Ra^)HvjL @m&q< $e4; yӆI5G 9~vG4I|Q:<8C= ˲6m GfM$M(l;a{~ARBb5X0,A ceas0 #IZpBj AAy,AAy,AAy,AAy,AAy,AAy6LjPRWߨ7gOC啚F/}==}`E1|ٲRo/4 <#sX}2cճ碬S(aa%mSڴa F֝YhAyFx2 #z9ӷP(4Mn7[,)Iv}oy`aL&X,ohL_]=,&&Ĺ ,ŀIYbj}ln[6C;:GFI X=KSSݧ#$$q0y9)AYVUMOOXKfehxDP, !'U*ve٠d:sDhH3nFaFQp8U*8i?T*L6812jfbR5 T5W8y}FࠠАQ9h4 CJv8v@6855q\Y Km$IB̀lprRe0|}YБLOAA^HbyZIY?ϝ׬N;xHhhH,o-m2M}noټIVVx̹kV.>m4(JIN0^uM~NSg"##jjslnm ק緿WWק?MTdVto}f;qJ16>Qwvu*E! Wv""|^YMX\T|ݻ?x)suFo_=}]Rۃ $aaB|8dRYU^qB9kǶB7w7Oo 2"<>.f4U+zz N/.)J]\\ޫ(?AK] ;&'%v{A^m|>d6?l6s乸*u{GH$j&#}e~nPDxm~~]=30l8틌߱05esG*ݾeN AA<`$}[CGMfA..~p00v8 dah uNHpP  >R{|l qnn};Y4MMiBW Ba`~/BA|\,a$EH$ E"!aEդ(A5,4'EQVyifzap0 Ih" 򒘟y<=bcj [6ƭ:/}5\P8#Ak3WRU[x^顣;wWg;'p'==}_=dEIRbw*Lk&&'p`>zd[DGEO|.{5^  0|%W(""-?:*r޵ n.q3[80 08$!dY8h4 y,:1p8%A@Y#p0!qB pgY/_ lv>t%i)η8qU8]Zyv'\Vq37;ld™K4૫d5rs8X|>Sg"#"2V6`! ?8}AAn 02"k<=!.-39/1 oXKJcB]} @k ȯ0ѷHkJQV;=B"#,vfk& EtF3g6Ar5Z#x$%;yS 2 faHDp̞q>"Ayzߡ @P(|. C hF3#  1AA AA AA AA AAQˍ8J AA/;M$9V___  xۜo= hijΝAAoaRlgFAAQ08ZAA[`Yv`ppQ@  ȷDsi@Ag(BAg(BAgsXi4ZШ\n}ɱl6۝#m  ӏt޾aYl6(Lf@0 !pb5,|Bsk! r;bn  tz}wwP(Z-I ÆuАiקW$6[oo?8pO[O AAdaD)miikhl2MrB  z`pusx:˲!A1>>> q>>>M'&%&t|>  ܽ<=WX& iJƸ;:V+A0 peB(ЬZ n"hCs@dww\<;EQک)y蜚xH(BA䥅F+B|qòB pnaY0$ 8挢0 qBȲDZA@I,Ksp  ]o_PP[~pጴm [Pt  KM4  2P  2 KB#  _qܓzGa4AA '|a   5Fsz`8?a'AAC  s.   Ĭ̈}fjk`WW7GOgdN_uWv|碦R$Wޫjkݴq;C7%>)io7<<#O?.pԙ_ݙwa53^+MSB<@8kXaXeNɵF/~ey)0= E IDATPAh1_}*.u>h&b˶IKGA-qgX-2BYgZ;;TjX,ͥiziFuww_,G²s~+rB$ s$m6ۏzCRW܄ܸq}pP༧`@{˲z^_rEdDWj^t0?" 4SSׯ-9<m6OKKwml6OMO*$'f+nC6mh*4$r-R4559)(LQ$`bb22";5%%#}e[{{UuOxx';\]]q&FPUq|ӆ8>fo  7s 6&KW._Vyseiݹ '6_)f2Wbg9;wuuwmXޠ7LaZvTHNN(ʒrH<-SMBgrL7\)fZ3W_VLQ˖-m~R`6_ٽssYX,.]HimXf}qR9V{l6Zy_&B4,*'A)KMk5aLrbL`TnnZ!8,VqyU*4y͛6jOc~SsUM-M~덦?8@!z{i\qҕԔ%YYpUKEeCñ11...$I4]RZʮ:@`Y@p8ZZ֭]ySiٍĄ)y9ZZvm2©)Z5 DMe7 j ._Yj%~Sqo)w7ΘА۷ީ+m~m [j׮ֶ͛6nܰD74^ŢP=sDBaA-Mٱ}띻II wn?RRbBa~nѵFsĩ/wwt[M& o!UV~ ~3q^^}M[,OG^Z0ZqbmimOINھ0׊Kvn:8,KŠ$@Lf[&Yevv{/6~^LvIAdbf,W)q4ȲS̛B{kl084peEK/oi[ZZZ\X/p _]mF3b|V784tZZZ):Ҿݻ }}_=~֥CP_F޴qȆk C{v& %E{Z~6'$I^|y]Zz0Jn4/z1n޴lL_VeEE<nn Iwt]sz C{vv=s֯[u˦+W55۪]/-)^vlټ3bXNvV`0+Hn5oݲ留'qѬR劄BAX,hD"4MMP(?0$j ijO}108v}{w5\N䅋_|a/' bN O5[[ aܽVTPo<^`< ׮ߔI%o;.{jzf X,.^wNF]x-Lff׿iZ7ר?{u2wߞ[lv{3dbXԑ`0KyG{T (%Rry!X,D{^!iY,PXXT"}~qU^.4MSMEQL&ƪ VFQ4].OFج'My6)ڰ `$J uVEKK!ZrEKK`ضuulX$J" ñX\7\\Ad+aVq\(aWʌ >OI'N 9Px<(PRSSSe0G@ J8F{y^<Phϝx0[,^Ebhߞ]OLrErMtee̚#$A]7M֮y$IfEQrrssB0EёHEQ$%m׶q"t:{.:.F!Ib:N (ZPnJnNv($EE"Y݉Sgh@g)3LUk|Al60?8.233DBa, _;F6WFeP[{ 77㦦{<^20*IJ}h!XVmݲOJKJ hN @@ӥ%8|陃9"H"III( FՆF1#(D֯[[^^{H?r$_[dlV\  rG$ex$z@T*ApbAիW޽GκZ;,Rlv{[{M~'cѨfm jjI$(b96 )KAt@M|A$x,~4PD"!Ǎ'yy9(3` ۶~wkbh0/X1o0g=\TEP,D(ds 6`tlLVs޾̄ ;+{dt,-5ujzz ddt AɜHc㯽GQTRwteg)V+e%|" ڣ'NɤR4MOoqQ!P Ph@ x\bP=~   1be%%YEpܜ \" F}j`8 7_uNo0EDӴl,OєB.gE8rL摑Q,|N}r7^XUU?C>X,2a|eJ,=]igus֮XЃ1MH$FF^{% 0 D"6 ZmGgwvvBAMx\Mf I,?p:hzWlv;Mk:}f#aSR\rJJ|$~-BrfOL|U^zVVVd)3;:#ѨB.߼uÆuP`4 S4dA*+*,8y\naj*/1+B>x3 H8AŅ.޹X<^6A߬:Hrrn ("J1b#!zt4ǥizzf\r ̌'$]#xCuVb N@`? BaAA~$YzU< kVpʞ]u">7::fTyyBr-g|>d2Fjx M__:扵#4FCe”\VRqL R"CaxJfs2$lZ,, wEhXggu:\M9!55533#33S,I%HTZR'(*,_fuOo4-//jBĊ 2ljzn{m[kg'&8\풐$'!W( "6].ɲ{a..*Tba^PF.+//77' Hۙ;Bt8} òbT*U(\•LArsr4jKKMqY5W(Xthxd[V&qNv6  ggg#0,d2b bZ-I򵚙Y.l:>>'vְYlDzǴZT*@1 WXXκZefP(p=r-A6m8::FtRa(S %ESZZRVZAB.r)i\.12A`J%++*== p2 w ;nP(P$';@Cܜ,0 jú)P(? ۳J@!Ԫ|iR}l%I2Kd,3-4P#P Hb(IKKD|>?-- U^l6-jSbEs dǰ|m$EZ cS3#: 6lX74frYYJ  CCX|\.Gdrr0_(/R*)bMh33<>|N7M֮RyܜÄ HV _ hNNvnnjNK&kו8[rRT&D"xg##:3###=]"" DRTX0:6.ryFF:A0 OMU61 R{?S>P(? Gۜ;0aD18ɩ)T74)O?ǃD"?`wO^oxWXg5(˕w=;b>кkVTV<  ݛH'ۗhǓ>g)I>xx}{r^gZmv{sK$%eGmyd:{^y@ǬƳ\zeVx\c~?TL3D h,I߶Y,;..Oz{HWVV|/"R_mZz,Resڧ-KŒe=m@%Ņji 8XYtj5O r甄BOZEWV?m)h5-2#'';'),'%Y\X,VQQӖB.O{f 3?-SN$I$I$Id,il`t./ KfF%˯^~@Z)u9ʼ,x(زwW8dah4$I򒙦i% Hv ?r(Ja(Bp$A\.i =2(0dT&kr(K  |}-Δ$I$I$I,H*XI$I$I$LRJ$I$I$Yd OADGa%H$BWOQ ~haGi: 1X0++[П_g"XvO$GKdx,p񓧯h|HD/X3_ZmKAmΜ=snN?66~܅-Է:{4M;VT0Z'q׻]tͦ#'lz}'NWfgu=}-w'OGKF&YGgWgWcIݜu\||pgwoocΜb'ΟbfC7_x8z284izrjz0<~c6\>55?0xmZ.>C λ gn{>jxd$ﵵbq@8a$$0c Of4}͉;b]R)2=3KӴdxS[ZCp˝}']='Nivtvu'eǷ"D"|Z`^{A+**6oQxh4ok/(Jo0z^X,p9ɩi45ai*a@tmիVT!uhDZUmX ?cee[6ob,Nlq\9iF8z"';kSUU ,V  8k*APTAE5 Tybhd]kWt[nCmEED"FQק̰쒔Ba0 A nwaA>EѱXtX<77G ts@@.OSffR5<2nG^n1&tt͑|L$B@*:P8"R =^oRH$lVT(41Z셋9ׯn786;XEm흗qj}(~-mC1t$us@ <>:].L4EQ6f]n*Ñ ɩx<-tBɤ3 !TC!ITvvAsz=M̌ =8άNwaEeE^^MMM Zpط[&++Ϝ;زy`hnCcjjH!wtv_|^aZ6LW 6IY$ =Mvfs8̵Eݹ{Ñݻm|ohxȻ N<-Uo0q.CT8j`0xg)36[Ri2<OszjUq{<~|pϮ%l6{iBЙsi۪;k֬jM&R*38lLJ>R* |2fSe6[NgFFB._zĕ}{w޼ݼgNPH|gM 0<2o`0جn1B<#|;wId H__ɜw8`?l}Cc(C33Dtvvuwtv$p:gfg/_mXHOQTӭm[J\$%CQ@|z:ݼJ,"CQ0Mg?6y3m"JBOLmL%d IDAT]ݽ$I:YW Fm$I:NnK,v,|^/h@p: F7[Z'&&?>tl|p8|gf-w&&hrsz޾`(  Õp8{`Zzp$.yK@ xѱ{mhF:IWgftg_(.r3v>(+-I$ѱ‚|&VD5'; OKs8]xl(Goܼ '&/C.pxxd019xF(kkq8i G3?ol$Is(|8+mc`̹ qlXn[{<,bx\, ' <TTX0<2F FJic'l60q{mLF &&&^'NY,֑Q$>߱~93d(ohx_LFDGgo_}k7n^|ڍ%0AӜG>'I2y9I? x< F3}貒֭Y='"Aݜ| ֯D.=QtsK^R?== i嫗._t˭(/#Irݚ۶U,|-xpnٜjeeͶY]vv֪+233I"b6T @$=]ը|XQY14<[^Veɩ锔M^`xk׬S3cc̅0 w].H|̌NKKeRj5陡q).*+~$lƪ7n`^P8Vddoڰvj57m*).& 7oژ4e)*,r]J"LNMYhxtn[ ׭]=8<<84\ |ڌ`0419% 8l6{֯C` IKRRgfkFVR'rqgBP8Tf[V~Il<9lcݺe8E }>ݎ[Q풔-7mXќ6\ "tꕫ EHRn$n޸vjDQdeyy-To$I=oasszHqJCBAXaڧbbũRdRfflP8E,^f5ǭ(+]n- VVmX.1^0 RI$mnw$ dKCr}~ɼqԙ$ߝWN322 l($ɾٹk_;!IIչ.5p\G(:j2}Q`v˝h4zˊ f*ҋ//b=3Nh]4M\Qah`T}bd~" id6W21{cEy,7^a~o7_/))[o/Sa 0 A(B}mϗk/s8s.7x57'SK隭Q6heED%0 _~3;[ntAxxdd2?#B.p8̑T z-֭]}D"p=|>B,z^E1?.xWatX0 4\Tf꭛/^RYwN//+CCݢlӪ{XS3:ݣKK7d(J%tvuIa8 ң7`N+aA0|p(Fd8|MX`(Hg>oҁp TVoFiA@E1_17^H=u;9{mX  h_RVVzA*|@(Ш[7M^^.̜f/Lx<(i9bH$-ڹCR8qef|]$8bzV~*YHafmP8zoGm U+VpſvSh2}t 8''ٜ4ly#A6XCG>ONN"`p9O 2sƪ/{=w>;++%E'<~#%E{s.?(bT*Y,Y(fN!Œɤ6}ffvs0sfNAbNNM3`p8x\.A\ 6pfOLOL|qfNMFFhtkBbq| E" +*+Fk5:uk!R(>EefArpWTYp9F8vvVV \v58l׭]CTr r\.7%%A˺5kG>>  _mhd~Y,VqaO>p9YJ%a|tnw,|Su,a(m޴Ǜn5,J>9Y Qӄփ9'R`p8̋ sh{ .r8|qP8 MTkx<~.edcz؉S0,/).zÏf8HWCpp|ͪUh-wZI\CG (b+++mv{|?B0bu;nz~?Zh' s8ɩ?>|hRޟ*c<@? G6) ^Rr!EÜ ½7V=zI,aðh4J$۷U?6@t˝Vf$j.~LPTÈ`H&Iyڂ|(;\Կt>$3Y-ws{w'ZZ o R5p8|O?>FEbP&&n5~ /.đϾ޺EQ$y\^a_ kU/ٲOj]F<*3IQ/\Ԩe~գqgH22>YcNM^wL>?땙ʿfg)cxǴG ׮{^ZM*`y/L9}<+*-F#]ݻvXWvl߶_?mjѱή;6嫵5a"싣Z x Ak׬ZJ*`NjWU 0okGG^?dY,X? ֏Ւ`- U-_;qҜ/u9ʼ,X 3$I$I$INRJ$I$I$YdaP4(a(Z/:x((X~2Kfiz}!) 㲀Nc.GX V<s{bߒ;( }I-/oS c\i:H,//h I[_'˱Z̋r,ECf;yܧ?2rrFcnoyyRe0[3N.c vKd9VryqY%07,';S$I$I$ɲ `%I$I$I,2I+I$I$Idy A-Ct,[.yi q]xZuDQ @:FӭMiz7X>ov3st}yd4t~NgtvwM2 EQN-w3.]1YKĿsbmbg]8~t}uϜ Á@ (P@tw8(a7yNH$poz}}]=CH4At:sԙS^ҕH$JtwO`d^ [-h;:l"12:d̈́ÑKW}>`tlbOĿ gfϷ#F~qTfphn[AVj}yZZaA~Ͷ0ZaX$INMϐ$x^nB4Oi򲒛MEcM[ypd ]{vib9.AfLTB4EQ99O=>la\^ZZ|VaAX,x}>( Ix&Y dg=h$z% N$3D"Q8҈FGF(h8AX4|t}>%%E.O3MDEQE=O~"x"ausX,`0(OK[8={wR*ձY,Fw8}WQ^ ȓ EQfDB̬.0X,>5=MA$~`` s3I:\(^D"C^hrKKO>B?0t¥+ Nh,/󣊴m[[lj"#;K$Ade)1  1MAH%HhX`48ᰙj<;0~#N$7oyښmK`(Ge2im6xjú|lq\lU$IK%>OQbu8] |I`ÑCG> sVsKXrr9כn[9MG|GO̖ &a:8<' F{wZLYz^f:=;{Նk7zzއS: އS?ڍ{mΜpq4M7i}<[B 3cʠ`8v7\aX'hhj蜚4M8ufddp(/܁+Vml|bxFzjK$1#By;xw%}> E"q hhd ȑ|-sm(cEMl}%qP8㇏|n0;{'Ng_`ph>:g|$|nN‚h,5j 5Up8jIKssMn$x4 }>LlD"=>>>Qp-H?qzjz䙳E7i؉h46;oCwLJBwޝ~;nkkoz̹w`rj_|{mX[6ž8v`4YE3e#c EEB=ixѥ1:6ffu/^v8]N ww8FFon՝=v3ΒAQTo@8>}m4MS56>QPhJzþ=; \&֭]?tk"HnNN_ $I46>q)s]nw"0ǎZr(*(0@GC=ic:::̕F FșF+X4MO|̑T>77gx^ϿW\X;4f<>sl`pvUQdr}ãcYJκZݜ~fV MLLUmXV wvUmX_i@?o54K2tW]@۱} :$իVUb]ul6W_>aX\n6[ii,+/'`4fc1 3<եuk,Vjc~b(JT8Eجtb5t`:R Mg]$//?- 80 CEp q  @`F E#~F7_a)_mBٗǘZʊ(js{3+0 @''gp8~$IAx<A!AlDh EQP( > x |euW PU& ڵsǙPVbxtlxdt h5꛷n?;-yX 8c`s~(Az^+cx<b?7{4խ<,ލ7\mAժMUy VFG4;++ܸy _ƪ/^ yka4ŘrA b^nA0SԃįbͶ_;Uoy wڇGFh?{s[D255w8H ?k(t4%E ~nnNR,rbw=!JW(?dhj$ApܞhL"Aݜ>Kq8']%p`0b8̌MU,.eƦ-ѱήҒb &Sw:JK1I3JNv@ o?c*[QYggg6399]\T _/%cWt=n4?0ru]pz^Ӊa8 |0 Cw8{O?_p`0an!^ym KܜjwjTH$?75= H  yB~`// 0faY,VeE}|6EQ2YiIхK/]PeWEQHw\lkid`1 !X,AaX,<42:8<_kV𧟷wtÛ7VkWFcDxSdeewvޢPRRly}NjbhMk7>>|H Ew~ȧ_aիV~ѡV E#SgbA[6olIAT#"pE8!bX,VVP8YJùu&E1[&OKK$BEE"afFH(dY,k&9+K (]rr+H0T(*z ?7??UwﵿI0tjbx\*O70jciSOKK}\Y\'E,.-.FbhYY.+)0 FBD{b֬2̹9Y5WBmT HL Fb\z-QY8Aa ØkE]Ŏ<}%\ p_?ɤ,J ;wvtv-4Ty]=-Fbrylh_&>{N+ER+WXm6J W {O]=)+H(LIoXIRR p|>>q \˥haؿwT*0Ĵ6MMMOk|>_ctEE, 1PzG86C8BPE1@ #c%qej EѽwZVAp0'9w1bV}۵MϞXl0 `HR,g{O0_KK_t N׭]t/{Ǒ%̲7zГwEy֨[cݳo휝3N-HCmY{ҼIBEQ)8ț7"#nYzeRR=U=rN'r{<'o05Vxo^7,,S9kVAʱX]`]PrSgZȣ =R0׏?/JJL0K/W6 Ug˦322 `?q"'e ?|;cD6ATV0m[6@wO?mhG?|fy?+1[g~`0>3?(A|[]s!3n Q(UN `??,F^LdiiSwdfdP?vF xFZe5a?(ߝx2z=*:}Jr҂fZC힑1k3-  +3##=-֕d>yڵCGJ*@gힾ];wM$$$L *j:ݛ]{{{}zK׫vlbv,^2˜|%,y0`uʣ'4\nOâ#w8]qia>A:)iʁx?=NDϰJV.i?OfcF٨khhhhhhhhfhfyӲ-$ { 8H0l_=E$@>;Gpn lq|r]Կ4'yow} O}pBĴ,y . л@np=]gW+*=wB)+@,NL<8{1J_ Ͱ$GFMfsGg/?0OU5o >߻_o0P/1V_S IrCJ |>_m}Z{JeUu-W+fα1>M*rVT*FKT:^jL^O^6hnB&I#rۉh4={r~ 7[,^ MJ008ׯh*WQ_%AWU ή#446=ʫ8&@H==Jh4M{ZQi$ɖ֛q=qL&jI'dAx^l=}kb~tB&+v{OobtՊʪj$5MM]}qT,|W$)HWTU$+;)zX,lzFNXO;<|_>xGF?:`bBvO?{?3ۭj  |d2B!W5 R$PU篭׫_V6z~"@(m7##"1 |Z=FR AR%N'8].PP(YtTjuo_LV][z/M& ZZ z餚v˥0 NEQ>Nn5uԥhnis6`4Tj@`6"V0LTML Zl6' lv;a2b/@D_KWT*u P 4Z-_ t\L.[~H,Dj 2B(w=w֭NZ 7Zo E3/P}ѱыhuU57nI M7UV9NRו*UUu|>FC$CTR4 RTz^h e4Ysݒ$IZ,@h4֪B 8N6IU NZph4@WW^[n᠌6s2KWꚺTrj%FնRl4 U:NFVk* nP_nljVk4TKxӍۭVRZP*E"X*z/0LbD8@׏296/H$^lvWVr=vꪚڡ$:_ X,.C#AՊbǣtc|l44p8.]r8B Tj `61!E mvj&Qr/\d2m6P(_VVVZaة3S ,67{5>*^̹ y9(r8H qW*Ur/REf "VRlvTT~ZCW5)J>_]nWcSb=rDX ԃjlPMR|*`S I~4Hloe)xgw>Fŋ> uwjJq\.W R:Γΐ$).]Ng[Z^g0 ;s $I^zoqc'N EbO9P(`ϿVQ%qi>_u{jx~~K'd9Y F 4Z]ZZ1 ݳh8FzVk\ z}Noh0K|+$I޼y3~L&0Pg8Alew8>|Zo4w~ =}{;rnokoN&d2wd|B&ZSөT4Z- DaA_@ \~ZB+W(J%􆁡۷WPUJUdddhXrpni "aaIIII\.wVVfjjJRbwIz< rsx!_>r?b_o_o_+H:y,bxm}C vx xޫx6nX?5 ?O$fs8 )-sgphxni ---"~ҒѶK˗0wK,SRR6o N+"x򬬌1~Ww%:z̹ z=~IHd2YٲicDDĆOZB:!q|۶lf0۷nrq ],&33̩))'5%EDGFD@LttaS!0CƈB^*TJ=vuDDxuO$ZPVq‚{3$q$ rBC].WzZ*$&oߺm['guzCB|<L&HL :z {?yEQ.;y֊o.Ēo }ۖ۶Tjeٟٱ}S֖11cc޾% GXP2'b$`ߏeK7_rrFG:/-Gd2 e6[.\uz=I8A| 0 K핗ٶHDՙJؔA@`0 oOH&xwx.;}/wz`.PpN Ӊ b;pw{(9l~?@xtO).GDo_NvdಥK\.5@v33#}͑|08"8?0`䴴X%Ԇ&$įYh2~m7{7^f@Eλz$l6ÔdQXlA(EQ Iܮ˖;f]jM]c|@A -SSIsKK~ E$\.G IDATwR\. 驩^K[JrojFzg`2LA*eIIw:n}q|zǎm[; aM8GGEkVo`,֝"dpNkjA֭][USkro+ɦt|>  I;E~460l6Ǜ3x֮@\|YKMF;L @ uג &tZh #ICCM-}&*22<8ҔJE Lf\ohhl0}q۷d㎊(M2v+#c(@ u*Uᑱ3 tNwٞ޾4IB)h;[ * F#`^^d[WU`f8x&r#bֶz^@%s(@QEP-[ ;*r8YqXe@(#55URZۦ@bB-(gXQQQI aa\.,Z0_TRX2~YJ_< DFF&;01[,{P5 |iy[q{< MBSSS8?9b@Q&|hJ,! @((Z2X).F`p켼p65&Yp\HOҒ9BxdefH$˗-e0q$Y3̅ wsa087Nx*zV=2(N>+U511(mhjtD3>uf\BQhWXF}/t׫knQZR˯4Z- u̟'Nٻ/B3YL/vǶU37ou$'Q  [1&QKH,ftt;]xCûWNsyŲeAx1Ŕ̌tjYj%(ܩ%&bA 6 H b'&d*żSU*離Ʀ,Ca1YII[6o|""2 /t(JYTu1YT6Qk&d!Uc {?(Wzp-=`BP$6&Wԍ7vlJu&rkhFJ?;p~>rfc'wBC`r8|& UH,xi_{՗0 ;uܼ%A70̍5/MZOIJѷ\ޯ  d?84887_N~6}yٝOSK?+1[g';z3;%=$АP(~g{o{FFF7'_?lGh 8xdۖMYY?Bg0e?gЄL=uGw{禆k- HqQA9]܃R^]5S6V!Wi{-VDGʹ ?weٽl2O/Q2ţ'Bfhf ,i&N~P!f/6I&7|SpDhCg B&<)#X%R$:]UM]uMJ@`phZeՍVq8Zn\:\dVY50847Ə$GFMfsGm_.`ڟmv';0@$$EbIk[ZqrQ M7drȤҌ&Պ>IPI"񷟫e/O^~uqCTVQ1 |l $qčV}oJ'&FF;{-V+٣7j{?׿p?'&dBf :pB1;iljjuN=uVw}asK[a~^tt4A*q$Ujj b88P*"%Ib Eb6R얶^WӍK.h4 Eb NS,h$`X7[jͥ+רKVSv{dRk4Bb%zpZ-+*VxlvNȄB0\T "ApI8FKyjVR{< jfjjuz<^oׇaxumKW& L.?{hcS󹋗L&3las-WU2A*Z,PHLf$IM&HlwةjH$Zhǥ <GjQͰfju=VS58SJ4ꨗu\ѱ#O5 VԹ$kϜh?߻pJՅWZϜ@:4YvH,1 nx^J&pX, &Y([m;xeUyVR袒u\z0J%i4ZJ-~ͦhb\:jqʤ`rq! J9v}|plR/\O^ov8tdto4tzd\ Tlnb8>08f::o7fffx<8Ϋ׋ #=hZ]K)B$outƨy՗m63g-\o%I:1; KII&Hbhx41!ai?|K,"GDD54ege.+_6VvN['[m6^/?&/x#-\Pиa:Ϗ놆G%V,?rD^^nbB|RRbФraqW ;$IgX7$yG)R^]8d&ISQv0q#C#YY_'rI__^иeVZ_o.oXXRU\T$~ok_z~חbc<r#"#8fKbIzj* lAphxtU#c<WUfefoݼQ$BYU]y&iX/-t"7';' Gnb$'ch/ O6E |>_SsU+N9}ۖKK&t:}ET999 GFө8VM2.ݲiw%͋|\'=--22  qqy_#&NVk?憆>zd 9Χ֬x=Kl߲5$'%$&|z{0E6Aٱm AꐐЎ ۣT Y ?0OvOYE> .f$70ԚqyaBCB}fdZV$))c;ٞ?0842MKK("deBdyA{{z4ZV.?pHbBBTd-&drsjuZX208d0旕abҚzHJJ "ސLŜS\4:gsEzJnHҲ-mT̮?`^H2>UHeZoJg'd5V.[D&W8^p9ݽAS#`~YSsLhu>ޠ}7nECfeUQQ. fYY1~Dxx E+`VHH,+(˩`dt,6&&7'{r`rJ>~876}o\nwoo_\.@$rzwj;Rv|&.71!Ld/l;+AC#,^1L<^gvH$;m6M]c/+lk8p8JK\nRR`(_8:* x<^bB۷)G|\/6F I مa" t%&$lߺo[tvݶX,ݽ}-XrLm6[B8;Vi</e(f)](j0 ƮX,P?EhX/^R\\d4&IAѱ럢⌎rs}<^KWx<@{źuib|>@H˃F d## {z{BE  44d2f]}JNLv : ᆠ(RkV\ONJb2 &|n g ??|T?Gmz}nl6 EQjx EQ& B$It _}y5^, =vCQ4atPT/Z~kni (-)oLII_v͕k8g}_!T38> B57yV,_VUSx&9\$L`8\nX,C$|Cqpx5`0JKj###?Պ@XVH,Yp~Vfl\k`Κ S\<&‚UյQTHG9`2prIrv `>e &r> Ø A>v$b>ATMo_swm??~%J 71 "#+/-YVm [7mJS# fIܹ׫k###"## $$$>gee(BeLmr8sjA-;.9lvZZJV-BBB$I} #?iΟ-7͕K'd2ZC/>>oYy6e ?uf/HIN%SqdNqXx>[hᴼC22:b.,xQQQ1\.瞘Xӡ(>t`jq[It:G(r8E[USf̸TړSdrEOOЉgBQJJZ9|DwO_R)䤑m(w\X&dBP\\T%sVZhfFJY`.GhhHRRbbBh2,$d|ݷHKƟZz2kL檕+ T37/Zoh^]Kضe#ǎ;tX,֊eKwʵJrr>6$ɄghxnKaW**R]]VN;6""A Hٹ99gfLJOKNL]R.zumWwI%sZh!_ ݝ]3"stT;]Glv}}kW~dŲV-2E ikv8֕ {?(SORn?̹ zEؘ_}YeKc`Mʙڹ!I|zd@@-8rkWg0VToaU=2>bҒ9|i 6IW9s&yϾ/Lʹ29|o.dkMq˩+W,6 u }[Av{|wɗ,fϷ_|g?Cʤzw9 W!p8w﷿~'&&ð/^z՜"'\ZԚ5NVGjٱu˃|~׷?iT*&dQO>;lҒq.ׁ/[ZxTtZ||ܪ˃#`f ?H??,2!o2sнS#$$?SKIUMT*{W1PL& = 0U u8)1Z:']ˢhߦ`= @;{~2=?!N32u6z"! 4CX44444444O IDAT4 m`L3ECCCCCCC3yyɤ|~Ͽ?孠hoRqoZm}HT(]ntR]JH$u H2?QM/AC#@^bK >̚Y7pI]r=q,VTL&HT)8!Z:N7:Ưkhl"W(@OoVBii~ 2~Sg[`/ QqjϾ/}Ąn'Ipr0D$af&\n0 thnIKKIKK0zU `t꒓nut@fef87:\ڹ'BRq7Zte KWi:h0&ajFRS+7r!Xֆ6F{jui^ZmQ&Ri6[h:h2rNW2r8e2b FF*WƠ b06`4f`4R0LTCf /54|>/\b4; @R;qdtFs˅K-+tv>p9rDv;U5u 0ABQ+ $VLp8$Aa0r#I ZVk~l9pնh2QJ|F`08N` @FeU5 =h.^Z[xIkh<{f߳o(h4 / l?{dN:%W(f NSqB֨>_T @%W)$Il6\aw8ǯWx^p\5uJWuM2[,zZc4zBz*h"ҒbZ %+T*5u *pٻph:݅KWzH4-2px<HƥVd6+*'LngDUVU\*r8 7BBBcϜ7]t:UJo4*U[3"-#rg:4mXh(DFD6]{˿t_weXJՎ.\h <O"3 _{ /*,d2 Y,V{!9)`n=~ohK/M!>>Rd?eŲeYu Ņb83#FKk^nP4sٜ۷TQPޏxNP(/$gZZBotE   YX,x% k9Y)K/D"qQaAWrbUiHOONJ\|c' K/d 7';<,1D "N.(Ȼt'i'X$IK5j͒%,f _rRz_~W$IZ|'N|@(/-_"K/[z ׫X҉{=cڽgX2~354 $vp 0ɭNɜj ^lg;Nv8-lnm}~N , IVǎ[lvLtlAv@Z {yy$I \,..v>m,E O>eƬLj$&::-5f #';`,?/h/hႁAP:gβe'NߴqÞJh|z60.j-u#(-) |EM-p$1))YXP  9v^n`޾9@dwoի͕ɾ1r,˻MT/^P(w>#,,r,_Vd"FxSs?m.;0445>"lTu:_a4%C#k׬%cuQݽ}n;/7g̔)B(,,n!(2dpxb.-_rǥ *55$ D11љt\./Rk_4)$aM-284\.d\vuzZsmDxij;nT*U|y0z>::,+Ac| Ygvl7n0=}QQ ϛ;tv>gߗs$IF<.Ërx؄wl R.ޏ8^\TTd\*+Ҽ>_wOIa*v'&$lߺ`h=6p~˖Δ54$x{q8-6'Zݳ;x(ضĩ3˖@TT~g ð斶E +<<"Z/_;n$ K7[GZzU~^@ |@n$@ d2WZԢꊋ  44AAworۍ D lڰ}!3 6=]'˖~v|l6{(tѻ')g2 $P g 8]ͮ< 9g{Ds"I 圥4Jc>uCU[o%DQDh j:<sBxx+/q1Ut00iRJ!';+(0PєWTNMO(*--訨E =wyffLMJI} ^)Q M ΙuvOm eYIF-!RB <Hy!;iBrBy!%Q1$«"##/Zxf̽UQљ=#!!d27d͘!K1%e wS[Z[[SLW Ӧ^R/XU];mA9D!PFD $r $H<$w{Dxgc%M͝~ϧMMG8.EQEA4 !ATH$I tA׮^15#vc̿z:n!Ak*.\j4AAc'LXsqqAhZo*0!(IHR7)x|xIvt"6v߆e#6qq= .fUTT$eZzڴݺ,PT ۾)B|ĘhT*#PwwϦ7mθG:-Z0۪~\uIInykҲM[;pEFFtuw_TAleUU͍b}?Af ,e]:➂ʈ޾ʪƼ3=CGonn }ttTsKkk~[m8)%tR֖ 65nA$Y3fNh4pMJLlk?w-f Gߠ`ISS rvCg̽Y\}n͢!Zu2A|\ں}GKk+ 7;rۗ;F#A+?gCㅩ5 #">>~```zfBPա*JfdLcBgvuuz/ꉱAA%qqm·HP5oޘ(q[YR 0W"H\<|p(3gn۾ ǩTĭw^Y$-;Q8}޾pYO:}fV\)zr Z@tRbz qc;;.!=s&˲7 驩[}908%'+mUE`axc9t]I)$E653vxASӾ԰C:#V}\Ͼ>{K::;m6{nN EҚʪF3O5ucJR6m'HLLEB/,[Rv @@"-uJDD@QBHE A}u#J( H%P$$}]^W9$!EQr$)6G!AHBIuU#Bȫd81B (o$IzUSu#b^K_xa{{W~BñeΟ`oqMD> H˾A`jUB۾|{ÛxS%Q $IE X#cWR~)0wήs.Mڼ˟Jhh ?w4IΞj-Y%IB}J(J QG?V$Iqeud~x0W85W$<"o>nޒ$i!@#7x'!(BAl7-A`@)D HWi8(J$$I~u##DzfL2y2M!܈BxАG8u399Sݩn.nbܽ o]7_U(o!I$jT3*/ƗQ@Ccӵ7/[ !:?wά`dzsިH>D^j]nRQƨ)^{eTUG^#ѣ7UҟWkTZ|]:E$'IGO=!BqG^Fe+>g9eƄ{`}dgAvDv=a<4McGǖ1wrcdKFFFFFFFf ,1:;a}߾/G$8n$r/}6/aaaM1,ðwIa2u<E+V+)0̥BӅ/;**p\~ᶷu/W7 {h4xOk< $XohX,]V}C#2bOο5,^.(NBe=$[V_VJ5 FVy%醆uKW.\|p^TpֶvYfq?<|"@Kkۖm_~e[]}Boںd$IfE7-," Fc0cp>{.<<,<< @Bq0Z=: {߁ JAqr8}~Ն]->}Bv 뼻uUa!!feǮ}TԞ;qp/|r7~kqD^m%Kd4nf\.j<}~!$I&Ywݒ$I484448v3_j8]mdƉ2LF yقj?@sK_7~ѧ0/ òg/صb?wW^Qy]{Lf3ÿ|չ]U̫[Fp IpQl}z=n.nwpp+rsOo_;;6`  {!dZNe]{uzl6bTtljcxĩ|v1)iZVh  vfhhhcSήEZ|zz4 hkW<)w y{F;tXB|\I٭W֭ 8wRxX(IRGgWGg+W/3M  ΚS^imkw?ɏW"#‡ 5/>qɼQQ6/nYE #W0!;k$Ieu IIgz{|9~eYݱb2$7mپpVVUGDɾvfgWY3Ya%ե3[-.#,DG~_?~83'!nb,iS& MG.'IRMm]CSsJr9\,1!a[,Zi)S\.uk;ݭxJ(ŢR) d͘~"7'&v}<)itt IDATYmiqLtto^>}TT$)m'kjxlvj5x bnپ#!>)kήO&&WV,\0k1Q]]w_:~"OVwu~]YY288 ؼˠ%NqַP [FQ9$A "##6kXX( csקVSSxт_حV\.$ݵˎW]s.uwjdڻ$HcAp8nљM K|gmljIINX 3L MV5!!^y_>{5>ߴ5!>>0 `{Ԩ5jjECM]ĉO=" `Lr˴E 74N돝8i6[Vx.2""88+ h46mǎIOnm4&ƒ+@6 $8_i茊Κnن <|+/wB͙=jm ZT,*{tUuܜ˖෢.L 9}vV.m}~I`@_]fZ%&&PZvaag iSz!ZIGFFTSXeXv~՗7oMƄeK''%'}?|\nSS'KU.^0:*r֮ R*_|96w bv㏦$'UVՈ4qblbBb% OK(*-u M~eάܪa!cڴŋܽ/&:*@Sz{wtv5X GHrF洪ZIΚuJ! 6&VCñpiSӚZ*I```@흝n,/[:37[wL2xt=DFF[̹ nrт E¥oSF]][75~Grg\~O?7~b8|n-- uy!U LNJIzZZZ5 e̚YS[Gs._ j]޾8} ~l1CJA[n ^?'3̷**Cgd/֭Y_7 nAh5"OPTAA æbDEENҚ"**2(('1!㸒[Ȉ;aUWTVMMO[em6[yEev=,-FV* |'J=7*ح?c믅{{xАܜl.S6ɼlmB$uw*W,_FQO_q|||LtLpMJ DI*^YVkjtI&OJߺDwt\9gzHh4!!,]5cE)ڰ5^rWxu {k^| < Bo@ S `9N$@>UJr~{yWsf?(x<ZMB( Ii$U*I$xfd3碢"Wyw0;H(m~:ES]S;gVnHpZ*-1MPLMO|jTTdpp%N>rlRb7n֌Um@MMxJppV&8Ί)޸ ?{nH$EWH $CA ")5r !IbcP(MMt0"2<$8eKϸ&%9i9%e-sfLNN  ofMƴi M MSG!33.\F@dD)2e7.$ BH(=B$A A I"B! $EQBH!߼ކ7T*,wgfXP(h Է@QP( @b9BǯFhͤj͞wa% ]n u3ssfx_0^W93 CĶW<43c($T*Y% I.$LEjGR>P-ʑ$ @iZAQ =CgJc@$IJo)i߬E -۷Fxڮ6< !eBJqӪRFI!T#\OB+4P$\#DQB!EQyTnZ RBOqx̛3|[_JnT3dRf7i'E=3 j|k(IMzNNVDx_#=]=,7:e[ƣdѨ%#######3sg 0CCc^ Bhlj#< I:>>%Qr('!g`zd.7ccGǖ1玁Vvo!~`-p kd\O %I2&DG>lA  !e(%x(250d0~S22222222cl`Ȍ1%#######3'b4zOO:]]S=xƦ%oGB ]E%5.f[,ჿSofq !d=Ӎ_#Xv?+޸޻&7yOH[l6{Oo/֎l[ C㸢\Bu_ivM&Sk[_f3׸3L}zkhlr\7nT<0ÖZp?KO8μ7m?saݛlkimC_ fe``XVMDrAt:ϨT*Jݗzb>sye].n7n7c4N$I6bZm6a0{WeA08$IZ2Pk[KnEl6!tvl*1 cw8;9n_y^ų/`0|[ZC;vpp?WVV']7m~&t?}<Ff3M.KaYhw8E\.tx:(Jd6f/۰0 0ж$4l6\v>oχ>~çϜ8p⥝Z?~QCc`xذ{ʪ+WwgZU~WocY`4:Q]n G0LVvX3va6y('N,p8'Nzz(·Nv9]NSEf6q4L 0 c 6v]$%9l0eYh2rAD(lv>`0;PV^r8a9608rdRT'0 gΞ7G]IE;~RR4=4l((F+uow7d'3_JnwtO|iKOooUuMzZj||CG;  ɚ1E =~2&:F'OJ )U78nҤ@Жm;{t`,*.  }ǯ߳onVP(/tQb}gibllnN$Ie*jjꢣ"!Ϟh4Æ^]-9/p˗-ܽ'7'oU55Y3^3{֢ ݭl-.yQBCCF[?ヿ|;adL484,!!8887'[R9vbbllBBysEQnni3w,$ࡣ˖-۴e{ qٶcWJRq/܉|Zʪ޾/i$IժRX dfNQ][;5= ErrrsKKnNv[GGJJ2 **O$In=}*̄,s |6mמQ33t=='ckΙ][WȈntihlZzU^%M/k[Z 믽 rm'OJټm 7g7W=lQqY3A aV-,405=~fnN}CYK2aMͭm 8Iw٨wt ;::jlݸ[)zgAA/jNvVppEQ,*)oh2ߒ{XvSsyB$VBBLOp9vP IDAT _J'^RSN|?466B IXbdzaagh4$Ej %Z$!A OKϛG $p.L%  DQ@x[#yӧw~!77@Ļoox  5hH4El]KRR$)JV)BHB陏?[wRAtTNXx>A˔ɓzzjrsCiNOOizIo/?K &&YSSޙ:eD~R\~# @4&&pN< 7ޜK6mX!$7JL@* !ĵp+,9#F]d "uko/Z.+9)i̙+ZZfMJJ(-78MMkjnnln:5}Sӯ\RFj 8vҤq7(p#s;HA(ё~ț?{ݷ7+i0ow˷~>mj:@DV4MZ&IUhIT( Q xyډv8O?̋.B(PffLrB@4MGW.!% |)#;t?<n*ջooxM@9GW(.-p%M}1(0tjys@YRS[g2)ud:3cZ]}{zzG۽wj]{:ra$IR,['ON ǣFD4ju||Ξb#fO  ؉ͿZJKKݵg_K/vt-*);g6CQaa6utvvUUp|xފƦ«6@;08t܅AVDGFoBpppRRjP(9YMSӦWT.?$ήi ІP>) M &3ssȈoaSSIrnBgܸQ!@i٭a@QGEFڳfQ1 >.b @UZVw$?tϾOY,Pƴ+-OdrRbN7=3(J ֨ՁS AfhimjcZߥ K-KQ ż9s:::}G4uLt=n,XEu{<~"lҴ"7;{=/^bZ81v}nDͲܞ}jj뾡A;uko]DAg9܅ M*Rk7nVTVIRJt|\ܗ;w{y(755/^`̙ 44H2yҮ{ Qrw_][5O}nY]lbMM{,--a*$ə9.9/|\Ͼ>b#nZ_0sk%SLy]<0,@Jċ=BAX LJLTH $B$eu{<4RXiBC=E4qVyBB`VQ f$̓Tr|;[ K˰FP]}0vWFկR u.n[م<DQ.-+/U>08v+k̮nѤ%2X(qӖs/,+IR}C;hl0 #fZ :j}`X7?sNٱk/>5-Gx<3 t: ˲vKp:vt\fEA8f-{0-ޞKNkTWp:](tp9aYra =Xۼu|qܲ? M**Nټmnww\W8t%#Nj;r8t' lX'0!iXv$fYm675~GX;r9NIfNHfFr~_wێ]G?}v㦭f_?Æ/6o-.-;sM[V;nk sGV@;l6;]N@1 kXQf6]ݗ.DQ'+TnnK-_l458NMd`@b;13cZsKX137'%9 WV.7GugOɽu]ih7h\㞐R0&nF̌ʴ'br[ ,4 y]{W͛3V9~A;kmt]w 4Aϧܳ޾aӊ^@֪TY3%&ĿW_~h4‰Ξ{'_T8i`(T,VѠ)yU5IwkLT[_8cXk?dl444* 6ozϱ,GԚu_jԪaw>OIOKm76lΙ9chxQ"5CCCoGV zVqR;gVHHpo_ߕb\-g)IUյ&%&@=T*03O|C.efZIN2y瞽&#-5/7gĤyE~ྐྵP^QMMqq/^.8\.WQI?Ju9g]&))բysfXyKގ΂<PT+ FC~ʪKexi }&hHJJ\9g7D"yG8%M1cKu;t^Zd4VkOOoRb>aq׵Tit'ta!/!NhHH\lLIisf1lhx%%9I^)*i:+3g=(=CUཕཕ|zH͡izJ%x<ŋH H?d1s,z:&:j|D"HScbbp G$11>=-pO2!?o)Lssjj+*z𾊪A<=gNH%]]ݵu9;8ON/.)1 DD(54<.>\%@QFG54OIt)˖,R?0ⲥ͛]]:vEPZV>ib b9?v%LXX瓝???{tAtvuO,(HJLhljrhbA޴)Uյu Q 7ѨA)uu WrspBe362R|79)ZdDFUv{_\ysn͇?yjْ=Lxh(M%e/QknKoRtbcO?{o׬.]V~qm]=.yqP ?#IJlVfƒE 矓J >(< DRrD儇\tݲ 4$Dwtvuvu;Kp$%9\ !&&zs󃂌PZ^|ɢАՆcDv=`&tttfp:]jJ&wO 1;3bhm79Eť)r\\8I# r,}oЂ(rXW(ZEQB*rhHHttTyEhzO?#AF펎By^* r07mX,˂ jJ"(J ޾) M #APP9.!>nbA>bpfqh$|y7y;oo|(!*DBT"32D+J8AAroncc'Ö-Y4h"YYU= `0H$ҲSd2"11܅AM:c$ED,lljN?%9̹JBޯIr ?aZVGD Eǿ2h- X  "8 (`{&'%;A ӧ<|t&NXpaؘ꼜lM%HBG0==.3=M HzZ3gRN{}ِ`S^Qp@ϛC0@@Q0A18" Kox `Ja;oob 8IJs 1o!" a/,[drf~򱔔+@` m ~Y z=͙53"<\Ap."un L6S.\WRoޜ#G jn~b2@^nNWwfAzu-˱?:ff9wJQQUu-IQf(\?ʪ߰iKMmIoܶslAnod)}ѨW}68yZX+o9{䡎ή=xhOoLF&뙻S\oGΚ9^5+3 p CRh4i)=7 ]Wא~cN*UhhJH$1ճgLGn4RtJᤦÍT(BCC֭t._ǰg[qמ}&YJHr?[(R(cD[s>pRHHH( _}#z! bۣ?q\8qT*u=~n7,$)ףR*RtAP*4M4rMM/4$DiZq)*aű*swtrc 8.pHBx^P* ">/!%drrv/\/ IDATtZ' ^ ,er*A,+kܸjzd3r~V:Lz+rL7X?PR1N0J_x{nE|nؼaw|R8ylEe "êkN:={쬾&''͝=sb_bX[Wĩ3d ص')1~ޜǯYZ~' ؾswo__P)mLf\GdlWï(jdu׹ {_uwe`mAuze4&h25pG {eM  @p X @w?xEo$އr{iF/">vTa4-y~d U")P]!s'1YGwѨ!?0E @p X @w @p X @w_|40 SV^{Ykjnq8]]ݕU5ufi'IeҲGr**kJqu }$Ew|>_ww"˲{8Zk04bCcb+M̷ 74.A&AؾswgW׿kX?_3=\*4Ѫ4MوkXںy&)`A0M->[Lfs_6]]QYu[mf~E~h%)OVf[ iCcGE:D4tvuHr`pƭno@tUuA[ L=CԭjX,-7pZll6{wOLfe[Rxz{^ݖ:O ,=VRZNQG}zSg|>j۴eۧ--+63HҲ&R @4IQ^֪lڲ$)|4sa-[JsqE(RA`(X|Eq<~WA'O$vAzΜx<^yCðEyl?pH<>rxwwM[S;gXi$E3 uC^#I88v$Ų $IQs¥y^)t:UNa(x<z9%)Hr:4vnf-0 z}bf^dn&O<bή˻=_nشA;zħlljźzͺ'N^t@EeϷ|(xn  lv`YVL#qkQ%Fgbtvyy֮{:yvR<0׋MSnV$IXp<4޳$IQW#c}}X5krX$)aXݻiQ[VGG yz9z`bG3zL&-Lf8xaCG֕WTiLzIiYMm]JRRJrRzZqcU*Y32WefKү032f/-/}+7oH$. Z|AL yED-NK{ҥ2flddB|\.rDZ9g6߼^Yn\ku\$EB*%H||?1!=<,p匋FPBPL8ATn߹`4N2㸋.746)S G& 7n3kFdW}; E =gLJ^ήO?RE1.֨ZV^1>%j|J訆Ʀ斖X y?ht\]{uu 3OU΍utmܼUTٻ_74;~ w}ؽIeUMwwSO>!A|}EUuM]Cdyab@u_W(3O˖.2 0 ܅K?W& rd 2V䤪ꚬ̌Ąx q5uu l޺ Яoh.8D0pWhZ\`0(R8ir,#=-';kvCCΑXi]Z]^QI>B*E1]t雖6~C '~&<%B[k[A^*\^T\^ xAo A*$'7oNZxBd驷=r>Zu_ /6?ߛVdDELeKM,ecf623Qkh^-*h}B.Ǔq K5kt} yOJMMIIN|* LtMmGVꊊ*yU?Zzæ-A矝OR \$㏆dg=ڨq9 r,7';&: AAR3)yӦN{Lkhoޜ.TzOaXo__cSSFz" M@DxaaaⓉ -Z[EFRFilljjnIѮ PZV^8i "#i/[hۣV.^ UyK=?nXN>gy֗df  L:E*544ǶdgMXPS[9& \ll𰩣KK45RZ^.%"Hz{zƌMHoP(ċEf@f>8?7  -]C(Jսlɢ9f(:88aؒ%rsou_~j7hK>Z>ohl%#?J򙧞ysf?onSs 2i) >#% < 9ibAH h8@@XXhdd%u!!Av􈹺B*ff y}.0c"tUɉK/̸+bNL&%U׬6ltހbs^|UjS|㸪ֶ"|7X$I;ph4}c^p?qj֌8c`yqEQAZ~/,44^XGޤm𼀢2uჂP9N$n<&Nx?0(JzO  14ȼ9ٴe0M#~Fzx99Y蛯xK=(aZ"Tr}q\.JT*D*XEQ rsgLV][w܅矛#U13 ]5yy9CꤤDL}jNt&8urDLjjnV!pLk 5 P8ig,˸HKRA@DxOYH$q!bhF +W4\8Sv3..6?/ ?/6&.;;oK$PZV$i)u i(OIp2bZu#$8rSU5q~axA@QCQBFW|R)a) /D"`&<"2AQWG"/W,[J`ℂ7__+/OIFR B&q\RI$qDHp !%@Q@%YhRزuv?QQc9..C4A/^BDq<ϓ$9i҄A(V&rwY#s(7__E|ɢ}橞^ W#"7rQ4|;q :3Qq:v}˶yY106rWZZ[APdzs+Eŷ=vwݿ{~q 268T~/VjO=Аap`aǎJAFcgWWQqXJ$ysg 2,E1 r]ggWIYycsSEeC x(^\VV^1bM^76bH%Z`HK|hJ$ 㛚3o5}4jFj42qUՓ '""iNVVggWH]JLHnAܜ.* ( ^mǮ .&y}յ@vVբ=tvwc6cM[:rd{iM\aX|\l˵ִADjZTj41@23jS'/T Q.IoIiӦ7D2 9" XբNAB*ߺm]5fڔɢr`dHJ*l۱lJVffMm00z(ѴRS>%~A~ NZ}}>ðS 3~,_}#z!&f?y&'&NZpp\e b52JU!9>qw+o|e1zgWWo6~8tddy'*|/B8z~[r"a(sJXvlK-T*_~g"N97ḿΞ5c \azbGsw NohA(-e9h㸶aadjmkx5u_ 45X~HcYnw~%qAغ}[1[,׬WT?qkϾk1MG/?V(<_]SKQT{G`6[Zh[-v]]U]{q隵_(MnIzOwV'}?o(^Wuc tw$584$aX$I084M։N9AuEݪv2R~aZz<ޞ>x}Cp{kfmjnaF~>08x]O*tGd3gϻ+W>]v]etOlcYܜ,cǭߴu=\q3qO:|xoo_GgH椒ҲDZM  ܳq=^onnp?|c##AiGaӶ=wt~bl޺CM IDAT,0^e9ǰ  4MӌxK,9yG^OtLӴ@.80 0nEo֢<ϋ{Ov0 {xO?50]-.ٵ{*sWK?I|9p_nزuNQAhhv[~33[,b9|.t\}>f| h磇MSτ[}\qdn$ϿXVή?lێ]b,{+?xʾ[?%j+:ht(&ַf?(7p8/7nv=`w8or豓6mu\,ˊUO,9 #wQ-bIhQ'ilcnw'5u`G_%|,qk~r%tX08?567oݾ?jP\RZRVmǮWq.ņ<ϟ9wý]}o-m ܳ{Ӗ^o]K++'''&ďOI7wǟ ~q'N^t}G ?x dsf8wREeZ~?dxxXQqizZ*͖?weAV|ά7o˟?3pҞ/Y$I޾sw__J۶tHaTYyeuMmDDT*cޮ.4nf78áUkA^nXMսm.Z]][7XI"NsPfFzOom;*U]CÄΑϧOo߹Gapp蝷SV^YUSc2_{%AV^P(͙>xeAA0pg~3O.)ᠠ l4 u|JYEENvfEUuJrݤĊɓ&VԎOIME:3u{0p̹ҲrJ%a7o3KQ_n@jD% y뎇㯉 a&?08XWT"v7^p!}xמ}CR=!'Nu{<N;;$'UTVV&%$]{P#)'N;QcƆӧع#&-mm$E;~!44JJZ[tԸ.M(Ipx{GgNvSgƎ:.1!ҕ+sfPm#X \+ZR+xA 7Cpb;{&aqS'3No;`w8 T*mlj˻^D,_zM-xii(MM- '_ZLtJr K+r0|bA~Fzj}Cӈ\QU[aIY9<[_؜LW8q‹˖/q8 a ?&<::*W^ {a␐`(+LNN|՗ssF\d$4$xhhx< 2@ Z۬68NZ-; BR'_|E +>.C<6-u6}c{fϙ=, vM C3 ~[ 㸁M[  }n:nr'OhfMNJ|iR3#@DbGL>9H$RR"q L!Y(N,.)lde[\@Qz^ZpJ t!AM*( )!h4!eY1 'N9}3gύ\斝"Xǧ$_km+4 8((P(bb.]ѨS ;abK U\ZV[W.^DQ49)RTVnj0-USRDMr؉ᘨV(*o4"c*qJ,d * JRJmrYB|KWT*ePPS=fZ&M*rfrĄޞ&JFҨq7HD 3#*7:ERSϞzNt+7c(*\׶qF[PL&ydyEDR*a9NJHQU*ƫ[+^~ RXo7RtdrL*h5B*e9t:T*o(*T*0 {8~#E.kֶv@$#=< z9 O AD BCB0 Ӏfh1^FV`cc;(F={摣9hvؘW^\x&7\sHq?Igc{Lv{<^N<}jv7PقٳcgY3O(ۺc7`mrzfА$v%/ 4r, (rU|fٷw\RTXS[000xeeo4m|&h4D;sNgiY 8ϙ5s݆M$";3cGp9]J244dN8nLDܱ{Oqɷ`ֿo},nٶ9wbbưW1@)Aߴe$ q1%egL>qBNeYlٶlܜ]{UԎ(D/\,˲)TJT*DHJLdNSVO)fvh1F~쑇._-=qc͙pKu7 uCcf=~K:~ 7wV{wARDbX ,v#xAXņ  BV zE].񄆆,+:"FuDW9FA446=aa$<<'wx%,DYmА"E%ohqBp:q Gl6+JE__U03((fS*S\0PT|ؙFad2Z,֐`ͦP(p aXŊhhh C h; K %yБ8?p<(-+oXd];vaE᪗^\,d28NA ji.iMfKHpv4M*A\.`ЛL b X* CQQ׃5^B.'ǟyj \.j ^Ft8JD*QTf( J%HCCCBF~ Gսexm >~aPܰ)#==?/θ{D$I 2tk.uHAJbuvh0xeJh4L搐`1XIZ6R!Bgww{{ CCCB6uXh(IQVUPdt8CTiA0F0em6JzzNQ9].Zmق^cYc8AHtZ$uZM3ZZ6$ y(v].jJ$ߙH4 &sHpeYVVvEHA>m255Z0 hVtu7^̜f 2$8#dDVFVq$)B93-nEZV\.h4<ۧV׭%:/2)F.7S2r9& w"4#-5bL^'"NƦ+E:vp`W YQIپ+_6m6^}Ju٢eKS]SwӧN(߾sO\\w;Eb?{_G%9-As``8e0ogÛ;3 3`09,ْ,*ޜs>|c e~[}T:]U]gάSC'@MRPPPPPPP 3EAAAAAAA1P0CXXPvAu;Q$/>(cԝdȅ RP@; OggWgZՑqSUͶ!?"~@ إ6-B(k>dkD" bAp/d21 ~Ʀ;? B۾ xٵ7kj8 Fⶳj׻cP(\.7Fl6AN@ ]]j2Ah\.jjuBPGg}n fXAv#|7D%Dd]Phxw`09v"zz:Zq?rhع<|NWA8uz`@SU]8׫VwAtv^oX=ǻ~Ad%B:N>ѐpGg ÌFST]/ K}b=`(d!g(&aAQ4xAԡ`hNahۻ"OANߥlpj.ugj\n q\7(ե#0 = $JIՖֶƦf2[0#!şуQ E}۶޸Ttl,Ct)r MўM'`05u/ h4& ]+\=&Pl "%%8Actdd_|`X7+GGeZm(EǤXmGvo'Ϝ78x(qhB86ʪ̗ nGOV)bQCqEː/ 8pH4u-V5)'[#m_m?zi;O>;uvv\._>RVqo_~KJ.^-a#+=UU5$N.0dͿ\.loSݳ]D"#MKkl+׮_ӟ?&3֯1a].PMAUWVg?@ў9wБcov Nkvdk7D/J$1w>@[pt4 P&Nxт`0?dQSK{x>⫵k^\/+s>}\OoY zI\r}8F$R*))ϝk0tqg_/<@innٰ~mEejy'G@Kt=tI$bE1[kdNR*6ٳccUfdWysgD1x.7E ŗo!hIf?pA+-?vRu %WBl=wKᰃ_ONi4d7gF͚9np8~Oן:sg!#0X6Dl%b1`\غ'46$Cfd7N)Җ56 JF#&Y(8n % :;'Od00X|nw >s4ə_*d2Qhْ{yng57*47g7mX` /sŮ.5(wa8fNi4d6Ϟ9S=~vtt.[^T|xBqq|>_9vtvM_ofLA/3O\rgV^6c4H$2qqzQ!d7絵*NIN6M j-^@0X[Wb.Gӎ1^lvRRbZjJ͛ŗJ"SV=~`4X?޲4핫aɞ8opعAꖺ Wb$ޮwlBxCul.1GPʥCV,j]=n\*)t붒+W򥋗-]|tm3g465aƆu #;MrsrMlb@0\P@s$Eb0~HJu[Z>ݺϷ7ptOn۽w? %%%'{b$)\՗NW_viR;)W_ohLJL˝ #1"Ѣp !xYy=s DͭMקqbůL&CQϬH}K/Bi1{NRD[6[|&kнB.OKM!aHTǏ"oRB0Mz!.. 9&#_.\KhNOK%%/˗_X,D,<(?r\Km2NjRN)uRv6PƫÑHkkۆk.Yd0<a߸q݌ݽ=rtcd3{ur2z$iljްneKY-RSkkM-ܸu[Q ,VVq\,43dtuu765d2 dV@H6uJd&|y;iY0 )116VHIIG"K=pJ467OmhmkOIIZdaSsFqeӭ۶~y`(DVOrṉc3C3}`0v+V`0~ܳ`i?1eCaTh$ts&egQ&egϟ7gY_=JN7Z4 k\ysMfs$$''oڰN.eҵk^VeMol [$ Al6 T*{`_}?zaghŲ4dau sf̸zF4:BI9k^y6//wulf cǏRG`VT|yJa‚ʑpq29cL8 !H,WpH(>u<=kr;XoOo2q y UCx$!!nVU^f2ɍ_r9 l6п4avɖVM`]z%'=..s޼77`~{7oZK&ppY,6B,H$dX(2 3mӦWW&$Ŀ-mϜ|#9Dx<AB @(48A"fY,l6/hosd0 ytݍ[s'M"@0<~\VyE%AyQ\1cH@=kƥRNW BP(`0\CGy<^Su„<OT)rP{zY HNN5skycy4 EQɠIF"Dž H(0ϧh[.cxwP/0,}b &nܸ[ yѬ'NQ^eR yQ*B;Ga4M$[.]2M(rgd2 \RRgΘvFYW:??o\XuwOcSB\aXcSK1d\M1{&/b,]=y<"rq,H$"'d͹zFUu혌WkhPT<(돯0O0EQDL?h'NɤT**x\hJG  A`Ydy@1 @ʶ78N0"EQ:7r9Aqz(!@ra&BAr AP(r80 l;)2kp/\ZU]c!*`p=3= 2EXv:6kogMNP( %&kz}6v}>b^Kq,$4H-xZzZEBaLd2NVެ7g6tkaXӦNiiiidRKMNÞsg޷`F `0`0t-ijj&s'WN:*oVM:dB=9NrR#Ǯ\ @N-duw^X APaA˥.܎HK=xHիCCTw {Ӊ2iRNm]R(:5\W0{  ƔMIIw3.VrΜP][t:ߥ5p2JINv2IҸXfW*,&3/wR}}oE{RlhljmoװLAtz}Cc3Έ.U*m::~TLMI;{t 0fN:ܜof~ xL E]pٳђCW?" g֌yy`q.~bQm}P1W$) 0CFB+~NM:Xh1ጤ7OlR ߪ{:oՔxaJ[ka-;pT^- }|:H8y>ؐ%D! gC P" I<@:$,p#)yPr<^/ P(qlƪnnwpy\'d2p983 qr9D"G<.C8zle6]PlvHp=<>qEvNEQd2R(4 FaK{WO2[u8rd@0"()fXJ"[Hh?A""n{D]EEk0" E<.7+k,8F$Hn`2dvRIl|7*ɵ;B~`pW),bCA>b, qr9%Pt:=..bF"Xlȑ{t'LfK( 2٣c2R3 1^HO D {t9 @ƍtPӝa F{YZgd/Y0 >Y,ցC-ҫjj^yG-32{^46s}ު_(J%n$6 NJ@X#hl)~јQe4j`X ,j'w a2((((((((L )Ф>5p=O|p7u{ H$~wD 2YlZHAQ/gC 3/il2/Q-~o ,),G$yܑ܀x}"Pr8kƔ1zqfmZ㲭.p1u4lZI 85|{M%WEOO9KG/&?z{c=P`0@:tZ =Hnn dP(5s8  _|5~_}rص'zzb& EQ "d6[ƷȀ hwO/Y .0`pm6[Gg׈J]Phu,n 0ѐvɱXd x==} 0:,Q?``]v% ^\PbϷm߽wzp 5mEŗ}]{8r.5Chx$8jjn Vߓl? 釕F͘>uxe6-'>jfH/2\a7PGQq/z層{@eUwo~{  }}=bv$ s8-KV|q^O<9Tެzvt:wÏtO<;Wo\?(zȱ~PW߰w0Cq\3c>mAth@Ptor}7]SgPjN;q؉{;ͭm?RI8:sEL56s_>imk;sK%P(w!٥nmk?z'}珯\Q^y]{{=>Ptbr˶lBT8NM/_libbŸD,Ξ8a[nSe,O?uQ vǻoY]SשV & p8l6DB@NʛYc3jj'N ^ږ9&R(zq24iT;xdvΟ7Tov5[,,&xwه"ɞ8d0zzz,AӦ:sNeɨoP `pC"ȴ3Oa Shmu9d2UJeJoutsݽ&/?wV FEK|n B"'nZ 'ODB\!Q ۱sױ'}>]3FnhlHO ˗*䲛U5C8~"6V5&#$J|~m]$ ˥ee^QWXk|~ͪ77mxdrRN72'&&̘>u=x MIfE{{8)Ii.˝0~{50њ-VNEӧM-?wXb1DU׽ׄBAA~򥋚[~M~ɇm#O>&O׭5onw T(&eo\&9) /1O>b̘ AXt񴩅19?w+- 3qbK[{k[{B|K4-AOZgZu:ysQ 5m9&:<>+A"`0qdȬ]R"PTt;;d fմ)+\6TP(ygys4:wlΞ8PUSo*U ](ZʋyF9VMΟ7 >9?/9)!{S Z:r{]VfNhAp昌Nu}CL& !> jTKO^ B]jon?raع Ek^~5D"An Ϭ~aB[oƢ!6MH~3//ghJ??dW;ȉ?-U+ k lOlnnD"Yc37m\gX7{ @$yb2_XX|vǏzbrL HLLHOM}wuUA04ڤDͪjjr iウYPDM)r jRS{)G^?~ў|byd2+ 0 Tr ?O&bVq:55`455]? 77')!AePOonPw73g/[* +vLE"ц_۸~ǭsr&4VΙ=3 ^1Nt*nۼL IDATV-[NcFHOKO$ B?`w8oudO ŪT 8<@d0"mۛ[=aA Ƃysټ ҫ`h4 D(D8 ERS4o DGF0 .K::9yUM{g サl6dR)xXL@ga#b|3gLZtW_~Ș0a\gWWM]ӔJ%c Ԕ<O.Ϟ9BQj# nV446a56FYF ĄxRW8qx2 7w E:!=--*L h4;=" bI%b@80 b@0 E4M# "Hb ZVQᰕ 3_,6-Θ>ac3 74N-gXbfumr 7iR]CcuMYn0 gOpz9aUNW}Cj(NgYxED K,KcPy, EQfs‘˥wX,`֌{5=qArx<.ŒHl6AT*a\.h4R`i۱sO \+\.gÏ% Ó^ GdR) 8ֶqYcR)u qt:Tw  0 MܽIMI~1Cv! /7a?t:ϿQ^ ^C r{onif [n`|>~3g]~ĪTx)k( 0e49))G"\&A9 7kr'/\rrҭ[Ùŋ_(4udi4 d6l6{̘}_*)BaG֖d@=~tɕkv##=mRNvSsgFFqYǎ:bK$'OC!vz{ּ⋿|vk^awY^f329Vt!h#.uU_5-?@d446qy (˺M-#6,*JL&s\`UUN].2ol 0~\]CcޤbbDl6瑃$Yc3Ư=+jp8܎.J R*MfKV昡]˥W_,Ro9.N*r_,;zYE]]tm9X|YHX&]\:6aƦl6;9)1\@3vL79))ы"0&Fd0H;dLFe σA,k) M1- GO*)kji%F͛;{EŗzŚ8qž/].u<W"9~*av̹ UE:Mֽ(`(Typ$\U]  ::dTc'N|aVV^9NfY,P(r9)IIOv:C֝PHdf2c'NUJh{\*RS[WVQh_|'T%=-599R2V -Ptǭ OZlv{̢p$)1a)KvB|>wʱXJ5L%$[6rJ$z^A ߜa3 CP̈ Auxo_rR" ?q:]\'..h2''%8钓Np8480z #?p qtJD, &r{,D,rBnw@0 ccU0 cf0%dz!))0ސzAq>Ar%d͞D"V-&&E`0( i4jUiu8є`6[>X,$a4i0DF U,E4Z6Pg rpPT9PT[[aZ h{@ O>[k*ն!X M&Lt NOLHp{<PH,p:UJNo5M F9F$i6->..z@ m7oHjt,fPx\lf"b /b1h{/|Dshu{?NBx58sqN: +Wv糿x!&M8" Rtp FITX6J>B [mXJ7$%&rfPl|!BQF''%aVf\ndv.  \.q\o0ƪ4 fE, 'P(D"6- qzQ#t:p_Q&9.L:0 \t~\l,i;Nɜ:l bU)uz}B|@D"[,VaNHt]&zp8,fT*׈Ɍ lNLq\ǪL&tص{z\8l@ r9A-ǹnLܞ+yFQ5J]ܿ4xz` !>փq?(Ac2ң+v!/‘o5ZMBAkek^y)%9܅fN5ch:rD|\'9p88af]fx}~{ۗP0fyMFcFѨ7)(((((((f(b , a0pw8:^7p8v{A8l8'~_iuz:b^*0wrq\S tz$Ta8ohkUV^DQQ^q<0QPhѐ^w߁CS Nf81 3MףAA8kp8ln7;kni nkwAں0;:7TFںںljSgΙLߪ AN O].j%Vad`0v# zR{Ahur g8{/ p81 ~?aVM[mp8rG*- <^o . pD{u- ^ͮ=7:Һsފʪovq/yڊU}g0 ۹{}_dXwP]}CgW׈=qm ~>Vr*n5u{'ݹk=Ja#x<va@CAS5m4$bZ>zC?Y0TUW=^~  mz羃oV׼OxFݿǞ޾?za,8}Owx')Ʈ{ju crA`(޳''1EQN= BF)zt\hڻ[,7yn?^/+;r(6>˗Kpx};wmhlz$-]o`ie@3\RjZqБ`(zp\i: vwFyYy=2v?~`0&3NwkoxdK ڣR4sLC>Q;Pyl߱zYyMm߻ЕI!:rr;}`57O9[[W_]Sk;vݏnjaCCVǾ+[P i ׸ƦodO7u;vd@ Դ>ѝN38&U+nSgI˗.:핕Uohh /ZX|I$T?e<pk48=3*#3jyM@D[ֶϿzWz"))BQqggWA~~RRGJ~j)IEŗoutNZdHٕhI=/wҢO:`:7jjVꚛU}ɃO `4gV?e0J兢HӫTJÙ`OlUj~ %oXeKyޒ+t:}rAo%VRKu#vr="a1mN?v$Tww/[xM274Ԝ9f y1%9=?/}LF:@*hu:Ǎ&P( L\z'r){Wެy# r,3sLSs |;olPz_{|& vF$,\0l7v;c(+Jg_l{}ͫ11"@m]E oܳeK>\<AG;Nb2_xKoudgO\0onND0@0t i0򋿼Xti[><66ndRٲlb?P[WA@AXK%>zϾ>_"ZzYYo_?]lI\\V;l<3w:iRNk[[zZ*p:}6 I҂s/h1ͭR4>>.͚9 S8`J/m O9s$%& ()\RzuтdjBV?Djj  ~7:1-7`@zzF>qtX.{vcq+/Y v+~E|lf\rEMm]* +`sk^}).6 _i̚9uJFLʕm,9)j͚1}eu 1utPR\'W ӧ],c蹋Xn6a϶565HH 7_kZϝq̟R} \8 b#A\Ag9zc M4~br1]*xтƦА0%9vWO?߻ ӭ۶n}x\*)t{Fqe޷:T[ݭ,Bm1&c0c}~{{v$Ox@,H$*YZj4l8d%%&$X7ch4O^ܟD htI(JѦMz劎.To0̛:8db2OYp8Α%Gn+% дwϳK,f>x¾rgzz{|K/7 Dr}tq:Am6Ūi))]=CRD(BRINNTbX-h4*WyvU+YY#JeL)9kVs99;?brg_t8 x^ٕ- Öֶ))SttvЪbZZ۲28]6ǟ~qٳB!ݼ|a)99+/oimp8T 2ofO^FвeKH@8,sfn774FQZܳ6[lv Xl {ttFN˹\12%-5 9@TW_y@iهL游8@]]]ł hɋar8)e766tڗ_zaFN!(QGFF>8ѯ~;;{%$AZmĄxʮp: E$D"qq:}_XV -[_aK$ s oۺif=-mm5W [,AD#ѵO>ٝmF+VqաPR;yO@M;mV[APzZU+zzH@$yƝ۶Czޛ8qvZ|i]}#8N<㥥7<}FG ɤ.utt;qz $ =C&3ŪR%~ ǒaH(D, E?:XW_?n5&Fy<ńB!aH$EM >bd2)0L(pMOoaىgnttʛ9wN~˕U7U#bN2IPH&LXD4J%|>QH K$oF6VkcSXK^.Xm F#ubK8geMkjiDD"!%݄XEk{[(>g斶ں zښ9-C$aAVDYU`P>80 j7 IDAT|X`0p, a@b,BR`Ha`i)iUWAMjji2%:AE /^2%'ݴ_ ZSU4ͬq1H8V\ G:^Yupw( ^K$Ye~?1!~y%e]]3MzaΜؠ }qK/b+xHr9,SήO\q#)Jh4E$6#ʸ\.Da6r|>ɠ3>vnf?KwFeNZyJ4a  :&(HNV5F QGPܱ EPA+/KK, H,-1 ,fCrrx-8A +Rɖg޽kbNgm]s 2 a.d E|thes80,p8A(zںz=|8.>_R޹=D"P((\C~BP(<F=Cnjno0(BD*TGQd2%1'a6r>pzz2;X>fw|q3gϗWB8ݶ-@ ate4+kD"JJ-^ DVlsB!OJL#EK*\r8IGgWYy%5rI]Q^'gp\D*޵sdT*e|ßTV] c-@dʤR~KRFjjϛ}[73Lvd;)1ťe>H8d8Vcgں޾ֶhxPh`pbJ%fMVT MCC8LIInkQRZ~ ;bҲ!ɜѯ**].j4Μ-D ]\~֧*հy859d̺X$ t:;1^?4d.)-oje8d qZᰣQD&xVj*'{zeՕ3!ʘ^Qy%֬;ԤrZg+"шT"! ==%-mdO/)-+-}g%'%\?wXfsڮk4 ke4[˚6Zojr+|Y 'a.{iʪYyyM^u:of.JJLr \&GGU_O5nXvM;o~\aFrEe  1 TJF+1&OSw'NEѦED$mji% P~fL&SӞp񶅰$WW_?wFș.Ry:b%j2K'˖`,]ȗ .WTߘ xтk.^2u 7w](r`0-[r豂KEerKdCcSQqI}CL*CC&}\u51!~uFϟ7/3V0m3;v H8"aED"Ϟ ԙWML&cт}ea*jkHqX4[FC1X[5㚚[JJ˚[ZiMՖW8N +*ΧB_t1RDVM |Ƕ?L):Ic##)EQj+W,Ǿ:E 揷P9$I:Nsq l_!S42L&s h \ JEyxXR=;crFGGMax<^bB(!Y$J@ 8l8fEGP9(`Nr> U*e  "xphH dnHăC|> 3BѨ6]( cU"}>_At:v:G @ FREl8d6tP(L&'5PhxBьFÑ!L˿sp,TAbP r9Z$A (11$H:q?G-\8>:QU&YX}ܰ*Iirx<$fvHp:Q $bb4F\.DpaFj`bPX>{^$.YPѵsW~"Ax՗_|CbshJM$I zm;$b1AjfݣD!S8G#ѨsdDVFF AΑF3d2tAZmbdHX,Xz}J>   @BqLrqQ>qbD,g/2 >|" ?:4wv$I^,,$z ܚfRb2!h5oA=l\Z?88H4n$I[ 9b,+ F=9,X}je0rP8lqVM*x^R988 e2Jr8.K(<y؜nVAvV2 @0" vcr9> f1 B VUUG,pY6J.NI$H[֭5(=|r}{lIx~b5QI;NC[6okF"H$rjh08Fqzb40  F@pwJáT(h8s0lD ZjzE6VttZmN B^OVB!'&9Eb@`ЇLfFCj z}(D ժJ) 3!S(Ec(IN0{7Wf0908 K,Byb1Η}br" {KՕ.ϟ|钇'Dqzq|Q];ҺV(-mK,^==d2X`4WX~k2kӿH?cj4Q_..Y`s qիVcOBMu];Dngtuck?0[ .s__?ǽmU>pp(sZvtvUUUGQP]=xs16~h {X((Af?q{t:Z;F$l EqgXԠAEMFئ#w6AhdR+,Hr$ 4f=x(3(B1L5َ~1bL&2P=l-D'1M4m9ןNL Ĉ#F1b<^ 1bĈ#F a8G" C1$< dBpyPBG-;p]>yqyby5@rU*у1 Ha<=s ak?nxR;Sx #1&1[G'Q}N]ĖcĈ#F1&.1bĈ#F$(Ecѿ1+F1bĈ;Q3]L["#F1b.A‹_ӖGo`a8n,VjZm8N$Gf"M0PbZ6u_}!Wd{a$Iχ =z;O$E PYUFqc^68XJIz}ћѨfqpR;^Ix<^7v*A AME&I2 9GFnJ5`tt40jj'#by\ݸMVc5uT.A'Nn $Is$ |>*AJ fy=O8HZm, 0;E@k5ظ=T^?`HzG(b6jEFjj4I##`0x[Aڑ|Ax^r#$QsFQz<$P~Xm6A\nfڜΑhߍoW09|& orEF\ťeԾɡ/}X\V^?>wFy~Ͼ^/t}a8w =bYyT |q>qBA}C#  oz<ԥE5uroBdEe7-gڱg5n=SR'\O.~S\V^Quߨa'ECcłKϜF6/}y¢P(_yskbvٻ_{{o;wnߒu@BEO7X\V>6t8ypB6%Su:B.yOe2YV)ioQW'O}~Bq:W'ONJzrͪ8e>-t`ϝOėǎϛ3;'{:F#b#͙s $FoUZ^=OOo/I;oЛ@s@rbbƦp3N=F zSVfغE*oNʜ~Fbb@R14da5n˛9l'Kh4v8G0lBώ`2Q)gϢ*9gG.].ihlJJLض噼3(?o ,?E9s3ҧbFZdppxU>'A¢f>0 vFtG7wyrc :gݓkdJZjm}ô Mi)xCkkܜԔ, Xmbʟwr馍닊K̞5vϊ*?6eR*RS[Qů~~f?y\0|3gΠ ^.)0lŲ%bBvשּׁp\/=~ aO?NRw?G*Ng9g*Nٺy)2#'5))r:q*Ԫ3r:y }JZk[D,6ht_8 gϚ;;bEvp?w`Ϯz}A=Sөܲt⒲ZAIWkڰVPSKsr[~ukFSt:uA~FG=0,ї.@,]c'RS1BE 6񓧣98`г23-YTQuZ PcRÀ۷z<޶G:aݓUW-+ٲiXvsMZ)LcO\eX&/:A6n7 ._g0(غwl,IyYӎ:c ?K}>\?nѯN(j:{tT"o啫jjI\jC ~gۅnhhFèg I.pB<==fNξ<;𕟼0cF6f2of/ on4"f7 8Դ΁!X,bH!{;SRdӦ [,PHRQ'Q+)-g\ɺXp8Belfm6ۢ)kfnNcS9b6[,^t:]nw9Y.lnjiMINrz7 XtOyo-޾~].h9|>lX6))Q"ۻ0&w.O$  &yOðfר՛y|{wp 0f  zk5<.wM&3 +rh/ 21Ԩկ?̚;"};`E1F A_?uȗnА){zVWwFG=^J%Rgd(622@X,&ÉD"λ&0 |T:2sW4d2OI=` -.-˝9-c'Nټi;* (ZZVsXd"R¾ ϿXPoLNJ|=hwqMm*  W,[f_psw>ze4Ek^tq׽m0ԨU?}eJ?D$t7^mxp~Nttvz7lE۷yG5-yh!(tˌD\.fJ@(J%tht[ IDAT:]T$ 9d2r􌌩eUɤ7Hg]CCfT hϞ[b0Fn6a*H$>ˌgG^((4qC,)kהJ9,͹\\zS.^XPt}Nk5[xzú7^՗_-_ a:aX 9ˡ@d2)90 s8aἰo/a>lphh'8zi"r۠hӧgWBaBC&siYVHFd2)FcܽzHp[7^m͕,38d( `0^_Ooo~̊*. v$h4b $!HӈWES|AwBa4\Ѧީ锖p P 0D,H8v$<.W!ǜp)"pq $~ l6@AH4 d"Q~>_&yuUF< Ip3AXL>R*WϜ=_Z^ #.)hݑYJ\& B`,K%%˼Nj#_;wbYyg;+ZZ4%"-51R$L:ZL6;# 2y<\~܅RAT*% ._Z3+otR"w6`jMmOoS.Ө^,4g䖶Ҳֶv6kf e,+7'ce##.ըO9[V^A Iگy7xSӻ{z!V304dޜ ZNvV̌c$1_ .h U74m !t:mNeYHT.| ͩܜ ʜV\Z6g,65A\FC6Ȥ .WVU;K*[ .*.--twPnm)IϣoH\.gFNvssVKVG:kӳT_˞ NII>vdYy݆:Nn^/ n˨ܜ9!̜VV^7f$'[,V9, *R*љ$ (as$¢brlV D#rk i5Wmvǽp$'&UT-Z0NfȁaNK0BaV{hAʚVWXZV1L&sώ|Y\Z~6Fzٵw<[^Q8gXS/\kֺFնaݓG+,|FbH p8ެOYc汫9ӛZZs%py\}hЋBܲdϾ8ZXTl4\Nrrұ.z^@ҳ/pqcӥ$kj!N&]D"O ԙsouch =}\bT*gNҲ鴧Ϟ?` htZJrK%}l .\2]=Kjy[Qp8zNw9yQ9Yvϟ3;fnT*h>S5jrFN6Ū2+/W"C<Y,&HZBPPt" pT`0 ,NaVFrLL$˝jE'xg4e2hP)FTJZ^TE".FUJL&3Ţ\H(T(G*<ԷM.#Z-N1 bb-YPV%'%1L\>BDxcNNv4=sb~L:ըJAOKMQ*Ri402%5E qFNt)z}F֨Ir9o0OEFrx (MLLo!:a4UJO7J˝qKd6)I׋ ! B&'VtZ7>3I%bioT(R 6-==-Mg1?+!ޘ/Jt aЧp96e5juZjJrrD,iI *ʠKMM֨UcS& Ao4uZmNbU*ZbX%%$8fVLS(rFR*I7wvNP(`H%?td0 FNKMIIN:`ЫT*AD"JHMM0<1ޘ/ANK EQR)&D{oT(K8ѨFVtD"aL*hȬ̌$o q:>.qV`\o4R5pttv-?sW[,ޠK7RT*dR>.dȞrbslvrbFI%}}K/JKM%15N zr8ɉhU*H(lX$D_,Q}|>/.N' !hB.X0z&f-VS2Qɤ2Vb%ZMjj*)IΟQZR cXÉ t:FT*>n6J4NUآ3mzE"\c[0s/,k c^(d6;#͟7g$v7bEGvG>;PXT\rڋUֶKK,˝1d29wAլ_$O?ßÑήem .-Z8??o<|yZ~ݓp˯N`(kv.s/qF2{޾9T IΜ+Ssǵðӳ2׬Z {@m]CSsin7!Q_6 LQEn ~{5oftq6pAh4X?>dp\@0"IADQt Ep8&erI0,xp$7  pA\.ިe>:hn w3br~p\Gnmo͐s8ld2'`np챵ڦhb2'}ZɛľbQdAR}+?& @x1& %1[G'QvЌ-ƈ#F1bL01+F1bĈcX1bĈ#FL#F1bĘ`?0XZ^1vHu.AP(r].O`0HT4p8LKp&Ͻv$IE1 ?~T08^S[72 [_؛8$I!E}_2@pŸHĄ yؐ$0;{}> \n7IFX@QvxQJ)%y˖;e=qፎΫjKʆLc1E'`DћIt(|P(DD"^qM-$^o$ Bc1 sܷz}'NCQWc _j;~a__yE\ @ @ h(t4< Ox\I. Bk"H8|Tq^ `0Da`]D"W|y0w̹_꾭w|~b; ¿͛EEΑ3g/ϟkAaQCS{]8~UU}uXzz&M{_¢* _,$Int:}Voi5_؛Xm~B>|-'v~o;o|qq$~u?+/|>Z^Q7#n|?@4}|tPEpc᱘ZIv߷?.T^~՟f+==~O:ss}/RMyQqɑ/Z{v6mADwO=cXOO}-V+ðw}? ܣፎNS}7Qqʼn}r{u@ h|$/K`NMIlAA/465;?p܅gRPXTW0d2_b?nlj.T}/gfLUb(miϿDKf2LJ?ݾm׃?zUKkm>M+̙-IcNMLL0Lcg&ˤnw jg6m$YS[wnU.n-몕RSΞ822"7{amϞ}f +N9;22AP(ܰ~?}WK*گ_h>D8%I(m3R$}Jm65+332NOOzSp8[6oÏrrj]lL&u:\.%`XvqASӗ,Z eK$ֽs˖,R~tPO˘9ώ_b (ݵw&es^O RSRSSRGh4ήiSHHlpDbEpJiw8N_Ctg_y3sg̝|Cpܹ,-!808DH2e͵u=r/{?|^sK굷y22޵$ܜ?gB?@Qڵ۷Λ;AF۽sGjjի5hAQjŲYy}ְw_khh;{@$'%z<Ԕ-7un߶ed& mX$N7n̕j25%fEg6=S0ϛrɱFFGGOO[0on]Cfy}˖,1ʛ9GGIL>.+!b1Fs8VMR)SS{L&HDXP(0=}}c=IWզۢR"ܞ]9&9 FPWoxa^OkXy𙒖x{zfz^~FJrX,2کSZX LzrSXmh4R*4J7茏7Rz}8v:GLfׁRS;-GaA[۽kƌl*A\((|rͪ-fo̊K  M{ݙ73 >rooRR7~罃>"굚y/xFN;w6Apx ~N%665~Js޾[ CHtA [j̙artZ`xuE9244j8lg6Q1 ?yqT"E1lR$445dҗ_ܟ3}#3L.D~|dfƦ?/Eϒdnj&3wD"_8zAn*X,R*{#ѨP *)1dƦf\]V^r7 W_yi{PG=u=qh4 9<2rc#\p8XqrGh0Dt:M,^[yx̙}f;i4 ɤ'fW0da?3>#;9w3AL"%*QLb)%q%kݷ?tw*Us ry0&tt EI.@SU}Tթ:KwtaB:gf^# L鏎ٿP(2b#" H(bt#'Ʉ"FA0ެW,_Vx쬬MO?}Bؼq|IMMcp#F}RRL&u|>EE.kZ}3*gS[,+1!i^ͨןoTs.=-C˖:sbzdeSK ڬ6 Ba+ Q A\bPr{}8EQE1n"FCEe1|%ԓTE). ¹ axz2iUu^^@Vfp8׮iiiXpjJp8q qF]}rR⌍A8Lp\H", PxQ qr,BFT*@ fD"h`CR7K/H$Nׇ}XcV$۷ PrE"D5M;*H,l6CQ/pzkן=[r_|~S&qak@PM8?LJB*`l2q8=0 RDŽa nAVK{v_^V^Y#iH$ryѬzx9Ν+:xS\- Cy4Mǯ\-Yv-{"n|g2{z{CZWߐ895%HLF#̺{ nF^|24wavsKd$H2 ڬ?I@j~qA?fWttv&%%̷CCU5 MB0mH(DK/]Y hk(,-LO4wh1L4Ϸ Mu}pXӍ;Nkddh4̌ `wOl5CѨ*btl q.\S[HOKr8?/b$%U&&& ܳ+ x\.J\( IDATk(.vzY~^L  ;;YjCCfi7ԔҲjƈb1˽\|#Uu ʪʪήn6U#cusKzZ+ŋX,VJR⍺8@`2>?Ñd ܠw VU$'&f}G#'O BP(ޮT*\.m*+WK7נ 2di(%%&HCK1|ʪcJ+~?IHfڨٌǪkj{p "gc֘v/~\UG}BhqA~lTX,:|XEUZb;_lZSn DQX,xRbJb,-q\V8Q B C: U*χaXcE 1,jU*"(.f2hBa^nZ0EQ@UJJBD(hq\ә Ϸ 3h0̇F  X,N4Mgdi4( zNVY-$FCFzxr70o4Sn7 f-dK"cjfѠWFZe2)(8cbIhPDcft8V  fa͚0> A^a/.X bF^Qm6K Srb"&!Y-fTLPi4jlt:-aR E5X,R)ZW*(08 z Ab* A4F#2FyFMOINYY, &c@{Ecf81ٜ'1 5 J B Z`0 D(j24j?PUy6MfÍM9.4M+ @0rb ٸ +&%@} @@eұpkRSnT*f6)IsW{_E:rYJNL`H [Z۪k{vJPew8qTAXf|9{ss;2YUuMɵ֭X]=.^JKMY11q\__)fu [?aFƵ{RJrⲥK&γ.p @t؉gmE/74{⮛?cwe)-6BP|nigLj 1g1e_~}z~*X? ,$ s+wg`MMYl3J|hT"0 D"f$Iz>Xm[qc0m7rˬ4=99U~T <]( (Xhy%s:|>?mnpnFn+kf==#F'1W91bĈ#FsLL#F1bĘcn-AE(Qfi: [y Iw41%xMA,QÁ9yK(:pX,=<x> ժ&  EQã曧]I29(QZM-E9A,QÁ?>a1bĈ#SbĈ#F1昘#F1bĈ11bĈ#F9f׺CMXH$ 1^Q=  ׼閖։ giYIwg"H(Y-H,i:n^CGhF"ۑlZp:85'I}@qF  3Ξ[ʪV:=~rrj ßj&7S(3 $  A LӴD"~  1O(r{ ({< \=D Bўhi: 4M$S7J4=|Ԕ8t;h(<G oD"`N8ߑG3?~\ŗ]={>|kr.]).t #BV ͗pB}CW"V8΢\dkߖAmm>ͷr} wFQԱ'D` 5'I}hokj_|7޷[{o̷̿0<2:1di BFMͭ3! lz4n_=_t y_.̮~ztllm2zW>n㶦V?mlikn٧5*{w:h# vܬ?]*mxuAy啛?xNotx|Eӭ< /CԔ.3Ob,ʟDfBA$y8u3/ G|5܅WKkj/:q?}uЇ? BO?Q >iĩ3[(nkD#-# ˿o溫?+A=r=wiim;s|zj*yEť6E"9z6'0]rR.}fN^/-GZZ:ߵϾع}۷}|Rضy#͆{q\EEx{߯|Mŗ>˦+d?yo{N9G[>&JFk3ͫ>RvxrR%}B!/VzSSN9OtaA~^n΅Ku "(>o]b$yާׯdE/C0tIGmTrltt Ѩ׮Y^r'x,!n=Sn0z{nؘb;$b-~JbrrJV  y Tt~ѥ+4M?#*rW)LxCLuY kV?wh^7155uQp:}x`him;qH$b.]Z]S+B_?mtllÀ=FKАB!cBHZrV]..Yaf;&&TJؘBlui M6Ip>~񟼼R׻m&\Ţ%qqH(2K (;;+)!~>mι|jUMX$ Hȱ({Qv{rikSE9vg:]sǩxPhEn rյO=yռ܅L\P8::i55RHi.)ÎD"[7o,-zg?krJrۖΜ{xCo=BND,oܰn`p4un=̹7S<~W:::%O!&^n6:v755=_ꑕA?tذ~K滉HԔ܅ٓSSoB. 9-nhlC0=v:DSSr{#RV::~V(Zvl22ؘ\.߼ifOE)kG4himKKM^d1AAr8v}Y_iggeryJJ24}>P$m1QIѴhO_eY|5 ި ]@`0~_ `~UTV߃y3_ͷ{z:v-`$ɑQcbQ)Bp떍6̱ash4j;G }yYzkFQʇngUu |>֬~ȱ+Zc.\Lk?l|HO{N>ApVfڧ\d2>}\&?_\B-m]=:vm&8D zO=YtN[c"!>nMM-vl(jܸ~-AiǶ-gevnY-Sn۽cۖEyLA0I/ztZhlVkګ{ 9M.ٽD񓧷l޸nSl6$%wXo'Mӗ\x繜[D*P Y̌1jq 6l2|d|~U*^olhl6 FԢT*dfV B}bbg!e}!IՒX`Һ=))IH4W¨W^KKMҞ|ou/|zz)ImCBPTr\*쎳٘`Y]=cvKuvu:pzbB|UuP |ч+*NWOo%nOccsZJr$766uZhz}$ɂETr[7m(,_|YeuԔF޵󙦖֝۷&'Cpah4ճꑕkzan޴a]M)IM~+WK[0fef~򱇖-lѨ{f޾'{tႬ&eޡN,^uW_'˖/]<99n߶eppp| ~y82:a(FGW=ﴶtpRT*ĄyHӴA-ܜK x)JEnŅ-_ZU][ v?5% \.oߺIR2CV@0_~3^ `D#z]ΓOP#?А})ZdR3۶\(Afm,b1IE/utx<^ٔ\sx[eUZ[4f .KU(vnz}o]>0;kn6x'4 ӿ{$i FK657,;qؘ}Q~L hϿ$#dWwϪG(b!#65y<  qz7@F"qǵel6h0 C2DٰnM(>|7l6i&!{UJeʇ3s'wbH!!ẌqH$1P |B!0 špX$D[UY]G,.((Xu}jql1Sg=^r@6o̸<^/l4vZ!&~C JLˮii4=d/r(zDYWX~YV=d\./+0XYѧs874lvfzzk@Pg>G/NNN9v۷ /RUrRrM1[V!rH$:!5jqwfFPtv"PH F&3#Cc9p(^ԓ57CҲ FGǘY|/[TKkf=_T]{#{AZR*e8TYONMDZX3gϻ=/0lp:ĄS*dR@cX$! iH$J$2@3WRtJѥK{_|W^Pt6EPk;Zd4_tvVS(rTq rFfI0pk[a@`6EH$;DC| d2-)>. +Znء BF9{biYE0R(A\^^Q 0M]U Me]ZRYUT (.VZ5h@VWߨZ-><0jo4\Y˿xu>.:ݩ3+lVK}CcyEUWOFxT4Z]S{^gf7l{ + ?髯}+V/9)l62B.ѠeKw8C#iGj[*ko_aSgirry< /ϿG IDAT7Ά_䩧\P ';["h4X|ъjLp>پ/{Z}[_F\͖d LcxrRONJZp C$-5UQupw%Q dp:#d$/gle%L(% "13S0:VŅ:E4F>fiq`6l);+#b6-FVL -.,0uFEP PdDPX"y859) VX.1 A-j6dR)Ay9 #W(*2f|F>--UR9Degej4%RH\\H\I &11f1 (11h‚X.*O.r8l *0hD"APT$h8a( æB4"a;&&rٲKX0eKD, 0 ٬n'1!>!>NVJ%fQ8S-38 CdYJi%P1 *Pf =ﲥ[ԓ,u 7u8 Ѩ1aXPk*FFP$Pd4`&q hC@T'\P(p>rr2-5%)1bE)l4TJQ )I33(UJ. 'bDHT*Z\cF( "^b-]c!Jb C6aCuZ-" :-ѯrRSl[gf εҼ䤄p8L-Baפ F +W=RR2cԔ9u`rVYP0H$LNJD SEBhNapZ0j1"I<ol  Er1 ` XXo6MF*rFK9<O.ݹٌwCX8O&qJ4"zǻEHQԧcHOM-,(sK!n¶s9?s6>wȱ=I%׮\in E[ZϞ/Z#@0ܒwoZiqɵv?ֶ9w^!oX& 8uڵ@iq7?YvoD^[T؆%eI ̺e k_3? S 1 IN\&e:1PJk)R A J6xt$EQ*&}`p:CДۣyvsfgVo24-FHUN1bĈ#SbĈ#F1[K`nG4y>~!I2JQ{! "xᱻo~ 20;a]S-ȟ;^Oy ( sK(:La{  HF"j>L^_| ҈ZyG>?@"oA܉D"5ϓXD1쎉?>a1bĈ#SbĈ#F1昘#F1bĈ11bĈ#F9fp8L4MA0h4 !I@QT0 2B9!ηH$RYU}qFFFU;BQAI(|7Ai(jΓsh#H8f%#?Mӡp8$9iQ C4MBh4#ى_.:6f{=ϩ3fn;;koܖ\+f#ȑc'"$GB$ lhE YoX4EOo(* vy}c'Nt'}_yusBcS_u\H׿}3e\Ѩ*{?ǯ[߽;owTUysN0?FiՁ_ܺٞ^ҕ?1yEegwݞW EA[omwxǚM(nkou8C3}}nQjni>CqI={~?`|||moǟ2}Gr|ܱ噝mg_}?ݨߴmm*N8nkkK:3DYֶɩA@$|k7}eO̡(snpLLl}٦UOM1 JwOo;~=DŽyXWpw}o ) ?O r MӿࣺO~'wik8qLFZj 3MM-_Z[[t#GؾUR1**=%܅ =r'^yY,|j5_xy@'Na$pݱ/?|Ufn_ʫ׮ܤĄCܨkx<^Ϩ?a.~,/\\v ~WT|h<ܕ⫝="pݚM}hA4:6>>㎉O>p?d^38<<5۬1]"_O?JGT ?*D2tDW)Z!Z}I,_$ɓg466=U{qHzhRA> @ՁC@&>|iѥˁ@0 mݼg'D˛r3RlK]}fkhNF;99d4J9 Iy $I /_dg]`#&5yP(<:60{y<~p+Z~r ۶nnohhjz7na9lKtZg_[ AKMI*+\bҲ`v\rb>.&9%9d42ǰֶƦf2#ϹE]]"5<2rq;66hhlR\" =^/˭m"Rpԙcch4h&"񓧖-Y'IH$" ǚq\~f[6=}r'ҨkzC,~1F{؉;eonnIOKmim3{ye2;՝ǂY&1,;{Yx/}4t~SSn}hU5\122o7$w؎a(MM %[7o*74.^p>!g)kׯgge2OWF#㛬'--%!>N";&Ά% NWGGמ{l.ߴqnmp{{ L C/[o3{bnik-ͭ:V(n޴d4u;zvwv;XHJLi @ԄEelݒt.W]}-0)+t&i~h4 yV?Q([;ux7zϾ h4;74nٴRv'iЍ_VfSo_D"6<99rm̴'#-mGyhWA{z ]XQUm˖.olxddb63h./4 {'IۓCC/Ef94M<}摕mܰcl.֖1fVAp[Zrjnlj$IRq!`zzzGg^M&co_gg^x\q;lrbb[{Q*O"ISSg EQN}xC^p4Pt kZ $K*vlߚ'/q/i)@ rMb( XRXPSSK`jjJ((?0;t(j1ˤFA 7'A#H(=]Gp8]ROOM546 ) |&olkkDl6dR.b&:N(h4C+ac\*l޸=C"X-H4h4g}ŗU5h"-fgVQ0̂e2)cl6; Ʀ/:氅B̀t\m)7"(lXlv{G8 @$:?/"#b6ܱ)3lْp@ _/Y>zSgKEʪή?CᑑH$xDo=O`zJrowvued]tQ4uFzZCC#bzx#+*^/ ݇=iE/XYF@YEU]}#IKInllfnBD,^ZV^QIZJOK^(?h0̎HPB\\\|/-;HohqqS^75%ettڄ_& ߃ Z*jq-MLMIJ\.0b&'UVlR)(f1 q6kɵ9 a< q6[[{;Z*j~^B*{z#L,X0cpCcScS 1!fgef9wad i).,d٩5VE(M) \!c(33 (%9:1񖪪P(8,6MQZ,WKrs-䤺-~Q$q؜ʪC0M,+3# u<h0]\[[xDB/-RQ872" EɅMh܅p8<4<@OojqySnÑɤU{>蝍l.))Yd{jJd\[(_PhԪM-!1>s YY q{_z% B=iƴT$d:r䍺料JWUFGP Y;/~\UG) ,.X@$U䤌 *kDf߲FX,XcxjJR8ufqn d \\h4J71\lh 0LT"F$DBAj1MJ<ޡiJ"6VL*01fb~Ry|GQ j A4*1J T"FT$*ZX,X$Zᑑ/[H$FPUZXt&3v{8NfFcxh9 Tjnm[v jln^JT HRa(qrLRiqF28%'%Bt:E^iqJ1J^KJJ EJVC x !&' B,46l2b6>HO3 6ONN,鴉 `-ݶc3(Һa6 +d2AC6l2IFrf3C  hkbI8a(lrLV8V zSFt Fu*CQ XL.lٜ.WiiyfF:A&! Bjq=r,3#PrM8AiGJJl) EX,T*NǴ )rAlZRTFQ jZסD"*D(r9B1f{E8=q#)1fX~||ðz=+VVzc)IBѨ-D"l6Pjw:^/x(250-\5 L6 V0Ŝz~D"QP0Baw|~DQtjשx8A٠U<b>(250h҈)Bi0" f`e ðx<(~(~/(zN cInaX ~lhlj6 8!bR^KBQtoK㌹y)`(Tbqu0>~>߉SE۾~cSKk\UJb_{,M1PME{b=^ X_А ov|ʀw_֦TLr]7R}^M#&VGgॡĭkl(+?@`b>=r/Fo??:E폯|}wTʟ-RRvf/ B-Ƃap;* h=~};wklj`ptnNUfnsb_;]=em#/kӞohd`}~>y׿4dڴe{qzfQq C][94dJ=gx;a)Jϔصgbbp87ooקG>dž{uusɷ}_& ZZoz] ::i<o=b+DֶĿPx;rX~m~7@,?OrP=vy[8GG7޻8K_},:=ԙ{(g+^ocf7؉(#_EѲ#G,F۶7dg/s{~⫱1۷66[88{Gl^Y`d1ƁqbݖjZ|;l6BrlpJJb͛6Ȥң_~rׯ]F9vܳy̸蓿_F&'t:-?/7+3/h4P X殒D8~d }۶(J$hTI}ֶqDoiiȄafYp96oCb]9O.p"c'N#HĠ׹=9611a,YfWT*>22 [}kהW|P(w ;|~a#SukWUmQSWaʪ!s 贚̎nV(=r;?]Rp3l6/P.붣bpٙ]l;'f abVEիG,& p8;wllFˠ׹ƨT#,-;{?K)I߾loT*싯O=?;4UGgw^nNwOFN;Δ䤌#G齘34dB14`c+/?qp._t3/d0oޓNCAO"}>}tivVf$u{<>ݛ6o7= ï/VxpG0 ⿠(zQ(DBB_VhtFQuuu6DfsV!Ji))C./\`fΠ׵wNLLw"JNc"ы\vAO?/93zZ^n!k>2w w.[ Nh˖7Ӱ1[.dp201gc민+Fl M{vf}[D"RxKEbNp' QkxǗ/] P(ykV3\\,uw^bK/s8fu][|~gg<Ӑwt&J8JQ!ʡ!Sq@Ri4@Zj9vz{bh4HgJR`A0\u A?uz4~Saj{~/q Q$g 0omXf;ErrRsy?ٕk6|NA 2[=,Nik0''&!waA~wwsz5aN;xiߨT*L 9v{!w̮^LfP4zrߣiSDQ[fC;1 C'vܱv]hܾdgaGƁ>Pȧ&_yyʠf+&Hyxfym /wՊšzd0yݻv:`AAn,?ֱu=;m%S MV>.PP!N}ٲIzw| ^uF*٧BN=¯ns8lwB|~YH$AQri8Vh4B.FF胃CHT[7izER)J S29Xp_m޸Q|i&,^Od2V,r!6_{mՊ©VURVҺt"$=iKtZ$2-..Fc</9P][D2ɢ7z\U >G 0Ƌx<#o~gfV%):t **86 *Uqh4233|>ΘI ^:tC/,YKtOpHШU<W \LC,qQxP(a{6uWԔ>ֻ:]2ϕ vUZzr2Ԝ.*JʕE%fsvVps5U5V?̌X,.۠ -u wd13##!Q V 9 2t.>_.dVR!BhTxvZ6-\@k5ꚺ: ֨UZyxdYjeŹs57ɺWشp9tUd2<W@`EM֬#y9UO&VJ8Ά.] Nr:fjG1GplDDB.Hl6; RQI0"F^-9/_:tgh/:x9Y0Ck<WX,r9)V8l6 NѨtw0zWƁ73b+$iA~^ŹqB&\cM-o.b  XFzJ=66($JΊؓAFaAVVkXY @RY,V[GgfzZfzJA)G&''!"#Gֻ XnN#>}+L52Ҍ<.Cr:B4VM( r{z z-AtZ\.×f.Yl)L׿pyrobj[s*L M%#=\UM~^.Ceem;a D2) v; u-?u7[mV]dg^x(^hixr6f%2X,6JE2S29%eK/&ɹ5u),3W*GQ2`\|rɢ`d T*Uq@RMB2{fC0P*asWwO{{'PZjJskkWwŋd29?/TqI{GjVrsK,]rYu IJ2BP(.CP2jk2ү0 cq&7IrbʼnyrQ*8l6DoljhhBQ,lv $R~nNNJjuYֶq6EL*q8_]ucbXQqin8añx;119d2iѱ1Br9M-6[$%V.]D&2R6mXi:H( [_-=-|{GWOy;yH4 J%`J0tD2u&Ydge^؏w|297';#-4Qr#Yy[jpƖ4,֪+0 PtKرx<^WP|ib5N<R)d2d2b223.ZXT*+0$Ai<.W((rXVZ ͒ɤig"(ɠRQ* ͭ{~J4՚ӪR!h46%H޼Q&:˖N; PfQ)Die2iVF:Ti3%%٠өD:GGS :-Bx\n^^T"9<.W&q;*3a%IBDRexWH"@*ШT\PșLP T(|>_FR`X b TBR!4Ġׯ[ 000E\.x<.Ihh(-'H Cbټi mw$ ul{3$ D(i>_(TH(24/7`ȤRTuZ-Tj*BP(Rf1EB!rQת >dX%t6]%' 2tl4驩d2p\\Ree{Kj C^<0p]d;޼QC4l$'%s`( ._/t:M&uZLKLBB"Z :N.tDBJ̠rN+  zni)ILE:x,_lFJN68\.'?/GBX:5%٠!JŰٚiEB! TI$ FE" .T!J$xr ADj5"ʸ\@(PŢ!klR13 3r3I$DҪզa QPZI0, Q`ХbqjJ24 !BeJRixXVij@@R%qjJ26[B@0CW"0 nnn@%T"JEFZ*JzbJ4&)JA #T k]+v8S50_-kGK$,Fs\e6ЁtO}Nek8,XzʟO|-֒3R-X\ Jkj;,5& _g+ HYRzf}Kz nwYyd0(i}B0ln=|?3 K4ݛ6=3@`0 ^zǸιgzb&g?/tfh]W2'''ss[Ze2ZyйݘϸKcdR!;B0S?#(N& [,b˙{&&'{l~Qd7?+JIg։D#>@,d?CNb6Pg BqlŒ(H$Ta4y^\NRC}dD&2̛%Lp{XG\<^\.QipnJ%,& Pi faX!4Y-1e^n]fDޟ~C@{~[`̧^38XWqlX˞Ӌ*EE@@@@@@@04CX ybrre 9cw`! 9AH(1j:$IENDB`gammaray-2.11.3/docs/manual/images/gammaray-logging-categories.png000066400000000000000000003230471413310443000251230ustar00rootroot00000000000000PNG  IHDRCsBIT|d pHYs+ IDATxwxTיދ4*:h`k8v'qnv7-ٚno7d{v7qwޛ *6h{ M@#@<<s{ܙ9# {FAAIC5  HAA&  L2"AAdD(~{z&: A.&@WYUM[{DqӜ8uCLt ]Ls-+EQ9p==z Y0o Ώ:kʫpjDW"CqQ!5MW]sLNNv|Y ܹFHII&J$,vcw83{ga0>t]= z6mJaAnc6o9\p8~ /~Wלehx%.AkHELemYx1p7cyȲL(Fe$IBӡRD"DQdEFVQD"roV$I_j@F ZZ H$(l-|);M-lش$))&FA$E!VQTHtC;88H85b/s a6+2 IR >l \8 ǾQG,Zdݜ8yL2k#pl8v4 Hp:IMIh4$' VF]DDH$Ijj5hp8Į Íeg*/Y>88HWwyFQT*ZVt^ Up8m;xdf.//*,c?p >sغ}l6308HrR457GZj*O>99Wr56 = VKuu pD"Qv͙*Qx橏@Jr2II.V+&0>>Ύ]9s})[Zoh駞j244̯^}W<@~.\J+E__?& Y틝>8h5rrq:hoog7V#3#d"a6[Ȟ2LwwA yy8Ch4j"())L(mmZ yyym}>z=\.>23((j>|6hjn0)J}[a2yч)9u>̟7ήnv+|)O& ݮʲLӧO/[~QY鸇g>ucn ~i\;~?23ټe&7'ԴTy)~eK_S=dhuZV+`0uvͧ?IlێdŊf# a6"FuxQ;NKk%ETVU"##ԲHao׋ll1 pPP㡽ӉEAaFi)Cbb"iQc5FH|Z-CôАP(̌"(uu:::ILL$)9!(-ex؃Zf( B. "v˻knr%R0}: x^8/ݻq>Os1>' %YtBfͼ*ļpx99v@?/32# v}?pyqհ  -kll$==um :_ճqӖK@$(). G~FtSA&k')3iuZk'?:guvCCAkj(-&{J$K4%/7JEnn{}K1LFL&K܋bd2!!DHt%t8xwaE׿VFfttlF#+7~/c@؜l۾T[ZS>+<{3F zn_t  2CX455c2ICrJ2CCC44xq8$''t:$ xscY1 (rQXuAYz6~0[̤ݔȟ> B[[C466ȣ! qFسg?q>t:̞5 VCiI1N'6B VKpIIm6}hu:\.HNN&ѕ"ԌĒpI%!}r]םoh9vv @(ή.MfGJEFz:ii(3O CXmV23XAf)vq'֗.vjh(B4*[?Cwt;k)*,C)pU-3L>>jc?r>E! b6 CiZA]C{o#6m55cj|>FZMeecc,h4J(d2F9|(XL?p% ͑M \Ρ#GXdE7$I$8̭`Fi%gdd008Dz{x<xCJ,.FGb2 ŗG45008O|a璘 TzZn {hhAdE%u5ΜG"IDfQXX@]}G !!'NNV;}藙%?q'O;R ׯɈ׋F=CXrG  ٻ%댎[RYYkołnY| Kկ]iIbө?ӧꦨE,=$ΡGo ;{ vcX8|dO@(BAa<(U}tSRTbԩ$&$V8|̫fMAa>Imttv1|6#GFEy9Vb$I_vӚ.Wb .$kfL$a``q8ɘLFT*cc>hdefjh xp:\T*xaVC˅^GV300@(F`2lX,fB0IMI`0Rz@ד~Sұlr jjkQԬX~?YtZ23cdm6;NٳYԜ=K:ͭ;:Jr(~gJDqQ|> z=9S&pazXjii7\ ( n%~ z=S?>Α#dy2>>l&##6lb=qԔdV|^wC 4uUEQ'??^zs~ܻgd$֤NrRIII-wtn۰eK_mŗ?': A.31#$&vͦ-[Y޻zP?@kK+hg>!M~z]‘fƟ*AfR &\/r|5(  A@vfNt ]fR'  {AAA#PAa  $#@AAIF$  HAA&  L2"AAd.;(B!"Ɖ]I*FFsM3Z(B$! -ʄ#j*irT&5^C  `($RMLh>s4bC ,A;&J=!MH4 ɕZ=Ba2l QTƢ(#kJÑȤN@QF$z Z[ȟ> Akk+;vB  {FعEqq!cǎddp z65?@uu ]]ci2ػV +3NjeVLN>CC9f(aڴ<>J}9fb1[ors8t(O[e^lێb!//7PU] 9u %iV"==={o#:E,vs'Nral޺`0Hk[+ |( \C__?O|q~ʯA^y5[>}*= _?22⥡f~Tp8x5̞UFgW7CccI|OKZz]zzz˓O|ܜ쏨X&'IRASs3-m:uE 0%pqV|ÎjwqD،$j Sa6#Ȳl|vfZͣ* L(?cX0 Hv[ocj55Mclݾys+xh*AsM wtS+t AgW79S8[[756sg65Ou (_~H$/^y jaU6yyܳpPs,* H]v?KrOg,Y|/Ugq&8/*Lfʒ"!Q[W^ctl /@uY^{- qWME@0GʘfkF!\!U*"(@|< jZm[o{ș2NDrBѧt8X|=}{#8q ZE^n.`CG`0= ow& t:HIIFR1}TlߎZƕt?0_V?'>Yc'8rj‚|Gb4(/s3ͫ~q?N^n.55|_Ō烬LJKKƷ-[yxgz>$ILˣbN99466ӟI%F1c?0x>r F ~^1PZRL(b[TX אʲ̙j oT*`Z8~$CCC̫oCWQ[WOEE9W}.㽍YԜE`8OJJ2ӧO.vn/˗]^1 5]iYP($IիVh4,-)!77E fU z_ f徥KBa>|=e IIB!$I#++ +p$V>zU+Wp#*fsV/4~Ѩ5h4xS?i|xLi|gHH&TbrYcŮL~w_" hZT**̉?$9';5?34LzxOQ/njAkJGGGYz%*UaI˥1~w?(.*BՒDVf&ŗ_pƟeBa~чr%DΞ?Vej^lX=z)/ܹsn~iLrv }$ƄlhZbd[V2 ~ {$ z=#^/@T$IB$fsIC]6(m'+ts([$&$A;_4t8/&'IDQ:))pIJr]}%A"2==&*DA.R$ָ#@AAIF$  HAA&  L2)`!@`CA*z=m ' }}jD#ܡ0rTFo3ވ1VDqG jZq^><ѡ.5Tqetl)bVẍ ILHPhmmdfdLtw4{N'Fm=4DOO)))u}H$?AAIH9HAA&y IDATkxF8vxo̢ MX-V.ymllCKBדF$) p\S `Gg'oA7n{Ჯ <AZZ[ٸi+5gx  ŮZmڴ<'.Z&2e= \|Dqa!Tb3Ob?p[w'';gپ}'U*?pQ֬Zysyg:ϒ#toaZ;)91osypz::ؼe+]]̫`h4?%=5Ǐ3Z}c.o\$\em:]ɷG>2 pi?{uZZZQǎ#2/Ņ~IA~>K/⩏?}.{`(;M[kMM$\TU_~9ٴe+F#;v&#= ٌ$9)@bܹX,56MQA]]p' 3# N9qdx؃^#77'x7_ş}-L._$BRa0Hp:3g6ja]DQ]g=DRRi0;C(7~w$dY?|)?)?Ӧ&D}{ Ü:}}BӒ?v;))΋/_/y؅"\`>_?hQH$T_Q_Zh$Ćxf7U 6ꆍ8~Sr$Zu#hVJMZZ*! 'gͨTv ycNpG~EQpԜ=dl8dt{fJOJ$$Dhoqlf͜}uLe(BùFjk͜I8y4cd3}ːez̜1dٽwv3gC,]X6l܌Ag2E ee3HM#^/c&/'YPT8xҒcO˕'O;:JnN6C,,j dfچ`` dYz!>S ݜ9Swt\f(E@zVs!>x<IBBMMl6\s/N{,d2RYU8n7 2%77b z=7oaͪԜella|L˥^8GvIoo>DU557k^\DQjPT 9y4:޾>VXNٳXV{xwzrye:|v;CTVV144Lii  7I(*,`<1a o ~V'^u% RNwRRTm;z?q łN38&̙*RSSQTTVVd"Z~v"UTgcgY>Sa4Z,̫h4Ǟ}t8:̙*l6zd@,ܴy za;w32gǎ]w`6ٲu;7m `4cx<#+Ҋ˕w[㺦>Z* ʬ,[pM[a0p sn^/]c2ؾs7U5ܽC}n1lݶ 7W9u V>]Nfᰓʘ;ih}&u,\S tvuc2o8G$tfoYVHp:i8ȞXY $&$PSSo8Q[[;bߡo.pN}h8d2qI^kS0ʪj QZhb0wN9f~~kwˎ]{Z-z޻/|l߱6,V g*8uuEa}tttNZDRL=on|Nū9GsB^mo.ZG$8_[1L(-a/=0?/˿: N>#Xf*5$v ab#B!YY0o Nǒi~#=-}$v. Wy<OlARR<ل$IlV8/`= Ml ϛoǯ_{{Bk{;HrHHH7БϞ%2o?Џ 5% )ɅwtJB{_x;سwg1b15=H@zz\"1(9C;66FKk+S$%%rx;zf3^Errccc׾΋/|iSbZo:::~6;{g҉$8Rs޾~̟G]}=!<##jM*իbN9@KK+ee3Yb9ZM[vQ+<j5@gg3JKw /923ikkhصۙgKVf~fOq:tvuKbb,o`hxn}$%|Q?H(b歴ў]"J.Yn6y'y'/y_/(=ٳ.{lֽqa8tz/b`ߏ}e3vsa|I5D#aPdeeƷxFHa#TX\f3II.$Ib6`\܉bbxͣp%>^?#IT*YjDQ۽*Z_Y^Sw VCZZ n'^N8AeN'̌x=YI= A\DfRU]MCC#ѨLvEc;v(po<4NNNt?`d2å$c4XSM#^RSQդ1]V3Q$t:&A6nJKK+q=LpR\\Ⱥ j5Ȳ=:aM8J)xFFpsqf‚|6oنN`Z~$>l e"ZFzZ+))d\ NJٴy jtMJNv_ VKo_?a%y>y=SJL7+\cd˔)iniej^e_P8aIp#23zx LɊ52 tILLDR# 1:{hJ|KgdH$+11 O~O$CVPa;_p8HHp x`0` QzzdOz=@IIP~|z4Z qg`2|Feǯ V&VIKM彍S>;:;ٺm N'=\)ZZ8HzjmDY<⁉|X(\+%vؽw/O} z(LMEp۰mx<*)--eK~] N'߷T$v/UU5s}K':$ANGձevSyhnTPm$&$ǟE1pzc \`+yhʉEDD5  $#@AAIF4 Qp(~@HD^עZu0hT{DF1fGG9|_,1P@D6rMW,˴wtw::; RYj%)i^upؽwM͌j5rr22oB}}pb]YÚMLXZ_Sޥh$\CW6IR_P-Z{LIFkuM ]{X̼gILL`|OSs3?Mp%&#!r)< ,]SI(]FnNNB 9tM-F߻$g*Z,8v=Le U5klB1\2/:uu'!%%1&j5066Ʊ'eӦß25/GՅlfdf308H}9ܻǎ`a3?''11aBA&=fᾥKIMMAb(= +@0^}ֶvrri:deΝ8}7|A,l޲FGǨ9[KZZ*Cü:khimCQEMm)444Filjbڬ<}sШ5tF8v*攓,l߱zRo@QlٺovK}9֮CL0w q uF$\ڳںz^}uB0Ytvu24<|oGIQlN}C:>^- ^*zd݉8ǎk'NnHl*wRA zb|,Eswg޹}SSSسgH VY`h4y6UVdbnV^CK~^.@ 6zodyغ}goNGnn6n`2up@A`(F4\ZN(HMKe$'%#c4VM_{/`q̝=H$[+WSVvLw";f4o\EQ8|%3#`0 ,xqB ֬׿̙31Fio9?22 Ȳ̕W G"3i"VH$‰'2x$s% o}ERQq 3mD(Gxׯ0|0}x:&8?|o̠N㤥0y4 VYG+̚1iSX8He=`00vo-AA> Y*񐜔DFF:/<b IIHiڽ'OD"׍v$'c4 f@yRRRXw~ix}~RtۧCXf~{akZ9c:3gL'ry֮ۀlf~k7snN>_q7{ 6$3#F4/4ٽg/Ν3}j,/~7Wd}dfdCvkO~[>RRb;+35{ٰi3;wE@BbȐA7q6L( >SzÚ+fv=XAV$7 ڵS&r9 x׏伳\SUl>ip~_;y G" LA~^:+2驩¦[nkkhhl$//BJr26H$"8%%SPR(@߿jkt(Iii8y?xgee2ol-Zev#m`L2u] IDAT*~@#I8p ʲ=Az:NOʮ{@T,f vMWJDIVZh 1!!x߿(/*Ugu̜3ٱs7;vvSدǖhk0i҄ }6mBU4z=YY+G3{t;q jV\E(&ngv\TrZB;&'<ٸy h#ԎξJe3u6iVs}=jͼҟD".͝CZm3cجߔdRS/|fn #ϐAA@ֶo{< YwioH$AH$!! f4M#1!G^n./Kd3x $vaۑ$kğ󝿛[Z0 6(MMHNJ"Uhiia0HMIa@4ioGQF#N3>/D  1!` VwxC_++̈fnjn!mmY$C1P@0"1!O~~ijn&`v0 W%AD"׌tz=4H8&3#jRSSF^y9b!K\&FJ/b&G zRe.v%IH In#&h Lr3 d_'55>z=nˋ`0~WkvvVf3Y7cZvw_ IF쬬nm7齵7}N'N>:*3RedNHH 'Qx> C&9VM> &KSV9=$ϵdS ON(ЉM/aBawLk16,@1=yQQ!iר U& E\w?V`XY"L'm^coU>cۢI|\3k|h@Nb\qbTg)Ey֕hC!{{U2fGmI+C'K̶09U%1Tcn"Iv=_*W}3:]rO56z̒+=.{.^b'$1m(Q&ܑ&;:,a#  p[Hd>I&$b4Ò$ F`⧡  ]F  w  eD( p  ]F,%46=@Z-nd6"N4FEB 0XU^L4| C@ ,fE N4MѨ)D{q-uPG/sؗP[G 0ii*K_4`}y M!,FC(u,g;9e/HrtM*ϵ(RXsO'h@ӌL϶s؇P[jyiӦ`29{SgΒ&uP4^Ìd8zQP; BH';Ô8`f V#ur:Q CSH%̗I{k}w )"b_"@Anh$ҋYILL/%"d8VbgM}ggxBDz"$hdl4F$t/:H0ih(;T Iv3?=|kU+0=ӄ7D؇P@~1ӧN"!$Yz_4My]GBe Zc[~wb87eu{u.Uq-ބVU=ZsطP蒦iΝ;Ǒt_TKx۽D"̚yqhkWPTTHNvQW_ϥ(BaAȲ̎]{HKMH!NeP[[fc.TUa8? qNOSs3fLhFQa!9A> ^j pv<۹knk~I8t1-,MJʨv;wPWW9w.vFjjkn*?k8y46m.p)Ξ;+Vq]{evBgѣ_Iii׮U" w2evfϞ?oQUvSTTȉ9r89 V1m ~kj?o. ijnSM&]YYj5.ba7}y kjQU93v߰&0q]Ɵ_ SL ##%FeU̻g" w^_`]|s!Avc?_~Irou_L4[(,hx^MNKK垹_K΢o`a>'0LCeL4O{;tģBeuO~ HnnPH${TҐeԔ̻?~e-!';1AA3*F`<ģ6*B0id'}WUUwF3jEQjꨫG$rsIJd_!IIn.ӿgϢ(ɾ:YG$EUx Keec0^R3p̚9d}̟KBB-- ӎ$IEģbms8N*]l2WJԩ-aC3kK/~ɜYAMo{^6oƕ|Oqv/[lL0&&&2d0gN7\``0P^q}&23Me굤8vuu<#rz~RSSXaz`0a%VMY z2q8NG}Сz<ysykjʮ\CHLH}K8U pGuxI&< 7leK5kٷ@<9r8SL7Wd1ȓ?FnN6u+>ӧM!--4?qcq[YX,ffΘFUu )rOjj yy>wQ$ IE <^O]]=6 1Ldx; ̚9ʪtp'[kfxR"_]O2mrW;O(Nl;FGii97 232t 4۶Ƿ> DA~@H$BvvÇi<'?W&|h$aZ5rDwj:ƏsC B^rrI7W(|D %|%CyQ^;tE$ ?nD/xǤfsUnu7{{fUv_,1=MbfY$ӦcQ_Sz /jX?wŗ9?g4N'׬pp]߾kg6[ظy˻s ^pK|[qwAt:ƍb`Jld2HDw&qb0X,b= :Oh~]k4ŵ؉,IL51) Q{F^j))<^L4E!It:glΟ?d{),=ᠢÆQ\<Ʉb\đ` e"~$ ,lb $IL&1֫0._ZZ)))\(-h2$TRSo8~X=n2()J{AAn<{1~;{&v% pZǎ1?ʾ  ) p  ]F$64!a1F,zID4 Q QviF%q&Y;Ѵ (=bע }A*o(mSʔi[B+ zAvTX@d$82*s̶s5«E D Xu(ia3wjl,O  V] qWU{2)%XY_b]+%vL=T DU_[nQP9^tZ{L|5ONxITC'p[߰_ nHa.-OBjJJټu;nKS hn1š6EA 仭lpR6d|>;ijLNҮnEd1AQ"h`tZy,ĕh13:D[؋`">Yִ)q:w7޷.A>SwWYց[*/ɁzX#It:η=NXJݍt:9IQv@\7#,s!‘Ʈ{ N1iZ%KB4M#PW@0jN'H4ɦ㡩 I2ULUU[G¤iuziljFQ8.m& HOd2tz?V4 ؇VCC#mmXfBP_`0D))ɴUQD"X,0udL[NGZj  q\t:TU݆74( G"n /RZZƹl6 ihh$@Zj ,SU]` Mp\PU˅5:A2"ua܅^#Ѓ3t \K.DFZ: c\ƠS]]qcxGohSCb%oDFz:ܹ] 1Vjmkc͔W\CQ2YhMojƅ߸ N:.xG~~ylݾM8vzAb̨e\W^\T@(b]=vNFurp۽<MFV\g5s :}#ǎ3vhNӤk^HII)&r ܻh9?pm;v2aX6mF|/\ի\X @ss Od%v6n̔ɓص{/>}rƎʼnS;f4յx^>c$%'Q[[GvV&sLgFLŵk"Aˈ>B4Ma%X&N+Wۇ566FrrIInoEzz:fNnՑbԈXƎTWWϦ-[ƿvFn_x f͘fc>sk$$)+L{F\wlFF`4WOk[W3fHV EE  F_Tjaܘ\ZN^nm|>N!55Ke9{FWṟшfcҤ lش.RR<ʲKX~pjMhv| WǷO7JrrNQ#`r9Z,B.Aᯌ$I v; deeRYUo޽|lV+A$[Gp( -$'%g?ɊUٿ &Nh4p$R[WOnN6555DUevKsϜX,`|Ec4CR;[V!&Nh4pQs={f!6 .:rsiim=jPףuDUu nJZj2wv1|X1ǎ@$n'u?xGo0v(6l̯)ɔE$&MόiS߽9TUEezƏ&)M4U]$Ig& uc*>F$&o`æ-7 ϝjBC\}棽rnEQw1Q6H\}vð! 1:Q^Q'o[֑OK"\-'9ɍ*磭CnnMG"**Q;Fsr5O{p-t9ԔT,NVU`6p8n,Ыi7.mov|hznoQCq IDAT<Љ^'[xqʌ(,-LBBm~^nR F H~ +$aZX,p|'$1*DSv_`0`'e2xW˲fd2kNbVɩR=U0L1`|G|O5sR%CYfǍe1\,-Ce YY,Z0YYnd%6.SZZ=JJr+W̿g.7n2p 1b_pX[BdY&zB$Il6)wVU0x@ܱ\II47iUU|yZIT\Kz9V6~׵hjnF'ogXI sfC:tQFPTTa/ pWPޠ'1OQUn0 !w^*cCL?ř稪MNϰ̜1c#31Z6oFvVE>KAAåuu VG9pmmISSyH1y78~$/0hUQ1x<L@&[A.#2[ py#*_.C- (v<=І$k4MXc.F{>'v3 O1sIkH>4+4\/4=Yĵ:ě՞ωàd!"'NdiGgHQ%n:L8?F[p'XWQ5V^%/l;D5x鼏b2EYkNSU:yrL}/Lc6wOVxˎJ? m"xPWy? 6oLc=O ]RU/x|_z#mRT5ks߽>UF4rf63/]f\f֕{5niT%8Ҥr8ĥgLv՗Z2%Z%xrtvQ;DU AGʶ(5+O324BSȏ  w5 qWhċ7';6**j%6$_eծtgfLj,XF{w AO[XZuX7n#2:>4Fa }^4hT! I!^Q4"QUW9I }@#IÀDGQ(2HN.2FclS8MCUU4MCף棔U`0( ,(JCt KA o3FfL1L9zZ ?G_춽hTexk% Xd1e >O_fjjk9|8K[7$$$ģ?ȴiSظq3>BSs36|o~OsK+^o;^Cxq\|_~o`ێ]BCc#=O<55WjK=j6oeM8D"Q~ަwCA>.68}*IyO0߿/ձfzSXp>#c>r۶4Ml߹Ç1c4۽ڽ,fAb׮g>b6V56D7ACc#엱vUp$—$&$,-埿 ~?*KK0n\_?0?0eaFw,Tyc* ?Ξ==*h03OSoAA 4EzM}2XO{#Gr9m$&&JnN&Fba왼nRז#1D `f3fL秿UY3JWT\cN'v!4poXESs PWۘ4q<Ѳ;ɍ6VZ<(#GԙD#=T\<:PA[Bzp|.it2Hzp8b!ؖ1}d8Im]},%dY&`6 ׭tJנ7 #Dc/NȸcDŏ3v݃N?2|X1cFq,^0/z1v ) w  2fHV]jOvVFo1z(6mBOkdIbMF<ʊUk(:2{w46z֬݀jofaݷU*D4rrdB;3QU}Gc] EB$Ib# ٱkvŋ擙$Iܷd1km`ÆM+Keݶ;i fL$.^][ٸe cǎb)%n?b0wnSX^hGb=(ѣuѹ It$"FK_`̜1Y3!7oE'L: oR_Q>E/ tꙝO^Ze+#8M]lCWI-m7|x<^,HIZ|>Q[[GVV&lexN:M~^.55XaxI-}N[u%7ЊYil q˳m"t};)TH;DН~v9VFQ70?Q$P}|FW;ƷD"vɄ&gAmDuu &< ?|þz \MM-uhcFSXn, o{ fTX_$Xk''Rܤ "lAJl9)#,1Xznj)qc^4>PVM1K/ la> vLz8 I1N {l$%XeY4M#jBx> &4QL4 m^Bɲ;nA$q;hFXhiz{c1p&9@c%2vL&&S? (F $a3%%@Rݝ;$ItRmJ$MzSwWX$aK;;wwW3L$GGȽF  }DyzF'ɘ:aBG XE( p-rFE",QȷKdh;0AbA  mH2ɜiUY[5Sefe0{W)Xt0!EfrNbj fx"A]$NQ;PJDL viMlݾMrlje왤t*(* di`a j'"v)H{/Tំ۱ŊѾWa O<𦐆Am@ck=:SSYs-A_xȀM/vBi(|uEXQT2TlָUw;]Qd?=_/1p]ïqͧ/S֮2>d(Á Qhmkh0b޾|dpv/IIoeZDBs74p=\_STfL2 YD!p}æ-=ʪj֭="N^Pu<ް^UhɵkCOe%/O(L!Q >Weeͳ_jonVr<-axJW\AU)45 $\& vKn|ӭ*[R.-3c\oo|G1皨~#JrrR_ ?/#G1[hF _<6nJ{{;.6PPnh26~32M8y(Qª18VPqSτLϟ IL;*i2zIeݕ{,?$-z}}*⻍,IGO$"^h1#.uhT|޼颇#]_cWb#@+e|p{Q SI2I 'ywZ0oAAcQ @?D4MzjjkHO }[ٳ<ø\. \XJfF:> 9r(+Vb6Sv*C!#=k)T`h#xcJ<C??7׿l\Ʀ&yh3O# j:؅Ñ%;z^/`>s!I2.Kde~^3D"w ;{dWW4p'O7-, %&4]RU#Ǐ}?cN̟^y5V<أdgek`6ƌ7Wֱh~܏INNfl߹eK~zbǮ]<{ߡT+Wٺ}'ݻ~Hm۾Wo_(70r0OHOoPXP~]N;/\C|O9{<WBVf1p@{\q( .ɲ9v8oXEUUgΝo߸y+ǎ 4Y=v)hljAVRLIV #G Ksj9o\ͩ3gpp8BKk+f8w }lll‘HII1NcR.^bԩ.J`iii 33Ic44Cok'{u~IOOB2=rWp)]SHII  |[*|rspD"f`|{|ݱPdiU#r>@u^-4 =5B4EӴ*[Mڍ3>% M{ZG㵎A]w+h0ٻ3{tQT $z`;q\b;uM6d7l&/'q`z/* C0Ey?F3{ttfsyOtT&cwJ'xڧwSsvP@wA{zp|5C=.=Ve^#?^~'N*e%#xu47Dg ee2n:vȈ\.7-X:;:rVo @^nnf=NTy}jLYH<J)6 > q4\j]C `dpXV6+WoLAA(:^r)K/cdeeMxxEÆ5};K8yw㟒̒c41~6h >.> ~E^~u)Ë EѐĜٳX[U -5EHOcޜYr/]c`ڔI|^~BW`=dYGa|{?h4Gl4@"/'4o._b왌UcO/|dDxx"k ;tDvx e$eE/^%wttojtHJLu@uM-Qa촒r^/uuRAWs \PMRRϜ卷}NwvW8xO~wPU3g9S'V|>?z`!ox퍷X4.QQb;k~~{*˫]\6tG/ !Ln~~x*X,}$ܙ*bcb{զR#p9p UXzMϚ1C=?uNl6.S&N[ rq6nzd**@N/*Ym;O'yHl6cs~ xЯEe2:cCwd=Zn!6&S&xbBM݊(̝3vF=IIIzؘhJFsJ3vc%I"::x~H$IbxQ!.] F$1juH-!m]>UTӀ~ȬnBF =aaz bC{,p $L3IDdD"E:CIuddrID@$ô^IEP`FUCn "#!>?ц юhÏ?WAa  rozqAA6@ΉhoNUU+W9~dpT;*[QUΖWpIΝ?G`~IY~_Ev{:WA;gǮ`tvoir[immcXa#۶`媵lظѽYp_oR[WAA8U q\xjpDDDݣu ٹk7WP\41غ}uկK~^޿.;wSUYlfq uSm;v 2zl aa46]浥o@ڵ3gˉb)P[Wǹs6u2Ue-/'baڴ)P[w])q\=~V232y٧ioogǮ=?8&O@H!wPP_?7$ ?Bfv؎2HUU~VTzO_2 ćd=*\m_+j$(0.A"ծTN.{?ֶ66lDdD&L`[u#&:#:{/ؕ+ɬӹP]Ömۉ$:8Wyb?q2>x}zRox'?Y;\^u ӢcdR(2_VQ!iq]7w PkSJIgFefPm~)@eIrcB틅:2,Fη:ye T\)ifB>0(0̑V_/{ea6xkJHIN>q( CoY΁8p0Ǚ7b#{`mKDEF1HNߴ(tnFz]LeFs÷M4}=n&M@ owǁQԙ(~eܽ ޼e-~Vw˸x4:~q@̎}>{Zƒ, m.̚ Bt5XT8&Oh4DX±휿pԔTΝ?OᰡhuZ6;"IMMdff0z(V\Myyŀ eV^{O/\>=/3@ׇ  w<;8==OLHhx!koſDll 3M`TY)կ)㙧>͘elش;voƌj~Ixx8ӧN% tz``ά\?h}X-~_ IPŤo,r1yD ehAo._[o|UBLL4sfθ&Dl߹-[Ƽ9(ϻ$_fope%%WTokevuXz ͛K}}W,Z8_&"¾PU>m ƌ?~ +*syaCd \rԔ>fb-:|`;wW;f/y=يJٷKMMECY8>V^ŋ|ʨґ<oa ʠeWb2)fp_>Gd7\N]}q,~!RPUSϰn&l]<" %mNlNXh( #'{S# ܱ rs<(=9d$EÆQ%xm}s9}ՉG1@ hP͓qCCs._fh3c벱lJ2ҹ}Rms{VYGrR"_ȏmLF#^6h\,޶W^{>aӖӜPMe9BL&N<@]E(YܽP*3“XV w JUU"##=nW'Y$#لj=`UUr ~߻ c!Iix^W.CUhZ^/]]㼽:ZM^$|~jjN>dH>fzz罵b%Vtz>V;ǖ$H7tt'UDD Bo>qz='..nW%3#}FyEDEF(FtZ]UAih0b48t߸!y}#K6lj':yKy`]2328r8MM\or mmwFwQ\Dtt<K}LdݗVoA;0v([9shll9Z8p0ÆcK!'{0w-AAO2\u>aXZ[[dbάddhX0o./:/]F^^n(\זɲ+y|#̜>7W^#/7ŏ<tDiuZdY&$DLL4W?KB&NϯF-UVB{G;?/1M,yY3_&Ǝ٧Ġ#IAVYV^]a>g _>g3k&+V!~1Pjʎk$-ϟ7|>C .7?x?ӧNf݆|?RVZBqQM& 0\R(6\$݆oN5}%wt4\cZq\$%&ލ: wHFcv:;yK]~Gnv;C?y|>ٖ]pTeO]bspUyo'YJ_7ޱ|0do~s8-%~S9*j\8l]UMåF z=7u#oꨨbֱ͋AF|ROp ޺Zm^JbY Vى2za\OC}n )pI=h-7-,:8=,f?hv/s [°!\inaޜY/)Z'n`ʋF*Zm>$(0Pk=ܟed%'.w2h(M2@VG'V;O;XhQc⽫^fn#X}N2ih_1"7Ȉ=,F Yȏԑk? pmI&I~C$IV"K)n/L$B4N!0eYe  IPEԝIF?ׂc?." 0PAa  #@AAF  X%‡ίhRw$ ,SCgאDUUZv7z?ޤ:"bfOʾ+jQ$ȵ( Ԋ؃\9xŋG/PV//H Q ;1q@U90! r]Pw^0>,~o4o'B .ot9KCD_V|( _^z[&?:~ϟǭ]]\wgcWTч?^!˲xG0'# aZ= vF*5U@m>~6҈ɠGSY_@?p`6?šێx)R6~8 B#r^ehq0zϑTEwTr[|߿?<3MEQ|>*//DGGzWM~b=)?q͎ .6FW]JccMWTVav_PN:}C o N'j}{+1q823?k f4hnؗZ0hdn/^WWBx_fH"OzG'uz"*.7~IWFl/{n}K aA?%{m 5u;0:UUio4~FRS)-lyMM1{h͗?'xg!!!z6UUYMJKK+K|>o*Ӝ;w_QeȲj)-^n-yBoϓWo])( 8pLV+'O[_*'NK>?y Kx(e!255TVU=xuux=#ƍ%NMosRSٻo?_穫`ޜY!(`L7qcFc;x/Qp ĩgׯ3QQ %== ˍ륱1g1C ZGΙy݄X̌iS3k&X8]ip9]E ׬#///}᳴nVBBB:dW'7;[QɈŸN.23oѕp}dgp)'r\]{'MxDr%$IQԔdzq2vuGXi)ϛ{Xj ,3w, זؘ`9au:$I"{ .TPC3@ hD`uٻ% $#/y-ooc鲷

6pEg?x? ؟md d+XK@ p6 ϣtE0AAd?~A=@_ We{5銈ctٌt2䗳]`{yId2) {&R1J%Cg+;ON o|8{gf_iJ[ҋr-=ze߼i;G0brj]i~J׻\K]A"Nr- X_~CWD?{/QwEX˰u7d2oΦL&C2BP|{hx]x<箢Md2iT*ծ4-/+֝l2Q]]h,HevvK.O_gN{֐L$1Z(y>~Çbq;)0˸\KLF\Kn>췜<э}|Bˍe/,Re$X#Nը^/#c,GT*da8yqRvvu) {~ӟ}Smnnt)_D6#QYY(ӯ?Z& eNǺ:c6 ?Zbd$dl~0KltNp]'xxK~fK\juAe _|IOO/O255}|OXZr* ܻD"A&azfٹyN~'] Q/tG= dgeeeSK"DRKF"QF}\|E&kWrZ$DQsEWG>޿zӅB!G?#v!]?04VFX tv #diZ[mxbܹwWd2gBvd*E"gpx*V|>?tzڼM x/KiG<BT }.D4x<^(..2v{< J}&142osc^yK"VNڠR7aOn:].\l_/~銘[ipcܽB&9s4q{ܸZ[O*L4Z[ɧ/,'LNM35=ZA[D 5g9\!QT{oǥkQT:fgigjz3I ŤSiL&OmM -ܼ}zXfffZd%C1b*VFc8{^׮pUR)i9r\iZZgNp8)+-i5ZRǿu*))jFY V8$UVZJoctE4סh,LF3ocSYQMzV z=﯁P=,(%fkPTr%>꥾t&-+^PhdpxzRJJ (J TRjlb^`&nn޺VPa5J'd芊H$FZ[6mjj`5@TF*-Gc0S_WK"d R^ Cq1mm-ܺ}Rot`0H&ؙӧRzR|x-+-chh*&z~l'7 悹W^ Vk$ooDVA*%W.]Ba:;ƢJHg\t¡C@n_1\A*Qj&hcl=FfGG&[ݦVVV(//ɴJyy٦cviǯdvz{̥1\"yߴ>jz}zvnn{|7? rәbSS}=sd(@pyV@ 9a`^._Ͽ<6|h,F< ͒NɮәZ?ҋd( 308JyiY`x1cC1ٖu{^~e= ˦E o /$V,21w>NW¢Obt̎lL&y<0RybEc=B%oܢX(@SWUx׋D.\_x>3Ģ1ffehx,Zw3OF0>P8>'|~&#>"T*f6&5FF,J?bb)5U&饯@3ŋ`2QT p=JGy;} M{[R4r>egqsg'JksZ[`n~h$Ѯrx}`Y[[kW#Ɉb|kX-v/,"ns TWUPGՏr"=} ܺsJEcC>b>N8!˭.qmnϟ,g (VPX*aqA`m0+R(x>f>J0t208DssG;;yNj*-#F/P_[Z@J7|{'O x%2IvӅ%H0994@VgYY ɍ~ɷ~7LMϠPhkcxxTAa!޾~q/Rt\mgg~z=pz\P(1;7F%3?B=:LF#%%%!tzzu'1^ǣG=TVVbXrKM&^6|X-f ϿL&~[8 qD,]!W+0MTWSRRB&l9|&c9 uu{\wƆO~FccC#5W}b6QŰG*x=42(,d&)sD"*_PLOo?>P(L.'xP(D"( ɄVhg.F[$aqсBVYɣ>¡pAgrSPleec]@Z&%ڼ2 m-G>ڵ^cAylԋgv^F~׮^tc2Լ;( ]y3{E"۸+ JWUaPm!Gm#ctd@QF' ,Jd( r%ł\.' %F `k^9 z4pǃ!xLOϰ5@ l b=@,Y%tn` HPZZJ( ywE*9LD:pQo`wۆ=# L7d/Qb?&6on]m. 55L$sB2NQc6}۹$ TX,F&!Lf$I1Rs+6FuØMR)2p<CÄaGFH$oC#:F<4qq\!J\ڸ4hzYǹ̷O>{Qer-I2L^d2d2\Y>LH^٩ IDAT`浚UFeeed2:J%---EߌUTjz=w=`VYA&AP27[}v! B*if1xp`(DcSTJN6}ren8/in߽O8F#H$/`69y=f|Y2V 7n&Hp!>f3j #VQUeƭ;'f>j1R((JZfdtT:)ſlnլ\A,kWX]}61=3K"dued*yYI|b1B0+t:͙Qr4r G&*ɭj´ǟRS]ũNtrGZ,bzzH,6()1;Fyyz= S77O$h49rz@WFbazfp8>#pp3xJ>x*%7rÇP)ΧiX ' Fq>R!Z=@i9rOmm5O2)V\  DC]lTXTqho_7FG{Nk"s:yb"/Ux,CM=V9HsF#Ȼv2*ٹyJ mvvngj b3?wC#xiR)Y[[#3;767 \Kny=/ LƱNL&#5IC_b AsRp{EWm,Sx^*++p:]TU0 rybK4աj<}Ӆdl2QVVhĿ^czf|pyJKY LNMY57QVvw-H}FIzEGWuj3ۓ~h,/e`dD2+88.u>H[,-G9}$2@D"Q)wQ/P*I6 sG_FNwt:MJqq1}TWWG?\z185CVϞ!^7$ c@ /}AQh'@[ Ea`ީ<Av%**rs9^;s*N9NEc{ ;;v4x\{M,ͻ\wΰit^*;w]ύcp3բZe:ƭ]o8NGFd28~^>nܼ]/ng7#Jp:_Fm?ldf̼o8^䣀ŷY+Vܫod  xUd=e7xb U6#c,,,rY b6P(,,8xqnݾK:Сft=պo s-$Vkjii\Ց1,f3 _q%4 wޣEYD1,//ȅ^Px<0˵$]? TKWgn)_}C &Ӆ㥶9Ν=CG,f KYY)MMBaRnݹˏ~Wx:9Zh,2}u }/ĕKs>5U>*V?xD(̩y[:s=~'\:'Sw8CDc1~{ybՍ%݋m ߌ'?Mܾsgn O|?:fW~D}]-fIWQ^גc]1q-?tRNz/NΩT*OyHmd2O*{'s>5|}OD 5js1 椦j뿶kM(R}76x<^VT?vnZ- 4j%8D%C}v|cv;tY 04 vFFq8mm9y2( \KKl]TXR@PwѰFh5ޣ`Ej5Hp$Jt;<:J:K;MP|ޖrEfG:(ZQr&?٨{QQa_OJ}:2:W/`ۿ|N,f3L_AG9].޿zD2A<ghdTA/|k1,'HV7 _FR!Q\(B\Z\T. .?qX߭|un,J7KKn\e >GAUt.{sxىdfٳbF!jy ɶ9{3 Ř&hd&O$y<8Rſ^ǃ=t:SQY͛)-+ehxLƣ^VW芊p8Rrs|!".ǃXfd/:]xzh,.:p{<H}]rhoSUeCWTĒǻ^ւ\r@q^ >\AWg NyK4{s\{z9qã456ŰO_^8KШ477 W\`wFo(**/zǻae$p%Z-8uDPP^VBs{[$lp h4*$SIΟ;[`]ZN<' hNtw V 3A.]8VXQO^`0H[kKL *[$ W/_;3d1MR R0=^ 3pn]I}ǵX^F_c܏t * N?/|u nƍ~Wl=mb wP£[L=xXX,Rh,Hs$efv33Dc1*eAyP7ʗ:8 ,۶E*hgHzZ G;;vbFTR]UÞ^BЮlJ7N`6p,,.QUel?V?濣F(nV>vyd@Ka4Qa`|bN`hxH}%~U7nK_Lr‘ eee( m6&9{ 2<E %UJ9|2jkI/ @ \xl߮h^O)+- /_zeIN*T*Ֆsϻ=^`,ou=l_TyQV~f@eUTG]m}TvrP!lkWpnGȿL& äi8pl6+q8TP(D2,/f mH f=fyy 'Zd2>RىDP(D*ϗFLNM77~CCK J?(}2$ ֮t:M8f||Bl6nJ/._v;8.=xfY^^=AKҟysl)r]6?ϳ~j;luee)p8L$%ɕ^ >7WF{&)ukoႶyߣv,: TUEldr "55UҦ#tvJm=:F4>X["t-QW[CYY)ˬJ޼u}pdrK3<:F[k+q8]$Ifhkmo`4^tq vQd2D?E>$tqCxL&ß [w@VɩiZ[$QTZprjƭ۰7}saX&2FFGd2lUj0s b4j_|5?ߧfg8tEdr(nm`L|95咿  MQWrh4Kr*QաQΜ9%72:FII 5 G54<"?1C#芊x['heBuu5\xRYP/FCyy9pX,NrH$ C(JI֭]Wh>36o#QNsg7r%R\T2ť ~Dc]fbtsgOZq--a18/'OPb(`g,Qn>m$ 'wSl(&Js)dCEEEFƻJ&V15Z%}XYYXUJkkRǻU5UT*UV"626py9q[zVUe>>!fM$TX28MWg5&]<' 3Tw]@B !ŷچ'x"}hhi9ɏlH'wKu [U)}_mcә<h*L&P8?0m]mͦkrF;d8-HUYQע6y[x00+3x,o:h>j&JQS] B^f[gN`?bLco:h>UfL@slzg~W*$IѨ5 ˯c||BZ鵲*ʭT˭hw ]zeI 畵2v+HH&\wX˳ o֝|6汝gGy߼Kٛ`{űփ~K^'_:|l7!o/}?K2t;w_^ν|Qv@6pWWNb &uu3=3SG8p"WQTH1얗Wj`URYY`Xv\yxs&3:}+JKK)..fnnVp*{ӧ[-Gv ~hjlWM3Yt8|rC?ɅcBˑÌ9ya"(? HRWkdhx\Qb}n<$NSW[`(Dk5@ujf_Xs<{8SPʠ ރ#@Il#3Yծbg]^E\Hqq؋[]\ݭv4YծV#& a mU 彽? . @ &?]|CÜ>uJS  NP(6j#4508:%l`i鎓ojL& U'Ǐ :u_wXVMMFAgڴ?htl944BII1ǎdq4&qmv;+44 IDATS8~-u8dt: UU͏?ráab8g<ͽ]t:?֝NM&~C,/ηI~늍`(t~}gFc.M&]7ȫ̖''~k8]vs`0 7ZKx^._ȥH&>ԀR[:mGhok#JR)iG[XHeesGFx<\x#t#-A24mmx<&&xE'JIRkG^^yyk4+6;o_dxt A/PUUFεAGd#L&yDD.#Vsg?!Ξ6v#h[%%d2 *L&C$v!}}fCRm3ss FuuTs/ LZsnJЪK2h>sѶ6r"Ϗ6f9gdtbْ<\nuW6sCOe:Tvavnh,FAaRYV33;+XWw#7OpL&#;ں~ŦM# 9rە;^וM*": bh4ف @^DXF <5EEZ@VSRRT*bP(DmM :vTdD.c2d%o]Yeh4FAsN&ZŲBC} y7ɐARx"IeE9kn5մ47qC fwaqI)6H$hZ q(6edt#p8YXXΫij'441L456Iz2VW]*r{A:kmcWYQ)>rLJ(FRQ\lrQUURDb2e"*֖fd2٦x?NeE-VI>lfǩSkʊrt:&x\joyǃ}F#cTWUb2kT,3LW3Rݹ\.Ԕu`XsOC}dRWhZJTЩNE*T*IӨT*JKKi ?D".GD8熞ێP&#i nٯ}#-tvޣA'OG"?0(^ݎF^OkK3)GۙXVd6=o,ɍ^eN'dgfJ p:'OVY\Zbzzr&'1 lv|~h!dR?DF屴$%ܹsX<pJ&A8֛Z";շ//Jlv;C##OdolH& FF$I]̆F9xffa,*b|b@kG}Q_k'v{iEkv"fgphr:'r2d"(&^ %P_G>A~~pU$FDdkjt:M& QwuPimi`uSN0>1E<gmmU#-Vl6;d"kkk9|AΟoT*Ŋ #OP__;_}T2ECCsm#p8kW!ӳd2d!Vkn,V+Gp:ueT*h$B` x<^jkt޻ 5S$F-$&(;6ʊH$L*cmm J7X+ qcZsz|mochv)b'8h}TWq[@ ߩ'`zDJ>'( <)h%xB,HRmgddLFJ/qxxblDDvʓ[&|O>Ç顼;wηXw_Bɕ/񠧗:wuVS^^\.lpwqPmGCaalgzz0{8ur dn~J87LaP_WGUe%]=ESXP@0`УV+,5"/.5TWWa6[x84̯ߣo`J.]zi45\%/OMOoZ#-՛4"PW[K]m&`LQo6m9 45FR *.ӷnF!H$q8>‚B = (-ɚ z4y7֑dJ$6> œ=}F_X\kTWUx),(@"R(zJp)Nb#"WlϘ1r_O&ۗ,_޺Õ/qN'^Bm!;Nۍ` u9T_OsSrnMrAn2݋ZS]y=P읶qt ȋ`SMeq\޶ۗ.23;'|T\<n-"""""""" ̕˗H$/fcl"Qyuj9'$pz>)GSpѱqzwtND0 e=q|~?dH$#055%D'x>btxd2|_zI3L&d2Fd2x-Z"/w'7O>೛_03;'͍$~_F5 ~9F_ܽ؀[9gCWpdSQZ˭;q/Hp$-{ v'LJ^W7FueK64.#؈Ÿ Xu²RAA~>zX^PUU\ LbXj]Ƒ1>ǎYX\XTd5V t A}]-|</E*RUYf*c1>xkg& ɍP*\6~vX,?`xdTu'`X H' p:;a[<*#OƵF}]-D-[-[;1TVVx-R;WMOϰġz>qJ  ҬjZ,+?z1~`Гs9J%} Ǐbbo:G9ib8;V8}ԦAfZT'HgeG$6""{0Po z gͳ@Yi dBAyY)u\Ne~ah4J8JI&rY'2d2AyY)HBV[ӹciټOBKdra`0ă>jk\=$ JKK䳰e\8 M'8BA?dJЗq'#-͖X{n "k[6Zn}*Ѭ+ aΠP(p\aa[$ \x JR-'Io08yQ\#'؈ Ҫ-f60s R)fj[w8|F>5ŖH$ernM8阞X1!䫍?ŪU*,+uuB}%HPugZ-h̰/mv;eLMϠP(y z=tBarpst!AƢ=E/;c')O2CiCD#a JDA7|0ȋFnsI oT*şM2gfeaSc'b^eqfX4j.ǘ`| X7d얥xۓL*"""%""lkj3Ꚏ`Yzll;F_u=FDTRJVGie ZsEb(-N236FV05eqeJbk޳[ =|,>|FDӫP/Q$^C#a*N* % Siy|$)=SBhn&T@SPHuC#:*(*.Žx.L&s_~wp:ݶlϡ+MUeLcLMMxX?{~7~{]ݤR)s?ܮ_d>wq]X(-)Ƶfiy?Z zZ\FǩX]edla6[pzh?ziR$;Nq~n^˵ҲS'().f|bd2Jrex/;aphxKt<p8ˡro6௦'~Lmm &c vUǏ3:>NN]~l0p57Zg7x㔐jkjw:ua,20H&aeƊ͎J-oocX ¼ypΥe~[lX995C*h,µd,kW-/Ŧ&r~jPljBs=~@ȼ+B yH0" q>GF|\<jWKNimiNp:Wiok119p:WQ|LNMD LLN!@Iq1S}Q( $pr9^ jkj(*2p nh4룴XЊW)2wfxddzz^q 0ON'x<. bmkuUރ3ٿџ |-W%%H`Kz}N._@$%22:F{[%%x>x^w_% 1<:Lnn;XTDC}>?Ug7rH~Rp΍WsF8F}su$76d>sǏ/ܤ]G{ Lj:;GGuˡ huXVV(,(DRbСq=yt:-RP(B.'LRYY㡶&w?! 0,,.SY^Fii)ãcy5Fnw8~m$]7S Bf$It:-&d‚&D Zie&D"AeE9j'ՍVL&hd4H&iZZ[q DZ+jsbKsToѶ#cٳ~Fв^/*˅n\c]mEdjh 2VwAχ\.S~tZ#q{k4yLTeߎTGfjzF5d2ỉ)</ξ;wСg:cmMC.;ҲYȫIn}QM{Fn5͞[ϼ"7ZOҕw|}Ż\F JDDDDDDD ٵL,=*8-m? t2==x%vN[.L ݶvD^fcq]/hgNr~W/?Bh 'O}x8^\N:&0>>AiI)xqjkq{Hk f#V_Xfff_0a4p:粲R!WȅY^??!>!HrZE(zNDzNUE):m`ۮL&q}65Dkxt:fPF/ ??@8aչ*ctl*T*X\Zf|bfb1F''r*0:6suA/W {`oݾC(ѳkww:Q(*j۾%+?O0H$D"HP*n7K&I8{M_)C!~[ovNq\x>D"d.XҌdzzD2If} 9—ph^37Og7|8Dǩ<!H f׻d2 dp:W}vμy`/LRIee% e^gD"|m~Q%4?ɶ:yxRI}M%Ǧ7mm^ >X^ZJuUӳq L̲bYYYIiq1ܸISa~?%Ř-V CX,VV w_޾*Y̾$ϟ}@,/`H454q\vFn4sxGY7Vx5ZW{KGmGZq{<0LHRe'B!R*%vNjx >|]*+*( ҙ45&&'~׃ Vz TWF_{ E^l2 )eehZw JXظvm~{kC$ro|GǶp$RNηh;XWtZ#u2<:&{.dWu"W澻K5U\zeGo: IDATkʤ|z&*X4ʊ JPU4D8֞zr`M54a2)G&‘T*zZAAe&7Wr5'< 1>1 I/ Kbq}{c<!j{K–Y>NC27@eE9nc4L:49P\4sB|Sӳ|?ϿDVsAep:~K=!"&yχ ?NR^glf"{v4]:jj=}{/؉{!?L5D"cHwwSVV,@@ @&L&tD"vD ǟ`fvq~OO? h>KaۆrqY|>?`a"jwXپvd |>ܸ"7wis,ǵF&!FON7AϕJ6m?x'B!\54P@0H4I&=;l䁍6 B4N637Ȳi VzFcRM1龕{M8Mzzfm` </u/GƉFcJ_{]x}>,V+| վ*N'X\Nii GcG)'  gvX\\8NZ[y84>ꪯ%Z&ZWg:'hOn|#g_@$3<2ʪŕ)((ͻP/K82:#o׊@ ȽݔR1r>xl!IiczfoUo6 r{yF~fvIu6$dÉ`DQoNG0\̪˵\y$t]h8q8jOnj]cdt Jf%ãdi(/  "Jt=DOn|F&vU8u򄠉#;֠L&{dYL2*H$BvAaΟ} TJ_{הJH$V^h\P`0Q*8|~,+DlBdh4MX"T*E<'//X,\.ǹNyfbLW8W6+Je$ hM^wB0s h[ۈb8NܹKSar9<afvN(fgXuJ9d2sE:hmifiٜAO,b &\Ҳ}]KVI\!l_ c_Ȁ\5έsuL(L m)8H$}MH& zJK8in7PX֛N/W<0Ć&wT*ȲHdrV?ڍCr$/mV5TWi7uƖ;No_?;Na6[0oGWw7W޾gZP\8Jښ'DcAMF {m^/5ms$"JP_INwh{p,Kou58 =\ʊƕq8F\]ڎ$I\#JQW[׮^ .kٞU,:-.ǺbPC=TcuS.z}WS]Li[¶Kx1 똝P(JϷhaa#-se^^p}>WZUڎh0u+鎓1D鎓(U*l6Seٷ().>`&lLvzHejr+chPTRQ^:OMZM*dشomoiCFSaa+-60!?Q\\jϿ3ohdlƹꢢ J}}-ӳLF.^8VkbfC IR8b\^/\񫤓y-Oޯ OVn[Vg$7;&a>_޺T*%(2ºb ?FۑV;JOo?kW߫2kvX]]ř"?_PIgh;Ç8u$;N?0HCf7kXWV? FqC<^?x<}^&MLuy]C\LEE9׮ ly߯~@{k_nFx=qً* K,--s c{G7F`D"8wnwXd*D{/M6-.-QW[2ͽtJDdxmx>F'Τy=}\k jdtuPYQN*&HDb4Z[/.-#9 s MF#t<]{}BpXZ6[d3V kܺI4}l KKK`i̩od;T_Ͻnn:-V V gϾh|E/_who;C!6ښL&#xw]T )*Yjkq:Ww7Ox⒙H$[oOnl {(/+l"94d CQΪkw]$?dh2ꢼd*%--˙!"򴼶Fй60HH$p8G*x .ǹp?R]U/ܴ]"px8Ų%C!Aܛ[uqsK)-)>fzɤbzI&Oe|zꪋK3==;UƯ UDM)-)A* *U z=wd0M/VS7疋2ۼ9<:F$e`!t1Np8^<>|]ffyU>?KaFD¹\`͒E `.JZ0Q[S%R[]MUuw(k?`X~ hTCO_?ϟAohBZFqRIcM%In`0H?yhvg'OϿxl !k H$(gn'rt뢴b RAoϟ=u(9sMZyJE>["C}u 5¶j*Ofvn"JuU%YW[whmi3:6>x**ʑV[]XD(TJmM5*._9H475Ƅ;E\KW-_r|cfvû? ΰlqOUGgic(r{sQ DC&Y'\SzyeAٞ]D4}NqKŖ7Xtu`X{>Dh,#c$ wu3;;t޽8kWzzq8K{q$ٓ<;h3qAURԞ{;=OۇAA|4wby2w?˵sȋw~Kh4y,-\8wiN2>1~W76DBØLFb82NIq1ϥpZ-{9Ѡ" __dH$"(z{d=zK,ĊFqiDZٵ9xPTաRx84Lǩ x Cb+쎒;B$#u((jZb kn7~(2vZ▋9ɤP`(Sr142ǏR[S,dS'y84,LUs 5YY FGss#;N݃B0:6? >Yv.HM\8uة%Gd2 rH$#k͖_ki`f֜U@>|=w_\&[G8AR!M BTTS[[n ,,f_D&_XZTWUH$p=p`(ٷ8u5ׅܽ?{]D)UUC!8B9HH;5^g"{KmM55e+GuSNf9zMQOoo@oOQ:ycQ~-h_NJ :W]|_n<52LwyY#2d|J(^/%%pN'-͍*smGP**tZ-p=TUV O5jk͎S=Wܩ#ZV*p\t{g?3].Ο{6߷I&Ç~鎓T?1Ev}mm)9xFFǶjNuj8X\<[rRu_g4xuW)W60 kƮm`DDDDDDDDD^mX\Z~f ťehj ?DX]u199ӹJ&v\O~Ls @<gu% 9N>S3\OLNv{d2t*ֻՙbX-V ½su#V~#F|>>ɧe_ύѣt$lʭ|F8N2 HH$")z/\.M^^oS]6 D"$IAl]PUωdrG]jbjpn~e7bi٭e‘p@ !~?~T* xO2</TT%?_et|p8BZkWwʊ|*=x<B0 2_SoP(҅st?u:*3ss=,+3ss\<t&nGζ#x^ff樨( P0$X.XOndlc[;x/nݦb7?#\;׮pZB!zv=4ey>@0$HPSS(MH$5Es9 TJukq:B΍ų?MVIYمWUU iuhjf 55Ÿ XZ^|G &N'dvIZ[-k0IH$(+-vD"N} 0¹ lʢ @U@tv̛ppJHX~iB ErKQioཷB9N? uB̓yN{oI%j5B]m K>BםCYYYAFHҨ*%IJFM$R HR^t D=vh&zn.UBQQ!>JBrrqw8'/#+"X[ ^DžvxR)J_X"'H:~y}v_A,=L$x9*MwlQ(LO^;hZm6}#͞# #TO vDJ#zYj!KT IDATmzr Fדc%۹p;:LxbD[s>~kWċKKttm;"/pťݐ'n/D~G|B/'}Ie<^;mI# R3p8r(iChTx"X뛷$?@aAܺCy[aw HXPSc0s]'G" xx!m!r<2eff?S]UTFp$UUtR. jprD>$&ZByEK%t9'J184B]m͑vRQPP@?'&csv6=^!?_()1r}:ͫF싯1#c m0;A._dxtvJhl8 66s#b8 KKByXbp܄pTEb%ޘ'`B(,( 22:FYY)|wuOL"Jqon ɧghj:-^"vyu!ȫD"A*mzdXe:H$B o]]*Ffgb~\.7zmfg/|*6tl2۔Bpك AyڐwRP~p026~ y}QHҌD?'чDmy54S*bvvw߾m]vcd2z&B"Ⱦv15=Õ˗!026W7f Gp .^l6P_ǥK- (X(+-u*+*/_QGpƆ?xHAay9sq[hX;Ny̦2d2uuP(~*ζ4?8ĥ/F"u:NjDӱnئO56,PRWWȁjH$$ $Ǿ0~l2q@|Q(++Bηn}jE*RWW-:jk~a!mrn8ϵ295m2vlh5|{ߥh|[+fI+_v~U8.࣒+ǃGTWUiq^#6=A+|~^9!gft9PW[sBR]|س?Dw2ԞcG8]ca`>+f/8J: L8fvn:z1J $)ΟkL*[gxdw޾#Lqt#3\no{Heonc'(,,drjzkA{6=V޾q&>P(L0?k(.311?@ H Ͽfp^{z W ڹť%%ȋayyK8fyeV8\.k{z̃9>ϫƗVNN)~> rG]B|ٓ<좻'n^pA+ }a7U_{#NvQUmX %z{YmF2$70Hp8]tv`6qL? %%%?8Bk&+ ϵI(H/204D[wBDQ_jEc a4C*4GXZZan'#7H KcFAεQ{> ,PXXȺ͆^' 뛷YY]TC=RTdRq\.Cn'i.&.Y H1Rc(rF>KJfGCosVПy`(CѢVyх^g|b ÎllCDJ$ʽu. >sL26a9AttX\\bbb *łP k묯c1{f14 騩h0`4B.SW[^`BL2*),,;n<`m}ϡP(%ε104շ Oxx\p5Nx#k֖fxxw'Jp8\N n\v9~n߽qur)X,&=}4&&p8\pQ$ Z*1<2Tx""JjqꋮA8&LRUY^WIa!>bS3$.HNPZXZt!%Jx` >tfiy_gii>`STB[|rHI` @P>G|mI$ߵz8[Uӆz::y}< gK*j[9[sc}O `|bTVN&XÎNR)ZtS347=Wx#ttvk;>u FCC#Ϝ8QHH}F;n9_Η^S^'^Mqs>@VuJrG\ݰ;ture-m : ̫Q&TJ-"""""""}]""""""""o2o0,,, [#026<_"'ܼDn S|N:K rb98IrR6(ϟ] dO" ϡPh{{YD"d6xBalvb x ;tu(|laN'#مʡXCPd2I:r yW^.<^/OD"AO_?]ݽOypepxDX,.JBa|>?kx}>\.7L-/^TVVVoXYYP(so_?fܞ!wzD$@,sp$B:&LrAl$IbͶA(byy޾~2cD" yb1lv ׇBa߆ݎcumt:-_\_uyXr$uC1RH$L*#L ʐe(**S\ˍZB*f'A(:`0H$\?M jʽHº9VP6ys¡0pT*@0yiGH$R1b@R~ Lvߺs-ð}\}-6v7]B|>sIp8YٞIh@ _<;tZB0TH8Z"$SI"VMPX,F8ޚyF$SI`bvD<'l{xBօ}dR`xr ~f ˰~'F$hxBA"@*"H$ *ˉF#Wg[Z]@"/Bŋlz<\x!2 U% Rt:tb6hx XHͦ2̦2.^8O2Rdhn:?Ds !T'?[fHdrf}KTL:D"!JLQGpׯ Ϝ>l`dt\AJH$d2l~ rJlb6Ѩd2lY4ft&[fl|9xX\jHC]/ y+ޯ25ypccmm8$A:AyQo)--rp8uh54E2`۵ѭ\ssd2b8z^ i?z=_|u-i>saRP(pX&VVuHPdX,}RTh q|Z@ V%JIh5\&C T*0ZߏD"A$ SUYeeH$t)XXY]EӢP(PլjH7KuUՋG0$Nhd@,JOOSl3J@N}6:I%FC4E&˞{IXf9hx;o_  RTN),,$t9$ uTլc6DT%A.b^h0oz$ mg@dNK$(Iׯ]ıq\pK0g`DyȋFW&;A'&i/+9 \DDeAK"/_o00yxyIR8]-s`wbN+('}#<iD^V `]={.,>56ؼuaG^{126uaD";XmM`c|F_օERC#x}>ܼܽ5ܽu+~٣.!>^:Mtua4$gWCD"ѧnݺ}wQӽA[!,i}21=3kXp8Dp?8ag> `f{$3a#me%qqq_VjM2}n=xq|r~?ll&91o9pP@4hh4J4%NT*s>cx<GF|ttv3o]`(nw#PP(ė_FFY֝{~{.,T*g`hXkl|&SdiuvSPO g044|,#<klzCAg8DOo?j(.onFcc=txdܹOWw6>BV3;oEt8ӍBʊrel|H$0z;ۤi*r gd/:4j>-z03;ǟ? ?kC}ݞ}2 B<iNwC m;l2+ SXD.QXPbkW$}jfbaSh5{q8ܼu:efvMclT8N 9>`0Hww/hI46ԳFC}=;hpɩi*++bW?6\ZZrq#M&~?S,,.Q]]g_ͭ4 i{z MLݺ9$ 3gN68;7GKsӾ%׺=t\.7HRU@XN#cC!޾qT{`0D*i\n7xֳC!歼۸77j4cvΊD"ٖOr7tȤR3lW?k8Kv7z"BHn\Ӈ"JQTT!:$DQ>@+mO9RRi6\.b1FD8mM&\Nmg4ay]{ښjBЎl_iaa!P(Ĝ;7S\\,V\[l8\l87;BPe@432|`0H8fl|Bg0B&"B|bcw8|!;ᬭlTV&,߆!磸XdaLINjl #c#QFA \.l'-ί3Vlr?̅/]{y8ťB!J呅r `Qk\MJKd2\.7MgNշd (JB0jt&MMu>ZϜo&t:F*YلNjR__\.7 cw8xw[8zL]}9η׏T*%JK-*~ Z؞ԩT4 /zh4`4X\ZCq1F iׯ^ٖ>hn>Sb ={)+-sTEEdȼ1O   XY]cumMS;73;GuU%r!]s)1x[()1b܊>X\B.bF t&6e_tZ b*+ʙ MmM b2dPr45 tZͭ;h޲KY(ز9BL&quOrd2 466.5j5g[)RsmR)kJmiFVt:䇿+@&"*+׹ rˮ?84Kj*{ d2ݽOo}F]] Ia`>+ i;{Cã|ذ1 H%ܛLMlo& v;r?{wmlPb̆xR{Pַ{u ٌBأbGc.а;QMF9/8ueo|-w!Affuyxt-js00L\V/13;$J"U98:3z|>?] ~L&rgLNN# L~_eMNMc]XƵ$89T*ձ%"r?/{;n95 rgueӵ͎5527]זD"H3TzkQZmqϜ&|t.'l+b6T(ذ۟"""""""""رP*Ril2kX멨(L&éFuu"ɨd*BXh*+ IDATYMgF.POs,FCze ~tvT*v] x{ AvvP]UVoSJ.ej] ɐNQ*lz<º/4{ӲB!GT27oeh4ЩBV $t >ٰdaq L?˚ޗ_JTtvk=:h994:N{A?Qm< i"ȞK6nJr_[ɗ㫯oRRb||pPRjH$ѨQ*H$ J%2 FL&CVT*h=g!\FRVT*E9O!=h\&uhq23LRQ^<U|~?`a$ ?PWJ 9sLNNSVZJgW7 @L:Zήn D1{[}LLNroBRet|u3s2==:'_} M7~{y+rD2AWO:p8B(?I$t?{޻I"Ǹ/" llYZ^vo25=b ;zm[Dy6e6td2=xHcC uX ),,``p 7]1U$TVW!JmmY'WX,.o&b6@N7%%BU*Gƶm~*Վ1`kx7w$mdvv:zօmyGQ %]=uzb0<2hD v1:6_|ekONPWTys6YTTxvzxXx<"ϵ261*308d6lBr"C\p m}:}뛔[,sE67=BazNjp026κ;oA uqY[_뛷!& O0<2 Fm ϱdʤhor?mc`0rv?8D r=>?S-  ~ ׮Źme68̯:EEEkknkNC?&=h6i?p`zD"m >*WB =xV&S6ݎn‚S=/'~AMu`9]kj.. x ;|W@6pEy9 _Ώſ״47qUv/&;glbmY{ͥͯW~_B]*͓mf9~ ./pyſFn޾xiAk6B0fSviXElq]\j  8zv_ysNPxtٍhQ' 5j5t9nwIBBVG FJ)**$5 FL&#~V! ^ZFVhΟkt0~*Uj5_02:@ey9W._⇿+(JA9jko:55՜:ՀJUDccFϷrvy;k=Z";xۢT^N4.^ܼug[&SRTa֫n2TW*FƷלN54 +stq4ҁ-g(h9|+o|=[r*r~%nQ!A@QLHRj4 :[]._ZkW`3Vtf ѰFw Mgr~lqiۿKtsg>GHR!m~lBVoDsu6vv 6fl8nz6!cnnnB"Pn1 +5x}llة?ئ'"_țKNo;*j|օg>ip+oa1ށ˟H$R؊w0eÇlf7{x_7r(""""""":_C 7<|pAnׁyo%zOthC/'Ϫ4CctAIqxTN:atd"';ܽ--u:gf֝R)Y  108ZvݓLeMt:]#a24 R?ri`0H$Fd2imȤR>k,f3jյu ln4zdRvJ*j)SӜmi~n%?++x^TEEtv$ P~gLMMP'}MΰnCTRH$6ZmD"T"WVQ*POgW OUe6 9H$a dډ,Q:6=3(n@ӱN2$J jddBnѨ{/$ ljDR)NRT wz<袦ebd25oYݛݛB_Q*# Np8Ri|9g=ݱEwօE$a O{ tt<:M8 wna' Q(Xf޻&3=3 br=ZNjb+h8ss819ɆٖfcA._ngu͇uaR(㔻-DQ&'pݜ? PO?Rȫ#.ʊ ܛ[Pb;Ԫ"$pϿ;|{ntq88;t:Cg@P ;?wnna4<`([K$d2E89ҫK$arjJے_w듶 |ϱtZ>sy:uqr{X6;(g2\xˣ.J!CeEEv施DQ$ c?鴘 h4## 7'-BG ΜFQsCv~=?a C1p8+i2͹]([Y].Jś7m (CӱJ}]-:,f3%F#u5QXXHMUՋ_ ba NTS$t*dȐɤ.,N d2 {}z=᤭,NR-/VK$EBrBͶJBŧzd|hmiBF&.,RRbѱq$ %%%eCP<\^TJEyy6DSW8YېH$ |~<2ZEuu5 jkD~@ΜHә9R)?XϼuL:# jD"!ӧND0@Vmm|V௶|{^kP*He("c rd2XLt:?G.SQ8`~o׽G]mu)nN|".y+`?wQv4mh0p}.kB8 L"AT&% juZT* JF$jQ*ܖW"DUT榇66씗[xHf"O' V=^/zubT˽TD"!SPPBvoi.L&@Q#HPT &ǶWPXfCբwȣJhx!N@(77=\Pv\7Q*T*L&+R"%%'F#(OppDB[S#rL- >'W^௒$ "WJ{ J$u6?š7=cRX<&ܼ ??{ HD4[q.CPM,W1 .a`Bp>kg8yP Wa߹ܓy'6~T:_;>K;ӛ~k;qvsK;>CI^qxS9@@ "Nax}ho߄z<{Cҟdj7n0y2/i*GY]]ceu ?q7oammťe _xܾ{'uiw8}?`n*>=4G"r \ʡ>W|?d}{wEp<_,z G"$GuUy^Wa`ܻMR(*,ڵ٫ PS]L&ciy̽v d*EEE47a]XpHpp} 3Oq9z)+-aiyѱqV{T!66\jO1D|@I¡0j>u)PP8B"җ[,/A^CW_%~D[=asa'^i*~i~~t3jMO>diy }L]m %%Z^^q\c[q" BȢҒEE޻_A*`N˼u#Xw߾dTWH&wm$IVV9uemO?gqit/OOϐL(1f-t:~CIWCA)m? xd2Z&J!sWy[dl}xM(&~(x񰴴L27z~"<00vѱq‘0o_A.;ogɓ *J9sX,㡭,H"}ǃ^$ɶF&8@8&LNDǽ;7a]X  |=y y % T2N56pC 'KgCTLMM#H>}h=B8\k+"o߸X/أll˶"O}]-7Z OMwC[Y&p=li.V9S3TX,(m >*'?  16>!ȷwC"`dl! 05=CmM 2)p]eSӴ47=Ñ 'tօE'B٫ml?1O?OI|49ܼ}rY{?{]}8ؚ^XJJKY^^PJyveeeT[H37[18AΒH$Q*\jD"d*LNKs^3OSTTHMM5V XB$TVF X \8wNH9>J%C1Ju:U{a*+LS[S`:1(U G„#]wzAVٝuU'lKHDWz QW[Cq1LVR@P^n`0 Jh>sZ{9DEKYِ\ԎL}ZQ G"aZ[Zvk(ΟˆE:aS dt\OL_A%%l: PTTDMuƭrT|kjD<`(Əld*|lVܵ>Ou6;=I=n=j~{p:Ɔ]{?а~9z9&ӾcZegYi =}&j5 |zn$a`FFذsٹyζ4361B᷿MOo/XP8̍kWߵZ-?ypگGreZ}%N56095ͥNy00 K$+QD"$NDDDo:[^ENJ''r‰&sz|YCѼN<'4׉GƑ?Ғl.5a1 Eú0/@ @8ƆJT{@6tJ`+0Vžar#Hs/,zx44333K2DןO-((`qq)..&qCN', _sÃHRgfziҗ\D( (ʐJz}<`qi 癜f}K`qq A}}>{=Gewz_L$x (k/ բf"F(EJH18C}}]9%{=$BET +suuN<{ x ͉.9wSlx<+ ꖶ?|K}]-67o㌎O011gYv883Tڂb滫Dw8'1H&-+/F4c`p8 K rM=͢6Xv88w KˎDZ`cch,.)E5*o 'Ot"̑T*c4U FI&SerZm֖fZ[Ig246uJr,lVh(쑪2:>NWd$ uEx~^?2I}Ɯ,Cڏv bJ^ryrgO" qnC*OD^Lo/K˼[tqf/`]Th4Pea48.W݅vsG~.]<㲺dddtyI&S"Qd2f~rt1A>.K˴4A{?~w^ by{-y*pN@:= *`ȫn7aN|H$B&tNI&$ Դ|6%ƈFi,dp$B>rr-;.% RIZ$emmN -ԑe!{67gX2 [es|>P(D  J=\Jygjk?uS:=/pٹ==֞&뇫BGWkg:?L(wPvsLF<|!J:&J!Ȥ2*J^tHe2:]Yy `0 VT zЖZF*'7X`cC(tO$zYZvY^v|DQBcB9D"caal6޾mmnm äinJ/ܼ߰ЗO>c \ť%1pVք6I&\:NcVVWܾ{ %SI$,;pYv8ڗM"N~% xv~qݝT|.Ɯ\./qi*ƨd2zcqlmy mSntzrXwlٸQx"$ln>xĘ[i++oSըT*T*ryD"ZՊ\.' Ʉ2T*%R*|~ \DdR*QH$FAPP(( T/ryYyTZ.b[RF#8(L&wnpǑH%ryb82LHS,,.s6ˈH%lV+ǎvP*em&‘LJYSzrm5[DcQpeCQO`! ԒdhnjXZMee%&&j 9,f rX tEDU-\.8'Otc4uΜ9f#A&ATJ,C[%PDVh R0:6AmMCeme2nJ\RWn|r9LFpFMFC,B"QXU40W  aZX-fN*4!"w ؀L&tQesϓ?f^BN mǜ>yϥ ~PWomZ?X[_k7*p)>S~AalnQQ6r޾(r0 h40;@6b6 {?e}};|ܾs 70HScpDhspx^OU JV[!G|j%O$8Fyj e)N'OinjZ'PS'BVq=_En߹b!Vɛn(F&D}sm vp}JvA ~Ras$EV 1pl6˟/}.[[^j5~%.^ߟ4y}cƱG+~)S=;/PSSM8޾[$Iznbnn/F'Jo0:>A<ʥAr9re([dt!H:N<g+~X\ZF"u:;;BЎB0W._`35=C[k3ӳsHRB0GS`lb:gu4csk +/hlh`jjX,F<R,;:@W^̲!) 젶tl;t`1u3Y6Tsf樭AT+(Ĩ&H)SeQ((rAL 6:[1LrBc ^OMM#Tj\Cc`2#(U*X[y,?ԉn$NP]ǎRhjhR+BYf#B BsRo 7 oP. 18gx" EѨ5h+\ًgtl{;esw_}F! )<*e2 M,ñ w_}M,^AA_S\xxPs8.! D"A ^o>x<>Je>0ϞF cZeq H$ !֖x<3s;s)@ܸK,gvnDB"(EQFDIxVm趃O-^^rťepgzf@0L.CQAKK-顶l.J"Na6H$^OEEuT*Br^} Uv`Z>Fޠ<mAjIf+YZZz^ L3zC'@ yG'?3uXrb2_ƜNl'/ƫl3P!Wp֖fR4:]%r AV1i0BB*PnR!ݐd.,+-xy'Ͷp cPWzLƍ\)Uv;Uv;nS*NvwN~J:N8j66d2Hk7H$ 28]PNi:VS]-^ R٨:vFΟ/ċ?CPVc4xoO6R4--1;7ٳPWPɩiffxH<-{ώE z}ᙔѨh4Ξ9M.C. w[[j{6rM:;QU466`黅FF&xNf'=M>ۭ=szyR! {LָEGo Rc}+FũbOc<<|,mQoy^P/*;`hinl6a٨+Al2ڂnv*4Z9{4jV6UUvZj=<^k#QrjkLFGqb\ÉL&O?%45@ &[ 1 ,,.1;7hڗH$O|"ǟ~{eJLj.\. ZE0bjjilh(OK&M&ϿZE dmmwP$SITp8Fa~av?zo<,o'ꮀ⻫VT`41^T"(Q+,N G"NHgd3Y!^Ob1e8]nz>T]4#ꮭ!IQmq:]d2Y$p,/;3iaǧ7{ijj$Vb]2$rciyJUHt c PT|>lL.riA$E m\^mEš~9L g%.hG:d2ͣr `a׍&#RT*Bxfqq+ K0HeQ*d*Jb|b ,3M(& KTW|A8ph4J&a|b)j_\$2H$,-;X\\Wr=,,.T*hin"wfhdT*n#Tm|ŗq^֖zzH?I-% ݸdpX'r?|mtoر'&[X(N "xwlbu UnP#?[^۹NF#LVЅL&+".-JqTBF?$H$>[K03;G[[+۩\Y_`aq ?Zbxd%76Шlx?\Nhkn~cǎ2jt{<+2~6?@,a ǖdfeljh@וU=;?&aj0KJ5PٹyP嬭 +g>'SI4 RNRX,gmϾy40ş9{6 IDAT4:]%]΅sg1Me'mTJ?*[BӭaC!2 f gBd$LmN$j³JGFLV>R<@Q.V㤒)*+DQT*UXd*^SQ/V?\.`6[D~ H矉ƿ&"("8Y<.|;oxUԓL&' c]/u+uPke2HY_/,b2X _0:D"A&!J _T4j5rP8d2j|>7}O&g#2N'RZD"-)<< S#\xLSP@2 ߏlb`:M 1;VE.Q(B~{Q]) n_DDxat}~tengɧڲϟ<._@DD=y0ϓmy6vşuEgɧˆy^X//3B9?̆es<σΣ?  d2+TQ&=Nμ/]1zE-߳S/c({F Ź|Odϓأ8=+zb.=M]aWdEb8T`(o@șg_p ^xK.?0L†"'5Ξ=_+:;;hok7V% !xWb6f;G?`̕x_d9w4ڊ FFzyׄJ$}^:q2 @țƍ^0Pglx5a =qU~˿_b2oJeh4jTJUUv7şvm:3Y__'=cgOhnn*WPQKNt3>9J~ _2;7O߭;XJ3st,,,'ݷb?67p#\TɅ%*8v9._ _/^ RJdss A_XD>_Hq2:6AWױ),/~L >KUT7^(]v8T8%92 H$JeVc*r'Wٳg$019ɩό ϟ{z05QzY03;&L yWo:NL0|1'BVF)܄NW<\!.?4@X<.ط$}f??cjf P\36> K~VV¼lϹdtT*9~%J%[[[}]*uz B`(8DBX#ɐfw`ZhmmnDRRhnjj1B&&LRe#Y_x*4 c$I._@(BP8\lD\N{[+kq ǏL&q:]\|`0l?eRt*we!._N6gQ)4왚)[%u FVWר?Ol6?Vf}2;NXl鏕7_D"4Oj͍\...7^ハ>ŽFǟ~N{[[ !]^p A /+Z 47Zfeu?+.rKYf:1qP)*{۫/D]]-*rG_DRb  011ET$IJ%Jen1O`#aXcTQW\F$ K8.Z[[Pe Tm.ĩbBϱw!}*T*<H4 @8FVV4}ԇG'/ `ARQW[Sv

ph'Vɩi._CS8&\6"=7 _|G9;T#illd2bZiokJ%rښ*t?jvvj46ԣ}8MBzV+Uv;5U464564`N?ԏH4|>O01.7XΡR*XTh :Li ZDpH;#ctw/̍.MtP(1 r똛_d2^T Y\(VqWxv Z&Ou{QiA\#hbdt]DѲ,3LڂHfl.`@)䅬bёv*++d2D[VE-x<\[K6ff onnQ[S]&SYm^GVT`xhg'?J HJ%ZH$TThm[k [}l6GG{; G:j|LNY~b[M9h;0z<1+qF2"b4D#jj095dUk464jajpkK @LƙӧXX\ťeZZ,eN?N۩:ts~omNtwQWSlH##cX2,ݶLIsSMԞ☜<%u(X*y1RKsZ4 >pS8Gx<$Iui,+2 LP(H7E#HV?Gaaqh4]a"jI 8Ak < ?QhٿEDDqAVCApZ['~oY/KӷR@ ei"m׆}(+p22:ڎE>gn~%%ns|ǎ{:frjZXhpgax|/Q)J%FF<)msxZZcf@Yr{,({Ob".f3r9r9nr`|r9jkjXZ^FR0 104?mE׮D"09= wf3h+*z&Nx/ڊ L&GsZJ 6/¸\..J-wd)k\cl|= NZ,+ǃ&cZޤ\8d>֖fau͆jWFT½{yV~GGvorjX,NtW*H$(Ao>֖f WVFcvl6+㦧gb13<2ʽ._*O8].A|+n3BQ.oF"fO[>^ZMLM }ŗBa8*Jk7nrso;a&! ?0HmM5_+7<4770=3K*"G-/;p\Dc?c<bx?\Mnh0\>ffvW._.Nq^].7@@ P $)wr BB C455 v'V[DPft.JYb._+c\N}g>+m86v\s \nft<# R|~{8e>VtKc\.'J$?MLNNhp8p("ҫȏ;|\vgYt(.C!z7{lA߸ɱ#.RYB02 mE~ѣG~F݆R$`6ho#Tmj&v[F'ڜ//h4pd! IuUT(Jžes<3s=D"AuDd믾»oEݛ+/H(raO:Jpq Ѩilh`xdLӧNH<'Ivu1ozrMv뷨&~?G:]u:b8 G L}]-ouu+g-/5hZ{F`t|Bx]ʢc{,Y FUv)Nq^ zC2l6ˑ!~u:rG'+-֩cZШ՘MƲŲG1.>fs!#EW=ɍ>^`{ߌFv7{8y0_fUv;'J;sZ2+]:1hwiOc8>1V[h$p:O WV,ap.O;\v~}Y[[C[Yī24n|8H$VVb29wO:n>Ndb1=+ ̮W]'\~ z'$o&7zzI&ݷ=#ho[L"pduMM5l*G!;xADDDDDDDEa=⊮e2.As /:3sM?$esW'љ|l68d`T[wݽb<~/8,"Z{y.AGOS˽B8VHi4j^U!b!Ees,,.!xOhijB-<]q8Xfn`4R*Yv8"r,-;pܴYv81LB;^O2$ HkKpleZM,C*ˎSoY\ZR% Dd2\n*,,, :imm჏>aum%f.B BA:gsJn?m1Y𰺶\>"pߡj1 e83;"zī9F|9T25VNl*B0 KX,ffId!^MLLaXɤ,; gfQill L^A&VJ @ӅlfcC;Nò!"HX\Zb637T&rVRKwu{kX-Bkk!>RT*-kkBj RI,# G~@ͭ-Bl`\.|lZ:B*Q?jlnnt;C=_Re\ܼ#>ÑmR V{ g%]^i`^ D"A6ET?0͛}3itM0>1EMM5kkb1'43( Fƙ_X [b$e?!Ic2yɒP6%3岧baj ɐdh1My8}#Ȏ06>͛}dYffxD=1ljl䃏?vGd2 oG"lx6ed27FL&C8\JDcQۙ5 er -M~*FU6+m)hdBz9BvlV+H{{+Pxj1o{,{LKIɲ633zvvWZHhV_4 Xh֌ٞvS]EO]z]dD>>fiI&{=Ua S_Ww%$;:Y^^!ɐd=N݆(j(DGW7DR.W@wo/t:mmBlb12 e0-hmiY?fXKӘ^TT>%/+Op5H$cW /.-1je/߂hl84eeeT?c6ѨY\|T_Wnh0,Ty Xf ɩr9iinBєFQn̅ZfH$OXw_({Ve r9V+KKduqS|u/z~`[}}lήn 9w^0QS+ IDAT]M8Nf.dp8A̋<ٳ&~cH%G9A"JaH& Ft:-pL.G184LUN&AT:R4jXJZE,f'Ldf%kB!鲯 鴤ә­WN*%ԩz\p8n#ɢDr6LRr̈bAjT-oKAW7 ylP8NCRv:2A='Ecm[өvLwZUm{^ .05=u7ig1Ř<}ߏuo)LtK/Fm6;zӉVeiiwETL6fC~w8Nt^&*ZTq:LMOcZJ456V;dY::(++cjK47jZ-@0(p{gڬ( _t:r(s**:: G"122F<GRUU)?w1W?x[}v8rٮHSv&fi@puUz{(D.o_{8v}2__dye'\@pVg~^Z[r,|\8w9 &/h}fIm6Z[[`tlO> O>ѱqێq9;ዟsg011аm%zv-y{:xZEKs :2 E9ad2|{>ٺsO#r9\.b8fIކ7{mM5cphxmie F4Z XڎfqG~ݿ?طmOui`N:Yw?ߏO|o|;W.vWpI>*||u^&i`/OkK\\@pVK2"008$zt7D" "#x`POaIWpcksy.^8jerjJo4詫a3O,#N?@_4oi,nX,|[ E"(+~ulh4 p_~⅒RR_WGkKF:Dv^'1cknm!sP zíXfue+*~FWmXx*@:y߇GFV 34<`%Lf7{+RO>@ S'9{:E}g OxAi`<ԯ ;%ɠhm3ŋj=-;4xcm{dJ!vC+=vJt]E˾Nv oe?VUw<*(((((((l+›rXw9; >ww ܴivRN}aDQdzfv2VYݪG@epA7#7ox|nQsxXz]tj3i #[%nF#wGE˘xK2! 25=bE053KsS#?/?jr=Zz'?wG;8pu:wA gf˗/lFᮨ@Vs!GFcqggV~u59|~jj4LΒ| E3Oq] zm0ߌuf'dx1W._ڒO}nΟ=+H:jDȕ%pzJU/7oݑ㳫Q9{w~S'2Y._@_^/r744yu084_3h;zhe/dqS#Iqy;C]m .S֫FbrWtpW9~*yX\Z"LrYGɼG>CEΟ;+\Nz{9uDnٵ:GFsy7CX-V2 ]TVV2>1!j #dY\N>ǻ^6Նfe|be|SW[zBB!$!0lH$J(B{KKK$ VWWYdD"!K$qDQ$H$zht:h4;(=3?ϥ b5ʕ˗T1f3x<?֎g~n\$h>^A`fv]y޹| TT{'015%r"^<|gh4F"gt|w߹/TG1:HTJՐNy|M :y%VCѱ1V+C#߲O#M %1o$^"pCW.^t&9V;;DhZ:z㌎m ӛn>g>>o= {Fo)ގYW@IcTxgiiVK `qqT*\nvv9 *t:,-/$ 4 jh4}-;wt qmGt:q:*FbTj, !'Gpm*n*nZ-\2Z[PTTz\.n7t?'tTTUVRQ^.X,|5N'T:餺76Ok:DINm'W?1oGpm` o OKo}WV}*J1]#F-Njn&* /$>v_XQێۥw\fӱX&369nelXD U,.-ĖtX;:c!vzJƂ^hmvb8sszL:S'D_b }/Mɓ1TPղ +~Dݨ`09q6<ǃnnܜ@ Lt:-t: xÁ룼|+RUUm_dpGg7gϜZy,..t:v} ^?nL:u\.gN# I|okz ]xT$ a Y6-] |ux+/RzrW;}Ѳ[mci`< " PTH>y@JT*(//CѐEZM6Eє/\Jp$X!1VHိ\s{e[\\^n_AAAAAAAAi`vr5o04<4[M!𬺋?N+|]I Tkȫo#AW{?_|~nQs*nԋh*.>@e퀙Yffv,108(K]|| Bazɟ{2 (284L,[l.ǵ7 y&&'%\%14<)ya/TW  ' R|L&*qyyJQ'r:x1#0 -/`~oЌHgw7lťe D"(N<4k5Zҙ s3i 㓓jbb13;;VE}}`A`~mscAtZ-ZG34<" WIRXj6\f%]tࡰ7oo瓏RYQjܙ3=rD24D@0AZmIrf+=-hmi~X|QiHS欆BMD tZVexdJwvL6JjRDd2(ELaٗ_]`XKBd-y>2yf)?Jjr9yY.#aKb;rۅI}˖WVmGHxZ-;8wt:>EQ]=C8ZNG6CB_WV(|^$J'kU|٪o¶ajk{pM~FF8|eGEa.^ %g ~o'$]r a6Ţ;sl&8LJ7Gucw{^vKϊ/=@l7]>&#c_4 I;x;*T:u8w8srI&&&9v#Qf|ijl̩ b4iok{}5zX\Zp3  oZwؤrяʭwr|}o^wD"IW]^7V~["2Rbp:MkXltjnQsuuNu*23;BvF(ו|" 2gNByj{ {jN(Ns-eD"Q;S3m6C#ܽw&j53q>uL:CGW7.9vf<,,.̯ 5 =!kϫ|WW44_ȶ<|AKs+ۈ)q:ܼ}GeY]]r˯rȟ=^xS jDD"XeCv(?g44,+4j|bx<B|L$Qk4|㫌a2iiiq>*]=,.-Q[[#;W? /^sq?a۩FP]U)qG'33dazf\}~וrH$1 սޤ|lkb %40s)'T6gFz.]<ϼW}dt4562'D9( IDATF6eD"Q***w*N۟~D#Q.? Hkw͜C]]-Fť%zJl)ؠلsKd4?#pTؿ>O+j6%K\$)hĤzF.?? 308ı6*n,@6Fo_?*@-khtR]Em$5q9,'xL~^-rX;[(h4RW[[2z'Ρ67*[91 l|I^'[FGO&qx<^2 l6&˩Oʤi 9~9bd2V9x@P_n ?F جVl6+S4]I-+\N (ߒѡr<Ϸ eeO6T3Pgo9~E{];%͢V=Ew[WiFm?Ϟ&HvȮ}1z40o l7No?ݶ2^}{vnNʚƵ[λ]ufǤ› 7o?3}xnj祖( ;KOX\ZP!:I%77P>Sm+cq巌mF,1<2JZfXl^o5[QG'+~eWV<ܱ} oo$L&Mn흽cffXψ2}FF8y8lk7nʟFnܒc/~dLjpdsY~6\zMgph?GMEE3Ǣlڴ|"1&&p9l>X?ٍ]g_\ք^A4lj$S)~456cvC4X,F4: tͣNu0}.7@$el|9#TUUJ,0=3+sxg_ ΤѨ5fy{tvw333KZT2j(ģTWW "L!D ::IRLLM1=3[X,.k?Dw{]< 6{7(C47)&6_4cqwvr"SSD#{,.^H$Çs9<?֎磫w\frjx"']AAx+,,,255͕Kd{^޻tvwK\x}>{ SSӼ{ ^/񮥩!\˷DyYN3P+y tvL&TѾno ]ʧ^߽B6>w{,Fȧ 2^sAs:K4WOۑdHttvqX;n7^X,N,daaZ->Ld%yu;v NSB_)f(vǤ6 ݃bF̋r:P8̵7H$05=Õz}]~)TB60^Ơ+&ӓh v)jjaqi>HR L2Ņ%jj9|PW[dI˨%t8%cY`yŏ?șS'}.ݠ2jktqRi.ȁ:b8M来o pZ(x"!X|+l$ &ȑ#z}Pvn)aZfJy71wtp\8Ũa5j$AGhz5TW!W(pY\Z`0`1khO z9u8v9nEx"oa3O}-蠤af*vØLF\NWVfTWWe{<+YLF#G4p fg=l6lDKs3n7 K456t:8xX,FWw/j,f3e.bMEq]/=pxvST݄McpaaB:^Wd5@ы\40>e ݒ ;edٽOv/?A HUw;Y:,xw\prT-󈢈Zް.AP l<TPPPPPPPxP9 PPPPPPPPxPt[?~hfdfnTW׮hݷGL`1WH$319EffX^Y?ٜf&d2)l`tl\^it~Y\\*S_mEA\NAa!N;̩c=59|޾f<%zUШx!H&SDc1_~CUPO WXf6}`K-M ΕKz/qoL!<==L&zz䣫tu"%0}\Ny,X܅Lf2s*jVܹsoCrY)So(|o_׾|.y<GܹsL6ˡ{nt:,/ч3?ejjCZvֲNkK#_t:XҥJBӒNniWDX^l6't\TW\]`0r:|8P_NE)//'L NS]]餶F:x{&"}v G.\p'}9y8nZlԨuO6`0PU{KTWW[mvRr ۡwp Jo ,I$kKzU@o311Hd*E>/ R dRZ0Y***dJ$$IV  ܆:S(Sl[(эBj;zd,8vFF-v}ڮd2)+M >yu:-cm6*1ͱx2NVe|baC-m'J6ɒѨ՛*JKZ יT*M^m KIg [ 17Vhkcf||Cnݹ#땸vNRN$Ikc*$a"-*L&F!y|>'kH:R7ԙ*JdRȂnHuJKRc fr9t:\FF#V`e.'Qe\y[__P{xNg2 t:2 jabU*j%?xH{QX)GAa/PlX _J/mɧyYI,n5o^[*})6CAAa#hL *()Wm[̻:3[q!ۼSP66jsm]v_ b.pe{9ޟ:B-24jv6zUUxHoxނI ۡ͡rXg.8d{6ӿlhoo>_GZ'*;TS*lU3'xwxd2Z6ڮ_}rA@lk4 vޟbxi:c7ms}E1v_ WŤݗUY 0 3п*=\jSq^+0͓$3e*WT"HI TQ*Ux"yyiz|Zf,Ū©3. D Q.ɋ4\R19lDBǦpW4 珫T3V<*TsdD|k6YzԲJ^; DjhS/q!߫l:7y>RmmyQpkX" ŋC'94mnV7?6<ɳWnz _e KY" z5ԘT,$DE&{RTiZjMߪS3K xiKȉp!EMEtR4Z9hTVӢ ˒ԙ.WȊO5*~=+)Sc|vn弬ǜ]˥w>X6U"mVVeߪ|xnY`1!rĮb<"M4E6ԛzPZjKiwtZGa;Myd ^@ZY?+VIJ[OEg腂n_t:&G*4CW ۰6#Lt^d.&Rk0kL Q 6tr4(bd\̓j-.[M+I"Iعڪ2$s"ɜRRd,"Pcz8tRByb3|""Wk*bTCW /L4]L:: -IN:Zꋋ,5t8_? }櫩z}."ݡ`!Jn]HtmIc$*^B)D* ҉ oWcbMxZv" oYn,%)*Y&aۛ`ۈ#LUz0նl>=}[Y:W:UIpġ>e޼kxꟃJCd*O+((]T[*VV|:#_ 7~vvKK.[Y?s +yu:~A=$;.beſienguVZw3BqS黭t6% nΝn6^f<*4{d2bsN<_ ҃N=dQG2$Njܺ}b8]]=r9{9jѱq@nDQ?*&__A>1N$ILf_kfҍ^cdt:bbrťe"( #xr_zzhnj(cOo|^D14hx< & {rMo寿ԉ>ϷHmMT:MGg7r~p+ܺ}ǝP'tvu[Xw03;K<ߥox,N#$IE<^kǷ@gw.33>#zzϼh>x]wnQ"33RY>xO闿ڏ[ -ˇGoKK~6-..ɶɤro3frDQS(FE^9&:zp8&IGEL&ͷ14G!| ?.ۥlt~𝌳$/U_Vd"YW iT!,tήnk[ HX[#:?{ 75 ʡb"Yl53҄h4(R(B}}PhcW]ÎhuwbH#Y(Gʡ\=eA|2| H?-2 IDATydkwPW[¢VmK&S475rFS?4v6f{s4r1{ZׯH9𤋮8tqlorAxM775yO ^@1+j<ĕ|\pIVVW ')*f'ȲdBQd^%%%S\Tb`2}Cee>uX,Nenttb2>uY|^/ eetl)**7gDorzW BkfQCwtu\.si/)++=Zy3)**ƆJbq|>/SDQW%1E(̥ !"47(+-d2QZFOS:q ^cn1Buϳ5޽.Nj,8X,;:9vE))q xuT2G49;(DeNLtJW}sL 8Dߧ]ߠ#Fj躻ʼnV!׮^!]n$s<ަAqomyq^ŧ7" sJ$Dqqўvo' ekڴ1?Ce9?x^ZHa/D} وHo4m2O^ןǵ @\`!"KNm/s vBv1*uh/?_^S^CSob}v}fϛX杲n7)~֧}1VVאp:B!'(-?:,a6?4;頷|¡0#>;nRXXfcxdE&042B8^!L&3'JuU%N[[Oa~_Ғ LMP]U >Nww/MM 7;bcs?$88DuUU]=%,B|]3L&Ճ]B $HR~(C LLLOFQa!.WqN qZ4(+-%`2ߺ9GYX\drz:'ů&ؒX؊*-DR?}Y֫Q|^/Nq{p_S]U"+,,FIR<|8d‘arz~3|W&h4ʯ>kǔei@9jEX<5//u޾ Ux(,*d1feeW9p8I|0X t_RDd*0h+++7awu-/4?:](}9ivwWVWǙ$|f}?cpyeO>3~Q]U6Xf]#G^$6hPe >ӡ4+k|v(bPQԟEx|?zɤGd"La3*+X[xA,( [ < N'3L}]w>յ5ο.= lb|p=ff瘙E%jkq} 666r_~}UQq311.8KYƆLMMQ[SM_׮^oX A$DdiƆx3A~\pTٹyz<8?p["W }hmnz{^_-ͬSPXbXO12"5U ;46ɔA_/~fxd{U}[w&jށo8Qt=}>m]x_SJ@D2A*33dccN*%~ÇB;Lf"7 Qʜfk ##z|hzeXII)N<2ѵ(?'TSRRBGGg7?]TUgONN*c%pQWWKgm}n]Ѩ>x'`uu¢BGGK255I$x<%H)CAYZ8Np$j}c~??A~wt_\.Kο.1*{|]AK   O4?>]O~@MVʝ` 8;NE%I|מi~`Z{z~4{t4em zO.=@KsߩTƆt2<2Jaa!~g(nUU9rPIee򼾶["2"#+ s^y\bu~UOLR.Hd"%Ν;4gNAA>uK2Q>2H34<­۟Th  34blbόAEUE1_Xo G#_6d91il^jrsP>($S]Yz'&r4lYZ"^Km ƞ((J5c^% LJ@U}K>d2q/`6fSQ dB$I/ñq7( Ҡ훍,˘f$Il6$6zyEѿS$DQԿJI9 vF2l6tH(m21"JI&DoIb6\zYm,Cktt͔)?N iXivȲǛMbkEQG2YVM&vzz8y-ǧhŚb]K#2r_i6kB OE|_Gfs2,Bf1fuY+Go˴1גӏc?4nll|j>ʟ{}<і) iivkbm(*@X|Qjv,&"dXER8XUK5\e|, -78Hj (H "M(*I(L,{4B ʲi`::o/ަ )\bNeHu d2155⫻wimi!OOQQ!33\TWW1?b(ѺZ~gTVT V G9o޺q/ #|xllo}@{G'R*bA4X[[6sY^^?_X/),.hnj|)?|]=|u9'?9EELMpDCÌOpjz($ ^G^n"%?c-"DQrdg(qp8ќm}@yaݗwwQ]U;kieGWS*/< PH&H$,HIȪ (fbb`UU8s$1v9b旤(V*b}smQUP(;gNMYi)avndBFFPWSw} vgN\3*456b6FpQgg b1f~Pmʹwt:rʑކnxD >?\VFKsN  GPU~[9uj46Icq-fǵ`H)a3˲MӜ)*t:s ř' /-Fχ(8vG:2ZR\T@p2~7BNjюQxXzp(476299E HQQ񓟲U{|+Gb8 XXXf }^]BKSS/B0hLu[~v>TV_raJs,K45.Ǘ (RKbLGYNOߏŶYD$YBJ%IRȪz4uZVE8_e%UU!L'9<$Ι8N|>/EE$q*b6SW[K< sDRW[ѣi,+ AMUSբ*>*&&8{KK˼s4=nοNu5~eeu5vf<`brsgϾ~JKrͱuXmVUUVR]U ǫ+; Au"olyy=zD6u$PPE9ҿ9Ü>uht]+W KmM5p(k<يd*륰MúTl6s1  8|<YjI$|*)//ٜsnǣkVO jkkXؠqv;u5;2e̩$I*+*MCs6+*ޮ>rV?)/? t_9ɋVQd2(8\,)S)+dXf$)d"0 #fK* Q3zwq!Gy}9hvfq1w_-d޴6ٵ^TםMm{gOӵ<6n,!% `>N( rz4$'XVTdEI]{Evڋ&!MkC=o]EeyMm{7 ?Kl,X*f,zd2,ɀ m6,61hYf|-EUPo;!4S`U3۴o}"*`6cb_X3߉9ςf# bW<~qi‘sS(A{ݘfp#::oy6D8Qo6Oi︹8zs/'n1ƣ/4 ^= AƮ$I,`]=ɟKi/89H;&wnby2g}g~;`6FB]!ϤRmMEX`faWaA^ bf^UqجG*X<*^{٭aXp:ܼuEQy=QUY,R*ťPtчi`go}xI;TW177O2SSvJ;:x=nl6SwL&4-͍hk,1b6sp:uǐ$~]=iG1ȥ?(\r A "-M3qӇdAx^siy?>6M O[YYIwP?'\x)K*ŇIlpW:a=gȑrο7zOexVUם~L&ӾEedY&gpitwrdgTUj~}}~ypxEQ9wkl<_R^~d2Vۧښگή**c["k2ᨔTdxv;^k~j W1]bqC#;#=%b(03;G2Kn8M$@`~&C!zzz"3Ȋw} 0?lV+W\biy9.] ˇ2o8))ȲL*>r>.]~W7sss9)J<رo $84[1ҋkgףG~&=N{8~(d2b=}Ȳk+77:$bO_^r^JHH XX*..&L5Jӆ8kin"LL&X t-l|upDO? LqQK,"czzl;58e W3|>/d;Shg{.s}4Yb(/:p"EB ,/073,@,qg&)<9|Uoܤϋb0$L!PRR^;:9|ӻ45fQZVdrP7;oy}JKKL&f p$IBQn7GsMNfXG8s$~~ּ EQD׫=z]Z7'˘LzX,L&= RVVm>sb[ IDAT-pML&&Iׅ̙%Q=˜KC#L;^iiCv9NB0^ɄdZÇؠ`2DlNŰ磵)NM/5\/[4A Kʹf:lہ'ʜ}|ml}־/>EUcecsskexٺi~14M*{"^~w?_Νk9}$&фa-Ñ>OgV!/ˎ,/c`<Rh3q$YAd-aHQEin$XDAl1$ d–j, 40}2߿*%Xla=~wGe UUQ@)hڴHYi6u ޵TpO~ڼg h3گ%HjdBAZJEfMd9*kQb8& IIF$`XS)L&NT6 /Ϣ;s7@=2Q"TJ!OMQbHYE,bX, +j < UGq̟OOO$+r$R,Sl)))*jM_Z5VJ00000000x1 x`;ʙg6 фEb1,m2Iń5/IRQDWz E5Ʉٜ~^{s7^| Gew5`a7ukfOYY)~=993ZtM.}FnU|yYWo}xAhܳx8r,aI ,#+2&J b $& $M bؓH,(O0LXflV+N| w:mX,wCOo?33#}p8BWw/LLNq~fjz.!g}˽n76/F$~Y;pEQ%Yb}}^}i>+&&&L_ˬ'I_}}5C!{hىl<_|}>G?B0kC!=V'&wUU%]g}c{]3>1oEQNZt8|m6f|b2Gܢ7G/g211Df_M]S~iq@ HOo?ȒEOi WϲV+6+TUU{u{@pUUsTWOoN~p$B,gc#=4705=xݣ۠u]9k( H:bbr*oZ|/~B,ɝ9(;B0xP8D6`6cUJ(bdiJa;|'[st23;GOo'Opw>h4ʅab1.\8 46g h2?w|׮^ዯF{pו&VWW}u ^oYYZZfsk/KSc엟PTXvذ2$hǍ$I=uqxdÇ$ b8>vΞ> dȊ6.g~~8޻466mz86FQQvM׫<@d*ŹgX^^BMb8*v&uuSquVWtFspwMJp:‘t\emdҵvxdT/@0Hl1>dYuD[+ ǹI2ÇcxG"DLNMseg]=ɓm33+A3fa}pۏf#݃ێǙel|S+^lv%.%%.uvw 0E>,l6 n ĝvcyt˗. "+TUV2>1`{5P8LKS{s-fzݸ\ŜhkfӋ'+ ^?5?T3{ss*ɝ78Bx9lffJ#JӓɋEGJ[Y564ph=3s~]ǐ)K~L*M^ ],nOۚk#.dtXLB2W޳۔f궬olPZjЀfM_j9y-5Ux=n*++y=B_'Z~aa@}!turW @Ue"$GF̎5Z[FOoL[M\ngaaaaV+۳ʣcOѧ'ŗwikm }}ڱ?{mV:&yѕg}}xNv?kkk8NyyfG6߯;D_|)ocZ_= ̛n>4,n]BUUڏ|b AH?L70000000&cFF600000000f`L-50000000?!{ IENDB`gammaray-2.11.3/docs/manual/images/gammaray-model-inspector.png000066400000000000000000003454271413310443000244640ustar00rootroot00000000000000PNG  IHDRn>@ pHYs+ IDATxWpU ޒ=@ٖ\3ΝYj Ehg#Ћ ) "6vW3;n5mͦ&AO C <{NT4Mv7n索2O~ߗey#Dcq˳nˏJgԚ!<&xH*k78u 5|?|1/\P(|c;`UU1 GdHR-,.~S!$ RiA#r0=3`x>J7HKs3H%S f||!$#J"2V+AEf!J"{vtvSQ!_aw008D4DQp8X,"n ՊbY Dcl6t]c.׿v_ʼ(vhnnzTWX,8EjID)k U9@:~9B~d2ISc#@wQeN'V(ʕ+ :4hܽk's;p:_Ϧ  t*~LcdUz=J&f!v;\`mVx<"t:uJ>GU5TME%.]vPUՐ$\-(bAR|$IXV4MG(~0NxȲL""Xp:$ ^/D˹4EMӘgjj FG6 ^kmt]GQUlK#2\|!OKPg8re^/(>qLLLLLLLLE6czz֧!Ƙa˖k*TUU=g&MLLLLLLLL ?<&&&&&&&&&O&&&&&&&&/41111111y0ɷNW61y0?&&Oǝ#&V/N~F=r_3E4ݿ$GUف!~9yWBo󂢨ǟ05=꽉?㟠(cmߏ矇J,L&LMf~iܿA,L7n2==C 1._ʖ͛ٽE!13;"˓J x(g??al6ΜD@f- TUc]DyE9/]&`8|._K<}T2ڷv$Jk?cӦp&Hlh[ϕש ڬKi?<+P&)۷w fextUQyq "X,;ªJ*Ig\}dX,ki3tno'NȞݻ_fǰGF%~%a:39t*r<v:} bH3>dv6m F!X,h^"ɏnN"u=;: /~G :igxdYfsTWWEt6mή{_MD˿7vp8gٸmU[fff}hii敗:dܺՋfs;PxwgRVezjMCC B(fٽd2EMuRvb1d``rZדL&A.3;;K.\ ki0 .SbP-MM M BaV+l%Jmm-g}.@yt:0p.w3>1M$ sG'x0|AT:MGy陙vgªDІa dYڷm"X|ZO/~ ņA$CQ^~0q:,ebbbbbbbbVMJ_r\`9v:ik]O*fdlyx,N*ݷpؑeoMLLLLLLLL-ؽq>?**Ï?axdCjinjÏ?p` X`(cbbbbbbbb^ZB1&&DXdg ("[E4vaiķilbMxad`l E y͐l6$kH$z=kЪ"+q9kf Jt:,t{*3/,0? vJp {#_qCC  y&OlbKWRU`]Hy_@4n%wn/>|]'&ǡq8؎ɏFZ,l6nSbPi;d4Iۏh5SE^σ`ം(ooEX>yRjaaٴq׮ߠu:_XAƉDsful bȒC8}ƹϓf H"I"eeeOb-lhgtl Cä6zH3Ÿq/ *ߏjµ7c\v{vsuV+S(0 ۷r5v};sݼ{~#~bt-|Mf|ɳf;ڷm`~a_=ʭܻ7ҥ+zo=d#Wr Ҿm+[l"{P8L2bp.7n`GU68IdYBtvlgqq=KDžR$x^-9 a AB0jrTU%Juf)VVQZ$6s\ &~c#w*&}ss'o##|31y9K~jky47`:Μ֊b͆`P]]=ܹ3餥ZdYvSGsk7^c8H+W:Ȫ&/6/"d2I]m˴ L Tr%JQbZDX, @UU bjjÁ,ˏ11Y gZXPUCLϥ+Whjlj,#:MP`C[+^9$IOϸ'LIY %x_YrynךلoD5Qx8y,.^a/T ,h iMPT͆ DIB$<("B)NOU$IBUUDQP(`6 UUQ5 ,"a%MӊmulV+l˲\z׆a( auEUlhb!cQ40bR?5 E)juɏ&p%*x n|$IN8+cMgMȲL$eܽ;a*e^ s_\2`ݺGhin W^Cdjv&VYf{G;' wvqM˶sG'_\HcC= b17>T /Ubu[~EqՂblf+meVc.?>@dkAVtX, I\`WrA$Hk$M~\StyR4.ƥReEU UfcXe0 E `۞pCeҙ,CEIYcFс_cʈ<6ױonvNQmgfqoL^$ <7|>FFJ囑r9Nysbbj XUUN|v Yx{S%뻃}O f΃SLY*x<OMSQQ8N24iZDi*Xm6 ]PPC_g= s ۳߱X χaQ[[)>$aZv, A 4YHH$㡠(LNLgin7a011Y;11 x xϩ3gy8]]t_LUet:&&ߖ`Wg' 3di]׃nCb14.Wq*r?T SzoQ077ϖ͛˞=f}rYlތbN<vff3&&k&MU*5И<`)X%yM,chYS(kښ ~~yߝ~pu:Yu8s\w~Յ3Mee/6$d ~m1twEQE w2?=A}}Px.o HBr*PU29xК1Kfo$ϓL( P9r<,S{6_ʨ`ZK Wn0 vtng~~!AVZrx"AG6ckss#::T[lټ*Kss# bQR ;:;ob/Lrg݌5a9٧WN7erȤlE~?=7orwh9!>Tz5Nc)_j&͢ gܹG|HuM o6nQh[zmbƭn;wZO r:y'?!J!CWZ,TUrdÁ ,,,p;딕x$g S_W):H.R)&&p\.G~Ν;{Ǎ, [JY:_{{ssܹd*Źsgd:|$)̾!ٹYcޱ1\D&C(h _+Wc{'ΞP(ǿ%n~?agǎ.-%fpD1A~Ꙙ8xa*Łw(Z>6P,+ؗoݶy"S8|%QW^9JSS3G_{g^vtuǸ?9I(#=91A(BQ s67g~XƦ&?=׮^-mAۆɉ }kZ*Ho>6xy2?7(6T2(fwell EQx1CCqsTE³n dG'6lգ/#I(RQQ諯bXعkW._nwPP^QQ-+R+*K穪jg^ꩩaj>? [nN?6kWr7oa}|xe40::e^/ XVʼ|vi۰@ P=r%/\=DZG^z 7PQQ[opP[kʛO7yQyab܅T+׮cٻg'Nw>$3sr‘/tcаT;Yi_E6nhc|bm[05=$\vs \~==TWU=TPGu ^:|KPWW,˥J 6[P „욼$hV!:9y7{p9a*+|*x0tߟfqqX,N"d6‘(qCÌΤq(&b} nsiֵ4so|ڥǩpf޽q6o-rq26ɧQ(p\xSSTUѹU4Wm ڷmevnD)`]d2R,W1;7Wz8 ϓdiijl$QQ^α`xdP87r 255EsS{vdݺu<rUjY¦MKCϺ&ڐe7xr%I 31s!*˩` q&Bd$ IDATs`vjή."(Ǚg|bb; fh];DVÇ ظEf)( `kg%A L\.OO0BudY̹ٸ Ş;i]$Id29 p:Hit`g6|t&mK$ akz5|e%i޾ήXKmM LFn)M7.Vʼ^J6=n7CCX,~&AJb2ӹ|(4Ihnjӈt?Y  Ge4YSVj"LOPQQN(x"A.v3@]m-\xbI(gqqJDQdbbǃ(J1#^,`ٰ".񴬌4 MQKo?O/f_V PY`E'O7^'Opj];E_I) n+V_X@%D"n_s@42/]j"XΨJUU%ZՄatJؿ.]*38Nn矟ZuRPhl5|R2 P((h^i8Nt]'LZSaE/f[mX|32l [UU%FqK?4lE8l6[h6EOᰓH$_[>ϕ"n9-C2c:ła<Á$Ixpl6rDa`Sozf*dY&H|7 f[?$lbYEQ̅&?x𧝝ܸvl. GQ~?xG.%022LuM5ܣQX G(f\z\.ǖ͛hnn*w6{oѾm岌/tn] ._rq>ʗbvDP8̎εvV ~FcC=;[ L.:ʾ>i(;|>v4MPPJA (ZX,r("FUU4 Ͷ0t]'#VYFUU B(>|l=8tc9u,/96 àP(`ZK l~Y^heeeיpߏ*z^uDQIO<׋(Uۊkײ#IT mBWujF6+@-WC}Ng2l?YGh߶FN9G0ƍ<=g޵;8N9&/]8K"3$ ^:rw Ï" TUkvjkjp]$I.](Nn ÑCx=LMMp8޾޾>'daa|u-o~ +/Y2g}N&%Hɉ>$Qd!u:vb0eee}>gf" t`0<[6oda1H"{>:)xN:swx a`Q?O0g?y{lhkEX{wE H+T515KNV5Kk:nA׿eΎeLOϬ^xxGyFK XCcodoe[{oYgYhvvW$JLLgnn*ڷn%L1>`ݺTMe~GFzaX{l'paaZv"p~?0Hcc/9D,'u-ܟ.sphL6îh;oٳvnB w7^S)++#219IEEHQQxӮi8,(r ^3w93|Ym[{pLMMS__ B9$I;їP[Sa,'ظys *3;7_^{ONԈn7_7zPnf{nbMܟ"ė%-_t^8ftt:E=>9qIrcor d0.-[Hcqڷms{u >8p 26[oFw5h_g?8*fh'NSY`au+WKS *ݗ.so{Sgα~] xJ\N'naa1ȥWٵ躎$ϳeFZׯڍ 'Ou:}qJn t*ۿwIe @e  x=Wh-f>āhh' Oĉxzp{vD@ a/D`4pi!<^o@[}ijj9r2,8rEhin&L&CMu5333X,"dtM8ɧyDFQ-%X,`|tSN$hjlDx=p];6**ʋ76LNާh,(tvv''p8ط8&2;ډDcT w{WLS#"Iؿwaj  LEQً3iUfaa\." 4rH$Baʼf\a",YH+:S[2ap+8}&MOx*YjH):)~ M7 ?VoXaO* KY="ɂLhK "*]m )l+-cqrBF#C[$QЙlI(ADe#l:%$h\%`Yh(:4.ٲZTJC@S$ӉtZY`,^9V>_jT9ć'uEv }ҠɳOnpgE"9XAgR{%T==u^ $p?a!Y5Ի,xfRj]"ѼN8Vmݲ_XiFQ]U*&nwYeٟ'c{G{[JH133CmM _ٷ'x{U+~ڶm̕/J:ǮnEhφM @q쁒'W=ܽvٗp}yaj <7Lvd(wuaػgHK4d m S3 'XX\s{;NEUhkk*LMp5WW[˅qob[nzzoufffhinbÆV\N.ǃ$ͳ~] ^0h]**+ʸs[>H4nvۃfr8r@Iصf-Css\DhlvWiAr/clO"2PI-^PNg,/BW@F3\J6I8$a2@]d<_0q//ڢŽv.ٺWVۊo, <YdVV%[:tif]RX#qE7htK,d44eҊ͐Bg/xq^ah'P$F* n@N):=!LZ'$JyJGDA s|1W+`Ecq#J1*}t>M4d+ԽOX-c(Y=I!2Ԉ/HNg8Khl%1kpe CQR|)`NgO"鏨l(峩<^QzZ(.<456<CYU Ia***sH2P(c`fvښj3;Ė}wl>u%JZ5h/ f5 /gg 쯶.蓨[ğfȽJV5V.Oh쫶Rt-*f .([ )-Kb(咸[e%YйRxΆf|V|-Knv n$e3S)=UV`{Ά+ڲK l+,bImK%{֭Ku6RZÖSHdX̩*j#sn6ϮJlBu^*X[mfF,T9,,dtD: /)Y(lh+=Wia!Sef1|Z<" eSs=W{E*y%&* PNg$rZ P崐,,ނ.q1vXwkq EU$ I%u"*j5Zf%UZ4B0mfsTWWVtfs9r<$aX<r)Wɳ\5ޮpo!͂n(|%7Gj:\/pƊla`VY-4= KooLF5_8(h5t/ْlm*WE-=E[7C Gj4c>’]YJ|)-ٺ0WB̺Z9u]DgO7<>)_-}5ڰ0(27]劣'ȒW-U te>QKzP =Q;jy/ɩ `꺎ML֒b%a..,؈WF:=b0e)j2p\.|111)nTR&ONEdS*rv9Žm|rgRK7gev6 sǼQ_I$ b}[F[U"^*\kŠ( VXC^20+ppֲW(LaϟE+NNPt2mi)[޴Fs$iʑ՞Q0V>Y-T,ֆd8q};`^HWI%}՟'']ǎ'=朜8vٖ5I)$$QN`z/C %DRfy}bf~R"C%0s)c{BϽ|L3 " kxaX~ơ0jKOaV?DyGV(Ab_ˉFc|!>Ṽ_6+Q# BrEۂ I)/ %$3??VrbcLf^ﺢחbG-ڏ;5=Zx:HE)A~n]}rgzeTsd/܇ ?Gpq^?m?#;F?_/ϭ9U!Gl+[XWa@JMt9!nM5d_ ekjWXeVRO LJ GZЈL:w Ҋd}_iZ CR.w29%̩sO\`$G'ꧽ^"n~42LCHӺyEO)Gd7L{)6HL[Cd>ǂ?s$|H 3^*eYBMo{ t'5c=M".k+ԔDQnG ..z=%Z-Pp47>ͤ8urj>qp>zbP^VJyY)/_!-5% IyY)I\VGrR99Y8Nt0]ҒwξVyWcs}d2o7ocQ$(/+C?GXXJVW}aAf)^8}].,. (rmbFjٯKRR%/#hlCk8_wĴ $>Dp$L"Vqe6RVlMW& hg]k5:=?DX FVjQԚdd:Q5}+cMማEzk]r.zTъ<Pi e8a2xm6sb_9=:E O f<0-+(sN0c'UN,ܞt~^7 2` 7+ C+6Dp% z}mii Vv@] S IDAT\nd2)2%oE]]=J$*g< #ɓ‚|ffgh4j% Bgw-x>\NOoo&vӧÇ6m.{zjt~_`{e=X,VjwrzZ787h`dM!YX\d2"Iyi)  [˸u A|>>ngxd 0YZZ"';R cTW$ Ņ6 ۓd<(*($/E|7uٌ/{;?_?E[Z˩B:z.ջؒ w_s?˲ǘRR#f󲰸"e Jgq{|w[J|fi)a1.ijZn; ٚ'. FǮ\ÂwV"t |2Q"T}a>1{Ic5@p ](4D2[rQ֜ن:t#YęHnu'JO L8hO"ܘYi#YmI2y9qdY1_)ђ6Z0GDat[@dE.(nS bc@E2tEq` )'UG^qGp]7r>PDgz$vsGm@:\ qZS4rv ~Ҿ`dV%?*IvZ/ݽ^WN֝;TlBOof}Z w;HKK5$>gE,ska& { T(LLLhni# aп>ǟG @R).jBB LLN20BFnG'NBJt"ˣNP`G=8gghmogwm 6{0=3O~ h4j.7 KKr1 "6M$&$hTnrQS{8~:J(&]]= /,b͡xgfzTl݂\.G*T*~4VPV>NG^n0IdefǞ]5 i1VhuZj wϒI inTc9}##t:RhU;M/`2ٶJH0 Clqg|.9d?&ϲWيl_daq(G!>^]ۤ/Rѱ;d͕latӂȖ`ң,bYۓLa# r4HWY;"YĦ55j,%|(t5YҴnFTKHGd%!!ȱ<3YRq<'Iv*ENj|ӊTKr^]NANO v59bVNTBB$'$1"|srhKzΥij /X)/RD)V_Dz_ w0]rޜ+UfJ* dl6jebA.aZBRR"ã罠wj(`([%L |>? x Ʉ頽kqyYT*n78.L&3LNMc[%!033KOo*+xL:2Vp8Lvlnc/}' :YY_ZpaktZ*0 +$X!l0YZ`(̀5@y|쏺YHÇq}/(Ћ$ԕ8$$$uy5~AK\|m&Y%A+8DEfAFLN2V#۽/]hIJr2Ma~>c㌍O}[%JҒg{~˱X :DQ9Yv$ ][W{x9x dRSR8| USMk[;ELNMhؿwKRS(*,J2ul6T*d|%}m_HNN0+a 0#FO`vv9X-$l)/}R`0ȃ>ߺ rs8kA ]q219Һ9KE)[^X}]fFURRHII> k>:^;u<Vv{WmWT.`|e#A,VYX\$%9?/kۑdQ_/CٽM0:6]5,^/]=쪭㌍O@zzںvvJ^? L@U295ݵHIOo/;wlqcR]3zU˖~pbE[## rЁuW-mlv$G")1޾{f|bFiI1JiI1w;: BTW108:Vil`dtdh0a~S'=w)/+EErspnֶv ˖' I߾{}hxxrs`1rci+le)R H )>OFٱ'Q*!q%rOPe/ŖŋDPlV(|g' =1ȓIe:7LAS* |54B]P>ۜ*po#:n`0^^Hֿ}|>[6/z_.{vx2FlvFB![䉣45rp>?'%94zz{z}8.zFfC.35=MgW;wlM;Wrttv1<<F~G?d^gHKKȤRJhՄRıd`0kutu͡%"PMX6 arj }{ȕA0Ns n100ȩǣًl<@Ss+66VedffPGbb"jkK$&& ݻOwOo:ABBDfڶkm'>>hʘOBA? 'Yd'RXJ1HyAqE5>oo7b_b*O}?\c>^%~?~W :ח_4O$INNCH$ x!ɘ_X -5`(a_8jD| OlaWM5cOmvRMf6N %%ʭ?0HwO/)\.rͭx ^ɩiZ /v* T oFGP jn4b`ikK~~CTJV#PՈH8Ơ׳vDjTJ%u z"soiM} *R4ht rs}A"iHLђgi`Yޝ"1EKNa !AΠDax,/[y>cLf3w[6Ss?'YXXj=u>"v>y;G_x/.Xo' Í' z}l\_ ?0㡰 ֶvR۷qA?u dgev{dR¡0;wlbnΕ{Vk().BEw)a@R]+*,  "ʌcdh5jT*5R{ 0*=|~8vεwzQTBPXXS'5W@nN6@\N0"#ˣ Ǐd%G ZdBQB""Ed7O\97Ap8=rJIjJjNmeF~ɣȬD֗dtQ:d}'i"F ^?0mw |qfR=RpįV79 }^[8|w[hmVA.dy»gt:VW\&#??308T*`"A;M8 Q(#PlA +7y$7ofnn<^GMN{j5tvjٱku =H$:8vgdf˥ S 9233zGKb21lRΝ22_i۹ѱQt=|}(T(غBߓuXXQ*#5Uldxx??|sYT*VToEo z;33cOd-ʊRD"A8|U(...zZZk[n{﬜rMEjkk-9g}R)qtO^I "Xko>1T܎#~$"3 zvTohJV^VᬞL՚wvT9B#{1 @R@$ ߾r]zzZ*}|aE-HHG_}3|rRdm,,,R[SMskRQy|>/n #ƴ4'Ø&|x&&j4t"l6?3orgA J'MnN6y,_4:VmM|1ǎbtl` ܻ^ϻg8N~W?y9fy_@ R@EDJ%( o޾{;rs?AR3||SzG.d`bĈI(> |wŊo rs?PS}~^HNN" p\d2 q:E= 1? o>jy%238r7n=>VYO.w>;w H\t'Ov&do!X2XB!*qq:$ yvvVDRb">)瓋ik ''E{l܊lf`p>AIDNb>_:ɘH/HXuldg=j?@RR"II)_}KK&RSSpx^8;kJ {''ňȇ}}A1ST_$^/+HHJ۵sD"BҒ~3o>ܜrsr?#'O~ρ^٭AS'^5O^I||>t:-.; DdnNl6{a~a1V Et!HlQP򲅛nb+]LMOcLO烏>f!JO ŠM2/_%ޠg~~;nd2LNMa43<<~ Rzqijfy 4cNSK>0]=LLL$*#FM? +3wIIN" p lvS3~׋J`x}<{1/?)ڹc;͎KPJ+uX5׋T*E2f78,S "?ٹgrƙeaa B' 34kup8Q:[* hjBmMٺ+c҆yyO.^fjjׇtb;XZ2Qu 7$*׮ߠbK9u7hH-lv4`XM]ׅP(KBLSZg`N7f2 h\yY ZRAvf&(a4hcM=wϞNS SSӜ>y1Z8}88]=T8u HM(e,.RNNv&{v!TJ w.v D&Q*TJdRFۉˑ#%ZZ9v0[hk@Bt G@NT&;Ө"onO{†D7M42~p~B$<dF\LE靰/INJF"hlMC-;šj`brwϞadt+W{W wZ0RZR `0hAaA>.% z8g||DmU*[x=|ϝGPonVttuSR\-dR)Je1b|ݬ -mݳ%R^1= Zh4c4;n\.'m%wܜ wϮM?wJűމ hܿwǎztG7q^ϡݷonz?>{EDvXޮ<LDaq0hdMڐHaP$T`d Q" [t,|+N ł/ =w!CQ@ǃ(219G[8Nbw8عc \~cGsgcGp9]An4p!r C=õz'࣏Z^NgwǎkAplVA&|Lj٬ ~?WQXXݎNNZ{PR\D8fp![eyž=brp>'FEߐERTl)';幩ѱugkRh4Ol&9)6r@Drrǭk6edJn")1P(DFZ mR%p8RK~Gu'P)SVZysgĈ#7m iQ)>8i(rܮ [9= [1^A"ldc6,:'JEJʗzLFSK+))Q*HD2%v 7";&3 NGTQXlƠGkÒi*qqte28}` H|ǏDH$q$JLGVV _PwɆh,N#;;  ѢX(Fc:cfE"HhnmRPO`Уhl l^x}[SiimcW/>iܑѱp/JEshkĄD\NJfѨh^̕ $&&pO|\PX\}NrvԔH]ۉL&%=: t-qY讎N2V1^m6x۷ʛd26j(!>>>G^n.qq:~' P1LNMJmu6 *"H7x˾h>x̱HNJF&x3>k󑛓Cu~GdeA(rQ.|z QyԂS'yvgϼAB} 36:N(w: * DByi Fc:\\rB8n7VEn. ^jLMϰ|*`ښ*Zmrd2)N P8D(?"gϼ ..A5Ցr,V F#F1^7 D? ZOVx]RZ:^{1ܱQmQx, JRW^C.Rwަ`2nFfaDQVZBQa;OgW.T$ 㣏=۴\Ǎge||໌OӇĘ-\z Z glb۹}Vˁ}{pRNE\TWdyقD"a~a4гf`R:zcZj5Y2o * V+#<|8Cv%%E61bĈ#FyZ晽I29Z6i 8NZ R{س{ܻ\..vC3>>#+8uf |XVjo?9Qop8q G.kҀnf#J964ޢ銇Fazf(+Ʃot^4&GBB|tq-lv =^@ Vpܷ}M+ HR-|> |ucQ jpN^jkU<׮ףP*PT2FFغQXXFfzz)\NjiikGRRXP@;LLNRfppÇljF7NarsͥL鴔P*QhjN'#deea$ i))$&ē[֎N%-5{ٳ =Fj5RQVYWFL&'IJLpam[4xLÇ'>ހ1=V5,+9ܾ2ƌ dbll䔧H$7o288REduϘ=Ș=[JzlIS3咗2aPUչ;9=Mӣe$;+%v;ԴT<_'uܽ15=oRB0 74"=nRSS Y2yʭ[ adFRCrJ2щnGO(w\v Qմ`zzH`<7Ç#qA?$&& Y2qp^ݠi)$!>Yce|baŮ49%ѱq9Q/;6O/^wLFWw/daa18LeH"ߏ-mG-$[-5UQ$mG宏5juT:zOFo<p`ޯWT#ߺdzV>'>KK)-)^w_7xs\ c$k}")/]h̠7PԜ<}47cZ/BAOw7@kW?fqaꨮfa~FJ222_lD`cc=% {'k踛|_8}x3 b xB33JR)`˒Ʉ餰 Nb~?N:jkiuM-x=z/d_'ѱ1hy (rӋTVVT*vA.[λgih[oDqJKi}$"?vãy[sP+2>1mϡp8" 0O]M'Gfmr7pgQ[ElGJKm#vz0哑Ayy9wnfrbč789+ ()-%;; sA^ Dvmx^>p ؈F?f?Op1A8FVDdt~?>6ׯT;B!v:4$("x^d2i$%&F:_az8fq#8v0e%E&!JIKM%n\ՐaDTDdkEgcxQkQ+m۸t"--x<1MH2jk#. )@R26Jg~jj`xoUu5Ejx}>&''& r=VT`Z8FþimK~W~){T¡@Ғb>*'qnLf3*DNv6[ǡ'++oʭ(JUn%)1$SZR̥+Wq\40al|<|VKA~ e ]hddd|; H&UNKKp[P48|p?c =f[VAD+;+KWz_,"vi,-HJڸM̅ς?`~~sk2>>o}BQ<kd395͢$84уR`٢XV&''h266J8&//CC348Hn^HOOgqqb ###J223fBII]A rnvVqn}Z>v+Uh[5I)rk{%_^`xdNA߲‚hYhmKuՎu @6_-m<N'TW|1glFv֣2ӉD"ynOZ]=J}{u$ bZ9z ﵯ˅6B!ffg0#p.׺`by} d|b8$>x9*911T󛠫 ;vMN]M\.n'&,vFj:zx<}'ܙY;R^Fff\%3øSSӴwtot295MJJ2q:y_X`wsή-Hyy;N\λgpx̞ݵOwm819EB|knz"{^#;`n߲r +M(Ru8`nc ѱߢߎD)M6BkS϶ŝKo=VjuWIIIyB[Z9H$"[?xdduκ?BB'x=OD'} g ^/Gk]8b[e?C~b2W 55{ﳫD}C#w јN/>㤧ˮښ +LP( D8tp?tdefxعc.n>HWO/^w&3HSs+V˟]%?/y>D]}3TM43޻σvtG sc{e=WVpFÛOPx FZZ*u7X2ٹcwZHII&!!qL , TWgh4x(VCؿ>vwP(DKk;c㔗P(bl|2]N%=%" 'Ati,^=7uL4e}ݾ1bxlhٺV|^: H>vvҒgcJ;dH`Q")ʱ[ꖺoh^ھc{ݩuՌglMlN*w;[뱯}SʌbNbNb&@Id${[ EɃ|~Xf.\;SYQ΁}{SR\DM]=]Y]̹wtb0H4)--|#ͭwŖ}t>^_ur9F#H.@A~P:v捎z 5*QHUf;;y>Vms.2882fx DZ5%'xں|LۃJB$;:Yzٹc[LP&xDTIR"hjnAp!&'&y]NVV&z^EYY [⢛۶'\.grjwN}{m23ҩo Y'N8qZZV:j~[)ʼnYZSRPTwW*5 Z-E CC|>qar2&҅JR er|>`Qz!CCҺhX̱ZQ/ߧ eN;^_%@)EEOs8zsH4),jj!wdJ~_$n|/Ξc2[Zzrv&ӳ3߽8B箞\.o`0vTT11?Er9֔\NhqkFF:2 \\&G!#EN[6U"1 |'ج)d311Ih2##("8qD~+drr/ΞD"!.\_$zD"D$ K8f||ֶO|gx ϡ@g__PyD$#+@'Ϊ&p8zwukrr )aMrrEڡ@ @Ss+))D[=KTVl˼~(mXMbF!W0@B7U~RӰM / $qUy Y&8Oo_?^D"ZztuFxF!\pF077jLIHKcZ/E$ }M 9hBGg~L`=v'q\2+ z=cwd~(}qoO>#;;[:gcMI ήnDQ23>$'xC8ς_"b@ݚ CT*/-)L&cIII^5v /1 PgrZ\d[ՖUT8];q&&bY]0,$r9U[2=5%)t Bގ` '?aܡrS%#xn6m~/*r|JL&#=8$=RUUuiL EQ$y\v΢LF(xs^/vlʕk7?x;:IHH304Įc y9{t 9w̌t$)z|o?5ܩv6).. 5/r`^΢1H".-['iƉ>,,, 1Mr뤧ӿ;jkjHJJǎQS] R1]nv^?l,/捛44=8rL&z;wke(j IDATv툮n(-eC})<|o];}GBh=mْzs' nvnM}45Dĕk7ƳD}C#++D"3>1{֎$ILLNھ~>Fp:rW`e=]w'p:$>|G?||L&,nE@ Z2~?zJEqQ1Z֖&&&pthjK2l+:NǒuPZ 6/}yAgpp؝FCM]=EEzn\#%%ں^ZډH; TQ[7|tu^s3l6;p{ߺwZ ZZƍ8{ #ج)u:*+ʩK >?{TFgΝgMNҹ^CuM}v_^[2@@֭Tmөܴ)VwuYaQGm͍Q\R@ii7r/2:ꚺ5VQacf` jJzbd u k23nEKR 8Jq|@FSS[GZjdff0?)).BV33;^CR$A%A^#//^OA^RDB.LGgeee:rsu.2NzzؿwOlc:|~? h4fh(exd[2&/7H8*DHOK#33FuK/ΓSTXȲ]h4lڲfYa #}mwIPV.s^/VVT"IwdhxVߜ\.;1::fή517j|>%l6g_$ RqY`o]0Ǐavv,rHHHYb-H?@V -f3$]5!Ah4p89qzsh#,\c֋7^?F_IIDa6n(CӱkvnFp8LAA> X̤rnj F';+crsst  cpq^y6zVc=OJM]=HummluRRa4`yylv33XSRhmrQYQ~LBB۳7Q*LMϰj FSK 99G`19x`>sҗa[b> .#IǏyXz>??zwN3=35%_}GdbjzٌNõȍ^6UF\A*f ">Պ9O n= )1]{**k2%b2=b1*ϋ=~8MdIIIg0)J6RR\( w~:ݚs*.*\3#[Ue%;8q VVVڬ@k[;osONJ" Q[[-ݽʊrRRQTH*+ɦIp:籦DP>  Yt1%9\vI>s2ͩ/""#OL e|\yxhP"-58N:5t: Q >|F#'C.F}- s1ݻvR@6`0ȅ_RYQ5%%`߿8q֟aww PȾM$IڞlD7na$kEA0ǻS33) 6CfF:>i23WnOo?| UUzM>S2ROIHH  aZ-TʘB8/ kRyj3{Nb1f99ƉUm6jjQ*QT9,-/Onv6(yS%M-To0q}Rg]Ϛ}'j( ߥޓ,Y-(04ǶUlVZ~@ @iI1h͈$bVpٌLYp͡|#cwƑ$L~ C9|T},\,kvn553==Û'R* Bd3:z޾~ Y\t&=-Ϝ`0pko'HmYXpᜟZ22:FNN6uGA'J$H驹ԏVĻ@EhD za5.<8KqIR|1yU>7';&w`0(|;r lݲoNSK+{" es%x0 54"-óqrsrs0=3B.GpAk5twO3>1ɝ;w8~HKKӉ(LMOp:Y^rZMWmT*HƦ2Y^^ $Eoh$';p8lܸQnsdL&T+rtz5P8^HaV-DQ ٦gCU:E-umݟGWP@@3.ýl:!Ko={i@ ,JN'zz%%Ex}cY^^&bؑCx˵R$ .LL`(,ȧ0 .*%N}'/7͛캍fel|233pi줬ʙsCs.c6&&&s8Xz1 LMO{}IXYYYe+'!jz]ƺFuz`ٸԄ$I$3==âۍ!-))Re333h5d:s']]Z\$ ز=\^jMaq nLK[;}{vehxp8@КM&Rq^"`8qy!Z޾~j8fg3??dFT`2%}SGBV6QEχh4RZRLJJ2&$$jPVB  tγgNB$$$p:8N6jǔHx7Ko WVVH@W|s~eq<)>jA0f$ g)tJFyj0diiyF:S4z^|>aE\tDQ\AX37&UWYuoSzĻ/?s|nܬ nZ45LeE92{v|d,B|XM'^?Ƶk7h4$T*9~0S ځ}j$3=WQIJ2384>Lh!))"1f7wwN?0,fgx8r5,-q`qm `JLvd2E-Dł9O}-JK(g&UzD\B@Vqe 󘙝m[b6JőCikDVq퓄aDQdii@0 D=5ҐDDQ@!W<oJ`cY}]ttv! Q\cm vw126(>u\FgW7r۳\.y"nx7WH _|< NN9߷ S` ?u'^s=پh\EL I ER  z^ۿ1ǟRR\̝ B7 "J9w"ii8/0MfohEjJ׋~?ijD6H8E.V>ttvN,3`e})=4p1|$ٹ}cw1M,c`4Q* }:@_nJ! c^A(j8]D:!=To Eons6\oNC.zp2rBrrn3γs6;D",\YdrJEWp8=w/'?vB> 9,vӟq1MLLNaMIIN}g( ^?~)*e{v)!--I0$5l\X'\h0V?ůOc~X^^ǰ۬wtpo/`ob1p8싳TJwwtIrrǏ2BylDiI1`_!`<͂}YT*5;z}|gx`0OAA>۪0>1bFDp{;g.vEH%=|08#㾜=)*,XU_ };LJ{?i>ʊr*7RЈ(MX*^lPj,\NEF9ucGVeD'BmY9 Ɨ0lM8wGnR]tCUpyB{~X ѵr8qVw= at YAU3;7Ga~>7k0b6q/SS47Vs.bYe֪:̌hV*5H$…KX   T֡T(hnm坷p4XQs9a*+6kdfd9U[6cO#f)o]>?snyהr~q5x& рs~$7Ϯ񣴴 |pEJKYYYBIq}ض=|  )߸~Guz$)z=L VFW ?@ f31M෾ D / c{̩ܽlX[O/~heҪ6C!w_F$㖖ABb||bjB8Aڀ>o͜}~o FÜÁ h5R6))x>rYŲ+{zXV%$m&P(dee}OanAFF: .>󹷯H$B,N' n]I$!+׮cY۬4&&bǽ:]?_on;:매rl6+U֪-,]Ojq: B,,,׏91ji~?p`0׳@Jroxz\.>ZZ .~?(R $)+L8rDJj'|JNy#ZS8w;mezz;s t /_?{PAb}$XYem0u0{yS%Fn+ RN !bpGޝ ³s IDAT7Sb"`(D(&t7/7҃zp8狍 IY1Ot}4̸=nyO.h4YZ"r99߈Ӑ'( bl[r?\.J pg|)͊ h4jؼZf%Ck[;vvI4r‚hVCA~~U \.CPͭflDB) gCY){zhik0TN))V0( f3I3m] z:::q{[p"˞8N" 1>1AvV>WZM{E/Xy)ʯ288}{VcYtٻ{77kp-.Kbb"]l@oS]]KYi ii\.zHgxdJuM++HII^ykHOKeCY)C籼r5]E 2Q(7U244 T5~dyCc#{wƉy/U(b`0ȫ>Nzm]=۷mڍj,q/ZSHIIv("ݽ3.`0ټRQ]dz }}C!*+˹|ڪ3x4604S߬fyyaJK$yEԌ $ :꺍n ~mUȺ6++2"G D]jP< / !99|QER>kD84O.8f1%Ğ{Wm3DѮJ'BA>,y es%cwP(Ơ&*c0L:Q`8[J mV]Tޜ]B/]fbj|kb,zz(ϣʰZSHNN";;ko"7kHINbq͚ZظYrsp{/2nɵdPB4] TmY+{t9J0G{Gk^[l^Y]K~^cwBACROPzH`T_242Bvf&G}-oۊ$IO['O09=^#}K y9߻ghxaUd iaan5nmdsSQ9ܗo]h0PXB ?= jnE2eɶ`@D#rضB$Ikqu9| ho="JgSE9:;{w2F"n7w@ՐB@.=D% "]֎*`/7pN~Squ4 iF+S}(هǿfkNFQ /ÅjTr%۳*KJmylNvWҒ>ZIzRWxs{xG>)u ޵AoFRh4#8~ M#+3޾>jHNN󑒒Lgg:@ @OOMͭx<d29EF 0;;fE&ڹ//_ETRR\DuM-*߬w~[$'%aJL87kx<1-ܞ>;0'&rkٱU }- 06 sg47 > 5R{ ^Ȫ/~A/DZdB&REZMJr2)h4T*U,mP(P*X,Duwd2d2,|;Q$dz=J^`@y;AP$5M\.b6%Tu ꗲUbDmA +hTS?Flr^J={s8wgsh7 .ϺP=܌J  S;ҊL@PfyE6<Ϸ#+J0"AEG(| ۬1{#"9ykEeE99YTzihlIB$|~?>So DE)2wN՚D$[ DVmA099fP(ĻߦgKk oi81ܖU\s6Jf|xlwsV< tw>KȞQ3N86ێ VH7Z(TdSٗWEΌcy =dbey:\\K:e*Ҋ؝ ǒI v0ցFfviEJ4cJ<癰yS%}9*7@[{=}LMp *6 PUMXS@Ss pVJx[~(  Z?Ϗ.!*؛բK u:DQ$Aj&J%*RFftt,y'DTGqC7y4K+0'x2M7IС3i%Slܶ%"h09:̦ĉmewf5z26Rt5z c7$39LS~'vs4(H J-?za2M8ؔ. 9P87NARpLA9k^9RZRLiI1  NNv7U0#힬={~j>l' &}gYd!  cP(z (*A EӅ %@69Ѻ$A@j4a̪յ5_%̢ۍjC|tl#1TaqCٚו$$$,<%b*M(bzz^G y'F\F0+++7p_g_=ѻlg_X bYg9Or[{&S"~߄tD==W޾U?s=45k7k;z@SS ۷m=|T?L!ɘ4fؾ \ :xVv؎Vl2̾=R == \ޠGVVP(M&w 7Uc4iji#ɨc=U*騔/_AW!ၚ0I(,,mOOWm*U*JpbbU.%HFrrױ.AIqZ7"l6#oTs =6F.;q7iniP( {wb墧wDV\Evl(+Zijkqv*+himcv΁磸@F&&&xOFNN6׮@TQZm.fxx=w~QYSˍD"*d[Z(*,dzzt\λOqM4j598NMܬ` /gcJ;sht@nL9Ǚ wՕkd{*ֵ[P޵k˵޲k]k-mWW7̝<0$94 t4$@N O4O9s5_6GGg7Ͼ`o</#O}A.\Lvv6֢"nܼņMX{:TW$| (OTʥmRٲsiI񃎯\֥i ǎN:*+ʩ(Ooo\9u:v[^Vlۻ{۷mY5ZZ~s١DO?$cǞ_oO|vR$O$W/S^~Ѻx"PB#n޴b1nݾfrjLƕN4G3,6_<& ZoN.kBtyIYt9zph8D308ǟ~4' @V(o;X,fN'نl!N PI*+erj'r9$ 7|H2O>#344L^N.v;O>B[ZW4P(X8}.m[꦳aT13;0{w朗*ND*tJl9jkSrBp27oa\ފV!/75~yf_~~mW ?8`_~8Oח&7J #q)0wG]Tc\!>cĬP*lkJĒG_,Ehd2ؕmPD<ʷA$=r( Wt? fXL,d׼Vb1BF'"HV@(RRlETy;JNjX[lf|byrr,t:$ n{>ך[D!5i{Ŵ`tVY¡ee%TSPrx(TURl-BTb1)+-A,VʁΝH,l4RVZBSc rfm6 tQW[BNy;eexyL&n{;* χbp8@ x*%ָʐ!W#J= ,qpd"<j\Z&B.|>ğ)s88ߘ&9!: =S^ldLIG}]ʿ&9 e%DJ%"ʊrJq:]X,f,f3H$Fd2T*?zݔLD!bj-B&Ä#̦Tmw0!]p> 9jJSڈ[l"QRlE$j1Six5A|dڇTjkH$iy۶077VARM^B@RX ­E֠h8t`*J Zz(0Mde9z.D^^.vfp8bU)d2VW<_Vx)"^/r|E'j CVVZU;LFffm/u 370H;l޴q?{cժGReV"Wt]G##a+%Ti(5LDM)/2W ʜ;LUKAAߌ1{ѕ 5B̻֖VU /hT(lX߸BUIuYR|e͇k$ @NA-Ɨas^F,\d>+y=zh4lyoVA}\V?lڸ!=B J^KT ,RjV7њhjq`\'f)(\Z'6I~udu$x8|*H[ȓjP d"MY\Ώ}^@IohjN╫i'b;RW[M07oSS]L&Ex[/\q=d2r`$g]@*V䉷bZs ֢Bt:W" LLLtj|8{'˵ yѱq::xlnܼESS>x<|`ݱ^l2C Kp8ېJg~N"܅Kq452 RT/@0#l.JQ`[>hRѶԬ/lY#Wim"(J&T=W^n.]==D'ry,n\unXyb1-7.K>exp\@,_QSS3<b/~;˜m D_:~**ʙcD"px^ERTX{× IDATB%)K-ђn<  R)Jttve&311C\vx<ƑCq\T*_  j@$Dnd"A4!y_:K^^.a{Mi9ۍVftu23k#H^^.;oO>|IJ://M\knyPOo..7&Y`u\m΁}{  ra]H$},:-j0dx~wIg*-6>&zeNBPV׊d2%IVK(N[".ʧ3gϱs6B0Zf+{ay |2(u+L +6Pͱ JqZD"N RI-ʴ}^-|_!?:XL0jW܃3gqx_O R_r^G@^Ny9X,d2d2Ŭl2QQ^ƢImuFFN TJz!e'P0FI륽7ٱm+9gΞGPT*iܺ} T*%/7|&&ŘM&LlGF,/jҕTWU~/\Εtupcܼ}1 ).A~^.6 ښP*ETx XJ;% 6<2dݍmn>w\;h5Z.ͦ>?eI;tud2200#mE'SӈD"45==rkQ!vCz&;;r||)<^/=}XY D<}A*iJu  Le̞5@Nz|Fd2IO}jҫ^~H??j-brjPV GB$?!ëƌ?Ȃ"> 8zp~NxۓmD*R]UI 5s6匎S]UB!s8xp?bq\2=3Key9cܼuwn\v yưZhtvPVVB@F#eTW={:77GҮLqo`׮w߇\7/N!xa>47p8|q,&m7FS0e%Ԣ}E$QS]o?1;w'A(EFFFF8rPYx}E4;!0򥦞%Hrcn (CgNetlq45JLT&E,JwRNx*8zx,JZD0PPO(B#W:v++R* DJ2 @Y VRR'HEzNxBCק{$JXaRNH$H$iLMu:P@4;l\߄@ ӉH&0LCV#cx^faBBwpn5Nf BH&(/.X~YNG3dx^|S fQlLLNMT*ҷȇ7gVϮMRfV! b$`sqD r1͓%GęZ YE{"0Bh΍! (270hL፠U{#,x#hbs0ϹQ! )&=[TCq}f]akĤdCLebu x׋P$J7!<-d.Vصcw=ݗ͟a?1!tTSNeS[prϞ];ܾsښ+^'&XWW!p8s)g5=Yn߾!m[ICłF؊\Äzz6sӕ{t?K͛;p|"5 oZTD'fjz,m PWc͌3D^?cĬd_,8D,RfgInj5T)2*Ue@sˎըD,^(Vm6wFh<7rgMUP `=vWg# 77 EӷSr7N%!KTѪꍜSjQRfVUH9D.RX4AEEQ#a@ +Tmt_>xD/U8^~?{vڵhZ #s]GFɱXPTtu{mY]oaXp )$I75ڶ͜x8]]v{?& '14ht<"!ߐCFFST&c1keH%Ba*sS"dD;#.I0%/C .<19rIETGpcp ^6_#/\w_wwby| #J|e2<^/"?d|q,<N295 ֢"ܽG gDaZnr^;?$I H$bxd[p^;ZYh4H$⽓'yc|HR$ G+\.v{vDPx0fg377Febb!7q̗B9|DVNj`ǎm(~hUE^0pQ7?ٙORB)SKampM_?6<-2| 8$.H$x&#yyÏb]݃{%hd]k~5~WF.ex&~>6.\L?$x/[>SlssFߓǽ HFϸƹ b}[|$IbxQ_x<>nl|_ /)+VN c6Zn@QQ!NR.DZI8nJUB ߎYx<FJ Cb ΰnp2wbN`[6ץ_S[]MQQǃlF*219ɦiniC"qz԰R߷F(@EfK !k.]P(`]ܺscv6v, /_D;z-mnvFH&tG(`4lE.h(r ~qZ‘:!8z*jΐYRV^yzHH$zULj$wr^Z^z@ KkڟZeȐa:*v9FNٰZzb0E<zk`'PQ^\6燐z/԰ ptrmrr,߻7qHĮ H~Fٱ}+}}*vY*+ٷwW]dl6 Z"PSSǟ/_rZ3@wͦEnF|==kVXtRd!bD"{sّ17+ Ym+B8EFٖyf+x  2dxuyjᗔ@0ñ@ @.a}ccWƐ)JKKbq4m7غeDAH$utRTXNET0:6 R 뙘j#Iyy)[lftlRn߽ZݻHΝȼݎT*EV1:6eKG"kc6Zne󦴔Z=immnOannJD"F"L&YXX`}S.\B&!ii͛him۶l&Hz9;:F, ]ҟ8>ǯZ0iS?JEl AdԪvWg:HC)/ܜ2G=Yr6efLMmYȐy9,.y6ylr{8TVQ$?/T"efzP(D,chx!*+tXH$x}>7o^Ԧ@ ~_?c6olͱ$uL&#D"Eh)t:-2X,HH:iDLtMlra2b$ rsrDrUD"33ȤR2)3t:-X*d" *$P'; UbP|`Wx;.d@,~5RLU'C-DN#۔OG+p,,<] DH$,.:W8bkf# ӲϚ`0DK ;octtlEMwק:qwR:{شq={:19IېLeǦ'&u,.:s۶kqck 3dxּvN u(/+%Vx=LBRU$ Fv;&A$eH$)/+X`qqʵDw'l[( FFF(/~?2@2Wp==LF#`K! Hd32:FVTFftl*EL&"h TB8A$DҧRdtt2B!hZE !H0%EE GXW^ ot\ngH$܋bJKqcY؞דT ĊR(Rl-BT>sƱ6BF*PVVꓰZ¥+[ {'OP(][6mOL$Zs N'k( 6m.Qٴq=/_MtPWSca)$)H4JS:nݹK(ȡLMF(+)uC!0-m7iPYYիٿo7nF9zg{^ca^Yzgn'&E"mu:2<2ʙ~:][frr}* i 2dx⃏>! s6 ӋD"I;gY-Lp̗ڱ108].*h4ٺe#bq ,/j5 Q(d' IDAT 8R @ @P(L<M&o;"Sӌ36>P(LQ%sRhtErd,xU2ܼ) ^VS~qnܺMyY)l#IiezHD2L; bQD$"X,P(D(Ceefo3D:L&ڞD$"/>d(uNwqnɐIp2X !H0+jIV[ƻTbX< ܥ C^cߞ2dx6wt<c%VOPI,ٸ驄x<:6O`2>u wOTUTkz%Xaa|x!cuecٲyGV,dn2v;>P(FKDQLF#HIrssH$A JP$!y$1fg355L&l6$H`0dXX@,c1ٗ>kvr,J~ .ijlHFWVSVV yt2>1I$!7'yR6mkq,,p◿! ƱX/_ ?]xшblܰI' m9jkVfK:z8|^6nxZ۾SG@7 c1ܹǶz 2xx<Ξ;P(x-Z[oihw84S=r! ,>?uCљJ xDK[HuuܽN8;l޴Q&&p=ܺ}gh)H&/#UEoP؇"SY4jE !i ygp jHd[j%`.;L(tVfK .U zK5n$,!>#/@ |d&K|_P4_2c-c){0ƣ <5 Dj"z.3 ߅=BL$P2Hb2)*"(yD#Xr@L&# j4dR$ "(XhD  JSu6MZ u(zBz7ocG14<C0os~I(ͣGFF"v4ڵuRb&I4CP0<2B D,308̢ӉT*EVE,t\qS\l%?/p8hz /nӕiLuYD=T_ \@/aq HD=ģBi\3uPOqN_Di('Aax F}s̭u[2b$ FF%HԄy<wq>/]]OGg=H|S3]XXX L2>>lsttvp8Vl!C<l4nB‚G}TaZeߥ<{`%X h4" udl\ļNo_zѱq|-7p8cTUqmkSfך"ɸz:>MUe}>M=pi)@\T*999LF͢ӅFG93x<-C-PYQאJhd ^_I&bDRb]bX0PF3J_/!B$xx:]B~1\"d\DbA$>}<*dg};2 p,,鳼w\}b.\Rw3>>8vn>xƆu\omŘMYi [ȱXͥM b1b/]$u$]=x}>n;wٳ{|ɱ#cjjݻv?`  axtJB. Zl?oŘ۷2<< Jy}}TWWemtvuc1APQ*$ j\mn!rp^50??ϻ͗/ŗskd4{?Oz+f2<)+u-2\((~g6Ck]Շ &bijmݛ~OFkiH4 SΊqLү_QoDoB9QEtv{:.=a{R hmA" `}QLV-+b gl^{D""y<^/wv{P*D"a|~rZ͖Mʢ߻zzS?d2NH$BRQ^!@0d~N_CQTXdDRK~^.X 7&azz?Z [ ۍT"AբiňbNg@Z* |HeRrss(*,t ٲi#2v"[lR];L$`-dg0<<*CokFkjSS]柽zY \҈! ZzH$j|\.7`pf ϒ*15=ÇVN%gN7]=TWUB,s>_JЁ}c[Xt:ٻgw/pc``pήn ǎhZ:=U醛"242`_N H̆XlxÏ?}{z;8# 0deV` +0Y+o ֊.caD"bY>ȵ(Jr,fD"c~N]瓕 шVVk/^B c:F!޽u.{Zk{]add{v3=3CS:utٸ.Rx.\B($77XXX(Jfm,nuu\|XLmM5׮" PDQݍ6C$I._mfQVZL.ѱqm+אHٽɐ2>1ñúTTWUT~a(jШT=UܼUVn]zM(R[SLa2dxk" G" JebdR)ϞEnݹ-i޽wR4'r #CQ395(#{lT*Fs-Μ=G(l.u  Fٳ{'7a;P)_bS]==|E~;S)s{vхP(8/46,W}(EJRܷjS3_lx c 0`S]]UYMRJQwq cc{ )DeDQ~λ78s{PڥON|NMu5j@ ?>=LKAa_X`gvvnmLRRRggW(ht]sb<{۷244.{wYZZ^z͹ٹr'wJ ?It:o~{N>Cd7:z~U:ۗb1̖f$I}НaK2 3soPW[giJj58},V֭-LF\N'e%a'(*,Dɧɩl[N|۷rufg((&+_pX,F2h41Vikf ,c2 %%%|R) U=w]B7z!kOWOh۱ BqqçTWP} @vU[yVxO0~g#L?IIEGb:N,0${< f@0̽~{Nz]m;عc;^CVƆ_[Sٽ]_g=a `ݓԘ{]@ Ft ITj-b:A&#x1'K:Ggr#x'Eo.&>#܏^s eYBEF.O5EM:@$XTF%-xЛȤ"k$c>~N ,sVu][uuEE|r4s\rۍc|bϿK_3ЛPi 6{;x2C2=;K_VDlfzfBnujٱI&P^VG,EEd2t:]-sAl6sz'5UFvlCKIqNQ_d)(<gb0z)/^ b)'eKӨ~uy<'09)}OxC"g(Q]]}0I]{3wSQ ӍUؓ *(1]x~8oܤ--EO,;w)+-JHRy'>MOc OF`~Uۯ;sȒH*vLsżWo|{FCa=vd2IiIIÎh$X΀FγWvXMmY/& ӑg>)JZX-DQdmlkFJK9rD^p8dm.Jɹ:Ap2D1~ fc`pE).*CYY bX꺅Jb!Vu6Zx5z]ȦVw61)(l/mжAl y;$"8c 2?#e-gs2֢ &&Vo1~ddb*J40J&BáNAPkHw :!ɬ^ F|4 9ڶo?|?wb0{z`@Ӣjh5:~ׇ``~~F``rjI4A>ݻv26>owi>=.]ǴX'!J";v02:Fii jcJ[htZ-d*ζi ggD(2xX,j\h4Ӑ  #f0X,^4럩͠fP@Ñ^GsרX12 K9qA"X\MT3ꝿb/r6sW %?3|ꝿb?#@z篘1*vX OEE91}M֝l]woPۛjs+lX}{۶;~hm[=}W}܎TPxVy@$b&JdX\z,--#"@EHٹYU4EQ6F"pt:"DQ$Je{WBܿAWE ŗ'eG$A$>@YFx o."ph4toK.NJHӬn_b1bC~ɼgΞ[O Laql%mhV{+DLT܏Ҡ'Ȥ¨uf4; Y0T2u"^u׽NM`)Egt3X[h*53C'u $/R0M|~L&>ÎJ&EDIg ɔY^bD#!t8GFC@$ennłϗh1?g_`4͆?d``Qq9x}> zE\zMF4 7nvavnoL%yݾoϞd2m;5xiLF# kp>x V3L4%2:6Ɓ}{ǟo_O?fQvl#JGpet:M׭TWN6yU86Ͻ.lE(" 6K[?˽^s]/Tt>+wG u뚛  kHd9Xl͚SnbUAcMFm9 a4FFZmnVlI"bqGajD"ќ2F!Nstj2ljEӡ äRiG[x:`21Lh5Zz8_|5 ~>Wl|s,U&#H ee\ `ll#Gqz'zS zL&o?ƅ 6bq'Vw;wНs7͙\.2 ݻx2hmZϿ^)< )m9j͏<}HYrc*DzSTXbYSQW[ `۶DvIAj~Y?ގD>ZkVPP4JQD"4MbyًFJE,vo{tNQTyh4 &U\u&6 l%F`XwqW.Di\.'pIQT>sB7u5456]}g[8&Zm6zzh4(|)vpǙ-T*E0Muu,-Wbݳ]:?]պy71d $a6e{i@L& /B{xˊVaׂ `28:w?jͶai~ /kz?xzRc۱Xs!OzI%Ғtzácw;.'^/pH4fcߞy ݷ{xMF.^LΝn¥WֺI$[X,ft37CJw¥LMګGǟjfֺ*_D fDQdKK3:A<@"=injk]. ֜􍂂]p^_^Et8iU?1:6{'Q]]˪ FJ lj@(Bo0U+(GE}QUU*((<9XP8Sryiu_$>PPP.^Ao@P+Ȥ3ԐGeeph8'}H8}{ִ{nużt(a4txΞj;?JSQ^FE "8 9,2Mhnopihv0ZTwN^ ;֌_t`p[ )zzbeݻvq.\ضbYs:f*(`(Ɔs1HRuWŮzillPBĢ1ҩ& e"8ٍB3c! Lj`Ǧ7kT* kv, {Y]Ga-kK}f|GmH[pO(״kVPPi;'2_ ˅ZBEN|%TA {:ut:MKfI&LOOSVZ3g1 5N}s`p{gC+nfddj8vUUT I_X$cZ9},cFJ0 ȲL2djzm;H$hl6A 4`fT{]1?aD֒IDLܴMd1|' <Xʚ(3^s$pN^'}ߜFa+(G8 \r*&)a4fX,\e`pƆJi̒NgxwqcG_ݰǿ9tGlQ,7nvQWShd||L% !*++Baffgdl|I.)<[ju^cS;sWVR7,bތҠcrWBE" #Gbp hVَE[~LWNR$IF# TUU>3gq+OeF PȲD*d2_Oetl7cjz xᅨ >WDVS\\<1$I"Lll>,Id2$mt 7=3$Iq={t&ÛǏ?|(d{Fx0؊:HW0K1Ր y1؊YΊD5ۊIdID*⧰Zwl2}}Ik%R)ǓjBh4d2ϑ$I"@EQ5JYs4`P^4L?|%Řf*+*eDQٹY$QE^[ Wysӏ8{e4>G?gbbϿ8IMM5S34567FS5N?@uu۶nd2a`bb;omZvlONȿ~EWm>%8v&f\N|{{:0LٿHg2~dI⋯fߞB0y^ϡt*Ų/*َ|OJhV:l]Z{Sc{r'7obp^Fak{W]UCKynķ7ondtt Q DRѺ {ښ5kiulDv~zf2>99fZyz>?ؿ~*+*p+34j5K^e Yk /,YqҞC֣*(<=WPPطW'Oa2H$8\Ei:n }]l5 ںQfg~r lk}M/9|hΟV`nc41 47N)..B@%T h9 /*j¾E*Fgzy5^4@JEE@ju47STTxoF*_}$a|rm[t˗Һu 55>E7:;qh`֭[bD#ќHhj8r}T*Z7{+Ё z; t*%8B<Mx)B'` !O19Lܞ'L`-u2ve 1`-3؊l.` J;x*f5id`$fF/Nq%IvTT/{~siʶrY Ѩx4Fp FeeHMR)wd|bRL&#E.ݷocM}& zSSliil:3瞊s;ִnB<q##v._etϳoo++! ,e/_Ur91m;si!N㨰Z0q}RZq:c-93F&%&ώ Ĩh-ab8Qo {齶d1d8E*nR+5Xd%ZtAxalm:xwgl|{+E;Oe ^㝷wޢ*$2b[AHĂq V}.ECS7Pk՘F}D}1 z Y+sr7=HtoPYZbqh 09j"(pY$ʘF҉ #dV7@P = Fv#*@Ek KcTPPPPXEgYO JaZH3.Ŷ֭,,.m NӉ  Q^VVg_F4onbiyQjk```"ng=Me]Aoz/G4CՐJ /SYYAgTVT095$Igi{:XZ^Y}f ۽m;mBabN]}PQk1%i(i*ZhvlQa˽/E$YA&>଴Nf궠5WIX\<ۖB3W[یޢh7b-ਰv2%n@Vaqh d ]JAe46!V'(((<2td*|.7c"#xUwSk9Q沞u?JCPeYpVg^pjctoy|*<R5Ary} A9==v#{c^6Q^ȗ_D.HVEՒL&s9`QD%hZ4^N@$z=tdZC&fvv۷z/#f2Nor+؀^Gdz8l Z`$IȲܶhd2hwUAEcaaŬB**AŖ͹GAEIyښuESܟ! X,O̲[FaKKS+fSPPx6}KH4esS _G0L ZB!VVB[~#¥bqZ7ɉ8zgPUq2m[1 ,,zxF--LLNrEf4j mm)).ysS995E4ysQdhU zGF߄iU7/ݷRA`dtL&Cۍ里qC^SCw(,tc2= HCT]D6l?K> [3۳@ŶWa&&ػgw}!Lr=lgrjAiiI[ s8(93 ;ooOcۻvݧ"(=l۱n ?=BjZ7^?V7:+,\ad$`{(%A7Ѿk'^b(Ybm= z'k7:leٻL"`㡩Rjkky1B0͛7H$O8y4 ot25=C,cfv@ H*N$T`0ȹ d2XN3H Dqe$ntҵ*7gX,mg_|NN EN}sQopUDQdxdSϐNw>x"ߜatl(rEAe{zx $' '2('"Td4HDdRdY&OIffT,,"H2ʗyIxخ^'@$$NVAYZv0FAde.~C^'|5+((ɏJ;oɈTV~&6[jr\N'EYi G:рZtnl%%Ŝ,{PqQF#*A$0=3CqQ! PX&isѶ'%&Ʉ^/?ychu:D"b]]OE`tl޾~<9fZN'EETWt: lnY`4YZ^Fh0Gh4Y6vc ?6jF&K#(c/pYDž IDATS#ăq8 Z'GXZ&⍲4UlK R+u1-gs_CU^$^lǎ2;7uXWVA38#:xYѣGt;o!#ӺytfsS#XJN^}wy>|>?%%Ť3Z7c21l6؏易}ɩ)JJٷ#SiZp\ Wq\H(/+x~ ۻ"CqS!cM(k)8[`-ѩ8X:VNQKC Uvfo/PQ̭\AH)(((hB/D^h=G: 7r@C[HR~(SШ󟷓$k`h4庒,Q`Ҽi+n~ 1#fl]6EZAA JnFkRT^AKngwFn&t*Js=ɃݻHykVTJHTPPPx +:0sˣGQPTf6?Me/=tN&&~$ ߸|Ze>?! s0 qNd2ζ|Ij8vUtz ,.z$yv&X,zX,x<;oA4Fp%IS||ujH<Ma2-|>JKK TT\!STfѳdz''>'}u5/`60dtZ-QĠoo/>|Ee%yey,f3hD5C6_&7TݮJ$2>}&պ-d2I0|_/D۵HH47R)tc x|MM*jH7bV~\rY'Z؍(IH$Id26]ݷI& XzӇN0Cwhjl@ `~~}Λ]lTak~&t:'ٶɩiv$vz+W0M>sXYpp6zr4SӔ?Q5GQ|>?.s]6pZ&Ocl?|#NIR@KޝGe|>?T ل$9组B!fCY=6t:TUU=tPSJ%Xјw#K3l3FFxntv8|UHŋEc~~Ϗl󳹱@]m 6[Ht:blZ2 xE6l6~jdzf~nuf[Vv@$"(Ff 7ZB&li̝nial|AuuW]g||"+Ѿ@0Haa! $IZ#}T*E-|{I().fii ZU͹IsO$v8T ӉqCy7͖&:ovq`^ٱ}==N&x<M"܅Tg..]agm|9lw;# lJ o`ǎnqAN:<_ 7nv,Ѿ"wvc{+ݳӁ$KhJK&r:s2m;qE*++pmn._Ɓ{I:.zVb^Fף8rji .\}WZ/RVZʁ}{d2 z .L&ӆ~F/*w^1kZ:$dN VũE_ g瓴84xiIfx%V%`P\Jd6P pۛ&(1YNH84X&5sQRbK)m>J` 2v5CA ޭV'EْUPPx@JEQe/`Xq\D1ddV+6\.~Ybi4 AV jL4%j),,Yχ(fdLyY DQ&H.Au+Zrf#Muj?*p'<{Ͻ޽+y)/+wD@JBC/*mښjkj\=ptV[y_l @]mmnwν~c-N ] %1kޫ1pj6I$-W LGD)w 5M%S 2.2. &`luiyZWI *t+ )}kkh&aoW)U ϚcNIkn]k}R񰲲T2qLbvloEסjs?V{zS^^gGղ:[`2\:zi۱.Ɔ'z:3s?ZW'OO$8|'Oh0pXW+yzSg=c4'22I&Lz#N%Q7%WNUޞ60f];X`;h3ݷ{;:FHޓdIT%2R<Ȉ/NR_sUp8F߻_ݤB8##h4r+ܼՁNZ_zp8J<{=DQM_*[{~pr}gɰXyE!2ź 7 śjټ)J!KJ1p8fchj&h$wpC8.ad;:gn~ G/208Ğݻ',/pq>t.>iCe}NJއLMP_=~v+LF^4^ǿ'77Wp:^[CSK+&ffgqJQKjƍVD H4ؑCLNN195}dYFS 3T \z NG#6 χn 8Ӵ`УBx>nutq>;sF3s{oR!Iwqo`WO`*^"` Baդ=5D(3o055Ź().CDh{a:@,.1y5 ^$"=dX,z$ "9Ln M$#{(/ŷĞ|!"|gfBAKAAAAAAtIS_9| ~OuUe" bIO';իiؽKmCբiWkh?9zE1wm[tއ8NN8Εkζj7䊠KKKp8iȲLSs+};·_3<2x15=]el4#E5C:r, %a‹ұm{c/iؽs#(]۸Ĩs"LI—7_Ƹ̏˘LkiqA2Rx1Bȱ:oS`c] "hhVv80RY󣏐Qr~oV<)gfXPDMn·uB\vg֝pz8JDQdYTWGy,ٌA55'Y?0@3,wu>w'MOӷ aZZhص@ ?ܾӍ,!1<.^bp>]`tt웺@,cii y^|^(bLd2Rcغ `Á&++aǨ2Q ;w}wMj4JKpzN24fyy޾x<AŵM8].]K˨fA,W@w{z=k|Y'r zKdYڍ&&&&7@8eZ>G%1ؗO1B!:n#2v;a hQ[,h>M; Ko @oǷ̷|Z&Y +$FgY8(((<IkeYJB211)/_ ?X,FMu5YYD" RRqz{vsF396rrxfӹ$ǎƐb`xd!*0rlH$j-%ttF"D23ijihSgySjDrU~?bvn] ''qCrnjGpnfqNJJ ]0=3˾=t!wpܾ[s._FB~~>] Fc Hun)p8 {o;j4jgzf@ @ZԠl4Չ׺Z5i[$HEJJijnMZWVk{8[Jgpp(if#55uCmۺ\U*ښ [#??斤XNGggo?뼀JKX91brq~t:Gh4277qu* SHRR `0RRNKWsDQ'ؑÔ0?46"\3o I/^?!;$I,E5;o+(fjɰX({vse %EmC8&*I\|̸tFڹ!(~-A~s8u8x<ڻ;g(,f6xq4OBm6|KKtr ._Z9~z}[o2@gmμzQ$7f|ɶ_F+[=JzZԘ|wPV+և jq `6ǥ (䑭I!%I&se ٹ9,kkb`(ZTOvyZ󱵢9 = SU] \?U452>1믞fp> SRTr, qxY&lNLzzjtm[3)h4t:t/O570H~^,'4dY&--?XLӨ(/KhZd4ݍ‹KjE  &X񯐓M0=3SRXXu`~~^GłAF&ban~T)#YDQf~G,-/@bw8YϣVkȰ'@ @ Ĕj"0TBjN"7'E;*QE)J@Pbb]$I$>'6+ 0A'##E;(b2EZo: / Q>TP&Qo/CYZ^NƋbT́@khjjk7T219nO)2ydgoX.P:_TnۊdPgAI_YZAAۃ, >n tu W;?#"i46H>bii0*>6zil؝ԗ$qV}83sl))Ixjq ,,.ҰkgRhmEN-VV4IP(DOo{vo8·%TP.#,~SYۿӃi.8x0֢2Xu ]QQ[,> G,AkqHmh@E|PtKJX"v;r4j?4Dx~*f3KnNOǸ}7KB-%Ң("Ig#c5:ETFU*⭡Vgx& p묎d͢N4hZ^Y!̢ݞ8xE $tXk~?P4׾sb|KȲL8ƿھ}uQXp"ݼۘv`iex=D^D~Epl j% ,ݼI}<׮up_D`BFP0G\cBq;``'ҏ~h**lNKTCsk;ײsg=s L̰ȏ}{&}{ɰX=N:NGm"(mj/\(i>?x$Ņ<;ݽdfZbl|Yjfff_  1;7;w133ekEx|~5ml ̩LLN"2H8BNN6/+׮ry R[SJÁ,tI8! ŒXRYAos499hsrBp})ڼ73;q ́ IDAT PXWɰXZeffw1>1,S3L015d Znƫ/^B•k׹Błᤣ6 |ٹMc0څJ!9F_RBjC}\J믓{72Y ,utRUCYL[%grR##@jCCҶi#?ͷא$6LʲTllm׍?7A=+((|I\^YFs iif@`c2 c֭9|M- A8CjJdfv9r122:B0webr`0H W= BAȰ Gh4j\%=RRW=WoutEk{p88.Ө$oyE'`8qo`b ٬/MIq1}TBHZGߪfbf$P&^O0 V9"(ee[p:]߷ڑ$U*Kz:Z`0+wePVbϒO;t(. p];^V&'?= 26>AMutnfO.n4j߱Ac2(2;7׋"77ZMyyvwYF233DRZRF! aH1F!&G0s={vEZZoN).* %_o/exBc#ٶ#Ӣ((x#iQ> ֫O&:PP&̤g畐}]7Ϻ @~^.`@0H0_ceyC &if3;p{defRXO rQZQQ^Fnn=4M4eG *ıY_~*@kVVb{G]~q+/z-N+G$^絛OlWnۚt$Iڵov;S*ښY/Ce [33),,Јcxd ӵ*A֭0;;B>ZCmM򐮠BaGFnvxѸSSS>;9٥`r ]ǽܿ?}\1nwӸ]1IK3SU@uYY g{vB|hLTd O㎍O{zg7K^x^;}̌x,O>xO??Off' Sb1L&PJ</n)akE/_Ab>uM-]ny f>=^P*~3\r  `JMMTQ J@P쉢Z dzfm$ ZM,#&ˈ$nJ=uU9zD% ,ouE7^{ I&FN(cKr8]b>+γBAu0Є(#@\ ~Fz=гUKRTi]Rh JQHZ;{sQ1PPPfyq>p$ԡjjx}K{pe:-K: {-FƙAӢhi؍Of$).QՄ#L&#inm xwtnjA?NwJJKx-.v= t&WOȲLvv67n4r)).md" ȲL-ݼETV\k.D(HRL"2.]fiilz 019N$IZd@ɸnԢ *,PP7 򫐳KAC=rI=ݫ&+"L߃Pťcْy Ps ,P\lCIQ\(((((<5/ݢBV'reY&bHc2v-%%r v?죽Y;@U6;믞=v㝷dnnP(LgmLA~L$A%nnw˲$Ij5gx43g?G8fɷab=Lînոh4}8q+++ ɟCCD%)1p+4܆~_DEE9**r3[oAQaQkX,deeKv"We7t-~E 0HQ>#[vƃV wO$oG+Aj&1;O1wG2>PPPPP8/ZF'^g۬|9=4?ǟQVV4SSvvpPm+*#߽GAA>v),,suJq{ˎއ-/䏹r+++QTXɫچh4&lj5ZWoPV1 @~Z5lk7ٿo/.^ ٚL b43kV&g h4'RW[Vǟ"I+zRM& nB$亓_ǓB%Jw!{F䌼tbHzUWp.qznc\z cJ ǎd2R h4Dk^SCnnfySʊ43,[ZœJ$Y N1ARM&DQaL1"$¡0jȽARM&**|uHB"_=$OJڴ{VPPvT׋jEP \oj"ScaIOKK$V7x M{Ggt@0. S+ǎf\ӘyB7ƽA>:6>??O>ϲL{G'}^`dtw>X,FKM[$^ΝHd+]v9;b'qQBЗcn=ִ e t?1Ìhw|i|`(Ix]YISB$Ixr5} XN׻v*((|{Y7'aIO'ŠǚUD$\.N r(0M<FÕki_?suM%MMlV CA~]DROgm6!pCqF3t YZ^h4&&'Ӱ^Ǟ1<)Ʋ|^::FxH90L ͓nnݻ}}]wO/i4D/IdG IAA~`EY TPPv.,))NlrH{kӾyL1uk^x]\TC]|xkzk_;F8b %%V-FwO/P? ę7^c_cs \rNV{O4FFx7⣅{v$͜^_fdl+a4j5&b9}8\#RR\=a;(.*n{-J,pNO/Ziĵ>: g|>< @qqGB!z٬4ډoiLE˾=|pc*s9w"*|_>-m7 sMퟗ^>WCµ_ MtC~UR(]A~ sN zeL eu@APQ^^.ѻ̱d2K"(JURd`$)Ư~.wddt#2t_Q[SŢBvL&#:R,t&&Iج6nv 7?ԉz/}C\7?hy AD¤Jv Ǜjb1Aq%l6=}}def HIIAT0|8].rsɰX_~[+|/AVZtT*dY&dS'ɶ1':8ɧLMMs::gX,nrӥE>Sw*WBXE~r*n Y|' :/#9WOg =uS}iqC-=e{ 07\}400هAXrZ|_=0em ~mF."1A6nld:t㕹OQ>eVAAxGAZܶ5➹q7x,=|lrr^JSYT*T*2(XLF\ǎD8wFc 'CP" ƽwkka'Al7b$ГǏ!IRbJJMYF 篝7!;!ѿRbRSS1LCI@$\uvρ9 !"t[@~t7c*GhTV1b1 ]D>{ȕָ0 AXq)d v"CLaˮsɵ_hrCXr KQ} 9r+@! 삱*1OAAAAKyERY]^iZDQDŤc5KW5APz%9zӓ5ݯ|=NF6k44u* ZVmq-}kpkqX __6qY]/%;AT#\/ȶJH(t 3&]O?o@![I|$Ώj)@VΏR.~Oy`rG7Ur_oːaffgE"QRSm9``fvWh4O8닑AP=xگ  s0Z~QAAỏnk^,)CTmdYFu#  }}/oIln{HEEj)N^^.ȊߟtÚ/#HҨ/8^[J}ѨՔ(kt^8^h,)!&P^^a ۶ Nyywtgn̫Xѱ1JKq:]x<W}N'zaWV(ݲ˿TNIMMrز~y 8[y}Jy4@튠'1L~A\*^GIqfeOA%C0$=w]`~~.gL0wi[4[pGE.^BkM7 -7뿇,ttvg8Iojۯ\ |޼F+x^]$qu.\$Iܾí&磏?eiin$ .^f~~Iv Gf;cp\?xhiipݜx I ,,,hnin|m7FR8:6/ϼ %4^$:N1:0œʮ\n bhx^*1<<$IhC184pRTX@JJ ctu;[$tEEZO?;O{G' 295M{G'}wIKK=n4mVjkbw6)ii,S4vl&++Xӹ~]\DSs+kJQnfrz.F6^tDQD%* ].3s|~< APq!zz߻Νg nJCio#Az{p`X|v[J  8SUOsK,.]DYZZBJD"|KKؑ~{z)(B6:ffgGS ._ј2ZG DVc6R\TDFX,F^n.7;9&ZK(FղD @T1)+-N0B$V+&*m Q+Z^pNGDޮ*/S$a0ĥSYrQTTE _"6m}[QFI5Bn^ަ D"IYdeezX,zMۿ_h4ƅf"& I2kkf;6Yb1N8RR!eM8&5ՄHZxm^[CJZ `p8,8].5!06>}h4Z9zK:`Rr^[öXʊ ,m5vڅ\DQjnwkg=5 1ȩ㯐j2F(,( ''|*ɶff &FMu#/,pA22,B! bf,jk1ڶ)66)*D$ =Ef_bE۝xkg=i|Kݤ1t:\ϋ ]]]jH1fa~p8DZZ7kU Kw B"-MYD"B0Zmp(ĄI~Ab6b0<(rXV t,GTUU3<yh4uEwq:X2,hZ zOPk4hB۫:qZQTTth4,,/-IOKgѾUZC,cjz !k3XY^D{tz=G4ep` @JZx,Ał,hZT QT3=5Mjj̄X,%  phsZETm2 *Rd.--LZM{GF"g̘ 1T)kDQ"H") YYEQuDQ$-y5ɓe9qܚK,#'9Bu#P(Ѷk9<ڦp8 1k֑Bĵ#0crp-wQ E/QD9~|Kqy+PKi":0Xٹ;z3i_SKW Ffj5!a QT៏۷f#&opNK%q@:Z6)t $ĵ6^|\t:}‚q-I\kͺ⊢8 y?!?,cGaC!_D(a.GF4ޅcvn9| 2>1ɏ\ބٜE+Gpeo6k֦'QXX8NYYxI232XX\$=- )ハ>Ƙ®dl.m6YYj2b!'=͌"&I(zw?rmXYaIR\\Dc." `iiePI͆ش>z I³}N)?Zy'?_3opeWܶ‚ffgiܳN&|nsM;ݤxa{/ЍOLƓ3T7_5Q$C{g+aw80SxDEJ45 `rr>щpOv CۭvDQdF.^5+; S?/*pAz*((((l/w^bIOLkEQ<^/TT*CC>w|;r,//jYYYqO;O,_Ad@p>DQdvnH$y*g!/'9WV`yyES\THgm &t20$kn4bfq`^}Vag}/^`g0dffROʪ>],k#6bYٿ/CRQS]EsKUmS|KhZvz#nVF$2lZdz8pw%y C\9`֊ &1;7,+%CQQ!YJ= @UeeB%AAAK(:E2;7ǭ.l,'&^->tɩiGFٵp8BqQ!zs`(īNPPP,~ښj\.=zK|n!?/.;J[dYY_ǵMwO5U\ľ="%EʶHA~cɷdDE|% E׮7o#33-gkE9w {w ̙3)ZOg=mϺ]{fk6LMk5٭^ERb9 H|!=O s~4E;Uu<*+'#=]x"_`_ScB2u+O_IDmn(ǜqO=r<DhyԔ}ñn>nl:ēǶTV ==iHU+<3שΐӉZA"s@Rdc~~?^'XGQzVGDE4 wؿ?J'(z=׵_zmCWC[ ]fc6o: ܾ}7X~]*` GǃU 3ne@ 9*<Bxo& %O6\AriJKKD{=L&3EK褤ɉ Db01>N~~>0#''aLII+3撨ijDBhX55=h"L& erҬimvt:-1P(ɔEADLR O(BTb0Y\\`0$|JHH|9%>XZZ`0FXVjp,"8糴R,FRB0 /O?_8d4h^@JhyD151jz},/-cZY^^k7l?b||`{DQ‘W &  m[t`0Hn^.CC瓚wamYYYgdLBb4j+ɟjag~w(lf6Tfkoپ=ao q%$$4DTzD"e}¬Hш\ٛ$t:DBP+}^; 6^y@! (/ aQT8].ΝW_`|U4uy %Xݺ}'wm(4 =}*aph*zIIU#sڷw/.YwdlE:nJ׽v'# m))1yz֊uOJJJd2mkT*7n&$Z(RR\8tvuFe2٦ǘLIh7D2䨯F:]%[+!!.LJJZQWǔ(FC$]-DQr9ָӪo_k)MjG0::Ƌʕk z23F쮫?ҕSS]u5nZ Cއ8q|z V?y_i{Ixnnerjt+.^FRFK/>({u~KK(rFgΞDYP'axd<>81@`[5%$xZ6Zf0ipE䯛\uP,urijoZKVNZZMVVMryӃVUdp0vߟcWႀ\.烓ajmB`zzBAgw >ƫtZBSӤm CԶ:nź+袲n!s``Gff;Ǐ!"Qq=wN'--o?FTvh4<` HcC=zq3<2B$PXgf]WKqQ!),-/$%9sm#طr$TB`VIV}km%VWѷ>sIēɎӨ5RZWy]A{޾>kqtz=~mv._Zԩq=(J:S_?=u:Ɔ=dsff( -nܢ>&琙??jy|>F#yt+>zLVawm5 ̌ JeVR2>1eO}/=Bz+z.>DA[DPS]Ż ((d2NlBK;m|t]d[}oW:BBL.pC(2::F X$f6HOnjv099P!rt`a-&rJY@IPB:;ܜlR1$F >-=} '>@{i/%&4]=LI_[o5خn*ː+#0mltO=]TWm*ēp :q&fP(fnhm@L5ͺ #NGgWB!ҭVD/pZC TP(>cVJbh,ijn3n'iѨ\.7FƽK%N .5U.ARqLݵ\NٌО:#lb`lp$Q% sq7:{D"4TI̭nҒ-8]dp^tzGѪzfmv0>R!f$$$$4,eLrZ:郎n::ֽ~q XX r: ]L zs c㈢87Zn~xa|=e_D˭ۈH8vkC_s-OB[bL ԡV//SM}ehQ D"j)ͦTd&\nJsLK% gf)ϥ0'H$<75FD#SKZ2*0 œW^  m[=~\xd W]eFFǨ(`ubph?=‚/QYQι q&Vk.\$3qofj5;|1Օ z]W^#9{7Z8w"`Sx>ΝH{G'r5Nz{N9˽(7Z8CCQŖ;d mXnOE9ٜmɜ͎FBPUc5f T*L^z[R|2`Ki5 SuZrt6.i4|JڌFQcdn~tEB$e~a!n1-$#>Jfc6~f3 ݎ`@ѰpQ'3#р}qD8a2]$bu:ZҘ\&?a~DfF/^h43Xfo픗a*[RVK{` VkbWi ߏN%){DՒŻ7nP~DQ 斛Bft:2FEq!Ņ m 09->nk5TU$d>rp_BkSv7-bu1Ltvu$t:lUdLNk⌊[۞x֍9 cnR KBh2#bѨ̠ꘝ Ə[\tZ\F$nB|4))(VDLMI&fsX,fd$'EV  9y9OP9j 38K*%//@gWUOLKWD"$?TltdtY~?^ .Q^k[ǢRhya^{Ekfk-sUe9YddS[SEii TZnݦq*[[IINhT$lIGUE999dfd׿cxRٿo/~?aD m/עT*7}ߟ.*ڿ:n̫ LDhZ d2 F#n29ϏBa )Ʉ EX"y-?nxEYjzx}>^yd2"3d,eh4Z&''IIMA2XUQsC+xco"';{[k019Io_QERa2i$j40LNNPZL:=.wl$$Td2YfYՌ$_ē:!h \&# D"8 H?ԻzEztx^J"f9ژ\.pT0 8br@ h0ie xOBē$-!qIZBBК=իUBAZ=/bMV1L))d%~Jv}Tc& #R)7T"⺂:og7>N@TlI*^ z$$-@OBBBi&' c0)-)ql \t8ߴwSmwQ( HAtvz3) ARSS#%$$6vBk0$b0Nz^U>@&Ӌx^tZpߏ^v{鴏%vc/r<+}+7m،O zs 55U Μ=ǫ/3~"}L+ur5VbaOAA4Ei FP+Wo(\v:,sG{G^o+5+Wo**FaݘC0oJ 3ƎO?81Udffd4fr6;bhhʊr կˣ^82__'tvux IIFQ({m 8k^|(PL$󳫴Yj?^_?,RRcxd '&|*-n8$6ƯP`4d8 .Om-@4EiR/@"` E'vrWӈxǼA,{-8n:@=R$E݅mYo?%$$LvHL/^8~aZn?"S4>uOx\z{TWŮ-f2r{8H4,z#^v:yWy9)6cg4vh4kt2>1ɏ>f_vk+{r;mTWWrgΝ"K8K\v>8$6FF -A~s|S>(ɇM#$K&hvbk!h`7#DQFaRT X-8;aC)xǤ ggX2ޘ+g IDATZKKA-gfgtlNdbff6fypܨU*G q:] b& *ATa_tjШ5v@$hDRJQQ!r ׇ5- \ł))p8̢}1J%* ApN9=F!SX)dJL@fpV{M w܋DCQ, Sߏ)!!!!㗀6000}M,:9t`?61 cc|ݽGVf& {l~1233gxdCRqz3G?Hkr5'&9r~5%E:oKHJ1==CwO/J7ZvǗ7G緹s A(rÏN$O kIvv\Eu[ӰlTVahxKrr2YYz*~,P(ǻj{ {zY!7O j4CNN6׮7c2+LMMq؛mlq-\.>⢢2rEgyWIIvF9qXo޺Y$%h4RYQFcor ‘/=g?n_o`0<؅MEwO/rǏqz39(huS|>TJ%\wù Ѩ5:]Qjk'&˕,$vwGdӅCa ZӾD\n7]Չp=<.}8NdkF=n"ȄOBbl}9\BBaݮ]%dge1y7xmͫvBV'?{FMJJ Fu38nJK۸*Μ=ǾFt:W7s1N~)p#1WcoN˭ۼ6^[0<2J۽xngbb[w`^|,.:t|pc^/U,--13;=}DR= Eb mޫ4-DEQ{R`V_{"j9^EO$p= ɐ(\kpvvʊrXcwtx9|\.7 {ڷ8v4NLai{j %$$ΦF_{ezTA%hE(^ϕ))NyGT|z4GEr9ѕJWSJH4v3uI? [zC? _$o319ciwN'==^d@8>Klb4JE@0 pg~3$ps_;|< ᓱk"s(t-,sFa|<&_ < b-b+"=w->8'yBxMH]~L~lWc3(>( Mfp[CPiQe$sOFP}kgbbq>L&b1x<Nk v1B0\cbbe NNmKMU%|jYxÏh4rPTX,f{h4X,l1cGS[]EqQ!DC#IN`66L%Xv\ZDQo4YpTÙ"O \N35p}Vez ;a'`Q/ efe<%ryOY"s>)D8?%r\rCNdD"QE?ze AprZhSNzauO?rGW_~=WVvA?o_wS*ؿo]̵Fcl6uLpi^Ά@$vUŖZM*HBaRlV!CyF*@)OQɡ(I$Y l_$ rʌ\Ea@"bC+&Ox%E$}a ŽOvAa%9ؙ!"98+5' c3t/d !*k,G YB&6 JO$/2% Ȳl ~HF\F^ ?ZדʃCѢ A=<lz[H4%*p69 !K?ҷZBܒXxF^ְ2#Wx/>;q5)Pt<ۅ͂,,䓌՚k d29UK{G== &iӪ947OAFIIX,f^P(ŀRfHO7LNNR0S]U{u֎`f#bٔ\JA~y[T*iس{ztt+m PZRLH  2<2 G'tݽ׎x[|ozjt*YXiv7l{ľȍ[d޾~*xxܞTŢA([&YƇ.D=Hl_ F[bS!I$$$v;>|p쮫ݼ})n}tvu33;U~~7y+A;q99ݽP<8hu APqs/RW[íۭX,KKLLLr`.^晟_Z󍄙@'_X H vr$I\BBg%^ t:-:JEfF]x}>~_G'31>ǟ~Fo_o!YY${8{ɖnn FB'@%\.7‚|2ҭ1Çۿ{<\xP8'z@ @k]JJW dl|lYA@l׆C=(0 opcn~ɩXM W&Ȩ(Gb g:RSS#yCjЁ 1dr9=Śƙ3g_=t^?&''{sۍlÏ>f[|Μ=^C0eIcC=))+dee277L&ԙ籼̙3QGF?H;' PՔ*E.DD"Tj{`0\@!P7_Gr9/"@Fu^zy|~?='?DQDPP(>|JE刢kBA4 (ǦT*).*B.!ɤ')22j9|8AA+@+o)jŠJ:yLϵb;.%+D|LJjf}q!EBx#TJպ4RI(Ph4V~P.WliGPN][sQ2f`pccx jĭVhO:m|U@.1\L^c~=P> BVy$:\A4 r@D\ I )rQpD8dc2!U B`'x<<9ma%]#b(f>: GPd)$bR XT ~JBH%$$$eR7 Bjj 2YlaŬ(41`gc:@#JeY3ia1(x1'bKTqw1RP$K'0j=OBBBBdJH4K%&ΉLxKWx#=w2X\v2{zHOgfg~a+21:#qmMo޺ÉǨĩ3SYQNScz Rɇ'?!''?n;}ݽL&CE߸BE G˸=^x^kfoC=}wtY~_ss\z)sm~ۜ;/tLLNq LƑҰJ6l6;G<Ǖky]brzu55LLNrEqwP]Uy}vq¥m̟4*, AIz;QN Y/C&RmޣJʶsO6;.|PӋjžhh0 AB0 E{/p|^HE2IMI줯CvVl쮫abrL{zdiy$</j RI]] v"稭BQCVf&;1pY22ɰZ_X!X,fr9^H$Jiq1-TW?z}( -f99٘M&d2QQg(2<#@qeFCb%[0L}M{jMC. E<ĮjZ-fZ-}?) ٿ_w' Fח bMKeaF]m )쮫1=3C(BձkW TV  x=^008Dy.22i袢ܜjk(-)ƚFŌ~wݶXml`a~쮫I6fn%77ҒbtZ- Fp8Lnncc=|T22(** ?/|ٿd $맦]C!&(('';Iۋl")) E.qִ4i! x<$lɩ)55a6'cdgeIaA>, IIF0 x^JKbrrbRSHJz{:Xt8l6t j*%YLR^ 1"It2==ci)oh4lhVEg "ZvSTBetZf3s q.m=w}$0醙L(" W#X?r\def`fw:C*+ ʊGO_p $Z2~rE\thPɒ$ס[5_E$GعZW7SQ^LA樫q'&|XTAV˭$${Hѱqo|u׋餲|S17´'°C$/7瑿Ntg;n X"mܓUHߣ1Ղ1wiW6oasҊ^!Eg.ئ^>}h5ZR׵ ll;A  3#$cb|߿P!Woñ֢HN$&&& )x YuINwM#)j}:=,N\ǔe*&OO.8*5f9?d~2F&;~1ug;'1e64ՂLEВ[{ CiH'ٚ,ɝ6kIEQ-|\:C8ӟx8nݎ(8싋|>l/~.peKKfgvncwtb6wN q88{ p!M{5OOqO=KlQ{G'6e <=rz3 p/456BIILZROvlv;46졣^Gd2kС( wnZjbs^C[bZS.sy]İ9Ih G$?d_dVXrPI AN1F s0$5u~C fffN \!wsD8for9nٳ2^8r|>;#>R$zhkZj*x^&'p\͓GQa}p ^c߾Etuo=lW)ebFΜL&te2\n7Yh4VWG 0e@ IDAT~ \%3#zѱq\.74˸zMnN EMuG<ǹ 0%% IMM(.,pp>N~ٹ9v{!'; ]Rq צF儬ii >*Edv]BƭoڋNpOq:2 LO?]4m _LLL24uZZc_S#>*+).,&&7oaF ~~{dJNaA>c4aHR&BElvFGG9tPL~?]ݤ[쮫ڵfff`hdXecf>t+@ٿ/6TFZZ*%ܽNfF:  &n%`4x zTUs5 򩩮Zs ^d2%%ńCI#+4͆%VV5j(M˵VDܶrft%$;^Pq%^|SE8F.#HA&C&[xt%$"CJ6cipѱ%= GŎ(!$}=f4j g8&--‚fۇ^Xv9x`Jr}kmK8cD#QZ͖\}.^hH8> P.!!#HHVUڒfH~-OcP_O@n6#dM&ZY'ERB:+ x:;IHHHHlE*z}~)**DV׏՚/. 4 @~ |8KtZrc+/5-oh08##[Y٘7kt|24c.+YqsPh p^,}viomdWL&)*?OeY'O=Wu #p7l߶eJSEgG۷I_H0v ֽ{8ccL9~_Z./zr|:3_#7G{QrmsC|2=ܮ?qήneev^i\dsU~u%uУjP慶 ^x$->K2<},wdbr.?70:6FAaG|I07 d;?ʕ8"QTaxdIvv{N=a[C  g|B?ɭ[q]jR4{^š#_Rp9JK0 ֖XEWw7e.IIIO=gΞ7p\hѱ1:֯(j||2,o_}pI8$eeAAAc8þsWֲk{N'&yٷu>v7oRQQI&SܹIcC=;6}A dmnZ$Izda4MTWUC}K r~?mlo#6>B?gRCTUV2<2JC}ׯضC,6A[ m݂*s),,@s-ҴBA2,U\xr ~|!!N>a&HXle {z !`"mc;m7H&S!H$((ৼ;R){m>?t0s~LJ8O?c͚U8r+)))Ejt$I-ɏ--7OA,㣏R]U5kPe5TWquN:MEy'O>c6[466`('Ofe{Ӥ 1 ]1 Mgr}^EQXv [6og~@yy9UUΏ &5Ίض,_ d29]U+ٺe3.5լYミdHS|q+0Mp8o>:@,60QU`0ihZ*L3\+~Xn-PSݹ/K8&xv2vvn u>c+?yi}$I^ KXhg/,>J#A}]-{g^,HVû0 7nҺb9G坳|y#k[Plk\hjClmXǍ.\eEy|snܿɆemĭQ*#e4q XQCwϚVM Q[\@|k3ckZtmIN$H/,'I/, K믽6St{/U3JKv.yCi }I]in473MQ.}-S((ZwG{5){&~}0{Vl]Oru U'd8z4C>Ex?7b %I^AKr Ih4?Ex&EM(@Gy4ۉ} s=˷pm/Pun w3EUj*\B"o7>nBFMu HYiFTEKQP˵I}71M"I$K裻@,nHA4r#?}n]SтpQ-`WVTPYQ1s{ky#ebӛB< j*BPQXFsk]bD J]{ƥ7`>¹i- kY[JƱh=&|&Oq}.VϦhh"ZnpI$i-5Dex_}F" B/iҒ&J5j XW 3)b3=~R4384~|`.Jul6K2d``B09ghhxNBbbzn!㌌5? !# >DD٦6=2۾:yQ<[$IX4C B|G PJ(*O=ɧD n2ˣ ՅݻO2$ LH3b1(k׬u]ΝH$RHˊOL˼8rk"BZikmu~8ɧCa6mO>%oG9E*:{vwGW] P[ήn{zر}۶l0M{{QUc*/㲚:vƆz+/ZՎ!v[@atuu3OceR`i>%I$M>z#fF<  s*/ֳܳ4'%>={mvΉSFK9qP7nRXXHֶ. 1LSR_W5w*->JJJؾm W^ptlmOl,6o`0+/eeddx UUe``Q\lX(F2h=ϦaGFn` X,F E iJJJuܝI{0*-WR)((@m[پu ϜYZ <nnl$( awOm~]7T] W*7j`YxbvU5lϵv 9w?_ST\ę.N>CQ$Beemm!Vl(rU&&71L'OS[e466N)/f*z{{('7|3{=Mxe#Ws"- h/aZrBuI3456PTu|,6N&!<枏 uwPkv\s I^m 8l&L`+*j@d6X_OIq1ʓ]Ӥrs  8d5RݻFΰa/IX[JΒu,\n=l~롿sU(Kl䇻ǹ}.U\9,xR^۽kӵjw:ܹċ,I/Ϣ"DXUOQ8Wx3SWBUu=l,AQ l{rI E:|@tfxz !tUr9?\8=_skhjMkڰk1$I^M 68t¦}T!P`O\%53qI8/$)q3tloOCY'lՕ/04]04Zj+15H@ ՠs^$IP|iT-.`zeeX`96Oe>4iѺu"m6 co~f՗C?}{ٶMgWcR\\W;Sgo'fwkTϽ|!hY4TAfYlA<4ijY0|xC5<񣌏 n.ʫtu9w0v6Cl;,oB G&l:af_m Eaݖ]ܸ|Dmo;}=gw%\yLO_ow=6zc86'}yxY rLna;s ǶX50t4PefR/|}}MӨeŪud3i Cr @Ƕ}o=:6#6>ې6% )vjL2̬kV" ͩo֮fll {a\QΩi;60111}5 -M$=9L+;L[LfߟNݢuKz*qEp=M(S !׼E0,ʎ}o3Dl[W.R^]C8PA!e1p=Gyau& ak\>sl&c[y\=w-{::IS*n]gͦoũ#s&&ƉVVS\eUzs2 \:1:OO'f N+KYe5ȤD+H%&ٴ{R][OÊv\k<>ʹˆ{xKլۺkZzP(DRhYNM1M0É$-yB̤ky2mB:$-%w |w=4%W|\f\_8/r|_O@ؖR)ՋTI_mZva㮽ܹ}znKcK;dz>i))ŶHf6aR-3KfHUUʹJ휑2f~vgrf_8iY*ꗷrilN JJ1EQtà9zޜ % *oqE%DJtNy7- I$-%y<)TH:Zn)/UMxi?URI011C.a㌍JAӠ2JJJڧ8gTi ' ).]Pb ?6>sMQ-RSSW8ׯ =dرm+FIi>{2;jsxIwlf>d@Izy/˓^ҖG ppEd- A\ssktDuf5$i(h\ HӹrW/[.̉;:FZnYBm(pUi:[6?y'cc1֭]3Sfp+HRy*H pRrD j8 ekv\/K(I7E./L}GJ[ ['h@$0YCqMJKXպE1H=Ttun"4ߞJ dY\ŜN8 \ي #2ME ຸ s$IҢ5DЊ<,I@U&I( zSS\ڗd:H Mͭ *0ѩi_bz,ԙȦܹߍ V=W~BIzUNaacfPU@+-A qG1kJ>L$I{ <̦F.OY!"CP? X6鬃uu])ǶwjrBv]v,۶д&-9qQS^M_6EA/C/<\:Ҳ,t=ɪ1;S7æ {j,k) ||w#RZIj{ )ƒ(-L󙕻OALmǓ3 D <ƲmTUEQrSŪNm=ssAy.0M M%Ⱥy'7؝=O~֗ݭW^SSǎO ::&\͛f$[%]&ރ5кe;~b֚@ @džYoNkDIFuS$FdΕ{x1<^PӴܗά竒$I$I,@!<M8 (65qh6*>H;0@N4x( $韏 !iW>__%P(>c/-JIɋONDQP,Lދ{Mv?dʪcۗ$=f&3+ib<9}4t|6`M>5AssjJ+BQg'&n+ b.t;-t$iZ2㸤r"SP5~p'R)+$9J$at;?S䒘9m%i1 Fg'=%T%lv#I?( =d@I$I$)GK$I$-1!(TIENDB`gammaray-2.11.3/docs/manual/images/gammaray-network-interfaces.png000066400000000000000000002171551413310443000251660ustar00rootroot00000000000000PNG  IHDR͡bKGD pHYs+tIME (2y4& IDATxwtf7ͦ:HVP.^w")*ҤkhI 3] {x?Lۻw3wnsr5AA Yd  AA K$LS  &r!t:5M(m j6>j8PAZ: 73k\~[-av3g3s\6ni9sEQ<"$')3@$|9N,]ά9غm;}$Ĥ$ پcgE˲ą A;4W! | ԯeۍfn Hl66UU=bjp8(**E כĤ$mێjt"oy[EӮQ'._M|``J}1??^x*͆g  (C ] AaA!UUc84M㭷h0_11ݏȈ/Y `굜KOvZ:uj F#Y99( OF=$9)Njh䉓No~:ӧNSB6oJQQDEEy6z=f)??f͞K\vDEFRFufrrr5v|tb۶f%k0`2qIFMɺu0 |?m:qQ>+ =<2O?8cǿn'%5MrWf???o]`0w< ~k^ΝK~:,g{7=xʕ*ԯQA%Q|yN'Ы:߁'GTb۴boKy_wڷ!SbE;ƫ/zZك9t0Qwh4ߋS|[nG$FO_. c*ѨA}͛6!%%n]o8u:l2r*իVe=׳,"˔/_'NF@АUʅL;ٔv;ժVtb"Mo*`G$4U^ݺrK8,snZ+\uZ i/ zi&Lje߁tkrAV-1 Ԭ^͛,KHb7hղM4B$f͙Ǹ#kߞڂa 0MrԬYok;1x>p K3(W.իg̹;kq}ǚ  e[";wҥsGV+sf|Onwca#+Y>kCU=`E}VUﳦiHyGعko !%%+WQj?n'-- Iƍxٺ}Ӿ3CSfMN'&LVjyӉHvvQQ;`0:֊JۍVZ iifW^zgS9u4$ӰaʇӢy3O:I:+HAa@v rظi3[neWضm1 S 7o|V'/`g  zM0xț I=p\L|gvy_]ҳ#dN(=@FF/nz;͛5,(tR(o%ϻw  5M\z:#''~FαcN Yf-M6#IyyylܴC{$%$a&v܉hơÇY~Υ'@ 01mLޓ_y?}>#}#Il?EqIwu⅗_c܄I\ȼ@߁C}t/KBw~ݣ'v 55Cg  .~rAhh6ouXV kߎ ZlMiѼt"2IIɴmӚs{HAAylX1M4OS#GѸQCN'&QZnQ\OP4;O$<,(dzVpUu}6,QKS N3̬,ΜM)s~~~ >~^Lz>6[s $IJ7mJtd>8?/^fɲe|=3mξ}^ pR :≹yDDTDQ_`RPXHxXoD[0ʱ/(\h(A;H*1nb lJ gϜa(܌2(_RʲLzyjV"$$WVL{233[Vx8x0]:ţj*&UUQULL.iP<ӲEs6SNӼYSԽsωzj֤)dfe@eLY# 78);'W/ ԯw( #AZAA  AA_p8nKDffŷ  7E(U"+= OPPP]tܽNǎ](**b?ѻߣ,e1:NǸxWq8L&Y˧_LG  _.y 2oBTUe aڷ_1gz=)ii` ~Z uoK쥶cnYs摛&%%#_;oữdƬc.> رcfs۷bт9Y, ~L4ޔI3?'xx;nϿӉo$UעF_VV6NԴ3TϹQ)ŠW(8*BzȲ́Cp/N'cbenja 0C["ӴIcztލ@evwL^~>rQ|L&CL~k<ѣDMrrs%kot6m xzjv]鼋߈ciӪ.`(Wkm}){Yrss!giF?р$xx@vEPPoKfҔ8~>'p8~~~,W^|u0$1az#p:o|gt&q]4wfVnߖ"2.C`~[I /C}j>$}x???F#}0mL$Ib=c ҫӫw?~9vU&n½w݉yY/IDRR2fz;oM=0{c{^RO]k2:Ἰme.u/~Y/LYٺ['ӰAK^N?`@U{8#F!6v͟SxgX0gMZe5}e{g&g&-ڲb/ө]l\;yyHh$++vSbErssiެ)6`91#t2xL8^&Oc ԪYwRA|egp^32<%DX8wM[co?c2o ~>:d|ƶo?ЯI䒘DZ5R2'NjѼm:|EjU=7Yb-ԭSJޞ[֮@TDZҥsG+ڷ#&} Ͽ*x4L>ȈH~1{duGWڶif ,YrKE!1)R% 4bϑhҢ-[6!''ł,K<Ի??Ο/Q#煗_c<ˌ3?;(t$207/9vCzuKurZ_j+֍W DAA%&JDfv3grѢYST[^4l65AA$IRhN%Iȑ#|1k4Mn]E'EGG⷟q*л|?m:yz:1Dz_^b DUUERt!=#~}{v.(223}l,@v;qcQ5Jf5l۾EQ|i 71s\nkhѲ9iXV"4ʃyDEFia-OdDErZXb4UCףoߖ;ѶMk7kJc׮I~}W_FZ\O (ȂhBf&vd0TUfsO3O:uoj]>,x9U|U,#*Vh3O>I>s?E:zNڅӉIHHDFD;|,|N֭[Ѹq#b۶c7MkAA;nRXT8ٱkUvwi\.?qVrrrGUiP.O<,֮EAaQtҙSTꈊ8s6N;PZU |V]G&x ؏Adrք7G!1)Ͼ I 1ӧ;?[e;utoժҸq#_$++:oG`w8(,,, t3"\GϿ߈k֬#>=3eQ(]u532SbNNrTU%%%J%5-ڵjA^~>ĩӧR2IIT\UU9w.¢"n G%frspB@@nTOȈkV[է2^˲LRR2QRSӈj%%%Nժr)*WUW9s,Zq)T*Zdtb"1*yf',, Ӊl\%B2223Q/b 54d1%$$ t:h6s-a#2"UUel23$5%EYs8v"#+`Y9BC}s:1UOzFK "+11p\9``)|ѻdd\ /? c2nj_: Apmk} ꊌDx%uAFUf ZW݇ PA)/:G^_$ B'W^hbU2WWE#Kd^!P5̅DF\G v IDATd^!W(VxA1UUsI^TTNOV/ Ÿ$ 5Dx]<} Rc1-A+듢\]#h•3-EQi"A1[w>r|q@4e EQPYIlYI>s+J?O+H,IN+KVV8|XTЋ,cܤEaC̚3 ߴv3gh %e?ٷ>s6Ysq2_/ayN5֗m;v2wBZQoĂa+F5)9瑐pwM8~瑘|d(__J]6tL4FVvv3}f̚k-kb7SPXXX<1kgak=ng |*M7ԩD~[Y}UFnӵs'&ڽ['v)**b؉<س'Mܹt2ΟgyNO?$In:Zn굞:R@AA!ii|9Nyۛ‚B>+ߵvi/Uv6Zvq\Knrt:q:i~?.df]zaf4xeJDp.=}{޻o|t.InPe7hۖ C$:L3޽I8r(%?x!ٷɓ޳͆(ٻIp8?pӉȲng}k7n=yTDNN.ne+V/\zO=6nf܄IaGӼySl6?:NcZX o"+wvmq#nNۻ[tzW\򞛛-WQd-"<)I*'0Ќ.U`t4M`0v USXQUrvn7'xnbVIMدn|Lsrr1p_^%Μ9KPUU +ǩӧA+8^|?__AT^|e0+YmM<1h ?̘͚8s,W.CTՍtxgW۵e1 BCU:99믾lܼw>BU2;YY 4s˖:vueϋY0g:$thߎ r]wpwHA̞s/dJtT$$~eeeyJW""":EBCBJJڵ%<Mzzo?*c8qTjϬ5s6% T~z8, Kf3Be%`4\i'U zY(*~0V"Eji\xfMy|ʅ Ы}̞-g[<UE$E?K"ЊKTUCWhfi%{uОAhưe.c4qJ7}%{b}\.a|Ӻs3}ÜӲEsONy5l@+!,3Cd}bq3JHJNrLez=ۿ d2ifx!33łhz~VYz+_P@XRRS)(,]dY&))( va28H5IO <q*C$;7wߞϏ ys$L4EQR䵗_`q\.ƌ@{12zPTUf2~bAhH G4#ƌ'b}b'>/;wfX6&O H ys$K@etfSh¤)8.ի oŸ uhTh0bhݺ%ͱ]}llv;,+t:6n; jtpa [QXA VǍ+R.4% ;dYe,e1N֭tK%}寎 EQxǗO7,L7Ro>@]<֡_mv|,R\eIDaRVr~|)ώgYH`['r:. ^.%}p5qjת-'%EEֿ AJB(u+,X2d cXwի>E ,u+Y@q=AV,y=~AAD_"Ibw  @AA"x ӯH'5g>aByǑe^~?Q>DnVO<3׎qꟸ\.MMx}W~i?p6%FF YrkmImvf]$ţ̀ˑǘ5g;>o|)g~T#A$Jg?.M>aC^#C{>b0yXك[RjUkr1xeR}S{~ʄ1#Yg9ZC)o%ic.ڴnŘoq:]^1yX$IbѯyxZJmnwǟ1a(fϝω'Kx|>qԫ[V*{e7j8~8PA ?oe,xZ3E )-[wG~72n7]tIcڽiɡsxZ6oFakOSy@ZhW@!2Ͽ.s(BeC|yybcO?3bx-ҏeWЦu+EivϿ1cF* {̝^b6EAi]v(8`%?vqINbb11D^PppX5WeN'^wVe $~_<8鳨Y-7!*daY_˲|%4/k]AA]Ppet VAe4McÆ86lx١D vdY._nyB%x׿x@týzҼYS%4MCU˦|8ܺ pLQn7{mM0ͨ&i8r(gϞ~VCvY:DnnV"2AASWlמ=4jPWB"2Sv-'gΜԫ[|3p\8N5hȅL  \ rq_TO$z$Ifg 魷&+; EQp8]r.  p$ѼYS a#Pj~[4IlY_}kV 2A,iլ"};ٝ|,/<5S{8ogV_;C{3cK` _~mdɬX :Ixغq PJeƍν?g2|`jתwu,_0zρ][Klߴ7dK}%r/πعeoyY2|wm%==AѲy3:v{wk&OGJu֬]"$8dLyT6]FAxiFBq7 ..++\z:+n& KIȠJ?-ېdT,[ARR7ntrt"k֮C\;9'I8~23Yf)ilXt9wt5kSJe.dflJ8D[nA\z:KlܴMAJ$%'n6;'Obd`ͺdg琚bIddgFVV6+~_{pݘL&Ο%Kn[ i,YSٰq3.d.4VYK\X?HZ58׮'##B b5=@DDE\.+V@؉` =#Vpq6mيDJJ*Vk玗A*iFF QD=랷J;v!I2]+%%FdDE;Cv)>`ҹc=FXN#b!],:vftNOhۖƍҰ~=dIbuiݒ[Y)un%>>s'^:F P\((W.tRh:Oq|TL&E!>m۴y$It؁&>-c*MR֩CcãMBBp-Җ[Ŋ;JP(Z!KqB/]&:m} ~:!l OX==%$ϥs:.%VևԟX_Ni?~gZQ]hFƧPCCCCCCCC5444444444OQls44444~>I%Iި#t;v֢X{\|sq\k|(:.Ǎ'y+=}l[By'n{4y)[s[۷5^ x-uc}5GYO]{vF@W;?}{_3gEg<={G"%(%N&lٸ7T^. 7Y~#s-O4nT_~/^RtIƏbt*H:fgNhF# QQQ<|HBALjBhX[,Ǽ'I; "2~"## h4%s& z=g_pDб;t-7l6Λ7!yBDdX3M4R_^Y"IKTT_ ::cXɗ/K7O\_~P\0"#lT\Yw>U_|9V:jb+zIVlWy ӽk$AXf-ߴi͛7oș3Gz*c"7IEQdǮݴo׆hē'Oڥ#-6yV$TD0 ԩU{QFHĨ~d\qyrϽ4t1%JX] 6;ѧW*/b᯳edɓ}P] ;v3駥B40G0{|.\\ri6Oe߈xq+M=绠`ЎG0_OnGqec~&s-`x;w;p{"";wk̞;;wZ-v^&JߠL0#v:#je,m)&S,$h2n{TS?v$'OӹSG.^``АT,_uGQʗ-c'p2~JMff$IL26-[)gѥcYa3XLIYq3w#[ڷmnu愆g2f@]ٳPdIt2bUj_o~ DGGs-^~l&22(Fkh2.R 'K,dɚٿ"{$OE 1c|I2iҤ&&&&}իU!]tNtt4O>ً>K=yRbJd@q *HbEsիyl\/NJPFu7j@6-9y4.]!Y]ȳ/xLhXG$+VӷW.]|nÇnќ?OMpo߁CdȐݻ{~uoBB8x(_|EQwzf/DFFqe^zKӫr(VZCڹ#>=QZ ':?z9#I;vOD|y1c^FV8xZx@dҪm{=$I|Θŏ&WٴC9m:t^Z̞lFQ=DG7پsO]H:F Kv;AQ~;cDIn#ɱx"l߱6:Qnm1Ik\voڵF$;)S*@2jh|@0 Vvs&;e[<'(0.m$rWv֡s. <:AOٌlv8#:ueX1ͪ}AD}} Ծ;4Z^G>rNVmɓ;z <53Mo׆I}U`\)Ď{ ""'OrqԪ^u pΧ-[4%<"fcaȲL=eL&:uYI*%neH\rĻ %bꤟ7p(vMȘ18Uʓgy%X =V#2oCC<~Mɀl߾M[rd V3aQصw#Fʔ*Id<}&pQބy,>ChڸYP-a˲^MǮL9gPL)'|2@} F,3sd eʖMHvE!w9{E p>˘װat3zd-g/0d<OcrN0];uIIYI0o<{=^;S)Q!M4L-[ӻgwMFtL>I$Us_ \~ŋ4)Gb0Q\r^O0[,DdJJz_+ vA.f^ "&&A8r옪e bejV` GN:MeqsʕΝ@%1t6oŮ1 ̚>`FY&#J>}2@Dђe)]2%J{;tQt),V+fێԮۈ2f}(YO?:M;N@J,YA0cX&3gϡ\R'2E\,SNM4hB: 5wiTܺ}%WoOl;w|I<,s΅_>%4{/}$t\qOO$ϗMT0apxqK4Y%ר(ƚNc%!wNr!CNP<Чklz.U/RI?&4GBA W_&x,]ڴ}tQ〇G2}lƏ $SJt^gqq/CCG!!Bb'4^>}r@Wh4wgqQƸR4y|P bM/n`1ڴ鷺x56MNPaJW7xx;l `1UPE 6SB&Ouě nfTQ+Wy<$Q|.jzH@Yz1z,Wi6Tg( H@Y (î= .^PZ#44ї\(Py !I:/sH@Yu67zp IPERxiܽ$I(Z"eW$(g>zB ( wݧT@x˗qʵ6ruZ=gL-v&8DJAeln˖YfmδЙ57b۹r:M5@QҦM /g()s]r?xH\H<9bcɝbE^|EAMYv<0!ͳi֪CG&*:ڣb0X,sh´lہn=p-y۟O<#M<Iȕ3m;tom.ux|UxߊPHaEE&<|X{44444>M !I"mR)ӨiK><+>>EQGڽ1#CZsC""{fj<|**1cvZ"ժT`MXm6Ճ qB*9ɓSjM4n$q1GvY&4,#dpk&OӐeZ}îmٰڱ,MXhkYv,]4YeTk٦7f#K݆8g>=Qѻzt@fMh٬ v9 "?Nq_B$dن( CAhOAx0QԸzDn{<|DllS| (KEg/^mڲ A(_,GTýԽ{߄PFu._ƲY0w6zI8x(r}><  "W]'K8$I|fN Qس(MڳQy&;ʨhB޾%&&2_rʉ \~(\ -۷3{<:^#IODE9shƟZLǗb)Z11-Y!DFQzm_SNm-([ @E=~ t: -I)P(ҭ˷ݰ3Ea튥tޓMZ0}D6(8IRNd<ɒѵGo4hB2etԩUȨ(֨CfMT Zi>_/fFiٜ\NAjyE_H>ػe+ULŪGPڬ۰93pC*Wc'7f$v~`O9s<*W@RHiYcz4i2BeL&Q?qs/ҶCgJH۷G IDATN:ӸiK5,TZuWnԬQ]+WtTZJj6\{Q޳fqC?yN8H([V[~Çȟ/PN&uT (@B(V(ҧCsT aaa$R >s*UQQQ?y ʔ*O;qQO8-Q;m6֬X>4C8 Ahhhhhh$D` ,,Ϟsc/9֌^g9/^wm.$Ibמ}ԫSmwԏobrC3g2}j|ſa0ٲm;nNG$a ^sx捪]ѽK'X7m?QXaJ{N̜='O3$IǶXtf3E B֬]Gڴi9x3g8ڴn ɓ;'y֮K\9^*zݻVUTf/tԑ)SPFGҥҩDQ&MF(V#F@֬]>Ϛ7!oȒ% F %ct<}h-JvmXd9$KLr! . fVڵr (v;fXUQӖԬאWISSF]zjŋTׯ -6aĨQ>/^C1m,hjAVL&OAؽgz7oQ\r___ .HZ˰#Xw_]1O("x\|qw-~-ܺ44444cHh4""^^ Ƣ( ^^^$ Ō&22fqI(Ƣ}.oA̚6ɤeZZjY,c/XK/sɈ>^?v+{D0|xCQcDܹXVG,Y$)]{wڊƿND(x4Ѩ&FQ}ifbFlwbcFNMQF#FXѴe[vd(jzyVۨD]/wPC}V.~(Ƞ#\6;vоx4oKSf]~*LMXi Ç 5ԠH@Y?Yx)UjԡC}$IXk7U;4Ϲry,_N\\:L2(^vӺ(^C9PjM&O!pIČYsT6ݾCTT4$`qW%˩N_ø \+ѕ… mnϵ7=r37f$ɓ''m(u7f)SŸK,P|ZFJDD$Z4\Ҭ]Io]|7f$2|;≮1#I,h4444ޣ+q{)w B)U;wP >{S#XC)!>t0.\$GXb5Qq2b4Y2eqc2}&] eʔ̜3|ZvC_ӽ˷zM[֮Dy =b)Tׯߐ6mL&Cflٶ](2oV&MjV*yBԩ1Š}gdɜR嫐>}: ,ugLHįr"6nĴz׬G̞;n+l߼]@&KDQT@hXgΞzlߴ7oBN!nL*휿pAF2dh#P/гGWsغq-vn`0oVF ]ۨZI`Zޝ[ (^Qh¾[iټ V-S{3r`ڊAgέlٰ% eέ!v;u6z׍}w%wmVؽ,3&ujuܳgϦM?Y+YlKnSX^yC\M+8Պ^dLkԩUXQ>d ,ӭgZ6kBS /ߵty%uVe XΝGwO>{FN!D1u;;Ι;:X-jU8~Svt qZ4v4 Ӥ)jь=z1j`z}?% emR%(_4,S >֬vhԠێZ>HlA¤(QDAƎӘ9uO9|>ԭS Y7fcѭ S1x{{fe5{?˘c<lCطEZ4kjdr\K=qz8MEQ#Pd N&  W~qI<9͛6۹)S͗gERJ)PIS!"WP &M]Ahܰ>Nr uh 8'~~~'O3gr C$VׯU%x{5kf^~7Pdɜpظa}Μ83'Tϫ(n@EfΞK,?,C͜ge$((asF0N]E͚8Bs.""+W/ :&b_ɓDFE}p~~Ϗ?`jTgi_G"Ȝ3*/GN]=3Nm|mW2eȏ? m4*X@-exTHaPc9e k@ =)֩p,[*h93XhQy]@Pk ;)"Sgf(so2Y۫d41jGҧWYmTZҥ4_m;жuKdDdTO""" p)N(TCڵ̙;ZQz\2%Kc ޱ/e*e* v;[tR27[,^E'I$CP@=V='w.v;g͢٬4!Sw[6 .WLǔ?:ݮj;?68Zc .W">>WF.F`O>!OΑxƚ9wʕk>6 ]/I] ֭B{ޣ C% k 2PAe쳌v;:N @ .вiŸƧ&jhhhhhhhh߃GCCCCCCC$۟di. Sg?|i ML(ˋ+5cL6#X(r2U(I"_oA`Np:qW]W#D F$>z?K,. L0wψ+~rBD;z(X%RVi ^>⼏=}e:OZ$f?{NPs}?U JW$qEvwݻbٸz:W^~n___ég8{>͓_$lڲ!{tlMH7nK|% d˖ A3͋/2^ FpN T\///bM&DQ$(8Qɜ):TS!"(x,L,SD1E`2e 2gģOPŋ!"7nF׃WNDDFb49~2h'n|ڣȐ>=ٲ}ILsBXx8SgɓgK!:U*9Nx,=Ql6|}}[ * .gTXީ%P]KW|17޼łEK /`/VwKr+̤c t*f̚Õ E)R,SR8hG"n.߯ict `Ws+RXz=3f!]8yS`A2}ܹrłſ+W..]Ž]='<rAbExx2Ț)f|}ټu;<ۋ˒e+!2* AHOQ$Vl6石OVkZcYiܼ5^ujcqԨۈ`dYyW2~5}F[H]Xt.]۾54jAˀG#3>z1xya0$CCgUa% z||tHNs{-Nҽ/I<^$I(t:tGG}6 LIml_=M>AgŒXV ˞9{<ݻvH _S?)M6k}JLL E I>{Ρ5ӶUs πØ;{u6qqY2b4&dܹw~}{㪡 Ѩ\+q GE*Pf瞽ĿEoClԯWGV+!o*(ׯPd Tߠ.:O=<F|96(~.,Sx1,V+58d߾s[$f&I®{j2EJN?/'ǎjKF @L[#{eܻC3yx|||8aaaƍa?q#dY 9 .S،,tܝ-`⥜:z'O1CRNš;i-3eʨ_:SKWt<F0e`5S^CPB9EtlAdd*c]X,ӛ͎xQQQ\8}7OnE&<~Ȩ(N=KWNv?q6ȟ#p5.?XH2%ҥ% ^IǮPt) td S',I2xL<ME^?xJʣ( ׮ߠNHăȚ5 fny!s <~~FiA$ = 8?rdAɀS3l܅Yt( Р~.mZ2OO8qʱ&X՗Mͨu5:uIYB:tF 2i>>vE,#8mAzn}||%Cн%Pߥir죫MfYI'_/ 82:0`0?xdb놵X6*U,,d̐\12 ܸu3PbG@g'߄ˋrUn~ ?Ok"KKEQ^ nS@>jҠI "0a ǍI)X ?It^5"lI:5v$u0A7z$V uPzU5mMپy(j}QԪYEQ(Rr5111XVzA5Qc4k|2lӞ&-Zs=v;G2iRA,\+QQ:oGiҼ5{Uffc4mц|ybX&L#CfصʨCCԮEŪ_Ë/h٦=#njYF؋#r?իԯS_Wc嚵S yKΜ9WپT5Nĩ@9FddK$ufYz.Ϟ=Klє{v?_>|@JƃvYy6v=q*[7нK'#f&({~`o\6_Oƨte6[WA$I8y Iزm$1}/L?ב&UD[[ IDATDtX8Ucj"I7oUY:w2ML2QmJaczvjEYflsj}MiZ?:q۷Q{ `"] 4kڄjU}-2<~*9v$7o<93p ,]<BH\\< /MѪesCBYv:kkuLZ56hhԬϝz*S=c;d&7)?1ik̝5:7(=KUf@ȲLZZ:'\nZ_Zеϛe w|^19 2*VoDB|Z-4a\I$U,]nboo$ɴ#Fc-]HĔe(M.sUH~t`n߹#* ?v8́uNF#"g`C\\ Gw |aΝ <~[ѾkoAӖ|wG6jZ܉1l)_jy `,͟ݟ+V@Qb%,;EgiR(!K셋fkHRXr aY,\b۽we_ J~F$ڴj3L9aC2 M$ FkSTӯw/ ڵmÒEhѬ)aah4*W {Ŀp/nc̜6u6#-BVwsgMg¹|$ϝsؾcϟmml6aݰ {;|Y樴t֓%WҬu{<̗n>|,dfL^iަ?APZ>8Zs})‚?g&3N\q?ûf=4 ;vFYz 9 hд% .7kϞZqbb4lڒ{~$u֌? I0 5M[r9v<Ya>"wy<c̛5=\r:ӱ[ORh4i߅]|hݮ&min:s*WIH0IF3u>$%%hѼu;#"b܅4hڒ"2qq3-p}h_Щ[/%x;d,vRA`漅̜ng^pa9&+9l'׮bItڙ|SgQ@ݹv N`\SǏr::o |b=..hZÕBS^+$$$xMreٲuyۺi`ZvޞBC.<3[@i4),#2}z-fS$QjHOOJ HDŊG4j\y$U@Esm KK-aaz<{ҧWߜ2kzt#|KڙY4j*h. ?%NemXYYx<.9٦ v|B= G >uj!%J[DEEcoks\ 2Y`6.mӴjќOck|ܦW/<W/b>ƙGq(Tyv ];w$4,0h@_.=IѢ\:{ÇrA+Yׄ,C 1"׌^ջ Y)CB"CBMvhX:uj2ylLԴܑ-R4n׌<I|Wјeۛ,LK"=#{ƍ[jTLm*?Nle,g'ӵ… afl۴<br899"œGR%񉸹I^NZ9VNNN9i/_FF*YYqp(DffڬwqY1t0?HE5} :/j,cM2$lc~%ZMXx8k4,˼~-va0p゚BHMM_]i/y22ԫ(dT6).|P.=&Ӑɒe+IHH =#KO!2իzC!" @dڴnI //n߾OFӸak^ G^P||[\[wggkJg 1+o?wW~KfP$accC||<$>> EеsGvڅE JB ?و'H%thKcڴi @VygΞO7pJs8UicvmیiK!{{soŋW4kD)0 3ڶiEDd$Zέڶ+׮Viֺ|rmΞh^po1l,H{~,Y]6j5gΝg¸1zݰ Κ/OFz3,r-ߕ2 \77WӲtԨMPP{cA謭eu YYA2?[ ""RJ0|@;#ϟefjXڗ9˱cF`/Wf+Ng>'>>[[|d^ݱ3͙s[%3! F79cÖmFf̞O^ yl۲)=5We9deeXڴjɴYs0fgsl\ٹuȲLLl,*Vw"s޼TڵSMB@k222:c?;+ԡ9r6kgk933j/H܅kogG^/fmSSyʟ06i-̖ϛ6̝90ne 3}Qղl*ʕ-޺}SRfwYJ~pȢYz7o̹?yڛgm3pws{g0A;FcToL~CzuQ.삕%ѱ $\_}z :uhNv<~U(S,@ TT4nܼEqttQ9|gj֨N 剉%2*=+I,Sr%"""O7biݲ9VVg4j ŊHreV՛O"]:u@Ejլ(\؅["IVV'4, ww7<}?WFRgVNG"EY:gΝG.UkkkzWӋ"n+f\vbh٢(Rv-]HjUidY֎"E<(]YYF^ЯOOT*+U$**zsڸJɑ2K!"׮aCwy:ZW_}BARq->_;K@F pqq]֜p4>jҘR +Ge #<}N [6Z -vvT*,Xt{eJpa"({LϺ Yh)Z6,Z<}ʍ#?抝d ݋ .嫿hM떬\g_ 266nقeKpl~<i\|"\y 8q4cGh4*@ pqv~gi?hs [7=<j,,[4ւF0f'xzcђ1d`Q@ (PP+,*dOJ>5{;;i&G$acC@ 5- +++dfeVIOO% `aaĨ]動֬]iS&!٤gF5rN$aӡ /H9@. +G 22 ?JC(UDxjYC #i3mjj*TĜ MKK'5(`0ד0d,UcGDәgggtMDJjjzH)'r{yrn zx;ZqtOYѯ]>Sq_{W_~j>߷nwoٷʛ7o1;[og:s[z0y|yx*$m fR *:ټ-R qKJ}|{1 >{+WzR%g|8;;Qջ 5m[t*$ѪesjըnRBen߅+cE4_Ə(h4dYf”|P.Fc6BE6lފ%:ժ]IJyNʗ+!RTI&NAs֬\93ya)[4{1[#fLDsA!l޺O.|w07oʅd[VӴ\8}ABZxtϜ1swȽ/qDFGSͻ;$bmӊ/^49dY6_tU{mP_ I9UkY˗X4Nxs? "0|iEGQq}MQ8μo'?i֮4,&- SfPB9lt60gִw*7saSQru]<#YYYyisNG}E5+r!We_y5wf 9X!OA{m:t1wiii̚21u\>؍o Ӿ? FC՘:c6pYE b*bbbi֪kkkvf ޣ6xJNϨO{IkM|:v4ƍferg 5i׬QիQI˯2o Oш h4z@%]mZ"23m[uTVZMj.Y̩׻'# ƻJe3_C @P\e~Ça,[/^1l@ΚΦ-3fVYzgNcA\ ">:G%Wҡk|W@g+Xێ]Yd9]zҕ U*"""8:ĩ3ԮU=3{dΞ(aͺGwb\vM[wбkF@ff&;wEл`6lJ.>l޺ZMjj~B~YaݍIO7͟$I\~CGҵg_޻Ft0]|x nF^=:x Q=v ҤE[4 Vs^,^IHLLbt֓EK}Mz|:q*3yYt֓b4]Nz1vd23HLLbqt֓C?aܾ{}7m9|(}{0H>oQQh4ڴB~ܽ&9!0kT*G>β%dϒ^BfXXXqݟh=o!deo;h(]{;ԍ 2f%P DO=/WqOغq_~u!3s֭3,?KKK>l҂s'ʒ IDATѵsGX`Z[M̙}4ϐ~÷u8cgg,DDF&Vkk`oMT ߇Դ4bbb4m7!2*=Em)$m"Izt#==Ys2rP))z8éP+׬`͌=DV3eҧh4y64>EJ.8ukW.7ҫGw޳Uffs߁C=(Ŗ kƱg@&`c:thwhjvj J0Oʖ)ͷ!"9M&N qrvBEDQbiȥӇvmېƱ#Џt TCeT.HT?`ꤪTd~Ng$IdebU͕0XaŪmZG v1nN&7TFd\ 6 bcc?n,I8:8#9$I899!"nnȲέYnC `ıx+Ɗ 9q4ʖ1K7,Jff&( \IpQVAe222p GVRv19 v(TI(Bzz0_O>y[w"66ߥů_|LH-Y]=vUܓ!3v'1ǎsy:uhJK.*(8::mF)Y(RP! y\IHB& uUN8vq1f|KR}+[&o'11/_%NKKn._WȒDreE,JxT'G5)q͜6d HR~rVUյ0$ $ _L8!㑣[*֥NR.rgor+(TP]{Ҧuw*ds~#G zj ׇWfӈ$0fINNQ(x^ |õ7ҩ#C3=vOsy<}7A;qA֥#-$05qALpAe4n5:LtL 3,l2jfrz""~wdzXQ|W!<w=ӨfR[PEbRqqܸuN羻+KV"**QaaԪ]@&8e||1m"82L<<:x11~D!{;ztʽ{~*[$9%1_f]߃1ek3 쏋pLd 8k&zvj\] eN ]JP?wWWs s'ٸeLDd$5kVc`DFEp9*+K c zʈD@@ i.] {;;#"n =~QUbTBR+F#*V0[=aZPY1 jT@ ٔZMpp +[kkkj5,Q0pssh"HDrr opqvӳi0iÒ$Itt :ҥJh#44ŋ >h4K֤,^|E!{{J*(GPܳGZZ+GѠR ܺs+KK ԯ[Vyʗ#,,ظxJA"(8D,--)St绷JreDVfFƎ,HjDDF DEGJdd11x/1ۈ(bee,ˤQMT*YYYHL>…]|mp#2AAb: g_|~}e12)V:WX[[c4]]ܫU}?gb$%%Pl%""w\]EWdff#ZPpvvXѢzB||˗RMP0)))/Z@τ@չy6*V!$ITI/OH $$… \"Ax+Z+ʖ)MLLl>;IHKKΎoHMMh"ۑ+z}/^fjQD @& 5iiT(_JK^x#FdP\N[~Mvv6ʗ] BP j;};^|WeZͺqqvA3zpvAq6U*WG)PB(PۮZVs _1} Q@~DUAiQȟ?! (P@6dYV%(P@ 7haa/hS9'N"wߏwlپw=+P@m-ƿ߃/_W^U{?~C'O9{C?!;;3AHJJ&EGEy7$%'/zu0ex.\Szrz_s5=~Bzz:N 5?Dwܼu cj5v,X,7ObzcQ,]řy 3.SھJgT|}J⣖uǮw˛fւx ožO'ovAˬQPZ4&B2at:`~'޼wӛHb:Bs((=o_s5iyV!<7p'ܺsYķz;7m1a>5r_T|cĨqK cްwR՜9wo?* a㦭]ٱs2ROVԭ]~KڵhҸ!RVMN<|bEjB׫Ǿ/P˓ʒ-15SwާXxWLxx+'*:NGtL46: ^Yޮyg ŧ7_|Xv/E޽j*$&%M52?+T_DEEӤq#|> G{1lvo‹\l1ݟ}oպ |gh,44Nڳe.z,ӳ >jژ'OckkLXx8$#7߳d<\u hֳ/OBBaTdRЧY NUP7_4 ؕuGخmM@%fHL ޵3^^ũQ^=Yn#:thFuӍ-vNF% ׈B$-]|'Æл`jլ`{ch/֜poMӻ(Rv-dY#z=;ef,W|Mt {;;U6?yʣO|]ۘ5o!Tt:k̝EtL,|Ctt4;sL5FMrR2LJJ ,'[߷7U*Ud݄#I"K###U6`4]Q1<|&y{?pOGu$`= @Oe1TT-RZU=r8uf]woOXb <} `iiWXZjƐ0FƵ+;a26'""Uk##WΖg ۏtОCg/1uߖw*^^ 6iL!{wV#IK,ɦjTFƍ%Ո=g`TȨ(Mf$,R\|aVBhh` ^Jxٙm׳1y k&tsfRqs]:,tٗ!S= BP 4nކ>|Xt9?ߍFCW9c'عu$b"jAGTT@׈0 21DEGGDPByFf~fYܺxL $EE=IXxiL9~}zR|9n߹Yh,Sr%`mme4-AeYQ)V(IɈHhsY5dو#Q],DDFbooҬMYۿw76i,ٱOoQ,Y0$j=}*N@` 111Xj-Ϳ8n 0RfY뱴{~e0]svڙ];#{ef8h\T*4nN. fȲ;?~(MExʟ.Sv-= 5+gԉ~SS9̛5"Eyoʜq4~ݽ…T}zXYbim! >t ӧ?jެY J͠a#[6N4iԀTxO%`cňV*ENԙپe#<ӧLAѣ$%%36nDh44m҈8L.|CkCדŒEsjMTA0d@gee 5!̜= AQWxRSٛEͨV՛,.^Xh?{&]3dhӺ{ceU zӧϑ),ˈh7srt 99OrU{zF //22 899`cJMK<6$%'}ʕ-cNwp(Ϟ`٢| ܼuL6sj֠P!{2kgVΝ3`' <QqJEvH! zlz.\«'tܑ}{s=8H(2|ȠWv]LMM٣deea4&99kΑ̲% a}`.^0ϴ?֭b2J,Nc>}'sK=x@2MDYf`lۼ+}3qcGy-Y#Jd*_6oXGV-߫H?(Ԩ^hQS|iL-w-g_џiܰedz}n~z0zp}_`Q<}P_r:.\DR˧s,+ri4z ܳ3ӉnNǏGr=ΘCQՔRsfr 7o᧟q^=ٸu;ׯdǮ(RFWJ4 'NfML8EpE/eԈȲ̔QTL7Uk9}kmܰ?s.Rb?\|y:o VYϝ8r8bꬭYv#/]x;wiifr!"!4ܴw+{Ȍ9󱰰@MU$[ʙ˲3{$< VKnosߏ9NȲd>lTPOC#GqrtD~I_wV];X|==Mwn߹@V-8e7n˯`PL6[wrMdK<=q AMP!xMg6?GŦ;8~ob&D|%Jx&(ز$ 'GG5?nC%-uܹ{ϼ+JbD$._50Mr:A!耇;̹ FzZzU;ew_B N>jٖvmDD#̳;WRp$PTDC!Ӊg OME´`ɉlT=vvz5rOKA{;;`{/55 YtXX2bkg&g>5[ Y1jSywqR?Sv^bĘ{1n4&(F )"; ׶-1`~ٙgfgvgI$XM40 uP@.UUUX ̝/|h4ybD뫮q;TUWCFhn[VQXX@],F2 ֜ee 2F"XQ9^~r mYw{?{q< 2b1  b$ 0R&JA xٚ IDATN ]]H${&8N泃G4m$uuuREEE˯% @otEMM C(LR)i֬uuD5ݹ\Mgfgjxs:s{'w>1Iy3Gmm]~㚗вE ]jwUIqQQ~?aQQa‚\<;!<y@~\ͦ]mY5_ny9d2o p8D8hLmQ8rIJ,~?lG;g%]U}{ ho.ڶMfKؑeDk9(F?v 5;U M8]6n<|uPL gwhX0 _a-Dt[߈wWkؼy3`ݫMs_˼ ߯/`6GX^zY `eTUUѪUo>AMi2狹t1uӯo&;\,^t!Hdn2ᝉ<`5_eЀ)JPvԯ\ʍF;)!ύ:y^fogzwus&z>KXd En:y s^ kOL>7eY}Q^Kxm)+c(,,  һWO">"x:T*!H* 2kuA#u]ڴi@-0 ˲`!7l0sa&gӺU+?;w"F13ڥ tGdnBA:oOֵ 555$ 6n<]&N3'{S^0hSښ-3}@x,Z(%%%tر/ǟ`38cXp>ۦsN UM}p0?7@)D;6.;3ͬ|)7s˓uW0yǑH澎qݻueYg`2?&ig >|iSo[b#Nj|=Ǧ7U[xg3{Ƈ(ekꃞ;!u\sh>4/F3|^q\zVxmvmW:(DZ|J.lF y]}w߉m?Ym!D~ è R-;TX_zysqk(iVL"0iq&  l5tXuu1.8A7so8uƩ2[IRXy}FrQGaJ}%xdžMH$$ySێ߉Ըǰm}F{"p؇cWk8QPY3+QЭ[{> >dnԹq&7Q Cv&op䍷A^"`֍-[^JGNY+'\T*Tvj$D"ieC8KHR0ܺX۶X|%>/{t撥kxlْ^ɝvݛR8 -.4|BFxd?7!r9ga&^ ='qťeKF> apRSS 7ߊ8كK 3գ/gMضű'̨v:һ`f =kڵfחÖ-[Hs_gs.ǝpr6{jk5\}e:{6foH&9SYjfUxѷ.A:.f-~.l4(5oz螯UT`(s+W;KC`6\nɁ3y{̛֭Zt2:p>>_{g{>?vamY6nvT2eY^ڶ4M.V\'O?y|\v^*. v/A3ʫ|6s&[VÏrς㸔 }RVgӳ{w֬]˗Sʹ֭+a[,3cȌ.~  t2L7N&Hrj شi3U5$)nXd)1dF:%͊߿DTY9[cY&L|Gqq1D2y'[iYx3>eѢ:d0[+eǻ7SZUʹ%~a`ek<.b 4/iNuuua۶X6yZnMEE%O<0+V1p׹q|ӝܿy7}B](ϖW~kо=55u#aZhuBssc۶CmۜeihK/_8P>"yU,]Ɖ'd0<.G:n<oxb4o^{ǝp2gr>7zȁ<8XUW\Uٿ 05?a⼳ԯCR4鴓 JA)70QJ&S$)6o7;ʃy$I,"ũu2u4RV϶)mqI&bTWWS-5kb6[l-e|> LС{J}=?_Xu6p1G翏r=@2nKx?7z(wlOWS s*+w CX|T s~Py۶#l\7 w6N _xZF>`{-op_0~?sW< қ?mی}v~ĉ'O=Of|4UN7 B')~笩y@M9f4~Mp?νmsύss\shS7OMMvkYqnoZ7oo-?n۽{Ι<\w8j$Ceb&;uC+.'Oо-7cC˖-QG(t'vȏKkGȏ'z@=ٲy C ~Ǎx,ߟYh:Xoߎ^={п_>pT~G~{4mqw(K""s7dMM D-󨩭%HЮ}[6nĥfWUWgÜeلBA/_9s1 8CmfΚ)N4hȎT'F#5{d6~T*a zyG<oP:g\,)""""t1 AMt3wg{_mM꯸^7vqL@Q@IڦN0*+"""?p lPϥ==H+O) ߁EÁEDDD~PDDDDPDDDDEDDDDdgiY,3d"nr,6&iwYlx9S_> /=?,lۇay4h0740 Ķ}BiY>_u[/CDDDDvP4kъiob`n V^ ׯ'Je>2 #\?y/CGyl^7^xqwБyW-,+o[zkQQ5?Y5XT I?,4qu0M˶I%iZ8NN2I41- ptf冁y]Ŷ}8N:?`ג4v6{% Пt:`SK3@r"""Xj4b׷AWP;\Mf^.ity䞫|4(4m Ug 4`ص$EDDt;:7Q@9i}a`~AaAOn^&Wlroa;i >}G?qP߮0 V^iQDDDG `<_L+aa&P4I&?bA($T4MB@s/ P(-[DDDD&n6NBz?^lj'O oqQ<>ve34e*>NMM G( x 8.y:T?^wcg+VsѣGw"+Fixd/828_QPŲL2nPYYa0q\dd `"1Wrc2h0K-cxhղ# #8x㺜t`EE?Czɀy~F.3=ϯu$xZlN,hFE^wnZ2aɧ%JqiNm駜DJ{)+w^z2=ޝ4XK.n]cKlgN]z3gYh1TG_ep$IwqEDDDMdђ%N'`IAAh$@aa!ec4M4~ݛ>}vXq=7ŋ88X+FAp]@ )qõW\~%>t?CK.qk?먩_#iݪ8x¡Ÿ7@uճUUD"a~?bF8mce @ Bum env_|1бC uxwd#Fмy ĉSO[󊈈̚lH$fh|}|23{n8ex&MʒHRzyls/5ޟf^( /a;xWԱm۶fsR)F 秸>p'N0 \ףe˩b/iݺudW-"""3**nm<q]<|w7͊3wkj2/<(**r+EEKMm-a iH8LnetX, f:o$Ib8Pχy^®G$-b8`p88a`F9C$tG/Zos) X|7e0fzc6kY̜56rkSaP#t2J5 ?yaP柫p^_:q z[` KYDDDDV7B 'K IDATOY(((((ȿݱ44- C;KDDDH$ǟʽcaHh$SXP@(zph4<‚8KaaHP(_g4` @AA~/I4͗+""""cL;Q8i>˖q9sg~zǝpt+ 8|N< >9H$BϾضY1=vE\˦M98sI\w-Sxl|LN: N:L~9OGNDDD;%X,^#yx,ir؇x{0 ;^{N;\} ÿ́"a.Jwo PRR_C8N桱"W8С$og$ 9;B!>/޻`<1i?H$̓O=˰{H$1 8xǙ5sO*0|^L4SRu+&gv*{3jkkg,\3Ͻ@t*M.Yr%;d7 u:.m۶4MVUQ^^N4ER)o؀eZm&Sh] ,q<EDD;,1+30hY߯oC.lt>?ĵiS<(**j0umVޱct:M_ymJP@a c:""""%OEDDDDPDDDDEDDDDPDDDDEDDDDPDDDDEDDDDPDDDDEDDDDPDDDD~t4w\ܑiaa >MMϕ۲mS~FYTh\d2I4oޜf͊au]\Ŷ3Ei?D~y8cUtؑt:_χ?c8:t 4<0p/$t xKȕme`Yad[DDDD~4 X>{zy,]6о];z΄w% ѣ{w~%F{A0dq0pH6lr4M>3[h׶-ݺuUeves]vtҙX,F8f$I<# `̴p!Q[Wǂ g+:ocR)9 ,ˢgX->vG®Cc&S{5`v~^{*>D2ɒ٣;e1|0Ÿ(6yb<׳n: FL>60 (D"ꇿ\;I%`@ @:^ X,_ϗ]^5شy d-Z`[`(""";&{^XŌRQYI< @ `ѢE$I.ZN6n"od"M2 X,F< /5:udݺ7hC5ZЩc':uD<<]tRGDDDdf}'zGV-$ 7oNqQ!iҼZjiD<ϣp8LMu Pfh֬aY6x`0_OqQqb1EEB~͊q]0)+/sND̴%%b8C-3= xDA<$lmY%HC/adBx ڵk糙2hs%p3{_#Oa xmYK.< &oMxz={oёAprڙK/c&i`o==_%%XɈCƫ$q|>dq={O>! qsm7(@MAn/2oOxnݺPVV!"v٫з.TUU 0 _GB"y!q\=J"б! }ޅ,^V^M$??d@f8|:?s8S}@l&=8W]ymJKt$DDDDvk諮ox?Cm3eY?&NJ"+BomJKIRl˶q]d2i]oò,,#ߏii4~u|1LYa;Ji׶-mכH$غrz8ضE0<DmHR8w zǟګ}e:x>8~{ xC>~WK.gQpŗJ,﹟}G̻&axK.}G +\b/Ҭo:O?yq~ˍu-<1x(^r(v$yv'gaԡGO#J;`xkBPDDDD"W_d2~a+/@0UV$ Q\~Ʌ_SߣSB!&&͜ŢEHz]}c`-y-Dyg<mf~r F ^\ +Rں5ŒeyΛݺ1- V.{f9qDUtÆ~N;{)ޝ8Ν;bt%Kp\xǞ{D"AΝXt[$ 'x\>͛P^Q/.;QVVλpȁh޼9-7' 0S[o룎ukǴ3kzo˲ܹЩcG0 &N‘B6 ;2jiѢ9;v`Ƨ~ztiZo߾=͜ŧ+.OwtYY9[osyN|0O(5n~Zj`QQUcXx X<49pn,` 5n1sl뛿  DDDDd("""("""" """"("""" """"("""" """"("""" """"ؐsEDDDh6>˲0 0;{>`^˲m[-k6LŬm|9oȿR r\D"I8k׮٠f w|86o=۶ءݻwcٲ|f %%% <?mөcGuMXp-[.` իiѢ0LuXth] if֬]Ça6@T*E]M-F ؼy3-Zȯ4Mظq[n5֯0 F֭U,Z=if{ePڿ_?,1qd*ňèc%һ[ylܴuӬY1:d7m;ADDDvymڴ]vx%KRRR@QaҬ,@a˾_[TTyJXz5k׮à{n\e{-{y[DDDd i2g݋@ aJF-$` 3md2E$u]Zh΀I$bu۬'v?SkgY5VXI֭l޼m۲rjz t lقJJ:uVtҙi>m۶,""";ot]ݻ|Jt%nqXlu)-Ŷm̙Km<,]:JK[ӭkW,Xҹss+䧕fѦ5sK6V]`ݘ`!ZuڶmxڵqZnyf<7/0{,\˶ҥ ifZ_k_ϝQQU^;K8?t&8g` """ߍe^F-fΚM~}\H """";@@Q@Q@Q@QB4 C{TDDDm糱,+|>S{_!PDDDGnjaTWWxb1 u1-p(3pA|P̕QƏEDDDG-䳙w1 q,&c6:ud*m[i6oޜ_sFпdR֯@]i׮KҭkW.Z̊+ZUCמtڕnݺl i29l2SizjJXt)aH$}}4z:4M\e84P3qH8̂ F"mS2zZ7кuk<#0iw몽."""j4M.\D2IJ,0i`0ܩsΣ]vKѮ][w_o?4݇%K5h+("""";V5^piZlARaWѡC{ àui+\ץY3ǡ{̛7sի[Yr8""""?D߿_iiҿ_+W{8Ч.~eҥ3]%""""?t#h4ꫯׯjDDDD~bS\uرOQ@Q@Q@Q@ 4OEDDDoM_109M&59oLO$p]*++gڵ 7bK/3|ߑ }(#%c7EaFyexǝpW:\6* Wg`YVfm"""" GI]zɼ?zM:.|4ϿR,۶y⩧ݫ'}h,E>挳gcY&oq60|L4:`@ q6K-`rg3Gu]RzT^0xLxg"pZ^ˌ}azvG/˲,^zyضMf1xɿvzf͞ÚXp?3>a̝%+WƳr*jjkyw$+*x';wcW|.ku׾︓֯_tiڜk+) lxyerw͊<|2M[^kJ` xk6j]]W^»qYbSl9.bzx_1|9b.7ts/zYbWMs96m\ʁ^7@_DPuox mc7T*fEDDDNŒO4fNIٳlslϜsb)}4;wf$I2gU{jksz="""򄄡a1l.?mq9):N S|}'"""L"""" """"("""" """"("""" """"("""" """"("""" """"dw)8c A07ZkN\;xFm=ιgi]""""g|ܿv^İ499E&焬ol&E Yz5Ƙ7/|qIgw\+;K=##:tVGx"GFطo?atRz{eR8T*lX سg/ӥiVs\0 ٽgRuebr.J2lGfٿ -#eA0b<ȣ084e1wq'\p>qcN5Wfti1{ZFGGV<90x(.M322?m-IDATB>gӦ,=gW,fA޽DlX;wE;wX,a:$Z61yJcc\py3z z$I8t0Ϲ|G1011AVKb 6nXO\ғ!"""Ϯ8q7>QH$I~02001xO ۋ1\.7粙NcgWCX:;;Xz5Ib{$W,+WZK.m]owIKK 9cE{bkK+םK?޳zzzfrr8\dpqllz㜣уY`s==9r.8! Cc ~{-{Z/g|||މ)0XV.dl|aV 3)[l3A&^c喛9OLDDDܼ-}f-7ӿlw~ ?}}ws|o?ȣ}]vc0s9/i(cW|o||ʫ#O]yZ]߀3!IBT9R̝?lk{Z?[a}?ZZ[&00-Eg`y?;y^˫^r7 """LRy+_1B$E0߹O]y7 s /|.SSS˖??G߀S,z"33\75l^8i\8+u7sn6_oADDD)tL&d3?W.{w?C6eD_5_Zo@DDD)f'`̀1\z>$IA`ZK.ָ&4Lz߼`l޴͸jOwmZQDDD04l:Ɩܽͅ6nXdz)g$I04|97""""J """" """"("""" """"("""" """"("""" """"(""""gB4`>tgdL'J7^?i)""""sͻ0{% Ck_{S>ClڸEDDDgb?z;/~eqуdY:sV1<2JR6=J.ѣtwގua ޥs^T*Uzzikkcll={߿R.1R]J& EXt \z32vl?'CC\bmB8XׇRW{o6Wҷ*"""r spڵ~/^%?Ʊǎr\000hs<ʒKѻti'x# ϻ\>O$VEDDDhfo/YwpM7SVgs8ᦛ;vhfF]ƒmƛnZKo^x`uٳ[o+W `Rf'c R뮿q(ҷ*"""r fl|m0l~}l&96iƴΙ{3Zks/c 9s̵3z&ڹk77m|L U{jksjֳ<1ahزu- rwoiD;es/'K>>s>w>uDDDD(㌞2O3_f Z]DDDD_:ħo! 'pS9g[3GBLFPDDD NM$Ij$R13':ӻFiޤcp`ϑ+1! UhZDDD6{nNLV*b A4`& a1PV05VYC&9̭DDDDi˟|_3 FQ& J41[[U6'8gI1Asg #""""O[d2q޹v&apޥyZkF3& `m%Cbs|O0Wf%Tf8gڤwXΒ8u=tju⸎mo?@91o|Z2Q8ozۿ@F7&fY7_&U&|C!q2ż9 l:CZs3?`'8Iq/ z`DDD {/}L&]wm})7MOw7rr^F𛜜ƛ;v6ư}r,m︓r+Ib10<< 7̮{Ðlo$I\ܽn0ꁛ߹{7?Uv=r2vaw2v9vlM7c 4$ZG$j53-> F PDDD |0 ٽw/]q𣻷͇>Q֭[˽Ϸs-_Ʒ ◯ᮻ=Ö?K.ZHwW/OG{/Ͽ~û( ?3?+V^AKȗ+ۻ|'>7ΓĖλDA}5_7l$:`뭷q񥗒q#ԥ9Sհ64z>0I~~3.F?}{r 7׾Vέ?:-]5V~{ﻟ8lڸeU5:y+Yb9LĕW+7|3_QpwϿV-2t!Kv?Xȳnݹ8gV*mo !wRIy?7Jaށ{Z.b#~nn Z _|ǟ1!ݷc{裏 uW$b=j׽&蠽 a %Pzl͑3.az:O_/<Kz966FlyikFigrjJcW*8rē0& aJ kx΄cl&%tR,iiiyB]ټicsbˌxU'& [esDڍϩg|BF˝ -R.sn6k7ljjc ӥ}juj(ǔJe 1Vv7[l._\iz=V* r8QD$iwppF!agsO:FiI] Μ$WQ.zX0D!q`KO['c Q48:.x~f*!?~{QR(aon07DQKx|m]/cҟ 0863rJ5j.C'""" ǂ=͠v NhkkKC꾕t̟ci:} X9O^gTnV=C> s, I6Ug9zr)s?UkgT=xK㽧RP( j5ju::(i*[8]=y8ft Y$XɄip9lb26Ia@h#KTefZ'ONUkٴc-DQ'HDDDΌdGE[[CCC,_:rbK1 JQ֨jtERשjs9r ɣ۲NgT#S%DQv>픝= 9 j 8ZDae s&k9[~&Kr|·wǻx6aLOOcm\w:=<ȣcbxd=!ȣpCQ(xPٻ\6ǎ]Zkr,?(Ib) MfikbaZ'044(.ou>>8ZNZZQ֩Tk1zddTt%TM(,{ An=zB3]21]4G&8<6NR oٌŸܚ7x9(x8r(BsYmhy-#ϳiFٶZ2sVEVc\"18_Zã55VF}ep˸5}Xv3\>@WBGGLUj 4`9/ާFӜ0gzj$#e"mVQHZ' /]@2 y3,A#GXlCXk ðVsm[Yh!Yr wvioocpxݻe=wG,^0( `tXX,/FQ]Z"""l ,ݝtqtfdsttၩ$..F9L#Jg!N,p7~fOg5=!Edt9Z(f 0Ԉ10cDDDD * +9:t * " Y"Ț2$c_f-6}bxSqח.XY3KH$1Ƥ2|.5#ʕj<2imP]2'h:͉<S"O@ l99 08 XwJbgܛ"2,""""OdCws yJI2hfl|B6a$qCDDDQnݹtOEТ. yi?a_uӛIENDB`gammaray-2.11.3/docs/manual/images/gammaray-network-operations-monitor.png000066400000000000000000003015211413310443000267020ustar00rootroot00000000000000PNG  IHDR + pHYs+ IDATxg\IPR{"UH+6lgbSQbbW (HH' )ռ\D{?>d'l++#:ߟP(,)-H$mKkj(~aB-KD mKRi&F|g, CJJKkkk: (,,T:VUf}gjbց'퇫kD"#r}|D C#' dH C,ȐdA 2ߖa3f]|e[]rmVsee=w@ h<}s-j(#RQQ{ tutOd0V3.>œNOqye׆T|7rrfF 0)**qK ~ yDRܶV\|±'32Bn|?x`//'d2 !%55iS&"l&=ő~۝' H$L~'Uak<nZ77d㷦Gb%e%6E&DC?"Z\aaQԍ骫5 /uuq:G ޾sgbjkEeev?h[ib i/㤋cG40[۽~3:0_0%5mY@ 0VǎRWkϛا)it:e\BݐA$ɺ[k! s.=}\,w`7p@? \nܼm b?I hanvHDfVV}@C]maPff>h:vWTzK!}|BOuJxxFEݔO8-5UU5UUP(|555zC r2޾}Ns.[4ᒽR.99,&GSg6[cUTV۰9MP(Y| go!h5Me7f(~K2'7D/3$F|^"pZaƵoC <0ϯXܬS&Np^B]tw+-!I[|Z>A!kgNc2 ^ C}n;9v6t0Hܻе#lկoSUU~j}:֯xש>h\mʪ#*oS\|G]p~ܭw7n޶"O }{Ɇ)i[r؁D"^teB\{̘6j=- i߁CdGPڵ3 _e#OZLfŴ􌓧͜6YWWOϝd̊Ϝs%hh(..{쥡n \]D"QrJjGs/m߼A^A~KM B]:x::N]{ cӈ$D2k:L^XD伐Y׭c-JVTT̙pڔs/{Dݹ{iӧNNѾ1 ;°LH?Oܺsw ݃?gxѷV^yێ?pCveEE6sNc1#^2h)gwY\R㕅\w^}۰F$uꗒ0x'O]T";xxA7DWty=w4 ;g6m>;;anf~Y'.rrrT*=>^. 49)ʲ=L&-JK׉INgw f+˗}>gQQ`gH$(+߻)**OLeUOw`(_D?ekcehh07}=()- ՃH$2L_̴J0N99aiii3+>yŹ.af6V&cVSSO!B&kki$6|~BHf0bbP,L"5<;'7'c^~1DM w㕭݋FI$S'G*J ;7Ɠ}LCCχaZF݈ƟRpzvJ$9+9!m-DZODbfZ_fjzs5+L^]u:#CC{fN2u^Y)l\NuΈi;n.ҒΚ1ʹ F9g^Bp6l3t׉f /B!DJxub{%''1/R4l'K&O!8iPq s[Ŕ>&":G"DBH,oW]ɕHPUu+ e~/ E1 [0oטʿ,畕?cVb++E6 Hjr|cNNfjxRnܼa*)-h/<^tQYYIڵ&cc;p.GЯɷ=ʘIIYy9sFSPPr$ !4d\(lB::v\ޑc32$IEEi&UWPWS啕UTw2I$"|'"Ooݾk=~k5!8q9ˣo mgb`gF BJ!evN{۫ K"BvӧL>'5B8MrxR BPƍ9|b?<0qNq X[Y&I:i°7)aˈD룘FBf a~WMN[)d0cF 773m))Cf <!P~ @X&3[^^_^=:sn߁Cs~Lۙ<}ǻLL᧨$ S-PTZuu)"(Bpm}\1 ۻP0 {JGך@ xu[oٸ[cXz>20З<%-EWuMMEE2H g"~aYu: rʵw#!%6XXXX'C_?)萖2MK mYځ4N* W|FyRtX[N6JG7yy9999$ _ѾÛdP8I'GNO>KK`1ZZ6mHb(*@+kz INoO<#gN65es/F8IцtHFfY!'bsԴ9F]>;s©gO? ?h93wkN7i8XfF`RRS-g͘8da4b&=ulCCcFbFPUȄ{u6j(Лg/_fn_ %''j(}S`g;i֔kjh,]/yyz;AwOBdhux-,[h] Ri.NƏwp51E4+*׬+++'ɖC#cמ j3Mi<f3454 ŪѷMMM:3>~mM! ƐZ͵RYmߌ!͝3=!w~ݺvֵK[GfNGk 2$Y!Ȃ @$qUU]0m/Ch׉m|WpݛMp @dH C,ȐdA 2$Y!Ȃ @dH C,ȐdA YUBBI[AӹQb{"#3q[G9{sFՆZcxq _P =V>sCIXCJܹ{߇tzaBʵ3g( _yUUՊ 'wti'+++̙ۙ5]]MroSB?XQkQTQQ1w)z{_.'Գg8|ih{ yyZ!~,[8~`;eZ]]}-_zM/^v1 22 =@jY'ϞkxcܯύwX[H$}==|TOq:94}뎿.aNKO/-jii>}&HLۙ|>|!r8E5Uղr373m\S$E:EP(]}JJK>sH**+}l߹{AݺvW}wZ!JE%¢"UUBb4y̹G1kX,VǏGiJIIO^F )wt1=#sP--M[l"HH$ruq*,*uX,.(( !D"]]ǎA&SR[ueL;:ʫ׮/ ]zdNoϞ/_za̢P)lwٹm[(+))+) I3*'^&@W7Fn^TVV.Y6u_7)s'FE]>/ srrW :> C(**ႂBCClmIT*e@}!֬ۨ*-=e~o"//on*ȻХVDk>[7762}+99E_OHqOwS'^͜3oPpNWWXf&|~@ ART+^>h93w4t^RUUmMu}$٩w$~uSSSs9MM mҰ[6;yxgM?ʅ3#[bUQQvOዷXyylwƅ."8%5 _<k/416^<\,a?h֮ų';{H$o/;uBII)I"n #Bp-wrܩV_u5=y|1ccv6/⛯8i۷L4{8-=ȁgOFȓ-YKKԴ]X&+D;y7a_ҹLXm}wb1cĐ!J<ЭWl\L&$>H$jh>yWvsq6240v0 ޭKԍOM^Xn\a?Fգ;$HF ʲ}4 rсJGf"-BJD"beitB**xY3 J^@ 2JRT7WðkW31`q MV۾s+M[葉.[n&L6Z9 ~%cGҕ._υr B~bJWpT\^㦺>z4)鍶a+LBWV?I$r>\#.wxdffD**+|k F4ZL"D[BE~ê554DB.D -N>tܽaKef<}oOa4ufo% $&h`بҝ{ ӽkKW.^ڷw/%6H$F;LPd5&QPքj IDATPֶL&IɁ0ob=|c'-|H$0K eee"#LBkmTSlZB\u }!·:⒒3B$"Q$Zmm}}E3kHy(ubR | |n}HBaIq_fS(1F{ũؿ"8!ǎ>r,2mgcޚDRXTTg89^ ѣ[׻&&~ƌ~KxohhHz&%5Un-K&oxuuu[wpT9f2|:+)+<%-4#` Ѵ]ϙ5};vpH6c329eK5Y[Y ڢVqŲ%'N cz_Aw+//Y2,-\rk7[j|MFf#0W-MMK'H&Fͬ4{TMz-tuΛ¼j.\Zncv&#V0wΞBjԍK$lEn5E\+''hD'L~{8viتS'u^ޱ{_~A@ 8nt^~ȱ ff{vliy_ZØǾ^kQ7ϖ 9c/O'Ϟ/Ys޻p.99yL&C(]iF|~mM-BD"y{yΚ1L&8q2dr>Ξᖑ2}7Z<rl6_.da҉,Y[>{cʡVtSRb.pɲA>]z+6jDgiڍ+ q|099uт׮44Hxπ!A~D%N1fkgnAk2xЍ׵g>'OEg?9[P@?nҕވD"}:! ,[ٳO_מc'Nǣ'K=z{w63+>6j\{4"/[j_מ݂8yzsƷl=7G׮\.on+Wk2mںØ[|}H}G`0}r?,X=_fVØz~d29gw99DZOK|x2!0Μ0YQQGyiBF:c=._~V>O5:&I>zT9B2-#C/׵_vܾso{ ޳ az`0dMg}H$5xb155/;_Ցw)((L0kNEEYTi)CvNzAuuM""O;qd-"O0yI9**\,UTVpTTB\.OUUU>\UUM~I[>u>bGb\«ƕ޽gg=]E"Qb'Gɜ>eU"r fޢ%L],,*9fnOffV9x\P3O:D$=b0ў@ ,]jd醆+V]ra ͱ07>SSS3u}{%._gdh`dhpރ.';v]ze܏4KM<{.|Dҗ;t<|͑MkjjBܯ>c^G"N".^:{ƴĤ7$)g9d2yђe ]8!TPXz&(BhE3s:BHKSsYB̬ays'#TM[߽7IoD#3{IB444\8IQsrΜ691MpށBHH"inՊ/u7o5zhld!;uAȬ5'#:x#'+JlMEb1Bu"hMe3fϛ2ؐ.Tb.p8dh` rȂFFϟ0JCCD&sxT=rfwoWPPwEKN=H N0tc^ իTעnt )%%isNW/գ;BHOOwzz<BHQQqܘQ'aY+WEYQ$nZfMdkyUߺߞ4Iz2qzm']_G)+)5n@ ٬ |Q(쮭sϾ>'&0n4HqC^%& ¸WZZn.ΏbbB/;9z8ex "//?u{VU}Fؿovrt:a!Hf0F:{#ڙ("nݹ;bAR'-mjԍC0tZ=1pɠR_|2jP LP<\aM.˕!_/;[9_d|ĕrQ7pc u?_o y%B( gNܺ~yY{uLS99iOe(***(HǪ[O^ @ LJwgO99w_~-Rs\-0`P ɡqnܼaZm-M^W_/ 32L)AzzBՅBKNsN=xhmپa;'IkoB͛3G8&xlX,.++WkڴxCGxaا!o|E t(Ӿ›b\]:ٮ۰ea,?XA^^.||V.JgNҒb\޸IBCv.˳lox].&'8qbqsfLSU,;܅K)*(̘6ٮє^ݻ-Yba/_h B=pHZzD"?U9D ?]P}Υ+תk0 +㕉Pi)W]Ӵee%RX,jki59I0pM#d׳¢"iIuu4C>dPr"Ntsqj%XRRAb}!$hhgcԣ{L<Sd`H$޻Їl >kTи1#uju!l{^^__ũ~~%!3v[Y640s~s%=RrQHSe%%"xi: N <޲anqITU5ՄBaUuuUU5UPP@ TU9M10ߨqllB/^9m2~Bh.]9m2[xBfk3&3j틗ݹ@ ۻ_SϾsj/[+ 0{O8ڸeaBP_|Slқ䕫۽1BKOإ\|Kuu|%6{He_0Me{!ŷzwFI$u~5F] >***.\ez}:*++YX۵ rrt٦Ꚛ99lCuuu e億rrtzOcC\MMdHkä!}^FR=pZZ|~EIiiMbXb/C<\׬|nQ7ye..N'OWUUed+؍tE>oc@}:zzm߹ʵ~}^G[&)9ٱ=FOr2MnپkID!?@%1 Ξd2:jK$SR[WG<}V[[Ήuƪ#8oX~#k͞9u`p&+/^0/]\jEeeUΚߵ?D nP'o߽t%Kw>]{7p(Ś2hzvsxz7ckcfܻc ,s2cvEE*gÚSg{dbx=~]{s歅 o;K-YPLM.UUս?:y=k®]uPAMJxLc5ԃ 'PNv7zdG{'{ 9G :'~]{Ft !~hF[|C:snp9:=.s1HۻkgcuTbopjO@w7mmh`v}t߶sϒ횺+t/+4@ 3ri=$^@ ,ZbnAВaoS EŮ-xhgTOVz(#3kiS'M7:.7]̬IfJZ~?$c$CjM˗uv&kW1{@ym.#3Fjkk0=BAC@ +-qPǼ1&D" sz~1O2~*1OnUT\BM̜* g!Qe62$Y!Ȃ ?D7n5!Ȃ @dH C,6а{7B]L;L&uP?!!1M[Go'JK-\|U"Wi}ey^~~g?&.@ٽw#8ݻlZѷؾ{r<1!B h0FZ[Y6S4##Ŋ F:ZSOUTTxu&? ޾+_@㕩}ǎ4)#3+%5 !D$ V._psaXP(u[NNN`4ZL.GlmU9wSRI$i;#CUUACYIB|޾|bgkMRVSSQTT,--9)~EEjjZY9_"XYjii^yNjeIo*+h4Y;-MMY@VT\,47vqIIқy[kk6ů B:ږ-0 sA]]]CCCPVPSSU\\R__O& SUhk'Ȭgkmպ!T\|B 43띺Zs;֖+=©9:#$IdOw3$J5|MqqIFf\bRrbM,&ӪB?"XU]3 s3G+wXUU0汲H$z셙i;}=ݲDZOt-cbr8*>^_Xkh 0,5-K,C==E^YP(䨨|k;j |@0{":?,SRM EMM ^'+3IMKM\W_u%x#RbIIIFeeR;cP\|BqIi`B<Ӯ]7ɥ\Dneh;IIr?zH$2|~X,IIM'H45ԭ,ĚG3IKEjj6֖ØZcMM c#ï7J*(,,)-MMMlob1.33̬:&ace^-%ȖW %%et:ײODaL 0w7WF$/%a9ٹn.Aë׉<O,(*(8:WW<{qvyy:6s3w^YYB+o/O*Z]]}#Wgg%rSWWKx%tzf[==]ݿa$d}K&KJK BHUb $U{-)*(hii"t3߾+**VWWo qf[s:񍖦"t:]K_aaOtZ]@I$_YE3vOh(%H"@Q(99X,B5N-3 H(*++aaXYY" ZZzEB*)9NNRbS4#Cwkjjoݡn._;.~ȬHD윌,?//`0fhc&K^ryBHQAC[y99P;fuuLJQA/ǧ!CoiGl_~܅ +/!׬иP]]M"q*իDo/O^c25((kkiA5BEEL%IqI)B)++gY% ^c(*H⸄W^;~KfB(>}8=B⒒j##772EwgT2́۳`ާ!L报ͧG!@uB a U hn~~AJje{i.0y99 MB5UMrTRRwށmg]EF$^"UTo{Kqbvӛ_s;;vKΗ˽w9DZը&JII .?\A`el7|73Dii*Rz}Cz450\Z\t|"1O99jEQ;5j5m_X|7)BSNbnfsmd֐0 0\Pӛ 0\3:idǐm6ӣOVK=( B@^O\ v!H(Tp$iHbtz:cY'+ҍ܅V<@% =O]m5<>>a4ӊ\.GQ`prjqqo[[S-Igܗ.w9t 7`AݚA5[Ժ{y9t/7Eop8.>Ohqlg zkfhGѝ;8mixJKpKEDQ V'0,//Wt< Rv_xFC}+9K,H455]X h4j&1AcSxvnNP$V뚙a2΢URT899,^VCbZ}OYxѯ}KR=`S 9qEa6UU=l4X8j%I:mjlT*x괴`aAﯬ,b<."BQa$55(*Ei5u5PBK+Sxg%<Ù&өT*ffgJrvW8}oujZL< CNF$ɕ$;Qe9a8e^PaA9+l|$p8Nn`JϷi-J),*,\uF8uQYPu8]WC|^mͮB!Oק'B,Z6m ϏbCA8(8AV[hǓ,e%%DQJz`5V49+3hHb)=uiGUvh ĵוK|>pppg+vb]+AˑP88{zҸӎٹV@8c&\um҉S֦l6{zOꁇuZ#Ғb4= zLA&XP Jyp$)z ժg`,J!i5cq$Ta LJm#&a BTZVZC[))*\z6h4`QktNHrlpp8#ôRS> l춎3XZΆaNѕ0 s\[! /S,<|b 5 j4ūh4[20EQ(r=PX2GFq QN_r"ONM8Ywupq:h=33tN E^Df$I9.CiCcfi>aXEnOLEc10,H$To8=)ZzNɸ //3ADF%AnG! ZN0lbrҒgޣ\. ̪33rl;G,|~<(4r$IL&-^o XXr9RIp:M+$Er9 *b}rGQm24Ž095 yM,'Oy꟟{lT1;YsMܯz#:m>=b)7`044RfJ Hvm.MEDHEA uh(/j4]ݽL+uْp8^W0LWA ZC+*,ͭDB~ s$IuZf5dY]=UU[yu)5wf,ei*ٵw [a ,-!RS Y/Lo` FK$Ap8ɸR* /v8REgd9BO(=wAQo` G)J%;wVN98LRF\pIep8:vwCm`P*U=}x\&Vf׮~`OoD"S^nN$=t Ѹ\4ucC>95EV%br58l~~WX4޺^7=PbxGUw;mD"(O$@GnKr u5QPPQnթښ]CcqB.`ɌmBL0Cӹ "qWv< *'8uy'[yǿ/G=;ݽ}} Cҋh4z{ct8]n+!R\TS>^!m#YX6))\3Rz\Q:TUUIC~vZ;]Eo2nögF~wzZ.=gi|zT"ßM6_38_Ņ>םG>AЉS|/H$~WϞ;Frs_w8w>mj:G4 {xrYMv ѱ܌ko<]XK g_@Qo5m#U^_CJקYͭm""}Ǐϯ-.ط᷿~.ƆTVZ|B~~^KOo`+M*+-5;8?0 m2(_[uٟTNgfdLNM!چ?pW^H$^;AVupx9ۜ\{ly}8q6 {#KH7: qs=_uR_[NKZRVFG x<)gCRLIT)`>t (=]斶+7*CG t>vZD:q*z ;A${n7h|Ύʛ`faaa:7M.=rhٻ4F߻_t=#ccE+yi(EH$r;A?/wv8A)r,$$2&_QnP~?n;zӟD,,,,,,,,` I طm#S*9q[Z۟z Bᚙ\\UYq{bR|~>agN-.zUW/TQ ¢77'iӫGYY7?O~c[6}c:Ɔiljq'b|b^I.O( &QPPuȇ?85ϟö>;'O$933{ԙ}{R-,.119Ijjz^0caa^-J8z+Z:eB//p8pNv?& ??;us'd{3e!&F~?{7_D": ./_|I s6n ~S>E y'Glk~?S,xHʎa3'N~ĩε  {mZXo̬\.eaaaaaaa=!MM;z:. 8=khr?R]!436$[B"xW8]ssޝr2^xɩ߿_}yrrꅗ^=:ݴ}vc9w[Y!0,<& 6߯~_ IJ%99&#}R(>t`?'&&+#`z ȺLJm##rfs 7쮃o8zH$J 2f-_PXYYhnH '&m\. ?7'{nέj̈́F*_wFD"Q*EQE"XQΞOo C#cc J"pOff1 9ٹ9]cѱ#D":ej15=a+7$sn 3TVZAPӹ xZ %#Ǯ ^ uR((J斶4 ȑCHqi6O>s]u9BvPWLOijAhOL..zjnMή ðvTa2.G"/^*).4$wnD(vt\7dwkQ B]JUz>FLKSywRd^^s ə9fqi F}iע]bX 0.6,my`H לEXXo1 kqE>9Cffgm##$I-.. rZ ?ӻ=~ Xjc(Y^EzVRpȡjim63Gd`0NK#IjfsReH[J9N4miXX?2:FN(/K~3ͭmFy-mCz^nցWRS dzl#e% |hNn***bq\VaFF>/++-6 >D ,-!x#퓢xОl.)'% IDATs-e9].õ}~(DRS3^J~V޹5;Aڊ Oi°?;x`@ D"[v+ {0 C((+-0JNx<.7L&EH4Mp`1=+32܅8 ~2a3sTg6_i3ېAP:AhD[j՟zjAQNr E"$ >yu+I-%q8`}MjQp|WtuLxy:4ERQaQ)֡aFdON;MilH.ff(q9ܜܜis>~͹e2D0ADTo_ B*Q)N7h\.WTX ^UYѨ!۷kWD~?e/WaaGr^(v_ZR7puvvǎFcVDl2%*S yE-,z|~ T*^Z APgWL*=zEݽ}=}5ʊrB(8w\0#I ZabѱҒb@, CG$Rа"%mβ'h ).jkvmRj 9ٴFg05=gwitfkfoN˔ rl>thF҉^/d77k4Cerj @`0(Ŧ]UBˍD"z`H۟P\Y*ðaۈVIf yvD k0N p8Y)S)9扉lsX,""1FxLR Kͽ] *-))..\\R$e2^//7-rKiJS[T𫸮Z,D"v|Ofݯtv ѱYJKeD*ZǫkP ߷Q.8@,ip: 1;;wp^ C&1{Ue}tcc\>oʐՐ]G^|cGVzErlsa@xu@ A|OYssaۈ,j"Q!}iɏjö|>X$JKSyJ3ң篯0s yV(ܩQioIQ u5tt*,zٜe5$@A~. 0 &,:8L+p.׌Ncl;.1&1dʹCR$Ekt}wO/tߐ#WCz]>1??us < i*U8IX,v8yN~օ . qt)+-9wbeϬ?hx qBcMcǙ+1 LFcr@^n= ~ lqQaaA-*Đwflqݹ#㩫xC]ݽ{[(o,>f }[a_A ߞc6hHo%ǵrɜ% ?[FM[zh4 kW³ssJ"ٕLhZ QvvɩeT\VCbZ}OYxѯ}KR=`S 9qEa6UU=l4XVxpf5$I65F*xRtZuZڀu WVb1tc"BQaԴ LքB -LqE%5"D1,y 33LSTΖ\hvT+EA>svIvke=e&Ir9I"Dqt IoSfe|>n䛳2^6Oi8r'7?0XWSNTymAx[Z4J4g0<*/77/7Űɩ -mG)ZZ v`ƌR:z2ixV&B8(8AV[hǓ,e%%DQJz`5VH$ZZۋ 3LP(ՓL" W='Na8w؈wu" m7 nUZ)kA{w_hIKSYc62 @p1kqttll)`Xpx<^EY ew=GԓTt|gZrigL&s8YYbiI/^ͼZl,7bݯ氮 JN\{z]JK zzb(隩޵Ա Ìӿ!80<=혙K(F8aPuݾC>i[WhJKS,9oZ\TV:2J$bB188D+ t !聈V9T* HՌikX,NSm6 p / eRiYiIWwIp]X\dfdlhaӯFv:1ѐD,32L[MSj Xݿmg> zv+art1B0_X,yb-&k&122hhWo_h(eaFEQv{¡r58uC1@ Epxi)HGC9;E_OF&,,z( b^O.cGQDɴ ɑѱh,F%sJm Hh  (r@VVH<1:jiuE"ֶC:83[ubf.~7Z,|~<(4r$I?@Qe2mz}}V¢ᨔ+NxŞB.0A3ssŘrQaL&Mq/LNM6$kp]wdf*-kuգcvH"rK=x-UMkwYNn(J$B*-dn筓LFcEyVeNzkfx%0zuĩ3Gו m9+u_qyVЁ Ghsk{"yt|s$IuZf5dY]=UU[yu)5wf,ei*ٵw [wdfGKKT|^OIqQɣC:Ip8&qKT_q"Mr_Qn9{Nh@AkzH,ׄ^i{I Nᚑ5ҍ馪LbmH,7\VS2>AQ6:q Тw񭓧 SA`I (0phq-2!$4j]w~Kq hikY'6Zۇ7gal:=ǶbaaaaaaaV K*’ !jH,,,,,,,,3yM^+3O}=5:fq9Q/O?打)7,,,,,,,ہmH欷`|5U?&WrTSYXXXXXXXn9y`??g荠.⢗#fx/_ըՏ<UϿRk޾W~̾=Oo<J${{@D/> AzW;p,D"a}ƚBOoH$**,OFΜ; WNpilHIs8] ufYXXn6/_} /G>{ͻNnh喲4Ur@ 8o['O FwɧT*eQa3Os-=[Z۟z Bᚙn5<аgyh4 80p^БR Rɏ_?_r`XyL(l!-zccK IR$7''dOw\ ‡VD0d; ^Y1mp"QNlκṼƛ{h;+71H$4juUe@ Xh4j_(R$k4r)WymΡ(XWSɉr=>dl,XaXW[D"sM"8{YV[$IX@fsx";6'Ɋ U*%}jj1=XD8XP(+ r~aqf \L&QYv#1ƄA~~9+C`4B7qFLv`6|[!X=p0JSӎi x<^a~iUҌOL $ᅅEbTy_}V o|Sي)2teef1Xy<^IqKYaqqL&MILq7ϯNNolWO<}&A(e#RLFB([ ÷?_N7jj ~jN93APaA/^g?s? 0T1{qQ!b,%y'l< ]%E;*`[Q4EQCÕ[wvν @.wuKuڻ>>6אOn*//Q^XP508oOP( \XXnE֡`d0 S>r\:~T";kN5ro;zI\^^nnmKKSx<Dh,mh  nc.8;2RB.W(aPp EQ=CrǏyJM.hNRVJ_u((J斶4 5`53+ʴmxSӝ=Gx{%z}}%-MsheZxyy>5$$gfmǷGY6vkƠP(~~ /=  |?Niß}}ɧ>~2޻H[,Ǐ^NN$h0ii<@wO¢#@`ێfz}ER:SwZCK%fV]欜l3pt:L& C4m6,EQtDі#c$I贺7f4f0lRjJx^mDoͩMEEX,.*,ȨryeF'H%o}R~17BӸrn aORT"D/uv%?VQQ嚝̠GoE4aXd݀aA&p8Czzʣ c. JX, 4]HLBykrgffCVP )q.U&0L+A,AtV[Zڙ[B!'I&'uh(¢R*CÌLɞvDSذ\r̎Q r899}ȡ+sdRxEp8]1rT:2:f0WUuh9ABKY)dKu6=/9W?tw|cf“O<\.Я?rRwT,tmrvuw#P7#H~k ÎD^GQ?>o})OeǏbͭd4! KTU&& (ZX@`IT,zήnTz!${zkV-ݽ Q,po︔a2&G$#7:6Ŏ#c%X, >HDǥΡa[EyH9n5H4\ٵɫK)dgӽx4<;=8N{ǥ -յ5\3}tZOLndsnwgWCDd jomlD!2LێAQ|sF1$͍8v0  @@k),//kWUdPr#^55v`- IDAT%{0 h5ι!JbZ46>a2v3D={vOMM)Ab1/IVj{GщIP(nK S$I"BSVZ"HnU "%,, "I,%EQ'N!IB*&Zt:LL'x<^^WR\pTJFxrNybb*ۜ%x 13;kR[6tv ѱYJKeD*ZǫkP ߷Q.8@,ip:/ ٹ^TCC8a;.W[Fg~RgבC0=%b3_ۏ9m1Л\SJŗ_9v0@QѯK.6WR 2 (sVܜ{6XHVC$ snN{jö|>X$JKSyJ3ң篯0s yNEu\ԨմEf~a7ޤ( AGt\ = 3l#(υaႂd`0Xiiuq 7Ɏ!l:")Z+5{z) ]dCz]>1??us < i*U8IX,v8yN~օ . qt)+-9wbef} ~ ŕp }3=hbg Rܜ[Vxev3f %Ž}Y*@#8W畢h5H$X<>v#"B$c4F1_z!]ҦR)TWd2dVzsGպD<h$*RjʺҚ'ds:cԯd-!_3ZثzhtIkuMNiKKK%bZM"B(a[ŰQ{NypئѨ:1 hHc&FfbrJVKWMQĴ٘ FHt|bιz< C0 x;D\/괴D"9 (#4v|!$y[ T[?qsc0 |U)I\%ÔvfnΐbbIypbxsKss|Z(*,pf[\W%'=\y%EEC]=={v_QtTZ X^drvBPP]m6\3}rky=w=?:vMiI}|brj= X ׎`YLzH$bB188DwKt !聈V9T* HՌikX,NSm6 p / eRiYiIWwPRT.,.232lh4 0v;hH kHrlpp8#ô4|>? $wmg> !h`rt1B0_X,y8ahf Aj5o4-ertph(EQEݞp(\ Nz}yjD^wR&}]Z@$=|}=9+`Hrfi>aXE&x"rs{{=D"'N8^^,z($=smA Ki Erq2% @$ASx\֮Ck-//F&Jqb1'rG}{3LFT2v$ z}I",YxEQokI~( d2E/a'u^ N-`qttB!F6$=wݑa(\+AVm##-e&a݋*˛]gV???O9t$INˬƑL9b+.}N׌4M\7X<>`ax!# 5ջ´JHmD0I1^* ?bi*%]4pF#rstZmEq(Tsg欔9}SS24e 8'&w^5`aa`0H0R*k90Irs\$i-/7'n: Ah\W(-)DöH$ðJPT(8 ٖ#bQYIv{;0УiNQ*4r=K$ɁH$ ðRhhK^am8#BaVV&=63##5$prsE`eX%s8KYЙBAM[xCL0Cӹ "X+0 bE^/EQ3s5F(LmMFY ֡!zcmUgg._{6rw߳,``;v^xwدS'Hd~ȶD''-Evbkl\!Wy +@4D!v? #OW];,D ~;$wƴVap!L=ON(R.mVߋ,\;{mʹf2;zNnvI4I#IH ))6RgMC"#7 >D Z`r/w@7"!FBru A*$$+Хx |uQrZI^8wrC$;$ju3ɩ{s(o8~/2LLN31}[{S.w_ymyy^]^^+種uv[vGו.]6[&>$;E}&{Nkaaq0dGOh;pqi ;E=\.I˂qQoP(6UVYx±@@lb.\:s$J& </CnCna2kk])ʬ@RKK VT*R3 HL|er4deV3 TUeEnN'LkuNSҒb:|d^\Z Fqq:/`2WBeMd^b[SY)MkuV]UY n `&ZZ{h|T$A  e%BNtl8R<Pmp0Jlg3׌OL&;Ij+k+(h[聓[AP0pl8b*%,(֖08^RW.\^%/}s)6md͹Lku4vCSU!HjFcv IDr`P4/'rYfS]]u\&k? q\;3{h[fd2̃ qfiuZL{%66J99ig;Ģ! R\TX,zx!/ښjFGJKPÎmE̕յᛣϞ 07;Nc8>42:F@0844RQ^P_;7@Wf\.7!br&4 `\| )acmĦbX,G"!6nF*$觟{A_e~Y EĦR hlbRGXZRlScÑq0db1at:JE%E \g>3YˍB.TU'?n6;NLWP: _IPd2)b y݂R!TUR(P(70TZR<7aqTVS]|ezTJe: (‚[JMqlv2*+~tu|>F1ͻ.*VUYR*Sty77YY^qp(C: `hf`6[ d6htjZx0r8 XrRICuf%77JhNNNl?`0޾fpJKGF`8}lFo(.*0|rjq.sP-ŢR E. l%N:.j*+9*n&#nVkWYqQqq)_t:pn~! 1ť֖&BYhk q V)) BQ*)YAA$AfrajX a q0 c2-M $r||r EQAƥi.:v 55P'lZaAZ(Ah[^vɼnYX0FPJ䯮ډfx<.&λf2di~.hwlχ X,6`0 BŦ$2ɠR`P.*)"Mx٬P(8 C*UmM5 c ƺښP A%nb> ALf<&^9>vW$,--3Lmi4ZgTT,bfDUeyPhX<1̂|z&! %ꥥ|uʔ}dw-nP sE}pWk*Ipp1ͭM cn~ajZ{qMeEMpZ..1ɩ<^EcbeerdJ8b=yjij|G fr\ozo_JI\@yI \a\`0(^vr7OO<"._׾3'm5P2{ΎD d2CmB!2%Qi`0B.^\WOpra*zH/ X0.?w6 $+|o}S ~"+/~i?CĻ9$8TW[}zJ`羇a׉M dTri4wke2iX͔=m0.,8B" VVpOQ`2ak?Qp8215MOǓVaFP4y ssL&PnTUcwxj~j@0 %ޅDwa"pd Lf7 )rUt:h\fKKyONCtGH(T B3M mCuZLוpd~aa(08>:>AgJ; ^0̎ON5c  FA :hpCJC#D "FaTjyYiYieXScDE\cCRm^DKKdzC#-͍}ݵ Ҷ4Pg8rq++-5$WxJ<2 IDATẟ0oyw[m :j\\~O,il5YT&:*kk潦׬[k&qY"HH a^l'zO5N+ew8?}M ӟo8]E?kjZf~IBHbW_awx;g>`SJč'~oBtCϾ26>rz^g2?&L]'~32̬>dヤ#K{!zS?,Hoˀ`p\+&v$k(λb00 8Es&M g;lǰh4pB!O~+B^P7  "Q_<7;[O~P]Dx;LxCH):/F_nnN,K86t:-! Y):*MP(营mמddRL*aiG8֖<İNEA"a!HSC=07O8'&BյuHaͱqQ[]6VMNMۃC#E=lO;Qehr'SVH$L~!ɶlnTH`FRMbjU*v fslD!%C-BJql;81 !z'i QKt:tffvlbhkK" &z@ ''xSڣ-|>pr;H) :|¥`X''{?oӔtHtw2n&`brt0 STE\L6Cbm2ox|nn^*ɩiJY3 ". &yuTRTD">nݑGDTV0htcI UBBRbars(#RNv>C(*,;ht{;l\\Z3@ t"(ax̵p$bcNkdVMl8]HrBU-raU8lv_(rssp$eRA{=\dFH;g]08NaG<.{Jl8]T %DL&bF"r.7j }H?y'cؕ׾JlcĜ#xXhk7+K| {?\𥧟K:}3~BN8=7~XWwϙӧ.[R^xypx11Qxrsk5JˤM F\4՚l2mkg7^qBj5dl6]*UMuT"LD2󺥢|'0V7s BeUBr'od0Bn90?FtzqqðPa\&MƑL:otlf_IiqqԴɼiO708ku0 ?d$7'[nn"P$)hArm/|Q,P(٪ K$  bqHHU ᷮ\vC&5n}ݢaP hmndʊrnfuuNdgN[BAScD<ѨTLJť`(419=1R*.*(/*,B=W!V2H=TVX,a> \-MBfg `q6UUQ.50:19 f36ܜPho0*RXq3Eږp889|b7z1dIjk@MVʞk7~HgafČvr8f2%8oySIb*dfeS+vy7S;}ӧڏ~O>=pʋ͗8}2e#~_fgxǹeSq\ e'F?J9S~ۿIyZg?/wͷ.o׿yڍޗ[Q͂|ѱmvٟ>vm=bZͰ n~|,3>9uHϷ?8XY^~/^|B+WxΡ5Ӛ|h9~B$9'CDr[#IH ))6R.O`0ڏxKo{͐m-H(s9z}=oSc}bUlv;!E"G>:'HCAReBk?2<|@PbrH0y.ݥ%) $ {q';HH~"Ӹc:k&sScz>9p&9u•k!6ze LfK]ǏxzH%mUJ.E@de=o`ڍܜ3jcΞ QflΎg475>貐52YZZ"d55Կ˥>?_Og ?\&yXp*5 F_&Ǐ{駾o/9LtqiEQq^$$}Oڹ?OP Ce!=<|퟾!JȇK]/uosM/n|y:> aTH>~Ϟ8{&͚$$$$$$$$wwAA*$THDBBBBBBB HHHHHHHHR! I*\6h4W^p2ΧF{Ѕ>$;$ju3ɩ{s(o=kNLN7̀lܽbܛrebn~m=CHH~g+-/W_+}tuR]W^tټnI`?&! (}/0߫w\ >a\. '[E58V/(xr]HZzBY|Zw[b3uҙ'Y,VJM777axrr=Y[[-HJ Wf*ZZR\XoڤR hEb:~--Ѩ$+`dHlw8&&*+rsr=@иqFSZ݌`2e%*2C S7BZ(LMk6{4mmnzh!R4㕗DB⯕յյ@0HBD(i `T*-@vv03ޯ{͚ɼ m3⢔oc/.-}MkuV]UYA}Ev`5MeEb3w .WbK_?'?;-Ŧy9ͤ MUaȕ:;Nh)ZQ*q HDr`P4/<2ZVZnn #" &FQx]MB!7GF:;NTTw\=3eE(.P(zyRS]%bjjmn(ysTVEc1d"OuRKs#qH&yÇ<榯pH |_o˼n`ǩv LJGVYaڂ9TwV7kr4UJ".GXrMn6,<^o,de&Y!hX,G"!>`i7B"9(~/]P\TXXOl* &&yyā%ņ9Jh ‘q0db1at:JE%E \g>3YˍB.TU'?n6;NLWP: _IPd2)b y݂R!TUR(P(70TZR<7aqTVS]|ezTJe: (‚[JMqlv2*+~tu|>F1ͻ.*VUYNP'&8nn"|/xP8t̬l67ZSAl^[3mkAԴ`8pѱlÇKnnv]AѨ띜:~`}ᔕ &2Xpx'77ĸf2E1I%`085 F-7a8_ 5Y$vTJ@^^qiظ?znAӋ A_\Znmi"?W85(ǰlBP(BAVPDK(lO(x<>>1YS[Y\Td\\ iv`ɗݺn wwv?noYRS Y}¦/BAe'&q9Lܜ055?15 pZZ@,xTjYIq^^.`br*++tYHTf><|s_p8忥P(S~PN\% :bjJ{xJn|>\voo?V|-VW"7.:n %ʂ htl8ct-dmM@OX,68]գ- epxFo_cc}cerJT(˲6S,ٝA-MouX[j\.wNvΎH+H y3>`0 BŦL<ɠR`P.*)"ⵗR*aO'ܜl:NM ht- ^&Ų`gHm TVR]`0'OTV$4i풀Ouqz1b`8NX閦Ʒ/^n?~Pid^omn.{pxX.zCiIqmuTH_V%W_8׏D"~|>/YP CD3P4Jlb8˵ZmzÜ2, =z,B249+$Ko0vY,Xdw8BV*m@aÕ=׮G1"橠 ?9dᛣ,#Epinj lhjuanPHBa.))L,JR7'ټ.q67#t jFȤR UT'&#1&<@PKcgA8D~$)$6f2LJK<1D4*haQX|~$v\WV8l6Fq5M9 fszna(.$ʜ`L; #,Ia8e^NNKZliI:/e3X,3QB),POkg#q_0.nl81ð"#aƆ#zV7+rE4{h:\N$ᄈCJC#D i*Z^VZVZn9X{4! :^^V G"ٖ&e;H b b޶` FcQQJb/xJyJmښ91{&Ym 9V/[HD'B̧KyY*>O`0\j㕾ֶ0LI (λb00 82/)5)H\.hnjp8fC 8Eф( AoT {D"X$Jfgt3zb\>T=,@(b0)EU͉EcJ:_*!t{pw(H|붪E&ʤ6;+Kvlmw(J DPt~X$}鍋KV`Bg3X,'~cG[}M{9cc#򫔩9Ybq4uT& qrr-6y8a7 Fmhfvo ڵei Al6{'֖a0s66ۡtν2%wfĘ* vWZqS!d}O')0)5>e>M'Ť0J%|[[d9t:-27'd^ܼT"!<^S*ZSqgf(S(lU% KcX$\·ʊr7AE"akKP bYC qfUUKwt? Xmvf"Gϝ>NI{ PwUU-Ҷý x77}É)*?$۴lJvAǏ=qܼذSw Ie oΘj3.'KON(R.mVߋ,P',shmʹf2'tabr A"t#-K+nX$=Wjj#IH x]oGD 0 g"a~!!0 9]΋KKSA !l6{<"!u A*$$+Хx |urWCKZ}{䨭Ԧ[jwrC&y$$$$$$$$#I!Wy;K}sg0l'fhkks_{;_]ݿym@O}VUVOL'&k_JJ>W, ?K-M$$$$$$$$#Bp /bؕΞ9M\q$AJ>K}}gN*W0qd%ϻ{Μ> Ay˝8BQ~㭍 kqLƞ!8v;/f-,$/t:?k JM)--NOq\C=׮L&sgvoAw^ϟħ4#Bb0Ǐ]ܥ7̽&%Ħ ># C>C$}O|CHr}#WwW)wE;7^34+W1 ?Ν=8ud68?0so}Q78bجހaN/ϏD"w$$$$$$$$#WGJKK$O Ww_Bཏ?vPHug{>Gh4ڃ.==w#h07}5)UVZrsB]W>c7Mmmټf>֒bT1W_/(?vۣ{jk$G#xY#\Kr'h/d22+$˵ar rnpj?A4ť%o+DQ岽-zBY|Zw[b3uҙ'Y,VJM777axrr=Y[[<ݰ27PҒ‚|&JLFC,DKƥh4*ʪf0iB!ñŮ(OX^֖08^R76Xfk*>`|bҼnIعN y@,TkWi1 6; 7'" 0L緵6h|T$A  e%BNtl8R<Pmp0Jlgsunnju3~dff Bs 񪫫f3..!bX)is,Jqܸd2(BǏQ̅K_b/|~r? i/RlښɜsLzTU.\ztrW ɁAgqDVZU=HXPiV|an>vjp|hxdum-_>yݲf:PeE(.mVo0[j"Q,XmCM"E7oj4U9٪h,xLv".\jin |PѲu^wss`pA`o˼nM4iLksP(XZ^9}=s{oX,YZB1txvFPT73rqS嚪J]=uMfǩW;)L&>VXa`0[pW*䚪J  JK0,.jLoJL\޾RQTXuK)nw\\Ue@|:Rvh4yEҪ*ˉ~q:]MaV>q ý 5fVk JQ4:5x<s9`hdt,9[$:Œqe]WzP4z''Ng0`zo_ks@ HrKyymac D@O,xD^NV)Y=۸f2E1I%uZCʊKj^wN/!DHp8q_\\loo[gNL Jx}޸nvv{;BH(6 yu- uu re/4>1A597';Qk\S BX6KKi4an>E"u4[,RQV0}MkuByyBb|r*ÿRD"9?|oSwJr 1K9tzeEԔdFG|޹3~R|[(,-8%E8o8]t: NKA8=q ǰɩ[ښjXlpx$'[,m8@p 0  ߜ_(loo|[gN$zzCM&I:h\"R0m66pRj x<)BO`euhk BXpļn* ²,.-4y<6:6~T{W A-MouX[[KrsvvD"^DLZuye@  Tl @@zK"3 * 2R..*"P ]TX7|~AܜuE73+ U;hX<1̂o Bܜl:Nۄy@ |G"KKL&alvU*+,@0:>QUYpTo(wfdX,N+x"Arb6 O13H8zF,ˤZ6 ES(ߎHNLrþ&1705=y⸦"٦q\N_Mk|UA<X'O#6n㉞jc9rsT&)VV׊ ].nfQ ;]cGZ!W˭˫CAR!~qHZgiu^.D|W_|!<( H P Nghp\k9MUe"Y0r{L&fxT*5>Y0O"Q[K3 hd0pG(hmi2.. S݂G"nW>oyefvϷ2 +K<6>YTdY6}cÙ{lmmO?Jt{S%#=:/ q8d7|r) @Էϰhi*b~3KI~w*X,f2%ұX,B{eR +dAɼMt$YYrl^WVf6C*$=de}Ͼoyp|^$}/pews8IqE#)^R+1 #F7r4ͻ24K,fJKOmm6FKX,qE ᰓZY]qAK_z/*}vb+g}uFe|~Scij 0Ν=;<VϷ!>L&5% Cлtʊ!B^xD".)[fl̼GڵH%SB'0 +`YY)!4R Eٗ9W%䝞W{fY;N$L/ZQmPT:&vyiǙi4:++ 36J5  @P" lEHaa{ijj]P(k;Pl'7MMwvI ð-FC$ 3D"Aȹ_EEL^ǫPsqJKKּ@ x@YTtx/{`p`(wWTP67 ec%d2\;DʜP((*!mJR38眯57{$`?fvmrw..mmţXvNuͣ9|y<,+,\5LЇ?_ҨG M ťT*d<^ߔP_s>P^"P&XxPƢE3Z,!ҨUsy$  N$A6D۫<O"775OL2;4mR}6HpYkv rRdfVZX(DX׷ kkXLQac?DQb "$W̲{5$p̪U*eu-fDh~lfp7ȝD"ё1$))^ zAE 9]oře-kojT:=e6Ribё# nhJTM땫hxXGI4P(<|4Xmdf`T]U+׮\]:T]UO$._AP^iA xaB%1(}+iZ(476wt׋ Fa$}n10 z&(-+-=P7ȡi¥)+-.u:+ 幋U9NOWǴyc83NOnVZtWy}'O=ڥH$B@Ӵ{uc{ςX,Njs< J467ONi@~$ -V #uO"754<,;{eŵrguv{g/,.Af?${twday/v6z]b?{?6X@*t (>!Ng瞺/0|ښ}Xr4MeOՃ5SĶBеTx,@TsOOXlPOS'RP: S kCbayO|?Д+ ^v]’XXXXXXXXa K>BbaaaaaaaɇUH,,,,,,,, %V!*$|X’6 K!2/_y ={s/~>B=r `%ɘ-3OIӔcvQH$ͷh~?R&MSvl8.{`pxg3<2hpt榿p@0ˏ<,,O| IDAT7^_]\\+n]$r!thB-W*b孧3`y0pP Ł'ű$cm?E>\p]xcPHFA=>!EE!_:ZMat--K1h5#3֓ǏVH<_9 fli?zdט8ˤRcS#nΟEd+uk K}'O(w45a2uwvl'G'Lr= 2XkL&S/_?뫫p_\Zefw4)Ay÷z 9gn3KK<}Ljo}S! ~æPn(/0M FMuJd[#KKUXj<~L,#"rſK*n?zDWe2W5Oe]OHԽɾE T3} 0PT%6-5眿_ P)H.Xy쥐2J:iQy5tt K߮jB{Sl֋9"Of2seq9 }cFԫLjC?hH: ;k^eؓ?ڷ|ޏ¯f`tO ̉u5vCe,tzz`aY][;EonntűVQ01iY@(yiXMSZG,k8>{tnnU++ @A4D"Dʢ"mv{ui]܄ H"UW[㘝(Rִ4^!NC:]quU^-4^oM2pv ._s')Tj4675u:iJ$mnC0kjhK}4=8|+JB:~Ќvbl vԴ9 R4-:ڏѱd*ÇkkeeP_wUC!itI nL5i |>\(//+{w *++b4sR2yH$b̤RiDjDx<>5m G"UtD8DD.ԸkF8AQ1;'0 _Xb?s(JY=8U! 8/)6ʒ-F"Ϟ463Uh, aH0 SAH|>/"0blF$ȗ#.;CdMdz$INLZ[C3k/165 |c[[5L VB.Xm6bqy%H\|?ֳW41iip$Ê]VZb. n|!㗯^om1VFnD^GJ|V_|E!WE3st;ʑ%R.V_d}VoD~k#'4QFj1πC I9<'_mgA?o+86!SGڸ7|$+ Gj+|z:~S$ HᘱXL;jh BCѦƩ)龓yMH?H&E"a^Dqw MQɩ; S[kL& GFKK@QVpss`ӧ`5r1;H&H3I9v Cӹ(x";;ty5ʊ f2H.AGFotvwvu̺be_XJ$wl|̙>nCjzO;ƈ (-)yt߯R#`s"a1*6-a'k5jxl8zd4]S]-I0j1T*%J[M|>A,Qr8Sxw8f&N dBՑdFn1'ƶx<~v`*7*3 փ@*^XXx"q<˨NWLQP(dcMV0YF!jk`afrҔ+Yt:e8{Ud6/] q Ҩ4E3TW4i(j @W\.3M~aÊX,rP(\q|Z&k 99q.<%l \rlumn|!tۻ$W۩@0 eB={{w<~*e0jD\sl˘?73ˆb(Ae A* 普lr=Ko}}`p^XLT}]S $Y[[cڮG ök0i\nVMAw-]|`arCPMow* vuumxWg‚Vγ -,.y. c==]R t{ldĢ"̷BrdR]tbtwA&Re03Aҗ^/~ J~k?aX,x:q6[a$X7560 a3Ȼj|]"(Jy^>/id2YQ" (ABk+++ yB{H"-36f^ #O)uNP^~s`])ܬqNvnv)v*H$KvKmGȽ_ZV"P,uM$E83:iuVVflvJkDD9D, ي ڦxw$]%Idx0A$I2d B"0JX,Ɣm2?E(2ȵz<^BCTZZp<:ӕ8z}]vo'Zw EQ" k/hd2?0v͆zP#{] K[[h,_](FVg^ߺFB`X7ӿ>* 1 [x89뢇ֈ*A#T`QT fN()[Ԉ)HI}SŹNf?4ƙJ3W=^7>>?Kz򈡩9,x}SfKC}:Cy_3. e2̌yXhf V+<b1Aj9XBaBaI$fwu{Iɝs%u~&nM$dz}$`dsun"P*Js\ WSE# ;+C0`um-i5b0pbH4\LP\ӏ^b$I:jxz]Y*Xm?H$hiŢ1L (//9T$YFIAӔ.r:izv)D"P(vE8/,.11y<1;GRT(^]󔖔(SaU&o}=$)ŠLom74Sz0Y\ZxYh_%ET: H&@@*=(N{}> hW\{龓}N:a(/銏TVΝ=<.-ї`MSޏ6 ET*ͼ]ۥťe\q5LyKKZVoCZQZtk5I{/KG/k|{\[2)K ȫN↏4:6q,@!ӡq&ZίaiScOKE4~P ,+-im1ޗ< gv#!Pm16a:>r]CngQˌ4M #/-{w.^*[[*)롢jܫk ێAG̖ j8 텡L aXMub;uf3 55ՌaLzuuvM.CZsjZSSevWME $S) ]FJKl6G(G |nl2)!Q$ HIR5÷F,RW|+,om1^~SV)9J4-9tUAܸOєJjkk$0 uuuCRT"mo?hGw.^ainZ&MosEѦ v2?u456}60P{{ վ4- `j@RBRrm?_xD"?0DQA*+]R|@,..OBCu;(&$U \&;:95J$qwg=]HmZi^]~<h" MΎv&X,Nju*򊋙 0D¬)GժW_(>t-X7Ogl. Nv.R#Yn0a/OH$njhx[/>Yĥ+> VV\+.㽏'0i u{EXX\ ~H,^(l=ۥ YǁT*h?:2r@Po}vw}R0|ښ}Xvrt? ctC@OBbyLI 004mUu¤* /gٹԜ婆UH,, @PxVe?^;1zϰsڥǽ*$|X’XXXXXXXXa K>BbaaaaaaaɇUH,,,,,,,, %#}1 ,,,,,,,d2|嵷/\<Ͻ). agXL&c&޾ݩ=z >$H-THԽ%Ο}Y[RT:]Ya0671KŬ6bQ8**R|&N^W\Pc\.wqiylb왾+`>; IDATH,6-5 5ǘP Rӵ]zV!8׾~,6{MuUUeW+ h|d(/gN;znCJ-`&յ5AQNWm)jkMΟBᑑ=qˌ4UL/]~b#gNk`V DDQK$H$,*(fWhkM$[u59"5jMkKst?0WWUujJ6~(I gp:|2J%F19nsS^LD"?'MӃ÷RT z Xn-f\nʊLM EbH<MVR9|ȽVVVʌ.]`(d2M;aX<?-I"Q}]X65jsRKD"h4fXmp$Չ \.u+.UVV0Jh|d2ɤb5O[Pk xjAQ1;'0 _Xb?s(JY=8U! 8/)6bp8!\. XQH$INLZ[C8*l|Rp8cݫkx<Ny<^u&aH0 ST*!LJQcO-՚L|~kQ![lVUYH$.]٫]r98Aʪʊלs>y<^D, |W\Q C1;jm Bu+x&J t꡶VJD̖\*GܱE:~S$ HᘱXL;jh BCѦƩ)龓yMH?H&E"a^Dqw MQɩ; S[kL& GFKK@QVpss`ӧ`5r1;H&H3I-l"rsQHD" wv-pj e"(]= l8^]3MuL2-H<^ę3};\ ׷>qm%%Ϟ?N7U*X=eqi @gH0 ֖D0ǵNW{$rF]mNWӸx}}h .(r9D獎KĒg2 Id2kxJ[[[n'N/,,xt.hddC1($@mM 0 LNr%NvjԚ8rc=0 |5a4Mxz{ C÷A}]Ý}4=19UUY!>z$=~'7P 8 C)Z]]5zLI@GO#IR(݊''f6M3[0 KȻVðʊmuuM+L='~YJy9~r.r]m PR) HD"5G.ݽtilW^h5/We2V!J_`(OR/ G6 { $7)q<'\ѯ +k7;m^b!ϧѨEEfL]mM lkkI&\.7۷ |qg:?@eiy<Q:;́lS44{;q|^s!0\VZrյ졣Ix$yʵW@v;V,0e(j;S> npު=rZW[c(/ Cβʌ2p UiLWGH}wʙkkkVT0j5. MLAy RntN1~vuumxWg]:34MMLP_H ǒy))//[q7򲃫)NH(5M[c==]R 0l*/,.y<9p}'?fϻcuJv z7n)Yc6Ê˭U[Yq. 6$Lbw.^f$IܤZ[K+w5,,y|# /?_j>{ b]053tca0 ;{yw^yݤZ>zwWH$JR |2LhH$ ʾQxW:bmeeEB!/Rk%KJ3/aÇv'RcNP^~s`tfWn_wz^^gR;zD"; -!R-ZV"P,uM$E83:iuVVflvJͺH(LRA0Z91)#0blfn~?xd2LBa4c`јH$<7eQQ&TfCTZZ(=>aX[;nj2ƹ~fCC}278-ǣ+6k^Cd*ܫ5ਯuWCc[ܵ! yxv EM u33we4MܫG:X,w?7;'m*UV*+ nS9byl|/|Oŗ4jCScs~aqi9Je27e4 e|>qi+ e2̌ Eh,JQ43Qkb Z5gMBH$Ia;J $bqsS$3Kc}ݦ߿ (+-glu:fz][H$J%33Ғty5EQ4#o=Vbv7g9SF)PK{ܫX$IcVR1/Դ^k16K%*)s$E5OiI ȹ`4enlE"D2cAd,c6q C!HZX\JR*偞:tW;֖}vy$bƦ?Nɤ?J%{UY"y^~q@xi(Rt:}1wKE!H$$Is$\"].jfH$Ju LL&emH,/|x Z}viw82Ecs^kCm-Wߝl?j^|ib%zK%z}kKZ0e=TZ{ua12sAZM0 To j,kߩuu0?8d0f`ˤ^(JQgwȥP>6>yP~.]^MkjLS.H!5d*eXa޿HYi|>(77, Sf30^D"ё1$))^ zAE 9+g]Q`Cm-߹hScĚH$o, cͮӖ * M UDku`ijl6l<aXte2AV}+iZ(476wt<^3zY\.;yXwc`( Egf rk}@j˘eD`(TUV0$Zecsyz5<~n^%M+-uo'nIV=0,d4rep4^]h+k z;-Vy AD"loϕSkk۷!\(/;{s/|O o/<+i<"HtpxQldI$\ ,lZYq'>äi3XraCzZvday/9hqOOdwҤiA>+l/>ߩg\byJ%GGFnx?NsOM;/%4-нYX.zE!wb` [L]g[ڔ?UHTJ ?KŶ* 幞OSA@(o.lz'OlO|? \ *++^g}#k$I8>a(23W7KKK_xfw˛* x<~3b~Wk/G_~Wβ@0o}/~g"N4_N<p:y<,,,,,,,,{?_Dy ;!ɤم/ǰ0+$Lf(/Y=4| A' #NGcmr7wd̮0=>xʵ|3 ]Vg>Of mm _ӟޝ07 $I?cyYi׶=_o}F:V\ .FRd~~@ՖNP.:;}~$IKws~_ U7vc}ǥR7++ *gE /h5_KW~~G0E?caaaaaaay<{OWf]Ft4Fc2), ˯A!ӡᑓ'czү,,,,,,,yd2_.{^ϯ7ecaLlyҥxLs"D"/oEH2i;f qfyy<#\,,,O_...s֥W77l٫>3_wx<7p$BQX,,-3GFؙS\C8D x8 ZCȮ9mvA@PYa0lxO ޾p p;28|+yJPJd9N]mMUeUUNFHQ}g\Xd2*P[^{% MSAR2"Ѩ#4MzEQ3V&AB$I&Z6~VQ3<2R\.W*4)_MXZ^Y^^يuric-#y?{,]mvG< DP_AP24[H)fKi: @|>/p8p&&&aJcS# ɩp(|QmvIR%z]o_)W?kV\p;.Pz$*$3_ƷP.GNhƆÇZa Ӗt:U]UDij357<j]$I"HYHJ^MZB:}Ֆ%MGM +$؜st j̴={6 0D#``Nwm'y'nqd㸗l8qr8xv:q֊vW]EbX@+@$щ>fGBj09myL3qņ&EO@$]> 7> ɩT~M:zX?ɤ33=ݝO=}DXmhıA$U'7Ν?2jq_^YRS]u`_ǁe$A.\RĤTrU(ODrǴH4`_R ]Ç`0(r:N(${O|_$gGq܅zb"Emj},Q&@qR$I\O&C 3[N'us[ -9ε5>OgABs,MVtD"! 8Fa.]H$MVOM8cf/UT=6`FJH$bRSq2--5p< ۠r$jkjKz^wEښjm!s8r_%ɤb9߇x tZ hJK k#IlV,P0Fg2UHHDAC},t!֎\ym!(H.I.EVC ~-r{,eaz\DY9H0 JgV]UϞa%kaqTWWUWU9' Rmv{D"`akgy Yۃ v>)>u2H |VoxUW[Cׇh0J%^O!C4>1) O8FĤ4eڱLLNZ%1-,2XNd h4FA@ xްW$pxlvtlAFFǮ ]:\wM3RjX\ZDng|bرAN ޞ_>sh`r?|>Nԩ~yhHPІ pp8JF;XX,V)5RlׁSښGFA9 a_S]5tZ2ŭf *]9L&;zL37'YVUV7@D&3i$_?{ B^./fDsHF'd*rdzf,M*A|>O*ON47 U;APsSTcc# Rk'&\wy$d2L&/^f*ess%*wj@ RtD8rMM Np:{{8ն`9z䰡)wL31D)޺Z29!*ͺ\GQ_}>LJWf2jXw ĔL&q|8~'F˯:q\ZT*'?#/Gy (W$ngb54ӧbq A+Ք=yo>V*+A~㕔667RhJ㉄B`pscK TUU)&H1)gH0Ng@% FajES\W)UYbd-ċdGSSvRI$J5)c*^"TVSoMi]yJ\NLD@BkZ ?f,bQҕGocLF"ϧM&=ݝl6{  Á@pl|eZ]GyS's8(Ac7 SZ[Xl6fx.zan!I\.Ǿtm:k{Tt͖d*_u#$RX/qrI$AloG>H-{ ; ySnj4\ɤ/.gĉc)w8C+/멞ՔwqI_QVVVj1 (@ӑx<k.[*x7;i5>W^VxZP$t=e> HJǃP_ C\.QH {?Ϳ+L _;oT*H$dSG{CZ/S_ P)GgPfgcCRKJffj@{{k"`4a$I8N)$wڬ$TJEOwvadj4Mi$s9ø)†pynJ%.WK-}"l6{Z$J["AL'`\,Le΋dv(ʶۗffurJCyPt& U陹Nv2gfW $I4︽_?{>ͮ9\nGY,ւ}*SNTT9^g2J(g2xr(Jd*A,n}5vcs7N44ԗSi(0ҙ4c4 `njlRbyeVHK+n'?`T_bWP foO@ H$n0NiQ< GAiJUUCWdmq"PSVzݿ{¾'6| M#7o}3lmy xnʏQ?PP?7g:GCùNEH$韻K$GΙ-xp9L&CxI_0<^~m}{>R*^Q475WV]nφif~?B_Qa*U K$93%ۑm R)w0 BTJł}6lbfLfbi5{劄–榉)ʿKSCzee-pun-ص_,͗D'(a+ Bu+Uʽ΄abQŸTcegٽ,/9ۑH6ZmJxMZ" I$IوlG1|jM&Slf*DBu! Jy662 I^oV666Lӝ|~&NC` ,A,-$I\Axx2/.Fyp85UFf:N$ť5rG1ϗq @ v=ix< lِ, 0.X#Q3sl6L.a%653$ IRZPM&STvKAP@`X$eY7>]t6% Id4s8kfUٹy/ |>_$Zm $IәƆp[tm{u%UʞNۂbE[ -\:[/\<0;g~$y<ՀaXNlYVڢT(&&tZrouu{yAjUN^+}>Իɦp85ճ#641!/@;;Fj+w[^Ɔ(E]FGo7J,~!jWR) Y30<@zB8($GB.gw)WD"J}EnJ,# o єNY,DP0^ec`````xec```````ȇQH 0 !F!10000000($|VHGO>30000000<xb?ʼn)AuU{wz$ ې>_VC˧?d2yo+N(uD$۳a?ǿb!˶_*ao{-wRdXi}#</7@ Y37_;)m^R" x橓싋v=Ol$WOz >>˿9MA:~;y 3Ofw:{{H %e:-`۟}S?Oo굳M]dVVWW~z_x+ ?G?1>A~oo'?y ~/l_ΑÇ] >?~wƹ|yK2r<I|?T&Vyuxdq=2:pU m7νX,sg`Kyeyy}}yyy9{&).]6WTL?hJU'h4~_{[.;bXw_˯=)/9 WuuTUUw"i+zU__i451o$?{qf>//nI| p PP]UEmv6f'\\Z @ VPd8oxUz3$ 5ɜ>Gy<^^MC "Hj#嶷*R¥bVWURb3F"]as7~ݗKJd/|S#ƴ5/,njl^ttaccH$>>wcqzzz`0D}yII(P_3LgLJ}@X,G,d>_ ohiZ33?t ǿ}K//S$0?x߇>?.ljl#矞K5 $IZ<.' ?P=]lߘ:ԾV+޳.<(yDV:H3>8P\!q.ً-}̚gf1!:h$/\V)Xa\.ɎRӹ>6>q1nO4t8 2v'&Rs{;22:&JDBjFc' $y}tlumRq\"P{ ATULRcA *cGfd2`pH GKbD\l'7Νj5}=]T*eOW'}2A:z{)G垛ŷ; ?izE ^}haN̤׮#R[SW{؅<|>/ZF싿Bĺ}5 f2Zuk)6gRlsKT*oˁ@T |8ɤRA~o۽мc|7 oo{-_g_?^kM«ϼ=?H$ey}zfNtaVѿBzaXX,F,3IU?b`D[[Ǐ "0p8M sښ꽺+Dfs"aX[A&Κ1ryu-=wð}{Ku‚=,\]s,On>w,/ADdlV(g]hjjTR,uݛB_9wⱣG67~߼C;BKðͭ-sU55Z_WKD&aҽ2i4ubyyp`PWRT*h4r*$ ehsS4stph||R,=}d<:6tG{+`iyess$-E`0$J>B. h|bR$@8IrO,Xm 陣Gr4P83;^=.q9iHqʭAf].#0 V'&mi˫}ll?HE'Oh[$P1 xJ[=@Ow'A|TSlJ|b%yT*Mi<] nnlpr:JDq\NY(6>C$a=]p: *,OXmjaavjʘ+Y4<%uRE+F/M^O~R*Rۂnr=.+*xnZ "_~np8Pv{D:& #ðFibg Áaciy3R=] L= xFD:Fx є*^ɤ%2YO"PS~1ݿ`_2yx,p򊪯ru,:PM˯0?.KuUb< 6;;ZTJJ B#7|\^R&#7˓J8NEpfWU- xp\@ l$VS2Dښ5`$I&L&C)H$" ۑގ@D@tEusm,AMzcbᴷ4j{ሄBsC= ɁnťeA\n|Gs{A- v'Q?PP?7g:GCùNoFcۑ206߇z~dj*8ܦZ""0? mrؿ_~>)-帹ѾJOv{6L3 Gg}E9a;_"͙Q(&Z(r #P( HT,)k/-l6d-VVV.+ [&&v;2>ߖ (/+Xlx8=lv;mbhnnLw7n( xvwgc3 ]H$.A0Ţq7(N2ùdYզT((4jZ -bp?BdzَH%bùNѨŶp353$ IR*Pd\DSʺWťem!Ht:MJ@pYP3i4k6yRo GFƣ/xo{-dmͱp:h& 蠇,-yH O M^vwԾW8.=(ΞVI @ko{sXy`t :R)mM!j? \|%;\=Aɫ($GB.gw)WD"J}ETwpH<Ã=t^6_Al 0 !F!10000000($|aC>Bb```````ȇQH 0oa``````(F:~;y 3Ofw: >I3s,SFն0R?4IՈOSFj+~:<{8ړ)9{{ a`Kyeyy}}yyyr7W=ϼsU1O\.>b(&B(TWUO#cۑcG웝pqi).;Zz)Ȃ}qb *z}g I$k&9}ǏxB!E"QԆGmooU*K+y Ūt=J*6hJP猖KtZ!wr8qmass$ ԨPȋ$K̬۳(/ӵ47A]DS]Uihi.^h4zcbR"y2_t%L鷿{gdtlX$jlɤO+kkX A`DP_'쐛[y%X,HX,SNp_5sqi)Op9}E9 JMz}>FZKC* ~kP(bҖfs-Goߋ3\R"|᳟c%vikgEyٽ&rCK5%/:qfffZ M .!OG_b66'&ugRdMu5uIֻBGj+|@^P^'\jUq4{܅֖%(*UW~I3s+$.|]/Y\q\"Pu:'>uARXmhıA$UTJ$9T1=7ooke>6x8HqPS]nysR"e23AjU[S̵++5UUko;ݴ*O#@I2ihtx ) f2tUl6+$ÿQH 1/Yu FFGyT g>$RLez܅P8tz]vR_QU"R)E"a8-/)!rbqHԔ -׮Zm U)Um--34|M0tFS;WSƆX-Mxvg]hh/.&IXf0X6b[ `h^/ aq/Id294< @?9tR7A\[siz&$):bd }NL$Ϟ@0h4p8X^DBjф8at:}md4sFJXw[ MƾTקR8-8ٹd2% Bb{]X>`I (T[m XAz(OL&>dzjADSZAWQX,z%J|~$D[[Ǐ "0p8M sZ!9t&VR8 ~k!(H`-䔱pMx~\ #ٜH$yfIyzL\^]g]0@^䔑$7 kgylk3D")4аP /.9tfZUʛ L&3:vXڊr$˽^noo#IQHZLME~ݟD(H*ħ>'/YSgF"f_AZED mnj2fKm||R,=}d<:6tG{+`iyess$-E`0$J>B. h|bR$1y v'AP_o/N940a\/:u"/ ) Mdye@p8p8bFrX,V,SMif)tmMD"/q|bU*Ni]>P8~D1HFt: 9@Lsl6j[8ŤR $o)|>/$ AP,>βMN-nTŜyTMPAp8=j[0M=rܔ; ™jqI"Alrݱ$Q_{cAji$Jtgyeuff;ZH>dtTVQ}󼥾Aк(_-r_9u8R?X/|<8 P E98$(w=kw<N<^P)o Jx%%MT5x"z0xt:C8З{Ho{:9~GR^O4Զ` K9vw-Hx;joIG{U*e"ZE-6ŨBd>~?T0nwcs=ϞyPy1<rG>wz}E2|O?#^| wܥd*K$/i5: $L_L}%6B6 =*R^R23;W_W[ͦgG#IqJ! ܤVVHS`*k{;r0]d25e4 IanaC`LpHuNK6=wbV-nE0DυTܻqd2u`\.]{e:-Ys}]<4ә BTWgz:ɤgy*nln^vpFέpd2f!d2d2Qէ!g^šNaNR{)B!AQ4ʿT J&XƆu؍'$'PPOٿcfv[VFcmA"S'ݵXZ^q{<=Կ`-ԬC ܟ^2h/t[vλG㸴wd2g`$`_$Ae;|A0L7;VHSFb&(/{ǯۋUً/5ǫ\V2̯}o}[P>oG(Q9fcc<2aMFtŎ0t1DR=] L= xFD:Fx =)UWUU]&IKdܟD"Nb_Gy"H<@<p8yEWT\:\^^Igr6(ʦWw9+xv{]U$9;;ZTJJ B#7|\^R&oX$S))N m6{U~nޢPs͖h5RH$|v$By⶷#?$I$IN$tK^e~!/)I>?מv{d2i2OB"$io5a=]gFqqiAvn>Sۣ`- Eg kQQ^H$^/tJccz#@{pǥh4>u[&bKnF_OÉF=|/Fn|+OP:=3vw`]E~-B$ҟ|XlS'րIE5jA>lcCnk[^Y6 |D27gnnjdX;CQ A R`_Z諔"em%2ټDŪ, IDATj*< -MSmVoj?wҖ׫T(,?ڐL0fh!IҶ`jZX,/ -,B^MQ e:-+ BRdD"B170 Xp8L ℷ1.EQ>_L6יeee:>gڔ e21)pR)f% È1]`fvV&(:6>p8k| `0JKچzX$J&SvbChiEϷXI*0bau5T A W >knlmwڍ011O$.\jytlninaC4mVh4Jy,A>zhid4EX,CQ4ڥY JK-V+9b>%q8ls+? yc['s:a؁?[YQxo?_"?Zp剟}iZo@`)ڹ@ t_Dƥy>w_2|׏uHs t~ӟCpMu׿Ka2_;͍AﺺNFc1HXT,wPl\IT46>aM$(k 7wmij19E5k0 S)ηU* uAm:BE&쎚&i0rqAd2MjuYZ` ];wTUV3=DeBaX(:L*I@ɤX]?0ذ[Rnjkj"ŝDz&ߏaXsUaijtz=Ѓm9K}x* RȡDBaEyYd2% Y5"7kN<-HNFcAp-[NFҴu3ِҒP8|AJ|T"шatL*IO h,񛚶вbcPaH*dh4/BA٬jɊܼsZPc[Џ0":Zl6 Shoj+ZQ;KTZR<0=~$JG`phƇ eA"!RwOOmuXz?q;s;,jKwwqaH0,+!GD6ڊFx]aXmlR  KA@\!Oڿo/9F]TB+>)ȯ^x_odO}c[67}dԑǿЃ9xׁ^g4?aRGwvu;9+IIAҜ' i*+C[q>̡ܴX<6{  Cn#*+ʳAXɉScyRPZ\s t /bA,D~2GϷRAOA^a|GNz;O>,RxE8'c藂׼#X, ~7owg{a B~^ZΞ E5\bz)v [l$EEgvg9hUeFCS>:^|W^s?z'V @d=v wH$s:PGYue"'/0|QQ᷾5oO>O}c+sX?,+zvaq_ƱK**?zgϽɏ*JnL&ϷV^sǶGGw:Ʌ(b_vCD02b2kk Ub_m$pN 4sQY#ǒd*F nc 2Ytr `չl-OoᏯP} }^Y׉uۮF)m nS?KaǾŇn^8y:R埻3x?A=G}P(>戍{|7?K?づg⢟NT>.oJe195#~{^ &Ps((((((n9MYiɓyzߘ QPPPPPPPt/[㦆oTbpu$SPPPPPPPE6BȆRH\9RL"x7:|pž]H$􆑛]wːV7:6~=rj{np:X4Vlw󎹹޾S5YO1Nש Kyuzz^yWg^ͫ.svM8yכCx'G8Ƚ_8sҒRA~۷ܳk'~! r}ELz *&(A6URVglSxw 0LyvX~y<ڙ^c27m_NMMfVYQ^ZR<77/hX$FQ N$yy ƪylZ bT^LJy'P (cX$j`08@wޱ"zF^ q<kuENWWV6єL&+ȝ+7 =}[NyJ63Yr( P  />li4ݼP(2n|>Wpyĩ4w|t.אVWW[SRŢ߯7L&B!VL(!L&hNRcV=Lr8lMmX,Z+5GxǾsC~e,V[Q59:^S]Evݶ{ۛBڮ7cnd/;LZ6N>FyaaPѵIR}UE\O/a"0njDorr< p|ɜ^?p>VM| +xIyEiӂ^!p۱<-.&\LdU,B:W1y;_F< Ҫ ++ͣ |Cņ ^2aqvX,(/ iܞ;,.{z{ܾ?F LSWKzXWSٝٮ27CD*x@`)O,qd6 2M]- pc8J uwL9\gΞ JKֺoY-%w: ts9M ڂ P(P(Msi[EJGC0NNMwҟ*mxzqSC~~?FHw)[ ju9mX+_&ޓw'SA|sILJoqs0PF޵#+A>w}H̙nT(x<Ѹ05=r-T( n'?/Ao!p%ࠖlE2S)Y{!8v//H 76>-޶{' {/T"H d_h27kҙ&&H K.WR\LPťю HOo3g[ZZN//( mTG{Ǜܳg VfAP{[?<x<^RyXԙ3i `zf@`@`0 Rf,$LF B2D./ [ i~x,L̤!}\ .@Yr6 F7էmHY2{N羼D&4`l|b=%K"533[YQaEyHp8-cnN(XS!hFEeR)\p{=77/ ($?/O_PÏ|]$=c:ws4ˌ%"eɦM'NQ\GQK8K%֙E)JbamTDtzbaAqR!q8̬ff-Ad)0$yi)xlwhlH7Lȼ4ƌS.T)-VP(;u5enn]HRǎ}WsRi`J :8~1_cbhl`=Wf OLL ʊruQ!<2Ӑl(%{$d!AJKtDB]nORNlm0b0.7dD1:AP2$m2 (y}!]kKS.oX,R:] Y E^zqHgPŲh,A4nw]hmiZHtqBy74o5F@/$9 FZKYعʊLV yXv NE3btbeNaYiD[GT*yn4kff-Z:N)$5y}'?$?z`pܹy^N d jkK*о{bdI~u:]m'c0Di!J |3gωDBHU*iz tUHf¡0ȪBU2@QzZ~e}.Ϫ[U?SVuET"J$~:pqrX'MGLL% |br )SE"QfAr$ni):ny8mّh4L; nʼnDfSɮs J队z}B,+- /1IzlX$2ͩT*ḶJ|3L[W[308rܤ,Bl x<~&&Ȳ'VFFL*r=jY-EQ4=ŭyk`0([;aFVc=țfsPLJYRZm`0JIIV7P5u|whxov x N&c2TxbaJhtfv[BsKq|Lh4bi.8-0#[xuz`!HX$U.IAF@`)v3!Q\YR6kJ'̣DEzM#i7w<Ҵ0bEkkӫ ݢTX,y, 0, ;[ LP 6UWS-Y1ҽ(Voq H%V@LMO R8FJ%x C!E/QyYiYY骉7,6MT(򮓧1kv]JRF9 0f5b‘!biA榵f0m-I?b 45e)c…(ZSHsC߸wn RwOOmuXz?q;s;,jK;үwqaH0rS3^\"ZØvH wCVvWԾZhn$yM[{{/UאX,fϽ!֑cY Pl?aq5-(nyy:pkq8{'\.VeFDRTp4EQ5FAAAAAqAeȆRHP "zUpPh IENDB`gammaray-2.11.3/docs/manual/images/gammaray-object-inspector.png000066400000000000000000003027601413310443000246230ustar00rootroot00000000000000PNG  IHDRXHMsBIT|d pHYs+ IDATxsy@ H$II% )[{NOظ{ݼٸnob{{6&fT%D{@f Y((QP('3'|~ ^7@AAAAAAAAͲ$ILy#M>ggil?{2^_onϥp8гbǻ LMFRPÁF!28n"RSSǃb!`XX,߿}h4JNNxلDB"+3 Co[l6Pds!?e~~`݁$%%AAcE@׵n)((`޾~*+^r)(((((((<;%Vn3s9i6(,m c2c6jϓK# G?0F DC}n6&7'%%DNGZZ*FQ jx <(VY]uSQQN^^e%%,.-abhxTCvv6x@02&Sj:NwvpTP]UEAt:+((((((( Toj?{!'hf5Ɍ32:__T9׺sJAAAAAAAxcہ"8 𞑲u Hl3#IzMx d 1.3=3( /ȶ~SS 7ѱ1 D/5uuܼuBQwbitlEN8Ɵcc|b1.V+ Q\T߿eJKJHM27GY͏Y2lˌOM0"/ψF`6!iB)2?02:(M 456-)#WgnnQr tvxQY;jSYinn¡)(((((lg85=CqqvS'ZM,G-w>`ÙS\vS'{nߡc^ڔ0cW`|bΟ]hSMZZ զ!j{EErchjl`uMA~>}|>Vfj@~bF%`0Dfu۱;jt:b KdolS\T%%= VN AP]C^:;7nzBAA>5U PPO<|4j؀65T>8|FɔGZ;z% uuD7c{*$I"Mb gf Fu456a-=:JARL]q8HD c:.233(//##COAAr < Kd~0H008DEy9$b;(/}xqZZXuri4LyYYXncn.]̧|ķ/aXh=ЂP+q9J 3򳶶F5E' tPDVVDp$BVf&999,,.Rh &_r_h»Ͳ!xdg'g8WV(*T? $@I0M=I~@m%YY46ԡh*(((((jxBU5 lQ(g4*{z,.Eߏ߿e[ Ixܯ˾rS4#Exn;>=);$#fffpPAAAA#Iˇf_9z}:h>g;*sOY%i޷VHNɾ ˯G$IBE~?X,$I$DťbxdT>.^J<o嫯/Kc[^~f  ѱb1A !"zzjdfAؑ]\ Ӆ!467M026 $Gֶt+]ub2׭>kPE)Gb9 ˵]( h72Y[󑑡?:7YY#Jǎj-M76hlA/~FgN6׮f[]4q-4(aAzZ 8 ^lc/,P[S͗_}g|BE&&(++_~cvnFC URLtQC֐p:9)Z!Vk0svzzHOO޾~*+#;xRZrsSj99,lNtק'86wl˔o' #X 2+f3SS3HH盘tVv,-- Ղ`vnp8mZEߏ餶cn.5UM.G ɏ×HII!E?cn޾t8]x^QܢjaphjgߕnB*S] oH(SxlO_ZR@IIVښj9v{6nHF#XV9ØMJP\pmiқӒe(Zj-mahx`0CIIyYKAkl޳bSh"޵5Z^qӧ:u7W# "͍-~&OYI SRaZ'~ׯ̧|+_']>dǹS'wonjϞ9-?jթm_[e((([k;o+#LOPRR̒͆$I?!CC$Fc.U&&&8INv6}7077OnN#ssMGhinbhx$9{8չ{Ip׮)/aGF)).vn߽GQa]3\~c%0>>2{ sTܻϟG<#?X x]rVgJ:M>kfVnR`J8NDQ FFfrƆ;K DjW›/~1LhrrMJBDPC##j B  gNucG> ˯dQܒ=3FI<`Wp\qKA"|{qG-?}}X,o}RƶfD/ϣDoAAp\;d`ͷ[R-X~b]WxӋ$Iul_!x<뺿 47ѹ  -hn;[whmiÇ(RF`µF~V%C:)j`c6yKl7Œl"#COQQ!38BZjrM@sSW]grj%ZMJJ fZm*:VK*(la Lo_?t>)c# "ǎŃ]FƆ:!"m-Tjn+++47׏e) YxӋbe`ffd'_Xg|D&&,/Νen~ZM #&#CFAVc;x(7my)y(BEnΟKJ¸=Ag. cyŎbf9Mkvt,tjj8||pRZRcc~v`y|ר`ΓYӝ{ٴKAeQlNh{dobc0daX(Fd`RSSfqi Aj~bqb Z X^Cm/,PW[Z,dfdJ4S盉j?f,l >j͡ I閭rZfɶL0DPPP@ii f3ϞRZ\$m@[/`6HhcߕmeqͲ.^z)xz0;;O xb}7&ZF78&E0"'';p5#ٹy>mX-rb{X,o^]Z(R(SxդoD77D'ꨪ|~ _'/؄pX֑yp;bp |Vi`mm݁ [DI$l#7ouu5M&.̬Ӣ ښ4|]#?O'QPPx;QG,j%;;hwm kUh^$|~|_A]dʓ5ބ"LNO㮫8].v?sY]ĎVvwQH$ʍ +r[8]G'h7$ ܻ3+ο_n?I4pgxGb?Ů)%jjY_  Z& t0dea;0MF*ODzFBHyҗɻųs(/-o>AVRdICa3r(.*bqq#G埾!lO?oZ yF#ãcsr"z1~D"ܽp8VnܺMVV&x `l|y;S^uZXZQZRLB !TFDZ[ZX%˕kt8SZZB^Onn]׮o/aˣ1oB ]ZV++vh1 ^wﵳ~jJ׹~&e,nΝ9ЛW IDAT0MJXX\B$:O##cLyh4(//ۋ8}]Ncɶ$Iloê< F323HHc1 YYZƐE,#;; +FFHlZoD"$I"&30(q8#c˯&S,端/lͦ ̒ͶfxѤNxH$J"  w= p9]+Žr7_'$Iڕ/rHlʼ$#?/K'Lk>87;cG5WWohO@{[+*U7+vM thjl`yy?>=~~O'Hnm?]fMo쓏p8$i*o8L2ZE0*}Vݬݑ; ik=/g0333}z:62I۷*W%b^fA/)r~>R*j{{tɧ'%%E^?D"77<#MJ Zl2IIq1|tƆz'*"22qnhR4jIM}c[-B0"rNcavnnG2CVCÔ'ZXh%++P8&-{ү@ہ|w2@5MQQ!UEYi ]WY[󑕕+]eBv}9\F}UK$bꪛo/^;qߤBctl|weCRXn^ARjz`-4HOO'Fq\deeV;Sh`p]kkq6G>8 ?ELLN!J+v;zR)ge]}t __ Y*tQS]E,gq _-ft]w|>߮trvқ|XwC1{9{ԎVK)[:z@ONNeeT;wo:vRfV VӅd'_O.g;CWO/?{(,#l6q`lD"l?pUV<.෍g,ICTWU#C#)-)ftl<9E(ghdZydMueT*99Lp:3ܺsϹs>YY456FeVWQz tb& CVCX-PKXD#QfSrJv Lyy=@J1M=G~fceN^J%۔X,cq|~? I1l"?3[JK9Ҍ=/ XŮ<.}*(<nIv8/ëv))ZSAAayWx^JFA}Ŕl6v_'㰼 }&++ ّG~$108fגӞuEyPxiW> sѾﳶmF:,sͿ Q\\ $x/pã`6}U~qJfScc|>8ak Y 䒑Z;\M\|Ғbt\v?$Hͷ7e`s-~g{܃ejQIz0呮Kƭ&c;VWiin@cZ#w@@5-DB?H>b1dAmt ]J,Oe`wI%6e`w*O$H$8]20 m~Ƿ]̛xBawޓ͵7X@$zzb1rzaNqJD")UGdQ5h@0H $Iʳl>,#cTWUo_ܜl_Xdbr FC(&jdqɆ{dchd>A4 *ѱqܹGVV& TVTp KOLb0d!"&wqx rnncHnT^RDʔ 999Ғ:}T|FF(//_@3u*+ HII%RR49j`00deo&RYQ._AY;A,͡rss0MDQ YYYu\gΟ=ͭ;wq8<~! G'u.L&C0? @)(w69o8Fr$V+#,-8~C6,--sP;|UOv懋|<,37MFK'DXuDVp().jPPϝeq%)jFr|'&% KA>c,Ѩw,55JRfVH$JUE++ /ψͶ,~륺ɩi)Z gϜ]&e?Ο= cO~6VktVVx^- r&Y__ߕmLeoɟ۹fqK[VY[e((9|*9 cY9N:;N084LBHP_G_Cã  QOccC2C('9|H$L^%_~zFFG9}`0KAx^2+ŲTܾs#ZuܙD1f4bm G~pJ߿N{[+h6<% qR{[0nw I$X- p1FMH$©Γ 8qF#{W8|} ][Y8{5ISu.?`5RRR<'0xVKR"ihgph76(x_쑢(2/# xkãGZVdwIyq\QX^Y{XߠRu5[}}f/DJfzz{1vmO+Z!?uorjKA>^NKKc디z(20K6ʃ "y-s$3Zj`[^b)1M&EZ?19t]W+v(s3:OWpinܼEii Ke$IBEj5G%=}@ru'33ѱbH$(_G'%~f,18<ܠ`6!J6t89sQ/nGF8qBO3*FT*7~j47x)**ﭳ;Yn@ l=:>1I(bvvΎ8N$Q̩NGk/ A`0餥 ׻ +aX<ښ =}|yotO$8y,l-3=3ˊ!;p n޺CsS#n6Hw} ~O/:N{zY().h4/pIl<5 6ׂ?1fY^^#j522:(ͯ~ HOHʳsh4JKrQBkR pޞA$4j5dfdt(,][Z]J5JKK(TUV'PT*R)ruVTm䠥t&Su8z=#aZ7eFfff9aƤ +*ʸq6'O8vP8D{[o`{XlӖO3\|`(ǵJIq1   evee%t]f߫f5lߧu:#=H$~wu.ܽ7oqE|~?hSSIMKRP唕zƕѿ} xhxͦHzz:U7?w8 7oݦ J}]-u|>?pʊ |yYe%%H>"I"p$L4Ŷ‰c?0sg}Ke\ >΍9q:kAFCVw=í#Oj;vɓx=c\8]s,t]J]mklջoqhnj`xdΎz"(fe[$sdә_XP{ !b)d3;;OsS^wRUY _@%ZX,qۓ[;loÔ UUQ]]%3tig["fO?h0與r vEf/Zq]`$A˨T*Z6orp;lo6Gǔ~ym 6v3g4vnײRJܑP^J Ob&_OQ=l6 q6`1FGw119cRxPG66zXX\daq4 KK6~?P0PJoܤo4-HX\,+=]/M>k[VDQ$H.VTh4.]h+V +X 0Ldզt򠧗opuKfy̷bY~A@VqPTXHn RS׮?8HKsgNurK$wBnImTU8.\;a^k7nަ%[RfAH$8 S,,.bpRT\"*Б+ (uؖd`!==]H$=7b` dĔGff,uJY&c{nwpT,MHʘ20-h4Gx(.*BgNw"I^7n`0yg2<:S8? xv=tvp"I,m!lCYfmmWI=›ddeE9 w~ESSdgsA3{d ̭ L<ɓ;3[20I1 ӧڍs2jJ>o<ؒI"/*rx uĖ0iuUggyӮss o9{P: 9 e<飴T Iy7c^(TTfgyek<__o~ L֎23Fc.\&;@(R^^9ҥYDIj$󔕒墨Pј#_C}3sjr|J%Kzq7&q`;]W_#/_MGbNpmrSSS~ u;a^V.]B(¿墤 LiI1Qׯߡm\Fq yFcBaAؔp%n߹M6'dQh$iiҰZ,/PQ^FYiɎ:T*ծ5 W x/GՒa4233(jYХa$HeFyG{[+>B $|~?c0:>$&uFFXh4x^222h4hZFlefD"ؖ3-O Df9(RYNݳvS6n⪭u|-o֖wv]c=EefQbI" ~y*(8ܫ{^sw6hij$׊r23`~aVicC>%ESZZLGqB=ߏ" SUYѣGM47Q^^j=-MMEt;s6GKzR7ή_9PyCzu^*H$\rcb~ݯ8M~"<<] ??jErEaYrPUT|gID"9_=P>tZNt D"Ͻ&yGO3 IDAT*Hk`9*@0H~~>[[tA$(J :۰;hB/pb7HTX~@$14ӧNcpB ˔PȉD"tEq!F0D~L؎f/q; #[&vwm,,.}kڎR/ٹy.^FyfG NN}z;./Y.]Hi`y.itjjACJ"7C*oTS\)/j^ MOoϦ LG\sFD"(,E2fEp|OO_pݾg}9H_<gvn>Pg?KK*6ic#~I GL&N~=^=S_%ʒ?OSl92})mZjl5s(7ˣ XںOƴcPclllpd HDV&\:} 1M1ml??A!"KOpp8BsSsN$++SPTn0rjoڝ{=D"Q;;47q]~3;7O$oHkOX ,.. rsY\Za}݈N~).b`S/ 2})7nBy/ *J%|j`m7vwm(J</@מfzfVlnnq]'ן}fjf )..'S3W*5?7netlp8NhV<BnH$ KDQ{ %V=֝x<^|>6p8Ǜ87~?x(ԋFRfq"(S3O'y3hb{Ar[N(J&/WG8P` ,SӤI$qA(ooP ǃF&KGTbIOOOh+imi" Yz:-MHhij:l F!yD"ƒݞ^*S33LNN+\~Jr</:]'ٵmf3ZlIRO/ 90[,evnK6[C)>L$G JM۱V5Z[|b? P(D$E*Mtf~~D yHRvwmDH$x>b8P\\A*"^k}>߁ 8vl6gf(*,`bj癝[h2B]m5%, Y_7` %mN66 <|8Bvv6s ngssuvbܸ}ylvezh鴨JVA( 1bfQ"j52L TVbARPOUe%NJ&MQ[S#+7v*uU QgeIcCf QB6Ν9-,RR\LNGGTk^ztZvvm ʤ\N]m kk2$1l5r7w_K$򥋌rhnndggMUe%X˗.P^^4ʊrVWٵqIb8YYI|~?99TWU27 #==Xr(FW9ܤp8b J%RinH$#454@RP(DNnݾ5LM\@^n.kka**xm<$b1Uܹ׃ɴIqq!%^|:N'sV.GװZw}҈hbkC)9~KdG?/6:AK8~eGR i4EKK^`0NE/%##ǫ^z}>666KHRJJJz@;KqqyPPljJ9ijl`n~~J%֝]jp5 u<^]@:qX4F4'u_U:ۄsgN+ gO^( W().F,P\TT*egwH|JDT j,uc-AZZ??E$p$$.tD$ p8L[k E\nڜm~ۤS$7R:%anwdc;VmH$Jzz:6h916jp8)ӗ2>1IMM5n )+ ck^{J".4Z~| B0/ ҿs/ǫϒ_;i`ghDTjCIojIH(,(P(ĉRUUH$\N}]-4}vS w61b#1$cq z|=QPav3!6F[w|s|c4$x\87v^ n0 )^E̾l]" ]16()yv#n< ̟_RӞ1mlR^j!Ѳ:[a~Us BK+\z{X,V~W?wiy"Ng`>%ܾ{S'Op~4?@~^^Z:rͽ~.tuͭ۸\it|h,NE.104LQQ!bقjuFFv#~Mo(R9`2G9uD[wHO 'pҜc<~륹i9ZwܹCUe-G& |s6]ݞ>$ MYqr9ntXLzz:PX,F<G" ˸gNnweXXZRQG/,u\4CTvQ(  ʖ[/ Ip<`Bߟ4NV! 9iiƭ @bܣBPfL_#v2wC_ZJ0LHRd2"h4j.vw15=`jf61](J,+RP8D,&##Y# eAxUYz:^d4)cwؙ G"\x~Z).,yKuy7~':ϝ7]#CxHĺ" u\|/Jl095MNN44xz}׏B  8/t%vSTXFKbgϜ`0b0(-)K m6(**D.v16hnjdzfDq/#<e`KR)yF4+/03;O8`4 ۳;qrr4"?Ɨ_ߠ? C }i)!1E"Ş}[(r233qIe2$iiT ƻϟ󑟗X,χb926?ٹ9n%s, h;FmG"43m. h?} 2!똚Xk Z|8H:鹀{ 1ן~M "TJ!hW#W0bq D""CSUQB.89MﵺŨ2zn%rb7}<^[ccc琥˄_D~\XQBJyy=^yFJAMUcdffpEq2kkN'%%EJYX\}cw׆F-'½byKnlb#Nx$#t8lnm hصX[[ť*: HЗ"Mb4f~a#!Mfm?; F|~?urd~aǫkt;b%/WG8vA`dVڈD zJ>"y:D P?KfaaJ:+F`0R_W:PDIq1%%TCa* 8E)RISpdtaC uXON.M? |Lp_i}n|~ %x<aQLJ⻐'Rw|Wl|8.y~gX,Vzy+JbZg7an~onfg33;&?}m>4K?:6~`޾RGOo?P(0c\c/R|$/3=3Ko߀0<¢ }/Ѵ:)$ӧN9uE"6ry02 מ*044LVV'OgCl6JFF.X7ë>uLI6 Yb8L_$Mbefnl>\SMt;g.tVcqq ź|defBBRn0rwbX"*</ 0mlϿ|!ӘܸuBN&֩(/# _i:b1~Cx$ Khjl  $ecFZmf E EbwzH$KKy;J =RI1Fb֖g0xoرVT*  9B)LNII z})"DRUQ3gHJ=t|ܸy DC;| Fƹu.fѱ &.[X't: Ø-Ɓm% }%}%@Gܼ8F??P( BnAb1X"M,--Dpb1ggїds%nS3H$,V %?sĢ1dxŷ{4t)-Md2-Me2ZR*_LX>o#9~>l$6f v|>k}fvp&G"1[,d2~'54S|X<X[S}ߋ]_λ],<Jd]q_X@.Mh`ٹyr"_$i`,V 5ULLN XVff B|5U.t'plF$ ⫯imifiy֖fAcr.Wb!HJqd_FcC=yyU<LLM#X8N,V+PK0y(.,dzfX,FffD/ÇIP(--F9<_3ܺ}-sxijtM}F0p|> Y_7<'];}~|r._:~=}iwCx(M2ŻA^^._.ڜV y ~X\~VÉBobf'l(QQQX"ah_JcC=R]-1>-ǎ8N PXheez"P4;ډblt8;[2]!Nӽ7K:=H:|P %99rr oޛV'闼NlG]/S/l>ڜKHM:ϋ~~Ar/7RHS|?$0R=RsW%k";@,#VD,+z[K<gtlѱq8s Te,@_Jb:7Ya;eܼdKo߀0hAvvvedt]qxA F`{w%k`γDb$?Zvvwi ?A,Ŝ=}jOicBAeE9;lnnqqR)3X,V4R\Th̩LLNF䰻K:O0;7/gڕbqn޺C~~k*˰XDQN< c-;{k{/c-loZ~,^5D""J룵["ɄwxkiyL(4<U<哏c[{unS/E.nw00t;/>ɣǏ)*,D"I_}9:J›=$ 50999- ںXLh`jjg50]HӤ ݧS}J0ypb`(ĮƮv{q\05=C0"w˯֝ė}Apdу'|yT 40@ 8N C0LY12q{< "w2NP(D$t|LLM~J|Y HBp8\rÁ\.|JBT0e2]>XOLmbya[3ʗ&gleW/z? ++L wh$K(/u#0@8f }Z~^M LgEi ɸOrc ~kiaaq  KBa*06}&1IG*ԈB!:js(+-?8ל B~_@ȇ@rj5Il6f3kKi`p\nb(KBp:_Hh`,IEG ]J_ ?.%v;K+xu Lnn.~׈\ IDAT*` B!gbr шL&# 0>1IAA>BS.>OH:0xhdn¡01.vw3;i%().5T*%>Ͽ VCaA!&^UJX~^/f#l3هx!gNi<h2kj* ~ŗlnmPql6eX^yeaqMv6ELGdumg~oabHzEiQ)؃䧅Q*L}Ƞ4dH_ݸEUe%G"dtZ"juX;QTT6'._f2ru:gg).*"7WGZ夺: BNQa!6)cdl!~%PhiK+bPTTHvv"VpM:ڎN,9 -MTVTPYwl6r9H1ml&UUBrEE(Jvm6<z})3sshgkkFD&wZ0^"V=Fh4٤PW'JᚓJ @I1:So#9~W.]`|b IKKSz"sBeLNMt`um.$#YD"<ii‘(EvzjOMU.)=29sGPzVn޺o}c# {\W@.8yIvwm(rzimi>. xv~p(bF.fs,_}s.@ ź磥Gl\Miѵ8EV&QR\̣G l7EE ޗ* x}>TD/icӅBP_,*/q]"RpBn?G?{{=pXs/.-S قźCKyfV#2S;.յuhm?4!*K>xGW 1%%c4>0MQQ* HL}}-rLX?mWC}rFt().p`6:W>I DT`۵Ҍ$++ WB۱VaPqQ!UUb\TIAAͣJEy9<{'>ѱq$Ο#cpx;\4bw8P(A$!-- a 1͜N٘ka|b DuUF|7ܸy Z~`4q '({'?oS߾F'/Wng`h8K‚B$BltZ~w~mJ#XrIJ CܹCeEu5m63?H8px;7oE.dgq6D\zIFf&o3N ۍc-VZ% RZR,l4VW׸tꬬ"p8]'wDxś 40:f),,`rj/ uH$2@c%E0$t9Db1V?&+#msagwGn3LMϐIScs 4n+/266AYHh`NTVVp^VpaJ%Y(nǃ&;J>+W!J޸}t%V=񲸼LG{>CU50Y@Xwv(˧X,6'G(+,,@"M"!P\\b!7WG~~> uXVCr9yP,gN@$Vg *N'Ul6(鈄(c yPW( Ab;53&ez=֝Ξ>%%333CB}˅qrsIc{`ELNMs)V UrTVИTD8Z6~?^oR~gH_Iq1ˏѾ$4:Ij}I%;i`^DUe%Ris !,V+RvOs_ttϿ?/IG[+HDWV#IP\Xݞ^D"5v[;A*u/t |02F~^rW`umMb'<%D"V9s$FF‰L8 ā !=X]K~ocjja:B._F`ݵW8,^% {&*37͋DXN㡵\Ɔz(rB!G0 h4JK(t(-)L/lA6[P*hm656yo~5و3 ø=:z46ԓA,S^VZBb0J !-MM(UJZ[T&M;_~Pȟ)(o]$ū>3O+?31g<mx{,cy_%; c?x}B c5.H&xNSS@mNMDg{{KAAbRAA> a0B$}^kRb0#>uPqq Biii&33x,FUe%{LbDΪHh`‘RivDG۱#f~w:m֡ =chg|bʄ/&f~aB~4|]^?ϿBy=zLaa4FOW\QJyx Z-L*ϝ%lomFkKKBFTRQ^N{07n!.5X &tqB02Y:dd_`muK/&MlyP64(tzz61~1\ic86֖&Jr$@ mZ}] uB'#C v\,`ߞ&fii\p=ddW,BOW._f#  ?aggc-X,lL NoG&p8( ^/>mҞojz/q=YY8Na:1\"yy#MRZ[p8|}5U,.-#T8P*Hө- P%``(DqQY'jr4  STTug]jF&‘0]:o,Ҵ4rsuk%?w~췶O_`0QS]braXhn)Y]['QQ^V}ĉ%ܢmN",V+/txs(~Z1ܫ,/?"??wHfCDB IKKۏ126NohjlL`DJHH$"j5"(pt\rI\4O'\e'y[H{_BGIj$,,Wburu:>v]O]+oRmz9qw$߇9yS|` sgO KӟAVV&ٜ8$2S'kHicJ{B#UmK/" rDb-,e14sBfowW'[e2:; GlllrE%zW*)G$q%D"t23dDYCk"EoGs4NoTR +&ܕWu ~ $z [[%-UeI.Kn9`dV]kۡ?(=Ilr9Uh9B( }*߃qG@ pv)RxxF"FSS-Vxxb|RDMiml`|^ W޿ov2H~,V9ffS3C!ze}@o`O ՌjH$_~,n~6^82'1Y[g`{DɼH߾fd4If1`#Pn4者Zu.[[[2:6S'L0;;YrX7i?)ittV&&'ؤHD}]-G(*| 1\ƭ[dddPσZ wӟ850d2Ok`~]<~JAAii!st:-CILмaļa SM Zh}g4WTkp:{rjJlmYXH-|>'OdldThoP\̓#D"R)k\؝P 67 .<y:)]ſ~ z})΢ĢQb8Ri>BI ]g?ϔQ]]J"Oƕ > bnJEW/_n4,ddd F*G~~L¢pގdSA 4/8NVW#%W Kaez(/h4T*z8=^fMv-O (p]D" NW/s]]=LOPXX@A^͍ loos%/ENw@LH$:@!yٮeh!NX4,] U8uϬ/#~AuU%xh6$$J4||޾lv;>˗.PV'--=d8}#ff# a;p=axO>{?RG$~8B۱V,V΅o56/8/]8t]ѸA,C_ZBfF&6-`0W.[H5WXBYU-J}133:LIE%RwitYܨcLOLNZmp:kW{-|s6?t榃q.s;s969YdgV0ېKe𥛚BD?=QF9XsH+GZv2.ʊ h>x^GeE9N R! JìolPOIII/3=};sֶ IDAT/BWgl0 VB`RrΟ;!rggQT=s` a';+m*+ʙ_XDR^CVE~ K+9}xLHu Mbb4a4J3CivH'02l[fr9{xw'?gPt<>>@I#!!!:3?Kki1ZĚʕ40ֻtu?x<˻. F`x䩠bziYB@X3::62waXXX\@ v{hk`aq@ HG<x^OWćN_XDQ,SVZ˿7iiA9 5j5c1*++i`t:b40-x2H(FHf󨧏|#f%O;J4I LyK+ ?% QZZǎמT pp CCTM(AA319R  qc^OC^OZjř nSni~~Ntv'5&pn?ƣ>w73/Po$%5U[_{WF4?᤭rSu5idrr`(㘨cѨ5dgguu<xBEH44[SJKJtZ'&1[hl81d28qXCM]}8XxrmkڵZ(|>4j5)))\nffb1z$"S%^ L\LQBSn21192%A$al|*r!vjjP(TFGWK50`0ŲEfA<0rP*;v[Hvf&V;&(7 S+Q(brqQP>Vq=]fԱJgCrdffPS]ŭ?Pcd*铨jMe&W7fo$ #SdI˪Wlߥy/_݁ PIFfǏ2d999 @yy B[֙T%|u ~7RRu/\.r{M).s(+-AӉ*J<cqJKt-x^jP*PUQٌ^$aEEn233̠G=}P]U:5 LGg7Y:X ̨!Le( ʅsg%?Nr32:"- c^_Μ>EGG7Q]UbvcXm"24 ,-FUѣiQ| A ..2x}xW&Q*wFYVQ2 Bh4BE C+F8~~}?v?Wr(7LsAҕJLeeT#'Oz)7Q1dσ !rr:ԉ^WiqqM p8L0%| ~K˿u:zOu/{Nvik`~a@ @W#KK402uBa667IK (NG$!*$ yA8@(BV =FC0BVqy^LҖ~NUΟQ0_bw8xSV9v_wbw8pݬonr9@iI^!Os{]- `iiG=}KWCj:qY~sӉRhj76YX?RRľ77OG{})B!"Ψl󑊰ka"<+}p8hkd*T6fQz9vwamhjrrh5Ο;AϓaG_\a'998].dȈcbZ155<BrY ڶZ1 BԚGnnpB0Ngׯ޶295EQa>V+t?ATىHssh5z=x E^nsܻ"*+N VKgW7W\?/R-nD디g94t0:>!!t] 40#awzr)Q𠽓 mf6pU;[یx<^)))$Ccʄrw fs"ՕOv3wMVjalQS]:.*U:l/.rIVfˮi{AEgO?;00=; "&JKr"?޹`~al66ͻ>_|ycZѨwpK3]ݏ7QZZ F~{ǏQVZ8ėDKyx<77?3s#,1FcG[yI0Hh`*+hn:HZ(7 Lo/Ԉ}= KBMEZ0y: ĉ'"0kW(.*$o㗿:Zp8L^N.nf|#5U<fc B+jf4sIʊD!%j5x<yy=rڰE%/ypV\.>5Tx`218E.[Jv{_ iRgP*{>^IOO⿋_^&.JmbJv\YTWUJk͵+SFF"J<p^gh6p'5,,.q`(Dzz:h ̱c!qCy"惨T*$=]ɦBEg_H >n\ E\XRcGZX@PiP]U5ՄÂ'VVq:];sM&2 îUz>YzJn'z * lv;U!N'Y:JsgOr6%"} KH/$ǧ^I%$%~I#:~ٹ]$$?#~A#!!xyEGǴw^OnJU7o}A'zܹ}./ u$>>GZ .򮏷yAcw&#@Muq7ضhmiQo?<.7MM x53k=rflg(+)at|VKÇ_%K <8|y~*UI3dM3J?q#?femoͩ="(_^58zCvP(0XVs0_{g0H1T~Mrsr$  [loo lo[lmm 9w$rFlD"{#|;|MD"VW׸{n'}~k1Ә-[qnډblArX^Y?w7wlq\ ~1L8fkkx<ŲE(vi&P(%~I<l099},[[#83;{Ar 'C><-Ν9̬p0om122JScq`pxX<.jBV*8N~cc464`2\z9mX,[d ;{YtL253˙S'ʤ)ߝdB%HS^tb1f@TDШ5bw8y}dpyzzp HJx-/,pq1ffdُ#h`JJIW*yEVV23y24ٲvk  |>?))& OܻO0I76vavgnr@݁b٢ I Lg7 & 9}͎b>mӿ' - gni# )+-ajzB0R+cG飰0b1P$+38NLeLMPK:JuT* eeTriS}288~!y1<̩Z<?z] LUG=jjTFiijkvS(0l?Zf{{x>\VV(-)~ʟY\Z _n$ 0Zt%nSY)++dfeJOgaq t 2l6;99LNMiayeU5lw9j#3FӱRDlPZR2F#pN'[MRV/`ip:N']|(` x(`I#!I#!!{O)"mb&$#OGl5#c,..yމek%;/<7 ̃N"<:|\.?`n~%=UAϳtv?_H|$  7WmO BBm+m!CnQoPʊ 9zXtdgcٶȠi(v:8~Gdpu<W+kdtkclZ q RSdc,r_Xs1Tj!"iph-  37ĉyi>wp9~{FCue%C#Oq8808DQa7Rl?wBodj'& 1ԔTm&99ٟ^_>wRf*"M3SSA</Ǐν6:6*c^7o}V-232IIc0M  NK,o:>Լ-{LMMCx<^j5J-h4^^232 Bx4 HH8J²EVf&O`n~ZMfn|j ^ h EHnw`0 #҅!))qi36>AVV&VM=n]m\|{m팎SXP@~^4sEo&//L&B&wR[[Co_?N`08W\blt P]]ByJ4V; y\o<^/ bgblIlZrSgN@VSQQ$ r0m[QnI7<ُK|B.r`hhDxd 6x}>RRRT?* .X<ζ*gW n[R\t0L211EVV&Οvs ӕdeeR(p68qzw17?OfF[m)713;Gˡflv;wQQ^4J |*Ls ? rrd2P]U8&8iiiLf;il8O ;$#c=8J/tl y]\._Lev{aΝ=C4E\$q::CT5G"Ƽ<4 ,R]UIFA$/)&죰bۦ%Hc>gx<\&5040+H(9xm%|>._/K">&ah42]<+u笍R$p!6HEIKK|W2X ␛*=.:PPOA"H]UUV;I^^. |5(PGPZ"q{RaA&?LT  &?X8Or /W|deeq%h-~;ϷgD Ld&\BMxa(~|E  =]Hxj5kCz^>$5,-p PtX,ƩLcG8{h/㡬5j&==FMFF:5 jĢ1X\Z@ (dr:=Nc{Ja~>>_\l6s) v*JlE~IhyeNFCP*jkvd\8wEfP8 W/_L @#K>Ξ>0v $$^i vH'0t:"X_Ŀn:H4 p(L$&##Gx?55U1%% *~0- "L>a)Ir0Kxjwzbq[eųra*Mh4نd vHMIdbf;@Ś͂?1%%|m00}A6t|W};K +ssx<{NGjZ*DaT*d2 ~D1atj*d2tO4rAdB!J%`^ěo5{Ƽ7.+r=2ztu?z!%so}w]hkbtS\gX'CB~~ff瘛_  K,,.q~fz IDAT1kk?|m:{r񠽓յ]btl| wn~tqA;s~:%$7/>Zb8^F Fe}c4j5̌Ddt%ZLF!tu;v-Mzz:`p܇dvnN40;jw 򌭭mz(-[$f<WhI<~WT4MNN6TTs>՗LLMqJ2z1屼BuU%f%7'g'(+Gp\TK14o3,,.a#55 77ɑ40o g9{Hi)N>{7kJpn澋x)v]PRW[B`csni`ӕܼ=ϲi6IJJ b4M0 =G[[tZ?hH4 Ο7zfphFM"J.]85c1&33ɩiN;*HYI &Leb;͓I,`0i6VQ(v z=m|u ,-22 {a9Ξ>EOcd28}8v255˧ mgN119EcCZLN`fvNPP]hZ66`6[LDq 4j5M߹BԺZ55195 bjҒbΟ e% @a!(᧻5?jHB|M$\n*+LȉFb1; :VӧN000(`|>?@6sgN1IǏtԩtu?BPR_}IUeP`(HaAec1k[R\G47/u5r_Xz9sQ:>BuuM b50>xIM40X HwsOM`q=xwrk Fܹw( l6;K47dvn^JKK@x>Meā ӕB `}c\^#++Hg?Foà7 DB1)OEbdggT*zјǁZ<^/ąA&V+Mt833"yT @Fu8N06>Aqq8s s=x3N5oʿgB!!'W<;bee<1$qq'ϝ!/xCºF V:EZ(YY,-PWSMjj*mJD\.lGiٶZQ**WVrs|DQ.;ekbzj,76EJN]m HDlǬL),移0 ?wE!NIq1:*X}Q|)ZjZOB= rI}VVQ@~@LFii * 6ޥx$ܱm0d v~I^ Μ:Azz:%%v 5XPK+9Pl!\ߵO'uXyHWV#v'wn *U5W||?%:I/&CEح[(Oݽksuڥ̌ 8KD#Vg^YǟK(` u H_7`$?߷U|x.{:Y_Z 3gKD.^.o[i}pRali^j#;;KyvUzYXY~?M0;50Um ͎ _\.DzJ!R¶e&8~ Vg9؂RjXTٹlmRTVݺ@wl=5-Ouh9u~KE][_(U̎?u!IGploT,[`{s?pgh4±W C4;@~/iidqڭbzxbsu\RRRZ6n<û0dfs/$$~p06>^p8LGiQO/7o}8> 16Q p8̿B]ݬolAGx=]r:ttv f}˓!>HUz}ܹFO_?3ol-= i&  437taX-<fյ|H|>;&^?Y[['x>'&X,qb1 nwǐM2u2`cy6;-<}Ta'<7-e Fcq^75Zr9~ 2X_Z  zPj((1X_Z1u ht1ZX&{ M?ZKE]# o}\QXXK-O#O 8lۨzd}yf?ke|wW*(1><7\.t}PBMxaUz=w﷉lT*Μ:I D"QZ[[j>ݿ C#46 2}xxd׹6@0W{;50OUBr˂F233ekk5A jG{mhm9DYY"KIMI ; W._8 I=;v;stǸҸegex E29s$x^*ئׂ,Qn,!tgm,`bZ"3dgqc^[&#Kp{ O9|(-G{Kj2}f'.]D$&/򋟭{121OE]%&[f.|-Ϳ+y"K⌒RZYex\c"SO5Ř1df"uͭ>e}"S%g}Dn~K*jX]!+Hjxs^vd2b4 5%JEy AQQ!`G}y:Z&%%G=}m=kBֳgOO|ď]j\ii.#?ȓ!d@4~ F#/ ):'w@V($55Lr6^W,qg0c*-1hM |sh"^VVVBdXPsiiiMLb4汸Z1-8].Vee%b1ʇVqᇅ@Pxs쒊htzrC':V/Դ4|鹓4;Lʚ׽3:d ܂"r ^ :t ۞g$@gȠyn^Gbr>ѽN>i`<壡pBA,#|VRRR\ :!աPma0 !#--M3FNX&y^ČE.X}/Crq] --`"\Pj=Z[ZP*lnj)_5 =jJlH4CӽnB8 Q ⶿?d2ѿ2 LrQUlԻ=Ә|kd޿Lb3n$K׽2õ gg&٩٦ gNh^ܛ/q}vސ40{-㛛~s8 dLMł/77o12:AG'++'Cq~ dx<^t*]#ݏz0[,@х;.w׾_H~oq8okgfvOGW7=}.GI!ffu:]]cbu}>$Ebek~ oXHM"ԨմEvv6{>S;*,d{JocrsY^Y wghk+C#8NZpi?=<}ÅsgYX\"/7769r~-99j/EbIle^p8+40LMO.hNVV^gxd4tm4s>}knw~S16>+nzYF<tz|m8A'+8ݿ' .yvCû/qk}@gs)6VS'xxrjkٶZ BLLNd2@VOqilGד"ry:6F<@ǍN` ``v~'OAsA2 Me]ߔFptZ571951>1IFRSSi{cGvퟚB$;֕(7149*ˉb1wvHczzaFrj,{kر>y9)~6O17'DHQ;Ij`j(7Ѯ>UUY$r !A 7ᓛܩ}$EM^n<ekk Nff9PW 8~͗7 !Hшlaeu_|yB~dggR$50b1 67NuU%s 47v󨷏lV; vfi=܂&xzLMtlEZƆz;:IOO'33p8Þ^ wvat:bsӼ|R9SBzBP_/:6fqic^K{::FN8fp)Oa JENN6􋯨('  (,(Mb=Jx榃TTzg׮T*z` N8&d2_TVl! NˡfqjxVX]]񣬬Qn2v{{<@DQh>HKss2r9kB7(7p:s;<\nJ>ꡰX<0O`um1[&9y] ÄBaAS}@&i`$ނ;9vj‚| "A4M|yY\Z+|7XZ^A.Ǚlp9t:-f\A ̑6j \D _u;_0O4h4IEE9=}u糼FPto-bTd2Ljj4a8r|#n榃lmm*!7޶ǐqd]L .]vNˑÇIO߭y^W_xe& %<8q <x^ $QTX:5er(*, VK$pg='$Wɔ\IȔ$@'w M{/6fF01؈Y;sg}tmd!w%2}ICV%%L7C (eJLɓ<ӧH$=r|tFCnnTž2T*T[hOFF%Ԡh'(f8s$>8*U1 !Pj.2E^?@ff6]yq1P襟;JgO222;l#'B0`55}4FiuQZ_B}S"4ReII%^K4u%CRAEy {g^r),(+m3 ϔ.{m66F s_-\}edddddd^:{\Z ˦F V+x\|l[y$ SUYA<gaa j+mi#2??n{R'++Bg7oZ5-#sBb s \n= G'}~鑯G}^/l&&^:r' YYY jK^/ΦdBՠX]]ihLΜ>N˙S'|%={U70Sz'P %ܾۇ?dzz}{k\3L&'Ot008D87o oWf308#k:y&ɧc27`$tTTLZ ߐbN'7oR^bum>֖c>FvReQj6cw8FMW-A`S" ?ӧLMψ76۷R;wpT#{!y- v EUeV@ @4%U-7ond/S}%Q stu`T/VW pi{XZZ&//۳+;) Mznަ-JjkkmGqQ!u@nj-<')n IDAT}V~UUUފN/]{50]695]8W._^p խ޻ãcVWz|337GCAZljE=s| !lv;/]_?P8̂umu-$50h.]o;*V+DA o $zϖJ  gs#S556019kz fs16;n@S#D\z̸A 5´]"(HEKyyƩR)}lpʫX~QSS]EYYt 4헮,Y#y`qqH >ް8{z ۽wq\TāF&o㬋KTU ,X\|In!( u x&#,:̌wڑ:?U/ϗ)*ڽLNobyiB+ddW?wGMc4)}> Pw/:cJ]zm42;wNKV*x<p922|e fs1*u+OW&C$##Rs ZRTjQc.)p8z huZn (*,duu AOG{n/wx)Y1*I]T=ØJ+Rj@/~(MӠ.SuML|שj&3n䐛C CdffJw8ʊ͏/N~~H9NPXx{x;ws_U}O7z..IOr m-nnt0==B@L+s&M5AnݾCۮ̯FGIכp:r~>=Qwŗ_QRRU V*[^0=3ј-M_xtE[Nch50%EܺM뱣ݾ>XҤb|p{8].޿pAi<$ <{'&b`2k;ǟ\dwU r1F1>1E{[ YYY|7)+ezf}{kYyX,J˱/{u~YzB0KJذO9}% Ld(s\َjɩi=|lvneh>uq| l{k36>.BӲ_}ƆGϘ/_Ɔ'k꧟1|~H$27싿084nayyD"ff˯x8pXL0`e}~]\g_nn!ɵ0x`02Dˍkq:]8.~?+O\-7U&L0Sۍ!TԂ@0f墱@0l/_˞=OGq1rmM z0~l.]P#xlƆzqw HRRVijl ;; 5rY677LLMQ[Sӧ}jI$lG*.mH1)4 8N1?oZ2k%@+Hq$s[*+8u, A`omT_v/*Yzjk)-5sns cF&&PngǵGfw6QT XSVƙ'u ,mP_ϓۤcn+[_WGaaM1;B륰}澨 z;w).*$7 6^ ffgKj`Anw7XX." <V%M<`P|L?7Ç##=nm#)).RVfgx00)1c?_TWWQXP >RUTTDP*b%*+ʱTUF=\z<>FJUD0HLKU%%h'^^gffܤ1wiyjq%w<{X?fF,Xٿo/ rj˶볡>]DF\]]css:Nyh47c4"U=61FHއ\#UذIسTYh4 ~xk`J ǟi` L& W?GW>D<Cfg(:<@ťe /51Rv Uhlc3Isc>xlrzImM55_緥M|usw7c3|> ö8?}6|ٹ>y\zTuNJT;e ̏Ap= K~~ۮkcۥ w#eK6.\@Z_][ÔO$!+KD"QzݎV%++`0HII16xbVWA.``0HYi)kk秸%qd\n[KߺB:ߟ|L'"$eJ"t::-ǎՒ\47X]]`0QbqBznfyzId9/Mt\.i7-ȼ;yF^{pb47\"*I^y>dd^1I$`w8%Bc}kk4% yFwϭ]NzA \PX,dfd _X  h{-ͼsm4 .Kdg}brsrp`s8ff@ VLbNߔE1`Y][nwpv/yyNQ:x<9|ͭ- 7o6PTXQJq8~Se:[Ct:?BQa|50nMM #Z_q轃 r e娲[[K$J"'H P[cA-Rkevm-'XXR{x<6Nm8_]LSj6sF7۪!=~M }[E%U9%%%h5HƷ M"(ZX,-W$yip8Z&yK U&Ӷ?50fPhL8K+&?ǟ~o\ƺHd+F50v;tZ-v3qkkw%E<Hp8b$mu-TUUrh4Fsc/H40Xlx,.*^Ɔz cŠKeRӅ^bAaRPɤ|k8D~޳N:=mLvc4rii©4I)'ȁf?BA[$33iȼ)/cumj˅Vceeʊ BDB) AذѨ@I$E~~>~x"^ǝT`Tj z<"(YY Nt&^lW.J>B0S~>x"ihG@va0y!}&]s[Kgmu;rKo,A0|>?͐y^lnn N2"Qfd pܜ筲NJfAFFYYddfRP*(3Ҿ2FۣUʈٹUO x;}޹K<;&9?Nή&_z?mn{rj]=R8xAnnۮ̯FF**ʁGe2RWʽg&O`4% ܼxr2'ܮ;  STXwX,F~^^Z᏿g|bׇ?  ZX#|5 L;Ga]\΍Dm$MVa;PTghb3rh5jѨմ;ʽbJK͔qw(51L,/h˥o`?H{Ŀkω{|sST5760xiZ[R/_Sbǎ wzPf()5Y߰184̱#׫rq0Ld(3X[_{;=2wZmw8g DX\ZF7>a|>?otsoYH$olac0K+X>nwf}cI~uznmg׶:\~?7,X8.N)>Á󱴼%~Se8 E඘:].lv( ‚V\.F  _8G$B7}B\.:ZYXZI?m.cKX_tւ#xl w1oJ1R*Q {kk0/X AlPmA22'u{I$ ΩAUE9NvrAPP[[#cX/,Ǔ=W]m;wXXւ1ɩ)Aq]c\7orN.O  ;;;}S QpoؘIsɹ40,..35=Cnnh#X*a`!ϟ&,,0?ejCD1J%nD LqQGQ\\32/0pV+OJ"Z^jն:Lz-%x^DKP(erq-x46F{k O}' ]JXeee16>Au5 <VCgјŚ륩!qR3HAP$8L>U*TW[xǜFQۻNG[+w9e00 Nl6& u 5|p"^Cxic}tJߵ;=~X45Q[S|llcbb sIMFu50ef3(h$̥05>ru&q8%ӅBUOtu).*ӧX*rQ_?KU%wFchn| vx"N4`ŤS u( SP`bjz^l4 L*YYYYpdeeqm۪*+^(-=u}mSUYzWgϜ ''Gz?}ee[˱ W#ј͹my_]1xY*yv~?))}S*?}zR'2?wNuAO^nT0DG J8>rF_CBEL&XۍA' q rF6F'ET!c4:JKh4I L8B ph4 hGTr(Xp,*Uv,UTFבACVK1m%F.C vɼ  ңĬgkUR MMӔ.lN3%(*zvWjL_ҿd'͖E L*Nz40ǎFb㛢4-fwCM|r=h+x<&FEܼ"2~gJD<v]n!395MgW7NKZy ߭q:]LNMRj`۶}?{{%N]LLN'Q:6ߧnxOL ^Z]槈/ NKEFGsi! {J_n705=CN 1~R9A704^gqiF>9O&Wj?8ȿ-yrʓ'\x=s/i9v@4QQ=9|s6GV/9F67=r#iZܙ|m'Pq}AL|A`h|Ba X\\d?[4[n/OƆ @BRh4O\\ W?@k1 mKwv[gO)EV,@KGk i=n2J**Y߰!b[Two 6E>P(DSc'hn$33X,B!Hu8z[MϟJ7lTVTr@<[J* E1% IDATA.޿x@^^.% Q(RLś8j EEy DTJA  z{!6aٷVFArr9 7{veeRAd+BPljD=`xG3!'(+5`]NzGƢLLMScH۔ ZI$HĶT*TI(baaQ:769qAS]U}h^*SOFt[[['8z]T*&EE,:k`>r[ɇ\c4{LNnS8N Q3=CCR_}”܂%g(\PWOn(//X_`]q2&*8*vK{f~Q:?*$zWya||FYYŴf" X,X~jhQC70 =(}}IÇ?Hss#4634|43>!ƬRRU$':, oHG{+wƢqY[[.^Va(AR'#CxXUYI$deNtIk1b8F;x`Gu9 Li)w$ *+h, uI^/3o afvIٌFrPbgɓ%rRs{mvxܚw cv6Μǩđ)233xkE`kȕ)=Kŕ;}uk AO^n.H̬LG4j CPP`"SXPAAp{3rذQThZ<^/_ۉZPR#GnNG8fqqI\T&NH-r845s$^(Rdk+,%jkIuEzܼVdh4uq qJ<6AUUV0>1IUUjN`ݎZ__G{+w"jz˯!?x1  siI$EPRVZtO8}8K瓡̐~N)?vSdv!tnwpv/eee߿J*33sm>CxP*TUV辙ְp7 cuS3t:4j++O9ycȑ}~?;p,_V,cϳTY8.rrs6 .YFV+( )yb1jNkW.X9x^"j`JJ$ʓ'KJ(++(Ņ,,.z N:s>ˁ!NH$(.*n?JBGϭhn^rsz}[srriOũ'x=<} fz^)ξ}{& q7'|E"@FPfM铨*b( V?{BA.7Sӳ;s ǃhmn7mx02*;H@0n5=m019IKZ50b.Jn7ob{k B!OVWo]22o|d640|x2=zy0# Ý}abr Ӆ86;S4#[w(.zNSS%</!WL_rNq FJF%W.XE j]8FM%%,,.11zN| `.)ybr^@s[[ˆ-GY\ZREII19H=L&j׮@8$It: q11>>IZ-EjjQU<Zc Œ>Cӱ ǎ+듋.7֥eB0 JM[K 6 #(Fr!677Zi9zc ?9AKTVPuqť%6J\&295͑vt471>9EMM5!*+<OW)-5zvw ۸?@,'O0;?O}~Kkmm f(b_zȼ)Ix<^b Μ>Bṹy.)+5K?Sby@w#eK6.ٹyWVHřS'˥McC=)}=7oSSm!??[~P(AKgW~p5VP("P˅*K2CI=b*F9'9LR#xwShaNsgN319qr1m`u%s8TLW< JŹ3wJ;}}j)0jK: \.1o:.YH:~9."{<22ſQzg{1rrHĿˇ%jI9Xo csΠj\xBJKKذ=MMLLNKJ0I-\Lrn?wru[;}Iuczf7}3tvz ܺ8.~tvujnU_:G8f|b?@ܹ+k`d~^,/KwK۟da96)5 ,/ak˳U;(4f' 40,y<]]˿|MNnFA(BPjH$ fFD"A $ l\.7x>yyol o~?) E 9w4i+KK#JWwDB*AntFYLDhoEV1;?m7=3Agqi Hd@шBD}=l~3 ;$ L"dx{w^FGWrw) b 23k?_,-%50Jgd vnݾÞRۋZfuuYQr9޾]P(TU^fmƛ\E ȓ'OTU۽wxTc?;msg/hoRY:Ֆ*NQKĕN\ |m'/]`&1`\"j`R1!֨q:CMxv p8L[QSRR,i`G) Qv]'/7;}carzE Hj`lLNPB50%}JS[SV"OSTX#t< 0 EXh9zRIVf]70CQQ!OoF{k Kˌ>z^bJطRmn@za44T3Q`..l.`F**PXX@mrpWML|I$2>$Q\try'(5u[w$ LCC7oRmdʧm'bxɬ G\*k$sx02TsgOH9OWW}h4ʡ~)oF Sɩi=#//b REyy'Ob2Pd sLꌤ?5!1ˏ ddބyװTUbhN$8u"iר*o~C8ƔOaADS~>نlqN ziR.'77x}㛌򙝴ëuv|Ylx^'&|iao܍'LMܐwW f=QC6 춲3enn<_6})'X_nw|g;^ǟ\Zrcb1nabr AWMN'@Yؠ;}LMMs G Fzfzf6n<^yEWM_[[[ܺ}fƫοuZ;\~22?'F+O2;;K}]D7^2<]\pA,* e0;7M>;rCTVK=e|uѣhnz\ɩ)rssѨY|ϧ^]Ɉn\Se.λ>*!l'PRV!O0t`0YZ^ܙ}}?|Epr5n߽ 3b1Vf5\.+|ͳniO>GʻŅauy\n.|1/~K_14<†"esu'ss8N;p8α<095bdbrH$},.-ŘaŸ\HY獿'[[V~q8b ض6}ddd?Rϡ8 8>FƑe?g~qga614{Uop8Dk x<6C,,,06>|[b1Âښjܻr6k`|>P_K Lru j}{rellX0]VwQTXT*{˄a P(b籺AqQ~BNe~afU#2 *$ M{n&Cwݧl=l6|>CãX6-OҌT*vq =IBt dffpxqS'7 h`zz[oWgv֝;"\K}E|匿bjkY\ZfphP(D}tb6p:edt1d2`|ߏfOYIqQ! ӳ;r JoܤH$*Ǔ; Q ${"T70H,.-S[SMaaEttvrMT ڔn`4H܋XFy\PDBmm jajkjR55 IDAT6~AӱBd2gw8X[[z:::ٻ{ 1P!T#Jlnt9ض~~9LFٽl<{XX\"06>^++hij\ǝȞ݂FS]Un*+ŢqP;5JJ$=B8" -͍46ԋk]]-Egia!͏ҤyZ^y 40NK45;osm*+1v#M666p:]HRj>Z._{ަo`JI0L)ozvDBeE9U;G˱j^c`pp8D۾}ܻIiI1>y::1P*8{4\C*bajzl^;}+ g_;0q RakV̫475R^V(%mvql=@ l6;jJp@* _Vd2=BWwO__~Ly50Kb,AZ&>1z4Oh`^`,jI8!C.'QբpT*ɐg5DcQr dfH$8]. ~O^^6\`uT*5hD!Zm/|nI&M4ɤ=q$AIV-I6uNА$&ws }͉1/Nu2z}==UF%NhRUFc^JYH6l=XIW&T 瓑GN1f|DuUշNp?]O4iҤy5~3x\k;vU/;]ݽO\d L/.Q=qm7V]z w;?>Kq6\edt[]K zh~iҼ^obl|50i=fIRIN'Q֣!ѨUD1_@*RZRT\եhK鴔1>1L&@=}lmYطgw*0A0u̮f6;&# EW^PG<|1kIՐo2yR,.-k@.1Ņc4;]4;WhlrJhGVj^czzۍf Vp8H$jX5 A&144&gqiǎphmibde]- t9qx;rtXϞ =Ley9f35lnm!AWWoO`@//oFrsLټ9D"Tj[[TWڏmE4AtRXX3r:f0Oaaj?NcuGSYQN^^׮dCRQZRL}]-|9%Xcp8LVf&pD?'C.'';~2331/PP8RD./XLh_AcגVd4S8~<*ߏ7/xeՌ ;k~y'vwT!6,s\.LN @ M'?e#BsS#f3k,RQ^{fzfyff& q ~W)SLB3sx<^h?&T*v&:JKJu.&n[,9 C#df(.:ҒX40ٳ@ 70@6B蘆 l SSӴ4t Q?zmWvDQF9ض\G+N:.=vcBSTX6AD<37GswѤI#ttv5&|-瓟'xR56ԋ Lv0<:JqQK7%'"aO>F,MV+9 E/PoFh5ڗ&A!ppd2)`FM0beu֖fYYXmv?63X6MLM$w{Id42;7VՕ).Oٷw9j~6pm jiÏ?E"`Y G"x<^J%@p8,_W[C,cpx\Czq N'l@Vf798! JdWK r *++u#SI<2M{Y2dtj^6>K~pߞ<.:]R\4OIJd2RS]S'v[߷koVkpy62P(g_9|&SYQA>[9Bw~Q*Ư@ sƄܨK5ᤱJxҌp8rrĎh$';[l j5tvo2Y~U$‚d2)or D4o} RDTۨU* Ш( \n7h4J0BR8mvpH괐n4˥2JKRDTRR\n';[OMu5s456SH$Q%,~X< zzZ-ި=xId'NWVG'Ge"D?@څbw8NkmҤI[s0Ç8rPv*+9~ =[9{ ;4;z1.] It ID"߸Qm>/+M.]cߧ.^j{wµ}In?͵7u;Ibxd0:y "ٱq 8Z719E}]SaX\Zfq;onviq40*%::q8rsX\\?<, F# K47qO(*,wަ8Nή""!z=clv|~?@&0lK ׵7R)UlYU*{ۋv1J ZH$BII1uu@AGG:l:gevvu ANy⹝9uJwO/gϜ˯.plHZazf{jϨ(gtlFD"j?H[20_ߗH%`^[ms:1eX6,ٽ~R'bD&|6,_] \r ~{.]ƭ;warjX,*+ol~qӿ\yL6,\.DQ>>nrqGN?KWKiDS=5=jc|b6,Dc1>O,O/‡1p0=3"3s8G_|uKgcjZ({jz](ns~? G*i[[ 45SR\g_#`68`wk3PP(D,FJ~oH&M&VW POsc@ɑC,..P1iZij$qr:ׯU36nFFPddpp;KKb nۃR>e^/~ԔRfM Brn: I8xYI '#0:6\.Jzaqq]߅+//̡deeXhrrDyfg!?W 윉^7en٢'FmE(B&73fkJoeskmlc2Y[_gqi[rI<^/u\zuBmF#4s]X5 GȠ1n]ݜ<~ښ*zy`hd"6,XήJJ_dhxRAcc._#:olX6=n,_^ΆB~a?ƽNlv;k賲rnZ:Pd0<2 U,.-rj4LC|;!id6s8.Ɏ rrs5;++ DBee9yqEݻZѨՌqAZXFuU%RVL*{v33;Gqq!%LLNq~Jgt@.ӋER\TDmMx^%LMPUUA8-:x}>a2Ѩ5@ݻZQ(25PW[c^p,CmMXC$!;[Z"[PG33;D"<ڰXX,x>n%7Qkطwcۺ0?6/WS](ejma``c^.:6zlnnpP_WVyfI(Z0ns:USʍdZ-`PԺdefbݲѻo3=3LMt dmL4ḥmq}VH#Jl8Z[S͵7 ôhϿ~W7:  gg.+-nwt:q\L}~ M-PQ^ 1rrrykttvrPVZ A r CvLFlmn!ɰlnRXP zU*+('%dpx'GeىPVZ2%rR>yLq}=RDtޯ::3NQU)ÁxwXFTd%40Imt"I  >F)lnn,l6bbʫp$"h`Q!ۄJ%fp:]( r9@l=NX,Jvv6~JE84 BbJ HJ%ja{ mu\tvp dG3"ѨBIɤb1 DZlvoL }4 (J1 w%KWxIj5vCPUj7z=`BDRPq;|I#k6q7͞&M4i<̿?_o:--MD֖U4t|] HMQ#Dp %YK΂4e2Q$$udGv`$R@P}R(+xeɚLp&SYQ@4σ']W^4/)A >蓔g_G_@Sb9iu_-+v\>/  2 DŲ8 ^Yzgbӧ1:6Nny =v2˷%9J^G'n\CM x&QQ_W70es1CNSD"0}.,{0r<>1ɡHIf{{- Jŏ>!a#ܺ}e>3 ,?s"?ѻ|/ݽq ̥H$ kt50S'O\&còɞݭ\1FcۿUIy&`UãAΞ9M4uAiq{܌؀d$ TdP(ߋ`c#t/`.4025=XW ̥$ Lb/?/?r{cd gfxd4 76X`iihɩiV'p9v h݁˹yS3,,, #299ymMԹx⹘g_1?ɩiD-L~k bY۽$9*JQgA"jimn&//7EBʞݻ,,EEEddY5z8NZX\Z8{4+ ӡV󱹹.^/@wt1;7hR]U2YY"0ye%  kOa^['++Qqp8}{fssFM7ÝZ$ zḒ'Y!pRjD hlh NIq1 ۻH$BaRpvw.:worcg"Jۻijq"wvS[[C,e|bC1(++]iĢ1R Sjm.v4S_WGQaxOnm}&H8m]"& YS^Vt:Aʮf2u:6{SӳllX(**djjW^F1Lj$X7nQ[[wu5\qP(̡W_END"`fvKx,93;T*fQVRntr0Lx}> L&(/C lmY)-))Y\\dljZи2CN\EV0󘜚6q?F]m >2VryP n,`-$Gz&(#ރ:V\e"̗L&>BHS'?_:ӧN ̑T L==M8Nt:A*ˉbD"Q-rQ(er z}.X,>;Q*D"N'z^Ծh SwP'véDZldp4 R) ODfddO* @4ETrB$\../EՋBT"xtZ^j ǃ>+`0HFFRD"2y܇{ 3!`!,uϚp8ʪ~4_7껓n4ϛW:pNWDy&{a79O|}._T'm45]=)2w4_7]/:ىlX6GD"\~oDĶ{ҽ2*,^obtlDB f}NVǤyQLǣdbj;w;_Xm>/ YX\s _mmX߰r wuE}tuPUYA OEKS#rr~(fj `dl|B{gw7pvNiD}q<@OO/IO<^^ _|u @cC= zzq\Ðh IDATD"aҼ͵HSyG {v sq q^:: BLDkɟ'Q/xp:]F\1KLNMzq_lX'\mnŏ‡q _p56V VVlv& ͸Qff󚸽02ӠR*Tw TJYb+ezz=VVVѪՔ=?| KdddP]UjCՊ! t8tkٓo2UR>>1(k~`$4/KˢhiyÇ<atP(Z;T**+P)A{pp i~(r4OC`b}$A& P(Й2%E{%r>-uo]#sܸy7ɏrD}kee+tǎ"edlAꙞg.>3DQn޾T*50Z?0o50=AghhqSc=^F!y.e 3?Qk%ܼ}8 ۹10grrCammS'D"Vōz'Fkk)-)"?nҼx#Z:wt8y(@FzAclb2Y]5?f**,@22:{5=; ~65"JZmhuZ^/fF\jE&G54jHш WvjkkTWclxfYR40e\~ :f3Z[i :R"OLrm?fҒffɤv"(\.G"koR_WKN{TWWk7B?) 99XZYsTW1z/׏\.üFIq1P(>ڤ+Cq2j B}4%n#;xI^V(|K+332:T*40V83>1)Ҥy$0;5=ԴoMYi)MvjR$NMжo/=}j?ȥWv::;(gtlۍN|!DTRÇϑCYGO_1d 2&&/L6&DҪyR`[?>urr}GyJ+YAc h`޻O0djfH${ KbS=N'#cŏbOL\蓔DIiO ymXZZf119ngjerj"ãc\.o 6UUvWqGR)L1/OPlY b4TpM23饮B!`2q]  V G"Da!7uAPh4A~j) C%67 SCAJ%e211~ Oj 33F GJ_xPVWW.3U3{ QkOrNV[]l~Mq # (h`FRtH ;!ν1 LC}-gϜxwϽEgW#cc456PYQNBZ0?pss5FFѣjqKptڔ4ߞG50jtZdfe284 C#mә>{#Zkkhnj_}ԉ`'W 76BolP]Y (~W7mT*H}N8FgW7 d=6,~4)Ј̘Gff&S3*{TVUoZ*+*b:Vdj){#_Žɼǎ祥>&dO<h5Nti/) uuܺsG*ARp8AEy9OPS]dhIXmVpNZ|ػg+ν~JEO_?PU~5|>yrֳDscݽ}LNMsS/[\Z"''gN}jilYX().bk**us^M&.# 9Zy#cq|~?^lg &@Pآ6X曯kӰπ$ɻbc}]-.mfssݻZjq{ǿaBIn`0(^}tp:%ONv6ˆB0bW:pHL&prP;TVayup8\.gl$R[SCvrg}}>n}lfKWŏ}wv40?_ۃdhx ~K)݉8N_40S117?KKXJ֖)Q34mC3T I# NEy?::9x KKsNP\KS_[#grjƆzG9|M&ޣ@,sfvԉctv'FV"AEY~-M( J_o7H_{Ʋɘ*mVظxG|G>< 3,##'OjH$bR[0?YSYQƕP>,~?5vjEрDHshP40㓴h(+-afvLa'NeaqCN(/+iҕkB!=pBKN(`vvQ ˅EH40/R赤i?HX9~@|]XJ++N{Zm v.ӤI&W:p4? 'r-\H÷a;xs)uOɷ=8<.//]fqi .]|d/p-ݸs?8^gtlۦ%N'W^;fJ~#HR [VNk`/cD4%VTܼ}3Np-)?/ApǸq6X-+zzirFψD"<~ f#I9u8}dg`g?%EE9:wqԉV>9Ľ])yNWvZ|v=w[oEQdphP8¹'FG9z},-1z=:k|y 阙tŮzzʵ 9pI$_F\^QV*=hgKN:*z2ض.~ј5IyG7‡ܽwF `v.!vpn $40!j^n7aQbe݉8N> ̟U3s/,*(]l6[[[OLJ$aphj*V#iZ-d2jyE B4>VP(Ҳȣh˜0SAVVV9~ˍhsoRPhdeŌW4?s\R*qD#¡0p& jfv~֖333_X@hZ$rTP(2P()~1w]M rAP(24HnҒvnnG'++l#3SGKKPX۶9ݦi;?o@pi4m>oER/zb(EEL&7ΞajjNWozinjtiiۿ֖LzCojzR^Z(UJJ%y<bqw~WOɩiQ鯾d p"&A5>770z=MܸuU3[V+WO)Ȓ3m /O/Q~?ZTT* ajP(,/gzfRDTR^VJmM UlX,,./~b45apxݭ-dBTG]m5>R23cz5|Gػg7 ƃf(CyypPYQ'eaq ׋VEш;.kQ*sw'&463==Dss*ޟ*Etuؽ|J֖f; ur>zp89?dۯa*+hmifhdc^Y)`kKض:$>>d&]dfnZiu'@A'AAaN8-Jnr,=Y̤g̈,۬. xx}I/Kpz][SMFmG}n~,Q x L\bIv8vجV]Պfe2*34 6s@{ֶFC}9G}u辜Ҁ[iI}kinIק%zN byrřuUsgOx☚Q(`hԾ4cZy9U5 ?/@}(/۳{y~ΜVNlPm'iu820^ł$IzFU>OYQpIIM&e4j%bWO[{'gϜB6| ɤeHDh4wHt:$IR$"Xxh20QAP_7;bxG$ $$<sj⏍ɢ/S_sXVU6DͶ'~uU Adumm:3jh|OG9zWYvxavWwM'Nщ^bL '{CF23;{!\~ʊ2ssܽAr;:9rȣQ~:7F뱴좧̌ eKg.S8806:e<1Rtuw޻+Io%ϳ] Ge`Hv8Q_Wç? +rd`H]3 x>a:F0 &p&CLJ9z4uQ<@A IDATGŊ\$`C*p>Z@YYy ?w=5FV=dZYV2,mRLz Ѧەc3,PV{8vl6_SY^GFuUpUF$af LNmD"L&%EET)07k(/8FeE9-ULA[;ΧÄ#|>/B!Eek kC } gEZILJHL&f||"*V_}ułPEi "'::2I4DC=ڒH1 O1>\P(r(tMvx#(bQr@Q~$N@IGQf'Ӝ#$н&QPJ^cUM ?AU/@]zHtIF::״''Udef"F'1)akkNIqEFz9v{R~@07X؋潔H8BoITVVP]Y_^" ry?xHK% D",\ ,_@ϺD~D]$_͋jk3:z%Jt$22p&엙&(F6%JmeX+c^]KnE~Qo2pxCƷšNˬd"GT~sv-!0Aa#=Y D$x%bAXRʾ eP긱,qۥw?EDQGǚDHTöU920}lcSa9 0VBs^4rDAeSn~gJZPR:⏋߷߈AFqbne;G!,?m',j|:߳h.^/~7hv:h"eɎ>CE/|u,\Q]U0Uû\dphE2222vݷ/8y鯛OcoyV_Magiy>22zz+ܻ1(-665=&p8,844)-)F3=3CZjwXCPXcfnN}&<ĢuΟǁ'}Χ?)k\q QeskIzeYMvo⣏?ʵe$667ӟ//DgWD"Ee_MطfHHHbV~٬rssmVֈD"TxhindaqDqQ!fl6+5tgn~YU# 6‚nٙ_fu5~\V14'[_\b`XXXXdsѾ|?`Dw0GhB$x}k6kRARb"SS).*`zfuOH0󑟟fNjl+ڹx>^Yh0`ӟ095MS .^8Gk4*;&˥+׸ֺFAII*308 U\z-\oŃm45`kkz,3[X->se1deer}>|=\yٹ9:zHIIfsS>Î,jy!&:V20e%jΜv/bfve`d-Mj?x1]=e`"5լ#KzEox<8v&wH p=JK_R{GB굙SP ||窎"z&("ۇ,ˌOL319+;u.n |9{Ix ? ǟ ;:hq+ZcuuIX]]`mf/_G_MĈ#W=19|9#F| H kk|>5_S?k,/XXXDutX,HLHPجVrs20kq20@ߏ,dfd<%0Yj)*,vl6!]t㥩8VՊݞƱ nXX\KiI s ,,.M$"(o7dgfR]YIVf&u5>n5ZY\\TH|?x1 $%%LvV}j?H"ި7@?`6)**TmjD1lI S1=3KKsxWVZBbb"ǏQYQH uK(6U~'P{OYiɎ{T~Q5:8h20Qd&+3k9{f LvV&ee34<ۇ=Ɏ㡬>_l~^{a.^8EnܺFGT67p]TW?0@^n+++E\men~lV\vT'&Ù(.ڟUOfl|{:.8<pCݼu9^zmOLP\TXvZ-ܺ}wckkʭ; LWf헑A덛LLNHBKݝ{ϙziadYҮ!`7s!g008LMuKFJzEe` _<kkV+:4H0dfHq8(..fqq!Fy{W#=||b E(fee"R)HLPUY8:A 3#CM?v5ԓʑ#LNMt wvqY{{NCt:Y[[WǥŷHHH`vnN-? gϜ>ŕTVq^Gtjsgzpxlo{$)1Qfu×_]b 2EܽQhC++?|tu??|%~H'5"2oFzZ[0:6a395 !5-{pψJM0r0^ǟO>Uen"s]fssI{ze_}yKWHo^tGcjM> +;l#" LM 2} Bbbe`**a|b`0+/+ev~QY\ZKVTa~AxYY-H 6S3+R Uzbqi(HC0p:4F"NlahS3loor0==CmMZ~|<_|ZN( Hs>U;ο85U <b 2^@0HΑlOp=Yt(Ւ'[.rngaq#Q?n HZI^x^, '[X\\]/**T*g_Xb6oGTVSRXfzz>B LNMщhfIgW7Μ, @IAAAV:锗r=5կ/IS35=ɖ&!9QVZBRR"'**# P__SF@ڑ~]n߹۽•kftl_|%@+W[yS2V52vL5tumyttv׸z)f'W]ʵV5Ovwgn=ϵv 4`_}CzjYqLOyFUZ >9u6kTե+:V Lr9R)..O?Av</%  _Ȇ2'(NPedIʙ(^T.;[E]m _^s<5r6YLLN4 DD$QvS_[Õ1t͆$ɔd`${z9P{epGe`zY?&)k줡ۍ,\~S͗ƶGy46nObrrdǏ)20ܺ}20){>#*+{ȡӟɧq20+ ̵ L{gLLL݃,˴ut2==ò嫭vy}+k IDATXN67HEL̲~3Bprd`,f222*+(.*Te`fff% Q^Vn [[8v-Q%Yb9X20 TՍ`@% C#Hv8%3#._U5&abraazfLDIbrj ^rsʵ < ?>+xՕ@l6 20++P_&UlnnRVZ`S\XHyY)Ckh %%%*`drs$< eRɌM9s$dee24&SӜli"cKQ^ZJRR'2&#427@(͆`0PRR識ψgF< VVWqr:ϝl#YTVɧSPo|jz[{YYB!}- #?0ᠡm^Yկ TV319*ٷ45r355ͶǃhT: t:U 0p8޹(ήnmW|\ @]mS˞i<QwԽNJHM"aw[\\";̈MFI0Qd` $IHd" +oc&pXݤHDM @8&DQDeUG$ pH$Q C.'F(JAuu{R[SMQa$v&&;8j``0^h4b6٤1LdRq ȩ-t:FޘAg4UIN#R;M&xgd8QR\ַߜ½Y$htLM9/_ Z:4k?9ɝ|ǁ2C\ yQ`M|Y{Ƀ!h>7E~e;//]KWEQ݋]44 oQ3?B L_S'YYYax[$$t{?8N8?/Z,h(EKá ~9GBDhinյ4,vURSST}3NrHS=t~c &i ;ij<(s$;Y[_'==p8LwO/$; q݈D}mBcrFa}}N$I{T 1HD ۇ,tgrrJ eGH۽U݋m`fvWŗ.ډD"j{o0G0zdYmffgy48c|g  eaa{moՊ=) {RhVuU%SSHq$Q#Gp0e)`--M8|eNzxh:|,/9r1DIbi&l=df8arjsvVeK=o׏fʐYجLȲ8vYXXSPVTɭmDIRݘԎƫg#,+A^Kb &# ޸Iaas!##HINjRVZ;wX]]ess_KRSS8q]=Dǟ( uj}i_T%%E歮|.$YZ !\# *ΫTUVzvephA ƭwR\FuU%] P(ڎYo~'z:^Nffi)d8IMMQ,I嫭$l/[jJ <.7f1"Gf$+#LffXXX<+8~Tzdd8inW[YZ~\̵\H<)1:}1F/߯-{/Mea!& ;w{ngݳ1T&))2^y+_GSu2z:SG"z!RYA&"J<Amv-Gd`l_Μ>k8  rLO# 2Ng:4޾xKWrsh+\+liurrxVWp܌SZRV2NEVF#5%B(/e{UwQɩi~]=sԭ|t*))$$$ӣ׻ (x>aOJdbrdm߼uRz=3pCtpؙ}|6h1N#'#T@&eRmpODbtWkɩi\{z}wv!2sLLNrpm\uaj \m~+^^M\m?dtt@@ѯZ\\QjnOnOfSd`ǰ &'@%>x{~P"CNv6u5je-͍jt$"ɒ*25ոdeﳰj@Ρp'BBBk[idIRHDPS9A%<Ea[2Kx}>OcZhind):'Kz/NMOF%]475C#XiW#pWs/`2HIdIfm}p4 X/:I$ 2#-͍_BbBzV+5Ug?Ǡ#I7nFA bǏ?E}+{}}޾]*oZZ*K KKƭHhQfs\DaY20%EA].6SS]E~~> PVR̲˅$ˌ;q(('ե+ә{x[Alؓ(//SZ\5j|>kko^r_]$IgN144LKsk[DGb}dzzfh@#H3@ fg9}7o35=CKS#.7z/6ױDnn.3䓙a{OFp`Iܜ+`ɺ bO<@>p$ Ȅ,#K2HDqB$n}? ehjIA~@< p8<el|rua`4ڭVWCY'"P:'[QyR$6}[ݗ ctN4{zEq?!++q_m$FEvVSDŘL LLybpՖ-M웛Td=N`RTX3=+P(@Hd/lT;Yh0d% v3"&d@%KsGZ\2;FF DTQiNuaB$U~Ul?b+Iz۽B[G:MsyU4 [QZ^<_F @TsOՆa)Bϒ$!* 1B(h6##(:EII|px .MynlC:'NLce5m%B!Bg khhhhhH$BB³`v/xFB, K2^(BaĈ:łbceɌ 5@;l6a6444444Fd 9|[!If ^RFi <U@PF%YD0ЛL-fI$$&`01Z,$&%h2"+E>)"4* ®F^OIl$[%LzoXBbK6ؓ@2H=2z@'"u$bQEш?gumgzڮ QPO~4Zh q:qWX\^v#2Pb~ŗd8y\rU޾x޾V֨,/ciEaA>=}wc7u\ȼEh$33"Gh<~!;;ʘL?/+1_15ݔ"MǙBJKi™h!/7 sOr0444\JK,{F߸y[[ۯF22Y g9@͠6DF I@@E@ ހ^#f"/==F/bAF7&KKKKpT:fsT&5UIIIğ_;pj+L֝{T3=3ΟW_]&3#Ԕym --Ǐ)).QA_xֆAz5y%>Μ>Iey9ii\k^ښ>ռ%%裏)+-auum~ŗL&޹?$ ˅gDbkKckhCh Kvf3jhh,-vXu`޳d|CAfw_ [pE9ʲLŀ I2x$s#0!&9ݎ@@jЈo(^=~`W;?Kkk,.-PW2.7CT׏jQ"_I-MH{eёDQTO[CCcFR7noUv;Vyza9s2{@ Q˭m$<_YFDt.o@б@o0dQpdHKMLSolXY_'"E4d F#b8^Ǟɨfp$+]KXI'MMzP.L_~rHvNQ;m>^~E|gdyj޸߹xAu7;{7nRRRL8!11{R-RSS?K*+*HKK%;+t 9r^'NQ :,x^F9P_o]`n~FG8vRSR(s-.*dccYzu233Yv g:.~?p 偗ǣL yLMϐp'UCCz&CC?w].z6xNѣܽw|`jjӧO"|T-G Ǐ5pAΞV\~33;K dkk\.N#S[WHhkB 0;lfYI6Mm:{gNg:׾uiE$Mq'vQl&@,Y 9 ss-lyy~`ZypQS]jpca&_ ukd4n{ o ܹX(b}}:`B_P+wgMƱXb9Y'VRlH$(bOW:(/#)lll%ySЉDx<2( f3>&Sf'UlIZe{V CIdY$IjceuFK[k tw^<G;vStɯ<€{JI,ᎬSѱ456K476@hy+W'rb} 6Q7X ,SUwrh(UJKA6+d >t:-5\>B,ˬ P Ctw#9vyu]ml8}<-Ŧh,(/<,>CC# OtZ8y4~奌W*Msc##crievF:C$v"KW,9u ev;ntz#C!@𿓧0T8llDCAdL&#Mz H&b1VVV$`0Z&^+,w5&ؾ3A7ִJ:`2`9^B8#H%IIl2Pf3Sf5QYQ)'5pEkA:ښxhǞg;inQFoqE#c\ǝ{t>]v'M>WJ~ݤR_r1;zf~ =hO2b_֨ lT76Z-##d^_0nYѨ5 c6z\F&&*YrL/;X\ ^ONQ"6i)-Ie䕖q8PH&"{!{ P(45s 88v}|z,K_QJڦd6Z,|~VWW3X,J8 lj؋~VWÙUULMMa11KW*+>"~h^ÁZR7`4bi)H<`c#Jdyy9\^O!v83=Z&WRTTF7,"%qTIDb Xnn߾aFɻ`0e3=}\01gnZGf3%z=ScIDAT3sV4G|`-* Cm Sbx,/Rn]~wc8?ll(--ezv#wׇMOYj]Lxd2,/gjzyb\2g\p?bRwCԸ\CA#ܴ47QUU G:w(35=Cǡ:"1-񲟅4dwz)]>_ܺ춳c8%IyC֙HycGn , d޾sH́\93W4fΎO{DbXl!]&;;?Oq)r2iw(2 qrM x馐t:]^AtU*`Us\|uVCC]>믽 ߓb1y8v{($[KlV3֭Ɔzl6+247=~+22ګpug\9 IxFis;m=+\ RcCHJΡT*j5u˕$It:-aj] Ljke享# |-> {y>:Ӌd&ff4ՒGZH:NQn6 F*+YXT*xeþ}Ѩwrh2;Sxج{7f,Xb1Fmo< pW*,C _nKKKqeY/{D9y cfW.ĝsg=';ˉTi475`Z]fFm6򧓧\di;xv6g ]ܾeϿFY$Il@ x%_6{mBDчҖ:Nv|SNr zDpb(=!@@ <*z'| w^ @ pmmx"玐eNeroȌJ&؛ƒ@ּ!,Rfr2 @ ݂@ `WF u`IENDB`gammaray-2.11.3/docs/manual/images/gammaray-paint-analyzer.png000066400000000000000000011536121413310443000243100ustar00rootroot00000000000000PNG  IHDR 1@ pHYs+ IDATxwי}NŮqzBO8B $ْ%9w}޻g?﻾l]kݽ^9Je @arݕ4M80ixU>d* @ 5 @`!EQr<2͉n@ P%"‰n @HL8&=pcF&qf@.A6ݖy`ݼ\"@ !Bhc̾ >m<&BX;ѭ`~ӟ|IqfX&rL={80QG' !zg(a~KggW{Ggam@ \STUݱc$I8{ 2q5amj|>TUݱ%EQb뭭î!Mӎ?ݲ"#544ln6Äc;⍆a===;^z)N^֭ۂE}$IɄea`b'XX,~t:CQT3fiv|k᪰lO>Ν;׮]Vv]>| }葇gkҙo~s͞=ѣ,2' $IzgNYVկ~~f]wEֿoD\!tw}555+fϞ3nŋz?566v'9d3eʔ0.6aiS,(jk[[WW˲ӦMy$5f)n$I]]`v]a=r[O~T{GMS_Ư~:TVUp-z?oߞN}/xWǶg8O f(nby7Mwv,ɴ,KuttΛ; IOln[&ikl^ ,%l&7cƴv FTTT͈as)ٳg8,.[cN|pYA'Olyrh;O(nni9|<^&̘>U}bѪh%0||`:a9P(TGU;q4L !ryaU`S$"tjKX,ϝ;OEQ>9tpW+]7fj4 cpvPTf6r9.\{w]wUVUBfNuuԩC#^{6s 'xyŎλ7/y؉SOk!~T=v{/b1.\8mnj|{}fw6nX!r8nܹ d2Y){RMM Oڐi)j)h*b&z"@,/}cㆍ-m튢ў={ܹ&7O?m-N+rQ38f3NK+ˬصkgwww:կ~fϛ| XrR[[EёL&W&l2ReB.KrY|QL6[Q=y}ᥗg@c'|^/qm߸aooVUU{܃&<>m0>[2eټ^xXs{wɓ/{t+ B2`aCjmiYxagn0f̜IN i]=M--P0)OudYiaL6eIAֶl6w@ ܸ(x,Xx9IL4cݛֶdW_wG|8N?#b/\e;ܺu۾}Z[[ƶ¸`5DH!L$ 0ol%"M]=Fp]~.ڧO,VGX4]SSðlooEA):IJ_^J M.L$Ο3 z4#GeNɓ.\Ȳx$8]˖-eY j*B|yǎVeYbY;6lP[W7AXYQO7n[-_|4Ӧ͟,Yjջ[WW?ڝ |ŲL4Zib "B,0Ԇό7l6~R}ݤZ QnMM69yv0#?(zҥpq4(v_xQQŋnݻ<V^=g JOr:}$Ln\ӦUU;!(ʞ-^vWVVٻ9<Ǎm:N pU}uOd) ۻ{v{}^gDV|A4M#>|e˖'@ `d"9= R@ &Mt:}uWUv;0_xn1@CÔal٢z8v-^o_1\]yORy$r` #e$\ښjauut˽bqfjjgeK!kkk8EG"99rges~1A\x0Lf|X5}W^1P( ?zCݻOF{Dir|2ۂd>sfc焐d;:;c B/s\2,л:;O:u鶶vYDQ]S=yҤ9sBqKG@ & :{α1JC%i1k([`oXRlr9ZhVOu"}.]PMn3xB,@0$I[v5.o+岲,C }A$_IvS  Rc7A_,K\+yu"gck{I</vi4FXL?nd+q<ǏLc%[ !JD!xE]+;o2]! nbatttx^R2NP(H,;.I*x^a4UMRN&[J,@&>} w=^t !Lt~00ْ(:2M0 r-r$Lò\EE+zyL fjx/$%EE VH3vSM ޮ(JR #fKL*HYe҅(vPuc%ee͜1 ov65Mj5W=z^/ ɔ"˛6n~rHGGG__׷8y(=0}{ٴu˖,_w>g&E+Wrw~ (~ڊz F骪 ؅ >RU/VYQr9_{p8_r2z?>p}'zM_| {?;WJoz*_(ߌ? qYd12ߜB[[{kk;W>̙3niWvnMħp<_QQyjk8++ :uZ.v2Ux:TVTTVV4]SSUVݱ]݋Ο6u*d0!D_,(J㬙4MϜ17{&JOT***VݱZ1GE?@QnJ&o I:$`PAHYI |>/!eI03: VMS-NDJ J$iXI^O$nwÍ}i%p IDATEQtl `A%$c @Va,γ$)̙s _iQFM;+oLKKME>mj}ŋ@c MVœ}s?X&Et%hkt8LcGBxVa.Y\[Ss׾RYY1s # >|$:o0 àiZͲpx}w޳ͷwݳi#51dl\x&0Nc%-0Da%l4ud\>_+)Zi%$#re,֓f5fx/ÜҮVk$ IRxIee p.IXIyy;P'XI oX+I("EnJ064-c%~|K{b2aEC o[B;ðV {Lq3VEEQԼ9}r_ܴasNsR};_QQǚ[Xj&5izj0zz?CGjk?ؽŦIuGM&c>MǡOG*ݵ 8MEM2OfϚL%ϝP__s=$uvu_\#IXlUvf*͖J0Vx/^`u5KK(JE9}ж}}p4k ;LN5+HE B8l bkzK &;&\gx+]'hMU'XB M~77:̔Y/eB]ݕzQ&W K=_ֆ@ $IuXnZz bFw7Tۓ>+?N`Kx_8-L&f35^ EQeeCwm6;8BzoPb+Mx{ްaZ_Ķ@ QaFSSHq{ FGW7eƅblje?-0>ѩ@ ! BN 0FLL$ԑ,z\k݅AP__{fΘ>aJ _ٵwX,>n6޾wdaʔKbUBX,C;T ?ӿ$IVeȎJ"IBSB(,KMVMSNDJL!d%x|\Jh%`Vn.!y !3q 3+NflNX߭5!+-F4ͦW_{c>e1'XT09e}l {M"+vnG2޹jp[ɤqv 6!p:]VGE :;ۊB!pke$RvIte(Vqfjx/ˌv4%\+$hɤR8a0\.5GCZB+) ӉBlN(Jww;V E&iZgg+VNްدLioآDQd?0x,X"]<1 k- y[B ֳInX7wN.LmTjM S8g߾faT 5Qe}oeKI'@ nF liiB>:RFBVU}|U*LrhFE'*ʇԲ4d)J1+@izڴi#64I5FgmBEU-*_c") BTX4!#nw(& mn)$It!꺡BPL\@ 7%BgȪE@6J䚦)Aq.il8a@ӴL&i*Nk`ףn:aEU/67˲wHY>Ӱ"ͶItH$vWpG$I.P%N1 7M7I&J&D?BKۈHقysxk_v]ls`WpJ$GKhBN'f` ðEYSӓaE|YYpRD"v{+ȳቯ~eDz˗͙ݨk:.qի/X9 !DK^ J?a J[GXɸb% U&/AYKog ,%Z2 C\V!(O4on탭B\.0 zᰢ(4Ms!yBjS$X7 |hpmy/pYT*ڄRGT&TVVQ,e$UXB!o%)ad% "XW-UU$@ݥ뚕`*a%z55KST +^Du:b]cx$X@ nY !t}%XE~ߏ4!i,y4eYfYV$>I@ ܚ FRlR(ia}>_R+L&IXF,@ &ECjlvEVB,( ÚIQ8]U#a | aaya@ ­aj'hJ<DZs䋅)*ڐc xկU=UU;qT$˿so+0N:+!TU[rņukǶ+4wo_s2E 鍷v?y q60 RK]R lS\/gLͼR +ԧt~/VPeVzS>Guc@}0Mopey徚ފ+J,UU8)+{s3.vgϲ,hw>po{Gǂ۟~?hՖ͛L;^޾>VޱcM-fΘn[;wC h^hAmMM:`Oo/Dz.fu}{/\fsp8p<{žXLT:lCsHRA MVE>L%$Z4+룕9Z nwsxJDJ"vAW98ß4+ 0VJBQh%ӫ(0 Ⰼ$ZZ}>4e;X!jSs?U4M4z[Zۺ{k8x{>1}Z.3~klh!BiG.\l?wn٤zAAB`,{B(}eKLp#G]0Br:BP0_WV>s?qϝ;+-Z0jc^tqSs/?f͘^WWsĩg'͛LQޥKvY|!WVVaW ̛;9t\ 8𑣙l絼㈡R"VBLw%O~`%OSp֊PM$ZwؕAq@;$C6Դֲ30\>Xtw=~xkkֱt;M@{{ v=(:ζbak JVF"XIoo6'cY"b=̏$0)&EQhVH3|"[µjFuGdRD+j2x8 ۹b{lg[5K?dd"ɚ&#< KG81O&#Q շ?ؙqFp!GE]%,c~_2RU"{úP0xvdEz=H8lyhc_`FE$Isg-\0e1/=oB0oƙ3D}ԆUlV~U+[K`fF::;g͜dтc|ra79{6n8|X&]n9p•6%dSR+yd",7b)Id*u⭀lڄLT}o#{sm*.[ 04ה+Mx8kvAegΘ>s-?o >r˵iaGk͝?A{u5u5_3p~7 ZWW[7U+x=E v:cDQ\t8p=wZVl8@r(Lsy}×~k:Eg\sb VHCd{ZwpKd34@QUGxA/@5(zHIY. #IP]]ө MUUXSl6c%i̝0(jpX9Q6`@51Ms,IR NA,@ ,&IE (38R"v_Hu=|XO|uo@ӴG>ʊo|k._?x͚+_$魝z=M߲yv !0߽WO$@ 7a 'hB4>FG^ @ў>_VDIQ|A_|[NmhPU^OşY8 IDATNӴ_z#@ ,4]_?df$}>5'aEUw>t@GS䲥;::cS[Zz{jCް}û#afWWr@SVVvMM0vN?+"˛7m B"[D_,~mDzC>4M H F:HnvC&(Go)岣 B0*,K1U*!QUEU1M%$RQI !d!VXx(@+nwVb ,%[L,kq,%4MV!|z5E֕P"4 >K-3Po$V!Int ғ2Lt&n@oĉtzEM(ZdQ㬙'O+ʮq9q%n׋dZ-`µc%XJE"XIoo$a8**XI,֓cliJ\.32NQT4Z$L&TS3 OjFuXoL&HıT.(6)XkP@YY&˲ӉBs(XI0EL*M:;[?tG hooJ^ۍ)jmLe}H(Ҡa9_Pc\fHuC3L$|N)Q&򫲬f]U՝Wtbf}^i: TK@ n2x뺆"C`ۧL}={ar>b,}!EacOdم4?rv @(zҤɃ# [+v- ֍qWޑH$o!~sdwt&=vd:3ggvmMuGgysPq»{s$ۤѨmkjL4QV746=0u:-A3O[d(wT$W\1cO9[`/W߾Y\\aMez4Nq@$Aw~oؤ +K8#@j=! xNoW ~ggu mVR-.*]9gd MǞ _"g D&WN,Z0?Uq-YVVSQQF ;on4sUΛ3-,˝8uf˸nǿ< z=EQeƭ\l$I;%ϴo 6FbMރOW;u!&GޡH} qOwFc{'tvZ"@%_Hny,cv:NIħA:;;$=J CFmt-EQ6BiRP(0 tPP@Z-AZvD"qKz5?T,746>nl^;tɓ= rs7_yrSTJecSsSs `1.hko8MQdIQQf Bvu}>7466QRTr9cX֦ NJ:B`t:.\hZ&r9J omA3L9HX-2ꣾtuu).rڔɓ5GQ^ZRyu$I1z\阞^L;Xh4N2i=DKk[aA~2ihlbƜ6md[Z[ZYZ-&N4˰ZO>c6[X*ϓd@¥X,t؃PEyV=0ȱkbaYrMm{{\./?Nם9w>7'75demݶ0LEyٜz:l6k]}%=ct, +n0:_ ή 4M:޴4^kmkohlb I6545$ RQ^ VW_qc1EE`0pI#=zܽ5W-Y4up$ 8a9Y ð 32ǵ7669~l)abQs[oh*m)(/.,@{=ϗ)͏>`阒+Ͻqcyq\;q2;+3+3lur\阒p8֮R)ǕZA_ׇX~^^nN6˲OZ-mji&X @<^V^7+W#mtߙ2m@>'=NGiyx'hw,~Wo(ԇ:H(H ht/aў!$(XfQ$8HG#XDZw$8VF߳Ͽ|opA80O>CQv_xMvݯhƍ}nϜs9Vú52Ʀd?ٵVjz6[sCv3*RiC K,ߕ._v*u$mZf:V|( #@Eh!.apq8Vwof }vNo2Vj4ZQN@gg[*JQO@wwG*JEJZb"H]#Oo4*bۈә%*DB#1 KeA >їD_r̄BT#KtF% vl6h MGݢ.3L{d; ==Cr9`6[T*veE%&Y4ZyUTb0 kP*oXΐ[q %D"`ð4s2qb0yBH!,HFWA$c,q'dX8u&oA,^4cyyݶzr|9þv`0ǿ<ٕ?ohzzKK$EQZK$ &5+9O^t:&>uʊƦ-[76[MQ/ G_:eO9-mxŗ^Iޭs2s^ñx||WWߨ ˖bcY}֌Z Ihkok !IhLI1A 7 ̚9nX|i03qZ-量~b.^|9(瓡U+̟y=,k:0-Z0 ǿCܽ/7'[Vr2B~~y9%[`A ( W^` nSRRx1%'Np1 Y-ֶ7nz6 3Mɺrڋ/h`! 3RHfmݡVnDNjX#ґ<]QZ<#D;hMl(,rJO٩/Ϋq,BHYnM8IAaz&xO@,KI4h $m !aZvX#0j]v;{;q p u ǢޝW0;[uĩ!IM@Yq+E%~îP(pXһ{ֶ׮@y)' T(J!<ٳÃdr/++sӆwЃm>m+ Μwr, rsYY T*PsaN:s9aZZ[MKEbh>~xSsK~^nNvw}P~k|~~.I $I^ ;a F!f$nןpS nXf+P*&q\0r:8ge)(?nl^^kwgMT0f2V AZ&Hp7ZғoXX(M&=LQ 7}FZ,$AAQT,n4W,[TUC8LI+~͙,:If(JQ*݆BpVKK;Qz!/p8lcq| 1C q4$D lN[0wNuM3y聥KQ$qagϝy 7mILK3N\y\Lݷa+e2YR܂cbXWWϲ,B  b #ú"cgy$IqSԅɿ[Z[{zE pgY! ,).7vLcSOkՒ1HioSR8yLcS35I v0ދjhlz{de~ԟ:P͕+|n׿86vliyلK_gJ:`^q7PaDF1t•˗H$Ittv^s‚|A1yAAض(/:vڔw,$1xkrБTTXW^٪Oz2{E %EƖ747Oܺ|~nZZ}>38&T*U{Gg4Jw) R:o}c#@ ;~tJlx}7M{.2յsf IDAT} عk>Rq7ucJ.^&I!}jFxr<w:주H߷ %_u#N_jtNcڮnIO$Gd_R\\j[<أpǽv3wvv=CHnvc8dCldS5{jjk%m] gz>F`ޚ$0HHH3yK@Pc;h:|X  yT("{w $I!4iöaxjղ%jըUBtrYWWZedLJ4 TcJtZ]gWW#ɲ2]2J&QYY !Dt8 kKk[,+)****2V!I2+ӥ0 +SWorsYeggBZmNvNgZ ʰZ901ϯj`ggg͙N~ÑJBTJdtv-iUsݖaIO7p8fdXe2y^Nnd}Be9vՒ3cKuZ-ev[Nkni'+-Z-!t9 0yy@G xgLi M, h5!i*sUTVŒrF˱%SR2x.JIj*-͔fNKáPH|$#ÁytE@@|*jN4{Gӭ xQٜ.pS Lfъ,JF)}}_ :*->NSzh5.A9*Z,2R)&hӗ[s#GC^r%$GC}>8%Az K/ð ,`0 : !ٜ,g^{[?%77Wyf9K5(Exd/~KiK⃓j9jB(Eo$~0LKi&u)(qlH0 MnJ `ZE%<ϧ .AS w)KH/}CHTT#w,A(erROIu^B E?TRIRPIVVv\ IRw[TeO%wTYBBBBBBBA &!I n&?`IHH|`|<%, j &(JkKBB}$$> ) $L&mVxH' ò$^rא)---baF2eʔOj5 ;B[,Onnbw B"?n<3G B$Ž3յ5GHR! ZL$u>)zTڲ BJI<5E²pXbh*SH@* B(թI%Ϧ9ðԵbԢ8R"+e2("Dތ"!I*G* A6 !LujR_e7&En #2]vZL^/2}8BQX q?1{}G8YC+P0x NFy2  !!ʴSӧrzTN*T*GrRսTN9JTTrD%I^2 bU2 Ϳ LUʁxMrws3OG}2׷`tPH< `hj|JHHHHHH B[\9.j-w;8-@ܜeKW@v?ttvd2/(78Jut|?yAT޿ixߏySѤ10`x P*%`YrB" Eb)]]z޾ XľC>^oyv[8pҎ]{zzVeӆuoafL𷣳o\j-`\wܽ!Y3/]qp~' \tGW]Sy˛}gشq] {avwX.,(xϘLή-[ߺqN>},^ήnۨ7|ό--{8v_v=Hhb|硾nPC桯҅D eeZUvwKY}3Yjb>:ܧ+< !l h>}(!!1aC[F݃Z7?X0?ϒ>xQWF'%""hqalH\Յݻ|(M8Ne8MrF #IJ|~FV*4M3Le9JܣO³Ȱ"A|:X,'4?HV91uZLF< &!PT**Hб@(IjE&,տ5 e+/!t0Ld2M"ZMӱҼٳ&LQk@ b9 $@b @  F-yW9fNk8tdU# h4? cpܻ=MfFbU{MeSrnVCZIq!@BPBα_% H_}&ikJTᎨ}v:F}ڇ%$>U<_Wwch DZpeԼ|%e٫o:|t̘H$j0!azn9p|;;p<Fúի.\N{t׮niiMW\{6b&JK<9ke,kMMsssHf.Dڶayܿ7l==n Ξ5s4qFt:|~?/]ݛlz}ɓ&._$ֶVys‘ȥ5`߳x:CE0$# !1DF`{=eKdI< X ^1}lIӧMW^\bu :MfNvFOOYFcU*c̓Of5V  ^x^|ྍ_>z9gTeKҚ[Z_~usGG'+*,8ryyP8mt,W9BT}'cﴵwOWNQWo $Gn 88/ p! A z>9(bL,1"5Gtsh1:eNw6e$nbV\TrQa&M8}ZZZ.^T)@ BKKkNx|Y3ϙ=+O>CQ<|t3 D 9%0 j4 ‘dgϝlnmoú7˲G(0^Bz'~on+s0.YQ_jjn#O\We ,^b5:WbZZZRX7o{{"шjZl|}C㰣@aEZ&01Pj4jAwwOk[m_|5;4S@ RÆ\H^A0 S(i&SsU0,˞8ykhή=M2it3$H-? %_c˲n$1pޘorDz!j ABMTNdzJPvcl< \f Vt<(AC0%`ЯZiiF)4q\Gga$esϜ9}I!j\x<_|;w=~b^_ϝ=sҤ99Yp/V]S[ _|镲 gϜ1AR͞50?_zw$HdLFoxAP9 7r:LA8uʤ5Mww0$x $wV֖'B!FN[8.q_xŗ׭Y-܉Skj.Y5Z[Z-a*L6|P(+'74nΤʉ3OOlFxx+nfr 3P gx$RE8Y^[kkrĜsӣLTlM(&9Dl$$$>`689ab#Ej/ZA(`g?ph%-w\^R\Gz{rssH,).ήqJm A˖>:t[wv:sn9 uUZ*ˊ kj.^SӧOip!,,(:|hNVI_\rST%EnRWWoQE{Ɣq C|ҥC "@#\.fLVUu!ji~`|ܷĩ3Mnfz( L 3ooЃ\.geH!B3=|xE3ꖢz]`4}==N=򷿟3sɕXL.%;3aբn2kjǏ-/7$HArsr[2YA~e^ƖfgeB!Y5 NG|~\R*1M~^#ǮݸQ:ȱ=7hN9wɕ+:(˨!涷z<}JB鮮nKYPAPظy˛eebL\ 0 d8_ONQ=:PT/ڡuо|6uyjC0.+B( ٰ#P;l>$Gr4jdd!yŢ$a6?IN%1ғ؆3$FYԵe$h4l86D3"+O%jv.BB*ZRIT*M*):"TRI݂ T>Q3LJyF,|"]A>;ljTP( b$p0թI%&x;xNNX,*nr\4y>D@H%%VzEV746+ IDAT# Ə-0h4 S̞o엿XgΜ|:4٪s`VTX`eC$I4Jأwwdg vŘ@0-pdْEO?ʄ#k* u %ED\\o?> k4pTAqG8Jf񣣗aYwtRWW5ge~s>>|{!baXTvG?^4|n~˕-`BAѡÑ)`E"a vkIrolxO-xZE8+-F#b7-Ng>-8YL&<@gghRifeEr>EQ6~/Mn& G"BїF1O۷})9֌,pP(DZD\T_~Gl D<!HDx!!1\&K'!r KIT̚npM~%$$$$>l I(*92nI\. kA"ۇ w  "bCW.KtRl OrIރ\Orf2]eKeeeeeG [fSFqСC`IHHHHHH|YPPJ#XAZ[oY@h4:TXt,cLABBB!8B$N RԢ/tTnGTX]\*I< uu"I)D\t(;ƲhJQ$|~NT&>JZt9a$ Rt!$2Bt(YbXJ A=0ʱ!*0DZJ!LujR_e7&IRuh gZq\<(yqgTxGV R%!!qי|ԻpI咠fK-V%h$m*:Ct9YO.F]+,Q@r50F_Ԍ?$GhR;kDݹF<wŝ5fJ%b ɲlwwJg AD2ytgaal( @d4 qH%%$$$$$$> bЖD"jiig`IHHHHHH|20fma!$:xw, O&<74m8`0v{ A $$$$$$$>8{Kj`"#! \.<0D"AEQ#SKBBBBBBⓉ -un~}z~sίN~s)+?Om?յ?#>100xM'azzp[N?_{СϹټy]_޺&^rŊed2_{_O_'nlje=_;Kt}߿ẏHi{[[Scmۜ{^|.0a0|jJ!x=:S2"=2khZ3ijj4׭ƙ 89՜8ӌy8|>2ݑfH|q&l>rq&t6fjDI*ed:ddd󈬕D2d||$2ۙe%L&'Ɔ痛g255>11:08bq22cFI49΄8r35-e7'n9LNMٻo駽goy,olh @Z 0 B(!P*D9(%ZM,y3>?sf4-qéUB;^{?7}|i8x^ze} < > Yi%U+:@&gJiSSSSS1Jy2m[A໮+T*r_ᜦkݳgÏ>J664䂛!m@pg)5ZZ :Cl6rDweYLfbb|/9{/v]066lk8͛U*`e2۶oXnukKҝw￸ߧf/O/ UVg?o|B*vwsSSP_(|䚫;B:o شB)0*D1dzRT:ShvCCCYu嗞}a&S-O`id2e]]!K:;>OU'+TH$ ;J%sNWꗿ8il4]^>{~K]V3r#N stکsJh:J_xMm<6SJočcU֭Z>"gy aR󮡡tɒ%j ܏uG`% $)xsp>/J/y٥4f/ҹl6}@SxV\>{!|n~WJi&`Lٖi͡/eH؉Ĭ|!Iϲ A߿_-n,Npn/, ?3|pL&/׽xAR j%R"3q Eq&aϫEf<3}/r8} "Mx|9Fq&B3L&R28D"ف1gbۉ>75˖,3ZŧX3\6bI{{Wd§J<<<iڑ:8ic\_MM-&ϋM'T_.&BC,?4qj64rbBq4 Ӷs:V*H͘gBՇ=khhhhhh|}@dن8M4444444OPJǩ !q?x^^+Ůhhhh ;q( A۷O-BqL~bIjhhh7pWNC ؚ5k) ] yZ"wxdYLx BȜݔ, 0̪khhhh,Qj?:PKK[S3illnIPXZgFD"L|]] L2\.W7sgNg#YgJ&$L-+2kh"3<"I&&F#Zg2559>>:48bqjrr|~aq&Rqjjr~9,Τ\.E3!$ΤR)M[~\=<)lr!g;],`ٶNf>..džx_Ykvf"gF.=E 1*⡁ mgV%O,VA5W'qV 94-l /LGxsܸgGc>ue/ߓ N,P#_qޙ"wE~|/>K9azXF:Z {Zs, Bry֩3g"ĎG#XTP7#Utf:s譈@ em2z><4I棃u,L s'`B@ lm Ԥ3M1YuE 艉#%&?nj1PpQkJ_ kڇW&gW;ǃ\ؠc'Ӟ8\`}G>!W !(jI""|``16 "m@ڄ'pCEa[;{aoKpYld.@uP[ىu ~oL2eAAzǿP# ;"4kIu>*ue#"1F]|u5ؤ,NK}q|"%rmcccWC,S tpYtnI?onWv;/\ D[&I}@@k،)΂9s+0ꈫ;9uM!8YyzR{`38V92dkr䋫G4dqtQV :H$R&D!x9<3jA8e_ApDp;bH!bM1&g&H$#BLl;1FL,f,"aq&iFf0 g˜9-`"M(}Ji#F1dR-+!ed4$)_gG`/r=T L<7.`ʃgF3S . )>  ^lVTML]159Rpaðf&G1W ; I>?f6T#" cBI@`}2靖Sq^gӼ QPdy>_y@$fsq&=="Rd% IDAT:3;ɒT@:ȍ94?>40221cɒe&c)]]+"MG"/-[*|rr<ΤkEdbq2ΤsYd|jzgeYJg1 ǩE47R Dljjihh_y^__rhhhHZAOHB!Btw_|ATJe֫;99|p;a#J9cF %@"G T@5džfX|xp 0zU gYYɝ:Rx}8><>qݽi\~d$=>r'az^/aV Hd2"â;`RZ(׭ !"=GG`}Z)"`U%vKCCCCCCCc 8xZ F&c3p؛ ڵs~,嚚"G`7>ZCCC!$ͪ%r?Z`-?=_̢D5444>hm:Nu`7Rss+4NICCSdZ͍3\]<g řd2|0<8t:sLRt8 !LTę$8iԶq&ccÜG$ʱ,;drr,"gR*MEf3 3dz85511#Τ\.SL*r8BLJԼuTatl6'?s8ND6m;2W&\pM4444 2:lێL ]L,˚]84-˞_93)bVBI>_48l6i^I&4 $JG!LdlI,ˎ3dK $^8'|jj"IJ8bq*0b3pNO"MxJgq4}Eɩ<+ ׯ[w'W^}.V "ԯ-PWWv tfdd^ҩxUwݳ{+/~ݥOt\.wwԓORB<.pitg?߿@{[+PJ7_~ 1u9/[*g9xЖ7ɪ'+_U+q?*CKtvRJkڮW^r]s~O<Rw{444444D"ƮoT*=ғO>y޽ ,`Y566>>1$#ϵCwe&<߲8/;v펻V\^VV"7XT+\}ն.=?H?qk;x֖իV~1>1 V(Bٌmۓs_!ȿtʲH5=1媵Ų"JE)Ь8'5innH"`բF46g2p ZPf |>_.p݈lQռ;_K;wro_"J !@\뺜ϼ[e'){ЖV%؅l|qwLsccWtyTRó:3NNN~_w=ꮻjN!I6\pU+WZ\wwuB~u} MM_o Z%r\100g0V5ιm۞e2q8-$d? "b6v^4;:|ZƛL)f2=9U$oiiIӏ=[MMt)+.뮽 +\{[gugϞJuIG8R !zI=D\޷@Rik9gV&~Kӌ=}yW.٥!s?䓮^Gvu p$q[n#W_(_wsp4444448烳?_uqm{͚5===oiuHmABus&G!QT̠GFƨ= ;7ꞙKBU@(a_NU6C2F?dz.lnd.|PwuGjܝ1xI{{kx8x`XP588i?za[@;i?d Vwwl.VݷX VOb5X}}` /V544joo_cqζHщrJiWWh||XО/Ǔ{ ϩ p]RuN!J,r0fm&׾v`ttJ)bE RqnfHjթ"L!\6 Ǎ'\6 \8$2 vV3@6TYjޜL&H )È~P.W@:A0=mJ%"Y᜗JB+H&@E!)XKҁ3 \.Z2h{,i{gq 07C=8H. ,2|nqf+d*s$Xiщ?8HC3,)LHRbdqzikn>Q\FPJhwı Hy~Icc!ęE!rqb6rjxl6iyDH&ADL&ФZZZuۂ x啗gזW*f&lB"fY/KCC=_-W"N}{aY 7ܠȳ3Ӧ58>y?vnZ, D&덄`ihhhhhhH$fIL&d&PxL ^}U$HBMG`ihhhhhh0M+PKjϞ=۷?sL^*ˤ baLOS>\ECC-!p'jD $V~V.a[vEBqz;ys'k̦ɖNր3 } ntN#GYm6e9"LD$Nڶ aD5C6CLB?!D&N$D"-ZAp]wRMMMj8ytyno\A/o>L1s[&uW\-NডW 8@ ՖXeF*R?W DsbG;Sm gd켤w2`V23lM|}e-[&Ougɒ%rac@ժԲ,By˓;::r9w]WOym۲Zur97 Rj*u&`ɴL-XVc%IιAtuuB tP\ elq+V=jrjm۶- [e,D3MRVH Zq$iomۖ.}<A8l㯼){Q.C AwBi@6M 8qvqª rMӔ8vUu]O9mSJ}wG>KaH&V c̲,JrNsR)i%Ka2z''QeYsRF+'KV>Kʱ)_-yR1 CJ|1 Xc[PrfT $!5kVV˓mNӪ9C$놇g2l-|س !:;<z禦fQ7 3K[$Xq&ŅO:OD"q==o:0_|\.yim/TT۸~ݾbj;``Ǯ'9l6ʫuT*KmsvdX '@ @*,4Mە>!Bժ|93aSOu`Ϟ=j XҸ;7vϽRG? NA{_ҽ?OغQO.PקI+.YR_g9/y^PwLZCC} (gUZGY:0#h fq-g5a+oVy?q>8ZM]d9Ƨ8"qT`^ ʕUs#_i9HNy9!Q"X0}j}ݧTE,NeF:m;vz'8cK]3naO|.w|葭[6˹BiX.u M)#H[7ߴf*!Dq+XVOĠŽxӾ3COz>a[M6 ![4w:X?1?kR_ n\$><]K\ݱٜseX:cYzyLj5ٖ\\egw IDAT !9K;MӔK>:ԮD"!UWwI9\LϒjRL ʾ8BēϑV4I0G$>w-r)DfTI& @? YQ a+5A@=TYgPχypsyej#>Bqk-a%lSK<{UbN`/:ɧoذ {ɧGGG ì7_+MOO t:\W> 'Y8pglmkoSknmi^z<{^BȒSO9(=Ԡuҁ郷Jϛ4鼬>g??,V:.iQ[ 9ώmj jБƌ$ o+̻\_I`F.3jFތq%\tq-jAٷmAUE+=F(PE#:pB,a;2-J&=I"aBbbv CjH&n <(!@` A2ܒ)L(!SEr<.Ow^:zNd-Qؐ&5 _>҄IJks+3"Zlqw]ݞhE N+Lw] O\T'jYpN{q 5BC:HԵ ,imcR;P\unYAwVb[­q'Aep@P]aJRb"CFJu. W Ye8z]}Bdכ3zWCBBRA:`TY>Rg*atU Ey&qfԛQ冼S abo !2r8jXZb9rPͧ7vTÍoܰ^~nkmmkmU_?B]CCCj|a}KRT!,]clָ>LgJʭ#ukukl|MMWQr>ȑ$J|P bv:{B:uZ=0^pGe4+YRԎ!-l ֣.HuJ"W WX(~<,< &x ??"&$v P`TP %E00ħ,0-.]' k:9PȐ P.­BфyUBB/0)DI-|eaM})2#J*'e26$\PL̖s9N>o[FmnnR/G<Ӟ aY#UŚQ9t~rrR9RI:͠H"j0{Q U?qBH;(,̖C@A bږNw@K70>$@ !d#C(!@(!EZ@)P*tj؄$@rjaVU*qq1rZm#wNWIP$>KEHS%q`a,y'6-|T!)!ʭf2@^xe&R4ijP.NAtjͳFʷl\?psXBٟdCaY6!oL>PCCC EQB\S"Cຮ$Icӣ?1,%_K=d$5} WwKḠN|P}X Ksv6uHj5i.k,өT "q)`i!`03H1q~YHِAJځ4Wmi&  cv$TPJ>AHv1vL^QQQJ%UC&0.}2*dATY+ BZ}Xu׫%dz54444:Hƅ\P4DPb RMmDI值G.j04P"x4WI:hAԫ!jpBPBJGgz! 2c150F  y0<( G:v "}D EmoPމ# UBQqa&w$4jUFTF>!`^R[#- r4'š4><{ԒD" @_, \H~} ? p3pJK5i;e7WGHTxdU+ i 0_Y` wI&s賑 Wyf҈`rm0Ua#c@)ycThx.$N$OI3(GА!Rg*^Op %+&*]htVâLMNb/ ZȕA숅m1j*IJl7j}0g7 =X.(Q*ebs:7Kp1}A! 5)!0p=c/?v{ \oh']2 e%$(\qʤ&U 1 J!\(RF(rusP)eEK2,e3̄a@RZT愡ËP_#\'@, RB/~VO$ Gz{P|v}O rLa@:QNw s@EjD"LmJ&w-BdLDf> H!dfmB̓PP A |tbUUB9T*%JJ$?9nx sx:#3 %o$_ѱ4H+\UPo.oe,aGr$˫m˫HT@-}3(×5pH SKC$ *xPBmC|6%0D!D0J4TbBPRAB  I"A,*j*>{#Qa8dQzQ+ٙZJUu!l7{EU,i* fTgh!Di0)VrPlb\@8fM`į{̷XByG8ۋŁ3ɹy%*rf1#΄ { K$@c_t`h&rP'7+Yѱ9@]P{,19AZ{U&6aπsN8uyB,g0(+´ (JAMCm՝r/e ur|syTcRL*4a9B:14$ShH̎8cmK*?˞Hs|dX4W_ cv.xr* ٞ9\] ;:g8Cj:0<<|СEUXh$`N+('a@Ϯ9" WY$Fʂ\qɧۢ* GLVғLE62LlfpQP.R+B'J!1 b0a2=(45 45솓TrBݲ s3TEXjjBVߡ+h=UY @9CI>d-8Ym,# ]~2~$ͨ%ӮBS1P2;)?c K!-!IH%b4|XDGG^1 v']w0f {xHX~CGwqw\z^d;/ln:}։ό >5jM.BÄ- tس;EHX+TeurKt` rulK.u 7s$zPg0ΈɡGGbW9*Iz!Bm \ȱ sSjF2IV/u٠\C 8!c 4`Q:44ip`. $rIƘiڪVnC."°UU0ϤdHLɪ`y[d-Ȋ0bKTXP˘2SܪW NPe++N"B)#FO*Gr5lH1vZDۻK<ϻW~K_d2!йi9@Ny~ e`btS#ny;R鎤Z^ǽkHoM4 ԍ0g=Һ֊scg`jwS{Kg4Rsq[5ds%a ѥ&l[f e7Y04~&~5ND˥z/x;vJ{m0;(A1AB)Rʚ]Lt%$XDsD5 jY`4h͍ͣeLb ^Hu\ "C.?K!G.2$G jWmH;IP'9;wB^މ @Du;SFucd+!krPa_cUvyރ>Tn GO5f*8B%# q˕ 3L+r% v]ٰ~ݭyG޸a?;[n_rlۺ/tm-~jhh|H]^[,M7ZG Z1?Ȫg2]s`ɉWem5[>j5jDY* IϪ;n !nS7k6drZKrՓ1%HP"2bRß$R>ˮ5B.K>J4""< 1?wB vGP*8 H Bx[˴;\ Ŝbdz㪡RXO JXfe%T*BHM$D(se_ZSg*H讛Cv"J5ʆwJޏYL[JB9Ͼ0v,R}g+b7,+.WK|߃4 s]W_E[oںaÆT*8;%ƚ`Qx¥Vsh^|G}b ,i/|#_pt:}Epahxxŏ=d<?ϧN;~DzKuuVX0%S(+ʷT[?'y )='LQB2!GQ|N=x:?6q,P<ZwX&pPMEG@\咋݈_6㨮wK\wqLf؋24 _c^a,BXRDBTy<c1" UKre \9$#F# KxNHkjjr< }}snݾnz#aP}8%Nq9\&-JɊŢT / u *meY +c$!3Lxkkbmv\ˆg%@ FiJĩm~Yj2||hU$tm_j l/R8ON*/Q';{Ur͵uIrz*K: zI֒H B[rWFF [kݯI@CCҥqDطo$wyT*jժcǎr9c֞ǣ(kDjVP8B2"@dSr9JJF8/[5QX<5iƘÇ_%)?°<0E M`CSd,[7t,mM͋V`qt j#28]!#$&`董&L6C'ByH$w,(t:4$**#r*AH8w821z~U,?\a"lrDQ@cc㞧STTʶC#oGo/O;t%]Uj}K},b ܳVOkؗ[mҏ)'  疖_0.nx3E,w$LaK<5q7?'s$!k$7E{䰩&- ) y=.O,3#(μuÃo EPd*5h|MܚZY2J֏҅W\pº əXj\:IM3bW9|TBsgRh_)=QA&2A_ 8ŢvTƶv bQ6i% d_A$I>*1=/SȊ1㞏P;o|A(ch{{~<Jz5@`  ۤePMi mx_}S=spID U67dWJCDѡE3~cɵ,r:.HvX,H$':Ral%)5 _*K2Hl0QW9HxmڑuY8,#Ay`iJiQޑߏJh dʩa v aX+ VVժV}&\oIޡCtNf€"G$3) Y)rS>U8-[x8Bũ~C GwR6^? Х+1AQi ]hM맴] RH!/q*DDB/ A@_'zƎqjt ]e$tEmȆi~p`JYRpJ"cqrR Ojp3ryes7xwEBPW .csι#xFGG*ZժV̴֩TJ .&lF9tugθB"ٖg&҈-u]`${gL&aEmҿ 9**HjTc #o:TlJxB!")+&7JT<Άͩ9;j" l.1hІQ$QU jcXf_[аVP`S֊7-TMM̴mD6 V;Խn.Q{f%ENoh`p㔧ť s\fiw86W͛7Oѣt VUC11֕Z{ +S/c)#Қ]_b@8MVsÖ`)eȡᓓ$r7ghVe5H RD Mٔʆ"Dv,j4zJ<́o(BH't"(s ư##) "jC@ߎYZ\<*\CrIa-%p> -Afne焯\seuD*OjIHazd{o3bK|q<8_*PP(Hƙ:CQTQL#c2iD*ʋ0j@`UjUڇd==3g WOd3e!l r'@!X惘VM;r0H(jJY>9Vxn"r: 'H>KU_OQ^ahʆB)_Ad(R#(df:XBP!FPW<7 y+GAh1ABc((T D "DD@R*>XH?KȲtXmFݜ˞H$8VW,<"ˢ45\m-;ZA WEdPu`‚\(m _)ZbT$[~dscL&Sǯ˴E&#۶m;{Ryd2=j|nRBMkJ(TUUUjI7 d2 ꢄRIEݰ^a\%w'xػ:{8E$4|uc\gI?ےqC|He5y0(ސj@$ .4JUBRA 1,*P(+ S8! "j\QJ!)BC!,%KR& Zt,/) FHieD:A0-t%ëּDԁG /H9š諡CHf0Cw1t/cǻݑ (Qww@ VU#3mב8Q@]Z>i݄Y`k&9o)p 5[-/;vӣZlttJeqMGKh`ۧCZ*F4a ""!(2d*R9GF;Ҩ8TG>DH,(%$a@ ).ZDD*Ϗq>Zn[ ]w}ٽ,䘲FNxL"N(=* <5MG.s$FK Ta8IĽ`bmiNWoʡe-><UjUȌ_*_۲MV`FT r8e A#׻W sY+Als4;l}×|YQ9Tgh52j@* >gBE Ǚ 娔/9T> 1wWMb`R9툠T.+G^pTQo3ӡ#,Q^琇,8R^^KII K>2%݅t1>-$w"O+7o;")3gLkZ`UjUGfP¥[D&fl LT}qn?L@),KNŝ#G9H˷)1rYj(1)K@d2\(# "0W +r Td`.)iɭ! "O%;Y" FR@`ȔkGU.˥ g]BxKB7N+59Efxe)q&æ\.mw!?jZd#EfO0N϶."!ZV,3K2zݑB!@?` t*56>555ax:D3 %tzj0Ο?6P(J[U}Ym.SaN:"AJ;rC h88d^}'C r9Y/VXe8BJ'j8d8XKUzXD bi$'䙑xA0إZP1κ@+EFA NJ%Hcb0$ptkń6N5 9&[9}a>;G$?Mv0fIlN\v1'GE+g>3!:th۶mZj6=U,{z"Ν;5˖zˍ;|Kԭ7O[oP,*ҩԜٳ&Dd}kw̛֡~3=?45ͺJY*oykok"={sAswͿ}<|'l6{5Jǎd'Iˉhtt3\.Jvfh^;ND-;~5EQ{ܹƘysST>?~X";'>rD?0pX(&ys0p"̜1}|t̄p-At%~[X;#djfBy\jVr 5]̝+ B%C^\bd,)wgĘax[]dpؕGPޗ*0, 9|°{)[dP.i[A%rg;Ҟҕ\[e#MD6>  H)Q+H̬X%*UUEBfC%u 1HUh]* T^5.)in:J`M]49{d&P:pX9}A9LrϿʁ8*UW[T$Xג"O\qMmmxSc}?oSc촩ᖛf3?O=K?E(*z5~ ޝݽg6nko:̞Jr;|כGnab O]E{{w]ɓ_ߥS?oo>g<ʫ!Ȗ6m𑣯mۮP͘1[[ëwaYmGFǞQ444Wg͜yjӵ\wcLpa{͏e,̑o"CJ#(99\O}ov|nO͐uRd Kڡ2sS `RJ9R_+MGi$[6%Ud"OIإof6 i\ mc3؆L@UR B0.)V sYv])PH!2kJRZqfKUZb5+D,H Z!()8x!쓓ƕ8jb힤ɸ=G]>u<+f.+# c,%kj3XL*8Q2 w Kr:,xvJJYf#rڏ={عovX,/0 ?=o5˟~3Z;x|u/[/t9?s/lsxŋYv׻~wө0gv[?|*̄fa_<;SDb<xƷ[O>US1'߼|oټ޽f7St; ]w{?/E3ϾܴjO^g<kin< 6n~ /ܽٻ3_}|ɧMSK/ Q)\Șy.ްycOsV,2X6Znk4ZTxқ\. -A^hj[6ӌ1ǏuJ$.bĝs\('4{ 6!*b?Փ7/H\7oaͪsf:u taoG/Ι=_<-wٽ}--y﷾եx'e֌oA)UoD\p=$ēϼ˨ԧn[o}WOs܎o  "E 7\N8{tΗ-]{{uw x_׬^~իVc:gl&S__I7tvpܹ^f՗y%c #t:U >Qٳwy#G<ٟG t˼cTbxek׬Z׷f&^vͱ'߿?8 豱x/ZssW%~oǣ?ٱhxY"tRgcXVz)/Sbi/ 7ģ Fˣ2s#2sSN|y]hpuCF:dNdexT#HR@g7 aMRYa^ IDATur'-+! F.9 g:J DcDQtL#>~u׬^zѹs0xɓ9z{'O3gzbI&ߌG/756pǟjinnCk}5O>u5׻k6_կݿߵn=kV>zzzYއ:R%Q/H(;Q!ՕeYy\rfx|mC k2^RK9 lc7&Ur.KH@ Eva # h,oi3Gѝo}L׾Lo@&J96g 2ƴla/| R}mwnݺ9JA IvI.魫}LwO"oitRs?immi~2qx1O|;wlټQkmLP(;qSg ?Ss?|Ɵx>{/gBX3t25sFO ΝcOө=}hh'v?ڵsv`p6ys!_|(;axc|d..+G\+ʉx/pt>37nqyɇ5EفCEV?eCDL\pkd_DkPg qî,xϩxtYPf A4y/.K pl+{;GnxC. bɶc@} Z  vH5Kc-35^<grhc`#R kN6(:w!LP[M6(ͺJb]w?k ]^xwv%7\> :zypphv6\o~{VZ[o?3 *妭-[6m|W;Q) uzcCC]} ̞hos?s?x3Z]9708KҚ+=R/Z2t&֛7JYj>(Nuk̛~g}O:H&7ްeʫ= Ł{3L]?{/k_՘}` ٻWK6kGƘ(poҥuuomM`K,Fb˯b{s3Ͽ0cF+>WB@Ztohw~WWϽmm>s]7婆ec'sG4*Һy]W5vA&ݖ[oDkSd*U֮ꜟJ&ѩ?o74˽n_hȨRzʮT[?Bd_[W[fD".B˗D,Z#m+_%YჼUss5v* 3fli7S 2oeәJ>LtBxOZ74[oqLpp[q&E|6\g&mȱiGEK|;.܀Vq)Lf'Ru5^i_%j"c ha^%sن6,EZgL3Wx5բQ A#22|jDnNV&h#_F]e",Yj6)%јEDHH)%tu7Vrg m(ۥ[n7S%L6{%3"BAFɜ26ȑ St;'aRð dVpG/[:(lmgMAk-hm*s(Y`A6پT:U_WH$-]Z[[Z[[e8ɰNR9ukVOf6M&E^=~@<fk/mmZ .Pq a]K7LB&׭]##R…?wbn`mf͚ [lvGu׮ᦩ«18DBL9)ڇmSَtLO4Ffjf25s"Uݢ;- VETB#8 d&+K-ـ3N|@X8%pٻ dRtl lpUYi,K%ANA&iQ L$GQkW " "a;: ƀBc*OŴQX\P뷆aY"O([;W8V!V)ОE4%ڳˢ0"Uu;r,Sxr|#.%H(KW2 e'J  渫ab;٬YnL)%y[=\(_7,KnVYnk׺#"Sb7Wݸtծdd##L$o'֢Vjr3`&l]%r sX !v\1c1Ǥj2cŮ<+ \GV *>% q>7ri* ZжITbŅo—z{>th7U\EVoIVGv< =GvtcM<&S22`C[jaSo^zC="BTQREaGVj"CP"Bs}gR̥Ĝ0$iB?DGvNrAA纠#IN||fI.ˍĘ(/&4%ͷ~x}y}J* 0z'D"J&Ri SҮHTV6?/oX,v]wsם!}kk~_z[qL?Uch'ko55Yt:,[' : Os 'yqq"\Ƃ2s7筤\_8BVV_擥+uGp \&^ ,CDb)G0 4sH:")@P B׶Ӛ#Ƕ0drG*a3c3dј(kg>+ey 6Xt:-3vx#G7w~ipp褛+Ո<28}9x2' EIWJCDO[V/ӮfB&' /ȿ+RSk),i]iXUZժV7T*r7?AvrK-w;pPd!'dH`$8} |]ZY."L|iptXK}0~Kjޒgds)-(.%;45w!xyZ)K܄59e]ILQr+/!4aD8 kv_EQjR>$UjU 9:2΄wIrr+nFmID}p'm e/bƩX‹Ik\~ṫThX Czqxܩٱ`您h 4dzbNf̦Ĝ_'8*\k)֞^V[J}C % MPJ`B)S~J4$g6'S^@(t`MLUo Z:I ]vĉ] !bR)3 t$6; 8)g>S adeU)|t=#$BS0>>~->0UjUȌ,J`L 1&M\IA"p&RI>HYYy9fYei6e)kPy|>ua Ra G'h)*pG!]訏/Iƴ;Dt8ؒ[g08?^8Edtr0 B&[kS51?#K2⽷:tP.c%4Z==S?G@&ۿHVOb4)ȡl<=vLx?)fĕϖeŻ3b!u5G=IDbQ`O8>h;B.ǕƂo;?x AlgB%DN7 d]"P! ƙB1)Crdʑ7?%Mi5@r=펄a8'((J C/g;9@7ٗ S򌚸!9*,q""4 !~r}vG ]U9pA"2^Q)*( g9пS~\" 91OcQܫ/ qPI5Av%99|wz\M4VrX҂ ]5j;oy@?dPM+aZR0 5!>"X/;R,`lllZUjUȌ[6)L%frK']]R2u'B󒽒*0ɃD2DoyxڄOK!#'(a/=u?u51F=##D2ti;몉@k*Pb1ȿ{#Yq<(#td`?2~RbJ{iFk]]*Bn $M J%.;"J%,M8X`IN"3%)OK NjC"ʁ1PB8^9ܷHk&va JaD @TZEMc$2S)Ų<#`=W+=%d$1?NC sKa9 ARVDN-ƛ,=b6l<Cd/@Bh]\M!x ?ٕ3}r^!O.<$mm f +mB@Uq7c̉'&YpXÏ?4"._t; }ҜG}",Yxǎt7OjUYx<ƞ 4JBV@"uT*I1XNa0$1 *D"NX^p IDAT,dVb>Ou 3##ŋ37nԵbw9on꥗_ݲic5U}x&ť+IZM Ȭk+wK. 誆WhYY`r";! 2b|m`}ұB)$ BCZшQ"D(GDS+= bMBG^@/ˆ0OC90r%&rQeIӠ=@?;ݓ[=g8<DC jS6F&BP0`/qjZ+qX<yl]r%V0V]P舶UA>qIWn JSNqvIOY '&yBthLa)J)GR1) BS(4 B*0Ģ*Q)3rjJnwbpȑ;vNkZ`} 紷̥}?Pj=;(mfsT]mzB84<I'8(!ǜĹʂXL `:NKDL*-1 1Xa3B N B2  aDđF Q9А(4 ҈i|+n8uH1h"Њ0ZCA! HG` A!(G&2{qPdLD3 B "Ƙ"C!jMĒ Z@lRHX k Ð8iFz$_uy*$5,B` 9+M45$(.NrDqV|oh0 #*GTLd<qBLkP |GSrW z!w$Lt?<!#4:=4`OZ?ܭgM츎37^ A$Hp'%%jdlYm[mIc}ܶg9cm{k[,k$.$)}ZQo˼7G䍺UH^^ݗy3_f_}B':щQE # BM誘F7#(AѥǂoQq2h3Ӎtz8u>6AYAG} vNԡe].cQJK6P>L!`,bfN?҄yɿ1:b1C3zfF BjtsU\,S{fqKob rh1X$ĎY[+H_P A%$\ QRR-oI{?>n砻BD=D!bŘlv[t/KE$v8pg.::'(*23 l7ij TV,si;ezV/aD':q]²vBhs(0cR sUY%YH ̬jr 8zLd+n6vƜ[H H?/f/A$BHXLh@{Df b-XDȖ(,]yP(v hZm UI\,h[`/Vsj^%^Ƙ4Mմ]TMU}%ʃʕPY'ApSG5Str+Co@s !2`~ߤ>Y#xLСCHX1Iŋ#,VccKJryٲz}Ϟ=ڣZ~bH޻wE EcK Lh Lur}pmV VFC@r_+w() 4s>^nPxH"6sΒD"$w ZDшDĪE}σx K † ֬Y3>>~)X,^zZ655~޻G^z`̙3˗// ( E^sվXtlW}+_82<=gNt?`'Aӂ,` T.}iܲ֊8Z ZؤKLS`  ب$"fz cii[$6BO@n.|#\,1"E7,ׄD4%0 Ak$Qg(9*&r,CB0!Cd YK֘l" #BPA[)7KUM9*~Kȧ]-vL* 2p1Ph%?,fuƀQVh\k(F'M^ǂpvƘu#&oޣG^h/SСCj]TFFFz'OZOLLݻg݃|;nc+,$y7]iSeB mq N~xšsbN$P(<|2C$>o7aⴠ/ XqQSdc(Lp@C*P(X.X"DeD}RbbЈ4QOsۅ\w 0B>5EX3*K >s& @N҅@r^dXybRHGbG3 99o[M9FQ:Z)(C\M^K"L*iT r ~=g3Vef-!(W7,,w^q8ɄVkǎk׮- ƘqѣǮB!Idrr{)kmww]ЙuZO$Iooϋo#^:щ8xpy{zz/_^EVD2s@]Ȼ|0K `G h0PC3M+c| ;A%-jDH>jȥ8JڌyA$}&}\7Wѹ0aDGD=#m,cfCu I 4$4NQvQ zv2j4:<`%;jjX挷9sSV3Bl6 }{-66KRPp5M=yArVDB2y5З2* ^D49`DEfR,X=ɷ 7o%]O?s.)˅B\9s*Mvkff:2"@qJbRDk6t$!uV':щNa!$H^%PSR)P(˛jUG5qHb4N ߴte tTRBpfLp @B#Ksh` ,TcsxLPtف*8w!Ğv^C DVURr|^.h lA࢔1(&3ĝ.+Y",`̦?*7dbŊcǎ5F9444>>>6l̜Ntx#C``A+@Q8eD"##]d] >ޠP(HJ]KMprׂG@vH].b̂z0Y&iie&d{ESgr$R9(H` 3UʕJ1g +>$#!P-h Z"@lT8"%sP@.3pdŎQ@)8,@.)_Ɠ3sB@RV`Tib7"bũ!؍r}\nAifs^2fgg߿aÆcFq/|ɒڧz*A`uR;s6LqxiEB) $"T*20c;}_@ThF . ^ RҥVR` 18@kn(߅ 1֘P`wz={W" B0gnER.z][ g Y_X,O.ְ+hދ\8 /98 fA&&XA'# =% zy9rz Z0S~9 b.s0! _`w¼DKsgG:СT@ew>yXLMM1#GVPȲtrr!bXιFR. ;FQ':щNPS8WCI Z9h1~Ru,">rrWw( }! AY|tE 4XJrti3| 8GPNvV.h 2k֏ 1ʩ6>.WE˜\ aC@C_o< u jy \k$XJ|YR br+"OnT+z%ɈjOQ}IYTRƢ4@ QZ@ a( TrBz& z2BHhEL ZBkZ]7+IwxD::o۶z}\G4MϜ93E K/`Z'OMMObwwё8FQ.v85_xwُ?|W9E'NLOű%/*yjjѣk׬P'NZE#? Wo`Vt+žyOik]?`8P$̜f`ػpx3-@(ʛ$dRcl\-]Nsp N&2 1T#o$w稑 L>ZF\2?(tWʸ%݅-7sz;2evCh,%P-ڵÕ4[0Zd64z1OpcX"x& eBd0slvV|;^+Rt? ý{A/$g1 n+qU,2\@ϖ`(CATU/)vi$zD΃FFD T%3PbhMBdy_y4MxDًX'rw眷֌-ٹO?喛o\v=͜=ujtlldxherN/LJO_l?{w}Hnowx=?/ͧŋݦ^}}[me/}u=8#m@( }|g|w}@L ʟ]ZY\jhN;Fu>:jd**,.2X(b kQh{'bdD.&tDNHil6l9C^^8 :W,+\HutCR狉Aम'λq! "'-k+GpdR 0s|ΕĕVec1X(0{̪cKndk 86  *#K֠ BꛋkJ*>,*MYecM*/t)4ƈKn8^hJiZn cP%[R䉪B:9=U T6.yY+ZJ=܍#LK))K^%`ɚFzhm컘/1fƍHСC/-UXv'XhD)v{3m۾&zG"_TX;<\=C= yf&1rr?$?X0 -k7ߺ%á s=j Ơbnnvn"*P1!@@sT-p$4gS\;tuT,O!uSC#HAp $2F-ACBYiKq7" {wvP=u3 {έ ĀA0!ͲU֔~}B_@l\v۶mckv yڅϳp={ˬ1}=v|Uj{z7=rV\|?||j=Gws5Ƅ[_[#Z _M7ް;vgy-=u]j>db_|W,_lmxݵk׾믻fo}ͻ_o\f=/,V}ɛnR\qz~ԩcc`*kW]Y(?/]hcǏwww-YR*bOo{xZ~Y]R. oC &^^U-@mEepõ}Zi+GΙQdНid ךh}C>$Vo8SSb1Q;$BR1?˳l,3pT-U@ъ+[0 R\28uC=:dU{â ""XOgsm@K#R\zEX4d ٕK7u A;\/~f!T+bDd ƀ5$en,,/]{Gj1һk׬~oپ}\sv{Ŋ{{*2"xuI091O̜=[U+6xuW['/]lj-[BH7\3=vo偁|SO3{{zY[z{7lX /Z4>0λVUV Ijzf|,Dy f$VRggg;OpƜ-[@޹ߌL%YI-mBWכ[.k}gkK+gK˽+{MM,2 EH>!6FgU- KXƮyxy>w8BLG[ Ǖkz.w' - xP<X{`.|zOC^4{=;m9CX0F𝷶-6i*kMuӭ3flR30 wLg 1ocĹ^wMsTS1!H|0 UFJ$*{yVL4!:]Xj9;YEBP0jhqMbC.J.:8ٺ{M L4sZ#Af/l%D馻~E߻/ P/ڛoyHv裏]lws"? D~wÏ|{X('/J*jό_8vx۶GT.QZͲsmo-G?2<88yO,Z6-YW[lL؃=DUg]) Jj{h4JW.r(jC,sYMD'~tH>P'=WVCbteuhz z}_sӭ؛+ia1L&T.koAD?(#bVqr,@EbiDnuMUDP^S>4wSCx8KxZ[NlR 0CnjL=3 !R^aȆ[G·{E2DIH_zݷgzD0\3_q Md"dc_ ]jUVKn]s[<[n0Aim:8!CD\r۳ˆoj>2XI.fZ1`Ġb}fnZMRAIc(iX%I 44*Xk#L[5..ܽlZ0 ycB3s"leVokH\v_j<"[~rN\ٴ$Wm~}}wXOiӏ Coɩ~w|b'wcn}-7x__kw/L]42rǎK%k\:cB244o߾K/Y{4MƖvz-Ϝ޽goVWx_=TOg28Y=kOgžc?ٞNze"*Dhsomn]}I dD8ފ[^hI沘5=U`vvV0 J weYnDCQZ;3sPii#EAiil)Ee}+7dl۹5_! PO,/߲fvieR͇V.lydr_u@醠FF`JsР.^AЄb#):8Tl:bh>k)bєŞݗ@))Aȫj:OB%K\p>:rY_N3koM NJu]y j%Ij lCNrMfg&(V\188x]~ۭLP߉N K}u$K8|]Oko}W@LG^*Տ5N=1rƔЉGت-]PmJ94v ݘi XR+bv}OpY/hWš---חw1()h8{|߱ϜZx Q*ݕ-vռ)x03':|Ek>i%V!J{as%ɦߋA.6 ٱKz/96̎SCgӉz: d'fJ]\\[3uHuQ Dqp'j"ct}1뽈è$Gu:'h_#3RHNO81>1_fz,=F+ {OL4fICU( -Ʋw˭%ĂsnΝHXVE\U($C3g϶˖^ᎡޞjkeZX*.C [eJM7^No$Y./}dtd\.? j kmWWꚫ7%Ir5OR=D޸6T*thh.߹|R|-g$k+0=vզ+fKRKU;}陙wMW\׉NZQ]3`l-t'diWyf= _ߵ&mɌ2T?lb_R(}+f%aDT*8rTAeUbF9*xtxPW х)ieP0]e5OTVuwIBT(/:(TNN#OT|e:ZxQM1P-So ѥkMQ/DDؒ=ߺOn΁ 1P.aO7<BW^fnΩT]yNy Tꛩo{μ )BTl3+{6PUgE|>Mr`!ےiBY61xR ̇ \ֶD>4\P`7Dz; \6?cgir],xr0"|;szh}+V!4^ dE '&?5 ;vvϯظ[F\|ÿ_}_3(}<^&vgܵk_y7z9%ļl#Z+Ӡ3pT-.#F2v"hOG(I@$hҾ>c{_}oyM`yx^0ϫygGwr{b:*o 0mHeUwl迥`K%ErANj$"sZTn s"FFC+ՠS*D.bj5"'wXdYI^i*ιf\.  Ys'|쾌S7wT0{p3x%S,]9Zo}^prr\˗oIyf^ 4m_$E/dr':щN& ԋAHqRvJDEE!EGAH >T#*ܡ-umi`@ID|Iz6) ZmgwC"x眘xР,4Oo #"Cd 2"VqG?쪍~⻏0v 0袻t3f?>w=y @nR\JѲM˶Z-|ڏBP(r]7NhZ') BK"-GIМ$6}l:nL1z!"XnQ׏w:th6}/KG,M?6z9 ~NtxM=?{z XZ.+P'VnLL/Mj*K"ʲL 33]U/t>t=XC&ʒeYȉ )|DˍVޢ|ސN>{3qBBfO-޹p,3@%? Om`m??`g9B)x#2AJuߑ-]{VͳNurڝn(SAɝCnЩK8+ʇȌwMCJ(/cP z('[(K]ͯ8wn^3BQ>|oYl7pcVlvu| Bʩg̞4 !1EʃaM5]~:ԹjԩS.jD':щ7,9IsU] X芫gUz*G=]Atu[Z fBĩ"=7顅( ~.k/Y i1y$Q\;!8 N({:jov%BkgrtG, "($ I-/᪍/AV9+*o6EвXQ! XLt 'f=JU "qbvJ95] !+IarZ X\0A%g+(t6+5! ;euh*i,Pّk ^2AKh\OgC]#F2tV':щN/޳jA$tUrobѺ[ E9 fPY8hHsJZīXo۹gYYRi>CD1u!1&Vܞف#Yc4"()d <`B 00nz&ݱzvԵ3[,AșHd.@`, y,mtP-VBJN=f5 RŃ4**\fngO%0SR{t ]F$4V,+̶=}/#PD7 nB]tV':щNa\\᷿(`Cf@AiA(We ,..TR!$"c'"ZEvoOґc4A1B*e<8AF` 8Kc B!zmߕfm6XsAEk9 %#;DB4L(9OlkBr=26q%]+45TYubhgC.j9h6leVSL(,ffd$c/>s#+_zՂm4}-_N8dɒ_ۊ-ܧo脻v?Ŀ7u=\e{ࡿg>'<0pFD'~j8"vR/x$Ju}`ٔBBYbP2Iڻǂ5^ MB ROpZf$J%=gث؊A уs@NƠ@D@d(ls,vPA({0KҐ 2apj|jPf rf0yv̘7+$$BCh "vv6 *m#Uk#NdeX%ih.PRCA cb[s}o0 ' :p^2C`@4h !!ypq'xG?lۭVȪ+?,˾ݷrrJ7>y_'IŬD_]ՎD'^ӈ_@*蒦ب*`ӳeb9yv*\g -h 2#M7' Ҵo};i6h4^$,:FСwz>/OL EMV[K|z 矿>ywf׿͛o;ygf?xcuܕTPH:U-k p%PH@MW? i 8#0NvSVtˇ PUHQdB@bc PoP\0\4<@0,aY%}hNs}&sH\FXz<" "`Ffn ^\iYkoXVxM>I':==l.`̼* ɡOl=M#ʏ lX]a;_,j 7,]E=*C"\cHX k53{ޣ:ʩ rk46"ϲvd1/ b$bdDrl'8)p3N["vMޡ*-@jȸ;z!bJi cIY6n6̼DO rԌ1½Sj*9\@@BddE?0o5[2e|vj7|Ν&&& 9kbܸ. Xo@$,}}}fOd՛>";ܵ{o;щ;UNMQesd2$D]RUbؤYt %_k[ב_|M9 ]&)}[MV} ׿Dh14BZ#+Q!K;5M-3<"|I2]+pZϯ*xfVeDM4nKN@5Mk*]d0'|ag!,JhBLBݺ1Ȍ 1LrVB(G`7D,eBDzDΊY/Ce -BQ@8'շ`KȖΨ^R4Lu,ձO* J ,^G~zժs33g/?qT_!M3ع{g?_/ ~ߝls;щNiduj2i5 M4#*˕$tQvJWPY)Ii8x꒯Ԉl/㺥ⲄKH}tQWݽ|"9c [ƈG' @]#9 02lOhȀ5ƀ A9L b֢M0AiDs4 LkXJ Y"#YUM ASh'w D]wt5/fժ`:D,u VuM19b9k <-r2${q=]wH/kfBַ2>>#C~p!~?{;$o~kne/]h(K/YtiZq׭[$ǣxCR䃏6mSX=9ToI=wKwTaM]و7RԢQ''=#Ȳ"zVĨ "1d s@@v-H.Q;z jǂ"Dù]!$ʹE`>]Y ! (H!} ` Zˆ8U4%4{s@^HȄ,$>˸X,=xf.1gMf7QOR;FD#XJ 2;!,MS.SWE(c^^.@+RL!_Â$>L84# FZ8#2i(;-#l,PElhɠZ4\ ;"jxX,lOʢ޼??os___w=qD'~C[1Ƥiru :K)SoChS4,ur'fE$v[vA+"[yYbp| {<ayKZIe鹲hMܱefFo4XHP 1۸'h 2CoObf""x3@< sKZJ ڂ)uQppHτ]`PP0Kn>~&8/&9Rqd:G7sz,,`f$`Ag 0텋XD422H`|7i‡?;Ʌ7^tKvx!+b*KU],B*JT2-\uu`.%}EԣAXY5}&a8r0I0A8NqRo-&Œ/[cg-`$1rŗ EdKJa`|삹LJ.3"15 PvU!e`$2< e|DK7ȕ"y;ܚRYcCI,h2Ըr+mn"0CbI"? :66=[,v/?J A'Ntwgٲh__o\RO&o?p/&hZiW wR9(cDjKei? ` &$I @ƾ[ϩAH⃠φ /J|f!$d!ȕ$*N sP]v, Bq`QXQ#K֧7<ESgHI0&Ovɴ-W_ժ+N\^<1ks{O3>|\D':щ7,txz@%*)Q+:M Krc*|?dNYIg\2gR<&*@{a JJE x sYb1-?Vd8-nwTV dȒBdDBba`!$V7mB!I vd"ebr6<3dr,dMm_[KIGV&tQ &4)\^zap;[( &V RUCȳdc}3ʎ>ުzshD$I0"EJ,{<Dz-YdYJ(RL "@s |?nB ТH}V/uuU[gg ɧЗb75/̿}87!:CFڂQ5o@%˅8411㾻VQ4nQ;;Ǜ@Q#NB:W"'"Kd8hue n +8?uVXeAJ=WSId$r?ѪNZwI5}&[g $A8閞Ȉ@F40?ORscqt:@71y== zx׳OݿRxѯ<\;~l9,4MD"x30^.T|J,0UE.:c,x-\I|m}^^EUɁZM1y{~e8w'92$DB& @:w܌Ys(V,U[vZ(X6Fvr`c!m7iV4ݽF,K]PQeHtҒb(Ϫ$0`.T7$VDh`5u$iy`Eg쪤[WWsU/Pj qn144Z_#69L= O9D,ϻ,;to!Ĥm9pl[0 (+[w]Jկݼz՝w|wn}y#z7S}ևw]ٿU2xYMJ-gy{"9Ϭ뎕˗92:Ix(zzoO$Ț lC\ZӶ9f&œ0L=_mML@攅ZDC”rb#=o!xLFJZ0Im\5 "ӘɱɲFF 8 'B7Kͩ~VC rLL[X$Az$q3MgFerxc*V[C֩sNC0>)Pa< Y@cq^ BD ݈Mknn7Ĥ=1-ۉ b᠖9ؼX 9_ȃ jĪزawp{͈ici8!#s#5,@t`ږ"L&vLF#4-^ofg.oH$"/,#Oo.Eɋ~4ai/w 8e$2yVBt:2t1mW/wIDWnZ46=22KFScfRd聐3x,TTאBFH4 KqiK%T+\"77Poȧ, .\ J/̛7o͚5o' O|uTc_9C ?qrhB/kDVq~Uꥴc|/\sG5_]@gg־{!/'^6KR+W,>PA~5F?PrΗ-]ɘ7zCkO854<<11QZR?kp;:;:v$oww8[qɩ+WBw9ƍms7FX礽SLMO G;oͮ#,ڹkwcҒ⫮o|kbr2';[Ӵ޲ZR 1#՟ngu >Y=-i0Hce@f>z_e~%:_l&PAn|BW1rR(JCIr&OҮlKˢAgar%?BC$ETQYC(+ * g^=1靾ЗjMɌɞ`HJsJ"sB1#Dzl0- 3&uЅ:{ڻad& !-@8LYQ`d k+2иL'G@$J_蟼8<2h4%=;a j<%NjXܞlHai"`qd2V5M3 }<=3=ԟl7Δ)R6IGVA@ xcigt3* "!-(+3XuԆ)"J93B[u({lȫ icRn)< *DXUɆr_;&8zN745CGiȘ D8F"mAJ[dbE) F"tƚ4~mX ---G}L<}LNNNNW999 p tP(J\/2Ms}ccpزM[Zu/ia"زu{/ j쑪ʣǎ埾~mk .4_ŋVUV۶cJDx3ѱq9$Ν5>q^UeE2w@iIWp6m6:6[^VzX,ږd*7ΛsB̝[J_h6MhkppqܓN߼z%9v!(/[`~~3Cקݴ+`٦t:斛>MӼ\?;Ν;Jg szm[.4_lhCCTpǮ݅/4۶][S=>?x0LUOX,Ǐ'`GēuֶP0tjk+.4_ﯭ~z{{ԊeK G;KJΝ/,(GFxAA tzddի{z{YYىDGG癦dnnΪ+Kde;w>J,_0 ;hŲ###e%N<9O&UU 7166vѱW.9w7sꕓSS@d۶D#ӵ55Pl9زKJJmۖjɢ1N7; 4VTT?~_|nyD߶ ϩO 7?q[ŋnQVxC˴YR\t5.Y|S^^{< eX5vό_~MM^kvɱ܂tw*Xp9͌ɾT/BI;,nr;G+n}"*%*#SlT_J)q|m:~Af؅Hu"`@uu9{'UzyƘii3՟:>e5ꐥ꿤ZMHcIؖ(I,YШ<,gw7htf ^GJb.Eq  IDATM=|J'2#3DyO韚6#Hژ4O@h}AڬVUΉrduѾ7GNcrϴ=6i8ɀ-6(~29EI(|/QN p;AK6 Uߢ+&CU;^A+;^GpsP_PyZoq^> iCx6QcM[̒Bh k>e W#r)+2Eʕc]]]`Yf*Ţc۶iYxV8njj4>55iYeL:0&&<teZV{GD޵YY޾ʊX, ݺ}GUeO~=j6v +$"ZgϜys8O?cc?e%UU][>'2~fN]m4ini׾O?dѢѩ/>я׿{]PPxM!…=Z|WHқ_ ++3i@#ىx,619f_n[+;8z صkOk[l/2L8  +n~SSSts˛KK>~̆d49g>Baaaov^^n<B?yj`` ^ٴ9/Y|+61onO}nrrrΜCصJ~s18q7ޜ7Ŗ~γg=/JMuUmMi\ӂ =V_?mْ%H{}ͭH8kY@1ܶ}gmMu]mD& oVXݴjtl̴,0^C,++}src$";qrrr-[GGW\Ng~ Xl[;?qMm`BA0zzҙLnN"R;wuttZ|rjŋ=w}/+e4o{БV;c?&002:F:qķ 0 c è8|?W7Hd-]MqEm{(-)>uG{4kw w}D؉==?{K:i'ٗÅAd kX#'dzc0īSIh x]11  c+d9iwٗsS"wQW%ɻas/MƘd/WN*ťꪁ JkMp: [v/Cr0 Z[ }%_v:yo i DHӁAR%$$ABlb4wtͮ[>h΃3O(4 d6 9N 󵚚3gmɇE M[)S,d!l!t# 1eSMm[1#Gww=7݂ AwԎ+ $"@p{l;twݦJ/TN˴+VΟVSZmvF՚jWZ~N!-UJ"J4ΝǜO AVt.-:z7S,Rz| S6"W#_l|eضN$%òLDZ-u]:}<ƆKٳW \7[mNt59ɞ 8og[˿~,,,ػEE?>1VGggeEyWw]wޞs[K_6t= ]:ȃ-?㉓*37ZtɅ?[Zcy!mn:w޺?}Bqͫ6hvgɟ)¾⢢ˎ{13Gm˗-ۚ/^+/+gZ5bdd_?>srro"Z{{uk*+JoT9us4Mkii=wX*t]7q٦sM7SW{_9no7޾­;vcrg=55<&yh;WdcsꆇG^{'!޶~7W7!Yo0>tч6wݡ51 ctt3~֙?oM s6/((뫨(g$%wbaAs/ttt8pO>tή^y^XXxW_˖>F,rui8x8Y~?N=~" 䑇َWJ'7۹{ρ'&&…DZZ~ å%%mR]UyG_$,1Hmqcz\KI+y?,I9NZLwLKBBE`^8Ǭ/=c\yM~e?~D8|޺[6Dem$vHS?c%288:3B.+Pv< Hv7[:-m]!Y?8wA$!qC!R(Fgp}QEht6{i68"Z +R!I,nst__)P}&qH汈L+oˎZuM?'P%Z͐^L'"4%"Ok)J3m?)vW)B% JʶmnR c̲,IHu=O} J$HGd\"H;d'`͊wRSpKt#G.I*N FGG qliX%M] { ׽m}OعͷAB˾WT߷aȋ/o?1I;ppD`%Y2ki%Ps^Pmm퇏;u ޼zT5mPS`UUo~]{) |5ՆQ0Tme'x6斏=mOصvGLO'TWBUeLCi?URv0δ߆+י޶cG2bK]mMmM=+'>˖ׯu= hf0g1V\\,Iw !=mٺH NLL6i7wC>@nNr&qg_"9uUY<AJۿ{Ͼdr`WL&wڳwT*?пrŊʏW^}7wSK Cf2v:M'cB^bT܌"ؖ>Rhh((`, i$ly☦)D`3'SG< P*gg|"DK5ɫ(+31DMH{Xޱ olWAH[O[Ba{^OA];teexccc__E#,ddymNde}O=aH\˲+/۷/^|7&YO<裏< &[8}ij2MKi||BJJ ?O.+)zzE "H]]|}bWWe2smw<0gy[[ZRRW[s}!®{/ v! c!|ŗ^y*+*կ8ujo8oM ;O[M4Dm#9ccc81S[S{Ϻ_Vy9kَ-^gG˖.}9z֛W!g1CKgrĩnēOmό]9ڳ3DUe巾=9~/D<3\y4,,dȶmӲBPuuսO/Td caiVQ^ 4M,[!'p|'uN$󯒱ܽw܆3goWVZ"G%G2}? }/Yut]=U}}#iNMM'Ytڶx,L,Jde]x 3z/m'DTq줣G9с}CvF]PA /5;ɶ%c|BR)9#WH"99uռU( [{{K]BdLՈPhouph_B⏁} ;q]w~m0KW^yus(FGRTeE5yel~P(}_uΏ>е7N$R#G{z=>˴޶SsxŖ7ԡG{z?ȃ{?y)_0eϋOLw҃ i`Udt{rdo}xz8Srg5PoߟC61 ed+JV_U䤄ңHbd#94T8ڶ9DCb`Z;Y8. tC8iCMC3tOPX@ 6Y69=b(A! 8G bXO\ۻijPDB@ 6: /@׀s@h.P8m4sf:d Ӗ`;D@BuD.g:g Xi3A=,#L j2l$$a$3ߗ8g"s oY@`0(ATԑf=34-O%?:$k%PJ̧֔1!)TSgn05B]SmumT\@]#BD L.1kM 4F!dg{W m۷Ͽ$nP8LnݺǷm| ?$id+ ECW~B􌌎fm0u􌌎#ኊP(J¡peeE8NhF4K$zzzFF`UEE0UU`4ysm()..(+*.  )>8K`*9f"N$*b"+UVoEEyigų&&{z]wQW[r%i1È,/7';(+--,, ???/Lp(T[S]ZR pEeEk%Ź9 g㺮 #˖ge{mxE[VZ*jk,be99uu#CYܜ-˞7_(+ Xgtl, ‚‚ɺڜl!h(\0V^p(0h4_VV*w^TXPVVHdstt2 VW>22ɘ7^_^VىUVVpGFF-˺eΞL&kkj(?/!++/FB‚d2500XVZz豇(܅拖eϩu4޾drznC=u KKKrsVWUeeʼn(4mI&SknyE` c=ϩ5 #7'ghx$Jϛ[zEyY<#;>_ ! :;߽ ??LVT^YQ@ϭ_hEEymMT^^^㼆2#[{gNviYqjjme2RUQqI0t='{*#%@@,Xu$ٝ(Zc a4ȹ92`ܸP*'RG|J\NiGŋ+J%eTlң?g*\ikjY0!usй-dI C9 n8"GiuƂpix!G-)!wȐ#Q1CΙ3+⍱@6x^ө, B/ixnœ_PvY*P/+r${uB7ekoC46807wȦmp\0 AJ&/͈wxR)CD"E, #e7y'S[K7)U[G9g.ȵΝo7l+?/Wm;vm߱~IʫIPW G"p8|/k?KܫPF0~G]򯝚uV_x+N& IDATy3DaaSO=rbL_^3q!ٗ7a4J &)-#RUhqujDS[$׍\Si]Lە?9p. u|$4pa;0D$ e %1qeX8c5JiwDZxjfp~|_H>K3ʾAj*5g䟤\.TWPu[M8zNZ!j0#z/Ouh )-8ky$AW?Lz1bn:/8 ./+/7fll|>,d:+A du L"IR;%8*o^ #"" S{@O/EPH KNg2H_ݹsZ=1$.y(yƢPdnn \]j!1ZS:˪{ ΀@KrT>%AyS[RN )B| y@Ln܉4M9۶k^2mg)C˲'Rsu\)<>\3 %${X9#@ݠ(]f.֍X1~ ZT" #*L&3'_0L<3y$\'1T] 17/(\7t) 5Ԧ`v. w۷ϑ2g ( QɃ)'@1ė <]* 'ACtaCQBIS$K3Y% \'0P/<$cb!ȶmKRE"$)xZKTJ! Cp` oys6b(\5 su A 6s2%)k*i,N*>V5y5=U}(;4WI"K*$[Jl-d'(Sej*pձxl' t Iq:sIBDԤ˨Oh5tnSiV]bn;wvu1 fc6fc6>ȐSX2>Բ,4% ڶm)xU k{ tyb&MۤZ0iq&tAI!bե _2wB2<90 @41E<蟀=A4rHH(Eat b@3l.D C@GqÆ6¶+TD1 ɸ ԕ7L`n֮cp0F@; I7L.䎗JJB1G_ T' `K]#\4Z $R`ݶmq.p.P(\* WU4ijrpotETinI*hnnSU[o ~]}fc6f=.6~JCK\_J>[Wj 㩇 "1(iq`8(H!Bb5 f4KgX( SI@$@0—@|3!::c5F pryzPӥt*D1934MBF<1 9פW)g$]Kg4mBL5ZQP1O) ʿ-xPvpcܧIjj+%|-UY0Z+'V%G40)ͳRp vٻu}ڻK,/:tvuq9Bvuz~zJ^,H-sJ;vll\Xn4]w*BID]6aK=|:qZr>|B@]#!@sPh`%2oD.X7lhvA"ӈx~D9(=d08 (ۡKׂefsPe'` rr# H  A={;!TEuLYYt荈Հt:^S1WyFqʉCk;As: Gģ6&04-) WjՕCƀ! zޘPCdCՋ O$5ۀi98ښ''T 7z)r~CvpE͕1(D]F`K\S{IXjxgR-w\)uKͪr)4c`;H O5.iʲ`UֻcҿD Dhkk?~=%kzzO?O?U_Ww;v𙟌Q[[._29 EwO]QQV>> ý !¡P㼹v㴵wi`WFJ2)Y[Ш3] SwuuOW;~♟>?p=cpX)N鵷nOR s~>g;s)+SOX]Ӳ#GWzzzg.hߴ;vڶa{ߠSNǛ}m,Y?uo̘\c5$jz:7D]lge!E{) -| 4,->DPJ"z嚦ie{8E\IRpε3}@bx1r!B^7A:3 и1F`(Vo09ڪ=is""g 0 I1g%[| lY-۲lɶD[b)Q 1' 3 {LQ>~N{Ϟ:ujGI0ARZ32K "!K*l 9)P =j&Xb]mo*ZH«,P,1'Ҹ|jk!w^crPQҟhghB1I"*:&խ|mʭ A0acBRf`ERR9rH[5]T[ J@ȫO4^iDo['$&ҁHnKWub_UBE"R QtyMLX+=?X11h, ̌eH`qx|V.)fO?t0X?7eZ555|w7775|JgJ{ OӾOsyD: ОA088Drt:S*?Kc?"LTWU;08:R*g2Ys1#lh>_,X&JD:]FFGŢ64w${:{Z[[>q̙j:m_=>1.KDI3##Lxtl \ZO:JQ*l&y.fs###}tl7|Ǟmz߼s絷t::;߾#zWzzXLN}[nvڽkuktvw776~6 _w+xT*OTckXs/|/o䃷^ye_xlwab^{id67+ V%w_rfě<`8]tE7N 1ּNq㱾Ezsܭr1޼>rz hL?S LҁOy@X, s2\dH;" A%j}MY Úyz J蹤P N:*%QCŘ~nyͩYC (UH!RQ{UCnkW1I й[:+!yHT6jӾ$AP.^Dm`\IA9Rc=іZk45@o?C<>a*YtuJE::((`t}5ѣ)+S-[Uҥ˖,YnOOO)6!aK.9YlɒS[h^zUTF֜ "ڲy^ukz䱲g3نƆ뮾jwܕ/RzcO=zٟvM]G[o1tglx|bhmn_joyGo'fꁁ񶶹/K.fήB8o^o#wY3g(E/8;"b*9s1=?>>9sګoii.7N?{GO}}=a3so /_ye:3{̙}y_U9zՊ?FFFoG?|a3s_ȇ'yݗ^|Ѻk#tuΟ^]]y^~хOGv4/]f=cB!J@"ҋX,TWuryP,ZؓOuttg~i= IDAT=7^p|kXAjk .\0_^eӧre3h2]TM{YHa9kߌ@iTk\jj3qS T$(5#̳f`(\_!ySIDN"* Rih꓇O]9x` b,J(E䌬GrJR:H-h.ވ`u-|Stk@s@"CE>B|];u+t@y.^2 hPڀѡ<8 Bl30!q~c% IhsBKl%nvs e+7`ˊ\`  #cF,"!+|ܩ^Rs @_E}D&enezAH%Z啃y s['(dIqJZ(oª&5,TӪYOTr ur.K0}d2h4AakBu%H%KuN}֭#x3(\/… ',PJMުHCki;W)Z֙\P(c7 ¾={6ܵ`67ԇP4J /~)_(tw\am#}w~[o?n}-7R+.ȱcߺ랮ȇn_h3>mt:} CC:3L~G gۻ@wwogjټkW|WnrX%$%"mP3B|H]]QuB/O:T*󹖦& y::ut,_tAkod,^tѧ>/-[z֙k+9|^9^UZi1G+ ̷,c{6fs9xWb^w7oOm}撋N(Ng2~7rw=}zg>IB$R>|򧪪7\K/2g,{1l6L1hnj}>l6{1j1LFO+ݝ]|}}3ϯ;cW@k?p\go&vС#W^~iUECOz)&thBHij UT8㼦ƃ6wEAA0g:mWM?'oő2=W1ʈyg(g`q9ҰsEeymp"B+f_A "QHRlX(b]:{YF+ y5 xDw9 3F6BC KZ7ꆕM GX,6z}/eqfF F36h9`QW3W7mIamlJTGNي @.Jru\ѲP%m.+od &m{.5 @ڊB[!LY9(o3ۯ3m5؋ ` CVw(ibD&Dc@\V-G 'PJ͙3r7<woLL$byf*v ~׃?ތaj $8 l~wR$&ư XaU.>yHQe)8=]T*ܜIgGc}k^W67O ђʿ904npJ&MMtfphX==ǟÕ+W,Z^FKs3޻ᣛ7?mZz{NXsic.ήbt"9P(?w{s?"eή-7"B>-e Xbgg-7]_'WUU[o ٰqT{_Y<*_;6xM{ 7;rkUYΫz$ƙeڎ:Tͱ*DY%;2nAcsM8sf/l#B PK,݌PLk5erUq RSw<{0] CЁ!nXjZ׮u!0%oةvaA:Ҥ1l6b1Nql~ /o1. 5^a^}= [ҁ f AkBHz!qAӪ6Fqyk;xPdu""1GM.yUN}[݊Ebd5LkUJ ru@1W/u4 ks:@"AHYatH'kO\=RS{|QjDd2\Zmo{(h *EThV:⛰ keԆ}<'jsZqHun >ClpYb1|?&R?_zW_۶ <c[c؎?u=/:fdtv筊:]+xf>|hSS`Tr)3hx|>y獍 ],8Tjk^h=s!L={YpMms|k;[U{VHfoK677MLLJX,61c{ynmmpMMx<d9{1߾ӧkF-[584Pv]mMͱrlB'ښ-7Zn^_ܱsוW\vb 9rTѱѪX,v5Vf2<|x8Ά{Nb] 呓To4@$$Cofnҙh3;]9]!f]8-?Ptq~Ѭr LW!;%*NJ&"HQŢp$!>RkV<4G:FTb @E8TR 0}Z0gN:Vdad95gZ%CL0R > hDGDEjLMoIͭ8V'j XCS]xnlZa< lh7&˸09(d:_ֆj?pQ$܆7[RTZ~#%|;JD,5Y4nn4:X+MF~ 52e!ףMMXCMh!o~2^YULвX #`M3xeNi]Fbo8I< CıU 9cEf0s6lRN>֭{9| j" qJAs?AC]lTOZWI)(.NXk^f)Y8uT{מW].xull~W_|/ގ&5o߱c`tt?Z'2@ŋ.?ϴsW^v R˖~ٻor9Uż}K,RH 2"( >G;s^}mی3jjkFFFI!g>|s/8gT*HxwOXMus/n>5x,&g;s͛.],^Жٻxo眽atl/.t֖}sd"!73LM Ukh֬oصb2$|od"YUUٵ|Ҟ]dYTW[k纎 ?|!.Z`ll W&海رsgzf'U.[pgݵgeKsK/޾sʾw:_|g+[[3DۯǟoK%庺{ZG&~\vժluT*>YQM[{؁494ؙ4ݑ1޺Q.ԬD;?ofjLc2*頚9Z׫r|ssȥtG4^s:^wrǻ4'5M~"AKT߽6Ñ-LBdiU KUo`x۱h<}m1̠bWڛϬ]\*[6Z"cՇ3zx'&XcL :Tiuކ*Z0b(b ][zx~"W+yÆ kHxY M1*l[3ώ; q'4{3aWh`<Z3.ZX6Fڶ$\׉P;gʣ9"rN㐓tjjc- U]kt3:P$,"?83A b,* !r#i}/I;VkWG- TdO o?/ƘȪTzˡB&j45É3"D9(JaCH &\HͭϬCPAc|SF6]R'MSr{=)e"N (J)o?誦豎-\Oܱjr"jllH&C }wҼi uW^v"ٙ3k֊]\wZkKeD7ƌ>+1qd/-tewNH͘z {g,)?Dp_-KHG@+/@?hHVUU=cb^kۊBsSggϚO:rБ߾;j_;5 bqWk_\=/](fQ-9N[ߠKC< ԶV}z:wU Kjo2C陽IZi+:ɤҒmIjeP$5'bbϖi1t݃]_96,Tl<^9TS?~8IU*#c/8<1ȻA=y[kxseqz Gٞ ߝݻ?a5a^^vWlYtqS|ɤ }=R"%7h˖.y+9ou=A?߯Pw h].F3rUթx?U_3g^ط?xk>~<7^_)2|iݹU&SP[[W_\sXYI*d2b7cQ *EPQ5cen5//Su]́.D58?^}˧OUUU7]^_TCL᢬2~ zF+>`x]o DtRBzl1}ol;έ8 &_}E"fϚ9{*KXH%$EZbYo.lώ 7:ZD,] IDAT&a@GPDab|; ĸ\E۸hڊIc~u²5<6~9!T&"f!"x.6=s Ǐf4 (Tʦ$'L~9x*1?Ҍyc'b*Qy2!j*IǣX:#!>3+/TiU2o2+jAÝ0*ݸ[s gͭ^ g\af&&V<N2|a+sPqpMN4"Dwy7eNh-9K^UV#]kOWcT20*yyˆt];4mu%;ZAI:tݕ%TLTLx N\RJ0!@ CR;/pt! kÀ_`40uBSHly{Ҧ~uh}NA샘i!k@IB ƽuGb^=cM$hXTkÅ"aFj(ݛ9=/?5zq,f")yMt2!(0"PïM/B %k QYl"*~IpBST8)Ho=.9]OA08}Pd.**(JKdePd)AL&IQOn6"iSU6ŀˬF$ ZKHD T qTb '-'xrPOxςyRe<6i,׷!dCh_oo), 4LqG)2q`XY5 CHFc\Nv.T@#0< @AAk4|є+J; G';|]B}]f0yY1jmL`Hrha;Rų屾aȕF_?PyhֲIkքF0 GZ3I7DW7sXs}]6PcXkh  ‹DR0lH{Ή`" AZ  ĪJlb.{'mzf WY2ŠRd'tUJ% w #Vk kX-kedlf)5 ]#Cڰ&Ԇ5i'c؜ґWq;GK/r$BW_=N7TLTa~?~{3+l6샦TX/g NvGɄ ʆgs1R+- wfK8DR1̢r)a_2!҈,3A!p.nHg0h͚0fq`b?$P&X =(Qv dJ Ab2 !ڰFc BTN.z9bcf1*@ &̈́L @i I'hB'C)I.K PPȁaeHI+THE鉆 A34*JcY_.AZ TRf0uXJ-W|PVH//UWa/鷄{s~8B""ی@0Y֚þ aYHQJT) {P T;=c=\9.)BXrewwOB+NbN8N0>4;yFa~!MMN9>>>6<<(?3J YJ0mk ++I(j&ɠl!#b 3![ Bp:BEM~x0Fdzd2`(GVD計ʂ@qN>1N߅x tz%F!%RDy%C32`,D$P ,  M D y)dc"cc BDdb6؄+D"t)bHP%BEէ[M)ڂAd2$}+iv;b4osTajNCټ'Dx+[Mɰ&8L1G]2BDd$`HwKFf303\QֶQԖږ~t[DRӓ"w:y `MTLTw! eAN Rp5|d( (DvJ$Bf%HBpH`vÀBr*"BY*c̆~|  MVRT H:LI! aODD@Fѥ@HCBGpANSCTm# Ca;-aHIg$T.4"KͅY,Dil#cԦFz.s , vzJeLrאnad 0e YBDB`4y­Z;G,ym+v PQ%[UQ)ߵ`YNےZZZ*G\ݻw?xB)%R6Qm)K?&8NlE)r Dà("$p{Q !0acCB:Ȋl,U`r@ Ŋ||V7F|OfH<@  ژJ/B(peHS'0 hN/ )6C@n h̦H݊Mԣ\.[1Fk\!x/JDD=s,Rɮ {a,w TT?e:qW:}ߚˑrRhfVoVc XQ%QP)i,_zʑD"aw7TLTL{"j8K6S3- GvKF `@!Ì /W'BGH̬ư1 F~a #B"%4DH @h6Z8̆Md.rb``BtF^: -$6Sd= *Px:JZ@$ :$R$Vȶ} $BVb` @$F<+R (_d4⨇ `#B=m;3Ko;xP>m# "RkSǑUQksJA#1>!פe8 Al@~YTQ` "%e *LNxwuU8 |WNkӍ)5S1S3ۿeM&?ё-j$g8(p9_&A8ƀ!` :?;Ѷ +TOaؚ+:1yF@BHLRov6X5A@IRn a2`7 !`_C"" =6lh ! Ɗdf)}ėCTP;aX$1rl"XbUpvȪCp PO^4^ FzyJlI{tN VDo9*m4udNt_JtxyGIR1xʑx<lGpbbB䎞չ[(nTqOj8b*b*޳D Cm i#LEj#셜+u}"q2SR V'є!*6a"EhbHޑIRo#oJbD' Fq&+!W!B%d+]e+OsV" Pe@1lxOefkF(9 ]E|:ilkli qleyH*xEY 08˄ "MZtQXR;1/l[1 j];֯"OD)d 핥ccP__rJyJo… fΜ)۷̙3{9bw#GNhxkϿR.K$ms_fÏk"9c /j>S1SNfl[cz~SݒW^f͍g7U1ņs /7[|BؾX7cK G%5^/w;̰Avﴥ"l$N49%BP2D)L 3a,"*rrr\6HD`RnrBOI)B6" %Y8@HJ9Ji$ᣈ9c8Rs2"PH!)NFD+c!| IaKDHDUpW'B^"%ZŬ@(2k ^YckLEKzI"Lem;I|bGp{O, O֘O.r{6A2ŠeVZfuEw}:dTx)X,ٳgddꫯ>x1f9{]rŲe˶m{}֬Y===5k;::*IEskExkz$?޹&qGsSÏ>:p_ u]ݫV.O}Bkb*CߗٗCc'_s yu1@hn&&RP˹M;O4Z6҇2-:11nFOO)S3vuuuiUIM؄@=3#Q_aG Cw%qlRs%DVer@]򃠹0 +)EeT5d66i)^0 w\)GQRdF= E@V4yH\jr( DJYL0\k@T(`"p-#}oD.R:j! $RRhb(?DF,& J>^ 8^֧ ʕ eV Z8" " ,!:b*KQau5M v1اMk|$SoGYvcctz"1peL 7#O7ˁ XRiϾ7Xh}}m{}ũTK_7fʫ]_͛٬8M{\sSC6oܳ?Oٟ_g2y|죟O}w6?w響r~ӟ)oTLT"(Ҙh;IjTeMRٚ* 93)W'Zc'E&o W,{Ϳu6:? N캡!=Rb(l\M7CDVﶴ>N1qЬMMD2E- $̓aO*$#"VH]e?k$(Q2I "xsk-b)ru@b0J,16ӇctшjTk .eX+$" P0l8OqTݐ;CFUUؗ?k(504!I߫L%pڸ: Tbzp7g!% M!b,]BlغَXLH%DJdNo *IV(L&m:)p\-Օ5F/$ "BP!!70\gLJ)+}/\*|㸞@9X bhQ|mgNT+-[pࡁᑑ˖|L#w[;w3ig~J Ϝ1AV,_y_tcOXG'?kݙk&#{k榗_ynTLT?UMvP'ul@E)Rh:Tu*⍞t;lMԌx3ˑȰX8Jn"òGgsK !)n%{ J0d&n*r ]RӪuLb hJ3$&|DuŎ"tpp^rV{~d d0"#GI1$rQkI1T2&k *$"X]ٝ@rpb)NXѴ@hЩZ-,bBT)x0j+M1*H %>"{*?彈HVTN'yrԢR@&`-'] (Q`@xmjd ZHKIIR}NF?:\ݸqcرc۷o%K\۽{OX@I666Ϛ5kppT*JѦ&Me8*3֬o=x}p"vc;H3K[M[wƚ;wkou%U$6 \1qt:}U| Nb*:%'AA>`HX-zxșL⎅boS3L~svqM3߲8T5znSN^J "Ikۓ#k"+xQl*Jv;Y.-^ې5fcА6 s$VA !s%9 &g4> #;h8B'UG(EechZS|V[Ci7{=@`L:}3bM8Rqjz.T°Q&cf46}W 1iC H5Ħ$VyYQ)t/{odu ~ܛoU}_ )EQՒe[ڭ=3LttDOLDqDLϴcv푬EI.q@طBe{ɼ@ESvCV|~|GZa)e*\Ϊs[^|݌$hRDxEjW LGH lV\t,#2! -Ef! CL]* iGs:UBf4؛ՖNƋ\-0OynY(%&ƷX}yy1ɓg#^GDm۶]Ol۶ Ξ=s̙;vZjbbb߾}Vٳ7n~lv]SOo|իV]1j'I<;7]7mOrbKFedde׏ 4/x3^ra7m[zw~s~V6G?Izֹ[7ړ݅#}ɖIxMVDTs}~}m-UŮzvJ0_B.R,dr KLFefbx1䅉r! A%le "u2yY2t7G0ЀJ[{(jʆaXkaX+ӳc 7G*$DbHS'ƃ^=`zJ1O\hC CUB+dԙB(P 3Y*\W9oG83F݊Z>x}{ne.fD"$ݻw)noя~hkA԰jv 47mӦOY Lj7=٫Y[׭lF>u=7p2)Q*mUwSvj@{^t:PZTnX=t㯜k$&by84*k` 5LP#Ӵ#CO~oDF-˜8E@uF%[n9V$cbxVWqziOe(֑[p"ՍmgiDuu1<f/^K46ZcW(dtDz;APe|ײtkE <#@fu5lq>s>m凇I&xHsgf]w O)=, 3=@m_1Z-WڬV!jZz$ f8 kR;aVRU@UTn_GĆ"0X9o΋sɈ-w2#R<oHg*-@BOw.`U+ ڡ|j6Kz^8G >\V*&6ߙ &[ZEab@S1ZEx)QꤔII wnJ\H*RJzn7eα֋ Id%DH ^Y'lRgn]mf_~.ԖI)"e^"lھ3@րxػo ɊV>䩿;pd'ދb2 ULNe7ݲ5;Г3W+Dzη`F?bނZ3lH4EPu՟XXO^k#&;8 Pl+ YhMc[#7@GE]7 o2"UGW GP6φ<֙%Y!/Lô!TlFmܠ*O]]mR9e]ADcg(7,A{6w;6,G#-<t!Bh:+^~wu[~Ʌܪ"W;;VbĖO4>sbXU0ޝ}FNEi2XL^Eji;bm\}nᨰʀ.I 'TB9͊mny-5,VܽN=wtŶ,hMw*و ^Sm=P2xW&CGf]A5eČѐ\Rz%Q\؂R5;@~*67.0*/nEzYZA‹@id.=ڑg^U;R~2%Y3wl1> )y+窋Ks@YJ"*mX~`nQ"Xk|9^~v>G?яw-tO*KQPT[q)eOCRASge&Ag;x,CY9}^ǾɿJQO$G[\l*LlI  |&i:XK/k7.{UC1&6_N$8" 0 iMN~laoAƄ؜\?k:<ńjd 3PUL$VM-ǧ;ϟ?j=UU]_v{LVS %Df8~`.]Ǚsգ_cp W7^@%&Z yx瑦daQO؉u޴.`뀙ꊥ§4$|JARC0,!2W( 闟LM#D`Tz= 7o>^_F~DQ}F;If5E j<ZjrKZs2Pנ ԣ tMjߘK/@5mhw}Qu{4wJQѯiiV77Q?l~)4M򕯔G V?я~7*-(@*jʂ=$*.d_Ne25A H`1>+w sz.9I !Ö\[ X0(xF&{yIiurv*OzrSwYhو_9a99gR+ ͭ 9/awOX0n1]#}{bl/s70+D{rVCBòv'xgir$Əd%`0hPR3 !]zTjO$(lR,(1yqq1 jP K5ы2p6ξ['ҨS/^P\]#*)C,* ~ =# ^tBb S1%-[`ov+oX5j8<{9V$P`?]F!Sg+nI*D1~m^o.e~P^OP} T,?1'1zwF݇¥]U;uI tIa!႞!0Oۍ㲫iڔc=>NξA#i&h&,΃HAn6$A׿~a ]͕G@K~x#tDQi4r녦ua{jy=ˤ=9'4N*щ(liL0qDHc"#o̾uJT|/ċN&)1XP78,HLƚ$uOOT6أI|ِ)1Mb*DEZ(,}ϝu^d@;x8H&ITA_9xl6]7jO@iF*HJi)͊kSF %ff[acҔ,]3|y0?ݡo=Y5όX3R}qF0N/C aj(/ۢW+J_?~ٽӳUMQIPz@k%)׶SI֊aZv5 pByRfя~3gN؜Zr}=<l1+dm: EMKTZ5?u .:X1!jOH!W"-炀ֻi&IwlզFDHF cΐpCxn32 ✝8uX ̜AdxÑ (-3Rq]Mf$fD`C Y6 g0Xv';pV/F9q^g^1LD0eɜV*b2&C..1feH3r^m ñspQD2f$/ȋsP(MepD19#!@ k0B!~'rf<GOWF7Qg[/E ^@% P(uc+Wy][^&&B&v q`1Y-MM3{9ɜwjU$ߘTM ҟeNLNI2'9qSQHDB fX{'1`&&I:>yɼO<ދW<^&xʔ]\-\:3(b9q( .Y<r''gaX0N7}8͑8G,U 1Ċ$MfXkM(t:JHD$S̤Յ @yD1ѣGk^s]cc:2y5۫7n0<4vzhxhh9~d$;۾b{G8tRo?Fv -{ϰ~wD&og;Mż_ovŽco,ξP]\1Uw:u;3}`Vk{B蟓КRQjCpr]riF$$rTם IADYiA.A%[R f iϢ3NY4/{t8Z2d3GBll;(+E[Dp YHE;H[c IsP |i{!&@]u*3p̘CLCFD <&ĴX&8u6] iОySݹԂ)ޕ ?B Q!|0Wmri3\O'Q [A97&䆉ٴM;|Hymx5SUV'O_\lŋg֮]30:|=w|#zkO|z33#>w~ԪU+:_wя~m qZmEw N ޙ-ƺ:ʼn;FFV3WNv0G Nj'[._?0sR=B:H #*ٵ hHDJY IDAT\QF*SXH`D-emwSr.,D"1h2l^Lro>rc E^~d#Čvz`t,80ʬ%kZbAPFAO >QXԆ 2N)*k۩0 * Uh)*KGUf/U)ۂ,U T漵w.e*#v>bT*]vA;WXl\*&C[޿z?kV{ɟ>rtmwՏ~ocL|'ñp<l ;V *nw W"qOj+*s-TS5ũ6C!4Tvђ.Fp sP 0xTx"ei=%cshg3`E$ȷBRH O@YCֲ5#saԒB< HEI(CDl2-r]AaQS ^ =l:'L Zr<HWg`$A@R<#L0* ,0 򛹣1귴wŒc$b$L<_IYV7Qֿ4EE!)PNGT4{VmvόID?,3*_D"' )W oc~;Jg+t:-v4O rO,+?{_޽mp^9p˕Ju[G?,LCqg2<tqxyuj65>wuZەe Go 1|΅W:ri bC!9A6< 9P~E9na1 Tdryup(36$x/Mzpb˔ГK~Dw%]!|oG ND\<9E%"Dy9XK+ݱHʯ/eNPE{{R/ⶃ]lDƨTK8h% KB$`_GUdo;ւ>cL@ZMWb,B#ne|jZݴm=R[:H.aD x7Er(K@y50 ]Kq}4յXVfgٳgʦ8v8#Gu%Ir[˖-p[{Gιznݶu;xCG?[>p虓_;Gӽ=/zj&Fo:'=zTL|g=c?DԽ;  ]7Kovڛ]Zx LC뼂&&`/ջhHzTN.H%Gi9/UCP;y?(˭B.!(E{疦"$20sRx/TSђ=*XC! K dZ(>@^"w"ê' i$ra`K Ja މ,)\J 4]p>!60D)ՕqeTn) 2`L] z}^?sӦ._\\!8j 7;E o`k go׭]?z_졺6ܱ }x=wk}_qoܰ~o ꪩ2Gw6{r7yd[ob3zPeugzNTC;,Jg+:>33}Y}-*firu"Ҏo32*:Labd9n_5Ev- a6@9vz!vy"{cl [#pW?Ğg-ThCa1L֐8(c"csyAeUr>Q^d[k ΐx.da">C0 3 8P*C%JW8;JRj¸*_`O<eI}o»,%'RlRqac"fS^'w5*򡪂'"~\ff.aD|eF @s&!M{KUȲNyZq;nSܱ}ێۮ8rbbw>[8cϝwA?я~U1/{pXSkIz]u lxt讟;eN;CIR'.*$2F)zR'1^! J,2'Ê6Zd Nj1\eb,L(@-]hr1~||a,C;=K1'+FȰ5OU[JHnyi`tɧUVb 7Tk)^kVx~*"%yQi EV/x&0۵Mz@ipk)lJ4T.POg*zJKT9==CS`jTe9ʡ!T2H`[,eT%o$dƈYeqzFE=PyÇyo"G?޵4Mp*r5QT\mL4elV-G&k ",gx ]q ðVM3+75.x}axc5B%t(#FDG^fx7RYCkpV.#& u$\(,&'`-[Kq$Q7&/G@ε9XG?񮅒R*R> MSm9WVuv ,_vDE[>&&p4*& HSO;:ZZ2 CvekV''+`BB8hEHͻ1 G9vcs`Ey!{q/;Ln]QU?6Q+ w]O3%1;f6tJQ"I)kZE#vcơ݇! y/c"ܘ@|X""K"CVWvJ7okWï$!&(䈗Nd8BmyZ!t{2S%R/΋jgL*7l ņ#&[l{܏X&2IY'X E!<qNa[/Ƚ4Bs$m6ֈ5Va6w$7( ~ R5 `42++t-k-7\Jv܊'^O 1Y /Dz;!!&s" 8ٳ;w?|y$I*-,_&'1;c<^|82$5!EglXkgg#&"W9rrG?я~\S={JuUzdd˙P BvyZ%Sr 5P CY{$۾YxC3ƦfM `ݓ^/dȳ1og kmn"(c5eBnTPpRrI~"!-Xk-탩{5t(vOkVez#9I'PA\"H$~}CadGDSV47UVD"nE|U)>G?яw3H ݮj=v*x应D -..,˚&Ɇ_n^8aʜ8I.)µnc|lu[b}"z\#$y 'z3)E:,3fӈ&j7 8U߇}"dueO<Z, @Tlm[v1D}:m窡m+?K'۳<!cEBmtLvkms@]4#Jׯ1fuG^鹢^ބZFV&ժ4:lm2{ރUˣK6=7^='BKL 1|V1lDQJ r'GVT'Hh[ ‚+MS]qH+Hxu>{{έٳ/Gtr3r`UMƵmcҶ̦!R9rȳ>{MS]kV?я~kA` J?/Ts( A9g+uwϟLIg:3m%xuņ;e曷v5"N4֏VV^ԩf v7K}$3PWoYadeЭl.⏾ݳucs툶?pulumR-+UåxݒܲvG'f_<9ӹЛnG[mmkׯh^\ֈxz,mfr_oĥi#MEĉ#jn _֮ cRSD{Y6By붛'KŬB &9nTkC˖oY=ucv-@5 uP,RϺH¬rI  ^5RD3s2iʩ琷bFtCQʰcұUzLMR{(l0mX˻\n/eg`eۻwoy2==}MX7npCDt;&bzfC7m0>~VHg]zզ~VsZ/>KSS]fLzPxu6Z*.7% ֽW7A0k̫\O9|+Wϛ;2啳ݱs/xW͞5P(]Җo+[|t݅+W ˔5-kk?<.^2F6’;8֋On5ػygS9=vD< 6-'}r>Ja)PT*EZ"/n}~kc. č_8R!{ZqrͼnYҼ{ԵxXfpQK \i޽C\\kaZ_?8DQjEIuv[?!=yA%˛"rl-ם+1k>EBCk+}m5eUf* ?*AFU wbòuZ?[nV_jiuD"֖׏Sa=}ܳ{gMܴu(H(u!JiQ|bҞK+kr 9xn{i8Q%aA#/oGcuhhDॿ p΃>('5X (áy<Ξ!\7^-U^= &Tn QLf9A۷ﭷZ-X`ڴiæM;::lzJe9,}w}.tW_dGFGx.Z_oneNr6oZWW_%loys7|s}E}?‹/[[{kӦN2y9zn_XCө}s{;.x {wolj``ر.(|o755^uή\~^7ܽ[nڱsx3-?r]]mo-߹>~5W)}ϤO0v w={ hq_'2-Qo`$ol3twoXX۷ep=A̪Ɗq|нwŠzjۿchŠ4ن(xU #zE&H|PR$ C- KiYkG¡Mݏmy:ED 70328`S(K;}7M55E("#3s_Yp#L{$! 38u9't .ዢ=r+wvNKhR 339ǐoW3 JTr{`_еjMI \ՍiKEEBZF|b=X:]å#8w (<k 0 pQj;ވڋ#(ƘW>‘5(0eRΗݶc}}.noWVz(DTePLJ\iF1YYG|vX8vQ7AQwk_.ܓn5Ű9z[u<iQ&g00V l?'zOߙv>banzBaޙp拳g\n뮹kf28}Ӧe37m x{ .[of.]ұs??`v<5W_`ܧ}ӻc'>~gKs)ɿhhh;w̙3̳GsO~ط?;-]cOW_)`޼T7מW_1k]_ +V]Ң]vm {z+XtΏ~av?0P./"M6o}c4,Ż}5?}}%yW_Frlzu'~wW#=~qޅ+]L۪f=3?ЗnJM oUjg1˽ U/#alЛ9@@ىiWbϷSI%y^{aNZoDv~ܶmN,DO# xsEԁ˽`fcyywo:VyF1눒ы2z^ڄUY 2s1Yzҳg-?[h3ެoH6az9rHD4hϵō7pC0 `׮]֭{EUUUBrsX,766o۲es4ApX3[kK#G̞5)IV^0tDNDz{&Lws}^|²sמ=;w4i=?fſ>}e˾_nXJ0۶QmM=߽')GƘv=Ԣ뛷:vekUkj̓?x>R*9 ?|yëV] ˗\r//ooVeK_T^zctW_jj  ugc뛷ş)"}宯[>xV7ivDlkm>=(J;vZh1#ɹ|8;?R9"nٲvYZ ڷ}ĚB:k_qӦֿ O#G>rt/]Zxhg>?2w•8ȇ dF-TO*iNI͌*wP+rSwəb74R~X8CT'JI^=?9e3FLX.RmP )hV SؚnxrJIȗџL7!JYƱX!fl !]W/8!Sr{p֫qPvAD%hK=K .iWϦÃU)#R1"FS'DKۃ|lѧ1bB~F/Q~Hd2 P7`in)߳h9ti+rJ%Rw7[{ዧ^4. '?ndf 'Rq˄%tyϾu[,G%@O}yR{a#{>G!?:!%f|<~;xR&xOYT1[7Wn/#srcڒ Ytْ%Kkmbsa> &G9J9O٨՞!hUk=oڴ&tw~e FFF?kjmi^:ٵ{7p :vӟD:쒋I`ol۾c_"V,omi CC,>9x%_x{{<3=}'K`y `mbsήYUU_{nk3s9(]9/] ?rP,^抓wB4qι RiMG;w9>#]tIvf~~틣l6kah `vY17y򤫯\󍻿2\[yAص{ۘ=+Zvv=ckVlii4}˖"oذQ5mK,K6m$"¨zqAA%r&uHRMG;;GGFw9֋8SSd2{\`> bXK/pYz+˒O:s4GW?`uUU8ʕ}?3m۱shhX 1 Baikk;gvCC=Tc?]{aA'R"RZsy+͙3::}D.w Nv3.uT:JR[Tڰ[߸ګNvخcO c (CHY !Gћ$ QK)DA9p>q:G.>Ie\\?bdNc{un ('wvv9rZSY.;;;O. ]:[)]-1˗-7ǟꫪFFFbpsJ<9|HOo^|[-XK::/|.1}P:f涶[NlkݼepF/7>vxMuͶ;j2); y˖-}c6]STWW]}P,Tey7z{0ܵ/$ xkR|Ū Ѻ_֖_;edaC7k̾ W?m\.'>K&sd'N2;w3{V&^uuuDh׭7l|uɓҩTP C l|k=kB4I\fr"/ZY$?2Ž i>W_tIX4`#/!+sUPrR1| voj\4onbMq9t!4,1 18$c] +t C$dA,CH%b7 ~ =8Aao 9 h U""I"R =)KKaR1h  Cr!tL ͏ZB!dÃA0cC6 12 Fu F}N 9K"H!Ž. !JnyҬhdf/ƄHյ%~]b2U±dP$d CR`YЁs`Cc@kXd1k% A@B=0BpfW&"hwB̤? hkmY|7t,^{ٻos_:DT__WCXv%lE 7o}MMӧN~Ώ{?|ctǪ/ fttd |{?X*566~ӟ: U'_cѥ\W6L:%Xxm|'jkm'>N\~3S,_?v_V,_ZFιmw|RC<&MICCo>r4i_}֭j|>uWcrcO<5ޜ˖Nj/`%C?k jo-?xz*{}ŋן{siY}ksLnpPL̪i7o; tI`JFx`ЮͯjGK$.-RR*^idL$^.Vʨ8J<h#@$B0d2H0th?_P> +[ BMZCP!e(A%J!" ! `BBh Yk<GkA$űE!p6v$wBB0Da|0:rT&ȧ^P"Hh4S& t()N@TXƢgzX*z "J 0$YAKH$@@2 F(BD<8P\{+@ IDATkIߟH43+v%Kz E#*Qģ6 F5},"!B$$B70:'A AFnEdeƨ*Ȉ! BAr;yk֬bofo1pŊ ,9s~{;oECCs{Ӧ׊}.XhڵkOW{ݮ*л~OwP_z:_&q ݪ.*cX\.I'bfP&E_.x7 a a:bq30X|F~ݛur(0v=e }"D$B`3iEmks\ ]p\?Ǫ`'VAϒQq0vk}b3T\9h1&  0홌gaJ}wxuRA AvqD/U*J1YH!޴ꚯq1! G%tz' dWLt950,+;Kkp_?S&RtSCedA\:};s%wJq |0ܶ}p:޾s׼9kkj*?w8?loi{?urg^g9s?ۍ{c~6|s!"R* |b6Y};UEH`+>Qb8!RD5GµSKH$cΊ[ZB[cD{x6HheRi-^X[BaTsP(0ysfPTRq~޾A>}p&}@Dfd9J S^hb]ګJYda(D^Uǩ1%EH 鲞M:*$4NS;J\5lyٔoM(J"*N6IfE"FFSA:'[D4_R1T3.LX1hŐ'ڱ{ghD= ePQÚjʃ 3~ڀ#xS = ւE6lR*`ppy1޿+sgWB; я2PAr"6s4zAl/D&zt50(*fq(fr$+J`)m2d٠WJ@IDIGjel 0!iggȱԦR=x[c.&r2Jk ɐ*&eD}b>B훔u;uLB=H=Q@uK"!4F#Y@ *"ŎP1&f}ka(6 TqrP%"`Pƪt*Rl8)qd `1!4]C]^ll!C9f0<@1iEUƠgZVmS#wEvG#fsA%ek@չ,Dliiܢ͞:X11?R1V{d\V1V *1Vw]83Ȑ(;'@I/?V.z FAc"u MU]ugC@D9J`.,'6@&rr=Y"&֐ɕ YA]rPQ$D4Akгcuݣ5,Ngd&V|)DD34!+ PGm=)B,aH2Z!qO4$(,JM4(hB$%l" u[xFbHIfY0J8SG2G" Icù~Ni-{kEے:f! &@Xcg^X!ϐ!~ 2d/`"5TU"HV^*ݢK4\.*@bs^z0`UQ#i6 Yz,""f$-t't[ [ x9 B egZHjgGh@ddܺe0Cc-W*"H`-xClFښmZܜ1s۷oR,)}ߋXП&;w>pUS7o}ѣ˖tگ~̼qk@w ?|gҭh!" idf'‚"6 %$Z:NR5mfc),"&!nIXP$D;XBK`Q!Pv۪𤃰  Z}#xBDnDm.:tAВ. xL("%6R$UA0 701ēܞ}N*ED?R#DlT$2nB(7W! "$I8Q""U{)K`OOϚPE2&bB%a׾R OYkŢB,͚&{ 壀8j`n u==}0v9̜=J;BgD5M `l%j~Z= `ywTnQ]֭[w㼋Xv?|>(?P]{ի._u٥߼[νߠj:|Ǟ[߼?O)xD*:wJE}U.&J]kP\^]6:Ni5o>kB1(Lqgb1`E8M5JZS/uh%p "$ 8Q*iJ ZB xZ-A?6 z1YX8!r+Hz!4Ƴ!PٕT-@j}^:}>x"((ű ":Ơ$5i'Sj=&dn=?c;(MW (8, c Z=c4!¢0BbHHF2 Q)@  ByA*SJ\ QOH Tԕt1R/>?#` ;$BǑӄz.FX ^ ""ȢyM{+7mB|R:Y9pP-%D4V+C%u6J>>"o0|WO.>00P*5tocODogv8z"NIcJ'TV5cƤ٥g_r!}C.%VKԞGF!r+nV s*R[÷O;BZ 3j3;HFDCd=@bIp,R2Jgݳ I8*T*||b%/PTWW9FGG`hh؅t{mmMTVА,&; ;l6RIr&I27TLHtYU&GNjE[=}m͕64N.:J۪I4]^3˧LR؏qNsSL^^[FGʎE" (,A! "TYOmZFʩza&Wza12R !DfbVys.pA Ab6poK<+Se썋'jJ-\uKhĦ~ ٴ]v:@B( ̝͘MoF7ōyo_hJLX "!;HYj.ټjvSSƺQ?m h&͞Ţ"r}@O}TRIu"d_f>o*\*v]0HX`,|7cުN&{0|icx" 0rɊf24]VtyK\m\.:yo։'isq ӧOmظ[~zo/]aӦ=ԊeKOv`0s'a @dqa0q" IDATgX1$ưz0FQ> CuK\p#|9Wt# ZfTfՓky e,B1VsƘj NtZ ?68\r:a05&3!=wr̉uQ؅A_f NOni>X;>zh@,:)S4g[۪'Om5: |gbd O_4y$W.rE&cDı*gl{mzĚ,5Qt:m-b|k֘Vo܈ˍpl=ںS_M\5Vfdf8K!<.Z-{lp`?=<ڟ/Y ;ik|[)Usf7kjR!Nijxwb'D@6Vkf~:Nyg%u>n|9{s7$2` "E%/{,/Zk08lGވg'7;/pBPѳX* 6nWzOec0 ~#|?hX|ywW^[{׾_o~G?kG|kx晝7phE+~`(=Fqc=2> PJtPCG ڕ}]V 5HJ}P((m+j|nəUf\='遥]o-\79OD*uU]kz @y(瓈.·=O.·Sp!Z!;Yӑ+Hhy Wv ᣲܴyE224M͘x.1VHY b[Ƹx|c:\AJYVLKz<^NBX ⅈl!@=ƹ5tۛ),f|>iy?(;h-foc|蘼z_t32 Q0ә\>35 2 yvE W«O D3HW-"3ŲL(4V][L˚Ɉo$<{;߹Nڳg߾hȇ>~_y͛6ՊVYy9c2|N5`9 2~cQ>t D\t۷ދ7lΤy!*{̗xfn̘j$R "228:VytҦ{ o{\V1{$|rnhV jXk8?moPrI[Y傒~+% U~aO |'>lW *,@Ĺz#{ܩ!lyjn J8-FF|ix޿~`Yr􅿓=9))sF'S]/? ?{ȳYjx=;}#a̾eq,1K8`Ȥg[ 3F g/=CHc!@5t?tqcނŽ8C';s.#B7CĔ@pis]+߾[Rpّ/„6g<XYA9\cbcs{~[3~RL6NGXGez(f̬yn#| "j^俋ڡgx_oِӳ+bl:mB0£J@i\|y;Gкz5^-Y,|ZG `hUya40gA[+GGG.+NA_2SDBUR%vJ\O-%fo>H2;_&hJPmDO||Õ|+ 1fo$TxRUrN<6) }Wv@΄e9?9qŽ$gHT9V/uujdl>32s*q,l02[H>qP@qxjaj'diKRiA[|u0~r=+kdtQ ,1K2HWxR_O#2˞[tv絟n!znY`ՑHgh%3]\T3c Z) P;!SY'3(RE=2(VK'`nyJV&d ˏG`= +kqōuw+“U/~3j =yUikB>2:zw_v!BRhD;ȅ(~ax+[hE+"^p#sm-l.Zfg IHF^v/"L@&KA^/`|mw܂֜1㎼B X,$T웽fqJRX/>3.:*)I*+%A lM)wN;7۞3u3[iJJ]˚gNz Cm9kէNLHg·*K| Aӳ<<V`KOoz_b&7;Eό5>~z2ƅCX*W%_wSXC87uBi^G% JRU,Nt:;1qjDJp귂5𽘣]Yss\7F\CBEwpQD*!T\5ſݶ쭹\>}DdlN ~/h|cg5GkJi0l\v\1XhE+Zq5Xxv9'px:><_ `h{j'`-J\k` (5,g?r87>/?vn֞3yk: t-XX4R|CAcǦsڮˁ#&J:`2.A@Y1Y(xS3Xs^R 9UWgP?:DPDaKTѷT \o^7>:9Us izTը(Ņe|C9 7L߶g`{ÕcrPMV!bᆋR! )W'5g+H\efch"HM䢱0ak^*3lЪrt&j~dfALq8~q8@2=+TCfzpՈb.=fsǦvٟySfv72j\UD`ɪA…c?~U{߃GZ^hE+ZfJ$[>^ "XA.tXQfxɪ 5YBT5:=mGk6bcV[5tE%$J ,g @Σgyr9FX"Mc QbHm`EGu6huy[>>rehǼZ"̎PyWz60[oXsL5$6 w)0% K; c@2p ZB{4[^GZXl81oPq,cR]%0~LOMU=q;f0(>A9vN0&DTGY<5ph<#Q,1C Vo,RpxT™!j5bpM[ kMvz0C`zi @gAA%S NP!BK@1g/^<>-#8RV< 11pՀ  ZO"_fѫŌ1+Wl>E pďt 5==^=vZaP(\0FGG_T*죹`o0s|⩧ٲyٹgywޞz(9>'~$~zxںy]wݑ UފVÅs}[rKE_}RwEFQte>%0 /[_y~7TE> /z. &'կutt\xVЊV\E;md蟞7zWp|^3guo7_=sz/hXQ%yf}cyMe[0{vtysѐ(r3̌9aV;D@ 'L +HHq'A$vŬЊEYԉOD@:BI 3D ;iC8VA*%e $f¨W0bHpqbOY2 E8w` JAر(zDI˗DX&T1A`%J ;8\qd'_ %BD4"-cU: "0$bb`XttGD2W̼DDiJ]nAUL͜%U=A˂ZR3s;53cfP2)t < !1c8NK'%4v'!5]b S 5d,\f-;~xF&"0^jws7_wYkb|[Xʕ+oF#|͝8qT._G_#/عmڸ^4|07ǎr}呯Ga~7nݚ5O=WQx|ſv>d 3T,'?c |V|喛oۯٲ7ɏT*CQ[pk8Fwݱj $|O}A+ՊJg]w 3MzpH;g|Z!DX8W?S^S493ɽӗ ՘ }cr6<DCK'MtN=9XI3(Zm$vh Zˊ\5"RmD "FnYԝ\b|HKzAX;6CR8RtƠ1hzcEhi^SP3$QĂBj*(VHd!t&xT20 >` acB¦#$0@YdSHc,V9jX4% :BRkk@B&fIiHhcYD(T3#r9CZ(Tx}`.#FNƨ9S[FhjwƂ5KT_" 4KdoB#$ )*Gիڐ<ϻ޷-IFWnܰZwlO ;t#_̹s[~bb3=wY/wwu-QϜ9{_'7oy]w=t?zz8a?]{>?^|qg/5fl|~019?{-7=oݗO~baaɧ˟/?=3k;w?zO~{?=(^>4hr/lڸsBylLMM 6cz1==30/x.97ж8vTd|`~\ciPg/ MA.S?}skۺ_Xԑraa(AZV z7͛6CV8|ȷݍoڸY#ý=7\tE+emU,zٺs܎jff 6)!{W~&FZ9ӛ)fU<ȍ[^LJBE7;}prVEQa CĪ Wc=>33}Y9 $[" i_lʚʄ=QbΠ1Hk LEHZΡqh$Ɔ,"&1V4;fZȒ&D.L(ViN2I'I7Y=y IDATE ""KF &J;3I= )PiS&1pvdtLD[Y0p0 fN$^u#h@1) I \bĩj9BiMpl@ȐV`3j-S׃ PVTEJ$EDIKaUAb*gX4"@Tk51 h 0Ѻv&r>F$ Y^ (W5hԧV^lJ2"Ռv5::܌QXyIqt\`])]}k?Nf8~ ^cۚի'''_gŢ~ۏY: oU~DtRiמ_}wu-7ݘEIRj'x-u7ghJ b.:#C)7}kJ4Ο80jʎK[ZъPG&Oq%$lLH(LSNb70orlǖS[kcJ6:"&i*uW[KCƀ1ZӔҐ4gi9&6Gi= Z@adt#V3Uf;"2cv)`&d\@@DM!Q*O@%50--VR(URpI!~o !BsӺ$6GKFD@'.Wb&ŐFWrsY"IE$Q1AgLYBF*0nĘ}e&  Y , 2Po.t^'%bCjF199r(J˗U+=Ύ 5 3{=ǟ|az|ڵk>?f~ݺ/z+>Z*ڔolCE.-_>o͛7}y_X V,_̳;O>].ᥡemm?mhŏ#L@e=ՑY?jcӕ tn~brf|-]&x?2Ҋ+²\m+>۶49$I2H\B !4oLIiˆTJS%<첏e&hդPdD"I5ٞpGg52hJbgG<]3AM8L)I3*IV9p a YB At1l:PE"B)2 EJ$Lo;e's"E]:$.ZuDDەJJrWy 077+`rJ])Nk1JGBU K{-z.Ǥ<6Pj2hhIK y =sdg}(joon<7>[X,x3M7^']14{e;oG(;}#`o G?G(zzu;z{V" nſ׿%?X|oW__L0Z?F a Õum纃н}(tlm뾾N[9橃؟n[WoKZD8 沌/&K2@|PkZ@(FشaKp3Df%219$%t݌%EBDh @Cdbt( J4 KKKn:0 S7%*IsΟAD(-2@LXQGtn*H)Ҥa6aXuLƳ+̞Ǝg8{$#JVg0ƑdXFsQi %g3e |WB@d%X9CYɮKBƠ5H )e%eܥ#f@Z=)XXkl|DK'N6pzf wxmE`#G_}-0 "Bs9Dh4Xs9"a~ZC %9WVAZ{+j!W=V.QTSH5gJZgv@a t-,,ժsZ7Hv=w \գ("CB﯍bdEQ^w}ӳ/~#~t+Zq6 &G&0 D\Ȯ2~B`49b["!s '\'K{ϾX~`eKc.;L¦}eb0$ %TJT:JJ$ Ϛ\A`?\Wc"1KTZ,Eio={-dz={7%S!'&'iFE\ "GwLW#.td $oJ ^ 5=œg"ZT>flg#g̪n7XBR]NLT R=UԱ8y1&05zў"WwbnD5[%#hE2Y,u߻◲7fhOEKe-Z4d)r81&x~0HXE%:犈,+ܑ_3Q:Y Y .Yu]=+'w/6Kkff s6Zm }*( : hs1sRӿF()fj>\B!_|7ZF|9U "L,M4z,>30dƘlAD?^"TVT.qhD49crOQ& j|z-BB:|mdED'`6Tlb*gZa&q==6EBS[gT Hj\0!`gгXC9rmEyI1J ANuߠ{ys6 1DlՋ$IGT YbfIR/YoR^@DjV1s`- vl=$R)ф%H7 PP'%#pX`tYˉ@;dSLܜ|J4 I%A|,hK7FQ,hhA i5 -M(֧`k45}f*"ȥ &"[@<@&uq m)JE -L"噩Z8IH^~r<|ZO8k׮+k``nPNٳg_y03^z۶k|_XXعs֭[rعs%d7WhE+䱬Hy5cwGX{]ր3BD 㓢"1hXc$ #uc Ȣ3 $uKir 1a'@(x&_lC,.JډDdYbEL|NAW[TR".3%ڣ]9&0ȰJ& TxEɁ !'`02%p"2HFNG:CŞD4| Pr&I#NPa1aZ# =C|Vf*@=sHE*8(AdLݝ ֒v@qa $e%ZVf cϷׄHbp PeH!kcK~={Agff^]FcLzuNMf 8.@(*=n(T}Mt$Ap SO/)nO%"=[qjHsd)S 8C}i8mtn5Qv!$X5z 02b- ĎkEF6@Ҍ" GʪduD _;(!'W @GޮwcBD``dh*!%i/)[ChxJlL"-EfZ)ysiJ[fo'"X:," }$9H Y'_BDDcAkZ,X=^g0HX"hY; @.Ȥ+Ol#,a `!%%O^) h4"| sGpBǎ[ B3ZъV*bM|js˝%XU2}+f/^V ޤ K{ ƀevZq H (Țn= 3n5_#t$V@,!MZ!!K` }K51 ږdNp.)$p,.-#bj ւx>8K+VauyZ@̈TH:6SgBk(I#&&CUz؞}KFȉ8HMq (V#0!!6m( 1DNEPeQ1Q0~y0وem_yc`4lv|"&$bǂ w#hR E{;K; `V;U=s YƂ7Uqw'jbHEZz0fT 1F`iصmo>WTE$oBD5F!ut\ؕ +˃ s%D#]xggǦMI|E [jy\.w遁8(}@n0KGGgF٧V+ZъV\E⊒YQtJfgbB-ۼU+\O_C08KF &!AX0%"3'WH[|}&z @_wo{~`3'9DEU P2%@Fa5 ,HΚm۱B^#E\ڬiY֒牵1xb^wMcK?ubf#Rx18N!O{ ,Ks|g1XWaF<)'. "+Yc.ܼՉ }y2 @3 TxITg*-jn`[_EHb2ݔ iG9&%:MLJRѠR24Ra5>`XKLEs¬5eI,ш!!2Hb ^5O:+dZkTԕiJb,0 P&Or#"W:eqݻ^^-F#6 OLL۷cӦM'NzcY}1ZъV4hh&wb DL QDCښgHlBv'\ W.Gc{MхJ/f1DI k ѐbAP%{nߐwWC !:Q,Q'5=M_gyJkVNnYVmmF؄EI&ƄN|C1KW{枂gr}j ?D]"_{ YТX,hO:>SKm[uAd,j"XDXF]ڡ¦S|ҮԴ1"FK`¸^2]ose֦݂=M)*(L&/"QA`tZ<\$v |,[ˈ\-ɖ o|WW$IbDT UL i3gF\ munJy{#W"\Q^jVZlٙ3g8nkk흞0 R<99U.5ssK$l.[rGF.6|>jLǫW;pjjztl,!ϻ۷ 6ovV'" [sD킙1rO`,bomcqy-_Eg1Ѓ?8@PɉcqFRl g<'or7ULn(#zM^>zI\@(nk+ j:11y摑[:tRLLLrcǎT-f܁o{Wn}-y;/yMڵg}2;7wW={=o#3<~".T*{=7pk?ĉ<ܗ/|{{{~Z mE+ {99t X%C# ` 2*mEB.k˝oqpj1*Ϝo8D,$"L_Z"@;BXӕ9T F[9G{' I96%B@c5:젷֭XA=EDhD$/cvjRN$ط(ݹCnQ}־Ftf:B,H"8@Thk6.bA$f1n\s;;ȣܲCcuDT/ 80$BQ"H'w?ze-0Q{ws枚i3pUF*AKhc൯kq]uJEGaik^nQIc6"b&h9-?!1ֈK"QC$ocċSDe/hē$c#D hDQ}mc̦M ٳg_u>џ{T5 "Zzնm[~;s }s2ۮyw˥ٟ?^ ױ(oE+Z -DQ={ϾS3XE=OTX(m !rmXm{qah-yx]gO˜59H4&g`o*ݾh1ѩ _#m^:uf \}Rx" vd-tu|v\RT/t>uKS3/ōXXjAj)b'lݴuR`1F<@ۓǦD{wZg]{o_U2wƞ'VfjQ$@)K۪ymruX۹ۺ;NL;N;Du5姦"m+l꺹7y67e\UJ43EVD RGE] os_(NtqQQT"A %݊7U]ABġ¦xrvHXVJ}P1^yYizQùv7r[ϻR>=|~'ViVyt}w4V  PZC /:a>gzxC_$v#"wefNOVF\s΢0b`T6yǛ72vANq(jRA81 y,LO~5]%]\k]IS&Gr^gu=gg. IDAT+ggjӕF5F;x/x[Vt6s=ERr}M~Pѱ3SՉJP1s&*,.-*|UJ̘7mYieg~8wj6:WǕ0vl\%{TPG4PJ9+α wN`{+LM/4j|MYiƧ 0ٙ Vu{K~8% `*R31> g:Mv:F"븷˟#LNaǎ %Vkru۵` v;k%ƄՈ:k{WWF3D`p:k2D@yw@{q )_ϟo>R`vvo֫zcnbɩKV/\O~?AApY_'N޻BĎ!hE+~r ΀1vpEDž3Sz>U"ڌ9\e]+sX߱6i6B!n ODOE<='Q")6 ;X\(7BvDVO͹\1z3dA{{cٚ D馮Ӻ{q]ע{s:<" sseْd]˺~>׺Ul+$J$% riL3ӱ~A E(Su S^{&7БNG;0G^ Y&㲺24eBxh< 6NBkM*"X' GNj0e٬ZTʦ𜙡2!V 1s ;L-Ib)8ڸWiͶXPjƨ>F|Zn(BI 99C@x䤸5g ]W$ d@&惂 q`0jG 3 4+˙Ԁl2.+totA.8BA{ ~l,+ÉRC{ͤLbD"Xɮ}]C#ႛwR՚1MeY%USk*Jlhdk{\)e˖bD-wѿA"fR'~m4Ɩcq}HϮ{*# $tyv*:rxmAi* yh\HͳyT sGٻ}pQeP ^90}~ A!c.~zri*$ǂu-fgy^j5Y4hg ^]*( 0F䧞5/a@Aרd"x^ݙS! 죲٬֚{G +j_߁(4̣'8<-lLAͅ ڒ(|c&E._t jA jIt -aEi^= uFFFo8c:Z8Zp8l823},)-eK< <~p]m>Ʊ ̥elfl>cXԞo {ض]UYye'[\W56ԟu>ʫ+,^x%4Baݫ׮8SqDcijnlo?i^9(pN<6TD8&K _>_N_xWuyXM50z?z| AIA+VF '5^.&]PUay|>~᭾u6El+Yfm:z/73+v[GvfM+}}Yݽ `zmS RS8 mĕˬa8@g-Ӽl.M"hJҨ5{Þ켾 >v?r0Lܓ+kDJe ՠ5i 9Goiіnzٟ/p^M̘D$ܫ H+Ywg[Z7A^4Oڶ4,&ihdX&yjZFg4JM^mۤڵM@$1U5*JRLT%dMs*3:Gc[Nl{{1K$BƌC1+J!P\{:F{n86WL^:1bXwllFD\r_Y ~3^{-h 8ŷȊDg׻v0}D" ^.ibgh{'CÉx_e j[et3ZOjJ;G +`@0X`T\fGQkxݽJ뇊2|uMZ<=HrWmS'7=];[bt#p)Զ;F$ǿzsX>ݢKc`X4twxC2W AJ3=IxoSc],aE!rM;@%~A BϡԶƒi׶(!Pc ["("]9P%KhTˡ7C"ٍXQ \c;ny;1(dĆhK׷i^3ʗp{oyf$ǠPϤ=h1`~gk|#Wчq]7nS C4,dr)`%=QpĂ Lj1s*5o=ڷ$,Y(Fс=tYA ĐK>†҉xUۢ bQ(8\, wоI"eA$v[DRO$a6 -7 !RFnu0ţ 0eroOcta,Ak9{m!(\/ &#OYr mKD,Zsxp᳃ !Ĕ)Sn@!yo?SNÔmo`8RC).%"*pvt(bRHYi?/I A`ۺX''Oo,Bhv&%$bT]IiLS=$`&4mAbx,;n彞xpta jGe(r4F& J ʦg!!w:Pb U2 Q+pTI-%D)Z+_ϭ !bZ+Yx̆BPQRC'*A3fdͦT%A`p pLa (;ohU]h (F]d; c.)tAAUrkF LDň~j$Ah BBHMONkD\e FF.]s^&ǥD!@Rq1}r c7j*)p3CL LmdZk`Y4(6;PL bBpMSAቴc'[AȐ(Q*vY3!HGHPpC󷙼{ B]=s(A/xx{J;Y' `; ;W5"0 TYl*!BA$CF sNF5Zw^@ @! y-K@! _\vK7ñ"69F&-&ƈD Lk9A'Τ!Fc4Feth3B @!̐lYb`9@~C?F2͒[5>tCH{FQ3J+CD̈́l\ tɰV&rNP$&A-0"&DR:X z^6PYAw rA*@1DcfB3Z.'M8i۷wwwm;tP8ŷ1}]q`xx^bd$}%%%\#HH<@EHxx>fd&1f~QyA dv2BO ģHLl[ȍhX2lTyz^xB,FPdfV.zԾ}ϒ820 {T3ɋ 0P##: `Yā݁. PƑpusQ;<0T& Ua&.M EԌHw2`nH%   %dsFG8) !L`@P|acr 90 Ά Obo{E#IP`(Ͼ ,Av<%$a nfy̼uݽ{|~c`_F)fg14B.k|#mNhjƞBJs #iAl0&Ss!7@)F&Ǿ_Z0%`gJ1f1&cf!"yB|y4E>+cO(N!u@g[.o58f.46]ڗi:`i }i8A;biA Ѩ]Fuc|_1͌D2QӈZWil~rx*siδ65!X`-S'Nٳ眆:8pj]-'N!&MtH$|{믬,wͺ CCeUTT\rҪ`ع{#mW_y#]\|#Gg͜qֽBOIlشXtoi##\>W]Uckd$r՚iӦ,_lǮ].8`344_taMuc-MM`uvvbC}9of2>ڶ}Ρh4rowA]LDѡ^|+/ l}S YQ^hႦֶv;dվvafdzboϊ甖N 9ۙ}c|NIrj7JLevJ?q/ZDD (fԠ FUs^V# !IeHhP8x㘯=+ hTQq32Y=;oxʏI6ՠBy"P@~oOd3K_8<ʇ,V/R§ $g̈Qk\%ǛDLoj֬G,|x 2 (R <ꑈRJEQG5VP0f{`5Zkm6 A׶ͳX5&}{)C9Əf\BJdy_F)yxgb1\:x3u]w n++---)Ig}޷Y0o=w숄#u-Ǐ]8rغqɓ._~۱){۸yubq~f@k7v!믽z;ÉD}׻q'ZtoCDUUVN0!dbSS \~Z(lްis%>sZv{9k[ona~Λ5?hKgN?{'bΜ ,mݼ/uvvT۶l6rήkoIH ]]} /X0oΜs‹>n}ɦܜ:Ga#3#oZFɃVBtNelK1C;8fBjXQWA+&I XWN_ovbcӧM뉰8_^ =w/~u[nk'OXZZzEKN2wspp0HZvup]w\|х'Nܼe[߱kw4/"D\a3>oVEEEȶ_zWWm?p%CCGϟf3gwٷ{ϞÏǵ57mM]>>{_;4Zfm&syh,=DbBS㎝ou'BݷoEzs A;w;-;yon+ ?`t:/_ڗ ̼u+VN OgDX\~urܜ9cٴe=wȷm̙׮/_v$m.^= Z0!/_f$Ϟڲd0+Xd5˗144k[~3wvvS#3gN?|hɈjcǏ lm;ȍ~CG- lOњX~olS&O~(4$=gfLv%mڼuUCógx"ȚuVYfΙ>yl6{ݵ<̯lڻ%_x-7cֶɶ%8044<88t _` P(^zաᖖ}W,_VZZ++=xs M_]&_(,{モSڼe ~.]򊕿s6n2uG~Hki֧~P(q-W^b ͝ĉe]y|ߖ]zK.޲;w=}seٻ?_q-]=6o;9_)G߻ok|el|kޜoTS]}wO:7уSV\N$7z^ov!]ԓnDƒ?6g(>1 JQ EUJs=Ci%QIֈd}>i%5hbK¬$jDD42A엀A#6bl YҎ5gHoklf3H@x #I 9P<&@ao@)Ee>09>-h <1,L@qp(` )SNN ^!#h[8M*S`Ԅ|FaKFSH@R²вPD, 4b}慔Ҷ=P@Ix2sv.?@/1!H!,!(&EfRh`F WiO0 xHP -\=K)qpСs,+_( q֌Rp8e_ߣӟąK+>VUU6::pgWOZZZr՚vq?oeeHv ./[{M éԏ~>Dws/~_h!Nwuu@Wwϖm}P(<ӟm{kꢥ#OG>zg)=wJg޻M^D"ӻc[wc[{.\˲;:F2!hĉG?˗7lڼxB3c-6uOME{vQ˲yuW^l}^{ӵ5_76nڼK/Ķ˶x?q?ԟſۿ}n-Ofxs57|c_{hy?>St|B%ܵo=ɓ&LOo?\p|s- :;n{MعӶm`~Sf+/_>DO;oKW^]ʫKJ]p=w?мb MM}ʊ?yOVT&ܜ* : DLN奆2* v%3p_F|d;š3UK'HWt}:_j(̂ Z(|Y'a.B PlDEJ3򲄣 uh^^BFc%UDI$ŊiXLa=gX Y@Bi6(& Nӣ]J-Ǫ|A %뙌Ә%M,7R%ʤ Z)a0(+Df߫B04/Az C(+0 $@A+,H03>sʠ4dsy(+1%f3h]F4 yTz(JXGҼoj.) TD̬Ea6u'ef*k4]~y*A b&fGn!Ȇ;2]ݲj|> Zx BϛuF"kj^~ek/E 47hcx\ Lɓ/:bdb8V[ZZryyJ+_VVV:qBS"NٻoO*Xh᎝`SLb|}X2Yr]WmW󺷷L Ε/- 55CB)"WUUF̓'OzZm OR-Y.\xP__RJCٙl~phpp(TwL<)vzɧګŢdrdd> .pɶwI)ƛ'NlK&]]}} ?tWsȰp6l?xP<~䉓Rӧ͛;fL-2{`x,?0^YQ{㠵N$⃃CT<ƛ ng{qŝ]] ,hhZ_roc)BP(bUUUUP~ʦNQ5$Ξ=ky_7o AHgձh4r+--z-MD*r]va>zR.Yh ؾE]W57fsX_WNb7m{wSeu[Zۘ JBkB;( 5@ScQ@KKxy33WYgVL&=]뤮+V.+-}{̛ߓZ߰v<txޜ$魗 )_=GH̏hxIp8yM[beii>| S:2,}P er_ 022|p>KE"!e< v,gV]_^^uJ?xyHP<0۳DWWUs\$n9~w\q:yuuu=pmobUyyd,U!۶xXyYٝz5W_?DQ)eX-+C\R:f^F;P '#?hҵ J$!ێbfBX4jf Ҳ,7wܴyKyYp*=081 _zA6ɰpAN!g, WybU'튈x|kz_HNOdrUV JJX&l7f!"Y3L)61|ǚ\pd ѦP]brdF*IfFyo$0!xвe0הTM*˷vAL@߷D>J#bJ&b}78llBٔٚf XiGc^Dk^ ZA$4 HX ֬NK3D$K}"2VkkfD ڴ}F52:`6 35WDym)Vc{bM, ,ן-+-=rؙO!f>~ֶO'{@PCw~G9*m޳wߜg;~|־sswC| ~kϑno>mɓlXtv?Fm|T ܻl6(K'N?~7iℯ=N]~ښ ˅CosKz8XUQQ~Sg2TyY+YΦҲ;?n{}yDnl_=;88W_y;WƢ뉟?Ȼ$-W_u3>5;{᥋.\re˞{g}3iSlܼ_I'3{{3IS~o|jkk"?43wljl++::jjvqkV^a暚;}}O> ?},).ZGEDxnb˗]ʊ__B456^k'g IDATX,6m'~fp'`믻f+^]%xUk~;"!w`媵\}奥%4Ӧ։ڳ9~ ,W:r"dłё +~0 ԶR'hcO&D?0CʪCSw@VvUm~ W3 DI(mHY\PJ! yTz/O6OGmIfI8u Y-΂ 4hGqdL@I&첉yJ)S&ރ着/jI  -ɖ B]FҰp.w""6z m R3YJi$e=/4m2HyԲm'G-4 BciN 0 i(<7xBhK,z ۶Զt!!4(E>fdgHRLIJD,|re8ѠyN$1p(pCz lBDQεWRh[]5+ $E˧Wo864)@DW2 ^JR$%ZD+smkDx5r'Onw884F:0">='Z͇`)Rt:=m4hSt:̑HT184dx4IGɄ9==h$GFR: iT*URRb[8éT< tzhx¨p*vyy9"ՙ<###\yd$r\.BXS)"*I&R==9NP@Ēd"H2T*J& `8*BJB&bOHeeT<m{phP(ZRx<6Jٖ;RCCCbQZҒ K%x,Q---chTk=88 XPw]500/>ɦxᥗٜݛd2|!_LoZTjd$#pYYi_Yii8dtuUTrp8mRee044 {'usnUuӓ139Hs@gl{Mֻ~+ɖdhbI0  r`rꙞP]U=@Ҋ}VUw2뺓SRɦbdZf<V$(_NNMŢx өT*U,Rtzj`Yf*|>$ \ϳLS-~~KJ;obNNM9X>CBaڴd"d2'T*M8HfDRN̔JeBd3DJ4556>CzD)eU; iDL&cuL,vqMh2}|bQavb)T>?e!b</K_J4l]$bUՙbCC}R\/͔es5Cx̤/fyeߙrLV)BN{!3q7FUE+~Y1ai߭'IyUxg/BaPMMM=ho3n@W T{S,5bfګY, A픚 77ߛl4OmzqT̞GRR|P2а0&IJK ʥJQufWcTR/]-RJJ%9lB$$d 2-AU>a" |LUuAJ< Y)&@$@CH#sɂ)J%磔,%KEڣ3T&]t.Clk_E |߯3G'~03ГʓY9-5qmjMYy;mY%c,%>x>HJא'MB/_osq\\_ 4RbP/J" $D=큵M=\j V0 R T,q.0sr,)Q}Y$zak!]9-6VC;IiII%utSe$;;V_x2uWq1:JReR,B2ABP`bZ!62 3)tD,4M]׺NzIm*e?6A+U @1(jmb. ڹJ}+Zb&+#GB@ vLQk8*-eRV)뺵o(: F!%"j,06G@:R BDڐL\}sNu5 NNijdR|%H"DfE2 3ܵa0TǰZ0:?KVc{ڼuivKgR_g)A1jWVRjfQk ﺢ!l֊lZϰR0u]T,H}jvrq<< }@Lu~:icwlx6}yi7\w ]{ڴL~~޳G$NjfSsy{\UU %a@ Ӥh4ۛb RːvLH%:\N/pŽR,BĆŹ,k hrΤj-a SG|u Ǫv=pE'U`-e= \169YmF%|۶˚21GOpo ɂ$%KjXZ$^: #V@xFei<2)qﭸOǸmv .DdRD#˪&-JEz%qX,VgY[WXKb'ʧ*CvU+/%l1Di Z䒜՚#25:X)"۶`@-fCHRIls5CG(A0i~ZP[jV6Cs4ruabH?SBcG], , FG-^:)ެD,RDep:Y_z>)֦ڑA9/Yޜ\ӚI߫Ot} !-Z4w\3? .#|sDt:[j(d.dZĉ♂3ZUYV* YED"eFCK|̚H*]֖"z'ƉcɳũJ2+Z`3LrZV Puݚͷ83'\]]sWiZ~_*%2XC<ޖL_^I)}4Rǣa]ys}gFG'+eǩzR1 j{$jeֺ\V; h$QSJ.%^gk15)J3电F4L75-6,Y3֣iGNqa}|A.ޚ._蟑c%Jۓ$s\b攒f47)"m]θvXgg߹i⸮$X"˥ͫ_}~.!SKk3⩮qΗݙWq% C1bHGl>>ӕƒ3G"}6r$kVe˪g~Oqž[W+XPCV-W$2掩Oh4*FڲhQ=)m#1#Ƿ,\ў1kX|>UvIz#hQ8%x`]q9.G%g2t: UY?rx:hEݰJjj(""0Ѿ8amM.>5cnSt|P E/ȔZqDhXt:jjH^]w]} u" \2d_] j1}e* LC`Ԡ=~oۗխkIDD@hVC8W<7U-\Ғ{, Jx²m.k ` SoWl(ziG&g ahVoYbs⮔]yЁ-Ǟb_)_uy4Ōќl鬻m˚քTZDsȏѷz *~WgYΛ"9Yv\>}Ϛ紥U<[}w&*ۯzWJYL7-HG>}Es:jFQִr]wX|C9Ʌi %>r6JĦ)bs#nXKw(Z1t4 C溮8=no?988]-V'X<47tZո=M}Q)]>|\\eui:ubZ SS5-U*KXNah>@ .a?tao+@ TA1\OU(3q{f7oEu:>"*Ve9^ue@LH* XI}\i ̑.Uui$d (ѱ~F $ @ԅuJd<]g+8g9<_va} i?whh ȈȄJIl_1N߸_Թj^kĮGNaVYi_F7|X@~;[<|p{}3si5T>wȗ)A1#pyGP%TIYq3O-_4/ZE?]7oMBڞ D bڮ]9UsS'o_k5% g7vq ,"``J X*zi(k]g ַξiSWbCҶd HIRԖ^@adPEw(2Ԭ$k7~ؓ Fj*`hq&?c[}GW?GMtTBDVQjΜFۓK+c(X$ h׹32N0@ Y}'H}Sսf:bK Zcyw3"86R*XBMa 3M#\Bo3%G[[C8@pavˮNsƺe=ј<۽ӧ~=rĉ=ϔH_KKM^7xeu9.am.ei/A=|6f>2X7U}e`d5j\T_큁NK>7ϟOD#a/ ?su9Ex9.?`Vr3΄E1Ƿ9B-S* BaHĞ>rnV5܁48JI?`u$E}xidD0s@OjS` Ђ10c fiؙ#;Q^>5a{2fӪ0=D$0|a̢M9;sߊޙct  jނ8qv91nYJz ]SɈI4 D fv8D[d λRk2HvtLE']O 14gA53ShgM;64,Yt_4H 9=4 K헻W'#X\JQ7h DD$dy#c@?-j~nx>8n O*A$[c=aC!,7xy;Ͼ_(u<=}ix<֖H$GFF+D)p  'AhMQrKg WtdLT r%GM#UE$AD Od fACmiƤ@ 2D=*d&$`^F$S@Cj_¬{I)HF \v%13)*S' eL*k;LM٦!<1Sf]_ ZPAUHzF cwi-'4(5t|>QZMB6koɪD 0]ŀو^% IDATM-#"6A$ 8ݧFglL$釉d zc AEG]y3_86\ 2L&T*xlӦcpB"pMJiŋ9rA.DF\`O4Qľ}Pec%00 tfHw~\._Xr# ~vJ|I bWdXj !H@A#$D4)V& H\'C&YH@4(+R+@rPB pS^b_#FMAD@OT+X6GA` DX7٣=@JiLD O+O @n&zމr_mʍ!LS#"J(||0@vbdVL!-Lr|R2[1CʗG pp 53"jL8nсB#Efv};B$\ɞ$k>(D~ t{Z3 'GK*:z>"F B4b`2@4!D /:LJf:+SæǾ z@>HR  p?~zfÆ#%Kx/ [•)aKWY!#v sqpO샵z[̙3"SSSD7HvE)]kN[{zzr-|ĉn,]oÓ:|ܚoZw$dT f DTCyOr\q "0 oyR>13+4$ |Bz 0D(LaJMaQGV""f Z^, bI,pځPhrAggr"_ژ- RiMU0E=?A%D7;Q$$_*_/Y)f|BK&ԫRn[3N3Ub`DJ) hR"]!e z`Fbdxj IUZ<l B_@ľbBRzYtC=3Ddh`w.?8eI+L2P2*_/%B (ب}._2/Y2 !(P Ȧ@_)% B-  /;o{SclC<RKbf@0 &@ J/A 2>(Q02>>01^f"b}0AHI*6i RLC!"lw /fd@*<7nV+OakQBbRTuEy722[{#H$)Tav={-.BaztlLŢ\}.L qU"HKsiyw1נgj,3 AxV3jU}dIQ#)BATx 8Cî릒{Ax7<7(Jcce2Ɔ' T<kin1MqJb)}QW͋C)U*'yu hiiN&ONN ̜564w d;{NLJnj]gU7GEjK\ QNɘHΟ ^8 ]eoP`"&4N? 8^X)VRI< d5aHAii5ν I穊Na0R&їJx2l"3~(֖ Iv @@T'G%ח QI yaEf &` 5R)"-BB0)^gFgZF`~ PI%g $BC,SÅ)Wa0 #*@"b-3 V(Ad 2U}5a>s- -`cVHZZ!(LT&#2), D( l# iHh0 A<4z3C&Vef0<!0^F s004j~dYauRQCT L|'A$M 3͞U&֛I"8R{y)e˖mذAJyiӌc.f6M󪫮[<RѶPJInotɓjo[oO76[%vۭ7͛S⏌}ry+/{|&Go`_s[Q)u]w'. Awْ .&v7B1 ߓdThib.w{"V0}5灃zfeR۷޲eU;w=_ItOwOwO]sG{ܛIɞ޳/0=ìL:Գ K)ݳt;bzfg)J#o:3T*^x?u˖WuK}̪Zw]6n8ճmv]w~GW~nwС7z}q,^K<<:2ҫ;4/#Q{T!][5heO΋42sŠ#omiD=S綏Tƪ^Ybay5 `},JI$uxլE `PB"4hn@ HΑ`H; oAK0& 4LQ*RR)5jPLZ~E4P DȰQ@iGy QJKV{P,]ː p@l̸҃@-#f$dhY!P#֮5gD $ Y!=dzZ&Eh fFfyE0NθJdu VH,u6vAz1!_q_'YlDDҘ DfͪjD96S!$T<f0mBl3anI5EN@B E[QJ)f{bb謪#)01,#!! K0@۷oH$Baddtvs=/fI)O8qL&uVz{{uOJukRJk ,Z<5|?+nL:}D׮YK_\"}zs_z RH&k,f\<ē_]|Sl{gE̅G?~7߾{WB."\f#G}?Ėx;n{ܣaWwM@)u͛Kp˗f2{O{ۼ|3]ݛ6߸qo=v|ѢNCR>|]455|JObV민bju,[Z:r4,߱{:;yw߂׭]s^2og9yW^rPxo˖is؉_Z[/j?/X'O=K/z_zE 5fƦX,w`77nXf /|Dyϛ{U7=z|Ŋe3 ΅ .Y{o=u͖mB?}yW ^_%dлx|t˖zWH#&߫$a]v̸zr`|:2I.sÓ'f̭J 'QbU-|j%!.Gd$`&`@҂jfIR͒u+B@F&$f&1p~Dd^?#5XS!D(ʕܴV7Z֭zH*9&6Uj &XT(;jHZP $&h#i0V)0kA dжJ" ЌXr2aYg+4@ lQF.I8ST8Y|SX|+l9@3%`)AJVU| 9uQh@}jE@|#JJ2ҟFN "ZBHM("fnD8*JUUWia$b(hmm'q)wttض],5sgMkk}966ʎ]Jehx칾 FV,_ށo}\F\eWwr#DT*{}ccal{a^lOO4wu/<ޫ6mŢkV&XԹe`<O:.Y4M˲(~{|WyZ1fdTj+8YeYVm#;w=`{wz-JRGSm=C8ξ}OVg}?LݯL&Ng!ISgjirh}}%o'O]|,sx釒Į,[۷'~ɇ߲Lp>rիVD&L0M’ 2H튣|1sX,R)H$ժCZxid"麞s>gJRgj@^۽CĎk]##g掳,[JJy֭Y_jl|zx…w޾Gvg?}Ʌ7| Ku|H}+H$rБww=w}N$j44o&.8̶1 X.Wjߊ{lgW=S}g4R"Ϝϟdه?P}}b'ڹkeY_/\!^K?P>+_qKdRV\kp HrM3b=VpFYY˳o˙d{|T1,JthamFD?8.T qn.)+t2I=DB$PDJIh`e D4 1D}9͜_ Ho[z!m j"C0Af;гpPIZκ-g)ZƊ: ,}IB`^UiBdC`p2faqFkHD m F7 dt̚TJɹWS}*4bԭ@0T#N:(f8v @)Ԙq*K3V)O &KApx𛁖kتʟ4y;3wK4GۮK{hhHw:T*(8xn8R #?Z,q^ٱkhxt&~?3WeŬwj^>v8;}eS~?Rw;5Wes7g+_LBD"L&T}{偡!۶]ǕGl&N. N$--M+;vťuJ.dq}ź]^X,*H8U'H88=B]]vjɧ~E# BիVz<_botܴicOO|:ܾVVj{v~vo[o|ƊmkeX{Gۻca ̼vj4vU)noЯr˯xQ%W*|{8hos]=ol2\ R^Ыط`aRӦ:.K*3 uќ߮'wnÿ]n%MRƼO6z/5+cNúTúαʄjE71V"APtR)$ɜ#kWU.HR+`dŚqb# sƄPC&th4x!Pi5`\m5ƁvHKX/z`YӁ Gop|!I> #0&XfɌ$@#^iHhA:{/$/琂Cfm (Sm &w5TCJ `AlҜ%*DN20̤sHMS+aN33 C\Ur!1LB AijÆeYw-Z~{/kryN'+.PW*fh/Jek?K|mu556@&ʎ]=ǿ!o}v˲6m=-D"E"F#%$kg~'|1}b2?99bO(D"L&ٲz˖.k?4ͮ޳r%H)O.z( )u[gx,k !8~[6ek}u-JRK,yw' ۞{axd7Xt)yӹ7}ar2d"xѢƱqR9x_~SD IDAT[tPJ֖+C#FƵٳ}ma j+=2:Z9v<'qVC}m_z$JꍮMMRd,թƆɩɆӧt.Xl^ss&xod2fj+n,kl֬Y[o+Ķ綛qw1L^?Z>ÿ|bo-Wm Zu"+NYʲ\]8t_l6S| HUOΔt^$cj-ݒo2W͸A[ޛgo۲ǜ5C}$񋴆 *st nc{7(A!U  a,3h d|\ !(h ={ u ‘Rb-Fd4-*@Uu Z'h 7!<#+ưPCl&fD@a  $D2 »j#]H9+n"B ݰL A!a%Y@ A|Di.L2 @E.mUk._CCآAy b]FԒ|48:HDJ A:hSXw ^$BAj"R@7>(28?]|xg_]] n"TvQZ?~\nͷL"omi7\~J?fʇ>uSO?zx>754Tʞ7$/R_Y0~kKSSS';֮YuӍ?y6!7߸aǎ=N̟bŲxWXQK0}'a SAdJk\.ݦɭǟ1MΤ׭Y]{ߺsk#=7OLJ422xv ۞n}nzիV"RLWCozK6K׬ ,8817L0=]޲yө]qզ ;|ؼ LP>tm̰`yxϛ{6!hb"K|.{`9Lsof}þp}IDIldOmOx qmvĄm-bK(Y% }yxKի=39ެWA-S PUyޛȯdvl~( kݵoco/J uoi==݄x@_~{ޟX޵O|m3W~'? /ٲe|b|'}^yNoO} t&1Ɯ9{{_ړ=wܴ` g}ãccX*RZUD˫['^y_ؿoϋ/|Wl M={7.ivgRs[>:hjꚗa)qM\o{Y-,*nG;iZ8etU<+bg0jadaa?I;*O 9kZѹ/L$6w^R$lgK`X< ؜VBF <֣=C( &F$DJG5iE%Ӿf%b$g ߚ^Af"I)l&+C#kh4蜆 $tۂ{*W`\~n7Zm;C{bJ2]%hHCD]E boqܓStZ/F4A)W漫e " \a'J,lc9fBd F)xʕ|>vGU͞pJY*8&&'&ym[Ȉz{zRz=vpoOO{{{[.w=wgl&{DkMe>ΎFc~Gk޾cl6CD㣣TJuttlú1[o.S/ymŭ[.Dصs"*FLl avuuvuu K(kz* tR),Jqdqɤ |XRZ]/ۄ!?0 /S%gO9wϵbcgM!~~MR0a!oo=߰woJ>w.x? sK ν׳N|?yC~DyOޫ~o굏)eHqhڄnkN eK4i]i;1X"fc$N:_;f#-*B6;,?7f 1c#Sj7KQ)3;=VRa-ph=˪HcnL_FJyw|vtZg$96řz5ؼd +z ܛY[Oi߫\OV[uMHt8E_m cØ1dbMDq͊N54SRF¹ƞ_|m0&>\Kz5o |?ER.VJ[ӇƯ꯾>ڭxM}A3 8yjuq [Ǯ;NL|a^;@[rAd`AE} Y?s!RWNw+-MkGfLI"kq*DJ# lG:!J-I um);|%3 5:(;͜rlzĵ=VyAiBlTZ4XKǑΉ"{WjnxAFR+jXȲk 4~kkJ+JÍhEic+S'k-*WN #(Ӥb2Vka] @6 "m u)idt*5Hy MQ%MH%] Q) OIM%ϿQ+AXn[n~S~7~;^)5bbn$l14ЅPF ɘxb5A~4B ˳sHV~J2sDH~S *N 3 KX!(p1NY@k 5G '*JD4YWRPbz.?Œ[ȷi#`H{?7>y.b"nK#0M x<ѶgG$f AetyG/@M ׄCC[zJZal@!4b#m)f+&i 4k.7 ѹ"$eUvJ%qB_+S7'8E|m$Db t"/LZqWCO m]]cOJԆ`:6T",ܑRc0 կ6?SV`5+ w ZъV|CT6F_@"2 p<$GW`IӛyݩڜV!ED0#$R/BI<$PJ涎u(ֳ>Ă""ּya![h1N,j7>ٗۨpq=4 [ \D9u'A_GDOvh:Bi &Cx\[NFdWC<\ea.qlGd4NTshE"OWT ͞m}]գW6"fA!F9ְ R72~T A0UQ]kv]@,_S&(7Pg{<:(SN6+Af`I}eQf32֙ٛ>T(V| b&&Tz3zF.1|gl?/72 `ht[g+gif#$ ",a5PȪ5zhKޮlF{¢V P, R`RM[;Q4yӃIOm;<]Od 0h2,$~zO; y2~ޗړ9\5!1E1#?ݰ-m0wIWd]]i_ ;ڶ,hhܩ0U~[ІNPo+kfKQoŠ@3ۛ?P1AO'~( -ؘk]=~OZ-BO Z Aq}ҡxDA)b3zQ!lzR?srC@`Ad`D.ow6Qߣ&&E`XʀoRW!=1LM],//zHZъV= "+뵳ON:嫔|5XWh;i{BSzBAmۺSܛl(ưQ4 kU` ¸@$MY[q6$J1@s!55Emb[»/"qlR{{{eɈ (!C l,r~hhUimo}zjLkV!ЖRlYz3q&P #pj٘(&ۧVKl| Z K "" YPlwOX޾ѝ_:yʪI *MX9WkUMj7\J,B38sR$bi*$] Ac!3~`8 ?wTsܑg8f!TJ.=SxSFq}h]JbR=S+OYo T`*XSTq요eD+R GZQO?+z/͔kQ{ZQ1u5q46ҊЪt5qW?rtH}FI.]V4!R(v yEQҟ zZ+K9_ж3P2<6̀lG6j=}?0ؑ*J֕4PM}?}aq񔧐EkbcJED+"Dcs)n~Ⱦ.uQG~3s'K0RL E8-@#D#qAdwe;[=7^*Ҟ0 ySp>%V@c98kPt)m7j;?ПqDZy;Ӎ^eq>|U`V0 7mra^W]ZQBش`U+ZъV B<>R /}O>ϯ-٨䧫j6ZTl;FL[zEJwg}WRw cL.1ѭV frkb5fc؀5f$RD&fT*w}gwfP( R*"u'/N,WgAD+¢Hr7t*Bfj!"3k1_'O̽1W\,zDaHRL+jKw}wl"c8VJ1(+֏!WmkHg`?vwm{3 r}Z+ϭ^,W-1}lE SO;{p{϶V$EHkO]>1W\,k%7s"s$\R_qCÇwo;zjA*3{aSJn0;QR)LϘ(LR"233fbb\ek(Eh{{zv)([#~qׄWya+ZъVo`O}q{Wl̕sR%.T8Ha4~gwjh(mc_J" dOu4" ;_T9T^(T5SD|h oLR\*,633o~qR`HXl~)5:u`h{hgLΕ立q5Јq-F@< R:TZ^Z\m9޶FbRDE:~mWe-/ `Z@֘NQoH{w-{"KF"qsl`aO_zTũ+R.CQ XL T ۨMat;j\ůϗO/kj\L=bfU[O_^?:cKU]z sBzUUH1F{Fϭ B51v!=^9cK0MDQa(GGg'抳z3~D"h7^YH~[,jDWk#Sڳ;md|kʾX2]HF[1(2mGQ:NR-gV}g>c)hWN.yXz .2"s]bFQГ}ρޏG A-Y^'S3EX@XwKX8ЇS?xmqc zݼt| <}*U1l]ݭk8va{@bq {v'tJъ.~ȕ. p2Nl SƧ=}c˔ fEZ˳c3,&PWtLvOvG8 Î.WsG/~]Z 409gmvo>{)he_z3 vo]uGJ}r_\znJώ.=yzmTr췄!iU-!Hh#xtw9;ZkJb_;>_^_Z(ms}n¡Ѷc]}ț(~yGGf^)q%@bVfk=9e{s"NMM}_n\M`!b___K+Nn~`UkF- 0 f.ss[x+Z(8.ċtzEDr [Xbhh[$1KkG?% C_N_*-Fd'1b q.@,,ɳ ~53GQQ/S)`{1` 1`5{X/ *y\SF/7s/^{)OIr5,Ȃ,hĊ~PlRSt>81xgW=Wrr%4h۠6fF*wa!@2Ͼ>qG'YHkM?~ W@+Jug0pV LL?#LƑ/Mw.ݪpQAeŚV]g7.WK?־/"Axwi{/->qrM8ɰ,bPmc̗jpŵ/=F~mO^]F0푃Vq"@rPߨ+vًeMk@d 1ɪE`>saٵ5UVsgR46ZB@ED=gfY@^GQXh,DĘp!s@Qt$Ia$ @4b揤c՚|yz(S1LKrbL`vNaڻS)y?_xB0`t| 07+7  7G?lu]Wxe!_5i/L]%A/ZPKǗz?ɮ\ZD+_X9PiKyŚ4-k,iƿ lfg~ 0f` &Kn^5,ſ[>gJzhRlO}ߛN럻k,WȕS 6_W#H%+rOΘ$@ SgW4/?m+ rSkOP|BűQI} a~i ߘ+ӑV1;>,I&,]ŠU"yfz=雯Mxc5?cEgϞ=|i3w|3hs+ZъJ "X|}6򲡩Eah[AW/vAURa}9}K:Rc@ǚ=HRgz'_Կ./>_RR$ypV( z@[I "?wfzK+K3#Vv%ey=.6CJ|lg#S/\ZK{ԑ庉$AM:)uf/&>’DP`5fZ$ɹމlj{zʑ|Fޞ֥򕨑L`: yxdѶ#r-I [4_}fH KӅ_;FSOqf-+nOB{Jo8+*^+;Wcި9@ӈGxDܨ+kzR忡qAF$hE+ZCXYaBm\[f \VRt,37+Fkd ?}+U "hρA;[?Gxtڼq*z܂xZ\R $iIY࢕(\+_fEo^Y10x]̍n9s0Q54_::ZX)־{vRLl4B{J)75@LlPs/50?X4q\Ӳ@rY| ́k HA[N.Ms/)7*V!9뫬5X $!$tyJ(±ؘbꕲ@=k_HnƗ$_S=}0Qg.!+"@saӢ P|}b\ @[e<+lk R ہϷ mWǁu||/Va+ZъV@"_};޹>d3˥R."G<R "`#`{I(DRkl5Pa }@8T"lGJr[DhLQdN3c|M/n\,Z!7"&jD4Kl$fQQtfDT%4gWjJE" 4a0"1K̨X (LT"Z)Zv@'a("h )=ANo1ɪI+ |X^]Z[KK!&hLҰa6#I2CP_|v-݈1 y KlWʢ؍|6""(2rnhڨ1Ɉ" 4bوFJŅ튝+ 1sdذ G ɰ2"̮E($FzD osuٷc.6TWR;vhR)_7{ງ V+ZъVwa$*ū,N(`" DD$#Vn)Gb(S"6E 3Ӫj4@@IXd@!hj5JlXR!Kk1BEEqevi5Gt=gbϷ>: UlV6GT!J"@0X-g5NC䚱Y# @PDZ֤F\k"Jʷ mVA(lNK8 .EhW(ո@o|+DŨ-nl?91W0!pf azb5Br%#RhXbñ11;-,E%@@0 @Eh>! J\!_bÑ#m BZDՄMؗchjr{ZC6?cٳg>|##p3.Yds!k7z`0T*Xsw93ͣGy^jfl jE+ZF⚩"C !@CCLVn)$ ^HyDX<*-̶/ ($ w $4ػy"B ؖ.tټ&6bӰ3(DHDk>!"6H"@[MY>7( ]5´o!v]5i4S& &E[ b-n2 Gđc\4""rSD0z5ɆCÑg^E@؂홱²@J-f Gq3el9;*l/bh1!4ЕdeWA5~H`=EE@%lIMYĂH⤶QU((N˥AMX *DkhFda>7V+S;ZHR? }Vkǎزehp\[_.MM<Л'O  p^' {ϿbES|߻{ hE+Zq`1  {!hbalO0""es2KŢq-hFm u7!܇ٻ * J "qdPdc͂+k! A) [ȳA" ! T r[ؚ!a6W97 "(w/BP*W_}3o|ޞܱyg{wp ,տwzzaG߈M|/r.MRŮN\X|AxZ?y>xOwъVbѶit 7BgMf 7-`&+h (̞qy.K,> dq+7%&HQi)"\Yr#!6R]P}У @)$8,]I5 }6! $NƴI7q)%)=UJk m} d"p6Λ"([("  ɈZxMjDH*E-L\0a,ku ۨ#6n'o#h|E,Sۓii*ɿ6䛝A>`mmS~#/42<̞ا>|O_}Ko71֖{?277]Tnڿ?pvunx=z޵spp`֛oڵk'3ܱ}]"o,-/m[vIӗ+G^"gK }Рp̹;yt{{=h٬^~} ? @AuYkl$m:;Ž7ff@˟t$$՛ ClMYl_>DX0l+5֠Q[.w "&/EDQ455uXzS/ZVVrАz-]]]00:2l, ...]3B㫣*J)82==O ~m(=orֺ]3v];gf7h(^~嵳gΆaEQV6ՏѶ\\ؘ_x^/WkCah |c.L#Fqeh] IDATE+CVqw<K6dEE""-`Xyͺ]=B EZ X@)`&'6LTN`6 WHZ?.QbQ 012&I6EWV#I%Om99&nh2Յ@ b pM6\5"$,}'D0ư1 &U̒k|@$1b!aFld7ElD3s2y#,lXU9Fy=/ ת4&ˢmvX%†4Ux3"`%r( #G^i~Z-4kXJ{>+[yE%vtt\t)c@,W*00p5/'Ѻ{X^svww޵ggf$JA!8!9;QN#D헦._tɺI;~׏?L&oRJkoXd]`mvW_>r/O<|۷=33_*(< loc[YT+Zъزeu/SScXb,Eh㩧v-8TJvyow ?c=]]vٟqii:x-/3>e;LNN>ޛIv]gbgKU[b#A9%τ aCceeϢ1!ϦG))A@w{_V6XOMDU{RsoE3Lo׾_{io۶n}"UG?bTڶu/-Tun G4KɅWv9Ztyq2m5^R=b!9^;vǻnMBm넬"N_QF e;R/̃!"T(^; 3ZC֊aDL 3 *Hp [ H@Ȃ18xd:¿B~_Ȉ51$h1CYˠ5Y4hࡗx$ua]@| 01ack2qB/` Ye ¶d%:ڍЮ3&2>2Y&&D,6ST\ ?#aoLЄ9D_mmOY?eDe˖LNN`q"YkIDĹkGյzngg]Mon^_z_/j]O?Z791QY 1ߟVox>~Ԑu bq ~3߉UmѢ8ޞ~PH YY.iF/sDLb)kZl7[u{K;ɝٕae<HU+'[K괗~#BU Pa55L2&ÍnEK\S4wn`/"B6crhGؐSy??&1)f9:Ak ZOS^^hT{QE\aW1X_'WN@ w_sz侀k^.ܥN 0coqՑ!Kf[k^X%k/4wBmdSʳ8F‘ĆajQ{j;sY.b/wS?h0rރ k4OmoܵkߛH\k9C sx2y`1:%Ru9l"h_tj )h] +hLE)S'5ޱZ0FațfA1]oaU1'}“3[z"!#m7|2w=Nr0r Dy_Ch-cj X$qFA(\EuC?2F䪺eĖ6K `ELLj3[jͳcYU"JZBXNrKzUXFUI̡v+A' 5=A #CFKDMWnV{bzGL)".L2Bd#C)b2H}+u,k#<2xċ܏?Oɇ{T*e7ߴu~V/y^}C 1C 5;3o=^GlDBs WHA;Ą2n+ߐ਋#&UՅ@,^[솈e`r]z}(Vb @A!PL=]FQ"pqNWV43C HD,9;%B &㱢|46[fTmcqrlѓKe_$" ( \C}plexT ,齷+eӢ13SP*Ax"2cU#4VELlo:)1C#Q(G P5T"ab$&K|T92;'++G[eP'$" "Ayp ^v3u2wxrCf쥅@2N3' CPjz a-AlG=ܻZf`'$s ^?r>Cb f24}Цi\闾#>hsZ[Ϟ=w[n9zÇÇ?C_WKGb!K8|JG@ܯC$Uݝeʄ2I+ؾ;XU +$!yT@D$keR&,"s恻i7_J )"'ztmE;#`Vc ZmF&kM7'/\FĈE [Er)z sl82dYs}$JJC_i.ri`B$?tpzDU\M/w'`"5Iĩp~^=i%͖}&In-`+//T,!`L4gH tYHrA_bC7>oW?rpf>\b }du즴X} LNul7YKXD;^bPiw n86Z^}e"&BdwEι7U5ݓ#DB@Wzzol nRu&ry麞/k>Dp#Ggff._JI.\v{z;}T$ǎv9rm%It# C 1Oo J*Nf[3hH xk#Xjن{E_bKj9aޭ$ oRq}wBD_Dd)0֐!en-6bCoC3_:Xf\ wѷs uy^DS>|`5j$#,`y(zx_$޿gL}z/2d8ƀ\ s$p;*eB礕MS08w쨑_=pjiYT@vy0pW5LT깈{'MjI&ۦ=yi9g #FH7@%$k@Dx`7nR|TܹSo9?j[3qpAcŋgggMis.ϲ4˲,K4R6a6q S&YC SrS;ͯ~N^z=^vǷCoUhȽW:PJpfp6=Y"D F-|q3+z7ExMKE|BT1ob;{&X}a^e |&~D';QTM>8]H<֪jժT* Gt:DD9$< Jvpn&!ϳ,KSՙrkmqkVDA\ngc@U̜s b 6|QL"ZkgJ㇦wζ-4=<4%o:9:V2l~86n{kgG[xq3襹cfQS觬wPMvoD%>f=KV: `m8!b LܻslDId_wN7xpgyf1#D{ަM3JT*=#O=sT*yVeYޟ2"kf "wī:JTV{]C 1TleY VJUxϷN-wr'\ǷzH-m.)QK:tTU:!"n~?‚6s<*3=E;njJ%4ƵD̲99TK|xt~w5zCM, ZK#:Zu[fnb Xc,zI1#wdXn\/Fu2 F3QIЦJ¨Lg諷T9WIc+W׻kTX<m#GN$cU"I$Z%Mѧ>{(;qyUZ\zJg9F5{v&6 |,f,ExߎBK^[j\j-7Zr =gb9Y5ݶ}lRb4M>#h9e;kK3+Fw;i@jĵnO4:QʑU(dFQd!]S]zwջY &fS57]>utX;`y^13PU}_XjZjzk $Xbwo4zpfD)z=1,7<-D/ݾ}[}Nl6{[nɓV^_߹sGIjb!5w UvDF;v#[W.7O_md+[m@T5rَGI@D91u]sI/^X:ڛoe]E'Zb*[6{['˸v{T90 #49.R.idqfʖK6 2 *vHU]+_'lɭvF*]')Bj4^M=E(C%1EU-J^*v` Kv2oݠ*d䦊-oUMw-JKR1t2iכZ z7ogwTEFbi4g:%&"OԂNğR)nu,]NF^SP2ջDD\XiM9necibwb|JgwԩhbfǒV/=; IDATL:SSkמv+[ [:91y.]D R•h2Jth߹*>Hz$(?>x$S8~SOMR#G\t˲ر~x}}ǿA{>;{]b!⍸pb\ϮZml||x=z}˲ R˫^h=y]MO}ttEZ:Sgi>8`F͕LƮ̜1T*sӽ<4-@}pO8lx~}#ǜe[}ꇗ>דlLTAlj7L웼=s9/ss/?;ճ/wz fG޾#7M=8M_lu慅o[~z$w\uLy獓<=XWnuke$}gNg/h[';YSi^ L:[=xצOlF0γ(-..=KIpΝ;wfG^VWW/_x^IoҮFYdN ]cW{@.W^Όĕla-`#鹅V|Թq֤ E |'`D:^r5?׎#a Ld8/5R\c-| dҚdcmRv߿ݛ7s>gϞq~ DU@1Cc~;\xLOO/]A70 IL%_6x5uXE¬AQ_ZΤ>OYz|})lFZ:l(§}a{|e Z>ɻ&'_*RB7Ў  ̗r ^8j{*'/.n%\u.ϴBUcX5'kK+}xvgkO75q̥\N+'^:;5h}KWן;qtX;KZK @}Rn^@@:F_[}n=$EJ6m@14: *16\kWZܥzd_g3Ɣii[:AH)!**"7٘v,?6;zmזϮt&J' "b[`#^Yྉ8.璕".4Ufߍ-FC6h-'>s񯦻7D|ۧ#^pݾv$)_iw.N:]'x7ڲ!9^?wrDC7A׫orw:))}sA엚;W:)8rQpJ(b4D"*]odK\5F WpTR:7bC~82# \R/A#3T+.*B DL` #Ux-uw:"z g"rux }>TZ9k孅YCVA\%@\/ T b42dtu98]QKKfȖLڙd9*y+T!$QEP@ LlȞ_[drjb#D@MoTCIC^ZḲ'gs*ӞRr<BΧCvC/Ȅ, 湥EǞGof馛R9O~rx% _C 1o]KBW.iӭ.yA Has #d3 $[ Xy3hTķCb(#BFo}Ll:"N@T"5L6}`JNɵ}KN8qNs !i9 @%_ޏ1̭&]D=O$"p9| _..vVbKNN.@;4 U#WP ybsq3 sNg'DB HB#"ߵDؕMS Z$TE¢}L Qh,("Ebdڻ\_mfAEї A@"T'J9"/PPϮ q_׻7- PQI ,h,BbbFΥܽ[mz"n]ht!=u@A _HX䈐|`4 [AUu.ETDB}V"LAK@k1!"^2lTp28JGAd #{I Y)пPD J'3LL1b;WThA΂>JC*zV@RϪ!4&bLz/g9gࣸ4(DH8x 'NX\\z!b $C@ N bA@@>\⫺7 ,2O lE%1* e~K#֦ z:]opPB P$DT}?i!b4ƇҮ0AA sp@*TxD$&h(NSf⊓g?BȨ^LM !/u'Yϵ [$ ԿQa*(!y>'X\U3"2LHJY.f\!VX?BD%$C0@1␾Tev5wN؃>JX_ QA"8fRH2 3U7FїD#U+  {]+M/~GJ2E/w C5C zjez=M$Ijw>OH5=E=&U ~E"="F@fFa*@$!d!Bc B.Oo(T XgG7H8J #w*.ľ鑑CI|X /;=Wႆnń!H ;{wKEh%<`Z0TT@swDDTw>Aq A apvDѤ>k(Pd0,}}x[*%N&xRPC}_l)!A08GPߎm"a` Һj>k?|Ϟ= /\P׋!b!x=$E6S(6<97mt &l-"#aC bPvy:U@0)z}6B8{aX-{m!Ј}TBOCBX/Z6409q-T8@x=]+ц a]Ѧt'EAƈh% {>[I(oT̙3}nSN :E7" C 1v}emm^:AVV&'ku=şEjc q΄!"*!J1q: e`0"VDD\BEb!/Al3>d#!>q S,`#hE ^r]Td+3BHDCP"%T"ND"XbL,qh,(SP/CX'ZČ?Xl>EgPh>q/$.E !E1#Q`,X箊~SI%HbL>%-A@ơz_A}A"VQBP"%OXF8hP|##!cX<!)"`kV= Z3Zk [@<8#t|0:4LNAwHdq j!nGP=wN^Opl` э!$b'0{G9x݃Gz?cxzo' 0Uς p)/USP˱R۱75وO߿MRZ#5 9{w1/TdTB Q0@kAS3x7R&.^}BDB$d2<r2嫁TOl)^00 xv,z7~r4"CE}>ۗx+!B2#GQ>ısnum\*˥4MWVV,+m3U_7?>6p%M(1t]I{iRJƘYD[63OqSJa_g?JHU;z=sk$3Zz9GdkkiDu=>ȱ-wACNBUzPem`~v̆8v$6!NDeho]}X ( EZ#sUX2TT?^lW ^L֨1 QC=|%"JbCX. <*N9p y0w g-}U2j-)2?7YuPV TDjq*##2O?cCB)L2KI-"=dA0Ud҄ˍ^"Y0 hlYE % 7U' JRJ IDAT/nť>sAE1w=wߵ?oee+ٺe˯/pgK{ y[wstt䥗_~~ekm3o_3mg䮝;O9j§?u]w5o{=O੧0C7V?HL$"w__sz}{;Ϯ2g?8s_|kv;ݝw}9o?_o8~c~өco~ۯv j_;zG{쾽{ߛV !#kk4o$z^$)_VlՠLDP h6xKAxо&Nz/ TGXE=d-odT `A@c"J P-Y g」}Mu+ iL &Xe+33&x/Zxy{iF쑐JH!swux9[]-$d@?O}S[w>{wq (9L +W1AP8}msA־" IA1`p iX1b:0Vᛥ _hV1ƀ 0 cnɊNͷN9UMM#@ňb*ܲB= c`€`L8 dδ5)vB *%`ɀ8*JVU^gMB{J4$'@%_8\>JEOk*cb A *ΫaofH.D"q>8|0]FFD]]mcCCl$6s##ϽoTWܵ{=l.`/w7ys=p`dd{K:I)Xlw~r|wޱ~x8$'\sqY U|b[{]v Ƹ:Z{y}ao:XgNg۞|ꙝjlltKvBDQnW^}m'N766>r$H|mx;ިƛ>S?._}>ed23 noFoww…WXd(!'Ou{}{ TL/.*>-rKyjoS:!ŧ2`*Gtʲuc8* GV)" \c%pP5SdƼXqkjߦ#TPO !L:I >`ILy9#Xtd5je&Y] B9CR &.fR+S̊T T7ȒI@RYi:4 !10  a:2с ^R |"ksb7BpɎq{"uV "$`$$qUdz$ #BD#7TFCVJ%0$TkbHSc9%H2"?>ka /,L1 IRZcl0G x{. $9"`Q)+Fp9hp-bNj`!E]]mKSS( (iǛ;'S)rf 0퉍OrcO=q'=}MUUnt{Ho B'o~gpJ3f;vzb">o4GFG۟Jz鴽Tq;xsA%%]_~Tw'*+Б:Z3^z-Ä?nñDrt 0~6=ݭj(Zh{e\={vǂysع~dϯ^u)wJ"/V8T;< aJP[qѭ'e?ժՙ9)t]FXe_79.S5kJ1%D@ 3~J:`M]=[IVH-<ĭVR (ED&TWɵ@(!B:saTprP H B.4\s  pqEܜ28:vʗGqLH8ƄTQw_Hj%< %%%BVLDDb܌k[µIa #M3fd~k|,T%HO$q|;S D+4OBiҹ"b R9z}!U 4'<#(hksO` !yWP"7Zn̺& +-Ib"+P[SHg%0`@. M̛;gI*4 *"ΘYcrsS-˯VUPJEapIp~6simwJic}޼Ն@ 0>13ZH+]´ky- .ת %<&^wN-uvv8 y-O)ne9fʪʊOt?&.RQywŋl=/c֜&3GG\rU]N}Ohƌp$)׭Y} `QkUF ň69b^["bW5@ J~)SBQDrR]c(|[!ΦhC@HȁQD܍1qqK8T@QJ$(BjBX){uN)G@ #<սFh$SQ93]j D}ꨈeԈ%N圗D\8.|h֬ܣY^4M'پcFCC˥txMfdtRʭCtZƆʪO?|x:-B(Zz=/myY&x~y]uu׶OՇnnj<~Sݫ*k8j MӘT Bh5W߮-MVjwo{VGwҢ\9tȊ˼pl]qA?~ /48Jy.ZU? R*gY<!D9}餮v|r2sz]YY̵; p@8d`+ cJ @)8gu͊\U T/ʜ0.Rqrjr1J3Rh%v@0%H jm ,h u X+fcHF09`4=>V4`@)PMT\RZmiw (0&3&7UxZƩSbbX0!!W{j [K2Ή@bY"j]Rr  θXV^'=FsbDCVA!1``+EALU+\.7B]m[RV{ sF1fE 0DU:2 \yS,X='@6sBPڶΘ@0@#cdVj:2'O3bIbB  œeuY"/ɼ74yJ$21G ! &3LKjZR)w`" Di \=20^}Rzҷ,=-^{zmF&cuɲllgf9qUQS:Jݔ /կzfӌ[o9 .W&U}9;>dEE䏿q,Ѫ**jk[f\bW]E[֖H$ !Phɒg7=RչsfSJB_QdЗ_=O5 u*!=}~m(3wT+ZUI%-pSSSE$RYQvt̞ݱnu^dcC3f656L\_<^N%_ZEpkk7䪮\>m&dV̜1͝^ymau7߰>O#E+41pWOsf2,}KĹխdƋ`1A!Bo:58) iA#tEgnB3N8;"pyK! n|bEG`9H34tMrx!-kঢq``ś2l!Z8<9Y!93*5BC08gmkbm.FĆF#oQ"`1 B61f$3.Z#U( |`tbg/qGj$Xsd0]knjD9#)dJ bq̐#n\q(9%`, 029CU5G<"%a4xɃSJ6XEK5Y8p+c`yf7x;eQ67 ?\S1 0Mqn`"d5_WQ(Tkjn{؏MnXTE10挟ncGYB!֮2nP*. ƅsλ ŲYg-edI>k2,_5h"H&L_.:4~Ȇ4Mïwܙˋ>齸驫=9?vO>omo /}ť͋$I3lOwOOgǬґO=SWJOu^7n. 1H$rwp;'8,!@,m  #Z9bW {#O&ya9BTzSAO_[6 `cL |ܼ ]A`[!C@p^7Z-]g|>?;e"?Ș34WY!anr1Fzurir0W2 sk8gR:ǖ(LBڸ VE!g:v\G&`|g:4)A+4M+Bxj2 g^_u]Nl<M /f16 @^EX仱*9|sfbMA2 `q+pVVoּ4DVĶɬN,`]ŋou!f+7mXӝVwnMNBѻ:-"jyXY28S#-DSN=hll-s'1-E{q]׊Pei?2U{{o.>98Xhy+'Oq/Zi$8r1ao䁌0f2MM J&g!+=K W7{Vfz0 KMDvҐR}rLrVeFa9gJ&csHVmk ,L/!!hzo%'{ f9pLHAsj`2 x`.f4@Q/6M3$^]y}s1IW) =6Kj#:433͚@TU7 :TaB-U=j-CɞԉI},'43ːaț`B0ܲ? WjJYP S0!2j tlOtp6LVg&".K([W[%rέ@)iϙІƲ}HZK\ӹclɂ^T#ƐRr_ _xn(92F2odt.HEYހRQBJOZ; @[h5Lj#C㹡6Yg-NL,D RQrKr$4 s[MA%0+A TUVN!9H1 d&Z+1!$j&79d2JeT$hH87ъi2NRL0X"7xUUEa#6B^ d%L[UPYrfUXtR~YIJ E۠xza6Dc: y%La#&'X0E[B$} c,Wޟ$fdƲ |n D1-fYgFYAQ:WƬsx`*> 0,Ɖ(y28BG [WWי#"BpرZUrpppıh&ɒT @(.L)XYd)4 E)-qE$-tc0-oL KdZ*e9f!-qٚh`id!D^A2LJ&'S)2.׫ T*y= !@3}EֱR* R4f:>4^Z*(%It#_Zf-,5T8>p 7X'N;uӎpQ`p)$yrtFU]!qb\mƤ,Kd[$Ql0JM !T΄v 5(k*cLI:b;X9L&WNTI. c+<듓6P˚i{8LL9eM8EM3*|Z0%f1ԩS5}0 !3˙  IDATl&˰SH!I%1{=/i?ۇȪ}捃ƸZ%KɓpK HTP]θf;888\f|[C88\躾{oKGr r2 'D[nt0tvqQ78XW&oݺt$rέFJ  ex (6bbLFp8,˲kcRʄT:beٗtW]u!xxxc<|oUf|>P60tY9zĉRsrppppppl͑%Ijll$[Ҧ]~SNY #GFѮݥAK`dOkjpy !]]]Ş ϿPQYxBvH&V^m5upppxy+ɽTlBhR``4b$IQc _bDc>(֧{y饗z{znU_w|fÿ|wyGkkt*AlܦiYWWww9wܙJZZZnNУ>e4ҥK[{;ozpڪÿ|x߾}w׮a(mEE/tzZUݝŷc?Eo[!R9 ðU$xVUKr&xb|<~8I"gBʙ$LR 3I3L 5ɖ3٪rr&PV,ʙ~GL_08{0r&>Y؞gY9G-$CM>.#._-ۓoTI~]]=ۭJb{0F WUcBl6 l!nمq4M4M@0@RWq3L"0 r~JՐKt߷y7v_X`A<u]e]'&&r ~_L&d"|>zcccc(f2BH(۷o۶m{o4UU26M?[nڴ+_J(򣢲9:6:0rinŞ~JI2>>o+vo_~ٲedr}SN=DQ|<7MS$/b4EQT*ef.^WU n;LEO]]]>?? C7銢x}D"r\.R)]?g!L&sxrĄirߕ_?0f*Yf i0lL cքRބۚ ˙pno9 |MnʙPJl/BP9Bpo&Yr&۟r>h}1*wook! i޽{;t;HR^dj/Z߼~Cx 7tL'&&&vvv8qq>6mn{1d2q{>|߾u!{Uyoo׿XbD|b׮] .,. +*+٬aG|>[Λܼ`ႍOlt,D_\_ b9XT͛g^ܵرc6lhnjnko8s=TT+/RsS3:t={{#Dn͛o]GdR$+(2!$mCc ^OOϼy.<8{앫V~{wQ`ɒ\YY5!BCC{޳~=X:6tsN( <$IzWn^\Yv~]~quu@F%!UUY54<4<<__xᅷzk̙{%Kɤaٟn!400cǎ￿'?K|.#cgt@$NF[pΟ{9uKKK}};eyd@l6744\__ᆱzz  ڢȈ .J400066uUYёle548#Agٳgl| )AQ[Z[n=22B} tttPڵ{ׇV \.B>=c|z%Břm3-]eQ.{iM8뷓(>/ZoE  ݷЍǎF֖VEQ<O~f93P<vl1<׬Bkƚ?u $Q*b|s1\~pppƌiS'~p ׯdrZ L rST_oߪի(>oٵTU圻UwUjΜ9۷oW1 ׉1.Qh\򹕟+Fuppp^:B'No^J2L&u=|> }>P(ܦV^f^oGGOmօzܹ< cӚljn"رc͚5d2 \nhxh||4CeJJ!YZ~hhRz&{޶/m;sO.g^ L%+9y'oP8gϞ=o{LO&SlvrrR/3nRdЍX,f63>aՅDaxxxʕl1&š5k߭KO(-Çᰮ덍^7]uU&LeK/bT{bo8X˿|Gmz]w_F~[}}=cN&0A|~w%[n [_OxQ+*+@BHUU[u#^裏}y 6e˖ZZ[ֆ ^V׮] fu̒e?RuUW:?uo[|W򕧞zjӳ<]wuE~F Qo+u={\Mx"L3:z74\AYrppLYnsO] pF0l||bS᫿2{Dz}޾>եz_m7gU 45.RZ=NBIryny|>_)cl|^BgB-4M@ɲlkzd(k Ø\b"OIiL)is]2 D$~FKТ}kpڮ9L_ B8׿v徣~FGm}>$RUijۇb2*U*)gR]])6Z&컳UUEl\.?44bkRQrlMb&p6 o$d6/ww=>m\G-g;hGI8"WVOe yZhdxx$qDQbcLqJIm}щxFEc\__mk2Mxpλۚ|k1ش /~d35!P*HSɤmB˙ 3MKK%p4 ߿nqLLܿoH6w`9888888\nݺ>رcEwvJ.o2+]_~ґ\.E)ň^i"AW]@OOO<vLť#f8rpppppp2 g9V{`9888\Ye2RCC#J"":$D0ltDQ,g'& IĄ3RZ$L%6 Ir&TF" !T$ؚ@9L&[Τ*bʙD"A[\._$j]jV #^r&>76 gYQm3c&Dȑ#ŷ>.#,YUBxu붾>g$2MV΄q8XQ+d2E%wӔJ5?Ӑen+ aڪVp[JJi9Ӵ73aٚr&ۛ0VevVR){(gd˙--3jjV΄boa7h.e?M˞r&ʙ dєƦ.K[rƍK߲zjcEz4m;VI1V.x+˟nl/S=ڝ!Q$.*\V؈i55η ]wݙ#"9r%#n3LSG6;عki~v=+/1vL_G+m=va豑O? /2;^vᣃp 6}a"X=o|*O455dsYuokL\zWuW_fG[oqFk˖W^ 霝dzf;{ wݱƉz.Uu)ݯm}=Ols/8</͕{.g6Ixd2䪮֬殇r?m7^|]+/[jI}l+[Ywݱ>99~Cv;~b/RƆn>ZUkۢѪvrraŋ^xisϿxYگ[JUk\%\nw~Ɔ  =Ym_r+Λ9osXlĭ\5͝_߾C׭]cNlzʼnĢo\wFppp1nll,nhS﫩ؿǝgWn'Omxɻ"&_=R]u7U]]u=wg=vl_{fO>7gƸ"bā`rsivһy?0@yT*_j>͜6mϭYBX xnZW'1iz_oPss#TQ֭U\7>ko\w)Lp}mo'6t̚U[Ss=wfg0.E=]o}pp{{?a^▗9o1kV&ht"w߻/nn >o|b◿~k?/+>FW^9CJr߅sjjɓ%˒a\}`箷|>_Mu\w˗- -2eWnC} xx8 _f]pLsTBOz. dJ$UuY#!Uu!MoH5WRYI*2g<LL5t,++/k9#HHMM$Ip8 fK"(ܪ+|x". B]ḿ_jÿ~t259::6::nu|s;g_k=3͞=K7wv,s KG(ѣGv}Q7}٬$JVF,KVi*:|zDQ1EYrL֒1ǽ:w5M{}ǛVf=̦ƕ+;3!X1!]gsxBQ`OvnW~|:~d$!С-]˥L%#L0+_9scgdr 8onlV޽<OC}`quLfҙ_Qc L&SWz%R&-1IV}~24IMq;B?>=bђJ^:Xl4rbC9c![KQ\?-b|>w$Ʉ?Gba[ FAI0NmPQ/GGc6)XdҾ e_}љ,lu(ʶ:XTrt4fkBh[ɤ]fYVLL8jsa^4lY7˙躞ϟlS}8mM|LUHZX T$3L$3A_vr lx.c ;zRI@ 9v_Ʌ hmf`g7ulmw &HnyaιgVUVڽ'19֛nX0ޕ\2Ynh{}ǛԺ"WUU[?:;:VXNC" ǽ^DsSW c3g,[zu"x'(o V~n/ҖJ/zIsCs& {zG?ak̟{U]lx\EQ֬^9wN<9W\EvB8X[na9n5W!Aa0DQD̝P_VGeYE +ϿjoN{ ŵ$p(JUe/|2uNևskB(~{j|>$IkV~}LA2e/w$Q\haCCDzo[<c2Rds.s\,sx<{~{>_Ȉ"ˑpk_r"D  *"q׵_1-JSJ++*! _; >B4JiCC}QXu4rnl.'PZ>`#HH.[ ˅iPUm`(, $I  v>x c\TL| SLq$~`9|t =Jk>}u EMG{" ͜aU:rRZ\"bɖ@)Y[[Z Θq:]T1F*U;y<6s;88|$L<~ Q \M_^T~RC)햛nOzG. 3rιm܅9X`kK98M$FK`98888888\JB$MBrpppppppLkp/$wiDlf`ʨJ.-gW0r&@VJӴr&^?sz9Qz3˙skhh*1*0<I~ q. df,Ecy|>_Dk4t]/gB 6'i79/gB5A`mM x<^[sXUԴ%I?{'U}ʝs5I9ʲ-Y#5k>˾ >’ L266rPh,Hɱgst-7N%POsꪻOu+$ 0~h) ϳǍA0)(%D"aDbQDԡ%Fg{8N'(Hy8a1yKFFFFFFfb"bWWȑl#vgr%######310bt;fL@ddddddd&,cd8 x}, 0HaY@z,ߟͯ"I T* ,hv,R)vDа?0Hx߾c׆mmݶc݆MWd8yw?e;o&v됉2222W pg۝Nfq!v{,  AV+px\sgxzyQM:k3jV}xxXP:w.[0I"vc<ٽ`0? {P~֌iYi1EC{bXC}`yܱ*ɰ_7K/|nRWWCQ. o!bwO-4>L% <w9gLa~?l`BtiS&n15`&cS&7t  V6mU+\N'S` U,aWLAhoo9X,EH P 1ۢt:]TTP(D k4d2j50/_l#'O'Yy(~`;t9fp z@GgBj*D*PTx}~ <!q~lZ$ ]8ADQ}w: (,|~GRǿd255Ya#˱K/D+~򉞞_}m{O,yNxnJI8t:9FBnXZ.ZeJdֿ~ck[_~r&T*mNd4a/J>а nE޾ ZL&ǝ}2I9@}HH,E)I&A %aYMRfy$# H1QQ("%Jdԡ飩P({0) (pǥ$4 H$xի6o ܮ};ATV0LtZ N }/j4s._yu+Mg O$ޛ|qm_fnt}.o!Z-h,NK_z:"lZ-/B~0gL؉9gSztׁxBci[tԩo(:6OPģtw N=JNi̮$I#oOjZn|}GVdXv֌'N5xw5ql_DZɍ vUո.N n3O˭mgΜM&6m Bxb.).ڳ@c}?T/^**,""I$T$ɒ"Z?p, MSet"Vo;$!0 (B<5g,avj0,]nm6e8ђV)**T(jZ%1@@x?SFCEy::d&`oWR\ xmmUEQ񂐝!I黖-7w6a8kH4g$!0wͦs/;AeO=nN-[K-3O'xO__Z|O8pY"q Kg2Ba2j4ޓ`FT#Gf0|qneҙ޾I5t:rlF=kFn_Ncc$I|42bWA8$^yrvyJht3O^Z]YQ`Fd:έMT@ͥA"BggF-]LA(lJÀhT( /\8OQTQQ+Gii[kޙ7gG}~ 9g/\n-** Zwq][WIq^KAZ,)S;Y\Th1S4w 8nLﯾƆM,ZXRR|&Zc$9g3g=~t4j}>𰯸ÀYl;w}xۼ9'OmUޓՌ HT(]-K.0`VR\TSS5.Yl2p"l e*<0#G+W(J%ԊDBo0c@4F4Vgc(" S G$Ij{ e @NGJ82q !IH4&dIȄ}T:BSݙLZM3 Y6Liɢ5U%F|<CJj,, Q 8\X,Tjfh)Q*UR:c]Pfeܹ#2p8 v38N K5+A8.-\ #h4L ӉUzb|̧`(_f<'z C]gzbI^YQUjN󟽀\^o0{;OlCJTZ$OݎNkiUp!!ޟPF0"_SEy~Mw*a/RrӪjDPPEDr*B HoȗX6 !TTcF&!;5~ Hdw+ʑd4~OudL` :\3nQafLJD( !]T0+%iF*FJBQV)hx$z)aٌD#%YGD"% HIT*5R!(Ji'42iFJ "%$02 H |x$BJhD$Ѧx<,dZ?K:J8 T0 fP %q CLC(i"X2222222Q;;GvefnZddddddd&&# 0p:̄%xQ!YEddddddd&&LfSZ6 } KFFFFFFfb6CNP Ddb),,,Tss&A`A(JIXy( R㐵KIxG~ED%?(H ;B(uh$@h2iӀaq\JBQ42g H) I >IRR { VGJ|GK1 äqdSH$T*Fz!M("!RpD"y…ڏm&bwOe۶::A y&j5yh:}vnL3[ ==L*%h̹͝ "z@&XhxIP{t:]Q^G!^oj/U$O)IV9R*^&fV9R&O)IV9RȘ;wvMu%rx|-HǚNVYQj6mݶKKQ;f֌i+OiFRT*է㏯) MN$e:aAVݽⶹs^/WXPt8,=pwO>r:7o=C̜13\.~NI˖.^ ;w?wT=3VVT,^ LwtvtvT:rtxv#ۗ_!p䩦z`M n}Ǝή֮oko_3J>mj}]uٓI&?vM&[#Fn [.=[];;8ӟo@kkۇ\,7m֌i{yZ&J=tfׇ{mߙɰ=p%/^zkͻ@pY?pRn!###wM F9C`JDAv<'?[q߸yk04_vƯ>,?Ϝ{Agu|>񓧾8tHDc%EE/lh47oik|~x`0h4g9TSf^ԒH&:ݔm;v1 >`8ykͻj_}??#?E4w$$}ҩ3o;olu8>88L&ݚ//~# Px1\PsI5Ԛ߀(6n[<2}X,/~5g/>{k)J{t&9&ɂ?׿ͯ_}БkY=n׭Aq.{ZӧmؼDo`4M۬{VV*߯78;~ο}׿-).ygϝs_]CCٍbFtIqtrMuO^]w,{+Ϯy 6LMi/4WV'  (G;5L$I ڟge;wYu[Cedd>QUU=r$Jdo-*pM,;`zj:R8@T^2cڔW_shxe˕== .^8h0b6?M%S*crc tZ]IIc?XTx u[~_/]wN}}cmw=v"?u IFdzm sp$Fg͜j-YZZ.k4@0[otZR}'76\!pG\=Dgv̛͐;;δwv~ۼ9jٳ=}%5M9f۱s7_iG.\^d6Xz?ib&(rd,E8g+>e2\d8+a_$q H0 ɤ3 +5"cp8@ 0$I[y(^/j6ߊǍ&c** OPhZ˖|'sG7R(QF ٣"- 0J| g'EPWQQVXXpQMFXBAF&6;~1,x`f2lBx CeY(T*rud2|v]t&srl'Jm+}~kF/664={rzrV߷z%T&4Vp/5.^wG͚9Cь_/I@p*#(8.L̎8lٺ=NOniZ>o޴Y8^Q/]t:~Qt8/^:6anWagɢUtZ@Ȍd̅ ~čuT*#>0ki `rCCgg׋ Z }ᰗ۰jkbsْ{m߹a?xeKmں?)a%%EZgpdׇ{Vu`xͿ7h]{nEQ.2yc; *++ =+ _|jpT*poh{w{ln.Z4ׇ{nNc>d6Bґ'",Zv1 F`V{7nڢR)gN^VZ0K_L&U+uf!o\40Cܷq$Y_7;P5Ͷz劝i-}ې%{.lF͕}Cw_дV{mWV0 ܾyRt(˭"ӧ?%peKΙxт7ZqRSL:b9,@dĬY8Lf C;$zZDvA$JZՏ<Z?(%Q( !0BHIh|CH$$yw(JJ%$))ǣHh8ART*EƏ8!-IKIr?c&%dRceL&唖xd'i- OE,DD2tғZ^WQ8`U09X ˆaATJqF d2Q d2eچah5xB8DL(AOd'EQ<#޾>J֯ۈ\y8 ޘdFARe[!CHU@ӈd215%u0:zĊ.oqv:pp"j5˖,ۼK-7l[tH̜1$Idq$'T7vsyr;;;X3LsZvFkp>ɤP0ūYY\T8>d_hr\,ܽgά;8Kt )--eUW3Mnl9*1)DQhkk90 CJN/P-`t(zX,\.VB0˲{<YOE8}6bf1WX>gL q旿X,vBs,Y-eemNt@{zCS;v{z;A,/*, [.R)YUYIQr$J#4PERՕU_TR2Ax<N]H$jl6{(뫪ڮMp(7ϤReg2N]gqxT*:CX-l[`>coJGE'x_<|X81cpepETAQ T*'*MS4 ji\ڐC&ǁIr|uUe<سws_R*  DŽ4"BŞ 0uSM>/Lv( >Bxd*y9lQV^:cw[KxaAA4;zO>d$vm lVUw׿o:}t]1 3g  \X,s|,{3[8ٔ.k`X~c{G9LnLR^uMorbK(r+qt<8trݮ[>]@-+B;VUVV.^N0# b0R0 Ŝ!Iᴧcý:pfL'I$|wiR3P(588TYQt8Vy-aRZ@_}[dȗ t: 0`X7V, A UTTRN (A õZ-PL&;xDxmZ6|#h^u:]6 ln Â`eEEn!Ӯjiv9:][v<@jD7}WE#N>zr i@0g J@R%c'()WGLn_}J$:V0 ;vd؞ys猼k5IӴBPթ4ˆ[EejАoph? Ax)//ۺ}Gk[{282` =Nw%;8|{r-']%ņh4"%1,c7x<&%1L4=L&Ii'@3HV"ANRgL 0 TNyGKJR"?0)|z=-u4!jI)I0~8Z>gǍW*Uj5´af5( R4M#DDy$i" =zoCGU*ӡT)Ll w7Fv,=1S*K*o[*nG6t"nJntTKdNSs#Apdn:ɯ,#(*R %? t:næ.FcmW/##D}x̄sUQb@Ƹ܂K*Ea(]&ddddn=q!_@ZPj:)Ioo jy[R*UR< =RdERo0@YPP »+Bԡɓ#%),,EFa)S,ڍǣRYL&$y$6Dd$Viq\d2;-ɟ6RQ&j#O~ә"IJ"+L d .X2222222 tQe,T1Y9 aAJ-$3&;;x #KFFFf,M٬'\na!#B9`0hO`ȌETN6PO6V#'F֥R 0nZF X2222cqXv߈߅ /8h47]Yddddddd&&8ݣi`m׍uYˆM[tD^ ՞ޝ\7##s盛/^yDep6tir멦30,&IST zr}f֮PYQnp$(&INӳ}箥dY7ۆ|sܹlɔɍWoJaaCw|;,ZP_O8;8i&q#Fv.^8@wO2) 'b0EfC7DcO=!J+))cAt 3Oh/d2ON˩P(;.h&76̝=SD'ΤץOχh2YMy$Y%DIjViEQJPjD &QRfJĩ(Z%HJH>$$"a baiI"-$t2@4qLJdIDB p%ԡ2LfL잌DHr| H7҄,BEjju [ںmן:wB2XOj:s6 =}gA^ϝw/o߹؉S]oDŽ0Dd8&l `6w̙=h04_j|3T|;wh&f PΊh4n'}P$S?sZZ>WcTW" u7db@8J$bȗfTD"&fω^oj %ZNʵ P5R͞W+͞٪ . F)h^oDJOٳ$ H5{D"!f&U,& +Յ:Ogi,q=k 2R`Vq8R:Jen Xg/ifRx3X(䦠p %<}?{W/X~;qw_DNi|3lYs9S&7n߹{æ-R^םT'?{ v'7 ?z~@]ΎήҒgΘshm;B8Q*XjTWsSwp?Wo2VOソ"UV+=Ϟ9R8|SO<6!ݽ]xч0 4歷`βRjTVV˲֚h,VXcWKJ46l`[4kkd9vwݱ콵wG:>?(fٲX8Nؔon{o-sknKi/Rcv332HF ywvGY%N%;rY>ss>ϣvMVq\^^o~'N͛;ҥyҒ?u jϛAޖmC^JO$^|X㥥ڵ{ߊG;TDD a#[R9$'=,ժnnJeM 2v0lOO/p m\VTXr:d2YuuU8rcWM8WҌ@lP_ڛ+}>ٳe) \4ޛos߲]j >-}O2&p]]=}`F"LsFC t9wz&1 bW^cYN,_voJA8dX6H$*UM Z!g6‘R)sÃa,vtvP8|ȑD< glxLVYsAy A\lk,q85M$F#<=LLz"|+" ˱,FE lGr7Ձp*p:-"@I?jX̦v#gTTX/r._Oߕ$fG޲mO~X,T*[ȜY7 m4E}hLo2O_WqaO<_+ RYYQϽ{'֭W_˗-]xaYi 0VVN u}~3q+yhy,_XJO=P_7GDDn㸁-|`'aB@BH4M$'[zR4r H%R,1 CQB0 !dYqAXo4M@=zSOY+e9sc oI-ק+j5xLР`D$VjT*vv0cMxt6$hd(Dzs;MZ˲^IF4884s;a :0 Tm Bzmd2V+ RN'@2$)1A?EQ7SH8L܎"\&t!Z7WGT*G A ,0BD*˲ #?/'kZ9߂G.^XVDB̙Vݮj4țw1K JnC)̘dp1ҁvء Alrw |K_ڽg_Qaa~^͖KdeF@(+b#sϛo,X5˕+Vww*:KpTPfx<=4- ɳI*e CM WaM{Pu%?ۡ-l$nw INk GIv})LdXle$flV)&1,U4Mg &A#.e{{;%:Avw Wht$J)!362$ A` óIP͜x|bim[!"""2) b]fBLR [Ow .nzEDDDDDDSe3ά"#"""""""2Yd 3nB,EFR p[KDDDDDDe- Cz>^b%""""""rwaZt xf׉ 0ld(Rln1~,fs H4#.r$*ZPqY%rB>D*e&! `aV BpJ0 & ƑD"!)lHbEld2nINX"$E "HV 0cHG271ߑx<[g)d >A81T*`{8Dž<ȽF#-ә×C< mvI bK&ٜq&1mV&CB֠"1 -& 0Le`Pb^3J @VlG`RiIz{; U6[v{L#za[qaIS$Iq;L x ( Ya]2FcVoX^BӴZ=kacd2Hia ԃEQA^91 (91 KPL %$I0 c$""Wlw(Oyh49 SiIG\ήQK;a}s/*!w&H:B2 gX,tP80 c6dRJ5uoٙLS)Lf0$$L& aJv[xCiކBwap,c9N*z>Zj4=\Z8'ITaX&C|x"|Ԩ:#N޳@ |lbm( Gu}';3DC 8vy9K,Z-78V 3o/Ϲ"hQ(4rK՚MFgTMvBDg[8t"$Ϝm2jzQa}'?(kw~OBs.dҾ~0yD"dUY5_;p++ʦhtD[x`}3=vȱs6?/w=u%NM:[a(zT*508rleyyaA'7lz3!ڽwZ0 =~" ;镕gϝurrr^DRiѐ˗_bzFiIЩgD~^^MuXTDDdF ws8EQ(:.E2L$iF&j:E B|V;}~gW7?6[^?uwcYcS(d/oul+`]=tzd8(JL*j4p7Wޜ T*-+-?wNKd2ڛo jCn߹e.<}B8olNAfp IDAT`YE"X \  M^$IRHZΞ=za;w߰ qVv`pըZ;qO2Z8oldeMM /D7rH!T:M4)!U#G>0̡G.]D6o9p\.c5^b(z^ : TJչ(z{Fu}^ض!aġCG_hktIe/_v&ar ^~hȫP(}o˅mwhGLH$D2!˲~?H8O!dY& ~4aY6 EQ x 06(,J5LOd:lV^9zČiK,D׮]|(pf$Ap7gV$SqAl6k,cXFZ0wdԨord%iDlo|O>\F"\lk7\<?vqTCX-yZd3}e.Noݤq\(J:o0d24'n?z\!,/5ǰ܂< g` `*0M K/ . \N0y *0N'IA$ (Rt" EQ+Zjt`0ĺK*(YH"ѨT"hL bq\mvS*Ce(Z-B߸yph(CeCXh4&!I>ECP z rr\^/0{[T_ΩT*?WfN=/7G!~LK끃㌪iwzK A8~I*)NoѤ\.Йt*-rzl> =D{p)-)Їm=j3RiiIѱ'!al|p"ȍ7ul(;-f65Nn)M9OzG^}WK7ނ;Q (q4Ì8.N_߿`Br9җ.'˲hтm;vښp 0Z5Ri$X5++ot:e>9v k4Z[炙CZRgvB&xzhZ $*e |7M IbM{Ah6I0vhrs*eh8GIv`a2&Ǻ,lͶ)&1,&vI `el x4!YxI*xGo bNh4B!8aHdR4f2LF L  $rL$A'2*--|lVK+ 9'O34M3LgWKє8u:#R_W|>C3cS-]L&C;i?{ڹ{>N;y5Zζll(>ttvuvuEήF} 0`@&];S sk5iweNakj?rXT*8n?H0MQ| Q5Z;JKv[2:y?b\4PP9\.gEy^Jxlň3O_qӺ UJUn;'םΚn㸒"_D˨BT:&-lja^ r9TT*?^Q `aB{ bTt}ߡ(i쳫H'%0qfa%jbh,^VZ<DbdR?3 yi JD0\t:NrrsJK0S9nT" "Arr\K,AQlnj e!0łaVYZ d4L݉C==45WWMP4x}D2͝#lPPlf ˠ01 >MQB&ݰp9Z6y<}++:v눛Zj4~@"F$.hnSR" IIe6MFc8P4??C MS̞5gLDxqaA*j4r\&+..VY:X,fîL&c0J)ars܉d2#h̴YBTF O6)H;&J#D"!D"FÂ^$I*zb !hǣd8T‡ 18ajy2]@QTf$2}A4T*)jt*(ht]tEZu&C VT_. #uRfF@Pc`Y6B q\, +$a4dr8p@*W0--d2q8K6E1 !,F#ED0 a&Ndh4H$V0˹e v9GNj<K h4^X ϨXT*rǚB! bkEyٍW%7ODcCD|;IEYi褓)n3Uojnn7w6tN]B!N$DevՒvݜڏȽtD"beE 7@ܳdј{.~]VZ2Uӧ 2fvG"""@Qa+Mgx;qT**tj0Z  jOH4pp8͓>Xսnû^O*TW5р'BOaQjȔwْN'qfwc2F# BZVk8'JIjkO"b6XMu5w_2ARU,{-ȵNs8T|54 ϻ*83M҇#m8珪 N&:NDDDDDDDd|8NPT Nڛ@KDDDdR덂F]rO Z{՚[([aM"HRɒY%At[8~ŲH@6 $pPp8h4|xԕH>S&;StFX_l2lK!,z{Ghtz?M,b%""""""rwaXa^.T m,FcZf(8 V$)E0S o޲,׷y˶HT ,˞m=w %"rgZ3FE> {8⤮eKXDD (w@cCjEH#gZj]XΚD2I\hAyY=Xq{У?qvom#;p3D<77w%|Ѝ6eXNX_7wlZ`JL*(0.e2_vmjԪƆE +E !IV&3@CQqupb"!1, 9E12 ò""q|fc#o/ϱ,@ a8 G C,{N$ZZ~?>c3@X±"g~^._475.uvvVjNjǯwY<7?@* p;8`x4۱kj68ߕE"[VRx7 ==jz_QAGYq޳0 E~TM08Ap 0, ^glEE]ZS]588z݆K/} *8a8cð{eY! 7e8ίaX!>eD϶]TO?LܸENG-Kd?.JPg-VtAn4Jd6ï2 Q|;,ˎs,F!![ L&LPؘ8TjϾ GO?T^V֪5fIRL/|{g57_v oHeҕ﬽|*EQsgzF~>I"ؖm;*+ʮ\W_/,ȿzZ/۰)}Z}ӥ%Ņ Ͼf? }9vFB~b?ʟowή;? ә;w bV*ϴx}>ϝ?jͺD"j~ kj֮ߘH&t~Oy'<<aZn٬VcǗ,Zpu6#q}}okcٵ6ARƾu=rcT*q8nLHȢ`(޵g o}]&ϯ?_Tm`̟w׾a|? BpOs媧lƌko`yYəΏD}}ښ~ 1 s)OmZH{۶s=+n>B(t2VP"(FȭXfiAVv8X_w+׮ -_TPX-Ȍ#'HtYj!ix"qyP~b1I%9n1R%~=TϪ1u53v{P8NI0kfam|7?/NoAB%ziá0ݓ|Wq#]P(եR>}%KWJ AgJVQT*Y3 hAuwK:zzzS;008DDYiCGt&)+)q_l+W&eE$}agm8BUBc4kޮ1 i^֡i@Y0 "[*`qe/_I$r<u:fݮVE> (R5\EQNCJe>ޞ>JQk5Uy^晍^&y!IrxA=GBH$Nm 47n'=thscØr]<ޡ޾U(ʌ. > IeH4:(|ðښ< d2srrd2ْE ?~RC]P>I9nׅ m}}d*zVRΟ;.7qk@Q:d2ƘcMӃT(1 ^Yx{U7(/g?T(V<۫|F3AhOݿ|e~sgΚ̘aY^r EQ :y$ssO=X]m ?gP.>zzr|CDBRBTPwEeێM1oά{Y"H\RtNEG"8D&*r RQ_Wƛ+/} Fṧܲu~@P&/4!"H<ՕMVot8nX&!IEe2ozL&ql2͞{:+&IR6lZ~RoT*ߒ * )IqoaV,^'_vo~{Z³ψX"""իWGu:܃I #h4L嵷i JjDQoGV^k2V=fd29"l0h;B8㸁Q `t\j1;0,?TǘL& ]KDDDDDD㸞-8N"tv %zyclM]˘0,!x3' 1;b-8N!@TNKDDDdOɢD (m9~Kv,acL&J}I&SLFRNR?0JQTFL-{Ra0JRr\?zPH$JB!Z_D(ʏQ^)OPo](&B8(8˲jzJ]]5m6qJdS*'dR9$*e jM zwaM{*EIA6qLI)M2NlqJdc]Mb6](&1,f@E&I (Y& f|2^L&V|kٕVk'r্S*'E`EcmwUWM/++\@K]=,^VBka.LF&EQ42ŋWw<#a78nF1%)*o>|ں)ú qXȃ2g|bwذEQ>fRr:Z BH&jo߱p̙5SCJqZ!S[Boj]"""40 BQ)!IIv,Jx|B8֋'km> (..q{B3{z8EJjr"""S"B4NC;04t5!pm;;^[S-H|`0u5fqͺ Xf*T㸾L&3IK%‚ƺK?AȅBX,P18 ҕ0 z+Oعܴʊ{ͺ CC&QVTO,^)jzfcN֖Nv>0L,?qT 9e m7r9gTip$8WϨڵ{;#EDD(Ft:b2Hynd4h57j!I?IGgg}]m] EN;Lf0˖.(w LmVR10. X-%8vΪimki޺m'BxB0 l钞޾yB pl^t8ج˗F*_-s2:\&G Eaf05J%X<2lJtZE% L&/\xs ?qJVI$B1poΙ|ŋ p㊋{*BϿuێή.+WߵZ-/G֬ݼu۲{̝;;|g{[Gɀj4A(R)?' 9At*Lhh4 Ҵ\**bL&@8a|:#}3?;hd`tҒ!o,7ᄋ gc9m'Lizw$Aj_5}Z"{wOy,ĩӟ><veY(FN(c'RTYiɐ^|Ṓ"ewŶ˗/ #d2fw`~~^nnNEy˗AQo2==xð>BٻV|*$:O.<N'Oϝ=K*z<'O.-.IWw+QA0 0eY*,'N>߷T./~ r,KE ܵd4V?8<O,I Xo?ڬVf DDDD8wd Mg jd2eS% D"F ɇ äiA2aej &yppah fe˗m[`h1wᛱZ,r͕Lg1BQS=pCT*Me2iIIBNVy\:Mͨo߁vJ5rpqͺ @Dhu7=p}*R"!gTmݶSTW}S5j$:ifTX0ڽo? Htժ|`͑H ^`0M80 ues*.*ݾs閳Hteo|GQNo`͝utKWw7a~/?OpRR٬{IC99VX̛l+-)./+t2Ee J36of---o,|SßFdMuպ ^{-L^YQ^RRTY^ꝵAкOr'FV8lW%JeV;l\pJ6T*4UGBMDMaحJU ~4JJx2ykt:NnIP uKleoU!U xhf21<$I@XVX,JәH$̲l&C[Dhidh4$)pU.L OLNL%Hj.gqqZ^8lM&ݪ֨˗kZ0{G ˹l=fIT~/~RiZvR-{ދBpiqQaAVA'?w"NO(*,I3fL T*;{yZZ[_->m6g{Tʇsh4D{T*^oRɤZ~bV*=arU9"gH")"%E#dY'w>dEfsD" lޝ9tOrg ᳨oUMuUo=l6q-h BR fEJ%S)y>Ad~s2t"x<&3-eev\*YӧPˆ F GF#c1Lqf?`9eEcd%V]Jy'd%fUprgsjjL6Ʒ^oZ'H kz)+ XWjF64(X06leg~9 (; ++DBBn~9aO$ȚxuD"Vΐ] 6lfܤs 76{$I{?ZwIeZna[\ծ^|[%!uӆ [[[[~i+V]3;L͛>vWy"rft @fS}>ve6fY6ӿ<6!jZ~];0 5lg[޹-va__w.=) |Ѷbu˯/޵c՟oqq8:<Gx.@09fFM/ ju.G 4 j |2"aV@Nzd6F1)j&$b2OyO3BEQ@d&(J@Ӗ|iLNz湁 R CJj4-Mzn;}뭂ʫ4??{ol uWxW ðL@UWyǎk--7l87OSoabYVݰonnqǭǽ\.QRwp7!|ukݵĩ.A:#_yڞkTPP,)F`p8dL,"f`Q4]ڬjJzÓS~U+ B7nX򫯿޿WX1<2|mm]eÆuEXU,_}͚|4vӍpgO8:OP(|׎۶n9}~P:LӭYg5֮A2hjECnO$D.GܮIò|;<),iq+ݻK X\"++R A55W @ IDATg4/b jkɧ8;R-{]x_3кkN?c5j5ʊ`( WZ|{#G[Z{n~c* f֯[ A:q܉ҩ_t:00ܻowjo{x/?;LS +)'ju*Q5~SRd}V ,] ݴ0`]}.+AQB%2PN('f: (zӍvwzz:Q  C N@`(|AbOdL#vΎ'L&HDe%Olҏ\.e%.WlB!e%nJ&i* /8gfͩs;==%+Zz#yb~9l2Ӣ(Zi4-GSScz*/'d7phz)+ dMZtd%tذG啕D"BAfaO$r ĆM$bLBT*!e HNW%HRU "t/,SZR5d9e+ h`CERaYEQ  BkjTW؟vFu˖榦3}?ۿjCQTE^8w λ‘͛6t%֛ Jyynk$=qkOe.)tö֖fÑM%pՊ+/> br@UsD]3 bh  ÌOLF6jin w  v5Oyq趂WEQr~7\`XnVt:ݹdɇjz PJvE Z-7m@dӆXAAA(drv k>JL&SaZ0X_Wo nQ4 *++u&Ixr4M5o3J%H$T8MMj*'"˲ddW?˿47u.]HJg2gDM9acO,sMš#ǎ_WǪU&E@6h/히k ( (h #W oU+c[T5gǓe65϶uU*e%ݛLf`c3^vz DsScUGm<;4{',I &p8R[[m4TcckOAf_\DgTf߫U>ʊL6rUJj4 --ͿukU$L^}ѱq|KfNt8WyWGe|+qL_s [5J@QpPu87ݰ֛o&^O.o6WW[KQ2Μ;݈okK:.OϞ>.$nz [sG~U,Nǎ;o_޹J*l6c鰗@*N۴^=C%Gk ՇzoWPDj jgҲiq855vn(t8aO'0޳W/8/U K%uVUUuIT: *}ԭF fAڷG?AKs p>W^/64<‹/JW sDڹtɎ;n[ | x6[zZ 7x[VJ%SGy-e6)V|.>XT0gDjJoq} fw`hdsI~ɧzOb`)((\>x <_4c`}kVVT,| 6v47_ /a^ M M W;' KX6cZ6.JshX8$)(((((|AfXJ\RL LJ?QyAV+8a(?a(\ ezS#c b&rEQ$!q, B gF7x{j$t:L,A I媒3lfzz4jؖPMOOv 8. Jc[.=VSR<'+ &3<|㥱=&U "ϹA0GŜ e9ĩ94Ffs|4AJz{N"hzkyB!yE1 CEQð  "R,<_. ]7 aV邬b$PiKAAAAsȑW^ye׮]/|ȑ^x~޽Oꭷ|wAL&fh}}=M(TVV|h4VEjllwuTx<ZHkkK&QT% vttɔͦ~b:i0ffd1Z w:x4jꮮ6ejjPȊ"jv;|i|| 8AN}d,z2>>eƩ|>+@i;!f3xy||Nz\Wds}Rh&ommf*jBR>ߔ4z.TmGGG,3 z^SRN:i} c?cffn皛zm08H r:##C^;::&&&na.<55g87g'"JP[[[,!.T-v:ݧZMl6Kjr:}\直ZM]]'M&cIi ^jIK$bЌN:prI6-6$p8$UkC@2#I媈`0H099vzj}`pF6˵H0 MS$uwr:,K?cÇ_}w}饗9߾ګ?˷~ʕf>٩%.$(bX b`)(((((((,2(9X󮻾`tA/%b&O *JQr  ' P8Dk1az:gzY,LPU.?mbQ Hee7KәRBTqG{O[ zzpp}KּyI8zd:FDRUW{}H4wrL_׭9^)3}Df6`(|Tdh\K;?)8}3F1ϋXJ}MBQ:Pddc joVXwfOD-f|P( !Tdu< ?eHReT*0bn;Z3AL&s  ٳ cc|> FFF||^a D)˲RP)d< 1eiZ6(O066Hs d1K|~DLMx^c,[$2I f&&)rcc (?qH2E1LGGG$qSHSOMM"@$Cfsl?IOLLL&/ɜxA+Njynxx( e9`6+AE&i:Kj*r||瞻vTUy>`||rehSlie_W\ 'dFP(~λw~fKRソðjo,?qkҎ[o'OvtYZV{.Y("I)[QdY繡ߧVk{zz%ɲeq8~`p,lynll011RiN<H&MMM8NJ>4 sSSR*Z:&w=Vz{{(<L@$CC֭qpTWH p(HR%I/_<} 4MbX, 2Lz{Ocsp1.$J3 `pp5kp{IÇjT$(Zb}Gvvv(rIjT$aYPLL:q\OO7`}NV{0Eccjkky3 s33ATWWeǬVke-e#iqT²7Z,6E{{{ &{E2~1$o~cG[+0˾gV}EѝڼiCmm ikmEqCBЃ_z/ wvkoe{]m͞>y͛6=vbY陙`;U,?q둇ho9=>?Nw.x\]M8م z{'A#Ln7lБcO?N[M\Ѓ=_~o~t*?8 HG{[3G9}m +M!NcD"CGUVV|W\~U/\ <{L6ڣSz>΁ep8|A<gYrg9H\P`\n081 Y`maM2+y"9j/lvH*+Kj-VGGGnkۖ͵5kW4 :E=|FQthx帆z=יd2yKp1 kiuYbel13it۶^__WaZF=2:V:f]"٧0?5;x( 䱛m}ͷ_n^Mf%kV4MFi-n]C!ͪHRP J?+_nܶA Ui4VK"R[ҼtIl#:NRU8gW8١d2sN3gr\$bK;bLߒ͛6v\cpdٲ&ᣗqMDQfD"͟CdŊEٳ$K]J!DQ pU*NZB ښꓧ?y/ۿCgzz~6a%XfQO?x0qb1T3MHtǥ("(:675] +Is/h2 z=A%{><+Dd2Q4J dR~ܵk>\:P(W$C#+uήM$!"("ePExKsc.駞 66WT8?!j=UetIDATe9IU)󁙙'|BBcC%9EQi^VkO 1F$EQX`1 3xvy:0*+*ho9ñ!@Sc#q*R?v0A7{U(2 w~ ?dl<~ ml6JMLNz߻w/`zz;E0엏'eKxGFFD[k˾۴AVeٷޙDAXtIYUVq;2:6_[W[s_FDΩ6=YA}˕l6iOu>ӟ놆dzuqT׉]E!bL&;ZG3T>ro_ju(].ɘCPcC0JGc1i2dt쉧}K.hgf:vÞJStmM5qӁ@"$v#(xAVP #*AFF(HfV8¡߾7H WL&44#r&dZ&*Bp(AvP(FQA$@0 PT^x)RkqR]REʗ8.+dI)L| yt EeT(&i *iMd""iP qBD4XBzlF#S ph9,p?1?$ی([o|iG_{j%mHA㸷~|;xs5q>֣<?22rh4VVVj4|099Q,R:N] d0,nfE1b i`0 :#zveYϷ@ӫ&BµikkcYd24_4$I666 JyT$--- bnw477_\X,+VXreG[(,KGG;IK.Z-jzٲew1g^U+[ZK!].|aQNiZZyd2UTT0 V׮]RHlmmq8,655VUUaV___SSòX8;VCQtESRuu5q|iAZ4(MMM6|Jj`֪*eY@X|(Fqɒ%vmv-rM7uvv80ͫWpJq7nذ<|@̳ORPPP\1pMsstl,]bKA`iz Idztil. 0, tҜPEE  ìZ&ݕN|>bM$144Z*h4~̙3d2קR6l`X+E*) jt9SNEQa[[[Zr˙3g%q~4 $ɍ7Ad*(]u (E|>GQjNOǝΊvWWWF{{{5 7`4/҉0|ٲe* \|ZN3PA`mmmMM\|>|u]a:*@%Vm lQΡVoF[\0 ;v8njjWJH$DQriE7h4:FBJs'xB@QDZV/z8A-E[[[ӧ}>霘y^^CdQ, aSzat:aTJP(z* JѨd\DX ;n22 ˲x<:M&S.it:eZH(m}ET*á& f9 Jp'f:5|6-F1 -*iip Y+Ϲ+A`0xwkO\K۽f( +@6~AEHh4;ٌZZ~+Ξ=[%%0 <&!y8q x X9SO<)bCCҥK  s97`/ FAiX ä0I$w:}866699qf[~=Bg] Hl޾}; @ J(!x0 ðRE ;'I@"( n[Bɤ(BA>pq,|"EQAp8icK`JQK8tqj0 HŤ X"=_ lPrTs*AE)R  8N4,3A<ϫT*A, HVmPAXDD@8hETDZ`F2r 5[!4mmmLfjje9Ef (E,VkP4J8TDQ,1AKx--ǎŢ h& #&9˩A8CD81Ų__ES3GNg^yZrʖ.I/jJbf:E1AjKfq `$&nwLMMrُ(R<3 DfRpI(e~}:1L0LqHò_@$wZ (b*rSTXd- %;a8N`NT,&I( D"aiY"z)scp$w8HҔ!2B fs6B2B8ERy ˝"$F=OLLArelnkzڟͦ)v{r.b)i"(񸫫~BH*V+MF3Qc,s"f3R2Fi"@dYy}ZV!BQ"D|^Z+TRX,~ 8vWrGT*AՁOpjr811jժ;d34pef'/",˦R).EQssh\EQլd᥈:Z.9e׮*݈d-bHQRB2EQ$.e^8 \4MB!B!xIL!B!BlHL!B!BlHL!B!BlHL!B!BlHL!B!BlvB!jryLO;!B!x.8;-R$΄_… Wڡ!B!s!**.P4,,>)"FIfB!B`2'hwzT:wɁr=oͺ 2?IX# 4yX]n=CXB!B!d3֭[tҝ\'C>-`XP*S\iXl۾_ l>>ЄB!B8{F^a}cIj׫P*\ğƏ%h(~3ߍ#yXc_^FjΜ9K!O;sbEILLN2uO )@R;tfh0K/cN7SbjH.z~JT*U]y*q?m8hhEvB{\\zPz5{YS8g_^π7Rb c(kpc(V0s97^-ddbb"fޣnZwʎ4PTlڲ3glJxb?]<==r-==)giL&aE0~bl|(lV+i3f~&,+ukHa}ĻC3*fϘʴٸq3)x{y/WB!Brrїߎ VB|^y˖Z^|pnݺe?f?a(-`oӶu+V._k{qX,e:|F)i59r3d< 0OM9cǎbv?a2ཡ( [񓧘5} k̞;~|Tt40uO9zM0c$~]/>"EB8 !B!I=VV-[мY6oن)9Mql66myt:*/RřR%Kp322r;ϏP(iFl۾3K97nSJE@?ujbҲm;} a-Zu7Ҵq#‹P(X<5WcMu?zuQ*zoo߳`BV-uWR"^_uj”L\\|o%$&k888hQW\Sl7m] @RLRr*uұ=..8::Ҫl߱~^zz:o9Z-z50%'K$B!BŸ{ҸQ|Zf .]̚1X,Ν@JJ#Iv/11lشxL&gϝ۹[leX,xswwŅO? rI f̞Kd,`pc@F.ߟԇm]v_ƴ3/XMbb;!B!YIgȾZYziZԯ?VSX8C '*:ʕ*}z1콏"(05ɵμ׶M+N>ͤuҦuK͚OitzV?<_M Я/ÿA|-ӏ2X=<<L<'NFRZ0_} kEK=ⳏjŤ)MRSS /ZƌTFhBۇ)&У[W|{(Fww7:whOhBƐ7#F J,ANY?,55u7QfuŠNdgB!B+)o%؞vB!ѣԮY#} ,^ݺdΔ̎y3ZM)]ܝ JPP FTr9"# xyy!B nZܼɞCqV.DĢ%+SSvR.ԮY#ǹB!i۱k7ʖͲ]z !s MPZU8BRhX.\Ddd$aEBQ*t>URZ5P(lܼ51 "NNx!B!7BHHH$''cXy3! __^ұlܾDRLz??_ o'qƍR [9{JJJJB!B<gBHKKg}$&&bpwszq޳ 6\]\U:~TR?VYݍŊ=.ggg6nȞ}پck5B!Bo !ĿđG]C!B!+;v\ٲYPM!B!B!!3!B!B!!3!B!B!!3!B!B!!3!B!B!!3!B!B!!3!B!B!!3!霈~a!B!s#2* .}[ 'BdZ9wɦB!B<u:BCCQ*/{g'Lۛ;>Dpe>}͞V}̑ !B!BiއAJ%ZZhNV-hִ 'S1Ybc/[!B!Bg_'@*/GxѰVZ~:V+Gc-̘:_.>HЇS!B!BL$ΦΘEmhڢ ~%00 uVERV \ )PEPP4,zuj?mZ{P(hP.HΜ9KPx{yѾKu}7lkNxzzVؾVc0Dq FAѷO/؉hѦUKr8#ѳB!B!Ȑ$__ yAΧTvl6\a]4x^V2&NfǮԪQ+Wy6ʕ)(Z9ЧWY\KKKFY̏XDp\YWZʖ)Kd ´iL~[;իVÝ)gZ!&-=@<< '%55+wK13)B!B<4*3khqdݗU5$ZoJ+86L9&ΦΘYslxyzBzt9?{'2yt=Ŋө+:u:e) >CFK;F pb礤ЬeL6[En]X Vb|~&ѾsWZGjVF}2z3}+R?BABb"ߌE|$\t!+wxc754x&B!x3g^4i!xΜ9{J=?r>SY\Oe"VB&-k)Şo~.O@@wIS2s'Zon=TP+xZ,Bęg/tYxG D-YJeJ,6[eB!B!&_C5k8w#G+UOO;,!B!BH^҉Wtza1wA!B!?Gj !B!B I !B!B )B!B!+o32Zhq&B!B!+fe㵔G*Gg Z$#jVdb_0x,ٱX,lڲKXR˹q32+_!*˟3o ٻǏ?tYu!B!Ifa=P {\qpj5^ԪQVd69|\dLFDqr܎Ϝ̙$޾R[Oe{TUTF~/_! !_睿pHWa)*_EzĶyi0Х[/֯^JіYv#꩜/Bh9yjv8B<%(沉fu`M3E\@z^ZZ*lZʄ.LZz:QQWdzf~Fl@ڵqwwd4q ~_6-[d=p/^zժnˬ]ƍs7yP(OPP`Gyh,߉D|Q*oGƜ@pc뱕%އA ... -L)U5m M4lPQL)|;R>B}6c&._AZ}Z*U(OH}ϜA&40^ OfBcCÜᝐ\]}KIIbP0 * '~ğcFJI}={Vj׬R ܼy.]@UB .6=x6ԩU+Kl#\JD^y-$Ý:j՜8ʕ%%%'OѴI#|}j.UdҼI #9v9?b!(0jU8dҭkgJ%))"PT)Y%;s'N$hYA޳)?֬E6e㤤V(Y$%t`qujTFP` .^L&uTPޞڱkܸy˗Rf5 *bcٳw/<=QC˩Yص w77 hcQ;8PlHOOg߁\z RIҥ3ٱk:3˗#)Ț(S4N =5 ^Oڵpt̸A݌4o~&#^G:s}n<]V+KZkޱkܼy8T.x[zӡ};YJ.~  /Jl\dS| {vb1x{y핷DjeğX{,? !ϳ۷o_7h԰F'NۈEKRz5{LVS^yN4*Vu$)) ɔ t:>~ruuۛ;vRR,oڲ OOwڽt.U ?_\|…p5Wjʤ(X%bh˭I=ivB!\~ iA`Oݫ`:}>f֭[{--۶s!W[+0Lz nnr b8xϜۋw?n&V+/6kBzzm^^4m_[@l63m,o܄bnZ#ی7'Nb6[)̗}QLi2E:#F|& / IDATiTV @ΰewP=<#  }ڳ`bF%Kax !OKBB"iiiP.ZVC:w;5Ӥ9|(f5k0_t:ϜeXbK~؉|t֓?'Ns̛= {CGnV%KaFf7  $bV]GRWG*8y*;v#̘57F𢙦0̚3Mr;)bE4˛CTT )]GG-JҥKa2-.P0=W]{K<_l6qq̝^mͬ9~7Li٢1cvf.1 ̙I|-~6#z?k|||Xx?Eݷ߾/2?~u)j`ĒL7忭3y C?<ŬSX8oW]cy,u ,^0K"ؿ>b8...D̛yymk{S͠xx8.]HYݍ ̟=yii3PTn8Ə!b,ߠ (:}ӧtͷC@Gǎo^h4t:}bێ,V;仍Bgzt]ظi %|9jgg]!|l9>e@"rQ"- wf 9_S,&C||}۽׵kiѼY3g(Fcpw j;2(̾}z:H}$gy[ hZN8Zn׮]#6.o/o.^D­ =%;M{zzamo7}qqq=KR35b,_رk7T(W b_JeV3gyO ф)99KexʾwPH<<˓hSv3͡-%xxO5qfN7An'jJ Bzٻ%,XxQ*U@Xq#NNRHXxlAek2TB(J||hڤ!^㼼bٯaX b Bݿ ggjT3!!!$ٺ}&c2.4jFC 7jȁCu+R 5Y ;~T\;v+WK ʱf:MxzyRnzP{_CRQl|'Cprt~:;wNpPP&ϭ]ʕ)jec˛ _Ȕp-\N~:t:,C?^⿩VL66ѰA} (N^t܂xs`ӉXuίD4y ^덓( eDУZDGǰsnf͙GWgN6kژZ/\@ݍ@\]]III!@ .SF>(*"##)y E,AX̙AΝGq^_!x]v .N_Os)ڶnIP` Kd ´ijr)J,A\\<[m'<(0w:pPi.J?.VKFlWTy~!#Ώ'ÄxzxкU 7<*FѰ0CZZƪ}ʶ/?&LW{`X[l!tN-C5ݾPF#ntО…8w}iiih4|}>&JWQmZ~jbEygtWkZ^##ƌtn^_!x^bZ)η_B)o%<$OXV6mJrr2M4QdxwҥcRx׮狃n%nU^*cǮ]tT,_G U!x9{.ӗB89{J=wCZ^Xggfq RI/$q&2 <B!?Aj !B!B I !B!B I=&'Ld~Ģ^nTt4;Jjjc/[!B!B왘̝'0%'E֭hZV]GM7kGGcӓ>B!B!DΞzy6\rBCѕWY8S,h?RZ~:HB!B!"gO=q6a4oք{o+^s\\s6ǎDZ뽉4)P7gO7kz1>r0 +¡G?vEBCشy+Cě .>ޢRԩ]+Ƕ0͌~,&Hh(H6%0qT׫Kڐ7|g ^W{Ù?{:._a-̘: Nǵ7pwwSB'Jz:~߮P$B!Bx$J'Cǎo^h4t:}bێFqm۴BFPР~=._߸q'OѻGwT*ԩ];vqڅ4 ! ֭Hƍ/BbԨ^6ُT*|zʔ.%T*uDEEN@?Al۾ӧϠj-\(O'~P.5UEFVY)9@\]\T۷kktއC??|_WTt4EfKLtw=18}?BFh o L24>z=Cb80k)]:rl?rubߖh$8(ǎf^B.XH&ӫyn !sڣ$?3 K4k~̩NV)V$ӹKƲ_Wp54 K=)]d߯a̚;P(pppۋzuj3h@?4:תEsr=VFL'J/i{eqp}ğYrT*=wǫ]u+ǎSNν)׮`i;pIѼIczvj̚;&Mat!7oFN/ΜSz=oޛF _u+?Oe?IKKi $FAos|}aTZ%׆ʲ_WmNƍAZz:#Fa֌)1Z،RS1{.^1H! ykʕd21g^֮#!!ww7tlOдe[Ⳕ9z7 >(C0ml8%w 2HN<}_Eͱl tjOgۋw~GG\6/|R6nBU=96l6,еuj`μlܶ5y<'s؉~?JZ^Oyo>?f1s<~6_D;X,ΏUkIHLϏ3b}"-8霨S&y Fm=FSXl}{nT<<<(R6Qoh^^&FFi0P(xm!{yzR;+?wc+$***6C!!?puukU&<ɸ#Q*mݒ6Z~&1ZjTFl7u^^/cFe[_!|/3?b=P !G]cn>s}O?~VC8bbcɋ9#[f&̪xoʕ)Crr26nb!L7eO߯Nt U:cKyOqz.DX~齽{{z>h@?bpwǫ_lشN~≳z֏1yp~䩿wc淕$~[aE7_&iҨ!#oƍd];'ON(JS6s}=ry? sz9 v߃/ >}_KmZq_ <??ʔ*Izz:JJ,?gggl6[[L0/w`ػoSj ׆Jbb"L+WYxf cٯ+xylsr ҘD|}XWC֭ 5qX9w1ŊQ\VX鱼r* Brx# :2GTFR1j nǏϾLҌ;Νod -!ySfwQQ\o~Xzo*MQ" ػIEch{KQc""b^v?VF݅$&9sؙ;wfyBsdSէDqcREKV׵sOKO'.>Z5k4対~Mn߽GZZ~r# UDEu|ЀS8 ȐmHNNfYeDF`҅kqL0ʕ*ʰQcٲmʹz΄)3>f'= IDAT>z~ZE|UWM˾l޺W^ɋxTVNffiڴv"VO%K:PbV]OZZ27ʹ%[NLl,R===99 BKKJ+ JTʽ{y2͛6ٳ !''\Ÿ<%##X^J0֖icgŪuكݺ`kc.nHZz:K3,^] ;ABB"[`8hܭ_tjϊkTKW>n>rU@nxJ<|iNC> HRLŤi3J?xi+H(W։M<[ѩ{o<ҹ{_$jzЦ7{k7eע}ح͜۹QQ/ K~Wuf;OǮ,[!qmgNl߹[3s\~۴ACGږN{s XG{_ڴ_ay+˕e8)1j`\rW ekլAJj wIR]hkk#h-pQܱ ѤqC piՒrex豰]"Цv/EWON~H$ԨV^?a1j%35v^xu/''G~n 炜͜OǮy O_Dܦd~o߹KiޞCI#L5fTQvAk >ׯu|:v%싳k[ N?wOr,4L<<HZ5qmޘammE>=[j-? !< S7RSS)_,DB[7߽#6.c~_|8 *Hd˶tn/8s<}@[o?<}:)X6v8h4kXի}}}U[,;~Bi0m2M칰]J M8#G{_<|>kRxqkϾ>{N90ttt};Ys/݋rssabv |:vsC ^~]شOAnʏ;;wvO#"2|4}`ȱ N=G; ʳ/ޗ{8Yԋ4h̞}6Gvht.xCF{{3+_~CǮ=r:>ѱ7::q͓Vn,] i30e,>,Na2Pap~ 3s\6nh扗_Pr%ZlWҽ7rdlݶCU n߅_W\=p s-`~l1uO̙W/<|ٵgAGй;.7Q!SR=9ק gN#3ݛh2YCU܌ڵj;p<|#4b>_]qִIc̚70x~]7aOF0sdѥG/}7 ssF 5}*)toL6X k@oP< ˷3W_*/sKf:s[l|B^½թMH?S'g}F!6.}ږH$ԫ[R%⬃w{x-TĦ[܃)(v;FQNm!lTiGG5zLGcc(^TO["a <{Ɋ%  Ѱ i܆\T (Uy&,Zŋa:xTWvrдMynayhkk`oK<%lLx vqnm;ק'8:\Y'"#Z~޾MZYv@.eG&I~=b?O/̻YƔ*U õu+|}}}ʗ/9P5,[FaJjתʕ*ghټsq5ΟCNd߽]eז- .pĄXYZpnjN~>] wM7" )(>A!d@ij-JȐ[eWAFF̞1 F0|;x< ,X)(۴&9VV8f$JulێK+%S+X1k^~aݺmk7l$337W:w ? ~$_ʕ-sƘMmeZZZTN8*Ih j֨N* un٢pM6iԐʕ*ry%ZM0ꁎ:::Ճ)g1p9h!/`O]:}% < :r gT>4_WQz5N:wzIOqx8Fk<|ĚKʒ];x LVv6ǎQ 0d8K :'s$ }ӇllJ0]E#g""""",'$`Q PT*%11 +J5 ssUO@"!RXDT[ =|GQG7m܈% !ɈceL6ysf˦-r%rrrWTI~[lMlǠWJ*Q i?~;w2';GR |5b#|B@ Nnt<ƍp}͙Eqny}GCJ@#RHHPfff,?ױ`2 sKR޿dd-di݊6p(^IGWI\ k3O?xJo)QVZ)A{k8"" nS Oϝt鏷39ЀɂX1ET ԔTLM/Ѧ&H$RӄԴT$ NNehX.'Na἟ڋO`Q-cALo`UGrP ihiiT*h{vw$&%s^~7]7canNZZ\bh!¹SkMƍ[8s=I?aL&&>DCD~,ŋY|D~ #?ŋYV91qcb)Lֆk~~|ڷ@FF'OLJQ=v^?yJͨV /_g4V^qq񘚘(( #@iL|B& ֕8H'OWh|:TؼEǬb?.^)]ř&~V<٪4B9ԮU6[U\|UP-?LWYt3~fLԭşTJ߁89,-)Dh ScTN-nڼ{k7WW]3xA[[mrs]([@y` G1kk6]Av!8hiiQDq|;Acm˟ ;qK`eiH:w#qr*O3S- bRK+K|,$uYVVhkksh.٤$O v;*l Rٽw?1xUYu}i֩ ,Ծ4Tx+UBmd*g4[[֭Z [(Y[[Qje6YoM,ammEa@u>5PH$*Y'O#'O"(XJzořV0sڇ؊o6r,}{TꥭMjUiܒ((ζMzu,Lo߹˜ ذv%+װ~7K o6+J*IllB"'O#RRSTT&Hh֝#G`oOM13S$ ]:ѹ/e19߂JXխTN)x!>]tl,݋uk++%ׯ:::Jtq3/ '2)SSgt4/O [O7j+8y M5@OO͛r^|o/-ʊT3%oRRR댥%) Jɓ\Z1c֡~>q}/++Kk.9&0F++\cQ2J:p,---J:χ> \&_=ƙȗ!/@Q2l@n{#;Nb/] !T*ʒ>=3mlN=ǻbbbټu;A ?ldܾs[x|ɏ 7m!::\ ,,135elܴnpuH ;/dROHѱVd2 eDG A---ppڅCsRW^ _u n* s s< ''Tc)Ve+IMME&>oӧ[gp=FHƍTŹjIijl߹_1CrrsKsT*#77W+JƔ~/OsR)J y4۫k&H6'DF TT===2228z,T׬Ic<ĩd2""z\ܲ9O>P`8p3>j|eԻ's]d2 I!>ҥK)`歼}L||܎6RR&8IqBɏ?K~2~{ںi餥͓P>}Nh W`U}[1W|B&&u*2 SfZz:l{;+F:ٰqYYYܻ![ӧ(Sk*4]۷sj%[DKK+OZZ=u,ظ8=@jgXϚGb>ř~Y~7m¢?qnTqH|xק'~O`P0cF C֖5~%2TPSbĘ?a9affg;]C d?YTTkʗ+ˈ4m&n7pud2J V2ؘz0{4zvi3Խ7fft'Q}ԙIHH@OW5m^JjItL ${Ċ vTv2߅SpޟD<#ioxux͕cG`l-]_WrRl0n ğG:֬0_[ K;6ܻXp֬g՚҂KbZlC >;y s) i֤1kV,{V5g>qq/W% )iJq8,ۻ˕eؐ,X0dzxb̛3e+0}TRzp}n+KKV.[Ēe+Yxڔ/_fᣉeѨa}kèCi/y}ZhYy4jЀ . VztvDM[6B Ӡ~=[L:'SFu6oW9g3|p׻'-[4c^Y(k>ʖ)جIcBjۥ%^: I KeRޥCUYl`M1y8f͙k[o,>t5k|pp2}3G5 llJCF T9z8&LA-ԔS'abb̻w+Vؔ(3TGW+?ϟGҭ7Cbn+URŊvAfV͛6f蠁f0֯Y!,ʥ Yx,Z|VF psuϷcanWӾ&xdJ[6bfq?xM屳J:أKiRXX#Jrp/ ---͞ q5}}xYΛ/їq82 TJ`P0~Qvy$8 IDAT(j޾FŬXb53fLNhTWp]Zyd$CF&33 6ZSy224d_p knXbL7ʕ*2'066YBlt.\\==b6ZIo5rOˑ2z6OiPXƼ:ziEDDDD>s<0Ձ7gy8C3lE?0KbEoR|yfLn"1) =رwlp1e&6&#FlLFHh(2e昛w 8DB׮$" bS89<{࣡qD3U$ZZ)]|ڷkJ[:%s>J:+gsDDDDDDDDDDDDDDDUgRڱ4jk(OKK+Dy@iP.qeJѼi$ Oktuuy @GG5l \rhѬ CS"#HLJsG_t = sZ4k`w?Ndddr}<.TV4^F;ww@{8[w4DOOWɕ3=# .&&mmmjTJ|={bldD֭8"ii FK <=<;ddfM*UZ 7yd2^zIFpiD%-- c#cԩ-(Μ;^(5mS'pE140r OMU'ܾ},Y[ҴƘX._Jbbz:4jЀ8"#x nܠAz.Я:T7!YDVR)ǎ)3m6W]'@:X,͹~6NiҸqq\tw012~zؿN+nܼEZZ*ZZZTTիWzDB Mr=?!+;=]}6pN֮^6S016qBzdBDDDDDDDDDDDDDKUgX[[sY׫ SXZl ^6%& 0g#)ThҸ '##AȥW)] U+W"77WPJ>47 Jq,ɋS&oߠ2NWҥRLFRR|ĩ\~ƍ* ;ybVt܉Դ4`fn=/^ʵ븺8SX1߽ wWWnA!J9s=beiIff&YJ}թ]l26Mt4\Žukx&'i&Xr]v5jФQ#JNc[mZ&++аB`{^5܌k7nriڷkKzFǎQnʗ+K{UҥJJurRُ:T5\j"DM4eֹ QQJs烶ѴqCʔ.Mԋ8y00ĄM`aaNbBA89ؘ7n=O%|rsC.^L|ADܻ \y/ 1>V&D4?kADDDDDD?HDDWUiiiʝ{  ԄZ5kaogKrr2񸻹~z֨xb{,klbL yB%J6Y̌ϱ)Q k+kC22312TА,yL Ք3@&%l^--$TZSg()ޥG$ &&.U(|BM---LLLxjT}}}ӧ+[+k+lm(Yҁ'O#'W(nCJTBbB"mݐH$SJe.]ςB~|UTΝ%&&Tzoid1u먝_MeU&T(_C@K 6n,(,F4@_tH31|T30'-#]M #-'](X332</l͕QANbE'Ң};wn߽ϡAؔQz)RSSRfblLXq1=-]I OQ4^,BئCFf&iyJP+P<6d5q^~ @UT/--IoٹgM+%3SnO ! DFzb'Js./^B&[@vɗD["ȄȧgyH$ʕuDT4YYdgg%Y*r8iN9ؘu.--*WHJy4B޵B&J;[[Ν@VVEq, -YټF)Q#= 4  hiOe)A)U==}թM5|*Μ[o``GJj0vroMNNp~23T+"յghhH*SR٘XRS_lGndSuNVڴVhA=444Xqk!^~b8`i%WڳWدoOzz:wRY<}PȄȧ?TArr2D)%&.ŋann%W^#;;\ޒCVV6%˭RRRy}Ƒ 2i#GRXXz 䐜gʢr܂Z*>{/"--;wSխ;w%+;;RQo333BTATڑ'OC&JVV]]]R&&V^F[[KKKrrr5: Sx&/^t XZXpm2YY{Py/W@nn.qKt m2O @__ u)Pj"9TK"10RĄrrrHKGWO^ϚL)Gn߹KVVR'#-s33rq%dBDDDDDDDDDDDDDDSYNvW]];R)fԯ[{;;\Z9sev@Whh(R\yx'hiGM\>w/-ΌYЀ-2YW%Q3q)Qח]PbopIM۟7nZ& 0CFM#2 cq_=M}~MDD)j̜=56w<|q4C;d){<|4{&ޗ§cW.] |O' 00ks䪰4Y~YI}.S(m6ignn.*DDDD :%ZA._ahޞnGCPev=ҳoZ7#ȝ>AS|:HLJb%xusz1}lBR[N>F<+Wٵ-m_OA>j :cР3V5snr \ljShA\|BSg0oba Ш my3x(n̹R5g֛ }5v^yr츰/0(.=h~"7?#nj +;~^,^TZdZpvmKp׶Љ7DͳQ2at|:vUw.ן_W^RvF_Ͽ/GFFyv+vSJcFbHWp1nи|^p_8"I~гo2Bn`=V=_977Mҹ{<}*mݶMOPsvjr&Ǥݾ.Z~i:dkϾho?xH:'Uju|uٙs4uiS&ϵ7eRݳ;NMr:eQԪQtgQ\5k4͚~1۷vʗc)U$qxĊefV9:x XӯwnST>ʕu@~3RFmpo wqU.3XO1_7qKưݳ;o&s^ }J߰*+ 嵵ՎG&b:.\ 5!? `ˢ^jUc^[166.r.$''|ZCna~FGFo6rrr>zӹo|,~[ٳc =786Kpg,XgYqx8̠z*J޾MIS2a[̸ S8VtґEVhJuY[S%{dTWѿZW<* ;[/Qttt-&8rMըHKKaz ƕDpgWu2*ϞGiVMu}cKbgk#9z e5A#l ^䗫蘿/MI!3ߍ:O{̚>s EJƫNɈ`""""Q>lŪuكݺ`kc.nHZz:K3,^] ;ABB"[`8h9vvݓ^YZpCZz(uƎ,e+򥹋;Q\JL>If"J:wW-p*/JIBtՏVn-Ϝ͜=0|8Zyׅf;OǮEU||cOƵ7:}n@r XDx'g#5:D>pkӚ={ItL,5W+/---F"*[A:r/c[~}zaeiԖ}z 55>T嵗Wxʔu0{b?n턗SҪesWt +KKϾt֋fm܃Bƣ/KB]9|(=~G+7Oz;wSt+ ;v%''i3ӱ+n>|?h0GɌ4~i~b@Ԫ&8$o kk+)UkaHp>Jre5|v6H$zqqzyWN=Wv/WCC~Пϒ 6qH:(3g)S6 ###Ξ ?^\xowMd~Xĩ3-_q=o<Br]ٱkR 9};YsHMUmMòM;o~L]KyqkϾ>AGm;ʷtpkiA [?ca,\/_ӱ+~ݨP뭠k:1G6m`xx {qqȒ񰓔+DR%=V(ovJLnۯ+3ob ۹3xh^~ȯEV DGp۴ͧ7Wӱ5Z|&:!y-33i3g3f0rrr+s.K3o?1g\=eמ}C{2zD (0h^xuaӖ? 09}nZgμ2{oh&vniL(ʕcRWcWwIKWc:֢kϾ8e?yqqۯ+B|~ 4~,+UD"PZUJti3La2OFT(ӈZ(+ZhFnn.na!TX#o.z:&ԭS[kkn޺@є5k7p-~[L4^!g%1kd$ ԫS[й PNʵ,^S'v4#2e,-۶}o9ri&0ps 6߷'OƦGa ~c+_y&vf/J̜=~{Rr%sq`bjڿn;q^Ʀ k 9O :r P򑇃=e˖x+ҩ/kx=uQ*'ɈOxb8:>۷ r Aty-WHeR;8 J9wUk3e8N bqxVFu6YApte% uŲEv4yBa܇|Ԉz'uk޾f-ؽ---9Lb¥4j\Y'areyy.{e+5mJ˻7nakcC== IDATdS]:y8Y[cll$̻k#W;<Ŋ+Ub.@̯W;N>+hd2C^}vv6~?M'?l\O檭rrrx4{vppNYzʲ6n;޾Cv<2^q|1#C8epM4?՟~akhWF cR}P dq\z]tssshP޿24C*NuV-[9 w û};'Nf˶,Y8cGQfuK|,˱#R~BTgN+\ߡ ZjRwOUwmurbo]q_)zqUTbvA^KŹJ7n+$;}ncSZU TJ: g,Z0oH=ű/o&3tX| ge bϾBcQILJbI~ d=-䪠~\:RŔ?1xB(QB1&Zsٰf%G_tG {~G?%%%2ի}'[acbbٹgkozx2)Ha 8LwaUe_~4ҡXc+*vw:3cw먃b cbwp:<>ܳϮ> 'Κ5D`toBVݏ,d$&&NGd`>*Y===k +V̛=]_'UGbEՖw$[PREW] :.i҈Ue Ayy-naȠhkkSD \5!9ʳg/>7$ZZ. ˗!# &/n+oc KK tttգ+R+r{i^>d2)mZՙ;oK/O^\v9Sl埸4{zUK@OWW޻ JOEFEs.$ſcԭSBxٜ >+WSjMZ5եq\bťo)ܱ=66hii܍g_rEK'hYX7rj> \vJq;,s!XP\D=A܊"FEHKK#-- ]]]fNĚu7q m۴wՕ򌎎a"?&D"(扴4Q\1*B\||cSiiJ"Eϱ)[Qa]2-ȩ Stڔ(^{y`^=!ztSPp& (}{CC:%L}rGSTX to~d 0#M2ذ~=n޺MVVCoRd \kC,Wx>-gH$\tְoTT% {>GKo/ʕ-D"Sa$ (-[D"{΄ݽGdd%K Mi!0d2ۺڶ{vlϑQQܾFJ8wfNcu\ C.c2Cn-NƧ^jdEek[^H$\֦saddDzuxfW>_HD"Pd zv\1F$ZL?33S9r{{EJcJȕ6i*K];unZ*k ڵιa}*V(/l6kBJJR;Mdgg3e,w턽])2*YeV]cҒeNxD$v&5+JDDâ>:&Fi/JŘhעcH$t]%&6ŕIx ڹ~_̢s e=q"*㎄G`KAmѢXiܭ X›o{>U+W.tL M[N Ri?A qy_,^HVVLٻWwZHzz:}5vxRZU(C_fm\ YYYdee)ܻ:)M7" '1\ͺ 2xA6Zxy`jjʦYx)o+%'%cbb 1L%$#HŦ̹~,::~zӰܒM'HIIU-9%C|Ǧ&̔,S-Scbc4SmCRr-H$s9nwAԬYQdKuqq*ۣ>{PT*-|DE~OF^soUM2耉1wq|RVM,c@}V}# !I1.b#thF7uhTX\dd$gχpxa6'$`cc- ֦o+>µ(εhkkSjۦZرYn*oٶ3?g׮d(7j' {ԔT-͘CVV6q*zP<٩1 S} Ȉ uDBRxpɓgەCor[6eRއ3dpzu猪D$ {;;<}*C||MMۗVhOqtNJJ._Ownw:}_E W4t`ei}"mxd$*YZX0gTN"|()#"4s(/>s&ԍ72z&C.ִ UFDdϜXѢ*)JUrYJ[:j)p+J>,waޱlii愄DJ~\VUiʯn( 6~&&L0VA>E,!1'O}&:`fێ] |&Cp?Ϟ?عO}(EBo-cb˶?HB\av݋ÂykyW]g?/>į" (w#k*ԕMlߵ=&S·\Wعg/;Wꘒرk7;vP=N͛<\*{R)7nv'~?O=p@ ;vp@W&z&{cNu!""GkWP~q0[dמ}DFEHs̙!vy ,--ս+$&%;8t$mqd2<::UWpwe-G S̜[m@f*"}{͛4GFdd<7Gqd2Wr>-RO>#++ T]iT"e$''#x0͘=u8˕\Aq^(W ~VJzz:>aæ-DGǐŖm;10ЧV-eEMa(|䦭o+?/pqvRwff&¿#uvv¿ϡokV^CTt4ݰg'A1w;~2*݆=\9ĝHR޾}GbR1ٕBt&'%% ),jcbTzzzq)G KTA{҂[(@+/6nJ||1l޶<ДHƏ߷pi@~@ƐO5#%5T2335ʻyO 3Xf "߱&.x 'O!++CPH3rUMMLh"eٕA)Mff&nA*GeUARR2lM{u+oVNv'redjjay9rAK_4v qS;v+l;%9TxM~2ذ~=N:ܰ!7oCLHL %|*fo#x&Gv( OwnMp$_D&qy?y"׺0d2 [}'屸׫CbRC.ҧ'[473#*:XR'n<7jD\\<;C*r}΅\3:lߴֳuUѴ o߽'qR)o޾eOfti$NpɫWw +P-#W’_[?wݠk4F%FZzV'ӻc&̳/:c`)\]5Ϝ;MQɩ`Mw^r-gΞ÷uKZor-M&!1Qp#(jcCe10ރ\|Pn"##\"eq9q*q79{m``_2329~4 dN FWGJ+s6~D}0t=:>s 3: qbbbIHǹaCT;e ovNѾqp]<]144߷t2[hӪzz$$&bb)4Y^\X8o67na՚u$%%cffo#OL=#A9'Zxy0l`lmmYz-/_&== ˱b"4Ac"m$SSSSZx]x a0~VУ3\TP)W Çضq߽Z d2;Xl\#HHLĄmZQbխï?cPdI͞G|ZxryZÝ1~e,c)u&[*Dq[ƎkΔ~4vHC#4vqV'Ϟ|!^.-ƅԵY#"?y0;ݻtbit3SS:wh'a D"'ngМ?8ss3|Zxq$H:zA#'AzU:wl@n]xm;uE"ѦgsmނEԫ[]2iLFjl޺7o2m\͚+ߧgw VիU_^ >TQ) Y0ot/3fcoWEg )IIܽ{i̧ rE#F3vץR)VɳRd Ə2mXm;v#=# aʴ3)9 s33zAe8toV4tK"}a̩Bޅ3fӱ[/LM[BP\sPu-/dAL:޹@8t8?0T|z^_u?bdhȜS8x5UH1WvHwk&x -5 O7ZXoP.s{ hҸժV!##C8)UOO*+oOacia4J+ݕ=zShQ*tYƳ!~3Ŭ\kkƎ)|kcanλwy5M6V(+wgM88sQ%,w,c?0CʊgL>eiLbRR4_-5kT,*׬Q]8hj+M^k9{W𰐦Z*T)=6U232aKJoe~II?HϳϹr]:v$:&Nң[ឰDDFL=O횵(n܍oN(nKŋv8r׭S::tTAq=xp,+gܽ>9f=GG[ArիU:ZYZ3J׭GR%ǩAJ*^^rSGգ V+W12DD{:`_Ձ׬ȟ6|1Y^>z槡ChַP %Y淐*ybb7Gێعe3vʵk-2kNПGڔc}+,[%Om搛W8KMIEWOOAaS(yuza\RSSVJ ۷Ȥ2HII&>.^$PiظYǝˏH$]TԴ4gPPRN2.X"hKxS|<}7oߦ\ٲԩU(moGP䗑cH$*W÷QQ >K} ",fGJ]TϑoTJϾ+no_93ܜ?pűk^V,kWNrPDD!SG~g_8304 +3,!`nBNR\TS222HPR$)m`@rra+<10'!!A06Vx{zj,OS$'+OJJ8R*8kc||dff*Y},Ѐ7Ր]FܺFЋZyVNm.9Bۋ}{}L0P?G>5$ 7c6tlZ[Xsh][6|*(q( 瑑 :N_8351ܜnSVM23pLFdT6֤KqJ*>7n`anƲe찲"99==]RR135=zD>}}cb)L&îti񓧔)2qq ʴ/]7x9$%'v#?Mu433‚kC[6$0He2PV/Ww`mm>wpd:ɖJ151@_cco+""""""""""""bɢ]%̝_~8@&.\|]`hdHqtuqj؀h`/?}$.6V!;~4,-hִqRvvv$r|)ɩ)BnT\s!!<+U@W/zj\ēOiPxyqu\ZZXZ֬ yW]&x6wk\r}*V(/\ 5ѵYS._ʞ}TP:kQL^~{)@z5dR) ^~\ IDATww[fgpRRSƆվPDDDDDDDDDDDDDDDKU3m5z$"""_@"""""""""""""A\3/WqtlkWADDDDDDDDDDDDDDDD?hq&"""""""""""""""""Qq&"""""""""""""""""Qq&"""""""""""""""""Qq&"""""""""""""""""Qq&"""""""""""""""""Qq&"""""""""""""""""]ϧsBz?Tř{Ϳ5 8K5R$==&ܻ5z޽{O=dgg['--ה/EWxc?ذiɫc`~u4i3ϵǷΟ6ժ->mxm,D]>R9#S/Y|)|w@o#A 2kWr9rk仭2 *DDDDsnZhK\v?gK{ѻ ;zг@cSnݺ=*ٺ}'#v5苋'= =.]BϾM޸6kR>ɏ5kЫg/R?=?7/_ynXK=*Y@/>O}W)p=ו`Ma UϹ){PJsT0 ZP@svvv67oc^xҫ*өiܛV>>C>R6]%mEd2ҹ{oyѶc7VJNeDřϵ0 xcř[sp.%ٺ}n<~8[b57mf87[7v WnݾS:4n䌉'!?95rH0gOhlw QWT\ ]]]hN>ù}dݵy6wk)_,g(tCp[֯A&)geXq3իWZ*L2q< sgMc2<}o>/]W<ĶMv; >!J*wVҭK'F@zz'3,[S :S&2c\#x1GDG''H||~ >~N1zDqjPkkvݯN+U.(kVVԫS[7--/RMH>}iOūW-u[UB ##'Nһ@o%%%uؑu4?֍JCZlܬz(wY)<`W9xTJjH@Q4nqE(||*\ATw͗|u2ƝL2'9m)moQq`7nV{}ɲl۱_Q֫/Cou̘5ߪ'gKWnt bŰ07ݵ~ɪ5IIMe="e̩3IJeNfND.Q-}zvM,] [5qR(s3A;KBFf&ժ-. #RT~T)?yRiӱk zzz/Wӧ`og kg`<[ҵg_>+VP@ AN۾3/]&++f۾3>|0޼}[~؉Ss-'NƷ}gX.R+UN4nMמ}z@yKè1po6رkpϴsXq3? -е'7nt^-ܻ󢥥5-}x{K?ہ[w>EҘ9g>>lۑ;wӥG_-r9eY4j!XU;N}hC^8rQ(QcMܽx豐[yhyЪ]' Ҧ2c<<|| dCt[{E__U*܍CHRg] X8{7rF__k++׭ ʕ*ҵSLh؅Tb4>My]iʖqDWO\bL6`Av.vx4wCWW022ſhΗ{UYnƄƌLhݚCo Mے=n;c'Mn۾3;wݎttt?xm;˔HNVmiGbR"CW+:w ق,_]pa#\O Kx-;+4@ػ_]πܳOY#cmBIfժ-2 pQoD>g)[ƑҥK)]{/vZx#(~6oAvqlȫO:HێplПGV H@[[mmm#<k~Usg1z,o߽Ƿ}gy[Vn 3ϼS̛=e%7wVvl۱:Պy/fb &M pjW޻ciӱ+>=N<q^hծlg f]]{plɬ Dמ}i!SnۥF@Y?UԵI]Yy5;s}NM PrϯnCמxlKǮos4uo?SFu`Ѝ ?f*V@"PjJ*gυ4d[8 MţǏޮ4ڷ[uI\v+R>Y'`;gȠ@ݻ8;5ȕ^8*4}5DFE1y8Oflۼ_y::)E牉gNQ֤q#u#nߡsvx{ypiLL]ҽnڻBi5V5o߽WZ}1),;DB%SŐ*pwcv&6._Fax4w;;:{RS淑yxy3a(tuu0y7na1j;=&.>~+Խ7n؉ں"EkͨSfy+w̄TPG><~gl߹U\2,]^Ǜ7ow ͨRR1=kw%J0mxx1 b>IV/طs+x O)Zd-]}z1vHttzE~Xh*VPF(_\Ŗ;XwW•>74n+Wd``"eQw}ҢR JhQ rerS&ҧ 4".> bǖ Jd2}(]g54_bEyV ᗑcԱ;vc6 .w,ǵijT/(gQlxIƍW6zҥJ{EA܃PHӺ7 "99"E 8v$cGPHشn˕#6.fee3IFf&cMbՌ=B)m~MAN%-ȥW=v"{wnX1dYq ׯq*LMMؾs~)2r:vmۨto=FrY{5?ϣ9!֯K۫\r 3s3޻O*z=zu}"?Qiur nɟ+bnn]O fuJgΝg\e8v$Lxu,_ɚKc?7i*׮"1)l6HO!Pv-Gpk###R73J(0bP+riy+Wd歜:}=}=:wh/P?ܸ7B?Yl1;sw7OFrˌm<c\=?p^=T斏2QGs޾{QȈN6Ξ !33=;00'6.c3yXݚqC~y $WysJU4f3*+aDž mQ[zcia=b͑}Yrh ̢s},;9qsmZ90..^SjeW|SܰkW` t҉ְiG :zL)cRʜ=w.#uԤPvK\UhGs.^?y4 "%]{Y.#"""v֬,-I$,,IHHP}oBVV*YYY"HLL*t?~}(Uzzz  +V̛=]p1_'-WZY{G{A(b6SHAK,2elf]c+lش}}Zrvͽ0\MA]ZZZt҉`ᑑxy6ҟGUEZZ:EmqvPDq%h^JvX2XRE4e)[ƑSrW֦Dp,)YB}ܭ^=!ztt+}Aƛ&4n$(ԧR \T{c KK tttգ+R+}O΃ ߠ'hYP|DS6_nЛ,Yg'PW*[x>=D",_e~ :LJ)Q9t9Zz{Ql$ ^|w H$tҙd -`2ÁAnm)+/Ȩ(n rJػy3z:~__[Rn-NƧ'eH$R-F /$ .Nhkӹc{022~:NK^8"%KDB%٭BLO-ƏL?r{{EJGTɕ:9R&Mekԭ] #CCumAZFK#'޾S\vhF;X< YRRRi";;)3fѽk'kOonq tԁ 5m삶D¬iCCC@gȠ;W^wtj޼y5C NZ>ú/7h؅!c'))cc~a0)t5f<=҂A^,ƚ}{rwH5b#ɲpZEKo/*V()SƑRJr|>-@q\O?[׫׮ϵGPb%E|WZCȅKܸ77nfȠ4mƒXj ϟ?А6[ҵK'8zaawqgm4m_OJ_FtL҇T*%66KJ5s33cT؏A"`nnFDd&xqV=GTV]]]ywcia@ddϞ=ǫUAׂhmdS􃅎*4S)]V/g-۸>0{>_"?<]{ eef0UTGG"Eutt> 8wJ;p7Ȉ'OqS"6&TW"QQ 7VVhkk+koYAemm Pۏ}yYYrlmsdmeU]ܤd"ffJVNFikazJyصSgXdBWٓArU)[QsP睰8KR\9Μ;ϰ_F߱dR-D[[[ɽ̌yڙ\M ZSSS6mKЯ7m}[)mCRD?ivzD͚5-j qqJ,xD2>LAˑi---l%&&V=t155IF=}8z$#+Ч011N=ΞsCjתɂ~ כoaYP@ \*/B*b\D1~#thhvΏJE bHΞÎ OHZxikkַZ |KP] mS7v(HYyQ7dffQ4wk͚4qt8p9vo cM: y+J#:&GSdIy:**b)ZԆĤ[Urs媰2'Ul6nƥEVVZq-YjW^0y: " _Uq/xҵFNdgK {-=B..GX6.>qa6m¥W#>>Q#r1ժVsssormZ_:޽w:VO 揅qtL6M(MCozzή4km`̹X)fB$lSYx6wӂTp0+Vv3ԵS11L6FJ&y LP0YV!v.m۴]NHHq6t0\y=c'NHX^YYlݸ}}'Ws#(G;AK:-WZ5ILLzMVt=9HR>R,RkV.+fֶ8v@91u17 0x`7\txn1$O1=ua՟넀,`ͪeTjѺS҂h!Crr҇R,5o߽dIٹb(ZjKK "#:%&%(vR##sK"22Rp+y~G{!ͺ q#>!A)F[ >{KD5WswW6o([QsPS6i,ӬIcΞ3040$""R锭n1tۗ¥=yTHJJH]?Ԁ{є*URaMKW;N>úUejjOC0x`/Yĩ3siXxd$Jʸ7MUGD kd6ʒsL&}xϏV-YIMIŹkk~۠~=Bo|ގN9GEP"T*% (sfLU},])ghii+s(_\ϑʼ*4͡u˶9ǒ ˗tK!.LM~ " _U3>^7܌Ĝ.I"0HKTѺx/lm Rʊk߫zj5 1>>g4rj(,VխCrtYٵSx6w---:o7<|1E hhJ{"xyNM\rҥq]M) IDATm---ʗ+G. i W(n[ whܱ=G帿hI=n5_:*N`nɮ="!133!C/옙#@WL>.DDD$Б ~,-n[L;a'.7߸yp S̔m`Zv%''sNCLl,ɭm6q>DVV<|TձcbTzzzq)ZakfnƓRPdff!˫ly.ZXSd 5橩\;T\E~HNNF&{짒Ɩ;122M)Oi)rڌ["uݼm͑cܻL롄\$lLە&Mw| _Pܚ5a$&% ʍ]ix)ȴ5kddddp}8% {]{1e8M߲z̹Ο.)|r-o߾C*͙yU?춫{,k|N荛TXNqhnoZMٺAxŴlNFzϐš@bbpv|MOOw8( UcCtR-gjbBcg-ή:LnݾT*EOOrejSq6222HJJf)4QB.2{ʥ7?aæ-DGǐŖm;10ЧVéA}RRRذi+>ޞ*?߆qitw+ ٸy+N ֚L>K4Xs5 ^5kk6o޲y<ҡ@<=ܕb驛ݚ6H1a{G1rO`ڲrZ^zMzz:ʗcŒET"T36.¸F2e,055څװl= ==+ʥ iʕ-a?0~4m\wzLr¥h⊶6ŊىiS&`a#‚e~ Y췌Mre}L%S{+,c!iő=mO OΞ;W˶xz3fԯ BU(QV;>>If5W_*[+׬#660w4.3,udK(n# 1ttuX^4RsXwQQ\]K M)b/]c QcKL4FcbbXbb4KT{^F:RXYX%d~xy7wfw}]0fD APtGF!30řɟ}lMxըAϾU* c:˖v Ǧ߷m~ч,YPɞӧ iY:[3) JAAJv,?GT*K@nȖ֗Xmڄ)S1PgfGܳ.0t;K8%%H13#FS7{BB?gF>۴nItL Ǝ'';mVcק Ǝb9cdhDͩ]R)*PVM-^[maە4J{fAvm~g0*3[*9cbls=o!?X&N#%=≉}HV-*<ƹJ۷-k++.36VճT=eΌi,ZΠZUZ eUa=lP UEtAžˢ x-}zi}x ϵ;'yS(Sǻ6]:{ Ξ@2lXx׉$qOR`$Z0̚OvlڷkCvmYHĭw#~[33S>>pa/͕א8]wܸw?Ӱ"C7)w>ٹÈs5ɟl4Ю =w6}b?[ aIcl;͈)ɸ4nPK=!ٻO#XÏ1Ie;zxzuB5=X(={Kg J죈9 ҝx5ꗗf8+ʽ1j !{U]e,]<_Ί""'999l׉=;#?K ##»4WuoYsenQDDDDhxڵisw7DUpoחI])j[|q9wm6EJJS,Xw V ӵsGvX|' oFTT4B5#WXK "xiiiTvq Z;Z&wkx9jzyi[6}eרɜ'+;vйAݺ0p@?F /uwa7_afV]B‚1GҶ\<^^5ϧp>++++Vd T`)ؾ-NOڶi}>c6 zuYdQ pssemZˏ+Wq=:wJiܨ!#? !iSo GP7>n1~Qe2[pY|6~ӻg, ;;[N̊5{^^5ܗ۷3!bH͆sfNg,^k++w򱈈5jPЄ%%%aeiɕkpsV*I2[[k:"HdeeajbJÆ RЄY@JJ*7%&!TjjTN'c <8AOOB:u14qU9J*м`(oqqq+>MuWJf'OщGq/jcWJU##&8:8%$aE*Fu:nnnl۾A+06j-Rh.]E֖122,6.*K]]iڄL#G!W(غ};&&&tؑLΝ@ǨT*oTZBص4n&;w޽zuQGJ{OQ1Tp/&DZ'o^8y ϞuhcIA~>G+49uݣkdžک#&&&?y{yc#T!NY:&ԿU_8}I""""""""""""nIjyy :nngd)LJV 11DDFY**!J<$ =zĥ+WСriaaAtL4))ameU>|U=(H(Po&+P)JY 0Eyܜ.~aׯY6(vMguSre(鸸TD[ Je"1;+SgpФVS~%$sm 1{kBL[ ZMnn.47516&7'WNY=Z7KݻhyGG)<}Kvw# w77:vܾs}}b9?v"Cd2uk{C]1 Wo_Yb%T-|`Plmyy4_>%we>a_Sӫ_Vْ쇟Pde aieYf];r|yS߻o? $BDU0{ݣ{3c_K>$%% WdUZe:߶/yk^u){ SgS X&v %=#GX 7xs;ϾASR@تUT?F}+m^UJہ鼭 `/|j?afaȠ*גtM܆߷nÝ=Щp9*+eedd4.e]Rz222EQtؑKpڵm) 59 dC [N|F(~+k:eh$C*Aadd\rs3g?0F%R[y+뚁q*󎷕%-ÑǸv& *ą ===TGd3S<*D022E@NŎERqa4n,|?yJwF)(hgrD YYdeg :N]irt7KՕZ5ٷJ+SxQﲓ0yTF!1g"SR8~Dϥdo~>d%;;јq,]4zuK_eѼ BT8}֯^}s:,W$&%]Čཡry֬KE3w"fN:zRɪPɞO'N8}Od3[1>Ud2 /ֆ36RfL/&M$55jU=uϠxB”ֿB^u"e궝mjzi-}r >;//?&&ڵjb`Pl^ "J*-iҘ!LDŽفfY׼e<ϣcXnNYI5]ҾG.XLLC,Ncطh٢f4~Ӿ]R˼HI:Í/Qݓ[0q%oI:&SYzbb" ׫ Q"uoؐ;wr52r,W" mpJ&L.Wn<'9%Z\@T ӔLnܼ@RRjkcZBUD*UDDFVɔ˹y6tYtεBRhV9#"HMI .>rij^FߞR5{2) A 133#??X9SSSLLLs.nUU*Orr*NN:724$%Us*TJXrf\?"3$;'\j5(yTxh tkanN%{._BAAyyy\rw7nRPP2/n#]YrtJR |2Kx VV4m҈'NT4}hߪ¹ dُ ПoVVkӚ?f8v$ /%w0%5573}Z''G @n]Y\3Y}.-ujs|߅Cyy,Xz&a#FrTT|LT*wI ]dTdƷ_Ͽ4npMd>cuF^of}O^ocnCNJHL3uhB+8LEOOѽW0:vc̸nH~ص'} ±؇hؕqm~2oح':ugڌ( hݡ ן S ۰qL*uoݾCtL, ݺvƳZUp08EFF'O!K]/vxVʸ1prt@*ҩc223IJxZ4;[[|4"""}x :t "w0JDd$'NQ\ v{`:U^<ؘɟ}BMHRxצJe""2L\5*j4+LVV]I]T]\~[_>can^fmZŋlݾjLMi狩)ȳ9v$YlLMi߾j```@ =CJDFEqQO@ ^7ahhH`;scdhWZlԨ/]f= TZ90'G|4SdgcfaN-hѷ”v^L\%O"+;}==WGwȘwYsR=MŠ֩gic*?C'!K!(yɨ]k++llmpwwe]Ե35dCcaaNRmZ Bƍ66ǎPřF ꬫ49KGe26ٳ:u ,޼s-\{z x FDd$m|]X)((3koܷ;ss3ݒҮm~"FyaJ®^cՏ˩doǣbPd1wtR).:CKB"С][o$O?bgkCzz (.>Ąι{>;n&'' ~Β?2ix|Yl/[wcU$̢KVɤ/XvrWݻvښ߶lcغ Z_~MuY`r9>USR;;yyyL>i30ohҨ!.]wmn߹9] ??}}}.^Ls79{ޛQ14mX8ngk "ڶb[>!\A `/Z̝ɀ ڵi ˯'}?Kc+\~]{׫#JsimΘUꘁƀӡ]%77؇+ӽkg ooͦ/ siܕk8o`jjJlCy.]w >0Gqq h U*Сd6c73&MOm9q4'} ͇+bffUkWl\R(tYh ZZZ29{B>J*˸/EDTvGɾlظ/ޑ98tkW+_ oKQÝٶ}'m[k_~`%lO|>e*k~^A\NJE}UƇÇѤѳy{Bhۺe7n].1v§ls331VPӓǏٙǜ پeV]eU|#"ٹu3ʼ<>|0 FZ^YC!3ܧNͻM:]@'OnF~Xw77BBZ֡W_8}>Ls?6[ Qcc._g Gy^ǎ$//[6addHLl2_MDsƌffUQ=|է.V˩Ә'ԮU"!kIǧl۾?,ȐMdȰ,3yc_͜C6*!q:1]K":&Zw|S^<}{ܜ!622e@]Ӌ:&백f@`i{TqBWh[ꨳgxVZjܙ]%mml?O\dyIxS5_ΊkR5+ڗmѽ[rƧibz3H0cffGvŌ7F R ӰȵzJfh̉:޵˝[PA(fft@gSb%gIӰ̧&iiixx묻^EǻN\/deƦ1Tֆ#Zfd;F%Yq/}|snϼR,+4mO:+JvZ+ʼ<>dGqqv>+83l0y:d xo`;&MC%֯Y5?|4 O-sq<_͛1zǽXt1X[// k'v D&!x0\oS;9z9"W®ҰA}®]qcJhwUNNE6251qf|ǎC?`OJ$ k 4?s,'ꜬvFtڹDjק @zz鏓#ݺtϷ)vdddlOdWITV̡#GWkء__Lh_Z\{ѽU899^L.8|X1prr~o{{{Ο$zœw u)]^ 'zuĜy hH$BBeܗ""o"q=} J9}VdŲEZ)>B^8;?f,qv(McnfFlCɉ߷ngػo(O[ENg;o4qMz)Eĩeν$R S&&Z =pw77!VM/:oKJ+{{ |QGˀ['OגOӫC~~>>o6[2餤;IAA_OId¿#Z&=#|+IIxEo Xf8g,99s33r&$$bfj9Jqz:>Mzyyy;~h񓧹y6VVV4W!οiee)Ed)R### z1XJJ*#njcػiZMmБ̝]+|L&c׶ؽc&T;U"`ogD"҂cGˣ8)OB(Plߎ/NcȰ\zX ZhNȾƐܽ[q#L*Y2nݾCPYd֎ȅ)-fogGrJ{7))(:v)KNIA^d2,uKuPN K^^>aWčؾXeܗ""o"v}6k{̚Z;w)ݟVä$v-oOlmАAZpeETdoyKRV s;O'O)uU{;[a۶Ý-[p%RRRYx Ӧ~3oVVA3EͿ>oBq(ܫ)88hStc1]zE:_┧bgzV@׫(|շX[YÄߟGDÙK㏝;9~T!]qN{ l3044ĻQdfʅJ9t5DOO}}= s%7=ʒڲ}&zunY!Ax\\<B;r=z'K))G%#G3g4\6 .8qO jC*R=U*`kkGH{zuiМSpe.VnGD -Vo/Akg>=A3uط ˿_XjB'p0b¸1?s'T*Uv*OǢ0oT[}zmf :ֵ3dge竳>wMor ~)ƫ$ PvMBw={/]䐞!09Y[Җ`鿣ҐJtȾٷ -ϻ4/yPTW`RX27l~'|I-ʗ!)I Uؓֆ}BK;a{*@Zʆ(|чޱ[k:Ms@)i( IIX[Yi6:RT*d %11Dn=ңˎ] =pzu 366ϻ.(ܫ袽u>gd<ˣ^^/Wy]PV_K?>E*y9!e^N_}1IKGDÙKOϞ~+qȋt3G`eIddfrqfΙχ'YYYq}@3fЀ~|tN:M\Nbb6lbמFx'GM7#:k5IHH<YYYban߰zz^W9g~^& 7n ưwXUųw~T*=zį/6Zf( ~e{󕰫ܹ{BAVv6;܃ZƳj)iVZ;D8VřjUӪ_'==~&Rم%++e+VұC{!,ԷimDHRjTDǡGꪕ [N޻HrrrXrMf&};>7UYD-yÿp2pڶ&+;lxN=~sf` 3J xjjg5R)O"&&JEzz-קIrwտHllwkE"P*J2,L3_etQ@kk+\\nBB" AR=!B^´j@DD !?? i?N/у `;HMMӬxw7ܞZrec.s>_ S~E9q4{W;5/y8w"FFԭ]옽>ÿvK`v o۪%{BBAVs Z޵ 7Q ݼu7nCT$%'T󉓧ڥs;//koRdxV&,XYZJjj*ZWJ%ru7Үf׼y3?NgmT*nݾh[}zٸv5׮f/˗,S`{; >ȨBcQLA~wUx'y@ccraxIzE:_Qki''''akcø%+;u-ؼE3_fBEK8uKEx#rÇO~xn/7c^+W!+ " 4ogfOH(~Lǟ}Hnn.5{xx011‚]:8=r.睡UR˫?Y*cGdWUsjΕSg===*ܿ|E9GXhs,fEX[ѧWOɛkդ[#G4VYGBb 攘 0~/>biaAp^QF"0kTf̙G.A2r̞P8i̝{cddDƎ)m2fԳZ4o6) _&000no2c'Mzuzl'5]} =p͛q)zo%T 85{kd2Y1EKfxUά 1)͛77Ei8{Ӣ?xlBcn)uϒJt n/ |3Td2խûY6oon޺͢ahd[}z X[t|.^ʼӳfM؈`aaN!ƐB'cɸ1̘=}bbbB\~C8_0e,4}۷kC;qTA*VMxըAϾU* c˜.^0Ê1i!p^VVVs\%۾]QWQlm3c,g3VՃdBAizUXԫBzUoP͐JB;Rǻ6]:{>=~F,ZBT"""""#pGt'_j ?ΊrF}HA:w,~KϧvD7x͹G;L//]:ޥR{`ϙQYI0}7ƌ]Vգ¯Dg{ݢrK}}ٕ®F~EDDD|0l(ÇQݓ933v]sua~;w" ,!S_~)+WBVV嵲zzڴn7!F37'O "#Jׁ)"""/aC_iM7u{CA/Y"7=پ7hae`cmͺFEDDDDD^'veڌ(0>Em:x톳?wǧIeN>)v:wE;bz!l+Plrmw07y]NtZxݢqrr|e""""""""o"˿_E(Y_^{KMo$##7nҧWd2zzz$×1>Mq(.[QwsssIHLµJI/6{)ێ-W*7T ǫTvѹ֛«ؙ(=#33S_ <~X[*J|^VT:oϿMDDDDDDDDDDDDD^v܇~ʑ'*@%{{ڶn[o߯N<}K HJNBRӓ .Jv222iۺ%))xt UB$.\DZc d6mzDkde)H$Ng* )iѸQ#Tvٗ¹~9J%v5+|Tt4\!KI =;ݼyĤ$ڴn\`߁7UbggGؿĤdbbbrXM5V+1;'.J͵ M5F&3O yr3S3|CVs%*ᑑcjlO~$$aE*Fu:nnnl/_)T*%*:aWQ*󰷵:|G %fuotkR 4NN<{Dtt,}qss+DDFRqwuŧi Qd)əTZ 5H$:.;;aaUjFƍs'&GڵdgQ\/^FP`ggUY[oqÆ\vlmi닥@.lm8wiRpvr"RTuZ~&yUFuΜ@fF&ffh/x&%'sE223131I8;9rb:Zbp(qlDDDDDDDDDDDDDݔpfdh)ssr示q۷ΎGq:tUY{BP*NdT~>>(?xߦMpwGPp U>BF V\5s33aeeIZj*CBpTc S`x#գp)x2rU8AΝʹ'r9*=pPȳHNIgw9GڵcK sws#))Z5Z;~+KKEApYZlܺEvm066k9s]IHL$"*ݺ!TBJj*vvbcmMlCjTGRD;~4iSvJE|B{V9r0CIw%խK3__R)aWD R)!u7l@nn.nnz0.N-idO(1llyoYY ه.3;'#G rzN|;w1.^c DR=TTx{P!g'b>̌‡wZ 6!ffxը&&&ddKK ,qɵJe bULU%ڵjB-a_E__}}}TBzFz+ "D5l2M5 &&e^PxhJe>aan^,V"H||<c[כĤ$INIE__ByթT*EOOʕ]qxJҷTwHR#"L&C__ՉdLC,,-pL[[<\u[{J\NRb2 C*bnf[*>% |JM// J4jP 5zP=TT6'G$ 2 JvdffhWT vbnfD"յ Ot0&!ffB_TvؘG:QEe75J VH{V#22jU!JQ(333j5 tzҮ9]:r ®_dzZ57lPDTT=8*+cgwGq.n+] T.4n3;;r>QO__99XaY=TTdnܼq-S.KDѳ%)'c q:m*W}f-Ly*G ݈ݭc:vw"v3(Ǔ'kto-{=g}>'@ @ 3b6 D\\EN-mm9 ?&'>m|=CYeKOOԨ^R%Kp\:,d uG @ TVV\ ,<qqq<~򔌌 q=Jd,-xyLzF')!u u IFFb7DGE1J7(Y>ud$iiiDFE\^^&&%BRCNtQ1 9}}L "((4RRR|k Q퇰F-MM S.:::ܿbE$<%9>/_FȮw'瑝R%K|ㆬ~aF+KK޼}gϞ)'$& ddd```@B&\ Nr3*..^rZXHM,Or3{$$ |=CYIMM%99Bds11NLt\]fCy@  +?Y,aSgS옦&Mpj W^EKSҿyиQC畫kQV-,--)ѡH²3^ADFFMBA>V 'qq񨪩Rʒ&,:uTԠdWT>==]*Vŀ1kVPASk7hȟW{TFjod_DBb"j*TJ)Mkanƃ0J000@SSzlyɞʗ>fEȌK~$'A024D[KF ąKBnLr;=eמ}.NZ5iܰWwddK:<֦a;s!2 S|y^El-Q8OGEE Kd]:@ @ dE%ML׃IMMv[fԫk4S~:3&[ @ @ hw-PMG' R>xy?+] @ @_G& _Gå˗HOGCSeJSX- ?)sH@ @ ygWIthז=hӲ%+T"}1m[ܬCNz|fi||7hؿ$ѷ%<%5mUq6-J^?:dÀO?;Ky=c&Lf9y CGj繏x[kҸ9|~W6nѵ%-x",_do sȜ~$o^guV9c?Cg@𓒩~Zu6oRX1:oGsf4?@MMM!K&!oB?GCC ҧgw*W2El~-l5QQQA"Pۺӫ;M:r;wgA^А*U*1sozϯcr-45507uKwڶtItчi'P|9™<ܽ}{)|ob mmm6ïPS]`Đfsl",, 2رe#݉wڃnQ&NG'"~ %%%WqU`!-S+WYt/(VԊFS\Y޻?kW.\q'*U*Q 7J~{cF :$?2>Gqwu! e@444u-?s,BOO-ޣ6:X?8pۇYs˥?g&ԗM.ƶ$| 2{P>@t nܹǻ8 ZS'M@]]_)OV\PaS6_MCcSgع{/֭nmbb. $$$bZ -]޵7翉0 Oʗz] з7S&GSS[Ȩ(t^4SVZQ=+S8t+,Ryc֯gEeȋzuxy¦tޛSP޻"a6/O_Ju$O=C"o9؏.:B``c'NXi6*V,/{4u+V`9c Me(ack>еsjըλ8 Ůұ=,_?\S ˉYf&O3ם|*[`Xp2/\3RCHed/0{_wغq-zzwߦLƾeL?F Ϙ8rp&&ƴoךKpl |Ue>45s.-2a4m^=^ձ[℄J}ΐqnL:Cqo.-qtmdyG:tkh6O/Zy9ݞ!Q0w"9Ҳ-lQn2dCxi@XX8}Q3gTv3(ac¹ѩoʎyIfG6W]YshL DUUUmj:k߁ø򠱃[MBsV4urwD(Փ>ƍժVQ־Icn/5q׮ⴿ'SRо]ܻpZ mڄlLҸt{vwd+U=D6-`1 B0i*66=ĺ8AIdi0ȁ=xw}ۺqws,\0y1C^< (Sdy6|'Ns '0*%eQn2j6;W!m3ذf'V2`\Xi 7o^9g!۸9re[ kkƌYxÆ5+h Oș㾌6ISg￿yME>~{] d/8GDN[vMnܺ%SΝH%eƻ;F_JY^#Jjs.V.]л$N:# p1m;Yh.'P9?qqص{GW۴g$&~\Hz IDAT/MҨQRy_Fp=zmyp~f!)Փ><8EU8Xd9'Nk&|ud$y竾)먩24w)LM dRf޻_n ub98{"_TodTGʼn>l۴<,͙>y"ǎd¹Zr#;h@_14/F^[Bk^=GF Q^eyz1z|鷹OM8|o ᭐~# g@ P AXD"Ȑo*[Lʛwq,ӱ'W,-РxkU%V]g!̝=C]fu6^rn*(gJNIaXZŅKXD"œ.sO.];>=3…Mپe=ȄIS }.wm_ <2V$[ʮ^]xGӿOo162G[u/#qCCC]]] Ǚs牋N\`8;{vl$MRSSqukגb:5lJF=~#]]]jVD"A_OJ+&%ΜhjjR /\̬>|1_ϖB qjB>s'GJA"@ѢVri̊,qunr!+O>úx1YIz\]]]Y_0gؐL}6gʤ(Ay'ZZud$)))<~׳}z?xĦ-ۥ2%%+(&V `ldGc%QryGW7{t*ac'OsٴPA9*]G}}4mw8 }J$._aXP.3+SZgpn5'GkY?f$wyrV.YȝXb켷Q\rz膖ZZZ4jP_6?;(YC9)R0!H*m[jV'$>I]-*:s3.[HS'7:mҡ]jT6*** 5jeںe-ljl*+H\mZ@SS…MesubX[R%)XЄJ Эg_YGmf%y5###GpwsSDQoskX4o/fU ?:}(TT$@ |2W}$==7+1(@TtsQQH$ kYP̓X%FrNJe1DzEaSSE&*:ZQ˂4l?Jjj"#ӓ*gD+tu#E̊ΛXjתɒHII3 MM _ @CCCCC9}I&LN||<ڴ{l߱EmF cGa&M}==Dʕx:'O}2qJN"(Wܱm](T A7r!+qF넄4rbYv (]k^N9˪eeHIIASS HPWWmٻ}{ahXDyav{9y,zö;7ǰ@/ )#&M2[YYqJ6m-psvb@>hd{.]޻?III;Ŋ)]+.?mI6@_Ȩ( R&Q̘=Grq#~ƴDDtr̊!XowWgՙcOQVYɩ~UTTUADEGS\Yԯ9y&|y~}ҴIco9 >iB;2*esiXէWwYZBBϞgώaZssfs' /Wntw1)OTT$k>{c##lK5ʖ wjU]ºF ZZUTUUQSS%-M>*2*ZL 2sML8ϋaalۑZ?uxVr_M / ^G H%SOOƣɨ}gΞgef͘Q.cNľQ8D"ԴVsb޼R4ǥ\$mUUUJ,I8)) 6ujyj6_- ,[6ؿʑtbEDzsQQƾxON04,:{JrJ2wݣX1+=+_\\W2yvi/;~"EVdm$b߾UP>MNy+=@_s<_MLǖ)b}.aa:}&s5k| hҨԨV/qt|)z֭ڕK\`|Vywn(jV 11\^`ALM )lҒUK?>SGcK~ȐW:0 <-ְyj *G|sLY6܌p-%U ju<ȵ vGzgaaxKzz:}Oky! Y ]b}x?*ߟ{>[@ ,d_ ]޻ב}3gǼWKCC<|H'fcc#v3p)wqqzm^x1_o̊җs^+}c6mND+)˺?0珙ٴe;7oI~Ǽyzes; 5zcچ  cIOOEX;vQxشe߿'>> bߤ ]CǓ#dddP&Lc(Z c##{GyK sJذnfRSS}=VTT{Xf=͚ʼj׬6O/ַC"K$p g<~3gS|9r!7oPLiIMMa;?~_?RSSIKK᣿ 4i_?> !##.w#HkNHH(>~er!+ŊZKAUXi ܽw'NlYtEZ)xe[֩];ws= P{!_ĵ7r5m--3)T ǎ¼}MMMYygY恏=>GXYZb`` 3gד><_ίL~c##zoSsqbcߒבzzC>YҴIO.szariJaL2[62%Addd(]?mUUU6m]O #CCb/ZʼK062Mr:ʖUN`g[ҵ6mFīרSt)-a0tjߎߦLM.00M+Y8 sfNc4sv m0w_'hiiQΖaש]0tcajT&'ˊXvHp 555Ǡa#)hbOfIHT%3m᥺XKW`RTUU)Y TiܨBB4l$II84kk6ѣmkܽǒe+Ҥ]V2e2|Br#MÌYsoa dE Ht3i5ɜZhhh&Od4wk6.h+ͧb+"!1ʕ*0u9l0 ,G'222(hb301]ϝ--k)VԊEf˼1q}s 5L>h45T=uVVа:hVq*}{ߠDFEKR̙5C޷WzD\| ЭKgJٳw}R=~UvM7k>(Y eJBSxtFaSSvl(w1t0Ntt4̝=Cfwg1#sL[41HaSe!;vu5g>;/׃es#M2揙So0*ϟf˥:C&r+{nܼUkHLL`psk~oZ)QzMNzm;Jc'Zҹ[鷹`.XL۷Qz5̚g=$$&kt*ܽ'H^'-=]*1ob87Ν}cRSSٳaldŀ$ u]_Ɔ2aԮYR|)7nܢUKwBXjըB@~:3ǰnf8GoP OtQ,[Ɋ )m 𗸷iϕg.Xb5o߾eq[=\ dy[o18|u}oQ'2pH7My'o{j>~ej]ߟiwŵb)Y7n&!!W7З 9ДKK7ĄD.]+RqukG? u jըi!gO4_Jd3>j$EoLddbEOY[cҧtُ{_ٜy{j-7?K@ | ?&T3ɓDzK5A07I};;̊(NBҿP/ OG[Ծ{W[jVƥ'>ڞݻҳ{ׯ,@a.Ņ024-ƿ¸9 ώ|w&Q1X[֠R\ NwճKs016ӧľR%K|vv钜D y:ccf&dABB׃^*:::ߣ @afV䛅 @ PΪe_v|*Vŀ1kVǺ5MMIOK\a\kk֗*W?)hjiP| y)ΞEvvhHXx8W@@ @ |C]ss3QL ms@ @ |9mCnם;hk2- @ @  {fVBBsehP{#@ @ ?2UxN @ @ @ @ F@ @ %Ù@ @ @a8@ @  g@ @ L @ @ P@ _NMۆZo$@ @ <3@ Ip._%:uEߠaDߖԴmHZZW8*y :>ѻ,.aVϯWD+Zk0t k=_gu(u7<k@d>?>^21&ɍ]{7v?3Gq&?) ˮ^gVO?xHjZ*ŋSv4wl&Ks(Uܵ!oB?GCC ҧgw*/׋ZlپUk֣D" ukӧWw |{fs{H R3M 1+QQQASS s3ZtmHD}6yʗ#,,{зw\'-V֦iƌ:555Yq3.Ijj* 4Z5zOGזDG(o4_gϱӋp2`ǖUޝ鵛8jTƤc162򕫬\-֠B8g}cF VTD_or_6 e@444u-ߟ OnJWFLZ5LswWgC>̚3_.93iԠwdTmwо֧sғ5ȬAr?(^s022jJL4uu{TF'OalZ鷉I,Z/ipw{&p&?)_]v12'Oc@L4MMMo0o"ҩvxVNZj-G3n*WHbb"OfXJ+Kl yQn/Crr2BBشՓ.{}z *Mp 6oc^G3$ҩ))1vd+JվhHTT8M[Qby Ĥ3X3dhʕ-C \ |;PFu1h(vO}2eJ3b` 0ϟ1hjjzLe=PzHժ8q4+֬cT\ @.2._ʖm;X|1,\ ԭ]={g:uPlҟTFSy+hb)- IDAT">/$*<}|3ٗ,4"?y TAn$''silX%!!Z5l3>DGЮM+F ";}9z롧W$| w~u5e{ee݃ÇbbbLXK||1hr󛪪* s˕-#+玝Qw"{6oGlݰcc#BBorKKF?[5ͱ'1zܿ$ѷc\ y "ԴmEaWktlߎ" cdh}FL;57e+9w"Μ%::;w1sdַаffEK'Z|Taw s&ڃ׮¢+ptm]czDzzL:SGhѶC*Y3RhQ6l H][{t"0(6iݾL`kvlپCfkvE웻1u,x K s*V(֕ɢpk偽+CG*깓6Ғrϫs_0p8жCW޻'WΨh&NkK웻ǓFfܾsW֥e[&L&Ӌ+֝:ukcc]P2|4-܂ISgo.-qtmdyG:t.k{el­M\3m%U s,;.-۲ivY&C&)))8GVރMMMʗ+KM9Jzz:=dMg͙؉R/AP-41Vj<~:kC4oGBb"1ozO𩪪E 7Xv=&@ިt7w^ælۑ?%KؠE8wo߾u+w;M}qܭM01qKcVāI aOЮcW9ڃ]{;jjj<|mi.e'**~þ-{txCGbGn\ ;7bx Эg_8,w^صwB[TFetѵsw3(ac¹ѩoʎyIfG6W]Ysa?y1 {kYƴPAq3="k׾qkAcg91Oɔeߙ5g>8dϾE4vpf 9w;2㙿h)ǎKubr{={>= n{""^1i -\ qpns .X$+A7oѽw5jKKN:H? wٗnpvo6O /FUks?`uJezB]M l_ur׏/^A2kOٳG7lsZ_o!oTU%tlA툍}˝w[C 511<~&*kPiiiܺ#h 'G@墯GUoҘ2^7oq*L $,— 93H$XSj|{s̾ w~T""^nfV-[LAcbc2xhFcdب%">о]$%%1jZ1#S+װlua4`i6FVb\062b]C`¤T\..Qc'=a4Jȁ=0m>{sfMF\Dw\rTD=[y,557 BMֵիV!>!n=p;xJ7G[޿OļyQ?f$͚6¥ƌ062ؾӋ˗`c]'0M}4_A5p͚6QܽwTT(S@Դzz4%Kp12S'kϾ4ǛX. fddp_iBܛ7ZڵjPԔ^ىxg%]cH^GFu$t'1)2t444p1L*V(OXXY5x?41AWW/(_΀ի_`ei G&GEER=.{;:ѵG_%##'?z\f̞֍k(U$1oސ "9%q&˞m )qCG+Zۺs,W&~'Yj%l|*cOb߮)\8߼e҄Jlڲ׃ز~ ܵG'4QSWcڍޱEZ7'mki }Σai\bԫ s'GJA"@ѢVri̊,qunr!+O>úx1YIz++ 2f֜L4^E[=Ԕ[4lD3gw?xȈQQQYپ ƍd뗽$%%Ǐ}zo^شe; }1uuqZ*ccmD"!)) ]]y[WG7{jiiannl:YvZZr_(㴱5><AҾp{eDFXxﮝ;6];w={Yy"[^banNF 8s|uxT׀M6!HBpwb!$݋@P*8wR ݭC8ąxK. }ƫ0u'NҨA}ڷnZܽ,nݾ[ۋԊnk5"g4a(!JrWe.eϜ|2899pZْ=vnr-'&]8u ]OXUCGh:_a`bhh saƼ88ݪ%R'ѵsG022v'#q}hݖjlԭK'jVQbH$|P+F[ `og_!vW*4dl.^3:{{;aNvw+KybccMxxκ6ۋNÂCdS\\;[9p)))`fj 9_[˓[H%L-fJ4iԐsQe,^B2?s[Y/tSM5D&카m۸t*wcæ- <+`,TݿBllkm`y<}ȁB ㄱ_Sz5233Y~#gΞ#%%ϒ%YhFU*?ϞJdo\Ϝ=M[;4iԐ\ͫWax1gB}O= w4WƉSgP*x{1x`|!+W' 0###FMf/Yz-Q3vʗ/-Y00ewCMJVl\R$>5V:j<T*œh_Qj/x9*VxXYZh+C)y{b4YΖׯQ(G)2vH6nJA;ץ zzzde n_"++KhXLML4r322 ccߝwpt@T:!:kdffr%&M ˱PN6=nLtԁGu.""#H$zs'c7c~SƌA*!00Hc tR|YcIn&[n߹or,<_rJNN,xr:lluz>{KiXTP󧎒ƥW8tV-/=An?x @𡃐Jӹc{vC 66NF椤j3%b9;wy,Zʀ~iF+_;_8w+&Tʆ5ټm jL3:Mr6»On=cYY-&akcMLqrDFF`?K$uigcffFF 8z$؉SZ{ SS>zhP.իUeނ כ{$r";8t͚jg/DRbblq|tjR7&6rrpuuaЀZJJ g]`agn9?v{%ӓiud_. s3b*zHf2( O>M ӽ߹s듥Юsll:isի!ӓPhưT>œ(Jyzjf <-wҧg"JGk}<Ӭ8e#X`cei011f!q~;9{KҒ:w.Iɤ |RR\ G*bgg\_kk+N9KTT#bL{dI\xv~mxƎ_$ }?SH/Jҩ;ȱz<ڊ˲v2ɉaMn+XȨh#2Ȉw.DGG nҬ߸IHL Իi6ΞȊ%tZElپ;wӤq sԪQ]#섋Z@OJL} ],,*C~Ƒ[{&%%kY~:ԯW'?e8k@ylίKW3Xzμ5|(CgђeL#kV,љ6&6VHBݳB&qpYn%YdT"G|҄HVDDT*Q/6Yx9&5%u][ j:kq].^s)Q8QQ9w{;; 2%CTr1f8CY|5g̢H$gekx7i ':NOr |υ81/wQQ} w~jjCΫ^x]I5rsUñyl)lXZZ==xgce.v(TE &OVGuwwӈclܼ!o .6\Q瓍#Ç π!*. j7ܫ ~$yDׁ:J*p5TϜw899")nu]*~fBBC1mέ Õ`O&^X[s3GD"Qz( L3nnҎrk8;;Ѷrlm_[ASEF ΣOߧ72 GG5lŷFfQ#m.~cbH|.0kn‚Ϟʒ^=̟x of˶8|aCJGԙyelش( _YXcfjʬ~`æܻ^y.(p'd<|Ęπ~LۤqC#8rJWaalߵ[+NˆM[HOO'99u7Ӽ:OLJj*FR.ظxwR=<|ىY,m.T\e+WNJJ WUB:jeN4jT*t)O2239}\3JE@` g]b򤥥LʕH$Gp (HOOG"PlJ%YY 4nH@@%++ B/HMMӺg&9|8A!T*Ν 4Q<|RIHH(y/-CNJwGAjUYa<~7'Nbv s sׇGVV[@-c_&%%KPA[v-N>\Rui6Q[kFEE3}:"m]I1CC0q|ƖxLes@`Ppyܜ8?rL  a x{7喓I}]]XYMW,-Я7M#NB ::&_ P8R$(8ӢEs ["!!b015 PzϢ-s䚼}7"2RaiiiiIJ+xY-|ΝHhK222gT(WPa6\ IDAT 9rg033O<őÆx ; ʕ)͚K5xØlߴn޾Jiq_iL aia6K :y 3wXYZЩC{ B__ҡK22iX# Ա6lBdT4-S% baaNjjj^Ǘ]ԭ'fftAp'H$i̝OK?l6d s/ˏ[+vАF aujoAFxwQz5ke5\9˫Es덞#a㰱Y#ƉSg8i*R3SSfLΘeh2/\L&ӳ$g0qղmr ];w'd9tAP^]wTP,>{.gnDfxRRSR"ӧMu.VVyb8 hՒylquڲ BS3m2)S4;w'=#2ri.m(J4q>zǮm:5|(KѓeO3Uz|Ɍs^QFu /\o{Z6 c54/ }T*)_,;Jx{y~5toZ56Gzb޸a}?ceJj*]A^_\" bŘtI8|8 &Q*?}0,fH=E`gg˂9t*f[1&az|]cgkK ߠa7]I%rcei\\~$qqqTT~TZEǒo{>naE$$&bjbBՙ&K0{Bچ!#G;6f|kt9P5lj?1z)_j-:cuƍZY?v$ExZyfμ1tZSv&^'3,^8/mmmW~9}I=v>[Rڃp1V-v_YOWaa;3'+ơ?4"""I㬟`kobLqg_0dhF J~E#d9/|kGK]:6IVȴ)~|6f2U+WwOݻyX3$x"""Gy&:w-/oSjeÅ 4=ˁfZ -'4h]ˆTIIw<<@ll[,\~}z FxB톡R.5g>W]'))vñ'=Jowٌy&ٶ}'Q( 8fl߱  87_`?)mԭ[PP睘嫬 gʔ.UkHIIy"(hˠ}zL""""&N7OQe+NSSugێ]lؼd=r;x{]N3bZ4kʠcͼz DřDřDřDřDҕ+ܾ{ݿٷO]CG7MPp0'O~7':r_׮!""""""""""""O.:sXZZRjP(xiFl\,+KKK,Lݣ|ŸR%$==Ȩh|}(VEi100Ǥ~{v66FPHa)řc)E,J9GGGÅ:!Դ4xxD$ΎED"hy?|ׯ_y ss3yG+Ka 嫗m~ Wgryi`qcɷ~o  }]CDDDDDDDDDDDDD( cvu9n?94q05:ȍDϫ0ldt)OT*+[? ]NNUi\~ؘX:ulw173#22hR== '|/[F/_q}RRH$+S+338q13F긺8T*{>Ade`mcEZ;ûU eΉޭ+o"$$n߽K5(QF.]1n={dۇ>ZTu(*܊vd2^!d*W; 4@.'##WB܌fM@(O`PETN%U: ܼyL[666q~e ׮ kJ%N4_T 07(Nu$y5kA#З_tQPJ|WҥS''!Ͼ?g/e\l,׮_'%5 +K թS߳E~o?KMKDDFT(Qܕk d; n޾HWkFF><~##cl}Ÿ%Ȩ͛ I<|Ȩ(7kEDDDDDDDDDDDD(H.cQmCHI)噑HmfffkqWB_PJ$>.BZfgkgɒS]A.CG1Ax{‚pΜ=Ovm166ރDDFǏsi:+rvDGGSlW^ïom_~RB@ph( HOOԯ[%}s/б};889p@úKT/e }}9w?me1Vг0XӹC %\_EF!o :BJnr.Pm`ai۷i兹ŋޞHlml@_Ϋp*RH\\ 맢V&EJT9L&GGBTeHXDJpuuŅH233 ^VPl8;&)I#e`o/(\\+'G yD ?}Frr2,XZYaX̐d|qW!֕#V֖|@@`*VDBŊPTZ w*V\.GOO2K"715Deʠ>N$yn3cbxv̶.*,ʔ_%HHL!`D"A.ckgÛ7H~ݺd2BB_bbb[XbGi+H?0,BB_YC+]a@ҥ H$TXDiE%99(WL&C.קf ,Y@@ Z(၅9Kjj*D}'C^⌙)Q(U*?{WDS+eȈ}\#UI{hզsgqo?%|Y\EIK+wB^:}c'.8QP>D?{9Jiު s/,us>bDW~>' ;}/SyeHOOg䩴hݖfFZiO;^ + ?/O1ё;C1ԮVkXy(J>z˰0TJ R+?ssscFR) Dok/z$uk*0CGk#!~=O#ͼ?ixO˞sOǠKNC# 'Uwmo໮4jP+@ =NN8;9g155_9ޞy6ί J%'Of€~e4oŊackMk/<333B_?H,lRR=㬬,23(CgW|ߧ3$3#CGD"rcٖ\nXb+WjU*̧!Abkks.a`hmm.t*S`|?uܾs%&6=eN7!P-[#O턱TTTN>cY*WX24jPP eddd†w [7k۸Qt)c W)92jzFFf&7o)(QV Osg^lشJ*SϤR,?3R#P <{c'NW~U*N1G :nk]yT*X_n9H%y5k7;b;gь-(ӣEںPB~q"}H|n&>{u$$%^O.r E8BPHr`呖`U{[TJB&aiiIV`ZJ*r}*^i-2qq:|\\9@)OO<|HjZJ's3S#"2=B#oCb 2FZZ'w%)IH'n/f tQT+/++L!^RR2GqWWxRBTGVVvQdI^zEPH=Pt>} 11y'G>|LqW$ VVd)<}㿟LZգ;ݿ삃=4oϪHIMeђ圿pױ+Ϟ#..;vӌi4i sӳtZ`{S7oqm;v1~r-@mp2ZiOæ^ 2Bj}oLJ%]nrJyz(^u7j]{p:}I/>x(jC]شuЯ#hӡ 8ON4n? QQ8;Q ى ed8uҼUFHw`t֓VܭW. }˗zz1hզ=ͽ/sHNNFPEK:ӎgv0d|ӪM{YG0f^tWxزm'm;t/-Zʤ̙>~̆M[g_dکaa~uʮ{4'$$RI s+MZ[Nbb"NhѺ-$.^^' [nrǾmߙ>~x~cB`舯auJTTVy*ưLM -\_ǮBP[1nQc|/Z'++K#ܝf^ ==~BB? }Zg~ắö ,D9{hc~ ±֧ n߹[+_ϳǾ2n$#UG㰳A& 99ǠyٻGc~k%'>e6G~=ЪM{v'h׹M|7q0Nޅt?99y 3q%NNVBzϯcW?Qqs"#>3Wj5wXl%^>iǜ (+ ýƻm|:e< /Jr?_Ǯ,_U~ @~s.VU*,_6Тu[˭w|"#!^-i=/<{;f^%f~mA'22jU䈡!KyR)fɷ; ɉĿHD.װ(W.]b)W4aRu6_' J115~Z] }bldg65jTadT& |U*UDTr!T*y YvMu pws#2SbE.]@uj-m+k+܊MmM5G'##C9*Tr7, 9} ==nB!' e=twNq~rjTƉӧ)fX 33SJ}ЀV-q-߸  Vo_ wܸy WW+KRTRc'N%M7ҹr]gWPϚ6n_ׯ۞pqvf˒AaddDJJ Ny"srtv\IIdwuZȳ/Qp3_cq5>>.e  YZ7jB=ҵsZR[pȮӧ2ahC:r0wܾsV;xzD5i+DK^s+~6ww7=AjLkȏCBcP+W훀]r[Z}?y߶mؘЗ. .ɾwbhhP(6ӲE3xjj sPURYTRLL\aݹC<vvGHPzR y>KVHRWs`/ֆczhxDBUVֵ%=\9+^(CC|LM繠6֕_N*V(g$]uyVe$g+w<եp̝g~.7 4?vm a[X=ϝ=/.l֩MJмvv_# v &T7HHLskk+T*o$<Nb7>cUTʊk ay\Z5]h.v6+|;apSg(.N% B\_}z3}L8{{;nZˆM[е;ukbA+ p\az,u)y-F__+KKuܽwEf!ˑ6.=zL:8{}{܅4lP7nr=U0vpe+װr: 5Xp451r-p9*WHre8::qg^u+֭XqFGGZ4BroΜ;O,CNqw+!,>iL566_!%%N,~/^³/Xr}.^3ގ9}ф#Nd*awgahܨ0׫SeJsUZ6o&M:.kZZ_&=Я`6̝C"H8zN_q<̅KiܰA.y-w 9];zzzL>@Ya(]}_.^R1Lٱ;AڵHMMKl\jw1|[{@/eNbc]:7nަR p}z/sHMM+gh 3Λ-J\zֲj.eϜ|2.Ψ-2oVlؼ5{N8 LMMyVCGh:_7z /7碌Q>԰~=d2v((LjתA r +HݾswղݼY? .ݺtPD'zήm}[ s7jJ;H@~s[ ҥ<&<<"OYR).ՙ.71ɵ9!))ЮR8x(3Mym|rʷiSN>[ܐiҨ! ¦Y|%m}Z3 y"?sP|^ddiYD)J_cC`anNl\sqHR,,̉vϚ/.>GGB/x9*VxXYj~(*y{b4YΖׯQ(d26[Ǝ-[;h{.'~+ 6mտEVVf11hXD,gdd&J ԩ]_ 33.1ix \t\saC?LNx [""2D«Wa8wR 3uƏ0fULtt Ag]9&6jLr[ p|ːdݑ-͵,K6>ެ\k'66ld2pllo-Zs&)AF1tP[ LABb"7X R)HRĤ]R),g7U+3ah- u됙{qvr̛V~ޭZbffm;Xh)_"-7EsZxuQKοcX^qEJй8vpX1CS'N}6qT(_k7nDݺEw8p-5Cf_RXswө};wLl,?PPz0h@_E-%%3.{| sQƨZdz2N]agrwae5]Xiح]REϾ|W5A);Z~;w|.=}}ukn8q4&99DB|\XYZ>.\THjUy&[R3gQ2 == fؼ?,,̉ҹ5kW-elپ>={P*>z\4[[Yq^JeTtjKÆǾ< n^[ ,Y4+KKCBܭ\V$%'&ƯD>,(hgg\_kk+N9KTT#bL{ͨL&'M7˴kÈ3v HBzwaU%}ǿ\UPJ,\EQ]tAP@Dq^]}<>;gΜs~gvmZѶ32x(Ę ʱnebbt i&$MGFeH6ܺsP_Vz'O\oظbE.:Z'?;P<``>׮@W7dtt oCDG4n֤FuԨ^ LFBx-RX:/cbcTzߩϐ׻'K3iL֮\F&Ѣ bnfFfQt>CGP!sC2ڶSMD4DfnYV>|[٬2Rx1+<&&tpiKT˓'7qYթ6 Xp_w->\.0sgNS|ÚXYhJ]ww$%&-GQREId6CFuT|dٍ̨@MW*QQ\xYz_ rSBbbP5$4y?7dwdtmu.djwiXs{~1Ƶjŋ1sd"""3~7m_xk6}\:> 2_4'"~ޒ JR_տ2^vs^^yC8y ӿOO- !0ѥc{̘řsyCxk6{mϟzP@ARRRux:xx߭,BXX8<|1wt<|`uZ7שU^iܰ22Kȱ'3l8KIIӧ8yJ+GbccJe444 }+W7n">> *+\.'))ƍ3|IJJ"99iFy0))){~.Oww*^Pl,̎gt>)))9<vxnݻh"""ټu|fO_>{:D7DFF}o_r9ϞsǏfM|6gtm$^eo@D&mA?Q\}ƋOCp 177xpyTI̎Sgx% DFE˗VcYxkΘMXs&̚zOǙ /oݠ[zs m &&}5\; [̘3?F Bs[kGXf:2Kr*VPT˓'7E__G}%">>e˰6NJ`L2xWvΒsEhjjR4?-1}L_bdhE,\ʂ062M+g"GT(_V:Hz #P<<79rPli/Yqqqci}}\ڴihh0o4,XyMLЯ7-?g& /ckttthaH׭S}:g cA_uq"ifE]b~ 6&&.d\7 }==MWMn]՝EK\ԤT,7Ke(eF<fа|uS.m[sn˖ NNڵi%5^f\*U@bb<&g8~ 3fǪ# <*+g1| RԔN]2}W+aͩ4lh9\܅Kȓ;7ƌIJI#&ì ~ Xc`(yfǐq[;A0͟IO;uCBqnL6͔&%Z;;e\.}?}v1ahi+i~©[G˄?xĈ1IOPƚ9ٶeld9ץGFTߥ*W e˔mh`Q٫˜`>]&=Ufo?;uXVlmXܝƍ6M51l3.իWԡQҜuk+^:R m-RX"tsb7**sҤ'`ӬRreN]1͟6pMV&..yў.3_qJfuT8i_="X-}S㼾z`nfƮ=p IIIQB9 dkO_O/FܻɥCU^RS{mmPG]GհW'ӼzǺUT`ndצ?wL鳈D;6U*WGN8BIQmfk_wCՙ7{FkS!..\ ">>Px rȍFwj$p:N's]6~<|DAC27yb.B9<~5pj;Ξ 꾊h&Oo? > H3z mwi( mʲ4!ޞ;V砯W~cwSFD >ӷWҥX0wqurs 6{P [D/_ݸq93vԈ_ yXF3Aob5:3H= IDAT?1TSҧW9wH ٽ =w%GWW=;PWʒfVv1AQ"VPMAHfC5AAA#j     g    h8AAAA5DÙ     !AAAAA p&    j3AAAAAPC4    LAAAA g    h8AAAA5 ߮E/JTAAA_q&tTL:vŵ72MOAXeјup.y :swr]||1114Áon߹K1u"6ͬHNNf ]; elkێ]xmILL 5Wg҄?062 Yj-W6:ڷ W\̙3jttt2M=Oy7oݦQ3;2Sj j0ѣuKlwԍSv,zN/-TJp,4j(&"6wv~g'md/V˨W]kN }L FFT S''G#ǺUTXA ~fߵ }zݬ=vm;wv}d'OÇ8KK'wO g o]vL<}{3e8rkYho""ܱ=nQ }2v~\8g/[LʕU-iR^].GBBσEⱎ|u7nmBʖ)M7<{>d`?:wlOBb"/_ )ZjTLCLISgPr%.ǣO2|4ʗdG<JT(_NC\3̧p!s.q%Kܖ-`l7y"m)ff,^1wtթͮصgۻ,[\l#;hfee:u;+2=~{o?s-+yML~ו@x_ÇԮY#˴ϞK!22vmZ1j^Ci԰>y&OV'm~g7VSW~oC,^>{0zPLL } ~\}q*뎦&Zo>L9~Twf%}Cۿ߮ٴ~ FxI||Y׿Ale=XC{; bdhe1<+8u,K\qj±'b˶̚6 `hh@ֹ#-q[؂ݧ:`؉N]x ,qu67\\.gL2\>l>ˮڔ.U93RHo(vpj;$!X:Ъ[JphՎ޾صlUsGΘMll,a27rhkkS܌hjjx\u`1DFFIoFwm.E/f4m. RΈH&L3V9g>$''ڎ[wJi2~4^1zˑzu(Q/x!Gcײ 6v-4u޻^ak3Sg̖uz=;w:ʅ6[&2M- {xxnQfeHȾ޸i@HH(AЧgwrI noCrzȚ- {3e,F BRRҺݸI[{g;5=Zp Ra###Waa993(XPnLJJJm%4kȜ6 (-(Urпo/N9Gtt4[9k~!WWJ3~PGhס vN8vaǮ=*E>;I^DD$NĪ#v-۰}nyCGbeKǮ9w^nqxmۡ->"1V]ϑqj킫JYzNm~ƎCGy?f@מ}ٳgzc*C2 [J&Os7K;:]?q%Sp!?KǮ}NGRckEK=o!{ӧ3W8vȨHˋ/}{w觼;c+,m씮ь0掭h‘6NI-i3=o!V:8k>iٶ=6v3>ú/n,\ʸ?Fҹ$gΜ/VTSkݣgkdEXXx񁯟?`eX:\4qu_]KlZ2H_pמ}i ;6lڞ>ΗFUkYt9/dרk=~rrVZCv4kHݹrOXX87nƦYSg3{BMS{'ffmV',,jU̬ :::)]ʕ**:p& ӧ4Tmxk԰>ܼuCQLiF]XY6kz*Z5$$$|qV^7ٰf%|VΎf+>~w@ir`FHh(n+`Q!!,_Z Urr2W^gﮭ شYJJ[Fڵqf0~TJ(F֮ZΑOAbaϾ]xA̜MMMjVƕ+WEयDž FWRP$.]BW4؈]:7{wm ,KʒxINzҹcЀ7(ՠ3lٶqUvuU@]JAsFeHn=Р\2 pȓ'TRoS'k> 9w/&<+lH囈} c-j``>صž-vaQ:(52\~ pMc'7nf-ݰQڶ򚘐;./^ @ HvC N9'ܥp(NO1g>sfN!o6ya=Px)ޝDFFINi~n7y4e?(X/# fi r9[k+Z99(Zbؐ*i<GT}(Q8cF`SbjwbbbٺcV8܈<7HNNfjj*;AA޾}Syhhhp^2jI_=Ff,^Ƒc'ؾ{x xgkg7VSWI [ וjҴ Зcϟ/ uToܗ.bҵ/VþٶŃrYftm073c ϲYf]כeA)\}׻gqT*7 8r^aY.?M4BWW7oO>ٿ{;G V|n֔>~xR YǷu$ xAXu&LEehLw711&%%cLһG7 ME4e2VYdz̟3C WV LEø˫Ԡ#(dngΝܬ -lmd27sWʧ{Nhȁ=u YSl\ǻwhҁKe{vvhkkӨA}>{.}W*iԠ~=BBC;$ѿOorȁ]J˅ mmmr͐8q411ԭSKN>C.\ko7Wo܌M=n#sVd2JJ(Yp!8ݠ{̙R%Kp9 ,' [/_>.^r8} [J,L& EVJS`lYv9~TeH/VT:w>$nn4sΕBf 230{BL>!z{vSYϙ)\Ȝ6Z3gNLMK˥RpZ߼;/W{3t֬ˊECޥzzʥCVN8yZm?;r[77o)MGG3|@…|heyǷ2 &O>9sV|iܰKQNqu[2Nq& H\5"2RH.c5jDDF.""L_|ٜLQQ,hqC*WskzgE4ʄc? QKJLn6l  ',,itˏ?>zf~ŋCO/qY[ԥz,\J]q6 ~z޾hRh 9yrQm[J|̯6x.\>KC>|ٵuSyJ{}nnk>xzt!|f{ߥtSGy*Twvc5u UuHй{o?hƠP@?ݸuK;\Z9r͙][㠏 *2 < uگ3&6… a <6mafZ0shjj֦>-dntN|^*իU%$$y0 Y*}Q|3 g oy9%؉*>sN1S.[ZU޿Ԩ<Ԩ^ MMM4INVMrSZ tR*51aj7zֹlgr?OMU􇆾r+$$c##ƁsN 6C`̐d2ڵiE:a8vc*T(ǺUjד*77aᯥʐmuupB~X/VTJaf-»hi?_ӳ]z0 r<{ KXԩCsz1cDiJZE'{M4+-Wk+$$$cOa 3d`L6+M&"B5F*7cc455ٷv,,55> "9>!`Yz511ի[JJ ¥zG,u]AE:*i ש]׮s9\/h"Ә/'r9;s֬mΘE 8?ŲI#)kJpFRbЛ:M݃2NlQt>CGP!sjVRڞ,֝u;W%2* /SvM@p J[7%$&f*UCBCɛscKvAF[0{B֭vLiE}ܱURYՉƵjŋ1sd"""3~7m_x6u#;mV'YAC^%Av#_v m;ع{/߼!{N<ߧ秖P\ұ=Sf̹󼏉!<5π~(X )))ܺ}PܬO-G cxxnMӯW1w,.˭w>j,QQ5ô5 $";$;vṙxbccYqVM9T@ll,8GJJ JkK/"2"E cld|!s3J( IJJݻhv+}_9U5V^u3?)m<([4Cgԯ_o߱c^r9wq9i^0GZAݻh=V495a?yǏYnujՔz2JEUʕVv+ X՝"E KCfJLLr)URCQz@LL,lêj]WHa*/WwbccIII!4t;״N=Gнpbm{-R׮K7? <%))INvlܴHزu::9V$ԫS>qv-ld[G'6`#Yb IDATŋC&aQ.iQ8OSyp0k{Hux~Ip *Tgf{XM]գ+N@޽&99ׯdڃ( <5}<{A?'I<(^ P<"_veU'fצ?w_ISzbnnǏ񼏉5ϰ ;ˍbh+*UEf l҈ 6qnCѥmk bI6ˌYHLL &ÌjCJJ}dY 205S{LyXsjgdh( Z<n+W3w͸1#)XPy&&*ɓ[:\,`Z2n(}{WA`h`@Ν(U+UG. 6qqVS'su2%cގ#0nHrWghȁƎV\2-SOHE]6¹X V.$,1#)`jEKg.;d`VN=LciғNg9}}j֨ h܆S`kcGPʻO$''ӱ[Or9ʗuQGL]s6ymc-jeS&ͤkF4j`@ >ՏY¯!͕һ`TQJ:MJ+[fVtilݴ7nr5qqq͛t؞O%7L_l#ƌ'!>XS^]@9N1tWmϷhg'VK_8ok>/\BRRzzTPNiXxZzz5WXN.u(=S{mmPG]GTLGJ{[E,CVubFqmswqL>HshSr%zt#T/fv|\EKy^<ԬQygd6e%wQ 8pr$#ApR? ݰ5# ʐiEzb.B9<~5pj;Ξ 꾊h&Oo_5TRUߒ+iґ |UAYʲ1mZ:Oޞ;V砯_ly/;:*^ѧWwWɏPt)|qurs 6{P [P_ݸq9ΥW mNB&f /j"to>>b /O?4Z5s䑯Zg.;H~]]]P?,E5غiÿ]X XMC5A~" AAA)    _@4    LAAAA g    h8AAAA5DÙ     !AAAAA p&    j3AAAAAPC4    LAAAA  |Z(~PIAAA~Ǚ /S3ص׮4? JcEcK~o%?N0tĘ_vtjKW2LO)6b]K^eiؚ? S/?pH?5ERv%mݑ?R}y-UfN=`26nu g Z'Zz._a4-]{p4Ace;HǮh ׋RJ~6dsVjoBkdc+.X̛kdS&L\.'do $&&,?}e)))xxnNKBC_enm%>Ҷ}i2p[5DZ >~ڍ8FV:8p+IIIJiΜ;UsG|ղo߾cE;ΉEK]ػӹ{,p2wNK:uŝADDDʅPZjt٩:)޾`v6n ek9cF W[_e;Knu,صlTguK%c'w b~/iו֗;xnm.X9ѥG鼶Ec""">Af-:՗_8RegS9ڷI]lOII.Ұ-m;J21TSΞ 1ooLGΜ9z: -MD;`4j`A4Pk8#rey&}/=?V*,,篑mN=V\Y/9#eI)]A0dʖQ*oF j}k3kұ~oGSS l?FzX|Ne;N]8v$QlٶY&Ӹa (X:w=n+Bн*=`؉i$$$&[gXЫ ^ZrISgH=eY]֦tR̙1E~&@эKG._Si{'} V'4qmjU< +UD[[Bf4'ESSpl傕CG!22J~6ڴ3mwϿ.J߽xCm. RΈH&L3V9g>$''ڎ[iz;eiߛ3b av{*fVSpQ׫6`OO&F*٧D;G˪uHNN&&&^: D&Q`Z9;rC4UfΜ9ifeɬSH|{9[G6KLLdo\ڴ͂?O̙cݬ)޾;5=g[&0ַ gΜ51v{bE`ݬ)9r]]]Ν ';pWa*M;'::ܱZ8һ`"ҟ9ws3IIIJнo:83ulu.be@AxѮJ}ʅ6[I޾pnہ6 :R%LCc# pnܺͧ1iṅ#NR8B_N]رkOJ+}>R޴ǫ0clL>QÇHNgi;n%*-eҰE_?&Oy u]{H˶cRI~d{3{BMS{f[(FNܝ&v :m;0evep^yUF۔ѺcN]t}PaaL5UkKgŏN]=t]il՜F\8zRzu ń)W 2J+P9O>dvN3AAEdTO>j[INNی:eJ3r`ʲ W^CO/իUUY֪%7n&!!˴j:߸Ɇ5+9 cUS&"073FͥUSaa]˗m޽fѴrv䈿7]gϾJ{v6BBCq[u eJ7r:{wmghǦU*Wb*7Ю3-OJ;U9&?yzۅD9 Ԥfj\rPzz\pI /_J:2w_zܛZ6JOwx6`RVM㠏lVTTQ%]rr2σٹ{/jT̙~i3hݠ{AeE)=/UcqIڽ{EΝcUJIIƭV} rzh[e^A3nN]gW]W&mhnQ>{\b[O+[ Y ٱ{/:@>DZB / 8pͭUq._%~޻.2&u)֯v爿7իƸS^ɍ[ٽ} >v<k7ltR]zjU# kW.gJ77)Q8cF`Sbjw4^c;'OD# f<|ʒCQpa_J::sMwJJ Gdڃa)'Vq@:~Tߜ:}ҥJIww#e˔r{sOmGڶK044֏;} 'Oa˶,]4}J%|*.>/WFj}C\.=0^SZ?M4BWWWvmٶ>zp!LEK"11s3gTfZ6պJJJܳ{w%ׯߤ|xn¹m\:uc>幹._eFjG\~djV{gUUJ#m`؈"`;v;cwwww":cw7xr0F?zk{TbeͲ8Ere6NJJb1,Zgٳ}3>G^jU>r3G9r`!*:cӧgwO0x@?6oݡ&.#W)/UL:嬟7VV妮揙:r kVr\.G6?ueƜyv+ëo(_p-/2=!.(aA\D"̔Xqcc0Wm&eaaNFFL'OO8ۡK…{R6+͞.|VVhf Z)5{;x2v4smD"ގ.Q5գkgtut062WnZ[[cbbbhվ#&N7 q{uu-h@T\~޾ 'ַ:::ѡ}[!^Pp7nbP~/_>YW>,sm' tq-jը hؠn>"))J?UPi>W6x 7mQ/wkԌGS`A&]fUUkSjW8_ıHaA~gh"/_>A^:xSffL8e -vC!' |ء}t L &FSwNq-u5$:Y.* 98p]/M[$ \{ .bPAQXQynccc$ =wWTu}$=v}ڴ|zebkkCF.hiiaog[SΝ2ެ)eJBKKjUPىyE)VԑkS ޼}ǭwHRhP.(QܽwDݸI]xI.+W^F?ȻwAl\ ̣OӫtOѼ09[&utD"м5M]#HpvTJ144Аժ*'򑛹DI`bb/Spa4@KK2K)krN?GTIS^Ю UҼ67uU7̤MB9լN%Q* %!!A&ә2c:p!Sd{Aq&""""ɇݮH/LFTT4*LMLT`?""D aaJ5-DFEak '<| ˡ6µY&F!X[Y)ܷ&"Rq2a% +DEG.|M?Cez}Y'NڤUٺc7iii mQ HYJ|#llmdDPz5XMjj*;z$zz^FWWSSS bvrܳ½9ϤwnE6XK*PXQlml&řlZJ}~:_koiݪ<ŋ(~S+h5eͺXZZP}8˗yA@UԴTZ5uB{qv]S\SC0'**Zc6ؘm;wh zw y3z$.\H*ƍ}$cϰ!SEyglȈ( `칂ٱL&u?]{Z?f'$$R%+\!$Tϊ'OSqk*a&aƙRӨY:ZZZTV׮Ie[63. ::׮̵I#7OШa%M׹ #ː?_~Ǽ}hӲ5ucxDe˖Vu< @Nxؑ>1(`) Jt븏e-WT*UXe#hqDضs7zJJ5lP&6 IDAT+;Y/,-Gjj~Hpt,Bu?utd\T"!!sؿkp-<"2eԷ1M-U#7s/{{> <<kk8VVxqO\j9\ʫg'sI9U1KˏK[[[YHOOg阙[ߏk|L/[(""""?(W._(YJ.^W*#WήsJ?ה?ΞϕJhkKIOWTװejjB9ޱ}͕ӀL&׏4Y+}+|~a.cϟAO&Uc0 n\ˬ?œx?]t?iXAAAH$ P sΜ'*:Q׹6.i6?T:^y7S>=s133<+vWqoWr-7opuoɳghH\ ~)y9E oڲ" tB;9y /Q uq :8EAXOWW?66#GӭK'p.\g uWJSS[sfjz%+D)RK:iD"]Vڻm[3hHTzBoRR1*-073cΌy(Oxqء=0!mmme砈ka׽ _I?'4v{{sTAianT*Bժr .^aR^E>LIL&i*T̔鳄W^sՍ¢i&Y5-,ؾ ~G 8V45- C]ں 3xox=ΐP%7ye';)R+)))Ghh~{KZcמY*/0cn72>U[S|f] Lαɕ:9ʫ̫BDNu4DeRRS}$3yX9^DDDD~is^8DXx8>9[^ŽT]&annFN<}&/]}\al߹/_-ܾsk%97Ocl޺PBS?;&1g46o[9;w6r QQMmzuỷd}]{+M6oNrr2lܲ OBb"ǎAM2Pݽ?(8QhQoBZZ11㻠=?U,5HNN&!!Uk7ڸvkT{WDBIIM9.g3̧ϕ135UtWY1ub޶qcFaogm5 .T/_ [Ғ+Wb此pN>+hܽw}1e8Mϲkx-ko|@ FGWDsU~Mq=>wdF&Q]=Rl%>Mq:9w4OSM!LUæj~𐌌 s_I?'e+)T&@B)[4$>> 5q~zuĞ}(`aL>뫟_⯿bdKK>7o?.yyIcª֫p'Y65wB\\%ׯp9:r9rW+9Us:NJJbX3| IHL$99YL;p/^2ulASn\/VA4wOje٩fݰxLL}04Qٽk'Ϟ#kc 臍 km7$''SDqV/_LrM1L> CCCpkv5?\MINNL_B!LbE:?'OcMsu222T]Oj87@*bcmEmZL2A)+.de_s3Sڴj0yѡlҴj׉ԴTj1 CGGҥJ||LMMHa!/0ym:tؘmZ |ZZZ̝9K u,XfקC׬Q#ǎ3dkuj1oq~n{v7ku5i&&&Q~ck_J5YG ::\p~ Ι/W4>z,0ahϚKSLML2?+'11If2l@lsL693 NyVfNXXƸe˔f҄L5p+ʒsD8L9ŝ7{:-e.Tg Zw'؆=aq$kԮUSeX&.,Yzup9FD*Ȉ+Gg"HOO BxD%K`*50~܋Ąn]:SXQ||OgAݩ<۶,] =}=ڵi%,׬Q. ڳ/&&eAt4ri@ݰb׶M iWZC1q "##qgbN?7x6wcȈь93Ytړ.agk#k+ ygZ̚2_Wѵs%u3140`)7ǢՆ>7_^1phҸ!Y>kT1g>SNmʗ+KJJp|,_l18tʕ~z?Td\_njߗyΫt*Ym;SM}ih`p kn%cGƖ惙fիW6NNNI*+;̟=+V3ulu MrU+UYrTVT~J]As:>"}#ǎ aʗ+[&*e'aLL,}ԔT][xEDDDD<%M[YiW[Px ah3OqOt*V.[Dl>1~4l Jb52io]]{ի,[4[2k,--N0d. ѦϰQÿuQ:~IxEPqQLDDDo_5%3LD"Q`[nG0hHV-_!ė8{C&ѥG_ dΌJ~=f[tk#vU۫;zf߁^[s~qPDDDkq12UO|:bo]QLDDDMg"g^< _DDDDDDDG{8=v"7o&%5cc#ZzӫGo]_ug™™ȗGą3 g""""""""""""""""""*DDDDDDDDDDDDDDDDDDT .@\8Qp&"""""""""""""""""qLDDDDDDDDDDDDDDDDDDnpC򈈈|@OO…011E"9DDDDDDDDDDDDm.{;, @XX/_fߺ8""""""""""""9Ԫ0$&$f""""ߐ LRR.gAFFƿW/T/O^|S $"""""""""""At#"""p\ԭ7oǗ~K%Sͩ_$7o%",熐Pyby/Zʖm;紙sX~>\ߢmm.^³/4\͘!})׹%c!#8tا-GMڇ9ϐᣁ~Ǘ> ȳ~\i+7YnƨDDDD~P9˳&Zv\ӰIs꺸ҭׯJ1Fn ;wSxbUulݱSùѪ=s/"<"䗕juͳ 'OC&}| rkkD*ş6k.՜qen߽G]Wpud>}ϙr sk"_y j\o߹;}+ZQ;uYz:rrɚWV*UH.s~zz:[m4vkϾ*é##"רqq_N^S7*)7cKnƨ:;uNCWZȑcǕ©ȷs. 'M}>rSmr3fZĤ$ܽ))Y8?.URd{FȞ!0?'ğvY9\ѵSG:kkLMqiPqd$$&d*.xJ<[y"#ر{/3N^gLMM{NpoΊ4v˨1$xJ\[ܠ TҒdL2]:r8-vtAWWŋ3{ *-}z:m~/0.jqqoՎ^޸hKSL%hw`oGgZBYR)ΜţU{\\2|4Qm;wӦC\m½o m;t diĥ3f#>>t7vvͰ-2nT;0|XmIF.;p3ԮU3W:T"R]56N\\<[dPe3D Zzc{,i洰G#̜645KvG6KMMQ/ڷi5?|D^=ӣ\24n/d2z:u7 g]' '''3yLFLZZB^oޢGߨ]1[r?$$& ⦦&ڐAW+[T1~۷49r8[{.OMugΝWjJ2UkުyСKvC!|LL,NK85 f>w6o{KL43`\\iߩ= /lۑM3`YԶ;wUzW˞}HOO緁C lݞ05q9#F]P0۫5R8dza-[Z_Xj-Mnj4X{K5`$''} ਗ7՜ r ɨ؍"_s(Vԑ:r#\:[381ˣU{6iɾ݋E?$$FLV6s`Ȉo&29wv_۳wAMݼQS©g7{:6ր"1"Ͳ.vڳ_[Ysf>L:Ysꎫ{K<)Z@&n=Nkv/y2k;@&͙5w Х1`vF@Y?UI]^wV]BBB\TWMG>qQϥ)“5f Ѫ=g+WvcjRg_a4Ys&"""6}qdTϞ?a具ujέw>d J`AxޏKs:FFr%. p֝O2Zv7nbӺ՜f1Jy H$Qr\ksiiiإ~HH(7na%޶X Ebe q>zу{9r`7XzN5k]WU $==]? Zyv~ ٰf~>Giצ%.]&Nh"e27i*&F?C{}#Jse _WQF5mްA=n޺{˛*'yKC8v܇m;vQj*/.==^!UӣGsSSS4u~QҼwhiQTI@`eU@ax)0<䏿?0hPgxDFy3y}zUD"a~4s/0obhDl5==kop?͚6s˱Cuyjc&5kT㏿>NUq-Naέ=΂93)QBѬĘε9qp+ꨠAv]sCYyFY?oĸS  Q Xj K)]RiQHVReʪBq(ð~6]FSEYDsFV,]ý/ >gseͅ rx57mfjܸuKg.Rxq.86Jȱ"DZ>*NNfZSS@n~?͛)>Ȏ{Xp.gN秊>5{HɄ> a׎{ۻ'O`[Ȩ(RSS>{gL /m^Og'MNyeESp-ʔ);iٶ#;waE\S\ Yx}(_fT\Ӿ^L?EKWp?{oA9|Km}Gn <|#0j9f<}zvc yhr9rNRĩ3Wy+۹c&cÚ:!ѮM+O`o}1g~݊DGʗ+#\ˋLOۅ|qb>,X+ݓH$:nl,*YXqy.Sgӳ;vRp!T"a xy KիUay2i*`ʘQqC__.cogK3,k IDAT&H$%_S=vFWGc##zu9鬵;n &&V;2n^~W蠫K] 1εk.(x 탎ffthVԿ˗!xj֨ƕ g.\ۉ~&iiiܸuZ5m>wC6'ۃHJJOAKKa~bp~JazHF<|4 d߅{I >n|ЂTV/ UV%qxc‚&%'PD&_|:1t̘ͬ WVy R;[[I[[4~zz:[w?&--4#0ʟ_AS'k9CBBɟ/O#[d211Ԩ^+Vʅ@Ǝ.Ta>po3ݳGi6OO(^(66 L6[G>gõ7ٴnUc?𿩉 a10<ݭ)km҂,^!JL#""Q}\}淾hژO2~TbbciY-7 ޾ bL1S?f} XZյ*2moOw7|OݍNU0Jxxgd2tttҢz\vHʕ-C:9w1\vfMxyQ 6܅eȟ/c>iBᚺ?<"eK+*s9{~äR)-=:#w`g!JվV[[G>K$6_ŶW*UȨaC2M^Q/Q駊4jXG"ԯ[CQGGcDBB6ZxDeʨoc@T[*,@FyvM`oG`mʪ>n⩒+P-+Wyd.>"j?fbiyikk+=+ L<3SS~pO>DD_x-e_Xb.JNaџ|_?gW._(侇ծr\)^+#J֖a'Ԅ`5N[--,8rpo߽c=+L5iVeٳ07W2 ZqQ/4ȻwAϟAO&Uc0 n\ˬ?œx?]t?iXAAAH$ P sΜ'*:Q׹6.i6?T:^y7S>=s133<+vWqoWr-7opuoɳ5q_aaa >a>{clHB ~|| ׯpuo[6tړ^ޘ<Ե1%{ ?hSISjC035E*37uSJScfLľ۸y[P #Hhִ 'O3uvXH$ڵiš;i߶5$))I)HA9۷ 8QM!5mM,]mX%<˕Cs˝>TMp=޽ x86gTmOıCԨVkop12 EWW?W& nh.d2}O*d26[Eʕ2}ak>}tœlsYVah`|0S#sH$_ccc 07c *& :+M)))8F>KfUqJ>rod277Ssl{ourN?G*d=S]53RۤIg r)2 8xc'Ly6lKX.gwJӹ[/\}̘=_Xܶse˔}狤Խ%yu=wcKߣfe~e~ܽ}N`Eַcfjj*8UdѵS&OKyGhhwWxGmml\9Յ4`<`ldĜؼu7o̙((>>w2l"ݳڰ:.('O!xW m޺dٸe. r?!>">>D!##EsXPݽ?(8QhQoBZZ11H_*gu$''pmH©Q*w^g$ %K'%5p瞝n̜3?WTeO Ӆ_^zdeI޷6{fƍ-mָ\PA^|%oKK~\ ½8us}<ĔI6yA. f~oX.>~ByEdٜݽwwJJJ {}qUVqq\ LsTTnݾL&CWWŊ }hSR/Ԯ]8L&^Sc3Y'KvTǧεk3ဖׯp9:r9rW+9Us:NJJbX3| IHL$99YL;p/^2ulASn\/VAU.H>} k++222pGPM~={ӴIcr0wt+JxD0Yd͛8IFF:::_,ߕKQDqBBCYvs,bYVhii-['kߴ̲TEfynD\\<&&>rMwc4um̠aͺzdJ()WVa?>>)gahh1nNDzܣ7))UkV()^(Cgiں`^NFFʝKI HRlTiS&h\135e҅,XbnfJV-&o:::-SV:S-"ۼu;!aPT / I_1a4t肉1۴L;s*/'G3Xh)Cjת ;KTɒlۑj2胯9+*󴵵Q[L [zjlX&MIdd$:TP: X0V4:uND"`ldIɟ?߿W ;xoX`mU93ԦQ*ÇdƜO:)_,/_.=9vfڶfj4vi@מ}151. u 7ԫWG!mBF >U)g`L>bE8@\eJ+|\E+UU滟RW<\ε___ȱBִJ~˟_!5Eit 7ъQ~?LUDOwư!q XT[6毿mnђhѺe ~i'Wɝh *V\H.8qҏP,-,,`ɲE9q>]Len߹˺ x ЏzuXL_#w'_>Cv[SE34Θ/_>Xz9l۱ d _pZٹ[/&Ox ;lT'̣ͧSrqCB3n"ÇfeHl۴GfF^x>-ܛKhiiw NP{ft^)}#X|EOWvm[ӦU,cx6?giڄ&Xle Ƴ/w._z_e+ˤcuQ <|Ġa#>r}}#r4gߺ(""""b4'||\ƌ/K+ а~]m؄>~(hU4_gϞC665… 3 Nzuǔ6os_:x-ȊUkپeWʆ[Xq oK&22 ҷO/4v!6&VPͩfi)]ko0~Tj4&63M߀t؁ujuNnܼ͊02ϡ#}DnZ.-`A*Y}}eLycǩ\#аp307'6=c&Lf`_qiP`O-<7oݭC'88bogGl8fΡDܻ0 IA{Wnr1͟clشMS۵` B:Δ*Y56PnZ$9%wީE^⾽{|R)Y8d1H$9xntw#<'3kk Mp1ٻ ݻϬiSJPΜj֠c@n~a:oT*"y-J+[R%KXHm(Ǿk/U3Ο9 zJ]pDM$ o9`~^\K'i:6;w瘚a(C173#4,EbWhXH>K3+!@Ddp͕C!JٹuLK5Oxq?c'L*OkArr2 '--7or1MMy╠yM|)\H~W!2*Jc&DDF~Ԝ;wx) $&&q4ddX5kN>KZZ r&ؽw?QQaXiY9ժApH(g# w!qq__=mf ꟒Ls+6-E[MhYVȖk2 cCY+(?rBskMQüs9%;HE{z.pso>>Nۍ4TdtI_VQέomvʲ, gQ1\EHbpΜkNPB֮Y‰4MK7ztv_短ҙr3ߦ5"hivZfN:Fj94#,˥[rZ%Ij[Y{_pci=qEQU4Mq>\iXLO_5PsW|SJ8uϿ[ݪ+,Q=jy󈞨ܡiWeff|Rru[7*5%Eiii|>c ]-l"grݬ&62G/%.؆;wLV97 +2F_ Ήl3p}185q`3X9gq΋//(^P=݋V3<^,ˢP(% 0ٝjmWl|Z^'KŊF bw*HBDT;>IDAT^kk7%ʷ(gs6QuÁjik:ŶSTRFd3G<=4m;o݁R*$:|POO_A~^_ [[u:m2Dq<t!V[^^j1cXGgn?76& bfhjzx,|~Z]^VjZ@GWd2ܼJ<ԬئR*x9mkmMp$ U*U+jd,_Q_FcNo߶% j4ÑT(ff9[6+33H ;$eج7]14n߶'|X,nZ*+˵D#4-+/-)(BCãa('+25>MӅY%6k$im$)'7㸖p$^_ε:sueJF\{VDO1! zZĤJRasð'O5-,v^_ZZr{;4s!43;.JhGMÑdw:(B( |Z٬ j&a}˦[v;I4MU*${z{z:Bh:<19q\_੦fIٹ#ǎ 899xD8ΰYXM2g-fhmmSGdRAL;2O"gllb6 zjDLL$)ҞV.k}I@ \Y_Ѩ Z,]==7d2j5QXb^^`8T*J(O[f ıƓ]YRuw hqٔiETSԴlG"m~sss3DB.8n42$q q\__f$!=cffhqAgFRM9GE5iimT)pGEQ;ڦ90LkkMdw< $IժQaOb V+I#&d4EOXG-3M$E: 2vA<110W22H lܰ`0++\H$)`HEVJE'OTJB755 g[[9X̃W;XFQ*99۷nF1,y-F!OmX$H adb==}gP(e-Iumm흿x٢‚];wt+p?x}\yK1 q\F4M_LAC0x<[p+MHB ,AwCHZmscA+3JK;j*ʕZ$ 3{ BP{G繱q 8FcWG,`YO?:jjM }taeD"R* Bxݸ~IpKpa!QwT( ukHH !d6};޿?ɿV\-m= feڧgB$I* )B+ S^^`!I"IB.bqery(h48+ Zf WONMk>>z7h_Z$)B΢BaCxF?ټ8+1G2&! 0Qp($GU+kk6j5Wwg4-s(ffڏmL-lڱm[oo~Oj55^u^ڗŰo}1Bx<Ǣ1\F\QBi&hꕩoVQ(<#T*envfVTXp/ Oa7m|WѨJ"';+G"QA0 *$IBn]hIDWJNZfUo|blKMy! 8433377_UYAQkz΍MZp_PAS)^OBB vuOӵp7>>1<2U*k$Ewv, xnhx8 }~sqVdX<:ݝ#I2?// 57]Ӑ#ѭm? "P $IWj طtTWuuLMόOL>%KsZlKk ԏU+Wa9pF#QZt9fժQ[g;: \D2984yYUa(?44439=iZ#lK,{mK uw\I}Zp8LxQQB Wx,Gjku:Zn:76Ѩss2Y$H \^Rb*AOQTwOHQQMS?MPNv6EQUUNl잟wV_;hT^rlY;;ϝ nύg϶8ΆU+jkk$I&1fC1 ZXX>QTX8r*L}vvcpxDŬL\VX??hik5͹99S}}99Y`hM*Bn0臆Fzf㦍4M2x}+B | Nqm||jnܰn~Ry*22l tvv7m9o h42\EE\&c6eL+.*hԩYRxuNMXVɔ;19ٍuk33;]$I h...R*CC5ՅcpheY{ZXpۣT(jlVfh,).R(1Q^^&IҊ:ehMu >jZ-eNg>TSsWwoX ZZ^2xq &ֶssz]=o`LKBصsBoj>vljZRo òã#h4fNkM* `@fgerpHT*~pxl2rEf^"lVF=?PSSa\.cr5^E(fkJUflH Gq8]{wi:dsr'jj֮]C^W)UCC]ƵZmvҷb.FV`n?_O]V2t9:M#L& )۸ݞC`l׿6WZp٬J/i89q)ltݞAl!a^{MѸo.P0z/oߺ95W0,cpY>CXm[j56def2,~aPo_cDߚ ,ieixyZ%^($y[EU]ͩ% | ۵S-Rշݝ\ْde ?qEf`vwS_LR.wCh9K r K4 } p(,D!H3JH3H ,4 H'$IEQ gzHIK'X 8$\~ IDATnTIfee*%.`; x-Qɩ%.=aYZ}-iZe/$Xi @Af`g"!H.wq h|@0ȤoR׷C* `y-='ItgE‚kR,A& z}E% B$E"?qJZ$ID|> zh0tt:h4f)A E(=P(!Loz eKMB4Mp$j4Jeja8y^Fe\Qx8'FPK[GaALFP(46>!JMт u:ffHweEh6U.5"xjzf``P&dg܉DJVOL 8n6&S4kiiK$re٘eXJV qϴgdd؂Pggw8 7KM0 3 "|H$R\\Ng"0a@ 8nfvp ? JRl9JRR88B"IՂ\& \kVek21 S)*d285=SZZBDV^V) IFύW"I!8A/V?!0)u.ua@"EQ@0j1 3 (r4 $1,2͂ ]^ Wp8"IBð `Y\]sn \1f`$Xi @Af`zfti'Ja9Iio|$e79aUG[<8}LGgw4AfMr]|5 /.).w0 0L,%bhx$ )ʼ\ٜL2#za BvVV~^.(;sss,l֒"INW4E1 q<;X,&zmNvdDq,?/+f_p8P(XH ??0DZrJڞaܜl$I뛜^ӰJE3=3L2F,طg=w#B׽h4^ gffU*S*E:0aFFA\g6a,pG$),6 qV[\\h~T$1׿yǒI慗^|FAN׼ñc6Zuήn'&&ojB#pwh-jkRd2X ,voذn-?9;7DQi*;'{tܡ?X,8cHn z>rۃb$m[6d o>c VNLN{HIII,޽{g(zW϶miE4oC+_bYǰ[6/((p>cxЦ[6;dIq1<_dukלrl`2UC4Mutuc[&/7LKkc:\o> 886> D1WVZraz; )7e9i0yA@+*,EQ!_Zqy V^nnIIm6kݞH$$I*,qxܞ$Bx%%%2$MNNx2t\cqFB$dЇ%EQ}cv#߯kxٹQr-,n>;;;^+! !HׯFV nOٖkR]&/7w%P(45=}ȱN3,;=3S^VpR(^.'|qq!Eѻn/deey^RYQ^q{{ZuWp./(_ӰEz(0,O( ^DQZK&!QY)$I֯[C$)$SZL0 Ȱ32ζVs!TQVvyzffu4M,{C$m߶u힚^1 DQ$ ! B$IPU^V{׭EJ!aXYYiGWwOo`8eYqp\.eۣR)S|Fmi4_ZRD"ӿܺe Mg$XyyiFqk<̯_j1ݳwvgd,KuHQZ]UYd2ynlVxj|qO/};n[S](ʴ?$mݲ}f öoB/yu{cu2LAU=x߽~+//`0d홙?xjk Ū+Rwu)m[6[6szCM `0lۺ VDo?9E{wZj 5TkkN5^rJDhjy3a_{y{h }7)߿ӿVyvBI^޻?*(/).b(>s$26_$I3/J8٬]'pH4ɥ_=^eӐǸ_ܜ+.Dscnuka$e,*,X(ivnf\'(8u3?ᵎk δ7ɩ骊rnE`aGzzZMIIrlPܹmefik'X |úV^D"C͛LFcc\~7/7gJ!af]jr5OtZ=r5%:x7‚w8^VVZVV\!5H3H ,4 .:= aEI+| `0ǷUx,ǖ;iotz6S_4ʴۍr!#HNvv pv)4 H3H ,4 (#os8k<~򩧟aY{ _J 8^#iWֶ͖ V|Ïgfg%0pC8ɟ?+.ml|"QpC_3?ϗ%*pÞB$jj^n-I99Kn .EQ۶l"K Ғ‚|8wl}d6]gfY MP(tgfD`YH4>1[owv<_W[dc&Io|}}=hNvL8}p(bEES?B8w&??oxܳϽPUUB!8x?}orrE !|02rLD=wtZ(ð) \.b,qaX IjaYeeLR_>`q<afR&';W=m[7 3 SS]g[xd4]P:{¡=;T`g7mܹc~gJmX3 #8ؾuߞ]|hx+_/yh,h~֭mz$ɺښ_x^/m<1::ZXWjzf߾V#o2S[,_<3==+ Bvv=wQQ^t^{ a}l^ _){zZ;9p0iڝ۷[ '^|@ P(֭muEd2ݵӠ#995#_wvuY,潻v:=g?r}gJKNǷxowjz}] e6nG1 ~;nWS] @޾GYڗJVʫȕuOzڠ;>m[Gzmf\R' 2\4ڰn߶!z>ccyŹ?iG*rϮ=p3g /Z߾|麚ֶܜW.%?ޙDha7iX;zn_BAA/Q7<^Cw޾%IW$!^۾mKjH$zĩ(+M2 MӢ(ڛ|I}m|BE$]n(W\\HS };oۺySSS32l%EO+W?pݣxXQ^5 ɤX>$*բR(,2EQ<Pj =ۺc֪f'$koٸA|DQ?؞?ʫ5m]zU(Z,:r3~{z?0 V\ wVS\XodYp6m TJՖͷa*UD"dx|zvhɩ`06`hdtƅw,իVƢwt 4Z,8TW{}>.d*,,P(7d xno"Ilݼ}pdaMd"Eh㉓MMDblbr#M2Lε$ v{6['JyAxhtdܮiʲZ-S;!I(A? IDI%' ʋaXjՅ|dXAZܼ71L+)m%X`ñCnmkB3sU5*@&jkt:FqitcDQ}A{FsLB?0DQ 쬬}c'c hD㉄J(/5c3Y3qP D"9Kt 1 YjxRh` $IR$Ɍ [,( DɄd*JQ` HhBlCpXE,fLF#&ɘLFקQjB(Ox}ܜQÑH0$G<{<^VRRoH4U(AQk\ V qJS$)b( # P4e0*rW|VbcxiD4B8˰LF \a4 IDAT n'Oej d]0`0a@ O[Z_R>Zqܠp8̲jE1 #A4\.Ecv{W! ðF ~e3lV\"$4-3 4M^T*ɤk+? I2u#DqrUW`}@0hZBճ_~? qӿ^ 'z|^ @>%3/h,;{z%I*.*׾SAQWߖ\ES׿n+7'{y' i$ z7q/%qڛ\r%\I$\\m`@BBP]ڦݕF%?y73<33? /eAA_AA`  h AA2, e+XAA V  `AA-`4m21/S2@4EQ& IR.WE)U*ӹ0 \17[`|!z.{hxr=,n{tlf-L$B.W-wil6kOEɩG <|(Z3>>Z#mkk'V[;I& pRh:`h2y&m*U_߀bYXf{FV;K$I_~qGL\WcdtQ=t:OOP12MK6OhhZ 3fXzo\Yx`Xzz9kzCz#Lf]S[WYutD`v~lbbeef,=aht:yzW/W~ゞ$INNMjs:?HӴNohl$~~2&seڳήn$1 rxRbBvVqhmma1Y))+W$apUV&9I52"22"{UP xd@-Xfebjj#bppp\v_"Q_>FM7{zl6ۥA^^{KX- XeryDxDr.W]q8|oo/5v3)1!#=f-ӥ|o﫵us:ĩ3##aR>95 HTdDTd@;;'S("íVuzgqP\.jFq׬^uQ=9ͅ&94$dbrh2n칲0ǐJٹ9zEalj^MS}}׷ruGgߨ. yj^|$ AQԔ\`$%%&ԕɞĄ[]nWk@ w\z+:*x&*Ԩ "fYl7.&74I!,kC#j]CSsDR97H*XG" .w|\la‚ Ņ|>_T\.WfFz~޺Ғֶ 9]gwϖMK X,֊o/⢂Ԕ:77ٕ:vǜN饡!lS鬹zmm%Eks3H.7'om.GT>`CIRXn3>npp\O[^oxAAO jff%ŅY^<johܺySIQa޺\B)+]RX~cIQDxXCӇCpX~97gMIQaqAP(y}BnlB/'09vH 0Di [BCB6,kvcSiI| EQ_HppttTqQ?_v`}IQaQA>ܮySE *gi|d&((b  lL& Onx0ғ9MaR)zE4v\H.ah{GjL78ؘ( |}}%~~K!( |AQaERav3A A $8lvP`a!<08aDBj@I¤RbXppȍ0-:o{8Efuo ad2\nI 4r8c> ivq8EP ~ݮ23ӽx<A<ɣc&tcOr3d/z7ߢ(dn(dX,@`0V OoN<_Nlr$IMf{!(BӀ(A V[ttL^f+Éw>x@@(@Do0\.A`2_BbXo0lvooɩG@1vq& m6A V EQ6``\.7t8d"I"I"X0p?ޏi8NHpаZ HbB'kdtLvXb|A$3j2\hqby_I ;o^kuv.4M4lpO"1f3sOso\^Yuel|`0+3c_xbyȈl}}1 ޡ9vxXhtT$a2IA"bo/ώOLvvv{$!EBa[{R5bWO*EW&'i4ֶ۽}Wjm\q8+M-+pqdtY燆AAAUS= d2uGFowtΞ/bn(tJPh2p, AUYKGF?}`4#*K.FIj`=IR yks-[&<,L,y{yJ{]Q͖UYb__j;]8v0?O,EQb5١!!8+ʕ+}D Ml6;""<<,,6&]Nq|UV eZjP7``o4wYm&낖Vۭ[bY,flfo\`0""[)boTT劉ݽ.7iVt:**olQk¤]z! rJ53$8(  "#e2k/W5'A(JbV[w f`^o`2+WI$I]obܸ;;+3$$X.W`b** p$HS-yksf+-saR@(P)gb>V$ynЩqǞ?X2?}9(`4Z,0=V淿~12"㧆 'KuƢEc^>- /=.VEAAĄg>`;b K9xnw,} d#/AA_49k^}},//wn  2]AA V  `AA-3X  ZfAA`  h AA2, e,$o>ļV3]=N󽓧p_4:l~hV(UWvM7Ϟ/{!?eN{Y&kߚ&s.lBcцƦEIwtsGI(K$׍C>"Qyr1 [:{qgUff+X_R\fhx㷿"ľw͢(RTNLN-Jw\C vo4^s:yr#LNv{;|ݛ#o!p|tl>[2 Ą}wzeIHJܳkGP`] A$88H,]NSFXQv-R3OH ~k=oB/? ä(8 ÂLd<$g^??nWl\.70 #iZ;;xm M[7~uZCc>QGAL&?1 ?,`Z&cEOk9Yo릍3j{0 knimM`Q` p8]֨3xX-<۷2ys DWw~7_Y* }1Gh!>Q4A)4=_+}OHRS[2?҆d().|gN[G0=?ُFoz܍Oܵc۫oO ;zі{=PG?a0{'&ijجY>"H$ Ņ\.70?--exdtѼ4t:ɔbY,]#rͷ|.WVlvn ]=힖+"äRoo͛6d2W$%l6((EQ0L{ P2Xfz:Y'FRUpVL&HB~tVS'}}D٫2ӝNgGgwxXhwO4ɸzH"Id.[, (S"bQ!Gu\c遁D2$wj&yxB`brړjU7;1!ag |ݶ r_ppxFY!;FQxvlr੽;:׿4EFmmk0UO$I$9s9l`(JQE=ilu8n7ft:Q=!h C1&`XBsIa(EQIy~IR.󃝿xc0AΗ8EQJaqq*BaYh4Zm6P )Rl@`0m6P($j {Ap,, H$Lkt\.j08΢^pirjŎdq\A;gm ԖpnQQFi|b2LAXd22./Cl^\`HJܷgwDxMu>6/Sffľ4~. aa6[|'bI#cjF'z+=퉽_&nd\ljEUYSDBo//119hFcc=%%&12:6_wdDxZjY,\A_L&3!.ή5٫ F;LfB|ܕkݫWeyGb1\ܜfg0[rG|}Dfͯ?wm[6 1 e2vtz{{GFFdmX`0N' fwֆ>e`asoyd2cv"ihl+Wxr%aP||ܝ_h$0EP$&:tC2/WQoooriAYn#kdefh4ڣObjXoϣ zB&MJJ>>l C?wgXI,.,/ðOZwsVgHJ|$+ A2yŜNzVm'n>>z!VoL IDATB4]MJ.By$ٓ  HCC>X />w8ߛ4G fAOϼCR#1!_aAЗ}yF}ΞaFAA`AA-3X  ZfAA`  h AA2, e  " AAǷ`(zSmLfgFӴKvh|$Q-; 4ZDf];q\}QA&ii:N/vAc@3].f[p8 'H{.By뇏,c4gpALrLv'KE(iukQ:5׿-Lt:#c} (t:.x=2:v74mXzGt:YwRo|k8_2r+tzjFMbFGt:']yjt86>1084:6n2(~;_:Ir`ptlh24Zm`o_yϝa!QG'&V{e-e4ϗ]Z.ɿw-Q&$I]_ +I$) ?b(e_ p8rp:-9Nì9Ҏ75,J'/ .x_\(t:y=&BEBDŊH(DQ8xc:: ZpVƦήy2ׯp8K0,5e.d6?y[7ww0̌nNGoowErsH+:%^vvjlbCQ7v7;ΦoԾ=d`N{{**8[m;nt%&ڱ rС( C۶^zտGFDnz}dd2"#w]nwwOoBdFGF޹E}gdev[Mm{'On(Qjk߳kǃ̻b}2ӷl.}C X,5<:FDTTfkleix\nzjJQa>ñlM]fב6yER1>{~];&{r ܳ5954>eF'd6r׬^T;* al6{pp86&Z"wtr8;{{y%&aCɪ9B ?omjvEX*+ ?'g5Zn](+3{$~~pfkƒ ÑJԴ_֜N74</ݽ`SӲK䊼u>"QWwjffD9]>+W(MNJTTٹĄkv'&'&IL& OLLX?(˙LfltTTTx Ґc^G4:6" >Vk_  +.U\o|ml|j"b(JhwwP(_G :j ͎ (j{5j HKx&lUW2 EFL^uyhxcΜP\"闿̓]jJx_ϟ~ž[7Ꚉ5K'XR<95Weyol.fuvn(&IRZmb_մlC#^<^lL %U5WN祊@bB|\l$+H0-d1''&ZliPtHRϨN44$!>nph&ǡ(7Gbc}}|&'.T2id&IRo0KV&"<|/z446[6b<X:;kssXLfSg΋Ԕ(:*`2l819eX2 r9C#Qu}2ewT3ks׈}}{zJ%Ŋw\lK(*eeJRkgW$&7$.{6DLUs:d2_˕AԚSňlluV >Pzl6 ^Ʒ~Rlvݟ~8?0*+XؘBCB23x\.LOjw8\h_monvRVvW굞>Þ iXn(lUULMˬs\f4n46}rrҕ"rnNNnwTV_\ ;:Ilm]~l6+UW'&l6ۥNV*U 4MvrNMQE]aXdr˃CCfuzd~ȻZjvEў|C z{{͹v24.68(q 狸ubvUVhggmvxaz606>{EQJd2}}Lf\lLqQAe l)*a2qqESY])l2}l}P*+*Gl6h9ֶuvnF z NAC嗫U3j|>|yRw[ Fko44-])LfZ]T11956>qITQvGF**rbTQ[so`nGD?})ݞ6bm\z184,HV1ԔĄf2x<&|t\)dnNw7=`5Wz/WT36m~hZ['M;]NiIf˭*,+LLNYs.V]j0n~޾RYQY=08o'=+X;ou\嗫&&']WͨW$&xE{{y_wpnű1уCVudt$1>r]()*<߆B\15= 0ͱ1Yٮ뗛2Mb__;W' #00@u\&I,; ءA]۷g֬Xm7tz%!!ICC"3Ӟ9x@{5߷}f7wvwvGDxؾ= zK `0drlVZZS{ܾuhxrm() jFGGEq.L>[Q m[7ضy~DDI9k9x@$Q4HKK=}w2ѱE$919) ٵc "p˦g|ax<.L&3=55*2sUWff`t:/WVsVgVB֭=߶-.gwo..,xu9m{zGK$F|٥UW}(^}U)v}|},&X,`0Ibz="# IfIe)_&EQCss[<}੍JlIݽ-myy)+i!>&O,޳kXlX|Be4|}D|=j4Zm4|}}; ,(0PfEH( rs6&vtCqhh#mhpHLttZj3H$~Μ8ߎm[(n80;7uvw۳xвcB6mPuCoHCC7n(\/"W(kjMNL fc'&;b}||zNV(9Eѹ9Z,00 4Md2QEяf &EvF;퍢(…L&R5sꚫɊ`X~b?&Zm,2`07 FՌAdxDb2"m6RDZ2Y$"F$@Q$=-`x<2Q(|[Ϸw46[6g^oxSg//gu6E{A {j\n5uFi](F3LJ UNj@]8-mI q ۘ\NdoWOo`vv&d`l0 A$=2ES7R`")$wn7a,bB4XܜP( ]@lv;G$%3}аjFP ". E>"ÁXLL4`xrnljnXPtCS{w8hFQ5Ah@K!RsŻwmxaARDQē j֡ή,JG /_V* fYX^Q[w FɊ?1 l}#-pWAQ44$xMq!aaw=\gs8>\x -JjR// ~??`/9BˎKh qtdD8HfippA(iA2Y,&p8Eץ"#zn[[˂?y<.xYXI g7C97khnӻc [U[w=>._" "gJC=3ޞ~KOraqibyݝ 0?=-e`pZuAxj|[=|>vNgX[ J jf&88A<`0GQ9XEDn7 N{' zn(\s9eZ~`Ql4t:l6 H$fjsgH$ז_ 0N2"1!Γۗ0r"!x g1vq?qܧ쮩ѸyEl,Pzaߣ{Ac` KhiZP1y=׭v ~U6qܢu7D"a?á( zQ0<2'oW_?oϿ޷~Vko_l}^^^`w8*KWOAA c}^\").A3OLNkoxj/ aa3/LLNںuc2??‡S`owtl6BI$X6gSiI}cSOoEQI4 >n+JER&  _:rrٻ6c\.g(* >.N$>{~l6d2W&QkNg`@H($IxXRCQ4+3d2WViZTvbl|tc 2CEeNƒb.3:6;ǔXI-ڦd2.W(B`1Q54 X, }~ ЧKS/+R(h!$)\AQTlUQU]s.:jfRAPKY`00 M^/Wbc릍i퇖XG4Ϥ!?822'?i6O/44P=PNjٲmsB\A&W4m0K4MKf uj Too~뭶bcb6n(|e+'&|~oZjXo0 l.݀X}eΗ]Ɋ {/GEFX"R"b-l6Aj7/+ % eGgۘL&íoh<_v)&:zdtl &&'|~dxxWOOΚ/ډO,).qMD$00ԙ/>s騽v](ض%22#b_P( 8L.&%& 64נ#'N=v?~ZJAx(FsG?zPjwkדW$|9::tD/W<}vff&>.Ϯ??2::..\,WTC׭rS(n*݀S?uV[P``HpԔWRbhXR`0ffF1++cdd¥ uΠ* #ät~!NԙN:s90?\*?L'OTTDwO'=~B/Y$b7<2rĩ[7Nd۶l+8ʈ2-;H{{8sBTdDb\q;eecaRP?0")h4 n\ĤH(oiĩqǞŝ]Mǟs`0-KTIQTWwO7mLK-УsYFny> IDAT#̌hQO<3`-ʡ_a_rjfhxwyN퍷֬YuS)ѱCofKi){g|8wNO~£z tvuW2N;;_:YcdoXV~& X, M7CCC>qddݽh~33?0Џ\Zn^NUY>86&jtl쳋?8x)>.듍ˎ~S2|魈1_9jJf}G?BCW^پuˣz<xjo_JOݿww_,`]r׬ԃ>GOի`+7j뮏M$~Ա@pϪ  ywBA} AA2T,z~DO,{ #˾:CZpoq`HOM}B l~mC>?)1^oÇHuս9mg%ƅ@B ZԨj}B[JW-B~톍sf~So㭶ٳfDMJLزm O۳oC͸8G4}mi'M0~ҕRCcӑ#Ǧ=e@Y)ðEM.^o<Ԭ갡CL&iǏ#HW^{Ow(6sg2h@s|]ҜA}ͮ:zx?EUrdkLU$~$! #=O‚K557+B[GJKNq:` ïzRb/?~?+?|_8f9ԩUK#;UU>u7EAA_~_GBY[S7|l}[BQEQ[8N6t޽H#zs5QIbj<)N盯775߰iܘQ C>pPQa+øz]_?)~;pP$gOQD"uIIi `-KaAcÑHm}399-5%dZ {;~Eio޹/[@ x!>^o~ʪ/%p |O>6?aWcu]iwcTUoЉv ?xPQd?zp:|hHd]Æ u=^YY}n)!HXQ6ͦP8UU7o#Gʲ,BHeU=rD>}t:]$/7gF+r75L}j04++?YH$ĮnDH떄p8㠁{emiSt/G33w@U3g%&`'%&< qm\.BU‘q, p1j>iZ(}a9ۃ1tu,^?ʏW>zLTPo[WW/J]}b{ 8aTDTU]t^C]BQ7+c,IIHmlljJJJqfF7~s寿/"\7(\ z!PaNQb~% `+2̌ܜwtbX|xlPo2n3z$˲$i`/agN޻oQNlok߲m5M 7m6kIQ}TMxNӴ`0s]2nswV}όs7o~> Ü=Wsj¸}z6 9c㸝v?x !!>KJOK+**\fjˍF[vɊJQJrss6mvWfs`A**\np}.%`ٙޱ{w[G$BIqQ^nո2uJNJ1C- f:].pqTUW>zLy@}Xsr\\ܿyWLMӾQlVV:r3ۧ$ɓwd8ܲuێ'N"̸]ܜj[ZW]ꉓڕ!0Gܲmۢ%^`Sڿ_IqQ}Ce+lh><%%קhA0b^xKjP&:@$$b\'IK }rR"IJl} < |hT2xfx+VNt&', I e lHOfeNgRNv/t='WQabIq&دo՚Kӱ,a22m639femVkjh4𕦨q$ݮiZKKCgϜR%`DA^>d2pdggff8v N;Og cݖTaFqqq-}wuEӥUjkjWT<# Rzi3zdBl޹(J5Mx#9`43ҬVlNLLDP||\MlVkrrM-_g3/n}fWC-v]/LE4M;|/??|+gBQu~G5uzĄYQf]\sЕU{{_w^1Ħ.X}g^ +4cQ&]qrkX zjg/U|"o۱sϾ7|̌=E'{$ 9URwOKײ+7$ɒhR|Eݚ!@0B1 MiZGeHg^ϮX5,EQ7dMqBaĄ3CApEQEQ=XEQEQ=XEQEQ=XEQEQ=XEQEQ=XEQEQ=XEQEQ=XEQEQ=z ۟9}KÏHWt70B;.JmimӧN&enh$-Yş+Գ>vdEQ|~g)I/KU-^,Z#=NOQAUU~f/ZN}͢v݆aC&OCk]+F-~اɊ#ZVp8\Xo2vtvr,LNNLLecNh$+3r{</VYUTgrR҉I RS<׷3 (J]}snGQ}o7^iD;:~tzr[^@ 8rİ!OVV# Ý9f$&&\w`08 y9{Y,p$,KY3DENU5N<񎌌S+V4M|+<$K nAmooߺ}FS~ B{M2j50FF8r^Y| BeH +:fM[9j2#haAQ#f}md1dYILxDŽk]|;~2>1r0CWTVmڲ ر #F(/RYUy붴ԅ˝~ThiilݾG3gW]/4Q lٺԩӓhZb 9{ؽw>aXU}kOI=6x'i)dUU.[p؟xt#ݏ1ڼu5<:DEq|!mڲW6~3O=1fԈG)wnۼu1}AN9{iA(G;{打'C۷klii학i2>\85eYޱk7Aw聞7+m䤋6ʲZYYxr^j).*d&!>!(2>svǮn>B^iZA۶wekj뢒J*j벳Hk[AV+0Jz.dee\ܧ}4Fqޜ{сez׎MjaɩS|#+3y,${%ed+v{>_}CAXMlM` !OIaBZB@#3Eh1Vpy6cvrsKrϝEVjb_#әs|nNv8h4zL{t57jDͱẊDYssPMm$K-ɲ;enQ%We ƈXgr<E0<BPmy9Hk"+CYQ8#44 H|\`0]W^mdC&Ykʪ 6:cܯo,q׮uJ4EQyEaY!`X$IAUT 1DG4MeYz qGss)RS[& Bx@Q人4(! 3 1eE#+r8e744w&]?q/߸Q#EQ4Mjj]n_juLNfYX,\CMs"!Z,?} >Zs é)sf4pooE^}qj74caCO Y@cǗDA7!Bܳhɲ` 4{e23nkOH-f3BGyo}pQjjweef0뙧k?X|cP0Ʒ #fU7w%KW,^ded|zsgZt%@gDV,И#'`8*!4I-Y" 1,'2Veʝwر忾x_+O=>O=w{|O?'IRfF{ftn|%0`|93PR\4v~8v(Ɍ!‚֒XUsvv/t \]E nw([ܮ{^{]Mk ?^~I?B~}ޜ{uqk\o_6.4JQEQW!._LN]e6teW|fB(>-R6 rsh%5af52xRm]]0ꋸ*+_*&:~E!9 nb TqP@6-1Zn\AC!Kx Gpt ʃ,JHEQl`=8@4"WA[1>.&dPK$ȐXIRNIҥv 1_2A*SFب|bvK}! ڌ$i}L6* }͊Ja,φc~ĒS]]Q}^)h[]\]A@ jEY" <8v@n3tWA;\u3ĨI<`A%``I?[dg$Fs *dE)(&GV[k5>„5tMar@At+"S/[f=AcRvy^V&`bɜdmC^' !;v3zפ.W(\bR$ Ȑ69^zMo"J7X.wp[UFue+/00.XX6 OF>g&EPn3, 3K2u*HN-0+ۄ-LHEQâ!SQ Նq}O*6F5"iv.D`a#^ Z2zd<-)(-QvOFuu ZT;| *pS`UgVN󈤉8!q )φwr2#^iZ8^dQd 5DpQv~M~8% F|*xhv!SBGF#+`R`T hC3hK[ )2!&ǠdI+n+8Y1 *j22!6ym2\aJJN7p@ Ps pYGA"!pkpu4QAw>iNpM[.gmws "}M0>bZ%|0 "&&R^j)X|G\`O>m?@p>I8L E.YNE(;hj8E|zcF5&! Cj}L*BOx!aRcCH]IOA" RbR*LEmwHa mqqA *+9NUŒKFy0~&yY!;W\!u\b,tsi,ɂf`&hٔn9LSNV RWp-X!^r MNOҏY T>&e =ni^5&V32ٌplO P` uNY6 Ш^/cȝh+[Cf5.O5m n" {glAlwsۄ]fꆠ!0IeHl L9Sspڤx˵sd]:jC23 hVWAQ_'@XCz"B`f @Xu%aK/^A bWBjH!/`x^Y9g"8 K0CxG{Ն1` "I:s AML ~XV [\\X|ɟMf]' urLN 7+2}ҬWԍG.a@A=C2#C,A/ @x ~/۠v?(b2*t[Aa:1E,!}LJZZ^=gd"LIIճ!F^zQ 3CDȺpi >% CH;C 2vYn  ~yY8`O8Cb}Gm "ja5:KL=& e ѺNU6ܮJhpAaOح.ĤpH(p!5I+!V.aH\`΅p_"b`I/burru! kQ!pn`EӔf$F& >\Ef*B:3HN-3++9r:5پf@0X:5]bLzbAu[>E]N))"!@2tZlH"0NQjURD@2tWA}\D07#rTCن ,A;蓪*6"V!P`,,)nc+5aMuM>gupCm%F>ß1٫`+RA7FV !8"i@%hgVD B)oۖy.ݺA&XHD$rn$,h@Ag4~6 U$hq&`h2_m Qp=q耏)H%H%)c,:K!E’3hE&uiPB9xBwz80 kb!@Ƭ 1A 5F!"$(5QXCMQ"HSi\ %5G1!I jN}|rx"`U\d0WQCa % Ӽ n2ՄN *h(CHXPCap9+F Ul!-Q&"6UTUA66BM2G,,:`1#5a. #$ F`M,*jvNCDq@!nuNa|1 Oϼ.̮oZ".Ԯ-3wRecȇ-­.k󜑺0_8(9#5aKn$UJy+Y\Lk\o_669P ^Χjƫ6('J’KHxa`\h@lP%~eCg9I׹=^E!uˢEQEQ_BmF N4! :z#PWMXKpЍ؟|29k] zHQMǿ)p~)סOp+w=EQh!uB2B듢XTϋV4n4̢(ꫡc!mm;wF{(#Z-芆YE}Y4zx[m B׺,MƦ'c"/XTD"inn4:ꙺQϜv7Ƙaec1ֵ.EQ7:EitETMS%E"YUU%En-%ſً7l\nCݞ`(D^Q7`0 ˲,2 i(ʪwOI7yJ7Wrss**yA>%/]~{yWVfgK ѨԔdINH4|>UUEݧmHͦԔ~/z}oTTDQ;:]@3b1Mvv @p`m6 j)J~G4jZd2EQ2Lj[,fŢa& zKDW`I~4L!MӪO}dY8F=Uܽw<_>xa.~wtHd@`=3qg2xvعKe>c]`ђe.H2y"q]e8.W֖mlش㪢0}Jm;vޣȲj G#m|罄>ݛׯ깚̬ ^7WLF>4:|칮+ :};zh4;q9++!_)QU%IujF/HUG9ǍZ,0ngήݰWC&Ǐ;b6qcΜYqٳ;/wSo?h;׬ظb՚Ʀ&UQ z9Gv޽w)'wԩ]{".Z¹wNlVkniPVlV=3NJ]$ٽg]{:]܇oGA~޼9(zygEKwUS'OdY'0,#~w5wV4u?m4 o{5u@''%END"Y3F/߳wۜNjl>'?^vSˇecp+rBbdT-IB(/78EQVԗF;:;V5ě{Eю8ISӴ5k7D"o~8׻{^)*qֿ qoNEe.* ^>?Y3Mw?X0uʝ`WVjS=`cG Sw&'蕾\ie2R/0LsjV^PVj1O{{Axq֎ΎO6fX Jǚ~U)pE[ںrp{}'NV\؞:Qf)W֣< {܀;?(`0$hmkt7"|GgD"YQ CWfn9u8ΝJhT',wE,˲͛qY9cNRE} mNw_QՆT'I]446:ڪk4gBHD(756_E Vls9|XЮ"!P,(+UU=굲Xˇ ڰi˹Zɔ~~rlyXzt.-4ިx5b8roN(٬I[?nLe)3ͼ?qb#Gv !EqϾ#G #n;FC!UQUƉÆnؼe>`jjJBbB‚|UU]n7B(>.e +0 --vf,fef8x!)1q-C.iNׯoW;{s5{ hi)wNĄ9mw{ Iq (-xÛl8'0YY@pNt#G /).b칚]{1|A?-z ݷ}fqiJC=6vʭ^be~Y+f:yrSC 8zm^0ڻw]i?EQ_˲Ņ ?Z}qcGco=OQ33ӏ=ַOqB||g Ξ7mieA`HNm`"E؞S)"2|ƞ8q8v}JJ֮ۨjzRTn7V'[XUTU-\$r;jpբz྅xb7fTLA͞{I䤋B%$$55xIbB½sg7o+~_H0y]tι?eAa|ٳf8ɓ ~xcFzO:_˭\p@ <ϯJ)ӦN#Gmڲmƴi?ko@GQTPЕ ˰ϛoشh4ĽW^Ӵ]{v? eXt V8C 4p}~i,8xWVKrǍk_7kKtZ;w?Ǫ)wNHOJpU,eJiDzQEQX5!&Ocq,0!g?L Ve&1A#(FQ`0;vKOOߖ$aA=D _dYfY`wÏwl4z}S,))?pP4u:Ǐ{as5?F$YZ-}]HDӴvUUO>k>wZ[ƎӉ$iv݆"~,gϝnhh~MX]~CEe^ʲlYO<|䨪FEQ_( ,).$i=o~zzjQ)AJJ%Iڹs;/JS\+K$^xArg())D"7qʇ 233η88( 0 fG~0Bb/0?zr_`4}!cAQ%v:yD), &fKHt&=tWj\ !$ B,`,kZ/(pɞn!x/`>6bˇ0 c>'绯ɱlO ^FD"Hn `-LʁapxH$*+a?{+u:P}T4%'ğd|>‘0rٳ5//wOr;6lڼqӖ'&$=ur~}F ST56;RVdMӘ ;UU _dK$X^o0\.wy3nȐ_PX6ɓnϟ>ۮ{ejYb1/yC i7!@KTWu˒{]~}cm!dSX2;ww귿h`4;Y==h4v B!RT*72c#23WmU*JZXq Äw@߂;fkk7{Ͼ5_ktOh۹2v{zzge[m6Hawx{{+ ǃ'ƅ/ϩ_?ccc<R%KbB{AA|Z5Xy^*\}r8p8|}ReYɩKp9D"ѵn=in+\K'q8~%Xz[{!1'{ʜlӕ +<[f{{yY,V'֯[;04DSAa!FTOt:'&'Wdwu$o}FiHO^NTTTı㹗ZD"ѷş~ծ7`.x(+3CB?0?|B2$/(C;vqlqT AFjJD.;u2/f [PX,BQtKk4No~/=-5.niYY=ϩEXTVհ 'D"7t] Ə+ lN3(0ͬ6:?Ѩqݿmo_ծHtQbnٿd2t:Z-1 CZ>R$]ϵJqU\fUH$DM D"H$.11D"H$K$D",H$DKL D__R" 0,,4dQJwAeD"R qS33W"\^Qy'u_T*I_}ɩz]RBEVhѨ.j4?{Kbc.pH$LxX S 8jmk*}qW!(3ѯe,ǝ*)mljh4znmO>=ydD%9Nr{0xZHELNNMNN]|Mxp$"Ht9lީiL&︥K46[ww qfynY.gYt;pV:snOEeUFF`!,n?ƴԔ~Ƥ썷j4˳VȞk@dIlLtT$ݽ-ef|o_]}CPP`tddn~1Q1@MI~/,4$00$GFxKIIR(CC-bZXC ò2|=8>00hFzZSseSS Ǜ[Z yZjdr8-c:z<{yf[GGdM]7+,:%RSyĄZ(g׎₢eEGG$y *lcX{##"T*UTd$E}dŽe&w`@qf EѪS%#Doz&SܒXTHXhȒؘZzz|}KbcR)"aa1Q5uuM5|t: R[ZUEJCCV1 M;q Ng4CCC֬^i?YU]WX\Nꚺ8 "#j[/x^ <.nV}NAOp`egRBCK[H9f)#=r_mR>f𹰚e9ټ<+jJOOMMNr\N.ܝ˳RNDummxX(C11љi^^'r7_P(~*23# 0a!X3*Mӯ}7\Sa!H|ZmTD%<ϛM&UFNLN&$,0j4\MyE׭ʤ(jjj:K (IJe[z@V3,[tra{C#ŽRDvzzTYɩ)m6;aa*gŪU9l6?X^|ZaAt:4=88f~,ˠ(j0j |'׭Y?؉\ &HrfBqv~:AZIr\~ *˸ VINO8ܼl`4oy0 :oo/FBGw&l񉉦?__-HKPLe\ W*DG0PT\BԆuk''<fKOMH'n~wVF힜 &I21!aEv} jFqA ABNы;80,ɼƎna(J  f1 #~r[kjZ p`@ ϋ?ܼNYlv}7/W*~щܼkl۲e9Ͼ!$I|cAx>>38oֳ $4|=vԖMr׏~}})&HRp$IłR8B7v||o/LO<:zjjEPDN'ES=fI$adȨfH:~2`67g?p8q sC/.H$FCQT"piZ$qQ((xW(?*y>c}|r A=6m Z3l6_(EumݳϿ(SQ{{!,ē : 2"/*vlۢVn`fF }h4,a:R]S0LeUubBL*x ì6X>aoeNvRb>C1a<oSs f۶yS[p~ti\aw.M;qr뇆&&'nzӆ, ccc''C"ɂ[FFBC ;mqK2YpP`KkxhH0A6k%(J$˲ /PU* I2aY7^Dn^eU쬅b!0ıqZFT׿ HhX,Ϟş*)o" ;@*Y'2 V~'%A= \Q 0\\RRwCð7/Ji6 *5=X lLT*^D E"5Ąa6oڐD%f T( (T*vnVt$h!f (0 b644EGGھ회?T"Yl[Zv&4$dUzV&$*{z+*d2YbBju xy׬ZX bl3g.iljnlj?sAkVitYs}<$o.:^zPtd~|C$I}]p; ahl}ݪl6۫뿇^)Dk.-/*io`ƒҲe zU+x+>U"˶m$J` dU9(<1!>0 >.E5r\.bU՝II U5;oH;yE:n׎EJ iYzFNF042"BdԔЍ:~j\P(Q-"<\rۖMj$H{!nRo r^(s+ lN3(0ͬ6 jǞ)(An7ߡG?+"xMMM_-]"H`^W[_%.+(""|tlv_C[5L:NV H=s+m$d޾'~.'gMҕkѱW^{#%)q W:WWW).*?"tKN]ɣCP[{{Kk]D/."";7>UdD7:އCZ&- x\KۍFoRU9'\eVkYy:n?q2D$%~ꄪIW&P12"H$>mK.6H$D"h1]90 `bRD"Ht E CQ'˰ZxApN[6wD",e4O' H@_|)H$Kty=FA(edvσ?08=3#ð/ERKtZmXX09cc à(j j-Dk` XT{otY,2c>sBCCB|SSzyaGAl+88 F >xfF9LYPX$X"H$*Ktyݰgdfxdt~B!zIsݮ噅ŧp/+?AؘaVw H*45|փ>>>S^ۇ+vlۺtS% D"H$Xmhxxnrn_v!HINP^o0y[Tio8j aFolbYvvvr 3ZD"Ht}4,[WO_|=㿼 VY,=E;]V^~sR%L<AΊ/+ IS;8aey&FE`oz IDATa/D"òl[{GAQ TV< WV/قU]['Lk.V4=~RRm6_Ek) EQ/pYA?N"Hjjjmk\nwsku;ƛ!&w}CeH$g`p#Z_MU90LB[ aaA 0]g4=8T(x$,[c֧}` ۽kbyvn߲<3Dnɼ3ϭ^r- Fau;TT=}fwqi$-PSWko];uuܾuEv]_ziU–v{T*8w$NR^a'$D_;IN̘ CК: gf&S CBB?3qǎ_ֶӥk׬_t~2 EVhԷrA^#MLdf0_ ^<[_kniHO]TD<3s]]O=Žm[lёc[7ot=C#A^Fc?#ȣ=`Z_~S%svO~k*PFEF^cABCM~AQM]=0  yJrN 1BB].s3ss.+=5U&%%[6tz7[#JJʺٜaXhH`Ulv{_@]}f_ Ѩ2O.=uj0 mmrXsH$z8e9E +,qϲ"U`у4& :06>y{R?cVܢQNu14<"JNJPQ249)Aj*9gVqI,˴wtP$AQTskJ*;SBt_pA֭Y`]N0 D+Ɗꎎ7,h C1Eaya @0yO7" kWF /h4Rf/H$?5uu]mׯ y~vvEPUND"ѷT"Vt -U,ˎOLHNkZ.^0 319ApFU qq`AqD"쳳sb_Xy05=#|[tJ& wXR)IRC9ko8~2OӒ$E$@Pdf*  S(!BYf9:*B(AR7rDE,Ys=znYu .KXu(ɚ[I֨*J(jxdq$Ja;>pܕյ;mٵsk* IZvvGwOB :pﻷܟvj.D_[DV }Z::gb$&fjj#jl^!H ԴtL& Xv@PP7!X="s8Tn9xr঄8a3SeaAݰgyf\.(VJedDxP`ym)59_oTrӓW,ði)jfw|d6yo\艓' `^n6yK0컷|d^eU5 KbcccJEzZK$rZT*zAC/ ӵMLt {v/xɧYh4r^E"k*n^WPV~(A6[,nn;]VNR֭]D"$GJ83"#tZ- Aׯyjc<۷n:z?zo9D^Dz^F+.\~q{HHвU5>f7>H$I#N&4A| Ȉ۷DCӛh+|㌥KbJJJ{vlDRtCCGFx ٵs[lLT*ȑӥdp q-m!A~ӻpT*v岟#S̯OW}ZmYjK D  E"#STVB 0  VRx;NөѨׯ[˲s9^?2:j0+rZeZIBh0d2em6A:VTAde/Yk\.PhJMlv P2z#,4tu;29s]=EvcuB811Ѩy[4(0Pd'JNJL^?0ƛo?⒒@\><2c1{ۅfIeNɧ1oڸ>>:611a2yoٴ1,4H*"|ȅ (';i^|dKN\!119 PU8<2GGg%$,J$a!Mͭ>.{y\& w*jEvVRBE4NLNM̦$']? Htm,m><*,!h43Dtj{grk  ߹@csѣwܖPS[SV^A- C0s{l|b˦{]rƦqNx;8(}aSS7xCk[ǻt9!ڰnL*{<8~ۭ7o޸˯&'&L/zybrr`oN'W(ǕJ?SQ5_=m;<ϩӥYiZv`h`pttl)|tGG߷7T^k|baC~Lðeq|t  &YXTiٙҲ3^?#ǎwvuoXn-]E^\$\=DgGGGFDDEE dry`@@[[t\m1_gbMH$KtMMMٰ~B@84P(iv:kVH@F%t'fe(T{*ðZooGR|y+Y5nw[{;q'N.-g*#=-=-0Jd1%IѩrGW\ גI}=~"驩i__ RS8nw`QMNLH_ZP*mr,d/q=)I▾s}(zxdj8_PԼSE驟6&:*11>HhdYvrrj|brpp(7 /aE5[$.!JlegSSw{~M3Loooaa1Imm;op6q܋/klq2uUV1 0L_@VF_2!I=0^ymkWvegz_Eؘh65}6u0###! FcY^i 9.S:tm˦M,L5AdJ0]=CC(v`߰:on]}Ë^}?w(8SQUb5^޾υa١󟶶$ x\rotj%H|n~k( YPX\QYU`6~wRq6 9V6ۿo/a#Gi[6ma$#N,u;D<;U5R*òE%g{("{yzZ E Z"=5%0 (#[n> |xD".D".0hNpF&)JTPd28N)1 S*nR)t3d:].jfaJa(qBSR ( a\R0 s\ 0VPe4-dL%r9.[T$0AS= IDATDR1 x Xxp!(R( `$HloEj#A C:nbAD׃s. KTJR øb* x)ڃ2aMAV+)$'HN'F@R*)&I ZaT"]nD"yCK `8I HX=Bh];w,[oi1 c~j4=FhAnϭ8M ;,;AXjJSCAB,?EuXx B ,XIegggv{Bfk%ex"sb>_"## a>aaK˄A,z'􋚿\8ANgeuuȈ^vnwGnq_~uKp Ξw$sEx(6>$EENƧRfD ˎOO' (QV/]%wz{{,bAOLYdfTo~uծ*ڵybvvu @dxh8+޵cll4Moټ1+#}~L!I HaN\GFl6;DZr"xe窛r=~| mJ2<,`w8&&&d_ -m4u< MLN}ZyBzء{W.{llfqz?##]o/limia CW嬸ڕaxoikG׬^),iFt>>f3EQG;B!+!ݹ}+qK˛ZZrl+`uTVWSHp=0֛kfvOBIy>:z'"_ӧ;:SDmټQP8⒒.\-6·_ !zpOQqɱ'v  lnna9nP?/e ,VksskTdo[{G{gjP{GghHsU9 +;S17g5 i)*yjBBB2҄b zl4niwaNBC3`fvhx$IA/{yfڤ5zQZVR*m6PGgv746 0e6{FyY\YWDKlhtI qK=N?0^ 2n鐐` Ú[d2YaU+WTLtIDx 4M714m4zz,Vk²܂•9[6mDt a흝Edre;VdgoۺAD{o[;cg5;i#IGowҴMxmmɒ%RN78%.=BY撢UM. QanYj.Ή 2l6_jlZ;95m6|+:$j/,x{yMNN8Ct܅2))qdD508dۣ"BCVVV[ IrxoRQe0++86:] Ajkhj[k)$98844843S }``pXY1O֜-=}!A%EkK6ML;yѱ1hni+-` XLi8d|>_&TcSD C+V;{B{Gnoht`0>fff`Ok{KIMksK'APEUl0ű wܒ7`2=.!;""<<, a[|Ȉ𰐐f]jX{ծj?h.5:;/UvR*.^]ՖWOWk]iq2 5uU'f+-PY̖s˪k.r87zwjjUյ}מk2OL8cثIaZQYsY~ Ţ?r*rɩeh4 8>04i'HY>O"K⢂ G8l\~q,yjjl;\$fK 7F;q饥-7r(@W\XasIQnvWl6lUuuq,G@ ?ϡD,*.,X^VG&K BPdoTtm>rWkɤRS_b:L\lLVFzfF {߯pC~NV`|b__//}tlL"Ţ(Z\Xr>`}>twOn TTXR13=-8(ssIQpPJ$}O[Ccn'I 堛 7DG;qhAzj ^/+6n*)J,ڸasIQVFz[G` pdxxzj MMOֶܜ?ٹ9tW*b4b6nFQNTd⢢‚ɩih@FzjIy93ssky'Bm6Ҳdpwx}`vr3Ucc8P(?/@dm%kIMŅe_\ D`77Z0`0J~6Z6vR\^RqSIѺxdJ< RݼBCC6I$On..ڸ!An nܐ=4<]R9B]ŕ;5=m 5ťeJ=u\|llIQQaz٬^ܐ+ T9D,P {yxwwt NQQu y9c=p_]}# ?pEjd9xwbBBk0 GFW^\]^(wNG(H Ӭ]%1 á!+l6#FRퟯa eu_"|#` fa8tSSgΕTz~mUD"dv;f<0,/ڗ]b㚿ޫɤO<\NӔlik﨨6_nʺb (}mVϡ%61,s4MG9999 7f?^_ZVGjIF٬YYr,  ?_gꪶKK6iVV<=G]u ڑ@''AXh QYY  aXt//0 % H> :z0А`Z;:\Ąx`YY)-ho33 ^,4MWWprrolj!I1i՚L]\Y,N7Z.<,ak~al,՗!  %0 A\18(Î*qGѫ@PίeX4h'''ؘd|/`ރPvTyyzT"a#lwwA~uNOjiye{{Vi۳M,$`2}& sX‘'$V '.띄W6ʫ-,.*۶mGY(cwtufI*pعcX$rrJ$b'l6 HSNNN0 ?_NDGT&1H$DrlQZmA,Y,( HׯEDž GQf$!r  O~tCb'NkƦ8qvvvqqnniwuqj{noljxh?I&Y"۬62NB\og'2J' F8@s8ðhiHG;.E9]wݕa@6r}r 7RR\|rjzuUHx`? XWt#qN4,Ntn?)i@?<<@.XSPǯ$Ʀ޾HO=u|\LCk|G Pp*nW\ʂX3[6Kn*.O]e ps! N9=NjѾ;\3p8ȈNc'70ARTeń:G CTS^YUXYFԷq1Jwr`ju ux 3|~o_߇7 wm ѾV50 `;==/~~F,..+I<=FXɵ`z0|mXM. F̆/MW@ĚSP(D?{ r 8y](c6M.OJJ(vSI%(HÈjT$..-9VVWGTa!(^US٥P*A ESskGg녲k*(p&OwwwƦ̌4`8 ;yOwn*.~Uյ7L\6U`@{E^px|g-N?sBNvK(+r y껏0"(%9iC۷niio'I*W'K=pRZ}==mV$̥ JNZ708t9Nye>APjrS s8HYy#> (?u6QmnmP*NvBL&0ME F)N9}(j6[wYX.  |ױ}wߵf +>l1s0eoo{s/. l6~\u5YY]e)bA߱E*/.-#ȶry\'gf6;,4}`pj]bŽm[K<.7"'yo͖Ԕ陙μ|iNW][;84`}Mӛ7WU7t:]zZbY[Z^r8=`[GǫaXzZܼNHDurQ455Y{{@*n(>rcIupqqqvW54-/k|%RTDxcssKK5k.z+ݕVupxhcnͻ8bGEFD/;qvaq9d2<}1=;;wAO>SYU308.1aKI1ݕJϝ/ؔ-0 DBz|ikk{B\A9YVuii9;r? ;{|EU5f$'ywv>s@1 w#BܱAApq<%if oe0 xUUE?_R SSsklLԑ'0߲$(0ð‚ LJ%agΝ'`BCǏ?hx<^lL7 e/ x(_wllԙsG\Z^zg?pW$3g;h3J<;;w܅ޒZa}Ҳ@lL7g_aXTԔdOѱ1A=Nh CU~>ٙ+++GE߳dސkg^ѲڪڹѱK)7EǷl*qrLNTVLNM{yzʤɩǡ1v뻠|rj_翲27fn,F>Hw\nroU ǃN>}if ug~󘘨 ySSӣczATzFD"9z_{( Aw;ŗq{|M&}\,LfT]]+{ѱ3gА~}rW_{Æa!!?x{?ٳkbn3\_ή?޿Ղb>f[ IDAT;ߑwݹ-#-?4M/..w $(pMZ[ o-.-{<YY靝)hrqq Zp^?Fnojn `2HКfFeٹ2`uU[VQT"b0ff(`10000|&pq|` `08>k4+ͧjkCR^UHpe  {m66~u#v}brjb4ꮘpd`````<n*pa, `10000000`(X _SAf999ea`````0 ںɩ錴8ggg-۶nq(ZmCCmV NuvgT}q6;)i]lL4 [;b ]6n]{~zf6+#=>.A᳠(j|bB^IMI$ɾ];w*7K"ImD5 ?Dk[C0o9&sͭm0 #l$ omR@DPݥy EIIuN9 Anr-%AA, 033{܅JR?ѬH%$_p luUЗ I;&÷z~věoWRXq+k'&&S<==wVVW eC8A( EGEܳ4:~C1䮮.?{_,O,--06L6;;WQU384l'!A[6dXHb@$pή+ť>ܻ{+++Eyzx| WVWz_%vgXf)X6 Ue7^lq[]qЌ_caO~ͷُmVV^Q)=-`} ͊%XȃQU}w?x_|'&k.^D0T9r<4$|iHOvr9z˫`C~iY#C߹qqibr];?T*7Fw=v{λ~߳u׋Dž޾Gl謬LFgag]himrG~o!A qqM8Afef0 DYWƂ\.l6xxg~K;S$I~^>axUD7|U0F>=KCd{GV-gfvnB=wrQſ?}eff$bFQᥗv bT&f//ٟ ۿcx fþľ WU,W  |BٹeI%7~O&m\#/Of uםlnmHKɤ5uf'OOKZuG2MsʿFT;v˟Zݮ8d>>E?iQ[68Ta_oqB_vb$:nt|<)1bLLMK$|EQ/a@fzZrrҡGx;lX_ς@xx䔋s\l+z/๻+IIJ~ovprJ :2"'+bl|s}Ą>Rm.)Z]lN |bܸ!f_|oNJ$ŅŅzaϾ^-,cf.Y~tlW_>tZJYZV^E؉)TvtMŅs9d:zdm]=f j4ں;O~/|b9_Z^Z^aZSAΜ03;?_X/KHTDxݥHO=ulUMh|:233{4zIVfgΑ CG.k4ᡡ߻sphM^6=-d24=<<]};tuFD}>>j{utvIQod:~OäĄhnmkizKI'A 7+ *}wsk bm)) H.w}7/Wl.)z=?{7{} 33?SfԙsյF1=-%@Q' b]]a6,H?سb8t<49 % MwP"c8=::zzb}7뚎0eg]X[7l\ X[> #cwyX$޻K \-,XbP,͖WҷaU,h0} aI (*X(Ho7EQ/R佷^V>r/LMM?R*fھ`+~CMSKo H\LL|\;~'>. 4M/,.ϮѱYGs )J,?tl6;*2baa`4 sÆ'OMLM%/`M\\lTTD_Ia]=6Bi{r߁CssfKWOo \X,~5l6 i`ٸ} J RV^)bbgܕ& `H$?}{?x`0<=wAE~핉ɩG ̌/,#4++. ~7G q"H 4]US@P/h;p˖f M8DEѱRgg>yG?#\.7 b9d,feq<.oY>~-?tб} QKf AlSk^|gw޺K >>B';m=pG+X@Qԓ?r` BP&|~XHH{W]ts;|~HpPwoBͶB") Mfi#NGTΜKMN^3"+ ^_|E+3s 1QQ  ?Pgp#IMN~s2ssJ*rsfjT&wuqywzfexܔ??C|UK&y{y~6/ EQ_oUHpIآT(YmXe(ggYDxoiHd4-W[JiYEGgDjJ>Dlp8\. HN^'trr)IKĢ뎄M0DRd'OHlXpP A::7c8. GT)II$EmVٮ7=0iX,bAkzD,faM \.jA( ,(gf9p j5ۥ8m[7>rj |Dl6[/O~O~KY?}UT;p8!.l46p(p se?p_dDxWwzaa :adJOKE8ڈA ##;nijnYkvuu]]jMpPc`f%X >Ba7!c7"3OLNq\xz^~Hl6p E0蚋u\P4I8I\u6 $$?A0  iǻ } s D?>M-եlx{yxJwHLZ ;4A͆%Ox\cf E$IVTUwuX,ZCޣ :l8"eZ.wE9H,fH$$]] ]ٕW3vN9(g͸0L:^$ F{qh a4E7|E>^߇|q\'GTϜ#jz@XUsr|}q(ű(G "۹`N Uf%<4:AC&&&u:4M Pm:~u654\(+m>\.Z^Y]!_.weXW6-m6EQcc>0z>zo &:J$~g:s&'[2S +MS33=qzfm[[Zۮp`xX(Σcc8= 7t@%۱?ŵƦ yK)Dg]Wv%&*2';ٙpi/H$Hcð)A P>oX8q XfE pC鹲|aqQYI%]a!0 |ɬAJ|B!QD";V7"vbc6S+X,3IC*\o3qVs>=5s'OeX 6mM HpP`EeuOOߺxV m.)p8!AA5= q0=4<24< \.weubl6y::+M&3 ﱇd2<}FYpR⍨TЁ0 9~bl`1|ȨJg69a87;rQ*aAA9YgΞwW*kfKyeufz_uŷ}sI;j.MMONWLIbwRJ"M t.-߱ 6c5/G?!>M~ *n5U,Fo88*_ I: kT*0 ɤ4M/S3I ]] 7~ٍnD7 $Uss.Αrg_ f~BnrBf2Z:rsv4EQAssc~33)zA5:j0". `0'&VVVzz֞abrJ? 0LUUgeΙ0>5=37?ORq1  K%T200h4 exXhTfsTTD,_X\$IJ$tk{33ࢨǡ dRiPPlXZZx} :<=HNH(V*ugGV,EQp{|rD" <Aahnt $t:ݿ_ĕjNkoB,'EQ##h[%jqn%`13k 7D'aR>aU_~-/7nȏs IDATղ|}a,D"*z~NA،\Ld/pa,,+ _ |}<^VfU(& . h&" SSnٴmܜ,gX2s>x+?I@id2SWD94-Jl.Y85 Qn,kSqaltMQ4M"kYװmZ0 oۺsVpǡN)ܻ{sqaLL4c10000|'wKNZwQG@KI͎srrRTC qKήu6m[GFblRCdLL]}w2|~oo_cS nge'&Y֞+ xyK M1E7o*>}͆|}cb[ZwޱuvnE9Z "I*i]bjJ2,u 30000|]a,X 7 fg4mX1$&ged P:61)zz\S1QbHo,X^US 6{OLNlJ-KWwHZZ#^#qaYE+ٱ}BQU]rs ]Ys$Iz{{bq~~Nٙijj;E"C߹|iYo_ru8o9poj>?0 ñ13ssͭCC,e0+jt%tPY]?Aqj$陙ֶB!ł+[[-VKpPZS'?/=}}-(ٰZmAlLܬD,HX,ꪪ%\czzwY/^Wo߰)BVrfZݱ'O=.1-. NccV @Q#3L4Ei4+d'>ʖE"M%)I"H&U8fÜ>Y(I=k BHOOMF'>UuE VVU_Un+*,q|fvn||byYMQ}9 #ΰmkqByGw):t=|Gj/^2MWl200|XM- MZiDbmlnnljju4RIZJrFZ*׶pLtTLt9V;]zڱ}``$u_B~f_/<%vgXf)X&ytt\V(! ۬V5(0XbhjzEQ_߅%VG^7,A~ޟl WVWy\^@\nnn1.vr`h8,,wmG$T8{zz {gwM>>1%r‚laf j|?0ՅZntlLALQvD2[,2,88I 0my9 LJe2L*pWTc24:*rY0b(&: bU+GD, l h$I*&:*,$xXhmpbðN8Fm6[iy,EQGHMNJt;qLݥzyyl1:svjjz]bA+jF~Kk`;y*+3=)133fHRy<08}Z,+,I';WZvu7ʪڴԤLinm=tT"NMOK NJ%ȈX$斶a'ɩY'ktcc^rW=vv޻g/J8Ј p( ^w|g^:{MB^iǎ؉{s8m0^DQA ǁ;c~μۛy;TQV   q(ncY f:%bT*u8/\\t oM. 7 ͋^ĩ5*t̜6 ~(R -5Y,֩TI26HW.GQk9kHI"d2?ZTzĩK JrMVT(P(t٦rЀdy`ŵfxho,׻g߁ܜܜo 4MM*P :ijj;qms7XS{vJGgW۽ev@L@s篽ҥKٻ}pb˽iݲ{k?( BMA؊@ sN/ppSM!U/*8pL~sUgr/_V)Ɔivl||R d~׋'?b_@vfFkk[fC AyInX*%4p&3ACČ#NT@ :kt>oٶ]"dҶs2=:?olZ}]B黻{j~UgE"< Or\ֶG)P02{ XkϾP(R!:|eHӴ62bO_fՊb APTdeKVXpz{n'.6fꕅc[gN+>oؠ\5+_hoh7 }e[IukV͞=`0ut.BXbiShla_@ww II+/[~bi2[.+';+QQfeK/w:]`pK׭Y5kƴoߡG4MAL+ǍizxX_[0oάu*,slg p:5+/\7o׮?w6a}%;떖DFjV\{1T|>vȑ#,7gJd}.J®A"5vT&_MBQT6]$yiǻ[ ΜO+֎n զ$?:JyukV-Y  ^o&be3PS^Vj}ܜ+/Y͘6ȈdLX>o߹'1!~˖.a|ysoo_u޸9l 3MMJL RCÙsVHWwwt69%9 EQ\xZm@$f \&df<ۻ#GԔdEQ%'X@`jJRKkkm]ݘzv@JKM!IJ"\.BD$EQ###={8|\-[ŚRZr?HRΟ3Y,VX}nh07PURMjuX9p8~M[B$i۵g߼9x<bT ry8o 4MGj"DQ JH+&כ/@ aX0Da HD_rq8"B!"D`l6(k6n-z}kwgm]AFb FcXQ8R!ҍæ)AƄ#lG"h` ;JCdj'EQEEQF\2+3#S#F?a94-JIbAvtcA^;wiI ;!J($a5czlL4X, fV_;f Mf㉏MJLX`l#YjbQ;v ݽ}}qqqq[0%.6&9)QT|ÿR!/+->vit \nkMNJT(ȷ`0hNNJL HRav:[ $`0~]Qo][) f54npFFƄw̨V 0 XCGv?XQ>| yfpl(RrBawOjs8;|6ðs. $Ibs*MkX,.c8]..;nla$MfQ+DWpXfzAR-VksKkZJ p8 MQQ)ɑ5@TgWnr8Rdْ %?+9YII<aP#A,6Kr7lbrO?sg/t:>ܳѧ!B˗q\(5e /zAT4IY+'L(X[k>ϧ_!FE\샍qSzZ˥R Bcssl6 #"(lY APVfݾso?ϼw_+r|%|z!bt,GZ}ǟ~fG/SSyxHlLO^z!4s[?޳8tт~AqA߽w/{58%9I<ޢs\io$W'{d?<o?qq+[)Q@D$ ry'<}IYZq?iHF J$9YYzCvVfx{s/zhm- O վ?I90`nw\ZFv}gzMaAQhz{ze.Ni鬌|tӦ+njRc?0wԔ5+)JX,\.be/AMm~'ɾnbMݿ|;>Mh4-]|Ź,m,߲/ÉB^^p88"B^_PģW9v:F,Vn|ۯBWC300|{)l3 E/YlDHx$r465wtv9]%mY|s,>ϘV~oZ}7 xRjJr Pq{`ޜ煂A#ɦO-j>_ffFiI a͟;;5%Ej4j1q|p$b2D"xϗ4q@n7AOC@W᫄bC>uz箽Yb````FbSl]=A`H3L$Bٳf9zPE婪$F9vPEeEc x\H([XpjjNehN3>.ðܜlׯV)32ҏ;~LLHDZq'O>ytuj8}F[ {z-L&Zh\.6X, ]El6 b1K A(*rrH`ZFPD* GQh( ^+33C!O0Td2YFFڳO?E$UJx OxUgo|0U] rB 0 Ýt:Nr22.^='@ fa P $Izo'0 ("ֶqEyy9*Z<OWWGQԅ5-ɉ g͠(ȱiѵJ`10000fN!2#5TFEv~/,_XaXϧh4=8y(X,L 9T*U~^n>s\{j>=|d` -dX,*JP 99 x^av>fN HNgN 'N;{3O??_bs>c0J$}h߃CC{1mjI4M]z}~ Ʀ 55n@P0N)Kkj냁d6ع{A)J]ci10000;|sOrgvqNr.-)pfzuς9ɝaNrgPT9ٙuvnpou`y3g}3<^oOv~5Kp+|Nwt^{wK[bq8؅Qyټ;1 EQCM]'I]ZWhPfF:QmNNHC?:t|>vg sz)L+-.ɤW$*!!t$LOWIIN H-U .F !BC͙ !bXw[@;hŒ:ktASJOvZ`T*3 IDAT`$98#IrY`r%3M?  ʣ::Q nr >DJ7x!p^navMˁaXRIg``$Ϟ;__hko|~y beOvdkX|pT.99EcǚLNJeBBT"ԪR!.\/;y|FS+X$I65h0%''\L*\ݸihWZ<`ۘg~`p}ڨ '&0loq[{Dr18B\Xx-YCP\\jἹ.xΊ!(.&>8UuxA[._3y9XClvVC#G8 SRvnza=YY{S @ i󶚺z1mj Nk';2YݥْxŽ}l vޓYpQq//+UTw} wKvc钅ɉ0 '%w8dтzӵt¤cZie>S^V ~w)к5@\N1ju}N.)HLgq1/|fϷ{y:!!~jj8>}ZyiD˽uN_SWO˖]jjpO:rx 3{Fye%&8yox=Ugq0ljyYyYݽwH(ljnq{a:*XVu4Ahڽ@A~n~~'w}u>9g֌K?}%NOKPS>=22qfmTOڳwjor~ӗi@=^ 'M _Q{ 7Dk(rEJrbSsˌiSzm6JRRzqwκQ,MMINMInkoߵgߘWz[D,ڲ_zjپc33x4>.^[L*))0wb-A,XlPp.Bq9ԔaȈ;]uvͪ*RRJqD,LߎWC~l| ~GBq%ǥDj4 =zhPi"&F x<>20x8orLqﯨ<:{ eRD"iimP45g֌8B1bl.+Q%HbՊ-7V% |~c+0  k P+G$ 7-HMIɤڨHbNT:ktNPp9$GΜ1 `T,I[.(4q|df\aM)b%qh:`d|>8N t==7-%c'N"""#5 M Ξ!Yl$zk0 48=KӴ_4( c].7 >oq8HR0[$9bKNa 0=A`0gSUU԰ 8h\ _W< | @4My<^$ϻƦi@typfH0EQ(vL.-|>t:}>$ed2)qIQVM*@Wn4 Au~  x<*`4aXg^NN r.</Iy} mYiI8 qaa9.4}M=;w{^c?y:*RXGć>qy44M/* WQPU(㊊\Zis8p Hed='ë@f$) [.PMR$~ H5]+aXbB ;:2`2ð 5]]i0 tuuM;ҹ\.ok-qRQ0&o87<WnEDx$I A8*28Ξ޾`0(J*ɤ:0zCa\.JHJ$!l4M8xng`pQ NJDBJEc ς R PEQfw3mY0 O.)0,1>LάtA;&(f;@ #Hk2`v p(մuڻ3gq0 !( CP(PT+_6h, `tύJEQPhljv8?z9b ϻ^7&Z+IIT3M\ZBP(DQ=n)G,zz;J$>+?Ar||C+5 P (,# ͟{b%\jhӧN̰>vT[{IQCC1ڱ1cuz'[! 1$rC4ϛ;Z SJOUݻ4*(:}j&"aXo0d%j2 3g]Xb}5u4MB+xB &765,R o޺$└dr;Μ E[Y@ctðhiKKGνDnNА4CC:mTܜAĩS4t:6j\Q!IMNP[m..?pm(N8؉q^evz6;3Sub͎]{JK&-]ly|MI܂b I3$IBamTX$ Ϝ1 EΛ[}D`BdRo O~"k9\W"lW7&q\RACTZV0 J@ydY,mTESN$I\`Yq0 \.EڨĄx`b|rlx^8 HjrRLL];rϙ}tYv]{+*$Y4P76&5%$I S6@l`FEEI `koq7@X(7ZѬ^VfFirmٺ= ~GPTg?r9.q| E=G|Ξ3kfkkAB0+3c+;wά7FqfٳgNOINA&B_쥗_Ie?|g~C Vy*jr1C0n_oWkWn5V|AM=œ>s{^Ep=~`vw4m4xC!B$,e(zi8Ee%+/{g{Phhh骰}Puv5 AA(| "׏ZzuNƑVV=ȃn z!;+#?/nͅQQ ɪD$>l6K*^mΜp$DGWdJbdؔXP0&lꞞ>a 'Yz|=ޭR?J|oO=R/s'd`````)($wko/zˍ|~GWi: y^$=o~Ѥ/G0ܳw1MӁ`v2M$Iz`0>޲6ָxGZT(߮X,T0f+?ѵ 8sܥF\Mdh&⺋( 6}g`,h&{ A4MWyG?Yo__Gg~kܳ@ ^;fR>=Pk$ [@0x4ci: Ѯh&HB!߿? _ _oRM z}^7\s$ں^w="**__WI4E}(aᢘ c_;r;M}}fhlߢ뻺zJK&}z /l|BЇm~K'K_0l6{|'N~:pL??/nJkX|NMN4$E) +4IR<.gd6MG( 0z].hPr)v]JaEt~?ARdn/JBFQT$,"D8`0q\(@0 \NI<4MW~3lWTzއ11$EmܴY.> !उinng߁޾`(V)/7~\ѝo٧gϝII EEO>->h/-6ǿAIQ'Nf<ч}à(zԭ .54E"LӔX$RG16 hPU#v{Jrm/ ǻ{z3ohBPB.0v_\&>aP(p0LRD`(44 Fin^mϗ-:Z{V&?٥T(#"kVf͘>U,t۶`XFZ.:|J3fϜBDtY}M 7w#Y,g:S})e\7 9[}B AV,FEAdYWm}U wpbqLLtF L:WRS|^>{hqmDRI%bbZ I%QQgXo<7RrC`|\jyGzx7m)EQVd6DX IRF  "BMdo_\.|uͱ1"EQT*>++362r`q1Q<6 #J\~5@ hal٥V`/Z4088:>z`(%9 !rjJATx<ㅈJXl pr8$IZ,VV𴶶ܽ7?/<NgDzzֶ{٧16vwO=\Bή̌$IbX  uy<("5O>K?=f;TQII 8t#v; JB&9FQnw4%"Ԫj3[,$IUJ\#»TJ657l6 B.'IRo0PrzOB V ^LW"FSm}Èݾ`M MbhY|/\0a=֮9wfpph쬊G~hm^N6EQ_bz\.FE\n!rX!Arp X*7 +J'(Jp oH;td6#0B#ԪO fO_xJ'Mr8v\RS񉓧/54"BE's}rcsloɢ!Õf%?08xea˶-[rcؘa!6&zG?ٮO69{ȱ)`h9~nテ$E-Ξp]jh,/+eYV`4fl##y/~SNGRBBo_T>S}ccEegfN.-v:˫8 6ۚPGQx3O+ IDAT\ =ph4x!IY.@ɤGOLIInhjR)KxRGgױ4M$'-^80GX pLv¸"OR\|۸iSUZ1lX[zieu^y%\Qyƺ>3{f\l,MQ}qye=kmo?\ytxX&J4LזC,*,X[}BU=P(hiI 4MeKM\ޱg߁ARԓ>rk?ermL~nCcP(fS3Ӭf(p8%M0 W޲mGttTFZZB|A;> J6;7HݑPS}Ɍ4P8}Z9 j򳺅SnσkW?hǮ=?~wwg6[J'fe#0/9ηyƢ)[A//F>޲Բc}iݽ2 afP]4\FE|nͪ/]:|VFFFPOmp!n[!]y7m۬6\.^MbZmԅ/$#G4dRyPS6|Jbb ɉy9B`ݚU$IgZhd޲mǏsq/[r7\B& 7GcV8&|خ9}{n\p II"_~+&&ze#vǦ[TO+v{0՟\}W ǣ"#?'x!IR}nc9ٟS knoln;3 rOU˵l㋊?U JŘ<E(.+-ERS1r0 \RbW7K&M`X8W(V sfLڳwa9rs<=k4=sT6)NKMICj  MWb"5CRbIQSLq\W˭r!)1`L~k[ˤ4M{<`tP.^?@EfE\ӧ:nl]wwOVfFffzB|C4MQ`(~^o Ij'Ғx n ]{P bEQ&I% !0aw=\.WGWBX@ +JÆ/_*`  AЄqEey9z!RQ*,:~?kzh  X,38+2Y"0rM&sgWש3/8ֶ@ rK&Mh>  ؅ nXj*/'Qqrj22Pu:=0 ^(DC-[KS&]W&ae%, ܜHbcY11YV\FQԬ06i1a(..bm] 4Mj*Bflݾs:g\ pwm{)ҕL o.8عko^nvjJMM¸n~{`ǻBr"#lVjJq7up{<zVhh*5%f$I ~w} ޾MSEc UJuw_@h-@VFzx' BU*LAy>` @QT1'lðpNEMOMܜlK&z@b4ҡwɄM"`i:< Cp > 4 #0 $u\~n+6_p8RSoT?0lb0C"B\v?)4-[F c'N]y-HBa6[d2Jbx|<'TTP2YWWunw88E$!6U\_(62c0]iZ {^ äiիOLH`{mTZq>.|0P(>{ZP (-5%l: eARo 0 Ov:zT^^nxi  90 ]`v{<}`a$ɫs6P(LOMecl@Lv0 QMzj*i)<.@ݎaᵷ@APx8 (K APRbbD qxğ?!!F   )4|eD8~!;8Ezケc\pƘ IH! MBBcc܋d5[z]Ww{?>dqA>3;33A@q #0˲|s`jtK3ӛfooM3C!Z-A4h6[x#3G`k184)-q66&KHMN 4'"q  0ٺa M%eiJ  sK*bJEheY"ɉ Dhm AII5##mEYSS[~`caX"8iI*JĒ.,d2Yzjn/]hazj FQtB|bW)6 ~՟p:ϝʤ#ʬ1Ah_h6vʪ+4,WX,ry\lc']$o2.aVoo6455=5=?kj=[]|GOOOOOMM=ss.GFTJ108˲LL¯}۽>(egff<4Mwt* 0-zW(\N!WD~esgww0&99)17';55&DfYhaFz7AQQUgM&MbnLf644lw88CQnw(r ErQS)UqlltVVUg/<"(""܅ :a78(,ZPjG ôw `8"2|E9s=--mvLFQN"L6<225=mNp!\,$OMOd/](+3}rohho."T*6TYjd  TS0 v\\3 )IbqdeK#OX.,?]յt"1Adij8Y<3M/\l _{lh%qfx EE"d$=-85ť4_Oٓ4MSEQ힜D`xaAJd9hrRXk[GlL4Ab85%idΚۈ_Mn a qquOo~k۷m=xȳ?ۺw*'3#-5% _&%)1:*ɧ~t{׮YOx{{%%$x̞<0w߽gwCccw$(=[]vSW)6AukV[wfZ==mȗڸa<ʤ髌]KVk ^yd)//ͪw$'̳;YEG-0'Og%Gƿlvgha}?>YTro\\.FEӿH-JE g Dnj1 koZiZwu\$']f8wvb8Ʊt]mvMFw>7rq o'zrSaanٔxMٟa˖喟>?ZjDSs 0R4=-eS.^hZ s}[NJojn/Dy9ْ90a}o^pȥ>9|m.ظysN@te,ŗLxŠ _?%f/|NVfRbS?%r?T*]xA=ޏ?v yw_-(xݝY^;`IMNpq[6m8rGvGpp7hTd !>n G}O>#wƠ-œOE@bBU5>E b z{yLtɉ<#>oO=$n*ھ#6\'Wj0m2Y֐9e/[ȕ`YcQg dm) CQhfYAQ 1 i dctja(i~B(YeXƟD L40"ȓ)/M8`CQaxaOU۟~wcH ˲|UxiQh3 /{9hcYJ+09?훷B^ +k!>>td2ϷDq1;mQ* B&8qbKrM{[Q|_'d  .oJ ð, A>?aE/9zRFaYccu9#A>}aK' I4)|_!# 4@(FaaA.O4 r,˛RyF+vY|a==dfW;AԜEx N/燻߹GOeٳյ "2 Wx_""<PK qa3a/ 0 4r(z a?23?<7D8T@떽<|j|[f2wL&{ysv#"(/EHڹF27 adEgʹwz^6\3I^[/ IGf%˿3  O23Sz\~d,fBg*?/0)Grӵ>]. >]|zaCv%Y a(g.cNGPyO W<_WLO A ?S2AaVfs?̢qQ3[3Srub >?#NN'-PyYx-]<ӽ냧Fג {2R܊<ԓ-mW\ Ab*O 8NWe, Y3X7 Lrr7M۬W9! p[ SqΛN  Xr0r\.ER*U@$_*x,?v  L@.v=X@VEQD#,/` HN0 /qW[ApD3,?` 0s: "anN@QT6KB`do_ec%B 1OWHϷ\ץ`)r~xi+hW)+aqP@@ڸFai\++޾>b=Ǚ͖yNeY^t8f3 32:23xeKЮnS>KZmU5W=ӑ(GMm}A~A>?nT*a!Ge fJ_|r MMfeKTJ p͖ `2[[iҸʳMTEY֫Ͻ`465i>266q\pPPFz[tvc#0 Mu2/Ad.-XQyZ6[U+Kk.pA`  jq\LLtt!͖#ǎ+uk.Lq\um]ggwpsj?Z㺺{ZZvD"D/{ѱ՚S0 *~ xٙϽTvhjnic&00 +#CR^26,R*NE13,Iv*3eY`@n$wE'&K*Lpc.qEŧ>+d>Qtl5D"zͷM&;k ܜ̝7޺;oktz746b*[wb546cFō-{АĄ? f M5a~n CGr}ݝ&L&s9]O՝(l >`,8ֻ;SSŴ!Idh|gn,Jv_CМC1 [*~ajꚚ[}/c'({Dne #G_D"ikxo7>zb#m=4ga68%9*uoƖEzO%AҲZVH.v24=42t"USwNTl޸!4DK3LSs3Uf9&:pZ[tY"!!SL7oZھ20 3%ekVaxdt7 ݴa\.kni=wc_ MgZָؘMqgB쎪ڟ?ǿ  ׯMHG`vW֮Zqs'7nXCpOo{ޯ+;zDDxxvV`xd]?Vט͖֑nwesNgrR瞜 "#2ӎ=|qϾ4MW<ÁkmI$ de[mc'TJECc3 ëV,oikKNLXl72 IDAT9ںz-[ڛo妥k&&&,Z(IODĂ,Y\RVZ;(΄aS~[6dU)UK/LINZGOyi4mmӦukV  BkoO,]?  ܲPP8EjB1Q5֭wvHV83-GR-]ӕV~pdg.^X vaXhȖMra9v6,4ud2ŋR;z4ƩN xy-kitT$ N#]snT^S[(Œk3 .66 _:9)A|l͚+/\l]A|罪{z>&:-ju:ͅMWV֑N2++ceQ4rqj*8(Po0ML;u QX~UgNg_@GW7e5!!q;սq:$?>pvTdDӨUaa!"HHv˦B vCQаXL$'' onNvUu رqmpЦ>>xeYhhl*.)Tȱhm[7/\PC;߳j卩[${p C$DDdO_68(**0EBBQ MEuuLt|"60#}g*.,ȿ-1|񉃇0 H{hv?_}oڰ.))Mkk뛚Z֬^ȋ/WL ZZZ _w/ ߻onqD)8j_ظa]VfFyř nņLINddl|⣏Im[7/,ȗH%?p`hx¨cOvtv[68t$ɲ ɼpÆuk! "%))R-V녆5VU*e2YJrdeKi$!o//~%]? 6+*.OJL Ķ7aXT.^X\Z^1<2jX[VXR*i):njzvGFFl*jOw\ Nv`lٴaZ>qxdd]]ݛ7n(\633]Ўq1Qmmm<:6xm8 A q/\$I/6& \`Yvph(91ac MG`YvttPUU]^Qaڳ5'BwKnNvNvD"yh%s-gEUJ˲eYmpPDxŲlpP`NNV~nI 3Ӻ{{iniJ.HJJ\rS%Ax{+F-ʲ33 s{z)lRD&yr51Qy)I+Uqc[0LCc‚U>veaͶb] qI !!ƄܩZ?IvR Kvh4*L,/AjliR]]S 6;.JWZT*f*XrLTƠ(zѭ[6%%&dge56MٙI l6eK2ӤR`+H$IOK+ϛh^liWwȨn@؉IJLH hhln s2 w'%&ģrԖ I 9 @E R22: xxxؤHҸooZ$dT(X%nPTD]#Ek(\.שҲ  s\nrddCȈpEB2shf(JMQ|^Y21! /Eў>O0^yzSĄfudl$ǎwĄ(R#%Z$/'$/nnm g^^jvmvBIO ðF68KKIHMNZjʼnbO4MJIJ)80LGgAK-2mpaq1Aw@v{q֔,NthԫV,H$ZT*ʤRð øHDSIfcg@800-ΝnJ%(J$I!_:85T^ZdRa__Ԕ')eXN*:uK4m2BT)gX}8FfsIX `b) /yڬ RpM3Ӵv0qA]]ӫWd2;_7ݴa= AUTaegq\75l6N;%9RcXtTbu8_0, }%&05j2-.A!&DȣcƲD, [7o w{Vf3' JQ1 AAnGPIgBFĤgC˰9YYd93_Wwb۷eRFce}f(?B@$J0il4GOy-_l6; IO}Fߥހo0 ND",kX b1aH[w,kXKW. ~:'vj{>`9.2"v\& 5*X,NLN8y |.Hٸ~oS* CQEHIJ|?;o jiiNKIFj֝ɾQNMfREF}M۶nV|^(5jө5$qS[ BZ"# ]=9YA DFD@zzr0p4 APO_4RRsH1=-u O/ZP@0M è*J= 04/EMӵuOl6bde޳76&:$DEӴJH3|}V t 2Bӗbl6gKoYx%8K/aY[*2)!|Mb෵+roKH$JL{W)HļW6O!4D$0!$ɲ_oTҲӛ7'$ɖ6/FV4rpDxlqSLSooȈympAM#0LӴ\.MKM`a- DАs/ D07;33 (,4ƁHzCGGWFFW7vD200HZGg X̲,0ժAӴKCQ"Ǒn7;O ר`Q0Lᆵ>_(. )I%%e,ˊ%__URVVgCcs[{DwtvW F hYz%3g e3/yeU*-eYF&5EVLTTPP'VNoY33O*hniI 9ٙ'O%qhimd $bqxX!E30 ?lL&U*0p4NM<$ӫ 8SyNP? akRs.@b(:22|R0 ۰SN EQqAA2s3z` 8v8>.n#ǏlIR?c3E:^*6a_KʒッOڛoIĄhlo ~ﰷK32:p'}ݳjG3S`8:*RPF(*l;wܿs8ZZQVQ11CQCxd2鲥9 0 q11z{=jVT~+2r0ad A0.-df(!.ZD}}}U5(?SUQ]ZbXVV'&jbcUJɓűq1ё:F =\c#J-!F!rsD0,&&Z!sV8sj:2"eXLNJK$ۍhJrFVC#==:!O J,ty8=-U"h4t􌎎۷ppP` 妺{z"WXX`wOU+T*`ooNo2_ѱq!Amm.{*Xr,,4fuv <|r<,4juuwWԍ=o0MdwOle"(+#jUdDxP` c]=C#j2((P"$[6LFDM->>>EEU*<88FT[{(2`\"ɸBAriǒ;(0"GFFF8ɶ##q11^MzzY0[چG R*.7 \?R$o|ba$މ"貥bc9s./('&[E8pT"7Ud@~  omk?fJT0FQL&$$/J"#VKGg8!ZJAAq1*7߿`f dfaD Cŋ"vGqIw-STf{l|\ZmD,r8SӁ1&yph("<\; Lb;zzjjLbۻ{ƵA[6j4j HINZb9q]&yтToXA>jZR  (T*{ /{î|g d>QKRCijhsMLV-D|cŽ~nEgk[[ZFZjuRdWoo˯V~-oCp\=.,(Xt|#pSJ֬^2sˇ㸾~sOF308_KIN\ qn߻qꙧ$Nڹxd0˲~UN$pEBЃ& AfDDf;gη,7 TdB?_Z5?U,-n_#_a>h/`qf{&b̅ۖ;P h| B>?W?sNieLOcRB=?w*2/7g𰰵k~~ 3+qq\t"X* d+w9RA!А+XP\3Dgm$ p#(X7A 4rNX2@T 9FPfcfv8/)i^1x8XPfS9Q7PR )X l쌓I ۘ/]PYqr#0.6,>5_R S/,w^o][ ?QޮNu ( %a^S3ilj>v< %hp3~2g"!qŧJWX>߂N,LJ[IKKy`}a!,[IOK}`Аaz+laNNJd٫}(:SuKǕW-\ Ʀfn^ nw}09_0?,ZTP;W x8nbrБcUgkv{Hmff!q#GefqgXt:z`{*7;1ݎ$)fYlHRw \?uu?p锤ć?<, 㺻?:pbԔvl eXF7h0s2 SV~nB`,t:I&) A.iZ LqdHV *$I a$mv;N L*&&&$'%"\V ;Rq ?r=LXXh[{H{{v??J $zЄP*p.dF$9c9D" 8tva0 3 /XBL85 x{o@Qݷ^mhh:rRLXRTEfZ\^qv$/;/[ý*|׫ ~ub?^| =-UDZo_hx>04vP(P(jb\.f14pBnZEJW^{[&VVU?Y(˲UՉ _A-MGgJ{['~ǿ?|]I 7 qGj ۭ7hD* @$]4͈D"vRO+{Zn0O"jA =\c#J-!F!rW:rvܗ0 u|dQum/0 Moů_p9]2EDUӕ M>^^ENiϛlim뛜υe'O!yZahhv`pptl<-5"7רh'&b_808tҲ 1Q"ʪꂼ܉Iݾ=^YU70"ɌSGOʾ3=}ݽVEzqA~D"^y{h`pri4^~АʳonFzZ[{Xs*Jȱ'׭Av C#jq]Uյ?pxX( YVqf]5==,4EQ6t>t޺s,;V[[6Z,}Tlhlvʳgw[^~{+6&zdtEm[6oݼQ&w|`YTI9C[6z{y(284484Vt- 4ECBCãI .6\lh,.)=wBLL4!<|Ti٢:~C;Sy?,4D.OO,3}}eݽ==}ӦTaX~܃OJhV{îl~3XNS7x~LIIҽn>ܷ@|\ q1?| oFEE FcXm]ۺ9>.GiSZJņƷUЎ5_SNKo0yte CڦVi0Jx60 aj^}oX=vf{OI{k纻{}H>08422a](5dv BDL{V8|T"9p#P*fY"ȤRĄ |}0~ 0`148(㘘#4M ǂ?JJL8p(eYZGDa8klnnljm1Q_;QT~< ]=_y{y\h``Rw:8\?.O IDATKo0x&Q  ?{x<0p0 =>|X=QTl2ԓ*?/zБ|;:ypGlL[U E6^!!ڈ0 rK*efl|jjJӻ)7a-6"EQc=}}ƩVe>>ޙh!)W e9YAAqDZ짼0 ,モ k0.W">A(jdt, ?<, AB.'KjIebfw  gy鿯x̌tq/G* 2ؗQwq B #X!(q ^0 bE"QfFZqI)lim6ZXnҝ(&,'&*hW_>| ßNyKD]66 A`rOa =IatY]n0?bxGF#DŽ)[8{M`ddX+KQbrS꾭[0E!ORx(AU*af.EOLBVFm$?t54MtdD=V-hf[q%UID"t$Iʤ204M2!0L.\Sjʓ!p@DQ)r TܔF}{^Ryf<^FEER:G$U)ʻ0 3::@Dj85eEx`DO1iе8M&*XC}N\XX( FqhhU+r%s>EAi)nݼuw/gh3Kbz'Os@Q49)q||nwnwm]}wO< ESGj9I-㶴zUU]I Fse۷mݴa]fz:aCX,ihq\.Ym6%SK\.No T1=pjAv{wOfMNN (ݦT(6> AT`tSTk[GIiի`T2j?ul{c]$I^ll LNcm- W,_ð7iZ0 0酮@VEĄxP]SgI|[GǢ PMJLkv;v֝ZpcR7rNWU${$"""Ks%|Oah`N`fjzZL F]EY40pA۶nq'z)&* D) Ą pFzڦ WL_fgc$'%춳յ'K\.RPlܰ>>.lVמ(*8p}||PUvFX͓ͯ kW:Y|~t岻/[Xizw)  }p_\InjzJ.=oO~+A33R{z+N/_4#=  M۹{r#=A9YVw{K~翺\.A/\ [fҲ H%һ-bZUV~z{ ڸ~Tz,dٲw~̌7{':uE A<`< 0$&[m5J\.R.m.ܐHJLUgKJ9xm޸!!>rT*wc0q c_$w*yWNT&{ŋd EQTXHޟ}V ]acWfD/J M& 6m2Y6XNscc.?I;=55t$b1˲RI]P`?Sq2[,n7 T*ڬ~~ FJ"Դf#bo/aUBbq\g8r*q@,&*a XVnbK!Irzᙧ8jYmVfQ*2eYaZ&kzdۥRnw-XX0N'"JR*2Ji ^^liZ"r{{G^Z@Ӵjde6M,cJ"t:\. ð?7]M!2:$ii4EfEx v[YS[oY^V_iV2Eݔ 4nデ~3Ym64 JB&,gw-=D"s֊1g&[NN\"br0nrh?[AX,e9!JKң(kZF'|1D" `h|GOk4^3AdAS]l-x NJa\&<.^^// @VfgwDz _jJ n/)+_v -ZV)  ^Bɤ3OR?FFG5j D,6' "'P@`~8߹K?ɏ5ATOB{Zb2r9{Z\!BrL.BO+p+pS,~Хf$.pC0~" !o0 k8T:y aݚ |ш7gٿC7C0/EǮ!_:80pnE nF A~oDw-<AF#; FPn0% p,` `K@@@@@@@#(X7A|y D::xiz`pP_M`e FcwOeYbVbCqjjI;Ǽ&p[òNmWj I4=00j8l6OLLΗTɗ`$|L_o\/USWTQqɩfi?3Ug&qaϿ[_H$ajjx/k^`YL?Q466>w4ktll^diluM(|aXKNpq旃bxzf" )8ݱ #79͛6ܼ-; qdE 2o^NU+c6܁L梒XpsKK[{2td*:U:88(H`u`|IܹgyEຸY ~``\Td&o<0Eӝ].Prů?ع ^ήn Ų2p>y9"ظa]Rbn}+f*X.yO*df'0LQTCcSskጊ?w>)!!,,aT <]Y/J(Ixlg].*\vbbb }R,77R盟Ht]ֶvSSRQn} 2-GYhh*?}Fo0d@rN *̌_@ҶewtswpPಥ[[WXhk[{R}ϾMͭS+/iMssPexcNp'ffK,6m46_Ҵi012ɲ$O:Ū8ˑnfvfnwnfvfhy"(,4t!B40440 首ꚺ0X j9\Аʪ#2)8r,sll<:*eU\R?_ƚrf[Dx$m H}|o_?!-.6řRW5bA39Ma<w`phw:,4ݽ:7qB!JC0 8KY][4h0G^1Q+ J [ZoUqjbDFͷl$I ר5yj ͭQҠ?}$7T$&ċB]]WGdo_WB|P(xQё5uIxxXqiyQI0}} Ʀ"wЂ@_\qJy+@E^ GEPh2YRb<R  7(p,'VXK Px_$ɉɱ)|0VkcurPs_xY"gb$IϽp-/[ffԵ a5֬H$v(.)3-.sVRAQԱ̬']=A%k׬Zzn-y w7 8ZnnJs0lfFMtZj@ @Qܼ ˗-]jIRZ&⾀ 'BjrS,9vbㆵ< +S >urtl\.>{׿|,)1aJ:z$MfRK$ Ô EgW$MfX"RxzvX_bD"&친˖84]VQv*Ph0//fXv OOBeš/åRe(.^c2Oz`߂$gEz^*zzz`QEQD 23F ۷m-B$O<}6;/`[7ot;##"3Nmߺn4FcXX D.Mm6^_y<@_hܸa'zA0aYrmd x"00 o`}Yaq9ZVr`lT*y?{.|^k6mXGQV>9j[45nN..|}F#Ez <==hX,zh4("i( pp8a8ABޗ8#s~ @THEBC޿ͦKdmI:z\.swwGN ݨi4Rdk׬/(z<'JL&eQ`( pLŧۈGq 66:6&jphÏ?Edwwo/wD" *Ռ/IVU"bN'IJ , ޳{煂ºŋrD,z_P*,aMB@ձ,K3dA1M/O!$8O_i 1!>1!C8oMb׿|,5%9>%A-LfR d2WAbFQT(HRP05=-JA8J|P(잜R bZzbcBa_WOj:0A\d2H(Q{zz)$OѺ5|8.Rjj,0( X7bftl\xx#H]ΡCBJʢ"#Jʬ>xcaXq```p``0"3a-sCSlvqq)Hd2[Z.HzՊSg%p_RU]TXV@(X4`0(۶:zlۖM8w8 R(55P&9Kx79a$b1ǰ}CC#)(c\]\p>WIbW!HEj"<~y8dqzumA8GEFFWDEDv//@  U((lc7'rqzCPCA$)ɾbe,ˎWW[78HrF^|\.KjjZz{MfKOKP4RM8^P\pQ!ST[Z^108z ǛR[{zm݄aF tzu&/@Q40㸲ʚ:@Kt?_響) ||U҆f CCZmaQɌZtI@ L^xv\iZ53T(D"!eو0o/)ՂD>6;jxx$1>n%"0,4⒚ںI*KjrᐺH)*.usܾÃㅇvuwWTVD;ƌX%&14=NW/nb!ASҲq_5$8h|rE; 0;80TYUwN{z FK1V^YUYU=8B AH4{<'AA1  cAA4`  hAA , 9v]i (hAA_p] ,P(  oDQTo_US^~uzf5<[hn?shs~eF{XZ'֭Ya jYt(m~kΜ1iUoZ:vr4f}Wʪ?5+WnٲexdOOT~vlFNGfVVe۷mYt Ay )ڴqm[:_~uIqہk\6Z6jݵ36&EѮ^~80܀oF5ՠ(`@(8 |s^"]fdt4;'ؘv8GFwt on_o/{k~J eY>#} M}_(MdXXhxX`xw-Y|Rdvo^r&5[/cwtA zoV;n[ /? 6<2އ{{)GEQ~Aܟ;R9<2x?DŽaޅ *Sq ah4_z ð7nݲI߼~}aI+vؚ_plDD?vlɼzo~zCHpF]ƫ/~zjz:",oE󼥠;jj"۝;VMOι޻۽Q33rމaX]}cYEJ JĢSgGF_擧ϖW(mw.ɬV[AQqn~^ Qr=чak]$w\~ڏ?=\Z^sۖ5EQQq1ѻoSk4z)ݷLc<p8~Ԕm]}cqiRq=nr|ZPXOٹ ?v}zfxxhHp{׍0,k6g1Q'O9i$I Yab{l6}wpxۖwq[iy~m{=ua' ;1[ :n=wtlbh ~|TVQvjgb|aV...{VgGT*LLL K f:RHO[(𝋰,;99eY.uuaEGeb&n@pP uMMbh˖dɃdKJL66A宮C#Vd68S~>yG af38&%&}bgq>@m]$/x՗ >`HpЛoGG|Ԍo~jDo_z׎mյuqYZͯO'Bov g^J3yaMvR]BCZZ4MS4(ǟz9qw.'w||/yサX,Sc>@J0L0,4$8*2)m$uȈp^`Yvͪ.TZd0gV Nkji p##c ^JDq ò pE?΋=0_`RRBTd8K^@8 \|) hmkhmmxo_?(f1[tz˲3338$yx}wmnn~>>Fy6\*,JK-,.1-BfWT9jjGEQ(xx\-##ә'˲e>I WO??Ͽ?b 8ⱇgYLNMy{yx>^K,q<44 $I 4M 1 ;R74yx74X,}SSF<>1aZ@yDugDxm;Z bahc Ͱ < c_< S+7mX[W8 MnnGƕJetT$Ǔf{H.\(_Ѯ..(.R ά9y{yQu1$GFGJeX/6j2tEB@&sVM izFR^eH( Ø|D"4òАlF1 TVV54D"2 q4C(x>Οm}pq ;/yEUM}cccsuk|{zTu$&K%Fp8D"Nod"P(w:{X,ڶeTz `K38Oc8!.n"£?0ٱL\.h f&JBH$ZMq\岯&7- 243pdd4p-(r)&( 2 njn0X "RYeðK X4MwuuUDF-[Dk|>Nf2"\$ F+suoHLʾY(RRZeT:::VX\z!˰=|}1[,,_>J5 prY}cҌ)TrR$]ǣ"sr2<^{GGWw=qͻԒ:{zgOͭ!&&z6OH{Ow$ qK<6>hڄB˰Q&sY7obFo0DE/[!/~CCB_|E 'S^YUY]8fJw7Id,(a+-={.獃H%yG$&ڊj8]tIbBa؞];.q]e]\KƄ74G>!{XL"u/ "# K^|eK>dlv'P űD"Ҩ4 1#cGeb(f'߲$ɪE#W\w[Z-Z8>9 XlsVk4E%ZA"o=Ae#c $csNkum}YyqBpݚU~>ܾyUY$s!@g_/oo ֔WTAaHrEŋl6gGCPta+y{{|g>^("J$gh$juں:YtIRR 1!^UUtŲ $2VgfXzoEuk' Ei 3-HLRM>z Jb/ZJʂ-7B1[,_}\]l 4˲&?26::fZ}|Z]DxH$bYVƐQ7q$ƍ&8O$MLLNNMbWWW hzrrJ 3==U3f+XSF%8Atu:ha/nwtv$/h12"ܹ˲SSiP'X̲NdhZQ=}G}0LccA$'(0䔊X/W@``P |}xr:nbrAnnALNNQ4IJ@ jjn}bbwޯjZ'&M&n'&5 D,v26>BtnLeGGf4v>لeYJK hzZMp5mwO_Nn޿(-uvwww;t:̌C 0;uas)eu:Դ~2oo/>2:j&)4$X,l 2WrՆ!ZS*\T\??519e49d"mvf\CCB(ezzz$I|,j)մᐹkZww`8tx\o.$g?*2.\'+ ,l6A 䡯ZZb6[sͣ/<3=w۳k 'OUTw4,Xh㏺_yZpg2;a7׀ ,8 ab<Сib] mVP(= R$ID,Cc'3-qZǟP*uG2|<BD$.x 9oiB ߿5p  hAA , 9XAAs 6  l`AA1cǎ=ג񦦦]   ^zϝ?jGX- nxiV|#4p>_"C,1BP"Clqvj)7"&DQԌZ}ٻ!8AQe6n;_{(zFY7|Z͓7>1y = ØIo_eY>::FId_󩬮3 @7I-m=}&8gM3juWwOKk[{GZԔJu?gۭ81><<684D%QF _uejj/ iy@ ZZZZZڻ{fja k^4g?r!tlvsϿ 56 ޱzCNn~gW7CQ=Q_.e;_~zFVjqxh];\]])--,V._b3^X,eMͭ  ֭Y- -KiyEsKQ~b|իD+Nðؘؘk)T5=~e@XU(6 t Ewv\Q*ŰTgWdrws[ hm}FuD&8d2765 \T(,KM]ChpP[Gr^#h澁pokomATܼ f7ua a}='Ν  q\ӷz4%ys[5=}|R1[hmfw2,;22k>i)<mpsQMOϻu&koؾuZ/*fAPAzCSK`tb/`YVѶOO8h2Uԅ%BWi謨Yl%|olj9|cqֶʪ+/Hx}cc OI-mnnnaXX\cۖAɴ(-޾!-KvvuraSOeO}L3Lgg0 aQ4=08?0` OܼEFFDEwv$ )>O GQT7469OV=<,tVֶv <?ibOLvT>a6ò+)9u6A #M޾H1>>ݭG M èg$qxXD"NKMdb9LQ (Xɠ s9l74:r獏O$wv>6X4MWtwsq^Dxغի,;66>V_ 0\.$y\N}C$[. M&Sj鴺7z$+ 0B(G陖P"8]^YEQTWwWgԂ訨j;Aw83j!MM-E%ֶ\V˰bq&  ;KfbpG3 ay 9VUo4;UfX1-&ep(jA(0mIWO/M]2OZ,V$O9[[@Tk{;}?00[|>7$¢킛5uxX߉v(˲c{WP` EQcYxC'Nuvuvolήg z)&9:*j݀X$ZlIuMʖ}{vYҠ7J$baϜIJJX,>f09h5=pLtòrwYE7m\_\D.JJBβ˲ M$Eݳ#7 ԙK&aހqHdFqEf˅'~hLtTk[scc8LSs#? 44|^~$j[ iQj <]@7'I&OawAFo//T0ooꚯϓa ww];lҲ ZRZhl76>0_'MEDG$4}|bB訨Z@sVw77 d},+##](ⱇi +/  j|Wm6IQE%eOѸؘ8nzF]SOږu*-5X}\SK(]HӴF~~~( V0C(j4ZfFEx<ooeFR\F"8s3+/p"2<D:[j6 Be##G)FQbjyЕ }ne,[][711w`ѢT+<=JE\099eXߟ~?>s䤤 ֊ؘ’҉)J|bcb &ӴZ*sEaM} !2z:]ޅ‚G$(0048#Z;L&J5COVk45B{67_oo77y~AьZ]RZ+w!Z(0l>719jO<=8o|b\,/^p8*q_QSW pA:pj1L##2p  [m6َ4)nrt:}CS$2((M"H%4Ͱ+ؓO^s IR& U!%ӫk9oݼ )pwtf/Q3OR{Ȉphjiټa=F&ɋg_]808$ /JdE BYQYut|l읷$IvN윰D"((0̞޾?Xh^l aacc>ڶfr;bR?_>{xVqiByENwCkjny쑟'&#"CM&S'OgWGE?q;DcsRυ“R龽}}6ksk[{]!븻wtv>rώw jkOhO ?E===?QSSu-6LLN bZ?qL68oo/ѸbNa<<@QTf铧 bejȱLҲ E-Rl8lRvֱliL& PPtL6˲I%z)M-۶lJAA55=p8""(\$9bo眯 ,+?G1_RWٟː Q`0́sWghukV]V,[: KfKRbM8:ufARblL7 U-mAxyym\6,,E^onim#k㺵aa\1>_pt6;~zYn/jjnZ^<8 9|Zeԅ)pJ/ AtMx<Œɩ~6oXT*.FM3 i:BqL\y˲##5wn Q_}-p]e$ ,m'ᑘoeXd'?|7n!BJ%aB?A5^e=;wZW^S?dڲe㮝f˫o鯘~){: %eXutWwϮ||N6o\_S[W\ZaL&:Efa4d74-]x]^Jŋ/6>60M565/[w./:11UYD¨k-hK74._q۷ml-0 ˒EQ'OWV?d:3<2̳Ͻ[S*7 519y#f{v$䋯>6oڰgNn7Q_ {j٫jjo;= uEǩTώurdEe'vMsr_~Sg#"+ٵ`Aώ5Ÿ?$QV^yiݾ8=p,[U]{&;癿SguZ݂<V*54J%;n=SOwuU+?xO04a-6xť쪮8eƠ[WTRfJkni-.)RRg?8.;'WߏH<{|nA[|>$ɲ393Љ)UgW;^$caJ2pDxJbdlA?d׎I ~aV\1Q_&.6#-u|oW&&&[[ݵ0%ð計ɩc'~cJI^y̬?GLOR~W^7EQյu'OREEFuSJbYϒT*P8eYAj}.7{XfdYvrZyWGFB(fg{G Qo{w'/HQNɦ(:c[7](, o򢿟of)aS,X,u@󠾡 ;w( o="-w[o_(*޼aLJl޸0<2믜ϻ0>1_Ph4-Y,/3Ԇuk8˫w̬¢b m޸~8;œ|N90844<ӳmm[6:]^Q rO\^Y]YU"oJ$߲?##aojzfQBFqZ?gX𑡡;n;7GηVXӷ4 8|nmMNN  ,kjh~ϮYR"oٴ_Zrٛoq\Kk[n~ Ymv;A8Aq\eUuaQ]/4rg0[Zb}[7}|3~r_@}`GF|}PEQD.EEFttt_g*"#;: Mk7^}qJ8lf㸓϶᷿~?vf?xGGDBR CTd$.RD5=w|ם7u{z$ $:l{cv%].w4_׸{ DJc,c[|ͼ߼y33lMˋ/6gLBp:J0,+# J-m#Y!4M 8 |>Bؓ$Ph|S8D"q PTpw,Y87_}DžB8˵aY31 s*Fh4|@(e:;wyϞ{gutuqlH6fϜ `04M/F~o9c{.]f^VcfL6Wwd2q\sflteYx}NF{z-Iza( R4?/bcS$5$' &466"Hqppy .Oo,^ܕ7(8xN煆o29U/_]{r{'O?=wV{P :Dž oA^[<>^xK^VZKM5=w! nݰf1n(R)E&aHXVV*JNI%ZA"c V\tKҒbD2}dXm r4Mc(#c?rt~BE1yO͉ĢX4ƲlrN,E|O0 ӣT*KKJxW޻\` DZXbh(*F滗. 99沒 B0 %޳T$jԪTǃ E1 T(^xRՄꪉm h<>op B!?FQ4|RF8Ѧ }E55VT1 $AU*n_aND$fy?/{U*+UbSnߟ.].,H'IRѠ8xW\TD4f͜!ʊ>qnXRZbƾ}+ecT$ B0q!I >rEy> ĨI$I!~4MQ>AQ쬬D$B!PЗx#DR0RUEHD,hvJ",A@ :)=v@2hQ1$qj#ru~ehpEQH x" HZjʅf31P(*}>"xAP wBQT&RxtZmbb__^0L&R1ꮎ%ĩqDZu2 H:7qk NW*DBDSXP/?cڔ?e?GT$XmZxbXOD{|gmC`0fı8쓋}r<9,=P41Nw.τGaD`pjj""өQ -b98M|Ыlw`*C@8/ NfX,F3̾O9q\QAA<Oy\݉Y?u#H4 W!_Xp4PH9‘f_faD |;,U*~ q),[`^#o T*eSskՐ14yx:n؊{-i,ˆv"2 C!P{I#G7vJgNd|ֶ"A/]i1{IJ]^O*465gA(&%%͛3[*q*OZ]iIH$B˲`$ HcT*HK;Y36g[Z/^lkW:z-}&BCcvAf"X, }aIIBD)5>fw>: 0Cr2;q~jբ)5P pŢKW`A1'y-wnݱk=T*؊1u'MFCaA>ǣi&NЃ[m?|جm5@VW8pJaNِTZROQ0<OQ[,ǣR*CI MaAk՚{YvWk~U*<RaArY5UA~|/ۜD_ƑOPhll^aFq=t|xw)Mq EQ5ge5]QQ#sDJ^ߦ-ۅ@@zj؊1 @h5w`TY17KXmG]h0= 0zTn=|A>A^ޘѣt\$;wKWڗ.Z8X!aӦ:|t톍NSVZRZR<`nشE*K҉*wHR]BᲥmw8~R(*,Zm;v(}׬ ֮O䘳wl޺],w̟{ HtGnsBT5V+W.O:T5q™z bڔ8'cLQ+DQ /{m2T*d#4c56=|׬Sj>ߞ}Μ=O[,}Ɩ-1E,wP,d;w] 8.!Ghj.6qsH$6>}Ewpj=zaۓS1f4j9v)攏7GBꪉ{\v=h48f]{%'% >p 댆ys:|تQɢqc+ۿvF@T* D"{XVZ޲m;(򅃈Ԛ-voGOOi*v_4@J5qxTl[~ = R%]^H'B tEI$b!9-5^yt IDATB|tXO,UZmF!5$J:-cEJ)LFcrrE1F! U*N/#L&D,FQkDQ(1 yUU%b1 j*//W 1|tiI12-pxjJQ!RLFT*IzA4шaZi5M-jʐOɤLFÄqI1*&G)&AdJi cF(}~r|+A$uE1yeKfef"2u8gΞ{ A$bqR1X\VZm CRFzD"N 'pBդC9K"kDը%ٜ1 ,??P.ժt> X,Ҩ5gffgefD@xJi FV2333;@ 0REQKbS)2% ~PQ#V c2lb4sVH$L1?F (3SSRLFcɨQeRZRɉ#ѨrV1jaXI*J JVP$ICrRfF^p,I$+3ݜ) L&!qPFSSL<>$HZmrr23z9+> B09Iio8Ou>=Ka3L=8H ԧ"/Tʔ|z5 |J@d-ǎH$k}W˯!J}Tz rW]ǟ~XlPx9cp$;k4%3ho{>W;~znXѪ;:WGkArڛ|G>:_{C$>CÊn} X*%ǫ;Yeێ{^6d~{:P(1Ԓ|߸ykcs DzBpK nYjO<FCoqN׿H(Xd\7kƴh4&|/$//H`vukɤw/]r2K\nL۟ZzGC[ZZF""9vı4My33G:[7b&P1\$bxOq|8 v(AtK<.qa@ I;daCb0LTt AЈI g{.q    & AAM,  &XAA7L  n/LT"[1! nzODQ$/*r֯8hVꇾ{oK` 㾃O$~Ǎt ߵ/bpw75t+c/rO:}5h4Ѫ5~oAWwϜKa% f[m6;N0,˞9{n=|:ifl6 ßo/e@0hN'EQh4Ѳ;p?尙C ~+Um67H(?qm].w,aכ8V-a`lv;q:6ql60?Vr ]wak}x~zgg׼EwD˝؀v#|kg?Baew9 oihe_hغ}4Mڽa?p:; F˲l4BEno\! E=0Lqa5"hӵlvcnj!I"?U{8.9I?eruŘ|acFZ|y‚ӧt7H$jm;v\q3 k,`?^KfP8n?^+zy8=rtמhT"L8R |~NoK$#>EJe6lD" |]G~4 ںW鶶KqT:|挩B0[/ٻiT:|֌i,Nn=N_q:. I4ʼ-> 0EQ4d ݈h4zCGV˱:vjM؊1$IFg>jXtS'WWðQeJoC-}?lAHW;{䤤/]8{~Uz Ac0 s1 xϼAP8pI%sV@P8,HR%bq8iikKzz,:-5u``q,3F>_WWwbtP;TJEj4LF÷5nsv7aؚIN>zt b=&4#G׬ Hr;:rY^McXcX9a^K߀JiW|^q{? Ҳj:J5gLa{6FC2M]=P0$ĻfΘ#5Y$ B(eÁxjIF&D"]=.ZHOD#.' 27mي^7~D}ՄήnLb1 E"VcJ8 B]@@$ʤh4ouBCU*娲EQԐl4$GBƲi Ci7TO>m}9fHN鏞m|ٟ|{΍웷nW2yۥ/L&2fmٺCVۯ~!.]++6ߟ D %EHщkDR5D==|>$/~lx7n?˲fX)RUL@ACDfzRڳwV5j5LoucG?S<?eu'P-.*4Ck5@c8p8k'LHQ],HBPIQI/ӦNf6?/Am;vaXFX|}wtB 0ftY_&L 醋MI "`m;h4$;Xvȱ:ѰnæҒw9p8[.ٻA :3ޜǑ8Ad:|aq3{\&[~#EQz▾+M$~|45*tyx.]޸y\.Ch0BKPȤK,_T"H2?7bcEcOpx3gٻ/lD ö7&*Fk766KP(=er50HD"CpOeTY7qլd}OEՌ*-!Iroݶ`@S^jO_) Μ=p:RSư,kw{Oq(*̟>m;jz<hj@QaiS7o^9//__oK__Gevt|JHdE;wx݉RS?} ="(zz-{Axdg?q"*NY)&C0^TYQ.5k:|M4ԬQ'xWo؊C566ډ+EU9q֜l6|xx]Ww/K޻XM&I@ X[{s]q?AH p]{HeYE5+3#k1*R!/4\DQTR65Zzz-yԙm/=(͞t}x=.66i4j'N231k[Z"Q0*,ȟR3)qzGBaQaֶ3}:cYXD9cZj]h4j2 ٰq9+kPUk;{V8QK/T)6mٵ{`*V]VU$9nlEQa~cSov{0 ptnE 2 RFcJEQ äRɥ˗2ٴ)5Yi˗TJOԟ6gPM|޻V,/+->{s3ӧOzDu=~׬Y%EE?Oz#ޟwjA\8[#aM~_sVb$͙Z}:Ymlj[yii1`XȱƖv{7oQ MiSX=q> -{V^a ϨxE)'eן:W__q(z}SFzfG"0X,xUJTIKa`8Vw"Qը%OZZǍ_'0֒$q% < ;VDN1mjFzZe7z'N3~_֔3 i˶ԔDcV=r,-5x|=wkP8|yՖh!i( O=Ÿ^|쑇8สss/ڛΧԎ]{#G=wh9쵇\;v.*, 5Bs(,fnwmڲuN"65ĩ8@(('/A:q}r,vH$?pREQ@ho>qBeFzZESRLI%TF=Pbaג3?/7묑R0L&^I|yym;vU̞?^3ZUV Y#RSS >^r‚?zN$-D+q\m.ؽgCp- cr=--mTBjx`pӧ(B̮={o\>}ڔ[#IE+eiz]xbS=XX׬ߨh{V"(a4$wwvEkiFQ4)IjbT`HPxMIܐ2`;xD=\io\= ðѣ,p$g߁IU@"wtx^߿7 쬬viШUK/PUs F *-6Fh,P(p7c有[ÑHnNv^nDd̨|RQZ\t8ppzJź &ߞgz޻Ο{J3k&I**##1Xa-v¼kfb>cFcjع ^ 3?]hAF^۴e[@,;Qz}Gd2j[ Xc؉vݷd Çs?ap7 i,I3MT5aڶK AYYEdҲҒ\ |>A7 a%%.k>/m JKK}~4ݳ@ۥS&WDv{G3x̌SgZ6al6;@T.^8_޻p,VxgΜx%EKfFlsT*!I2#=0fhpPT|`jG!i2쇣P5jCX UѤ@qaAIq`3A]w9N",Z0/ǜu\{Ͼhxчyԓi)̘6kYX\TXhA~ncS/[k&UiH r.Y|>{͚9=1HL] IDAT[<>%Ni!yv=fWXq~C?gHOd>@zzs?|*=-AWYD̾p|u#` w 'N$IR*̟{׫X݉DKhN+oܴ`0_lF9}*EQV<̌9+*FY#ϕ $I "G|׬ߐc6ˤR tz݊w^჏VU߷aO! `SP)SQ 7Au'^{ټac{wg#F*„P(5*;'Eg{G;lXkii}7Ι5z RssK`ޜՉ+t;YW_ُ+֜8Yko' /%~a==o]3gL?n~{:PO sxR:l%T_z/@4(J&ݼg4X;}\a~!nFeس7eNי3ZZRSRTCs{o9vh0L:ycNQj lhr|CE+'O^:}`[گfgYVyp$2nlR%ꧺ Ma3LO״G]\`AA4]{gR8>耑&=Xw3 6ǧN:f].B  k AAM,  &XAA7L  n2`AAd0  `AAt}w ڴe˲KۥB Cw 7H(޻:1? IJl@KK vtt:|dD?v&eG:[w$wϟ{ ( {FM M]( j=׬+.."C6ouzpb4=tHVfFNۉvs== äLWnw>{aԔ ?A.YimtIUnǫr " ry/λX,?UWs;-?뭀`0ge~ǟ [ZwÆ[.6tD) #}S6}m.FsVVwz;n㦶KWRR欬}]E97@`m9صb,qn_~㍯ t~V A@{G߻O"!yiHd߁Cǎ!RR\bӦfϜmfYBkg{zvsJ; */a[;q BSrضcgqQH&qbc;CPnNv8p?vFh4Ι=3+3p/ H4oAWp3k֬ƢWΙ=sh1ںm|>rMդpJ=wnw.^8/7'Eܜvc 9sZp~Nv697zcSj7mj2&W~v1ͶrX,vȱu@pRRή{MP)P\=8wtvMQ{8ؤITAQuz}iW::Z,}k&M{3g,{IQaΞ;/.6 sg`ӻzzYR\v{&MVQ,kټu{b4̻+5%ֺv=h,߿cnX ɫ['z#NԟZ0o#=p˰6 ;:;8<~Ǿ#@3tr'*FM-,:^K`u'̛=SQ>&1[-[ EZj ~rdWnqهoEǎzM8K$bo`Ѩ23aui$I&>DG:(6l__4>mJzzڔIj.\h\`^zZ @bJ8̘6%+3#77glŘEXlgPԩ)&!܅zAWEQp8"?(*`X"L oyQeeECrzzr߸Y"̹kX,+}ߟ'TWUQIz!9lv_Թ'eR)eٝ?x{<޼XF A4Vq 0 3{VپsMϹ X7I</h! M0 y$)J$A>?"A0 EQsr,M3H$bEQE`hpz0(^OXK\I[ I"0DQH`X* $E"ai˲W#Wr H8(D1T8rhqW9b=׭byB0"@Z `9P>zT,D#b(qhF,{mwC!T"A e| 2 AP(e9_iͿ>ѯ09 bpՓ?RQ1f._ rf!X5t~ x}6ҕ+Ď2|x'N$Ify'Izp{< Ƽܜ M`T*b1jj3$' XHT*x<^{GgNf*|N+9Itvu47O /J4jE &aM,];Q</#-d+y(&0a8v\EZ.M_8A@(E[. S(*^% h,Ͻ{uXݔ53u*+.*LKMQ˖,"Iie1 S*V?dhqebqh=ԥKZ+2CR8ݝt^DDV虧SLFEQq'oZѐCũ~A UZm;w)+`$rN>94+9 ^"i)-)޾s{"qsK頠&!)Ieێh4 I5&n۹'h2rxMLgw8 H$limPbY0o`A,7d} 1 Č[ab%]==vvuwtj4 Q=vtvrjǍs/lݾ{*<|dR().x}G'>^Rrq^w׬'OE(m.3z߁;vQ|>/d"r9Jԝw8T"[y<>t :d=i))Q; ~ = R%ɮ4h4ԔI:fjPtQz63#]QE"D&(VSvКIUj*7z{zz=^L*3{R9͢Pe2Ǐ-t^~A~h(* iPt&'%UM0 KOKs\nWRUf2dXc;ʱJQAd4j/Z^?cFڍ4HbXj7\ltO>i"(5IJhhhԓZEjavkryIq1@Vt:Dl=^F6r,lʤRJEdYi @$Jb :]ffH$p{<_B.h2pn\'R(,G"`p2J_Tdd8a(Z' SSRʤ'?/75%ñҒbo4"H\!3&Hѯ8Ou>Ð,f?ZlM{dqڵW H"/Tʔ|z@tb ___˯eÆZ^~͌{^O7"eY.H<}܆[-Y4ftHut BtK1`x|*4sɋM q0荄PL  ےvM_\tqlʂq[$HRS].yx燆GrR5*]llڹԔ;#=-T74U|zZꪕ+2(xMOEтk_=c闰My ,D2k ;  )q|A\.^p:q7IR,rdSgN4 aaJby^E#Mrom᯽J> 0}}J&#rDGC]n`9NL(B}>fyoMVfE11fVcS˦['N$o 99yS&MP=QE&ij@ @ &9NcM&AA*lvVJ-0#u\lCQ揞65%uy/)z{W(Zwbあ߉48tROO  Tw+.6j<\!d8\7df߷jkyP}G._TsBWץ[yێg/,w\~K@0\T?xxێ]K/l `0==5ԙ3A$$yK"7m޺eێPAN9?04iˇ6/wtv=Wswdg1S/z69"k;.꺴er<hߕRԚu<2$>NeJ\lСq:6=+ޝpce>JXPp)Ն 7TY]K/ܲg{?QUf szzN#=wA.6hlaazZx\l̩3gCAF IDATC "&:\y2tozG[^|P4A>jU癧~{@_@ņgX@ A-DŽ"V(27;K"DF+,?WqA#ņvBőс "W5! %H&N(صg.}}ˏ8><2ꓗH4q1d_k ]Ax%h|(ˉ\9!@xVPT.]پlBA^zZch*~I >CƏp784qQ?Q[zAؘPjnmL}nG N>44|#0 çNDDTYPZc ,֖֑:08:N/J$;J&&$,sZc M@!@>y%CBBɂAմdiSWb4dCQtju:],  b1 S(ξ| u'NUXqGH\\AQDTLF CNVAvw>ەy!tNi6wƆ0T<  (RL뚐@Bp9uo$:mnZhX˲CQf3Ez2jx<:(sD^!tL @ ] ".6f`pxph(>.@wj}qs{GǔQb 3 戈HAغ.%Me>?IJ'"T&Q"(4PEQMFDCo!(JyP\mR) (B(e0;:k/Էwt>>}j*F:uМE"3}hEzzR;(bn2I،AuΌtNKVQm' KRL j(Ri4bQ3wN tI ׳E2TZm.;Lp<I^ZmnG8 8t\@i&EQQ/:}/r{M˲5kN2ibA~>3g j5I (*I EI0 KJL@Ppy`sOTV-]TJ;@UNJ$XZZZR`ܜ, L&# ]l8tEᑫa%p>QUzi9Qys8VV|4M8y{R彽}凎&EQ qq5km66`wJ:\~tEj4'O$J29/=saIqᬙq1$I46 UO)gֶ1Fc\lLXxNtUw(j4APq"I2-5y߁  _|,g;TJٳ2^d2Jŋ>pS{G'˲}G+#wx1 q8MM͉q **;3 EsK̟>m^J(  `X]nwќI Z$Ʌ6nbY88Aq=:4uFZEQ,?mF#RS4t\~Y}iZx< TVnni-=쬬Qˉ*0Sms g084+厥IdJrD")_TZR4gvjJ2A摑P+JyXru,BY9|L8!`0|w.KLH pŗe <|j)8bcX/LJ?p_Nv6˱*RܽOT>N,AO0w_rp_X`IIgؼu{D?C% ~_3_t234}e{:dzUILWܻܽ*9)DZٳfl۾TO AQtEv̲ 0EMF%WPd ò2+OJHU;va&aœ&+Y{-Y~_,ZXfϚ1v4Zuϊv*ۺcW<(#iXhC?ܸ ?/]7Abٶs##᧾})ӦLX>,/gk8 )\raqZ/bEK;.yvPXxP:]FZfONN|qw۾sC^HIIZqQQW#T97'[.X |r /x~Jt8q1ŗ~!=sFvf K&=/BzjwDEF>^Ͽ(Jl޶ㅟϤ$'wva2Җ.^xѽ!MMIdft#G$~N !\vа^{'DQtaiy=^Jda9'l[| /ӦN~7rX03LSkI8"WŁ2 \&y2EYiZRBYy $EJ)@ ri (fB\Rʰ?`~o;NXx y9PT"MzB)zX EI|TJxeFP0 C3 EQ)%%BxS4ifbhVRCT*AƎ/M3 dRB'I1 8SR)a4M4\. I$P =* C(BD"AQyߏ8 "BDB 䧿i!@\h]!( AIh @ /W(p vElc5mU57DGicKP a&TA<\.cf=}/EaAP($%K)@L& t0 =*jϱD4UqV/}׃ Aԕ="  H( {?2>]A(^ !7 u!2J?F Xxfw_|А,CfE6?os?cCBGj~1 RtPEǖ ' /FQoF*'aX Mjs5 ui5PWmf UWh=0>س YO@@dC$Ijj=woQy% (;c'8 c O6~.u| B*B٩T*BQ1 t+{jŋJoҏ E!PAR,.rL.싽?Ex{rl5/AGg(#}Y zz{OTVhkҸؘ[)͗˟|sG+*(?O,L=pu,v{ƛE7=K"Q(tz<O0lln?Sr%fy@҄w>7)}-Y``LfUݠS9zzODvudxD0"<<;+s{mvmxdBR*}~;r=n'::ZV*Bz{lv{Jr!=W=<[{G}#\|pphϾmngzAw _|7~ɪZ@imk_OV?xh\zZ0OV-_t8nG[rϜ{ֶMm9p￉(] ^on'kj7~l6{UCAs556o }<(N(Ȼ4v݆)r=X`bGg==Ju: GzfLx~}YO.[De.Y0_RwtV>c21mjrR–;+ß_oOWĢ/ ݗz,*P}݆gU/S}RO 2QmimÏVbCNK-=Wxw칚,Κ8q MgΜs8ᑑq1‚bLu΅ Jw_ٲmņyiS$ ,۳j劶FE7~ey&Nx[_E084|jIs̖Rj}w< UV6urYRXV AP;iK==%  aݗz8y錏--P( }ǎhZcx{n (u_y5V-1!Kjj[eKqwn.?|ץK-'22OrUa He҅Ź9]ݗHRar&N Q*e0*x׬@J$U)Uf3I9YͭNg klzy~ph驓')r M3+y~`pa'Nn*=Bx왃Ce{q4k*pή]e{6}L0XT4'??7-%E"!.Wj*e\l III=# ]]&1#-$Ibc2՜gp)#-$%8ťVA]8ksxROoh3g=:k'*4PΟWJR[DkR(ɱ1 GFFΜ1MRnÇ+%QQfyገJK&.u8+BR)#" ~ 6& xxTJ`;&"""(rc:!ʜ:eTJm-qiӧMq|xdghljJs 55$IQ$Y0̡ BTvJ% pL*뎥׼>3O?aXOKH$%Jv{EuFߋn^\.7)h4걙p&OE#FGGHy:A?cPAZZ8W4'sߪ#]?ɏ" $+3#Lдf͜VvNjE_5&z&I&`oh2Ai6KcA^khj@22M"I]n=愢qWYu:))a$IW3{VsK+Esh;:$xż}\S ǰKU RDQp8(v9ztS&)(2 cw8*G?!H҈pAR)@"@IJ~?˲(~BF r BIQQ##ci|>n `Y.))a˩VT*% 8+,^'D(02qhY3) J$8 p%IaqmELdY rWrT*EeRieq%r)rL֠(j2nw ǡ#k~Av;c&A  IN6! fD8)!ecթϧP(p'%OAd2YhYN+IQ_].u|u;q11`tTJZufnnӥjZ- =}/k4i =55P `dYqv RTlLLcSK`|\, v=?/G*Ĵ  b;Ɔl@ J#McirZ'_4$% DQDRYmvǣQ90B,$E4òN55Nں=77[ƾj4:::bc<0Xoq HCc0 iԙ)' M͓&`铕.9NN;::V0rH"hE%?Zr.V|M8k߁y֜q|̙)'uvu;p0?7Ű:'̞ DSss52q|ϞJ:8Jq1,DGIңN$'u޾~m4-Yw fefx-wd=p=mX{ǣ#+O7w 4M:HtkEy9G+B!o"srW~Rҋ0 X'*"#G˕]{hZjJWCG n2gS IDAT /Wn4?Q"[ sá!) XPѵࡂ\ j/#R4 mgΝˏ.*-KӦV>#W:JeZjJZjJGgבb9U(G+$DG-g?ܽ\q4%M`PV_㸖ֶ?`2cbb,,U(rLFFNC}jhꮫxWU1 j2JeMbbq1*2>6f_?vs?~ Zm껺.EEEs]zNall@cSs^nNi?}ꔨHDRWbFEFLư0RliimljF(˥(B]=0:*#""d4&&$s5_jJrLtt}ᑼS*qq1==/3Rc;j;:޳"L 0&&jhx)+3cRaKk5cz9 MKnt*Y0/7G._8>%9Il`ݸ`4˱諟3<VFHh288zJh4_hY񓕫~'}e:;^S&-YT:6Z@}~m;Vۋ?P9~]96h ^*Ί}daqRII?pBw!ngVӯcJbvF ;{ uu{2#]e?jiiveH{o:* __H$3gLi&FAޕ"9Y "9)VssH$ӧMD_X0)!>6::55gI$%rsc)'r2yrRMϒq , į~ۄ^1g[7 So=A(kگ;qH$D"M&6D"H$n2%D"Ht ,H$DLl`D"H$dbK$D"&X"H$D7D"H$nrv7{}n*8v-y?}kb98| -ujk_:W]sUn_K2&ZcY؉ʷ޽sp?꾉.^>8~rL>rr%A7[r|w`",v_lm(8>aB#C>K!t:]zzP N -ua9l~Fe&P}$Bws庺/?v/m߹Bq9Y_TTd({9yUQ *({+dթ3g]j`xZRnN%.[kj|`̑]jC1Y}?ﴵu<bi#["^q2NwkHt[A`ԣR*W\ HmwnVxItGVL>knuEC%0ť}}{|GU-,1K-wa(Zs… ?ժ}?nUB]}0Ȧ$=Fv>r5dB|a<Ϸwx}ޟ!,,ȯtoB/?#i)6nf x՚կ] Zd4=SBz窗-]Jw4 @v8Mͳg˘ kfs [F.Y0o=nr85,5443{VnN oVZZK/> Ϛ1c gWț߿z:vE-m˖,9c0*2gxͺ b#ݯRi: HAhV_3 k)wݷvD efo۱k qXnÒE8YuIѐ[fg^jmk߶}NMMM[ 4X0s54M[ɨh.xB|\Mc^s'CAطb}Zk}=hMMmG`zZz|?ߒ }5 rWUIc0 Kt>s6qRHKMJӧMlO2 h EC.g{{g36x B![8؉CGGFG ( uOb9vX"1oNDB m 4ijжZͤAxںzR")8p:ޙ3e2N;m䛰o's6=0nJ9cڱ'XmANVV>s帎[s)FĂfNȬHLOOxbz}y$:*d2v]t. 2n8 a!a].J͝S{3 ôutpWS[[^qðƦfeFCrRjJ'x1X$ںܻͭ*-hɅ؁0\&PB&kUJL*x<3O>Xu֢91[r?ܴlϾgy\c  CBRD  p8;i)iq 0uIyq{H$x|msKksk[x0GDܺn[HCQB8ALAB@??]$IFp8<Op"zZqpxn a,M! (h! ( خM]kA|*rEӦLzBA8 }) _l…|W:xEa ^ow.]Btsk'qլPbOVbYbQinnm 8N@dH$G*9].R}}UwHINdfsĹM{< ׽nC^lť%uA4hzًNL`6GǾ8V\tik` &$%:Vמ9W0L{G:NT$=~"  [+XD_wY,ֳ}>_0<_[:sT3GFGVW`0XsBKKB˶Zh:}:++s,Mb".Ywz{f͜NDxij颤vBm{exAhoسq L8AR|~ii)]]׮d*sґd }mLt'P6Mkozy_QCeRْE ?[wxltiS1 [P4l?´CXbQnhٳs gG9/Bɴ`~QrRKlߵZ4\(򜬌[M>kjiڿ8Ss gM(ȿnHTTNܲ}G0,Y@T(GVW#p|q1A<Ûp䤂\z?+pk?B8+3rkW;P KhV,nڼp#KE);AV*Ξ5ciyy#u| 4{ݙ&Me߰._GgN^Q(tz<@=exzZb`09)Qm u9?V& `Yvph8Le#NS&JH :vph(,,HAQTTuzC@ ԕR* 0 :Q&\i436t1c(?SwmUU{L ܑ@%\. Gz76lܻbYV]Zj5c |?C;3<35Y VXXϟM`0o/qr=o݌x_`0 c1 `0 +b0 AS6ʉ4Mnzb|Ek lj<>@@"۬<ױ1Sv{oY.kt|n4"EGEYqtl|Ѐ$IA"QLL􇇎 ~ðXm_l6;<,L" H ff~0`0Mɤ(Gߡ)d1Q|>Ąm[7UBiffɢ7RgJy3%\/B`X TUPU\/,izhxxll|79H-F "c7['[ZnOE兵Wr8P&fO1>׿os3`0r8`6il6 i&gM\.vkGJr5jhj48YvMbBohmm8v<~f ը \*446?y:=-@f5=~`4DXT&v=z/S$)wnSPO(b=@-7ݵ/?o!Rw  D!I].WsKߏ5j:m /f495& YlvSkk&Gt7{YB`LKdtT |쬳r``rEDEm3 É 7^ `?,ESShX2Ǣ(j|bc q0 # ~ /eYmff<^ﶭ~K3b({A6"a셪զP TJ%;: H_󦵽sͪ幹9(wPF`|bL*n,f%3`"J5b0#|ll "<`ԔTA^ok{(6&$IqξGHoCpWOOfz]]]=MD/,SU-m=8N(,_P][k1+ii6mrjj];o{>if$A]Ѩr;NM~gWwbB6GSZ;~PrҜ)^'K﮹t9?PRZ?P{鈈{4} ֭x {@,=+-{yo3ȦMe)ɉk׬ /ZEwni?{6K\ @$竪GX,X}*!>;!..{ApgWjP4  wv@ZjJZj Ͷjjfr`}xoLJLOc0˖PVWw}h4%wvuZ,?7EY]>8Ѩɩ#N8˖,s8K/"H7s(J^?t j;uD$EGFX9oϹfwu44`695e APA|^xX(ϧiPwA$J%ں ~?VUS}>JH]ryb)-+6$A!J[WVZZJJ/5|; }a<.b$'$76^g)aӥQ{IQC{{`Pڋb1.)-pL695&L3搐Y+BQaXfiT)rWTy޳J~퍷9Zbs$I(P(B Ù2RNyy99m~ڋmmq (9)!REQa6bbx i)@l:rxqCfT|a Q p pi)Դ5GrB!F h # IDATzl8WyZ"EgKJ~L.WUVWhqyQ6oz8Q^q~|]+-MCQc޽SRw]{f!A8D,n,ǰ (d2UC\AHt:<7Q2]o1H⥋EGE{%,EY}?Dd2f=qr,+)1!9)q|b?ϕW44o(Uu:mMN.q9NR1#F0rj4sQCbVX&H<~tQIUյz|+?݃a֨J G Zv-pƧ}~t:+lvqQǃlJJgY8I%kؘ@W:2bxJj-,ttu:|ljdӼ>n $%ğ:}v6; AEI(h:BC5ry 'd1QQ0 Κ͖ gg]v:.Cfra(t|>MMIF| \ץ-Yf ;{G3e!(a8e3Skk)Hp8EpXPS[}VmD5iQB^}!bj4[ظ$I}Y"AIw;A0I4MCx0& b$q6y<:- bidd!x-fKE兖6}>%.^j;cc4Mv B!W)B5?X,ڱm\` ̓ ϴO4McEE>QD4a,6+F $ǽ:=l; 푇jJ% a6nwBiMضUDvA ` AࣉH$J?Q"4luﮝy9gϕ 9lx<A0xnΆxPftlj1vR 2j~7:6lpN $HOtsKkFz! 996Ͽc; HlimoEѿ=@$\.s9U"l9I\.74T308dX %PͰ~bH~ ; {>'`v8f%\<ϮK( h#0yՏaO x e`7+U5za];"uW]PT&쎾G yS"ᢅE'O;.dz}84Ieva$wvwve3q **,zNҏ8l׬Zy#~V^liŅ)X,VFQ$E"$6 EY XuMUaG*OLG.3rBiAD,E".aڋ#W\NQ԰~ݲiKCE:}޾]2tlv\dsKqtlphckV/5 eUJBXmƦ#OjRSY|陳m::{zWU^sߋA:6))ѣm؉ӗ{,YE(ff-^mw8i&PtpppD"afFommdnǸ5 %؉SzM33׮ ?/;y``ټqZ M--]]'֭^=W'*,ӏ<f1(gj.OL AZZٹ_[~驧lj5Z.)ШTa㏍WTJ" r"MV-2'&ڈyN)XxcJ;vTN6 +3"<=Wڶnj˽|ْcg|I%?i~jf0lhXtѢ`!IR$!>֮Zi1,\*..'N90:M- yy.sͪ"P*T^ HqQ宮۹NE~ooeUNaX׬Z0IQ>m4fdu$'\ϻFo<$UVUWenrE "xaaS8k#"ܽ"]]5u5u|>?6&&X5GW\;f_:ulk{GVFڎm[M75=~RCT*ݺySxxA5uoGo0mڸN(bcb#njc7- G%)K WXz%A5JԔ|D'.x%Njkc$O8NMLDGE4mw:]@<0i<.whXv_Qy 5Ε7jԱ11|oll|UuxXΒHĉ --\.GE![@"GGG 3g=vrjzeJ$1QztɹںK?Z"j vp.^X W F+J>W]][VqrA|YlZZ*y7r2`t}&@h9Ws`ط-r]O..pap\72/Ɲ[XhP_{GgQQ*ۇnoh:WZ^\Tu+ 7~'>>nBV`2=/ږ-Yδ4M{CGff~޼&ׅɏG*YOg{ݥ׸;=3Rvݵnw8q P(+/3ͷ0ۇ룢"PQQI$]iH wDjJ_aM4M|'OGG(7%WAВ7*<,g?vڟ[Q1hE ]j:ImDŸq[(T香g{o'&׿qƹaݚ5VB+$I=7'{A)9GC5J~CceKPߒ\EMLLvvu{O15?7G>$n#Ts::6~ -wlh}ʅ HVI$S555q=wM~=Sy9i(Fߤ d܈۷ ðW^} ,qt@gw7M:g/~+*(\Wxk^b/ngtl<%9)/7G.0}[cll|}>xq8?l.a<Ebf=bJy#+U+/5nc .7V1jh ɹ v<^oY'O{ܞVos@ ywH?"oWrrґ'.x=˖<Ѓ?<499{{f_  ڱ==-兗_jՊeK>43s;~?-Yp4MWU76ݽc\! 554EXd;q0ASV߿ w.,ߞ}niiUյ'NITrLV75p8۶,wǮ ׮^၁!-YCX}aVM-%eoW^/8'A{&bccݵ3-5v?p˗nۺ͛A419SOwDD?! gĄ.BsYX,_y`P.ټqòou i-6FD*ْR}# emٴ1<< P_ytIM_kwB|M/u }ʪj˽0.6@QTgWc'6["!"jjyw?7QQo" ֯]~jڛoK6[;2kPpzܞ 96]hpx7^t1zWrs&ICGFGǞ?ժYh4>r|/<~G{H۶uC/Wv\iRxy pY3׈55e" 0m~kV-^X|ɣN8`E"aoOMMill{|{ϛg|/THVf0lϻ q{1n޴a"#0= ˗- i=ӹnC-<7{퍷Ξ+ݶu׿M>o%?x'~(~7.^? gΞE16?#ǎ ؘ[ ~԰~aa$ɾgK-ʘZaX٫V,E=pMO?x}|( 22L33,ٿtI^z7{wݵ{{ͭmL5_n0D&MMID'&N&r G6_p|>}*AX25e2pyܞ>?4Hlb.|ui__^x9}iq{ |^~~2;,*,X iӌbMMI&I+,,t8 tĄr;=}I ( 8^U]pa z I ը[Ey,+91'EbbcnOj(ЮyNhqMEi))emᘝXb9HO#H43z;ޱDGG ou I 6ip88A\.`X`07'[A$D&LO9R^K54$$ffX,T?b E$.["J糪b|Q_~8EQCOP1c!bsyp IfC i*X,ʪjAq\A x!hܜ?wG4M E-_T_^QGdb||0ήi@4M$% hRɳʕ6&& ~{I"p|tt 9Yl6LQ4M<E ryݥKIw6^GF"8ί~ 5bh55E@4M'%&ܺ)_*,J$M~a  (BQIR0CtH"cJ$bEJp$bx@٬R*!AD4M $I6 _znq:y\U^{z4MGGEx< A0 r@`~7}ygg?{遡!L& v$ pF?&>JLIIqĄx=^P |=YF(MfC0dw8dR5 HAQo` 59iphH"|%׋AD(j#"ش^$byŨR[RZVZv>::*"asM j0 9xb+7{qt AD""".54^bb 23nu `X em( ߲y#JL/8љhOo͛l˵Xm^AƏFxoQqѪ+JJ. Ec rϜ+(""|FƦfْ 쬬r8PfaQSgknOQaB!DGEaV~ A6mXWQY}<7'{Am6An^ _ѨΞ+?AAYVȱ(B0Ys ENҫoD]NWQA>z+`]&ŝ8uFR8{ @D,Bd+*.D>v gnl6źP]{bfƼt"P JJ>_ZjJdfwݥeCWYY:|cd͖ -8AY̌4Rw ϟ +dfW^z7x<>d3 ˗--9W},ƬXaa s\ٲ%bc+*_~ͭ7{ʪдX,* v22n6vzO..pap\:ӳ! Zm)I|>  9.-JFƧ  q: HVFGE /S68lvtTTHH C1qU*˕\mnm[XTAаI$(PAA~?f$IOLLNN)Jb(<,,- Mz9GF \(jy<^:vdZmi6T IDAT՝L33 \,8rb -݀aPBNtifF&l.Q{X,VGGCQDRj#"\.IRcSSxyQJ퉢(bX`l .6V(<^QR lv;i#*zz{ccb* 72lκKC?*w^jh_nqf(Տ &&^oRbAcSS8k4*Xl6[20 J$qkĄxۭ׏x<ޔ$Db0M3$I¨HׯiCdYgwooqa##u&ׅ\%颫OPKl.5_)/]歂`0W_GOw}(zG1qk}붾;`0\?@ >U$y^p[$Xi)ﹽnz`0 W'ft"Ʒm`0`0 Ʒ0 `3&b0 gL`0 1Ϙ`0 c1 `0 `$yE@Ӧ>+睽;qOIN inmO,i@dJrMOLQԩ3%kVJ6|e];o,TC4IQ֯[dѧi-8E74ժyz.749.b(?7gE]t:i"Q~^ 9kdBdeJ*/T~疮$r{_WJp֏<.79)QGFFXmA%JR"T('1!!D&mjn9t\"J,Zh].L*MII x< LT/^T iJ"lu8Rz/^jXa0=y``hD`8>.666(200xBBdi)<{!Im{d/`Yյ7[E="l3%|^LttB|Ȉ0:JdN@8pRiXNZjJ I ErR53 I)**RvIFNMINOMMy!b}>X 5W0 J%CG".3:>J:9)Q O@DGG81~g;x\nVf\43sLIxxC> #/L&>u$Ʉxr-y9@t&"#RR&&$INNN QSSP(*x}斌4D<08d0 J$ $a@TTdlLt0۶ޱq=} G F&s{_{gB2bq\R&'% ?8pwשEK/d؍M?<,tuT^Yhɢb]x4#=O"DGENg+WF۝AĈ`4aF[nԪ];s׎'O8>c6d޾)6b@oo͞(:5%yjzlhĄ1n(x}Ss/j$M&tcSALVr ,,jS&SyyX9\()-6d]fex7 (21192b\jŔi0`KΕϘ2bZ'F6LNNErҊ UE4MGRR)) 4] ðW^kL.DCcDJRْRӌYBQX,1[,gKvD"imk+/DQ@ j/,?/KġINL~?fXjuo_YE%e ;m]olYCOoߏFGF(Q_4Zxl).,8:6~ _Pc9YW,PP(xz⫯,wwìV EM>b| A-m۶n֨Cjj2ACC#~ m6DQĤ1)!ĄU+:s6#=m4,kF, r Vdb Dt76XgJJP(lljz=gϕ,ӵ~Y,7Npn'<,T!wtսaZF}eG(8~4cW_OMMcb 1;,+I+;:#"NȈa5$I^|\Rz} NeQBAQ@ ` zzT EaAP dguvuWfeetժ`KnڋqqgJJ lyW/]l0fef 6{nl S*r.Cb Ʀ#>kDBᇇd2J__ߨ֨>CcӦ ss܊=-tC  C,s bYѱ??{^N6EQ$Iَ;DB ;zK M'N*ϥiĄc'NML9oxܻfwu<}q1/5?u ?}斶3JΕO,p@cO2{{:=8<&4=66r֮Y%$IQ^}DEEVԞeڈƦg{{MLLޱa}fF:g_GXhhjrrYR:wR8pBu;JΕ |>y;YH((.*Gz~>>1YTn@ C?yѼO%{ skV\fb=ppSK%K>_zZ[T_zDB[1kPr ŧ;ΪN>::FQnu:3AwMfrY,V=(zrq(FDCrTgj$37wAQ 8ub6<| "")hp)(jB0 lO$X4iblX[wŗ_vlbfL涎Nߏ")  Xw|bdBПo}>ߌټ 3C"#BBa x}^A:vW^YlIVfTZ^Qi@lHJbR08\.+.*NbvյyޙB>Ot|\li`QdNT0\A E$97'F#?+#ðĄM/T/Ya@ XQX,VJJ}Z!ZZ.7}ɱnM3:Oz\\L~^HO.͗EdK[GMM45= q4E|~>`X(|~`aFEwWiZRv|>p A DE]\00,Hȏf +gggɤQ>.@x<'( A$IA.prRbXXl9y싯_+.}$4\N$Pҫ-Vy,U*h>AQE>E`!"kr[ڏ8npx<];ɧ6m\Rzp`X^o.viBЗ>Wy,E|~Tgah C( &;z_ bc**VkB|$F`q'X,r9@0 4<=Y`^byVfz`eU5MK/Dt՚I.GMlLLqaI6{vlzeN3x629^A܏;xC4 APB|F6[,gJJ;qr9JbllfwKb\R;j Ĉ C CE'@X,EOQ\.bٲO\n}|*rؾUVh.e]|C0EQԬcb%'&n ENN,HJJ8e28>m h TNj|~p:K*<^oKkE*R("gwϺbAzBiBzOu!pQx> ̜933>a!.lWd?802:vl6?q,4MTGg'qR(tZ[B0Ϟ VMqJN/-+OOٹs7ހ 9ppP~szCQP`Z:wf{띛г|':Vp+(0`\oln6'OH0TTLֶv^~0juwUPe09NjJRTVXi]wۋr|ddb-,6ADbagttfD„Vc+L:].b``a@}ƞ7ES Sg qԙsg(N9g0ljSgگ_;g6Ǔ%]Zh2:rlr'22а$=CeN-c{ay.)=?wK~T*X,KVX~O0 CC~mk_}D x<~[;wX,NrڬYB}qZkےE X,l} |j\.G(I'?=qL`@ERY\ooɎO/DG-Z0_ D-oޚ:_FV:v#G%B@*iǶny\QjrҘVt"D2<2nLMI.,*ٻ`jJYE%em>6}hҔDQ]S;#/G$4?0`XR K~w˞}Zݏ_a LP}u۶i7*+5nm;O1 S*ZnYZv~]˗-sѸm{n2:wZ7A`MѱHXYyyמ}!!jim H%WbeKD_G2t=`sv:VᷝM%_%r. 0%W7{VA\l@ ٳ`FFz|Ѵz`hx䳽33 fȺ*288n*.] HՕ+ӶJrO-X$I74=vbhh888hz}`9&r3w|Y|\glzБWk(JJ_*J9>h..IJ׍ܕp)?ߟN۝[X\r+*1 kmbKY x٧j9v\o0$=vH$zodgd_wCKgfw7^sY r$EݷVJ"#-퓝*);? ;g?/ťCntΞ+*.--~gY;:?1 1!.8(g/_?y]\U- ͟7 lw@$y֮mhlNMI^bZqWָ`4&bim~ܜ̬ a{<4!I*Hkj뚚|f  ^j?ϯkjNBt]U6{ 8޾>oo2YLtƦi||1O\4gLn\!{ qD_0fYg鳵u?F1-az>1°tHn#\.Ïj_ 9zDED_W)?}ǧΜO*(w`uME {UB.Kb4M8+?7W&j||<[nhBB!)JnSUf_~UJ"9)/ttvnS7x{i\ﳑю.\JT\lc'h3kT*0v?&JfY,VvfzтyU5$E&&ķ_ )Z^_S[n6[X,D,Z0MOnx{ BNdUuUW׮YRHINpٝMAӽI~b fitr9EFFGiju!IC+. I%\t$1r{[)6,kGĝ?WJ˗izTvаn`(r*/_AQ4)!A$]XR)rZRJJ/Fn7AVc4Mi,a^y˖]8<2"J2iӵ$ǴZ-. 4.[U]m(;_^QaF*B4AA|tE iuSʤi訟3 =n i$Ir`hƅO.a١ccږ6A--5fARTOook[{jJ 9ldr:]A465O.)^JӴ'txgÅP X"D"Z蚿o,^l"!_;a!-NRx<^~nNzjʮl6:e/UVq/\O=57ʨȈ{[)6{zl6{^N'Raf̙%eco;=aGP*]sfs,XQy9/'K DGm۾3?/cbKT"Ak>__jZmSDt_j.7B}eϜԍ뽼 Z}wxf7iY,Vh||sskt:X$U{yL替'P~5=FFq;yLS󵑑Ȉp__-_(omkghfxd$8(03#084Tv4yʞ}x<'X,V~nnQI#G|g\2 gDLoА+ĄW84{֌GV=\YueddEQ9 ooܚzr$B":@ ( DA\ $jHiwM2$/'ð@ÜNF㓓*"9?B B:V*Bb"#|}}5A$bqTdV x{{$0 qi0PV)RbH(J%iJby{{y{yn"lF\.aC*H$n`p\&0,80P듓㍢X,}wq\-JNW`m]kV"fFAQ4 W(~. <<,>.&,,T*J$(ke2)R)sf( nQJ$1{@<?t:;:j =Y;:[ڟvZBDTAy9OLtj*\&w84Ex{Ajro4"#RB.r9.HPhXD~H$ BCCB!r,*2  xeT:DZn^S_븷ӱ26?^kDEbO_!z { j7cc@.=agGGyoRظ Xwo-eC7åfO}kmgor hdҵWD_H)=dZo}K36|A,BP(1WmO@AM3`AAM3`AAM3`AAM3`AAM3`AAM3`AAM3`AAM3`AAM{ imk_?yw~R?1(m!_$If6[{y4ns*G?q]KE-6d6{=o06fv3 s/e vRWǞx|)c/JGg~"~ƴڗ_k-X+_(flɷ/ɺںCGMIwݧϞ/3ebj20r=}*$ɪLH⥋4MӴM2 sĄ]:_~Tա(&)a   n0%a$iz慗}כt#Goݲ922kTY>$$ȓOeeKzJu^ .8y\fF oώ5h&{oX%/z\QOoMSK/LIJqA5ug4dDͽðԔԔ[hj62:U]Sؚ՛6ItbB7]I}8 `fL=u,MADF X^^^*$A>DE 4#|}|ޅ{xM q$IZ6A b0D8nF X޾>ͯa†T@GG'EB3<G,\ֶؘh).WPPd(i|$:].Pq@DDXzgW7Fj {{y |V7:6v <ߏa1f4mXp+0f_(8vRDCC6A/?wADGGАƧ?2"bB"M$0CccZF,4=84QU^^( /pfy<;|>W# !Il6s8oo\vAZKۑc'R9lmq1Z;+/q؜m)#Z m cZ (jtllL#nD2,:4>?TgW =Ӎ>s$IpGD qby{UJXza`2eRܷm`t')PA}0AT*Ewo۶D8D"TѶ\EQosg8k| q8@06EbE%ǵ;Vu9I  Ԑ g jj a#=BjJ20;rZd/t$9{VjȤR KʆGF~=Wvddz{{ ¢ή.tfϼ]cUaQx]ki9|sO?7O-4(䪇ԅl6Nhʪ+cZ]QIizZliljf äruvut -,X,E=lgJ8\Cr3"QPIӌ_tQP`< CgYV:3?oF~.a%ekjy<.IRq3gDںg<}}5n,}PԤR* " /7zqȆ&Z=~켯fϾ+W, sp痢ezz~׺{N9g vkm/Ş 0 304 J؉8΢(JT,^8?޶C&"bn\誇޿@(5OO'MMNjlj =:0081aa 2tQ:=-ec=TUfgY,VH2iKK[wW+?z1:*).c6[FFFVur8읻&'%Nl68u2SOd$'C1h2x{{½kϾMde^mh<}?[ZKRXaPlk|e#CϞ+:zTJr`lLsHlrm׽w8O_P(uȱ_zp `̛tb?|l#̶?t;"5$b;Q,d]\tq^n60m媫k$'49-9V]E;>S`0 Ù;gfHH𙳅EeI,uѧ\Z]S[T\} ,7õWt \"0,ϯT*X,||yE Srbt:UUm͙2ѥaXxhhRBX,ڸq~ߤ$=в1vϾUWjr6khhGVpwe[?rW@htw`X;BA;:>fLL F3{VAqH# =5E$4|zHJLq<$8f$BBk<-AA ɨRqG1T`y'sgWmippٳIi~3b`06_k(a0,4$D*]mnmk_x!pphh} h:0 Z`Ҧ`Y3 T|rΠ7TʉtHvʪ+ M)I 6h2}6{TJX"`SAAl:| hJNL`@" FlqZ-`=  hV0(Lb:y2 EoSz{9bawG|xr }A,GaFFfr3##cÏ\. ?wF~,],_]?ۻ/!>^IGL.K3[DҋG{{{-_8"<7_~=(0 "< @R¢Ҳn7TȞ$ 1Q#o3 xl-uOzi))WkXspϛ;f~\/W=bF~+hp`Jz̟ RRZFnD3dYbcU %6tͷt:INLHCQ,=}4McINx"EOg)鞥Lz&D)'*6QyYO'3{'xfLIӴJ8@ϼvb#Lތ3N×IkEMz)%Bc-g'q4`8/U^~~󋟇M3 Sq[>/~ɚ|RLܶ&a'L/ ~~meO9%T[ʆn)M5[2Zyo7JZۮvM9|'o.br3en`S啄 ϱq#.۞,hlL&344w^|Ƅ/.~{%{7Oop;A}ϭYNYk׬6k}`K AA4AA4AA4AA4AA4AA4AA4AATS~/   c߇F}m^,trՉ[ZN>KĿ l6[Mmfĩ3~}-d_зt6465_kqsfT)]a_(|ffgyR F'|bb4k VR껜$ɾ࠯Z4tVkSnp岴_u3| \g4M[u7dтK|W*>l1EQqッ 0}g8I ۶`HxC6ՆƁA}}52Fnq`ph??Mzͬ[ݤ嚒HQTs2& FcQq` ͶX4C+ob?d V#I3L-j!=`SV?А`]("rh28r>޶EdCSs+)&iw_n||Ǯ=MbX!S4En2v}`Fo0ٻ+=V\ZVTRa\.j?1j+*)-.)0L.i[>ڪTH$͹p1 d`pSӲ: +,R=< =Z6 \~}]&0:zQ(ֶ;w{no\l,HwT"fn:{ E(kE0we/V_c_pWT&)4O:IBŒ0gLTjX;V(bҖֶ##\g삻B_ R)1,WT߿z5M׊K Е-rM͕UBzVf㗫<__͝BDՆRݑQ' Jϗ]p\DFG׬ FPt8Nĕ +Gb|C˖0 S[˽-6W\lZSSZf%:*b66 ŋ+Wj &SVFzRb>A23,^p5+ r8lh|<̦'g͜fF;m9vԆ\Ukzm{tCG$\RvA h1 E%rlF^nLT$LjQk{{O_߼9 .^ttf-7$Q<2:h<ݸrU˗ժOw}bZ\&h0ή#NfJvk-j/uDX^oxbݣ] 1 -ϛ;g3зjccb0 m.V,]~hheKbb1h.8uWywˇO+*/i׬^"UWlV[BBXx4+##22")1&_mصg,.; Ogdlf-^=B~_߃ڜ։?6HK]t\vAX\7#?s  =rDOOnR!߸~C˗x{mtx<^NV&{vsg:}.,4䱵:0*233ĩ3uuW/],>ٹKJFD!W̛3;==t`ph4fo\Xt6&%%X, E|>uWKΧ$?rŕg i _"3 &e/47g&bJB {vqE訤dzKϗ?pxپ]=յu8/Z0P ^zy ڶ'jeK&VJlR=>w:]O>.&&f5&%8buuwdeDE 6ۋ˽ѧ\.@fBQT&545O)dJtvUWjbSS9vc'Nr9ŋK$sA_ bQXhA'Nɫyyy]jhj  )2#=%&:v^;g&@MmVwǾ{諺sEjlW,n\aX\. A.#- ? %MGEEÊ@_} |\|rbM1 H$,aI'wɢG>Wj?X[[oZǴ:a8[=^i)+Z.Jn\OQD`r,R!(ժ?L*EP5irY̖E%=DsI%A.IQ `&Vb=fUyEG۶(ck':CQP@bdDNQ  eXest!AuY ݾa?_ il6fdǧ`h <.=(X,U>G4 &*r˻oyGF>ݽ@ݻ?[~l 6ڬNK f+ѱ1qe/6 Ӳ26yVr |MFY*l'r?2MRdt\Tnr9F3pΜb}d2;Ϟ {dRalPTj6[P)jڷ4b,.)/~. @"DnTj;ƴZK4 g-[h[>ںNoe/Y ø\.6P(/\207ߵ;=u[4M]ojn( /~RX\uauq1~~(qL7M&X, suRY,].*+YAnOxǹgP U?{}w?䣯)!0 XX}CcNV&bEeNV 4>>(]ml̘(!pp91Q&2lhxd\WTn@$ꍟ?t㏮Y%&G`0 -r /Z0-$IA}ia\VRieuRcH(D=A.kdtS aΙ?oΧ}[?xw9pk{"#Ӷr9ںȈpOVkT cٸfw(Ny5@J%CCvC{.q1?ĩ3/U,Lz~E CI7IT*d?x٤xE=h _ }UjXfq0쓧Xm6tjjˡi: fnhLHd ĩ3QQCg RRkkp8 @VRzA*PP(޶ǎGEFr8l6ΎJL?}la\.ty%wZRΜJJJ,-;XT -cnJrϜ <>f7L'-5lx=Fq~ԔL&37LOmxX~~.V8yF7_kqoXEF]~l^jAA-Pyť̮+Hss*EQg&dWTVJ%U*_dnwqmwiJ C yww@V p83 /W0 \D[{R!'jChxNf)I]=Jmmm 3r)I=犊iimy9<7$8la1mhl\ ,+XX\*1xJr`TY~p-mA> ťe)/_zh2XOMx[SO<p:AH$['cC/Mn7(8_TАf9vzjJ_[RbRPp""^fX,VpP\&J$]($Uᑞ^uϏivJRO]Jr&rYpppݤ;3#](0 308)tXؘ .+JBCfKWwV0,8(wmg€g+z!@,{U(Q[m9nS/\wqzK.ʼn7YWJ%Q{',˴()Y ]/ffgM_n/.*\0o.?/G*>LFt[GRܰn 7IFӖz6;s=aL&Zb(;{鳍O)1!**R z%FRefGFZT(--QQE%'&ahkdXZ /:N@aA>Pˇ\#.fC{i1?Yv NZ?Q ӸͮT(T*eo$"rͷ > E& {|r=>iJVlN??i{}}̍lx}[oӅ-m5 ;y99tu0vz7՗n`KL ar6)lڐ1%8߾swbB׾%V{E ccD>ʭ'XXD!an9 ôZ-E!x<@B z(J?'gY>5k IDAThI<?TUz.( r\et rY(dAhR9UBᑑԔd JEYt (:B!hiurL~ jZcA KT^3NxtZLFc&Y$RdʴY_/ew|``ZbK7o޳o9TR\;[t:폿]Lp:wwKCmy ,) [f=] qI &S8`]SZttvoczZ[wl߹;!>N$כg8qDeRbBJr?pú5=}o~E |}{guKmlZzeBKLWE(_}BК>d h$>d_}Бc?tnrbd =-7~gӆέ9Qy****-5^=kuk::ln4f.~6)<\-@d||\M]}0'"L&N?}uJ!ͯ-dr\55uǝ9Xpž4'_.;3s̙Z泏>,O~ا[W]S;DGF>x/G3GD[Q5UnjVNG\]h.3lٺr=00p8zz{/i:.ΪVG@gWҹJ$[@Ȃ#G+?hqð6޷nJ&=v|= tiu: 8MQirxV)ϕd(wBۺ}gUuT;hm]GF}j4K'P?(08J>0d{{2ÍFǵ `ppm; FCnn :lݾI>08*R* q2j*U0|򝀄L4 A-jrxv9Y_?iJ0L } W~~`a Ha <rsǷm:QR\^fոVWp p  .O&VJ.{N ϗ1 vBQ><8Q5pq4I2-_X6]#wes!o/#a }?X Lp8=Ո848-*ahx8&:0ӟp4m6GlQxUvYLjwYR2kƌP1 p0 eS`qp8˚U+*^G&&>re޾>T*+N 0 ~@qaS5|~Q)3{dtl|zxAq\PAQB5úA<odVaO4uYJ^SU9YJr֯]tq̝75]XhἹ E xf/, }J"=5ET.]RiU˗fdMWkxdeٳ"f\f0L᧪j,ށhh))I8nphh|kdj$a+OU|>kZZ%I2'+slVY]Ykk(-#*аGU\^\.=w}bB (G9]@2J!֬Zu+-NJL]!|JbAk8-I7w:I~募w+T3gdgeЃϛ;7';RLMIH0:6˯))Iq.#p\PdfΙ}ּm~k3V,[2kFɛo+ N۰nR0@dKK={! ˬ K_xw'(Z-1WZmA~RuE&?Xssqӆ]{~˲`XlIIq4Əirh8qMk <]{;֨ϋ#I_ܫoB(/*( {Eff?~# 鴛6HOdef'+8znӆiE͚5c]OU*efF8|ْ;w!?/72"'H󲥋_}͚̌xvؘ۞O}GLH"%JvUS$ bPT^?!EqJT*%Ir<PT 1I^_ @U*V W%jA5o˟~0 AZ&L*I B5LF{(.[R*rfF&8NRp8zqĄ 8) ZMmIh B0A&`Y"u:EA\n7Ei5C_7Ų,AJPt8:׾wȿ!}>oek~wKr|,VUռ?WK y˲,@@0^$$IR(4MhEvh:4 BxxA0izhJP$t\&c9tF͡1A"qkzu˳x;A'/6 WW:o]QEn`'P(,1 r#⋟'nk4o`dd@A^6W"  C   C   C   C   C   C   C    Bt:GFF0;a+a:"uE,6%0&Ix{G-;Q!] BtGX.IҸ>-Q!7grNL8.WTTե##6EQq0$-Y|FEQ܌A:.=5Ecv Ą۾ؽ"$I:vD}JJ7//u\;LIN U5 ?\n_}_BNvƆ %I2gMAvwvulvI"Lio !t8vDttEQ?} ^_Ù3^/&:*7';` :F#zxrrJlpl|;~"?7'&&$?k ޿oB]{-'I3mm޿QP|ܪ0 3˗-H ן..*\3WANC5 mm-6SNkCZXԶkX8'%&&%&̋ EI:(Jz㐏ϲϟ9q{,^WU]cIYRR\Toۻ/]RR\z$ICÕHys;jشðz<&NCXX6? zw?oLg߁̌әsflnmo8◔/ \jJ?|um /fqj\hn)).Lo8}Q f 66]YRd6Gi0*{!Νp(V$9ynGF5GHIJ<{)?/wfI'}>wDek[{ z}17wά5%Imlly>l3g BuMmf r >Vƌ0 sw0G.o2#*mӜY3-11LZvFIqzjt Sm|$;^~ 5|asm˖..,' 7e̞9DQ o_xlcXt1gΜml:Ԕ:GFkrr/ݽoy-mnƲs֭8~C.dgJR)JRy]}}Isfuӷ)75vvwOE BGGsM ܾs+KKMiUlzᑽ~VvhEGgWeUIfsE9yC#'*O0'O9{[NTVfffX-1-RT9Y 9:`0822T*pΝ?a0U;Qkع /-m˽cnFƎUHd7;/nرs{s8o^]̌kf4EEGEtԔ Lqqֺg&t/Z|\lA~^TT$I';{ȱgoS;flnNVOoߞ}qoetl<7;rp|xre4gAq],\NdrRbfFzM]=q흝fsNV\&#I2%9)=-3g{.x<{E+t;tXtTdQaZfٻv5/i4V=XԩLp }.}v?*'?;ۿtqKy^p8]_y@`i3ij@xTTr `8n4xA.&%&8ѨJG`nAE, j dؠB!':XrHNs!~r()V*gwt:Ε+ 78˸\nH(AYbY(AQZ&I""IqjwȿFc 7>l^ٯ~?ᚫ9俟~J$I"Bwd@DES(,KӴ1.VgT.봚oFR !$))dkDBRqE~$58p )b'MjfF=tB QRu]6BK+.Fxr?N{7 +3=7'W_(jͪjꉧ= I)j`ǫ;ZM^p|lx+Z & ;"=@.ZMM0 z[sBE[Gb1 x-mY$q 8 ^^RPT 2(JM;pnYk0 8&I$I:VT~O$c:z~\L0샿W O` `mnmR)ry(s&-11QQ=y^HOOu\oZbnU]SWPg4k84Z-յ6j 9|x^8IF3 OFFD(UʪuWQ.*,Ї'uwt?Y[YY a11ѻ/, x!=N+Va֞ނ"plttj EeG**(Ҫ,i4K=q|||DeebB‘cٙ*m;v͟7az5&FT.[-[ׯ]zMw250 +*,hmk߳o3Hcewٷ֌$OUײhRU\&;Zq:)*.*A<3W/3(,$J nG&75]8ZqF}xN{q Alv{zZpqJyU.%&_hinX&II7Gj;;/j55uͭmIF!/'[T-6֨Ϝmk舎X-̌^oVE;:/ E;{V|\,~ ̜Qn44U6gLAW1]j.eĄQ'&JLݛI h$'L )fj4ںܜ9rybB< ό۬tor XVJt`0]h4$QS[T*/]k`684|dee,^)mܖ`BvZ&!>ntt#Cy #Zm||ة|YI IDAT?xxpV/[\dKz<^\njjΜUPEQccc4EѴȱcJ̌tV;84pL|\36sVElO 3pΛ;G%e2ǡ;]̆k_ۇy}>Yv NZn roioߟ[T`ْ+falkϕ/*[\rrظkRC_# .55屇"\۔ÛqGZUhHA r]yCAA[׿Nc:Z9($ԍgGA9L۪`A<u 'ipxTGk #ccDh/ op_AN& Qu APȧ;Anm6A;2MȝKՎ3L5}$!p) Bx \J>&Ԃ  w7IwLuzrv`! W ίŝt!zn{ u" WGχS ›ue AAͲ%X |{pf{W; Ap0;wywپß\Nɛu3Av}om{I^s$z{{;:;>a%`LcyO5ZFFF$_qV\Ѓڞy?xJKg/]Rn4>oÍG>zAݷ˯R(޿Oo8asK^Uq?XR\i0r8x7LIܙl(y^VxbB~OGI=^{ 02:&?GJa?E̱<}5Q ?vͪ+ ّѱ w^zi}W.< x.ǭqC]{/]pd&p?TBxA8az=A]xyhJQ+~_ŝ&'&Κ5$ J Z 0J9(J.kphH)rA(yr<ϱ,)LoIrG$A ȝi}6޷nxxg~h4X:tpP4ESha uZL&0L$Y 4B\nVC.F ^ (xx EQQ].7s$Ij4=R]0xesMryzw8>hH GMLL=i$I>C5Z /ItZ6[Lt4xs̢(J$i2_$UW^|nr( 1 LBy0FV! yA2N;yFp8mmQXkM:cc-qq7c?Srh4a(H.]((Q G%&$R  ŭYz)IϽҹ/n=CZvpho>᾵/ (JdU+/)_q\CÙ#*\.WrRC~-._tARōm~ֶ>z Zbb>GUJd<B$>*I /⢁ŋb{ ˦ ervvF)|3RS`UMʪ`U(K/*,`~iwO/ֶ RR>$Uԭ_ъ%%'%Ųj\(POOdܜ`=thrRxz>_Lttnn6a#]<ϓ$k{vܽt‚<7wl)_9Pą ΝP(:{')2/7gIm?rl 07eOuYWUלyNT-,KrL&+)*;g&aa,E1##@o|eO56'6֢Ѩ{zz23JEeeu\\NVVUU׸\G>`h}aݲ'>(`dtɯkwX;~T0+ʊ ~7!ng=]ngْŋxxm]IZ|a;QyYcUJU+23C E--- r9:p$n~q`0( áV1 3pe 9dٱ1χ\.Ϻn5sFq[{-]Z"IҶpx7uXŜٳv><RZڕJerrRm}X~% )~/v\t:RSV._+~O?KkWհߘ`{ ;~c3ge2Ql6{dT#oqo|M&=pp`P֯0^yw{{}zphر+V,-ϯ8~I BͮoY3J/<>{ (q\^woN 5 "#cnOm}Cap g)ܔp.).xuu33!.&'% tp@dzEGE"LY=0>Ǐ114<&Wi٬3ZvڝWX/斎.*]^[X豊C`!\nq,%|@ J=pjFrXݼv{F2 FL+CXؘ$Aer񰰰ѱlhX0c$INoH (ʌ116̒_w raf3X0wdgVd$I$ul߱+Nn>L0ID]nxGFZxuAyޒ$]\'DIph0]Ry>ա#Ir/^Bxr,9)|3MS`&&-m:/=IkʘLVK{G%ZsMU`0 x`  q$03֬\a q>-[[xAϛkutT nXI8LMO? u.8~r-]fW&++{23n$IqhEpM|>V.K˦~, ;5-,P(RrE ZM <ffBQS[AsymA:Bdi)P32ҍd}J4::.Y488tdэIR掎ή׫h/Rvrj^hi͖u YF]CQSii8GLq yǣT(.(\.˲eںdF|]}CjJJx3pcWw7P$q|JrȽT(FFq`!]l0GD:U-WȭK.}⢂6ZLhT˖,&Iq‚|VsẼzb4&a4ryccSaa~KKVըZj6n \x4 c. L.x< (rukޠF afsŵu[f߁?,g/._T\Tke&ד$sd 믨?9\!OJL\<*s^~_Xbp5=--t9l6c6q\\s?R)?li]{~?nX/[ 5:6Nԣ?a%&zQقGm+.Uyy?}O~%^ty0 \Ӆ`8<8< q\ VVUELA^yiUd2YVfzwOoGG' hLOKUܳr7(*##dehlniݳo?…󣣣o߹DQXW,[yٴ,"T*Zcc$Eݷn Iƥ8tx}jzQp#A=Ў]{NVV)⢂䤋.I2]jwIOKfjHLLo8ssvV $Idww׿S*)I"K8}.@fF5+%IK.dWUU?!';,Ayɳ 4E]ðT˵c7~ukאWXJMC?:5Ɋ>DŽCP$T(HcccI 6 ۋZ/]/"}b>9RZB$I$A.$rtмY~ F_jnW$R*Jٽwo$IJxIe<QQ nJZǰPQhJNaba4Qp戈4 >_M8.I0peBe=oĥPR4jͷi^f%*Cy5  VtF/ WP[FGǾ' $*Pg!,ry!0(j4Q,+"EQ(FF!dY`@.ITJemDQP$)nnFA>-]*R'r$fraKK[]*/7PS[+W?c$HV#L.Z$IۣѨh4 H0 BBbyHєFHði(RU2LEi0>VSSR^!h4#qnGJp# naA Tj덎tfqP(.W# q<ۻƉ0y_B{BZXHOb% HhW)VMR1Sq$u'ԭ؝y2/c+aƓI:1WI)u'80`U\W)-~~\bAܩ $8 CƘ"ieEQT*8(Nb'܂ASNƚ !Cz&@DJU069o6j<Bdn48=_?|,Jnm{g%Ƙ-97߽z`Lvu]MEܜ&9\}/vRڭ!")eꠔ2?|чy׽EyF ԟ$IDS2e[e´rZEGGG?yzΝ;/hDuK5,ThdGrb ݖfm89CeM5uBDV3yDJic̲,0Ƅ賞IczKi';ƘM {}{MS`G.ʽ{gɶMT3ƈΏض0 ˵uW|o֛k,8F=jY|{;6oz{}n岓jY ] ТbJ 8H'ccz)j9 ^޽1Ex0Zo2\yA^[t6&gZ~6-gWEXL"0W3˪7Bi UU^E` a}nɤKUeT+"|gqYmZ_VƉ{uo\toC8q ֌s @JeUVI۾uM&ۙ @H, yh<'.KAqRTՊp Tp0]Jř9i4\ o}˞+U)d#7HIDAT6r gr gr OG/^yҶ+ƪIENDB`gammaray-2.11.3/docs/manual/images/gammaray-qmlcontext.png000066400000000000000000002406671413310443000235560ustar00rootroot00000000000000PNG  IHDR pHYs+ IDATxg\II 4{;+HG@{=]wD*E~;ųQla57]!{].{sZԐo97(pLfqk-E޹{s P__qqܩG{{|֭ͪl9? XtV:`cFߍ>P%K9 񚶗:LzK,/_WW}խwNEԻW}͛۶Zh~6muTb9-oXWWwvC9z>l\>>SM A~svREEmi}C/=Γ#BaٸI>tf4ZX, Z~3^XŬ̋5:n>|=Iho)]׮9dȠxwFy4^ ϬgΝBr#N>{m>|4HtY+K?tvhxMGmG\HPcjf٥sgr&Ŝ{𱰶_y;*TCzĤ]FsrSPPxOҧXQSh4ϸOAw/lg:.DvS%L>Ba ړy'""B-wfL%4ڹDgg, MM4r⢒Ĥɓ FXqAI{۸eߺMsRVOJX4=?JJ9KWV,}BN47oXb'OR2jNYlekW()y<Çذve]rIA=;ٹJ7Nc+*Q̘>ym+>`@ݬ2;wCUVVrI0rD+ڱ.]SdŽkkklٰtHÇ/^ŗb{WZZP ˥[׮ˉ&DD@ \!<݈Z0d_y3xo^蜜˵ů_ܻW-WVV>y7rKligc<<z09!w[CvZ!GRSYYq#d( hh ln>tL,j3] "k 1g"T5lACi'!6kTD|4[)+޲{|'}J~=lî\=~dH,9pH'`"hB4MGG?hjjZ߽7BϛǺ0Lam9ݿ Xmm}}KW**zzMCVZu[ ɁdRA~!D4h|>Nch4%Bs$ݝ60З+'Pg1 C6r ku V3_0dBr_d144 s'Դ[]!t!=) n%BH_?_:5]4bQTk+/_+ 3ObX,ٗ.c/͜>󅋗4Oʏg9go=B5Cdp =GFPzAy+":Adhkc?nWE؈4jx5!=]]B===z8+//ܸy뗟^Ri*QgLL\n%B@a`_QQ!q +B\Df5$kih˗.JK\ncΝy533m05% >hXZ!ne9BTN4>\.)lT IHWO!c߿0hmmR2,FHKظ~2?Nr"BFq8eWML>_y"HM#HD"4#L&q[66Ϸp^.֌: *!=Ũt9D'[^H(+/2x׮wymqҗT;*ӥP(\lϮ$ŖQy{}}=BYf.Q!OQ$w׭^ٙ}ԯOgw_[[ĸ`"WA4hzzz!neBÿߚi MMMZ؈r!*'lHVla|kƟ޺_d1lW^k׶M~AR-ksCАJrڵmpʌe (ٲP(>mR+*""]8痟 F3} \]khh r;yw!:A>9zlJpP_y[I"T5i}G>aV#6a[QH9{hHOKK뗟:T[WW]BZHRD"Qu AAĜ>' ftuBd%c?h4SSZa%`˱'([N nrMKCRD"_ o]/\vC("*++?ԐOƇdOqBD"yFS QPh؉nԉD|>?>!)_quLaC6m B I'O%|Py{Ž B.TtNRDd0Gwpu/|iѣFΙ5SظiBFFF4 JpYtE'ZDҦMkX|mv#:Oe89ğOw?W$ œKWmk#[zdm۶iӦuٸ~{hP|GM=y*z l?Ah;_z`^֬ Q#>>f] cJpzloRT33OAm*Cu}q!\Ѡ`Ã")h 駢c4T]=݉:]bSDO9uu>8wT h( &-57 nuݱuc'Z[]5rN iZeaN#NڽOCC{x~Ȏ.,s( oOZz=1S> 4͏VV6m30wuvzQa**:r8BhæsgP>:O~_CC#)9cu۷7wf6]BSg̑>޷{;P_BNM|DV>{9&{_Qvmh[xJڽ[t.]ڦUM蘳i۶Mõ?OvΥ[wZ`. ]|~#=X4wb͠,-Cv~WZڬUp3m>V?U~>~>_; {v}$ІM[ߕr,-fjƍC->?b钅w475?F`Ԉ&BH"Wn*C?+Vm١nmTp+kGf5?F`lP#j6~@[kBAA G5Qm@^~A}(: }\Pm]yz ѫj6~@5?F`lP#.Qi^]; }r#INMk\> f7m,tuuoԴr?KwsgV.g6w߈}`7=ƺ:;cO{PYYgݣiF[ZLQPlͺ cOETVUovά&s.spqG=- >zMX2l)A ?v䀁~qqԙsVtR_g"?|8x(ݻB/[˗;w{K5cڔ.-'Oᇯ8oQ#ʪy#_NEE?Ͻ1g<|zh_B\L^=Cv122ZwP3Pԝ{UVU?z8ؑҰ! 2hdӶޞ;qY4 |CvǏ>w6zRP+C 񱧝;pH.}LʏO[&EɕMyMKϐ:<ŢRV/_;V(f^ bX,X7Kb/MKhcss W \rmR`ERMMMBzzz={HPSZQM y_GG!T^Mֲeee!co8eeWML?IDrmr8edYSDR_tvrػЦk(JbnD#|1Y9Nǜ}^"!?g rڵmsl!B)-m!)ٲ&& %<숡\ ndig'CgBv|[žbXC͜?Ĵ=9xS|>B*]?)r++cZE҈B(P"0і!={UcYӧvPQB W_"**sǎbBzbUC@y-G,]09傫9~Z`^3y_~{I#FZyh8>?w CCSg:Nr]Ĥ7oN @͙5+nߑ;Y4'wAcLML<ƺN5wJ .TC@y n~}`$b~ɘZwQ*/XP>ZUP#j6~@5?F`ԈRhiZaZ"W;wd' 5?F`lP#j6~@({7lv-&4򃇏Ž}@ՠsڶ^7oy%eߡaǟ<͗{wԻW W?dĸ2SI*ckc|>65!ukcϟLӕl_Xn^=lְ*6ٗ&hXffn IWٌqrB^YX B>^6V,FZ[>ڡ3sgtwSqЁCgL9b8NP(Tǰy|-!l'7;vzl'7K?V%iP+mo߽ ;už~ץsasЈCG ivXj@ݿ&wݿ'vm>x^Nx_Qټu;[y6*"6&ݻ҃Gb7nxg"2RO &jP7m|!d25<x-i>^r>~T935RNX,cc"Du@ ԴӧhiiiX^3rvr.M:YKKJ63# P+}OB BaXK,ckȎݻBHoc<|h+~M6#̙x)B#HOUHH$Bq8-Cuk3--/_г/qNDxuݹw2?^ߺ}wV5nfjJ޼}ں:&255P(ёZʶcll*-o׶ڭ~*jgcuq_Zʉ=gkcf& ҩ}˫mؾuc>IL&j<ñr;{UUW#x<^~A!BHKKk!bW$ajRۯolG#S>_qK444Κ>J?}&6Q:^ X!fAgk{)y>AǏ8BH ܓs J1l3|PѬ?hikyur+CvuNmmaXJ2cPb?Z8>j6~@5?F`lP#j6~@5?F`\y>7Q2媪34X d_`[TUWoش[L)8hI Aݳ۵mKRš-}, ›Ww;4xﮐz"RDojld4yRJG'$9;+ٸs4./Ib'{w6p+~5&_M"u>t:[ת/׸I['kkkS_HHLN%UԄ֭V^O^(;=EoI# yMOLG*K dfe;S(5K|__)afAP#`JFtMɗDSH1<>tZ6_um/M bL=B\\"^*f |Q*ѽ{۶mBN,m]=oݾ#i,m IDAT\B`|a #2*fv\~4#lƎi ~1nA~Q]++aRr]K9;_y.^ĺmz&>_ y Cվp}MR髉)l[eƊw)Aʝ}@mo߽ ;už~ץsasЈCG ivXj@ݿ&wݿG6eoFEd&L,[bV^_A>x^Nx_Q!׬l}U+nOM;vץuZ͜69#5i=&׮BCPT }/7񬴤z\q+Vf&sXݻʋ?'hz)ݜڋUؼu;[y6*"6&ݻ҃G*hox𙵕Zzœn`*).?xhPŬIưcwȖ Y'ZP>B`|a7ccOG**>|I0}q,+-zߴ 7S uutuR㎇9'q+CBMdK^~qLTDaG508cl2rjkkBeE%cEԻH$?hTO?L&{]a͞_X8{":)w'OOE3sI9/ʹ4}d---*LfĩhoϏK9b8m޺Ce$a+٨jn߹;$g-j_bBb"jjjX,c-r<|h+~߭I9A({{e--d ̙x)I}bme`0Be!OqpzQo޼DD ch4H*!rccRBHCC!=ߖr$  @ȐA2|.ex[Bl"e~+׬ǽ\&пw~211?gfrN;boהP1e4_?䨬[IVTv\Tr+*QD#/ZRr)SV=x6t], "Q#ss{S&]hv0$g-j?J:r8/-Ğ^Z~fwiXavU6lߺ__e->lȾy|X,~Vwqrd26Vwx555'OE4.}U>IʸvO}}?uu5551gr@ v\__ީ!Bi^D" 8=]aC  \n(23L;{UUW#x<^~A!nC r#"^ML,W\:<2J}ܤڴnC3BeXyeeսD"Nо_ P)B`|aGUUWGFI Le'HC>ge!ZKJH4_uu\nxd6t]j_DXLskocg+[N2$Xgoʿ7{4_=\BgK^15!dѼ̬lKKVN5NF%QK455]ܽ,m폇 #NEaJY8ovmƎsiӺS/ [I )r-V،q?a3`Toh!c#?ߩ3ػ*VPleKD"oľ}~dK7je0q?oWCCmc}UriX׭KYX̕˗7m7m\ϱX:Q}؎ׯ^R(S2_޽z~׵87OO n\|"Ѣ ܯ_P著kw-)#|! ݿv-AT$T{;[P8zXhɉDb1Tp+K5َF_2&\^~_iŘG}24kmܗOj6~@@nܝo1QLlP#j6~@5?F`lP#C[fk (rUUsi-r|aPU]aӶ7o1 7W D5?yz'Op.]:#nݾ{pˢb*sfLU7nv!=Sf[X_tlb&~Vc#ɓU:*>!^-Fh\lָ$eF拍tوCiDD|I4U uͲthL&'Nر!@*ٱ651.=Ν$fUef޽m6!lE6޷nߑ֑SV6>0šmaŸ5NΝOtti[|{g{}# \=}Nmvrr=,m]ܯ߸7n a;9n޶C ($- >/~~\ne^~vepH.3cFYa;P>B`|a #2*fv\~4#lƎi ~1nA~Q]++aRr]K9;_y.^ĺmz&>_ y Cվ]q]Ls6NZ?hrXܚDUwM݊R5^[WdzҒ~sƭX htzb\cw~2 +/**|c뢥gwsvھk/Va\n٨ؘwJ9د]JjVgVjO޷k>B$6㡑'B&RNȎ![6d'<j5BQ oޜ=c$`?ATı~6L\mIM;z(lX,=D#Z 75*.y]Mtı3Q=` Hҗl|Ƿ^Y[@e>fHw-!HqRSDdQ\9!%2Ə?e2Smm{!iX'B`ӈ Ctu}- !$Y,&Bba/XL!;v "}_hhڻ 3f/0>4xlk7lYrB(=bɨ7oފD"D[H_G[Z4R, B!dfjdjb"`dHh"!T)CaFi}CBHOO{ݿ--H$>H֠\C s!- o"uudi߮YK?0Q>A=1̎;(!Q0 i|Hǹ=QGR6󅛩Qb__NO!_Jt-)樘|!{tȅR_6dYOLC  !̠>Bw5!|7)CreLO[iiix{Ν>}؉; G8=x!TM~A7B155s2BU&f&߳;AQn Ƭ$Xr5ȨB{ʭ$Vb(mʐJt.pL?}EG355P(ёؽ֜:22sS/_Z~h~}\wkMJSSJ0jbb<:vxJ*L } q;½,)&VUF^R$/ {;ld&Y*h#\3.f} 4pQMFFօA + w$n"!2uORl=KK91lm>iM6VΛݥS"߸YUU!.>ᗟBhkkOJB0BzB@,wܹ>ӍGBrϟ}'ebVҵTWGo_9G!Bi^D" 8=]aC  \n(23L;{a_x2ˍcg#{ 611^h^q(qjӺ NOBGDFc啕U?Dt:Cv|@QJ9Q[WWU]}b$QG$p3] __ emh-)#|aŽGUpQvM}Ʌ;]tiמcl%[FF,~f㖐y4I($!"ZTUW/[H|gLa;y:!t6.SBKʾ~;w<}FZ=5iU+"tm2/f[]ܹ+#L}<&M :FTH.T$1+/!1E?r81N'LVFkld;u{w]=] YlH$sp طϯ㽕F }&[jhhm/](-k㺕a>{ 7krа͜9\'۽!B+TP1e U !ԻWvӡ}I$Tv1O$ZT~A}!<>#/ZR>GB1?ۯw]Z$ڗ\D !#O[hФpGšm>nҴb8Ak1=u/|13DJ&2g| )ڸ/7q8eQ>ޞ_;I*^h&L|g$Z~ ?V`lP#j6~@5?FZƟ_0`-GFآ:J\UU5uƜoWub!}Zh|T,[i>fHj>~tl'7K[i3>+unऩVlI˛Yi#a|կeuw`F\ӈ4`6U ug뮅@#`JFtMɗDS uܻ`ڬv.&*;=yӺU{R©_̾4)(@233pwKHJF]rfszš_0j%1lhqkǯЯ/Bhg0Q1lG7+|y9߸%fy(ref޽m6!lE6޷nߑyyY6.9pYX-bXntDGWVlɏ?pWwwqwH}}=B(/vl'+Wܳ%ۯ_{qǞ;Vxg\v;/H1kpSNiX^ǰBN(!n0JQrB&ۑ OWO$FZ>zb_Tees,wqʹ4p!yvpגbDW^~3n|G9^uiP/yW\WĜV}&&r+]fyUk7lf;Yj-"lߵEGGB|߭[f*!ףȝ$DzkPJq#.e}N(v {]΅B#1|-buwסa' LL|Ϟq1>!iמ+U߼9{:UQnP:ۥa/^*,|fme)y}#JKbϟ<y"T1k1,-eCVzV-(e(9_X!z"e%Sf\nNjbC1ֺ IDATgb1!yv֒\HWqo#(,|vS FdC]Wn.N?(ڊh*w? H]q+;0}j0"p0 쩶v =7{f~ayt'V4KGWgjD˓b8yێG~xy1;\ e̜>EKKK瞙EVN2 i't}}=/i5}}zRT cNERlxxW^ ~މ)kS$8RA \V=~B(Bp=]];w;wW1VR[Wd|]xY˦oldt6.Ӫa(4bpסbGR'jX󅛩Eu֝ࠏAzy c#YK9*F2_ȞP/PqG͛O!R2F ̼"}s,/ʹ4}domfTTTT L?RW ȞM"# |\@T,!TSSbX,Xאwlwvwvpvݿe{w! 5G4 r2BZFυ/6qKX"֑M}g-}G`0GF3;vdD4X;)/LR\TLeagvz IR?@kI1GH !'=P%J}Ɇ;L&mg}>1/g,72F2UB1Y25zūBSV֮m[ٚW IkS {/uKhB}oLKK˗g_t-NDxuݹwP(=o |)ࠜ˗5߿Kn BSd\N')'Ab8)傣kQ+,)O++XnoFj=;B/VkxGU+C*ӹWȾ'>р(K}OXiM6VΛݥS"߸YUU!.>ᗟBhkkOJB0Bzݥ >mi1dXسn /($)'AnS]}~=v&/ XܵsO BO% nƉ:PhP(rOFmm h@:dP=FabbdѼ8?MM6t:==3 !TV^WVVݻ@$"T F:*Fq)9MR.Eկ/N|˶C̑h0a#j*(ld& OվBmP.]tk1vؒƭ`T#S+C 5|ؐ}|X+S' OHJXU^%HVMe+ $oi:::lG7/C{ ŗ~=cj0BhɢyYoܼu玧OH+&jRN߱mSl+;[zr޽z~׵87OO /_pQ+C߀xyy9nHHL{ꏥf lwff^zֿ?FCN1]WOWbDV.["\}&xo%~5446֗\u`’qʰcsȽ5\|IhqvfUwhiz!}*E2u*֛Db8H&CK|F^ܵ|D|=}:ٮ%H}ɅJBΎ>r;x5M wd=lVъK455]ܽ,m폇$i5zԚU'ڌqi:x_(z%R_[y?7__gd; hry>A_ځe|&\Ff ||--s7X$XkRd/4-,"2k@ˢڷ0aWEŞÇ}XhYV?_lZP#j6~@5?F`l^~!_;k[h|s|~%S:cNZ6$ҢVJ~5'ۢ_U]dJv6c=F^Gs,e=fÜK^&IS-m؎/zB/e;Y:L9+?V] yYXX96:Q IZ*OXXGzzzlኋȕ\?HlY9&[ZXg[4/(B=)=~tޢ߭؎#n|32`SMi=6k5i'&/P7۸ Z 7kkkS_HHLN%UԄ֭V^ORfŮ.N cvx ЛoG[9߽{b?4N6򾢢__S#G4"l\ 3+aJGx! -k}~LMBiC *~5&_ e\ܰyߌ )̇:cV^.BMK6{L5wDe'oZw^6TSFgmm9m,/$JKھeo7al;4SPsy Z6ٗ&hXffn IWٌqrB^YX B>^6V,FZ[>pf_dic} חBi~TUW#F4d0# -.xۀΠϝ5M6Š #cK(ldf޽m6DwqQ\]>YzKl1FM]*Җ& h41^5 -vc슢F:Hea`fK}~|؝=s{ܙYxiԯ]<<}N='ƎP( t ( G|:I&LwnSVV>o>[Po`GyJΝ~GWO /vŪ5o?7Oߟ~^'qC'OCGJ8BL?рвGB2O5:8t9k ݔ#0~䨠/WH) 7Dظ/?'ג+kk)h3$1{Nwݧ̔ M%}mP3 ܅ʓPdIm甈C  b(Y2tq{Eex9xoThyncllp>ر0N=I,Pyʶ!Q&+2x rڶC{6eqsvun~~̹&=F,/儝>} .X,?2ߺ}ՓؽMWA]۶۷~ C2xUk1 [l>ꨳ7> zLRvl={:tLW3fH?u$1yæ_?ܼz{o[ltTWE>?sUU5lHL:)aVVӧNNOM^~ru (+ڿoOƉnv]b5}ɏ+BaR}Q;_3z_=''*рQs735KO97{͸Oז?[TTu.r\/7igg?qvrԚ<[6577ܼ#Gw'9kK׬۸fՏi4b{tbذ/|\./(8?9/ȁ߼u;.&*Dg>YgL͠p&ƩI {vn;p(0]<]/5*I2s:dy|AtyCGaɋtt9|ꚚCq1驉_Mngh:;1L='Zg0T'Qgz=g?ohG塆MY,흚srT*^nlda\v_mB-ՏkddT]S5c@?x3O׮GL' LCf!Abv>{~$1y0g c)􌋉JMJ99?Y,%99 ̴ۧp}PL<А庹8?} jjjΝ1>\( NJymSRxq\z"}W_AOf&uuv{~Aa?07z-6oXcccoQ(616fMLP‘bE6m+Ө+W@`jb2&(T&-'7E:Muʵ "ب~fz$˱,,-U:{VqWS&<`r@,̮m:9Φ?s|*O߀Ց{}Y<$)Ei$O7a?[jm۴_+&+kP,N,6jX$]n5t.Mn߽7'rs?d%%`gkjkSði3u<0b8T6oҥ?Z1C*mwa5t \-̲Y(@S U*F!4 ::* 2nڪX07k.bqTьWP'ـ?rDV|Ū5362&&ҺUKYkE"Qѱq!v<{6m>`NL60^: ES;^En*{;[H"Ü3Rk!;^Chjj/^.9PⅶZ[R(JVclb RU˖Ľyh' +o cCƆu v `_CӚ߾{knL؎v024s Onؐw]pv&+k᳿>y]s/J]蕫D[xƝ%d%۲/ =~:&LpYbtk?۷TRņbfl0,9帗YyyEƅIOMe:|#sM]KK .[p(J& ϣp8=)3(zJNuݾm={.\riL\_^XϞ9ؑCm۴\Űkp^(_d%a2xQfJ%M3ڧVU臮XJjpHrt9X[n6.^ӫBfKLgMƚ֭w0O=3U R0^ݦO|Pò,  G zS&,p!=ql֭gWG庹8صXz KWbKtqݬ۶ݸe+1E~\zE^=NL{}RY]]}`h1 Ru .\\QQUU G֍A Xc̲Kff]tf/1 ЮZNngaai4Jt ŶJʢI\Y=gk``nIɚG)yiYYL~w7:rάܼ8ʤZط i'3@VR[Oyy_7oT*PAVr^ b5^8Q1 2vÆb2Sb3w/ ]^D?tR{E2b;Ϝ;T*kjk%=y:x/> ?,XMWG͘6X:~/|P‘iS" rάsOq'ϕJ.cMrsuBẟW<$/++_p\v-0$|$08/,Z0A9a)e,ILJ9ݦogg;&(0|Ҕ `me:fʴ>&&ds:R<}DŽOճG`#mei1&|pgKPx3{Jte(XpwT[u>Z,6X8/r=#ܦN&p/nT)7nIzDT&nx ZuPC%qvt:-ֶoז.G" F| GW=ۙi%9>r$b\s!>e,XfpgšeY-=i4PNX߯Ooi~}@Rń+)-+yV^՗Ugz@@9= p™w-%M.56m۲CMea%)F@4C\]GMzVNNnSY"6T?@4LJMִQ?风P?wZhF@ fZhF@ fZhF@ fZhF@ fZhF@ fZ_Gpx?&:1o2劊)f6jWFy!-z/?,t>r(fw}mpgn3g'O~=SwF!;oborJ\mn}:朿>*.{(|kYLMMݼv'7CY+ɋT)yNe<?R*Mݽw׳%C$!qlZ u EjRBO'Ke0u뎫ӱ v /-a3g߯o{wm⥃uv<8Pa'32=ګfWWsaip8o(uȲ^!4MMܽpվ>s-6B6s'׮Zѯo hSg&M7ls,-+s9K='G@ :;=~\Oo?Gҹ/(,,* rml'ӹt{C ӦDhB='3Nwԩe f̞|5͓O#~p9L6v|B,,0LǀҹP 8|47`$l{6ee.qeZ Y~0~ 7mqtp2cMmUk$~n?N.N 6t|ġ# -++z8 $,YQC'=G .۹{/{bXڰ/|H$; sx-YB6#GG$ q}L͠T27F:xe>0]<e/sKr^Ϭ IDAT=`1#</_Y[+ aec< C Z kW:m撜r\iұ-](Ӥ TUU%~+Dq1Q'??SfJn061NMJسsہC QBe~zIGej 㕛_?&`\LvM R)hR9g9;u tFdiBܼ?~J'.MJZճ9syCO YI'S#_FFF55Y3?Ξ1kP( ?8sTI3_Mlhhr܊DZ,:I{0l]:L D r6>~2|TUU_v=b8Phff5333'\.>Ed?D8S&Oċ39wBpPhnn:&8)W>^OJr?77/ry_}ihhh(?qEr`S뛶lۼa5ETF~7+h%).ZZZmӆB:1ڼx &&cOeҊ2yxC8^T\P/2(Пg9Sc/h(yxht,+Q*e>x'҇ hfff\(!W:=K=fkkpZOL)q;~;j Oxhۖ{\d9BxyL)*~y1jcc={cGmfsRHFy!JH lrQbT:_<]/lE‘d"t W,TrwWn)Kgɬ20|íwN=gfjB<f.9F@ݦO|@ueY7}iOD庹8صXz ђ+]F|7kFm7nيo<}vѲ׮^ѻWCClٺðg?gpTWWm_ oO9 vo{ww7%o{w3Qx̴k[c^={l߽GTVWW8X\.0Cvj:ީ/-+B<XGΙGOT B0dJJbb+y[R ZRHF‰Q(w6.T{Q$:'%9ҍ=1 ,:6/14<}cH6th߾}6抷4e*#88 ^'w7WvPPPx]R UU'{TPuWXEe }"sӦKBEJ8?]9gɌLy ʧ~ LF傹|>?cO>:pH7/Ok/v ,fffpNʰ ]FILJ9_4T*uq;n~kgg;&(0|Ҕ `me:fʴ>&&ds:R<}DŽOճG`#mei1&|pgKPx3{Jte(XpwT[u>Z,6X8/r=#ܦN&p/nT)7nIzDT&Hܥ+ㇲH9^ >萰ЎvKG  <}cH+u=QZM Ce<\uu#>$HW"JWTZ{{q`ɂyGC(Kz ST9kDKᔖ߼uw/ځUgzYMMD3'㐰 W/yB|0pɌ2/X#tXlD'{T##S`ܤ)sr}zZ t@ V?@ #Ѭ@ ?@ #ь@ ?@ #ь@ ?@ #ь@ ?@ #ьx ֣}8"#aļ ˔+**L٨_ C2#\Gn`yϕx9zNӧGp qPkjkWYl#ohsh]ff\mn}:朿>*.{(|kYLMMݼv'7CY+ɋT)yNe<yy {#G-\X&!>z/kmT(I ѻ?t,βy;ҩSy q0p~A!qKaaB3@>}>aP(13-E7IC W3rdF^{Ԭ^ j_w=l6m "7CY+[R Nw?D0쇅߾ě@_V<3iBXlgg| Ξ}diY<3Y.N" 407/w"f̞|5w?p(aڔkޘ=g.8z|m$EoGðgЬ/'3Nwԩe )ϞdcG(J0JB$/߀N ۔[c;j5d=znos/ߴ5+Vxy:\N:xY:>Pbg=zyY*v;Kv^!6, (bH$^KdQ:IBGܽw2Sr36Lͷ.>A4r",+OCKRG}~?7z7WuB?xE";'~yP8l)-Ug;eyƱ K9q2wv=TimuN|~ aQQ]]]mo 8dbhيac;}QWn^{= hfjc+/^dzRLKYJhղcy*㴙i4Kb1/O s VVӧNNOM^~ᬙP:W(7ߞ>lj|W ¤v^g8|{NNneeUڱ|̝ofjro qV-++? x]^n)'~QkvԾoٰ rݷggޝjX\,]nU?f۷gӈa҉acrrԁ933n~rSXTLCi@t3eDCCC.39wBpPhnn:&8)>^OJr?77/ry_}ihhh(?qEr`S뛶lۼa5ETF~7+h%).ZZZmӆB:1ڼx &&cOeҊ2yxC8^T\P/2{a_y" DΑxAC,CCc^R)8,H=>d@33S6B 2'&}9BSӷ=g?h٢FrUUU_ru҄qxY|وׁX ryXlbH,6f kViGzyJe6~ؼ~lmp[]Q&&|_3%+)8QILNqvrDBS U*F!4 ::* 2·[[[07kF_qTь7hwlϏH 9~"ubL[d^$E>kγgLo 4j eaasI @Z>E^䦲%$2̹/3-AKɩ1jwԖyz"Jhұw:ݬtlF >8YM7?OrhXZȍ3l;94 ~WvvmO_(0w_{Xx߾},@&+?*+/ך{U.#~ x|bb|~YaXrq/c.] -6&=5M( tZZZpܢ\,6\h6'~,~tz4ʸ'oݾư}ho۲wϞ ,\Z(y/gxQˋQ3;rm6B66z΋ ?RBy)qX6j]OWy^bيt#Ex鈡CXdjHS7oݮS(>ߗΒY]e(y Q +lr\vSS[[\,= h.N#5}۶l /WTT@eUU‘uc^={l߽GTVWW8雔bwo{w W<}_7}=hf/vK\.0Cvj:&/-+B<XGΙGOT B0dJJbb+y[R ZRHF‰Q(w6.T{Q$:'%9ҍ=1 ,:6/14<}cH6th߾}6抷4e؏[o-4gblܧWϝQ{JeUUWTV` w>⍢fLjll, ?x~>p(H^~>(~Y'32/\|ڵN0@hAisRq\tVV?333h8ԋK\O3hfObR7e vvc'M ߯O@VVcLobjB6 ;c*wL^={ f?Vc'wv t e\'qq>sDWCbe wGE߼u[#ʬb"w0moGJr_$^Ӗ/^B66xtC!a~!n=i|8:(3%6#ItM:ar=]jjk_:~({}"N atP7Tߑ^j!%]Ѵ5oztME ѻgOf**'7W#oNiY[z-$:mjB 9W/yB|0pɌ2\s:gKXlDT4 y[*@eUUz!h~W0nҔ9k-͂G/\B$Y_kEZЭ~@ k[@ @ D-@4#X=7,M@ @W@4#@ D3-@4#@ D3-@4#@ D3/ ޭפiSx 6,S6p6jWLnx??1seS5)y긧*:ˎ&p\xZٖ̩w0+n(UcSn`{~3YZ+΂2&~:5Ý_,^AjIMTUO[n(^awr34Z hhȼTjtFYf 2:[b͗wDW9A- *0qre/!DYoٚ`foUDԉ/r˿jтGgik9[|.|ݓJl'fBS>+U_p8]̹ N**x5?dD<l;Y_Sy0SyWqFafA{rځШC hhȼ}Cwk^m_/!Lvv\3ݒwEӟdVA?myr5՚Ո~.+ʕV5}DsT\l+2̥U},+gZgfJ%ϫ1r^.]80\..J:5/ g-/+Хm8S>5Ԫ`#UVPC ? .5H뼃1GKK;1WmR=T̂t:J9 sUA ~}Ԝ؂W-K0Zn':Ҟk+<~PrDZTeU`L:̀iT"δ΂f*%yS u]|~DΑ.5`̍&HQ ۖ4sXח1E0P},zMBuRlI׮:)یxBp{ރwD ީ&\ {`zO{ZrE<^xבoz[q*0p\|jMx]8e Dܞ?[)gSR]p\ȭJ IDATjM[c\adؔ|ftxzRb%yfWAR~k~ _̃ƜVTkJO̹Ϛ{"S@G.<,J7ftT*5a뾿h>+9Yn'O %EС/hCk ®GvFE݅J ԯ-ylBT{C>tTR^WO.OEK1Np" yU\ GsTluow4z}X,%- 99 )@RhEJs}OTj4pPƈb=U)0PjV;ڿL95OqxZ%Z):rm 80̞wIRϼ R4ܒpᗇjU]FRԔ*4n"NBWaNL60^V0Ri+gЯr+C/s$<*nCG^R)]B̩%/!`ۖdVEW|p穖4W2SOk.痭|q_(4|ϭƺ[p?M1=M "o5jhm}\Fss!ǿXz|G:biR TK!Ŕ jp:Ku4Y]?U..[ݪ_ R0p*XuWyQh4SqJ8;e hZgC t"=UN)җ"C7]KoR2)F</'8ivx|PNJ[8թtz|G16x†7`͗>l6V(Ž!8x^sX`8JZ `&eKVK03g:/-7Z(*k &NWs. j5\Űkp\0s?!Ub2Sb3v)H.Sy^b#v|A[tt#ZS?P_RZ6迅y#W%)u~?5Ԫ oZ] snɻ*~E 6B7ݰݳa;]V1sDS:]Ĩʩ 4Pk4 yjً9+oMJ-_wቸZs^I{i1 ʕ}OTʐ;cSԜ.TL~} n_OhćZ$窟UOj ,#E68OK)S5G~#*d_ *4V-)UKuds:6WNwV gԇfeZ5pP= ?\ (~-WS,er5lӁ\ƈhk5*RH~9~  _dć+2 a¡̔ O+1ݦY3ȠT9cRҫ:~({}tOJґCKG*]_|ҒhZ7=Od󮻾SOʉ!r9dpڊw9JLQN1Xe MZZ2Ybﺢa %@ҲnՓBV6!49fּA fTrvmޒlD:!&V"O>wLWs.׿K[9ɹ2ؼ3s"0pN= [@A@@ 9#_ oX-^VM'@ ěvGhF@ fZhF@ fZhF@ f;^=;jxM6 :1o2劊)f6jx;-L}Wl^+*+#X0}]Q <\<}8 sj?/$qpt}^٫e򕫷ح^Gߜ]eo- <鯛׿fhT9yt4y1*29{>hB=oh4 %ѵ&"6g^׬ۨP(Rwo?~"=X*eAa~K;vS-epwi)߰C`Ҏ9}2]^mnzUSSzM㇤Wsaip8o(uȲ^!4MM߽t $*ė[7k_g4yf~ Z>yf҄pC6/1=Ҳ2xD*͘=7 kR+km;gL:t`Pp\.y\Oo?Gҩ}5NfܩS˖-R'OFL'sRlRbc@\(>0I6apy x߲uZG}C~??Sw8zz_x߱vŪ5o?7Oߟ~^'qC'OCGJ8BL?рвGB2O5:8t9k #0~䨠/WH) 7Dظ/?'ג+kk)h3$1{Nwݧ̔ M%}mP3 ܅ʓP9G(+c3s򕵵rfQ6a:PĽ/m锑u҆nSt+]9H2$Nhhc+IA:Bel濇~ aQQ]]]mo 8dbhيac;}QWn^{= hfjcյq}?\UU ;wux?wRW9Ք:Fb1/O C `ei5}D?_7X[X[\sRy[8ӮVƷ/q@(LJ8/jF÷VVV;0wff)GFzݰ7iڲCq1bEᖒzOfi'Om k /7/}{vIΚ56YcFڱ}{v:^!66, ( =yN."r 7oݎ8YO2Sr38\lqjRžJ0r",+OCK:(Scܼ1Qblh ed0Ӂ,NWmLVBDat O7)Tgu sdH<"W ՠJ2t!2mmC?;60okj׳fL=cPo<U*ܼ<'ҧX4dir\Wg@MMLp׮oڲm566RlccxYӷ:pXjiiѶM 4j >6D  GD6fW,+Fjae w>C:b1Xl0wͺ֬Ҟ׏!6lyYI 6/\-ڠΜnddcg?γT*j%19Q$] vTBJh~( qJb  *.j-)U\,h4cM-55770'CCHjX`FDZj>kiH$޽=:6.$|B.gϘަ,҉i\[璒th}jNj2SMeogKID'a%r L;jKG!gP%?ҥP^^1ѲqacSTgx@i|G҂5dW,6\h6'~,~wtz4ʸ'oݾư}ho۲wϞ ,\Z(y/E//FmlgϜ~ȡmqŰkp^FI&+2;LIJt^'%H7^8R QXX1< ӁN$"x[mehH-t=$sd eSC6?˱VF濄~ ?usqڱkOMmmq@aW'%W8n֌mnܲ.\\QQUU G L{}RY]]}` Xc```v暚}0γAl^S[aس3Ջ?.^233ڥ3.\aXvjurý& xHP8d/]WWWZV/NFӺM[**+lʸ|^ZVͅ8ml#ˏ2-Ba ǷWuJ nU+륐,^P*+*+c8l(] (3%6zIu*OCK,s/{bJeiYYtl^:bh:yw8t d9P0J:т5AR CC6Mh܊,Y |3M566x<?8p$/?ڔ3dF態\>I-?]4T*uq;n~y΄/r6qʴ}z}17n֌V-[x~W_s#|Ǟ}tj%1)E焔2;;1AᓦקP k+1S715!ӱHJ;&|b=l+K1;]|2.Ǔ89wSCxŲo޺eby;wq6u7|qcJq˯N/GWi/K!Y gO~ء0Z4>A $ 0wF.5\y?>GHqFv^:bh h(K B)/b.!F5$M E>Hu`!V@N:avEY ?s\JS%,kpJo޺ջm $iՙ^?C oGC&\p] уH? KR(?BV?@ #ь@n#@ ?@  #ь@ ?@ #ь@ ?@ #ь@ ?@ ͈~s"?B Sgwa7T@ 7`Hi iG /}Ǐ@ D3-@4#կRn߾i@ kq} O7߾s-͛ZX%Nu|*47Ј;w{Ni1 C>@ x|>ާG#ь@ ??{we1a&a}݇R1+G$$thk۶bHQRrBݽ:J20o;;;kq5㏙}CA?ACA?ACA?AJo߽_hDR@$Z'{Ezھ}xI?h2-LM]]p[ܔf }ܿ_?w'|GŠ? ^B4"[;jApiDnAou," 3#ȕ*KW땔MM;-u~rK_L2y2R^)95ıГCApXl|̙B<}p"ϟO<~ԉ!CjjϜ:QQQqL@OoKR.]03M˸SZZ{ÅT M2)'7yK1\ Wx4 גԫYDNyiH*uo #RN79c R6~svKv5rmA9ukUT*((0Y[l)SS#.ŋޮO>jjv7n466f乯t%x<[A_} ɝ3{HH03;PSSS '0e $I$j(S^%JMM LD9Gx"554S Iy @d ԕd#/ B]S IDATmhjj iD*BK\U] k!᭭--SRRgngGjԽ>͛^3zHxns/~%o%!B.)TT*+'JI whȹ =׎3fǠAEv]M |ǏD")((TTБ=Ng5}򿲲J[[ K@K}:DRGIdæ_ .hjj6uJgTkpnݾ;7G{#>}}TT|\%aKBPo$nʷ{Iڤ9p7AA  9"aWv+,z;]wpx]] LfOם_^@PO H1Cԩr;o`oD[MaGGK[ɩȋl6[YYY]]Ep8uҦۉksÖ#&ϫo|FY,l8ee綣xxDF4}#"[]{+*K\VE Rl3M]~:ϵ}pvHI4_o $9 20A>+_7yoEQQq dKoཾ~$I^6Ξ9̫{&L/m;KV22b"\#iw]%{bwuT3s+7EooX.Y|%b,mḼ&|ܗ/Ph3esugΚYqGl6ӧ+ ><%yɼ =|0;7םS6Ps󛚚ʒҲI?f55^16ڵ/å$,(x>}<>&*ZO 8E& t.7gޜ`.y" 喔Ɵ1}*"&+diYyY 1޽ Bqc칸3EZt%*rǏ%EoߙjSS'O?55 1>@87(((kYkVdqvn>J\y uu?PCT_߀rKweX,`ybs  @];1|ra'?{OGgQ7n\Mfld(w.m-@8ɼy뎻 SWW[4%-wc+`0&T,>뽋޽[::أM߈fdjeێGہD!I]e#2Q Ɵ,(>}F.ӯe͛;GMfJHÂccffD>hEEu 7mk+hnnF݃A"QPUUſVVUH(GDLYYYşyDZJ1rgin :=cJ3NM04п~-8ПxTTUtctLDޒɚik}&,[+~l &25 B'OYYfbt%cddfmonGv^3M>Z[[oϾݾzm9Z q)Il4" p֖;wM2)yo-q9[eGx.tHjDA$f;;- a0/tGW@x,q] :cgk}豩S&)))u Ig0I?AFJ`kacSӬ_\!?ʮ`\.wİa---|=)++P)BL&; ~`Anop8Lbҕ|NfeZ̋%3_yD ꆅdX\.I$Ma<MMD<ć?tTtrj0*+kk눪s>y&:6fLE)gbTfÇ=.Q6A/31MNuMMt\<2Q &.Ç64$qw)"?ʮvrwY婭EX"n͛NDrrqko %Z5R¸hďJ1bᷛn=jdXHaCb0?MÇ!G'7rĈ% zstW$.rvnjءi\\;y݀'/}O#$;4bL#aeM?.hHpDi Jrrj;2ݕ$)aɈH$?zхC'<7JFu>!:ГIJ7>[~,Ph6KsۑK #F2d<(L?# ?( FdTVV9:[ƌ0otM[ fvK?xKA 2W&G߈oDr";Q q{J yWsjjj-2Z(,zkmxB9;C ͬ߹{.]rWI0kjjGp#V #9(ngīS-m,ЖxJdMwn X<^҈bu=Vo_z2A9*  ut_lcWAAaQԥC` RsTE55Kh<߷lb>]ILH<($\SC#+=2p(tHP̫g")ș^I<KExĩ6SVZxJȸN>uyi~E9"DIIiĈsSZ$y{Q-=%)*"ƭ;sHL,/3 wpM'b^K8aH}X.%|lTdAx$!;[-vղҮ, 9Rw|-Lү!{#%"R1bzA0#a!&Ȍy|LTԟ&pPdM;`՝;kfm@#mWQSSc#pP+MxxSé'uYbN9yHq fiT !dM͋I+*Clv,5 wX8;74q8[7oRUQ.h&**}wnG)))}|w8NM`Hzwy𧻓/2dӯZ q-[y>}̋eb$Mx\njZMy[Sf}Ŀ̊%%]zWo! {! nA$WAArnA$GAArnA$GAArnA$GAArD> ?A)6 ?,輢t¢u y呭B\tqu6V{0bu痗`:>c]rrL"_w^1X߈g"D"̴omp 1%Pj~ӖxoWy]幖6BUktm6jeDF4}#|F:%wrq{SX;t﮽t^ʼF4.+Lknow t{_؜fau6?kKb&qEٺ|æ_˥ͮ#'oO톆l6[YYY]]Ep8uҦɶGyϖ(4v$"&?c^3Ax"^ٷ g+IzP\9н_/x߼=?Y5c:o߽AB<,5cls4zO!j̴̴yiD"q˲O -Ɍݸn-2sb"\$`0gJq8N <Б'3e\Mp ~1}jVڕNܾKم jH<{1rJbzrR~:;؋j@]?sPͼ7v_Ccs2ɥHH^6Ξ9̫{&L/m"A{]޷fic@5PSSwl@a[k{ɩKPh]_|Eg07q8k,&D11ܸyjٱ>~,7{P n X<^҈bu)R\y fko|@] v> 55-6jy-p]6B"[F4 g Ŭzbns8.3G7m1Y-!tcæ_,qGi48zOTmrr1q ?O8uS ,_.*Yn㺵m[ZZrZBut$^{[ Evsun1{J yWA[c'*h;v+l*,z \$ IDATptR xe,klDy=ȹ#[C맶8/;}ڴ)VFe/U2uOAs Mi67o9f`lnf{::/RQ髠0zԨt@7 ]ټBC ?)xwt&y1>&+=y;`׾,t>6*Qtl @|6x VږgjBojj0*+KJ&z zС#Ar3ƞ Ç =y p֝9g͙s-W[[ ާFTKOIq/|מ55bFSa'…MCJHg0{}-#]~DdEŤukVr%%fL*U =k@/_ 9|ػw-f߾}g0*+32XQŚ8O%Ğɽ:q¸=9L"/3mqEMcFNnsthst.++?z`vnK~tGJJJ2^޲mQ-+"Qjjj_~clD.@ \{pI_cعxz OGd޼ué-wZ FU<P?|Hf_^@ x%sC>뽋޽[:: D4H$o\9bX}N^$ ׮m&B?G!WӯyRMHԼtA:dQ9}ڃ޻7wμXM:EQQ냇n+p8V@:t?(B\O7te14'{ 2oD32ܲmѣG~֬*#BSYY y۷o߯L&@IIɩiF4KͿ0v$K҉} HR$lz.lⅠ̝C5];1|r)?#S̜>̚9?Juu5@[,{ 򠏴?, =/;FNhkkB* %5ZbUT[[[pC޶9xuС#!AK' UUjjD[IW*IlҌS2 _ g. ݆RRR>}2:.eشޛ3Yڙ3?{YSsaCA lOt^O hk#/P: T-# U~qsuLy6x݈+~CӦL>}yk@&"O6= e6=r6zԿPNǡ?,w`Ӿ$uur]A$TTUtcݲ뽽fLqG a0/T[ZN[[KAA!1!v+/9TUUu6cAܺ'wޛ:e2mUU5*|k'._7nU]ѧ"/CZZM^8t^ӧ{_zs !<,xf>MM QEhM$J˴':C~:#o6TĵxVN7 -0 Q$<lٺ};n̝Ctjj),-:-2y· e?ʮ zsuO2Y,.DUչsf.ᜁ2Wvv<MMZ~q8\fv.QY=y 4cV¥9JU9ߖys\PUUTLt&ׯ/$§Muihp"zD=IA4i1R"NaX󉗌߾DׯNDfM'"O957mGy1rYyyn->K\̞;{&D*s**zn F =<|0$ Ǎ1bs}CU gJigf.czx'O?xHG[kgfΑ>`0CNԄK 5cz18%>oǶTSKj H5$HxOl4n"bG<|PIZ1g]x/xs2R%~`FQQ t9%<2S'o1#m6$n6mGQkF^ts9?O,r8xЇD/ڽ=2D1\S;AR]Mm\ }+T>}lo+x^_'/,z]ǿnۙ"2e 1|L-G&C6 N֝vv.qܒR `ݓ该Eoh`8%1ÏS)e^|" + t^Qxvh J%0.]BG ܎>¬}qWUQ H`kܑǫl3pJJ6֖+WQO=(b&yHb?-k .[@wn۪`L M9M&%нk>jd-2a2Yjjv$efzM3z4kjjvs.66'#FRӦ =5ZVξW8;ee]I7s WS#{P<$syiik~4DtHSSSzJRײR_T+Ico;pY$ :y ]_\)Gj.5rPͼ7v_Ccs]{;tM˗SHxIIgcr4qހH=jjČKΝON\Dq:t|l@Y3 =n(!;[-vղҮ, qt9y‡1*+ҲO 1Qc޽{TzL5M Mo/dkáZdn?5ծ}X.%|lTdAx^DH]<<~,!Lԉ?~E=Б}WcDR S.~;>8lP$޽+ӛ3OoN@`0lQu)xxާFTKOIq/fϚ!>"ÿ~eݾ}jjg*ºl =|0;7cuMԾz؈"\ɗ)g-P߫pI_cײ׬&wlP~rJ2G,KRWwYf8?}@`0ttjjĉ#B\ݙL[w]]iiJڿ7D^.]nH i Ťt !C%VQVVB+Y]z-8^ opaJ" _<,pw[ԈbM֋cS"XR?zɥet@jlltY4r%l5=R]] " ޾%IHpÂccUT[[[pC޶fQm--E‘No:"sQB$-QYUE"yARW(**D"Zx5I\[Skba hll",wD,ҪEΏNDDfvNtlO[[[սoL&kVTyGte$PZRq.+æC ~ɦ_6Wwn_~=C+Z:(pE-[sQ<^d" «06?.ݶ%Siut:~;Y?-MњQQU T-o$']_܃Z qn_MM Qٿ?@MmL#gԻhɃuO[kK^MQ$3YkYّ'xSe 3` /doŅC+*~u;?ޫo\VDQC`N/Մ%\ji7m8z_]An r ‡ v{:lǦN]{so7 "<f:˔-2yٹNKå]"BiԱQH{ΧlB 0̢PTUQ6erd&kDڌznop8Lbҕ|NPfVΘѣacN3=wLLS]SoK\8?7W7,$rf\aZZZR/G"o oUUΙ}"tcccuMMtl<͘*UPtYqjk<}܌ *1R"NaX󉗌߾ܵ7J1~СG‘~{(L<3Mׯ__HAܺs˗ @E=* LuMxNNMcXFeemmKDi$ՌHw/oݼD)'B_Q#Q۱m ԒD"-27eԓQ)XlSQ;vٍG >l$G%t9Ɗ?٘6ƎѹAo*Wqvnjءi\ۜ]Vyjk6i.e$έ;w\Ót!:w,1bokΧTڣَ.oD[v 341wZbLL:/$^?W[[M/>Ț":}G$p AAAL[Au%CA?ACA?ACA?ACA?ACA_WWvL*,z;]w>J nj^W]dG;t?Hf Qr;o`oD[MaG%-Qh (4%NYٹH˩\]wfBMK{ؘԄe虘L恠 INAAǵ}pvHI4_o $9*'O{8{K20a{9uX?} l,l"OQrDI?x_\΀jfne㦸4tM[ fvK?x(Òw:ս7^)st˿aid2Y, lv7dLII).<†Bص76kjj-2Z҂n X<^҈buͣEo]H441Yܼuph;w `JEQbIrrrW/_ ,\.W:ymǏ%F{ D ?>tHP̫g")MMӧgf!礥gSO^Y6֖C{Q-=%)*"ƭ;(yOE&eea0ҲWbDƝD@pMMq_TGCP'c>]ILH<($|׾,t>6*Qtl{{-^NEӛr:/9}@Dٴ]֞NEY'}{{g utUz>Rq.+æC ,oЋx9Vi2*+:Z[m-(yO0hPe+ܐ ADRG^ɚU :y CB$+>B? D"y- ` y^QI<^:Б@y} IDATYu"o}šClu .ȿq+'7X_mm=+ғDvRɻʓL}(`R^BbBrc*?+V^fHUU5D} Ϣ_A#W!Y" pwޛ6erGonnp d=zfjB}b a\.wİa---oөL<3Mׯ__Hꌒw=xSX$|8.3;K@•MCau&Y]ז:QTtLSW_p@:wkjciTpHVGD^y0YIWr];IrTrnSӘ,QYY[['m/(䮽TfÇ=PR4s/Q:x?vM'nkٳTUTm'{UZd/U)i۱mo@ Ԓ};!kj,w\O#ʾ۷l$G%G1\S;AR]MmT˿^N^L\V^u'9gΞǷz ڧϞM3#c6 ?@A}˸z",dح;w\AT:RiB=G Knn;  ! H ! H ! H ! H ?CA{+~ #RlXyEi¢u y9ĵ A=Sfl6{OLGBpe =I{6Ϳcm7EGwFIdjV\WlBX@,qmG"nniXB~&iY;p 0Wl6[YYY]]Ep8u#+)6Ӧꌽdjc\ٷ g+IzP\9e%9ܓϽm=sƥyWM0^D>Ұ7$\z{}//,7{/m546K8'a:Yz}/,zkmxB9;C ͬ߹{9b,mḼWw6Itg,xyf?}x.cISQ d^JO-PiЫflpDMMo(ص/å$,(xK1QR8~oA٪ӛr:/9}Z7,KO5@! :/7jHE9&&l?լLtlٹy/_;:@ޕ:y/Y[Y`0ӧ577XY`0Y3lv,5 wX8;7_8M55 D>H$44H_S,sbDUU'$+._}@`0tt: j mE48Ouu5@[,;ɼy뎻 SWW[4%-wc+`0&Tzwlᰎҷ $CTMHԼtA:d$\ttƎum޽3nf1NWn'Ir)DEPt97TJt ȥ.]9]tQ269g'ly}}>={lk֖kmn6+//ޫUhf;l(BimQ!)kEii1,w%W5dp!&7 ٵ'-5j._gd6k:)++XY gs8!-B` ^ή"nUUU"<_#nܺ- y|)|f{7 N̩fr\f<0!/r>|GҸslڧ9˽! }42RbG骪c8Z^^$DB#VB6o0_HG=j(k@qvR+++dr]2Te㍛MƧ+l&՚`d{ߔUb钃G=.#ٱO^LMMY~ SiԳ]=}s}.%$a;ZYHDF^HT2htAs562lqBkV 2uzEEuW#QM9Ÿ@PާwНz=p;C4֮RSAw;RiT+ s3Rs6"V, i4fQ-_-=$eJsrDݺn^egcj>Ff Z [Igφu /q̬'m_gq΍ ^K ;SOO\@Jf3R+9vVv{H$s+gme0ӧ\Ys֚W͚^K\Y|/lDUM@#٬=K'&ī tTi6kOҦK ~@(9|';~@(X ?@d^[@x(2wd/~G"ߪjygks]^ۣ!Ml'ߺz4|X%)*qHZgnl{83ɍoHom;v|!+n8;z:ҷ97R,)*ll>r~Wǚ.侐$}˂e4q`^|x~Oܥc;O$ t۾%wPi`J}}xk0vB@MYUUUb ^B:J Uڝs+O߲ERD")-Aiܥ+ԙ$dAQDc/kQ),vT!ҋ;4Ba:>QU+H-O-&*u@bjMnNrCR)Du Q +*̧L$9U*yp'E0 vW^=zp3e ̥9f$IMr݁-+;H읥Ƕs*!w,sdWTj]N(%0| bNLl0Fw#=e[ۯsBHII !DSQ-[KgwO_J BZjGHS'|ؖj MCPo :t3xN%wPi:?~eK QE%J)/U]]c|iuuuUUkU7v/ M<6Gz4x?>ח3&F)%g5pwHu -J _,B1nڕs2o;y͗Ld{ǏoRJJBVX R`:z?ݹ[DZ^jbSN9RUgoE[RRДP%wPi t}|' *%!⧜A":C@#(v)u ;N$Nh/߿;4:K$Bo) +k(+A~+~:$ B|b֝^-VE7M_C[D"?3!Vv,^a{WUU%Dfv\.PN<Y.WWW6;9k>VZM2p+g\vi3#ƙKG}F6ZL֢TP(]3&F2||./@RzI29dPcPJ`!ԩ[./Fib^.RLQ Sв:ݫBUOD|@ xeg/?cgbCK M:H/;HTRQS*3;}WFQܹGOT`w򕻎P-Yibόc/ď.S3]Gl >{!p#hJ+K Dt]J{R΅L5}2b[IgφӗE㬱c4I7&@=lu;:uQ2";ۭkd,\ߘ/}|,ieR8o\׸D"K~;@5E@Õ]:kjW 4w@vv/Hv j|RdMip;X7f_$O'"ЩP ]t{:=r)NpP  ~@3~îy]Ǐ  ~@(X ?@`HK,ߌ0l߂-@1$jwt:}ݲuR"UA!,I Іe3vaWYax [GWgtPC&P7&Zh'r)mǤg|Z.~k)[r5!bX߿AUTT,Xjk;F0@uM @H7󃦐_RRj+vS8\UUS(3,l'mؼߠ['{m;v ,lܲnfzV"!4gNp{9>gncotafvS޹{_z`qdJUv`iav88B-glD$Dnz W]<3n:8O1kxK[Y,`9i{jz\5AJԛ|3?h,YO [3'"#23GANŝ)Бen۾\ltܙ_ >oF\8 h:PzrRzrHD9NTYYt/!jjhMra0,ӧ\|㲕j)I&; += @ `5O4=*qB觟: dGijYcZ͋pır>}}yltdڵ!n޺K߰ij#OklC\Ԝ3?hp?q:ǻu'LVWW>#!8sDOwMM(\K ?FѨӦ^O@h4"IG W,  F+B˽r-e/!IBC ӏ<^!reΜ yRZܔi>=Ļ7GBkX}#F09K4p@=yp\c#C/;X`wOo.KHsuvӻBh!aSN01nJY~ SiTv1;[ ?#ttݧzik1\JHj` + 3ÑȨ6 }z ݩ!Tf# #h kV!Xz妠` c|FJ% n==],9f4*ξ}"|+)}ZW2goz)^gq hg^K ;SOeqkR&-V"Sn>MaOw'OX!uayc.5E`o |D"Jp(*o/o/k7on\ZʦC-pakJJH$^M8C xSLuP0HP  ~@(X ?@`,P aߌ04f,nxu8&د~ 1/.%NdǣO| *8o}[N߿w1Ԩ-v0~lgw?PP|̖RH.}u蠁Zpr:tPRVVVWWcj1B!WS7ڮV";~=D:]KIݲml IM-X}NCr \bQϞu9=B!TTT\I:z굔WSK~ ػ`II\B[ki7̬1N(mEkkt\_g2uqNmo w/4qGzMi3fA['{m;v l3GW ?/X3|+BSK[SK[H$dzhyFzgL&Te)f#7$BĞda;iVg"-l̬F}ƞ9`?L"UUUIRut]:]JyO >~dji:t//$$l6 ;[W  Dm-xHE4fzg>y66wDbsS\rrtع; K͋pımw;kG>}}yltdڵ!n޺C ڸ!D$!Vaa^~X1Dѣ2g՛?_;w:\qIAy<޹+!Q# B_~-//۫۷B㌍m )m ٷOoI#Fq9hZp yyǢNq=-/["s 158\(FL7PW喚%H$G0K W,]B k)h4:}i2xѨT"hce>'1Zl6!`54WJni)[ptWqc$*/H˸1o.F#tt{9=]]lK2رc=\ed2m{)$p8 =g꩷onUUN h-H7o{***'_O:j]tgƞVWJfZZOotUPqq1s3Rra["LMqc$*BikkE hT*BH (RBeeeT*یJUvqv ٵ'4${ .ecUUۘ P~}rlsZg&*,:/_LMPi){M[655+Oy"qb4}mGNxZLf.oyAx}aRr?-."$*YAK _4 !喩;uҡh9>ܿBǍ<{ÆR()se;ҚXWkAAK [IITLx@ D=uu'\YYbWX8Ax7o` ^_?hz8qmP_LM.)7zlQB!É9up1IJь8E"Q·bhw99!"hceq1_P:w+|VK't֭y m8؉Z"폜S(~}LspF>^!m鮞>~ZL>w%#{zs9\ZGc޽$9ob*z66%ŒDp$2jAy޽CwK$* 4pzzXzY$ꚕ+BBæL^QQab} eY63Ծ:ǜwn?LqZJHhОzz. ]?C&]_\=Οhzi@qĠEڐwJZ"Zk+ Wg'>6H)7WQǭ ?c#cM)Dlg^yYĠEڇ6핷GE$:8D qLX%mߺI^EEJiXD"j ZKP  ~@(X ?@` /@"QG oF(JRl6o▏5y1$'Q Fс"loQHL.\Jx@mq5:g,L-mŏEU"Ayb6Vy|B .C%eeeuu5S(x|55z#l> " [Ii[.W_[sᆱǝ{+46=h, ϖ /_~kb-#k-l|{lܸ Vo+?/^4*G%)X~KN^^$G$m|=keee;vyަbOO?Ҩ{e ׉ӿBzVNцCK-Xlnch2#=QfQi7}?`а}u^f9Ms7_y+/޽~K[.n7͘5aCll%].\Jtp6v` M-mM-mE"0St$}zBt VvL ?pe[ZM!'SKQ^FzgL&Te)f#כq6Nnnfmo7eꝻkVQ$ ][z̙ϋ끧Mv>r$B`VXUU~g'Jvt)r~3aBQRR7(7/X,3st?/Μ_+Ny8kɲ!3k_["Sȿ.\:IRӋDggLQ`gk~0"u5\z)Z4bosd[kPf̬֓'ܼϧ#F}БH,]SC3nʕKN;wayyq.8s<!mΒsqgb~-8p٧OO=L8d[wh1A!ғғl΋--jGaVDdDf ,H$8cqF[wDupYb'p/_tqv\r]-%d{񻔔*]`P٩Ү%4`Sv|]{B%_>q,!4L(o_~-//۱۷댭os#w=]q]JB|7oGRZ^~ӆTTTĉmوή[B]:w3zd:KMPBQᐗ,Dұb|kZx@"8jXbPbU \K ?FѨӦ^O9NjFD+995bBH[Y#ݺ}˓L&rteJ'pQ@۷$2WlB'jh08n)t2H$5"7/K"4΃љ/44=wTvIY=رc'% ǚZN:]EU!=|=L&ݦ Dos~2Zm'OpZo̊C1Cyy,^=Ѷ"l7!< ۷_[RSN~Zq.꙳ըRD"1**!VaaΝB=e25 X ,SJGGwI wxrB觟:!JJKśEDWQQ/+/H N1S:tw v#)fšáid?;cB᷒X3!@ zVUU%.)++XY gs8!歔0 Ы?_Qx|>BMWUk4`o%%Q'bmXx6ljea4p^{FV|qP(/gxroHnõ*JKO>$ݺv $6V3q筭,$v}6p.2d0BHUEeCGUeeegƋ Ow&GO?}4xz'O87$fáuid{_OP3̓xy"ttݧzik1\J b钃G=.#ٱO^`1F=ma>L!پ3TIIIa*5kX}#F09CRˬ3s6"V, i4fQi2^0#:Zn@PާwНz=pŲv: +{!zty)/Zb;III~P=xhm|F5bݚ?8XzV|y] Hi蓧&ZUUY(phČ E·ҧϞ ]!q̬'bpZJHhОzzOd5ámiCn߹+ieWZ3k+ Wg'>ʚV2dji;oK`nc뿐L"@~Em HV}5KP  ~@(X ?@` @"QG h:6`wfI!ڛrh# M?7Zkֵ¥+uf+GW7|P(4o$K]g֢%?[@ٸ>~Rvoz4-ǜ&f֦ z[3uN&f# MǦl6wݓθ{zz-ld6%w7Dm YZs{f"zkq$):=_[1r%e=\SH ɭq%n.L (++1B-kim|)ދna;i4i Gc?'YeXG _|k?"U y7nN-k867o}+)R>=r|ЦRRl1=jBx~ FIONJON:|`qzrN9sB"Np@oyX$v}?"}眹d2FzȮ=W⣎z-%H$ZdS.?rϻƝu$ɳgY[Y\wR|N:koұkVһ\yofVvSRR}ZskSrBT 3j3ɗnZ7h@Yk9"(;~k7.n7͘5!TRRj+vS8\UUmY;UX8˷B( V4*1i$;[kqMv>r$k9[:8Y٭^q#{o t֟!H8ɨ#X,3st#qKO4=5=C= upwܶc@ hH?=~1DR8XZ<ސ#IIMCEǜeþMU`k%Sȿ.\: XUU~g'Jvt)2B(/_:;D--GWkL"0eǜxUZڮ^QRH7oi5q2O ܦXYQ$w(JLIN6l d%jR,?X vׯ #ZZW/Yf7HwMw4EE#@ӻOtuGW ?/j:v?Q~Nh:͔Osnnc7F _ |HsϜ1gq26D2܇vz5B(3Ifqn^n^܅'E@mزD&'ğ98D,Yt-&3h:K-Ioګ6V2***BEEr2D.]{B%_>q,ܴxs)W.`#! BHR"뤣ӯOo+׬-hFgjjPP` 2nKшDb'ШeVVV޻xѸf>LIII;[Ql2Fw"NϜX/] \0FQӧMQYCO`p8ܦԌ$f uu_֖ݻwZZNtXru>}6[O/ @k|ee cǎe<BBضRbCې~sF|ϒB(pQ@۷$5v|SgS^'''չzѨT@PN*#ʨT*e0L?! B\$Fg=Y|%~c|%D*8;\Yb5+eq0EE %$'D)Iubӝ&`|TEU!*,ҹ3>]!j$u"k4]0uB3XGGSRjPƤOR@T\-x%?xlŪMԸ[\ {PXX$Ez;a', >5 b2uRҠS{8KRySZYzB/_%%0Ir<)%55&#說c8Z^^$Dtz:ZP&&7oh}1?uL&'_OCEǜKKO>$ݺv ͊F5 ?pDBj{x鲱ѿSFגkjh**:zB(,++;s6^n0L?"xP喝:sKWVVpB</[Ԁ6}pč[B!?15=s{CjSS-"hceq1_P:w(++w'^OfUUUcQX!|5&&KsD!߻[OO˭3<:{)k(\||B>}gC}Ujj$-u1ĒySZYR _>;'\hTIko^b[Igφ/qZ_f~y8k5WRk)!aC{ɥu79с:rPrƝOvhX#}箤]{y?ii19\nԉ/שB(O׷ρk+ܼ|uUA7YCo4@&L-mXf.!>bvk̓a cbP%;~ qOĞ.-eSd); Hd$-?n͏r|?\שsf7)a) b}A`,P  ~@(X ?@`H/l6oby:Cثqj.Nr8 6D^]Lk (Fh_n`HPQ"?oɢ==ݖ9za4@ p;i7G@T"4p<~}40- .%NdטA-RzrRzrR3iM?:}򥢢b/CH$RC@kZJm;f{\MHob&H6V|Bl'4lcFt.%g SWWanyZT=x?eeeUik_Fw<5q`!CvڈmB={ZX)462\JUFGt /Fz;HNS߼=y|ˆٚEA܅}NPhjiJH$÷-1 fSBv9uXz8\N7lڪx/}8ӧܧϞFG*S(;C6oq2BHSC3?G_=[vՌaV553<?p(*u;Bhsv"p.6ZYYb-0zꉓU!mN\l4"VpBk7najj^IKqV/a۶lt󘓞4%E@#?w/.БE +)0/?!ѣ6o^o.ܚb2~MԘMjjt , F{qe<3L&͘ϝERDq $F|Z^ʱL-mMmL-ms>|Xd~/w_/O,Y3 |AZƍ~si4H줣mQfX!ZZL@ r-%`<FROz=-;2}gd55&+YZRTQ3H$]U}kjzKSS!TXX=-)-.ZٔW4=ʥ}ad:BH]]m$ "b&S=n،-Z!T v`FJzOM&8kIQFacSȕEڥ?ܽo0L_Ja⧵[SLU{B;>K i)畕d2[.|b0L?"xP喝:sNzi@$խJuNB| r骪c8Z^^$DBF5 ?pD+G"m,v9~;aC=V!9/k5%E@]!9SL##WR:yGܸu[(sS3ߴC_=z|ԫ M/ @ z-%$4`xhO=Fw>|n߹+ieo|9UpQ'N&ZOrWH$`Կ)h6-=qoެݸM![–<:&mi 'lDU7* ]:M4% W(@{~H ?@`,P  ~@(~|Q?~x(2 ?Ϸ_>X-BHT%**䤖 M97V(lgw?PP|̖9sqЀK,ҹs YvõTWA_|yW^HOO!aC>?_K6 !χ_ I+\nYJjL-P(jjIizskk{1{̌Ql)kU.]YЦD[J)R"$nQB]+Y{q֢0Smfjζ1S.yyy{< 36'iwNt>R 8e82xEdj΅9F+~ŧ9 g (HI=t|vƙ,)}*LZrVzJ~ڛɧK}I.I555߭Zk3bV䁺k7njq/feCm'rSU4mߔ3O|uJ#"=BtHppX}/e畖ֵN$2101 AN˰lf`۟G rrZf" !ֶfl& *,tXnʱ1>MGzy1Xs[.'3t&SviT\|;v} =[|'N&ϴs|mtl<οC.lhhse"ge?6= \G^'O[<|T3S&SNO57+xH*D?%JY*I3S%%%6qg% D/ ) N72UYUEoqQwȹ5z!Bbez-#"=UII)x! )UUA-mM9k8tyod^Gwfp?sNRD;wO=||pϷ K(~Vvptb|4l߱SǏ$'%|=CfPpJ޵;ԷO߬W]n⒒ 681y[ݷ[Q&qа=!A/9z8l )/^!wq!L4?x]Ao v***o6x֜|}~׿Kڮt G#vx9x_IH %ϟ%H>gbZ/)[wSǏdg\9Uk )?w7ERq ȍu"Ѻު** E(f\bL/^%g0*:/^r[y.  F߾}߱򠲲 ՚oTWWnoRR\΀ !J޽{^?Z;:i8Cz!A~3(!5=s**.FG9Lw62m4}E2)ݡyӠz:+ZQQ)~<ϑNQUurs)'yNIs,uw#O[>’mqܼo~jfm-6N׋#kllll.^J8zPZVּL-6[ˡ>}T?wYym]ZXR@II)!PBqGE6sOn^y ƒ#PXd*@MM $SLe{;а=!-.5|6m2x0LI%XYV3,yV߸ww(A˼VM2겐+ 췱E2)a EI͗:cQVV޼pIx>l?9KZ2Vhi)JD^3VTTo 34.K@CCJK>򉭩㏮S1GbU׷Qf5Wxzz{톓Ǐ[d#b79#HW@y#v6324K[66sO[R!q***˙@N83g6,k EK{ vɤuHJ5jW^ZZѼpI q<[DMd{_|jjj#z-q""S|>Qc A mhh<۴0[UEpCD)m<[r *D"_ 8v)'yS-,S+**[}}=ǂVJNE \./)Ty6ٔ+ttD 7^kGx^ٴcc ?WVV@UuuJj#wig={P}ɪguE ="of_xq GDʪߛLn^pX, " A B554;/[d1sk[[}0:٥՛5fAZNsk5=g4M}7l™nK\x'/8ftz_@vAtu:ma,_XTDt>5`^h Ǐ[sشa]hxĬuuu'nvN?¯ _5|'-v~E;q02|^w_д6ݻ:$")Ƶ7$\|1cg|ӧϺ."g71X̫ғL6c9NTٻG@6IDAT Ϟu# AXX K,Oe~!j*z.tw~BHB)B!?B!@ZT 낪 BݽriU ~Z+Bu j`0LRj^FCB#B ~BHB)B!?B!@pG!!,++ BGԊIENDB`gammaray-2.11.3/docs/manual/images/gammaray-qmltype.png000066400000000000000000002351571413310443000230510ustar00rootroot00000000000000PNG  IHDRKX pHYs+ IDATxuXq* &eRb6vw(-"-**(}w4\XK+8nvo33{QZ AADU\Rd2\nw}WRT(߶m߶?o+mY,pbKPD(.)AR;NWB}xASpa\i)Zf `yxAAGAC; (hhGA  ңAAz?fhq«%4ڼK8ί}lj1oʵBº;V57յqOYm^ڳ/Sf %6CG~>Zn㺍[KJ~"bˋCBp2ҖfF]|C$Hmqݻ۷nTUQ%ƽxy?ess7{УOLfh3+]FBbҀp`$!D:ED2Q@@?IY7j}x5'(*[Z- sI,yhPUUٶyxЯk򯬪 |(9%\DDd̚~*̬l]ݻ>^v6֝du&}lE߻L&H䔪*9Y ѳfLŶ+ r]辰k7RܷqjhvVv_ЎUTR﫩q?@SS=#SBBҬӆ~Ϭ:u4=ރijl=]]I$Rl̬,y9G!aC %&~CLfm؍@EE33H$k+3Sx&>0(D$Z[[vԥC#O,@"@AA!Q:p` _sr^YXXDP\'OԸujjx<7RS3{L|͹qN^^y(KN9W,**r7gv[Fao{ꅽ`XMV۬,`)}ZHo7a%%˗]rQZa<qݚ{R(:6WDq/SRӄ *++Ӛ>u "Jڃ@ qvxH`:XXX4޾ާ7p8/LڒHhв%,/$$/'ͷ&M|,ǏC\&:7nZ F=-ۿڳQFt:`k Y7W'jf8,_nͪ^jTJNn.`ֺN1=C#SwаظjȔ r=xں:11SFd%$&߾{Nglؼ`XG_v޽zmX{;x{wO6+>!q} HF#C§OQ̬V3pxmٴL&GDF?qj׎-X/^޶eDVVQSO;n"'IɩڃݽD" }Lϰ=rp{ yy}jjضebEE%tbBB6EkllH~KM4WaC=y>&珼Fp8ގNy< :ťi2Y 9%5,!`%EEgh0RVVAA^>>!˗$R.> eb`Я_^'7YS║ȾAAA"i@PP@O(" v,\|ێ=k06%99:Dqq1K/VtuY>EE1Q¢bx&f43hUU !.Οİ.'$@tL@[:uyE9sm2\6T_KKOLlܜY3ln֝{.}j^4= ugld EiͻI?MM ,h EVWWľ`2rrd2gmB+wRRԊo\}=]ᑣLZ6yl 8@f\ˢysfEr[7N矟a0J%%%yAS(:!y| ?0ddd݄`}~@~a44ԧ49bCݸ]XT۫EWtpmZfmEeeH{@ l`g%EbVVVֻ/IQOf۹_da20JDa *0'o\M1VVF3EĿ9 XvxaC2JK߸}wۂ4Ҳ2aaaORLH}WrxۑҲ2^*!.;(:E p K~m6n,Lf0Jƚ(|6w[[Zt9&oa{?Q</$$ݤjᄄElH֭Y0(`DqD"mU|茆htyoq8혠=]Эַsv6c.\\\\1T*Fg1 7yu@['wF,)!Aӱt^(SyGl-Bp{vmbbb'M?~/[aC<ׯ(oZGGERB"N罥i*$$?x bًW=N:~=$%d_zEq8*bblt̹/!.^SSS[['((͜ASWeeؕYMM 9X\Jj~ &Ɔ66o#--͗X5n5U&oiqkoĽ|eld͵.^Gm˓1tjnZ HNN]b L.((ܺc7p\Fi0'GkB }/y4y߿0g lm͜W]_Ó02$%5wE|HߟP,.e/\.FPP@PP0'7[bQs:1JKcb{wk⃇Al6*ɳoH_O74,p8wR#0~\XX㨧eeeՉIؠ-,*216j éW?`g3߷okkү_sۘ<rkZQUU3płr9jy+rr>ea"&.`6bkc8zDyElg;{jܼuw]uuT*ebb<'2R3gLKJhL^ro޾r-\CC p8.eeeZПҸTꠁ;]Ƞ3(TKda. kWZD"-2O)hK\ABBmqWܼu@}vF#Y 8$u""S]'6jD"qa_~.s{NvNER7w?1ϚQQc#55՟mcF#SVY?bi7àMQ&II)wLr?;aw߾{{ڭ,_ںcH߾Gڢw5 V>W(.&6yD;H"co;F{~`RR2@}ٲe?'yc̚1եv6I)|_y#ZWGV]d>);bO'ܶg9L&SNVvr-gcej"=]Iwy-2a2KW8rYmo4joS㶬YUUdŚׯwce kWabGded͙՞l/w\ \. |ОqTT/.nDDDܛrQNܼ}WTT~hL#ҁܧ3DAEEyт]~ww#-%uZ@n"6CdwmI_ܡA A024q k;j/! hhGA  ңAAz"pʪ눋WTTtw]o߶oߟ߶B*+:gA2""&AAC; (hhGA  ңAAz4# HvAQЎ  =J?Jg2]   .ZY}^l `2jjtwHC;Cg7;Z[^4# H*Rds[]  ңAAz4# HvAQЎ  = AGAC; (hhGA ڎFA IҊls6lDEE5̞9}YBmXw.pr{1N,{úϞ +T*[Ar-5muMx' ;(M~}hLP^^~ÍݾqEHH"kAoWDC{dme87?yk pΟr8~ҡi@>|L_~8'y̦JQsrr;"'khLjO4b¢bAAu\I"x8}695'X4pAaᲕk99|Kd2|6xԴgxRPX:c/_ٹ /_Bs~ +0YWGbLNNUUQٸn<7o۩=e쭝ǑZ}5lQOXl#E IHA}5fLxKW151{!*"5'b)8"\r\KKeede_z$glX-7{OK MG=<߽PW_k߅0Iɕ˗hwu{錀+䔕k>{liii4|VUW=UUU7mu?ޭgغ}wyy9-[׮hGl_X0<cΝBİ!`o#",-Fp8"h6y7QW1k7nx{]:WotZH{99a 22>YYظVOz84:޾:}v-wn^KmCzR^^cz3Mw=7^%nio<>{骝kh?wi+Khc+/QVR)(,LM{;?d2y""갏;:@<.:9Ľx%+#3t.))=hXGQQ\rrzᑏ Zf۳SH*xk+ ,}ႂ8n!m-$,bIbŽvc:+nLG q$%%@YYIOW$%%--@hX*<>`0JYƇ^rZ}5@VV"$$FYYILT G5!?l'?e%EqqYXX{+'+ri4:^S(eel68'XzM cUEY0Mq9wnd8r1PWWUQ:q8Q(-gN>l6 X[YҊz`?P(T(*.Έ{ G IDATZI !9tI;'NyϘho /qk7.]jmep\EwN)b6HKIUUUWWWc p8`@qI v?5?@RR@ HKk9yh y F:g1ϏEr<df:y %CGTG$G=}SD`kjjjkk2ҫW.e#holkÆb%%4 RRTw?'MJJJYY)>Ο7wH42iee]m>}oTRBʵfL{u:W[[W]]{|)oFu嗯^735Cy!a,~b2P(4(Ůxs {u!]":f2ii`ED6rwX!/¥PQYKWTUT}9+ KqIɣP942iCGg] ..=XMMKw< f21QѩS\Յ4G{[֯]Kip84a! 8`Պ'NH#GbߠC:Wݸ~6͖^`.6ϊi޺cW]]|2:B3H$ҠN}e)-LWڅWw '24 0nKo_,""!"] Sf_Mt?[яCC#1ypDC%S\`fG$mCډb;okcu1nr̟Q}%'ϝ 4AvYf9NvL;u./'^鯙?g6gh.6V6P!O@  = AGAC; (hhGA  ң yJ򓤨Դgv,RT 䶾Z^NHHJa! RZ?C ңAAz4# HvAQЎ  = AGAC;ғ:zM./fh]}|; 1wQӉDzn˗,1͕8b̕U:doū.^NO`}zr<ƺKho+,,S1ZxII }]K+˵㥥eioύ\jݫ78+*(0urs9zz~6VTTl?nbD5U=~~̬PRR?' }=ynQ&F7^ k֌i^/]֖O.ѥ>/_;.}?٨Ϣ*V _D= Yb7 e.<򱔔ԋW;fz8 y2&9o {}ŎޛWG^{zy?y:>혻DxC;xBdfeht6x|f df&+-9YY}==>7bOLsf%EynKNvy_=eDOjҎ4?Ȁb}ѧ#BbaG40w%%"lQphk!{p!\ٲc/}=dMMufb cde,ȐB'6;t Rx<"Y^QҶ($FOWH$p^>{󨌌4o Zڍ //s&h&&ZT\LLDD[{454ddyml ~޽%$%rs@3 .D'n[j-K/TVRl?ip?eb$,,di1˄m7P)2 u֭VQQ&םܰB 9%ɬ562謒? =;9Do5WuQWW!'G :1:~>~RRUUU454tq11:T*F@aQсǎxx&Y v{8܏~bO1 *_HJb/x5¢9N.XzuMMiiY'j(qn\nÔ¢}!((8?p KH$9,d6_ZJ7pΜ:y{=hl.^8ylqNZ}5l3*k2IM{ {R1jRweenBh-W4ASClqWS\&6ן4Y8L&3$4/9YY,eظ&ƆT-R;#ÃG~o!8cCͦ_5f 8L76,qRJ+:1?|rٟX̬9 m۲4KeeeD"x G}=0(bq8O55 G-s[p[wJh'O:h\pKTUSVw֝ƙX[= y==K޸~y4|WIQq҄yB3mFIQqЀ^55حq)*%+3p8tCXHJ@Pp(Qr tjUU;porUp8̬'b4O;& '>}zOJs{? ;<?SJ#\ўðC؎z}Xx$=_5f<ϙ=Ys+;Hd?`g{{wa8g͘jd8m*S 3$5\OGڊ)X 0qS&7d DEOy c?ljv6LMwze^tʤ mY:md3i9 WUUݫA#/Y`Ǟ},lVY_^^[DK) [ݜ3䄄7f]lLJE-@u:|`؉SO=tq%x|99::HJH4~ܷg4ݖN>;xʵL̞7O;& @;;ټ| [q>|asv2lnfJ F_5Z8FiYJj}ְ z${ܣsq8 nZ?r0,e~9YY#ҲOXm.M-mÃ~*'"''wy!w[~̢p=KޢQс`f^Bbvao{!\hW_M>y?0g2>er8z {:dU6ٟtz3>Nv=~\n: ޾~2a<"5~Vg=F{y}{v /4l~ӈ_[**.>qLaaD쫩y}gwGRIr@=]%n +.֕!t$[7;!  H yAAAG{i;AA:D wFAGA  ңAAz4# HvAQЎ  =o4;9~Q =ԙsS\(d]O 34eHw:t; Mv(zy_Ѓ$ݽҢ9{?g%fLs%syevft^_x9==fu$;klчD"QSC{roܾ{o$,?g-pƿ9] 6aGo߾w ))*LtvSY=~ta""=GZBE'&ضe\iiYjQ&FMho+,,܅p-NKۨ~j7z~á}󆵛֯57h9]751VUU i]ݽc`BbcKJhX|H~quW0ᑏ._y찮6abd 21~ȁfBRҦ;{ג8'ϝˇ*zڍ+-cUU]VFZ >{csC{~Z]=YˇaKNVVeL|p0@ځ:xhӥ 9t??`ҩ]FGjkO<ӫZ@c& >x8q^߶sOp"/_ٺcwH NitYyzaTAAS]':򔓕ݳ Qn<3Y,aCvlD"ľY\R2ixYoVRSݳc+GL8|3'7WNVvͪz:[ggL2u2LDڲc8'?KOE>bAܼ}! O'O;{KG?|zBbǑuӆÆG0J˴j> lmXk N7y eg|""Y,etOOBBkV,s?n<)*P|bRmmm/5՝6+)*bˤ Ǿ(++۱mO8zd~A~ZFF?|ܷ{Tͅit:ɴcE&dh3:uxf.Xavq[0/QHtlȗ/_Kʆ a*\yj3S<Oc~[-ԎHKwKTPXTSSf򄄤 UUf/ַ_YYe{K&TUUYۏ;{ƭ; %3+F c'yJr5Ѧ;{tg⚚s^^JLJ޳C7:}-6DD<{nkmE" +n\722Xy;vKIQݿ}ıs~_d.Y4Fߺs&TʌiS޻yշ>D<~G=}fcm{ȁ}^z}kUw@eeղUkLp7BKJrsYO462`Xio߭XꦡgwoZ6KJIu3S3̶l¥˯^9}xTXж:Z]]{,1)9f}d|?+X?wa7eeeD‚KKIaⲲr3^7^t [3iDnl2M4p@;ZY;ZY޽bb(,,pa3 4|sW.&YsxbGFh #\;-/ ڲqK~ǭZQEEEg|c[4 >~LMUV_ ۶yÕ~7ݼ}WAa!! >DO/n H$)) dd2YSSb(hBkp8N=x&YVD'~M|s ɓg16[PPpMoBDDd!ATTDG{P^^>ui 89x<3xPN7~+'+kei&F %>1J/W^^RT*"P$+*\.EmRR-{6le%E%S>9ۍ@`P-o'+#SKGx-ƑZFRRT@RRZs9E@Hh8qel$#- ڃ <]:rB&&*J&2K)  ..{bڒ߅⤔TUUM ,]\GV$ILLFWVVpi3`uuÇ JWgރ~ܼʵ%%8kNb@K<yaaל\'g,-{ÚN%r8)8Kh4<O,..Or7WTT,// 1=SzzA !z}E:#Gx9=g@aQсǎxx&PXX$/'.h>}`PzL* ,JJhrrXVVAMiΛ3Y!ak7n>qHO5 1 %4Zx鲲27=M˵ӖB'da)2Ц~rOꤾ}w`( IDATJ4ґCd @iY)v>"AebhoK/,*^TVVKKK p[mɢSgy(D@unVmZ6oeeejj=՞CdUUU"K|BPPp_թOL',f.@  l6N3oNJZPX;A%zpɥ+fM a S1}XYz8u;l^vAJKK'iK;UUQ9uDsAxdlTJIfd6V4@py,&>1QWg04*&өTj _Y:nZ˵Cn(8cC\Waq1oyyCKG~]YIQOsY,VYY gӄqc"'،%޺sF?uGO^jrrC ?!!m[6zzfd2kjjcz7mjѷY,ITSӦ۽H\~֝},Z0,, RTjz'p82.w<$ʵGy.QOo@aaƲ~]RR 0(7%؀ۂyĿzcnWTdfeANG!,,?@XX,,mw:(812)3+&6rrr#x 7BEyP^^^W_v` Edef4?tԣ\ZL̬ޥðv#yw] :&\.74<2S#s|ȫvw}ÖN2v6_~2#GOXZ1//_]sƽv?yj”55Ly94Ѷ~-_h͆x<^OGu 1QSǎyzyhsfϘ3{Fw^\\l =*z8Wۻ{{G#&\;[w)g vn݄M#'AX=mBAAVAAz4# HvAQЎ  = AGAC; (hhGA  ңAAzw !H z2oiš/\Q mg~\iRSZV˘1Qy HW/v:QZzGrj6X!4,RYY6_?fGH]˹߾-^8bts9r/Efe镕3589<}b B}BAa!O2qtW,gy9OE55kV-OHHzPYUeinl"`g|""Y,ػx/3fQBvHR(I@Q"t(U:)hs嬾J.E͈cOLV|^9ޯ33F\EDDv}~/:|\]MM~*lBbRm]Q#}=7o^|bLC"bnĥ߾#)!ɓ6yy8 1G-ނgKJ4Y;~GO8.[`[435!ekc]SSj1k&|xiV{fL7[hf݆M^*,,|B&jl_%`HJJnɰC$;ojj >6zHhnn6 9~U2Kf^=#yמcJqʉ#?oy{쎏S]]w.^ӭA^%K]vʟ/Dݼsw~#`׮x{y;s=jjkUUGx@7z7={FPqb;|Ücn۹6^v>","4kO l율+^*:sO%.(Çn6}޻[ 1۶xv qْ^oyO&L<%5t'GwAZz&d޹y'h_RSSגE bW{K}}])EUuuI[3,+7ɰaJc4}w...43ifǥunPQQqЁ߻틸o'M$%%Lcbś)i*nj ۶l:eז~QG`z&+**?xʉH$*)*eOxFlUabGuu&+BBB:iijvKK Q#`&ӌ*@MUUFVm1s8l9ÏԤ:BEMUFjh˕s T j'܋2**qzLreRRbb [wzU1X544B$dlCc#@8yQ#WtY{~V3ikoߴq$%;ju/ur6OHjkkDޭ3 @{8132 J!!e?-ok2yR)iܢFVVȲJ" --{ ,9S(俟 bDm !_$%ߺ^HH֓ HD"> -L$xUuYtɕ?]z{$wSUVV)(*i/q7JJz $L$,f045i4Ns|S\bmk-0Wށpjk)ALl?Ǹ$[|Tw9m_%x|rAMji*(S2&<{wχ3RR> N ]=RTTZc\WIHHwsr@qo3oqnޞ}OT0$nT[>x>ys/]?-nSʑ9 lB;mM~Hs)+Oʫ̼}WTTT/9S2<|3>j۾KI6qUՆSed>ͫuxWZ:Io",P_!@Oқty׭͵WLnޓ'}5NJ5{yshW^kjkņ _[[Ð`3 /^H"Thxm]]w~L3g#+**lv7zkk+T xn%pg~eӌ ޾{b8NLfk!..njbywSxs,g ϧ{ ?zXozX&6XSyŬE" t ߳/׬s_bc=-o :ܭå[?:ʩo5=kR:ڃ+kGܼ',fJꫢ{|> }Q#5>ԯH;N?mܼ'w쀎>\ķ`~,B=CG~{vm~&MԯkNnں%EH";}}QajG9ub@YV6!;|}|0vxB!T0#B vBhPԎB *BAS;B!4`jG!L!Р!T0#Bגz8<"jHq@G~'Ots,zع|D^e?0ʢwjnZ:::}er?8̛k5A~ICC]@ǹyӌ XH *,|bTG/^d9ۼ=TYYeeS ='k22'~7I~UUt^ GQ|jyk{5rQ{FSwSHIMi׎Ysf`֞q vuOBBBM}ڭm~1>1ZVZkúo\W41nϩ "n!w]V,z-#-~"!!qrTGD05u+{Cl|8'$=W_ߠ9ۓNYa׆ _t3ٺztE17]|W]`~ tm>jo}3^y7%%k[;_MS&P)55MMMϛ|xiV{fL7Y:~[ q\~1hfj"B"mݱZRRBp^yyE%ផ}AVSs3DbMMֶF V6a!/^J&_|`԰l+/lv𩰄dmblUDD ZoR{VvQo.VJ_D-+מ;sJC]>Ӎ С;|7>aJ t:o򆆆뽼=Ϝ1|݆MRRRs, 5=@"H$f?қbnڽs<=xGUTݹcPQQq2~Z]M?~TVV7ݱ(/uXH0ݻwjt ]:TqJi3L465xttߜ꒒f+ Y,Vn 5Ok1QgCTJ=6x{ssog?cK073ݸaLISQ}}~Z^Nqn+"糲s"„=69ʩWʿSonӛשrqZj`5UU!//WVV?yzaJf JK_+m& ,..N"f'%HS&VUUGr@[{&))A"^|,+n! M4^=$E w.hP2f4Tj22%%o:oZCC#P)J@&646 n T j's,@VVFUEX'$@\|"wrfh /'iy-w\&-%%.&`uJZ>Aﯵee41X,HJu=6Pmm?yEUM^WiÔm,fY#.!bL)i>q11PQΛ6đa=NI@DݻUblr]hpPW@ZZ 55|s8:*"[$7bR HTPeLyd]P(w{<,6[/;Ooxg'7Ϛcө\j/+4Zs)/G͸}'"‚4yy-1_bB&{qYt]{B$O>;97oNR|=/РGSV|+E]MWyq11珐6qUՆSed>9uxWZ:Io"h[238(}Vl 1u:s8J#0ϳYΞu"$t2 ?̛kuܼ'myO?=c3d uu&P_imm$'GX&T "6p:o.\Z`446*'O}b̼d빖O :::_662ysBBp߹546xb 'OLp\K i:H=t#!,?wc̓Oyh TD4`XNSq8!H$%%ŭ7P_IM˸w? Ͽskƍ ?}r`BbEjzL Yf<:oсsQ'O122ޞfOg޾^٧B)d=~rpq󨭭Z Pm]Ǐ&N讇ebsֹ@FJ<{y:.6siضskK0;t2=k.]klm큿p8λw!Ѐ5Rc_Aak=Iϼc`>ettui࿍7VUaa>scA!zo&[\$ׇ{Ο7w@!4Ȳ 1>!CBAS;B!4`jG!L!Р!T0#B vBhPԎB *BAeRs}~_GD t r_?B:oCeg$#/ O8 IDATIee\[?/8wke!&&^{WZ}yORȲW:YYPPWW87o {QXlŋ,g*+lvTQVr!sG0 `dwl6ރL%&cF~`׶n5AGH/^{lTS[dPp+5-iyӮ[EEE<\]=G'<-(\~+}8[c|brUu׆uQ%o߶^i6c:Ϧ)WE&eOH ?wZQqZM~:snxϚ9#6.c/6|*,!)bws(7OEyxxD So!q Lf+,iI'h.Vt@̍w$%$^~SW_?y&/i9޾+}E Zf3cjIɷf߻`tuneKsf&"$lm%%%(ϞWT2-YYd557:/H$vlam|llnerd2˗ F v]ˡ ZoR{VvQo.VJ_D-+מ;sJC]>Ӎ;%kc"C*_9._i<͐B&?xtუFjf(O ?-/'87}Õ tzwC=F7ASlaaa!!!45@WWdP:ʔs,F)$$4Yon :v m$%EDD4nj045-bMIJJˤ̷NIT m[6Qdqqq:7ZZZKKIG% aJS@Llȸqc^ Dc2[Ć--}]h444B$dlCcVn@Jx`=deeTUTK>u|BR[['r'irr=~֘wA!OH HKKԙB! ,**hrz:XEe8Vb8NBR2ﮫʽmm=dp))qOАA̝oo&YXkk+O!񓗗 ,ںGM]k*s!g_8Yz?mvVPPX㼒[hx;hFDwYu!ܨv O[{-7Iz+;-O#L֛x;5wTm]"$>|QB!#&\~ϗO#B vBhPԎB *BAS;B!4`jG!L!Р!T0#B vBhP}N9: P<:Q ~_w?-(.σOz򕈈۶n@wY!p*a2[_s#QSC.[ xZP(..fn6c+H`gYWW87o {QXlŋ,g*+lvTQVr!sG.|/ xxm^j1kfsKbl#*ZPyѣFꌿwrezu<.^t@>'[iO 0m~Ϋv***0@jl9M?iAjW[ $$ǩ5hVVsֹZ޹?.&'G >bMֻ(--M)AN{dH:7M  *.)htfϘn gӔz[=mmuSH$R|BRs Zc|=tZ߮ RW}9f0%w:׺VL~xbD}wxP)55MMMϛ|xiV{fL7Y:~[ q\~1hfj"B"mݱZRRBp^yyE%ផ}AVSs3755[Xۆ=j$477[لx*L~%Qf]Wc]fKY9E [w))*^I$~[6o5RCJRLDdVvNTD3g#W9c,fQKWwе+[0~BYaL3ӌwqaÔjiܽ~Ce[wQH$RΣ܃>qTEEx)ߝN<֋IkkkLlB]}T;::s/^4PpzLdxP"+43Z=}sKJޚN44gXyO44?ED R)Q_͵~&Zc,]RR4:&vup3%MEyѣ 6>!2UQ5VUUGrb%`f1wڌa^ɲMgϝ5sbCdwPSU))y;}JW2YQp+hhu9 ++R\\lm .>;9L34ƌȼe}{BRRBBRRS}*+etqo56:Bh۩Ikj8,!rG!#&\~ϗO#B vBhPԎB *BAS;B!4`jG!L!Р!T0#B vBhPԾG};s#nڞ#ݽY9<~N (>yNݵ.\MxWZ***:z[7YbtLϙJ<-(^xDTa3:~"=l*+*U\yj72*uׯlX6VK}G>}=N/#귋cjJow^kVQQѬ9WW3~pdkhɸO WJ!Nvk[;_MS&%$ =sD"9.]bxܻ{W/D}x.%en6׬۰kC_\wPǢK^|bL;[Kʭmh4ZhH$  ͸}GDDdN>;T\RB<='hjAtdJZNsse0(aje״B3 ނrOzQWSݲyexZPx(ˢ2R mm/գpd%E3Si]6֒[g+*O{LM\D$;ojj >6zHhnn6 9~U2Kf^9lv𩰄dmblUDD ZoR{VvQon~TkZZZ L{EEeXxCd25BEƵ+?tUQ6yBv\d]&a)=>߹d=̱em˦ҲU*Ç43-{fxyoo22ҽX3֭uzmx,sa CDELFynh1IIaglԴv3۰IJJjECCC ꒒f+ Y,Vn 5Ok1QgCTJ=6x{ssog?cK073ݸaLISQ>f(O ?-/'87}Õ tY9Qa^Ϝt^+ߩ7MDRRz "i]_Vokoߴq$%%***?xʉH$*)*eIzD"QRRBG{\ii鮏PsBBBJs,gLI*=6F&{#d2ÇX-om[6Qdqqt܂󤥥 O?:~&=0%Y3g%EEE@w?444B$dlCcVn@JxZ*++R\\lm .>wuh5ftFq؂x}ނrCRDDDZJ:鴊Jii)`*9NmmJ-,HbD"dc4)iuu=ٻgׇx}( Q{<,6[_B&{qYt]{BM Q .hBBB; 9 l4>*TEhFjgPDa6}̨A[׏߿_ GɥV{z䔕'JQWSUf޾+**9F\LL@+AX'VUWN]uxWZ:Io"h[/_Yޞ12d:3S#T<6>xTWW'Iz577@YYyyEEkk+TLP555>P^Qs#ᔼ}?c }.:&bq8gϙX-H0*R9p8[UTf??]Vv|Q/]gMM->9BEEEEȰ<&6}WQ?qsYy…WƔ_t^$.&>a1kf^~~Bbܿ?q;iAGGG|bF0oUH虲rhhl|U_0˻vs2$UQIuF>r:l_bbCji;ǓqKV:}&Ok=7m!tt/⾁3tCGu-_Px"$`y3LM'%)y<(00XHu 5yv?q uU$^KH94a6ENF@X'7 uu}"22G!..njbywSxs,g ϧ{q?5pq󨭭Z蠡և ?zXozX&6X};+;[B &Ϟp^}9ܭy%@wьXsZfhkw#¨v S [{-7Iz+;-BM֛x;5wTm]"$>ES;BGMq_/̿G!L!Р!T0#B vBhPԎB *BAS;B!4`jG!L!Рǩ}N] @qupϿz9Q96uLKVw١|Tco)k[{>s&17V8w+pT@G1wȻvP)I+]xEvZrw󉎉_eYةREYʅ\QQ&o *,|bTG/^d9ۼ=lnd0U@ؐXc!!).|n IDAT>{.jR;&[p{-+oK4  rBCm|}6Mt3ٺztED"O;,$$|"tKʭmh4Zh16|*,!)bws~o'OA  2m!v\}}oO:JK((|FR,gV135=WΘn"#-)%%YMCcls?pw.rLtL,źs?7zt 0W,kd>$׹DHh\B"J!nڸa ]'OzQWSݲyexZPx(ˢ2R mm/Jգpd%E3Si]6֒[g+*O{LM\0lam|llnerd2˗ F v]r!/@oge}ܡ?`]I#¸K.~x6]}jiz@Rh18y>+;'*"ŨΜp&%5t'GwAZz&g܎<&.<=x_vswjk!Ǐ x wO -fLKYgCCE_|3EPwVrŬ?.MB!;.[rK\-Ӥ)s%&߼t>f_2jtP杻ǒ&N޲444]&)qzLl<tWCUuuI[3,+7VnZt;kGEq-:iKJJF|8{3%MEyѣ 6>aۖM#¶lڲݯ7]o.H$ЋWTeeLf?W ܞ;ۼuͼ9:?ˤ̷NISR4bbCƍ[ $JJ X>LLnZYVUA .y[WW]iA3UN$B!uԤ$)'Nէ<|DVou^oIzD"QRRBG{\ii---å}̟'--M ~ѡ7))Κ9@ ()*:,]|-:G %@ erT*tuUYYUO`kcq˲ @{8132E͐G""sXBG{bbCo&tꩪ:bd{t9ʽ -3=LHdYP`hif-JJJ0jj$%%$$:uŬ~+WÇ-NVVFTU]-$$XIIUQqhȉ3Qg",-fq&H'+D>b0j*h| NQNéR)[E2Ö%bfD"dc4)iuu=ٻ?R>5 N!=׸r :vb=!ǃ0a#3S~۷G7Sh[2ӄ]^oR)UUU"2M^^KsK޾kjnf2[Ć@]}'ekcuEssT︕|{WZ*''Pyx{67->3RC#`Ϊ;;Er}55߿ohRR+~U^^Az䔕'JQWSUf޾+**9F\LL@+AX'VUWN՗VVavD>5 觯ioo~ 2DC]dpi=6zlٴqn^R-R(ՌZnysBBϔ@Cc㋗,xZLl|Q뎎ׯZ]+&7IGGG[{{ޓ|v* ŪotOkcgnpxyFӻ[V&8T ݻS7@R24._flldXV^p޾+\+ 7Ց/\|hhlLIMeAUNbblf'$&[CRttt'&*zmldx /9ɐWEE$aLfFϵ;8QWxmڪ=~f 8 p>"GǥQ[[+%-AC]E~SëjW̍' lT!=fZ9sl۹[AAa ~tyS.kz4#\֬:@MMmDy+;:*GFj } 0O%nܼ'wgگZhy!4M֛x;5wTm]"$>S0ԩG>ce1oAFMq{P yBhPԎB *BAS;B!4`jG!L!Р!T0#B vBhPԎB *}ڗo{lݲmݴ+ںwNPVV>yqxE:}r܈[ἶOA_S!ICC˦'n546̟tL?/\QQ&o *,|bTG/^d9ۼ=TYYeeS =>OFSk:~qnީGTJqqI{{;QrTjw|7gEajyS\9FXXXXh" tk)W9ڱUTT4a΁Ռ?s42nӂծ@H>cϦ)n9[W_O"._wzuS1Gq܈vFh1JMkllM^u@iiT*e @l6;TXBR2616Z]步eֶv4-4X{{ IL&SshoOaJ@P^*b4559.?on߮!DIJX;wyaJ^|bLC"x;::N%$2즍&Mn ""2 u5-7PQ,z-#-~"X]=GO8.[`[435!ekc-))!?@N/|lpʾ ՙAD"f kcGfs+/_%/^d0jlӌ yޤN"OյfwkϚ9w3%H!C\ -Ə:aww6|VvNTD3g#W9y <|˿s|s64XZJ 7luƵ1C诊4fH!{ZXf6-8AluX"f IG7//..%B7=$P杻$%%O޲BdxSS^ޞgΘ^V^n&))V ]3KJޚN44gXyO44?ED R)Q_͵~&Zc,]RR4:&vup3%MEy8}`l|Bdiy9ǹy\T7MDRRz "N l9oAбO8ޜ!Cɓ^wO W9 SevOu=&n2i))q1S2>s#62YXXqْ?p4 tPSU))yۋuIzD"QRRBG{\ii---ågJ,?OZZ@ Cqq7o3nSR5s@PRTtXZt tWCCC#P)J@&646 n T j'K2**% :>! yWT{w?3uaF.$T%*tT*ە!Ko%*( 3vco+&{//y9s3̳3zaw yzf#VZ9v͛ :h`0 EEfy3\tW^p  /*RQQn]/x`XxdHXlv3A> dͮdVTE  pvT2JVg7j*V8Idq8,r#w2i>{o#DmT*] [gTjR(N;s@QUv~m!4*Z$ܚ|^tVINjJQQ|2f9NhHbex =**Xn^gEݏcbWV 477*h iʘQ9TU$$*̺'!!1rpi))1o_Z'VXӦN# KJhCk~DGnYҒ ϗPWp8^㧖?9|P,sﯢ>xpHX8ϯtm̛VZZuu^bWVVV @3 ={fs D"QKK_6M8r?%((|q6 ʘEE@Qq8M1]Gee 7,&6Xq!Jx@r8Frfե~nkYy f)=#+y@ OL~.!/Vl!o޾% **ݶWԐI$}>l}Y Lg~M_ @`\UUE"VYPBٷ[QQА SU]mZ&6syvNn}Yw|>@xN}mرkzIo_~͎56Qر?=>ʩ z !}l]4Yz% [ @Ѝ/ nIޮ=KKIM4! =jvVZRjVUu.Ow 隟 ^ezuoG  |; yys2D`<  R; )(# HR; )(# HR; )(# HR; )(# HR; )ݜڗb{/XRt-L~mAiiلm~Y, ;nCă|O>'Nv}_7CFCC >f=ܶL8nM8AF_\qQ7^$HD"F~ Lfy\u3ᑅ/ѳO~Uj_\O@}){_T}va6R} -TyAa@oɑI,̭.;*Gz+Z*424 ۽v[ʈUb0 _,cs8.N9f748:صk󿾾"aC@CC\KWQ(W_ٕ<vt=]@C^֓:Z4hii8sZ};)/ 9yL xz9{.yc>sΜ:A+@~>~TMM5)]O8&|ۖC4HT*DpgUcƌk2F^z}_`[[Wg#gFxgWvln ve444XX5iZjM;rr'&߾|>\ZZX"hT2 9yLVVt٭;"oeӬ3J6nF"5m_ATb02Ѻp.4`11gCh4jKήn[]6::TW,\b5s]VV0:&vp;Æ@l|Bęӊ O98m`!/FWNAEߥ+ʘLrX-Y fxع&s b/]iuujْE㵵$ߵ4hTН:ʘG96I$~J zD\hz' oCddd&p2cGUb0jyQUuD@* d,ۯ+߽_EyX,VEYy7cr'Jm]b)ں:iSqc`Xy //7HMX'$} Ot`#ȼ hȋS5 rͱcn$ܑJ;m)fyyZzfj᤬ R^^TU۝/;ynK@ &\VFFV_p]Hw^p  /*RQ׷Ce~#CMMf;#hD(D"L"ٕ̊ Ag@GHO I Z j*V8Idq8,r#w2i>{o#DmT*] h.VOv*~M;w  W ]Qqk;\sdd 8H&޽/Zle Fii:+zlu4q׾4}jiwtv2/))p:D))Ii)/@ ,?Ɵ1O O--yCeUJӥ+W*ʣGbzZ/ѹD&^>$z;mX,VKSj6Wzb1u%`ZVTWX,VN|4/_#=@_ϙsOp[]6Lm64̺7eDQ<9BS)}{ y0U5<ꨅibC?g{ җxnF f[صGIIi߇b}|t::]dF4;ָ[d?;CAi!}l]4Yz% [ @Ѝ/@BA7AG;+-k_zUDqp;e(# "/sr/OGA>vASPjGA>vASPjGA>vASPjGA>vASPjGA>vAS9/&'q٣rMMMMMM[=gjcnҲ S+1XrA7vs+n݆n}|O (nzG<B؄%%Æ`л7BQ]69JJJB<?dE_GAzѽ?YX 4jb9Ɲ^Ԯ;UL׻w7or5rDsSsǽ>x!iE'O6_Pb0А+hkyҕkeL&E^^EY*++vgdWQ=k&UQV^|aՀÈD!ӧO(|:Lcnj9|XF]@C^:|Hxąhu&.++[Q%(d0rcʑh\.WT%\JxtwzȈd/*6X"lpk=@.y+X, HEEura!a&fy~8\/Ŏ|hhD2fWv4fŬnG&]Y)//'*UU4U|pB{p8ii)y3XJ#FPRb$پW@RJ@C^JT i:cwߏͮ}Ҙ|*+c6;yDY^1 |XJPp8\G}x-Y%)ώ tO--[]N ݳu\:tE&"_]Y)|\S[@u4fŬn: 9%U} Qaf= #KKIN~ifq,ִSD@KJhb-kZZZr? TstQN]}}uu x}iee1|>ǒ Q_{ -,<6,~܌`Fr]dER _>?]OǏ|~዗;ijjp84(c2FUV'\4  +--ںW|κr*1PMMMM18Frfե~nkYy fMEY A|bt yzdy-OPQQx|P]{~gRRF8vh_#y9˅ DW:x1~Y][[;uvk>SRy`1d{L"իwz܊Kؼql@cG!~JIIv[L"9sb45.ns{MM,6H 3_W wt"IWjWb׭ٵ>xﻱXlGc2;s\8&99VMv iiiC̬{S&M3s*L^~>Bٷ[x4b`k?yQ ĆT3h2;'Szҵeb莹 Wv7:m77bǮ=JJJ4ewXcjh2@ AC49U ki޳̻K,.Zt/A}tҒV\E$wyJv?wjO; ( 17>/tyAADPjGA>vASPjGA>vASPjGA>vASPjGA>vASPjGA>S_l`2M[45}o8tLxds+n݆ou_Դsٯ66(--0U"?3%dc'h}|O (nzG<B؄%%Æ`л7BQ]69JJJB<?ᣜ^~C$ رݭc\))Ӽz=&>1,7{&<KI?1?!_bz;}i;}.!!(aJ/vWF <a tXd2{}|tEukbnťgݕy}uM̈́:\X,x9F}=,;|P˅ ZuU>y"!!ajł䔠--<9`]y(h7]OiTii -ffݫQb0v+hݻd{ZX.{_n[\W9.;q;@5ǃC9&*E~ۖ㵵xJ F዗er);;#CG3pm7Y<6lhhh0kt5 k69خn!/FWgAKKᄑbχ C{&|{mmMI=w͘xaUJڝ9FgCNtԽ?vň7m*XO-B\8px>=kf­1\eſfn`>x /ϜaP_leRlծ[=jjjW5r=EE9i3N8v;qIqZcoD|DO4֍Ǐ=a,#C& _;n^|ʪ~疗WԆј~@]U\pFiS\Ӽgk7cmp.mCC߈^ ~2F1۩wT 6bvxl;cw hȋՕԮ5 rKW1yyeed9_EyX,VEYy7`@mq0b0"h6bX1?znp8osZrJZ 3t,#CӝJP>zbO=}Z-Y fxmLj222p8YYͱKJJ[*1uu޼ب:L"͘U+$B@apLbY1Jm]b)ں:iSh4*;Ś3yyAjjbD,(|:Lcnj9|XF]@C^:|Hxąhu&.++[Q%(d0rcʑX努kvQ)5<>]\cNo>g2Y,N6r85 %%if~KHru87NsL'R[}8=bpQ9,/`0+~Y-li񟵩J >_ba1XR)̔ӟ,y_TܺVY_ȐpSN|A@xxbGL4D"Db+;bH#Ij*V8Idq8,zݑQ#G()1Rdh|l+yRlv%!/FOv*~M;w  fW>B im1E<,_>~,R(8>x˖,OtrޒgG}Ch[Iѣ6ݘT*ךg`\WEi “ J˰XSޗ>~jiwtv2hnnU:bHSTPPU>x03랄å '}EdanXbM:EN9fEǒ:=r˚O|:#C<|SW__]]#^_oZiYYLl<$B&_5暚ڰiss39|muKR _>?]OǏ|~዗Nkkk?<~ӼQ#Ft&BC?@zFW4Pb0hkhh2$[Ui;,<ȰO6p)@AiPd/*܏i@m]ݫo{Hg]Gee јUm#GE]bkR?gFP|m0=+/??!1l0=#+y@ OL~.!/Vl!o޾% **ݶ >k,[JJr# 8p"/gpJ^9f/kkkNn-tgrJ6,L"yCDzNo[q 7n0mx,q?D]oFN>x*M<3A/*wrnjjҝ2Y[K6{ \iRb0<(1LM3g66oS'o7'B&۹lbZVKҽk^M$ǎۯ+W;:WUUȤU+5+@`ZVTWX,1k9s.SNy&Lk~fֽ)Y3s*L^~>Bٷ[x4b`k?yQ ĆTmԹ^!t ~^F H^|e~{sy_[-vڣ$No_nΦ{͎56Qر?SF@ ͮ| ۩io޾3jdo "!}l]4Yz% [ G_hm#A޽%%;{ 1AG;+-k_zUDq{ߨgs~ܽQ|rS{; AE^>!^?]_!  J ҧԎ  } J ҧԎ  } J ҧԎ  } J ҧԎ  } J ҧԞ|;yKoG H:pގoBz;!v'B^ ;8A[:6y7n\7҂bi:mpg# _pyZ-]l:XXQNЉS^!ÆhOQ]]it=v{?DJJ;ljY:_^u꩐3NNL$?4P[:={0+h 4Ցsa R^QqqoO Դ>vlv{m~{0eUcc&Gٳ}I~q$dAAh'O&$%s<}MD"x(yJ oqXlɠ޸y+1x^Pxwrd" s1Lii -ffݫQb0v6b y;wK _,*.f.NZcAWp܉t< B዗#_}+;˲%kkI$ s3&n 2$-%e,NʹS$%%XɓJJJĜ_EyX,VEYy򥭇ՒE`&,dc~K%%.OwHb鳌 @Ow*ByLdee4ǎ3PM-9%-y uaWQ6u HII=ugJm]b)ں:,{aCup\kv;Ҳmʴjw%%5>x7DMUG^O_+;FcX}q§i 0vÇed=qJ{q8 Y?υ,lEEEG~}q8`+#^|LUU6t6 :Q\.OT4b0wOO>IHHYbA#S]] _\ba0EE**twzw݋ uxy9"Iou~+ϯ))I5k\Bqc>GMu@;XlE!_:_\v@ p8ii)</FH=NI޳}{w$jCRJ / Bݟ??BQV < **(hlhl̼{O-[L?tVIǷngAG µ?LLN "BteeLO`t* saCChn}c#-,VK/[(6>yKRb@  SRffݓ*pysd<|hltt4^-R h^C%~HKI}q}z,.>Ś6uYXld6tq8c'662$tE>lveCC{X0onpHXiiսz*+tE<_YYuXKIIJKIx @ԃ޴Ҳx>cIąsM:b˅`cb\./|ijjjp8O}>ro AG{ۈQQWںԴ٭z_TavmmO>y7jQ)߼x|> s4ԟ~+.gd|_R"f|[̞l6TT@ '&yN_O: wm^~&-$%%uN޸AXy~Jg#bjM&gE?yi#oudl49N(_7eDgj9lԯ"{'禦&)4Ubk~fֽ)& 9r&/?J뭨n䧆y䉎VG-LR,;'SAWv7:m77fu\ndhtznصGIIiZᤷ/N[gӽsy͈f=T$QرiAz!} ʧyϸ\@ 1F KӼgwX.l](AGvf/ԣhnn8{PRbݽ[5K{]Uu.Ow.wPjGSVvxoG|E^>!^?!  } J ҧԎ  } J ҧԎ  } J ҧԎ  } J ҧԎ  } J ҧ={; A_lrrvH=q* x n~æGtE-'N>"^Ç 53 fnrtpuVֿN勍UDuu͓yt[HHkwLxda .7x@Mw+/jSz5*[G۷#/\zuwva]q& S6ltҺW$E =.]ֺ!y9N6F~zSIbIn oq󜮧{Bx̍+Zx@3;'72<;[gf4nj?PWWŭ+/N |Z@jbǒ3!'._͘3!o޾^VYF =ݷvt9,Vq# NrOih S;eDy6FF^eCuu%V3g1\eee cblS﨩>l(|Cyqm`.0-hjjJa6TX5}3zawfL*dIZJrYꝌ6~XRPn @R)VKADٳ޻y#FbcGf0]$'/W\#O_ R(uuktG帱cX5˺439z"UC44dIԆJٕ,/w/ <X,6IVVFFF8s#?ȋo?btaCNLcGFRD8`2JyNaUU4U|pB>pR\5r#N8'yw`]e=)tsr̟k"*gVT,) tVIwFohp$"G~(gI,$$yxN]Y A|bt`!aeP[W6=WQVy/Hhp9jԥ a>qOF~ W#hoq>*5][W?u6RR_&g'$&BWY7o^eV\Lfhkjj2yR)L 9OPVTT_ @`\UUE"VYPങY^pH N8~Ϯf)hWY>z{G~43`2BTz/dss3B1:i3$-=slcmνA_3"<4ɑl26ٙn!--mhuoʤgn䧆y䉎VG-LRh܎N7\Uta/NG1"^|e~{sy_[-vڣnpe6fDce:~9vlϞgt!2IVv҄ Gg |C49x#:kki޳̻K,.DYkitYOI= AԵگUU]l*"]鲞Jtn~^.78 Pbn|^F AOAAAAOAAAAOAAAAOAAAAOAAA^KLf| @If{_Q Hǽ;}|O (|F!A#hTMy4o.,\lcI7ѨRrJUzӦ}ϷttEEn۽*k!gŸJKKD뭬ӝ2}@ HHLpw便fO߼ɑH KA:dok.A|wLxda .7x@Mw+/jSz5*[G໥vf͜QmJڝ±}y@3M5Z߻Рù|I)GN6uWYW]]zʵ˖j}>`0eL-Ѩsڼ%|U5 ;U|Rwxح]BB"QA,V_<:]x^Ph7BQ7%%{hT5K>PZZff,{<8$.!iRm\UYt43gltt= z#C9̜a`h0vb˅wUUW9YFFBϞ9bV&ጬ_gC h4%MFUd3224`?iSP)aᬖ.7o53h@,|Oktd2H$]31 sDYwzݼ-\m6xf4{#R+LjkFװbwa1u4MLMmd,iFH"{Z(RD"BD Ey_K҂RU좴L54M,?Ռ""=sw;ggf ѹDY9Bo&55KJJ8:3[ZZW2 zÆ4076rsprrRRӧn"9B*'++--Lj'ϞOsڳ/3sZOx jF& x:PRRȎNdGn2TMT*MEYD|[I>y:=hcneg>-~yPxk1 `0'hD* s8mhije3ec IuuASm/\Ly9kim}}mfIP8s$7vxX(Vb!c .;u:9D± fGE߾sP]SCߣ3?x{'?u*پkRR֓-g\#{Kq}#CT;ۘczC/НW(jGHpȎ0 k2IIfʇݗ_GGE ꃶlRűzÆ"ls,ɓ, [90h˦/Ćthm-}݆ۂv"2KCo7nTW%$3JII677Z:sCح8n8OiV'Og051 KL>}jǭ[ieaޙn!--mfjwD;)cۂ]]x[[[+[:WN7FGC?y?j6,2?_Hp?]au R.;|D>Pw// y" A}oz,[ӁlqV L"|.QSd@6!\Ymcr7Эa.t5=}<'i<[oAK|O6Ԥc 菺}]UKϞ@+9p=rn?<AAݫߐ  =صCAPv Ua:ݐAWAԫ zصCAPv U`AA ! W=]{NfZO9;x_ٓ~ Wq:@^gt07EG XŧҳNJ?jld**ƛ98bX :LN^GI\_-6K:[ GxUUea^)(̘ܪ#~ <pp8v9 IDATu Mk-;Ijo׶?کڧ# ?=}y9_}2WWV.=aO|xfP j?@ OR0.K{v"}4jjϻ x1݊111ZUU8y"M0t+)(#JK?WsS$RJZ؃@,NSOGWc2`" +0D"# ) CP(S+kN^fEY70uzkK F5>j<0 ̸Jvr2QWgr 4#+k4fR^nHzkk+ԄB<. +j۔ӨB^ƣ)/>,I*Tb32bSkee=bB7N)\T Wj rDȘhfh۸kߥ3'2tQϢJI YB^{ aEG'jinO"C<˛zlAdp:M-xut{DRQ7pؘ&m8/q?P_G"2rƍO9ڧl6ճ<O\BBC_sSTQrs\JEg-tI$M<;Yf=3/0{Ҕs/KQ r9o ѹ~ǻu )f-_1!%󚚚I>$䇷M:-$ O4: yy j3tu'r2ӲӯnӁ@I>fISl#ڦHR` 2g'QI ~sIO:>,p^yruoA]rcp¡Ȭ+]no^ܽceU*|2E˷ e?l]+--l톞 Ӿ/<a~k[w]{-z:  p Oo@z y   zصCAP ï j ^vAԫ zصCAPv U`AAٵ[\/^Y>Ԑ|Vbw&"dGfn9R\]ⱼ[~Cc{:/_'b]{h'@LLLI8dRq,5' #FswR=`_JEh;VAP'ݹw?.!իlGG[yL)Vj[m554ΝJ!ox՞|wD12 '+՚$ܾs=mϮMH<ޥ17sաC{?d6"KJfEPhTݙ~joKG+ee^hldxDBʅ]DDLL>ٙ#޸{#!q,y}DBw߫کTڞ斖id^S 1hrOi))i7ZEDʺsC1+'&&rQp~+Ǐ3@ \`~pH(@xJ544q6y=X<źv5!kld{$a%$5xQm-M@CsC|lW/\U`l {YKkf>wm'Z[[OϼbϷ@X'kl6h8 3kll46hy<ޅl޸aSop|VUW,\A7mX}JD  1\װ[[A*|y؞}T*vZc14+e>96np=w0&jzD+]j9rTjd=~LSo\BiZC;>zDBBy:_ãW{#ptp5BH~C1sfuLZc8:eeeD(+zRb5dZz{y.&;FG d2l8}<Jp8^nF@x  N!(X]M$ ]hl }3vڽw$%%wqGXɤ= gͰϰaZO& gs ھ=p9%).VUE}q7r!IqGh鍍\Y7s]f9޾kV0˽uMgNO4LMXv#KG[nW^#5>sA_x/XG89Z}:9v*O:_(9ᨘcIKJ:~P>m-ԴP3S%%%3lgKa0wO;|DR, /K_BB\<#`yFG9&`㩤84{9mN׮9 --]RRW谪oVrjvNqgO%IJH10h *)En^}ƌBkhhXJKI+V0נw)-da6I0qx6s]]uׅK)D"!i_u99O22xXY]JI]jzMM'QRT|ϪsT;l<熼J rrEEe˽edd=ݯg M]c9\H$ '+h_HUE- _Z Ū>{B+(!%Y&C & wE.rn)Hf# xv#pө(+3/_ill苓dmk x2JՃKc0u55S#dBG]N>j-h\Y.C?C}=@$h4P`E&'GZzh42GAA^[S8:G\M2h6tȠyBt y JVFFVVKJU rrkj|a0}'1\k \4a0l6spB҉ fJee@JJ2zzKKYsű?FN6վE/v\xS-Y8сF&))"Q(JDaFR VII yArnO03!=h}e>_XT`7;TcFKH=`c=g+cQWGnӵRUB[L6}~GLBNaπ4H[.[[K' sIoa0ii)1] a !UTn1ɳ;mE@Ri@H !kWT$60,%%bd2ەh4y7s }X2ٵ}+!><.:i˶,qqenQ $I$uhS몪*'p6&FM&xo/.++x<^%JD*WHOuȋK)&F+}ɛyE< |UsT y\W_H$0b7[#p ݺf]`P\b -Z61wBΞLyݴ?)_*+)ȉ#,54^O[w$$$ $-%%"ai55'RòBtyu5CGDjd1<"Pss3Ɍ<cea&z@x jk dKK:Fkkk'\.WRRR_?V[֓,ĸ$᧲K)l6zjZαmo}pr'ORlvB qq#vmL챊J@= 331NIM+.s?|@6.O#zr駲/_IIIJKI!ۍ *Fr\gGr8c IZMM 9Bkk. 7]%@$TI0$e##L&B31XQYr?'8<$ޞIO:{Jg0st1rUtnl(=Kۛ{Kq}#C 4$xӜ\ id;>j"#""ry޹MJJR[KSEEB7vL_y&E\\|dKs/:gb3dРDz~ <}kkkpr \151dxd\h/%$$,w1p^[`0E4l칪*X: ^de%_?۷ c}}Bw!##cin_P;֓o-jTqް  Xy\a!m y`,VrZ?e/:{wi4Dx0/$oEAob[ٙߗUt ı+sF];A )ڧ y U`AA ! W];A*k ^vAԫ zصCAPv U`AAJ7w.>zc'N{c wVMCB(znyOźNd݆A5yY9 v2O4xh| C[KkՊlP}<}fldBݹw?.!իlGG[yL)Vj[m554ΝJ!@wFByуL+++xYԙs.HJgjM@ƕ  曭čE/^®Gdel XxK Gajj:3H$7/^rɹ@P?cNܵׯ5J$޽/h sMXMaroƊa]$%$9NlJo/11%ǎmGX]}[7`!-#s "=zIi)?1=8|WcI.E ZGDDb uV"ڍGح<4d۫Dq3{,dTс,++#:78$TEY7*'?&CpA{q LkcltL&h́gQ4kkM[=p9%).VUE}q7<>я%O%KJJ޻B '/|pTLLݱ$r IDAT[f4qpݓIq-0?q)MMMUU:ZZȤn?\.eH~A*P[ K / ttꚚOf'gO=;~¥X"|kVx{uӜ'L&RJ*TWS{{Wܧ:r7|9=&:9S%EE nd'ݺ#!!1d i))st _M˨8ar4J%?ime (T|B9f,V#ӑB݆H{_p\KC=SgP FVv]=.fnl(=˵\N/y<^ZƵL ^BīyܽE+>eF_$} <}kkkpr \窫m Y3iSpHlt_eTaldKOWomRl\^=(}ls,ɓ,t8SАчY&yyVf&ykOr86YQ߱-XIItkHhZz'OGV&E28𑈡sAP[e+|{:#6nު  U&wIV2r  :0Эa.t5=}<'i|aAaCSE4byXYFГW@νq ɯ^fs8:ZγfLdnmEKk+VP5br/U!bh4>/Zildgv~y7mKYyiK)d;Q1#DzoP8܄qcD H[-)) ˥2*UHV_Ӱj[m554ΝJjUPpn0ZWDq~#fN-e+yIKK>ziƀwY/FϥH$@.+;g@c#'R.:'"`b 3x@-MFx Gcx<3OٿhQW{J?/ھ5(3Rt>p'^/jdYO]`(UdGׅn>sކ<.3NH$7ވ;rHE^9z av݅]wM˸V]SZ\SKKr;>zDBByk7Da| o[wP0hl:ho X,kްa;f:߻ph޲mGӦ/^0B\z(W#JJKI$?_T*mODd~~asK4ݐc[C1sfuLZc8:eeeD5bl$|ŕ  %<.g%/+/WR$rsT 4Lٴ%I#98$H {_L\8Ojhjjڳ/2;'ź̾p[ڼ50?KsJA!162Y\.8Ͼ}h_9\o;{5=j"֮^UKv~bF'\`\R&[yp8чg^c9&F+m($;:HOao߽gcck7KK?]8wdee7_~/w7dѧΜ;qlKs˸kWTTTϺ{o*Ƨgd~ɹ鳠*v*9YziJJ듟_xa~iaf! CӚ5p@ɴu8sx{#"w8o֟_(l;B SRde?yJ_Ru/~H\uDS_\f-ѵml4@~:ڗ=mǮ{7o @ _KRRwL+|83KJJ 899a7 6=6tמsP(^aǒEQcI %'_w,>cb_e";j@Ι=ji%׳lX/سP(c#[whmmTV~ɖֵ7FYz`1p0r2IBSCM0l6;l>O%W3N9dQw͏.$䓧}׭_ۋNl>tA".++ceav)%uY7558wDIQg^>ΝJRQVpt~e{!?rDLCP 2X`F*FT=xXt1QWS351ʺ۾Zm-Mmm@]]%%ŊJ@}}=HhSHG^ZYP(u55;)׳n ɻL"!<B)(|'''##ӥP7g/ ѹ4{)#G DH y9G beL,lNWQQ 056Bhk+ȗ~d^`'!!A#aYGVQQYr/wqqqoO---HiScFPQVf3.^ب'''bq aFpWO25|}99899i)W222cFc0YYzC+V$ܐ7YLLLZJulš+W-裮b55:xll3Zmޏ #k=BoFF((kkj~3Grմ h㉆JaC wtn|~a~Dț NJAl6B͙dsK+ɼvrզf U[KryGڔP2͢JKR(UKJɎNd#EQ(U222]O43˭ѹkڦPm_rמ}<OVh́ jii?ptx$<fs55T@$_EGaXoFTU)(su55ya0xr55՘c%XOY>VX1|0$ "\?j)z8M{oስ)/<4nKJ8\EP _a~HS:l*UTHg`?C3DD~4c0R^h f3 VQQκ;j'Ϟضs`oa;kZ]ڑ~] CHJ($gv;rH[K0I=jN3%kԧϋO6BI$uL,+/o`28#rAIQQCYtyHHH z_gΞo_+˩W=/r H5>'o;W]Mmia{aG0fƉ'L&ÉKS@o4u5#GDjnnf2c,DP>D*6~8jĈ!xFASlcb}~&`}@$j4655X,5UW?xh>ɤo>~kֿx48$Nwį<.1d&8inշ,k*XZOP8Ϋoӵё9 dgEE_>[{Kq}#C d@=ko jii>{Juȵ5&GL0^Ԉ3fMYVVnb<QMMؑK<&n :~!gNVLe>ӧM573\쁈km̧ٻLOsb?j6Bgjbr#릣dm;VX%:w,.;eޢ^LfVǏ3X4hvc)+|@ZJ*4$X\'O̵hǒw;rwSV&Mb /߾{ƟF۱-`X;t lsҺp\ l4L,,^ed8!$xSj677Ol>p8Oo_).=z0x@+ .t,[0!>Ȍbbb" z}7 ;|%:p@A̞`GNn!--mfjBƏ2kC&~~iNӧ &@H]!)}= ecG?TUuJ+6!{:FIQҿgz: f̞hhX|a->{qC z<A`AA ! W];A*k ^vAԫ zصCAP~_ AU;A*]E!;:2ak2oqǿ>KvaVEE &fnCB(~M:Rem/Z{x<!K g<Zt&ܝ{_zptgʹbZKIIu)CV4 ?jp^*ʝN{a cII *ʤɖbO-r462ܳs;<Ǜ6ӥ4iϥT記Lfc7skTNn¸^nH$ u^EEYP H$~ti^UUÌ6N%u*C]j7+"kwQu'̽şd2ynG,\.wϾH&QtQI'ܗ,rɤѣG-YS~pld0 OHHpv뜈';+A45Iwr'@po F͜[.u+abბ6o }ޚu}>,R) E/cII|bwlϪϋo L.\IˠPm111 Ik ښݻgېХ~it7qGc3'Z$L׮kOGh[4'/\a3p8J cXlY39NlJo/qqqZKI/X"i Ǐ+zܴ̣uuCZOQ峪fRO }@zXPܬycRuܿQ{(s]hȃGcX#ScŜx\I7lGhX`0$R Psg!fX솠-dYYѹG256s`ACBI$%ϥK{dxؗܿϜ;`bc#C2i7Zx1W3X&^aU6~:" 0c FX )W.7)ߥVo޾E&۟ee~6WTH$RltTi駰޳l1AX٥.;2/v?qSgΝ8}eX+edd***gͽ`ja{=y"!!P0;9?}BN%!K9 CBU_~SIb|} ?o`2-L<`oh`Zc`2VGc>{ǿ}Jq8/w7\FC?)ݾ{oϞ{{eCvh)Fkii9p9---l6A~RfO:c0fX,CW߰Ꚛ%%l6;&ؼEKi beQcjyԲSa~!)f~_hjiۖyzca.Ұ>IDATyy?܆6mfSRL-۬qF "Ȇx=a1߻7oߵTQY99ZZZL0ͻ-:f{OLgM019޸ykhvG{HC6})%~̊JdޱȼVdtz$+il6;f*fx, ڲV[[WW{߼u;?7o߽~fxRѧ2$*SKk7oVUW="/vhL&38$t![BXm;w766խZ>0h R҆hr&>{~+;࡭Ìz6}1n:qLĥ`مܽGvtjmme55T3+G3l!'xi3ݹ{Nsy)%f h#G]l۾swSSS=|eH.~zss3yTW {H $1TE+ " (*Z-*ZVE]jZAu\[A!-(;yfμɛ7IMMّ6VVV>8xQSSk/}#>zT7O=U􎝍j< ._9ǟ<;Ͽ{歬73V/\O&g_PPSSy'y*_<_ZRvJjmGwqu8ܨ AS>'fwϼ|׷inn3 A&0tcQ#/K31sp gGJEN=}[6Ax-^/>{ѫg|(--ej߈`J%AU[Vog⬧gƆ IJbidJڧm k[F;Sg;|]d_r8<ý7;#@ ȼ1wvNhޒmc,-- }=mmmrs.M`0 CMzxpio600hB/TUUGJxޭ˕JO>[^^nmݚAkY]ii)yĶ`6*k7Ry&$WVFֵ J559;u"j 6ũiqzzz&Ɠ&-9E@ ST߿i_}b׷f[.zOQ7 )>ȭvqr$`nnfkc`# ԣq'Ν:4-V:ϿEmY8CV`0t`?KTTWql̖q?N q_qPf FУR5<|+Sg՜$ SRӓS./MMi⢢Vxu⢽D"aX`0EbxH,&N7}'k, ۽E`@\TR),S. juPT΀{b;:tXdYUU!'9drD hE^xJ "+#c:zTjMD"1fQ_~* -kia)qJeHD^WJ%Ǔbc߽3C4G`0ZbQB=qL7τi'%R?w>Z(Q('99VV|`0uW-^K vqqyPHP&L|YYUս[W y<Ϋ\3:74? [DRMLշ*>ԑ&_Nssqu{MĊi\X,rǾNӴXKd^>H^\\YYYRZjf?|9-} :'9DV`1sg >5j[+"b8 rqv444[rjNXZv@R_zd֭l?ɤY*-;#f4ΟnK/f 6[ jP.֭/^JneS::k zzz ,/>;TE@T5&vي6#x̄s8/HdF]i,歬nQvLVɨzXXk4KKvL&ȇG  ?+v]充o,w0~)P_5zRJuH,u;{ڗS4;!-w}}BzpͨCg]`o/X}Uwq[~STNK*rjz^=E|~+s%B9v8TcY݋zwћyðI p>P$ӻ-,/k7xE RkߎHP(kccccc]$XL##LVμqSTٵk'`P{NRG>|5뻺v+;q̭*:v6߽wT*UQQ1cWT$9{<9#c8:`يdC=y*,4Xb=JZvCgryŭ,ճ{ᇖn..WE5{Y,HT*+**r9ܽ_3ЯuO-Xϝ99"%`|UUL&@ϗev)jL& ڻ?N * |L>os6 .{lajڕTI=;FhN!_N5w~A]kW b7d N Jr{G[Y|xBg͕H$t=hDvm53$&vwxpvr\`^M q'{̚rZ*s9ako?lcz=E0+~mw>}-ϵ׻E7^= lj@rlZG75 'V-A~#W;c\^afX0+o3u1Wld3mZ "y;GZep\_M 3;:jӦmQ[c*}vFE ߰y˰Jbtș!ӆ5YݒkjJӯo{`Fux=}q7ianFuwښu6 022rsN -.[! ;O|"h+'T ӧN!>22Q*ҲRs<3F\)/>u\RY]]s>[6כ)m22W^$}MNT>}Ơ?tʺ{਑*h굗Sӽo͢FJʅ7n!ۯY#5~9Qeea3j7yJN 27XdE yB裁!P!)B!!)B!!)B!!)B!!)-1tݏE? Ͽ5wWEn߹}:0Ҳ2Oo'~p,ɒAOf76P-Ws߁w(mmیorS'M3[]ȳS&5폂#-NS_PX7btBk럏Ľmt&#>)KWLbw2H$5|U$˽{~@(ϛ;ڵ?V&yy5#DsET*ee2;uho2oĞ蟎g2> Ōi}=@Pع'ŚE~_͚a``wMR/տyFߎ:`bbk4߇kk{kmؼ|b_oMMwǟl˱'[~ز+fuKص}[1GozX\[z{"QnS/}{ʺݫgwyH :vvmlf4w̛3kFqqqAMMi^Μ9[eqvYZXܼ5c?q]˼`2}jSuquO=JK'O-//n͠ 2l``@f'$^Ҿ2t r6!qAXms~>Clm1~LK 9pt/jFR`^Ci^*68;AK/d1&&&*L{NqEorqr$/6@刄Iз89v!-w8}&!hnbl TO[.hoLs*jbb<TjMMM|ک#M|3uX,* ׬ߴas(z"W./,}dپ~>d2LF K~L\R` 6U ٩K8::Vk :Y^ӧLuj.֭/^JnV]~媡![ۊ{,O=ZP [/@~h E>{1BAa³]ñ\`~]r:)k/[V|sԶ핕2lkL'%nccy=qU2k8@P(޻Ç8yVmJUU]u;9g{TUU=xh-ͬCG;.K6LJv 9r+ Em&U@{h$|NL?W]v%;*|GÇޛ_R郇*ДKJJN|>D۰gX{sfϝncBbF4ZFf˜65:vRiYQN̋\Y^~>?3C;pBñK?ٱS.03c, }cWNSޣ[6NF{h$LLLEOyT瞛NLNOhmk@ }܂#Gh40<2czH$µnWAӕfvccT*WZZɲe]zzz)). \. M  Baj5vV :7v׋dFAT"Ii 8N(Z7?_9Siv;GӟȺuy\օĚʅB̸O~:`]^`0nlL =Q@ nzBk[>DwOSLNMq KKyQ9{<#cؾ}+e1>>@fvv7zs$MU,$7')h|vCB$1:6FQa!z vCާ , ))v 0=$-zۛfM% hO^@PPUUIyY)SQpAɄ''''h4b0$SS7$^5232p"{&ToßQ Y./+7brjp8LwO/ģiiVl$% BXҬV4 }}$uxgg)++e\nAB);FINNfphp½ z=vp8Brr2x+`4ETcN4cX(*Lu:L&&sۛJHHLM)gSUD[Z|RS-huZX '77۞?-J]m eeY$SUY \x "U ^Q,(߇^vŲ&C Q(w_ih<ښ%*@ uM @ VF =@ 1@ x!x#,]#IFF# xʵ7pD ARRRy0jY23SSS$''5uK$@z^,>N$ffbrsz`^;׭t+b~"/H\Ir*@ pK!p> _x6tZ3n7Ϟpbe` r%֭ڵ.z=6(Hg/.#-ʹ]p㭷"!{brs/0f˒˹Xn],J\s[wZgϝ';+~lR@@ E,wϸ1MZ=^/^tY8N t[^lEštenzzph= '_@,p$A$e RhmkrDHMRH5.42:6-35^vA[GiV+.5s,C#(-[ٳk''Np0x~A ewt"IRD#"u5\jjZ@@ <8N4hXH\.ihh9Ip:At:2DPlT3'LO;N'Y/iV+iSf޼V\Vofl|v<8`IIa`` .I(,,bI,v}$232el|$ p8&=r'OQ^Vʵn lLLNr0>1\7of|bB^&S@ ʲr/+t !) *`0@ZZv G"(JMffccc &&')_8djj$48MUdgqrvp zabIatlT v, A0tQTX4pnRSS1KL4ʦJJ3|F'!b!''>l6^N%\nfÒLOoE ]fSU%> @ X M&** kGR z~?$INNZJ('Hǵ@  c]^pCXs*@AP(b$@ M߰$'3>>r@  O$raIIj}8N)/+%Vd19yinVm-m.6׿Ptw"xlFG_MdddT*IOKTsx^_K}qz49YXRSѨ|>;b7FFH`bFi"$,.IPP0-TUUrjuJT$I NOo33nRy7him% h܂@Thp:βn]'Nšfeu)UUOLp;nJt HXJQzzzٸa6[WF"N XKBBN' (EMdkk().&7''N-t iii ##P(L^nT*۷RR\fVVe-T*TlexV{ SSSQPR`!Z-7l`fbbUujrss0mTZvh50<2BlL1IIL;z  U</FQFGFQ*b- $$Č #RD RHOgdg٨ٷXneA*eTܘ t-x^$ #=!d.T*~]JRe KԔ4oyYOt'NIIfrT nRRRHNNbbbm)**Ә"*EqiodJzZ쬏$fg}TUV Eސ!但-3vrrٸ~=Wuqa2r]L&#&»:Lv?<^O~k i:M 7^?" ̓s5!|>oC/7n 6n쬏$3׮uQQQJ&͚h[0<2LA~ |,/b#Eի=v܁Z.`pZc~/vp$I\3*9zo|_g798]\t#!o@eTj e%Rјf 4Z3͛Vh5F$CnN69+] U+]w͝v9h'5-׿_ <ѧwdj"6Ϛ~P˘X_P; Gg0515={>trrO<M(`>4/^_Ѩ! b2ٳk$X0'<2|>n7nM$lNl6cNLd2;h8v[V6Se9kj*YYɟ1#üg"蘞&ZZBբi5$ n;`!4Ĺ> IqHyqfpb#WFNc4 =g+Uy/_/ٴ}. ;b\a;Ď}+f]~W[[Hx}Ib]~:ېO8đ7`0ȵVzۘ@O7^'N-;Wj]: `GF}]F2lt_mg9$zG`4jEV3ۍgfjxJ7^}^cɥZ>715'~ w,a|b_9i,iH$o4vmttvbXh4bIeSe1 hniv_ElTEwuݹ@4:70qDŽ܌C8T*Q*@Dbb";>~a6F|tv^ef ȴوD"o&%ŔjATosҖ_I A qzqݱn>'&0'&2::FZuU쩕J4껋{<^͖"%9i x{gY YybqϩO=[wXi<6[wq@cOp oC>osB '~JzyyryTo?+HTVX=c#Y\33\.\33}U*bBNRbHH 11$rrrU"߿hŋ8ThCE$HD, }gwpEΜ=$/<#>̳|+~"h4q\P 1'&23D II?jC~`~ G"*\>22JV힏[n~?=J?u=Aۯ_\#0Hb2֪ԤMPǍ)Ѽ'g{ =]+(D"cm>:x>*o7MzvpL1))|9wCݝUp(;zwJjk,_iff\NLf3)1f2%t,5c2o}tu'?ӧPՔTT1>2 6㚞R>>w.}`uZmB!;8{<}}l_/ I]=>}۷RxW ?ًTmڄhbffW_V%)ֺ)( $pòr#IH8WBܵ $ 2(voOOP(U*22g?gN~_CfFƪމE@$111InnN H("̌iS&Cڼs®];|74x@ՒARr3O4j5HKMM[Ә{~ShH$B&Q^DDb}" @VQQkhԚTFA=VhbnSa߾=$bTPFWe~*|>?rIOOgj*/OC^ohdxd ;PR\4gYŚ]Q(0h4 Dvݳ^~{:zzzٸqavccc7@> ++$ T*5H=z: IDAT'Df~̮ehd[f* ߏ?h40=dttWA>CCdee184Fg>⥦+8 ),, RR\ĕ :p/>f+[;S[w_?hsc2˿s^ llm#<?"lRRm_ D3 ? SĬ  |>¡0pP(D8Bi ) <+9%h`7zVŋ/PY)*_ٌl_/+`ݼ+KPᖣv9swd8*yh#4U(**tAM7@$}lx$238PWGazOd.h9#G9|[?N$1=MbB. ~*B!Uk7s#*j_R=#jb yh4JKk׺۰}#ĩw\U,=K*?ʗ%  2ACtn9 Ëö>wQXhjZmAVJYj'&ݳk]]L&#˖Xr ً?x' B[lu:~峟frjT)vlʕp8޼)>(0$'?@;QQ^ƕj]2o#yslonlH8Br}`H@PMso"bJZ>7oš墲ZMzZڒG@J䧯l߻jyxs+R=(IkA>Jg?,IB)}'gCVVF\,4^yy.E N"Ѽ.8 Om|ۜirRqx?do}[,gWqE} k,W` Hjrlw8I<kzzNLNV44'%%ښ\iie׎44ySHH8B$ː >ZF[T(Q(cG*(e>? Q(Ujj,U޾>| ξs=4_Ȳehf`pٽk'% ~]ފm@@ Xk"0F R@** /N C$$ca=bA&DtH$\kR; @T:ǯ z$I~t:݂XQV{h@ ^_:~5z1ƻ{D"ypY @KzRiVwس{ ')+-噧?91b]Q0Fјo$!E%$imQVAӡiiuil]zwAu=đC(&Q\T˯׿\8w}jJǺ;Ӌً?ǖIΞ=(|I"#3 j REaA>'9Mx\>z>sgOLp'NQTX@sK|^~DU܂h@]~2'?"oj5pcDQ*DʖA\3.[IK9wwp`>K4eϞ]9ZOMV}{ePR) h4G% Ι3lR-wV*B!T*5pZ$Irɏy}\nBUeF L-?V+l`1J˥}V֭ԻmE 3:6FRRzY}( ^\T9 kXymmXRRXp1@=L}듓0?zNVǯF hҥ&_rl{vGfDROTK *aD1RťWp87 q#.^f`p"ZZ0&x`Ш(JRSSi~?l߶$@{G'9X,)hZR-\>6UU<$lOiI1wtRYYNzz-\iierrdJJx2C‚ov߮ .]n[̠@~+ۻ[wNYG}q\GGQ;+J~#a/:BFFkOsK U=zoRm'_cA6 Qj>}6/ukR$<7vPJFSF99=Z_RUO|㔗Ïx[pcI[G”sr-/+dSVVJ4XA > $x())tRYQN{G' BN9YT*Ò" %&;FQa! 'fn)*,S>#G9t`Ml`҈D# LaA,`^je7[\P(̶[HJ2_m(?E׷νtPo<{׎]Puyyw]ϛ0=ܷܺw],:~3w!Uu8.9ݣT{% $N* V+w,dۖX^?,SSS[;lߺVEOjj*-mmۏRʊ2v;JMTo2̵6C8Ɩ)Gه556zKu&B޼ %EzsVk*.2jCd[p!-'5W-͜nUεⰔ̉45=DžgO]~Y. F Xj ̻c-٣k+mtزu ϝ%%%P}X,?Z,C荂ޅ,.S,_|㠑/#$I|W_k a#V%nFا4ˏ}OtZK B+zֶvejo >ħ^<KYP4yR뭖Q/ahPmF>4/:_O?W%N^:߶vڸ˫,YYKP*@b}ARv>b XSNzЇ>`O>O^1;P)U:t:}|CףPTr.l_3j|p: [+ht(^{U7}J[G,pv:I2FffHINPP 7Fh4lR+ض>J$e]9V/w%}37&;n{*BZ&flRM 0&Z& hQ=ϧ?I.5]aSe@ MWZȲd^a#pdd (_!#=O||qpe1cB$.[bBm\ I^^GӢR*0rj닷ͷjHL4x%O#\b|>.75m#%99& zzMaA>_53$ShdbrR~)${0g]v d=g܏ٽsC# eD0;;{bbrKXRP Om`Tj.7pLSxH$ x2qF.D" p@ `S9js{<^`ac(摐G$9J%:IL4uUIr!{͋0Xq=$&VT]{ eP(ЈD"(J9} tZ-99ٌp8-olfiL?b;,!7;LJ -1A9/wsa0unB$,gϑdJsv*3#&pʲ_H6[l LBBSl䥗_! cbJ /^`0HwO/H }~m6 hh<YL[dSXO[{z.IK#m`)zt:n7>M B1AFzz,Ďm[ v;Vk:<<9Y*uukvΜ;`XNp\o$~_e oǴ,B^$-9h73"f\'bz1!XX^OPZ =7rXP rEJn- i?|ٽ\{zy*w۶nAAsSSv23q:l\u}u;oښH@OO([ĭ]jkɌVWo"PW[# [6o};vn*zV+Vk*}}lTjrQ^VF!*l`1v;Y$͗?@8GriRzyC7Gym[D/bZʒ"::с՚;o`k] 3,_޾>֯_8(ZbZL&|9tߵ흝lټ3Γe15ea4^=׮u֛obo-F1WAT*1  %;g8a^?@87#* Frh5wu򺨌Y 9"p;wlg?{ 8k ??7?.\ƍ|OPVZMlTE bg]bI&#=Nm•RS-m^8jM]`Yq;wl+UV<iqQMrcqbQnN69s% PҒIχ ~+ÕVP*DQ?X[&$_x=w 3 $f[ bI /c4b}t: 7hx2[c/Z33n s~j# $jS^iiYe%XRRHJJZp[ZHJJBTVX,)$$B ׌_=W[AAAA,UV˾9$B7SSS"}+QP(t$$V}BRPc6edxd_>v"qY ~-*,}T*m6zzرmͭ47gn֯ˣI :hkz%IWv#FϒgV3r (_J7 y?XBurlR>z/իjMemTTV1>6׾d_XB0Ph',I2G߹P+A8!Ė#H,-Iٟ)#^foɏ,Md琝YY6vrcMW(+CT2e388J&˖əszv{r1<2@Z"|.)V+>%q-#'))ʤX # [sfΙ䳩A~k4k*?D"r_: Ÿ> P)o/ (žptvvr X˹n~X$k$ReiIk$'%-//ia[Zn D" PUYXld?@4ȱ 7$TUIvV1 GOLoE=DQYR l` ;},[T~V*m` hTm<4.7ߏ`  rJ3Y6fg}1*@ x/F ?4>R#G_]CR-i_i~?a9ye^V K0PTX@Qa~ٺ_}232ZKTbxdFGxoDPj8tN##4?V%0>$֯CCC_N~?0#=,<1wdph!lh5Z^lt<FC y99<F&60h qRCqCB>rh)n`"2 Z y#3zE'KU?-b];{ᄌvټi#)C-\.7Μ4]==ܺίBnftlM Bfbb/|ӏ5v 8].,8yCs43{{&;+ @&a4VYN'?75'&~;QVZ2g\w:b`˖&.^*d233V22ѱ1AURIwo;oʵ+%/'>2yy456 ('3#`K8D8J Ъ ZT;+ VP frZI|nFTVSOEE9?} ]aAȪcBP#zĽG6>W20cOZbm1cp{>b Kuzm!l NMˌ/Ռt phv 8NaFa||B.W]{{945qݳλsON'n?|~n߹3'Mifqu Dqu޽GWO9s@NEijk7(jm 7kRh _ѭy+ |>cS$ bB.._& NJٜ絿7z)|OK_]_~2Fõ7غp8 7+Wq\_~ҌF 64 RxQdmx< 3IФ1+oFq LÆz'lx`!\d`~?r` R" V 20cD[\_ڹAaAV_KBU8.^ </NK{G'9|NQ9JA~>O+ӊEI>!Q|~W7 ;:ywinntTj6D::ȯ{`p7>ʑ bN?|(^`]݁B j[4gFl~bG5[tILwt/+d"-HmM m71@ *t:ccj¡09OH$FktZy|lWe2lu$Bn ß\l!,&cʍFNJVJB.W,PJݳ|S0e󑑙AOo/++}X޵?+1::ӟz;_'OÇ8y S) ())axx)4h۶2<2Jaa.^b-s99ea-8B!N:MQQ.m[9w"l|FCVse?njڱ+̠K_ {IM5}k(N9+ڹQFFF),GAS&nbddV!j;l6d21bpmFFF';tz^d` Vt2::FMu#EV B}@c9ZmKFJYM'߫;ﲱa?o~+B.b`0ivr6/. .DjP*u>h4R3:;y5T*&H$̉jN OLP%III1))) 06>.zGR(**?faA>r#//q̏T*G=vz~Ooyui v XNy1~/sv;Ӗif 5D"zzV]r]gΝgtl|%q ̺ ::ݽ` }{Bd`A&h ǃF!=$h de==w-iF#iu‡TyyO}, Tڬ~A\ƌJii λ|ӫc#ɄM!n"]?{Oo<Z,7Y@2>;T>U;o㯿=091`ൟ=--K zAظi&l6;&Sǰtn{vD[cdt2==r].~S.**AQSQ^15B1YFR!Rw=  Q^^FMunA(vԴFUL[f8o/6Nqqn-Mܻž=WOQ¥Ixlٿp8Lnnz$-d`!ݴt,233frp8TQ% gp8,馵T*IIQbxΏ -sX,ѧwC&]*79+EQ/:;P(Llѣ8| %%Ⱥ\xaA%nU\.o, D׭N\n Vw ?{o}[]ܹW_W.= áw X y@@$'#BPf'r9:^^Cף翈Bw~wYRrv ! 9ƹN y ~_ _%>/]=8.|>>χ AbB\LXM]* )E g#Y!Qb1‘NӉja'x?~v'BʡZ 99bd`F(*d`$$$]޻'<}[_i%B2e i2"٥R)gϒ$^jD* I"wqj|t[ 'efLn7J`0ęsKuz Sf޻OFz:#|#\.z=gΞ'naC=cٵW#^XfE̱NRTT];D2J@0#ru6 20 @ J"hF___-6ml󑚒V pt:IFC!Rnɿ|>d2jˍ'IMMnwRcoA6xD˅`@&r(K7C4Cb^b?J`УNuH[Pۃ$o`Z3./ekJrL&VmNbYV_Z+-L:N`<-T@/S.ޖRuK &5j5 EB|Z1'|fO;El߾Ui9'OW>vo'H c㌍{n#pa׎m\zywp=4l{ p25el6ۅɡ .w`hxQ j/ ns9b10h+*5][mv_2().vFFǨN:^.imjcÆ:& sЃol<ΜsλfVM&N>"n_ ;aC=ij5h Me|b@;GA>?~0m1 DΦJ*J;33C~id'7o|mؾҒ9)8475rT N _b%bdtrR۶q5 =MpYp;ܽwձ\ HG*r~\LF"b@X> B`[&5(rǯ=YY26>6 mK3wqAT*Yt2.j χ_),HERƛoSW[CnN&7oro}V;VG\ǺrN=ظ {'N@Eywtٵ?= s (SVVEVcJK~[%>Z6^}{8qCOOOSQ\.㓓>jk?NnN^p||Aq[ÆzzۍD:wǞfGV{guz'pAn߾áq}mb5U>J(?/'={0 06>ſ_nrz V<̌M,Px )F0]:(>g%N>(f5KLj4(kM+P׋\J~~޲7ZomlI$ k6Osm*5-`(I0u5f~:jZinڌdFCYi ^^P?/n;YV}/p9x` -D8rrѶZd2xl<8`xxMtRS]V!c%>P*\.{*bfwPϔyܜl4&cf4 &SIˆRjP*z  :ďa@ winjD@66Dλdf<{y`pq=n 匎/o qC!6vSH 'Q_Mw:&9Jjjva/oJMz ^8H8i͆Ba^Fyy)HX,n*JҀa "HKߏi2!Lm9ɭ[p vSg7)p=20/os5pgE Ξ@ˁ}\v2>|VK~~>JQniYbU @xYdll܉4oذ \M'-ZS}T,!c9ٶZtN ?? <5dcÆhtV` TD [+87'BQf  1< cjǒG8AsFgFz8䔊u CVF㬣F4eHujEV zoZ&H d2t:nRO8Fѐn2qNIEx\dD1 t,|a 9dB1wi|?=͘|+k9gd1W"/7W|=L>aC$D':޻O@0)-4jkzԔ::b4Q)bj5& Ad-&z&`Z?bPBO&2$ײF㱉d  -XDRRVPըU*Tj5j B0?1OMys#O<2\?@aw8(PZv?m7o”F^^.w:: ì@s[~Lw}+a;Xslv2338~42`h4B̅èJ7 9o~N 6h 8¡%elc'(..d׎Dd22G?VX  k7hj܌N'$20j5~FK$sWʗ[lԀs{N='>N ^0H+vq`}e%##ZrϪ:hZTђ@O>#y])!{N V[A&/|WͿp`LFO*[m~gd+ پm+/}s+j3 sf^olQ$!.ސ *U*r6LZ~rs?aLMu^[Q\\Șd@ɩBTՔ,޽.Z-[H7pRTXngjjpssrYJAV|&&&)/+vRSSl63e6pݘLirvc'Q^Vg];/.8ڱ];dLrE\DA~> 2+t efx\ xޏFR+! 3IU@GG I'%ZIL> 󙞞+S ݻvPR\Ŷ>2or`߂Z_L1 v6h4ʗfcuC!QZem n66ԓn210(0ig9.|-G"dǓdηo$d`!܈)-,2l\.WjI*{$'㐱8 LF,cbbC!mBRcVQH:IahPk(\r-6yyyP*p8U]>%s2ɘW#>O,}_"[,WQ$J_f~_O$y' D)uijv2V8Z #_ߵ!$ę@E`&ôr-v|{V%)gR)= R  GIMIr='/%rHM}oK,r.\|_Lx0'ʵ$-q' &ʵTy3’Zz`GLFV7n֭Ȗ.O)%z(GF)YEO\ar9Wo'A1unLVn (^t eFX3.d%C8.#ў\.nwf W(dzBvvV! 6g I_3+3Ajj%JO9.4Z zKNvI YLO[\85͂q%tO0iTB1*,Z@TT@>  f&=yr- dBfϗT A1%JK!3#\#O0˜yzlna``{wcJK#8u55?O< 26rF+*ʩ\Cwwd`S\\ĮۉD"◁Jis z!&L=|>j5>VK$׾Wشi#nSZ^[).*pb0% @pRעR|*|sY>O- l(V"l6;):sh^ Zo_5޿jl( ^͛6؊@|(bƭl߶n 1>-׏\Pf-M 02:FjJ h A&bxd+ ؗ JYl%sƕ|s.Es/b0P_Kk-**ʰZm۶_{  PU6s2ZAD!lvSCbunt rŘS|B|dvOףO>Aqq!;wl[ SVZ[vn߆e 623Q*fe`h@p8LZ 3%0U+q\n9@)^2O-f4Ұyi Nj5ãcT*</Có㻻{ٹCoѴyji"#cHww萑xHMM!5uLIYYb9\Ni %&= ATtxz#TkAUA:bAI}<?@fFXᒑ!|{<F0//Fy/`)>Oq臡UA[Åز%$ܷЂͤp80gNb$M Trnp^1608^-#?zG d8d4:ȀPhi:jnxT"jeH2#`"?񘼄pvpp89w2P)LLNq~&SC#|lpi4Z zEzQ.i9_\Z{/9t:IOO^BzQHOFu P*|~MB!4 7ZoT*hܼ7ڨ ܽEAAxgA]@֎CƊXC|'DR)DPkR"hBZH20O-H͎V^~./Hx}ώm[چᠣ.#hZL&e#񱗏y/^}p 颱qã:sOuz:#n 7~MhԴ a4R(KlŋGp:HKKbpb166l`rjɩ9[M  ug'LmJ,J,2w˓xbdgenBV{B &B\.^M65lVK8dJҒbFp{B{G)-4qb{%n& QS]=Gs%{Is(Z)- ?|u $fgg1mK]Mx]N8+q^FV]`;8].> 9|k7ZH@p`l6bRObRK\{߿zob烴4Mk#[% M]mX vT_2`Ks#jܱyS5UX6RSS2R*x<ܽw;1e6ǃRIwo@yAφZbtzyg}]-o=F17Z6a*55cj*o=F}]O"6&j8;:+qތt‘qeg )(vχF&++D Yb$Lv paY|-pahx͛~/?} XȂ;w26>ǎc̹.v E!nϜdF\J(bfJ,vsv .OoK%5% \Iqjb0|RYN?$%/|d2:/j.\p$W}ٽtv%//^v܎N<=^G HvpYA4/ts/1؇\ft8y,~*6oG񱗎05efdtlvB΃Wssr())~㟰onԔTV+`ހ @)Ԕzz\GWw/~3hKh4rrqѱqzqF_~]o '19Qa6ҌF8""/MeAX^VKԴ>nd\ʾ{xKYY)2Jhm)m\.7;mr9WfFjitL4 NG "CqX kiOb6o5AWL9kUzcb\hR K.D[Tբ ɩ)jɞ;~*e8TWp\:[4j CJJ Si؇B?J aksEx|CD232#/9LZzh5Zd2xlQl{K3Fc*999ܜe$l$_$'㐱"8 $:brDɋZ)|j4#/Wyh9:}jiaK†D|-Y!}dIҤ_H*R=< 5!*//{h$$$$$\&CR`9mP SgV`)Ě' ä>iE,6rD"Alv;Pk%^KfLLNbm$Ű1c~~_G2>[?LLNŰXf[\l419)kXl+2]Y2݆ IDATzkTivGbgF#,1t˾v~Qxѹ+WVVܗ24<*H<[\Sg򕫜=wx; 02:Jo_?w9~8jc||/rxB +.7` Ȕy >%OnN'='u\riˌ8d gΞy'N?0H.\|6 LMW4s{9m,v]$" N[Tq@@ )4-ҭ0ox< ޼9G.w䜻λqBΜۍ!Wrv;Ofll\lgL|7!vc;p88N|>?33ָ ˍcv3eo#_DQ]o3rD+eK<<~ֶ[|oD1(*qa41SB&طw7J{(++/$)..\vB4-͈%r{GѬhvHI1P\T(#4Q\TDMM#ٽH4q̿n T*ڍ9\ hx/v]$">#8N)|fƗkN 9rI^M&&&9rX,% pifn޺MFz:PM/Y*99e`x.m7?˗ ?yr'Oahh7~OΤӸzɹ MWwOfph>]gVKM‘0pV =<L3 hwaχ<ʹe l6;V4#w{z/{#Gy~"267lX,-p8`q.A -$JI;vV;dl6))K\z#̬r*ɪb|"+2^~/x?-3ŕ4 i4,N#T,_^];e/;55M{&^#`H˘qC2]O|܊eGFQU®)FSٵ;@vۭ`8.: XMy3_ D!z\Y*s@ xWUML(࣪F $ww(K%di\UUr<^v.]׏(ɍ r9DQg?vuhkmڍX-&R)IӴx|[6~ xKScm|LNMƑ5_d`Z[vfID1>D4%STt0 X,r;L{tuv eeGhew](]d\z'#hޞ];K[,oR((T#Hm@{x+-e|IvѤ9w]׮f>;gm[3fn;wTU~ |~},UI$J<ldW1zzc{8F]c GknG=d2ʥ3ӱA !L؝. ?`ރ^`K\L:E8cǾl-wn9be}\paZ4=Gwҹ.)5x'q4fwIbsy{z8\^&Y勉 PE.FC/SUwgOۻH$Bmm X[{ho#r91Ltuttt5f0q#x?-1̳.fS10PųTp:ώP(dac0hcrj7oq`>z| T\.'~}{&8w+&&&}#V 7n"{pUN~b>4ѡo~'OãtvvdYv[/ Žl߶0O~h{]A4YAzwz0|[=ِ!R-!$ɀt|bF|>?UU >j^+3[5i4U뛛JmYZ`(DuUVӢ*>*Bv%Vxr:g4rpRBׯE'&J,NQ]Uj3S{Z:Mi sYn#(鼘 Po]?6 UU28DA1_֊餵Q1ML ٽUUswN'@[k+CCOpF#۶nadt KAKRAÇDM͌KuUfϏ l._rs媖 gϝgOEUUGFәHO8Co#\z_];aWԗ8{|E$_]W__AR4d>4_JcE׮9Atʐe-6ǹqVIs!`6O=FQUzFz0@}{2>{8c{^/C޽CzD+-f)tD.)?vuw8z-K POrR:S_[KGGC}]i'&1ԐdM?w\(/?zx=cD@N`26LE`2P8̱~-whsǩ]l(.,hǡ ձk*(?%~c&#b{TcgD"룣x<,O6݁]h6\NFcs.ʮUf۹\}ܽwtcp|>SIv>\H6sw~xգ)_ 9ͯN~,d ?m.fs͸T _zq(,35=nRkǎ_{~^zr 1`A8y8@wz #cG?/oUQw:9rH$;Y=w/kE 0>>A.,g_ ?{P@ HOO/ŽrnA]H${v02:MMM%= q:z Xg!AjH&. yv\F|,]DgձXK.20ٗQ]Fg.Hx"N&! zBR®B0\.N'uu(B6S'1ܥT1o9-\N'h7NU˗ds#sVv,fbׯ5--s(?.]nr;thxݻvq PX,Ƶ7'ھd5H &.L\ sksEQTVw_^o)b>惏>!4~:!/\h4F4k|'a<O>i/~~Wq-~zg/gg_! ~t$^d*E"]K$%L&zFcLLL)QOQWf*H[{PUiwY:x._ZayQCc0VlTy(cwlk18%̹EtbXJv[V\.'Niш ו-`B&y\ fl6z&^L&?۷غe [l>tV>O:b^W}YB~/Fq,| kܽ}l&M;\3lz}͍gO>#FUU(Xt*I#DQhۗ/ HĢ'ϝk_cwh}>#J%x8ԣ1[۵EwfCn\ gҽGcS xT2!Jƹ97o#+ز:k ]:p9\rh4+lE> hokqɏ~P:?95E8`pQ/g. =I' "#G"[K(&r`0X{VD˗/{rр^X*O|ji0JefʑÇP8L윏dY®o{,hWÇinneR~Zv7Pq<ٺu ãAtC#ݳCCab8W J)Q~,'S ìF 3<-lg(#ᩮk+lt2ZKgʞ$#.~-HCK;qU%pr>z}>t h?=IM}# Ѩme֝ȤSF¼ TFmrGPd\N pqMs7t6x1w8rಮWN55RE~>?ٽh4ʣ?\.G2X+ܿ|D[{+*?SVO Qpdt3_v l6x>l6L%y1nKWjAJ8.dtb',KWq|eh@XRm6N1 !YlI@<38#850\^3b2nSc*GUUl&ΪxUsnݾ0NfY@$~q] &Y,H2d4!I+OYJq:6O˪fy^~&J{[o6@YY%fLrw3gKW d `6%cZKKx~¹*$tRn6E$__݅`rOW0ylkM+jVPh,Ǯ8F[mj͖f1\ט  e$Sy6d:m* .9$UUDt*yEpxi4ge7jzcm3Y$"= ye 39FܣKҗgvP\U eV0 vq`dJ%Si *󳏋*J( F˜.(^7T+Q`KIʯJYQB-TJ쭪,[|y+ii(o*_qq<{,aɔ9vBCmvWE*)keY|H">^i>tZ˵d2Y=~LrVdx1ER\zmY޷k3-\JK{#Y ܍(lw * VHUB SN s*쯞jI܏*d U9.o$jK5f2 ˞1k 5`,PkPF[ڬV戼h. X [%h a+;_^_-ۙ}HNڿXMydXnr#0,8{+“YCY~ [o 2?$H3xe{)YRUΜ61<G9tp?cۻO?Wmc#l6t ~yCX::klBUUS=|tw*wšw)lлՎ/xgΗ=R,?DT~\l㋾/;>NLZ|" Ŵ>i_W_fsD^YRvf߿Ƴ|)87 K7WaWGIBTM܈)y2u4 f@^EUUW{LNNef|>?;wp}|>?e`޾~í=~nܼ$G|~,[n7OLBa.|u1v`TCGe@4b0$Ɩ\ř&R]رm^oAEbfE6#8?sQ?c,V fs).X ˅(AZnaXtPb!N Չ(XlVjkk1M&nX|TUE-a} {w"LZZ#}׎۵Tvv+/TvwFÇ99O~ xJHsK0* ލ`d@8,zmXFӍy2 `KQcScZ{9`{[L7 ݻwEQ|>?۶mexdk\oΞHkV,55UϨ7ϟ@@Mώ +l|iB@F5q1r)455hnj$L24+mttڊn/,F-"1Ltn 6obzڋdkZ[ À@U6 uuui$=Wᑒ-DƆz-O{Y5$ A:;JvM3hiÎ)Y\LLLR__G$-UA+dKU~rlau g;^fYh )J#VkQyd.תtHga. FcdspXlAUm)$C=k.]M] {XVtZW%;7+^«!s-Ts-ؖJ9{<`4:~Us1~;t7tuv%Y'&ijlh4242ʱ9{.^]~/Ѩ;:h |Ttfٴh4Zkextis` ;tTgOoãځ vbّ15|b\GW/t~Uwzq:8Rv;E @$$D}}ݜYDAf2V5GfH ٲ@!2iG"ix< GbQR4(dNg_-Ïhv/fdsY˅  ʲ(i,L8㙛X\e55s TUEQA@, R`pu¤ڏ,wpinjes|^Vi|WUifhm@ @MM͢v= +Dc ( XVZysQbdYjkj$I::\Xp*d4~Ēh/Ӳ;ʳwy_NdWff|tpPyqN)#B-;{fX(J:'jP.ciw~#?9e`-vn%ŋQ[`$&x9e=zĹ/n~SȲL(" a2|v@I$BaE{בL&ٴiӢWUQfT*I&UH8lƾHڸp"`l' >I%NxCU@$0P7c_&_sA״084xNx[S475 Z-ڲPӏ34'X}t,CҺj٘˱k9Z[ kfO.SD)ƃ2m<[*eQx=hE/z\q|W=+@enF:&!< w=2 (i3L&dE&.W(Kˬ.ȕr4f C K2H=EZ̲ޱ>x Ry|i4`.⏤P "Qcc,RʠP_ϣGoUڇxl6ǃRB!.]­۷$iާl6K$ $b1RKORYLgmaF鼙/Fz#YZj sa"?ɏfebb+۷?{]:x|m[736J,vS|RW?=wz #޽pttn$H$رc;w[7oٳgp:ݻud\.(,/j۱Zd2f Qa|Ȳ Ϋ㨨 r>O4nL&0.)$c51J KI;wDUf3;VCψsi6CUthZy08ȦM]\y o~M߼E>gdtM]] mOXr2ZV~LV T Uۻ* ~S+f+l֓ jA@ XMy2<*Ef%IZ ̉ǘl6ӽcxd2mIΞ;dش|kk ;dӦ^;jE;c] F?~aes$I̓AGعsrJ^b,9k0J |H4"2>&jk^ST2h01$r<j6iznbQ\p8L64jx"Iv&&r0fjh~NJuupثӚ$reA`&RW:r:G޴_]q)[׏/Kbٹ PW_O]fʝ8+vI}Z%T("0h^xiy1n%X<(*g{ â:fɔk,0R2B4V_ZE枏&Ȩ˶ ך!B8 MVF%W؎[;\W|I0 jvD"` T4Y\XfR$D‘-V$F*GDiIHJV5g&bsޗVbjsYcQ6;.xB(ͥyjW2&^rzy=U"c Y.xj>{ |6)W@(TU* mWD&XB&_ IDATLʐY(aB.$0)nh o]f@:X~" 0`d C7IdoYJoQ2b4JHFcoшhh2^&$:%bp Fs=z\ab!#Yn굕fXHUm::Gغcϝ;7htxkz EF*D"Y [\"Q`490!爰*zZ!}\lBV)XpYmїa,)RxNe4>:3Z.ՈVIBM`Kq՚cdcx0~{6vP?b5*IKg/kp`p8 dY&ˑsr9<\l.C.#ϑe$ hD4h i21^Cγ#wlw|M`nE0w~h{‘`v-ˑHDz\NOOoٳ_ .TͬQ$Ci_Vq[x8ηv-%To|gUjڧ%Ϋ&ͩ8T#RQT"p;`s>@"k3i7s{{E'U Pj`foL,g臉eQM6QԘ.r;4RW]m6 Q\9nŲ6C5e-;!m.e(By;_qqt :\xQ$ # >p8 gt:[z`0>Tװ!jNN`Z$7~9nTk||OjZ~Lϔ-_lge?ʙzM" EFHn_VÌs/r`sq9| i*?W\_- 1o,~E oB$`QyϭbV*IxdG+d.+hfKd6[0/ g;m22*pˊ`sW'ãĢq K0vPQL&Ӓ䁖$I5jAS-sSHdDBrLf2>?n _Rfcf}\Ⱥ]lŇ̨͠/zuĤyHLnz{Y ]9rQ}gFFF^8Yt)}}qroxgT>Gv]M$,?$I$FBB$N- :K4m8PXӕ쏳RKg9r'NWK%7H(7 +|TmFSl߼L*,L(DyY7CۂV6yn/yVI_RMM"4ęHZp{tlp'nZe00LS딻6@P}3tފ*F|qDQdjJbrRaQpR4.s2'ӗO9rFN<Ʒ9RZTn:P"R ()1 BŸIFKFoF::NwC\'&Gw,XWW77$g*nmom\z-RT7k+4~MQAOCޅ;lfpgg9=s+O`Qq)r:At*TTV191J* p{}=:s!"5  $KIDLYYe|\ynJ& N d2$qƳ|-?0@8fE rLCc4o碙GG(J/u1pj-_ih'O歭+P_@XM_10`b%|`*%%TR=Wze|bB/P\RL +8(syO<`llMM7]hkK ͭ,Y!ԓέ[6,($TOt[fz?qL^Cg^'㥶uOﶉ(K*0,f". ϗU'VєLY >O}}MoƝ)X8`-**^l۔WT4o/_&ۀ/+iiiaΝ 4xpȢEH$tvv枴uB3sWh nܸD"$IP;?MK|2##p~I(p'Nr ص JKb,}7Z;ŋp\줴= %|z>/{@e})fClݲSsELLN"I;?os43q8D<>ص[?8}0? ZD"?qkң/]Z\.;زy#;>Emwn7YM6:rCK_`fv#leR L2ld`0F( IbvZT vo:(~Vj먭_7ݴnXSO=EYY?H8L__Z I8Wuu~,:J^'L@MM haxMo&D2 WU.\8ַ&ߤNO~j·",#[gU޶{`Ue%WdYfYY(F1xE:A PTTD,3|#v1kjj_Fm.z]󡩭t5sM6044ӭDٰ~:NSWW$WzYѾ7tp$B*ԟ߉Sky㭷iind|ms7G= ĩjhk[Ӹ!{rg6' سw[ܡ[I-"~ }Zήn?ȝ[0SL#q8qtws4EE.).V\Ot:DBhr͟?no7Rf3]$I:#;HNf1r94-3LެZn\]wn ~O20d\}TDayN'd{O'&''9z( ,̙3&S~d2Ą9"LEFFFx;$_~m۶q[$oݻ7t+={{?c-9r t*;wx,mЙ?CrgrhoogѢExb}=***]g<$w+ēi+äe/"D_yZ!ȨR-ϿȆ9rlLeFګ= D{|K9ABCCGE@J")QLR4X.˝POUujZɠWhg GDmYt1O>G2fU+Y"h^]v ݻ=7B_κ۸ښr}hڸ;UPn}kV+֖f~׿S5:lQ΍|/9MMC!ZkG,vQxzDOעH̝L*̤Hژ/w?Qa$T-F3wff{C9},=h_b* ,TFGG{p355ek> B9Y6I%-gw 19tGA%wS̵687EILmH$w]AɘXۿjw}d:ɩ_m+?$py{TC299I4Iw p%EhWၻ7j޷Yd)D|Qs+O ^K,_{?>}G +14y I"x j*9YxZ% Xɹ NΪh*FIf.?}kh{3:˸ ::N^ 33'fj%Ft:$̤%cN$9-JL7 #id']7(1$U$^Iۂ֙td@Gi$IyWL]pq⇗_G!̛)`=9U VȲwM:w&j.ǦlRL3G>"҆tށqJjy_^D._$Eddd^J^~M.8@:pT2%##v2p''A:-^s9U-\ٳh?Oihh -:HZ2qk$ʿdDҀIC?ft"2g~^z:-(( [4oĉS픖 顼t:MyY=Wzi8+L+eXQ3 m@׬i9!7|CioY2:u._`bV 2k%Gt9 g֮~'411ހ!ed\c ~W#(BRȲL<W2dDBM1F fH"Iy-#ꖔ[id?^~e~uXj~D>CdYpʐ~mn%b:'$gg$уnƘ,D"w~|feEQpdgPWǕ^^+EIIǚ+h59fؙLkוP4<H }Zq=U:Lz-a' Nt::Q-JZTJ zc$G=/@r;Z֓Knuδ&og:Nk.bMMetb42G@:)Rԙ#k Rcnv9^dYСw߽R)v{I]K"` $q:_~_gG{xjp1KځA\`v%? տ\7(WFޕ_5;ֶs 8*83}#x?N弐Sddr*ZĠ ZdNvb!m8Nm7q=,oogʕ8yzeȮWdYV1 & T9HBv-/`h4Fj6ht}a+g jB2jz\;rc&ix>2SƾU@hdimp.PH$D"벁A)|٥j^매R%m0C% 1`0OcCWY.V]_R=7# "W^y> sHeef.x1Z盾DϜ=DzK+:Z>?`0D(3g!K/{ ~N(-4_z B,[3gQZRBcwdk{|$}r?oO>'NbY I+;H$ NZdZ.@t:qf=Xz{sߧrauZqJI8pڏ77V>F*~T fLǶFYthA+]-Dž[9JJJJ pً祵u### 1'&^E(yR90 1 4tjW͠QP1jf~I܌LPcuvz0@Fp(*MMzNPs_)y IDATgy:g{M>/`מ}47 p׭di 8N lc3>s/}= JIQwEʈ',Yw;NA-][ض@hqkV8yeKyٻAx'vN#ǎ@II1{*B4*h{ux<NvF|P*Ȳ?}xgPqݳ)"p%%x^JKK좢WSd6#ns1&CDZ._biAK,mNHY۽.m݂(ͨFhhh@DA3{ae_IT3L ٩*f511p6_k^U=[geeC5DRiuT] 1{%yzH,6^+|r2b>@cc#gΞe;~~ORZZ:я}^/7cv|ZZsbNuP(ą !Xj%zƆz:ٰn-ǝ[b;ͷw-2{@VmX+ZX*tug*hsMR(5t{{XѾ\ңTfuC!]6G8D~4?-}j $wl?~|!߶y*ovT:mL&DQ&&&IRP>.@Rg&]fiLr?+}#GqQ6m\?c=Ĥ 09&qeevIJG$7+7 gU1CYa Mu\8ƙ <äܵ''H{lQ"7uI8c/g#xm:08 M*>x<Ǖ_`-5"իWs R)ZrN(c;NRdBQ3 0ViNkc/OG!yiFE΍h domlZ@/)m޵@6i9O+H& I.]&/b#M_zK,_j-*DH&{|jj|$ *+˳kN#cTT12J<tHd7b=u*7zƹry޶;?i:៾Ǘy\.3gNW&9EEIY@24ȸLD. 4fmNNLwO7o:؉o;::F 6 `>1v_<ˁyw 6/K؍C١0J'7.l/%emaf ^3agY=% 둠}Z'JFI UE Zǣ֪qM4ha0@; fmYAFrцgdi4&*d0~$dIF%Ғđ#B|{7*4 (x#fAh 4D) QI$Kl8 .DJ#9!J T턀Gfo ci.F Lb^3qv;y L`ey@fZiX,ƥK2c*'C(I׵}\t1&W]`*4\sI@ZdR;xDBq )BFlWrCUZClշéF*tX,VK3'sKvk/DuU5? @ V@YJfJK@YDgN$=.7B!y7?RW_:c6ٷ%%%ܻƗ.]MGĉSkl`6SÇ26:WBz=TT[_qσ8Ȱb@2D^̿Ǻ?Q yao$p@ZԘf*;+#8=g5ڙu~ЬӸ9Jq+>tz<jjk).*̙3׋妼12WCuu5:$,!:#UdAFs StYhqBl| ½M: a:>̹sio_Ƒ#Ǩ@M9"ׯzr)F$pd~AΪwO|7঴f+˩Gj}z,kmlE>ו8; y%2slp6ݫvl UksZu# KYKꥶ6&&&cŊv::NvYnTJ"ёYRYY.E\d$NT,uMo%=7⛹j̟3zNnNsi\PA@}Aʙ3m\m7R/ԝUpk%%%BLS'I88 *KCYI2@J N[hZ (---K$<}ftSc @$GFQ"]+côlk xق<W A;s2'sb/}},lkc||`(D(OZYe*gZ%O2I2 9'tdrz&͖%W% J Kew߽={b~&'$qf9x0MMXrgΞc =wa֬^E*S/[!I0* ߙ#9yĆud9͇ᅴ` q@_Y@nzSݼr>L)ѲGQ~v]]]z I.AO-k|ȤRI)++SRϤӔβ 1hAF+$33uI\&`XaR;]DOƍi_2m_ üsI]:?cOV1 q]>B @ ҫ|h,!dBMr2 }C!/[ʕ>. ##0:$VBGFgW7 SUZ[w#Ee}^WS7(X<>vo6IRtvuP(u52)~@@ OX DIlsyl29r݈ôN9|1eU}}o񙧟C>ڽ@ wl 4&@&LO$F'Mi^ɤ}Ԝ:72`҅!=//C$IJ/Wf|y94r3}Y?09Q4Ajk m(,IN- dYS/ 55aCctAVs{[7ȳeE cU{E@J[0(b`ʈjQJYMD(s̛HwOO^CpJ/'N@Ɔ~s6GF0>`$!2+VA.YFJ<"景&JmncZq]vϧ֩bz ;;iin\pj5CR}G#NK #)UR$XTZTJiI'K̷-¡ :N311S' L%SzŴD&vzW eDmV9@>}" ML lX+(fHaO_*3f֣Z5mM6,_[ؠB[.k Nk 8Y  i"&2ʇVmG$$I"I 0S!SD=466p9شa:>>qzNꠡ zYѾ~sRP8L:-ĩ|whmQķ~s7{^mӸ*}Z[cY:9x[lZljSd=Hii-nyOGGhO<(֯lvݴ^G7XxgZ>_=nsi`>qD"cwݹOOJtmS C+i`~gΜ њ)賂o6ТKTӲ}^6Ξ!eog53h,D$:Kuf>VZ̬*shUͫg9W_ uM6wO[{(ccc (~HH$q=TZe/~K_avd(DH$˜ZT'm`,@irq:FI߱cXf@冽 gf*6wKׅvsZ}F.Y?; 6 h,[{guh&^3g fv5soW SHT8's2' P__ /{haoyOzyiWnvl5y{27! SZZ+ط+W|Yɤڣ&)LVΑht*mJee91bqFG' d,R1,]!Nc {DcuЖhۙw eD0FnQiY7Qjh97Ìe,1Y?4\vlNm@dT0hd,͢Q$YM|/d2E"D+y3 s@n #:gH.zlc3SC3|k747׳kr+J!%lH4A}k_ec \>};crs"`*(P@YI& ܌eb|2P0%%(N>O&I&S8r]PTTL,6E2~Z[Z|F&!@T Fc:0I:2Rά'/┼`O#LA28IZiL| r)Sz<7ɶLYbаy˅(,9L/`.&PWiFNէ 4P:SDD*me_f#5ZJm SSSpdR)eГ-~{rȭS!ZlT' p%.]4k䁇!舘AD;3wNf.NIQڶg7$S)m`N.JAz<|^^/>ϋ8v(R[/?7[`%S磫TqJKK) 3gr28*jjp$ADQd||D2IJVWWu$ղ`4;Ib͔ |x_^4ZY&v`m:ul=3g1~@FL50_ Ժpk ,̌ 7(8#ƤQY>*2ƹ_kL[Әdh}sÁG$&'J (R^^АR:011A1s2'<|{?@tTP\T(Gcuq_T*,IHDK5T9\s*[r t._8*RDSe%O;'گ Nen)eeeb1N$SIQ$zI%^tPZZ~A$Ip8$%%%$I>?tJRÑe LCG* P\lFӃ$~${Y{vɪ IDAT%Ö3+gd-}`.s=\M`/i:c.##]ׄl,Q<#Km5ө<>9  83bÏT7't ĠKVm'H CHR [Ǔ>).\,+I=7552ttusdNnlٲ|~=v@+ҒDZ%Q2&PXK\3U8PD2B8=qC3: 㨆q /WxÖgJ@䍍 yYm|Fݠ$ô74"S)J{N`.^NkA=x6=c{|} 3~-') o&F[yV/ hT߬k< Alm;?yzCʓ02 Dfj;CC =ƣtvv 7 *++hTIJgٷPukpYw>J^dmw*^+nj>}?hGEE~Xj588ĉS|^N'}}}םTWUA=wlyWWU3ʇ?BDY:9dtl Qd`* ݃i^wέ,nE8)G7Pt"Y,ji5JX& F2ya /jkA /DC}=˗3ˍ$-7B~-hx"X^xwӼ@J,tU y:te_3koa؂7ea~*@|@KשLX`dVsWB@O4l-cE`NzsQf(Qa.tn")˙7~"/… 5d k'e=wSinj۾ZλЯi<#p77m`ppkon.^Lgg7}JypCdttL{ELkkkX- ?`0=ž}~]s]߃K$ s}پJdtl K0}=x0M޳y\I$ȱ㔗v;r_PR, 6 K~iT4 _PȔbb;O# X*ogʛ%G /5k˲K8~6n׾zB.Sq8DA MF`;{:fafY]6˙9ehHa~?T˞,엚=X (4Hfkプk.8ml1۰a%FvNon5 cCK:h!(EzIET*WE ؏DE$ m9sYڝrDڂ~y ]5 6=г1Sg7>!7 6PV^а~mjj2#H\tYs[у4g+wY6은y;FD*̦bICdlͶXVv~5g\: @uu+t-:FMlU7Q__Oii %%%ȹ*@ii)=QUUvSPZZ,K_-,YχLOLPQ^@e (ܕ\Qfyy3x<k`r8--yMu5EE~]_*L5-#=(@FeO,MMhj;U+-ȲT ߗOm#^~K|? ~eKzygx_~ϋ V1ߦyDo*$QL& "$ @Ott罟_Yض,}cهZ_\pj( *+d +SAg ȰmB:fJPUUϗ ) tek+uit|!ӷJUnߔwv^\\rfic\άݾ_>ngz2 𙧟K(@ۂ1b8XX,F$' Q(}T4RK P-mK~ D?~OK*~lnc*G eKe @LmiC#NcI>6a&Q[X~PX@Y?O? Ku8 J|zjxOlEprRKrrOZ<G$OWra3{?͛7իWsYx '# ^9A#h^6h\^mrofI_շG$2eЯI*"#IX,9 ~~ϋ/wsq{d`A ȘL%Ғ|&JN$+:5]T*wpJ>^ *3R5̩hn[LLNf:lr11<f@ @((--g۷/rԔ>Iss3.{o#"k֬! S;כ_M*X%AFc<78ugx饗y|2{~7=NUmPY2l= Ԁ-{hȕEl!qjN0O?$1{bfP)JDx,N("a0,̥XsLr2ş=wm B߷O$=44+¶6ڗ/cxx1+I466M]m H_e̥d: ,37&i A y@ 8N@Y/}or=/pBlʾ},dhzqJyn< ɀ+*¼GKWt=Ckٳo|\ӧO3NA`ppG@;~xeAgFv. i[3Kt tphPid,fOQG4C(ʙ,}Y!pt:w (dg2UI^Go[LVu#VZo.oJKy7H&ݻJX|֬YM$fڵ?- $$CG:-L%I&J^pHg. ) ,EQT]T43,c8z4֭s=v{tvvvYܵkjYƽfe>`(3p8$I>^lc' D3{Q0=ЀXٰzFh0ܠM,-g٬fG{֭@ r-sU^Y BdB7}; 'gY쟉5ikkcΝȲ_}۔WTqF:Exӟ1_|D@fJ5^32tVը|Uչ'4`@$ ISZ˲-j^]:kkIVX%{Eɢ0sɡg:wWPr~U淺?J(ރy-Iл6 UCk!|ϭVVB _UTQci/{mgryo}rϽ@edIvId($g1c>Bl6 /BXqբjxAh)ȲqϜ=_-61By\6LJx?,6--  tw155ECC=+8u k\s쏚@u4 HcNH0^FS+H ȂuiH+HJrX*!"@$qqUBa}EH__$cTUA jL6C,4?{,,D&AuΝ;03e+JYrhmӦMÒ%KGEo1==C5mNKNq"!ɬyt!E E~^rOBzXJ[l;Ph48 13$u4M%IrUXE,sI,_SNs=23nN*93^:&/T/2Y%V8`0h:jXhr^T_>} gfã^$Jq7-^ܺey,Pf"bxݵvXRJdLt?GW<8K]'E"Y\RAE [( NI*4On:N<ӧYCwOveYnc{tN9u֭cϞ=tY\ r:u;zWx?CJZ Dn[zoݤБVy (JtA{}cry^yҩ'x؎62rn&^8oC$뜁 ۙCw`w[6Q__G&F4$Ibpp fdPQd;$pa gO`$&B4~owv)t~5M& Eg|r6CH2opMLNdc^S9:|6ItD%NޡJmP",Ñiq'}_r]sso:$ : 1s͵h KL="q:$.Q`0xTʾ>~?G._H$ sm۩YG~j]_H/dI$f(  ¬X:I(2lL M7̳󴶴!==|rawGY^\.@̉N<׿5s x(Y'HJHP¼)p.5 c;cUWH@jJvlW_}Ix{C?7|3[nG$8t0u0088h$xP(D4edd[0QE#܊pҶ>P jyV"N׿]k_ԧ7%{,={+$s^锛Ҽ!+$(ؚh;$!H,/`UU[00Oaȏc "W-:hHtDQ;m76ɜN^|%98X^#u.4.st!GuBDQwNi,X9κ/Ӻ]/?o !ȪP1NHv/(s!sS`ZgYL4^f_o r4sDQnD6߿zQe˖ҿzw:lYӲU͈W(fHLOK08|4$[ri͕`& v?ݼl&vY43Oj0 D8(pq9/#m Vhjk{$+' KZC]!־İ~WĮ'I2=_]]+DKWʫSԛ֗y ϭ+Nr*pȊgl=(lttti7g?um{ *UÚbI\^W*5\*s-PT$<^Cq:pDBԠiTڶ)m% \$W]%;u_{P8i%>?.>Y;Oi(t$R:Eba81^y m{zHξ&r4k_dg9qȨ:!=[Q#Oid)g8gQ$6"KZNw  AҥKַM$!k{}ma_xNWڷ˻q{IdY"bpThB>/ %@0UV `0H hD|衇O~or=v5G~tbw_W_{ϥ9!S ;KA,pP006G$)Sd ,!iyHV'YDD LJ0=<Ӈ0>I09zI;}t #7֋(#V˵HC&uǚ5k8|ikk̙3EEqkfHg=?sQzg\2u#-d8  8-h{ XEtv022SS(L,T*E*_3ŢJ6낡_?-446;ȤRu+zft&:Ej05oRHB# cWT^?5(h 1T־^Ɇ$LR U6Ȝ+f$gΨ4 CNHU$u/wcZw!2 P__,$S55ܸe}[[3y6yJ'yͽk_3fn}>|Gryyϻgʾ73_]dӧϰ|2q))իVqM7z IDAT¢'.*:R KMr[nÉz^"ųsZuC:$ۿ"`Z`2Ix|&*=]d2 #cFjH(ˆ1mD"+KTզNXtqygx $)TjS$s"9s"} ZUQ Ac\1Bb j`:mg͎BH"1Ȯ`UTQ[>7[oL6CwlxULNM3v?vo;yoUUE-뢊kz{bd(E A`.@M(]hdBHѾ6HkB!#m8lh 5;v3$Gc-7 ~7Y~E^X oN%F+͍X 266T:\y1̊:pεCTᗨ9oJןJ~qyɡ*G)%>m%V|Ri=W["nbw<I$n0'l8'>1G>0GcٲlߺZ#jƆkd ;wAMM -|K_⓿}fH61,rPZE#^E\UyKao/2^$YI",36:ƒKllן {9r)Cvv&Ƹ[Y .22:JwW?03 ;w~Ր?/P__o={]펟8+uW["7xi.cCP݉%{2_@FRB'̇53.UFWF=PU Q "xr9gTK")7?tV5ai]r{ɟ3 ɗhg|'fmʟ w77 Qjbg>k[=#HZ5MeffU՘"7^겠$tו YQ9J Ip Os1jՑ==r?#3֯[ˮ'$w޹ZZZerrqԮI@@aff H?lڼB9^% {&d~' Ay٧yV QWWKgg|G02<̗%]U-ras'lߺٟȹsD"Xˎrm~Q "?X4NL:& wjIfttC xרˀNsSi;w~7}Nb!WZSC@ ٞ"WqcYs$|"ɳ.9*iGJeu>lɚ-WX\*x³E lH$Hr-59`o#z;ڲ&)!) %'nzoOF0f+Ew}+d2W/+VWkYt W" sϻIOW'GYgN%:tQҙ {_}v>۟ZE:lT*M:&N(ZWtB#LDF^z9>݅$$SIΝ;$/|立w{^K(b-\_[Y/lOyV uk?ȩӧٲiwݱ'zKzxm^:;:"J2|jk0HΤ7ȲDM|/,NM9r*3Z[$D4adasKBaRl78ߺnw(GO{k|q/ G[Hq\;֒Yv8^~r=ˇS$~\9`<;)Q'Ȅ&xr!-yϪabbҼY>`(A{lPEUF|pqvIo N:Ïaukׯ_*/ Tp$Lorn PU[✖}x*&\P ŒH&bp@IVɑcm|8wzn;L0__PW[ˮOjJz{W_> o+/"P ly^wr$IbʕU+hoaV,_N0`ddU+W2m-w)d1bck$Nڔ@8* $ge^sI,HdKI[}-=,QUsT0&X [3i:QBgSKb{5b!dIz>{6hYwaİXitC3E^s1.ZN FxN]a7422N>_$LO'|N*J`,Ϟ)#$C.M_aE/+t"fRaeȫ8> @vv(4RU2[Z{72Eϧ.!#tD%N,Ku^< ־^sm_(a߾|K_46pM?Wry?@06ϳ|r44DT:z$xb1xp(l eɜ [FM(rSv_ K>#~n;? E]mcO'HqW(I$eL&a`v'=?H<^CgbY83H1LW*L)DGRjJ3#URcEL Ӻ]/?o}>6ȜO(d(kL UK`0HsS;oo~oȇ F&6=hu%>XU5j5k6|bQeM{ϻ$@@!Z-nܹs|T Pذht0`R(hX6]V)8ƐلzcBk%]9Zm$O]Jw9\*bO}lDp<{/'?`gT9v~6ْ>RW,d;Cܺzv}%aֻRVM00}!yXDAkDޖl;"ǐ$~i*7@:Dws?_{p8B63'm_YtRwe8  ڭL*"yXsgM.$q:1XjnI/IP?v_QcdmBG\ٔNO ndf\FZQdW~&vM<'{nF~ӿCGG{Yr}˼+WXdRiKI/B:-$ [heKUmHy5Ar'}Cn.Eg7/ܥ*e: YdZ3 _ s$a$в# &+b?҉p!\ C]l5j>4,ɤF"l;M2iB@ D2$wreyy*L4oKYhln-; 93yegTEJ2ML̔D^硁"3xVgф;kA #pL  ެy(oemBZKf&L ! J1:e\X42ܒ%*K1'"p4w%W['^HF"#\ҹկ8g(`2w]v_ X$\Hli-ӑ*w=jaajűKû#ls!nrzW+^%aSVG@ \˔Q444 yX6o:*4V\{'FʌF"T´'ǴUj; #C<䯊*:lKzdIP⑐ uĔ2akAU.axi_Nupش+ aqʐ Z1>`%==&`y.WQH(!%V$IF=;n mK]8 ':ɥQ(2 UQEUTqh$/Fhnn3E"M%D'y$wn\I_ު2jW*vXbD]R]JzY$\٥tӤӆoGAu( Y|-Iu~K|Pd!HL&ȲL(df&A&%v~AZUUWe+BO(i^ϸT׍y(B.X,"˒k[d4wCvNB>禕s%c|b榆zW>'ބ*``X4莏Ԍ,KLLLN@@ab/U$PUV(h!V1::Fmm LiKt9jP4jƺ!ls1Agyʣ!bn>JsW*,*ԕĹr2 006}`= d̊P(Dcc,HLU3TQEU\EpIЊ"TP(DXdfz@ @KK3B@GGB |6&''  ܌(d2&''iiifll Mimiyd4dk ӏY>q:^ _۶"[^uˁH6˒5lu%TÕ`99 3Xu4PwȠI}FP(H$b||JA-/9?$Ir]_UTQEU .(IEd*E}]cqbdLgqeY& vfBni]Q}}#475/hojbddX,f{hYDDTL&.WIt,qd$޿FдS=OHP68s]ReU1w,ŸʛT _+'2]~ق,44-;ARn 33&Z4@ 1fdPQd"' Z;ٻw[6obH{n7<2= $۰v&'Y9wkB!V,_`먢_Gʵiok# ICC===twį&vmtt "t¡466c\jI3_p֋Nu4< 嚩K^zE/^:*** uv μD7J[V+=׺p( LNN299Ђ9y4k\{1H088T}__/\ssJ_xe֮1\?xDb]`utt׻jqW6M۶ock8t(cuVmL2{#Θm\~-k__/'OZ=SٻB4=(s/فs;~=^b *uł].BE!o|yd9TU+'d3F6fBƧ-7_o)D!ɠ7E]/<@}_hl[Hryp8"!mcc#"2,*tRz{Wbr6nZUU+\Z3Z8w|CM,F<#J*  N% sYGF0v3:}q$C>HP,}MNNq&'xŗ妛Xl)| EQ8r{3g88HG{;"ɧY|…w@{[66fXzrLNN1<:JiךzW 9y4095EOw5 #WQEs 355mg)Ţ44Թ-5m@{j$?l'`-QVԼVTCD lcV1o̝SegUA[aq],7Ѻe+6FFgbbE [g=},13h4ɓ'&Lj8*#+ +5{~gU9;::U33tvvp ׳a:BwQ1 ݕ݄پLu==H[xJ($ 5Jaõ Ǟ.z(B2NXmۍ@ݺ ]DJCVQE!"CL8vt%^gykreJ$uy\ɐd1u ["m٥/eF@sin`XYV2Ⱦyޘ_Ÿba hk1wRE&P(lb hnnBe&9uʐJRŢIR188D{{ǜ|Ro}8&lF0Z8i o%F]G0QwKD]xIO]^S Y(%mcZV>zupUQł׆ZPu+nzO$Ǐx2/HM"8h皥N3hrHd*\w+brYyTNIܻ*umE *yDN IDATR%;LB@aIFHш9@QLʌe"FlTͪ=yZ97³0έIg|Cyf2@BMMM477L**$W̯~@5DH$yXl#GXG[[O>4xUV%IΤ,,SSw$gi, ?%e]l-ꉪaQQ-U.NcTBNLkl*̹ :Ԕ!S]u9w8\%fbȒqK%^8`v<'&i.2V؄R)\!^p*tL?BBIٶ e~vµm'/0==Ȉyf,V0+J^X. *o`EI$ٺyh'1pp;D`a.5S' (ϝ2RelRQѡDрrw$9p=B62%ܪXV7ϋN&ɒdM_VZZZk VFYur]us4(U*EH8&Lf񘘘LLL)\I#D_/XL&SJɓ9ycc4ڙ@J$(8qp_b7bRص%'O`ph6E]Y| wOKK XX4J&Bq{W,73BQStwu1 7y(B% fPbH$fG5IL['[g),׭2eJ[cxJJy1 l>X|Yu𼥀I𝟙o~'aYvuϖon0Jq"ȫƞ+Aْ4TT׷\> PL qp{[B,[fb+HGg|}afjj\hikkS@`0@wW'xt:[=|uvvxvu֬/ .-RyK-j+Iq}^B*C~_${uζ:LVy_SSC@ o W>@XX4"lN0#7s;eyO=+1z떊U\%?`Ft SΉ.3AlL(9Jտ-E.Yp8Bkk"399[EUTQŕECkk47(5MU%:vJ7[kLc?hiut "1sY)  %$لZgQJ.TӞN/5$NYes.u- 3smm E(J Ev |!@'<[AH-gK+T9*Xo!"5dǦM$8@F׍fJwFYo {4߷k.2nYxQ,jr !>WYrЗ&~n](%o=8yg]8%l[ϺwVa2"LhlvۉD*`4bQXT4U (></ / ?}x|n~<Џ]7v*;/#{ǿq)~nxd?͗uy/;p'~{b7>c~ 3<<9l?\}~i|Y/{an|bG219I"`hơGxUTQǢ!e!A|t@*')He LtH:[ I s# vJ%6k$?=x+Ir;,_uQ򬹨g-p JC=^g~~t<)O|SUG xtt/,xb333Ì9vcիV8ƍp?K\kf8p0SSW]ךkL FFm[Yn-!JnXb@{[-ܺ}+t|k ]u?~b8::F]]-B}R(dvDxŗ8wSNڞy8pЂ*~]hT|PUP(hR]*`tǐ,5$;d*y%3RqBÔ}-' tS\9Tz9ĭwֆPo4jkjY`(\9$D~BwQTSD\n7 VcȲRBجcI ūDp-!!ס:EKgoW -QO9G"F ^UՐ$t]XTpYH$ɱ$U粥BQDDdY.\H]m-B3gXX,W0<25m|q)-]JCC=MX@$Irk|b'OqױA6|3##<.(e5?q'Oqם;bk ( =$Fs/]Zkm[( #+zI3dsYך/_Ɖ8qT"AWgUA\EW2 LLMY b(BW D8U>C:|iShD@L6 V_'9_li=P(0qB ,]|jm-3~O@:+aŧ#!Ͻ*a@`0HsSwk_u|Dضe3`eKL$ahQtw?;o`mfb1.a:o@ @ pkO7 EwW'] dY.Y3Ȏ۶c|YA7EC~CQB+ ;1,>}EgM/dSXB,ac>\m%n^_.'\}bGJIR<뜒s'q8SSFA$ Y$Jihkm;v2/BMb1#]ؓ6,hP瓵ڏ`0h˂$Iv(DQB?0mczBÚoJba m0^T\dO,wb e祹gA*_N6 %JƒIT1?μ!z)[UۺI\,++!4&B`~^B(J1l6kK3ti**X,D ` ]c!YXzY=K'qqș;sIvsN (Et!Js) t*.>\>})+!tby8G*{^ګu4x]$T[z.Jٳ_ss#Kv4_$N9jsVUϕfXfz2d\Mfbb@Icsu%iȒ \A<!OSfD+s;u8DYI:(-F eڒiU~UYB(JJ XAؼ/wmq6#:SV쾕v?g\ 3::jLLLfͺrovw@t?[rdxSYRbŢ!a' /J\6}9wU YeK٤ kF*,s5ۚ7Yäo"W/Jʑ@Q@r%l^=I1TP=*\3.H֮]KKK@ ^}2,\$ʉZ]gFX>xz8 M(L UTQEW Ij_Y1$ "AB$D`xvǮ$#ʕؼy3044H:8@KX'{Dȉ!b,UHm!X.BYc zzպY(V |N M8}X,BM<ȰI#g_Ksԅ[vr< .^$A*|X?QQb]0h~*:,ol&K2ix!bQQbaZ]'ZJ$0=P(.,443<2uֻT|_?Am[6}Hsnxd'z~}Jww=%K:.9 8pHnA[zglD'!JDUZcyP,r||>/A9^I\^-s䎜:uQ\x|PUp$lנS(fEQd̂9t`CznS<[B q=< 6IxF&!۰/omSU ߿ .rv9~Ă>r*BA;KyWndĨ)Ķrbbeg '}1:i%*\pr=l;8?a.F|‹/򶷽{Ng6(pMDխ. .!zGw?4ţX@Z{%E?{>ζ(2uuuBdYb||g:?kb\m F]b }q6\Cv5E&&'cU_/d%q .YBcCMB&0"9sUV2>>'~ dG&5'L ln};w ~UT vZ[$ϡil`0D2$wgy;4BX2QT+Ho/HL8y4׭uq9L]]tuuR,L9шlW>я(F&fC߸0B:>яٶm @Kzhmm!344dtIG󎝀}~wEaD",ᖛn\=|u*ÔY$ !8vxߓdg\n>iv Ƌ)J IG>±ILM1=3?si4MCUU;wYyy_??'JqsNFFF8y$_n"J3U?󡴒$191u]GQUB9Klkل*#qKYm*P\2 {s "[N(D":::4/L.\~ݶV@|+lDvwu,KRT 웚L&@׸h*ދH!A7V&hҐgcCGUTXB\>ĴK[r-z;| j5Vϒ_"X6ޟ ,ҲZ*Ϝau'd9(YU@ $IBUU 8_W_}~3 \!I $ E׾o26UBܥԧSL6œ";dUyѳEH$ZGEWl!TU+y.$IbŊO8Unl`!nH g2e&ru4lqs[rN)9W IDAT;>N"ʼnmb˖5iDN>ͣ'fs*N8 ɬ0::dYDQ,,,0\A]HRu4FNLOOwE:mm6Zht5"[uؐUx0;01nZBm 1֒PᆴC].dRJdz"U8A Vmp3l  vŶ!N0Uf  ut/8;aBuXc! +믳}hjjw5ݥ^-|xmdY]\x7Ӄucv؁/O~(g?@ pH Ku&i:cuEj54&=^Y*kE @ww7AbL~ff!xD5޺p…mC#0Q(J2jRx^DZVarlP 'а5N ױWe_m^|7~Ţ7ۿ;EKKKG2&D,bdd͸s6Ν;EQpstrB!\ j?iXޱBsnuPL& }T… .,  ,A>_@T(+؉D΅L;ִ"k!5-w5ZR޺X7 ػgۇY(bϞ=1<2Ҙ'7,2[^Y[Ր5*Rúg(c,T "CQ("d00޾za`d)T:[cx \ `޽AwW-Fʹxoaeu_ʗH2~_[|O!|s5O/Ng^eep$N8ΎG@jVD\L[̢ 9}zE/VAӫ oT`z÷3zx{x뭷 wȍ 7)ד>e\''~v~{nz{~ xnn00f hkkEWg'&''m\.cr|(Y䋗>±'Fn"77 łyBss3ZMeb_pc֚7$Si@d\ARAV [X)_,n(d/~}a/X+0.fkJA)u1tqd6 |(X066v]QÚ;2[jsueyD3eNg2/ꁔeY!:::5]ss 1ZS]\|T Pߏo Hؔcqq 4/ǃ7|(?ΐe Ei*ʖ [ۆ./"ROBA꿔uB$B-<Xu[L=W w #pGMuvŁPmHjڄa_\kʊ$1}ӼpaD|ް^v[dS*6O*H۷(r?M~ ߇<3X]͡g'ؓOxDy7ӳD>^^uSj&sZNtwuBdٲpbb@;.# o᳟j0fY`þ)]c"ぜZX*q T{q⸝j!h,2UTl²vtwur}AVö%t{['R(k(PSZsol]6"ˮmuW~e˜%Sgϵ{Y6`2fgEP4q޽M+ ( THӘO䋥&#L ,ch* ~yϡT,[ZZb4Z7קT*a!B=|]طw7/aC&%7ŏ۳>G15.O#DzrJF+A IʐYhznx0C0&Vkr~PmK)༯{1|Svl1Z4cukQ8Ԗ3[LRH9K5dųSO:R (c2l<%LR00zQڊ`A d2RjӭkZ{wR ++,^?~x'0Dڣc<@@'{>'<~5z0Iܯ1p"T3Er5& Ϭאd.pSڈr, DMʱQ8>+!455 ne0QA H eۉȘ,+T*z}rhj 2RY؈`lP&ct䭿Os1t~$IŜwuuSK[GP$ гs:;xAQBYgy{<&J;X(;,*v…7&A']a`̤n#Vo#RihqBFH /ssD0jTVG iMP jRCTUݓۨz,IN@LŒN-p5dV=gkMOSf^0=Aܹ`zzbC"ǹ{N{A`)ú:Mu(I)Fas6s&[YQMY`\pa/YQF~_P'/ad:!ܩ&.Á9M4q;{{[SĶ pO0\hYj@YchԙRjiE*[}(uqrM4wꟀ7lKb׾NbӐe 5]pmC3,fggUR ɔAx,8shK W^.OXmi10Zwh`z\{{Ai&2d~Z9X˦ PGr' CXY 0tàeyH"Ƚe4H=O)Ypa`L:8֭,.,,dHt@DTJ~*˲.`R@N`+bb@#?;@:LeכV[^b}mt@LL{Ξ]`ϙ ֫Y? gz4bX{+55-rH,7n=!x9mU4YZiԸ~|>T* QT5)}L ['CƆfq]m`#( n>LY,$>5Щ\s,ϴﯖk7:^z in6&:lKG Q"ڈٶS{ 5u92\+Gh&a d6z5P(]z!fw… ? 4eYJ}WO5O\ǝ,t`cmǟ&//mJ^AˆlNj%sc?F{ i=nRvi!2[46H?t9S{NdOjӵ,kY`={;l;~J S(WQ*U:㤭,SHZN1m,.\p]z"Wa~zR?@ϲJFbXDgܣv?L5w?b/7\fטUA!dqmk5-zZkg褈 5e#7k+ f"_FMC$4ďdJpl;hoo_^^F6E.C'I00qJST:7oOKhmmE2??G<+02 ;E~'8w >P͐B:'WzWq//?e7xW_{;sL~H&S64 QLؼo8{4>d],ogϜF&B9|[籐"e -H={v64O._'O k_2ׯ-\v}O߷Ǟ|pH2PJ?jj WTP.P,Q*䁌EPjyx`v2bBu'3?ɳtu|SL9>;V;k+B\Fli4XGoNu$9F1m\gZ)g2uZSqwE~P,155{00zbbY eOLC Ӆ|B/~R 7nA?3O?ejԉx>^~HBAӼ (9H$?xp?0::f=ҋz^'SCQe,d,|ÇAkk+Z[ZL*h[ʍz\%Y\Zh:e?t7d>rW->WVJAeTe4Bى dj"_|9j716&~o0Yau몫Ʀä;pBi>;ZcC=徯 Jvb.J{UWe|n5㩩:Pq苯?ʯ w^Ԍ tm,|)gsfgNG[y}P k]Ҵ!8;F?'m(Qj^i᝷f@=@. o^@u'ΜfuyGyQ jz~ϗ &Rubg:A1(nJ8A0ֹͫpŒFs2nMA(DoCUsAL ((+(($Yx(D?|r j8+BEڬya:(:G`9?uZCpH .qjZdTmLƎ:6JRh?DP@*5APp…F~/zE ZBPI""3IDAT([@sh "MAl#~T'!p2<4HGcX,DjuČF4X\/'rט m ֱY?׳u,NHF011 @5EQ$󙿃W^yo (|޼Wp}:55GY/|BTKL#];,QK_<ގPHէܘ>uOϒdx/O? JyHSSI Ettt2}>GGGO _/`*ƍ8)|x{>: O'׾왚sY/X&A@2B<+?sgkgO~sgO#NR $E"c/Of"o>r}X]Yy)7 53;_TJE@uvR2( D$CdD    4N 'xoE>?>sYQg'?>ιMuӦw~NesXYY͛޽w"ˡP(@rXYYA__fgg!rp:8˯\*P*pUDX3}.:;:DZ^s4R 94ZkGF$FKs3?t LK\=~6koŝ;w*o/}}p ąZXO#*5wq2V^Ȳ$ Tv G./BP>I7@#" A. e CG4OJ$I6m<s'" G? "!Lvz,pL*8z0R48JZ[[pU$S)>4`aatZ$TR3Sc1cjflvQuPp򛉉{)ςL h( *cU  Zun{<ܳjd^ eӰ畈'XY],KhoK j\pueV+ V+^)|Irك3soGh9)J< EQYpdEF~Uz ( n|!L X6+wR &Wb"Q֎ꫡozz W'432429nv&84x2d]|*݃6߾ DBzNBT"#j"$:;;Lz>^A7#ۄ=>N!XZ^FS(~ךg2bs 'e7cL ]T*ge@ ą 3;P=/z}PEsRSs)}&h i85(9¨PEHl<.|tٵlz={.\8C|PP,L({E=ŪH@@kRUP4(b!(J ޞ\.|.|>[auu+XYYr Ǣ!S(7|.]IyT*D"a$x<,+|bbHD˜b?+w~YaxxCXZZDkk+{p|Ə4I2o1X,Ç{9=}ɲVBXB(X[pc+͓(H&SD2vn[pxHTMMMwݻl劢XY^`^~!_([hj -bQܾsmmd$Fn#!]DSS#7n͛B\/_A0ǟ\vg=LLNQXduZȊl*+d,hP Pnbr^~_> 6*2*2ʕ R!|aܽ3+W>B8p`?|>f055$ σe,/bjJ%REY˪ouu,fffE,(&/W0,FYn&!c#J3~ {iX.(+2E>@, z$@X 7v|Y,,-bqib E5P@ <^/jUn+ 3ݽSk:*NC'N\}_^}VMݻwׇ\d2NxeD"LNC\@N a Ӛ ovA =ʊ`unOmdxWg'R4μ"y}{7/} <EQW^߁(v:Nx^/:K\EL,0BSv|;QEmO*o`@|Gؽk.^-}.B $2 ӓd{A$ꖘsgkN~ҙ |)Y5\PA} ժ=((F>/vtcGWJQ*Q*WPTP.onR#ѶfW-̼PH&ܤdHsssVhnnױ@ 9r9(p8d2lv`W\EX@[[K5osnnqRi47F!"FA|p… T??]!jv:x<455C)"DAADZٙ,ܮ]x ]x*$;U>ĭxc߿|pQɅqoj $!vGK:<;3ߏ\Ir xTUxE$@v@Q+2§( @"hqMȵHW\ai݇TB[3dYWVIR("hj݈}}ig|}}Z<^w=4P(~ ]Gn fA4"&''q~pOo'N3wfs3u @eȒݻwak \ߏ+?Џg^֡zٹ?㋟M!Wg~Mn_T񨾕ZP(DjfBn12rwD-V,GcbOCxX][y#SShmiA.1tYBEܳ:N8goV]HG|݉I۹DB \c\Cjÿb(ص***ǁhokÕ"Jp}ht//VVVLCσen,"a_("ϱ>bhmm,ڴٔTT === D/877@~\ܖ_I.\pa[~ͣo/&&&z^WM>JHt`|CC*B*B8!@wwЈ)bqqt="fܻ7Pp88&'AQ\za5#2v܉ T*Utt$pޔ ٩2EooϦG*BP$IV+ys pb#… .\|,=4J)B!S^}Uirو {x*Y(I~Y\}>W;pp~K… .8/k//|384m @ < Ϳ,..aq/f L2:6FYi)}x>h4$I(/)vf & χl~dg$snm:&j|zBP(#RPPlH$HD(BV#2>D @$zcϏ LMM5c 8!NL&q25|q25l6311*R @ < CC;LLNa㡯6Z|krm1Ltu^o,--sn2X O,RT\DqQ{:ۙ@ *<$Q0LȲ"vh4D@)‘22MxX,vj2;7 VX,ϗsF^LJpuP$jDb6EEvF#jBXG"OIx464B9 ;z37O$ر3Olrr--͸Nl6+mx"=8O6R@ feG?Fz^vt}^*ǿR;_\/W@ _i@ @@ eP -C@ o L4#le_Yt:d*E$A~#@ `ـ5`&oUǃjU^GNH enν<_98YJ\'n?n> @ EK; 4p̗FН=~Z@C#ج6DR!9qgˉ$I vr8f(uU(+)!0n5m+8Dcc= J[яaٿw/ \x<^ZZ|q2+JL啯?(`D)(_^3ODDQJKJ*`ZI& lKCKӜ +_\|q2/Ȳ̅(/;444Qk& fq8hJKK/?8"ee8&8| C##N Nڍ̹Qt"2-Mfr:͹TSJɲ̉Gx2=z6OfqifA@ Qb~ `0DR!Issn Y^^d2H&S d9vfhZ6Mb2@h,ڬ1vv?8H0bN3Y\Zbff*633aO{,sg#v֖)+-e~~ť%X,H&baaq ٌ$efLLNx8| $ *˩Q+hlh +e$Z[[XXXTڔ\:a @ (W̽} ?d2$I&3^FlHh5X\\(WwK$ st頪{hZ˽>h4b2z@KSv%ۋE$Zhl'kg#%.'{:`phƆIl];j׽}>E,H$Fet>luud2z}=rNjVRVVjf*m (c 6X;G Chh4uƄFrF"ܧFD",$)M9 ؘP\Ti@ N,,.RS] <4heLURd2/@ 뽄o jeiiI@@ }U PPQQ@ xyc |ȁ?-( [}cq٬ o,hHh56Nq/랾K a7e_07gߛu:ښWM1S,Ax<Tj c ^.tz l;6̞;Gnn@ μ1B^-EK@aP$vbhx;~K;`֖fZ[# JLEy93TUURUYA0"3vScz>tWJ\Wr`@@HIi{>̹H1jڱQTt?9v;{;~VʯeYщ}LwfY׎OZ^7f*}jq`ENv^o?jJ\.Z[mo޺E4rd7ܸy׮SUY@Uܘ_v @mxO@={ڙ;z׋hdzfIsnܽt:d쨪dph?Դ)>DIɣC`t:h0O,j-fddjzf$ jkjeqi ݆=O{[+5 D8u@ef3A6$YJKK:>3^gQ>OG{uuO=`NϽ^e*J=zbP^VF6%Lb4:m֊b S 2X Ѹâ ͆VvsOSzˏ ۙ 'Oд{7;*t*{:(// PZZB:zG%h43Ӵ{7崶4t؇FtPQ^#@ x ZCks3U#?mnF̩وݞ˯(/h2 kyuu t:JEqQ1UUؿVK*P8ht8 x}'#HňD"  zú]Y^^FVĥ%QW[^:ikho_t:Ǜ3(/+ch4jjY\Z~Ÿzs_O4evέ1$ITk@+wl.%D"eprr (-)lU,ɻj:ԃijkQA ,</5sI U*;Ɔz.^Byy{:XX\$Rrt:s1rR@:5wIT6=%s'hlli\.Vk1@^=OccKKD"jdgƺkcC0ss4lķjFm@Yd2ajkkzG33\.T*|aڰr'e9C0y V<^/ulp$Lmͣ3psΤYmX,fth GTX7kϻv6>Ѽ` VʊrGeZW@ TVTdaqBaa%%|d }4 Vk1>6^&% M R!p/,bPVSTT NcGKKKl6Ex<^$YZl I٬DI/L&2,*P(DI972>XV$Kzn]/p ۏWY1PXX -`(@AJ"ϏNEQzY)K4l6+-bTJ٦L&b!`2I&@0 _ Gk6QUDt:FJ”Vq:hZJK\A Vyce`i>?gNdphӁtN!L&--W ^O֖ls$fȀN_YsϓNmXWI^2;(**BVa1[VLr~&e ,ʶ@ ,_ǻ=8rp^/CCB!lv a6h48%f+ʊR! Hii);w6p٬w#Hg2Xrښuۖz.tN xt211¸D Xt!K;"x}q9\t:v$He8+zNe *t:J3M" (xɩr]X"Vl0==}pϻilh F{[+lĞvtQZRB0. @N>*V}&q"|y`;R(~LB3x:4)m-h4&e]Ks30 GׁT*+ee"5&J̻9qOu?3*Jd Kbɤ3T*N2}WT#g JL6ey˻eYnsړ;?S+dT y\i ,;w6 D\yXe YXXr~eWVFՐJu={D"$:^2,ZF/x5جϗ- s?yD$%JV/yn~1<̆8L& Yih)bT*ҩEB9#T*Eqq1H6uZL&& Ipf4%Ib|>]]]Hhi޽x>,.LOs't46+=ww9sG)ȲL ,'&8x`:Qe%34nI ]/2l=Lb;9sQn3P]@wHӏ^X| UUȲ eA\^^2f (*z(c #~lN!>vTUrMJKK(X(/+Ezjj2Ɔze} sz>;w=lJ_=71(ãc>p2vvs$|q2P'Op 46`zGu.ȰB{[+KãdvK˄,3:Ck"k*. ;}A\b00XVT*6Nlf2JͶ4͊ÊÁLF1"b8VFC2 FsfuYXXdq Wium ^.PҒJJ\D"fJ!Nzz3Z[ZsΤ1Mc2YXXnf%DiI IBʊ}ކ@;O>;#HdH$r4DYIӤi2,DwJ$L&mbF3,}@3,3:zL&K&%IY+,#.\\#TD2xc/xvff8yO6U1}3 drʕk|qSf3|]ʲ,j`r ޽x<Ω'x>Y_`zgVkkkr:UUx>>Ҧr`>}~;;O%ˊ0<2C] ~1 \|G{('Ç㔖P쨪DRQQ.^9'4oA:-ssnJKKhnO٬DI JZ gN|& Fx$ZYW._ *ephĩOOl墬S'Ϩ**j4T*"(v\0!x2c\cǕk9|)3gNn_,Dc1 ,:y~ǁh4 Sݘuw @G[ڸzUC_^ {j5vF |f9͟ǎ*_[aPd 6`(goZ:Oi' Lee'%IS_WO _k%iok#} !Orpݞ/2G BZfQ&_LO+psSS{N^=޵@=ĥWDtQVZJ0pt?L&:_ fr99rNYPZ dhZNv{HL&;z1bD02:L?/9y8CTTJVkxeF#ۚ\_VF"i^_?$aۉ| uuLMOӴ+O?U!W<\vY9v0W^j-`0PS]MeE9,h4^3Dhok<0 ȲDee"uǾ/7vW+--Q$2|>]]aj\ Dөv h$Z{ڹp8LMu5h r{vێNl$}ċ ^ 1/1Œ^[-)+e9pttrtѨus`^ W)(ӷ={ll2Q~P4b^oEE|_ceC"}/k6'k $Vp/P dXZ^Q*@WQ|W_=.--}}dvs!)-)9M{>CΜ9^gxx>??GedY;_{󔖔<힟Bo6kcp\.ko&=R)Z1Lx>Gp9SZ"Nc0H$LF*Q>bT֧iܽGׁ}208$9EAAB8hlhť%qsGwΜ"͢Rampϳ^4j \[kzW]' ܴѱqdYsOt$ 4 wh4hokݠ 1`Q :O>;|޻J&룤E"`0(a$]!I2QIh4ҙ fi[n5b>.RSSk78v0}Gӡ땠B8^`x|~?vh,gYE'pzzV,TQ-Eh4UM,&Q\ƝZFӑNgG|v#JyܺsWY飯BՒdz} xx$I^_et:ݶm}ۉFc#_tz ?/hkmaii7ocwYY^?933:4 $Dhn .R2翸O[k =}وaB^> ܺ}+6SVZ sn7{:RV9#'sutZ-9|Y򰣽 ш '臽ɄBa翸r!&&Qz=<_}>3nݹk֝cq%.Z{AoГJ`F>9{)j6v]8m:ۿ-e=$8v D\ fe\x IP`8~wr"WvSUYɞ=3pr7̭;wǟ|s _\rBm-#~_Fc3;7"|{[{gb1+F~^餼ӧ9{d0((O?眞Z#ΙS|zGYoM:/JSSlʌferA UULL*qevh4T%IcYx>~{},,.;7Xh,jR=OL̎JB0uNjsBycQ,,,F,SVZ`2el 켵{?Ovd9}r\켕Sv?aݜt*LԪlpIf~?yU0<:,n /RILO(X: (/^o]ewdqq#ؿT:=X,F  JůkQPP7 )Z C>}S0<2JeEm- ѽ:b2ɓ!`Hن\7u"`fvшl& }׷ΩS\rp8ٽk'Czw;O?dT*]=޲Rv2o~D2AeEd`0Ŀw|̣gGe`Knj_dzN>?D" D(,(ի_&9vHȊǃڍTڂg!RQ^FOo߶ ݎLLNqM~% )7kkq1KKF"sTjX ݁{Mee%##4~ ;^o[}+CHyŲJV·_Wc=^@ 6._,޽Ӊ{a)^r }Q=J*B駟zO$IeFÇ%î,OLN}-x54)58m6+k_W=y\qƵ0@6kWDp8$ IϏZfn05px<.N_h@0f{H\ALfSE&a㡨Zha2X,|~ZA2$LIg()q_@VLqq++ 7ԛ:yJJJޘd92vϋRdWɓb_=b.FfdubK(eD"fLN=%|~<^ffVy_ȍ R"N`Nj%ܬZլ\Y! L$sjqsC0Z&Eh4l$S\(>d2E4[')Dd2GFIbcb* ^$,]l6KtuGFێO|TW$LNI|>Z$Q$w{zz:tT*E2Td2E$bqZR5- a6)XfŴ6+d2bۘ@9rN~"Z M8L!IxFr,#PTTD:B _{#(G\j9<^%.'& vbff"‘Шz!_6f]Iv[a蹈97peEfs%x|]Lj5M]~~VO1MU*uYtG&a}uc#{t&]U 464AzW~`phGsOr<;gN`DC}UC]@IR` @]$UU$)B0޷sG y`VUVQYY=OEE9JdYjunVpvFKEy7n"D8aGU%`<-[qlόl&NV˿K *L&CSSg64: x<ȘL&ҙ DŲͺǾJ9/DC $)<\տT6Ynٰ&\OVeoeBZ.£pE <✼ O;S^LF tZ\́No=J)j5v}84?n޺C[k3s ܁nwNS>g~# Ng(**ڰNԉ,/pMdȡ.\nGPS]MUesyh5$ʎJG8to`0 2" B0yi {u5eQj:/xn5&'Nnܺ!z _Q[ tu.fsRYQj^"|q2YIl6a6hko0 c~qGy0`yS6瞧J܋_)oHW@{RjJ,\>#7osD‚vfh,2M1Lh hH$owkz;739"(j2{NB;vf\/,:$tizd2b& QIR"IK$tIJd*hD$4 l9ѱTܴW4މJzEt8g!2ebFFFFq: 9uIeFC*BՒJ)x(Hl @$U>| ' XfNu}dqec :|7Eo|kx'߷ <-|m%_Vfgx|q2C] ?v0_\ ~NZ3wcFcxxG_|̱#¹UrYE3i>|=Ξ;Fū&(%O? 0==CG{CÊ$$-MMO$2t:ã47Q\\̞!!?S'?OO?C~~Kn~6g[Myrt:쨪z/cnq_3% S4Shom^dxa4Ɔ[[tTW *'h4_XxUŘs֨8$45sy"(ee 004DyY)h;'Dٱ ^G*ǫH1fy4ROdzAaY׽Na" ^hc8+~z1t:55ڂNzGZNK[k˺O,cvvZ^XѨz0MKs]r&IYL%#{mxdGI&sd}*+[bl;Oquvc-.VCOu`ҟ|~7mXJ55وGQ333>zO6U̹ݢCi#X{cyx{`2@{{+hd2ᠺzLNMសWb59م*(K뮬('RYYAEyyeee,--v @f0>>^r-~9Y'Otnt 9n=rlgn|“>wGuȻ}v=V+ |k<-/ߺsu[ۋ76r|ZͮO$SYQA8l2qM"͠T} RShڽO>=  4/e)/`0p-"J  b\IRJs˥$UcywQXX.RdNz$KPcii:lVRôreYC]09ٞd D&&PP`!J)/Jr^h0dT I$X1M}~NX2J;?'F1];Fb:؅$KBL/444 I},sU* ׿u^#9|Fvgf瘙`~h47 ܜ{5_BϽާJx<  ¢,=ܾCׁ}ܺ}2ciy_}):z633x43o~v17=?ϡ]@4ŏ3vTUH$q`>ISL̽~fgssEGK. @$afnfoʊrD,Kd:}|9Μ~b6X^YFYx2zZ[)**r @@Ґb! b1\Ŭ33\zGev飯z7nVΟ^gxdV<^Bz`(G#>U IX $:l37;3;{#*V'){q=]B ]܆j1Ȧii$ I8(xP[U*nЁD*_f|2~Er9 K"414;N% ({@QQ!z"(gphՊݾNEy9r M4enqtpW]=g?}08Dqqr7Μ/.r!o#][Cp-45W^c] 355Mmm Ԕ˵뷐dTUWplxtIv:Ǻpo| $eBx}-~֋h8u؎55er>sC\ 4j5p8,\y!sI?ҍ24].8BpJA[q:bH\ŋ?X,7Mz-gfѱhT|뚛N}mLNMc2,.-q@Ǟ=r* ubxv'Q tZ-SӴ=z{t䳼LEE9999SR\LC} 33 iO<瓫?drrJ,raa!*2nhDmS # r><2O~so3F666}tRZXyu*!Sp8qDK?W(.*Փءܸ~,}msӁl$0 @dLٳgٳg:D"]ho^'.]F& h~U @ gR&/^â}fffhZjkkkvr XF y|fܣ}?HDx\T*dD".6-//_BNI}eDEy_]>H}y_MR}&L&pb/,PYQ2 h ɈdBQr%8kkvp@nߡ $PUY)>A^U*sp:]L2<2owDǯΎvڍInR- r(2@0hyV'&(/+A$`WsM$C6D" ~@xF #b4^>*35= A1颰@ ?1uHE7H8d`@t7v:<@T=0MMu. n*+9z( LEP(Y^^1pj.2Go_ gN$ j>3>.8w^j5##܄h.I|q><{M~K  wn_\L\ߝ{sE}8 p8 |VĽF#ǟ~㆘`g_qc?*65{.45R_~t紵OIAY PTT*"(SӳVWFF\|T*e%%,-X)-)acc\E:w;^Gn\ PQvwT*% d A"Fh,Fi [YQ^Rfφtͱ ~"t(_QRR(CÔXLJJQ*qQ~+3cGؽ]l%m#v1v26>N,tHaawMƙSYB!>&ܹۋ 77:wg0bV% KF T*Ѣ/+D,7 `4xtE/KQa!-)M,L~/-ShO<gsAaAeeFZ-FM5F ,hߏd8'b1cRf=Cd4RRRRZ[pܟ<ښjrACw]XPbf|†F@"{}g~J< ~ &lTVVp]JK 贺dirL&.~vP_W4ol`1^ܑ=gΝ;':?fggb1$nZI?]r}-yyVgd:?}%^? K_S<[3_)-~B-/@DZ ~}HQ(hmiEUUTUU{U&h4o|9bcYOxlN?mr*릚6`^'}P(D4h42.K YZZ^1d<~45-!j4x<^Fco`__aB7n~dl2}8,NS} B3gQg7b;.Lq<&ѲVd2e$I4j ɍ)!!!!!t8ǒkܗgsBٚ:uDP'W)-)!ӟ:C" ++$qvh3X,N"7pWzN# E9ΜnZ[x8T*%@"& 46х1[o܃맭TH$kWaynnV#J uz]DPgHe!ABN'ꆽ(J<} JEwa hq XP)x^*fRI eݻv7p2<^/#XpqQXD"-KHYI Cã  @9"(Sbٜ&Drrr(,,ARThnlHH/eF;?6G",--rɵ#xx<B0x_a|†!b16 "Lv{bSHG("vI&ĥWBp8>}V?l6r imq$IєįCp*8g8]I( G"S]/qmA=Zx<A\.WW  FIIKT*ijl\<@LNP';H&b'8}$ #RUUIMM5N\|}}cG ɘ_x^55 \>s={ϜA& \VWW'|ɩ)(#9rss)--rQS4lFlllVl)ZZ9A:FzN0 \|Qcbiix"I8so219ɱ# ,u\.*˸z&='q槩>Z2m--OƱ#666gqiH$2Gُ.멭fhxF#^?lSR34yF\.$LYL*o%KK477242N#03;kyxdfzZ_1D$''bW^?\% N8!ZsZsFLMMQQ^!#ULp8ZrN*SUYAUef|гXO\bUӎ bə  ̣+tnJwwa'~oaxdT#qX,NyYKe;Nk^ E͓H&QFgbtZT*UUokC&o?/cgmlm*"n:kki[ QH34 4_vv$ Essx}>ahx$eȕq .2 EE(s$IZ @#]m׭G2X JE";wL& Bd2z=#dD)j5QL߼;y{B"whm3 >}?k$q**ʹt pʡcll\5(,,5D"ܾަF֝Wun$! {&5LFAA>gNPS]BC4}$'JbiiY-bra18~H$tвN4enؿwf9>}ZVZXøcJPƑCjx8h4F3XF#%Ey'e/.$:ycq:,> S$w^zͼn%8 @ߏA>2Dmoܢ}>cccd2I0D.g T*BqizfL2uV(9|xrU2Ǿ{C_j/L2DYʊl'o jK/FuGΙ675_[z ?>=[6e>=w=v='&}3?I<B&q%s:%&zܵ#/Ƕ xD"o" 1adt!@?8ssa}.8n}zh4J_s/ 7G~M Nܚ0=L&74 BNEE9񃿦0Ǐa;΍"v o|ry]IzԂ54257P(D(bhx]t\<pdmGAV #B,PZLu{vNݞ@>51p>s:z göF~ re[ZESS#JZ[8wӳ!:3pZY3=T*9ӃFfX.'_,e2&`0~4j͵2W=wQQ^~쮥 EZ&,..qPMynps TTS_:JevRXPװX,8vJ~?XwxB|14<¥W8sGmLgyExJlL>IHKA}ݞǶ>/][6s>d/H8fqiKqQ!~L $b2qܘͦ,B8frjF\n7 `D CXfB0D@0@$hGڍ|~dL&1u:؉Z&r˳(/+%3pp'xtD1BxqDd"INP(RJy r;fv9 ɗ{q5z9y|[Ȉ^\ 999|;ea8uuu6@/F$%#+PO,g׺dH$B<G&h\BLJ$L'H&T*>sH$ZOlFq8F&lαч,.-sS=ҵ"f/_#5w?8бbkT(.#\qֈē d ~~gNp-@dO+8;֖"ZFѲImM5SST*ٷ< }}ᑇaT**H$ǺvtʿɩG209!dEUgٷ?d`Kj ҺN+| _mT*cįsLNOŐX, \h9'` H&Һ8N|>?kk|Tpٔ ^ՔZ(b;N"`mN 7͚ݎa~aյ5VVVz}&X]]ڇc}}p:]\nfSGvK20 z=P>=3C(byeR`ZKybFa0`0HaaX"tfssǃddcs  >gfxEsx}>z=3o~XL HH$h5,f3yI:}0ebyVV@TxŨxz2hnj`jz43"JK!JKKz}C0 _Oʃ@j~ tbccx2 snWݠLF,##I$hlU,-p8fr9 ( ,3ONUh4`4<̛X,b8K+h4jtd(h4FE Ĝ<͵`DDTz%^RrDIA~3sa6bLWi4:tP?}f$ǏvIe%9б^%ŔKptwg.*++h0r@6NGRVYZZ@~L&؏}}=wjZėR%&!+qzh4RS;vB>r\.mZ&BmM5H`0ȷx|FI20_ |Z[Q*sBדL&EɗZᦰU3.#RKP'm?wt. (+-ŚŚuZ-}跹u_!p8 h5Z1Ëīg%CxQY 0BO>>߿Ooo/|ffgCfolPVZ*dYn~axVk`PJ+Ý޾]kG~[ijVjϟ6]B`csD"ANN, |>|MB!rBa8DpʧGP?˻H(肐=ݶ)@ @ `bbP(D se8].v$Qztp8aq:? WP8h E2@fp{D}L&S MiM2rF_v5_ ? tƲˮcrj]pXgr,cbbOo_J(~ q\1xR6[{on!= ~)y$ &')+-cJ&ŹrP:_ӹ~'槷I&,-H&,/'Hr~y 7p/%1#ɆyΜFJڂ&uav~p$O X]]c}}RI,#~8PJIaAd"S'_f3H]&rz:%E 7lSa.EBT*Ν^4 ˮgzzp8Lk^VֈF CSO6{xHTFScC z>?s tǻH" N{Ѩ5'<-F % 195(7n[w1nt[*EE47LyYdJ @@h_Ghf \ 5U/P^IKKy04Ľ&''H4ʄ۹BCUe% b͵03;GSC=P(L~~99 ?{:5 J˟\IW20BV b8yB!dƝ'QUY!^,"똝# /zRI("?/X<է$gf|ya61 g%'GaKKKh5sC쪭A67%/cS9Na Vk~SE,.-fJmUF`4I$|>^/ qԚo0Lhg_[+^ʊrJJ9vkB^vwaNokkZllnFu8";sLZB:'3M&x)x7ZhG0=3KEyBu$^=tRXZ^"d2]žp:imThGבP} JE<3@Ւ+EB>N?`ɩ)-==-='3>ad0n 20Q0MLNMjH$®"`ox}>OCtn<+nٌd215=CYi I`ue<+X!qmb8<2L&u2| gϞqg8فVƭ[twno?5*&8r09Of"=H&h"s/e!GQa!7n煨-S_5$H0tǝ~"DB 43772rFC0biiX<#]447@2c a͵b`}mܿ|@IFF25=ÛgNɵy޾@+)."s ^B1Z,M$v P($m~R L>IٲuFj珤tmn9!Fqd2Y\@!SQ^_QJ&t:QU)Y28Nrrd2vt! 4 h%#D" JL$ vqIƆzVVWI&tZ  *Z9݃?(3?8}(nh4$ enܼ~UB|p#.^̇p_|x6|~ }!LٮLLNf3'cJ\'  )PHg;NA6秷oK |>BN /JI<=sPfd|> x˼i3='|-H,e~aJ @ ٽFݾ~N8Ry7n1:6h4)GFܾ'5n&9uh4J$QfPb Ag~as=%" eQkBbJE#N:ɇD"Rd3 ,-/3adn' C|LpԴy]W %q #EfmNcc=1 |> u{L$(G388̾T*k{G3 wVVVDQ|>TR`OFI $P_WოooWUV>cx( N8F[k3Y__l6PH˸dPJe`2iQ,E8VVW2* AjtB!P_{YlJ\eN:p857ťe6:_$LN>/ {?8H"{q\TUV͵P(e(RRRF&P :tϡ\<gR%jE`Zz*?OH$D"d2w9}4h ׃57W&N+[f\n8v{0Mr|>Vd2l6fܩ= 9rp8ݖ[93HI? Vt}9xqt^M&:\\?wJj$Fݾ*a&YU[];f{pA|>?jkın"(th AVgO1&l&^=B.'gGt)p$]D"Q0!ӟO6pn~yOOjn_p"Ͽʜ>HaFh4*fIz2 /?r<^//7xl -Z[`ǃas|WfMM.!H&ʧw{)/-ell *+KE>DRv{MNpl\LIq9| 7nB (-)GP?_qgNd- x$^ӹ\ڬr2ʹe˔NIAj@o.a&Fb6 /s[{'O_ Ɔzqo0L+Տ|{$UD284DgG;> gϝpOreN'(ss|t"v8CNNee/ i2' =αlo@i?ΎvV˦ѦF*+˳lۦ8qǏ295N`g`Mpʶ&&Effgк.N IcC=&ֽ_֖OP_G(:/Z]v跄ijbP(DAg.&lx<FFFq]l:D}iuȐH#ӺZ-F.ӘLFe46 X۱MN /#&v z&D"}456IlS |Nb[+]ďk  /\BѠhcLNM{w-xx,NaA PdB,f3YL}UUTޢZåWL#Y@ő}_&X⳶'%/=M"eA=[[?ۦk~urǎikm;L-5&Y R LN I xf!9}fp T*!"5ȾtXEHoղM: FCz=oyI `_ۗKpK▽Y:@EyX~n˟9w-a]s x08ll2p81PˍFFxZsq8( Fh$ zfdnnP8L[^ =D2@ N[;zx<..;·Pb;\A `.]69NN4MJăB69IEy`ť%fx3l:( /:MI:tv{AA$F+bN.\7Ϝ?njZl+m5q\.k,Rv%Ue9Ok_][Œ\'OQO?zʎz{4b^I౑`V #荳n6oNAhqq ^/Q6$%%r/~sse>2ssTU%>3\7\ݼ%ͳD<wF Q網fI,/x> no?hL\z65&PH\EfKGܹ'[z`d2hT<`{zئH&llnD^r_m&lW^# u} AA<Оb1lB~Zx<^ x}>c`K83IAi9E- :s1rT*>/KSj}&H{CìD"ؿtnF &ikm^YѱɩK>tZzəeΝ=ç5hL&eF¯?¢8@D"+ɍן_n1>1ɽa:}vWejz Lo@Gd`mm-bZF IRWWb` ӲaP7в{?UܳSH{v >,{.Σ d>7  23$T IDAT>B,%f A. [~ɈRdiiD"AzM(js~{X,MR*s$qh'w7bft||]t&CG&ARRP(aܛd.JWbg[+KK^$ FU,8:uqB0^ɄB@&^aVW xCR*sMi:6BuU%GBVTZ-ƕRDTh5Ņsg)wH&Su:9OJB9w9$ B0D wERi Y1K*tzNfKih4OFo=԰03;b1RP5(ԅ*#inlEgaBO6@s5{n&VqV1F1! d҈{nV+aJ+IP˫눆CVT!!13`K/UE$٬ h4_]gWND"Q"(CÔDx}>JKKQd]ͳct{hnj|ؿo/odO^d2~#+S3>V8 џPXxtc=nD"fll3H`_+XJ%(Ŋ_##c<&P(eهP(DUeqQ"s@ ()Rȫ_->wwYeAY6gL f6VN B H]w_ˠMn3K^Q!FW**^l6K8F$VWfmXt{Xt{c8n+DE_NQz}EgmE8_"E(20ۈdzD0eG3 "g)Cinܺ2Rffgķ짦j`q붜,t>D$%pA;t ?_s3jg.]8jUn\8PQ^F[k+c NUHRV~ Bx}>ޅ-nD"K3s}gfhȻ\~¶b_x<: @ ^+ˋ |klhƭ475bЅ $Ǟ]\+&Ɉ"noJR^*W(HYP(^Z W0 na]a49t2-;nJ tp̻տkɟƒ_t{::x`d2ɓ˯icGHIH#(z?agbb(Pӧ!#dY B,^%OP*]Mȓ!iW `ݿϥΣVf z߼E&v<&qk=ٽ_3$rS7@cC=`j"(FQlFaaqq9 sg 1!ξ}{Z,0(¹ T*{vBRy0'O$VwY>rgݒvzx 86>2;dLJ20fȡC K<'L1:6N !w=|U~w׺^~"Ξ_}/uynBAzy>^FqQ<*si$r/d`!Z[jDYH'?EJbZЁnؿ:R\FڅZFJ<}1vsNV32.}[XHQm(¥7C.u]  W3Һm;y!>ee>{p8ϸ#7*cO> \>mF~1CGikv>r \CTb66tr @0b=!vtɄRFˤ I hn"HCvޅNER鴤3_D1#@. 20{ޗVetlh4J6zz"MMt%PG:eѴ r FC* ryr6b4HRD2*ERRHRj,mǷ-̠wqo>gi iw)AeĻ|AΎ,fa9zS8QyA~&Vzr>Et  :&NNg8 &QfX( ̆ L:}^zg[+3ٽ{[rqbB&& y0MM&t:,GUٜ vZ4 wFVNq:X,ff0cRZV;E,f-^c~@ jɈqJJl6:!˽)b8vVWWfL&~JKJP\yB,DYY8*`ey !`uʫi܁ZAJ\P`^įh{v"FZڙB`r/ar5A/@'0:Kr5Aemjtz 禰9K"Nou~ FF$eɤ̌=mAR$ &ZC&BRD9KH%W쥡}k}Šlj;'O?}3>^`݆ݞ{lolhئJU ɣdǣRhLˎf*+w@uz\Siulʢ(l)?@Ӓd0MTWW-(pinb|bCdh' I8tS?zW~@yȉG'r( $:a{{wo!DVX4e-G m~Fp_{FETHTjP/ucbx$&J%9z_eyiOs/7.DinߋB/Qk4(*+!uDg0vV1@$sS,-bڑx J`$u l y5)>)Rd#fvuѾߏ$e)q:iٱٹ92 Flv}j   t:MI3Ee {uテFd|,Jh8D$" \MPBr5A6AXNHu}@"c5P넫FpUVLęCђIU(9OBTP(8p4JE7RR^P-NW9pYtT*E롴I鍈 hVG -JVۉyq'ncmlmmrxta 55X-V$$.w imA:! sAf3 F~{%cYimif/u#u:qL"%fLkvsغL:FCfil \EVցɠ3ýddlQ,S^]P`;2_7ʪjPk4 PZ^`bx:'J=>ɤB JT*8pVKWE !8 b{hA>bw f28\eDB< HD pyyh4A1 =(ffh4ZX NG,( l0 ~jzH!^ZY/VMScËkM{iikrKiQԩS?7oBښHƇkެׅ*T8pzƍx~)kK3<\Ӯ ;ѬkYS'A٬W[^ŽYz㝓G;384l^Y뤻NǁsoVH6nH9Yf8t5) .=24>*[I~M˯9sI8~\ux L6&(wp,1 DQY%CΡ(e?[ؿtezyׁ,'u9M[cWWVYD456ڲ7oQ]U*-Y4 JR^D )0 y:Z-#cDQ:vqQiLWwPz2(;NGR*5tʼ ̑CY p 3Z!fd1b8$ (?디XMph' w.._J$,!<@_ u| j}2<>-"7t03s\pk_~EWX]M"@V̲vsA?jRtb68yئ5ZvW9; y q)nݾtvfe%j "{Wslkǎ&ffyT*}p Fd% ()q27?dwrR>vZ[[P)/,RE;;c408v4Z Y1G\ٴȳ0<7G}]-ŨߧwnSWWFΝnjQs>,??$Jg #;;Q4Qsgkk1R9G}Zd2108HKˎMKr)WoԄZ&J1>>A2$ee.bVRnNPT)Q( B,'?@UeFv   Ϟ/{G;ǎnV5{zEǃQ$}p饷IX-Q b]NтJsC7JӚ .9#וM;}_w]m(yQ+}~vT*;[YQoe`*׻=R9y3Q4d2E{[+%T*AL&&M{W}߷Ɔz++x}>[&*~nܼMGNR߷[hkke=$]TUUbSlg<7 1 'f+E"gxq2 dʊ s̻QYQ!R\7, )._FYY{󹄿219EyY?'~E "EI(B `e%ZMrv5?J($B:_[KA4;pM{I8q|Mof%b6T*ŜP=/_ٽuzϗL.?p'02:&Lk~? R/Bux<Ѹuyמozfp frj ,s|[-_˟w,zd@ (o ¤)<3R`uuUbfI,znQqo;z]. Voo/?Os FÅ HRC!dx/UoѨ@<@q\d"d YZ W}ɡ1s( X,fBNן_ATr>TJH4nGE ^2 ux>FFǨ"R^^Ϸb~!i^><%;.W)e6:K?ajg999U4LYF.^8'  |eNS8vFF=}(͏CŨ(//< 0.WN`4cF&ҩ4Lݎ 䶫jP*mOV ӉNPըjNׇfp|˵c:-/NYh$Jii _߸R;)}j|K>t hjbQ?׉EGΝ4560>>I]]- L} FFF *=1=—__Ǡ7T)=׿_/mb{ g m20ΝkWV74Nl6 b1Rp,7oalbÇ enݹfE#CHHja|e>|d2E[k _~}B5N >F&}Eg/hʊ|g6 \דYY/`uu LpdFMovhT  ""?O, vz{9r C20O*Pt5vL:Ao@P-Wbl6ZhT@P`Ȥ3d***E(R)t:djK/zhlh?Qd4dm5$qHO>K>E]NbU|⢛߿!vw384n 'cL`4ٿo/U&'$)&'XX\0 ػ{_F0bXH4ʞݻx_^'_|EssxPo/ a_'ޖ)P r"e%$IrRH@y_QX,۷GަiQSS(t:J`0`6iݱㅹ%TUT20J-;]w}EZB@EZYI"+ItZ<Z#DQjvW7uuWV@̊ڼKKr~1˕m=|M ؿP,lL$bLVbM9d2)--d4^Yac2rӱfBdbuu5l6"(zd2,[&ٳg^2ScN}tZ#7ow*-AT_gg/>nw#:WgeD($;HY:v s-=Jww& IDAT0>r~nBE:(:vf%jf,3VBNcu5I,;'4=}lʙ_-VrX,,?g.dnhY>}*++:y~>x"&1/ejk+F\?-OdfvN]m-Z6/SFst:ne[Xl2Jj4DQwr~yEg⩃@ OB$%NQR`gu )췰/K _f){ڂR] ^&ۣA k( g#?$IM&B@)Rm湂@'5J/M_T*RvOOgf(++ô2;v ]s=ʣA E ȮvAH"E6rݍVRV|$Ibi踜h0H$TUvW7ͷ\BT^C}=^.gM3J·ےIf勯ŗ&ij|証X._,%%=~0BEyy [z}~cd!ŋz x?19ŲVf{Mڶ/X2f`ZJPwRgh>Zh4J{N~%z{v&ȾT*-GqY YB#Ϳ. u)P^^{/eLNN;0**ʉF٬'v?\g}i4LF#x$ AQV_BVq1(6`~y =|p=yi\ {[_&fK9A*˙.&OS3tZWΓO0 x>JJXp̻̚,`bEghn#nSr01-(h5J r_܎*o;UUr<%*+hinf|rR6\ G0ؿWa0רb EeDJJTUUqsBY,fr2I Νa`- e|^}A`zfySo&׏agiK*cu )+Xb޽ _`Yp,nA0/Z/>np&؀ʵ/ dDefff\Pؿoؾh4ʽ()be`3+'/<*S"j,DB0Y1~y,;zz/QnÎRb5 +Id2,~لB@b8v ZM< ehrt߃#ݻFسg7~ %!lp8,+J7wVL:̴s@NO?Tiqm |;#ܺ} $⢛=w12:J"/lZ@6N]$$uajzy܏}0Tpvajz 0$}RghllxOUt/bveCM֊s @ÚQQT?zTjʆzmllXf+|>sxzT* r-/+7>`Cڼkk E7y66VX,FZb XML%QT\`%DI(ʫ?48Bv@0 \dLFDf,y]&k>'7ݶ■g,yQT?hZy\{Ig2:utWRZgK}CݮX ɔρ*yM|JƳ#JԈoO"/\vdD72ow"G!QH"`R|n9DPVSy1+1 B!j5(,OZL&#ee̲C~jםKѠP(dҬWr֦{UY7;26+Mȹ\t~<)oB{ILMȹ}Gbȳ/|o3ֶŶ9b1LkR:X$TUU?`a)`'=W"Ec2TVVDfsh7n2==o|!bVYB&Zth[$I˨%NT(AS zKbw>E7%%N 20e,y8E禯ÎfóD37`jjh<% V˱c|~*) B Ғy,̹d5c LUeeQxc%i뢳ݨT-;fb/"IY])`s}8vܞ%4-;r20DSc6w\# "CP*\8[D:`(D d%s`>g<ebr_4Y1//#bF._WVr20 ^6\%;価 lw^KsYr20ѨVDz DH~Xv:,-8|(1b !Q[AeEH7o271Lӹ;b顣JBZ,IJ $T&Pn %.k "(P2*++/>԰IEy e`q/l(  ź!*.| ? c']]wɈ";[}`(DsS#$R)skƲ鐥 co 3Aua ʦ/޾X$20 Ng0Lx<b <: `2XXtrdXYYf< Ш՘fA@R䥬̅$If<%l͚k zYm&]< ǃbEE FNC~j\ (z*DQd~~iΟ=Ϸ̝n>} ͊dbUF*$ΠѨIg2hjDQ$O`K2 ESM u4PFaN&jݐb-?yZڇ@![ffG$t8oVnccú+/T(~|QA~(󢨩y^碡T*e2g)6(hk6cmg(&[cvuw3| Of2l6<jD"墹ʽGUUt^۳D6?p8 QeGNw# 17/hHSHRNgyOUU%>վ~va4^f3)R|H)RCq.}k3/2/]r 233=q=tv۝K477XDec+:QXVlbzfOCC==<%Ae)R$f3SO)&!]f244̎DQ\.DNV!TG095bF? òvb6+QN" HPtzMtZK.M(d,ZJ䵓z02,ZGd4xvrRm-sܙ\VB;D zQ*tN`g Z$eJܞ%r?/8 }&Agb]`0_]mhZ<%FƱX-t>Ŀ;Ϳg_ñ|>:%%N|wP(h49)nݾ¤Y\d*EC} BncG;1L1!V05=M}]- K,7>Rju5I$bNN `dZub+oQ`f,;99Gˎ87Luu33rH>'=rX|MIinj|Gkm9 8tp}Xfe)jqɄlh420dWG;uuLMO366hj3ghsB?NF*XI,GsAL!'r;}c|Ą 4S8$I"NI3[TTS^^&8v\.&9yrQeee!>_~}Dtp;Mw(/g~~Na$V+(Pbd%>;ݔmlg$m ȟo¹ܾ++r?/9~A0ZA{W_~}^.rIj(bl|ӧNkj>tpd\W[^gbrᗊܯgn4533Ye?-̷{-G"jsgP]U=q87''9yG;Q("޷wvUt&P_ϝnn޼MC}P;o fd*qmÁ}{ijg݄+x(L%q{<բT*1 Xv47a6֕Ԉә3l 3ҺknO 1+T* *1âۍ磩QN_PyYt:7) <~1ֵjcސ|n 'MBNrHE&SnFJ2g(طw775={z[<=d`~R4Uq&w=Y%?+IQA^ fY)l&$W(/0J҄\mBPPYQ^\.M20Da}P!DlL&"'ce%B"l1?6UFfYXX`brgONW7~p ?@&jb2q{<\$S)4L:ZF̈ڡ/\\D9ٛwd2x?kW]UN0_$\Rҩ4:T*?̅^보Eסh@:;!-K/疥5_ iX<~)Z,54 @Պ`$RY PUUgi ٌB۸d-i}u3)m< ̷D"d7O5):kY^t:=k!IbKW(^-3UFZ,&bߚM@ѾR7)*DZog8uj|Ɋ<ơP <1XO$H/%r+͢Ѩe"oI w(S475"otQ85 d2"eeQ7By~Ŷ} W)d"[XX(Ed$IPtr.jwN'#YNnȗ__7:233VN>|?n'Nc2T(~?e s>3O_|/r\pn;/w[| Ry ݏ=J$` R[S sdD}{vgj4u{< Ջn7RHښ"EӲbY|Gmls< cx=\.gp팎p.'ֶ?I<PZRVeuuF/onmi«kFUJaX6f~4-6MڌT* )4j VBؠQf;VTsnb0<2B_߲mA&''I&=rdg1`͸\Nv;b/d2۬p9r#im=4dJ[߿g_Nut`Zkd9 6t B/K/@,CE_ȶ[<--4YCss% O_unjp,&n֭v-z>[6qhf27?߿=w_/sBav0ݻ! ȢNիW,-ٶmj+|77{r2mmm޻8vڅRi4ijj"2>>$I+>6 B:d2E|kXlv%xH۶H$ܺ=szMzN۷kyܼu֖۷mmf NtH$!;|[lfk_2zaEKLD"mHR,X,$4qrU}6tfmjP=K9z_ ,X,N'.0 ڲvO0Lf>Foӈ 훺XZW,p=n?zޞn;;9:߳U x2K%v,"M+n~}' ^usGO{z+wW74VTd}y0 4{4{Tܬ-- *[/F3 \2d^#! @EF_^ګQd>cPH*$R`  Ly :;ڙ_@ +W܅_"D$Ul~d}|RpU\.<733PѮ~!&Mb09t`70x}^ohpq0ssK9$Yd2G470]=CH$/.Ԅ`$Na4_} t' ̧HK)pA.p8߉j(ב㳇$ vҕ+D&~n ! "?[r90#$XZ 06>+pIl6hTӈ%N~p-7N}WSE70Wi~&+.Jڬ\xFxt:(|EQۄȡt*:5:6FI$n`0Ϟoƣ u`ب-#zà@Qà2vH8vN.cm"4}^dO b#}>sΎvmlu<<+QT+ݼul6qGDQqy\><}@0MH.CQ.ɼӉzy,,ؾo<## L%v :,J A8tl۶F`44lyc ,p8FwIhxI=bAd6vq:dY&mlu<x?th K$/5ՂXRǴѾ׷nݺw=$I-Eri24rIR0;8Fۯ%ECU:㳅x<0_2ۙÓ\Z h `uxt(uQ'2R&KbqdY \T>\K2U{T:L:BANE2,L%p:,D1m{tvhϑe;2;7GߦMA6twL&ZvkEA‚  b1dEn/.narjZ 91N~pRXmOmMW[oK6{&1ؿN440 ,wn"(^f+DQĿ=<7$c2B\|NOw7+LMO؄( ,i$X-Y +lB,hoczfƦΝ:|<-}ZS3zrj!puvںzT+y{44{wٹ9|l.Y(.s8tp^%Oss3FT*hdn~Nul~Ԩ".h4EE.Ce~\.f3=nFFGq{eM&^ǍGii񱸸D 7`2$$IzHUeCy$( Z^&-+=Gz}=D}$Eؖt:q{w8d CY\VQNt8ivgb1[8z #c\vl6{<,/^@.'qmM~^fDѲUǃA$^{8? ж]/ȹUš#^?zG!zN03=rHl6O(oH&q8Z(;w`0` N7DH$enn^ss. ڽk'ť%I}JiϜGoO7?[>tKadSy70? \JM70Lhjj$HhW2g^oY A Ƌn`$Y&LN$p$ٳxn'UW0V/ӪY@*;,8l6Z|>XYyɄGoP yɞޠuZ]|܃bll,/d2E0rt"JafHP˻^ێFC53:(IX: k&yb(J /UlPOJR1[xPI}1-͵՜*[Bg}@XSG0>@`}r}W_*W_HV.DQl6cHD"`LNNU 4Z-475c۷1?7m D" -AV t|_ ybM,_:VD"+-fPDQz{{| ^Q UcN:L&*6LzUmAS|t$Î^H$y/ɤj^zOfBIHljT%MMŏ)‚ߏfh0 ݄#+`&A yY rJ&! !ZuPv((ǣ@TW,Bu Z4V>e si_Ro5#KtaY*ijnF_V3AgDSVEQ܊hGm:xr`Z?soxb`.#ΐNgeѠwC!,Ai^ B"|UDtUBSFdQg/Xh(uV6mڴl6˿̎;k惀ng׮]4xصk׊[[[\٪ܭeZR>ѣG?G`YjV^F0~{r CCCjo>v;\ׯK6 A&j233(˹ˎdf~EkHRvUGK_,77ܸq#7F \77' i2//9 " ^bpYqg@QW-K,[2˭6vΜ9lchhW^7ƶlڴ8F)bry7xn;ȂW{޻ɉ lC'vkdSSG3ؽ9r0)$)(R5^#V"L2rƌqn`(PJ8D"I fa2 X-f^o3fy;_\dx#tt35=MsSeX\墱>j7?@WW'{[79^Ёv K%n`v&imn`\.svϽrL[M0MUs9tx?>o3Fd <]LNMq7p8h @377϶rw(yql#J#|K+!jD8}]z:Y$ NG4E?TI?X\rMu3Ϝ!˱kN}x@-tQEv;$ HsRޟe*70wd|bAqlꩻ) oJwW'ׯK/t:D"\|]v"I&h/sI xtt`@mJ #ZWez啗sF +HZW kҭo\zN yu/UGK%\MQWU_ĮV]XQ5Ji\I&n1BiIcMRlEQp5G$AoW9}:;ݜ|V-;sgw}ѫܻgtx7^*??rs.H'B eރ τhHx}ٱv [,dYaqqa %cS"4*$^7 LR D",\q44(JF Hb2_XnS}ɊL*!_Xl2t:Xj\$)Rz 2ss9w"{P)'a۱X,xܪC#ƥy] <qK|W$zI3O[pP˅C[K SӪ^?88vͳ=]N&)s(mX; o=poj{:5JAIn>QPUQBV_-7-.s0WEfiIkEUAZ=J.?M|M z\>6s K4x҉ ?Ǩ/,T*ENQ/2NVzDQЌAٌ\ B12,+("?2ts\2(/l:p0  y;u ׻>EVvK{@(V@a{o;J ʟעfH$yA_"fG&a~nEVxomN}xDsS>iۦC IDAT 0h'  rfZBս^P06V3('vk닐'eBn:ד/ , Knb(JgՐce`^RVFNQ1Ȳfd2ky_uC]Ul.KsSf&8z!I2tŞݻ%V548x;ٌl&韕̯L-&nRڶr}-_{R^G)mhR]FK7T>Rs9yW.eA$pSYGLV/keɩ "b-ucdYʿe:Ox GFFF t֭XơauEONwW'\n}q1NJXnXPV(g75Wn>r??P <-, o}[H|il2W0@)+@kY޲&ɨ.6 :Y !ڠPJy\)ϫ裲l.E:&7SLLp%na5c19}<;{]pI$v?OXFNU~ynA@Տnr?6Ζ{h`08t(/&S1}U^b+IWB7դu*?IKPXjRX*)Ԉ_XmJ*YD[YOA(b%?K%cEFZ:̵WuBLG A4<ш L3{MTJuL&@U~ntnXf]_|@VE>nE֖Z[TW,= esXYcyctiP^(,q (##'I|{Þ*NpVr9=Wngݛ9Οgٻszn~,>nuX2 b<Aŕ_S+!.7,Sb2ҶXZy%vm)˫*ۦXB9i}_B3()fc{bpzk~6F:>?7y]cv 体022ZUҥ+X 1==,dp|zz6_Z(I_j/Ǜn/~>o֖ xD 47AkAC&X@4ؐ)N6z#r&;Ƕs3>}h"[F6(A|tg`׮mLMt{hp98)|ՄH&Nv1=~u,*EOW"bebOx$i)i1NΣ-KJPz-Y.*{sTz0V5OTD"Y,&Od$IX,&LG+5`8\p*fkV,R IpɻncX[ƣmp qU~6Dd2\rzx2/>$q%~ٻ .q]qU=@$##J%4X^N~w^tz?ٽ{7;v |YիWzvͩ.\9Ƒ#Gk,..r:@[ "YÖΟ;O:tI&sV0 06|E#_D&slܽ} AoÌ 8@,թΣP(eaΨ CJEx G۰ 9l49`^@l-W2OKT'uVutY"ae2YR:H.<*H݌1d#n< 1t}>$17?ϦMNqz$ MM⭷y+_ް>< nܼ4:ȡx+F㕗_򕫸\NYpp$^Qh/& sGq:D"Q=> ^;&'Itv#Su:DAL0LܿOoOKKwЁ=l{jc;:걮7ˮ n۶7tCطѱ1 =LNNL&I&:@"@ 6ťcHa4|۷'OrUyW>D*h67s{eҕq׮jb.TB86a&F8X6^mE \*)#˂XZΕAJTl9AuRV4Z eDup!+tKWjJw1 @Ob5+Vm670:bFEv;&ZD/mnm-Ƹt wdtl^G&a\~>jL/qqMu\|P(̇jn4nܼ^CNڬ[5p$G1QT{!+6׊tgx(FcOgEA ->/P'1o*طc^dhvWB8!Im\xv{;wYٽk'\Vnd`SX-Mt#a[q] /I2X)!,sx NrUu<*X-.^B$e: (ken4#b1=n("#I2HdN"ԎI&r$#Hp+pNC@!F#ciV+6o7>؈n"]]456r!x;v9ЎؿO.HJHX\.U<Ou]Triii˿Ao7x7|2gϞ:'Iĭ4-K[\X:^j\wVW`YXݪb%V:ହPV٪~)@aQYV+%\I,v([p0"]xDB! I2J70\N"ΔnlltH$RٌjhC,qZ8 _d2lb0G"4\D1޳p$^9:lL"#.,Ws{>N l߆(*|w+X hjl+os:N*oϬ \%*T~PSW*:N(ʌ \ղm>ӷ:)\QbW_!ڴTJjl 4_M_4\WqNoUu.j.d*tQTiT5Q@*]Pc?*- F%I"L"2LzŲ7C(,xA(Ka{Nhf3{v}^]'?z^-XWyxb`.dY.QwRXFޖ!RF4RJK*-U-ܗR*)nhԨ^;RzP6׫ˈa.+żR,(j0!&v*b"R-Wa>zpbzRwՔII m N(2jw"|˾y߫ɲjEet:@T,CbdD(X:70#l{dQg HW>2ԟOO \;JvDc1ĪF9UJVەaMەeꎥ\nCA0Ye2I]y"X^:Q\^*}zP|_ -ĕPDJIaIWB֖+WDVJՄ\5-K$%z̽^bӉ$~rRAJ n ˽{vMN߷{|^Lη9sc z&fima2H$8 '>M-щy70o(r%]*p0>qVݛtrwd^{8LI0[Us9r XjMﳩ{46zg)( 1:6D[dj4' 9i/O=- T&8 .]YB : FO&\qDhAۛ#ihԛ#7 Z~sgH7?tڛ%a$"zlE ɘSBQDrC"0kՔ!aD8dS:^[>MYi,, GchÓV %2 Gcؒ {cZۯ_I]4˫ECy7hp6eKR=,!-5jZj^̧\\>mVBЬ' ^мzNj+SD`4 TX*Ks|xZfYh\P#ˑL&Ef{dn`etlCC ݹ-iokwx ׮Dssyzt:ͫ_3.J8XKWbؽk'9>bqv9)lV x؋\pIv؁l"L~?kPd*je)SiyIRv~ӟk굏xw;IK_v#y5峄%n`]/IǸϯ~$IΜc4泛J%ْ~Ʊп=̵Z7%})i=dS:-m+?M6-oΐI)3dS::i -Y5ݜ!4ӹ9K&%oQwlɖ_cKT¨' F: VT\߯D*!еM&.M"={L2,9޳]!yJ%X,쬄,˄Ba"K 8( zFw$.Y/*[Z|LNM3;;(fS[x]d2b4  q4z\8Y /tuOm "d#߷gюqQ'f9w"=];"R vUjgΞb`6hokerjM=|t& ê ꤿ/j- ɰĞݻ>s?Pղ.\xzN@nh4jpssZf2L}]NMp-SOܻ==]]XG6`(\ܿOsSFipPl6Yz1n`i)@[[+~Áq, h4J:&͡QFlvR$\W`0d|`qq̒hbbpH_ Ձ2K5-}+KSK6 ⿎]|DZ\j.WBGk+3$zɑ,C$G9Gg͡sf"'= IDAT&R v5ΪkQ RDoEO-ahG'&4f\FW5N*ANjRZ ˮ:Af7yxVWҭRW쫞Y \ K#eÊ)3[ksZxB##HL0dr޺ݻ74 ik- kS? $I,466Qd͊l&Ji/z^xAd" A+c6F dB^O AUvqH$\,N$I"Hhf@ "ΐΤT;wihpsJv7p!6XF ac4tCb6H&I&r9__{3IeYƿ]ps.K/DΞKl.uHNCdҙj_U5$ic&} ?ն_ʗ[RDw:babd2E0traON'bZ Kl={SYYU(גZmQ;~wlF؈طwwcwK-")щ,}fe7@ $@fVш2-?|P99'P3D"j:32ess Q)(6jd##YY7]=v~ڻ\k@)Wۻih= ZQ)΅_`"KECDJȋ *4/"˝Bj|0^"j U/ QNksd'X.Jv8zt쁋zub^ ^^kAq={XN{y1޽>]=\;+j$2w!Iml:\;;$Ib8co>Tj`Xg *VP(d=5\x<}?>ү?3f,0{km  4[-_K6'o t҂p.+ :LU> o-ODV#JhmQj4-<X,J.yA0@$4Ei Ch$Ix<j: IB[ sv4A\?Q A!1R')5F-r#Iz{du,tǹt :*5U2?ݪ%ij>0Z!J]2!~>T y>?躀}I.MP]C^CtS؅ՒݍC,#֎ x wWacEM$c\u{Sg~Rg{[ε"{ucYZu\<}/v}z@e CGQ5,/.cWk,ǧçE\}_ۣZd-'5{FGYFӎF t^*_3udq Ъ~"!j] ;إuALi`8z;DRnx^ۘp%nC 9*dJȃk)m`B4'Wl TjDC>Dbaxs~! Nԯ ɚheG{ۡ_h{kۛgkw{쭏, ;t[bo@avo& #"vMj(266FR0 \p8#:rb@vÍ8zοݵ;ŵ볤RI[r?`nmZ;y/qA$BA`Ԕ: k $k{ճ*I V$C*JDWjWԹ""Sl,395,9rlbAHg~D&*fkՒ;ju.@,5(Fm WmjG$P'=rð~_wga&ZfAwk $I4-4aP*0¢w=c13/0HX~ϯ._(pꙧk;z^oX,j?ˢ(ɵk70lW8.|dw4ad;k?˱hCf0 Xt_2Y+Cɸ1lnkN+`Kp]C2M_p5 Yfaal6ˍKxy뭷$#YVWRV%ui4P\+R4^V[' PkTk ‘0Fɉ toB`zAUUr9FYYrJ_[s rqyׇ  *# ^ eG8c>-YA VM$|g{Ef{m/$Pj#ajZ6Xxg$tÉǙF"=DG(b)qkhQ١P,3 .y(3= >l2薃K5}+Jr[hNTrYdXZZP(r BUM]Vd{`70+lllVUJǎaeuvvLGfn~kkDX"͠ifh4>%ۍŧE"\6]8Ԕ>>=&󈝭=# :n`N晷0y>~~Y.KeR$RXd_x/؋9eN&r#b8'NV166VVf"t{ul|\Phh (L8HKiJY^Y#nKZѵG 7=.'|v!O<ϟ' RX~[`Kc E1%IF,qut]ETMUO[Ԅ3 弱j3ޱxlnn2=3CvxJB<o#-48|Z,@[23>x6٫LOl256CV< *;ە 6g=ȪxlgKw7xD"NR*X[,x<n޼TBQqjgg0 fE(dZr6-,#2Ǐ0 $IRJ&cc x$Qjfip3x^FGFK `||x0պԳOS(FGlKx,fgx֪ĢQ{mn*Rp cєN0b;nJS5f`rjBeI|')TE&!#:IDiDcFYW(N6)D#Qdj*^NH$ۜ$w@{!/h-`I)F{1Aw<{iyyH$xBWE~'{(Quqk-6hgffhZȊatd^{&H*iC!a/jhRlCWiȚAѤꌍ#7ȊJ8QIfVddEe9#E]M\ı߉RUUjh%n>@z!I!x!ݨXӍGb{p?L*uYcd\}N?{]ELf2\aجu}tScWfS(X7;ߎF({bWps7'O'Pè]6ˑ~:C!AرclllPVW[nlom)"",/7P5C\nҳv#ٱ{b+ G(M$Z2c 8QK*ԩS={SNEVw@4 Rtb[뫤G=ztzHP\ ni_?i Zy=8M<'Hi)4Z(sa.^}& [7oj7f?- $D0L¨s7Y^]Fmh:x$k׏!LT/DռN-@؃^)soEmvW v?vE1$ -*НC>/~+~rp?.Ĺ\3ȲL> r++ ;AtӠ@0 cm߂I.V[YAhD|!AQ ~xMX[[%'x'Onn;nEV;iUQm0 dYqmTե&RdG4 @Kբ^7? sϑm/YZZ 0<162v'cf$8:xдDpշI|KM;ac},wzo6qttALghVi6蚎5PU ]Se׉ƺxFw>Cw_\nɲBV^g_( ?gi #ܾ7n_k{ŵlre\_EnmA/'$Ns!}p 'ƛgz7E]΅>Wy߽yH8o~=LJ< pA 9B<4D*R!<\xBaVEfqq׮vA"^5muνSE| `)f6t&96-(mUAwm _T";2#Rʯ#k4u6 t*};t?suFgNhοRPQ5Bxzgy[:<&q{-9!L/s̡>q?Hu?l. ~/RZx(/"JF[sV?/YZ^֭9_k>^7j,/P,_0 w0 nޚl/zwt*?~CZoOTUJ2r%ce68;(llnR*_ s}qwVk_EQ(4Mn޼eK6JJy8t`{Js Kˀ31>Fcs!y8G$RЕzX=90d/vuQPpDu._~4MСC8W;$1* Xr@$M<" v ͖|] &-/ᗮ 6ן3;;k`Um;H [ JWˌORh404k׮D6l)ckyNW/r"8ܶ8RPjپ&ǏgvM~^/:)U"OA$Boh"AH N#7|8"# >d'SE~An|r UIҬ,14:Oԙ[Xr̩ﯻON?@'oZXQ|]S(I^^ɲAd~q,p}w}Q3ݺ/`>״Tz|xO?="SXY]$qt4M|r ϾZ{ (r`?#{ot||M?ùa$Il{32EnG; WTKK/ˡ'0 ??Bˆ>3Ouҧy#JtJ>~B0 ZW7xJxo`Xmp$}*0pe@ ɐdlЁ)$IbqqUU)@vx&##䶶A@kc-E%3&&Soo!O-ì<[v'O}^y~wwJ"ॗ^g?Nkb'T XZZUi։ .Bm骯{];Fõ&1ez{9ZxY]Is[[?jFGJܺcccl,22v4KhmxJ~fKY2Cmz%f78z8A[\to<7NpL(<}$IZi:ZEQ\Rzk{micS~70,,IL7/4Pad$kJlv4daqr?(Yn`*Cgx"39iwzA'zFqCT*U~$o9K'7ϲ]gÌdK&OKnaǎaeeCgXXXdiy (vo<(KAfh_6˄>h9's%EZ*6gy@lrC*;0 4 z8T7ʇ~H^ChF<0tbCW }4M<X Y'~_4pP*Jzag^^@DZ/&q/Eb8JxK>$"p@|4ˡ6ynPg,HD^wd86 ̀0J7SӴZ ,hԩTmoG ƹrmR z"jC*ӨP8cs]:_"&xVsi,OuAUU* wbqHRϓ~=4]' F7hf<4DIG~֭gyôZ-Ys9rgf($qC]uW L4MwEpF)oW >B %E]I,^s-VoNe) 5bggϑjבT@ (k>vEGE[m$<>ESjz%Dі@u#w^"F[-/_C56["&ZEUu*[ 3UŸKE&Bɣ zAnyl"ҋ`(p&* /J&*L9Y6Og~~P$P*ãc[ur xɢ<r#`tZ IDAT dYvB,f֛>K ^]YE@Pnu1?hm憀m<@NЖ[+H@57+󌎍1}H -u0.,F70b躁T*jubw a`o"8ZHD:qAk[&n]{-_(xYnkD_}ܐ8g(24ӧ3ͷ_~&j֨7vZWŋ5>wpu݅-Ko{h7u?=Pv/ƈb.>ыް7n~A#k{o7)K׼R,>E.EAVѨ0Ty1bQ:)!5gQ AK $XWI^|AJ M2͛S%v^uN:WXmUE$A G))o"zL5( km{ i4gEpdX)Z rl UYΩF$en^)ngL8膎st?h;`OG?{V+H$IaDr-C'd3se}x'K2xN~W0 FykP(ÇcH[sĢQ~LOM!+2|;+#pgo_;zRr ++b1nݚcqy  ](D)9z0>ϝ~j:c~U9̭y2CCe@:2jXYYML]YInթrGn #ڴboZ17Ae Vۮ! UkC;ȋ WA QpV;|*bDA>@esKԺwVk}ȺreɖaoC7LnNԴT-TM݆-w2]kaEo=Qw:owܲp`N&h?v^|Jht C^"F4&5ָ[ v@fh>0F#|8wm^wyͳ<'a!dYT*s'\Kb=rȔR'?wjxm'7μ5^o|;&lc|;7EV&'?N¡">M|p4z)ex<-~}FGGep(D0?t8~I_so2ss䷷Q*uh6裋_6`.w#ҋ߶G]tyG&3<%3/u-p^fDfL5 "C:uq X'tcZ'_ggCzT!5dT>k>m^#*-ɬJiK"9/6圏İBJ>rO<#wm?&g !b!i(M _P :BnxTbC-;Bh(?P}hH* !-FHA}'D$դ^ 0TZ1D$i懭d[[d$q;~q썛;zI8󖽩Z2vKh4}¡(q٧yI S77dh4bQK_ԈC707nyOg( 8q̵#N(]ٴt8f~~e>xťeQu9`OL;OBxh|- V:`z Vg#L:ax(j݈ree/W2~ǧ͘Ѵ: \{k/QXy+I0Ӑ<W2*2:ѡѡ:mC}'D&nITQ>JO<[vO|J8櫓afwI( w;Jg׋W;{k 8*rK"[C4m!׽$F /ɑ:rKr\m ׼$GCzLScMtLMZ5CMUͪDzEq>4٢YL4*IFUt DfL4:]"2<զHJy~;i,VF5eiu>:VzLftj%}y^ XDktgyt^!@@Ṛ<$W]ff; .~C^'?%^x4ZD<3H&AǏ}h:xs-"#Y~?<VHX4J&!+zx)JHOs?&koFt$I'?Cv6xH![2;C98~aB(@\!N7ҋ^?</oy.|tё&Ǹq[[9LK/7DQ ?m+˙VfT}KH_@Kg$ГwǓا7w\^M?TfJCuJ[A䆇ؐBm'@4mDMٙͪvCm'@PCe/dF1@$ezdXTx֖U M0DTrm]ZU; ?3p찶uZvvj乬m6:{ru [Og࠶e<ܭ̝!wo>n}klmmft+n3c}CXdltĮS,].;> >L]_?k~>>v!66m7F񸩱xfllÇ4GjD-fz=ry>LjFE}> 4-U)p($~700`.V=![Q@ ˠilc}ix7+^@ @l( }R-)l(f*P< m;ziM)A@4jaԋAf׏3>j0p &GsŻ#`8-nb%}^w^xԧn;AX,j[FEt]GuWxp;PJH w>#pBV'x NݶA  #abc ΓΡKMo6c2HR8r>Бn:យgI.nk搼8hi]l3O=ڲ6C9:"55yH]nY}kb%isg!Qi0,ܼ.hG(J$/?[u2n`^@DWg;n>v$Pn:)!JvQwv;]n*kO R{'Ae9E6052.QHOH";v=;\rROh4<.-r9ξ?}*ZjΐfaqRģ<[sc0.^ں),70/u<=(9&'M70(7X\\懯E1cCA D ;OQTY][cb|A giycGp>+D`>HzÎ `waj`'szݛS8S{&Xw!uB7zﱣ F9/_@sm:ɯ#pd?I4{T^U;uv3`h}d&m=&7v:M Cp5ݼВu[ $)[qA[XdccM)JZ^J~/~HUNd#I Sg0L مeFGDB gz~6M`n/ys2==vXX\"kO?et4sX*Q*>o) wo^ch(M$ qUx;cx?ɏ!鱹iyt&ziZ{Ȋu8Ɓ)~_K/f;2.hUEV/_R|!wN~R`P4C![$23gai> be=kW_;(bqP7»'L=7A9`{G nx]ǒ6J# v:g* F_K<Gd{DQ;޾q:bqkNC4],տw-s=vaw׏vMTBtDQ n1@4MfPT C:8躎(JWuߴT1(8r0=>SvѨid3N?|%;# ? >1}U+֭yBc#uyatMGQE!J !})J212sd\e8! o/u QGm> );$'NGeZ͖ +C EM`nuG;#~n5箥OsJ$Y?3q]qKv BAs!-ciYJTpTY疐 60J RU=Vgx5@bTo_F׻/gAQMb{ =g{ֻ,7c$@wz*-Sb}j=sU1T/At x|*zjۢEwK#4IERiW E Qw;ٵ vǾy/.8CMYvj:{4'Љrwt>lp_B\TUWVTȲZFƦkgg|>&ER! 4M2 &Z\>OT8oKu4!kJSDn}UlDt:M^޸v¢m 4<Y}xv,y5llnUj>UUV*x<;d:6###yjEQa |>tV,Jnd>D"0)uBh_N .WV /(w[w:{I\_훉Sej=%pBZR7:^zaCqOkcW=uVGInrF%645}V+;Jk6c^3 YV\N/_ؑ#d78~[s g2y55N?/|cGi /]fm}0ٷyS\ x/ ?;ESdPs{ﳱxёh_+c(m:rΜ}uʕ ϝ~zQ:!KR?aP`v&d^VK%TMe$ &'%5~r>摇Or}p>Ӓ;z>""ap-ǐD5]塓rpfW.(Ib||ZN<c]  Q?e2 3 VrncyҩJM6" <7b^zAT ts0nɠ)(MtKς;̌mpիWw˹s$T#sl9rZKCPR/%$*TnD2;W;V.208accs;lcdjlIY?DȣQo{`ttA IDAT~Y9wryP$>!c_>|7^f\bXT }}=n> j>7QzvM kOjȲ^S_~s~+ ^|xH$ | >棋F#LOӟkZ;;E^ϝ"S*fI&;nb$I>RokǸ|[~oADN?`G`y7n170a *ޝ}M|~?xt;V' 3k\tx,Gww}F ;˹بh J <$>f4?nCXdp!:$J9rSN;$R8rBMBXR\Byq|Z(3Irpk%6dQ6 r#-3Dn/ɢcu<>x< *ˮ J4e}c/ȁ C#Е;P2$1Ő0fP,&71%x$I701>k4MRKlrA F(W*%M5'ƛ^̨7y-aV!b(TT*V.GRVJ"W_;#'EMÏp8dR-=r I?O@t3JHzY g)#533 rt]dY\pa.DQCS٠@ A v?LT`qGh 7)f̯uXv$#G0332hGٮ@׍ aL%Q-"rIH)P4 qźLVG, xK+=vF~N0v]gM3'"r_b!@\%Q*Y7RI%JŮvT}?3gzvOۮv]."} X% 5#P(«CEč{o{#k:8377gX(PaztW_yɚi֡:[H$t۟'$p,878ȼ5!"M0%$Y8I䥪 g~^@G3C"KK5e+W^!%`\ZjόI+Q9} ("ׯ_7bv.^##w+euU"KK!NIVeEؤd>& Yԋ#sG}&uuu1crUUٵs'obї6e2Vc#j[\*b̑mne+|PXZZS\99Wq:uk܌$uMD~}~$Ik&ܩ(#|)`mTZgޜq G4BZ|r98  G8uTfՙͷҮȵrCB̯000ի#&N .P%TM=oul*pM}Mg+w \8A4^u4MN82ngy~Q&=C[Z0%U ,:ܤ|v=< FrIݭu}|fֹ~7ߴ̨:]a嵶Hc~mu)A3M$LNNd5Q.^@Q:;;\Z .ethH5Ѯఉ+髓.8溕KE4 (P{_Uj|~A.^hc^swK8FY^̸ cWp ׹58ZQ9'~~[P`vVD4y\&6@ ;v`q1COO˷ox@ RN&'س{W'?bbb];w0=3Sٹc&3cX>HG{00 0=3(MM,.-Q+E&&ydǤR 3C!z|*}}HSS7ilL(*Xtzy=\>#q0=HezzrL{{۬t="gk_/Sn=rEc9{x+Wikkƙq<|oϠju"WuM$GF,֘/АLɰg.'?L6crrޞZGUU,r>_R((ay_SK$Slݶ 0+%SVtBJى3nFuOM?[YYᡇbfvRLXV1B8K64mhXbSh$ F0S Szmqt7QG)B&AUj8 i$IDt89|8x3gϑ/H&LNNq&&&/2gϝx< ёos\*sE.^2BUwxXy7  Iyy# VS3Os1-DUUv6 _#/<1o4"W*=X,/]&j`:oػgNGx܊1:vk׮syS)vI(l6GT"Ӆ3|?:;]injs/p9@@4Bm7vgҋD#emsD"J2G}Lw*r&8kgR3۾DRAVRĹѿ܍pIT._a!g~aϣ:W\edl|5K9=?7`qqk׮YXw2g>_s_.*EFX,299$SSS6O8¹JA(f9Bp8L<QDii%%n,2˗'Rȥ"Bd'fl#(eEgsף33;˅ \}םs囚(:AuR6Д073C4لZ\aJtQKtg{Nfx]^ʧ~qN<={8x wf<455j jާBA R)&iIŘ9E<ՊZ1WWh= |6 Ԓk/B---N,ɣҍmh(Anޜ&S.,gqbC_$$I>|>Dy#,SX$qYDIB 81oΡ!W{87oNSTf:֒(ϻlp[z%326OOYm+2 mNKK3toЮBXZlF>:zgβ5V>CkK3[29e9t!x|4|aaF;H O~\UIҕFu]ZuhaWiVEGyhn(̙hup;H$,M ۷RXR*+(2mȪ"MddWVknl\K_f4M3hkp9sUGF,G.%KE‘(+,*" IEp9sN2Z91<2nu2D%.A,G.Ēy"KHSctxXdznP{M%p|jwWɷwY+Ug8?1BkmuuO("|>$R(H$_9* #cD"8'B81bp(D0,˨$ =%S(BAB|R! g-P^,T* ) G p؈*bDr7M6P\80.r8w>߶RPdW ̋>WsS9蟓yԦU3.~777&9[tz,bWoO %qZZZinnxg~P??^fLcQ t>g;܅}"8_bYlz 9Ȝ-..255jdYѨ#,/Zpps(jue[s4XX1q=g_Μ=t5yjԏJu oiIys) C1ND>gbҘNfsLLNK$!jGdVt>:gijj$ I|d݌ LАL2:v=wݓso8 o&Mw\j9g 0L6ys@p]ĬZuꁱoC7]0,rg^{9κJYBKyɟ2'[IokȝUwV=N3x 뇳Nk]q4oIw~]|ˊK(@ | rQׯre?E3捘Dq -$ vs>͛o\ "g7 +G0Dkk /^ex<ƇG8t1>$jz?bǫ"9KiGBlb]eLӝE(_tI vη?T{e;og;t1VV8is^:NH |.Is\6jۻFj<B8 ~B#_RC" IDAT?G(edY>:дGZwD"A{{;w 0b,W&q&&' 4CE`qi'XgXCۉErٮ6~{?(`3HvM( $qk|VN#sE!H$6e{os4.^MNN!W*23;KC9@0djUy}t̸/0 " 299E"͛D"V2>2J[k ):;;;v n ST07?r@g>׹/ w{ɵtſhOw!/xǭ167UU4c.\8OٱG,fY.W^p}ޚ0%OP,AdqQ)Hx,f 2F D+G  F4`0HR! EP(P(bQdYgH,ET* `̕:},?Wg EnDRallp8Dgg+\@P$ X?싂묬rEOX䓓Je4]#2kihԫ<I"Xc "Rp(D6 W*W0bxO5}WcX,EDVWWΦL֏[E0>D ;Jd*eLp8Dn.k\ʜm(<碮toB!#}!v mm$N! cG 싂 44$9tX4\J5a7%=[3 ? .絼5:x/!6sjT/V=/ΪۑTo,=Vwk +Ī)R2>ʚ:{8?uabFtre`98Du}Tmvuuy1X$ uɫn34\Y_yni8! ""H+XM|>l>g_=܍$=CW4l7q3ͳI.Lpp=j|(W9<#{k[k5sv+~ZUq4A Ww Vo7bWe@ qNnykVͲkȜM3U;,zɒL=1yCecD[sRj-155+EK\dP$)h\Q**W{>k&{&"36Y ~ʸn!hd՛-/dG`nmŴe6 K02T@KlBrDAMV< rg$5y򤹫!ڤKLX cX*QvӰ yՌ6׭cƾ5θ3K<5 &Osڶ֪kEgvb'Ii{D9MJ64MCQja`; Ll r+ysT EQ8~$[?O-׵'a`@`fvM8QZ,.T "ܜfh gf1>^._!mW20ϩgBl+ lc9\*11u8L0=3C:=}{2??(s8>~ o.?4J=W* [cu4d#c\ữ\QX9qKϏ7 8yX'uRd5m[ C|p@YP,PUͥ452՘Z`?e" jƃ%׏SV*P"2+oycmƧ uk~MME7puu+b7k:۷[&O?!˭ w9=QKfb]wy"9^U7<&Zc_dY֧^n乕Ew[$/.$5fb\=6u>dɫ |nBF 1CƾNjopfHχZgcMBAӂ/3e֪*ґB:&i}* &gIܶFxrcjP0@hnm#]$O[g%_t4d%{jͪR@E hPqXvig D۔Wc֬ݢius[F&kԇO= oi>I7:[pT*-#I"hMlbD}3@Bq{Ѿ-e,Wݱ1yԩ96FEEayh:h81 [BN1,tobdjF8-}J^SqpP4ڞ[*?kP*jEt([kǁsv5Zt]3E|+kQe|̺mjκ$͏@x~i>=\:ohw߮5bRDP} ssA4Mcn.M6s_񪪒*\`ig|W7!BuN`'RZꞓy&PB:%?nsm^q>] lB. JnNN܌in3ymS36Ks=scJ)Bvk0:{bS['u+k󸈒:$G:2O z WM}}YS)\Sjdl,VC?d9VEw>gOgUkޜF4n3Bvr={~?}pܿW QUv"Uǝ2iouDQ<,'ޫafv2ͥ:LqW-zpYff4Ç;ܳOsS$ B sz{$\:{04TuM|63pp f%CJPB۬m6lB{{;oRcqsT*2 Lq*h%LuK"k:TDTYlԺnz{饗XYYԩSznhu y<+++A9k?jdM ׻JlNY?gݮ+J0VDU5h/ ̧ew+䧨ʩgcεreΝHSSK,.U=CWVj(VpRܺjEtKsJЩȣ2|*b,kT_i"*rljqx=$y̬1"s%Ibee?_7 rY\<ɉ5<:?4I* "z{^cMV_[!ښD{nruձ !ۄK<~jtzX]]R1p i5?PBW~a`21N0Dt::کTlbt lX4ʖ-]91+A,Ր[vmU&] z=8PD<\ruQ`lp&$٬捷8qSxT2I"enӘjd||];w|ɽpRdFEin? ̔O V|Vˬ.&z`5Pk,W".^dhh'۷Jb?;y$?ϭϰs^ss K~,aIVU?IڡKsn8AH8bs^xYAp17qpP(hF Ik* q>ZSEXILv54_WU+__D(ˤR)mƍF[ /g]4766$t<0I#XDL?8.HD!tA} KK{j!Gњxk(ตCYpt$2::ʞ={PI1667~Ǐܷ@0Lo_s3ӄc G#@gB1Vr+46N/J4bv6򒿊sw?(zqs[?mn_q=5IZcW ޷@@" #D ة Zr?/MlvAEWM:6;{0|` @jkMeobh"`(@7Y0>t?H=zAp] A\ۋdK%`~n008T*ȊFWG+RW?u b@"/n\k"Y.aB@@ h 3gx^, [ok:Qa%0dv(2+lk$%Pgjcq1KG[\b1_h)tb!51[؈1U:LD[k@bX|9HN4MCE}NLVĢQffgߺEQ1ξcfvH~s>M.VVWinjZw/ s4?M119OO{N_Ķ~ʥ2--d2-a\t}}yp͡*6>cU]׹x2LOϐL$cuW)TD<Ttz}WD@6Ы$IǜhG$k2#(ٟ###$Ir++C[[TCC?B4UO_je+X+AU8wE r"&Wa6wĠA"$Q*0MÆ3==mk׮ܬNG׍6U\YEױe֭u jJ s|?AT*1;;d2IN00a`ctwoa ,̙sto颭CQ*o[n7:7!9sUU9}T9J0ghƙgb=wKpE~e7+O^N]4Ѐ\GGAQtJ5 ehv|^|y~ kMl \_dzPbk*%➥AAy'UP~3޽{-r$2Gcr|&-N]!)Rr]MCp,N+v>wjg,`N2TT8~رcq7Duc5^x<{JM$ї *J֪Cu^ҷuR r=.~uzɪ|2>b>~twoA /b@ٱsUrBl%I"='/ LT΍ v!2Zhd2H(DuE!C /%8[obwT?ɏ,OM|qH-]B!38td$jؒMǁX,ry s)ZZZ|M xvuɏMTU ͳk n\?_2 !T0jk>s߼?L9wѭYKB<Kv{ktw So>3LS{d2AKKh3 8yFsHAPpXz'=TR\XhbjjfVr2Gnm~ay5 8Z[CiO굝*h?/s]Q"z|t~ YEp9< p:ɜQt=:=3޷lvq#*Hh>EQbN$VQ* x&6+~hd2×ֹViCsy5礊k?bȆ H#oV_omVJUp $YEQ]gv~tWr&MٜZU5uJKJ0kt۰h]ۡJnELjMǷdvu[e[np6-Ĵ^f9>1Ol&IƺBIIӴ166i0=}UUQ㟪֏/62;eѸ:|fZ[Z\N9xRJnJ U*|4֒jyàmRPG UUiki;(۔eG쓶yաYG4tttMG5\ڞ5Fexu-@R]36uM"}uj91הd&ikM<[k~d8Q*՜pd2N3;7ǁu;[pJT"T8stuv ~׾4RQ:}ٹ4CF ti+,΅ ؾ}3$qFFǸ:||5k S)w?j { O>j(9ƿ;}( eT#i]s4cΥyptwQT sE2WUrUxADo<~Kggy.XZ^X׿A$:LUQ `vnCI9,d2.^k tttP* |t>@|2w=CL`8ZLMs>ocj H /"+R&5vn|cխSs$CKbB!HI\am+ ~ +:șE=Ts4w<^¦JJswCms St߆&"[4B|~BV %8b 1~r9"Rs,MMCa2<(ʷ? $O{NVVWT5wW3g@6B:!P*ttZtR;)%z\+TDt13+AJj<^Ϭǹ<λ˺զY9-DBdn|54ic1_sߧNy:{.|*hFqc|zbl:Mne\h4BoO7 @oO!,/g^竂dw{l6˞]x_ݻv23;˹jLvr&&&~:Ǡ:;;\kx1vډ ,d9z,,d pETU%<`̯L}sQ@ oxΎZYY]ea!k,,druCFhΎNٻ{7mmmt380o^@0`9m`c](yͷiH&\c4 s533HD:=O*!dϞݴ67A[k+X?#z:󻉍CXZlZiaapⓓ8Qm΍SO}m: Ns#j 6\ SǪ>X348RVW2ڷ@z"\S]]ҝyY28)5jS]޷r;!a{kD^]>ۀ⸁i>Xo_~J37KӜ={ l$H$k߳.---ZFX"w|xJe A $EAUU$)P=Vu:UJ%Μ=ǁ!2'NGQM H$I\ hi Z[k Ks}d56ƼL؍,/giiifV=fw{=@!U`}Uj9MӐe5 VyH$BY.i1d @EU,˄a_ݱVUIDI=J\& ϔ( T*+(A5V\?k_}qΝCrsf&9/Y$j!F~&`ǾKsYj]>!&钂9tj{\toͿ:;F\@NwylkcW٩Zt2IwN'=L变9}:}22TJmI*Y]eI\D$ @,w.pqDmݷ}6fHf3V]&97o!AQT #y1Z PUW/ %"ۤt+v/N[C,Uъ~ޘ4`4 ۝# , T@|SK(4R!<ѰƖ0-,eY8QUUY.sLmSNW^.Cn>Z,J ZQYHb1ވ:ywwW.XTQ$bA\~4$`ZV]X]Mx0{).*fSeC-蔥"uhrYC"!}yE;Ybω77MAMj&WP6'SE$w S$@/jitfa`/'l㚼uhnk/LJS5ڲk J `yy%G,ݶq8l8(b@(pޭvea˜Fh2L񶘧( ?7Oa+ZyX-{+fjk~\hZ=Yr[ӌ4-7-T ijɶ'L9N-.'M:G6}HN4f3Fֺ6oDig't\)rN|.`!3cnG~$G[ q-ǖ 4m`Q5jMadbӴA;4~iw\ϙQqo&1ۘy}f/Z8aNxp]@[CXLP1kŠˍߐAƯ;ᝅn\VTTֆD"Q"ɝ͘_s40CBD". Y=hin?1=40/e^8{' \Ei40>>H&sƠ@`h*=44~407n'X ))}zQUfE!8?O d=9|ɧAG[k+X EQ8so40i!AQ340#qfffAr8x`LAS|yá+tuu"ZD'&c=ܱ!ba(7Qed!WKvopZZ5BF&`pz-FMW4,|` xZ3t.}.a5yBhNEۑIEhxZ_AEm`=$"]6i.2?kʐi]Ѱ"J/Hcl1EHdF$Ihn#׷n[ 40x^pb_{ٹc;_Ύޚ<*XX4h ^W_ Ņ/R&^^/$qtvv(?0`ګ|p5MsKvn˙ nܸI{k+`oP(h1q\ i`2 \clnpaw026i/A˜Jn'bc(̬>gs<'ڜ[k__\ :qx^Ϭ--H$VVem|2᱘A SGV4 EY̓2A]䁞y02e“Mi9n1]dz2}Ev]c>jD4.h`ޝ%gN0ke7YnLsi-oYGEY^l|y1K&h74l/`YjFl5yr`! L &%᫫t޴֩͆,˄#~4w/޵ٹ9PSSdmL]eiV:hll O 40䗹40 Cèb(dH  :c1[xFD#:ؘA326jnR456̞ݻhNoOo.V(s[)WVW{TUURUY5+GǮF -zikk%L25\Gy6\VGݥ߷.w4YJޅ`t:5N'_5!!MbӘS2+;uۣy402ròoX|:}1GN@e[ٛ.h`2Z~=6hV$2OÕNXR_FlzH"1%,2ΟŢ,/X,D"7&۝{˧N A!(KEMӌGL&<0>$I\=QX,Hj5^䏁.q>oSGQEUQa|uuڹ#"XQ,죦.З9UU$Y Zڅ$Il6nA*f f3[ $6{6H35= @G{;TʰU r-{҂ t: w6kkB;͌Wg<2El6* t~Xȫ.YWGAb+;S7dwsd7"e0w|WV(~3p+>u/cseVmuM%?PbU@WW[|9g@53 YYZ%/ר$u)^L⤹5ۑ2Q R,9Y[˦+}+ JiY-+CmqqWoWVV /(M/9Lk 8oha%00*k2g΄RXom,kASU߽ܯ(N^>>iѴ~P62zGu? mxVDY6!glqjaιzYVKrdX1Y)ڜ-Q(iL,Λ`@+ufrڬHl8fE7z jkz7LMMuiTVV"Ic9Ȭr9Odbb}{p9 tw]tQٱ+twu!/ 2?ٷwb>tUQzmjZZsA_caaٹ98u8/?ςⷿeoAcGO?yΝ'7d\ dY[_CM?[+>xC405o{qƖCHGGwtlk֐t{ &Q~&xܠkNYƘ  g_ H0>1ɱϱ  xIjj DnCy5jVYM=\0ږ)gtl_NSʨ}scEK2ܴ~"pvNX,1?(@\}fYnlUbSSLOO -R5Jy)e`ՑQW@_KUU%U,//_g p:\tarffk; 7DZHD(f80iWPIXZ^c.`%AJI9`homK{{+qҔ)`˞ݻxw?=vz\6B"`{_/_\Orўݻvlok8$I`X/7/9r8c1 ~Isklh‰륺{v}~Ƙzih'_&{zqsppx@0Ȟ]; smdkzFF62J6]*2 юL2lͷYӢԼ*y3 ̵Q<dIPT3e[en5<40}Rz}r8n, hFCCaVVV^Q9v,%-AEEPȠX,(eJh(Ba,rF{QU44~}ic .|*bSUհPZJ(//W_C4dEf"+ h%Ma^bZ$ (b1՗Ill4+/}yge _^gBQUв.2>sjiXD_I"薽 "۪i:uiX҂yZ9Ft~G~]tTU5ƌn%.8ɤOec,u$hDTcme{5TE}k!3NBͷ~%R6FVX˜ ̖GFFXL"UT*TQ<|H&rhFcc#@f~?LLLUvbzzEQfxxx<^%4MQԊw7`כ@<?;W]{XƃGyc7b0PyNGbF\} NM5T9>xk {:y԰^E BZX,^ׯ_رcK?Px<ᠪʨ۬!xR7x ͹hh o(ud7E0ȗLMMdX4026 +?< eᢼ~7e7|%6'@UQ4 ~_9>}:+wN;G^:~bzCh9ND^o}xX_2$i:;$I,,,Ӗdee˗/o8Ol$ILOoSS@kڽYUDž3?c&b[+?&y1t6p :8oFQT"/GWwnso/e<0L"TH,D,dd<*(*`)T2ex#P%KA1 ^~Uc1jsvSȥ 6^8st62s_gS`HUNXMAym)lĢ٬Yzp8p~:Obb1N:ř3g ^f'o1η_gwHj~+Obzώ.Ɔ/vysE/vNTUeuud |jZieM_pVffg"H07`\oxz L,;ZDdZ[?8{zӧFd*=f~55:0>%k#,-/92r! G$>lX9u8 02:ƾ{k"-LO׷P("`zfƘY>/yioocee_Nbm4S**#c>t@woM9o }LxǗ^8A4^9}ʸb>x}wڏ$/ҍW/sVxٿOvK60!rZ6:R&dt^Wh R0+̖M2\%9ۉD"nRD>Ν4M_=KuS?W;{&t__#%Z~ŢËh4Uٸ/ymg}340W*FǸ2|Jn/Pˡ" "+ Ȳ#AWNF pv<];w{[Se{6Żzkؿo>go]]W&rw >``h`!2j<2~`p(m,$s,?eii>ϥ|x1{vb[o/--;w^./ UMO>f1 bETU?cGK]]-_|ko|PlC!cJxg|uȲ̇K̴?ٟ\/{qΝۙQUUY3UMCd2 U}2Y^ EQdd",K8T5+q)=9"I&*imJubP5Sc@82 bt:Q8 X 555|>oŋ i02 455_,׋,LNNr '9::JSS?] |tuuM_l]G2;,aw'p\TU<mMNXcC:jhoC>O`0 $!  _#ꤺz&ofvOoטY^{-ʹR100?n1vI 1FG9ebuu7u}2ނf׭: ߷1j9y%V+S30H2xeݻvgSS#.^" ygho˙6»*^t**$ ۙŬO*bq1KyIZfS'LO_}z{q>' ߋ5}:*++QY x9glҏh-V%pRUggI&Fue=TWUh 6fg8d~|xoh}FˌS=зWWס s"9/zVn۠g)>n޼i;vO?UU9z(TuA] LOO*ܺu ݎ+=l-x<|-xQk_~ OJJѻ9D+کNŕ\>??+CW -Ecj4PHw/40VP7dDQDXn 2(kL!(rIf 46?8S' ʣ|퍯rv ,ކ$\3O)k@]z3 eA0:5g>E.\_y` QecCUU,țo04M3gFxg>Cb3n.3Lj2ߥV~WN |F %6'nXhϟ?ox^DQ֭K 9f'Eꯌߥ>7=dNإ/>-Y\%8i+ye|po2Lߣ{h'NٛTWR[ipeU}b\IUEU$YB%URxIH2"!)2'ȊQSS an(V̖N`aq8="Rzp\tuuEww7 \+WHe<90;Y_I~AwWOWCC%R&-TVhiV>kȒfGMEZUͰP5Mg_++Qu(|LV.^W-('?Oe< fW瞥<Ÿ~&:mI ?|7N';wlr ;wqeDQd\>BxYN xH&b:UAmmBTVS~XDX ~8)]]=,.."25ܺu8 x].7JHIejkfcm&̱ؠw2Ltvt܄߯w=r~~M9@q:2L]m-6^yB%}EQH4"r(\9nq+#,v_;w,-S QɆM4Aw✶UUR) EV "!`u:9,". EӰIQF>bUٶ6]+ى(܌b/(p׬3`)EUUUZhll4k9Mj>KRWWn+)@mmia6۶YM|"qG&r.xR!"٨v(J\Q/- -ogQݎ`Ÿݎ*+؜N*iA0ؐVÎ˥Z pXXYVrCHVp+DgۋoX@ "`A49$I IPUU\kk+(zݏ_eQ;oi*UDDI?묬tVx*F#BUUanOssS"@F#v2==Ecc#SSTUU( -izht]J4!1iZhjzEUc//aa9J0#Jp,E$3^f*\@x 5UTToNp_|qQ&&n21q|yAА$vHdSS3,/GpYNH$E#nSS3[\,zfq+p+4>Წr9q86_/~aq1D+,@G>og#o\FbXx|ݾ-/.# *XT7DRV,,FVvT9MjPW[˱fq)Lxixrz{ 1offfikket:;1=3۝VPh斜%VWWiiiauup8Le%$^Jr[&2b$j+d$%I,.TCjoUH$$)R@+Bj`Z[EkP?PYYnGUq8$ 1iG?Egݽ]eFFǸ62nkoN2b?b*Et:ڬ,4PT@m"shF$) ew<׈rxz?u)WV۶bp /}>py<ǫ.Ѐݽ+uamvy sq ?0 ߡ|8TЀ(TVV25u={vs֭t>}oCCWE D Y*➞.*+`0cׯs ^Ejjt208Duu5 m6'&q8DѴ %`x`hh_`<;O}I {vfvvJ]]-55մ2??fcjjS'䏹zmƆBMCC= ?JnX&zk}{ ]PFe/N"WD\t'aꨨ(*ǔ'MMMhFSS6HPSSaq1,+$q7D!,/PWWHh8QqZ$@#(X"c(XK*PlrsOr,o/Kqnwqh1h).3fl9U·Qy]\59}=== ?<|:DQp01q$q<KK$)>̷뷌%M򶟳adUEH_}8旝y5ם\9{_BrUUhޜeVe^iaQG O|Dќ` fs-"c֟t-xOxd`(G {  hozL#_`\kǦcrh Сwu`S4_P4 (b:D?^Ǎ7 FSSSl߾Ph6iD"Acc#XTJquE2^o MMM 3y/ߔ= JoZ~xPز T)TVzmY{=XQ܁,PTUKBqjkM%nܸIww7ccctc4-'bu5A}}Wٵk' 099ISS!ϭ]v -RWGOO7oߢp~?IoC:e>D05Zq*4n oep }}x*}==I27{>9L<jQbΝ;zw2ܗ7Tǃaee`0ciie)2<6{Yx9RAnL&ihh`bbfH&ЕA?6h4JGG;444pƃ9633C]]-nݦ2mmmF{Dй|'LLLSdvY0 LL9il-i[YcYFDQxړԦQFeQ0cUdpin+-w@QIɈsdR2m582VWƵa342{>r񻿫QFeqϰVn`wH32iV2]UEW$ Ax齻K$A_$P /k=so~ ? !@ xkPt@ P$e6?nX,^mYu\vsڷ*LIcPtru/++T*CT_055Mkh40 {V&H(oJHjֽ^Z-`NG0b%PTT$oF'dY JI| ~?lVOښȺ:_|u R[[ ~?6}$S);X+רkp~?FiP(*GPE@ x_y;LNMa`N:/;,-`48tSrvr$h4#))-]O狀 C6X@b4$47àP΢@bl.c}}D"BD|~vSnJh47 ]tasUֽ^TUU70LpUG(+-c)w:{Ǐw><2 @ w/9kms@ p8V) ())3DD"8H$tO<؎Ñ9DžK$|gNk@.p&A$.]Jw؈Jbhx @&jRp:LLLqI; 4GfxtF S K(q}}uh,N?st12:$J""/d2=}iݳF@@ ~d2n1<ʴHII1k ~?DN'e2j5.W6@hϿ]Flx^|>e2b(zz"r`_+UGFp+n7sssTUUa1n_{:$&,3#Qt̊ $"WVHL,޻pt1>1h*guC ĽJmM5zzpjY^YOb]@ p^E+g#lL @1T*vu/Nn7LR)/whdumm;G@<|~\TUVw>^/Ο~ʝNz=\X,fWXשpX_L%inNgjRRR^Wڮ!vwVhnj$Qd2vb*\."~ra115=C˞e~7@ ] 55#`Qqj JE,mR\0Oz=xIH$nIHIq1Jl6J"͒$9Q^YjT*IӨ'LLNP(78s$LR$PR  L&JD$d2T*S]SFFFD46bP藘?\%P*dY m6 갹m s=?}={ oن$IubF:^4(gR*m619EC}lR>;O#<[ I*Y^^ܻ?DThou6HgsgNsUΜ>7pM*+Xhjb#K8fAN.]dz $ػ)߸ɉc뙝n{d3l6Y)˃TWU24<'4ǎxs(J|u:F)++et5'Ij22:Ɗۍbfqq̛%sAt (-)`1ZΑ2;;dppښ Z5Gާ`Ny {jG߽LLNb2pdH$z<̲Gفdno~$I"뷴Y /b6VY\\V#Nv3[^ Ϝ>EkK U\vݝ\8tJtR}:@kK .64Z-ZCR٬T\~K(*G,--s`>:Q*Ut0B:;,,,RFΟ=Þ&,_~]˅ޠ/pkyut:4 u %TUUp>j5dzzՂjEձ=xh4J8Fӡ ʶY:x**P(D}]]K0.izkkj*8~*Ƴ3Λqs`>T*5WRY_M>" r9kdYjkk Z <#GS p8,NMM#N1$?=T* 5gfAS vc**=b0wcC<5'g63%rçijjdffNYY)~VMM݄ajkjiK^ѱPnGz^.?iBuuLLNQS]inzly+ !ʨp1<2$IױD0յ56++L&p:|fɳ gmU5_'B# @xcd`FuUt7`"Dp8bn(..".i@UI@=:P(XM< -5xK t&M:*x,j!LQr3N'T ;>#B(VNWA ΓɤeRR\LIq1LDd@ <'$a0|b.--Ѩt6s(j5DQDilV+$ Q(rx\ܛh0PRRB*FNG jERQVV륨L&SP`4٦@7@ D"*@ +XT*IRTWWvfJKKxJYJx[3LP[SM4UښH$Wu:@ky7Aoe%3pMI/hebT R'L (w:P*lVj5N]o LT*ŗ_]3 `Y|m6R,N F~Pehx!h߈Hdh52 QwI|PV_hV۽JMu%%%( LFI`0^]d| DZcG@{& bicpxшJ 1"@G{kk˝餹޾{d2Y~7~}Dijl_RQQᒿo]Td*ض|cB#^@76S8v@h4FYi)hNlG2v7vWϞ=~Wz'(*2ɆZKKsP*IR5RX $r7Ꙝ}'QZZj6+FY"M;\sL&``ow++n]8NWyE󔖔,BAbb WVdQgG;*BA{[ @oVuCP(8t,0^^~a;D&axdP(LuU%UGP~@ xEl1>TBfZFvB񝞊#rIx|m-) %Ib6뾣>"̏ y,p~n&HP(K^]v۷{J4' imLj5\x Pfih󕞃0>LS^|fCyp8B"@xvnFG#I?֎I3Y"S/J LRR\L03KI&F}jGxZZ_Xdݻ΅ TgqUloRH$ʊI

2hZ2,W~M:sgDRر#LLNTPo'NW12@N52:{<_^}=`h( $._FQ ^O}]-@__{SRRLqqf5$^GFp14<yYGG젻cgLY^Y)@(-)yfpݖN^ B\Fh4b6s%#)om-y~~uxQ9D"onERq\NcC|7 9wBA!.~FCI /puN< 9q("G 儾gff-9@Q頱޾>2,BɃqJJrn-@g<wZdzN6em̓Zfnn k__14<`;\ݏnӧϩno.* p8ž=/m uUOk;zi&#Ϟ2 ?,..F)2=>WI4 ??&r"Ξh40@$/=smjslzxSyQT;tT*ٿNsI{J |pWE׳I2;m z}K(FVX$Z n*:yT:EC}=#J?;}.Xť% k Z,9{/q322JUU%uLLL )**BK^$+(J<ydfvVmLв7.ZϽ! dϞFZ??eddDD"g?rQZ4TDBI$tZ^G$$I"js9l3t:c<$?VZR"jlV NgHz$ZV'/3MTJz=td"FiYY)NTeH(__N X6:0 p^N837 `Zsx}> CKnnql6+dz Hڒ'M$8~DQ=Zbts rIpA&g=MdY;UU0_H4½~NRt:w+ 0.sܻ׏񣌍34J>(naۤe&o۲2N'{vTv-*jK?on[ݾ/,HQQOxaFH˅' [ܙLFޮ N}ѳ(;tfXYq'aY^Y!NcZ 2E&s([^Y=aGHo= 13 ´pop47kC~O?$ yh4J",gϞ *[AJ3j)|>?'(>m;OYY| CCCrV܉ ^)._SϽ}8;;X[ǟ+UӸWtuv^Ygtcx<똊6mAPbF}>Zag/,jL}}CҌfcqyEN4SODS'$IB߽HH ?0ᠽ-#}CdL&RSt*[i=!_G_G3^y0VK<GRq uX4j5t)EGXzBh۴Rʥw,`~hN(t:^Ƅ^諥͊F!HHFFFaL22:FiI1Ͽ$Z_p{qddxCRI*BRJ1 w;b]x}>tZsehjSuqq1X JVE!Y`2|xdqE@t&APH$1rjB]AFFp:$I4-wP_a:LFt砷zYzp~Z;whcdt X,'s]WUn<\4l'ɂ֏?߰oo7z^].RQTnc|r Rbfu̓Z*s5==ݬyɤٻC-&4Z E&_E;45U'd9 /_e!P^/>D"995M.AsDc;6;9`RɇHsc#1{ <7#aarjZ(HS464tv%cɧsn/gN^bјTQ:-DinӋ_j#eVeWFo7[ .njuG?IQTr񋯨3sH D#QZ[藟bx,--enn3sXfF 255JbiiJ, {E~zLP_[çhd272H4۸aphT:4{!VJM,rx׹ۗ0t8pq91z78qXCtwvp񋯶v&q tvtP_f&7}7?7~˿1ڋټg0VG$%"++34"n*Mp8b<].xumV:`Ɔz'&MޮDTB8HD0@{1OPVZuv3ɤ3;sZ<71h4;qqn\&Oe2y_|16lJEgG;(r:r.lNMun!6:rΝY/?H4J,j_&>X_GɆnӈ3 ڽ`dl ){h,={sup`ȡ\3>O879rPAZd2IgG;`X<ֹU|[N_`|bŌbU~]^u?i&ffvY8z6A @6Wcgf())~)w:" @^ܼ.)r5"ӧ` w/^'&HcqP ^='oQ'vȝ;wv횜Kȍ(? `zJbfF1X,/n i<JP(hvL&^E*VD2A" JpY\ZFTD{>t:-F,nZvn$Jlnqq镥_@>ye ̳Y\ãh@0}ʖ|%64B$"IXlK._n X4n^h2 G(**z)mb1ŰX̌=^^^ZL&A' '0L1N+H,,R%b8E&VW׾x<( jЀbFѐJ$ IDjܾz^RFcnܼE uR)0~.Ξ>E"h4r`ZO{H$ᰓJ0 r$y䯡V%jD0L^ LII120{^@fz ƽ82:fehxx<ơ AIdJM*d4 ` B&'0 LFc1[fyNK$HLbmCV(UJa^'`xT*d3;/Ytؙ?V=ɠhQ(87oȉGDc1,Ϟkdq8S3twq $'# 㗟~εoP]U\Fee.{ph9%[?|biCFTǟ`2ijl`{B敒0==sWwz209E;[e`(:D<$--{}V}@[,fyf rEscgObuumm7eʽ{cvn2T* bUDc456pG?0[m2(K&܈P>ry0n0:qC$ױ"x9YֱX '/7+s6\֦XG#^X"(XpFӳ;x-Fxy޻pN:A8att Ictt86R*%wֲ۷}12:<@h4O!}:+Ugtt=|',-/s~?+U>znW[zT>>`0L:)3o+L}FL*Uw<ۥR)ؖ}=˒/Ius{pc6x~6ɗeyeێFXbmm d4>墩p$~!+n@JIee%%9D7oSYTDQQQ|b!=]|O {:I? ۆ']DѠhH&LMP_[K2$HrQ:Fr^]ephŌRd㡭p)Vְʹi\VFMu5ssԅh8t'C02_>L=d0 }me|b Gbqb8TH$B*"D+XD$p$B2$ hh}טO>I$|/ŇH$HRR)^.>'f+,DP(,J$ 2#ɍ^ciykoJ fmP(~a.v*E6^.~U~r??@>aI8~!>߻kqLl(JܫkLFDhXܼuxYt:- ,-y ܺ}G|^PX,@^s\T~gioka`pBA2_aY9z|}mxAfgP(,.-q>%Ngqi)rӽ-|Ǚ_~l<<2F{[+20B"PQQܹs۾))G`lVY$Ou^:vwvp97 k3ΟeJ…`"399S'Q|Ե,p8LWG;t:V6}8m-9˪gNӧN&d`1 XzZ,#~?L%4 .h4*jܫTVTFtd% JE:A)Sr$I>$w^{Ax>rFG۹?0ȡ@&Gqq*wRZZBMM5%ݣҜJZu ׾!wy#WߏjE^]cowhU8N*\-Ϝ>E& zuͳG^igo7>S ygh4PR[=|$rfs#ct: *++Y44ԡܘؿx"N:}=b9??Bb5sibow'PnnGׇGF9}8xH,cn~OFIRً^{uS'o:$ C򵜞.]—;p͜Pu| >0ODR&&+h564vJ$rbNCaxӒ|\]UE"sc`P?܏F$@ ߪdmT*U xx<7"?P۞9l~Ӷ˯u:u䩀y8_P_˥+86d0PTAJKJxgcʲ;/Cx= XUjUF==RFRlF;P̎F]lKVKVwUWEO @Lx޻#33 VH"xy;]$> clv|5 R rZdibP_I[޽ztwu{~KS^^h4M( jM&ťegfjS8W^a6tV%#=i\. CCa$ >T I^ TfAIrJ p}`0(?{FOub 76;eeΡP)+-EՒUAV Q`<|PH5==MSS`{}8].P]UɴbNpr}=w 49Pa$`daY[[gaq820FBRv{XXZ.Ed`r"9EuI0c#תʊ'#99`XhkmAP3|ٮجW|NWAzn x02JiI1FF7q=s@0X,K)# ˙'LDX8Vo\|9arjZȧ<95</;dgeCF&$ qmv:"+FwӷAӢI^(h~aus>k7(.*dbC$mje{{ӧNsWH?U9AbQFꪑݽ=$ jfR IAA>KQҒLOIDdpRUYTD/'ѱq,V+y8CLFGv{kaH驓]>M<WIRS]m H$T_^m boqV-dCFF&33S'D|!x$Ì墨0dj2!Ʉ}^o 9R[ PUj/Caa"gOsƭyxI>O ʊ/<<"YY?X,TI-icp"z^HF9ySD$P]Db~&Q*eB&Fk$v:P²avRǍ:EʘvHM,-qP4HS' mz(*Gnua\; TD|Ү, @i§5R%Ii|Xz1e>L<~ܟ_<I9 AD"Qd%4cHIJֺu;ꧨDu!(JK+FئP0.#h<ŽE`avT543p}Z$+LV^!9ylFsH26 IƲKMc+ me}u TFߍ9 _Dz57Azvr%2rq,W<=Жq:μqqNl>qZ(<7jvͿů=>|YHvT*U*0^ J,r ^EaT$Izb;DΡMq\lbfvH +v,vXZpQ#-fwVAgfimilrtaqIcm\r Dmf ӤQ%#=ɩiN; B׋T&i20Oppm  'OKt2rgN=־~<^&q'fjt, 7Tia\#5#Mbok4Z=[kF.'yťHRaBx7whL4E޹Ju- ݽii\LK|~Ǻ% PB ܺ\dqzGuQRuLg&YʺLD*Y%d2yߖ FQSJ$PXBel.%O=yx nS]OLw40:I(H`ck"))ML _=$+ Y#}.ΝvO+Q"2SpDf? ==V5#Y,VZ0ìoy9\*ٹ3#\1{Wc*.74K BP()!R@]bow{+W'o{pu2;;q+395o歷a|bRVjw&IeFӉdgcg\%b*N'rc!!#'!\Nz ?)e5u ܾK?P(Hѧ#BНHer29 29~~?m+U~{;l!Ȑd (TjTGHerʫ)LoT&h+u-ǐ䘖ypP6 ӣT+.q㉦KH&5 ȼuz2 ﲲJMu++8n|>ڸ|ùtwulIؗ$ DTVVVH$HVW¬*{{@A?7: Bw#5dggtA&%l%CyY)Z6N177IFpz} yB4MB_twuRQ^+ŽeBdXI037Gue%hv PgN{9{n`X괴}RSS),(`mmZEϤỻ r3tAfHl<1;Ǖ{d^c&XXbq2,{L Rqh,NaIiUǶ%fT >y vw:XY5Fd`23PB_ z?#/ש?Ʀ^Yq<`SUYd2:b!++uBw?7W _-|]$I$I9YʈO!;TU195j&KHuz]5xgj4q YYSUUq6ssh*Nh}zҒbÆ& ˽rS'~ =x`PtF0 luҾp,TWWj0z|HpdF2I>9Fy}*{pU?MӒ|Ը{ڱxLgX\Z ^D"ybZ8D4\.2v\Ώ6ea)+-f!ːh4)JJY^YEp:Q*"cu6:MGqPXO׉HRd2998nd2s:].WHe2 z=͍Gp8 e z=zvvv91p8lv;;;I3.p8bzla6@9,f nB(vrx{{v~8^p%{n܍|X? 33s\npv^O>6 ۍbI\I$u19ق/bJJpH$ :N;P*귿R>A( {doo|p*PZCnvs⍋?0ĩg}I"{}5X[[' B.?KO]ḁXX\en>^fXކe`ESWWj ޥ L~JIh4Eԓ|8IR0GC@8(--%7sfcwwG$b JuGH(|hpV'qWaB&  FV}n4jZZ ED_oiyJAOiI o\b20tZdq20;;>I^rs$nl:Op ,V+yy2o;RR"_ )MOO{n(Pٹ`$LNr*pEјpKH$RVVV;TWV2}(|\n7YȤ2B0*EaA>jZNvyn#|9Hϑ)ꔔJKKq\,//#  % {ښjt:~HUe`E:۸{?@Ks#cDo_?;z~U"3!a iť%[[D=wrYFKTWUQPh4QXXp`ꋧU1[[-BHn=T'$NwW'Z6:.DR RZRR$--8ƣ vJKBAm@VSR\DUe%yv`?c;ʪg]Iq UǘTw>]Z[),,@բR)1K;sJKKK$ߧqD"> z=s(Jo~ KII1>ŷ~v[[^|4 @IJԗt}쓌IOK{6|ɉ-Uy<v{¾X*O^^v=rJ"==:2B115gv&YY([ͳA_;u#ϖ)RKrS/pyn!+33dgg vNlj4rXFڍ|[drjKfF{{{:J%dEkoyBۆmZð#2SӤ1!DyLG Gʊ'~.5jeff}h=33C([RPTTW=JUGj5?>XGH$YE5WR\P6vM+'ѹx$y2@S |>L5n/ٹy&'p0XYY284L_4r{}~Y^^y-UŤF≥j\. >OcQՈEh> +3;wҌH$b{gL Eыh`p&ܽlWl|"nNw"I+D"x<,KD' B jUB8]\}]YYX6FƱX-vNOv #c m`}c#A.L6"8<&I$I$^ZCo9Ѓܾ}m(Jf ;q-lV Gx02$>BT {<^ "5xb1 lhj~?`(2<)5u0v^gX,V2lhSR#==zôj H<JW4PNRb7z<U#c,-p^O `fvu?8ZBӒ05=N^+È%Z|*"DrM<>~pRUYsq]Xt: ?ˏdef7_s<^"a**D|ڱ,hh'--p(Zj!?șX,V4)6=>?ss 'I]ǁ'~ȗ&Ü=s@gNv_ ~@T'~җ }f#KPUX,4 ׇV<@@MH41bw8eb|>c?`A Côz;ڄ٨\O"=='D3D R4~PP(p.<1h4r9vV;%G"pmrr MǨ "ƣ oXؤaL5GT y G8{:ihcoRw AhiiByUj\r%JJj"ɰ;h43nck{7^[=C!T*%ΜF*bw8FG}lllpd\λ~ÁkEr~=w{p,=wzłW8P%=JK &| ,[VdR)NS`}}\8r)^/R_XNCs!L/#˟h$f ~@0R)RT|e{Lk~LkLNN{36>Wdonim~p8yKLLN:nn{UcDM~h15="E._hʵs//fdt1V&guwaX~0cķ~CVVܸy;Iv{/gϭ5p%C&JZ[HM5uЦ pdeeɡLVi}/RyyMƔňY8Bv iijfE$:Ot0啃ۡPYrssz=b`4(*,@Q gff@Xʌ jS0">Nť=;h4ى@@P(Jdooc c.'; ]0irvf&ΟeooO˱rଘ Ԫ(^5a%)\x) O.՘-f*hZJJ(*Oxbie##tu`e̬Pacvn#3CPT8+טL(s7*bkkp8̥WPT4G@F1~% Er}|[++ypVD~vuGj?reb2?˿OyLFiIɁjSfn~S'cm*+QT|9{3hS#Jgtuf33/TŤ3|.ͯc;Hj(6sM&+jĴE4?GvV>bh j\.'V DD$b]';QjW>ى*}1 23oZK> Οʊr sp9Ν=B.GPN[K3:ݽ=/G\rs7g1kjnwPZR,XL׉ゾk[k3K+llnҌ^a%i> )))A^o¾2D$j47_guȻ.H(` -/mdt;gowZffQVR,yhj<ʥ`W{}W*+1-\`;N(/+t"y׸~9YA҂5lmocZ[G.+0X{}her$Bd+IC4wPdP 罴;ݞW&u@">yUeg2S󠢼D"H ESTX@QT6(^ 3#̌G~A𷋹DĶHO?(RR yD;:/KV_/+-I/A$8)s?qELV`GHt`V>`Xk_l_glV/+ bM5v8B 'a6>\i%lHm b}.79AD kK|H$?KH$Kf9A2îd$I+$_:GX 潽}!:9,PHpO?fGc7˩FOJvV_ kK<3I>9@ʽl+HFxyѼlce$ǟ=4Ld$&v]Y__]*D9uRsOIMKAjj*wrilgt|TL2gϜD (\'LDV`IeE9( <^/VU'+3cШvR ,V+bp4eF|YBU DUeE4NOʪeZ#(.*Ls@$dphҒb ~ Hƫ4>tqߗ}/oXHh{86?@zZ:pE0[,z!fDbBىXH@03:6цzΟ;û.rp88,-PVVJ55dggq\f`h@ _PacpAT(G2++\8;ш[F,V+._A.v)ϧ>nJ\Vzhe^f^Qp:O{k FF R}zSǪDYiIB˂_@ ;;ikifbj C[K3SQ?GomIR1p/;DpT7G0 .p>o\[9uWa"KSx|e2pZVn!ph=t: ND2]Ss qܹs;w{'oOzɩi޽w UGRBD&`v:]. nTʿ'c٨&ecc:|>f3?[4m  35=MffPa /1<%??"RܼPV""RDBVf&Z˅\.GDqO!ɳ'5 rg9u3O(g9*4 PXLZZ*V`0$ѱqɏݟRLH$BTBd{{S]LFKFƕjMpOrN&HIIDlmmcZ{TÑ10:6ly> ~Բo6SWW%=- :"De$''⌝imͭm23?}WN 5<;O- o뛘j C452;7ϙf2|A"1͍Nwh2QYQ.8}6JJH%pi RQVFZZ{HE$qYD"A6_|u)KH.lFޣQȘj;:_F⛯&by%SSUZB&SYYIQZR??q^ϭ 󩪪ANvh4j@ Z[~6n榣ܾs~}n )*,oj4LU4̩8.3B$q@jEcZt,,.K$==@ MeGVLyYIx"HÂ`gg'eeOx$g]߫ƳeOOD7wttZtx8UWHPY׿TڜG>Up,c5^'Tx/Qt?$555!NID"Qv ʊr QVV4ڸv_Mk,..qٹyr9޻GmM͡t =x0-ͬM,/ڜ0d|nxuU>iruHQG {U-Ӵ6E(_{aA>wzq'$phJ%I3T x<G¾X *DbTJ%nrz1<e~a٥#05=:RIeEedf319Nܙ008 Ñ1rs \~M{k -V&m =xHnN6n&SӑC:oll W  &8?b@;;i85=+wgA>$Ik}Ă@"˷ @DD, STXɴFA~R}ŁE"7J%hSR%T*, IQh"NŒ8'nٌjbZ TI8FVB`|bDP>ѱqbggXiF1 moKFz:[=0ayܹ{Oxajdu*7o`w8VƄ1.?zX^Y! 3080PX>?Lِ|\pAHFgh?9Y8Hgk{NGMuEBD= dgLh[YiЖP($-gl|otEydvn Y nBo_?yyx<ꪑѱ 맨k7nF"Ð< KKYt% L xqՉ47ET7ygKvV&fj&YCqN/gN䃫װB`$CHOC.s";{l`E|s@5~~gA ɊkOITVSWɾ/ 﫣ⰧO6x$ gNwxA𫬮p} 8V ?XUe{T*ZQT¾Sŷ%1c0>\ysEbgg9E_} z=gNdlb89L}J]DKfikms\~Οڍ[BI^=gOh2195-!lvopuC/Kb20RGZtfQUlL-ΞN/M`2!zl6~etDtUЩ78N҄p8ٌ̾NC"o&5Հ9Pa c6HBDT"I0 da6׋n'=- ÁN A<>[GUU%8}9{3*GsQ¡%ŨT* MYߒbD"EMj?k2j@!$"20WX>ꪉTAKs#}T*#MLPjdeuF8{4;de3MOLs0֨d`h`0NP(|߿|DpX;YYYloo G ~eE"h4,/(Txhnl; o|+/ ^1q[\Nff%,RD$ʪÉ1W͖P׻@A^ӳOCp,;; E]L5ffhԤ^K"|eo~ ͟CJfSKK"?Iͽp fc}y|QɌ*O"3#_;YXկ| 1 gO$A cc{ ޻t}3F7oquf޿|I;,W؎.$歷!?/Wȓb|*8^N#EŸ?CRUYNEMl6BEy‚|޽t=w1Mt$ A&FiI1~]<{b1[,T35=K8Hm C1MMRTkovQUYÑQd2YB: x{wcYL/[X3,"M7oަBI-99I$OœV>4^;.$A>|hsl(HAgNɮc٘b077O*9rJNWr qZŖ% @˅fu/XY5_!erxPwښjw/騩foV+yp:]C!>5ՌMLr, z=@ {}|(_~LvVJC۳!ٯϏK%%饥pC>Q9@8F&P*lvQ*;v ZX,bZ{ ImwWUww3L~ÚX$qK2H BPH!`H"$rwϿݴwwWWuy!rǾ3̬[7o̼y9h$gX֖{QN)wsSK˛BvޜթB+ E>ΝӼ=rOCo29hgp: ٚ~~vY^s T>qJ*+`OnnlSIK{G;[[|؛'#b29vK8ן9fQUYv/.*bqq"Y |rggN̷VhlnNϜ>)~>~,ɓܹğ288H_8 _ŋ IDATq-.>g_" |MW9_vov Q/g.~9qS3Bj͛?'(2~'3|>Lyy$1zzFX-(J;(.*D&InP8DN\.GVuMY+&8 W]΁O?X<ښ%Coo?E\|۶W|4ϣ/ge`'.*,eyvA P( E _A,' 184̧}*.]?fuG|>3´˯H&8b1>K>«x<.# fr9HNN%RSA&xgf#Y476H2 LFYi)EyY [EsH}042\&yԧJwѣYjї<;YL$M=paJJJ$i4u~\zX4^d2N,P]]Jqݬ"/7"/>N:Z' /p)nvtˋJA~n֨)gqq 095ÇI&008"F 'PVV֘*A飞>jkD1JKD܊hII1Yey3s/֏f=\zp8̮ I9"& Khj::hjj`xdkAv|X⒴p`}s?m۪럈20y\~mUJOMLNsQNLRTXgjjkA>^/c=˭YgFz`wʪ<W]JdCTŘ% b4}I6vNvmmCyyF~? \.Koo;e'vOwNT*#ehd2r \Vq9]qrsrD"=\.7;[[XX\deu`0DIqݷevn͙S'}ZF295r*X2藕v{(--TLwO+Y:0ߞ"Jh4L&#KQ'aiy"j x^tz=H:rsD"TVT}[5h4BuUvx1En zx@TcG0Lt ٌcɾTH8 =s P8B*UUWa6FRc۪(++ yy46ԱnG>x@  gll8_`cFY^Y$Il6ܤcyep8*xH$XYY}'57T*1 Rbf{I&\qd2[a̽f> ;/x^._,Dtvw۹+W _jFXo_@AQ)3QXRڲ(07qJEeivZ-?rG= rPsXa{: hl`UCA\uv{0 AN@CpvV1;7OJ(BCa-i~rG*k& f;O݆kkR OĢ1EVWVR6f CϷl2`ܼ\ J?˳YX[PǺNQQ!X9tZ-999I:s$IY]] ?A-<~NQ" Vؼ=~wQZ\~AV{(oc˫ZyPi$s,Mc[Y$%֖PD-K*ٰE}zo^`v~ UFX[',]xۅdBIZe[@&ZшR$EEn8HB=ԆMMZTWU0uGS# ûHMd2?zucǎpī^O.h4h4jbN5 Gl6}\.7sgO*OV@A꧿z>Ѧ$¡3Tn#  } Yb{-^b!Vu9/*!Iٻc/`_5p ~iŤ|F 6P*2Lyyt:lX 鴼+.O`JmXxCn.9XfnPe&99(J J%` /scI$5Lyydy1<ȯZIBI8X欯]I &)NR5*\~|ݻZ{wld@&a6imiƭvjÇXX\z5|طoW]'s#w:TVTP^^[ʏG,/)j̩|~ d~ mN; 0Ͱ>P0H~ዏc'orgL|> +t:1"NWƃIۼQd!.A&&2kjj^` 3XZ"J9gp jYK&vwJ9@Tx_r:Iv=z"ull8ȡ3Kkrc|>o_/YZY" P(`CNuEӟ h4Jii)?я1˓rH"ʍRY? 3n.yhxfL!<.qЗjsM%gZ^^+ɻIV%H$x^<ff3@;60D4767@uu%riDc1ve5$WV!׷ʊrNPQQNUeQj5ߝej5^2>.Zkio$'7b9t`?*qM0|i?nWY9{/|yH7M`0 x^zS蓓Sq>fq:]x}>BW^g`hz&'/2195ѣ9v0S33t:& 299E2drr)(+-a~at:-SS47525=Á}{݃ҕ4_\ñ>,OHܼ!-wu+,,,fOiqhj5HHx"SA@vN8_^ J흟oAPJZ&;6zimnzՇƓ~E.׽˂ H*6m^Jll4צnFY6c4`tzΝrAQomwA۔vO.c0χE.SXhri.]J2 2:zlllFzdbn#݁$7@II1 Kn(Giiij`00eeMf0>1!n(#$ǎ,Ox]rj$#7_GQN?ƍ476Cx+_"Cz=xǏr V`vn|9|>?U#ilMyy[dW UOT3x<LRVd2IY]GF[ ) f^O*>>39|X,Fap8Li(a*++1Q46c6I&Ҷ{rLKs7ouP/@Vٿo/We`Z[i#)YŌ4752<2JkK+>7-˿l6~ ANNX@ bnwK<C z|~uH$[,ݻL=cҺϾ@N?*덛aivoU4???Fѱ;5j9y;3K$Ir: ClVMyy:@0%:?0H0Dձo>o-(JwV'%* ϏG.r{0LNNc1[H$R=פttuo|Mr(핖%77I=c2ql6H$8v(GEWV=ml;yؖ\Ym3OJs,*gOt?vDږna*UF.//0<2*z$ &CVt:y.&&ӶX,F͎|g$IfzfC) D2 f{ػg7OϾ`eur %//\byeRBPǎ|]iaO8XXXSJq=vdK(*r|yPӶk'6:h޾gf /7ۺ]mk/~I2Hu\kA:H$⨔[C^d2Io_rY:iw,Y:eZ^<[hlnL&~d$ ?9eF^^.pUl62*# uL&2q*R)D@#KB.Gd3+R)j: RKӆJ$H"huQ*X i-ZN'K^55;'=ˣyl)RE=;g97_7ݲdwOZp8BYY)9$yY_XfX =m(o^&jkjHmu|˺*@t(PT|wa^msFm\NkK39F#GH&ߋJbuuNSYQJR&=}TWUot8DgN! d>tF"%%SA%HĥL}mUTUUֶ?ñ!i[wTBN*YӲg1STWWښS'c0ܝmΌTnsTUUrVpjx==TTۚɏV` QkkܹsGI3s/^&&'j>_ ;w10[ }{5 BM׉Rݷ{x|>~{2\FoO}ܗ*Cؿwt>?,Ζ`ZCPdU~hTT &mV]288\.'LI&FL&ӖܜrsmV--x SJ8o}ry:AT DJ߱}s@,hDV 9F#fe|~?Lb1?pj>h$N,3"-'7=ˣ+9uRʱ}*9M|8vY h4QfNzy7nupq>p5lk6Z߾㡬IQaK~r c2QZ z %cwb434<@Ks}k7X,CAP*ݹ :1*Å9l-}\|A&W/C#w ^se:n6:5;J,gonG02kB!޶|kCzl>|m}p$"ttvSThEd86R20>_V9M~g}G}'|§~("<84p] r:u:nsp=wv?@,k{"nS3\rǓ[O/_yCWRQUU Ry $.7;/,0;;']_WK}]-rWKurWkw mkAAƋVJscŅ|y&&V]&xp:EA.r }S;[[rFFǐćñ0w'8~Th$>@ϝ=M( H L& G  ~'rsP*U\v>pLeBt Z ;8zj;GD/c1*%*x,Ne2>/w'&s}}cGKrg'Ha~ƶJBA,P:zĀB$q.-MTUVpM [{GjŚavn7x́{ @o%1pP(Ζfpܒ%Ғb.^=F(rh5$k_^$祺 Ju|t@ lh40<G8v>kkVV2׿yٱ9rp7'Qk4456PTXH ulB̾sg+R٬>͞ݻS}[A"! IDATNvHB-ӏ @iI o25=MEy9tvuv{cAGF)).Z@ksv"D\p+;B<.QWd7xۍJ?@Iq:ߏJbdt لccCoͶN;1}{$!aϑC# f6l2p8(//gݶ@P(D%?O+Hn֤ 7 R6?Ody sgOrbc`QR\,S^Ξ> x}ijkśءvc;bJ͹3YX\bqqjy}&݉Ij7inzf/];[yQVORoD۽қaff456P(w).*" 233KʚMi4-ko`2(.*Z˨[m.p295.]ζUU}Zvltv𗅼ܧu4|lY2q{<\rɩi1h.0lqB`aqLF^nnʏlDP(( f59iˢPXЎDH&_y2IӌvS &Fٽ545+Һn"upyeH4ZIY C$M t&&$ݩCphF]NLR[+Ɲ3~ﱸ $1,-/SRTZajz2^#D1vj_Ju3ι6e  G~- eRAwO/:|.]siOɢ |A7_aNȫ;G|(;[Zꢸx<΁eKa5]Zݳ5M`:8u8_|y/ d 29]ӆkcqZ[ēJiqz9c*@5JyyyĢQ- hͽ7'b^ hEcĢ1RALV@"Ɍm`NH^n.gQUYNnN&S6:slrY]]cqiur9DAUz9tYt:v &L255ԴԆ /,iCAi& V\ziv4swr/.ٕQuٱ}èS阞%Lr5oH dH"IKfS?zsSɒe3 ۙؑqQc$:>1)ʖxǔE&dԸAH$pܬ|LLNqAL:N'amH&I&ze%%~ɤ85fjiuFrFLyܸՁL&CH}8q(GD15=P(1cdO.;:Q(5iQ(477P_Ǒٱݻ} E{uuk-ft/| *x*zzDRoݻX̔e_=q "Z[O>^\!ON?6rI:ΜlK0^'CpI},c~</h'.;# t:4^N Gxט; ɤ7Ғ{k6߷"iA 57/ zvjnwߔ]n#2t:))׮,Yyn)˘`0<2w/"-ߡU)Sñ! ?u@|[D"R__0>>.% +}Ç<4IyҍEQhͯ=I]t456Ovgɒb}0'kLyde`"s aZEH$ Y8y$pVPGKs& ANyo6RTVVl%rťeFFFd>|׋)D"!L.).“j8 Y[a1J/]J(f[u%7;¥Αos32vkAV|>dK}N0;7 !wgִ ,Y^Oɶ'kknH$B42o$2r_*Q(m}ӧN  cqq/H{gC#bq VtZmFCC#|[awlc~SH$ id~~ko05=MOO/#\t%~ߐ;wH?V;;p$糧OQ]YӧOiin`0F@PS!|u^r%095ͥWqݸi}6eo;[roA5M>1HE{G#e`m53;Y WdrjmUH` >6(i\N"/'4Ged"Vׇ`H-˨llw3ۓjٱ۽X(J$ҩ \.`0Pc{gxxZE<b1SRR0vssTr*+DhO208D[K۩h0`[_|>3s46}{ Q^I娔Jn 9o/]q-Μ>Ki"*`0L&;>,o#cw \|E GGJqqi ':gc475 ??Ǐu}{p_/P[5UUqV'*zU63qCjG?@Q*{ 6HMtp`1hjj̦{HyY)ᰨ5fXMd '<*x^)cGN˩JK1M$ M"0L,,.ϋ\|~;C4Dp{Dxٌv__KS ?K U[0tF$%~>FTswnbeumw/b`0'})H&$RJBZcbjGMsvm͖h0p).\xҚ()  BjqN?ʍ[ ױ }[5 099͑CulmA~i4Ғ>A3x*ã`f-\LNH$\n0z.zmV8 .]\dzeDbhZh;2"lb1 PR.p8~[jZ2W755jScɌ|sfduNJ2$Mz:c-(@PH ㎭7N8 Ub45I5h0F1QTVQQqn&&)D2A͎j;}ڵ-J)IQpݬl=wI#)m۪dyK'?Gl6qM({vsV;EEE&j5=TWU^|\NLJ[jmٴ$_Lp2U8`}S>o*qgk ::hITWUeՏGTWWX8w >Bd24Ȯ<[d`fg0Pk4A$p݁ |4e^^. L%?߂ͶR,a088DKK3v#5#Hh2MăJz`'z<b8JC*?G.P [}~?jZ&H;ϊ8BR$)[p:tDQ}V\&'=P:is9^RI@VL$ll8}D H2L bWLdɒx&H$)_*_%M_oJ=7&ug٫bzEF\,f3EEEb2'bF#~6^PVZbbPZRB~~~oi4dɒh︗yf>ߟMy<3剙嫸\n\n7._|z什Kj2&u\r`0Tu84B(top,to-20}]Y[3^`hf{dˠ0k!`2?_Aam&fGPc6|~?Ϟo~sf3NK|I[Gd2##Vy> "rEV+͍ ܸN{GΞfùA"|t{[}3>B+ãcyG8t`?^˱#F.sagg imigK ҹK1  ѥjN?[파~LNN z3%y&]d2!$q=d`D^O_ #B@vx?QE>fׇ@ 1 [w_++h0jazfшb&bڇeD>vR[n u[dcqiť%d2-{vKG5ty</%///A$+>uBkF_xX9LGg7)-)fdtrvbyyɄ}8].JK1M`2呛\M"cƭv>x)ɬ`0F#D)lvb@ʳ/ >(N'99OmD"8.rrryQј^Hfy|_F?9S3lߤoxM9[9_W8`CZ٢gm͆y^,Gg~[fdtweeeBA0dO[}'*vO%%EĢ145+412:Fqq. O}]qjkvtʊ N' >TJ%hdRnN7J%KWGri`գVIA:>o61kx1z=n*{v_|r`hx-mddIFuupLFQUŞ=i-G/<͛q:<0(M IDATMט c0W^([~D;PmzzHͩ#9'<~Q#lnk ^=_o^~Ub鰦~MM(r2(aܱΎ=@Nɋ)3kbbD"amEEwc209H$yw]lYyY${=rGXGz;}8Ng5~20 p˼m6K OcCljXB:mSmV6s_}t: LLN#p_W'<ӧq6'}p̄bddWTG{N~ԉ㈢@{[=ηI4Ё\r}{D--sQ.\(kܻ??"LbmljnOd2uEm%}YR&k:WNѹ20eʔxe`ʼhF*[]S\o,^5yBWiD"Q%RȉH _oANgH$Kmv:P8˔)Sf!IR[ "e^o\emwqiB!b Du2_ TT2.u]foЇ2K'z7 ;#6w|YP%}Xݗ3mZ3JR嫦X%*\zmp\z(k)I2kR\>+'W_Y7s.^ZB&{x[Y 60JE |%ru]GUXt B6%Ud~A 4 ]IdYEdRʒDį$IwM7tL|(HƂ (ײaLèвaNo25Gc ( P*gTy1e`R9Egǟysp:?zW4640Ɠ_c:pWҍō[IS=rnP_OLphYJg5\z͒IRljn@:5+#׮0e`ܺ}MLH 5Uٳ֖r Rp1)Ky DQC33|ܹ} UUW4BCÌ$qFGG ~#.n(D J%y"0%rw+&MMMS&dfCD0r0 ?>zLO/>>{*kdt 0 YI -@MUy>g" 8<XAdDQ`v3Wj"@$N:!<]n>iN֚m[ۑe>N:L OU70fiADLLNH&xǩpm#1xD2'Ӑy`x=_H$o mo.k?cn޺]=_7Vk۷0c1A@ӴeQ?$HZbl6v;Ff3 wRȒ(ՙFI,r TTf.ӹh}\1^χ{rCCC333K;O0D$QԜ젯chho~@(Çd ==P+Wq]dYw,,Ȳf+x,bp9N+KNcc#CCކiܽ{־8mTUUrٿom8NlġeW p}:;;<%rǸp2ұə؃tơT?W m456219wMZl-'Ӟi}_`޼哧8vp_} ?ǣOp]p:صs'=tv챶cVx). V٫׮]@8g>xpMjDIΝLOH$ؽ{7{::K?zf˖칔lvjjjP~4M[Trd28N$Q~S33S",:M&=$ l P~t_RSS,.C!~ˎQf|l۝3h7oDwOmܼ}o}>7oAUTڵs##46̺ER;].*mA.'T ^MfޮymA{|~rm[xɗܢ]MͦF^&b1DQc.w\=(1 'p8ٽXEUU%sq0S +(.[ ۝شSS־ڌe֎LU,L2 !k|:+45nmeccd2:ff&DUe%.}{‘ԼuLSV?hmm!Xe;Snwq\֮Bmˎ;d3Y:::I8f`p MH&)T*׻|QF"xxEQ.׊4As'X[XG6onAU̯6?3 Ըs_4`D$jkjz<( FR1mg_|6/ksfl)<-Smmp,<HA7= ̺`Gݾޒ+*;ktں YYиYn .g)ovkJl O-/ܔ]$u4t$yao$I,2o\g#/EeƣaA*k~$vƇ-zۿ*I:$23Mmc 056/Pa~\c/ ]י!PY&<=TV:BSE}<35R\R"Z!R>_&Vf)VC^N7ɋ-gVE  `,e5`yl\Yl@8..79pt*I{ 89FkdWG:dw~pajl}sWlnMGAM-A"{TTp]vt`tЌUYTE)\mgxGRL,<(hJ|njyYYơjd2Y<ȏ>] B45J(B!ˋLj<Ѿ/ؼXpq`߁WԢd "`dtoszz_0?nn7ꤧ9۶m%b PO(fj*eq/ؽk'g_Dr.\O|ΑÇx FT\r}{bܺ}>qTU1$I$p!$s(c2oe&Rd揸#2 N띵=--x<)d,"͆iz$YsV/SmA$IDEv]vc"$  'OLJ?iv$Lɩ SSA8| osdϞh' d2vAk˖ / ];f>]#2 w008ȑC8r CCOnax=>3̈ݮ⫳ܺ}Nۯ64F9 9ta0+Gs->u5xɩ)ǟ~T.s!~7ǃG$I&&'Ձ(xMMyyLi*{略qwd7̓?c%[$Mә Lss3uu NW%Szň/(Sa߂3[eV }Wؚ *\u…l׊x<[`TKtgctXP؞Խ 'moz-0R__O}}=A.f/44M獾\\J}&l 㱂\njl~Z|/婩Ae***p8 E&nƗb)s]󆉒d3iDI! (,cw:ɤPz% ffweӨ9c.K;{J$&Nk<΢i⺊@r-nܺMgn߽ϖ͛o200a_J[֚D"ɓϘ{CUT*++EE2,Gijliw]m66ojѓaZlak{[2Ŝ > dn'33"8=0]{aN{zپqN񓧳vd[w?xǺJ_yZO9q?vgG?ղk+zqE>Hoo?l19Wٷϔy6Pؒ_p e`fE}ՎWF8fppl6; I$LT*Uiu,u4 '†2JyZʰLq]GQ D"N"(YF42 aX\EAQt]'Lk뀐eӉ,^\38dNKD6v1B4hF&DnT2HA<*6]n7("8V酃wݎnvpmR20iH&Sd2i.^bIF.\mx\r޾>˯r]Kjdj*S'y6 NS_Wz9w"{vWg12:ћb9:SB y Q_W r~=~;A:;ؽk'.]W] vF܁}{y)|}KW58|`pxx ́ ǟ~ƦM bcӯ>qӲe3=3Q@4*+*f^6UycL3 YۚNJ4$\w}>UR)/rC*b||JUX,F2D@@QA$I*)Du&&&ҞULRvEA$0ε0($Y&5TI60 JI+Pb(v?ۃF,AB@UMG@3nsYRIˆA"ᴓ*P]],9bQ(GaVMn74M'L[21 QJF4&&&'d2YSCd:ys|I$twȾNxhnjpvs=4m޵ n7tLv;?_oo,[%[w_pLLLٱ;wﳭ@7 ill8.\$ɂsk^iES',ן|ptuv099ESc#cϒYl Yy20S<^{vsyijh`hxl6ky;J__?ǏL^w7{۾΁#sq=l2x ׃q.7Z6_p"C;qdTU!4~l6("` I64UA$ M]\L&100tp8lARyq^Q4w_6VI,'8Od{211gݼΩ%XΞo&m] 20 44ԓf :ȿ?\x AmM u 3>>J ͛6bX)4 ¥rdFH$b8 8DN6EtTuqo^e!~^QK6>=;WdXjmj}}u n/lc)^'20 VUںZxIl6ǏELɊ@ ^/OFYA֓7s!chl""UJT*eȲD(4c%r"nMlځeʔ)SŘg%$hb˧WKT4׿6.d2E$b.f_[;82*}c^yuS˫;y_1L(jɤfHH,%Hxf5Yk$8u0/^©ǭ:ˑ/y]4yXcbrYgQ>_ /Β B.~[XVa<vƚu&k\u_BoZе|ܾsUU`dlz68vncISd2Y.]B[Ns2G?TUUrqzztxǏr9_u37ks/_aVs9N_0œ=&|?T\|{Ǹq6Bѱ>: d>\6Ym)2v fi_ϗ4c9)UٙŠ(d-􊢸z~!M(iJY^Dvh o$NgHl6<C4M^ }qc ̗_rADRinܺͦMLOOpB0?ɓؿoU}K_Hi àrݯ5s]<'OyiNnI6ex<9tp?`hxA;ܿǟ|ƖMf%S0>4:YY&&$*(7D"Y#4dno=}_qqc 4 YlY I(1G:2(Xe5omr9JX,F*DtHC57 8tfU20v" B0$p8ef*}>oJ8fSs3l/Si.E^z~EaoW'45,,wUf䯙۷$[s.rQ&qd`)|)ɧ15dlko¥K oH&r20vԈB c0 =2jYh%_dzxFp "aj#DIKC>߰{xifRVj5~kt_!kG9/ ^q|;rxη׽MkQuQ[luuv6 ?W߿߷>tksvvlFws.[?y/Nc^=syҬp '"ngAۍ BfwLSh4E*]Z)̘[锡oӊ Oku3}(F>AdEYx:v(4B0;iT9e(޲do@֐WVpOAg9].ܮ Ӊ $x<d~$Itp8|VDp2e۬۶5}֖Y*Rkfrr+WB?evfB!{&yvד0ccf6Q0K\.'j6T?T׏A7Nt!d**EE*AjEF+G* x]/faƂ o 1|t[^|MJg='XĽqn{lv2>&`ut T&$t:I$Sef&L*.9RLnf( (*٬MD\.ǫ0TL2o{(YF)ZugܸyKg[:yMv&&&&_̭۷O?ܸy/:KwwD4T|麙>>ҩͪH$ d D OnR2DW2(:TVUU]6mbwVt@ ,8$0p>f),XY({o9Akwwmebr=YG3e%HS6Dsf^lL&K6AdN;Dd<3*H d ?l鋎7ˉ4j=$/`޶ո$/]&.Y~l|a.^l}?K=\_.?'K[N[ϞZ6Y?yX9ctxl^0htYm#K.x xC{k{v瑩*c]*HGF[[+CVL&ɓI&ȲL8.U q6ffb8s9M%{tM#iH6g:7`Eo2G* 9/`Z0(Ɣl Bc$H3׀EfZCLLȦH#hZf*Ĝ֗n_лe2 '‚9ef(x`Z1{+X7PY5灖DK@A"!rmM EXJ$dxdh4F*/?~HN*Qjk NOӲe3CՑJ9{uHD*`޾>tM΅KWؾ}V{32:JcC=ݜ<~sq>f<}M"3::F}}zC?yJcC=;wnݾKcCO>cfR4?!8=ͱ#t*mm-{_Mܸu#@wO/O'J/]I"ΞM͜ܔ@Q@ KWطX,΍# LCl#GiA(anmoɓx^oDf3LNNQWWˉǸ~&7oOra<}EUUqݻp\4C릲27-yŊJpfݎM7 t^Ynjs7̀à?SURΌ&,J=Dy\*\*vËCttJH2D(:N"DҤR! cثȂsY"(vDc12qR0R (\!ӿ\Yi8;j$IfKs膎h(҆٩>< X̄"˘^fHFtC@̇lY=>?TM/YAosno]Wr|sQCoJE۴R^$lۨcT/}p1n޾C"dhhĔzCx )w]>}cǎp-:#r]:ב%m DܸydzGFx gϺI&|u\A{s%/\ZձG._$n%pòlLycǎe`VIVɲ}63440I T0|ggÑ}m.]җ3e5՜>ujNu;gR糂F?v;+g-q]c",>OM_Q$24t߇,̄ie`d,TUēx(b88Uk'(xdk!;2l67v@` 6 YvtV3$ӤR3$AffzQ8(IaY!)$EIMï8DEI5̜Hq#| W:Ini~&&&y|W].~?nʂ53$qjkj|~N{[+.\t:ÙϿ~c%Ѐg}W~L=1mH&f^~$ǎ&i>+r&&&ii̎۬pEN?FoYfŴ|S '&>S ck{.20ۦ L~MM8< ,Y)S G H$Juuիnϲ E M/B{W(V9@,tr?ز `\I'D'bP%v/v#(JVl6A6'FUSjLfu *\xelA0 Boc\A7 i+$44K)dnh 5&hd4D:!rH&M4%dVd|ב JI0 ]$jr/+ ,YNI0ɨ,f/x3VW?O9zXFt Vd,1s9ZN[JYιZfUffIaN)N> bqJ䳂)Sfc"e)Sa P(be#p]45[WBƗᠺzeǿF4W5 2a":Dtth:jX@4fr?$$ZniNUU1<=xEFKih LL2N6$2=N:"]^w\AFo͖]Km9WRl+^24T*x$Z7{8}K9$X*1i{)Z8w~bOX{hlYe&&' -.]垳}{;y@ef|%{;;ؾ}=vEQ$Opu&''7x*m-Ox$޾~6773<:FcC=vΗ zS:`.* \|97o)Yx׹YsVQ)YE$I4Uv`oq+Q4o\tM/;(bc^0t&cA=-<$'+:thi8ET"A\DA@Pt2vS)2rua Uk$l[z%6κJ캀P*Z\Cq=gQ1+䛆b֭< mkamJ `x_rO}]-n%#A[~ϿK۵i8u->z @:&p5jkj8~o4pFQV? p`>{@*Bϭ Ns~93,620KStDh4JmM dh,FE `N) cct: TVV066NSS#SSSx}> Hr|q&ȲaTWW xf&(mBa*++cW(!*" /N# ]G3 F\}vIfWu= W\w,c)28(6]-I|mc401p:M\U ) tj!T@@ `-Y\|kA$3S%cI5 o[rE욀153*ZZCOJTldrivy`9;Ƀ=,< f3C~UUhȉ88,uuLMc|b]ihg||QdR4b.EWCC=`0|D*q$S IDATjd`źbٙA#m +~ק,*T@7fz qf8,IQ ʭBzPP"bЛ^.ܥff Ӱ@U̪ͬ,ﮯYyu瞓˿$$?ܺp].W* j[#)"=ak$BuZ{} 'zό"H)h?pu.w.% |Z^II h>)x,%']_|<㨛O1AnxGBI2II".˨xSspubUC$rǢer3L/4\(20Z? V$8KzHMo)tc[DH~UN2DnåQ.aVuzraF#_b209׍]c7Ou]oR;wq]bDܟ?unbPթTl K>%,,.ęS j$|NkM$(GL0e^lFE  ȲD*d1H,E,T9<' IR?Lb6h u$ Yd$iEa#V5-۫XˁhZT$ uD$ M)6'2"wulV'>m˕*qa$m,Ko.yɶ,݁T/Gl4n\TxX$i `jZeDI("x ͂bWlDm $eF\H;> 5.Tl5 h86T]Ux@8NsvG@H%k%xmj{ckmCgL"QELfu0Le88Y\kԋ167l1@IXf~͓[_X)70H$D"rJeIg._AdeɂQpMlAHIV@#[S=' Kħ/Pd@j!F)Ώ/7RSookoٶ4FE\:z20k~#8VNZuzUWUvb$ebn)VDMcPU\.3iDDݱ5ץؼ0Ex:"$J2ҷCA\(d<ҊT_/,AΗ+-9-EβDcSa2"")R; HUc0HfGGHEHՈUl$m6^3m`EUǗ!.#*#}p$r\t&_1(5L+ AðQ<+aZ!+c- ecq",n4Nf8[硅P])YFEfr$OS}ky?VHdJ q|~¶Hzѭ|MO*&L{2+r ex<(47H{Nl<oaΝH>_=Զwz<dIn<ԗW*y><8\ݠ\b*,wV\. .x0|iȭI1LIrZh`T+@6{~A.w Yȇ@Z-I^:^Pd؞"%*QIBd=ZÃFR9P,7dM5ǻi^vo_!(FJRѧlg8(鳤~f*~84,ȒHLSO-Y=Dz|An-&, F ]FQZCZ4QdQ}R F"xdMk2d#?Jx$^#t+{UA%D$\5殆.N27ss|ҧVA\JQUכ:juA@%vssjUfggCQTn޼ɶmq] 2.kn-,,.23;ǽ}.O?ׯŨ%ٻoafvᡡ/ƥW­[2:0~&|ӧNr7ٵsvOKw/^BQ9ą o06A5njU&Vǫgϱs^xeNk82^cGPx(+-Yǡ'ɓ_LO_I#^י϶ښA˱n֯S)7/>\'o iZ@vel6KZ""%!nтIQ+gW,$y{ŢU \I#=xޣl3`abS!OšCp5@D n ?P¶= R2B  L+ q)"‹z,EAj$1YǢ $ӭRH.9ɐ7M)My.4ss03179ŵ\8|q}<ϣP(C<ﱧ'M$u]oZ 2ƨTlݺ0 Ud5&9s/]Zra^;{=c ČփN?9o RZʌT~&c(R.'xͷZslngzfcǎ_픁O jXĢn4۸t&iHlQ  mݥPsY:et,eRLLI$QmxXR( .+245Q$ZF=lKd`09ut}tPy-䰾qnn\e!HՇvZ"th4\|qK:IJ)$I<޽x>$/ @e4zђISh"O>ՒNj,;oc#{9{|ZEsݝyŗB?_ #?)W_'NQ C*$2uUUInl_YrkͽǾ}? %hHRE(Vv qLȇ ]ZB,>=Ҳ3MTA`ʲZ1sY`4.1QBQD쒍YI܊J8h©F*Ef \""QDI}hi- luI@$D gyTJ6E.x5!>fdN6% I#-H`uBXD6HAѫ'n`ߤlہH*e͚s5 ]\"BȨִG6`\WdY&H;ju;d`t]GelqL<ǎ~yCvÇ)W?[Lkmbػw{1~.hGߝ@T~ӟtm7&6#Kk*,+_(z>_dnv$'ګW) (Ru. iIj%h hi9pFBK xfu EѶ87X `Q,a K)Ea,WU)6u)¢GckSuV0ZGwX !>:Utc|ܲ`SAB>Jh񱱱LURQMEQPZvBNHȡ])Vnn >q!p!d|BBM06DI߿E5 ]Ex\^A*NX&Yhde!$g}Lpu5z+ q,CחH_4im $I"iWh glSD\\wGfs zz{LLܻЖA:eyX3^u~G\J[i@Ǭ'F2,O1=Wyu@vdQmR( ;c1Lg{4;{Uǡ`,kȄӧLVx=JAZEa$aǶMC$w98doZL-N8@P :rq<<,L&KK?,DMDHu3aY42!C 9ʼn) 㷧x}"VLR jnP%&ȂD9w5M*ʘ&A?]Ny׆iWDФt5=aSt]o&"dQ1Mq;H(.w_ڭlXs&6 xW^}\.ωʫƨT+ܸ$a6η_'Μ Fͪy'=Mk{3*)Ւ[WUK8:7j5.K&H~k# N-;*8;VTX\l6ۢ9mts^kO=B:u]|mF[$b r.S335HIACubPg =($"8: 4OI2׳Iճiׂ,(GGI}HE1͂~b HVغu+ $^S+Ո fT}"W\dlᶶ6 loI+xp-,[x۩$qTU CNC4t7aK_$) }LzX7~9<,۷2q>gΜw[I\x=M_Vc6~s4kؽk'/ 'czz](t\$ܻ7,$ر#Gep \z \|峽^xe;vW^; {poݛvl\yOlehR)jر:\-Ňo3`yRҩ_ǎV]̲s.^ 8ñ#9l^??bZΩ'ΑJ%1LN~U>Lg{UU#ZcG4EHAjwjHaYCՇF@$҃a3 Ҋ̖mFYԭݟHPm.[B2JRÚyk*$8a]ۂmo#M]]TdV, Հx,N66}خk*8TpQA!K CBD@D$ :=̓EW3l~Ԩ%*fD)EpP-`w\&S.ɩ)z |xӲx% ۷195E>Zr-oq7?%f|xӳ=rr;oT*S:MQq: AB< ns.맽}Z~4{q$ z^k !Gex"ƓgN/s ||Μ:K Yǃm,bGn<$S}InO\Y_Bljb?3n+|_t:z~ssi;dҭm[V\ enܼIhvR~/rV%+ ܻw}~̭[Ǣe1$03#˜Ptl.W*L . T?U'[0M4Y\dO/Z˕:(5'?({3q]!Esd-rHK JT2}3i0?--1t*+W9v𗊤o<4Ͻss/8vcwq| W{8y1z]9H_P lT2`k}rCwWBT!r92/۷oS,;h~؏$@4|s6 r<0]ji-VhzovĂiQTm1Ib\׹Zҧ(I$h.c1DAWQ"N(@\CpCIܮ׹Vg72p\z<۟az-o7L_kDԨ!"":iY& =U(0g٬E0}sCgH;Ne2l5 elml۔-aPl4P5 0Vi'hs-}vɜƿ;Qe$Yk<uo>UVq}l:`{}ӧt]oJ>p$ϲb&eYj%x?~藿l}/E/eEzzz:LNM| }NglwΝ_{z}Te k˶^STZlbcX>w's֏O_J5o^7 -/ /˭jXfq :]Z'*;qihY-Pm.j iʠѣ(L/$$P"}U$ѧ(\VthZ0.$%.TbzY!NFmhZL|JE@E0AX}}\^+N^-EA*>=@IJSUU-{T*8%SbfNw䚢|ĄnFnC>bn @T0̮q IDAT.r9 #/_J 2 $D_O\q?*ՉZ[~Ulj^&6iX8/ (h>Z8og>˥J1\e1k¤& о`^i7Mlb|r/Ukd<\1[&kY^(#㉞Ϸ9NxyXD6mQݼp]~a;NT ?S-gz[Rm[d8qJ(h~ \䳊OFZyà84BR.P _׹h0i$]:MWC2kLJʶI iqYfk$¾DC$ddcOr `@UIHҊ>>|(A `*H䓏+;w5M)VWj-r;1dQ ("#޵| "Ua5Ap|jhAI Kύi@~/ɀ[s! Q"P+9,G-ciРH Un]CT9d.,V.[^PE~E!(],3NCu)L/}I9UgX<՚7 `J[V8YOuD5MZYu7A6Sw\%B׫]b__Xz+K<at$WQU u]te^_\u]Μ>Ņ7`ضC3q##>3sƑ/Kŗ^AD#tE~ xy%Nx`ll7m|m޻x EQ0-0[n]nk۽_zcvvu[ 0ДycGPywT\~?_2558}'>7@^M43n|ҕ~a=,.fv ,NAuTN< z߿ݏc٢i\V B@4r:aW4Fd (gxYd4)vG)&,qIBE\go"#8/rhت1 WJ% jO#tL"^Xdf\lNխ^͒oL rGZXԨ` =y#:|0VH Vg0Lx^9Ǥ2i"H,e] {MKZ$?v˳%BٶI.Ws^H2iE!cϜi2"6n F pNŀ ``{eB| Mp|uїcq4M#Jy^X 鐁0 %qwٽkeqe7o}jO;4%6(>x'4l.YWr;sNz7| IػgivN\FyIWcڹ>,G20׮d`yNƕ+W9| [7dio'^._om|ձHG>_T*ׇQӲ$ Qfsܸy UUyEҩ4>}+7\m4}vZsm[g! iWUܰۜirWיuN cĎXXPqY!!CBeLCETк՞KA(6;c1b&c  rXH|@%j8k2u2A @}bMV}fL[ eh'˲,B}6>G*M*(jg,ㆡXo M!$" ɝz٧K.p"CAU M9L !Pm"Hy3L:ͅ vmT*Т^}t J<$IzD'&EGs8?AZ^&>2|gq13_{Ν _|^Nc62tǼ3ͳ@_bv.y>yh s6w/~εk7duE=)#[QUT2Y_z{9xNL|[β}(/]axhRD^c"o@S ߲VlIVk(ۻdm~Vc0kLVH2FؽsVCDA_US"DFQȨ*{ ,ϣhwhY!9PD^Uep]h1 E. $XF@`;Q L>= !Id{tT^E8+dw~Dp=7j}c)*м >fxw;[ XG$%AeUPE$!@+}$AD #TQHJ/ݿG`p|;m'?Z)%>sm;cll7w'D{~}`˖kދdݱv1 ޵[ٽs' w\命{ڲL&vDQdݳJJ&WlI0VX,JOҗ20:p_7  ZU $eݱ'i~y$e}UlYH FFQPE1('L6X:iUPEECD[s[\međU@A$HqPN.6>.bQ]Br a`fnAUc@U:GR)RBp(b;ȍZwݭB.RT{d]]YٖﯚsğЕ[oAa"DDTXRp8VD]lx^+;~C&šh4ضu5:z \.ðZi%I$ѐ/Gm ~l&6 cvN+p4,|=ɮ;vlo}מZ@ݶ|`_4|pcۣQaa) G(_IYGQ Xְ|Y  EDTm>NYie땒*˱ L C&.XC$RiYiŢ->|2z8Kna#n}goDJQb@Uy@)^!E26Uܮcdo/c.Jl)[7\undv: RF6@TPZv17R'~Pی1  5epk^>>AKSryX7%dEYuCXj{<öm#AH(#nW8{< Q;=Θ³/p&xd.r>ݶėJ{[|Htέ9hr-[{ڜW/"\ɩ)|v azmUl/ z**N MnX57ćWnW[tXjݱʥJh$#C:c Fng,F$|nQ y-7`[>yry&J[H"!2FT h]FQhP ɢD< ~MnrTI͝ݼ0nب@zAD"1o;=tҗmKBI{Rg뢻.tۥG ;wi[B&"ѺZXxb61\/ek_G㆕A\q3܅7394ruYa۶Q󵧞^ 0;?CE"LܿOa/ gN?N^g5ۡ728s//no|?+</z Lŗ_-)Ian~z]grjr~'EcG֜ |ߤT*s8z]Opg.ęS|8ssx۱STku~juIs ǩ'd6e`æXy)hFW$b3kNDqYfijl@pvʸ,ҾwQ#Ƨ&(O,y1v)&]ªLA$z=`os"f4!Ffu.cՔ^i;vLDQK룏<kL]pKTZѼj@¶mJbXu`pcwa6/]ӏs8rC055M_vqΜ:KWPU۶[0z۲yeb[׹LMOęSLδ$)۞׊N:׸xg|_ |i,7~ qͺ~]WýF]( xۣQ/]:n "Br~} [2ۈЇJ 9pu*x^8Lt>OUSUvbL5A5YhmvEUxPk+fGQ4MLX ]F\5)UkgmW(d2 NRaxh;q#u8q>;o[Wk7Z@?3sߕ|a, EQp]ImeY]n\ZX>p_n(Pβmۙ mo´a/oYG#6P' F`t@D*y**YLmٻ덃N#DĤӥfɁ `Z(', ˶D"<m4(ۃxO޶[w)^FRVB؎.xj fj-"(eF8ǍJ.Okeo5HBS$vD|1<2e,8 )̚fk~,8Bg|j;J&2==DQq mcڂ*v&> b1barM<VWv ۇ(  E8377,Q($ qP(E1 u\.===dY(s#͑L& UY쁷FMg{]JMqUj&I)y%rZ ?5 <1s鄅CDBm2&AAiFJAgxrBz UUqj,Q,Umfhhl6ب2HX /*}}}HDѠX,G.}?$qz{y|AxFSSo2:z̛&>LN 4$Id$?q*bs de⣩c>Y$oP JOObmg]h.Ihpr}}x(곉N[mtt:I;uW[,20 ,IlVNybZV WEEu=,j *NۋeleqqX,EaFHNTrj-nrc~+"ykɕ=` .1@E"u,SZfmrx(bydG;6U0al|Xdn}MFF;@~OJ1_nvu=aJ F+QD8QLQEkbz8bHӨ9yYU^ZJx~{0]Sp3Yo hEbѦP(yRjJ$ ,/X,q$ TUa \aObYb{&I&o{?_ruoK3wx3%N03;GO&˯eϿ4^;wCܼ^Ond{~'?kG3LƷ[8GJ:W_Jd``&6Gɶmla7uUg[_Wwy^8#W&BQ-^F~wϳGmICLU)ZI.VW%hA%La\:%,jHCsuV!wRN|h!\5 V-Uڔ IDAT&-OQNKH@HsSAUŏ,FK~WZHzcRYD< e.aa[L!HEuD!x>Q⥂W&Ih`yS "y\tlhE8ƶmt:Cq5j.,!' d3>xRY,׎pXIRo H2~/Qd=w1330DEU2~t:ͽ,dppw17?ϡCp-,pWVW9x^~'s? 10'Og~؅M20dEЍR5˲+W)$Zjci;v`yv>1 z-lM[cI& 0kYM ?SdxMJ'* e/df<+OWVpJZVHƬʲL4*Dtalp + fH2%a_/m|="zփE­(RdI%Itt\c 8Oɢeۣ(ֹ+B֍Oep!zKRDBLKK 9/s<V@Miqcn3g8uɗ(ܣ-D )ۧx|{e<]]rd[E.>3>xyc{rw}33G.>gGG;'&-bQ.]ޗ=i.y>4ssş)ˑGg?'-7nުXGmلBx:vettI-Һ[!c?q0M!aѦid2&6& X-w q1Lɳ@j ъF;c:Iԡ^/U~:MDI&wI2$KKpri4Zֹ"Ul;b>hv ϪN@ [f1O6#rqmkݢi$kBr% I*ITR| S]u!h' 8,W($Ikk{ׇ*,..liz؎B&hb{ I#k20M<5ʹm.k$5h踁|)fu x 5TCdb̨e]i⦁ZU R݁L&6if:Uc L`%'RH0I&wamNg[ ;Ͼ{*d`4͹CtϽطwOiC$ٱ[v 255;{$^2,  295 ֬rFɩzM4\ٳLi r$I?:UVZk]ti"Kmƭda+c"y<SNF(dƃ a1]d^~`bFZ. kda#6wK({4-Z[%]!YF/:qI&=> ]gލhDHQ 9) ln defurhM\C^Y]t^h9f:[ l{Y-$‘l*^W#АhU5Z| #eY)dfzx<,`eYdpcyyiZ[[1 >fgg1M1i*Fc]Dvѹc \w9D1&&(#ۧ|\ks 06v WƯpu,z);w9x` +Wyp];G0 $sU4U0 G%Ϝeߞ= Gfqq ضX'K=rL&/&Zzimc[BG)*r%_a9ڍ!{&&H3ܿh4sso>dbb(<|YLUA=\L:M 8H!#Dt+Ⱦ$l&L̤ɲHL"d "( tTlT@ o?^ O7SD2R%s?a! 4 >ƸH0˱5A oüYЩl4G.F׹Jq~uO"̬r/"axU_&f 9uLeĨ@ב;n',kS%[ 3(\6`K$=^] KRɒp:+++DH֨d2LNN"`yy冎3ƈFc 133x 9'cvnX<ZjٻǑ8A^;zi4SHUN8/Nڹ̋,޵՗2Tёa\x133ض L9k3G/ i10Џg~~se 3\coj ?a.T3}8|.׮#nw.\ڍNmY[p-xK/3<4k׸s)rkN:;;9s>ݻwI%ӥj,WT(RP oKuEjAL&2>_/y<1I`td3g9.7KKtwuml'J204e`^(,-/;8snl LS j+|ٔ{v*;|\?S>3"~Ü;ϛ?EgVpe`Z7DPQd'cn~Pk+~LDUUfvBm00_~ ߦl6G2X|>/mmkf20UicUsU@zpC8.Q 7hqEU,mPޅ=%Nfyr-vAE`DžY̐QfH.N!_%itmf* )}ma` V6 f&HڲN9lB_@U1lTBp_b*!"Ţ%oIh2 8uВ"2c)eG @B\J6D*ŪaTuo۲ͷZ vh״uقlv$ Yv ]^jg9 ì޲ d EQ Ah\;P/*FQV^HտKٰOxA߫e)rMe`* 9:0S20Sh,Sޛ ؿOPU4pJgW''#7߰o^N8ix<<2T]A1 q탊ذʧ%2$3}$*E< Vmrho@+c_47&݃#3\{p/`o`)i޻~-RK!hh.TgTQdٵ8娨ZU`6AՍ)_opnuXk?g۳M^I |.\xY hYN Hc>|j]7bѹ𩄺 *Gf@A\N}\l$B%P$ 6I"ϳ$ "].D aqH2M!J13Mfs96KLF$ 4Φ.2 `޶'Ǒao7`ߏ`&a&r\kܞ xzI$km᪮DXoA$$IX0ΡUK'IkAE"M4DM<$( ]C(ԂTp;#m! )mZ\Ӊa#IuuE*~|MN[d>owNJ&ށѩUciBF,$tQm*]AAdeg nnt[@$u *^U$|$L R'3gLRBHY*][8d/ʹ)t z1k'ܱ̥,ICZT~?A{jO-H83X\Lڨ2U*l;,zK}ضm,--'z׮tzZvkϑiZ &cV1|㵆m{z< oyϜ;{<۔y<7ô,n߾P+SAo)Ղ L)>5'Ǭ }jL7L6[#"W}n`|`u\>D&Cw ]߱~$5>ML0v jŵCbK`nvVL"EU:moGR`DgU"P49 *m MCӆD֐`L.c VpgGtG )b: ]D1.ND%$. vK `{xn`\^Ÿ9VIEa_ P"}O!\p8Ƣs/$ad|E5()w!SuRa0Sf>'`Y6Dj7n4'#0v";GGeiؿo/w0 VWTUťidY.\WyedxEQx! Y/׮ߠ;wӍa\q(ǎ d׮/d>?|;~US9zu;oĨDm|Y[YZZv7l::d2\+ҩtIfrjڑikcn~tj4&ͳ٢;֍+~#jw'|-֜MՑ6qdX,O\cF nGX[5bT$a#!vkDz|%l$g 8!QV% ShD^dIB,ܪ=[i޺cKUd $f ՙ_xkYPUdMXB3!q 8 (m>`8N:M$uKVuTrT:V)nlb'Ng(^K#3;;G:&ͲͷWx<:q Ǚs YZAҙ Noǹ!K˜8>$af8q-.^ P+'&_XC _{N2,Ld?;Ư^+S'O /E D+W)hn'&5PU߁2cu@ d!s}[*"vK;C!X;dUL[+ :|uтoƘ.$ Aֲh4c,c6AE)jR&rPW }<^諾%V UVW sW A:mllvӮiF"t$R}4YƨS-D: PdV2nմ Z)dlV]oLx<ȤR)LӬA>(NE~Lq5[H kGȲg8|^+`*& p~Ξs'$y乱 20 Ikk+php.]jʏ<,/G̹1>yɗ; OD"~ё:q-6%XE[#ۃBMF Ecq'ho4H$B(W0W >Ҷ,_mGe+$e~2SU4I"ZCV@%.:oE( ~d:d6M^:]"Ç7˲$ YY^^ƍ%?J5WJKhޞk3V=o\kGM4B0950S ommbk8R޽Bt*NԐɬ]ӆS2VV[۱XUz{_c OT7Em&;%;%K{݋%wan72Om20_Loa<{0l2RFOA6̫bAadI-86+Ebʳv*T1q(@ ׋[Qē|Onݲ!l\AP2ET3aycs$qEY 仄&k$I%04ؔy`O{cmHdG7z>ƛ,#M3n%[gXneY(`Bz ⭬m2Zb2c}-xud8s'5nkI IDAT2-CN!#,T Ii,1eXK\Gn|J1jQm_#}'v2#7@0@nUV$ WS68A?[/. \.7K[eYBQ! rJ5DM!`!+NеSd`6Z]eٛjj5= vo * 1?+Qy]K&e=q$KQ^<TsmO rIg0iA%4` v[b)fc)cmاmx5me\u}z]A+uK79Rmjs-Z!s=~?er3RUyMoeo0LR]I<^V]oeqd&zU] FQ.k( ǃfr@MTxgΝGQgy6^6;a0Mn344@[(T*X. ^xW !I&6>-JU9wF&3&6q;;W퐾!Eca:|mWNmU8((e<<v,PX~>Xr>O˅,IeJx-FۍaYfp^ad!b,-[ʑ*ԕ eIS&@wgjn:Qxx*{jkï((\GH0ev| |:2Hc e#p;;;KU@jU>8207e=;ρ}{  --~ۏ9y8`n޺[oۻo\erj7^022i re*6_ Yabrrq~'ΩmK~= LOO7ap VQ/^dxh,mŕ+h#2v";GG@x1})e5-Oq\HdBA_#_NLd`mc&Hbޚ uahYv<6#JR[^ZVZܐ]\$D5.^n7ŘS^N $˅kZZZf3vV5!*!ey`Z20Ll6KKK ^7ox1---|ͷ5ʫ_wnfg|ΕڮFܺ}co2ss:Ȁ!J7+W,.I`|ۚ%\.MA&ˑdrjiMعsT S%MeYXXX,I)U9y^~16mzy)>=}'k4 j>mŬĪeo05eȊ̊]fu!Ʋwk$-$I|>v|H?L~fοԇǃTK䬼"*Q<.#I2-]kd%B: YI%Irm{OB^w{}<}H}g0LVd%1]'3p!vp()7 20L֑9z,AU20/#)^e腀,Uy< {9XYyH$r5"Rpsj @Hzz*Z YUWUE3NTd$+:=po=d!yv#0P'KMlhtCؒ7S?`UUE':OqSe\u%"ikĸ;8JK }^'D (uTO&IHPD"wZ&>820ӥoSOk<#20[$'?HRƵj\.~ZD x<NdY󱴴\Ah.C/q6?L|o`GGP615p>8[dovZ˕ZCiZs&]$>S-!<~MDA+ʶ@3YW2^o'Q?-dsg)iq-Z""I?AP6T!hyh4ʽ{,T*I&> v-"΢M4D [$Kq(Jʷr;]ULS#ZZطtu13d6@ F"|wGMA;Ev3i6'_>ٿ;ÌodԽ8@3c)06j]~n[B7 ߟ<ݽǿyub#fْYEWW`PD*csÙsc {x!X: <`6oG4gX\Zz!c5s{:Lb5G'*ڔ_vWVVf!rv*n5;sCNQ}m<,+;ٿJ-ɥu?)S,S,^Yf4&X))"^t0ǃk|afcs9n\CVT= 8ԭXooTYXֽq5q\km_{. mX\H!U@ dsԲymxx3|o,dH$J$%io^ϫ< ovۯ ضE$r;w+$ ZC?OSS6wR)>]^HG49NbmKC?:1cwn.e111ɥ_s<~I{^):ΐN_@\ r\.I Ӥ͇'d2ؖŕ9Z~:xp[qslb{QNLLL7e` eM gaa{gfvy\ʩJk(w>iYq{|o,lTʹ |^ ᕕ2~RշQkvt\Zڶ]f3Tj۷ݻ1lC񆈭Wnab\O$x\x= #-$:evnrN4xP Yg:bGnCI(3C/%I*U(akC5fUu9YQE!HN1͵ujYR ̟?ƍ[r,9i߇iY)TUDVۏ>`&o?!0nL6$˴cS0{=L^1-ˑi*ʆ96pd`x~8{19uǓ/wAۻ&OOdfǟf9W92ٱknIgX\Z*p.ނ L[MSɲ]IKkXa\.BL4YGj73gZEq3:>1?wDr /H^R%W~c):KQQ[́rG$"(YRL)FgNCIs6 1WUdYf4S.oGElD4Cr 1x`dYkZZ+C7J207zm I(ׯx20Wq7DB0Hdd2I" _N !Y]}c#FG\0]c=+kYL$HنIK]b <B(BW-dY&Ls|Yq'z;kZGd ERV ni`F5tA]ג9[(˱MlΜmL寸tM$K&|ͷe[Yd`^Ml~a&WƯZ]x1UVw s)Xϴ xMmn@ElަvmK}%LO'or/iBwif* $UijC'xMTg؇*+X_mFIӴĢQTEff37PU7}CN6dH̗ .v0Qnf;Bi+!UZ:k`Nxƶ`D"e=_^&twwCN8[ *m2<<īrǟ2::i% X,NKKy>S|uΜ#řsttűL>4CM͛ts=z0M׮8~M.\dxCŷM;*h 8Cg9rUVW"z L s3 dim;Y武 LCjYGyrRfX:uQҐ6%jDo6\wUu386.DY ʶy= ,7b[.r<¶r ;T*E"bp`0f"-FļBh*~/!/~U-G*#+ *Kly]aeȩ9vJD2wI We=6fik=Kl1^WQ$J&24*"J0 lv-_^$7(| Yȭ8@si~~'bbrK_~M{\>ǩǹp2{vvs-y$/^Fec݄"hSEeW]߶<54<~|I|z}~M,6?Z]ɊF7 E)Ŝ= '8f>giyI`aq\.4RkLbqdLǃZ4Mɂ, ǃbeuufL&o^feTMޣ z7޸;w]c,OU)67q۶n7==Ȳ2]9vt]'333Kkk+e7W5?7yfffkGc s-fdd/Μcpp\.ϧ}NgVQnݾKwW'n v&##h$IdYBm}?hoj`ۼz .^)DVW/Ξ㝷O2^2ݳQ /Hd#1::sck&)v(SZMl |Z8㶐QPUaf$ mjU,q sɓ U۫!#`GGDl&.iy^25m~q\ B\Vruٱ@Ʋ(##$!Kx -ǃwЋE[A>(N#P(TUgyKj%k$*QW%z tAl(HTVioozzzX\\0,qRk؛oۻF/6V?l}[AqCt_= ?O~WgJ7˷!~WOfPU_[OWgg4  ;>۶DւDWlVC6LޤFMm>&}΁ \3Z]0$] GBt]'^75BffgB`(eƶmtk]0hUU !P V(pI^Eq, ;%;dzύw n'Y4Geݻx|;wkG=km6 V'k&B XKKUu>|>G(*0??OGGM>pdowM&~wQN&7u :,NćOcX'ac/ -wl visGuRܾ_p[ؽcr18>$zKxP5 4IR|>¶df$d*E{{;di*Xt%{!pĥSu:dfHOu.`%Y+vuJ(4̃bJVƭP7xܨRu晝òls%EzFX޽{```˲1MnJM4Dφ L[#uZxBk|{G>u5äi"4LL&*x|j4z"ciUȄ3X,v=28YI7]-I߆"c$^ L{{;k<2 yd*:b87q국W9(ܹ{ocK嫩3r YR0+<Z]|(ЃAn~z`'cvA>槷}2C^D =x(y/wC5}C=Rڽ6N 1/0J/wI]0̝/)# x#,JXʾlHIJ39 :ʁ#_T~S,sbb1w)&4LA^SCɄ5l4 ,2L`@'kL,{<f%cL2:zˡVRӕ5Y2؊sOt:| zP___z}JdEQm tۏ/NAdIv&#yhX,(437/ >|GrJ E3nĉ$In~zDuĔs^2 :_; |54}{`Y 1͌?-,?!?!"fD::Q9q(OJ1L\q=ZZ1Lܽw(­۟P_ϴgCy4<—woh,ƞ^߸NбgO/Gv$+8{m>t>88x@ D=(gfd2_!O~==yI$y}:KL/C}c mX"A dˮŠ9)YcY=6w,zW~R)&.'ctR<ϔ%Y@A2Lrgid"{0TJ}eycpBrJ_ug޸-X_p8|K&tvvӍZK<'JklI$a6`4 I2 }4$ ܻGss7?jr/tz9w .Sطo/ 37efv98[o~uF~[?wC03;˧?g/( SiqekrrsN7s^/(NIg2$ Tp(LOOɕ|[ıc\v|477q=ʽC,-/'ܹ{&2xznBd#XVˉRs^ܜIΟS/ÇTt)4T}2Y|2v>gw7ScX9hy)K`JsPSU6.9X.27d xPP&C(Y 'j [3wNG)kz֪T]t_ec3g!%uD"ҒYnQ>[pijjLFoO/N'ҬZ^^@ X:㣏?a>J:#Z-c*uF~[F4770/]jg|o3382H0 Wkj40 ȒRJ 'Ho019.F1o<ģu:L^?uk7nP_䔛l6::裋ny۽d3!mmF#'[_槷iokE$z}A{]tI"tvuvj|/*vc,W9PxIXZZNaaa_AthצfOxmIeWn)(}itҊRi˚z#_ߗJ.<FFN9+NF7[t:#o~B7?XtwC ѿ/=c \jRw)ySƚ^_bE6@-gMG3KmǣȲD$%DV>L&#&j$XL]2ܰkNlrC/A(P@R=RYrrKX[jJMbYn8W kʫ*xl_Rz5lt庬[PEǿ #MM ~/pWT96S2,ES3vKeu s"`ffgF/Ӹ|eWܽw*eD[b/h`swc1O$̬+;?jF:/*VS6ɶB:+Y2?F#u+@ssȷJX%nc~yamYmiKQ nyBBXάH퇊a;DAR#y|pxd'RQiY.SY&זHE(tww!XD"^.CmWjjzIu3DQ33˲eUQ8w4/]c\r9tpǀW8(  T 4hNo~Fss3gNŵ8yEyMݸIߞ=t| <&Jk'y26(J;sp;ǟZEkD2[H$5DKss^tN믝z|^ `2"g9$l6v#"Yb6>dY^z(g]YemV(=vXܢ|u%vk .1دZ#Tڢc\Ύkeq]8O&Sηx^fggbFA;dY˲hؐrkh`>Ki\N#ϑB/ N%t`Y(^ɩ)ο{?L$ ܽ7wNcoia~;s{t:CYw>x"#{7^OmS~<q{P)/p+Rde C.jK)aLH{tvE ŧg^D'`8~@ayN /lr%*E>9aX,:ӞoBSS©ɤ3 LԚ*^M448[L&, Nph`._ao\r}۴sFҷgW]㵓's{z{|*=H x}>.^ݮzzZ"c~߿p?{ßOOXXrؑxpXg!,//#UNBE>E18OƁټbxi, j(s۽>n%I"DAQ,u[[͏[ՊGf21 |q)G.( yDKs F­,)9!K#Z) ㉢- Pptd2ˏ JeH%a^bd)75 o?ȞDQ$2>wy446pK~cю{o!u:̵fv;VEQg=egKWwx2X ϑ'?UZ]Ny}8vjftvvp೾/]\rcG>60 4Sg͛tw똞p*ዊF̷v) tB޵ bS|r~9v~?M)o1E@-*) d#|p؍׫:47vd_#Ilc U+!lEaܪկwr (3|oltŽWNj:cJ2@qin#@sSn4Dotr B'$IPNǻpuȧbaVHeDžwruEX۱٬c %Jd2"sժW!T?sje_B̕S]^qy˞e@r%ߑH,r9Gmmuu ~6n +wo[_"jmJ9뺶&xٙH 9FlV+Zz=;WPc pq z=NXj-v;&dT* 456jpb4p2xuPDߞ^=$QI?m[[cKWJ J\y_;B0.v =m.::UPxi,u6iYQ VColm([ZV3YbSd-@9NWw/~ (ny^NB\-'E!l+o}[bݍ,|~|>ߎWEUTQEص=/H,.- '̇z}xxfgөM_VTxcXA40_xnTž`66H@EPPe E'7ՠ5h$ !KT Ų&T1,-EhK-.]65> nB%ގ5pK{*Tv[CQ6؅+PaӲ+[vKf[ZL7EAy4N*jN&HGa60<]~PhinffvޞnEQ]|Ѐ |%޿p |P4sohuU^)\v;q AE:ȣܻޠGVd8nܢ=?x2:* L4/xFج6ݸɩI&\yT2a?d(̜G*`0k'<{8p`s^u6ڵ("td*$+DF%t&MFɈ"Dd:(Ir6B%,0[6Y޶_ݰߩy o`-ە5VmYx<=x{}ÄBaXܩdT2ܙL{<* w'Hp >#q!>9>Vt:-b[HS7C?wiϯmm8oTJ巼{Qx*htx (fe~~}c~!gs}͇R/y9 = =xuؿoME4S<a Of3e@UEd*M2B$DI.Sohj-zmK9Ef+*:4V+t6--wX,tݭVF&V]0<~abGypը NO0ؑ\r6K8vuzv;Ǐ`z~qpFk~5:I*9v0^'O#mN:A[ ]iokC׽ֿr!K 2--{nHx99Q 2KjL= d2ȒwzZS馱 }aaFpxG#>K+coi.ݽ>?08XB(X`S%ь QYVvIR]APcE Pt ٌQb ) 55&ALd9Hs _{ٱR9/ɓjЋN'ծ9N::ڴ@&OgO:'OL5LX8mױ~2v hKS}+5"-7˻Fu8q|͹\CmY`7r,P9C"%i0HLB#g'B%aPAAV$]M N*55Ȣl֐UI$Ih2`՛25k ˿VŠ^J+-?VW =b ~ ~j/WM^cu?Wڵ};ߖxfEQCQ^`2H&zzzH(J/_UTQ@Y.jJe"i#25zXFFU˝@8T_uVE= z@'DQD ]Xh4H&.p[ֵ7ԡ(R,/۫z}&ja,Z!G[[\#ʫʫ[g|sDel::1D͍BKDq!9$T*M&]X*M|@nX\ Q{=*@tjsqG@4+z JaY'ExSEdSkPHdtԙu4*32 QGI\ 115C, M'1 LƈEcbQHh$J$ayi%(ao,Ї~G}Ν;x<FF~tvOc٬xs n7FG] χT`0x65{PW˛-,L+>.:ljD"(-l6mt: g,+HD[["JQ֫y9bXI$JLY*+iD|Q=F`9FRd$dEM#DP8Lh)Lx9R@(F`)rP,M$%&񇖐X_Gmr$ q}9&'i痖"D"K̲d2Ŕv**U7Jn(ov{hmu݉ Ŭ(kEQb1cM/z". W Gu 6^I0?`vvnxO+QAszNGmzAiN~ Y+y4<Ǹ=s9Ot*=ǴC2byy$Hߞ^{/wNs{ 汷9rstvv0dq oFG qۉB!lauu^+=gddj ͵".E,!% *sV1NG YQH&S$Si4lC P`LKS=MMCBuu6ItttP__G26IXVXXXΆEE(6;^f144D&fDUU 7r?b6wDkL 0::^2 gzWhkkebrEQ; p1#Ԛkpp\|9/ ,y׸syp&'q}FLSN敮z2?An}zrOhhh WOx(55FoJX.*2M!;,XjӉItyPG"XŎs6Gytbx»yV{Ig"Am_Ch_n=\2  PhjjlVadY&ɰ BaL&hl\|y@IFIY0aXg0$ҲS QT t4759P>YAQz =:~|XVCN pTe%Tz005tEt:D"bE$%L*H\Fnm~ҝڕH^?^O|NfyMME[] IRŴ)   ފ"D"@&p8B*tfp_o:z{yh'ccyF 450! NjF&&0MDѬ"%r:#<|8wp>.{np1C277`:;:h4w3F˹jt:XXj{,Zm`Mojjdzڃն((UTQ*;o Am\+Pih\~.~M=3蠳CZr:|tuvD4 d&#r:ܛn<!jys.ϯBmmmQ=f=d2k#Y{EQH&466bEd2ASS#KKK%孆 /)KK˴4o3=l"!uj,ߴG€NBFG"֐Vԗ(LB`hhQH5U՞~\q (23ڒzl3:bR>&&&v~7WW?jkkikkc~>@wwhkd2tuuD$Dkvy]e>BqKKKVxhhh~+1|u;[|..Lm6RFϏ߹;sY=]OAszYs2"8" *v@EQZ=t,fbUQ*Ojyk&&&u-:FC׭)CgZS(TQ9=Cyl+`/!yrKנ.'SqM_ UTB1xy Mٜ/wer5V+dX;'%'Nnin:K( EQ {`||\&Gg> APfx<,v /rm~f>&''ٻq8!_%W__Okk+@@q;H 0O0aZƥI)ݻr2>>Ngg$x  J߉@Q(bAss O< Ap={^o0i*(bΖ>C]9/(JH w"Wv(ŝ 0w[S.2-C[ePwoiM3ٍ$I ?EQ0Mwo?dxx={P[kfjj=RX?vC8hjjDG*lVIف,+֚;"!j( LL[SSC*t099dvv\f`0DwwXNfff6Pr'47c(=LMMZ$N?MLO{H&X,fBLNNh\.uOnMM d $]]x^RTRn`~onnfbbg﹊*h^XQz1+b)ӟ@$R0Sˊ'M*[c2ȈǓ&QJUUUNGYr** ހժϓQUTQT*v/pEQEUTQEUTQ'zIENDB`gammaray-2.11.3/docs/manual/images/gammaray-qqpainteditem-paint-analyzer.png000066400000000000000000003157551413310443000271620ustar00rootroot00000000000000PNG  IHDRg6dsBIT|d pHYs+ IDATxז)YW H8@3cw4F+BśPb^U} kxڛ2]P]M:y*~af6#@ xf0>@ .OXTuܜGu2̣@ ǀ@]׹v`jzf_}-##?81Leٺɩ۶Dч%3׮֏ѱ]źcϣiy @dcկ/^q9<֮!RQ*9rl6KRDI~/q&L NpIdY…k=]9zqTMES5TUe||wΎOL;TEJ64MC$c1"`H&v CC466RTZLMMQ.WX,D''Q梆dYv;TD2n'JH&e&}>`r}>N:M z<~ϊe˘MyW* 6o򕫴A2 {8v;c!appq^p,.@Tq[0 ryJotH8fz`vv^+WٵE8AXd4oAJERQ0 \t M8|(s*.O!E2 L\.`q044`0zP(0;;,˸\N)JS*ZȒL.cdtJ4FF׭]#Ge":gO\fݚ5[ukdxd;s51端᣽p5t .\brr ^'O^@ <}ʹqv{u8udV v~$~ӉtR(\x@O\&ʥ˗Ȳjلnog d)N'` N0L% sànL6[hX, EVIa$ɘ&t]tx>tt}j0Xf d?G("_S* %vɄbdtY.7֮ŋK8W;?a6Zٺel6$/x@ > əY:;1p Jrقr<`X,N(Tt&|XVB |bH4:bog׎q\iib8mN6E$&$NɄ$Ip>P(t:ZdYL&6t:hB% FT\.R)S,bq'&hjl  $5a"0X`0H\r$___qMpMlXVb8`q<]Yr==Ȳt_ @ x| ( =aڥi;$*G' bz7n@):@ S(@ 3@ x@ 3y])O 3|o,iZr('dJͫrBp_IP,R,luN}\@ 2 zjAm8Mj{QUM! @ x6( e"YCÿaZ5F'۝IUUY/R`4k ٹ}4}74MRz[lP(w$J6G48l];6M9x$p8'?чi,[K!_`fvdrR̅(˩(n' /l6&''yÏy:X:LCCf.\⥝;b9z AgGx+i}'OaXH&g0ev~Bg{;NaX-VΞ;G>gWclb[6s6[e+r*P*}g7W^CZYj ~G0S.qNt,bxhp4<"xtH$Nwd& ETUqS*( N*BUU`-7q ~O濧\p$|W_~MSȲ0 8yшlFdYf5ts5}->sth2b4Z]nzc2p($I{ݿ)/nÄCAFƱl\xT*MP Ia^}m8믽_}dP(v0LG8@ <ȒĎʕkp]gx*333HD< C!F#>RP($&c1[fsd3Y\.c[WG92]R?!B/n'?dph t:;yt:a'o_[oF*c6AYb9CC4Ɓl^7n:Rko`Ud2344LOO7Fc5'`4:Ͽ=7obphwzs/7hh011MI$ G 8"${+PT7l)JvNp8p@ xq\wZ-b(рfllY&v$PX,RFEE4RmmUgQ((˵|yI@ݺ'XۋW}YZ[?J~߳ tEwfffoϿ";fQP(Vf3Frl&#I9zV\.b0`z+wa~F N q#ɠj-*r<6X>mExYN<$?TCL&S]9҇lHL:JE!?nOiUucwǁrB.{xlQMӄ=y<_+@ ?E'Q>c:y=dY&,f4 G?yY`߁x}^ذ~Rp&niZMLR`4rn4]'v(ʔ%<Cן/&L&norj%T.KX̟u]308 /ܺ1PC~* &t:$Il}[Q\qr8BdEu"p88w_iŲ(;} %#S,q:HT[_.475EdY;.wϾ7_mIrbaG׫%IE0B/J|ϼjkýrb^-Q.eyJz[$㔊%Z^H,'Ntrt:CkK 3:6N{[뒝Q t3:\.I&L&4t:fX*b   vUX S\r M{[+r|>MȲL ,fs5%3LMOr9 4sF?/I,^ϒ|JSc#(qul6֖jc`Ywߦ\9&NGZB4pi ]bDtիV2:6F&ewk>n "m--<|_ǎ( &];wf?qK $122JKs3bL:U2=#HgLO(KA 0L\v|㵻:bGv___pi ,/s1VZI痢D2RQHg246Dp8"2PHLx@UVٴ9 c\.^NC$~Νm[9{^l6lق?~m۶rErv˔e9v֯ Xw500m/ I QXIw~Awk:z۷mKx=ndgk6Gu+#:gΞl6sK7_ᣄ2gϞ睷Xlt M29C6+`#ǎsNj=wMoby/|]],pꚎl&׎C6e"<v|ESS#mmr9TUP(P(ZtvhG$>o|nJ%DODhGuFXMrnG$,s=_*x_~]͛%Z[R)rp: DD"rիV( \tQd~Y۹v:[7oNWhhܵNbr e= xn&''T*d2YJ@ mȒᠢ($Itlv[U6\t9#fH$)JE#{- h'tuv`6[@>_`ffYz_Ϳj9p>@:CjbrzeHLccz{Mݬ[˗ imm%V\1&MSc#~+/v YP(TAPPC;%jXvז C#/tFood@d" p\(AfSS#=5!~mCӫw8|^TMKBkK #cT*[};>+  vdj~Y,67gvם;?lH$qMq:4֮;b& ۢ;f]׹|*}vύ@~-}71:6Pٌ%NԄ_u:;C5麎lf0 P(k6^4 SF 6oڈb059E$I݄C!TU% N05=5Yr`EQhjjh4嵷ݛ> VXι$>/$˖"YZ _(fkN /w&,nZl6wp``\>O(rNߏ&sU`kvC立LR W7rxW*YilhsB}^ffS:Ttu|tLB@\pԭO$D" ױxn鳬]|dddEQ`|"J>(` 4--\p M$R3!yk L"\q]N{[[]rQk E }9+>h/oZ-zmq"G w4XH8(Gp8ĹXn}hd:O:Ϥhoc``m.b0b1㙙@g>zJ߸IOwFƲn@/[ X,?`3LFѿm/lO(=nZ:.pL&\ZD'|X-jx\ Q PU#P<؏m5-_]/0C dBZTV8w &TX IҪU+QUH"tJ/|.xl(>~yTUJP~49OE@Uպu7&<l*@"۾mvvTz[V"&Vgx<\.&$t:>3mNrnųK0ÝT:MTZR}댍?N5I8;:Rd2$I|ބSS5\~/gP,233ni >Jl\%LFFFikk}|At]*Y c9HtqXTRDRajj ERR FG' `OL`e&C<pVK5MFP@4b8f IDAT 4VI,`0rd9B |\.K0'\'N^؊~{. ~!" ~' gxɼѤyhtuK9LZay_pt*MӘMerv^ ~wt;xnXC<`~R$ǟZusnL29˗2::FTj2Jaٱ};GnyP}uD"\hZX,Je ClX(.Q6EuF'%P3cSK47ޮ_7U* gϟ&ˬZ$clٴ\.ǹxnz'p:44D;>=@]!;bw~퟼'~x/X̘MfF&G y_io~t:ÍAR.oOUf9tZyO{_0>>AdԖlFOO(z$ib6Ȳ躎dӔH$) AL&NTK-1HTf4جVJ 4 꺎i "  34<‰>֬^ͩg~Z9 ~9f&@ )Ifp8 Xb ɈRB U'VRDccCvBHsSSa0٪?6&a4X,nr\]r=#JE¢~/lYྨӱZ~twtټiRϾ5WrڍR{ ,s&0hVk5l˯!\z \~SZ[(bϾu G'+{X-'B?V,_FR! 3<2\r99y46= }>M RY<^.UՈNLT:PpYa@4F#>,,QXuҹ[6aG9b1w "2z{⫯сfΟH(D5GFP+n;ko(JwQrllΎ FtBFǢ%+T w#͢CmzܝE4:I\ hG+`0 ,KMVniݼ ;,ߩ,l2axתsi[qk~H?6f3~MHgXv6T*kttJm/w`0<1A&#'& ][lEQ(Kdbr@c2XQw`vvϿm%IFLeSSSom s9|v6v8>dUVڂs]sﰩ`,28>݇{![ukk/\ :e [9q$. tM# NF4:I298=d2cX%JyW8x0&P)xu Ey|mj^mF#K>ޏvz{߶^20KP0t8,{<,f |EQQT fj|>/D5 Mtr۶"I b?m_"0>۶!R4#466$IÝ*`0P*F'tt-<(x m OppI3^2޵׮ hnnoj ._aU2nYl΋/n믿e߁C]NYv5|Ij@l 5Z|/ A![0sGݺ?"0]tww{m:d3wOՉfjֶyyG=Y˟G>Ggn"ȜLJ\FuE1'D/,&,-Gz *ߝY@`< |6g>7Yc͵;HӴI:fffz<455p8f:d2yƧ6BY}W:3X x, VB!OCCb2((ZMU"*?i*:  5b'޻o355E"9COO7\ᰳf*(@Mz=d2QLDhJ(brr E( M!($##-t]G].y@UO?>*|NE"FLf3^IG{JF۰|>_e/x*sB.]RAS^6-VK]^ך+JLf3B({ZYfᱧj dĺhW4ʋ F#uQS. UtT\hYZ(%Y0],Y\*Y1.J(ڢ 5|jeZd^UUVCˋukZ(hsˋN)W*5ͫX ۰UU)+_Qf3ݸF`4d2nK9ǿvK2:2B60xj*NP-BdxlvAv/ $){q-*RffReA0P)/d"!Bxmj-&&j&օB2:AGG}zXr[[Ss1::ȹhn#NLL9P :j㙙nh8pU~Hcc^>CKLƺCnsM&MMM( T t:tQ.:WIQ̦'{.i\ve=\z ]1uos9$Y=/vfsDQnO& \o>+~ߡ(*;^܆&'ovΦX7bMo~:(ۏMimk> N$f$6 ,xFXdrjW05T*޻oWKe2twu_w4R{y~zGFZ-AFF(~z{@ hU1r9vK_񘷮eM X*ayhhhFV+--O`X,r`.XpT*\q?0;O?ct"muVϩ[,VKilh*|rr^{ufH8,ˌOD岸\.~s=Μ==~2:6N˟G4:8|cܼ'~A.GU5Ν3gv-T _1.]'}A6С#Hr$|D>'OyhQ??,H^Oذ~.^j|Y/6om,J8k׮T*g_gKD F.*2xJ!TϿ_};?yOHV]NxfP@cdt֖4]GXf1tw1J'xafrA*X, c9r IKs1Ξ;ϛe&<hh!~˟q{o% ǃng^>?c8N36>A8{7C|[6o$HrY:;hD8v_E:AU5\NMX-ZZ1M>4{C<1C"-‹e #֖х|3MSS<;&Ֆng]{&kw8lu|]RL4:][X47/)BH[B⽮ ٤.yxx\KK#E[&QMjl MF)-4Cm> 6ns`>/yz$Y:{kBb H@ `A8w`~|^el6bhU`0dX, Q,x47x>rիV%J2P,}VFGذ~r!2 ^Jxc&P(eY߸YsO=WUڰ~ݏE>_`jzy*BTyNgH$~ NNҡ*PN$L&sn`DCtuv~%7=_H*J o׵Fl6dYϿg?}ZGw|fs9b8pHuqڨN#V 8P^Zgvu]k7u횦յWsEk7Mus[Er1\7<9/oyyn6k[m؂/TQTE:% 5 v1LȲ<\Vx:̺51#X,5y8ɦcXV7X,1<2ʥWT*111ɚ5vQZZv z{p8DP/puz ׭m-H0t:  ^޹c8t( 6BjQfBAKHEQL>J$&p4]LMxq pΎv~J`vv֖ޞn^ڵX`|,1ccGy475uFc\Nuwpg,o` UрA0ޓrntܬsf".b&bNOtK(JDI}PfDJTOD*/7 Uo>/*+᭄&>ȶc}Os5=KW;?xwXycc㌌jik\zNq{5՛aΜ={\|Nsi{zeڬt:D#y\vWQRT_D$q1L&ž=Sb%󸭝i;kq=֭]hdUTQLNv'O~iݟgdn޾ի0+[ hZT*ZMf?4Vra66RR\LC#Lf+HJLQ3Q|~ CFaa>K2p>nhDѱqX7!BcsJͥYsZǏiGL%hc^r! EEd|RS؍͵4❓pq6[7a4|l6۲yYSRvV|tZ4 IDATyh4 zCc$Ut:cjɂ|l~ j#v'O f#jl\cGiphc{#o^X,^VVIKKCVǺye\N;bP7oc2شq[pTWozAaZZqc%@V6|GxA̠$aZcQҒbrscacJņHڵ^(_GYZ z=Vk "*Vh4j23QsQAuek92'ǣVD0B9m2VsXyB!t:Gґeoj1"^?W+j@ܜh~zx{@ /.}fvlۊgzNm&DAa{v;$%%N9ZAh4V}Bsh{fl|-k̤χGeB}1֮Y)r`^O]M ؿ'll;t:*QTXkPTBg_z& QTX@ZZ*jnݹˁ}{x筣tvb]<$I*-AA@VGnNtjC%ٱ}+IΝ%9WKFbb" fyyd,I[:K+!z㶩j|IIڬ*232bO]iur$HТ(($N$IVey)<(yAPюO |T$IT*D// ShE%BlFR)Pm(*}u6UXxCm6V]2hlh7O=cHh =V]Uf155^Gד!Z)j111446a4dR[Zپm+Mi47284D^n..4 7T~U>`0(395E^γY K1.]t,QV(Ϗ"q.U  t: #qaTBbLt:-޸~pi|O&3O$ 2ӟrc칽LhT-NVZ-$$,|OO;p"Kxhf̌3h&%eAr+L& I^:22ξ:/cD6~yi3_@MN"bEL˿B&a||J$^VJ1> E9)R1LNN{'2;L&Hp8,ȲLUU3;c*陳u+b2b1|O@ ՚H]Z<==MGW7[7z C29i۲Q/zojS?# UBw=JK1XWYgŚdDEJK1RV&ɝU7A$!+Yz٬tw|F#&LuPZZ<;drrB ٬|>0 u}~ciB&%dbphnz{HIIܽ{n6s8niK<Go0RJI{l;55T~|drsY4K&v{W>_k |TnSt_/:mt AT"V:wk_Dzy@Z-ر7m޶e W]m[6s=6 lt $b4ˡ޹ήndY&Y$&PwqK|}ܽϿwʕױZS`6HLHzj7餣=w"VڸQkdge12:FGg+su Rq֔4 :rssv&j5W7oV`qG;D/\.L&KIP*S__so޼ QE"X8vb_ESR,lZH_W^4 ;wlzF\n7iidGFmLMuSS$+`^U PJHJLwϮؘsrBñ[P8L]mM6{w/+7~oZ\?yҏgy/FTJwxUS#8cE6F/ w_V+1ahJ߸~zL_ FqżCV6|en'7'ex?k._׋$I;A$YFR ;r/ % ! K<u 喾ovDx||4y rEeY&c0 VCGg"IGb9˝{ZߋN#`0AX%GGFc obtl?ͥgصcNF!99d,pb7e7ofj;oSPT-ψD"Xw{G'^_gƗ$YP\?Y<( R㣹Ryy /(xY~>^gGLjRKlt-Tc1.Xi7Ʋ#dPH$vN+MX|""HA333^a>Ϟ'fc1=m;zvÆF<^/{wB$>Snv8*Aq>.^(l߶{׎XAnpjB臨EO'%%z ޹{qbYϿsMeN9*דB7 0;—e5Wʎm[h5dgʝÒL OSZRBaA>ܥqwleǏrq=$Y&/76&ocG3>1ΞݻE&&&Hg˖:ZZZj4dee| TZa/tҾƖR''l: fm•@ EZh\.7.Njk6݋,?hhع};USZܹWO2ˍ[|W*-ؑ\qKz rO|bhxɽwt244w෿ͅ/.pዋ=wNbtl}DwwhjD4 y|q2N'i!*R,ž/|q1 ܼ}AGȲ8^wN3<~Qx<l֔XQiI vntNx$M0yx<;:>;sMU8].233H0QD4Pv ###Yv#I.MzUkw'ܹwizz;^*wVCwoo,6^}=IKEiI1F`rr OC#ILL@RTTwiniE2tb20 ѿzF5UU~as] * h4 sΖs_bafvNZ"fC))XfSNVS[Sgg PEΜ=G]M56uk9u3232$'VYv M?"UPQ^­LR,)446ӧHy6CCɧ Ѿueki z23bn޺CuF_W߻(ueܼug8wdpH#8^ * 00˖9ɧS642::cGD"T0 kbk#(?>66˅[R\?:`d`pkVoj7񣴵GȒFDv^wŪdj5ٲ)}:6SA \es]-'N"(+[CrR#{z)--O| u?zwaX(/cbb}{vx~ii;|xFp{<*+Sik ^_Qdgeq}*R$}x@gqA_c?=Z= EEQR >Vk`0H(d2KKk*LcG/_\Tl{vbYd=ooy*~wm;oOU%^j ;o- `%>ٳ\o:~Js$/#,y^)U/ë_-R>}/o? 9SWJOYHOOcbrC+[K@MUajjk f^::Ϲv{sNUU$%%Ɯיٹ" >/EAغu+ND[{jҒb}Oz{woޢzW㻉'{cZfO[_ɴ՝v{c))$&cv,N+Œ۞upƶf"*r "'3>>j5de-7 CǶu Eooevn|6v:]sl en0*0?JT/ IpuU|zBDH{Gg"-^cͪU@4i"tuu3<:o'OsQ1WrW:w ^z+V<֮Y,9Vvlw#8 \2Pѐ/GE:زD.7)JTt9QbƉ YéONGy:=eAr}f3TvZVZEVVV\eYf``ݡp0O311, 0113:6ӱ1P޾'LNM!2S  2n'Ow3ϋqxH$cf&=}rmOMOsƭN;:c+8+p\/E~Ncf^ʉ`P9#8lTъGjjLOOS,΋]ЉdQThhߒn7իK1LB;o ɰYx^kbB=wtعc{}fff{ I2\%r5*=t*:jy#7n&fcz z^t򸭝>44es-ܽw,;(_GnN6#$$1zzzcήn2e˸|ǎ13Ç0==M~~-pcMLNMQ,*)'O)(siul߶[B!nܺ%9+{> kkbBK!2_^̑CxB(fӢղ++INN܅/QTWܺs۶`2\|c>ÆF*+*̈~?8֒`hxņ]4hhzDIQ!F>?lbmȭ;wl\Kc#vE+[ ]|c#Wעh28zb^Ϭ3DY& 189ȚLf]\zVLJ'Na2y IF;{pU ?gU\"?`jz <^/9I?j];陳/>nkh4jN:MQa[7qy=N㣓hzLVf-tH7o}23IH4l-SQ^ƅ////on*)~O?bdtp8̝Gz}TVTp5*yȺ8&1_YXpϩI?MM p>>?cqMIKԌJbȲ`|]it\rÇ ڱ D]M5NʺOLb2ٹcM)).1ҕkz*+IXC|%9 _r`^>?w: >//]&--5vH|>B颥1ccݳ A(8ff\WKb8o?SCbbZ39ccL-LK[++zZnQs-Ou)unG/ը ~%r_Ini>bhsrr2f##BJb8xF]Ƌ6_h6}ngdDWZAi.T*: B DZmWݲW^A@R}t:Rm6 WeꥦE^n("Qx^N9۶nV4op\?|Hߓ'Ӯ6 IDAT0WU,`w8(--!fCRaw8b%232hnj}E;$<6p tQ]E 7P'O(*T:uHQ"?/yQVq|wQwA(bCzRSmtvusRm6Μ=/{B]M5cc}VT \r 7s9&'f24<.udaIN&+3|zsDQ=j& }OuE3 PP(ĬӉJ󓞞%9\K2Iqmj7?n߾0w:ܾ{` HSs ~M֬^,dgGF8Gb gd. _4~Ë^~9,T_9~cˍϭe8XQN'==F t}(EEtvu"شqSSTJqnc``Af=޻$I ? ?'VWj# Mس{'ZnPQLr ìY];wŶ-u/|.6"I0*Omfn߽GE:o\Wˮhh|DqQ!i6rrرm `6%7'.232\ D4 99H$UtuC$"QeIq۶n&#=ۃ理w>N PZRŽeH%YQ`6xȹ _fjRm6ϑCHKKpA %ł^GXiYN =CCô>fxd3EToB [D5::0ToHӣ)tZ-&-{BzZnZbM`00'5D%993v? GꪍÆF2 X,6Um$;++&D`d4zHT_8@X{VHRR2Vժ?255hBՒf[x+Vy466Ƹ! ~?$&&f zީ؟d"##kbPW~^{bIVO9 S9Td+ZT*5լbXbJU7PQ^ly#?/HII=ZS8H`UiK;7ٌr!Rm6j71t`f̈́C\B;%=-h>^<KgW7SؗŒGܴq==:, j)AD>.KuF~?^̑y֎`fRZRCAA`۶-tttoc4;rF^jfTp!;:Ѩ$$9~}}dǢJKIH0DarjҒb ˙f ֮13CqQ!:m[6cC$a=<p}f樴$I"YzT*Yί֒^H$ٜ#t8S4&]ūӏ痿% 1V EEE4.KxGsK+WO|7MV%r{+rV F>dzPl$*7fg]8 bfEŠ3m2IIY(z}LO/( zdrr!gQ(4B0cc j̅I^аc<44XrNW|FF&h4 )F]ZUѷ6n7Ƴx< :/b㎎NN:BH$Lp:Z>#fN18].BviB ݿήt:gsD(.*dq[;eex<=$AvlJ03g TWmxcA$& @pz(cV?(-)fz5nZzUi/WA^7qQcK;0<:<}R~i YeB+Kd~SϏY:?>]Dq v y6^m,f/ecA:q&Jra2{` ,f~oaZ14v(/T*\$*Çp n &vl. DD~^7B]IaA>իK|HիK}֗[HJN3gCHTO|LWwr"SWS>G\<8x jfFѼtʲԴE+S].a#>ܻw}!v _^LAA>Z1l߶ѱ1:HOO/xF8foJb$Iܽw׋$IFEXrԧh4jvLLg~O"1!">?wJի2ʷ߿D @VS GFz: vn;XR^\jgQx<\r[7sOxTWWaINwd`pkVh鴜L&#U6SIX0A".p(LWw*+sT*ZvNIzz#cl2җ De V'wrYzp:$і шf|bш󑜜%q]9{''&EաiuZN'pĄN:Qո=~xcbkVz-cbbZ`&+YEŒ:3b``$hVu< l6*xǧF ^*`05# ^Ed*MFFuI팏OnX wvFԔ6E4Mqz Al" :$)$bs1V\PYKD"Ξ@js[0<2dKcGDE>,,SYY+ɎJ\|Lx20QTl\Orr-__Ҵu,vHj5#=;;- ?vD1Oc]\?yߺQ쵗jt rQZRcdžEn>Bw^=))XUs_ƞ.)]?Ǫ/xvq|pdffPZR /_EJKy?EQC̬DGx\riɩӟa6(ٺe3##ܸy6Px*{LLNkvIMuj'LLNQ! f*y϶-8zW]gvG#"IyȲAX%/ϲe0x%ͅZeY^ OE1E+ϟgEEV$4OTCZbï_w*>nWocYcn$"ŔymXXrQYkoDZj*錌☙ZPOOK NGrR#QE[(FJKY0bIUߨRٟ~^O__7nҥKj"wyDb$xj5:r<̌4$h~S;xyXVJHNNj룭$Ш:N3=mlj|6* ˥޾>JK7%*t:i嫱J5:?" BB՚BQa!wĒLM&K8!=-m[000Hm&DQñ5ULѢ۶mV<{w"0Lٽ@ fLLNj)).زvHNN%%%,Dl_qp𰱉wENN6eek00SR`(ݻ-lV4 }&EVb>咞FzZii}HMvYS㦥b ۍFd2-kf˸8#(tU8ߘ`Cc־I?+^p/}-@juek '+3*zzAgvIK16>AӣfՒC6 |]ED0Otvvc$STX_ "==T}V߸IFvV& M¤ژc6tg%U0׃]n s=Lp8f VcRR]_\<`Vt= }5ɨz"0$&.ŒN%9y!W+ pƶ5 ))ɱp8MJf[Ö$IqB`||RfS,MNN+mrC8(z^˲6l<33X }6ԩ@lHDR.Ev!&I2Joluo{([s䳳gIOOi;Z=gsm-Mf>wݧh8@J%|Z)‘06nw. ՛z&cToR$(:ٳk+x<kvZZ[9o/ekik@"wv155nߥjC%\JrZ\\];u g@4GF΢KGqfARѨcu$E|tl1<(yYV (xI|MYU*Z+xFlZQ$U*QGsP| Kmm҆kcIz6|><7$!bLKqUioZ Kr2[b]AZ-PHIǸ\."&p8,|zj Ba‘HYUZ|9a>(b tύ\ޯ'3##vx(-)ƭ[Y I0 ?ϋ(ded0mwKQa!-hjnZDMu Ibb"HrO?,s?7q񢈊KGCQT]/d t:vZl\_ 2o;e>MZZ*MڙDGg LOw13JT1>1A#hطw7}}۳ ^ϝ{ٿwKW Zŗ8z Q[ Ys߷H$yfEB;sq<.Ϯ{HѪ2 A_"X4LQ`]r4E*4 Ss Sű,Y LP.z,DsqrMB>9FaJ3 |LkDz].yqT8~FAZlLO/k%%%(RN"*!ȥr=\byx>E.WR~\.Ol[ӑ ]Qh4$'/|υaENJRIWEuANe ZSSvEjQ(b3Kbif ᅪAThl`hcs_s619+F⽀_f3yyyh4z=gΜ7M,$u+ T%UPO%t$4):T|\#)":2/=!rMTo0(@ Z/_T?V?'7P(pNh.1"sD"KicXp4cK`E" b0/+*m7h@{gz_\R(t wI⧻'w{\皙iix O}H=GTTe=YoSYO>(yO _b(bZj C?ůػ`L$`/}Cb87oჽKO~NG'-/\gg <=$Ӈ122??q=}N>؞}G'{Ҙ*9?:~u~zOZ~W7#cOJ%<}Liϥ7yjM\# {6T`]L I-V ,XbWQn4 \I\ $K7 EƋ"{*Di*ԚT]!{MK'pT/pɓƤbu'%Tu}<J 7\"/UԙλewS-_]"f5l.rݛc$&XPqɓcMEA/G${*Dt**j* "txSHKȱ̡›O]gJ F5dr$.NJ E%65L6Dcnasy<3 jl6 DQ ^Nrs];wy.0xkWor8$Pjܽ׊@$\^qO=|㽷9~uuϴXVZI$݇$Iy7 F\|%K8}DQ,cr1pZpToMs.m'!Xhvn̓*ᰣED"F![(ipBFBUevRv;7nBPRR}),9j6y0ZpLeFnܼE6cdT%ioz4̟ǽ6}M~^;:QTd2ټiu>D2$2zU ZmhDaZԸNʸqUwtbZ9&Oa>7o#IPR7_烽y7p^ؽx}/ZȆ`~TLF/H(d4<]˗/e|ѕ7%q3y /%z=y4SKyZr:ld0'ɧQ\@ gQń623o fIA'I\)TZ!j5 y&QjBrJy\`||t2_m6GiS$BEjt\#Oq\1gqJ!龊:<:Ngt i y|h4UIjHiin^ `bC z:c8Nn߹(Tq>p~ګ;pkVs16m7r hi^A8adxÎl%K+~]0.ϣhf[W,ýXAnL0{;qE D"pݿdxdd2EG}>ػ"V4Ӽb9Μ*_̩3稬(o~6nxyB!zzٶuT/Af*~^p.8EUe%Ϟ#9u,͊Jb>pK,h4jQT|ZQ?G:aM\tNc96l.K:Aբjq8$D2^j6f~fy9??G66.;L+Wi:FGǰ;?_şsm䔱j*nRȱ4/_'44#-ťWx՗ b^e݆ LKWظ~.\OUak1 zT R:sϧG SPB٣YL37 (M<*G!HF9Ĕ1{l5&r)0}fcd*76kLf;}Ĭ`/LFfr$Թ^@Ty,謹55:̍p>'Yv0#D ,(XB!7 y2RȵZBdrQ?v~BNg>\c?/.j5|L&C.7㐹9qV+ ~6:*tϛSdh4R_+ rep.#rIS@QP ܾA|>?VK&_!mut, A|5=N<g˖M>s|Sgv{mh4VpM*+d/  @:::ػw/(N,V}?~"=}uEEE<|~?mm"~߰{׎{I,FS0曡1K4:sjHYh' +t:튊R?,bYp8fLBEElRTFqEŨWtH$3Z tZE\.BuuU큁vMM8S dḽ0\Ϫ@ zx\a>o:#tttҁ Sn] iX68vx<^' STTU jGyggY#A߾s];N}VMttvFYf5`]ݬ^!0]PT䤩qAa,IhkD(Z|g.$DQ嫘O4h>#TWܵ}ܱc8=f}=-r4Q?VUr}4jBςc/X,Αcǧr7l_W>|od6eX\i1 x<5 OL2RxbzJg:*i&(<5Z[fs2%53+<9*PH?yvnU7aCfQx[e41HLI:6 #t|X կ~9 RTb~ӭfh4h0{~qta*!$Iy2 z̏~W7d;w[6$\ jѨ)*rpeFغe3W]grYu5ܼ}Wq1ƢJp:\3'~6ʊr$Μ%Lb2gϋd24̟W(dB\|ZR4IV4s40o^l߽h4r)~jik  Fx U456rl6-H$zH7m#֐pY?{Tֶv/[J[{.W1ne084L[{kVa>}hZwubR&>>Zl.lڰY[aZ:XdR>+)/+#sM M -]B[{ ߈=wy\n(++gƭwjLz!˥+WqQ$InI811b];bR.\LEy9NN$Ijٶu3^#˳cVT*m|roolL:S-.*"NQT ܽ87^{ _lx>,}_KK,C%2 z$HĤ׋^gll;wyBSi$ +x$3p|M,Y,jjbu}<^ =t:APZRG`XcLL9\g}#(.|6Eq lRcBn4>~-(3rZTB.BJrFgY|:Wk$) PT*0NZƇө̫ Bd|tQD7kZ4U. *V$)(j% 3 |žLF6OlIL+fp{XԈib1sQn޺C]m-Nh,a<{XNGk[;d~֮*& 108D:f|b+Q^QzZ388Dٔҕ+Zٌ3I}]-۷mR\^:#RcUmmy9.!I==,[gY07nyAa>JWw7s8 p#Lz4XN*"r]W,gaS#S4P!;I*c~wl O8v[3fJXղ!nEN$$ǧ:@63t?S5@P8LMuã<AO/zzt*˗/@ GYȞH4ҥbD1 5jjٹcE5C#x<ڹؾKW򠧗ŋq:ΞH0Lx<aZQEN'CttW=yT[S;o/ҕ¸M.pE<͹.16>η.o*nΞz}#:2P0ĕk78x(ܺs.ry>q~zzv&" )◿ G"9zى'Hx/yQT'ha޼hz9u9ҙ Cw\qGd;FYY)kV"J}g09<!Wx6P?-戠?gLF *ANJr sUewes6r,I'?ge*엔0,Z؄diT Ϥ2>Fʊr.W1흝fbOc :w!(PŌjȆ$XgBo_?]h4]4 ^z@`0NȉbbMt8(g1d9֯[Krw̫c% 0<2JWwυ)-))x~TWFXt vl9aܻGe"Tvn߹KmMuArE\1Q֯]btHmm Xz%==}Ģ1D0*.f%O\% ܸuWS\\DoLhaxd 7)JGdpq:dQn;9kөz }? p\s_2 hZ$l6N'y4'ͱqFW^zQI/ee,ZD.'/<$l6MPTXGXzlܰh4FeedZMsWlV^~E z=l"'% h褸@jI̫c޼zl(J޵\. Y]|; yaNzx=TUVD&u5yjMS\$4 UUrhݻHR44' ٴq=b^~E(.*~7ery믾n*q2>>ARy핗 !MGGǰZ-PVZʆukXtvuSUYJ%ؼqTUZwL&ٵc^۷!EJ\.:wضu3b##8N+WNgX|lX""/EEy9$J#9~t֮YEW FEN'ZӁ(2۷mAVS\S"`߁CLFvÁP_WKSc`I X,?x–9q4y2&'uUVՅU+/atl J(/>$֯^k;eezf ׮]ͱ'qm2MNn*&P\\ng-v!'ذ~b._%aZ^ܽP(蘲J1ɐL$(/GE9lXh$VRڮ8F8yUaCT̈́":a0+v:JJf*HSBjt:k4<(jZ! ( y>WAKx<>fs=ڷ"I3? ƜFǡYǏG:y[ $IH&g"73B@>@bCOr-{],Z܎ebl|x"㡮 wQYQ*{̏<:~i`ft*ܘNRy21"9ӧfh4lb}&4j5 6dBZ-LJF#NRR)|>!} T*T;c6bhꂁ$4jl~fښ9asEK>xL&LL!H=~Wſ+E|y F̴h t0hmowI'&Hlټw~ϒNy3eʻU*.MUK`XAb!w9zSW_7^d4Ǥ믾n׿}Q^'Oez‘,Y!{^b<#rfX,58~vl.`dB00@-J̈́R4 NגNgdh<*Z!fs Z-*乜R.*<+*K  9r7v8lP(B~Vi<%L1?6x\^ŎBXYd=O 9ʜ'' @Ԣt:M.Wrcjp~@C^%Hd:|o6˖.؉SdsYchxw[eB)B֐L$i TWҼ/ȅ k!Q=9T*vn?s,-=~)xi] IDATh,Fn}˦جV>eee_>z\.)ʢ'ASߠ᫋d* 63X, zE.U2Pu:%p0VȭVBDrɠ |6^GQL>[*  l'0.T*.WQa[$&'g@6Nf^o@Qt<@XAbY9>%rzئpV=o:Xœs9N';DQIy*J!s˗-̙s5jFN7r ^zq7.\nցJ墮VY(/A ckp J"kSEOW({Z-YR+UV+p; hmkڍ̫c^};@C| Hgze NInѨ1>zt: v֨3u4ذ~Gghx7^{d2sشq=bxx_؅`C~Vl$JJ\u`Mu5@-\tYQFii):Q1b}ܪ-3_v*A@ӡTZق`Ѣ&nߑ;3|]zjn7*mllȷ?ۼbH/,~#o^n?k^_kfwBxNFU+[9o6W4SUYIrE?'ͱnl6+f!vlߊlΝ z=pm[6EEg}՗p<O%5~YVH8>:?d޼:<6aۘ?NZ}yvj"*S\T?mVfBSd\)qef:Us(jgry\ט-f*\n\x,9[9vf׿!fE+ٔrhLf2Xfs}uhHqq8EN}^nE O(@ѣ hdEWW)tg{nk+6G.Y`_je㫎v~?L@zIx EN'6Nd*ŕ+װڬ4/_;w Gdy:G^Fv嫸X 7ZؼiX{mlٴI48wt jN:bv}j}0q'pyuznO|>υKg|uΆgrNTaz"®T*^zq7f L&fl6ˊe@h$J}]-[(QQlXT*oW\n(8Z,OMr͢Bl6pȣ8L>$~\( ~L(>j'$9ہ@He[՟pTQg>Yuu<;'qX8' DQDR!" fDQi[=6 -juC""]?=]m  "bܾ{ JJ\p%K# |Rpn߹Kq N'fiN:p2{@[wRVRBeeh)))=/FVl$I ]KR[SM"je37nfaS# $W܌Ξ]$I~W7>@0KW(/+eҥ;ǔ(qu۷m7%O$je3?/wE$fJn޺CIZi\&9drVpzzQxB&'TAo[`[ټi#==tvuyd]ZBuU%\/ Xh,-ܼ}ΜcA|@1a:`?޷9{"۶t9A6_?ڱ%q%JKJ(qs $.Ls6Ν {'*[w<QL)Ȫpq7[>˫:9a_ErB[KQTt%'z AP!Z t:]h6 O]m-6Αc'EmM5`0ULEy97oݦyr;ٵc[ ~WPZR[w~H\>gll^GyyYyܾ}@ @+زy#tZoBMuK/ ʝO|>?@V([7oMS=qE$FGXn `Y0{mڱ Vsz}>znMےE3d)}FFd4@bܽJ,ljdhx |~?IO" sX"r9f3w'S}3>{Qnل?gӆ ''|`4jay3g%JZ`l6E p388$I 188ĥ+Wٸ~5x}>N>򩛧I)/zJ&n}C$ephFC}]-;wl EN6a4"C \q!/Z 8w"^=茶Rv;匌߯~sjsi!V3a} uzA@$R:N^qKRVVB2bxd׼!+[Q%yX(55q'<5)w6%Hfjyr,6AΞadtVKyY`0b|>0EN'jJ Dy[6mDT1AMM5d рXt pՂRaalLzu: >l&Kii CCR&&huZN? J `ʊ GX-Vj\.J%N5I}@Ey9`GO{׎^sx*wwඤHgEEE  Åmլ7D|2ɠ ^^GYJ553 ,##mJEMMEa[*R[[(8UUU  E1ayyn|ܣH:ۭ_w)ϛ-3dl  FSi3(""@Tݿ_^_vI#Q*zE"X$IT*QjT /.**TLgNWX?_h^}$V i똪~*Gz΂wIk=veE &O芊r> }{U*U[`˹i ߗTil^_Зc.ktSҒlV+>"cm-qi^ -P,jiWb]yI߳1;7pмb?9as|b׉DpX6flh4$Iv'dY< /'Nȑ#d2Z-xt:__,dYn7 DQf3 sؾmc:4GdO7 r\|[6}zc|u>+>^09a_<PX7 0d`08lkxI"Ja6EX, ʸǑ$TUC0=JWK/홤A;d2,[W۷OLje X1Xa'# WK 06>N b^}-qA&11b1#X"T(ՂdB$‘xF;xݷtLz}=y&3& χZQ^/ *.fs E5XWq1?y7Z,# H^Ϥg"9nz)-)!LbW>\.RxNK&Aӡ7 Ch5v@0VII \Ϥ:' NX0lV $1vc1r9B0NCBdYF#zT*F&"Qj6_NtF(s^".W1nٌZ& $tZ L>?ٜ܉F"9oŌ RiVZFfQ8v.WU*A!U.(ghv9b1)X\f41f‘s:~G{G"B;YItz9$A6gxLNr oۭsFÇ{Oz @FM6EE8j3|>b! ǃd*p ~8# ;mwKR)͡#H&S;NGk[;"d7oaZYDcQ$o:aes3;z< xq.F#qU^ڳP(htO$LF;{ƆutRQ^q@0d^}]!\!J\.<^/hT2Zx&'{`(䤏qB9 n7֭ccxd|.0hd2W|ݷ9v4NFx՗8z$N޾~2 QIttvVZ ˖. A϶2p80 yjkb?q Qqy79q4M c^}LhaNp8Sg%l)++SXd"I.pU-[6Ί39I,#2>!WIONzihσVERvj`/+[)q'ϰ{׎74/L&4S(8 ijSCD"36u&}%(2G̅5  zJKg"0D[j5TT̴-M3 ۢ(R]=zW蠮Nɭ;00خPp )x++K1^VXgq qEpXZ)j5vl6K*=\. c40T(b\r_ կq6H}!Br,w|ȇÎQI$,NM l/.dRxqD2a'1\xNOMM5R>Ϫf }C$ T[oPt266Ą݆FF$ɤ0_01O?8׮Wq1 (.*wޢFc̫h0ׇNeۖM~֮^EuUT5kVa4*&NfJ6+C|45.q it?x@W ˖.R[N8WyxtP+x7J~5  ]l۲Jnބ"-]BuU%Ѩ;=+pO5AO*.yEADgセȳODH 'EVXX]=F=nGZt/:ўvVz>VJNO._+z$A$2އه23]!a~q5ΙY] B\@4e-HD\&p:<ѫD"a7o"COwLm[y덃׈-WǴWu0v`pl/UTU]GQ\* r=u_tww!ID$S)]lۺEVK$|A!tww*ڽA4DQDZJk8V҂jRڐl}/wnnܼŅ#LMO[\\Zwq947G-˯GXϾ Fbi۷!۷mO?g׎zBPqzfgz_199E4a "yٹ961z*JJBg{;m+G;>rl$hF ;  ɧ [o$fn߹ȥtr:xtwzhvBoܤA8;]ZE7F4!J?;wlj8N6׮ /Jikk?X9V4BP@$ܾsÎ:}H$& ƁWՑܺ}m[r%nܸI Zl:7Ncrtuvőtw17?bΎεcok͛6Ӽk45hYs\~<7>L١?9^?B&&&&&h(_\#'lnŗ OFggGgbr%ogΞp0;7믽nE=>ǟ|Re]?I4CMb^tS׿-o|Ք1#:?B*՘%ix8i7}f~~Z5`ſX_{zb{_վf[pgw&Ѡ&z,"KD^~Wy:vI\@:]/z:<|Sltꁛ(Z[(ˊa|(yq^0f)T)E6Hjnd\OfU'p%T,pa]<"7_$rU!'r_1zZOXןͱ RE_+?od {=۽9?={TUqn:Y͓ˤVCUU*)hE IDATWPa0u0\No*ȲLRAE*J`UMaf&J.ps"Yٺg?7._@XX{`-uq1o^#ifnzB.ԝ[Opivkϰ;u-qim[g3;~L2AKG7=6,W+7Oc\pHkB|6֝]:OWf7i#f\x~gTefoZN&r$P-;d>=H}M>a]rw~'  AjNܥx8Lݽ(Ifh'X"U6;^k˿kLܡoh+b;o EpLM9w̵ g\85z8T*#!I`h{'Y|&^8XȓϦyG…oF\^/JЎH.Ƹx(W2tn-ܼrӅRpk, {xQv ^8 iZCQTfOÁfCĚ̊cP/g3 I%78 üNa-#W\:}O(ڌ*\=Mepg~h4^jՊ?R*3{sIYv,/6?n"-4wx36uR*(Xmvf&!.9o`䚦M'}MF9J6ں8oy8ه\9 bkphDK{'<(!sSt.ltҿeW~ܽ~mtnb6W!?ľ7/~Ͼ73 T%_06rh[,c]NeX,V6o tMXZzuW/ox @z)dp]&[( Xv44 sdRKDZ빎+&&q!WKSzX]j/E1E`4 v`޸A5s+"4J \n ϣsB(e`V%=d` cXlY.lhֵZzx,EK{b48MM%ټmh[tR@Xh"ii'ME9G>Aܱõ n#9bn!Ի03I+ganr2~vޢXa9Ks{vn@HN"m;E{w JThhTKe\p\&M0-]a_:OPSE2$Eq8]WD.BUU l޶׼?nwj=(+tm˾71qkIR8(bw8lΚtk;v7 J"RdRK4"4 FNtv0zm]M~o^C4",u.avjhk=L߽0ykr*;tv:ZmR`;uh9.䪌Z[;{]~n/^A!If25䣥Yt& ϻFݳl >~:8ɒ=XD.'W=u7"mdɿ 8]/=ʮWj"YDZ;PUMհ; x,"j7 "v^;dpNz|6/|؝NZzhe4Mg'`|.KÉ妳3=~n?fSؼ}w-{`HӍ(CH\Ȟ"I3}NL:bsk~lv.Aqw~򧨊nG4ृ`w8}^ûM%q8]Dw }C[ɦ>m8.J<)l޶ qԝobZkBxM3.sX,l6;{_UUq{!wt?ˍ 3Q( x|f$݁(4Du x$ѳy o'Q+L4%VcIS7mumXv{(ri j7ӕ" y8,c7j&­8SS3cԧ{< 774nnno? se8ۨw5X;eBM>|x+j{5I_q%lLLLL^$TU\g%I$ >y@*]˕g_Jգ@TΞ={j (ZA( XVdYFUUv;|?X[T%s/2<_zm:-`vsZq9T*#"˲ sg1ǶQEWG=qn:WAz0a(KzeSdfe}Dxdԛ'mͰ< WVpx|/?H.N02Sh_./\_ 033CggJ78xU'YǺDbPhKedYfp8( t卶Ͼ@D>x>Y-tb;o>}/ǹzm^-p EsGss3)}wQ*5'.cZd, ~$ݸ|>3~'|紴43<4H* vvngdsJ%>z^J2w'&_or119T2219-ǟ>8G.[\?fvh XXX%c_2;wH,-ŗ_fxxLFt4?S/(,%8s3'&6v0;NN|*so$Ls.Ǵ8r8_~}UU1>rl.g_|Y)BNpmocbbYQ8Q>0L_귤i>KnܸɅK;'/8r8_t&S,d987x_=1.\ba!F&\._Y֓wS4ߜzS&&&&&i/UU)0Ja¡0|N o |gج6>|smx^vlʞݻ8w^R%pDHDT.8'C\n7KKI%6܌磥Ϗ lgbr$zq\fݻflؤm66a|>dYf`S?nm\K2zL&f.p-fBg |ȖAff{wÁ$)&&q0co06v7opٻg7mmgwmVp8DqYjgrrɩi/MlqI RG__L6ZՃxbÔUp(L*P,bP(XJp:lۺhmm!/.#.2y=w];H&'`SHwמI_/[ IP`{9y |ԏC*|*/kSSl->2d$lv[ *j*,exXlkׯ?~ϊHbԂF۷ 6(|{ryb^rzmvvI,ȗ_aK/O,q(vbpM?(1z6`ǎ{\f~!ᅦdnvx"W_/ǿjp^FOw7r͵#G܌jsHKs3naϮ(8cxp3G\C' ܹ^E%SV~&}= lbrj>M=wTUejz?;?򕫴4s5zc4-p^9W_o2v&mdصc;Ҭy "}}=ކU"0]=vN# lgtwsul I_o/V /svUYp2x#ǎ# е68vͩgy9vH8%|lvdYFfg<(LLN>{ҰVd2kbX*S`E`4 v``WU`E`4u5M[F;?>Vb#}|kU*vaq}knH].C3( ET>&EPk Pp8i$ BP=@t wr$I/>лri:Stuv "8mm\tW{fOo6Gl:t{^ <5s{okc>\t:۷ٻgSw}ޗE &7soot|~D"p-iW~hjj"[R\1yoQJdC>\۱SIk/j0<4H:x7dÑJj6\' Q(u]aMpWQ 311111111yR4~H{{$Yq:r( %D &&&&&&JX2Z-b}L]%Rڊ ޹$슢>]]UaYe_q4Atъŵh˕J a K>ϵUU[#J-OӴZ_˥wr4B@81H `+ ƴNgI*%WoD?-B%IRLuSH=Je7l`14Ꞿ`cj?}!j"ЏQuvAf-KʭNdAX/E@vA31111111yhiL<*0D.!ݙ<+ †ZΙ<P,W LLL(jdY&cX,(욆 쪪$슢횦{~M30,wJaᰡG٬Ɍ1||\FKϛEQX,5P i߱ 9yT,p֓'n=0yE gbbbXO is`Kfq1{Fft`x{.W0؟O'0>N&>~:OǙLT*(J>r9ky:]4OQWXj$c[޵Q -CI-1z75'511111111 U$I$`jdrȁCș#bi1FA\433q/ ʹv0;q0L{ONNͳu~^%N7kxZml޾SG,vpK;{<`ʲR I=LLLLLMӨTY#DQ4 -kt|,E2)bhkj kfOE`W`Xu$쪪{_=>X;blШɒrb:Z&Ǻc|}l̫mϵUUf""j՗iHڌ5?\|/~s$-gO03EKG7Vpc}Ηގ`-:z787(Bs{P~֗i.,prYXmv#QTUESU>)b[\WKɓb)&k[?+j$ɤQ1,4|> vi?uOCp}bFマ!VjקsuW|ZhJ Cʥ"7p9Gw͎[wiyh[ǚR&=opߛx~VvG(z(*rcWpm+m"]GQER}ؚQ*(JG9N\X,Glq|>iȲ\r8:$ _j61?@2"O$hoksm:;ntl:tQr[\l.G:E-vgjOoan~N4vhinfvnhYVfnEaaaQ0E}MLL*vuZCxEpEZJtقAF4\}3\ÁjOOwg]o~/8޻|u^F}fQ(451z)˜>sh2\/q>Qu663ss<*o@4Ꞽ8>>u<ǙLD"Y{8dYTIXF y/,YMn311륫fq&C~۷mnݺMOwC';ܸy hn"ssF"6$ IDAT].~?H.&&&IS($%IRi6m7'|tts*SL"@4z{ K3CEDDrNzm}e熢GThjҟKKI~qw|hWEVںU)rem[݌\̮;8ګ,,,U%իuwUEahhl6 25=͖!n!8<h:&:(?^ 61<4hm߶[VfCB!^ ۾tvv٩OaPfԯ޿eq.^޻:ҫ/\bk{<#dT!KD z\V{i( $(*5M{֎\>[V,zZ*Wɲ||1~>^eMc]OHRQtVTRXt?ʲ1Ęsfcz&&&SuTu(D(ZK䣵5Z{Je_<{63n^( vnWt6@'$}zDlz^Tn׃DI Q@cvvRbrWXV*r $l.LmEy1"IRmNQTbmFqS:ӹl6$I"QVsM[u|>/B`Sxe>I^+Q(}{֋$IbzfM' ɩ3g"pZ_^|J%gгT4Z[IrU$QKxx'cU}}N&>~|L&K,h\}ZeE}0e굩gk@f% 1)NF.]fydYfa::ڸt _;@T7'B ];wpl6o~m y`L<|rF /LdY6(2.Gwvվ'x.'ܵ#P(Dw.rt:im+s<7h_}e7Vݷz<}4FP>!* آ<+(d>IdYFVqlp9qe[4AXw?r^OMriy+8{Adi wiq൯/<]VeY& Wejj\\jT:(@w YQVKihfik` Ѯi.Fl_+ʾzmtjX(I>Qkc1y')OMVq::{`k 4sOb1U^ڻ%MӨ+KE<WU@EMSd DIPX89{RLҖk=ﯻ}>CQBHUK AѸrk]CX,l6ǹy˗ 6hg|||._e֭%**PpJBM`a,:L9Kkt#:\*]?KH$55kĥRl`w{P4^.W vb?:@|>z=D!,}P)ĝ%I>2߿^oNKKK,P҃!{~}l3I,S.$)[SHRA4Z v>GĻU쏵, pY-$eR0.# B q0@r(Bt:M4-of1>>$Jb ܸ~fm۾ΠnsUDI2fEvDWWbiy y($JLUрRKBIjIR łkZmuG̤gL>쾰0f#v?Derk431]˼c[j\ EQr$S)tT:vѫc%q\8(Jv2=m8]N\N'>*j s$fTTo]c߮&&/aq.J;;?Z EC̜ tɐƥ7Îá:;p:H1K0A0UU{]5EQ4e}8>~&#Iz}AD4~ UU EpM vAjff7JOn!(W* z륿 ( '-`wa}{?jރQM!hEn@ww.UӘ>+<ȡZPV(߿7or-m~}N'Rb]^]q.[fR,7JD 5.˵)tRY` %oUUɤ3HGK']m'CU5j@mܝbIؽe33\nc2n>R6gر]" s6"] x 2>1I88M;je`;w'" 055S;E(ď'?{v:;`tz:6st: bdlVRF]..u" 3=;__ ucu{\5$i5^=27#/@f:$z+BPH(vQYYljD#*bL%{w4p=bqPrbYwn503M(>+hb Q6Thrj-gd2,DG d<%r<^ lkVx|(Z=(s\Wק}^1\;F7[`h@Xjx|hۓ11yIe۽÷iJ%TU}M&#aXp8eJ2{YX2<=HD6}:[\DY65m \.W eP}Ve,58A{cT+HCO$zzzz0UȊ?z?U& n~@$rڷ*^Q TTIRodHjo> r+: B} ogq1N)R"fGD$jI$\>M@Rd9@9 2 z8I$377$DQffgq:&&dvvIOEQ=Lw MSDh$B\& g..\9LO+jۦRiz{FLNMىڸ\ F3AXmV]ӌvQ`7F;Hメ+l7>f8 svV>XYmϐ$CS1ۘ] [س{|9JoN×.[o'!J"nMa|𱀦 '1Ǩ>V`/kEQYC{fNΚn '@$2 9R,nU;*$ $IBUUZI3J%l6+hd2"+HΎI,-RIol|EVyPEw ߏfMLVp8r45X,DW2DIP,-CC_z{yTڬd9,K? !SSs|l`w{P4^.W vb˲luuFEXx\WK&ӸNRmmHT}F}-.^D:捃awJiiiF$ bǠ6N$k|kH2롹9\+\TdrT_v뫩lPe$`p{\LMM@D(**SX-|jU#IR,. AԷ"MXA0$6lCkr`Xo{y pt:C6e!H8to:v;^}m8rdضu+TjSCbiG,bŚN'[d.=,ܺu22rWFO"O~,..RL R-EV+,Yiz*mjͣ[y65eq|]~r:;')$DWg';vڀWL_/ߺE=s/0<5>'͠(*,se@_zi UY,s" L Mjdv]At)*kl~krڽƲgSv4Xv}a~{{<>^5Gx(?wԨI޸u{uj|w3W~:ڧj>V e hMo\q6>^ UJQg]Y(L:KArټ2ZK>޽{jt:kx\`;X,6`TP+&YbˑHhTt8moްA_uq5%ra2ށ||:}{ɴ~zdj"~'r-b{.v7n ?Y@o UgW~سCj%!YeE^޽ddxp3uۅ!Dx|\\|RL&KX,R.bQ24R\ d -źT\b>&H8EJE"l}V(UVKqJ"" zFIRRE&w'bD'HPQɪeȢ58v͂Mo $) UT|L:(hdAeޚ2I)q VlY9n%5ŎTփ! SCsGX)h(V!X]DnkrE hGD?huHLW)ggT5Vkw,ƗHx7/h4N 0 g8윒ecIݵס#V#k6ve9ֲז5-i-Ќ8C^>;ȪzU 4@w'eee? @I1Qq5  ʕ* ]Bx u)J9ԁJWJjldDHU "R^jUJ4>DO<^;IjPĥK3;RR)#Nh\<lj>Y~uocG?œH`sggoGDqPK)n;+rnwީǁ8׳h}!dss~_]Mь]Ʋ,fɨP(" ^UiYdYF^˞F7 (7iZ3,ƮzpFVaP Rl%*v'*g'uKJ8C:✳%/Ĺp'CŎusKKK+ce@4E$i(KCh4]G:eYT*C@4Q.WL&`&t]G @4%/_ Hj?ihjVBZ*L$@j'+%14aƊEP72ZjD毉j9DˆF#h6}HHXXv^þ*rw IDAT`W^f%a}Wۏ52`,+xn`{I[pn%3Z FqUn7"C@`yg Knu}o)ə\#:ɷvwPEPPhT2\._@2dAXD^ $IB4,XZʂRt:%؎L:\.I#: @"Gلi^)l.T*HCD0Vk0LO3u e狌,KLR¬'1F _z#me{,i2L ׏m8AĖٕD=>` r8,CD~^N+]3X,Xyd ;w62>JfЫigo($쓆pXV.9Ԩ  @D,..BVdP"fm "(3#΢$  BU#K&q4 σLAyx5i q.0tMG88(Kgu" De`C8!+аt)(tXȞGvR[؉5w8}Y9\AƲ?ξKHO \.3&tkەDzY6,CCe|JWF$^# bhpB 0{:BJcżjHnP#όVC<G0BP@$F@Q L@:}shZ֩]M&ĥ-s :? #D;E%. <dcZ( ϭ__(2kCl_Y;Şۆqlr?g26x,x9#=7B.W`’8mW*uf}WhhT|_8,m^io8b%!u)/BJBP(1Jf B\ׅ x4evGe pMv 6=!!Q1d .(T{%gTk5ۅbyU5]X.CK]z&DuNJ.JHEQak.-֚nJ$V,VM"J|E*S;^oB׍5g4a&B!8r9-.-4AvKعv ҽ{@U#8 , MZV[mf)fi\R?ێT&麁F д oD$q$b1d.!鮳(k)R fT~l֠:t¶ ( %ݻwCUUb]8w>#?^ù1J71!NIMI6xK,ލ+SSx!I"TUED^׿‹/ㅗ^ ,FĮ7~/3Ab[&pROK$bZk4N8B$k7jq( &X; rC4Jyxu]G YM`>_dDt}[m?llX DQpyضZ+0.  _.q8,i~M4Tjc1ccÝZ_zPl}OYԪPHtUW)gxu[( p;!TU R)υ$~|e ϜRX Ag?y?o`p ˽XUL?o44dQQ$jNY?A up6wUqt܅zD{eٰmxg=E)/]@oHjR $1i 0ajdck7;Z~ƴ?B}=<0 / ,>8giry۷p%|L-ۈF1>DTj&hTe:ǹFC>Ȟ8~|]|?Seu'1~3߂,I 9B4"!`|LX,3K^ǶRO'Ibgƿw(jiZL\$6K._DxVk(}_4bt49[G"a )|7׭ 3DN16=õ7阛[1|;7 ̳QkmC\0ZTӗ{q1< bh(zK)uaVG'obb\ @$d9dYjt_880#wK/`0zs/⡇+S0Mw8G4^zi?XJT}s;c_waUDܯn8ѨH$L&LngmCU)퍃O֑ٿayd"(q?^,D"ns㩩)5U.ߺٗQJ3-@otQuT)^n a۶;[s:b*"TpaLf`xx(ۃB<[Mat6::Ȉ?{{2`6g+S RHΟťikUفއ%ZĹAqxAE `۩G RFcK݉b$ec#RY~3dYbTv;^ՙ&m[Yo^ضB, cbX\ZB@Q:ĕJ| 'wan~A"gtW\rr [=W#60딐k\ !P`$I\~l=0oc(H(R2m۠e|qzkl~o j^ k9-B$|d΢j$8܅3ȃ8k..[9HDAm앇pB8{wn2VB $uϗ/GPnw! @l_¯'o@܇h4 qxvOexʗ;4qORb R6 5xP'jH;$cp .^έ_#l"Xs卆t&(kؖ `TahTek&(Rnmt`|Ea5,FPk?m%h(#Rm9 F[T`', Tqp((/Cisb/k1!epVK], Z ZV3%(S0qJW~\([:p^ץ덷Kq8'!fyb(>#isER6Jh4T*F{z]'7Og~ (xŗ}v]H( |@SN.;yi ڿ趋hlBiEQD.clVöLk?vǎaF474-.t`Fi>a' G;5k'vuk?`-9o r=#2޻nl654F:9~$`b]ƿrIs(zz_m?n65Y3^_&lpFXf|UJ5MTݚ:ͦ/ 2qU6>K,/bhpazfbvn;FG3#f$IB  BkHXy 673 v)!Â-q,,dGZRɗɬԏǺn0@ t:RK]ׅ] x6K͟ XEmJ4W3k2|[Ź uqbuM7;g-^H$r͝cj1M5ǣ̠Z3qU0F y؎}|'zsDQRgP(@){dgqPh%gΝ}CXC{ 9 orumY/\P}8B:00nd@kY$8"|_H)E^GSSg|wR 0L$tKKVkV$1eںh-K3>`Md't}kN{s֗h4Й%uRRaޱ+^m?^\\D!`~[8#Rhg쭻 (שׂכOf& i8% blP,%+!l2Vu~n{ek+A(+ }Qۻ~1FGQU΂R;B˴/p%+ryq`?N9lߨ?;ݳ'9|'a` L+W`[6>ijuuw)b*Sf뛧p6H$ Mck; ;뺘f}wb}n`.lV0۽{7M /^V0籰'kJ!R%4M 04Ļηq55lsp"PW8Lqʙ{ V!U0ǁ D>_ ah(̾BXxPQR]wchp&&0H$^xw9\.I13;7| xWh6pwOb` bѯt 11sXX\D8 /= i0-gΞo{qMAض EaY"(0MҥKػwi:AΜ9{baaZ PsZgo4L<1qM3+lY0=3N jgv޷Q5˨n˲ CXo-X~wbY<VtDQD,D^뺭rp~aY֤/% >iH"BBfШ7p}ۇz4 ".]g"cn~##Ø܅K/#N!񗒛Y wdi,,.>Ğݻo[:PPdC zdYF6g5K/#cwKw9(C""lVƂEEӗ-ʴ֋D@P |-BDB^%r cv;} !ΟZG>˲ax044P0P(Qqpyؿ`n~j$2&v|wxU0݃> FG^Î""dssؽk;^#JRNLԙ%^͟aR,ˈ}4vP.Wy-`oYh26AVjj^qV렔¶-β3w^ !brr%iDQ:tKKeV߅$)71nߚ@'a qR< vEZG }/^,Kpci) <_JYRRBA2  Eki"wEaQIJaƸ(JNO7GbB%*qYZJ&"0*̳^o2;׻[ĽI(y{Yy%!| p9R EZ9 FUFC5v 0Is{# pnV\t9IˆPG?ukdQlvS@#1 VZ[K?VF%D"*EE|6a9O9mN8jA{ϯ.g! O%mmRsCh%ĔU$QyOk&i9/>n'Az2!qJ%»ͶLcQf޽uuߚ ҫr Sղ,T*3Ie(YH$M RW󧪽o IDATRɄe٠ԅ8 {}Yޙ^2p6 >0b%,l!`@QyȺp_J=MuqUY-2L$bѨ 06`C^>Yp* )?~g29z<+XZޭ)cttslݦ(b|v3;:5a`~~1)gT_r]A%J0RS(xR1e,f/TVlQ7ql$$#r\E`鴷sX,ʕ)ƛ8tNnk 74P[)# &1#]ꚿRYI;p:4M"_}2ȢGvݽl654S L =q{5ޒCou4뺘$-͉=M ꩼ nڙ24\$`F.Xإ+SJ ښ`ii -i۵k:&>O]+ xے)H8|>Ӵ&4 Hԙ~\.u]R)dYN,ǽL˰#˞Z4MiVm~||~Za## "g}BaYٳX@ dY‘#o_\q;ב?R˲i˲>-IsJ^9W;=itո Lv]m"0*J)J\EX@NQyd&3S\ ~mx+۸Z!ÏOa}p>t/nQ(xABsҞeo`( / ax5v88wA @R\+k@)$IBلiFc(Z e!JmTUd2TkULMM#2M4 W`ll p###XXXFGFT-/فoͩ<lȂ'~oL4hj:88flͦJxPbޟLƘx\eⱘk:e?JK{ͦ,gRαaL\%~ܶmα H J) ?9b33ﳳ5&d d`0xܫ 2hʚ%"h4550> L RH˶Q(!@9>9 oaͦz%xܗ3!O8Gkow@2/n}ngڢdeۀX,UU!Vp|&Lӄ(I4 W*[ocff!FGIȒBkD8vtɟbxhw ߀Qկ|7y7{%mVB ($|vlԅ$K A0 Ppۉ!eAEH bnntjل;&Pp( @%4 8,< C隒4G0[R=6&"T5 ]~' _? _?kGs4b&4͗(`Sm3y(2ˉ2qA Luˉ'llI$bKJZC+0F,z$/oi5eds9`I,:8lj=7O_7U8! Ǣj繮(紟ϔGO٫M/ٗ ۯuFvgouJ!vq6 *x  @]VbZhQbSRca,L&k[JmQ6060m8/~\F/\=G=LjoBE잜Ļ'ǧ߅\x:NA8{'\ҥ˘őÇI˯brD jFgG(EƶmFm`/p(!o@/gh|=ۃ˗/o]q8arA@&ډ-?p}a}\TpwsZ5uIޗan'Euru2l65fy0K]"3Kmeu$I.+qh! [g73g躷BA`@i:33~Lׇ `Y!`Tҷ;p8!/꺬D ՟~Ci6(񏽽(6&Wxٖ `PZwT,*WaӷRa6/#<0~3§'\RnZJ4[}a0 $arr@0??L&D"3gBazz38^jc(4-$ ds8)e^`Sǁ~=ZҫSxd⽚?MM:oi5ѻa[8[D8pvnWIBAg{1}AG8nY{|Ӛ&,oAm):S/'aZẞr`ǎQ$ czzcccBp"vHs8Whz-VQ׽[>!*sXLE<<:@$@sljf `0αaXXveYŽmۘf/J)6,n\>mHl6\`6]G'lq0D,v6: q;+<`ƞe|ΚxsCoo<[O@TUFFqAq\D"˗ؽ@J)t]axnՔ}k?~yfu哶1cA(HpfFkuivj@~eEPǻ,p4> B׽}9BUÌ4>S<[8,y3~R0qLJ`z]A{a0`km6{|6f7m6כ ff%M LXTO_@hox 쳈bRgϞC2@gϝGTU1>>!uFFήl0̎8ٶmȲJl8ۋzj`-2tJtd2z|uKKQ> 6Vk K%[el={>]`"$Ipa|| xxݓ(x@@ 's8iTkk_p8Y 19=p6-W_X\H `@Q~‹FUb1<?~翉|J\.39>w;C\ tJ)$e63<|m;Lacrgb&Ld!C ƚ0L_+JJ8.L{w !}ugp8u) 7NSg5, 'h4mλ8r0&''06z {k EQ@j5>|ךnV0f}ȹ6R%i}5DZ[BP;0L)܉Ҍ}\%$K1K]:&˲(RL~=WwR !75f+ŻA0K@{?8 ߵZxhcV8t !p]SH̿P0 A4 D"lؗ*|a]<0A}9s8Oрk WurnZы*c1Ts\Q(l5֣ݞm|>QdSq\.i>#uJbBD8m`{*BQ I"A68ӂRYf+VêA08(H&S0Mw.{mh-[,2ɲhla|EQdF|6me#R`EfFljD$p~VGZYn 7H$;m{0 DQl}ޖ Pf}3$L:@*ش>KKKh6),lEF ?"f6,ipD!]KaeZ,1Ke^?.L'0qu LRok8GO_& գ gYUv`x#m l6ְX 'I;B,@m\*U6=HW5jur"pgh9P(U;Yvp q8Ep8r>Ӵ`~67(hF&1l `[vrG0}RqrfF7ޏ UU!Il;>uTG;[۷7޽ӎp8h4lQcLO`[qdq)>Ls=; uj(إMhY\ׅ,{B0]f6x>߁,K hdY(Ke0qQL~=WwREQ`Vʏ µ׼>!GoFy@7/ {Cfwam=~mča6v]p8m(U@&mn:OG# CbhpMsBH5FoqpDs6MY??w> 2~ٽ/\c;}Po$.y:*4 (u]f)Mf@)'@$.v;EW}7x{m?$j_?^2q]!o?Kw޶mf+I"\_^m?F㏙{y89gqu~+ceہCaT*UL@S`Y;>&&?o\ ÀiZp];r2 À8BQ,1BP,14 2 eA(Z" f[ڎ$hݏ.f:7NwJ?ܹs8ton+^H);{ؕ]\qGA虲LT8̱ L?vw~ݏx @$ذ, o6/l >z(N> 5jb``3sm9gvzQJ%fb~~i>ؽ=+"A\t rl}BrAlbPM]\Bxe劰, AU~8.\B,idH˗pi`8^td4o$2zjq Lu]&N8j꼕?.;%If\&5kz_X_ `ztz4˲Zx{m6v]@@ ں>O7x@Iۛ`@ QWl66v87DXIl% ÀeYe t _<- "0}u]u]ty+JmR\r媿Ky5Y?{%4? IDATKv}V=E`6a3"& &1A,&iڤ,D1H#6%S|d@&A%}o_73SUWݳ3of^ݹߞ9_߮}q>Y{1ppLե8ޮKK` v z_%;! xl~;}: (%re'ߝ\e`p]Ǚ\w)4 3){AՀiSLgW0$"CHc '0T ­CiN~XwO+NiZf#yYIO$>q$w%޶S473nF;-ض8(0<Lf "-v]s ǵ d2T*tdL)Tʊ.] N!"s9dYz}ض 1 "cNYQV./8F&AIm<a'+\2T ~HQDcN-)`0@I|O>W^yNRbO(~m4MXCǰ, B^R Ks\E\p! #e4MnH+f 2ck:_@T;p8i]1pEB(۵20 Qo4X]4Mخՠk: #8p8f2hG& f[Nr=~eu];[CLÄa舢JYsj0BP@Cp4lFEEFEA]X(ELi;B!^dBf ^O0\z>ۓJ,m՚[0:e [[u4 <9)Kq˗'Vpec@:ah`…oV8.fܚHAa'a(4Jq$ޓދKf;.(ei-Ur5nܬ%۹\F{;-I|*eIp?1.Co͌q6ARJA$,k>@,`8|>_oiNO躎 ɇ&r¶S(DƽűRf:=ohp0t]piZRc&UwֲZ[8U򧠠pq3&Ž ڵ9ifm?mmK@6)k"xMUBᵂ tJ>16%̲o7sǶmuݤ}` t&i a4NOavҘd!U<\ȑbCӧyιB%>kGQ,?dw:PZ5 kzMi75_1RX>CI|&H|Jm$oĿV|V8>2nۍ$I/:mq>Chcjy pL@ >. D+@MIW4 qnOJ9Dx$Rɟ!oBPpLWKZ媴~s&\\\iNk&g5dhh+@i8fU!DY-I%׍-48V˒x1m ?΋E`#qQi4MZm `tJAaQK)%c\ ygyή yΙSJ%14M8g绾> 0$K|ϧusfW6sb_15G-Ɯ}C6KxY#krcQg1=|^%|9 㱰Žchtmq<ϋ5M506۲,,.V8jm:u A@ r90y/^f0Z9XiDK%&ZFk&zb1Xݕ\.#^_ǖx$>$7 ]⏪ab\(V8͠Z-' 7u$vT@~lp{.&{y C4-`sS_|E:u>ξ 1(7r3b.{Ph4K8tچx;߁t|/ #<.7'O8ɽ֡S {>8c1SI aQɀ^ ģH_\V'<Q edBlm5 𕧾 ;_җG1,h,!&TVROZ)-_X(R$-f[-(Hwx l7^Uo5P|ӧqKQACӨde9x޾ӉRE<8a0 bo:N<9*lT¥Kd7իfa+@WF)4mRK6BC/kG'ۚFGS'4&81f&Ǹ13=c]ZV*\"o! `NuRt@b}Mp4*yBn/\CÝLR:M;}2/>$7 ](8c٣n !Ƈ1f3X\\^}7I4MC߅mp#b{{ B'N;AXQw9߆0x;+DQ(J NN 1eT5-TV,)V# $۹\|_ C|—R2" RGŃ+oNQhkE++$ιx@.[IIvqn88 =P:o9jf5%zA@}_o0 2.P/þx:|_mvvg3J dgc<9c48Չ@Dd2 ,˂E1c.W&?N%#m Jesu9M׭S/G~pKA w um ͎1lKo8 lvjaA Ч_M4Lznmձ=V*%Td[xMnZJNx5\<{Ytz8o)ě!AJiDZwp6ƕʍǸ:1>ୌq+[(|3,`X]]O, k:O0Q|SO .^T%[O%Gss?? N~}s+((( fh yin^gǩ'CB7ty;jkN@E.z>÷监/Ek:L@Tµ Da'Ob}m]*Y)8>u53o)=߷V.&PAAA(Rt f+x簵G~_}i \ ^/_C>?c>fg{=t+/>/Bg>7?Ο o{cG= j(=W^¹:1lll%ۄ YjMw!4=)̶um  G{#h;xgp7'>)-o_X?^/ qhamu% nq)\v C=wߍz~x[_x[ z0 vz:ɴ8J|OfSnBv^K90EѮc"$ژ/B)<8d2HmY7˚FyM s>T֔zEf-鹊N\M;r8{PS)K x%>cJwc`bR|>Y.--0t,.VaZ <;\.ehbuuUrYX_GVXZ\eX_ӟ 씍jaue*TPP #7{ rʝ>!!vRe~oϙޓ#$w:o;aso7ۿ-ŬޛOߔ}Dp8iիWCAAAAvR 8aۇוKTk6R)PJd Ӛ?n/v4YG/ʥJ_N`ils%Tr=8>0@FR_a mO|8F6C;%[[uiZ-K NFǺn0 8#ZB2xg}i0t¯bkK,^1 LYCAa[ 4|R4VX,k{+`}}9ɤ%~0$޶S?oYċ0t}i'>51 -4gx"<38ޟH`grI Jt fc@7Ƴa_mx1\ׅzp]v{n!0Msgws3q&N8vB)6,gr*,Du"B ?lZLӄet0 UuAEz}իpMwj},--bsk a`yiotTkRe~ߝrTTʂo ^hX̣I%]J:scwzK+ ADZC>G -ca"c AZ*(((lPJq!d2u[[r܂n0M~X6HR)r~_${촍Nu4M !Po41&ga۩T[9iiu#rm&x<2OH qHX>ٛmBoMkk2Ƥ7f7/s=Ͽc9"4jECGP,0z#^?(ڎ$.QT* }a c䖗EJ ʿ!ˢX,lT,,eDaJaeea0 \[_CE, )SUʷw p + AjC}Y]BM`YV  iө:Ea10c8Nt28rB^tz"l6#$OpI\ 1xIRx'{ą/o??ڠ%=N O`h4})dB*9eM`\f[btH COn|?)MY3e]Zt0Zfc2yݞX VVv?kɶ㤱l'񶝒Зx˲$>7 C(xM$1&]"6m{W,eݕ-/8@KG>j9%ryC28st<)m;p̎UuO*]05??zgOd2HӉ`XDc/#bILO)q~Ux$޶e}7MC(xMv}1^mrs C.-H8x=>cI Qq*%X di1c{;*((w~_F9]HfwН>A4:gOAAAAh2wX4l9u$:c y+\."L*^@*BP.w,NBaV7;E˗u1wkZC48(1)َeɤYW2񕕌v÷ noẓ Ed2i2O=O=sl;NٜDQJ1&H[lo}=/Ώ cھ Gqq1|?? !4PձP.4&chƹ`ccqX^&ۮaccҶжS?oY~ iH|Fq,RK<@$i,..H7vTR9qk_1(no#։ X)[h'8 \aD<:W9+(=fR.%>!?xo)}7MS0xa@ u8w|i!P7^Ӛx_a1vcًp퍱^q|ICӴD3!\Z󽇁^|%좠!Q,p?{+f?Oчg>9_O?x;c ׯoì8Q*.e}NKKh:`6}OR-i<χN4iJahAuRDyT*$q%H<^tf5z;H* }qCY~źO4dCVs{7_ĥ+W /¶m|泟Éh[E\ėk5S_}Jy0ğ3N(J;C?|WW$-ӵk["TOܬI>Jye=D֖8z䪴ŋW'Vٹ˗7ic!9Ok4{|_ٳgd3!`ii׮mjPaW4m4N \tZ)KKIz.NIpKeYě!aJEk&bdB$s>GїvnH%|>+zZ‘B)) 3!4M!$z$%DjV1\_>jx[ŕ+Wtq)s|QrXYYBGy_WX=r LMr4B4B}Db6yAt<̳8wZ.a(Y馤K|&H`J|:Зi< i^5gjOHaEϋY9N~ %'iJ7VzKZĨ벮qlrb</8iZRy$DTѤMp:y!{=(JX]Y>O~ h3Oʕxi[ӲP.X,…W0 e^TAA@4躑I(( T*, AXAAAA"V%;{X#;f4Q*XZ,--%~tR|:c! 'u/Oc8 B&%]J6/۞dA0R,椾 ٬|k&yI۹\F}q|u Bŋtz+8{l>?RG}ۢ#Jx.J 2LY>p;WpΧ=cO<A'ܳ^?fjnn ljzCH)ʼn+6c /oH:&m_tmpy`t:ak_^!MA,.VT}5nw6xҵ81y{ K{#}?ڦiH0 kak d[4d2-U3\Rlv{9`rolӍ䷬K٬#}W0qT2V1JQKai'BfJЇأDh-D ,o"%aHoggNG>o֝>]JޖA¶m͂eRr!u$~0p%>%|=~1xB&74 jyk[[uiZ-KGޔ$ɣNZLB!7G;L㖔@a&eJ)0oE4*%,x928 0<!c39T \}o~H­SiDt@>W h)PM<\Pw<HћP PJ@(Ep{=0X( p%ίN'cL{kv_d ]E[e ;zu=ٲF7:{>CqDQF9z !RYwW60 7¢K5&;Lߌ30Nɨ.,f~G~~pd}Cot1ቮoV7 k0 ]% J>\ߟR)K*~ ,˔ %4 H C(%^gˍL)xBsʑJYZU$oX'۽ZF]Ib|#17?r138q!5yضMDQbV]ױY@? p.f"X G:1G(#D!%`lݹX,;> 0}i}6H }Wi[jzV۔ %~vAחxJ㻮7s| @c:*mE' l"h~jY՚sF7VfGZpP($N'Uc:1.H>G-cqR:URbdZosP̦ lE8! B"BFbG9t&Q &FGѨD7P% PȁLݴx/y͗jd0 ,,LʁaJEkqGkڗ`†1 C1H5 cTc6zm"f,(!Qzic:mKɬpu߼lvRnqzp]O Ծ 9~0;8n66A׵dBVM$BTP JChGsxJNJBD$L$ bz%G6b&oJ9Cs$ĵ>VgkJ+0fya"!*,Xt}z#XoK>c4r,bܢit:=Zr]*eIZ)/ˁ (NxMv)y؜4ϹBfx>Sny` /ӋK7zLZҫ'U1g|KBC"]ŊI@Z#7QW qf?L8~(i:X1AtӀj+n!f~??|=U6.>B,~=Ӿ_x?@R. }b:pR:A{ / }?BP%F>LM[ ,:9&EՀ.O~Êe:4X8D;DGzX"Rk7ITP8pd[\w*xђ/snw%>H|G=F$۩%|?%1>g eZ6~Gٖ~[dW{"Q71.Csf8"q Q4iTJF6}~қ%dяA5 Y+Fc`bze-b#c0C3 P˜Z)3P,qӲ0p=T O]?'?iܣCqu,s ήbS|a8Q|_E޿cx!~oɳmo<| x4.|_ tE;޽ßm??e{UO?g?sݠqt| !෱[[o|;.Rï|RnF>G6a `8ҥKwF~oP6>܁K ;@njS>!/?,Nqlx揃P*aZu ia0G(B0 g;F0U!>,RYEGid;N 0E'_ކG}xUŵo˿=ݍ.5c?^|e|Wυ&{xSӇޅ}מg;tFG<|׸\+>ů"yh4VrFcrxy ['קV> v syY͟'m;iq# P^zoVUn=l>vqQpZHqDc}G 5F! 6%H@>R,RQxPY^̩{#- 0l6{1W?a\0NO~]/~w [udwĖ`Jo~;?O=UFRST;k(XRRml_e-o] CBC^/䃏㳿aړw>u<"x (W8/ v:-zdAX73 OWyϣT*byy׮]CќKQsJ _L&]\c D3Dzz#9F&m[# ~ےm j5(8F@1ٴTzכByEfl6-#ۑq۳1H%l@)MalLnizr J 21CG?Hyt:Mp] )q ۶  JL1c {x{ƎrۯNW%c2r,IqQGb}C^ò&ymo'Mଉ?p@-V'(gXp88hZF J5PJ:r4R מ?6:\^bU0Y9 JA π>KijsAI(ŏ|!'ap(/8xJ>sp}?`q?MuQ|7' )"Na a!?~ DI6jJ)Μ9y?"8gX[[Å p)4UP3<3e !0ɬy4-DQ@̎rYua !;4D;3A(%vr(jG6m!,B&`nR ez("v0 e.\E*aXHԖG:5adAEBT`Y(:ǁv;H(`l"BJmRa ,+q0y.l;|>?2i#bT*↿ g m8NtAI 8"bF=G!z>N#24! .MbB`Y)DQ@p,LӀyxj`qDGM,--I(8w9?x})VK&A41IDATPeR?&#c á`Cסi:JY'edMF.tMC0@&Z'>~6m?j}e"$wiu=pΠ,(%6DBh8R{œO> @\%$4M.L4Mq A]1!c, %0 as n`A ch˲cB`۶tT*Jir{A$P:y'ǘvx ~ o O'M9ó 񱶷}d BUcǣ8?p}Ĭŋ$WŹ?(8s,8g@Ȏ@m߄eMQf`"%~!nB LDQJ((%6cDQ0?Q;E]Gƶaƞ?FO>$& ÀeY0L $w΁`$:a2p.Lzkٳx9G>iz,x`YAz4 |^( (vLiu] CR]Z~]ב!p?f]`0mm} aV:F$6c*1:!(ʭVyr~ NJ0.LD&AEv]1Zp|G, a 0tXV qc0$c$#,8F*%Ƣ\qnryd9%1&N$5:u iԩ0M3m8wnzMU> o}[nwS_b2tK̎}l 80N~qq'ְ #d yTӠB0u%ЩBJ2 Zt"fLS,Uc=@SqOa%>QF`,NVᎽ' 8]1<<1^!|?eI?Njs ҥ˨j2BEp]W^Kfiu]|v%Z.uQVF5q8LSXUzjsJuc4~2 +WgiZʕVT*lJ|=T*4DWT>6ȫVMkihZ2[pmaT*RvЇH,lmm%Ղ5ZNq⥤FTL /Tq3QTpKIU'ϣj*  ".]TdAJLr9\rZRd24 z]躁r$ŸX,ѐcMf2t:66dM(U8"BK(/"+mIH%˻]vwt 03`^2@q2Ky L#HRIUy)/ToD<]d{ҝ?xI^H,?$I ci"ƪh`,CT( CaEYr$Iǰr$>nD"}{կEV6h4B4A,$Iō>|۝hF#ӓ k>z9%pvVsmgs6uRE(4Ru] :,4ME׳?p(ZQd* xq%`^f+CQ NE,ȂXhb`4Ti26w0 | UҵѲPҮt 0'xQ1 !I8Ç~v;>țۅkApC8)hFU n:pj`&Ëkskoǂ L0:b>}B}w.t < &b5RB\2je zGC7H +͛c9 NEě|̶ 9:iݰ*AJ稏g9&ZeZkcH򁓚LRSMeT0KaPKˇMn;7m[竿M&-}qiRPfj5rpk6 5dSi=q*vww2k8dL9]NÓ"mqqq]=a(JcH$P1vwwX,%|p8l'ӘgN}a0`4oLIEWTUio o^z\<®"isaɾ !`Y`28dGLdGμ48qiga0(@U'7h8m 2&BMjB!_eY\ BxGG-Պ:\CҐ3B X#YSq<!ˡTG<GZE,똟G_|>p8Dt|@!ਥD"A9ZsenwQv3wxqe{unɀv xpký~ w{qzzVj}@4B./58A^ j$t)ϒcAȲL234U15\*ӑɤFAV\)Mivc!I",㱂=*`c0 Ws~#(G:j~BϒnAJ%VP7DQ2@1a6C0P4NDyj^g2۷a}7l.зTzSH\חa;}ޯApǚ@pGnX 8ι;|o,`YfgۋB;:Pmm]P(`ww/'''8::`0(u r;aZ%|> coolfyv]loD"$Yi( :K: N8ӅBa>cah/^bn{{m$Sr9tVdl~H~VԳb&zb(DQD<'* S$"Kے]4MrjuH(r8>>2#σaSnrG0 ;  l"!jc8e RɁt LGX\\Gvy( ED9'0QA@ee<~JGGG%^4MCE!B ɻEKz+#ŋ?@$"a}}/oH`8|wvvvpq1i}xj< =RuZ `kk "<,>zDx I}U[Nsd9 CpGhniaY{VDQaH$h X gggHR:ߎ_2&|(G}h4rRE(˗x2*666*1 e=szDڵ -H$/_b}}}FJ>DQD\&HMjhX]]E!bvT*(Jԃtz8"t]`0ׯQ,ZK׽jiD1zn'^퉡^|>ZYO4d- 17bqqV o;+*eP( Q,R$&. E"DѴ ,9 00  /q\j:шd3+W `Ǐqzz I`ii(lٹX<358z-~=_D4`0 pvyAp~~bZ ;ggߛ[8??ɆT l$IhRdQX\,8==nB_ \^^$I8> BݧzNqj5MC|d T*H&uh.N;+h""QkѨT*HaTׯ EhuH[9tpO2 βf2c;"7͝󫯾8::t]_|q1qrr2u2?T*,//\.Z"O... L<~0!RI(t:MbQܓiT@e>-4MG$8C.60}m@{,hUP($"9Z^+ejyjN^&OnI(/ ,>Z|`ǤMb'K<2@A'{)͆}s'';/_kNO>5~%W{45M{Mlm09Gsk/_m#AxrޔPk㭭-bx|キI=tTp`' %)2Aяko>nPDZd𬮮"bH&X[[#|"_eR\. mux=<|.X1C 7hs=FDb߷lr[[[|SS8}~h{{24CNIFP1Lz!'q$ ZMx#7aygϞ!#R`aa `oz B1 ׫|M%xS+V\ӓ.FOGw8rInϑr`e%OY.]s[`N-&6NM>f N \koj1jcO3#5]雄2#(sJ*5itjipkvxSʯ tdY\v{0t _~نomm?~3>Ї>=e( 18'!P0te_~P\!汪ހ'VU5.",ʴhCyGy V#̱, 1 eYv;_L] L(ڭe6x 6rEP|Ço(ґD'Ç>|t}Ç>|Z)󪠹IENDB`gammaray-2.11.3/docs/manual/images/gammaray-qrc-browser.png000066400000000000000000003571721413310443000236260ustar00rootroot00000000000000PNG  IHDRE pHYs+ IDATxwt\W~TBF!' A)rZRZlٽ?6؞=sg<>3;>k{VVآĜ3 $As*Ƿ, $bsAp} S'&UB!C3B!wB! m7008UB!g|a ѱ1mtwzk%A'NK˾|>p8fbbr=ֺM3v~C!7f0 q)<*9@ʁ@UU~?.)׋nw fsNUUn7v=_IUu59UUx<:5>1AWw#QUׁW2 :kuoĮ=8}.UwvK oԔ~M-$n7.kxp88]zٻӠB!.Sv;qBqZxinn?>;qaFGؼi=}}T\DRR"#$%%x(+]lyR^/Kr|fo}Yrssx|.hhhbxdQٲsymuz>޵W_~ V{'@ll, zBdge1<ήN hnn!%%VIJJ@FF:QPt^/#Zlfu׻g~t4شa=mm)E V |RmY-%,#//@ZN Zd6qCη_J~]NA144faQ!,)Y¢BZ*T^y9wohd5wEAӡi)**w/[;:x.6, \vtp'FEQF#vٌ^#I5#`NKfF&CCLNNwgGdú{HLL@Bj(^ _{0'( [y}_k`ZF%3#͛6Pt m{{zt9o=!Bsz9[Λx K^n.˗Q~]]dee I||@͆l٧cǴZ-ZO!_iB!=(B!n B!0 !B<`$B!xH(B9UnLNNx*B{(XbBq+Ix~? s*B{D0K@!ļ. B<شZ-By&B!F\wwC] !BGp =EVV&==@zZmچBՒI||./  IB!n9@UU9t($%%裏ހ^?} @ EQHp`q:]$'%bXfb|[ ;zwKRCA~?ͭn23҉ , y9\BՒf084LFtPIWW7qq'B!9svFFxm\xUU9}+P][p8ihl.sBϜ˗+h'tvvv xw룾NcZQU˕WhiiEׇQHOKU(1>>rQ KL C.!&&X%  !~0PUUjhmm% ڇΫ|@zZ*ET\@]}/"!Uɖ͛X_Px:::ټqÝ=;`гEQ ^ >>`0@Gg'111LNM 0Lx^zZbAףȐM!BܼY離g O(Nz=9لB!BPxXV~?I Ӆ(˗ˮ={ɻO2M-j(Ă3w/Z|5N淋c"W*fik!x@!Bt !B<`$B!xH(B񀑇@n;>yOf`i][aZ^RUԐmA16[ !=ބIIJ\Fq{\~TUl2#$&'ۺ!~U`-G|%^qL: ztB̯9>B[[;}eL/jhhq 9r:[J ?Sd-B!z 0zllcOq >6ođǨoh^xByx&SyO`Ue2;ba T\Ċ#jJYY<|$6mXرİiz.]>Fa w !uzhZt:&"`>ؾî={(-]Jk0l޴r26o¢:|deeQ[[^c޻ŋPsillzz{cמ,-)!3#P(=Xd1:d2~;q' P[W?5?=vsz8gwP~btƩ?BZx- |>?i(BcceK0 ~FAOZܜljjtt:nL(/j R"5ƪ+8~$Sv;}e[Z`wѓOb||z@ @K,ȧIb"##hhlb||$ }},**$11ѱ1jj&#=EBbSOHKKBN4 Q:| fcTUkg</v%%%O2y5L40>1%%./TKNVxN9tr\|W{NZX"#"b69x^'OtxwILLqi3ow,"F iBOx[84L&SxL3O?Ibb">NiK\%tvut)YYdffyA7݅h$3#@0kL-^I7 fMMH Γoq̻,]R¾ &:ϖ͛Xd 445TΝ륺8>:_~jyDGGs>DJJ2o߁VEѐ:ϵ> X-8!'gߢE259EUu5(BNvVuև^rҧ_ED)XizNiDE ?snb0ذn-@}C#qq,,*䓫˴KEQh5-h`Ib6_(/[<Ε_ ;;8::/^dbrE,.^hfKcoLYR~_Chnie``ee,^Ltt 2B_|W^ U| m2HLL ""KL 6E 244D `ѢEEl8 z=& [J EZ$ǵ>9CQhimyzLH0299),gdt.,h4ir0n)DBB<0=N 1w%UB<&S` Z`ˆ `? o`l1>1ItT]7-?M_>M5ZAEB=$I:;AfB!M[$::jΔt>o<`2B!$Ee8;v \z !B|mیU5Pjjj ~zMDDռ{ݻ:|& \zz{پRg!&5u?o )Pqj'9[n7ve|bVKaA=tCCDQaCA^}%ׇb8N>0F{LM9u,Z$})*.]KL oJ]}+`XxϠd60 ,cYO{^6l'}~6mX^g.T\Fw\yں:[xh5{ukנB5Cbt:ソ>GbB"}e #KP3g^OtT7XVVJ]}=cc\\ɋ/|ҥKp\V\A\lliX,y>1>ZRm·Z9z 8~n4^{z8zKJthkJߺIe6_GI"V,+#22rƘ `.󙘘8 H0qwlv{/dnGTu:((`0H(x BqϚ3 JMM`0/==͖FG=ZUU&::O*^h4ReexNdžukRVZu:t:^ӉxBR1_qHLL`捘Q?xh'soF~^.K$vbΝ155ũS,g`Y@dD$F Ryҥ%;wtRh!lĎ}Glڰd_HcS|W_!Isvo\_h0F VQQa0:TU3>>AJJǿi Z.:?/769YYq{OLΚF5#[6߂*;!z͎!"/g=AWDs9N1n}}agc3;T;!?Ixx^::;ݿPN^_x4l6?ooΝU^ r|*^N/xp\=w.>=# ⹺]P&󬱩7/n!xp=F z7Be 6P +au:}>lڀ-%Z [l EA}CN'H 339r>(3|ۼFCZZ* ϡ_5Vk;~_!Ě+yx&j,))`:Edo_o}L xxo~?z^XR'<%!Ix z~qvϿ7RRx[Dr3دYi4 Fҥ,.ly9ׯKwO/wM{s:=9s񭏲e&~_Skٳ5Xprj~zzݧBooX+nHfF6`A/T !ujtdeepҥˆi>khx+U5. .6GQV+ʶ IDAT5?yl23Q,>d7lw|D\\,n9 qf[fCm}oوh %9eeKپ#@!3#$JJٵg/6ҥKB/7g*8CDDPNjNC(AfwwJ fK0>Ξ:V:PHeꕷ8B<$n7g]wOy ( zNj ?Qz*x0 (B   KWr%sB3TU޾>F# 0>>8Ʉ^fK Ɍ1HRb;{H(Boqqqvl!B̿9PHerr2c<YYdg122-FffnZOtT` B؜VaA~>m\r^p`4Lgp8naEQHIN&99;P}!Bqh4(:FKJr@RSm8NS>HINtvIJ<7*T^:8>kfWljn!?/w^446QTX0B֜`h4O?QIQ 23Y-ˌ}e7.t"Ea$;z]#GCLMNNݲ2b|>#l7v ahhtE:;AJK[`X(Xz}\<=fJr2`h:,B|l*RHLLdyPUCJ^Dgm:44L( E`\.17@]}>^/m~Qq[w0`^[Zq:vwArÁtNWw`jN.q2+CAv|1u;w 5u=W>w&&'9u,zFCrR"11RᤩYB<Ⱦ24DEer[( Goo߬mP[z GB=vɞ>brjOvTVU{~::;9{I__?oA*TA{fߣ399Ŕ}VTUS[WϞ}q8ttv'mmm_~Kxյu?p*9|'Nwtuŝ`2xϰjr&_|>?﾿yylܰNKbB6QQ8?3;{ʗ;K i6,V +.q>>'ZO;q8!ĽBVKZZ*iiXvt~1h4h^،:499lRb"+*㧧vyxo008@DDYY<Z-O9~_HODQ Byy<}h5}}TT\╗^羁lWYn`}F\S ~;4+GuM-gS][7P7w|޾~.|ɽəs崵uXt8VoEB{w uʉԍ؅;BJ 809"33sشa=Ϟ#?NFݥp=,ZXķ?(*,dӆO`0^|G(Μ9jo< }7425ev( ZF(JRR" 4xo?d2h~&o;W[7o }<nk:5KJx[dYz=%O~Duxe!xI߻E %+%/e͢OYPbg+cU,_V餰ӅF3-]RBlFC^N.{~s>GjRgm]^sK|T222ʖpOv3>1ɢB֭]sk(V5 M_`'daQ>tkx$n'-wϷKmt:ZC ;ZwP_ݥ(Ƚ#wA!B<`nKxQB܃ԫ?Bqw3Pq rT+u9|=O9s~'OZp8)?_q3J7S[)Zc ݅b^[)9ʥ˕Pxsa?N t:9v$ p:]} 8N Q !})ZXHWgILLN磹r?8.W208l&jaAvJ=cư~Ue+UB! f`C444E ilj288_AFKkYTUrƲhaaxgUUgjj Sd$ŋR]S֭QJ. w@ Ǩ%/7[J E>sn־^t:-U^wchh+m})jjHJH`+fJ;h$??̌ VXΥ+3|QU.::0MXV*\np4Qثs:Z,+]JTTEEE$%.Kep:ڻsqZ7ghxytxq1 %Pqb.׬sl|̌ J.xQ9,/+T[83gi4Z-- SS[$_ !l͛[m}JSXP^S?NcS39,ZXAobyFBTԲ|Yiw,f` UedtέrriL.T\ܹ BAP'aTTGuMM,.^D(#qA}iQ4kPQ#%Y+/aw`q:[:۶K'p:]ҥJF͝NŶsdRS0FEvV&+zJI SSWǺk'Y!kf}YXb9KsK+ M\imkCU!?/7_aVX>)]Rڇ{.BӴw_ONv9Y$&&`0HKM%!!EQPDl))hZf$&&k^CaKI!--^` ==?{#2Rh{(8Nf,ESvgg҅ؒfmoh$&&HOOBE HLH,rsIINwEDxBE˗A* i,)Y dq1.q'&ٸafx `ɫ*ňku ~]rNGWw >i6qݘ&Z cERb".Wl ρYr9SSSwtyF:ٶq`kl6Ҋfúf0MZohbA~y:V(3$%&e6Ktt455$'';`pS111X. X̵B &\O⦷SXX0+x~_~?b}Pq f:HOO#;+\Bwoʊ[/Ƿdqy^O~}Zm=B g\O˽2#ڗK! B!xH(B??HPϬuOCn6o0UĶss{nh4 ?.xVFC-\G[[;!\nOo/NbYR-,O1 !ĽB΃/|#ΕGQ/+CӲxB컯(ԓz\, 2-Gallֶ6.U^tZaTUur%S,.#x<^8O=IMmOv0Q!z]N9:ʈ@a0% 8XBVf  QTXHNv)),YX̌t- /(~O_dz=Iա̗.e \e9RIJL`:uVV&dSxVfByrټq3>y?!9y*^Ʀ;]7%FƮ݋a|bX'O ꦨ?k 5uDGGSg| ήn>J r ,X38xH8jJ@Ńr{~Μ-ta2 =}e:;8r8@:;l2(+1!.8EuMBR_pEGG4+)2U+WPt /G!|A.'Oc)Y\LuM-ddx.W^!+3)즺$]wtE}C#L~TPh!/Tp\n7i.EdgfQ4dtIɌ\Z4,"M$%&`$%9 Bjjj~6$''5Zb'++Xrsp:prK.`!nEױs/Ab=NnN4 GFHYR=.URTX@RR^ǖio Bj IHwcdtZ߶biI Ul޴Z e3׽mEױV++W, =;;l> k2sp m33M:Kܿ4Mk^gA~0>C\l,imkܜlcw~\n7U5:uƦf&&'p8$%%k`phKRRu|٧iwgݒm^eI'w~j4_o[6q |>@~u:FբQ N ˅%&V>h42:6FbB1B!x( 3C`@ @(Bף(JxZ^~4-:P(D @ĭj"wE,U[w;F!B a]۽w};;a B8ewWόxduPK$Z(O*+"2*2W8>'c6mc&a\ Q o+E:wr֞`b..._>,'5A6 O w0|uvu=ܝt-uB|>ߙ~'8:neNVvncrqqqqqqq\ru-s.)/~lU o{_oy\\\\D\S[oVQ}K ?mntP*?jZ>\?h[ܻ.;ΦO>eAX`0@II ̡ͥ(++eqL88+W~hIR٦[{D yhllbps,Eߢ;ͥpx-];)WMfNx]GsK oR}'?Č7rx-C +[ص{l#3=j$>'Ƽx* rs/B|̜1};z8G%Cc.~Ǐc\;SرXD"c/q##,=~{@*fl-;wes)عk77~c6^WrpVJ<?M`˫з y4/1lN8fYqcpxDc1Fa|C',)Wgj~HKK/Dm N<=_]]=Z+Yp'/]8GCyi_tմ^O?Aec{m"~ʫgDwJsK3< ˖/G)=+ IDAT{sח-۶\5y{?ߗ'?cU:]Gkkh+W{^K'\`H>UUʭq ~ zqVts$g7L%kS__i=}H$p;w4LJJT[:4651`@?6})#kYv]zJː¹m B8t*E=AvK]ƌŲe>pC 8 h߿/TUUH$illbĈ 2_Q3gLO~̚s} ~sϿl b/le],ZwgoCǒE|Jf^mXXIsK ``/{G4;BLZ/%r%R0˹q/A7A/9f6---H))-)vH"ZZ[m0(/+#L֊ x<4 ٸH{;0HP(H$§mQSzD"0d)J)=Fccǎ=Уuuv0t۶x<|~ڣQvO<m$I2#aH$BiZZ[yKV q-[ws[0r\]./+ySk\'p wp8SӵH$B<Gk֚lڒ$ ҖI,ˢlPEoٲuD۱wĺ;ϿvڝJZrۏR vT*geY,y~g o>XmhJ&CeruϞ3 ߽=eY$)lUzKA >tH$†0~ÏVb[Ζعs7%%Z}p S'.sU(eάJiI sfС?xÇsͳٺ}6~ ƏɓC Ƅqc}1Xƪk))-ᩌH`}9};xyF)cFbQ9ǟ$r熋D"d! $K;8_S]ߑl OW gL^[oٲaÆ0~~R*[봵>$rQSDQN>`7z[ZYSM,{صn7~nƍxٲem7dR__81mu47=rwf70yjjy7w]w&/*--x}>:̾/\D(oJQS䵅Ui)+-G7M'=]{صk'O2\ C+ sZ&Nm;oۣ۰e˹v,e )ҳ ],sk{f(B u2"WhdF{K .Bz>1Hܮ@"W m;+}( /z&]o~c%|^ʰ㯒^z8KدRȜDڶѵ (G~>_gR)J>q۶S}>0n:CJLƦ&=FᔕRF{{ǎ3vh<Zk>aڣQ<xh %`kqF?t~bF:MWaVh_E'zj)|*P񅱀?O.󡩩۶&*+6 =s}6mmq؅i̙5 ۺ\l߱n= ضciksng.Z-žťa@ϥydDdn=8f{v/ףς&Ƽaj/u E8 zp҄ eWPVVƵ\ƪA xvw@jZ~kNEy9^v;w&pmrv\5`ee5Sa€6 B @/Mp.%S|M!8<;}zc +]\\\\΃K$LzJ;+{9 f.H$0 `B}]_\.j`?/lD1M_xk֭;rZSJqUߞ޴KE\ss3+(8z+/~~Hu>X!|i-~ld*kAqqޒs߾4 Y~m"v>^/WM('mY<#|r5'ZSSSͼkhjnf߾Su y[Oa&/ meˉb<ì]{ܕ{vo8?wWK$)z>RB>d?~l%+W/_Ҳ,|g 'N8CܗSwۥK<9eکk[/*@{VYYb]K],})Ks' V& ̷Z~]())a=XIy(;V>;w*"oQnFs*P 6 )!mY$I,b 9q$FCp GЯ_S -qS*F-^>| ѿyt엗Cm}nc S=j$ohzltNYz-/r33gLg8zsfͤ_<ٽ{[mϞ+vRض@#V~S5,ϐG?.?Kw!0M-[azLʲb1֬[_gp\z|9c)f~m>}ηf=GDJ]kwlooO?cMٸ)˗C: b9=yڥ(Pr9P0ҷO9ʦO>ꫮ`Ǜ8UWGcc?DqqJ|^wκcQUYEq8L~}lN<ɱcپc'K{'Oqi6|'NaǘɻK൅8q%Xϋ4 )S}Ǒ'A-^KrxM*A"tJ'3 ,&HPYiHJJB@<'mY$ b^/-):%)Hyb.۶Y~#~΂;g9@47N-?k޼bSZ)@ ||>lň6g9c:UB ׿7Ϟ!_Gj uFhuI~rΫB9?W@-B̞/jGu6)^UUEUe%C (HYi)7l$!`ɉov=k׮2k9y3gh^~u!>={?&Fg[2ez:SzQD@lGv_=y x;w|'OyA/p C̞yu Zo5kU B {7Gzp 0m*b;wfd7|u7bNʊ JKKSU/5NvhF+2|0~xodUUL8{/eAVZc>ʯ~[&aH*+*}CEL8a9c4(--;TVT qӍ7nJJJߏKDqęN{<) x\TpCkt^nV<]sW\ sDڙ[vDqf!t˭K X 3@q}I{Zg< yE?߿Ӣ{1y]zgM pYzG`1 H{Ʀ&2d0>ɓ&l/}5j$7ldW]%䘡yBO?6M3Rc n?bc.8{35S\Y3o}[ywSS]̈́Y-~_].J qǍøc7$͞uSN-1F Έñ,]Ok[lr=sOީQ]=¹gt18/fBXăoH3J3BtG$vFH|)*ί̭#T8T زtĚҀ-ZC,nI[,2<8}D6ȲCi2+t;ZhA̐cY7PPx4&ON) gپ}'70An[Zc\6~*n>XdIp9f 1,f֌0M۲غu;#2?b/q=7JJ'{K0+ˁX ! H`9ϥ7 1pVZKJ`wUYTL@eFKl^yF(PBDcH۠!0x?J@ H'̬4;zN\.$ s^wfŴw۲; $3_7 4aLqZNTgѬuN7ϡ~ܻK0ˉwِR2iݶs(-@i4D, GT *B' .|ڮn CBHCi ,۠5IY Aثii`k\H51)hU!A'1p\J 4!֚~!A4#bCKZS$"iDB~(@& <҉T@RmE$eҞP' IDATk"iKĕiY`)kNiDkqF, R8l*'%=ߴkş{OwgzA̋[ 7M...9v~dU= ^?cgz@CDrF*ӚHRvlу8zVJbXɰ AyDZ4&5Ma33(KCkRp"9ՒIIIb&mEF``` M"ا(6-~WTTL)6c)L )$=%&54$mMԒX RJ`)' V#Bg6(L2yxF/s!{u=ȏyq`/B1<gk6mohhmrqFsK eѧLUȲm)* rA닔&˲,>8;[(YMG? pāV&MZb|g`g l@5= -VSBsj[ڠ%20РaҞY|uQhKI"#Qm)XR@&D! 0MoNű,P8㡭-l ) !/B`H#[>߽Ӧ^ǐZ= n0}/Ej0PYt9 #R PHMj4I’$>9>C08?@3$䁶fo`WH=& Lvr BkHHǢJ8i^,n- @H B JMا $E>ACPVdRR)Zb+I2$ОDk%vb mI(I&OaRpWm֞(C>r 꽏Qo F^'ٖ<8p͕۟s1V\CǛo/=B<`Ya#7Ϟź 9zO7o9N>6?ӿ`;u]A'f 6W.('Z87ϜY3پc'<(>l3cǎaܼeoRUYɬ7jZf ǎaԞ8ɵלig"`n3iH>ZiW_GJA,G& ^UTF^J; h/&5B$']VHf c &&mlvܨi) ~a!0XELvYr|!UN 7ig19L89tNѱBYHwA-u¥t&qr#! qwki^I($)-1 !VXr6 ' DSh R6r6(jɫ cRѤjs'+"w ^k!GLA߉^D ) ԖS,BkͰCC>4HRx^FVWsǼN bv-gqQ4h:iwd1H)R=qR\x&os>ۼֶ? ͧ?'x{﹛DZsnw(DұR̃BS^VơÇ1Md*wxXqi2{͍7S^V~Wĥ7!v,p>fdfb?ö$8hLHj-ych@}೓6j5qX06j2)[#DJ'5G* {5A&$AS HiB~AϠo.dZK ǝiO+H lIҤl ± Ly<9.(3{Е+{ >/_9b %٤-DIQ~-&Ky(XyHzݵ׿{e)* r ӐR; zz]㵌;ҫQS=QS]CMu5S̒0._wExWy955 ߟ'x 62t.[Ϊk(/++X'HK2gL8eYLZ^-4OQ~e_SEqq15q,\}0|X~IE¢55C`bmu)!I 12 ҦD3AIݒmu4Jeʷe }ҥc̊m57Th- #'=A ANm^gRZBi!$ 'ѤM$ $n)XJec) (gr { 'N0p@ʩbwÂaJQw=QBGbyEЏuO~zQv<ڶ~?n5bqyG.<\l߱Z],Wrny%_Og$ɜc d!d{_Jæ PfN1h1T2JKK8pWMI 6~L\uA0}߿,6nΤohZvtRT򏰬4w9D2ɲW[P^VVhW$V CwnA9n8#ِR hBaNN>qLb2A|bi8(X ӕmVh bJ gcNE x4~>+4>4Ӄm9Z"IYHBӞ!-I4%IX4-ЊL g ډ<2cT[S:rRPz=&[ٽ{O6=þ6zvog%Ə˲7bه+8ydAaPQQ>d!,_2^~Kqq6o1-_AIq E L⮈G (TtP6@+91*J>aq:apEPuj}& :6@&!Jp D H $4J RA6HӃ߄Ox E$A(h%fVJ("DiIESG{BM9l@! 4I-@csDqvU_Y\M 7L_cȭQ=b8`}0t`H?g;^_Lee 8p(D*8jkǓ&Mdݺ,\zktqqqq鄓.3["0t-&MQT6i]J8Ƅpf(o$mS`1 ˝rd~` pNLeqL@AB"0xM L#( @Ǧģ E>WPxK4MҖI4e8j&DI,HY63w 4 >l f4F@ GL9eWp'~m],\H$U'1{LnF80 jkkinn!`__h$?JbL ؾc''?~'NpSiN $ AO34$)7mӚo$U&1M>qpk+rZLaLœ1E$Fˌ+ىx`-:1Z+Ḕ٘Š&Q}*?$Bci [ Rp\ D,I[ZNAr؀ɡc+m8+"'y3[D3+/\ xpR,L< xlG0B?V<O^Y{E0Dkͱǹ)T;bҤ̜1`"&ˠAHӔ!!%%|Yvd7Mcp>[hYǎvbUg`dMu})//+(׬[nXn=@)WO Hy6Ǎ-踇R{~NeevE AyyƏ˶8u4(5#y.7}B$Ҏc8^{Mu&G_sc|AiLI8mYTIA[RXJID⚤=iTN>Ce*(m0QRe D&P˝8C2gj>^>}(DGCuvlסCзOl[!D?eW4ţ {NӞNrodFk5G#z,W.O>wsa! uġm;~~}L&9|(0 x7v mmm>rCM&ulݶSZ 2(N"3l`F:|@ @yy붿SUoŔ'Jy7>l(MM$?`oYSM}Ch9p--̞5#;n(qqReQS=08q$MM|>FYb% AJ9)L&9tdHy?x]G(TMG1+C;5 !   Is nR{\@ZX"SMrRhIZ ,eKiڴgK4E^MУ AL4CÙ2©bO)b"XRM4%%&$ӐTdIm+W$>|Ç˾Bp9pZ/E ycV*#GXb}wmXvn7~p80x 47ϞŢ7ߦz0>)z3mJI&BFIr[lcI^GϞo2gL^|n3 ?|oCii);v⚫[\tTYF#ڳ7cH%S)L0UkxzhCSma(illba,yo)^繟$G4 ,⭷ߥ҉U^*Q$Vq"5a1.Ij(QҀL MЧAkBЖX_+傳L>FMfh AِV&Qn*C!D1^S{ 9+w]hoHxzRH%ʎƽ7oދo6Ḟٝ?4F(CR= Cwޔ{CVځ0&B2o4uOH:hCPBPhJe/[Lɒ{GatA0`l9-,mf@7.Y/ppԙ!f g}̝AG{;ay{{;w1o\RH$ʟѷM}ǻd2N8Ekl֬Z h[ʞ_n5W}Ӟ^í0084W}F/ǥ 0P_Wwuß0|YzOP__E;~$Ntdƍ?&?cW_M׳s m N|ͷT*QU `~YO,bS{BkNB+J%HŽpz2Z0 1#ï!C0ƿ\̥r|b.j? _Mhdu%)kUkƴDB]ȸ fQ$[CU\Q'X̟R _`Kz}f+10g,;3B-$܏ຌ FolgD"N"tb.Y (_c? }fZF^{M6_ˊWNI!dhx~G@ߺ}DŽ?dJPpW)vy@'cp]X4JsS#BBRg~|ӿ~G|,l\ˮb=<Ɇk_*>ZiHRl.s64UJ'j֮YM]m-l2\+1\2.?6n˕)XBWȔ _rXqDTՂAP9~%1~/0|z7&+"ʯRijPiгhC= _2P9 ⎡*Ж,mT*^pЙ2sL98k0k(7-dms;8u`UR)mA*"[oo.ӿ 7D8f佼u\ϻ*23?Sxi$_w-?St6[GMM 1kVo:ŝ_}J߂c'sחng̟7_x,6~c(Yf5K,*/žxW ?/(,a;ܲ$d:BH "kI,Ec@6";BFuC s! iivvYvtztT{Jp4~ZM~Jokk;x5;;~Yv57ffJ?Pa\d1|c]&qrK IDAT&xϝ}Sڍx,Ɨn K߽{d"V58E`igzu֏M%|~p"QL*.E ƃi b 4E,ZJπr@Kx `0% p/jo}s7LJqxh*I$" 0mBUUm>kD:&,qS/Gr!\OM/kJ0Œ3.M̙<1g1Nb!ZW((Wc|/.nLL{#WqϺ. M Z߃<8N&$]+ܣ.t9c8J(Y!]xv)v\b*Ă+  Teg3xV0cfHA0bQvԩ퀀Wkޙh \20^򀀀KX}p.JERjfdg^0/cvOY._9y>tvus|^@7>\2X{Fq˲}Xwj^|S~Ǵ?0kV3=0l|fO>Wأݨ[@*ZtF7sbȇ{j ˟ <}tCpԚ?$n.bU,]u6_K.7ߦ-RBKK t%d2<_~zsS#ɪ*۹+0g&Wʎ xO4OE/עKIy|?{"ʙr2mfnG1mXUx3(|Z0ed2lݶ"*A kyH$O%:1_xcOL&/jcVZMtBO_tۦmf彝$1~+W,|CvR__Ǣ H3,ZmIR]Ncú[s7_{Q-S;~֯[CC}=&tVZAuywc$qV,_\ObHX;og?No͒%wENvuqKjv~gҏݿ!֮^MsdblX7њx<^yMghGB ?kZzzz,]y~,F@v&8SqtUe7x#9Ex|G#ʖYr9x455.o ϣӀ-(,ZsJ=|Єu2䋐CGg/C8>˾HyɟF{k+6DYYn [^޾>ϝo~ Dǎs.^{M{h=;ژ?+}?pwmgVs?an>ܻ#G /!" K\0#b\wfx\.G$|F(^-\{ygv嚽ctvu_dޜ<_`ɟOf=uIیlkoƼ}FkTb,\0_B*UMuu555 H$ nZfbǻ;I&RRUpx6ce,]%KK:oc$"8 魫eVsCCS k[%K+`eΜV,_8tu|vX*IsS#UUU vCTbhhu/Čy28bhKDv.0 `몊Wqy2X[[{B\w5Zc1~߱o~Bp7ݝ$qj)xǻ;w122†`wZp{)c;{IS/оw'Ov!JNP xcr[p8ZkVX>pqDXv mmSڍ!"rMgHQJQJQ*wC|35c(枩W={,t\}}}ᖋ=̀˂XDX[aKMbyʣI1RQBzxFQ*S@XEȗ,^8,U6"hrXԨ<3S‘!8>CEOP Wx]}tlS]]͟G ?Oh.?J,eQb,(`p𔃵F(pޒ(2)XFrUn6,1j⚪1"44 1[XÆ=EzF#UTpG`~u|Y^0 IV՚s}W|cUPU\YV3 QeEb}ev5(-  Ԉ&,ځLЙC݆5pc"죰vgu5$)fEGGC !b%Sd~U Z]XѢhO)bQ""b$qKU!S<(,Y`ApSɧH` :^5)˕8_\ 5/:»F/@w55aEpI+i jšQՀ,1˲†9B[ZtF.&\h+žnГV0a%ʗٺ'X ="WҊS# *4N QaQx ihuX 5eo#T< %O,VȖ,yW0F.;N ;; ҅xpUvB:A"֢ñ?s۰U'ua!^#S}D*YgjpC&^ɬeiAn3&k=0tf 6jJ򖗻=㌽09#d]ώyEp"3p|u>Qu.P.ž֟? K mVKu$Lce4Wbi㚣})*bS |9MvMceAB E,-*a,I[ִY5BU\X !ȕ\{NOg k! (,-ɈK8X \v|Y%!(ɸaN ZZCsNO<϶A92J)0V3qXt"zha>Q2/pg[4Z]g}cQ U0llPƅ-=k ,VԄ鼥-.\ۨ(AYIT oz _nw1k4OuyCs21R~_X3dxdS'<'{G" ('RUd3|w,9oM1YEw,l7uPXɁWp<>Sɠ& G3yK$pCo=8(\]ЙێmdžFYX Q))y`YZ\GXSuqChtѰNV?651,M)Z}#S{=F|o`W2T1_恵㑇 _O4--qK%~'cOilhP(#Qۈb9r|>OiGF*ް3QmRo_4\ӲYKQ  oOz'=Gr2kI:S91!xGMX+{_=CwNl%nߠ<1ZAW4]9G}AOx̊ qNߜtq-<Ү7JX&yW~ oz8+q\\Y)XfGz&21nWc^iKtYP߅gXUz%^dicPr= + hMxՅ-}SrŊ1VY{揵q'bpu@C :*!Vބ ,#.#PXgo .'PP,IzП-aA/ RDlm- mCyޜPr5 IxHQ2S xO]iq(c δFXX<ڏܓwJLka֮Yͫ[^ <{Z[غm;}JKX`fj^}u  Xzz{{Z"0˴|Aǒ.z~t7ɉuӓ1 ~aChrd6qGXS-A|*2%<ѩXg}`t1'5hh)c)&,R;0Gs|9~ &{c>}~~ƫ_Hst qN+u((h+t z\zs|y&XʆPx̭1,mvX 4$PZDŵB2yőaS}B+V22Jaͮ.KmBHY*z#=bB q QLN18 Rq@FBt8R" =2/iҜ5K8x0ǎ>t:#Gc2{W-]>p/}6\ץU+WpUK|eW_]kӿ_y!V,_ɜS'.?M[_N ufzM%0 `&"b|c%avR D QC{E޴þ^Q$ }@*,>6beEh ?&m|cq/R4F!V{ GݲN!1;n1EҔcSQX\j,#9TF:<1D%,& 2-={>,G&~/E:!L|2]>r4ZJM-7+[Xf5:tʣ U\tmx=ΙCCC?ʲZru>Ɋй\qvYuD@kA)S0RuZͱ!88(19-ѨfQ]Ms,S]E}ҒKɆȻJ +W_[oYj%Z/ZDUUt7|2J)VX@[[xիVwϵ 1|V IDATkϧm..ǹ#ގ^~lكj4UVC!E,uTco2AZg@ =,}9?-b-!ڂ!" X۪YXorl>88ha,ŲMml81椦^V僓| .z/=É!nKKf-BK/y+8eiSK: IM2'gh,5 K"Ya(cRwV-Z1] N dZcӆ-?U]yS7\֯,ۼq7n߰~-Ω,.3&2wNe{f\vXBUpғu8oqZSt 9dJ 2V<٘VBmİAXT/.O(z a(QraU:&TN ?QPj<ګala$gg9c-(+8^9`qptВ8hח sO(y5Z`2*FP2 5E䤱?(h*;fNS䬻9_"p= 燠| }Q}~7n([iK4婧}A> )\ͩ?giJz̮qЎ= GJ Ӏe#7A]eӥ@/ˑ!_O0M.1ESfIp!rh#SkIf<3}:1ja myƢ:&b0 f-1mhkŽ0 d~+Ih̛̪CV- 5Iܿ97fss _! GƓ/x׿z-Da/x8t0w줣_B2 Hp -X~5kWq  k kE} ZR.aǡ'#t [m|>,#˜vH<T*aŗ_ױzJ۹vn5^;w=mJ%k6obۿ'"jFi~scqs庄2Z)6o~PŨ;y-<ڽˮf=ƛ<};x[k/wBNP)?xiBPG~ReWأ}ov`Ep58N7{!oϯZ . hLZZKoZ8=ljKsC\C(C`0r~Ə-d!F\ O1- 5PjՆ}!x̯QjjNJ%7IR`rrcq6lhI9WYr%=!j \23=E*Hc,yHGSԾq+:9>Fg? ()`%͒f+:}=" Nݝ;I;8r]k6m믣P(000HJo޴M_ /b2븮˯lk=JklnΛo7^__X^yc'NG!r1enFVXѣ. BwL&* ?yg|kQW[ ~K' ZdB_KEկ1p n`=fF6oX?elu^y~FGӕxeks]۷|U>kV;;J4Ϸ kPJ [%]ae`9d'͊ ⠧I/ fWAsӚAE: VWYPN8ԯ%Ca©ȠXVB[*n#Nl,E85X>?rr~gعk7[fM{ ׭_PW2l'~ D 3T%uZk%~'3>ǻdddd‹/}dUrtM7^?DfuU$֭%aNVXj.51XtVѝviN*" D*dHY"ڒ)j3}gcn1P6B[2FCtfz-%L- 8O4|R ~2ȟ~;V|^.YL,,ؼi#".rd%>3x85<4 3(hKY-VS43_/AŶ<)CDyOAǿxmEX̿-ZLَgĄeLֿQGQI&ڍPC!K%mA6_?RJ)qM8|*վwjZ}OsXܠoYRXHQG$þ-WB1wb1H8 'd=-UJSqg%F . CU )\P4BHC<,(lM˶,%+ ea0g' CÊ%Za ʏQwǝɬv=H4(h%VZwsqxwƋӓVK e(V$O|duOLf0C=ӝ]Ud:bDEPxB( }<塴CERR2"*/o +ٿL[|ym^>8xw}oI&S\zf*֭]O>ٰn;^;~]gBO<z!>oL=6!JM/,fN\:Ey{`W&Q[hl)(PTB"*PZ-BƵPH :O<5q%WvJ c JJH,5$lA""Z) Zp"% H601/JgQ#$2 +"=#J23'&Ӥ0`u0H+j>,%WsUrH`p"Y\SSC EMpÊ˨MQHk A)cMJkƓO=ͫJ-p|mVx q>X)@OO/;Ev*zDo|?'|-tu;ill`M7hǩbfW\`3 .uBPpa(/B ORgW#Sv gimhPZˉ3Wʨ: x& |-p$DBkAօ+h j㐈XXYӣz0|A< + R{ha[ \ܢ~l0AF{ɕ,ECcrsY6}H)f|3ںZ,)Iq:͝s.&ćJ+Z[[tFV,[6&K|pq/nA-7Q0sT݇7hmm%Jo_?uu|=rr-/HKs3SiokeM%"tP ؐM*_ $I j)\-C(fJiDF#>W .B(13}OmA2LA)1o ip0*OpN$ hk\OA G2IYWV[⏑e2^ZRdTi{xO厎v>smtuvrӍ7tϛ7~73ߊR ˲hll믣͛.obѢXVfhE1wNu庳G``Y3cxg~-̭S8%b+KR)%iJ RSr/&5[!e(.ӸԦZw|& !hnnǙWڞ,[! 4ET XdI ϗ.i"}A۶m,0m<٬7 m8M2D"瑈ٷ-rusMM_7|3իx}{VXQK)ekDjI&kMڊ88+//[r֦i:f͚UU?⧄ɴ¾JW%TX Ob &"P§ߕ(pl"*x/ 9jT_rCT@),r}T7(8ڽKQzZpXT#%4c!׀TUץ {HU$IČᳮo,*XRq*Sm214MKfu#MBق SPEAK@EPWL~8K&z/-~ڙGӊS4YȩlV!^H>$uu\*oW}c,]I.z)99sxŗ~@CT0!*R8mI&kr&D" 1#(q-)##}A.o-mHEH  j$@XE$F4dGVJȻ>4Y1qo{~*S]HJIhK"<-!QH!ID{)8BXEWyD5X5E\B"lA$>>c}CX dakByE6r/7OFzەWL֐S7z?Lr d,X`Pϖo|  A"wYuylt1`N]w~c%, w۔St ! p f3]`30!!(ZHR}BQkVlӡW,iȎ4E_NNjmS 4DmMX@\L) IDATf (MY{Lާ֖>Ż'vJV%Bq+@j#8OLKeQ@ЖD%)[xl^sJid8 !E}ن/˱c |s|o8sLqd41`\BS_BL@6l^/j6oZpn@5ʗ&%Wh|Q;k|FB)_W"MܜCbl)x҅_ rAm#C./9u.lFt~cԴ!$!jĠ~ c5HWh,gеx%Jv901 Gk׬Uo*-h6!BL^r#d`Ę;W90 s ResaLdBi1b I<7yڡ=!ҷB Sm扉ٓB BbU_W9G#k|d΢%q45K "¸vm=z:Aj!Y,/)m$NHPFGk e |dkb%0z{*֚lMp1x@*"D(g[Ĝb`YbN+,#t hQL%5Jy9wEBJH-"1[cIIh|| &fbPI٬Pt}29P$=,Kٺ6b q1! "D!RF 4@U(ckK(hG4ϼGh؁XLPT0TT=%LMWc7{&}قCH!hh>ǎH$BsssX D y\H堾~+B8(9EP@$ &de׶(!#*) ,.9KZvgm\mJٔ@e&(YIQJar1fg`ȏ0Bq! ko6</2CCC ElO|P.7]-q8cbc렵cҥKYz5wooٱcǸ^Hկվ@N|IQJO? b2{m<0ض@hZ#-OpZ7SV!]g@2/BVQ:dH85D!4JRK4k䯲GI(ڸf8B0M!Gz\M)ыfW&}ATDNq`+GrD!Oĸ'3C?>`y԰s׋tvtpKK?p믻V^~eټy37o`hhj_^xwtuuŗ8~46d2տ?ٸa=D;w mm\ue>z{c/7oD"95v2M ,ZW^O=lYǭP(p~A&/_f\wz%2 {~ߏ&"Dv\{N<= \yK/F%_(_!j?Ͽ?-˗-ᖛob!f',-QZbUf4[anR^lZ[JOj*cat=k 5RI+AUy݇})%]]U]A駩 NsIXEgg("ozs/d~Sfr-ߞ_OΉx\u 7t ̱'fld|e7uϿN$AJ޽o%L?l&KWW'&w?~huC|Ïz%=v>v~rOgwIMM ?qq?ukٱ>{m̛;柅j>dPh bF (DZ"nlA2fXƭjfnJI`Hg;Y!Q qQU$:AhzqtR\z"OħJd(Jآڸ:J( T`KhjFI9%QB&I]7|7XUt0.Tr|dp]RF,AU `<gphz4H >J RulXY^˲3 >rl6txG,#!eR1k},I&eT5553>g5}ttjOR[QDETuppAV\+mf*k_-gn@Jɶ+`ەW&в$eKRQ}a,]jĨqKm3gNͣP,}4]!f-:Lկ|iT5W_7Q5\!f4XӒ =>1qRJ|_wScNuppǿK!HD~DB eX*lim=zp=ŋ!ɓ?p|>%kj`hmm]];~~ۇFc6X&¶l.\@[k+)ŲF ˲H& *St$sX3H$%yɫx@ǶYh!ZkX]¢m%ֹsڶqF677-_|eE"QֹJ8òKuIrםC!61l #\QOؼtHqZ0Qu:S Fb9CCa 7&(WP#f VjpqqZ!8LǑ#G""]NrT` W@VlUd-&7QpG (-i3Ol !Eg'$Ji)cQPﰄj=/0ahp5i>âRZw 7c7Ey$6RLJ%He ? GϚDLY38K"Ds((^PEUMP :5uQrExy\R }|@ZLlM21[ bQI4b#S˜ E_u ueAW5lO__?gWծ>`lN!ɒEP  4$lXܠiNz {+c&N)tP[vR QPLiyEQ[q(UC6s'rpSFXhH Ň!EHg)$~{_g߾};^^8q.fym^^۳'O{5>_ q(ɌؖAUû>$|AB%5}V4+:P-(m,#?1A +Cp.*(e4x>䦝 exzqU?JQE?[uu475a(Ay8˲H$O&i[˲'rVB!B ?sdW\.-JZkNAkL6@ $qNy xTs'_RrW68e Pk=c VD] Xc}$ьm *%M&Vѳʴ<УܲFr?W]q9MضMPwe_ߛo9={++8yeż[xgplcm<}G{X,ƼsX|?寘?o. 㢍B.,kEc>C?J<uk#zzzd2׿6`}+o'|+Wo2$" | :=!)jAV5)MHr}mJ:!*ZK&,U߇\^G4'{2G:ÖxTR8RPibmKFv0K$ߺJ'tK #g5e=UCOyĒŋ,ɏ~r/c~T NMM 'wȑqmDl6GcC# gɒEz<}\{=b(Yf5B6 B?Bo_ׯ㖛oec |K_ {8ye…ƛ| c|FiTLRq<\m*ds-!_TD,6yL(NrxF2DR*5Cy qGk"6FT'ñ$0r& 4 aCkB95 +7 B[\_Fӟ_<-gyE}"H, VzD&̵% n l15iBc"fl퓰,"!!> y۪N2 O<ĸRfp%lG8\u+|?/M֯cႅ `|6lXO<Ž?9]v)ҒikOc b*hhǶG.۲G{M7~D8ukiokeuӟ`|.^Ī+ZSF)1c U*H:(ߣ8׾vqmM|K_(]9|=$o˒ױyExPB8k:qGm\ß׮Y5˯?q5hE*1{8h,ec ,l=}ɹ'%1%6lz.l\&M.&Y@4#3RH5IDcԤjqHj'j(sM#= "DBJ %H8ZÉMV$OGfqJq?H .wg{ f[RDm$|EXBj%=o!TPíB) H LRQAm,.Z䊠d3OKh&b < 1;$~U eY,^S{H %([>ÿ ]Iձ|E_cKET, G[d$ V\L)Lh|HQJ ҆*HPq+Vg&=9H>~+vs^f#Jm[?<럲jfȦ+CJjR<=E.!I#D>A>xo) 8su ,Xo}obUlr%|dMcǎ/: IDAT͛紪?P8:yB:ѣ,_^ٹH+,LV07-5irE梅m#lebӋk29E II9_#$O ,X so/Fcks/ه~EW\8r{~inv%ۮg5..jzW46N>eK;Xyy] ;w.{ޣՔ۲e 55Չ"_(pQ/Z8;w73۱˗tQ g`x ^TZhC4G$Sa5E;K%w@a*ӋJHHE7.2T "R#)7V)bOHNoI+#, JXmxyrf W@ a1 2!J7nV~r87|Eیr&VVl\K;ĭ#&9h,^+לq|h碍mA&W^>B~yٹs'n{}ٲe$ϑH$#̛7ΎvN8ɁĢ PJﭷRrrN8n:9q$?Z3o\>CCλ0oP-Ĵp^K6eeּ>|-AmS .7E>g466\xx(ȉ]vE2|#s8㗈jcptXk\ߢƆ 4qr:ǎ}~?h4xDx~.9ʡGx? o) )*AA)ekl[\Ĝg]ƊuҁMC lҒl'j2 j4+<#:X`RjlJhcc&{h(}q>ky'_ _NJeFm`VX1U[ro݂@`Y^?*d_~|Rz1.]̛oc͚UtvtAgGW\g{X4M`׮I388DG{L/O JQ]]-oۮ\ ={^o"ϞN>t?oW^~YXn('5v܎K_z-RYr$CeϦ6}1Li1#, E jC2jrE aeBRX8ҧ聦d<(ur|c:ؖ@1C,ymGL_[&n b3L!Z9^ښ$QJ.Ha!Q.0<{_!B8k>z@#]/j Ν x)]VXN4?§Dm*xc[\qu|ګBvP_Wŋfj)B|FmK6_̎8}Oo۶Xf5˖.ቧԩZZZzەJZD$45u1X  i[IT."1 D$IcVd$\b7$qp0Xf#_)P_Wc!B1Z }YAc%9E Ztn a5044{rK(KQ$"iI:I9jiKAօtA* Ԫ,-ΘӖDá? 4Vĩ亜 2R $}}z !Bbh ku%iQģb0Ѿ&!b1RhPZ0\)Vck 2laB M\@V>1M].P\m: ~o4d ',&.!BOEŖLQ5].;gM\bo B1X=©MW&` 8 ) _ػ )@ oǂ W <"ZI<"J5ÀI)Z4% f(AjdĔK!]PF70@gYڄEcJ|3;5[+Y0PQn6-DY$8c7%C&`lggMR(5]}ŠD^`Um{sN"t108_Ө'zL&9؉I3I!fSጱY@G X!G;{yГQ QәR؁d(d CHhq?+XZE1CYmu C#LJ([m55),<-PG"AK d"IAĵjki΢T %|m!9L8>hWAY>'O$S[qBmBjHض=*>ҌbtuuM:!v4#(y;֮5eV\ɦMFmo⋪u"'׿`%]l>`E#o뮹|N3D1\SCl $zIϧ>)ƤEkRrxHOSG@S5YY%4mC't4 jc4}&֯.)i`Ney*SIM 5$ZA]\i͉b QCZD#H]@; Bk!,[bOIXQ 0*tr8(i4 g & V}=ضMsS'Ns=R$Z‘#GB0w29dyAB /IRRt]z{zqxϻ~g׆wֻZVESE1$!&$ r䙞й3"$zo5]Wb #FP]mb9h%,UHꃎ֓6hMXdQbHgTJ$Ԅr?qI,Z 0 e B~ 2Qڡn.xF !@XBJAŠlq1ʠ1 4(-Pl GMl (x<LӤ ˅i8[ss ds9R$Hi P(Duu5dZFftvvRS]MGGmJ 'LOWw7--!Eoo]NC{{;Xd2I8!^vy՜:}͜;|A~au7npRgΘNKK [y7jGS,Y-9z>@!%ko?p3ƛ^{nx<%\2gعl۶( b>!Rb ̟w3ol7Vͤ4e|id4tš+ChA]ą_N4VAeXa@xqi҂޴"ՂJZ)hrQW=& &Vh<\N]1Mn+iqP[aR?aڗ#e&Na!r<%a=Nޞ % An%cib2Vhd(ɵU9kfЌ gJ@^kzk r99blc9A~錓_fh9} @:B^/naUmBBJbX?' "Ia x^D6ϓ7w@A}}s6sfdyk|ܥ'OqcVQMMݎW^{344 Zsn;:ؽwaeKΚ?2}YFQMM̘> o͟n-9qTQ xbqO<8I˜ ' /Zs%( ^ ¸y(K_Z)d]xl.VН Up D'gnPK4hۖH;KVy&m+N Zb FS*4h;AiZ51cRI#6}1[1FTe"+{4iRyma"_ɫk6;U +\}˲97hKmls]@K+F(R[T\~B,zm^Iu&B)՚XwޛB]t!D4J\p#Am°W4ii>?\%ZM{GV#a'|%X۶ayRDn]]cqRU g5@Ue%tmۍT2E(/W#F}Ub].Ebi)d>r[/%ɠf䉬NI.Q1}S'O˲=s&GⵕF]]-Ǎ`򤉬1555̚95OE$BOOU̝33ݍ)z2.@J ZzlzRq1"hд'41a $S|ȓlEW\qK2.v1WПV$ C*KO2dRg@kWYL5t AKT y"?EQnNkɨ*׀x t+m+4ِߣϴg1g:Ƥ<9k N(u BI^H粠 .46H$.Sp)lt$>!\ ǰ-<uucC ECϏ֚meQW[G"l\nM(2^-iP_O_?0ho(xݘ\p(a\. $_7.{n0]ĉcOz}464 ٺu;L FK8u0~]]x7Z04mq2 gxJr[3Zl0Q)ޠIéBUntVT,Ob\aHZ&Ǻlb&A)RD|U~@AktOL%PJ NJ`Ɩ !ք}5 -EߐOICH4+xT;ޔ&J빔 0h&UU%ǜcCv;} HUU%c~~eJQ[[[!,r(à oR_t Ɨ_5` ƍSRȑJa{d%BFtr{L4Σ2aB I+(h[ϙ=x]mm eeGgiA4cpK0BS]kr_Q46!ɤ:8~u!]SLQg4{5!M}P3ASO<`TBҙXF2F1$d ZpMpKZ T\U끎ɴT$V[S4)K7tuJ;eD"l70)@βOd,͔:Jَϋ/Lkq_8~ _Xe\>|>?QFe|#080w:]}>!xi \l<'8umU>AMT+{j­]c02jӟ\N5*5%|t"oi|h+jCaS1=񬦱dDX6B)ͨjư ⵈ$G:{mqt>O"S+"Z-1CxRh Z(2JHASR4G5}iMRLMu Q$ Q(Tbq6A i[r6&h罯M19¼|4 $>܌eQFIr<0-FaDX@gLlM}&j*/K+P~,2(/Zi&:aD^ IDATB&S$ EW,Gs j63$r)li`iq.dXBl#_ aI h\s88!hԑ14JfǴiOoS=d]򮳌LfT2k4{[m1I L9-KjRX|`b1C ڿe#p8 Kk'Z~s^د:g$LI/+cgѢ7Q$';mND Tx4agL+S]5E4=Yl-" s%^25YKrOН豩$tq ۠'})ޤ"(Q2@|h6 !5`n6IYNȡMFt>.+@ MOJpfFDRUi1Τ'#-.Mc΄LyJg-t1/R@uH2Rt+ȝ[XWp ť{5&u}|:ڏfHg!?!m[ZZis.j&G/}}(˦͟r*".ϸc>$JxMz TVDOdxװr/T_xǢwMž--;wpS9z` @6`0Ȭә1mB qIҜ #^J/W88J]IŁVAUbR$Q+9Ԯ8">Ŵ:IJjڒ3Dr"INpdd-i"wm(l}(ùPrvԐ|.͘*ͣ$c"[}<7c@9eچ]CU0 85'-a[0r6؜ -΁-[BUCLm4%> kFVM#L)g}Jc ɹs@9l^BXBhQ[.f6ݲ6֮_O2eK={پs #N0~:̺ ٻo?a2gLeṭ]ãDH߆i/"  QP;1}h?!},^83o+V>pUܭ\r9*;6|qcDpzO¬Y|)>C0O_2Y}۰Kwxsۜs֮gTH/[;f?77o`Ϟ}%`ss ~-]̔ɓxd>.7q\~{L8!"VFk#Gٰc̞M7-N@jAܒ`  L$svK&K6sFܤUh>ZIlC\f" /711Ԩ0 E{V͡.5B9נ5[[!L֌ YD\ПJ$I>k뱉lQN!Z MEA&`P&bP ,HR8g@+q"޾7\sk'Z1߇K/`Y|n5,&~g ܹV}w׼G*.Zأ[oNyU\.8}8~Bћx[uղm^xU9#=eKY`[nnkO~ΓŚ>Z:::a፷xWQJ'3'p:b8;vf%L0 eQ0wv ;cJ{Gǎo`IttvP_WGwOϿϿrI#qӍsil磵-AB!na.?xw $<'?'qQpLY.kplX6U3k͘ ,ӚlD%yɾ2=(jkkJ$ ]][G1gt[0\.BGG'P(%PW[C ")82ypo{lS;pLfLy7T3mvKooS/%cƌm'lѣGm<paض}'dcvc}%'MSYQaĈFSO'Xp>>wX|5?;]x4x5*M=]1l *q?Z|>_sX6I5L]:O9vt*x%SdSQu4!!fQ6uASx.qXi*0H%*M"eS MGo} X2PR! eU\wY8^)űǙ4*G淿{E^-vBȇY]ATmrQLĂ7+yRtq E ߿H0 soֶ֚6Yq'ӦNwϿG7`J)I%ŗ_%sם7WЃsC4.ɜY3ٶ}{~FmM5D22dkm NxFZ`wxdI_9( ` BrE'㺨49:?υg?V] @5iĐ67*\m1EoR65^ f_ 3em Cz8Ơ@!KaצuVDx4EuD0A0^T.&˱dYIkeqk`'68mҗS} UPojX) PqΡB*-I<)Hg44t% E[EGE*e@N lmLcOa SOa&4ܽ!^L&d /ʪKZo~׳;f?3|S&O* i F>p &DJzDc#=` @8& bYuuɷў^jk1M|_du]zXx`ٳfY[(чW% ~V>pax=Da?>c1tG4642M&N8rQ0nX<7ݻ4yI$,^nMMu5n|v¡Lt&M*,[BOoK/*J;֣([Q__|b2,5%_,4דLp\,7!.'zفiR]U͡G[bTTH$C44Y벥;{5ukq5 ϋdquU%}WaLq\p?5kޣVܵ={1bD#._8FߥE *R09bD҇r8.ZvǾoIʊb@ 2`088?Vhni!K=*ph)e4r$cǜ6.rɇ>LWw7w~۰c^D"ܻJ~ՖeK 95p?=7mMQ`,Qc3x_ǃeY?p>ssf9WHQ|%QWX.2Ban# ۖqsW a8 m 4YK6ǠhB EдM UIuƐ&֤r3Qߢf$XiaH98wTa?#?1JL”2;v 4•7D^|T k.Yu7O6u 7t#|ӦJٸiSowd6u G5z3{}),ˢȲKxs)'Nb߰[oYF&e4j˖\b ס`Xp?p!$!D"vr>)i2nb[oYΎ0MR,]_~@' }Yh`\l޲ng7J+ B|y gΜe,Z8v (L͟n!R[sieQF-R󮾜!@), ,:iZ 7 F Gxٛ1)e i$uzJ}Rj|+~s<.h-g;_p3]AksDIcXϺ$ lzSM|nph [qPuPR;2ìFE2rb1AK )d)}\8.|"¾(x/k"{5oSv%zx`R/q_V(Dqba^6 i917z/D`Ї>|ׯ~X8x _Xe|̳y_|x?/߽j,m|>^~2ʸyו( f+=XEߠ!$(R+ܛP"Lo Au@R4p!Pc#A*)PJb`mɼ:ru2uH#onA> zj3gMpD!Z.m1zp.g9rӦNa1w0{ vͮ={ػo?,\0CkRpÜ9,\0 q]|~,x-.\%:a۩,6~?g}N/Z@8. <#(yXn=}}?`}|u5Us]|v0?o~-̜1eK3g,Wb?qYa̞= ߏaHV>Y. ;~˲3{VQ xTW92J)>ݲ{1~n\Gp/ʛ+2]i=B F/di]h~1s i1Vl(@Iw>M4SmAk$ƠM^zۆ!h|nAvѤEdHL,,AK =- `RWїhi"Fh+d*k{/|={0~\kc)9B2,w9'DdDml݆R.{!&OȾx,_ֶ7|?|n==}Y4&69Jwt&ve|mRK),&ImstqKaڐAO:ʼKϰVS$lc+ٴ0VrHe T XJ;69 fgC^T IDATCF r`+1~("@mڇr 3FoD*++Fe vz Z^/xR>d3l±''ѣBsn>B6ČPήn**+ N9 u5|>|.g]]-pld2IMu55870oncCwܲ|)+DPYQA]m-X[-eصu*{e\.jkjU?#GYa#2u5x'WkMMM5dY5TW2Kx˗-?v㶭ꎲkj }QƵ%3I/!^SӕIA_2@ex8h ָ@ ASOL!AҸ 46 &T! l=1DQԛ@kĨRb`0]9K) ,ZHm]-hB@ iGks)jj h˲&|߽wNzqtttbLN9Coo o@Gevϻ_ph`4s{a9!bƠ`1ŁN5(Zp Bo \9)Ԑ6a0:lq,ڣY̨? $;sPpScl¤JML#*`FXH$kCd&+–d2E$&4W k]&=gvh.o/(YHp#lnlܣx tB]욡)ڄe.v0p:: C\Tuvc%kTWWaYv1\8>p<۶ٱk7x ՘CJ9䚛§õKz=C !Ju\qCڅA.Zu^QQamSawK,+hs.Z`J|IJ106=MUnG51I! &nh%J01,fl m}[+b) ۴ y469<[A<2"i x O KkRY*_ʟe|-1n q\Æß]G5`0xxWm eu r R>Tz5Ma9 g{n*IdFb#h!]4'4}'55!ABE+I׀[*,Qp> QZt A@wJBSػfq t&C&! _(2(㺀5s5Ye4wK5^ZIsLe]'Ҍk0iK$x fiYř#5^i ޭʸ?K AIuPXt J,_6v"F_?J)6&hiia[FeQՁ UJ<̀sVb8dZ;FU|@k'Y +=?G狸NnB` KJF+-.[-S!ڴjD0FV H @9SZ0lm lKz?-ʹ.BAȗ7A3JvC] Kzb\|| Іnݶ#G w naZk,&' vr"}D!?(ɠTJNRxkI ::@Ofq\ܓL6rܲ^Lrqդʸ8\^Zk,|,ǽ\1(>>٭@;@ 5|_N:9cq" I4ڏKNuk"d\̨s=9a"I8Lx#Qb\:=OqԎTc* 4=dVuΉ+ưO٣ǎ3{L^~ *++y#_W^(~K˳qoo{>޴8憍 /˯:!e|o֠~^ɏ~I&/L_˰?ټ={]vxմ_]WWC__?կ'ϔN8v8W2:vI[4`| Lm0g'z,T{57j4 y̳6('s0* #ÚLE:#nF>ݲ+D޾>>-jll~ή.>ݲ.lݶ#*)'/^=C t[9y:~qiҙ |Y7?!ew8u @?%~)hz^|5~n~̯1 ɓ&>ѣn~߿/ڽ%9 6e dWt^ttt'Obܲli9P IGu-Y5\Fݧ J<PMD&XG*k? t: @e/ kvtvp|n]^y^if9rL؇i-۶sIZ[[Y:|w׼ҚV=^'p=+ _bL2p¾x-9r.`ێ|i3iGXZ-6o{8;sNAD @bsTHeY,ْquv}}[^۲ekeɲ,+QYHIĜ9?z0HJDR0҆W>}K__W]+ K}>þGY3g2}ڔcO){^}H?(aŲe< tvuSWwOH(:'VNq}wڸw/@ N0ȱc̘>iSkҦ;::ƶm~߽s]8O;w_<̏=." ;o؉)oG{~p 1k >ܶ=>O>EQQ!&r۶]gò-/U+).vFl7z [LKgpZ|YN5gsZ%+=ḓ3 C+9f:`CD'p`KFgZF#^:-8 gܶ(oBy1j&O0 ~_/G~WcBi a?Oh %ŗ  wijj&c.aٲ%(R^oWq1=eY)nͽIx$FZŌS;qB |!h`9`h#ryøpj&ON[&+//+,,jynN+>im'7ݳv0:4 p^㞫P[%34;81&15ұCJqZ ɦ2B3~9\ޒ2::fxO-\ )%%C.Ƕ;YbG%zm% |dge1fΘw]n<Ibwc\GrT={CpIB99̟ˌΖ_CU%%P(w [?qR3YfPQ^Fcm7-(?t Ife O H{ꕩooJ-^K|3҄K[mQ 3 %9~P !_Qz; V\S {B0OϿY ~y\fϚ e_. `+yp_!q1/]c'8lrssXt`8۬YW>~,_H'zCJ+>݃LlJBKSs%s- fϚIEy} Кn ;x-U+nviq=|>.\c'?>7\w-2c4̛˖ާf2ɓH$ؽgXWyS`Fz?^yiLx^ ìXw_D 28wh!Р4 ABpKp4Ѹ`ZI^@!ErS &j&Qt9< + z> /~*sA҅v#})3T{G%x\t^{40~Yo?9~:G#;;?+y|{A^Ŷm;ҖF8Qg-߷\sڏqj/BSs3wuǧߙN`^ ш;5 xD;J r*uX*Ckr‚jj^8d$?ܶZ)LZ1q& %! 0PScPN" NG5 GSxG lGc-, h0mIGLa` AwLhvFAus~N147=NJֵ hi-3,6( φ ua d30sk`n=K˖.9:^p]w|) >_wFnp4c@II1YYOSA[{;ܼƳxT=Rj(a8MEeTiJCo )Bk"Rr,q!f -ItJ#u z&å\W's@ R<ױ (m(4~`BHsEYY>#Q,"d*rf`_z,Ytשsʼn wqQבR~b0 pc;DmM}̠mA ϯ:x<Uv֮B>%0Z2&4ф jyp_<"7_qHF$+l$R8BHMVȡ2J DbAѦ;&]=BS920 2 .1%!!$B8Rc~h1ZZH{Pҹ:x c',}D")f,ǡzzz9z8mmg1D~]|cg@%9#WH!I{6WE舡A#r\8G,$H  RhWXZd0M?AiHQ#^d0dD)g vz,,P'>I#;-a)W*6hStjAȒ?@^b 冀؁j>xb_ݘմضe(%]H$'uUkM"aa&)5x-qDBJES`0Hma}+YYY̞5s\vPZzRQZ^W1GH*W/Ghy{f^ ?a=zÐ0\z9 iim'?~* &{ ]}P08$u{^V,[ m(ٳطW.\8u^CJZ"Cΐ=]W.\ɓ{T.Ґu<11M /!|I$ )G8܀# M|P$PbYǁɲA_T0HL5`XZb&Uru.8ܶ}Ϣ\"rB o4BPV6^hkkGJ롨0\޸Al {(?{+_^^{ N88WFkx ~_ع'OeK #o\1V"76UOoBw߇qwvӾ Je:=|={qT7 !hhhg'?pn~o/m;<쟘T]ŚU+RM"aop ׏nyشy ^}U[ׯc+~9ηbhmkcێ{ql~c}bvJe;w t"8QW/ziN |7f%lyo+Zkn:L[~lfy466qq) B`HNW_ŭ75J)jj&o=ġÜ?wB#/`,Z} йPh_>Pۧ,|ִpǝp:} 8Y "Iuha4hjb4ܩS1g<$WP(%! 蓄| +ҝLv$` 0I\3` t$p RG#;ͬS!GǝcDf# `԰ҹK_yr.^u7wM\Ʀ&I%Ypk'TWWS5(/+0 jk G{mw'x7e:=FGG3O'B$5ʚ[`ru5ԝ_B=uy"83!z#6)=˥(..vfJ,bppf& imk'C8ꈫ1M֮^[?u7tݿ3΢Ghjn&zSҐ( ŸRn?_sf=8xDI>bj9t =e굫yc޼p5ڽ^ ˶6u z3{zhiieʕMA@PS ;Y;Iq4ɍO VL-T)B>X¦Gqa1͵6yÚ[4%:<4xi+e l[-9πqիo7ʭ7'7'+q۶ټ]Nձt"rssXl)D?< psn i;u\MaQ!K/"' 1{L̛6x" R3ٕZtIBoi̘>-8\nqxz?HpۭQ^tqИ'VV2۶7-5͂=dGX0-]/ DJ? YWAJLE, 3y ~7 )[Қ&a &Db~eSD-7Gϱ -HFЮh pm F){¶mLӤ<rmӋV:59J(/gtwhOpikoGkLz=D"N >wR?8^'N`Y6ݧO0ܜ\&N?b^Lc"}fϜU>p(ĤI,yXnL8Au1˽^/cO86\9gprε}fN'HnZrS ^rH<ĀFS5k%{IAh)A$­ IGBBM# 0!56E,pKx$qAob h\q-1uVlwg#%t[' SNrr Kh{7oD}3֬^y9)eJ (g^g| KMgTԣI$$Z pI22 K ?m鳥ZJiKc j#xNÔ` G: ϐ}Pr+J~ AS`$R`9H\2М* AL Vq 1%b8dŴ#6 2bS߻C[ ,%j"D-A4K %qF[N=1%tdA<^G'mhAuWѦH$# L\-BJ+ >S}oڄO #1TD•Qi5I$Hɶ%Yrh%(K !\]-drO xA՞Bma`_6v74PXPH0u>e+ rm#̞}bo`c僉C^!4Thܼו\K0A h z⚾ 1Z0hD-\͏TTŴC^6 %8r87du}&x!C/#W>B. & /58h/[kouVݳYl:[Z9uys  vOJvVgŊ]{,7S 47)5444Iee%EәThc1PM(/# }'ܹb!;+]?3p pMn ڹiD4#hF0V C]D`(HD"A0$M [J(**dJd=ncEX,FGg'h !, 4(/+CICCCRÄ s#^|in>'4 N`0koY3y?7?Q ضNǐr;(*r-ͣ{aiD1[ZCk^/s%b<̟˞ryhR/4΄Bc1M& )>˖굫8x֚ۚAZZ[(,( 77K9.Fiʳ9z?2>fiZX ;: %gPF=4ݼGYSD"\sZhjn&++@uöϛC^^.y:UmOQ^Vۛ{a] [,|(ݘ"GH$S26n9QZl 4G]wN}}޲ʊaҒ&MNfL:yr5lڼo?_ZCgg˖.F)Gw8~~zNҚP3yS(Y|)m\< bDXfl~;nD"IffL /moZ>8ӦM!u\EuŮ/^)kWJVHʤ5 㐣qEYDqI%RjL!+K@Ody4Y~ϫLRh%lMwT1DbDAhl(mຟ pKoɈd|2Eiiɸ.!+^r\ԁ:ӛЎr-줤79ǒ.,'O~>hӦNaaa6.i"d]TvJjj&sx0yݍ9|(~|>z<7_=(kWd|ôMiI 7G}jڸ] yM̹b67GWW7{cO,;o'q>[r-eS]]m?QGCc#߸kH!ÏyO$,7nfJ:;HX%%tuw{[?@i+((;ne``^y+RpUkƢ\{Uy ӦMƦ& i`;d? )oqRQƑXzo?<|8T{Nは !U?QwߚR) m3;yH_^pPRE/ӂ(!U + |1#ptX*AJ4{4~S B>M+ ^(x1LV00TD Ǎ %I؂#I(Yt@;`ZHCg8I4Hdb|22hDt!@7u`#>vCyaj@ Hk[tww|>;yBxyר;yɓ9c:UUiii'Bc=ƒW;yKI\pZkˢ+W?-P a f $$ ɠm0ظUh IR,$J8LF HJhm37w'i.3^[Q@aO_ .UۚN`zLN.a&zsyZk"c*!W4/Ѱ,_|^f̘ƪI!mI<O[VQQپNlcvI0e(:|\cQ "BO·<ɛG{G3mUD=LMMfrME[eҭ L$ 8qK2DބC\ۄ[};`XXڭʭI;V) 6z>ΥY`ӎq…~@>(ۅZ=2{5"pO744RQQض>'~!n_ꪪTA˰, 内 S^64ac)5TWW}bir2,ۦ"Ck(q7D2-E8!E/sa:0E94!$^F2%>w}AGMNkIh—޹ҕD1#Ztm !0 %<ޕҘb^B.dң` -0D+pӴHhMrڱOKmR̫;#.]RXP@("28˶8nC![IX D"tvu(,(HE ("&(,(```xmmHiTnn.UU(0 [Z eRu5J)Z[ۨSr>}A܊<˲)--!hkk'''˲)//C%p(,*$gBi)O>-F)PJ}C99tvv2AdC`(DlE$qvu:Cy$A\KG13åu3Il45:HKvEDa-n&^PQ 28?d8oӧ?"5 ܌@`w|6-]|g9GeÏXje<ŋ?k~ t hiB!DOM"xQzd?Ccr ہ+(4 6pA~: 21.``p)%ZkL4L)--Ao5YY J[%$`!vl i@VV Ӵm8& cN}Ke>8~ skR˝:TKnNg"m~VOZgBiI*w<,^a|6~a֯sTWUtKºOfRXPŋRߗ3={1oq?oh}r}>u Iop0ʋ/ofdrr:k1-h7M\`!`F DKF%541MD1K5o1&u @ht7 .r{ZcFjqLF)ń 0 @ Ҽ*/+òpR99) LӤjDJCJI8 ??mmNi}8N8.{Ds׻^E#ǎ3`{slb;v2w=E BpP-=  N :bJd*ˉٻ@0+f,jpU+xɧX|5vKt0g+IOgC~fϚ @HnYwQ翧T* H $D&pnw};}ܙٙݞcݶs sιJ? O=NS9t :'(BGG'yyC4r? Vqm tܑkqq]~*\}-V1.^h r?g80\]q9ER~Ih4z=HƮiΊT*zVJ#ӂe\}_ߏFqnwVe\?௏NFZ\ޓbPJO]AZžŹ|C(,*&7/c'NՅfEgg'_+WseG-atvuq1zz;8hbtvv &tm@[{U?ygpvuPWKd\!3+у;`Xزm;~~~l޲-RO;N˶9v}}9l557S]S˱uK?Udepeooo}9۾s7zkjimm'$8i48677~uص/O/~ܲYQXh4+*/(ܹ {]褪 ohl6m.8tFr%gޛJvǷ| 8#yrp.'D~]oTDׇp ty!0A/jDwtLrd&w.okkgtfJĄ Q^^Z&U ))-ZOBڌT"#±Z4Аz{zihld d)>\v"3~\<ֺd .YD^~&e463QIL6]'ɿ#$$˙YޗeIxPRZ꒹ח,Z0Ιͱ㧸x90.ޑRf&.\>Lj\b`4^7#5S!AdHItttR\RʒE ?w˶/Ұ=L\N:CMmC;A]PM١ oVh?~D If xʪ*79W̘(V 4i"F0M&Pr 0446hff$(0̬lnnL$||9}vhnie``N|}pwsŊJOO#}}dfeϸqq; TVUsj.Ӧ$?ʏcTp2!AA455wA:GP_߀l]Z.ιBGG'q1cŎ &<, 75"eTfHfĸqcpifJ' u99WjVSќ9wV\^7&*&WT9LMNbⅴax' - gx\G"T(eu`48*|7$?/C[]7󥠰8|}}'>.jI> N˕yDF0hgDFc6$F!*2|fƘ1Qԡi  9tuY懎ۇ&h 7/jn`Z sggCNXX(\j0M>JɌlbF#Ad\!axƎ]&6f,<|)RU]ysgϱtbֺ61L,^/OO ֨dTb21MH8fF`V/~~ANF"##*Ƀd6sIBr-Hͭ7RFxIw2y׹qX#wދ(Eǖ~qG<wzwܠl53^~:<\O?\"'*v{o)&p2~8.Y'R].`+_=]],])I ړ Z.*GIiÈ@Ngqz܇u{qZ1>Wzh@n*#tj\\p~qiSI< F2DwW&v=(Uwע(KPA D>g ?üw-{= ` H޿.9?yǹ_XGLTG Zd u$; y4(|=h={pc?1:}Y,XD|`0`4TQ^^κuX~=z-ړÏ ., 3bPQ, fVEQF˘ +;EQeGttt5kO}}]]]wrV6#WZ^K#fMM/?rfMf3uu.=$˙u:v;/M[QΥ,[NGsK .gª9r.]˗/eYss 'N&i$&+yɘETdVq\䉉LINVI0 .3)^gg'O"4$YҸ|9 DCc#-LJgsK;v ˞[$Ie8*9W(+d BC\CώN&9ȲH9?ebY1(ByE%/e6>r޾>fHa0/9֛[s57 7qysPP@Uu ~Sk擿}κ摞ԓ$00??vGyE%'9i=u͛??qFw963;cƦf.\̒ ؼu;60mZ-ce8y̡#ǘvbՊ:sA vbJd۹r ^Ϭ4vGUU5U,\0>M«k^""<'OɑY3ٵgߣ(uE~ܲ¢b.\l&axv\ĩu:"#gαlb?bcL'<,1͓Bx9%EQ䧽Ҋưm&h(<<;c:'֡';pOO[V yV5صW^~gI|/o8v5#=/Cp1RP`w[Izw5wx"GLj7:^Gusa%c16C zW!G8[26:(s Zf0# ,{n gΝ$&#TWג63UL[?i3Sw==,Z81<4~v;9Wr5iS6u m؈,̛;rEANtB$x|N:Cn^>c\|OX>C># Fee 0ܩa훯sR*7^]BIi%5e&PRRFqI)<|)G9N9U+zEx|)39w~~,?;9v$qcIKMnJ^~>k^~3g344DQq +/?A;-o m &!a@pr&/gRЄ/=D(h4z{/ooG_e26z.Z9g ;-b>3hd6S\RqwwGVL=.p'( ELLLxEU<<_9q/Gb.ztJ;380NsHURQQ[oࠉ~<-M IDATb6^Y#<, ,w( ==l6G\Ibhh;](Zˋ!zz{qws}<? Ax khԄcnV\!kuDEFFHp0&6#/ɓ&:si&3c!*2޾>jk $(0JEez#(466C( }}}TZg}e+kחyxCCC\ͧWD$<< xx~ 1Ot:tV$9*~7MܜG`TTA*:qqZHDooZ[hll"0(eX7`UU5o&?β,mNbcƒê+/@]}_ 0 Ҳr_m;vKuM nn\tk=z=oqCA .^ʤz;w6niS z7u,cۙ1;b%Xr)+/łZfVzS$$I?3O{zz~vAAMw5 dqL6lV9,7ƦFQ `pn,&MLɓ&a2i蠡qCii9}|AALMN&=muh4/OOϝCcS9Wd‡sUF9y0@AA;e )t+/˙YxzyX0.W>Ogg39p/_fcT*/'O1mj2a&7/ɓw3}|w,6oGz83`0q. f 0ڍ8ƒXmHS⡩ի)*7qې`l;y 7wvY2̋W{~pwwg+{צ-((,eyVvϜh4`jwHU++aཷrV̮Ʒ2|]~AS&[IdD8e7N&?H&OJ$m */'TjJE5}JUƣ(=Ong,^VIJOO/'NFV>z^Xռ|&;p4G1YwY0x 0zxcȘ7.gRTRœY9v YvNlWx{y򋫩Sh4x~҇|G^y|ɰY9p%76='NAӓOiP/FQ^e bȲ[9s!+;_VXFIY9/pdhim%qϛCҤ\R,dǮ=0s?+>> W(ez/nI67^{pd nsy JgEZAUI(=Jy(}%eZMHH099Wٌj$NOP` .\b``2?HYYOֶ6j42sF*!!L:ÇT**+hhhDסQPdZM@?===TTV2.4wLk mHohMF#^^^x\( Xm{Ρzw)ܐd!- VJ]ƏS2wvn``Ǻq1pE&ML$3;PLILlL g_I:[zȊ#PSS3;weN,Q=pwwC۰PZ-d.LAdYF?@{G'^^LGg'F#zY3O_&Z ^ffgs9r󙒔9؈AA^'ODHpа2{yzhlW//O,V+fFj?[sd,ejk=oEA,CWRd0+H* ('$!E>t} 𧸴ؘj$$,^@cS3uueɓhkogtDvP5465 QQ+,`\ROo}Epp0j?QU]}(,.bÏصw~ HHUNU*$)(*-7)lRM}3m;غm'}7}Zps2\PPX). hkogyܧn> xWd1 XNLy'JxGn2ISgϡXGT'$IH>ڲm+>^ZvXhVx$r+ pK(*Ӧ 'yF_vשjVX }  QAAx a- b0ǂ 8zİ ///$'?JfVq1x{{?>.J Lߐ$ vFRis>cEQend2?{Gܣ 7Fo+<==`ytuuuN9b!3+NWw7c|Ï.e_ ,ZKnsalNWW7P\R7}OCc#Etu9=y=ͷ8r8.g(--CQnFr\Ee;Ξٸi lݾu_C[[;U|N:hc`|."|w6/޻:Η̹޳#ncٰX,EfaZ1 BeVGZ4EQZk( vŊ,.Vn9tw ÖL^)bb`(+AxUܾH> #9WpwwgܵHN7_$IdB 8sl_?lu8=/O_|{:p??k׏/()-l6ooeێ{>g+Wɘ6l IFk[;#_~-.\$>.M jpEE%=$Yiƌe$&}0N<1o8y !0 D  ,; L?wwwBCC7kYJJJÏvZ-^^DGah™qa9}}}}%::;QhGkTKSQ*X>l[?n6jY0͜9wW׼Ğ}x79s3RRر{s?| øݘ>+O]r,Y'zLQDGq.߱kEŸ1#e: /KlXﮥ3#2a9c=.-1axN9ˇ F˖m;Pr2>lv'| w־yװl|?ȱ+/QQUڙ_1d"&:W,#aΝ@rdg9 X eK/O?;v,C y ,歸2g9s8yYl޻&ޔ1a8<<>55DFD + 6ZM`pw~tZQUدU;'x rIRa4 fy ٺa$FOOvދ,˼t:~>̜N#!a<~oYbruĎ$8ɸ8 hjjfxȹo1U5|LZrcnnzF&OdhjJFOFh`4Ν-т * gujPoV~~nrwuuLIY9AJKl*)-Kktۮk).)aDf:ƏwY~7m6;yËW<" HQ%1w^qKNnz 'mF#*ٳ+߱V+hҊ7Jd2aZYvmX pDPUb︎ݝE ??yUm׻{oGRZFA?BA#*  QZQ~FB:?Ax=*p{)br ,VKpK.m;w#no9vSs U{zz͙:$2mTVV=`ijn +*illrj~-+͙ =* L>mtZhlGJRр1.(  L&11cqtJs=͎Fv <%9 ,ۇ/%%pss3W NysZ+W,~l6z=}Ȳ"+x{{!I}}}ȲJ~=&OCcsÏo$ www*+II>͹/ʠɄfhDׇl݇F$IIPUxttv톍H%*2Xg{EQZcpwAx JV%Zl©(rw)Pm.>#nnz-OeU5E%X,V_;oqQϝr7̝dJrյ9z_]:%g9Q3iiij5>SbgW/;l67s2c뎝 +/ 7wbb 2=6m;v9u,)Ә@Ε\HӾKJoM?̜BWwV,jaz::;埝ۏϒE PT?yА`6?nކ0 Xk/= Ϻι!ܣ`\*euCf>P廬RHMK|0=vYY\AfvE\Kb!>6 yWQTf>zs(,*S?n61憬33Sy7`{-־:Ex^Y{o%$8g\U^Ȳ_~M[[;sd"7SqgW߰wAeђy7@ebyg,YZN9o>xw-Fm__w3z.ࡡ!JJ)] ³lX `OO7m?.Y鏣l=P2u#V?0=ϲ!^~9(gjs.0wv.]b)(K900W׼lVBª=w^[2z+W{hسo?!/kj1LtwwPUjT*"KMm-6zBSs |w b-i4wgxg#ۋ/bD (*)%11eU5 0dB$4+IKUM5ZƈΞ6b.`FC\\ /zYi3{WD2o+k~;j6lܵJNIfd;AcSaaGYk] bXGT:A\6?IwO/~~>,^މ\ v\V`0Nn Vih4j<==1j5̟;Yt:gtusyyz`;76:^=R R(BCoJb̘(B0~#* u 45zwww@rL|\,E I0dfe3h26sC<i } flC/pxEJq4 φ[v w6ںAD  $ S&"rnJa74jԯ(/`]]}=FO||A; 'Z"l``?T/%K.?yj̹zO6dep%{eJN>C]}K>Z+-+l6?{Bn~M=~c'NrjyOp KJou_ (]}(=gEёOWQhjn7!KJDeQALCc#vR+*eEQhni!/^飯nSS[ռ|!jGҲ2Jʝ婪!׀u:o۹ BQq ^wuuWTR\Rfʪj@ooWT+H+OO#.g !L&3?lB_?E@MmgnH?0@yE%y( S^^n.˔QV^AMms۞^  ˋ?FGEQ%7/ֶvAjnnֶ6Ҕ80#;țٵ"( .^YٵgN'OSRRʅg{zؼu;m?yKYde琙АV:BSS3wfaٱZ455SPXD{{vpGk`^~!Y9SQY')++ɥHMFnƪ3ib"d\w-dfesK/,dml޺Ʀ&۰< BCB" Y~LILTj5:^(vqVEzN=OQI)9\\۰e{V^GѐsZزm]ܽw1A0b *$$xY :TSQwAؐt[R1mJ2YWe0=w___V+FYi3매 f!""]t:^X<|&%`fPX ynBtb6ehnn% IrCeIKjt-Og_ 21cDzdBKhjnw־$I|:m[[()-Czd"3+A!gAPlvoCn@JCi494jDGEFp >{0a<ӧNϗ[Z9|K,c{/(|}6u K[LEcfjQRRy+/@E0~awEqI)"ֆÇ#5Veޜ ؅jeMDFFJAVBT*AAV*FsB Zm_̙yZvdYf(g0xg/y핗@)L|I>e_Hd2#Ij\Ln~:mb_H/ӸRV䉉}u?A'>Bׄޑx $#l2[=3ggݜ3z*UuUJ^B #<$&1LHo##_Ȁ @|9q o7nfq8ڪ Mn) q\(&2FMrE!ӎTauH1E!33IUU%aÈaC9p0;z4E8L,I5UU<|h4J&alٺ q%&NOå˜9{#1t à Fc?qRYYˍ\gİ Lm۹XKc>ZMe LZb<[{Egg'K?(a%%y6bqRkܜ=_죹'N2|a0d`&N#sGx6źz<(r1y:BGgXiS oy]AFf&nʊAȊLeEE̝nϗz?c\E 6!h3Xc4u߯7~Ss4O΋/|%p<gG+aҤ Lzwgl@ ۹K. o ag+/}>r vd;СCdzEUU}atM';hǒ$ 7mp8LX`FA~>>_~M7F$֖q~f@h&HU?qijj/HO}C^LӤx.][?Q@  N'ӧMɓL:EQ_bʤ=cǏq{hjn{/_~Ə7`ijnr1{Llœٳ馾 :u [mrk`\XϏ~ ~Lv;-~d@ |9_ Ǘ-aРr tS CKY".^###NGRSZRˍ>}ɠrf͜|#GYb}I.^`),Y(DQ}>Z(**g>^ ||l…sמ?ODoDZ[9x[ c/#Gu,@ 9_)TEQj^x~99Y,($KIff"=\@o s4,ͰxJjNvΟ))'aKJEXYcƌgf.f _H>!FԜ:M ЎTW_wo8f7$α?~>s(b姫ٸy Xl:--|z ^֭'`Gښ2Fk?7|lw\>{pA^뛼7ٳw.Ç MƏ> *++X :ә|tR1'֐AIq#'eİac**x܌7OVnwPYUɌ!]0eE>VysrBocqZ0 N֜JfKN9ZA~0b]w]9q4A4jN"}.݇["gkϱ~&Z9Ys7ziGchljH鸍D"ܵ]H#lٶ`04`=3go1(,(H 狽پcXVMM39{'2y"'kNv;L^+>Z˖ ()lljbæ<|? >~McپsƍMotQ\TĥˍUY8!l3O?AVf>@pop+y[\c{%&IƎh1jNf!IQb~N:M0"ݗT\.1gLfϚᠥ'jزu;mmHK󒕕ܹlظGR3?ǎÜ8y@KL0SPO0׿y={], ?ٙ|n1{ fϚA]]=ӦNIW0 Fs{mv Kmttv袅lڴU?_|?E '33)'2g Ξ;ϼsؼud֌餥y)I:bel/Dq)'s^Al(==?g1cTTK^n.iTǝH4ʱc'X~#MhvN@ ^W8Zy$YwHCס;OJeDپca՚mKvɪ՟SO'/Dٺm"F)*,dQ;DcG4j-:fN8aܜ')+- txPTX@Yi)%T *ppYWU2zHTfu:O0M2227mbev6yy,ytkD)t2fH u)`4%r!2]]dee!_ dYnANNm,Kdu:Jh~8lrB@tdŴ?40t5aW%D^&Is#IOt]gL6E9pfB~fn7y0s4L0Ukt4 49t('N0y11yh\vÅ:͞Ő!S;Xq3$IJȲ[NG\_y_we왼{<3~&{z9~$]bhE K"eM(K{{ŢB!hkkC,ٖNH^;p/QJ s2m#4C#!t 4k11e C0$0$0%ٿ{VL.yom<݀]=)(B8h75k8a<^l޲-IDAT;>}pu6s^/ 2bss *Co#HOIJe1wljϝOwdgg%5*+̠ ?H4Jgw7Cf(=Qs vC޿8Q|h? ̣ҥ9b>n 4)--!?/B0m1Cr8;g~ Ǧl:}?Mz~UA $I";;ֶ6nlj҂D,x@HHKѕҥk&afGVd $'7f7iii|ls(H4iIUee6MedY!iP]ӑdd-,$KFV4DS3}֜  ロaֵ@ |ssQUwϹXgL͛; ۍe#^}x]̞$I|cTJ4bÇ C9eYoIPUUS{Y47ϗFqQ!n';+MM;@å;p1G?0e$_#;;4o55 XuUUlٺn?4 BcF`G+eFpc5Np8l*/} <8wt bP^MeeFOmkkt1t`fΘH^;w;6M`("&C àM|N($Yޔ;P(fLl6PM(&x4Neq_-Fp56\UUy-\]:u(݁TP.L,I+)&fJSHtC0e$YQiq4MCVUdUIJ%n4dSJHu@ _Rݯuk4ݠOGP +3̄4ߡ+%{'LӪ7!Y$EQPT͎bcTUA@  `$)+3PlNTt2ܬcZǕx\7Їn#չWu6;bIϨra6;>41 ]7pVae@ n} _e U dՉ"9 LSJ8w\n?#&7++crɊbIH6K>@o`8$KT͎e":&V@yOځ\u5Xo;@ގUE=@ ܄۰l&)nhIG:Q 01O˱&&]ں(]i1!-+7LLCvaHHs U 7 B(ǩXKXP$ǭ IZξ[0lA~|&BH_ شe+m;Ko!{;w~&JgÕr|idyvttpyMK#7KMC׋*a.nP(L($l7A6cvx)Aht]׹XWϲ%z=>F(./+c՚lشJ q\?y2ECt.]_yZکo`ޜ,ZM[w~JxIC__w_+/@Vf&~_5>cF   ʋcǮA]%|b)eT?ɆM1}*ӦL.eo^cttt%c._Ϝ3q.ە@&7Tb:&צ`^{MPt8~H EVQdY8Z,hտ\|:ūGc5¡zzZ2x"~w/`|fyWb^jC*zMJX 233(,(SOSs /gb;R>[Ix9)s(..rA,#-KqqP`(qcǐ+)?p]<2?4cFDM Ϝe)cJKp82?LhljOii V̝3+ܴe[rk\nlbӖmx<MS@ xйm207iH'UT3:k@4-N8F먪("+\v ]CJZJ,^{̤E0dgd N{mH73__`55dgeuraK\Xϟ^h4ʋ/21xzVlȒD" U>9D(2FChf:,t8y;veK>N=K{[;K{|ÁϗYزm;=~?Cr%-=,y }DDׂ6&##P8@P8L<ӢaY]$ UU1t].Sp\瑛u/eW}~S\. ?=e_()w;pM館 .voȌ3u79Yso.&&_9t(ǎ#IcnjC<2d#Ƭ)b~rs=b>pۮ4~W&MU+v`-! DeZ{(.)Gvp\#mLq BQj&-xM%V0f8ZFB @KzWk$K(!Rd9ru\z;iw[@UU(/+ 7ku_@ @gW'HKmfւۦcudLbJ0`J2aPu\"hL_L%SJ?ຮQ ~ k _{7~=ǯ;?Fc^[_"u{o/ym~cVu x=yoahɌM[|^_ᲐFW@c xp7~G-diDQ~}#oUkRWW$<JK߾}"8D"o_?u;vӧOݗUwt?RYQ#=s=/&% <4w$kqt-~I8Ot즺~vϖ`'=#;_ι XK۪BU-ј1ڀ!{|9g⣕,[&7'gJ TUV un7F2_0Bqa!,[z=uJ d}:[m Xd/Ws(Im+29_aADU0vjì>{P~=5u1 `-ʗ^x58z3z(T5UzJJ9Aͩ3wg@ .r1vؤrE:^ƺ/=8ƧeI&i===@P`Sma" |2}l>nl!.'pLrI(&:6 UUY{~s֞.ΜgW_Nv˲MU͏~*D F0‚hljbP9O=8u lٺʊA\nlP Pտ>^oum({p#/`M 1zȔLvMr?@ |^Y|&HEO!Ur_xpnn.ڿ$ ;x /<TUVV 'fyĢ1͛G /:~?I^^.. N'ӦL?4MzzyzͷC%||ר9uUkIJ%_  ɪR5Mw? K/r9Hg¸N_g,Z8'jm= DZo*sa:;;)/+#s_쀹9zJ-k5* vaْs,%č0}djNr0 k <=#?g 7疻d.`̣)(?r['ÏWzjk!2lgon5wNV~bJu=4M#L6%e{0Z&wty7<\XOo dn7a8g X |ܖ&݁.Edo-( DP |׈ -%zjr4$G/iȲ{ΨV$ @D0Mh )_2GsC@ E C@ ??<IENDB`gammaray-2.11.3/docs/manual/images/gammaray-qsggeometry.png000066400000000000000000002325541413310443000237220ustar00rootroot00000000000000PNG  IHDRi9 pHYs+ IDATxg\IM$ zT{Cbv{콡zlXNS($IvvBQA@繟;;3;p,_  j3Zu7  {1J2+;q$=?BJj}uQ*99y240T-6Өa A +;ðn TUsr5kjafV r99>$6i԰Ah4 $+D3 ssCs_ i  gg@AT Z4Az0  փ AA^h$ɆM[UN7Bј~d{  `9&X(a7m8w/SS6p߇;xӧB]k+˗hJ%ɾ j_A͂cO=x877$Is3S?_6\ѣ<#A3Pm(**:~}̚^Ǐ}!cfDWrMڡ]5+& ŏlTm  A;P ;ul5, ?ԻWA9%cO~Agiѻg&vխw%bS,L&;ϙ#H٩_p]-TR2m&`+Rc!D޺s3D*aX;vPWm=4>n׮MRMMM;r(% з^ݺ7mѴu BQgԈaKWYBVN vhnfh&N6a>z N>a,lݶ3:6dxu=rNÒe+:xŚ:%.y3s6V(V(AԵZjQ#wqL&+wO=~ݭoP*AP-U94Addf^ؔJʺs/8xXjs_yq-++;;+{˗.NIIp2~qX|U˗;TK,0v4`ۖ ۶l!%,l^^'NM4>l 湻VҮGTtBdff5j@s(8Mvx?&&ƟFb+JE,G6mVPPס}>e0~>ѱ\ɳA}8Sr,πgll||##Ck*133q/?iᆀFx Cѱinn^+,9$Iب/ו{doޚ0n (ڿoR)ÆX,ccAiBX@iT*{ACw7ה4=P(f ٵsZzwoj߾Q]  ,--1kbbWGmI\r:ITՖKWYpko.]^:b׾V>z)&hndT.믰232??w?7EdɣU$Ts s*<++[S'@F6_]eogKmpl_n\m(˳*xr+477:xsPt񂲥zXz݂*AO/H*֣ӳ\uIIML|;wPݭ.g]}w֍_ײE$ FPMS@A5Wr*2aT(%8Wm-APU@eWRR [NAz0  փ AA h  $bIu124***V8yyϟWw+  /Ź[AA-' j=@AT Z4Az0  փ AA h  `@AAP j=@AT Z4A[aQ鳫T;rw?78SƍS3!O6h93C+Xå=-M..Fϯ3BQ?T\ԥӸ1,--]TUh&♈g#APݷ;\EKW@+n9)iʴ{>̘=,_W=z%H|*!--#C#Ö-~`asXp!} ڼu;.޽0l{ 2|qc'LV(AAA[G"`л픹ڴvp)uƲ &&={y|A7j@ᨱ_aSΞNTJ* AW499ϞpsvL'}?{P(1q\XWWgPL\oDޢv݈Bi˶;灌AFJjڹ ?|`oh4ZEڷkۡ}ۍ[\fð5lk]:u/_XA_ (nw5t:>|4#-Z0J422j̑ .Yw7/^T8Mkc<ݴ+v=Coe7oM7EQ7{T~B1#4<=EL!9 X,}wL,W)_A`PϠ XްaE Rt>ܼ<^I\ٱC;CCMJUj/FE2E\<=oT*eTb;NJOOW*U$IׯE߼vm[7slcA|h ;vhaքo^Jhڤ ++$1'3R-0=E8NPaw[⋿dddW)_AZNׇq7nF<șӧjUT{U(L4~ =7slZXX|?/ԱO 򸹹ſxɖklmSooWj?ҥ>f:% Gofjڃ4zuxP;vqvju#򶻛 ~iQW!*7?=rj"9_,UgbkKX8I8.#Hn:*z]F-+t۱'}۷~ԩc{j}6?~?Ji7$۴00(<#4t=YTقfC:cN߁C1 +SƏ1->+U`_@fwvjqr}FCCr;ATt}to j=@AT Z4Az0  փ AA h  `@AAP j=@AT}m۹r  ȟFnԾЌy Иi[ c9RH#)} 52;6/=Ӎ[M Hۗ\% 9t2Udu~r0R  փ AA h y못Xz]݊˓! H?X ey9ׯoݨ_\r%Î|Uf |gΞ`RD /bREIQΞy *+:6ňG1)A+XٳUڶ@AΈ}.l6wz2L.OOR$IfLjdlC gdTÇ۵S 6mJ$ڼqil Aݽ{?)JD:Z?w1ydQJaQ\ U b$ $R F@ .\J$B,C#c}V-{DEEYL*a0Ys_wF^ФyήݖR| k[;)RmtziJGR 񿇰l@-b2^ ;&OJOil6K #$JH,(ȍΌ9p\g`R @T$zq5[ʩTJ 4:RTeLK}#.,421iܼ-_'sa~ճ~倦/߼V*P?4v_<)إܔj9_ZjJEpL".*,?KWNw753/˵F3&/?_(T.BQP(D66ϯ_^|{+K˲ezw3i)Ʀf(^<ѱlkV^/.*ԤPVU**fLǖ,YH<KT* ƪL*?2߱vE8N'T*SsשKe(*,xԥM KETKˡ/EEBeaeN%N<=vHB;{'~Hck=!P$LI)f_h}g3gLgj.n>u##55sEryjn5\pv%mofaf2>qc-tulͣNsS͹gNxػGAl6e\v[,lNǤnrr9ӛFJ*gORl6ǡ~F~|dmR={lˡJlwTN1upa^NbYzb8~kWſam]RRSEBP(R |{*|ps AUg~]'i`hhjjرEZ1[L ӻvho>{{Y$I8`2M–d2NgT|M w4'}x`2(cDB:MWY~zGv'}=cOXӢPc. IΛ[ dVVT#m[R{1l_te+`k{Ԑ+Oă[7lGS`Z{9.n1]:7@)1=ER)VvkNb)rRW_2RUn~=y3uO?Y?w'եkA_Bzö {URBQP,D)66Ԭu}G ߹NUn{{wwlmmN=)8s_ɓ&T[ˀN<^zxܞPd o;uj،*.]~Jآ!^=ziuӠaAu؟o^;Y]}ף\ܶ]pqkP~JHGsT4=W+U'UA ۷@WяgAcc"Ks#=>{/y99)))g_~凷|0ɵMc{v>w㻷i  ?&}=eךZߤ)Eeq]ծ\.rF SEXZu{ݫJ4_̦բ\yR`v= e싹n7.׾[i_T\I%}p8G2 wnpws}ݫg]oU~MRm#!5]7Y(Ś<<܃zqyd<' c&Nc8ѨEZǏi~0щbwi'p2Hb@09=5`21TS\peq}+yݓ\c3A2R˩M"9jįʦբF]tz٣b^n\=3621b_o4Qf7;3ږ_6VV 4|m]ͣ'O Tn^zP,Ju~v$[§;P(ǍQ b)uھP %MjW>zݿq2o艹oaidr4wkJeEʾ~;R&o_۳cLYxը&\AFM^||^&-LVrjڰ/Um(5melLHH=$R)\BњO5BbP?>}o\8ϟܻݳj9U M th&33KTR]]Vݨ+]kV-`8'ԫPv"MM-[&O~҉S '>NCM"{{{ryM _44hhc?sn^:Ftmǰ[Q9YZ錨QkicӦR@&cS)\9s3-\[wC;\151zuoڦ 7W\T8a|&vĥVb? IDAT[Y߻aae޺T*F)W~M_3oG(_lǀ:@" [7WSs/ZXKٖW%EZZI%b;ԥ|J˭ HxƱiR_,_ B(%Y( EBQJDS!{>~Ϳm Ξ:u&W U뗔ljllP/;L30]tI yv;s{NLVɌyo\MczZ䱫gGvL]k`kώٰcͼT.K&sVpX'8 \۶7b ||6ASAc&0̲ͨ`&\f._sı'( k;&wx/ML4h1&YXofm9T+Wv3ҳKn+VJ\.q& SJ3S6ٶcו 5yB(9Y($R8V 6ڹ6.G{aiډϢc*xpObVXiV.cR6n8q?gTM_%<+UED$%SW%%^usGЫi~w}5=euz<0 aڷIB>ށoO1?m/:kĈ9;Ԥ\`R9'L0nt-q[Ǐ/7A>܌z ??W@3Sӳ]}88BAbE^N444$bZȤ-aB&[0== &8; }A%_rj[mX*6jӦu?xw=|xrśoD3?$nVGr,l  ȫ=x߹Qe2@T:vbھ!:Rz2lPRR(n.ϢbZ{3ܶ}^nn^P$ "Q(E$% E|{;@ 7nؠgǴW-/gKttMl嫙!͚))9>iSfqa)<}'O߿Pe5 vOLL 2|Pn6m=lݾqybbJ%:F$~٥ķOp\RFPqoղ%_wWӨ/|mh ]+\teЩlٺ}քRvރK]סTS5rjRWJJ.{P(2۴u;B1 >j߮m7_3qTL*a\yƕrpD0 |3SEQC7CC&M -Ф2̲jP^Nn-]\[Vz'Omڼ-#M$.*(NdI\+T?d$J."|>iF^ݺFs7ߜ̴dC#ݻ̘>MWu8P*jďg6nTCö^6ܺqeN6A".=}ݺM}(@J fqaAnM[gMFS4VV<}䠁5bb\\Z-8~1ֵlmw}Zo-;&9)vF\W~ȈߪeK֭^S+l^I[\*۪D\β}R/m934D;:wyAS =Udae=N`@AT+}(+^XzZ:Rq}h^}z<߮M3{vwLJңzkRߜ29!4APi}l޲g`$R)6v)C2qs؎yS1qe.ZO$I$HI$A$II TI$ A FC 4*@JCAh=h>BSV֚6c3$_r,MR"3 =yv(<}鳘xWg{Mte`dᠽz~Qs|1YݭVw+j"xe~A&l,Væe8fc'hڤ54,=}w7j,400033 KI fbbH IP)W7L %"+[;avNijF!ޤ!4: NC:PbFGhA kUGUDU5VddV.PSZWIqG (0UogmLO y6J2P(ĝ\NTU E)eWdO?zE[{8`5.*,LOq8\S3㍚6SM=ooB/ ^_xj'a4ڝ;7m}̴Y3ǃ9Lڸe^(|#G*U*P Ril]v^Rlڼs073ۿw*+ R+r\PJB+J\P*2BS( \.W( \P(\Fә b1LdY,b2YL&d1,b0 6URd2ŵ0J6,&d0X,fi+7?QV64:_PԐ2̙X"'{M ч`9Yx铧QWd5߽'O^ymVVo}=,͘էc[u}>̾ 2W8{>ښ>+pIvh'&% iekoffQNݏ$@غ6 wQ8+#N`eey] +((066Ğ~R, B!SO Bi)B+;C#ëHN: p uIrs3S-ML9q^"zEs-_ H_sGm>hiɓg.zbsh33 hF"226KLBAvٿgAj P~>Vfl6%xeg;+UJTA~ҵ${bIf<*ncmZ6 k't]5| APmһ@63|Kn /bRz\e8dre8d8..S*G8.) 6aS96p8lX,6aZ3zB8&.,@<;{@,[t|]E*PnD\qSy APq9ڕ upw{,*G[6h^$H z\.cs؋hb1Y,7-$I*ܡbaԖ\*e^^>^^w#v^U@XnD2oofڴаH,nצK\K`@ATsyzgef&ZZٶo'sk+VJbY?~,7+KVQ]nݺggo_49kH4SǠxyl^od&ʑ}~ 2Y~n&+k*drJ)bFJ(]k~};tRTP*\ؾ|K޾{W ?;'gu{`@ATCm۶377Ӈ7$kb96AFڱJ30$HՂs ҅12oZ@2zHh ܃SGQ8. z *4ҌIbkEEő*Ǐnߩ7I%b@zJ7/zO_S|zAرgqZ%Ixf d&Κ>^7n;6/ψaC>7oޑ}5FrQ*seʭMשS2K\&{m]=ZdٵդH1l WNԕY=" itկRXTj͆GOr^F9Zɗ*BͮD\cҫׯ*ӧk7fgP U&;rV]d0t&Ig΢q8l66IBRJP*JRT*TZTTTT*RRgrQIDPtAt? I$ETPy9YCi޼NRA* T(NQ)UIT|X"rZhV^w[E}ixSZzWU˖0m;v-c 6cNpP[6[7H`TL+fϘ֮Mk&I$ 7/չՎmsꪭSZeO՝SϦk|i΂-~+r%I9P*w;g,_h0\qLQaSv9Zɗ*cbeWO4p[f ~f0XbMJ͚oM%x)[`2X%olmL&Q~: &W@$Yn꟏>eigd2Yv6Z9i4h4Bi4:N+N34F2lRAU;ՕМp)=_<{J}y .5۝:?{ 5-==#c@`fii1f;K4;w:ybN]j&//¼)tV#X8ܣ/_%̼3OIdY!.\&bR)&W(]]a1oWeQTݹ\/¥,ޜիke46]D4j԰I{(Hsrs19i¸JE3 +u=pbIR)&Ũt)R1 +(,`apiEQpPrP.>]l ۫+A0dX'?/pEr0 1LoT a}pRxUͥRo*Vp(ʡeȩ8ڎR*84{Յzn鲕#oJ$9u]Maa5Οkrlr¥ d2Y.~7f7]}}oݹ]$3+ ܽ#d.YrkW- ر}[KK̬+Vݸyzj+{j:v|~e Xt6) 4蝻edfPn,0 p8ljIz/}m7n9O63 -OyksMhia]{K4n]`NQAocUZ[axSRIVvvH8c8fQEq *)1Ҏ\ExJg0j+ m5g,236dK|*qK/HOO?x;UkF䪵iܨ9yt>| m{ݳGVV;f[}FP^imeUiS0c|FVҴ=b؜j+ԺDexuZ MAܱ1#|u˖,կJEQPH$(ըkeJ9z씕=`/=rhÖk0aTnnjivg:{֢gh4qpbRǤR)^}&ÉSY)w=QX|'a1 ;|X\c]y{֬ Iȱ]:wQ 23'΃~ IDAT?6].ǏbQ2گi ?~2IyX[Yx.P(\޴qc=4{C+-O>O[.K_/#Tʀ!ÙLS&ثG!~܅АIkԍ[;Fu~ʤ dFޣGSS{6eRW~h7L[*X, kwٻjr钅zUYNzƀt:=_⟅e/ŏYC/}P)iiqsؼ93Qgo޾۸eۋ/D9YFROd%d2YVX&-Fgg"5wҨ( *.4O'B53mu@=ӈrЯeֵSNj/EV6 mrƍV=Μ3H^~A.κr\\BH[Zq۸fL-ɩBhаQTkjm\_Y]*re]Ϳw~ѻ^ȹu3BX{ >%t1q[n$_CFM_<}OIR 1.p e85H.FJ2\a9\AQf(p B(ePIKz}릯js'OXtLnؠ0{Fh\3Uw_s|ͥ2vwޭ[^fX]eKŤL; A/ 꿵'94C6?[98;9jiei]ϠA;v8۷XfW54A5BH{>pf]דGw/yIFIb1Y,c-H$`&H.GäSQcHL&(  Pq?U_|"WW+Fcv,||qQQ{d2..[9r233ݻϬtQvfE%\yӺ4A5BLOުJ@vf:p5KSd6mݻk&ᣀ|Ay`L&0LQf2u[,77G(*٥ƍ!BQrۋiEEd<.M*J9t`W'Oߓ'Tn??_@JjjLLÇ%Jr338%%fu1 h ߙ%*dGGE/դqF^ѣGUk6fM~u` Jn{I$H*̬, S g`XyB&r#@iC\.(o9h#'/=B!]pgϊ4A/OP.J[B8t -[pfhOw*JjV}ʢhrQEԛ{J%RL*fff5M&T*5$ * j=)ռE. kO6K"|qM"<}C`c_9u3xf h jsf\.+#E\Thw004x655YT͛WXARSFM ԧ̬l RekGdʢ~iTT5aPd L*-edfPϔQS%Qa6[{4PPd=y%B r_w:޺uKeR]]\ gkgH4n*?{gD=3[2NBVED@6DSTE\qYd@UvֽtKL֙1mII;w9s̹{%$4,&݋/xw؀GerO#*2"EqJD >v=IRc5MLLG>sCN pMk8NDK%.] #dwJyhq2T/;yh4JT~T ۇvPMx!YR*o>rLA^N~nB&: |qC:}S o@kwZo'ӫn >h#LɎ>6^|QOШ{{(>|..)ˎ_ڣSstEZ+VTurLYQ'EFQ7͜|~4VsV8kS>&? Hp]xxXLto@k{Ro׷É ,zx|Ū_~HvЀk %~;^-,_\M؏2 >  n aJF,VR^/y d1 se%I*.z)s&2L&B @SWV[)SgKo隒ɲĤ17ݸ߯[u@$CGQAidf('_wN7,yysg}g >Eд['2һ:(; po%9`}F' W冪2 qjS9 jJ8'Y l;OAg|Dv;ωw,k{|CHS4m  zNqz&oش?]3_޻W\7,c-H-JY1TdyϗUF`0mǏ{pS\Q{(*4 j!ɓ&nuo\n:aOFI"k7  9v7 iP YO 'YҁEE:u-[?ƞ8f1F&=1QW{bϝEThAISz0~4ühJ'q[*U,xy(UA.|OtQQq͢XTe.(5'4UAj pS'@*?rq]:>羇5sL6 H sYOϞyn?mFQA EtZ7vj}n~|Sc,`@GNE&,@iBP(>xi78+r  7VʚϋX8NSmuY*Z_,\!f &0R)L!Mb2QSK$o}h᫋zrH└iCДWTwHgʩCN9UϜ5-ݺt~fޜujX[8+qLQX {v9ߓFN Ѹw~c>+Y$IH>YΝϸ3x#Al>˸QnZt_!8x>Ŋ/Ϝ=/׻wa]MCԫ[w-b؉?oX`H^=/E~aGkԋda `IJ1@L"m)Vf&Qիb5Kf29lR S8Uu*2Jr%\p^FRhս zumsmS&Oz$ϿU*<2QhX]wn"y𑕫de$ѭk>K߳X,'&2aj^~Q7+4M' ^]ժܼ|O*U}?:tU 6jiN/K{iaᩧ{ṧ'j}ϽWj?zF>B;F𑅯aI<V[&df@|bҘqvms_vY# m@@@Gm$I(PB4ެ-CCCCCC-(B}aU@EEC:&oר9ξ^M^(0yX %6HPRD#|q'bQc_-j:TV 6yŗ_71nlWzuѿW~icaǮ?>}YS7maÆif'O~9V(~.xUuc/'(|<**r-b A ߿߯۶@iiiTNtǂ!gY4FfW>#M+t:>91O{h\GKocs3!q5D2;va2oɌZjm0.ZQS'ߙR# !"p@( 0OId6&?_d6My٤Ml2fd2LfeY*JeYUBYX̥QE3FR*4JHtص;͓U*4gΞݰrR. zWc&dXϲ NP("0 @BdWFJ E 2Gϟ8[rE}˙YÞ}Д.xyGw^޻&?;I(0MUV <_'φMYlqİ?9Qŗ_[t״tmIkiwiiǕ>p/ ^gݻۚ=y3xJnjUuF~==Ȉ;7\Z(Iҽ,3/eKvm'm[vmԓq5D8L۳؄ĸ^6ybE"Y"%sdFa)K[#F4 MiH0Jf14F)(d>‡ 㻅fگ5jM0$=믻{߻/< @||q\I .^qLL)Ν_U打@y䭛74XᚯvT?uwXWu}; y쑇e6#hޜW2;:zNC/xq /ܩ#XVV@ll A?~W'8NOhn=jO 8E8Bm6 ͐~9:\ID*XrA#Jh"ykAH%&HNVhAO?\֫+}Ь^CNk괻~E:.˻p "&:g/[$<_Fի؛G@yEK _(//6-ܲZ_Zxѫn*tǦz=iuJ:»[bcjͿh6{׮}WϗyQǂ'gw 8Q>j,;K} o]# y?,g\pU j"}JsNk8-F9:AME*%pSeˬAIHPScT4St[5j(O0ϵhB7kW~.|y &:-m$ll߱KkR>0Yfwx!klYfW(?,,LNqS̚0~lhHG]5:z_~ZGuڅw/W(Fu;29>zffC3g5{|kf>۔9 8QnZt̼9n}mNbL9gGEFs3_w߷@]: l IY9ffiG87Mq>Lưt.{hɶ1"LjKIaL0E$xHHG8e(՗;~5.o3l@L yq{N|W2Hݳsyܚ+Y>YYwxןc; ?y~\x3CҘ7n*2IY-in wt `gVF m'$H(5?_I|xM-;~mDO`Ν:tpݫ{KK-NMws~%Һi$$Afee7K 9SU޺u( IDATʆ ? iZ/duoi1Grpvz F B`B tSN .4D m A.11+'Xvn7cpH%5LnnCeev? MNiݳ_{bK9fZSйKcBNO; .UJf$vkC$]. &ɣk( m\-Lg˥YRG d$TYvںPpN󜁐A>VhwӢ ̰W{G;НBHG-gʼ{v5mS#{:Tp\ZQ8imn): #h4Meaj 7|WV- ~jm?v>7i'A:{2s-0=-; NM3WOU#'L3`oӣb"cAIQ%ikL;ꑮ}ڋu)I&^g \(Ths9IG+5 b$032KNY&֯IKNh8}h `~fc-B쨩yR[lTccUR0"zt(l̲MaVdUt %Iڿg{myiS İ\x)qV܂4 9E:u;ȵat:o۶~-4OVN]n`JI^޴c EĶ 6=l K#g js!/2KOY/UJ IPd$ /Fy(ox%X^5QgK;k|Y$ 6dFP$w*ei_UQ4<ۦiKz_en&쎿pin4uˆLOVt w.g8ss~˯9غb/nY5Gx%B3%ޜ-0G MQ-GfTSvs=Hr^杓US2:M}R7s|tMZ=T 5gP="d!%TJ`%(2Kfŋ8mY"YC:*Iܡi $k/"I{ .67:];h@ *O#]r~2qoʽk(3O0` CKi.U$]s ( NT`p֎V|R*o|ᣳhq :<.Vha3I[wܷBx'3մM$HSriEȫNIIEb.u\88t/_o>*4yNdIȘBqWX$7}|FJi'A:{2s-Nsg:w$q!OQh~C wO6 <{zqZiS<. sUi5-Zw+bY+hXGY<&ގ}n GVE1rϔ8Svc{u 4'I:mڟN.[ݲ5߽k$)r_NR6~LH)* EbʥVgX7g٠K}sf$mβJ)RdC-@0 `0Qbz4>?l::$`ޘ%o}]C ZzZ$WEX fucVQٍQ:SG idUQCRQpT0tI['p)0p{>*4r$}M?>~Ԟn6.=ݳ5J~8z "'s]1, !J4H5D'ϕ``4frW"g}9)Cb tR/oM! 1䤕=\yY͏3(5i[l_-GiG=؅Yv74 nt#_`p#66F/Xk wǯp `捲ZA{% J$IE@qAAӎz+ a_w0KU@EB;CI:J^-W8X,%1'ٕYr4~=?ߞuSMFZ$߃٘e˨;j~QR0"zt(l̲MaVdUt %Iڿg{myiS İ\xZmNu斢U~Qo~V¦F޾׹SGUB3dGX,>c4X @UfhB 6

SKH$jl{7*>? d;::4H2BAY bUshM5$wT$G-QC;Hx -A}EmQJb~O#[+h rpa](oB)I$HGJĥ7(WwbeuV7! I;;b WcfJY(5CTXwm4=/Wϥ19/\fOHHj)IFl>s3nWXf1lsq^_:)>ԋֆ6V;\{+,5LnnZ-:.+˟KN>*4׍y݈N/q=v ]CɝyC<^ E:m6iVwtTaRYm*4 2zA\҂4ZhAZTh\$R h i=g2H0֞$@I­\c)ݝGMYGCDQAڒ#|s Dnd:%%8[.}vΚo:j%7e ݲ}t9ЎE=a6X=mj$yOGΗK+2{kn)?e45Mz}YxxXcQ),uJ%KRD8*4N_RCe$I~QhbY"@x Hp_'z~Ofn|ޢzG\Aө{)ΖuJ+H=& C$@xWșN>!$x 3!KNGj0pP>\UhwbNRl, N-G>Zw 4 v菱}s+mʐ )1;S6:ZWNQHG2xr|lw,~igQxddcĪ_BZ`A HLP$`ޘ* (R\(2Il(?ݚ6d 6(kHS%/NG 8ݴ®-B-",1+(W U(4ttڪ(!x(XE8^*jOA>̯@\xb\/ >*4ez8N?x)uI~y)*bz2=~H,UaN]Muưl*= se"(M\qrvwUb@rZSGTz0KNZڳUǺ1 >s;ufiۢaqԘvԃ]eqw*Vé[ϰCp¶\N{ _A%I?KF3Y.6R:iG=ҕQPX{B3%lKsu9^ s<@ts9IG+J b$032KNY&zx~iFE=Y %#G,fEmQ_EP${ٖ9?e M Klf7:EGE5 _[:S' V=m{#+Q2rT}#,W}xpOڱHiܰ-Wؖ+D):/V,Zh~lйhܻs{DTt@3)$LtD\`D*mޠ\q޶ӃߏYݘ/$6\UDz1o1MO3%rn!*|6p,x~w@hNG 8ֻw)4m9[\Y+ELj5NEE-3:ӓi tF<_Ȑ\xiCS&D 7~ |Gߵo7*YU~Sg<pEmEmh,E(rU^ L,9eE'8+LܘeۘUw`8Ml<]nƧ ,Ҽ5#Un$lӛr3q^|PE jhe6HB5q7.11+'g0 FnH7t?qo7!$x 3!KNGj Uvhhpe dhӖaaŇg{;R>`DB;rp3ugFWGhI'IMO CS^VO.xuٚ%ԈұISY6L+; iEA"1ARl3jlP d3 Pd6gF%Py"'lȴ&l"Q א Ju_&::$`ޘ%o}]C ZzZ$WEX fucVQٍQ:SG idUQCRQpT0t FP nh>/pUHS_<|sr`6-aLoui7JJcb K(H53 ]Yi%a r֡w8yL5CV+3yb@sZSR6^tߢ Cb^杓Vc%%gz*NWd4пӶ,b[W%K$z igΝG 8¥&ql2 66fho}Th7J} ~ٗn Gugƴ(HW(iwPQx}t!'(yE\`XlbĜffWf)Doӏ4ͨDm6"A,[F`D<>Qؘe¬Ȱ-J@<6,AvaRWtw^-ڑ _[:Ҹ&呄wk)>*4CF޻OXxbI;v@AlfeCo8-Wؖ+D):/V,<ꅇ>pNP>SKH$jl{7*N1 ;P>X\`~t>D+ ݁54 Q tԐR8BiKz_EzE[ߓy{h4rpa](s x4:Iu@MIan;vFV- ׍_sWR^*4IFQ!NMxއ)Iʋ#8{0]v4xuw^1v$ћ{rtMZ=T {\% `~⅗㴍gd !Pd^@w"yOC&uI{[W` /"c?EQ\{a[|nu(E0iH*N%؝/ܙLD+ Z|4x}p] 57Yt63 f VӓUbQgzXtjW8C5?~2'2' +qP,|δ&@EA׎ANmYm0:"P0"Po%)I"v#Ҥ؃ >{paV+^>Zh~lйhܻs{DT<:oL|Bhx cה=A>B)I$HGJĥ7(WwbeuV7! I;;SDz1o1MO3%rn!*|6p,x~w@hSyB֜v:Z"D||zW2  6g +zb鴵eF'zz2-Lg7LJMb+)0;th5])a M.#P}e$ 1!$N&>*4~߼Qɪz:AWOP~*9eЬȰIю#X8P++Xrʊ>@䜼ɢ,Ƭibs3nWXf1lsq^_:)>ԋj(AMX)"zAVTbiTk&~paMj(GHЎ#;jGNk:#1~[5ܙ'0oSDΖH;g9V/#ǎKMȅJ^10$Wy `I.DiJ wP7 FHpLQB${z_ Rf{>E;_؍0͍O!9["[ݴyKA~.~|ޔ"y])dăE"1 `ӛ.J4 õw:jʟ(h$HjVhZE"T׀]5 28A_~~rX\/1I& / dIyVBYib.58$PWd&bJLd@ >LThAE߾#}$IWrn:CRR.|dReB*ŕ&QO)XRQJT,G)9R"IyH0DM^ /=|lgIy7/gfԛ 64*4 y+)NJM3wʞ4 kc)HHP(+52k*M&7#ϛMFσJUXcUjVbUrJSSX8JE蘃C8SF^, uΜPzՑI9EiG՞7 ޓQAiNnqUƷf@6 I`M)uHBX\`s 'N֥Ox7*kZFx 73o+|?1kfR1T*pXn]6{61$IjZmAF`yh UDyQ^052y}̨P*ծ*clR.9T%r*ť1!TRmYB4"P9{PvÐk8iJ+3/G.NnvG{ Hvy>nFv9OߨRe 0aaLXKcFU!`0T򆫅<x= :6?>'`ϴA:yp/~޼emkڽxah [eUlTTWDQy{/d*ݙZ6؝k܃T"N45J9h6a0hXlXƍ3g  MV]-2q+V2'f3&iG5$5FҘb;A׸UD|Yn^l ejYR-7ja㰉LHV,]pƏ,\gZ'2UJUWB ,k?u럘=3OoX|bzǝC}כDX Q(ü-h45(o$Y-z|*PHVUJc_)8h~}amkqo/<;[&;-n4Dy{ ICE.-4vBIhhI֭egͦNeOM -HT,bY(U; B|hnyKvf(x+׮ݰ A@@R!EjPN\RQ,}J.g|.P(r:czSy$*Hqc. /ij@ B5@nܺUL&OHHOH|(lyk;:mq S8!HS Hp6Mafcl69(Oʙb@1/ر3K6u /))[;t'O7M<\v `g(h  knl"ϟx$>7n/W3yi29?98j=_R?D$ff ,qLFmQfs8l65\r`s\9pb-cG8|4ʩN| srۺe];"R>~OH{mU͛5mֶ};͛ƌqٝr՚w)IeRrr%[ \~֬4@$[bSy ~j6e?нqs/z $Iyrd2Lt{hXpf9.|)$ Cz/MT)5BC`Pƕ<}llwԯW~Cc))ڹuQܓn=秧}ZZOxbv l޺-K\"F{ i3lɂb7o\H$OMmѸ"VC5{OT*լ+4Ԏ8ggHU촪HrB+s!i7pؘw],؋\.gsĎ+tIV-[j91NoNӛF) {0h47ӦR`@-=L*IB*}X|@Pce-#Q/_JeM57;~z' D~Qiެih4sEHD4ˉCp84:}yJHSX,jxeJq.+P;hڍ$$uXj`Cm{iiۍ}l6 77(`\.0wL^^t ƺuܭkg@A3 GK"\,|~&>LA)gsBu'OyV*5lS֭c^b8sszeKLvapڎ} ޛq0E1ٳyy]Nmٶ3lCwuof]Or땱Z߾=>}.&!!'~3`я7o&}^NܳoU'Ys,Z0KPwp~wAmM3 9eD׸iݶm:.?nG1ٓNHp\%v֨" =v#ja_IRU(8գ6ĥمÆ4C8o T:sY37G;kfwm :e0.t54yyy[ܲq13SЫDz tځko˵0h,QfKJ+ rVVm۴.4* A;x>G`gg;f!s&M,_?%by MNz|BѶu+۶iN]W/_uZ [Ү{e滂N[YZZYZq/NTX3\H=NOc31/mۏFiGoQelQܪ5L*EssBglٶs9=pP'{t~M60SQ\\LCvM;.c'[G$lAC5mC ǽ[XX%E.}K<}*)Cf4rKCX9=G"j$5NjakOS;:i e5NJp~1k7n\&+s~G'Wo94kִ^ݺ7nW0x1 FG`өS;[[ kwYpJeݺvБc\T%AHl %,}K<|}eg;xhيUoeB(.7T*^~o]p>~ԬiJ8~L/{<|anP(lAcPܕHh9~TА@fdc+cvĜ>Y$:g swߍ B }l\=q S<9Ri?=^**ȗE=C n}   hv9 IR$ fcv9Pi79МD, =zt2c4R"Co`ڶRdc_}g,Z\=2mE<::E3+++HtRlV-v^8H._yiRhܸ1F3~Lɩy&>P6ǂͱXJJ8AAKq8T{E c1 cc cYl9fa,c1fX,6ldO@"9\mcШQn?9:rD:ZM)h=vvYQ'{{߬iKKKH|ÂJM/_t-!mH!:c CyEbz9¥4ڴo*<' 0hJ_3uQ(<~" ǣOXҚ\ yhԯEBǂ48@+h7jbM\|p;_d2#6߽CGQiӪT-٣J˗FZ.dp Kdr"K$:DŽ㸄  . j(B.cFm ccv6q8l 8a48qg~Hok_ۺ\xM&̝y; 왻qy>űLp|%Hdeeէw#3޻'Nl6jԨavrrrJ+%$wrVsyxV1~ )K͹޷ij6҈Jp8TLp|?sqtڵk>w[w5b Y!M.NNL-4@)[h ~G//~Dk҄_E; <( %\WQv L5QFTNRTأ9!AH,>h:רfh;>f@^_`?wCgN8>Kδ)/99(e {9A 5v⎭u?n۾kɍܷOׯ\o؈8ph$L/خݺj-M0ێ.]53NaCG)##cڌe:4Ru{̹Q( ;lb$#vNN~ƤFڣwb`ݸڵ;Ax͎ pj/_rE\[\$lG̨ L=LۭiFRai蒖6tTP݃АuԱOt vMħVm3+$xߦ#I%$ǖ (h r.bfzZNV&c!ߧ|6k9&RJ"@MA#7 d2,P*8NHp;LeF/ YYjf$(vifj#sM=fcƖ :blj/ Uә;thwq9q4+խ9KĶ'6iye ]8KNNV&SSr8'&IwJT*IJTJRT(IURI]WH s)5r258nt:It:NgPGgG5b1Y,`]B+rL.WHerg4\) \ \+aЩŊt`0 X,E}MPZZr?|~= 4t)\&d 28mX4 TQ^|%G=Ih΂E֖V4F4 t 4(u4EYYrB7B)OKKS* RT* RIT*:N4:Aԇ4N4M4uNCit֛R(\QrAdҙ̒}+B(DP(>)r]7RHzvqcp B" EN%JaQ@ *WZZ\+ gխgT+kec <ҋJR*TJT*%B&#Bk$R)!%4(7 .%|TJHn c0 cX,X!bb,:b(u#UŇte L a3{L=;qjfܢy{[ɂk) )KmgW_~L4RE >XQt'+뚶7ifF#Q]:b0\K+_;;{̩cEi4Z)WE&iT )ART* "SAHe~ eRBb21 j 2bY,LX8QgАM IҺM.m3wBi(5R*,.ulllN):v& 7oߕ1f`S棄.D KR1ܽ}}=ZZ]|Z켼-y$IgYiv+k&̟?ǣw}|]D_ѥ̳@ ۦ_$0 c#JNɬݣGd>$IRs_,d;zHԉ#QǍwo;wd0rzzX|H(ҥaO0\Xb|1`Tzz߸ƭ;K1GsD==~TJ`t~}+gK F.K2M#W]i*"(uK i62ϣW@Й6ٰ @GNo.M .?q\t\>kki|*;ww~7Psx2®198A(!2KIGZ06T*m>L5F#*05Ï̒=|r^;;.9w<EQ$qcGmؾݩ3 ֦ @JWw8uLּlS=xO6Ÿf(RSyO.Y.YajjH Cu!IR&drT*RT!Ie2L&dRL^_-dR\.ɤr e20L&b2L:f1YL&d1'Oee| DڠIS]5@i4RԞ4k;i4?'LP*bΚ3@A@ Ch× ^^ y1]񿿕 %E@$3TJR! ( P/ (AP@EAx"!0RV5eVF?$(b,RR$RH"2T*UPg8E{HT^Z#FS`HPu͍[޾׽* 1/{i<工+k`?oAVV\\'qO7jd1lUG'W u]=RΜ{O>۹rL&S(29@$ W(d2T&rmzm22SL&Ng0 &IgЙ ɤ3L`ҙ `2 Ag0? :`jnf2 ]"AnRߡGOn}l?Ç ֵ?);;ɕ=emmmYq4UU]Otjm:g2 ~ p^>F5q\xXҡC}z/]4ð!A06KvKMZ j.G//mް꧟ Y߾ymkow=רYBL?rLe 2RNrZV&e .47r\!4K-4whb1t&Ig0*d0t,L&]2 :tPe1Z+*xxoo`vvvgw{x T_~Gi65mE׋PRi@j[D{O-< ?zW_a~mg +|3/詙鏞$!NC"k, DlZ2gR)$1yRT=yDRuj>wmrc{3ƍه :4@,񙯵Sࠑ, ӎ|HJJB{G'?[Pj4o@PhLH @cR4ǚ[A(Q_/75$IT$W\&U*THC*͸X%Hq-ND/\̵D(R!IZ6c/r*R ȪY'/_޶c=;tCCiJ14Pf¤io޽M?Qff@A@ Օi3B޿}) i< vЮc]pƭ U3Z,,,h(daf cdehɥɤ 3= csҜk;($@RQSU**xc4JEM}T@E4& U@EJT*@ *@I Ճ @@# @B U%4jMj"((TOҢI-FM}';!a@U9܀Rr" 19Uz = ogTJ*IJ~$`al\"X, A]5H|FRMqywՉUiѨOR9ISuXJ@ jəpR3y M_vcK PTvvbQo^T*:RgɣG~>~L-Qj";+km-q3UT*j.fF8B&H \,ٜ66 ٽOǎ{@TP(qr׭/zq&mSdhHpẂF)F[hWgꉄDsr`f8Fмeo޲!~3R9wMpԍ~a#Q/_JeM57;~zC|bRᣯ^U[Zг6 $ ty;w2XnN}-֯YqcGiO9lA4Wʼ& IDATRڵY_walN&̹i)h޽0eLm9ݺ/z{wzK Is صmsNΗEVXXp:|uofݻ326|9N߹gNF'&\=NhN !NROIHHKƹ3@fd-)), .["vdW. SgvtmFJJi(gv :eΰkm4+kUB۴bN.u愚:-V Ⱥ{>5N(/_ڴu{ͩ~> 8($xAgg'B&*ȯRFP͔ +x<O 2.ήu\]]۴jŹFL߯NV5k)6@Pn\$!?k EtG4dx@Z֨icA-qd[0gkU1Snru.^47U|N cwګ+hrssmlmգأkR{GOэ:-:p$SgέXH˳/,?!ZǷHUBbXq^ Ŝ53ƦVB T*ޝ6f{Zᙞ>T{ܯ[wnXU ܻsW_[{' 06F׿#bY#v|xἆ ~5ff]p\PlJ\ޡCP$TQ:F :..ή;\۩7CqYsl]wN|K$]:ҥ/II)?zo2kfpĎ].Eӥ7J߳mҔ`\"&~ZOO&7/W__|eVH MWXXX\qsЀyy7nuy;dtqs>G_2|t~~A6̛UfMhwameF \fe4 A;x(tFqSՓk̔J\O))i(O=~n yWo*_H5Nuϟxaq+6\եGa2=bG3gOl4zM -,,Ěi(O]^z}ܾ]{7V*.DK-ZPuE9s16|g.D3Ӷ~̳ۼu{;wOHH:xPQ~>~ءt9nvyTn#WWWV?6*WnܺӨam[;b &=}6ia|!+D>`pӸS޿K?+ԯasLAϧSvAֆ[C)>Jvæ7j2Lh;o^*ڎ~`qe7/++{=-ٵm e:R`YX7TB윜> 9t%rp"dT&R*P(Աv虢MfBfcX5PZ}b¥+7jy*CSC A=x۔¥vnu\]S lݞ)_^:c'$n\Jϸrպ^=5k!壱:i(O' ׬^F5nQ3V W)FbΜ|?]q{ێ/gc=6gvH>JLzڦu+F7uؾw喃 I<^Bpuuv8qu1ۨ<:v2 `%0͚&?u%1񩛡ZBgLӯݹGޘϝ>^~72T*٘;؉h.4T1g`p c,Q'>}}0@(ܰ)bMx6j^ :x託gnJ(]K0 yynݾk*Dݻi1irz(_HI6svta*gfXYZJ$60~MEQo/σ Egc.V<<kSQ߻AcSN΅sl4sNݺn&\"Y~KIO7mޭ/_.>N|BR;M[$1AO;\Oefdqun#3n^go6>1I+h&w.2J\{̸IGL 46`"1S,-uMREײpޜF:Q$q,8A =}CgLfC۸?F72h.stmYZ wJ9!3~|J"1LXT^8;}c)a@|B-|6r/TREc}_vZڷs۱g_POpӲlyV.Yp-JŁ[\]Bܼg-?c.D"++>{9`{a`סy<1o\Xx, sQ)%c;A9zV soζ+YVgͱhkʽbx7\Nw=`kk4$РҰޭ́&RۈF..r/UDf9|D 5w@3KM-[yhd1vſ`sV/taf AKd?ɪz+xUݿLEyxVGA&BH=%bR{u'l3mD6ruq.f]_?^vnc.&6uƖ;]P*XLָ_G?qcvcccr( 4s={\uE֮ 22h`Ĩq.^[NGjoYrUbbTσS'WZn#uťN<.^usk\svJJȱ|Jrqq&L</^I L6y4$M[oݣÆ8t,?KnK]+2_u^Fplv=6xOo4~-[cE?xC^ qU(h ̛`Oy3ݳpV/[ _?MvytsuuJJ*qcF?Kk5o^y eaXZ5#ruu$P@ DMz{wl4R3nֺܼ{!I| "/K}}͛; ++6J3t8׬Q\aӖoew@Qڽ;5|ڝ?i-ق 'Q̀j@A@ BQSK+kB1bƈmAhtYӦ<{5kl`4~g #-UPtֵmdeZmT v b=DRSk;0ͫ|!%JTJkik6es8(Jj@A@ =t |V!s,vN{wܲAccRyo޼{KWPT$f**3#m܅L("("(Jg%߼ ~ *XIOKupv^EiLӦVM: 0,)9yH( K=jۨIw坎Ԫ(! c|l\V.S%%a-_?2RzVQnR>֫[DKGR\Ϫ;jmR4h, /ǜ=u8:U+Q(z{yn2%]L/|߹@ *iivDVVV}z=r1cyиQ'ԞX%z>< nn,D˹<&֫j;)h KAceߋ^:!\Ց;wRY)h>^}Vv*L/|߹@ ʢZ@  4@=P@ @A@  @ j4@=P@ ͛;EUQBr? |ƀ%@ U MF ;E޺ (!.'@ (h T{@ R큂@ H @ (h T{@ R큂@ H @ V eo޲!~3A7oߍ7aSa#Q/_JeM57;~zC|bRᣯ^U[Zг6 $ ty;w#T7oߵܽbHp\ A&޶@(4X x4yy6j:5e҄1ڍJ2;'۟Ryb1?| vfH{9pԀ'{B" >πþ=?ڶ)}0?`˗r\] ?uĜ9}&~Z{Y*ŋW۶l8v$R.Wl۹έ޺z5k:^EQEڎ~?)@߾~9`[D?7tF5-QÃuC%$>ӻr1KzIT*q.˧.Q#:Ki\Jh5+Q ֖oW("cgX߽}g͜  I!svOj@a`AF [jR8`$zܹ{C{wcWJ%Տr;;AB,83̆ ~r:?y*hH ^X2G\@Rx u 1O֪US/ 6l"OoR>֫[AJi\JD(k7ѫNfm3RL/bIݻCw#boo7u9X M`RdclPv\XO!q$bWfh IDAT>}6v@P$,@uQ/>"хK[`s]D,>ݣ{7SRR>&=M4_9{2*:ptuVS<#v6J$K&CG%8.dӌ84TgcyUhu2TlUx`ogaaq5$R6n\R@ B4 YQ'{`X}zܳ%əs1]t.^t{oa)?:P(x|> 57a}'N nm 1`[L7;٩Fvt1})ǣO}M-_kkkB]Z8oM~ثБc=3~=}ݸeJCq\cwAUKC\Tgc5SVu թETL6)7nPwd2#6}O~\E@ Ar=sw3:48g<K"ʪOGg0TkϾ#4NW.5 78;~l9-]WE3RnVUQZJ-uխWQRW]A\q I$!fwv~,k%?v|gwggf'&̠Sj4I_gold7:ixUɄO/T9f G:#O+>ʻ3kKo[, ,M{Kkw]ʉ|iKOvBMOwtRrzbK]-2;,nbVWx<'fɵci t[`yc33O=R[>~ф y}K_QQq>rFR\.fS'"xZɴzT6F3ߘ]`rH9jv%x9|#M5ZYPS2VoRET{T-o&ЍTui9<.hY O4txML>Cf:,)~?Y["YA‡a<ol_0<_QwbDԱhD53ITEZLDDd|͑BF{yL ) #c%IFk6]]`rT*e:mX#Ě= i%cjg?Ԕ.V&6~g4*hO@ɚݬҪX!zq}ϑx:]29s1`XbcAӲ]u/ᙓeNz\͌:؁TX!~~yɘ!)uӮ')eLVnT&")u%2y"yJVDDo{\Gb1/nǫvZWJ1 &2c%"J8d yahf}dn}XK2E$@.mW,ԕճ'vrIq5vH8p-i$5⨜Ewgz%)u%ohN13V"Ty8m4 @6ϮjOVk0*d׷o%@BD[Ɉ(Yk2a K=ohF~/2MO+xd 񾮙ǰIOM5>NLD%}-<ݸy+;3hzH"rShowgj`YiX."oӥDXGqdU5Xo9CD2&_|2Ԙ]P<{=K)f&zy)ﴷaOkƝ ?~>gu-!" G'J"rSH$=2 V"R KQ"!)KmD2)SNZT VN;mz)⅄Gq 1|~<"˨9S"c!GL+g6-[sWw/tKhTRxnժ>-x„TucTK -PGDf)~!˻H,ή !b/|~󲻧1mvxU2=O<ߨok7I}qv?SozÒYQ9yOiC2nMeNp<)rZHDzyd09]לqճ{ۈm}M6횷igI_ϵ5V=FQ#|8#p\MQEjG~qFdr  sJpy(h塠\ py(h塠'>4܏qvy/QrNٸ׿^0]` FHTTf/WKWbaW}kb̿ Ƃ&)1]drV$">>Ƃfݲŧ0H,i>>f^qϒd+gN&rsO'֒||>Gxjb) :VJoH%nuN 5%^)._fkG:#O+>ʻ3kKo-|Yat+'85.Dr@9참iZ]񜘥*'ז=y-F6rJOK#Sg, N_О4?jk˶ :")1vfWw2d}i͆j+@(Yյ/-37u'95G:=H**Y"*.ejr6ۘ]SjfsK۽p܅}1ၒQ$ f.Gm':*|֬ xqņ52 M) d"j'yF∈>ͪ/Ze׭3.3sQl?`Ƃe5-^3''g.:y¹Mð,ˊŒgDsǜ RG:.Mb씺E<<%y"z=M#Ԙ]cNt`s/58&_uj'mӰ$5⨜.ay(z%)u%ohN13"Ty8m4k @6ϺjOVRA/^*aU/ɮo%[J6ַYO<ѨcE柳%O75m^j|JzQFzN T:QRBY6b=$ SYQtfvpɧo!ܔ`Eۙe=Ov|H2jT1o{R_Z9?|L}V[u%줺;]dhׇ:"2HyY5DڟgtMXK^V!GL+t?ySj1MbRh_0ԛޱiVT*01Or}:J@7AYOs)rZHDzyd0i]לqճ{ۈbm5oKˌ ;RZ1X4܏qvy/QrNٸ׿^0]` FHTTX2N$5G:ld7:ixUɄO/T9fᳫ;3vRioi.K9)P2;,nbVWx<'fɵcil^ ҈hK6dï.5ksrvlB.^ko޲!IYĝLrt>Y_Z;m{b0]PK_і:5Gj~_}iy .gDu'95G:=H**Y"*.ejr7<]Z=0MN{+kj.rRTm)[J6DfMlDBHXz牴zzWJ @l,h2RG"v݌blO}qJUnrezqc%S0a#so+7R@iGHY~,&|K4=)DěJll ,5] *RC#,CL)YxC[R%Ť4fKnV}iReDCJ R|˞aX":oDTiџ~0o>g"2 ÊD\Έ4^RW(Z '"d5ODOu$e|6 fX%q?H1 &/b+9DTY.n*5݄04|2KL%_&!l?KDbmN|MQ ÿi9qvDDD)#nz=icH8pՄi$5⨜'p=%|iP$;d 5sE i6$6Ϯ1F 98Sǯo%#"ќdOYD7YDjRs p%EO7M'%BA`Elj񏉨Ouv%i`6gȋ0D.NeEi/Swgj`YiX."oӥDX$|ID}DJf]}iz"1aⓉFy=K)f&zy)ﴷaOkƝ ?~>gu-0 G RBD,_dvXR4|:1 IYj'O6R@UJuO/o"pdTzvFda22fMK攍k֨F| 4oӮy;A lGJV=!|rÛP^=:^.Wü.G.?i \ py(h塠\ py(h ?cG^KS6LaX(R4+8;R>(6?#B P<4PCA. <4PCA. o&ؾ⌞w *]D,=ęmmpƬhl ;Dݏ󾺵eHRjk@"_-IW_ͷu^I2 'ί1^(( ӭY?H,7)hbnE/1UaY6bЁCzړ9ͻq< "XKaë,3Z)ai MR;/atFd` 62w4dBMɧsKz~W&Fq+L-veѭ㬖֘ʙ/&iunksb>,\[:~]NٹW.uwԯ/ +b-aɆmUٲ1#-՞,JN&r9zx:/00C-}E[%}E$f>Έ$N&rjtz:UTDT\<|oy>zZ2ԽxO,g}XۥxW Y"ј꒵-eZFULhБaQ KQΈ4^RW(Z '"d5ODOu$Yg|d=̟ϗk`Uge C"fCDRM;, C3K'sdZ5^nƂF,QoU\zbHAD,CၻRtS4GzRaIj^Q976&CODayN0:#Ҡ^IvBMk>֋RlIlucB*q܋laę:~}+o%x%'uVSKLO+.xd nz=) pqS*߹(9)JxyQqϒ%Jzݻsy"s:lOX CDbTVf70=O \?\D2K9)0H"j#8P2+Lc/[ L,5:uJ13CMݙ%C&WlXӚqr焟ϙw]KDF)˻iBX*z >e^9RRy@b֏ŋJŤѾ8aὟ7caӬUab(:p؉yv5*Q"g-lbo!UvV*}0y~צu֨59ǯtA3K}t=k'ʸ17 :}cNٸ#|/8#%s5kTm#Rttw`>_ie[7Rzy P{,%|/8#7 p{t8Ada H4…N . <4PCA. <6@!h'/ൺq34܏qvy/QrNٸ׿^0]`ɍۅK4֫esrrݹ~Ͽ:wA:w׶͇V-`YQQ;uؿg~~} =}Q0 yFJRKM;~Q[ji}.0{*.2;,YYY^!=8ώʕ*}4W`ըscvVFuC?;棏Zq#v=q'P*ۧwhľfkNPwe2H$jݪeܣ>PΈ$uxWK$N;ܻODϞ=iy>2'o^a󾧬tV[#s^s/RƗ܍iSÒj4j0T"iԨAzFAȏF ֪EsT0 >_T*=rDH𥴴/zJؘh*UhxZſҽ{/'UkŶo&\.^\V eeN>ԥgא޳̲8j^-SyB",^X;tԴ߶lرmSb+W盤x|o˧-ȗZy+[jܶca!*Z5wƍ6n Ƃ&)9[]vV[2f=ꑫM "ZcxP(,-g>gT"&>PΈ4v#z¤Ͽ6j@,[/˲J <3ye(𚳋$ i=eɓ'I<0CC&OMMM#eK?|`劥OƏ%sâDžU滶o{̚=@[ `]r7fy3y֮S*A]{qDԵ}J=çVX||CCz|<3"I@9#g~W5U)r 7$6ϫذ *? 0-,0 c릒%=i:,ߍY2a|}}F>S// H$'ZV.9.+3ٱmӺl^fάDy"bYsPV{kT&decA#ڷk|*Uvvwj±USxqCqcJep~ݲyC{2.0M%ӦXg'EN:=-5mOLOoQci4n8j0KSywӄtqqƧ6oܩiLAg%'M*ug_V55fE*.Gw5wjjWӧ kgiW5xi^ᬈvf:òlƌnz>XAJJ=gbc zpNWxYzc3" 1oXbN7hr\lF&M$|'f_ Rٞj~ףtŸ g>Έt9M(\q6;ЀHy @N . <4PCA. <4Xg瑗WIϳ)K02EZgWJJ ]Vp8K02o EN9CA. <4PCA. <4Pt =#cϜ;/B{1lٰnZMN&W>Έv#zW"v͚&-Sݘ wsSbMܶMkntrr5jw<ݜk $蛷6nz ֨>~ҥOtsrrݙ1Ku9oX|BBΞ9],|6[-;X!| uSPٳfŢ7ZٳF /aiFarSNWF͞];m޷5oDl߮Ͷ~ۺgϒ㸣O 6M(UʷOЈ}FkNPwe2H$jݪeܣCgDр~}:P%Ib'gϞyy{4Έ˵kQV3A]zv ={,KmS!l,hju[*a#{7c7yîFEְ/C""JMDr=+;R}v O=Âv3"Mݼu{4zeڃ{wq鿱s,TL9LNIJNy>:sׯ],W;w=j0$7jiVVBܳ{SسC*Lv3"݈0M@D,˖*˲liR<}Fgar8]_7fd%J>xK7oMNR}rss{?>ޡ!=]a[wE:#NWZhU)rfĶ) 64>>$!VdW/^ ,5jTT ,EΊh'ۮh._q3,˶hls;uDRwv2KII{ll\CTڰ ūճ#|8#%6t):;0dO$Б:ax1gΝdz6lnZMN&WTFfϫ"i.Įը7߹wsY36msFP+V\ 8>esrrݙ1Ku9oߝ=sX,a3fmZZxw>P?O9]5{wBzܶyjԨNDOڵia6Tͷ*э~9nc qG>lBQoޡۮ;Aޕd"uqN@ng*ʛBl/hV]7jV-KRaX%ol׶L&D-7{8qRZZ_}=sTRilLHLh4*V4<_޽֗5ztb[7*BdۼrM(l,hju[*a#{7c7ǯ߸s G$IDjV}kT7mWD$ Oݳ--dwľVm;PaAɄTF {U4WacA|t _J-a~ܭ)a:*k{(=#WBADj4++KPXZH]O?|`D2/ M&| 7P3 =n%J(}uCcN޶yD"r,G$IDj/uԵQPמDW??xX)ː}|CCz\vöo&UȶyPXH$'ZV.x{{u} p`ώmm^f5sf ˲:\KJIҶ:u0ΜKOO'̝#խSo8U(o d۵]l*;;;ZlAD/ADY{]*i)^R1J2{hCnټOhCppܦCpɌiSw\k,tW.i.yv5*Q"gEmh4K8e[4o6fpBO+?R EF G|8Debϱظ~ܻ+QP g/^m֨"dd*ؼ F| o'&~>p#9zttAl.?i#4fI02o ()塠\ py(h塠\ݹ<*y׿^0]` FHTTYr<t QBbFTiJ={;OIDAT%UkŶ1,Bd:刨l2͛5=~c':rtwľ%?,;Z_ =YX|hvV3OU*FJ}Ftvvv~t ٔiO=34imٰcۦ'+V7I2_ecAVoV٫wIMM5>L,U*52SwwlK#j1w?r >Y(N9!Pi7jظQ;ذykBؼd^Y |8'z>c&N;EDV:Qnnnn E$v >jܶcw@gcA|t _J-a%g^!ժ8*K7jiVVBܳ{SسC*LvaFS._z~\Zhk=mw+))mk%P-emlþXv^=̾?*T߯&'Ox`ȇl'MFD˖,<~K=K E]3DJ{z]tи~fW?R ֻxvm!cXt110"Og6m;T,E,ن}j/uԵQPמDTW*'<.GD0̎ F9p|4/}64>>$!V˞^q󅋗BM!о͒e;uXlɓm;vu `63Ӊ(#3szu;X"| 73;,IbL"*g~^h~Fj6Wffhi6{vl۴n5׭3km^ry ,ڸykw6we+yܳp`cA#ڷk|*Uvvwj-׮\e+\6De'>nR oз[6bh}Cưw6$HfLb;'pBgiʙbH*.Gw5wjjWӧڜÆ[w*aژw 0fhe^^%?ŋ7^4!l= w@i?Ǝ2O4ѫ?lױKΞ;o\Z1;^p vDDwcnTY]giW5x\ᬈvf:òlƌTz̽#F;VC)))ʎ7p;=L:rfʌ]"dd WCcNorc$ݍ7lwww^/6?t˕+̰'3@ZZO93Jzv ~0 ܭzJe;NDr^:F|Wݘը^mws_\s.lٸat76`r}?Z{}mֳgHnCCDcaz#>7,&~>p+1U*Vt=:K .Dw3"!8;(%o;wbْ k5 ǏV-Weʔ&76G}5##+5ǥV 1mʰc~cZ7zĂQ\lqDcس9K,țk/A?tbagF  ` J>[MV]GDwc 2䟧4th"z>_>_D4)[f>c> ߈;wc 'z?pԹkQn޺mLNI/8,ğN :صkޡ}>svُ?w{a=6o{ϫ&%':yj>re2U*E߇һߢ%K =}l؈1d%Z߄rʲ,2LŊTqkp{-|Ț5wg+w6d%Ky i_"ʺ>Jŋ5ztbXNѽ+0M7z8UϜ2}>xa/]2TZX1?񡛛BPy')49)~~ O~s^!LDԵgH~^%K~gfcKNNi-77;[~e 5w-z~&Ll6af~zMQhޫ|\m@QijD ӲeJ= :g8Q;Nբ[5k,__X7YjZ}|| bN#"__f.%ŋH$""×D"Rrr2yy{".E@RR|<{q*bv1BȀTQ+W-Q~GNGwzL){AOHIJN=wFK(S 3Ҥ K;FGG2Ҝ::lپCwC=q=VM{~&KDq!zaJڞ8/x=7uuL]]$$ Ǽ\j8:" ԑd;w%M?4<|4xBȴtSE$7~ssn'” }n9&vsSZ*tq̛0#N ;BJf0Tσj2J{`n63BCJOW8:.$H$%/,>{\5% !eo/_@r==VuѮ=s5p8ohhN~[@sGbv턋4SG.W-p8|u@*?٩Vz_KK ϷahrܧGnGNGtw f=acSsYEULW\8'0$< 8,3Rԅw,w=5J{`2>{ DGy%K$fM}t/, "(ۍMr<"r_`H>})_q]8ndK.{54"=3{pPfzL9u̙ۑc]9}pĽ@z1';M]9ر޾>Je`]zL > X `= 4zwqkOIENDB`gammaray-2.11.3/docs/manual/images/gammaray-qsgmaterial.png000066400000000000000000006275121413310443000236670ustar00rootroot00000000000000PNG  IHDR  pHYs+ IDATxe`\י0siGҰeɌqjҖnmJil7lf,,3kF#i@t1ǖ>/Ν3=39=q/zAA|pc0+ah0lCU4EdcY6(8(rA]I+ɲD"ͰX8AFB( YX aBP,0A]~eWp$ժUJEW:ezz(`Aݭл,aYwGvr4  h]@A}r  h  h  h},@ ҁ,(!v ~ͭnc( | Jouy$In"aMϿMuP(q~ѳvu0 34<[UQѨ~jST8 5=cMW/F0;k3 rE؂o0JŲ4~= r AAtiaqIAh?n_>ar_kU|>cS/<19u$IήP(x#XkWA_,ihh_Xia P7'O*At@(]E=|>_,mlj r-.v  ;p>FFzQacEĚX,v AP=Aכb ™i> ilΊD]R"=}_Hz$[;23V,]Y݋)ɕegϝop8]+_gNQۼi@x㫪N՟WX_3Pc^2c^JIN͎E"X#EQ=}1L/0,uF/vvE"@ XUU19955=m4J B!IM-\. /W5=~^HESS3Afg 0LO $$ȗx! A~ Ä#Ն8@ebbBӵw\,-.hn=;8117?x {u,۬?x-׮1u?#=59ْKLm6RSRSz{)T91Ql$ʤR6%I"@4m9۶Jզb!(:95- FX,^.j5۽hS-8~i`O"$[D"۽(JYa2tI3VUUYa2*+Ѩׯ]l1sx#i`(3k$||>?>v~zuIIf)))l2$942fX2;_4m6-y^8%=O,knijEVU$hj5CjHX\Tzf6g J! r)BF+O$D"B0a<a|`rj:B]0 m2{J}W~ŗ_EQ2EڗOŗ-UՕ!i Y@;=>ҝiz;A0C}5d pon` f1s\{1 œ쌌s{"h(>q\ۓ-h4<RiqE( `0"M`(804xy <O$8NyiicSsyYMYmA;fAts`}0-fsɖX,tY{zZ*0s9Lf%RImmMMU%ǥ(h4 pf޷Q(*O?^C$aFQA߷mnn~߁]=kjo/W_ln9KJ.]x|_zc8 PH"$$JϨU*hA10TWwϿ(/Ypj5#Y\ah[*[UUYrݎH$r:P`lK"f2RɦRQ%IAϏ H8,.*կ'H gdxA~454  L̜k"Nku*Es.\ph4JFR :]ߠQ<8׭i=6=3 NMIql[;Nhɥv!A%_qi#aII)h &#i))FJ@"_2.+;_@$ /AKOO_X 4LOψBErs.\jnip&j5F!+3\{Ǚ6Xp|p8\r\a2%bqVҚ0K$^ӻAt¾\A@}2>tIh/5% rs,f3"X$P(aJ ,VD"!fD`0(*VAQ A"0 KIINX b$JJ>c6~B\P(aMF\.W)G.ege:ZQ}~?"YZ&y}>*Zp\ & A 4 sr EyhlTQ$bq8#4(RD"B qW*@" 0 ??d2""J%r&1޲JH*byM‘}n^"pt%Ֆ]( 1? JN'^$].J\@ x^ՆaH(>Mh|pv-/ø\JGAw lZ@E t#1H,]AA}T  h  h  h  h  hn0 EQ  .?ݭt>B0L(vDKH$rs݁`p鿡Phqq$+v_X7qvFAw${qngYtPе4 vܼ?׾qAЇBWci(٬ҒښjYabgnGw_~UV=ا Ü:0<2c( T#yk׬[qAWi>7wh80vU"!a゠d?~4mniݽwa~ d\.Yhk5H$28Cn0 M( 0>1?>j ;;>b>jo#ف{OLN>2>11>1yБ@0x7zgtt|~~j Bssr] LOϼO23LMO!b@`xxdtl=KOLL>Xp8H:]L6{L?XÁOѱ={G?p:8w߁陱wv:rtxddfffCC+]vvuoټѠ8.H$ .8$c1uvfflۛoC؞VیJS5kNGNgK7 4MbwlOJJ?# ̐$ӫQw')/+?H@ j5Ho{R7 ?vգPcs˙Urh}ϚP(͹b#A ?)*%YK%`a"bXAlݴ EPˉgN+ (V ) }Rҩ3`0b0, w>&#ahf @4vTp:>r|~ue\C4d_@ fMF㭿ƂK_\ZjM7~w?zHLcfqnjզHH`,^4{/G/.nƍeŷ9LDѹ]RJA7^lN*\А.Iwg wv |V:B=}}_gW:G%ر]Tt ۶&[APabHzomٴѠׯl`ݡ>TWVJ S+n˷p89AMwx  Zf0  Zf0  ZfTt4BpACQ"WG$)"wvZAH.'c r|;@ %X,K.-kHAAw<;, e, e, e, e ;&&&mtMXll|rIEMLL$ysDasOq4M/8nv2if H${o܍i6{".tR5cf(ck\L{z>p8|-@ˈak!$'&T'ʍދ$j 4=D~Fv/D"=s(R*RÑȟ_Y]Ynh3 ٻ_,=C}JoN7×쬿6tuw/8jf6u/ajq8TQKӴ6{ PAbQZJL&Ca JF-3!~yTJNG?X1 wAlNdxGEQظd Irl|bhxE/x۰n՟ɩϏ^z}Y>h,nM]=bbrnUP(ù?Q%J23㍇BY{4xɖD&~833֩@0 X$NBvU5ŊG;4<2<2R^VzOmvVR- Ib6]OW?qðF׾w\r_ ,6K)r~E EjE 0 GGG,F8VRS=^ٶvAo EoT0\p8*|;#=r7@wzg_lku[c;r8lAF[Z۪++ qu˦eijI xweRIRbb$kkXUUYS]y+mb{sϥiwon\$ɾg=ģJاAQHܽg;fWx=zŊ2 ;_\nM~nέ@DuME4klj9Q^Vr#C)O/ rn:\ ?8]O,8>08#s 4ÈŢD&4vur96E3˥ |cjz!6 IDATl2oB?;~]Lkyl6Cd=p0/wyB­N$l6B vvuW(@$b1I`()1Q |v(($D,f[6m0B0=c}P:bfbrGQ5MkwC ®{^߫;_Yp/.=vbrjZT_Zф3M-=<>?';sf#MNM746g8ATWݾ㹛]]W]U xֳ'XHb<5%yqѳ{Գm d@ \&s8bhq7zG[&}l6*3#E'>ovV۶T xú%E>7nji$IcXaA~eEHlŋ]=ZMA~`l|LT"MI<|$3#LSKŲam^/%ٲyz  쬺UA<l?>¡#'hKڼq}fFήn6]ZZ\O:=qA"g 04m٪**@Džjj㺵|>#E"p)/(ZRTXXrssv/.MƺU5:|hП=AdfFZIqѹQ_ꗯ7vܡR*^zwwO~nN,kljfJK 0 B'O7 ʤ WUW=pBQ319~횃fe/*/-):\h4ZWUYLCc6hueyNN6 ''o>d<ɩO| x}{]o6#=C#I%Mӣc'N쬌rPx#+LLLmk]UUYQ"Xӿs-gϮpK&<<ć4Uba A>linm'XSSHXOMO>z񦦦5;~*&jy񱉉Ʀm07mXf{q:z?|kE7ÑҒKi)޼q@Q/u5X,X,f;rĢǓnnZNQij>:]b]mC2g `ͭ:]P <~~"(޻jStl6+DQ6 l6{hx p!?/ up赫k/stll|bSAYiq~^n <~.Q{D,XLs^醑1Hiǘ{ ` 6fM`Xڎ HB|jz3M-yy9ioN{Džp8rQ?P/{P4[\\Ԩ՗o|gמ Ppɩ@0U3=7508T[S-JGP)vsujx\oxoF**&w>R~a!ImOJMI...:yll۹g2ӊ x띖ֶh,ӻp:ZbXod0<|+J33>*ɪ*.\99uH$qpwvjԇ(jrs.vu ;qT}UeynvVK빎Z.[UYQS0̢gD"S4ͦ'O? Ghm;W^VzgWWwO$i;r4MOM\D#ͭm&±Reŧ"N]ښCM.LcspηeӆO$F'&e2YS`Xɖdqtlƻe IQ3\z]^nٶv Jήh,&<I]=2L*(cH(r8׬mhl"I2F3V;pɘq\^ |꺊,VUU较hx]^'NrYjϾ/݋fff(jx =;H$_ Օ6}}Eҟ_ͩ68,κU5*0PXz\F>t6KP( "<|$'s8lA8H$P tI==4M~#BW|ARRFt*8TQVj6w*hh8;rsr'&&/U!\&]USp:w޷b}5Nd_^* k*Vlo_,k︐QUQq$Y}Zx[._1f?cKۣ=-f.)񕝯OLN%[HtmZυÑ'[/tv]>0bh:J)I`o?V8n<3ŶlIlimMՕֲҋFb5Ж͛ ٵ{/NkjE"a4{㭷7_}ғDiz|r^|r}?V_(ϋbG~ߖ"?=3ݣQÑ[JiWTe.c5U)V-^nuz].1bgw R.քBy֭+*#c33\.f0o[(;ۺnU,;z-JK՝rvuҒ`0 ?e"ۺl24[JE46W ׮Kb={46LF7Zb19lv_X,Cz]Rx ADVv/.k?/GxFQ*.(m&H߶mhrTxx_ISk3F^"uvu'&&.8fatl,޲JT(|~WwNNXlSz?2CNQF^NPX/qb6Y̦Yp8vA~&^'|Qwlvu NׇawO0)Q+4,tp)`0hR$24DX,;^3݋^/55`|:I|~ r8F>776>a2bQYhB|\.WQ;ȱ߷->*?=cѨաpduE4M/]s JKqx<; 28f?kz}EP EtV;<?=⧂a-SAs웚|K4+ ;o]ww\HdD" V 0 6߼q /?_l⿛(aT*IMIX.J a1Pb(}_P$ R$8.CL&X6^p8G '/ʔeym[6x\hFPT,A3331 p (^xlVBlۖO=T"i:#/]^VRYQ(^7 qKt(a 岧xm[<^݌u_zú5|~#"Kh/mv{z ?H%[w;"-(}.1Q0 EE5; q]Rbs}}y;Ғ[a)ɖ5k['%:ןp";,))zWۓK>WwV~! ~>!!0җ4xߧ 0 =887$O.{z}yEBA_n@HWwwo$EpȌf4Ng wZ޾ZVnG@Ԛ* h OHbXp9b ’{SOH$b .bx"!A ?؄aVq8XlniƢ?E;|ǫ*>TB;f}?񓇏x{  AR9?himF ƦNpAãX|~DQѸ|:#h8*)k;A?cϾy9v8luk>|>}.%/*Jrw;>>>~,f*:b0+4=q">fզ?UqX?pc,kpʹ%(fgeYmG ̑ѱq>rMFC7?pT}$l߶EQѨV9e JK*e0:tXQauD"QQ5KAZ=66nS_@N78ð̌ ]h4ן=FX"d45Um4=:6Ԗmp8LNL%׮0̙OE"Vc6{z9\n(UjJgoF361~PIX,2c߷mi#crtphA"`(^w$Im\6zƬ3gss8NUաP۽r8$M  ǫ ';>11)IbR%;?6"VFFƦfHd0:f290 # -r=p9$IcҒWw/~Gr{kADBQIQ2';yA"y^L*zolem߶_ٵwZt=cc%EZpm(fgD|wio6r0 [0,55؉S%b(`! \p8)0LRCGl޴ܹp$B8f˹Ʃ ^Y=766؏"^}_5bX,AQ4~Z6J6kiPT&Zfgh4*=k'&N"!moq{׷y5U zLf1(q\.GrP-Ͻ`U*dD"VM,zJ Hvv&a>?==+"HVF:7unpdkmM첹`0X@dž bD)X,V-fX$fH%%dRRqm6[0%%MLN޷u"!AmD֮Y-I33JAܜxET"IH;II b_Xpm[6dRa‘guoO|Fv/fg}>ZLJJ8TJeA^.t8(&%% BiTF.0fgeX,aRib1DBC r &QR*&gANv^V`X aTJhP* @ ##{o|>zm[8Y&deeY,-Ѩ)7q(]RH$JJL0tzz P D"5UZ: lvT+f H$ 1 +xo R!mɂ9>>pɎ|>RK4QBXZ>7xSSfcfz:%X,Fըq2TQd#( HFZL&vJLIIN%bq(JKM ƄBRi& \o؏atI8[ggzzSUE;3TU8p>~aa~uIqQBAd@ IrYIq!EHJ$VM*ddd DFR̐Uը].fjZ04#]0 HA~Pql~a6:]fFYhT,dP8l6Qi$ ! &Ѡed ApH%Y\eEL*h.-)$ԹˤdnOLLwOLlݲIb D5FEQX]n70y8qܬ > sJ͑őhrd+y0J(x EQ]RP(@$=-e)ɖlr,-5%) +ݑm^\q\HsKk+ \܋H\_ͅ$ A M{/K‚+9 46|Խj? ޜH$wѱlbO?2.d GӴ}n^*rvϾ_ꗖj>"Hcsk}ÙkWX+APùfА.IwS4c9vtCwX,KMuj.;Zg?tK#(jjnMCU7be%ZZ(`_A7nGB˽FRN]ýig X2\>B‚L*|0  =mIfDhhNaSq>AHR{R@t{&.SE0"Pdrz;8,O> ?J2 :ݲ4YY; 2TǤ!cG\! L  L  L  L *v1 IDAT L  L  GH|>̌un~$g=o0ht b& y}t\8M׽η4M\nnSl݄(yva~ZSntQ^~dKnN6߿Rz}\j\ǎﻅ~Xlbr*!A.H LLL/֬tht ó9MӴ@ 0u\.FIdZF.(r8 p8 i5jX<:6v/2 i:~Vըo0 ߏ^izᘜB߻/b޻>k>}yۥX,v -/oѢ33c + "!~j75=Z0 D?{Gu Mޛ]i-M Kr_rw)/\$PM%YVwiշ>b62ߏgg޼7͛W\.7Abu EQǍ&lqbcTBH`DVM"1eFP595m֭y%AQf\W>Ѓo[xڮɿ}h6kvWT?2ecYT|K>@RQ}~~ݞm0)[;:I @^w}l6$I0 ۖ+JBpoo_{gH@oߖ' 'v{Kk}nɽI M-u*8746y҆"KzݗH  { ydv\fiO\m]úukwlg1YϽbTbӆ-m|̃'y\0>>q"5%y#242cdr#GF'Y]=cL&33#=)1p6nXN! J«ErTݽ+̩W[ߐfjtڡьԔ'RSs2IR(d2iՍw232Vbb;Ξ{cGpZqrttCO-.ͮ6m  "ь6:6 ttuˤ҉) d'u榦Vǣś-UO*a_JLr_/YSqyW~ϓr8·ϜNJLdY>? XLZqLؑCB`qiFM'&gg`YZ^Ve\N8™i6Ǜ[^6ضS'^_M]ݺk Һl2kDGG0|\ThٿoF(n4"цuks mAPWw RTRd1I|6np8E%Ϝ>915 Ad*صS 444/.ܜ,m\ldw11ή!9oAL:{@JJrRfQuNLL ݆ vnr`phhxxd$ u~I|S, "*bvuE~BE1EcbT0WVQkjνy'pLf0̖WT+E$I[ڋ+( ‹ \+'6_RZ^\RrBAm}C}cdrʾIPH&NLM]T ::_;P0=/=  wF>ahrRbvVfcS #"h~aall"ƅB'OT(0 (*}jTBdYE$aS׊J<«dB$cbTv+m}~EՍe˯76I_VQ@/+#Irrjb8h87.\ b JtJJ 2thd: νk2 E%.gaaWΎOD)6¥+6# ]tebjJ& Yٲi@[GGAXEXqzݱl0T&wM&&! F0*Zt葜,T"#bۦgJ$(j4;wTݨ!b`pB)*o( l6\xrLdRKl&kWVQm2)Z\? J+l6ݎeR|yed2m 5``PR*%bq`k{gdsyE F^9{07'0 +jjiI%.jaq1~A BĄFy+d0k&aĨ$b0dҊP7.\.Z\P$ b}W,d2 wzކ fFzj{GǾ= xQݍ olA^{W>ǫol u:mA>xE3O x6}lb#9O` e= t:]C߻t56p8[6ob0|> * \&{ tr8ѭE)(ۻ]Ϻp/-صcp8lX yJ]C+Hܞw;W8^rtw=v {N;zG)̂];GFT0ھmkzj Zuet{Pq\u krwogX,_z4Ik[O޷`%(AXyN?:6?0QG)䯜=wOIܾM7̑ #_Kwl۪MwO{<[~o?jV'yxzeH$$IX'qۛ9Ֆ 0WJ HcӨcIȤ۷EB C =|ðĔ/@0??.^W]Sshe?'&ξq~hɤw>F~db~t|7-VK^_yM߰nfg2{ g88D];ǨTAyx,+#}aq S : 59zzAA*E z].^k4il6+@,{~0,BjuLAT:4<D$^}'g{G>bV߬).-xtEQ zϧ(11< DnW*|> I eU .`a?7?Vk}cբb[v `zfRա4Tq6> `5p8V@ f Reܗc7/,>x@q C׭YєVT mڞ}/zH{*z-/"tqs ðAnjq 3SaIzlk4P(@QtmfafAJ+#8NF 0'I$IF6.Aet@po0 a, 0 APzjJFm2[o<1*G6m_6 0YL  VdP(E㗖NzY]}XZ6Reb5(|gPjfNvVubr'?9#8ȗ└$:fiq3l6w#o=L)2YJðjXO7$rSjqR:R1ˍ|e7jlvh-kEUu]}#A~6|QK%۟#FJQq{GXmPB`( l@Ww.A$Dց mD`2x< l6I39 a8I*< #+0rY CP)=d2WTV]Px(=-  )"X,-pAl6×ooJ)( e6H'$qNv~IFj2 v֓T$ReXh?T܅⛚ےYL&ASl6[*A$,j2[RS2ӯ|3,K,H>%:zm݂"H(jni[n['qB`iiY*7.O~ pjEGEݞ`0=VHB!"I❧tD"WO2"QT\llSK:F#0C0 $AFZՅB!zJ H"G&6'r&&&ᰟoSqb 'O|(ͦ($H9o./?v8O<տ&>V%Is7oljn 2$BDn5n/c(RY})JohqTbX)[۹\D, b(::@顫hstڲ bz{P>dt,p87i#A6<0w(E \\Z<ð}{wWUA0`` ǣB7oXK\;v\.IZV,U=}Fma(ef/Hq<, ꘒĄ>xO> y[{'A R4DcX;/]%w3A$$;Wqܭy׊6oA0II`79J}ZqGgwfFB.Y[T7wB`09wBDFrNr.|a r8nl6X,r9 ɉ9Y7jbJ9NP`0 _@}zE8y}+u0nw8l(JMLX-!zzX,V6o`ji1Rhq )@ee=F8[#11u? A7-|>_w\)B(bRS11L&`nwh4jetPٺe "x^\yh9l6[{zkkGF*2㓓SmC#skd߻EQ6#pܜ@ݴAyk8P(x7oڰ ]|<QQ{}~rbH6kTJavvxdlmo`|bRQĤx\ӹ%ws$LJbzzoeRfɤqi)+3YԔ&ao6[r7G%Ʀ]|ܑCy?.V3>19<2*HvlP{&'tZJH I&{|bA`m\,P$y~^9@t: 9t@,$ 8ett[{ڒ my(n8NrRb$ؔD56>AD\b-72 &R*2)t8ׯPo` ::*:**=-E!3rs7 ށ%uLB!xQ Z C^wK&>o4F233XLf~`X$LvE;ǷZBjjnx^l2ܱq>Gk199F<0= iǎ!ie>Eή/!Aoܬm>x߾, [v`Z0'L&g{ⱤĄUSmjzbqѱ1Z?<\W`)G{LjF}j#K ɩ0Nkr?.l6{-iEtdrM].7@$k1hMI+VhA591*%AlKVoUA&a;ׯ}_[ o1*FY*`x䤿w,h4v0 jԱP)ð\d2޽Ov;>Fh4* X4Fh.`h4F2Eh4Fh4* X4Fh.`h4F{*`Q )z_ASE;'\EECNڻ(*ZOlh4 I` G+q9A4O?>}9w$ɾѴߞ=BΟ/~(xd2A8].Ao8^\ZfQ&æ(h4|iq򵗞rw+p:]>dy[rwoGQ|@dRyǩq*ˤR2Ljӳϧ>x .d2%b1p,'HCQPmIx C(l("P(L& 0Z8ny\ndRE "5;].6~dy$Ee(s췟 wGp8<!v#lH(0(ǝN? Ia- !x<$1 l"ivnHVٯ2:O;zG8/Mn JQb:rJoGQǖ8o~o[ɿ}h6|#G&UP?0nmλOJHxS$I^\x~qvWǏ~^X\W&'穟;8]=޾M hmhimZmbTV1| ̳8A"}'dR;WxkZ<>`b$l޴cV1<2ZY}b8ݷ wF'&[g~? LoOAVfFYyeټq=`ݻwh$T&҄^شa ӟ}?3 IDAToۺ%?oF^l6c6+] uo`0 QLLCTRV)@%tܧRF>&pAR='/.-8bSSOԇ P(21}Kv8 AѷhcOH$g]xq; x¥~{ \,tE[q, m$EcT l+ ,,.٬D>&FdR4ǕK>?+3cxdt`qHDe33 ]||b>ONMC!p8|~VfV| ťy0 wz>\5٭A8Ng X,۶a(j2 ܾ-oyyydd, j`(P/0Yd\9j;84~Z@gWOBy$Ih2(<=-`Pl Bl'pEQvcxxvET#}K voݒaruE΃L hH(xE˕kno`@",..q8Dȯ{ܛˑ)r8Cn["$GSFG꩟CvY,, ]S&"0,x LF:a67?_Z^˯Ģں.dzBJ~Y&pXL> ѱٹyu?0,&ԴP r8&Fa=@/!Ad2{zUJ̌!>^y+&&)"kMNV&vbr$It9̌!)1l$);/X577?95xbbB6lOLyeeEL&$ɩԔd@m}Crb\H'C:&&=-ag''Hp8VV96>rYVfƽTTVhU:«EUaڹǏRƢO=᰽^`>R5.ǿ-g s7j,V̌ʵa@sK[QIj/^v_.M&YY}NfxjK[;`l|zh4pytl<VV8Svvt: y}ӆٹ}}+qKOKXu0``y{zҲ'B~ճ禦g\.E6cn~axd¥eblmX-m֭mni#I2 F RZ|l6_/himXm+E%]fnOYyUk{bo/8%%%npe2矞6_+)s\/z\RV~ܛ#c/:LgWjov=wu ,)-knm3 ~kUTWV[ֱ!FMiYjz;E%etlzy=\n4j"b` IbǶmBaY9YY @t/!H"A]=lE^ 0 ewzlLLMfg hji--|W:].xN{6tyvvbM GFNJJJ kE%#cA-,.ҊJ&3spxD۲)/..q8s olxA̖ť޾8'&y~RY}fM H%j`p$@0ٕes$3ec&RavnjQȨliln4?0L*VkiyeKk{&V0lhl޻ APT.m˵XKK b2 ot-<a>(j;FGܷ?J0,1A ή_~uttd6_-*oliX{νyr%M-f|h2-,.>J8)qfb쮪p8催k0[m7k\nw(Z\1c^Z>`2-7k#۬ӟqyvN J+,pYed+ͺHѱX&r%$eG_l\Z6ag %efjQIo Y[˯:΅ehl|Lω{-`$Ng٣cp4;+Ox<:;l&'S'ǎ"`<~2 d2=|͵Ub~ݚGzP&J>}X<9=T"Icc߽+phOj4|>KC;|~A'=pP(iݽk|r2R@P$%9j--g 0(0 S5c>r'dg$8>1qO0 l4wv޷oCOnRSw" o4u$cO|@&LMM|>_RR™S'2ӖܷnIRyɋiAvH1e#v [pC&):Jix_Onqʵ└3N=| ^D|o_XZ HTйa]v]-mvh4i16cbN?cL?}#FFǂ Ilߚ;5z<ˍ,/vȨH$R~gNLL^+&rhx<~S$u$I/,,>xCOnܰJQ{&u\v  Hpk_3Ͻř$Iӹl4] Zm/^Om6f#)Ru[1m\"'WkCOmڸz)дԔS'?zx0gCczdff:O(|ݻΜ> dlliyn+|e{w IW"ib%b *et&]@QIj]XXP!")$I͊ơ(@%< xE4v3ϏpdReٳ2PQhXYe r.^.^Z ѤzhA(A8@3 b5cGM p8aX|\ܾ C.<rQ: Mf|.^~ݚ7_|oEEE9wՠ( ܺ1vM ghڸ8Ț)H$zvG^nnJr"fwlܰ@ 2LNgήnݱ)ըgXpjѱa:PlH%bDhzZjiEU[6XPQHO qi2|&= A' Mtڸ_{rpy ??獴[/PUHI EԔ_{=56}f@aq<1Qh\" v}jz//`(4t]X"`h~arrnfܿ\;nNz4P(ёf/,.=ƅBa|i٘YM$'utD". uM6-ye@h4545ONdw"琢.Ju{KVYunwXwOϏQb*Bb2(F>6~I/ZfF`FQ` x[\P$Ao+^x/=p^čLLW6_Q1vtvͥފ|2f/^/?*$Ez}^C !ZiQ(M8w&6 @0arpd!! )qtO>[o{6QQy"mz_/=P^_xrE! a@B?@AZsaIL&30042XY  a 8fy>A:86 ǯGuKSΞ4 EQBѹHO BPͺQ}fEVdR76>t: 6pB|>Kk[Vff]CӶ- @ A|>_` `=|h[^. +F$e^&2쬦?AA]ݽ-9~ qa|`0[R5v]6vE&aEQra6=(as֭0LpݻQQi) R) yiaq)AըkjN>x8l; fg8qrjzqqizՐ<wϸvGEHEB,mzݕ陙5:.Rqh4E.yKp9[6ڹ޾ɼޒ@ @~4ڪ (911:Jq szFFǖK}im``>bKklT]_}S$x>_(zגQx*bBprzzvvN.cڹFamabyㆱePwPk}~i1KKF/7u(ђ) L&NNM{}\ZZ2VVߜ1̖UV&&Wu0,%%G.hjjhlmo*Zt(fmvv(&" l]T߼YQU=77Y|4))Qk2ZZ#eR?^-z镳c 3Y !{w:NG>klnrx IjMLٹe# :Z ٹںȐ7\.wm7CN+|MoFVv88|g{ԉ_xѮ>::=ottrwoLQIYXCR\ Hp++ VU MNN%'&@0D`X(/,vX,de,En1ϸZ|k_{g}^\&陭y[LA&Y $%&$'%̖˅W-}{d0n>Gb0,#=fmr[GG}cbvVf\$ݻ<q\.wdt>+;#ZRTzrYlfzưi.(uY -0U)q9^yX$Di0 WVض%jQ(>|^ {wTGⵙPhbjj@ HzǏL'Z22IܵsϏU{K+[;.#$IRRxIiysk劏XNf1 {q0wWJ+*[|>G:0ď MI6lr98*n{/~D$GƝ.j̩,,.ެWc ƦEBaFVW F۔Q"fK[FD9Y85%ybb5 %t@ba0+_TNNL^VF^r=9%I^HLKk%s vnս3IPV IDAT;ox{ -V+ Ñ 2fg犯546;].]dNNN`02lxٱ-O$Z5X(deeJJ*~߮\z`hXR*FT^]\.gxd9+3#ޕo C5}HwdZ!ܒy'g6#s?go0t t1La0̾֯ݿC 3K}' ʿ[W _ǧxG%'%= ׊ <Ľoe2?%fOuG4jG8(shhxdt|<ܒ&&(11Gxr9FdOy(PRQRl}2)i)>cgQ"W?lvb^y]>0F^0,:֓A"h4F[e]h4F[etFh4m,FhUFh4FV]h4F[etFh4m,FhUFh4FV*{P «EVO{8~]8uWl7I^euɽ nJ:]?Mio~x忓pb~ v{6]nVTӬ 8n'\Vigyr?9b!)j}CC?xj!B|Sa`Yy{{'0fseKr'!ݳ@Y[{N:fӦLfYvdVQyb%b]|ɤ7z"CÉ U+-[VyBmX16&涔}^lR^y?x H * tZJH$=mQ_c'=v2iZ*NիV\0и݁9"d24?0(a.$IZ)Hf2YcC|ڵ{_T6( 5uoU&$'>r A_*ظE}R)㑑VeN00?1@KkK?;o6qC(F'z" rlRtϾ1YYI =}}9{uuwu:XxxXx6::::q\d6GnOJL Ir`pH.-Aai@LtRue|Dñ}'{\vT*?^G/Lqi*[[jSU(^/ C;wQ_0mB>mʸr!| ">6F$,k;GF5G0{H2>/>.㸮,:VR )K%HURBk 㝻\Nׄ9_&FFZ HOK  Cx[}`P*zfh㗛[r' AOJH$=nG,Y, ZT^jh,\niDq9!fLF{DG ؘqcfihGE (ð8&:oRN0q"#oxzw[2NKqApAFlчhim{{W_Fl(rNrPy__b1 $|钼lw8f͘^"b0nð׭n0Z!u`LUUJrX,.?rr(+FZ-ʏtvuGFZ0 <_o9z)!> իp|ܮT*%=T-dšڋyHDbKkGay^.-_X$my}DR)A.;gҞC$A0+.f"q\OoMsgK G+N$%&(JR*a&#AU_EQd+#|--E"0zy}{ozZys?p:33 (ʫ;?zx{֯joΙELa~eGH hEV3))II [}:5FGG˗?~]˱mvK#I*NJ$@ XRT0uJissQy'?~!#=6EQ+*-pDxӷ58<-ʼ>STr @3̖ZlقZ-G/s{/#=m{>SuDNv_xbakUJ<(J7]ni5؉ʗS$;7z33O9{\NDJrY3TJ ȲSg.2'2Sg͘t6m~iܕr}~:E1ڵ=~"=5asDxsK[YaRIQj?J̞9T^$ZZZ"U*%eپSgD"^5`LUUjJ ʏu=(8rrpOoj0q?l߶$ͭ--#pO<~{o(y}qq1O>h\lL+}˗[O}IT;V.LT(k׬ZʓӦ>JPT+.yRSkkA7w߹G0JA6-'UL*Ο;GT< ͽPSv{sSX{N ? 0I-YO?`ޜ=p>d\lə?qqK-|r Ey@3 b~rc9Y0,3#g q{XtSO<9SE}}Ss.54b~{Ӟ3jjbc̛ҨUS'Ι9C!W|ɍ\{2ϔS'?zw+.,xj+-h.βFmѱ["    QT=/xׯ{jca{p_hł|DR\Xأ]3_|4 z0!f랶ؘhV ȤPHZZd20LQ\!#I008tH`xI>%)I.qTS{QH41@: PCG#cv <ϧ$Y-bb0 x<ab6DI9}ѭm9.^:W-ᑑӦJ$#LB9.EXÚD0 /8ԕ^ !2((Z['gʛx9f ^DvI$0i颅AX$N& BX4ABg/9)H,z{Q*&--%EpbFG1 ʎhrRb|\lLL'g/_X.dg`cՓ!oiiiE ǵ>`\;+EhJ]#0j)H$bLdL&mll(HJ咅 e2镋cw||\ eM4 dEfoj4FX '._ܿ(,^`߁5uu$I,?"i].7B.CQt{n^0JUT{zq8W}`5Www44]|eGr29w<4C8$uPe4)jljq\uM @PLꎏKNJ4 7s%TjUFzZO__BB|RRG-n6e%/54wtr{j#C^M]'S&N.)ohMIN 7MaŹ`3.6F0yx> 33` B1 384 HRbbEb珧,:N\xn+znɃhaL&hF= bXp#{[s0*R!P 3 R*T5wܴyѠ/*(lw8̞x|ǵ H.i4juZX$2/{?)--p=lRX{ǪJ9adaHTLl|P4d (';;CmWaV#bh颅 Mj ֭h' JbE3Mz}:Ntu)I A-HvdXz"lA, ߻gN$&:9w 'RS֬Z ?}P+Eh\p@ LF!p^ q26.5w tu<戈ׯysf#ǎW?  ÔdeDb;dBT?awŒkWը^/I,vWejPȅ+>hǎOW~uwcw04mʔVK$J FEXl0' z2'{-{HOMzbR\uۇ$I͟7g*r`pͷ "bZV,](-.R)ܵ}',dĄx\!HZȯ狴XyzcNv60S7~E2W׼Κ1=1!N{lݵg_VVJ贚/ðK2E" =?xg9Yv AB.g<$bܸ7>} ֯]S]w8nhdBM݊eK_7 ?x?X,^xa[[Z gڼefR\Z,L:ɥ>/6&tLFA^xgSJKbA_t ?ԩsoo:2bSkW*ST4 b`K,ɤ:xjJ- z}9>.vՊe_YW|ɢҒG9|40`:[mooD!B|}4]n_{gB n|Ą/G߿kukVDG}A]I[kllZ{z% Iixhkq\ MT3{Z!B~2k}skkNVVuPikڿxP(ϝ}DȈ-j]Vn%J u/R JgϜnZnV_XAiQԩSke)KZ|À!HT  1 s+_1xPǭ Hzje IDATna!B"w/\" "D!Bq X!B"Dϋr\ oS%D!Bgc%wXǫ_9Gmoea?zd?H.~-Mǵk-|CٴI-ESd ESݾptnL$J/@g  uؿs0mȸDnǩ=]Mx Dԛyf|- <ם97e&q^ֵ=]qd>RptBʽ<ϻcn=.%+VgXoy$pD*Ϟw_jכ".X 7}BY(H-ZS4E۳=45;W^HsK,½5?]sۺGB34]wр߫Rkg-H+F1R͙#c6ZS:{Qz^MS{{ڛ[-Zޭ:[ui 'g}oSjXWƆ|(stܜkQ\`Pc0N43ptÅ3.0kK2wmye`TkS,H8Ka[U'sW<1f{?UjecP}?mc\ʼɷ+P벏}55|Cqs|*5ZHu34#U iV1.G{fH<%RT&yăO7}^Ir,k734hD"Zb" Sgs8sx],X}BN{?)vJrH7_ٗj ^BF;e Ƞ1bka|n'I)5:XL) !J-W̛29f{?Zp/x{9JcY׃bLdY9f+UM5:[e B~) AP>/Lbb (2H"Qkt ДLy\2^x +8n&avbRD"}^y\0 ~0L$H)) he<ǹvHb?ûK5vGF̑Ao;1M#,.'#(TkDb EkKTX}1Lf<7:M(|"_bͩ f}Yן˾SQ};T#BP\=Ѧ?{pKTձ2 *c&n|6K?ͱ9.+0«o|0/g'gjFiX8rF`!ʂ\)A7^v1n;Nx%*v5:[n{ E\< p\0H  }JL H2T&HB3Pbh?g`=v\IJ,3$_hwf̯<첏m4ם]89x*3+ +BÅ, U895Kx˾},rBlRZz^H,ŹgVђAH,Lnp,{lwbz֬k9L4aZj&L`C՟?Qz˶oDb1 @>uh}ta ϴװ, Hvє좩7Sל:>2q\`\:vh=l|m}U,۾5ZI}<Z㭿Zb\1E3ן;vcæ[fMnm(d)#bnVj/?-yřՕGzbS{;F[/]yOw[tRy/O*e䗘b+ۆy .1Wعe]-?'L 5 S{xь) Sf9r`|dnFPAE$ v˲>Z:gQ+NDZTk>Ul4i=Y_uRsT1βES3lnh>s"b歺 /VlBQLg oh? پ VyR _"X䟅Ƌ*ssƆwi}Iʃ aqܦT:Yqߣo|?ottcMUJS24 PlrBv9ES/?s|_G)"R] Z{2'[z|.۾5>53^ce +j#cI%vl$sM3.9}ުX"dRʣVT56N,2>CS_L:{I9ibd6^-}V,po 0:^6y䬼NE%j (iEby\Kd>K*^R_3n DYct0چ g̅ "ɁASt05ݹ'{;ZƆ*9 ϽH;,dJJ;/םOik%W7jc|{5záV/[~#ESO9uqSFʡ)\::!)7y@Or*Y0[IJ̢um*5ڹ+tٖ%?0T*Wi{|ݎq$4z@,0&u%zNXWTȟ:ja9lu_T|R2YJv~˥ s cΞ:2KfzI&ꧧY7)Z9MN_ڋgOXb#b2rN3F~ct(qR$^0}=iQִ\!T0m E 9Af u;%Rل GD4Ta"QjNŪӇ֨c#0Z <-> q'vew?v8b9E3*\gsüUwit̂i7u9:eka9w;Iywn;sbުD0M[Xct[f`A파KW14:2x/WJ,<nKTig_g>ܤei*xl)c 1OyО CR2ҲFvz0G)|Eek2s.;{fj stG%ev6֞:c$I8t΢ܢVZcqES* FabIVjTx;Ur xKMzy wW+3 @bFm<.'/%0`O&{IZ.lxEU;Gc0vwEx0tuxd@WH$N-)ּs_C[8em=Puz[ &9+wU]RfH9+kR<'eNқ"`C=]]-o"G7D,ANiD7w57]Ο6[7<ABh /J4TQ:cqk|49+Oy#t7ջBaMvb2 Jn-Mc#CcÃ08F~ rО)O-KXul} է{\Va.k72&}Nz=Ξ֦N" F̂cުj-?_)-qQq0KQLJ{|``ݐ)()*REDwlerJV*w(@E' uR'v\hO{O^j Hf^Ijv Cj=#KZk`%:.Xk^ OC1$p> ǨvDIzS$2 ~s4AW{) t4_V4s>&<C0y"ݎG3&kzSlEQ,%+1MRC}]^9vQD7ztw66ԏCx0p2ގ`?EA@vє+׫k7"  0$T``)pKtG%\.tpva{۽.c]3iz|dhld8Fm~[0KԵQio8^ϗv ,AdJ9*ŰؤI? Mx@ {? SR"'t2wDRIgHB`KF*vw4MT7u67$Ē\stHy/LдiI$|~g+5ZKtyyű,)y-h]Nq G)eR)E_:qI4E[0hʼ%ySgO]-9> :xĿ\Fs$n<v)Ty֏ _Dz\곥s_ua~ 0x0@dGS\He(.ywܕUXTkS3E"r7S$|IӋ%RLQu;80qM5ϝʛ231={bk$5gijCPTThX611a[uע;*,M-"JUW{af3kFL$JL>$ᾞ'`D}nM7LHnx=<ϣ *5 #9A&ps%I~'%;?prbF (%7GG.מG1 DHF+MQ]-#D"klDZ IES3 a_A(6tʣ=- GG*5!zFY`cA0ŰW߷J<8YȕԌӇm4Mu6՜>^h3>Y#18ðP@'ڵ5A ϝo[}GQ {N:޻l˒{!% !$Nm0ƽ*Wuٲջե}?ݎM!̶iW[,yGD_!D%b6e??`2q9-V?O9sDSL>mуk^$)V2v҅7O{[>x鏢 1k*NLopDSN<(Ў{}o#'Z `1IifzEa0 3)>a7Z|Lr~؀!#q߹zy_wcÇO3Z7}q_WFf_k樉ȴ!#?w/)6,4 ;fܜV*qIHP_Vx߆κǃ¢0[S 3غqd:~W t`'Y?zpvB$f=p f]foxtVa\ ՝)jo]gqlk);>za~?c&̹r ;ph0E߳;Y¢\@QzM$pb.}uF&O_Kf,%qO}n9郿Qb$EE'Wmrehj;~"'-#9؎]~%gJfpeCB+]mavsmm ?>#)+ ~"<. P޹i4E(P\l=]+}u#O^i:rGQZ:2c+B Ɍ"*K [ZN4$m\a:/w܃m[{MYؔ=p.]仦7%N1>C-X ȝE7k̤k>r۷=sԄ;y!5[ @Au1VonU~[$I?SQE^ 9U5/ 2?C2R$J5 Cߒ!ѱcמ"^bcސ`@Oo{s nY3 8ȱهtttb81{Ԕ>ZRXT(P7gӯ35&eubW#Xƿ='϶ص;=5uHtiٛzwq?y>u$Iݟ:ݫ/pͯ{MBJN^h4;f¸FPYUmgp:oά@aGG禭V>ÆdqG 8 #J(Pe#Y4S[Wy=8(_P{{{=$IE!I4Mcn>Ԑ, BZ_d|G?ѹvFͶh|?{'C2nƦ^|ixih=MQǯ۸.-RR$I$À,8N0 $Iʲ0 yG4M8a[qܦ-lO1F94,˂ h4 }}i$!jꊢx^V+I,YQXqyY1 i Q}]>5qqꚺ&YV , $IȾwT$,מP dhZ=_{YlJEQ] 0'Ol޺}Cƍ. 80}%KIzz[~ǏsDybB|6Z}}^ku:1ONg ;Hoh\kmo fϴȈᅲa_|y!%$5jjS'OܱkOB|gHWe6pޜԔdcO۸IİЮ556tw ӧ^({ ]pdlZZZpIwWw``c濒 iY]S2|76lHřJϝ=bz=Mc$I߼e-7;-5E:-wtjj>ZtE9w/Z޾U M,ˌ9bʤůMr[Akjjm6O{8*20{;v.~~e:^=r{dqDx˲\z]Uu5EÆd͜1f烏fϚ>lHO&'<|xNu#o_z^~b<˓Ə;YR2u!Y7o^|,i)w/ZiZ>%9)>._yDGEz9'spΚAEQ/j|\ӥQ?}lûF{O}|EZjjeu5˰޽p>(ʾ'}nf߹goNn(QQ?fmݾDNN9tC]nW?~%[O2fH},ӧm޶)(0 M(i'=p=j j[cORƌ:{쪵랋]{ oh4 ?}X=}n>thC2'6n*BJrRN^3/W_,k?}tqZjJyřkwu Ӧ 6eNGM4E_-[d4Κ9mHV晊>Yòr?98((>._= hnny⩧7rOXm흿z/lE{GV0vL҄qc8!lqcMWpᰰ[xV8km=9X]6j}OPE''MקѰ~e>r5-tI{mi??zjݾzsg6dwQP<z6d*>YȲKg kӥe@Q_74߸:f[x$I#x\l'X{Ͼu6%'&FEFhkooh^fWe5$IW;V^?eę3nݾG>}aagǎ9q¸3Ua$WY?^|ҝ{{ע?eFNQdhhȉֲ֖ȠvO;lpPdkjn~g ,CF^U]]8$KvcİG7mٶuθl kx==^@ kjkAu#'K*j~'#">^BXV~&7IJLrϿOJMIo Dp&'%N<g  5NO-646 vuwd$'mܲ 3kFddg?b EQ fˊ2 $Ą>Zx{'K?Bcں~DHp.YaӃq{ZZ[*_,7fiSOz? 꺺u./?tk2 eC>zL0.vl[1(j4 M]{utt g PPXNtV`0 @a]ݧJJ?ch-fsB\ln^A}C#Xxx՞^1$3eA;;nOo_>l(0iɁQVqtصtIeYJWZ___YyÝ]^S7EtxX/+$2̀Tr/-n̲]]yx=N:NU0uD#BrM<Ȉx)-+صdwOO[{ǭ=\rMBYQpΏe IBðY^>z/:UrZeIIqљ՛lR^sܼ>0j>\2`@{,~'0Och煚Z=zľy%IrhVfDx0 X,fPQYz6*2"kpF{{gMm]o-*20Y̚i00 DcNUA|}}RYեAF˒txNʖgBQ8y(wEEEh4qcFCJZ[[;:TUص:u'KVdO  =CǏkjnٸym;A TQpW]S}?_{Gd 菊$IY:}hƌVvEQR]]ug*X C=-n'-5B KRRBBzx0pf݆¢bA(A OwDFLĄڂj ˞n-xCV _>|УO1㊢ 8qp~"> Ąc'rNGTThc(JeeՆM[;: FO c j'Kj꒓ղ]?nxn|*j\$<[,-^o1jCڢ1~#'M8Urh43-˜Y3YZUՇ`\=qկ]MK bv# U=g꾪e&M_ hNN0% 1EI cEa >k5RuLI#G 3p9uubQßmkbꖖE\RTAx u~VXw{?| IDATxk=f;1aauuKNJ4L,ƘLɓ&s9''%^0Vqnh6GErS3Κ1˼_dDmhhLWqqq1x<,tâ(6446 z]dD٢(tD^?oά-1}j-q<*2$Ƀm6 E%Kǎ|{{z'&btͶegdYE O?ViΞyfL8tt>ÕE^M,Irm] g.a(2a{Z8wI [8߼u;@  h/PQֳ ̚1-11diN*aa'rsZ}է/EQ!M-'ьAI jhjyDN$АZ89cG_{xMcnj޵g_Uu(IGYGKk$$IjX)u\-˲U-fsJrҖm;;Q8Sw߁!Mյu^񜄄xa[{9}ZJJ\cj5Eł 9S0͢(7PZV0)ɉ ,W~4ͦĄ`5)MF#pcF*(,jnnѰ,c]Aƞn{it:]ZJh?w] Ϙ:E "**62<1cA V+rwwEQ~#G {тsfet}e93gDGE;.cIMI/xw3ؽ7x}gLaXdDF_;zZgfClٶcV_߻>l_=}$ߟfѰ q#Rc%IJIN԰08߉i8(t\yQJOMu] ɘz6>>s>*y9۶Zab=sl0쮅7mٺiؘhV(m۹sǮPI ޚeef<})J6e^3rě~/&:J_s.$+*6o݆aج.l2-Z0o7l6[̳gNX:::[϶=jG?48ct+8b4T~#.h##~7 aAE׮' by9q,!>n*)1F|HfFWw{@%8(讅p0 |@S00,99ov qcGϛ3'kq,-Yt@R̚a[Z~> Ь̤1 ۺc_~ q`00 Ѱ۶c?_{CD0nÇp q|=--,>q 0쟯90=e &i7nްiKHpچ;~ܘ7~fHu:I=xێ;w LJLX`h4?yvo۾h3g?4ޖ}3'NK$%%Μ>tis/jRKJ~ g^c,GGEΟ;$H0__8ew8c[)0;hg{aS'O8%.N;}̌K$^y-spMY`O䤥^JJLz h"?rq(Ah$].72jEX-"˰$ju: dYv{<$8i%Iz=8N4-˲NBrz^m;zB׋Β\.^(FQdwO~'a0eFQi21uYt8N$n[eZz#IveEahF$NG(GEMZJ Q^﹅0hJp^)a=7=1aAϼ[|pi ^\p$Y[ EZV]nwu4M+tdY( @@˲j,;'͛3 Bxx^$hoePPXtي^GdD8q<ǫA Ck4EexAP0Y=$Ir:s-8ܔ~Gj(d MKj1 s8>>898N M^$)$.hxQQphXtj$InGeupxWk־K il6ɲv{iZ$m1Օq^/f5CqFz}|,~+20Vj5pAp{<}}u6a8#&,.[e 4 %J0\8( qC*Aشe[m]($ItzeYvݒ$3 #GvE;|q1ǝ?qA#5Ew@]( x^F3&…(paTuӿ [ίnj/:dYF] 0L.0Lx0 ȋEQVE߽0~e$A r[FZVLPr$ya .Lb_+R_R԰F9ٟDHQuъ|pѭJ_j @xL_,SFa؅uͯze M~b6{U_9}d,˚;R(t77/=)x\;/m^$jeʏ .VC/ICڟ uz.xG{ڡa_ID{cZ]De]x;P>U),]zy܁nJ!aRЪCR8ۺc1LӦNڜ)N-7g9u.{::>q8pWBTbbV{]-pr]  .oAA  r   -*X  `! b  r   -*X  `! b  r   -*X  *XBY  |)@`W C^0հ>k  ?L _zeBBt" Ը r;+XǵwthXAn $Aͦ>N0I${jOAAZ2ĕ\A@Ѡ=yCDQPҏioWHv3˲k6(0k%QXz /x;>m Ɛm1 }}Ed5;] _nWnoh˚Ϯ8w+E>>3m{ ?)pOINJOK(Wkl7Wz9Aq==E h].׸1B--9y==0LC5r󚛛G  IRMm]RBCGF4'KEv{<-~VTV$i2tttLL+r=tl1o@J 8vqI:ceR,v{ustTr ZmzjJ\l=}8$8 RRhim-*>CDll̠FҲ3U'""lؐ!:6p>,DQ++(j:ٜWPh1:zS4 I(#򣇺;a VU׼ww;$338.豚ZaOou!;~bמ}A445ZN ̰ ˲ω^ `j Voؼu4L>|BNnUkzzzATgGw\loh[znw۰b6ܹ{NUSPT|u+OXUs:hֳd6Ro8T`b<˳=''YtT$m vaVMeH<_1WOw}X}}Ϧ-jj\nזm;6mf6EQڲmGuM-xefS^~9¢ (h7lEab ')20 8(h48^Wp 8[T| Ε[x?EO ? <~옜>McFEFL76(0 !0j4{6e)::;W~t騑E6udͶrⓉ Zl[UUjmm$i9cArE+VO$dɴi˶qcFS7om6>2}d>Ixtee #*Xq_l6>]ywwϭMѰE!+\DŞ>CQdlL+? Z}qjk A% Bh4(\6إ =A$beINNLشe[\l̑ǂ::ϋv8&""$@}p^i@$ 3K#JeY1IvkyQQkq18 ۧ~KQ^3[݊^,~ewtzzzF qiuǎں: :F c.az7rg] l7EQN'| ˲a,+rsHR0K$$ȫ0u/NIΚ0b1Kۧ(JOO^#"&:!11L^3 tuu+g,Kx| "d ҹ)lBcv@c=.?$+ZzFÞmkz9/1& nh0:GnVg!E)E8sC 56spS׬0qE7Xv w++Eq8=(8n6'O|j;oq8]%K?KOK2i fAn$2,X,77Xф_3h#<$$z$IƔ.=p0;,4tӧN]JòaKr "CBC= =#kA|}|̞jZ?Uò>mrcc- NXL-=i5Լ3,$Φc @g7dWtW8IdHXI|sA{R!ҪF3f={t֖}6ۄԏSa˷BEinn)>URS[k6MtC,>7 q]S[;l!t9]!AA$Ç  ;]Zf2BC,;Ąx٬X,fLeuMmZjHTr WD;ĄxNp8G zqbًORjBCC**΄Ϝ6l6a0,(0p!n;!!.*23hay>>3MA8ICY1| !Cݽ.16PgPQ.^jI|@Xeo~-̌Mhbukc=uP&^6hr 80a\zj9p0{%v}q$vXIǛ ؘhptY9EI jIaE'k||}EYeUEwC|cSw/Z@QS Q~he@2Oф?znٶ-Š3g>xy:4$]Gnjj(*%%)<,DZJ8/hn<ݡrr:eDI ;ڵmRRbhH/( +848c;@ٶ6NsqSʿKQ@t:6>?~<t|\pN+9bӧ'!w/YQ ?& pdez+Bnl 08r8ZbQ"l3 6n޲p8yr޳ c";;:[8=z8Eѝ]]lkow:knع{EQHBdɒQ9/^w9?* `'r'gtڑÇEFF烏nb /{WHINoh,)-H~Y< =}æuu1.k@zCgV I|\~B~Xy| IDAT,EhHVPP}}|V녭eE;G!u, >tHrbbKk~)@ED8n4eYQEC:tsܘ1vt%cvGwwK$)Kf@-Gl2Ӿ\$aRSY(*2\ѻ Pq__ ˲_kjٵr꧟'.uz$zz{yWh0 OIP.xMv(r0 E]AnԔWP]S[]][XTͲH]Rh$Qj5ÇY~SeUS%,g͘yֆƦ+(>rgOP[ AAF1r$|] zLzZF1L'MPB|Q#|}߆M7m5ix??yswٻ}.I"Զ[Z**+u^AA ,Ӏ 7Lڽg u YO8fQ܁4  ui@.EQ+tvu GA U;ڳo,C# B'r1 .))?v ZAP SD\)p,|ezj#H ȭ.ȝ$I q"(00  \JVM^ 08_5 r1 ?}: WMZV魲IZ|a4;:`jEQ[GY,  ?f+*]"irF2tc5d 'a: ȏXIwK ʍp |!wAnj[Tr㿬B[$Y$^R6򦪂>5WWkyow|7W9QџY*9[eA8xMj/p-s{v/=V P S(rD?ytc?_ϗ,avON5_;9ȔP<Ö^o]:KugogGuuͥ53deN83h^;0_/?-ɷ1']s}}/ A~,BN!x.,4*WEqqHu.OsW4jIWtwzzZ-+ $ /}nQ e4n\$GG r~F14t{:<7x5 g :!+UdE;$k4Nq\QsVHJp:%IbhF%nw:]$1 EOx< :=ЗM]N;daq $Y`H^XRϒ8)ݢG!:4jH?9t 2 ɲlw8:Eh4=E,uzeY="k5NrZ[Ϟ=ѩ(NuB, M =$Kz 0 C!BNJx/@n |yBӢ4Es:p|,TMwqߟe{E}ի%YUnr`L'RH@BŸ`{-YVu+X#\dcg^s;3bzӽ[鶩ll]m!G%3ŀឪw ;[g~2H_j)ni2o;?ntF\R> n]eC1o>׿lp4g߫E<މ1DdN6^;i:#a3EO mhkoc2si)+|ϗm޲=f8f.57J޾S߿Ҳ BCCXSIڼK^Xp6&uPβ.;Rœ:>.:Tg8F5ݶUw-UY)onw|^8TZ^16>oCG;EQeQ鷿n?|ٳEn'.6w``Eјhj=z侃իJBQT\zQF諯i6$޾^R,$'[>.oaVVBӶ|;[LJe)$A v "Fwgy=ڴٷo L*UT'Oqi)wv58M+ԁ'z>z2;#{ҒC.*(?oL6H&]4bLv' )DN=<'45L~;/ì~Ғ*p t>,7ON ]#lA;^},?:E\ll 1;l 6'{YfhRD"NHoHp8;:FF3O=q`˾' ?S|p=kƏM)M%e-mm>xxxnپ/fᑑEֺ5+'.|% A7,똩a:`8LxC yNDŽu+:@kC@wWxnfmmiy='Z.@voܡ~K^ցP\ :AFWɚa;vGrRmt8'O9xf1Zm@ Q)S^wZ,ݟMLG%wb#COG 쥫"Iɉ=|hZD}ѽae4M$ $*:|fόIg3٦Pke*5b*ׄ7Ք˔ֶ_k.#9}re_gD[T}OÝ͍81sntb>gG,SH䊾ڒB۸0 =qӈBnBuhh!!$"fH @HOmIuܬ ['7w4qޮ9KV:ڒK"SdΚh*<:k{>ZaK-8xS}!,q1wqYE3( GN3)T8NQt2#k&b]./AXu=2 r!lU]+6"@ij1g4OwzEM0 EmVMTdRZ**.YRRծ5|Sgή\  0');/'x8OeaH-bj1;7Z~a_c>%7%I>4(^Śstf{!'>.d6gMPTQO <b Q')!1**bl||fD##N>=c۶ u:-`ttHjr6;c ɭF<.i:h=k7ėMB4k7]D:ZX\}tg6?0w̙=5D&[R34M[̣CҢ >k겶uU9mJ NL9[ Ղ~nTrجjBKN*X{_xt\٣ugSg"lnȿk¢u9셇9k7M;84`=u~_SMyGӅ+7dΚtѮ+6ĥf\(?(=yhkڍgFIT_r`s!*TyF'$86Kce bϿ{=AKO]0N)Xs_HTLÁ5S>DPjt6<`D0 3잇\{AUwօE8WQx"4:v>M#Hʅ+7iN~c&e,PyB⤌l-C鳅?/ygUK ގwGGނ?oẀ @^>95ڷO|Ý72kBۧ{".oKFi" }ͺG<}~6O|@RLh~ު(]uF6PMNJ0왟nX9Σ?p|<[USr!Sn ?n>ԏ::gȈ𛌵Ekm.+XN͇"bll?|{ A<6+[ZJ NJ{ 0{NJ e/c{7nbH$Zy{O0ά7C}/r?yΞ7g̬o~`r_`VsQ "X,6!(J u>{օE$@_pR(6)2w> terϿ잵(k0rQHh.K̑5$A*4ֆDE^ogs#"*]H,a,fuܼBQywm[aL;P,}uUK7< 䆄@wu#1PtR_ DPt26:Կb)+ ?29"ݭ}qII^B&):mcf)>x&LY=m%ׇEIeJCBJ_rC!B0\HAѢc(dIi<~ywn]T=g@, ^/MS}=ADCζ0CZfskTYo}' .@$M YA3G §F=8@3AaV aP~}<G&O/HUKR @!P\ U(qt껞\.; B~JQ4"8RS!=}f"ڎaH$z&f=y!BE'S2=RZɕ:\nܘhp>47١T['I%wlmiJ$ݻkA$p^Űk'Qo{֯aP  WX M.Ao#EQ)*0\dDML m,!a ø =N'EB8/Hʯё*`aXLJb8a8q9)5B"fXl!_(r:>/yS3r>/"Aqar( v34`" z(ù|1 s<:1@MNg9UOזw(ry[q;(hO +  B3& O@OMH!IB( ؀qrmR 0摡MC^:68(͆ryd`8.K_ C3 CDKCu´, gƪc Œw+5:_$Y(a:[+Ϟ^Y,6Å bAh}~"08xr8@'*T7>@H 6.{Hy`P$PCnd ETgu̴?tجkrUP`O(Dx1ⵛ4E1d̚>#ЎKN\x$A^Y~n{Ǜ/#(*Knu$UG)DT,UĦ/YB E.[5-oα[/TߥhIɚ'r)ZL#rqtؕ䛄2_b|ތ5h . ,+9qh=5ԆFh09k^s}(6n\@Y[R8:40}@D'Df}2fYk .?s,Xt(5Ք9baX}!5{ƅSlTRht}]mK(:ocOURjt?5{&}џ߅A<*K{;Z\J EQƁFŲYdiUN)58=oB6;w (\tW˅ 8D%t!35Wy\N$-ck,!_$Bbuidݸ) IDAT_jKv<'R_mYP (R=)A2aޓ$]&EdŦΰЛ\l۱kgY} s/+vzUEC:Vl?c0DiA\ϗWre))vݭ}FDi;|^O~gG0n2_?B`v?y[<GY,6_$8WT|Ew}}} C8sgO/ߘ=֡H9W&^jo NM0./*2&wtv۽+Lt-jf\/LQ-JQd@QTgWw`G[WHl}D* 󵴶p8D/9y $Ӓw{ohmܜ%]г?kR) W%WM8!3S=J`[{{hHo˿c9sZ2U9O`8wCwN0OLs&E9v}ɒE>~,i?NN/UnY8u?BK }v-*cP  ƽqZ ͷj5Y_lEbc\;9rk.o!?A}2%H <_bsZs|H*{\.g 5 &G&20M*3}MN&]=ɉI`ص{n o3;^"'{=;d{?+ő{Dwk5v /VƎ9|/ Ym#n۬8N gKҀ#3J8 V<2}IC#o s-v?yxW^G }ooW?;zV|*q7߮0wάukVI%koW.)Xw 0qt49)>q=n+.-Hv߽5juoos~aْE'OU׮κ^gvog_qq#]1C>GO40?0;D: /$&[lB"jm_' lǫ!(:?f/[|鵫WfLK')o׻uc'NLǟzF}ײ VWVlپ^ "X2L&q\ǢBOU {ժ%uu֭]m޺nw'GMM1DE^l#v=1>;_YEmGz@Q{ރryʪRHUuݟN.{|>A A_Bͺq]-Ax|D@̔:hL"@%Oܮ B odJLfX}}ï~C;.H_Oeb|aOdy_H }6.蓋NrFVdS/'q?} !~,O^UlorWyt0x #4D@D UU{t^}7Ca_\}nG~û&'+'?zRsKoo?0\.Xvy:{w21ц$7ak0 Eӣ&Aŋ?uwʡÕpo_0#qߠC~Ik}g9-laaExS~t߽O~Mjr I믾׽ګ6aj@N_?SRIMUC֬|7ZݟwgŢSg:r=LQqr:m@rĩMlx yK=x5?λlaaؔ|864<ӽ˗-yNwZRZ~PZ^q r  Ӌ/,.XXXt~8zgbf`ܳxŽVm7{tuj#It%ɍJy۱yMnXkatTjq z7fY BC/fr99 \,NF8l˖D0f wfȞjq1$V+pziڞ!!\Ӌ'RwwŐZ:f2 B,<""x^\V!$Ivvu3 v{ZuI/)X׿yGzSp2JQP(JII \hHQȈOK _jn|32q8@ 訉$ɜ,P0g֌+5Y3ժw vYpA)?(H$DӸ\nlLgoCIuSV~oWP@<ŞB.dM@q08#>LuŞ~&J-l ,Јy7LBGbyf>@/wO*CI3!vSp+ ,`A:-CP)d~f&nJΌ4C3 ={t^~:{l6@ C ݦtɮr}=Ea xNK amlDaD"q8kv4Ð$H@q$6# <mI@C1OfRbBRb'-H ,> AA܄;EBNJMIAQwS4%(1Q{@3 b(2d\Y왃#8 Gހ6 re<6:lI<|uؙ6GPJxE'|ߦ8ppd)>C s#rt3be>zշw\C*RD,0cFngwl~_Y]=o޾~X|Xϊ_B:rfb؈'E{F"<X]ؠƉpA/=tfG|>ŦKW,kx(| j2,gq=#n45VF\.ͦըmS UJedD{Tںٳf@##U*eEUuOo_dDDm}}"Jx=1C{."@ %#)1b%r8^h8>nɞy# Aja˗-=s鳅P*pw:G>.zrs#I1*8j G$+=۷jdar} Im% gV:Tg,o]D $)fGCz "1&Ԁv6G(yWLx,4 r4~$*J_5]|裒AA/$s%]hxLMI^[3 n\왡ή: ,P?VLmNQgwGo+xv[I3*k}^!d.{g~ !ސ㳱ˇ><ߟ-}i5Y[ԪqY3Ηwp._# ,ڼeVY8^jJ2@,foh\.ɞ;g@ ̛;gť fMϼ~ AgH04-5EєWVMVNk?|Q3r*j+* ȸ؝Y^w Qɻ?Y^oSM!:f=;qJA [QYuH( Ane6b݁O=6R)[U pG {.?P/X\D9쾁qI)'\qyIZ-D*yIbлr5>J/ks)Q*"^܄BȲ:PdkM{\^RG {@.d+x"dpLhCICrL(@ M3}c1fʸB.Fd PX!2.!].:)'Lν~,?0ը1G)ev#5yĄ}phBh&-&MJbRn P4QryuuL1׋aRJ%cAj* l lDQNaBhhf ^qi1j2!uˍccEi{apF$E"ሎ6p$ֶ-w,?`т/t|3s+B?@tWw7Q)Oo gWw?OwG" ՟.ottԽ){bVU¿a˗-O$V`4W7vH|E$9<<䟭h ^35%m Ežaݠ _65--(\$WɝX[ZvG_Ϊ0 c2{zn&\jn /u:!~b^r?ڵ2M?ohim빁A ~k*iz=I4`ҷ,EP4Iѷ+0̨T^QyEQ}m78[Xt1~+Yy}c'NF!I$;i $ȗ,/6>{\\36dMEZZ[+oQ $Iyt->rqCo$pqHΏ? cv݆h-^ۋS|d O#FwN4jzlw:mkm; 8Nt5):F[`[z*:,>krw庡M(OmhWyEդCΝ/p}&%O3g zʪK-A+Ho[Z"zGXt1q:}Jg ;jR|;?0R)x6'B/"==5%t:s6UZ^lIAk[{EU厍^8?PW011QhALLHGQW64^$JoԼfՊl1ME%6-<,l"B.o\0on^wLaGWFV*ϟ y}IV !>n~ FƢ%&Bd"X|^@mw8=mi:9)!7;K q|IsK LNJDQv>{KV) Ŧgf=WS*˗-ty+*rygWL&[8/_Vg.5Y3rqx=RTtvu3rBC![?i2E ܪڋ/1 h8$[Z+~XX(ERK-! Ζ6>۷tI(.-w\rlц(@Ww᜖u[1mj5{8\a;XRbBEUE)IzXoÀlS^YQP `֞#-Gkؘ¢4̈́Wtu):_\T\5=C, |V[t$#=-: -\gE!!z !|<6mjkxB1-D?qtshٳY;vNKMI)-937T Zt MSY3JZ>rDӥ3|>}CZdh海ޡizFn*+aG;{&DZTT*<\a9a=H\l;;f̋%v]sE3rzl{ҥXySaaR6[iyEJr@ ?SXT*6o. 33d2Dkj3b+jg911T*LfsE }JHJLomxG{dXi))MG/4\hZ&=-M"۩I D.&?ݳ?<nR|xd[:284|J Ge|(I^,Jū;Yq뇚`vT"JH,,+HibێǏXBClb}- CohcǽODxnVRZFQt!v}|БᨈNJK˪ktZ dϾքriՙ## rϗ;ͭm}}ť|_וWV.,VdD8`xx=wL9 Q4Md,U IDAT}/6tZPx7--%ຑ㟲u A[ڮZsXڢ^m"l&C4>>.o'~x<.I=}1>\QdOXը</)1!*2⚋&'% x< dMgI-/}>߾̞8$sssg>-5"%9i֌<r::̌Y GG7mܠR*1 hNj-iZ(,Z ?qxú59YkX/B& Ei(b66>_=ii(jsq1&]{>'&&:::8rClɞn[Z QQEK֭]eZ.x,0 ! 9y A Èysf2uk@"/\0OӪժʪM@LINDQ .Ѩ ԌKMM5cϳ;=p8i)e4M{ 2ESg 1 3000b4޻~R`soիidj4~r ͝=cRwj5^lɏ+gpphh̟3[j<6tj}Q+G_<]x[ӏ%|TUO-J Ix8~{K -@w_js+;;Uӵ+2?ޙD{Z Uai0XwVpO^A1YRK8?_svןl~ƍx^D2{Vs QI ,`\f"["Cl*<[t~|H(`ѱzY3<OYyR)k=!BY Y㞢%i^b=BC xTOI1}ceC8yt&/Wrp%U )񓌀3/p:5u!n^Ӣ(zܩSgWZqw|}`FxMxodR b%'%U.$b~eKRR) )B̝=3kzf@F+.}pF uw~ih8y̫zo/w۵{NYx T~{=pfF?A%nBD*0 c;.5L-  ry\RqM _y64j5 X!$j

@azLKGuRβt!G^lRsftg '0erXl2-g74Ͽk5E+<.o|fJ+,K!cYkWu6 O`0d̐% P/ÆP9W *1%ޘ#0WK;<KNYahnN֑Ǘ,^dZ{{W}AK""NKMYl aK RSa@ ɤn1V$YS[T*b|9}<6jWuvvo:?3lCWjڒcp,N#QhBw)RJ@[Zv {e'{Ȗ-koP8&!GGï9}$S}$ GNfQ$tZՒz2dZj1]9K$4.n85mped&* 2ݎX,FxXǹVɤ$Ivuجx,xr9P!H轷Sgp:]]Z& G"QB8R){zzZ걎Ȭ#Ijf͜N,˦ E__!Aj 0 nGӪ*e].hԩ8 9UUFHnqCdDc M} (VQ1a7oyJjNjaYV.'UJdNv\CJ;*& rIݽv=OTrsA=^ۧDoSFcAh0;IRj@%7E[n|~@􎝻;~8T*i5΁e1 h4US~édOF8N*|v?ƚucSLDxB AL * 7L5~!]{FcD,H#gYl6> 4';%LFc75(!HS DjZ d âp DļWwF"% ZZ[חS>ɘHf1ɧUMV5/.XD>΃X$JI̠7o޺0쬌56def9^U9E$<И f͘}."Hgg↦f@(ð==-mLx3:`?olڊ e3jO k/ھcW81 A - ?aAɄxNvT"1Mnɞ5cT*a9N"GxvnAH$ΰۭVs. Pm`pPH է Bv{Qa!˰n ,!K0 r4&!nx{{AQj1$iQEy ?O"(J$˲.PnNhW\<08V=j\}LBqP(L!N9흝nfΝsD,Ng*+DZ"p|8RD=^oYiJ*+/#eZvp*'h5 ed2|~Z!'9㺺{HA^n)X̠gef iqŜZn M!bR}\"w@,۵{/"W͜y [(Ї:]MޮXᮟh!P#,gJR79#{]xf:I,ns$%6(K!ġP E^!<<`8X滳Rh!c;ϰ @;QFXb6_3rኖ86J:'N/^8?Hw ??jPd]}c___,ib(>zl``miBp-rMGh< 5$)>QWo4&M,H$c0M]zӬ"R"X_9J^whgp00ũbϮhtC~,"LdK?{S,bަ֖VdD"v쬚ZѨG ֶήA zD$jlikollJ&&aϾ-,^8czP~^H$lmkomkomRX,{BteFc>u nPH$λr _j!$$y9|6@w'_}_Z#>.a"hhl"eчod-B:vP)KWcs>HP8|ʩNh-Uy8mբ|3DNb_'d(JzGKpVxzFD`8}gdݞuv;DբzC!HO(mV ΁斖Y3'ɣj3337GƦ~/_"бP8aغ vu޽̧iQPdeftvu??tOCFJ F\|.oZ~Й LA)bXpGf9onivOUjWLVc\7)ӪS[XnJ=8w:PTܵgǮ~ތs= Akjoܲŋ .nǻr|]=kYt:E%D{AZeNyԯjq S(䩻dESm_X&H BԲӕ,j4ƯQ EQT"V55+sQy|KHA1ib6R<w_|"T(jjS"ݞ@. w#G7|Lj_nkpbg$M&/2f a$e}?ng/4]K({j ˞w : X+۳w/TFzzccK?t(q t֬ HڽÏ4$h IDAT\v=04ݾ!4M 2nJ$Iu $vLfЧegeiT-/v9ItYlMt٘H$!`h5mmfH$PTj*;+b6cB?Qwa w?ŻR(k-׿H-]0oAi%Bw,K/B.1DWl!S,rRVvRr?ˁ C\X$u,ƉFF ]'REZ|Mpتq Ca;v쒂$5h}3\>}ZM/5!qudH$k_쩼ܜT Ӫ TT@،ꢢ O8+sTfak?7IFoj+MWi[+ 4'OPnOH?6j%% ݥ̚1 0۷ﴘMsf_%H$wӪ KrscX0L!8c5zR5޲mĊ'Hl9r97l2z*f5cRH$ 65ΟV(=βl4 T CUjj 5#5OK^]u ?Y3O4rb㊋sNU{#'/7'##7WP?szp#BPSSKEy٨ow|ŻR$-۶ϝ3bBG/)))~X~'y:ɚG9ŭ{*Z@QD&:'Ѡ"Ρȇο"`7wϫ0a98HycXBPAAϳ\4n8Ov z},qፙi5X}y:GȬe$C@ O] !'{^fݞ֬E"j:>AƲ,ðBHN|~UP(H8PAuWOkko̸ ܷyxszuF)_'OA@#b(M `qH$Y7xxW Nnܴ ?ERIJ*ڡ`hbI1 ZZWb-qf l:ɖ:7VufDgmb<(ӑ?td!UIVOL !!pFT7dHdNRd&;%G@yI$j8Ѱkެ ,'IW~z)u Cn7Eo,] 7p.]MFZN6mޚi4t:iUEJ2 j⢢{wt~[,_M'Llܼ0?/c6jjII-v٣=v5 AqQvܭQ?pQcX^nNSS;&î={!d>rl˶6e} ̹W 0LKks`@R  5kח+h*_5kH($ :|d&1#."r=Ċ:xAhK"hEc#rx_@0iCMģߺ!v[Wwwk[@ sUVe;B?lEZI8o fI1^bK8F?\X8jPZc]AGrkY32sE#vb<(QY-LRYm 0Rs;…Ҡ,H8p;831SIl,qE6L&K X4;++YA\βl]}EQvM,eff d@ 8eRVEcS&Oqh2v#G%W۬ hIO}q69Ű[a+4*Ql qtOɅfR(@u:L;TȱEN=^WS*&l6I$Ʀa232dRiSsܜR!+Cn1ͽ}'~vfh0bqcSoӫ1 եiSKKorfN0X,nNuV$I6:VJ$)'*rZSQ^6Y%ѳq\4ZF!BzmniP$bjpN231 CQT.'hCSs(&8[S{f^'x%>9YzOCVlnzr)}b_n.ɤYnߓ5Ja+4agWOWVO:n{xWwً8Г&: _Dzl ),s< `xhaa\!XV*MFtUeFg~hNvvvv[2y<^ `x Nt|ɗ$!0nxW !0XXb 5U<"Jm;vMQF^8^qQ!]~ޕ"/_ٷ{BY~wD̲쑣6FU7xLJqܱM[b^Q^6ol>" |ŻR8xтŋ| x<7?Ex<wxִ݃wt\TlpGg++/.5X+JҮn@2HRԥM%vE328ո Ր™%'i'oA'>x]L`v/Bu/*ӫeDՍyU#A(I5q <y|a[ߝ% BHyjniBB~=wξz>B׷jͺE _PUS}aKC3ٙ}Wg~5-q]!wDmr?ĂM9 ì]k23UO'b6#e7n0̓?z>6oʲm;cd8^~FА[s]8rx~O$5׬_^byx<4>])RAtiRsr>_k.AOWwG T3X&%pԮ -H5E%\+]'II2he8ȲtL_]c ^h49}xbXyYX,WTQSeRi~^Z&D"yrK&N4Q,Ep82iohp|aJdш`(UGΡӗ8oU)bM+ O<Zyd`j"S9օŚ[[ÑV]z\pX!/ tw^xARv#JPK ;:Dәh$9t|k[!US+i:it:lVѠIӪYApQEB@a4/p-gR Sq?(@xp8bOp.֞LJUEZhA@A9iaGy* }W6wUod: Գt!b4=ɨ$ ǡT*tJD"QT4ͤ&L;ս(!Lu#IQNθ|BE _Ǐ` pXi!IlV˱c5 C*K>? 4{[}[2Ռ""(q`;ή>!ˍg?"AQ2G덿熉؋r1&1QĮDv_(ƬqŒgga=^oj57@Ӓ2پرZsC{b؉zT*S%g*'NlrAR|M4.`8Φ0i |6uy#T8NwEqP=y8ݭS̩<$09S"'^}̢N'[?[;o1Ïy<^ ê*'=VSȲk)6kMqD6 **,nx  Ûl~hz_~O3Vxo~_?Dp [jjܶw}&֛oRTDcyy}Gfr5!8ιܰ=O7ZS܌ %BlI/:M{ mk /jOҭ 8O(khl:QKn>hLHtb#Qo0֞B6@= 6n+êycXXz>i@nbL][M?k<0iVazƴ~kF}[UU~WzgFvdK?hʤJd/_}5yh,λ*M7\xႏ-/~ FQtܫ}WyrpB E*nϳA@ɸqNgh4Z{k9B_4ٷq>C/MY1;5315sQLzF*! -<9E"AY>b!E@*`9'hA>;O!dh(ԏ7LGq\*ohs_p= As; Aʑ Agc>kqӢp@Ӡw>ـTs>FxVFxjBXk3g}qf)4bWy$ҏ2PjX,7 86ol~ 3`# \au3P}wnDxF'KN3 BlAuQc,u hDN!e}OE T3଎:F Ey&쑏!c>s{c4W 5̜1M,xX|}:]M'Jl @Q[Ֆ$Ah06uUH99 r%dK4UPAߛo,+01K51Wł[+ͷ7S&UN_Y-=eF"L(U} ǙOLVe ѷӕ͘VUQ>v D鵵@E~XfYs 0V %YbqHbw8n&EQ8W񾶾.#_fæ-7f"rT3ҫFя8~x) BQk},*s`9pn-_Q*ˏ\<]CohJ.u|ZW0y%HhMͥFGlٶ}ƴjZoIrj$@}$E.&A"]CC#Mys(܊ֲtŭfEոx l:YXNuA IDATYzP0e:P#tUw;NO,̖nw?~_⷟o sP&7wl"wf?FhbH[{ww__fެ8;˽>_~eee~#ÙYvڽkysfkS+'?pHvKJz/BM{zw0Ēlvx F~QS+jVeW&+RSLwTYMP(Y6ksfj=y?232bu]w$Vݺs'CѕS&|90gk}Y"n ٻy$ItuΏ6oݴu[ ,_riOKnI*WFrDbq+5j{BAiKSe |oR"Ma0Fokp蚬xxvD>ooj:M.,KWrIet2?rKoAֆVƒ7Lg\s񷿦K?$-XlpЕH$ZZv9c&VfX2󳞾)aY/;nԗY}4j|ŻA7z{TRbO*xD~T]t:fݵg:r0 eu /<~t_sgY8=H$?zїJ\| Cmt'/Dwރ.?7 صgg#7]'V(Z̦~A<u8Wɧ+J|O}//Bxv7\7'M%~Ǐ/aX65U3gLҷOhnjnܳ/#ݮ=5qy!<,RLR NN +IyWtݱU2ו ͤPYH H FuQu< y&YIVd B]DO(%&Kr)]{[,s8N&0eY `REE.u1ݴe;K?xNa{ Bn cf>-`04#GΘ^=nΚ9}t7S<ޥ7H^ fe9i ZtM+rN19Yc_ePN:9Pb(AJlN _(Z-^k(9zl8ijnq\xȱ<BH,۶`f6nwk[0^?LP$D"C/0EѬL+V*(Sh4cמ7}i,7Sp=mA?vW~Z}6@QykgӗX  źwv;qira/Sc-=/(JVai~Iڹ2*ۆNK> A%%֬}?N(_`A6kV]aòΑIxjꪩ',H,p,W{n],Μ^=y7Vw*2UwV[,jS伷DZr`r*W61!`NЬT۴3;u:ɝՖtXך}լ|qŅy n:yl;{=хvrJԊѣ;:dӦƎDV}& ̓),,,ظy ZǰnO~~ngM[駟\A,=j/_s= rŞY9 546hDHfKzLŞyVY^K2B8ʷ &gH-RP ~0ifW(TQ%DG8=eQ$.@`aͮO?)-AfD"]]v T*LF#a@nFbh00 -BWh4;hΜ;W~E"DHB\B{wDU+D`c("N-RIH üAdy($iܨ r[5竪k2Y|9Ap.yHie2Y4mjj(N0Lj5A=Ͳ,CZV*t4Mkj Ü.W(rxjjN|]N7q8eJ`q<:N tVktab!ήp8hb~Dd2nڲ /<eK{O<>zԈ 7B=#Hb0 sg p,rj}"H6J{S_)$j ]l<9*~\Lf:lADp{Aȏ7O >0ku2T&.* ⮌IP$IsfP0ӧ$pVlP!CfY*9KK/ >/:OՍԔ~jkqרu9.:#x"D"NJil3zC!+,(H4d2 vWTVgge.ё$Zȏ Ƚ,_VW![ Ps͉V@qݫJH$U05j鮫?JH7-w']E|LЧA=E  r.Bd:θYPZVVU]jU~nn\U_A 2A}@{GǙ+*+c:]gg禭ۜN} AP 0 3fϜ{ 0~D"fww\z<  %XaaJI^q `(DQ$EA@0E={(nXP AP`!CA~W  RS1  A'P Mjj}'Mp_AA6hQAA U9; AhD8߫a(B[-W,/:CFHD7E&{0,/=a"2K BLkKćC@ i`Af >ǎhmk;:;Ϝ=ws/ttt<sNuy"ZXinT8k,<]۶y=]6m4A 2A;;WV^9gŋAhimx2`% ܴD*r3[vUUVVg(TLvoDP Asfp ̜aE=a͚u`rӪ&(1 TOV 8S>[f9!Ϧ' ӿbWgwI(s ']/k%J{k0JqL1K;y$#J4 ltu2G&X hp5UvI"G'!ӘahBtMا'(kđ#N%X u"Carlcz $0鲊l^\<^RTayyr>JёT Dͽ<$Jb +xٯp{ggG{-)IT4:֖$J/&g/_,e{$!*VTBh0QN%A%XrܳAAq/ AnahAd(B !q>8L*{?-D|>T*;yPkعb1W?# C u ;w~)e>w8S۶}xeW H(B t: { \YU7# س ./| ,Ȳܾ[ A8|cO<W>z};kAp8v-Tby9Bx;Ǟ2>&^}wtt 7Y!?oeN _zvLOFBs/o87#n۱޿( 1h 24)ӧ;~"Z ,˦&'W?qD$9^VdR "劄#z'la0tw{ɤ^g,k;,T*r9a@ h4E890᝾U'1Vwؤd gyhF$0H ]H0cq BavBZ4Re$J)H0 ypD"TJEz= @QT My$I4h4Nt546DRT*7H"d_P0 aX,0kA!A;dH%KJ.^,Jf\.{~֭tMm]=4A]v?pG?ɓzr;w9{H^yZ?|muM]fFOH$[v QD@&/U|pRT";-NԱd?E9aBvDBL-:67J" Ʀhﭵ?s1|֌ir˟}0' 2' IDATsgNT*,{n1͙a+|~$`{׬]2y d×w}@0ȤS&O?nK;f rr_v{ԝ=agf|tֈDysf{H$Bݸysss7aXIjTT;R4 Jdviejoʱ*fi,'#8KvJMIwޟ6e򼹳KJJ7nޚ`w_On0OS@Ǽ\ZֻE,ݘ`qy6B_xe2<ԊI{?ݯ~EԔSnپ#d4 GR%%X6z5k /3kfaA?xlx Y-vttvaò[ڏ;~)6}b钁~u.ϻ,]`p MCq%XRIiYŧϜʔdsMfL~dĹgUTV)ţF|zŻ| N^{c)(B6`! RSSRH8y̎rb1?ƅNH*G t籗z]5trqlh4 y[h\DNUt\lN*Te|'"oqa jt+hI."Ys ^&A&DI$I:.Ȥ "%$H$Ǐ;|SR ccK`0ܲ2"ܶٱ7&Mɧkj!UGbX*4z:BQQb&I2dhÌ[\ZV~m !ljn|g+O9﹕JEJrrIYYffzUuiSiWW&X-@p4&&fs\~~0zI")񧟅Car5>~¸o3 s{j'$X: (BDؿm VA" F$A88 W)Fc1( AB,}3^`Sqo9)@iQF☘!r % AYaԭSvot͉KM3&hNADy%lcHD"s>Դ{>I=jlr6w̢}6q<6+6H,T(22k#șs%*5k75\f?L*%y!8N*6 ӧM[RSXCR`%'D,Yii} g=AEdR@fFzjj2IhEV|>+zB>ǵ.A !24A9 ˲/].(ƞD,I-mp@D\\\8t:r,l=Ė3c'N&'%$f8Y~gQXb ÌJ0Q pvqz> 1)EF%f}xdH]W &%ŏGxnʳghO +Qh+-[Co2^v /9xww@9~E IJrǽ>_{Gz^ɤ8744 r3{HҢIp p<ֶeێ@,S,\L卉T*J$,{%;K^|۶x z}-$I\=Ж8[4MoܴѨ.9) cnj9"diWAHnkQ*ÆE"ѱcFh5Z ?QQU( CG?_T7AP jj~  Xafsܞ}Ϝ9 f͟;ga^WaSO9oO=qhΰljuOT 0ǎ|V>[_&i}~5"hRBעvsO ڟO5(>S~s&'-'2/BgQ0E"=JhT ~oTgfgelw?hni)zDQ >%8lc|HTiUŋ_Z*.yt9? >mʧ? g:.(fN5~W>2H4g̏V}n˖.7w#|>ߒG nϫW~#[Ath4He_nêOW>hx-JC$9,;y)wLƗª՟ozi0/7癧??ANv0ܖ0za{{]2Y~^ q"v~˞DŽ ohi:woD̀(wH&< O qA:}oq3v{W~gf~o~FXjD"VU?6e^_֏LSR${IB?fz([yA=G@3󣵲o}P~"r2eb}4~G4utv߰Z\jJ+i4MB|)xfLzA{ %XD(:s< ==m* -H-F&M}dN6[0k/G5Ah1m갬 [(BT2uS&Ayy_EL7MG߿QEY= cqqm#(B *N1vzѬ1c"_:q&O)$)ΐ Ġ  &+q[kjj-/I)z 0Fm:܏'-<wj{=cj؎ B 2a`^#}gm V'D"r~ 3NaY!gD5,S8c>a!Jζ^EL%`EImy' tŒŹ/{-f_^;oG /_h$|cB-SN4JXb†j5܎Z'4;3#/ozX-U?sI(nOL.~5e{I3͝=S$utv%er|ƴ)gNł l۱s-!a_Ylˎc@g5hA;,d2&/n/nظO?N|2 |'[S?_Hs3 ux쬜03=gc/J{ Eu].1EI1j\;ɢ_V<(j乑Fճ u>6vz'רtR@5/mk.֔uuZϯ\-{[ò>k! oZyE M&cQ!AoUVS&O|}GQ#n.H^{OzAnJ8egg=򩓧Nڳ/--5)1~u vR-DjĄg|uzo)hBgOcdR:wdo9fZ,S4z1^@u'Ol3~OXQ=  WB( 0췿͟nyb8)f4s /E"[f646(HF>vmIQ}A! %XBP(\H,GwP@^q 3 şσ3 |xF ñXoD"7?sܥ%N{=s[|AAC _=v!r!eV *`$1ȼݡO$̖_lWfSwco0V"Bc#fkJ;q FÜѢ('wFX   DBk8F3d( {7~ߕ$U˕ Z"I\"pA1#D".;dd'QB ̛3k)ΞpK"ISqKPMPq&iϷєLii2hs.jdZqr=pw`ά7o/@!#= ?Ǘ.Y_Dۈp8qT"5({GW6\thx͟A :7FϗDZ<)hԒf{R-8+۝!j;dž X'W?qAclم#PSR -'0 Hb݅TRA ]\&k>]FE5-Us#aImi(`co ػuZeFhZ0oNԠ;w)V)Ԕd vٗR*o>ֶO׬u:]6[L*s8y%<|P 0 3f͜{Ir>oljjim[#G>̓ TVUo߱%.'L&cYyő̞`ص{/} sf90?8qe6ou\))=`ǎafÙxs'O?1.t BwVj׬]p:SS}>_ɗiko_fZII55)ţGݯx߽w4]hAW_{UUMGG?N[hl۹/ɫCcK;Q;tɓb":H82I&! G_3gL9bH$}fmnΰK]q?o$Eܽ0?o|YaeY4ET*%V?9rٚ?]|~ꓜaV,{|ͫ?_ p8?|NX"Carrғ˗ܵk!ON?Vׯ ^n߹x) >ZYe|Q#-.{Weeբshm]]NN^qaa {;>ȱc|znAP<\AAVba&55MJBa|Mmm  Hb;vfϜ7oe z)={*-Μ6UV͙5^}-u\93gh4ӧ?00iS'PXo?|Y=2yfgf7Ἴm;wE"7.$jZRj)K$F),S*2SKJJ=6[ʧV?X~h<O(dHE"%y9$IL8STZV+13O=tysiS&3 mheUb9bD"ytxJd4X24 yꚔ$N{cve5+5`eUo8 JJZRU8cV,̕E!PHab(@(CA E+h+$Il`(X$x) 4fgeDO=qppueh!PYa%0Bp4ݳsA4Mk4T %28'ub1 pe(ˊDLl3D{xN+h4\2u5ԥK]ћVAp(BXgSy 0 efds|]}#c"Yx9gX-t:mss봩0 c2 6mf[[&N/JGT<2i–m_ܑa~۾pR9r?\hz dLJLORG,2 ^oXh5>~dQab jseѪԔ#[,7(6djYG99Yw.?79}qcM[)raZjʤ ǎ}O׬Hv/](IFm۱kDscL r׬]/˚[Z{^r8>]r=UAn /_`Y6uh%!a]|3q˲^?==-1&ɒleno+- LF\0^3b6D I:55EQs$aXRSd2-!b)Æe1 g5(5%YBQf%26D)4eZie&Q.ŢMN$PMFC 4M[mu:шa@$q6D,y>xE"\`(NT(Zd$IBT7&#= qFg2Q)Ə8:6))I.1Bg)J.A/Θ>E.$ _*gz3A\\B䢅nogAR M&{OcZjJZj}Ay   ],AA u"B(vUO  @p']?Q!wq`awK0F\B,ԯWbW}}?^, y(B !˲e9B.Jc=GQqC $G'Mi=~bNK?h@ pfwg?&6nn;RT(.ͷ޹\ZԊ'{ͷ/^m t.eRʲ0 q~!nyR lϾ+_PA:nFo  W%X!lhh5UK,Zh!{tqc-0L;8LV{BD";vno}JJ>tu4ʊDS&O0N,?u[[Z**_gk[ۿ[1>hwt|ΛJVοn9 Q%O8ƌ޻oM]A6 OQTlX|Q!@ 2a̟+ٷnڼQwSX,fq.-+Qd01 ӵu4Eٷ?=-m!cie0%$2 A;:; Ϟ=H$|-22Z S.672"fԈn߹KVIp8\QYe;hNKM1c'N.{ssKjJB!Ïnjpǎ;q2)іhK8pRwiAɈGSO Bo8NQgTJkjjZZ0%$XmgVVU;wˤҌ4|]AфŦ8!⬪/yVg~QnDe>ۖ˟h]Ra)F"h m#r 0 h$E;~EY6+3DC}^_B#òTx ǿDe[ZZ7lܬVz v# y.{2 wb]jo|d,Xac~^ ;wohim%%ePWcD~qә`;hngh9ZNe.")P_g2r3* ;v́@ssfHw.[xm!pvK϶ymS=af o C4E8rҲۤS#~gDp׆zwc(>/^$Ipf݆%.x<}S3͙~gZ1H/Ny.hA-9S?HT*McrY~0HT66D}ZۤH80,Fcw8{*tXO+ ,`! Bp[m߹[!" ~YKm`SE;aAJXV^ش|c$Ivv+*?֬Fٶd`HMM(jpV֭|ݦ[Yyh0E)8+1pFGe'>e6M-.wQn-ɦB $!B QTD'gӯ;{CDPzC !n:3?b$~}gٙ~jy%$&'[VM 4:YkE?nѽi4ZHHM3*0E $!IAȈ͌F={O. i|=|Q@dJᤌ$A645F:W2!UZ h#; a<~qXm2 :~q0  zl6P@GQN*6-).al.P.;:!AbAPRf2Qe0(›) Ö,o2;:w '#+BCsO c?4sUpPN߽@dDGEEFp"I (|\BZܢ Y2N.&nv ع{j}$bCEZFsr2lq\);ju+q PAtɅGw8Etz O,|JN~ɿETū'Xtbo]onh4珽-_dk[~/gB0?`ЩhV7 J^1aN촪AdUuuv愑,AAR7自*}{yh*L:eXz;A7 ~~fA~ >2s%cSY,օ 囶lR7md>K=AA,|?{DZ_( T"t"Eb.',4T? b1YV-"bP2/VMMAA>,>(b|އ>|'`qBę߿?nH;whWj^kfdcw޻i6?^r].ug<ᮿ`nwܰOݾg߁{NGhgfwCy+{ x5V|SgKBVؽw7g)(vtv~А%FAXELfB9MfϘAw{E5W}u9+Z%2Q$mv\b8?5yW(zn۸N׿?{q#zb=oڲիM)l?'֯l6߼9G_h6cT@qP$"#|O,Ɔ5I\5tY,>mǮ (r\kh˳xR@Io_3Ͻ7[,w:~IQӧNYh'j~~㲥3ScV7Ϟ5#'k5ڽ?3gLHO|ܑ@[#HΕص;_,}qShH`g0"~xL@na4 C` ^dw\̡!00ݛJ{) 8\inBV-$VQEQG( B $900ܪ7Z[t@6Mv/n$ ޠp9&9$8`06T]=<ʤ^@nۧ.\ hf$II$b:fZ[ZI,?hL&EyP($8].r@$0Ld6%(J<2 EO%Vh,?&? aQB~n ߏX,rz(aEtzq|>A/ j EQBa+J*i4Pq)2 z I-Vk4@h4<';`p\fs%2]o0,RsޖֶАG$Z;~jڔףp:O>S|1{=4%{-  lim}ᯯΜ1_A܉yais{3նn?3EVѮ_{$."Ny0/8D̨0P+ ?^ #@g5J}Xـ1o۹ HVcb]]{Tk4NS ̝=3::jt8y ն~l /7';؉S "I>70%9;{zzHDQ^9J垽::;L&<_v jjkw[tq ;ҝFEF:Ҳܜ[M'N545].ARgNսqӖή޾ﻇ EGKJbzzt3O=I#ǎT<7 0*v8eE䇌XQ[[WYUܢߴ56&:?opd2?_˳Qz-[ϽkzrJ$g+kҒ1)/7sGUUXr'OO=pprpr89gD)>~%Ns&dMinnIJ/ad‚Ҳh;v9g^(:zBRڳW(Ι=kd ;s  s ru:N{׬+u;w>rĥƦ,ZpשS5%Oмj{z::4 X>dl6[UuMpP hX,VgWWRt:o" }ڔyN \.wC=(h-]`|zڱ'O|aXVV~( wLtW+=]|6.6Z᚟ 2ƆV*zH?IQolʏ=RڬLfQF;3M a`qPЩn.}fF:q[w ]cI;v%Ir͟t8Yȱ!5t:}%9Y>7^`@dZK_8pHV_Ee#1Q,$+.UTWArsbb~f'eR 9u \oh3O}/|7Ah Il{D`YtSG=@0Y3Msff(!0 I8e2ӦнԄBArҘgFEF+F1?w"8΋ϯu|ԙAAt[4 `0z`E[{ǓO?_T$IrFVc(EFOR~܉Vl |WVUc(:gԱcY IDAT{<-vƴII/xxXgN %(Mf$+/WU_dn}JrbNVfLt;?]Y#g H"MkQ8KRۖWTnٶ X n/U8)i4Z@d2uG!BCZE.׈*nMNbqBU]ŌX;WuHl;=5NDG:|d6םZ4!$2 Uަ k;.cګWv>~0: pOʼnc,ӳtd~qqrߴ$>m4$7CKryal[׏$gf>NgO& h ϕ` HҘ+9t: PR9p .# \V(rАXǥRɈW訨[w{ORP0BcC1 (pq"Jm]__xXז'K/p<,4l465alxO.,`F[֖6$n ( (`0e29w[^q#5 (\0llJr]] ?H$Y.ɓ%%zA(TWL8Ѥн \)jYLf,N'`3q8/\ΛH() %NqW_`F!Ե)]FQ$)7AHaE!Џu)"$H|q`@1%Q¢!N7`|@(?Bpe5`p< 4U)[Vw.ɔ{LCAaNH `E`Ȉ0"^f]1gL ì6[]}HN9;HRbכu arz?w=S [Z۴Z#J/_ڻ[2Zv`.b(T|W֘-tGLZa3t(70<1'+6& LpFjX -ёl6bB(IJ$L&d2x\]X|q9O1 7n :A(9;6=ie2i|\c9Y\EYev)R*GE|!T>e%'O*HJq`Y!eY,VccD"#qj9"y\sjӍF,Keu5#3p';."1 AŋxII)'`4\-Jp O{cQSsDcCy%,p9u ]^q `Гh41c3'p\ 8 f0EQAt:=ImG(j1 K,/sBi wuwG~`q$I2iHX5`]({U^{@`  ko܉R_F&d敫k0 O~ER7<䟳23V=@{Gǩ3O?xxxEM[Eyht+ '_).-|Αp*J~^6z߷y'ynsDF-^8_.ȝO sf|7\ΒE ,7w߁N8Pcמqc&'$ݳi˶V6yN뫯SRXP8)p]Dž a?)ODIiّc'lvLzՃs87mb5kGncy[wڶswĜ'ӉbQ[EUYKb}+d0qytq"R}=W׀( Wj^gR7wóXa0j J^ݾcކ}X$Rj ޾]{#^3u(cܝ=rUUz]^q)>6ùYO[8.NGGV60W^^s޻5ؽw_ggW^n Amm>'ۈOxy;0PX L*Ȼ6I^.tт r roؒb>| $1 c},>|aiݰe<=|,= w 8GGEQ㎅ [/p8;Wr~:m_|8g?5Vg_ܲvw^ 8Wzw{vuu_7vvr@>HxU5OUv,w Noݿǟ~LEQ==~ }e{gCQTOo_ť7HTTDD 5%#& 8]~˳=}6$niikhj5*- *|׏  Ϛ0M h2]7f7=w;Joy*' fS^WORkCX/(*>;$NS-v<Ѓ2@@.H( wݥo^}V,Vn3o.E`hfg@~\`T Kp8ZZRS"IvnOd-q@r$vA?1!ĩ3}r:G|܁hY3y.6PʝGOo߻JrVѧ=WtV\-v/_fјWq&@}/%2J˯>pRi@t^S6)-sUv!_V?+E6'Ϳ_ggosyE X#EeY+,lYՌJB~VݦRzF=I8i6Fs`͟gA>g {:dۇa0e#FݾswIy<VGWAA<2,,dtYAO{U}wTp8W>"qLKS$ڠrAb/6eҌӎ?yp_XhHO_soА`ZzO)n$gf6Ql_^(ٯP~;76o%P|w`fy˶˖.2@o0nfhE n|nq7=&QoYUe]Q~^+?L*՚x"E]\~3_]}EgLkϾ|Hӯ;Ǎ|g_|yܻ-}%6Eֶvaq1m)T>Rl.=oV/J&N( nOqg⑮Ϟ)"7EQE1" E,r@e6[<%b \^[WF9l]țb2F.f!ܵ';e\Zb0 !O fl@Dž6<95 E %`aLڪ9!{O\YࠒҲ~>#˅ /,,t)^M`AӮ>NT5<cUq<$8l6O\l0 zD,0LP(1JcRn7aqp)=(`]X^yz2 y Ac/tK!byu}Ňɹ|H T"J h9ٙ̉dXqibvfCcfB{CcS3F8X,V;ޜ+u_}~pGiu;FO΂ šŬ'[Ӄ:[V/&wb?t٘(@_"=m _,HHS5])ɉv&ezضcf{f߱kOKkB,b-mW!*H/';w^oiPt8K-ؼu+`?:y)'͞9gE&'%zވ,Z?Aqd`1b~qje0ƇLFaAN)5cZzSƧ/?/qLBCc?8jxGgי% dR)&k-qMYT ,Us+(\HA-SU[WwI  ˗->vԉh5oI `Xϖ"21; A "NZ]ֽ`e^kX7r;EEEEd(zv@9p:*j`'pJׯX#Ş?ғ3:|`IEO&-n ĎeaN71 q1{}E$DEEh4ڄz7&dϛ3@ӓ8Tވ^ШȈ1OfT*VP+ 4rA-JKYU]wn_g{ܰ3A 7Iqw\}d~tJ{8^XTJĀJ ӥlVD09VvT~KENOFe,&Jmko? |6PeuRf8CHR&@„ly IDATds&҄lhV 0 EQzFNP `2.K j{H4& m6GȰXt(bl6Vu]AX,^r9lX,rd6x&O&F,=m m.&=v [*{-Htu\z_{vqQ9NVr0t: өR].\a{o.k@f2|ϳ@jqelr\]&oq0< AA:b0p\g @(P'z;L p9 7}^U { Il8dvp:`p85u8Zawp8誟fuAdR&hi"1AX,  A$I60h0941wϽ3b>|xA8]@=F.E qL&QrZQ55WBB,3(Ȥ l ECc~ԍP'-Px?DD#.†aXP&I`5!fF1:bEQH8|x\Р<&d_^ AԖfu`P,v{]z6< 90 |MZ>|ܱ$Y|dxu9U[W_8z2rȱ;8ohts&;FtUU״BA:mǮ-vV^DQ^q|Gdcu'9m탉nֶںzKzaϾ%p8e{{n1~xcn޸iˤ߲XP: Ǯ&gDžir/U*ʱ)I .4)!C0,#Ih:Tt /W"|*E*{?PrBtAdOo_2VKђ_K]q ;:gNzvP(2mg5CޞH [}zHLtTtTO5j͗ky2SגڅPss~f@f#v_,8Rk4OPP IIN,fSe%eOb nb9vBj8FIQ|oIc~"olDˤRrt$~\8"0 ɾ޵r[tjhl2-Q74ڄr36}f CQ4&:*%9ys2ӆ$I*bbpO0?YA^ M7{i|܁8^K1 |WQ(it::zV$WuPz]CSӔI  ^fƚ+s5gLS$ɖֶ[Tꔔ?=h4maK gNq[Z>Գ%%~2قDGy}vk6^D*GfEтE|zL!K3eLbukت( #`QFNI̢!}:{}˔-b*NIN:rD@򥋃&y._ :1Sg;`X>_*)k>:yR'Я,f'UHt8T){]{ 'd_r-)Xr"VܻL(To7|_иlɓ <;v;g֌Skz~׿{fpX~чJ%nvh4 zŊK2z`B$wϒ~2Ųmʪ*>m ^sq8]=~ϟ1mD"Ncٳ"z3mni`8OoQ8 *QջĄ۪ T+L V-#7Yr[AP\Oj }i`]ei٢F pn|ʄ NjTP\>Ecv#-uGѹqӖֶ}8=uj=Ttl5~~( Psp$'%TBܶswLt@|ɢ!vu+/WO]dg&>t]T\\zl}מZ9kq"סJ_' rymݾА_3(t~gWj("I*$$%"#].׾Μ-A`8/7g|& Ps._ѕp@# 5i|܁0<ə0<z F#F qfL?zr@EQ]txZyvS7֭qrKͯJQftb~5= ^Z.dhͮj{M}:;AxsgcmP}AMRԛ;k{-O`Ř "6a , .WcSKk{ȃEG<-vܚӧ^4Xޡ6Լ}G^k$ď4}iɽ`mcZ7mNQH$%aCˤ=\((:v,c|:A߬߀a؋?WߨjK;N'Fa᥋NgskBB8,͢(˵vww?S 'w=S {W}}lɤz(vݭZea xc]:Jo3'D?PpK]pFPppaʄf"P9SW)w' 6GQTřC +ʱ\;HnW(DBG2PUsiS E > A'O="Iii;^}t:vay]oA9|^NvVtt M&Ճ<ޜY3ܜlZj^E;.; rAe,twa?IQB:G.w Mz^_'D#UEQJc>! (eŦM.IcZQY9{@y,O3a/=EQ ##Fb-z4|NhIo>\hâlA㱏~0Ms7ߡ񉉩KT*5%e9"((&0>U5kp˗:]306H'MD,J%ms ;o>;jP)ASsaH{FOVVؽ7?7pf[tjOVV n{!V3 bvbGfie. 9{pN3B@t"FQcI?DdW!beqvğ*igAyC| ÌxP6#=WH&x,bvz P0H$/Eӱx1 CB@$wX̟N'^h3uWڅ#</O|d A(*r@(I' B>eCb6Wjo35%eyktG;M g+O=c&DCG*O04]R\< EHD,MJ㧟/\1i:vB]888t1Ba޷ (|DQVYR(]70X=\l(^P484'oZ krbGB >.e] W0.8 }M b0d}` ifժBZޑz{z{'3xHD`t?))\ERmo_ƩK ç]=MPBP"!QAТ%&Vvchxؚe6]/GQ x@0=xlml|>;+SRd?cy9Ey>BA $IO z<Faa&I2C!fm]C]}ݛQwL&XE K +᧟z[bc_Kd_ݘ*>wؔ"|~d&/|pׅ1>OD20'PvJw:eK*Rؠ Z[_}W\-EpB0 c4g8f;q S}vF}s)8p󳚟za'n߸~nܜTʾ"LH$O<׬^?Һpa\.OOK}K") D"pnk U $'_ՄA2J% ws[ '}EFa׮F`(*ȍ*4A9]Vk5JXh4JKwbчv?a񢅻vlܰN5a~xvfƁCG^~eeKre{8{9%O4=tճ=82[;,3i}Wھs}^aT~wS;5 CzцBCޮ}׫kUK3dW|@`vm>hNX2tETE ? wWc8j2Lt勒RN8QDQm]w+s^X_^-,ݱkJXb0 -7uEQ LOko/]xp' q{E[0 ֔S5uFnvvYX_%̹sgMFcْEч:}Z4g;C_R CMo<5pབx4hf YBY #$h &I>]+/6ښF35v[l0)2 t~_P'!at&~cɤ0"‚Ʀ xŗxH$rD,Q ں3g'쎊eeii-FnNβ%PÝKKׯ[sDɪjVc1U- 亵kðbůfNvfْ%drϾ0:7`W^{3+3lbZoO_PT*0<_x0㝰ɃoPQ^Z@~fc"strp(^ _|Yi0a9Yjum3 lenNB@߻q|f@iQVk AAL&ӨU$IU*J&X"D*jpphj1&A*5%E(5_ybs2-,Hij  櫌K,U-Hy|QxzU-HZ`RIBE3Ke !nUJ>}_( 4k Bba zZd,/[ɪI!j%xjű,HX&TdA0  ^An,BQTgWw(ڸ~-) K,@Q=JBT FV4MkԪi4A#Id4HS(驩 A$bV&|JV) >AP(J22ʖ.V쎄BA#RШ$+J%ZT8 \24bkk J+֙\gUBjL%0Dz1AbE:ð\-ԛ1OkLRHS#0ARRn1ZrEaF N`P Y H4 AbP ":D ńPSO )4"\-4XCCX?0 V ;& z) eRiْy6hrL R H.Z)쉘9zlTlX;:z>]XZ,س,u: 'UJ* - ZF*h4j$&L&#pL.uZ "|J! $՚tb^0A<b6B.hArR* FV0 %fd2N>?T(VTO8#~VT슼z# W=p7%K8/UA2|D"޻`cS˷~_+R !޳b{_Ν8]d"R)3' IDATf_hxm&+0էj|(y0 322* [4AT<ѫȵ[|Y󅢨hd2>ldAiI@X bv{jս[\jp8u]Wo\*q\ 8w] 0 .TyB!CsǭB, P5>11aOΌۙk'LLBnKӴq4Momj>/4L^Yg狃Ǘ?..☇P?pDes.iGm5'+jGFG䍮up'?gBdggwwύ4ꚍP(;fu=Uj%jqܧjjɎ5yHQֶ{N(VUbBD gκ=qpp\=\1I$MmO8x82 3aw 465عu͞y<e7{d|?k2I1 ?0JQg_Ke0 s*I3^uYC4EQ~7j _,yaJ%\loͷ*bQNVf^N6Ajm S5pHӱZ`p}eeK%IQmm; mB{H>9V 55[̦+l`0{.IY @3rmNNr:]}ceES V._6=x[[;:CH ܷ K-+#&)jp`(rYS&^~ LZZRbĄappyúem%fv>q$I,K\5u@=t‚i3)j*JtZukdR)MӃCCUj|>A_r\. wtv1 beNJ,kY2Ut.<i:Ld`z8!(M&XAomَ]+u:퍮ھcW2,*,32`GgT?9jhóX0{ᇗh=08Vg`?էf\x<'+ XE>Ar$NVV GQEgBӴfƶ=NEiXiz%0*byEdL&V˖*+AwO訍aH$9F<[d4dee<|d&s'!*EQSOlj4MdX`EŲD"p(:ud2\QA:*defdq@nw Qi)D"1>a7 6ۘL*C`@lz{OVZi0P) ]\EaAM7 *Jt>B[.☇b^mㆎN@aa㝬y8]l0M \]:]ŲƦpRLciis?r_yyu 6u]{NȨm66Ve{LKJ%O1 1lYҪ@.Z"'=.HBZZ Jsr=VToZuZ @,M'NѠ7MV뙳9H$ l{|Xu?N.A*p0r;] e\ĄJNWpv t:5Ų3g.YDEQ}]Ro`jx//\?}zjxtг{/ 1@,yꉵkV]ypf_>hL&$bI(t83=-U@.{phri5VjO`}͞ v3% 0 aS64< lB(D& ͔CiJ Ȉ\./KOKh8T(ND4}i MaF za}E-+_i3uZD"a85ժPM CTj4 ظP(4MjP8reҌt EiԚ<[Aa*U& k4iwb^TT884n*>OhpiֵkVBBPWwψn۰Π_ʤiO(3yR+*Adȴ&) @b{L&]PT9 gq+™=spM"8ppme/0#w^\Tnͪ/zwׁk7{d88888 }>_պd 1@QtŊ3Tb$qkXa(h.c&LBdqO$ǯelaxN C8C)0 ٶ|| *ARdrBC3s: 2=ݍaxwwEt 8uGwwɯ[z?ʽvS#uGXn痿ȍǒva4i0jpA.MW0tՊ|Շa2_Cif`d205:As\PMQO^jCЍ7D1A8//wUR\S,3 u|Z-/5P&۝jMKbFZ&LvTaz-K/b vјFn8{Π ܜlrfAh8"$6,8uᏋhElQ0M{Q(G/bsUIآTU=e8>ؤT*$@&ÑsM9rf\V46O&@ `2. 2thh?tܜl#V;08 -fSaAv:]4Mk5\;Ϟkr8'NuuXSRrpo_"tyb8~ӥV).Y|睦i=4Ǔ8)=U`+I|k/hS`hXNf0q[7o x<[B0Njb3gGm6PR`Ąh(ZZ`m]be9~X4kjiq9]8 Yή 6[3@0X[אǔT|xaDhB+YLADo_M)sVf9NׇxnNdk+d[2c[rM%ikjzapoo岘ͬT(!dE$I2 c;;;B.ήQdưƚ\HzIem| "Y,IaCЭ,X .uH%4CPcSN\nwWwع~JQ34y=r33;`8ٽwf4=7  ]lwgΞ{23Gmcn|$ϷlwqD,kp" %"j>wkG^pb̓V-*a6Bо GFH|aX"H.kݚw:^0 b`pb ;mXł EQPH_}x

r{<޺9+WTNVVUC]]O^zdv{O/yF4T(f ،'+CpUҒP(sD2E%3=ponNHΫoEx#6^`X&`9Bgb!E`PG`1 #M-vlj3,Rr^߿qZ׻s^?VΞk;o\ HGgׇMFef`FQp=v S/Y(X4t~k01apQa~iI~+X@ P[Wjyo{vH$kVB}~Z"IrEŲ2 øܮo|ںG$|Y /v;"ⅥEvݿxaG1ݚHi2Q8NݓH:q8 ~\.{|9K>i8:טEQiikVwllnٲJ24SH$?}{ ޲I(ΣN,](Le]DI"bw8Bpww]wVH՜kl㶍Y%T xP(=-,4- 7[hY_$yYɟa?X$=WX;0k"aBCvd2L)X)7 H&F`cCty g7= m?Q0`Aa"{.g#oNGW{|׺;;ْy(8Y=m IѰfJ0hZZ]޽u.-)Hf4Mt7߅˯qdUFzF]޻$_;rxQa~"6 [7oxl7Grك`|AedS\2caLVTܲrӻX VH=~𑵫W.vϦZSr, nH>Cyo"Zr#(C3s;NRq KXA> !'؅|>ahD"ȵ&qqolYGa pS4˽=2vRC$Azr0>dՂa\.sg|B$0,1s8P +>E"aHHU*ecS˩ǝNJP*8>qwTfFJB`X2J\fˠi뉠(֋1 1Z<05JP 1Xd>Wx hHUvt fLvvHc}%=5P. EQBi2EJyW28i?xaagnXfB@jTM]4u\9A OO~{ˌk"u)Y*'fMaNFjՊήC>ӳST3^}M.' ^0l5p|Lelՙ>\`3 M3)x?|A&V[.☇0 F;70p=w&mcc gvtvX9Ba2 #$IF#Plru\*Mt8 X EP0ST hJX0ÑH`$Id‚H$λk~nͩa >eqAJ yM:\^XZ\ZUʒ4]D,#"|IL$h p$'xl  Gj|‚<6'e{UZmЋ HqEEbdB'AiWl 7cO$v[S[q:>;D"';ޝ˗}ym> tZfx he}+&9Vz};v?QYQT2@h9ƓLo*G[$cA}n^g;S E1t9ꎄbBEB"Uʚ޾ʪSs0lήx<>22:088i;354x*X$Jmc~쬀r4 IDAT VWg7,Wdԟ!Ngl@ F(OzNhp @qD"#P 4 AB!==}Q\&ڱkO_ʪK#Qu8:E4}ӟv@ X>j8se#VKDM]bjfa|ZovbYA r/\hkn9}˗pVmJR0ɏc_?x 0$W G<>plq'[=$!0L*؉ʞ޾}.+[` ȃ;pq.'I5um{ȱ<ɞv |6N HҨyoW隺; ͞c'P녶ںںXѠfȗ왝'ᑑ|DvJI%#G}~VV@@-Ʀ5Rt_aY(mjMivE wه-wnͳ8HZ?wC$Hx(C+ 鲦{uP4rT,=m8U*xA`@0xVK r44\H,yh,EQ\Ҳb%&ќ,Zr"Ik1;vc'{jUᑂ#-|skV\f bN465Z|uP|~էbG8a(| &iS]k+EQ]=vX|1$)ZSju:l 6xݳ55%=_`Z4ţIWuu&9M0zFV-,-aץhzxdA.v3Ù=s܊pf_4M x<̇;|N^BOxL5_"FGm;: rZAaFGmÚu]6.kWZf,{lj9uL-[rr M8ep)3KnrD2r&KgKq`0 SdMFcEyYFްn% xnD֝B$wAFS$i:;;+'+ka^g9>30 ZK͝t"ٺɠ,(M7MQo\X HjjJjjKukV}ᘗ0nt-> cnrNI"u}aX.<HF(:YufϑHt;w; m}b p?cU~AG8I?KM_a<8EIb?G}oc|qppp\=\<$ع6kMw^ZRhӿY5V޽ee& 9 u˦/#O\+6M3-C.[XhRQ/Kw{j3ouԟjGǞyk|ߧ#*k>r2UXV/yJF1vʌY~0 X& [R*kvc4:-MӹY_57 xb6Fr\&C$HC!R9ij0L$ P#`U-&&D"V*(ÑQXpW6]')yWS+gƨ06&Ap(F0X'II w0nDIE PJ4<('dY|>H&AUJ@@Ӵ|ED"Zh(r:]hX,V)((aeT |~۝H$D"FˉaHD>뢯~wKqƒVI K~:pŬ?f?iVZp =f8np//_m6iN$.yx}3Bb6I$[Q4aٻh40 (M<\nTyώIEE>\f0*on{B?SKx`%w>08aL&[vu~^;vB!SMwޡT*OTV9.b5 gЯDϛMFXAZnt\驩֯C,|>ÆHŗ^v{ر AmpݞW-c 1 J0^4)KOە/d3jrpp\c~[Ο_jeVf&"c?54}-n  1 `)f45{PI ɒ0 OgZHQ д@a`ҜE!x4Ð;^o{%ir'4ख़K5%+숇設!yFTgp8\Wfai\n訍 `tTkIAJ$)eFɪGM]푇.+N*vD#d|cea aGz$MӬL*ɮv<ʌFzlb'`+@aASO<6g3"PdCѶwM~:LJ4:aܥɼ6v_y?Ou☇D"={ tvv? FdUuCYbںuLAQ`&:K,7U^ٯ<)˟x;BA x8 89tZ7ǰ|Lpuߞ-N7|틫~zJ#%Zi^&{4?vtd{g^:O%ѤČ ݋tVL|gS™ Trמ}-[YPTq:ޝ{ KKz1;۾X$^rS*$'~ чV.ظ~- ÿË:ffldW#AX E"U1>>)ģؒ"M!3\a.~-O$FFFOVVWU+㋆3{=0 FW_{{}8az^{G?/7FWKgH$ZZZ].P$ljj yEQ}Y:RXA0}I*iWDK X+Yˠi:%ŲhI,0dAiɕe zO?WӀxfFp<'V K/C6>1q؉E׮}a1m /O]*_aIYq gX81O,yH<?~L8mXp0PV׬^~`Vb'Ɇ3֯]c2pӋ-+˾^-##/$M%:> CK_*I'G/=1eV*rpp?cB4EQ[7ߥV}WyZs EE7y`@Ja"ͦT*b6[?0nb{IqѤX904MCJq ,v6ifjyd[>?eMم~'ץiuPuI MٍbLe:z]n)yu37Щ KJ,mh&eL&Z:Us=[Ǘ.☇q/,)9u6>- ʀrm>lBtj5<9Kg}{' m?ٸn]M]}A~޶G6u=}xp8K/gݳus_׾EJ''fАbsO'|U+ymR}aۣ<|᧟zlS`!IJMwN~^WST>%FFmfww@ Xr& GF};'rזwV4MGQwB[ܜ0 J.BS1c;@JSb!;C'[F/=`0\h\1_,L=x-T賝#fXoo+bB^ަ=аox(cwOVD Xȸ0LMm]MMysZ&p"77\q̌{=;e7ʤI'C!Dx:㑑e(.Wk[6_z|  ===2Rt  T^V%C1"z}mm;b(.66.6gkWT*N:pqԔQ(@( Zb8 !?JO-Q#z$< dB2>,[S[WZV^0iR ET*B-A{0 6j#\\q{$bP;c4M3 a@@8Mxn1K8A`r,jGi|>|kO̚9tVa]qb~3B垦,_as%e}[%DJg%-&iF.L) :"SW{A$7C n#U5J&O1}jVAB[W0LWW7I'4M2""jojZήAw ";+3h=E Ӯ'>l:rIƺA‘\&gYb^ߦ\SZv>0E~(6er͛ϝE74dYi\2|@"='~su T{J3ðą>G{_BAcrzkj|;$ _3sbϼ XV.u:xw:] Ð$T*D"-{fXrB!DVi 0R1N P[Wle)IP(d;HA.X$ gpp^iX,83[QHB!*H bHR0j2O0r84E$AJq*T 8+ La˲522+q3M]\ JY. V Bn6PD*ZyxaWWc>$h`|6Pg#o$IRs醷8>pDQQFA×7T*ǷIQTLt@`ЇC3ŋŢcѨ#B{Z+>I1N i0%o%VQeK!hc(B0 2$e, A=K  r,A>S :r, EGs9K(B!^V tӦN9no;@bq:1K iys?gMݦ*JIC wbO^5Z gAہvD! z}nv bI]]!f)ȧ+WL:3x-g؀MCu;yVmŽȴ)"ҋnSg0<A14C4M/\0_$Qi4Fn;B>m* bkrso'72"wђ,N<4noƦ&֯Y5g,XTtnGQ^bb]˕4E/[h9QO>V=`:7F7s>  Q'жs[2rbP(!Kd rK(B!yA2t(&l1/[X&畞?%ֶSg~g" z>{D`0HH;ߒHmǎTʂI#nn4=uvuڻGӟcxO}c7pq)٠K\hj9"Q)KPaNxGAX|gWs%sg6F<755K%҄/hhW IDATtA/^_Eŕ7^}1fX.+aOo" /?bI@ p= 2YsKiSFp&( NqR,թLL1; PO<ϷwtLKM5s jmimKKMrf+0 KMN~/\|paROsfͰXm/ mE^/4@LUUx}-i)~#%a6}J""o?mA x^hmk?uLlt ry]כ|bB岲2fΘVyfSZͥK υB!L BL(>.wZ3rnwDRV\1{ \.X,RFd6oj4XP+!Ćn$lڇaxdz HZ.Ӧ9*7zmp7qjUјy _(BơP(wƦI[Ju .>.^`XaI'=-UPЃ_֎aXNNɓU*ռ9J|8{>)4l}ٳfu~!R9_-om^p T=} ~0Ϙ>uu ##aֲC'Ej}ʲ{??4FF? A/`!EQ-5c*kMϜ9]*;M~ڔɁ`pE lv ǒh&I⫏=0V]rV%0N#JH :eJX h4jNj'~bJCTy?@jO3 JÄimFuYel=6&f+AX8DQԴ)ontVFg?q0J5sƴbQnN È)LMINMIj!"+3#+. C# _ '\!nJΛ; AqX rJrՊez | AdT͛(A|@}>_SsatTTtCPCU(B!eNHD76655_;w8쎺֯0,6&|w߿cAPCEM6URJmo;ol \_,.6yq`AMJz*ՒE bGKxKϗ7P^8oX,v8gϖttvs礦$$@Q(A@)p\&*rqe"-j6}מ}6}~`o{ ttqļyg) A(ڹko[GH|o&yb p/Qd<;Ϝ8aN!߸W_gF,oټ):*֛" 6>ݧ" {zG5^rRS $IYGiq}OmFc$Z 26`!jݷJ\|T"q;w}GM\V~lIYjjjbB%j1A(r834x|ҫox=>p}fUE&x}^4 ȸg*w|5 qH|E׭V233bqrbbQ.8cccxr @e(Hz֯-+m-i%%&@D*! ў"e;^RE|_˲~_ʁ!]tH$7_02(ЎUUޮnV#,V(jʔ\~K+eaIX,љDA.hme8͞9CTz:m4J{)JB)u|^ 7|_6:xl6Rfq@Ld;v ;;3B^ZݣR*ϛH?ufU+tw$؇,vvu+Ԕd$ 02tM \tXRbEQzA>u2 _o1,dś6n=9))9)=aXf5jK$%?xC7Z|  AA.C  ],A>SBx~ 0gnC0>2m`eّ0`0A^ߧ|;>XݦBBoXڠwpxֱ/AKOoߧM&s0uO/תּ¦1,da 酿]({x9SZ*\3φ_v=IJ[|t߯ .o}mՆ>>"wdbtsk @$&.Z0E'UW!Kϛ?qMz_:Ro38tƠ˿=1smǝQ?ECށktVh.}Ŋr8Oww˂ \TqM%&$4^09}[>=ҠqKG~6;O:S 5N4L-*9,d"O͹\QYQy%..6h׃Bb[lDj,V+Y $I5B!D7Zh竪k{HHMMSBL~}>J)L4 sA<8gL04R\I\@i}.Ĩ Y쳷nY?f N+7';1!^v7)/)+?o;:[HˤB͞p8lvL&͟|{GfD=Rd555wvu,+r# }DQUuM0ܻN1}Dp8UW{<ZƖ6gLά6Ԕ?FPm4M|/2 R32.9B9rOW5 6IetR9z|ãP(TS[OӔ.i; :|+C!&5%97'6epڹ.MZ˵xFN j$bZmFcRiNVfI)~kRIVfKӦL- $J7K8DQTvvNAQB0 B,Jt||fxz_H޾w2[,@d2OtNd63, Xm^aپNjzzFliYCG<^oGgמ}ZZF{W^:n>/s@]OvngEI{{ a| _~ǔot-eTnBm)))9U$Ӥms%c˰.]޾sW__UMvqqʫ'OC.׶z_wp[{vW^oxNuXJwR{8z 9j~e[8^r q :2a ǚB(|=gzp΋gڂ~^Vltx^9Bs M hhl!**pࡣng`tfhϧը?Ā٠[N`N.gU o Pd¬̌|Bh4,[17;K]ZfgeO~*A HfΘE"qRl8.ٵ~ݚ(6r{txb-Ϊ3)  I'f3 KƺkcGʄM+;%b _V<eY>שu]hyɆh VgIO9yLNv}:]ήp{Zj Ea zȱ$n{ܾgm{=s-nв6Xy7n*ގXq^t|{ρGx4ժ`(4b5`b)EL!"pf(%EJ$`!0 ' oC5 pBY2`xlZj40LH,I2"I\&ࠩ/BQ˲fp` $I*rf߱s)ΟgX_z Fr9əUZ.Ryng 1COR#Hik5$I cBl6{FzHD+D2Pbð;Z lv[lLJ(*';r啱q\T o9A`&P,^8erAtZ,d'{zcll -?i >jmҨ_yP(tZ$%AvRNXֶ~mk[0,!HBBxmB.#)~lA&@A,Ko*7~1?Լokp.B) V?0qB<ÃSx 1a$%"C-TY<"-<}!y | @ (yyq#ރa.˳3*  Rυ$IQ}z˗ U(P7aW) A<^T* E 'Dq˲gKJRSS&R{AJ2|0D p;as,MC @0<&hZmWOO8H$]n < _!@Jn*>w(XY#It =8a78M#E}q17I IP( B!a:rE ?00 jwuuP-OrE 1LxD&-?74674644AJjKJ NwF bc!++f 0ukoXmv[Cc+ok;n7&BJ]貙mڔ Rpu&A0L zmN|' 疗_%bN:rZ ?::A?]|`1_{c, 4MfwttUV^olNA7.xf!)>[***+**2e2knni^xJץi ]U ;ǎ(>Wj6[/]Ednv|Lq]}cTTd|\X,NJ/>WZ~Rf4FƐ$) (:y?..NTdg-'OWT\!-%NO 8 zr"qs;1._FũIE/uXR#)* ݽ$-L]|#VĐ|]PT9sko~ `eRifF:2=s̢iZ*: .'%&5g,M;𝛃&SVFL._jE\ll}Cө isGtNvVx1 J%*ْ+&t$AO\&3-gKzz2ryJrrm]CrL~(#a.O:e8 BZz<}iN]`ZcJH[Mފ:?99Ӏ$_+l*5`4?8mdLfJR:~n2@цI11 OGDDD#R0I.z++_Ȗc|D#g/^8ðY3罌Ȋʫ%e2yRb&9>666& v/_vF*޾+-uC ;:Η_3{faosSrD ^>{f*|ܛ%+3$ܙ.t2) ΖJ%ҩS Cŗ.W??㏦$F5׏8}G⹟ܭmL\0Jem](4I&[ U5zч?^e(Z557ơ5X c?wݠb|>}5^AzpSrRg+-gY-fG4qB͍7,˚̖Ӧx1 P R*-PT_U24';^ߢѨ׬ZVq|b^T&Iz,Ǡ A3o-Cp$FE _.žvۯO9}v ;^^HVzӶ|J5w?c,~kCmYi޾'ֈ u ~lハw 1@F`!˲Μ=z [xD"nmm{wM>pVfƗMXlϯ>&c+xcUK9Jf-I8=c919b ʾb,CrS$yƹgWdJñd~=YpuY3%+ Dc;ҞzkgKO2^! dzL)yBI$pvo4ƪ cX&/D"R%a/8l~3!i^Nфeqr{} Nh$ΨHiE2&CyN ~V*W8uɕb/H|1? nu=&S(iR)A6a\EӴ7[,^BATDGEa8vC !t=% IؘX9K7N3b,+A*`!u ndp (_(OttQ*bcDXᬼu}ںz\Gb;<"hy IDATT(, p22 ȗ qqR500pRKoooZj\&x]˖..=Ahiks> ؋mm&EdEI^Ű5R +K;b̉Qf&$8* $U3#dMdr7~0j!nuFc\|d``P$ϝ3kcڊN\t9#F~REeSSˠ|ĤĹg`9C6J$$ +VU!z}'lܰnĒ/VcmqlWY$]2j]NKMIL$ a(B'ikk͟8Q"dRYOoVfBpzq Bh1k+{ޗp,cfx3 $`kZiBvALmEoeYGB.*NZ=3':Q)$细6(қq+O& S&bpwPOO͟?kIZOƎfKtQ`u։5ՃF;gΞ;w0`D9h+P ٰv ˱,P|r=Cqq6],TJeJrRB+;phKʫך[ڼ)%%БcoI ٰ+.ɲ;[fbs;:j{Ԕ==}m\v/]~Λ3;-`~v <_ %<{::{p7yP c@2>$rcӧNP45y|:~ R"hfmcMDd2.9B\~kEdlTKTZiZtXBdEg}uГ39IQ!6thBw97D,Veq\cMK/Etuukq =ݽ1AAkj_/ˣ-m# wt?|he-ˈUVY굪s%e[6s7AX,NMIHb s/W~s5w1Zc4F2,òlx$2&`!EQQQ>/0 ˖$bW0"9, `8o1BiB;tUieig4 %L!;M2A qTi0ݶa@{R,J墸mYQs(v]>.dZl鷿)F{&dR.23+\knnyjAQf 1LwOC!<*L&kmoDH2%9 qL:`RRR"U0LE  )#vq<*ȲlUu9yae4#C hR)HV-VEQ2j%1ðVr:S?0q 8xƦO| {FxnYeٓO{ 7ݗ@QTRbBqv0|ú17UW3aPIJ7o)DAϼ"1aL.jk0Ue7{ 稆ح&ᒺaG'h.i;u>gΌIԄiVQ4iVbV~L8XB9l;u4[r P~OꚊ^CR!b2coR;hVLCuE]]iScWS;en2-"zS3y4Y>LQz. 8x׷;xXT>ЃZ&)1vy}}S&OV*}Um]t̺+˔\N.BsϾ;wK$Ԩ(#I{GEF;C6>bR"]{<08hڼiF6F NwN&)2BA8+Uol͡( qX\y_]S[hHֻ{2)LNJeYreGcb?AŞq{}Y' 'c;ERnB+r7FٸbyǮ5W ==#ȗZHB5UPȿ抒- f'g4ȝLfTsǂ k(B 0$n( SHj m g$@w#!j5˖,NE1CgMfGgG A11z,H;~"9)`Rml㸢SgΗsN% 8NΞ535%y|U$''\T@ѩ3W^KH_|q[.#HPX8IJ O:4j 'L)w==}}/[lS:T*V,[ v*); rszpFs˰ M7łIr/>/7e4q^;vfϘ6P~ &O%D BLUMmVV&b흶Mקb!9]|nJ%B?p-$ǫժ;}ݾk`!(B!RRRRIuMힽcbqHg&\HnXBBק BAEy>\7<!8$I$ɲcUU5*pPrhF A8.M0"[g9vDB0#\ 8a8>yy w$ "|xA |8hqAq Eoǩ'X8.wtaJ$7= zhc(Đ$AQԇg'IqyA(z}|[ߔJ$M-;' ]?F {9oRO~t܁}dK {z!'EA1Lwo/qD"1I^p:iQ`0DQI^o,6?|;Ө䳄,d""*lvS)$EaR*GΈ=@kjj_l6;I'OzO$Q]S{Б~X~Zm˗͜>ȱºo}$A<~*;+ vqZ통MBQTktj+gΘ&\fՊs%eE}Of74Mc4- G?T^&Vvyڼ_^oJ|p~ )Aw0 ˟`2n{l_?嶭RԨ6IX.gKAOA^0+q_ I⥿0N (5cIgKJ/\sen=ޙ?!ԙbc;{rs[v83 a .vfϝ3K*N->[xiR$7'\ j4 ϕd6~SD:O:Bh]/O5N8b]RZyLh:|xd!+## 5N\OQT||BU|$\~nPיa;@SKKWO7@_b!m{|t+W-#b:sX޾̌iSڽ[U,7\A33mdL:uJAْ`0t:}>SeRIgK?3I%D4Y,ΑJ%rbc WDa86mDy9Wz{bRSS|>kU.>w.bPH,/_9~{(tBt}Pg"(=-@˰jdUB! 9. kcT*C 2X$j$IL V RB |xߧ$I|UpR0OF;(S1 Yb[(-+w{j?^X7یiSFʐ)~hs]]CgWw啫+-BJ˔JŃW*;+-+>桃 !  2tMRtˤ'&&0v#CBpP(R<ϓ$j(XF ;@ 2aG'r죧8ᷛZp8~$;.Jͯ74vuu_XjeFzZxY};qB^JJ2ES`SU7""R6n!@7\ ˲MnaE"\_AKH? Ib-}jJJjj7޷~9_~b>esGG畫XU(r8i;B!hJu@ P_ HrFte`cS3IéʄJCPk[{{GC^W^?VX4tj[ϴ|`J#T1wz.>.vbX@ fΜi\.H\y~pf<}8 W%qOOox4M%g|V\>qB^V;cLN1k1vhV0ǣss*0oNsמ}lq"?ð u#ykooOeM]ToEņpzl/Ȥ訨,8¢y;:'dˤ N{P3'%9kEgW,_2!#?t8+3SPժW?p( ٔ*Jp;v}K-4D4MTޱǎ[8ntT$M  EU #G8eXLFӤA~;ws֌iI 8 c Rrтw/MQ#"J._{-AA~ZEQ xD.~w.[݇4 YCuA1ﶴ.Y`@=C!7Mqp݀8K%a߾aU*$od^f|> J  @ӴL*|v]P8.ԗdˆhw8\.7sEe2EZm .Jba6cpJ%$I"xl6`Yl0> E"aYd6$)JaYfqKdA`,˹^ Ci7"s9lHT*ܻ(n{ * MX6dRX,y8a0HbK_ cHJR a/vC2vn\aT*%a,fc LJ8@hVT>!:>2L&%IeYɤgyrU/xNZ,^ahX,t.y$yf\nE2`d#a A6E${ $q[&YpGm DD$  n-@ q|~an7ܧJbh1 wond|Bļ9N=|$LLLT?eEg;PʐOw0.@ d<#ye[ :yMQ@$*ٹ}m@ p 2aY[RىagIК?fϚ1Zێn/<}fՊeR@ ;Xq+**0PX9YjaW~m`钅zN[%t^/>.V@ "2!"++8T*H87wɓD,vFeYe@@`i0D 3RoIwЁb 6@ [ ,N ϙ=$ !@ [ ,NAi(  @ 5g e|^MCD^H[{GCChwA `A!<ϛL恁ZS[k^l6 Xm{nK!2>?! b1 ;ҧ;v;.0;vm)@'P`A!`v]{6o,?- 8_*}NEOx-@ .B'8gfGODEE7na)?~ꉄFq̈́@  ,AH2< "2wo@pg6[^˲AAf,gZ(|#@,8F1 ںXR0LAiE o4DDh4@ P`A!75,*yu M1DUm)@'P`A!ALɞ=<;kBvքb@* @  X@ _3p8tQP|r"˰,E}]V pr^/bHyxqD_!S$q|G.x<(Fof>p/PN@DC8XFeYUj\8d{{{\.FbO'NLL,8}˖,^8Y/,XxVZL8r||]]Ni?X,Foږv@=b獷[b׮^x**RSFqz[[[#""/&s^g,)ϞpZB1FWsw< ~_d/k 2aY >a@˔Jop3{&rcnVvϜ=;)gIqz_uMMrRFLz'NwuwGO|/]{>@r'̚1M,Z;^ﴚυ`{Ggm]__V?iFgo*/{zm[_(Nb='%s'=q>{B՜.ׅ%ikWD_*d"LO'IŒm5cR8 ~q)zA8kji),,8eġ[Cyt ‘ֶI9l¢33OD_,&=5yGUTV-cOtJ |~~ڔ$Z-&A" 1Q<jj;wKҔ{a*JCj? σK-.5 Fd<;M5bEhùum1NI 񅧋23Ɛ^Qe@ P`A!(hÞ5Am6{A6ƳXc,Y)*4]a< .#a!!’EPAQñ1nXFQT;dh48t` WTV;qrrD;<\ZV^]]M4]^Qsg&aX PEQ1 @X;w`mmٰn_GggUM+Ow\qmI"e/(4M  `.O8.877T*,VԨ_8vT|\f/+\8nrr"]71:rΝ0\V:jo(>xLkC$Ϟ9}؉v ԊUNSܬ]glliYj:erM]ݍ `8u+ AALLΝXt` arnbN֬X>XEҟ-(U*eeeU^wԔd-(>{M&'%ֽ׿ED ~y]VZLVAQTi?dX=pT*, z9 D-@=",r, >S*^Y]־q:Ǻ{{+hVt|} Mon~?`aYF3)'W(,xGB:r졍vGdf/YЮ{;q\1{B%G|Z`vx1*$:t>,"<<"ɚƟNR"bı5fϿe0؃7X@I>_^dYZYYĉX,2Rn *|k`{䑀|>j!oقa؍KBzP ,՝>S\ZV.I-^"aAAzn)PJp;H|]`WSS{L1En`mX,jim;piz̚)|i mhh*(<]]SR*p X$' ՝(JRTT|3&:J"Dut.(YtxӃ6RD5459KE BB,x'dnw89Y }>¢ Sr'4-;y+>PTh:}FR:ήޓ*+~zY=}i_!- (i_C|`:]Aq`0?Z|Ǝz/\U0eM\i)*3800+璓nS2T O??``Μ}쉧hAaWx*w80 s2S"O2y94M.zG2c˖em7 v{V"5oz3̷+Vo\y$f{xw:.kģ4M=ϚOWgWc?|zo۸dzϯY2;kLg[>'~x||#nr;y/P_z3D4}Ϫb0gM|O^WY}+aL{N,)IϷmiX wPyEo~ a7?ɮa4˶;x0m2;wobv`eGܷnʹ)H4&}ْEs칐;(  ò&L4I$/^'32Cɉ/y|0p:0 |--VMyz n4D|>_[{fWպ vGcsZ7i KC-mfE*12\cC-%$E`5qa9nr*:HM ::7 ;:- `P]q`eֶٖZ}i]@nq8R# -²l@o_@VLjT@F "{z{FsC[}>_GglXV$IbZV^òܕr@vF$ɘ([Z[-NYҪrh2LAC2 jZ=w!ӧ=<;vLJxAW_y`َoim߷6<,%X6Ñ5!cά .o_IKWgO/5~XNM3"ŵ77E8@NjY^%A{nfn_3m^VwXbhkk[B|^8xтĄ:0W]Tr=~B"L4߯ف6ehKu CK:]jJ5;:?? xmjn/(Xr55%e xk_4uJO JaY66&fՊ'N巴 '",,<,eٓ N&&BCxΞwĔ>{EEFΜ><Ųa# ,Ah f8֫ʮ͖%$k5?(!>l2ؽ'88xŲ%uuyDD/^eɓ&fge^*\ZVx򎝐dSLj5-q\’\+-{ϖΙ5SR7&%&aQZV`ޜʪ¢3VF=olLVS[_Ph6[i5sgkjaNXr" 'L:m2wӧ6/+|#9)iD|CUO3IW׊BA`565gefj\ #P`A'<N-9)QDB̌ή+ ϿEsf\l25jL&]bn؉lXztAIQs'Ξ5vtt 'd=T~rR!"K͏(oI,2@q-=4=]5kUԋ{jo"fhw\~fIԱ1ڳ/6&j{[^޻nz " BQԪ3ӯ\-}?\"T(ap\rFX6ghɈYrlyoaȈ?ukɚ0ӪAEr?U+DG'V!כ=!311j4aتKvЫʥeK+oɴ))z̜>g$RPXcb}G@C7[ntQqDxaX]u?0ɲy>q|ˇ;3kƴĄɹP*8N+ Ʉ쑠̚1=.6G7=h]Vaݽ$E:N^+_HÑqN a/o/͝4jd Cʪ+mFFr\kl^"o߱;(0 X aq1C-# p ._̻A`ʐ 0{<ޤccJP. p RiwcPVf_;t鸞v[ZۼW[,Yi0lm=!XntW6?& >_yMStnڸ^bcv$|VcGo /Dufe4ii7mvwuEeM8UPx+ E䤲ӧR4 x+@.Ξm.44$6&$1^E# 1}GD[es/l\NR^Z9bY3׿_0d > IDAT74r'O"IjlnvoV)bt/s#'Hy0_6[(Adg]znj̞)I;:kwKf3M=}[ UI4Ҷ&c`wњCZ?- q 2!\9N uyGW E0 C1 .q nB 6u&ˢ:ЛY "Q/*+P#%kD  nDyFvcôH@QTx<| I( @)2b]|/NĄ% `c2GX=vn<=mUC1 1#x_ =IѴ,d;-^Ѩ_a[Z$/!", fkmkW*Mb[(h0o$:*R*mhhN AɹEysf i(mv@Y>A1 HOIJBQ J_3bm}CfzX,J%F0 _jJ)b"&w|8G gNuDr_Dز piJD*t^:(Ӻ``АPn+0Ldz/^,1nReH$UVUY,VURr$AǬ$JeO_dJZB_pnxw#Œw(|E*(jDvjK#"s4SJ)T##%8}5]6ӧW-Y ur? bqB|ܡ#G=Ocs HR*1 DFH d(7\w"p$W[WQo x!0yğ>пE V_`X<顷bׯ{г~fE"VZ!xI'K}ET>ᾜ,R?V\x{׬O6} Db秽B~+Lt>hljI$⥋~}ǎ]{f͘cx'qLV$?y}s紷wtuu EM:e >SA/ aؤٍO>9YdyTr.+4$?zA ;vyI˗-XxOصgcׯ .z q~8q˲Fc'MY)`gw8ǃAWA#hвYpSQY^>R+az{` ba@vwWZVGǰa}>6b.p&er<74~5W&'%^ +03%zG"WTT$'eW .¥+W P ðڂ¢ F>Slw8r/ۋ.jo2J`Y5.6fpc# ,(ŢcOo1ibaVlɢonO݃FYtC&XdŲ%߾UcC܉s'޴X,~{}x>S#>=(*!>HrgHINRѼ( 82_'㮕=~.\x<^!4p 2|Eg?a;T*9'df$a?xU͙=sƴmxK :Ƙ,+WK9]z>> Tk'-J7eֶv% R֬64M&ӵҲa"_sjzb{̺].6;###fL 9{zʫXct8_4NB`X{ꭟlt~cWPPӡ ,8EѸؘXD|Z]{m}eR)òwIg=! A8x$I 6P, <{^׋8A(,v{pcAQ$GK(rJi A^Cnc9 $ ./pQ G1;Gg%*molQdㅐeY3s|a9s=Ix^A"| eBe>q( R)CCC=>c6[Z[& (!.0<R5yxpE1,˓Ʋ|>rx|-|} 3XȲwA&F.u$IÁㄐ2 EPc{U!13.X#/PEۭpW 0 lv[OolQU$A8]C=RyήfEUuNVֆ* ߷?]zd Ü9{n}n`?__~`2zgk qׯuUΟ7\"/_8wDj$6Zs~3_NuݿQ4C,Sۡn;`^BTcw;k\<+$Ě@!PP&h[;;*%qSwjko?w`PήgK O{<sZl6VBՌ^oWK0  mvɓEF~q#>q֏wtvuuH%iSlzhcwOϞ}Kʽ^Zw̌l/8\=ivTx=fc'o\DBd쵿?;_?B.ӱmƮkeY2.^xcҲY3+%&d8qRvRXhl6wޫpߚ{#K{r²6T^(h8>0lx[7}!3#ZiYW.mmL䄌˖( e8tdgՊy9+q3#o˦̍!HZ=bLƦCG&oaRni6[:R|h.bh5#8k=J.=~"1>'O?aw8;pbV(Ο1m߁<|ꇳ#⦊Kn1JFr²lUuNysT*UGgj)/<2fΜ.!E ϙ5s(rRߗѸc%N{p^~apCI~[Z<n;4${=p2T~!"BHT2p?qbЕYiIx~iu^KB5_o Պ^} CW`XQ:ɈYmjꗿx/ ̛O~>mll~wtF3.\4$wRΒEÕq3*Qҳg_|塍#{o򋿿1A ϛ1,ٳL*;13=؋K^{n~F# 1SKϷuYOpTExsmrVHyIGAԌF.mpymm ìX$-5u) n;"<2 ( 'fg9S|6%98 SRJ._9x8/11^q-FGFJg11{D,NOMɤI N")\&:9W,%'&/~ѝh(/vW[uJ*T#2;}E Ag;^8LZ& yCr_a}3=\>1zvO?IQdppPzXh0|[ xz]IfL۽mhj啕u >GQd-"A AmMy5lVID`Es]x_}eOjN(  5JFɑT(N)I' m6;}S'OއS{B&2Ϟk0UʑEkof5~x9-'at'N icnz?wH$ ԇ587J$2[$A74.> 1>n%2jح'CR8_zǎXK]|uMF^r#G\6}1 եj"# bhE ôw6[mήH=0VT~h=ΏHMCu&@Vxzk|Z "@eK" kk ,xC\nB.Ic) n8(~p/ _L<\!,GQ upCr]"dQɱw[Nkd"@Qe3uJ E(]77JM F|~g ڏ8nj8 NUVV-INN;< g=!BZ@F}uRrood ÔWTM٤_tRna7qXJ91 ""(\}|-<χHXEege&&ĻݞZ&6Bo_iw=/xu-K.߿ؘWYΎf#-"z:,<([<^s]37K_4ﻷH^YOOח ii8`5 @/˚jz;Z, h,uBd V>C!(DzG,mzhY/dgolnُZVQyxy HGc9А ; B@!>jzF~ZD"xrcnϜ=r*$blj)xT ɞUbbJ%&6}df@^IeG6j19v7ﴻ@M=iኍS5Q]ѥ!"YZZ>xHP (61 1CnFQTHpPCcsoonVWI j&Yr qD,C("Wdɰ, F)qa)?-Nf,O,8 A`dJhh4$rYVf-V\.WyE\&J%jRV_*)~FysN5LfEh`z|xRKϞOXѩԌjтy]+M#m'].wWwOGGghHT" >{mln 8.11'k%bu-~ڛLvZeL8ptW(X{XmHliBbCCB0 󛐙>b5zhjjnq8E LLLXrsSiT78 aYf+(.P؜p?9=P_%O yn'_xV8 ֯[֏g bx\d_~%8(pIuؘؘVCQt)Ɨ7 :ɄhΖfT"9cZtT$@$ϝCGV._6wLD"ΝsyG޿޴ں"qI I:\l"p5emM+t =~6]$!UZV'-G|t<',\GClطcbhޜB K[olv ECC.2BY *Č S*U_Oߠ(:1' ð{Vy&Nx}W lh vOZ4yB0ݶ*O'I<,ZMI^aӜ#hjrR{{}A0an)!.h4޳o;[HʌP59w]{̳3gL[Mi3&=#?{Gu鳽kw{/,[^RInzn{_%@ ӱqqUzkWN1$c06~{tf̙g9'1!3#O;q)I djܺh1F (9E1=mS'Q |v|V+2Ş\\RyiE"a q>aE"P{݉= hdy^Xh% APs76>׿#|~Ry8x{G𧗞˰Hsfy>,CWzH&Y&M &t?bB wy|%IvV RiًژLz]z'23ҿu8Xu\&" sqt.D&L0a>'." " 憅޾'N]KB--m kNLNw{ôUv9to$4]SWsW_תkj/9P,UTV,˵ՏyMB ze7!V;q4m|tɄaXnvB! pZY">oG.'Us" sAT*]xJܳwbEdúJXyկ ($''%~Ƀ&_Ft[1N+]1^*HƥjWPrRj̺'2}}/pv8C%OOO[8`q- q<e"lGE M`kܳ6C{Sl:r,::x0!>v{xںNT*E$|xw׭yzɧݾe;۷n?"զk!h7؇PTb8xvo߲y 4}lB.oLINj]/ؾu3 < wvKFz}{Rdl|tvuk4;';ExONG~k4jeY0e٧ylu+}3V~)˱y99|QF׷wE"ۋW#Gy>*ꖝ7#.,jx{&3?d>Zl!8{qڝ2h8j[g*ו 9)D!vWND[*Z̞D*1]=*RT@T r2!$708+'?Zl?xPU4r==|}CH;[]_KHShivll|>,_d|ͷy?Ͽ!0F*Tm۔ya8ʲU ?[^p\Ɖc=+6[1ygY(jdhkyoln>u800( QbBOhjJEQ4p8^V^Yh׳|OWQUeG(WV=y=NKD~;;mq9]3-eR;oz**O>iy'fm|oϾ9R0zyz8;3mnW^x'֭-i _|嵖>cOL'ô'ޣU$l`1ql/<[UT<J$_h4d'&s, -n߸aR\|^!lW(K "IFBmfBv;9O AmḰ>8`5fh,~+p߷FDz+WJxQLFuYsfظ~T"IJLսu&B?aA`0qNP{>yt(!O-߮.gϱ}/xrj*iuھb:KT76R90 AA+-JO>lQⴴT-ؘ FxV(}7"o߾>A _7G'N$}X+s'&`FQtphh>IPZږ.)C-ӵUb nQ|,ߐ,8'u;2L)VPi%Q T RN~gU_TK03bCMf5%a?q!i\̋Y8) ؘuwoB>oMʼܜZ( 5JBKT")r$QS_ZZ5Ip򀢨X,;~INxgWעܾ˽~phhX$IDv3R͉CYѲۋ0Mr*‘4úcP82ahGmIRp! =##fwFFK%F1џT]άXm} (e\m^d_B.oY`Na8˒2#ޔ3~掠z1Q& Ca>"hjjh4446sa;CltxF7mdvPTA D,>O(q\k[{Sk۶-GFG/]V)SRlڸtI{T(pgYN&tr ߷>obr*:* p\Fϝd2037O}Cӱ'\&[Rx(:NAH-5iii)C s0*A]lZ ::;# "~ѧܜ j:rluaY0LcskM]-*,/Z5Ut^QW;SMsjlvdj/ ;ov= =+HT*WqūV~r¬Xt\  sxktBL?X``Sg~ttvN1Aٙ'N翞_~횾ʳ7m(XwVqúSK+y鴷r3awt>ܮܜuk].{0 ðZ U5<++3cbR+$щOL_[ZR>x**[Z[9W*6nXK|CVnJ)&IdZΧ(ȂhΦ Ә3b1aR9)NdKbSu-)?4Snl|}ڋbH<(9 PlFkAcjmz$q`VU$-.XTufxdx钢5W]EZtA-t{ܮ kx<޽)^xUiyeYyeјwUkhy^Rn۲Y$0VBFzں5y>r,00 ؙQ11z!-5eFa[LLe啕Famjp=w~tsϿqT"Yjf3566/-_7?np%0βظn8N"DGEdRh'IR0ZJ[Vݞ*dI{GNFNLG A߿w)ˇFFrh4vX,d =;|Agb$p^' )lEiWAVc(4493N3Jus܅@ ݛBĴrSS! NX,F$1!^<==4Dӽ驩bEEF*r:NJ266FDP(4>1i6erYB\a.8)4j5j!NʲĔ$Hd2iCcs !JǍO[,,e(\.\+&p޻Y,t:ggqsJa+GX* bF$JwP{G?^V 3``uvu?_]ja ͰRRQ-7 7z繧1S"ẃyAz7qywU9% u„ su8={INJ޻ժ MXA1lf/9yկ$'%'%^ Y],`;e{\w=Xa„ {&9(o 0,;2:66>%T B}SSB\nwD"ML $s?0XQyr qܗpHÄ 6܀,;84|C:}fdta秦LKJ9zlbrek/Pcɩ)Zwj~c" IDAT}z}]ݟj1O[>:q2XXcc%eY%~Is|~Iij sa|i Xan@xwуC{?p8)*;kk˗.YR4w]W+ccc~eYX3 (~o:q/\ Mͭtv=+"(Sϔ"<_‘eƦjǛyًfi3%sJbҥ62 1 svHL9' W'ȕ*6W)f9M[,gJ<^8[]snI^>˲-u*?**rM4j5qC'Nq8q11[7oj5\ҲNmڰN4WWmJ<=:`^)a ȯߥga\]07 !\U]_zχlިɤ2L6990jIsBn0,{ɤ6O,I.[RL8oTUxG?4W.)5W,_Y$lw&''wlߪT(ΕJɩk}'O9]RaݚukZRW,_v%EZqTߒ5Ic$ e>_[9L a+̍ A$IDEFn\]R xPSPhfqc'z{ǕW}ŗg )q'}%1t 7 044vkV9z<';k"H. r,t1 S*0 *+XE ͇6opyw&L? >Jda\[N "l2`}~1ūWU* j4U+ Zؗ$iEd''%dga&pp:"(** Eu:mB|{9Y0 (i4 JLH`YrEy)IqAoT*`Wm`J$V U+$EDt\$!!^Rq~ʯl>EFJ"*22BHA-FCbB白1 y9xXu8t($²,=: q ggO0W07녰8?p8 7e%Z4}rAhnm0Wvm|_mY [mҽ8BP{G } =#U@9< '7rͭmt.V:p7_ym{ g4OO??Euu|#|14?0_djʴw[<]6܀,{#G}ýnaw=x{5W$۷nݼ*.ě3 pwFF$%>)_ie|ONM]\:Ԗ֕ whȅwGQte|{:GTdo͕CYy~tt7eY㾜f>0k7 -5 s H^NE5ׯcS{Y Mq19to0<^oEeyڒOUULNN/vZz-k׬ںĄLHdXK+bry<>b:90䜰^+K1H34j)>g?}AH *%dU^P<ޯhru$99[.Qu<=-nB.wut8~;fl,}Li~^nCS3˲93k/`ppppx5gf/*EQu N`0(7A:X.%997'Kdnnml$I./6qdE\ RsPr8nvuu$%\βX,2iK SS-m>/B[\H&A:FFGyOJJϛ[d_VAB-;=uf-B1ðؘ޾LyЀ8eab=Xan@`&Igw-[DRyJJL1,::jyJܐ8)d6wfw B`qƫ?ށ H]}/b;>xdt<|tdt `2wJm}#Nt\s\0E [⵲f{&Y7S;U? RaH!"C>ᾒr+Ξ*)xML< 8tyw_|bV7wt~aGO)z=~"pTCbH*76 9X$Jlv8$KS7~<=- Syst9z&G0 u)? eǺmuԏח 1!F p'HTRXl>?*pKkia;a}N?+5u r\fcS30Δ54 ۏ8uL W؈{wf{N׫yG?UW@8z|o^N@kkG}>?{>ljnicm$I89OrjGm?<'Fs6FQ>ZQU Esk,o "RZV![ݾw߁A+<0 ˲]݇cYqɲs{ZjFf|OMۭhji0e2jd[voMJV$M}cM,.Ȳ\fFz4Muuy>/'[.)-ۼiGGGdže2M۶bΔ^B8ltTԶ"Q D"zGFdž̌tJYZ^xuZj A ôwvئVB!+W,pi7]i}~|ԨU+/ %_se=5ˆ(ySHs6wtv緖brX xd+X1F`0N F`D2sXUQu׿YFzjCcSMM=‚)P ?/;; 1˰Eё`pú5 }ȱkUccc]]&pZj q~tIKM]jMi#ccJ'%$lߺAQYRV^[ߠR)oޱ],9SK 3 34<aZX4_TNbn']>wpɚGQq✍ EJrReUMzz{RSclJ\W:&&Z.;$ s3 p8$0lΤχC@!F S 1 0,Kp\3= yPF<3, W(#`Pk@H9ݼq=NB$584 ENuaIIQAVz &cLoOb4]x$1q;)NHnJ$Ƅ .s,7c (UrމT*yw8bP-(qð߲0_07M~Ih@T&%p|b3 *xZEyB#/.A7[v-h;P(ðNbM!BhQnnN62/C!F&0,Ji":^JZGc81oQh4'EQq)at(v{T?D A0@C_87^\HȣEQ")LdnM ;5 劏]N PB!Q__ 07 A.--j5$IVVUGrxܞi))ZZADTTd]CSCSs\lLNե&'?txeUVmnV,[z6**r=w@Hԋ:b΋UDI}IG6n>3ZbF{g'7dkOwX]EIJqTn2gȍJR)\ O.E U/E*+#tZ>fHr\I4 4} X,d:EQJi3S:Z^Ye4?uO5tEK6aHg4]cհGCKqu@J|8%ۀ(Fn0itRMAaZe4 /NRիVT6 A"#+V&'&tZ ]q\YE{|xmXS9#"цuk; P`GgWbB+^ϔeeftZɧv hEw8 j IDATfyeUlL "ͷ츉 %e5uu27~k{R4Ąf >KUg_yr Vb&gj݆[f 34<Ԕ'N\hTTZ:ܜ'O-qLGDnV;c=C68JFKt$)>qT"<4;po5ū.unXba?.)q\@um]ggRر}yӣcٙkp\u'Xjder`V*lMm $'%ظަVR9=mݴa^w"v{B|>"bb)\zeIiT"iHqqC<+46I#MIG I/QJDUwm{G+p΍NuXbt]֠ SRE8LȉVK'RMF/ &XּlTx{zzWZADA"l6{ZjT*Zm+/w{rlݚb$&zal6[VfRy{aAt*GFA2"aj ; ^ *  I&&1&msItNc;ML:IsL;I9ZccA<d]vaYv߽ohsw]X~b5&PHRA^;(,Xgk#RR;V{tsK"zǖ4J4M×CMVLE(sz53E(A4Z_b𑚟7{>f]kmk^e8A轮k|鋅t:3_l =CKb9 $f_QHz/ol۾cBܝ!2 s,5O&r ȵFa5o'{oc}ݓn4lkkQaEQT4:M\.~϶K>fp8|5!D0qUUiϛGa;C;\ݯJ%O>Ȗ͛~sm/KE]ݝY:1.-~I,55SS WgHS6R!kd뜙HH0`,%(V,¬)2N hq qkB%Uv~~hZPXFqܨ뛘`h&%%9jH$D(4:D@bgZ#q?H2)))=:m! 'k2V<nykQs;;ַϺdִ|7r ]瓄 ,daDmz.3#$\oDcN&;N^GRTsKۡZ݇ _wtj;Vw?q$Ɏgef8C?[nBȲҒ[o:?n p[7Hk.Iטd3Xi}mH4g3\c5%ɺ82LV]e5]N"`xjR*(A\VVUj5>G}*b1k5z64 rBjML \ng-H++VM[ Lpc2Cmi ,Xp d'29U`9Wg#ubK_!IP4M3D:͘&88qlwOd @T*ܣz(4n>R*Too`PKQTwn$S@GC;J;^oJo|YB|Ɠ)^Rκ*{ܟP,a }ycՙ78ۜi̚,ƕ%ÝtbI;sO:&;GY4{TN_"AD | 2,+SSGhrQfρ ! B==ݣ))7;;+ISDUqz<^ǻγ]yZ09P-_T(<۵tL&%Pٮb$e,{LF Y^B.ÑEY3r bxUn*\B0; zAZEj(`ϺB4+U3-?B1, ,U078{GNeggF>`k[шMkx<1tۈ%Eh4vLKEED,RO*fd 1w`iidq,b4րɔ=!F&,˓D@\Mw[G\n]P;2L.;o4:cGr&HE*uE ?[XF"\A A9$ Rl/] b#BȜuAAu5O^IENDB`gammaray-2.11.3/docs/manual/images/gammaray-qsgtexture.png000066400000000000000000003724551413310443000235740ustar00rootroot00000000000000PNG  IHDRc$l pHYs+ IDATxw|.;if=,mٴ Q"(nŁ QDADe#7-ЖѽW$Ma YM۴ yxj.=w$ɳS  :  765 a_77ԇu.zx=_;'AۿY,V% __z  Hw[IIR3#P{ 5X(^AAxAA_AA|WAu(^AAסxAA_w+g\sy$ɮ%xCGz7K>n?ܽsR/L&/-+_r7zDz =S}qw;=/;Sؽg߶~0dJ&k٪havMYjuqWW,./%y;2 @pL& ۼ֙a>T6ܽ$АI'A{Ͼw̛sG 孭/Zdd+/XjuRbIjc煋j Ntߢu*"=-GF[n:%՛^5bSNvyҚ[Zr w@ijnmǟWzD.vbJJJϟ/XtςC2~sי3 FctT j o5!Ir?Μ=GDFzٷ1'Nܽw!;+׶c cǗ? ]LƔqcGΜ=4-N˛JÆ;לٷ3 @SSRzc<|'ZoÆ_v),**8@&{.޳f?TIf2=w~o[,W1wEKTֻ>;P(m?Cdt90 ohUwueW^gz|=ttT䆍I$ZBi0ϛsZɕzCOho;={=~B;N!ܾSM&>o]w&&{^h~ݶ]P+Y2o1fڳ7,,{ |/+W%tzZjn:Z#F pT,UN._o?z](|u~;/R/rBeMWՉ >$00`W\'`v௿^X2'V~;Φ6w˭V~{Ͼˑma1.{qfxEo0\Tre32\sk%/৫x㵗ʴμrHX^^UoD"ٵg_IIk+^hVٹkϬ[g .\JMΛ2 6}ZI#G8e7 wZw(((0.6f^yvh2u@*?_@Ր9z<''iW~zÇ t/zqm孭$I}띻u\VkM+oml՗\.';kԅ첧BCBN:awC~y&k2[[hj;rsAϟ/Xq&y[~x՗$N;^A JJ?3p@K,8(h_Epu<85%h4;.H)!lni),*1wگ \c:~kn㯼_;elhlzWL?X,^/*U[sK˰7RS(Lyq*^),* RaQN(;2xNZYUsiZxםnWٳO$N70corzeݮ˅Zξ]ѼaC2zxqul 0jH@p[ysof77~-'mtǶV ]vZ l\rWݖN0L\vm[%&  I:}fnNK{wC >[fgGFF|͆7yog?>䗭s'_ŚxUZ3WG5rMYLw+wm }_ySO.v\.`8yj-S|>F7d6YDB@L訋 Ǐ:%XcF_x=8v|>fʏBrڬ3pJd\R_+ >/@) 8:'r̙Ls/\,Ç & kJne Ng655Si#0,@*˝x 2Gb5*o;_UUd0RVg77tlFRbbCG˝^)Geh`7߉N @ `1466)PG[ko% J:|L^SS;t:] OgeFzknΞ=?mdd0n9Z' Vۅ | :wh4=>:'OnWK$+cNω-WEk! q_/jٺG{xvR aXN(_HHpSs 픫f+..pJ`yk+_"tG4 AA\.Ó;˴ZP([( | $IREĩC2һwa9md2\΢rc*`}S&EGE6w t+//ݖW^{ƴ[a8s"qqՃJJ$0XP(Y>M P(~ӮLvժ %ߥ^f*~٧w5p :䇟~mniP(M/ h0k4N7"FPTHCkkke]n]gs:պ 6jxqq9\Rq7TϽ ᶃRJ@׳4zQPR/kZq<|U[yU$77NéQYBz!qy6ڶ @gX*uUUy|wpJre9!:%Λ47͹6D}.l6Z'cXo\]5+$w g޵{5rtz mǗwBx/--s%YwLWH%&:t^zheWrGsO[[A{g1D.VUUCO9ؔ?*DNWWtm3DϿ UPX,2! 3ڜuƦ&TLvI 5FU*N$#E9َ!In2EuuFITUW׀Η1_@$UV{YNfxa&!{  *--N-WWͪ] 3L|BB  RBO2%hiϿ*yp] X$۰1>BG*x7v3JK3G.tc*`-Sع._ h4O>Ң7p8%eeadDBB.{8~Tbcf卷$ȨȥK5r;++NɃ|*u{;˽ed#2e݆'Ns~ٶ7&D"g]~|444=w#%SSI\βK~ן! {b,66xO=O/~|qcFd565lhHO9Zܹ/~c}E؍ncܽwϾ$ID¹o psA;JI$I6s X"^q.3wi/P(~˔o5~~ikt}Ԝ$=Qȇd=]nmh?ovëJ ;[~+͜~ӬoI/-vogzϏ7:';**e,;+ťwu~Wr'nYzSu܉^g ;n|F]mmmt=fiS'/~N |y:Й>m͜~Ӌ8:'ڵªx׫AAڃAu(^AAסxAA_AA|WAu(^AAסxAA_AA|WAu(^AAסxS :  :ȿL&SQ^65`@H$q# ݅RX|%,,$PLVQ]ab ~uG 15㏼^sԩ|s Bgw~v8 HN |G˞y+Qr\_~Rvڵ`Bom ՟^^^N-KLLO>IѼ?/..0,)iGA-7[g-2/._a؈#%$IvŋgffYի׬]x⊊ O~U|esCC'~O[7~v xwwb￟1bH7snnBG~_2cTV֗_~ч>y?k>q8>-TZRu-mݹzf2a'U_n`LLHh蛯 ;BAkJ+Wh)Ի]ڵkEB}ݧ7BCB|{[,eԴ4ogd444taJv꟟͍M[6m͚ .nݾwԯ7͟7tn_p1sFJU\T#.^V/A8d'܇# V}g0M&K/,۴d25nR bZxw/Eysg޻B(om 0,8(E&+-+̝4]R o/.Ͽ8fS 3t\uڵkRB1dȺoA]:@ dT:;be>hڵk'5-l6wvSnXf:xЫ^Ӿ{F_2sƴӧF\I0 :$А*T_Oq¼1|FF#=7y4@A5O IDATEۗݹzǾKꔺꪪW}֢{[IYKK@` xWWW}VYJ5aʕnK/ݽp׃@jjΝ;:`mwV'fgן<fx\.IFir^nslgn5X,>{»;o6l1ZCnWcew Z3֯fرL&-: bAm]W\زy >+%֗vK::.8wD"i_|KKOzXx8?ѣGo#\.Ƀ&N:y4Հe4Z-aL&f_1mm~{++%&&zU(==XŗHOOuZ՚5k0 ԹPqqQkk;ZaMu# u]xO׷8. ]s BbŊ{9lݺOOO_&;۷X,6bWP BCqK٣Fl^ds/\F>;WT͊ϕw6M1 ^}W_ðI֡B o2>Ä˗/L^Tnݺ榦,YKzh4YqȑKzmرFBT?yJד$Yp' ȝ4-? _N06ɾϝ*)-vvؘ7+t8LwΕN8n2&O]Hg]2Y*ڵnA!y0Rj* z4X@-[+]Ir%6x_G;ֹ̡;n'k6|  CvÕ_Zh8WoxԈeO6B7y>_0tH:2H۶ [9;)NI Bϕu++?d4>E!7  ~ŶݔUA7y~e .N[5)cǎ)Z[!bxdffbX&suZٓ?"##,CAzwn9ԯj^/^ ;Z#ʩDZ! kW|0XH$3f\tFUpAA7;|?;AxTa)|QƗ>* HQ )q* HQs0НonOUA~ɣ "q8,@*xL.rؽߤ* H緽ǕUTTuFDEEPfDAnBD qF0b ¾ x4?  HB  P  C  + :  P  C  + :~G/ǍgFCC=sΦ&flv`y$nd2~}01c\={n}СCq}QQQ$Iھ4AWDG 8 ժbQZZCG%!!AgΜH0Lf'hFTh]4T&z{5N&uљO͊ps7, a]1S=6jivc&)~"m]20Bs~ [W[V<|P wqOfNP׎7666,,`08FOGbt2q̙s-}x:LbfϞw  fΜuoXt? ֿk֬}y;i?[zܬeL&sĉcƌ9ymZZZA|A;wnJJJ=RhڠW F諌d0"n@),C3@GDJ?s݉4T!0ٺO ƀ\Shp;'i RJ!q vY|쎉Ţq˶NeFLO+mmjV+HwJ%GպΧ~f}zA c̘1YYYΝۺuk}}=Z4gΜvmmmRRB0 $I8%%YVY 0 b8f#"H7Y ftٌ,4N|FNh̲!9AY7t̃a1/mdHgyx8車o]ba5$M84rvg=>f0aaaFqϞt::]3&g(>߯Se,K,S/|>U%zvhp;K>E`tB.qbL&ANgX8$i4-|<;陙Ç?}Ν;++Ç_"Ip]w&%%UL&SKKKxxxYYY+Tm**,,Ujv'@:dNMFnu!CߤKym{`٭:ʍI C'l/J7|[p\{qxٗI1BGE\F 3uVS[[$F[N.a宂)싙ZjI픕՚A&Ow~j$0w,HB`zڪ8 **z;ʕkjW+**<+L&]$P  1B]Rن\.پt8h`6i4ZVVVffŋ7m_}}="O@'Oh/hnnNII\ZZf@j5[&;F@h5`0MP%b.IzqpY:O}3T'2 4V{"|FB~*l=qOՏɎ t؎5`ZV6D z=CZ~_ݨ;sD*d9C~~~yyPfU矻=BIIYINWTT2L@@vZqW_+_,Au:=$ BZbx<^X,4r\! FNK  HZ^^oߞJ HN8qRX”6 !r&X"|}{Sx0H-d*gVrtvCq&l*րz֭S Ps@}X`Fl %XSBcccLF/+|>?33':DV566i4jyBBX,s7 8ԯvyDuIioP lb04l6P/L&;d~~~N{Ab_]]gRTׂ 񔖖;9 GE=e߮Va#2ϭ<I'M&L& c0IsE-GR^紗-=O[>+?Y5eu@vcc&io:~BurB ⯎XO3MܰO 2?:NFAe|h4!~og|Xh~4cMVq@8uÂsE-_%|=`mg\/{;rc]7r3g;.Hlh.8&0 rrF;vN$EFF&Ac2I,'$$D˗/uCa tgF&b+T]8Fc2T rvc2^DEE?꪿>xJq_gA-!F3fLjjFOG*S) z@`k h1ݤpd* ^ک=B:M.pq#0y999))ՙ~ EXJu@q:JA$IbjA8. N0}BkkkN:~EнW"I`dggϜ93(O,KRX,v;T͊@hMv@d}}=Ɋ-++fY-ךY]iӀEndԕ2Nu͸<{3';Xd(S,p ~fǨyBgB@[&d6 - 3<42dUq8?3tmeuH$ `РAޞB{(]Jcd:tFP(Z FTTСCӣ] ε_t.4hBt 8NJjKZZdzne6r\+ѱ/zh1c۷0/%_l6$2eJs}ev-h}) (G[,7LNMN٥F8qFt J^pႂ|=A@L֨QY٣ɣ|Jmu\.KNMe2;豨մq@f=ܝ\.DፍnA=*v4 aekc4:c!ʝ$$,8'M8Of6=HfrN:$fBwi4Z Űajjj D"\.J1ju{kkkSSKAqrvY]]<ӎ۴6b:TC]d2tu:H$HDvbB 7o thFg BY[[KMu_PP`lemKxD /8/qܬ]JMMN*A0 \a{baNwSH$Ot)FE-b Xٟ;^*ZJ$.W$5ATUUy2׾V-*|U 5<84_}l\헪d2v㖩Om*d Ԍpa\]a@,;=?7GBŋ-5k֟yɄ!B ̘1c„ P Akke-ɩiYaXb‹UU!a VRi@@@}}},LݢH3t6f;h% 6!0H75`CtN*'6q~k"0Tk!ehSbpap&i޿M!A"󃺀Se822R]f!--2 GBzXb4\rxܶzP}Fjd2`:r\k _lL E(.X`߳g^GQ DD"5kVNN=OAiU. ]p8UXXX,} IgiI/vL.!mWpz6(MqU>ںLh2-.ƭ`qɅ!e_t&ў? G9J,4.; 1Q ZY,¤$v`0,q\"p8V{UV!?;;S(AP1 NC~Eщbc0OaKd>-komX,dr p8JjqPBٳLk׮A|A!!)S?Ӭy ˍZ(.48%L:]{c<(8$zE.%%Y Fxx(ǻrjDD' 1NH<5AUIIz`RhX A;I/P_yQʐ*O(d86 Ω-Nd'{X؁t \7,J28%j "2=V$@x#‚P\NdqqqBBӐnl;ltz<z*Ɍ0`@QQ[ ai}cGS ׅ]mMXXh[At:.+vEMbP(l60#BJSwZ["H*'IRt7^͝;w?Fzҿ7st cuQ¢K)ilGW:FQ}V0 KJN\\t4:7Fo΄fM#W: Vl*q7'yyzLNZB.J o-P IDATqLbhA50$BXȋ̎y= L i4~hж|b@^'Jf2CB[ZZtd*++HII)--\.7/o"9rhdX᡻vE Un?ş~[l-*Zq6m}ģd}*`ǣ!lmU8gBTF Μ9sĉ[n?rZFQ 2~ĔT: @^QKS:l"IB& `㴤ɗ.da6g.vΧk$MҷphƀGaF`X]Ҩ&ֽh¹,]ILt\.aoeH2$%g^ixxjfD;DC𙼶sOj~<;~.&f@MMV*//7 )**h}/ڪP*U#F wZc;`7oC74!QOMtNXzáiAtzL2^*}9nܸLM{= Ν}]v) PgbZJ淣幇xj(ϝMKDDDĬYFiϠiL&҅|4ZG*mhh Ů] zB4H$аf 0ܶdzڊ ųi+)nS>pW{(ju4IN-H70 #'Ш~p[i!qnFM*K V-u~^?WAA>G*bW>6uj^]]k%ZUUBCB!1::q=o:m(pp֖m L7JF_focǎm߾].~-͋ 9s椥~|`U(AN0 cwLxQs5Qޯ -ae_2-BqI.#/N)2V __&5XZA7FMcxm '˪_5Ix7Եp24$판~fYL4/ C}}}NN֮]{<p\!Ɨ\ҐDrjA1sҡkEԛ<&` ztGUYY?~CÇq,$ŵ?h4r\ۑ8g+Z>`0ƍ7z'On߾ֱK6QQQg U( vSHRi10Y| 8i7WaX}’$F#<[zt瀯q`7=XyFфB!boR0Jh4Зp BxБcGr˔cN0V WNNvNMMm?|S՗W444QsTmJR(K҆sS;5+Ph4II />|!B| >>a„I>K ` |>1 b!?/>o3z)]p:-5 4!q0 -ZQJk @8!nV2|kZd-'*GUUս΁\,))iZ̙gΜ<fLfÊT0 KLXQQo Tׂ􉔔 &h׍D_tw_q,8!(۩+&sKPMðŋc_SS_Ga >Ban6@2 } կLbbbcbbf֭[/]F>#=Baѣo`pE#Xۗv_6Wy¬>xr]ctpj {t>V}gF>|DNΘBU,(-JIˬ=6E3ݙ}Z+TrȨEkh߷oիW B1d\ȣ"Fㆾ'BC:pBA_g)$d233ƌ :N g+Z>_0a#G]"Ç#q$Ir8ܼ6m_gA%Tϙ3{n߾ɓ #B\!N6A|ߩYx#AAA>ٳwuAٌA~~~ӧO8q"|qA_W'Tz=dff^ZwMp7IPpqZϝgOFEE-]T*) o+]Fv)+7Kڱ<ԍy /tyA+A}(+W/,2 4 ӴL0M0 iMs҈Cj1 YeY. ,<)+G@@@@-@R80 ]u],ζmTPrMt:ay("!r}P G1M4M˲f !( dL>m۪O4q\4y^D.G ąD_9Ő$Vi DZ,Ku4MM@338-36_0dYe^DArrlrgH@@@@pJ+ PŲ 0a ǵRn.< ?w6ٶ ,'( 9# }[[uN}G?JE<׫Wwa\;5 rkW:W46:mU϶o&Y8knEEUK4T|s/:T?g2%s1w„8W(+в <;qmzU#AtvZWb*َ͕mXiZŦi +Z(ڶiӼ@R)t3m9(躮( XZDCZq!qt0MA_)+ۓ[Uմk׎(lc=ǩgYVZ۶eYٳ{Ϟ=,2l*뺆.Hm^tZ1Mj2 RUUycd.K撹'f7q GW,z5EQ,9EO:.--ݸqSiOl hOLiZDA}9_I7sJ(<ϙ"Lj* |88NU5]W|>_6mڶmZR[V4d2)"ޖwp Z-N a d{leEQt/\b͛4ضcV2aYDIJ2=(.vZ[[?t޽8r*(((X,q(n| ( ˲tڲLǖ0  $IiyY<۷oDl#KvͿ( !V<K)W72dd*@;z%KYFeB)f= 簠B!U4hвe޽{>}>c>|?<qT*Ny晿կ\|WݴiyӧO^q`0(2,+M(25MaZ]ף(r:8~TU>m۶۷GKeزUU1қE( AkW/0*@~vZb$ K644|Nj/>pw4MUUq~?w~?:j]D"WUUիW7xcذ***:vضm6md- /QQ}}0i$Y@޽n|(n/;MQ B]]]mm$IHx<%%% û!/;0d G )lIyyy~֮] z i+x9~CO?}Νg|墋.}JKK{9= `С˖-SN?t,Lwgoڴw1bĜ9sa`0iު*{>}FI$FXRG8,'y3#ZOVWZ"+Hܧ܂) L&E.,,,,,={vyy9$ 4C˲gQQQCC170MSN˗/;֬Y{{:]3;ݝoΝ;W_:k˖-?SO= psΦirDb@`skFCC}09f9 $" 8 wBKnsHFibQXE^ q }.;nݺ#GmvΜ97ۿY4 ,o;}tA42,m⽨!Cc޽{w :vXSSc:1ÝoDZ,q,ˊȲR__׷o=N(JSe!]Mx_/A~WZhu|%eTDQSXb9s>c>}`3j(Jˆ,'5M/رcGH>qPN D}m]XWYɮ 8i5mE(={ή];4M0 ]YeРA{} v뭷}=~/\8 ˲xGr0 0mqDN\ڑn1C.ZqGW\qş'&_yi*D> -Zw3;|4MK$pSGN ضijv ̲,WTsWPnll7ofc{8r{3 oOw^8~衇fWTVV|!C8w=\1g쾤(Jccm|0g?Yaak*AI#QN<JYٞ-޺xǶ\4cX8,6ġCއHeB`(ܴicii)ϳ,\ed2eK%\ڦMX /bv'|}E4(mۖe;躦i(\),,`ڶmYmeY,ˢiZU5nƳܵٶm`[[[Gx]ݺu͝;_bܹ[zľmgnٳlR c /w3fQdD0bП}8sN_qD.'xo2M`PӴxu5%IjVvg'7?$i'\_~43J%əeYeeUU$</u]ҥaaq0L&Njƫ>c! Ȁ(m뺮(|/B:J%>D"_~1ZR iZEEŻ3r ,˶m\BM)B-5+'LYTWr=rV; *+UUUu]jv뮾o9Hcƌn)St]~i<۶EQϖe ~_a˲LӚ8'UUU\ ?p8ݻie͛76(BEѶm<0'G_1(J<DZZgT[qql۶++MӼ R8|뭷b믿DSLmhhhll( P(E1󊢦iIa. px?bRaO?L,ċ/K.nw$`Y:{7yN|wA:uJ$,s c(Zk (*((lVq鴮kEE* 8lSW!4kkks ޝ}{ Ci?Tq͚5޾{L}$IiZ* æi!˲$Id4M iEQiw+ڶ]]]=f̘UVڵwOz 7lڴiΜAHssM&Md:481`k<+g}@.ǎmllpuӤg̘1tѣG[nرxXy_r˂ DQTyyٳ~h ;"O I.%Kf,EQ4M4\6Ǧiqqo_w ]t]߽{heg;v\dPǏwKd2}fcL&ieɲ~?J мbj[o)B4.4M `a0M^{4 q8&zbh67]7o޼yfoLgܹiJnG xNPum}nU߯a{YӡC&JtRGEïYn}YYMSeBsXCRq=qiQ2$'d+DB)ec];e9eF*%'>`0ثW 6SOu]fںu۞=m@eu]3 3,"Qxt]4w06B6~KQm[<[i0] v_>}֬Y4MK$6/..߿Ҟ _ZBkzu۶m*!'D Eqq Ƽ+?|%gaUqD\UU4۶mۻwKdΊD"/u駟knݖqT* d!, YRT0,4 j4M4q(۱4Mf,t m JFǽ^_}~;voݍ!Ŏc/lӦMΝoPoc_)((暫rݻ .8%4Ms9w;w?ݷo_<?s++RP*GH'!T[[vT Պ,m۶%%%{Mгg={/G{eEQpXӚ&I'I,H_~:e9 @>/J)  @-˲(dH,q,Va(ڶ++E9=Uv8Сѣ}x< #y޶d29g%I.\+}ePxȐˇ ׭[jժQF={6mDQׯ_W<СygpvWT*%3^e-۷/-ˊFc@qwO袋xg}ߟ3gΥ^%I$Pq]ג7ػw}q_:iҤvڽ挍7(*cQٶC hp8Nun !!TV8,p6e4BDox?<ǟ?>nxw[z饗m֥Kɓ'Xbs'Nx4۴is7>䓪v'14M/G~8G!}>ѐ_<7M#LBx;dYѣ 73ό1/OA>}M7/<4[%ieqg1Mi vqmaX׳mvO8'ࡇ0 w}ƍK$({&O\^^dn=ms9SL9tĉ)pccCEEҥK~"r|C M}!>>l( }M?|%[ŢX>cY x ɲܭ[`0i&ڵk4h[oU^^viĉ@.p8 CGʒN Ð$0@ N/[oUQ]7l)ׯk\$Iih t]lg04My5,*qX.Xq]7E}С=1<5Mc@QX,0i;ceٺ&',q,4McY$I4dm[^\gr+_aJ<MSe,[d1yn1ڵck=QUnjJ%J2%sc;Ǹ$ qsiµ(ulAɝdxT|ƻ$ TWbApO2Zpڵkw5joOvYg!_ATUMR@  % Mڴi?3zaξ}\QUU$S4IQ4M.iRc~m;d, -pn?RJNWKeeI^$]|'N4o<&qҥ֭s ht֬Y_KJ'd0?âī4zhdmSQuṮw_WѨi+JFZP<4-QiZ$Iܧ/tGo~ ?lIccM7o0z<4Mi/eKfΜk׮?0 r0(E1~ي07 IDATߟJɪ6g2뺆0t?i[a0\vdݫθ'dэbs+--3fu]>蕘L&GA," CQ9Cw}t]Kt bK~ֽ{w_)GO[[+\9BS6V\ʵsMNmLxdYf"u4 ض[48p-Je/cdRp8H$,ˊF4MR)Y~իǏa޽zW_LR3f̸[|p8Vh`YP|2,g˲X0syE^}59BGC_Z#:a+m; sQ(j ?}:3M3fw7elEQ$Idw-M9 4 GCJZ' ȏo Ȧ,+4㩹r8C;{ ph!RTe.OZ@TUuIֺuv1z;w~G}t]P(ds){{wg=IglM&_Q5N|>bfBqq*+@y <5W4*_+2 bEh(Bp-ox Z9ǎ;Yf->/W^9БTZ T\:+;xݵM* ֔CKNY%-KS\ɣ"P%|$R~:۶~ŕݻ_{zMM,>lQQQMhXb%%?j'T۲ Yʢ(8`0Lb6J eU ( 鴅eTUE 5MkqA(`%\;_YM_Jo@ k֬ݻO8\$"{dvTQ+E1 VLɠ,HAZ2zwh{4zܧJs8m]$K_:tpd2Y_@x<ݻ瞻馛fΜiYVCCCKvRXXLCPQPBD nCpAå,`PE ,˚ax8C95 AO_m(Muz_jܹ7n- ÈH$>|ٰaWE0""Mfm29r8he\SeIYm*՟a`kjAWu뙱[/O=pφz+_y:ŁҥӪU+v+2 ?ϗ/_^_SOM:q9qEQ[itIIuu RwuƟ>_BD"!m۶I&e4U\\$˲e` O A崦˲&2<ϊqi8eeX,aaٶ8~Ե_8l[K/NBb,ضuy9n!vvKٶ EΔy Z7l؀h+O&W]u՘1c#GiB!YN뺖H$П( R(9EE8轵m 5NmF6d&uz#fضmx-uJ;izĈk?Ï|'m|$I{e .tʑȔXZ|"vioɄ8@֌W,[%r`O6e]q9 d5 "ruxO=㢯`c<\b%I 6mB+iGuguֻ[VVV^^~w^x&L3g߭۷oePQf4M n`1~Ip*u4P(IJmEA4MJQoVt:[DZu]0, ;2N+se4ImϲKS ׯR>u0 ؖe`+Dnݞ|.]/2:81bD.]^zd2Uw&~$se8g7} yxdܱmtKru:`x"AMA8v7 àUܯ;wZWWgW*;w%Kׅ~zժ/GՕW^cǎf_OJU`0( E1#IR/mS qVUU5aeYjVE<L`fY4 embYV( H-qs29?8`)uSFqQemw5&4_⬳?9s&ò,*X}Y ^qZ,{e$.eW6:We2( 0,Y2fDpޱWL\RFP( )(?Z80uu5X@ Ðec0] uJ 8Y BT |>E2TY\qEQ˲\-ZgP\+Hbx^+P(d$2IORxY2a E #ضc7555=cǎ"|#~2ܳgρD!'D~CdPmk1L/i*IYH3AB}ٹbg4Sfl),i.US9Ak6^! b*..4 %p8|M7ٳwϖdEz+ Bi , -`*p$-4M 8`Qxd]΃Μ9ӭ*Yږe% }ݷwޗ_~yȐ!}ݰa_/IғO>5q3gĂEAƃ[Wt]WUղo[,[`PBﭪ(_UUApE"L6-%Le*-e*- Y ˲U`2dH~L4_w}SN߿_裏?ӟ4q۷W_W_M>+@ ߶m[ccWbsDb98SY. IDMHS?@sڌEB5jY6MS4M> R82('ei)͔6b4]鎱8˲n0J,p8nֺ:۶-BصkԩSw|E866#ԩqs B-˾KnO?k7ga$H0eχ Rјms eQU3#rCx1*dYV۶mC 4'zA.UGW^܌^0 -V9㣏>/tG_QF=󍍍|~ݻ߿?1y'xqi4RԊ+:oxP6P_Ida,S2 g99k>,  4F4td-n@'@HKY,+rTAee{2xeWDp ]tDۃ]v;8Pz:æi]vO`Շ Rc:EѢS~|>r:b1MSeY4۷o[__HP0J7pd9(Q~,"tHPQ0 66Ffj ADDG"a@BB;[w^uܪ{oPuϭsUu<[I c=䓗\rɇ>UVGFẮiZR3^8888cƌA ZVT*uttEUU٬m ^*}\.a㚦(q5Te B0HuteqV&Q u"l&{[sqjO)¦}vhoo?:nbrNg9HGGǕW^ϯdAӴkמy晍G^OJP \ߵs?Ǵ2Vޫψuh'J KOgoI  b,$E.lĻAXBwa.hI1D N7 ɤl8 뺏=ؒ%OM>=2r&!iY:CC!B=S*?+hFKKKXDӒmQH,#) aV~ B$qeA6<ϓe4MD7n|Fad%|FGͿncx] Y&b#D[[W_}.ϛw{M6;!ǡ'?<4M++V8$_Ip@bipwU;-78m5_5?xa& N8X}fB u]l!YJZ7@j!r|6+eRU^_E=*?ެ _җz{wG-[DFTUUuLgY͛vQ*S)c߾}ӧOߴiS{{{TJӞ絴lܸ+ M)d2Ӵ &N[].eYijʠ zFmE@b2/c,s#KDa0(KMROQYr<3fxꩧ<< ,9s&L:g;8 A,Xwsɒ%+V C/QP b]ng>ge T'h$HpA &|ZJ):4,!`rg{4E갏>j 𮄬?BȿEYnyJrKGGǕW~yɒ" eYv]aʕgyqs9g  ޽{GP(=˲y0 E,K, U71 ,r9o jJXFq:aGDBITT:k֭[_%K~g2ٙ3g-\xh2 ꫯ>餓.]z-Յ'^~K/w_x\$`߾WԻN|֍iP"]8ވU`NԷK @]qbǧ j2nADBeٙҪ/}L+Sa7l8Udg޸q޽{NIN+IR6 :KiӦ7,\}ܷ۶o~˖-?M7=frT$9ɔf\$9NSe]ב( +o%4]r9xa*OXbݺu<_~y9O?-[oO6mڴ{(QA{.Mm~cquO }]N̷H T/1`f  GCU+X[(%BY/ؘ5}E_W XreRow]vW]wwŋ|IVV+ IDAT>J$b.s\6=T.u]J4a-!q0 ~4o&$9Jaqft, s~X!A l)+SK7F\U"hn5%ꥺ8TգV(z}{;ضSK-}ժU.Yv-Hُ]~L}!:qq=uυX\#}*%&A ?BB*w{J RJ6T=,GC|_eC!$n:̇>.w?mo>̚5oa3yA<Ӵ$PJ2+him͹hfZ LٺiTR麮i*T02q ,4 <;53Ë.߻di`^,,̙3{e˾o}޼~,jy͛7}]ؑUC4 xV 1 =JY!pO´۶$1岉 EQ<%H}pt^\CNdv\Ks{}DcVK眳;::~aQ5MۻwǏQV<HmGٹ܀e$T+}$$%'HC$A.\~s,op9o "@LVj%WfhÂ"ە &'wEZX^^qlhiivgpp4@TP\6!t8۶Q\dR5M=]2PXŒC$˲4ğt]3MR(Ϳegzoz^$oLǹ/]+kҤO>{vLkɵ,1a=N9! sgOUroM`nq$'ό #-R%c mV"! "@%; "zBx`db6)Jbı_ŎNhA`뺫Wm=Ww @UT*ĺT 0`9o"f0mkf' 0Ţ(J:ӴlFa, +(A!~:mȲD9AL`_]?jǍGzqc]63jEAضm9wsNPDaו ݒJLtImڶdۂibL$#K Ap_"fGD h!) u r]Q} {(%oΝ;(7tI6lIIjOmnr4|{8\LӒ$IuбP( /&B1-:1CCømF A( o#  :`-)Lf͡}kO^}U:iҤK_E4={:. baipqR g:]J;Ƶm==@'&HVӟ4{*0ݎq@PbGkA+E,; X8eaJ}性,l2G oWW֬Y 9s_5!dƍ˖-I)E.W`jzj]\o ijj qB@Ft-˄\Y07mJ%YS2YVEGZåƊ  (r#ρFOVpgLנ!N;slҥKMmevwwO:ƫ]# jnQ!@QJ*2ӴzzܶT >B_H{ $8*$yg.!(\ -#bb SV9()T2Qϙ|Q7l@mniiRxŋ 0cƌ|E4uǶhКJ}TEQdYdȆcL/J:EQr6XU*EE[s1≗Ug'5H)O=&BYK;1JRoE=dBH{{C=idO_IpL,Cz{ܳmVQ̄$Hp,A|3a.0bFF5N8COTVa$%b$+7NY BHxg:7ov &E "e4)J|Gj*.x1 PQT*EA#MMY fm ˴Yq](J$t!R*?mbq؝&8*&QPEQFS!%K-aR\>ءRy_ 81:oʤƵŲe۪ #K|9C3 ?$lK/bmH?#ui%a4%BY/=(Fbu%kn,nlꫯVP(YfdQl&!DdUU2a@b6J&H\m۶e!D&15!>HS0a`عaK>Td3V[?s>{z=ϋd Hp@m_AD_~|_Yk:O1mc+q#ĆPbZz-E٩Ŏr Ub|o[ŏܲ@mqlEQnT*? ͞~{>TUj::0k#..o'$IUTE-Ѿ I_Ϣx&k@Yd%BPji9rvYǔv^BK;::%#NS(g*% J )=u}n[nfLXi!,$H+-迂kJlQV 3F.-la* _jDG&_Z0,۷o}OӴq>"m^PJ]Ev>]E$Rj۶*.a )J,%)8B*bs1p}~A_5)L%"Ѩ) ƿڍ?#v޽{_Ipu8zYg\&uYڙ feRy^}B)zB敂1H L4 "[Fl `栚Lhڃ".+Iy;w|e˖ 6ٳ'>yX{)BUq\<e&R)YVlvdYA_lm[e])<F$^cWbld~QQ\Xhca̘1c֬YOC,;Kd  0HGRW`ɭRu5sc+P15 獈~bY?HE̶ |H \.R}QJ=|G[lݺu˖-[nݺc _(ʴi&O|rM3=ϻ袋Ə?i׮O<.,kZ*pbF0 Cdum)Qt]'C虋HA N}d܅,T1[l r ˴- B>5) ۹#" ߶7L&_N^yce]6~ŋm]]]o{t]ׯj4󼾾O *(0Xt=oe={svn{3YT(Fyz!/ .pe ywq`@+!mG-[k!\6TQV04,l&G!mъ#bM!GKZ^y]׻wkm7;w|.ELם;w>\wulqC=#˲r #l"K㸦ir&H2!EZ0} se6I '!+l̈10$QJQ{#WA-ZwuUW]}k_|[xww}믽ڦ[o_rkk>Ek=眳Xwoo-[VZm6 W,j TzL%|  L3mmgmUg ((뚦%˒(J$~yޭ.dy )&(R"S,8-8 @A 7TPvz#GWT9(Ie!Q}0 a֭[VcDQ8qӻg<41(5M×^xWn<(2֍$G, EQD$D4) r#t(!DQD* bW b l:~DD%ҋ,O :8Yx/+_?vimӟ+\~^O~n;3+Ar-;v|ꩧ6o޲m6LqԔloo룔Ws P%x\7A+R:gj8䓦=bT>WMjP Rq\I$ITU@_ "]qbQ#$D( ڃEZE<#EQlmm=x }ǎ;v`̹Oϟi?|y cv,fs:Bw])bێ$ɜ~m1ċp&Lk7A`<1} 2Q wC]eW)FY#=lpL| lyW?O:upp[0|ܸqxq's]`Ӄ, C'Yێ5(1buMkَe,2LB|w7 4mBq aEǥ*ߋ**i#+_")0٘/E\&Q'D|ґe/"Tƒ}Dk Ş==CuRƠ}c_W&L@GGg__?&6UU\.O<ٲ,uۄ)+yy yHV3R @'7= 26f|)!25sjW\JC}Oqr*d2<E( mmmDK/Kۢ hJcدj^q`goO>mm׵,P뺁<k2 %Hƀ0 ? ?rw0w W]ToNRia}9eS,M|a;z: E[E0!7xO~ $I`yBQ)I,Ktq dh#@3T+*ˊ,Km۶?`B] q/(Tw`?[WvƶׅC+4ӟ J.:"w`\1ڙZ*kc-|栺@ TPA)(PJ( 0>(oiw` 0CCCR5F⍠:5Yb[ZZm6wܹs綷wuu}c{uvv477r;w}\B"QAxHP}$_*´<1k{x_ >aٍG I/35&+"o~S'BTQ($CE AdYE(ɲCQ<IdYHĄŰ^p ]CĥF;0N-ˎD?<gs%+֭[ӣ7lY˷e^|;կ~ Ǭ{ukr,M[`ҤG:^K93Q)B.>93!SNxwpEUܱSo \ɸ;VIߤ+28tԉ!|?h* ->$` /RZ,FT1K_K` /΋1P.$L+O5*Ca{̡1r̞LuS2DA }] C׾F)~*b-!I655|tw ZEqCdN0kh r~GDn_RqNCBMsBOO---t:JṄ?ᡡ!$8 ,l;ݱh\ːiۖ ĵC^s$x3 ׁ |QR- !-n1BqJӘ2s|-/ϹP_ zֈ;+OYPØG..)@HhDH7"J| ĻcAUk;TcF,ǁ4w D|0F20=͎rz'\|cˆa㘦i6(Ei\8c'$z)F}@BY$=\:E_En#i*-, auz "t^,lzGo QzE`Ŀ4lE;Ŭ@>/-B7Nޖ| ̐  /7laˈV/˶ ?  ԫ<Fn~lJT*>r\.b2at$(I-5wXrʕ+kQja8И' V!1٢k2O(@}ZAq/"6rUUibѶ6X0Aϭ9mJm;<#?TR/)5w/Jh|Ojwܹ~%I}_^ziAikᕉUC1I)d#(Wĝ;{H-`܄ '㯦h6 mo=hٶ,l @O>Z Lo둕7˱J&}Q_fٳg?hELqTIA@S){7Jh  e+!?H /45[8%"*zEa&d\@k;XTa[V,T4_Ww ގl0}_)͓$IT l z'"{`W_}W_p奕DV(a nwN;.];5y~cU#LZ,A2HG[QAAEDBoqh2j'3F52'⛂1AdUQgfD1l͟?h( u]7 %svBrx?sW^骷Gց[a7³6O0j }?ϣ ȍjz-qj'w4`ܹsFl믿k.$K`̂KP::喻cXeOAz_ [h+,f^B>"Bt ;IʲCGL,zD EiJ%ɑX;Tihm;/X"c2YvⓣJŰ.~ ߠ2Ry|i_o`0a?fu19r=@___SSSMO/_mD!dRaݻwgMTUŚA8ąMJe#.)"жb~u|'ur\  :w?\ۄ@*e@c;?& A@F][kH,WV2\&Ma/ٔ\Ze+>/rRT/#Te j12]hXu]|20#,K/be8%W SS 3pMӌ"lRev&˲b)M7RLtР#]=4ؓ":<߲Z^ضS/!u ˻#i0 pH}ʝw4~ UqC#j~*+ZM̏ , ~cP*d2MMM۶m}a2:S/Ͱ!KKN,ʿuGVA|p$@ 3ZY bDh+3Dǟ)x*nd_=X,~UȲ"#< Ռuyx9GNy7\"S3HE -b$DFyxCW֌L2GVs,DGGǴiSu]>CzJn UKpb@9ax[\n^g6rM9A7z3:jօc*51*4B]@`l[T#V@ 9RHp% R2_}{Ry^:~{k /vZÑ0VT& EIQ*,4Ȫ_e( \Qe뺮8 D__(푣elGo7>q_{dgUU3M|;|>~8p)SxK6s͚5+V3 Nc N(g2GxRZvbežc>'H0$I4d,⤁w ]@P,VgV6RpD+AP[a&4TUϙ3?3"TUkiiaY}Gnҗ ֬Y;kӦMhru =|0'UU@8>TEA@}tၖen FHVPbIVRe4/_hj8o=SO9vƍO|.{ʥK>lob \sMss$UNxd2Ød(S!ϾysomD{,V$"~l3  [&ۃZ0*uڈ([߰2yrWN]#k w(r<\\+MY~_|yhÿo_ZfΜI)}$Wo}[۶;88XJ5A(2< ˒k̜nPdT1kQi#N3 [m)ϭ2<-2MkժUf}ҥ☑ 5(L 0iҤUV RJ_z뮻ζm9tdY y>E4 Y6BnIu3' lCDCs[AF @Ư5ߍ3.HF93g΃>^Ο?+馛?oZo~^T\54 &Њ>/%A P.*%$jJ&`݈ eO.) F!>'CW–O$IꭸRE%I}_Q(*9s&L٩޽{ycss3뺦iW+VJ%UU !_ŖJ뺶J%vAdIeYF_q]ts4 ?9x[kA vE"a*4+a%LFMͣϰW a…P`[1 L(ǹsBXvdt &Y((!IܼiӦ%KW(|ĤIꪯ~kaԄICCC8 ˲DQ4 UIttIJPsb@RSVa FtLtǖksnx?ﳟn]ppppǎ^yYf]r%&L۷o޼yӦwZ19IM Bf ^GWؐ5A0TeYE_Bm(#WzCŒ躎dB+PDQ6mgm۶Gyd֭۶mG@u]ww_w_6lضm<}W@ *,2{ M$024IQ%J1}SASk*PMcݖ `xq qZo8EFwcЇeoo}w_~ <_^xXnO~rWp?i/q[U)SL6mԩ'Oc=X1 `ޯ(~jƄ0b'|'qYC eEdUUp,˘hq՘YI^f͚5k2͛7?Xl6(u+A&x!9 -ԋyJ_1MJ!t( =TIDJkA-ѯ 4˲[wQ%߿_uV5y?O|;zb<-:2!,#r]P(A`!DQTUUDQdCJ,]2T$TAk8 6E%/ۀ1%+ 15ydQy3f}f6leYǏ?ѳK A7\yWB}m—!UKCŅoxVR  8B}Eaz#HNgf[쯿z]]en+<)J|)DB,+T RX FEqӴ 1:YA kλ7ylP^sG3+V@]A&NqtO A5[v>3Ui sT>Ն̆RZ{Zp8 5bE52,q^{5]u]wGdA߿d@'YڃPy>x lLtlTU/ MTUEL"g&E,&T'+l0vxxbMl+յiӦ˗j&t@HLFGKhM wUҭUKzBhٳzNkmC U@ך(MSMaA( *J%>UQTЇJ4  Du%˲X>t:ilf u]7 @T*̔R, #p `؃ 1U"f49X^X00{즦+VRJ?r-|aʕ7n0C9NBP$H+Piby 7 ׽j뮻(+ģI{)(nS9(]"-,|'ůq2em;#|EUUy5tmAFJ!iai"A'."R:yW* g˲K&eAGAAG 1{RpŸ|>V.cEm˗_裏Ϟ'Ie)UkBQ$Hօ8tQogl[o]K x0踣Pzj1,yEQĮs9gҥ=pa FYUU iZg&4帮[*"躎9ȁTPXBEtZph5AJ%thN2MC5ez8nde4Mu3tOkz}X,r{kLYV\Ny< FΞ=К &R…#+*2 ?ѻ5G5NA:nooH *g`M~߽|w]׃(!T&QTv݊i,e4!A L$ fΚy2|m./d*c <+Dc 󻺺y[nt]s0Yf{BP$HV4؁`i¿l9ۭW\qݻ$Iĉ ذa /gqg/UDnW qa,R'OB뺲,O>C+\g̘a+&㡕s q|>(2Lg̶JDQ}OL&y̩T*Jm):x]G&~TڗN,cr_ה /sξ!s$M)( $xK D;#* |˭V7>)>Z-嬳d2?} |3gի'O|Yg7ο]]z'͙._<9{|zB|T-3r[[}5U[Z]u{ѢEဓsBP.d̙(nݺU ˲`ʔ)=c6lx3زe٧m޼4ˊ"W_ h Ղ +aN 8b \T*# Ey{s饗~O=׿^{}(15kX NqRU0 E.mSJUUfeeT\(RͥRIdE~b_ܰ,+NA˼%X=Cl0֬YjժL&sE]~?яL}߶ot@+] AoWd;3 -4|ӟfLJ@D,z(7v⋟{R:b3뺸baY8671Z/ȃی16'BX:SwwvvnذAeQ]Ї>#Q#9AL=jr:i]U֞ꜪTRJ*3E dB$!^鿊vӊtȵyWi}ڏWvh>}m۫H@@"sRIUj8>{wΪu>ԩ)Jϋk=+ $rEhFbX8rCl޶-Iy+J (J!˲ kKW̓+e;#-oy;_w۱cәA}tD k=dGC ^266vQF*0#DQ}߲,۶̕$ɲ 1A:]B\BZF'],/_nÇ5M 9{饗n۶mϞgЗyA|867RLԧZO=N?ɲx`aw3`03 2 &馛v ~宻B;MYzBHDҥ?\2dB|!N[[[ᄑHDQl6S(4P<7EU,{zqniiKG>OдRശmrR( ڰau]W\q?/| lT4ڸd6t;$9CY9xo,+ٶG!QM$84s4v{p̭i~nݺ;oo Xfg>sWϞ~7e˖~_Wo~m}skpΞW]|O=~_~+gElŋzL0 F#h A'-x RKdy UUy?q饗<mڴ7D_uU[lē`rљDt:*z%^8dFVZN5MK&W#E ЀReX,z^|;vtuuhZqlq$EAXKX8.ٶ:}_UUqAX8_pvD"Q(l۞fT6>ݻo߾ .`Ϟ=_>z*7B_뿦x`+VKMu]8?/] bEG>᧟~G<ҥL]Տn>zDPUXŒes\6` _ Woor)3J7ʡ*DD"5~ۨ(4J<E! 8+} l6|y.8\w.]_'?IZm'Ir,}?ر'?IϽ˿a\_G߿w{]zOxר?&J/!ʎ?h_3Bq/IkC ?4uH$[n (Sȩ-[vXBKK Ţ_ʕ+W^hxxXD\ * !"O If\X1M5۶ <O'۶Ap]G1)4@iS[@MӣTB744t 7 x}kcǎuuu{:/yժUV{+$LG}ӟ޻_WO?Lgg'D"---+&+L0 ߉jV OJ\-]ofVT0OJhFORBLP mhy>uv.] w㏃^QCOJ%qYO*tB *3HE"<#Wt]EAUUMӔ$ HfYV XAbP @>ASm<=|h qPo|#sttӟ4|Ia|3y~[n;B۷o߻w׾u?SO?!P^xq00`0a@Cc/{A_6(KO>R.yǎ]d30 Fc0P2U m #H2)~,d0Сo 2ByXY8#Ď^ AC ܊ie<{{رcP߾}O|o{ 7׀y^:7Xt]u]U@-u`Ѵm[2<H-H>N4 yUU5]Jf:~E qy.Bcum#~!zx!ŲDb۶mu+5?2 :5D!T5+U@sTlk1ƱX̲l:qhL*ނX, GBDJПGw P'[*Bm[AQ8\"ZߙY׬Yӣ(e#Ma/`0&&:yʠy7x#F#]wM$NT#(R7BQTpDhty@UƎo9qDT1$,#-Dį"˒,i$6- ,8NR1E(གR+!A Z/ [ w.Xq\__xƍ0 `4svz&C:iY׍5QuڰLt$ BNZ4 +%u2MSL!όD"###H䦛n0PK@}AL$⚦AnQ4Et d Az3q !TUuO$I" x@@q[(*`tu!i(Z`ç*5YPkpWW'(HPC  @/(@)[mnlC Jqa[ "q?XqP*8 rjjeB<3 3Qi.(JtĉiIX,Z,j$ؖy+$G"R,ClJ$4 vZ{/{PB^ .\z_<B-3B(mٲEU6 `L uX_J "+k^=6[#r%`kFT@Ti|+&Y^MTU$AK$'Ozrޗ&rےbq V4 DH, CE"0=c Q {\B$~H@>rȫtw.Ecm%덙N2`,(|߇jp@/^"\׭KwC"]@g T舖p2. [zq}T*Yq\kkksYK_yc XE)Ix' Ad"0EQd|QJ$PŸնmq<|\q- jI^f8;`0g<s-(v@|ph|5k\{?ķN{$s Aa@@`e$'*F}QA!`\!*}Cx3M;h=Ō+ 1) KZnTuHA_ W {*N:rBjR8| 3Z* IcZZǏ;ַ7xW5MkPA.y!bqlp6 bf'"EQ YQua0"$Iu]QdHbB'!۶BC jSJMՌ;[]E<ڵkppv4cjb(hu]^ _ ;j3.!r~EV*2PBXW9r 7{dI$ipp8pJloi$no_%W2ʪ---"/IB< Brx:m+)i*P_QdH}϶]-Y)l4-"˒( vBHxHTI[Rchת2g\Q;<۶Al5TxE݁2eR?%油l?>(< \׽ᆿ%GЫ3(|'E귁X.C*5q8 ];=aY#d[Q%K=zCз-u'r9؈F!B!ېɌ1VU\H,,C]( ĸMGڂ% 2p Śq%KRE]D'l|t8}:N?]@Uўoc}up` +{[^AۄoKBPL&[jEÇ/[|/R4f ]Mj6?D x[P0/zqԂ "P;QEVPE,X瞻dɒ U;0 # [nܥoUT8 F !J\DiCi_!qM+E"j@߮+VػwM^xL&HoL!`AIL`@~EDQ!aXBB^q%pv_kEQӞoh@h:!V|="y^$attl TKPѸD_ XN?u]AhcP"_x?syЕW^966vȑb8[TY0xXE!8UUH~L"2m㸾 P* D'-_vE[[[kkk,/d!.2 x^yj?|~BDrQQ-8Kj?BVJ$Zz}EQ֭[wގk?G &uibeAJ,u$IQdYvqPW x9*.:Aeڵk5M;uT,۶mO{`*‹~)h> %& Fa&aPu] PVE%`8vo*Do3kA 2`0Sx}亮8t ]- mm`eiBkH=cBnqppWh^ffXloo۱z "I(˲غnZ( |  EEq( !,˶mkZI%u:B(=i#_(:\B;͹ǵH*3_-}8T&w~Df%J6esܙ+ Wz[o4]UJ$FR,x0OAШB9ZbQ*,<د.O8D%vU`!袋yԽ⋟{9P`6LDQYlI+,.͐p^7UîZA@)Awq' ffx1&*P܀!1Xz-Uΰ},=UFK/xϞ=| /Zh:wY!EQ,"tET:+!+`"D :Vh,x//S+ 1 1fy hNTKChA-\קjtHP 駈θ^AՒDԳ`ey٬y(J<[EIvI>3WN#Ab!TVDN)<:,'ô HrfYa0tMd`S į W@Zڬ2YᏰz{|00jYmɲ, Vt%s[UQ=s]qt݀H`ONginX$skr 5@ *آꟍjs%油l}bSA̤,RoQ968.%F[+cznWsSO?M$$I---l6ɢ͛x Q?y^[[(Zx~ 7CkJxV՘A\E5,2MSQ=;v)Vg=,`}xAh9WXP([Km0{c̅ Y3Ă0 Nܥ%K2L$Q% Y8K$ :uM44 0+ ,J&1,;OfM0K.:;;Gv^w{c0 ,$Io$ cqLiYeYHdn^sКBm\lW\qŻp_&V camcH8Uڬ2ZDW|FjL+Z~)۶iBKx1TGĪU!!4888JdI9](>NGGGDQjod2ŢC,slυ̶m+я~dΝ*!lwimMrz~vN5_*Ql.[o4٦3#n:`g y.Q*51<σw ~B%q\WWW4$vz%ڰaC&9rH[[l6P,\OD[:+1M[ZZo|M+VKB͕W X c.@>ҟg,WŦж2*V[+<yu-Pay0 AD"t::RQT*ٌ(xL&ɤeY$Ns&a,֭‹׮]פOgJ  q馛6W*ʿa/-0(VP2pt-$ UL5e^r]]l&xWÅrN %B!f]mmmtZUUQ1ƒ$IATu=H-Wm޼%j '4 `hJdT$]+^@7VP NYo DI2D[EjI&--- d2^rB>bQ۶>Z҃]w7`0$`_x7wYTWTMh#ԬWUuQ"1S}fI.ܟTZȞX,F.GFFFu]/ aXh"x?uT&!@|P8f.?mnv$ٰaù瞻qƑ ߞTS* 1ǩ@O\AVU7 IDATXqx6}?g^c2FxJ8i !$IRwwwWWW.K\6 #r9q:::uG4mEInK ڶ86h)EQnݺvھ>EQ8Erp cRJ!`+Qȧjb%`Xt\T+qFr( O'WZ\AP鴦ϝ'O 쁍H$??ˁH4hu0 4 EQ$}H]T[i ZzqZqT*JJl6sWP8co:+r@T>>SNώb0 YN3|ն`7@S*G =, jٗD"H$ )?Rɧ 7GNGǚ:l$>IK"phJr'BS9 㬡 M(rz!$X9Bۛ?Q-bvPf a6J6(Q iiB-tOn 4S* qd @l!BWg G"T.4jC! Qa ?DQlmmv]T4MmaBTE@jA0<RnI[e!;w`0IW<@|A- yPhYzZP-|<RdYVW*  G<qBI Q|0 aRT2k*+{xrd[u]W7<l+* dI|7IEe1%'@; t4$aW fz:ܿg6es٘{뭟& hI}2T2x#Їh!;BjxbmѶm4MӰ,˲,#!B6IrtOOhe D!1!wjA`0#BG)բ$=p>]S+L~b)m;]]mt Sa$lUbI59h$Dm&td0 M@B}BQ,8c[3 KuYDJ#/9I|yUiYL&(فJS~ }H )O2饳^N2.^:Y+BHè1`0zՕWŽ!z@'.Tg -+ `,Po_bBukT}$cq<ԋ%GM_qF`0+rK;wC(`w)bX^.Θx&b 1)o_!S!T+̶4 j@nQYtJ&S 15fоB֢dWnƁg`z#P#w6?L1B cA Qj:vuʑ#𢡊Ε}~. av)خxnx&~sy{油l.;~c'ۍ5V3 jsi:Րy-|TUNo2}\"Nt=6|D4h T  H4*@oooOOicccgv c1A%\?fKg^}u݊R=%6%51=mx:c&Bc,,˶me|͐LJ%˲Ç0]]]'Np]_$`_g]'ERT3v.t2qum򫣋6 s$G5J@A' M+Vl. gjG#Y֮]}%7 7lkk8ѣ=zs >F`0#m_4\wMOWh=tށkRvPyYmaEQQkm ,㶶Q˲ B\pyMlY%\?~9_|3<922BБ#ǯSǏ7r2 2MJM j5 yqfʣi c0xIT qk׮ExT5#IR$9u N'$]zֽ{#o{cccݻ54t `4lW(uReH@O`\AsPxGS͚Ij)jAK!˲8ͷ=V^dDQloo}_ke]zZne/ BhtttϞn8ɑ/6 #&8poȆa;f0i~-jθ^$d]Ae}ɀ)ҢHvM?Q-H$AղxbHnR_DurzzYc1{#xF?|sύǙ'b0l߾Gd2d袋}woܸ_ݲe/rܮ];wCb5+ŒTkeY%ddXCtv< CC#5/ XﺰM[SuQ#UU#`hJ==KW^swwwJ(ʢ(;՛(BOOO/6=;~$GRkZٜQ 658 K2vqcgۖl>9YZ}+hWWW.SU5/QXBaӦ i=z2 tAK.ݺuʕ+m~}YxGx,~ȿN 6^*J訮oqW}}رO:2 Q7}͸x<Ѿw?d2yud2m;eYAU/\nY$I!v\3biBhIьcj:cRÚ<8|>$H*TkvOjZiy귈x)\m+><5,nY>8Ξ={h@Bm^ s/R8 1%4axt:}e˖ !A@GIDji$I+媖ӪW|w]Dz,ڦdkMƨF^>mm%>[/$i zcζ~MQ䶶HpgΛi) Nq<X\Ŏ6s`t˄[sǘy LBQ]zVd  +dٵkג @)\.ښɌ!Z[[st:JHpUë+<%2e *6 1|H4RsaT D8S^ڗL;6lYWHNt\+\1~D٢`Il\{7ȋ#%D˶+`:p۷o8|wTGvܹy_|i⎕+WB%l}8۸qBW]uE]t M&i+9 " MIOLJ.8#vԄ=P/JEgsvy~T5.$.W&[v;\O]$1g2YС#رcT`05)eY]۶]ic=><<z_ڿ /:y?wC+ B=>(z*AYNBHd#p9_WsnUUg2&i&v!<,&]+T*y^6w=zUVרP`0̔}!422ӟ,gy:W_}W'uY+熻+S|L?<%@Nr زT*gJwڱwǏhiI][.Hkrmm\rxEmC;v\6} jEGY\梚cl."xSYxˎfuZʕ+yn f0LWNWV_+MGs9q2 q3u]Fc6u@-YǓd.SUc9J%a_GUUڛsk,."<MBMV0PUW]14tCk(1 # Wfzȑ]vU뺺CqtګLzHH >U9U sMigz;P8yT2RUuF*%KfʡXLEzxJ4ٶmѣ'.|mڹsiKt>3FHvﲈ^:ꊩGJ.Z>6yfжm)B2ul..̹vAv\lWbeB_2ϚT\mX`8|n\BBaѢE7nLӶm/Z!y5NiG֫<=~8}СC/D"jX|MJ)N F׈F oN9[<s|I\- b0 Ƥ8#ƬRݕ*ClHʄB\*|ym;ڶ=#5>joo߸qax|߾W~Ѱ}4=.m>jsQGrNO;BFtBr^a__MN),o`0fvi=fE15Rs;4jkZ\d<ogy)C:94kd#7s|]VhE{8ה͜WJɡ0 cX۶M2)DNS3ٻwfxi -ocrx*!(h7~ݺ߿^I{B!fѱ6/{Ԧol&`0Dܱ3_!qФYe AS46 -r9IZZʋ~z0jtp)۴i+b[8]$zzlw40ǻ|yTj9wd{uˢ-9yy؋\lǎI=&`0fj_`>4YPƌr4s Ysؘ,S֯(JXDWFg8.(Ҍ^1WXsWp{BXg.~,g$xFOչ2o{s9`0fh_}u/L!dbNr|kXgg'wjLX`:9o|/#s /ȥC_VSϱ:6VSMZَDd0 a!W\ׁezA$jG'%h-&hdښdy~ݺuT2l_߹ rye5y 9h׻g1/\xL[`N}zG5L'֤aSM*η&dX,֤W(mذرcccc54]k.~y߿P~'_j`0 \aWp+!_i7<0L$r3d)m_~[PI\h\\fu]{&h["y~֭nHx؊(--_|ܩ油l.Ngn4%ۍ^sw WDZ, Vfl*XiҼcjnf&kʂ1uCӊxWugyرFN-jp40mf0 Ƽl$癦 :'pX1eЄgxŢ ނGe0 BAf`ti^, V>:6bdtt>6њg4MX,Ra ` ž'+P_ݧ]JiI~ IDATDh4V/`0}eXVi040!LLA'lKa0 `0fa_q]v̸Eޡ&NgBP10yb1`0 Y۶]mRj4?|tR'o>ɫOazgp8?>g,!Ms 1 Ƃ췯oYy60>O3#싙Nʌh~'A,4q&LVN<e8]semkoo<㸱IX۶%y}wo-o_{7Fck $.h-Ǟ&L2,2eIIrfNNLhGN&I%{dIQ\4K-vUUVWXRIfu=o[R/b1JJv!d3PH!e }׿@6o $Iy )Ń`_^xQ8y~||X, 3:U`+$+>x ǒl9$KeϹTsI]I^KE̙ߞVR B8::z )CmCߘm@ Uuů~]ؿI:N9nOOWOO7DZW^w:57ףK SpD"No~Y-smq[w~W8V?h2ؖ@5eXXYRˮtG;6qB! rZSfkn/jZB7e2*:43?ctuzzZ[[~٬'ܹ#\Tѣ`0}P5zaICkdw#mڱ˲lFnmWWGCzzn755 аP{ݑ>3i2Gh4yrR:/ wC.No߾}ĉٹl@31 @?3\^A"0Ѣ~ لJ_^l6˲,EN^(  W^!;7t/XBB?ˑIKiu_ _v+\>L޹dL.۹*`x+,2 7Mbj{gG}+ |!QQem6V9IJl2F ]IyFCQT Q(3}?c1GBiM>Iq?!ȳ?*+6t|> Ѩp>`0/*cYATWPqIJȒTNr*8;W,rdFill|t:5"f=y"( ɸ\N@:6 $Ih4$vwgI[_z+#I۳GBOS#PqM!ti(V ~oyyU(=133d C[[<F~_$Œ`Ac3TJ-:=wX~=V 꺺B,Ti멧b#B!Lz<'Np'r[הw z/C[R[æ c4Mh$'T!Ss>'L}e``l4=yd,@OOƇ~T1 k_8Nq uTdbO+UTQ*Bj455׏h2 sXbxq h6tznnp8߼yα gz?kĩ?͚g,ODw P̈;;|C>!W?ܾ=$Br777x^fW I,`0 ⁵ȕ\TxZQk-]닼&q\NQx7oPSLt2F=##c##cbQ? 677ޡ\.w-N.Vt9td]E#9c3`?3`/R$4 }H9'\t C"2tl6z 0 ,XJX,qe^>Y˔xrEBa\$n0;;y9'Z[[}d2 N>mۣ,ɓ kkk;!󴹋:ۭSOO?&n_\,5o uH&SvX61(*M$I )Ⱦ%?RR|&AimU EJe(&s8<|\ņ h4655IV5wtto A/]zX,~'IC6qF>( mC#rX,p]Gz###HX>aIGҢqJ@m[[[].`dd ,_J(Z_}?DҥPhzzqk3nⶸhA{VGW88QTmVQZ{I9[R6ll{C@__/I:zzznݺJo|K|;/L#̌㩯8~;wb=|$V==]`p`0<Ar8Ůl[R(VWԧ ȗ!, n힝gYVt_QOﯫ Bdbb2L?}xxtmmXaB"VOL&P 5M&innND[[Jo>E9W `0売?3\Q \bEnoHONFLEPhq8NˏMDBBXWWqwǥ%#joo;Ϟ=inOV4yԵ;3r' ɬ3mϢ/kbbB\'nTg?^`0y0+bTN'jR VQ)BIz>~rz,@8nmmTcΝ;wFL&ӎF9+9ʱb:3m{q\|KFſE0Au6(I$EQoL&)(b|Q \z۷-K{{{<Oret9x>|`mmҥ'fffX`0x+ǡ_ފ|:2E݋Txrl5URXHe zEΡ>rVʊp~~p`0811Uj26 ŊGhr'Myjt-Yĉl֭[fG]=w7~'] W WQ<+|Ֆ(RgnK5|RXA&QָbH$u +ևZ,QIn_Y ocn5{Rz$I,KN;P}`<9*֊I톓,+;R cYVwp{8)byI݆"`Þ>c+$K!:̜P.ـ`0eRھ¿‹A@mp TE+G"iR?ҫJd (q8>H\Jr$+bnr*JAgV#S@oˤ!_پV~7`0r}  yAI%)eHP0%ܶ3g$ xT&Yt\*m)Qf[U+ʜJB(>$~UV8ft:N3ɤ8rd2KKA!㭘KF#|(2$ʟ<¡:Ύ|ay_& @%mUⶸ-n~;A<H_}Wħ̶O$`fEvg-oʵ,+JRCBur5U[wﳢ+2èXy,--,v啭V+`0ͦhTk6=+M|ҸUsWo빝b0 fW!M! ^z7T^[4eY@F.g)'ޢq!//5ZĊTjyUIlWWW,7 WWWwPB  ȝnAr[~֞k;͝y zECc0  BYEo.?ܾ^U/_e\~3 DzBcDRFe3JɎf-TDHE"C I꧟~ӧZHec7u};V3W)[nڑr%M'.M6=wB̐5$# z_e`0N ZO /[׊+<ۊҭRA 얗g*^XP*-y'333===}}}wQY I̙~s m+̥hO4)^զwQ}z6"ڶД wk/-Q3N4$x@fmҖV4 !$I!~)\|yW%˶i-*WkqIO̙b})s]G1H[[۩SVWWK͡\.v>2b-,$έs6m=s͙֕fW3Visa5KX\,YPғt/Y~`0U#TJ][n/鴠OZ&tU!QVKezE"uRuʎ쮫hǡw/qNOOollttt݂"H.j541>A Uv]3 ˑ^tc4U#7,mh=?mM;bFt!]+څa0!TT*psIU+yR]AWER4)GTQQTvQd+H fҢb&ظsgxaa [#VcOy;Y!sZyƱĨ!>ʑQ][Tvԟ4\ TI%ĩlZPD ^7+Ȍ+x~CKzI6IP.F_ wUW wd2ΡHV=mҧrfCrұE*Ɗ?\[4A1 ( _Jݷ }qТqnArh_NlUyŒ UUxڣ(}eam1c 5|mu^`0}Bp F\!|eȹkq)%0/TJ_O B2`YjTʪ(>3 uv1jJJYV/mE"B ot:%(N"iQDے)+oͦRc[|WM4]^itvѨj%%V(L4  K|~fNX,pD `0rx4@dPJ ; 5'\W 'X^]InxXKک1e[h׆AVu\$IL&L&N'Mth4fY^H$uZ_kii?97N'Ğޣ{ E7wL`pܖY(WexU(Dt)Ίq/?xe{<Ͷ+TWT[,)TE\^m ^lSNxx0b VWW#Ç]hooի4z:;[Z tzn#ojikkۣ0 ~㸭^l5J }lTg_SrJI ڏ@#|RNrʮ{|*+*EXǏvtVVV 8|>_ccc8FYOxbl64?hZN:? gggw_c0O<v y`YV`+!^"T*E-:}ׁ uJ˗,H, k a)RmwEC(q1a?wn[YY~f.Lt:=66&|>z/=X,LxSSS~j튡$ɾÇ{hBa:33s y`Ya^ER)_̌;}BX~+@gAisKzBHYX(Vj eA"Fժ/\8GիONNs'rKI&O|Gy*;w|cǎF"Zn]f?7 yaYX,ҡ*@[_ŷo!_cT"*~%b%ܶ|/^fkkk( E=8B9dK)2*6xBRoT[!B^Nz݅By!CcGz^ڔ,:077CVvEby)O?:;;*pOzeQgmq[ݷ r(+Al%%⃦&TR"ole[!,k-ꫯ020C0RS&ɑⶊ= *˗JӴa 9ՙUҚ'VNW2󓓓́@f΢6`pmffVe0r՝G4M6Lɮȟ`E~Q hh4FcOO0wfDf߾}>].qԱc 皚YqrO>q¹+W.gAtj5Xڵ(H QL&#Yۗ(^!`+)xfW^ٍ{ #]*ޏF *E$@0+;^XBh0H %qb8nhhhjjjjjJۀrn`ffΝD"&? sV_1Ol|>ccj|^xl60̃C&aF=lO)J:Qeƺ|`RuJ' g\Q˭FީT+Q* zBb4Ud2W\`s~_FcP(OfY0$Ij4`6F#\y'KF]~̙)FyK]̖KQTgg/t>ޭQ0 R(P-=ᛸ0 IDATdD.V}tdW_}YWٶ\[ خ`*+oBň"/TTTXʩt{Pt WvFmyjSS333MM 'hFIt߀f$9r+++P9R醆 SBOOf[_߸zH$Zí+0Ů1fkll4L&Nx\,1DZ,+d`0).!R>uߍ|%@žRIpJw֢TWЊJR(r?UB$OkjugDnϫ$*+Rz%q533#8Ӯb9'(\n_23"j+w6z'TkyKJ@]|*I ĉXT%-(IV&[K[ysq&YyϵgZ+>X,!P(l&YzrnnV(7--ut%ދ³gyV gmq[ 6p+- ?paaE($B|!ll>'LC? /Bo;`-kl6T %lW;g29I1PR3W$IL|>}k}Q/Wtp d2Ӵ‹f1 #= Ijd겒0 fF#RR  355}_ _c+FZv4){pk/+,⡀\  k+ĒYU^8VAEqYI@ 8rDvWUjXVa$mm-> sq\PPTK)O)_|ׅc&yBV*&rbemFdBkw]|q `0\AT*wuuLOOrp8X}ee%ȯqvcGnqA ?ͭ-|U s)(-"[K3cct%hWYh`0$!QNNdA$)X\CᷨڌsE/1ĺA1>4df6eXĮ V[>cQvǐ!EQ.Kf3Lf+P(1MUOl6 E#F h4rRHH+q\}3gN>},;33X`0A$IH4fAP%(v"W-@XBd:ߢ?{z/)yOO+r z7cYWE^4mChds\(ێL4}*yRBhZ2C$I h4ZEW?DXlrrR0̠SPa>ǭt:M4ݟ G@IjiZbqA劽U<qx嗅^Ԡ7x!<ʶ WFa¾35۬vQ<ϯ^T6=b1WAy}}CEl6ysO$D"vc0}2c ŀ~"׏pQߞPN^ -Msm Ծ"N%(&KtJ2C^LfAZT|<0b`2΂7Y\\?>55YN2X‚duF^P.q U"|aA@dA"^7$eY!Eҭ$fT,7q֢e4 \nIufY)+$IbJŠJIj~)rB мˉD"---MMU蕺:RI`@0` TzKJ(`E4JZ+4M,["qI.AW W[!ޑFTԪTWdA^ j$ 4M;u=iiiz {t$mnn}{Hhg  i%ܫO W&-+YV4 +jGR-(E+ǽ}B(įgrC[Ԗ++{O @ӹ\|>_/BFq:f@U`)c}Ns}}3 EQz^բ'fbX("zVU jx<.4LMϯ"Y`0_v$Z\VvZ^)KZı~A@$)VȾ"7 @'Nt\.700099 (Ǐ|h'N8~FY\\䓏7E^Fdz*^ hd~1ׯjmms#Z\\mkkr ~nSI$,yx< n7˲qqgff"Ҭen飼#" S+bU!_}llbxx\/744 `0hyYC:U,&1ufmq[j !j<ϣ,`qqU| aP+B"AG?`YVy@9e`I$5\.X\\ ,e@FwN81>>~ɉl6Mԭ[o$wUDd(C[+>sh4lVѰ|nxܧNF@dKKĉ>od2DBxNGB^c:VsP( b7KO?} oBᣏ> Xa0!AY75!6 s EQ:a$-H߈G8W_zfs(($ޫ`pmuug<~p$ilD"N$Am6jdX^V ^A:W8$ԗ.\x䭷EgG.s\6MOζVnߺu~cWVV8===<Ϗ, Jt*KT< &r΂`8.D,hjF(GkzIX-./\bYN)f0Al!v׿[.Wr| )0hP% V(KOd? @׻\p8㊡OOT{<=}DǏ>O<{cǎB_Q(IR'NhooRyE=P?qn Rɕ@ hh/SW`0Xjf1 ##)UGrHJċPt b"/_[l6/IJ -<1 Pp^Wp/~ ǜh8% E3X,IZ B$I*F6ٳg) Y<Gt:xJi@WW~F*(N^e'&&"`0 a5zE=B]\p(2-hfFlbAQp_~mKP@N;;;gpffkx鴇an>`0pteՌ"4$I\#2o},V䥗^& dStB VQO$0۷ggg~`0~[LMMݼyrƍ|+H \sC^AX,V i3a0v^DW}x_GGWꪐM.Jttttuu MZŋ q2@VK[[y~eeevvM1 dp ^Bq5)܋$X\(h4:X,k2"d_"-W~h_qիW^*>T(77zj}>V VjbO:}>_<X, tFag$v:׮]_ZZj^vgffY`vERĊD*@Bo*H4WP+_8 i.uVSU^[[՘LH$Gh4lvV;[<D~Gy^p8b&f |>/YF0|*k`0LM yO3[{!Y#tI 'H/zenn[ŋ_U1 H$Rl d-AzfY,Ihʮ888<88i}>nu8hnhYXѐx, Bb@emq[} !yVycOT%PYH$+$Id6  \gSK-{i_ l6d2e0腴r{ t:jl^UJ''i rYAϲl&I$xP(HVla0 fE$T$5Ků ">E _z ~ Z$ֺ!Hq=@Evw'X,h\3MMMT< B!Ѣe `2:.gP:բwU@=ww}`_^ua" B;ރ7+Ft:M&S*h&Yb4ZNW- `0_XJWk*#ԓ/!/+r Bm~ B鐓%N'L&#NwfvZh4M`0AU*5կ*R3<>'J6}|2n- iZsO^}Q4"Xl:d2l8lJzޠ-JuL&,lO%h4؜L&gfzVVVw`0LTUT/C {"m @{pO$ˁ+H`0,v)+!EQzA7arl./H<'󄚔/e4 -IӴF1 z^ ^].Wsss&z~=t:mlʜikkl[R)ѱGfYv~~>1 P4\Ԕ+ \FH[{"Y"BH4Mf3.Nm-)LL~4[4-//ysN6*5W/rl63OvwwW֒tu0̇+e` LqZJ]RHv#a3Em!k~f/dh4'AA%O!BQA$IREQr|QH0(Q(l6kMJQܯʒjv_co~p[b8.N׿R 5/j|*#GKxͷc1H 9@HmɁ@PT  *'BU$S@p~8P(nT. ¦ ^Rܹ/]zʕ.`/;*^H(*^BI3Ȭ\sъJ]X{WoY&*FlG۷_466? ],+*8{|:O~ʕL 3`0$BvÂ" p$b" "~j #+gY$V9^XȯޝזX,/HWWWS,~a/]n`0}@[ eEP- ^{5~t:q5eayKE+R"qn\C%نg+ӾUIo},/|G*NzB{KV'r43 sp@zr"bE\AJw+įS*lWeF?RQnDERR·x~?EQ==k,N7%?\5hJ}E+@.{w~ftzW1 怃DC'ɋU".!BWXAU8O*d2]ys&YJ`ܬ"V~*hs-eJF8|g]t-@;/9|Ad2oO?t:xⱷ~O=]/v:: lfyy9ZyQl6[wwlJ&Scce.^3Ƴ*"'OD͹:::Zm6M$HTJim Mð#SS3}MS|ajP"+PY ~~OemN+$Y4V5fA=#Z ʒ,|>ɬysΎxeb,5>]{C #'ן~\ރ%R4ݮw?3*v:Zx Μ9v[YYk|OGG^zo၁Hf={fpp_JFb,,,dRA=(jnnmWvNY][[[XX[C@3gP{$@I"~[RJWįa^`YAzW_.e2j9*A>_hhu]>%{۶+ŋ_][ ޠ^zBaCم'K,Pwh>pO1G"ѹ%qǏh4V hhjjP ܱg'5F;r34;3"Pՠrmp~~YQq  Hm%e g7h4Ž>z~]{BXWWm488JÇ{XL5sݾ=466!N:tl6߾};Vq:@axFh&''z͛+K-MkjrjN{VK<(~׮]rFvNJɝm zp8$lqLssSOOb+?An0L,2%oznsUXZ Jl*VhoW-Su5S*-Ro*mTzDO07 ֧bȲlXD\գ(B8$\zP[[EBPhmm(>EQETc[)[!r+k4sSvq_h>q☊b1)3 Sůt:=44ёL&+mot:z5MPX\\LFqǃV$B011355uX_GG[ɮo..}qWbooLJ6Wd2)621 H$Fc*U}~ `4yFcDb7f hp\CCp$1ϏOr7;; :}ȡ411QΠmgΜfs|Y003r:whvm`bb=PEln+)b Y?HT$2"+?LP)baڭ,5WzD͝YX|a]~=!HiZ<=BI%RYZWWNnwuz%:P&mv%zؾn->>Fr?u !rŧybV]x<b7qO649p8 Xljj* Ց$'/=B>xI㏮_[%$ɛ7o=z/sDڎ;*bCk5>XQ"@oᑑ 5QKK$I$VPIPuSO={NOͦ'|b||s8LX ϲl2XX3\[ZZ:{4Acc*CPRU, FWE"V&YE Z,! ~Cab Bbe*FRec:dl= e|.fLƗv6CD'ߢi DZZuF$UbfQ؎5=|ss_}=W;_˺[k7-|ɍ7J6}o ]`0Wvݞ}doPGwtuvFaWWWߔ |^q_z'O|ޯ~Phcw l͛O/A 4],766r݌ҨL"RJ@Y-"XVıX8NTGDXy@Q +}h\,+ȳJXrT*N[w0&qVyIw -Oшhzӳf) *7H$<Ouz.F9D i Q2KPd 537=j \RQd k$sgfjjT?~vuu466y63bXծξގ֖%]P( ?^٧ y~vvȑ#td2Uj4d2ymp BSO=cW΢9u#{}08}#Gåv#Ѩ`.22z@] !4^ԩSrwUEY<+@@T,VHzw?C$LL=~ۛa2l6۫醇m6z"FysgF$;J,8>33Nju&i蹎DTb1wWD"R*ʊT*j===5a(xb1өh2 +?~ѥKJr[|;5;'OOOONNl@ %h(g;)a+4?gr8j ڣ,WxZ\=c+|[9(i(#QUX@kBA QZ.n]Zp~U &ub*^N3yz OU>@+cq."ʫ%ň$W}-z àL&c-/F{]v=.jof Jr3Jq\:~pSSSgz[w'ܜFQ*'OD"])qIo޼utA)E0n{z<]<,ϟ>}pdNד$B\4֭;}`CA* JN58oZ>=ꁁ>E>|,|EQ]4 KgY!)irq@ Ըl_茲tGTaYT*qD\E_?E0 ˫^a L&3 Px<0b1ð5\WlT)J~U8? dnXґ'&7~1brHF7ݩ*=rp6+@PX,шaXPX__i+8h4*&I4M_~C*zEuqbM[D|.'O,6z~{{[ϟ>oR2ӣѨlڎQ.%Io E"hOTUD2/ |l6Ag@&ABM$Ebqˏ ^ш 0|DkGŕJkcc=hŨf'''SX[he"p;a *5daJMJ /W :,mdW^ 7(˧-?~LTcYXaj̩S'E"P($OB;jh4H駟V ">V]1  ,1jdnq8RFJg}ۀhZ X, a\v0V4fÆ'"& n+|bgg'8Nr, kk㬬Jw޹G|W'Oxdfl?H$"U>٬rlfm*HRsssHh4\&^D"zAFx.kBXHDdee%U* z9uN$}eaZZZ¢iq xҒH$F:qOL?z0vy5fLoTjb_`8h5)_qoAQ۩xV.ͤ?a AVR)W(~A],qv/B!}kk oBaA>D"ժz`mh4 h'E1'N߽j4^qrO&"#iS|@-6 LG^ "^ĕ<>:: pl c0ϞJ\`CPiTZT,e2LD\.zVc+(zT8Pf;8Ufm9j5 lo v]_SNz<OL7Lksl:*ek=.H$4t^mlxv"0N;%M&s_~Ng2lbb(L Ǔ J%@*7Si5>> H<j6P cx4 լ-O0 2L*&IIWm__L}Տozz=}zO$Ν) KLf9U73tyeeYќ;w{ |J[!<~!Smv3hBV8 5jD^Jl8n f p_\\ll6 dF1 > ]|0ke Ħ#qn(O=ӆ+AiR2^ct E|J8,/;<T*gBH҃4ť˗/('NDJN'''Ak1ݼy{47kkH`Щj0֡>KmVq\(>VIr0&.,4UU(䣣L&N^i?z]r7 ꀒd2 O׮w@-ILjI&O>=uTTfvjRCS?WTF`ӧZq-f uu!}RPE Vaޅ8,t=)KCH ӾnrT*vgwR}9q]P E9/FG0?C"@| (|=壝:P&_[2Q@4{hlry-0c||޻y7@f "]]' UcxjJT*Q:(*r #@sssprAR^ƚ RT* EQ0K.|E՜ɤәt:6 ]yWWki+*K7nuH$rT@sR pm+ ;Rs e_Ho*npAh4#raVY"˹_81E2^z3L3H4~q`/ aɓCC|0Pd2rܱlsSSv tzcc#uwVU6uff +KeU x޽{iNܹj߬ffEZ+igp5r9_ѭ>pg׻?M~r!fsX|h4kJRdRHR@RfH'(l|OӍw,4[p hhT*6''WfҥiL[ulOKkp}@RQ.v tlE]PTF SWBZh~q<gE 8>4%nZXQ?kBPxW)P(T*=L&&$Ƀj4;T*KV## ,~? /i֊l"](3gNdhFGGgȬ98!`Zf\NFC X,,_,..t:(0(gT*?|bbBՎ͵J9}t>PZ/ONNVWb1+L&X,RT.3 SSH$_Ak0BE na=dT}WgP-+S+_}q\'v\ r2AFR]_}U"Jev6 3fRH/f[kba*8{:ߺu7~9q\nii(uP o4s}.r h4F[H$qollDGpU|sAZH$:qb|hhERݺBahhX,~g-DTj2Qxsj]^ PmZJe6 fY"\B4M]T*333jz|||vvER,>}d2^tۺt_\UW\L$5x2,LVfT;?@:H2%+ #mblqp hRSRWWUr CTD,>y_d CguE-6u'ٌ3rrHH)O AA{8qbMO.rPחNKV0 xE1E0 RT|+iA`fkkl6 $B8 1_ nyʢR&&&fgg[|y6]N766&J;w޵Uo qs:djeejzqqF gY=!KҁuQTM,5zOݾ2JCW,^<ɩ5HA4fDQAؗ!5D=JUW04"5|%疵%fYB eevSZjvg-(z DR.SzN}¿Vw$I7UֻY8JԌU t:bQ3x[@(Z@ ( n9sJRrbE?q:Xlqqvf4U"?67}}=;;;||tXb x ܾ}o}0333cccĉ333m9G8M62Mӟ~}ddhxxpdd,33 0L,TX,P2^qQPOP@du Ì_imq+Վ|ݥtڵ| /K^#Pa%5蘆nCC吅MyrX, TV$_'xwl[j !^sU21o Q}<(\AaآK;sj B"xa[*HfgАd3۞=PsgϞ0,Ndb-ӖJ77_9HQ5L&u.slldllT*AWQMTSq cm)T@(  ˿r'>&J?$)zk˭k T*>T <@QT*B1oYqwnes:?zd{Y__lN>raX2ziWz t 'ZZQ>3āVin g(0 $R4L_V~bR$=AI~@Ӵ@Uz0~ĸlf| B"$˲Jt֏vΜ9%IPHVY@RI\x% ~kϟdXTNrt:b㸦KB.Џj7**Ot)(pQ(VTfX/^/RS'zz<$Ǐ0WJ8Eџ~kl@ (:0_XSSgXwP( ){]d޽n[jif$㫫kdF1ɘͦ)\rR<}:sB!tج-,xumVo} A"PFQO, *1dr9`z$IR,(^}Jiٳg}}}z~`o?]U*'OS&q(*E$0r9J,dXU`2tHT.qqT*={6vpwMӍO>,kw-`P(n X(2@R]jGsg1 a1OP/>{t&˯r #\.V"K4݁, Z,Fh8z}OLwd0Je<_ZZ:k.rf'QegggSƶvbX,ӧ_} ?(rOf4Mޛ2(jO+Z~,LM, W9C# aD]ͼj1e9ƑRc$JT>$LbeRy`-5 BNsdd$u.XS  ]6ymVP(H,RL&`yRQ&(*`(q xA| pZ-T~ Ku2`De@z{"+|6[[ZrjmַŰqū`q?uj2Wj`6b^AD",5{T}Ŷ ;99߼y]V뾚>z+4Px{SՖer>R`[ Rsg?cp a6oq Uv`_bT/m'oVU8fd8"k0n"^-B"( o PJ捹Ϟ~>( -P@ӓ!kOFBQTP-..qycB቉1 @fR}J˲N0+a1qpI9(܍%_$C6h9:n3Ch.nj8ٗǫ '\MAA3ZFQ4bW/߽8  ›XL&H$rz{zzΜ9p8):.oۢ(T*E"Q2I:1,%;TVDEehڱ@T9:@QT*8#}+at@TZT* [Cwn;(  tϟotcFc,˚h4B!Brv-.m{{=K~a8՜{1Eđ 1?R+{#( J2wkk-`_9PJ2_3z^z^|S}hBYzńz00fii fr244mU?[JM&S4j2lP&- Q7WcZ!gkAWy_J*[ )i7ӗ&(r90 Nhc.ZdTTRqap+ L&Msh.]S.K-CZ:(e:2 \.Jswʫm-/ephbh\\\ vvB}}~`llt|jV*W+S1ZG82vp8)4.݁]"6ޛe@x_68P24 ̀al J[, )D"f+ , nْrTb`ooL&e&r +y  *J$ \}O q#f}Wxa&Y $If&-ʾA 2^*͂jtPaDt:͗2 +:j$ىxD".K% b}xqbn_mE(l4T[Z9Ímϴψr&tBMyWft?:VmB,hot9\vn"L[r ΅ÑL}ԩS333lg@Qĉ67h4Z* ^o6-K88:+puw:c׎&PUTb1wb=J'\EQ٥_w|iH H$f3,ZYYDr\ð''޽V =˲[[苠t~AQ/ qa12"(q.a`0s@ iE"R@ڧ4i}!ihx>w޹8K3 ny>DhutR ljkz"w /3C ðoqbxxٳg9AD"bXlea3D4׃/ Y+g?/*!UK翯ʶHo?yhY_ t`*<420 0E^~OH$ j ô%3::b󃃃0GTy088ۊbn{J]/:22VFCϽl9RՐy1/0LVlͪYxSH$b8. n G(J$Dw͢qo_ '*m/_tF ? +'Ҏ [x9P}d)໾?~/gOB!*~~H$ yt/(dVX,BEP($lRnmm fؑJd/nvX,ԆB{H x<^*>nk88nggvkZwzzi( )V%I-` I|VpD8ERaYEQXKOB G h^ kj[fq֖c$I$)81.s}cJYA^Jt/_"/^7Xyƪ`2)ѨPV }}\ᰥiq{fF̬^SU5[zт`+ᡇw6g:ϩT D!I~ z!{B(r\& vAIdOguum DDuBdf;Iӽ2lV@$jr5uzfffju+Vhle%I5H863AΟ?wWIRR0 M4MU* ط}(uLSx싯 CyLZeA&e% ^^ ~8{L " qQ%ɢ榷[ 78.,r`6bX98 ='LT*M&_ׅB!kx@Vjvp@`eYxۂǿ(ssk+ΎnxNX,aJ5>>sr쫯nNWtl6(ڭ(6pԘɽpu9 tZ-Jtj?~jhfiOl6d2W^(j{{t&eNMT>-s H$zzz^AH I q@"!* 0 1 qɿ*r?5-6\St!'ťlPXU buuw>| PXΝgϞuHS]XO\(ߺݮ 9L'[Yy;22d?"H.//<;;;%tROT 2avɡE"j.`V_`O~d`T=F[?gANk6 dYU*W\(Q î\u\$I"N8Q(bx4 x>2鴃4f :up777744444dZp6[iJ"ͦ>Xѓ6jrAeU$IXD"dr=yͪc???dj,%}Mwwwj\Ǖνճ?y;V]Ul-P n=-|~8\`^Eo} MA$VNs[_F2 O[%Ŀe¹ﶆn nй5wƫeoulC̩S>i^-(-iXpϓD"tS!/l.RTL:Kz?f2Y {u=af nŋ'N=~ VT*[[ϟς=kX,u L6>> _owpQ,q{{[;>B.Klvyy=N7sdDMb1 5@xRT.+ J%M&BqvE"!##C[[m 8n y{w?M|~llt&8P(}}}5k#A"&a gTg=~[ M._ԓXdb*|#m~70ۗA?Dbpp0bkvgh4WBTlnk@n=`1$[ԃj~~b\z9N [C*jllaO>CJJF!Lx\R-M59qb|bb,ܹ}0̫M@@n cnXc5"L&k|a`0"E>ƾQceyhLѝzu/Ǜ'mcGaenVw"$=YWl6 bEuN$):yD}, gDR*P$yÇϞ=pO: 677+ ^?<<\*fff#Tzdd n١^~})eht8-ڢhD"Hjp*3ۂd:Ѩ>4====:::11 777[u$I5 l_HO2/QP y841?cH$CCssRo(l۱Ǣ DZX T` z-&YY^))HD4T*[h-H$f|e6==H5i@:0ɓ㽽`0xβX,lvnnmzKK+ϟZ_DSueL(+Ws&բjČFĘfM$~gitF\,0LTR(]_zq-zgP*1Art,+mkHy'(GXkʛy=A>iv-;:wS_7 b1)j@"\OoqT~>c#Zd$n7`ss(@ 3,WZ'pRX4zp|"L>yj>aڭrx|aaM`nӧ'GGWW;tTJDRT*$ U*J8Nktt鍍EzȨdo[㸭-(;66266mmߤR)FVk2j]*d20J$4R)?&@$6WTvJܣ@,{Μ3{#ےWȫ9(<*mUE*< 򁁁zrA VW[Z3T*f^cc_ڏf2IX<pXu$no ╭0pcZ9R(0!d@.=F#V+II@YΪ\.bkj|xݚjugk˿+V)urOR,Ը kp/>=].W А`r244`ۗZzG8>W]ɭ`ddNvfPORiffnffh4JyVk'LJ%G/H2LE. DAJ%LT*Haād8e8{peTMs4ɉMOB1})q9F`Pj&AD`^rhÄXL:5׋ H,[__oy㣣jz}}ZC*&-L"AOQ@ZM0S, =I(JqBߦ:.v\F"H$JbVW i``@T DkAQ\.Vw [ݖ,` T%hk6  m\gr9e5EsQXDžBӅrYΝ;s|Na`d2\_SNryxHQ!+%뚶D?x TPlޞxig1p^#XgR0dw5 8H[< iD-o IFR"0 8. -tڄH$0;e27o߹p;bp_A~QC)U@f /MOK߷NH+TCԔӤuq-KXoexAfAMώz|zftt陙oY(Ν>Q*Qp͍<)jQEzP& MW2fjsl^l~f wܹwyR֖ES&GG#ѕRP8uPeF,gFW?)b.F]뿩?-m!:5MOIIqc?{_m)iw2hVӺJPV&k1ðys F~1נ?Z}'\Q9D"8ޮD0Z N__fs׮sy0 ΝUU=YXX:5v2L9BpcVEI oFmФes=7?џo֏=sGMy?W~G񺰴411122277!#4ow@&}ŶB!AU] 8!Xcii-H$ -H<(3{vM74rj 9DcPU$ud*NmIek̬Udv&#QG㠆DE"A_N tJOQ~y_~9ˋWΛ7ruwwakN$IΜ5'1q"=7c[;6gSE!W"eEf4Kr~mD-pF?gb"3h|_Noc߄ ͵O+ͣJEKgHKUrp4Z}Rv~>@sI( 4]F~H$i V+rϊ ԙZ~ !vkV2gΜ9s\')11)&&&66 pE +IDATr0QLNy^Vv"KNz?A+Ԁ2_Ռ7ݔ!dp78/_Ox}%" Qi´srr}vRym8ߨ̟w{CbQ?I$j+_D# q} q駸s5N9I*+//n]׎nvn4MzVRt:A \n$^G(2 7taj3WBM/=NwT]֢bfQ q!=X9@"Ȳ6+Q+쥧uv:3  "zy͛iiJœ]sw+t;@?#B{Г&,K緟4E33ߡL]1-[6o ݞ(dصJf`` ##;wzLdP&CAt4QG*>E…ʂ rFyJuk`K _{TԜֿv_͛BE(}%W_}GZ޷o?<̌YYFC'gN߾}~۟F+c}SKR쵣.-YYF924nhhصkWgWWngWW8v,sKK[o/^.wީ?t|.--h4>'$\͝9hŚ˒$|'9̭8;{wmnn}}_\V;IgЂv?sgn =Q˳|d BPޫ'N}珏4"}E"y^peš8~ ð/STK&x7h*B*eDqk!pi U>"O9GBxQ,v:Uh*E , j:u->8 hr01fTI1Ҭ-=YmK8z R %#4!W%!\H#!\"~ x I;nLL̈V(ɅOHR5A5xc0p\s3Lvp'IbƌxRCQTL < LH#"IR|n2eqhѥ&8gEWtH8ӇԔ+ qwܹr劼@hJJJQQQttd>{dsRYyy\`v{<oosl6HWEN{(y<^BDOHIjW8))$`E9MU%*+%55GT>N͛ ì[… KKؼ+qW^>(np8[[^)dy^iEB8DHr`Q$U< tuut:Pzzb{(,˦ɉyw::::za[n˓϶jժ׏ s``_gb7x#pmAsA ZЎBK$(>4O22AZ ömRXXHQԥKdM߱Z55~ a;vlhh4/'h^zC^o8_@D(pNt)eł "HaY3ԬR)GuuիIWV'h+;vݵkHkaQX%ɨׂWkUZj%s=rAXQP(Ν;/k׭[U9… ?x|1 {\JwƯ2339"WVBVU~ҥKFcYY?|͆a~+"???##8 EQjkF#*SN^^^.&1#aŧN>uʕ+G_ (jzɒ%Rڧ00P _ ^ׂWk+_bXmww'Gw>/W, _c*^x>UW^x5@ Wt@t~PQ-hA ZЂOHWt IWFQoݺUYyYS颢la,˹s(Wyyy˖-S(偋OJ~*^ðhgx܂a<Ouu{z^e˖|Py{˲xAyݸqbpW]}%##aVk0]rEއ999sVUUy<U[[`'ϰ^566y^ZZZzBJZw޽{PYyY>ÏJKKFcVVdBQTttt_U~kXza %%f\`^t:"|W &&ɓ;T\TT?7{֭[nyf__xH[[n~+\.닍t:|l6 4?la $9yfwwBZv:#aBE\nݮP(%PA$I"8 0 'Iq{f͚ӧO^Q#v|M 0 ۴iӹs\. x5,&!!ϟt1-$I555/b|| .]Bp_EQV,KGiZP8.n$''gee8Pi DP$&&Lfy;CI\qㆪ3Iui\ U_k$I,i$I4'**jmmm*UT0X2 BN0M(**2L=A(}5ͽ0!dZo߾A/y>d2effn߾ر2aU08+hfYNAp'T($qG/(j3,mjj{N}i&񪩩i…qqqE\KO?}{ٳgO}}}KKˡCCpܽ{WR-[ ðDvWaXRSSj5B(:::%%EFBظpX ].|wA$$$dggߺuKN\TT0Z.((NF N8o(( 8aq3}>oss Bb\zUu` WvKPRR2w\PSSә3g5 Ѯ]F… *(XGtOOٳgG2/ ?^W+P\rjj~bVP 7n4TUU"j6??O,X` N!qd[WWMeff"Z[[+*.; 111ÇW̚oەJEaaAtttiiN۹sIENDB`gammaray-2.11.3/docs/manual/images/gammaray-qsm-debugger.png000066400000000000000000001434501413310443000237320ustar00rootroot00000000000000PNG  IHDR1nsBIT|d pHYs+ IDATxw|[y^$H^ġe-ے,;'jyv6IGtܶiMqqmܔ7@,,ْ}ŗxy@BAAcȗ:AAƺd2,$fosg2v@0H nAֶ(o{^{:xcǛ>DڳobfvήO$9w}O$xx}΋>&3<2A <[<``]kiij"NY7233K_/LRUUI0ݪ[֬ZA6%+eQ*hZl6+lP(Ao `6]>^nR4 uuO\* \d\j*}Z-d+ziC.Q`d*l!qar9+V\>A"e`$I/#۶oSf{NjD D' '  Һ  ܉D( p  &? 8eTnE&Ja\H$J4]0 FhD&-u(  \ A l(]$ 5A{DQJn\.7Zԡ p|(!ݲjM2tdAV&Ǒst:_::|(woEyEI(I:|(U87J-d2|ۅ_lXGc2<(O<-MdY))vO:$좰Ғby#hϬB!tZ#rID0"H`o; W3+Zva2(-q,i TVvuqL^Ax'fK0HD,#H044j5x2'G'NQR?pǎȤd2|lg[GG"I ;9֎bf߾89r {L&=oQUUIi^yϡG̈́aEEvOdٽg/U˯8x+8q8rI$?p I dxd.셅hjvq8s]i\n7Hfh,復J&K usL"x<&!wE `$A ~R42 \ܜgT*gnM&aazfX,v^ԴZ-k֬#N'xzFc(J uu}ʜtb4 LMMHgҤ&%%%e˩jqR_555dۉP.J&IH 98J8rFFGb(J$jAOII v-->F!22:F,e~> >%(JʜliYnId2ɼLF<@J9LYZN݌Vh,D%>O6}׭$L\$)kDv4ezf]=YdG,'X,7?O4ze_$I3}MzpcXj&fffH&+d2HD  L};r`0𳊿k4}Hm輢kJR }%<O>!x<}Kx%G"5 BxǼ۳_1S~y?P(O}&Q|3;ȍ3 z*1NLL?Hii 2;ߤ @GGv{!PT*Jd2pen>œǩvV,IP>z H276u >Stufiwto]8  sΜ飤ā\ħ$ Dʾg (<vja8k nU}MO 1Lck8yQ 9]P_GwO/ja5#c}@.9x0Lt:͔M l6H$h0x"A:&JǃB!rVrC@c( 2 ϜjxT$# 2>>^\.Tg[ׇC<n/$284LQ1*_{JsJE:<ǚ+9z?gfvј=Jή=}H$B64Lgzfh,J  +eh~z=L\vkodbbL&CQX,NQQC4EDQT7pAS,J VɈ\.#+I It:8zɄ?^XJFRP=l6-b J(l6).*BP(.R0 nh̍#d:J%jVRȞnZ1 zz}r";zZu~ kZutoh(.*h41(\Rp8jsWlVkn0]Ϲ9u:z"Jn/DTQQunw1p `6%R>+I*>/օ%`L4]j LVTf9x0LNNQP`cDC284ݛ6pT5UtZ'bٲܘσ$~\n;A,_x>p ::Q(D1,f3Fjje+2 H*B7wӴb9] by#=trٱs͌,-Ype]eV["cIRU]f3f `:`0p-=`-?(un733 K&33R)`ek =3}BAN;3JaIRnL&j:Rgn2(D|[4d)/+# c2 ØM& F`$b2$IˈRVV0:M,$>h4TUV:Ѩf{~? ?BIUU%+737L&6-Z3c1L&dBO#&'h˷ڽRc۷h%aIPi%K%µN|D1&#VRTx'87|y Ek^RrkG$tnp hL*7n]z^[Ppjdrd;dC>R4M,rLWVV$!!Vj;W9" lwNORT\R U_ݐYT@n7}xvL=3f1::v@A8B5d+*q{fEݶMkAaήn\.7kV##NQ^VFKsVLLL.u  O\_W \$rxl# ?)oG}t:^Gn? 2!VCyl6fQ\>fg(--SmTUV2fٶz#c#X޸ Jsa @[{Vޒ-Hq߽Z(RY^a @nh,W%aQGxYp8̆uwq"brr֖|hˎFc~w3l߶{B0:6F&e]"ODQJ>jRUUA iz~{D~$V+}F|׵`0x8z8>\AS.VGqɩ)lXnCAa,j*.m_Yn-Ӛ*L&#*Z3_la^͊VEVP_k`S^QB&F룵\FYtꕭTWUr1 l[h,F$a``2֬axdL&C:1]M+9pʖ$ 2<<$_@A;m_׳`NGӊjkcek+I&])-ĩS87sgR)GFYPO~s|q~+Xfh顪2>2:6F}]yk3'Iu}ޛ# Rn D|G%= H475q)ݵupמ/,9yM/u QR`ú~1­iER$R*i\즬[xy>_FAv_1}(!L$$\oR¢7\Am*r z},u8wT:}Uh4b60). S]U n\ְQ չ ޽K8lju YEQFFH?e" 3:6^$x!0=xe,9&$6xe7=oږa>o~d2(H={?O 'nf36Cٻyd2Nd22/`ek t[`ϞdjE4%RZRL&IyYH4sMu$qf=JKJ(,,aRAƢ1HׇBb1#AiF#pL^# V0 r$d^O(FסRbxP IDATlVJ%d@$ER"H$( Z-HJJ" 7P)nآ\.ܳySLlfӆg L%X|93B! {F^ǫYQ>k[gpM|?E_?~9Yg>^R$f2Lݾ1)V4~AlױZ|'٩`hlR𓿏林xA6err*ݾ RIeEۿ3~nGV!IdY>K֯Oq(/m[T*vf2D2AaAvOSYYWA.eQ@ˍQL @P0;롲`0D2l635fb0pX4 xd"j՚ưX,Yj5Ev;. łL&J0(J"Vp8L2d6L& KV k WMC}&Xr]♞b1Wpޑe`v?a we`֮YuTsm&ٙYz=`p*§P*PTdY&#Dr j52t:C(h4P).*"L⛟G"D"A6A% VD"A&!͢i1 YBeqqkK3-}L }n^ `br LSO>NqҶmy?;3'ߟVQ^,-a pZ |>b\hAAlш7 & JJBRf$iLVK(  a/,$F)f%d2hrl6+:na?%J)+QXpʸREK\򲋶.  GߎA  %2 nT˜s80LsJ8r9vۃu:]>;_pNps-!QKӹwRpb< pxW&22 lZ8]lDZM2DVNrd2\.H$BCC;ƒNIRj2,JIމDFǺ^T*6lAP|3;X<~-h4zޤ%iN:377fehxZB@ ȉSm|k } IҴutQFc ET➞f~~A3z◿ƪZ`tl LFq9/g),($IщJo`3xN4ERAͬb]kQ՜jh0z]+HVsެ v;Z% p bqJ//IR~۾fٻBK\ ~?bfvJÁA/tvug/:~l6m'?7?|k ӟD6gCܴ1==KMud}{? ?s_ Wu^:=ogrjuρ?LOb4)-q_*cc|:]eAAnfw\P(9Kwݘ& l<h,LfdTWby# CѰ\`gi sSk!C&}IRkV/<0EEv %eR,f3dLFUU%MMh4|Ab06>A ;wy=u S8xAO*FƢ( 2 2 ^C7v7K/36>! mkQ[ݺ0Ͳ{^۾I+$򫯳}  U!P(`T*03㙣y.d2\.ϗyZ[i&GS)JgAAe<@`0fe~ޏJ3AדdDӣP(Gl|]T*Bf pry $IH k$T:M:Η9$q 9RDo"_c;ihѱ1,ϐd8~@=93;ɶv DE9zPTM&>ra/,^  & VY5HPFJ"OR𹮤YX >I*: xro!xgrXžg8~'N184L<GoгoA\.B:7wahx^SUYfuo~yD"UU}k?TVV_D?Ҵb/|x}>Vle>ӏR/ZB<g[+tys]MҒzNL_@^\l{ӢaAA.J.WcYC={-^+!˹wDQ^~5`AAY]yEQQQ~#cYRVd2M+SS]]{ZttvQdStm8KKhnZA]m [~9 Zbd 㹕?Vht_l}w]ks jE.QXX@ylgeC><{ [~p$jA::j cFv>|7 z=fѸԡ m펭xnH$Bȏ{d2DQZ-2rfX,F&OZ4jBj*BƐ,];~&4:p$EL%j4b1 Xt&A'g&KF"2,zI"2FC2@PV G"hׯj7v#[o p20Bns 'tmr<gk% z2,TWSZ@&?ҥToX<&|gb.L >ty3Ot K$יB!JA'X`tP(t HA;H.'Y\w`phL6LN:{.{< cWC oxы>J! =u;~ ]Xn= RYQ+évb?%Hq:^|Ujpr,f [<[rinZ\.W_Ƕ9,[<ȑ#Hg2<}l6K}}T*1<|}D"x$ &&hgx_Ǚrq<& pgX;  #Is^/#cd2zOannχ$I2;;K:iōhDxd2EZيT{p)]=A4.kf hjZAyEC#8RSUEgW.׳~Zjo`9/ccbXv^~uzOysؾm+{T{M+ܴB\.U+[ر(JeA&PW8@ӉÚիDxcttuX.FGXz"J̗P*H C!‘-+$";fD"FAP J6ǎ3085flߺmFVKmM 2 gi UUdr XM7`0Z>r6nclڸ!sOi flVKwO/n4L&WE V^I(^T\A;PH$ KpteCd2=..tT*MIiI PAjte ,kqY{t{{vEj LLL'u|'^&Q]U=vc=%|ضE47-VYQqH$IdY R# E ಆ:^ 똛ҲŒݼ'Oc(q8ʝmڰ{7wod:[ʡ#Gyj5<K  ܶ%u/s:/Y =>o/l+ҴRɺk:AAݱڹ4LFߖH$Q((VDu;rl6ˑȷ";x?;z »izZ[*x<\.`0Ǒ՗^b8zã'>j{k_yʫ$)VZI]m=KF2dVzzOnʜ=4}gu 󓟿G?|B|hArd2:jy'I*+oT:=oiz?C߇gno< 6,Gq1*GMwd ??7~Wx9͖blyAzzO3?GVhOOm0d@0H(&UzşVϧ12:J_ 3R5sFy%BVޑ]߂pjkw; 6n餤AIe -Tp_@]r%PPQQlӤ3ʜ#d2ž}xdV5m( ظa2[ٵ{/ sTvOytV24yPrQ45O>8q28^ өST*zR)zO{^HaA6nXO_ۿIOi*+/  Ft:;ذ.^{c'&֮H&憓8ب$ Z[Ywڥ>-A-IŇtZffgz8^d2}ۖ_>֬^c>~n[臯>O|gՊFfnn~C|?䔋M7Ս[S^:U+[j6; @2H$/fqaW>38hjhZ,f3 P20+(dLFf# )--azf,Pu_pje+/ 6{aF&\?7?FiU^]o-i p!}^>jE lݡToue5緕2Dy9KK*D)(qa@\7~}C{G'33|#?3|+_PԼ_}c'?~wou5TWUO_u.e 3H(f-A2,O?,%%FFFO5WoͶmY1 ­皏C|O^^wVd2^ Gj$(-)}0rcZfl|gLFuUw8L&6oH$yO>glVʜtk[LOwxi~o! I0gR bRrV=㙝z׻|=w>xf<;zY9SI9bH"P(R T%ѣBů@[^|~?$l *k>ݛ& sI"1ͫx< ͽyrsc0}{v;oAiI SSSTUSXO\\x^V6mXilj!/7Ms 3(+QZR=),b,1$I450;;oFaA=DJKK('55@ &/7wٚ+ahB<00Hbbbdf:3V{3AA^MZ:vz}.* %$ӥBx:2{RWʚh$3qg: OkoL ..vQB~U '?}LG%`AAx -5Xk#8Xrv,g E/[' כ Dx.;LvR2XHLX2wwcflcA*“X)b,O \3LKNlg]`ݏ܎r`qʵG޷dS]x\.PTstϒuϋj>)*DYAx@ @0GG349ŝT67M,˜ߒo#|[NQ<{%tz=2pN珈>}K@Y#Ӌ(V]杻;VFM7o`xx4حv226p \elbKMmeDEqr&&'$##!UUp2 x^\Yn7i:fNR;x^*himvS3;wy?"$vm.^mp::PJ>Xm]۷m|&ۃN# %=II211IڵܬrԌO=a^//^ï295I^^.U|vjpzt p&ek1 $Xbb8--}yRQQ..f>xRB399EGg6dvddL1ˬ jjjy6‚|n7`0ۙ%4 6vl];٬l۲d6m@^n ++{M۳Ą'ǑVܾEQ89z;wlCVd^?|DR]gg_t8w"r;:vچV`0p'?9]ȲLgW==vUUHe~~3g׏Fa{=?яq\Q)Ϗ䙳H+W#55,II|?dl|?9Bmm)?yuGSW p~xiy<^.\)))dd_GLMM>ȑ'?9cܹ3@iI1u45`2+׮S^s/s/Pb** 53W?@0g#|)>,55LLLbZE^abbrE\d;YYO,GJ͆907mk ..7?0p8OLkv] VKtܜh5p81l߶uEit:r#r.߭hZYsZ,.qwݻ  6JĒIJLv%%k ϯ{p%ILHsȲLnn.Y_DRb"=}oqqqvCXbbzf3uuA::hq=\h:Ȓ"VZ5Key9~ܜlN:CT HIqs>{255 %dfd±'EF@QZZ̹ٲp/xzQؿgQdrjbRRRXWUd``|FCLL̢K ZE(/|#Ęc7c0“l-?77o-p>Ca~>Yyuhhhj#﾿{?Bmm픔xyBr(m% A_=uhkKKi/`0H0h4"2,["|>z~Qv-FcYQtȲL($*F׋$i0wSUV-I:4شqe~$)|a0 dYFha0"q}M˩Sg8?n|t/0_nݳN?߮V/St3yc0j|,V,G~5 Z`(D@cS3SSStOxDLAT@"2AXuh4RuE!**|NAC$ i$][JY'+ G>(CM^v3/U%+)xZ6Iݟi4ERBx(cT$$)]wO k?mN xAҐ$i<Ѧ%vw0NIDyZN90LlټX^2zgn~~wdgaYٹc;Bi`QRvҿuy7λhZY-՜xiQEQ(g||>U' % Уzq,rF>̩3稪(GH-jg%IiI,Ȓuϓ}+2ZZ^vc#V!"AxM8X㕧jC$8wׯr195źJZzFf;;6cx=^ Ӊ$55d;7dKؾNdz p\8.RTUV5_o9 g׎2wѾI[05_ΚUF^nݷj >`*N>CLL [6oBQ84{gGX<}ݻvpy^}'Oetl=v uU|v8ڹ3#ypK&.]Blll`cS3@[:?i4659*hjů IF|.aH+0>bEnp!KL  **ȨÏ? {R"i);qX,c4jؓ8v3st47$TsX֖222ʌÁNu Gټqϝ'.6OSK+o*W^grjNcMaN<ޠG?;  N`iv؆ݾS\]WLI=g.~ A3,Ϗs='1~񴘂y MwPK0`llC^nyy9tDGGt8yôwEwOvBydf377Ib>=h$ cT1hToHeEy$xCr6R0 !I]:n7b0(qt_wf%=@S3}sI|\u"^f+ zqDEE*^cٌb!)1  ϏCbR"G>?FyY) 6$IO5cO^gph5$|~cgMa~)Ňtbb,ج/4ܺ̌hni*qqq ޴FC2'!ay(CCKCa)1;˜KEU6+&~So7}n}=TedF@z}`[-Wp9㋷'s kK_  YsW9:A["/QYVuK|(SI^脗HKh4ZeOc3$zƦڳcA@ѐZ$htZ =HIY`2+&@Ax(#E똘evf <` xSycsJ=OAe^|ח #AV8mIr'wMNN}iM;pwlTT͋[J0T$I".ĥ+WxC0"`  rx %!Fzv؆d& e0 eCT--tú}{e0 Μ`OJcۖAz/]fmHD(`0 ZL~cjjqFnԔdVZ[&66 >YSP8Y/e8].(/Ν>%Eq܏%t0h4.]̌uLNMq^9cB!8s.opu'&8v$ii-e-  188Dsk.^h2RpO?;( x2ss_>=^, sg`+<XUe&& v ڵz>$cV4DTT%E$Dsirsx!&&e͛(-)wyqpes4  +k  **dbr2:??A2iikC#Ih5rsr4qz?_?EQ$j4ő`[޴YߊjeNGzZ*II$'ۑe8HOOsTQlR$$I"77Ffhx oԲuK5c ÁVso*;LMOa}@232#!  GBnAA^>KבDff FAӳq6VI)))ͬkM0GGg YWUI~~}(*;wlst+[[^K $|e9BcS3N6TUeᠣ C?@{GήnffLMM055 nlf/FG0x<[Z p dzlܿc;A: .,/&') I$h4=Nc\(| S\ZN:CB=Ӊ`#sg`[=wVCKk]]ttvsg`I ZCqElrF-.\B$^ΉS1\rmٸMHDJJ Vk YpyC_0>˹ڡ s?`MaQQ,KdPK{Gxf<F#v;Drٶ[~,11m&#m!M&tz@Q^/&^ Ol`bbbnSG{Gqϙs`0babjX^3n!NJtL,EA%!]jJ !x-LNNR&\P$֯fRuEk h4:,n56! ۷m͛tZ{z9oZQ֯c۷28E FCK[sg`m[,3>16md<~~n]8`ljULxYLѬYWMzAj"slIXsll6N:k-٩Ç^y< ,rlPn2cVH/UVD|n_>RykW~y9:qqP9--uɶw8!+/P(Do_?Ⱥhs4 6%.NL ZAx% (N (##a'FGG֭4ftvuSQVFNNW~ ϒd2vWLUU|sNvNttj!2P'nCQ&}^/FINnOb]Ued -♛ Ol Ɉ`T;8+8.::x!RSR} $)25&&!$_A.QڛdgqIRRRHx}>7QpD IPh5cU~\ؓttvGFz=CCdd?`08odjNѱ1RSQe>ڎ <0#l6+:0::^g=WHwͿR{46o~?Cx^sֶvTUm[(_=tZ-O`3664($S[nLDy HK]|YVV3 DQT9q &y{z6Esg`EQ,V#K ZFƨ(/M6066N,$g'Ͼ85NPycc|Q^?|B{G'kK|(BOo;o[4ELWWyydgL}-o?9}wHMM1=3ClM70?Vc#k u~dYFYH`ٱ}hRillf2>>AUen.W^BgϝgmDϬ +D|>7} IDAT^0;7G}-zzd|||fn~/ry::ittvuUֱ~}z.fggr122_މ%2??5TV~(kj&`7o$P<77_;\{(CaHOKcHDbb9S{g (.^kdfSPGњBvlJ͆ûŅKEyN[$ I/{ u1xb! `@R9\ft5$$HHMB=NG\l,fJZ͍2xLOx&1ц1Ub{Rch4 ֎=) TUerjB~{k7}gѺVd[M*˨(/7"˯@F"YM2^E&&&qeŻZHdmT1fPUE`0`d Xٿg7Z܅R2$ufNq;o"2fEVdh o^Gњ:Il^:"l-uU(L0xؼq#j9?l֯{.)(*O ?ѱ1nݎ_u. c$ uI ~0Y]vۻq<əm:n-&&G~&iɶII/,BpA:bךM 1 0 ry'+ 9q'׈  ʒPUU<^/^UUrzee,e~?n>997jo>BW,ZHsQAŷpww흝!7184D]}*̻ݴSYQ移ϝ;|wuVp`0 l޸~v‚|Ҙ`rrTEeǎm\tDY`EIqmlRMo_?8x "~GTokv_=^?|PHf`p5|d|bE\{AAx-Wr!ZZۘ'>>u4ܺͧG>򕫜pTl6Jfjjyj3j#u%IBUp`ZYSX  3vŝAz{HIN 'Cc3>150]:KEnjrl4AA&&&Ԍ%+3~^}#\0ltuu08dffrMNv{^E}>h4tZ-@łVe$l V#--. 1. $rIvlʌ,Eƨ(-FrDTo^cggi1GG0,jz+x-E BLV;UcĒdEx6DדhOAXQKuUtbZ`0x(/ >tή.HOKcpp<<^/R$ekKWXF]9= TVSY.oC_o-%.? JUe ?PHS\^h/\vsv2SDصs;U-ٵۍh XZɧEUUett7ini}/]JwO/|==3<2BͺG_UU:8/[l&%%͛6r͡^J <; PڡD(f?‹;-pǕزy/]7(hZW1try|>$3#,=)A~?7뙜nO>"np233KW4wl23]Up:$lȲ룽NTaEkljf`pUU8].{^sݻv F<'OEꡃ|?DM&of`pÇ^rL&B<<25>DOO/ګX³(Rx`0Hss+))t:tZqa剬45'Gp\֟:sήEo&7'\Ï?`rjzN=OGg@OrUTU};:t#cxn1668Ɔ[_8Gӌ۷ήn_/?@[{#c\|~N'$'Oh -42>1('Nf-;dfd019fLO0<2 3p a8"hHMMڛX,fbbܜ߹io~g什n5628ѯh'%"d,11Z,45 pB]VJí=~ݻv788DZj*kKKLOGk8111577ϥW1PwYחo,πj UUzh4c\rsH^n*™ĩ3(,F FѠ( 57nFF-_WI[{,zzV$6~uN9~aT㡭.M-)؉SI4jDzc ),gnnqRRRHgph|~7똟wCEZnaXxe(eQ1IM $YH ekKo˩3gy7iim#=-8nlߺdV+&vn D($Ԕd.nXHFGHMIN`!5%xe##=Ʀ HO}w ޛf1LجVػw7fs4՛6ҊN#%5 zx6_ p:]T%))l;غe3ǧG>gݺJhZz~qP*Zطw7QQoۺ"*|=6ճ 2b 1ɠ璘7np02:JUey GOoTUY^{H{"iV]$!INP0D DIDGG( dSWΚ^{cbf^=tQjjo͛x<9ߊ$SUP(h5:ݒ&+%:ğd[Ow ?ot#?ENdD+ذ~]dݟ韄7zO]~Fz:7n֑Nc}U%P)lLN|?ټٽkpoہ}{mOw#w_{{qz )V?gjj5-՛eߏ_!TU% | \(UE4 qK@G '..7~z{ss9y [lfjj‚|&hljfllo/\󓒜=)/u d'ܬk@Sp1WUq+Q\>9B0l-vTEVd$Il6c6G34*šh(,xKN%q“$?3;=2V!-5~뷸tw,?K?ː Ѓ$| AՎ&B(ZH$ 9` K-g=,̻w35=ͫb28q ՛i IWW7MM͌OLW@U1,aF`X/b$3ƞ?V˞];(Leb|KA~?n@0H%|_AxN-Iׯ3gq: O".6JZj*OBVd~;FMB,p6ND29~$5Xb-`XĐJNN6qh4p)("Me|sN޹& =ȞȲL0D嗺(q(|_ £Zr 8f7ߠN{TUYY.).,EQ\ÇC'ϣ;}>ԡjٵ 5 ed|_( W;Uso, |Zv̷$IU#5UU1de.MQUUUQu"kKKEYA:x"M1:0?ރGi,˭ 7^/4^4Nb{(",Of RU2Bw=~vpiqs$ae@YDB1:H 'eIQ#"a 'k|Kx9=Z0Vb]Ci1`]KE`adD9nI",@NbOJOޅsc$b%V84kZ^В#Q4^C@Faɴ|(=%--˅Ѹ8Т2L |5*läO'Y:~EUn2TѠ.ACC!N Vb̫#aIt*j2KiWؔjȳ >@:ICHg8k+ '~EqU4ԨI'Ϧac^ #2*îaʯ zŽD(+Lʝ S|ۿ`XP μJNDZD@E3~`ʯaЭA /U > KiNAʤ_Ť$1S(*%|ͥpnLAȿ!JR dp+(j8p8SI6;Aq.'@Axyߒs_L$|Kl\"-&&ހ 8XAX!"T4KPT? a7jpN @H&|I7\PtΪdhw) ~$Iۅqƽ*3хC S~HdoЭbҩQpH:U ,ݳ*Q*Ag|໢;NG^O]-$z{ISv\]b}ă^ZKy<^'`p5U t pZ=Nؿh'qn5ߓm8Fj@)MZ2 bnB8[Ē@(»/YX\"ՙDe>B,V188(-T'iiws Rn̑ e Kbfy4FN^0FzvV/7ܺ7I:qG(3SͦSN:7h8Bj@@۲9-*+Hq`Vg[KbxhDL 8iGV^hzH09(/^MhO(LޠlęsU2kԫep LeeE~BTx2%-(W͎YQϿKjpK&xoO:W $2z8v|߾#|Gjȑ1 (x-r>Kfm 0C(#.G$} )Ij5d1Po,㰞q{v'Ml^ @>can:o2P)Ȭ.a-|0}c08Lj1iQdFU#+ #-xvvj@j=_]H$ ˁ*, @Fepsj/Dތ?~~c[RjHc[8 $J$YFd$Y_Ƴ #y/_4xhHx ,8c֙uCSXx&@#c"! I0mE%@eQYsϯd Hoj!b"ӕJa,C^' >|u:eݍ,+Ezy}m[:SJ\, @4EŶ޼Ke0`}89L{xe$tt1~]rT%_&m;7^&ӄb ?V{UǓoϳnUS:j~g8j˛/RRTQ5` @R%/x$ ChA0B5* lh4>>IRi"x3۶K?±# {OE6L0ZMwsE{q)!ؔrۘPNS-*`Y{RE!lpI)4 &|ާ`4l,̺v{^_R۷:s{71 'Y8K\/u$c\w}fuR̳4mYڤaeba߽Zڷ{9;d ʹm M,޻[\;c'5+ ^p8D8Be>m1 n<@ |>dY( jG*ՉAQ~P0ia^7ZFC,'j׃' D׽_n߽K] |.Kw O {hܙY?'҃ۄ "렐ޤR)ڹv[SP5F$H/Crkd7tlՙHDX`y.rd>*;!2KTU#p&ZR6MT Q M4,Dt&|B$$ޤQ-_t˄Iƶ,fn^!5p婻x~T&Hՙ1F8^-#I&>7E<ՃiL_xW/ Y X\QgL/@\duKg}~ o ĕp@ĵA(ulz쭫a[Lߧ^)z8<@U57`ro HQͰ8Z*d(5=1[-LӠYҬV!4{ =c,=7莹(+(e#@$JTD򢙽ymt3rSٶ[]TU#`uƲLZItwx|~D}FT$م,3K2}`cq@8}jM-F]ypr h{}Jl.΢j~\±mVgZ'qFBnsI m3}s˓w=>~Fo;۲h5;U :H.bYaѨVpjjXz,+gRtTY2khH^^ `t`}~FJfmp<Nji(dZY`ky1'g6<ޗ,d7א%}e:} c+ q0v"<|# Tg.COO7N x*3w%05D'>7 D:Xj2p˓wi5\忧Y1?BQU54> NHШW #0ZMcCxJ d ΪV.EE ud2ksS  h@8(M1{*xVN$bu>=ID)Ko0Mzek8<:>|i0pG4"D)HIZcY$z DZ8+Ъ׹_Ѩ՘}"GO!+2'jQHo?~PzB4by.$u<&/&NǝfnR- FD)׆1'ae>FIdYƶv?5jJ,n{*[,?C(2MBKw%54cZdVXKfm{7 \#Fq F^rkmQ-!"pH"$ɔs =Έ嶹o(e3ݹ½';NK}{} " TU=^zh5vx}45ZkS:t q#}Zg{c2ZEөqhT+tv?%HDɧ7-q| #γi9l&J)s3j}yү8eՖF>AqBFѬ@(${0ZM+ Lcm 3H"E,ՃvnDxvv(*:#L] _0 |(i5j,ܻIgY-sQ5T 9wڬ͠j:Z/i\ EExk4Uѽ~,"Sыﳵ47I8;.ϫO<Ճ/2 odUA` ^k,fc;`;CfD;$:YwsgJR+Yw Y :DcOY^YD $?au I(`E(Gq;Խ}K~_/nItj6 ta4x*I SHo"+ K(ֱ%.ѬUi֪Rn3sKl$BQU,d-pljc #z&z=F #G 05n] E^X_R=l.^_agttup&sGVcu"$@xW/hӗhT6Vq.y\ce>@DO(hr.҃?˟P) Z e)Lk˓w=^f}2[hNvmOYBVZ:+30m.w˲x|~T]O0q}0T.<[1B Rb{DuFJ42sKrY8PHc|Ƕ\Ee*t@$4S>e1p$']*vnq_O9M0R)$:T 9tDZǝѭ}l:u3|0u}C*;3;kej@zD#$^ɾA`;d7ehgτswm\IQ)S)- EPd Nn`-2\}nYQPG"58(J9<͵v:۟Mv5qY8!7FbTE `_lFtn_CX#'"IeJ4fIR!J\"h5"QL ?~I4} d7( E әL~4mb }Ov 6D"}Kݧ qgzE}rK.ksp'!+ Fbض쭫],2-t;/!hFA^Q 2n,3v|$Ih5WmIB=t94X q'ǶAIe&YAѴ}`;q$dE,$Yhjн^lDVԝ2 jI.ncιW]0ɫ2r]2\3@vIEQLmYݹαwV4BeUը/z wQHoj?$IHukK٨JɚkǤ{<θX=m[!Y6STm!q tY/5j* F({FG%fU3׿`ŗ&]oT=LT ѓ;3:^(i`[NjeXIfu (B4rg;u`{a`mǢPYvfνees;J9*(@UwMTR-n268*zڎM^vR5}qldEŶL,B=;ma3JcRDZmd{=Cǥ ʔ7iT+t2Mw)Ŷ-,Ӥ^Zg)ZKö,,Ӡqo ;$KȊ$I6i(*m>OxHamEp&.󬨨e;W"GWdlh<3mYؖaƾձϨmaqp<~8?_2g?-' r<ϝWβ,ju|3" Z6t'Xt^j%0<$3/RHDvs^VU $wmwYr2)g卝$a>ȳ|3hUM'Ջ/t *Xz o~86wER̓*ؘCP=wXA ZlxC-EU"ˮMe`;b-bؖE)!z/;`>T@[-hھ~;~{\[hfF^%XpqqwLVv>"[GL*$Ey**a[3_$b XZSֈ,޿X|YVL٧Wl=ĻzeFԵϘ)[K4?Z^_Z*Zw֟_gTݳu3ڇS0'~1o-/jԙ |Ƣ`4.,+K̳_[NR%ŹC}vwl<>d7ְZKn3y6gn;?,4jU'ɮ|e]rn{rd7{7ܧdJzV*"IR(lƎOX9 Fb_)oY'dm<>//1(xq<|+e3̍/1ZM"IQtT7&-T737hejFh1:CϠ{}޼cعh4}~VPJ1i&rh/f-綱-~Nqw4V.6t/m3} ,$8CxOx)lt$mqӏ]q}eqr;4쭫L^Z]vm@w|0jP2,FJU7_^)ǡl2}_RZۘZ05{s]^2=i4 w ecYYb[ks3L1tjcVN9%`i:ėr*ۦgx`4mYNz>pi !h*cPyx~ +2|s ]E$b{man$2`q\kh iH 10qբYPHoѓ 9dY! hej8L\|?,PBz;Rч$+%N!`lSonY$ɻ(عwBl,̠ FąQ52Xr _&R-&S4kU M/Gw:eRfL\x>+ L\xHYUX?p;ș;"ѝ~ 7lS*(8CPT*aۖ&TPp%J233 n@GӴHAR!ܘ|S8=P# P?|{}ȊBqtxH"w 2`h IY"Gfsi^G%-&Ldyt i!E#7DQU!ߢ@8J\nCET*<>?ɾA"ۏ(Q}Ⱦ)}>/b MӨdYL$L2 2 $#2FX4J8&,Di,Ӣ+E&Mߎ'n8fuu?m۔+^/rrA&;\l6KG\3*$l>}pmKFN_lKq}RcYvq8KwG1PB$ ۶N52qg/{kpx40Kio#1f}=J(E۞ûfI(|#xQ812Cv C80vv|DS%V['PUUݗ;?`]'J8wHqxv~S=G}9e uwn'}uKgcq3@S=dVI W醇aO |0 @wz+f7,]#f} E,0"fj(x#7q^ʅjIo#/r}LorV _UX̼PГszl__VNĎv]U3AUD\E1a _Pό7ȉnBQڷen.?<]twuٙ$_8ѯ>&Ͽ ?;TU;i>2wZ ^/$I?'T`a t"I PaMpՙHĩE|M$IBx;{ 4)AݽD)4|0w}u; p`~wYZ^᷿1,"蠷ap4MT.SYY]e`]RuτA?sa+2JZ#П  >N~hӟcǶ%s{ O_G{h۲ȧ7u pн>b"_#e<^ЯY|hVCYc=~? U8>6}[[ik5 K׻#C/FJ2] RXM`5͕I8'#ɑM,OI1ɁxDzX[~ąX<| v?P:mVFfmՙ(?!$? o~I+?o^.bO??8lq/g2ֹpΟ{Ѳ$IB4 rzE\e-cfe0$caRQ|EYjd uԋ'Gwǁ*[KuRG ;Ȳ7H 9*<[ ؖ,+ĻzB޾Ozm o(7EQ [GJ0LlD&_9,#  yq$^yr.e=;#1.(tMET"A?ݝgrlAjxYEQUA xAT(s, /dZǦ| U"t8WY_dlDۓV㫏 PzRr@/AuouФǟ_ A~*^Pв,,_4MiHdpMN:/Ԍ@6H{ )GAI ]~ƒM_bd4c KKaŅ]Ϸ-pPei":^= !ӄ 5H7 0xdAՂL":貫:48&zVk j&4<]>w|XB4Uz}0]v3̖a< iÜ x>]ױ,JJ6啫twib1 _J+W>eZsqn߾K6 _\A$~_DT<++q |N?F2)<o eafS r=~t:Y,W"W$EӴJzΦ/ 1X(;Tٲt!ȵ<@(-ꖫm5ݙ1W)q>]]X7] H̔]ERܲA*,=<.Cp遂*nWLzܙ>]rlj( 58:.!٦l˥KȲ_{^Wqdh4ޙ5w11>Ɨ_^eb|fzIJ,&FO?p8̥Kwfrj:3̿ oM2٦;'.%Ý v[QXXf08+;\ݶiUa 9Bu@U,U:\fr͝amtm[!H7ݙ| JVvϵwte,FUHWkвnk*! `_.IdܽGggͭ-e?bF @W*ǣ X][^FVh 8յ5 ]Vb&,@*3=CwwJٹ9^RsfnQ8K4|onܶrJ19@(M۝{Lvzg`\j|[?v͝%9:,_aܷW.Q(N7{%NooPӧNv.peY^Yԉx>$Iw. A$zyHDgg'tx^]~]]ttF^wv?@ɷ22<{pU:2|dqlhaё}ОJdYf`Ü~N@ G@ x @ [F{ q`x|{4b-@ȊjVzN%?l1?xآR "Bm^±Q9:0a@uc[ 0q7yآ2fhЏ/$m_ xH"j}~WK)'Hb4E|]dU!a"xɼt0FǶda(xe`D,/ICbK 8}e6ָ_rlN}n.sz0Z_9mUVg[Խv6cCaW(^F ]VTq@Wx|^ D'dK3gӜ}{O4 4zhř"qN]z۲X_^`k}͕%]d6שUJ.o\ }Sl `su q8ܹ =Iߑouכ䟰2}ϝzh=p_FCN x- hqV9?ܹۛ cz1 VlB$<^߷@jtt;@zq@wh;;SÜnzw4]qUF'h2ɳú_e@ <X#9~ka`;/ӟ__{@a&_PF(r0𚫃kmD$:q$@ =U>IaɼZI@ ^I^9?a|_@ 0 "Ǐ}1B4~@ xL*84Y_`{;K٤jKN @90_,bD6wgyezAR[:uj\y\@ W @Ozex|jf:$IDu~ѯ|L(@s` q`pp\>:gϜg%Ϻ:I:qN?Rl.r%@ |#vwup8L($wQ]yǿ.g*B),.c1Uq.p_j^ba xRE(õ*d0 8y˹z< <G6ƍy*qusyܥM0Isfgt ~1P,ؿ>b! UDDDD;j#z""""lzPDDDD~EDDDv@]FPDDDdyʕ""""r63oG<y.o'}v+""""?֦60/|UNz?*~O>Z_@?/~׮Oky_CP[Z&yՖ8zZnɓ'ә7yx v;~KefU=-`uz ~L6̩S>|>8`}|79|7|cУ[oS/A099<™7""M(=LrF={{2zky7@660"""".("""(2 """"mboqU:qNMmǸDDDD_zʫ>4~_|A:M٢X,_?:O$< /xC}'1oxVWW'=·>" S>u~$qt8{#ϫs9cc%z!=vv"͒Oyg(q""""Ҧ `8|Q6>:W_C?0+WcIʫqN=}.9q >8{.q'rexxy8n`2`0oe㘉JL:M*ⱣG%[qL|9a!cc%0}i\ sVNm\#f Q(}OPDDDDno6m$,M0ֿ8I }skk"Ƚxр+7(R(znmhoy >I2zDlfܫmzpTճIg6 aM }pxUXDDDUlw:s9$ LcVG@UMqL`L0µzܻ 9G^\.v=?{VȤӄaѤREZØ <^X#>xٹ:Zk@'~*9y~B z} 0G34cS(cYy}{ǛN~C!SVDDd ukoc'Zrt~xgָ Ir}F~X>Ce,c,ߍkUїQ$ߏb("~ofdٻM:^&z7ƙ, "4 5Xc[klm~Dh5`aVѧc4#cIB΁b1""""w'[Vۖ:H.Up0d3i2>DhbѲg ~>Qj$IpΒɤ)󤂀ٙir$IBnRt&My-l:VVZJ%>CڟxŻ)QO%3qvHb-صcT瞣?iVG*8UxuQHR159Jws:Z2"""s|g)8x9u$/|X ϟyo\O}gO⽳LLLOO;v$#Ib0i&*y$`0 C$K$xbxb!O&BsD0h&i- Ξ;G <9|W^s9z0oXYu|s|ɁR9pLQ2R񰶇MF |>Gamwm,<|1 mIGz]*v۩sǎ…V9x昘_|JmiÇO?hpg }3gݦ[sWؕN%>IlbГ@wׯ#ޙ-%.||CԖ>xÇ% Ç0ٔJ=rt:Me|{9opc~B>d)ECLNNE4dR,XׁQ5qL:fjއz;)~ŗLT*T?˯^~] _Xw}O;ƍ&a߽ww76@c̆y29R<{4LǗ[_CX?#CU<}i]ン8zx|웝%"r~ bi4JsVk߽{ ^~Exu;h("""wf8|g?{PΪ^1/$JQ.(\_0pq)Ǚ✣R̛of3o̩S,..gIcLrF={{ٶ$+gbsND@ ,J귴@®Bv>$BP(P( ŻH BPJV( 5x< ɩAkA@D(!| b1Έ(˲$IJ5Ns<ϲl**rNeY~Vɲ\88=3qƓJ$Is5`C6+XKT* Ba/ut:}նV tZ¢lZ]%l& !M&KKMr=a^kWύZZ}>$ \V՛> Ea"ш E8Ab|`4r *JHTfl6RXM$$IKl.(ALX*U*bx*Nz.O@Q fjaM3<2ZV-fZI&Da575MLܼ}}oޚoͽo/^N$CJeye5B(߹}} B}=m6Ҳl2LsS,EVYf'VEYmVPR% R(/^Ngs*$QmzJDQ (e.WʠA](ւJE,nĖR4 " HZAPA21EhlV7W,.-p_,w0L$a3`0hjAiZR(gwӟ}lY/nd2PqElBP(>tA*Ǎ>yaGWgPt8)ɤ71 sm:1^ź:e=njnjlq1-Ζl.[_W笯jj:B:]rrR, LA͆hReˬ^0$I Wx^0\^`EIy^RL%5MPlTJQkjEު=H_VٵᰇBa49=]pdYb2\*t/,F1VLtuu[ j5R*5xF"r{P(myy8/>ȧO)BY L>Z,?n!,}~ŋmBP(p/1+ B؊rBP( Bx P( k`BP(^u7M!DQBPU0 qB !ɪ-oV( G $)MIbA̬P(R ু>WY'BP(~G%o B=XVUزћX,^~syu0WWhieQEi@0$ ` *V+8Aܸyk=s8 P {>pxahN'I̬f{J RO$#hCɹ#DcCC& E+WM`xY߽fȲFFǖWVu \D"yn^K3lVI3sK+Ng?s\qKViXBxK%H`YkA1L8q@EQD0QA@QY+‡b{ }>wiuJyl|bvn0"oݾ&c8OA%I(*N߼}瓏ܺsRw;.\b2 mli ã* 0 ]Y]s8> JUvfO3^6dپ ~`ye%O9SӓSX*ݽ۷}\.wp;|ɩ:}eumlb"WUfS>_/r8vDIZ[%)KŒFs/ 7o--/CGF +dJb*uS &) Z-5o/\:QinnVk{ƭ[(Bݻ{hs=;' PqBPtT*AFtTi.JV5J L&˨\6LT1ñjbTlPTѦi^g06ΓS$3N_Oa6EQX\ZUUjz`hԊ뀆0J7xE ;n߹  6r ڱ$II~g/\1ll6V13m--@LӴJR#mMlw/>qY=vݾs3~ BRQ1eluP;=3;_|N2 ]*OR/\ޑڱb&I}k>_Pw֗JN MQ hګnHta烡B=VuaaP,}لZnkmx{Zl6Y,fdYVEI+W*imnv:u:m: ]0 w\ކ4hں:FFjIuZz`4j]NdZݿofj4Z=va[޹saal6QWj#45z_ӱXP(`0ۻ'NFQ `&t.?~hSSnonjuc B(zJBx'SkTb4ߕr+ C\.wښ^\O,@yF>z$qwtL6wt&T(O,7j;e-BPUbd`>PV ) łH,bu7DP(o 8]U7!ڈbBP(^A0 S!m̢P( ŋf%P( ˲feT< AL&S.$, lQ1/;*\.ժ'Rh%ޝ/r._ ²bVr{hYoZ2Aūv` L&n @PyeRb8sCU2V/{8/+/u[y5aUN&e\~?1AtWgNAHix\o"嗟#ȃh,a2޻o0zp*cj}ӧNq;;q yɵDD"y;wp_\^inn%ieeZ^sÇdY--hVga|? {玅%ÞL<3rYX\y%][St[kK.fsxYG>fg疖WV*UKs̬b BLQܹw9oC0,J˲tz$9뛛(\Z,}3)" (ja0ZvǎݳL$ADQeqn߹g޽wOu].u8"D7 BK+{ Gv B4Ea\޷w(3sLV ^ ʲ4<\7蟾::7Peyphh4C]%SrsjzfvnZFb}z|hZ'p|aq tv|p 06>ΖK+x<>xaO$&:;Ƣrk,Jt?Lz_|E7o_yR1ORZ[Ԍ? BhfR4}٭raql6Xx"a=uO$CH$ԔLD$( 4xCrGgN;ffV՗_/r_Rq>hZPѨ:&LE B2")d26x<|tFhø*qu{Wg'O9m-?44eAf8i^lDQJ]ئu9 B#s`_ ټ$Ԍ qaP5-;j\6EQU*'9v/O$bd(RAj|!NC<ɩt:$KT(Jac,4q{r&)P*8Vidt,NsUB8<2p8m&arrd4NNN^8V v0 O$r04[.b񉩩AVul9J,{v~r"JRVu9E\f7nCJ[;oBX*Z'U4px :HM@0q@L6SY}>?EQ&[--ކ"|Y` VH4ro\(Kᮑx"a6bfs:3a_4jL&SF!JŦFJ۬k>hxcL6lj: z.Ae8A-K"l>_Ŧ\>LXܠl5Z٬ll6'S)AɄiU=6dWVPlyaiyr:ɩiS4c:6Hz<|3MF|FٳoCJ&S Ͽl.GRIVP޹Ah,J=n,˱xuG:ѨաPfr9-]JjX̖YbQG2٬BX.UjBћJg |Sc#MәL&HaFDT2u$~n|Yd#moTZZZ^~tl2esZ}յ5a[[a$IJvMᅋNpjqiy-ˁ`h49+jjl_Ɗ7;lx=Jxcb_ M<[xIյΎ' ?]$?q`0/c /c&tgɛe5V@ ﬷0Vn$9^mK@lFc:P,W-(^$JU<-? A]h}Z[j.Kkb{{J3Mx"p؋J /Hع{Zm>x06[$u e |A+؜ǼB^wjSUCY_X*_In{T*dPqf8n248P5M~#hY>1 S_[ݭ9^WV(\.oKbw(&X+i.YX̵i z}Ow7{!M,0euBöʠP(X,n<~c&6>\A CմMP(BP(\+ ʾdYFjF<A a2еRpv'B HS a DQr H$Çn^qAgGF?8uq8see6zX =Zzݍ1!%}7a#kj#\(JQ϶6;kl,TbZmOoFK^cnc7ݖ'T(ow(OLY)Jt:J%յEQ o߹wt&c_wMS'um<86>sǎW_wctzWOo1B{Fru յ@ d2k#u1vu_qQHtd2 1 7_w1VW,_(EUmiP( rJSl6U;wƭY ܠ{<8_~V6L(R:d2/X^Yi34F:l6maYF@ʁB[ߺ}zU{6a=lzfvnn~{}.W}sSki= ,BAX][uuuv(۷#]P(bBk9˕ =}|~ x3AkW/BP#ŌBFP.M&SĦ4MS**fv9%.eM7PEd;ix뉢'ob'pzUK aX`j9LʲbNK3VX,iV!,Zf3󂏭HÙ&1 ިRw}/0 NZQk/Y_2贏KgEPcyXj8TbqiZR*x^ɲ,b-jI* I}l9NMJ%AuZF[YݏdK.+jEѐ Dz,$E*`eݻgrr⧀ߝC@*J(J%`Ē$AdX0T e58 s S)k4jE"ZvgB85=#AxӑJheYZX\ ם!,W*(ju*X^k5'&Qc:ؿ\a C.^.S(,X 8lX0P(޸yP,| }<zeuM%"[.GWoss.xL6(R-/)q|\.,[9->hnj4M'S)Aǿ|eYT*wG~;ƭ-MMJ$T:=>15j##bVD2lȊ׈Q(dYrydtd2^| ܸy[V߼};`004A~_ŨB2es:.NC陮Ύ5,Ec1ĊTTrWK%vjjZoB")ZV I{>{AZ2}hllbȱ $I4rI+dJo/\*|H$й:;:9gϝ?|ՑÇdY^DQRT ߼9`t~{ʪd0[4zOLNM#'h~V*^DSdgG~_:|> ^GQ$ Z1˲ ݏ6IYZ^|(Ig0 e9L޹ۇa؍ί|WH3wܻp*_}sPVyHoq+ãBݾHP_ͽ_|gN|>q\[kK.W'MLN>$IW]ׇFF߳,{~*|zPLg2d~'}[w.^ןH&+^ !LRDrzvЁgNj>;Ur~VAP.q dC#r;2uZ-@F215qשׂ$ }Ziq|`Y}>ښOR:;{w&S)߿bT*Abi2eY6L'zu^,Vd%I2tvMӾh/)3 MT.J^lDc:{g0$A2 vj:ˉ@Tr-- Bp4ZMIRyY9'J"@EIj7<,رcGq$I*W:h63l y vކo;7j6KIJh4zwϞJR+^qea]jUAϯjeYڽn E[[i`"I`[[ju Jr$bhkK󮞞X,j*e" huz.V#IҾM!6nmiE hZvt߾sT*U*U2EQn,дh0zh4Q@0 pK#2AǕr d$  z񸳹$ R Rvt*n7낮T*d2n\ϊjdhgP;pCZ4gfi>̜8|`<0v EQٔwtg9ͪըKSS7c;:w\q ppY٬׿ě$=nܺo_oKsH:1L4M9|ʵUs8l4DcwONݻ߯R:09=T j(/9&uA!/@QD2hl6+MQ-w$)jކ険muM Ei4@0822Z(?J$]Y]5uuc@C  {$IJeXg(ڽb=rxqiӧ z}\ijʲJ x BO$VK,MMw(t137 O?ahkkK"u9](ܼ}U`lREqbbj:w]5uuF&]o.t:r:?KF!H"wㆶHx}}/p8?H{{w?,lnrz{Gp|0޸?[x]J%e:wC59:~ J|z ;7?Kj w]^׻9~F&p[p.w4 g+P7QCmHoRTbˏ+P(pA~Zt BPV4ϐQ(U*ܘP( bʽ(BBA6^} $,KB(P9U(AQY/▧* ŏ $M|EQ<ѧ,( Ǐ6>0ేNZCh) W(^ʁ@wKWwP^__]:x?0H&]N'0K϶6ڈw4k8{`r&q\! B6ۤ :kr_AW}啽{DQnok?}q>tA,'K1!ex/jR88q(R1%"qV("Ay%+R:i:]=pXHJ(˖?8u+v-N?$I h5j YDcK-jJeAQ %p\e?p`"ɖ;7 tuv475q\US[(8!TՕjUV[VEAPTR=_9Nwd `n{-#vCPn B*Vve6}ˈyOKɩi ,'&cXkKKKs={ho?7K  OkGWDgGG*ZY]ՉbNҙL/\ϪOO/~ϭhjn~oaA~_l,7773e>Nml޺}{//_F`8EaX%e]z.NN/k8߳{Wť]]nrmPl:{JŴ^~w=wEW7NL2v(rP^wC~ IX۶u`xݭxuU]NgkKs43Z'qqjH/?QXf eJADQ:q?D{[롃GgNHb6 (]?~freoþKx v C1FK34/ dYFPHZ)ZrY՞>urmvXS3 m$!rq6x&gjyPTbuu{{wgťt*^_omi.Ao8uBP(qP(>#e?15Mڃ' WmGva A: zf$IU EQA04-0NW\N8lVr2;7OĚ?;7oΝu/L;E w0\2w (>XFҊhHR-F V&&,Cxd2 >_8{lS6mvvN,,.O|x=#W\.hb_;_,1n- z~ܼF}s ׮_ GF :gf2L&ɤ͍ 3s 0 qѱѱM=}hp^"Ӫyf[\Zff|bbNNM776ddŢA~_k$](vMwv56imH$A(:95]TP0000\.^_ |$KV\C(eZ^\(kéKI=pTrjSw]pyýAOoj!j4M閗_kq- t65.-T7)RTMG#c,$ +FbZoieήaX154egeR9;7'o}rrRVfz((cxdTv{d2xQaA_@*vvugRaX pJ" < <&&/(E/].*̇ab9|EQVaAHJҝ>RS] @4M309}rbrb#I^9M34T*/x<^ pnl~;5Օ\KOKaðJZժ(a t:]EyYNqPxD4M4 Cѣ>ahi@Jp8 =8SSSJa E333 R ˅ $IEb=M89N;q(æi0M l6qq>M4 H&le][j9J55ֿL0N"hA`קz߷0 0 IJR)˓ʤ `niŢJZB&F$ʇ4es7-3; S(cQ}] 9}s}MKrY*VlmౘŸpR`E<>g4Mu:61oUE/0Xp:E a"wv140 À,R雷x| æuȡ^NFeyȭ5H'Og i++&'T(nFfX6Z HscPT*͋ŢܜQ_pRSSVW᪊rL*B kk|>dZmnj\__ga_rh*Ux<VhD"醕Z =ni5x,H(|^vvv>TUrܒѱ̌ښѱ$.#=0Bj0 Fv{,˓Qh(3[,>R)BѸZ[]5? ^cG~Rj4!f ++Mkk\$L̦鋋 m$IUUcR*av{*33k0[VGQFZdj5x ?zjj4)bPRegf575 BH$EьܜF0L$iLOWUckR\$䕔JdrXyHP DbXz{h ?rլKI)< #;Wmm{px|PT0%qx|^ chNaq>P$e5d(.Eo\Z\m.h@@QtqQaFFhbz} U)>O"Ūjkiꔔd@%''ONM775x\FpEQ}JШUh0z5N;33W^VV= WK^\Å9( x : " M}V ~T/ )\OI),ȓM=t~~^|wtl\.MmEU>*h;;?_]Yl43<'*8.øXT^Ź<7ω 0T}FӪ++5GQApS^COSA'JAA_x*J/w6]⢢‚sȓ-.]bXvfFeEy4_Ogfg擓uhkt>gN_v>ٲ]g .$R)~ff633wߗJ$>Xlx{܄}fU.ܿw30 zO>yypwwONNzӐ yDRIc}nrRgn$[*\~HV~2KQqVԯQzGǗwݔ`f,a.p8p;Jb'yTAXk_ګ.\"hl~WΞtt:`H׿7PRTtu=ٲ70x`%ӪyϜfsƭ@ (IyXo߿222X,v)<[[TJ Em*筚jᬚ,[z^Aáԋ(iFA?şðYiv{rȨ r3 (eHںFy7naY(>ꌎK$'HL*r=7ﶶS666 9YJaߺ`X^z=9)ճgFFqa ,c6[RR?pY$SR +J"#-x<8<4 ؿwO  B$ÅZjoD{4H2iٰi cJrҥ+5E! Mh!`@Eyنk:.$gbe.]:tr(F?D>kC$=>\&}ֆ' >H(R) RzʊrF-J:%1IQv gN;/.*JOk4ήʊgP"KHcWłG(:Nm}`4r$IX,' pA$É{q0H4ap"\.`{w Daf<3b8`fl6 h4h|a{&q8Ea lxEQD"Ql6;90 #BŊ2FQX,Vp`6o)"fAh>UV$ILτ˞!O͹Uh, X.MPhllbkf BQRR466PȅB=MpD.i&*E|>?"HTPuiD˱J'dRhas 3=HbXb..FQTal`kf?(GdGp|JiAoOq4 ð #- Z؊;|?oug=ڊ) $HrǎV,SuThx\ÇwWGf+ $H'W CRbq$Hk llnij<$ ~!I/=R*?8w"H^9{fٰmmp8>=7o)-)q-).( 0x=*ˋ Ϲ>yع )ɇ쟜6[,0\WRTf{Qݷyphv;iwk7nnVs~^xsSS_Pht|µ7^sA|핳6}bj(/7¥+K;i;NST;r_M>[[]#Ikkϯq\?w;՟kk/?qwta/\ ÙuXleňD(zKz{{"RMUajG +})3#}lFco~<3IqoZYQv{Njs Ғb.^{,]&{&M3.]FQq#-!_"g/xgJ(_Hէ >'S'[ $IjԪ׾B.Fٙ  B`$I>O$ gNv{ٰG# Kg+e͍ ãc<.7yԉ@FIt8:@rTUrs$E97^{ue@ɖZ# hIKS4e][8uxOoGg7 0 7moBQf2A?8X (7ZMIqQMiI1dܜl} |~k]\\Np$E( #H$|왼G))*JOKq<%9:.U36o6jՙS'0 7ב!H++lv(&gffg7P ˆp80cB '&ݺe11iN(Hjzp||xXpd#qT"Ѩ[Qsp8-5~f5OLN5oWH4>ǧѱX,v#9t'j2,͆`Ebg0k7f=knp8[2A(wa>vX<[5`X,ճ/{0M@QrQtS)xnw<8Zfefv$T}J$/l\E$'C![K}J2n#/\H O["a]a L<)8a06]7o~Ci>$bw,  O A"ǝr558_?ynr~x*M_ q|%DB۳Ѩ՟FVҙp$۷t̩~$̌ɩ+n>wECQ,bO?yIqd*qr8/tauʊ.ر#/]zO?o l]KKK SI H$+@<'[1 cX1GiSz}?ӿME, ٹ%H?8;;+ggfg/\CwARS^2=;jfMZ_qz}l_0 A:9'd=saLG糳rr~_e _[_ޮ(;,6˰" Ng<~ddt+_:;/\rhwo_SC= # [_  8!xT"nwzrAǣb h,( V"*._y4nKPݗBs.~oT} ]9NEYik{ǯҙ-I8wOĔ:g^b4-u^0:uaE0ܨ({Dҳ"$v"0LGWwqaX"~=G:A34_qa4i>ãc%Ņ'{KCPhfv≯q>%YR̝>rh,X?33Xjf75Β$eﶵ..-qy\A55榆7oSk0?cLWW[}qHWNӚ AC}|ȡGNvؘ(̩fUV9tϾ-ǜ΍`(~P(TOo+/HP྽C#c.gfvfs,VRInNv{gB!GQ$C<19N?fZ0sccdt&+33 %鴧OX5jEW$H Ś\̩oD,~jjj~aqyp90 CEsσbf.D. 0m]Z0F5<:f;=>9ON֨>?99 ⢂L]IZTԼhaFQ/{]AH+$H`WbD'4/A, EHB]p9GP4-QJ$(:"m63h! ‘ȡr\&c9-ǎbx bѪ~KT8v4ÑHyi 0,D."BP~~޹20.A3$b1J$E(:lv6aoscIbQ!0 yhD$E=kC@ Mx4DQ( 84@,D`p8(b&4fG0a`(F@D"_CT,c& $ uIH$oZ Xz'B~iaB[ffǭ B4Lg$ 0 YT "ADIq<>bO/(0p$w3M`0RB0MG"oG"8L} ?U| HR)ZT*MK +R$++smm=#=秦|~rR;KAO8S)ȴZ]Ui2F1*+*vzx*(}U$>4F,V\OͱUMaS(cx7)v>CˋD"[ |t^R=484+$Ine ܼ\.@ z}TvVF`Цe;&767ﶵG"BrCMuՓUx\b80Ax%H(JoiHqs90]y@O33stxh,_fFiye%/'aήWVOOa'vH4h'G'&,v\CawuÊi~a ?_oASV܅I 7p8;2:rN9tiLRIR\νbY23C[?(mvlGgwnNvWw82-s#jjl|)IX,V/Hg? ZKx,Bu8/]Mi|HĔB..^h /\bFrF}]\O_?#j>a8#=fZ k5=MFRu:0 55s\TZ\TX^V:?huUeUe iujvlɜ\rEan2I$hxr_Tm[{Mצ>%ıSɩS'6cfLP8Us8lbl6Z*lvO̊ѴfS9seccē$ Ay$)8c:   [hT dfdliw ITKҁ}{FFX, ~mðxx aAh4F ht}ݖ?rFJIQԹmx4QǕk7,5$=7jRawDы?z`XU*՗67 S-nj&NZC h!) A?RrǚU*J7^רJb ipEܜlcxܠi"IAIB }|'i|\;}095XI#0",bpb($fb1)@E0 i|H$"H7bX0?f[NF[GahX1>=k13x, avϱ?2!lqh4fٵZP$r~ JNiz|briٰi@}]hloscwObutvWV4r' !n[H$:t`綢ÇssGK4,6W^n6[~Wx{G)dX͍w[@ __X"bfWU*-.-+>aiiԉ$I/LM45 FQHN6 1TWwdgdC,vttxUEyiqXrpvbf4*+pyn(vؿo@.";[FPyd˱ѱƆoUaٕ#JϿp8"2ff +gOT(&w8_*).[XLOճX,BṊwCPEyY{gOKկkm~#&+9;z HQa_(.*X^6ժѱqr;PWSEQTWw}R!ahfkmmY_%%vϞ9 LOpjyYdQA~,km8}[Ppdtּ\{,VS'`鴗ݨUNA^ޕk 8sĞ榏/\I6}zfVP8ΛwD".M KWSJ뛜yJ> ^b qP -aldžo$ '/OOK9sĥ+"w>fccp?zD*'Z[ۛ&&232BXscݶxُ77]C^ @4}߽/]z.!oz7oT\/^^W_&nG$!0|׹9(};}^T*YY1/Ldefg Mw/ix|Wwoffr]Mӹa2>{y%e%bCkoUJ0Ey^$kf<WxIE]6‘]. l6_-$''5p\.\|~U*e^n:@!WDP(xtf z}>֝je|AlVJrrsSCcC=rWT*OaJ A_OT2< 3(>Kf-+ȑFoDuO&rL&5V hnjPB UssY,Z :6-MRT"UYYY,$R!2Y r2aRj5 \$򲇕oih[eŅ{Tʹ$L,U\W FFѨrH$̈#l6+33daqf2f04M />y"#=NdB!JӴZZJ$ZF,VVLI:Zdj+a< xP눫YOTyP ,c zWAû+v_tgms(#@Tbe*>olmt\UiQ<nZzڢP(FzXȣ YpA0 ΐt2*!=;hףb4|ہD1D1gB(^ 8LW̘'~8TCY~$1|^~<1>Kd$HA#Aob%"4Dv Ͻb1<SbxWC;hRLDa $HTDMR, 7o<Ok{T*FcwZ`VT ܜT//^tk7n,-<ޥ+W=^\.qwș*hgQ`CX1:0N=zR!ᩪq8.X, hcsb0fU$|YEB!M+FA{fs:7BA,3,k Ϩ=&ff}E"Q0Z5[jMɄ㸀χ (x>.n|"i/m^um]( 0nx}b(Vy<U9@ hXMF) MsC(;諌Ѵq1 cfccӹ! q0VeY6EP8jp9A,ֵ`($U,U3*@ hX|> 8.X,bF*FdmmD"?`XX,,^jcsMF*|>?VW|ޓ{q4?$z΍MPpN]1[,b-V> bD' K.EQ[瓯ζ?5/.-r D,ҨU }ᾁAp_T*霞,__=sjtl\(/ Hӭ:P]<lPj ZF {V fkcyvR$φ/L~Tn5,Hn68\A06{jn53 sS`@$NLVo:lUϊ&GLs*]J2o޹K4NsSˏ{KIqaYiqpߞ/O C27-S5)9Ee7ϽK=w([]_@ >z}#ccyy`rD@ XUUQ^VG+ EBa$ lðxf`(ӖNMϜl9F777FA=}}Uh4 @ |>4Hv#^un~nwVi8N߻zkmHKKkIX,V4  ߺaT*z}hfǏl6HAѱ{~?'3s6ؼ E"QÑ ?;dꪊH$}-Z~*EEAD C.G,8)&IKReYֺΞ993;gw8P(F]z=<`i~@0( =^? {<^>=|pv~aif믽^X$x|@ 9)ʵIٹP( `*a;6JeJJ2 Ǐꟶ3H lN ベ VV{=33]岑޾FkUJ%MZF2==l8rL,޼݊ahfF_O T*<wl|_LXN܏3Nz)PZoJ_43 <*MѻW`](:s‘9AAqjP8A$.ogBzNA15Dvh$E.wa(LL`[']! ?9 $Hi> ],cO58\JD$FfgtVVѨn_\Zyp8rۛ.t(Z6Dml[X\ZX\r8_2::{$H \ГSbEљ9.r}Ag?84lZ5{ޝ)!-Y[___Y*'MQԓ KP>jzcvnͭΘsP(LIIMOK_X$IjyeuaqiphddlieZm-'c%v8].E#Ÿ\nUefa̖mFSVZ H$h`(-5E0=;ց HK[;aC#cii\Y[eh'$j+1HT%''' /ZەJmwl|nwLLN3 ?8[_TE-mǨ기lOg?D"o95=gfci*C"O:N'tM^$tݽ17;D} j4Y(tpdfddefMLNBhKJLl~ IIc^8giAISOary[GgӳIMϚ|^qq!>ԇOIqq O=76?g/ pdm]mri2̌GOP,,.)CMjQ'M:E+`gg`J64=44Otd㦧>yl;zSSM5={l46=kf?8<D'pDln͖=3R*5$O"Jjr8GQT*Z"isKeyH$BQ>ʅsgwelv:][AFᕵP8V)LX??' J1%o0 D"\OH 0LNNZ Vmo|rV[ZR x:P(r E[ɩά̌ٹN t:]&cR^n'=  q6<$ɵ +22v'E^ڨIښ\>tee{<ޤDZ-`, 2nIqQq.-5E d#UTjέI$ǖyI3[i0Tv{5jLOOtHևTJB.D K'&v)̓hԗ0 a<>/ H%,IFsphu=bxjzr-W/_D[G8C3@0870 66~?xťlvǏOwwCu5$EE"?YXo Á!8R4@ w$A.;#=MZwݹmH'(!$a@ NK,w2 +A6\t!!![7~HYRThw8o^JE1 %PT:H4l}\&HTV1 395]VRL`n~ ?ښҒøv|>0 S\TtZ\. :AðFV(Fbj5_EL0jx(:>1yͭH$" t[[j*-S|1`?Fz!Z[^Xŕ ^$$yJ”l )&N[YQ23rsJEZjJ^NT*D9Yi0 s8't:m6\*)JrMƵ `H(ܼqM(Tܜd*Lh>x1`ٲLDaAƀcX$63Vmpna34EuvW&}I`Qhxl<3=] EAu\oƀ|NEQ--\8{rU*t:ۭOHP*z~naA*$ "ȡfZM&LF *jxd3Q,C1'h/.,0v37'EdtlJV΢|y!PX*ꁡgH C[,;g23GJSSRN8waqq) ZJ*JRS {f|4j|B.ppPOOHܚ~huUEvVjV76]1S #3X,STT"(*q8ʊ,VO{C3'Buc04\Ea8qz>~8ҌD8O-(M>,9'E>cR"_?_503 Sz4 tJS4)NC4M24M4}ҐY-%YCl/ 졆 $E8G?bC`rAPEј$ GoLJDãٙ={FV9<_rl$OܺqTx4W__.p8>?1j n'Œ 4MZ‘ Ņs9{{*$H$pBD!Ύ,,,,_. (1gh'=i5wnݐd.JGQ>˽~D,.-.9P+`ce%ϝ[XxEO<2[8,6h a~F{>n|0&aݽ]=@phx䝈?h|bffv=aS34\Y'O_Ķ]\Z2 9Z4=1SӭmH$JQizlb 'GO>'18ciz(E(D=3sViťh45qðGOZ:0K 4MOLMwƌ?YY[2\.wӳHG6x<4:Ƨ1SW=/0wK9GW_rƤǍJzյٹɩh$* RSL64=m\.wcs ߯QE"ңMi[fdEY16C#O_%$ϗI/ZJ c_^|>_*r`J({𸑦iPj$ J  Js'u50 k5̝鎮O>v[=B_\*.*4Hv\{vtMNO₂<& 1,y^nQR<(/?/@09*blb3W.](6JLyi [Eǜ M[5>mD"щɩdc_`YI70 (jiiyvnPW{5-;i܆aə.a^; DZZjGWzyiZrãp*`ooL*TWU޺q=:/WN/Zj[Z ässrzz(bBy;W1xs ôuMx<^ JlxWWuZ( .o0 dTʖ նb2)Ғ>.>#I'wniuk!-m=[ @+J}?j)+R勯o4&J_v[{L"b7}㏎|>?ܻ[̨-gfwP]Zsk;|<έǮafyeE_Xj5dJTXSG>? j5޾ѱH4/|Krxh=waXvvL&UU~hMU-(u5rbjZ /,V U4CãIIXp MP(T* z<`fvAKߞ3 upsP`ڄ~\(Ŗ& `f}ssu}Ky߯QLښᑱ?& e2)aAjUPuUO _|{/Z%bqO'&m6\ucRӯ盝OJ4UJ1iu WV"Ѩv8N+))I  \$ =oi}:`euulNM5=|nM߂ϯp|~arh4z?\Y]_p/? ɩw8kK;zԩ@\:pfo|mbQA~R쫫I;{b8-5qG/^{$Afed B3tlA U*cRXG\ ?WT}\{*aFcRMUav`ss2ҝNb399YZjjx2Z Ikk9YnAtBPO`hd4 Xmv0K8;+ST$^T(zi H$Be{;-5%nyy 0vq);+SjI4ME˗.C3u{K++v=-5U*n(" ?WT:M>a3*2d0,)Ѱ ewnXY]UTIHl '-x<-3E>_&)򃃃׮|z``HVB`(ja Cϕ yG1ALJ7 cp85ՕFcMQ+7!pBH( (T۽etD"V-APS]бr(OJ4"~!>wAca < GXvvΟ=e2AD$OrQnMUe0JM1I$bÑJ$\yEyi9 cf\@єX,VN$)R3HTCdQ*dҒ̴TXA)`(,+/2 Cљ9L^VZΝm@+r\pBPMUI hTP#=(A(|y/]8c8 -/-[8PfgΝ=t9-KMMjFcVf} |>EQ P_[d2)AdR\.KLԯo?w&+3ciy* IDAT 9وp86ܙ`(t>543;WSUD;!N0 O>{PTX095bJ5z4Z0< NM+))1-53[f_ 0olï cxa q c#x|^VfF,G?¢QT 4I$;oF"QD8D,>6y AP(ݼED97F@ r6D-`]X0 }.EQ.ēH$ sh&b/4MC>?3 H  ç&d,f;`o.b $q݂:]ER=);s0`)ӌbt!1e;`o2}1A(.\ p8T?LBȩp%fhNfpQ*+q[[, _kkVlCe{{gwdcRIqa'RRL551<:vxh=S_ѨhP8pl$HO E#N')HtͭsgTxO]^Y]\Z.-)NOKz}}<x5-ۻ5UݽE]taqiٲhLO`e{cc3';+?/7n7 23ǍƤij ݽplln\?{I8whO|̎>٘DZZ0 HW3y\.wrz&3=dqi,.-/$d,?VEQ>K"peumye5ŔbJdu5-mpX&=P,mmwׯ:sgaYNSS3ppȟQ@Zj۷D"!/,VUffWVONeUUOLuv;S `]}BZ:֖$I-TWU `ҔlU$U*$B.I3sUi){>EkN=4M\|._phA?26nlQ$i'&K&&667y<^,ɘxʥѱxvrgwrb21 2TTXpuPp8}~VfFo)9Y*dONMsX zzB{wjF|=Pl4RG̖mFF\ӫOH|xG]nɎvKΏM fge흝[7E7oBz}f6MS`ز7|Ecf{1 e2VtbHRbL* C(UObm R)\(wRLݸD$}fD"TsrF\33;_vRS56x, # h y料D$EFFC[;++%( G"jWP [[Z!M9EQ_^I4_:04`ghxD G"+ }]GGWw1)nvvw|P(aj7$Ib@5Ue>xrss`"I "h>{;%NѓF? J5ju,d*6ÀM3sEMcR"utVWUB uJka v$I ^ulqa~eAe;J#mN:vwoxt,+3ceu 004\WS-̌ze񖗕zH$CхťMqR| P8<53O-*)."HN@d2y<^nNRW[#špX_o[7ePP$4[LFDac|%^X3332lvǍ~_u A"<;Z 4Cs8pUeAk99Yz`xt,=-#QEI,-)q xLNQlaA *rjz"^7 _䆢(pgEb,+-OnZ}<OBӥT* 3R=_~n^`4&!L(!&>5kQ .,j4y9مBfThj4YL AЉZ cH1%|ZEQ*,O **aǠOd<ORI+J nP(,+-Tף0Ai)`r3&%!b2}~}M Aq8N,j ǃP}m`(W\iõ0 펚JLJTFz h5Rp~^NnnNQAX,p8 G'Ɇ1x<^A^ڬ[/ʤnGi0rs4>E$޾ua 61C081ƀat|WoESV}sӜhxZ(lvz<^ zHDRSLrn|α^`4&i4lVs՝]DR^V?R*jj-.*E&Sr0a"))ium 68g\{ {VϺje݂a=9o 1EqϢx4DIzBޒ@ %|5H"tEwaNW*w4fAB.1wic'>cǻxv,,,,,,ύ8~k`8Noh|s 27ςW80 oz<95h{g, HEkRq{rٿmLJ[XR Kc z~-q^ONɤRL Z;g2igVs1)qxtl<ɀ6YV6-qF&(LH1 31Dw_[S ),?98&76{zBoA0DSӽ} z?pqy0L0lnis:]:ib1ieu/Ñ-EP \&NTF1Ѩ;{? C]ӳS3؄H(?vtvށxƤtPxՈ /&I|>vn~phnwdGOFꚙOJLꙝ(}H4>i'؍8+`SA_jZqh=5ՕC#C˕ec㓕enwyy5P(r9 |畖WUmlk\026W\ɚ[XxRa#Hr޾ŜLgg\&rA%EQ KKw=#C0p8 IyKM`e2A;PJ4޿{{br*&j' ܽusmcj23zg߿{{zz6Ŕ|ͭɢX"6%?$ɵ[7].A*tATW[326^_[111uٹyIѴZdfm}p\iv Ο=142PW#.וKQ=,ꪞ^%b1É9d{0)oo/|mca  |X 644>mq-% &%%?٥iG?:Ae-``H.y< -,,]tG?@ XZ^  Go\:99pΝw^Oc&C04 (Ѡ0\._z $_Hdrj:45hlM\.xLFE"$th*K/lZkW<^o4={AEQ0 C0 0q?.X/SzA3 3=3kkkRy A E4%Hbr0ȤRx@=^/E( ry $)R.oyH$ ) xbq$A8Dz9\& B8+rT"FPT"y8{}>\N4B FD,0  |AQT*8rWc!d  1S=H</C"IO&$ %R"(y\NA~@, `0B!x ` MW"4 2i,Y$W+iNWco= F bQLrA8PT* `2vxEa ֈ†16z a/P PK[M߿h {.22:~PxܔKEQA( BFi>S_rETWwZb҅i)%@H$Meaaaaa a zrjah>fwllmz}XvFFzuU++XlVWW395}ppR{0616V}҅-3 AejB˗.lm)JKM0]Dq//+aYYN6~Wc' ^뀹\nNvVlYVjazzk+|~^^n쥔`aaaaa]K!pl|p.YXXXXX~k8e!,&-5 ?-D>{ð>T(ffqL:3;79=v[UQWgh4jJNɤ3$A޺qMea>0j+sVV"w?VKk{$7р"HnN$IQׯ^imJ ;nԂann26[m` ч?xE4ԯ9 Eg ˗?|Űo*,/*,잾DNgwO_0և> ݽC-_<nbr|\qR!xk1/|>NvfeE1e$ISJu\vbr]IT]p.>g ~;nZI|M+++k??n|ڌp|e>w;۰ z3q|,`? ҇#zX.{ cR[`߸0 x<6ۖق ܬ?8bXGgVєG"˅`kܗx4wVD33#4 ATVZ3 rH$ra")* ɤR7 l6UoEQ{ØNf qi5ojz{պt{]ݽ"(5M- Zjayɩ`($H4&%VWUbf;VW׹\. 0Ľʗ$I$E$!I,).^Ҙʊ sK |> NSPܺqmxx0LD,!"7'K4Mdg ~`($ ?wxooj+-)ceQOO7(jcs33#n cia)P4-HPӟB,Quxhm{Fӌz} |>?[g cz 3i:fEpFA4#$CQ8q5a` saye{g0'>I"+ሀ/0<0 f{~IKF0  ba OaiH,Q$E3i:&%JF~Qa~_`|-,-,1A=p8V`mm}m}&t@'55ܜx~dQ"zQ)yxt@0xPťeapxhU|>l|rP V oot1UCNwh>|XZR<04l0޹T*D"A[[L76+u5MϚ'-;MZg*NZ1 څA8};B.>EήJF`+N<` :^Ѐ-b9-$O:`a 0 3 0 >gP02caaaaa9!/όX JJԌ-i0-NL܍Bur])qR +0u9x|P, phmn*B,=nREEx<_&)nyLS;ߦ(QSjtCH+77g+yeUZCE}vT =- ȋ?).-$p0q9j.74=ȼ IDAT01FBq4.9v,)lN`p Ahp8"ܔC֐z 8˦(fkeFo.˔*VST>e "@HD(d/$fMW=VI姵Ra"w[uҰa ܒ&YsZjo梼H(SRahmb-n{Bpx|>ö]aƺ=ݚ#)G TizU<5ܛ`45%hAk cًS\S\qR)5 k3 JYp0`L˼pKZK7?8,,,,,,dR~y[/\5 Ĝ  aRNl wz,&s "hAtlŖ! AuKGJ4-@ [@m+54MWύ/~uq_Hƞ/8A;֢-p3}+'suGiz&J^#;ާh:`x,NO˝]=(j?ޣ'M `>|F^[0D_8hF?/ z)߽}3٘7od5hZF(LCfog|rjgg7 ig%[ ťۃfȸw$ 5tʥLT>wgbH4OZD h-B PkU,Vt5[۳۳dz5{صY1=E&AZk :bTd:~d #Ed ߷O!G1&Fq\L* G"{z@_ bG^[Y]XEQ H1VCQsْTl6, B9o0dz}A^LƏ~t:]t:vH$ɮ_\_WKDKkW.4j?ZQQ!EyͲl8uT:< ANh4G=.HYqBRyǑHtee-Jg2,M6i>q-EQT:ZŲҒ.C'u/,b" ¦|FQq+S3{^VVd2YǑvH$~h:y`T*AQQ6yʥ l› 0OrE*$5<:fꮢ0F8c\kaaB$B9UucsgOGQWӏE(z¹ͭ=>#ƐNcj C(GW|iy㸕5-3*BWj `n1,g]GUjկ~Ғ;p{h|5fѱ uI>~qɮΣ3s.VPUUy}${7>̲ ZH%ȅ1P4=4<+/jldRiNFKA>aXKsSyY)˲ܼ_@[X8k9N5!Ka cy~{g'*+* ~_uBC / (75C@@@;~}|g`J,F"QFkqeL6`mfdRTiA` 1a !!a yk4y|rLc"IDB8FZ`?[40l88Jje `9l2e2L&8K$*` B0 }!Lza 4,2L0L*HR4e6 bY.erZJq70 bqt:PU*2PTDc1@>Wv!H2)JDŔ H$DrLTDc1BQ3L"Z-<F_/3ADcq ZMA$I 4ju,Hl63 FH$DFP8V|[3  5B.dx"0(!LѨ4ɘMD2gqA/Bp$" y$JZ&ř̦d"Idd2l@þN/8 (xb4ELN)p`h0aNP9:.l0ƐLH4Md2Qju&( @4jS4Nd6[A4cXb6ܔ_0T*4 @,T*g/ *J"DK JBQ4L&CDRIRX 0(D"J%s)z4-Jqa[K2LF.8&*a,-MaՇ'ɛwzr^z}s 6[~;"LLM+ F97NÑƖR';^BGe`D_1,:{Yx?}KRY|gɳښ{0dX_XgN|ײspGdnOod 5e>~:61PɩښťͭD">9=NcE…ť%X<:>aFdU EN|~7:>t8Al//IT`H.UX<X;_TWU9^]\f"X0uΣD0ò ʤ_8olnU;`M2[Z\.T*Z,S3_6/-LLNkwwg\&eaeS h Mr\,7Dcr_ybaq `6=^ʪX,>yX d"i6O8VRRl6i@0TZR5!Q]Z]]UsW7 ߺsO*8J 9Gt8Tj]F"J/?˲,M^OrFsh4VTBSf H֝{Y') qXqE/_<'t\2(?^8AsT ֖c]KKw=HcZ(EY?z}<)y@$IQ>xW (㸊c[. A$E_Z,Ev[˗wv)~phd~aQ*dYePORA* Y\,?{҅+kY}3E"Ο;zsgO/,>9~mmmNSL<fǑC#c[ǺrGDYY7^^kj5E-CpAus66Ml\'u1 M3LIq1A KZD"QU ,^Mȕ EMu\&solll:y|}cON5[-fh (Z_WWOyưʊrX|W7aKB"H,Ҩo/:`!!a @c@dڷMe?>3 {@(A |{0Ɛ0 4f0aB 00ӟ|"] BϺ{x?uKюtp8r9DyÑӀǏu>j]Gߓ/<915UTXf$Ɇza(d2W/_Uv!(-)imiN&SW/_(*HDh4V_W˲,}<ϻۋK9Ag769dqܽfG&WV3LY{Z7ff)z%#;?03;O?IRY r]^Ym˝3 虹ͭ|H*0 @&q>vnl6;53Dxwo.,IRD2&5x|jzqqk3sE<βl,rzv[VJjLF#t*ew?9ݹ`jz" JGb`0J[ðl6;$9Ǿ_ϞJwvrNu>ML>~n*2O24< >z{rxy}-$I7#^zSL*}ں^{S]xsH$}~e*8Yw`iy`br:,--oܷϟHxl|" Mb'lV* ᛷRᑱ{K⇏F};($Iy2?T;0\nK<';>AQQxZÿw-MݸAW/_rt9tu5Kbч[H#Ng[k֝{``ЏOlJRc*xh Kdνy&IGQ{AAKK&'Os QNm~aam}= Y76"HSa:D,kϻ{޵=\|.C@l'0*oA*2LA^'g/\P` G"CjƇPU}xM$1 +̩.2².l6HdX, vw=#> %IBZ!ˮphΣGtZm?(L J4=26q&&?SPP`?ninrmAܻhZʏrA`d2,ǥ4 `X&+G?ztjD2FH,Jci=-xX%)dhO>1=3ZaqZ6h4X, tׯuXJE[ksyYi<%$I*d* Zm,_]ۈ'S[-7_lz.N#V+$ò<]<. ׮\z /'o`XvyyaD\0Tp E('x5գ:ʊ ^'zz*+DbQOo_eEyAiݾRЉ IDAT;s*/m@.A&:vD hqix`(TZZ\\\d24LLN+ #cpxWC VkEyY>S_1 J8ҎD,~y:hW QXd2O(*tVWUMLMoo>y|ڊv=aX&V*5jM#(N&BMucXxQF=^ߩ-&SС<Dz6[:ar\>>1yH"Xp 8" ;CíMU.w&9ѾrҒ9HxygO0.]^`R\Tċޗ9L&roðK7yTJE(DZLat*  LfY7 ^_բ(ưD"Ap HY!H2L2 #Hd:2 âD"yGK#}q| rF_㉍M"-%bG>afrz&E/ zzfvv~nD{9q?O7\S6M$BÑ˾t:xC#&1yj4|>trl^|B6Ƚ ý}&Q&b-d4NMln #S3;Bc`p8Y1JT* r4?F>LT*Ѩ4Mv-  [-f{z)VU/Bޠ Ba;G GXeٱe͆e=/D"VʪncYe?˲,qy~aqiiyx#0/zz!j4S V5a/ L&\^]+t:8nhxxG܈ OJ"r,H&Yi@DT*rIe"EӁ@`0H$"lb`0hBp&I&SFp8R*(BK-0cL}}34<ѓg=8NƊ_1ao8ѓg%Ϻ_ xV$#pbzvރLJZ?uVPpEyy}mOn߹W_W;6>Lx4zNn N<zSG:t PxiyM>y pܟ|z+YGOjk{`Xع;w75ѽ*=^_Svollӳs575qw!  nllYޗ}jh4$ɸϋ %% L&JNR \x> } 5'Oomi}vCt҅ҒBgEYٕKtZmv7FJ8.L# \ :. VNIqlGE"jq/쇛.ז˝yhTJV 4Cg2`0P_PpO&"U*T*9~n+G9D2yUD0FJ "LD1g?XX\H%Xa^(6ao>ؘ58K$L&FvvvM&M#cU.7  ?paqԉV6l,Z76܋Kˁ`>_A 7&1 \zxUj $0T(^?m~_[w7$ery 2 EM fIPPn<P+¹3/z^8օa[[b8gɉ?wʹaK+ׯ]).*I4<0 ns:)ϟ=F>vEuM9+. ER*-W SԩS'!d=/uu:sgN ^tb17m괚.?eLf|bRllZ#cW\LR//];<5fZDQh_1{!`o5e3_prta٧!HrDP"~T&aֲ!3 {)ő[1 0T4Ƿ77ʫk lXe]T57wv=^o΁r15=t-sb鰿hWgL(i

ՈŒѱm{DƖa4 ᧓h`$9HƩ~C5(wPl˃ #<97<2'Μ>)Jgf66j*geRiqq{4]k[.wgqܳ/::aѨՇLk>y~jzcI6zßf k8{_)7V^7'! Ì77yٟF\ma9ns56>Y[S=:6108\Q^r!(Go_08 _J$ t:=^j'7WVV|~lIQZe%'{*peXH@o[82G "E5L exh%p<@!q):cX:et>E&)V[`D"Qc]J[wRicC=7tv_[P*:C篯<޶XwV4!7}wet#Iԉ$IR!BA.6MKR)GAb@wHcohxΐH$ϟ-Z1,j2761 #w}}#<ϟ9}_<V!1\&BE==uDII1kxf?o^XV,BfUelx'gpDd2IQԋ޾<&1Z[jh^بL&St"-JUVR°,%SٹVgJ6; ꪪ/575v(7K˫IdRٳ~a\'aE26A)A( &D2@qbG|~gxax(?BytڋϚM`(43;_TTEѴT*%IB,D"Bu^ RfQ3{8R)>OV ua-M K&qbrt:mYߪ(h^8V'?*Bja{&8=+ $}rp<˫kSxb" GїXq?K`2 }KR b('a%SÇxXjk**9Hi 3MNG89zj{gholnl<`2!6 ZM> 8#HR!!0 APqQsDqPI1,NU2T,<@RTqQRPUkk+A2YB0 ,˚&Nq, UU~$8uuu5VYRAXZRx::v; pb1K$ ^oWGq흮Ύox\ >q1^Kf1?D1yH 4Z hZPAr0 ApLJ7JrVgO9[Xh2 CT(>uT"Db^/J76J hno*/+-/+eE^ t ynCQt)FW_uw2L҅sXnmnpfnr$aXu䝦ͭ]9zXtxWEy r=O& #(A/zjT"=ux_`4q\z)e٩+ JŅsgb8N7mť劲-hxƦRqJU^V~bqi9s(/y@>L2H$Egݗ.hZT1lumh4M\n%InUC#,#HH[,9D ApwOH$d2 L&+IT*93m7xlqiYLLN!;&I!Kٹy^/HV V$Jf}'oݹJbD0 L*2%T(E(Z\\si=s@ [(-) RVZ**i$ye VI 0XR\D>~l2XUYqCDro+JVSR\sҖHs ՕTyqܽW\C#Y_Z^IR@pcskw[X][0lqi9~KE$I엿^^^y˲k@Egfm}cs^`APn!f.-Z,7z"qs Wn J IDATnOˢBf*EQ4TUU<f\`Yviye{gd2Ȥ?X v8sX??dqb1Ǣ`0f \*n BQZ]VZ0RYRRʥ e |`hXQol\nV#YY]+-)FQ4VVS$slmi&I*J'y@}]IIqwKAJ"TTITj#c,˲\YYI4{liu0;FC>.?V\TAhXX\鴀H$++˳@$JbQueq۽DY_Xl{ׂFÍX-f_ _UY17qjuooG"Q[Q^fW7\aye6[Dz?gxdd`pHOt9ythdԠ5hX[r#ȑ#cׯ^y^8vm}# \ۧOx:%ZSUEK\.woJ>yXv)-)C_}glGizjz%IRZ`pLMxDH4 d9i4jZ|2h@灭 :J bCd6J%x"7Dq\2jd2ll6 P B mw}, lj`(hXX8>Uj^0D"qCAh,M"T>@VM/hf|~NJ$$I Tj2T"0r,z@$`ՊaX2T\TH&3;=I,h$͆‘HR3Yn+ " :ZͲl:iH4J+<"0,ɤ^="Q^y>gVe2L& H<(j& $ V Bd2B0a; pl6j E"qC}r z}eE2ժꪙٹΣGVse1Ϝ>b0Z,JYW[36>tkk__"rިgNzðN5ed ށ !W>/j*79EVތp 1sEQVj*Jܟd2iqQ*իדdRiqQᫍIhD"賫B,>Zu`0 DoiK"s3׿zQW_ 3d AAUP(_tohl6oT:Xq\Y 'D[A HN %WuuTwoMٝ=g=g?잝陞۳U*(zH CŪQQ* lSd""˸ydlLij4G YY5u 㓓99Za%b N4E#rĽ(pEAy6EbTZYQQUY104l3ÑSI1ݏ\ppW .]8-TVWVse/]I;O|yC/9 %-H$MwaYLgrdh4~>4PJZ KL1iJKe>|yAb<]<]<&ae%ae$l!;l:x60AkcX(J,~2yL&lJDvD,I&2ٓ:0[@v{:376>[+tiJe޾њÇ$I{MKO? G"fs>a K OM(ʛkT*e$iOoJ%|oNNM)ٹP(֨?q3Ģ'&Jw|,˦ 잾D"obǣc(txù37w" s{A*/VM-'O-,.-tFc'z]0?xxskhn~ƭ;MǶw;gfR~H$K8S{Gm#c, h4/. Bfo|[G'MQzq{RUeEWwp*N$ΙFɾe!IX"$>x||l6I$0+ْͭ>xn~A;?8v{>N:v,=$ @mIrܻM&#Bq7OK0Ou@'o޾ywRRRD>Ӝ"b1X$F/,.1 !x<! get: ^$B83;OQT4rBNvcs_$(r~)pjϟ=Ct:㉲4Moll}7f(tҙ֖m;B!˲]"ðͭ‚H4;p8x!c[;L*=BxvMM/=r$;].AO87'S`0 D" CEl7653 HT*#ѨX"Ѳ,DQB]UeE<izk{7ߘ b&34Z|~D,|g!AJ>FNRH$C{WVNo..-RH$ӝJ>d(67DD^n3c@Dր kcd2z;x`{D"jh2p+JΌ! c8C'J~?Eф0,LrDgWO*^_D`;;P(N1 ˤnXA1 B@$ im I$IחN.n>( ɤH$|6! R/Bò8CDBa,\{J$(E1FOxyi#?&gRd4|0 E1PP  \$N|owtaQ,bB(Y%dj83t$B@$I6D4I2 V)O47>@\.7aPq{L&-u?0Or{Vַw^d_7cy=(ʫ.ʤ2AprH,BQaT*B3Ό }ݲTr99Zk%%jh̛hǰMm]T:].C3\v굗JK5jots PfYrUVB mZ&&><(h}&5rpuU{H$ݞѱ X$ o޾oJ$.G"럩\|F667z"N&Z8].n}\FөP((|>Jh H4L/^N'tq7=X|E$s/+rssb8bttEb K DR 8.HĒ`(,^p E E\Q ' PD"BQZZw9]?fS$Ie0<T*0 O&S&1p"0\. "@ D&Q*a$9tN(CܜP D"a4})Vyf Á1dތ<y6_vYpos<;&O?=w{ϕ}b^y2o1~Û1 @0` +kxj@^ͽ}H0L[Gj7T?7ގD"S민 üt --c^PuuS8B˲l㱣u54M߹ H|lmoO:i7bǟONx޲A{Ar7{PeEP(yi~7FRR< \P`6gX¢D"7Ʋ?\& kkk;E"o]ǟѨyy99ڵACu KPG?Af4xf2*).lm9`Yp$|39?!I/﮽|8]p$H$?w[s*zX}|il|b{Yxܙp$(*bQD,+?}*O'MFGF t475h4M+f&M3xkNl#G돜ni9qp˃CáptjzFR7Jb{"/,~ O1 '}[Z1,@(9|왆cG _\ PVZR^VVEboB8H@EY  n>!3L,ˢʲ,4e3ƜO3tnn~`T*URR4619<:} h42,@^4YyEkjb[n nk |@nzfvqiYVA \[JV񦁡aǤRI2Ng G B!C]w}B*ՅgGƇGF7=^jpL*IPvrCՊD"!e].Sѱc!3ORϙ1p J,b(VR\DBYfs(OLݸDvE4Mg@Ƿn\n'h,vۑHdk{'Hyo~% 8I;;%ũT*sm'?$Imm9+o^BՇWT6ɟLx( әo2 ffrx"AX"տL$Tj_sf iy+/_b.-ܹ{h`(~9a~"HQSejU36f Yċ10 c;ajkj>uE?kƐde/+ьG$U_T3aXap`,+ bbq^\&cB80,rݞP:O'`abRr8(zl;T*4Rej`Ó|d;L&RɔR*`0HɴP d67GGΓ-f Á1dތ<YzMD"XzMF1‘B.O$ D(RPϯÛ1IXf ^T(Zqۃ`- ϋYXN<<M$Iq=XFcI%T*ER\&K4)IX\ b1 E4} t*%Q(*HBG$BL&q0k! $Mrx<. 3 E L80Lr'ED"!P (X|DZt [ߺVV׺zzj|rmV'R)'`~aqrjH]8y;S39w=()*|#LyYr(.0`ah_+eKKJw1/.gn~A$---wvo޹n{66Rɴ-HJbAd﾿!J H_UnNH({JՌMLM{<^T_ :7g!I Agrw?LFTW<\i E^BDzs&(zqx^|Q&P|EyY8d:]P(q|jzF,dR$|o"aOoEE9B Qӳsou]"D1"1h+k E;P4_y`zAZͱGbʥ Fc"uƼӭ (8?we76xU8 p IDAT\N`cs;77d4675 =^O3 0kO$0/_l^omcr~Y]X\*0,+vv֟:ybaih0ECaә~??+3sӃY*qNG8_8wV,hSSC"ooM RR\cd׌P]NpzvV,/,$}h4zP50ãc:]RH$5Ltu83@^'gBVEyɘoO<^CՕK+"HT޼}WR~ʵG굗^GVy|qyYjmaF- `0Q[EyֶR(//0ll|b`8LW"||NiI;+M/]zFF9&d3 *jjfPU:V_w*7*+[_޾sh*aWoߥ 66&/?;7]YQQYQ~4~kKK+.Fc]R`ݗ93!=|uzfEnP.Kbbk8v{jXZ^Y][/+-oos˲PHPe;4MuEf 7c0O(ADqQFxxxxlgç\{GWgWU8˲vWOynήXvv$z]YQ>6>ߔH$uqiH/,0o ҕյյӭXZ^ZmǛQZ]];vܼ9uD*(/ޱ im9IY]f Á1dތ<E"@@H$Anܼrރo<ڢhΟ; 3gT*E#|ӭ-;;H4S[[Ʈ^ HGgϥ 縒7c > kg8&Jϝ=CQ P"f,0 MCYEp7=r  \f<Ϟ|>RW(ao6EG ԩqe> G"-NΞ?we9ϝD1ʥ C#mfvo.|ґZnh4jkFc*+T|2pPuճ_Vz'LM˥V"I2  zŊt#g~ fC2h+H$$)(g2ɤb3='lBX,:]h4ZTXv{XaA;kT*d$14MX2t.@ Irk{GשJ@ |Ep:ZP: JA^g;dIqU([":΢BPȲ%\K흂Me%xryRtk{;>   2!t=DT* hjzyejrj5d2`YE+ prj>L޹{/>h}7@oE0 {ATwOǷ0 znOWOǷIj!6c`p`GFǷwNg:M~t 7VV&QM{cw3/杻p֝{G;;{0 CpX,~];{{w:]{1vtݞ?@̇7nFюEPZ| Ym-t{zFv,֌W}=n4tNWo`n~ccw8GCN룏oh1t:3)Yx*-NR @Q;;ՂťgOrsZ WTOx< h#xDZG}>?@ 4;B67'XbǤIOo;|ZIbZeX6@Qf/`7-'WV"3E˫kϞT<55S(Kb8 >1hwGF76එHdlb ڂF"9 0;7 !:z :,˚MW._\[_Zm?9]9EQcS &h,& #\. `P[m޾7^uyyLMϬolJ Ȥd2e"r҅]3#odzv6 sxl~gf/]L d" I*JFE𡞾~.Dy^yXΎj+U;3͏GLBXllbJ$b1 (#( (PH]hT)@ Nc8p:%L&r^7t? }aA~kCV/j/]8qBm?R_W$zVRRDt[GW:nm9r;z^<-B_sPS[ۖ򎮞t }ꕛBGΝ9]UY%Jegw^S*,˖,;DppmgN8..wxdTo-,.x|ܮR):ݿBɘ͑J%˫k[N&mOf;ϴX,sA_][ڛ!yF~A޺s842z` g`Ju9ajwvjkR)r48< Ο=ML{WwQa\&ZmM lomOL\{r${ ͐i1$/n!(J ,K3̋uEf 7c0O @ (*Ȫ-o0ϗ!ML5RR 0,,eia&43 K4Uoű`//x<7gY&LLP%Iꠅ|M$R8'?]#iiH*|w^L&j5bHD&(thu:PPXKs N P|L&s=ԓI= E=CglLb!ը. kowjÑј dF("`B0id2Rx y(FPĘg NL5zv#tD*0, ҩt:hnNH$r> PAD1z}VΩm)^o I2JqceٙٹT*5;ӛ1F"S,.47L 3sEÑNÙ1@WV׆G8]>Ͳ*IQ# >m;J2yOx>sVFF#J5Mnnh4!L~)7'ǜ/4`(0q/,(h4yyܪ \nyB@* jZ$L(yyFR* u6Md!|q퟽ϵF~vws{<;Z)JR<<b؇7>x<ޟn0,޹w_TvvHR,ˎM@{HQԃG{n[,$<|7eX$3֝{Ņ7n> vuBgEEM 6УR!7oߥi%%w?ȤrNz死{PRRtÞ>H82:r;JEwONV{c13;/ʺzzJKmdL[?X6JCERXVutuomܸy+Jd"3:%s cf++n߹72:eb2ud4*J;e8Ms|0{jEQ  øo0 CQqn ]qnCnEQ E?!8tq WA4Iim9)J)+-DVr aɈaX,𣏋2bz<^?릊aH$E;ZL% ҲfwU2}Ç$bz h^ZYb؜ob]nn:~W|~Aj3 UZR\R\b&H^_iIqtH$WAٱTJe[GWNjUWWT:m7re~a'M6D}"2x "=iFєɘϾtz<޵H$:>1Qk\.wLxCkk[[Hdxtlm}}jBHT*q{<$IݽH]m^afv~gUf!kvT*wuX\YQ p0 E}RXU[{DScYvn~D,.).jl8rgl6fK&S?[̠r!* Gt2 Ct~j#Xo~l.)*Ҩ.;&H9_{szfaꪪ NEQ ǽ^D"tXaI=$Iex$óIs*M/g2L%ߩs w @Y U?|zB \;V9x:XI3AjXI(}VėaV'aQ'?HdT:hviNR2$O`P1HO'>Yw+efkk; *W_$O,>TJlRÓ`APo?{{qi6cv VT LJ,V[Ey\&;uX(j8L*T"H%n9KĒd2){&m%V(4(eY$II&S( ';ѸD*I$]Aܽuwݟ_XbFױ,q{YYg&IEq ,,0s9YKhLV/. h+ ٹ zru>H4j -eSsݫVd2)0NMOvGWO_"0kumݘcv!"zz77L&ȨH$TT'Ok= ô piyuxt,7'e흉DBMLM tQ[A%s~~ CӌX$2M=}V͠ /ƕADήy^>95B!˲ݽ} }DR"`Yvlbrn~aIQm`(///O ᶎ.\V;:>>95 Xp8bއ$I!E\ݏ&Uxrbg](*,$ ?`ZKX=T]Y]]UZR|^ۣ|aY?3Ό{k ì/~xxSWwE&Doqݘ' =}C{}L/?4CT*0Lwwo}]m{gw{GWuU^\^.,(q{<xbbj++D?9r}ٹ@{gבaVWb嗮|5APNTH$ n:1 kjl 2/456N47 y^d2?IRMonm, |>gƴZ9r{b J)rFƪ*o߹&IF'>n/+-1\WrF_~p 6JeX M ǸEE7oߕJ87568r76KK%T*ܪ(Ie3205=t\u a#Hˉ摱qͰlQal0 ByN|ɘ7gz_7DBRrsC  zAFw,Ry1/Qj5%SpÇkp8`( M~^9ߤQرGR)恡a#GeO(*,]TZ^V|0Ppd󹳧Puի^9|DP(r_{sgnEBnKWO{V yѣ!;;'FF׋ O<0+,(p \ZRcڶXw,rlZx˫&Y8[VJ >_RzO#0&BRWWc;9g]4w73^\\>λϞ T7^l_^Vf7ol |SQAޠ?wL&j5ΜV*yz:M"RXPaZHyJpdsk[nw,[;*l>Z_gbo^9S'9zJՔ@08=3p8V=~ j5oeed4nX +.0緶j4@Bʵ+  ץIrkgo,?9,sݞR?8t֠ι9`BAE Z%%Gj+KKi9qQVi ⢺å%e%w~nX.x"qx"P(8+JBa0V֖ۙU%E8A[J%:͟=*g.WcwX$8^`6/,.]pH,q\Ph5bFZ RP6B(D" MqQ3e_AՖSbB֖d"119}iJ5=3Pȏ/.*\^Y;xOmss(77n9L&K#\.(/eǎK+gϴJREܹEP`2֔.-)ާל0a"H$Dx"q)HTWU9A\jB Z-A>LkF2jm9URTč% 3sgO=׼sA›1ތ!f s [Q|yF/90@pdd*hw8@X'[dvIb栅|b 5HRiR\ >|yA I4I*3y(aD+fX{,9iJ٥hLq,;)c_xcv 0 RBchgXól`?{Ǖip-{K4heIQʛޝ7{fwv=g93Ӧ;I%{$AH}Dw ADIDB@"#țq~K", ƫ`Ǭwv!h4X(3HTe!b!"mUB;@#P8b4dYvI~vxf s SU#W[-{{ ]L2@P,ؠ!ͣaσaa4 SKe3{9d;CμɑqŬ7:oS$iX]=}u9cf 3wh4nWGFNJVV׺{z}~^k#cPzݻ?ݾmye^wO,%nwO4+yf ^l+..B=5 SӃ#EZ-;wAsy08AoXDI{h^[X6+BP(p8nt^Z^)/+i B[>dRědYuol|TȲ J/v6L$^|-Lc<95r:òfk#-n0\Ѡ_[[B,ˮɤ|eYCz,_Y]D"<AǓNg|>[wDQ\_w<*2@].upB6m2Om-ͯh G"ee#ѱ Eq垙OU$IS3y~@eA]n@iIk<84RVZr7 N|}7o!JEc1@:|^A/v,r;w<kl5Lo:i6 S,(?qp~~/ >xН/@Çܸu{qy9Ee@0(O;<:V[S/ Oxey`h{>ǰ{;nܼXA38qE`4} udrmm=71GcX, JR^vd6uzᕕLC#nOQTyYl~I\Ty_) |Sk'(a,2,E!e X p $Ie$wVŹW j&):ؗ@0xyaIW0ZX\jl$ 019t/_07o޿w t&`9&qgΝCl2l@QTC}2u0CH(C  *a /ҹt*A:^rh۬tOKkl6H痖m6a\Z-8_zݚs=Slb(.$x~Q>FiiɹZb` Ö=@h{{DQ\T=vkiNE@F`Kss vh0466Fs$DϜ;JfS'Zp\6e \N;EQ_qs=9&%a pXocpq {0@Ѩ^qLEQ1GRi$:d2%D d*h߭ <‚Oܸg2 M33nG榆x" BV;42.^qsSX-..Kz$Nːɰ,שּׂ1/:Or]\U`fZv׀Vw}x?XEEEEEeP`m#f f ;7cX^YYZ^q8A,,.qٌB70884ذ_g^޺{‚ggF1c\y+ G+KZ(J/^JFce% /,,UUU ᵛӵ$IOL 56, 3k7:zfcٳ/OT+{vf篬(vLkK(IoJJWz]zYf3ťo#Jr (l2}p(IE\Z-OҾmqi+k[焱apҕf6cㅅn{Fc0.\x+ˮ6] Fw3LלQX9sLMuEQ,lV6˽3 ¥CchT \?(ː]9c BXƿ-)%ģ"dYb !eUgC@!DF{^K.wmb2LZO?^R\J%IYy<^Bsx<_p$Bz`qqQ<0 ^|$X<4UV06K/0L`0Lr  VW ;w=|Ch4 8qخֲRЃ~ax߽cJKJ\V?z IWW|^w (l2C ɤ `K B޻qꭓo([ /HWOZ]]}GCQZ~o z^$?p؋C`F&wEs5UuS#vex\$JۧN;j*+:Z-o:i0jk:our`I,,C$IQay>!,$eYaB$1 @<o  r68eYQAf<#A`ه/0$Fr(,s{1XATBʊҒ?V1c0^W,K* jk?JT,s08QZAcYh NAsHDI<=3ܤ; q ʊDrdIPF#J"@[_x(EQBC#{ۗWVWMfr004=ɩiIhPui o9|ҕF̽DIh4Zyat|"L4L߻ooV,"(ApdiegzF(Ӿ;RЂ `OG7:=O@0PLO$Bp"y(Jv{ f'JBյL&/ò>_EǛNcYVef(Baz<^af' A^ky?4cm-&q kkښӫh4j= r\VCiiO۲2//]~ihxt`(Ngt:˕T$<^$JEۓrY6Z k7oU H4,N5-M p7-^rntVs8lo //}xEE9`m}|umMIʊ \YQK4+,, Bμp8ɤ^7̡plr 83OՂA<(>k4jkfg7w?qiA8^!z-Mc!y^)&$Y$?Qu +a+$ RF1 (X aB(hZ^4Di`΂<˲YE3 el6K eYie(NUL8( 6ޅY Zd*HBr<eh6Dl2$)J!Mkef pfu:m:[XrY,fApHSɄ,ɘLF$SAy6,Jl6ػu(Z83dyAy^q!Τ:Vy( /9vj4(KO'2E1 DH"Vј0 Aŋh08(1 6z 1 hh 4@ t811eY_z?8+7vo_:eZ IDATw:;wwJg'lVˉ8ѱq7B!PWWoO/ͯJ'^~3 z!6y٣Gnx-7{O#LM/p+ϞO]r$IO(㟜xX*_u|D W^޺P7g_|oMT*"޻jk&&Zvf:9kD#_, XegPXXuð}sa`W[+N<{vUIѴ4Q$ؽUBCã:v[JLCәk<;yL&,,.鴺x"a2xl21 Ӿ~MdY|G}[@TWW8~~`0{WаfNUWV~ıH4GƿoC!-x!ufɒ<84|;Hz}믝yfAeI$An ʧ|}xd P{W2sOۻo)eF,{=VUյ֖uQ>u߷wKC`n~aߞ=O;>1 i]$!e("EQ0vC(tC9La{݅4Awq<霜nmis8‘fOgÌIyxjzvvnaX?k߽ I Q\C`N=A}~ogakظҔW1.[E9j)-o \[w0S(Gu0 (g/S'_eYѓORM _veeYNRp8x nܯe&3{7'ꦠŀ^~tuumphZHUV}͂|q\0`$8}fη^VZb| Q_3/9wa{gfT:L&o9#O9{}7EQHǼ_=Q]UUP{OiH7{l6...xe=>dtV_  G!I2xŌaWkhKk뮉ɩg_UY֩7 ׿{PE^̹ !)*~Px,NkjΞkiiX^Y1 }{47ah.++ۧ ,fs"(/SZ[\jml8({߮tS3g_|$)Bl~{:2r,>4ce/.-5⢢X,JӴF : ?(QnK2Ng2a h pp3/Let:iyx"iN&qA5cA<$uv 2q>$^|_\]}v;rpdG*3 HTj%BNK~ZCҒRVgZ#HQC˪l(ʠ,  2rYDIjx<^UU ! %.nL۟%SC+'ouQQQQy"|CedJSF")^WQ^$K$)#hJO$dYVE"(2$I8KNӚC6$LRAeeez^Gc1Y-fl!// N5A`f13q՚JdIIF h⢢H4StQaa,/.*&yPQQQQQy|d&IQ|ÅV) NQ^@&SUXe, 7VTT>ՌA5cT31'+āx\P1EXۅZըf @-6jA!$ JJ'|/2R0(2a9(P@)$I*zL$aF"E7BE\_uP8M#.w:ygbjԴlD(~p+_/1 a@|sD1nXRRLLL7?2 =j Zegذ}pxitlOox4#nݹP޷ ,b&I !t~oUU] OEC8(+;k#c|gީ7_w9DIYxBA%ᰍMd2jx}Ll6Gc1y;T`9'7XZZ8~Ӵɩj?!DzȘ R/,~[`aATj };,(²,;q/Pey޽f'rcl67O@8x3O٬D"0,['noo_?XLT8KO?}3J̻wRh'oQ$ NĽZCx.lþT q\o0 ߻7Jݾs1UZ /$)I2(ɜ9w/.-ܞ=kk+kSS3kk+9"$!P% m-?Y̦l6?j?Z,/WV8>L8r" 8oYX\ #?/ܼl~0Tsmv1$!2rD<(EIe~j(} AaVǟ>{WNluA@ʲ8{ͯ-(QdZo76" A9hY=oqqba"IJ%Izu:]]mM"PJb\e%M+/[[w%ʦ{0R~R켤ʟ $4k4t&Ѿ3cV!?0޶֖ᲲҚ?m6_ ØƲҒw{|m͍ u˓88 ^~_Z,{zgeY9{(I~jt||ee؋Gc]*+[w(4oFt杻piߞ4cC8S& $ɲLoǓB5cA<,˓S9ҙ Oquvf 9@5cg(™ٖfSTTXu,:N|vM&zE?Vh߽kPQQQy|8+NK$!]TXIMD:)+-Fc!eYJI******ȇsV hFcEEPXٿp:"aqQFC% $)l:(I GC&iZ`HF  F'^ hTht6M"~8$cU0jư-f 9@5cM&"nT?_Ų$IؼᆤOB3 S޲|1T3kPՌ|mH!|BA(IݬiZ $I(W`:^Ǖ:GL%eyYƔ-aAEepl:6ض="$I ( P )L44 )61 aT2d26vsy>o~$ IMX,BlvR)]kX a\n0djmݕH$%Ix}PBEqC )GaStWf.yh]+zn'H2 rKRtB`06ޣ/0sdvl!h4v{xg-"˲.;ʅY2x}Aaף(0 EL-P8O3O޽Ͳ[u!$4AoܺѾr1`Yimߵ$AܴUd" dyeb篻=S3#H4a_X\BvXA~"omi}IA8CX Ly=g^޽ ŷa|ȡfb#c&ghtMi475 nc=KKҒj4.SZQ9Wf=xi ]mE ɲm)hN\e˿s84²CãrǬJKVv׽*ɘL@ MMc4:v n}w[C]mҶfǻ4MWUTTWUnQJ ڠ#(u.l6) ?OGj(/7 ^W[+I@ oQRvulb2H:Ҋ]Y]KR5իk u&``=8Nwf}yyy>Toi` RПf(\sZF`Ռ!f KʲL?!!A))&BHPEQ Fx$+ `2T|9$8|d3G_8_j@c̀SΨFP !!B(ZӲ 1!Da@P 0$#_&lm˭]#c>p"+F=,cFHD #!$(,'"pi O/^ܮR4;/B|w\QI1?q}DNb%B衙0BB8@Aʢ,!$0BAw`?S / O0\< ݁| ,ɟ:CaNSvB3i,AC^-3c({r!ďLV$$# @^aNR8)g0c aAb1IQ U/N0wwkEXsvbvb֓}/;ohId+Q!YDW*"IIq ˢ,CAP8I hhHf<p( IP]VQQQQ|˟f85 Xh`bBX<4Vk5 xTb$Fih") 0\4MR02 [EEEEEe{ْ4 '1&J@ MIDAT!8Ch -pu4A$C!CAYE,#($8A@Y6k2HzYp(Ʉ#Ѫ S3F`(1s8*+0 KRT4G*ARt[k3pdum2/pVUCigJμa@qQn_Z,s jmɽ`P0r{-Zeyaq㸶aAX^^(/;쳳8556aP_&fL&cmM5M,MMT;y$OvWW[#Bh]SBg^^iIlyii~ðd2JgJ\fE7x|]mxbnijuWî뗖W$Ijjlxϓ% |V _0Q07P[SMbuuҖp$!,w=htWk u[u|>Q1Ҝ((04$pBA(!//ki;kH3tD ʥvkYf)& '=opU(f=Λ(k7Sӿ}罋lx<&$uG1ѱu^ o^xl2]ٙ8Q/]zνTP8\3 8{" rܗ\u<_YQpbp"0=}9!t `bo¥F̹ 8O;e r/$IzT:m2F6rr߄ 8s N#].\l2***8(/#pbyeF[tmyB;@!t%tF' x}c.әEk.,.zٹ:87H"VVW?cٵuׯyeYںkhd@ᅋWf+r}O7eGXNJ`8$L8IA!@)ϑ!)OFbh<OfDAvѪ[zNgݠ6kiÒ?|L, h*9!ǟy)'IA8]7\n7A=BkiA^VZ*˲fizqiyyebt.).njjx߽mCA7Dc=y]7o?{0ll|baqYł|?ptl|aa`n~VWX5Mpi"hQa$ la?Z,f:N56ԋt9%SD" wAhW"(OPYQaiZc1v{aA{j򲲱EQhE[%d6k[K,,.EKW295B筮#?8mmEp$z]\(o{}u JOvTUV45$Iv#TDs.71h4FN_2 &4H2I &( !X0LLD")$IlA&Sg$2p5ΪbWD"/j~8  6XR_fw?:}sڻw Cm6S՚H&Q KK[Ndh,n0јޠJD(ۧ>  dreuyz7nf2Ĵ_XU[0fFQbS3Db~~yOGp8r{B0˰vVUVp8,C?+nxPUP0 +**t/+񇛂$2ul҈2D8A BWl6+{wǑp0 cc0FNJ?a6-"؍sl /.|죪à%csSO|Л]U=*I,ɲ$+2r)A! D>)J)r@겄E+Nbobuy~rXl&TqfXU,Rs*؅wZ5:6N<}cx$eii9D>CuK衪=>1}6}67B};ƙx"iKK?\ЧjP8 !''&Esϝ1g::Gn^;\\>p\WbiOLbu'8}e7mC7zlY\Zz豮'ZZ8C}L,Hg2]g:! 7oÖG: +$:yotlBDsuߧ}g{/,$_qX'3ihzup(F>~;58v:`@q`pt{L<}Xqvuц@@1MoK:_hruPQGsg{v!WM]- cd2opDEMC(U/ yLT:gc4@F#Q>#ăb9`AMPۡn&^Q` !ԃV13F)2Ja\l1FnPTU9몪CQoemhve!t/>-=x) up#UOMS_ g'Zo}0+nmT#| S<` *3rBEY2s>6>OF#my˯.!XӋ>6vot4ΎbCoO7Fhl|byy0ϞO$Zmdz0/TU0ZJ ?^<۶%u,eObŷ]w\vҊ#2:f14oc[]~2\>gmf_]aO|j{[k5mߕSSzDss]ݑkn<925`3 LgcJ*Dz{\lnf&^[SIܧ#Ts3vO|wbr:1;{ǚ!bM׮ tw}z**֌J:쯃`ۋ2." Z>5[JeuvvW^ !ܫgOX @X_w1&"k:kۓnP}}kjG"a cPmVQqhr1y:V]VUUeeEJ6FGjkk-R)yXN% |\D8|V'JiPFi29_I1-3itDC%v%xjmmǙ~* qضiBAӴl63 󈦩m+N1 #nP0 R\_d2,ˤfY%PU5hy,˜s]˩dP(bMelPUU!Axìe%m6&B:&g" g׈Cu,"UA(]o`Fَ˲]w$Io,%1$Y?.A WJ5X  XAW%>@IENDB`gammaray-2.11.3/docs/manual/images/gammaray-qt3d-geometry-backface-culling.png000066400000000000000000011503421413310443000272230ustar00rootroot00000000000000PNG  IHDR?Z pHYs+ IDATxw|g~P$@{G{g"{@B zq!ƐKrG.{bFA^k; Ra(z! 0L^rxԄ̬̰аEy  Fg2YmYm6 ]=$Ga"H@dmş|tv6 -4H Wj;#Hmyt!].WmAjsCR'4H: Af G H R ?(z! ^ HSn3w~m23~ښ>AV~X,HزEѣjUc=wT:Wkl^ 5㢉 ü{v_3oQwn޲--aJ;ull^r' l0p@VQ#߫B Hy+* Zn/u'tF?O<>MV,?yrfhHHo*zxeᗝ [z# ZӰWrgn_-c#>=Ͷb嚫׮y8goٷfo۶ͤ pݫ|ێ7F OV@Q,1 =C+3gŔ:/w`v] }~).ǎ(;;ѵk?όfwsNGn;L|IPjdZ犴d2i݋rR+jAgDxoem_.4;6{`PI5mҤ饖wc9ktiU궮P6XDsޭ_)ҸqNs/?~㹞ݻ=?Pfy?M:OQM} 7>lָ1,].x瓷%fjBRlMCFsO?_J% ?vHo~}=2vtVvͣGh߮-$##=s=ޥVM~M{ K\ܼϿgk3Vn+Ą}_~إstfK+zܼ]ĴN?pG)̏>x;w{?~iV+Vl~̏ 6n)0iws~nR5MZҋ}?oG﷾ ϜMifs^^~fQdFR90>jty-/Y&J߅XAv.H]f\Q6?r3?ogO|l\YۺBWlΗ ~lW,[hRK 5/[낤V\흞i6_=v?P7mX3jİRVZ'7}?efeoشeѯ -i[Λvs`[Yɜǵi]|bֱRS,(_GGk׾]8pm;v 1v1 ۽kϾ>-b񣏌8ΣOL}|@ P*'/`a8uОaQ#ߩcfVv9 @"0 ۧWvNnkn EVB./>QP8]!P]^q$##$9QSEW8`0x[oOjF&zP]t *enR)Fcd¢WfF<ϲ"ZlʚW@Q]{>˦MOxw];~"SO._4IߔUhfnQ ?T J0ͺyT..eM/|L&Su,KE"R(רQ Ve*m,g^J5Gƍ?~T*ұ}$ Jtd澜8Uhޓh_J ԝ9{nȠ䭬u)kzY |Ȇ_HV7ou{ou:a-[W.Z1,//c"YY-?#ڌx̔*4D"-q\{Vp̨[8rlߠ+|D"Q۸6lvy-* ?+/=~æ~-''ya ޯDB}jLy rYbb?nɼbATTpRM7i_~4<9fԈ 70>pٳIC*/)W}*oSu,YgN&<vz0)GhhHHH$&v)q+]6`~x',_r[ . .<PTF}lfaS&Mܸigp:DBQ߾KTT2hBpӊ:/0k׭q<(({wKĄSwPbz)+%UhO=?z};rl)/@5`|Em۱kzRTZV* =)+W}ii6Zbʆm\?[^xt$R*N|EKM{.ǍݷvcOE<1WjZfʤ X,kҫ9A[nm{CBJðC鵢 + w?>R/LLgiݪMVg,U)KM}hJU ;t:}!yxm}}SihM{qG||W^֛< GGMO9׽.lڴ`(A^f£7ل.vyyM1L;vם z1_|5##sQݻ%=ÇI 狯קWqպz묇'a[__4Csz؎s  E/AA APBA9/i0rj\Ԝm}} Mh(vt\HN,TD"B8tc%AyCAN^^tzAB8/A APBAAE/AA APBAAE/AA ΞKzh  ?(z! ˏhw8vSeWÙ~#Z}jru[kfY[3;L^o=w+drǞoJVfصBrbyP*]}qȱݻV">=}Q# )8_6R)8":oҸQ98y/3{VV}"^W^[7i֨nj1~ܘjZw#1lƽ;оմje4\fXX M4lZ}۶c'<0 CQUsqnzFՑkKu5ʹӫTyU^ 8UhNJ:ҋBzmh1 ehƏx ˵b^عkOQr݇/ g~1{ѯ qNnkqH,'~͗lu/#nެT8;~dĄ`۹j~&ˁk+hy1 r:WrŷiO~[$-LV*T]Լ*E.]׭ߨpBD*m,*==Y_ˏv׬]!"۵?n@ 11-  _|on/ư“?]۸)'-4$8sf{ګ׮%~8gۢ~}zo޲)zB&Ol:yyxQ#Y^,$йoSY_}9_ޕ(ۢ윐`8r\!kӺɵ\oǏxgnOF${:Lxd))֭ .^{mڰJ$vtsQĵi5G9fz'P*,lXnj #G {ɕ(r,Y*WYMN? nw-F#6mٶV[DDܲE4?|6G6mؘ׮ðWEwk1-[p8^M۷wY(5qc_$I~5;vN˷;s|շwϟ]&Λ3W֬++AJO~78:{|;z8rwܼ.]+WS";w4œf?~_lqn^ޢKs.0gϝH%osI;?tn߱kOHvm?Cؾ_,gY̬ ,u?/?dۦu/\PZO_twAƽI/h:4rE nZ*6૯Rj +WC7⨑Yݹ{o̔2w@FFj۴~1#?xB!ߴaͨ~\Zz6oX;z\|~ܠ$@=u^o(1 q>{]_:]^oΕS$i6 _@(ƴ2h@&/^h Fc^=Ѩ/˃@@ ؿG7o.J$9קW>N畫e#:uC:xNxsNCݳ{Xhhl6_ڿ_JPEs8k{橢y;r՚|Ukkb'Oj|媷9q#cGD"P8߳gUli]v-8(MVSʵVbf7<h5?;{ϿZ9\ )#(J& Eið,ðؘr,a)&H$b ;u0 {qq2҅9xd:srw7٧%X,~NSRX=w|.GwTbCF<|cM*ղ24M+2 0Bj6lڒרՍ"+ĺ ðogn_ϙ7~”?v}'%1}.8ΣOL}|@ P*'߾s7Tp#k//me ")F1c Ì9ǰN3*V1]ڿ41sHH{]K^5e $1 ڬ0LNnnhAfQw3 P3<d2iPP`Nn@ΗlZa. t%@L^~~Q ұ,C$IQYSH$} 2Y+ mS(rQS'=hJP]̝Kl|~ًl*K^K=rX.1-巉_|.oGl^Of~9g?q<Ʋ2  ߫> 1-_~izD'j䔋?,k_*ZXx8$0 zޫ_|=Tp#@]&lSJ٫hxJ rsQػUkrrr-?w8,*YT&K® HTrH$N E) $IɜV.],*hС/̺p8K<.E2)~p\pa#B^ؖ7S.+:]@^6|`NVFGD9v_tY+r ÌF7M&oW9= e·^=w]:wi&6e9| @лwG%\,Na_i_uc'N~А샇۟JaCf$ x?&r2S8}3Ow?d*96?;?.rZ1,//NME7sO?SƴhWQ`Xf꓏٣۵ϽPؠZ&6S>qB!aN{tN7rssvMJ$v#+;G*rxZ~rK$btA DmZŶiyI IDATՓllVs]cHC@^Iş/> pXbBgݽ߱cABkredfz9qX$ qUlΝ{ZƔHa£?phyafKK$67nJӴj۱sw}aTe%nzF,!!k֮й7 ¬  V g[ޯ:w{>DNDN+VǩھcWtts {Uk|g9)b$\dF zOK4 6m}ƕ{\end2XzЀӔnD\'}S|&Y۾cWoAgi:##A( z-eYsFZDx@.#3HTaX`ƍ4 Yy^.VjJrΛ/ _,ҫGEKNK)G\'$t.>7_eђeӞ{fK$qcFyԱ/f߽;3%&wܟc 6>x-DKΛN|jY)GD) -V?ffec&t^R>s:{.{fC]:R/gّcL&]{WAdr4ExaAď|^2D#WPN*C`&J}zAORVqAĿ] H RQ; A8AE/AA APBAAE/AA A)j_@vvvR +zY<r}I͗j,HZgycJU3?W5X>pvK"5I7g<ݿ >P(T%,]]LRU-Ỳ[PRgݿT m_Ϊa=CBB>ao{egg~\î\upF(<7 U9D[ӔJm(XйfCE/nDfC(T= kW E/a*:RXU&v{j+:¤x˒WPZjrQi^RJ=+#^fB]pu@Ju}SѫBwWS5Gzၷp ԭ>j?z/~Q|4b 7n;;§q )h^A)dQ72B.T2uޭ*|˿c& < :z]4 huJ\1=Zih UHuʶFTJ/C UT߫EvHTF Vї >Bѫ}_ SU|^فU9ЋĐ#75zU=Jݘ+T|Ξu Uc_:L<@ )^bRd ^PE%*[\5)+tkR}E +z5`PGFAʩzLVr@Ջ8 jaExgakǚ |fB_0@ѫ@$ HF("T7j׌,Z+|\ D/k"C`QW^ rhMY=U!yʟ|ߵҀ&CA|Qƭ*dT&zArClXVcB5r%5Rm[u]m0m]VˀU :smJV靼ZG^~~Iz Kv[*5* Ozjk~kmm|d耪>~)ۇϿ1_4 R^V,zZA,UZP A)Eu(z HP=YE/AVOoVDWѫv)A Am/A*8"^ 5ЬBFb)C}(z'("UaNe5( Uh@?PAa47Ρ_F׮ZVS~#f50TUI#Uz56ܭ]ŏy+vL_+T*TR&EaW_kLLVGe=*TZH19i k;;C!A#i@W})iEh >U1IcDǯe9rZro# VG/z-\).6YQi7%F8\&81Lo"]V;{ʥ%`w͆+8wbm*}DcZASRZ(kweP\, z#R.XNi)AKLֈgdzS8](UZ?c*9C&4Hh Ҝyt@nƚvs6KP -@Cjuܡ;i5Sդ?_}dqBhA;1RN` p-X:4C0,0RIЄ!haYgYp8x1\+mygX͡9HR [7_3(`ގ:8g) 2C-jnEFfݡKPCE/i\akk_gG}3`88GIpKhЩ1BAzh$8d)%I"%`;T ڝ?~&9y^d]C q 쑈SEkogp@/; Ci!ӛǰ$I0J4T4ŹlѪ5쳍VE7+^H4K}ĉf߲[m <4nfZ-g.xoT# FPr*e1")Tg*qu*R(r ͎bM`wѶp Ecyo+r $F ]sd)ҡVGym`Ѕ=,,G t Iyw6}S;Rn)ԖD/tYD+ PWyAƥݹvk"g:wz<`#IFHy$"\(ҺYPS)q\hs8.1YxĢ;br*8g9PptB/݌:z^ol@u>y1BJ[e\UV;Cd.-lY#Ie~ҋAy㺽{9+lW]Q}vxh(+G s(fpۣV9Q*0+Ge#H"d+)1[{8Βȏ ψkڳK)u*d C kc[Y8ݔM9"r{7Mi9..ԡRm.c"rH7M0q-@|ݩt&4.Ҥ`p++X$;0˵=L]z{˦]ȡC8McB-,2Ӆ4Ʋc yzrYЎقq<òTa<c 3,r &ty`B\jZ`T[^i$w^rXc <4Or$@!DH=~_w:łs;zӣG,Ip.3U_@,0p;>g؈Hq*$`,(8"'K ,"MhwCx ('?*Q@5K,=xnz0h9iۯ?p3rӊ^ѥ $̈́h|:#ҠDzbYVhwFvg=3_ўGAjH^ZY(*0 L9rn0"_905]Z`jlѩ3bwWgIV"2]iOMc`h:EA*#1Yl=䥍3f;j .RԾ/'^5S7u"V@C^]aqIprݞ ,Ǔ$x[_uOQ$~?^lN6-x|iEAD+D6pR$T{2d Pڴ* M3т3β8,Kn0VG䉋:dmnW+NW3,:xjHi@5acؓ#w[o?(jCe)rw3x_kt5Mٮ5Lzq),p/_?腧fh*oRA-X}?pv_^"t_:zȕ}=*i&lNi[H@:vd,{TcSǸFe,ey(-IE 2 ma,s,AӤEYM% t+4Xe vsJd 6I&b9V;y h0VuQy"rZݭ_4off!Wn7-7uΰYWY{ G-8Tm]2[.u*"rEVGd>tjaw;ƴp+iR]p\!:^qnend-h|e@SN <ᖈ*%Ps{}/OpPV`d3`bcw;lt:o5w'u^߼mɦ q[wZ$taqk)+?pxI٫/T B/5:w5RZۏ6']f"FzŴ'd(۷3h@n҆jWWsEJwW('0Q23Cΰ`[td=˒VGgx9/rfZ8rC%Ϗ ;!(vntc0!vfŸ[yF64i0i*3# .FIcư ufnwrhp+~'jog bɣjuQY"E fYPF箅^Fv{_8tbUF" v4 >2̸G/PGb l{KYՄՉe[}lqN3@8]Q+ׁ,@Qqx~w Vn8(x)=j9.֣P+e(_68f^"F8bcwdFEHnr҆_F}Lo8\WolS%ψk~b8 O}9lxfT' mvM{ R aP =U>ʤ?VnxQ=<3R)mwOHH+vU/=LX#`Ll%yC)Cg6Tp+C& @cG8 pΤѽY:kh4(o9}/e3xQ8!#"\z{;T쑉́AbX5jsO Fq$.E9ܖ`-馃S4;gni ] ]}LoΏm(˶enfF<8KHmFFyOsc7oHNLۖ mⲭ{_xnl0G7v38=a`~oYw2y;O\J|<ϸWnH&p0gu*|>4 uq/(z&[L:"7` \1~Dʍ^Գ,G,In'IWXp>{y8[&moR%Hsdڨ̸衳)r gLqK%7Ӷ7Uw##ͪ|cDЩ F},a' r%WPeElb_ֆ\I<@{;;/*#e;9BShy,Yl mNEzhfЋ/z}K_z43{CZhs RD8KHKlZ嵾^T0aPP# \5ytX/H0V(QTJ%Վ+7pEpभs; v7Y8 s v aZ5^)t;2 Z"f0'Oqe.7|0()XQ# ] gYqn˭Ӛ;JN 뷕Ry"MF4È!ķѹߛ:k]85qݎ-EG`ZObjSu^kg mNsHϘ}F#[޳CԱql¡uigRS9Xַ4?t~G0#8-1Z4ws".Α8#<C-ih_8ئUbk4v_UYgҢ!7w3yzfq$<44+3ϙ=sKaݖo7xϧg&J'5Ys.벫fO;=Z7 MsRfc[%b@ o_[{O2{NM( 8=eBsܹ7ǃ+_- kzSaЋ[N??!bm%;1IsRw QXXR?QƘ@0sS]F> RDThVBk. NH#+9.WdgKƒ.߆W"YTSA>2xŠ_|ɰ FNktjaƷzˊEuCΏ6,,40zS<|4Jﻹ4 a6|-g:w.e}E |?{EfUej,ak{2DTу vpMHQ+XeMH]yT!2Yhb9{ ق]]_jG$nx"%YҰcsۮ;`p{lԂfkM|:Nwg~4|r=,Պ3buA>u-,kk^jǖ[[Һzr37ߺae[Duç= v,zƅ]J6"/{] dzӮy{,ٲ˷ƼfW+JÃM@/S$J.-n*UČʨeHӽ񡗓(L \ݖ村[P?4Ey>(xtASE]1bjEyqjiў^\`PK8w_۲)i9uFolP{_<&\ ɏzUaXrQп;_w\$Yy윟JAEB+8- )ݕj4RrJB [p0 sFR1x(Ї@eYe]PF\ʩږM^8Nq_d01+ew CNq#&ql!oV.^FPNn7+#W?3f V)_pcpw[O;d֒_~_ċE|6| g 6{cR * DJVQhEvViH lBQׇ޲?f謶ҨrR4A(3^~m`]J\J0=(j2"$],K4ӼWSҭzO]cN\ ?Zn;zls)ΡR 0.&q`υ]5b<9}Cf&C랺&1[3{'or/i=0 C^/1PKꖛD[mؾ>Nf, n)W)=;Gm^f Ds!Я;6}\٫ @,B1~_ vPbhB] Q/FoR j'Z=#E(1BPv@vR@\[%Xg@McK%BUWg(iQ*-6Yh 9X `A$[[U)8ZcTTd Nf󑠄tɪ<[~{~B{v˕Kezs'R]w;Cdb;9}pz@śO=wza>lZ)lX'R$-{oĔyI60CgiѱY[*[X]vV%hn]svMȵljecLni69 gx<(8{#{ģN8M -.y#nvj1˙I=msn%G>WeŎ7Zg1 `) i[nQ 6oՙ'+>-st]7C_:7_cTNL pw+n9 JGGA,V ~=Ձ1<&~>b*^a;HnH)jf/%{DgPP"fD 9rUSQZA$7,Gxw-)DCJ2 ;MLI$D b^.*}v/eZ,sд{*(,Em0>ݺC]$x(]}:ȌfN*P\lx!=l.$jk49cs#h)>i٘;|Y֡7D-W"an9)͡SK0C?h^=oH]BI}:v%9ƿuN2{~ yծ;=Z] [bg;C4cz'MD\ Ś37Mn SB=ӕ2=Xdg=4(her leϟꗞ6jbS%Jt\8{zљޗZ7+Ë]]kQJXj4m[x EUw.YBףP(%Lfxc8(psûa|LyV1A 3&f O7OWߋs 0 `h!8- ( "ːю pIvLtF؀eA&zG<ћ@oT nT| _[TXODH(M7RI4"^"r{p`{ ^+x\~ƈ3-v@`qw99sTo98YF3+։~mXtN:'f =nd~8Xɪ N*.W ~eaX7F3ky1MLoy#˵މU.> [OڲǢMDu .Mnڗ^ev^çwn!T DdwE-Izâo'r58N"( ~jDv^HiX`wF=g7o!?sW[ZP6|ڂSS[<]{lo1V쟣c?_8Cw^Rs2[8w7L!^(e/owUy$Ye}~b_(0Arx|-1V#Xv# KF,On˄ "k@!|&E.*[V!W EF2lL b#X\Tm"k?쨪Iw+ <ӕuh y1TQe\3V#Xhi-9 O@{Ig+īgDA#+i!+]MdRV"?oY=L%Z=1Y`ј({w 74U B'~ejP;4m4S]t>ł34'9@^ : ފLMK{0޽ՐYϼ=eŎ{R`vmx>so^`S+] Jc>_u Cg.9 9[>^֣uAP';b.@rv&;P 혦F&ns*O!iB2n *D$@ȃV RX0_@bDȃ z.$vBcW+v>{l`q_GmBgjp+u&b#O\I~3WV~ݓWRzK؅qGO@?X6|ʼ?#z|*8!3W?$eLbAԷfg/ϝ~[9/ Vv:A$*=p.R^n |l/"iÕf9j+ՂcĜuDP-.@ Dޡ)X̂Ew;{!x%;޸_mrUkYYm ~7\ NOꗚq6ʨ^v%%`eS*7 ȅNwo_<?JV0zl=BDǰ"Dj`}Ι`)ۡ{d5H0M7շc ⧰#<{v͛ft[*iy1MҼjŭ~GL L_ٮI]'L9cɌͦNƢ*|X-4i+iZ*#;dzk},5oڮӺ/2{rmXT@ҽ>3 . xŞoh-)hvw( W%{sΏR)K@("u "F߮oDuO]X&eNRT% {S*WjXF/&7R(_OQm> Kh(D @Z=d z)ÅޖSZװHVZ)_"eAAhAñ&mbc%G\Jr)*Kɪw*fhN*&/#(Mjۥ.OWL$R틩uYYfl'z''gySS7 9yΞxe|ASoع7*-Ԋ^mt->.+4`pٶ~_ʥ2r(eb]j ߿WFq>QU\˭+ϴt4(<1K+P5To']0}K:9mDݗl!W+;S+UdwD[q(^yLlQEƒL \*ꮢ8y@'k)c>]9ŻNmЙ27=1sWfng'.Էcox>+Cf/9>kt#kݸ>zGN>md柕{UO).LxKƘ!ELYzF4c>^xh_EF,<`\IQZI<"a5x1مywJAGԒ_w2DxYuaBى\ 5 >%]NŞ40OBՊs\I>;qprT BT'[nQ\JնTAAzͭʫI&02{kwicAZoޡyAyĔy) M`,e:Τ 0.F#әf؟Xpq\\J ^Nݓגt~gك?a;6zwMݯco~x#v.=wEYt7Z8A~2-za|Lf&]VlW|(i#3&1/n_ `\jB!cǑÁ 8s/^Ĕrx+7uXv^s6ĪTSXi! x 5Kz_OXlN iV#V{dv:r=GH^;3+ʪc(`8l7BntB}TYu8h+G.*פ;PtOw=z.|WuOWzeS:v찙 hg g@2fvL15Z +HTM{U1E ?ec7􈞳NlyFA.珚҄p\ǟFs&N&5wz[|PdC9 [g$˷GI( ob&@׍*Ubl כMsG;G.]G?xMR5Z޳VϿx`l]7c;%e `U*}F7q!DⲘ($˸bl`#mX7c/%pwqt{U~b;u}JAy1M~^r )XoYy0=ehAN?Bv,ELS׺!3[D|bH hЇvϝ6ecJҒ`BMzזVW/p팧=y+ z!׆E:gYԅi*նVJ 91cDw&5E?ZB1ڮ?,+o iuSl}<'Y6 .5w]${c;|3M,_̤ZS}f@^};FL12bg/2!gxˊ/3Gx4tBo~9鄶5G.[lg[8c?Zځ,iQȌIb߷p񨤤MpxX-"]l`5a`B,VMyʦwR.z2Pi3H&E*4@{ D-ob,Vbw,1`UzkMhƷӅ;Uܧ > ^@VIi0Q{콧$jqt#GaBv/fG=VF ;2)տm8!H ,.eəZavOު$<3f,"+fB)4LSD3O9?{Cf/6I{C]t J{-,:R bNO V7u#Axnf]Vn1{ڗY7s n'UT#șAVAh|fk 0fܦunuŎ]m0ylBTi|ct4i ʥMBJCegMҰ,.+FM0y^O~~2əf瓞yM"o=5cQݐ1_L'>j1V.)rn|=N*Zq~k~Ά^Jz-#3&]Eʽt3Ws1//rVp)NR\ /OMLL%Ƅbqh].:'uEezYzb0q{>%}:bb;k@FtF\X'/$ Y@*FܫC QVtX P DULVV:-(pQ8ծNV'|0\~]Jgq}I2y 08XNд5SG0yAdgekAiإ/69y+ 'ʐ)(wiZ\j>,U}CaڻLZ9~ИOW͵Lb洨ҫ]~Vhw,i80ny%N g!7k_M emֽuv{gnrufsxR-w~)YvxZҰ@OE(82ĻZTCB9}&ryAdorkfOҽ׼ Ţ g~#O|N9wkj q~1+QƪR][[r-vivAR IѪל־|eTv{kpV' lo~] J] .;#O5PK:rӻjPz#yqjhڵ"_7z> <.IJQ Dmڍy"He{I/8/QBl/Nw=T8hm [[1P<~yrbLx@Pswr$fTru} u=8OCtlNX_SyO-8%CLf|,w0^!f+?>f&w;7x= ٕrߴozK\%sPJT6[VUtC],5427T)^">=y t3F }waQZ7u2kr4PQMĻuވ&03t(FRB&}y7%`xs'fm[6NVn@8"1s:@(4S6X}~N*) zOqZG?uSvy#ۭݗn1{=ldC{;v%Z "#l¤#313hn߭A3q@?sWق %ⲁ_ K o ~h6D᡽[\xbe&F3w=KG)f^L`+ ùi_naEezVz瓈AR1RȑZҹ5K OClArgJ蚄HqoQic~.ÒcO)VC JQNT?uh<5)ى",_ByFcAym:w]wiЂD b'RqvXD " fxËE6գJ i9a9EvV`%f\klA%z;}37nJsB-G/x;x톯V{襤n?&+.6O' h9`K"' !?ՏV,..Rj<'=Ci$"XjP$<׆Z$< zwh .V&99sd؅.Eef6d֢ul5iD2t"J!Mmt]=Zꥱ}<ԡhkVWe`R>iW" dv?xh iI% ]ۯ' pW83)V]hb8kՔ߮9WGDGiuWemٮqEvX]Ԝ+2O^B_z&U{=.I Ƅq |l%A^ {l dxo]D5kkeG"ݸp ^ =IwB@Sa@*A 媒thA"H(*o`A(Qw'jV#*;=vA{- מAEO wNfXNv;Vچru% ;A b7s[y2 dx$"道hNL-)SLa0A#LD~/IMQ|orDt_ONɵaQG[i //12j֠ 9652nUI f|7ƽ7ˊM(LƔ BX KĀdP44{cbg-Q=0CD0u({s2)Cf-> $jֈCy'6jgE臯~o;s☡OO_r+s#!BDvVERQ0_z72~{KujvCS]O{Kt9Q 6IMJz#&c!=$Ӊ?^Oyy"uasdSu2ʳxq(Fs\LR2/^ee@b~\L:q; A4ۮ\gx8BCJj^-3]@ )dJ 2V:4^ _JoP)}\PI0OEM#%Ց-dIyiI6Sz?;O/ n g x_ջT%.:4W<DYlN„*eȁ>?o(a=qAaLGrK7m~:4* O92:,X:>5,U6IgB~`b$!eCJ@^n@Jtcv²H"#j{w Xo!tB)(TJ(ȗWwk{\Sl+ IDATv;`An cs^9, پintr)8nW۽1 ٳR'QPXէ0{vr9(,x{;N$eTRE:f `\gŢciOz -?J=¸A~ۮ?fדcޭIO|kAIA{]"jP'b1쟜8n˶6-$|Gv_B:s/'rrֹ7 2ֈi*]Saʗ&q9lUNYd])$8<ܥ< (p.7L(ηܿƢ1a*rۣMV|+Oˢ3߰@*==l՗2cs:ymvOE eHCu(ow :!+hFBmGBkKtF:}dP vSzve#ˌ΀N {km?;-'/#j]n\T@5ZK|T 칼f=eB y憲T'3Z6qHx//5&1{fdҰ.+ó!PH`Z M0:*Չly'1Nf0վ"04/JgVAYhՑXsv2zx\MytB^YU d<~_iЧ~|Q7xgUE4ѡYU;dzvk_Is_㕙GbXѡscL'@?OKT8śQ7>?G] 1[{.\tuN_/mٶSOMccg/93)9ݖmmo eKc\TrpȕZo?3uewT3SwSbw\)Wa.(!1_Uk#r<`KXRV/0HTr xRCDa~iEy8 ?}wR&E}cp.4AN蚭RawZZ\X933[wYhTnyo:lcQ.֎4ޥE^zZ]Q98kYBgllIk>$1bF[^qi8خR}6 /YEa11='bK/1Js@!Vtd_qXZAII{c"FTRi!Qa{0[H_RZLoQHcr//hrOHrIg ',N) ս-4qKa7E b+ [R%ήbrPdF9 L(APh:U'-rֹ8/'v^YvB[VNV7{i$bWkxȮTR_mZ&k5#k|FCmI V[4UYQ_ɤx|5q'+'o'͘OW2,g<1sԐYeؾ49V[;>kt5' x^aC{P4rʼa'oty/s@ihP̧}3ov^cv͝)֏2~ؖ|6K,/d>ߤxQ#9soVMA0qpY6_o*wrl,oI}jh=|NTKj)&Xot\&1!o8N_ұ\~K&6rsOP^F:d9vRTi`/qV#ؽb"@ǂӴR:׋D%ÖAala,6{l[ѱnV4Qݨ1zl8t/ؔezϬ)%bBfuWek  n9$؜^(EAUkTd{6Ӕ]%g&_36w,@@5x#EDIĜTɤ9-" a8l*oPh ^ 1ČJX?5+(1n373r+bkyȬcâϹu #UTawEQcMODӌ+b((R2}1H"bkK꠵;mzB[uvHY?yu$>7Ӎ"t sc#,egcfŬQ_H)n{8yLf ĸ6tO ^lV &-]Ub/+-2ס djxL^ss#AC_4 /\("D `_\^5jB@_bey 'P.PDƍ1B~?dzC"2[ش,_qWG3_*=a~S[=?1]6sao+Z)c& ]w׍0 C r6lR<iwq28i~ԤTnvm:;Oiu!(sΎyS47Rp "]YuW#Jj]}m:y8O'1l5䒞xMQͤ9D` CܩgrFȑ y6D "!cIMSIiNJ煶IB)"=y`9pYZie$FR]s< @AiCl~!S⢉93?$AѷcڐCVoȥgs$Qv'Dv;j3dAM\=1|SDK~UYV0٫ F!C8 9|DF.qxJj=GNYE]6@)˫'MNK3Ftw* vϘ^޽vń7_,׿;<[W]RգdMQCNq}.%EQ!!AU^]Ic6 +ðia7+ J,6l oQBQiY uO81;ձ իyA!}3>8[b@dth݉3VZ1ΎPI4k)Ukbbu_sU0Mav*wő:uI߿s̔Wbr ˌj=Y6eA>m-zJ'[#/!dҹ;elss:*i J 5=d@1*w'w{*o`kVT#Xq>H C2l9x\0FB@5FbQ+ϼ:~Z~q̢Q|#|`Q.r Nvߝ{c\nֽ7%. JE4]SFMQyڐnb &"~;934Eߠ*,, JRG9e*|ܯL6M1l|v Ӻ;p`7 {胗o.ai}m_aOZ݁;xNpB$SZ&&3T?Duæ*iy!'$B|N@XR21f+YÕ[b*+'9#6%2G(*C 7HoPDlo.,̀'GX6t%2 XIǝ>tR^Q%iXq UO*-fx8 ƻ8UMk="Ѝbt'/}< jNZvaC 7\%MHi+;wUҤk?DDLˆQжa1e,m%C~iZ/D*r.ly&)6'.{&g,gt$pu[#{]M2@"2 DyY= f °Qmluj{}~RYWzmP6&mR:ѓ#+:9?pFb0եea [^zYNمݷh)c2i˰od_.r,u}dϐ~J¶Y;vN!-EȮ!/mttؗ J wRdcF5alg MO^!-s㏼7cmZ!ŵa= <kujQJ'n;j؈Qy[D=,mvKT>'iRBTɷɌ Mr[GS.֮3cE7QjXЦndh,!N(.g/'CV̛H{ % >8C}ܯab gI-E~DF-vnm/f20ܭ$= p6a#3[~@SBv>{Gh1,MkO_˫m#6!5#yQZ(>0%"Z57o+7(9.$LfjJ w0އl^/SG7zx)-80ws9_>VV>-i$5ITywݜ^K* B$Jj(71C&GMncor_e)X`Fqkp?bO~# a0W W"A$.$832e!$ggw 6yȄwҹ;&,2qc+8;e_J\Yڷˮx+[敜ZUQoZ,# łvJLkh>ߐի֘\f bË^P9Sϴ! ]~'̈ڸSd9AD,Ne"_l5Y,0cE"fC/4!K' ,Vİ PCx^INyBIGwpZbqK6u4y4ȗԻRf)q7Tv=-CeRݤ1v ZCՑSo7Tg:eX LVD`@ 5W`i VW#DA^"1zWpNRP8صCk?5l" $fl'*U[&A8i #ΙL7F~ϣuUeYUˡ^XPJY*uXC=r+_XHutiT;*' 'ҳ9ey9 仸 |A9.&B8P탠\;m2V5F2?؞RQOb9TL$۵ 3G SP8#iJ:y$NS`>H(h:V},`|<Bd^QdY DЏJ@^98qݰh,-pRk8xCq H(L!IR$)HSv-!I@$ )Sfhb-Vk튡m%B٢N1y̚hw]K>cؠǞtGuljF^Z RfrP_dSf%S)׽}\58uT?+(}ЯOZ)t|PoLn!&{uȑX&}~5+G|_5>erTJ]y1};ƞ|m OK3G zE-?tj@!a~xs쇿 Z+د?;g-2C݃]+,ӞfyV߽ᅒ_U? 8h?w}Z' #hhۊ#13/12hߐ'$pG;IïQBi%y!ڌ P$&\T (!_Tƥfdlf b8q5&œ' d[6KDzGI[nJ!=O2˼ݰc5?^ԱG1IШOs^6swJfy" zL.]`4!C,8qv2E,Xq~@ $a(os\Sg</$t" YybԾuـ~ 1IBb?@=0{oi :utϚaop N^NJ2+'.[E-»^4dU(ZrJ|w sf~m3t4'M\ф /B*y7K0QnPSs~8r[N/(i"ϼ6=<+N.s55=77US _4D)|n0q M"XЫ3/"M^`F/eh}q|qY'1Ԇ~%Ҹu_ X p,vv`@q8H(` )dGue6blRZC|CMEMIfΪ@ j P ]NJ*dp8a jv +JV6D]@_zcXMj2Y+)wU{SlA9R0NAdďۏ"j0 Zq;>||@HdJT)BcN|#m`0_q,̍3Ӥ"O)љ"X+ћQoWP6/M7& (/&s~zsLYKoL<3jK[zwzf](صI$j3+-rjs^7A :mW87f fk_Z};%& 9oCw^RU}MoX^b.ţ}8/yel!#Mx#OW˩+Nr*8@:)/Zku,b"6 4&a*!ʤS͇[(6)M4)E&2ۈMlԾ?$A|m2g_XlW6ھ_j`TXK d^C7nSk!d {G\3pY*g`8`$q IDATVP@ڝG؂`?hC{Oҏ7JK`CP ֌1V{xOYaNB߳2io=FG?z+QU;^xQ we LZ6+Bˎ깓&, 8ue.Q{QB"]Z\cny89T<أRL%; xY JhC>y5*.,BLT N\se*hxyZ-'4a+ALpMCþDwyZz&'ߘYҸ/|^qp䝗[k!_5zW:!oyn[tǮEc.>qWWcR:{=L yf("LvchX;T/%\5bN͢ K.;h cr>MaB @0 `وM):dRܹ Mei"x6DI_8nQ9@DLj;Yy0qsWn;WZ% !+䔗ݔmI~3"4lPо8KXْ0$gMl% X+yK}~I-7tVBAMË,'@4#6:jlwR0!lkir{h) FΈ'_:UiLOtޚ| %hoƯrZ`xRDl㦷ϲ׋9tssk3JUx6xZW,0)M8h"Q.Bs|E5bX`?)=1Iޣ?I l *9@@ZV7$xIўݜㄎ{O^]b0aЌ;})*Gޙ=~Jn6AcOMZ]Ӈ$^'2U!ԊМb4:#iaH+KZF`jwL0W3qfچ&{ݼ~+k {F:mLZ]ׯ^8E@8eT%Pmsj8b 3kօ0(Y, Ngн1 ٩!@AS}胦֡y]vZ+)b 8swP״Zv[ȹu=bP3}gK"WZlG*^Mo-W=LQ^ϮriY/wJW  T& rCp`0 w2:J`rէ z"0eT+_Ƹ^2Y}5]PBUxIvevu4,)IJv|MfߣUzɬ-6m" 4Iة-Tzcrlh+kO!mety9]pbUW^!ՙBcK)obaI@{X4=EͱKlA4+YQHs;$/Y mU}NvGbPf+ia>'!D'd_N냿r/Vdt%1-*ywK;;й{V/_DYR.Ĕr;/Lnq Ӵ"@Ȫm5w  &z]Ebx1g9 4it\T*У2[0. B eN>94TR _¶0!-OE%=lQ:3o$g}-ŵ{ [jUWL\7g*Knv>if.O6}gvg3"Vi^q)}RĠ/E Jbm@dnc'EU;e+8#m4GXjD\MZ$en޻3kěcI Nx}rL͖+fMabcsDha :']$!!VR\Mbxe*j>Cgo6ND\Lxh JL{OU!Oz;?J*:; niǿ>푚9W:u~DG/&,rTߴYC3)bPSf~=Z6pAZ4%SՆ1VqIߘ':oI kW-ՖK3FY.lxFk}I^N~sjD9};>Zu ^?Yl*'^p4T9\wE$d0 RJL]ڛdFӫ>U0$;٩L~#V>*{ʢeS8 Re:H|=5HJ)!r4(7'{s.c!ܻ =*[? Y듼n޳,Kr?g.HƄGkg^ o}tƘY0{̤%5 \Qn<Ǻ슿:i(±}yBMt>K>ذLy޵C:bqۋO`Rގ,.:)N_/:- /0. S= :A+M/X.%S! rbm4 0a*jdme%$N9Z R=F:;@&# KZ?y4^Ks&Tr99j>PBcf iҘT"mK(\]3Gԛ df+_g !^͓IRxn("P#vxdvvV{t:!| 忱 4rl.4f64~1xnCAS&š)<ѯ_VNB[VNص=kN]K=;= J+-nBԼ6 @L?vD#@o8.^!ꥑåR4CH"F2R 5QVEg4[XcA ~Hd9IKD˦+,\q9_hVİ5>AX7j@IOI%IJٲأ\ve0Ro6mL `Zn:}'-vJV.iJ$qc v0A/SVfwoWp>ΐntNX6Q'M`y+i/w/1;m]ayNJ8$ޮ^wyHSǸqATe6YK{wEo*NdsS$p]wN|kdQ} ʬQr)OQ"#_ 6Rs--}@|0g;笂f?}kVK~R;?uK3Fu;ٷa=|=\ Q< 9ɮ˞1'ޔWg04AVŢ[;pq$8!Vֳd{ӕZ)]),/y"yd*l8hݮ],J TW`"i -蠟-PKcnnnod}/dD <^YiTMU V8\Hu帼⊏ESb;N5d,@8۫fVo;I/Wik0$WS @ra!Ln IDAT&7Bگ &~qB8/Bv3"\!4K]F#5c,}=G(% İtt1>ɷGGL hf(</D"  \ XĪk=e2!myN3& ieqxQ+"W9lځbSN6>WӚf;e7Znγw @QE._}^7u=++9DUpK ʥ{ڀs#i2s}&F~᪒1ryB}2)| pK7mh"yϧLNZǬ("̂qbeO#o5\{1m [_Mb ɒdIױ&7I&N9ʤkV#1\;~kԣ22w,{I):m g6Ea:1Y`:~ [TfJ50N &A#6}7'y.+ j+n}{h^㗝\|>2[1Aln"xpހӉ'18%.crH f{}C3umӆvƌ~3^o^瓃N]?Ѽq++G}a=Ϣ1tҘfc(Gwݍk|_q`"7>+Q &NY /^̵: )>U?FV1,by[E,&{ <0LB~#L{T˾O0L%!;r#TǪRmƴz!Ҿ|j0R3 VDRvWXy R$d?py_(,͕a2sSi bZS1G!DAX& ̃A_Pl[r]@S[ 3BTҚ Yτmf+@bRJnWR(˪%Ty^7&+86 (pƆ0ssZNY!mbXDZ7gMxGES4)SO<*}ygQ_$g^ ckw6F/Ďow[>>nGޙ\}w)7֞V=Zl* Y dEoPLؘhϔ{> :._}5YADV{;K)J8{9dpӔ]4*yBVڣ}+iva:w * th%ipר0 J MApB bm}4` Ֆ7v -b&ccPul9_'BRr*@Hpq4Nm1Om:h4YMS Fs%M.$hfBة^DyT N"u1'~nN&ֽ}l@Ll*yWU׵4F*3ssZ_u'?:`3yaiEVmPT:On{\ȑ .zʢV^JܝS6q=SGӥHVJq @(1]CU:[-s+S4Y*Zp#AW&l\-``Bi@vf079N!7_QI:+p\pXZ+"w-r x"˙ݪWeCpP)wƞ`2rZ`r i^baC{;+#Z wQ!fxmSRpUsGy:!))CDDc.\L}~vdlm$#Z ~nJbxfvj׽9](*~x rImȪmt"U{~o`ǿj y u_Rs( K\9_%ܺcekW칑7`YwΙ~nj[c]SPvO 8C*͏w4˅7;"6"9 }ᥱr?]a[0E{;wnWÊF+FVU4[RvjDn<# fwo\}سfLR~cbYv߆!BCZrJZYV*ٽnEI.hZo.IL#P+uTүeX^Ŋ9mnv;{ U@@k~EDTһ@'BzO6wϞ2cC@@@|B gy>oL@QBa'3镜,=^>%D!OlDյu61x4H X|9_,ns@ 7TZn+=|%49(kV_3m1#{x@dhRi.@Rjy/Թxɲı f5s863i?ŘSji]Wl~uTY mGBœQAr~۳aқILsKꅫlͩpwJ8Ձ'U[^>yQƢu4(6&"6 46&!-'y`+:?I۫TRN<[t=Faʽ@  fbi&-,4ٱ}jwe@dzc\)} KD-vs)= Iҿ7_ܵ.ͺόB#uwL\ı[>]0j іX2w;F~2ۨ4Y6ߕ/451qN>]IOgU I Eg^A $_28!$2 L\n^BxzvA&ӋS!%3oU2#a2 lvZr:9l :]@!Q5۝SG?wۜ.ALzozs,% EY3b^S'X)3VU2u_/e[ RA`qO0yV|bӴ̲|ʝW&E>D@B{;Gr{ m}g?krZUQ+4f۽4u*}ػ?nhޘWiyc{,H Y/(~=2AܬW%3ıj&#W4JQP uщvg͇J v}`6׷[>{~;KeWF$^"~hZYcU/ ¢֛=*d9<9~oNbgttYְ@cNQzv5|'fUaimlYcc>4H(SDI1uwԥ[Tbʨ׏ ._E+Ƃxe_F,ToX+IC4aëŻ{wV8kpRK\f/YM'l٦вW}2H5% JQZBEJEedl=^/rHhO[o n>D;icM3FSgS| E66\H(I_Xٓٸ(7O5ea_iYY j߫S}kxrZSN]#^[j7 A $ӔsR*ZGTP+yңa]ċj톃 _OP5jv N͂asGklSۛ?>sx_W-@'pMƾT $QIW0EkJ/n!P~/.^cV[nMk+ 1^e]t0(cT{>ZpIJ6RZylv 7*}UR<:A$ĬBVxqD?q+xw67\^ɣЄ /`)Vd߆B2P"(-cCJ*HUtKΣ֔;4N7[jVp*Ov%%Jquj *!$+cL Ȱ Ґ5ƙpj?+A"Hb**ظ#{Р="J6Q]{nI;T yn,%1.R؜!HHVێ~~|צp[1.yWXFXAjAپWjTN9=ù;YbȬ}fk=ДY !=:ol6򽠫92SfAoקiw;K'"kb/K(e:UB*G8 !B "ڙcJ^EC𻌉t{ΧLDH3]G<ɩAdɘ ρDà. 4bJݏk!Tv}$Eٰ\9! ΨJQ)5@J)8%:&\ vW HD_t#7M 4k9ZƝH[$A`^E3.YCM/q BP';wч?"2`xΣٷG!')gߩfmdP>Joް]193&Yq("7TI#R븅y$:EmcsG~6bk"{IB"]K;^NWT({3W ߼4_[LȈkJ˨#z?q Zy Yk((k#^TrtjЀ|Cb ħ.?o3ݾm+2<^]C~Ie[4ٛh ,M_8EJ+mcK63z+0?hJTgd v{|fQUE cKr{% j5⩋$z+cQzىW lo{ ,@e_8&_lTxutA K!Fzw"8~`0G+zޛ]~_: ‡{そ0Zf,:0 2E@kdKHF\hb^wYM*e\/~e"Bޝ5**BȱCI &,xXM뫷Y*2lX7էK.Ϲ~ K^&$2C)kauR!K9p"ĸdTTu1@I,( z}fBNIcV!RؙttM;#Wmil괬|mB@k>Бo,:u^ *};KlEfLW[#\8 Ɯ-)IjplN춍$f78zrʠ ۲A;[e2{m'p`ػ?ȫR)J{Ȍ)N(y0kX iHH@F>y慒m jee[m^?^U.v &@bY"jԄ ;6' 4:%bzwBf,YlcgJ> )AoUm*|$9&L8Bvl%@B(5m4(v/ɼ(L2+|zN;K+G W~Џ &Ȥ'NeZšc4naAMᅵ & EMU Bci zo~ J!\tﻫK;BAws:oWh Ϙ(;4_-.\fȍ_-lA)ֺc^:_uo޹E_4ug BdTS1H boץ+h:"Ϥ派S@d~u]}n}vxQ#n?-'q̄_{@$Mm.>M[C~IYte@-P:Vsm# dL<[`)Z:,b~("ba; t_n`OUV+SҐۃ 8Z7pE0rER(!q_9 BSo!#7̧!]\܃rQMYBӉ[2^TQ)ӴԖd K r&̶畱du~=o>ۑ 4ף:*E۶ET Sq=H4Da;0~_*݃tQ.5;1%834ynݷ VZVmEc\֐띪4Vnor=4 u_c<^eM@Fޙq}t@XS'ᙑ]%Ɯ3o6MG1ѱ_X,0MYCvcV1Lم=B^(6f/O]޹ʼn9|`R+>~Σ9EJ` dk)Ywq ?$tbʻDd"CȇH*bDK*' |VrٚCoV~;ߗ~3ZT)c*5XfUIXBO:Ϸo)KXi9c]oAnޓr=)7_."l)o5㄁bjSu;AK! Ao.}4j\zx6._))0uaUom cus-[u+ d N-ƿU@FP7:hW'_i k1{uWsGis|cwjCӁ&GIZM?ޙ5( a=#a+vgcNQE[)xa<:M~z;=_~)ˬ2M@:UP),w[%~螋CBntnqy@+veQQZI=8sj5IwQfđJc,J28Z6DOx]^˒A o؃GʬE$Ji՚mSc -{3(ɪLVyq{j&T.-OOGN~Óx8cXլ`7O熿DsX`|䙔}C/NL84OkBR2~] /ݺ}”{,T,* {랿?wh`^YN\85eP}BTC5δL`|F٬m#;:*GYMK#D!u>a&#O|pjTYTo^!p[s' =oLo7mZ˫Vn-BYS̃>ZC/gtS. z(<7.ں2C OlKkrZn# Ƨwi9!?7k<ūV I?]i-vkpl \~:SvQIWwkiPP{tLcs 2⚩AقMwm)F5١ƱxpZyTYWN 6^=] ۏ<ǃc&BW K܉V ɲI8V횊.Ovl)U=Y?Ch=eu3s9En +7\.V]H`BQ**s?\0I۫7慇3B*EKJXשu]Z+^eD+2smQ!eA=ms AH8y"3h0P ;N٪H2:kı&v_J=&*$U$/o̱' O/0/|!:s4ɯyw&^: p: w0tޤ!vÃ'] >AΎ[GNnr.OHjfvF)V4khJF-ij3)bBTHz*Fw2~Gqn,^zQTe[?nWefH8e52uGqi_ҖX> r9\jԿwJ5o.JE˃:sKtu.߈=Rfw+(=dҖYMمaܥ<9tRcj?{p"3`@bK{ )dZ56o'.K}p$m%CԝZ ɜͮV~k 0׳ݙ)cġdQ(~]ݼ™,btNNqF#ۜb4m}Vf ݬv&8_y! wr%6;eWn&aѓ hCB`Y9zAE:,@.Rf26 `"4[p&/hпlT/ȇ6*+E$4q,Ae4|yRYdؠTaN]ֺo{wkp#V'lޥU j]+ іZ &z$uBug۠9/zӗ BBf/M.tKョ-F]nF_XSƃ>CR3- v]sWONzq@?Mm?;<24\Zsws5eք٣{v۳“f,*Rk\k>6ٺe)4ھ=Ն?S7Q9e~KStEͨe#c/gl2r ؙ"Pˍ K OlPr zh{u^&:1 2q .wǡ BQ<߱qlNF_sd<УH"*yЀ7Kczqբc퀘 27 ONK1,mEt<:S}>\y2/O.W͍$)x SȫU5<`|ffgS>Q4&_\ԩ`SYTX6E%]sڐ {`o.F9.Rn(jNunTz'.R?o1'Λ O/27:ү%<ŎxGɔSzx6Bkev6{(B$DEVpeo^tǀGcgBnz$P5ݼ73ϕt fTnW4[ Sʰ@љy٠MlxO#xL9NTY\K $Z\ g|~W!=޻yBh!LZq*]W/#(]'N_.d1~(tYB@Ɗۨr+Pqfec"JPf(weezGV'vU+Y40{}C 鮦se> ewڛ6 {})| _^qJcꄤe K#Bc^[TM0HN;;oQKֹ.?;ೕ˔Exy9:MV8ݾ (*s\%(=?Kv[6z'.:.m7L 7e`Ej:v9?_E:u=|a̹<-hK-M&EM==vF}I1(qT{‚=;!GyR}\]M<z IDAT]hRkK,^ebn@VT6h@ju#eF c@#m2P~:JtQ!)P!8 dhЀ EA ADA "ҽ"!+%?S~y `O"!tI *94U^()ץQD2_lvK a=yG!YQONU61)ߩOY~߭Eld|Jp"uYyti9!( ic{v%&=;U"SI "esA_yqEQBuOr~+2*8:J\p~N0&.«YLݠg9r; $es4*X8r<P|qȉdy!ܒWר#Ȳd:'N^D5Ǿ5h:4/ߪES6=ɕ8[};<:uq3chmfaI}M5zMI]x}ħLP Go]4_WbSVϴIyxp5eVqmѯ}k^ɲ*pNO߯5} 8+q;w5l"8tEk_goܧG/WM-@?"ɘ !XeQIdOt&u X0ɾ?sls~?r' @oWUE=YBD ) PRP)HӐJ2QMF_(2T#*hy2E HH$;=%_,p*ul8ehƠtZ!UJ{Xj6ˋq4&Lљ|/%cXv= &,0k5=ySnDk Ԁ%@DA--<^Q^cX 7({|D !UC :-7yv_,KDI,)ryKf"wM[\EQZY@@A)ʏjxr?6Ux/Jhw77#ỷ jl+&af{9ݣ_v}mWQڜa{,٘ע~d&qlYTh¬²PZHƘBVUrzB?LSm9ǫQemtr {Gsd֏|$>=ܣSue_n11Y0w7F7n)\:6wL/Vޮ?v$$ =WTuϦ`N^ }}x;U|~y &R(@ӐG26RPP i*_3c|{.K!błڴxxyaA=#Ee^ ,h-WB="`Bka`JAiժ6MCJON]4 H<ĘihAhdg?E|po!П:o~ꂀ !$ 1!@`'_C !^Z9l`_UycӪ87LcrFqYRu+2|7s".'zji*k+aҫ_PY#\??ag+)ALr㢂-K5/xUV'DXK)ib[Mz/?fdyݠN+v_%,p绳MYČa]~_B W̸fq/~U WNd9o\9_Msdɐ}a-? =4@R%~<_aW(K6n7.>,lҙ? $*Uf/@0&?XT* vpw?^I!^_bڨ7ÆWlr .vI1':drNqj%~a eB!7eʄq<پ;^Bq ,RrFtUXR n7vT;=^"D):?o62HS7rMeb(!$%JZa1K?0Yy@ BH@T{ )(IU?.܀@"|V 14Srn#3<\e(fON.ci BDH&k %P$I212 b ܛ!'_7K6mt6:ڧ}I< ߬^f}Zd 2ekx#yTJ9s{ǒ~Jc5˜S8䃟6')G }bnǭޭ-PidL>TP):/d*ߗO~lޘ.l ](rltž_9ymQpje{)yT >]$|_q[Ħ8ΌWʻYO8盵5Čax˧ $=o.axo k}Bu< {tq@hOsec M3L,J&& q$\FU)Řn;̧f`VIOwLqyhY2h$-Q^V*\jJeMLSLUsnH|)iP!ҩUm ^ V}TTfr{"J@w)[4\Q7hP;]n:|"٩lNi!}mG61>xQPp/qu!B!XD luMSy1&nYbڠS6o}vu!Zv,g҇϶?Ao })DAyCiZf(LC;aų $@J-IH)Q$WDJ(Qz,1sZe[8i\L?7gGs,*輱_KǽoKߘ1}ĨSW.tω׈J^Fgyo浪춍Όpb,*zuO<=sV;M}Mt_ Gve-NƯfp`W S|d/ C> Qmٖ3\VR; FP/m`nHjg+7w~cώ/;ޟqŮQu]Uڜ㗭X|X\׿|^5ZP&Af{1&2EI&? d2reK0mNש Wو7f9Ϻ^OR(wC34)ꠢ2CYKVˁt+Krˆ}4a"_E#E*()Vٲ!VR&ۜCܼl vl^P䧻)>@JNۻ C ꎮfЂηwiW>h:x SKc A@BxjqFtK@zafrJ 3[vQK˥#I?/ ŮHf !D_U'k|cr3L ߗowAL?;c{{f˒%w0{'HKnz/&7@ !!1w۸J$ݝC(v>0)G/yllo)qp x5e;F؊dW]%j,wh,cz(C y11dݴDx.%<' ɖX/ذSn84h4" ]Cj%9ZԪH@*%ҩi0AO\nXRT@d1hkOՃOM -B"AI!igSӸ {`JpevCQ@aR rjo)GhѦA (*IJ0\Q)ҵ׵UbұsEٌD @#rG,ܮ-8;܎!E,2 _teH[nj Օ9R_ܹe7./׬zQo~ryڝ!A 9Q)j>w`k!Gjv 72͞t:g_9](?wS<6 ӺQo}BZq&zF2˴6 Rr{T(/bʍRl/}^Mf >_{F;/b׋0]a:Ԧr2%Ֆ;{6l|i=}\ڲ1+J5Y1 G>g35:Tr9{ŗJ 1;x"4?L(@;?kx("Jb/(]iĊż" mӉ\ѿ9Ke( 9cgLna`xsXBfGj% 'KA^ڝ!'qhhg|` $ b_/8xepUyEq J1@Bnn!jC%񕸃H$ (1FcyQ!XGb/D Se%z;yԸA?|M(ߤIKMܴ< @Ԧ՟n PwlVi xv)q7R,!kU*XYjePr{ Di£ryd\]mAVvT;'YQ6@c^{䞻V"kElX^0n?`HT*r?uwȑ]Q>nO'Ee{7:UGoJm8^W؜OdCJ!!v u?v Lz['&Sg,lq;gx5$$d!{##gPs$ d6>|Zڪ1a^ )7ӅUOynz9J+ITsBߨ!R/s(fz_Ì$12$Rl 5oT1c2ZQo}ͅDF{~j?Xǂ| \UX5:RT)z,Xc(r!QR m9j?[vou?oj|L߶zo&FQڬFnG0=ŒϏH8:N{ຓ,>lB2?ܦzߣc&̚u{%?/[zݻy:~L zf*kӌ /P ArL4RJeȄ(u8fQV6$:!_eFHڐgڰc+pҀ!PBɧCA1aO8ξlyk.oTٗ B1vkūǙ'H\Xd$5JүaYLKB7QQcpJl-"}-.ʝ!^T]GGx!!(e$SL㓕mЉ ]V{᝜2X@Ub1\0xi? 0mO{|+{"ebf2=ۦ|2V8AedU^3E]. _)ܾG;,&\8$0,~~ˬ&s6+Jrgg< [\X*' |EJi`ǂ52#2Y3Rw=>>&Ddp'*7z+S] =7Yr|\wh4R3}ܐ~a]PݏO㟐Unߨ?\*3)6r)8nO9q<}wOF\fc+F#qK/J_9qd$ CH^L%zoWR s"M\Rbſ$}EMEEEi&ǚ(QB`_@ DH?{NJI9)*@Wj#J.%OCVshX8-!*goòOP( };dj[T >5!G2h6jr'  ضwH3lú:t ]D`&xr)=~$$ЙUiT9I9N:#2^vz1:Rv=>Ncw32vȇ?\ޛ *k^>m7m*̶Ɖ"Gxsn1̈́an\ #Ǽ% 1oM/>Th?hgCogn>#&kUCUS͛ojq~QڷR>2FѼbӂN1ْ͘EŘڑiKڎըG@ vodWkۜ?McrrKڦE֔Mr=-nLp+|.,22F d-O&o#U#w?1ɾ!:wWWWr.t^ٮc?H!Eo}-j7ߚ<*C7eP̳۬ o‹xzRur{η/ੈaͷI8-f$RMi7 +=~Wr(`d~q6e˱wzr<0 >@&=zs9!wE ;`(ɘ^uDvyY'/iq>q(p/蹉b/{>8ߘmP =@V&{Oj. 7YT5( G& KcF\^Kf㤓ٌ$=wuam'!!)jccD2SG痎r&*;BN wxp I](nX= *+j k7?² o`Ӌx`X֙JTN︗?e{eXdUWx R4kʭ/T#%aA! T~ D0T/\J) g/YޚƙbD}v'veF҆Z ,AOبG @(()D(a"ad>n`?ptt8)<O^_;1(  }Kd`aRDׇX8y$']9F;o B)Swdo%U:"*r. HH(Hk->?yώCRBSَ-Hٰh՛`XRSB\GnMYϐynS(?[t|o{;E}-9sѕU._"k·y\0̊Q9^xh‹ETa{ފ%*Pb 5=E0JohWIR19ESoQ/i>-7)bZn55qou1kYkVsO@Ì>ay<a/ڵ{Up \JɎC`ϷpefV[hF$m:6WtZ.3gA0%ҋR@8 aVjtto{a(Ts P:CՍ-0LKWn$*KUFa=#Tf]ΖͭNOB~U2C&dSٮyjNe=X,Ui+'%k2S"֨ JakuR=^OJ̖&*K ۺwJaG DdOi}"j}Aޛt2Ӓ-@#(=Wn?xs jG~u\~U0+J󳦍j|4#ɦ˥gQdC#}1˶w]1} .q)x/_ftQ|y+L;bt_4 ~36 adp>Ǔ?=W3UǑBA]3zYEg8tɓrIGyw5 |ƄH&˥e:Vofy#nJ7DW^PTYɧD6$ߺ3 aI6g=ݭ@e,۝ޭ)o<юB缶 ˙ ܻp83 (ÄGE$"&.)ֵl RJb89/<`{O)񈯷FFN_'A iL)Tx;VKzwzpd&DHhڟuBwWw*+Fr\CBB!A>FZ !APf;#ˊo55)󌉞oB]/LF"$cOzDXLiv]sBB2뾴g:TeEv<}ϼgvG>AyZwu r)$Bh(*G3N;tf>tʛ,V}eϤ7wCQ٭Nulj8ELYc`=5aGf1_Ĝ[L5c9@Ubۖnl83;PWx<fUmW,K3H^I*6%Tx@R1a̲^uAe9;_/=b13AAr\ѻq/K1 ))@$S*%B$c S0V'I%Y,-/}lΓfH!^C@iVr,'0ԥ-tIq-2U!\9zhw+Yn\ۮAD#byIфe!,,קT7!EJQW2߃9v{3G{sKÚa;gND k۟w:,vrT3'n _g?:L{,Xǵ{U9=B.- 0mseUr ~kYH JxaJ ɡ0ըĂ?!w($9ǵr{̳$$ |/"p&İ ;GeJNC7bKDҨN'Seӳ9|l eZn ~ȪԩŶi\q,fCa~4؛]hlviU/cԛl;"JHMSnxl׺ CXqe@Rf;"#)Imu]T(HQ#YNڸ9:RDJKyVwvarK^s5tGnLJ5߮r jnxyڸ C(S &HNԓڕj*6\p°H(7o!2x 흮,O{]QATT\r8;@h5s!\fI2 c?rq J^ўk`S\ٳa/ X#:th8]Plj+y^Ri4zFOn/O+n{Rc׈ p< !G0P ^Ij37*_Qsy`(BoZ e6nxeѨ!^[9@c_zŏ UoxuZǥےNfTnp+]{ ! >3U+BJc/*EEr,z_5u9(-k:͆W;lzOnŎ=ǫU/L3oy} * =;w.69͚=]ai鷭a,xh{Z>smiIJzlCã ,EE)/Yt^V^:_%y@:e E䰤0XI3ȫXvz|{Y`tmltd3p <(:?xH21E*J,+&c -ZgSb0x*Wr#GtC{rfC7Y؄K u)ddLMFSI#Йy8ײ-=^}>o\^ cn"O U}^f0RaH2EEγyj$BJ<}gˁP 2?2\ |]QDMʻ=ۥp# ^'y'n S~)昊^߮ā A*iVS^-83Β <,ї-bl9k\r{1_*8w޷ JK4 ˔7M )x۳-O_/VLk>R93k/~n+p%sM<5O0J0hwk`^a/)&Z%e2(_:ZDFOX2Wϩ ?'ޙ"6/pp%ɀ̥ :Q"ZNceL@izSWsssbf=|b,n_* ,!k[XhqZ ePJ!FO\^,(_Ӌ_Jx+s!@dd=^U-P) <}gk cWlu]!ۓvKTCRWȳeڰ0~ܐXxg#}7]#^FQڬ#Od](=;}Cƿ)US, A;r ^=nr^,_2cf\^\nm=Ҩ̪4) LHMt,k$ plUQrۑŦhjQ ~>, "3S6 c#9L6>i^A3E-39Ψ+sN-z~AΔz\mԩ׽ؗr7-"3<1FOɔ_R +ɽBe ;dy{RJ~4SksFQJKTQgA̞~?x"9Ӓ-"kݔW0jM)P g.yl#їڴ6gQf~^a@g.(,c*.$[.l4 {ձVj7 NJ1KaRʳ(e>w3zmftVg`E|Q JDev)*U5fTcӣ;+l}XYSjT9 *^Ŧ¨g ]BBs8}} qWdEFNNVݑ@&IBvڭf"!D0x2BEBY#|䨾>[LWBs {ZB8\(Uj;xP]YfLwM:Mg0h^cz_ PG %c/2&(K):MԤ!W`/pݯʣc {~XSB2 jAtgr T`8-l͒%Y"laƠH>n)LdǷ_ ſ1Cݵu'ǹVnaQBr,xd=qףB~HP 7c2?LZ_m\_e{ '+P+*,9$IG&4g6ְAI)5>KA!Q.S!BBV6kέ ;޴_3GuTqy`T~՞$1o PQ捎^TT]h˝m-a-7NBޓyZm|iNZ+mϵ[3}m4U~~˚!Wy{7>xf=q.-uV{q/vd,YFOkkJs#{~z瓓d- g--R,ܴ1" xpTh.0l{Z?Oۡe@D?fXȄʴr[i8\oyk.cXJ1h6RAR@ZؙAd SPN$)[e3#2χ^yԻP9);݇_qlKP|4wRthn9ךgk몘Hʹ x'I6rWR}eX ,"Bax:.,Yo5ŷ)WsV5Z$A!F 8qw! *;6N֛%(o>6/yUl!O1LޫxJ0}t=~\G-$$ٕ?sΒܞmh1O [gT 5- #|AS#{o>fl]kJcg<끐A]΅DHiEJܑI'?_H7^\1b/KO.KO!$)tYc:-"C K~2դjH]pC` g L&@)mADI,.+}~'ſ=S:Q+K'r1ў{X98C{IZAEtd DR G9}9UaMxMFʶVq,3SJ,H?T}ٕ?hVɷuѢ B`LO?dU[ѶH$@sB9q/?ҚQ$5¸W.D{͚҈vVn.5Gϙ۱V4_cc_Sn} 1ΔW׭ɐVc^ tюTTҨR^~GU?2/5hT!Qxrb㬓w zc|0̈}{OM#B{ ^*\ CJEb돎yunݍ߯?exo6s, % ] 1 greF/pnpWY]|޻z>X7j{~_{&bL;H0) J=EA1+\ޝ{}w֠!BKO#V!@A IDAT"J(E'&,Y"&֨n?Hop)m @!BeB`jlҒ-j0h{Ovq@|~1yc2Ph noZ=~#BGNmxSOɧL*e+fMYXE]^\),|D*b;_b_ McQL"jeSƀ+\0tMѤDBa)TqWwT{ヌG_CVf؆O3E}MUdneQ(vyOF A=je!\a+5ce }[ѳF͗;/Tڲqs/vھ| +(po-~]P0d @CH)T{z7%5WΛ>tU nkC^@;nVn&Ah΃T)P^ȓBa S#񝪰Ua BGj%רf4*S#;=;n׺4"a{)TX8#s[(/f[m\(:sVf^t⠥;~25e.HC BY!~KBbf.f@FNpkt\{[ *WD:mU`P8E+)c=3.\ ôu5[Q<oH&}_}N" d6>|]gͮP;@ĽwsX֖ܹ ]OL{F5sb߯mt߉&OnbۡsZYbH]- 4%m{_dʈNǵ}-gCM0Vᱯιn"LOdG;o3BaCQYׅw>1 ƆE[$Qr~củLn@ ^l iE[l wkB&V7}!)%%F&ȼ J D>Q k0!4LP hbA)^}|JJ\}k"4Iƶ U2j%JR̩ V`^ȓ$,*b9B}oEy#R, NUN4*/p\lK!"@q|p)p8jKV.yjX\?x jB>9b+*@BB() RpaHSAE*4MGCH7py6BLhߪU; (_l:.@)DMRiɊvsf`MXS]~c]o¤w+||ooΙ̱;ΒzTɸK~lyKZ7PTJ0/~Qø{8c @OQ2gM8elvۦ7yM 5J*%XwkXy$ Ca1H5_\6Zo;KXN/!R cY$pTAg]#cELRC*%8T 5UZ k7@~ n;@A)9eIAlRp1 $&bNsJuAĽƉ9va.ZZ5 1?E*ot*[Oֳ}]aQ=aAXCQ `V?pӋSn+sM[7"Qᱯ|QMeN  ,|09]Ǹ`8 |WgM @}S1#sWRM;y4N22_Z[r@%#TX.$Yo~[2qMkR*2!J$S lDU]Z.O;OΚ=e^cŒlsZ.r2xXd+$HeLXGpp &eĠ`vb I8̰Q˻_izwhw$k>[O°7E)2B.0TRWÜM(55(DBٗ@@*+qN6PGS@)/YM!Vn Nѷuкslv][ dL]D{mԑ_<6n@5v2JT)ܒĄev;, :tPuk:7{6KJaX3ͯ.^+ŝ7#Q˫:,URnH-rO˯گyrTGJ\^ؾy1ep1 {%(#B)xR%1l[J)%(YK_X>5;F=d4O7#A22 "@P !sŮVN\KF_/>3\lt z&YA("u#@V2CEO C?|ƳuWU@xUؚdIxA.2 d` 咪Ë Nܲ b\U[:bA%+JWx/i冒+_o{vrɜ_V[wB\?Li:0Ko뭎]a5P9=}^p&ϒF!dd]Yv8swOnH=6a` YX!݇|~46 kT^695=ɚ6\ԑBrd`uN&'wY]gBSժK7elJ7bɸ̩c RIܺLJ1]^Sw/LKxi!-sGC be D,CdĠ @1x@Ep^qsl_,ySl2KBZ"9L [r;DEYk"k*98]`(G8V\BLΡN<]Y(V:У?_?F'2Ut`M;`DA2Wŝ1]< p86(1(6٪ )-o|tc a- '.S[2Lz ^@  կm-v]aZP"HH/!N&[9?nf OHyϔ=_.%<ȁn17:+*;9K*M[z0>*١7˫NT/Ib^g}I] b s85hT҉?kPޝr-g:{ <0(;(c2BYM P}8e-mlΆzy7G !So Erb&T3-w -,W`qAUg&iYdj2PPym?s;j`8Pj\ NLP]SyX_-<0o3=n{Z\\=' a,YF,IQ!f#š3xR)c|DfHïؗ7$3>f0@hQa"7;-h:r_Qе.8'[X u;SvuuS1׮_m Yd6,{.K68cM7$<*%]%腾_,dᘧcKkM(t/X;l.+NWK8)CL% ce /-QJᒪ>y CYХ 6.e6sd:! Fٸ?`<] Z2_C [MsfIlym GWw[,Drc_k֎.ί.)2)Y-*Lx盭T BA拶UEic@ڇuTz cZ E=:B`Iʫws-sjg՗ *1Y`f.[iOƯvbZ{|*wFTzoi>%gWhX9ru=^k Hih&A0#6Pwl]\,a-56wmkȎ1..) }{ƐNW.=}^A~9&_4^h3o0bkw($ū^vլe][o0&V92JFJIg) Dm~c2`G3y=w;*{!U]a0 d0ƌPec a>;11}<|T2{؝+9X lF&*WUyIVXo%ar5 U[yZ?p{蛥]|;n`>u-7oe^ /wVm63>|T{4?-buZTXV3u`+37 d-Z4`.h3bIP4 G?Ӯ0r8OT& )=ٻCޮ#slt>5F6飙iZеkkq}ר&g_O\ Q~1xoGߘz_ي^h )P!on*wo|EA(omV|Eh[b `2S(:*;:|se^@`BRo3 _ֵuX nKKP)ueFր;񲪗>34#'(Slo  &+h(]'}d:D >ZrsVZ |q'r>htIrd䪎>1QsT tqoSy~:|2qm?لkih8Fmqe 0.580Qll*a[‡lҷ)zMGI$v 9J0x9|ABe*!j-f,Y.EV{wv]#0u\^$¸eA>a)J$D6~TϬ#B>;7( Bb~Wc|=@ =aH": !VU\Z)ȆzD#!m>R]8kq#3=eԊb+n!3լc 7o P/ A Ys;l;ޜxS<*?q{Xrj IDATt4iqC s|74`QT/qM}hF&h;.e,H)6WG {kz8۵/"ٮ}7|0 5G 'F,CIn/VUc8Ln1o|~3ý[3{5G?{Vԯ __gsroq):&Pa Hk"EJ"⾣ە(5Mreoj[6ZdyB4;ēTU}/!R*D?nom:oJh2B7 Y1Fr.r#8 wElVԃz!89_˫^Ȩ +`𧪝y9zhUZL 7jQR²­ެNT@C o)V&Zg8W>]0#ǮݡCPSp< D0pZm_s43mO+k`v;nyy{l30Qnf.vTx# !ÈpZjeӏ p@,5vw) +R*Yw1>*A*1w静GKdYsߔմF=a= e?[㜤*ie/Ȉco ĝo{_״/~ǂe7T[pe5N՗ P}/b1oZk9s$rY#Q]>V 0YiTLW*ktA0Of3ʄ3Ay5Q)UU q+2H{nHl?@Bыbkd2f F`~6'ÊRս`SwWri?_L>;v5r'8}G%90u^7q=Rp[ՅH8vJO_[ D>dT"^W8VTIۻ)wm TϋҀv1e16󶻇yoNL/$lj}g.+7MXZǼA$R"!eӜTU +$6ZRVJgc>;$#FdYiw_Կ,ã_-ѷ-8~٭)Դz'*%Վ^!P~q< nV,B׾ԊS u;p,`;mhjua:a?u+ki)!tVw"e5~v1C$rӾxV<*n/$Z]Bd}^Ys9I?cQE6}N>%>rS] ϭ}r&̈DȦn; WD߯&t{o?mҿNP^1ۅ!N BX1Z TBԻcx6<*+rU?٨o~ت-#j(F1!I>QB`۹@@@H !T:t muFڕyPצ0" ֈiC\ #09m{tHƗorxC kadwb6.KV@Y2X%:DAVq^t:"NHY9$e,n?ȋ1z#XQ" Bڜ1X'BܐG;_ m ykHy6ҿ>xsrB9\ZEGe8 _ݪm=7Ejuaguvx*~P hD3ӛ#aM<xEs, 3,Hd2h(9K{p=Äh EiHU\^Z]?dodf2MnI?L@0!RTA̦DhRL]ǹ?L+WְtY[C#!Wuk63mduW=")P4u91jǺwJ* y8f[rAշungt6FyjB VU`Չp\s:csWo.0u^#=}F8!|tthRgjy&zXJҎM>sϮtG" 8(YO) !B4ylJo !BJBY{ƍp1|RހCe%گӯ)vhn F(#6"婾`ՇyjXdds! unUlVWuE@0DX9Y%KJÂ(d.XZJ""J FPQ#w@SKDELho᳷>%m1=rت&-&O. dX##9]=yQ7sqMGNTJ*Kbہ #e5=>nUE?n ֮~zl汲XFZZmzVWqhx:0uW6n7r?<l\ܫxNH)RErN{{] 4 SGjҷ/Ju'H4=oi^lBʘ*d+2 1Ƙ(x^Ϫ+Z ~AsPmك(cdɺDJrVAl:hկOb)KjXB~}EkS=*|ͽ:d0xQTNwDJTiC4 BAUh`L&H@eA.Ϙg1*œDNPlVXYr- VC_jO~l,e@ȀKNn~5{R k"AVqӧ,o2u~Xξ ب%$~ ]'*ϭ8}5qtت|pt5s~)<|R 5O_OY$g/HPiX=ًEKK$j 0J)cTQk0YR 5Mly?7_ZWt¯+c纁4] + BՉ#2/QqӞio"gEeE*zxW' |NPUc[ߡ*| !yn' PR2O#lWlGFW1QjМߓh>m:A(Q Oq4-݈U e@Ua,ʪ~ޙ V˕ԑj(1ypoa Ӂ:믺&@b@V0ÆpLO 9YHnSmRu@ p ̲yN:K*; zAl_=U1N!?xeny9/0%`^&Fr\y{^saxCfXNVO]ز'm[xP;{/eE]- }-s(]7t\uU_7$JaH&S(LUB/xy?{U^+O{vV3}k4I !B<>@ @H3Z(*^J峝#-&.݆ԑK+簦?0zSL)DP~h H:Ɋ0{I")1l,dzH,|lL?9Y/=r>YTD>;VMYd0B9`m:&^yXAdb<ɚ{`J^w$$lZzߞ p=9ϩ|m[S=GOŀѩ%ATCQ}|Yޥڀar8jhXDR**Up*5҂ZRñ T ӤjRE9+"ם1K)&dYA-&$$F^Wzd-7|uV|-ẓF~3c3! ~`)o(~i๵O&3<'4 H;ܨ}d[ﹹ%|l `VM %N6).79pIƉ" DKɚ)t /ץQJxꥏt} B~V%rI%MKiA14PQmD22l4V ԺK!9 u6b[-\&D%wW4bֿ ,'\#fmױ@_09>SJQTvkwW|k6$r9VW1|+v*y1!)eIb XU!!#k4 !VGc$WZ &tCsW*)FE{kM׷)J z0OˁPnƚcsJc 7ry2,վͺ?HCs%U )!h /T:|i"P%au(ΒK9OáN @[(AJXrƏh2,uBT5c6rnV37Rb1QRPd1oAoVi^˿8 PE k/kj!BEONjd4ϵ(B!VTYZњ4ےGx!B\;nycJoWlV:r|nhñc".@[V?3?{9_AL[w_,3-` 2W+7}ܷ)~Z!sX,c׶GGg?x}`l^pR3^/SU5$)*;z IDAT-:6 CFj4PBVҴ?WW|vFO(^SJ8+ǥ{TRy<1TT{^QTDw*B=}A**A))}ᱷ^ @u: utrd hծ>єqT&vzJX{o071\af\a5ÑdܴP =y+Epoj\'*+.aܜؙA M7Xe DŽxrǝ!"ߌE;ʫC9{woۮ4w؛R6/. ѝ&0B! 7NP7Ը~1x^0tj xkRM͘?\v@grALֲU0+$Dk.gNwi94x(pZWRYe(MLX"|STF Q (<ӾW?8]R&1@dPR `b&D$A-&.n[Dŷ]E*LUP^úB#+60"Tt WހDCyn02-F#J%pLLX.jѓf}ݡo̷+,!IO`mDQaޢ5=86<@R-f (13Tn_,RIL*vϾ=]8v.< [o!@<CPJvu$go# N}r1kaeSC(7sM|ɥ䫧,nWH8,å'{u(qItE HiKO|>HHz乛USL},qdZ*D2.SFisPwl&KG%("\^&vqft!!:6XH4 N:b*?R2d5!VԒ ^iPW93A1*A(͜zd,:f au^Vd6faIZ qGA_}{E_}Rd O7؋!mSpx`aoh_,n_d6E^aG ;>O+1g]aƽ'vX"?5M׀B3f) d; RVuQnmL·(XQ-l4H#"<ǪiY^ p^j22NTn˄jyu _GG6sH$d UǵLcH'*J Q%Cֹ ^ 5Pi28 E`!ay(ZǽxUHP(n1B;DWlum`](;gᰫEo} / {7lAfD QR ?(OFҥs;X!囜~}M"H})WzΪrB&{HH(~eDΕ?p>e:9O|w+9Y9vm6v5kuO9oL|DE ^[-7[ aU]O/ntD5[qCޞ _{㻕>eu]n4!)2M(vm3YEoE X WT?SLB ]dxx$&4T ZEBx5'5H/H% Q!d`VzR'KE% Lgj]>FoöL9 ;B z !q@@` L0Ec09sVg'eYz^L pNFcRKGcLU3Bmjh_ebJ*)WzZ~3_ 2}UO&k4 @]TȣH:UB ~c2[LcZ:ĶٶΫ.۬|0[}R޵h,#*8VmUf/m}yҾ_OgX"e59k^׳L}j̘?vRc(7#݇Oj*tXuψ믝4UE(hmAξPKM_eξ)qƭ  "@)%"*N"Ȓumlï,^*HѲWA$ 0j^sMhu}߻g1buwU^w6FoMߢwpd1Z}d͚W>_HxQ5rࠏf[p,$S4[6>x{M*g,kݻ"R6 L1jg<-!:Rcz^L3@k [aՅ#AwLCBP; s:X kd6a.TbzqS.fl/KhFClԃDPIҥ-%1xN6ԯ;L]&È2 alR*Jr?;ϫI^y|]o<]LJsݙQ7tbrLӅ#Hj5JBg[BV$q֒f*^gUs_ӽ}oF_|9s|K*.6m .N@e!c:ާ.iAxn1WO^RٸTrw{^uﺩɃ&EhVq=pƱïU[V+M R֣W[sf+K{<6 6K2o~k xu=9VBzݯK^I$2#J b0BH5p=ڟ_kԲ}P3)5zCv4)2BBl{@JbF%r5ٯ!<||!is ό79FU|OkNڽLݗ{檁Tƫ;\Ժm@ x[OV}>¥!Tֽ]V5>TzaȘd6 bQ4ۺ'GY )vKy؏=@1__ EySw,}<) $'fqZHQ}\%Jn.!s3d˹m/W"{]P!j6Two3{WI6Y/?aНs/ǵI! 4FIP`11X eQ1:RR'k30>œ)7YN*)P1rXA4`P $"_T`VزC!|__?uo|hxц=&ڳśﻥ5,M嶲,Un~cH խW[ _O`+jixC!B~Hs. fԯۮxC4r4A)V-5!guq3^g\ Ya<`! `Ar{Sy3&OuEW d]!t*Zɍhq{p7(rꅏkʓNa/U P׶,!J`M]ug8u|$ Wy[㇛2B3t9\GcIy^L>Yј{<*]EP ݑVCP EL z>'&# CHIfK׫ UOV'}6nXEfIl[ԫD>T d,rSC@ jB2Ͼ_I,')f.\; d~+gBS[ڦyw ig!XٹUk<=L;[a٥#ER5 Mb,ܔ{1{Sn&Qb "`Uzg]eN9}g~(Μ3{}٦Bb9 ~ߴ)Vq#q:C83~xIG[m!>sUR.5uXSPfU67Lb]}"!a[ݱũX)+{殹̫DՌD}iS_I(538H|W_=+Y0)[EgSCAE 3 R)fz\~#~v+(Lkc{D44iSP]s(hh>kb,"C>ZWX{([`pJ3[!OH@jr.PJ2Y)o<|J <_co}lW^o'\!//RaB!Dcq! Kj|G8s0@?(Ogs۫Vpw= Į-Hg `}ZB&$N;984Q(aҾ`q3TRMRwWF% 84_>-IƍڳcT lj¦LrvpNȘ֯2w垹 $L>, !rQ)m#Vb$nƇ\rj5-k  hl9KM0'ʴ{rKSvUwgVleR/FA,__Qh"Q>MLsJiBb9qh߅?jnǔ'ߘ_+?ęal?iԀfj…i2ysʺ17]褺.l6Jr䇵^S75;!_$E1OHlR%Pu(ABՈX|[18pBD7=' }/NGv>@$BPF rhJ8@ o 9([J. v_B/h[Y?[]cH_]} VjkHX Bj/K~-LjA:yLK=J Z#$J $cQ^^{kla[2MF'0~DѣHA7yi۫+M7fxu Vuv2IRd m aSg3~PC*-&%!ތ3>=D:K{˭ګ+9-֣ԎĶ~HML7˖=~. ң~ Q @&GB%OrΓyEO/~"$%B,SĮ=sv0S""X\\dz֖_?Qy&]%#bx6Rjk291lI:>XƘƝKəkS7%J!%a8Zwpۦ+i/7SY|+"WgD+rHesgk>Y˒F\BvTJgP'! md IDATuWLNҊ3sQ, Ζ>3ْ<yaє$vlxe gpؒ)%(%P:ʬ4^Q^[O~QPǾhfW&BVqaYޒnV_DG"&‡ FiSͿ?,We_|3M[>kwxcL7xvß=qð<Ҝ -<3L{"=ޥ' [ZҌb"-挧.o|ե SSP5p;@<'=amԊ~^Q$$Tq ArA}QՅnА/JdvvLN]i5G~x_]QttoK0KdUD$&g#!?I:(Ixrm KicoyC"Ȩ^Q3uZlIBKWor5w9H;t8-?~ꜶamQòՇ/̄˿}ǵO} )c,=[ob}H 5^ wE+gˠz8L|T=m1ˤeAgӖ4CͦMqRor&<7]3 gGK&!KC>\Ye=g!>(]ΤG ֌a)A`kITa,+~!<3h_%bSrDKJ#.H)uaɁ@7Γn<*DDMj~)2MU3_k>{RqH:&~hٜ?4c|gsI.0;g[&z!?Rr~5îͷKcRî(YUVug<g F =֣pS3_;X`ؕ%?ʴ?vefDo}UϚXb͙۔gG2Q%Iu;]Ćcǁ=υ:SEXufhNj$wg]^Qy%w}}K(R]4ȒЛ͆Bdƚsbn8hF[Go9)yaUCLS"9j#"U cX*.qGM/vԾA%J$FDB{u5׏8vl:G H~F'4Zŕ}s,bd ү3glXJLRU3ݾqYBq#cSw%T3yt3ol*r=yz==fXhHSsiY7sD@@~;ps9o{wOz@C78ΌPmSоEb=*6>*ګ~E?`9HI*XP[3~qvP0z}M}?'m8JxpI|ՠ%bqEъR_S$Q|֎Su=^>k=TYdDixا[yNӅ _3!%Xʀ"!0gv:#[bGz_>7šQQNȪt*m`P~0SrB#B2j3<.Ƀ)DPiQƱAl]m륽) #RB)aXcލ<-13B;<гȓJ{)4Q=|D;^hfgn?$vMbBQB322$EOH=nxoF( ݶ3QJU~/ܘh3󲥫n6J:G :+c5U~tKXic,z;hEyI,Zrfۦ %`KRU|G0?߷\?fso'&e%z!e)m޿iHʍ]z6dU/_ʔLD:򎘷|rVT]f[3DY}Jf_ys" Ҝ~L8`]_ (4pV !2c[Cl 䬺-sQ`ӝ1ډÆE߄SժC?T*:݉M14"r<6,3YA@+!!b`i'T(9~K X|Ft&"~(Is+lܞ~?ю+ ̛Brm{"RK25>ֺo;=K`R$(,]葺KL3lawZQؚf(~ʠڡƎ9Y+lX_{CUKm$)҄9+YmҸVPR.~l u_Kf/A,]oPDi~kG_ h֣|Ȳ!ʷUq2mF -Q|DqA Hv݂ !Uߘ}4/W~g,행+DiMD*KKv&ҎDza xbﲆ:luB VHEʷ%n\{bno-xGkԽ5fI=Wy#X{C)=N; zuB؜[%t?iL~l8I!vw$i7zZhJJ`It&;ҬjEWH/MiKJ`3ek;`uҰRQUY*OjFT.-^.>2hF7E0яG52U*$ O FMa lym[Ssøk6n_. mL@,K%N:4:Ȧ̆rQA>kE6w^B &!@T Aw.41@D8ynA>tj٧Ow*7Vc `Lrک@hj;~  DQo">5|E:S|%| %w6PBHUzJ&Ӝ衹_Ϟa[hz3s~u<ҖϘ !PۨiN{1+%"$ȉazPBDKDLyf.jzVm!CB߿,H܈7$uGQ3]J+ÇRUBi.^o/$$.< ~ЬGj{)c^Z[,g!$֕ iӣqH!d9uSonmxOeW '3ekšwL( ;gg8fKesgHKDknV$҂]BͲEˋKJCuͤnkkWCV%I`iMYU4lF/p1ib+H!0MB4#Y%&@@̗)^eׁBt6}nOң*In69DR39ߑhƌ'AQ$yB" 6Db 6?A&q DL]v'\!,㑸v_ǒ^F *0sU 6HqoQ %E_ YJc` 8w(@p38㶯En{cӜ6ѻ\U73yFuֳFpjؠWh7< rCl ]7?XQ>xx*Jmigk`;0*(U^bxUjWXA@>r&BKVm=_}2ֶC~A;kms3̱sڶ~fn̞S^9)yd %BbÞwtu)ƶ=5 zՑ$BpY*m,pYZǿ |^cDwomJ:eՒ}mg^;oov%VqjfOⲤ;veWt)+)zf М*8GD08yku20se,a\\`b6g˵"@>fMMD02(f{Ȓ9Pj|n0=L*)ّȬ۞p5OgQX;ϣ7CJ%_/9 0!#n7ElVHv\_)4CZ  L7^|_qVf]g>nz:aƵryqSmi"m 40C"ݩCflU+ SU`,@ӧ_uqWjZ9S$e}=׎lճSI[Vud`2$&{]DOBe : tRliVbqb%mӮ)z1Ӎ WT^{ FF]F%0hDIAʍ{j R8߈VnGrY܎dap׌aIM W#q%F*J7y(/VE6uY5/Y1)yy[.ݑx f^ӦؓLG5^!l5)ZQk(ls&aˊIXS_PꢿTn\&3`mDDhU!<9h3DB(ml@Q0fTmBИcmY$Fl 8kp)%bF ;hBU0A m$3L[cnޅ+>ew/&ǜ&$@ƌ;n=/,E axshUv\cZ`-FIBC S_Fu#~hO$UeYm~ٌO@D@A'jy9uTɑf@0+ߜOJD ylW{}=i&+e`y`s︸l$B aPJ]J Qױ0?6{kknS iǦhEyI"IDVG[3!-ZUݸ<HH{koCҌ1//5}Buu$beEV)o W}sbl+z]G+K|򍏝j ~sۏ oV{2sp !=<8G~!/h\Ϊ zD$$$vż^E!oKWUj ߞHw ^@B`D$ h"%v ^Gd&@n!_֧'?^9(1#_8ǾQts;߁'J`{RB>MDz;KD&'t89}R;?E)pG9ag @BE^҇+I6w ڀJi&5BH0 M;>` ϖS&H}{{s9Ck,Ϳx鱧p{@X℮ȋK}hOGcE)>cPSQb"RBȉd L'qĒp\6H.[l*QrҾƉPd-oxnBk_z=S:N+m凹2YB,YY{׬/ΕٵΓRE?m?vsA,]{îG[[_=|~~)'d׏uC?X;Ɍ'ӤKxTlkk%zdx;/H REtυ _>`Ҷٓ}-W,e7OÇfKg_>蓍 woث˩NݾR @Ent_t:*t\T?jE&w 9 yFB)>Z-[3Tʊx2Ec(!DvsKE!RЇiFuۓNP`T. HJعÆ;0 mOd)uj Z Het,s< e&sk% ޴392jjzחcGm2Z"M?\*U[2vD:l>+2D`@}1#4 _"/ YoW8rEns}zca*&˔J2 xk2"̜B_ͩ$@d `[|WQ2kՇ@v#{nw2{ F_>s'D0;홐onjaݮۇ-Z37l5Lzz etL3T۔,rS+.𺂜=WyGZ>yn@+NOkaoy0sƄlЫzWk7na3{ًDbȠPݴ/C^yźǞNRQVR[#& M,e,u;k 9GJ,\u vJ~׸RO=N=.5B; x $@ mt`,a@L{pOJ6aiU uM9!@ݚBP4ĸÎ~O"b%B;,[-2 @ %n#LλxUldk DA+˴zLiꍭME)-,RKg,ui=GU SD}c;a'^e84=l`╶T:r$}z*Iҿ݃o,7SRB)ĦB~՘SU=&f͓ym:I0A _Qiliaэ ]GnaoR:n˰+o?._?.eA,ֳx=]岧 ;g\/ Kkjobi4HW?8Ɉo=iJVMk\qϹda8lo{%oTVu܎7C>_S+tCs;vΘ%ǎ p IDATMC5=TuНՕN9jg^\(2dm|̸&J@.x*uCߓWӏ4gU1c' XB׍&G̻4PlzY1Q2 LGe@/+KWon]d5`[H8 j/fJogƓ1yJrG2Yə S5["ݴÞ()J(@@x_zc|p%&.Rt5Gs6!;Z55r?X*cHgQmOZ"-cE7+T~@P< SyJ^=;JԺḟx=%2!zSz<\P^z6!qӌitX,x{Qz]Ul5pY B3͒|ir :ұ\bU7<]jXp;5:Տq})3QȪ/ q|G;\슥$U3;ᙏ?}tiE*J,X=nEb/#:j讗*ucMBO[lIk mK~UY "3c,CMvG?O:>C!8  =.vkױ JU)2)agseaѸqrQHgiCAQ`Ч}AA77%hP [ETt&hy7ky*sA2๟%*R tJ S`-rĜ%w۪ + h;yMYp@AJ7lPB:K6G_Z6ݷ(J9_x[]>0sZRA8. WVS)eŏ>TS-vNw0ťc3k{-"H(U vEfL[!^|`W0զMnd5\7dF "y 4ꚿlӝu#qʅ'/vub֪n|-zYշϜ8ooeB>Ԟ[*򢺑QH,жc5|Ⴅ.綾l `P- B_'Y&v:t9`!dFdr"fLrz hq#/8dC./]w+d-) +nk0) 5B0AY ; 8P ,Q /~}5#׎@dO;DL Ě䂀GCHXA (z8dsZbWvIͱbסF&_{/՛Z|tp/+/~웱Us}ކڦ釆ST7BROwK}s0y$s,Y- BeϞ'^$NbYUcuci=V.z\FsmfJ2E,ZWo+{W9+{!!^ūG>gk8cdQ(TscY i ԝL<&Ö vθ:Q֜@cE'=-e-_HH{##~wsN-囷l֎ û7/GT7(4=Ycm=W9@}qAtا[O$5%N({Εh^_X: 1a%#N~ ֞DFdQ9j0sةB~DVnSԒBP39Z Sw x@ z\/~{f)xzKE&]x?{EF 0)qx05 Nհ7Gbs? J~Ͷ?lN}̔!e˚&|JM*P"ZLfرn@]\Vsag%v9>=t֫T>r USwW7ʒxezm>A4|}ŶHG.2VmؚfLKo7*zȇ=st \ w9ͻf5ZMk0Jd<., $v ٴhf9k{zNJOI*^2j])kSZ\I$M+]Ik a^LOva!iĿie{2, "%ـw#w?FJ4#U5}B,La5>AґFoj@&a!ӑ 72Eh,"L쥹wfؙķH Rnh߫8EzV|=3nJV56LwM++tu$/[a[8cIYթyMJ=L:;Mw]_}9_<<g*w̜8|ފ'qt$3h"RY%P]Ưhh\g6{1;K`|ԡ<9i) fu9xNmǓ'?hOg qbN?jFT&J[#uzZϾ !4ܜQ`4?a3jz(D+1ČĚ{p#s$-=n\OMh k}EW!$tUpYېf'Td U݁|TF>tAb`|Ee+ZIk.~mOjF1 cfd 3ft=&ɱ !a B$n B)e%$>}{Zs?ٿe@ym"K{nJ$,>ɂ`!Wt24óJo-{|԰lvj zaA䮛Y29y~LfD&U6s*axNقɂaZqh6t;Ȯ _~ "Erlp$RRjh&I@ g3kMH/OSHzvRef8Y;QRP5T8 1h馑s8U߸e_6lw5H֍4tњIkKw[뜐,;=Y<: "0\p<䲴5^T9{ 0=-M)UMI}E=vhݣˊ뚡 KJ4zlznyzs )X vU$(i.'NT`VY5'_~:3:( 3Jd[Ux|^܌%3gS hM5j]#Q3#|TDp:Pђ9~L9ת{ܞjKE,3(Ze8lk.iߜV{bR.Ih6 ^5tOM/v囏T]bʆ|H)ӿHcϷX>"2@ uirhh\0X{O2GD#]#|X `@r8iPA ! `~oo̙IC3J$΃4ŵz#,A,SieDb-ssZ`ƕ_;h) ವMwXf|@9D.&Q5"HE]NID63" }}h"rK$;t0s@7M(VMmށ>r4R;ƍ(鈃X QIjF 쳹 };]:? !H$ádqhߔRRm XiļxZV5fS3}ORuo[G-G~ow& o|u{u9Fӌ#haqlZ}/I"jT >f|ӵ4  /YH 9~﹍^HQQHGĴRK^OJb|EcLԨ! Mzp{9}."¥hsv;=s1r .ԱukB_7/ĐRm{_Xj_֨1r, VK: it]ZHa 1  8r",}pc:cDoK~~u|WTn1pN8vkAnv:i/MQR2//ccx˷&,S.L.C)eȾثgu[nvBcSGʈ7rr9n/.5)/`Ȳ͉nU;mS3Sn8bgM~S~T"IӺmd w"kK-dpeTD7zҮ=+S|TMԓHItbXdMo N9FtC8%Es 9ٸ+X{]4qz0xЏϫ\1ÁkC*W hv*iq$x=@m<=z;P(} Bsǰ- k3fʑ-P, ruY]83[xH*@^pk~B,@?V|ι#)f&R uğqj!}]k cŸ˙x`Kv7]2 ۓ21|MD4_aβ`1֭ xme=ó9f1p8Iw]/!oCQ@nYV`,4Sʣ? jhb pȠkXۀ!h˓`G#*tCojoSr^oelS2OJZBx]샥a@)V[lۓ/TFk\S#1; Hl{9g!aj:A *>]SSF{"l}_i?Hܧwo2Cýy:1s7˖3yGjTQ.yyXQQ9rQЪ+' -Z1^\hYiOljΘγG9ʟJ3i{݌BP6~WI{ sD,.UjUesD;&9AӢo37oܳ ukuStN)s%sX]we#W4X5*)ݫ>\\\H4l&Hҹ:#%VT A.Sb#L'ۺ SZ\F :DD^\}e\;%r\V-jPr:FS4t@yu(ُvˢ|tٮ1FNOҹdE`*.z*c"K Mg`og q`w8nē)YRgq9rIA]dgNtmݭ]M- Gn[#֚Cg')ם+ tg>,|-b^ Tj]Tw+o/Q oɖjĽw\㦹2 ׇ_ݓf\s>4c$zD {dE>P}k>]+͉b͡Sȸ5$W޶dӦouf (CbZm}pFNׅsBL(!EcKkQ>XYƖ]?49̿? xDpv5/*!CQ+}3uaWv'K^>oI/Y<enB:Jn IDAT'qB>N]!RM4X u7 c三-`fuɲ#甠둕b3&Ue64hK\74C^2k ;Wr&$Ƥ1 wi"@;"s#5tXQ3~qFi<ͼ:w$=_}uHeq҃扴_[[Kk/[a٘sIJ?ﵥ2rpv,,}+0ٴG~O^mۖ .Y x3nϕRwقiĴ5%q/򋗝-ql9ͫ%P&ЛnY󞸜D@oz.gY%wZsgv8BOM3oE,6d馽;嗯NYw5Ni)i_nM0Du $FކϕEwhDc8䤙ct^*4Y,EC~H?Ysxu;5-3oW}DZY_^3c)v5 g,aY|^.Hg?}){w 偶)7RS:Zk;u1H'DE% @ :bb`LnhU %ۡ/ӋE [H7Myipd-NˌyY?ϡrYJ͚\b.R4Mp7Zw}+6np% .!7ރBh""r(< (go,RB#A[%yd+ؒ,!u`_-FLt ?xzt/[TF['dGZ%,jW>86yd,}qO<3p펱X${<79! [!rI&Oʺ3fxĚ(tZM@nL_Axec;.Zg<PM#! H Nc1#%:d&2}`v9t 5w&fg8! -*ف Z拌1lS/-䜕@EZcv' Aү'?]GꛉCz1Í߿]*U0'߾ϴN~֮irB8FZ8gjЍAU0f7ѲB[wwyya=9nx] =ي{nϗ7~ϰKXq"=~#0,'V|W:)ěQab=1q.:j!3WԼ._k"]S^~A_dl}a}D2>tu?xtu$>G^ ZޡcFi+ʂ5̮nMsT8c]q ED829W^p W tv?crD{gro|dޭғ[.爱^5vG{Ĵ/ߜ 6ytJW/}+7ק홁XJs;u^3bΥj4}^QՃG]y2>hCqA.Y|-HC+"@"7&s5."{8bE$/gNc!b,@AY@䚆YbEs~ȫ@>b3걏'v8E!/(|BVFhދx\oͭݡ?kK)##,W敾T!&l_,K 0@D ʑ4=u|%&Rc)?,CD" &1-W^Y -Ƒ' c^W Kw56)_p}jM@|eTwlcs\@* ,HD{_,MEEnf[`)犪c8d_[bkvܠ2%qѓ߻Z%  Ԗ=.^$qx 'U ]}cCK꩚%S ŠΎ^Z rG6u%jxL HQe 2nw4Ŭ4&|49W,® P7햪8WdxxT}s\K͋`VK (F_Oiv :j>Ruyz:n #W($F#,)!mD rB^-ҫgMv魼# sSJeŚhٷ\0V`]ڼ i 1+ʄTڨ͞RF"Kx2ۣ rN8PLQM#c<>ܵd ojE#-1vU=奼ntۗJ!BBlҖvaN~&`YvmS;>+_B ?}נ>;_q6<1S54sA_[2Sڝ7nG:!$a7~OVtm!>LA}r@n֯V&n5'э;f4 Sǐ\9!\$@^_A zˁr退hAAU#bm2n9,eiPoAսԠUF,xוn璟}75[>[{q3_{vy' ss$'}g/&KBȜ4M($2X:J7.{ ␅h$T7_WwW ؓ,)O&zdrV|̟ז*1-@`nSWC<4؆Fe*#] U8\+\UQ =wdA;Њ6qM'fRjbOAMqH80-AX~19HPBd/ƒb&g_j;S&_à,rtM]PuQUEey|utm n9 -frwh7Rw߳柾?VDp]˾n#iw;_Ĵ,ߒ.eD7>7|x(vKwʅ̱vj.TP疄Z<5L`CMW͙|y(ޣӲ.4Ni5ȵG\~ B M:~֔g{n{ଃƞi,\uV499c֞@0J༩\QoګDWD.<3L( rM7f^@Qdk<=NcMOJʌ%\3>O].ˁJp^}қ+aE "f@>}@1/z-̚ym4M{9pz);ٯGv 5R]:iq_LI\']6<1ϔ;vGy+%UݔEO{*SH.:3d'n_,xEA,h:"- Hn8)=wXc7!pkܕvH^[CAFRMT=uzi Jdx\BY;fPeS-{o 7 {l7qdŌqV(RԱXۈ iB>$?քU^]=Wx ;7o EUw9L%(QbC[H c#c#eٹ`drܴJA?mjEQ13}@S)Ѳ莽Sg2{" [sy@TIK"cCS/~=I{!6itj iˈPAtЂRRƆR^ 8!_.rkGP{%D|st,.z]L! b) QsQC>Ru5."zSU;]]vd݉;@U+\@S#pTvW"+w"k+VG &5-bZ-{lY;=v轿yvܠ׍k4ádtRj煽.h|1L+ d MS09l%46EfVmNYX 絭ȑS֝C.avk/DJQ%=c eA0}EZ"MΘqB4 rYQHa[䶇ȋ*'f?ghE5e(+b1=|8%94eQF\|mف߽TxUK;c8(5ͨ(wǔUXq}\Tg}e0ݪ).}XpJK5w7 Mmy&CS=KѲ|FI`稡eWnHQKH*q!^8~W[Kk;|l?YE5H]K,kK^w݈\nR?օ7_v.js=w.$1@c;'ҿٻLN 9R Ao[#[Wowf tޒENWVY@T}DoOW -[llw򴟽lhg"-5ffoܩA俿 iP}AՊ(膠끶՞g|5ƴТJ .}|( <&(*{ls~8-\s!# >l@3a{kT(`!p'RIPfMlڙ73%qU$?gExֻn֡!`i u*?C<Nd{p۲͇g62Je,ͮg=_^c2e ]15OEO#gs1|*>еGfTE,A4^E)?>ۖn%vzm٪i?ȈjI9%(ҹ?{q>lhkۑ-$>wӠMz;L4[wY"`!X^,JEUMo,~p㋧F`!4_@d}Gyk=5(}^^G5FMjٷ[XI[B /8FSX@q;;naĒgod"u:KWr4c1 Ocy-! \73RϚ\өӲ"ἒq%IhȬB[MeA3 ,"u;=~D)?/4,˹aU%D@@A (j3&pߔV2Jt/ѯOUr{8.@UZͲ :NrBcΧ^BN ƨmlH8%1؃W| e87,θǵt-0e8+eUv+ hnt[7Ut1,Q<5 yd*w2-ߺz{nC\ߜOX^'+hqW:w1Ki%(hW2rdTuW-ۖmOt5[>9FGtk/Uq[<` Ԕ$LWENL&U!cb^?'i2r{塙cT;>X*_<8sLshrU zuÕQp'J7~3e.P3(Hg4$}V3OWhMtHby88gaAz2Y?PP7/nrYPf]4]hg4;y{3˺^tsM&2X{g+VG JeP/rh`~4LP|~q :ⶾ,4fX )9!_#!,- ~_,М~~ǹFn'1J ""HiwwP*Hg*vksGdN=%lLK;g΃$7Ѳ BQT.XЏ~ޑ vUsLmPG 2I+s0ggTvy3z5n\1qpdyXݕ2Jr!_koNo ={vK&|$) 1tOںvS'US~dݩ.@cl󕷉e%zT9vC-o$=8kl. #|yF65s{T? NK[Wl&ق=gf+#׵ Y߼&4m5%J US|F5>74ǨaZ:q{]+'(b$Q^EU;@cMTdWoUOR [&{ bCHd84!F40[kY4Mxo吝9ZΜѿ㝾ȝC>y%d׊`c]+;4 3W"Ib˞ym!2Uɜ)BxcT`cBA72%\2d^YuxLUru;;?W4f+?0kng=]]Je oM ]wffXŀ'֫ء[ȗWRYA7>^P-US(%`.T B>dn0U]Og&n~e rAE>(ENlK+=X꠱@,]êI44~Ԣ$RSxx^,h'29j7>zR7 2{-p Š/ؾo;_]}j7eqѓߛ%$MH$HE[1۝ Xs[\וn|tE8rٝʺ9jZteϝGN+ zVkʝ\{|Ի9b,(qآ R=q !!S2j޶=y竫=TPa^JJoϝ/\ډ܉ry8爾Ս`c["ɘ PMZ%d+_>0I$<.;Y,<Jo>:]H\N!u5b4ċZBaeYQ-_7a,rJ V;^<'~Xo !5ȹLe$zζ a8\yp7f'U:3`SEikkMdaW&)D#SG=UCn2R&>3zއ'!DƕCtu-Yul$2RQym>_[*޳:zϖmO!đ-pʆ !ei*}~UyW.IȘ7 FJ^v;flg"tnXvū%$Ċ}R^[8g=.p;1BL/ݰ:o7ۓm?|^f42E%Dn0))P44Wo9kPCv6ܷ{f揗 879)Ci+BŸ# enCͣG}%L] ,Π%٢ΝD\di3ޱ7v簚nBq.勦! 0zYґaA=e|Хڹj͋ɒlE`~orRME.p.WnEٕ&ҶŢ-K=r$l{',_H.u9#c;LLˎִ[_BX׫気9B}*:(-md+TF}G?7N"I c-ٴqzw94ct{X$}H T9b1?2ST[6u #Tuy>[1./tsNJ&y\cOJ+#rkKdYkm^W!?=6rkYPNI.KNi7D0 毽!E8eqѐ} ЀJgLO6S4n,` UT"!,lp)6r*20-frPnܽwu-|;  ȉD%eJzl9QJQr) ڔp`v|1rX״l~ GUQPgMmN& Yon WoBԕ#LiGFY\g}%7NoHօ:w㧦 _&K@ /";n?,7nYN0U%wXZ$xɅPGW 5}^#G঻GܼyKOMk,q-_]]Li8O cFu7W:0wڐ] !_F6I# iLrTah*ЇH,aD{nAPaN%mQ'/4wWιX3 <sl8h{9݈4Z;H,*FqmGa$N/Cx$4g46dy>רyXcgrO:I"4wpCwDVK;3KO∙:v-L(apiFۧ쀲m)gomZV9U//?<-7{wor瑱eK]Y+'<Z[ܛS%nە fjbs3_:S/-EL3\}]\Fҵ/#]9#Wj \3UsͬXf[(P>H]hޞdsf,<j0Su79BMPe;ŚX԰~yic&b ȃ+#P{IfkLabU}mZ8Ĵj*2,1t{.od)rĴ7Rc@:;j?_r]3]]ĸ7_4g+u_~$P!F5z@,)iT]H\ݡ {ޫS!DeȮS⼼mo>d/1`A }A>!D줺hAU9_i:7m.}BV޲¼#8 ,θ5.M.9l+j!Vd_M~Ja]"4WD)o檎\i3_.{0=#ɜsnZEc/ݾ- xvۛ,j0xz7;f؛qt_M3 IVyꥅDixm@s9~k6E1M Y=v.B'LՒ=ܵOeXqOGK"'D*Kԫ#3L!Ī0WotdP9Z}Q׹e1nyю+ ;! H1#\}ծye`#Ώͺo^XyW"vQDR9~)]w~1Wu;j/EuL!09Bv2K0E$aQ$A*8ŲpinVv7zf(w܄9gdy, yYDv YIEnxTLO^HHiPO(mڣ>Cs>(rtONckw@M-B D|\3{t>2w<.W6*3K' ΜX&KFr['tNR\R“.uU #zZg6MqBPA8º.zN7BKLbOHL {<)a"Q ւoQMi%}qUh:2ߘ{rj.ߤ?3U3y؂ ӝ]#}]׏wY_(bM:O 8Bko~:ȻOW||J|۝K6;nbJo=;[?'~ŗ-bn_aTl\Qb pUgK"!= A?XQӕ.Xu6욻̴ܲ9s![7hTƙ#Q8N>{\ΐksL97L$,[@A?3L9 (Wm<4Iۼ9"օ$7{ (wg>rM&<(j^j=M@$6lʈh5'Dv"!PG//e@ R85'$Zπcl;d ]Y(Li‡HQye$ Jb(esU7a"JqTIF5+NPM\C('їrJywCcMrIJ*"S _溤US_ IDAT,caѴl\uʺyGfwJ/%cG\ ơG&"o_mo)`9=KoByZ8mb,3䴱D r|ȪFcЉx>?Tqo|L#m)dSj8 Op骽MO}`a~qG !PI EpHXOU'劇Wuc(7y~E 8g&Y-_i@]ͷ_-;2ܰ> asε'Gvjg$@X2- kwpBM,?رS 0O\eR^0x5h<9%0mIT>9vt[6`O>D0T  r qR]뱃R(r;{K69^.3"wڸ'Bk7j}g{t 921Nc=UBx[չ}'nDͭ=Ȳ*~36lOx/(y`Di'uXۉ7&9BzO.Q1nk7GH"J0{OL \o7/QHk G&98G\(ǣK S`*_/m}˰7#6-WE hZvXa ϿIjʲ:|0@:2\*?WΝt{sH=cEW "O(}ު(kIrF"GN#UCŒ0K"X\{8^,/g5 C_W\8v6RXY^gLݼWJđGvo ˢgp%4=+!~Կ68>$) OVyrbr o./躧mͧybKmQEl,zQߚ=O]+lـQ]5;sR͒U1)x",y +;]؉*xZ^^oM}ؓ/H4z{7e5a.r i:tgЀ㰷7Ws?@`GϚ40|t2J՚9{"[^xׯ;n>|]U-]u CF XklIoL}} G}2Ǩ恭߻~OICJV眐Ruԝ9C#Ka!]?H<8y)uJryev57NGyiu||$&,IV\ -Խ$RSF %Syf6rfD Dh:ТJOt8q2#bMk$t~mB"v*+<<]$8۔=‚>ֿ{~B@B~ypC`L3kwz>00\j.'X7sQvB-<݊$5;V{ZzcȲnaB6+C=H \]1^ m3% ێ'iCBg}gTi]tE|nضIJ@ <6ngs|hgWHTn;>׌4:sx K\3*_q- K#0ƌ2cbo# @qZ7oy=˿GɗM]'R;`0ݼSV/?(_Zŕ`uOI#Sl3#bUEF0a 1;8:͕ñkb̗+  s򕹳|C.k{-}XVuӫ: ě/s|hH{}a,XvobƘо}ld;yop}yXmŵiqƮiFO0pNgٰ?{|W9L$9i6&SSA5%-_IuqT̰;n1KPxIA|hba&< gMڝtg+.v]!n;H Yެm# Ų[GO+D|Uv@9E[6ct27PGAdv)E*T<5Xt̓ʯ/Ôbcܯ\8dže#џ9ى]Ɇ+kĮ&7GIZjci+׺&E) ~]__ȫ6ÅL;DvVoC ~uv /k5͂,K,2;n,!'5B]}WhJIK0;2 J ' _$>\]UՅH0z9OY/<z m|L =7闋<%us2η޹ds#6!{bh0ܖr_ ѴXUUpdy8@l1`HQ6TkPҹn3Y&y9} "  ?kVT|z>3iO!l #,GO#;He1^99X֏"Aa/8yk@- {d@6mjb'@f_ԯyjdav'1BXdڡSgwC1zS=pnj,hܲ]]'BX g^R?X9a1~'uWͿvY$GijOk;>_`ضtyd sjB eMds % $aD1D~`l "\{^d%"YyJmYoڰWSmO??ߌ K>}>|UԠl=4f`i+ܲG jI;5hYm;&Sb-bH>sCG/;[{|x䲖Tn"eȨwF"-5z#%-_7 C0xĔ29GEtОSM$jP, ~oW/yeׇO{|Js.bN@ Ry*C)}r*ǸT۔c,`U BߩBbZHe#M"AD0lLO;uUĚxb][9OZpεǰO)oLYS+(eǢ߇,ufH$:ea>Ŝsm;lΕwz_at0n;ì:w[!Bn@Yњ3;iP} ObHJyy,Ųdʙ3!"~˒'!e4[sWj0nZh:BX8{1{2Y9;|3Q`#oHjO]JOƝ=[q较F4"$;0cbQHZ8b>2a{"AMkxu n1rEuv4?1ۓ+\xj΀+vLKh$:҈6 Yt2`jhJnB "\'DCIcH..{HY v̡39 0Ж>@pf6mR4Je9H7gΘ6Ο;3f(@%ɪK]Ё#ҏ̨ZNH5U93FN+&ᜇOq/A[qb+ {Dԍ+ݯc?]k'W`a1m{2SLJB!M.X7 FsgWWBsrT׋C+B%AI4\Uu RLiͶ=jlBc2}7RE~{UOIK.$Z{t,.ދ;^%k=:+(ķ0o7~x9q"Ƈ8+ t~8'b[Ljk9>WKb힢ړy Vսg煺c|   b;oF>WG:,8n"XF-AjLt9ѮS e=cgQ4Dt{ddZLNPDW65{ N/wGNX8nc8fjAl;֘<9mrf`G^>UH0}bjנ:%_ci?~M0ޕG?/uʗ"P}tS&iF#9|3__\y:/04!U*Ӣn&in؎q΃\v&`OIe5[{]ܩ]WY*;XՠH", @) $HPT<-\g5z 2nī0qiUP6Ń0FŲy 8 \Fq]9l֞zRC"?{ՖcefY>,^G[;1F3|\WE {-;abwDGNaݠ0 L# #P-7ő1O:~/ݪ˵cp^co=}PPM ƐN>@ "2<;9>PƇfX`Np(pFmPcP?|30-+vsfU1=FT-7.s=O1g.SѐUE&N^r$'-ٶ%F6ԋGOKj=#_w"ICҮ.K̲9z5ǎؾ[o9xs |ᡧ_~=pTH&G0e}%%$l~i?~#Hؔ!P_Ǐu# gيT$G =8g ͝/OEZ;MUiT5@SH𮥛SCRAp#ze\7wXӉjpT@}qdnAv;'Œt_[;6MV]Vzv$9Q;N{Tk/>JI|0;n)K/Bm<=G&㷯6˔µH0z q77:]sJڒna?ȭ<%¸QQ0rYz}z5nF/PsOaÁ`gױC'r8? pU-e jd,K߮>WLH)0e D e߸+8qhljLMbmo\/*w"XЍOt()`'uy66@`,y/u_F߂;c ӽwcPϵ`ot 0!h.T :j8v[0!SWO#Fȓ/sLbw{O9t9Y)Mc9NvКǸ xRnS@?zA|!vP:¸'svbź㳜PC붤gL`cM99gun!.PRrZ܋X3'\6 .,\'?7hH,KNvҪh墯 `,iomAD z%= &!h1IJ-I3G0v</>5?^Mp]!f@k?/#o/v>?ו;Dú~\C/>zӦ}WV]/tSCG1.v g_޼XcroQCpVW3[LD/R(튦#x%#=EM)e`TCuUir(BYw݆b, ~Bx 0ήJAGq 2(+k@U enZxC4$ !0,Z(O;p z/B,_O'%TEҖ}݇(hy Rè:<hZespu'[vBY6'fI n6+tbY \nkA D(uYy(ݸgƖBw OzѓD7dǀ+Sq؊uoH>hMsDx3]*E(,ߌbWňi"@HS/^뮻9:{10₀9j1ɳA3EWm mݝypjeW<4QeM o dmKꪬNXϑ7Ցud8zަ94 ͟3rWuI:bњOIv+}fI5ӓC F/gǍEcJ=E`Jw'9:vyJ=Ho_^]]}ٗ+ ՍB+Ҿ38t1pP{/[~@S݈2$"jR^&ƛؚ47A | r(*d0"!%sC eZ(g$p@T\/Zg5wb1j=ͼ W<Ȧ`X1Yavypӕ"뵒\H#B)D 8]M{3]u;3&5Ts{'FL"! \q!D,\F~!Qi91bk1[SFqP@Jn G0Ĵ\pfV9%YP5M 9 x>K(؊O]0@cvbP7{gukx_r5[w7m2-PXlÆ~NyPX*mG2kX#\̄;5BYyމj+/MP VG_0 `up k6cJ{$;n9#;s ;|kBZ0XE&X  $rڑkƒpc%%m&.z ;TinKzWÍs(p(h!jhr<兹\?tA&^Ʊ'SҬ#IQ\)ȊeZ X[ k^erc@}ȧ`MܲWvmO-TGҖhYsnXC/-w(܏E,/9GPQuf!F7ڱm1T$6B{љB֞ʪ]OB*KaZ8#V$$w۲DtٶP*[' F3O8^E.(!ўhI̘cMO=ظMijÎ8oVo=߼s/>&흩GΜw`G#r Px؁^~[G{}ZR{]dV1r95LNHl}hQ䂱;uثFph(0uXBܖ`0qL3ICMuA^0X$n}z9wis S`˪]>5 SZ!vMw<(.\Aݵ)ߟغؿ!t, #l;@mȀ5:Q!pT ]D`,vkY~drÁE+hojVX>Ra%2cr-lc8Fכ G7jݛ+uml/H8 έ{Vʲ{5#0cm;'ݓXN(DUJ ihY.rIK:nx|E_~eaZkR^-Hp٘/7S2Q }6Q|ɛ/՜Ȓ u4`_~fY1TbۈJW R b˟?n"Fq-3|OIA^򮂽j:͍YriMc ɔ |Y{|v }@Av҂lz 2j G0 (2%+B,n;Y38CpCOc+io zHN+^9Ol;f xfI:rJ[v[;ZmF5#aʹoݣLv)xRCjnH c[At2Y(cwQ!W}-A$I$|1;*oݟ[4n}99/oߏÓ/w4#lhyPwߋq·@{nQW9DovM9Tq9#9'-mn"B gڦ&C~Ƞ+9_ 5YK0k6(wR8~dYWu3𤻪=rG19)q˜}•ް]in ߂4(jCjwg-T.oRv3>>rL2B=ѰuqN0c$ƍaJQ88DEFC#3-'fB~-T %o)7m A3?x1)ϏuƽZ$ u$`]r;7؛`Z('Γj" vY8Gw,ǿ$olS89͗x"}^ԙ4cn}qJ kvgGuz@Vd! LGLDA vGyOL5+Ff/'@-\$XzsD܀ljcY,B]9gqa<{tgs`ê_>9gpoMb9f$DG } zg|>Okb*112BA)T>;qo&+ݤ \.(9}bᶑbvźGf.[miՉ]}~B`G-2A#EAOr[guCT" ȡ:FBe 8bq¬TC~綼{G+YEBJ*5{d)?;vн݌8K*"g>{/](8QApk/3]* 1!TY+zw[m)Xj{mR(}ۗo=ai\ֆb[`oOmb jRjYdbCnn/> ㎃C~qL56j89~xm5"wRXR]%Z@#A>P L+<Wq.+1Z[ 16 r E-`Us;ft0]w ddB?=,i1Pn} nGfp_K; *L"8vO@i%*kLz]k6ŷj(JŒ")JGߚMrCmˬJgzPBBXVM=]D%{8uRK/ hē'6n'㄃O<4h Q(!&Z]M{+Mo:X %7azxZ+K8q4 A31/^lbY1`/-{p?;G7cǩ޲ˬηl5 kH~&\' HR9:0ozjXHK 73W0ScM/>78Y`8ƈ]G) LP{Ƒ%L)Ø8{6O=$G|V|SLQu˪!Ɛ]|D"MlSX%a1ęp{FOOT)WEZmXqE[ay~I3Dݠ`Ƣfz]> tu9_ px1Bt~6n&" Vӥ&8eރ{nPIE"M-fnwv!Ekb]U`h!7ВW-)+5d gaHtX29f̝V+8B"Y~d*Zq ˦ESb% %̌3&1e_8{Q^}JtOOR BdP^x|~x׻]Gl`PDFɡszG͌e=Hg~Օ:<9D]]&l8 FPBE< ACcdc 9zQ4DqJI Y,=]$ u$K¤h$H,Kz=ɣniƽ-zs朥¦JxIh:˞cȢl ,89\H$Gء,$`@n01uzP0Hek[!w7L}hE9RНB$,xl.uOj()^Wg2p~_Q* *YL׮˲^ ڴCkĆ"N{(w5[V}oA;bؖ t@z\G.l]j`,iGrО &^z#t?lUV16cY8ԠB$$ru$XS/];eM< > Xxwkh8 ;bPsǥܝ)ؚ"mss4bo 5w^{O⮭s1F\Y&K`.&˶*;<#0j,;[PKR*{T*[ b٪ĶբN)r" \قdĢe'QKd<\s@)%[=lݡǧJ Sz͑lI`ǟTϾ2G{7-7g?]E(ܮb8 !kN<39-Wc/a|ĄѸ\D#XyisRH@v+ ]q"U*FhPh' ԞONCڒ iTw,{@7ʟW$*8/8D|3DBvB- @\ w^HB @Ȧd@bJ.u~ 0`$0B0m{jF׻Z;8ƶ,aϲ+oMZaܑBKp$m{= MxQ|$h3M5b?jߨزjߨ6MwCD>vS'}kqWf-_@v<4D xO5B$%]mɇT@tKFLK4`ܜ;\tǸqs^@5 x]t1@Hݶi+HIjC/3IӽⵇĶ_}sB(E4c+<ɜdڑvbfuOeK6-ɴt>ru[n*2P'Y\6G.".gF/62ѯvd[ΏREn۽bg˟g{꿿[OyR/٥|?KCoLD`KRoB{.#M D*鴥]YsRn]lM0 ͚fXCa6|k}N){Yْ)  2&>گ6< Zj94Ya4 <:!bst !d^ҵ A]=.\]CEɠ #A|1([#DVUsi@LmV_o [^"˸Pk`ąqI^hq-3'kDE<_}C]?!"1@Wf׮@MgH᱀P|N%C9GKj{'*[\ta3>aNыjoOjfڧ.`e<-"L8^ lI7$`c-/͚wkPi9SjbG,XKdnvρV/ "!ٴ 岙 І?~~ΟnG+΍}8IG1q9%,Vуwiz(P9ƀZ(-$DM-3 8udeS2-bSP{5c9?5u zٚ3Ork"G7OTQ\S!ȷ0Eg`o;G.?R~U;REBs'1l;<:o7\aLteְ=;va cU^TN*U!gnE==(UH#ut@Z9A@pBdeg~{~u]omI^hJ$`<1Bx:' "I6\pBP7'8@H ㆱ4C'x;aPF!rt mߗ:8pB $0L+)/s{P2%=L~zIx+Ӧls".Z!CE%Y\ >Ib(syM^"{/Z9a#A].1Ôlz&al7l `\t7%T@t֧gn\ k IDATpI}=w51#AI:n% C:{ q@n!G4}Ynm߿J)]IpmM#s{UE4|{7yu8_-b{;?oZ'd|s ,k߿zY+| % /9Lg];b`Z(OD E;8 HU% H\҄ ߗfvsKDRjAlQUD@.!r.ߣ99yCZA[NRsUekѳMXMf%j7Do~?Ԓ}m4qz&@{PbʜK.'<ՠc_]tU/ˆ e_"O&r!Uߓ8:sl:j|T+7ZHv办tnu=ăNtӔ}m^ Tj&3x_,=Z*{-ɔ,rْLzwwbk*G)@Tcgi cNmG*芮gEK )_'Qrjz{\ҡ.C0Fvj@z vY.-} TU)0u5#Xt%@@0Bc1sip$fUÉe^4U.IA% "ŀ:qo?0-^(!K,@w0$0M 薝ɉcj@s@]w߷vuR'vihx$viVxҹBZw|q_|ӇT'X+a mFGݮyyG EGzHSv 3.y||.у>ۥ3L)4ߞoNo"klSN~M-FϻM7񯼷_tWߨ=|:n e1涾q ~횓rDW> !8Mf곋{K}MJig:& …G !ʥ )vThg?sy񊡧Ccv"CY Ի{W|{ܑ_L9Ly;O,'՚9BݦKN >Z:EY8=n*k2r ²ܚpP0 :d-ZWVUfGkjk kCfU"Pvb ='K/ пw9h/sp9,G\]M-K}:pka΁ ΡK ѕOt^Z|w ?aad)}W6t{?+J(`Z²2#}Ľ> @tZ>9+'rٚXh(vi-WO=q:L+")ziNg7g5Oq0eL3fk9qHe'}t\B1L= %ąd/1h~D`Kb cZ짣g\ c a^cOLڊE433h`kBDl\p#5Lh* Nȭމ9)nR-Eþ Cŭw׳.,z {f,*lpr" I I+t7|²y"M}QkT|gԞ[L И^ƶHHB7G=x9WW8# )h:9,㣇@8ё`ZEE|,V֪+.R ;nZX/$\~w%*L"9`jA j"z/{ Q'Cj!Ie *c\MRInCO3RÏU Lw8O8/vYf -{#%8KU1$#,D UeyҊ-*NLGW| >ˣej+gO).27Kݙ<V)0j_푳WAwg K Œjomc>~;qa̸"9)NcRȟ?>mT"ʗJd\UdڮlqКNW+6on_Mlw7;l "^w!:Ș|e@Gu{F_eo/ݙ|GW3T<e'][{z %zm nr*k"XBK9)]t*μ+]NfJ3*]}B=9LbGN2 0*Jeã+0)´HЏd:U]4֙tik\-j+Mi"\!Dq1gD6e'D8IKWiזó~ubvQ6y(HiȰ cX56HT rp(>$˵Ƕ7ˆDŽ=ԥ?5wf?:R]Ww';KNe9y @pPDf<_ 1<)@]UHrƺJa7X> #E3sQ&ڽK"!0w,̓7]I%b]^~6?bw@;BZ=(F`a 1`ǡߣ [SnSMܔxNY=uCˏd򕑣VG1e$*{{ҹPs;)B[K5 Eϵzk7RdGca `ϰsl9wQU #,TdpPUF5H2^^"?z'a;,acP#dQ9)瑣a!R4,,Nfge%6'3SIk?8݇APB ->$ h{4~-;3U! {E_g5VmnƸQ*+W³yriumJHj݁!) *.&MFDc`u( 9n*m RUP-Y2^r%]Je?6j*dSP$+JMmZizo*Bv.IYKQk,/_+Ȳ_: bwxd g|Q(qLlZX~Nś s(?r*1qh. @lٲj_w:q{WϞbz!K7O"WxIˁ)\3AB:Ŋ;]j6v7=8ɋw5Q?1ΏdsA_.~|lu4qݽ(Tgj+MĄ,&^&9zn೓--z;'6n;td3bbE0]ѕ-8W<8򾻷<ɒbI2qug^I 9r! ~{[ꊮ´T>p-/¶o0O7L);D !>4_ jtkҁŝHG37&ۓ#Uy/}9bKnV( | 2/2A,6ZBp.y:8ʨno.h0|q x@/ 5mmm]Y[&K3dP.ZcK{eF:^{,K Pd"5iVo%`=M;Z<Sj" 8q&w3 _[;\bx5mONuQ1|B=/Bѩ~ptÓ vTFY~ID]uF~Y]+]i[G"ypKuT5֔!K7M|WiőF~%q=˻3:~riL՛a6[CS^6^&B|{89x1ޭO6 ^#岅)CB8Ί@b@'\iOvKoZfbġP! 5w޳aߐݙ\61e7c<.g 3@q$qGW2vuf}^R\A# TVpV®]y!aZ4-lO/X (cLv ljV2H(DTvIN^B)Qօ[.kKЎTqA)̘[~bܩdZhy]7U/r/\{̐-|9'_`DUYgMMqg Co|_U:bJ&UWF-{6}`Vw&&m#D&Fl bٚ䝧T4}_rsrEJv.dR&GC~1FY6Mϰ/]%D+Jd`٤laMWv*Te 4|[}wR+Jk 7%i'ꥊ`>5vϳtNXUΒT;v#QQOϠ}k + m^52"0{BqӲ.tk,W?8&^T+<0iʸD2G_[HJwW}f8.F8l}Wg_[Hc޵ ۫^[HL ;,ⱎG~cwiw'SC[)D*plj}\:c|idYزd5LUh@rgt$11Tз4tPՅP(q.#6lG 89!G`sICݼ{"eת|޿.ooiWz`z\+_;dŊ f;8FnT7lE]F(ɴz81l#"0\jt.9t@kBMG:(3Y]Z)O4<Sl7xvO:/eBiW-+5U5 W2m@B4$!˭Y.5zm:%\y: HvgX=4;R}C$/N` k-eW6Cdcp3nR3/4'W-fm!lh[n,vx{,a΅Ǎȶ-\Cdd:|}ꑉe'Ph۰M1VhxWu5UVFxa0fN /<[pyu>>PrNQv7m5UU:{Lecq8,;68tqBґS朙S{\W鬬]l# HyU;wvɩ#'|7wa\"U$rg' i o#٫Iosw1v82ɢoWw)ɾ]dZqN0߭SF䪢33S򶢗z 'B=VdZNx5ycu;,uuZ|QN&cDқ;"0w)F%> )]r469 >b bS虩yWPJ`=袱E"ECgȽkO^ᘞyIA qa5qk0 81@Y2ĴJ23C  Ȋ:yR{zsNJH$?\q{WM+>žLg+֩ T@IY *<~b3bR򅧃K֐Nn3v-N,::)J%k矔t5a/"B0B`qWX.w\"=' {xoqCr ̹'Sܚ7 y{WGu@\rEۥv#G&5PojO*GlKvip&vrLmߙvd1?+2cL(IUbrd &Li=pv PYȿ^SQ> **tp0!DU$EŲ zw1-dEWQV*+#\y:r$I EdFaKMHr;Wsaƶˋȱph4._|e¦BnAD  |^sU:7. t~n ׮\/;E=n.KZ[G×g{gi MY֕ykq)ޓg='"FRHV8Sv$d -]lK5aق2`qw h؃t̓,u*//D6"a$j㓕MZǏ >'J;i f6cn?CFϾ:/.1 l?X{ߛ!Y?o0O q#@#*lq=dk6Oxy;[5Usrt]/uf{ğ/TK)b]} `LU`kS$˶4۔@H.[ @`|nL>^x jH7fi>̐δn|x:P#A!z&wtQO IDAT)M"[h"k*Q*C\*)MjZșrnj֡15HSX$8gR[[5d\|M_{Ťw5 G,شԶL=1\c3ԙTAK`ȶ_!†zeA[W "IhĚWo!@^s & -~"/fw77י)DJ٤֔?GUW?:d fMkOŻoW:M{YG5;JQ>'8Ħ"!0cX$Zn-)%0&\od6[u+K9uغ8ߢْ) ?)ReUȣOI5QܲnO%pR9Wmܙ=>̲^(>{GŖ/ MмrM4|R"iV[)iɿz3ўK^#c{]11nz-L] O,Zi>~@8 ;2+J%wtzܥFB@8s;:Zز`c( ['[fݖgKҙ.\d4}=GeQHWmGYʳ|,e-Yc\wptV!wOV[SY/^_>/ƶϫ]Ă˩o-q=(DC[;kkNn!Z-[ _sf՛_,~OfOqe3:fI$]?&9ѷLYfAB;4/Y9-Ff uJonF7[sOG-,r4 W`\0Me˧." &Eݽ@~Nb+Mga 萛3ǧ?ޯP㏾N4@+:[ q 9y;`e뛛(Xv)PKF 7su|K?dޑou%ϝpw?LG*~YI3f}\"8FPv~}@(;.T u~c.q[edqjI bUaW* ΅Y`]h)n;5|rmeŷgؖߴ&Ҽ ~ATbߚWl`'kZOL%,raOuS-@!إJ!ߴJuJ2?`n>;-RrU~vj:'&Iric:VvKs\xu65O]LU(]qDM_TfсOxBkֱR ʄD@.s֪4!q#,lB)T(2UQ;O6Tn:srūgblDi%iؒr"})\ c):yS/{=8> 0cx;; ՕQeU+#B"`uutuA[:Ko+8?[E(ra:e[Ύpk8.u?cUڮN2u}Ynmآ u-_ j+WI 2Y\|g.Ώ\m)Ôyçύ|%./{NHAHH5̦%e A5F !RVۭcH-ǣ/?&WEi{uݚ`R)(l /qV6q(:(ÚJ4S[Kyw!cU%aX**(-nٟ_f ´g&˯5 5˲lAիo@6U}wJu y1rk_.)8ϯYbCP`/3-*O&BO\'iSc߆$ T%ȊN8V @zTmNl!`.#ѕNS[nO Ω)T,cw[խ:sruئpw&j^3M;].ve(Й)Ԟ.?ggJQ6/c/_T27?-D\8JTS~ʹ!ȩIǿ,|۾Gc[SrU ]i3rngjC5$loL$ZaL˒i*Aӭ|wo9C_?{ݖ;,97jB۪*}RL [ <!1a0nδq %vIP)Jjȭl5L϶T8KeRsv6$#-}r+6g5]#")Gd&wt!ح<_o4']{5w O LCnw%~}uv!W0Jbnа%}^[}coЊ((.@$qI>=÷^kmw.Pc3 G#2r\-CxT9PiGӬ-xض=xʛ.Ә1{t>HŪ X7!RMmڎY3] WiqB^=Q|  M7sfj/EMir<(BMfM{_LE]R䲙P7ps-,jػ3~ƭbQE.M_?uq+K`~OS?G(D%uC"?_0+@$ Vdَ6s!>7jrCQ=>L\ad rug0oFk'^ :Pzᦰ>c,Wx/>Θ^Zgl5{OaƁJk{܎u[MDG"Gƌ )Uve)PU.)?:V B)W B VUQu1lb)=%~(EI\9VYzSOqf OBϟ&05ȍ_MZ(`\:pSG ?1ŸYfwEgi%; a"R,3YkZYȝ͗%9ٍQW 8^؞ ;di9!)͎3}dfK }߹W/~,<ͬxMleЙtjt"JT<`8l@EVL,%,u2ߕmVH:#l'B}1DG14#`] wR{@v&&3\7KaaQAR,U4!iNkHbSHA' FV{%V?!ۤ@QX*[3={9-JKL+V$!B+bfո)2RJZ*WSCbϾɩ,|}! :wiIUJ `ysەyhw޲T_bPm#̚w:HGcΥ\dr,>z3nomoqU.@z.Z0>@LUcn2~n7B*ضF<͐4tC$3p$wh{p[N!aG\ /*էM߳!fel[KUy-ەYɤ @cX` QU}wl:ɞ2-W/k迼hvd}jj_ &5sD2/[r !bjAC>@0d@=1jA+5/3YTjU.NWTu&l}6eJ4#~\]fD>W$sӢq$m*yзЖ[R_1Ha&ӴhBU$D *C$h,)|'NCnS3&kFn,\7YAK]|xl՚2FT^+*Cvsټ >8Y?ra CPcƞs >8Ϊ<s_ۙ7rƍ5 ۑϓgﵹ`Z8 ݒIw;7Ö=BŷD-1ݒ;>'JfpI(q1͛}DI1)8O;]d±ow ~~^v5pn3抧\"Tlehӧ:-{S.4wؿU-9>>W?+7(N5Ss@B0?zh Tڗ0r]MrQ#rZhlí_Pfٕ`C;U:V R8ߗ.WÉMru1'`QfQ+X-gV*Uh~n+/: !J!?c|t([.  L+`11B߬T@,LSYag`,j\ʹmrrn]q!`!UgP{:t&\HmgjM勹\o{-TԨ%ER>7"P}k՗WT* Vd1JH^P 갛nڞ>ᛇmީFa7h5/| VD~c!&P(G}d]\Tצvxm͋]_1*Ba^p1yZ4.߻!zzfg.V\@;R9ˡ2YRYyXZls3{TiBg2CLzA'oj_d&pGI`˶߸7|Ewko3!3bb8}ϕe^@)+/n e2m?U%ȧr}l7M:ڨiT@$X{B "ȶJ,۪5VK緭ozTl-\?B"qVv9]5˭FiXfk$ɩ9+w\bxՖ=yvAiv-1?.#Gş{&Y|4~-۲~ K!ًiq <9-~jP/,=+/G?Lr)7wjC{**j hd,(-_:2嶺9LwB,:@`N-:iYALj3 dI2Y@NesǓ8XN{1OWVlsUˮ h+%^Y \S98=o\Ϙ(T [h,u\~BԜeG+6F6oe6p~~RSQ͟vƴws;K3ۢLj W.q锒}m!8fwVd=kD`TeW`ǞIgX[!ر#BD38!1`ܿuwAU+#!cUk71ϼ%R+ӧgݒU8W1!{[AH}ųo~o.USCs[炵GaoOz(}i;F v;!.#ϫjz!$ƝGjq?tmقq1:%OՔmz33x\"cϽdM<{]?_Wv͓3y[Png-g*?qP$Y {گ` v~Lf;r\,MLdQE7K݊\ dgcĸq)ݬڸ ton̦:ĭ7w/9xxlh2ǟYU]%*\ |e\Dn.OI>ܻZ={"6l+0EŅRS΋~8ތ=jǧ@j2% [DnVτ*Ԑ[jQITMMvةl.7L!DvkLwhۖe\76оl=(1z5/;:WMpt**1["yJpX jN՛S0)*+D0e+(ٜVr 0ʂ!,:v$S EH״_|MO`L8cYkTDP^aGy`@ br5 ^o|*'صuW`%X$D[7粟|}M~%(Q xL aFӹ*iK5}WlN ;˦yv>' 1f QxݱT<8e v}XhGł_=5v$Ҝ;_3UP`$&<][&f6>|:ct WMs7&bQ{EӶUB7h"k|nG{MrtaWp+=J6bz@^N- # ܤacټ\opc?q:겊:dWn55dFf7Y O˵U^=^تQ%<*elB-oL8&Kh2{l@O3/HrBDz$Ƭ~=fiekwO<9o̵:too8YU/msGӡdrB6,96].![*TWY)}ޘG s;A0TnnDGo? viDAgI_]>`/#UG3\Si떮{<@o/F$,!, F( d 1~ګWL?1}ܟtw#1-6qB~x͙N{| > s׌,{ȑC\ȧ(τ@q.0:1__ru҉ǚxB[5켊jA҉e~(=rE$TM90YJU!A B)U\%c&0!ي-IH,E!#U|.G{`lSxv ~d% p. LQiF<敻\ [dϯ!Wlz+!i:Kٶ.U*%_}j}.\7cx?M_gM\97mQ)_p:lpۍ ƴ"J&LRU9tCDiN)zi#GZ 3nrz_K1)b4y|m4%0pq'H:k-[w'ojZ$@ ^iGs 3.ZoSJd1%dn%L;*s])E*I!id 7vg-Q߮/^xhaWooBToً.IAIGO *Hre=!)jn]%SAhcw qO8xÀ6qr۾vgmeZ%*+47D`L꯹w={Vdzu2dݫދXl-*Y*" .CߛyHpG:g:lH)h{ 6krW,%V7~To|lj&Ì Aɴ8錙ǀ8gcd~@/ny|vr4pezb1{6_"sY2jىuǯ_hS {N# W }k% ;\y`_% &)7_35hO6tySqY)v}:Q pIP R5@K2%u@(r >6gi9i,|SFMik7>XQ7`ll=/΀"rK'Ia1~8UK{oǀW3aQxݝ>cPdb9Hǥ >r6X&{-F:OSY}ZY6=k%U +x&ܜX Ɗ{B#$T= ; V I\5?:B,Npma.Z~)9|jA BQilũ e9'` m۝ekP) ZK`jV;Ux U.l O4Y_fǟ hs8e5s(^@: X??7d&锌@yK",ob[.|p%2~#L sYy] )?֞lUc8쉦b?Ebζ~懃#t@R tu9'$޻Zt@ 3E.Dـsb/ʘӬϷ󽌪"OLTx tg !e7b5cEBi"5=uls!鬨D'ӎ\v[5 γ+*i4q1#&TqalA} -b v1{A&>[>4~l}{H8ORru5H0V{' #r=\"2ت*k#3!^9iCVZYHKW~^{$FoК`\>B68'$>;>v Uc#B`1DSnd;yjڶoL 0!D j\U$Y|XLaDzǞË:0*.6. e 8i>Oy qp8N0(ƒ{~& sw%[\ F} Rds}jCa+t^y_Tτ.4i&Wdȶ[ˡʱt(N;Pv1`dm*v;I͆VAGq]WdZZjCIzXxM|U׵>Y7m>&;&?fUvJe~kύ]NLyojJ-ݳ›t<R4~tn;:rRBP L/=Ň!G:3诟cj~Qq*\.@*0TdQ@/b,{r?aOw[^V?gF@rtA(?=]SHהK ]O5:w=Ζk}_Ǿ#L3Ӊ|'HBH IDAT]"yINH" 4_S-GBi!3kT<ãJ$nTY"ĺ!r`Uaf,=j񹭶hn͎K>:B̊- SsV몭M-{QQnI^_ _폯 Zau.c4Hǩ ~^lhe\Ub<.~?S' uw3gJoc鹫ʾ@sˑ=}t'lS,MlDWg>;shPryQ[0R셄k(1^W|o%<64|-kْ̿l>?٢WI6A6fd?Vij_ m3Bxn ƈqXٞf(`JC)uìFHU~m5FzM_W޽w_7x`)@ uO7sղH`y]럜 WeXLfPw5"̑jhzuqd? WXOO/lhҬ꾦 !bʘD|*\"Ģ##Np){U z#vj6T{# #MuX_YݯTQq<_H Ͻ%a3u5A!͋2y:~ H# >pVd+t@Iv]#a%3+ s0olq붉#vas]/ͳ |.Y0+j[Km-=3՗>6[x8yr\~dM7~ idSxMp fv*!œV:il;g-Nob1n1 *T$8/T)M/E5LTcvG(nnWy3ɏVl6EKgrToՐ* (#s؅DR.tίeSI|Qwip |߰wwPZAԨ.h8!7]#] :َ^g%2W/2/nT{2`c0۩u}`gJT.SS%6GΫ'[il#a2]#i=`kݣH~Y </0+IrwUey+4Gn Ơ,@f/eI-¹OfJa6Jg!`MoYtJcIH"Czc!csF/>=\.hHBN9#$^^pB̢u,8+I^XoC'Limq)xE+G ousݼWbjBm 856!WR[98A&ֻv L* eܳdmjɪfŸprW Hy(p\u+! Hu9 0LnZȢ@bL0#Q2:'ϩc߽IJy4"g9Ecߧuh::[\7 j} 'lڝrd${j>C(-:]ul Oz@@;dV`ƝeAOܧ0FDnWg0!Dn͎,q)So- <4%uʍHB&FMm̓*m#_{\UfԷvz s[\Y@# xuOt{Zn ߽ n w;K YN<47k!VUrSp,YW|x* ǤTm(y"[\XDb ݖ67Y< !\^~U!ۄAu@hJ8$fh\40eYxoT.V4kol׾[nZ8`:9Wvfeۿ:iw~\"2֯O1\/.Dβ_?9ϫ 1t]H)_Lx >+FB/Au&JA9p~.иg 6qIR @HiqBԂf:lJQQU*͘DH[gZ>?:UX{{w`5UD$t }]AͮQ@2?#Rn؂U ܍ rHE4-6N?N7/BvTLYٕ[5dE91vy/qC3٪-taVc͖LqkLoʴ,sٹ3|{Y: y.\_%Ú40пsWe Ͷ.o=<łM9 \"Xw?=?ۙy)ho#o;d;>tXz +<\ 1HDǓW+4 . JQ&0jOMK]xaW= |N`,0q`N꾂R& Fcu| M8yq@#. 8q.GnRjk[ ЈX6U)꾖X?6Qg6}ڤÎrE>~T2kyܾ)~7i'oHO-h i*5]8_ s+z& /82VuU[Q=㫸ὧucM1^M ؉SM?;s݂ӑh l@ z^G,>sfFf8JC!JceGWf6"N;XIa J^sm3vQ0fqyqCC_Riij2BJ0;z`r(ҊՑʳa˴QBՕӟ`,7__?!$RzP?-nsS#+~-Ba)(+:c҃ pG+jsk6MAN@g:D3KA(7Dt8vBB! (qfv^'(>k]?Z{m }HP4.]c\TR *4w#m `T&fddɈ!LG hQ53yoVj/ Vږ[]a T9{?0jS\??5BA &9_ݹ7`:O.'YCWHRd@hE=y?Ez=i1R5sTz}qW*'f$}lpM^N:}CVVr3צ c8%t1q dhsA8,K?B(DJBl]kkOn0ꩆ2r5z0vuY<( ! Ctl4Qڤ<]P2T8ZRn[2H2r:`PWD4Z^?̰vl:I}uB z*/QQdA׫+--jɫ L5uyV/l ]BbQDPEEY?~z<rY/^xlN9pHR$Yv0&id iȤD^?8HH 43پa; ݕ+kɘ!.t?L#20!%Ǻp򙍻"M)&hQy&8xcw9-m[矹꨻Uewёg `_I:\Y?2/ٚ@<҂E?Pk$6z(A[Mr *2'NIYEjzxY;H^ tɫ~Pcvl?oo;~1mZ)ZZB74?#}[(w<=O8_AC$g.Ta1N˜qi&Tےxom=7r(T`ܘ:IBmS)4]hS C[aIHf(sJ6<>?k`UnܜK5uD1<}<&@SOЬ  2` 7 C.ת%!)96V  aIs8^(ջI$U#qgnjڿ˺,$K(!U?Pz2M$f>QTac0r=) t4N H2fsl"RC ع~{Yέn h$,(*8m9}>ڽXϱ_w{d S8˵{b58*Am:ի+oƂD0zf >N#dJcrt:[c!!t:x"D xqU? `[zHӆa6+եВ7mg M$~AT}}}o*e5{rz$'cʒooaDnKB_׼~.;v\  lxgwmh,5f?} EniҏZ7Ӵhҷ*:x=le1;PE,rp)-?6TZpX *VICBhr/a|;Eo0HA#:"ku'ς@D6YjlQ󴑁nOOgBPQ%5ZS\4FӷMSGyvV5kwY`Sa4,nC)nn@2r sT &Rm3'.9IvsT 5YDd,&[K;مukK"r =?8,r-()xf͂j8.7yͦ }!1Pz6 Cln؜C&t„`ͧS,<@y !i-Ic1@U5F{t8{ABHaՍ}5g/'QBRn-͟Bd\NN-OdEݼS荵$ӑX6smk0EA@T ,67b`؈ԥa4y?"qjIC~T:]{x*X Cl'%9,KI@5o椬z=HrύX3tnX>#e3 [Yboܳ\Eje/8@@{ꝀA=T5'_MЂɀhzw]kݻp?jG[qbѠl^s9z/̞`K( f-~gӯ#\:!ۉ{鴏$h&OBNÊ9]}mxBҵ4}6 m£l^&@n&4X]Uv!TQQL:vGk8@͢=uPԾ|@oMDQN|7#%+7Q8UXJ$ gㆲzoIy)`A ɾbsɓUxaۛ,)j̒/zg/ i)9JQ^jy?8aӚ/_# A B`?i ar0dTU,;:g0(o0bAFԄa_eX0w)E%W눊a% QY]4B l~vXbOΥ 4=dq !bFbθocyEt#R2r]O.pNQ P='T_Η}}X]P,H!4u)CBKky&`|nɠ@a.3U 7leU*mNww8k<{m)wOUE,C7c\;0C &wQcufFҢwse8E! }_2q >bLSyT|'DщY KF 9Nf!KJ E)ښSY~Bn gCi5da2l)~ Eq2Mӿ-HKK@IZ}zo <!?Jd`fo` Uy0Vȱg-*8(o}(d&4nfX)v,w؟5~) y.szhZ ԘZM_U%8)U%7''pTa~x#`>1 L4qY<:FQ{ ZwiCюBQg,~m̈^E'}w8ed$b,]*;?"<8t ZS6'Rwl}no̫ ~ U5zZ{B VsYۛ{ݧZQVGS/>ntJ=#b{+\ E/S&HJTXӔ^,KuLfm46;CuT~O)|-QC\AxNdeUNUkP<)lW-\~`Y WxC^RgEy *TZfxOӔBD 0$PT Y6? B 2~=%I8.YnөRxK2g2Ub"CA$kJg#E%-^@z* ^^x H^Ш`,FGbLPBCs=? uVm HtOE1Neh׏V퍊;$旘 v!$@ NyW}'/}1d]CJpӳ4 a䛛p KsǞؿCY!Xvl{~W^>{0h9?{=%ܱc^_; *e< 8HW&[\41e;[ W}u5@-?;ѳFփx"|+zݜu0zmt}8l㹙3Dۗ.|mہ!YAߠkhʹ[fa2%qJ Fp"%7 &W'23iǼ"W@BR&rN4TiL}v-L$KJ!CS: 㰚gNOD=(â|WzN (b&!I?F ~T]iRVFNx_N--H//< ܌|$fu dH@ km$x_ip:t]<6˾q'@0!g)I Mҹ@BVȔuW S²`<_7>1yTv*(t@9{ !KS,M1 S Íwj] tfIN``,f3@;x,heWQb2clB@h.wKʭRMck M:,l(robω@h8#Oʹ7xv6ľb6/+WtLm ^OjN[Grle;tjil/0(wK.%F2,H\z,Z0Zt=hi[h?E'4|"n7O.hss]fo8ϕs:@,wRɦ˜^s*?h( K3Jc9,LOn2v96yMB <fFxl1]BebE*:o(SFcx[8}g IDQ;?3˵]c"c,+j$.44fe5Ca(?kϏkx ԵnE!T5\8ybٝ.{vO#{ss\_|>!<922]Tb ٪5z}K9qx27UH?{l i~y-N;RTې֔\~& EeGo) 4{Ɋ"+H`D 2 D PPDxnXHnc-q3!@QxRnp'4m1ryj SWLSA.?j8!J.eF?"Zf89aijQ.? j[&{9]%l0H"$@̮ `UcI `8Z=,kn% Ϛj.Q0d` 0eǾƚ}4ts6%U1NS "·zI8yfyOPb@(1 !%1Eݺeo;JB˼ Slq@t1] !E~?(4`;ӲLDӚv?9k3ni1Á _߮G?(YX:EO Yвb|f^}*_iE }jфAkvwxQ\$u|2Su30MIz͠ue&ۚGf\o):q>R)5/nV]W<}pr{17N~_eiB]oH +J~zԠ'4v/xHV`_60 &H"/iABP4|e^Hy}!k!/d0T<{c!DQq"C'OKכhQY8YNU{+Ѳ)=||J7^~"pٌlG*>~`BGlxSV=;{C ^Sfk0LS#.y;m%yqֿa1i7 2M싷-療:i3W<5)qػ}vW=AFQ{PtMdX e_mhkaGm!3Ñ.?7dবo~ܳt@g^ZÛ朏8*zFaBhnp cN5|dibb4Med*"ّBHA{[fjHu(fn*aId1i2B[BXikA,eA EYܠ/n'xQ,A$EOhA DO JG[2LQfp'um{tB LNr]\.`"I5BF=6}g-i V`@AT+z+@z}g;c݈^8]BT9Nw瑮Gd˕;u_)zk@GDiu(9.} Kis 3+kqπ4yͰ& Fw aQ+f?N 8)B*J4a `1*`n3~O$ 1DD_W?/Zf s҃rE_/EK3G+%Eؼɳ^mlԥBgQ/_Cǒ\]Xr={=KFB&SMbSXl ?9ϼz E)0 @w+QD%̞N^Z LuYokom4TA;Py l)6{[qGp;䎂 #ɷuͿ)Eu0)Ē)^]r_hK'RV~wcvF @ue^4U5k#6jQZ+Ln[untllGcO >oF2vxzs\Y{i'u0++\;:X.u|+ﰷht'1| $c'&O2 mirmKflΏceGp(] 8z\nvp?tC0:?u"3RbI`3U" EY E3J?r+" tJ$qUDU9d 'jfq@h6i1{[ Nk^v}͒@Cj)Ij } JgW1 e.^SNSU_~-gmK n+}R(YFbrD>#9Or@~J.jB9@]ræqUO( H}rB8Gh'x B@SO TJ, c.Y6iT?`*TOp\[HW7s.t~~p_S=/2Q%D#ESWu~-r.VK^ʒ %ds:*ҁfޑĘNE~P[Y9.%~)9E06S~~O=6q}jЖۢG=v=Z'2%ultgMs+ U^ʼn kj[G6I.>v.G3.EY}ztUq-D*?DZiH-D/q*-^j՞nhQ&_c?#w,~):CKnrm3RZ6^F%R rv&J =&%F@QT8 EIQD#T9qLK\lٯZf#|"XÄT*E *QUpH)d5-~J^^_f_1@Yc)nẇGxaaPMWa7TWCB 6ҝ=}!\J k!eLvOEGT/_$Z%78IJ=zl2u3'@N䶆M}2~lԛJ2X SvzBAw.&=:xmP->% pO׊|R:< uU72E K?pS'/ V/={o,hyHݥCD!e5r\]W264{ $fWudiKU;.\s;E{vzkjZ_e7gElC DŽj%%~=/JTTuZظ Cze[<5fJ;?E9͓F4g9f1w(ɍj{cD2s4u'īur}kđr{7m2hzYe3R$dDŢ|JQ m+P_.?uN[u)tϟ}Rg~] L@$/VS/b1*4^gMV:L=1z͖}o/ԅ3B*Qa{ e}~>S4e wK0yv\& k}>;/.;mqKŲ.|6adm*L.z˲1I9a7sho횲ܽ,PAg_9Fp˕rڊ  ia$%^Zn?%EZU#_/ዀ:./4q(dؾe( i% 5z<}5enyh糡 <8)XMݯ QrMsz_(?_l_DR4X1B:]u#[]Y,wtKǰ̀eJfbQ:ĤrAaEE3^ G%3}:_AѸy(5M85;hѶd*$%hhV/i5.ef7$@%acxux 0485ۙx.qpvUO q\^>6*'} 9DtLG*Y%;f7o->"By.aßMU۳(j :Ssy!"HRP!+. /\a.[\|>V2:d1@BpV7Iy?=g8Qy?= =-<}wz?!Ѩx~;tľ"V,hB`a>榏^r +JuU@"tދػ88Xf{H)"5){DAއ{s{Z^o,q3Z~4^/ Zi3H]I|$0p cDi3Gy>\\:f"7-{ s5_0ahKO*Mt JW毉\T"wyT94ne H8ouF@WW2sq-j@(N%R1)`Xvroyt1"4`I"{KpVڗ-mw_ iPPdyDZEk(PˠGxYSԅ8٭ _Gǵ,_q"dqF<遶ve{}i{PoBnrxp"~}(IOqı4-q, E߳/⦺vp<.}|kXbR0xv\2m&#?=#VmB^)m=VT@|j^VTT `ݴg5M&71UF9ꨨ{y܈s#p[m.<pkchr@JsaY&9*ӺŤ궥v?uIGV#Y,nE>I5bߵo7+Mc|$ʳ )jxYH4vF3!U;c$#&m٫ S\<JFh|4YЭChnjQw4cj(dX٭Jem`6 _SC #ӟGXRb+y7^BtӪͨfd~o$dQF`%@1[{.E*K0B/TΌGkZIaYM#8?"Olbaˎb//,8ocƍ/Sg c㓒0$G̈$eit$ l.mK0BJy9Y/>}oRU(eFww* p}=}aC m?YDdEwqYKIJx.$;u0`)a!@0Np{34}1SffWiƦ&7yPKaBoAwy})o]kZ}kAo-jJȨuwa8_"bC f:[ya5_w v}qQqI1bʋVm)o/|?}7A^gsxHIn}gi16G'Ir5Ɯ3Z޾{\MD*a]MFZ!dۿ rSzl_uثg5o)|~fqvz}蟵Juy7{df2L,aٙؠ'ђhhT;r^D]1F(\*'FX' F؞CJ|2PMոRRGO>]4𦽮7?Sj\H2QXQ|NOuKeNVHTΞw,xwo:2Rƌ_=礢|lB_?5U߹ 9мfiuU5!f4M`%'˘R]1 Xl OI/ }Cqqn0B(Iun.^.8=J&wBj{RjWxM^yP>m6J{u&qq6VP de}a5~ջ'F98cn_U6Kz~1^9rMoQ:MJ$1Gle8Q׮e33eXο~ߢdI%~̍`"TB= j.氉k?'];о 25;-ȯv[?T$OW7@[2B6ZGi-$YCIQaR+]کE!zhRe@i=u*eCU44IUXwt]_^T.aߗq2vϧEڀ{;!XӸPD.`$1s^%d); O7]aI$ѸT.M awT1IRB$jFɲcF Mkua tB ÆSd`,P̦zoO GMhqME*@d{^+(;=,D4n9m¯CVrM0M"1'jW0Dj۷^ךJ5e1y!'yCfDhs1XpˈrlbL>8"1Әe]bA6 CzƓ߷ؽYÜɨI:9SVo (ۮ 틇4, _!wO5.aQ]Wq@o޽ v0s[QuLµ`~č;OXC]qJ.PiNN *+`6~T+H(K6;_}%^}.;!^0/,2>9PV T$ԘÁN\ :Jm*i*xp†_?Y2눾rXowX[̨`/v7is\<ѐ-xԨ0785iXQ߱U>Xۜ {G V^r߀ ;'B0.9ٱ -7h}>nR2$ެXT5ХUm\irS{Us(ofN  w(a0Y ˺=b"t|=aU. duzzMI(5G2@s2~+1c0/@=E-Q)jq#s]{}7|ͲY +9'`u3?'kq.ZVC1 l_hy$:IQӤ̔ 5 Pb u:o7ʪuۓM2BVDubL(̽d ݛ⍎z]`ǧ[E5/\eI=G}tL-4B)enDR!z"RZc͹4!t{_+(I=IAD|}r7B HexR>ݞ3дyyͶبQlWXq'pyus/5Gm>&eFfCW'.r˖Mo@XuY_O˲ո}^rb{]~#LEA]$&$ja0:1ΌUQɰp#jCcUu al<:noQ;yI /ˈ;p6Dސu"Z1 EIܭ#0cLQQ4:TR LC=,MKwO_`EǦY>[<+j{D& 0x^ͳ^%}t,^ 8E G&VJwY;|~ ^V#awZ\~LKXsU-YNHwQ> `mK}!^Q44bi&;,,0qCfO@ S5L8WC_M=ŠDŽ}@~wsWHѸyzJ=~T{n2=qmv8it(8'P*`/L{x͹eBB"IBp ?| 0!:dg49?rB$$j0  n3 9owomO>>7EM_J$~}}ʰ^ o矿?]MwC[QuEbL{bG,`&ō$(nэdgDN R|CN|Jgt&JkhrP}![X k~ΠqBal%9x:Y|\!`J7>Eu7 ceۆIs#1]L|8'KP(R7xZ-;:lq Orv%4Mw 7sL&]ګ]8ErN#I:~菺K/ܜ ٷyv9UHxp_n>Éhq<6ȷ)0ƀ0 $e'edxI69Q,w J]X"v Ei,Ae)#(os36QW U!\DBg ᘠ:ɴ ,\REBTa` -I Lcq!;=T\jK&9)`X`{bLw>yz&K $@N8iX!JY MȌR ][G~,08${GΘU$5>x0gx7$Fbl}E5BQaFT2BC_Ym_oZ$sOO#gv>=VxF:SK=5HØܦmɶَMBE]Zy/[|9kXD2{m ?[V4BRI۟y͞D"cwv "q^V8PvZYkk`j;xUͭmڥ%OQ?wJS 7W^?9+>os0سa.ǑJgs3'K9ڀֳ#d 0B<g5ށz!+-5ZUH&LVF!J̵RlhNǽD_Crepi*XA)/Q1ф,ZRN?#=Hm ,W^ ng I8&dī-ęՄ!g+qS[j9&;v6OaNjx$04/g($u| R&:As|$ys`obqd5V)cr63`Deſ`}P߬Uc/nĭ ]v8KzƤ``޲J%HR-|0AUXR.>!Җ \.(j`<NRly=pM2>B iSg:=Ndqeߞ5?\ U'^6bhptlO sȿ~bv]HCō?նGϦ)C:ƺܑQRexS &x ִlg挒*1!d֭^k~`(!weWސ^' _,esV潹6W1 0ULQp$n` H'b|}vBNfj'qpBij~-I([1ұڣO= 1x,kHS RyF)P4Ɠ7 >x$j2Qw F|ASYBnFX1ɮgGkM=[aNN]N9_#LJs$N*3L՘1UcI9Lט2XR o2G'_h>98: rCܡSV s1a>Î8T F`lsvZQpˊy٦eOtqs{ءpB0M;O>Y̝)mGk~>/8 E71m]M%pcpcn~}$sOMg_h"a5ipc&e=VAMZ sU1U'~uoWW?\` 3;?[ɦ4hx}Βiq Bk:ocuzu8P!!aGe]3XPD٤9u[*oo(H=X pn1U\y#&]ĘbypSB&Mn:ƼՓLO&FĜtF\:nU?> I 4eB=#9$pHAfi1DIX&=hW ݉ rՒ2M5Uqiereҿ>Vs5n>qٻg}! +$I/(`OYZ>Єf9$!tl$63kЈSF˫%'g䛵*yl 9Q9UqE6}y4)3B4aIf#]ݵ4.Q`yִ[wUx5J*!j$3eH(Dʊ:ǔaOV|;фZU%vMeHaż?TKQ8²bZ&:fi񩣌 5,Y!ШO^4fjC1FΖ%LF, sX--H$oZH$&gD,L#YV fr5uZaWZ o`$//faB0TEivDmjFMݏO9\`F)ֻcd٨_ys^0:=G37pvln%l+ ] QZ?95DE;w5g'o_aM0E[jLޠF i 3 h$qhʐ:5_w}2a52ekFB7x|U+4}/P\b~xUO+5a7o֠^__Z;\M}$sBA'Wl¾28-kR9qNҨhJU"Mc5H)?8Ib3@H0Ba1/3QYp[fm 6e 1Ϋ?9*WdAHe5ؠKj`'`5M 2TV<0g LYή/;9Bh*yRmf^crvUT}-㙪ufk+'pT Xb$?{r̉u*juO0}F6|5(^4´K@Q*#D'J׈irUf2Mnyw/;E٧1sڴX-0}qZGV/?E٧+Eӟ2L(87977'?~0w[~΢ɛ^ZH4[OPG|mjW\1g1 Qw*|J!;%Ir*σc/-9Yi~ghNJM,F=q-g+RZv] IDATv g3kiQœvHF*#SG?:Sn>NVh,瓅yE8R%eòу K6H8kiYAP> 0&Z6fcgTha2Z&8n]4>o"1вW MXMmwQ\Չ[_yݹ'7v4"t[;n7o_#}bo לi/vjeޖ9Hl[fh'S&M}(f/)uhɴ36cV'9ede#YI-ƦM/?Rj2~$ {h999fa6&. 5FW35#yPYu/ xR <ˣcIi1&T>yL՘۪G=ڡX6f@ >*'5_HpF*q!fyu^RQ>K*(e4R@aUJ@ʪqcg`HhGw'[irqx?_V*jG$?h#;Fw<p׭z^|oiB\fbARuQYKyfx"رhkGlqg5vx= PT& N|}bb@-H4^a:>ѓEb(5Xq"dUwTCSFN@H}(CI/zt!i2Pzw.9=(͖'+ cO.sefi֛HvCI~}bu5OP1Jܺ1wŤ"Y ~t?LͭPh ᬳ״m/hCl,i5=P}~Vrnqi_sM&&٧+:n=OUֵ,}k4eϟQVcq3-̞@ҋQf8\ڻK0/0oñ-9vj}ےmD࿞8u`$Ҭ; Dob}t.ʂ&l+o4]Д1S!ZS^1 G=έ__>4QLMȨEu:IHweD_l6nl?z\QT8-N uS;˟<<^t.nUըF4_n^Ϟc0Y!Z:y(\Z:>(EKL.F Σٝ/X\MWw/|N6x5 o[,~PN:!pE.ٽ殯ѡ/1O%:q*/Tx_\" nk5| c,˯Z$KtHfc/uQfcQ"Uu5{b$dgd<=3kOcG(K6"@ѩaH+ Y2~zs՜FgoFw@8? ƍFQm;)j֊{aƄ#ŜQQlQfxi8bR ѝ8e3 Baky{6>6Wo_bpu%Ցt[ǭ:pU5|0/c+n~2Kgo=q~c2J#+jG<:whsX^}*WxmPxtݗo_הE>߷̾lpswuiOTs_y^})>S!Œ>s{D@sn(zY4 4|7c<] iskz `˔a\2diU͎][Jbrί37o_$!ұZ<秏R0Va\u~`N7=zܣ# <]f1IMݽ,h9'm7v=+://$Hzg’`y`\P"6h3q5Խ0#.7v*(ϖߘR;>]4B/>IREBHϥ ob"ԱqGp(Љ8 1R_1Xgb" X2*ڪ(){m܍݃4Ĉ}I ./%/|y $Xpw6¸kw?zffa DXYus>F(3>k ?1WkMn=}8cEE^ͫhuB Hu^:ZЀh {%>;k7]dٸli'GT.*>7UGcUk{=9ĉ KG} ݍ}`,^|)ٓorJ8zύ{Edn?DPXOd`- "lZɫ^Zp)_bWtK%dLwZ:o+whntqLM7 a;~Ѥr{>;@|\0pԲ_0|V൪O=how]Ւ^VvX/mwJ텔pKG͉( BHt4kqp:*Խg)u`I ,#Ʈڇ !TX @ "_BbkLFdoԨz@!MQ&k(R=O ϐ>,ͦ0' N"&U[IQP8`hR'Zg#Vj߉ˈ M7M0d7X |^0+(_34Ʉ5D@AWi5V]Qi $Qne,LlLN n֝ I,}{12j0s4Ӗ|>N9R :g1b,C)r-\dw#?̸e]IAe$T[K35z>mT(b0=Mױ2A6WZ^UM???E3LȐ//pE{ ".f7ґc;k'x)dpG{p?+м PP;xy;&l-U;awlĎwrS@P8{]'.f_hi+D)|VLvGӊw. Xi8<]2KL4dЊ¶'Rrبdu>hF甐dM7W‹*A uϩx{ӡ!9E H"oXC(QEk¬?4pNtҝ8hYcQM?nP K ERFWǨ1=8}Y4 5 *&)Ʊ5uӈ6Pq0i8YeFj%#K/;"MBӬ pW# &K*˵ a}SI"kv@A$_|4hI qVCO F3?`]q|O17Ƙ/rڛ[N82  Q*yh ,.g*%c/,Iteٹ~ip#+66}Dbhj Ih/.Z aҢk%P* ƢCe eCu m5MyMy:do6|o6xZ |.)#{<>` CL@kCK|Ӧ 5g/r:-ZIH2&{r-*+XT00VZaF@U[‡z@濯?zQfCK *!n>]~j[ġԂZu~؆L]Z(jm!0SJDAy~j@0`=57E_zK[kcx[6ͭR}>huQ!ohR>]26/Fhq{IԨmCJSez*9 /9!x$p+kIZ+*tӲ%=oV(lt6ǦXRzo>G̰Huezo=`9cˎ!6.=/:")>O<>ecWъWܿKϭG)QS\-ՖKtd6.[Є\3!_=Ϳ&5wtJߨKiLsƻv3LBID/Fr\3s"̇*Ofd_߻bf2L9쩫bjhq ,eDx|h(Z`H4SW}:62Kܣ/HVlg$i]j6PnUO2es)Zsc$* z 4[I]Ȉx~>aj+ŸH~M۟F"/z8 p #cM\v>IEuHAP\#&hio}k2M}mQ+$>ܲË+rgu$DWѮ{9m6c>Һ-^M5 Q W{ìρ"W]-/ А9k!sRq*dDݰ~bYy b=}[ነWs(6,,A?SA篁Hb S,_/PBH q!ޝ†߾ёR)s,6C&ʜ'&dQ5cd:LV}N]1E .(vX_Zn3 . ㇸs{ IDATNP]'$ @0ww08+RA=^sdlPn}Ω8;@0tڷ6#,C̢ }Xem$-%};|inhI4ux~GV[DN]ٵ3!2>=?Z3'&|ۋUSG#`%6 ,u)h)NrF N}iޛsӋ&i0!#ZoV";:1q#}ҹqU^?%I3 vy=S`i`F U޳W،wLx^1%LfWL31km)q#X ZQ^c*wbRڲSjZ3.8ybXy1CD\=wWn^)#Lǚ҆1])(3 8XI. sԸ|sV^E~6y\_Q[LAFm&%ꢊ+)d_$9U!8^CEV#BBխCf. BQE4L #uvKRj)V"`#c/ !+ Xl+|.f֨p.`ŐetrVIC&n_D+hD?W6:dGBT'Zpm" +Bj SFעUUvow允ݰo6u8|^7=5IAlg|5*ʮY==#6CW8?olƄn/0nd&uwXi9t /ݤVW#<`^3^܂%};_%SV//ǿd"7ܤ\9~b'C˪# +c3 =Eul|[R_1vhҎ~?;R^?}7LjߓǃIjLraw:ܰs80!+.W@K>ʏ_0Yc%⢘REz]3DKZe! M*c CÃEk*VZ(Zuƈi[5vHb1B}~r.r.,x~^?j%%.|KˋloJ-_L"[QDO 7'J4dD8܄j2DyTbyjjt.0qBIhqalGkE]m2ni7HÑ$Auj/9= I41F)x.Mm}`!' *ŸIcq|K>nY7vCD| 3nc$Xv V ?w#UI>zj"*~{z׾,uԲ_JtLvz"sd4JOD٣ +8<4Cj͏fG.To{iwy.$$EIgu>yCz|0m\71acOMffjnGBZ`р}Л>^hѿ!B^s/܃ثҽNn»M[F+"Ik"ױ 3$Qb˴t.L!I"%.ldU|PppߑrC8s;\p_w:-l$2¢/LLog`f2c2 >w2p1S~OZ1S|ޓRN Nf^х b@@JG7}/ _ p>#Xm06Ͱ +aH("%ABL!ល~Plm.EI7Uò,ۜPX4tgㆨa[iG" v#h A]0FJ%3Q:xZj(ZqvJH.xeE d9}J7u4-psƥw&:Ę Z+lF}gv b|u˘x 0&dMb2x|AA'+&\%%䰒/iwJwrh oxN^JLq w{={܈ϯЪ[kn堡 SsR%HϑxQ>~T+%[knFL\dȼe#|.f;Ŀi݀$3I1lN7I<}-ЪӅ]^,cTxA6}*ˎڇ,$zr;8!{|wۛDI(6 w;=02$]/^?Dy?DJ6n2^ УB24 4PI@ABFdQƍK۸\Rf }.x9t|!ME<;O >Q.Qrxd_ӧ@m0qjT e_ n2rd9#U[C8F7mJ6^O={AADE<@`y2~KDG[EInJc5 $/h6+j$gF8"sJ揞Ors[R0#K=w7IIna,֠ WKk`KvxmB9&I1c2wʹ.δ!N)0c`L!0,eiݝ1a0Ttox)Dt= {+s;8K:|.wÁmWRy|]w /8p|mNoDoO,ՄW&?n=ƭW Eͭ1NC4, DC 9ܢRc3hA-b38Ҩum=9tV4#b)0+BʞϠX8B]1yml$2ƲIÔ)q(ʠUvlhc {5{5G[k&TYRuM$A@0vz>dX}_XFbU4#, pżI#{jE@ c EC"k HZ3*M՞N$I1tt%lIWM {|:UY.( +;yY?npw#ZƆw{ . s4A_B,}10w2`aNSW%{A]#eW7M~1{ΪhZ% k֢x3r3 ^ham DCz_ˋָ0i10cW*K tM˖puXIUCjeu/wGhB [>|ṅr˚`IuQ8ۮC{%]b=i.{O?zIG͘8 p„f]$*̉sGcư|=%5>j^yn> "wx;i=o>kQW\œ>yhq.z*@Ȝ4-RFS^U,aɖ.n(KRmyublYB"/6ḛ!Lӆ3q<{OZ;()&6't(iNV[Ji@/{|Aȉ<Ӭ (:]bC=:YCg|gkM% \/y#dzNϫQhR5eolÜz`\UV閝&>W٨  ڢ|B0 +$NN /ln"KD|nܱ >^N iJ2;Ǣ(װr#.JV%,6qãّ8($8nCmx<2*VBb}SBbK*pYFHԻ _֨/W( :2٭.+aLk=f^-4en}6#`arA}7 ۇ75ڞB kT?yak#:szMiN^[YPg.)+G Dm ZZ}1pSeؓcn8SA&CQXeG/RX>93,1 %Ypw:t>s3F&\QBuV]+6bGNVٽ] gg[_R0Yc<Rap=w:x.ږ9>ݘl5*). yJ Pmq$dɬ ʈwxJ56$IV>)mx;8.}=BqqdB&%tIX0IdeC_ ]k9HFvKMGiu.y$]a3̿a`,]|Aj.<~y=q4|i>U;O /V1/O_5=сg3 SF`AvjJ Ί@a0PPKǏ4/#\$_5~εa`J`E@sry%g W;R1u IT*U+_B# dKD0FS'tv: clÐ~ˈ軟k f<|[Jܸ )3 7cNN^S!ӫ /e jWX;&8@ 5r)Ò.d_/aVZ;v8o ն[}ĉ']&.2;7BC& /t#)AM8tnƄ*Ƕt6WiMvzxz߫us>|>iH֘]nT pPmpթs9c"z8&*Vm|l7R ح?[sxOw,rh*r{AB 2$AgC6= b\0L )ҽ˵(c!}ˇL̶CWG99^scTD]iU0 J0F Zp#^ uӜeՁKeGcAĒ\۰41Wb;Uċ)vEVb0X~!<~SZW^H }s1fƆX-N)z g?×8zMs(!FB0+3<~~b)b2 `\1l.&12t_Zruo?mr-_REj5P.~eUI5U !(:\u"xoȑ Bn8vHp#e o~5UOǫiCTifFg&]08&L:_~yv*4 =hT ZT^sG ,q^^8/5zjM'2tЊȐhV!%y@P[_|B ׽!BL@KϏ(ڒiH!cuۋG~SF~&2 p9#Q~4seKAuU]5?4O--kg{)]Rxe;e+.}ZpSG{H -kf?EGNZǏhA W@4iB?q(ft9iiB!aMfoDY-& CSĥڨ'*X]Zڣ|E R$)B 4*Ҡ KhUTDhpBiUu}:VjUn00/[9# fO`NCN~A61o=Q'.;=kAltuL8˵?B 1Rdh!5Ri%AtT\p#D$Wb'i5d`*HGBbǔ|C"x[4uCh~:B 4'k pB?/M0]S,LqFxq:s]uZlOí#T+IF JPPR+k*q<,H(IƲet#ڨ$h=dl2N ̆=y(!+&DOaVmAyӜ`$x| T.d"N/{ lz.$IHSJAhԤQޏԪlw^uAAVc5|p{QukT[i#Ԇ=XSGFC1ƨL}%3GQlNt4Þ%Vwysm9\<ąT`c! ES9Ƣ$U|P0B%s0~mFHI {|FI WAZlDΊQ(,D{9bYU?zţcbC* QM1YؓIM{KvwLBc3 [Y zf?my)Zgq jEf-Y15*AMO_"@='6W֘#>[5TF@skכʙb۶u%Ղ_:0n?1֟pz[qQOVA !j% 14E4dhfjĂ 8s$ K2.eٹqȜ Nf)cOѼ4+J,ʤ$K\vQK :%b$dhB jҨӍi0c@b"_qD,@F;=/W<^>p=DV)v<C0- J5/[T` "]8gөPsnxR^-1T魵S c}mٴ$VסZ^ -&)TX 7BPt[A[Zd9z&lDžE f.y˫m B1I6Ue6MLUOLВ; n~i,`HL:ODz{c9*|lY!dҳw1t5/}nkyYWlx}8n-NLVzL9$BBBu?[ώU~ʴb}O,:3_l㲥{_x˷~ rg6`ծ xP>!sO=|zW:wXT"`n͒Gx惊6՜h|o y@I$$&TJbPuI MT0dM'\H(bAd3 c^E `{Kx݅:] IDATUzuQm?Dx}ʝNP䗊KyrGɖ׵&12ry|bWkY"!M*US!NMŁ|LE>q<k/mY mְEi/2S{ OaF>_!DC1H?)-J7(1Ƙ*XcL ұr%}Adp3(Fm?”We% k uC@]Urduf.9X -UL{%@$oF@8tӕ j;h`Mg՝!b`6MryASy֒ewmY>>o"<!jTtHσNN,D%sل(t- + }ҰF2רڜh{ " DC*Us:3." )7+ԓWN2O "!ɄV§-5?*NQ;=.;cOHi̗>c6=G~l/M|^.;[`ݹ(kJG^bI!?e&YuPÅ p>zoPIR)Pa%``^B96dZ“e  /(1c K}#U[#B!ܘ!ڏ~Ĝ ʕ[JIFL56Lb4_X~&Vcyf;@u( 24RZ5fR$?+_Ȕ> N%Ԉ$Crb+.m# (aj!'+_{"p1+p9FsP^c[*qv^)M(͝cc;{.uU߮]j+r=wg Z$FO*Aq$Ȫb~݉18ț ofC"`7g*$^bhKfHIZSYn'ktV7 !)M=? VBiaC9f﵆oZVza^VL;#_"`{mQ#.2,Gs /HY%:1$%*AwLxnN,'LVyLx%Be"ȃ/vuǤ3 ?;Uλ0g X${!a "rEVjlHhW4jĭQó0&>ov2wBx2l 0#j`GP@Cu4d̈́pa^D0#Ǽm=cdv)cؙkZ{`zh_r(ckDXߢ;|"nUe ,Jd78.f  UJR"B4 3Tcl#g1;~I?j` 4@+jWci5ҧ?^ޫSMK9cg\?D7ף R8TfvG`]S{o>%^BT0g`sj{$+w+YRSeNU|B!Ɍ(5-Yl%}=$  PА!ZӴP9>[%!iAMv vg^+UYEDr^cn챎U;:V(".X Y [$P+7HG?'JIN RESHz|X^Rhly7f Y0o1|r˝7lOh^Kba>J~a!MA48^IMA p䶿 *}?" /'} ׽ CFlѨ YJo拟*ZU206Czߕuh^kO8E#E $H"eZR0a ZANfhZapG FZ ,ǟr6c4Բ5[>x~iZi9-L#!*B3!JguZ a+:}}w]ʮmeY}߹ftoQ^*o>X*F1Eƣl#m ,+{|tDPisw5! 0.U=>C1} ~7-":Nc #f3o@Oϕ>[%!d>*@Q.M`hlLF]J0i‚ewų߮&l0fau`Qcaaz֓o|ڑԨ",0/[X޷sTUQ9]cT 5d:@,~DzsB+;Ts'>YrvhtM_~ yRNa1Bh9P^}@͕̃/C.$|?x3^-n,w7"dO-Xi4 Q\M9㌏ J_gT&\HRb(QIi}^㊍ŕmNk.gf)^$Eы5ۺ/ !ID-єLSd"{dҽoeVXc2ƯZ1wj]J8+@Rpa -Y쵹˺' j9:p-4wa!IJ]Flt%6I{! U72.c#,*1F{#9^2h!';*-r{L|V6;n L!BKR]Z<_[Ҹ\v.mbM]BHQ9^p)dVg5}^X5ݥ~B AlAL=o>y ƫRgv8"%%*-_}h\a%}:l gѕQ1M|w%-nD4ΫS\ÑS^Y*$x!MF-39{z3F 4*~Y᣽iO9Ey4R#U[5/=,Vq|~_XVۻ9!0id_7즆Ѿbk?0_Ȁ,ԜOF1[N7q2y:Jgy".$=>fGl+j% TS|a8y^\ۻ7(=9qv .\0S@ P)7p3y˓ZlKkSKOA] ƍ!_x 3 5Aګ !letoK&cw &H caKڢo|m/@$-U0}&Vcyۧv@ YMP)9:`gPP}mN]:zD 0X${wtBsQ䞩/6{0k-{Ey jF#*ϰ *̊ F[^ P+))fƚ#f曦d*w.rQRএSۡz:YMްt X'5N=`01*}F|/1Ϛ?^"o7tRȂR`aN EkwoDDbVJ6F 3[o,ާA7(7niR~n(5i ` V;)+˞}'Ęf R$dj+:9qRZF*0Qa71D$0[[jL]SG(S?Do jedޝ "0O:dWnrQj=xsYls[[)j BLQT N'ZwTPo+SW5^|_F(,ӴbIW;;{c3^Qc  EXaVӉS=ϛPHGs@o2z/A!cٶxԤ5ЍFH1 1A}Gã*Of3 A!I.:u3FQX"njLH/ab!Cc68B٘(Vpń{r%g9Fb’gu@YޣݵCgwdb銍5ݜ9 Բ`9keb1IXM 详 -Qmw|,i boXI ,;&VPW=~Od{UjPaĔ5ókvY?lbkã>6n `JR! $ܭ@Җ޶BR-Cq9?& !F3Ofl93=kw밸Uq'!@cI9$ iUa4&`ŒΙ%k7ENV0DloOSC艹a$P=5X׫n j"R $SGzƋ<&HVn6{ ي_-4 ֐`7!i6PX˨J3&Ro }p}MW5K@ $k&qZO;[#=Zb`s`4 ЛD {,>s4RjOZlƗ;!VwgV>nbrMeތܵ}x~C^9$q`~tLb?]Ѹ~qA˥ @nWշɲoޘ0{^0{QQHu^Yj菱W]7$KTK1҃1C1_#$yJJx9n[)]W$HQX^>_`}Xgmq\O2̚v̰|[]a4P* qi.F;O\PD$K $v&FK;.f z%iu?FOz#jU\_2׺aNlDf$ ~s2[ _3s V^r8Bp"_~kmWB5JL e3bG#UY !Y1x3Thz[ ӯد|S/?C8+n+xnfyAl *}@w9E5Dc-^/7>yw2P#sa潈fl4l=ӌ$@x:Pz5g|NǔDI#ɜBJofޖ$E\n[,h깓}SHB& SbKִ qq][瑊Ym{˕R(8Vwҩنc-^}U{Q%:f E"Q3'1*rBT'hg&xfWi1Xzտ. ܰCZoCn\Vt2,@㼀,Q͂[YqCiHCc,)O%Xv-O]ߘj Qԁs/jHV":ָfoDڸ%GЀ3 ZBqzF662x(e1=aA{t_&:0` 8zP@F^G9E*ك]*1lK69S3Gm>8/~XrmϠ׳=>02 ctL9(MYuL3gtwkɥ0U\_6Ĵa7oף=X?n=A}^ǯY~X3N;L-=DŽ&A!T/}#s3έ{ㆱ.ڼuGK\[àXD5 P +vnh&xh;55QC_6y q pd!>nkӡi_gd6r/o &o-^ 02Y_ ^(zx8&T9árW7gBN)j i=-}_l\ofڵh0c@q0d}ڤBC& ƷO1 thէg\Qg-zdZ.9{4|8e :==ޥQh c>_*5_8YZ+_i7٭܈n )(kt)*K|4->{i47'z5CZҦy߁?lFP-RT;sE'_q3'-jE*z tJ2{IЪ5#cj5H7Ylլ!iʝ;|ߛJcgmM/MEم97ROih%yiZח =eX!|VM-÷sklt c0)ذb(/y C^LfEp{Y\GmP:L!`[GD$ՒvGJIWPZAn;}*rYл@GYӆOGg-<96q9&?R IDAT<{J,GhzNQP)'H' sޙzD36Zt?-PfN9,5<(ܪ4(VDߪ;m4{XDGϺ~Z\9ץ;췭LB%qo23Zӏq bgJaK ?Uפ@mykd]2f0WTDc!DfK{cYYd^۟DLXuN+V^O+mqH7 oe\% ˷ zf}'y2oa%/FM0,bvH T!c̀sWCJ@A -5[h4'q@ISF'&W-W,mVoЪSz}7C BNL"Sm?hu~l(ef8'<㜈t䦦7vis9"gíYp -ju'co$EBx>p)̎lۿF^SDǃAo5Ski|h}P/3݁$m`F'A  cbMe}].Xau`9ұnɽi70-3!Q1}ǰbk6oݯaR82 ڙ ݩ~avH'Խo ȓ'xh}K˵ Ǟ `H.-/u΃@$4Vmc(wķ26%#s'BhdxDswk4o | ݋otm݉IŘDTq\cEAr8OE6;eUuQ OQE V{UBzCIH5>10)"s٧g}GM iS  ^lezwe.:;ux¥Cg5D -!~:d:7ye׿&2z3a]n^׶Źnn\3(NΞgD.<`&,f7FwL=,5 ]]w {,_)"~\JfHgSiV|M傄!fK ;% j@[lcK Y+#j)~ _8dN3x?{{^effoGK5 dצ6dzoMm1Zws;$tORdcd( C-\mmS,D@0%KtR ,1buN󀣉m{!1VD4 cX!\S,rq{m8>oT_}1g/-j"׋7}~۞ѻ%/ὥ/-Qdg0esAm*PcP/Jr=q8NҎGri>KV!V!:3O3w 3EE @KzWhAxA@wӎw1%#nּ>ޓDEh[w?rGp{@c?!ܼ?]̈g`Sc>/w!BW M]ny]^Xyb @\pM_Kx'< V/m.P+1),e49M/Ae:j4>j+$%RkVW|sHKwk՚Bl_SF`ydtaK;)2/ХQ|ƫkwwWڇ]JD! Q@"gb{o gߌa OEԙ(70m#Co ́WL]72Cr-H{vJ%x) kq]~ǞUykyWΪ#W_}KJJFKؓq5?gHϋ<fHoR"Eڒ4ߏiA1ę%-3MLq[_ ۴:Y0C | )D~1ưME&"p1V1B\ESkg\˶1e!BH.'I^s=uHr];nEӦq@,*g{T\9aɒ8_׼It6 ß׳c Z pa]K͉嘸"x!Ġ|EP! ;/^GC %+{e [ RY?boV(e>oTc/ k47/rAs!o NjG;eGTDQxq8%Cn8Eg}{Z_E^Sg!1!O\jjٻSsR{q < .;92'/5Fq䭱PQ+wm>5y8Jѿ c'uL;.8,8~Yɉ&a=+]f#]bj1!"!$ȣӎ(ߘjr0 ~j@MRy~\ &w8Aj_T+R QyV׍'(rm%}9v8;&Z'gsr< k0kdәpJG7K筎z8qa;2ӟpdb6V$ =w/|'aSF.ުÐ!˗'A cVq~ײT0yc+bsWBhJ֘BF=-H B¬2h0CzxL=@2 l ND?nF&&3R$%8m]Th4}iᚭxugC\-5unReFt!V":>'SIoHqŜV}1/[\|gŔWumX/K?'"5NfXW8L B}H%^Mⵘ/Vh@ʔo<~iTE-ݲ.i@#~ry ?I|=EK7h8rz5b̩GE;O>%HJ=?kI=Rx]=ɘ5[^C,g޸wzBMv% <!+,9#GU 8պ8bXr11x@W) !;O_ܼ7X`SF (9ly! |x(%u /< To W Ъ9@jJʰm)M`o[ pCc!YAAA,Cdb#Ѓ ޖB8,@5Ҁy}33 CZ?R\D~g|˖;ʔj 9)SAIzj)sĝJ"0!Gu O6|g8闶v'bD,᫾>9OqMCIL<?;Īr"Cċ)Ǯmァ_>5koh: ?˼GcdąK ttMZƊo?Sjq#vKvK'$EeOCy=h0߬jt2JDTh:~rYRp:SBOl׼ܪ4hef!,) #W^pVwdRQ$j %(7`IH"߇z6YE }=Cxh18q!A' =Yv|PgN>nqk%yEDqYy2(_!BdA ѳhU[*(O^FB@ C_y ҌүV25\xb UA!aZ JJJxBI%' VNA͡|"\t;9BiD(,4ou<`h1.2f!M:SDaȓLYf N (Z0a'@ bcBe_+K87e5دTmvQ农c}L^'2Qʎ>z@F+M e t/:)7XbU5Vsrv@7$^ށեV^wཉvm&tݴg9]dSf+ }t!ָZ}Ӫ뀧<|tUgHor]21&8 @-.$' 5,Z ay;|V ug;8ŵjFܸ͒]Gۼ6U0޼)# TWv &n݇Ͳצ~4lZ`y~գNyyю N@$ E$&pQEBI<\ ӛ u_p{v<+N")fuL߯!@hP$"uFъ[0;hc@64-jT[AcbO~du^ى(WBֽ'5#o? ̀ ,hAL4}/7]+=/"^Pb┯^|1f 8fjS.[,t27 xy6(eNh5=3ӆyR5e<3BzͱjFShEc>_uq.'=?X0,K[߼`E@.ٲ^v!,Ot-}[/f4ʔR\3sa#i9@ϟ]3,TE?p.^Ԗ'9xn&4*VocL*n-1 Uـ->JI[&ޘ'ۺ[&ĉ7x/ C떬xgJg˄3\rvgA8 ^ߎDQBXR$&"q߇Ng.TH)e|qk>-n6|.~`xc,=5u(k=<(4:9sfޚ(f5gץ{]QB"F6 "q@uE`.F"]s'"qBˍ*n;T(dT>?>7dMɊEٯvקJv/6|?%aOuL?K6wqYEХQ́7dWc n93m8+zJS[Y0h Mtv6˫H=奭I7mݟuRԬA&vL;^ҶGu.Q/ow?nڿpfAg;'/WKM6[)͛LI)aFnK2spc\B1qͻɱ  lSc$I?z\)>A&N wi& Jr~U\-"|5)a o-pIC@,*:SE8*ly/">:3a$W3K:`|gԊ_@xxN;Æv/F/Z_Ruω' [qRUƚDK4;昸WLնt2Wjyjx'W r;&-XfLq>G_Of5'nDKv|vǰgU'? ynaAAAv/$w$XSFZ̙˄ STOy$~};w}_<UΦ83Y`hN` Dsq]z*3`& \82Yd5l @iTО3ʾ]^87㾝G&%[ qC(pyi`9C,g.1]"5" \&9dBȺ*'k0CH"ưGlÖa!EU~J)2 /6d@yI}#qf갞+RXSPVsJDt1c{%Xma\?l8'nl-hվ&"?ыV}k4Gn>Ы}6/Hߛ'3DڸqF=-(*K@PPBh h5dкF0]M/Fo,7=2 A>*߱1۾Sγx\T0zUWwEp\&MPxfYɵjNȏr ÅB`yBcSy@τaޝ=l{O.. !bw]LLvyܘN ZqFF3!qg׎Wr? "NO]c8,ٰ|Pk^8g\=K &9EB,8b@7 Cr|]^PE F*]3C ,*@oQ tY[ 1*8ˣ|zGIE˥G]*h 5Wn%s -s#1Nz&I3m]ыnrBc߈Q&r^<}ك-~1]'DG5ZM<8ɪ)fBН\sSb[͐y8:\ʔ3DUQQaUoD?\ *ZIL{ySFM̍LͨA<]T,jr*W?eX^ɃNŇ B⫊x0Ve[0ĕrP 6XQ L&u_gf KvgNp&؇ьl>D/]#Ke]N͸6M;9Nb?%ᭋGhִce?>"1!uK6ϟx4@^d]eHBXqxA}alvD${T4Gsm"%QɏMVӟc o4\ֹtޓuqmj!/β>v<@H/T3! -$Xu# #!7'":>wG #X;ƚb xe^Z&.i"S3╥A߯msR=_ZOUIJ~|(`{K2zw E0bSSX]8Ql>G"Cڜ6+K 0?n!G@$.%/}!onG);p> Og>]0ܤ)lחǣzY(%=5? Z ߍ$J얖4ӲbCqzZ ɥLj޼!I"M-fyr*[URBL^ɃAQyK@*?HuNo˻\ޠD/jnݼ-* +ył.]-8Lr^Z=-K∦ک#D-ì'lϰź!7M/?Ѳy**#f%Sɬ|*@kO8\BVK9] >>vm:Lvagʺ-ѣ2[ws|' w]8N7+IŔ>5͓<8n}Á\ػkL N,f2`fu pbQ[>o1юG/Z*3UܤNcX*1Լ NeڢyǪiЀ̂SW* /oy*Ud]yi?eyfy-ݡW3G " g9ˣ0X=uW`jxqKIo ^ݖƓġ_vb6 Uo[ΰA̩#I]'g%ȍmN1"V.z!ΠW1{!>fٔg[ޯ0 }5ZS8ś!N[Gios Qvg|Yא^ɌlDm7qs739՝]Ir*f Yv[ q5x c]t{JQPBkx_1aC־B hY4%r]c~/ь*oWhEگ}Dq<˕BtK0P",)!Nd0yIǼyp]nV.-p"Zq?G@^YeFo~dJ+AwsA{OklGE10itwRjcO|NaǖR=IH߅K*i"2 {8°=:o9]Ze z3ܴd-iyJԁs_&=5X{)iߣވ퉳Oy kT@XFlπ̂V?YN<ھWE+9YIlhM;{ C.7@w V F5k V kz=:rh=2(O}8hf&+7)]όVn la)W)8I`)S$=֭yq\-X@ЖU;}Lztfp̝[ۜ a';bev˓6 :{6I-b喴t_T6 A1E5 T<9Wfu㌷9J,teZc+ZgEԭ mN7.qX*4@؎?xްrgwk0ohM`o{骖Ѝ}٢2Hb$ XST- ,EHŖAH{T${I"O_!`q".^_OԎ''$dX둬:`|} B#F&{#g5P <΄O]7 `X8iO̡m!ϓFjis[&+\j5~ư @@Fn:w'~"9}-rwj #K7~'l>GG6Y0WI]tE+bN#a8Y}>couU-$CU8ż&UQ5gV7,_udܓɿZK\tmۍ ֪}buA3w|mu)mcNZdRv͌TȣodBP$X1J{|gVW0ae͡jmRL <[a˥GC<ыV*Qx07 8hVkYeC a?[ rDQpE ܪ =%}O-^0܍cؽlTȡͩԃd݉CDD,ڲj3 ɋ]o=BB( ?u|As}󜜭ZMІɾS9_ ?c@D8"I{wSG)eJ9JߦHrG2?`cos"N{Nhg&BjqUt\m ܴR!2n} Oeq!0BzW+~<^Tvodȼ蹚;܆9JP^#W4 EqMe#ٝQ浻O(y:0;vBT.K^m Z#;ai%t?qY/.q&ݢ5Q4cn9Tc"QǪ2owթ)//W5! YY* U0 UF#B.&9a?^?$uŬ+ޯ >r*! k^|_;/ba4>ڨq^%1%C' ::O=Te:pYSWߒ F6a V9v$ eՈoUĺ jV,9E!`آ^oH}IdI:+~גf+*.#I+rS4ŜH>bA&lSqz֛`^G5j$փ͂-sa-}uz\$y5;0==_[)ծO[ {֔iO,O?]-iH3^pG|RHGdR"M (=mΣ8ҫUs*bgn|2id95CDƱn)&?;^fۗ/?ƝEz1Nx "dBJI¶_a_ByoG?l[VXLUFߩ*/WM|Y{1׸I7ftl?*V, 4 e4G=Qrno|)ۍ}pdwr._[,P'vƏ͠]$*74+癖%TIxZtOUX6Ԉ}@> fצ}ӣH<"L*ݑaLy% o$A IDAT(eAnL{e+l.W/q$.{2?'Gx.ݬ~dh2dZ`zr&_T^&[Y?& pJ Ae! ]n1P[BtM|坧q}o?5m7Z"0bM+-Ar3׾ {?q1ZD S˦CILowa2 CoY#]ǘM'wx[փVcBam_o0Z~|7ٴ?^iYZyȕ0A9ڸחAd>_q|A~H\J)`ϣIx3믽w|`^AVXɝ掎uNu ʜY<]fY>6>yBv,P}Oen!n߮$!4|ö[sMGt~kyW*<8eçKX_A_.0sDС7N`k.S˦#{ >m]:_W^YE$˷!uW"RdE}b uن3WAo=𦤼=}>y}SM Kz{cLfn8] e$ęn {Wd66kǰAvDD4kB@f"^C@@9&͝[.ԿS(7p<`̱b୶nݾIK/[*<8e{d.7=/p*'Oo1*3kTQh@&]R0X%WOo&#zuV男mRdJr6&_T sTH}|$QdaĿUsɥ|qQM|ON>=>+ƄGlQ=] D`Us2+"st>^?EOŋO6jwLӂoAYF,}}(ŝoS/,} z @+ޝ0?jv7.g^\&ÁWU4c ;ԓ'}iZJ=oc 6*mJMOHe-3 ]ltT+TU9ݒ2ꂷ8#\CNUB7Mrڵ3:ws-]*|yaF MQQLK^͖n,M%Min5 D2N.ḠRuMJak%Qm&ʫEH<.v߿VF5-QFZƒ_hUQ_d>kT]2 3_G{KpSx~S˦qJŐvDABC2Gh#/hLR;>y@/M}iz_o>aeV5'N =k)>Ɯ"Mޤ$A6,hKiz-g={Lō {1Tw[K\:&sHMZ&|jx7H=urFs^/?i N|UL]v9.W^͓ aSCMwD>=Wh8)g % ,ׯ 41Sj uyiB.H^,`4.~ǾTq/#xm;Q˪|_w\&3tͬI@ gQu5 @Ax+xh~LOZajt͗]^.WFX6 `e5WGgUUT3s7ԗa6u@F$;Ll_a9Dqǻ%#n&Scq׾R̓f5K # 6+2WEzԨƊc.>YuC˺uSA沇0I$e,Ve'~m3W̸S? 9g CbGH~4;9N~)2eD}[ֽG^]Vk+^-HDwJ+%z}Y~|6$PY,' N]$rF?-_qܠϟ9tuXQ w@͸oIhqIN=jx#n@ ^mA ٰ?DGy@}U$sL/d e]'7i  [Ow '&;ωV$IBB9U1zhq/`_7s\p/V]l՜)lxh$O^F % T԰~_!=^:)gKd %`HwCd!7#hL.&{qdAtW^ " T* pMq?R901d$J{$ *թ?|n?69(GSW*i~R8ӥ1G$`xoNRZ2 ynd]yMlK*6kx !&I Ahg|B\71ө+{|ԗ|&CCg02^:Miw+/g^(V,;IJcRϡ@c$Fak.^Wf岑a\ ]f{@)mv$Ťq"Ւ-׸~kΓNy\eYe@AV s}+aXuBHX$-%IBH7TCI# FT*H!uVJ~a9"xһk{w^lű+h1^_Bk >\ |f?gT.~Ѐ:8l{Ϯۏ*nDC$x0IwVY PZ՗Ք.d[X Qae d?ﶆd kxй)%B8{L#Ii 1 Z__v'!7rm%ؖK^/zP }^Cm]kYmYc%G8gn"/'\ˢs٩^<;̣Vl@$C K0$ɋ etW[-['a˸U7+ Bfb$r&TVNNsic Da)p{4{-5U-c(*|Qm(ޯ[;E+eI$ Weu4kԤw+ jRd[[mx*6~<@Pǟs)rs`vQ^7~;?>DkNH6iC&hI}vvn>?rId *Dk O5)䊙_k+];vrj%&] Oܾ|-Ŀ::tϻ'-|؃Mܔj7&0ϻO5?ۓ^[-7w?|6{hPk+ J$QV0YלV0^j~q݀<*mzJkaU|Q9=q`sJdۚ6hE  5.K zϤ.V pUܪyXrM؎GϢ16%_wf>,+%ܗ2L/,^ɒ=e0\Ttdzr&X7pJζ8 ;$I5܍\s %¥FX(p,Na"J2t+JՙcJITH"9\{g ʢ;A?F#{5˾a?3{%Ӻ0ރ05~ێN7G$$_[m8B$f"{k-Ŋj@$gHUz6g+s<9qz +)co:L1h^{<`ԋ^X6|oü:ɧN~;auнZЖRw$&QDȇN,OƊ.Q7ApP$9ÍA;-Gai^FW^ЗUf˴Us' Z[hbn!h;I$3G\L326bһ?kmǟm o>?O+71ڤJseA7pz c/.c ֹq\Qkܫ1߂^{{F{&FEaLci՟iϠJ+\=ݨ%K]7s/l0 i|rU7󞿊<,@[M+RFKB011 ql+j@a)u$aRVc\9$PGn'R0,z9a]3L/,^ΐbIH &iG?v?mi{d{ ב9tHN];I6(JQE8rJD٦љ-JW:lYLizP-"c1!};US)LK- F5^߷iD>?$4]mFA*D`jj2 q1Yސ?>ABp? @f 3o|~āㄣ6UEUbqcڗ^~6u^ zg|q #4]m +uPEF K./a%R/Jr?5Ip S+GQQGb㟦>j|ܪuB!0AI@jaIAw ʇLPvqsW3}~BH BRH½0%Wi7gJCnۏW]SFc9r:+#S0n * oWίh܂Ĕ ::L7c_Rؕ,jrzfe3*?Ƹu{Fe|u),q5C)K*I'H1?m9@YPA꿲 IDAT^p 14KΗ*٘(~'p݉4\AIs!'/i+, $QӱCʪ9a]m&~gr|{Zq _oVl2I냕W~TS UFz5GSe<x#O>9?KY1%=ctWCYSct/]%r[c̩Mw{=qդvB[Ы@vDJ[roY9.lu4^8ЇE|~7˻xF no{S[*o9v7pTb0c!I@4)hR=;c4i2Qeby,/AB$ V90J0ө][Kf <{nAf׫$0FI 㙬Yj&MEN1 JrǗh[p^sHT([NĔ ̀ myࢀ玅4T 7-zdh3$#K*HA 鴞V#?n3p!d$ 8dNȚͱP3jӱD+_4uҶy ڤWޝzjQWXJFQޱMݎB|JQu9>͔_ ׆HSVσt:5A%䕌GqecJ*;~YWQ/X)lOEFRn928ozþr3~Ma7?[Y`i^z'ϛ~:{z#o_:cw;?;{5{o~gwsU>?V4G$d;vsTlۑn&-]Wdm#^V/UQgzVY]CHT~:LPV!,kՒq{+]yK~D(]yܱQf=0H E@ S*^MA iT@,-vӈ(QncBpoVgMj/w[++$쉚$n91%&=ޯ1:So=ctmA/x'^ö9 FD>~ 0[Qrdn~NFc-`x~w11PD cBbhSPBUVkܤlvpQ8)(_U7/AWD&(~,Aw:+cS~sHo-n! z ΡܞY/5"&54 kx鱩}%I2 ˣdu:uY1$x%ժ+SUw 2u#d,y^`nIgSVͭ!mp^Jaw5XV1/PasMza۾xdyVij)o`/;bkhH nDž9c+DQno_k*-^Ċ^ym]w%'ޗuU[ j6zxIuMgv!L@,eJ3%z](5vBCGu-Tw%KVu޾ob&|v0Eu U{E>x'MéSVrt8IdY4JgL@YH $ˋ i}ai ;Lt«U^ܸånӝ3j+ yq"DIϘKFUs&6v2+f.BN.ZcQe;MW2V]:h`OLÍ bP0L0f*Is *j}S]c(l(a#=It~8"&~ܒTEWT^%o~CΏT|e2yerԗ0!ӱRnkT3~^SeR_{Oؖ:j ?]xp7d0򪀝gN A(^t߫|)*ݬqkrq<$I"chd0i)ܨqbyF;<$@$Iџ:I }d(*&B5sk!?"kû=Bbiݪ# ov=o㚭c`۝RzE|ohm@8 =И԰GgHSMA @rkOFv Ѱc]3.SZ|2ܽ~):j0(r1zQ|R OE"]mJ"NX O WXý%BR*Ӆqg&]YuW—Gz>OCYJGߵbBo:l/iRˆ}w&k+-$˵\]L`>c+`s[_W})ew>q>Aȫf6]$aCك;.@7݅ S~YΣRפro;sRaW]Y92d O;k~-h/>a{h0v`mמmiyroU/BS2//M< nʋtWrx Vmw}[ji6E9ĒJcma5Zhrb U~d *kD6of紱m yDhl mnTTD>~dvUQ$n{HY  Ca[F0Ca4uGwv`y'W2ns,ywjdԹgAa#͌ѸIJ<*Ur)$pܨ=(5/bN84M(G GN*J ^kWA@ -k\pQ¼:3w0<$J$2$Y@$ިw (192ŽǼ|X:if+hS+}o?=/[0toK'|] coKr󴷾hLy~^g"_m(Ǡ=4bN\ ).6}nqǴf%界3P)>WE%4WIL1TR.osk:`9" )4W^#*@Ȫg{pz~Cq^Iۃ/&,) Wp>m&~\g2 #%<-^z:-]ЫՖZ!%yR݃Qqo,\uU$L7C56//d-jD[sKfͲ|g6dYHσ4Lg0'99q-j-) Ɋ{Ҫھm̛dzrHqJ7}$euM$qH>?0}䛎c gdVp9 j`Q$԰yFFV'I&&a $pWQŒ 7(KUVbz ݿ.H|X ;o YY8uA6:pY$ x+u˦ H9HF$bHBH0Ave1[ߍ̬y Q;vmRz~QOM۷-CDŽ;ŏ+{qA\20IB, ϰtݯO4JKqz?9io|eձ)\&CIϘC?[Qm9 $ vu5;ŏ,թW^?+-0DHaw}jiH1ٌ9/"$8VZ/%5?4/U-$`ׯ>fO+#sŨ?n7?UmK=+-E>vb{˖}8&f GE.,)d<$1ޕݬC=?/b~PpdPϙ] ǭ_Ʉ4IOݙ+kUkڞcش3tPghwTņLŭ@QA%/S^,BT (ۜ4LÊuɵd0 d]]i`o$^$b4q|ʲl{F#vN jvڶdEUn`2B,'$jnvA(R`r AzVu(Lmny CK+,FnNHp~QWTW2ǟ e0_?c|l"/y3M`瓗2ك71e?~ Y0jTgRp^SWMXuݖJ}YU\g̡7)Bm,,9yI)Ac M'B NWY'@YEi;esxrePJ$q%&P@oi+jt>}v⼨p~+Drmy;?_AO#9c/.ՖB}fጴ0 a= 5 ZŰKT<ZZH1:8},$#QFRg+Op 0N={kbtg:Eu26 u^R~ٵbڗZLD l gxNW*!dp<<8XHer|AIC'mLugr0 a+jIؠMЂ24A=cTV%Qv&O"eٜt['rD#?+*1JHXe._xMC=p$+3rx2=B@q^}X4U'/w/d-HY"DYћǎP7'@+5Zmi#b,hԇ G^YwЫϏ>{> t*˦; ?M]c0ȩEN>o,(֎M}eDR_Z|/ЛKo;,Ok(:CHV_sF8r@NqdmEiuF+7f7Hwj]Ȫg[pzǠa7/6~bɃs&1L~Gml ǐk/鋼f Jt/;s>Q$7x*yr,rop%] OM%y[pZ5s,R-5Yj4P,>ߊHBd:xW $\v`'@)ǤQ|lGxwF?*dxzNd<=-BiURj q- BROڝg YkSkbbJRN)M! J$3z "MAC!ܺr Iaҳ% nOm(>"a{7m1>ʆtXiј_6I@<ѧ'a戠Co,cqD)F\Le_{9"O&K='ate ULIϘUsA :DtJzu2uh-UZH?EHW' f 2zkkK+ўe] sג^[2`!\S9o;zqPmCiHNs(0ԹE]yȏTGelɧ6ok'|XTCt~*VklW+vɎq{|{Pzt=AᗭF:wӸynVp"`&MnALRŏ!j :nISgȧ.J飍[0݆aBX=~9BQ$VyBcI;jL7[!ܸUkC\e嚥L b245^:|)dx^ANEsG;8m$4'$D";Qu)ܟVx! @ɯtXă]zܷcf ;Mx3 |%(weڰWZE<7;Jv Wz<'g5_oSYAAw"Ɯ4pWΚҗ^jpXqW *Br i$UP~qh\$ N#Yw|T?3mҪW)wL6wNOI<[a#QLolzA^oq* 7H;3wV3|NF҄חm3)K".!*fcQ}|z(9⣂TLeG|TƅË& pjl!m:2j}Qnۤ[[-a `Rit@_5'|%PNVsA<{4;jxlpKG%GtV* g IDAT¢Be't/,F*U,XQ,{R(%qר_rshxd(+W4+ܞ fNg͖2yr/$nCU^ThˍE @X5̔aYRTdm㔬 \\Ɨkxe6${bb𢣼^Yc瑈^ѯ)Ò~({.K11-aZ 滙fϗO]猘Ӗ}V'7=rkwAZxY埘7'g@tGB>1#f@f7_u]L!w>W]c_ji~`õ{r )Py_ g{Nv[ie/E˫\ODX3' IU0"&hHc{v<5-u?Z[bhqoaoxg/y@.("qHIEPQMq,^P%ΞUT#Q J؟_(@x9LAд u{i~#e4lhn~Q5q^ l6&k2oҡ̓$-^mWJ+CPi#8hÛ}pMlLR&CgfM(!1KCax񻢫)nPDz]-> _G6쿷[ MQㅺgJA,e-*t] (e<WTLL%7$yD"aEKe8 ;5[s Btn}|6⊬&|{D 12 ) Ա@R=yx>PvoN(R$çS/(̉}$:#B KNB8乫w砢j%xL Qx&Xigg7'ф/MXTUeFUٷwWUta:?a0RՁvݴox=wI9[\޿[U.p P4Dц{69._pæa..?1‘E,/j鹺.Y/ G / ?lɆUHUP:>d DKɤg$ \ӟR|o_jRz;+{w>=}d(5q>wFWq;: Q8fςu-;Evz5؄a6Rܐ^9nygn(-Ld[-qOP z~'}Bu:/&']T֚^ !ȋ`zpdWioUUWTL9{u0|Ox)^KN<?K:I ؼ,Ų+I4ZMz'aDAzaV2/HQBQne1D7j&^M٢K'~QHe )̉m:=r9 >Hĸ!BB!#,`6)B,eww?=uxgO{6해$7~q8s}u"YGVsΑB[U]_햲=vlѽK6+EZG[U߼Ou.nAY^tՌ}̱$DZ&ex0?XoFs/+䞞ܴKW"J|lgݰ/ttaog$}7s iS6G /M>_[k4hCZ4xJWTVwh-oJ￰$舗;5ϭN]^7ٸ+hQ_(FW[^n5E}TJ/ Gan5nn5J}$g(=Ksvs:$Zb8'tq$xtYQ_ $D4D;;r w3J'ب^]`S[[g8w)K+٢v|^*& !dW(Ym#VJ.*i ɨ,zoV T> Z?2+]{S^loNjѳG ӿ=yx U /dfv>c4ca _[X\٢zM5LQ*K fc05{i.c cǑCL #>?pbֱs)KβꞹEɊA˶v>})J]2-^ֿ/;QVa :˔sҁ=&.g~ZDzGm69lh;WvA YTꅳR?[c±JVlϔ`Di|XҬu hr9'h۟shғ=3'oq AhN|s{E!׾J9Sc6?3gy-%ʁ$~ʑL xH6& ^/|gK~kծKU;j[sǚAvsaWg/3׊+sX/}&[z9u7n6Wz5l,tZ"{ءt3}|Bք]*úm䑙O֩n/RT&']TޢlGrj? 8lRj{V{Pxwe/j߮g)͔7`9+C+w?LS)V;K/=ZE,*j$C.ixa4R56q ! )ȲȠC637/e1"H'Ϊ _b@0$B《l!0L$Y EMJz2lcHa/aA-hPBTI6>Bd;Bmxt-UyQe_u7͢D~oJ9W}lɢ̓EMJV !I9wYqWguwG}>|WӇ{of3O] /t)Ã*ˈ&=mS8^9,cgie[m]8 o1 'L`uNxA˶Ӈ SƗ:_4c :.<9wSF]A[;nJD=ߦ!n䍶<ĵ[&mP=NMS>[[ʣÎBViDc@㸌:o-}'k/.=Fwʒʼ l1/7>}GXB0-߈^ f,&W}b~HMX[[ e4ixEWmUE6UJU&rF?DBQa!(M@)]dh*90{<~V5Ui$H吡A\ =8J ^ɘuZ X-{&&*KQ89?T)cc&Ըa.3tw2`H\JxgYMG6Sw3GT^+}6o)CgG7NC>s߰@Z"o3KR1%˲cB4'U9Jkl5_}c & 9,JQ074"ݳ׼nćI+NU:ygח1A:@zҩ9ÉZHWipQ ;/VOֺhC_@euEHr EüsuT K7🯷<8-xM4Ms#%j6JWs i)2Ƞ33=),H"*&D?ZyjeA1/;>w}fgkܲWw ۟ 3|v_?F-=榛fW*jk6:2(Y7)EgUo ~7")@U,JsfplF`+f'\. ƘMOKh7@H_} D~K0t4(c Urv^" ߸Emyhbt{7ti.0EIF] (9=mi$K]=)Y!R8IC{udVU[TSHBLJk8NJ -]iq՞nO(*xr/U&ooާYZ3꟫L~yhr6c9ޜSFaq_6?뢭 fri!5nQ\7w 4Q@vR'_|╺C29iRqhinAhЛfҒUZ9L2zB Y$ơ|P( D` ,CF*eOd~.$9MLy W!}6BqC[ VŅ &V:hpZB&=-4}t4gI\RREBd鍬&d5@#8U嵽4/3Y?{<>YG"g-[WF044P:n>@2/F>C{ViWElٿ|"FlwW-n0z睞6|ZF~a;?}@D$*$b 1h^B*#J|!}7^L>5{!}*䱼OO ';­p %+A..PUDPsXx@PzO.NJF}(9vqY^EbsXN]t1f>iQ9//$](-珌zo͘WPwX>6͓*;KY)haUZnwaxcÎM--;L{X.ke%(@6]I3RGsSܪO=k9&+!vpK(AV3;)"/QDLŤ7Pf CcHf?Ytpsң;]zj r2BGVIVV+|lJHd hSMRIh.RCZ2դM P ǡ("WJ+؃Z]Fu,f5N-7%-s Mr2DR瓿=NZ]5%Y_Qb Q0Q1L|N3J02+:щ?_WׁFSdYf`o{.L<;{Q'I:Q.gtG5^w0ϟf^ x$Y,Ȫd稘u7vL!YdnPb5ơx(OD** n4)S?wiRTrwn$A Df]n^ qqCŰy|۫k w_e=Nx}D),S:ǹCƿLJs YŐ೓g$+ܽ~{q5y\. X~wwɠ %;G[ed ۨ^B AH ޜSE7C\42t89{ /{;f&kF;q~ܠ`j"#_ab:GiHQe$Ǭ_+vajΘPJBBqeu{̞-J v8gF|a,7wG|-l;t%x=ݬX *gMNxU 8EΈ&#Fw/kÛX&+մhZ|^|qC6RGr zZq"s%\kr4M')ߥ\(IBt,Eح_˪tXL`xn~=kN1n/H,*U9lsTB‹י猧Jy 8pzJ7 )킖'mPc規 v*[x㚍fv@@z|T@lvX`ЁHL J'q2cjz䲼H0#]0BPs wm(E0j@f$p1$V]g>&X &F!_|U4vmXf D(Yil4mˇQ8WxI|on}38-)))TʾKb w?nn&^b +?(*批}B"o-\'X.|$G{ AQ尲z"ZL$t >\xWݢ2$av5)x<%9OǟR k<>~=NM Tawd+kU v1^T`A"$4u4Y[˗q4N(J=kY *` n-A?}4%;zZRՠIߖ @{V!1\'0DzBfagzx CU!QLkWl/IlJ=W&njuLD?>?杕)J ]2wy4EvBW77 2u*KNZջ3_pʰnҢ,tyPO٠g$[uՇcBQBBw'L1|.4 IDAT2G 1e=Nr|o(5ոݬKFڣFr=7~P0e-J 95sIXMWtlw|PZ ǐBBAW=gY8dc6{S2ڲl>oHڸ;avH^r{<̶g|BxP?^PEQ}%SCzcn44͟C6VXA=UT<ETo߰"64 A5%څ'TR@BG?-Λb@< a:R CaQWW^gY>[adbAf8޺_dyэ{i7r[hT)I$n~Zr1$ˇv$(z7 ڌ[Zs2,@c6^_9=}ĄזjtJӇ#kq2AsWIKBȗWEQ^Mq=77h֊iNQѯCK2K&ig.!EA FT %z] #@ڪvNr8vxH>l\%RUy;a$DѨׅc :I]n9 ZNG\vIVUŅEG1`|6m7A&Pڽ#u>tu omMl@Q=ZUUO˞>; !KjlR_(:$T^4E9m,Ep[ RWs腬z*62zx+ _0_E-|{̵GtT]gOF}x Ǻ-L`Bbu}^LJdWK/ʪ`QQ%Vus'FVn+(5 ӻ@} 0qiwUn$EnQdhh2 >ԇ]Z/Ā|%ɸ 䂫SE DEԅG]bt:׵+eJ:?o|]Jo#s>TTe~zڈ>y, m}VTu-}TsQ/X%zI_kEOL#LQ*MaY;#Da )a'Lހ>c1f3ϏJI bx0o3\+=*sxkn֮mO5zEN*ь7^1U_qn%UT'H!dM[DUAkjg#YL q"s7H"Q‹DVylB w8i]Sd.#/fsXQ|~ۮotťZ0b-%Gq_ *URIJ[.7yDJ; ۷[3!ߥx4َB-lR{D?jmzn:V.Մ[#*KOQhy&J #Oah2~yp]d//;u/P pgQ\pGt T%̤kau x>~^\` a.G0F ֬1-V8)8ɠ$ڴK Y'CQ]$Ƅ?=pC=U4X\w{f`+`}D(U⨬㈭N0TU37Io5ϟ"kܪiӼY(Gdt~;#[3n%l' d܆6tH{woQh.Cli _a^8%$4?DYLƹ[Mkvk@9l^-&pS)W[Gr Bh2N+*pE=wIFK֐*ϤlەTW`W(A!q{`)̽[q/I"R_YV: nI)f #T|Z bE63;r Jt"=>qaW .-9z#niy)Q"ZD@pCl6Z#.{v?V6Q 3S9?9Da+ wjG$V 9=uxMlOv`>&Fҙ)/ƅczn?D)*RU-RC2Dd`OEIF]0^gdNO"q!)0MUqY^ 'ohE.uyِZx9n3q.}#6盼A6. EiQ2Q2ST)Tݫy3 \vrg WuzMoev,Kw1K7pei+W>0#:w8S1Q~vT&3E.h'㨤j(*@q`d!gB.{zɳ7ȯEx Pqhɵ:[@dfؠVYi#A ]ƽHXWT‹\y5߯<}޺$~#!ɮޖ ȼúlu%Q /Z[@c[ݤT]?{< ^ K 4urXّ)=~i1.zaxu)-HQd:W^k hsƎ>4Zk5yI[~؜_AᩘZվhYZPZm1IEuy'Z/+OKx'gFQwm&oN2*MKΧibu*C DeCqicxXq(s8RO&Lޠ菸~Ptl=t-P۬`"|9$),yaZ*fr i-:pMs&zCU:!LUsh+.KZ3k8mz3RQ%o=;~Cd`{qz"Y/H1M@ɵ'U-S25n { {[  /Lș"]( <>x\iEeݮŌؤQ{'$')s&0붳Un1Y_~HwT<~Db7!}ah*i=f[CeM jjN;7bJr^}'>DBQ ^ h,7ͭARqCdöז)%6VF7_y>?#ioHThW ܞE}(/L(dE.+30ϗ"1%+PBɚ cn.hG\v Vb|ΑPٸȈ Mɢ!_l>?v0&oӡZHJ*K Hw)+slE.42ҼJEjXZxlb&h;496j2=buT`툪o!g &;(j$5nhÚJYu\RSԫ+ӕ'_(E.;8NSA^TԪ:I1X*%1Tm.8ysW gB{,kZ II#1&`X^+EJR"}r&ռϪ޸a*5Q?{<G8 Q {'qDbD5eyYϭYw[[\!սGHE}pJ(IZ` NU*&ٔIl¼kC~_ۤ:%ꞹطV| $"P^WtSjo(ٹ  KI&b&Ǘ IX_q*C 2te>G\8":b'jڜ=4/hFw$|Kz;1_o>Vϲ~wB܋WHIWQcR{`B Z``,,ҕ&]/׳%8Np%f v-]N/ަWMEnq,3H Q)ˁk<ҁBN͗{z\p IW ;R4QZ=v0@HsXr@U!ICM̅BЦr@Ĥ`nطV$_(kQ=O?KK:p/^iMe:Ջʳ9Jk}T]]$_(<T5Й#gUR8II#= %UwWCC s!, :KB^ 2B*CKF]4V<uz%g`2+FgDAH Ι҆[iڵh t6;{(|zn갻׽t4nrR0u m|\fV0leA-HSШeayFxe4^N2, nCLYC׼n2 g{fOQ })_ 5lvA{:=ة/cE".fz07 bLKd]$f- sq{Mu&UV;UC^c_~8_ko[-=VBN$ xv} ̢x!{XOg1('҄|xd.) E(i,i9=ɸ(]TxYŲ%Ex$aQzĀGk:P1stnaDa>[.r9 DjOCD6M?hvז- M̌L0Vw&UnYW?mg2(r/p &^H֪f $dž?<;>}9~@2˖Ejak v, U??.X3 & D PYF4c Vf9jOO+F @oߗLK<׷Win_o :*vќe\7X̡Qdݎn;p)"lse`%=vZG BAh1 wp BhLVr j7}G\5Ebi>uwO)dtUa#4S\e=`8[QΛxxzyJ0$UTKo.8!__+ > ]=I(B]&MVR-qc i ,(33ȊY!` Z5 %6q7s `k˖t5n}Zg <ȸ=<^^Qj鑬P)EQZ)Q$eJV(Yr;-ɔ$ӲLs^t_(%@GY-o5W+9ӗ#zXSS!hm&v?ZVz7{Ӧ<ٶ%%91鋵5,үɝ:F@H\ЌȒf[hw[@:eOJ-ܪXh1Q&^*ud)?^I(Zbzjd]#L'i*svEmtM#ʌ YH,ո)tzPT:rZCZE%7ǭ'jfznAو2Sу$)IKՋ6:?k-H!h6uI0 mHfg8˫D $`[CKUta鞽 }j]B D]UH 8\2_70HTqݳZ0=@_ټa:ėܺ7oI۷x߁SG!hԵ4?xO?sn6Bx VN_w[Mi[no%Zjeՙ) 9hD{iS/QB\)Erj^v0nNX͑a$}ϷJvzt$ۊ /Ȩ8\ '+6P;DSGô$BA$pl\ g6yvK__+ -.^+b Ţ={S-lspr'vk5~q;hݺuh#.;p놭4G޴}e  VU|?Z-YKjD4?0g8sܢi*ͥtaw/Qlzm [12i P:JsQAoNk8O!3g"бһ@8 rW:\\!)љ{7YHSY5u.)͵;+.ETD )O{Sn [JNdYO#9;MIq !T 1w`DD)*v+6 %5C$T9 8CacQv_ * :5M"Qm?h*%XP+B*uOaU7!n3KQ^ېVLMO%|Ih*$}Sߚm̰!wna&7M;om_߶𲣲}ٔf|OGb@w/lpy Lώ~4( IDATBh BnϟL$)$oUҴutXQ=~qyz4>),.Lʪ# O% $E}RU1#9\m8+wn(_ނDHں Cx|v!_@aYYCQRM!W?R ͸ x q 夓pM{ 5X1FSFAȥr4{`ݔĬ\ Հ BF@0VFWմ'3gY.L!w], Y)_(TZ7[SsGdt,nJ۴S2ݦW )+ӡ.Mذ?cc*u2I_1,_(zvs @ :N+#r A+Xn*l[4 z`ݍ\N)Wv"嫢M !4y_h\s55s#"qhF;U(E@JHIۉc;7wNb[Œ)7l`(Q%{C){? `*ݗ3Ι37~{mjp@<Â<~"j@˗_svZPB1ѩ>aoYՁ5˪({q/yZgN5khUw{YAswTQ(7K$1I)qEFL@4j{Vr-&e@ޭU.sl76)W&g|վpױ+7Gp mH*m;ޯd~zܘJ\HDMk_zf&ӯr;o'+ XMCWͨbkSUY]okוcjͥkooYy?kXVy[rN*G c(+NͻTr--o.gikj5.7#).Шy0ʄ(Z24]|>L13^rm + +LQ؍{@AAV@QQQAU.)28]߯O7^t%ڝ!ged}k1mȀr%]]d_M%'^sWgOpAU} SLRѺC^C/PLgTatsV@~~>]^>/at8 ߼琽d07PCk &Kf xñ$8@նscIvE/XUa;~uV 1$,[Oxu]VhT>Rc}zʦRMUnjEV-NKͻxH4 UwxKwj5rVF%`>XY#:\TU*=xlc*e Chۏ#+x <FÞž  +;jLz% <@NQGϴ Ў!y$UroݯfnsΝtyM'./o~{^Z% Mya״RW͊]z!/$ 4pNDŽC'ZiRj.?6YT\f6f{p eE6S(%z-BP%iHN&PF[3_0~Pi|P;SCQFg,o-g ]5 >vz![%nX]op5E*.Q@gLE xj #.2H2ʊpϿb Te{J.\f#8l>*Mw$#(Y2݇7vϭ!=R?s^M~cVDG0ziKw°^ŀ^ VzzaV)Due+x&Aqi_?ߴ%揷o߯~yQh}1RZYǂ=8z='!9YR.\}  t,"[`VjW{x1V軔1n,LϕI$ʊtg}UkZA|V!,׾q+@[6/4[9ׂ-."Ԍ%((˨@i? A*ȱ͞L=:6j>( QjgLZz{[/6 ,UFAwߘy7 # y7<K?~auqXO1aR|uXO$a]Űp;D$RxL%[ݼklzF>iq7ges[JYe5V~ŷ\fLWSs$/[jt_?b>v m2HEx9buYn'3VS|GVCV޲ػT<Ɓ[*5~\zLyTȪY6'knI.xyԒvKF$]@C3lKڪZ@  B3=C S<@I3Nc]}3E$ [ xid,,;rZǓuXV~?6FC\Xwk:R O_+YY7o-ׯ}hđu~8\M]F3IZRdaX 3)ʏGruQ^*ۦ=qxU}}=eǂ=Yc3[r[S0i{"ӌ@wNjwaWXb P^B6go; (P02Gr,˔}GV%' :w\ϞFzLɄqTW⚬YWo 8d8.4[ЅYLlZ'UՄm> 1Q3ҬSeG4j Sa$U ܺqA2㼾/WΙQKƆV[r^N.QӜJB* 93?f8ZBXIOʰO{.GS𱔠$ђހ%96/Y~H6b]f?oc?~m,㦌cvð}(┱mh] MD 9L&d2Ee-K`>)t%Vѥ ÁDN~wRE:w?&ZȷG$^Uym|r6rƒ`9|Rll!NN}sQp!2SuZ)'6v4Ոf˙8( 0y< uJ;C8ɒY(┮U5ֽA$E<$1p#8WmRooW.;sNj~/5gwHQ"^w ⠒ s%F2+骼X{Lb|F9%g߼dNފEsvX]O=^&XQ;nUj]ka+,:ygKR'&,fw*^.djn&g @Qr+N̝A} x O}3ovzno֯_1 LZIUQ'^ưxH.weIяu#i\c=kh@+ ,wd>?U ĻJ!+kMz.Nzd1Ya-ҙ/|#Д(p+‹Q M9. O "E2ZA-vd9|r(N>6GV cw⿭Vɳo@i^hwi 'DQLFÙF4M  Q<ϯTBEEU 1dAT,1:-rj>m϶HLǨRˁ'AcsX킢O+ĉKfOFw'Ǒ,"x5A,$>Qqd+)DI_-K.1wLh˪V.Vӧܤ X#_\$'SJ mhdg\Le&WsE͢d4PȈA¢ǻY}3Hq+%ǼlcG?e6H22f•%p*[R U\n߮VɵHo@Diz̻|{Xm[IxjEh)y*}`fGI&b$IFE&(˼xٟcqz |dNos/_M8xG^oDU / _N?qʪ%ۓ="Vw裑V=|wэİD]?F9FzZR:2NpxqbS`זP^ksh0(SiM7fqK(/q K=G;PbA(W<%.$3ӄWJXU[(nZ fgufwK}[Rp+,˃Go[٘(>#^Bhcy VSOCDյ 8<jDE(sT2bٙןq x}4m{,~{*u[`}rSyN:͟W>r1Nk&-q|a]Rأu^)q:$5)%Fc&25m)ذr2b=F+`2ӷTW#iyCv V=={yy~lkkIW7w ]s`Gh t9n?ب #L*ݹRp{#wVY\?\bYAzca>cތ ЈA+ȃ 0A`iF$'9*eGӭtȲYQ"DKxOfcrFyna2V1ĚI(m'^h߾eOga7^swLy) Ƨ_~,$A K]"1tca܂OLMqƞ;u7`YKUtzTQZ0Kcw~1פ sq+R[mD#%T`P.Cspxlwsy@QZpspBe?Tu c!0 w+2I҂ h&J2M2**QTBUd0)CZ[V,X˚9"XV|~Hp԰aDTQ`5e(c(1D4K7%M-(#>?$̽|>k^[ ^?i ?'-vAYJ1O@ ͠ARϠ̓G?6}אzA,|k-oJ ?;qDp(AcNH8 JQ3TPͨxyU9pƿx5_](rQվUd*qE0?Gxc_A=dq xzON^K U@_ j;-U~L:{"<9 re}l߫ @[/ʠ#DV8"ՊLQǍC{*H]eT)֌kk nAEa |YW(t*:~?ڿA)(,ՉbX^t׍hL|ZKDBJ[I?Kkb$l-m{4 ]MS)hܠ.wXh yF,OH?^ *àKB49n3r3Ispkn^llAJ"0_VmHQ/OKG!+uz}n8 R;ee-[(xΧO8w/G~2SUUW)9!n"HOqŌ^<1)ҙ2h}-'$H M~i);Ҳ5hK ȾMU%So܇EfWɛXU]poz-DxL3 vz3vhUHi&\YxI )!Ba-ܾ҅3A݄no1-t򂀈L'Ӽl}Gw1@C=:oZύ8@|@A+F͈ѷ2aTh4(.1Tx,5_ Ԓɽ:_X,(d+)tRsh][U Sj{)uI_uH2ʊos7ǨZgݘ <ȘC3ı*U݅ݶES j'@ȰB~lݬ TnpNfLPYKO\-vmb zwd.UPdIg|~p.o IDAT^n,*ʅ鐁b1qiIvp.PkLc'd23h@!0Y2[Ym(&1շ9,t(.R")T"D 1,!Jh(bc*0DiM[SgQB(3=DJcGN_nkm}wAXV |yM)q!a^)a-vCHE82Ȍ *k`uo(W@fRUuqxRF== #Rt{[SHw3=ah)V3nOg{w#!#h /7Wy7d!6?t3m(c۞ 8 %$Q q-m01 HEDS&8뱐VM!%8f|5A#@)U#rh;epv%EBi!!-Њܓø)cYBa[.Ȍ*`w.!HX2 u:Fpy0drW]R.K&- O ^dT_)>71S.@KmBU ϯp7t-$fmSyΗNmE/?4_lL%[Juq+aQ[B&RHK+'H ӂgbK.[+-;wRo" Ux\gɴ|fS)VTgЬz$9"B@'np#hVSO^rp@!2c 8s@iS^ ]%^cwOZIPDJ٪: %U_o؋ :d \"Os3H]1Kr1: ist~!bwNx!Њ1E4F C bLEcbXLxǂaWLj{M}GP3e<9Gp^/Pf ?z%';[\._|WoHyme@j1@4a]@es&F.Sx^HDB4X+:C!D&%%n?tQPoobXw}IA֖%edAWV$ 5q 4rnx{E; u;mo-לU"ic)xC= Ѡ(Nw=VǡIqƁ$ Ūuy[KxQ.@g CJ< sa&WG2AV݇ ((/\oE-W.ymDU.0 8Vch*HMcV1)!Ie(1l܉:Bo;JG_ƭo+W/;p\76>Xvim6z~ ??wI|s~x|=</'o #Ay4?7e=ϔvFk]@ !nu,x2hDr ? ~-ɵ(<ɿ ݞc"kwH3K!Bqbs Bhvb2R߄Kڳ@K3".O[L.."agΓZ%DI&=zl%˒[7P6>[qwt}Ś@ݛ2Zxde#-cnowEM~@[aL y`u/(n)q/Wjⱋ%e)Aia`?Z4 [Hq r&\T zXw0if[{?{S PNy:݁BW8҈Kct{+H-Lԗ3E!ċg-1F9xݕ]EEbdݍnƉ7$NKy>8IL6^& uM;zJN3W^s脶.o7flܣiE<V͓6 _2_^j1E0/yI=~6Y/p T!4xtM8g9H3IGΑ?oǎBE뗣Zo܋ utG ϝ i&86`:2(/5Ϗ)[HdЯۅLk[J sU5(` Og8 ^l a;%ǫ])ƏE|^'>D"IJE,DH*5ʻ,0)/ܸ6^^zIƦ&n'u2iͯސ9\[H0$RNw̵p3_"Yezm1I)锋+`QО͊'$g "-kQ`ό?xo3ݡuILaYBqR׈Y3s;PQ; 4@LNUQ g?A|P;S9gD7\mdFrIXLE{=)?S 2`JFO2+I+q"R=E/G) L2._?~}Tme5/زOllt8 adB_6'"yr7mu?J Yhe-=rN9ڗvt|6M<| l:(!`Գa@WI:5U#I [R)"JdZ''(\9Y4KIC34j/~st5"yyHa6(H?Y~) chb dz BWWM[I)nurPqH/x ;=n#E2{K(1윺xLTaMESmWjaֲVS)޸5[ğN^-Q} NbZY8v'J UkwBFKuF@L+ I@YfQ3-ܴ"f ;VCSĽG'A0ϩ IcF.""3 g?܊_mֳH~۸ ͵7,$9oJN;#{|/qx Gq+դ*V/״1VqWL =Vˠ}/ww>kBRI溮Eg_]LSߌC?T| /d./"* "̤r< .enhl:DWBPaIdhs{dt.vZبtlՕ;DH(fOhRVƽ³.͛߶khcf}GzyHypwL>XPDg2O'e5`|LIb@\v|X'ex1,ss:9Pt7qsTJn$afN.pNȡe̝F˪ 5RM=:Pq})tg*p 4xҌP๽\!3y cgqwyIJfiu55qJТu0/YBͦ}Cz!"hԧcv!#:`dtȴ}?N^eܺ0onhE/ cd- !LzͬNHu"Zq'SaѢW*@ R)OIHJ|V5fq\pcu;͜{ǎn]^^KWZʆ};wHj;M$p =)f ۳<,,Ϣ,-'"$ϩ)h?J#4'˻jI(+.@Vcfy'>NǏ.li.^|GO}um8O7[NgeJi9vyrR uwnDp(RQ#=b}MߑnjŦ{y d~KS]Wb۔q97"=]pdaQh[YP< Z8|>#F|-iJYN2:\Rt4hųR}(3@ k;TL,-)ܯty{2uGfݶ<GfO-(I b}S$DPva)oH+uR|x'T|7R$LM;-g.Z%{y cZCl?esI Ҡ?&k7ڍS P8>VG8j`@>O;Qdt.茚lRkNz6XV2o:ŽparZH$2O8y_N--)>UףawP$H&VIvhqIjo8/4-XR?" j^hRt=M=`|&nN vp77:Ehjq,Jtfk3DU * aa}bJykd2R~vJ$. 3CX;nqx#g]vvCwE==y3 XY?)E#F'.nKڤl->O>~ui^5?wp.T4s/2 ~UyxE`ݻoLcv4`a@xȄH1 .{}ub>l4`|l`sVW., [KTp{_[?Mv=U E 6Ex ;U;$;e gLdBqlHiq(vS)JR",+ݿHݍr> '?ZiyoDJ]ESFk!(6 9,6̚D ӇvWW\vkU)#c(R]܊[^kDc$a)APfIoQo:[x۬c{8Y' Rti viTs-6E1tax@!D œh3PdP)WQx03`g)#!`st8o ŭ.)"K7is4nAbw"c:9!wԴwp.sMToE|ifnƀAW ZF?i2:BWuŮRWJiq+Օ^&aݸLd /dWRyj}%E ͜mRG#2dLkw|HϤ?u3VGrJ;窿zꚂ&Uz<&HR`RHВAM F@;;h.=hmm Dăb*q]fLj^܌7kZ4'^}V%[msomU5Nj@k(شO/i?|ŗcqlR~b+݃DhĻ s&cp 4BFz\PSgC҈l@d*_v\w.(0FZ\ RZx,ݎ\mj5v.S_87Nt#0F?UM"e1ƑKR"@!/ N7uXnNI+Eʼn/1I1FkZqW- HF(tpbf~G_D~Fd)CwG[݃4*Zٗ+eڝO^e;! b V v;GR(W [|a_;B + gOZvt5BDfBLEmx'LQgL$eY~bi@/ӝжl00 mC^F7\DǚOKh%/{Lq\aژQ7voݨ:!zhɽ$B`rEszGR[QZbnQ x m ,g/*kNjjPl־8)AdIxolvxJẄ́w6lǢIp |R.p au"nki"(|yi& KWnCmh c{+fk$ AY6G7ޢ=@Ox?[w(:(J'"!m_ư.Z}߅hW/RXK;1(cG<_T2.TXD?=֓D>hhb/Joí7X*W- Ŧ;m3Zw@j AUѠs@V큢Ip A9y D>9{` [j IDAT:/,?GY07wC3I uW* Gs2Kų_BI ЅHo.:86m.pqީ ڍuRCJxffȕ|_\B%(:mӘgAOk BUFyLcDz5e`Xŭ_HB{/Rä= RF" #e^uՃ?٪ [Y\Yi{E Dn@.W~Nr)Buht!̟,:g`X)BWh&-v0 f?؊o( ~Mѳ`0`^pHs-=zTp4Qh2 9h@~It9 8B?=NLC-)2| <ɚRZͲVcJ"U+$˓EMnMv☵ph 6Gk}Yus#g:)?F}SKd5K@_zAavOecOp8Q֫g0/YKV5|}` XĹtI ōa1Y/״1`G[}-ߘ\eG|_^H3]Nj*q8n14b+ f˫BC3PIVdEL5\'CUqQxT pm=\l#l-WNYBqk xqQ.-/[iD_8[ˊCyec %,̥<鴵+#]aڠxe>F F䫅/ݱ2h\G2\Gcq38XNo(#&I^fp+y$^-@È.=-/>sXG[ef|%USLEYty4Iy_77%[H 'Kyc1u:˹|u=t'BO A5\yf-Ҽ ?o'.wv-ƻh4ji.3w:dh+ϝ3DeO圙RYcBc* u'ذQ9v&`7sMuuX烓aIl7rM+q ђ0OjrSIS@` VTuʹzNp1~jBRbZE1]>uPS?39od$ +B}R۸ :d y6rܡʫG I]L/+]tn!R]òHz4ݜ{68Ss.}]7k VV ݶ,缾~'VϜT(J m<|*!gEYEEsQN Ij٣FO=a-[~kRZܤ'z=jb}-ߘx15{–\{}3S8fMX/pnJ_9~9q=7Z#WNq ϵ''O=d_γeNs6L:4p0'wX;5w K.δy,sٸoQ2``ΖMӑ~|у[/tli7^\=$M|p'oˬ}njlUvlȅ_#14$ ɉ!|&IY3*jR24RX#aX,z2/*/ndZ˸Sb ;oYkxl{ltFN.Z̹w>ȔvjJxt0\gj'F߿*5Ɨ俶Vr]nuvl`[ c v m:;njca<]2[m/ZKVnqq=`7&@}t)=ײxh=o\|׶++A5z ft%7SbZ{(#-;էqၧ 3n.k6sh19`S;w?3>66y^X2]{NTO9盻q 9p\m𓇍}27e܃{#J.3(>MX~+,\r^1&StoaKmkV^ZǽV g`-3囮[z|p+=yn勬jM_RM_zb#7#}'2pɳ_}zʷ{_޹/wfΔBPЙ(\$[_]($>٢G #9Nd}PW- >橖yCgXgTkWuK̮VFkcVn6[F=q/7nCS|/sflY.w>;[ɳduZ׫VCazXo3sva:9[Xf:kq_miG}dlLB#orV7^^XywM;и  gf~d5>n;:rǏ^Vzs3s{ntQL~@ y̌²7<;-ZQ~!V#$.z5[%p;}BizO680ieyfb~]N.יrc- `{__NOps?(y㝿ng[nଟ~zFt9 ^-XॢQoF#VZal])ҡÙe6L=|8?wn˯znbvq]q^/}K3ˑ*A1cvm6Ȟl_b؂fHIϴTpOB/bt3`K[S*јs7>vcZkw:0Ʋ㧁g7MϲLBv<\^*gs`+c3_ܑߙ39=زݲsNN>f3{ R~tMe79QΆ)\oo7[`^p_ɹayݻuf9`WX&|ݸ[(,ѢUk=vVSgpûfs Gsru[;~8`2ds{0.'?2`RAޅ;wٕ+^\yW!pè^/;][|{ 'V*c\ C}NȸF]AROb,9!o-ổ1ՃrEk(x;.*u~98z]3σoztRe-']3]VϟP7Ln[N>+- {aR٬7YY/7vw #ddluwM;|$4rC5yoOM~x݋q7M|56_3/ n wX[nqGLl)mR)0Gz) K@]hF0ߞ@vHһ74.BI`^ ROJ`~+Y:4#qp-.8 YvGN4 f=VKXl.s\溌s!^(`)w ^ q؆?nl=r.=31vGmEhwnuG*mߒO,2eo^RG+h}?)s`F2kW1cH۴4JۑjsO#V2 qKhxO2eq9pp]yW> n^f[;㙥YXxzGKn3޻?f/}rYܹ?Ii^*8pSO?7/FatG[f]h 9`&ȏ>TGT4urBh@%[FMKU,\o him@yr= 8IvapL{anV03LB;1|7Z&JݮT&?lwvWTS9iWɸeg7EˮRhwrsSy-79ӟkFz3nirm|j5O$`n9ŤhE_+@_Ƣ׀q .BvohDjz¾ũ|u364` ۖw'J˷tǘāCzl{kT18o )/XymR]yb{'Ͽ=Ynx‚ ;Q*︽l[F~1++u@?2=VquH- >-RVhDwÔ^i4+ueU- EA݀] OYnǓ6Hћfwa+cl{锏HWV+dt:f vv,iLao~v9kۣ#277n.~q4sz5>]UkV.8O>mv#= hOz41+=KPI&#*vREVGU?R(o3gϏ8df2h_cW(]`7'rՇqf8ŹG71 x0?_?m[7׮>-~$;qB{G1;9 Р.C@]J7( ?A3LVgo>%^h)ԟ z7Nkf@_QEȆ-%(p{<کJl0_z='3{ai{G<e3f\ц:R;RhNMC?؆ˇd RepgU攑&/ h3}4%ll&G>Kb;gEry4KYQJDzdk :ifk` +OF6KBlB~]K`zkLo-Wb?ƶ=f\`ܼE/c[wb-z<[&,l'G@Kp%(CZP' K!S[RXvo7J?obZN7oGebyRES7܊;+`aS{lSw(<{^X+n| 䄻ms0]Vt9Vܲ&78耱l1#/`RP9A:G,Ƚtig0e6աGq`3o5[БA-Nu̦Bn+dƭU7Ɨ=| h78\+fFxB"۪?1ɶ:PiI0$RbD4TGB]襫B-$ -VER#AJܢYc kSJb9ds;Ruك?kR3ԂaBNO+YJ^?ҵ7VR>`_Uڵq~hi}GrB0IDATl:OP_kh!dO @zӗd azR*#X؀%5[~=VF$ՅH0?faj%$@;h1GHBH^zaSS7L6eAdwޫ0D*`m?*9MHV:>ě" up .3̏dJN IU !<ɇbFe ߭LVOЎNk4x"^d*P'L4)Lڡ .Xu>~ k9VJ/2,7E d3KS7]4U=)B]$8jР`<>^Cy$#"L6$izi44Wpdǡb $#9}qCE\J=B $Ѷ*ټ[Ua2(-- ZIa"X0U]"g_%91ykdRvUv>eT9"iEPN Izem*Jd-z#ieYfp|s|XPwq, BO _(co~Pτ/e V #6$:qޔPϦd%4vbC~%e*FR)6n'=_ "Uc\jLHf? 6^yk/@喴س*cPM@@9*DKW[ Bތ͑P/84<5rɅ Y@>-R"KcqEVѧ}E@wG6~ XkL!-r"1AF+B' 39"5 w,%:,&!Ö`ArȱA$^pai\p5c"'Y=+'Bȯ1l3BUuuH,$-Ehl`w̫(IW/n@~T_@ N u>C]1ADB]$gX$}yK'BFqR.Id Z2¾xR{\'dd\o` {TuR D :ݜٞl_]?$BfA#9&$ b (҂[Hy"~ qV"{[*&L)ADMN\(&NIe<\R~21+(lܼy&[Eԉ^k4* h7ÂޝƴuPW/n@!SD:BmTzzS:t^Q`$D.%yc>K0wjV*=`%3|m̀aH;e[dq%"t)6 m jl0 j| ,D{E1 )ے땊Ȳ1=3xQ3$m$/; KZTD'Ћa[*[*Z-8D^.>hdp="pQ{$]"{?TN iJVL@0٘^DΡQ7ټ#L*dJiRs~D}83DkPG0 2sD OQ\&r #)Δb'Lm)oe/kWnva+xV8G;[ZЮ"ANEJnvAVN6F'9RB(H RWB7]$+dc%\' <$=(chĠ]:ޣ0zJ ekBf zY"LbܿeUp K1u73D6$H, hGH,hI+BV<9x3{^A'[RGC]垺ꖨؠ"KhR&z H \ysHJy@a%vqP'X~H%²A7)Q0N'^m_ɚvQEA>wm_hHnaZC4X SKuEXRR3*,J΃a 2#`zvRD@;Tц #+(+]>J>"ZlPN%F(<3)J};RjF DDL6rLٔא4! !z iHCҐ>א4! !IENDB`gammaray-2.11.3/docs/manual/images/gammaray-qt3d-geometry-buffers.png000066400000000000000000005060541413310443000255110ustar00rootroot00000000000000PNG  IHDR G甥i pHYs+ IDATxw|}(zphw ).9؉%~N◼DZ)q\dUJD+H^^zo[")R "bof{ @AAHD4MQԽg%h${=0L 0 :\&STǵ ȝ" /q<!,.*:e0sf6 |$E"`!ȃ G!Sv(iY~{ =AAÀ,AAe,AAe,AAe`A?#C!,; !dY.6!>ApB0!1Z$D<baHq< ;~gP=GͱR !d&bTzӭ8$~?g6~? xwlD2JAqJ4="JH0r/7ݳ9nFc1&@1 Drþn9gYV$%8.HR D"̘L&cD"H$š`0!TdeY\~!T*HraHD_K¼w40bxO&d!bX$1 cY6 4P(FcTRTFq`x @T,KU# Ȳy~o?RTIvwCըT B_sf x_ڼqÓOqs&}a'&;8e78yojn^B.{]ÑN۝bX$KK߻Gro0ϰDk{¢c9B^^^7رۅ+_~F211>}c~stt,^QVfj\~.,.ݞʊ_T*u[M4^jvR)DTkE_wJKM&oyrs_~[<~O恃>/z<ޜl`U֯A˭6/}Q./\ǎjnnydNMD4eXagYvvv.bѠJ@8Vk4py>=a yFðP() Bǟ=w!L\qQrɔT&M"Br>Rd4φ',fEQhlzi_qThT;zuL*:{B4{٧u:} 6m`FTa055vmQ/|So|MXrY5E1hxá0NHT,[-X`fnn>4e4J%z ׹B $1qx TRTR5xeX* kZjjSm,$#c[6gX-- ׭Ym={|ayei7/P(17@QT",/+(/'9"JqW*b&XeWkjI B7Z\SK+˰,~ZΙd2?0PXMd*5+*]-mh$x"iㆼܜh4zr\.CA@*:&B*$y qWuMMNB-ms"ZLKJ^/~⺵D$)jÆu ۇV+ID2TWz-` x5"DJ%#\&gY5Ěիɩo,O&PXTlټQR=v@@-v V89qg^nDT mW¦KM2aReѠ<}欐,4 c'].<5=Q E,|6lV??pYGd$}hVZ[y}BJ%ы.OeKM=%X<ͳWSSR)}UM͹d4ĎddYv`p 0?c6@.zhikH$<kא$yYjU*|e%yyS@`@"EV1q[^ \lٸ:q+D`iʼ3?{ϛLF@ղzE㥦*MӅ=B8tjZRqGOؾh4D"W_{;{<UU+*+ )M5+Ews"NR8Wa6uZaN7e% l:nC4}ю.408 ot/49sΞql"t8E`hzڹ}[}~^8711~sVVfGgWKk{ÞL&'!I~dۜΙZDL*|sK+E;oGojj'xTiq1R?Z,roM۳23Z:::mYh,UQ^v{=>=,-) lkf;*+ - zΞ+**x<=ms =kAD"!J߻t~k_ڕ:ڶ~s[{{GugׯW[ۤ=FCoݓJ23cY6 bl焟*ZZtcHxBd2,WTmX:}"LU*/YhaO<ɖ-(:zxSS3O?QYQ~ /+rrlG;*  kK C$^AAdnvȨ?y>O {Mg#o#Rf˚qzhYTi̹m[**ʦ΋/;3(B:JdRT"H%h0DQ"Vx8 $ER_p8Id1anϔyyC4Bjv0:|i̥f=NNN M6+*,}*D%%E]VyhxdUmL&]9v0˝H&oܼD$qg03 ~VhH,.,ljjy> 8neQeb񎇶O8⢂Rĥ&rq̰Rmt)L(*).$B.I%djJJia=n B)`ܜlaYr^OQap!@.V`V,ܱmj9++-Ӳ<$\*bvukp<72:508H$FFFrc=* vը8e%$Itd*57??22vୃI}OW48T*UWWF#u:5 K_!LH'H"ju=}@D1]Z\LJt rsT*p$tD"H#0\.zn;~j)p?K1!a5jV;::FQMQB]h4Vebr*?/v $Zd2${Rðdr9?0826L&?]Aϭ}kVצIiJ  %> 0 Y q LFcA U5š_}F ÄIRU2u&[[9Z>RL*$X88nXrs1 /,p\"(00x e!y/ ׭_;tt$W]hVn'<0 #pWM81 `B!cLXpKD_+8a4 U5p^\,Ijq4a 5jlH4e;*NJ$}}֯U*kVd_hPEQTUeT"YUM8aeM$A8.Fℐq 22U$Io_@,  bh gTH$99wT"I*rbrLW1Z&$XC##nzEmRt:B' p Ä6!*ʼnE UaXqo mY6[ᅅ`0$XB!0L.n8755q\2bHs90nwfvnHH$HDO$mYe^v:7nXo2ZZD*XoR̹aֶ{~ $IL@ 0;7o b= t 222̹t6???>1H${ :M2,/77 <$'9|XceZvܜP(LX,Y</.*\ðr:  XzƺK&HNj8yݻdgb/xaemEF0):??=8Yŗ^,O/0nK~hT&mPrE~^aXUey,}Yef<}+a&qUM͉ G(p8d2eoxIlrmr'ȰTn8s&i[fBDok)/+eYw VVhoXƣN ]鶴Y"h†EEpST·d2̱'3$AZ;l4kB#? 4E) D}o|c9={}W}O%WZYBnQk׬bX#)b'͑+򭿨[㹥k4Ғ񉩊{d4nڴ'X\.ܱ-??/baϓɤP(mgWwzg?{E"Q捍^~m_<D7޻gw{(VTVVR̍mխ|M4U7nm&4//׿yy`JIqQA_e{}: iڼycc_R8izUmMK[ZMff&qV%m?f(_s]ݺmu/-I导Bݎ鶎oT*;wlI8?#Ϝt~SF Aw;n\*<ٞH/dYDZa*s 0a/|3z ;<m !\tBoBkq14l#kt+  #rG3bؖKϯO?+ݦ׹t?x=pi ~ҥCUq]n_$)}JK'NMDZgU^ucҿc8޸IAeeoxQM0zƼ>oK[յ訛#r[{EJWGwZ"YU[\{8F/R!eq\VݺAܼ>~?nExu׭"XC]p߃  B   2C   2#yK$n;1 b:G0^QfQ,?d*ߏ!(ke(Tq: y?//FZa9AABe2gh~ E/  %X  %X  %X  %X  %X  %X  %Xȇ% zR)=AA>2|, C,{G 0uZ Mص֙T}><ǢQAd)`-d*ݎL&yZ-6Aa IDAT#p9q,`fW^A (ZNT  %X  %X  %X  %X  l9iH1 JK&2~J_hܲy#A[:`6[VOO_d\FR}41  ȃ./ 6nX/wtlm努ҒbRc N2hmVw{kΚe3ðؿvݞ/,p:gV)-)ưoDvμ4Md2يҒbXtӕ9kDdjN Ξ۰~mFFD,A]&XzB͛,f CqQSOUX<K%ۊ?0, "-$X IHj?Ȟx Wֶ6D\\\tӕc⢫nú FƣX*>H  .͛6ھE&&2 @ R;6mpE=t.?~;Hd[d2SO0 B"??4IœBh4o^LͻyX,b'Nj8O$jWVOav.*͛bdjWV,恷ggsrl?A'>Ñ_UY1=x~bb$+~q|{vWV"bJ\X\EEho$9;;wĩyN~ÞwR[CKy k׬*,($H!(rxx8x<Ͽ/+Ja['}S gFƦrs7֭E?mXvͪήKMP877GdͭmfimE={qS$iSݦww  ȃ.ZMMS g ~H$y~}Ö ñx`Ĥ f)Butv?{MP(Z\tq_|?ȩS †3r_} A4߶u-.=_B'&:ycݏe%ou/jBᎮ?'oimb,ˎ1 SXร0h+)\f4D =?FM- 96PI$waa(L~ ˱r_h`C$!d9aRz-y̧_().z',_76do}#d256>!˿XV^7ղ⻻lAqoill ÓSIҿ-ee%x i3;ۖH$|>GG=/'Lah,s0+?y?_z܅D"UR\4t=Vsex$b'r2r#v^xIؕb).*M*rJ9R)[P(_|WyBeި46f[-+W@Ѹfu-EQ"mMM9{CA?EX^Vlji(.*dW%qcOBWoRt h4ٶ,Z$bXcY\.ϴZ֮]uR4M ߯V{z 0h*0A􆲒O aJʶٶmݒm0,L ϋ {@ac|bW*g pTa2"$HۭR)#ȴy(jE7ia"nX--)./+=z3P  p a5+?Y[2#úWI>S.7adUU9م9b64jM2̱٦mߺtGwz7sTR4L&B.' =/]nz}DҒU5Nysd|ݩލ0 ˶e<ݠ+0  贚_z%;;hꚕ g}tӤd{ŊJgP)+;U3gWUd2FC$yFtp$rqA$j4Y&\42:y>bhV 8^XF/D4d2^7a޾~ׇD() >Tƚj^q\H U. H^gXT"Q*jk,fI>77P$M2a˥MK%FS^VZ\\(JM&Vx^,k5 \.רz LVreYY c$3#j1c8ըssrd2Aɞ'-fH$"B&Z- 23A=f2~;rMMOzRT*Gn+eRbqri233(2"qXjuZ-EQRT.WT)J4MNNbN`$Ѩ%X,6 & D4Ij: ÔJE*EzɤZ  RdRT"6FA8rx_tI[  AAd.BA> A{۰P  k(BwjB `!;hh4: Ù&NM;bQ^n\.[ ||A}>BiIɽ$ N;gD~^J_t E-K"X#"33#B]nwnN~+.{zIӢу|n V8x@0@QTfFFnN=&yFD\aFy≄-+h0AMX("Ib6H$eb8 Ba32,9<{z{Afɞ'HR,8꒢B.i< ^ywndrj93;J1r4?/h48x[HD9V3. ^"j5a8VTP`0o``0P*VָŜH$:-H>疮ǦŲIyٶH$ϝ yjpoDcLAG0OQjbf5h,6998NP+ >H,upx" Þo4bŤF]RR,Jz{E9ZY,,;7??99mYY<\XX% (qVuYGoF`|'>$0tuo\+'S+zB,s#|:rrᙙhj8s&7'{br@,G7X-7e6LFc*ݹc;ðzB;:N{'EKR]ݗrMSSDOo#vfefNMO/=g7~o`wG;:nټn G&&'7Rn{G"ܟY`Gc7}kbf7o{8~^x駞Z,7~x##cZjCo?n̹}zL^ymߙs{)FsΪUoz٧6A[[KS/^U9z>tzEUiIɕ+=hԖn=I#cJ|brʞر}[7=GQhp݇927 I̘֯}bcncvv׮Y=;7wYh4׮i8sj1?wO2ůsrC۷wʊx<ΑcuedX?3©xI,eX-.ȱXYjxx;JTVH$L& cc]6EBed'#Ξھ7ž[TXpl{{Gah0}[S #5u:MӇ={ Y/ŗ^eehQ2ĂI֬yRS>٤R)_ym_M·u zAR,b&~{K$Y-fRQR\TUY3xf*aHA5+ilm6_,paaal|B5C{sj*YٰvݞgfΙ+[jj6y8cqySV(ڹyH~ZaC[2| P]F! o瘛?nܰ!??/ XabؖP(JK+x55֬8F޴qJqoosL&ݰ~-+s 6M{D,^fu$iٲ֭[jUJ^}gaTku{>EukV/ 0 #0 q\&Hvu˯gZMzɩP8njhrI\zBhϵz‘Pv n$" XjddñX,33s۶zc(I ɒrhP. ՂD" ^zs[sV>mnkX,fqc #'NJeYB IB,/G>d8Y,-79{S5+[(ڲe`xwnEq#c/ ɗ&,zI Y,h*,p8PwT`A}%$I8yn/,r'Jӫ ?q8#-=}EE@Bqx\$ -7nʫee,zz;qa?)XDnna4MhaH$ mYE 0eo !T(쪭0L8\$V6a ں~TCCSsk0t<_]Gye6993Oe!+}ӟH${s$8 XNI•8q#?>8u{-c^fdRo8]g SSEkUJd2utZaϜ;ۘrl.y+*x/*2uLh+>sVV}lppr\< o3o^^O_Vx. l\0=}.G7e.>n33v|>W0238h:qtunxZfՋryOoRB@j33{z29 K.9Pfʳc98t1LaxwoZj SbtJb|\Z͈!JSR-+-9{Ez K,ohR*c׭ócQ*3ʷ/ +X4//w~A>k׬o|퍷d2?@P$(0p&2%&$0,/kni? PVfFXhP(٣ǽ[On.!>kO>zbDGE>vD(*\vƦf  LV|I$.]H"@=(*\vK$*_[RZ ˍѳ0hx|`EYv~( -00HLtjD4?7'&:J*DC4aBP((.Z.^ߖG6&A$˯ \Q|itTybڃ'O .).xEB{bqTd8wZJ2 AzZPh\8J,-) Pgnrb$7CO W.mڸ^$a.Yp %EiFl1fh57zp>Юffgf.Q.+@(JDG3Yx!5`QեEu W#2"p)3~=UoZ*.ZzC&ᤤ$3&V=3֯_@GV / a[HĢB`f-tqCzzRS0,,,,?>}65%Gq=S^Vo6[Y-IW|FRvVflLtjJuCϝs/쯭{'R8,crrqGFό4ؽ{,,,_j틢hnNES6 :|E'^9̛Ȧ"1#)IjW-ʝbaacN  =|f_6! ˃%Bv>:X,,,_b+ nbYXX@fE"?hYXXX`x\}eEࠑ?d<(Q**rX A*R<@ ˗PJZ[aYXXXXXXX3ra, `gX>I>#~„aaaaaaaay@nH[ `g~1:6h\EQ#aZCjY֠@_ æ !!ZJg5.xF?'w 8y.tǬra, `gX>~왅4=:' 7,F ÁuXXXXXXXXOW\=-@f/%F }.8MIؿZIt>M~ߏaAdz\|2Oߏ4MƦNܓ $I2$I4A~? PH9AQ=7=ð 'N0i;}pb38n*LAnsd{{R_b( w{ڵ{yvQEQ7#A佶9BQu/UlIofg쁤',,/#G6>=V{*ۺe%~iT"~ӝ?NgE˗+r%bQffFIqaZ=bMӓSãry<#*<ϞuQOl߆ڃ/ ᇆ]{[7obtxis242"|W]8_^!IlX ~#x$]nik?wH$nޔy5}>?IyJeVF۷9o߸a]Tdg<{ n:l@UV{pWlhAA~yYR:I>tCyࠠɩzh445쭪6/'i)4}'9)QRݧN3O$0<<2_1$ϞV` %V(?;&/4=5mvz_u|^(lz~^K>HK(://wNPWWtddnNg/`$9::R)% b`T)Q1Mv`BP)2-y}^'|~cBQpP6- Fpvfc `2!1?CCC ;vU{[ߠ(f[-V"%bVa p8i@ "M@HhH*CB$b C|>_P$i0' JB!$i6[(p@A@Nv֏~Mӕ5&H,zWkk\QV@!aJ l8!Z Nns\Iľ DGGDGS5=mx<GRI$b75=j|1rzUm[^7z珿5| |Z-!l&$I.VB`kJLT*uZj  (pX,V VarY-Vgw8`R)2 Ak 9sH(~cOsiC*055vQU*2 v;EScU!r\p8,+NbHBdZ>N)jz  (_ zڞ΍ב$eXlv;M \VAMfHb& ᑑSO+2BT*X$"brrr8J)H9p\kuZ+W>c'N.?|`ںo})IIe~.(JؘRnd21L(h4\. FXlHlljt qSn AB)f0rD,q|xx$44$9)^Zm9مnx `z\.WVD";95pNf FTTUUSBic0p j q|rjrR%oO!?(]fʥ(llvrZb.AT*%IccjvG<z~L,$9m0:NAJ\. (fY)JZirq=ZF  F A띑g~099UjZQV?Si;R A>h2)J{u:]ANAАؘ r*X,z}SS<.vfD X'O Q34rM&B0 @r~L&XbA1 c&@afx:F aZ6;M\&]UeOlFkIQKhz4-LNNzW?UK<OR8>JXԴT*1s9z\ 9pIdjƄ ' $˃.%'%.$ɩiBoZ8I&A`KND6M$ ݁xZ%!x<Eb-CV_ x<]=R>Us`a;ssJqhhjk׬2[,u^@є)xs`x'Oiimct" xFb[6w:z@ aT4MLf 22%bqvVƇ{+N׽`RѬ\Qz}x1˗~\c8 Fd2WVOKM1QYE!!A-m@Y2@靘\nl[Yv*P( ,^2܋!888V"( C-_*\lUNkjz::2ME"Dm]r+zCQ4#=-66G |>~䲥K"_}O?ew8)\q{<  =v^x#/^\<&*`ccBB\rRҵ׫k(2//4PWJt=Vu~EK-d< N@Aٶe33228T4.[ ȴҺ29i{|$8yy9ͭm[P*=}N>319yNf$rSS3.\`ɩ)"uV3<&|dl|nw.p8صx壣c9s.99pEe}G:3[0$vÑJ$\rqVAP=>1i/r﾿sҒN: IDATt:=R)?a2Slhjܸ}k+j~2Gxz.=-nEQ M(>S\[ljn_ 2"|||tRؘuk {+zW(s]?B!?'qə:vㆇ^^}vd2T*XsW G._D _T!A-ӏl\kWTD9M_z'ݲf_wOߢ AA~KΟ׿N#NTJJW] JIN|l^uv=g jji;},cr؅aؓoˤAm]óz&?7wm]CcjHp8ٙWɺ1#HrԔ5WNNN?r,??vS$mA+C39)vLNN=v[o,/A=ȝ\ rf^n|Iz׮#0͖qMiAA0QM ;zxEUе.]&lv{7r:]S(dc655h:-:wAiɇwlh0L>9vdgr\\14t9iZo)hssh#F9;}lnvMcz*j'/\ @Q̪arypxxdt1Ǭ̌ {HMeN|v"ɼU+`X,'%9FGE~&V*?.'~ǧ*U`egf\8hAT$'(j֧b6[&Νj}ɩ]cDEQ W( ðC+jB!zmbb"#=]&2ۭVJK|^DlL\&Y_@$.&68(p~rt?/tZB16>̐d0DGFlwv@ ں@>oCu:h|GFƎ?YQU38x+8N p8h\W CEUM[{ )1!"<&2"<5哢jЕU5ǎv}frjծ{R"h\)2IxXrN\KɉIE 2b0,55FEEj[cFn 7 X/UJNv/_25=]QUqx||bz)/W*MC+j''Sɤ9ٙ###iG&z]6u0:$88)1 C$ 0]nH^nJ"<4~rr*$88 @>}@tpP`Zj wbb233K; > p\8AQTǡɛmдNHOEQtvl6+C׮]?zlxds:;vD3'CɆaH4LDGxxfip-Yr QT:ATE5p\/]?rj{=44iA~~L*#V @RTIQ!L LM+j @Ӧ$*"B*wяX,5$$ H93 9YY|>0'"gg4)HJ% C \\(r8\$|~L(\~~o.$l'0?g\.NE \w'Iw(<9XPkިl>OtB|Mы-x BHHhRzV@Qf砨mni o%(8]..hiqTTga| Rb'r/\r*M tʌ%6 'N Oy߻vGoV pP%'% 0lbq0 yfB!U.bfƾ#G23i>r^w@z@rRH,OMN{Oa·a{=^135"ZW\16>n4̛)X?S|fgIKM=Xؔό|:N NNJifsٗp8(˱>O$%%%4tb.vsZ-l:P{v(Dp~ N7ζ-;vd^nvbB.["]n7 j5 gΜH;{\&KR$""PYijMMX<,sx'O ]:55} j.{̹Sh5ߠ?CCC8I\n77@(8nXryhH劏 if?#= EcO(rsUJy+C .|…ŋLMOw^J$)ɵuCW/Z(";m~dx0d|;w]nէ|F`&×B$((}(A~d|7UeᳯR*9YoRS~hᬭ߱s7ÙikV<=>1~p ;Σ0p8^-j#4=>9k^׋H\llhh{@P``P`_uZj 3tCdӆuU5?Oy<~B\ܢ9Bx/$V\xor<59'?cٙ>o Ӊ h~^NyYKD"QnNV]}CsK<Ƕnihj>teouqe%uժ;D@V)W567HҥK1EU8 jjv& T`~oR*ϟ?+3j{$IU ޫ 8Ȅ_{] Tܽ^@rR=:I$Ąue,-.6flhR֬Z9@|o~Vg? NWR\{z 姦$K?JtTdTdC$H?訨WL}GSئ%$8(>.pw_ @"?cA˖.khڷ_.-PQ}{**zzEBQNvfJrl JYRTX{`}xXH,A \8z~LvJݗnGTP(  bLkOlw?$@Hд@>44/z~%1{.'/;JHܱ{ϑ#G>ax劲ֿ7+H/[ݣjKLH۫EEFdY]o~t oyd>?11a~<-(w ֭}ryhͪʪrLSЧPȓ;Û暟]]. ΊEu?x͛645wR4Q^Zbw{o|晁"S£ǎ +(ȯ`OEBXQZ,wp ˍ;X?pt Zj{+~_0X͛665wtR4^^Vm+F$"<_X^V|ْC -/[+J,Zt['Oo@a8*22.6QFQ۶npoߏAJRR~^.IC~,L-22B,\zzdK/F{YaH"0ʢiv{>Dtr\@\p:}^/˓J%8='|X,x<3An(>t:e7giz<yhI$b&gU˄Ms5 IrrQH$AZmVM@AN p@H$qLz |E,q\&y{*hf4X,gy<\&XdGF%ٸ=˥R*D4D"AS}!.H\ bRT,1 ٘-Th' n tc<.(r0w8{<qw\$ArH(Au8H(1j;13{S.<A"`4~޽=2.A\W"3L[`3"f~qtr8\#St\~(AH4]~/E" I"P x<JtJ0 40ۭB^HAD"EE~f |fmy= b8].14%18.n$I8!;za3Fo'fXg4t:>_Tt>v ^gR^v4Vh1rE"En[&0E~a 3 \0#9I\.G,3ݩf|nAD"!A\nERBBy|ޞ=}Sy} EB@AbyW -Ƀa[t L- f)rF{"8"H |>K$Sw.v{` L8tL*%K;#P(_\|>Yqf3I@"$i2 saL_=19fjG}|% nc/.?z1|CGo/C[M - .w}lܰiT`}uo>Ox?gڂ/g~?PW6Pړ'%&rB{ǡ}m[fwsXfȜgOsc05ٿ`tTT63_=?9Xx<}juO?xb­->|}36`;166_/|_(Οۙy<.3XuXnui?`}~}`s|E;r/_/  НnuXXXXXXXX G>b,å!<# POwiXXXXXXXX\,"F~JGRe,A*>8;X| Ppǧqx\amo4-YKã2 O+W;?oR>Mpz}Yvac_ ~\ɇ_yl: ifO@Q4#$IŪieM~a8MV E;>>.B `gX>:X,,,,,,,,baaaaaaaϰ }uXXXXXXXX3L&wqҀ=ԂA9{`CfKMI^jL&}6=-5yrTt:_2'4 ba}@Tn޴!*2m'';+apq}j .^t9_~U Vٸ>2"hc.> -_:dcS˥+W(]\V=#{^_Fz"Ł &mjJrǰpӆ֎3g|g(w.V%&sG/r IDAT_CpqѲeK>z R\lEI^WVZx|E/\hjnɤK- M. pyBDb0[:ΞXHp#)ʾ{*8( _0pT"y  ǻ{:쵫W!3gc8izp:{z{+ M}FGXt1E;w| Cܾ-6&̹ FD,.Z,#=j(ᵡ!L^^tLLԪeAA3G1?{|CSn^QVi!isMSSĄuQ* ~w̌+JB!ǎlm; lE|>dl~U@lbK744#0\T- q-mN++#+`2 UUHO+-.| cXk[ .Y\VR4TSKŁ$ID]* @ Hرu l\pV~';͖%Ņ`4VVKgef@Ч_{vLwt)Lz9dk64EGE&WV4*/n;ZWQUھL(~oݼzGaju\O?P(DQDRN9ޑ{eŋM5+._ilnH%|>r?|>*bt:d|7ReY! vp?p*jz'N:\lIZ]e\&ړB@. ^{ϔh0w~oydX,i5=11?voVQԼ @{/ZqyrRyUbH"ftN:ܜǎ:]wqgpWU)Yd9(J)7٪ES @ @~5\~8)Q"e?~ ,fvv]Qviޝ:DR4`0,_j8 hú3Y;.]ʽwlڼ-¥^7o٪KK;+.jۼeT*]z%Mӗ._ٳw߸w"%E?[E8u̙֬Z)WU*eueE2Di~lC`yX/uwNr3?|ךUI. aj4o}iy fnsǧKXp`o?~uxBv_,r $t"V3ᛨ;԰bْdŲ5D1 w[ar,f0 rUצRi2bA0 3 ӧT*ϭK.>xpẽ A>P(DJ2 >9a.^RT4ðʊGoHHwbɢi;qqåV*k0ojI1d2B0΀`v2>kkkOe /(oU= n J~AQީ3"LU˧0̹ +ˊ0R~10_(S~Gyh4x  L*|,?/ݽ}v&|~ɘJK]HFoN0LGgW^_S])J-]`TE7 Y ,ˎ7[ HrJ(jԪhv@$4 fq-^ 09yt$ŢZσeY?`w8Y̦ظ7`1 34q|%B-[{UJo}uGP`.Μ=\?vSQWWUb<^_㭵WJKj˲gΝbCh8v:]3X9YVVϙ'nlg%[t9B`*rɒE GU枻ן;q&7 0ܜbP8o^M H43#?X ef -Su wl_SmVF=?\E17 Jܥ+*?V8Ax}>HV`͞QIQע8>IDbJðbq=(`xm; 4t}bVQ^_>_Pd23^}mSF}>Mbb6{7ϯ(T*mOh2FCn?wa**׬ݺ}glA3g};`j:ZڴZŋ{zq3g+U+-Y$VZ'EQNk+;].V̷.#- #ZZ"z_>Zhr2 LF XX-斖֛fX,B[o ?PHE øGG!j X G"#nwFzP(\hl +c'~Y"ci{7꾝YXڬRᬌ63 -x{˶p$R*xaYv;?0x؋/A"1 i}mmK=}6hN/~ή̌ܜeΞ\hx,}WޏBPb֢-fS]}C'|>L+`Nٜ˲</+3C]|eD`YiI2wHK=x|**V.gĄ@ 8t>DމB]ii؄xkW\|L&5G]rxN9>]E @Pi B_P>`( )zN~seYhH٬vjhTTMB! *˾9z}lwfY2#=MQG?0,Wtn &}u) ƒ;n00&$mw?_/c W_s`6`FQT. \R?hyiIVFD,^hÞ }PaA;V&|MooB#(0,NNNJ  KBH$J% }~ 4M;]C֋Dիj%bqr%=F(ae_T?/T*De%H$2#A|  D$I@ Giw/ZQ!x6L*%D]??:9x TcJb=J_}޻m;0 + EmVK~^Ȉ{jc0$HX4{"Y(,_Wxϊ2F-0 CQtݚ2l2LT(~_SRf {t}_{]Hc_`ryϿ@;P(cX6+EcS-:<9)Hx(O$ bR:;Gr_bk`P&MEIܵgB.O~WxV0L/]P*fgfDo>K>85}K  Ed(e}qU{>hJ pӅx{gbxX<љ&5x{GjM>FӴ| we#۝cF{ĕy4MOL(BQTTBD,ˆÑN( -sd8'0 !H h4jHęi1@Zig*P&8M^/IZڍѐ$90`7 Zڏ=YbyV,f//Ye"j5#X,ٕ0̨3EV7FήD"H$pT*EwN,wφ;g>kO}-9^`0$IE"H$q$51#QO>t'&36>&KWZ ]Edj( rGGGt,}$I"0w e2D,q<B4M'xwof%I_k*%dSFc,C^O{gw~BiP݃DT"I$I6Xhs/ Z-ӹ I M"G-}ND$ ^,&#x"߹goxfUĴDV7Fή,aFGo]==h,vvdgga`H &GDږw+Bb6@0'[Z s]!J1XjUF!_m I;1X,: x"qmd]\46n"05=1wxiZc0e٬2khh%Bp|bbテ齽eRЊeK|> RRlkx4[v As G{ ݃c8`0fZ-=}=='k׮U Axu[a(>{hZSSUarVX._qzB!_d1 .\z}CAɔw`(NٻO* ړz^}}N"h}Vj4YA˖,9PWζe8|b;)2 +-m; bE`h$ 4,[X,8+/Z0*33cw`eGlՔpi˶|gw^W<?yt0`hbW<09خ= |Ƈq~GΝ| s8]Ab.SX㩪(W)'N6y'|0b1JKGW[Hbr={hÑD hM;b1TtLjUMU#W[=OqNO$UZR/\bÑ(+W,S)UazzgCUEy[G疭;P;\kVժdRUǎ6%--Js"* soYyO?e\KB Z/_mocYf]Ҳs䝦( (ĂD"a^ JK+J÷blDz>-/-./-1FdΜG'p4y5ٙdRVAϯ,).! fb[Se4@8 Zf̌;:$dX{}JQK-HJM^kj(JY %I*d6f[dQJ fiJ^?(̌ ?w<iDBanvvUUR}eٱ zG\.K sB B.OuZZ, `O >C`8;+J.Qaz2++a$YJ5U%E8A04-lVkA GP4%E?onuZjʄ? Ia<ѐ0Ar*2A fi: h0,?7#=EQdIe4Cusl6|6iA$CðieK:|~ 0fip0*SR*Vu3UK AHTsQmVJFVT4EdŋdefB$HZ-|>VUQ^ZR25>)Y-&EсФNsT*j5˲"py9TJV1 reY>_Z=6P!=hn--̛| u XMFhoXHL |9T95v @Yӧh >ESGp8i̷9pf`q8L3.p8Ùf\p83͸p8gq p84i~)G1RJ$E%pbhdt 21:6Coh(Ӿ19o9 AiCKĢϿif x?Ř !>1'0l3]/!x<tA#nJ97'D踗Ӿ@PsΜ+~ IDAT3Op$'xp2p8Ùf\p83͸p8gq p84,p8i%Xp8ӌK8p,GAˑH4--u^MX,1:0hxJ4MOO[S-9wdXUU:A4i yHjmmkm( o^Muh7-?/7'o^`4p92>$/_iibYa-j\H]mNdfWWUa@ (JJ23x<^ <%^intI.>$/^CPyYIIqы;.++-)+-W[UU),x$I\m Mss*+E"!$#|>cN3wj/],DgWw{gW"de63(jk+Ny99rPu(JQ/Y@RbXgWwgWw4[]]{vh:U^Z4)Y,skx<ޅK;a̭*,gY%%쬬Y=q"0h0MEQv+Hl6UUTJX;.]mVsR)`x@ k*of<:wbsկ??0 Oeʊ;gg>E_x94}ukV:0cS~P]Uj Gc) y I-4Mϩ(1zDSwoo,dGyG::z{ŋ23`'ONL ! NHeeYYǬYzHAVؾcuq ]=aXVVݷjkT'Ξ;T(Kg7?) Sm6Lz57A+W,O~nө3^7+3}jV9}dZzw6&8NtuΛ[]|)p̹dOow]-$R#VcVSU! %bkoxTnN\.k:u(0kx=:o?d,?y?|\ini8Sݷ܅i3S`4d"wc$9Br2]azֵHd2x<^nv")R($5<2BDWwo h4իjsx|l|d4*r Êw&NWKk~gf`O_`0B>V4OKIb ssUM4?yDәsYa> @"ikiE\&C8aO |E4MB7@&-?73#AA=vA2y#M$H E?';hVFH$YIݞW_>f&]<Oy3ɽWV^B2T |'|>^ HcמS5TT]&{zNRbVxDcBH dge^ϙHdll<+3C$ dH$(+-,,XNLP$V%BFzUZ=9ƽVE*b6'?ClnxUE0##np&k)Pd4R|>v;0ɾۼ_O݃PTgT6Ct"x$q,g6h:ǧbV}tC_H ?AQ ~2lr2LQ?zW\~uu7o|+9veYА3v}s 8) WR4LEa^tq[{[oo|kV@dnuը>/J 9˲MF!3#] ?xconޒj/ APS,^|yhhxUbYeYߦHJ(0IJ@"XxHJ$BD"D")){I ޱ{kow&B$I>wjH$e%(( T˲NЎ]{~X55U[?|fܽ.Dr#$OL6y= @TYQ|@eyE h}^DJ~5 Gپ!%% uysssΞ0Sg^ O.[wDo}EQζ?==Ln~狗.)j^752MX0 |P($)d49PvŲK‚< XH4:|t %EX<I%v> 4: 6[ AM!˲>/GHI-#Id ZaD 8ϲl,ڷ#P4EQ$G8AP Hdʤ76),n۹{?H~gvŲ{olFa ϲ,A|>;[ٶ#fU(7\^[aEљ:#~>g#A|EQX,ǰ?cƇ ݰWW>N+>x<piD,]Uj6._߇#6ZEg0M8ðh4*Xa|k\.K/l~koBd0< ;vQ$yφu ʊ?~=R* ŭ ?0xxSFzF &iC$?0(ɬ+X|>h"'% BAZdZ-Zk;RxKueH$x> EutvSO:|lZIqonP HHD"^T̰p䗿x~[A/(;++f}>_$["(L$Xqysn~/x) i3zd4ڬ{6!Ibo>coz]ZY P0,A#02ueAT*o򛯽zU^`|O?j)ShN' @ݳ!?/j1]|* y9R$d&`f=-zvU -P~:lkx,~cF׿'0 #w`<8{vm˒?B*).Zdl^vn'B$,l=EQ*n߲?JT*ivn~tzm"{(RRP9a<^O$^defܺy?xT*u3O?hOK,Dp2jw8T+EQxiT*XъAH,ʲqrs˖ CDQ8fgeŢys&''[ zvb Le?HQբhd2ٚUfܣCI>_"rsso__:s斫x"yhssI 4r _4---%Es|,t|v0 tZANE##bH,4ΧZk4mnVJZR)=} $ DBT"hCC#xi (;w?߽ Mׯ~ ipڽWP=J-fTNg< b`О:բe2^;NĠݑj}z <p4= Cu$^uGQ{z33YF۠$[zUc0Fǽ4MXCND4M{`V郢(ktOٚ_Cj)m G٬s.T  ﭷ痢vi˃ʕf++)6M0 :rd4u[9X RyaWPfFz{gMRa6MV‚+-Wr /\m8ټeJ,/+mniFB WUTx'&::|>˰Μ]8>tz@ݡAX,[T‚sܣ4X<Ǘ_y~BĠGzWծj4<z=FC=?`6V,_hh/SK@0T|iIqQrjVfF4kiz0 F5̭Zh 76L ]2tllza]##eK͟[Fc<Al6M=!ɴZkxpfYU+JQZ,^ZbRA<~i;QX5RK;E30Щ3goo<ݴy`h߁Q<x>$XXA #q"\ܐ IDATbRMQמs>EHt=So/EH4r!)(nwh4M=}ct۝7FD\.e2ȠISmV hl2ffdTkh,/Eu?+ X`YSmHpeU*UVFP($I=(Jfx< p8#( fd2Ru=Aa\R `t8 49pVYӡl)n(A6fMvȨTb%"0LظtV2SB!Aɽtb1V!󯶶 2J0Ȉ,+)J%>M#n5LQ_b1k;1ЌT*ΖH n=DffhD p0h2x< 4998hCB`Μ> ##022&`bpϷZ-ng ˲Po *toip8R*bA,z'&vg4ըY0 Cy%s$ I#ё7EQz.#= EQa͚zE@07PIGYep?(4L6NL#0iB@tggW8?&M;ynNX,&)jll|xx8HTJucӸ4t 0,cXRoZ 8NpiIP('7jX-eY6uu ‚AGQ$f3 B|cM$qYus {71;XxhxD,kVO=bdxr5%H$x"hLs%&rŲ%evcA`(؉zƓށ{{?[Pn:}3MӍOvttb@eyٚ+펺C EP4O\hNWwϕA"(Zo޲b1DBPhXTJ%A]='O>t/g eY;D"a$woXg1ff>DP=F0}oAƃ8{W}hx?vص@]}07>{hT $ @0>ʴY>r\{ ))e%8wtv5>SK/I\Cԅ#Q>C=XFzZ2DKWΞ;/|= ZϜ=0 r<%%A-ǰիjs^BADFeY d4Rurs__CX\.k<'NFGkkehj#0˲@pH('ejD"ظwǮ$I!0j `(T=!X"dgA{#{|U" ps]c _&AQԒE*g>D"xĕ wQßI: #z{nDZ۱NǎKq#D/*zzCHB+~e?2nؖʻ?{fgg}fgZӧӯE(!! f(u"IrՒK]F|c&r镶vEF//+c[ΜK( cr vn`hm4/dfw B RPXtJjr *%`As +D"bcӨg;X7!eeu%q M--76[|dfVNxh(=@75߸Ѻaݚ/fe hs ^˟|+*s|}}")E L\-.LQ[6e+%=Lb)B7o\}Y;m$6&zŲ%;tHFvN95fI%R/Dy{yi<;xWxh*uG+ɤCz?_ں=vww" B-7o\ >$+'w.Ō,LyzݕeZj4/d":CQT֦}-RHp̹T 7STK1S&GOOJO\+2պezJ/Ϧ= JXmFX~֭/(,;{D"qzyjBh<;;LoWPѹz@Z?=}<oE3Osؘ(7zSZo^ B8_:.6:#3jYHH0lo}?xj4ŗ.\ ;J kj9\*mmm[xAؘ1{)AUV%bIjc#*x/~~u {ɤ)ibcLppИ~ͪ \ZLK-Vĉ O=Ыfs[{Ǹׯӿ8QVQ)Hn 2Vs؜Ӧe򊪯=sp yl ۴qH(z_xW˛o̟7glD8ݿ EϛhzAo ʡfK;!qMG:QNժӧ]rͪcB ~b$A>rt~#t76:w'B. 8(0(0PnwtvUJ@0y„+W\_ru`@;>>=lacdRP(rIl,44f۔I8S$IQ $Iy{y$׏ T*Zm=ꃏwefNL?mꔡRSSXT\]SWUSѸ^1vlX"Eёck!bQ؈:Zxvn>Is9gR @õFłxfv}8A -C}ChL'M2yP(G*)GWw@ 41jYKbZ򊪪Y3C{z[Z?z(@Dxa( pl6 K9y~LÁs)qZfGzj<0 cX\.c76ㅆ1 C Nl4q{O?r)A^*Ɋ/OSv8$EA AJRgAQIuM]UuFp1yE5uUU58  ) #Zl6GGEܐ զiРz?}@}5$RtՊea!L0700PVVQUU]]S;{ H$ Vh{yn RB a|\yyKJdyE@ RV'OIAj9}#G;;Ԭ\w p(f7\J5RX,"AZ,C/&J$&~~ ,+%JL&EQjj~✯Jh\y$pm[6:N. hg\ Bxn҄Q4OlX$?`,zKf/M4'6#;0W9N bv tS!zjju:Ξ%᧦_p˖$Λ׏H镫O޲iR ox+/+tT&!:_/֞_57tz+/ba PAX,oKqe|:EAޠ\2!U5GOX$4$f(J$A1-,,zP"ԈYCBWP$7[mٓnn2Mlkt l6{Bx?_ںk--7bc&Ob`nnE [XXtiAqc JJ.ލ77`3MC0 ֐B~N-7ZKECKqSlZo0 {{0 s8^dã`5^ony矹*9((IRRpǾmie8o۲$?y\[788W!+]}1h,Ù_X\zlՊ^^ٳfL<"IVWr]9|'زq=`Y, 3W\VFJ-A)dP$q!P7mX }ml6ĝ/Yt]] (>uʌiSY,^/,ZbY``mF 7n\b4 ̛9???D/\p+ӧMY2v}EYy%I&Ib AѰ;Z[o7CaYy%AlaFЎmTo0;q Eѹg?XqhG$I'N9}*5L)i֭Y:^%H>rQP|7sEM:f#RXTb;l4 ֛7ׯa_x(aJ&ѤP{{ 9=8R)oJK{z 9BD"Яy(z5~d(dR`A{G'-IX| ?>c"D28p5.h*Zpcz\Z5+1 STj bPUT*RT47ttt҃(bW( m==/H$vs;Xoor%qA(vzRJ$bDߏ@Oo-9s.5'/_87ٯvbr9㲼JR T@0 p8l6Bű'͝- 2٠hD"Q{Gאe.(00a|a|۷e2 CbA>8A_عば bGcH|w*tXVjM;vۖ4EjVp<;JG'dr PpM"b\es0 9.RH$X,P(H gd F  E"ѮϾE%>#wwz:X[7mXfjFo0l]\/Kwt>Vb  a, R|?;i֭]ь-"P"h:::<5.P  CIHP_$,n-MƦqu ?"n;{[UҫyQ2bc<^Oooґ<_¢q22x\.EQA ZZΞK ɧ Af訌̜:Ry65mU{2w^~!}} 2srmބĥ˥feY*jyE``.Zp8Չ;K._zaVXTpkj-V[]}m@M?qY[AJ%IQ%Kc#kY滇a*孹*I Jm۲KA̝=sIE~El۲hAiS:^|շ^'ɄxeUMfvΚ+T*%asrϾGvoqFkYY|~Fd؈ +<=5gRR-H$IZUuuU*eJ8tL >>^8%9rr<%1_x^\ΜKO^QUģJ$ 8Hk藣T(|}R/̛3+]V-|ɫz DDm]݅ yjg~DYEF p|>OR^~ \^PT6f ``fe˦ Bpp(鹑EJ$􋙛7#JYy 'LHpw;6}TpA ~R.Y6Nn'%3M$vljYΕ7۸\] A-s._  hASgΦEE]t1u:i3 Ŝ3gLBFi/ ]?3:eۦLd9jXfL2p+6v+EQTZo!̙=jkӹtтY3@PaضOMy_Qa@__c'x=a4\a.H;y\WWH$8wᐍ t4vl;|ZZlI[?ofE-]`)$I" -k?¥L2\^wk/Fc._iXzߣ=gxKP,^ &*bC7 а1ۋ{$X'O5L&_p-Xɧ;g~ Ao/ ;q7 [f5Ԟ>B|_:ukV-b>/3O[p~_qɥZf .2s0 {{x焄#aARnIɯvpp*bAQ`XO:[X\^r 7ۿڳoxK.-?o8A.H ˯q>uʊK=}N_k~CCCPpRҎ;V-]<&$fyտ Ӧ,H dú5%~Vx[VE]]!"0c]|ϵpb-](w}t:y)@oo_YE%ao/:__}glkEdi6}m6ysΚITБgRa>yukV檪/އ ؈׺J$n%%[W_yiT쬼lαvglPRrUu = <߮|6fϨcL. IDAT:yD*OR{)ϛ?b$IV7Ǝ RS('1-]=}#]r}lhzY6lN|]7/?;yks[ b```````faa,aqb```````f~DG H`' BF{'PQ]A`, n (bT80kX>Vӎl6w`d1J( ։i+ N`,nw-Pę'(p:ԒB> Ǜw9aNrY'Gœ>0' I歈aa,aqb```````faa,aq8~Bhٍ&I|O* =zBhٌ&IR|>O*X,${z{o@\pIX,LrIX,(6-l6ts\Df{vDǓJ%v?*(lZbH,8n4J% (XfRϗH("7oZmAT*i0 0DrtvN!pnj5AjQ٘T"p8Cso(d2Y,VX,M& D,! Mf,mۦ(Jjo\_lfB@* }ALf!OIbR$)JA q@T.x<:[b(e|>Mm6Q" {8s\DaJm6d"IJ$b  'l<W*qth65S4"C%IR7(T**!mn[4jLf|T*DQjX,$A"HDdalLpFfbD"$I`;|>EQ6d6Q$q% B-d|N#piS&Ϝ1](8u!zo5wtgϜpAp{=rEЭ6H$!#oܼB-7 퉌FW{?u&9CQԀV_PtM֭XN7_1q1W-dL:::{Y3R+!Ei4=SR"j_r9Θ> 7Z3s75mU+M:V[Bqo֘w|`>ngf|g=<ܑS̯p]p:rlű1Ѯ<ɔvJY*bQ8lNI;_QUm2"F[ZPh~bss P.]"rX׍FӞ<})O[jt2VVQ|L$k#_MWwwR7Q۲i՝|f E#6o\/CZKWHpw۾m7BFVõF6?q{{{_xϞA46 bz8}:0((d[hu~IWwH$s}wC/\̰lE>ddeY:γ)acl%=}?R*=q̹xqmz!><=!!BFB!4q#;w G%d~TjU$ΛsBv2}/+'o.t"sM6bfpUt5ܐJ }x:1LNE巯/BX|rgW|T9s6ZDNn>+/K|gӴZ݅LUT:՞}VZ^Zz5'/Sqa#G,͞mX*^[[wjtuugdfk<c[`'??s.GX2m6sAA 1cB|#@3rL&K/<';/dlXZVJeӆ[^EQMW=#>>/ddyxU41a奥Ws=5H fΜxC$I|3){;>.汇wq1N$#"Ѥ5:NG Ţɓ&_shjohTT>>dH; xZfc$A@:͖' ccZ@)S'Oؔ}$ɑQʏp:ZnDGEJT:uʤKW\RA<-kGgg{{ǃ(Zm\.W(bu'EQmWʂ\/dvj(*JNgˍCrcƴ 2X !"|p877*rB>!aeߒ65d |e崈 ҫeӦNSzro//W&AOHp1#3ˢN\j:Ξ12/6wm>>>x.) J"v:BrӳJYN^BhX7$Ν-󃃃Ͷvf2srϥWVU8AQapP*_/(4QqJYP```(dju:2a|P(: d,ݭ]7y̹S&_v:>.V,aFkAW;:N$ޚN0VDDU+|IR>eרp45*2\6a|իwHˤRR1>>JY9A]J28(P,O:jY9EQ:2$(:;7^N`6Ϧ_hH8ΖanjT2cԒK]Rn2 KΥ].tEl6z `a,ƣzlX,כ[͡[t okoD#9ZrYhE*ri6[L&J5bK@0M<W( ap58a2y<@( @R7g͘.<5Hޯ^ZpAbB|+03?qEQƢg|$fw:FS 5uK- VfI0J'\Ĺ_|`nO=(aa!-J$VMC!z!PR%Ǖ0$b񼹳? j<=\jz|\SPQ6;~p}j2bAU j(\NK!+*LP(\$anj5{2qiuA [Zyh{uMݽpX,ww.pwswI9ɓ8t|ߑ$::1 먬ֶj2DQ=Pe6ёNgOoo҅ tvv(jYgW+8lap0POH/) B Ţ'$p8Eo/O?ǔ Ӊt: &cW`[hr3[4^^QQ9iZyrw.dB^b6Lz~E6WV(Ӊfjj`@GQPwvvuvws+-6MFSgyE墅c"Gf٤ njU_7ÇB7vp_Trih7gZ:|$Y.;NO/Ղߴh> Eo"ήꚚys} ~Z춶ܼNݾyߢL?/2L bu ܎ @|K:t  Yv5 I2r\N?vP(\8?{ݾf$ǎwѪBB!@;u!lkP*afuGxX(DY8bնla'mƚ:Éa޾K:;**+ hؘh.[S[[SWzX==70i℈0> E @{/DDktߋfCQքa]݇kCjj/]^|\woYZOO/ !h1aovRILtFSz5zop:<͖d-fY* hR) 5R D,j( e8 H$b77/G(D"8uSӨWbOOs8@t775ϗˤE[Avl=1QsSv~[T*=kSrr2qS&MFQSxeuh/,3&hu(zyz:ΪZZ:7Ymָh:KQTOOҫ3ӓtYt}6JˤEŗL&srR.ɄBa|\̑c'l6[{GGOo~"+zCn~IVV_Qv8>zA8;¢으99D"~٧x%Νhbr9p$}.5E[3 W,_BX,iA ??X$2iI}}|lK huZd||,Ae啥Wl%.mKq.Z4k .8c"(*26fN+gV.[dJupװX7 3LDQT.fb°Ղ HOo/}ccŗl6[Yy\&</&:\ZjvlE0adl%|1 D?>Lg\L}q R((*6W*2\.l_=(t8^]S{bfXhP({qhnٗ{F-_صt=*r -.Z4{ #f/].tZmaɥ[-VQ\RŗJbY,`0446rccY,TRVS[fcR}}Euttԍ 0 ٹ&&$YSW_PXt:FcJڅqN'eq*Q)i-VkCcb466}/^BYZxeU]<ABnj6e};$(;|Noݸ~MxؘI&}hЇ=:L7+"][L;8? JfLl"@n~g A0vhWnK/:p(i$_m[6Q}g;}R,'Ν=ibHG@dтƗW܉O6yDHQ=Fq҄;q=!:f:::8\]Sn (r8 t:>#+;( ~IMp0P(L[z%\ bM:y@kp8nn?A.^tp|ș3OKQTWO7 /]|Б^xp֌ibww7יUl6;:jE=觟'ɂccB) $MfsUuAd:?4[,"֮Z!M--,?/*3O? %>;q^o 'jޜY'M>r4/: 7^i9Q?h lɢW(9cys)!IAV,]Pҳ/!5cA1чnelW{\nl~p &*ꕗ^:HAQqL oα˥˥NfQm߶Y6xFE]ښ( 5E +mQuGzu"t AH a % IDATmfwtA0b)oItqt7 n?V>LfKWO_hpݷ76;Ɨ{^_h>6V5wwiIB  (2'Gm6'.Y8!a%X՚w\hٺyZdٳO[bB8%艌,VXaaC[(*j\[ΦZm6/OͶcX,}EQE瑇v̜> @D~a;;'$?sZ(H:L+/)*r Fʐ~.f\EW\vJiCG2sr1kekV@QBu\jQ% N_roS MeǏ>ጀFfOIK?ffΜA:ݩ3 q'>{۶p842'rbλ~ ca]}CFfֆbb>SmݲQں̬MFG~떔>MEņA#Ato*h2ѿBr([j2~@CI'NٶyӠѸbْ莎=fd_ǁIIq;oU R~~|w~4^ozzyjΦ}]/ E5\k,typH$OnAᑣǟ~PEν.Y4^(^PȟioY_VFH7?Sg;{--\1o,^m\>wήCIGrz[T(2 $>>c;B?/˟_EAdf綴nۧfEQg~盙{(騏!+(,|٧7ϜK]z%-(޾7P||-R$UTT $I,V+#ǵhwL*]tqWWw\\cC;E>r\k4=/f94[q)NE9zp_ocλ>f :1I%kF()ټpgԽUư!}!#˽CBRIŌ~|o@/\^I/Rx|ZjXe2[m;@ղʪj㠑$)EQW%Et[~n?u~.-Ig͚Q][k]ήn>%fϚYY]p8wqdwOa3Qb,Ųlɲ-x<,ܽ>}77n$ʌAl1K3sȟlI>tWWTտNs BڮR*vn "ZqQ+n'WalE⨈{w؄ޝ8ϓ~y}ZJ_ZܲQ0@ JntTJX !q\& p8%\.۸\֯Zڃ䤄BX=}3$Irfvl@Y۝ ί!J ?pyQp{iᑱ‚3;kY&'%H$baHpMY=4p ШG&Ǒ$B!"aؘ"^2[,:RD%,;ٙ#Iw /7{WB@&v}X '[m6A%iJurC;y<222ǰ>j[\$Jssw&(bYd8RwLfv76LaA2 ܲ|Hyk2[bcRD".SXwa+gf;weժv;󍎍ܭ/[UJFgOaK[F)˲@*\n80jA뭪=z0`nnfC'DĤ^?v~dFzZXhR8tiťeѵ3%y1ApWmؒ보`յÇޙ-_MVU KF6+MVM bV2 @nwk[;M., iR)VVW7^ B "ȳOJّ8WTUk5饥%..-OMMONPPcq\.KPZC@(*{VVWY]Y]o} ̌ѱ_R{x<t-S4>9u+ٹ;~"HL+7n:N$~ɔ$=;77[+ '.t]*qVni}pp8]nJqeYZE~aqibx)|TfX;slJrRRa4&9Ԯ { BEDE%xGLT@,/[yؘںzX400nUfKcsKhHhfFڭ]\ZD$eE{[#YPkE|C#:(HbYl8$wE`T"qR _XXXh{zf短8C ^ qcB/ L ƦĄ EdqҁRR+r8.[Rn쳭,PxH[oL&7U嚝_X-* Efyvv~br&%&.?(<7YJ%'8X>v;NEPIАwj@ [!|Si;ZB3nVTތhhHL*kڞy>u!}gNBAEY eAQb(òazgf>uR*|wEEE|KRoݷ~?-0[,Ar\.Gq:]Gd!}?b B V1r)N'A\&sb奤RҴΝťSO>!6p:Xua/<{)*}>HOHy)In{|brdt(Tӣb6*Af9(F!Qb ~qx#t*Μ0ޢ< zz޸ +*7=wFX,R KOKZꙜ>PV$$Ra` ai-.SS|>?,,T(nw[G'yirgwԅy"v @dǯEQ}#cZcCh3epﰆBxAа9;22& ED}cK?{_3?pͧN>`avɩiP><:p8-9:6Q2EÎHRb+/>?6>qҕ訢po4W~g6s, (EWqR{gν;Ov;i?]X;[S'X M-<j\.aH$-VX,F#\",-XdX%1nߞM-gfva@7 " Aޕ 99Ꚑ`$Qٽkvn̹ ]=GP( v8n27_{ޛbcN8ʰosĄ;VK_x%H.9wRA^}9DvSw $AdefBZT(v$ HM!$%&VU*SO>qHyskۙs gfxB@ b1[65B@ lwY,\cϏ  .B &FGEB HHOHXgΝzFfFZ_@cskbB7V_SOIIם=;S6DF|>O,n B1Ary2p ;闖W^Vv8fEUDxP((*˙>sB_@DD8@n820/c3Ks+{W殬Gdgh.bXp2I!<4)@߼tzz[!d'.\:;;7<:::6619ٕT(6 օ*vjt׾Eq\DNsB9ݣC,iǏ{ԛҕk);*ӧ斶3. BP" CCCFD,AI<^P(p84'Ih4)A+F޸V9NxVeDNpO}D׿7<]AiEQ ]h4UQٕ`YaYنTX<7,b(pxؘ̌4_ٝ0EQQ^˲,G<(M>q|>+*,&Eq GD*&&WU=} b;;SvxhTdr:JEQߏC'QTr(vKMMa&'&@L!"<,qv~zf&!>p0"<B8;7QI~EbuM/ $ 41\p8# prZZ 33 Ȉᑥ`uwV˲kzj@?*ȲC _|L*ݻ1AU+ss!!8AYE1 |%H3O)7bwvuޝ%|TJT{///ghdT*e2:l _X\jhj ŲE!E-[;w$c0 (0 s_ @P-s‰ D蹷GO̲,y<$It.R@QQ P)BBZZ---4-VVԓ|>E瞹;bޕQൕ՚zٌbṧfVe8cqq{ |^Kk$EQ.,'g AҒK?eYV&:eٹ9ݑP~_q ɓ8g--~(0N>AH6h6['&sw7_|s.@QؑWۏ X$>udzhDAǎHCAC.\/?1BB~` òS3~?)1Ё B2h>E3SÙG Α1IY ]=]ʋX,~Q@bB|\lL/\37?߼ 07+*W&d2[, {!k;ٻnUV8v]YFw/}|>qvvhTTdfF̹ 5 qKK^~CWfM5M&SumpB VYɾUu(j5E>|UY?aN+ݷ7'rN'˰1Ғ;4Mޮw8~37+ F#O? ׅm@+k Gh[3%8 _/?JEPY>agfM=rPsKGN8f4^qzX<}31!~t|r8V)))s,ˊE#zEko&Ey<h5 _om(ٷ}A[?S,t@{;11Җx<$A|Wԯ|D" j |zzDD8p Ґ`7wkkbxABh-Vk`˵nx(dEϷ_w8<ORJ%Ee!I E\.E.gai%9nJeFl*Av0*KQtA%IVCMjcksiڿ*d2)aUIR(700H.hԔϧׯnP(h4ǧb"ukbY`4JT IDATUB T(, FP)q6>-BBq [7- P UJHdX&Md2ZuߞEQlrR" Nsvn~" 6&zڱy:Bb4 #JCBq\N+8Xrz+@pOыKK!`eefCIP(r Eٹy˵)Xqﳾ%]Xh gjzfs"#D:>F'#BE»x<޵5+ Zgfqq)::V$IR!J? &}8˲6fGEFN~(PJ"#p[7UGjGPkem=1n{,*RأBz @+Dh~~]v{5MZJ$vðn|>ǕJRxfSߟd2feAAAvxw|ťА ØLfe97g Bp:蘑I8K8[hs%Xu_D`}^x<ŏp8=w8pp p8,p8-%Xp8[K8p`q8lOaiyG1 bXMl<~#2aBH4W3U-g/cX#=}kTr/,>!iqHxucxTRF=~]jڃ0U+dyU׏G襕?(m#?!'BI A0(*O (մP侅XU׏E᚝9p`q8l1.p8b\p8Ÿp8gq p8,p8- `=~*ՖrL`j| _|9ٻs ׮ݜ_X y{D"zzppAe{:{b`!BM <PUUS쁲Ғͥ++U5S,dO;R)V[SskWO j==95]Y]yyy"peuvxd'Ç(vƭ. C'8^Dz؍*s. Am gAQԭʚAۻKQ*;:1 =PVZ8djhlv_ԝݞVŲd_bP85=sU(wee!| ErslU%%&#Ghv}}1 6JvG}cSWw/My9ǎo.45ެ׾UYJdAnNQaX,/=11 o|@MͭU5ot::[ۭVk=E:˲3s\_b- (peUfO;\~0$XyZX\:}\RRcGL&uADb/< f2[r#I8|to__єH!T?p'Hݙ/  EݪnkDQ`Yiɾ._4spfee<~ 7oH?|RX^Y#ydVF%­@|u-m~"p8C&880k7? $QO>q",4d6atc[LLN(WzjfhfvfEell̎ T>~0ww陙[Uqqɉ /^nLϺZȮ u (:Pv%sgxd~^oOutvutv?ml;:-xrl@{Y ^yy .\7~rF_?7kk qG/445 IX^|W-KQaLR h͛)*ؑ~cz[ޮz@z]L~iSO,λg2YfFlp= L^ZZ( y<%O:WU2~vnvpyll @VzA~^hHpGW{g_ nWX6STh~7J@ر#3DBasɩή^y }Ё$,ݷG=/UBrDPx{zk $v8N"IWXwAArpeeg)mhl~Q8l6{톎 b_xNP`V\n.ֶwt*A/_=vx`J|"0pI?66.J\n~y6~~-mb+EPPN@jCrTz'|xTSKKm]r} @|FC, ~):* 6…EKk*TYU]VZH ~))ޣV ꚲ%$IB].WSkKّeQq\ ..-GHVVJ$bD `X7os@)A<le{RwffH2{wVumݦ^7E1lGrR]}#0U&ܱC,l@aA>h2 AP595=8(_Z][d Ƚ@~,Dzloo_Rb⮬ @wz#ׅ8 Ha~?taa'OYQr}vi$mA|I( E#uk[JQI,*++x$mGW~ޙ:!4v==mH(j4=wua,mk{љ}gw8,Vkhh(C$&*kT,.?x셵u-ηHܵ+shd?Ȉֶ^z(vWm~ 4 @]nb\l `0xDW^U+'''  SmAlvf %IBzmp$HƭJa~{ԴR_KINXcZg{ıEr^O?lw8.{uu~DG_֢( LNOk4m2"#$b1oszkԪIH13;ݞ:PPx?.{Mr6WM$#=mum \dlln? O=y ٰ۟,7P AQ4Xq:.ݬEQJI흝qq}C#' MT~勯ndY6?xfF+/R)Qu8;SbJ{v`١e+Lohj ۖ|~}VF02<ڍ[ߠT(r4KQFI&erEc" U]]WЬPȏڻH(n@NJLLݾp:-Vkff@ @Q4.&vF)a Xյ3.?v-keu;YK_.JO8zz]EJ=CM4IZ\l~pB(JIi?p|b*CFScS qr~Ӡ ylLlxkjiL=&3,.2$IhzB 6ͩ;SA2JUZEWQUsf%A%{7q M_T)FGBB_}śU51сxix<ޅRaYqA$0{A$|4M| qϞ_O?jt\\^{.\#9mgJsK[skS/t_$ _?'&&:=" 2 x4]S{ʵ+. my<ںok&,ZVKّ/RQYXf('pl HddijO?YY]{VU\\LPPP';{SO%#=Bl1O~{go /^d>}JzQ*84#<Z]s[sp646K=a6>x<Ϸ 3s9733rJKwe⁡s/Dk?H.7Fyw{ه:fרUDB!S%P(Dл8 BzUOQ>ow8B"D"Xj4gm6L& 7+,f=-o}eۛ3Ivjy<8Ym}-i>sVw3?z罿?i욟_wѨΞ߼{BS'8~`j2_nWV U*P(}{445,y{S\녏D&o (W\#yN;pbW_#yNeYHcǎD>_w@FSP w$'ݮw:]^~lg §O=W~x|>XUM]fFz[ωG4Mb}p|F~^%W##"K+*ukzbu:C#1Q$IGݽnv\W|F nF&pblb鵵5z#AZG)ojfF"K%auC{GWhHR*o765/,.-X6ɲ홛 Rje$IJEJRP 4m2_,8ʚǏ}ONr&&GGH l+++^/70X7(bF_g! \ KW-,.~勯GEFX$v(L| `צ&Zmnڡa!55E<< (꺾յTFzϜxzzgfN=X_|pg_`6=5uphhhdBF&7A~3!^/Uoa?a #9innbhvv#,m&087]ڸA ƭ/8nX+,ˎOL ,B6P(j5;+uaBPѠ̌]R 2Yb$I$ը5jX8''%_"m$|VM>݊? MmvZy*AP(JaB᾽{]qW(TJeXXD",9AȃZD|\"HD(y<2,4$/7'"\fOJLP)$#I2(H >aDBaVf]YA@l7X-,K{4`L *RT~XwOQB|\`LRPTJ"s IDATK$}{H(sg^n6IA|Httd~nJ0,..E1B;9)Pc!E933P 4"lj@ޮh 7T&U*!/MO݉ 2Tx)jC{l\& |$IB (4I!ڼ@˕ݷ&(*@(H yF)V"J+B;swɤRA<^Td0VR!Eq17KH⨨Ȑ` 20oGR"KQ, Xօ%ÉaF 9kxHeVp= K+ ۽"H1`ܙ+htr:",Th_=^Nzb\v#fS!ܴ㣟~/짻E(y(p8<#9p;\p8Ÿp8gq p8,p8-%Xp8[i0Lg=>aۋ^ʷ֍81!3\M{^` <鋳%|4R\ux~O>Rh  f~ b?}?IrP%; di"A1p@'Bm-A'lPfr<\nҪo/٢ s3Y]7r5A0[ } 9.U׏R6p8b\p8Ÿp8gq p8,p8-%Xp8[K8pceٹ;}U,efaZZ xؘhA~K_x{=SRstwwYivu++#=-;}}weL!ݞkP(4][Wo2_z2i}Rz^L;+32"|g#}>/)10?4qdLrrb^N6i;{N=V f`4j5ݡ!ݻHd|\lVVpttlbrveBVcYvMoiX,aa!9*ٹ;.KKdZ_ =Ea߹3%/'z'&G.g\0̯|@0l);.DZYbx"X CsKd djޯE,ˮ-mf9$$87gFPU{aqqI*fΌu\ }`iLt6m֠iN˰ٻ676OLYV*9442>9x 2{lĢX$m ̌t=>K ^~ʵ/~i0|~O++*fJljnmkF͊*ժT*;i_ШC\?xoޚLNMWTUS>ZTܼUZmoU/,ժfӹd4BC$qcsKKkv髪 rscE~C:,4ҕk‘ѱ[U(ժֶֶa(ohdBC]n!{fjzF iVEp^v3ofj~{z{j$bX,:sή:D"Μ819xFFGe2Ep2 "\T(0 X,7zʵ\^Y_%ᑳ/,;55}UEL&ÎGaVrFFY$ʪڞ;}?TSw3Lf3|njԳs5uf0:6ׯJ$Ʀ6պ"uasso{!oC`ۯ߬WCã M#P!\_7=vk'&G{!<1<((܅K#>19ahXh깋 FEFuw;337 -5u-vߨhSu6F~F`U5uMͭZuͥ~ @D-x呼 sF7ʍz8m82"\$!ӃC#^/zoܬ|oRdDT"ilnihjڦ JzW_?}R m7ٹy/E --9ٻΙ^yI mhnm(Xhx sn+⇆GGPFEEN.8<2/}!1!^*4efEGFFtAAra4Fؿ-1x,˶9|-,.NJLRotl /`Y)N E.  ?$ȼ@,\[ӫʆ|o +ntFGE~Z"d75޷JVNTȤ҇ψeƦ#\Wߐ4&:H!Ɠ'a({W :|V-{]HBw=qq~7&7ivb;.q{G !TRXvmmc,Ll%FBgΞy̙3&x'''IbTήuAAJ\.0T*}o>ΑQI,]XV544{X˲-__7n)*θ]x 1 'N˯ ‰U~wz2--⥾~eB\\B|ҏ V4oN9D(??"]²2o<wB~ZEzZL&0 ?88d~ǔJeCccQqIJJf |cVuRbRL:2:ZT|>9)Qϟ+J8+.Be+KLToh7͙E\f}X5mU3EX7nVU]b0 so4aYjW߮}'*oV]r5-uX&8yz-cr(`i4jnfO8qkE孴t>aaag'z E"=2:*JFc-mN'A8]M-~Y\`~E$I4Bhjʌy Dw e 9|Z;:u:.9vf~ݚB bcc4,$v[`,;f2>_\L@  q\6j3RQW*"{RIrbûvHrd2ERn78K$EףE}$F˯cYY~/o,@ӴB H2F)0D"kWș-oVU>[ Y3<] 2 x ˲.A`P㸏$)\x7a^p6KHǹ12tNDa^yfegg#3{axH$H2jemv+WuAA1Q"g.h 8KvŲlAzL_EL:H qXHx)q J%N3 ?9|;yM6G?/Ȥ4EQS*eCG?:pH aakWA0$Iy>P Svc"‚CG=dY4Xmjiini-,WMWDA\$ |\eY{}ֶ}%6&g?z_,r, 8ԇ AK‹$ XΟP[O>;;"!7(P_˲ƱE )?̟(˲kWoh|7 aݚ:hgY `^B*h!F;QIt4CsPV[|\}A`Ϸywaۦ#}>\$F2rX=xD"X-#7[sԙw~_&rK:+WT 0 o\^ynڰvFrȻ%XN \.0ԓ`Ͼ<]*'=M&spH(($I \.&z QۅaBR)럝=3=5%=5x:0E (A8s |H$xpyò;8x];ݳ &^aX2 } ╗^H[|KVq `ny|iܼq=ׅ3+-k?tDtO{ʯШ#4Mli ]=?6[EQ˲,˲. ?z!4a(z<^rE8V᭻5Vx<J?[6K$b\$jhhe($'%Z[.]3M?x;yO͚B04n'Ȉ9גܠ!C QQ:ql(0X,fY8ΥV)|ډSaĄ;g?½ 1.bC w.] d;mٹm l1H$b\kim[0o@ 2~AS| if`ppϾIy Nq8 MjN^|%91?=}w0si׿Xy ݷ8|8. GgIN!EP0r{B!E$I 1L ڱu:ؿ?Cqdt`bW@iy B¥?vaeˉSgmvC;)Jgge>Ȯֶwۍa-Ӎ wa^ZJ% ߥ}у{_z/@x<ޑQ#EQ=z}P` *@I3a@x2<(M4MVT&XֈqaE,뭩߻Mϛ=:2jиXD<'wfw=ZǵZlZmED,xǭ.7efjtʼ99VrgB_Kf;X,iztÐB.˧$|>K|XGF ZA~D"izd&A׫TJ"IeY/ڑQ1(>|xtTI,gqs`p07g6"!==L?,(@b;#1L& 3j4 j0 i0<,2R j[ZhVk``x P5f2Ԫ/}L*h!IEutDG,K.-C䙢s+r@ij=6f}`aFF'Nl]쎁0:#9o:7x|,DhRSRD"ql}@lhXZVy*gv6.{FFD444]t9<,f՜"5AEBagW7qQqF7'K5jԪJoƨժ۷ "d6ə=- suL h|䡝Eݮu8 j1LhM]l]feú5T@wn\V۫퍍W vxhǦ,3#; ##o'S |6ϝSz=zûvPu6EK,7\PpYwONJ@Ni!0 \( ""!JKI9wR?[U7_0LggW]}û[NZ K6 /Sr͟;lijnhaaLc&Zb{cyY wAI%Sf$WDGEj5o?|>284YXDVWݾraxd^왙KJˮ\J$u \;pffZk{; -eH$xMbxbP\.KNJt"oN H&,>aho ]~\!LO;whm]}XXHP` ֬:W|!Ib׬Z9@3+bik875>k;Mu F]#=w#ᘘ膦Kbc*nܜ5+y`Ͼ-GGE)3K.[Vm0 '𩱣90aqu``> IDATy񢅡!w|E!21r2LT{{{z K/>lI rL{{y8kj{RIE1QQ _qZ6T*_"H(  ^xZ2unOnάYO@Ш_Pmjj6 /n!İАPjiiLNL\Pa6VUuCcD"YvuddnfR9:6Vzz.̖Ԕ 'Σhph "p͞ڷ<]\i_N#HCc3,邂uA 746 fegy}ƦᑑؘKe2) QQ-mB0?oatTtP>$ɉ&;:'0C}}##ge2G&\^ ؄R ˲&sН;jJ,;rgg/^aMtTdĘ\qf[{m[Z A0 $vgfe2ABGv_A~^Hko(~V+jM-mCለVW-?f+2h!&邆 nyA~Fftt4-5WUZal][=haj P(limZ8ނys 2]=b02jZj .|ށԐ`H0 DLƖVْh|Sݷ|>$; !NF[8/00a8#9I$>h, Bp:  666=sfA~2Mq]`!C~ނ9 P CBBf$%NLv˵ju@WwwXhȊeK2^qC7L\3 bccFGG[VkVfs>9i}eh&YрcZōԔO]zey?B4FQtlLtVf]'n7&)G|.w!1.z in zZ;2f|ƥoZ;ÂuIx_IF1]H<0w>rs$6/!r2x<ix<w,x)'X<xSOx<`x<M1>x<Ǜb3Msҋl:xPy>,w(ٝnw `` $INW\:# IzP}rs,p}* a. )]ESsMƽoiwE'/z )H~6fϞ>P V@WUp3GNwA<]!@~&/i[>ٸZ*1X<xSOx<`x<M1>x<Ǜb|x<7x<o x<yg ÌLz}ըU1QBpr8fx}Z::*R(Z,u0*UtTX,v=złhDx.( GF( BeY"\J2:*R*8}fj"&Bt__lDxXxX[Y+1Q8$i2Pj"#˲c&s?AD"aXXhV rutt\$j4h\8'͞L:EѴ^k iaBCH` MӣFcFFF`ip:ssfst{Y&eGggI(ёr_vGGW$Q\efXpj^o[{2ߌ$F=]5%h20 191<2̲Eg<adt qZ-d! BC(ln@DϤ_ܗNn eE T_d.*.NsŒ4pLsb\bYzZZ[{[ϝ"#Cq/zN"U_߸v`ݐpl* mvm[NKMͭ pəZ|gNK|ήSg\^Cu\W[ qk̬z}o $I PY$/=-$5]\.Rfr.\ֶvBZڗ/+jnwM]3E%gO|,5O:s}NC|yN!(APōʇwMDO)BQʛ.[ڢ"#RT HWT\BQ$ Ä`t*n\v{"2WTZa3͞=j#N0, vZڲiR7=Ϝ=p:1 l>+:* 4]U}FMHlIN߲ykhlQI r,00`\޷fr.jmu(a|$xQxX虢sv=00hnOcS XO> GnEˍMM prsf͛8eqv;.yޙ9 9F"GrH q2t`8u`xΟaN畫w=kV.r8e]aۿ6 TV^S[s]Y ϓH\]kS]n̬̅۷kN:ѨIYYf$8ͲZ< VWw3(BQy($$x":84ގٳ<O5u,@j Mշk;:!r |Պe:]񓧇 r$IϷ`9i[̗NH^lCcNKŲlWOOw~ 1Q+-/IhgWwwQx,!!|OMp,YtInHY՝۷7[۶l |N=kYyJoުQ*V./ {UuLX-+mEB_Ke>zkkWB/^*+khjv8O>B^T\RT\pdDxfzFzB~?왙C'NYb2 y$._YYXZ&vlA_~RY&J/)۶,?^++R'+/X-*.Ek6U~?#){baD3+(0P,ƋK.:r|EҾ!O􃠠_pi`,[}pvnߢQ_{3g}{EKevlʤK>eFa!y rZ4W-_4,4tMF#I.Y>Vwep'˲&7HPZVU]e, 466oT޺qVTTf|7wt-\2cߴ0._ Ȇ.Z5>4dHJq²fTViWV߮vZʯn޸n̙˯ܮwȼ99֬>v!"<\+,OL͚,^vEvmJϿrx쑉,[8[׿CvV&`VĄysH%nh+K.~'͵wAo(̞eӆ-6lX` ̿\Eq\xZjJww}\T,GEF8CH‹xݍM-EAP4MQ2 ˸vgju:5sfm]=òc&$E"QB|b:]dD8f \eLS($qfFz}}DT @c{ M}\/òI240 Au Ec( B! E$E,˲j$RiE32w.9EU޼eڲgfJeRpI2L&hhj(r $'dRL2#$)\\ro/r1qeY]qOwѷy(o5 ?40@#IAEi4jTRy%1>nݢiz :]`@X,ΙM:75u MM-gYvddGz߳=,k)r55eFdxzb"a03MG˯;q՛Lήh}Ųdz)!jlKES˲]vp _훷Hi _yͣOnn$E24Ͳ A-0L@ͪԔАOS*65q-ә* ccb\nb(j" ccc'aw8z~hjWO>y_%xbbJD<++S?7aql{eYEѤ.EQB EP RgTެjjnmhl"bFr=ƻ?p:gfeLai8 0P(N'wDPE.!D"Bp83KxggE֭YU~Z*ҏeYn[p~Va'  J^IRʛ=Ο7-Imu"* ^}{Ą%y( W$Zr=`鬼YѨ#b|-g)h]! ?pe[Z[ժ9ze2,* 뤷ͻ'ɸ~\_,,XbXX*o4̏nXLq55eFy!%". }ꧏ:Su15eFQq Ab]╝>q'P͙y1>CM-QeSSj8jr<}6<,lfVf{G>鶎N[V\rH\~ej}ч'lnieY%aXÉ D"H%#I. APxXXFz?zqJhIk6D<0d8[\BtZ 2n/:_r#oݔk*aeY~۵u@ aX*$b1EQ>Q |u:$Eq/BLhC׮^E>wrغy7+x<W*ppO]S zf09}0\Qy+:*rFrl{{V._`\ifY@D _)DD$&'%XȤx{u$J$+- j#۽` !>3a~7ssfΞŲ0־fҲeDTw&aie$miuu $IڱUaܩYHDOI'x pw?(*:*`qB!ˤ/gV.r\JkWh Ü9[vή+k-9g7\?y\Je;Fĩ0J٣;JѨW"AwEH$'H ]]II8+N're!CP` jfhe<0@kXp ??B!//,LMf3I ÁQQ[W|Ҕ3`ǭֳ7mXF{DqLs%-m;o87`8;A9zL&ݻ I3doߟ !o rldh'e2l45ht[@*bP( A0BEO&ۙi B!EQz$/qH$KmC%lvJ%C \訨Y3t/Gv)PaE!bY[D۵4X,A#"ϝhjn}Wodr-grD6p̛{6/aFͰl X,"P4[J?n^k{\!d (@(bՊaAJ\&ZRTʛBuB$7}O,ܨI3d^Q:].[lZ  5 d6邂ƭV@ ?! A3uAV 8HăCC q \{= DRD.[->O$ tALs9fq- 9 \Cŀ]\RZ[WkHALfK[  0UM9q҂%8~&?lxXح[\ZRz)#= FFߎvFjʌ23DBᵊ:T"QyjG/8NGz<K JL?{vMmDXؙsK:X+.xtAj&@ʤ}=l+0 ʞyZETTX^.ߵcIQ7oU;%T5u1ёݮx}> HQyFy0}Бo}qZp81&v,ˡ#P7'&gvr?w_~@ P˲lY+*### \صcIoܤ)jq99^̵];LA ˞>s.91Q:3+so!)j#9fef̟;(!J*Q[(Eb RK.7N-*,,iy˦ jG߫W^q3.&А`\VT|~Ee˃u:{Vt::T*W^~FGE flzCGO<|t:ryB|t3SBK/rBjNxWSp8(jji)PZX ]Um-+/̌KQ!!'N^`>GN'AduMMk>K.#|hܧvJM:4˲ ԙs+-^QP(Ƹ IDATko<ؘ3g-+,VqO ¥{:4p(AY۷mX,CGtOBP(na4*rڱ퍷o~TY"uFr{G]=BL;;{Mӻi_A w<W,[p;sr? (>.vO..kVXl;_(EQ//o>hn=́G~_qќܜU˗MN!ZōԔOL|0SʯZn7&py_r㢧 eiGtCkGƌ.PkgwXN"~`29a9=ޞ.p#7G~'=Xy gfQx}b x>x<Ǜb|x<7x<o x<,x)94)ah|[IQ `Y$) a-`(nEQ UHd"gKKÆ_EFM[0>;y=^} ?XHoiwanÔ$`߬Lo9 VxXg/p3L1~&/=% ?gr"ܿ:x<o x<,x)'X<xSOx<`x<M1>x<Ǜb3eYp8hE"\>yOz^ A@$rE}>H"H&BvA ^nwP4aL& ")f|>Ae2).q1 t: J2tr$IzYDBa aq\$`|")$bD"fb( #O(4rah*P(\M| q8. IR'A@&JRaNd 6#@. Bm6D&Jbv]P}8,t ˊŸB.\`NaYV )pebap8Haklܔ>p>EJAA\-2Dx<(FR ,|>NQ2&wzPe2E)3 @cfٸe=iZ$qβu8$E"0"HbalvC`X&&?G{}v"DGB@TD"a.AhFQT!c6y=fJ$)IJ,~ aӰ_0N'ARD.}l z RT(aw8]R*JY}+eVvU_:xf,jre? a2];LfҲ+İ .H~G>`髕/0p{kg^ri\Ų,*>_U]CZZ05eF~P@hHȶJ.^*z[ؘI y-(-\V~$IFuƸ@EKeV*@SSf\Vjj[̬˗M ,ᑃt{NJJعmĶc~oqL xGVk.[a-.^ʵ7~vYb`pG?.(@.(p-ӷQȱmecb! =k+ #BL}Gs|7{leYa-I _䛏?p8->;}y ׬Zz}3EOؘysr5ui-\02>~^RB7**+5uXhouˆK{C!͙r+WW;0j#_Hޫw׽i cz'^\ݽ+j՗bqøn"i~C >ٚ]ݙWhW8x@ 돾?44{}5HX ߞ;'uv@+oiԪ?nŋK,֯SX|?ڳ(cAoWd2P%d-a{VWWsZݕM%'%R5Jy|ŋ&vxgںБł5Nt/\psK4??_`WPXT\R̦۩ڲiÖMwBBS]sm I{x9W#YL];B;pbcys;nұ1% ̟xL&s l  RiRbZz$ |>ӉB.0LTk'ƵZcs*5@ ̶ r{<=I \,-6Y J;CÃC^B``5$xldVpKM7{jTP$I54DFH42ݝ2;Y&dҹsRM-NM-Hd2ԔںzkXn4=l@-VȒE</::02:tZڳ.r8:<>(|ty&B2 nwWWOjlT"J禦LmjpJb9~&P( #c5A7j/^o# QŢEUUՓ$IVVרj˕Zq7[g_,+4TQ޵=.63={X(ۃtWP t= d2qp<@.LfVFRh2$f0 cGgWK[[N5~!&IwlWkj;;GGsWlOB\ +fxA L&|!p:%b1(?fW._.w`0"{8$vh$!zkkǵZZMDRBzm ͙\b)*-]aPo0W||Rf'WV]X| vtI%j_2N]jwn$)Ju:Ϗj'jGd&T*|Znd0D,ƵꚚq5gj m*uNj'KY,´y{>9Ɂ@×_$B7 20lhx`p5[ <Ok{{#"8͂\:=22N\[&{rj$ }~@ ""v7Q tWT]ZmS3XʪY<.BhZJJBAykfbo\jERbɯtR &Gjo>NrHsDEvNL dDBTO]?=p:,t|rQTZyC*vlی dN Q[$&GGE2ήGjԳډ%>>>Vutl$IHm]ԅMdbuvf-VQB TLno*u[.D춖NZfP sRAA|:vizi:rh2:sC+0 s\Un{}0`91]HD n!P(\8@SK,PvLb2Eq+(8vtnN<SgjظfGEFB7hjnj.D,^(# #-,pfPՊxTDY\)*?oβ$K8rR*r\UW;g> EPjpg {HIT a#cLƵW{/M'}:ar{<5   0l]nNsKlN:axݪ7#SԨUrsg;qbr97r8NW۷NhlָvᣥcvTRY<^F]Z >r 32ӨU5G\65hq:aK m85uᨬZp=kYڸϚ=+\&)H{l6X,bCof2``T"c(((r@r6[r3vǑ' i IM}mf\'JgFFF+O9;|PW&{LVU*oF%e椮\ Bxɳ..Ypbccc P`DGGE|@WWwTdxjz.k5Aɼ!P8:6xZ,Vkhhߣ{ |o0qGTM4-|`qANWeL>zI(zZev<.W^bJ$P 3rD"fw\<otlL壜,u\5j}5WXLXtȱ˅BP PelK$H Hu /-H77z:f*.KZTn !qeUWke.Y<+72$dV(&bO0АА`@BD"rXP(n)K S"{<&P(V_).)[(z H CeUCGnȄN' ԅX.\\ZN-7L.\9٫Qu8M&N=%- }T>C3_&qvr8\GF#B-iSg6^r19q̆Oz_ekզ/L[0o.nAaLRSfS@|5WknJL( gYz!p~5jn4'&ĕWDGEX́<h2-f 8!i8hYm$ɁAP(b({r_P`v3 |96Qd2bcg%%𸼢򥙋qhniu:I(RINL\N$!5uEO`2ZZB,ht:]\b(zCqiyb|<`bb$ ss|{E1j%SFy ^MLGjLǯh&p"5%9>.&1!e&YjHl6Wpe7ZZIhX,Y.]imo_jĄkjǛ_p%!>Q|0,,4rޕH\a pْq(QLFEeuEuR8}f CIL/"!Y[_V_ IDATa0M&F!4,4`x<ʪD?O%I2YYEղkT*u8]$IzqEWf%$T>m \* wt.7Z`޵k;5 "1evryeuHH0%r%OsnaZSO윩|ML&3>.Ą8,E75$RQ|l6ɳF&)7guP`Fp8"6TvtvJB>44rF˜3_ 5\Fs$HTPX0 Ѵ0m~LtTM]}bbǧ,>.ji|*W|u˗-E$n')!ar f=s0޾~XX`a!xi.k0nl۲)&:RwAOs8?8Qg\>+)b=#GO<0ݱuVݻ`Oo~? q\/3\E],.-O0 MJL7QU3/ ȪMf~כp!I[,ysSmtQ<044w߁-*IA$ILlM-Gyi7nX=t8.*.APw8pzrޕ{4G 8 Ei+$92:{9s׬޷~_3Җ/[7菟<؄hdD[ O9wGyAvm>p2 <|pOt i vG|\ zgU+:%j_. A9G!Z}fY߿'?@]MybǶܜ۶l(Νnmd>xXӍ â##vp _Iԑ{/|7mW R\w߁ڱM; eY^YP*C|Aŋ;;?n@v_{8wuwS_>4=x-G !&g~~N⌅+Q-r!='u[M-{>b2+gysol&*88H,]v7LfL*\|CT3]YldovM&IRՎ# VZr~=nObBǹskBlpRb#AX,֖s2; ewKxg}Of?y$ĝ;= N'ɤfBH$A l)IcD(J 'A}(R3$99,fr͓o^FuLW>.|D΁țϳP5u0RC'@LF @$B Eo$"B$AS`0ƇZˀ(*_`|:1; 1;˻LϺc&w4:;mZ1;ɝзi4FѦ`h4FM3:h4FftEh46Fh4m Fh44,FhiF'X4Fh &,!AEQ)=Oyez9yrB!SVQy18wN3OeŠwpY2%c|-_-`B?ڊabG?HMIv8WJN=oXSg'?sZj}}L&sɢkd0 촳;+Bu[7oDћ6cכ[HL{z.?? eWII M>~٧e,8^VQyѹsRSJBq7Z/SLZX\z9`bB˟ع-5%ɼ8#vG a(a} &RXas7_(pxd̹Uv'?Ԕ5x<`m^{˝Oj&L~|󧎡(jًJKϝ411Vvnߚ2d@D>rpNs{yk C#OVxsa!Bx)//JhzjU+Fƿy|. &:<!:rD&.EHpN?{bqIچ0+)a"R~ĄNyb00UXRrYKwlp:~? Er/|'>.vfw+s8G{ܜUvl^oUx?̟;F$Ϝ;wߡ?P*hxbs3? }'Oi@dqƺ.w_ٶyY;'NybvK6ضyVR|*&&Id2yq/WWBnڨ%6]&+7%I+/;yjfy]nYICݗp?&!@d[{GEUqj=%e{ߊLo9+ FL*^ +G{Ô >@<300_C^JYK.=qjppARtYK\.$iVK/LmvrsO?[G{~RE8_),XÁ"^~!8(x~~3w!,--o__9uR{_J~?[&ĩz᷿AAtz}۩omxÏ,^rB *ZZ~?=wo?tnٴ11>gd3!Ԏk>0mˎ=q];T> >OŜ<}۷n6 Ss93L_'{kjj~АRq){/~'<,lQ7^}zٓg\# k2tYv1?}{/}W&VG{#bżBXh(Q_zq1;:_JD yʛfW;ˁ ں&L$ںnCޑn8qIX?ݓ_PHGFlNrrD"\r,%Q.$H23765Q%PӗWTe'||kQ%   80夦nmΙ?wp8Rlj/sR*B!_lIIYd)A\.W,9NݦT*b$I& $V;QQY񉉎VBCGlf{F$ygCry!(@83a_87.L[T(eWTN-ܚ0M!T> ** -YIi`pB-mkX,˜E BWIlim[w|GzՊȈpBB, {{CBBCe2i¢RLS,y=^&J"uYoQB?0p:f H[{GWw`$I$aaQIlttHp( !0XIj DSg'3 >' f0FӒEbhTA"$gII񣣣zlݭI$+>.6_.^yBX$XVGpi(Q90<2J/|d&''5\۷m&6 !eUwtuo``tdˤaO9gX,MccvG_`͵ڴ|ތ~Ym6î PTN펭VP( $IǵCåevgZpHNj p8?_f3;}\.C^k\.wڂyYK3"1&H2(0 '{L*x.\Xm(nݼ!!>nVRbsK{Q*}7r8A  uw0Y訙4 I`02T*H5yVF /^ E AunjXd٤1 x?h9WI$b]RZt"rVy>>N!7_Xf |VTn</'{O n^· }-L@wv+W}9bha3_l2b|5ꑑ)J喔 -6$%C cIiorRK(шE⪚ٙ,.t9Y,Vl~@[vWW N@?o.$IAP(J$u!T)$ zWwϖEbѽ?+$8;rۿ~ D"HjHݮV_$BQzB!DQL0fdR!DD*XV% .Ћu 6+ڬ$~~۷m>pں4ob㓧D;ֶvM+@PTV^ cB7C#,E1 t٬իV=ci .Xd՟>{!/}PSS; P0ť~~II &ms\5j;;geJ/!$Ix LMCP\ p82f$i FڂyQ.\2{VtT>|XEeu%nGܾU,އGED"#tB U_;Al6{Ƃ2MHDn A!ݶeS͵-RD&az:\X\nMAQIj"]n>xHX-A""žع-m8 vr:ٗ1M B?bq:uqTV!t:|QX-DEF޹؉S rtzÇ9u܏ I!@[ çD)00<`0\n?ڳﯿp8*z౽5_ae כNЖݕ`^`BO&I LLP(q8h4 E<^Ye#HrVRFg!L&sNj]0.5R^FfX(.?w޾CG^VBex\kZqg0zarp)<,t ?ɑc'"#&sOJ%/9v"m\˵PPZ^p8Kcy\.L=( bY,&'J. nwYy塣<):x\.b hJnr\BFQ"00sؘܸGCL&<0M=}\rRbeg]0M.؉S[6m8ꚺi fp8 N&Mz@&LNxr9<jy8SBd2SSSO=QZ։ gdLdɢ[dNL=<[7o|ׯ0LQ{<.q>4$fY¢ |5je;և0YL&p910A Rk'&pUWQH8uC,)1A(\.j=$ ͋1l6;,,`4ZkI At\.I6氩!k^0 .!l{)ɳ0APE""qdR*</<q?7 |>ú\^9NA5a>AV-:v/RdL${p\r7HHeҬKPMJJj=N2R[&1{VRddxmm}ˍb_6zs౓GpNGoWT3[p I Þر B8:6VИ2;yaڂ¿pxd/WB4s\.+*) !d0!榢(rgwa[7od0 Co}_f $&tlArŲ%Eyd-]VU+v Q ]dH$VWW^QE]HOC$ca)~$0,&:w?@1+D"ڹ}q1QmݼȱeKpt*AV:v IT4 n #rWWZkmnP m[7]Q\Zf;P߇X\q I>j5}||EB^(ڵc+@34V[Quuc eIwȾ'ͧΜ3LRdD)nN'ɼ3B:p{2:*lL&SS&?:!V(n*K3+4H B.Q*!&B(HN @!+ *2[,:4 i'& #* o4'tz0T"+JƧw]=1Xm^ЙJ֎i$4:AbWAfl6Zxt:b!IX,qrjFv I+b $I@D"VT(zq`07+Zq#A%˨8#:Z;|8ѱqތAC!^ךL&baT qgrL*܌HJ%AǍFhqfT*>z#cvb("oa\5M(ʤRBjio^Ol\T*h˥NvH(TT8})bYJR$Z,VĄf0 \&R/woN0.,E:Aד$)I*bx=^N8NEb$ j&Ih2>1ux&wr,V+I,K$>aHh_#} mMMhvWw$X/#Fh4F{<6ui4FtEh46Fh4m Fh44,FhiF'X4Fh iy8y0 CPtlB?"Hru<~NWO#2az ξa*N# n]]$ICE_=_`{Az ercZrށ!b}Ki$kڃ78R9[1@9.@W{xCc74]=$X3>(d tU (EQC%] C=420Lh46Fh4m Fh44,FhiF'X4FhӌNh4FѦ`h4FM/Qp:?ޡPWȚ=+-}^C-qEUAd-\,ZxMm݅wnIw qb;Rg':O=722cBpwz*HY2fN-WEB2*2bmjBn2K+9].'wuvXX(:eR'O[:yV$M7.h'bc6wA&Ԯ11Q ]qĄv__YuE23,Zr/^ʫ04kr^_\ZV_p8ڽ31!Aw՟>{~˦ qc|o &a]+mADFQ|^/_L$>|[^QUT\rM]0jV]Vsd(ׯ q<jA߽bHERո$`4U\;oId~nn8N<Δٳv8I=}}'O׆Yw޸|)d6G^BVMfC]?0x¥~jET*qG˟^ ֭MJ:>0Պ塡S?v/\ʯşjC#/uuDG^qPzi,7Z%FӞ}E"3O>p:=+e8g.Xbdl9tXk{;uhxꉝ n4>{~ysP/qzS~AvBvMX,=zJxN:S\RxqZm fKM iΛ>ӻO9' &KGG7’U+x\; v8 ^kimYm^^7{zKϿrWxb4̠?#K*d}D,IMI<L;1'OgReuuU՜ի>y˽9pQIYuMMFzFK˻|vMNHpP(#&y5~~((NJQEUu{G%g$_YAaqk֮AػX,|;/Rxچu$I;xH"%JNr\b U_|RA{-ɼ^Hܶ8q>54h.+{{k(*.?o烏>VSEǽK3+d+Eoޟ^VݳwFzZtddYEyrs ӻ  IWV]]85ed:u`X-J} E|[\R!L p)HĮW /^߸nT*@GFF<VRZNQ&kfjP w 00g;g]8z=3Y5+Nʪήtͷd5\>7k+Rtf5SwdzB;!'OݻO ɳ2dR jWZ/&Ij=W kBgf'˗ w@&͝BYmVɔbyxx@\¢)B?9p(wuvp`H$3-`?z%¶¢E ###?xo^5i߹ $Eh/7@ggf2iwo۲yXAH`ύ^rrR\j$EQךJK `2]0`QusBcOO0ںycrR" #0|=4MPMO~Z&)1D, ľtEs?5~ĩnMLJ(/>>R"\ &>>NBp$|$-Vk XN(֞}SSq*Ubb`G[Z sK`^}'Onn߼v Qs iʹ,izbbB TVKu+yk=r8>?=- H(fflsWj4qqq/.ngV`NKIyvݑ2i2 TJe8I08^]Yq׮ O=nVr!JMNjjj-6۽̌ W0 +/+}OIJJrqy4Mb+צgl[6mxיÑή}4 VXm&,~ ~ T|>??/t>9Z,>14HJL8z= b4M%%?I2iXxkȨJ| 4.7\x}֭BTgG|I$X^uFD~{N{|1M~#iiy{\_(r\Ib=[ EQ>f8xxJ6^6>ֶĤDxt:Dž (5%IRVĄy߁r#_X?[̖IiPTpfl6uZ- @  ^קּa8N ZsFFZGgwۖMLiSgC/\d:%m/l\ }+&x^su~rsNkR!.v{;:23rEM'%${lH$ڲy)"[ZھWiv'O}W z}ܙ޴Ԕ^y 7]N$4=55_wm]3,.).0 ;_?[JQ:p8))_ʣZMq$JR<^@ ` v /'h@( a"$IR$ ebXcd`p,%_9_yĩ3s5==O'tSx\ ;w44{WO'+<7'22^xH4;h;;].PC-KDA$1a |횺p8^~_}q1n (i'3ܐ㿿/F9hA^nnNH(\ZSmZ~ox흝ngph( 0,QMKMIKMqb߁Cr?7XW^]tL͝]H42;bfq1G"nSS{UV?$rWXOJTn4X,vE x9-JbQ,DU6Yp8 !HHT(!a^`yիV>'~W*ZK/F I-+y?:=3sa֎..! A_=3uk$^`o5[t 8;{xO |@ -q:]8c&H֬Z)3"wܖ6=Xx\ ;gnA"e ~v7[+ST*'@!BQP(bhDB`U*&'7]kqp:$1*-p($I\nwYi [*$&$\ki%" :]}6@ H0FFF}~$6dg2,\8 bQfz I^PL22Ҝ.MQE)H$2<2`XH|b ïxO$\,>^4M$IQh֢V☭9]kͭkWbڣ@ۊ IDAT7 ãc>?vvsP!ł٬T(I3ݞҒo.FX,6<:&$bI$If755+Hes/^3Lqe29].^#\.K0#T:4o0MqD;rs)f?z}$IFaf䇒xuk[An;J$,#DakFF&̖ĄncZj1;w|٬j2V =wQajjqH$IQm۲q Zjc'wOSt8[ ncA:vMʣOS֭\pA~ބcrll||(~zNӿÖkr//+e=}opFGM׮yN} /TdfATZR<=m;x03O]EOLw.]r Herr0 Gіֶѱ@  WZ1&T=˖mсoa8;;sdlȱJbl^zcON^^ R*ΜNMp$g߁⢂p8>f2p8~jfڅo1h@0ڳERE0 -]rilq|f]R]ZSUYq|bC(p0 %5UzV5ATj'wOSt8[i$A_fIueg>U+W= Wj[B "0eӆsG\n^KKM&|Gg`^wy >tZ:gl 괺W> x?uZP$$B.N0a(hf0L$''1T* p>rŲ5RIZjO .(W(irY4S(Ui0^?ƽJP( |˘ٿ8DJN' IrEU(xuMdXL$2OdIҌtL H0EbAꚪʤD@H(H Ijj0u؝.L~؋*RP8. W._ JB!TJB83D=3# XC HA~^MU%#*23% MӡP z*)1aN+IbhqQaMu%-p1LT%gNzL*Sh¸ܥUE0,JTqH8B^^Rv{&>~tf%  ϽF) BA_|YNV&ABP  p^nNMu%ǃ  V7E/.[*c( 0Tՠ( APNvVUyT"II$IzoŲܜldJ"R% *+Jfo/%9I.1K B$IVSSU0EŮM$'w |jQa}૵- B]KT3(tO#R!OKKe.0͛IC 5'ss+NM2y^Y@dW`I~B?DN(?N۲S@niHttܜh c~c|B`6qq&X,b-G],bX`X,b36bX,ŚglbX,5bX,k bX,7;g `|rdqJDִσQȥL#}HQ,1X,bXMX,b`X,b36bX,ŚglbX,5bX,k bX,<[dUGs',R4=-g]0OZeRIYi SZ:FL4MCF{zhZJJeE9ϣill5R:ƦkCѴԒB.7\5`\,3#}a"I8OStiIQIqRnR4`TUJ$@0h4 b1\V]Yj&SmN Cܜlt:'&IMI(/ Zs %5U94MO[ZN(/U-PH>TRR\\XҖKKnz}h4vM]JrS{z@qQaQQ˵Z6z^.ed4}e7ϣhHʊA Ų2T7& CC`H*.hIhnm :LT@C׿@UtI5p-nٴAR$92: SSR+|>A#`P$es波(jzf&ۣi*U)"b4 z]EyB.۸lvK Jf2=cC899T,! 2*˙JzPeEY^n.֩Vsr\gt , ^mDѶnFy A@r^oզIP (+-H_\Y8wtv{{),*,(/+- =}##pD.Z\" _j  7'H L-m.۠/]"vlPt skizp?wg~?w!I]=2O!N=w@ x1ˤun3g/5\W|#GOZ4M9wZL&e{KtM)M W Og׻\X,+Y, P(xEtb!1 9oxϧQǍx.$FKKBP ܻSgΊD"w?u\K[X,JH3p  zTCP,{grϟtwp8&>h贚q{< ?0x$r١wtvg>eҊa^0n\N~Z742z:攥Rظ ;4ǫE9r)ǣP(_ljj&tHԠׇ#w[ w1J4{._LM6}`Pxy/7\P{G2զg4z"U(ϞkniD"kͧϝG140 ^O@:sسQ[ZO9' i^o.^ڽw?${|^S9% A> z} Ǐ;108Do{{:-MG]53(J=0}횴ZB^~k>'f3"BΤf˯D ::NŤ_vcMj^C1=Q˗o0 pÕm]Kk }@Q;V._6aˠ͖<޽| owvuKҖ @D<184 hz"P":sbjz7?8U''&R;~z^>8<) Tgy(J^ۼa=O=F޾%U׮p8`jc=w0OnjT"ɞ}II%K}l,).HKKOMehusXQaAue1LZFF}!Ji;vd*.0 EQ/_IINܼq= AI˹ E_yzhu yv]xpŲo;onmSHw8S3cĄDl_XtIP( }x~VGtGg7a6Snȱ%ŅL\&[RS%\R!IRIi|/UN;<2+펜 gAӴ6~G2/KbEQfˤu_|D.wvw?y ?O"Oh=H$m|{kD"߿1WPl޸ashuUhR^VoN.+Hvkoӱ(PuRCJrҍ=yŹO$p<&Jk\|Y)//[jj^ph_336[/RÕoK ]nG46ˢ@QTcS:.n`j=ql]R)˗- ED‚ RRm$g{.5\(rӆ ,23346.(ZPTW[>Cz<陒"Tr29[fJRL*X(jD"42jars8)$>q*INg@YiP(\ro 33m&&OߋK*|~'>/ЌVR\$ a~R[YQvUhc[6s8̌ncɉg]ذn-Dh4:88&^ P#H(e1X W_swfFݻSyh4:5=-JvG_ڥ O' j.YgtՊI4&K}u^ӫSKĒ[75u^|[7[&'}>_hdSO¼ܜW s\M|<_?  9[apfi5JEѢg͖x<p{Zrss`HtzzZ"펾u+n+HR 6JW^{nww.EvH$i5dg>r ȸ8efVoYأQSSor4EQv$ɴTE?9GA 33"f AMS3=iúvi;[p8vSB2i4Rp8GdR#Grr8Vacbce7Zt VMpZϞé)v 7v/_O.fs3Qp fl%5bArw?[*NjiI1E6ҥ W z͛3Ilr޻_֮^ cOL&o{b |K&TWNZ>HyNs8n(&)R$8s.}E@0;n۽gM\^\\884pEEy|J?>Odvv&ˣi A;n?~߿ßb;óa[5K쥿 /Fx hXfK[o:TW0;0xiכrE- {{mHt׮;vQVٝTUV8Ƹ\.xT084˯vvu|;B$!1auO?r1 jmz<t~^R{c;`(`0DӴD"ٺi3xZ\.Ch479`( 0| `J!JII^S򩧟 (/7CQqK62 ٺycWOyؾ]]"QEyIE:.áP*opq_ z=p8tዮ |\p83~WŢ1xW^7.vܶ7O?.y>^ ׾ko޻@bbJb7A4c1>0I$}^O~(kזԙ~ 7[W-#GOxt~>\g{s/?<  0 `AP>p8rI8]/EQ$ 1 ('+Ygj4ѯQզk>vmo}= _?ۚ￧øئim޵k/zC1DQa sI|<<yǾ+W?0u/}=xw߹rteǛ[cII$I ;ws@o?<4<ß7p8hiCcp"(s ¼PueŊڥ(nۼ'x(Acvr8흝N+ q;v|okzI(ZxkorRիxF$Q(Hp8@g<ۧݻv޽k'7096C羓}‚|n?p a. AtWyKO>wXC |Eѱq#(#5@${`s:]\ xW9G""(r~Y̖g˞}|[6Ϟim7rܺ}>?Sg:j„3b[ǵ#Aqqoߺz2Pg{V.={ȱ"v. C1EEQ}rۧ^K'ԯ)˘VN@`D dtm `޼q[{}$Xy\.W{zHy}GOQHs IDATD {ūVGӴnrӜM9(ͮD=y} y$'%d&&pFc4M$tJE%jb CA  $4 }~0X'sE"RXjy@,OOO岤DX\jIp( CSSR$P˻AAe(-ɘ r86D"J8ypϛܵK,p$XJrMӁ@H$bZmDXlt̔rTJriv=au\ f S㝘0Xᤥ&'-8bL}#NJ1:jwNqVTzjx HOi:=se%6hp8-7z|>W֭Yd"HTZSL%tR0u|\(mA ZAlvG0,+-x\.v*| 0 jA'&H PHŘYFnT*r>fO~s]KUU:tdttv_4M{ k+ShN41z{ k_brR-R?XϴH,f$D)f>"z*˸\naAH$p8]F&I29922sSOoDBZmZ Ai4MC!pH$rep8BQM>0 rR!i:FQ_-vK%҅>y0|۶-N")lX,e9Y;o=zD f Ṇ+eL֎Pv*@04MfpIq1==U]U`00{䩪r=5=]^V:i|'ΞߴqBA/5`(:086_0ikשּׁ(04))qpx܅zJy዗D"L$%&N9߼{N}U.yAGԅV@AqiN{zW,Ʊ(@vsd+#cw/AM-A_zŲg_ʤ4ILw 9C#ϞOINpҪQEG(j5xLZXђ"rTO'&|A =Wmf˯Ŋ ssf/  8}B.ojnݹcEQC˗-M]mlZ 'L&5i^bvxY{GIe%?9βKN9[A*_kn]]Ԕncs k͵Kj0.WV84>~û H!:{-&|Gg`^wy܅PjcQA~GlS Agi)>իr.F#JRSR>_g`L֩K k5G.1-vlߚ A]Wp_0nN{z=oyYi풚X 6Nle%E֮ܒo8nth56LZkUWU a CbBB04ܜx\BI&RcVD:8a[AD"FFFFr֮^\nBadxt`pիVdfd"S.T"PruZͤuj`pPӮ[")jzf u4B]SWRT Jހh,S0t:vn C畫M}r;wi|^T)֮WAt̹xt25P*|NϯX *'zήn;nr~?i#,Dѡ.uj*3#}bŰ1fv˯_Ms.Zrso¼(Ԫ뷏x5ƞ^ďt`pD"AW`:vm`윰XZͺ5!HO B]1_^[[TðNxƞqnՊEHn <{]r%{!j?v{<9]/^.-.jO"c(*LPxbL[Y|zS [I]}%-oh$Qi[Vzǯ/,ڷ}#p ߕO">]DB?ѱX,bSXLY,b6bX,ŚglbX,5bX,k bX,& 8`p}A@$|gqN(|4M[>Ap$lj#AnFbgr,S@D/dkohDggrGLlu(},bXy&X,bXMX,b`X,b36bX,ŚglbX,5bX,k}55mZ TǩRSRPCӴ0B!.C$ >>SA4 yyRo0OTV/I4ƭSS ,3YaJJLLH081>1a6[ $'zAHfK8j5)IH5U6#e:3c#HB,dg2kSӓV+|>?9)QT"U$Iujb! ROOKa)bSS3hgINi<dҔdHTF I$鳻۝j0KѨ:55=H(LD$Iy|Me* ԔdH4[J4&Ѩ\.KIN P4@,eds1:553cp! LZcx%%&TJA.5\!I>E)I))sm!HfCӴ R$5%E"-CC#`P*$KKcXo_?IRe$'̓V+Mщ IrfflcLRqOS#Irbhl9r0=))LiimNgZj}h4ZR\J(2F[^xogPkW65\i@Ӵ9a˄2iYZS S z}0lnihܜcjj<+;8RRggpp?HQTs_x̿H$r|}k{L* e%+ 4;_!JpEYeKg<8={{/*WB{DŽI;دN&cST !hBBKX;EQ}~I{vgg=3svNW\Zvb^ot{r 0BŏjZ.T=W+/͑>~7;;O.8Η_عc]?|fK+Ɍ1͗^|I(:::89:8^ZvT*1͑a&E% Mbjhj^pv~Le2e0*KJ ƍ&^bZZ[{z6_b16$Iʞ77~]9''-K:0̶c'N߈ Zb9:ulQɢĐٳdR鵺ړ8p*7z+0٢VPP|j}G{#H\ٮ=iss8^QqS۶PRzbՌ+-e0>WR89V;"q~ق3gBQ9#ѡ}W{bќ/>ٳNʭ|eifd`>y@7/L+T(n޸EÒ[7mKŢe勓,̴Et,K{5*u]uwwoysc] {H񌴔^yEq/OMI~,+W >y/>sJpfΈJ^P(\g_n޸`kywKU5>^~$;:*k\]x/$8be"ˍpvťe{_fe7;;M,jjݚU3g+.tJ'%kh+/TVV5֞Z=A.ťw޷~Uw7kuw۟ ٟ>M%\Px.$8О'|}&괆 ,)-[ze\lLYWQ!Tkԥ׮^3J]V t= &Զ|wxS۬VǗɤ  ۶u/x_-񏞲߰89iqru35_S{98(0a\֘N?svͩ<~9qKWyPX,&9,4cq΄~tU+[z9+p6^Օ0R8;;:8tuإ:J0b(ryWw7 ^P3wNvtpXCR 6 p!`hxxphh2 ڽ?..9oZy<\nX29~Cd2 I$b>=sFdttv_ohoGŧH8N$$"׮8bBC]] 06EK88BV;| 9iY̯;8W֪ԚhP F?<4D$ ȈwFco__DXH( a 7m6|||lё9cBTYU#JdڽF#Xz/U8;  0pqpp յn4546 O-Аٙ^r.D>h2"p HDK}YJ&<,4vpJd钌Ȉutp E1pА gg[a?!Q-aa'8(@֨57p I!V}Ofž>"`0 xzx "IeRIuMmCcSCcL&͖VK]1-dDϜ0P*uzHo4&; `p؁ARRիn+`2)ϛ=ڵ6=sT\l`2dR)ϋ}>840.I=}cc*jޯ:2<2PTJ*zL8z;:m yK(.LLP&SBPx;mmauz=Rd2,VwJ$)d1Yzd.JZ~ccg/_,_)I +8asd2 u+Q犌fӲ̌ѱ3g!Y|xh2-P !DRR&89O@N  \`0fDE |Lf2z'ۼi鼂sR  `Xl`4N fss˜9q(n2,P(0B(UN7U((JX,V52$-%yBT֎OLl߲YR)UЮBr\n_}><2r*7U(xE)Dm߲ iVdqL@.)'uQ޾y7[ l릍|+JV+5$ɬ r)p8y1ccgήVq:0*x VvSA/Oϙ3>K+I-V+Aa[6g q7'svEm< )V( |qj_ʿ#qwЛMf" J)W`H$bCxf, @&U{9q*1aD"X,ʉY3"#k/e;k: BrI4[,<.wKKJ$ Aj|gd2ɝd,^|:  $I(+eIB$QVVx+l9K֯]-VVFE;8׭Y96>N^7MG*̉EQԆ:|YfX,^0"1,HK7EP ܆O.&a(' 0O?rVU/IOuB=U IDATvvՁ޾bphO>3'>B844|mْ /Oϒee>^OœYEQ%BfaGg/;[TλzGG8ܕkuV AV0 HDf/(P(D6b )w>Zݵk3S##*:pLPrϗe c~[6.\oo/@Oo}ܽwv=~R$<|f z~OoG$ E󝥬"PxKFFF)+WHP   f3 Hv͚fqڬ)ɋ\y4Z`xK3ӅZĩ xzO!$JYRq8lΤJRT *B! T<>OVx<#^xGӥx\+J5n-(tꍦfE JzÏ_xdz:iú5+Wl`|bEV3LK݆ 5{ol|EVkX,&.X,R*V LE "I?hWzZ A 2R:=ts`hhf3gDgٟFcȢD>'ɮ7KWT7WWAj/_e\.BhI;v[DXhsK $Z?`BwusaO (̎Xpϗmݴz>yB\vz/źrZeZFfVls,h2>ӧw:::zd2:JpvyPfcc{dt?c2i؋վ>WTnݼfUV6|QR >>$AVTVoټal|U !y: 482Y3fٺi+lAy/wGϜ0$ N/rV^_pC$rGoA³Eun Rи aJR8;$Q^q1A :7o9`#I`2OtE ގή]$'%(bF9p[R^sLG2GAŋ^7ǿ+8At:o45=W8EqUH,˚[d g'GN] 0L.ƫuua!!7:$|sbb"a<{| ^sm[^~pBXx: 3#Z,( ÇҋIĒ@ԊUEq3g_+lްiAQ4z̲ ?HTxx8_Z76>N}_Z߾F ~._--+)<[f8{FTWXZUS<<ZqZuI sihzz{?(JCCC& }ٶyg_od˖DY,jZ: ۷n]}-Lb0AlZt_gFEn޸d6/kي ׎[47@"'&O^M朓|~ᑑқ-mxAŋM>j.IOMJL H`0۶l:q*O;IfDmܰ022z8XSME ?ޥg̎uʵfXeK2'/d2^f;: ^Nc& M&{|LĒQW i))&>.H]0ohMͥkQ _j%5Oo|i 񘴽ֵGFFख़GsNhu:W͛;Ύ6LGsNj9f @N{TnWwaA/sEz+.:::,L7'fr&U7xw X,M==}ԖEw}Rb0?^{o> "֮YyБ? 8:8 IAo8sbebX,Ao@VS8;-H7wNs͜z?/< 5N۷i5KčżIC5v$IXI "6_ozk. h |uo~S|}A&/v[,Iw P8O6'A.o2rlNINZh!Iu{}CܹqNB$(0`Պe9'N ߺy5u=_o{y[p~,Ecc#j {TTV#waό}owԿ.9`0N~*g*ޛL&9}M<^ FSo`H}x!ufx!mn? ǝ'0u>w;|,g'Gw3掆濂:ߓ扅vhhhhhhhhbhf,)vhhhhhhhho٦:1B$AV4; qBZ{͆c($mi6u;  Z0 æ`[:82x1M!Ijtt=L?LfKWo=!pfo6) HN7I_7L:ۛCwr!;oܿN!'z')?CL1ECCCCCCC3 C;X44444444S `L1ECCCCCCC3 -`=i$Vk .{TV[,Rͦ-+bDBEMf` 0@pVMd0B"BRpAQDd2M&xYf#x!IR7FP6M[)E"`$i4 F#IB.# Qfހ8a(ZlvRtzApVPOFzGczX#Iz`4!T'IP(($4M'+0 ܆ۜ4q0 FBdJ4$cI1E`4 $$y\H$1 dx\ d4  <wUp\@(s- "\W ch4LBJQFFK |Jjj8,Pd2'KfN'b LX$0T՚f 11[,z E|>}Ph4F0n@m6Fm8Ţ:K,-Vk r Izh0B|P('b2:D,LH4 [ RFcd &]$IJ"Sq\l6&) ,֭ZjPL powӚӥ$9:6RCAhZZmLHwvwkZs2Sy<iWg_t|osjݹqD,^tIpP@م5zEsdpܢҲr"͍P$b|ousu9_^Q]S7P]0nzZ<< 9z 88詭D"%"IJ'@TbɒYf 65$uݚ^6FegȤKWNz.x@ l}s8u6] ^o8u$+;/\ǡp֑c(lٸA$  g9مhxxKʩwْ̤,sl|\qÍF0eKE"jm0}*|IFڶ--ygL| 55 nܰ648C]\ ibUUn"I/[23*Ϙ3gk/_Yiώ}/+OOwky|G|) ۷l9#RqyN 9#jIf\^s^rblEeqONVUsĩ\$Hf͎el6[euyfY&,L5bZ,M6~uh7[ZA;oDAe;+؊;sf  oSb]WPUSK=::&/X۷l tc'L&D"NOMe\?h8lv\K:}bTv\qީ`lYkhHpfF? =pھ͖c'N̍Kw}榦'NvuuWpp_w0[oȧ]U5e_|}i֌WՕ]e.?ڮpvޢ<=f͌R8;_p\\̴p gMF*y+.]j%Zr_~E&}F͈|j_K/< >vts?{Bwql?ߨȈO>ߵyŋwt?yZ"/JJjuE%L&*R^qqBS ¼BlŲvin~o$I;:_V>'>aܱ=-%ٞj=sh``g 611q)w7N8GE^\,9ٮ=ψjimmlj~/xzx\>pO!p?;QRNjKp^qXɟsEL&W^rttؿ_ 謹tywnZRh4+q|ƌȵkVǾrkVmްѫbjKKIN?Tnr777WJ[ZVs?9W\zIQOoI/Q*IwlԼ@g_lݼ19)V-Lܴaݦ 72ha!Tk4'O.JLX\QyEWWo//JTO''%&%.((<[v᢫Synnm߼P=Wn*|ѻO8W, _ _~nH?tvt -*qttIR Zќ楥-.=_^QU˗f.H>v&uq3]^VWf0g떍RzZJEeV|djhlHOr8. ֶK2ҸS[[; NN&`jw7 Ixf䄢L&5 }FQח/?sF#"0*2͍ $e;"Bvtt培prl|tB8Tؘ|>FTRVTP;TaBӧRZV?s ٢j.]+(tlXȈp?__!\`Z,- LFKK 7HI^tPJjx>j0'IU*zN\,ϋHcY3gZ]]aNb8aZɌbi/*NXhX,JL_U]s;-vvu+*LV]{bFh4ڡᑄ|/4$aj0\|Gx<~jbphuP(|HSjutpY,jRFcnI6[ձEv0b20=K|ʵ-CCCK0o9NNIOX,g'Ȱ0?P]S27>N\rfKb8'h4M&XL$9:8Nj8lvjJ/ Y~`0;dEATڟ2u%@OIN/ZW 85x<^o_@{z/]L'`0M&T"a`P.R--h6 a$Iʥ 7<2reK2j` ڶlIP( JoWg33R1 :|T,+dGol 3svrKl֌ȈVS5ALL(0]bX,X,%1KJ\pEdOo_OOoZו*UQqIfz  ]oս43].==L9BZabPRA]===de\m{TDRTk׮g:;9(Tt ##~~^`q׮#N_fFd{BVQ_"f{bP(Zmf |~Oo=)d4burp[P8OYFh2E" prZ1ӹfD(Vm||``phAWW\RjOdhlFQdlla=r9\.g%JUeUuSM^0>gjAȭZ iATh4o(0  r^?>1nx<ގ[oZ6 X,U+Ll>u$/pn|g:"A  mB?(BBz}sKkKk"$8fc(fZR=pc2+kZ&&P*ݰvMŇY IDATf՚o>>1qA>kFNP;Y 0Y{_A @xkP IxcO&-\`4Y,V|\LP@@gW|r,-e1P7tȈpL1yB]OPH%CLS,BB,.HpzPVvb舘mp!]]'NFͼOLvrrJYFի9'bgMS [-;C"''M׶wt$$ 3 >|heu3;wdRs ;~23=AQ`2:dtpoX`j9ɺGj/_/?-L5r;?At2$ ޛ;@$ wՂinY=8C#5.w$aX-tQ(.VU:B6aʆ1 [jōMZftz0u[.ߏ65wtD$1X,&I ERIBZ Wk4l;0 _o_o@QIT"PK$bJBr VpȈֶ_d 5wz?R<_ 2@R;9:h:aɤBJHILz:>/͆٬ё >'4fc#+NQqi@`|H$JY,bB:e*Jgg'N"Ȥ ##յqSqA &&4l1S]'NMzf>~2h2uu LEG,V 1.黺V+ICl`@ A}|>?8(wppڬ|YyTdVp8id_>:6RϗWΎ Fu%& ͏ZOK6jVVU/_h!ŴX%/TVe̚9>b111q*/`0Ύ~Pf^z[6e,L/.Hxbg s̟;Ӄd^2TNO\뗮\e2a5*DeMY3MGgd2**=lD,+(4LmfA  ?xX"XP8#(J%@Ѡ(0?_޾^Zsd8$;l8b)*.pPuQ( Feu-J$<?ٟf,^hj.^xrW,T8;&SKk^o0̗._OɥrbUNrN,IRɴgIfhl:WTtu||$6??ߑў>ժ όZmfpl| H*{F Ƙٳl(ʪ괔'DUp $KYy^o^pLfWJ% syE^_wM  o1<:RK_>Ҧ) #8(ƍJ]\ZCDcS}ĄbeUfZ*an4]7L5<ݘLP(*.=7֐YV Tj$I=)Zv..V;9:h #/(a( 4aؽB$!A K7E EQ n%~q #L&^]Q  Ɩv_/w6;V55#)([אH u;׾!,֯InA7 ﶥ@{T˨( (%D6y" nE!+AQ E {_:<\<.Z8d2U1 ~ўdw4IwiNo W dޢvuQ}x21-=}aAԿ_/ΝJMYU|](17tӅj[s'j=<( G5A{W+E}o}vZ@Y]* EJ;ĿJ4y7'A5")%EY マ|BoސAV+.WQa,i}7d`OM ߬aoӮ-7T{Ķf?'} C;X44444444S `L1ECCCCCCC3 C;X44444444S̷h ,DǛi  +wF8A4#`mv8mi :hSB݀isf><]η8XԹb2i||ch2>L?Z;|=ƙ$Y:#쁧|onuz?T{7GwFhkgO_@h`=!?!٘  nJh-9X44444444S `L1ECCCCCCC3 C;X44444444S `L1ECCCCCCC3|>XBH$IB&>AZzCyՉ O~FoltQ8oXfA<@WWϑǮ74s6ww7#$I<}hξ]"z&qRyzo/AnwS($'%._)!-mG^oh >^S^)`49[TL䪕ׯYx50)*=!UW\nW#`08t<c^|Պe$Iwdz2L\^QsZuww7Al6ۅǎ7'nS[?o6-,4o !Xygoټ)jty CWZ;ﵵf_yY3 6b߁C?3sc8]8sbBH$i))IBcWձ9ys7_#kq'FhΉ³E6liƖ'c9XLB8y:wl6 f˗ |${z>S\W_mim;u ٳfnްE؊:u|g RyhN^Aau''(~ѧL&c/o?uZ,͜e~ѧg/ןE!A{گP8Ὸ\G|?|obUZ!tu:Χ"BO;y:o5. EWWKgΈ߷ҵW65=/P)<@Owto8_2$uq2%Z̏>?˟ެrWp˽GgQz5)ɴwA jG7I>ZeaWps>y:oꕔ$ɓyC#|}MΉS7~7WWAR [*2< ՗Rpt&Պ8uh4[*2"qfzWT!:=on|Z9rlDsp3; |U*~~7~}Fq܋m{]O]:UEH n5/y/ɋĉ{t^$@ #z;ݝ-1-30;ٙ@ !|ч'dmh\q qq=+{CFWYtq/7_U$r_n}?B{TQ ©nGV=Ÿv}>g6B!c֑WoƦ6FE)1cڏzw7m'}/+[~|7QJ/Zt/gNOM !{Yk?ߔJ$@ % L:Woo7o|87n ˲GZGܮ N~{Ij)>.'ROz{RIZZJllt6AQ*@tH'…f͘RE$3h.egL0F6 W {osNd4ZM5G#rVy?;}<ϋRzVi|~1 b2*TsmmmgLrExs!C3b Fc$ pE |v0}pl>X}$?/'dpx + Bcr߿}nC;;kqz^F4FP$U"/YõASߣ(*)!JVk yֶ Mg,KS{Z*MIf%W0 8|+#=``bA~NDz-IBN79v{<^OՒ$)^;  8X=cXADDԾ΋]=}},y<]]=5jɔ|ii̘2$Ŕ<4l%NNJb?0Ieh:ŔzPT*mhhX =ΜZ`hi%ővJLH$޳B 3swfF0IO7Ů EN̸0A$AT*Jp6erٞ}qq1ݽݽ+ hF 1+ǣGkvת'B޾o)ڸcAtZͪ˓OWWO_ںE IE2j5YHX,fDtE  M'%&/z7rsSSs#BSmx}>ǣVD" mrSA"wr1 {`qq2bps̒FAuB{pTj>\z].{ ]uX2T&!;y &ƈߘ_/90a0杈 ˩a @ 0nFNH1\>yDR_ðؘV{馦ZQ R BwOo؂< $8)x IDATVK$7~m;n Bc781C!$`cxjq VŢR 90{zkN|DMI?hqov<\ H%RZo 8i8FGj5_8@_N%pp1 .,aIZqLfWTj q LH_M"n?Z[p{x O4 aaD !Iri23ݽ //`qk#AB-e䉅,v^:yLOQTnNVrRD"`}w<OSKK{G ;]a8 K1%?-|~;}M?J̈uZm0$p$Ie#IfeoyeR9{x17  <] nL)`(Py聨yokŪU+*&d&]#s 6BAQQQwy;cw8ju9.{\4\Ah+@ 0vB!E"m B`P&qqܵ?ܸ,7' %''+tn۶"Ѥ‚IcjZ"swޫj.Y$<r̝ޠ X"nHc@ {Oyj鮕x^x˶dgel۱+!> pHT4w3Οo߸566F*ʼu?JNJ:yRSncHLttB|X,v9]%bg1ue%aɥ6ib;}YXGl;w4==l뎄8N 6o\{CYŌD"v,ˉDȈM(DYZ1 ˚|?;uX~;f͜t"Gwٗ~3. mm:ԲqM[G6KcI a%99SgΚ-T@0800hX@@E ksf%%%88vyKe9>|&)1A;:oryFW3)h hjiM4b2k;n\;/vk-(1CADZ,Z}fά$IA !8.%  3b(NN,,`:5\jzN f ..pe}>߉SXBC0 t8Foo;:.&&$\.u8 8ن&t97\3ԇz}> pg^lڏTQQwAW6h6%'_hx>hT7dێ-Ԕkjۺoaش)֑ 'B8<<|~޾ _X2B ̞YZ+ ;3% 9X}r8.k5OBx޾pE5rsnjA7 ô+XP\d4`偃Kx}^jN9{8$fϚiZ+*,EЃebaɔz@0H`(YP484|1~E;j5adQIP*d~1y:Tu8.HF^Q.^цM$A b2`5yy9]y99w Êپck(0K,l6dg,(ޱsocIV0E |)Y|im݉ CVs6r}y?\3|ۖKT D[ W^P/Ș }.H^b*rT*B__(NaX XnBA!GG1`0x;-5$IfH$j5QJex^5 lv *hn]d?8!=% X#j5B~jjq=\m4PW,:L&0l``rAiH(JcXmFjyxg4ïuAu:eE"NU*[Zw5 5Tu:axFWHJU 'df8 fiZ*p}8n``(J*69 `lP/.6V, {bx>ǕJ` !0:V.\. "ըK8~qm4ҴF|Fs{ÖK;Z$-@ Zc+JR*XPH_h TŞK#݈&F6 bCzNNRFǙ-SLfhA,f (JAg.W>\%,Fl#%{W8ftqiˮ rU>'B> Gc,d\\`!tU|OX_2H($ȇAA y  9`! 3`! 3`! 3`! 3`! /L9Bßr_?hxOzq@%eQM>w9> ^MF6J|AY߶iX%I2s Ök ]xWA:}[}"_>jBP߳ƫ_(J&E֗a%b}_g\>ߟ/e!>उ  '%?}:\ >aXgws=wdg // HJL{ؘηj2'+a>___ IJǎ?t6u5'OXn)J?Srם;rW,+1}_^D 9)+$RɆMeqƣdgO8]uu'N\U+M:]vXq11-; ]=v2ͩ)JDRC,Z^y9LOK-]h4侾 23W,+l;w=]*,,?md jN:bcX}F>u)L2yQ|RiX6m?Q-\s;/YPt?0X^ 8!3cYDP$Ι=S$C3gUfLxQ\.p~zBW8Pj9/7gɢHڴsχ =&/7|sK{.(6uJxg*YJJ-XPhێՇkK_QY IŋϻA_(ݴbW=yqr<5I~W!>/ں%JTN4UTV8um=ɓO|'ߝ]GjJ,ZtT"Q* D'.YĈ{_TNXN B7n&H')r붝~'Ou'.Y}gK<>\sٳf4{W./MNL2t`phtttՊq1z?pXͻ%U;qj劥۠T*' +8yꮕ^XqR,,ȳevV,S*F~l=}Gjם G.hj,n1/[xO#945=vInV1|3gV>mA{59IU[ϕ.^(7l*H%^oEeչ%:V(l<0 Ѩ-V(](%9 `0sa|VhHyŗ_1} {"Qbb-+`˶ndyP{VTu6͙=35%H}V,U_5Uux T ֒X{+hZTX?yqoyM};΁w4H= }vL.{!wZnãsfԪp};qjݥKF)b`(:X[w"erhcK̗ˤoZ:yR8q = =v࡮H>g׮۰|Ą\l6 ==-P,`~i{g?ΚQ/vu74 .;w o3&0 3e'MLFMɔ 645ɉϛp~1EQwh \.4-8qe՘6[榧Yuyݡr2:t9!LOg!+/-7$pYy^L_RbV\W!''M,(qH%Bm݉)'ahji9X]]xW,+MNJq2}Ԍ4$srv-`ZΚ|ZAhi=g0fLPȋ޳/`|΋&ӤB$<^߆M ;׶|YiN p8b>U81!>BsŲҬ ckWwwrL.mv Ůg~`(k^A%b1E6n6[jBq3g;:gLmjmk?LOKM6+,OKM1%'$x? C-:VXN9{'X ;1yRaa~-UXf)RSiO Cc ڽw3M~a֚ pl}fFIxG,'3 c ` 66ze{+"@oؼbҹg^UJ_K`$t$O:†Xe2 $Iz8b\d2B.!htՇk"[\ncttV apOIN=~ Hr'3wuh?bӦ>p'p8b0,%9ĉTLtɢ  IGD'IG쑇BufK`m8?P_ߘ5?z@P\4w劥 %H";kw N+sFloN1:+ I ;v:::' w-y;:Zw `0thE {™pPǿ#KYT/^TaЇG?\϶RLy9ѪI r Ԕm2 j;:;:Ru!VH{_r|M23gy;2xeKx wCdjJSF1[/WᥗrCk2yZ%V2ybK빳g-,NłxLt4z/8ӨC,[wdzjj}CcSseԶ{+[8x LjSl6{݉S76Ʀܜ,ݷb}:gLT U zzN:s\cw4LJ߹{o$U&-_d-CCϟx:*ppp$SRLO>Lc@dO1;{< .Q(n亃!RT*{z"[p9@C yⅅ{u|q, 8,/Dxx$/ujE( }!/ <pǺ\_fW>qx^HRA$,ILIqq1 M6#?/GVAZMZjN^m48.¹3gZ|lupkx7riӦ|{-,ۺ=9))hO.xہCIIᎋ!`Y1$$E GK *źnɍ?M[>~6&%%Hj(:[P[wg?D"~VsIUWhug~6q]xA #%Bg05%%6&>@t?x ! < r^{/465ܵh4̘61:oy`ubB`IJ}fDo7oayN"_.\h/ۺ]Ӆ}}-*BZsor5:~T*%Ir8 @}^t:p vB{bbhPK AHK fhp:10Yݰ̠wyzNu'{z^yRz@0v7m0snwttvE(j% &::2aB!Djjao]|IV;}{9[0 MOˮӢ+n[.8NdnvVLT:kActlv Ü.W}C1z8R(J5%Y\Uݳzyo-G(9 mhut -@ ,1:zմENy奿bqbBx`!HJLOW_q"25֝ع{cINJ0L$)rX< "IHKMy_#4Mk5w޸>aZD{Kw-]yF,v݆?< sMbD"̞iYDJd~B.OKM˥Q'`2ǨSЧΜ}}}:).?IGG>IܕQQK-lkYD"1#^xB.ONJj6f\-B|nn@O8A"iɚ)Hy@QTxԊiә*C!'&^uڡPE@@ z A( KOK Obs\^/S(r) ` RRL7cA2)( zVh0Ol9cz0ζm1y4MDGI74Q"x,X6hќlD"|04 c I |dFs 'JcJdAqɂ(o4:a6h4dtZ lv{јD"IL(* 7iba0ljn!IlV*@pBfL& B0Fl>mrLtXD δ:? zVkhf^dsq,TA,ccIGEEW}Z{Ggvք0-5E,˲,5!S*T B!NpeU L4,5$Ble c'ώ: dgeFEE;M* H2 x<Ad+iX EffD,;yJ* [TAm4Fvp^nNLtg͜QXwո!K5Ǝ|~ә˫!d|>hwt 6XӺ=9IJ<ȤR۝%3Džx^$8e2,)1\ $'MfMQ`+OLHd;rss$b1P(6&&&&܉Z ٪+#Xͱk!%wdAAɢQAAq,AAq,AAq,AAqv_. q: w2F ]wஞO@1 H\-!V(Bwj Tv?+AOrஞ[}"z`u8+Ak!h_ _w`ih7 5`0J!A?eM7PJA>!@\AֻI&HxQe!zS AXȈ"xWA;1,IN&ɯ@AAh  8C  8C  8 `4(ܴSAAy^ys3ך=7AA`=. ~-{[hk  ȝ \9K, ,g  u&# 3`! 3`! 3`! 3`! ˊG%IENDB`gammaray-2.11.3/docs/manual/images/gammaray-qt3d-geometry-diagnostic-shading.png000066400000000000000000013607221413310443000276150ustar00rootroot00000000000000PNG  IHDR]Eӽ pHYs+ IDATxw\Gǟ(G/*MAw%&h4j,'hlKb%vi4 ⅗~x9v33b@ Ѽ߶@ >t:.O*U$I]̬LgmE[Ot:L H=mbFpDs;d}[ha f2S@ J4@  @@ @@ @@ h\Al\m+T&2m&AKfo$ g߁çϞVԂ\pF>_L?1i4ܺ5 _ߧN|H.I'܋OWa#|Ϟp1L{;޽zv}n|k >ޭxV V4ź}bw77wӷO1GVy :ލN}Z]*zڷw7|[w`X oCN:9N wnjf2sİ.ul$y܅NxRՊq i I<ÒMxx}t:=wcէwO^*-ww.u׷wuĤB+.afO 4v|^k*[ʹE=vk4>uZn?.ʊ`[T&MMMkӦuͲ?6o>mjHpaKm3O4MJmy{{kb= #_,^H]$@ hr+| nܼ]vv<~tݯ#<~vNqoÊjN sr6mbLBCa``[Z{gL]`[&KVېxr (琐}& 2:yO?.7n.ܼyŹ+?,_x¸Epwqv h+Lx[6mqK$yxֵ!EKz~wq?~t3d.#@8x$!1ƭ3Mutt0Aw7ʿvpkk++V5))b;v;FV۱sϥ+̝= \|7ރlm??֝ . b?t:m/\wu=b= M|'*$Ν#G񪣆V3pwǎnƟ$c'h4`1>u?ItZl\'EK1lȠ.c4@Q9&0" PՉYY>Ԗ4h43<|tVV܋eòa2p¥渺8'$e}mkk?g>M_4}o}4uKL˗}d21O>lE  OJJٸiˊp8;wbB7Wbu^logt7^Dx\qsHLV1ciz";T&#$ɍ>snܻ+~m¤R/6٬ݻ%Kw 8|hX޽zhZ$ͫVTTMxʜY3S_PIܾ鏭nVWrtmrə|ܪe $aޜOl\1q&;1˖odr$\$%A*\}$WfPT6Qe.\;{f˖-=uAxruZc˟>_2i¸cGgeg4uFn]|}vޛrm }ᒯv;>5ػP-7qitzgWpsu_4iޭ@ժ! ISLj7oQArJM_zK_WzGGyn.P1!,bYT h>$xϾL}xx{x|*/]|Neàɩm PP16~dW&N9gђ/߸ɴ)nGZѝb<֯oo$=dAn. rsuIJNwrY,f] 0 Dk++f'lmB@``[PzF5\Y#BdΑ7oN)2^Jcgt:` d;;ۈ(/2"en|Lõ5PT\6"Q]54hDB!ageTޞ'}l!8مQ )2ǛN;;;0.W@$uqH핝cQP懶mCWqURWÇ <v-[~\.'Wwgeed0B)w 'GGg ̬S>|F9;9u[pIƉD":>n;$Aލ/;;uتe úv'C\][|=q<56dе7*0ͨl6N6tW%.^~R)+lJMdNqq~VΝ~"*Ω;Qё=<1 k.0NT4c<՝JcFeF r"Sͤ{ikQv.1_.90\%յZy'0T&PRn޺Vb.kIZjMk*owtp0+0Ld#*(*Y$D‚/عk/N H{{Z[6,j* ð%=+ƍ}½>3<==OTiLe t^"ƶ Zxz4ǿ{p9cG|ԗp֕>[CK?:Qy"?;jNoۦ5R^R@$oA N\jrQPI޼u'ӦTT~~Ap8Uw7ieF~A{Ia\ 귛뎭2f$IT /N I1 s}}3MѸ\a%Nt ~y-F ˯qvr~Vi?|e+kmm]PPXacAAW+ßvvqw'Ӧݍ{ E~r𧭭AV1PN%&٫g}͚9!5Zk?cXFlatZAg]HII ^O}A,^p3O>1rP:nIZj+ }yY3)1eP?/˩$rSe T|/?OYe_-[xz,|^VVVG6dС#GcႹXELfXh?ճ{]JWt(d԰@/bC=wޭ蘵ڟsrrVqv\C<p9\T]"KN7mroeU*UibBv5!6ZR !+V/ t:؈h4ڑ+x&W"jɔ\a( k2lD_.YXaz*l:dЅWd2yLj|~[{7>g] M'`:ҵsk7sro}xՋ/I wbȓJ/\ܹSG s':''$DswZ&W2 ҥKT*V++tsuٰeI I99 wテrB8tfQKmߕ @ drBATsÇ/]y'w\Tl?ڶ.7B!A9.]ҹSLо+F'ɓn޺իR"??KvTtF $r9մu.s^!G%a?{/# w`K1l| Fի5NrVXg߁56p\d#ճ;_;N"f9?Q0 ZiY0cbR(7e>^=ztb?^9޵[w9z# XɿW6dPp:!yFSL?yIˏ.؈D_,Sk~YTXYqvw;Hloɴ)ԓ;1C½Ĺ w0nlS)%~DB=1q»|, Mu_NPk~A>[Yq|pfutZ ݫǖm;;k+ݻq(V*Πjr`5Np<^~}b} j͹B4"ҟ=e`=6"aUlo0!ͭuD̜=o<^ťu';'[ε,t?RK@vMɚQT+Wuuu_ԨiDǿIhm٢i3lWk h|*7WB^i'G7I7o1ܔwٟ>~d=`G &2@ &Dq"NP͖?d~d  +`<ƈ'O2wD#0mPpءmiL_;D a&P@@ @ P@@  I(.V]S>_TTh8[}BsV}&4ZY5n=hnobRi3,{1QbR>@ AS@ P@@ @ @ @ @ @ @ @ f[:d@RS@ ^@ &FIIMSToېjC(xFZ6WWRRBk`r mۂ@ 2Ml2}ZrjmvKx D"QFLJJe=$Je$m@ >Ml2}DlWKKKkAIILj. !hB]A٨#3SP!hN41M+h2AT z@ z@ l5 ;vڳ_מvmA f @ @#FO@ oG^||j߄9~4dI)&N{ٳήw#;y!,ԬEmΘf2q?!D|^wpg2#G >tp [Opr]IܱkOׯjobiڳ_ {_9orDl\A'E\vܸ~m6Ԗ[0n2+*f{y{{6W2&ܫq` ,ggRN'^bqqH4`@ztqs,qq>nt:փ1&-\0/}p=v=`-no4ac/\:skW5Lq5xg핐pKNԖY,7A&p2Y,\;jc'c =B aM.ml\Vӥ_k~>wЯ2Z^f*3+,=4W9-iٷm[K$yEfkٰm*'(PTqBE"JjLMPػbww7Ν@ӥO3y@׬ &;'d;MaX^tm^֧wOZclymsM}9l 2y_={}&׷:iE LPQ iQAiiiJJlν?|deڽ/'7ɓvs_nJo޺2cTC1|Hݨ?Zv';zdRٶ{DZX,$ϝxMZƿrg>u.:&7i|bbrtl,ɚ0~l`@ڮeC~~_Θϗ IDAT̘6eNNJ_C ++{k~^QHܿ)ڶ+Ww܉A;p(;'w$IܽI9콉.X<<  \pd|8!'B`ꇓ{tZ  }uߣG`Ý?)ɕxzz,7J5lWzzZa}ю{J5&x;vQԣFV<|_efqجOgN:vNnGEt־>+_D.e0v6lQ1rk`#@n~JD8qE?_VZ#|>u0 ~'W*4m''G"GF-ZV1kQAZuڹ.b&I@,ٱkIvyؐAPRR:6jΝ8zdvv.( 9fS,INImDo޺}gZOHvu^ B?p:1)F_O%Ir9vZ">*ҟ=j'~>l%yyyy."+'ݵg߂y8;m۾鳣G[h;8mp? smzgԈuWݍ?xbӧ~l+OVO:pg|>X* >$r߫שsf(/p7 3>+8]bRr^=ѱcnj)-մlY =͕m[ :}g*3+2"|@I.czƿ5<{n#mrM[O{ЧwO&YXXr4HK{>LL&KIOr]:}2mJaa7- ^_{(?|͇-7~.gn,b`^|ۦIxٔDصGcQǿ;T*6ݼŠ'M|},-_Zlo799.?n6Xz3J|;>xۯ>y>+*ϣ$L{wB$IM[=氇 4vh _g;lք>|҄G5s]{Ӧ|حkݵw4Oƾ3 H(+>7T`mӦN .wOHH6=gӌOr~^j@FƋRMΑеK/^+-[xIIJ&uOI?/ ,*,xTRTT4wb'kx. 6.WN2(^"5 ;Fcؖ$ݻbV*$Yټ $Ir@ CXWxݍi=]v$ɧ B|aC<٣QAI);l'OեIA w?=zXR 4\ʒysgx -:&GM\Q%;v;vݱkiKg2oXRߝ6MpaÆ8eooiD5l }}uuo, Zl1_7o-[ap п/LNN$'Mx06be# 2`8;9yegXlaXxXL&t"\_ʼq6a^ZW8w.]79%հΖZJ"0w7W ;[[\s/flFDBggLFd|½s\ϚQi./( +6 iS>\Z]~{aaѢ/4#ӹ 4֝'O޾mu`Ne3g6dɣ>u#qтYwMy분Dj!4<`5!wDGtK6~ܘ /\B`0JJJ||?yJO6֥FSdHhemURzfmmK^|yQ(((LJN$I`"?_AT㕝4r8=Ԓ*cV>wv.3i7>ejudXԘػݺvp 7o="":ɕEĮ{ )!A[m$˯NգְQ&Ub#uhȠ,&sO? sgxgX"66?6X7D"!@t:L./r7Ň'M`jbR2ER U(<6]F׷4MCR'I˗}m[c*2,+]k1q@4U6UZf[ fKKKJJT6W" &ʤRL.e9O*}F`"8A:Nױ_OB,u2&'7܅K$i8+] út|ѥl٢9r *ށTwxׯq4a\^0,;;$IuiVc'Ni7YcدkȜ ?={tRV]qs3lлgpvrzgԈg 2p[0wNN{ȠT^Qɝ;GjBagkK-|VZ؈rrsb|iL:j0 joظxOw^F$ZK|J9Z. h_P]>v\nbRrHpa"P0־M('ϓ+4d25U'+1+ڿݻݼu':6/P8f~}zƿիItqvw7>9%z a"#cb{A4W{|UBPlopS_}z~&~Z'q" S?D*+5H'$IyRN78왟.tE"'O I} p*_|nmju߁*^zErJE_ܷS$'d2.^\qVbj㘍0++b{U_\@ lmmòm!1~ZCA>$^b*8~^'Ӧn}544[U4_leu"8ݒE/Yyp5Ð5c7OLJIʃOS :=m?xFyxkZ.m߾]~E!Ed&uŝ@jbۻT*rqqvS(~\Id[}.fWiӻ?tws;IBCCRL0kN\|[&}:g~Rioo9v[lLptt I5@wF ޽z܉8y 8_ q|֬[ϝ cxKV._$ڈ}>ayY>+;`ǰ˿_-v-^8ZQQ*ٟNAg//5r«U˾HX:\&bbIеKobw\ #Ȏ^e^Z``[˓nٶ}GPZo 歓nӺ5Dv ߺ}-W"9zĘjFjEome1qwkū<) 1)=6ڴE"Ѡ5-Ln|B\yLb"cKG41=~hE)u)uGWUf^O8=r`/&7Wku+V%{w~ٰh&1 iٷn23/&9}¥+׭蘸"Zxzzzz/^lVQ#sW݌8o{ޫgwOI^v_<xlyB( 4`a]x V4d2isfQQבSS g6[[׬Kfogi1G YiT$ CfVU~K( {IFA3Dpl6[V߾պo')u5*^v#"#.M_*q#iL6qZDD7BEQqFC\,@4Dp83^pim( 0%m57bDqws5ɢrj5Z7bݺVּ+? ax:D  :Zh%@ Ex1a|X;l’i2gӚF[#F1M+h2ATʪiD0+++@4j@d44:P2`0.!@4OPi9p8,oېCT尫MSh.x?~$=6p<<ܪ% \ ޥsi!a,ꡀ@ E"۶B1[@ *D@ P@@ @ @ @ @ @ ./&Zz5ɩVqvvᷱ(.(HMj"ILj&5@ȤZ__LDu,|IuObn=2I, `R@&!IȤ&`R)'''GzoIQDØTR 2.XIP# BtMb֫{\D#p PDMjU@&PL2Mja&`RT*s@EVXP$o]o&5*X1Z4|Lncʠrv0ʂZ\*sIU1X=9L2Vo*TPIQꡯ$cFd &=BPH役$Mbh-$j-$Tetʠ-Ф2rQc1 &9uL\A>Bbn=zL4= -6= ZEqVMTQ6$4K=\dQ.L,ʥY c]40Kk]({y}1M@@o(ķclzҷcrۤ7ʥ`.*ܚ~:Z5kMbiIf}IrƮ5 7C$M@?~„[Ԝuu7$]_CG7WHo[_! 0bʠ"kQkcS .Ʈn#:Dk>OX}u.gR?#Gu1鑾.I鑾!2\Q #=#=#%2@ P-Gha "6 u[۸H߸`nzLT8j6z(WRoQ WyfV}#r׬7cCzk1Q8|s7[ f ,ًff7.[eL[1MoQCot@@}ȐKU(լz2eLc[1XeVX=rN z39X}svYvh,O C4 rNO3 IDAT "(s}FSYc7SezlQ5Jߨ+nƘ[_ʚ_UY!(K]/L4,obF]Fm1^"#H 1`Y4Ӵz;6Qchwq@@dJ:-e1 A F}l6XEcޢ19ePeLQo7k[1-ce*ń: 62Q_5͓` g5lKtq=$`$iڊ#wJ" 1aN~gLVbmE6Kfh8G*))-cez2XEcJL&h4 3(ebz2ޢQ۰sy OzJ%fVlM̀H jm*3[h| @b U0 VI)Skt(vj/NɔxWVwY)J*I3N:FglEdubcez2ޢ1V_:EQ#cLXc[R̥kutFT Elc$ $`$ $Y1Oô,f c6Kvu~1C%0zMGgXR6KmyQf |%Kg䤅 %mnɋ z iW= mn%E;e8[K:ZlLES=2.zdLYQlެ#cj[HM1^ؒR+3[=N ޸BpF#hF#hx1a: 9pKp8'qpȤ4^eMhn1J:]OkVz4Nj+I*)ku™'+,+K ,/' F8'pL@`g"{k =cQ̈́A4c*^bj̚92cfiQ[3KU6\'We3{t' קϴ,fHC`g- g_DjI  #~`c$S?'qqIi*Uzʚ 8X_8HfeJ:\UƧtAb3B屽1Nˊ4F3y԰Y.:]`( #8/ԙÇ1S3S3=ES!lQf7caM-ʘ*%\NYzM?$`$ %Z,)c d FF$c$it-r3k+:S\*u:5}yN\9gwc)j NPs,Y]Яɛ”Ȁv9v. m|IQ (X[V2(c,p2/&2,o@(}#2ƒ3(c@Mv,6)e4LRqx}eq7NԿxm|׎-F{[em]aUl!Fue)$B#u=y)3J5/(@)}~ѶY2MBݵD|%M/!^e2=Lp-qʊShOkL,_,԰jsƛyƚ&׿ + fͼ:}ͼ:=rcCs8I ׹I'i8NWqV"1(Kp' 1RS$Y _uTC1Ȳ$1C:{#.ե߰#1ˑ:)0Jew߀ɼ3GsG/Ô|Įba19T`^J$;=ͥue2U֜ W,__lcO/x+m2(c,ȍuԛŕKyꢂބbKܴƘ574eu!9jd^Iiq=._1H#+7 $`P@2 @x)]h#z.F'O/ 8fD׿]:3;S\L1$hP[Y M 'pJV=`NO+e2Kzd@be`:&`h4㏲`DOgt+ fi!HV4sFe1Df"afDXxH$"Ĺ7n9 :R׆lt7oI O>kJhOu)YBnBGFKZt^/)ˀU9"|hm TU; NHibll14 V,jo}JȪ$*,XMK=y%3!~%ݖlm# (aK8pfCF"$zzj@MeYxַ}vK Df(` aK>St,mbAqWj.a Yʱ Ӻx#‘li*OǺ2ʼn oNor⇇nyxn?lBRE%[%2X 󥭭FEfZ @Qдᵚˈ 3Uc6S(T{Km"Rm!cl"tP:ԅ?~-wa;|$`;sM2<(m{H2׭%U;[46}u!DzT}+!wO A. }aYqJ0??<\(H=-:''3ԑĝ-xwC$C|W_R!؛m!U'6@Ǒ>䰗Z$hF`AFdXe9i>)QHb Hʲ}'k KKEO:֝;Y27{dd]'xxʇnOdP#!3|w{ñ?J-;[C ]nG B !B!WӒ+N3_&dBRHYA2E(0pzґԑc%{KجwײnZ̆ԝGI=U]ѽ: +4iqn x ~}N^_у_OxސU%~x83?<3ynV=6 Dq[NI4]ݺF~kDܟ(4Z+qk+=nBhcWٓOtu$-̅4Ud#$Shꫮs$$&,/ukP _8Ψg0B^{nݰo OCكm.49݄^# ̓3MI Sw>|$EtZ<㱘F'/)I"de&y1pǻlj=O _8/niܖjn5A[vLlGRXWٽz:7 "]*~K$y6ϹDϸk (o[jo8pr1f;$7 'b|%GS5N5uzq%a($XWn|+2bot$$jOZ},! 2IZzJ@e.l3\$!u@E+t7_Q n?}qk孏S є=)QE4,*II G=]~ߠWReMܮ-(F9HDs_p=91ai0FE$"ZKKJI-=շ[  *aj9A5WL!a}f}"LJ0}ᷬ9.t?_Ɠ_vWzO<`s zc$#֥N~6*c'_B>b}}}۾pz,'f{jee-X.\C T:ڌ.G)$,k DqħJZd؛gLɡXFd3}J @Uu͝;ƃHjjVXq`'&exR Eϰ ^ʙ?]g<.Inʹ?ޠyA&+tޭMSBoLcxTzW&HCx-5]7ڽ%_t~YWx5,GU;)!b./[IOn;:4x%qz7Up_4[Ԭ%eża(Ij=Vs4 0[9]>9f#"Π)FII"aiu5MҶeR~9g -fn,ؙ#$PHǏ?g9 w]M]WMHq;. Lvsl-Dsrt^oZmXAeCBŋ.ck}o± ~lc;(mM s:%s j7(A* k~~;2?>4/ ڙ⇽ɳʂյ_w= Ar:ZFpMuL:׵Ew\ ƛ,{^qߜTvݎi(!  уɛ>n9q2B=|6 &u3@@eN=nMG <)jEB zCOi\vb_#% 0H s^)IRHLn={~fw;~O(2d!/ڎGiQ;Qk&#YpE*׃ Aܺvw߳BRvX7_wu[3sX,Bm3& 8K$K-f2NYVp5-Edd"`,t PpnR*o=PQ}>1y7)W_?0xsS]׬o˓w޻՞;99 1ʛ2CYK>K1F:Ċ?Cfi1y%>~~}TRMIQ.ioXIu}E9Wɓrǰ6$7zjɹAJmpZycPdEk'oƭ.H1[Z(8b^B"͸ E\|w'daE}/24 D"qK;W| \ BO[ >bYk}-azrT-'qaDAApk؀HJR,[5:YNCƠ p( Z珚?iӴF &dT_(k&'_=N|ήwVAC.:0aU{8А|GL}Ix% I1GDɟh4{ӄMFF ?8+>dl),0g Pjj%]c.o\|,.BvD0R`dr[\Y.}0o!0C)i BϜyVۄNq8PYmC(@u2Mچ Þ7ؿ15,eg" 5~U5 DJn./@$Re[b邼܁A"ĭ]"ՁJ fh^p(~0dAPˆsxRZk1fϪ2q[NKhpn;R(Q12g3FP }£ IDATBVX?4~~$}i|#?1Q}V16-G&&0#=1h@7z"I QG;MԩSyz6 μوT  ^Ǘ>yDN32[/j:rrt~>-deI]^x@<)KB@FhʙvzJjE۽#ο%xqO!Z#%4)Ki3),[4=Xq2J$5& ^YKMl {bucl0#%e9|@!3gsHYD'.^19ErD/&)"B ~eҙiB!$/6@IکV`]+M/ N;; jBQ .Di^2@WR'Zߩ7:MceZ[ޯ_o "ee)7hmngc{%r̔`v{#1Qc1>;$`x}y5H1"ϼBtLƨLzww.:`3_6>`b$esg̙ŜxɵPBQ4/}z${_釗>+46fk˹9!F<2 ɠ۶RZ϶0 K,5[z҂Q%7-gu6-ǻ]NRҺ6}j7z4~?r-4t^)fc4en^I]K45\V;0H(J 'dL&/e٤ (-g^㗎gNkmP`xa yv䠮@IrpuBz✤UbbuvdW植.MTey6;'1r^c/=ڑ8K|L{?ކ?$`84ӟ&VJ(Giua AbvHᣑ/ xV_%'b{ CboVIEt 0IH4%3Mf5- ڮؓR֗ nWz=hϘ{t=ϫzZ_OOM-N`9.'*LQCeGIE IKJ$ia`3mNþSaBva3fsmJ5*̋[]Eq5-5mrVmo6}&ujtM޳!+Ej$XYAH9 %yvk5U$ίQdtx*RfVkl@PiS<^S>|4dݫsiNZI)K-q{M.y1?C5wG^ ?<ހ<xoaB@齏M=G9<r\ۏ~g}v^V=~ךpPL "Eɶ'Mo7ɏ{EUZ[?ZJLa9¡3 ANM /\·t'MAeݝ~9Upw +Pk Ո>FO޿pZKBTv5-ҶΦF^`#dOJm`6"eɳ2EUHL n1=8fOI( Y!&L"d[.5jiHL:pd`Acp&^˨|JgY!&Pciֲ;}G)fL)kg7=m(-?2r&@bY+t%38b}=:Kw`W$&8ľF_F:QTs|>`B8Xs ?mn4am~di @ L""% ӣECeu):'ea2kb o3u~b13)#摉ʦ3 IݎȀ^j E5$.edi۱IFۣ7{Mevj`M 䒇*8:8GDnaۇRLR] իPDץMbPHR(E4,L(.Xr$M#Y LQ3xF2"H dhąp_Bۯ%ϯXW8cF@IJb5qx/d ΥL~T2F"B@JmV8'Ydy8{̺hfiXJ**^/0DaHCB4!Bn{k}h`*V,k VRhzn|y^z|7% xh}Fır$Ҭ+kp~JP,ytmf*)<HY"dۅ̼s|x7TQe'P^# ljE!{t@hjvl*ϩv\qf=ta8en%it:>X# ey3/bP䌏oZ e)< }h$IRɖ/hJdp%J(:QUM߻)wv_MfLw"@2E~7=Bq}U0ho@(l=Sw:% } >qYP)kl$~eE݀@5)(@PuPHm΀EeOj}>j} TS_7WSU [D{j $,v\h,onc9īF8Ŕ2PSVq)>NH(QaפsQiM!c +n`f 3K!0"R }h0` OlV޵ϟك}ȋ3-! ]5u>\\/{eLΘ=:;bdK8ƨs"A/8N>&un3Zϼ_9iiV6܉R5m?k-d(}8"wpU0q[Rhg|roeٱQ{{c˚jSTr/A`ɔ51 88ևoS{^ѻR[w+P[(' t=4/65rjD8ߟ34ⱘnV@&Һc~;"3&{*jn<k1$Ib9 FB[]x3K R-?8U~g#5Nd)&/j||¬`Zw_YӮwjs](#HVl+1yx"q8h 7d{E~p|!#6U{,%]wOёi##gy?#,#2Ez, m?,u2;WÙKi&}.k`,7|G?85_eC XKh;Nra_{8d[ FJZagx.xCB鷒Y IT挄Cya@Z +ҧ')Q-ʄd:{rrɅ#9;}r2{d8en8Ƌ:;NjN$/,w{ձIb<}ryܜ%Rm>[ O1E&gޡp)ņ!Ja p{  I^m(I TQZ^z ތYÙl}v̼^Ks&[U(IWL1hp'>xg%6_6s6tnC4vTmgo3G7DKXU5Q nSbK;y1"#&GL!a|}iӻm`)R,?'g.S,KLwLM(F`IsveN;yac:nzEdZG3{ACCHiRpШb[Y ތM ,㱚5>~ "{SOܞ@$j0'ShP} BQ^X6k7RO_IN[9 )pBw]VޮsiV"Jo:3]+82L(Xk3#"+rssSD~-zRdCL&mWeH;-/[8|ĪlXëha?98JdLc\SE ǏWw˃eu R6GTUHR`Xɺ'7Okߖ)R9Ǒ7~ʿ- T/D).[uHg\ң )({r!#JAakcSd׃^ 4S禖ӏ(Ymg^EH8TVA(j{pҶ&T~$Hrqb$#GaV!Z!Xoh^)Jdh%u<":03[pT٬m@ @ʒa$Au$'3њ=0pj'z}"CnD8SZ8 /T~[!ѣ3%4}3uM(7< *$ ENcsvџfGE >9&by81c} ӾeY;1L!P]c(ǚp@ëeXIg6cևe?t4]92p@Q@ Xm;R¯nU_@\:g%]{ƻ>4$6]r+,yl_ U)a`PW"7v4^*9Ft^D3ixSCG& `@F63fHny@`qm~FPcGFQM^J!H^,)xwg8-Nj {86G)45[EIy7ubn/9>:SyoV0:OyGCKIY9I4T?_"8%Y1 '}PIbH:&Ķ& &qC36xWBaW6׏J~ &_wVpye:GoK,n{5lOM}x:؀)X`5dJ4.: UU+Ay=8Dӛ]!U ;#A` j!@̱VHbxNYW*" (7كu`,vΠt."Yb'#uM4ϧ.~aOzSc920oDpB"ּ%7/}̛W,S}x_Jx +A yɜkصIYĖM}gmٖ.nc]{acoq+8L6qt,'S&8 k<k#w]P"kR<3uֽwb frJJn񈔥auo޲_BR.k#p[D g4 Az &B0<n~N )G 8J> eѡҎ>)crLT!{bRcM %D ɔ11FR~_#>!ae9l"&h~!˖ lYۈlDA0 )8kdk1[W|@ȑPك%!p5e d V㱚u^S )At I֑E-ݤ,WVS%HQ5,&()ra[_}i'ĀEd)^;hYqr3fVkLC2[r;mFˢ'kf697Z1 1aboas7<46? 0ľޟw`V[u8Fh/wy#{7Im 5tvU]q7V$͗r9ۿ;sT~cq,3rg@]I4|7e7*;~$!UeIE>2aźp~!dj^S it]u{Ε>[~9MCLRf3,{rLQdR W;K 7Rfs ^DSnKݧ0i'L6?BgKRM/?Aj7<4 Q[ϖ:/<>%W?!ٟ%Ր/yt@[Ț>Z E՝ !TiBכjz]-- qv@!K0 ]w[^S\)8!0$YOJ8S~l_S l+ Ҍɵ8y1`]_ i3Vvnd,sCd klYX)jy|] c<;=`u:Ic(bF/;ur.a-KDϚ˚ 2M ՎvB" N+ E’_{xw}z_ԒEwG^=7O|T>gc)S?kCyr@,H]#V=fzA(qJj ?d% SHIuWeW|ܓC-'5{qȱxeJjHoK;oT"RKv s2EܧE"CA5wzM~TJ2>Jv|N,o,%_g JJ𡀨_ 躶ncFٵWbj|mMb^/v'Fg5H#}F+N$|חo%]7Ǫ.~fZ]N>Si IlK=PΡhy|W"=H JߑC[&{. 7_Rl U7_ )^!:Mכ:N])9ѐ{_g77V"^ulv%n"j$V7t՟Ϙ!d) % {±>1}r0V1<`6>˪U  {=(CnBWP`)tՁ,<+"+x N2x8_56tu~fy_;WFzCkA u^ϘF l M)+b9bv *9c5_7$_c@]SPw' 9>fUdZ"" Uw6\̘n!lQWkeD[u}'\n~0PE0+)Sb[~ :!˖uI-d)+5\UZ$ƞPٍ nl `ONOሊ0J}&#%>o-A Ħ@ Z @@q^`maTODSE!nicPT# Bv(2Eм7jYN8e!ˏ~QS|o8؀H7*$1g,Β"^A{}oȾPaz8e6|WYhXP{ǔyidFZ%-s ztG_T|IIZkS_\Vv!0AI(l\z.؀f˙DTH MG 5h4\J?ecP#y/۞ v%Idt[ji _AH5).{ܛ݌=@ Ab!S^ 4]Xs{YRHB)a >>5y8Q8iyFаˈ #K h; fڶ*@Ra{Hq$ qaUd)UiD1f f'V}:>^uT!!I3k7uWQ=wXXQt' /L6wr7y#LVU6 {74:#ƶ@Ltg vtKkKHc959I&+KpVfqi`ptzR\ΰj<Na*i<* ņ)B;dh1%$yhVՐNwiK :5w?8YEQD޼(dl~|`+ɚuRb4 ]qؕp7vYUm99z_N1ۊ"^;0j26W6}'O]2 <[ŀxBwqQVbtW52广\)(ٮw״o M6&0('ܾK|mog7)f& I7.k{Áދk`"4_)rn8 8LpŖK* W!U<( vKڛ4>wnLKK;:;0er۵VY R]K+bt:"Q3l뗽HxZgz;n0B.= EŘtEu#X 'o!i%k;@)P`4^Qv"g/lVvdHV՚oHwP lfΕDzDTtm"th~{).i:}=v?=pC]RťSS^)²6g?SC_XKr}}NqlO,6ט*=ofz5RAe;MVvy:cJԟ[=xl|YU3O\)FS41rѠ/k(]8PRyqŠ}bw,R0WddY]?huH<<̙1~N;q EeC Vj<06PH$=q_ m xIzzZ"U"4ܖɲ%),5ln gZ毬 VfcB-46olӢQ+r[,ssx"g}sټܵ O(,,h:&emlsV֓(:h@R^ bq^=XFrZƪMGX< +_/)]ٴ72|`aɩ \0?+gyѫM3؄Q*hw,weO lUf5ngQ?p+XR;c@/Tض(|~gC/n}Zg8't$~UlRHh-z;'Sۅzov)~#0"ke5yZi?U0T;ѕ~_EW<1>{g r(gO;L mAN$s!f"Y?zSeT9AN<żEN8FğE}S::4gs;[-+ҡ-Վx"aL.-P(3sŢB򅃋9s`flcD1 eL DvlzSF Kxk.HV&&$eSE3+5e@ V27DJDl/!i?~|bf(wy YCP8+CHȊ[ 'q5PF rV7fEqbsL j˲wv7wBzBH&p{ٹK[Pn;vI֫ BׂPb0U5~|Byz$ HW1їzd01lS8e#pҧ1S׭NI-!@vӷQtysҧXKb3)S$%O4D18VtkPVdơGojZ9G1W*jkGlGwC\dxN8`tܿpuztx?'7g}Q`b9J'&z|3sg'ci^]Gv/UV*jUQayUO(9Y&ζ~;d3IrE:]Ѹ7<|q72|K[{bL}2:$"Qj/4fW./)@)aZ94;zc&N@A~ц3yx7]z`y? 0-i)gRv.d%I' Ϥaӎ/L8FCxl_H ɫ P_ =IrOr3cn~vl]7"zQct|7cg\zdbBbVʚ`Xÿ]-;{K?連QhJR"ƑGVw̭}VрP535OuSljrb gYY3n#`,Wɣ/u6ݚyNg@~7g.|w ޕph?x!MZ8mБp8|5 W8?RZ1 ҥlO /? w *gb t2;x;Wo~xygtqT3L.e~xc A_xQ Vf/7,`#,otScw:z>蚣cHxNd5+%QVZK0 S\})Su-|J>1k8M0>XVv'$HxEs`{aMn^!hQHgd #3[UW,zʧ&(Bqw٦ų=U5 xvNE JÀ0dg{6EȢh{h`nv86m1Q#ŽDI2eFt5<dTMgpi٣ݢ팭ouG1r"EXld yX+#Y^0Ylt)XE=5^L #oU"ZJd_h><8 p|;U}qzH՝bJJ 7ϟ7)Gj .^Nqoѕ/?,[>|qvx%nl3,B@v'^yl Y-fS Sc(#pIb7ѕcf ޷*Xf^3( DIVAN~)؆ '%$yO.Z^wO3w7E%,w$ѝpTNL?FGxY€=4"itKQɘrNZk͝i*ZogT**@wuelos"\aP H.pLPVSJɶzFsg{ASpLe4nkoCp!B1tڵ;0$(*+쇳ֶ~"$ssc\ꁩZY il{5a1ňXǷY8[ND10rc|&7X(fU~0 lF9|v%E$HK_EJL1qҧd3JOˉOKƒS*YL?.յ%<DNmY EXM88e{!@s_=ڍDfEA̛,0Nv>~_ f,VS6 k?sÂiFרCyM܂d\Dox%OU$zVQ| t^m|f*`Q&iX]F4BLPfr 1qQE$ S{ Ih<'\E6yAxhZN:'  `:`nvx,%LHҗ`Q8;drc +eDdk.km DZbBQ5uLvb,jKUZiK0UybUW4?( FZ>EC>¡7TƄTSLWhn8ºR!%I(`))vtB\tQ*2z~UlGjW[9m}ko Ro- ?ί8>\7,X]xpWFS%'IՈWʚIǾ7._.{?DvczhO]/Vz8FiY@ƲPHm&T!B$i,4~7˄XXUX@Pynηdz $} 녟oiV&o 8vRgA+_pR՞n!ͫs'zN=N,yy.z!GrCy ah(+BM{u>~U˱7:ϹWq+@" )4oc׳]Ў4`&c5}o93W"rZjSxW:Pv")ZCq|{O.|r͢JNMqJy݅;gl;9W,bzA(븤B*/xA3yRB $$X>Eqja^x׹9En{P(+tvf!p:*?u̞mC/H*z0G(;jxԨwdY*"D!0?!T bMKdz `~N䬹@"h,0,3qf`LKHfѴ嵚2!Fղ׶[ojSe@1DVbc:"m gǮF]Yk*US:(o>93+4(O'uJ^H/J 7/ٞ74BiZg;_lz"hSjV ` p Ԡ06ۜrΙwI*Ds2 0B]~{s&G#EQ#Wz_:ʋyKȒSG͝Y>" py-9u0@֌M #.op3a sl8+{٦ɳ=;;Yϧ3'0_)@1j8n:Xe3㏓eEA帥:ユ=`߄Xnze;vIKǵX܁dy@0+)tk* Tv    %s܀M xYIxEOЭ8km?s#43:,FpOtۏ ɧDi:嫚M?-4"%4-]At?s Sdxԙ2˔JT@XGӎo$9891}`.Ƿ n|UO9Wn+s%V*Ѳkm(#;Sp^sW]vߩUNFwxR_$(Fqѥ/.. mT@,$ } b_=#()xx틅3>r#!v"V ,-+@^+cQ;Lm|K ^@(^5:*SX'}ݑ6Rš.*[<OBٵC. %i$41~b~̜Q5QݢbPnvŨ f# 6f|{AD(=0J P48^h(h{4EH`odmP2Ɲa4UY0/S,$"zo|XXZ+-\"ķ]fw :EeZ?{NCzhZYhcϺ^szc(S|O2]QO2I#6ut?ZL i)c~G> ۩^!Y~nVUZ?Xh-%[loCx{̓w;_BYh<Y;FArۘ6Ky9d0xyap2s8EN:i!(Tϴϝi;qne <E22k qMC0r]+ׄQ5:ti;MztM8ϯrCg?dm.H'ғ剢⊐H`hFy3|BrzFed5Rg:1qPUvBbSUuZhs.p3 >C*Z6x0f3 qu=e~w(Q۳ؙW<\-ď+LL]r|@"xV/Km;Hjz#rq\[Sچ[8A_W*{$U&.=bTՖJq٫\zu)!,qU^@0B ^k-)o@ƎDIJ,í[QU`rv3ׂ@)e`JEئf}4裗z&> JHG5UvLKǜ'Mlr2VWP -^k `AZ߻pj5-q orۿO1rG[YMrx88 5Sn {n]_W׊qD}n>0l>R$JQU (w/@GOy%a,: hj{χ: eQgh2$<vv!Pn_O!Ja-@tFqe^@iph%睡 =ѰcġG&HxUtLCO8g0F4bQ@iIdthdz&Q!4I']nO QQ|IO0d,X"DgYQ0?t|Q54 FpS]ux@ QJ >vbh'?%c/ {uq"󄢌).J9>.}Xّmb$R 辽H'cex|-A@,i^nͯ~.t C&9 F<cĎWg 8׉'Xg.񑡴!ӝ+*R2P~v&/ u}f>d  2 歷dZ7u]"|xK垜"Nۙlųe4 `ܿIkv6{ėmlN/@ק#lr`{^,X5:u/ơ㚉'T9?he`9U}]PD-˓Sɲ`q(7|ltC$7 BtQqʻ3zI {EecӜ,;U({۞`D^Q0@ I8sP>w*"Dg0a)Xs6n Abs9e8FӄZ_@uɉ+UScBFhjo'v(ppR&I kLmBJLW&J'd#]56 QWAYronߛm{½ QQbS˓2 a!lXݼ ;#Y vQTuΏݱVA&[.8dʛXGxo8hl?8 Ze"EW,a\csӁ٩HF͖Tw$l2`F9LM#8!!*-1A(U12f&ښ[,rj];nR6#HCH(7{BGC8۵QU7Tv !] fw 27 8Ei ([XIxɉcYY,@4@!Pp>1i@'HO0Ű[h:i9LdQU4P1&dz88<x~]c1og:OKvW2CdL?z.݁_]^>RSDS]yGZn~ڙg}/@pP 3 {/֭7xjn~4~Mۘ2(S%cDI<{%GO 4~T^jI㹇RM*]x9~}>wVE՜*;aZ|_|ē3CM5bԗy4P>"k f5@3^wl' D^n %% a8?}rbc!"DVkv C H8@Xl65!i=߇|c.݆+pęgy2U8.]:v8{OQJ1CEs-Qe.qL8" D C6J͇:~/jh"% eVW_EXD1`B{+R *`%p\qq N3]-GKJYUe4W?OFLJc(OÊ;=|"o/h{iF3;11]͚.,'}ppKLJ)k7./E,:sͿ(ŌƊQoTk^j 1fx?=O.۴71*ܱ[0µqF,(`mIw^ZЇY$Xj^] IDATRwD-EHEF7N1UN;{"}=*0@NX1z.}:K1'^1Xh ͌Bͦ1 \cKrG"' "Ytb]WAHHG0Ix@=.ۭ,N3z `.0 z/fV='c^*ϯV7[a/ۉ乆lyLR!>N@<ՏLnHxT?6wp~ҽ L!-R"uvW"{'4])o(?ۀ~vdkQ_nJG.)u|[iw`f?t˗jF, o2Sw7 t}NN' [3̢t?;28}0D!F+|_>]^;19fso7 ocB` i/'N?3c/0 ͢~zP$wScсovHg9W,b4+\X'!Q4Q4?(-2yib7eXERW4C%Y1: P(P*˲6?['#3UԭIT8at^`~) '[F tβ;eEq6yIbe4:OpFÑ \6E@]qEc-[,3cey,s}91Ex&jt5,m q)#b41}6keDf%j2>Hp QQt,Sbk4ޜ' _/|DcC-U@dt_'rKOKtmCLJ?R:)}WKюA"OZ6Z;yߌy3WF>L4SMg͂ΫW$;JVɁ!Zڄ;w " Ҧ{#gV9ost r h n뎅~W.!HƷFQh?tI=zXj0 [UgLohϦ9 @8`4;T5XSB!_Y4f5lyp4j;BĉḰ4|PP_X}B"V|dLCjX'˪ ,gd Фo;X8c*JeQT4%bœ!iPfڊ'ۻN? 1Q9+0q Ti<]%a]3DȜ܅ո%e|Ac.X׍Cf gV* YUl .nB1#)h[g1K8gWV v<M2%o1S/G>D a^IvL`pRtHhN Ro[C/P#J8MQ QL%@tw851]5s/D),uIv_:(tU3S9Wb@J͸= |=;U (Yȩ,z;>W09uJOrpp X%FZ?zx j'ܾ+gW !/o"B^aOu9lbkD-3>[O/V8 [:*'F}h*2W$22t/I(v!GŸ@B$ ;s`v'' j$owIJ2fZ+BkOL"1FU!EW_85B<\@Uk[qaa,bsU~Fӌ X'}s3eBLD)D'W],@ UʚEތH4 KA_GyRr=z2 t9A߾LH%=Q@,^p^Hx Riϛ3,oج&ܞVau]+i3ߢ1"IOm_ZZx+yaEJɢhe4D+r}:.Z}rK};T?C>s7 @`$_hy㧝o>T弑=[°;goQ˹|ȬG+l.CCJ~TơD(=(pb㱱53dg%R2_2'O̾)RitiQ>˩:Lpr@L) b(L}wIǑVh C0U]ssZbv~;f_&or֛b[a p{Q[+IS/`ٛ_ȭC&_}?7. ǬPA%z$@DN9o)o,{ͳ7TN\m}Mbj@D^NB`{Lԗ xXãфaEt74<qKsr*$I$# ڟ" 88  xYѨ",YQ|5B5]xF6I^/r $LW[qQUVjk=٬,C:pI쒑qjm4hx@qmME3@}@ (=~2Kܬ*|k<:Ir(JhLy'18dm)"K2@~O,Ps Js7N~F  pE핁9Lwd|crZ%JXN}Ëh,S7oPBMѴ;̗ix^!uLn }pzI PmB:]X`ş33(yx(7>}|?:̽%F7bT7х~ot9(jБVL@hsB9TL>I /c k:ˍV8.H±k}Dң^{D,=ѐӲ@.y_Y;AY{ t-X~j! < xX>C/I΅RD;6 (--~!<a~FUvk,W85* &'v9@1XEșh6smeC@#i .77T?PA GTC g+{A)4!-9*|G   K+qrVL  EhE@f BwJs J(07RʛT/XT"B+-vi(WKy{7mܼnn]YR$&@s_*Z}HEVb;;pQ2QZְZްR~O=p088=vA)l^O5#SǠ%F#RD1ZX.e!h,OdB`L78Bhn68 BᬬyOH[ٶ޽=>1/E32f;[)ƹK|"uJR?:.6MLsǵ@*k'FcΩLZGI^O8v 1(pPy~\; J<tܗ[~<)Jahp,W"?+Rڦ/5w}S fVk(`lwZE}9.N7tc4 ,CpStGĪ[II;.0{9ݿTOc嵏K+F`hUU` @3ᬜKo,7? @gfs];CL zE;sɜ4Dy bV. -*b1db"$y܋g&.voV=+$('Ma( ZCwg s,"HZcm8tq|/0 ^`,m︂!:!9 ܹq*pPVsW~sb4.Fcf D'" mTyCƙFFA(XUx"{<>~Iv;[+ ڪeeB19 #,t6`]r몞̾9̖֋e'ȏi.gOİŀG+i)H'/ W&EϜi:B9!]~@^/R5ikyAZRô~)5ttiiܢp4| umF8#h}I^gѐĭ+Ǭ%rkuİوjN)GT¡֖qe+k=' QZ6PdVdp(G t6M-]_.9<"iIvuH٥ȹ9AJ5skfwtܛ Ӑ(@f9][mw^ .kI)'Ĵ6]y09c/K:# ?t\. E$/.{7?³=y?-o :9]F|dno\GߑLһtǟ J0}l,#TܪݪE+35>9ۿ<362pv6:e$$?Kwv\S%\4!Աckhz.ɚ+\(2yI)Ų 0(`*pr>(Vf (`c K j%.I!HLAD ׆Z')j4/-d;y u_ovBl1dfj"3wdUE⊘lBu5b>o#j~VoW868lY9at  hya1V.,#3=b̾q]A\Հ/+XYKmo ],t+rx.8#'ЗU(Tev̔-7R:D좒PV]͊Kզ]"[ oDJPc/C*|ro-Ex<1L:/tOppGe o^5{2n~hr|ۯ~O{':މv0~mamݷ~W8ԁ6E9Nm / g} >,kDf:i 'H 7~U]p4i3PpX0wkV0wG;B3rfhRپ[\WTG=WPl``{-b ٫os9kfdf\:?Rd9ZYEJ8B.iEh745MKP"'r2Zg-C ~kП~wG=0p_oJn:$KC@>Y>^=CФnwj`&18w ya`RCyi@'?W6臒+yB|2"1_. nW*!xɑ)5 X~3B!Ѐ Dz`d0)F"9jXvdvݾL6ۄt\]5{)#PUOj#)|!mdZx!^ luU/9EhR?$nvV-ܩe)w8tT.([3>_d`Vml1vufzVf9CƢs v8b6VRlRd͵Q;ׁs/s-W% .6!gsh0ɇ}r68QVJPe땜p)}/*X[XM7]b&T6zkϝF3`hpnݿq72`4.M~lpٔӂଘR(Xe`色҃Vַ>o'LIl);?xoʂL|-̭u?~d-|(\Ч< Sru5T/.i~V{! r6{jd|8\&T#8(6<u'CacHnGK1(QA²J(+gmz %,+_f*˖V|5/xRW]MUWTknp4%qczzVe\ h@tk S/萋F*W4N6/n}0:MJ:3FJ_eRDžHy9Oþ!R1Npmު!Q-(L˗'P8v^C"u Wucˤ=\9sQ5Ӊ?)c(&M*W5/u޸v;lҽع 4 Uy+`u-\ebwN}rCgO;Ck PTT $FnC/u{G p*άtnvvT/.Si4Ȩq;8#pȀX 8:xY@RMQ$ Q}b6d!j0&S¡LY|%J(Dt#J"#~YNg3C6fĬHk w;f69#h?@w:ǜ(:/JaKUoQd S\zdQtzݰE"{}ܛ?! Jho㫟ysfwn󒖱 S :#٩ucU،p?ZoTI'nE,+~eR}EV^:k]NIޙv,B';h'ISN ?ش4SՏ9Nam~p6Gٲ$w矤bed,2:ٯk Z~rV8:XR%4!J0۫˾陦 9ƽMtj:@1B\{lDqٟLߏC3iϗYgŎDC0-w|~Td@8nOG-SPCTMUyPֹh4iHP_: D\cʗH|uNh?lzr];[vzvGD ݐ]OS8]4@iZw`:~[AE5 DvDדwOEO &åHJk򽹾&<j1' cR!Q^=wG{12r PSXQt̤5Vd:9)@Vֻ*W"I<&!YXZJ c&޺$wBBg]9+XtcȘ Wi LArʩYau-3`RzRWCtcsrfN\"#EH,l$9mobζ'3r6 GL5 Cp2qS BjA+œh-ֆ0 {k*XUII_3׊g/,R샜qQ4B go1\ŠY\񲭋_[p$piÅ/+҇47?{P:r3_o!j[w4~xl(8%j5jmZmku73xʣw},p`5 PhJ.MAhaf8 χkV;Kڕյnk$ ݗ.QTTѱ"K 7J8Kgų'@wjƧʂ338:.]Yskŝa$k͍-f֋#$e[}3b6ǝLJ\5IG¥khm^@\7R~%#8Ķwz Q@LVmvSgZ&{4Y~Ʊk, %NaJ(1k@**-H3>B$/\9E Jh1^TM^ɐKuwTËq=.eܖ"z3n)ލ͛TP}G7>-=Z!̵6\> >[kS;& 7pow*j굛;ȝ^+O~=$9̩} }BD>|8^_rE]o,32`Z#m\_.Ȥl^ F;'vCH%TׁC$q0yg 5t&u5_nzS6:6::=B&M.~miONg*=f'I*qLjn$dGQsnt$zX|qj%tF eJC銲|IJʜ)=2֦@ bh2,_QC @k@dk lvpK&-(l<NGttN@o 5}ׁ"d ^-(Ҵi9>/&нf*™]@V eVD'o}j] 8I:R5:[TVϰ&nS^d,Xփ[90^֦ɾ@*1J04446ٳ(ki9b,δɕT-ӼLApLirn@dN Q#)1ryTՈ3ĝ+KaUry)%e3se3P\@AG1$tBӵ 8A&N"h`M:<:0T4ݑ7`PlcWtF :EXQє0j=Gf0@۽5Cdnc|9sPzR|Ox]$H7xe6{_i M"临qlWnt(KYWpXߧb}l0]}ײ@j7~mbj9!PyΜa-F Ʉc?18\J7DI$/h+FZgm\G047;pq|YoTl{ÛD(9:\3Ep@C䜷2ܫ^^nk;]X]kry7ݖQs0 px5(!V~8#dⱊee Zm 8?9C^O浾&0YJtuSJe#D:۽MQ,rj yJ S63"p۠6#$/M2Uw8L+&'\밽mgpH @xs9&Cɱk>_ΗJY[DfJcek륛0e-dJA ` d/[%`HRdc 0)У\4)fsT)hq_ tj||fJ) NlCIUn(龎z ș 8q5|펶TY\T깅\IpKz[MZ) 0 ~oFk4isMdtE^_3sbƊ_%^*tοuΨqrZqDz!`܂<_-x:[lۿY3ٲۃ.BrZ0ڭѫ#_ЮV IDAT8\i~spmnu[v6 Ֆ^{u_> PЕ[k>,ovQ(7=3BW[[ p0z&ޙy{~lNNt:qPe%60SR`nv7D}T׽Q>IPsሽi8XV\j8+`[|8R5d9怂nؕ.8tI 8H6[5r"=%jӋc#!8C{˦[M9IQgK`@ B6mx3 if$ NxK$JVV:JVW9OKdc㶶vdmEf9DU{O&8݁tUp~Oq<`Xi~%*u(K0681БuS}ݚ_GFˀ &4$F ]!TG䥛GL-%$2 ۜ2_Z .^xWx WK BPM3^qcU)ҿww&#!C)xꢺ)1s'OSdžOP0I[#0BU9,&*G,NV#G?5i}7PxW?K.&C+&J7-NK7 {5?t\DMصkuBtN{n"uQl5aqJ 9%aHT!#Սt4z NS^:CtleF5䅗ENn%3 *j'm p\/> |$׹NtQ[Y 'W$ilzF:I1!@lEHJ)5o[Zj>tzU5oar꒼%-cá(&:seetM \+;NT?sكN)JF64a+' R*-%X;oCoX:`5FKGX9decTfF#NHUEE_Qcyͧ=GiՅL%bN'rU /[%V?B+Gz01K o?#XsNDܩ#0)YR3:_)F?Ұ~7~_E:$*zYv!'W jlߞ85+_پ[el[T 0iyP)JEݓ?c cH|l$OXthn!gx=##9J"D׺m2JA?)KCļ[`S5q uY@jwH5=YU@(Vy*3p0E1ϭ cRjݸ֗/VNt6s@{&Qz]@UBڏI>U1G%|z!(V$Tsxu!O0 Hށɶp7*w ~M7~ֽ&k/ʞ{孮ٿd۷}bã"ՌPTxgn@'TjlBVO1 )z eFϓ& 3cT<EXe IޚYDfrGQRsRpjka@ci0y~r钲zF~,jkONU̓Vzcb095LI̺A]錽uT8)\ﯝ!k뢢&jkzĩsLiiBH Ӭdc%s`:Hx\/eX@aGSx{T*QLyity 0Ө!2$X i= m JI8[YV9}2@ôrO>MEejI8 )jP#6=G #$U2(TsKtf2"i8t#(y!|292dkbXɗ:wC%Bf[?>L٬;9 SA>bmFl !I Z+#p\I ՗ 8ft987*awqxRVj%/7Z5\QJ~.yᙢK =BKgᙏ.t_ۊi71M*Õ>7r7(Ӯ?Ӿo2B}?aD*foӱw0"ѼcRtke=sBdOXFHbq^4NH>N=KO*2Q) (!?rҵL,D ×i~I Ș)RN Sl3d&` `/BsO}yq%]K}Cu.4+*|3PKhL+b$=Cwo&HtmXYc~j Gƍ&i|5ex=ة`hpHb gB[H"$Kj}:8n+ kܝ'̜ ICd%6@'c {ܝ lW2KtL?5ISc[;" BV=L@%YTjV&ZyjgWT@:ykr>NdJҦ TTOMKyBФ4 K!Ib^P n=p (%TBҕ-4B!Z֨ TQ.SV+Ppkߥ=!G=e)z0tbLLIԃAj N[ D,+%0fRnn&GdMFg8'h,cSܔζ NHd}E C;GșTN>Εj#k-KP(lsV~0@ c6~fa#h*5 @ƐGˆ,p+WDdFfnJ 3s]wosp3+[b朏ś K!s.qKg9F)Cc^{T;545ȨoՑ/rv5IL|l<'ڣ?`0#}ч~ڣקr"RS2ݚo 9AeAW\hݓ_q7.BɯO9+_ٚ~ !o v!p$H|'GN '^5tȫ!T <T|[?[6WNU9 hgR|0dg$*kM;sj7;MBu ̭0Zƥ|<)d8ĶvT,f_ws'wT`ФTwOkuYs ͫT(0 $tv 㤹*/0z0^A`z3'Ş,aqZ/s dzU͊MA4ThEJ&C˫1hTc:IZZill0wuG i>tNPFAJjmnXDfw槕Pliֻu2G0Af8pCw+,ߐ%_:9r"Q 53s۝n3꒸q5r`*ve)!! D9 ~R 3)5YY(9YB3h;wu/ ?ebf{xN 1>pËOy\q,f77_\`v%{w2 1D_2Rsi_@>AXT9G[eQfƏǾou0sXض@}sOV߸qˢxl`F"g{'~ * 2J3d&ճW><ے{ oy$'9s>k?;x֖J( vf$39Ew!H#Yugq$`!`:Ί%Fu#LfKsE䶑Q)l&e2 /{m-MPpx`+kh) uwF5H9|zI_) Yo9 cb&gR]1I*3U-$)ZBMg(4&t?R2Kh$(MtVt;Q>lmMԴ)f9-.Z> hX`{uי8$˟Ln_ L=Tڻ 䉽!)}Npb~l-KɤSF5er>qmE5 rC̆ɕF@&8pn5)`Teu}PJg EJy/E?BʋFq/ Mo.z Pe}ZZ׬3E^]+-oػnCHj >sh쫖@+Ԥ;8`{P mx(~樬lJjrPҲȑ&JVo[v.ļQ>rKl5/?|xWu)ii꽿mV=p$jnM" /|oe,M Q*&\sMTԮ=g_.9lLFTrs%7hTd? XV/.lutb;;㢢.bzP_/*en+ #`6e`x@] J+'+aWU )䈆OJ7,lZmB!Qb@Uj TP3LAH tDfWQ\ YJvFQӹVH +e0n0@%Ktz"~N<9 X֗`I[g$0S #KvջޡN|ajr4]U1'}>H rfAVⰹE^`>kme+YfO)n\ j4Ge1sOfsѠSkY@oGDU7fͳ;B3!H…\.ԗ_~x&SE_9!xI^ǥ8z3C)ű_kZ{7>5=_n^}ֽ?{"i6@]ݻ} w%ib@0T7#kYM߾_02ƻU݂0Bʚ[0Tx{aNc\T+I]G7?I~F|TL!4x{.N5 fة3RGU[ˋ7x/ ,:*x_ԳD@d9DVtI}h ?V9(my:>F[eٟɠ)MF u0)hY'QPHUqLeY\|vhCes,2G4y3W]τ9 5'߭؛{ H /aYfPwCm3oENCpq{@éx$\J6;LLia[ rw/7Pxy`zk 1K%s[+ӏiN|Veܽ5j-S:{9RoruY.v<;!Qsj$W7sVѐe@t xp4MS72֌̔έݴ  r@!vɟes契mb뫈3DC{Vy!GdHR B:no4:Gd,qtgsxjt#CdTA)Vlk3_Z]-l54W7eY:<%"V'@@QQh mq*Y<zwGL* pڱ8W"D#cHr]\R0+1gKzѬ;pu#夳_N2GC=eq 2 RL&ꖶC)4d I*zpP"k{rdr1 7D,-Rd44kܴvR(̋f`aC`8B\sZ1׫0d(fwohq\KM-Z Dd}*h6}\qzN<].A[Ȟ0 NUd[Zk3A1>49B2PaF 9 c:e8#`>~觨B"~ Ʌ`Cc xkwW[:/'/LuZo6n9u _w+ւ)txō^_xҩTs96KH@|~~bkkV?DEdJnzpKe?Y2WtM) ,p^绽뻪읟}u>x>SյRdp絫NieT 9LZ;חҽC *Py.1#Bea{&[Dž~'K@r?%$@D68NP l" \Bbe_˙aV=R.;ߞtXt}}]W^.\|v!Ы 9c'eI2G/gra>̯CwBD%G-I$A5΃=j:]{w…ã\-Hx![&@W <{FgߏH[ٿO_;B~Z* ~ 'gpq&X];ﶉs#w¦|AY0#@6u-Lo%ƪ[H>wcOU'~z7U tmݙ/~__1vٯqVf $SiEZT!"ərxL,N}/Zc Vٴ5ՒjtwG >{љ *C(vהZ^dJ?԰I$Xv:/`dO~;9.!8jC46jٓ;x^w݊D蘟r5d$@4llh:c!"P!S,;dE#K@h{w-gs)r8W#]#ߠ`虞VPRn(lU4 b_K~٤N[> m NCf0)%c^8hSdw+[RJPh嶷mb޲0C p7n+G3 3fpqp\d%H"HμP]ljKv HAf'eh,onWOԯ-G< 'V,wjtwZYqFcPԌcUux_QO^PIފD i)RrQO]sEuV %\草kCˇ_\nǷ^>{+n$c~!y4 G.**H9= p4>G-}WK%m?V 8W(\#ehSwWk2#?w~#H3j/r gh/|;Ock/D魺)L'a|BpDb$>w>[L'+| Bf"/X\ہxXrij4ո +b\ߘRrHXY r`G+XtU HN( Xzaqqa,YHl#T~/%GwAJ߈ 9'`5Ckkgn(!K- }+\3vɓ;)˙X( 7 sN0#xBUvQYXR8R(ܫ;=xxnG+d9$J!uns2tvc$un$!UpwE#T5n`jl m-"CPv'2#z(T7*GӸ`PL[W~sE5 UF WEjh(|> &tńxڒseJ-w,JY@ [~t;v˜m3Zѱ# ^儩+a| jΛ o0굻zۆ@n{rWl CCz +ߒ OVO*.c>a3F"q|o<ﳱBizgywVZ(~UeJ/?|cVQ4xg~ *xkW12,kU*Cil?@;9L5x:-Uj&@8cEUcw~`U3)tl\']qDTe)WN[q5}}xKvjI9ਲ਼S)F.>BUy&T Tb9DH$PԓN8'D +T! ¹i{AؙTO >j'ٻ}.V,7X.}aŽ@rGK-–ia) Ch/r/I" #RJ\^t rIclcOV(vuhoPpЋĬa [b̩KH/һŎbձh]dsO\(5;HiڥZyy ;Vbm.7qFj}8PƲot"?9̈́NOAqli!B V,<W6uӔ  ?ZrF ]C K 5녒TòЄc}Wp-k۳_°VW;o|WQl,\61u <7B|?e}mkii霰r=yGdlaK)8@%dא(hyk=?/73ʐl]s!͓>MmS=]+}"`B=[Oժ@r[fOߢpm[ZO|.x V+"*%=^` r8ܛ@^r}aO/=]u49˷A|xv#6  xhq wu)ゾz@VvoO{ wOp@0t44oNp}W O•M";[i{vBU`'H>%!1CHoy>1 AoS܎L+VV()^m%C/ l O72'|kjʝt&RӵA(Ȱh($ᠿ-qQ%M>bB2RB@Xy?zI"ֹ+wc:\Z 7kVx{v{Vc tmL,X+"ROig&x8pW%c~GJvJ@L16yt;3PܹG2~xZP=tG8>yXM.>|sc;* 8@*nɇ_VxZjzs ed$M8'WVF=M~ȅW+|v{sU *@p!$n9;+_w~YEأ$=ozq,q%A(x"x1Nݲ,I $f֩Wl: IbE?1Th  {RbNgC$X#xR+n4m>v7iڂ3egR&aaS*(u(ѱX#@$*8Vi2ݽ,mesy 9:S٘۴{FE"Yv84 |oBZ A^xF w'6l_G'Tk#C4ŷ6{ªk Ix]>Դ<$\]V6ٴ?/dyy]] ;[tE uNHx\._л.[&kmO \E^ofY- 5\&AU4҅vXp3s%rCD$μ@\ڟ>߳_NCKߐLŽe67㵆3B@@L'pj- )FkhNՠWң`m(9(R /UJFxƧ"P<f[YbũW ܻO8Y3#a28C4^.  Qd;g5Əu^c5X|^rcmqӅs܊DX;v?V*nBQ jʵ4ɪ j"FD T53^NDȍvs~ɏJ/`;]kMޖ"[]lNPݞ$6, O!&s"fJ!`7H& aܔvIV*E#lE9e  TUtKl{qO 9\%Syf 8[<+ba_;Lǿ6y<7APr63**n:>..|MpgNW3.]Oah'k(E3Qksc~a٭TTOP+pFgrq =C JncB{ " F:b9ΙCf PP{ٷ~nt}̗԰/_be59RSEUm /}vc@W۩5|@-@f#z'_X4S=pSU,g#T-ێF$c(`| !sho|3ZLgK̚4|P(kDWe(xҏFf6ID@ lcDذ['R֎+e$ 4+dRQvr;q%%M,:$hʅmAIH2KP>18ho8ˬzTNKFln?|\}0[b22HWI/{G -U QSg 5D@kŷgUI *߀G?ֱ:=9 8S3fW8}3G2'?mjUj!s^2K4 ?{J]=E USeƔB麟ktyoiTLJHx(g~AkK3hGaE>a0۴r%/q:|fGڞ`k@s ] 6PQ(- $ wvm0cOzl`6%y<={HR^QK5DqJ/P D~|V_͖j~cmїщgV 1 @;CCS9YU8rt@!=/_9"V9·YK hZ]z9S5> w5/VO遠nc︻-{T N0x0ozRI`,uoqze+#Jfي/=Xgb_Tx`@2Vi'쮔(zu!%ܖH27.) 1iJ0)r"AMmox4Z rtsVZ^E31fJut\> !tYVa Hd%CC>SksOݞIRNs ˹+1:$`ï,U#w/Ollo̢eK žo>e­0ylp}yn(>% P0(Ȁa.6lw i7 )9%/[V?]=pm`یDF]^}7FlUXp-${7XQz_O/0R!83wK&]B&V,Z2J'L Z=2y?xKz$HnjijHt;WM~HX'Gɡo" Wc j(YĪ 2~32?/!ϏO~#`oQщ^/Cp0 7&%\߉Yώ[*lPRJ:fvJ*co"ajcw;c z!dn(%j)`9^*QZt +D%[%|GW [ΙG JD1[%,c(ꩩ RC\/GZ}/{ a!, )O&BΔ jPo#C"SDMA]up@kkRq#h7RvX@Z>8@δDqm iIk^T^[AƘ+Mmhx%@6)!r!Ρ6g@Q y PmkJ%@Ɲd4BnfH;:Kxm0xF9[RJ&yzlh 5@ƷTTS97wBQRãu{{}8(7ϭPdFC;=rm@|/eZ.pSKnF ! s9s1՝vؾ',m(!K>v7.Y>qxmq**w߉w1Yң/َ{V~<C) VOvjLe~ƂD|W_oAc@I 9::g~3$,t>gDHpl/|ixH4}0\L=gOj2xT!P~gq MP&>3}.LX9| T: r{dc~&P%+؁G]F>_I_յ޶6 Fc+PHn;v8?oZ*遃{kd(T%?ҦdC\"pÆ96uS1?I|HHAk@\:BRo#uj"]fS a˙>t=aw (<^Gq}\5RŝmDzCt5\!%Doj!' "%`4BjKE"`eCR F>mEcL}}QJ"6[@Ž5u-gsݫ[>=@­^8>s325C&д[RýϞk̳8 ^.~]q< qG.HƚBiS3m;jcu3Ͷq]}\| $SV-=)nOT`Oܳl ;"\W5<{/ !Ύ\>ҿG{'3Y0u~䧇Cx'yB$2Ԫ(e=s_lIlH/1)$S +@xNۣٯ3R~_K$r4kp9- hNLǩ{G?3!g2*hzz4p@ʟ,cݮ8N~^=PCsӭbVɏ,a`FfEcw{'kU#I[{?VJPUz5ϐ¡rs<,moj_%|uu# ߇$p'ɍG33yai1&wXoHP3( h @Φ$)\2=mq^G╜) %SKw@Sf0%.!+}d1JG: PqoI˜&dUuRkev ^MpJ$\ښƲ@Qdߜ*dEs??Q7_8i{ fσR*bpdyc 75NvcU[+HEv YuͪT$'_k;fM湍Gm $PJ@ޝ'sBl M;)Éfn:Kc[ycOl-HOo`ȇm]>zu񺗏Ip7|,o]gv{瑽хL͌/zʟ\P XȎO2UPp I^p5>󵃦/ /d*v>88yp.^U^OBDӇwa%+g%=t{Gǭ6Wnɵ 3 ~g遲 B=-$#c^rRTΈC2˥Jd6r#mLMWH$D/lX@f4{H3r$2q#\I4EX fLŵ4*!M Bذ-[( !g/SbI5 DP )\ n*约'\R>I!cﭐc!yܜ檡t QŒ$=@Ǫx0 L-t!iL3;3Lzn밒Z&J7ܜ';ýDyknYl(Ӟ֢?UJV)V:> C/ @05rmvR+|@j4cQ0 W7}Ghfkд?t.6{{+aY|'}η~pK@P]`A-d+NrvaϩT*ڟi3k%=4?EG oK&Emf,o;>*eF%=T5z y(e<w{>]ųkY- J$Vp_s?6<|,>Vb @h݀@?Jy V;0!榖;;BQ|!=2 ]S~s%+|yL+P-Oe?q0ug09fB YxezE>F9묛 [H+{Ѐms Tpd@c5+Bud@Dt%0r6pg7$3 Gd #xc4몪 qlKz.$ES#=V!lv<'Fj8F! i 뚉_2)}}ҕ 3(eO0-ZX]U@2~qЮd3]@B]j_kي9va{+rr\5OՌ|dCiezt pX 7cΖ 遮գnDKLmPوp6*v"bFu''$2K-tI55 p$&')pM_T_+o~1DS)8^ǵ52E@x@H Fcem]uDt<DpS[8- *oz\[dRNj=Gm꓅ $bZKlpLߊO$ԽW11I >URQ uA9=#Y$֧t0dG4WPTqLhGG}#SO%/̏Qao_a_wR$7;HR0&fBSA2X=)#g[a.UFo̿x{#%+=\QRpn78{3@n >5CKÍvs? Os >ߏf~~lWUGNMT<_Uͯ+3߰k}\(leW ^)؏ -Mph!Uų>ɇ_HljR(vΫ~-,ޅ^f˿7T puj"SKV p·W™nWY~^̔_^Ahu{5S!4nM̓$BVDl,Qp/ rYrŎy*A9Zzvh?pHk`04[lٟ4fh:Y['yu_> JϿ}?݃ڏʺ^P׸ UPTV.fs\"-u$"H۲3;9zC77Y;(\E]w4Ķ`wp JyAeY٫$2Cnc.rDX8!OwRl^%7RZ!s8?eCU 2 PT迍]r C( aX~PԛW()gFjɱzB͋GcBC |RbW_7; çjvWo7p61mBvh{in{ߌmHG NO3oU7s-O:ffZ*G&Wjm]MWJ6!Kww> A7ػ;[rE 4cMH0[? ЍBKv2|<# 剐a5•ĸ)XC$N& գW?OwwZQ;ͮcWyK \M" كUΕhcknϱ~p :􆴲H1IcrM}<y+WuLy>dD>fѲ2xV3tm?A_.1Z(y7NRQ3 KA9tU;1tX|CK4.hlY>sw @-;,sűv7lU(wPGzRjE?ӍiF_ԠiV͊ojX׶v*/S}㸽[Cd ۴+"Or[ $Nɣ_}7t'WHA+:?3K2ʾ$=VkA^dO/ wt?7O,oq=? j$q ª߹%)K?x;;#O+}}s*ٯLU7gyj-&@"?Nvd{?B!$_QTmoO ( ə"WGfZdԒ 5^~3 au+4"Я4ȸY=nkyK.E9imhHV.D J#iSNX.:a1-9m1u Xɀ.ŃZyfiLۚa!8 ;N?h\΁@$6dY E˴wYl&54]uqI/zYQm)f:LQb 1!RCa|lrdx#^@pXvZV+!*+C`zL| #;ӿ/}[ie(J4Ųֱ$iGٹג81nםn8zD2IђGW],㙝D$ 5 2{FE9j!T]߳R΄=*HK%\C Jܽwdq&y]y 6Lã|((Ȑ[I#ʜ9JG DDʐrGv@{m.3{F#]% IDAT{n>} Uޛ7ˈ/#tŴ^W$Usr3F|c t!Qd9̀ լn BDWJYP2v4zv*4!22-*S-{O P3DARf;K@~@qWiu{-qy:ǐej㱺VMV*%3$BZ7s1C&Dzg0j@4r fv̶^wޛEZQ{K{Alp|{KYx3l_);quK[jQ۞kq0|xsnz_[ ^.ֻU^K -/eUS>x=ұV_ep6~UCrK`w:"$57{۝U\0X4aܛ':̋|a'x\ycJQR$7:gO~fxQ٘;-;{rQCI{尌EP.Rv.+ Tֺ2ZשOK5( _!(#sW6u1\,p_/(4ܗn}|P?ƛ^hڗ}(pycy|n|i&Rvʤ/J@񳭅Fvl9Rzw(P8wok6{= AVY&Ҹ}jjt.!/~_RHxJ1K$ '0Pu+g-'T-&KрP烣JrU-DKKO @T,Ըz x0GylO6>hs tOQ"{@%DɹZ㡹P +X6'lD]PTr$wݾ)pfVɎXMuO&-a,\]Mw $W@r3VMM۷ H+s%a{(/!ޤ4\'3I HF\WfRzwb ha'j#kr+wyCo.'HnLg{ڍ?hyXB hb}Cӓ[#c]~ rg(ԗFrsPYN 0"JULPR}W5kB҇d\JW)yuRUV X>Hb?-82pAgd0X!vXSEΪHEd qA@0VVv Tmcxųݷ C3MPMl% @b pC\Muu-{X?E)\]f%_ 2bAd#C 6k[^z-t';BU-S$pQc._犧^Z)}Q9_+sB68m?Һ^Hs`B:G 6ʢd!^s3 (H`xIZi$b!2TP: kk8F!b(Ʃa΃.%Rn"T7̦ nm<?Hdˆ:Fu+zlX|%Y(1F$wϙt vN>&癔z.*sM@t Hgh{okyŲ]MQMۛw(d(9u7U+󥖻fXx_CjI_49Rk _A5ʝʕg Z?.}``3C?r gFᵮ΄ۚw&,pP(8Y [>~{嫡~ C ¯k`/~ok~Hb⢅;@!P3?w&8f%!(CdBHLo!Oo}Jhਡ3h^xPAP"8Ň\xsb*И]=\M_~`*Ub$=dӸbtS,g>,2 P@RH ټD ThD H( ,a%"ۍ-p(FG鎕½eH i@p)cL|鴯X~Z̫ځ&t1$$uՈDt%+k2k=IΜ,kzSӇ3 >u4r $,7c%W2~mcD<El̠~`J@THt-h he}L D{5 ȵ pkWp}8>suC+@"mn]M+ & 盞UǺ[KBѬglPKG]N[ORs0]{V~#+V ΌF,Wh$"=^F;ٵuORtGk׷*Z+vHxoyrig$ xΎ?DOtĞn&G/0f1w bή5O):ݥ[ى۝*Wב?ܯ陞pm(2*TGT%nA +ksHˬFH$ci= V F(AA x*vL0[ƍ(,͆H {jV%Zq@ztkNH`F#{ Hp"D'޽}yqe ]м8R^OWσ' T=WX74B;}qkenEw3 0ȗo2E$B_ѭo8 VJ1)$e~ ݋^{']ؽJ P6$gRNE:cD3YqxHw0HdVsL[= e=mԳ:.WcVUD~+Ow4H=;~89SfrRܖ.# 0DdIZ'(%C ϩCE%;YaNInӘGCR Ƴo?"'RWhlNX]#i]Jy;#w'P[nho{X6q^\1 #B/ .Y0l  A pWB!bjRԬ0љ uC7\tn46@a2@$]"ObW#&]W1qaIOSu//}><{Pj?yo t$! oDg_H>]5O a˛bF9,:+1)sؽ:v/$b ,zhM~*3n{<'+[so|w=,6#df(1z{jZtMP kQѭ;ϖaT[8&-[GN(svɳ n{Z$d\jz .K6w$yLV,js`3$I[)Tdӷ`{ JV~y@J'[NIa# Fn6>,ES1_)X?"<a3gܣ)nHAaSJ*h׶B0ܜpC pi36bk2zݐ&;k &@j>pl@வs qiYyqû$|5bJ1}:I@.m\yDL@*XPJ`U R'; ϴ3劀PZv(oD֭cߛu&ұ|q㊰zWhXI$){׾0!+NݛjܟJ.0v0WQZxzgǦ>鬣ůKf_0ScݟYz, O KON"@.3"2(Fu Zxis5t&k0!O &rB"8*qz>XѻLH[7"gwzdU':2HvPj'tOt;Tv$k0hE?7- daJѰKnHPjpm-Ҏ\E$lчRP1pj|ylTn N&[0Ov>*H-on"L}uvS]')\x.~ 7M8QKe5 :+SAR 76H|S:ڴT^U˫WZe#*1׵'sLHAs@2ֽɮgug?)n .7Fetw$jkC crlBLEa]d=|Pg6o#IQwL=^Xй<¤8m=ب蜑ȑMGz2)NR+ЀG֣޳18nj혙BPpnhh\Z @ HjX\߿l cM3GF6m|(+9sJpjJ&1es %vH^j{}Y.ݗRe+oͯcQ&k*2%Jf8x.(/>LʅIIVUخ]%05#s>BNoK#xt??iЬ @1?v*Q"2ǻmy1=sY9(v?ְ?~Qo@$LrxJ C?UxIP[B)=m+T}c:ж0[E";l^-voʃiR@p-dζ8GݚVg+{\#M $@)$#bJHߓUZ{(`IJtg@$dha]o`IrvVpi{v޿tU2@^N ~O{\t]%Rt=}s/\YG麆dCs^1c~`A8nBj۫j:sDph{eb1ڕ-5c:%r W UMium+#]\M͍6@ah8^2sݥs*7SVDߩ w 9Ip3\ Hcw1z]pN4$+ҋF G $#Hڊq`U?5bhہ)fa$[5 (|aV0ߐ$Dwl ŶM5#e%D3\?pesWG 0)J5/ɗ%UVB-\G WU?+~ϷS{_WnRz.;ƃǂ+s?ߴڭNI@NFbLʰO(《=CwZӣcZX!. W5NcJv k}|!PKWDukDnό4"gu vr8}T z]$?@C + CRh pڶ0Sqz-/FGRl@A_JB43r܃[j!]M̪ m=kt:`&ߢMIQgW?Z~90:^g Z>*LX' BυBXqOH2Oޕn@@$Y'?Rx=[b ۍZÀ$eN]1in?N|_b)v.[q9=5R><+pp5v u\2qq s55uϞ ?;Z0he$R-[(\*LY4quѮd[2+kښ[{M[#+B=gn0Tقv};rnS-t`u+IX#@<^_d|'z־`G= T`ea{ -L-['~4b4(%t4[|d kt`RSWp>, `ҭ̬HĔ7㇛ѓ]?ήNnmbeǁd@&㞶Gv sHn[p<ȳ_2Nh'Nؐv}X]P^:4W^}GeiȞ5`3 lYj~_"|WUbTJ2%\ zOq-XsOS9 J[@: l'j@݆NG3ԚU7\jw%HU 'P: G(6JѰHEOÃ[KOh(J)9?uK+;C&T˒u)jqtw)q`|P*WWׄWX~Sv^:ڛo)'[oSM˥ /[cWXK;{ ?tߍM[y,V[40t-ߝhR[p@ftfge} E?oK.R*]8q+qV} ӿўo3= bkHȏ ~Y zy *Xb['<==SB4e 7M3!O"dC@*sf#z:["p-[(|mMajs78@l} Ad145mKD(pstb;5~M=(ay}y]4҄ ?oZ&,МKr#\EXdssMeX&@U냡|i$J@xGH<HsHܕJ Ko65P[`(\BbSˆY Ikҍ}v82wYRjRr k,7mP5 BS\vt>xQ̈́ [2`E&:'wɪB\v龂«]ůl:VUN׬kpþQe]]sEu=;6>b@@EFcDIM+S#Eek- 3kDW2uu)^;$BɸYJlBOW1 E-ܩS'N#_x`|\\ٗk_}=&KfTdL!Gf!ȊcwEcdSBbDǍ]=6Uki7}%~~Eh@5\%H?49wEA $ZiޙD1da;Rꗿ-@xH82CE^0Qlo;vw:g1|6>98b'Gi -}v?,Dt'T~5nA_ap5zMqWcNvr WAFiWn制ЈOm`g7ɢB:?;}+qoTD"8RAt13s]ApשH/gF~wk\|_쨮'~,چR;#iߛk}ZnocMY6Z3.L$ JY2T2{QB{+coNZ&a3i&4ĸjZe[E'fͳK-K 1 Ŵ O7jc"bvFT'b0ӕ04oG*SN!Fe>LowrV)WFd0ezMԟی|hD|@8N!2 &cO+5<:LNHQھsMd6x~Hoɤ3١_Ϻ:' +*HW{ 2}GCȄ۸7mɻA_ur>3۷g]+犎PJ Px ~J3v-}뤮se# ‰o{V|frQ"A2 y > DB{۽)L[rPpz6A P3R լ9}W\tQv:wW ObJc\1\p\`1=HAmHtA=w1PjMu1GaB?[6(#fQ7w'Du ,l>wk63 Ls!z)I*Q`w*!~ nEB&:\L'IͱK7BTl٫|*W[S'L IDATY+1^\S+]r܈JsD/( +\L(%:;KsŤs6 HFڭOrtm|inR+C7=+C J8=$,'@LG%3EM2b HXGf`σbRȢ-+?6O=%TX+; .HT2gcѡɯ2 N wӛ~YLS RN-5d;=A6^{>7+-[&N9ןNۓ93b  z(:r.=;7޼I[p%3x/JNS(" XƳy-Pei]qw$הHe#K3{#I-rmMvHHZIiW̞3̮vdFV5қn}/ ޤg"e%Pd"Nd˗q{BVdښR2$tG(.b?a"D2W)H" IlxAzlo!&=PfgT[GF~qN@1\a0BC5M&D%T{޾UzKo0vzzYaEA'`x3 s Ǐx>r\0sTX-r.l؋|nG6_#ўoN2Pgrۗf [ۗ&?r,Z8TFzu ZflߧLiJVL}ioνH2FtrǾ (?&5N(dE+?$'7ޘޙw$gF_pǚkARAMchok沝=or[=w0<7дn!()pl l2Ÿ0KPc ٞz{3~{R>Y$ٰn^܅c_%YJf'9ɜPg¿TSh,fɽ+or" YSlm/C]ӛH"ÊҝH-Ln/ Z>m Ef~Xg)e uCZaϮ ޏ`aʇ̉5CnAq Hˆ*N5NNm C18P(eB?d L@b+Zq+⮨YIek^:?h XVJʰCT0lX*f~:-BwhMsk_ nX;η+B ]>0l5<ݳ-cNʽgwޯٿ;}I,@C#?2߸Jt "r;C 'w?8;#&5so^aS8%|::"<0s Dc?}^!_橈J␅AO呫@~Ȍ@j:sCр8[vEMURh?[ nUc(LCO[^*#UY^80 $?(E!omJ:T] =jdG:y9+kC[# ! dG#ۅ¼) $7LrudI9ՑN3$+ fHFf# =t[$ԐZ#J,S[n&_Qrl@[b|=uiRN8Jf6]GqN x]W^ AiZ!3B$ tC uݞ@7#;nIɁ4|F%m+P#zZGjvWVAd@RmHM[[5Adۡ/]3p;r \m7/xfQ8TqmbPO)?*tahY$)ڈBCjJ+b"Fn<քՔjv/oG}Tjx yTW^ ;9:se3 3Hyq@I%,wu ñEΫ piI,($zlc5πr!>Dy]d &TPskYbIq@ifŚwt Q a U5&ڇ_7 yT N8l\g+G^= bh\@ 6:i`R!)=/C`Int K"JE~c/,,G2nu8W?8g,tKo˦폅pj۫ʔ|$H/ 㽋42!kߗ L#V9c B!Ro^ o7o0)j<^<(T: XJﵑgݥgRmoSoup@T{PasozQCP8A qsbdXSS}.>p^,gݗ2ipc>н f~B 'Yׇ=./`m||;W{( Lf,|:@Ȑd: rc( tjvfJ!:xơN1˦ 瓁uR"dE %XP"ཟh @A0pWL>[q B($@Eƪ 8dK 2|uf"R""._uwD|MH_vx/Mv{{]'@7n`u-5?2q78V`%TR\:vMɬXpͧZK9*e6m#C Cb,Dџ}DQ=4(GM!KL;٤k(l_#!J5э = @LSZ$W?(z> L_x)b3Dv}hC)j֜hdλT/_*r"& 8niJDF&DӌOPS'cagg8PW/y"N7 U_DTUo4oδH 2Z^IC^nݿj|jgC0ޅo#@6RoL VUƇ Hxs*ўHR@'5 yyNQTH [0^5BDɫz۰sODVkF8H s;IGS X=X$Vî՘Wʦ{RB "_@ #Vdo ~h1$_[@gTSy] UzSAF'hdV E(j{z\}l{au@-qUR {_xe:*ynv,޸8_\y;" 29q}h%"fBE2MqbmyzΏuVDO5q{qsz8~祚Y/G: Y>O7SL-$BwGMwk" |mtJW'I 4QL#lKϵ/܎dt]a@(xg/}S G=LT8+=^<Mww0 R-- LL@b,d͑^d;MD(5P( $` zz#9R3K 6׾4-9ZMk;+9IILS!jvmb;^;p|lcÝW'g(";LlXoh gdqe()Mqew2D3y=F`H@@2+`o Y<xH2% #oTB&BBi&~tgJs,$K^ i!Pts,T5ԮhDwŽ5Gz`wŞcHb~uǓ$ ݡ G=9 TH{>GT;PɉVtĔ`<:r^_e#º*}}\Y=K_i'Te/_VǏ)f8F|pSeV46>s߈f7m|u4Pz_^YJ.ChnT"`p>?pndfwEh ـ 0Y}zwGЄ*(1{ΩL ~$ KvvNE?[d\ (>|nd/k}Po wӑ$)fǣFBL4WS;cܞSVf\h!+Tb.ͭn\ڒ.ܥ݉bΡB۰3 5QBxbZ" EQsmWkvFO|r&/ZRR[pP)'Pl~#Cw~(dBHM/*@ݶntŒ~  K}7@ IБijv0sK]c$Ʒz>рm0x};xosF qw]țےiJrjN+|pjXǶ]vu^%UƫC IDAT mf,jmٮkqE^`<# TҋCB]l@ NB* cskv͈YnWJ"5X71x]Ɯn<7Qn/ͅ2i%`P6zpNج[ڶᵋm#ߝ\V'Ā;ϼr T+zISs.,[rCQzqTRǧG/4ϓC2:iP vS=_+WuL1chFӇyΕn):P[վo1fS\sGNwҤ# {_Ob.]ۈ k]WzfN[ӣw+22%LJw?]&akcB<c <~Kwa|L{ehPi!.;yce".<+zHܞZ?v>j=T+N8ϱ߱rU-/_Fh$tJ}3d9$dZT5Pڐ"JF |MEu/IfQ(m[2(]$S ڧD=m TH]fzL8qͽo!kfaaZsՑg:oVmn1WS:u '2P^*hh'䡔Ņ$ܑeVv|#SJ8awrv L#3(1d9INuD_*e#&nfz Wh>Ѻ Oz>īnr'v8Q7xMܼFTnq0*&RD&chbDrӨ\Tb252삞[GB:~ H&|~5xIFm 8IZx}HjFҴ'n2F0w ]B9r3m&pd.N\O8;Gc9ܩ o߯г{#vt?@k^۽À) -l4(o?dA_Ġs"~ I@ D&F~kURc?RP7+y(V#D DR\UxKT,98я̼P)5 ncOB$フ~D9x[!㠎>>`RMշ ]mR\\̤dJ9@骦tuKۏJYTϤ;bHMO_` w|աX4XtI(T= /]}+{NV2 ֘q'y!hTv~Uxb+u՜@Hr i6_2jN73uT-D")U '?1`a- S7, 1P@ PD\M"1 V*61.iҭ.Τs@ 8agzh}["Qʔa"TN$s-"Ž( {0<{V ^ 1$Dk5+HD\/T5G(ce< i%vpDB| ]=qW~-'VrUXQ3XCᯭ7зWr,,#os=4o4}Oɡq#LH 7H:}XIl碭ͭAΘc&.;M.rC{zm31jkK%E}}nj$h DWM衢%6/D.S%24\RREFB(9Ð z)-8E܁}ԧsSK$d s@EuhSzn|#W~ D&;V|BXuʔIM_1.* HJ꺇ƝH8p-MN|bi&S ( B\C#Ji ?bK7{?d@ `Y Gjf[j;v$e ^sCrPv`Pi2N$9r{fvze홡A@Fli{D>Ei #Ց{ZW\2*?8GU^X+oX#nGw@D^iQB@Gҭ^8`$`\1C|m0XͶз=(U[=p}A VF/u߾":ms*^Ьxqn FG㡧]yyb:Ӎu.㞱WڠBLpQ1TgG[pb3gt?2ڣՐSAB=iYl]bg 'i2W` _ j ݟzg\=鋽!QJ|'K;SL o\/ڽigʔ GRjq ,Ec?Hn'^לB9@κ &fR"Lm*5vk3]woTt@˃7H8pwa{"H*2'S ('/ǶId-E+YWJNa|pt㕵&sbG9Eֳ!sPR;rk;u a3~%_xOmP{/N)902߼@L+:>c'Kˉ S5,S"˥P"6$o`Jf !G  C_ra~;YsIw @JنOO2()i18`1 icߙ]8Eq ."o<| u;-=+=c |pm Ejq]7T7~"cdކ |Rkx1Œއ;bmdI4w"K,' y"'Qשd{W֬Ð*wmgU_u) ȌPYĐdc1$ތ+]QzehcPčL+) -ZFNt>5])eZDḱWKϸDRmv]to0)L}CfhmhX$7DLb3+^޷K*SϥnP'ogpM壟9Z* +a8+/nmQOd`75-Jq@ YfRg4i-7]d/S.SS6>v/}3]-:'J, ID%L󦕞3ߎw2Lj-zޗ L8ru2b%;beV'tךɁv8d=P&vk_bwH O~BnYݷo,^}}bb2ЀէiG!bg[l_W@8O^j /]ljͥ,Ha"4p<<>OƉu~ղ9Ӷ' &*diť,e$EG,ĚUpVν BrxKЃE4 p]V| O@>#pwꁼ@"d"Ӛk1%Q#`VJծgy@) }enW~k{̓|UHP ]&еP:J0 #zDwy gOYd'O T8zg28_Xt.`TD=4ꨐ;fRgTWǹHGuğuТ5(z>?5M/1H#)fVN|Awǿ<9+oU'uQPȧ?34- Fe6fj{.!Xjc?^ywg3y'MO2E@ϤNGA+wlӕ(ƀP[7=25Nei3*цԸqbQYH#A5cixo9Jzti_KVڎ-Q\n-oUXT+?hNTz'd@51;OΉc`Պu77ZCbςrU#JR5|'$zxkuiQ@YTH xDռD7 49@ԁ2 #*۪nIB h<4pÓ\dƚ3%&jZ&oh@B7okjV"0D]i.z剙3 WvRU\{$69= L9ˌqa bls} Ǥ] sۨ^,!\i{f{oxϱv—wZoR P%1H…-(gY!\x n~t us^}g5@P1,~{9׈!zhn䕁1)#bZ w8|jΪgPRR3Y5+2P?v缻>**%}4܏짂<աb|s7er׀">c5ckí5xXO٩fc=m[J16lϐԸX7UxA@PS pX'V8TRWۤaؖ;[p`[ҌEb mƪ9 gQ: C u&bzwVUQïP T)ɭ5&]&<= /r'~ )^hZx[ XQskY1!7@lg [f))Z;oO2Tb0쑦ʩӵ ]U23"ϑ]T~\-X6=n=ߜ$ ;T H]=#]+0흃p@Ȓ E(d}mع[!VdiO_뀲T;paHA?墚I~{*\HNf\,9. n6[FctE bSk3vm9၊A$IDoD;HȄ~cmxX2-X <$@b,\M2D_܂K8X^OV^,(ɤF"o]pqͭpi=Xౌ>8\(JL޳-7-D."GP YI$ yQ:K%RH!)B*2@`՘QPZMKpHtd>E drh#!(o?2"2 mNM薃  kk_X+yC;NԘ9VNq~<]9iSQ L G/[G롙=QӹJ`@;z|_NCAH<$SS\Zc#_!e'>?0UMC/F6WOHC%v:kLT%Kތ XPkD|,Q~eF$@u&Szg_~{P>SX³y `>Qll똼Rx0!['ևc[;%Lैs5`8BB9b3PP^ p.bNqWфVZJ_%Bg~ 5RSWdg1ҒXP2Idi9 ppd6 0ƞCH x᪐[_R0'I$eij{2^EZ8gU*8en'X] wn|I,tݴxXJ/=Sw ([TjSۋ~xek;¹d_Hն(ðH GW/\nRz> ULf@Hxar #dZf  !6,iI2ILjza%bzKrB9]:Tz C yN7o]87#W@Fq<3xV2nSuPyP;4B[Z~ #JL THm88'\mB]@&VK/f'i ŝ. cĊ6g{]F %Nz(qb*gΧ S*-r.U藭H~ՓKDRޅ/3rˣ MpKU&E7 IDATwHacp?o|a_պq$ED*`uX) { yDG Yꚽߝl&BC|*ZIjX B(X0gօ[?zhs^nC"B.d= tsq׹+ EA%x~jcd@ҷ;iGԯB(T 0"$dvm4ye]늘nw:[pũ}zF5YRGЫ)1xS]soBԂd$ՇBfSYP]"R@D˩se!fJ;ёظ$e2.1]N׵;HJq͎U>s9yVp 1qo³Hݮf3kw.hL;T_U#=>^nJʹ^) 0^y J屮j .ҮEfrۗ3'%3g{ph/bmn D!kwho?߷H~ӧ.aܗÕ{H -D 1x8f|륋c_B%t%7ᱯ-53.l١T^ #)HnpܹC(-um90cmt\yL%3z1 E$w7=Gɵ꘼ۇs!(R:聲O (6.4ЍK71=JP!4$̾kCЭxl1Ӱ|[gShVzh 8T(p̧[hRyoc(N8b.;Ky)6\ PDDxB"'R$AG ;5/깹г*$P5rk>&@nGZxgb}OS@_JH#l gh\uc$&PA^[k5ǵb!+4&]L5Mm-<̈́*=w}:뮟N9f鞥p)3"'6?n%y輞C_.D F}"="^=`B]n]P{ſu73<דkR:ݼv=dh e)⽼ɍL3.v#kkq.s3s挺v-ɖm"%W;ZP^#2 *ʚyKVċ^FƋ{w{9]@s(1&swlS V qtb˅#; %Ӱe9@lmlUq(Hp~];A:4fw7%kIɭU;P- f#3=J6e PMUaSU3YZiX\si>r9M9j [Ö5Kc5CN1ME'*2t?%EK^jnWdIrlۃ'Vl  .' `e8nZo՘EC DR m/ `BhԮ.܎97,wilu)nW(*gvU2;6<sss12Zx ݣ߷`Ӹޯ/KO>JVRk+Sۯ|oDdder /0BdHb+ PG H/h JŸrl#kI}еLRFP RFG; m+Yz=Zc:@r:E-ښTVP*~樊6Z%~GKy|ٲCdqoV:}xnڪ61P*jm)(CjDՅ ݴӹW(}_T>Y|ҡV=(- @P,2ĊqKo1+^׺+t_UE@%:J%h`Kb۫uvqn: Վ>ulv$`۲m(ᑕA.ϱtGLH%ڛ1 3R7V04U*0t9-|g$ joo?\r4q1~iRlķUs'l ]M .*$J cP`iDrPOn9 bRA2mg&"!#F3[Q%lq b: GE7 d|ͦ=pDFLLrd̜?1iia-3_3Y(}#g%b.o|Sޒ ϗz? &Ziq˱r^jouO+*py[M-~m޸:9 CկjZХoƎHl;^l{kHB7jm-*`j1ɖyq>Rƅ섪'/i՗=z+R.]AI`(yuR*R+(zBmCegK.w^:9֡sJ!HjSvw=r_fɃnnu=CcB%D[U{Osq%L+&sb_JwF<7x66KԘVvoN^U~rKVݺ6V&e>rW/?o~z@(2国#[%\ 2Bْ9_Bݽ1v"ٺ`@T#}/a{ QV8 *؂'"ʾuddkHWdF$S6W\/3Re"%krIj>4#]h#K]DTC7~n1fn*pSHp>!)iYhT UI,,zp!$^81ȼ ={'Z;(8ibK7%#b|nS_VNK◘m2)1Fb.S0[(hz JZ.Pd앩۷9ݕ!$i0 XMܻ+*f5_mM`msۜ(ᵉZg^]h?"E)^Cߕ\Ibմbʄ\0Ő# &k1̐''naoA";ߐ]C0,#̱LGed+l/taڥAS f|5_f_fyg?rIb6"O!#mu-<۱ܛ)h@ KՊ+Ў2\Q>mLhNʅPJǁR֡`z--'/[B%םUf6 ΁$Fu&'L[V n<_;ɂO7־ `{S?D}?[.l^l mL!uwVol v#9#Kƛ'S2 : J2+7 AdI s1 0/`g pU C@ H]ƌ"geYY. n5SǘcS˨_GR:pC[QFhzپmj^9v_L6ֻL04 `&j%5Ygm)[tCom~wkf0MjdJ F5}6=KP&#p;5m_x]l{~w]B=Z;pP\aLq*RcS 5>=$"$1jb@m=oy7_ؒS%H{9PW"o.!J> y˽T8sl %y$S%hnJS$?@dmh~B^guyə-W.xʼ8p_XY:(}ͧ% P&vןCbBN !QK mrOԖA ::< HP3?9$]0H0 م䱛Gg,E/=5ʫplobU2?.R^A5B>W`8rpD&Syqb$A>G3A}iH/R\w<q,!t}n"ݷ-ՙ(o*#'JkV# 黯gѭm Lm}pVWԼ)Ps\pa` AMOvn 8Tߪ¶5yk.`.<0|8GvwN̄. %G< _::le՜" P/1apL;{ep89„,M IVV0iUbdhO']}wJVAW+j-j#W8j~U|sdTH$` *LXd̓b:ΊdД[^UF8g{Å[Qd)g Qsnr-u''Eo)8Ds$)]ٓ6IXDãx)+g#$-t4}}t1 6¾O}xYjl?/ےjN?M*\_nkm|2n\aOIOWdlHFǐ6i'9%"S{7JLA;$ @<9 dH>=." +J  :;H٣=B (вlI^=_ y>f.VkBiٓźTLo8nj dj>k<{J2l&E^9rReO&QA@p̵X蕟 6؁jn0&ʗoֶysпL7xU&DŽKL#e$$;5/m_1Ժc4|^kVT[It0^r(-pk vɅٙdŠ$5=>-Ks$&mɫi~+łiXABt@e͊/ {(_ 'j;L}}W߹C%M? \^2UM%|t%˔ VTO&- 483k)R$D\ɐ$Mea'Țѳ3wu9: ba'=IwG?L ؉' JgL@mpдxc|:y}1ݫjt'B|>Lã=:K~ىPrߒYPY߅ջ9ʟCs:߮j5|BDgB@+DHk&B(eBC{H(vzXD(hdRoFiKp+SMT*[`<]'JBmQlY꼳a\C@LU_ez^d́b.TC]Afۀ.a_<((VRqi.Q`kk泾~َ| ntݹnwAW}2,J2Nj-5<ܰj&()oWPָ@Hȓ7%F d %HIhž+:s A6j'>43.^=!Uϔq5sǮY9VzV*N)z~Ǘ*1隆nFBnYV_<j>y|6= /0-Y[@9Γrw{  #5#gfcwÈ܊x%;$9vng} Ɠ!X t ^(U2 2VYޑLLO6=,I.gcJ&0"ޣäҳ~Oz⭑S/Hn_IZj|T­"NrWxWeF$@`'/IxQ!0dg(GVp1!J3 Cm V٠I 'iǓdf* 7v:L_LZϡrKUZ{?+@Mm4I;zmM*ySu)3\ ~9nm7lsYj,Ʈn-G*%0 !:\KwfMWp/)x \TB٩A ^LZ v~lg*A*eHB< Zb.feSj^~PB\hM%zv?e)D^% e5 P;{ˋ@@̶mI.%4pY){Xhy=ͧEkЅn  IDATK-xKBpHSj|Wh>%"|`#x9\D;D'jI$9 9 {V5rd"Hn9?Ąmi<J&g&J&&&FǷQ˥[L=wK|~ގyyi*髙y]W ,gHvD,{ɶ|gu'C/!\OyD@Tf,n)BBzR?d@Yt/$;jIi$Q @.(YB\2S;&Dg/-̓$Ө_rl[`, e{ƕMmMn"IssUocnD ۓ:0((fV |]S-ϵ}K S;&ڿ>Ji^lSPүL^j$0Ǻ zb:wLK-yF>wHN9: UTm` fW~j Yi̗^.yC+qQ99Bˊ`+;gN:–V1@Axũ]7 {REK2-p$yvCEXܽ7 aZc (-Z!{6iTepヲ/L ɍk@X3&?{Qn9ųpi?[{_d:[=tv*fMf@Lq?=r{ܰ9پ6{ybmϞߞ|xl@%UhXi`7UO $DJpZf_X{6/Hyv5$Xit-p7ݯ&xydNxP@L4K *E\˭h7ZXN۪/kx#j>Qt9&eƜ05 |Rך& ra*Y˨"XKw!|`#lXO}Λ2ʶ D\#ݮ|X]-n!,Pk-7,7ߑĕ7ߙ#3\_+̾R[)ץyos)Ls4)fƒ_W G]dǣtt R|Ɏ;ߗBi'#ن2+ajOYy[6u_ŷ;v=vcz-?4\ (H2us|l׽mg%*HȲ` a ,@ssUf D7{1m[/IQr^Ju JEkJ0^ԁ-hޗ ~?khN=ɔlZ De/y ŗΖM>ON^R هW7LۗLՓy[ݿ>^3PV(f%a󼥎jK{%oqa!57[෴o[ͫYp.'@8Q̏l Q./[e=G;vr\䠓F8+:%@T:(GػE!=P2 q]Tcr!V}Lɱ;\|Yφ=P +k Hx1M~{Ǚ(DRK[[.Lfꂉh7ql)pKg}#^wj[sɚ Dͽ`VZ` ~t׍Vvd_~P#X2mOdxWKv2C  cv~k%) 4Vt͵U\R,"AM육F*b%/AC@R r `y@1ekJ.X2 mEǼ4M\'5j9 #ܴ+BiT&%0˙*}r ] d!sDm=^b03{|Cpj3;uzt\~#[V?plicdB2,ZĎNĚX\Zhk=r9,KXuT=4o^6xp.̙\ M%I學%QIԩ۞4QrQt X@r{ދ,T <|J}nTD qOIOLcҏ8LL\+zrبR D*D35b GPbP~ife]/=*/u7vFONNO䒞 ko8q}P6ʼnGl:`낏 ]ez$4=cEθ`k_n EDǟʄ5ߓ=. MNtBVvq89++'{_}N+Ƿ<;s\2*jG.\ѽ-AHrAO> (ZHْ*;HG:/ʯ\-US9pBGGߨ<4,K̲W@t!-n9;0k{88di\M߰kKrt~ɫ9_أy3~[,"5s`tnq$<y46$p[ɺR{~2!F FOl{x?nk3ifr[6 nzy5Ө^ALשfDVQ0|^e?Cf #p (-4Y3 8r-xv1'@RbBaK]_eGHVbJ\]=kdr&˃1jNq^V?R% 9^}6~U@)7 vWikPkB?Mgl&%g䭳_saT țPϟwb)S7Wl̋cQFxyp< #d*zVf~pl R{/ŗ_5lK>2-OiS<Z=O(,zeuz]玣Sq]H FE F۩ z#_ [N^%]MD xđZ#'mYV'sdjwZ;'n/PbϏ3$|٘o8v/7~3C8#KS^ !(:Į#OY<"LS唅%NNHf9ơ1wrjD(r}RbmJFl|\q!<dUA57Io7ReSuiْo9G 4R5XLL}#V[~4[GY1bG\Z@y7_9LaPqu0_n؟+%AJ~=TI̱>@LhܶT-df;wRLj>^60 ǕBA9E;㣻m8wYL)a؊b"T2K¿miey<(_,oƓ5/aNdwG+i!RmdOW +kӣM#ӗMֱ+[nh=p'|̓/P1J+qB65E+Z6 JTȇ}~߳[[?ƃ;>42M%\nݯ| -'yŶkggQM_Mn_';2JʣMíub'#꓎XRn|GX8H6[y@Q>tJ6 e)! "F+eH4r+p`^=?- L8HD)􈰄0S&6u1y{EEBqP' I٩R* l4jzʆ sQ_IBj9,h}/+S`7Ⱦ8rM`tok7?1Ѻ7hu rEyQ2GgdqLuU3%`T#un㮱x5\p6zpGi[$[_g^="2WFzD{`C+f-zUEBґT,%eB 3BfUNнa8[0[rkggm0a)h@ҵ#j.[aQ8F[VOZH/n]Jo M,zh"W2#dƠ: ˑd+hy3%=~S ;τòGr229m44}H|Ovh L (*eyKmC(\ɾ=LA 7 fK[ewa ɘȎsOO9o1.la5Eë-uF|i)&Xs3׮% &9J>7g%8"1ߋxl/, lmL},}9iz  u1"q;[a-!{ThXke-[)!\8puȵA0^f6 O)-m &|~@iGD5)JnUv4&\׵$!j-SoaҰ`s\Q_3}G;ךKM=I^*?WT@@ES Ux tylT=is&ve#}$WkOPtiP+#%iMJ!Hj"P/t@#}!bH6 K݁X`J!0qdz@3!aREZpÚz^X0!qͰW+srx/fvPv6<燲1"]t`>!q"N1r?'VיyGklx4xɺ$ pSp$'<mpAej}| o>a`.d[.,<7/?o=_q;jџMc'<6J&J8l62R>G~[_ĮkWD j$C؂l %'ﵜirC`iwELbQ}l ’o XX(],6<%NbǮ0Ad@[.>ZW ZbPHbW7bLؕl:yaJ-Zyvo nuF0 @f6W];M16k7G6%dM1?k @7!~EmY::s~7`V}5_n y gIv݊b`ɤH5d&^ߒ}3-/$J2K?w|kmBAS酖;| @p궇|X,\5<~(N(?uZ&4z[dnv]NUY*=:VdBTY^-zR!BtȳiȎ.ܱ5<<.):,l l=2 6 Qḭ);oyHӓ߅T#Y$[& <@D(8d SYjjj2 ߳?^x۩ SZ.md;Oų(G m'?{͟H3<Ǘb5";MS_?սޅO_x2:yۃ`d\0IDc~[?ZLz^22 H F›Mw^ζ BdSk]7F=9sW|XYpF{>~|PYsX/6fMܸݒaC)y.m|8s XfM>GdXD`&3A$Aٷ)v*p*}H*+k){􄖙U^@(H!)G=b- Pg3ns<4 z9P4Jm_!/-V?6b$rw"mC7Sm7lM+tb.+fLRyݗ+@+TThG˥zXY wɟO~I0\[^Ba# *Ev!#[0Eġ"V+<;n:\lwfOsϵ~嗙(=(6&'0׶!PiHkR䕳8Z:u֝BO(7JvaLK0mZFaB M\ZJ{—A,bYJicݟJ#, ]8ܿ(ȅGvH1b~/ƢH^4Ko)d~l8o=2sI ~n杯=0=-60DQy$!BXTf~ȳ /$u" # #b(?nlo˅};;DJo͂a `CV>nlTB\5+)>^DmO圗ޡʿEN ADDk)8)\ &r} )`ey6({iޥ7NiZ +-T첚_*Yei+F51~gȭ'V zSm=uMks)Hd#ṣr!Q2*,D j>Q\Fu(lDSG!f }hsHȘdWhk ,̮'Ə)P>6e2޴];ժj\w 9Ou?ȝ|/23$* y2t{O}%pJJjb[ky asmjKD΅0+r"˖◭lEzr uLBs ;>,u.zG':閉|9 ,ZH !KFۛ'Nf2` NDх1SKf)X@pioI;\zǾց J.We;,Vemmo9qU%.[L<@_X0"|st1A;w$"8繫JBSͦ\C ~|e;/XIH/DneiDdFV6TS?gN6NNxYn y H>'^S XEd>;죷}zil[b;G>{|W>j̆goɺvp`"N@&ղ&/eݧ-l=T_{FßPZ%_M0#6,4Yy&^RK2l,Ypɽ" TjuY~۹ *5*]9lz(2ld*pĕY-h>X?k廡4Lo\VB Z謌(|ϖcg&!~0BCSYc)u; z7;9lJdTߚPn2}/`,D1_N QcMuo| x'c}oRws`_긐oYXn.٫EY {DV @@qP} S[Xa@0N!g/J O]ʆ+Yˆ^`N7y_2˻UKtŻzgЇ$$Ŗ}u=9^Lb٦hi蒌<,mV١'̶]AbGadѶK`ר2b)/cX,z=h!  t*ǚ.7ޱkch6S)L6iG5L,0t JRp K% D1Yq50kgU[ye5Ww\+Y$ GLɓ 5cX}@*" +lQЏ5o>J RhWm^TPᄄ۷ѯx_vj{6m~UlWՐ]_nOdR4,D8uuKQn(c/u`?:ཛ 69Y Ccl*MջOn'qǯJI $Bv.!&,ʓgW|v۶`Pǝù-oP#CiOHZ-{c{e+b[! ]Ȫ*b?v  1ɍf`HRŴc8[DSnPXH(p0۱aItd5*:9Q_ibeݪߧdCsSH6=]@ChW-{i8ڴ7DnWp[l0F|(H.Gv&V9$[s5)@0`zC8~t.!0B|%ZU=:hk 1mo-Z8vRW²1L^vsid 0nrP@*%!p%jf*i92_Zf+˟lϥe=g rydNGR0,d֯)(hKd3GsY"ɹ\ljcj 4f}+tiۢT#!o4)gPQԹ*, hri@JV^5em:Sb;`Fv=MV[ 2Q#pݥ]3ӓMfEc͖$9_]Cv~[>yեLtՔE0,6N <ڈOtxe'?ksgsl\5˝ͫ^G_|T` !VfSu5jN$2TDܕdm?~yg/tuw˛_;dLfpፓ}[^<&htDSh#ƚ[2vi7C×F`vRS)E@D@PD]H6+갈3[`B= hNپ@\UH%(Sgd,n&?`Qe(H$b2g.]bUe2 %8g;W֍\p@ +Z@[ >b&g{z]0j4A>7 n}n o2v]r2Υ]&us}v354։_qrxK nyk]dmEF2Byw[4Yus^l #` Ƈ{~@j|ؗE"E^Dњu ZTʧ#ٲ]8H`юz߀Y.5>ٶ՛q %p˟%?R4eO~q`*p"# cArA$&L,o?+&24\^ 9 rI&OFz.\Z9ْDH`x<C+P 4By.$f'w/OFk./yΊ[*4"XI^6=_ "]#=+5@$RAAI?=Xe3'}/=Px::SILo&}9qB:ٯ=>^}dߙ{ϝ=c ` ,! nρ;6l|/T$S~iLGXcsw4fp` 2L?oORmGK'uvk/`+HdfO!(x_ͽ>LS5@&iAE@j}")lB(Bn$ Mf- O/z}s`b FNM-oj2YBR { Wl=d&Or֓ ZU`vtT1fsW&K$Zk(U\Њ/ 4UKfwUd|q[L;Y( '?4-(E6 |=E+[ݗ *Ff%gk͒u}_NDti'OTUK(:'4^]?ԘP.yL$KZ7Te|hif-<~D6QӋyoD&C.>w^'ەKϵ,vgO:u*䶒Mr9 QpJ' 3tO\WdI!%afEo-qLQA L1 B-'Wnj r _=>,IG*fSɺKCSK!w.%6d*<D/bOB*)rdK/;19pއ6<0#q֪SOo5rRIXj^ɕ$1铹dj[5v~p(45 mtPH̭۾G>W&ү6}$W=9ȹM_o7 )9 Ih\Oz]Oqȩ ׋k) m HmB;)Qf% $"" s)`ZE@r5 fF0UJvٺh$e5ʸNY#M-m[jG͵mFlUD΅NC!_@d(Nu?1R ( LfSK[H o|vx;uo UN@ToA_+<klQR 3o8Ȁ0N@l@chzAs7+\mt<gt`݋ SȱwVb"K國=S7Y(yfeFbu삮]jb!".~ ͳ.5nJ^f+P! l4D"I,Y */H jz7zexhOMiRu`X(@ H̢t3D D̖wW 6[߷[978  "bL;ѡ'm7%K/쪎+zv܈;4;@@sS-z&\ZZwuR MRl{WLwV_75)({(}Oo۰ý @'G=̃Kmtj&1tkqufr $|>~uDL`gKU~XS:$0œd/zp'r*{V"4%@ K7/\W¡C{?%gjsvR?l'8|xk`CAVgV,7 'هF•W'0׾AV`=\ƝN!k/ Sp\w+`鸹U-~xq/s X|ޑ8oSMvT;V >S_OlȜL>5RH(3tt}?,yLƎR:qPBc@2s*븲]C4`\@j 8z|qk?.@]\ʖу*b| +_hd=_qG%]mlRʞș8|C˥ӥc&uODJ&z [Yi e@%e rdeQ̒_ȲqٹANĕx͒Z䬯ꢇ^|;h &e#h'~ot඿5'H͊8Q!%'s`09K`{02`h,>w V8>8eRm<Ǘ=Mï}C-福gǐN<ֵĴezk&)4gMMWOc`*% QͲ P"ɰ/`ZTs=Nܻ쐔7*P@!^PEY^SD@)MMS/gqfLy뇺_~G \XMg]$=8}~]Y=j黼fnĭ%Szå|/6xQ62$g~/&^|6e`}gݲqیl+ẏ^lv M ]3;޷86": ڂ43K(nk''&]9 l压C]?~~Ʒvw6W=D΀3KaVrVs)8k_ܙl6xlT+Tsܽ:I V?2%Y[vVc{ϯX{ FmkD V ɭ1={IZ,o9dd@mR9"2%q az'~@r*ABEr;PQ9Wa@e0۾v쌬@ DS+:N\ WVkWf|"}o77)]ꖥ2vpߥ\WxhL +Z~`"hHՑgMS/KV8s<=q͏4O>],Lі(P8L^kˈE@ YORrxOܭv_}uAov:~ِU. ĝM7-8vd= HFSUoh8kdWh:$+̶s.7ejdU+^ 3Ӧ-% @Q)j(ӝuH:0:JkBeUg .DYs81R=#>#]p[L{j;!d]C #Xsp 8ٺŗ$p@C<",H\?3}gh{6-S%P¨+O.m1 4w1k#ÀqN02>ohŠ#ӓM\:1p!Y]s³?>vr/TSCGggNnO+@ܗNHw77g|]vF"b/Mw΍5_N`7G/oP/ޞ M ]'ۦDQ,V'^L!sT>KqQ~"3Tj&0)zp3}z t6] 6`O4o/iQ l!,WKL^j̸C>%o=wAҭtM]Ho'rݣ 2 3:n9|)|>Bw>r+qW!]'\p/= KFLnQ6H۪.!#&[o|uoFUC )D/ Z)ñ- ^tLl\UK躑S<՜ & zjsɒuE[}ə2vY?>N3MS]k[W$k9Sv E3۾ԧPWȪomw20/_}%)%[ɸ;Q3#r\C*5fD[tͭL;^qm{0  !m$}P;ֿ!y{vw'P0YD(7Y>};\ԿDuISLUua蜆@kPMut$RUg?;`@T8󛵑QѴʡ7Dh5^Ov/|};'S@=NܸţE4"!ɦTGk; |˪ vp&p{G}Tͤ['4Rl VCn2[piO\;@%.rY[;(8;p HC ਄qZzȝ8 8p~KxT۰8Cn S}uvRP x{lYQt>'^kn 4XzU$u$`3=WsM6Yq-uoN[sUKto&&ZV)]{zlN'} +θ`0.(0 ,O.:_Z}hY|OTJ0m䴐c(6 xA7X+l;v~dƥ/6OS8Cdo}??dnR#S檣\Y\'Ni @{~GRK uڧ`'3#GGK'B$BntybVR6JIXxAF450˸6a#F]U{Qe?B$ ;N6?)ڈ8F֮l?uٳHdKHymI.0n`t7H;5fGi_!Ko[n% f~M6ɸUt*yTy(䝝葶 wcHVgJ%#Y,X(7 g:te( OGE7L}O3{}rP~El['tiP@B9R3կy$C[(H4' 3xO7 _ #@i4mPĄB .~d@lIdW)Bfk.W5n#FN-UF'Й{~۝`-o3EO՗j P6o4*j8) 9S򞚡ڷ+c# >eOpʇW ŗ> ύ(Z^m %IR:$IEO_zf;fT }㱍{XiY&: .YY 2ٷ[l p$B{nzgo,a=a˛{&5UAٌ[ѥhדЁ3 VGs!' =t/ ICu\Ap2M{Ҧf*T#,z@`,(vϙWAEXy6XYfLyTmSD}9wXCe70$'%ek#m] -mbO$Db"Kj; _T qr\I9r&;?-i080K+3ݿ&O^i1q77u;PXE"h&;6Etr'6 G>, l̵8fh7;F^*9 El$,'z`vZKf.VY_oE)T‡t $wyeυBN;/M"/;0!|?lcxjF[Ny./^$3K[,]MNjŅ:X-3ͮ>4mI%~+ bK1S?dFrHz/-nUEO1n"@(Z^29+ Pҕpǎt%[t=fFN-W=#@E+2xSá{ß-Xms Ϭ9''a3!d.w_ŧ~{ѮѺ]_N-CLVxȟYBds|vUECb(=R& DS3LuNԍFͦwÿyXɚH 8#b"ţy|Ԓ:4?._$qS[{A#2l nc.q(U%dQ\dSyagb Y3qNHC>dd~Z*f w -@#U, J{w<tVwfĕwh͍[\mep-$5h|v%fN}⸖$M-r")<NL0DL2 _9?൒ JύKSn|;vxWs!F:r`uinuP}DS,O*YE[*txoFc췲(U[8%+E ȁ\V*\F#cnsU T= Hzzy\'|l3da>/x 3qog}h 2eKKǹ+:_FVlrg׶?Xa$Nn/A䚥u~ 3*9mG\1 B}@Xn59ۭEtaxÎ6B N$ZUbd۞/Zs➯^9P<ۖ$7N]JT$M ``.=wyB"&umy_ ؊l3I7Կ> TyyBѨdX9#۷;Y:Sx۟H+<#|;^%6#ӛIqa0X_??򇃇s/5 8"g$$G'\ykg˵3Vw遲#Sf@doO$ZS)kTot2OhJz .#Κ͢6Lʸd;΀,@9?W+%ZIN|4RuAJ@RZɥ_*l8;~HJu4De,B`(RPKK"ŝmq˭ёsFo6¸]V\ :EJRRIFޗYYGѲ tmgIׂ\ҵMJ>+&rB(Kn_! WHzb%UU!){bi(."`;CD bY"R湫FvVz"ÖL8~6v\~0ݰɫM \GpA<_hI;Y\o_?{Z12\H@ن]sEYJdW]KHϴc}''ޫY䟯Aq!Aԓi>ˊs.\T_ίx7smmw#ۥ{/n5|**ؖlj=U9ED]x?iKLc͗N{Y O{Of{C_[hh6yn+㌸s5{rs`d烳iY3c#wdgzU7}lrMMf{M+H$!UU[qx #`i]܇(^`'1 /$aјF.U[Zf݂@DA ,|^f\pgK[JujhS`g>ԙNi%75b#Ĵdm;Lw`{m\uo+Q ^~h9 1 x#srԠwjU3rA89yK7==]Z 7 ƾe? Z:Z:.npϻ~|D׶/ř ʯ/ w}- u7mTYAglS5eo?"-x՛C(A+\+a(_ mUE܍5#/5`Aq@ pNkY8ג<mX2G^\EOM&XHP0۸e'[P&:a3.s4}Yk9ISVޫ25Ãcpa*02nB`c "8FvC@Y)< XqQ{G_4YVB5KBUKf|W77:[io~ʔ]C]3$j&>bWֲFpG [@qn#C]Ȍ;]}c R F;+sƨ dyOG}OHVe#RJF,Vt/;u򍉦F~]LC* ˻BZW%z., Hֱ#F:LbEўo漍i=EDT5wd !K;G>SҊLE5*" /Yzlb0:N~j<@cE9G 5#$=3ajXj$)'EQ$BvTʬI1rɺumCY# "dV*y>lHTpۂKM;FQ4sb!נR 1gҙU[{P TTPY0d_*2ܹE6뎿  g.eu{͗:f@H{έ~tW6@}>/#V22VinmPB"^3;kPޫKWg#Wzdݸ#CQrhhs2J9 Ýtze$Q~Lx^I`"r^31wX+7td?̦kWlIm{ck_Dly39OiYW,5dLcણ%'+bBr'w{@DⅤʤHB0T.|p+{Rjs,TT7B$@tě6ԏmK/mv IDATe<mԢ -( V9֙ 6 \2ʖ$9gGf3:-40Y[n&@7"JA՜vCy#',KJPM콹9x@L}­0X! Ғ.З9[FF˃]S7wz8 %q[2rr&c>w.8_(f BIWo\ \31$ZĈ!'9/,o6m}O<09{d[B8NGOؤye*8/d6}a~~U$_6H ɘ44zi7V%[ OG*o_R_@zoiG5#[ ~ӬXUJ 7T1&172]Btj1 ptv>hM,iX4.-)65]=*}ef9Squtz (Û0][=M&SJJpVcc5W/ udS jfE%[^h~O +Hzji/3V!LGed"'" |R?TJ^KO<WvI @<<{Ys$#ϙ4@G2-p(d=tl~`aKR"K:qR"]}9 D]L梪?8zmĤ' dzj\c[X!@4)T"XrY]jd"eA[d/8,v+o~ku[V>Do1"G\zVD/ʕ9/,KRIL4$*6޶E3֞hjN"،E]S EKKk<6WHpg/{ Z|q\}{y)>"qΎ&jjgap7 E:yGP؞_ͻ $i7+ $SOشk1N0%SzO]j\[G#S]-ۼuóJDg#S'VԵF })kѧَ1.} Aݠfo |@5w2/[Qjf8BЯl~FL6P4|nvS!HrXVOx<;?T~I9@tW**!@S (]U\5Hs]EΜ $'e*jjc/= 4M|Ť*/Lق,rJ 3]˛a̲Ӵ$Y+̴ z'C )?3sj33IG~k_ 5"/=e*cA7}t|C l9 ȚW&%VrA/)G8%ɇx̥Ƒ Dj;OMtze$X_?XqORc-}ߟfYhs{ +]𜼾hx t(Ribfxzv7bccgx%a"֚"7mb(9"_ܔHKiՖ?z+ ~@] EBN(3GMCj"c&cj]G3e-r64'7=7Y-p _Z'~(b`vOQKC3{GoNR FDG ɾMO;|* rHfc/ZR Ó t5;K^u%j`6$QUշwRYyړG{&z Cw}vx 'YсYx/93=ȀC~sߟ羗35?R 5}ý_)zt62JDv(kmyibҠOC!)S:sc XWx 蟅F.`rft|HPS9JC'OdIC4Pwgtnb!Fg\n@nRn,d&;7!{R[G׎LhdW&5bF}.T@OI#)͕+MsMMk-xY%Y@nFXlW^2˿N|s `v.7s`tC"; ["q!A;䫚;9Wsy;hxCYwQ]g1,4mWѰͅ"ި.SϧOp(zoF%Zl.'͉շ H%@H]-EWsmATáQzι [ ,0$`,j@)S|نw][q3{=VK>%Ց fuM%֗~kAxV7L1T _͡)Ѭ#VLfH++k2{mIPs?bY؆9Q8̜`"gh@Y!F$=ٗ{ ch9ry75l.`C)bnӞjs[ 2:rC߅Ӝ yώu3M_6RB`"q@bCe͕a\]B*kZa#SV[u*R7oVuG0ȀD2X1:hsG7f60wlSF552BQdvO7ԞٚDi{ %W[Н xi xKodp,LzhW4%_3 Jxʋi,QZ{_/ln'WU0X#ye~P{^:`90Շ Z_;BC^Tٌ/Ƕ ѹ&0f9F`H^K"Pj-`$72-VOOݱ H)kVV5%yNI+\i~|ͺgI]B4=x>X= %.ċM!$kx-ެ!Ax f -&f_weŷg_ 1ofX(g-Ͱjh,sJW\-s-%`rq=|-WN2Ud,Д,tGLI)8yLNj^W/.JFpmGT#\=Xj:mCİ(ϕ6t>l %C~럌Ud׎؜ J-Ο~&$vZQ$8RD2(@ƀUW %B_d9!3RW s[GC`  % ٌnBJNA#k7F&n:G|{R@ C [~xfs=|}::ܲ@8!uZ%R T4.>|{8v>|7ZpXjghAe'ToF9]QjIx`DTS5oW#5u[TKTRo:5fIrnh)y&>J,غyع00 DhOWPkǷFxq>ѱk\O_&2%(p ex7l-u*#D#osP@X=-X5q ]ߐlIL|CWn r=QΎEJʡUFͪ`7 km铵@h˸59/G.}"wG-MHdHzP QșG4a_=+j/*MqG#5]V 0h$2mTZlp@BmQ[yT̕j8Zw _>'*aԕl.T}[& ,h#1Fzg>[˪Q:D&izTw`Zi+16 k}Bc8:go12v"ès+imd1vi{|[Ua| =JMΪ8g59[h9+#)pY-rrnĕmB \e8d,F{Ap:q%9yIu[jHПhmH.48o-9O;p}~9ǟj;\p0/}ݖ)F;Gsǝd7m-iVEDQX,ȝL0噡/fFUMRN|k Y[-Q}eIO$`:/5˄F !ԑQQoS{KQ_5GGdAmQ |hrj]R@pmsۙJ p@in# :p@Jۂ P墤;|=򕫢4 Fw%O:^tY(9RM-3k-vPssvnh]@d~ƮqASS3 6͏ @(/o\4C,pezdnu'`!m Qe"`8rA72Z<%)I_T9Y5o<v+,ĕE௄ mNjzfE'8[u3wqw{rYzu>ZZ>ڝta2^jy6=_4KR[2TKu퉀+}?2X*pM|I¨6xqQ, T.^Zg ܡv2vk6vчrc|{a"P7vb9Bmy̖ڪkMzYwʌ88ו= yNCE&'y}3fy#%>8od8Gtz%!"!nK ^0/! Vcf*5JZeA^L0;4*+o Cд62ijW#D0UDz³%D*őz. R:՘3u_ZY5O&MTe/_:_G!1WTHw 7|i$ dYϽr__}KL0Jl yZ5'O.8oXLu#>54sG`sJk_VY:2j`4;hȐTÃr/EDKCkZ}ũw,^gZX4,E8 H;3-*6sr]nOZ"0EVz'^e(@|`c6EF cBҖDjo[@| v?ߕ}^եJz:Z#%B-7UI!eEdDj*=Oteo5Q.$BvA;Yђ_[R wXp^4Ft'Tv|o^B!8՘\0@`^ުt39j2qECb#'njͅk9}c}gםreg&w7Ň\]_z,3z/$R_y5s}oT9F'VW:lBp 7z$1;3=bf7K'F7{_9𺕘NY2W1ΰa&P[a\vIޘ{J\iXsN.h1>fv+/_$ThU[Hf#%x(Wl!J9?gՁa"Gt9EnL(LAl ) m>orc5+-B0E9r?sG!6MOMeynUfSO~g u;#>7wXΰ7En 'MI~b*A}`p醘(Fڝ%gՐ1`@QǞO|/`z@NybpgN8鍝\ա!oVL[7refZ76N/#s {,9z9΀X,v%Oz7L[4 0j(7/Ԇr7 ,4ج'>x0I'BsI+5g1|@CT{89Mj*#\@N[-}ۺĉqΠsK&#!C$\ͮIY z/2eviQP)ƞH$ MҠҼ($mz=ߨ9dSbJps|߻]*'yKsnTg*On?{]r3]_+e8ːG?14qց PIV;9pbЃIW 9یfn훚pFKݱC<7'`|~릾% 3-8=y}u`^CP!@]^_>ԚĶ/d_8;waVllȭja >Cf8nb)y2R DV@Dl!VzS=-=H-BZGȕzJ{f=<_vN]<,iMз2%Ia%pSJ'rB?!5P, \7m;O]or9^ ",>nW|{>O(,~Wa8nxy=΍ |FFx)H`\T呮KvA[u]?Akw\ dߗfuv:a t0(/bCbVΖ&G5>GlǾmK7$?HZ %Yi{+uV=U-V>ϔskᓇs-,alsi&"F+sfuoh״RH8/"Pb{β@\p 2`hRPw>D+Sc &j}s@9'.4'('1ĮxͮXuIU]k(v @B%넱#'qjuhEk{\߀ LA}x: 9@֊%ձ򉭘HKFlG7?[h苢N^m&qߊ b+=Q6rtԥ|w[}m#=ӓbƐZ$,j=K/ u)ct="@p1P&?I⑷px4sK?o=33f >A'H7wo'T%ZrmZsѣ>V(v.y4@ٜ)/z .* #"o4]EKsktDN&hJrfcGʔjs~}(:L,jU7;E>Us13 s睝>೻nmM3ͮܿfps"f.u l(D$|ۼ-h+9a r!E];I/l~zT0NzaҥޅWz1_)k>gdU 8L(̶x i23b=/ *IY~AyÉKF#琂VՑNg.w`tlU`5ʀ3BQf,uH%l$o\ |7^C7hj|6牔K`y۶cr\juv!TpD@b`fT._cg~oxRhm|N2+lɨtg3 <2b+`lצD昢ڇܥ"Ś?mf@eqxo=N}sT%pdW8fz3MM7`rmΣ{YlHNO.,Os{嫖V>}iæ{{k{fK_o=z^^hW2 ].:@(꫎G2weM-k<~H0Ml0@[Щ{{i_G*#GRf]$zo- F]F@R6 S uGv[=+I%Ȗܭԯ,Bl1MIn1+'$xin}=Gf͙qb9>I"m[O4q`–4>yedX!# ]FR,iك ?'"o G3%:CyGDShG /wshsBK!IO9\-1oU5/Db K Oo StA lj|H5=wxp굑/5=}WV>ם>mm.Jv;U8EIsho. U tM+E 殫91Ff0Z3$6䴠Ģ@aaV_L02u0BRKLlֆ<ΰ-Đ8,GK%ҰpqY):cDn`OAD ` ҼυU%v3Gױ$+kC, )N<ĐM̽Abɾ]!m 9pxOտԟ T$ B[!f?~Lq,>Xa7?1c1H>>; T ̛ͮީ}~驗9n d yUBmz'7?덟ڌ@'&Q4JEriQF4mC{wYae1rH@zoLF`ԏ_^ۑAn8+񚍔%PDQw~p~ЗkJ.-aַnۏY6!E~5t?`6Z}ibRP}zu}>2MC_p߬j˯tP",Fq=?CpAPnV|7ڽPZ^O42lsmC 5| 4'89ڼ-p6Ӕlb.]yӡP锿6*vۯ`Jx0g ͦhTzX4jkQ2(gW==wu f5׷_J[NtUJ[z.|AVLJUu~rOh"Ns(PgHA-d-_h&BJ60 (n -E!4qóI6ľL 80GsMpC|uy+jp[vz5|9xd$ l /j[۔H~t| xOݿbz˚K-fRO8Krz`5-ە?ʐV:qw:A@_"7߳e Om)(fDY/Ciʉۿ⻄ZRT-^T2 68}+siO  Ts'}~}V"M[;\Hgv Ș5W;i ݽ3VKy݄NC`]Ծx7M츯(uGQs;ݝ=1u L-=󧽍zN62&͡#=sЉx%lj 68Vj`]D0׾ŀfw_-ZA59m׭G>웚0Cy5YkrZ wpo|ζb۷\V:o[ͧݏH߸}~6=&g[("uRzvM<ĨVZwڧ-S 4PR# zZK m1B[5)"Rڂ 2 [뜥#dȈL("Љ1=AδxÜ޴uIA7ZI) JrO- +^)K\P3%[k{1yMv h IDAT2Ǽ>Ѩۋ8uְϤe資335/snB9՚g\:qB1dbBʲ>DZ9 T/&m^~l|R䞺h@h ۖb/BRrcßRVu [ĀJtِu-ڹC ;䊝]I؉ P\sg|zؔPZ( g-b٢i_.o u^0̀2 9i@+L@%!F862_3̀b=T(G odu=!hv2o7Hкme2Yb B CB9)!vEs/V!cD6j]p$CF_;+6mcݿ_;uJDMM>[?>,af J},Jvg! #0R#s)U_\5+^7hՠFAj|yl]QCʉ=PY舷 xKVN-_!k`eB`'xlw\Eq-\Lpy )|IN@ ӫWKE٩j0DDͿ߿TXl/?4zR(WZy" ]xd>/X&aTs̴`Y 1Q7ér_ջ{&ngWiG ȀO,9[uRY+%ߗ"vJo5IlrCwӞj)8D7+5_Sz˭@f8frn 5E2#8ݧbZ>b^G=j{# 'gTM<1*f`x {_=5m؆>{^2 C0h|4T>,gk%ڲ: "-iz=͔] F͐’^"^&t9%i\$DToLҪ3 Р<+iy;_ Y8\WS p.lF}i fLn~JoBPFWT “)&]r>AM* mƿ3 vâz[$Ci!2 yE)@鏞qd&-6wjcm(ە@q^ ghP<.jxWb :vvu/!U%.tlѿWk lU oO%2G8_>exᜣI>0;&:^zq"0BMo08sZgu>>pq_/8/6C匤Wu47gJ8?ٖ5tE ZeSp![&i0uR{ybVf3kG3ٔ#pKN޿yg[Zc\?0eMb6-Pr}gȩ)6@dJs+ݓ(W7R`.a)!w%JMj@@ nGfc6p9J gSFf9dZna$||z%@kkXt:2WŦ2h-H[ek3 uvOe_\Z7D}^>q1˙nXΪ?>1Q-3q>BA,TzRWgNy\ 3πCBhs3ݏ\t{:k[sHHQp(缵9Sgl=PS {fqvy bٔ^{`94MwZg> M ʢf.=mӳksv{Z9fllvLuuUWuuEOkꥌ!} Hf{2#=Chl!r<S =!*M@b`:bEdz\[N&VR.50g_Lc-vMqt#yDzt)`J;[0F 0JL猔Y=)FQP*Ub2k) &ODU[&+ոfaGԵOQd6xS93{gy7*,t^xo0e1o9bF!F?tj~*i NaC̴RpiCl7t{xx,ѿ~zV:ᄆ`6))NV[}<ލ!'/qIRӈZCu-+m5Wn1 " V0 RI&Co, (%?(\&dAm_2C`E k_G;RUd@ۨ` 7 o/p*vsk+ f{$-L}ʛELi``E˸VUz\ U0$ZOun@AJ܌uj1Z롊2*…35OnYj/4ɝ ֦!cv|T󦆉iA-U'G}u{1$>?xl:樚j&o <6zk0ut1S`m`88sT 佟d3QKc@M\Fpa8LKHs {4qGl9}S10GcB}VN 1C  @P֨3T{_.p25܂k-lڟ*Nf3T70]JZ^je:˜`GёhzV$-2+iRD)BQ,wٱNHzUc`WyO!CA !V`ClJJ5!iEV}P-ʣ 'Zضί{s+G.½wu`jIе*gN5bOGv??aBkJuݿh/fU"^[AkLg1{߫6g]<L$}w<6\- b8O/>u.`jɊ1{{cr1|gv~>|gݍs?<~꧟HBłk?vSuڢ1&o'߹()@#^/5yPpx c3( o3EgB)JF+/ae ^gd" HDQy}ӏf#?</ |io_/Fz٘O1  0yz߲|›jppT{s0d+kC<\ԑRGp'9bF gW1#){oyUՄ:G Ҋ&ёf qDuVk҂^^H#к-dgDwtMf"ZdRHore5ms[M BGtZ yn+!!\ ]ɜP8$(|H"L:#6&Q18 3T졶M|0jی9& S8mWG[c/6ԃ:8cP-K-D?%LW`Qu&cW}Oy3+3p1k^wЦ.UAD`3GxP„T{J2]ldR w7 C@p:++NZ3Ӧf"ۈQ+M+rTt6-0wo_ljaeQZ`FJnvpHTfRޱ/ ̜gWFvֱt#'ڗkvİiE ojK=;CkּewfCQ"i!JRrH $g&eWT嶩tssr$>~j-DRR_u 4|iSfw;녿9*uCxB-ÑŔ{nn>>\΋ʮ6!`EʇSΦC FMѭ:C!9ғk({#'*tҝICsz8z,':=,&ppSXkPRpN~û>w+6]:I3.I ;ʈ o<_qR)0/کOJFxXFtQ'/5X1j|"'2ἄ:stkE=mo%9V_4{MJz!#,-[0d, FCa旻 'rNPkIj6մ-/>feȂN9l.[gHTeL Tǃ 4@Ǚ#F0g-d^!m IZ]EAӛ,bP !dJS|j_g"[K 09qt|D؇1fVd-+@eA)*휱*o ZD@s"L yClʄ&'+^O"0EnGy 1P<-gyO4ɛYL2 @m{;hq B 菶o:eΖGuIK2j3&fsbՔ̪F@CnZM.w^n?,Gy F<5V$5؃km+ͯ35[vr!!+یFVW^~kjp8墽`m<06"X}?;Ɵo\KP|lSQAO=6xАW_]J_XIQCsT[}M]oz E}:UuZ\´Ag'GlxkF=p> Tc KUgĺ\Qg]Svo(6jٖj#"<%j\R5XX^HW51TJK\b-`6xIyRɢ"RaR)uxK PQJ?e|_BZ4708ي,W$vh9}ӧۆc" tfv#T #/xC:|K8w%߬ՁaIZ]oX{GJ +:„w7KήPb8u[娊JjoCYܟZJ #`YdƂc]KZj>׼TO.eky nW8f.;}o>>1BK|j)r]Pn=ɩ͋11u+X\ܦh]G3W9ݡW;{-'6uL= g~ޝrgݙwfƕ_OzxĄȂq(Y&Tw@TG>4]дmC qَBr;ZSsnq6bkӻ,t|?SM@,y*wx}m>e] esk'CL !6ȡ`/=X %l<_ h^;OgQ}zf֭zʬ']#L TMD )xv׬Fyx7K!WwږZ)A1(R@r`apՐ\r'z\r̗J\_J`lݞTZ텼($FצgPLR)Df2|Go>hx |w}Pc*RAjg>c^a&fvjtȿfL2ZG˜}?hc B0tR) $kTB76^_B 1sɁU6wt1r{xB9)Obzŏ-c]݅7+_oa8Pq1X1ĭru*{U%Tw(p.&̌[S,jܥ@KCo6'#F9q1;Eި^0^Zz~r _.**6HslJ;o~{<58SћG_Xl]W{g^.ͫ i7//^オ^i ៾ܯ:jrخ?Cs%%Mnjbat|m31I*%0%6xuGaI'[OfS #$FKv=.&L+ АX81N%ypf+[w2^7kU (/ښrr%[\콳p/i:;NlhfsT eWIoj*Gl/%x`#7_ ٳgyZM#o$3 `Jr.Jyjx(*9C əg#j4mX SiMҡ{.rbv`s|ҳ?׎r=~V_A\YG)Auq̎[}١?M…5mg8"Zci%3o<13k_ Cœ7/"`6E]vEZsjsb!r[鬤#wL|mӫժtbGR(xkdl装|dAˬ"ɡE^/vx3|4ۼ3U^Wjlǖ1a0w*c7MZe56;LB(x(B{fa ]ѻ}wxl]=nܙl_m^MޤmSbCa,o: ?9w Dr띭&|ir+ !'iN Axt~d#8'h\`FkُkZ:&ȶ~7O__|ii׮.)7u $^yROoU[ 󹦮@t QI99>ɳaJ#<|&@˝Bh(ۯf2O%"Bc`~~:0tچ0=yaogF4ՎSv{w~Hrض|w 嶷kMn"p=yrM4ǪsFmDB?vPm͞|yҠO!5A=|m< wuϗx1RrdqL:h>>bx ,͛5OUGi[޹|<r*vēy=]p@01%lt;\e'Ԣ/{=1%DBv90\FUiPO&l & G&LZkL9!cS`fVY;\*e5VaK ܌s c3a;#z؞đ  6j&V~h fp?|?0kGd*Yf!c@{¹pf T-< 26I|xS¯\[sҸ.m^w#ir0/0=b+yC{PNTZ 4u3iÝ".A5YA!z4ݷ\E?η:g ; c̖ťOZ$O5Ƴ`{Jdyuօ%gAv Djm=M`|ԑ^lt/ k3[7W߿hg"ldƁxֲqIFE1Lgb7;geCpj d`b,)UPy]y=3 [)IcH7)iJl[54PGuJ?e5dkC~{ FJ-~[Mf206)9``PtDy>z⾎(U8(׈6f'qLzݧ]wf}{B, F-R9]?oJ^@ '.-/u J+􃒳yjOD#/]>J[U 'xRLuzTklײ+Ȧaf7%aSԿۇ^XR(]&x\>g-!s?9^J3%ERߍ| !XaG!͛F-YuMb/aİ&pZbf{T&y!To.mqh<±G:f p3{_L-OU޵!)Hj6ݴו_ǴD $'y\W؋X3Cub74obѓR^ -Z('-P`llW+F1@DCqNAf3hQ[;@b(VOnjЗԈ3>I$ѧ9>lϘƘ#kuXf@*q3P:nB85ߟkE) "b;"ku(P4&DULʿÂ.5eBov_欫xU{`d8bq0LWq]lmwxRl:s#1ʎnwiZנ:T5PԤ`XI]7%/D.sYFPGHGD#*pDKw˼Y԰+H!&!ELڍm=Naզɾ|"@ ԮTQNW hfd{4y]/8'"Y_n`fVt(eƌxs[gЀxR%mj٦jwTM׏~j0b,33i֐`J\rx#»+o]T psoOSNl/GM+.7ͥwx2]Xag>əF NV)R~{o7MGWw]t.%ҵ4j=ˡ:`,ɱNP'LE]]ˬ"p4fSTƹsYԜB|š;f~rww!(~q~÷RV$id"|Q, Ukjlm6^9s U=ሟ@r>ǴR<3oˈM6m9WUԼQ[ӏ&IZLғUhGs,A*貫0xSÓt!a0%АS^n?HGr@`J9b&j:l-C%*޵ew&tBH "JAP r&Tbki#VUF`}b+2bH 8. F10apKm|dlܱO 1c㰩/1 iD_$ ,^D )]$*2*nJFZB D0L!PfR;`3LT勬(ؐ:93hu-zḂ0J' Lbꢞ 猵𤈘;L_*„)t1k>l:B K(orS' ,ǖ;^Ơ%hJZi6Ch|weoP^i?NRN}g9Rq R+OhvKbf#ʭW (N6 6'\M+ݗ]ZI8BPzA ZɣZ2w־<{`h_"B$[ Srw̳Ν)OO` no=gW;vvcꃞ®GrK;TKSk6$]\~']…()-|鋦ƙA$TO1P(Hj;u-k4"dE䶀|Db.\~!mJ g/#m" 8C(ΎTD V@7̪u*{L - j`Cg1QF{{2Ku+o LQ29̦tGܡx|,U:Z06)Sh-X"PHie` Zؑ4B7v\T5%yJUbTeS 0g̴.`c;̨ x^A$ɕS+йkQEP$Êu`:bed*CHui4@aI:syʣV I5@ cpA^c33`fD[+i)e@ Â.Mg5[$J_xRFpw{HjNWi'j-B1*rSCIo~W&ŮSMkVg)),ן ?UDRt7IzѦ桚h@~!ڇw90u3"o>)宅;Mϼл7]L59jf6hW wϏ F}خC˳j$E]09 ]|s[.qW CO\^dߩc13,Lc&0d*7yα11 @ y "3 :v#ufjf`{KseRǤ][%#1aCBٽYY #F9Ro9KХG= =ٚˡe'ن V,B.۴du,V_j!B`7ɥΓK'=%OqE3,Tg#*)כ#Pz$c[Zb#R5v*T`v-iM5W b0&zH «[6^65uunpwS|A `Jb` }q71ִ=ݓ<1@8[8~x%t#dJR5(5ُ7/$|ZIuw4gjQLȥ3opobX9|敒=}ۑz #Ax-XKa |~ΙY0N]^gT<:O-=Odv[-'7l7yw Fxj _OhMC#\'_zQ/ YI+)eˎ0G+)y xRH!xQBfNJs]|ά:'z(`00B5:fu4q&UՕN{b1w")DٮS䳽;Y)>yqCvr>@܉xy'I})PffЂRaw0E( (e߃dM}ĻNsVVX5$XUdl[>À7""Z>jcM~sg텋f4Bu!ʮ]'BE.v, #%~<@[~ X1uV&{ߴ9US-,-sDcΪ6D dCӵ\>*0*?5PQ3uDTVE2! IDAT7g>njX%W[ oj|uu,l W)-e(GlݮJ'sC]++fԛK:ڲ<#A8@<o6vNoټțz-3-,"!@7l6ܼO^8pj7y39bJo]~xe.&)L0ơ-.3G}:u#1wph6+QwsC*+Qr\,iɾ\,ˈbƸVw.춹M6]slɛOf ϛ F\Ŝq3VGnwM?5;zoMڕlkuG1?]vL ;ol9ϿҎ7V[z&g;ts,t}M+Ʒ\Ӓ"D'o=?ZJo>7s?_+F !7x|RIy 0Z, c%!56۸K]'!@ǭvEMMIb'V<0$b &j9~n6u_ ^ 0Đh])X!kby^CIZʛ+2VI\gde=߼ eggpDLɬ>u0L܋uoVnRi[ԇnR\`j6-^kִp=ڎqtpom` &6~հ{05Aj((v>+Ɋ3*Rґ ͗ϏpfLX*ݖ  EϔKW0lΣ%#v&S#iZyIޥ*|oB k0=v`62V52H^FNlƥeLK EP)0(C q `}{@r !y[wYb 3RklFEfaTjVgzSRןʛpN ;vg4u c6J&ZўO6c9JAd20Ȧa $j;VANQQ/ 7ŧR ȧn|ş[Kza-J rҚdW\NGQҋL^ϿdKOLro9TK&" &b{v>2ͪcp…7NLR:ѥ/<ޑxWҞoGDŴ+z.SED0 3ѡџ]s䵏͉c楿>ۚd̖Jg˭?o^T(ª%K8@ c-2MPrlq7qIQxg0'2o js傑/~&w{sb 6`*C>±pȦ 4”`G0ۧf;?n[2y2QpگѲvzX&8ٯ(v7]x?d?<:S| CǣR缙5vLq`Mb<\~5vx3QS bYk0pLџ =TǼٻ4oQaU\nyZ0dD`Ȇ<!w~S59S6B}Fr#e(yQ};r;z#+d؜`uDT.[FY)tX(+~_)g4g3sΘQт#Zr^s&bB9Hdyt aUAre}XHRG0\9%3}\/`>n2"WX-,7 XB{f$(eۚs//z-"TT 0Gr^oEFdY'yCsujHMe|~WqtJzRƩSPe@`OpTv8K|HPV'-(y7QJFr;H8j$[ o*P$@Hc[_aZ`961t9niv:SULYuoxǮʆ`s(/ |58>lrtױ֕udCckLxp-v詾ၱᶥg^ٺ= ɈD_{grj/lSPRԖ&兡>O6(yJ]u\|YIUTV;'?fW`oz~BRա{sBr+~9uϝh~X<V-\{s`!;b erb;ؙ5uWݑ=϶/Iz !"i%S) ׏k%(;v vG!'꛸7u7b\y]{|$O4ZfwdL)S7{0:5tc{d$<2Odxxk̑b1tabKSG{>KM6%dZl :%mOhP[ZE*F7%y萞l0GZ~`8:L0U#Qh+Zպ#i@U: ]E #d2 ^0O8foON޽xwe0-R|V1,[ӷ1#JEw~WQ=)::lJSu@Rr.y":= B1I3mC\>ݮcDN LxCϵt9άw\.||P C(@I!2@C[qm&VP NQΩt޸1ؤHnZ\#6ۦbIJR)tM"3 XJMrq~޾1ƀT]p-}׹ l#9=djYU (@k&M5$gm?x nmgwnglvGpf8&IZT(u֙#Cdfe ۰HHwW=/ Ӥ!ɖ̜P +NچXuSr k2+Oo S.uTf LFBbҦ.?TMFXVQX':4m8WT.7<=Q=ZpoZ tWыCY]@T})6z4e9dx-ȦfThS1\Y,A Thߙ6vnަk7 ` ug,2Yrw-iCOG:B. -ζm;{&.UeBaxVj uwݺjZuIܲ7)pUcn|zƆ;WEYU#>_O ntƮ\Bu9=+:^Ղw!Rc/&oޏSoA24QG{ Z+Y8 %ZwܥuLb!L}s5i{5_>êM7G| 'ʇe)!L×Ϟ;ֶ]U, 3{6_=+miǯݘeU͊etc.vZSM =N4?z^n;f+C(:mP_.13yCA=1A#c'9M˙Z ܻ̗R:c(n#]_}^-2T-#&m5LS-A z_;O&Nl5K]:M޿ʁjO{gt6 $eT`=. m\cc%bUeԛ㱛׽L`w]yϏM{]ҁdEM M0~W Gz;6ܝW  ?;^KqJ+/>܁DZǃW\"鬫$9l_H9z_z3 7E8]C` ٣J"苇/un=)þ|[iC3.0c]CE;ҹ1a-BSS?o33\>s!-ȕ[THtvu,JM7<~r啡G6Qf KTn#fw?pO滦wuNv~Ѻ er6133?_RXB瀨~R|çoľ I"x֗ocM"x߳I3Sjb @Π}Ekeyͩ1kgSv(]neP{c&e<#X5`V-m)@X祬!R>qSJ615 iVVjRJ oU /cLb\. BV3XXj1F)Wu(vHBqcGk g\ ܍*$+2s\P%+v>bbUbm0E 4MS,#%D5A`W QF@.J@Da='iG)pg0$ǚHtjѸp.<̞IT uW=(O "&ߥB"lEN8Sc1;բ]Rf-V`Ĭ}NܠpH(b(=/, >vhSiCؔGXxj;];7jZ vV2=2{ 4}+w\Bvwz[DJl\{ #Ɩa#B=kӎJF oo.U OІg:חKrVJrS/9ں l e~4@/[ٵn)WaBMVZ7bۣs_W n%/Į|jS4¦,=n]I/_|`r"9q%:qTjw];+eo!;#F/fz9,&BhsVg>-mynxCӿĈw kD4rT$m6IhXY1Xv cb5d Lgc=сg^/},G6Ĉ:ʕt㿙 $rLtg+ݜ]omv=܀f׉BW`y_$"|I/>.xnPŷni?V|l@umS64bV=&*,{/L%L,e%yOᎫ2:Mڥ5Ht O4H}:XBpT6؀! 2բQٵH5 aSJ†Ix)W 3zVW#|b̘@șJj%}H +JHP8 cC(2H>n@dID0B(ICcsϜ!.Ke;dcYs@cHӤByv3La¥MU4cz 1C[De\o ~Tfd([Xˍ 9H]>=4EF5JS4R:g0Ͳlޔ٠MVz-ch_!Zu= vksه!((ZCW=B\_=^)MGԽׯ{` ІdgϞllUj3JHXU-#־Ӌ[@$^TowZ7MW6KSXkI.z\dSf-Cm«EL~{irk|!D :osyISLpjpH[>ѻ< im!uꇮR_I~rt]rvwns!6XfuУ'AmWDs<1_:)Jɗ,bHubж=ފI;MM)3^,gx.'t*7|# ru4:<|m%͢TW;p\w8By{eU~iCĄ`q U6y;6E _ $MMɎ@}["Sa{[1U6[kk%ϘMXn <$c-;.Snt*1@@hSuUb|ݹMθЧz[u>@}@rnt  G߂Цh@Y]~Ҝ^86c [Hy5d=0ԩ7& X_|L;7k\y(lʅEn8yCS YDh<_egC(?ZonoЦf=Υ"= &K%zo|ϯ>tL /I6I\^/xorr-\<ّ{xNp6C>кͦy%BEz0o:苌" /4@Xh= oTܗ4SwG)jduR6q6*օLY}^bA]P;cm$:3SXmw>Lq*5 Zx9Wa[R&"B!{٧(!PLsV&<Z}BVwI |Y;Y)`}J$hyRie*HY J L$\50MkM<(&-7zRJ@ߛr Y+Iރ(>lb7M5=Gl,t]xSӮk c3o\&ubz  dT;s—Rڎ QSS4 1z2{ZvW1:y5F-<'yeu_@m 6% ]s`1)~;-[ʸ>!l J{*pMf@(SsH`nV7 |ұe,rwsN/W ,+"Іz(5cKǎ A[NX]N ]iI-l ":lIYN~6@Z<2)&YM.;l :CشIt *puNԵ>t8i &6jA \] ަy`{o~Զ}׏}9; 9_6_':dUm5vǗVY19V?&49OHx#}GkaڕȕmUX;1#yܠٶ#Fmr1ĦzNGbS|MDt_}ʫe}ԤSM?gu uc{aąGoؘU*FԍUBޱIe#ɛ+;?;PS^}2M %p'@qt?X />k/I\;W i=":^]ګ-(_<_HeηrSoy 꺃HDP:ۉ,/t!u m --jOSH k]zQ Kk'QBd_rSqMT/NC@8%o9j?!`;=J!$= gqSl>|QFj>90V^UMAU(@BՁPuhE(1dKJq dKU 2ULj&{6kGvJ=s'uCfYM{ݧxO3Q>cbL溤JSDjiR%1 fxh{AHL0h(e7 9h$h!,@ )rg#>Nk5gkACZ=Ax%%mzwY-MuHKc0N[9 ª:CV˞6%FMNdcR"!nv}/ zuiNk[f $&Yrk=W˹ɺ[Ayc=k_Pk;oцh`tsq(lW1+$^m-a ~-I{΂ ">~w2"Sj//2V=̵B0o>p֟/o#MWs] *wC}֜Zbtqh_:vѦtDۏ=H|še6<ޒ\Vqpkt=ґknXk0L}kֺIegvҽ2ӳ4sw?^>z,y_ga6uⰭO<c7{p3ݶ{ҹfURmFЀ0P֥i?v"9c w3x@ 5 vi/ -H@d0]ih0_ؤiʬ@`\<;)s'꫿V ʳqLeO/,'<Ǎ5A^JQ x  ,Ѻldݗ\-d*9 1W~Kß&Aε6,^c=vb+-|2oT $](Ѻz7.9 /6Qn]_ѫ˻eԖܗ??;НM0nlBYNW÷pޒ~`Y-D4V!e]bjg}grӄ CsWS`%5ws72U㓑9IS/?rcUU^.v.lvŝ7/_蟿2ivPI6_~:ZrrO+,lߔBy( oF"u?Ax~BTPӢu6|nWqN0mXFlؤ( G1/r-8VX}:`oK!R.8ʉZbB'ݙ-W!)&:?ɐHĘ# 6_|k9;j":jtr1r<@,k$+XX)qu_8b~)j!=dO6y>u;7F.t=6KVWip{mۋ֟! lG篶mKyDkmR6uAZbݱJFi<щ'$L~%`U@;_[[\eٓ+v,^ᕎF1f^=<`&ܗ?=3ʉZS?!=ޜs%Ohјh4w:ﶟعm?JN`UүƧ4>yꇲ{d]D`v??{/lr Tmg dUmk>jw yNXZIS/M _.w>_Եx҉/o ON,zcC?=`H00:INzr];lypvfvVףp>nz4,yNPҸϦX㗒g/ 匩6ښr(8^qYuMĨ1 Ke[*?@k{oin!5Jtt;$>0_"SݽL xv1*%KrӺRu3J֕}":Ldg!rRY{nFWXIH1 L7R!C\Yt#:uxȷEVLBTHym; i?cE}z4𥍞97"qVj:11Xby~LcF$3k?dj!@!.,[" $lv>"h`e w.HF6mg@:)TCk&mi(Vc@<@"謞u 3I?DaӢDSX]þc"<;k$lPj=[,!wVvEgcTvs pq3 N\_t%O&yCOnӆ)NF7(lڤJw=%"ؒUʤkZ{ͧ! _1X[>rS(A;CgBB`pt3՟\֫]ͷw)2|oz %uVd1{Zn 0/NYz e$( yΠ/=MLnFdKv/(3aQdS}Y=^zBE/]0F(~9 (Ct K-]܄İ h\Y^mc`pNB v)D\$d=] 'B:' + GMSP4S A430˕@3לN2`E0$T\ nް3ޙ}KB*&">^jxEV!FȪi dli*N"2 &`blaVLֱcTM}L1<:!5"gff*BH.h$V|EjeL$r@ \2ؐ}CM|l)# X q@`0F-LaO2+e!LD> nCb۝j(X`H- $2g%Q0?ic̅cDsz†Ŕ`NDݦYMT޳k6@P6{η)\[PoA-^Ba!mT޹6p*{VS5`Y)1U嵑P|5!"*+sz㽵C[w7)Cpe. LS7{$?ؐ4VJM>OP 6JA-=|]{E&T 6Qis]3;a@BXU ]S1GIzΉE{=wçhȹaOvݟ_)j]Ӕ];CLG{׿jOߣ?^{7nAbX^G6w{K{pf0Mɕv?#'lط0˒ = f#-7>yKq.XY8riDgf#rxg_BЙ^^;ӵ|{l_qL8 }Tm,V%>sꥄ~ݠω|_7iL$|q"ʬ9yJYh) hS`d}ހ5Y5kvSiC tɗL8({U Y/sU |ocN.B#Rb/oY &)| L^@t^niCn!Vn/(p Du2v# 6$RRM5l洗+_/t[;'SւL RFQ~ZR::|fɷO# oJ faD~Y-]Gl|nMkz1To=ٱU>.ЦH PyzYB]̶>>ӹ}Ch0DMl@s7WBQK.>']_*6{ܱ5o-F=GuhnoS1NUeo~uWyIBٛ.~49WA rd)=`k/By.,f{V+QX%]Ʈ(E_\?Sg_ J=䢌\MC0Z2-j BeDL}WV!! FP;9e2F\UpC|uBXv ƛ iv˾qO1d3%$Ygi.ګBl)j2bҕUJfbt /RG.:v\NVNL<rVt^)3 >kDXW=RTQN!=v_4On똁]A2e IDATn?FIStmc yY)@q],aAMC@69K{a"%Gs" 6+M",@`,5mӰh|e(\oyZ0m=+?܇)ך `;yUŽ!@T k){> g-C}P0dFaR ~btIotz6@֐f>'g1@eBwLx/WoEgEWщR:,h.ohNmF#k5,yE wk0ԭ`&hJmhh^{B[[=ܶޜ:s>CxYnB#ٽHvk.J}Nnvn]MW?=M(tk[ Ol{W}x=6nī`i87«eX?9/l͎xRIӛ6w>y(S+Zܥ4(c"Ե>,xRΛl8\7 KAHgБ`=|kVwS]_D:N>\qVƉ]!뼨 b}Xh\y=C{t~"y/FeK:_<c@_<B?ŁI~UP :,!,WAzR'!)U 6DBdNWbAhh 1dGiSa/4Mwv8_ Tq2l M L$9yg:J$]Ɍ3ʔnV&ףtϻYGHJ:bc(\kJODkǖa+,yTS߶[vŵY .!v؇):0-,ˆYlV`4Bɸ!~ RWa? m>2K |x& <*s#1Ni1HTh\اRM +:PXr q-.FasA"lo6> n՗"0jBP0DAc%7z5c;VQ;`^).s b *&MPCBWg%'?EVW`O)_= hW;:}ѕƈdH?9q)fGuǘWpt92ɛCS5ӆ.iڮaj7"3w޿rb `u b+r QxxJ2q=;[1Y:sk]h`8O~[͙hCJ ;{G[_hͤh^[b~>SVܾ'̩eN(C 9mMW1`*)8izp2s?Ϟxuh lY.-'\ !O(ˋ;5~}|?.[Kjy;,^w t;:Y,ZBD7XU*Z.Dܙ _|]|?H}`˧Ev:߁EcatjRu*BYuBzФ:QhߍH Ekb|BwjVMʡb jG{V_3ygi Ȑ ;JQV)6Y\ ;JIHѠ-0dql bLʹ!{! ! ej1VWdh`2掦KTbu1M叴Ѐ`'%2~lda_9_cu39OSwÒRt[wBHzĚ> 5tls]Ҵ-B eC].l,@]DۨU"@ҀrbtS$,@0 #3}M'!Q O?#z+5ڌ{, 2)ƅr7:hIF\JwҦ+V.Ew?fma^٥lBtэYH ƐBGg@~u :7a-_X'eVf> '`[@5qG+gtRS+ N1G'?+eN!6{FWwXAZךR[xѵx瀷Tqۨ/tHʽs[KcOj^7G/IOMMT_{KW^Y91M+XxXZj'@@R 8BCW. :gz sJ[oqd)vt֮0m6%oМ]XیI1Op` Oiᙟn͞SF׻zsg,s/5޶5pt`L}<~xWݷn"z\Fz}*G{;%q:`ɛ.4Ӥs};cmT_S(KC@dӑMOb+=̝xx~Noa$xNb @t6U|J"NV<Oԯ ! QJm{');^fn S*qKS⬳DJ=LVE1iGjZaThHjXкDҞ GL?=א Hjq` V.,ZbHH%:t̼r]ޠ% )R$@'7̛?؍X;vH3yc%qD n役de Լ]7{o}K,Z[J5D}uVR!v!Wx[5-9ƔM!4{|t60 'k0YAӜ?,EVWwN,YckF-mb6 e3DL [_5Ĵ3W "2A"`u]XG#-"2 $vNIF[. d!.yx8S#JZvf vi|o1g̜Sz.qHssXrMD+9w8f%V D,fCzenY-5KjR3 gU1(;Bpz5@ b cVMlt;znB%mluhK ،*,hUN`O9=t{rb3ƚ͊P!GҖO>ƚr7b^<:tsowS*-w) fcZ \ǜrGM9U cCD ]yo^>9M܌v{>h{&&GO]'7]c53;ؾxSP2m<&yjJN<xa$s _ 4-.WYmBf9q}tֵ>洕e([@B*w v Eg1mS]fYR !`$dg?cy5*ϳZ?\ d*MFSֽܳ )8,ƞ8 JŖ)$T)AM XNjy7Z5T T+Xے )hC.aO \v#KuJs؟xSM@аEԜ5loo0[7- ^Pk i_ l_\{_-VM*5qW gfPK ZK{G~Sm%eWN2KMijh˼.qŘzkW3*❆P҃6m^T5Yl ,Lz\,hڰ-˗=ع<2kcDUS(HfS}8yUs$>t  )vxE*"s15sn-YnKŤ,o`w ^nf_۞X5>|.;O Ρqnkv{: 4;>WG*W1-?Xj&w%^bj(eۮB1LwNiT|<NszN?S~h m)&Hug kQbD%`و(S cL )zv>Gw;cKUo!x"w7]ɛX+쐪Tw7Wpw^;3s@x+wqrGx@V >WӺKG)OZ=Խrgx}/?̎|)󉉀*_8|)mGx@^< dC|]@S|qpe:)*$* CEʮl;3ompBb |!8mN1f W|%K):<eʖ[♰΀qeawa1˕Y_J`1"~ƨe4\ iQ{seR7P!.?-UTBxқ4(Sc,-y;0v{דl@WF6(h rAK۴Y9ƓmDʻ3->}/|W />\&d7+=r9nf&lXL0,93`Hhiȏa+HCSa[ BclbCĪ2a [}@02wQG)3ӢtWքD 2f12`^僯< 6"&YMH_ay`zث}K8]h篾;:uspVJC33oR_T\`7ڲS}BghiLO8-oj]Ld!=y뿷5L M|xwm '8]cTk~5mF, hfͳ/|N$u\CDCҜڬ:iO 6@4-T ~b9d}cCw?97(nb΁ͽ?\ӦMV3#щ߮tm˃JzlW@L#^ѿ xC߱h{e8q 8wk s@U %ڔǦm2aH@P([;ECZſ IDATB@x-o?-VWA #l3FUZbR1tǘU6CJwvֲlÖ7hb)RXJB* [ 4!&P,@jgMd%`-(+L~ t'谻X(v}3+SXP]EdD࿎ O,IMTT}ޭ=]mbR6:dϽm~F@OT19g&7򫭞}Ɣ6]y2")C`Lޏ|g?υJ>dIA)6Ϸ_J`HHP\{u6k+$cܕ6LtmWDeӦx9VِLHlUlIdyM$XTӱ_iv|6uXг4CIҖ]6Qg,@`pK=kޑg1ݗ;5iIPh&ź>mfRnFPmt7OjnM"(_qU [@体 "2|ttc%w|[.Z{lGV(|tx3:5bB[,.SvrpS*WW % ]/Xs ؑ=;2BX:oMt6s7~ߓ`Qq7dOt&\+oy++"eptoq{+R:ߟm X z)ںE:W{6Ж>;ѹ8MAh=kKȓO}SA*NО7!!_:5073~[6=8=#y}f݋gܼ{絳hПZ +kWZsAڲG?>?sn\پ=Dg|=+ fvaqh~ 2a p6{*έGokEl?CVVsCXbYA⺠90exe^caC[yWmw)Hr]ciq`G5š،d}r}XzJ*y!,tv` CrWX\{) j7@RG[L.~# P*Gb #R=86hxA db.fu;U-tĥpP;YdC--di75m'̤H8sAҼ<(_Nk__M|1zmkVkhnVss@wVJOt>8Z[)~qWm 2 €ak@D!XHXL,lmc>q Ab,nt&@(vv ]$AFy*SZ- V1jk:D,Fa *c)lL{5vl7/{K- DEX0{xHZ&t]]laA:v()R!Z=$A+֔5+Y- vx2~q`fcW{juҢG0 @KBDlTzj6L|cd6ffΘ(WG64|Lt}%{| Y^|x졣#"l7/pmpC(j䦏1% ȭN 9rgL[V[0\Oi,5*@069xo-K5>!O7 MO,x3~燲ו'AP ?Ma`xѣ[7k~)71vmޝ<ǢZ* SG'P BwMrg,CG~p̫{&#)=B?+JSbUAEWr'W"B7|ɍ|cƻg{ձ ?{?WVw?Hn;ˬAo7, _K)g@@$9l@=@m8^[7˜˥ȈXJC Dɑ2j [AbD8#Cvi,Cf`Bm ̕Xv!IRE:@j v'3]L(aK}$#xN*b@9MY.3ІX&A{r{@鮵㑭Q愊j&`@o zpk*JlPi? =^i r܋}]`9]/kz(b]e@9hL%@"`yi" `\")} *SbC"e%u@@hDX69=V !(s3f]*{HH,ʮ֊=evMSl1#ȦQlXT2i_iaP[bá`AtqCЖXJ:[Zz{ְmaMuzco01Mj]I/Hr L 8]BJc2ӕGߌ)l\r-l@ĝov!lm븀Ж+dkKَԱRƦ ]KxU *|laù>OgL;]t,0r}9+rlG#K W^ Wĵ]7WiLJ $}bZ~r#'j:5Aw&a9?0f59ݺ!&|%oKkum>\G/lH@Gr~g*S;:/޼GO<14>4Y).rx ʲ^|s= KAB Ou?t୷N-men>yrrwWR3?{U!w94s~dƈg^y}?X.:`eC ^/xփH_@u@e^p2; o3]y0ۼ|Sf=eh!{Vg!1!شB]H,ʪf!LC ]]t$j:{iJN#,V㼒<}[ªXM -=Tk`}hMjv2d_iX $4);4 Q٘T b n=,ml5BYɅn&l0 E>QYQNԶ(N ȢJUUGDB[G[co\u?t,IYw8'#[zIp bId}ӵяF}(]7TkË{3~˜A FSlaAXXi\ń`6VH Nb倾i`,`F&$aͧђW)+  %E@jtZL>16堭R= P6=ȷ ‚V| *u( AMoeCݕ5 (b:t 7#9#×Ju Ԟy$hF;JcBlslm!w'LV󍁕ؔm&"ciۂZښ6ˌrDyDlO9H8R3o~wdc-REbLw; ez ٮ%atUNWPȉAH Xo\Z]M<)@DW[&(WK{/ N-xs[:8ܹgLJ9*2 Y$/^<8O~py>xw׹~n9^86NX4_  |xǻ絒S l9l$Klpᅫ֮D>lA@>{{G??7xq?Gcʷw^}G᭭ŕ{!Kٕ}|s{|YF~o<$|ɏO?9=,J}[{"6DDEaN6Ι6-ZMA-<ł?8Ave4}C? yqc4c,,AiANYH&FGc>(D~9kbu#z5Rֶ @Kj!LL>$Vwt6rQYAwh6%tdZUsuXl65V-k&'0ŖCCj j@P-; INoϰ q3@1F:Bfrwg#  (L3۫]~ozIIzŸmet}!! F:Dq6A{j~.p_)*/Z?iNXu>v  D v ?d' ^2]./|F7~rN(FPdً/dj;ZӨ.]8+[THyD9aDqLcrrnbQCL;DSHH &(X\^-վ~҉AС}Y9B)^w\m@ J!xEP!1S;&t;EWev3wD[ N- PXeh1WӞVi)E5<-?$ޠ)@nFrv *c5Bq `,ĚS3Uo٨@v) `W5<|*Ylq[?8r1N1 uuP(JPuccC wkE 'ѵ@'@-U-z%gno1" P"6 [Nz8K[ +#rQyEzر[INպbE*Pg T B"HZ[x{7hT}zLS;ƮHaAc6 i>^s17KE j+Ba>З :z>#R q}N~wj @50޿U),{ {o]G6au}L7,EcRyV3>o+7SZ[yuɟJ Ovbs1ӹ̥"sȓtd.iwiO-3}\}wr!Bvq_~i4Zm?`rz:SVa_2rQcZ%JT{Ɯ"UŊD{\Pa-hnA٤jg $6k+m9 ]6-J~On"HRx7m(mv)dtE,% ga Y:Jå!@r٥Z}m pv* dTC(2G@k&ArQTzjms%1Qe JzejK鹗@JЂ3u%T@ LKjv ew5݋=l:>( wWX:ݦ 2vGAseUFY*v^.VB0D)s5x36|us͉߬ ^FV%7(e 3.zY";粟'# UQ|͍;}j^~ b p6υYq N1˖-+m1]_5X-%9-}v_D64$>V8 }yҳ٘2ڼͶס]Dief h<.bF;ͫK۪D-tǷ<۴U\#|)T/{3MBg g\5 +6amkKڢ-}_Dl26l{Ǻ봥7C!؂Rn=X+pLa ], [=p}Z ];ԞXmT\;5曊/*r=t㣒?x+b5 ȀLg쪔kvL]@[.ҽF[$Njk[:+xA րX;7A61L_7Kw۷v8x˕'㥨>\~A5CTo\ gڊ6嫽:|D#;4qw;?U|^}eT'=0;]_rQ M3{6ݽoBRڗ,I ,wx'ݗ އ N'>o87dkq8LfN)6wtt|ǵz>#9q  ,Ⱦ<$fJW۰Lm3 bcV%(+/ߦ eɐzu<BUW!$v+J^ _FPo9cHDޗj SASjt嶄rh8JU+mr[X}!P}BѦxlXf9amJȠuDګrpUՑ.6DIT(Fj]X,l?4Fɘ]/֐Bvoi\N"I咃K`l6mEeLĩ a۱{\1'RtbcQT/O6eB᠕6f)oBj۪|y WNFiE YŢ 0q;j/rd-V<ܓW_Z<ݓ9 N5bE*:mxۏiX<ͩ5xed{lKg'Y],“g.zJyVvJ>JCk^QݞZ'іLd>]٨ ,Bti3>5yءΞpHr- tGĪK}D >,۝ڹ:Z,7 O‣׊LMoLWXOZr;RM|GbL H.Oxo@:^uܱ Xvsx̑&O>z b% ?+揍nǾO|>cK#gI9xo0.ϟZ>Iцm)0`Ef3O{wz{Fp2X.hq_$UPA2lF"~$heH~@:gv :?CHIVk{64E9@dqG=La@Q@1m"l)r(QgyU6 PքpjX] &$ٌ@:uʛ}RfDﴣC;K=  !ȶ)Sg;%#i1W6& |(K|WˬTL Y6¸.)BQ1,+nNw-\#]XAT~ĽT )zu~R$&)he4 "L B%,K(F DF AOjw[j_ vQ6G 9@@ل@(EN2'xLpƮO>M{cUg^͸|əW]?ܽt;3L ,II}f]9ŝ҃6'd/v!_R ;92{~05p宙퍁[TBVl_Q!SS>A?y Zyc!BqE;k &fpXFX4u.0nM1t irtkzn,ci%T-f/fp:`p}ӝ=쬔\R1{(!Ur0$;{* Q DP>?>헿+8]6Y&/;N~b1gwǴi*. ç\a kxj N:xp\5_&譙 k>H~<&))w<j72`(5L ɑPb,ëǺt7w\vSM7WcYIʦ=g+׌::: [MR2"%a{`H4LW}0Md&^Z4 !YB-0mVju SPi8! D40$1ώx͈ߝ_hۂGDy`i__ Bfe,dYiQI&nКNk:j1JP(>)wwj>re>DtbLk L; r?J]nY JnZCs}?dtq3\FxjjO+coJwNs%oL^>i8F~5qlD߭T7. ]Wchbl47%BYđWn%6{ryF,\YjB7Ok~hTwd66 1BE ۗrS"ɑЮ/%N1(V ;~tOus9-q\T T(H G }1l}/c+GA+jB)Y f'wCB,ƲlbhHqp!낲 QLے=}# 'Xe璖Y@-l?,Rek3EW9|_xbܬ4`(Ty\z-{ 9C){K!.`;-7N9a,a0u PѡuqԷINkpg( l˗ ƔeKեb tEOjmϚ z ǒ߸Yd"i2uƷ_?ޱ%ۆ"J,md詿 M~kOL|?{ƒ3]0xxKCzX,]wReZ-޻ 6bݍٸFsc4-eږcUU,zM~@U=RX p"'yp2'_p'ՖKŠeKK#Wv`X5Cb]uiĵ7N}"H{جG+&ɣ9 a/bVV&yM[iny*:u_#.Qn:;owM9zU 302AߡYN x!'~0BWf,a@"q:@HF @J7a)0zVVkR{+&XG v#s "/`H[_a aHWi,>R%sek|!JJe\NkE%G5d\̖|] iI{0X&z:homvl'͙"hȔ+C 8`oa.2V`V!d*]fy$vf bx)?qu7nfVwjb=7AٖJy\drGysAЍDjyH+. qφ"nfНff:YlEiCF)FnXFњY18靈u?{0AlgC@>ϓ.dݖ_tO'zne]ޭĻ":[EJkjO1rzb3Pa=%u1[5Z_\ҳI0ry 0 X@uЀ(L02,'ta9`e)iTt9DՒ@DŽWWyVS;rH/[asPu5' BeW #V!l|+mW ݠi9a[e@j_ yF}{MRvUH5KB4,@`1YF"z2N JduyDs:\-)lG4 2R)> ~ނ@`̧/㑺u!"@4􂧅53_24{1߾.zK"T\,+KM-&]Vo' VlFx6 JӪ I/]=k,"j,>6XQw?Doo-9{~zYY'xV;.&gR// 3/O1jtb @Q)XԱ̓۴|eT~rlWE dm#oO^xi>A ћPnPT*櫊qƇ\Ҋmѓ?3wnlWr2=#U& l/"\t{[#;dt1=c3vmz;#ѣ-GiUDm4)_<禿:;h'=F*P0-=M?H!iRա<"¬휸{?TƤ^1Xu`-7xJK)61WB)5T&xΑ݋H (Ml@ZK802:D"`.Tf.v~ 4`eӜ)P0$͔-A!e`1rom 0a0tX n2(Z-#q[lfֵ)ͼzbN7|)D.lwB%zClkW/ 6FemF=<ּ|{CBݻ^t[3w~0͑b׃DNK8rJ=>Ǜtݑ"#"BGXѲ(?|H&^4bL`[N6tKtg1VY#ZșҌR{+iFHAXN͐q TQUva c9C fz%H 6L$6LNB^ eB%0a 1%hiizb"ڠ$ʻ܅QV2jO&1yᕶeV]XPr`'ku77wEvSTwFUYE l:>7γ‰" 0\eCULPbg'?{Lܶw;=8ћ#_HvTN:쳯w_W]:sѥC6io @b&Oۿ|{˜["SO~x>wqb=۽rk\ʹ]g)Ո8J ^nL^4J~ - IēgZ&"up0&͏њ\ Sȩ޸nE3<0SGb<>rS*5Xht̑X̸," urUՃ 2R嶕 qVc3y+om,:bżTh?7<'IO"jƒi,}MH:"+je͙)1$?}L'(֢ڢm?J xYI1WbʷRGv'#DR)|[ID[ͩސMӟ^,@ S.g.^~O E֛c;De;<>1̥w"ϊqr>^Oi<^?ʁߕi1U~xm.^on$[em%'2'Jex :Kehk3wX/@g^<A: wetL;D`ʺBY:I9Ն`Umx ,kun[ eͷ\#oJlKy @([&X:TIN/:[s;01(|]YBhpjXSF0eʤldƻLmp (G,U1 "0AFj3|c&8qճuX4~ ?.wre=/~-?~r|⇑вQs!{zб~+kBG4ilȑ7O^fsb͈;{ fYqAwbسxumG#2=.ͅ&bnv>ܙyݓ;Ȉju +X99wwHB5Ws[9Ҳ3WsIڮ.OuQ FLy3w[:wHDY -f/tܣh3$GRNd 0\/dBPMIH['$CF@ikqj *1~yveFZ!0 h5^|0$K/+!V:4Kk9/ICUKw1Rp vڸ{ &9nitP',ۻ'geH )gqDZ4=X@`ʧ6^U֚j9 w1 `."T,nCbo?I*%îBQL2g|I.1XgANL2Nǯ.?e/bkd][‚=s EoSpsUF"F[^X]!$]K5Sd{a@p͋&AtRLiZ4 L¥SQ0hԍo 0VR}\NP1sm#I[~+mxpoz>+͓O.^z0' J쥌7}wEHǽKMޚr|ẂS2㿝8Ïye+ J7/dih" =7>P'#떂HK#֬8ȩ|ټ ~ ΥLŹSY }:zftk7=6l&3?<  >wS6"ݖ"λF+$xub sv-4$"8v3ʵJ/YʳFĠ:a)L%Ky$ljrIZ) ܐa3:rgwLuZ&l#mP_hY&t2 hQk !4y-{ZtRnNW҇(QD`htZ*C>K 5kP%B$T@hesvN2[긛o^,ӼTLT ( IDATrGjșWo9''GݏSl.xM, dmj^~m?9.bInoou:>~{;0$HZJf{x49jsB76{ w=z-R14XLQJuT"7G2㗇4i`@@LM6U҃ZȄl^\Y^UUԨt-n2BF T$82F2mv{[y`ٽMb 6#*4o`QFA L@5)@ [FXӿxuXO C@ 9>lj 荮Z .ɹT Gy7 @""Ų=ULrr>4bl<}$AAUZF&{Xa-;Ѯ6t\K2LY, 1}uTbTq[P\׃8*IɽkA +#G[p/;aDo_:t)gH񬟀5W6;h`k }aGfHCo%n~ri^虋P~͋C}3W4z@(|{n }E%?{lp%NZ5o0t;b-wq .37p\y\-;LT9HjbӭݮB$I귏_>4Y¬by @8v 7ZG|96б|bZmН厹Ӄ+rzVV 7.+#:͈&$)vv#|3>=GZlY{{x/OolE~$94?<_Z MY6-#aBkڵuF^alǻ)`ivuQ !a)?6WfL2! յ dHB+9,4H 0 uYp3R4TA[GSX' |ۘ\qdQ %+ T%!jA$vD=1KZrYQq[,1>{z W!̉mfnj~E-BvB@u>~؏⼟?rnI-nn:߲Xj{,ղX\3~?.n&LG;;=֥o[wM6Vvvn:'\֞|Kl~wuSYLb.kʹja&lK2K/v30XLC_1FQK)J-Q,ʵ9g<^>Ӳx-/Iw79SιO"|f-RWSs'U hlӚ{64c xgH-|E>M])b-Mk`J6b bh.Jxh.ȕ\olmtu.mX fdշgqZWt{j]?֖rn[w=XJe&[V [C#_O -[ ! g7O\_AN\{I;F&GO!f!?`R6tG˧߾80O:>{y$jɵy {y0ꉮuzWomE;[N,EdjfA,9FYlf4cfI V~KS@xW3 ITw?(:űJ+Q6+-iL+.HbҬ-h?^F6f~։^=rNà윸&_m5% r9R$t@:FcVZO*Ql X.s mw>bH*k>s)ȷ 21R @c\V*K&bh5,ȇ6cw2IDuLHxb]l#VSpGG[M  =b̤a~V.}'YYA7Tl1~Ƚ} '$Q7LnͦfOQAx7ʢ\ .iŀ@sv J*AsYaEmމ8wspT[FV !KAF$> hYw%*OWVm>y[GZ#k^3G-뙿؇ VV\ܖhеn|CRp5J`aU@U@0Eg`ASn *Fiq]p_`)"!wͮVfd"i 㠣8I`b di j=W-1EX'R5=ﴦnh1O7w $Rkљl9F3IhȗZfF^ڸ[CZ*k&X@|Q~"UA9tt5mw􄧪{KBn"g!0v_)X(@{vwߩO~)ZmZ +K+ygεB1 DMey ֮1+ˡp`#v͋;=ѻ31MnA#feշsz ힱ~ַy zϏ9Z55nn9W;98uMVC>< -MӪHzcwys̹cpp':.yݷ|uw*{/\7޹J39t6Ŏ'loDL%D`18ost]2KAeŎDNtFd<8 ,fv3!ܞoYwB٬D*{ɿ3[nci-I,A!6A5 6)5):.Ңb9"َ\sd<0W؝X k'# aVaBHC2: 2D:Ę04L.gFgC gʒ@{CD1{!^wu<IU9UaC\AVЌ=4WjFUQfWn[z-)Z2rs; Hq%бy*񃞎 qTV !Ȇfʝ (Z|K{G1S}z0B <o'X??cmE&/^5 N T@@Fsbg̙.\(47qhk Nͱ.mu %1#+j4ٚNL{Ĭ֓5LhӽG9}'^;[.ԛ^7xk$7~$h C旖Z$B&Q\9pēJW^?Őv!!m~+Y`$){זO Ŗt+Xqtwџ<:4{_?v%NŠIRC:Kmnh 7o%:6{{fԻQ;|ݻQ칷1 _X,bi_.'4mAT/p$$,⋘Z NxŠ'Rw4A#Ŧkxu.XaseSyy9VdL|LܖV#w9$TC"ZA` #V $ƕJS>OtQ NԌ(0A"Q _9b.QaKy揸m&eA){% E 2ܐeZ -Kt}eS8G<7{W{ WT&YHHw2 5X{>h,eZW^l;s_ HrOyٽ]q$;;v:ؼVÚp0-Xm6UYu$]S;-&Dg*\H[m.wjY:SؖJщr:be-1FH`~MK 0 H) p,4rCe`@@@ "mjRt. @h0ʰf_)N%i<79eke@2bNYG ,p >l6H`H]JW}EO7Wa#mx[);VY jĨY[{_ 132jsf)Aҳcou&Vbl'2kFFg [?ۚ~ŝ[R OFL{K؝KEjFLP6[BOXEvs)NbzX&/^3<'DUFU j+GQFQ@4xzٶăN9V ` 3[O`8FZOߏWS7}kfe+9-' x+So Foo;=0޼ݍ…Qyl{r}MoK/x:6x?qJ*k|"O X6dmyGy+|gHgՑk<_e?y觋|Ziزu*Gifb 9MlP|Irpwtі>fSAF<ϟF3ii(S< ITy|LL0*^`Vxl0k}&[}= dT!k `cW[S%q4ek[jT)ljwsL!tjӡ:a|^P@jPL %B3nʗiQU@Mݘ<`Pr۸h.ZtEȍkEBŤf]I +-\'E6P-B" d>ddc;CO92N:o;&}#&-\EYLQjZtN]w=RԷBMk{R&rxDbހ#)rjHb,(Ӻѵ3p5bwG1TRXA-7z*дYlt,XQHO:GJ61%R1Eh&ZKdזXQ-0j)f@k!-:E;gKzυZ2nrJ>6⏤HA{/ܨvW,hjYI&Dw3 `h(am.ueaHHä͆a5, ȓ) 0u[3c! Mk@|"#o62liߗrlV 'q3| ClpJ&tƇ ܔ[4eH32D>4?vz-+i۾ί}=+Q:(ZaJ#c`mb4Yܮ6+[G3{ֽAXgbo9§롣 gZӖkٖvXdg]@ im F#1vg3Fҭbڊdܻt$ ,򓓧$ȘsGI] I6\eu# 5M6t6 ½WZ*<+$2\67~!U `d\}kϾum)Ɖ LmR{#QO[akAkAjJzE⩭aZQwZO>xINpƛ.[On|En㟼;^Ι&i2BǓVjq2t>1h*ʘba/,\]'۸RͿn^-R=cdp6mK N2[4qҖݮ䰻l-,DsIt9y %xynB$`0ZzK% VX9Sϛ% Je~dPifNX݇AEB.1jIdkP17 ^ݫ# ) ZWRؘ`2өcb@Q SpOdg_$4Ô/b0PfxbOժ̦,oYEZT C&C A}Xrd %'D=!ƶü pKuqm>`:WEœW6,qvδMCٯ"~߼\jY. ?~~?ˡY68[=xxJ1uuO Y;/CD˜3=X&JGEr"ĸ' l.I]h[=޼Q 7W,-kDvB+nL2?:O04t.ԶE[_9("$c-@LB͝Ӿ ]C jRRmW0$!@$n#4Mc `,T7&9B^!uh|rFR0ieys @#,>Q%Nc4hҋ?bUEژ& ^zBR<6"hJ/Z:b%*j<%JA2pJ6< $DIRh+)m[a+;W{NDP3_ZEn\.,Ba7wݴ0t+,-[݇ƦŨb^r/W]:HWzU)]K;-|k\ $2RڊlܿxN)]kNܹrZ@>8ZGjKZL;ME޿g aٚzĥ|X;gT$Ja1L`$Fk8 /R =ӶC{n-%hU[?^n sEꚊ .$m9+|VD^?c⸪zc1EZ8c1ۥƒ7֜;Aǫl۳r'z?wm@؃t>iK;/ IDATZ/ospubeNl ,\ }OrrGͫvͱtFtlގ^`urϵ s4*|Aka?#mU R 6kIzg HJi| ͔ҞɇsWTr\bZ+̥Mgj:kxAݣX[0Ab܇ |ۈV-.S)C>ac@d!TP.t;GӶ9Y$ Uz^!lJW!r'lK*|HX#:90;=l*w#9iݴ97bȼsy >(zWڷOqF ~؝fMet/znlwE[Fj~+k'~0s͞3Ԡkk}]IsI]ѺT HZdup#|~2+Tw*VuWf!ƖhB3ђ+Kc>;Vc?]dL @TtM~}U/Њ9F 1Xǝ2#iҬգ]R3jϜ,e6f5,r=,b\_ ~'N B . /9-`TV1FF$ z` m]XwaA+$ʞH$ vӰ C Y|)9ǪJ"5F(]v^w {rMҐ Ff)?,F aW!2vIKN)0Zmk.stpd`/@Hkfr.%@ $WvC;eiM&D` tJSUlJuRb2owa-) G.aƥVJsmE6_ɢ\ps+ƛ5_8-9pHN[dkœCSs=sqB ,n&o}!fTRVV`VR[S7=۶\M?#F<;Łk6dURѼ/GEѻU3/?xb]1wv$94p\6nh ;֙x{PWߝ+_=5FؠRݞw|\YyxUw9۹y89߹BK}'>Ғ۝fo1,;}7}>-+N}mxx v$parҕNVy7gٔe,x&k? ׳9Y@JV_=B)2ΊO|aͲM)Q?3 xIF S8@z'D{-ŰJiκ`/ >j.G)U咹7-" QlSnݝKz0$HM%5{ĸ! &(D҈ 1hVg93ifjU~0AJ ql&Ѵ9]"" \xi @LLf&}=U`n UnX.MS $t$NYn[rqu\7/x;JdaJQqw@بMGF2f #Ę˞4&o2:No@:!g~o(_Ѻ05h]) viȈ& & (=zsf_ԙVNſ隊DTQ~i+1$ uЃhty)ZKp+KCH@J*j:ܲeݞԜbּ>8enۓD;˔$$b$X\ȭ_/D _ v.L'M2y'XHEkˢڸ ;&j[o=~?Ͻ-gQlmN٣p3No1Mvx>RR3j|qѷ+ꖎq<7a6BfW[_4ؾ0@0tMnW(_ E7,Ys#@@a6 +z& p?"5XO+p^,Zڱ5w',4eX |{pbV^;ӶF4 g'[i7xt&b@b0 DOqGu52 %͖޴TrI;鴢#Ss MH\||(ai@ECY Y][8 L}A 9:{ȃ~Qz3\tf?.{v,Myx}p9 ٩KtYtݭ~O7)^<"*xG㨋wmM21ь/꒮ aK`vfz ̓@FfwBA$elz)(~rA*511qُF$:!5cYքܤ/#~ LrC~d!e}RK"TmiuI qURf &M?uCf_E>,8L"fzKhZ=ѯ])Gp775 Qû,g68lP+FλZ x,F(C { _-O[I5g*U]2Xkb̧>x¥~G[SO\brTV$WNjC~ ѭMjwtW޴o}ݫlSLiǥrӛVc@(ef`{54;u&+N{*5NVI\ݍ|VWodgD9F74ׯ-|+;-^ewǣ'|llԊyI=;Io-O|ybPdXB$ֆjk(ֺj)֦fe8_IrEg>.hì-9J2b,-:Cۯ?*\#@!=aXQ69tMn6|#BmNJH+uqL}^Ę?7id4u8@'H V+# RoE`@iP(QtT3Z;VUr'j^;9{9(mZ+zR3L jStt'g< U)ٶ45)%hkϸTh\pȱ# 0M|"ُR5rE?=+Ɗ`@;^"ֳǏ{*6 &% \/"u.Db5Zta#8N2Z΋K85[ e7!7 HX!)Kuj>.1.( ^j*+~AG~/\<)n!lAhC {{'} !?L[? lQZexap=!<o0r0y&Xe5ٮ uV1~roTi~TΚ[} alm!_M[>t" ""l>2`>WFɣS[6l7^٧.:Ϯ^>}z?~[yugm!8E 'K|NьQ= qSSk<)gz![oN;[Sv$WIԭa~:L.[h=+ϕBэ긗V %qNuޭŽv,ֺj*΢dihֆLtRpQu L &hiک.t> XF ’fm|+bo:,"kBRXfw-2 ى5h617r_ׁ@ek^GH~?C!4/  6i;+xOX7-b ꐪ`FB)^rM;.D:BwG ?<!S|ڬgNrbVq߳:@֚A hŰk cSR>NDͅ-#@4u'_#j-:!Rw?kWL Ja ;V5W< Sv!mѿ~1wZk'%էl9qygJ`v^Q%9yPۿyߧfӟhySoM,R/%moMoؘ4?y0_D7j(,fw6ܛrvk磍& A=r5A݋.7Jצ AItMY ;EPb[Ҕэpj(,+{V;D`xmOO"/+/H dj ډ@nLb@aGtX1b>, }X0  bekM6l(5!@ Yiߛ&@r?({ygLF+ 7 0L8P,#9lC3 3@BP}Zsa19`?HKVT~?,:Spp>Iԉ ?W^r5j_MŮEP(\M@]_\DipWOFҰªZ|/=.Ej+Kl?{jVQ#]ҺޝsΚ~u~V3V^X9kBk`UۊU]lO>V&==; 2eN2V! 2hUdv")L4h;pE烶֚f$@C8_G^V2j{8FHH;dC5>`WP@ ?((MՊش 3s*sLPi,NDgEd.p!jMVDؚ-d.9J>hɣ@ M@Dw"ӂ%zm+SW=&GE oR,uG<)|{2 Q_ Gl0&7-"'lɨ'ש9.G6ԛ־4~ 7Wd!d8Iڝ-S-F{PGUܽ4NhGvֶrG]%4'k8,udbwH7n}"#g#Sne[A֥ڈ2jvn$QdFs.K^W4.ީT@ǚq\7X@a,$X5lK_4\7Zph~bsa>,P3umۘrOVk݇dʒ(VH#_u7nQZb輜uV W0?@9r>NhbSoJ=JRMnn#e9&fugl8G?Lx ŒZ8/6b ݾ~D4'(h nwe2trqnz}F$$&GyE5@_3uO>}`H㳎fSn{(\4tV5K)dmd3?uRdŹ /*B+WN;v9|7g.wjBZ<涴4n0Bs?Η=u!bh6FuK)nB3/924ѕiO~9hd(s*tp.n֑>ys&u:L Z37QVUY~.\Y^mEfPpy 4L"mII5JK@o Ȩa0Q ^B)vf" Xhm@0eh;pM8)ck}ʐ Axi\F kɗS?Q&ќtk'Sk|#~O39=1y&a7yJ[72{;+Bbr*^Hifi{7gm֖[L܌9UyNַmMV '^5)]eF1n`cl<_uwVydn>4itӣd Г#Bg{ʂ9Z7U;W׮e#*n QmmwoBWGD IDAT6sgcd P68&p\ʏ`[UE cPwc/"~9BI zgu5 Q| ,*f4Iq'l'o_⥝^߀C@8-E޲w6hl=@ӆ`ʔ#oZk+[ìֲI9TE"By!YCҥ"¡"ѥP}nJBD[( N&n-~9XZG?È$flgj0BhCsb\?M†Y:r5TY>;4r` aVSRSG|@/+IG^HBPc_66 \b[̬}Yf9OdXMs4:c$֒XU_yH4QpOaVȥ9WM NQ#r|t;g?HN-i'yߝo])Xea1u: +n1EP &-VsbJؗl_w롭F\ܟoyS#3>7ƈkp/u&_‰ۅр|s^QXv/5{䟒fW^RYhWϖ ~ /gOn*%Cu}P#JE@Ut @s{MvDk[AU 軗F;5{C&vސɘ?ݠu{ !ŷۗ& s[{gƂ+nYVȹLĈڡ |WG6L&$@ikǧgE`T& hDC6Rl!8U?>4fgDL0VI]IOlҦq,R6 VѣLZp8vpp#C `Tl4y Bh0tnz>go=tU\l0_b4AYNR/-M%[Kd}^P~ogUr}y6},ro [HXVA]-=. r'^>8턬p48s*GCq3\GrG~xSEss~_s/}kww?nWߚiE튋E:]/x"N*9gM\=,SS'nWҼO &~ח 6MX  o#!R7 S4g(uhXhubm¡@I; >./#&e 89E>-yNAb@lB{ŧYOop88:c& !]Zi]]6í@7(١1ĀQdKQ?2O7=6)[qgށ2,\c!"oHagNmktǠ%1b@`p2H#|Qˆ-quœ  ^_'i8Qv#D4RmQPglB[xls{/#OLR8nN WA{.2z~{.ݢU2p} 7#ڷp>h1 GoMVH7feu{.)-]#֖b4d`gUJ\ uYUZ6zfśko_'[yh)=`T0YSɽc)&ƶ3r#겵dN1jr4'jbmIޓ4ƱGiv;)d E />ط$w"SRۚ^X 4CAdeI &J bL=% $:Z;<Ih7~_q^6͖{ ~`0}$&DNIv?"nwN)/Z@BijbߢL"5ujBH᧴!Q <|ҏbӆXG|6_0sG"6_ˏ!![o#bzSZAh;-ѫO~L5VVɯ#  7I*hSM{ϼDYMH>WGaԊ+^vѠ)CO\X:хJ#"W&^?8Ԍ"2j`dk~Vyv]0er[PāEWwcTպ%Ds7ynt;{:ݛYE_~$W]к~u_~.MiNZu=~Q;+[zp;{Ҳ۝u-ݺG3ݶ.}{m[C 4|{.f%ÛNCBKQJ?Zf YTmV?8[19 l-yQ1u:O|ސpoՉN \Gqqe7Tukg=B+NvG9Zy}Ʃkn0^t$~ˡ`U2Oi oh/_qpTc@S? v;^DF $@ @ 5v >Z˲Yc=1 %Hvf4"L'.šf + # bD[cu PYY8oiypRթS2?1aDQ't(dRmRK@DeX\MyP[j" F -=@ hn~^mw&@Ns},_rS*&@h ~)ZU]ӱ #agVa$pى+'[_ ; փos^ojcx=^o1nT`H_7l7wpƓMVM*ߟKKGo֫J#+‚/lYwkUd^Odi] &Lkh(qX E-Sٺ68hE{5AkD]צA؎n1L>2݆]`weLEwKMzG) $$ut$(AB@lKo]d2Tc'K<'AjO*K)/8Odmb 2Kz)~eK@$ yz^q]2;!^'",1fԢ}waWAqE{i>Ss[6asȉ}X`z^3i[>zuVo|.{uІX|dtƚ})\ [j!vLt A4VXӣ:B @hyxjb[BGF2wXzJ&m.*mv z`D=:F7񓓿?hSt[\x-Dx \1h }Wbe1B 7 ;PY%ħ⡴ax*40 480M$! vAXx[qmZs|+-w^`s'5 7?,[[m~/w*M=In>A54-e~rd#:I08E7ɇlW'wOMgۨNG'¡l/,8h>xwVp ɋQfh@vsoI ?W*Qeʽ,Kߙ_T0Ȝ ?'?[{bŞduLs`>+ez[|JBhA@A@h-߆jdGT5~~J˲&+2J!_S$N9 LQkaV:l.k0՝q Ĵོ>?pY:Yb6~*@TSqTR}G <Rn֚ciYHg"43vtŽWrAQd̙lv -_o <~,i^mtiEH'Z!#tOl#le$sgؚ1ioژZbF&>D3Yi6p:ڨ:]yMZ3{ߚu%oV|J2ȓS(fe-@0 5ոBl*Bjekû;_[ʄןjК9 &dPpX+=? 9%!U얭m8Y~ˡ 6  WIAm=^7"ngU`4dK+)I?/8Q, XY#bRީcڱ_>g*f@R83QN݅IDb-󪼁DqMxq@SC-EܯuPѦ,V{+h0!Xi,z  M) 6HUZK q 1h>FwzF˨ A;,r2nWF96#AjguNmv?Bj)|LPX{ځIqso1"fw0{?~o~Ö baN9EwM%ovÍ#/`y7c|mjC- zVa" eMe3K `Dt7;K8x8TW&v7Ch gS(N/o!Y86; G4O iZ0;= y'a@@E]L^&epd䑭o^ʜA !f+^o]F'W?3?X['f?MNɬܘ FNGcx|ɷn焰]qqګW#B|#h<\;b;cJ_z挭)_N[+R?J*ޚ38*:/'f8w[(Aה K48g|pn|OY/Tt%a1%-ezִd'ҲOe$: _A3C/!ؗxa2!ְwbrV잣&$fO@JaTg^Rƿ IDATʉ9kM}씮qR{8DBdbkNh0giDb(ȗ 13*|Cp+Bi&"O 8na*"OJa_TM#L۝2- l BB(Jcɪ(#Ja6 /ؼ/9}@&/F1WadpYiA )Gx05l#|5lB(Vjpo~^WnK;Wcw ޝV?w_-}i*Y) u3VnĝC{uESrXr[?,Opct5omʴndsysNl @ϨЈ UtF0YE +ږEQuW[&r F3Vl=ۺ0 [n6ud2q(YBA؛Zʭ@e<<nDzaʃhqX22︦aj)"`e`@T/9lY@jw)YEEb `dv-'MH+}a_4>$zZFCIӤVmUVoY}&Z@RzMw[؝}L?-\o1ԋs@{\Nk*kcٙ .BbRXoxlr6{طcH+~Sx!Ap{ũħ"vf#U%`r8٪9UtDՔ_%lx  !,r%@Z $$P(zh:ZwY8'6 w~x4a9alT_IύUVӻHk[⻘`@%6CnWhV{C 'krF3 SAmw&9w1g->87|*Wkd4YGV2nmWɑ?,|c$ҼwHxoL+zx"͸]q^I*s2HXMTWL;$~w@̩z_uN~${mPO"}vC+B+fȺu7!KKf3aAdU/_7MCQ:ANkMk;u&l.KYZ 6QK`PVthj1йN6q*!lW[~~TH+ި,.0µܔayGH.zú2bw >) TχH'k/8*Ywў!,d)U˖Rk[?*wGoK\V>Մ(.aNK#ՖKKֈYRO[M64A|kԬ ]]n3q-ji;cz׺CBl52-y[C\rV6"۵{_[XAT$Y\ 4 DY_j{͈TB{Zid٭"7cԱ'fpUNDShu.9'noA7Ύd9.(?5:l_U'"PlfNO>OʆJli"I]1[CU+ c{T=Z2(UC[ZFWHW؃DKP@9j/xC0Q,@@m .Ѩc1qiE+,-mbjNǰaCeStm̼) l{ڙ݉gGǾ:SCDLoD>~݉ c;`F<£kbD0aL 8b VfygLsWuP7`jqL/<&DqݏCѕ{ٱ{ 1"pDәO]Mؾ}S É~ UNlB}*buw2tszՑtSsر;YY5>y.f7~"_TfP#{O_95omd-Wy] G93j嫿A,{s?}* Iv.V1 38,ȵsFFI caŒR'y!>0D-h*-Oyg#%1\qDyaa>,ALWEXm줫k l(S^$l:\n8c1β`@yaw 1dk|@G[ж R>~4Z9 ܯ - 7{wۚrt&e^ SСkY9Uf{Fps# $>iMSqZ0Fe.CIq?fvT^}-Ky8L_&FJV"”rˬLx@O@nT+Io|G-%Gbb8ڬ"'FOF. F#~uaU튚3(F.ҝ7缅h@Ƈ%ްJw^wUM#ۺ01,@3r6TOtQwO.۫paCa慣!,KgyaJխ$L GCY`QCV-;(BcCf1]g0Lc_\% K۠ ͬ2r,2f |"F2q&~dz@U11LYf>a@`X ׇ 2/Ȩ׊36|1WHz=\^%7Bu%zZ|@ܭ:7Dj`y٭GRt* jÂݻ;*qvM Seߜ0u%F>qݬJ,tOl|zp>Hw/?DCu#yPf I,%cLKrF+  3GV[WʍW`FeU-lK੅tm(5xY|+TI .jƭ7>?KL]m{{btiiҾ#9WU\]:dȊ7C@Jv{4 w#uό&օ @hj?JWNNMl|RWFגvнqa:{4檵}:P0]@Obx<Ąd^ݵW"Z(up$) ocn1c" êF짂1:M!ØF jfxsX9Fױf#I3<%3Uz\Cن81;C}[%~VC96z=#ejF\|5Q/qԆقNCxh6$-ܿUܜy2R(" `? {sl!T刉6Jǟ擺h`D!TJDiG鯕Nһv52t5w*wxUYU Y*&$~aJ‘o<;'Vdt$]rWGom0ɛߺ 0/5bJ>(7`?,LT=ԧ{Tc㉱z{[7Sκ?=3B{T+D~ gMԆ7ݾR`}yxى#2KXdݬfO;!1 Fxpp(y1ni_h54K ;߰$"?@p lrc(jTo19 ?!ր9WAE3N$b>zhl($ SST˘蜥rFכr3h]GmQOIԭ"JsDpv7юLuc pڊLJF70u"'d|XEfQ+;EW!,R&&Q(P^4ng,"hR;s%T9>`G¸1z{<:W|\gC%xQx:(6AMpAܜgG0H (/8wە9+s @OvSWGƘ#Ξ6ڜI {UŎ`msJdg#VT;QIo=LzK3pRo\KTc⌿8/V&r|r8UU3׌nk^9l+E MnDxv1Yr3΢Du5^\2)>^oɛB*g>0eSn1r3C#9~hl3ua*}|ĐFVT$UiA_ɠ+ Gѝ#^8}sEMsC Y-kgh.0bEэZ&ʓlw"m ;c1j9qHHe 59ii >/ˤQ@l;3? і%uSf`B48bׯ|p@9~[efl)"fJFz*BI/`}W_FE䷢dA1*5Mz>|@, (jY *b1c}!\p [SτKWP 7Tg@uGЯQ3;gxKhCso¥]Em3f<+aUCq:AO0,vOY40XZex7o_}n+x$r,O e`a$C{ŊT]x*,t#[ʥK󉝴X]tBw*d OF%I@~r _;c9q0ɳGon7C^%_Yj'^\~~vvf_9s>˖b~)s;3ӞZMVċ 0)ut"jnbDug.'ӣ{͠oͶץt]]޲F?p$i:oY٩?_uԱu)+gd_hkyc%sǓEw}}_[x>C[N3O;jw^x*ߘh?84v/w-\'G~k|ç e=3+ObLJ.pelmRd,4 _6/IsSZeM|!|cyGw"nPƀt[~8"[K]R[.60ǺXcK0g'c8S\w@^yr8{Rlp0o v|M"D6֜Ѝ'fϏIu5%6'RE*gB[nqrvft#6$o&&% &Oˌp؎q hA5$υ;uu'njݐ]9BIU8v8eb/eΪ&vAas!(&7?I~YCYOQ 0iS TxinQiv'!{ ̮Ofq=FH87&<RŨe'o.m=3۸8v(Ʀķէ~u157 }M!J{cRlQ^DT> #K7.} )B Eun^9= %4CTut~.ͬQyqb0ճrz_"ioo1l95\QS`0E@PWE9)0E\DVU{Q;f[cp + IDATE3?twf)8EлX歼_g-޴_>Wo~g?Q]+q׎]kc2%`b\ͨW|JihX&S-wh_o!/&_hZ$-$L3iC*'yunJ!FY=5*f0š^  gbN S)5"w1,adG1 a)Ϻ >ü FNS^ZF\gJFnV+SLO4 zCre |AݣaqG6o,6 mC 8*ÎV? ܯ&]*{ӿ]uV UMNIſ-rq&,};Z;-vH07;RL_ D鋁;\ 6ZޕoFWPwVO|U8M^zY*otQ ^a$wNHU6 o<6%\_-%/ % _C:`~\@h8[ń2PZt Ssf,W\v u _) &O,inejFikGfl5C0o^-Fe84ydLYܸ4;pQ ( nmGՓĈaLD~QWQYjm]BIYSUw0MYy{Ul+S:W"ڵCgSUu^n%Zo.`Z3s8C!kWk>R$ijr#*ix[}aARO7ee– jX$ Ed5Z V֛|Gr',Tٲ$. FąP9y.0Іˑvjb1 v8b gïw);cJm/?OAA~'hrÔݣĈm S ghXRiX*ObD$pDswc fAG3ܛh|߃BЁԹFꐬ0x֒ (V[MF9g'*"mDUT *Rq3~^|gnrZqOydz9>8'}aMc]SJCIT;谽ITDxl;Vvg9D@XͷQߧerhFAu) YhD=+\[dn*cGS vt[W14?S%HGo!LY6.O% UFB/&yU/NpA12)8;ro{*. I:j]h/(硈'ff0D8"Fqa?c(Q,kF+)!D S ۱W 1Hk`cN5 Eg˰ՊK(3 F }W|sv-glxmLbp1V'\?wd:,)H?vX~ S 09} :x Lm|qŏzO㣧uz)7xp7ӣ9oi# ;6E0zlSF3)9/>0ͥK;^]j zmE2Y]avC ƛwo~pF٫OI G+<nVu5>qY!gmp8$<')ʍ,&&C 3P*tۼ)͞#TVm%|qЧQΎN&dS)5I0"aT0 +yՑDbVOE助y^# 3ˉ Ji* H2L[h[8 xlI8{G9&0DajQzq"`^%Oz/\\w %`K@;d^UK f=;l{Qjcf]{nRl[ΙTHD #^'~u镣]\5P:0JC74$vܱzF7 Ƃjl;M=|a'^yOȅw˽#ȐsG|A#P+mM(FkW?^C_S օc `z*0znBkP- ݳs0f(]wod "/xWu<bpINNOf| <!0Ay :.fZ.\!q Ivj^}c,ڱ7D73!p@1X_|'o*P˗tFgs_WJ>޴.{͈?{1uku 1JӔ9G][z8Yw\]7/ ednF"f2 6t3`P`q?v0oR!;h3E E [r4~A!EWa]К> nuRl1\`LL3L-n(*IOvb!^kGwM; 3у0se`Кr'j#rN-(SZ{L=hק1c;c( #11&TRX-uU;B|=uxt}Jf6./_L'PJNQnkDઉ@0]fį?y|ti x ئt Ӕأ^Aꪦ(?>yk2 mWD'C cT$tkٹR ca:D;[|ji !Bo1b黢F ֙!yi:3Г`GY3Oa}@Ƈl5#ځ9g5ä 4?A@q-~z~Al!^T-.ľR-:;%܂ m;@׈V}u'i 5\\>)o3Q'j~hWߡ_%2hPR"3V9j72O1ܭHa 3"JPybVY4Th#FٮC_D,&ݻ^oqߛXne͵Xz/?2v_,xt5[ C}ɑd/G`ҙ~_Q x^0M_4 vx (D+^04vOq~:̉Kr|;/&Sڍo{ϯ}iTD(Rs?yp &4}ΙN@$e7/e1q7 ߝ[aJWgtpڇ?uW^ 5;QP!}k%M習F[svwv>3!vj QMN=`]Xc H`Hft3s4ⵔ \5=A 1dP70/38Qw5IךqaGTKb8]wTӳs(cF]"RWC~nν_UcP OQzЩq@X)tq\b4T@ZX& 5SJδ.Mf&k3&vh,(N\@Q3Բ <'R5\%W[mbIkNON-D1_z3pTA'N*Gc_;MV0Snfw׉/\zyYWoZǖ̷#˗kTGr?ҋ>_-b+'j.)yr 1F+ Z":yں03pByΔfķwzp3^_" ) cw3Xingf~j:P=r xvW5(FDu<3YU*5gȗeUN"g#3rXn}@ i0%&39@"#U0:ˌ},뽷Ys)]`[uQ]'m'y=IG0Փ/"jrS#{w(_q4%!9>W'Z0(^1:a^!ɯFaf43Zj?-.NV:8 Ve~0~HG_0%B56A- Y!Ę`rT0_-Pn ll Џw0%Fe{T$T @ԃDj+PzMXi+DjSV]4tLION:-QӒ'NLlA iZ9Ô:[-`ͩP;H&iѰK`0Lݙ\Iwim- Kar&&4cÛ"nƶ׿u9]tP~̭MUi36j1օX45 3?]Ys*-!|Z›GS_{zHwW;4Z“|{|\;Bwb:y!aBC_Up8E1-~Hf yr0|wUP7Cbq.ob' iSwђtp`yNp^iB@-_91)Za楉TDnr.SM `1b5#yxȗkpɀ_\h]PM%D _1!yazx%իtCH:(Ϲ+Mu|ԩp]iKn˖FnIxntt=%Qmpi*rUlpizLjw[w?g8xmlؔr(50pveі{8\JA{fs.Q<˜NM`y1@} ~Їh*3@Z@I0-]`Iê炣?z}]鬁Yo<8_.zm,9{mS8H%Ee/zЁMUT0Fcp <#̛4z`"{ x¾EQIkK{?񝛼 `ծL] ga|hjɣv`Fn#umwj>Tq<*jW0Xzex7n_&k]Yeo>NrojzbcP}KO\1H9C470k;}u3Pv>{b"yr*X(󦅁qĶsZ;^x- #,\˱rG&eSZ`Xٙŷ?١Ľy&EDu]w7} D)˲GgdX?3c5%YdI Hh}]gCVU@:ȊΌx_!(zũH;zPJVX\dW7fwO':*&*90e#Z7j=3j{`taĿ8:h=2[!A#yОCH́!qedaw1 :,H~!@0lSuɫq9˼D:@() $'67@, sY UH^2|ZTrMɱ|{H(/{٥̙Lz^HU™z8,787Go>cern+K[ҽ7=Gڈa\x釟}Ƕkq\7Sfz 4=Zm^_Ȯxl'U ͫcn6L˄s!9 o= 6:_?a{I[٦+oXu`wP,n6v67AAn``@DIvFt8 =u$ىv2ZAOh R=vѼxg= KgNzlGafnAkW1}&p  @dC݉i+&XI$%ǍȖCu7N{LqR1', B&{1~x\ @|c"q8܏* qCE;m=^'G0I6sJb`VL&yˆڽزd4=(qQPnx1^)KmKp vR=lbӉHjFқmL`TDK㹵ؓpLwBzɅb^+ Zǩ,oG[;RP-_Yu>o1kӻT8Z3s^)R'ndgՃd2y4ږ6r&K^;}2HBh` 5;Vv0y-50el];9\db|*sB04'sمƞlAUd'iAYmu닳z~{~ډ"[R_QsrbOۓy?zKĕ4G^&[b7<sk#Ewb@v$c6X6Kh|:q̬N5Ibpf% 8aV0hcvQ?L@›Br4^ J2rd OSE y$x=}m4>g pe{'\:7l\?sc3β6K7N94?BaHufk}L SZ/$uYkN')Ϧ ;f"ujׯMT[GF}RtvãS+-%ٌVFm276F4ug Ι{Av-ĢFx0{[W[ۤj̾!!QNlN,axA "o4&d0 Ia؄y8ZjFn'ӑCiZtzr֫Q5|T z,Ą54s*ә͚Is*a"G%e\nCmzյ ٤ָt" !9cVXR 5FE#gOGbۖ97{˱F; f!Ƥle9S#!R[]8 @Bďz;-;4d5f"˺cV;1A+T#Z4$;kLk]?\. L|CZD (-eT(Vf7Ku enH!Y ]r1Ic*٩!ϐ^<1uqզb sFw9 )R,[ )R/޻8לHiY7By5Xc*- qxkA^`uLaxa4 U-E7O;sZZ-5͚ E'baUg"l=JTD ٌ[_ G_J@]0nH @d q.{m'[q'-l%$~Xp Uu z&|%Ia>BЇfdz ,Ļ@݀ (n0K ;'򇄻<<V`xI\=X2҄{X{'WnyBM=.|3om,8~gj6q@tu C:#_Iv}}c'7%W~ $DY~tᕱ;-ϝ\nٛ?esٽ56N;MWJ5\n[FY 981;А#G~ubk@$8]ƭ__-k=O~3^ظ6܏m8eX(p̈́ : A!]Wjffr q@Xh},Bc ǰ@(4h>sxm؋Nӈ:\ q9%MHr_Kc"*ƣ $I'%jaslNOc3n4IzRUKz&b#Yٜ^k`Ƈ H 1}&T|Ql!0-?Ğ@qTR\!K/ {LsZd6J]дWm[y-Ln6Kbr9~%>\%z UEӏLÝ ]yJvRQsfV;L'ϧ+'c @gR[bPu=3M7ДF;s۲C T˯,:Pz-0!H5iљ|'VM/y؎VM3eL~e0"p*IIkKb[!d"[\Bg>U,W`8MN{E1 \ |"qTmoLiq*rѮޱj39ʩL.ȮݞeS;+'?{}YbZf&)j S PABz0l:x~rduuz>ucQsqgQO$dS%~6M=:a|xz>xA8O{&$yϷcyE@60H)*#"xJSuJI qBr -^ <ߏW}(-3z#X/^AMKĂjv-YX'AAokhPL{4XqL֖?q궶+-k-},`h^L3Tׂp05 ^8)i̧}p*&}X0'ԯ'4s0xJfCAěG篍m\EəhȎ=9)nʞ7, @DX]RASkԂj}!>D[fov?{9A( զ 8[MҀd`vewNY0*?i"M3쵳Q^Ovj[_}ޛw^_|o}/O<./''ť̙_nn8兤sLcрa3|=iwL׋7㺝=,H6栐!,r3A,QR `j>`ha v 8D$7ֺbeEK\ 9i?s#y'6n'O؉i;9c'h΍H CquNCoC^Zus1eFKJk5BUxTA\ i 4<"$Dg'"{M)`db*]LKG%/,aƹxvx#uBCG1y ُJמ7+]By{!9rZ/_ew{cZa KZ)hHBxm)zG{ͬxdstR`Az7VRlI AкrW=pjfScrt*sp~A;^Tl6ؾעsV,w@D5AoɃ֣7ӻuĂ@`Gr~A gfH5ӆAɻ ܌o(vȭe6rӔ+ i]+HEl՞BlB@O.9ns /NuiKǣa;֫^.x0j.xP0ƣIJ{ymȚ7d0&`s#qIQzM8_M&N*lORGuu9w "8w97w(gYMG;7 `/g 3XkAI+MwF 앺UkcB`R/; r ;ƴD;hBbf*Nx5+4?8{ZOE Rc5><:tT3KS g~ ?}rO]_h%?^ݻ06X Kڎv~]5O)|}aSM}vX F5j7{]9GcowD=> !Fp q':VgZU}'$10uo'vnI>ԛFsWo=y7NY5hҖQ?қeɱcƏU pnޞQ.!5 +Pf !n"R?7Wz4'SGB:Z]3:œ!TSS\L$(\1vv<@v"i7wV\ވۛ޹`P0BH}8;3M/B7ruD>Y+\Y/J Ld>k[gu{O D*{> +7N3^||})Y]Z(z]?VS(w.L7*c{DfƋ'/ϝp{Raf!R<>͕ocJ?|Z(Ŏo3?8?Ω >Fʂ2!xS`嘗PxYZ%e!1 p2B~ 0pz{\C ??:~x4h@",yX3IJw✙[>{ʊ(d'32@ ;qBx|uQH$w{qzp\0UʵbxvI6D(s}H0T^Hu`:V랓e.D%Q'ČPg .Vr9\mxx̍)\BS0f)(c2T]Hr)0A<uO+'w//ƹٜdv<%j Lyk":ė[ -uGOԮ_ne6]1Lӟm01qFl>IDi F8h'=pn,۞hq +'>]L2ec+w7x{>GȎGgfs+B@K UK\9YbMYHBbesޙ-Gcz^n^<mֳ}kRI9=5W>ɔ}7YsVJșLx$uR|}D=}3i iN{1đc9z_rPPJvuUܒz+F|m3_r,s$NuNT%!_ 3|a㣄ǣ}B |>o6ˆCϖn /sLUS 8jɾK-'LgVkKsbrٗ9!A7맻.H6^<08Y-}ƛIƹs{ f,">3rW(칺*]ojSnX0½g+?g>?~h^9e!KԿ϶.̸r/Z~釷>7ÕW%>/>V>zxTi^Wj޷cΥgȊM+g%m"k,Ҿfc~ǩ5z/H[@kĨ#7~+c}4( ̱J}ʾՏC% A1p,bX3)%sd8%lNTȓp>beX8k z!=+p?-0FOen6Sܧ[gùQUi;իݲqN:'bJ$~Tn57>?BW:KWٙ1 u19)X`8כ58?e ̸21٢W)[ђ;2P͎RҨzUMO}Ďn@d_筍3L| O1jǂܭݍ볞!>  'չt/ip8ABsϚ(-z2+1#10/WveI {ia'>wKTLsBpٴ/t&rkM_SڧZOzƻkrV;͘j$1 䁘2rc)o3~i aINBV9s9hU cMneW.XpߨM~+i!Ѿaט FŔϠػ7@C96K,o#cg~Ѝ.PIw ?w .Jd{٭HnḺ$FK N?oޓ+/Z$&ឭ6K}݁8wcn4+,jW$jVIW.[3ҟ4*)N|v ID J(𥇷\~:h@HʾkT+>`N+?<>wS+pW_%ԧ2lngov}s,T۶zAq˿70.ֳc;(\qt_wo'S\/nPpW/~){?]M 1U>DTm&JOW{Os)]ۨʎNZʛs|F7*=GNyZ?79o ?KCBPI3nPae +'!q(B87`!@a@FgRp&T^}A<+p2; btO >㍥sѽMYC|֟3$8>o3\N>ՕghjrK'%̸ z5c7+e\pR\kxLAJ,c] /Ƭ"9[q^ɢHk&(! d$D~Ղłx<a'viL.ϐ]`sY&מ|'Ypv*c']c޶3ŅNۖm { +p1b p5M *IWO0b1x|f΍ͩ\|c?x4ckN<<vzNt뻜s;!mRlwj346=8q5F;g'^a`$ѷ&׸\c$oڼ>,إ^mJ86̺+ \sn_/8 9ѩe@pK ^m~iG/Rɰ0mkkє|:`0I1 [\C G7ǯt+g~g[,G=@)K?b%hvNa jyoaF߳ogK?q5; ^zxs.BK1 $'gy::=΃ k_=reЋE< E`n}z U"@u{(0L5T۹ϾqxX$dx-7~wR?+SOv0g7>KN6d׿W}V,$p 3LAKUμĈ#5g߼{kjPVeA`pD ܿsІR[p_x r^Z#D; Y@PqY3 zm#w8BT1!~L5qQ-Ʒ`þN*)e\kn&m "U/ͥۃ> ժ=$@Ty>gcv{1GБ^\V;PnIHI9s6v) !@{!*q!{~d,+*X`h̆1̔qk*z\6@ bhػWǘr ` vC5kﹼ_p#Xí'BG*qHto#(pZr cSs"@jc&[\t˳ģGQc񡀾PHח'?d KIĉTĨ4n3ҙك';R6iqL9?L1!gODo sֹEyuav?>YST3$ȩqhI6r}o*ڢ ǜN! ;` EX#7!.<a%L|fz:ƲA徰~d 6onpDx,?*͔~,##hYnN@?32K%842+c}MJX 3^`/lLխ=+:ZpyB-Nݏw^b4|kɝ;O6^= BNyn=;|0uRZLOpUNqWb2`gqn=;c8 zj1]<=髯Dz* {\/]c ZP^Y>$̪,L Zh%KyVpBZ/[ktS|y6m&t_ǽ[lw.XȞwo} U2QsHz_E`;"!߯6~(L 1,$"8WLpMd{%k )OS9&ĊzcX'-\[O%8#J0 n<ݙ\LWv<2F]/w' Rd9DN$IԷ%EB^Ln$wӚ9IÔ:l‚ B !ɥzB2"D 2Œ|q*D~H zAZ5'&3n|7M3bڱ8B#*sre2q:Օ wVqCWeٴ{8TJG@Y,Nrȓm&D̼~P6's';LyT{ai/jwv͛gBG@9@@ֱSLx}eʌ.A L@[1eso) VC-(c NWY$=З9qD@@90'L OuRhD MSrpDرnq+1 ~'PRnr2p]]+ͼ5<%KF`9ƭOW=?8~cVx: Ve#ͮ?>s nסM}0cM`h}Y)ΨNTdW5d[@\?v(V˾ nB mWHՍQoBp DvUOU#VaD};lp !}\sf~yOĹ8bW._~R^| ̤2SƗo5S+ٔאg*?Uɣ9ߩe?,Cn`"O# $RNA?%K\;)ӈu3GMHb=>G.2&+UO`ɋΩ@rļ^_[w#u-Xrz`@MvfNK>GׂBRnt'BޏD{q@oap;ƩFQ8"ӐZ?7!N+b& rmUlH۠ *di r\?Rcgם {q7]tqRX#OFN|D>J\T+ D0붓wI%q^1|b2xd}}3ۿ{K Qp 8$\πCW3U< h& 1':ٿ$NP]۞?WG5&!iDDzb׎_We!cSY8Kډcw?uǩvsj} oczMp5.]LBP*ɡZpD7,{|ⱙ 9a$ʧʻ'b=0T#ڥHϵ2Ud; l׵4MBF),ɓu``7W?1)?" 7A G;4W@t!%BIJ^[?&D Y1@1LF 0"#T'wjht g!V6aC0! I dodra``D4Ώs=J 0·cHLl졥"Sαq!IL%/k7LOri(V U*dN$ nNFޫ^H+bs?^{)9ҳy0$T3" b;=7L= F{ &>m?3vm@UI @] > 8@ O*Ƣ2 -(h=n|0~~O$]?9@ B֯)/ݸ^91f#+|d ՘U^޵ANRw68AIŒm!Ld}hM&?\_%ɹ̽u/>5X$G%Z!gT< D8lR"ˉOKv+#s#J;#P*U\i$i&Ÿ#G<(-+.? x)]Tn/voqt?EjE@E 6şg!GBbؿoBEPyi[p*hgDX!g84U@GjVj% 9"@AsS6$=\w_vY5@eMb^/qxbG6(hA?>쮢+ІC?!4ɩN[W,wLsE-b0AMqop"{$H0I> F8nxNOy+k~|CsΛBMʼn#fN:/SO\drkc] I@bĝmfkDjӷ^='zxth:0Y|_ok b>UeR"MnUgcezzyNjs}P'L;@ )^c2iz~m7j~,SwPEuōjpj)^0vn>+ G*Wꌌt~"dcd0,Hy\Ц]!VRޡ?+@mgl╵/m.vU{ Դ4  j\RdoE߹&MCI+D"'roI/ߒ]$1ƩB{;ɵUZ^YvsdڨwV@!In>^4lшZ!,,u:7sH*Ϗ}X sR{:?bZH@#RbBƩN"u+X?R> 5 Ky*|jiRRM14T fG$AP/PT#P~xVuX8vx|f@`2I{C)DVd70_ $ SS #2a2מN/xdTf5co]t s|]^|t* 7IK@"n܈:jvFÁO󟮠Qf!>4J%қ)0֩Cq;j=u{tF^%o>,e_L޳ OV_e^eRLd燙ow\,Ufw:p)Gɀh.ǒB 5$ h'ůz_R&78Υ(G)*`wYE*Gp0ɫ8 >bgڋh}[ 9JX"~}KKȈ*~O}q/xωp$!^9cDEՉ=Vt#׬LDٳ嬂T+2@UUFA'7cc N>XwOyZfߚ_5@~X~)(vfj|v$}.oyqn֧yꟙ4(ر,->g(?=L8<~ҙK!OB*{@nTΞtmԹa+Kc_ŏn0x6@.B#A@>E@9t {[}|?s_ utI"ҳ[¢ӆ G@sq;aDfۨ}~~ B}NToo?x@@mss^KZom,o >Qϋ%/k5w<++S$ >|w¢$g}I0([ $bm`\˹g W(*v5YcO+fh.I:O o 2Ym5?6'[hd4 cTvHi}Sűh, ^g$BČs-N1n,„3ʼnG%Ť4ZӱR0C._G̃55j՚ܽ6 Ռ4w|ƳK=0'䖅Bh=?VuZǒFWF糙›aE"-u8Qx]5ա^BUv"]|B!sbD3XҹbfCh,S=)`@P<4I3}7fzaL0NxjS>=_m 4I{~jJn\&8շ>d*SZ S=;PNv|loQ:TooXY1M(vPxyd7:$"9vn_~], Kۋ} [j2R}ߋ^ro~uju[VwdJS'Ƕ+ 3?=Qo\)xAk27DbL7^vͦm4  d"/ϴwH$75>$@=|.Đ7\,r@|E {es-Rq 供9cNjeJ 5Bޗ 2u.BֹsI%UH*N!)\Q"sIFOv.w&ҏ?Il,){8Dr39sj:l CP@ z餛MF*-ijlm LH4{4Z,nNj ҋ;&K#I;G+6`3 R$Hm;~ùa0v\Kؙ78v.NPU8u]bCҰ D?׏o_38Rͯ֐ $bWH+j޹£=os@=:G`"y1-04ʂzzn!]^5v^iEWMKFމ@ Jkru4OGW3طӭJ`4m{?&!4v8NTE BD2Ap6sbsbJDK(>J W>:S"78G]/0%;$ *M.dmwMPfH8h֏7{c*)at3/2I(몵‡၃I TNv@CM@FI>,tBqPEaVǟgy:!G 2lĪgi^12*NM#z.No='I3<6"f7Ń'I37=q GV?U `{>x .)*vhGƐ6 8QQO.:Avcx6pKtsIk2zX>=y'[^x0LbL麱qA ہvU_(Lj?pr:3_P P>A%?Mi$Cbhym3Vuդa?DU+{: 8=p$\O.q%Lb܀XZyeOܸV5(%Eڎ5*> pՅO۾45CDPjG0EaXrbll2 :)c/UHH,)F/fܸ[-/ߒIjJ{aIGiDKF'g3wl"kJ\&q@"4NK힓KRS.k/!H9H2(DRw$Ov 1[ \A^ꏳZy_QP*LY2m p\~*GHy׽AISy7HQ,'y,M@J?BfA>"dxU$,FjR{jF: ҇3_;}~N䃿u҉?FG4H:Pu*}1 )5n[Ă> bX skwLcgW=sbaM-~dvlw.ɆrBf<GX_xt[w>Z:{a@D'GU|߰n:sLa\g.s͍BB>JHW'zdAn?Rԇ~pM\)&K'?xpW.fy}oQJEmd?03tOf:yedv"Gux+?痂 |(Diht7_o4kAXc"D/ al"L8A4SA0 JS1>V̒bTKY%WR%WR%WҺ%S;FkGo]` hm]֍fP?Ta 05C.H$vQ`ve\\fMs|*R- i4Gr^b"AH 5 ( H(4d{$!{,SKZW͠lxešChzbJr$m/OFS$ yDZXz7" @(( bJ_i D"t6.O3UM٣A$H(N&CIo^:3Q;^hϤ݄[ofmGZѶ0 L''rpc3_&ѱu2֡ݭ0\%wiA\g]MEkf| MI$bS|&¤sGM''T zf*5cSC_h0L(djP3MSZ1)#?M#ie(b`UoM _ GT8cxgp P vugS5K()H]T_"Q85p!p9ΝF;zMXW*/׈ ?e Hv7iͺkWܭ^·8`ݣA_%hOp? aDD Tp5x?jmJ:5%E`u^x)[V\W !^zDNH.K{\ S'_Vx&^ZNzP{ >99`L5T}ktbوӍW񩗋AѨ.WH+;t«j䫛 B.#{RƣCANʩ7±l2#$k'Nny (\uhӖhzxǏ2NB*^2#QJ@vQbxvql22l9^~xf%ܯ40彻/ߨqd؍O_;3R!LhdiUcٝƅ~.T;Q_7~[:,Q!2^Lί{7q#  "PϷQyDcZ_u/r9nwekP "SĠmpB 7@pY5X#P@bC1'Kd:H= i3W'\!c ej׋iDA܂H٪_UkQKt)`/'Sj4*cq3b3( EP@/;XycS1! [YKAnvg^T;c8ħzc2.>S%PѝJH97k/juֶs+nj4SP?*z.8/ 9L"1g{qtnxw 8%T#dnsz%"RTqj&⏶)BԜd:XF,Lu1MHDmz w:>ī֗4}U|(|%"O6G [LGzKsWs] aݾIKݹ?t'iIIwBi;_*g7|'Os%y,-fBR2CC?A&If¹7`t C}H{ц8$n~-z^>P􄈡@6#TFA2N IDAT^2T&*}Oss}b;ܞ ;:$u({5 =-O'$ s /?(O>OJn'xs^rҎT/Uƃ(PLo<+ 㓯$;{0L5a !S/SߺpU߾Mm'P (0zzdf_WQq$b(?:{KdtI48zNr+?w~aǢaYaPP|{ݻH>>7DS=1m;nN_h{,-$"=kn3vSplyc#'--]> =gı֚SX>3ƴh1Tk_vJ0dr7@J p9m ̟9]!Fn- h\{K\[z E 8@Y hcCG.sbQŷoK=(80~\V{ "%?pjagu&v!PH 5!z:֞(js1$zOTK* s**$`:1nA׊7 DX'P$ C1;eV@ƑmƬa卍&S17R]7S;C@@;?CNS|`g`n  Ftx& ]ߎN!s;`GX~mQD@e^9(A AM! ipmw ? -c?!Gc$F~I7&3o~ $`?@xl.^xkȌA~ҙgF, ~ȈelGcO|aL DQpݱǶ.ǙZE3o򾡲AaQ:w:aqA'?E' Ѿ}bD'?QA~6dߩ=zBkoN:f:59~DMU:Fؔ`a5q0E[?૶ [)yG \[àSu[]9MHU!X씐 $wna?0%v@:|!ќq"%5q>8 "C˲=A:y"ҳAZtjaADk,; Dk'~BT/_!XjcԝqQp5xRn'R AU'WqAApU{#y,Z@nm1t>og2tRƑ˓LF!lRuU4 S J~T݄hr~&yPB@`󹹱񙏶B8ܸ\!"Z&q0L&{gvվ|^TKmv=:v{A~Lo/ xqcRh;k݅o3DNɭ-An1h22ߋ/,|$ٽ@ ^P@(A8Ѻ7}f0Ej}0ap@}v_/T^TzDN D'7ot{^w% p"8$eǸ" d6G!Cc~ID2%K XԼ ?P `t+Y#R%)푹s@YxPDuK?Ӝ\dN8bn w4&q)095}X(p ABTaňIDǾb*|' CW= ~0v d@,AF̰{ DN{+wzUߖ(&x2aPCeP>~|$ae[\"ƏY(J{0Z A8u;>0PVNܺhuT' @O+O0H Gp޾~~~C<+2&?',Sہ.k<;@8yѣ+G~my1ĵl9u&nLdrHv;8ewfI=? 8  G w<Ʋߒ: ID)cRԽC·b.W!0 ; 9QhF*e+Bhjqq A75.)i {\X:2^Y=&$h 0h]Lpt\ݴJ@ Zw€am.q Tˏ,@2.s^ jWzq.#3(rB`8Aߩ Ddd͝O@UMQ5$ɧOv8v#mgF_ \;4Leä<\sv@̜еϲw@Y α)&̧+~DgM'r{&q)7|C=+OO/94 ^/Lg> ~/ _-" _=p=\1 GcD }'? _'F wx>rOjTN~#$b-;·7ߞlt'nzI2r{3'He zTJ[l2C;4 p 1Hq/~!R'^9?1so q/b"pPB+>J"H5޳#Qs-4 NlX[Nt6-BvsA8fx%׎Y%'B]ke>r-XX VI.}Ojn0pyw'ٍ6>m#7ܿȑ,r{N/-phfUQXqPLco]}%}]/T65/RwL܊cϢ4ˢH57FQ3|p\LO~?Kp;VGu}"2,'0t]TN!K t=c' ]SO$jh#Jg3~rۭo_hJ mm\ɬ?{I}}a<ϳG-Ya1Q΢#)΢6s͂C P2B@ڕUzLhxa'tG>a*"CJ 1D5qA1z}%Q$.PH9siF|k5 "sfΚ b1'#V}+JM F\b$#UtD{GfOĭdbz@Ht1IS36 A(w3ƯW;BP^2$3˝ q%*2Z2]IGC̉hroP0UjN1[z;^rI>sơ_ne;Njv{ATɭ6T'Ct2mΥ 6w ~d\?V̬Ch{g{0ZϏ\vb Xd:2/ E/InV#vP1VHIUuzVw: hэ5?fw )ѳv0v̑06̀,Nm__Wտ ԜS[vtюO|*y+9}PNȬM{뺻;eEߕ=d@ 訶 G8Uf?uŰVPU#=οtRO㉋_JJs-e*HɗIKBP1!Š.OC  {Km̡!DzkgN :\ C2#R%ͽoʟ09z!94cAUxz6$0̹Gjj[,m,Ỿt ȹaYv<5h/Bٕյ@>ݶ ׵h]oC" r=5DU}?\Hf[^,X)O`(\ 3qhYI;s,x]"h+w"!̗jXxpskTg@ rT>))a8b?m.VV5nj×-;e{O}}oDS%K\mG }_jاDi0nئ:N4~ Pܻ^#0ZX'dCK7(ʌJ51h+5;ayHTwBB Jܴ& !}̇2 d FL҅q.q# 7~T96jnw*zAγ˵ډBX#ƒOALI0v,m`Xmzfj Fe6?'ܘ x޵Փɬ1G8??KTG;=]W]{tajTDs@w4Ƌs$usmj&,A$H$gJQvBEMPb"f%lΐr AaڠѶږ}>s/3:QQ^f]swH \~̏6wl`}utvkq_6f\ia` c2Y7W&vsGc^R.0fοߓh{ k|o5"Z<dMO +?`%m±χamfلl?uywS *$k8` x @ٙ;d&ҏьw玷/t'nķV3n$W.*,q\]EIE"J[h+۬]ƳwOVtfi+*R@(2YwZl96{_t{A߭61p+Kۛ/N̺U/zLi?g(0NƀHsTb;NTx1Fba"nj-c0aw<>#YPG1cr2Hٚ 9'e0{qO9{Dʧ&g~e$:ˤ4>ؖ$ܝuJF2xjiOݭjteE&O.v@ ^\~Ju?" e G<؝zyO" B2(s+z# (#\ ǟBRލDJej@5#4-.{#w:gA˞Fᓻ2Zq=+3Y|h@zVŅ͂ϲ@e^4r[0Yi`$[}( "vM&͌bN3[QsUx)VFC dz3FK5 y8-a&`|~Y T(].M>k@33ɹ"zۯwSʰK%^ҷa-F4m0-/ؽ$GuoF$Frnj'UnsniA ;뇟*&, ;~7pbמC/! ނn ɸ7sBJ7O m=<ĩf²?6lG IDAT}^:qO92$2՚s2 &#Wn\N!ju{w_8oC$\Sƙi ҉iiWB!$~o4w'e8O;(_%#K"1@ @| BssZla,}(úebNYi!)B7l]tJ(4Z"H3% ؜SN-N-߱Z5@DO6L?gv:U#kumX-ByPV&J,v~p}ɨP9F*Qrx殈ճc{P=7="]ol}vF `BEbD&'ƜG R6#×,Y=遴0v&3om6Ҟҩ Y(Ø9U2h1#@v$oxfZs? )gF<2Ã].=b9J+;O#Qcy3~1{pnb¼ ۥ5Z )`:ӍOo:2^=^~1"-cj<ʚxAZ!jfШ D!k@T'Wf気6?WI1IH`)+XWSuυ5Ua=(WhQ,/Ш1[}ok7CgYzaw#%%Xx N泚g$Y:?:W(BC)Բ Tk_ 畑?햿~-|=g }-t45/u $+"ˡS;4+PrRm-Y/1 {8bB?]z'I/VZ@-ݝ`SWAr|p̿*g6&?lo Q?Jw7V"#dswy^IoPZ}X8n7 ܘ2/I>r}mq)L7&&A;2Sf8u mszuхn7^}G۝3ҧd,t-?ᄎ y8^!,sq_goL.ұH cV/+?y(Yd?#[xp[='%/J:hc$NFD1QDNA &ڼhk&ոNz0آ tF QطPD{KTF ;lZA;\v9',0{{RUTS^ J+OտϹk_D (nYTo+"I޺d?m~ȴ!joOv @~e.w@՛\ݎuQ?|Fߩ\sf7Sj$}wp![+ObDrk<胴Cm]Z״W ߶pid2;S!LE(.OyC:](['3"fmIi d*DpɽyafftkfjvV„'NyXC2 fau3^lw UHp$Ƽ1o\4; <\7H&6v?[ Jת1+ۛڨ4"ń[T{g bɹ*r秧.mޯG\u8㍹N ȸ{0|͗|*Ta´̈́Z\n5OؚK7ۨu _j}@N&]d^9*N%{TZs\BAAfcX!!{ .*Ӵw~:3[]qȔ._ڬXtk.3\[cه;DZ8F!7+G\7[H]G!bϢ6eH5/uWRpd~&чN(臿^8L;J'5nmJ#1n=J I׿o0dp@B:?Ǵ?W uo {|RT?~P{ цӢQB{/b#(Oqz Xg}4n0ZK)L; º~l% :Q -`NbC cѾ86 tuw7{@ZE&S[pDZ^#C頗`0S?0ZL\2d:Rz%پ/ˆsE"4|hgZԋO&Q%vd\HB`ڙXT8|gUvWVez;A @W8\2 vxDDrByeP9a91v/,ut &&\byut T|xk-w4g'oպW>Q͠_Bs!~hA#Yn:'45c+{GD@ab9awXK{šLt3MQ>k0\ C4!j\aJ*CC4O18 }4¶4Ӛ;\#Z~jAWl j~F:u!,1R4tۗԳ?&DHz{;h "e;V^eeqَ>HLMU~6 +! /qa@!Aw ֧;='XoK?ZK=˗{Dg=NhlͿ2z{SOϮ 0[i8vaZxR }" C>u{0 F=s3nGL#bH N_8zҷ>y<~DD#ߩ'2G(zMN?ږёб^IZg^zjU&S2tJؖD%XoKid54w2 ik Ǐ;YB/_^vkn}bqq}rvkk.Wv߸޽q Q=ÃC5\Q[=5$`4I[? zD=XGZಓm#V=g0ՓF { Qf8yv DvHhT&^B$^ ܉ 6BwGΞd;eÙ!VyL=o5chkL*Ƞ~Fqwt}~JF\אp|`qͰJ"iݥXr!R w=@Y~zNەDÅLUno4xiaL3T&SǿqIӳ+*9}up$9E.R=:]N%4 Fblj6Yg ` QIcb:[q2Ff/dJ7Qqd3hn#"1zx 5"]0zSڴd ص2k=~EiIHo=ƈyYta[4bjvh,8sw1{֗O{aZ/ͭ^w+C1c ځcvc˗"`;e0|\'_.]v;?g!i+ {f?ĭ7-]^_k\PHRѸ j)4C@pzn:%-L KƤe>ct4CeqL3&-#Y鄶a=?tGw$6wo ,!Β@@R`M#C1C9@UmQx@,@m)2ӆG7'ZlE|#0Ƨ YTmoݸ%cqLڦ9[]eB*j67WbbˍSS0vei'zv|:hd hM /Ʋ D ,Mm Fő32F3=99ЙTZY\/X*3kDC ͙0aAmfS-*<]]xµO1맧sWj9{ Q@3Jk\;~뱽cqgŔ -i[Y e$MwT '0t  iC5Or= U Q+~Pv%z!pS)xy<++|`$e{S/wC+ɗ6Ʋ|=,mNXw7bD"hW<s/$[\I3*Kr75}צ7&ۻ\˜vf*W]7>ufiHjfz;=m g_N-DKI2`Eˌv7#X#?5aqw]s(t9L#KחO?q*vS~pKh5LZޫ9"\LɤӵU>tQ;Ζk#,Mqf}dYQLBI"~!@QXSd/M/} O\9qFGc#ozDD/@İ}2Lڹ0a"f) Yw+^fA"nOL]yB1soK3V}pzԽMkD.ɚ6Aq&oW5vw5p}ICwqu[?o&P5RMP'cCMtOit{w$PsEX㦨`J$[ow?p˶_̗gu:ո(~W)742#2w/mFC1!<ӫ ;mqp~}}|p#:`&iO^  KY0s_Rw),;WYU/ ) KV# IDAT%?i#T}޽^t?7@4Aows&?( } {B;cֿ!&[HAhʓYQ= !#lN#s{"n̰C'Q~lmcOu|Vi vg8ܔLC/@1wξjr% B9af'ouRLmwƎAVy\(?<ܣ3O+N iP/̀D y/ΥJ _S/k@<@V.3MS+5wouܱSo Y =i ``BirᘨuA{WTg/܎OK̚U B;Re=]&Y(|^S  r|ʮE$mɩֱܭmd̴s7w/I: 0 ~֎#3J'Pkp'+0_n0ݙd7zêCAP @P>7iH`0wcV 0aʕSRܡ5ᚍ tLA-YK4<~[|jPXXWZzcf˶LjfJfe{^ݓ @?N3}?%GYVDZ6DSX1;Kchgڛsifɯ|o8@50ʈx@hd5&Nm$KG.|/WϷMI Ct:7%|F WUYդT:b ͌J=EުR#b#FIȩWe;_tDԈ(ʈI#))#.|=&(Q?Ԧ־6vڹӹH0*> HҵS77>7&ֈ0w}ojD>rS_YY撚^LlKv(0?ZBXz Qf7ވ!hW6+c0Hh 欗p)^Ͼtn:+M :t}yͣPm`v4W"4KFHKIL >ft^*>I@RM>bQL?Yɢ9ׇkn]#OWJyϵOF:&-dq?a d @LY2w̤U]b~}A+mĸ?y6&y男xҠ9Oڴ ޮjG$!&i^(VbfiH@6'f#A:VLb֟^viqj2w{NJnu.~1VC )'\H`;N\}f!TF1w=֛"nUôcZPL*TPWE0}!\fDQsif秅kCnCه{#rʹٱ+ \Kq$"b<1zPr@yY3svyjkCcvҷmڻI!ޯo#5F_33*b^+3޵mH:if_1K0Ŏ!j:fe*!Juor+Oc{Ŵ&R=j]bC.' ݎg.L~ jƵЙ@Ct@g*'Q^4j>P-r4\S#E8du"6fs>dooi!UOR"餞WV jPA27ڞz Ѷ6ju=~)nK1D=4nrwI?z$'̸0lCz@n|ηB;@'󟔆<"6%GxwsKae%k71G..9{uTnyN\Ld:mynB82K9%&4y$s,z޽ӧO_Hȕʀ=\=x4^I'm/] gJJ[qFJP4_gWM:|8vkKaKk^TVu:@&8޼aUZ1 1=aVnJc4dÃPZNP, QEMsDr woO$b@:6ox_nG"Ă7^D{q*cvP*hZ\.LF?I 7J+'.zŘS>1h.xfX=7[ 4g/TnD,v('+o0^٘)KXzpMlr^Z `BTA6gu ,04N'-X=ARi) Z(!٪V?aH\ lS+ǭdm _|gӏy1D͏S_<"(6^x~T]C6u]K7%Ck&yQf9"h{S,ObsAPkc:4rP`R˝3ꏇ~? iS4'7FėZ0p;0sY\ Ad!{1q~:/~rwXCzg;ɬXo*Y w蠮Nd?kH&r#ee>J3/%`맡bͷ VǨEgC歹':_uW`OqWqmM4kWZgrV '6K/|v~b H1Hת߾w[x_P8HJdks8𽦆;ߔf[%s"aLC5r7I1r'nw37&q1|⩳~4o>tI9θ+'ƒfwWNu4 ?D)M3Wڃ&im{> 00n*iB4 .\⡈Dù7;ۛ5 e0R;i/N'oq? D2µO`RF.ENeBMtKD&,~%NOVkCr5=t͈F?׆X3p녅]AD|ĕ5"|@dcfpAPQ0;~u5L9 jx*Zf^Birbub:Ӗџ+ʔl3Imqe'7M\c8Q# 8ra1kFdjy˱--|#ũ}0 H^bgb0ԁ:QϡgEV](Me_x4rlBc=!=`AD̠ZΡUfj21He*7vvh~Ǐf&SM# y壇Z;]؝O_f7}R̋J9hMZʍcϨ-RmDd쀾@DP:[G H`i{3GN~e撥ok CacH}\i1LM#lxɍ 4|ysݟ~ *' &SOfɭ#BCXw0b vsvf4u~bFc A/ӛ׷=1M$a9`H!Gtte\DAoםt^r+9H2i4^"sc;krNp\~CX)b[=uػ7@XђZn&6Pئ^:L΅a Z/tLBΔ2BƬ fVࣇ'c!탄fv~;:7b'/35 j I{LΫ4o> DB@(Fs7fe,ݱnY;:ݵULfu-m, #f,[LDuc^}yk1*wf` Rn#R_H&VK @(UfLJi֭\Jh-bfd.!O~ݝJڵ҃ZAzr" MQTfB0_0?dAȂ4~b֩t"Ob(`{,l pxtZGƬzgPUn l\O,mH{慅t-;c6j\>h k|LB&''O&-),2`j[hF$w4&_1tW"\Q/4^?#W!Ž5eF/yQX+YT@C.{o4zE2ɍlb;:"ͤF#~Y@xcBil͠'w>}aibFz<S!BK2hA52 )QHE$vD$؂ݹkIQm$XJ'/\Q#chyD2];cL⭥+^4I8]͎X^hp UȥZa﫬EA3Mm^ҷͰԗ(OmIJ05.` v"@3l'}@~;pC!wAQzg~S4 j *oK&BgXt3)aAuj)5,sZ#թ1$!VfK(lI @+^*&Mk^%DTG~7KF:/m@kGh{]wf0ZwӢ.m Y`*B$۬ƺ`,WE @(ZI IpSxnWXGMa*A0s04册'AU4@@ڭU@>6+2̝52MF [D&8392T;)mL*mDb [M$(=7?vmRY;IHFk[1D܊/2LSه KA,COJmYS0י&B"so$us[s_*ށ$!E2%[3Ų4231cO8d(YcQHz_-rz=2 MlloTtTgwe9;ó#nJIC 莦c4z Ai73jajK bvƊ77J g~,%Y=M+BzM95tuu8u湟#0<}ČVK>_lb*չ(rӶ{7*e辋 1|aXZm x)?ѹhj+=hF*04N+w>ٺXg=^`¹ȃLL &B/6jģ5T a@tթ3^`yZ_1$%4 MgSX]e;}z"uBB;۾(º65 Fԗ m.2P>o6_'IBdB&}1~ Qۻ+{|0g{!I@`ү= )$aD) 7}`z*)Ʒ2Gwߥ?~6z&ނi(Llzz|~'֫+K{nA'@ =tf~Wqme;WXp>_c}hP~7 XNgЉlu` &)D7`.*ŨHh5VO>۱le_ ]>3cvϩLM}@$UHW\D\BAv:6ۭ,ΦM@}|6k3noywg>s"eO"K2Z3.QywC@B DG{i Dѣ*4/ "*`nˑsc}īcʊ } ݶ4c\v -g!,$Ͱ{VwHri-k,kI]k\# ub>ygh:HZlj [G'hP X Z ]4 R&Exw&>I=~pq CĒ7xt-C Lc6bƭJGz"q,=z(iPG !L##VTv:6O&obtM$-gvTmci[r[ RUk ?ԺW>TLfdn*㥿+`۩.릿? 1:o! vWoTFPSF!0ѽM4;NJt=7sw bFmc@WVoJabfgcC3\k>߽@7];;yB2B!53^93yrb~{7s;64p%zz,zU3zIپ@a@!-H9ܖ4ȓh 4}JB;LL:/@]F@aW+V7w>y7R?̬M|1]R73F$0@E0nԏDB)bwa@2xܻD|BwØ&mo2J K7K̨p\Mcr&+.|x-آ_~J<qfم헤qgxgS8 -Z =%xG)L<)D@۩'c&y+G$Fh3Ml>Fh(f3%OG-J2 IDATШ0)Dw.xTjό<75 H;HEQ z\&)۽?)2/mn 3>^atˬJg[=_앞SG ÿ6AtJҗƳO7Ceh +8T4 J ua#BY,Fl*xW<Ī/n[(¶R1 ;Wр@fsQ=`/[*C/Aq{zV/GύZzvr[4,.%$m[Yo\f^)uzZr'K/~K_ ]J3ScWQPj|7 '0r+/+hX"i\@ȶ^^2mx.ilTx=;yD7I"I΢0ƛ^*l ~ FrRnFPEohv5uCS=r;H'Pep>B] X9sew9Kg 4k W( T_+6o)qP 8Ǡ\D!"&uZ DA\LƳA~2zX/L+Ҝ' r#a:yDTdSFݛ)z*Jbdzu ΂L™.d/uon0"uW6+m6(IGҷՇg^_zƃVo<9riHR*hhO1NWQ_;=˂P<e oRJc} LYB;ӠCaDBԵvSSt]!:鴞Iͼ[Oz,Ou|? y陇sʍSۭf7rqPT=.MN΅N\[ ,,eẊZk#Ig P YF3&:B$$#Hh:'ҍc_Mތnc[/ Io&7Hq1ף ɕ?x 0#KݩpB)RN) TZ)7YHTJm KZS }x~>L(_Qŕp}n]M*R٧;˃T˨|" <󄪿T NǙB~Y 0$~e7/h[oy~PHZzs*J.52!Dŵ;4W׿!digptqE:xۿ\|rjsiڙyznR)۩zv&֭ c׾Xnz3gXvdiO 8+mu5.[nQ%Üؼ=wzQvlBd&~_9.D48Xsdxi:&*BE3˷|8ZC<zI&4&ַ缄W5z;~{,ep# ~4s3tzܛ1hmEI-uiiQjHfݕ1FNzRcһLW^C@PzmSZ th׽Ҵ5g0D_kaوEC H7z3n73[UJȽ @:GY WE&"n30$J„Y}xfʾ5*T0ccFPAA)njfOo; ILx 8ONdob*BL/EEl=$bt.mÝ+k/V(-8IsS7n1IL,6b`Um(fQN0j+bH !zWyqV+ZrmWկku7{ bvm3&Tyr~) zPm (/D AkhGǣ7"@g;/3)PFduQŌxQ1PLQ_B*\=:'Rcu8|)RRFd/ ? ^,)[hMKwI!~uK a6Fg_nִ!jKDSY\t'hvnOL@! (-hCj9c{׬OFI{leۯ13^CBߞ^Ԭ^5zc;Wo_*un,֣M̬ЏjD8n,%4tne Ejd_E҆LJώe7V ML/=pVqcr- |_0|gha6A^6:\ OLQ|ˏ'^z;6fѸǟ0SBk~sjS7g)!(WԀ,y`A}NaG-2Г)Vκ{Q?q}@C)BEeŕnFuWkP#)kwKo``fD!71;ɥUD@iZB*R$ʮ]j(7OfoT}[A.߬vI"LZ6R+Z? BҮtlq7>*U9?[B d&U1~z*sk/j  w|/Gj'\ܪ׷ ɤ;DLẅ_H8oo o~<%p'֝zCk{ޗ! Lэk)%VvTd{"TH\7GJ_/ G]FCjmEy$abo&]܉A5 C—mw0QdJH~.S+1)ŧgC}ql<Դխ2)yĀPq|\nk EI)xf5;* R7r?.dTPRv(&@) o̓`'TXy-iA/c>>da]sוsϠ[^n+nu?*? {xJ2LgSnJP,2sˤsF;ia7CޟznEt*ds }c0'0%LT'1WAT}5vֶI1m3[יOԧQ8@3OMdJ@7ܞxET~n"h7 u;c[;[VˏŖ|(4-7{]s`}J82=%5mlc "A',st}I+dSTY*w+88Ǵ ?C𡼒{}?x|"7 73#*Q@D$H:*grrC9F$3j7Э!2&6h12d&JLU("3!r";)2E2bMj  M9|<pKxܙQ8:5wfXެ4wOt:|b8ɽ0v7Oػuw<\C喟O6ONx߲@OOiN`U;F12?0ت5NL Eм{A\ E1 qlL- k |VOo:\1T:S:]ם̋ 2| f8U='8>;vfZDlt+ e ܧCp!Z{j>d2 ࡞и2lܑʋ)F:D*1Zl{_M]d0 EK8U(tu7ro2 lVe쿱U:0k.x)*w^Do;k?g$5aJrٓZJ;_&zCĐȋϘn2_,H&k@^"y'7l cέ[mtdzZ],Ñ̘?m`F<~$YE\Kþ_4򅻮D[> `,9vKghG6 ?Y5uyE? $$;x1 ,ɟA ((Ŝ( J<浑*X.,9"/PIz!v ~%`ĸ2-8 *e%nTݝIlF)~XI9{7: B 3 {\HG2Wכeob Caֹ6ONvgrBDЙ)'ieֻn&„y|<}kk^^'] $GPԙ^\&WNeH+ˈb]T9Eq~{#R8.ҩg뇒+raVV6>Oid>Wsǖq͂bL1VOsk{_6\B}|h5+q/GJ+͌W ?W~Vt *An@;͑Gv_S^:q.,Dvkl~:J>PyCb~isE2S]:n@B|5W9u U@IR\94FL~O>o'*a@6RPظ+}&(ޮU2r/ݛ=](&_kL~YwwYqaKr7unnI9r6JfhdJ3_H{]EBofw$>$6TSM8]ѷ'\Ctm-걟 veį$UܾMfn}m5 1 -5]3?~0PJs! m>>zI'q0'pNWHӤv 0@$`cڎ &Ts4y[/̼{I2Fp~u4)d^ϼ OhkɘhtA)a6*JYG9;k^ ?*3(9}X@ =ON`"1&S#"5b/_BȘ`lR ׵va[X r ԛmP 0_j\H\nfԻ'2H鼻0 KwDܶwk#V)Ck13 )y&)q-DC\zbJD@t3!hmG ADi.BG ek] ɼ@/5 - a[ŝrj/h[9>`XtdȼXf ~6)t'.ޑG$Od+'Ư^<)t13 VqWEcZ3dfuF +qDӓaV %d PbHyQ?G1 J!#Bߖք{ILp;#PC3{7&dba'L_+á}"?:2чƶ^F N|X}&zrTW&T_̣vkvP(4}ġ+l.{1eayJ}_}>f`H{ˡ&zkRHTHy5,#wM@S/@FܐE=V[G9`$T` L7 3Ҏ=a:[mwvFs\P}jQs'o @[gR7YJl9ڷw։nTK Ej?w3c#wN\ۧΏԯkwɛ'~uaOf߬dKc4ZF$b3N.\v@Z_Af 0ֻV /jᗲ#}{l pB 4 ɜm+3%l#Q`嵰 )fU?-]b u.Q:2L$+;No\"v|l{ժV":G{a^Gnf0Yq{Bt))<AI_Da띹Bbd[J;s0 `L븝1 2=J"06GACb8&5LB:!WJ4OiE(+^Y[v&ǠM%ncK(?ss-9ZB== d! 5H\Wvow=hOPw: R]\:ݯ&~7W;F*âB@F4 B'"4щuvv{$0ҵ-ąDtr}ѭɤ8|Njg ZA1}ͷ(UaH;$;jŅ>p^{{L56N `(eX^M+YF嚄>>A E!bTA<&7ʋHRn)WPdDSMItkwDAq5d~yToPzvgnV~o۱Φ:CF IDAT܈߸Gh8tS[ʁJ~f!0S&"ԂJƙ$7X~,y_$ = ނ@q-M=#W^pʆ>|:S݋8zWp{; 'ba{^`f7 [~;6NPؗO"ㅩ@mY @2V%B!g͍/z :Ƶ SUQK8иP[̄QX[B J Mp@QY1P*>i쮄i[($F ksw;wќ+`$^Te83*}e ")HzUn&om\bi% #QbT$%y~Օ1}jP Mo` *E@;:R!;5oB(%87KqDb'7j}Z o$>K7v{_? B] _tV>zǎE5{v 98z4ֈI7]?KAM(dkv2fϜ={We))?WFˆm|G͡C rT+Ly5bRKugZJ \vګLLI `)_#3B=_/0_~LȅI'&|*3fq,Aylzwn@2 F?F^dfao=nkem؛zcgڡ_ER8q1{mV_Ҹe$Nq=%ZoCDHoyJ$Zw=C'xeAr#Fe쭚Ζ2 ݱ/뚷˄|Hd6eJGJǟ~=#*}C}CNil,M ;/==x-(}4ZG[?;sy 9XLB7yOb_ꍘ݅k馋+')/'ewNR!ѝ[N2lB.|u07RnAĒߎٽ6a rxIEGܼ ߟ7XMxI_. 2;9iu]&cR7:{g܏;d[4>%G!zigOov~hp2GZYa# "i6ԗF  CFjqMvs}g @O (玙kKUq 0Ⅎc7qFw񨽱&Ҵ#M"B MN]CH"awg77 r'rwDeSwv !1dd^o@BpXXBsq8e/8G'=@q&򩠐6nP*XZ3FSg:(=46R$`zU7D~̟Ry/V͆|?x4]< {a(O"%}jgnwz)kfNb"k7v&~}T-cx6N7Bّ2N-)Za]YU $]!Csy>G P8$CayPE ݵ}ӽ#]ynDF(nJfAfg ]/1 D.bAj~MĴoBC$: Tzó17.B3۞= M\x<_Q>r־Lj 56>owT?[׏CU]qI-/0*v+?Zxk;CBd"9h5~#:WEvX'q65n77$ggnnf6]+?ªGq~ן.ye; >vb.*"Wp=ȝ TM &䀜!rb J(P(V6s 7_Hd0`(3aqRuͽ=bҨ7f$ofgRۛLܼAHQuld&r%`(g3kk8twx2ym Իw# C0K"0 @st(7RMC$qΏƭk6b0U;[2xXHjƲ J=on`ZǾ[_|Dj ٝImCRγpӜ;܁P7uz76ϏVe%'B>`?FE>Y{o_߬G|'JnowqP2ADUL[=;? X`%b顙(͜bxSIU>~F^P^kvw dxDVI&6WQɨa`(\ۛ?ƛQBD/ ]E^&O3O#8?7#oŸ8 n:+(T훌 1YJQg@_7 prsd&XuUPJS1}6IчJd`\%HJ(Qe;[ĸpXn!WZېDbΚ3?WU|$1B0?djVJ 6KRI J\J$ĢX&~{~5D e2,Lnov_] egk$0iX p|D[ۑP&W=u<8^B q&L_8B" sf&S>g0~k_NL.LG^(MfTgZ,O|Ⓥq> SKl\ IweMBRhoF}X3}6ѻPT|Rbπ]Q*K7Z $Yb鏸2"ݫ[\D5#!sGWp(}W6<Ԃ^\n?#ZYykZ;Z R$DfZvᐞr'nEfBvwd\VHrֽ= ~t|zu1q^Wٳ0Ja0bZZށ{O$D`&͢|T(:f-aMz'EWWpZY!xyZzRpH@%fTVmscߒz<,Kon0w=C@1}wk/@$tN`tI0bt@:~֐Uh:ͩ37Km݈wj[Tndw .KM"쥲׀԰3w7*Ssӷ![?»"wn("GI+bCFऒM c;>CZK1? !)m܋gx3W` cA[ &JI4 p*? ~z rk8 )PmV0v"p|w)bL56މҴ#2zpeX0;`mli}5ga^<]?ʔĕe3zVy!}OLa> 2 c{_ɐ M}} I},QɑT8ooPuIC)T.:7zx3nwa8X/Z?6$(r篮]|ۆ#atj7~ ءK 'vzhhW^Y VÅMGƧԇG~^e=s3a|کkޘ}kFa}M㺮4s}Kwdb [dlwvUۮW1=Q3UjJE.˒]S$A v }{w~dhY$J:H$2{w߽;;ߗx@KM}D2vlWʢB/+2?Lզ,z я%R!UH.K|+$u^Qp`-+4p @'.[[8Q OmWY@<\@Yo+;fMmCMZ7,rchtfdx9C&*% 8 2P7e-J88" ;fEVQ0"%3HsF3)_җ+5(8GV~BwIjS/}_W @3gZ-XsEZ[eVپC☖ 檀V[ޫG9-ofN\ eEdž}u]ͪ/l]wuqw PMCpȅr"2MQ Bם<)8v%džOc#K [M/~`CgwS2qn*s׊'irͭ]8bZ ꡍwOLJi}ezd}i-67'E2FzlIZsڅiǭclԛLPc0Ny&?YX–{D/ǫ7F"N2u]_?^`;CJ~I Z''7>_C\<0eeK 5m=g1 h癮wjѵ@*[XIF2(_~B~_Ttӡk.}t%_~$rZ) %N48Il}= ( DCIdxѹ ZL, C 6 ]gӻ"gӨ[꩗.=;epֲ6ܤ\-Do^fZFE#|wq|SMk:R%;t+z]GB:ru3f\B@bz,ww {T%K 'ƢL3ŗ"#Q!A!d\ٯ6L[{J/ X,7% ^ӱ`PF%*t6+Yy@]ٓbZI]U&m{|&/]U[$&Iaho⬀!Pvd؎v:qi 8A˥ &I\}̲N1ѐ~ǀອ,~ֽ_5iݟ v3¦-TZT{)p,BP$2˝GAD"hv:&m h[,,e(@D(vȹIK˓a`[%aa46t:jrh%1ו9ڂH=ri12O N'ϧND˳² LJE!t%3hpzزм tp{L&Y^Ap d9@۞zU1[_]w8BOL^z'S jxj?ι['n*?S[#g8(J fGZ{gzOTr+,bPbcV|4*u6%w_+xB\s+D6Rϖz bqT*P)ڤ<sM>&E>>}b_rD T'TWs*DžSѱЩCZM۔A`ԚnݻN-"2ks-3wU.خJE X?wL([j6YϮ, FJӜؽ_#wwͽYp-C @R Ɩ"-l˵˳tWjyrǎٶ8TN/P or y6>eqD%PtWj`KsϷO]e孱9.m heCA&Y(lUw4<~գG<X&{#-0zeW&jɆ/~;?Z2[+HYCrjC\bˑbP"XFP쒫5~:MiL jxukey[a :7ZYlt@QLo<7U/S" eHLr;_*_u)Փ~+̻CwB- ؔ_ B8%=ZL5.v sa PX"a("9d:Z%;OY!y~ZC^vۭn9MΈH܀/_>:]3Shg#YUOu }wã9[ h͌WVf+ hzFp$;\ W:L"Vsm6(oAq5ʒmPfz= 0;kbGKbe#؊(.Ood48dwtaHʽ=|LGU?<fIsy{iƗ&nm_߶^įջә /Be?ujnsSFUQNs ({04TKE IDAT"榑9FEb,Mi@>Na=i! UCAhU{x8&BuDRPI*$0Tbv' r%/LsBXH'(}A,4EU9-' +,9T5][ s@[֚g^MKra>=V%#)e"u=ϿF%PԴJ4_^ E]Veuc _mIKQMJ.Ot6{}7Ȱ+,r;{CO+Jn:w@`Jw8u7M<\hs*9 Ե|ߗOi*d1nL+eB)=k.c6s2DX1h$36ru}+a* s!d\N}>\cgJ;,W\jY:m/NcL⻺mv2 D1HwG0u9y q#PύlOLJڱ=+P)F?)E H +B@I,ʉ)F&5EɮWCg$] @rfkNeSJA8SWap{q PndP40.&ّh[Wn\;ݔ Hmi)T&cmbIT·S9m.(;+(Q4WNUjzgՋNȿ!]SUˈQ%@!#EE$JAg@j4U\{a )o-_/Q ܍9UZSY-vRy!.50#][et +E@lZNm qL[q  8.C9Eo٪;?jxC ժ3KѢmxjي>uwb%*d8lZ7A&]!7p3@p'w~0BYf2Z۳,_iWs"A$"0j\B|VCTy' @ Ef'94/-[YFFœh Ihn$eLfn P ź@\&dy 5N\ I Ci*FT3)9up8@h#v<5y~ۃԱjb%8{=wjmD΂ޗ %Ŵk<+WG]WdæeGt\4'\t.~T{Kw}X=9 }#jH /|so(UO2b-Tfr#Jȍ8utݎf>_QKtlɺa5OžνAjg`fm{㯦F>) upQMOBaMЕ{*{=#`NIjZ9Ql)`gS -"6Z@}W;AR'@YʉƨňƩ00pӘ&LNYj2yx0P4lVP4]l6".%w(Fk0uiost&w<68<4vmEBԿHv UJs[)`:vL+P"Nq]IwM2#*S"h䯙'1[ r@O ab75̩h=L'8tz_H^NdX H[@@0u܎<+츂@dlV WY,G0*D ?)Ί.VU[@N8c -W ߷wjnO%DKo=/SKOȉ%V n7lԲƐqV,G,$)]޲ɡ[}e^4pfݖ.`ux5D] >VVQF AŁ|E#!ISaψ..H! &| ^'S {!DeaqN$y, 3Wx!B yŠ+*8H|j3ݙ Ͼ(r ۂi9}i}ҥ7 ?Ôّ\b!3;T, $#)YJ _kMٴ#mz#w=KDu宙<~yy$N#{+_>or9x |~p`&}ߞ0jnCٗ.ǫ"; ?0Wqc_|*mYty\1W$Œ/Ue2 T%jW#%ת3sMfTbnŨ%IQ7ܖŠjbP.XmX$Wjݞ+4 wpl2ŵ4aKCWRpEN$Ne&ɖŸ$.͑0 KE+ /E' /a$qV[hWoSg,Hy:]Z]n7j"@7naj.Lu:$ DE@d1BcPΊ0ᒄ<$PȈ,8 dD>88$7$ U9wBCA)Rl+'F>m ݩWG|kW,@KLrcW,S "(7y쮇s4T PwîϠ[u_Pmo:r[Fϟ 勯{^|)/! 9@2 M޲pi-;9[`F j-yWeA,Etx}.c/KhP&ԡbS^>ݏ.%OCTY 1Oh:KC[9[nK#}щ#HkB==nN٦'=%AGBdDbҟg?tW,u?#\_ѤB,e.p28 Dt壟Yv}!T~I5 n@< D`[R/ߧN V+FmFCC]@\ʤ6kLÊYHV {bmWN좖?Jl 9^ ©ڑ{8Xqi3Iw'87=u&"Vf@5̑,@o"o>ǩVbO/5Oj|S r rcUO^9hrs7k|`6ϴ W"D_5. @N|5@\O(#!CpIaa4D(#a!8*$*2w3DpƜr[;Tt-r -?i..GC+?PF[__(FƊeGFG?-9@@ Pfn;?3u>{vMoHӛW'F/wtv}\I,SO_~w-.;.<[~]ޱ01v:u2UWb6sw?}bqSP}΋Zh=\-$$}VwПy^w@`h^Y:s2/m}-˕^ߡv{p%-:$1Zssvy=]꽺p#3e1x[+.qS/]sҦZD›M%{@aq.29_r*3$,w @\f"lD*ѮpR=)JeM~>V5Rkد1 W&SK鞡hki@fZٟ9eHm5;mFQBfD_*S ($%?>eաbWJ謭^]} ]ЇUKl,D؅ml؅FuK܆9nW %NkaG n%v]*ݴr֒N|RH2( izKQ[Yڡ]d`̑":+xh3?a- 0|JW_lRahc[A lӛ6 mpn㆞SeOkd6n|ioy07x+@~T:G+i3Z,%UZ8:]T(M[~>XejI̡\P&|/ ?U‘\·g~,9f_68Ց,.ueG鋱 L@ "9e+piݟu7ztCK6C&Ȩ|f=sW˳\ *f?yȗx;xjͱS?b 141ei\Z;~J+slyoUPbSܭ˗)7#˭AϏ*ޝ?,WetdhUGS/?1j(5 k߅%x\VF8Gҙdz٥ڠ:wG^=]7p~mgsr9!1DOl|嫛.>iA@ bt>X%7`wGhp_Md&YJ,R_SO'LZk-F5K [ck,`SPb'E5Ȁf5.ӝH}In,$wrm,˟wUP@JuS`br^ӲlO#\MeUJ@hn.#d5 ncyndP0.ɮ %fa%7H(*!lFB G\pM:J֧ƼznORi#fő=Ԫ!P3۷*::TQ8֛eHF92r5u;P2 ֈ"oPrl#b!56I3jyQn 8x @4_fݖ lSȈّqf04sG"m;3Y/\jC1٩Rܸ)4%-]7at$dZofK[¥eD ֥USͬxS;"9-NC=L!@((vgɅ |W0ٞ"v G#g.lK?-n|WZ4R|ٮqK.j4\F{iґg;:y_)"ksdzv=Ʌ4 p{ǑW~Ӓml0v朧R-8m:q}bQA8v4_{!RhT٭=}Jgq],3g^pǦ[^{!{@Sd}gne -Pu/mTOے _\G,w?ܓ~Ӽb)w->/ ч"gS=LYk IDATdW2V.*DEAP ϿnhQ@ \<]AWdۢCV: `:˗\{n?\% ;e溗8%櫄fFQ_n1"73bSK.KnE9b7ZlPYLtd5;kX(V wǮ#99" i-(y{쥍Hޠ^p$ubG/D m?PYtrjV5[w/ʹml]tu燯1xu[撽`hÅS36&/ص4<ܕZ(%nټ%FFW-U9Gë_JךbGt7zBZCw|*}Gፇ&eDzGѭgFv/{'<2-Vsu)\u.]s}\:75S F3W~m^nHK饇t48wSws0Y$'jeeVJƑٿX:0*]W#9@qg+gQ8p5 jp?T9鞯ų?4@W̒jsS4 ykg w_)s䀿<\U}N\ف^+@"/]F71yZ0AuGmۓ-hFip\&Krzɴ6\#zSB)|pvרUj`XFs1j`#(ɷs &XCYI pw,|80Y!y%v\T㪰116 $)Z2d6a-a,adW-]Y--Dcnms*;FvjV$ǦŨT\*0Inz<%@l"h"4{0|A^[C'$k\7;Q;nbϷeֵ MOtkF{Md,tW#ZZx}5"\OYW'Me,d-g~9+˥//<e@ mwAPZ 'S[#ӿK?lK@–K]<.- kc?Qh2IOp7&P '{֍~$fx>'$f;[>me&#ݽW|RgC$ɻCJ,|x+x8*P0}Kʿ_=#T +Xzu_8=c±ᅫB4Cޡ3wAQ_~v?s][4=я{fև[Zķ}*Wk'|wmʛP<\_xso䄛S!Pp*aq\tc35 ѹ#OkzK A`c_z2X)t~F1SoͶ4KNV4T6jpl( ״ۿ6OPhBCUj>l} v[ 2@{"m(+TNn,3xZVjyb;Bv #S0:&tvj]87%~Wi5rx'tz˪/l1V*]cz['(@ԧQ Q@v@ HP?NV.pxhT&5wS*WyMڙ%ŨL:DcfhԳt! pQUJ@0!+T\:g.r\F.PZOqZ:a6riz LMP!VV#&w1%5-$Y]KBNf-7 }o<~}eݗ q2>0>/_nP#+>7}̩P>1҈|Vi>L O0!ݻ3d78 $@޽Å >-;/ r50 p#f;}򆏎]~8t{#2|uWWU{@@ADJ1;o$ ޼y;s҈hAO$}u+_i#P CI$xOF#ͨȈ/n|8j5HdHQkx+GQ_1;kwD *KC"EmJE1SKV`_\BU_ܱnSU$qh .] GgĒɍOmy03C41#?n^[^E]޼<j?8Og~](f(+qQdtԈLMgV5\)N:>P/kFV)ř`0"*W2m-ڟqQB*E3$ɼȒa8ȅ/Q|;3}H)Mψ\|tB=?;XV,Dɴ9c@…JfA5LpssW\;z׵v~`hbi~xi|~ol ɁpU+ ;30` A !d;q|HWTI HBgH-]ʍvVtSd$,?aS$3S\5gK L kE(e K1. K@-eSRlJ-L˯-@Y` rS h>5nةC$D'. ww+͊JTwM#^w8o]w/VwiC(z7~VjLpD&^Gi˳<`E:e~ckk HloH$H󃂩mwwnPN4;KښnkѺ7"/vλN,cf"PP T[/*”*5TU+{vzߍKwMoo ds=A j{T\eXTa7SFN0A@6{['~$wxC]O/@+u7qx&^QoT(-|)W|d?!EHG7;4,ĴS-SR+{{V\i\Q$K2(6 }xr/Q}="ygR|#@ESWN& |wqi")HR U+SLI@&B!IWѹ]pHf*s)`T6 1fڢ M._^{Jj/?|x-[_(Ft̢*y-sSchᖁmT7wUb|ԩړZp/i ɚG7m"@|e$Q-g rᕱe1"ϟDgasTWP R58Ze~ƇS@@BbxQ҉ŒQ@A34@ɵ+gR$K4O čp6iH4ݦx]¥XYϠU8?ՒuT?,Fţ!V \I" , Sr` -aG1haffԬ֓{1 J?G]!qH,B]̵% 7B5pܙc*$:N}zH-e 9qU*2Q@fFۺ"D@`T MΣ^M*JX)<=] v9/\JBi\yE5o_7P&u viPkl-WP$R?W`bՔioi$=$rMoNF 5Bdz>4KJL#T] 7Z5q?l.f0'yg{L]E&Zk'˅cɥvxJ%.nS?pbj P-n}~8$ 8:fٗ8f5ܒ[~"ȉXÙxgޜ KfZfXYԾÙPs{iܖzh`6yБW0|U" o;{08EL J@{b5?׏֤eK.E#㖮Ǘ 6{+@X^]/vsĶM*om7&K3g^9 9%ouVg`f,OTlCSLZUs >lL o)*k%JS5jhN |Xy$TjVWLo\RDN(V ƅ(qu !"z|-ccUwB꾊ϯt~(hZOy S<՜&;huj9NiP@lR) //K"E)U?Qs6uYrYb3 OL,%q+_N&&I;  }=GzJ" K9-`Bj8hy)V0 d\:q #ЊBhRuwZ&Nf_N DWG+}G۾]&>Ӛp!"d^dVyΤ焌 +YH26ZW {UT\c}cr!('{:_bxágT)hx+-w՞a;#Όm\nΉ]|.$&FZW}6 ɹ_[]>ݛ7_ Okt=" `tun6pAe"te|t4|?*#n\U-':K1-1 ;ݾ_pB{Q<^۫MW'Fy.\_)p.611<5RY@nΜ= G7[_X{RprT-iW1:Ų!GW)Lb笾.[RQdT; ~.bOjd㪛V3S;X32@$+v)n>YDU/@('U;-IZ7\%phNBN3Ŋ]Vas [*@:VƺN(%#is9 i{ߚ+F-7XNT5_[rG!nQEb@n_ZifY)íLEȘ͠)`'U-VRB 8Q2}1P07$ fޡ !`9*$ R=<Fv43 R μ4XȄ bƌF :RM3vtRc+7\\]Y.kF/ /%՚eSkHWMY0P,9)IEzMco'S|L|Imj+j_ֹe ,߿Yq BRTK Z$gL9'?N s!pc@X\-۟gR/Q"_ﳩXm 3_yNXn[#hS\Zݝ ]׻f;,w"5>ۈ!1 U\[ @mk2p\m诏1:JNH4G_zE)F2%gSsڮDWCթ$?75csǘ4h14XpO %22Fn zf~Ycn՞bD0i'RU2a"5f' IDAT#`:? @* Uc @e2~{ħ>GebJ:L"[)GRBY;goaXǛO`$@g&,M"hW}9+{RSUW(e*j^rn~@:%}i4ydn=(,<ӣE i42 ]ha; 3ʡuvl'u[y)ϕ>`믲In%I@fcdf5#&UݗZ@AF! q T-Zrԥv?++kNR}s-3CRf{ް(qNtK"/G 3BZl*vfGn|~51Q5fʼB=W'w#AFN ɮۺAK Y%^\pW 4,LjV7"fw5-Xt=ն@"0U\HN:8&4؟e#񱆦?| D)Cى^{z*Vݶ_1M\#<8^к5<,2љXHk˒;fX۱cS R$)ܴeeѱ1R0h=~$7@.s%1ꙿn™ibd+"$v* Z-XhvEl`*@)<>gHΧ'ԧc7??{ Bݢ{g"hƿMH'%Sm-1֪4;Qi&MB|x hjFȅAEi`fk@qr5O&]bJQnyY"#UCRnW UJ'9zzJT?,H@LM^P P]#Q#QQ=?NԷHy]e5kKϵOh+@d}SQ`D "Wדw19W> hG{ɶ0lߴlW ]|7F=.}~ee.21} W{p"gEr|̖Lъ&sLtCs( !Fs ?n|I7,~PlܘH%1'Dg=Ë]x51=\B2a`k'n>X9熵ACoOG??{[ T?{._L^*2!-=nیBtTr1[ w]Bn{EEy@O1?t >#"FS>۸6OHRFrR1nkZK HGB뺚r|vxb՟LfY{|}k6i@.\ũh?Ʊ~sJɉo[{U'ABМMPT{ֿO~bd&?6_q[7\߫Վ>Jl{.e~=+vE޾/?kzRDu(R#K2@fG[{! , 0hS;'%H3\_qPJ`Ġ De4<ӱKç ڪ&,rlGܻz3lȍN^B06gϯee2!Ք?|ݧ[5@\Me߼%!c`2QYDn̒(]o~U.MWQ l=vP"Fs[nJ -.aVF+T G[־7~W2JAyt:Vx욷|[4'(A?&1"=F!TS]y1n R='lrfRI\EIu2s;|`Vg?6?58/B d QǙ4 o *ӊi(uK =d||(vGrJ$HP)+)n~XmUcc7E9+9]f\h溊CRp#斕M̑\!d3%Ir+4"=}JW .8P֮|mw0@aɬ>i@XkLq-^P @Td B ,"rb>@P.`iʬ]N1$ LudDj{8]3WbQFl_č7+c}JqW9վI ;R؞rkr+ߗVvqbBCrd3 lM1x:$UBQ,kjG^h~E8#F}!\!$Jpbm-"ʤMYq(<Ѳ/n$@ʮFE޽?bfSsʳ}k6,8mo?3^ݴg%&aTܶ5y`󮅋u3Z>Mqyw STFO=x .K!e>\E IU7, $'V^o[:"AI_(|]ȇ\;sO{rg %wI`sˡOtug9L%:uLC[ùS.I Y@n5ߺgw9,GpH͏cu#"9g[{rII[qRV$I 5P%TLo# 8 wx&՚rP0s&.2z8K vNz cH@**lb3ddXt)KtRXyb`jnxu_/9p2Jݩ|uC `;QWj%pj8@ sFqhdkvb@ X( $Y;K9_pӎgu0|c+o? ٷn \p賣ee>n=[ "ܲ-\ 1Aov ,nWB樮z sl'3P8dʝW~;%l2Q[9=̏%JV$P>BrpU&LqnOs(%kw9_=)>Xơf"m=k}jus\ C& \:[ѧhϣ;~2'4/?U`buGLnqeVY(p^j"!UHk.@g$8H61%:#ç%^8]ivUf=#S3V}moC,NJ1j_.֑KOXikkt3->Y0LCEaSn YtX%B)'4 2dd**:U %pʞ)NGy<)tM.h^@]\x._wybWro85~oxD@AP uG f#mX.U>ة:\F`K&ڽ ?Yp|Ȗl<8̏JR oeuv\=?|k]/]H]5#m;g?Xke{LH*5ʥ%Rl{ٞ/42_7MOGkz. jI4JRv:de"!0"Ev.Zz DUy&^9.E;^n qWurp{I%_i:Nvw`YteC͊[l`#>qPq rfw@L@&yלfMZrTVv+57m5@MrLtΥfO0a&3!rAFy ~"~`>_ I~c܏rxp_x-+N3mkx2fN2_bW M֮{(??3:x9J3>{u ]f/ y2V$47Et7_F [yq\Ź7J8{ɜl9f¶]l)$HzHc Sy \Ccp(-tF)$յY_#k:.WC?,zv7.Z|c8Gre9Ndc?RIr; `; %{j=Z0`x}-7߼.Hw~O7 LX잝$ԭ n~zKsUGg4s% H3)W+}w=8!.򓒳{1't5\R%ʁ"wޱַH!S|!yTCJj᷁ےVoGEJUc\,>LGnn@ {ʘ!5&PsvNu쾧T{U!"ɸ'~Z3z vT.f>17Mlj1fM󊁹Ws6v,u*vo@32W@m+K"0 }ҘoNM~Z\x@([1וkU|i2ΉJFq\O7%T<-a{Cr`^Ʒo$%\R/zJs?l\) Iwx sn ˾\ _rex {"ՠb-1 NoW>Siay'n-F`v":剖Uft>$zgFL"ivzgkʟKQ\sV)$!A1sO ʊN|+KW_p:8.`yWu˻,V-o}m4]|=b$r-@Ύe͇8R3|z\$}(.{.n?Kj9#ɒ" aH>^r-dD.}m\o"$G嬞O.j?X FmM'Q_i|1 [ ,U{rr︷J[~ Iq߯6X>M2T  ,_%=74 ~2Lrߊ5_&eǙOiȎ@@VM~a;5p]pp*]qCդhx.@r k/"9=:!GNViy+?A&MۤYb'|OHWdҞH_Ƅu*]etLS w@ll5 ) s f0NB~rte!H&69o>HfRKCKʮXLwO<1׈7)]^WeW\O-.v*dŏ"ov7ZG)J,;14S^,c-}-13-hMj2TxI&eGP~Pq @,L.h?fg%#f]Z`Ω [u-}l> ->:60 [g PyN !4wdx$' :!媨9t6O^xF'c|UR@2pntt,W u+qE.W/ڔ?(!#1+V⣿ѭIɀ@*N ,ٽK3=R{KHe&2ᅙW!P9kjkU=A ҬdK'>Zr"Fһ<('q=}f@ziz%gvh4S!$8+D&.ǜLOκGwsB!=tLpx۪hqdd$9MQUaBo.ew'ַkkyv)l!>X?Kc/glgAՙ;jy[أWэѱ%81@"I@D}g%RpQ(*9ZB73oU7XzeQ9yhkdHB 1m=$9ݭ?mOuTbxo*[rM_1HJ@P{ۖ5\qtoܕw,E=$9Du/wQIT +{ۗ&U'+1_iz81 LeͱVzFu B H@>IEy73D@r狏Yv_(*1*1YZp_4{*$cbQ|5z"9X56}Ӫ3 s9њ!8WӚ4ucal-:Vm@GjɅEe9{k(UkP!T5t}``>xkrayS M(nd쐫rU,oS'U#,]7ռ615qr@ā8/UݧfmtZ^;Y}2>_æ_z2ƅ5ppYȉW@~1y+0ܛLuJ5;E1j'U1(%#$!IN3͑äcxktsrpb3.*™I= H_] ?E(y?Ѷa.N̿?=oi|[rSzdHdDHcez~0&1)@"ucwnFd $ IDATfd'g\9[,]9BL6l*BB:Zbg[ūei|(~Xcb$$E⩲x @.9 ~\b =tGS|nU!\sWxsG^!׏ @yAǑT'mxp 1䛕}Fxu*1MGpvɼVA3B.ԮT탊L·JPLLL|O0s @r#h*G@!5'SYO%j0ky+02zZ5 R.Ok|^{rfQ !0^zk2.QwDb ^X&%hjxs3JS.X(& I*(F 3i%#$W( U *FۚcuEiN翞nm{nͶadT˽(\ppcc{QF6l'4qN7"f&mc7F^;G&L W졳UbDMrW LE颔H4^2C8WgЀl݋BʺfNtH2ՂX[ivo:=d ggohՑVنF8.n8_ܤy=*~'ԯkjjfCڕ.w2GƟcTIH,'Bo`ox,,]1~=ɔLZm=[Y7ŵF9*2) |pV}P =qߖ44 `ҪH\ n~Ӏn Ĉ&ȖGU%o=bc{VUNpn-e PsnLO7l\8Ƥ-M1%ښ8آ.;;Gp$ɶ7E1t"s~:S[(⌶ddnegW+fj!PuꝡGR\EfopoKV?|k 0i'ޚo->8ZhOb[usBi3L_  D" !EL]5]3'X9HIHM_m1ލRdG cBѵRHr@q`M)L[$4J/ \x+^R@˂.wY@^W\i@*qkպ,7WҒ$$aTsө#nfm.qD%B)̪%k9 QLEsg m gt[o$'$`dG2EwvWp%.Mn7$ WU-u+#TDҽRxCOHU0j;Leu|,j*! 8mwǒCf[*c P.JsNo>ӾiN le ‘{ʅC$HBvlTdbR>2݂"'1R0wW*}n'z(r'W>ffΉ0W?6Uur@1RCYM|h&_5C@H%O$WNL NFF3ǝnZ9 /؁ݽsyL]\F8ׂ.·ު߯ V]@ukg._mowm2Qzf[&A1fSJ/o2$Zѳү!m  L4!$ QkzYyckb gdْ0c]'+>ґq >ӟȽUqN'Xrc O1< ZJ̽ADwxݨ?UpG1@ѕO/%n׌dqrqyPMì+ܢR`ۗ0"|n蜿]w@+@wl9;۹VƝM;Ь6()ؚw~ꢯ:w߆}?T!9>䨞z8~foh*6T ݛګ=w,2dl\e{6~?R2d먺=o`8R Pd9 ƻ@\0JKJճ*P.8nu dO2|ibVF2@JJ@$b{6>5 !,+ 5y"dD0-x~R&LO7mkӪD-aSc V{#tNfo(Ko (W- ^#w|3?T_S\bc@@nũ1ѥPRXi*m$J׷X?Mh`Y+&'7ү Ub1yV?zpN1}\p,7Ss6dMSUT}//9} ZY \2D \s%Wo(ko*o(+H䊔[wo{.Wg}cU*fT$dL=]T޼i 4pEko;ޢҲfh@RGCA|4؝Ù? D(B Z8aUi短/X9@@"a !e*DƑ2ժoD곾lI aKxnx ve|>goIEB uϴӾOՍq'6ۍN4 8eÑCii? .=txx`Xuu\ ʏ(ädRRtu!R$OG[&>fDPd2 ŏb]o}a6o:k]ohǦ;x&Rc#%Hr큧 rIQR_|;|ŸL6[햇xn7}vq|DqW#4D qo8?uq2%ΫNAB(wbjCŤ)).k'XGc\"ӈJe#_?wU6RcbVg6RH :Dxi[!$gbz@9 P;?F~0="9],(B9jHvGYDG-  J"L=(z$m T/pakRf"R 69.$Edi7u-X\#w =xc}MHtZ4>ۓ8 +VO2;śEEi< L7x)-."`郪]bNI1.jYÉG-`ގo"7|XxRΞ y篂p}f?˼+Uv=7sv0n"o]J{7ܨkӮh2aVf&ܘw [/V>t9Ȏl_}PaWլVe`ժ]%}۾N[[fhtr9GS9=#ӈzRH% ]g\;sn )H޿OZ5`:J@M$Ooqr?Z ]ը ֬7(NcX!qs"ᖆ.{ g Vәj8|S2́ 2ngA@HoB04 \&.@֩3"LPcx~Cp9P[^P"Z07NB>gH=*:Mr?SAJ?8M-=txh#Rx6=(AIRơ:Cf"]shϦQyu:ծ/O\Tνn݊Z䬉gl$[;}pOzts?6RDlcc4{d'"ytjw;`IZX1i붭ؗ|W4[Fha⫎yX!V  ٱR$jCFj TwNs{Y[WmX{^v01N-=7ӑc/Pr8iDfͧ.o,u7ru AۥⅧ?Hӓf09Djo1ZNJ\X*}HZ4yHK 9 hB 4#脷BšCLUk&@=EpZowusʆz/Vjc݇qg a.@7B XPgz*wWI)] J(S3z0=vPwfi8n7A'=z{r[($@{~_Wzc?]%`DW>s>]yv딐HШ4 hj;@=YuyL$p2Hu;: _7jνH}vǐI)Rjk򿝻`;ovJ4۽g݀w,=ط_S:@$bű[jWW{fJV*vӂTڸ a)^9%fݹt$ZyELtݘ-r% lDG q(ȿ`-5IK^ZmO+if@a-B@DW65$IAͱ4p`9>eNU4p>Ag]y~/a'qv.޷٨jxQvSCϘ+$zڻ 4_g+  DGfzvgY)ܤ-p3ؤZL?C" LƮɽy_Tkmþ2,=]_7tc/2~AYt]I/DoogV8 =J8|^ćkf7yY{FKmF 9pTbV[hn̏ԣ:M]=Xan[ V_~$Aˆ$Z7J^5=w:gP>?PٹgÏ}x{df{f+} -U1O~}j%7=0̒i|J{κ5R;H;Ti![NrEZ3Xe`M@!`?d$ЩFׯ2U pTՇ~{ÿߘ!['.B9䭎 5>1. !@^`Rʱ\/=Up[(l͌x4[ "U9P1q=oV@q[q&*-*!03Z_9ʜ6,7#X/B`ȽdC/)V Pɫ7^ AP;!j*s9D7+FHPo5vLeFm9 ag\8yS:WќVCTpo 99 昢ȉBo}ykz^A`$xE5, W*IIM!Mp$!ED MfpUyFSkG3/k1;k:sv8Z`k>Bqδv`Xun|۵k#mHmW4GU[7\ /5Bc~VjTxJJvM~I pδ@{.7ta38h# ԭ2W Yht:Lt:釫sSF3zzD oLfpFr7;j8^|FK 7 DrE6tg%%\\bC] .%mmD/FCa ]:V@{xuokNPOyʁky+#:zXZl(rH"CIؗՌި"*7]f~JEO :W-џ&C IDAT}#9!ˊOi\G,Nd`gP|y8z*P(g~Wռ96IDMo' AJDD.!@^]>GTb2uOiZ3ӕԓO\f殶,;-ooT#R M߸~= Yt o2H03 ﹇]ot$:1uBaѯyU.v o4AYK{z]$4weհ`ABUm= F3Yk {Ԭ<f׷q{/iQaj/>-*H.#;wKCWWpW$ 7x6*|HrR#@%3jYӲ=: 쥶~#ܙҌXlRo>G?geqK̎? 8yAn<. z ; _ yR$TȆQ}9pћAH}4Tێ`dC`I[KOhzFNݡ۵?K%JG ]I'uWK'?Å#\L^:?r5J_8ڻUr*O?FGa;F"8 qYp)BT//DigvQG\nd,'ð3# '$8i8^S}qꭨ/a'vj('ؼT$@BVv  "Psw#>9d1IW]<3OR)~WS!n1!!3ɟ&Sw[lm ;iV “KL@rhéC!kD, R'BXM\„=~Hi43B bl#(;8\UJ鬒G~Dq_=شс9xt|6yL8^Ǘ=>nq"^gv&GySY>d<>3݃gD.cAtk5 ljU`nETJ*%!_- g j%~QcU!˱oFJ@$H$ =pd+u[w Ơ@ fa݁{ٹ瀸jnWR(*Tʥ$-#3@+:g% ڳ;6]H f 2B?[ JJ$ ZHt*zHJݹ])~y49- 3Qx4x`q*$~!4F3cwjV)R{]qwㅧJ O3[_H0*Kg4=y9]8 n^@\ɿj) gAq=Ts;nK2:q T.ሸ>=s%lx9>;%BrF<ri̛:IQ^OODtA_kx6Ua TxG-X=~{T'rUI_jL+ʋ23ɟK. ]3z@kTJD0=XuIe+:7=2A!(mKoHnM$ yS7{&7A) _RqZ#B7]]o-"n{d DJ ˔6lp)ۇfS  Xzx#W<\0VWl-.n?lr9n|tqc|;0 a2э/Ǯuu@2pW~0]*Lˉk<&6PT ‰_6}g/]*H4{#y)_R1v." -$}FCS̲ rrc϶}HQDOsgh+8O_W@8")>?…>T D_^CiH(׆X#v[`A$(QV~#"m|*B ^zR!>-):jtncnPrŋ\h;2?J^|þ_FHm\]73]Uν<7q Gr\}P5I!xzˡ;$e*iymߤ3r N- Z{G-*mUs8bt$YE%} [y&R?(l 3U\Qթ} S~WCշ # 2ޕDT#H7%!(MCct#KkÅ7jKn}ӥN[ >"/+ m CJȸk ϗMD{BhjJǕ5wQ'uj^ }08p6 D\E}]zKoNq$L<;tg;Y HDʩD#|"hOȍUdD%7|=mXzƩ4p۪?V~k)[i݁_9 3z25͉'%nRͮLoQDWP>K W#Mc`Yrt¤Ù^!!@(EB ծ1 *=;?;tR7xu '1={hE4xU"(Ǐ)3N][(n+%nHrPIwzB$2Fo^{Ya=\IȐ0*. ' D .3 ݆ٞXsdsd]¥vk@_vlk6P*攷j': McH8G}Ԑ$]1d6-t#o=X-)`~) ԞӬ TLJ+lUD* |M-UB!!t3ajnEۀЭ$vWiPsaf:mի!/>>7ճPxKMu|ú]I)DTٳݰYjn.YbN9-5=TTfyr76T?fi񿍕_vQi#!SC-U7 tfG{؝`wәT3=7"W8L@ TC+ys2Н+I @" ۮuw6@i֩PѲ asx3%2n{pr}G[kXϴ=2a]̩iOW|!p&O퐔No#9쾃JT( :#{PT+6"B hfT|}YћsONEl!HBA@BVdu}偯&&h Jl-[U@g8lz'J  "Un9p p}W=>TM W|졩fזVe>+3K?07#KC44oχrv(=_J\3}ؕZ2Fw~$;įTcWJOc@B- ^)n'Re,@$o#q Nlq6 4f1WxsF3G $4F䩤zf_nΗ_cbjrB >5V &V|y2ƹR< V6p-f8>K0Z3Oy # թxz'fjѯ]cCMP2́~*Е«Tڇc$n/^S&A@dDrSͽ@uk$~$֓)?%uu$3Zg f BU%ps]r䑮J]e>M[9s0gfDP+=mL<{J PÙ/us8(N0N.e䝱ҶhCTZȴPPe;WåCyjγ8(v:="9A4:D8MSHPKӍ->C_fPDAH!3b$UA$4G7 $FglkXA[<?X7Ƒ]ΊB^#A11S_<>s9)|b,=g߳Y16:Rtd +Ո}~cD)j#(zFP AYGH%٭@Zxpv+5HG VVIB%[9=#J* oI%oO=Hhsիmԃީ \2},Me$J @FoF۝>ũ.P*V>5wFoJ{9/u3;]ǿ0Ӗ1 E/j (A+g f2I!aKR豮5WCDJh=.@-*KIfHə wv:2G$6Gj "Cd ;8!<^nТڎprN}ѣS|'S`'8+!8&]Sl+>95?Vʁ?}勌vl/|oNY ZhEa)`m EP8P~IR`ˆDAHBFg\~ʑ쓒2I@B@^L25{6J VN#. CTnfwc7dPtoFLP uf:sS?Xʞn7fPfGSK'4z)K2\i#ک-=OR \ o$iZ.GFoOd!D:]*Y*vd3 )%o¢lƾLcS[^"ADt+ U-P72Hb"QN ݧt4RR} (EOd;Ik8 '~s:B+DXh-4JO7/7Eo("*%CNSyWw"WzoW"$J *EWDQ\6+~F76A.@ܾJnfM㛎EʐK t8J0QضjWtx:pK;(C' r"g]4zC'\3SM.[HMC %UI5 f;UQ",tcXh譃G  l*#T0bׂw=ތ﫾v+D FKĮ00yinNۛ]\-jlWKhB\?~(qgx&RNN $ g:1FKaԹ Ϲ|)-y% 6IJF@ڑU+/?6PH8PHB2#>p9H3^؊TsH)~FjoE..i.:H$@#sh~gwf$!ȇs-Lxr7T% )D ok4Sa fn8"շLcӝ]b>ũe?TF.hWRbm@ (=9ծZP9qov2V)R|8vKR5 >ϨmǿtȸlĚzjv-DezRީ+/y3s u_HQgTaW~ק4߅'m*mDDH$T)J0:h#b׷.XQ|t GEt%HLRE}T \vg`)~ @R|:cI\u*%a hV^?M) B,Dȭ--&]YcPp;p+ %e\ 6Cq%$Z%+!P@$M/}w ԩ45ˊ^@Hqh=~5k?]ɿ5p-,X @pņP@.;Ϋ7Qܟ߉Tk m&[!Oqk -,Du؁bA$KLhUgFW'TC'\v7 4>#XOmSy_+Y~na2G&ې7D:1W=Ao3>(V[ʳ]B(*"@*6Pz ll OHuj 0.[TD s\:#vVX o q;.؃䝌]z"a"{(?r[; ljDqj֮`]*:T:('?z`Q;YJh|`K iDz[K#73[>u{Z/DPK\ՠ+*Nڈ!Хh0*MK *HJwF:AZkEI}Y;2q߾N$}2G='xWoM$DodG{R#Sfot~",^/+_Hڽ|yo42iPN7T<냷#vKPaJ@mw@%JGK0^GT/p0fj3sš-9J҇vo{;):D:S;ŏ P t00Ks%˕Qx 3R^^@bgUH/5.p[0I3V|HmRGW_Wo+~'~J .UXeWNmpsq+, BS; DDӋ%?Jl/q~C+x4Cc88sg $ XA|:?m7Khd{:O/&l֛00xFc2?U6ũPi/Q!g$ J"Xٯ̍:}ӝtg]Tɍ|WRU5$`C]4UujA-ks#w +t.TH8Q#5˚[J3ŮRiFw^³//w\Ց}[MRˋln0 Hdș9s|gg&bvlCl66`clY[{k~hܒ[:ruݵխyUcɧ#@z6!d3@2V9ͽ&(Fv6r?J,~{®N  f{~u4!iDniP".9{2Һi NJ@e )0%Gdwl \D@"422 磱ǑEdY< 7ӔH('T9yf@qRw!JP*^~SHƙl'JSϧW+hxzrrO-*HPĤmO[wCj tH{F&U:lu.#nY\Vf^N2ekaRO>j8L.kZ YDv+ 'JJWDPzFg)aךNTwKi=߉n.bgM7Sn9^8dQHAIٕڎ׈NmSϘH-lF3H:;h/%}3k<fҋ:#l̮ qBpJ' &/<8>'=GA+(|fqW!P:ֳ/X͏ g@JdDݻrBJq%dMFU{ز/d^Z>?#OD:Z~UWIRk4/ o1iõDz򱩥#w+DjR#әL⤳蚹cSVgII$ K_e^m/bNxG(:v.ţө7vo"E(巅1QTnQJBݷҭ#ϹlM Ę1K AbfgvNbŎ||%|  b d +΍@˭7 3 7җ07}"IE|}Pe{Uk|@h~|$3OΕb7PK`u)Y囝+٥W|#[m~ֲe/(׭Hxn lzRz 73DRHvB%+)ilvv4"wN|< E4m;(*V\dz#㱇R QC` ^La.4l笷lG wOFO(U?DyW k ZZFD懊#/+=e5?%&Zr$rB֍VԳϣj^Yd:BL{9& ;`]~0r.")&۾ҼBvKکPkT%*ǫ'RŷhL.gNv>Q~t@Dxq٥WQzJJvU_Or/j^)VS敠|0ъ>@ lJ,f½v !@#zS1P#nة$@*Bk7$!(2w߫6ܵ#4>!{\~1*f吹AM 3lz#{4B=͞~}䉧jiQ; @gx$2snP0l'Uzj!v XRb.$e3Yc1Rf 6 Ü:٥fnE,"OƔ\Aՙ\nh6+E5rP,t{;%3xx'/nҋ>Q~kO'_#ffH?B%k^{&ZX@)bؽhbv^ͩǾ=BqkjawrbW6c+6 }` AG3zzwԂ\-CGn;co' m:vsP_;3{֝= ~jQkh9&mI"ntQܬAcvaaL6SOYϸ2aJ *S5EAU1Q8T̽^9n)%oOu"w$J57_HW$2:Z)ωQYUޯ( ug1=/ٱB#> `e\+۸ -n[]Ry}6d{10}}=pvw}e\8K=lS6\Wz(FfL{݀\j)rz؉̯tP7J/ hE╣nhALt|$/U IfWrK#ò/M>aLruCf]`ʟ9w3?z5GP@6W7NDs؍ ԉYB{ѝHv+5sL޽H D9%- @!&~R"6윝_3ъKxB0<$sr;_C"OɖY~yΪE7O}R.LFYE,$D _ިg~ם5N_N WTRZ S<$y)J'^xz{5i$Nc };v}@rFAueA5 5ELrF IchT'I@HhpݡɋK؊ 7W:A F0P v^P' =Y>8&bF(y<1Qqz+F4)u'22jq2/蕷x0sܾh_y1&$iOjn'Ԁ‘ϴǯ;Oֲc7ʆa J*8Fɫ zh-ɣdjf[Lzdl-/>.~J@zb`\񰓼KZ)6\P 8oY䝖F jXk9ln5mWgWAɆq'v؈ma}嶯ۭiK,ObYt'* ? FVyyl~2RQg_[w_d_xsV.h!5W2vPmNw{!2$Ԛw%׊] mYZD*'Wf'(?F#H攘W[bcvHYuw;] *_2n:f^LZO MD)i$9‰kSORQ^Z}QBvZDpz; ^R]y zuE߿nr'cүPc?" E [ɧP2D ZNgz,hӞ99V5iv~*E@ՓvHҜO&TӜ52T9NyY7sW<Ve~l~[~%tkYUOsA%cKo5g~*}9E :L8NK#$殟ӻe ԚVIgH*"?ced =r}/wu=8Fu}4/yk`+co0*&Xn7{!zY5vl9C韱 -$K="SvK= H; hgOu[.oLqg9?$\cסw|*DJh y^k_F1(B) .!0+~K%.FW[n2D14oGP jwu~h35u+ү}+倧Uzx2ρ((h_@~>˼3+ʎQ8 f^3Ux)Ő[VΞUnѨ AfJxuҢCHZS+[韇GhHrd bҢL1NΝ24&^&z? '敏񰓹G1iަUTXyocu06k2bB֋ ac1` }*wRkAmQζ4HNW3O)=:WXI C= DODLD#I ^Q+&~HzҨ͹ՓWE)J+L`Tv x[F}b2_ V/ɧ(\A^TL=ޭ:ӭ/fw ѵI($f`59˕}d A_oAk׶ЗNc Zdae"' X] 5٪4ݝ1 #|'A -AW ,-SDe Gtt(7^IȨ"@D@P 6i1{О~, YҢ|o/yK^BS~|^*  ` ң2+)\>97.>#z?`7^1ZMQ~8F$r~VR y?Ěx?vC ޘ3@XnA:NP?W̤JXJ0^TƄi`LDKw`\.nK 12oTcmQG8A`ykʿz/A.l LIG@k ߆R3T wy(|L?DJ aJ܆$- @vp zXyJ皰  i, 1̶F}4YApƶ.o@B!wwىMP5qD%Ac,~(' !`c00j"vXfqr hpOrs9=9aX@ K{Kvt6ѨR-&޲"jJ/BioYN:`X~4ei.N0(QnoAZ-Y_{ iO,KuuuqI 70ei.x؅DX,Πl6@ӵ-U 8 * 8:XWa0 `0U qt`0кj[7H} c1s^^~_W0}//6s#G 77%fE46jڡ#ǔ ҫg&w  vBԮ&iT8ofsMM c7S@oS;vYn#wճqǻ#csgOLsX,VfӖ8Z)Z4boH@yC[ʠVZ tեWm9BCL/._-q9m]M-RZpPVQ1Dq8Q#a_!i؝X~hu+;FEEENKPu1'NViCg|d@MM{]2[,?4a,N7F>pkV1 qZ\ z.=waykmaV~o-9F;u߇hz: ˷{lhR;w7&Z,B_u}1Z}t c<|JSg{|j;w~f^W P(u>FтE|#dBHPPwsuuϟhGxW_a1PQQÏHtZ1/{JιOMiV&ӯ:|ZQCLPѼ=01n:/Zs׬]Jnj>u2gkoŌ3cԦevS  ģF=HN9 _<[+5 kTzƵͿo<]XX߾6?vi}avDl©/nXT臅)ӻQ}i! fsaQc)*.?Cشy+N[>VБc⒒>^كRFǷnۡh&~xUi)6&+//_꼶||cU?!CZ̋5<*4`0@EEEQQq޽a#hݾ?W][_mܴmŷ_}2eAF#Ͻ?4rHꓭ(]={n}=Ϳ,Mf=T}ϺQbTi4t;]=m[6mظەt}-~]mNt-_7heۚ(:kAönnX6&myr҄~6:TA,q9(?СC}S|%.uKsAruu!Be\ˌDlq] /8a>%d(T*}T*]_{8Gb2ǏÂ<ω6=R *++G ^R_C.d2FG^oXZp`0 z칠^tygȟۗ:+M; rft/Ϗ ?l~ԫ> F)..}~;qOΒczCd36===|e^F3+>&`?喩]VG4J߇ڽo_NR5+FvCEWHN jÆFfl3ߣ8mJ׌FcMM$T6ixsk6)7vY ;]ǦͿ1{3\n=aͰ < ܹ{aCnR'Y/7v,yC&9tH GR :{%~V>??{ցR>md-՜&_&u^k2w٧PP(tZâ.]lsNa܆ Ź/lEK>pS'ef]V!N?x; ǝs=ꓭy~~o08:KQb`q?v,x}Fyzŋnشk4U.OM>|U==Ņ5vtsmL3^ +k_59v_kr6C"Tj߇oؤv\77!?Yd-R[YNJN+Y2 4dTR8/{<Ҝ i-c $- AYP`#o_L=3 ;v~o;<g; omY8'%\}"Ҟ=z4?šX`0lsgflQCXeSvڬp=RьG| x<5}ʈ'xqZj7 ku\Zt6XgoCp`iZb0 `]`0G* 8:XWa0 ѡpwBoCh&N`>:[ȸvEh.Zio0 b0 `0U qt`0u`0 `]`0G* 8:XWa0 pHuڻc0: `0U qth6 EZb@A~t:e4sƦՓUEUUU\RUUUXTނ`n W9jcctVmH\ .Ùʶyy eءk=l[`0G* 8:XWaM=~6omoY0LKu 1o60suƩZ qJZXU]ju˦iT~Oz^|Wkca_y6iі٩WahW%.zt# lڼB!s-l!@>z,4B@^0 ~&̦־jڨOto0/\<,*̭lJIRQx-35tUNΝkNҼX,VKS[;t۰1ӏ?h9IHuСC;PRknn}zڽ/#3b SS o1s~p^j2x8pQ$pu&foegӨCLP(yyko-NN9Hӟl =7+$$~0 /Zq)cwbhbo2f 9n7(\n 9{ AЩGz_4[̣F<ګ}4$bgf@ү23d]G\]]bƍ9z{Vg r6EOn۱KWۘDysﱭ@P 9 ?'$TitƨQ#=B 6MDPo l6;~={l݆Y9"Q#M&Ӆl[Xh@+pp>m+Gsn[عO? %WWW_Kll6JKţ&MLDүQ(ţFι~~%%*tUqI F??**`2M??(+:XUU*+4!+J>!fޕ\cEc7oKbED IO0^WSu=22b҄]x&F.>]8h`8Zg6]ni'3}WG7[DQAsb+z%P7nB<^pSY,7vTιc6oegȸ 7e'//X,>s.w;vt2'Nl6{g_ ADL&hH I왮.A RPr0[ sB̸#G+ҫbZ9zī_vuuqqqyf`Hk&ݺF r^Masq'N7wzɢ'  ŋl 9>>J)..ޭkhoٺ>M` :%bQ( e_9"ț>7ٽȃvuEG`PZVf}k6Czݝ`XT$~ڱ'eMݧhAtEQ\( ӻ|SZ,_PX%%*?`0VW+C_ Wk&)/Ap4FSTI?wY,\[1L6HZMN;K.pֳϠGMjZSPmBg޼{Eo1~ߺٰ|~M(++X,KZb4Kɔg\ у{1c޽9/--C'G=1< v//QVVol6P)pssEfk>0Ϟ{.~8×7n+?#b7lٳlٙܿkϚ7?}[5KXe_ڜхEL_~޿7_ֵռ9@^+h1cnjɹcM,8 һW޽zJKn6 Ec6[@DYL:Ρhʂ/O7nճl ;wj̲jPS#!NACvT;kThwJRO{yz hx+*|}-|Fٹ{ߺ6 zBł4Z*:.JOϐܱ1˓ /?swO&˹~~%%Ҵt? suzӧ־u}s(++CJ +]7hV|?42`0sca0bg]̲1Ϗ ?l {@.\~2P*SOڅgCxxx>9q{Fш>5l~;؞W_}_o𓄭I5$( Ԫz)?+yveS9">mr|Ǧ\;@SU~C҂W^40<~W|gsgf<5m˯K>/BԖdQ]CʵZ#uoJUXX8|xkVP z^! ,8u}@A zG7-F'X?$ <7o]teLS]垞~~Riڄy{ IDAT㬏ST0L胍XlW^Tw(++p=lܴ7^m3!4+kXҫ  m~b0QCvދ6~tp OP?r`[wgfڽgJX,F~⮮a!6 jcQhԗ; x@nGE6?@nBؓTv3v_֩jܽ~焛k]Tj䐈m;vvtC }`09 *U?w5DN;XSYYy-3h42]x:+sRܽg4@x@v*g[uRS)7 ^oX\l9g' ?ht͍ ; t"X,|'t__۷f`Sn]d]s|MM TTTh|||t+RtJFXg`Xa{USScX qOOg,..iЬ}+eXBCS>rYudu۰i3L*<,3\.{/Yh4&<~m;Ɍ1q|CcՙǤ 1+Vd2bD#Glشy/4.v 1[,QQuNE.^pæ-/-x]ruuyj%B8xWQâ9!yWj01n:P԰|X[ۦͫ~o gf]o c`Jn]?1h~oUvێ%,&+4dKu쬩r6EX3|3gϡ;)7 o/qc^cڔ82ta2wݯPP(D+/k-Zu h~=e7h`kGCRt ^My8J x@Q{N\\\zyYܯo<y1ԽЛdS #Fك^_",Og_QO qZ&pcE<ܚƐt:cwR(7Rm;)݆J޽PV_s;v80zI{eޜ籊´=A=A-E1 `]`0G1qufpvhk,Zg*1յ`pvpk,Zg*1*͙y[B[VcsƦՓ]S=M8tڈc+0 `]`0G1b0 ` `0U qt`0u`0 `]`0G* 8:uﱴb:sFT*u~oT⎐rmiqRMmB^J6Ծ i 9M.{v}yCRhD%uɶ6\ofƶ@ ;)i2PΎ C h}ie7Br(FGEGUkkM'`0žM^-ŎAV篘q ԅ 64'·تdP;:j&͌`C}Ĵ 2I|c GU8xJg9„K 7IOA#\چ0iEb٪e*+zO):]KZ 82#CA8PN5we+[GUl m9u\ICJuо2afaZTaڴ팺9o87l8¢fMW=i}o`Hku8T}nAa>l/<l )NqP~cOu9Y9]kW1l98-ސsh9/i]]6C$ X^;'' 2Wb.pp;4kfjN{4 GUa8S$%͙;w3/HD*+WΚ%CgUXwdtn.fP~J@(LNI@G:]N66Q>[m#ଶ3UZޢu$2W(L E"IP\.ϙDre Q! bTRB&P>DH4r> I(Gz,6mvk`s*\;\gen7T<mA$qj /t[rh,幹>B} FVOVi:As#\˕x2BBa 0o&XΗ˃둘 KVX'd!SsGj m/bCuQ<8mq +S*9W/ @ Ǔ vaYYAA.reͩb2/bD$#AZ][?W"x'D'S**U^?T*r~hOJ) 92T'|\,NH֊P,X[HZ3)inj8>>[6Ri,7p(5t(}SS G|؜9IIIs8)j@Ψ66rbĺѩoa= +bQ< Kre/ P|,XiR\Wdd cϗx2CH$K}4d렩)H"Tk E ;τA+T1qW"Xasybbɓ٩OisuIG/UGU OiN7[d 8oHBv"@֏RBa…o|`;x^;xlx< a,#Z> k HXb@k`TR$$$47mgpZd1+Cb'Ν$7 d$QBB<(Rj3xj&$iC@oX;Zaru4jWAZ0d!ZDlԱ"a^'(l)252Yۡbҡ@: 11 uYH"h_j.ϗSm><<D"&Se2H$ZXg'+%%THrRtΑ1He '@h>h|@Riɭnf}MI@>>20E֤ Fꡁ+aֱvˑa֬uK,%$b>?Nhި7>o7J{[jRvgf4ŕ.K >$Hy/P(~'M=" Pk !e2RHFG>H$X)ED">y2)QX2o^D"O@y$gA[\'݁|AS ˎA2ɸd=ʄd2|#H2gNR> `0a|mP K lHXϩl*W2)8>ۘ\ fɎrwe.yMu?82 NtO8>>i.&UE=ǃhɂ}\9Zc1݆tyVj/@IS'FDl Ya>Qݪ$ϼ%\W+~ 9,D: Gb$5U۾}Pe x))B@)b[Qۙǔ݉1QWN9F־} KOdW*WrP㍞L۶m͑An(lT 'g"!WhAR@Neg࿐sQlC y"D_sԽ;QnRW@j Tsakғfb2 U*>Ul] {'<ddY FJ2Kp/ϩi~^@a2JefwqP= jo˧3d{WG.MfD,N2ZO['%!34֊qBa *R+A 44`G;g*o@ @=8ڬaݺ_e2.@ IhJ&5U>1:$T!,4`hI+@@Ȱ+R$s ?MDYpr x ?31$%glTS0@L`SX FWcO&=pG,8)N;<^~e|fdr,&3{`lXRڴOX,Yd0W;h [>sfG:;F u#'K͘*}=e|\+zPНy%\MNиL"F 4kh+OQ0%> ^>vGVv{(Oo%?}Sl>$Q9c PߢeV`o^t\EW\u*y<6cu1Jj_ُN.KJRs8Ҙyk8GhBT2^XU}U|6wx\*&r!0eُ=Go gAknZc7^mj?׎^#\'+^ S䔔cSJroo@1xa#/Gq ɭwC ^^3vfEKM7,Lzyhꉉ 2xwQ nglYo,~PKW]4,zoS&\l5sXAs@xcҁJŧ?*>Id>HZ[A]Rڴu Be@f~ Sy bQ.jޮo Z3dsyy(nOQ8nH~ϧM,f 9Wf@/8`;bO+&U=JUr/sWpګ75 G4 Y՟{ 215U,qfJLLOreHMK$b:c>_.䶿uCPݻ6>bN0p= s$tϝ$%7υG*ھ]u IDATǓ={B9qq2~kAyY>]h2Uhq`|LF3UƢ|F!YZf.2`t7Vg`2X +' ёgEBcњB xo^?WUZ6&2U!͋Qhi]AI#)n.Yއ=&2k>j,`YyA=U\4J/洔89v@as(IPՍ7F퍏 ^l)a2]gkS9Ps> 8C @P[=߻¼bK=ٳ@BB|||Jcm{%6V bӦ.WF-&#n\܊u~}m!6a&mJ8]e;jZ1D˃uf/&x9hH$W^&!TI4Mz]K_r=MB V: 3A/S6Wn&@l (qVr\o1Ȍp;o xEPmoxPhVWk\cÃ"rP:+¤rܨ^L&֠,6*4'Gi^4K`: Uƒ`ۧu3W]7V}JZ$C&0R>RP7@ŅU?͑yJd== T/wv)J{u?n6ť\Jr1swW+6}KMyO.%Oi@Dah8̐E L3On[VŒ˦f `|SydyDY5bchO^Bh4AU呫_@m{-JmPV#"RLyOwoHVX,dhm&'"P~uv||LC@Barj꿁|r9EC×kِB`᝝$G~ `9$]s |lƂMjೃf`:;?}¬dZ43Y0|یo Wl.0Xtz'۩__UK.|[=kbvx=ڇv2s]֜M3Uh,FFtyra((.o7Q8,?qG$q ~2753~3néB>%Oن}ʷ34Y~zѯ8nF`Ug?o9W-F7n5b_J@e}{Ȳ@HRFڧJ16ˆHHH@?I$s7HQ0ꗒ"r+Ƶf6Z EJE$ic-pp5ZSZp@,^$)i.CQO61%Q|<@6\`e5d융kVx2Dߛ7oBȳR#]>r/BڷG i؝=.w\-:}޽ѽ sl3Qٮ+w~:7OY\v%(P:Qu+ٻ[݀>Gʺ_V}8=FqeyLz2zi^E" ³G!vA /M 0S.=^ i#Pb2S4P8P!GO|/=M2hLd\wF#b4YG "<_Dƭs_VexlrܼfPخn; gUZj`tw  ?uO!r]zF.Ow +,dm?(.UӔT&AzX0[V]-YoO;u  3ƕ|AWܟKzx*`^j8c͝&߸.;]xItX)Z`685(HN~ >>A"ŭHN^n,ŬYc.j7"'+j~0Qtf}~jVHeϞ+̟R 'OF/%(J,N%] MD" @޲V[ ʬw Ct~r^Z%ii=]'$"9T^))4v/Pr1|jf,Zid1[bhMH儱Y{C?(^Nq-UK0`"5|`ե >Мj:b4,J!hTFO knEzLEiFJg(;m;;-&3Li^#Wxĉ7k3aW}ōuZڊպ]"V!!`d"N }\?{{{^yJ̓g&=yU3^uyg}E%k[OPN ;SM;uJÂT'{K{@y\1TG6?2???U6aN'!1;9!n(8a,7U5DO.tNtW\̛PJYm#w޵EwF^z<gǼ$s‚ Yv\?(PK_P(Ln[%,077z D߾=7%4o?U7; "QDOM$&J$-oR T@~~II--~ETG& LMMDT |KRSӨL/fRA) Rʃi 1uBqEc5$ % ;:E-5*d!8\#f٨ aw\6X ktHHNQݭ-a2.4HqvNɣβzxfN* ib,I1NNiEmoiğ$؃}uƺTjJs1N+L_jn;6eh!f3:sJC SF n「$5x_ l@WT+@ :~ @к>MP_=OZfkcZ4S Y/C4?:.-p*#3BqW~xG_դczZZCa=ܼx(+bXerjxB6/SV7݊k{9X㮝 6|~}txD$)l R%))Z~W.guo?U~SMա2YŇZ}t Q(eJN,i|6wP<w:$pG?4!D<<;;))bv4o^hiv{ԾrtX3:ԇUgDĘ4%W> sQ M#%;t}Ѣ۾6W>v4'2a pUPKw|]v 8qY@nu∆MgxI?_ܨt'{qEq k$'V u`5[p"AtaN،~+z"GY8yTuKϲʕrl7i:bV5\6g4_x~K5lo}sT'~;`vK,,/MlM @^a΄F'k @(s܎̡~FNtG RyX aGzwŖv uNC,fzVDrzcx5L\+41pݒ; _:^)a˜0Lnu*E3)&-Mlwʩ(%T0+h 7%W~a1CtsF㐓}41qGtt.Z=ƈDiT|HQD02ROJMN,]Nme˛b033Je=GC=LTe. JBt\C&u @ՊA0z7LVT^wGw;2+,4J$7N9N c4&nf$ XN $lz5+o7Zuf!mك%'4P"Pt^|9NhpZ9_Ǿ$("˕:y'7[iv|Ni4f3iT7YZpIʮ4_0w]qJoPxYj_QJuv^Ϯ@܈Qb՗Tj46iVfEpW4]ZP45Q E13|Gscr0z, ]yl몚~v|u^Ĭ}iv\l"qFNʕJkPk]= i)/bFylD0Yɍj TXpfarw_! ˦xG7i0 cȼ)HY`65Wmjv?=T*I6QNYB/NJjNH/5{װkߔ|Nz{E)>1q<ZD}Wb@^ rK~~͙ɔ^BBի(!xN[~`r x! =4jԴo @<<{,/-0-==*?\/h4mqY#Ƶ^v"caZ, Cg_KͤJY4eN3V7 {sm[9/j8`La2[Fo l=gR,3ӕI8,Av8[L"DU'c]|>c M/FYP_AZLgҊV+DU+85i[no E4;>erAwq$ F\5ގJB_B-Zt.s:t;62$Nl~Wڑ c0?fCeC`Tծ.۟|Y.1QBww9 ':AsZ wGy@!㶡zC VߗLwCByCƩ=&fL˹ hs.@W^'xzؙ$+뉄1j86'OJo2;?2a~Qﯿ=\'ɉ~g]SSbp+--ϯJ>S((02QͥRŋ]7rbӴ?PF|[,W׽܃S~&C'DL;u6޶)jF'p8ζ -V_G(r 2>j57whJz{#57 mv05xȘs4[`H04%nhQ58ڥ~-|UBcD+r؁~ ?q$$~ Iab즏gx1\KGRpH P+D"G<>HҺA0Mtg <6BtZb8z!qtg4G;|M*/:#CC6tĠB4 /jhy@O̖䷙46' RkI4` 04qrA{Kݻk;_:i8d!+& 3j*4M]V~,:HnA h29靸+@0޵\ug=76 .4[\(+@Cp H8 Æ(+}'s $ݧI ZRn1(+bJ+_Rh~FeH|_FMo)@>qfoeS`Uu[:4 bDYvz7r{B 3fgRSEoy**g%3RL*u{DiX"󯆁39߳ VO)JJʤZuv"DR^D2c+HJUgd$?M;SϯMwjKO:Ԯݳ{W >U IDAT%RZwkZZyؼ)`箼ٟܰNu[tޓ7~Լa˓^amiϤ%Pg !+-=u7/G&ϯ⻘}bz!6\Bqy`g;!%A:,ݰa}C ,2=n?SdRCٌQ]U9t޴A֪j4;#]w}{eݶx~x_?їҏ! 3m8cUjLfnKQn,]gUTx4+*!ntò1!ł* ZKVݛq\Ł3^ʊ$AJ?:b-RӦ(h~wiK[i6 A/ʋѝ~f< 4 $?y0gVX f\Pc5]UYq~"3y:HНtK}SGAKIgzݱAޝUD4[ }6LG0Ұ`˴=&9"HFs iNv_+2~mѶC 9Ay5^[2DR7t2c,G1 6P;cnj@Y`l@_g)X[aϿ*I b' !'Pput/rNT|O$J{QrٻW[/B1/Ly@m\Q '/W  fO&a)!o=zJ›j{kҍ/_9YIpoj9cҕ&+t}~tdͭGvip6ƕ19UqXwYDžؔ]v*B4t' evPg>sեkNړR?$9; |`"0 IPHKi4ق+-pd+tXQl)|N2ƅ9|I_XanhimtnO/b L}Rd!D41"FZ2FWd,uڜJ5{yI.?H:P!@BMk.`ٯ IpB*`2JMu@l;Y_T& `hcڮ\cPiLeuś68-:T8A&IE۠_ˬKto7אWwff`Dl7= ?H=r1zÏwWԟӽ춬 ,G1ɠ%ZLs};.뵐 ?vNj K6Xl[dۼzuՋ+ϳY'>d[$_Z/@=xYm\{g$WIіW =l棳ͻUs=f/܎;xU [&zkm7g]se`϶GߟBs( v~Lu ׸GDPXМQkk&^4;P! 5e$A":q{#Cs^;WvPfY.Ͻ86/8Nj C|l1?n7qY^T%n0M0&JߞJ`9;?$H42H\JiÊY |_1 HnC0Bod\y37ϻŋ˸LL};0:$@I$PSѝ7lmR=%L߻<ǚ,Vf*cw *aO> ϊ"&`5w!d\B$ Gp1&ixgOKч\Z 9N9i0aǝ.bpZR1>ߜ.WJ8NUFq )4/WSD j)Û}nW#,U w#̙yR|-MMMS@%$FGDG:{.߳Q/ tǰ[`?4A>$(M$~"jTQMߐstce9=>_D~~GXqB!=WX) K'{W~i޸r%"B]Ƙ?2UoMzoDgkqx|]Gƿ3sHEF'jL7ee=;x196@Un_# _-|FNOC/Çrl7aJ@ny=RZ̊b]a =3lNRNh-㏋c>x}ՑWF$.J\yȅ_sH ͏cb gB3|=4,cU֫)~+!vR-wV&(|[BVf f629(h5!" fb!qpHV;;a-+qv`}74_%9Hss 3{xi!B(ԪFbb>dD}N~_œl/3͒jkW摪~,`5W<'ZJksHUg1(VJJ6~yf"F4-()ILX(1C~’ߔ{@{J j?'VKI LHwl`4o#??fnnZ-HMM(+(EE B@DbzoRBTob9G.lZ|>qay]Ҽ--{9?<{/v2& s']wFgcUMVr3[~ pZ ŭd}^nȘڕ/+,9lrN7w47cL6|]NZ`l?k~{fذK׏~aYC}@1?Qk,Uy2Ƥ rw/ W{~RvTrk(KN?8433iSJ~2Fz뾽mnl8(8^+]I^ks i2?K{x9_c|xIPxF+4{--4l{Lwuč3QDTT'O9saAve'uzBgҗ1JVOjNOx3"D[`,jV3pTB|GNMn9#S#x !~øld6ܖN8[Ljt퍓gͫ7~2g C2: 7*>)G&X1\,/~CpL foGwwf aO2h-'.|b|R[zw&:em]VV+">!3H+ne) 9Ƽbsi-v%ѣ${ewGBMkIJj^=vђ΀@[N@HƔwجYD(Tb )u[Jh.a1ֲWgAv'le}Y<1]훜+ѣǏhn=oHBD4Q"IJRO\࿾Jk{._9v`_?а=g,%e5 $ŏjH@gaUoȲ"(~ܝ7@ҲNIi_,5ԏRG>Yzn2ؿWmMmUQ︡%!;y/nXbj֬ oyJ;& nw/57ZqbP^`:'ȗk ޫ@UQnyA !곒1!g! uOjiv@8}@K*X|a@8Ԟ%$#2BZi ڽ\qB@>&fOe{ N֪ѩ5 + k*OBuiaFCl&Mm ܵn:-}E]%QXLT]*aɌ@)U;eKnnñ3D"QJJիo&&?_ӿ5^GRz$q˖;iُ*%'gVTy/U$D^KLD?ȼ}vZVk 4薃ʪo}rٗޙ{֗>NLi (,|CGwqEe7}\XXQW\uNHVlswߴÅ젣5@`3cέ;>cf[}~ICCY]ww|`+Wn4\(,@t%e2wd˒f3 [ tE4HbGit!oC8>6iF=n0+G!QV&tLUqF3'lᄹa4GiB  elm80[p=J_k,~P{ SH}C쵁 gMآndDd|^,TJiNY8'YbVJB$vj\{sfg&ҀB>$(axWͳt,=t+ik5\'s"> b20zq}=`*%BsFF\{ޖ0Z0ݣ1!%dlβ0;d AW?8999199[s.,9(Ʉ1113fHw T gd$ ϣg?%9U_Gtƌ2YBHTӳ}:)yKZe?_sjDrJ׳naYdl[G7̨0unvҴ+݊Oo/W[ӈy}`r-N l,bCWmweӾSY_ vRF7ps]j߁|ڞѯ`--kycl#c[xBY/"BW7+G:=%̸==X=aaZ)q&nh. RPGƿ-1S0g<ԭkU OtSqL2qEM{£)6Nxqž,rE đ,/@@ ;;`62i½v p`\d:xs5L;){|<ç+l!:s5O}$(G'*1{;0p vIRտ(E- x 0ɞ>ZӀ8l}xF,F,/MZF(ՖrHf'l1$$yo('# ?]Wuk7]C]@`Qf,^,ə +޿v޽)18yXF3w>-y0ٯ,oӦ --V *J@%\BZOSdք!Bjdt CR"On^3c~{/ .}Prg U?]6m=?ee h;aMj?潤K|sEc?)/Gm8Y.qp2FNLn k]XIþQBB;}B]Z7S^3q?F\'f5S7Gv;w"k̓s])k:eJ_Uuw' 7NRQcW vGL_WT溦7VWTPVJa O%.W\-tW'iv֙3Zu7 p:<ھuq;M˖wge;;pU V]P{}ymD&dg)7u̔DmCNU#P[AkI V c-'~bizX-X fW 8 L&>f2k#0L]R0g.\m8,ֆH͞ό8417t|H}X?́ Dk')WV$깓a%ںM* 7Ü*qªfٳ޽ٯ<"99ϯyŊ"QZrrҥw%\ז%Ϲ?G PC=q(XI991 R%99[<npWFlJKzzK!?L[@jMۮ.K_|0H(jD̵J' X~PFWz*sG(w|/{SqOcJsC̓]7F H$qaYlO\#ȸ\m,,~{twp@"5zq8g* _ז3{k?{h';Ft`(\NIy@bг=k5W3Z۷[S{NJ5Q  $ zA%b2N35ߞ%zJً$7m-<&3(tq)3ߑKhN6_FS}d=J~ =}Ƴ?  pS,\xfj_KlH IDATk-kxM`LQ],zXs'%C@NOtI f d׽h7^X'ϠE 6>{M(Q3H$3_HJ0Qc~~B``gZh UM,.Y( g#_gT|1Vjl_P_8%S)fb*ל@(|J1:ħ1KDG:8-Mt~#Y۞ON`Stw9/ M/jn\y@HLA?I8ʼnkUM[I[#w͑wvKftks/~Үy%˾޵b׮*ʺ1gO^ShpQ[2O}5~ /7z;nO;J? G]^KVg @ 47@~6ڨדgub dЗ93OiZ[IU۰3)DӮ,Mw쒖C!4^f4\orTwW֝zlu/I6a[+S'.A솤M{@@E'Y HY2؆XLw_ќ 83DОMj:'VY|ˆX̕pY`2-Th閬g0rk:gyI,=.tWN9,4H3|ܭ -8'^aև+ !2 6țt*]˩20bÐ F#֒Y@(_hroe ^?wחlx|k Άd&\E*Q NGGճHQ@ՁyPT#99333Y(,^vv/s^(۾}D2#77fo䇞#?g2ϗH/]xZY]%`,Y&bʞm .&I+RSEYq9/U܂,/P|}90[nfo԰kN8Tќ]/&]PZDu |y۾7N~?,ݣ1 B:GZoOp]G>ԎHٵgfhފQm\}sח"FnlSy ,W}cvDa鐥nq`צKxEQ-Pn$@ B6 :=r\Rҝbq||\!i316P0bnlDhvVӝ.Q+O4hgzKsGO~WL @iG.*89i4# c5E"IG054[ž/٨ 0iG^3v$Ojwfݝ[ l!0f̛XLT=7BpZt_1tFډw_官Þ6fq1+W7g'p&͂7mO9; mQO`"drd3l?ʈ |rNW((,&XrsYccpEފ+#8+"S)mh1 P+H++~}~ת[_ȞrP_X9YTٳg_ b%⛛D48{㸪Xp-gV5] Y6za Ώ3/u9RTg s"hܴ%Wod"q~TN ygĝwowW^>aUwZL:~WplR\[c+đ׍o~~?d|ݕs \;%ex},*v_f_ɿeyGnf?~]rHX0;7oĞc[AG>u՜ਇM3oGÎ"Pfͮi?B4jY2Xvk/5JjQ`4*XhnI0d~ye'J s]l`JWֶ.Si#h`57- -`2#}鞮Eu1<]x3\z#x[åI3upY7 TXfm=]+_ G $KiO߼EOrV[/:H>=[j)1ڜx6jr{:$d&: L:pu}IvlScctˡ8҇s^Ě4j_>QNtdɷyHtF&3XqDhoVI9aև5ܵKTQ"ȼY,L$ڻXs'[+zف9x"kFOWj b02to1MR}9هqrKT A,YB=3g+eəIjb04TWDGر]$EGj4Ri\qqs.&Hf/| <C 7U2쳘~Ԗ;7Vvvav,J,"\ P *XHuZyS.Z>Vd[_ Lbs_O]|JOFޱB7޴>'nLc>b,c2ي`V^+\B.& +[eGxFsKS̐m 4@x` B󹚜o]G?&"W[pؒ\<+[xȎC~rBH,P8WBK䀆?._*b6W~1O醌7 v7Z^uoopzk[/{ρYpu12*HqkdLS,/b\xv \c]-5k֬NF1HtrmDZ/}wXTU:R^eP,(bbқ5vaTz:Sz8D`Lf;kw|F9oѶ-%A@x[7ٜ+U\/$ yziƋ0c3jb/@8:͟oݾXY>f_>6s:ӇSD@Qylc^9;ݖԗ߭T ^'[Zް ܩc:_y2??.^{+[aJ(VY7?ڛxJҢ ЌZ{3">Ik#SVWܩDA,`d+&uɍ(gOLŪ^nD` h:m}(LŠ龾I3$u 2Z@½6V7,dBcC'Ͱ٥.җfO Y޴oOQM WE5gf:Y5D-RuTւ@j .hJx m#& Ƶ5'/&Pk4e&S?~VUG(T1yh+:BѼ7cFbmEMJc'wXcG\ERIA&hZ p4?wɥi/aZ>Y?`,`F$@] H+Z1ݞXahOsA,Ūz|Ցj,D&o:T+5؃xv1b1is&=>{CVRHf4Pu&lЌ["@$2ÛdNx_ _UoYЛ _?E[73Oo~g000Zo݊46GFjltpƢ3lval,NJJzz;v Wz2&n'ӇXY7o ul}lZ?u6J,~ƶ6GQĤ >C|?uCՁFpq[<;{COFB8Ne| FB B=s08L&SN56̚te.:9R}"}OI ƛg Ru hy ̜pT 3f\G2} #M!AԷ0Ɗ*CZry2SFb%U>wG>bWfʇr,B3V͇^EnZ"7 :(CS}݋N'6a23f},ASKKMjcÿ{w;)hK~F~cqc˖d%@oO Abջ2ڽ]ExO4M(4`3dd::6%$$r8^dacST)Y%)_X8J!^qRD?$$$ty柹R®? /;dYc-Kf]O-ofhF{ov))1}RՑ~WK ]-Gn2/].^YfJNq=)Na0dg&k}T-]_Fz)?|%4eզ|L|USi^ϑZ8^qPT# MkfQmS&_gxZ@EI Ck ɹ; hk3jЯ_M':pۭ̑ٷ7eNR Ҕ[A1 WI[g/Tu$Ĉ:u,.k\u;,iTdJeԦMccM2c;TE9淀EG_W?SIқfs|$t0[R_Bxh.ahJ2C%%zĄߖQvml6g䊬Kng=!Bُ :OheEcA>C:w]7tXan vm*S{cGh jNPMRRbYn_&'[eR.~_0bim㍱ݾ[O.xwR8Q& o=:;-[>ye8FWwnZkګv==yi+[3AZRy^9.Wd > m8{ŷie}36p둽z}azMasW0,dMK;nR3Vk{|#(:ڨyT[TuEɦš&LD7X <:P J~sSk0aRK(T PD:Tar R~̈́H,Y#ona b4ħlXbE-kx*&&&vef;|j&VVKR-||'m\~e;#;-[\;,8ȫuYɱng/y-4xĤ>˶Uև;?|[^ň-^=TVԘ&ВO^Zu|B+hv0 kz1鞱6lJvQ&qJtW ¤Vwưw¥z!4"O<% [o8|- 759Ȧ;oez\p_ џ:f]ϭ,-Ue94[pv!ӢQ#D@yNitƭYF̄KŧǢ4;Gju"5SS*9{[ebsՒ2)R}y?T^:#(,Fb靘f.P0A2hb0w qТRrNbN @jD~HxbqUF>Ҭ^@RDZ ~vTv}`Ï)z 6-Y4aoB7jA $2Yq:Pf-jooEу 6:u,c-XLՍNJNRnm HA/G j '_g h/ {GO11DT* H\Lc,K6 RqZAGu>`1)`iR17^|(xqyW͞Q@nas> &I"ǗhJ؈Rat:ik^ݯgݬI\ppAym)'d3 --}6ǯ_p_Wd՗~$ &$\-[ssN׷ǫaM|>quot]qpiTcq>ðZ_4TU6BC9"}"SW_}Iw자8.čna,L :֧﹟:;o0f#9钒j??r V 9Ik/^RU kߡG@tn0KlP╵=g@enJ h9k7~4A :b2(L:Rkk&r s<4PHmT 25Z"&}jF3ߞ,5~Xt&%@W/W&ZDg>hyv7 h= I pII oUbc#[[[{ǯt0oh7WW!^}:""bOJJ,I@xUl6ҫc & }{7=qСD.%źiILL `bU)5nXwzd~?O)97㓍[hFĤZ˞[CC"iq1gN5A!*KUB]\hů:l۶+?PƟ{2!Wc|_hZ^OMر㞬f39>ZЬq\ganSZώ;q{uE] & ptjؕUKk?]l[5eО^hźP QUJ)-b# 67#pIMvcrє@*Zr3%7 \WC<,| a1@ *hTD#&1HEuCL:0@&\"PB.TV=}Yр3q3uDa- + aɛ~c ,d~NO\amX:qolS}}ٞ!F vP] \uQ뽔*N]d1l+@YDğ Qra)*,"_QڸzTF[=g@hKy+egtX|4֪OofE60F߻L\H^t%&&wSIo?;wG |قrv?C5_Eܵ=))^^@U_ײԌwnZ4lan Wl2˺=Vu+h% ջC~$uQ^Zbz_C&}"g1w :K}yQJ\pZsۥQ^}ŗ8_-Z>s#g? S;Zeu s/qM oOaԊs;ϼ)1tT^ r(D! [ki ֈw[!*TD b"]eMaWcՀ*5:_A(hfES' d~6*+1rb\e`G˞ʼo~i↙>e%[Yw#&=1ra>Id%4Q0ogj=uo{"S8! QO|wlpF>!ǻEuRRz(|B4] F h˩}3yNǜ46UgHq{L$zЯjm;WWڢ tHBHЯ)\?jFRY,`/zT[KBޏ׵}Q'9Xa%a.KR95_+79]z VT\Mw" "8V:LƊ(؆Vla Sq@L[4Ybw9BYK$ o^NU 0 pp=@fu(shs>3:h?):&ЍjG xP۠@D[ 0ZKiZEFvto#uyUsUW@zzZvtl@2ٟa: 7'w1fƪƆ_ ?k_ L30),sɓ_"$X<{tew *,,qv=w4fN d^K|{o+tlOFU]M;uPClwOwsKc!+l'dzb+Kd#t}C]YTO_T SO6{tU ~tb'ɘW_DbNǸj]F\.T\ݾ>#s c|ni$N\e7lLLdiR4?VI"WQl*F0cN *F~`' !8F*A,#$RZW}b, @2fSǂZj @!Z8a \0-"'AA)a}U5qɭو)7ѣRUlyKRGnA29 UWyE,:Uītvq"+j-cּf^10(~㑓}|NMJl,̠($ܲEOg茺sө/ssn? Z,{So|# 1HLL".\x)2,0nc#|-zV8!%V|u\\M_ S^jjlBC9qؒ\)np$ub}f S #*w96釽)k贞/w_^??4SdvhZTt|CUN6|;BoX F{?[EM=%ŰD35J~%[7;˖-9zL?J]y@G81=Gja fsMp|]Vy>O&bnhzyv)Oᣙ2Pl2Y[A>kN~j`Mm"c;{X6>z򞛕"nmU6YU,voGL01h\2&`/`Xe>PI,Xځ(l| ȃBlERbV؎Kժ_,X%?WWjKyT3#BÊɢ~4l/"T ^* UE! ~~Β1a^DƿE%G%uQ ?v}M$#Qdr~n!8h4DdvH:*A:>měΖGK@TGƏz%Fz[qYDmK{WR,O !biXj:Pc"ZRZ+u-#*k枘ᡶoZZ+bUߕYz9AEDv1tu3NSTw;hҨae3Cr64BA>EF;BˆBHK v[oLI],䲇rt͹?l&-M5o!c~uS4s {jjlMG%$$&&&!kGM{˄giqal6gFgZ1=P'8;"%%Ŋ>j?b:ykǘLԆ^kFHEKQyY&7 VVcOݨŋ#gUSs,鼻i&2D/*2ٲϣh}{jEg)GR*Gы]8_h꘱y ^?Qm8xC)J"g|rW,%ws5w-%mGe郒 A>ꐲ.O-C!2/H򐡩ɻ'/k]iޛ7{tKWv;R>q|gWIzM]YКѶhEρ>:ZӪ7o}糔u#dY" kc}ިZakH9>Ӎ蕽EB&pW!+!ExgoԜ۟_nԖ4%UcM*xDȈ~łU}7̈́װ~ [sKSBÄ0mq6=Cq\T\wj'!TZ [*۷ZzٜZy4V/z :YkV#^EV yLIA6ڌ4=ADC+!V.c$="բVJe/rK au=&2d0AE8_0*6y QU(UQt?.J/%Л:*>a8 }Xvqu F _{ (Qƅ@yQ5##[c=iu>|pU]@ :XH}ԘJcmDxݖ7HD+C{wR#97aMxn/ ]Ƣ۷#CC9cci?_1xglU&wam͏( / @+QsϹ/ ˜w`}cSSv{/_'eUPv. r KYxDYzxrC+Z&/|fЩ:,/\W>zv?+Jx'P 5dyעw}ٶS9Y9"F ̮Ôdwޚ(2Wm޵];Ι31ؚag(۹(vk0Z.ke5>\|`T87tc櫣tݝNĜ`S놕UJqOx} l0#E8=_U^&x;uNvR 5rF,ŀ޵.g^* IDATms [gRKꖏRywIQ(o'X*`𚹀pn?g {׍Wy+_R;BI',4(c)FSFF38ޯTNe ))cckgh~̐3HI/Ni~:ϝ313HuivaNai+sXBU.kh1gw,G?1J[6}ETj.07>1rnZJ{oeJѝ datN ^SWSFSm Gf@m'3ݟRF!3YDOb2).'8a>) ] #FQS] g.hsE&u0nZ4 wz(I 11)05ħP'"))Q;c?DeWY7|M\2jGȻt22]U~UpFC<̿xsD=#IT_=]fg4v!eTL ւgΓuއ~n~-L&dq-oٖN)fa٣7RDOtҖ펃ݾ{/?`,܇䝇2$~.ay9W:p_Yf='K )mmbjfs@5a>zjK'9&s&GI|뼡n]wbbNnmn1'w,[cjl;Qp>kc&WdC8h0/D]L+ ܦ;4y-(<Ӽim}jnSHw#> . Ыɣ }bckĀГ6Ču,feB^+Ů3^߻R9T$9u,R-qGx~w#@,f sm{^+Hp&tϹ/*Sfi.y(d8DyU|􄄟w'XvN3[}}H>-7U&ݏzt͛vU.ݩl?qڒ%}= i'^I~hU)oOLUu.=&tf gwsң?Mk0%*qÎ\ءP(,>xIМ`ĚRRW%rLE<\!) ŕu"yt1rܯr/k?xu?RKOZRG^~7i*U$HPxgLww$5 X& 0 үQS|ӿ sEE!k+X˘S*JywXiN^+ k t&(z:%A<]h%sU)ohU|}baZ6 *e`Fp\rա DiTܰHE׃1ÿR!⭲(0I0.\b#v,A%zųg6-In;ւq `iωvABk '@ODPLZ$!ϡVN BP)A=dn)Ҫu=~GYzDf{!N& =!;si t Ǒ. hW {n;\q9<&*k#ܞ} څ-O&.#7Q620b`g9 7'M$%nX2  ޑ7[D¯SM{KLN^u~6;tG6594>k)<dRu"&YZx45 [x:{Y4xY4S_#Zy}S: eHZ NbY`j,mdnβਗ਼bjZkMG (6W>C`5Eŭ qsХVu9&Vp?h :*B",h;wtk^tZ/s[K@&񛚌L>+_cAj [.SR[6RيSh3A*,l]-*ok5"juI.5-Θ8E8NHn,q{|P*?AVzX)a8(UXN)C}\YUz MЯxC 3#P^ 倥7ߐ`j+`JMtYxab2qsAi1qn mM4QB&:G4 Jլr~-elRD %e}P̙o=邛ŕ3VNn6 ;'.[LFLE( V֪g}eP&seJQudW]ccQBBRO5-7^?gw|Ǎ~׿˭x]ߜ?`O5߿ ٲ&_?[dHG554׾NJk_XC^omg'zǰt^, w":݃ujXO@I!~j'%%*~gwxtaa"O$:C_e7T߱kxYCU7E䖔G/x\!b%-6)Ma?u]?ڳ%?PdZPVN{7صJ ӱwZFt\ eݼӎG_,ak]31'VlQ *.@(eFFV*g^=jQ!ohizi@sʪAoR2 SMB-/-/ICLcGrA C 6LcYC1D[R(#xqw*` S#E]z}#ϮZ v'4DFP(˄[۷:_ @eU!2v!,gaGHkQkWLd6u,eXpȈW $[5(yTXϨ~ BY>9C[RkW4{[_gT\i]hFсNl.`ZBe-d&0%UΆ{\ދ▖)UuH>Ɏ퉖D'M[ts[_VFޛ aK&D,F\0] aŝIOeN`\'տy;Y.[Z[1~ ] '*BB.^dl,vtl$ߠ{︨-lx33 m *0X E4Fi ĆXP@5Q4ކ{g~ ܛܛ?9u93zZR[ .kҺ癗`Ղ@?] {+iif›J70+ST gl4?zӺUV$dFTZrj5'Ӣd$7hV%x4Wn^:3%=.Z 0 ֚2[0>i^ }֌EMt7"y=5|ļU)=4`.>!ohN؀Y}}iT,Jp'#/v/}ဌQchգδ8UCȔ]>'r6Z<=<՝<Ҍ \>N 4VCOY] -xt r2s% m|۴ 9M{mߙ^'̭GlPWoj =R v-!rހbvVҧwh)D$=|Y-pVϯq驗IAXOf2;Ft}2f<,V*'ѷgȢ*$_BBn{dL#{ot/$˩+"'}H C# puS@⪬Cˬ~S5Aut@ {Y>N!BV .MVE#5wuwTKpL8H$NE+Qq%XL+H]RO)gߙvju<-Z3*rJVpvcTX}:tΞ=!64z3OqqR lVT؀偁qvvEAAuuGxhhr8IIoި#J XF~kxrO2_ǖ98:۷ʔ9dᑙI~:\-֭N֙ߓogԴhҴj#^>EWo^kgǝ|R.|& 8e(_P#R3_.| ?`cbejݼTٔMy? ן9r9nӶhԤ冀ar aɯ (g+̓5ϥm/.6sڌk^Ʀc< CA#dhV%(hKy'zbN*AX} * *f?y~W&wy|ﭯGɽtwOlEŠo.t~I(2V/%=u:ǒڦ[ii&Y1+w/_fc&-r`Ѩʂ th4IEmykucxdl|Ȫg%QVT WHX3_\C%%M5w0ԕi~7@ߴw6#Pդǧ X: h+ɏj~zGc7 |Ӡ2KHn?BѯC\+Jm~/!2LQnƓxxX  ()"kSᡑoeaw N+q /B؁:Zډ!_hᄚY]_?iOQw 6be{Fxx$ŶP~woF'Ȳ{{dp8##^%UR˧Oa?[;;RQ:l4m[-Ukj<~et`s>I\Z-VyhoX c|;1,@Pح´F]E^nI]i\BA.r^4tVO赏ʇN_; 2u-r6/ /bvє3WtiӀf ^E?}a +eWZޕ1Rٶ*C>VP!gc5hŲG-|ּ/ݳhKA^%Tv~ɾOӯ_Kaٍ12]%ԒٽQMH9L p@ۺDbS0~N+þY!qQn[+ʧ&JkSe0ƟP(]7IHʹrU/y 5u9PWEx`<() ym4g:H ^6l =a) 4(H$ ugLOK? 6]40 P4() =m`::]>n ! @bhl며s>yF<768yW|p)#cs ]HxXVQFؠFci޽;>ykA(i$U}2E_`23233YR ^7V0nͫcipmbeX24*kyfmܱ[>qw0G&< Xzyq_RVo^2'+5٦U|E 0O ^jϵL19%3̟ dYZ,-7v4P7_SDDD**y23m[ԧV[<+dld_L a3|;'QG;^>Դ_pVmsx 1Q1xEA7^=`=&;iXΑ]}/2[^g ϟtfǓ]D?tN%>K5u5!#ɾA 19hyեI(]ÏO=F]D_fS]-il/%?y[;Gf(zFIJdLgPƠ@ D&0E2spe=2H^){K6@B['r`h<@q82im08 CPt4T<-IB $><|%0PI3 m]I@K\MA;{ MQE@u;]o<~qH9z!b(~=fŕFqRrl޽{["#""/q?&z]P, v??Nl6b[K?[xxf/ X P 0P\6P1%?;-vS IDAT Jj4lM{]offӞ/M]X("cU͢JUGc\t45 ꭛")Qa; )(x/ϯAN<38,:3]^V 2]f~[=|ܻiYx=]ª+]O $wxl~ZLz{8r1}7~k֫jU?zk,&JUKɡKW%K{N'hƉNr䧈m$͔sn&}p\Ɋ[$ $)i픴t}TsU;ѷ j/)X}ɷH sp-|٭~OnTUe/@ BFn?)^m~7р8X:\TA PQ])#e`e2ܝCJ]!o}SLd =JA&ˊi,1޸' $hh2oalť,.!Ffo~Hz[H7v.H @5!sF,xh7Nt&] TLfGxx\/BVȟW O?_pti?*be33YOvkĒ6nSWnnl52[fĐ\:XR^EkE2z$orJNH !1&gZOשSU4 &?%/S2Qސ`JE4#QIFF-;SlM{ʚ}$ V{.&ܱ' vy:fEgI@ik O5rPa_ɮ~*=ˎ&U+)2ʞn=go٘_Q/"Rdb;mҒw:@Qd"ˍldG{o\Syըg-a.|scU"Qa%DTc;i+,i3 ]OW'+s0gTX:ܢK\.I,)9bRP<}nu9 EpGZ Uem|Z;INBJ HY r,C0[Ne슐d)gm CHiXP(tv4C9[vyfr/( ddKJLf~aaCC;:I?24) *ٻo߯ 1//{Ab"RFR(pwϐ,e+qcË[#"R=6}fcKi5 BP5U4J Q(2Stya!Bkv$C saS?dQCYh)* -^~3\,x $^0k#|'UpWZaDQjSE=X EbDjsPUl;ُvLp356VK25PAO~GQޘ[fܼ#_x)}p WLa:2j.^\9.W=_&ɚO|KF-MI]yG~ ?|Խ_oU ]Tau\kvNN,/k%<g=rpz5sq|y뷲w:=>N4p&tn!+{ܵ w)LK3LJT%t%9HF@4*(*W{u, W2Me(Nϔc@L&ciXY}&ko>hDDDN9,+#od؍]*2ГYLY3isuV΁^< l>tijoIeK/̦QTQ)!cM(e ,=@<)1eF>EM/\L!Du*rv,$Pڔ] G_\;fcf}YeBu@ 1;NtV%|n d4\\Zg#@#| Lwy(ZV#"""U=󣿽!{PSKƞ׵+w컜KIJ)ɿ{tגjdzcGj}zQss\km1eM΋+Dk[5DR;+@ buP` ac%=lMW {-WxmnȂLJqVxO7,dֲ (*~IV8=FǬiXz|Y G~eɣ %MM玼:WXhe̦z_- ۀ/~?q.ͮ I=0/Kw@at|͹RU0y`b)UI^"V/e+HjE׏@"C ]}CeE/2Xu5a]lm=rSڤُ|"1 8͒"'Ӟ)2('/f ֛G9ξKQ%>» t Awǂn6wrR&ɧ!ufBQ%q*^x[!'{~W0sܾN/2jצLw ?nn a^fs>LO~)=["##&^FgS7gRog.Sr{')irUWTP֤kk1@P(#_۾BmIdBNe'Qԕk^='2CC.b+]BQkFI5$ș^^/)a9ʭ`(h_^_7@ XC+v_%cŕuߍ쭉QnX?SVR}hf]QkvliqT } h{Qdd]]To~0ܓJ,S=ezȊs:7ϯfq)5eqNI~:s8գcQÁ?ۃ :dZ"]ya6?d00z2QZ Vq.L@@%I͂{*i UɃX"KF2;/Wl,w :N{?.۰؎2Ü!>EHV2{>hUkq>p\M2!Rq1H*8LRXd頪MAnu:8즞A7,ﯿ %zv"|# Y32*-?Qض ))K`l fM} ĞsTᣗA ]ó<, K˃6qM5"*jahS8^yP|?-F"P7mVycpim׫)%K}30cT/)!DWt5ugB 8rB9$}=.NR/O6lr^,:ʘkpӋK7e| ϼe5|wb1 *KcDGAJgag~9ֹV91S跍{ptZ} X;+J4G+長-?Z]spwZ7ל0+}ry6L&n#Mz/iV,[P~Toݴ塃"t($;لe~HC5U,Iy[/qMF4T)KoxmxWW)| .4/'IL.  5cQjo$~)#pPUﵠD@'+0܋Q"YǐR6jV/T G|%^x.k  j u'8o鎰&Ӗ_H(0 z.vzIkոs/.y;ٷ?+-Y}F9 nmfXd`ILCEU;;PﻭT<lѝG2W2;%_Zofy蛩ܽ"<j{O*&8b! GԿ'{FnXb>@I^ۚ|Qe70 \dkd*e#]M% /@ EDGޛgOES4w>3\Q (TQ]/e!җCӀNM5xSw!A |u(Clrk_ <%q`{< w7qUU/|pId444L`Ien<=SS9-OLy K/N ɏ`2l ϩɭ{WY?ml޹'M =vUfo"ܠvhR<0̘b*o2ET3;\5}lkg3EPS}XAWnDm3lȈ20W=W3RfO$VߗB4uERs%#w g3tޅ%,,jul/}4U_Ӌ35>/1)2vmZQUuȗ*j~񏬎wT}c-,S|˰=5me=nd"Z}F?&%u^<ڗfe'ǐc|>[Le#J/6tJM+'$~^+c,,ˣ#\a~ЌW3!=AdK(=ll)}(._tJt*@ & x/`}\h[}l"#JZ?Pr݁ alõHOݟ`P@"l2r :gGD /e)x$#U%DX""Az߮ΐ'|==S. Gybe>'Xÿzhh,.a̰Ҭp(R+׮]{UcW*ecF9C#3?i%WkهX~pX턯+kgg߮_RMSzSxɠ팄ʔy7\ )cN+tb^Jzgwmdق'|zUoz4j<%BK=1ilQ+E6l- LԹc"v6Vz%7HuF̧@.}{w]e>*_vƁ2;{)] U_A|>{esḭA/>yF 6/j7&Gȸ$nF8eΑY:ZEM=FkS1P)H$¶]JbTQBSYE^K]qt^-h0CTd{*Mg@5BPR̩п:|@#yYU-?YK@SMZyz'jkD0@( dU~9o_Dx@_Z̍ IDAThn cmˢ;C C{%PS!WbD\XL$>ɍ1Ĵn/HH4"5GŕK+iu¥6m׼e4 'SLϋz#M՟z"=2X,v@@,Ş:aRdGDDSSvt0ѭ"GDU? \bEd%JcY}dt)5 yqWt6tEE ?/o7L2f7Eh3j`i"ؘKEvټїVk@X:SFتG/9Xf'g$ܢ\YM>?شh{g!}hf{f(̖A2XAdpqIT^Cw4( ,D;/wYh)̦ؠ>Yä#}_^aM3DFFl*zuM T_|m˾8lL]"EMq k=mU*.QvNۂQM'WRD-ShNN?`;|RNґ՘8wU܊8Tp[+zb ӶWV$Y_ލ'Kky UbyшYuxCbJZw&j/JG$7cJ ^k:ɧ6ֲMrI \!9qEKѹΠӐ ٚ#We-ݠO6&"/{Ѫf|3޽hUD :l׉S 21^>e (T D]h4r3R]yr Yp#2K3xR/B@ R=W6 rU&Y$FH- &A"R$wFزf$0ܷםu6:5jRY24]{Ԃ&5/ Q厣&I UCLQ,C]mӄAm}7_x$m<(Wr1R8xF:׀NP޳JVƍa|OGTHH=r#,;-_,5Wgx׶jcqdDm kR_HinըEyRJy%]M}q)ރOyպ[.OFWc+ZR *Ȣwmdpdn\d' #~[wwZ<6K=*~s<+*J2ʞnSf(qK˯EYQ=gnl;=$ {H{M%C> ۤcN*:ͧΰ<~t+~د;y rν]9}W<~Ȯ1B^6(tbc4$c,q6fOUCeqΦs> SW}7oNcЖݼr4]_;3UZCRtVexxBn?МO(d}x/cznU$8wSnZ/fOO[)Y6/;oKЦ?Gj>G oMQ#s?z8z{CA%46ܭʥ; ?>~AM<0hUdMYL'j*DB6&"S3@Qw3tļj9Bk`v6tv LLi2.V24|.A|<;a*IynM4q0tzAx-+*e;D!EX ?Cu_Th}^^jD M2iRCV/C]-Ά)1mF9PG@ 4y,oH7  (<hCg/HMclu cErOgrvC1î3ɴa?Ք!5{&:ܸ}薚znga#SJczVV0\K!+En;kGm^Qq􉶭ͬn6T%yo-Ri$?4#7166b23YRdĂ扜Y_*La6Z9{ ^:)= enjrD]}&淁9%Wawp`Yݦ.ݵffw71]RZwW۰mu+ {c27tkjJq|Y6chլU$㪪CE]?K+Z<#t oN>U=VF堩盻]:(+z`f̂|&ғ.uY}M<.p).<[#n)\vdQ[3[(U}kz?:9%0fjSI>0;pu^g2W|D_d%ٵK^X8 {{u]v].\K~/Aē}~-U˗}|Q%22BG90#2m###گ6%RBLJ#,.zjZaua貵ӏ"!#=jPym."QCa%b(YQ{ gBe=79oET_5^\A 8xYz֝ó s 4 \-i2/Ddž:L<޺K U2#*jHDUMT-5FO[ Y0ml|/d )yl1gV,04 dcn>Uvp*G~OB71w0QtP.li_a}[U1CH,b@3?x 6U{[Rdr$EWefvFdWn?`g!& f<xpAU%޸zKj.x!JI DUo\K A~Oz ڗ >tЫcLf;Hѭv@ T7FO|E~OiNIS"&r&b `p{ { {r5YLu}G3̬X(V.e9b˝ˣ*Wd' \8/*W66Pbz ~忤0^̓I}q36nX}DsI 662#zLe:ElȔj+>-zoEa6ƍ:9%l J/S>:]E,)=PRaf=3M~[n% bbg-V[*l9€ ؚϙ)gg&d=߰݌w)3+`dpƭ DQ@H=}6i; .ʢrvJOնk7hAc ώk?=w#%7c/QT{O޹XY|_q%8MvDF,u`V#?{63g<{=kEzzwU| BdГ8J?H^"n:R*/0¼=7P8475[=| UB 4r#@֚@WV-08LR o:[JE#I#h~eDKc`K\Auۂr$[Yp%{]ʨ>GWHg4U']n;@Zp hh1=+ c=|sCrZBxy`orPI5.~lH?ŏ4  in8Uj@㟦CZ2Ah>=89@ v :pS~mecgc-f;Cw.ii5>zSEZP@-}U]+oIտ2ws >#_Ɣ%%SNOgn"H?2n=.ZaRiVcبUa{֢DAi^tdTT}CϤ-ַNA+@>uC :Ơ_6IYC~vyb1E=sRXWԱ{ukii5J<+D+ߍ; SȖ.mQmHxm^L UH'ߧ-xrW zW_Jm7LswW#ң m5se5<Vt^EɎ6p1eUAg+Fub @h̔3]}ȝd#tU%;AKˑoffnMKM7eg +݈\dqFۇY3YVJ.گ^u൐l*~o?mżneÌȫnlڍ ަ,s  &z]瀉EH,mzm=mT" =HC( \_żT|ﹼ>2i40;rpE`J<Q$"|==:d$P<yB}<, * (( =Akk{W+KP" ÿuP(`5gm(Rq3a`o).ҡE4UCR6l03e">ZCT$]8ʯ<>jv|kQӦQeOE< fNd TQ֡Z7N@u˔0q(|\j_4yͩI Ჾ-TRC"B.qrgN|!G|nf@y%BGdJk<tVQ8!-5p^s;r[Jn%/I xq%ic69R5n© ,/,,A;/mdD61qȈ;: RSC /+e?=9OWm?KLOgc28ԑPƉ=*r>X*/sq@ B -{.ƐY~K1+h}_y,d }GًRޙk8{c捫'8&dM͕uR̊/Iw=.{ym;Io5ЖWco&"OF_|JTr}`17pO~ӂ*F\bq"sY;dc椧`&)i-jÇþx&w'v=d6jV=6_7ڙy pAX!qp>U~Ǻ'iV{ψ`µ%Qwծ緶=׹u7n Sf.l<^gsw/k\ܵ}9{5.3xpjV;Kia1āM+w(~ѱ*$"Dp; NUHoHKm'@p5fyp|b#Ŗyu-kJepǰݽ'0_ǡX[DBލXж5+zqÙP/ @>5$!fKZ \hh0s?o+)0iJZl;8=AtZ{m-`OFN}}rA #!j E>-F=lr6ǡ03t  t:6Xop#*q-ݕ\}.5QNGjʂϼ~Ncrsz:-\@ywGL;I( F>!$,KZ@\_=~8 L;(Q/ AjQB{ xj\*F{AuJZc^$&bN幻$~_1;nVwOFz:SKdn|WbޢCjL\/2D@0ZՂegpeXsB4UƔ|UA="|$ I6~ I1Nquw㜧%u3VZ&쳲ʦ/x 9d66ba:TާΪI^WANYo7@FB/2u(Qja>\@8yc;Ug@WɰٳBKۗIHi@3gnw6ހ_(ǂ61vW}>SU'"AUNW@~6<VgZMLPǧƥܸ>ySO5*OATJk:5/hIaLG5<iRybGq;]:&/`!hCq6⌷4&&ޫl\D~ٛIC01Dy8L{u(ϞWHE#Vv\rw*?6Ӟ7eG2}K:&UÈ^=r/^#JsV/FvDd sTXrV| xUZC7sNo\`LΡ[! eM\hœuWC00}`&-NT ЕZ24a?-Q-m6Yd낮Q#›4F\)>([0tfyO]hajH/֝#6fyFյr3 -:ZAÿ\3.+ٲPY?+}QϿU5Hb f06aip%2Z*Mxo[0jE;DԷ<\)TeLR;hTΕ(*^2BA2 EYs7zC > 7hJ\R}ؙ9r[Bv ꂖ;}1=0Wj»ڠrZ?G.ʭIҶg߉yMmxZM$WꟆ ;]V 90>6} 6n/XEq×Ԛ^V, k4IU3 :4KqmABMg hv H *ZeB,*"FFm}r4; "H[Bn IDJ"DȨk,d{lqlmeQq 1{(կԗãVii Q/bzϩk~GHK̐)PXhñ*0>2M`br@wݧn H8(`"_OR1)̐8'qQrLUc MƔ٫Q4JWW29_~Ǻ 1A:^ҙdfJ_?F m n(:4Pz [4Ap)߭o)K Z9sm']&˃i6cNu郁knU1%+˥wp.ރ~Q) ˽0fny6˷!e?  '#g]=_(7Cg4]3 Jf+ פ@R y,KE*0CIf<MHƹ?B%P`_-U\ /M.)VѨwZ=Bw 7+AN ;}ç DMK_ُ^f@o?p3FR,h4kv"">l5]!CH[R߁O9!`!rP@TPus3LEѽhqmyo5>PDb %9jnG.-RqQrڵ-MmCv+nǍuԸXK;A6" ϥ@=3rQOKpBccXM`=%we^p 'V-q5Hllb25җ,ɨVY cVq=XONZqS:G<%D7EuBXK懵ǿ-ȦǏmB¯(7TQ$}fz}|ɜ=7iX5H^NiMA۵/$}׹=z ^Ȯ1}u"?2ĝ<_ڸ7"QUNf joGnrptt0ߢ"/y(*!B9vӺ+ҹ^ml~v'71 wNEbQ }6۷΀k֡L>{ȼ[طd|3<^d PT~EXIgDJjlYkt]}r[WwhNYsO $h}̢}~d~}IAÍU}Q\hSm3$*J 9{ Ϙ5Q/IP.x qQ1 ED<2-M\a5U#[1贫蟕X~o]F AFbS>BAf`n.&7ĠЉNu%b2w `3h^t2rzEFeyXAHbi9c=r!B eBUo/`Xc-$ȀA@dqߕh $ɷ!-I/;6ht#[ټ*.L,*4 e^LEM5 Fp56OBM 5Ag/]v5l5I:y[g?6 H! Bi.A \ޙ5)2O#e=,?g^Wi< !X5ݺ9PT78z Ym]Grミ=- G\ R_75#]ȡ _(R}Z**ɑ~w?uDTVVuW}1rFháa3$nEEmnj q72ҏL'ȶ%rYb #}\E(*ZCW甼Yh7q,K颤$S&RWGdIY׷cnߖ? WIS!W',֭+]Pv˗VʫA%˟\A 󭞄Ͻp|掐^}xl›w&^uSR3.ݳ!:B_}YeAUa,З|f |”ѵΕN0g ;Feˬ5,y)}&8 U7 bx] bsg{Dc`U y,#j|SM2XGNШ@oW!`úbո%ZR+ N @ :EhcTU@?Dbz8QvX5Ŷz@GP)0*\*Yt*Fw &8{εܩad."R/@5\Q,: @(:zs`| :k5`|6[ע*NN[Q>yyHᱪd$ #@hd `u@vsdd"#%X%pPN0FؘQO^'L֊2(>{&FDȩ泟A ubE_jYC{!XW`\̦`5l{ͳ36W b"#Ә_qRNj^;DX,fݒzE3ȨLÅ|UW>M쥕qV6FlZW^?=Q\#W'2~O?,HBƒUof/}ܹ,5Uos\6,=+}#ܰ/=ɪtxbs"zݹ-|>mT؎/DZTt !IB+ tNF R`*8Υ"ad7?[Kixr]8Qq"pI> ,`y!E3u.*++H[iIdZ <>2~$Zy:\;Ik k_GB 0+8IxNE=xrV7ow"p=ka`h״3p.iDRCp %d.!\]ܖY{I_SSM]0i^t74#;'CS;ib7 / mtQTp4!s#b>*&Üh #pfOIIw LA|>z2PQhCN6wgo@L8عOV%,EPW/148\46rK hEGţ>Q!+B l j |$qq&('h7q4ɕD$e00D(+?sq݋svdG vzxe0Y,locVTo{Ah/%̄9sRP8ks^WYatVQ"꧜C>p:OްA02QH _"1h gθUSlN ـj76kM/ȢDᐄ%X~VOiۈ^<}n*CüC& uܟʞWT.u{b9S mQE+U0$A BCd nhf'.>qYPQēQ#V܍HCȂ\U R+kʹjOl:R*99#Y;&#^ќ2Y#lY&cyY*ۏy0iL&| 334W[cR%$RJ~A=MdrѦVvxg?/(puzLOxf_&SCywp|p`C}+Uś'ҪgK-GFFPxԣck?v2V/!!aW^Wo[jg`1O 'Ċn7V0o܉ 8T?S׺afJ!dUn<{%"wu;~Hin?t*2nLQ.KL6Hlq'OA)96 |OPW/ͻ;*l8>w`l[uz_}&򠰲PS[vNdcpԔpgǞQM=)k]0VJ˝'_b(XDy 5j Ⅎ@]=bE=[ l-hP/ǪNWٓ?0`bڔ:{)9EPPQx51C6ڔd\g>R2=cgh!~wm2})|2/{9:X̽{ :fԞK [yu{߳_V ݻX,&ã&&bUF{l/:-`Zڡê֮G̺ee=:ao ' r"I+)l{ΰDʽF z-}2e|4dH\Rŷ^{H ̖G g jOrwyqP5%RuIL?B+}0ҹw㉬pѬb8 㲷܈=S곺3iBKc+k5ck$?\k~KH<0zDvÉ𦇥 /o 2ыmryZan=XybֵmSj}8q'^PR ã peL\H狾ؿ'?7PPAg7gJM;u;Uvp7}S$\򾾺@JdnbĽu ץ#eAG7^ t5FCU-!*Y*YQ0ķo6(6Ϛ%~R$G,$ǯ]zS=K=ĪI,-bL'?\m'P.ڱwsDؽ$ w$΢[ӿ>@Tn,Oy6\.~I9YY[U*=Γ/ j FN!QADx_ة5wP yk-1oK {˄!+% d(hpMp&44sW;-.=3pmuja9V9ogiW zqCJhpx>DbdFtTW+緷/YQ oo*^M?8eQaa+ftu Glt^;N+RiMaH&U\-gn F[/&]b1r%oi +C#9YK %b2Yr'4,WM/f  *it5b?xi0hADBn*O;yi`a>*..*31 љ-/)y#R}6^kWϥ @n*O4> a#{|1Ы jfAi0͋GTYtH.FA&-?qqBiSoZ_f/܁ 1n:N(h5KL{wJr kE^nc{1>5Rj`p$G?D~aXO z;J7+b5r7LpZ{Vg*DeQq/aX~Ha`&gX *._Ld1X3')X6a!j -UXr'Ed4Fy)v=q}"R@^ HMqI 9ibT}~@(9B0R׸P:$)B:y}`< T !. * <ѽh0 )q䲮-9NTQF: t5~庨fK;飈=I GDܖP >g)2vCز}BYف40||1if NG2.n~n#=P/FvdjBBm=R4BKGp}3/3L;\EQ͓O/qi=3|+[ zwBR?Niibyy ^^WnNL !!;LQ[ cXgOL1D*ȧ#O1jUg9TN?Z@IKsK} IDAT,zQ13ԬsX#`6}?fKV?FVqB,"㭲N͞JT9xq``Y1Ne6uFM+ .@̺"yot.7.gFwZ Nvd+aı׭u; &rn-77+,bL;.|f2Yŕ2C`?4Zլ)E|BP=||be={e m\np:ޯ8SѮW2^T=X{nEL}//>2<7if kpm̆įhTUTO\/vzpԢ(sdB ~ 8<~pC|-"A}ls=OCC'\NQp/Åh46($7r>P涛;;HL^M0k vz` -Ix.vy%bh0%܁8\ _e?4BGT$lոR'rn)I(ɝ?lJyiC\>/|EX}Kr.x_W73M(Ii BE `ehp'C-m>CS~uͮvx#&UR[90Sp뱎>UVBqu:QR#%*;ڒS7#E Woq+,#KDLro,I,N}ϩkQ^qWz61.a E^\k-w׼[Az_[>x-v9Dß~(y[5+, &#I鏾zUJ76a . ;XIc[)e;TĮ@o?V,3Oaļ.+>dipu$Nl?bLjm4?)ե6 p ٪&~l"Ԯ'|oOht"$EquR9 vFWA<>"]jM?I(+vxQŪ`g9V[(+@<>A3zjAp{Ὴj璉V9/uh1 RZkFMn2kX>ͳww1_3lss}Umz'E#v!uR}\sSLfta:MtOXLHŃE21B0+,ֺ~BQqQ}V5\xp=d2btb{gr"dMYXsǮ :#4Y1DZn321ʇ@Su˺~Dwn;ҫ *eܼ.l7V\߄ 1.ߧ g^XfѦ-By?}EI gF0~0ОT[CA/_ =(J$#f 5# >~mO CO.(!ֹHgȇnPyp+&8-!þ*%eA,wBomu`UPQ =I#B \43RaaDEp7G$-RuS$Vc"Û`Z oN>À]{Tqo52M:YD[e-ca!yC0/[@I}j&oA#]Ҙz?xɲOSkS"ehd|zΧh!P(Q_?PPW/|S|?_bBhRcb*D}PS•1Vs$/̠|%ѵ+դJ=TqW i|itk%@"(-)|K*.mQ>}vK=q)ڸ2uk nY:?Aq___u i~uL5gT "XDijt&倹 Bсn?b Anj*mU/ }9EJLk}IiliܖB_洩Ov_4i" ,̆Eic 2ax>ڧFwױ ~*4` Q~wߛSM[ɫHX"MMUErG`wjW)q "ʾJsa(F^YModE*UNy-f=% n\ci̷o 18-Ryt %-WفGY3FO}s.Z~W Z0&2˴/փ7LT.7苈eiGFw=lLDX-o#cD|mpKJbfGdy@Fee' {+tfA) -˷#=O5Aln?.[bmNn^skDx,I*%yp$@:`m68]!t(= S"$OlZ.F#AwO'LPU^@{)p]]RtM|?vpхA5|_<%T9VG m4:NvIӀލw3B0WGշ3PiL,"s:4F /i.y=9!/saңc}$-ܧk9vqi;m=^bХ_* 5mj힌x˗Q;vɫZ󉵮B/(i,.GgRQQuMg':V_Rc'H ;"ԩq#8'yתYHue㗀BIwǼ~ҡZ^&زO1qcGǴ4N`mb/a |>P0rx/wG\L~i()R00 N˞_옣t5p7Gshj' M>2@ SNߘ^,F(^%xF꽍ɮGr@7d3+bN׿ =}$DjpZTZި@O9f}I o- ˓ɏnz ZZ;+-9!++ RϢdb]}z=kiw[+#5Id+F svx_p߷G]:nYǸxtΖ54V6Jרǩ 7qUE9P^JMM{!0fĥ"墟ܘ4XzO mFGϏX{#Ehh +,Sɶ#uKI{`/3X,,SR"[S4A8uh\?|(ZӴ4_ #Mt"qIݷVՃcsBBe1v8OKo&*ѹ+o#LmnJ`BK6WM"I#߅6UwX̊G{5/r8J[4UNޕ;EE@^W?J K;2JPXm#G;s#n`|cWRiV9-]a۾K0?Y1 䶬bG'P+2@V\H[䜓6 QLhD38*qgƉ3c 0bĈY39&uSCMQ2}{8oݷh>7|}_$Hz`MU߂hY|N'fWӗ܂] 2X_/9] 7&ߪ1?bEU&ܴ4k p7$A.B }  p5/%pUg~=R>TR JHe = Ȝ6Vwt 5}y3&ŻKPj:pd1v"I?h` 97Ka/nInO)3û*jhsb|*gߺQ#b`'  p,ٽ*1óM_DLI>Gt2@0G`#<}uj"\<kg-z`JFJtu! I¼Z2y6}Ľj!j*ɶS,c&DbLw:œINʟB2ǔ+"Ą;$EX~o;}xI&^ֵʈiZjH"cOuytjIn(M-#ݜ* )QSDOvb;ɶkLմLJҢ+ =u_SDGj'^)S2m+=7"L~š -TyJnʫ(Yn+6[0xǒ6Mݣ ^wԸ@||l^CrgS?լ,>3c\>ncz'cUޣޒwf)>IҫӼ&>>NF;XqBT몡8FK eZ[2*2pvM^:)w=*lŜ{mb\Ѯd~}zx9z߫cnm0d9ٷ=7rAOqKZv4_ɰ4<LP5o>5h ޗ>cTPXiQPTkB{3;dcW,-hqaij&R$IS"-X\dU;]j4\*rnzPXf;z8QQjh5dufy>%W^ڴ, ) "$Z D=֗>莺cO^|+|7^)Dq\5{ ίK*`OLe'oiM2)_6G~>9&vVZyKuΧ`k7ThbnI}x:)c,%' -'*k\yn ZA}FdcY_Cu9Ubiŏ+Sv4=jxgIB;Okާ>%!%+*3>{:)("TuEAhܳ`f'/jq_.8{ HR_xHŪMOubN/o\ oz:;݋~ZZ.7]:!r z]teLU|;0WC1&Pm:5*(lD-oR+"Py H">PԝiiAYTG:x_1QO0zpRQ8'0n^hb;N]METCMy:9>Zb)Q#|&i<<D2]xҙ+[;Qo6\oy_]e6*Tuv6k,R/~^؋L;p,lH@Eucv|fG`*FQ[zaA&:v8_b 99܆~ 9۷gl ?G&43gͿ7ޟw_ɫe78_ZiӋlK(i0*&=>Ҽ T!&h58l`޶q0 dԏ EKKwQ[g1A@`lH@xͰJ fm̬۴6AR==y.09x3շ~PCdd8e)S}Z㪕Kw@"ð[Uѓt_TEex=_xg9Ӻp ': *,s0ɢtT@W`31K߳@2}Ԧ?e'_.W 'ҾWЅ+ C3X_b.Rs6`H~ޱ i3jhy0 o뻴6h{걥u~xKNT2GG5]3&gZ^ޚckb_Y7#^>s϶ld՜WUQjsUMs 鱪1=UsuK{h4=05Z7d0 iےJPځgRPhPu g;/Z\PlHq.ZCEl%X,e`.ꖎc3~Dz]8cvg&nbPKgϨI<ɴ4tK0cCe3G/ZxVGrisWwX/U*n0 J\H2|M'/. 1 kWFrh$1Ss+q 23!WEU)~ Or+xڻ(svH7j8hVWG__ ma+:ɠ1W 1.cCc31@27c}X ʀxOyDxN=3 2ۧaKpo7%arBBHx&~ <ٽɽx[/z2reψzنGK ٸ=~GwMX?ȱ4I#ZE&&Y7IVa脢J4&Ұ]X/}OCyIIQ75((M8IIQiiAC"ccN:hh'?DZق/>;dF' :T8(FxTmIvQ[w9G(~Ǩ2ayV/&waGA[0aSZ>끬Z4nr[WdžV$yD^+tm[EAiNvb~Q,/\^:]'O}RMʎ%6nmsw\!|&'Ą~;kMsMp&ԑX2E<^ P+asxws׊nMv_N9QE6BT\\|ZZAKTL5ᤧsKj-1dlm_kpB;R#>lu)7d3L/v>lQV*G `")( "fprgU’Uz~4bBs_yqz\2{N+;:~ oluRQ0/}Ax\b+vc^ OٛWΪ KFxw[v],Б@t:+6-+;|@ e]@QJTꆙԅ(f0w?_Yt ~C06d__ݔ B-ԳUyJ7_Dz.+QC ~"C=B0V :>"M*ݙ`d2b}rЍP ,W8]JhM)4D)5 WkO,UkXa9AFwp;?byza8z rJW{x*We˫2o>@*5ӧ8fcN)xa9ڡXH= f*jۨOsqTts*yݭFē$s3g"(  D|[}5kn[9s*U tO\F}RT.PuоDtΦu. z249o-pC2 }FŤsvSk=Ƨqc^^Sm{{?VߵL) yǏ /fFG'sBG:2s}?IY@=n펼ѫ/j篱?[;KgΫ=+?!!:v϶N؝e)?G<H*aVᰨZ|t٠û6ŦEEE%@E?(x,Z#!-H Jc tΤрOJB.71&ǒl.7Mtt'O iiAtI¢8hCh5 Jppqq}KY: )-s%!|7͞ryцaZ6/GEMqv J2$Rmw;htDym{u f'rEQ a= Gt^ס\?qk˵f64caA7iʱ}=qeZH`e&R e3)0YL'ktO-y>]XYT j\k}"d !rJ`8*L42%gwx;b2Pu-m|Ty-|ЎY(`\]U'կE)K^j!R$^%GcVb 8yKZ@RXYX:!x]YR5)UfEYVVgW"7mv# 8?fYU!ٵެr\$.,Ƿm:Pw.kc÷ .\f0>_z@?ͫ\?b7cl| 11͝&Yh: ̬"R!Yc<$aCHc"d'xh=d [֩K ~^ZǶlM'$Dk^W{ MKJJJr8靶,{$6[x[ `!IאM`᷅BGKyGst4*S*:00@҇{9NeFQr4!-ITzy!x<Χo>}]rZ 9k?+@YctEҙo/4)&I}a4})WRR/jreC IJc/̞~(<9m0GKoL9T/Tzc^ޙ/We.'z;OޝyyG޷IRlN}>^v6g1 Hi6/6jW Щk+7| 0q 2ҷtYYOE"dkeָ#oP[ 0 {K^jJWVo3=F}/w\U OMrP>G.Bn]?}J>¢;+t_@!w(VØ]Cb"FuEYSU:`;xfL KfU Be_:6CLYUNu& b~(lـH12 J5mljfR#Bu#p~m?}TK緂w.Y&wek5~^,"`1)3cUhz(Tڍwf ?撪!@N0*jT&_6Q^JTjFub^(k tmLTP7X&FS+"BXp0QRAYy%w H;c$r⨢"a&z%j!I<.P\0qܫ@^ttBG0.7Lݛ|iM.x@Su?VaG "[  57n6@n%jy+/(tKƘTZgM/z2YgslQ g7HZY@7LO +7F/w<>oڛȣ1Ơ4ɾ[4=ZXˠ&PR*-6/8¬I 4xzz /5-hD7 H'^`'ۊLQ؃r u^R*o铔B^WXy%;21}]vCu:CO3~8nrvUKbJu7F5n#L9# :>"z:d1J֨|PԐXgY3e/zu!= qq0d-ޛdj[zXO,ޅkj/vPo=JϛXF~=/ԂϾ.>w ]L&j"x6p\ 9MCf{/N%xXz`<`&S^OU̺^WSQWoa+:7N@69bnSw*̺M1]Hju$loڕʈ#[6ٿwj|H*S,s?>Hf!2+T$g Q0]6{TJej5jl so\3Q#I^TA:XY"IP@W!D[FV/UΙ ĽF> 8,&ej8šZ/2Qg7R( 6|BQB9Y(|ͼj5^Ssl|k6 I2[13xOϙ-)z3gh~@K::NCL#P@:3c|)cl ݛz-ѿx)h(X 3D;;Yݥө˗,^|# S ?N <<_.S#M\(dGE%j^( segu{ga˃SpYv5M2ota^=|Qzc"DZpu0)ƺD?r=˴6kfxo >0ȃlϬՄ佸X[HG (hgNRZb9! >ZFGs:R}kR> 3G>(5S`[Qlx]#=}7jDcZ,Ð"RqnFģL7FGj9ݝ5 Tbnf$5WTz0}Vo06ͱ 貙cPnSwY}e -7Ֆm.ϝy龲e1+y%>>n.QtZYUOƟE_mu[޳jYa.ew ~tg wWe̝+؝iؾ>ΓB}P*ݭC9!ԌIصԨ'6xzzHzъX_{. Ihgo?!'8rņ-sQ^@ ?4N퉹!H$zE|?d=O:~]@H+q0,@ȗ8He3$mԑO^cZ$^Nr'J?LD3`2(KW#ݝz(\8:S\{o }Çոc0BwH*3jHkleº{i[z9ed }qz+S:{>S0M?@2 %Ru>4/''<::!7wXTT8|HPy4+$辿}mg6͆Y=Zmm&yسw낍?5XWF~DQ 2/TeOF^NmѱMՂ |G6[ΕFtptsu8n+oG.7nMOxMA + IDATGsiR{R5-.7)!!._ѻ䉧u}-2DQ8Kd%@6nT;:=nAacv0,=mR8(T)(=u qq]_wLI sj6g93YP4>p̣{ :C=^1g!Y.!8auu3{)u bx}gɉ/ee7;q]ô 돉o2KwI\Z7aMΡ }K䴲7Y+Y]ӭҐt`ݶ'X+s'FSj^ù7@{'&h#e5G62rue<5M=m $6r H(Rͪ,ЭI`HVjO=  A?I}~!)$JS=@しT$ Hi䢋:>c<H Af|){ku6]OQQ)Z\*59%Z6޾emy:HLzXwfƣq]z[X<8`1e<0S(PΚPoz#m7ϭBbbMuD1"\Fi%&"@d գc"n_i<}1؊!  kUW &_olQ3+h(SeU28Q%ic9ߘt j t !( c.!>H@"EhV~fv 7„ S>ʬJT,Ps}5~{z2 Acե =: -<>~fcl5%=ͪ͢fOI}?M1 56w"w*@f,v=_?Yx;_&oMK 2) Mm#7Z@z:vۥ]> GTiDLS.s8k q)AlkْgktxBTV,9Ǎ|h#`K#im3Qd[A顗UgM ;}h]0yPS;*@H\*IZCJK =L9poq H.2?a2T[{;FY<|^#2Z:4?f.1n#Yt࣍5=ƙdRfE3rZnfOe]0D8hkQF1`ȗ/0/@jhCwT??Mj#3 F gy Ďa!N~9Ń25F;z?o15|ΝQ>#QQ~u/7F.嬗^7]KM\'=ogsv~B򊟅v yK{{׏ϷvܤDm*g*. 翕W/+]]#ysrkp%펖zbV'GJK Luf N.H[j>Uf <^ B.GiRMM4ïkZt4:%JWI͵Wc4Ւ5K5԰P* 9ɫ=NZDmL SEuc_0 \im0]2@M 1s_+; nc9M4Uew]Y\*qY˴4ٻקl %2iͶK^R$ic7FJ61-L]44ҤoUÁTYŵr9JS唔Eio T&\ؘQZ*dd2JXrX3R$9PAj`nH;{SUjwgFdEQxSC.ZiP>DQW׏\%oP0kE6P!NNi7ʧN~F9ΏUQrhT3PNSqSyaUfRPS@锚edz O;ڵ8ff"cc}[l-44owY> 8?L[ t-`J*L2JY?tǻwbɴR?)LksO('>±/f<[꯴F0X_[Pٯ%oJ󨅳'6<'66h-qaS4OsJA_m۳cDp8Ó¯)60ڛ&&7MA^ϛLm@۴+X^,9u^~xm>"iOiYbC0kb3;NZr ʛo=8QVhU#in;+M-!%&㎆W Δ u1D@A/B#Ґ3i>@~TÂjwZӞ3a>Ka(ʦ G)smO߽Kf:8qF٭g"1)&ݱ2 Ԛ ׫"^Ix[]X\@iSߖLQmp$h_a"i'~RIVRn6m @r=H0x3_8=g2Ǎ<TjQW4beՀ!0J33@䌦u)sM/l">6rq2uwP꓁ Ie=z׌'B!\Qyw/ QѱyP_O71uraֹP( 66G}VcM)+3(ȍ羕.[YH*dmw^d֓cw3AqN>ғmu&bB*nE3"S)leΙ7]V͞BY1JoP7a 9IK\.d%+{.?*`0m>/j6|;e(b ˞˿_\<=H^Q ?xmL!vv.|m\\GjQg&XggY~NΌ'NekBokk8Ε] So(Jѹ۷omMs:Y?̹81 b˺[zsQ͖>5 03WW,(vY/ӗou⭛cg\&иM~ICqqt`Bo@K+~d[,ă c[Ԣi{Iō1RcX_+O1č Ž_h+]vZ6YRFگş?@Wܯ:qMPTR9Za>qM L,pw§^W夓!{Z!۹Rsu"y%ُ߰stJ5.T2ƕK֙7UO*LgaX+/~^/u*K6g/YM=yMHЅ۰`:Ki9;0q }Ljx+įɾ"ƃtuH ʽ~" ?>(b/EHJLT a]KEj>hOZ,@5Ɨ27A29qrBԄfп}5Hk V5+"gk_@s:쎐ojL QK;5($R%zBOo|2ED BRx~! N'KV-;ut}X]IZ6 IY+p1Rr/uh]Eiu 00FEF.\7zG%ZLz Doͬ:Jق$3>/dK_ _'5IlRI\-d.\2%gmk]BBȄI%ccG.,b:I=)ErrǴB!HXVsD`⥱w\ʂrsQf,({m*UvHUֆBGGab"IpxnƕQHsCf BGxxrIщt30@bttbbb42>>V(tLJJNW^9zBBc;6nqecs~2A-—=]GSSԼdPe-o"gÒɕӫ3i1F=̩F471j+Ȳu^O:*ΧNdzfZ6j eVCֵxnik:|QQ/UD!_c9r 7P78 {Al"8GgݮX~e2@gж]`cC뭉y^WeuaoLX IV=/Fr95+3Qw25P(DRӖUTCy a|gzLr/ꕀJ5G-_/0r]ٓu-L֌JuFy5T[ALW [L, < zڈ0QכL/[d.im Ex!Mohp1TK1K*$4QԤ=9g'B ]S+(ХG1y^ J(Ft칹)ތj!PY[׿Iwf.ߕ.*,WrmoyH o1+kc|Ox1I+X+#OHX5u *w'&v3M=w+O|\0XYjg|a]@ٷ}9QQ[(dg{~QO~\giYZ@C[/zʿ4e=,Z M> ke]4QձwvFIE{m]n̾r+Ӏr4׎^3}7/]Դp`d=G{Euuk32PAP *{/Ѩ1!ǒĖ`^" u 300 i&sy͜g}k{ӹV/LDOX͈ s!+KwK60!y̔|.CJNN6-/""*U% +|f;K[uKz.Q{ܰ%ä7v+f3|ju@D1N0x RF/?XӜ\V A\IS/>lC۶>AX9ݷCxc_$ޞjRIis*o}:8:?3ɺsZ3C^pH_aL tdk]봧1x8B<ܸۛk%^#;  CN\Ю\^ lk*_ގheLu2ʊf0}ü^8_9ڸ}v_}G0g" ``ql͙L44OY{W'3Ht3a7ttZ{<c¸p(9P2 4jM#ӏs0,JtO )T8@cx<U?#I8C+F aэ:^1XL՚%6Noj⌠^ɦV0^ёVgbHܫ[3j orB{!mN_Kd^fu.Hl6-s,dO;ysi"uW%d[)b8=}s Çnpn 9w]a1a$''x iۍ :ahi[ i)  (s?2ȦV}0B&dݖx~|v5YTDQDR-M/ B`2EċQw.B֏B#^ZĊjYD"Oˌ dg)ݫY`-|5RO=h6Fq%a+t>:BCS:Z 27Z|ی;6WľCg}1+<7< d2P + ~򏨐߅e'swX_pt лxo"Hw/? qOd]*+~weg󎞋.ehڲ..)ҍ<^ѳ0DfBLL n ,mq\Y')`e38rK*9ykVVϵNB 3gs-.iir-|~֭ qܖLsa1q[C-ڙ𛲖,tϙ hNMO!=y IDAT! a2v;Ox>'f7 oﰋپPnKG렲GqaE`a &K*}Y lُ>:l&DF)rRᮌ,4/itnEyǿ:{GS ~t$Wt:he&B?8ŷnvSKB40*5=,}7y&DEO޺v؉~)*ºoaNދ?3~@*)((`hRR3^::`_nd4ZCr&_D8k氱f10@AoQTFp}ˣ'QL[ڊgÂ(P\ITIu|S$Yh7hr_LJLm7_W/T Q;;G\Rn.H `2ZO_UYls$㗵?ю||/q:ȟ5 tu=[7MLfou? Kc䗐YO3)7.4:.RkLaN|Z,LUE(7h# h:.ҕhFGЅMёdg!VSq Y :?EE=$#]OUwqx|>? 7s,^yuSR)}ݓں;2-z1ftIr%)ٵsj`͊ b0vYRxN8tuƖ݊`(|EIvMA4cno-E" ƺ=iXikpLZiES;&eVcb-WlMaQC uA G9rdqž+)N/aqϔ\Z~'HyE{a>gG}zM6qMjЊyJ\ ( poșʽ@R55޵dqaSzTI6C.w3Qhc<ޫhW# sc;HI'|YJPҞ t8*f TvocM=D݆~l0itV2U[>cI7O\R_d} aץFÀ(El|m/0~\|; &O7 I\ HpZ45 XRΨ{u#z.OnrE $2̱d";dSYil=j82*k3S>2u{dEO2 5e Y7F;^w .ep326DܸV;Ջ+9߶U]I sfgma~7d[O)m6rRޞ|&Mw5x~0`Uף\% ^E2¹x+S:y{w$͢0`б1e2h>ji^{Dٰuk`HeA t*N Ī^?q3ESrrJ@WZ-azCmAH.I3ɂ겚A%R\ɷ=;N_WSZ Z>dR:?A9LnN xXd;q-H7W9q肒̦eӪsg8~ÛLϾ yܘr#ъ bM.t]eܝG28lbʭtZɵf K;OX18{+(H3`)xN5gR'3uKQ b ydSp'q@tBFyi1n֝"d╮kXSװv#~n]^3N,HoX @(zR(5qcF ]Pfc[R@ ]ȼK,D&ꅬdM͉lJˬ$促k FI{@ UYu< do4i"c+ڤ0uEi4`crh(n(~Fze R*/tW ;"-LR@1M.1&GR$fe?"}?@zgR؋Ű<^|=]Y+1ΊC9E(T zi[`Uưsl^) 7&i^9aD7qw<=Wϟ?|1OOr!g؊D٭J|cRwӍ<_q!ß{J:Cۗtvu~MFKW:jq"z-\j+&&ĉ5ϟr"?+ =g,iHHHI7o~39d\{z :kGB`!no4-+'n<﷔p=1p:ű#*bПΥ9}ˊƶMw\}݋=FHIϻϔZzmeӧ"cg3AG4@Ee˴ u8y5xvAi݉jy;w;m :rQB7_654 ﶢ܌zWE5i Hp@B9~nK<e5`=3lO{W'CpkE,|(Jt&i pDM>:nV,kq$oMX.4n v(˘3vr sHRV!6vՉKH1AG.hFG)5 %` !Ibi_bN ~a*zG[od{" W٥_"%Bb@W3x|)6 O/^;HmzՄ֭̈́--\/=}*7:y'>y&$ݢ6[fK9xfKo+)i.DkzVdL[ON*[<:XσV_癛no. 4)W肹4UT?^6,}.J!GFf #L44p.]^^,G_aDZ"| (|C]1] {AcVf<;msw1Ϗ$zkg Df8Rs *G!ޝC?NBs7Xo_ڐ&?6Ɏ>وc굈MgNe?[?]xCdLj~<޶|6;HYS1sçʳͻ@rdo@v3x>\̓ا&ƈpKao66~^ƺs]I ;cdWA=Q3͚~\rߎ:8TCO;__}SIImo33̝pT>=Kx q!4q }YF!@ϱq R՞ IgN`` 7F3Of@ [iu jlF{ՠ S&H?@шhbeQ]j}$(oB=/)V`e`2o+/gRf8zCXQѥՊ֧2V RkFR,&TϮ['<*@fhBy:} h+_HNa|H P7 @@6HA}pfI%;q>;|q?SN | ih͸-[9 R ܮaozR,1?VYp[Z033ϏM- D}\Ϯ`NCQQ8dgB9lEWOJoWؗ6M߻eϜ՚U)r7m~[˘}}ϕ vs@JJrO2bo^Մ$%%;֚#\8%e#S|Ʃ'%&'`\s ?II1E嶈DD m)`vUaaxxxe-3LVQW_}q s6Ĵ4L-hjjyܛ|- nLZ%Ϳkl3+?L2~xBl+G]I y{dJ{!Wt 6CC) /׷+v=n(/~hqE!#ަ9,z]{vzf; ~;毛U|5X_Q/I{\L Ym.ivu՜}B Vp=pFT/¡Y\=`j5W;N @ !)/wìXg7l-*!kNTԜsA:^sUΓ?F:hO(U/R/Ut1,W-z_oMyF a~L 0r0EE =/6L>޴:!.E&ҦM+As{'joY#&%E=@h'G˫E$5 g"S}}5BD|Œ:u<nM{tɰJq9/"Y}G`uEв-/[oP̛{$#õq9=W_^Q|ƔhkcɣYKD~ӫIP9]]I0T?AjzOtvF!49 5r%/^|orZk)Wkf3 Do&r!,1'iFEKh"ݠ6zS4Q]Ϭn芟Co ѐ jtcgZ[Iޗ 1R|m3fmhp-R)Y>}"p˝KUN ^<==骿K;K-h888/ϫL պ6\;W$yIS]5iXIϯ^k#(sаl 9?uk2yWEĴ3 mx!B/=\1c&=% voݽy()̌9R, l_bpވ1`2ӽӧecPK`8Q0#Ȧܕ%ʺ+FN6?U%؇6ӳ~Tlj;q٬oremsJS"pߔ@+as 4:8RmUA,c=AUփnoy:~N]s.x2jUSF#PX6nͭ՟}萋Y[>h6tv>y Jҹvy`{*m/pl*mGk!)>$~.iYFni;&́;ahhh6Qg;j-p` 5̘@ 4 LuE[g*YYg2,8~~}:J4DGF%Hk Ȇ&X_fH0: ^s|%)lT@,M(B)hZH Wuqٚe e:ohM"Ҫۅg:@9(VM[ùo< e-_`X=Ng!R- IDAT,مPlSx, faz8Rک2+mC3ԅ[a='e8HgLew ]l6I4nF({J!J.E:CP'O,"tzd9t뤔2EEY*^=v([ZYŕʉc}%Z=Kh!} [}ht }ܯ~-kܹTʞ6-?11vLصkoR/HC|f KA~g+;lL`VZ=MwwFyolOJ:+nsnu6ާ¶7hׯ;gM Umek#<4YC8ư- ?J]+Y__S7*}yoSCJ'OZeq&*ϻX>ّ(U^-dVrO.}-_*{ U@+z(fM)<) S̟Ru'+Ե}+*5#3lPuZ+ԙQ A\jÀBi>%2Co)"4(5shn!C.iy%aH!``?mR:Ø(w"aX8=z^uPNYq|/&& P ֒\9f;zf20Y-X.geŤ&%bïR7gffFq,Yr:rE i`vZJJJrLL ҿ_m7մa€ٔ--/=Cј9wb;6إz+EQzt.kAbbۢK'%h:]۫<;Lxq숭PŎv%^ᅾALd9go]~tC3~ijJsn#bwe%B4~vՅ#֔/-XRZ̮hkI%W~FɀK5ne?}7"0cQc@jDmk) Sx㲕>ɧetce~#7E N39k=·υA&εJ= z}QrTg] i/I%D=1,&4j]蝓/_$;qos>؋?A]/]qYG]IIyA7ۑRmpB={b0]5rʵT dkY`RUc oXRr%34nRªd;M,-W1+4oke *)+VP{tzs NXdGݹ+혴h7Lקy__ǩOz(ۿOYbp89--W)+[q-qLyz rHJ: ГM3K@5E3i{vp21ȶeHʷQ|>f\  ~Cچj_:s>0c+rE8 b@'csRw7V|cCGw2ꇕH="Y=܇v&E[L]o=?Y#stԂݺ4YraOY>)V@ow 6 (,=-1dͨuտ^ܻ亓wUA&/^1YIoRKvƻnNhߞ~2Rɩk_XUpo=-*{RWmu\BO1^74a$_Pj3{tv jR<\ٝ"8olF6#6lwMIQdB-iT7+6V qf]<+\Ћ(@J5$ѯ/#+\14G7-tv^ PY0Մ-ΧT,h)B&;җOw %6ҟ!Wyp#&[#[OTcm.v!2Hbٰ? LlDbr2O-};vRmNv}*MHtlmH\ޞ M&;[@.p=_a  &;[Ecy'zγ~9koLtQLŮ7sy?.(/!M{ q墫Vwk?;'##K}L`:cˤG__Qm'RP"1{*|%t+!o\ߔ1w/dh<_?UIMm:a҇uEtQq-[U{bT!XHXɜ)WHII2iȐ 3ux5lPspI_p226DL|fΚ|l;;+R6BG8 )p _Mp 0eӦw~30PRӍppyy T*76[j<8 #87{|3aDOi J{̝<;|ܡ2r] EE9'OlV;˄saTye[[$7D ^C-^'.k /y1ZuY+Y FZe&i /U2 @/6#Ovli̫|k6#pY3zEaO72yrg2rkEB@? RN ŇZc6v Ǚi2p%3ƽS >gѠ(00 F Q~iϭ67zPGȨG!6;Ftˑ*П`mm~y52PM*Xj~’^*ά5H*a9(e]sb],{ot־PƘw0wR;SV0]kSťcE nUh?*!fJ[ǫ߁j_ )(Y:+ 8ZdjF69p}M HX -Yz뽷~Q=- %__+[+L>qٳgϟBʁ^<?3s!>k%EQ6^!4u5W811 G0"B/s9ii8dW( )RWXNLd`l4""܉6?I1SJjjbrr9]煇&&BXq"hn+,?3myO~6ut{^\i=C\khm" ѫ!܆&fRkd`DuzDZ L ʷt^6Uq/:j ϧSܭC)"2/O9_ejMqe'\7.|~bc㮈ؖK S٣ku `45=1[ W!mhru xLr2rRW` ͨMj2 sd.B6o3%IÔ毧u _KXB6~638ZԪCKҟ&h@: JFS]JL/ՂZo=emttUD 2Tg!yuEF)ܜ^ !zFN䷺0xXݣ= *>?Mv+emmWc]s/g87HQͤ6!ҌbT`§2>v/J5hCBr Kg2KzoӜ,AXl[UfCfiUwo23TE=PŭQ~%卿 z6fҪ禧Ѵju)jEhu,~6AK_ XWҎ4]՛;)grK)%C~S=D yK]mY<`))'aYsg[8HYA슁B }wN=9o|M t_5v:vb`D,x--\o*.eɒ[(if&"",Y°:ϸJe\S]U啙TLp\E5CIdJGSgS"Šҷ?)spxmO!Z=3arm#O7>ւÅex+W:5 O̯-ZFLyӓw6ݱ6*?Ϻ|-CNww{8[E TxaiO<M2d?:M_Ti2Q:]1u)(CAgQLJyQM<3\uՏG$K7 @&o1c(6q۩΀Q|UL& àqq uOY!DhNcܸ:e ~٫f^-2Vb*=bˏo\QlPO&Cju9ӣXLźG@o? Wo}Y[noQ4ZWDt&s֖;]Rckv3u(ߕ!-nVL<5%%9)p}ֻ\wч\HqLL}*k7۝صp?1:ViЋÚ StNO_-EGs\\Mp/qaXL@02""S.w ;X9֎a0W_Ks(%6liR(I<^vRDiYY1QO*{[q3Ba.tSvMGG:W઻\*t[[tؑD y ͥ^`kgH܂~K7]c5}۷ArIY~+Ciܿ?6~djFz?tr}|vEpQQ.;FsH|oEE2EѝF!Vď1E.MN528vK##Qq5-  3A.HhjYa!g_T?0.8"@ IDATۚ }bu;_E|k ~}-7RiF騖I\]R唆ڣ<ֱ&@KAkEJ7p tZ*nN lv:e6%DS}rKZqZ_͖&ꮖoE+6 e5qWX)fڈ]?\7mFAW'XuSCXx.>&[+/QЎnP5,2Nwl=a@Z^ p\f!A)Ӈƽ|oR4@+K*ަVQkIF*U&$<+k^6i: ШEIb)8ի͡5ki-5X̞^?:qdfש\^r;Q] * E($Fk5V@aVe;~8GJR=A^He^$[۲YB [P7$>}=^~l'o Q!2$_=Wϴ2&myR{P\zIs觮@;7zlBfƙu5Ú0Q\)ra&]_\ D&냋-\q*_VH垧XO+8r:Ń/zO"}\s׵[Viͣvcm﬘~@`@&r1G(̏7h3wC q*0P7N+|Bqݹdc@`Ё ) 3T PܶeuZ8;: 8Z\3xoP1=wM;u⣟Q oÿ߅[\p[+"wڶX-Șz-ãqӂ"TC*Eu<=GB 7-~dHkltV{Jç$K,Jd9R%_mGnê >Z_F]njDzڔ2e=hŕۊ1nmpF}Λb4@h'nK&4 .q*p&KZlCn-M7Y嗕=j_V&!76VۮYoBgyff6R q.\ى:@,*Ě-ˏe<~vvVkSf)(wdaEf\ $FmoprrxU7R#]Vf/%VQ4p{9Ûx99kϻ$yYCI/,w"%{UICj:׳ނV8YA͚񘢟qٕbinG |Gv&)J?kaӍB SK|K$TDn=`߷w^{tõ=\qwv*Œ=@w"Zrp M*TcWM`q꣚|^b'?G/(PosJY?8V6/GĒoZL쳀RVs&~^ M7÷F 4+SOa*6([DZ_oiQ $BKAil8ܔ f:鸝3a1-Gy&> ݅Q\Vtܭʞ/0\Ll\[ 2h @ PJЩNy*?590zC ЀA`80 !Gz]bl7x.h9A`(~cVg]M7E뗮L1 ?-PZXly"75A:0 TC8 0WnK5-.Ŭ,n5iSq$N2v naJE [ Q("1Uԯayd{J1*i0 s'׷"9A݌c ?{>/}uȅH#kRVmߴ}8lKN~coTF |ry?$B>dee/[W<5N\JBQ\ܝljw de$ƦM'ҥ ˗/*!,HŗL YM99qIz'^;/#0I$٣>Hٻ/9+;+dS5~9q@4pkz:DqZ-)  V7ZC: WOi76a@$/,&/D~ohv.zC̦M|moHr? ;Ny,K;( KcZ-`Y2WΓx0`Fm&>Z5+D-޷ZG!p4JH#Lhc~#@1,Cs9$֖W{R\<CVYh !['|q᝭ss8˷NYd{V3Z+mtq+cmy1" :]*_]M37L> hTLؽi!TF4n2DSDd C 6Ւ7*EgeEBPP@T Q :M6tB,͍C] ~gãׅDQ>c|J8r@Muq PPm6": ;tirBgӅ[ /xMmd:4xòcSKM}e:OQmQWԾ`%is͊R{)3#j8c1OwpstzE21L)SxƬ_~YĤHi-(6/jc]X%-ܣrbm[?@g!o=?X~;Sỿl  1A^jcly_[|ژGzͪdg0Dڞ&ƑɀM+ g; l4:AQl15wS|['BDD$\Dn>R9c=P^p[@O\ l&&lY[̣͊Al.!y"ᰋ.n#t̛ȻI+P47J&F1AttOw5jGp̃P2VY;۱? %tat!~ neA9VݨXh80= pY%"P;VGmnGg +!Dn kzQ[`U¶qo(g*[|D1(?ӳ`fimKpxp&75zt:h}l۩jɷs[i{9fE7`2IYùjqWMXz+ZMbh0\ui7 ?OO'On\|?Lꯏv FyzMM_gDi̗h%?l^]zMw\O7z o[A_mjYGh!Ag{Kuk)E,6)\ ?}9Żmn[mbaȨ!;D #)4JQ"00LsQ%\n@7n"^0l4H2;(/PpQJWN癇NyTp[[ $eܺu&8sM6y.G1h}Sܪ>dg6m⒰~0ӛ/wsEfZUʒJ=wZ r7mV̑.e%sUM6؈R-LYl)Ln9Um?}{nWSM'Nױ( o[EVQ!=ҽ3v+fK\4@8HU6@ pOqu~=[ :]hEp^<]z(>tۖk,V>bmɅr qUǵ4Ov N;mKX- S=.r:b *SqWGBXjڸ:!haW|A:@@@ @Bt",=0a傝&nnR6&R;#JTQi5GzMPPI7z+V a!N LOwrVg\&Ŋe ~,9 rPgy/;Xy]6b0@F ^||Z['2psVq%Po@@rj@j܂kQͽy$1oC1h=´تE|8GDFzTFo1ꛙ &eU& 5o>U -0RK MMb5K bߙeHt9oBVkH K当O6^zù,\a=IM JOlaLMK/4H:_?Ucl$%>~ FsQùIWO(=|8#==7m''e Wwf !㎎F+F;{kIYM- W]̌護~Hqd@o_S%z=W/3>Ő[F ^"H:3)QT;VUUVN t ~ \㉊>T0n#^8;sFHr !$C3|}E/3NID`D"Y;H0#ϴ`z4ka¥,X]u HZ6AΫ˧WLvNq{ol]\E{J-%Z; a0b%1g>Mol#4Z@7P -@Nc#l)eU_jZ|շԚPGOu(]p'ͺ4Zjy.)PPJc+ ޓ ,dz=Oi avtng?<+j!P5&0 sSƜ" A`mLnR>bXGʩzƣJ:zm'F$2D:=0C{iDhIk\傳q iEҸmNcRQ}=RdkLoc6V>JꅨRE|<<}p[1FWGGҹCD>@ %/]pFPN彮D@HdOqaEK:qYDQ Q1RnTL l۹777,gPDb7@@tz#) op7 Mݽ]r/3S:UD927>Ϸ?2 _!5c;JR[֟?fϘXMKO!Izj-&S8SX03M](=V-ޤD\ܝf1$W{E5˩BF1 Ñ7trqrrֲ<lތ^7sXXrq[Qw5J/_SP OY3k-}lÀA"[`sJ"S{gլb͏u]goF$R!0?Pb <)c'Nt$@xPiirGs b93pr:$.~7O75) S{萖xXo[/MTpa$s Ah\@AE5;ӟ]r;r,TRޅҫgyEzm=\ !3BCofcC|Ojן>Ys$}hzaRlj]s&IШ4ܔ @jTnTk#hA psqP.e~"~a=4j,ރ6.7?;P8cW I( N@,2;{2xW ẇq sx<Ѭ3LT{R*YeT8me:z|O(EX>m6]姾 ~"&u*p V.Wɝ".1>rj85c,S. l4 s&쬕'- n?ü bם-Rg^| ņdƯwƓ$/ kX:'{/??x;v<j ƋcŒz&R]`Lh`y5m_7hKmF$P vM ò3Έ3e;$v~&Y~=^^M,>Yɸq޽/ldMfɿL2:yrٳ8ԊHIOϕJYn_uv7k/we$X, y,0_g@ss܌N[қoIO%$(fgg-d&2*m99+UrhCKM_aLc{P]YX7shB`*kSE|YzA뙆DHAew~ٓMUV*_c,ִf,01: Ed DO 8Նӫd/5ЫXZC "T'Nq8OKoh @!T4==D&2؂htZ+_j!E$F ?}r wB @p+RG[Y!dxCj"+9Q̪z> F-pfZD> %)nsYwSw5?0ut }lܖ٩i}E^$2G}BvEӶ6Kf͖.TvE7C~[KuCMzVkwnJ,ye0a}>D[:hS ;`t>n8([hAZ݈@+8=0}u}"Un{bZg{#$zj}qq/nb\yD&nc H).D"A, δqi~jN,Z/xj;^&Dz J #ﲇ+(U|rG4Z]/O*UCU ' >bGTjɠ J!Qՠfi$Pj&Q;zB Eb 4*E,AJB9diDֺ@_q=뛤BսA nʫ{];YjZGl^Ծp~708(j_.Jq|zwzz_ )kw2,==%JMKM`$V:2bX'7:=Xp8r`RWݟ$Y`NNzUUD~~7~^`I\Ӄ~F?'͹\g??߃#6YA,f$Y"jֶy@)477#yiƊTQQ+lsv~c#6i,^13w۬LħO^U(ga/s)mR-`Yt;dqE]}˚5},AfTyɴK UMFuU\ų)GKt ;@7Qq96a20s2K+ KI͈n+Q^D}t8`Ji[n8~;j"f~z<ڰݶ-Vew۶t F&Niv7=~ؘ=р  HFy4u@reAq[T4`xLY%ъUEi8AmLquz=]|2t 3\r5R [V\J3(F# Zb6^D (dE*ScB2O1+Q QcQcF0FS*:٣]jweۭ~6T1C3I g_:XwYkV{Z& 6)F5ZPpo}wx^ W#aՁ~ X:~௮SHM+-T! hBL)s/g{z|wطm7yl6o,2X7jKd}|c՘ݽ||Tܜx=++C9H$S.奰?I6ҽb&*V;-+4P,^>*X0\vun9;B֩Umy |7O+|i GZqJw:{|`VDVY$s >~ wJAPl ~L"G kԵ9~qs}69C͋LT*2@<(WڀGW%S^ǒbg NF}\Gm&mBmsFﵵ+3.5!V>["S2ҕ(bHe 㧄ZvQGB8'b51D$/w1j&LY[ȳt,}!A[yr" p f8+RhnD\[Uލ[;V"C]*"ش8b(Κ?֠>-SE.YZO7T om^Y+$K'"Zh`<;%i&u\a'S+,?VzA"a(yy)t:E֬d[= *ų'\ʰ yy 9%affI룛kmr[\\߹+담Sq.AѶ&[<۲IQW1o抄[PG҄{UQ?+a58-^mYKpb2s~Kp2:Bƃ$\!#??9F"yd#*^DuGTy赓g\pr1#L?^unc10 Ɣcgffd99l6uW`Kכ M%vkmvb̺կsNQ;{MWmLJHJy=.fɁ/uUy'O;>5w5 oltQdlu% t}B!Q u܆kb} vc70,n!톋q9j g*(nNP8uq훟6W3Q.2d+)Ŕj*TȠ4i6˥YyS=P@@T~W;D6Ӄ3oGU!ԅ!7FLZGmz }ϡJ(&(j!7(X#*۴ٳ|>^;û W듯XRG'CuS$Gv;Ӛ&]rQY.8'c^3W^7Lf,Ŋo}}g2l nԃ.`>#33+77===}~\2SDBԅ )Qfr:͆U4z oomyRDQټks6sss"u޶u^+Xl ȟIsro{GEym)`^ `FK?LhXۛKIz[W_׮.+So~`ڲgX {Lܜ`C7cyMĴuE!~t7E}[eG/)VY,M~rM}H񞦬 oZW6|Y:L(f$N=Ea6g  F ԫ>SZ ݽh]n}tnE y-:"2>O UP"jm]Զ!/A@iz&_h2u),UYHo#SCb$=׫ӫJC3[!OϼBDMdU#`sfA`BQq6y\K1y9Vg jd|Ud(_ :a# % 0z @28D0Z(#ٕC/ I+4jbiYW$LP6DKAeqQ+/gr[W 0+[]-v;i۠s?)ͫX^W%//bI23'LbXo8gj=c^Ī*%ϱ*%HgE2yYYٓ=ZtkܣNGOm%qnA5msK@o?3ߐ(׬a%z^.W{Zf˩JO~䕤f^t8&,##s <[* l6_"a W8966O$''' smm:Y;nAN-x>85J$,脄QN/X_ r>p')YzTԍ,de&/2L*͋+m z\^m…w,=ˁ]]VZ\RAQb8){ץ5/Q1H"ǔ}e*ӹL{bR/8-a2iD򙓂&TkjkfUQB|.3fDVa]7%?J543A,@ :+ ,LNÂ(HdsŠg;ysE"S}0Wgjc+$-w۶}rf `q (C5_OjC5퍬BEm+.5fWcR`88#zv NDEJ@A1)GA5̲*?}h⒚wadHq!1;jPEojiAfiMKA$L{8ss2mB4eD0Ϭk;9*#C^9)̴#L27oX80]|E1YƙR{yo}ٙ{\p^^{ŒHJd`cI/­ [%#xљ!)|=Õ,?a3f@UK(f&%D\yÄRT957|:2Q?|/u&*`\ SN=<]u&(ܻf؎"SsbZppEOorY6?2!՟xt\4ݝ%/bg;;kǴe_U6>rl6o^?pv憇[[]nDD\-$==d߹^5.`cUhז{y~TUg99`M7b;ꑐ/-[kٰ^6A$BVVܥiז-)q]fϓWtݓvIwop@;β+ &t -&ROzdI1{P?tPmak!~6B"o7k S. ?W$F k.ruD ua hmx`Z6 \?B A r%ߥvbɱ+x0QDbmT8Fl!J̩Nx@i5VE4^Rbl? )p[kT4 KT Ѩx+a6"QVa zCD Jwq 1$P jfxjWSPtpR R+}Moo9q RhƯ>Z'(Uwep. YnTt-cG~jf8L ?wVciɸ?g=j" J>;݂uSi,:x2-q}7NЮ)4 iTR^}k+  [zĔ0~O_Oů :Q ZY*?oQb>D<{"RT^%SiW@@jjmjU3Nx* 8"Wj&R:dv2k>*/f/w_*TdVUYHCJ>n*c @ʹҕ—r IDATZHolųBUDB`.¥8, .*Jӕ&rɢKffC*{{7{3%%IN!q#ʨRAˉ*/k{ 9bq0vc? KM'O$`zˍR(l^R2fhɗx*6)l9+w Ewc")J{.h('"RyE50W2t_;AE$}If{(xSCaC  7zAe'd7.7E)Pz>dy:8%WL'HIJ=X4¨E 4ӳ`9.QC7{*6PRQt.bW1:+j2e5=2BBЄ_ A22[ P\z!I"aG24\( t2>6ѮDw}@`+AaBBI*5$֦*M,X{ΜvgN]ɛ[PH\;1]0(✸/\ݚ iB~ط<r%XHVp}E-a2#@zwl~|=RRgͱ/ͬ S[+[Rn_]7d =Kos@y%Y.܎w\8.[2S^3PC&DѬ^1zDь>Wm€zUpthͬuuU|G|ĘǺNgHM- %<{5ney<5#qlOU/Y֭ VV=X5!u6F nv؃Ji_y\V֬[f$T@m۞prx*M34V9 ^?{*T䚔]O{_Rb1iPӂaBBdTQ6uSX8'(&Fl. hLH( Fa-Ɂ@l6/qC|`L|n&Iֱn_Vv#&;|-uMO1H H@crmg] aMi+{.,Tj)4A TԹeB܁A)#2JÐ$eD(dS@iHSH mF8x@S8rQ5w08&X$~EHMoAg(G zhwş.Q"4ߢhL ssoaAos} #}~Л:4R:L,|'d49]R%N1:2[%7_c]vcKTفE_w5=W "eqw|J>2D'*ڙ4bWRN:Ӆ"C/,eog2zd' xFkY^| G]K ?o:|rwG}(Cj]#tAjǀ=퀿d[.i|С8wd ڂ ;oncD\"QQAA?|U)7-m23HxR=*(R 3Q-g'ͦ*]Nz rE=QVkJy('r7S6UBkP| 2-@2n~wd2 (@D25 3F6Af}M3 {{?͹ֱtrn0 ;cV VIV˿nz%ﹹwՉ66dbuZNxѶVg2Ի:-[18̏iqs`vqώ_3l C:&Ϩ=4w |?3{'Ϻ=24n8:hԦ=vFG0FVVk~0RzW}QC}+6 M/~>ƖOٟRlЧrm(5MT8j>Lnٵ٣ y};*yee>X[ ''cZnûB1k疋G]h/%*Ql`qg?}v]3> P4dx\-NDs88vlNGG w.}V ۃSR1߈1#;;Úxq*[Տ9Ü\ΏzYrrE(*uulۖ.HT`8 g%D8|̧xKUG'& }VMyaR3Zڥ&?^{>!tfE-c];;."91$׾/U<4)mjKk(alPݽǪxѿl?TE ?M.gДݼDɋo@mij_:p[n\?4:䄤^ ^Di_hﭦIsIDӄTN Lsownj dob^sݔqOo"p5h? d a5!ݣBčnRa 6s@e d66FSM5&%~0:tqm4(!W?^1w7HZ8پSM;QڔA Z4֮!uzNYqfB/:a<]ڝ>k%^ݒݬs}U˖2ze Zjԋ4#%Kh" Ѵor^%Pg^uSJU}okoiݍ]WU#/}JMtX[4^'~4ccLAeIs|;^6 u+?*ۭ}/mi|&ȶ>{U?9GT{TQg|𯕝C͝/Lx»)f\(Yq/33+ur)YRonK9Cr>zˋbz@@^u a.ȟR3ʿB$;;X>Y0С \dª90.IO Op۶v^j( Xcf&V;VI}kDoHcDP/>ƅa gR(u)ƄuFޫM L]Rc[{uh%;{k#HaRtANG1;;R=epoF[=\u ch4 gX'B$mߟѧ5* 칕.#>{4%fԦƮfߞbvYX(Put S/f&+O`tov䔔mAXc.ƤXJ=ipPmw{5/_5-xYY87*dzݻׯ?4ݪ_WG +%5)׶W/ Zx>ԥPLO8奛ɱhNLk Rܽj93W/v,e뉳;9Fpw^j|=O"8n ~ >II]BQ^^9Nz43"QѽVucvYF65'wbOxKcS|4TE w`!fF1*<b'5y4+)IFi))%۶$'$%}?ujx)~ݻw@FsDEBaӍP:#SLLnnnLnnL||x AVp{dn}祵Gi7]t'vr4_tnZsW;vh=m0)jy^ATS꒢!^e4MD{`P.dg `4qSЩ hwS'Wc+Vvy 2[gӜ?06߷ *s{V/fh*"ݎO+V-r)6 ,h@R/K4Ub p4VBsXR3 _PXƣM\z]Z#8~c| g+̮^BTk(c#֨g/7nf]  A=>J;]”Ϯ*4yl1cM.^!#:d2#,bKZI_zCUػ<GgyzꇬMCL>>. J!2{fg'Z!dv32E)82Nz,efBѕǾFq?z{Wˮ]˃'±-Π|lOX=?boCӭ!KPPFH.J ==]|zݾQQ[t;MçyέYq"Q)>+VLya3eٮm˪^zz.Sקu\cUYY[#g~Ŗkmx11Fiچkx@nёd{ï+*\q&/OJ)) AjS0,lU#żMǏ٬xӭCt"Qc?G VXB(;6rUJ/Ǭy( m/ 66RťH% /՗PEc""&2 -Iֲ49>mϨ |]"= IDAT!N?@ΞZՈ"}JqQ.@ӍC<>Q!StY 8MremtnE4M6Ӓe5B!qh=ZCTEW;ϙ]7&erd63zuص x_eYy(Rڧ7MD"bڊ:=AVc(U&̚"vY5 #MU3E̎nS^޵իgOՍd5qؗՐj-(DQfT:d4Χ3oQEhfmjQt3v ׍t@3.SZޫvlh*~NjT(#Ma&tzڎ9tCGq؛UUc-.[zDM=eX]v JVROSM$z(F4 &5:vR&s()|ͳ'焾e-N[?<6yaS[wt>*kuX$}PDسo~s{HeqR˴63*Z{qC?lr%U -ޖ9?rF, nyjDQEnNLf7>DgB`1ރ:\0wd2H>#!~H94f75ͺdmFs^5=Y0ӮY $\ꐗOJ&/Wq/]#d4X)XjI@ckẇNvqFHB<B~D9pV-f{{ I_/dܡ&)W)fO7ו\4q[k~p譀r®~(]D} RAL4S=1/]ctyC>7苵M;`vEEq2;z̓20e5"Uj͒Ϛ⟹H !$i;ʼngPޭ̎nB 9v6ǛKƮع{gi9R²&wrBS͞sy8uHM3=+#ܰsad6qrK5f9c|BMh7%z[fKB9*淓&9 y߈xLDl_ w-Ÿm碣#0P #~G(,ʭnlPشxaq 86_N*whM=os?M_)ZSB]pt)c/avrr2sXyxlHTʚDE5! ''q@SJϗ?'ɶi `\oFsJ=ֲj[kN0//|B_^Gϗ MXE(Ԛ"n)9[ !feeb^[23$UZcFFΉ6nw)X\ktw7Ow7S%#[L_SU ehmϲ=^Y-/6&ٕVz/o~ZZ@,jȾ (U`Ń 7#dڻ>F0YڝI7gFtA (d41b Hwt"/7j8ʔĨkD#س-~5dWb':Rb“bo+mH+Ym9ӦE\EW#HT6,٧H 1{葑Jc5Ti(#ChfIZ!{ 9WzL 0wou’WQMX>-IZC*ՔCf3;M#]{bԈƎ[%9נjnk'-ɜ AfW2JFK¦8yK3NbH)5]!"&Rautss˫I"xQ}J,|],q{B%:Rɬp2u0`$΃5^OA60+dX1+ﯴx9]4EUIq7MC+^|.MIymIFGk []U}+fYkT~rPIœw!>I=SdWVkã?дkrvRO`nⲮ}gvq1{dHЍCJd9Cf )RgؗU t1[S>e]CL?}~۷C=ZB 00̗[pה L&דB4Cd76KI~(̈́V쑍X0>υ;{wnİ׬oC ei;OPrebpU^t5h$z՘hF2r`Tт-מY=i7O @Yt'w3O3~:~<ϗҨSQQ}7|2?n _aB>[%qRQ56!>˜9 B^ga0 ݈ELfZ:$6 rr2s]wD8n NAk"&,OMMB,,/߈J {ڲ`Gx͏㹺V rc͎Aj80?MVjAII8 UbåЪ_5~#Pt8>_SSOONt[UIB5߿%Ry uknmwO un g _cn~z$Ft#Yv#1\zvx5ѣt>7-/rt96O5{?ekbFR2jľm뀢 }$Gc-0 Ek*AoD#99N[57 FR9˴4ǞllBEtAwnЬuPb^ҨzO쏤NvyrżKr]8}?ؕ&3 Gڕ}lq>|(GRmE/|-lqvB,mfl (CX0 8&@eU)!HfK{:f|X9Vnh[j_UGx@b=}3jȱn.`z`|";sS]죅GHRTI2*Bi˟;ni縴.߯ Sxٹg!vǎ 콮q 沄N)MQCؐV!o@\;)aTIj푳UY;hiw^!8Z޼l׺ѫ=MC<;~4΃s4yi3߫{{TFLw$T&&oB;puS$eW|M04\TΥ::0Hwq `F;rl  L5\?w{f`= g9t0"PkcM8H06pdg9͟!8p K! ?iЅ UjMlcu$e=N* ;kZl&Eq YLmDqL>7-·.>q j]zLd/!獌CV 0 /8}!fnȢy>rflzwsdž݈Dr/ F%vݏ[U:Oi.lSpPb %dlvС?$Íow [Ok)ȶ *>x B `ČwC+?{3So}k]9N#z:d+N*P"kۑBdXxfctsnl΂!mwc'C sr2p\ӃN*jj߷oռy acֳ}ݷm[ 0apVV kG!x-ݭT#X/R,'Mdo5W DKK W e~qaeV%VƢ!!Ģ(\9uWQ}M=fصvᶉ+~ɝPqϮ]PirŤVQŜux˂&77 |êo'eD9{[[O~?5W4A$i75 _$вjվom %}Ao؍=M>ܑ͔J~nOCF :L PX}^=\d*-kj! R"{#|cܸ ;1~O,ˋ:hq|WX(byAZI1KVҨSRw(*,}[$šw jFfL=m-czb tN. 0!s+~Z?xw=3jڨ|a/vvme8M#MZĈ%?g'$67dP]4'AwJ[Rś:3*٫ο8}a7_aj܄9~`x&۝KuJi;眮|2RQo7z*TjG'jEX,Ꜷe洐!@w?WLviyIFC||&UԚ)-v3ݚz!D$ ےJ|\ 1E6]/>AvT VϮgW]jiV$)OKbY&6h`)TS3{FPf1a7?"4G8ij)2sr7V;W8 IEr"R"L&դ8NE-2I~2eis!RB'2 _"DBrCv >hZͲux۝TH05I@>u|ct:y눧)Oȩ*}^lv/zڱ<-}U]n#ͼL/tvP{û_쑹[c6}DDܙ5=,H-xR(;66ut} ~~fsH:S3Aۃư11y_e܂ ӪB7w/K(]:˗#,r__h~yR>;*kn'{vŤYYY۲6fffaZ#gVmϛz"QFFKBI{pXU:mj:::**\߹'b[)|\ jPʪ8tH}UGG0IfddoMMޠv*?}{QC mc)ooŋb֪D"ܾeL/Jh hY7sr2*I b>O(9# n>`O|[⒒@$%%)EE*kLLZ޼ ; #mD_c^ǧ*Y`h_8lHk&T_цhsEp{jʖlb2 LqBYB[02[fb0FvvzcaI8tד qKuwK$C%=^7KO./_ H1AAӳtwׯPR\ROܺ53++366Z- %"9tS=s}⥶hÎz;!۞S~l{)ZKYD(ZXF:vow%nR*J(r^<ݷ&ܿ>Bv4(4ʫ^\n)(stπ7PzH(TfJ}?7Rj*=ӸeԲ.;qn~6L߫Z:;E3˴skj41WPRk-NB))6s,zf['٧x!>N :Up\N@S_֎YE;(x–"iy=·~e,t+=Xb BޅFHR}T-+^:T]9P(Ot*IvIo7z'!ziڍϼx&mkռs۲&IҹOaOh`7Lc> fV{&E% V6adPl֠Uƞ}Yx~ AAmXlt|B(6M5e sMǏǬ1K:ǦuJhu@QN7f&,k} Yݽ>$B ޮFZ.\,:p 77U􀅅_U$*LO~"sp?(~VlUU땝P< qV?)[]K+ w;{tUVV#ۗr-u׋}X @Q 1Cb>֭MuB4i;40~NtȏG{>cyoԔ7)g& [tlܒa{O,k SA!h)tzꆵ`g}9|E&75pO|p|[I%SK[n˯(9zd@ }h6}aQZ4 -^4I2ΫSh&ׅ7ߔRl='V' Fo@; B%Q6&Hd}+YMsqSƷqxOĠe u3ڽ[RpRVp,K>oɇ BwZTgT!*P?^y+fՅC6fʽ_a#4Z{&tH`@$TE 6Peuݽn_Эw[[tm쮽 B(PQ+@23s>7Pڨ0d;GE؈$Oz&.@(p?y? VvךF%v&@9*AVhe;J{?/yq̓9&ZO?9K4I@ :nv-l4!:=I2:}ix̓ l W(v*~Nybzv@6dUMۿ:S/;1Q[[XNJ1 ڻw%wJOnr2 {W¸Fww5N=f S UIXG'+=@(dp9 ܶ-jqd 'go0*[_¾}bwXΓ˭z73ěq0HYYsʊ=LϿXBv sG愵5א.gr^0޽+7V|2c_7 >zmG^vF8*3Ucλ Bj|c-{Nz,ke_feQ zWY:{NwD>^`"Q?6ǧTԟ_"mo#^˾YgHk3E+)&oOtx)G4}KGؽ.vilv,G?Ue5 .>r) Wzfj㷾Ѥ<^|dFX; ߦ S قڵ|Jk?8BL8c"54qb}ި{!s>[5ʱ=qOla\ᖞ)q8jܛ2*Vl)(8]ZƦ߱'kzC* c{M 's޽+@bG@#_/nQ%ɘ6ƺE\2a+Ɂ>Ԙ0zTO[39~/yK&%%da{Zv5*xޛ v{sڰYW01ޱhw bTN)+)1X n s'VhzO#ɽgT*1@[jdG A2bAB+h{`Xm](DE<%D#(_q7@@ZHb)l4>9= ҩ,K3"{g-Z;qUbG=-ǍeNPK9 c8omץ3-7M˝ȸzKU04$`~qC9&Y.:BHBh-iM1zgޘh݌ Bn׆XC(Jk_nYso`t3)?6-@&Ne$C JKorf]XwnW+ryԉaJa(ar'(XYHV&-ѥ]Z:US2PEڔXZWlV+_{EUߛ?Dd;TP7sx)ϏϿv-?}A*?dCnp_]VD7(<ַ jάK*ReeNr?}1#7]8@f\}7I=%hhWWٷoEcw<<.wj'ppHcb,*1\.'W߆;?g o:[ b?1SQLN~Ob7aOϖƸƸ5LDӧr'U."<#vo;Qeo{N}߾b;p,;;!Q=So w'f0?!jr_4el _rgkT@"0 3(Qމi;E}0uݨb)+)$K5)JjOgDH/*kKY$:TJ6ql}r#*Boc09|KY  bKCR@$f3ap-н0-ROndr[7NCin^%'q3&OSinGS`B1"  HK(]O%q')}ԮH咳 <"|J:q=ͮ vJW.+Cu1SaCkۼ=-kwZ,0%̨,фZ87J/sz)X N?޶r|2qM s *KcB/=Xw*wdiG.b=BVcfi2S(}͆Hۅu+u>Ma_{́Xjo?zaU13ǥpB.^y6& (BP!z0Uiw)qn6-cWm폜utjq=uFrlɨTN56٤Q'sg ,hR1U*fCCMw9V͙Pa|͢~o8ޘTRqex0˭T`]-v ܪIUU ַ븓UUqYgG<p "ܲ 7Hf \S[[rt>-r˜C?IWntۘOa|֖f6Ѭ>XiCܙuI6&:~6nbesgassNLC,YG[&K.$qĕCt.$%m|3~O۰y +8-h?~ZrDyØlם%DP'p¬w_S5{7<1+6.7TG)Z+#殿ESck'&{vWKSJԺT&Y!g0ߎ0yl.;&T%RG{8ނxyZ 4F-բ 2+ҺLdVgV!]S#-mI&k"7^Oe֐@lQCZ8ۿ wy% OMT`0kplހ,VӄpRȡ+`=!@hDg4O_3}A(VZztE)]>9#PT+%9c! ,VLAkj\1O#1; 0ZQ`?Y`1wGm;},gEC>HGH0RS0ζD2uI10 #00 ^ (I h%.C?6~,@YBIjPPVJ$6.m] qX0b2Czܪ A ̊9| #DZ?tsingj^6VƌFv-/h|gٟOPz9'|1qbl)WYY q7Ϥc?Lf Z[wnRϥlQ.u -pGrՏ_}WǝYVV@[l) 0[[EcmH|YR1t:s;1} 6&bR#x`==ڱ׳  Df߶-ߟ\(.tZ@; =m[>"1&FA Q3Ll[u͋&J4tHB`;뒣D?yj]=)*u|GHXQ6qYϒuL 2RBI}玓^aN]sS6oFf {?~`|dPՐS@b, &tP{p$2D~ZLyaU5,{8T 7Yfrq9\[R]Gk1NI(ZMJX7@ھ=W&me`!-ݍeKN@FOI8^ܧHZڝ&zC;-K7)O܋2ɱlBvs3/FBQT.#-|,P*1ҰTN#JلW}җ4z28DE9g{FoU҃i{P9 lxiO^w C^+xw\)Ah;Mk צ''b-A.TDDFNMs^mGf:O UXZ8-M&BMu 0`g8K&#iD (jg3 s}.l[[.={)|RԓNrT'φVU_`!h)..+)2qY=R;dž+.^15xV(ÛyBa_|G=pl-t'$ݸ("mUUܾ^zp? Q\+-8rpbjNuuإKd?*:.5܅-HhJO"!6[X\\&y#g*gNd1u_v:(OI3ovɢ%F/l5kl)q*poHf-2]aP`$FTXԗ{6g~68b[ɰ5 +-Aw p&}|m]qcMn}<0`UP6M=aZą~!Z QBbK#ޮGQWZi5Rksu.c^b1s[v{ €)r4lղ GۻʢXrXaِCjS۝&Z!@@]]Ƥ vwZ;ԦvR 9|\+ڳ(FW-eH#(sQa’+aM;9-p&ul,FKƷf lgx/~v̓8,Sfں 2/_Gڋ5l-L09 9XwHH΀҈J9=IW@)Ybcg &+waMf1"# HPi29ǥӵٵͥ"Hy0={Xui;gZ?7O<]`@ `43@zZWp.yHBTԓR]tW}Lۭx|7Q2]~dQii|䗋ZSfMjZ=W˥ IDATݜ%6դTȎڼ<`ƅ,1dթSsFZԿU_djpkbVּ `b87 ZA_=ug"Tҝ^@vc1Ga)wo}\.oFޞ9Xs玼7<$ 9 8 GJ_4 99xS^׉I pqB?|? r+ /q_y9yy@,۹H,Id&Xwl8=4_շx^1Vg:n;1ub6mnyʺ%Be9y+}6@{1l~mGFۀA0΀ER,i߇8$&u"jfqlگ}>WkSV~e0 Ys2~*ATe%L7e\'nhd0O]I9U;%fK?1ku.w\&`Gb!-'.:mxǁ^?R)V_/E`n2hxӝvJWdJz>JBB͇EI(3P[n]~g@T4: ;!W+bms-w-JHa˖:94%?xCn4Pu{vZ cZy4G~$~[l%G3r-CR(FKL_q+mtQ8+-5LgQL Wk/G}!Xo X 2Ur`EyĪDl*~0cWْbn9MZ4G]>x֤ hR-s2$6ޕ6ϩN5AlL|cuJJt!=0tĥQk!z|XG_bBvݍxa_ R>,ۥD6!;m+ [|UssY GJ%_rz[}gFp'Z}+Alnx˽w0z:`EoA (fO{=xK8{`J_VYM{83u0pB|4[,8ϊ+#P bp3p<Dوk+z#l(fd '~жv<̈Y .t{/HY"ju{RH oϡӈ4|qDŸW/3f dc0L+% g]OiG]n Sw|7OQcU_xv6<:iSuN@C37\^c}Ũq?OƻqIneh4&>[tƳix<.JTOΨO[[73̔E \ZZ/ p<OtWÿ4\nĉ7>%g:''r??~mmXC8BƄX p5u-vƴc0[i22sⷻ*\:6ypmR~zv8>r>dٷýYƌ ՙJFasU'a(>{|}&0E}a`C3!eCC_:$f`6;5m;-}s*`|c<ۏ0|FUhs@?ljbRg8v04xcsUX6S.e8}b{6o{ޙoq էQʂu=BQc Iy+ Ѽ_H ` ٟeJ[=6vԅ:r R! m,լ7b)b0|XXCgE&I]7>+kz Q*诚L>?̛3Ks/Jp9| 9㌜$J%K); YD4rƩR; x4K >3M-M˘k3O έҋվ>Q?رݒ-`]ۛ/?9n,GB!; Ukt?"jzԪnM~S9u'>;@vJKK wQ >ЦT#Gfr8"NP =u'.v8GO88pbV=JZl@b:!8HcLL-NoiYqOQm `t!&UrRT0ar'{ ӄqqKƺ/ls^\^SңmS6*붹E3tzo Td nQg:R8x߮譿v:,Z뮘uf>g< 3ayVJr#ieV@4: ( -O?R،[mXJ,ы&6}:Qa0+Ln'K(t%Q6WvW-%0TS4'Qں7L>g˥fT((z}wJgYm6ݤTL"\TOEz{pfXMGH=9xrVTœF9Quu\Ng0Xŋju<1;7Q98s:]o{)6611Z-+7^/*-RupޙiC:-i]IH S /ӵ&rMK[9IP̒-8opZaփo|h0bB5ԄpZl%ZBgOdO==>̣XnᐽW5ƌLz]XWuQYݻ]V} :(IqVHx}]yV!fPwuzhY;NBJI aOL*u6(<~ l2r@ ˽zh=AD%yVLnMI`@[6Ə-vֶƇP2cGNJCK?q^OM&R,wQ( ѝ.p@4>"ě~*ݝJ>0j%..-$_4ې4ƆfR[ycSzaE rUH'W%)L*ugAIh&׺򠘜:v(̰ ޏ|j-u!@)7gaNosgdMm}a_ VjJ&F!J-^ j$t ьfE4zHt.?t"a=: ihAsaDRHHrHF`P>3ƠK+ߚ?rc)moQ.u|˖R6[UU 6.13s__ }>IAvb=t'ǚ}6_z]1xUwq)YxN/# ;Ë%~;1/7 bJJJzg>kĮ*M?@TR]۴Tk2zR1{z2|2r]]6ɮ'4|#pqQb\c߾b&SKpQ U2>?ӳwLx@KKn `Ŝژ'w,|wӦVFyW5,*"~]R37qA(m4#`ĭVN{d0Xry̗~JaXƜaXON쨩ڝ^bNDkmY4㚕bYn JpDC-z_j+K|c]{UJmc >n]ljwQV/Â! OAR j"sH6?Xߊ4 Sz5/Σ4wB6aL|Y0ӭ:5x 7"j l"dP;^`nͮ 8o-"v&C([,{lѹy[ C2{7(Ρu6ZZLk PYW9/~HQY8KߣHYn:7[4 ][]ki݈V"(vi 4/%N ',T@wv7l\_#cHS4 cN_ƻmjMFTȢ.[\lrd1ɹ}ҥ;NatS7 t7y1wCPPbB8TNfItzS`iDA)b鹮Wn$Yּ83c߻udܦM{[Y\\F"xb<++Z%%UT%&,G(Y??>Α0Hϟ^]lWo֢o,9K?٭ Wk.|}8v{ n~)0/ka >Aب4'R$ 8X,\o^oMOSQ|άϺSxI[;Sybl^(dOv{˖ҝ; D|oC; 7Xsg}Fuې fUVtv_xu[Z ~<}]o|󫡦9e5 }iIQ)!vtBUwYj Ii?C5FTo_=RJ 6ib̟qFW{ >.#y*B.3sVb[j=uW"d%í|(7mMۚ%~` "M,suaeG,VXSz"yf\J\Vbf!|R֮ MJ@yR폭^@~X+&toCN]3sLwascH ŵ%u)KQ:PkaVWs?J&Ӻ01*MC?}dQ0LwUjS199AKH#Y_S v?[ƫ*ٔYijXKq 4+Ó_%'U?yTNt|~KսWBaWFޯDߑt{-,V)L/~2o܅bR_‰(C}t ` P %,7]GO)rxlf7^-6n܆㜹z9Q2㔗'ſz M.a,ĝClW44U\N(oE9!Chw jma2wCqa{xҌjU $X{dd%X#?̌,7I}}qjAYz]d&"ӱKY*ƹ[RI9 >)lp:ئ!?dxh IR VNl (<Թ-^8fvJ()=ZC+Q"eQ:{5RbP.zw!JmjN͢u!#? [Ϻlww#qA/G#zcP_Ѻ~iǹ4"d!jk]3< 'Ƥʄ~v@k&58hAaǙ@6&{b\Z?2ͤaKsF",K@-V %h6w\IUKY'/<Ŏ)AgJ1iGՌPP 8_>Zqiz' ׋;3CR8"F1Q)KޖV׫[sad..(A $KRİ"&<7۵ u `ڴѴl,ӥ}n\YKa6,|S5IIt\$ 񊫪g9qBi? WN//OH#k~k_FuHsP\73/\rW\Ëq˶{3wGF;uk6fħ rW_z͓ =z`mn@o?ݘSYVV 1 Ւ@V_7_^V1[Uu}9"'?k's|DK%:.76l `}ըj@vxĹsOt:Ϗ_T.xG ?B. g^rXw ܖVVU<}AC֝ IDAT 2fYيٵpeD&Q7+_pQ*KZ>۫zc-y-طd˖-%Ba0`۶ x.>g|Sv]ʩD&0uV%Ƙ8z 0[WuYw_ye%|LwG\3ljg'*"Q-氬gZ7]W 7j-}1 lmp]K$#e 0/)6@nؗX )T!*5= 3fDZip?~"G6ńSڴS3]6)>sX4, I #4zt7 JqQ~lvTNP,+ JF`IA ɱ'Lkˋ &'݉I_.&Jf` @-uk'&R)%#TwU:6^B3UO ꛹]gKܮȟV帝'?O :!Їp8V9.m]IwGB!<7炙;>j;vbbT ?cceglN01xVJ{wA>0VO.[@(j %hu8y;fEr{IǾzgbpuaj7YUInOY$Zu _vF;-#yӆ4zVf}tޔY6SpP*cZVܻmF?q.-pq'S;Y?A9|ȟbDG׭]NHk^4e2W'p~6t xPncY_|G:HE,:r循*CA/D$EɁF\t޽+ædFw`bC}ToJ%O >aD'=rkB ><1l83p|?HyKgقK"N\\.aUUe2Uܕgjȓ_V]S<{[w8P\'4d7O܎y{u:fF0& qKפ_ =3oԳwS>-ϣțqx'.h%"/5Ե'R! Y,3jl<+H&Άӓ݆dj+,T./}f~=v6gw(> t8(j"[GO+D1l`Ϗe+ݴ]/?)J%PSھYV1hukx7HD`(-iqKlɲ V3+oOkn׺}L Qs7n}{e[.)kӗ܌Lﭺ1DsZ^qA3Eeg q ;ˋkpExgŤ+'g[%a&ۈ aؕZHFF(wfg{aaԥwE) X@!MMn!7-jbb K H/ ]\$}dz,̼3|=)K 8!_dNK*KK.xv]}N^X%VMP=O3|1g(=허!ZkmY P'h5q0L%FUF.CSObˆ\YӐvdC cԓ@:AoM%֤xjc;l!j_En~HXyQWc/?^wR\l BQфQ)ce #Lva,BAfdaJDEv~rpf~ۥA ňJ5lVڻ;tZomvzS=ߐLxH@`1}pQG K?ccp~ʗR BQnL0\XgLI$ +v;bCjnfKfNCQJC WH:g>#Ovx@=jcz_~ZU?!Ȑjjp  s^gv]c=(j;9jh\oZa|7'_.NWp޲$3j8lSnaattubb=RYYwovg@xCX{}˘2YGm}\C]YxD_q_ ڏ6\M͖uF} Tp({(Ju?^BãM/o`o@P|)pe)nR%@!!^ŸثP9ֆxǖ]xNK&E F>_fAp/4x2 V_?8|;1 `Vxdd~i Mm|r3N9wO _Iw{vg 'S=)>6 mq^R,滉_ye{++*UB3Wjrl1iM4sj(/mc}!BJaw035CN(…JDoTΧJrRGa.l@lɲ{r":IW)QNϊ;[U8GTas|Vӥ9h(S84 .:͵zAIeD rAa $0tyLQ.Y-u,&[UwwklR 212خΙ -6ơ;'@#ܕ]z%2(#(='~*RGd(/xs?kٲsL)7<~9 ."V>+ !ĽAeIIz 7U M;gv-\x濵>÷CFv5o@ԁ%yGYSnΞY9zZɷ W%Mu fN`ҼwPdԟuphm39y  dH(^mrf@.p:b‰rl 9!GhqXa.vNp;T zb1q|ryz_C.=4Q3׌,xe'oD[hlf9*֨Pr n\zSO ~ dt)ۇA$uMK*ZDoyA y/F7|#Q5 02ihF[Y.JRMm$J;+]7+`4CHr%_aC[=SڰO8i3*J*jc!!6Al\^ŷKJy>3gZwJt.z}k@48xF3m\ _6b4j';u(ȥ5#%[8ZMJw?;-4 '{O0)M,!4֒z(^fwFyeNfнdDTá^_fS|$cS`:Ͷ$aՐNCj-R0>~*Z 1hoOR~"ht0ea6YEa-1%y'Hm՟&3F(60l}A ˋ˙u"?V} 8RmfOt0s~Gùrqy0giTŃ((^<{Y$%`(Xs#cDx7(F (V{] c݇^89wT6;P>} Ul;9g9+IU]OTa rϪ ۙ˕Ծ~'Cla5Ră96_E<ޏ'YEs=jHG CvoȒ}} r"룼Q2ixυk(L때,]8tak 2vq6* pI'0LĵVRUå}s9~Ȩ ݹBd WѼ#ڻ:(QVV3AJJJ++GII۷,,,'|tq{RJa+/rN|D[lBY}94nd/roisO}_Uru`G/|~v,;Ȅ"ÖPbu 0 |BN'3:{ު\ 1u3R=ӶuZ{QDo jÙR /ްfoP駧B.2 w0;`]J@0 1ë }Z BQA bZ|ϣj94)P"$;{֮%-Oμ/x}[8+3O(++lmyy}vsT*f~~}QQ VVf9SK&' LcWZOYWX$>%L-6G~А~%\psa?՛%Wo])CޑKs3} +okh;Yќɼ_4>kԼIɁGI4A5]*W4H43XF@38TӣKKw4B6 ҏC>-kwoS؟4Ze\iK;i<έbxL- IA8iW!DAx$>o1_aÁ OAW]++^j}Ez/Ny#4T/Lj۠<)$sݒܓl"e^7|9üD c;AӼ k?˯l`0dv}ę3cEEIX2/ۿ  ֺ9񍍓V~ h0J&1VNwOh43:q|ц2'RVؗV|I>;c4MyOu+Y2K抖?~Su3DiZ +6+\pT" m`2ōIMMIk))) V•gN\{J^mNǰamں cV2Ǎtx.r7-S~{>+uqr\ 0?ˠaPӝ a#1Զ%ZR Boڡ/D*:,@NPi`b1Lϟ P{>zW T^d;!w!lv{$" AdCFkl׮XD!&*T@J.T[Bkrrx݃cp0?~{-"nFtr^N>7.)u1'eL1̜fJN !EQmDXݐ XF#XU"eʚ;84i`1nʗ׽`ҩf $ӥ3 GzNbE߁cH*IE fK$Ҹ4ZQvPv;@G xx%Y;##eҦ@Ri \,3= F"o-GhMä7 ޵Υ ]Ux!NٵB\DN75E ?H7#4EMhjtFH3cZP܇':KꛚzFHNL}* 9s \H[`կ2/`UU˖BSb1 .?7EV4Fk(L5(=?]ol[U'QZt^[©k6/X7OcDajW fmPלfϿ4`^{;W ԤT&VVf~ORw͞{D%AAUݞQ_v>_^ru7dѸb/fGWZsė}!!>{8iIsEo6 8;ݲT\Ⱦ4vPz&}6t76bƓG1 P}첲 (--)))ΚU4$׳L[|yIeDL[`+ I r]=bnr /8LyEtM:vt O?|RIS턺}Dqsx|x$ÇePAW~$,Q3 [7ť970%'jv#is)Q[ r8!C* )0 c]A~./#uK}$\ ,@$ (='(%? C@LyԠKe{kI=$$AiT+h-̻ ҀC&o)9є:r81@":e@X]@Nɨo_n`cv,qu6rE@(j@U2D}X&BΠuo$0XS[E=k )N'jN>FaMv&MЇQts[#ĘmwA. 0\G@憰EyD]eU|b btt34bs9m@`g;X xbNH 11ÐC\,Qە'ˈZn?fkq>{I-zw-twwh/;G>kd4J(|8X=SJgH)V ց}&CKYڑ[v|yT6f.:SbO(;~/8x/pv3b_'17-[i4wBcOܹdq/ݧ̸#zH S?4?/MսsfPy_lo*w Sd+V-朿BP)> CtlDzl$BY9l 7$j~{idGyCLstlw`0#OEa.u҈`0""ݴø|"玭 {=ؔ!FkW`6u Pw'mRWcMЦOVٴo@$#U2~~ x0]{wٱH`+b(/s0]| k sr!HAID5>Pim5XSpF>;v_\Tt@,)+ۀgn__\sɏ? }nKri4Weޔ4x0^$5w2gШ#i ៫v(yBii%% \~Ӆ{c ܁+q{N~y礭M,i EWwrgoS XD.DVi]"O_lI4g=)`>IZ.]}w(%dz5{|9oo.`Y`hT<+faZ1>Bew2603n;VvTq aXA -ɨYn0v,Lb[ ƌ5 72ܳ=9*u9z%Y> ఋAQںN^N?AV C!.j;}pQ?)9(R% {"*TD'̰9-#vV+%kXlߟN&'#!ȳDqO_Olbw;8㴵cuw~?V GyC |'ͪkm6×KE0h]"HdYv_׵[~HuHcSWʦjgިJbS]!0!/0 rL!)ei^$IJw ZCQw &vM_V\3\ ڍݜu锓SUʱK+TV{Xv&7JVObr$;M/xj@c#e;{xmyZz;?{7)uREQnH2ߩӉKo t{sLNm BtP@,EsKGcC/ٵАfNpJ}-LZ!z]up 蹕,܅- πQn㮅{&X6[KBڄ"`kmݸ/bw ck+79<~vv;iT'ZI$E<;l~) t`N7Z)29N-|uGc!d>ϛ%NK=U9gbu= tI/-f2*\.)}&ܟ܊V#u1}vpZaUiiIq~oм4>tMpȎ9s1?svyՅ[ *(iBNOoڸym95#]./6#(h1 JuhPT'kHc8PϿ8P՞=k7Uc:jLc? A83 8zYc7Y7?/\pa϶4Xx>tk=i>\3=Pb~BЄaԑ}=SRR* )=qVظȺp hÆ;JKKC2Rge^1}>+KoR5enD e2,QYah onxoMAլ0g:|1+25Tq]Thi{aLaozikDeoj*6<3qiZ Gb<(h@)""4v:1Tb.Zcɵ`['I{ݡjpi ى-S7T٢ ؅DE%e i=݁q1#Cidm ZHgXLaRGu=e &U2] .Ы>_|J~"mv\0`A6+@Q)e ^<`Ǭ0@ӦEGh 'X$0@h ?E]L:~g.sUpn_^^Ԕ$ 땕?Ic Z+cuzϧܹJ3p"˚3⩕/PZʾb^@^NoGΘPˡa[C$`mQQQ` ` )xCGgV􋂿lpdq9& i.OTWccRe_Q56&řLڵ<ǁwv?-J$Ʃ1yF#g6%ڰFɁO'V.~"C[Rj[]m6%a]= 헇zzܭ~3/t|fI듿{OVkʒjjTWzJh%%%%ݹVpZDq2*q͚ (_\X_سjMOSojf*:'HO4kNi~ zćpKOx- pv%#^tA%kD9.1eJ,яkos+TDK5,Lk@.[ Cv8<JOjAJ"֍Ez3Sɧ.9L'6u'?[}3d#qv IDAT}[:lP>㶎4 P`0۔k Rb%\a]?+d ʇp '08ž]ZG?32_P`Ļ>H?3&#X@P܋#v8a 1I 5llrHz3SO[]}F}0f[4 `P2c!d`oEtzQiX3wm@q⍲=MrZ!yD`>9aIHďòK~Yi@)ڷo8ۭ|1j.)ߩb{vؗc>c̔y$zM)WӒug<9,b؄@OD.Ts3kY_.^B 45[ȱa/%1R99b) hq$o:$<<3,m!Lº%5uA: B,nWR,o6_v$j֓L jk'Y2dɊ"4KOUIiݓ5x+8L42khm]5;0MFqwbjB;R8Q͞N^aOPa!*ԈΠX-mv:L0]Q ʂ'k H#Jd#ZalEz.d68F>k!һ{acd9 E>DÍm\þ]10&P%Cx㜗% ƺm$Ŭvꀔ`,Y(b24wÈj 0]f*W2Z|1 c=k %93<)C\!>*сgDިt2CB4Qg0^kP?(kN7g-l#CԁEoEEUq@퀟wwo~??WJ'ĝsaW++l\ j_wjJ@oR|w}nUV &spf^๊{tlJWn6nGx=2 [(qm@7=|N*~K[ep2~uҞǟ(YO\>oB L6.7?H.c\g\2yrQid .zN$pf|[t;qGR_wxgWJ 튰g7}߲ISm#oyEVa6r#l4ǝi~SP߆;Sv7XA `HcHXyk3QJI&Xk[hs1TB?A. #B4v=ffL% !zYH h  GEU;Ip Q*OvmA~ ^[.ɗRݣ0Z2]DHDs\lY9XǓ \1N3QDp0a%}Oߚ#~g\l$<(Nzp݈k;<1RT[h]c&FvF{tQMIW,]Ȫku,TA8=X$'hG6 ԕ2C+;\v?ouVɋӉ!9^9~~&jtÉ`sD1!Հ(5 A0Y@ HW_"-@D߳Gx y?0 m,cH%Ykbw4vfqȚ9](HܮSL[DNj77L>_lY˄cwޓ>8v-S)/-helQj^ uy7{\`ǎP }@0l0@8xyM!\Nװ-^lW_Q"2ǽ_KJFɈ|8*J5J7果|)I}c 0`ϐBwJJJw('t;U <6o>$EwuG)UQVVTRR6ήHX_3E4`_q]2Pł?qe?;RzaV;D&e,LEzW[N9=7#\^ˇ_Q",T8 )2}r]b.3O{ Q\z>s/{/0zE?UG cy}O͜s݈6Xh\`zŔ,᜿x4`*y!$!jc2磻30 R Ƈ(<8U'J6&N@G)$B IB fKxB1 KGr2AכKyXA˜-7xɺPϚ` 7R)cz++d6f ٿ%[$<g!en+?"lרI‹76[N꽀+N¾|aQG6$ ފpAቺ{OY>8$\H *+9_^zc.\T~q^x6p_J3_/-Kܗjo(s jbpvJX0T\YkUIL<gfdqSʘ}^$ TUU uPkV|񸝇0O=xA/=_KcUnOLT;Zwv LFX˫_~UekѐDgr zMمw^WDA(`="RЮ'O3dQ)[?(//,//مgZ~V^^cGKըPnx:\ˌ$:e V準7&uv|F3kS.?sx_KLɂb!HSV7 "L#(UYB6ON4S"#O1 d;!HldB"L'Ǣתg.4vM&`d0f#A[bkb w,3f!7& ;"1\]pn؝c5.v:)IA~Frm!:ztsg:{}"U߼0D]ӂϦ6v=\zm9Fh풹X>^|^3<|5Nuy;`LRL jSȣw@{؀xclPsTW"x(Ϡ DRdBQDod6wTED1Y2B1`tzz:l)!]ၢaeo53}:O0*T͛EVV]Kue&Y ~ѮFex6;u`^ ?]{?>3,Q3EAZwTά?/d,Aajcغ8W6W #ZQW}{ǵqegfQ D5`7^qۛdSM7I6;[;wl)tDMH>3?dl|1s{=l)1ZuF0%2e hx:y.<4ͳ]|?k[u+E ~7w9;N|&ͷhɛ~YК9f_lyGsD>F;$蛫?9֝Ǧd/6U<(X2,A8.ܮ]IDڼy?\ WNk׺H URNGGk;mϓ|h4+la2ß RNF} 9 ycjutnδkwee%R;T.y=iңw#(jl[]z=U'wmq\NK Wfξ~]_rk޹@0>Y{c)IxDm7KF}eB x R./ڜCn6ĔT^#D  ]x\~ ~h$I֍iy3[+ ]גo jt/!:{A\:ң-?/H%QtֈkJ>ݹJscy%DWr1(JYjx#5|^Pi׋$bFwUfk9*'Ym/tn\!9Mc-Vk(.N;&"SH`7GtZ{"C[׈s]ݏ5ӹfR(j 8upb*,<~&(Ҥ:XMa=ډs$9W5?R"̲XKPdJY0<^?My#a<^6~sy"sܥ8ŪޔgGk0N)+c.XKi% j! :}X9SXcb}fNd_.i|q1)~$#瓹h 6% .S\:VvDbΛUY|MFzd"N'EKDfh2- ^VJ[>˘:Dny E:]פ.ah`zڂGY;7$rqG_wq:>uz^ʼ:^#dnhAmv'4hfZeK8j`⒖͋N>G)sو?‡CT| \̮N]c)EwWM M5`he`42t*^qf0M`&Zُ0dAqcu_rw"Um䄴[Ă*ec_tC9kIrE>{18xe9APVG'_5%<\n0zPvG'C׋pxMN/"[lKulK-!:;{JKGSEOAdgQK\q;'JFOI! HZ 3¾K~Gv,h!Bia!IIyS7 /w |N;h6áM8s9s2 IDAT 4άt|Dɥ|٭Xʯù>lMzٺCs߭i#y gzTi=W/=ўj4#\޽ a]n6Jyi5"@/澵?yt5^# $4F'@./]+z5͝2.b099;{{uO|cC)(ܩT^bbζm=D^Cs( Wy 񟣟}0PpBY&,(CjdT:cnN*ՓO5[d}}š仔W1va{jrKAЪFEcdb"]Z$?ٓxzK_uhh]݇c~ T ߽ye hnImD$J9un{'tSD~=n/˜21`1{DXKkE;{˦!"GSmdͩ鍝[VKƎї+^ J p"fUo]+=p8rY#|~g&8'b_e> .(,vPO$oo{I_EvWadqtY46r>f$8_ZAkgHkMp%kÊM5JNM*U]['ACCRR$9[K>|ܪ^Qv֘VotuIG:Q|Q )Ev?|P/;XȾ}E-yWavpZ;cNw~z~~g|&E%VG&))ԾtPϨh-ީ>6WXbX\ݘgj xJMi2$/!GO$}UaUֆr&#LFśˈlԇc)naX!ގqwl/%ܵWklWw#az#i#6{BWI @K7 |ɺvo|ơ3A&Kvկ;y闓g87ήWqsa[ߘ5|EP"gC%ar85vE'uwѵ9)&=ra!lHfw[r75wtIJ8_'q‘ph}8ܪz|T|. IJK.{fL#pY[[_y\sՋCq Vpg䠢[ =ws\ y#tA &@P ŷtS+\n'^Go> 2̷ROkDr@dU o4 /-@|~3 WyQ:G7r}e4ƞJ{ki}MDcGGu<33+++C/vA䛸7jaeTLXuHmU3#THJqx/{?| (߆̔%:ZiSz.co]IK+?2Y22222RS)|B.P>os3ے p %٪yyݣB4&|\6[9C:cVG}&-x;=ݗ?v9aOI@tZrW-V~^Q#]'lu5ao\gd !I ":"yɣۍt밲rx8BI %8b1L,V\Do)A&h[%iBmqԹ74XiFs6И2Yt"gV뛺/ld&_M8 034|}{?\ Ëjԭ66{H]ĕk]gm^ ?H6;fu,}$EH xї>vSXƖ\rQyVJ~) WK7^k b}KJR_ 63 nf0<tA{bniOegҀEy1M6Rv@W_\`n8*G}u ٮ̈ AH7lZw;C-WޠZ' 4rt${e`6;v#e#\>ac =({Kݸ ˃@>x}N]H,PqI"4-1q@ K݅wwRb/MMR.^GC5̞U6!8{oĜ/4<|۸3yw#6ֹD2 <*0% +vq~ 0riQc1}䫪P@)wOY߶t[ѣL{קzaZN^aEEH+r {Xa1ՃB)EÃȚFR'WŎi3;5Y&Ö>{,g&'UKzb#,[[0ǜK/<"q >ՂSŧ.AW}Pĩm(q>쒈6ܺϮoA$8]@y$L=%^ԢwK箢6,SR]Qɾ?Dhnԓ<D^f@h Ze?.xX߽!( lBbONr2u;EoFh6a|#@ 0]4Qby-RC\b_'WoW$.~=$Yn>~ޙc0`1ifKV>M3Qn7e^ U§^9kDpBg>á(S**VfilQkLp~e=Q9gz0rӮs>lΗݦ RMbOtT>fvwÿ^lo.JH4_~6-hA!I[kM1#u;FŚ}u8ԋF SRN^|߶_ݲtᛲ}_'cŻƗ}EvZOiQpɓ,JI*U~%8-jL֮}1eLfaXjhPBYZZqbb.cIύP4HgFq,󧶝% B¤;7Aݝcb/V)t TǨl6+2RVGovdǪz"jL1+"/VvU4zө}mW۠I%UD@k(m=6NG'am]m]8JqDAq}:@jV"+~~1;~{hS9>(DTި|̇Wx/^ ll9lvEu EcP r5-(~֊ЌlQu#b7u%آ£|OS?A6t@(Z^jiäI5D@E.M4g+JnV N[}vҿb{ױݿ27yEU{h9ު.%IbS?meWn.\mP*eTA-ҽlV==HIDݳ8TZ3()M\U.'|}r7Gi1 0Ͳ+t}-FaG"Q?K:a2VWus8ƩK0,f|zr:,:`d& _'VgS&"!a E̛W90æ_Ui=qmפ՝^o?Cvȷw ɮm~ytQEtixLu e`2$zޝ8o2z]j;.}Lx>]gì#<;DH % o0! `h{{ ݎ$Cؓ>Kz fj/ 8ӏ.2nO!$BnNw,Pt4͋~p`x!\@‚&T\z7 H/OLʲJcjiYSÿlXw\٬顇tI}1GEH/5,`2n2LM[drx*@X3gc `vnjeޖaG#%~Q#mW$d0lu:Grh'/>1Ņ&N||LXW}׼ {pzbTrn F8tzr$$HFk_Q8l  Bиi>QHus#s[>&)'OB)xo1So{\D Hza3Iʞ WdxٳoᛸxeE}G.7')VVlnIc^#p0Be/G0f5@+7~?HһKXZp{$Gai)Dd=^0j) qg > }6EMȓE@}Vpf∏*QkX:._cRJck:=jw>l$nE-dZQS6BS[q5Ƈ-zalB5k69tաl~m ;~Zt\j2-c|j!Dn|2QE7I"n7fQ6ʪYjP]2ל8;Gpn4Z:,fv`VuDOC^S*L2^y `:{o!.~r"vvc6;k?Wэ&ll{+}tU+}if fw n7JX`1BfL7=k~0 ւ7j_vAuuӒ5%<w[~pRv_ DrCtU;f85ց$f1`ag9!u: -MLAojl޼(ͳG5z/mogSJ*]yC2C^nrU;?RlZr  ys+II>k;dSi ]d2%Wb6,Q(4ftRlg\“ ۶mH_s(sP]t2*ݤv)6?j;?(NVOҰ-uQ5oHf3f䍍ۻ!HN5hh=#RR~}Zn6m$쉼XI; ]0r7 !U`n*b|.nn0fouuHrGS GuqY5Mo 6/\0+ _^-a2hzwC^NasL ȺF %UkĖ fW@Y`|9o[=r1ɘ6K|LKk[Ci3I ,Nuo\t^Vϥw$zIop &zY4 IDATx0Aq ;  ;p!"$ٹyn)AYa`?3>&oVF︷;ŚEǥtӨ" o>Qk+9\rLeo/)Pa"u kw4 U형K5k*" u:F  88=HԦW(LuVY" 5'Hcͪnh䄴:rrl%!DzFn]q0^`f9E@A7~fV/axheM;c7nP)fp~v Δsֻg(/Oz> Jt?͍#N;vs78EoH3Ωq6yPq~ĥin|tLH=/wTrGn֬ vx1ktHq߿ W L߿);{IYX7hR;wb><azxHLS#Sx@&}oi h܉b)~)=D) Çc%+EtM*]u& ҟL(i&̂hӅ1#'2.`k{[.Gvw|^_P0[GT[anڴ^L͛yɣVFE\U8?|’CkԵ3ƒk:0 XζNw%X8VV˛l8s(ɆWK:t=pkg:<̖]9` ".\ (ě9+sM̚.|Ŋh{UDэ4,7 D՛i vV}q9 L'ʾK=u/ɭ]JyhD씈9ڠM:TՠM?rIGM8Aqٿm tTqG[ \%.!,(T"=+IcͭˍxqfN/).X_9_*cE>5Tlgۖ՝U\ $rvϺ:E]ȞX 8\meA?̙rlcb4Á:|, Lq> "ڵh߭"\O}r mc,5rZ;+JRrBZw; j}],.SӭO7#{7Qq C0ӧ}"^mj=ڎd'7>*YkՀJ\83xȠ|t fPF_JcU<TPu^}][ ;>?b3aQ訌,O)Q56?[hD},8!j*S?\(V_6m|. K5F[ D4 7Q:u]lNN eL&=X4R]J89ܹ3; LMߵkٜH2N[D0+G TD]]Q$Daɉ3 n^8O1rFtEX`T{w (zﷴ_rKNBN\߮?4Q[s#&H1*A͌(O1]AqcRЀs? =t>{UU^.]ٶù@1㖊vBAO/嵩QPtgĞ~逸zƥl=gd'%u2ڭI7~g.c}VGX0ޣ#1lد` YOjS?;?ѥuK)-(AA pjN}EQ䤀'AVFF!߽j/3$:Bۇ_2S%9}Qqӛ.bfgP);D0n_q¸ F|ýC] FRЊ'Ch\WS%yOJ`BfI ȹ8ϊC>FϥI`uvN-4j-N` )cAٗ/ ;pP7Nbؠ}FDQ+WY8:F3<< (HRTPT'i)U?K{A@iK#G,#N'Y>wqxEFFj;C6o̗lt@fLk3{۴(қEMtjl@Azљ*++Cȡ42bbԞy3bJZrmbb@yPCӧOR 3Kjj~bU7Try UwG)4>]@M8S1q?P.%Nss}p6.u hvX?Y7rw{:z2GP*>-eRO0++ÃH& KM!ji|}GO\aF`W/\2}Ez"leͮmWT0;1r5Ϝ)ƽ2)I fM+Fv]Fҕsz3>*`-n=ilz~͹}s/$Ce#~2>+|5W^د[I[M.E:ɞ6q4-+@ &P@[ljm:9ƻ'1^^Zg/9%_ji?5 xm^?WzƓa:]KhP 8]:Hp9֨p~Wo0;4$w'\VMpaͫBIހ-Evc '0 0!o1?uyɔ I=@Pӻ\0賡F̞^ǙZ7z@0&54^s=):B4`нSNHn^|s 5~$Gx)_6[tF\WE(IM.ul=sg"%y7{P@(L':]|A|;0Bk?}~;O4 NKp*qoϒlغӑwb~l Ҝtsgͽ3<@W0ϗws ۧQ?7o)@^ܖv֔M)ES~k垞lz%? :ȑmwjo%'ڑ#? z;7ykMݻw 䠞ւ7erRrԨSy7S,O# TxUOđqewÄ!B9$X(a~.5+{H(_0Kii%11jOH7*UxFF&UyAF-J3$'rޅ3eU_!XLGwTaa}77?rpqp/Q*MPqik{P'r:}g`{QĔ0ԥN]ٽdh6h%b=P[`2K䫝95ԍ:Zkqu$R~􄬭EҾ~yȁ0fꙝ=K(`8|LyiE ;PKdyDKl!4P79g3ŷޟܲg>5sjSr#gF\5qtjy_a-DRL{~鉫rEƎ,\֌KJG(O65Vw뤌_*|Az7~åW}PCF{KXgn _m2ofĥy]VV@& tHڌ}ѨaP(׭>%EQRFMJP_gSZZ1U"F+-Lrf5h # ^u?8U(9QOIZg9XĶRО/{tŞޗ@LpUjj޽[( xaRYH/Q!_PtQ& .Ѧņ}c&;H{x7 kO*,,h:cQ{ 3ypgM8^zv KC4wǸ_@G_Zd vKK2DFՏK N!}6B">;bw v'bZ8Z{#jܒYa ΀9Bd,U$B&s Ct?&afs t`r+jž6Df?W\u#fҢ40JlX&9sѭb!nǘ4coa1JӬ]pGD0OLYIQӮ*RG5U<8]N9'aZgƚ5ռ;yc${fMp?sHlqs}XHO%('B-57؃ >vۇv < M-aI0WN2ny&wG9GL]/b9DsD'QoWהSW v-PAIffwUe'*gݯ kԍŇ3?=+O"YLE{ʛ'427vچx*kyOrW\z H}hwhYZgCI>!ṫzOKrTjutV{jfC^~x1ګ߆ԉӦ&,fWR*Fw;.jbWSO} GB?Zӗh>l׬)kmk|z t+G7Hf27esK+.ܲ&৓W^X}'Z:Оvo Z 9W' 1cX܇ӵlTbwEufN33eQ7{vc@7M6fK6 d%1TMTH"ʠ,҇63a Sソ?.#c~w>)w.<U;ȏR/y˝5)S6ڜJGmo QvuyǯvT du;8^&IiW,_g黩:|{N<;D%1[8*mHt߭ @09mq,fǂ7ݏ Q(iDT&¿>J;H:yrJ;R:jߵޱ'%V.VYƩTj O'Nݛ2ZupJ8fTvsSc"<Pk%Y9e %ކ_ CY- IvqDPiBgt8lFFf]q '::>my5"?ix {,N('a=}:a));ZT^QY|GtE?O/߭7es’Srt'?WzmjaUĿwN=͔[d:]R3ɖ"F 7-.$r2}vPҺÿA*cuB=s7ItFW®Cg ۤL}7*?}Vr YRzKqF>gRdX_?HH^cI>ӾS6@rϰb@qix&фpq7mbGZ$<]eA-|ޭ_{CK[0.ߠ~<6}r] M "|t9xa}\܇S8(0i3hs*|gzoQK.yj~{5mMPS[Yn{iHx+=$Nx9uۆaه)lz0ϩ7<9?1YjP^^\|^z7~["kf=&_O<mM2>$Uߤ8]].y 'y:Vk=i4LT?~wq?Q J~;/'\OK(̌ FFEK%?ϦGOU?eV4$bMz#Gj.`2"dGJK4`扫bm۶RNG^,LKԠ\'L-]X[+;6kSO>~5J] 5S$Mii[iSps8h"r:`.>f0.B3 {F_4Q;p`on^v)i$F7ف*mH+s{mB78)f2ΑoDTb62vҏdffojP40hX#IzBJKm:/O4(\?W*u;w:}:2w(Djiqp2V&u߱#VQ,ǘ+Qgnz%x=Nt3kn=ǜ}~+Q3ETchwx&n=uR#h4̴l&S;Ѓ)U+nmQ8U*ILS>5Xzeu):D$y/7me_v&+aESE9S#b:n?@ĐtAyQ%V{V!@!-,Jp^;@`V7$C$mi8)2{zJTp:r{Ӳ{C*jZB$I|ÌI._Z峎 D$Bfj >uݞ1ݡ]9{FB*^E2 J|0pS̫ ^pk?2[L0Fq$4gRCx=+{l,ud$ֹv8dA?E A_@.аEz7`}4Ճ\nNn3|yHwgyCHZi{Hǎa+/\r)O͖qd._ ֺH&E Z$u'VODc|:c,B.햔eݺdWpʙԐFȤPŪ̌]oYkrQHLT'&U NR]GO>.e~9@*XY&Lhɾ{/ӒɠVFarRunۛzt|%̣v֢sei HP;n>V}謨?N@Hgarƃ88pyʔEEo£^V(U[^wFΰOZLBo^}y,~&/j$;O , ]{%|U$u[='f}87I+8&ܞ޷#ے}4",V p6!,2&SnCqd!||b۸sQׄ'!pẼЃAԯMct2u1Ʋ P)=oX:6h Dov/LZ.k-qKWi $ǃF۫x%PN^\Pb!_|Erz2 _4&bt99x!oݐqZI%nnX.;zc|"8n2;b@pFb=PJه0+@m3Sv9HO tu|]~yɖi?0;x"+"DVыEm7%Xd!]riߍɡ9oO& jk|4:p{z{ g-Lȳ!BM$o ЍۤSUԟ!-6d2B?Ykۦ=l4%N;]ػ 10rRK/|@-dvp>tIr )C̻A;'@tOP_"XWcy"PG=}̭QGmX}{ƃpa2OU^8s [gye 0˦^pwJ8J))[4f:]Va-M2C0.qQ3DG5?#E0mǰuz~M=aX?a8m :fw.~#" 1 VK(`K/vY/,MYEйkvX.ubAÎQ+U\1Yޘ?HC)7?UmFqq"twwSԇ )AE)E{5,\j*,rxc Zn}VoHރdeNVաo)PhfN^=N"tx/4E?H9W7>bHDͻ&g> |Φ3UV.yrAȳt6_ԏ7oK1mPeWw7Y׸!p8@s:H?:^_3rnqqc |Kv]|]I YJ@JV|jGr22ZqxxܨPh<1VekЉUG7Ώ a;7RNfٟ^pt8Zw^ c^,~η亙1y1_vG}弇=}S]%>K1!~:[5Qᛛ͟Y5Qy>nޑ*'\B `#B0xzqf%I:aƹX-}y%wW߿MR{$>tD׵"{DuLK)j:]c%d ֶW{bpdH^.S%e͉(q%OE24b"/^.A0qN>ڢʈVIzE7q#@J{ڠ#=ם6墖|CR$:}qhp௎tVTrLh ߩ my4h>9 gd7¼䐌 ,}y6˧ItYpI5:m䰬Lťpk*$,{P \[[Fl3#%Kbv܇k5_ۈH0wOI`.][,ȍHލVRILKߍlv!򎹉sN)`ۿn̯4??RsiR%6[==9UCRSMs(ûVUf@6P4L%,|XVVn%z2?T?|ҽBf\hX@Ot+`37fLuT*uqO6TjW(Ҳ-JߏqQy⪼ j>k'׽U4Fp;v$TryǢ\ci&5.sEkvG d/&3Z83kMNխtnVlY¬)|$ 9," nVyny%?gS;b3i"]O:;[cߨwf/FLߺEOΚIi L|EoDf&3 1|ҖvNYH9ےqX!ɝ7n$,&W_&jij@TT!_*?{:t{;_(R$޵錞wJ4=' 9eGt%g//

DK4if ɠ;|o9~\( _~< IIҦ9ib ,A]#}P|cب`Ok;$qN+567>JqعZS\]T5ž}'mݝ֞u?Hʨ^~ _ 33+++R8 j&YY-i?K%$BzgxMP'u+3LN۩,%^USjM6zvsr fb$]X9y11+ 7.Xllk゚ړ&4}I~-e9綖'xq:a4Yh· t-vS@؍dp3"G"}NC:~w$a8q$YHߗ3[3W-X8q$Ŕe֥]ެ޺풊\.qÒ9>uMS.*OXgH8{EG;1MM_in8χ> h5:(9(;9kAR`i \;[ &;eEPO$J;rt )j{M6mikB9N>=D2ޚ&$9b( +Gg ,Q#15BDipwЉ}Nk ʣ$ecv@t0pDcKg{P˥[Q&w|\4MCz$7;UӃs /$8EVwoOg434J5lɛPQx9O8/~2Tu!?;-_~.[kw4EvO\Ȏ iPReRRՌ״#k ʠ[+o܈~cH%,-pOjhT+V.MH0?>͙*' o@CpOC8ÄTRO5jB#KJ(}KWr0lfcdБzۯb4v,E[:jy <7Ӡi<B*ں50mS~k=c^jljep]Z™wplml:ª >%5$r'3P,X<3>jEe35*W揾ጋ5ut.[;)o6i}'b|lAGσė;;t:f#4V咾Bˢ$y \#KHg,:)0oKw|,wftf㊰C }70j']#,ÐUU:r׮04mTf/Ǎ04VX_hf+eo1&?q#}_eݪgNٗ"v,{QX|xH{oyH׾17f_HIْCe$9M-SG8cl?C`Ĺ܀Sf "HD;FEH/=ä JWvTmiax zHe>tm2YyVP[}K*~uC`e=Nٹ"CҴ WPCin>kDTLv"fkڼNٗ\Ǻs,CG ,0[I:M~ELd<I0)8qN'wTvFΠ IDATgFpFԀW>8nc]i  =/s1|wJ'I&\U˙3nJ.}1陙i߲2I[|4##3 ѤJTOxytƚ9R0%ڛ*ylEMt l8~bOHN.ݺupp5(xŪAXHis(`>b->+_9^,%0y,c*c16v$酩zb F[;+LyVĀ@z:Ys]r bWq_رum9|!qwDS7[L'Jǒ}XQܔ5mKIktDƓ4w/$1咺s;8oxv'$uvcK7s EN+,V-4>f BIbz乻)->_9/VAUܸ3/kmS_3̃mf<{d^z /t>SGfg:~LC7 X1[rԠR}<.}:K]9]8AOj7w̳dSOD >sc-ϤsZ#INN?/Q~9|ߣX?Xb]3lWl=P5m/Z4!$_z,ijVr0+7au|: HGI#`p.P|]85;a&S6C0Ns:.Jmm:\I!(LONUy&4U{qK _Kd/P8}* gO gpyځ,1E`)IV";Mmcwk]T痎e:n{U2uO%)gz^[l%lh{y}9Y ܷ`Y1{BDHd^!*kܰ7CW/@F!#a8*i]>v_jjc5YrF?_?n?iQZ䠌`lr=!PMߔ96`껱c~e8fgt:b3+@\xil>AS=C{.EkOe!C֢i*žc4qS 6|՘؎{70*KI;.1x>n.a.^i]ec]|bv+ͫR|r,s{MZRKrXM0dU?q_}#7'ZSW{`gbߏHfeeY,׾Y#f*lXhÑ;GY?W_hX0}}MG2w BBAkY?Z)`wq#?|6T++ =q wZJ]2l޼ry6%{ Չ Eؓ)I XTg\P.,?}+.LwRpmY㚛{١7tG<0B}/smHVed‼oNN=aUGf:[;oL1ԷJϼ{ E\Mؒs~=-sMjU}7# ҍ}/߯A ƫ?3Lx>"s*N6.)kb'a7*)cRr$t n"eE[$A өf5! >3d2pw(xdFO& TuN^zkoވӴ mq覅0~iHx878JϤEN,4wa+L:CgH!0Fof-<}rRZrHaǙnѭۆ*陋]St%SgdZY̵4p7wSiIB퇡'=֛>f]GR}yϱ5:~;Ň[QzTbOwZF]QcKoEf *5{}NTM+vgN : s\Ԭ_~$CqLq0x!R ;v<|֤>ЕNϱdg5|p`1`|<<_7蟖v|an08*u\,\Y*dۢo&Ybo|R$ܶm֭[}Ûkke;v$'?LGXb[IHQ^OH|' Hpy\Πz򶄄NNPClAN &Eν;^񑻪?_2}vPXGZ.kbBN`ܲ2u`&񆩂+Z * /',`q`:/z p?O][]ڋ:lBVyѷ9ar"5 LV{M@6Z- )b1,EI_H:OVUhѿ&,^`sgd?49}y$$bj> @l?+a5tat_Euak*y=#K΀SBDL_](u F0æ['~98 6|1S姾=wK߅Hk™ꭀ tg#%F'_+_'Jt!jZͣ!>-|}oU}i}WʏFدy>FaIs*`2qi`" /.v`.Ѩ2Cz4G8ɸnyQjQ8YZ.p.qۖ.zc=HNh4pY]=/aӭ6?$|9uGҌs@Ʀ!^g Z_X2-D?.EXQtwz333HkX*?8_oMj怮  8-/ᦨw%Tjjώ*zEU6Kfjχ9!|7iUƢ!0!}r?-\/jr>8S~db^&MܪX.uugK|p" Vb1x$<")mFUddd%&cRS*YYju"0䤫ժ4Jwfo^mF~L68Z%/uuv[oEt`d{fݯry3 am?O,^_g8jGݞ?}L# 09{_/wBk݇qX~1f t>P%ln8`9#wkcJaKmV-_1b.WXnNR9bu\r# p"kQDc V3u e8hqʭo ij!8Lvo_0(ߛݎ $P=[Ew#y;:Ehvfʂ=޶15C>uFpX{@cj%G"dw胏浮[t<݃pfSOonsDw+x) L#@5}"tJ-a^$BLcqL4=N+duLK$`yObb rS?kjpyus8 < a2}84 ; *$Z;gN75-8H n3L84=V`ې|oi~D|Ϯ?M<32kB;2g7p]?~j}ϼyHZ`N՞;w+SߵYƦe} [07 H (T (s}ʟg5 (#lΝ(+_<<:nRKԅz cct27 BM״#_\ 3&'^sͱt0rT}ΡtfW6 L3yy(rﵔeԄ)z̽{ӽYKIlM_{y=KvS5~r>SW790tkxgݤo%~BKRV6d|tuDӝ3F$kڎGE qO#,b{ m;F`W4m'.Bߕ8b|~߾ӂTjy<~qqbM|8u` V~~˛@$)RTŞqGyİ07G[YY\^)?}ٲ~b"dETb^cr5gic̟jRNH(I[fddkeSТ=Q߫! TDS#Mȡō#T~"3wO_XȻ1gNb|l;-2 wʬtc7Uw|cd"6sWC u N]'NFZtLNW-;)>N87sr.హ啸G2-w昦D'tVlv"tq5'A&O |؃O׻dra'g.j/>yQr*(Әh[xp1/\4;0s?; ;k203 3D(&XbM&[7.lnz41ػ &0{Džqb4~sn{y@[ՙ;Ӧ v o;uNV5a$ C5^%9,"{(&xa"~byXӈKS@'/`.-p: R(>Dkc@Qd+dBot0$, K!©F @韓jBpGy$>țiU*/D0w \}3!x2Ǔؓ8wШVE;ߞ o~{?j-^YZB\S">pB9]udʸ S2-8}G?J++{޿J5J`Ԩ[\1JmD'%UhfSݤ L-}<:g~1|ùY[8K^m[uk}sW\QdR0tdf8?/ix`IqS>> OuP4|hh4}#YRK;|1=dIԣ#FjEWS<ú6dşR03J G4Yv5JoOr1瞓g.r]ClOgsHY p lzn|SA],¹x}*I˱re unx2v]D{Μ6)T]Anܘ<,V<Q$MoMx&_߈rQ(Jrؘ uTanDIL=!^r[q${|#` iZ{FJĬG"˙H\}DQcR8Fk'ҖPb:WU Y#^*F,&Kvԉ8r*$֖sl9OOj6~v[Gﺉj}VoJ]krQe%`/]Tח>L w& &IV`8Ӟ#(G-rV;0F-w,*Y1H]i֜ɨ[ę1y7k\i,VUrRM !Ux%NvR1rEy} ԣ!IϟŰ. %W* 6t^_ĩs!q4ɐ\qFvgbNHC[BM~sxEe-KHC@jTخk_0zA^%?Gڟ>P?֕QhI\QX?Ѿ@!H&l_㝥AWaoRx-L,~;BXQzc$EqY P^HRp<cum ѵ`,WAts#GjyUt8"YM$!ۣWMP4ttEyEB@|>YV(iݜ@TZ@~!,@c2qfv\Z%ZctZ -/޴)/ %[D2u7 0B+R۷]&nug#s֔uƠ>_̨Jy-1ЇM 7k^&@k?CJ㳨T޺H_a9 05on8NwGP,q5Gʿu/q9h{E7ul[Mz G5 6g$a/T/<-ߴ 4Lfoث'g5x}vPaA.'<µ旞9e47Z?%gh&|ɹaʖ9Gr9'/~2fD(8tuW?JjV@f'2B+ćyJWŻq[Ӧ nMjD[inwʊ ӳe' @  -/jbs6!Rq CBFp8_(.KYz;7zSTHuOmvnΔ9&awv5IA_=xg!5@Q$pL' \n2f&SmHSz{%pe᜸Qd1YF$|^V;s9!p)EU{f SVΟY߳{wm}OW=dƌ=GJU蕭NSsܸŒm[H.-u&wZQݴ<ϧ%דwQ%4)8"}q{`Kc|x;n HizdO,mS*5kkDU)nѱ?!jz0yhizŠOH( }!ݿE/[vO&lLu/RYE<6Ȩɑ}c`=1^gi~"-%,@n8ZjՎk#0B3Jί('cdqVBxW2) Lk[u"_nWش [QRZE-s^jk6Ƴ: jGn3BVP-2)v4q@ivW :_`h Ets#Nł[@oHT\6S~#󸂺@QKƠu٢Yt+d*\ G+ |fقoa.ϣ͝~*b ޖd04KSs \|D嗺&*:8s>ف"ޮ\p_i3R WqnJkl]5N#hQ|Ŋ :φPK G.<جm#@ \bxSpV̎.(9 :s<]kmޯ ~qzCB8+FKxMK'+[k֬NlؐR׬1hWӏicZn=ݝb=Uff]Z G o!Ug6mʥ1\KOD'tϟa4Pְ Nt(BZ@QW ܵ#~qyb^hϞOFxWYK(Lh!!ј߇ؘO"&F1sa!zJ${0 vsVh߉u˙m: D-_wKy=#2զ/,]˝j07ean@0>O3+w5MQs:P;kc_jM2,_$ **q:F+v ^n[(f!KP=$gZq_PT,)$ۓxr4B1p@Н5#H/T/\n$b'F8ݲbaw} p/Acdũ1øMhL \YY#s{Pιw$ߐ dR߈ H^W(p >jpmtPؘ;Lʰ9&*Qzp1\K,,ӖT>}տ 'zӓiKu'Uڏ5Uap~NQߒWp`j,/T"sX--u׮?1ZJ5k_mnذ1 zKۍ7LHOh|ZZtE4&s5i'~Uh˿> T颷}!17"E~}~pTE)c?O?QEfwe%Yu8>~O9Ĉ7}`؊W{+PAY bF̲h# H)k[4,wQ9}9yf/ѪGa(Cmז H XH]P|ji(Y\X +z&oc\ԙddtkiLlײ5g=?g/rfLޗkQ~Liק/maJ""Lt␜<?LxӊDXiyL@F+@r J;g'J>VVA}#f3XE2@HRPߤ˛0[ve Yz8q4nwH/`-K pֽikdcNדx)z( ])3.ewEERh{G";ktE±sgƏ^`z5ɽL X&3fAj#Å;^2+ׇ;݊m,6E\w6_MZ5lX7{ju6]Z]ϙU(j6l'4<}ԶݔfkƌmRt{b%%pjtW;&^?7 ֍Iꌏv>mYMyJK]?ۘ&+hl5-Xw{~ h'"|m4 "=]7%+撩veVI:M~H#N%1Аפ7$MQs^Bx+u%y|ldTkr˅ vQ&wfb?H_wO{/wzҗ3-qrcTʦ#5DQ 0{Nb<ΘreS;S˧5ޝD77e̛IYԻ [Ѯ?VX؈N#$ DžP1Zz,"m'cP p/ Cv{hˊiug!1XqM= Gn/iX4g?ey Xz]֐[(D_ʯۇHnOQ}P&q .0,6ʤlZߓcRV!HBidI/?ueyAi(Yqi Aw_PzFԑ"S(b0l!vWwdi͊݇q{Qa e4l@B~y¸W@g'g/N<~ A(%q w:+nBAM5yG`go8U;C `^/BҊ;ni [[S~5j_9!6Y4%6m0SU=hLLA(ZQ#Uoy6#7}:EU_l]H侉#y깋vdlf7lxĨ0a}CBBH!Q۷CaOu,M#~*cfH@˔soNtE/a`+.>xPIRB.ly漒6g'@e]ŮMV'#fb@ x'A^Hc>i1Ll ^wH[U7Vڲ̱83eMWeJ~7%T7Xz~hkhp L\P$/mA9RWZsn+뾉9qV-65>`vO/ FXJobHd䓝HH; Ovחٞޱ%s7M4r}P'5۸Pmuޱ|{!N73X1(x-%<y܍QS@^W6"@sW,پ?X"'o]8z0ےo $x8D;޶YOP8|l+28a4lƝvB-Ly}fiG"~^wco3 6)+zoHQ1 bHY 'ZIvosk 6n gto5:M$/?C?p ꦓl7oy+ ֮'6*UT괴Aā ެKLZZ4_h4={*OvQʵ ݩmOEgLPmZhU3Y] 7DŽ%3EC=YT^[̴O8@n(H qi4R g@wq}l9g]Ɍ; ǪT6okf|n,cHn-I;|!3Y!#C:鯡[ag+P[m]m)a#s[iSQEYOS;9:+H=A[<72$$w(f))m:ӺÅz&dF|.u{o/C=kr]Qj}d)/f]“('q@ԣ~%-⊍=hKrc^>Sr(°+'UfW8[0/S8~]k!@uH>~f{.㳄TM+;6,L;V?F0efa-[6q槺ihMK} hP@oT(iB6:Us6?];ktCB;B^ݒW_!"s_Ysgn ?N[SF#YJKU>=RU*uNNzbǻD/8QB(%Z4|B2/Ǵ8'"k]ۉx}PC&MZ8>|Lw[WmQsyF>p!#Ȝi.PLQIw j N}g=@HDhGCSjtTB8 /ݰME2p㕨/w.Cr95ͧ.P hvV0"pko:d jH@Bry| ,/PWֺ9#P)6DGky^.0hC%l|uPw8ʯ(Qq!@awԠ^p(B( E$BQl}Y }OHpP IDAT8Ό=p"@B^Z޴bQޣ #DrtStBb-|BJ|8?wSb%n;&sIϱ F#qd`0'wd KV}ti;+y`cǚ@OjyG?znXٽo@XJk:GǮhd[cgQ^7:vVfᙷ.FV(L$KSwWSHzqq~?++bɓKrRBK-_J&X^D7(8&G(ry=58='䠣*XfkmۋQVMI@eSEo VRw\Sё+  19sgIrM5J*a~@ٓ^&ZE3wXCint?2g#0ۘQoI/rH=KX1>akƻ$1cId{%jiweV5*6] {+Yr(]'D0Y]c kWt[HWd=b"v'PbQ>X[k$)wv]2*ZJ26u 01բLHp9Nj:]EWߢuxs6ֈD+ZvBB}2ZurN͔JQl}ggtN ntQt{DYه*hWHSl)n He1Mp؀6CLW"CNg%~6t>CP㸑-vL.C(i͌:Q>tP`H\RtUTa ' ޖ0WDv֔gLS*Jީj51cZхg\->}ًL=qgX]2[(e85×& iMOm/Ŗn)C]<6܃ ]z'+utb gOf{}b~ZTx}J^r?}7_Ô&wL~fYR*k/_ g;0)>Idvk"lrM{R"TL/G}r!"꟤ӛf]#ilO*uyyҧЃs4 C;wݏX~g9w1ϝ29mh!zN;#F9+[D$V 0Yw LNp{!E) (II 㔔;^~`]!,snbN'mJpݝ/f_^uӅ/u>;Sx䞣  =痝:]l4,tXg| ]<|; F!/0t@{OᢎS"ĝ. RRFƉ Vqw8\ Ne!8m8X8dRB{"+=tu$uHV͈:q p!XI6:Ѝ}.EMA >!=GpBQXPsظ}Yf b8(pHjB49p[}B`w B([a>l0;ݡb O2A w@{Smh5ߛE(Z/gU*u.0??e⋈ ]jhɷB5oZR?]I a5"U2IJ(c{oϿ~AE꺫çdGݜ >ɌݫTj Y=9d.`J@$`e)CEE^EEc0:.MP_3fhhL+(,,D]?y95pDJbQaab~`$,T{LyMDdM&LvÓc4_UBw]9mTMRor/~AQcnԍ4_3!yٝ;2y ք*q;Ͽq&]J>z7RKo_gTGwl90dIZ%X00YNL"Bq\Ⱥ%}E+PfWPD$_ei.ì/XԴ pdnuȐ+-'wAIق:0bI>Ϟ9r5g#%y՟$}ߚZtaO,b{iW8V{ً‹DHB|#fxXsBH7?tadSfhk=&G1 q1M1pPd0#0[ْ8d›cc;))m; %U5圮nPd`8}mtVA˫@`pKnlJWW2"M_eϥ%ׇq57[]oKz OR(C'%6;S67|vMT9;qtH`UEEEsj4`vFc -hr E]O ӏ*Ufgz}i;4eK$P˫xL\EL#/dmdAAfFر[Q,MNnTT$vRҁjW*p96{xYls+6U|dP+$hW_//(8#ù1'ރ؈U+߾oB_@͗f>ڹyU ^I_}SVlXK Me/TsF$m꛻L/A$t)ŇD(cg nGF}!\˥ rA]aJat/Ha(;.c VwxB%a7F@ߦa_>?r?D]f,\n\^RR54Rn[ >xA1P(>_ACa*!inp&28LXO,(T([ )kJ䕩gd81{Wپwv~AʘS7r%.SnDQ=; 䩖mO8J.䭕g>[B =- /~jQN֫K p{ݧ/Az<ew '!TlڊFz^``=\J0}'CI'nd\\+HR!_vt=RY:YHxah0'1_E{@ ˕]8٘M%;2d^>v4t- Ow PԬ^d1ď=%FVgӲ 펎-"]6ƠZƞ,l%MO٨߶u'SH~GSq#:@GW0In% t #9;zbe7N//*U?z;eYzǤXgWڤ $86z=2uo$%dq*9zvTDo,'Bӌ XHH0k `wEA}33qhnÿ}EŸי(0:ֺ!O:n%,NHD]/,0-Vw;s)F&r3>|Gw_p!%yQmGqɊ!$)Ra0JV\֛DQ'ԏ %5##-|@B)Wizs@Xmպ#¸0"m4H2Lq^ ==?): wO8lSE' *xd-z#'YLT U qM3jHwwQcCMg }K?0'YÓl9“xjbK3ކ-P~Mf@Y칹hCH'O?imoU{)jthS:^rNcFhcud\Vʼo~1307俐 \96sifְQ;1ǫ{5mi Itg+JLԏ={D{]Ԡ5l-[Jim͚:ͭؾ}ubX ^YeN _XǚF+\q/VxxÌgB=ɔOTӍP &zSFiB'( \w׬۾-f:/|uw jCKXt[\ˤ_IG8.A|>#~dU?W^^gOa a'Ynn)I#<'X/.$>M$mSF̌OJ(k/!V`QKlUpoٿS6.U:T]t=#85CZ}tɫjMt~oQO(˗-%&y^(Cט2ǟ?-5"TU qEX06ysl&d1l}[OJbko#lӬW}!E F>|-pl幼thDy9 EXW9adpfnη4NPL%^D$wicWgXzI:EYhuK/ 2l<?|~]JJu(;n@!Gk2IQԬЂRO\JsG)- $wui9g`R%cBbGW,ux&_g@abA PGvܐ3^zFaWb8/^ys}bs$E`AU@+h{+ e0}+\ؑ~e \+{hlْGOC}C]r<ͫ,Æuk͓A^zFcZ9$hFz9|g[z=}֬f*t|s-w19,OA>uB|LcP>P7<1qLDai_!6J:] aF,5kvlؐOsk=_gfx~pOj^_JX"/oma\w(_d&}.jg'*Ir e:Kf!,P Ͻz_N"SƲx[/"(0yhsARE{.w{ZI`($b¸ k"gQFf4;ƍ`A !BBǭ6f{mTqtI.\5M#;yzZXn^im@ Fȩ wuMϖ<#|4~tD^>=\FQm1k , s9hq|&V~a#* IDnc҆1v^ Wus*xҷ>g8;]!]st $- S"OC0͖ ym>Fo/dΈ s{zRFfIEI ɌkPycʮO,W(4M}f&+6 eI;I!&CMw pIHjT:fʢ;⺹54zEayB g}+zeɳ>ìܰ!qj7vaB(A_j.8[YMSBp?fD{,2|ci4JLG50: Ɲ\"﫯WoM=2R+;6pSm>X,ysV}h(*Rnڸ3E5$DƸ{Ck$M$|%mnd7Nͺ IDATMuIbq q)P:q= F3޹s.iI;Z̉S6Ake eժeeZ*^ N],JjscV ~e?̚>U]>E,(5-2Њ柽Lz:=4螱<{ȽG_O_mZy GhpǡjemqַVZ8 CEۋuX07@S.co cP9QW=uOPpyWME)95,:mCŭW?>,MySofOa` NQ'v̘>ϯwN0<µֵe"1`!>y֐7+~o!Fj3C#һؑ veR^~#bx<twBdHihFܨG8>?7m_&A8 D֓Srtyچ+lI+;A/\m7C}0љї8  3%sH6j㊼Vѝ(5~ , B/z#jXUsgw%>zљw2}b4clwA@o03W+G}|zZ桇>Thq2<ځ)-}xhWg`3yfu]@Q'R]3_Bg5g✬vqN]òZӸ'%hh͛LM5(W}Pn36eetIN6`ܫ/^Mp0Ycnv>=рRyb4Zk5"ZVU(*|m1O`yL4݅0v 5w- _]agzUTz6ײ[m1V -|xBIb3{Ć] 7>C+(ir A!vVn'?a ^c"9T±;RR "I*B^M71rԑ7K]Y|G1|R#/M|#81\Sҏm]gL1a1Ǝ*cP*KQs~yd&ۈDI}SՊIc;jxΆOktZ ?`,9-]{A2FV)Z#cdWۖg?ZPJ$7QwF((ȯ$ōhzͺ羘5>řbfH"nBC{U1 ó;qyR;ӚiT0}4X7ykj~S}Xа>'S` Ͳ9Nݨ H0&:% >'h8 VSxg6~Cc6崫^z6n>SV(kJ6hC}yٹ|Ot |1š}!g7k?d]W5JC}e#_i٧zviH/@ c03K9In)*ZAgj2HmE:_%i2X!@DT>?=n$#mXz@.#یEvuK^͉[ıXmssb^پ5S%b<^誸#= "]# p.̯5VwOs#{bCi>/W>y MLGT =t2}C4$6WIUc;Np9;i$1bb1[賗0P*LiI]`ann3c(HrioX խ:u}NNM쎡۾"<H(<γ;@ eӴ'K0p#͈VDdV ݙ|[c9~.GVSTuꤋu[K VmŗmLdZyک{_nD > _9~Mqoɍzf[VBx?qޓ_5DzD?A!O;w݇8/ŚJ:۷oC=шqlnV`Zh -]ci(Ϝu,!&y>;,F%DZ; Y4<֭95T?/6_>ub1w U8[JXZհ?ƍ;/oYzv׫]־{wO;-Gf|ë2Ob>%2r<;~ƫ;D}2fBu[QC]^W:%]0msEwil yρ&i HRLo(.# Q $7nu\{R6F(E8Ӗ"X񿷚K#܀Xĵw|ױpі V7U?[3]F4N| OKI׽#IEdRշGs "Jj뿗@C 8Fx}d1-և941P@:{Bi&9gR\qfQ7*nxVnfWM+%Ϊh@(V_& V}DGPXў9ܮ_S0GЖs⏜wB>%E^. B}jxY1xpp{ ^8Np`IgF]-Ol6NȐdūC6{gI  EGق ԽoCӾ͛wkJd5ռx9SO@S~ m؂7{vk{8ψtɇjU[cW DZ㪉4;†| wU ?ͲӣZߺ'A˽ZT*; ғTuI׎ʬ~M\!HL4p+"_* ait츿R5Ưh hD1?i4ҔJө,VKmܸedj5mg}9a~Ceՙ.E'laUFOuCd#6 OHnS&`]ϓ#މLOuL{w_'ȯC KK.sKĸߩKHh7aRD0@/Q] ; %R1QrDȽ%sn~5Ay&~i\El6!$jܬr%'DU?pVS &R,c,aK_ޓa nGN˱;"_%8@qt9d {+&qqKb<949R)Kq4r]xRbq<Α 0l_s\#sH. J:44O/jnh>mb43'ѴdI+ ppëu]C[Ί/>cQqk^:{:&fKFE4q\e !U+9==aD]-?joK0>!T }aHpIyV3TQ03SCi͇؃ mW}yorWc֪NJm |MTu Lv><-)xB!%;U;]>!pl|F& mZդy'T*]v&O7n XukTٳ/J(lQ}s=ה_vO-.<#"J=F-C ՜R[Vk7IaeIIK/RSYx?ɾ6mWaSn5T2wnTwKM_lgOcԍ|80D De%5~$Y|c̖zW'q sz>=WF"_N gSNqt~t1 4𧲺1bшh/"괖a%nMc skw^fZ3V\ֆ1 " low5֧FUxV%C-DȢ*}Z&1QK߾~t7S zih_z0rOEgz)o#5^r"dS76$j1[9?;f_3+؛_4d}=a˟Qř.}8m9V(̏o6[}Pi-[t:eYjDic_CLlЏTTj^.w/!- Xq7R=UXb^6%ۨTo98\}W=,BY-bθXCn6I Ə<؛ZN`Wlڴ}B=___H=G2h;rC߯Vf*2_v?Eb^i <>ѣznY!)ԊW/EdD4,ip \$Ą| ѐ>9ݴ>Wソ  j+:PG'NٜS_[*|8gQ-$wY^ѯ;W8%Է]Ғ4}#NwUn0%Vf!]ߛDt:y;^%h7E>''SX 5{oٯzqDp]ΐ{ؤ^|&ZD~E`5qTz7Hz:2׏Nd՝Vpōѩ?\gfQ F;L@ 5}PoU5DSm߾a۶M`8|G泹two; 0#郟liN-,핷'lTԥwÉSm{vn_`N7v^a~6gK=qCE300JWPuPuVV |0zZI'=z,8{uPe~_MpfY'LVVR՚#n:;:ɱoOQ#jE+cOz=m;{JK3'gW*F&n@N?5V="PY/R5o%[u\h$Yg=?^ غ(}?g;t9/mKpb0A4{1G͢E@ѝvDnle|~wCT9ᮚ˴Uu JnbRn þb#0ﴻm91#ruhA⋍]f?(Թ o\@f0Vؓ5*# x)B}[ɳs7 O|lqjF8 `O!.7%5\7[_q}& / _b$'9p8 19Vv6nGպr RAѠ96(Ơ,Xm8i@lw8ڢ9;xfux~d{N԰nE(FݢyC{MV]G=BҴܮ 3Sv!\ƐƀP!ǖΎ%W6_&MO̟uzLrt!}܀%zzM?!]+)]BVVhlց HT+\mzV򋈟$IK}Lnm7q5f>Q^|b? K\gOE=鶅፼'X n~@;}z˖M")?`4a2JݢEﰹ)5T0 QiD 0U`/:ZuےaQTPWGNZ:Yl=;Plq-Ҳ{ lp31,c$ij&Mۿwv_϶lL?v{+zsZ]X99Uʷ-# GS-M~ur>9ɑ[L8vI< % @m5C7}?UԿ4rvga`ZmN~~)Oɹm_P1QyUaӫs< *Ή<cGw٦;Yq#j72'?+Z¾r lݚφ4D"јbB-l_҂X&cZ6CY*XOMuEp]H ѥ́0!F^ENjx\ۅpXNGEp8D5 a=n_)"Vu5*FT3Y8{YP|Mޭ,-t_E39n@Zy\_LTqۣV8 A ٻh_34IR] 1\nӆva!QcsӣƎ`QDD(Ô=q*q} S+n]C~L/~*߭s#ĩ364,R-hPDТ E9^NBS/īׯ;K% y, ـ#Q͑(AGƯDB EU IDATM]C+b!+FcRS*{#`ǎ? cb׎[ #/YK_[Y]e[7aSxgÜc'okpxG97g[m4}53_xѶV Kz{g-OTpt][>φ1}In)Bs~LȠZgمEp"[>}cnSQ*HM5,zcۙ1}J#22)u&'Ai}x *w.ʖz_J(QC2Vp7LutNO;F56 "ӛ(p"zg? 0Y>z^|d]^#3]GN|aĢcoaBɚ Qy r蒫]6v^¦Zދ][HVX>UGO^lX>IV;`" J": ).^=xTg1)j>-h7}/W$q/quB#'G3EL0$ɷذiL kiémtd}8Ms] G܋)ck0DGN8{H'y#% z7+F~:jN+ɶ3lQ02Ne.wؒju(?< @q#*H0cڐ2ruMupW K./AZ7})ՠRwb)FN_]d0w NgK/թNW)C#Hw #Kb5yTh;r-%Ppˇwϸ3\Q|KTmMbW_=}sً P׌â̾bNE9`Bp i&`> $H" 1))+ii|^_Ӷ)DU{fI@XNݢsW]_079PnmG۲[ U3 F m9M؂S!ovBwXmܯI)RR  ^*u'&Jhew!b $"= xeĢ)Me1g/^w4}q}q! h"$껇$Iu B2:7Qy0UW3V\Ϟ@|7*s_6癔zDz}ۃ2vY܆!t2l#_"=ew =6MADB_zp[PJJ4JhjV{>璓6OOaW]|{ڮVՂ#!B/ݹxVNGҮ,NE¶n'% {W|ܨijWI?_={Ce htJDN&sTMR7YnV4~′)"WzfǶn{VЧJF -.$hfI_M>v~US_Nzkj4%gB˵<)|r3;TVE@Osݺ"֎F;bʺ/&X [6 Qܔ?6oH<.N~%Wz4݇2] g†q07` )GJKgbY&-!z/mx3+zKRq] ]2j}vOzcOOQ/,锬{[CB8+[T[ QTL!$.`" YyMۯ 1j?Z2m*~x㫶ueKaNNIyx1Gط#Ju99?P&7F}mg/9ypLʹ?YA#.\EBץܸ깖Ҏi/_">f'Q߾1m'5ްӼˉHyϑف+Tw3S@t87&:ܺkRQp8^RFG>1 MZ$ÄcK3)J~c2ᒄTӆ5p)bed^yoĺƻ+ '1Cޜ 2\@C(Lں|C`yhhQ+GokSkؠ9wڼ Eŭ`-w,t)p yyYw_yDN+%Nð!*H'y_aӪE_\KzqߝB$A E%@ĵBӃSn\4y!01Z 0MS)]J_Hx},cIMi^8+.[mY#ëjS]e Y¾MZtk'/~pF ͪ )& ChEi!%(VjƍsI`+327?[IŁIy-lмcܑ!P#ܶ@NkOeuޛo(Vjr3pU^+OeeCF5+j())YgYMMݭhY(;)lOK3RSJ`Hl^|Cp:hxFh]NR>3cDzO5%ܕ][[dz6`HUtzh͏u>mY"1(;/lW{{AcqOW/wcIRNltDžc3>^x/_:O!1pIρSTQǹ mIޣgqKdcĬbЌ)7 g\nX_|w&NR$G7y a #|~nG'- pa&p&we'|?Ba~Q 3ڇMs ˳X7LssGO)Tg58t(UE) 4I8{@lPдn/gN0CF^*=3v `4jP!a$dHC8Sƹb/^KTgR Afv,[~ٱSw͕V%'Z?vYG?6#;r: UsW%%}&#}詵}G<5t?Mr(A5Ζt&Qx|H `ܦJQ1!D!I:aaY{1b  y\v3%_ۑ i@3~!8NӢJ,&{aa)QC3 \wڬcG {p{".;SUP8@H*)^Q'0\ѿ!qD KEљTp9\G7AQ1%efZ (AɧX>HJLSBA+J%w/\[m!wL:fBщ~Ů0&hz6ye5-{Ջ%'$ Msy.VLtQ/asyNv*L:oƐAb4Jp::(PBt O'm^23h3֪[j}TNgwϨZυX5ܬf-*KoMV/[{,#4mA֭[<{l\\Z65wJ7Fu$Z'?޶|I~?~kg+pdzƓD5HJJ4g^v̮hAAr]R.:j}~b| 3hJJX3ekq`YKTH6@e`Ǫ=9DZ\Ο?0>DQ_PXByŐ%ayEq2Й`a(gpl:^+c>Ac֭oMz,/oR::s?"+p?U[9#]\ƛ>Kdp>y˸OQ Oa<& aa#zC5ڱyLw!*ptˉP,VW28^Epv3\.xe]#NB=1Έ8bX Qb7'i:^v*}D!hgƎ:09^Ҿt>t~P NGOR81WIՒ F3z3L0^5P!Ү_r1*AsȀPGù]ݲʚd("||T+oM` Dn ;'Ͷγc4-Y7*@֭+0}U?y:NzuNNo$PѲuj5|ga ;Μ1"vº>/HUK-L@0tH'&%'p? SU _0:{>Vhڜmoaab ر> t~aZ=P+de&ȿ)h@M#Ks KJ4zFx_cؽE!yak#乎'l݆QcnU{RX)*r.lJ/zZq&ƹ$:uFe>A?KF͎`_ٳ{UWѪ7nI>}[h[,z1_e.ӟ/lV*d~q/p&:=V|6& 㰷{3=#zWCF`U7"SEA 0̈́@G!/s1$u,Lb"A,:vx@i1X5pzKe}S4amG یdIc S @2k<`4pqP8Pi^(y^6Aoi{p_uD-~qESk`h7#]0uu2!+iO~t L:D ¥2@ 4P̀dWQraуzD2a$ !jI}Sbw0MvM1!K\GPeΪ2x9X~-o⑓)j_v^9OK2?ƈ- pCiصSO *oWI`&koؠUQs8x曟Guj]1cqVVp]!UY'Rqbxua까ͱǗ"OݧF.ܷW[UG~^yDCΛNS>x\ QPW4Xl`fbC"@x5eNJѼժy~[tuOL8˒H{p;f$L`Rf޴/F_Z6mĀ_4X/qnFvYBn%-yI'`bb-*F}5ms4{ 6K}xC*π159x f4v-N2C5p=k6~Xy%M B49Tyy`gaafZ[֮R صbXTyДKYc]r|_<}ڔxHIO1,7% j%6N+k^J\;mnf@"zC'دL;>f/14ַ%?, er nكzeG{w**@Hw)4{S<~ :/N2o3oLL=Ld< ]*wQm[_9R ?xPIH2GVc4|n`5bYY轆ػ=j?OA ?1jdiΓ['LގyL7Men@!~ۿd>8jNsrc<ͰƕGܰ,{vrʇ7r_+^V?.xfD#S'"01rl~b> IDATӒ<^r2<B=X*m*^$: 1S猏 ݩIߝ}(Gi>Ofdd )>NUGz8=c9.ӆ3c @zǠ2] $jng˸6; 5XCLS= [&Ø@i) ch!wz@Ch$*n97}/`DQ# -f&NQGOׯ_x[+5ڢJ4.Gq ߝj?S]x JJUo1cJV g"h1m8uubZc:Vߟ<n王M>_d+{zk.e~D=E`H~veKQvv *Um0Z"˙xeZ6 vl]飅l溧?2,1y+Gos%#ZruDq*8Ӗ>h1u+$ex5;X9ȩq:B_/vf@ha!wqb/SXdz\Y6m۾}T|5vk?X4E%oLdqLhҤWck C9סf6mS(Ҿ;AJWAm0$~ŻGX$1:7KwUc^rBx=&lh_.E, JVl٥J)rǪET\er~SU*Ueo5//yGU*zI[pn_T[|ړ s{;[Cb롊iHGv2r}WҨt*hw|\BSץc_@F½ɜv Oe%UUf_$"0gFK-) ]X׻z S$br o=&o\tO9߷@|ɒ;IsRL=bc'N2V+V2M}b.dʤKBXH: !Ye\P֙' mKXCWߕc!Hk@',@(ps_|.{<~` !jK3b  |E0=IͽP=j@Qۃ{$Ӻ %85`cYpSYܧR¯N8eaޞȲX9=&oȘ;3Pt (aK59ݖ2pݒ D6^q8wD%F!NFf<i!$+Ό>TNq攄]EPČooҭF1]Ϗs) %93WKՈ n'0+ ;=QLCQt N__n(j&'#.==gSvSjb^KX{Zfu r~[7'6~p8I.pk0#1:d"'\nxLaIR:_.*rr*1쎭V~ϵ8S *aA<;r(EdMn0^"!Xʎf$T!^vĊfkCq8}48'r ZB+V UUy4}oH)YUey@)Qљ2Go }!?figMv#ײ\Rۮ#g!\$TryyU,{Q&Ց^[ "AuQ[*a;˟c JOqLՆ a$)X ka^jHc0f.K?a~q.aļ>ێKg<7p< Z¿,qU+0pR,&{< 4;0 u;Cü"~Q.bKz(A& 8 Gz B>=BLPSg3 䑈%5 S\ ls? Ĵْ됇[u46ݹ0̚jFL ݡ6LzbL4@`'Vc߲I(25R 𡨈x+O"ܮq. !ƍ5To#9BF BJ]~ D ﶻvm.)*ڶl)цi=CMEKe}8$uR&OfIAd/ ߕzW/?ѝV37j]7-k_+҆ #;Jڵ\ku% AP4Vpxff96֡jb/MdI{- 6{~KFd͖ /[=wJ!l|.`1{kCwl1O}]Q]_QK[SZUXҽ |3ʉowU]opK 烃'$;.פ̩Lн$NX,i6ZqC쒵h|"f_T2)7!Qv;+WLቱNW`$v/F'FEKFē[@n4qu/~ |*^1\Sˋs8ſP=pNt5J GI nmKz7}rBZ' CƏ0ф Bw_ujunz= /mx'Ɉ3) AUW”Oguc!3\uM+yO9&~q#gύ&(_KcdP/ލ2K K4"pVU {hrSAqb2)6 s81 #9l`8] s1"N!٬"1]>z/h 0DQLu2m5  `_י;3гbQw{:C44,Va-)"ND±.4Y ÇьuSC.@F\.&1!p:rK%- s.(vFf ac5`cw.8|9v(mE^ g jiqagO#lҩfxaT2R“>.cZӅWi4rmA.?+Ҡ%%/[T94MKWT b+<g&L8ؔ2m]kXq}}N{l #K,YpL+0ZQt:~PLFsXg D#.f?xw 0Ԁs~{G391 YY[<*F=if͖7f\үK_}qLus]CNdΒ2j&=R&~%HS֯}XɼA8TQedh^o߸mDTCG_Uu+^;JFH̝=({b 9_m/Ar?LZl9}wQ!F˙1@8 $AAΒ;Va|f2gGխ< c(w* a}(J4m@!lFy$g<nOYSKR?] ' RxJѕjQq: d`3{{3gTEHaaA!*6?fX{Z*~r{'Fie[G9Jbi jեl"<1 "kMU 'z?2-!ɵ§7]M+Ky :HF+nU iltȗvMˏ^}B|#n#e7o*ˈ}Aqߣ_@!op&c2 %AɆHBɣ2*ȳ 0Jٱi?@E-}1^`мځHr f-?EBm8`6ES3&.Tl 1mCdIu F(9'hHd1d!![R8Qs[ྏ{$ lLC XZwJ a Xpj4e7Sªd s!(3d>AEQ^0fòX;  7 L7b (#<qS+Oo>rFPS+dòFw*0}B]tq]V1jVְ+83,Ad)&H.{~NҮ`uȈ7fn`tzz#10;a5k>շBW! 0UV""4c]s@Jn\J}ˣOyWp松i'N~Q]cHKdVU ?dy(vvuٷ`1h}d'LX*T\H;7}Ekk2}@e"#ߕ997nON2 P{>fx?2{qOj`W ,=X/V0boOF#Zy6Zk͆"K3`᥉uؿzWQb$5(e&mk oY2IQ[0iݱ^fo5+aM@F`T cX5${˻SY,B8>);$F谺%℔wƖ60++Uuygk+2:~A)~] (nڵ+_?rFn02-/Hx)nt\:Dwn{7Z}Ij8/=A . @0p8g>_6xA@lJUe ֶ֮,Y zѼp_ijzw{5c0F_e0 r+d_|7P^R(њ! -k?Ԑ70,CH_Y3/Zřf<={aPԙ _|IΤ^=jj[_ {lmOן* GW ׊'^7Ϣ7'@|Ͳ ՙ9R?OY2~ݻCٟew>ۯfa3NE_Qr-.GYIkSLz )/_0e2chJUB />>(o`fEJDE`5s4AC3$̓z1c02,6Ӣ541z%AAH8N܈ (6p:+|:$\v zyTۺK+fG,b)H2b9k\= qTW4iNzGjGz{bq[6Y$${3 `BAM$ ֻUc>SnW~=.'Ą&}țח&{I ;~r2Q(-=>>no\zw0 MI#6MS zJۖOZ-RUʸ/^FzF B.{ AkO?]DN& jR/OzyOVUWgy͓C  ։25<U^wW:3OH< NCo+KJnRHSVe1= mp-"ii9W^9ViŔPb IDATnؑ`Ri`D&'dz}zrN^]YSe%{/NcG_giҋ<8Ogp.t厮p<8|`ݝuzE%ۤeP㳚_P%VUqmk? _ժgժԎOaov@3"ϟܮ=%yN%]E^ÒZ$)H;QvN>J ZH0<Ș3[ׂ̃Dwt$z̃ OiNmy<È+brZW]99xH !sʎM r"^͙q$ pzoO\^ތ:].X[1$mE2нbBmw]-b$.D˙杯nZ7`x&\&^yp7#@K*, %%/UVн-e t.&~K4>ygn^16]B!}VZLiѫnK(\ީVEs'{ڸjc|Yiex^˾gܡҹqǟ˓8_4g*Gh?ţiJ'1 Q)?~ˍ_#@Fp i8! tR6C,:!ikQh6e%o]+ ͈toC=ɰX6 2m6up̞` "݁B`^qǴWؿ8/ ;I_24{*SwĈ1G>N 2,rBnÂE˿\iqL}!^#jmw.^53h7jH#̓АQ5dVY*VCO5s"Tiuͨ 3qAZdR0 q9&.P:;i]}mă*``p`ac#G b0Yքg'4kev&%j;5ftnb$]fH!¡)ְ%X°F4gXx/<ތ!}$}MzCYyiJV澑jL>ݣPlriWɶ.by!޽:aZm?A RXXe^K)Zo~P򌆋lVv(݋Q݋J壘ܓmǗ,A̬_ L4~auac"0&m=jqgp텅۶mO4{TQFFuۿ4_(^MmGrMiiypkb^~`Z\RU97% QAmsx^O*eZ|L~jΜ]퍎HEo=ګW"4?~v~<5HjaC}X؟id\JMwڮy}-=CN|4qSx_^eo;t^7؎=>CA=>>0%hTWё yp$ٲ% [Y^ @ҭe(%.54^# 7f'.њ<"8lH8ñfMH"J VyK;&?9Pw0 0o} 5_v~qsV935ZGRnz䭚DhL?z^4\m{)BX ƍ+*ri_G;Ou,c_=qF˒ڿlJnJ} =yWqʴ^$hwP8IjwY]k ͛wlwIٿ04*𘻸\?rwߡʟrY!O [QoNpw|>j~ٸG>gf[ly8_c>9 _?^r]r\ _II_bDCR˫4z%{n>tށ.)x/?>uX~ sšfmLKB-3˶!wzQ<^_w.21LEV!1rSv&઩mE0dGa}kۻFE)u#2xs<Ы?8n`W;&[f hmB U}NJ^9p)( 3F|(ŪD4Tc36n=f\]ncDXC { ݑ5}Yq_oonMێEo j8 6C/:*UeAAaaa6ڑ.UeVJ9-ǯ_`t[vGY>P=dpNj!H2>T{!^}ާp7:D&D\ c34Lv߈:g8;y$U:WU mKb9tMܰa36VT]6+U4/g\{}'\$CWXĒY>;H});xl7=S%x=!XY ;Xޞ={k*QMkU*m"w1Ґl $dg: Y1kuxg1hCok"ι8O4)%n8P\5Y#FjІ\nl,[`qԛ}pESKU4vyyzdЦLn{v ["G (B=&$}8ϲ |tC摅77K =bt-]Y9 +fOAp]kx#8ͿMc>ܪy`Ix9N9F[5 ϟ j-{yB[>rҏjQ+C/Usg](j,<`xE8n,SʨZir6;b0KCGf Gɥ00r}$ Wݷ}>F2(7Np㊏v,(`ep Jc$_t'08{x<Ϥ*2p]`3+ik\4BŽ.k\~ȍO|kHߟG'`F7lrF.7DD4 8ٰ?Q#{<^Ulذ} eK@x6{h-ÿ2btOOV f36@@ f7͜z[aa9p<}hi !b0H6wZvW{RTĩ YV`r2=ɦ.Ee|i|䁛ƬuDE<3Jį8'lA&H["/@ c"\sH$c+Sv}@X0L4jÒ-a)8ۜ {oyANj{<4@I`/fG"Doָ ^K[%O5%AS"f[H{G ݶ Z-h G.?bkyUS:c)-[Ա[A-o^4q_mn >3HCZÕ '.x0$Bh"ցi5};wGO6?<.$K6'jAA H@yjA&vʪ|Aظ?d?= K0ݽhѤQ* ~P}Z>,6WX<S;)eeo %/w*4R?IZH.DN->w~'w+*ԁl@RoeBa]~ۚ?6<}_MVzGXx?%iz,!D|r9ɪM VmZk߿8ػ"DF`Q/o`?]W{wx,,XBB$`A`|sg0S> mvŊJ2d/rzcPWb8]ͽCP@XTk|e406}攇ac:nG,B b FUeH[jiᲱگeIc[׭_ʲXGwQ$X[+PA:CMsval/~ņ|Lɉ;cwj@̴ 'd -;{*߉ h| c\ᷮQķJW /愺yĽL-?OńAm(h=LBχO%}>"ى'{Z=ъHυ3Mlzb&mhTzc`ݣ7+}QJ? Tm6IA-zva $|RE%Ll+D#y< 380qJ)kՏz;1u M8J5ۂ[! F0,kvb*wGFְwktGGЭ?;o:v!-`Y۬\h\b'q۟Ky=s}aȉR eؐ-XiR.7*m?۞qygb&NtnDmrFQh7`zEi`iVlGMiҖ'6gvMI3⍿c"czomY[WƻO _ #&c11IO8p/mEZ$.]d!B]ܮ̵& 3+9՘P^|,9 a+=R7RhâC#)Y50ؓ;sX:} #IN|;FOojZyD=dVd6Yj4tQWs0 ~a]a_.a{'o8s3Ty,hb XQr<~ c8]>>7PaP\@3oi- +} _ѩ(=ѹ(7pH(mx\kzN-qGN4^}> Pٕk0CLFh 8{)n"Pmz҃1SفՂ^GSSDDP/r]!@rGY3(mXzDg'mˀ }KdڸAxwlsV-lBȴsLv[Z'zGwmzpJ{5@|Z{EE^Ѧetr+b)}pk/ARUJ?b6Q>\.wS* c;귭 do* ъtͯٲa$'TR=K٪CW37VՙSAH.*&W,X}Wӆ]і$ yf/Z_ku(K47&&aԴɶ3+NH BaR{Vqsg޴dǸ۝V8VR v8XȳG:b8B<^4dg.^%"Jm*"L m]@ Maz#"ITO$dL{_MxyDsI.]3- ?zHǴp( a1 "^Na)Oi;g%Ҵv-ϓ Ήiy36L ڀkR (zcd8e,=6Ĕ8GuuVE[ x0BtK%9Ӓ?ZQ!18IoWPidYx b jn{GGHUXE[QdjGszc*:(-'<^Q{'df%}SDQ fz1yt@ IDATYqNoNoZ$6ZNȪ9Zh]M^U_:жo_GUe3nE?jՁ/T}X`}_)<^d1'#4-ry}"tXL=:MVM:c|Bo7ejD g4t=)QW;їK W)_HŦ IK%95|hOG>R8Ȏ\;+q!bbycQԫk;Dz-B|1zm"U  @x 4Ţzuar*Kpg/ezDL3pl[+n :/B׮As;h$Qp [jU[o<7}pa:=#_yj@oVsRnkAneW_ٶm_zLVaelCyy3|ؔWOEvMX7;vqgs)9b۩F9گb8f7W~%w&ZEժu>QU/Tr/8f_6ꗍΌeoޱqZ$H1#<r\ ].1b#$?OZW J`65`"`2ۋ<`SB(g0dPe-/Ȋ$AՆCT!# Ql6ttbXgO ;Va\iwp=C.0Y+ @Ciʠ+UV2L/u7K&@K Dd= 13˞9K<%``c6$D\q0w?5h 0٣"B/WShBvNmTngz|ʵ? 7Μ2qUFx@Qn  ( bd^SZp|Ipx$ӗw6iH*1dpF?5seR""=ΐ`:rX_ HM]fl x'ԴxU}qtͱu+XkLqة.7 /Bp=4occ?tt1]׊\ Ct-X 풐k^jyoH>ŏBb[IƤ $T};kʂأ-"Wۚܕ mPwȪO(GǢ&Q{J;5zNGo io^@w+}wUU4 JcllUU‚ZUɧ^|;ruݖ9**'v_Dx[=!KGL>7FhJUW%*+h1y6+]}0/ignԉIn8ϯ1̺r{}udFP^X^yzpqq?ÈX8qp +f\n8\xԉ.[x_h^pƨŦY.Eba`sbK/-ztUTQh\0rK%ٮS65c}'-u$ԇ/-:@0rB,VL#I\&ji;E`~ʭ)+8&sh򊳜~I]VѲvQ wǘzZnF^ Wr0jc8kĚGw7qeoQ$7cfezjH!=لn6ٍ!ɦMɆ$K M5`p$Y\̼V d=ѽwf=sy4 Z]~BG j,‘$YL1|h L0x :P`NgO#W"ir1ˤ!H?o@@RTKSPI;d T"fyMȰOŲK׫ѨI;f@0Ejc yK"R9xpꀟX?/]o[j4' Iygv2z8U ))eo)hڿlQghf}t-g5 hRz,6Uɶ0Y r¶pECK_ :x/5 ESOC*5鏞^vp뗊{Qk32,eePNMnrfhwtdsOO!NQS 2%|y$ۙ7q2Ay5DqhƷ>?P(CN(HF1=VRA˫' -Ox9m*Y~׏fwTD!xq+E⁽'1 11OQ&|pC=kÜ=5g]̥f/aAAӍQH$I8Ot>q+p1^!u,'sUnĞ|AYL5@AC3m `.7?jAJ^N QeLH/^8rs:}|8E93-+xs0i6,K׺l!YKG7[PaNWe ^.eL!d8u&La6;BFXÙ̶QHz/m 霫 YoN6xU_ C 1ڭ9Ae˝x+n=J`2V8sH-6 gd`u! 塱h3@lh61,)DlmBbĭ0]o\PF~}F#ߴig %e_oƺ32鮨 墨 _)z^{h:lm}=v9u7hÎג.t%u} b_biG,YPSptxZr#&靫}y<0PX*v˹u6>ߡ_CS#Qg$ܣ-%%Y*_QUm@vvq3ڬW<Ɵjo0iKc HZߤYlAlqjG쎰1ֹ8dV*/oS^Tq R{&OjPXp3INJ2c7ʿGX )_Rȧ-[J_5?i_f.K(rK wv&9^:?]7iwjrYz:#k9*\]0O~V]@`e0h$8]$$ z.x'Q7@( ^Oz|tFUN+v>>~-2M}Npt=u@peät~S+A;M+T7]<lV3zA`.7eDu8kgLsI]ikt,H~}L| W48 IqN"Q7(vr8ֈPÄH}=Te~Fwi2V-Z ʤR ɤ2-`.w͋}sIM6r s2il׵F̯{bzS` _4 ʴU. ")/x)b/FS_ܺVK}Nhf_usVoO[݌;ͩ);<_Ң~?MV>1Pa?*{E.F^YEIOg}ě~4<n&:]kFMuPTDYr3@c|uZ >ϨKT۷e0=FM!J lQ[|nvBuj4͛(ڑCS:ŘGB}+^jd5u>9\r;+~tgrW#f>yV1"xBw\_DT*kOTJq`ExGQ/>6@:6_~k5ŮN&!q7-$a3q~sѫosrfkt D"7Д \n`ItPDP$I#3 yw&I^5ri$Z=^=kzQ $ & ~~_r Hٝ:Z/[UܠK7Ob `^ ~-7GM[N-Ayy\4gbOyůCc]]qR 8AXEuyXǍ k6TP,]0 Pw6nc!Ndˏ5-[&/KWb:pm_mgΌٷv1` -9cj؂i攈gmӃyNXD t0jyu^Nѥ.z.? $p4kMCbM\^@d @u:hU~qFVHqqFECSsI:PMTRṠkgI܈-hɲ]3R2.oۓ_6~z7m ?6ͮ䘝ey|T4/-`v8MMv%kf w ԯx]?}!}2x#8p)+IOddt 6.#`˖On}Ol[*%D(kwo.6Eipjw_woe-gw1gтO^y싋F ;j:3&:iZv@ƮORGS锗 o]TIŅ0ۼyBJ> ϶{V^Y0iGMa؇-(Â]6.}FljG^oϹYZ^j6Vya2Hq:bK^%=_s#ug~pN .aܚQ$47IA(n쳤s8i;+mz~}dwwS! P9{4۩Lq ~Q}%2ZawTqgPm@ʵcwĥL}ζR)|ɷ) !lli3vYPr*+2L@Vʞ~4G&SS4!fta;6׾:yYr1TD"As_ #?fǗ'ػ&vŵ/g9>y2D;n  )K#mj>J T=%"BDM1>! 2j~\bo@~ nʱZ-O?QXϧt#׭A7]a{s51Vqel]PF,kgS?뾁=4>_L%nr͞1͙W6hz{_s74SiӦ5Jyİk<7x[R"W|.;mQeXE8;TD,Z0 632gПUk1SU3!VevgL,\WO/S{HƏP8>j#%bYxc =#.ϳN޶'10YBhI[zM7SG,|}tND7̟}8>-̩9,hmG`U>Y4䨸j.X`#(ʰXŵjacO,x&?K*pD^6dZCtS&J/^ss 2'Tg݃8nNwyi1޾޾%+<@l!]3}34cD5Ԣ~:zp I&d |~b:ABKJ.W=W=z o"tWP6Q(>sYT_ߑ1 %M1+wioӚ/xuRr:D$72ڶURjáu~oxΰ7*&Pz<~*{f̹8|#GUg*)QR.Z.Jc8SDN:s*уqWUgJ/MMOuPqM顎D/nӇ#>p{`cE ˢYǞVe겮٦9ISUeot\:>#-Rh 쁡X F!~~^'lْV U'|.s`T9*̼ghpk׭p.ֆtI/F6768lO}ɏ3^0kt$ӻ{''-׵oޜG1SA ER}Mp hGODސ\u n ᶜN'uCRMe`KΟַxC=_wM$7(v\"N o ^\ݚWz }|{"ﰥsiנL=QBgV0y+rP0[|LD%sm߮ ;gfw ΐ`öD ZcUZM tBZW#1TaE|>9I'9:b {:UT#$`:b& 4KsA8~D+! }x s]̾QtK,l{x6z{b:fw W#R^*)3 S'#WwB#iLbb.8 R$/AVx HX61aE%CN K'8'YMei4&Kj]"arFTee̙s8֭Zz_ &J l~└d-4=.}q~11=U1&iHl1&Q1xqӶq)Lmsbj5gF .luO6ƦTE~n[b2TxP;V&. ;wB_j,((U(8`SXV5<ڣU~jV-?)FOZEQd- f!!N؜EWc\-Vm |XL:`CqI$VWf VO"Hou a{O._q$Z! ^6##0[nY3LGSKII&аZ =ꜣ$1TT+  0i$$iqgv=>: shg 20n[Ռ|Hu{Ƹp"=qf/OUI ,MBԛ7/eP@uړ16;G4g2ͪ/[Zۓk츒\T[K@"CaT,}{vYV`y !]t!ӦM`p^KǺ׮曣VUU8b R)ϿO>Ϸ-VmV)C=ԣDZZ%RZZVO>~|\YYBquÆ7Ǻ_ ˃>{V}M29P}ip񘮦߾wM0L]EϻxC5S`y;6&P]+7- 5Tжx^3+4tqR:.JFFoJ`#nڲaq}HQp]z! @bXܐVPf:'>J`@Rp9x/6 5L1 8X ݔHhےf5yfW̊9:}JH4 sL|F&S[,: N7QɈVuɣՃU(_U0wTYS*eJYj^pugwӺ&{ʛ:.?6BԲܼ]ѿ,6|4T4)y*bSwbDdh3FϹx7@#9%˿{EA}} Q&S嚍 5=r׋z0g6PM;-HغuZ-gnF#KcUm5ss&Or̳~ӛFzҀ_=F7V&y ADZ΁+b@b R&Cizϣׂ _LcpY<*oGڪۚmzPE4oݮ xnm͟~ӫMh2v_v7jF}Dqr&{x{:pUf-| Dm"i'AN6d'YLE$g:.58/ M%igZsM㒋LS&kƊ0G)AHKTq̮e Ï9 f 1w̟WYohŇ r9;Y)K0 8ZkvV4i!]zx'CxhQI_Botp0g_;(cwyǜ.D<\$b[,4}]/ɲ=-Mw~3s{jb$rOBCgT3E:O%-k(_ArT^4)O~_OiM×کBUE`0X}mzf<׭۾nvJI5j VqU;W7n} \ _eFƆ$tk1N,b 6sٮ\S5ˡ;v?Bz" \EE!6,0,//ML&@iiΦuv1T/訤R)c+[߇bٲqTQETr|~) **rqGT.х88`^8IUzHv^X0K{׾^de]sxE^? if\}T(t[dM6K.gIA֠O(`bAgifͦkoyQ.zV{ͶƗ\^׷5}r^+D Iy[;)ɱAM;gu|]1w #-6tr:7"륏' u (3H?΃&K A - ^iՇ!I_n[ DSG"B@ fp_\]$I&N*Y3t\թ}L9:"Xq~Ba, |k\.ği>әBа)wQ6%OsagT"IAk;IÆP<Ïcn%J.nh G]3;1L@.ll VOS@ GMg/i:0@( ]WSȲJP5#rZ@"p@z[<aJ9րeG75ӕ--geQePwA*Д81nkeMvX&(B)l^7ڜk^Jql|>aB,6# .(;bphYPhȾ1jT1]BIvߟ RNp\aK|/# #CV&*9o.)y:POXEAKyKuݾd6li;.%\AY=>&V5M{k6SUZn(I>Kݻu>ܨIV]85q_>ܬTfdOWzeUMW*U9Eu"uOۘLNDF {,P똢ּ%/l"[>4-kQt ~@), W:f6^o$6$f<^k]DfOG =uk^6`AH2(~ >0ew ș7zFO= ;y~@9fbApko{jQCs@A< kn~-+ 8.lhMkF=B;A΍6*Lj3m#u2 ` ;y~45Ot4 ЌS!!`郔H$#{dċL >Wbǵ#^k 9=va$[t'4h:n2aq~IAe8f8͑~HP}S#$$ (ŗ# 7jjCjgbmЀs֢NFrezm$ sKH:b(-t\j]k;TQ>F#ji^C-N3;0èJ!zj3/.M(J1@A^]Dϵ("(iĜݭjىX !7xRcd (G1EU =by-qiv#;vB7B8עk8ErݨmP6n,kp/,AJ.׬[>p>N1 6攕kRJ[]SGq`墅_jWsJ,o;̝͓3G$/ӡݸ*.NxZQ}q+M}1=S[.gԃo7@^ѽV@mk, M @ rIXo`o]qۢ{圐*䮘O|;%)?ӠKYm eF( vORМ ^+b;]|VsTxq@gD?e⇃wt - !/ (C-NJ?NSkzS' #A('rӳ2CryQ84sә's1Q ;{aP ),|b=2)?:k +UF1Ε?Ps4DlxM$ U˖-ӭW*U{(22,^~{~i܇x>V* 4ջ )GP [?<";:djG{Z<EΎIJӽ:9mVp\<(**>}xLs;?헲vցUR:%启 ~Ep?8Krp-Ta39RXR)20#~o=9z_Li.c?y|yY jaN~ S,ȞX o=R{[VQ\]R,)Qgo޼)+K5ZeٳS %DhyU{iر _lT{{zH1Lз5әFۑ'1 M?%Om+A;i?徉'a\v mvMa U7zG$ŭ_ pob-s(|ܼU*8bsUSCϬ̼F q3([y)wM~/f _C |zN@-Xp`ǯ :ttҏ3g9]|լRzÆ-(T*JW35S6]_~00c {?$´Ga&3yO3ie)Jܼ(W]\\R))-[FXRURB1-mԻRijm煘H83׻u=qY@B?,'$p}{I`͌k= ,Ci 7.wGvF}:{(:/i by|GLy>J1 ;L(JG>|p^Éws_-,)y(! 7n?"WQ3ȇŁ$I6+'#IF@Ծ-+u^7/H .<B@IБ/8 TtE~tˊܘ݇leƥ$ґ H&7O'17/[$?z6(wg) j tİ(UN8~[ްf\z'!"8|~Ҹ3*'fE^Dz&pFQu̘q<4-[Yx槒лЭ6p^^,lq3;@AťW]'O̧ooΠY366}R޽߿FQU592QMUddX_$q. mYoM޸-^'Rxu)&;,34ɵ-+F8z?]j=5"z'.QHs AeTQjcڽ{6SUovF)J?j,KJRg}99[JJZO‚ ߷ve3o0RS-Ƕ|)㘧K!GPȤ &cdo?f.͚U{)ЎEaN_<ؕ+|Sͩ͛.#5/osLxt3ReM6Sj[gԃ ISࣾ]v;(-t4pVKCB+3JT'j'VI2THߚd^žC:OT5nфiSz^ߑ/b|n~!z|ņE`&xh1nwXe%G`|NȾi^ѼFr1(Z |_0;ᰙSmsP>/1n ideO`-ɲߞM{ 0gZN;h b!LD ,4Ӆ!$$I\"@bNTL}ܦyZ'Le_&dIJo4ǵ+M;r'"h4 H`tAj㻷V''"8>q^{/8xq"6 j2:߲t%^GȺ@O@A"PPs,V|QZ~(ZrG9ޡEmΞz͒hfUם=CD˜~"d@bn7+.?Qh?rdG\2H攈)  Iq 3B^P1 K :I"CTaԯry/Sw:׍b -LV1R\LdwyK/ƝP*U5産vϗgnN2 X!$OfyAg<ޥ('$hVEtPAe\?+^ ߝ~pZJ۶xp_J.$'^ S(tktYY*) ;4؆#< _ZPt-xڻ67O}=9O@{*VwHH~U)Hl !JjUсRYOrTҎOaKЊ잿;cƑ11=UAܳO&ڞ-y}T5fM |5|:B>M8^W6xRCG &V5N n{OCR׋X47"T @|յS?I˫~"V H 1/sncRw"Ӓ;qwfc!/xZMCaFp@HR\نA _1qQ 60ԡ ll54Q6&ͦH6&ѴMQ4(X}hCM8d?0y{;{y|8ZZ-PB!7 ^xa7%%|598nOBplf8qa#g㑗a{b2}#HKQt (Ϊ=6F/ .%eES@ % lY63<ֳW=]ҽ?D*vRيŢ[e)gD"qy #[?%oZĕfySϻ ] x崅N6NCnktyjW}Ѩ(+{)3;'Nq_ &ywl2@Q/(k9+myA!ge6T]uPw}qe>܂ (V-48E^dްB͖6֝m^_dPII *jV&9[ԛvI0eQzgGpDZ@IBԳ߫.{(8-D$uv= \M'k^'2p*&;|sKcm*{myhu:8405oگ)ωJw ?yeB]ЇFs’z3A-Զf?7 )b􌼀Q)} +_ [oG" "6 qs{lIκC\ȉ Eon#YDPRV.DH2kr| VYv#h i11b+`pNQ|Ewֱʩq,񱁦UA?s> gG(sh6ל3;ϟZsjQ+/"bg(\TTr_cL(FTRq薁΢?|öyƜw.dž uNju7gZ1kJKijϊfԽ%\}xS#&^]Wr%Br=]νzsa>~j>j@US>fO˪|ϣʙ։ :Ѿp3S֭ лx]ϝi/ijAԂQUjvGWEٮ' Ѫz[𵍀~3"VtvJsx~acQUud"MJ`urF dq0ٱ.vXZCXPP8IF E awWF; w)|+OZ `T@x}KqfOvW7IL 6Ȱ 赥̠j'z Jo}o}9Ѧ3 ndU0R\|SGnMd`Amk<ג d]13.UK0P;EusnhE-#>e/B̟8D%% ŀUlPD;Uj^(>T q7x;zb1glXWoYn w15'p!Z9 V˄C*|MeŢ+坭=QaШgXQmTe2^KIEg|xʍ32P7Xц[:b(==۞0Lxfw/P>pˤX縩 Y6!Ӥz_/oW]G]9ÿ?uPzG@Ή[<;ڸ@RRڵ{ :jĽʹM'&$#ɬ*Y7JE/ j4\7 @oP_)VvMl~s7W`A#DzxXwŤw'|W7Y0y?n4f_ZѪ[_&ǁ4[Ze:m@+!u騬 0f(8d֔V|3/)Iy>%Hv_T}{́zzk}%X$h='Cvx` R<`fNg5'F}߼OO=pqw)tVzzE#v>57w;]^ȶ6Ajj'N{k+΅mGEV|NIOw^i/fBccz !0IyAmɛ>FEG>`4olpkkCW=xyয়MsFiY6%BG{,qu>je3I2.aАsуBup+j1 :kB4QI}no!@')j FFjK*Fp@F4h'sZ}O_e×wSDѫ[09:UTs vN^3>_{5-4*ԟvǗ E;ё З: @Olb1(! nF;۽RÈeŕ aʟ+j{y]w.$lR&0~6.Y7gEu9K~bNK9P/.v ߯aR=Ξ&#oFPl@Qu9qŌ!mÔh6ƅvTA~j])+33^Nњ[> BV:xHTЇh4III?;5 ¯ǟrrbrscl։OXJP`#LlXFڬy3omߞ{)eef1PQMT}㧕5KCgr;b=<&*$ȿ|1N Vv[ݻJѷOok/5v.]rg_DZ:Bc 9YiKaI5Ax]čZ¼YTR}щ,'ܐ?:{tzaYtق m}hz'4@F Ns[8qX>{a0Ӯ%Wd& aPŽ% \K-LnnrjևfM/̜5-i >aT{8P=4ɐ͙4Q!dvqR.QV IDAT>Zw:5)TU=SR]4%@9@g= `ehTND>ڐG~ɀb 8FUk׹4YyKm{a ĨC'GAky9@BWtqoW[?4ZcNĆ.wf{K.^c+c}aCnƖp'_QE ܮSyAё~N(~t3hI,4߃ӽ/*,O3ɾ{O4cbrv 5EZ^|e3p%,Up[wbRu _Tק\on1=?_aYC9Z\%]RGA)ߧzU/uvެ<3t|QL傔4J eBX+Hl4ju]{؝kXViroRk3XZ ̣N;55K(d}NJgJrۈo-Ҝ)&;hƓ+6ITu[omBf-!),`OH( BeeZa C ':`:Y7uyөn,]}JƻR9qig}<dTMOpgQ!dҩ+ptAuXVßd<N'AydP3@!@j!' Y7sgz0ԅsxyU&RC΅ŀ$B0zF{;z_ū-V F"l:0p4 %*tv{Wh?f_ۆ}pI6{k,li;s TFpbDGyDlMBF+k^$YpP@vQXtc5ba{GO+;g;{<"M+|_d_{Ϋ5Qd0g9U8X"L9/!K&yٝPOY 5ib1g7\bF< 6@$1A3fД*"8ƣYqȥ t<3mM5 0<::԰ Zpr&"ј\b^g;YMTRh5RR-l͝uEޫGx~?+Avb@wă/0R^E o+~' \/4l ӻ-4wuvjW!n=Lаe 7w{? lDq ExMKHxK[[?47KxW898n(嬏ޱ +``6fu#u]uS`ngyeGCH${rXG/i; qV s%H32 !~~ޒ`=I"88O~Jc K?,_?ޜrC 2Ov ]fP?}pڭ_~|v-Xy\$,}罨v &]7tFGvsI5H;9f R\6+ Y=ӘJ=m3*E3B{]X4pr|wb"wvQqNoy諬tR%8׼jGGD")9+%Jkm s/OCÖu"欧^eaǚzvGWż 5(:юG 8N,t=TBoNF{q Շ>StS58p@2FB@WfY>ߜ8ɖ-T|J̶<:x9H"mIM=@< r=$lDL?Gl=PG({K[=l\*j3.=2ͬy i ݋9J7}'v$h4Z{O7fP_#56{v:4 +ٽ-YF3smzϰ?O a\>_.H"-qG\ 2"D_K{3#X"HT sBJ|°o^]5b+ZEDk`jPը ̀2bqAP}'B-ɴ 6X)0;]6[[۶ܧĆbڴ94δMe1N/cSN\wIsUrXvJ'e3ug~ܱڱЙR1Bʢ8op6{2%-R-M^ܻdzA1N&xsYMR+7z.#z>44 W-f(\myB\1u:Jpm\! # f&G-tZG*ֻD9q!"Os?5FEu%ܛ7bl{3?Q:]}\Ѣ/V( ?##LMCk]d>_2o#u(GkW¨< ҬY͒T`e#R"QH'.)IP2p>oQ+g#z*Y$;vK=)L3&peˤA)X }n՟.ZC \ P V-[&%}GRKJbQ#VX*,D֧P&.WJՄ qQQ/bZӞ}DGoMH4Uyyfxivj<0wYy'y/6WX/ wo64=juT01иI\;@Qۼy5-1o5n3KFHȂ7X8L2]rSI#j2j:iqM%.BdcFF&]3c:}5/|վ+'d]Tfs&5 20--}#$ϣYjϞ—X'wzby}̿^|˰SY~W?-&Fs֘\kSi)w%im|~LLLd>E*>QvkDH k0)TͶ^*>-pTFW6H>) 3<@ɦUIN&+:a)EH}Ro>g ҩS۷E;1q($d\w\'ڮ7!L+h͒Ϫf‰Nz~Ņnw [U6mp`mg!lxr] A9,q'm~w/Dt\1`-`$/@y@Zv2 D1P]IhU=r!#͵ @ٴ@s^A~њ?]­g~|Sʳ;K}KU^}3 @s#TP¡/eʺI:=F"[IDBQgb++<}6]pdw'zLx5K~UW y5LP3#@z:;L;rYPq u4D˘>vӓ FlF,`f04q؊ tN+~# f:\JNDD*j pdzVEbqW:su>n6^tS#kfl0Ϲ+n*yX_,sREWLNʩwE>y˅2YШo({vy,[ yRÇsg}0ؤ:DBYi]I9-nnDھݽEHDam Ϥp9TEyX*ǢzCnFp/s&ݓ-_#t4@:43V-φ@$ox@,ݰB*X@:wQO[QNn,3ϗJ{GZ!( bc娞aODBQk_&sn sk;x;LcwgoŢ31O^@-_w=>NyC?:UPbAh[X?t<24--F pX,rt^8Xh,(|wUƻ;LFEYC9.$6N0|ޓY W0B9,76hb&1[L H_ fk>|pg[׏LBd]Ih_0OGL\wC cHW-YFM{1&6+xұ:rYήjogG-4j[<ݒ;+s :{$mp0Z^Zs^n& F{;h q/X^-<ĵ+%9VpںMlv眙K.tPx6'T&X> :̠8UGu9.ZtWRASQ:DZw7͒uwΞ{~rPA̙Ys䚘LX+Ϡ7Mf6<4qF0d-/*aɻ i =}kZTܹ Q41VM{wx|?3_S)DH$")' l9J {"|5==t@o0>`+Ջ_ׅha#&W!RJ6Rn/(3*\Y~Y.z`fdwޔU f-t>׺׃$&J`$OeHUvt,*+HO*MZ픔̱Z f?OQpvaDVK@hMB.9v{nEJe7+Q1e3eKgPyݛH\n7? +8e!;a7 23SSRB\j5l6XVm9Fclu5%X U^(kb r//eɫ)Էx?+ɞqj m+kΎTƶ"e;E(+N[]럆Nd@NO1zB.j |I۬}U v!to|Zpo}Op)O63v%(~Pb"8"v>_T{,:mk2@Q E b 蚒~hӹddaT =ByQ`#0~dd4X,Q # :Io&Z׵BĂE=ዚܓfMBy  7w50&&pZ]72RUU33yo'Ya=QjTu ww-;:ѩS9~h]DɴPuM,J>Ƿ⾬tc@ɤPŕd2)|D۠Z2xkm`VeUD^v`͎`+-bVweuvo|u~ƍREL^F6E'N,mmk#(Oft$ֺtKJiiQQ֓軚:WB';I.Y,F!Fk7J.jQQQDDe]>8aBnII;xۡ⿶TmP\'q;}cQLJ "A^~{|%n5ؽ<}_ʆfM7G3'],>$vǎ%%II9:9inEEU0&M +emMfF[v%ꍑP_r7z:4jxis\앏779].l<˻ɘ?pWiA-MyLIb PK9 ã YA^AڰRK2{h  \;ԂYV& '_0|k{'Ys]O |c4jXi#una-Yn_Utu&71G_Y:4\"܏~Ыk^oN|2cz xEۉ rK HTRWUxho͊JNT65UaJʆ7(:v||h!E\^ZTZ7-=U;m봰O~2"RS~1I#OwHe IDAT(:#DD[oO7+8uFELFX3UpfS .\YӉ.V]3Ybd02fD͈$/D&ܒħK}IsϲvQXkW7@,v,2N7M5>5$Tۉ丳5݅ϓ)N{($P11Ac ~U#x%RIL!@FFZZZF Ib+?%4Su6ܭ"R>1G HĤ^Lyѭ(@,dݍG/{h%̀-%>ٗ ƝS+N/RwbY{"}X \{ҪE selLN i spd461!3I dz)CC8BByw͌uU,{Sas'Mvb!-YۯZM\A}knvFCDfUMK`ҿ+9?qHw˶]ZLysG`OrzTn8$~uTRQeШ/L +î9u2oҦJϚ{ص59L0*YQoW}֓W]CTN!9Qx@Ukp5qS, zU3foÿYKD:U"kz֏E-w)CèшX0UAUk|_nZTx "bXh4lE£Ia>Wd4GN&ѥ j C#McT9lvBU#TSc]AMqH,I 8`Tˣk7X JC\Ub3Hz͢ԡ:җO}ƞ(S,[*54Wܜ}.O뢣5-[U1Be׷h&\X\ cb4JG4e;=$2KUbk@lDŽS0n\ݍY:d0'X@prƍ꙾~F'y+SH$bqܖ-Gѣ[mőC?~MJ$96<<'>R5|8991DS=DLLL8ROZZQ+nY(ɑbEOT'K( W1auwP, u/mA ČAwיNts#0 "ؚ&Ct_ !ڛ-5I_nv3\Λo=G7Heeƅ $u6ƍ/h~^juŤp{Ց+[Nsnvw OW:gN\yi/LoJ^0T2qmD4\o@} pqb1MNj0ZHC_ K2L&fet5ZtPkRF&5nX541^ޮ>LXWj$Kn6(TǭQŢWW 9e}Q4h,5<ag~~Q鴽W[dj /ȼڛtXl懨}MowU;LRP)55kϞuz:qk}Zb.£mHQiwhCDsxm8*kSPD$*lkde^ne9ÚϚ2ne?)s3;X,xgDj$@\Lj=:MT;*O;y[ A(j}ʌZW7UUIci LEE486{?qDOH(),hHw;M]XgX ۚmٷZ<&A'uC}5BO'@Um_l#6b"/5  * $s: tsyKMw ) 8rF)36B(d!-߱q[7F%x2'p:f{E6.[̲8:z_R׀M-L U}VR FF#@0r=4V^hu#d;?ÐS(:(i4c=o؃{A]#jq Մvv".L.\J-<ƚHj-qe"ke-BZ;ށ>;VYXYW: rk@m6 hK޺ ӄ|1\[p'r#ou;"Q!QKmY@S12 !Uʨ7CUVrU#G󚗧ȡ~bw $mAG`X猩3Lac\^\8{/8R1ܣAQp$-DL,**͗gAWk,l]R |N:Չojә %Bw`~c|]3{Ūw㻕Uk1-E7لbQ FEtռh~"6Na0{ZMaĄs/sL>tAFiP[ich)?/K'E0F'4Dka1efC_?ۏB+8(?6oZcВy W Q,WG]3{?}Sc+A.MU}}ߪ^'bw|6Pmm`eD^U}|АwDS>4$:+?:ѪME=enW IDATGv͵.ׇ7Itbau'&85ks}X|Lz9bq'8rDE-l`C9mli^SK]>Cڡ8Өsc/q8}k7BhڡHֶլ;+8E,yOW/C9H yy4/(^LB LZ]6XK"̳tE Q/^R)srg >o{vZBGk{o$[& N(8AjҖ9?f Fl֜ Ot[]\^Pr!QQtθA7Z<[7=H>mRdu63p Æ Ɠ@N q b41B}Q8<}.:RjE.qRs#΁|za˺/*<K"r_O5-M!eo˳>c]/w&[0[^OwܬeYED($Tݐى@F(;i՛#4r}I fXqPV0`=o6:w08ONT V?oV/!@Gd􌹡-GX,:RDHU:QǪZ:EL tvjk/.>_?xwW  /EV\}z<M={NL" O7y=dA2F|_";RҔG_ޡM4T#khxa4%e'ɰIO@/(H$>"8s C$sN m _;ٰ9rtШ ELSn |㜖rW${h4))}5v{ H͌0Hv9U9w)^zڧsZ@ 22Jg}w<ŘU*l`q QW>f/?.遂Y\9#7@xťCT`+h Z &OwvzZ=%z͢I {\UTAz5TԞ2۳gJGfQo#N24 f2IJ 59>$Ɲt6: 6+%CBƩKK+"v"bT cnP}=R ]@rKoK[wwa+Sٽ+{[XUT}?[14;q.QO~Z6*P0Nph ! L\W&NO"eW·?v PR檢 (eϖf#lf6 5T׈"&c4O*߮W(ޱc{ɱe_]G =b8A1qcAΰӆْq3.WhQ. _{)MV铹ok,q{t{]M֗0{*_z'~; y+4>yNԦ6չ30q̋9'W,&tjj02铝ZdR*$N\_~-DdUVb)fv x"5_>阾~&M$0i[eLP0ls( (.SRNqV!j)2Y/YieOc=T1= Vkc` ζ_zѢm2U]?~7$db];RA*;HTX,qE@s1;ҋiiRGzu*O:kCI;NKf ^2x|m!1&w@0 nY޹ \N,Nܴz|s.f 3HnV{ ~‡W Am4C:;,7&۟<d yd#-66O DbmzP9ZίעC'z#̌ CCU-Qx+|fM.k7'L:{   '&W}A_Kӿܿ,it11lُ'q&*':[{Q=­5cFgXC7 S?v~O=u=AϬ\_|bV,k]}Q[\ߜ}]SM׋5œ )oyff$wvh(L6 5[pЊk7"6d`V+ Uӯ?_E26jU$8i987lw迃Ѩ^L7TSZج杌 kbި<<>U%nwx\( ,j~]`˒N'xN^ A{\ǜ#{jĜ S;H,i_kUхKhz'~kx蚞8{J;\pq+bv[ ő1F;:#C]+Pi]sܱ"WܒJ]Bs}~sD!:'H  ϟ~ތL2$ SfDC2?g;5߉"Rd=籎|I$KU-'2uw{b'6).NdUkQ/WNPO?|p扣[C>^y49 f-IϤڢ+>m45`( b~:h1Pcz pe;6CpqYⴶ`#9zNO; ,3D,6^wu<=ATju*t0sFֵ>ʺW6<3>s86jW;fMG(@ pW>'(prǝZu wfG6 9!>AAPd~TΤ.gW!!0J 4Z铽 :rev_e7Rf_za?iG1f@ {̥+%&iQ>׷Ęynv($bjLwIQcS\g$Jll~zzƁ5L]lTw޾=GoYA* 23ܕn Kj 3^>"" J OL?I GdF81sلifVw~xGhY2a-njbGux%,}?uO .M u ֭˺'ʼn^^ Yl{ T C5B)!i?ʪ֍_Ö(&>0-R*ܹsp2 ǫ}E;l֭y_f˃IIX^Z"Ҝѯ+/Y{Sg]x;uVyA2338iSș^'@$n[9^Fu)kJICE+==#1QšlޘU11].!ە9ovbNl놲z3Kg}.?qբ^|p5K_q/o)7"oϑ\97sOm =##a8ˡݧ<G:@Q RpW@pN`!BǮZYL+ M8& L ~TwU?/?6%@]%K&fIrͤmuKKm#UGZo;ne0b2߸tPѯLs!Iw7Fvg7n989KWER%ҕ D2åm EL$9;-* O d2N%F`٩5yNgbway6*s Fg'=Iӫ>:Jfu4jwDfo?QaڝZGFKqɤ6C5xI( Uײn!I# UR.i/ <"8/u|9 333H׊^LK&xn֍Fmz#a2xgz_ [~$ᜒFRk4蒣FWz1>Ѭ $0 E"Q#a߼Y&)D]02WvYHZpI~D,N&'ߡ۷oPHliڗ>&z0H,J4,}J]?KdϛSۜyeץN>d<\ve?"pSŸ ~qd[|ڳu؋yP-J"aދK& ىp!!EW2 KV_W)58iub2SZ̈́Eo)_^rʾ[?кdckB׺fL]jݻ̶VnDp۶ D,ѩO<fq*amb!,yOdvG7b1~{Jˢ4> s^|֮n¬Vs-)sM&Byj{xoc+S;HN*uq7n'x_ASkNJf46: X@!q<SJ#@0yP5T33$<lz緲ȖB=6NHzV_FMxyݑa#!1۪wo A};)-k/ffWTqVҦG F9ī?I,-&dD[w/^}J/ֲݕys7}_5.,'uzʛ> !ońoMc/8}[?I5cˏY+?C>դ(==c:v7ͦ]|W'vX1ϗ [@@z-*pm$-A`́~sɿn &Lf`ajMs:QzwW!eѻq 6v6*0D#;T@.˸=thvTOb^umZ ^Һh}G'01Ypbtz9#qU01c}\B eĝޅEtfDV PwBR ^aB.]7f՜oǧ 8k#9iՂ4!MA,LPeyb\Wu1.] Zh=δI?Xv_➿i)d͝*7ܪ˷)P$?V,JJ*5?Yp3a`{[{)/O ( @m94V8ӹyh iH>"XCNEa J>bGPYOW#(DbilçVX(zOe2mN3֯ڻ7 ~˅))ۅ^y'=^MqX~-6nx: |a>"33}ϥR;wH$*$.{w_ VdںCW9&"hqhƕo[]7om^={YVs']˿+zCn'[1^ @IKo2]*T5ݖTE Fb3͈z`6 Liu\?>qhu +R xVgT lcg`/J&ߞk~*hWhl8~Fܴj )NZJfiTńGw!Z&iks|jپìW#ʧNCGV-EؾCV;p!١7l#fWDzCбGNK Ȑ׵[V{a6jpnBѻR: fOT"&W+u B!LyS~%WܚPy)lB\ I+USM:oOpc\V~3qM@.KN(&FyQ IDATk8O41ՏGĎ2rܑir܈*;zt^xы[×uIYY2y"EWk2yYY=skL(o Vt*+XOvK,͆je՗df NgOcƢ˘膐1XuCX80XyqΡ{2$Ş5WC.uL(9oI+ DٻTpd/gg>߼y6Hl57tyNI%Έ ⴗ4 I-фހyuw7O~/,J(KgbQP0WwSGd-L(Z;M6ġ!%.V\itkFXƒÍ7N&[hC;!S.-PAo«:hzUrU%)eH6;WC ?m hSSđ7ŧI =w9I.#ᨵL- ?wB;5-*dž+aQj8w6/yY҈Z폎Wב-y޸mh$l@*2OIrN&@?:¥UW]a!~SS; N?|fq=gNOpj T:tqi bIq% Hw2 bb*D"q[HTxŒ̔RKFNG$$$>w Ti8$@pp-(:|Iff:w(. wŊnE˖;šFIk_;pm{ <?6K*MXSGO@bd}|O~x`fYJeRIbo<=s{"dk<<|I~GC ?S&>u"66Oݗu]ܹ`dY}t;T8;ΝINNz]XiAO9)pC=iЭBy̋7)~SY+{搌#zþ3q;Z'4yNW/Aξr& &;lw3U#rl"8ě.5 N,QYkRC7*~3пQPӍ1nIgvbImo 9"́|qPGQo7㩓q3 QLN1_(fˀPqowt@}Gyۦ@VZƼ ?],ְ'%c5Q;qǫwks}8S(u|1EdDvi"z>i04( 4s^y9ӏ7="˟YM2!AP+h?Spjw?٭ CA{O#PdtM>aHR;Wl\}cynRAtޞ+'T)f#PT6fSݪ'(~5`VA|s\ߚ|13ꕕ5;mM5}g9kBZw/#+ViR?wXׯ^?+TsA t^^RG(KK*feN駷)I *cpb&Wfjٔ{-(*R$BSW}H1pw',Apӱ> gW|smvEMr= ̀ MSt 1iwN T3?`q J*ŝd9b8^@+5VZ9`ȧƹID!q]s=k _|T9xȡk1kcv+t/;AfG4:LX6FH Htx]e7 d#bvΘYtۭ ovM^Fg"~BRA?? egpB3hoRך5;*K7Vz;|?wj@$4FFLPk_?=ՁV0<3n*ܴO.a=cx,3a`'fa^Ɋ VUYnN#xÓdrke/JLx͗d!e ae# ;Koߣ\ǘ'!$rU[摵kdhN,N\~U,l er1㓣n>w}(ɜ׸nZ[pxdy–|&~If },XD-bk_b:p!@|Jsϸ +˭#j7{꫉ Oӽbq^QT8~'A WW}?x<:R#V&%T5Ƽ,%X* .t-7[6&-u(/.w fmjCPRpZJ\/C.6U[~.JCx8 bP]p~Da9A)^1,,jIVS8 .\=7RLq5*OB4HVs 6,#ww̞{dt2:WUo dguXQ(r*@yNDjkawvɒE%be2sHa1u+61Y=;JM䘗k-Ć;K%x7,{ECIWO FbD,Bl6rQvPP!qLr8\ΗBkjMxvGWnj'{T j|u's}R;R; # ' i\TkEijώ_K~ ;7 c~pxpZ@>G đ{?jpo.ڸ 1ݷ#^3%F_Trxk>~0h,3 2Mr[F"`.XVsvPs/߉̾@?ns3!%x8xP6P]ҟu9g!(-MJbcHps1С;7ÿKJ%'$Dd*A0q}T)[x:RĉT2kuDXS*QJ') wZ*79ڥg J~ZX~lЗ6uAKƔ fA:˫Vf~pvRϯ>o8a;o:~^2F>Kdl5F_Ʒߜ03V$o{xv22~&fL3;^_VRth/O 9,L@nṭMf uʳ {j2'$.yo(5 T48$nV Ѯ-d]58Vs,ö>??kŐV\>kۃVØ[ u_4~i(oy97A E?"($=%c|]#XmwbGR#i[{TЩjK,it/.L7Ô5ƖϝЈ]4 vnlAV}?PHX|Mᩝ0qGYg(aPM‚eBro?ạ̃Rf9u^.FdJ"in{q_*zwAV@x[i%S(fGNgbp! 5md"c\ɩ%Jc~dA}ͺN^P|yaJ4 J5xkӮ_}drZ%'?jS9W|5!>l|5$_{zNrf# ?ʇ`ndEIUnmܬl1ߧNVHlaōUKA5?;PSQlխr>ctM獴~Vgc:OZy!%zY>7Ipf g)9Nl:1{ Qa:fj4sDÂ9 3=JP2BxPTmPy!Ivxoi4s+q`eVwJ6ZQj=ݨMsa@AaSspBfsu.pl | s/@VcnܦJaVjS;g,@Dlq Ր*$īm醬 WDrf- R_~}@s7kLV! !@X}Qp! Պjjjn*ZE]KP]%, $$! YhLQn?w2w̜&!_ eT޼EQt@6z6/M.5ɜ''pfz'T9귚Xl??&+mD=ֿ&|[#1#rOks#J1N&KE"?ޱc_vvW_ᔅo3(*{9A-d^p{˭ՙjs\mvff Y2w\cΐaBY, 30)9=(%/$!H~*1_]@ 嵝_P=솤x;76mI2"mMy띃}Vy>MȤϙu]`grdp?虝8w׎:zazpo=vwfb UDhh`b/ ?#P @P"ZՍz{RP*|Ғ)9:er\!A9:ƠXq2ݘ^/fDNrh3Kd?T{ƒhQƨ^yݞMm g>N&F&"ArolXbK7;''b 󳼼۷(1BVV>SF*X,>_/d2x1E"с+$t]p6z gS'?'ὗ⢁;'/<_y,3FXy;IF.E? bFVB: X;WY^}ByKO=}Y 32G>쫩шu2[4]4w9i\nMHЍf7?ά1rWii_Hw.7);GDmr9s'gZ e1 {zhc lɾIۡ}OMV!xW"TjwY[V 'Lr*^e5pNnnv}]Ҭk]goHP.rES@U''/zww!a!}Iƅ6"/Xb/XC|'-<Qo=yzqwJ C#d;(ZSTU%2F`0cw۹ᷥMG^9u2幊PdiW ߂} ٚO p.cy(!ct:d0AFmlbW  H$haf<}aEaȌ8nC+/Ž5L' IDAT^!Z L~5[R]h[恐>8DqAz{(3sjsf83_qA+e^)JJ LA ˂Y N,VEZ}ۅ/̸ z x9fE(14KF- Bl+??kkBT.oǷ+Z bQn|▐>Zqreh)I_^vv̹uw!fJm"RI/ﻚ>_B_ھ?n.+d8{  e۶1(O;$%q-@G 1Z٠I!_}f9s1L.0;8n#%o8XFGGkjR}&+, R4޻qc^iiމ? u$*RZ7_qAP9"t+ީNUm {~'s]τbMbܑܓMtֲ_ #}#oeCܗsxMiirO{ñ!ş}Zьtf2Q\llX;)=~hWfym[Cl=P͸Qօq=< -޻0cuz߂d3M3@'6dDFU6 kxܽ-&32aRt[JO鯠s=@ /Âz\-b"X"-2lKQ/=Y LԎ.T7@V'sMxgr ig\.5*w&¼{)\&>'{z 8˂<ܦg\42 4>cوj~W,Ϛw6Ե.[U^q00uf9/ݛ8̦肛#!)5fƪ%WWˌK٤3,SF(Yq5ޥ:=7gOp Y[oYYPPtݺM659N}~]J1Lr Mbk-Y,0m-WRvA^[k?¡}1<'Kd2y6Ĩd9 ͊W ˩eeWOU6tb3St<];e~i z_GVż}-u;ֽ*poUy g;zܯߠF{;v 3kBf7FZfiiޝFXVͷ nJ,zt!L̐N$'~q#xvMmR2qvuXY,̈N {4!Zhg WS/GFdMDoYQ!#)$T3s"+ZV\٨:tR_`2CNf*Բ0ݵ|o nudw^u^6rF`> `暛36ܲo מ8}3;v|b{zЇTe"Mlxcǡhݙcsv,/c2*꾨؛ %Ɩ8\maWr91C 3/lm è7^sB~/rYo\d [&?޴Wgv'o>LJ?֪ܤ^8mSVp`ץ'<|IlO?fHt9ڙL&lWR;윃l]wdEdrmjjuMMZ%WfȑM')oEvbC>Un&!͹2Ƌkngk-bc^lL䡵Dh&׈,8"Y^SfErxZZ IZ=ԍА`YdHmcaM}/=l럴83m;瓒Ͻ]h̘!t꧝')=2J1n0їJžE鰍iEIyq %kE*546V>ocG\vܔ>z:KUZ#zcI ?\]8??y`6 +9A#!7;d 2%LU35FgFsPz6ͱvM{Zj,/V9ߨ_Ժ-j?C `Xܹs?ZKs]=yh2`o "0wͭN^_l`tKBNF5Z'Gr6tTvV sHBB[n70f$L,T~rˆz4$ju4ZsW _ q qC5|memuea8:![wje~~'ԚWuAkfq؜LҒPbVtݷI;b XG9V,-Ɛ:gUI/̼,K0Qq-;;~83[ÀSTG+٣'}NVD'Fʳssrrѧ*gDNy Dŝ線o]&:9oE|p#=Tq!.6zWȍˣ7kYqrr]qWҗ6]p͡+hSON [g I&C_9f–wkM@%5́qXr$BvRen2!{zMn..Clfg1"w  fOWBkSb??ݶ@LLIvT|a$˜ԧl筺nkBt RRl7++?''u(}io @Z[$2xF2a_2а 8$ p !ϙ5v̑.kӕw|i$l+:ӛ8=$ΌpjFJ6`05>g.JccQˠ#n0 zuqZ*9$'Bf KJ2bR2H,#Ęt'W'Ui/jx'5qZ @dd=lEY1x9?['E̦#KzWG^ؑ <ݝż hTU(?! QwG˱Oy $FADnvvhk4FETIE |;?6T&;q M&K}֥ k!FGcPŶnݍT>*xŷYGXkk~ fB(pxðUShhRi(ڹ$-m@>UTT6[;yϑ^ D2Z@ v/c͝}ïUXD9Ӵ"JÜ3럓CfǀVGZhUjdTC,1]-a*vh#܂g: ~^o &f+FlIɄI ȄCNk ň7+ˆ7Ȣœ+\J mffȐ V9G Dݪ< u߾=e=緟Tb:8MWj'9O^{vXMm3?vM83Js XgmcŒP#3]*ą *mƌjDUs`c%75W醄(vlS;,Aɾa*G }bU 6*L^O}]Pl/ 5c. Ұtp4Z['>]Z]<ޚm{npi7mόUK2),]\U%--vtQ2␣PObzqD>EkE3:-h_lG==(=3rs^b/w-M{pVXLǪe~atCӢ}ر{1lXLG~XsO|s$݂$Y3I @>Ы7@&5Z'\<-KbVT9Io7pxy FqѽЗ4;^Fdŏ^5؎O~xt{(kd_r,[ws=x2*NƠ'UwI}#i`޷It쫯 ͭ=// /9g.h__g q7v2BYfKI6°_l ꂂF2ƙ#!)BzH J{Gjcd2l\uƫ[^n_n腼`FF^%Wtiк8Zܑ3p*5ʼnz_N6#l4xt; B`S083L{S )ޖ"#PL be IDAT #qftX:>~&*DwՎ_4troZl0y3m=z7=mEC߶q@AT-Emkg ]ݙ]| 6OnJ49jH +- FbE^,$DXA3+lI]Oc9d]MM*fAI7PE}X XW=l2B_\W=~DI:JUyعY薜dKU&Ϫqa>C>pǥMRS8>=&ܘѢMSh3N1H;K Jr-QR w"zߛ,_v\ZXP*Qq>5Ν쯿ޑ^. 7:8Q{j*vkpDUb0E$nb咒: qJBSb,`ֺ3YYy|>K* DpL&ӲLDH*% OG0()J>oe_q&̯4ܨ4q I-)7o7e8j.-8rdm_~ jޡޑщמ M-|-_| nӡslYE !nT}q`%l4 j:8NK bIy63e~iGʗ0zV0hcTC V`b׍1Q6Y yHJA*wjGA F*ǚ)SiC-W*595Nfnn:y4*Qs?7K0ĄvB#<iڊ[[n<GlVDT**U,NX-ʒb VCCÊd+$V$b@EE@bBAYQlRmKmnm*s/2l1 m ,ͩkkz#&ؾV"ntb9LG?%d/aR߹fvdžzx$2s#~SM5FA0hap;<Ϛ9hwmtTJ9}^d{Ur vaLP.5M47[`+W++tDJk>gE~R>@kS(89cǶ]{EvJ跦N&şXT{_} z]w#4M+*[P&k^<Œ˸w}ٲp;.$+xZ/nv`m WV&hq\MuٽnWf&$F?MJ7)bя\.租ZpPELL24B8n~ЂhبhLLLIll1dvk;CV*/?8o4Xc٤Rf: ˾=}|og_}m'M#7:[AXQG>y7L ?\n6C.YPi[?<ПU'" Eӹ2わeU˃G[峦j3o%]>QI'-ߩVqun h7 K4Xz. LVM!E#aé5Z@^UH{3Cwe~e&VAx`gʛFOJ4ظ^(9N' 6l،@TaYJ˛1 d2y^-k_9BinICÖζ;=ů^AtN*=C+#wWVFZѝg%λ]HY{@yǟ8G [Z[܃3CEaƼ k K2SO5Kzf#JcC(C!(\SrVTYS:RbGܧ^#xDg}23[w‡bQD47}J~CM-2RםKU,DİvIqDCjdj(bji;|qT̙=Zoz-q'"TPtJCEQ{*UboTAy m+UN@^XDqTKa=@qۧ]V0,| @gݓZnwO@}]O}ԙsoc5 Uظnޮ֖1ISW tMWl<ϑ1hJNMa:;ؑ4V:ϢgKW\fz?B>}A7c*7cUK$NVi^|*\;;֐B~qHr gĨKʢXt,aŴ[(q^h+?DoDZ`/W;Iwrҫ \жj99( 6uvd3{<ŭiU :-](v@uѨ.l$RԎ.@咯@ >S2nM0MPC2n/LD,$m9Tr,`a;`]iZ"?_ᔡ14JVs8ܟ^]-71Z߆{˗3b3)Fֻ=f] t'QfK PMǻjlihIMSS']GhES,%xr%@swQyv+P =PT#N\,#8q}z7Ӄ]v/0ż\.>){};v|=kVcň['&'z<]66AEܻ7Rkݣ@e2yT;w'lgZ~NIIܲ$4bQ.;FO)&Y-JM-DxW\vW_v$FWnXaՖD]*+6JoACO-*vu)w$=Hޠ߾e7[zȻeW)O}r|!Loݱ蛇;k ྻFR3J^1*ѝ1Ta0K?cW$!\{do_̹Õr3cT7 ^~?/t𚗙An8ZA ]|yQn&jXfmc2H6{0`k+:GF4<ؾG1ۂqkS"wF*eMF+i!C!;5ڢitxF"xt[Gnz$;bMxܨ;?msU˝ZrLfen`żbL d5-t-mQ8nJatzl0!־[!Ū5fd$Z!ihx`L 2#YUϝ]~W.VVNP(wd͙0vֲp4ؓu4LNHL9N'{7S۟PPZ/-drVoIy'U}}Ԧ/7>OÀSwfWql9QF1v+3Ŷ'za[ZmB0qDސ!.oM7y`}+DZ2陧l{մ?rrr7n=|ޯr?^}o߾ C3zBa-G}̤ZZ~Qii{-p0 X[YK[>˒8 o}/aGJ.ΜIGyL=-m/s%;:61]R3&@uWOUG.-jh[4$53?X!N[HQ1v_ZOJ^jȽ?okͪ NK_޺9-pRh2i*lq@Yo7hi5y+LW7|qm^>k]#vLgYL4Zaazb@(!X]qln#  _XՎ QH-]Khhrb+4RB[Cڼ;yۗT$ PF @b/[4El5>Z-%8;6gJF?xpjk %5[x~wI j#y|w5|J%89:pn_>Gى `}oV;~^q{2=Q7#qlLjH$]caLJ{J.|s׌Q‹-+deʢsks2ҎU f|+;o߾̙BbHQމL~Ըkq`2 D"A `kںR ʍI_)P)ӎ)&ְ gLL1HD@@XgΤ[ωPzՕb bb]]y<^[rn=pm+t@`ǻk s=sTݔ ka5DP٨bvvnVVʔ'ΦZVơh?S\{zW|g;35J[ ~[q?v-yԦAXjV`[fk%͝qr5ik* 1sOY2Rsq sT/(:z@X  ư@fvLntIDLz3#]$W~޻3/mzaWtV%c# [;-ZW5 Y6QKH bEq5 (i͈7lMcty: UаemP3ɽ63ReQ q#ZiH o-G G[ŨAB;N8Eq& ?vt[g8o7 *y!p/n<>8|3Lda,$_.[EF"!ԩZ8;Q]ruuDN5XeFU%Ǘ`z22^x<ײ2,{Li+|b_ Tt0@Y(-wΰuQQ^iZğy -0ldWhi#]*.w:ƒܪ-3# `ņ&{WB~ڝ$0-++9oރb Lpk,>+;;{'}}ݑr95) O:L0L߈X,5_;LW^{T*TòzQo[Zx>4K&wUL&Ϣ:EW*:cɬ]{x'A @u 0xnޛbpxE2Xx,zd(|sdu5/a B|>+9T `=v{];90Ц#'Ū+6nșuPcNjeݑ`5 8mvŅSbB~-fBsfV"3cn֒v$H_-xǯX[䣦z"2Eiq,O`y%*ae 7hJV98]#mZ4ӣ s25N1L&K&g8qnm*''Ϛ:ƠrBgeeYMM(>,.[VYb x<6 @ttiFlͯlmX\k U_""  Z*xCsM"8!'16: `M[Ss;ti!'Ůt(G$ĺ-9g)/4"wfkz>fCB aل D {VVjkkw ik*t8B=P 3 d{?i@Kχ&7sy<Էո}y=HANk>5 (7ݭ͘zi2>)hA" jo\ -ta&:fɆ%gN IDATUZCuXL]@YZW܏2& p@RQ3=?dVt޶'׵D";*dT7"A( *TC\zg@΢I]pRC͍@DxݴvS ^eXF+j $ bU-1NfaIQF Eu[S9O4l0Hg$l(c^~Z^ZJ:t!`Kˡ]t Rb\Wpz#F}At~f&I6u.W S."f.%skcilxW\[{Qt?E"K4C'F)꟩ b?>ǩ״h](0>Țwܵwjg#ꀯW{ʋ,dLzXj@~L tl=B C3^N_kL90BMB99|,STž>*y+WO bϮ]bIpvB[RTOg Y{apBƖOx!VؚRV녅ѩ3Vo91O S޺u%>&S#sr^b1Ý2dB+|šS$5 /-#"^R#.*~Pjw%}2 ҄Ng>t$Vя[sc|Ԕ\~竵C:`(XRt%ʋm.J]` o8K6ՃP0(@ kw[oa(&-jt{w38;mч{ճh?=z dYl|zRv˸&fP*/7w5²houmw2n 52M'i0*~Ώ?֦FBsy+ `)^ޅ0/"d2$$UBax8ȟseXcG)dS1<3p,81K ڶVJ ^2!@P'yo6ԟEi% jٰ}~/[ut%wi/con5{7R(ogTe-:l F Nw-.xINdfffbo,fꧯƓU/O7JqNݸ'fasr|~{şL#cFwߥ;+<X NW&ݩ]HdDWashOgR$xy]F1r&nݘypt YNnLP$k]N?'y[B,p5{INxK ۬<,VV,[_k|-K}3 SS+57v7,T{%yGSc&+#MD%bnH0YYG2yb>? /J$QeJ\D=vZmc'r3q8"aE{Ϭz`P "s˭#+ĀW?>犟l>y\IwSXIb?M/ceK.dg|nach2~,:sCqzUX=oމ'`aa4HJL~1VI sH=rw7 Ol >ږer߾" l6h4xNB3|d7gFc#K23s wd29, NN Cɝ \dZ~F͞*b{çs]L 33+$`ڇ ovâ`ʻDcUԥ!7Qee*pw`tRu9ė0ZpF|ޞvBz2pf!LJ QEPT> Ɣ:H,ELo[$QJ95i~k%h]*/nXXw%{6m8A#BFvpѢ8XN>HSH /w6s2uEm *5 AFm鲐@vIsU-A‚O ''D;YSe Kݢ*$D dN9ڏ>`a%pԳG|rotYh083\Ev]hS8>9WPuBd8JRUgZJ]=x\ fwSgOwIhN!дk-7Йm[&I.W;;St]ka<+ƫX¶o rN媹M&d"UhYngG^])3&Y%hCaO訽\`I)*a)g/NMB!$' 0 <;Lą>[b Gȓ13"`]Kz`y?^ܽ $c)+f߽Zy,&)| < %g&zu>?D H jRyy OZ>8yKHc%XL&L?IvvXǭ#[l57;vAsTedFVl|Nuz~ԥ|nO~\O/2x`#Q# _uKr!ooтD4D[PW!˝; .NImG$=#/5!3Pd>D]c$+D؞`HP¼ N loH1RȐZ o,!喝4M1:0\pjlz7azU cA~]R66cefGW:3QDBgoH00P 4{Hd e1QfUً6R;R1 Vc"-?E!hJGSR, kuFZ#`KBJū5Xi@pzOpzǫ\/j6)ƮB9wXc驚M7F6{.ϩQCޮE6YQ?9ح^/)|+qNWnnFNN6].@9)2e*K-25O! ࿤P1lEQQJq1RXҳQyj;YGCE`k(c0iI/\q_˧}{%Uh~3>Ewp'UMmHySJswvJ0Ew9:wYiQQ}nV(thXIFU`n.;+. 2Y%0=iNJQa]/TB! sO`A2d-' PFWG b'`&i*<^X,/,b o |~PޏOzE-+b8{r'VM(LߠwǾGqvSfstݳ'ZgvciY+5L ZzOZU>?<\!<4~_ ~ NJ7l.#^I@KXO@$@dzeT0P'\J:=DAS(PU<1c8Fye%ǿCQSgY3U~*=SAǡx}JZꞝ @QZ0@LPʼnyqS+} H0LttYUut!FG*V齘Ƃ0Bvln0ؘ==P Nm_p(4D>]dָ&"a à|-yxӫ]Q ?Diwi/L$R/`vVV|OXqdEk;!KgD=بΒ&y3^CM/*^B% GQc87+/O+/OMK+NReybsO#YfLt=I?eN2)}1Jz|yUJwGIq$ ï7I ouSg\xƥD@_(< r.gش)p뛾\P]>N3ޮg+F~A2Ws23]֭>oމbK?mرo0,g:+拧7Ztbaū3 Nnno^~ߖ^[=,~xG;mkԖYuE=?qgBF 1߆UenҁK\q'` <+n<7D!x8 oB {Z:#Put'2?||G"SؙQ&sP 0/~~teaЫƫ5cޞh; 靕$zuv"H`hCKI e44tZzBw@PocA&8qO m$ऴ.8u l8;FF}≫@@TFhDaHO{4Lh;V0W!@qiw=/޳ARRs.H@ ku?Ww?)8D"WZRY &-'Њy1aH)e-ғCū7!%HRHiY,=oxԂPzyvwr zX\̫~\9LbP}|պKo_a[\LU Ps~VvU3㍈ (t%T!בګX񖭫{n=iTa7̝!kr =ҳw",n!%Jv V|߀~IhY]p !+G6b)7C˄ iwp< QJDs2ŕQU0Rȯ_*ѱps7J[zC۷v}LN:E}ID=*7ʝEGZBMDGU<Ą2\?i\<!⣝+j!D$|Dm+X+Gw CC3uqՄ1AqpSML9}8(vSmH\<ߵ5{ IDAT!l6l%8z{'@sDiǔҦTA(CIN#lZ$mu]g'-1CT~Y/mc/;ף"$||~NFF#V D"FaEYT'Xu?&D'ͦ\n?:jRVrG2OΘլsւמޔe2ş`+qKFԪFFd3n_RFTN1^zxVѪ kY/Èmrη|V[]ed9:SX8' +g_}ܹǏRZeBo LJHı,8b|,=Q/--0c'&%Q4!1kITbhXl!85m`xx5Ha=:j)܌;wf}K<^frE~a񒸘&>?ʳQ~dfefbcNhgkͩQ>zze' '*&ںHgzxŁ5#*w)K6ڲUM2/[2#D"UE}{5@GG䤈x_sCrܝHL(A5tp#d%uth\yLb?Nroi㲫ˈ=襩 Qޡf|fXȒ~7Ƽ=祃qQd3 Ѹ0[G AF2Y=Jׇ907UZ6:\)UKoG1Z%&B(bHeJhk3 p..>yϗP{e٨`?@ǔ] gJ\* *5fآ/* t˅2$k4r)1[цRxn`JI.ഺ ADo}1E`p,qhI^KY#M)5P)wK.y4xP)*@VQol>V_FY-a Kib2i_gHb;ϕupJܽ/?XrrGADԣZYhQïE1]_Q6qV2SD8l^r WE;ֱK .n/]=`cAc[l1E5Ǩ::>̻ҦcH#+<ܳNdk{e# :=D" 1G_zĩdh7} ojF yP& F(8@% C#Ewp$24E;?۷/'95;"QLu/sGq8ht{\M]e8>@xX?s 0HD81֝_Nã"9:J#B$VVtfwi=4.Lfeo(\Q+qtcy^Xt" _͕Gk+!x<+jڅO~ Kx 9;;dgo ++jMEY'dC;ZBa_`Qwid^^fJJux@QUBsgYQx'"si`iˈ/0<K=ZP־r٩ȁmk~_Q&–´=,:]y^X.OdbƖG**Ҭ 3ZWoۚX̒,͂0_1B9= _$Y~51@aʼnSRSut-ݛ2Υ}ӪI1dȥUv;z{_emQQ˜2{/m5ZWރx\$0rcS$Cm4ӝ/x:qkF A>{<1{oz楸_xpq2WB;] RR02c/lB c 1 /d>@0d4dOwt$s̡{;_~X@QQ?WoSq| 2sr22}Y҅x Upc_ݺ_=_X ua+chG0E1tEKOBaP@@B4"'??=33o2V&rVb, V-︋.ĂO,˳3v]cs8"< xbU$*J!&BabZފ4R^mvTbs&[V5'iS׶njZ,fɴ![<H<_.z6o #m[\u[Z=c6lF*d:թ\}^g؛:z0Ֆ죯o]Mi ]aɤihK[ ~oxN hU.yjjaS5 ?nl:$.D{UM^n0ѮvӶMۈѹ/4cmRv45 m5%t['mw <<<܊^ԹvnֽdxL ;U5I-mS+#Ʒϛv6S; vTZcM`5ny35.LVIpHF=jON~<\CfI*5Ai86{gphnYmSo=Ҧ]"ht!xܽ,Ɩд.`%i`΁cf;Z3Ou/~B$:Gkbтzjr@'Skc? q*ZCa$*s#1%b77ٮ]4gZڍu9PW-Cr, A%2\fK$fUO)A X( < %(e!a gn@/[5mתg}4%(%hCYk/fؐnؼkk|V) ~%|O8u>BȖ!b F=VQX3~Y*x<  / kyxpv gK,L/,{4E?NQ6ż댎.0qƣGr0%kfTqhRcm[3Ba@]L))Ec-5oskMWyC,;Qb[mXF\yK{(I"!g ]Ǿl(C MO{: `d 5.%Bf&@#c56X"$̥ $"I,鉘3:& t FR"m[qQ߬2'mk8T7Lpr~RI ?"+sKyo4`@pA:zg\nw͟ٶ<\ufǒ.*ͪç2W4 D>Ars$ ǫԐɴXCᾯ^+ 6RA.=BA)S – 9*!R+{UnyY1F 42..᜿ӯSN 7~,itJPd@ri>Skp{1,`Y/v_ٵcǶ [ÆhCaVժY.RTn,5`P\*þףp]J7p]v4Q\WdAΕuX3ץreJyIJ }]( 3P[goW@ƕEW'rZ.rFmCIz[yxR:=l4B!H,T̍.[^Qbjwx蝶sfQ{l )Bߘ_k}" x b,+0z'W=]WM#@bph_pKۏbXqۣG0iM0ՖoUʲ2%,X\uƲo"H'AFUW;q_9nuơ=_a{6fnen=Ə^~+ˆS"Z{xzHILμQ{~*oIptX%}BG%Ϊ/{ "#K"%0Q2Fkjrk0J_߻V]Q|Orlx3;%)^;9߿m:%E"Nk+%s* M0E8w'#YŞSP#9׶;bn[jteT=cR9 go}_J"j4"7 Pq [RQwaCZ*H"\%He#~VL+!^pxBGPkAh@ $y6@.O 3hHח{T; 4s(f$&\wf jA3áUbֹ*TVg3na^#%bpZ&ChL.q5yAubrh6jgN`Hql(?0չ2!:br{aqm`\dȥƱRcپ&cB7A`l$zr~=0{2+u\Փ^5J6ehx !F*pWS2JʬslhKI zY>8$T0 |84QE2$ms2)z!%)>UhU#MIE.KMw€Ҁ~pv~@WyQ~b0xdudB]'|1 $LA1D`__Ǭ>^&0ϭm<3c">?|S9/츸[T}bu/ o|Ol#!~̈Bk[k(חp?OP׏bPG29Qv6D*5үe) }h 6qV 'uOdٲӖxK=݁lؿ`% W%' 0`ffބl{h9+CNy7"BF w@juZ;zFg!z%LUѸ^}l3YRدVJI<]RáTOw3ֶx9b;G:0SR 7P"Lҝ2 lttp(/>o 6[f3+TF2 A2pC2rh°֞ޱxHkJw֪_Uy%7M4G7Hm"-d0>892ؐd4 0Ov3гϩ}^؎[wq4""jo1|qLQb*ūi;/KW:^ dBpNf& z.w5`ytSbݽ4uH7;yqүZ? wW($*Ă[B|K~!]X5&х7+(a[,]yGUV?H64 U>foZ{VQ|~rh2ˏ^Eg|4RWU޸ѯxetTrض:33oٺ{HÎ(6f/RHJJQSg{/\(\ZPp?9n$FCvu_GFFoW?},5+]nRmvm de:jo/xJ֏ {RTOA^x+jZ܅ @YugO*6lӴ\MNx{23LJri?L)4Ǐ%NN40:1Qم"<&dIo ľՁNB&qt£V|Nv+c^$($J!"RukŮa>md^j?B E4dDc:~}f>e!pIS\@L&ؖpPj h C@Fpwʀlm?~a~PSH=.SŠK6ȹ}Z:/ےzE:<@%FZ''FAMvwТgn2RQ{N 8Ҧw9އ1`\/&Z^%*NKrhiP@@F]`2Pmҫi RPlٸގ='}ez,Y*88&mW\L[xzCKN7q8J3o:[]SB!t'ˆiݽZRF~2+A9%gdձ<5~wPlw^+5Pm;Awۦ {PPXz57k$nLTKvտa`f۷4 PXVsгُx<Ν0(Vߌ7O@T| j2^߿0)R$!r_z/6;/"(:c y<2͏Tg: rr܀Gl,P$fZmbqְ ٰ֪}0dYxc֭0 ƓL~mZ?]uڵ7Ox*!x)_A# 帢YJ%cݺvmdQ;w899]]99Js0 i ׁjvq1|,4 Lۍ:%,f:i:yqWn듅jC qN%Zwek.L :jtR/ K3/.p22r_~-;" 93n[V.,<[έyf h3,]& ooaXԼ p}v֪V.@eeu+^}uWZZy~x$@,g0<^16?<sr &{QƢfy+b?] Ubk_(_xus4ܷo_JCohl5~uD:G.Әʜ,o"q }ni [ʪ-@(A hM}9ZdPW2u8F#:ŃXZ?ęA+uj*Kvԣ;LJ љ98V>) oAGM6ӕ#6!]g7A c"g{Nl^"H( F;Dfu(yD.2H ZA6y35N)ǔ CeW! zP7 fvq6*8UBL7!̪:KKL5PZ,\ur yxb#{<6RlDa^TQt1j3 3þ B`w5u~q HVز܋[h+*vi m.VhMp=J $a&q5MZO?`s>9=@j?`Fò/.w}k *76s"nWUq`k=y]eq0x|A\ܩ܉X @9uӮVXctG=뿘VO{M-ZRR+: Qae k>I˷~~mqLīeY^*·Dxky{ۛxg( [vTPk@*wqwJ'|j>*^'UaI\TFJ?ȜHp4R#9#ƚI>_` =7Knn={GXU FPVHF\y"\HxRgzl~fpRS7ݻǓ`1ӧ/>@$HJ8!Q  YyM^ΐ8Bjp]v,-}wɊ9Cݗ߬{B+U@1tZӕPkWC;kd8୫Q!EQ2ɴtaE)=[f+k'jL(zׇ\#s~P]z#r/yUGs2@D&Hyp;7bHJ~?z0ZLjmɑ: aՁsKv3}!b1,^ǢT*} 'Qkd? x={1UZZzT ##-::?Vߥ-{gяڤcX2Xqp%~]mU97#4yBNouG[PUV% *J ԇ޻{ۯkC\VV4{)̓ÂC퐥R„;,LKDu0ò_y=͍FrtvY* 6v'kuk.6-/499 Xe`->ŊZU49kޫɩ;>(.+y-2n6=ا~Uzyg5x/@M+79!š$=tops@{6)f4 `@ĄZWM[+u4Pm *@L&a834*)9qqDJ5!FCڑ"45Y_PՠéP45nzzrxT;Mrd?Ҿ{*qH=1AQsj99Fx;} ef<=Qed | P*gh`{ԋR6`RqGe^0~ޅKq)g굡Ƒ<ӈL | 1uC@ (޾/Ϸc$+O '%e |Hsyyrf7p_*FEUDbgf&[^+׉-fLE|N :j-ߪ6cOOI!Ԉ՛썫HOmDo7[Kyen@ xUn_笘HZFTV=[f (`rQ4SA5{z:N/?رu. &p(NǴ#r<~.W{n8CPkj7oMG,k|=Pԥ>apCyV߯ڪ] CjmsQ5h &ۍӧgPdrߴ2+;7Ҳ~v۞y,/02ϻie 5@i3_JnkŁA;|z%_ 6Qʾд*nD]A]1["0uDp~S>1ͦ. (]Pu%xy{.!@1GF;Z$"¾ϼ[5Qyjv Q"}8.j +<[<Qh2P UE < ^ֲ2>|ڦ] TuJt?w1qŗM;6e)]s7]_qdKLJq1 pzzZqq,<&]6G'FaulHu'U G&OE XN n#y8m?GEݪO258|kݙ[9p~)!;A}|Bݸ+}ymWt Kp xK~3ұTհ<pcY6`1qq;srk)19=^"#sE,X \(+;/;*@cTp8Z-䑭G0E%JwLRKKiiNJ@seK$߈ #3OXހuh>h2i }52JdG&_#1JdlAZC3b#25jo3X ɿa?RW v?5b{/&mOG_՝GLUB͛w@6ë3%%v`ax5rى7{Lk ܼŮn1Uvo ;h]Se.&'gaQQ􌼆#˗d@\1ovCvv|FFZRRHuvvTj2NGV :r$A2b!cTRT F0Ee (j<w'bUaO F\pJkXUA;z!b1PIIbnVVRff2 ;'%e%'ggY7}ʧDvv⫆?kR6('ii7|'ijOYxij >gI>`,-o,9?5^L )^gy?w/!nCԥ^?kԙ' -mDءrJH祯pnﬥU3+ګjiͷؓU`t'hB }luwѥR> ?^Aؓ!?d[:XTxACڲt]}fZ qJqpSTJӪ89!V=3*L%₦%rSC ` ћZX aS1,|-n_+v`ut8c@ vrp%Q-Lъ[*XR[n+% AWg҅=DEC2[:{^9Aseu4ڴe3˪ m++"vեy֊z}bcun:}yA5TsԜL.Wea^ieea!!#>엑i{ɨ4$Q&?(nGw=+sBɓK-m:mHPǚPVj}Us@eë0N5}bjցmoȪ=g'- &5C^G:\\O$8knuę;wYz愥We3Ͽ0= U:VFp5:g,2@uÅ;{#v #uZ P+[YIp m'6 ik L?ԛҗ!o'-lPɪ>/ ,`:u4@Gʫ=fw v¦`R{V/k)86iCjw7}%8%6,BPkR8MԊQ$0|&jal榯{AضR@PtѪ}?}C1 r,i/e]XA :YW4=xTy(rg),-fd mʥ@&d%ˤ +鞋)8]-ŵqi{(;(|TnZEɜYwAfPZeAwJU_fڕU߉H2TχȘMj,r ^+kVPR+Ngd2]rqkc<_1UOVG2aS+R(B!ǜO d_~hEPA?=C>梢iZrK ^q!A-io#0: Swښki&ow'ٳFF斔Ě.a :`'N/+f 5fSUzLr0!%ɵaR[5; Es٣iic1{tz ,W4ϩ:?븕G`vd=tZVuON0uyNPo͎IMB/ik|@p&M'p&yOm o"6֔9 PZw&Щ(VV&5L$Q;yņ[wcye\~\w~pM#^y ADJ?.MS4ϰvt&A<+˄}ՙEٮ5gPY&Iˠ[uF+C@hw~J (j+n>FIZku Bi;| LOy!=]ɢ^=ήyTi耢NP5bxMG'&YM=0(Ӭ&2@HM9*O]d..a3D<^ [ew{UW?y 1/]:"zq@)AkhԴb>xީ| lA#%.jůǭ!1?q8mܾ,dZ`t?{_ٟ˫=i[VMa[Pk+ ~-/:6J;yX?POؑg*,so-f})5?4= c^ {HM$61qH1ִp8J9 2)DccxVRzss#b2VǘPA%XDɉ!>!b^x:DUt֟p:}[6z5'v%AK\|8 klj76:U9#O=倣ۏPmfE oy֟UC\ ciO.h^+DɀCW%I 0B/lPT,=J o; ǵ zOU/ Ӟnu26FC3]he$P8\7*nm`P=Kty:1dGď[_sgq_k|U4ow#ne_/Z˻|iߣَwKQ>!aő͂ ڠA7X[Īst4{*w_%~;]9~k&x?ݣvtԞ$H9""Sв0g_2ŦPiGe\i Ŷ/8h2n ` bFFFF榦nyM6׿h)W[ lTbH`Fz*McK7PMŒxb[߽؁0oԪL[rwg][.ߕwo۲nr1PIW/^O]3St/K[~2n*K24C+wβW-!7*{%%YY J%S(/-$ŪF`&jzs(#4̓91<0a Fe0DOnneqM7,"byOb)(о>@XXX5JHJ|~y[{Z-L~G̟ڹ,$ءuAX[%泱`T$.Iس%ϮfRpZܑzuС&z=yrrYV־^zpԜ 0E+MPI8ts=4}>Ԟ[cG@"8_ìdǠ}*KE|/7r 1nv&zk-3xh#8QQGCqiYZ6,풝`h*Ehzɡ;4ȩؒ?0 ciAy͆~@!(j|vdk'w~U>0c2zT*W9zCAJ/$P[;TnST_^<"D65;4;,x3a!*WJsNO S3%^k5# N\s>D7rqpx^2Yj  *ƁMT*'Mo)NX-25"ԉw矀}£R5R1=v(PǮ/U]W7g i<3c`4cܴ ap=W_{FC=m=GzuD􌴴,wvݳ2|n^8AWΝ"n'&~` vM P7saP1CG8.W$F]0cqR\ˊCY\H(L((xìÂ> ?J8wɝ7L]ވL@rלtɹu/l}M{6~婊(0û8ؿ"L6މn}t՛yeB+݄A5M0\vq*C:o+T=Y9x2?47*#FǛdEb4x\ôvoMdNQظ/n@A΢5n:Zв(nYQaL"y\=4l_s"gd)LGPj8\ox3uTmP񤩈n'dCB3 ɂWLx3 kl$B:~zS)80GMfUGo1|s4]f뾏/vE\":=-Hò7uDoPr0ZrwOOh0 N@\GL&t%OFG Q(DFJ$~%%q[T[9==}؛;M-?5y,fި#|bఁƲaQ:`y32E"n~~tA^֢Hk;]:uZUԩB.WRWҶe_*\qc?<Knqɺu;.{&I8v0IaL{IY+(uՁX(1؂Px>Ӫ'--]"t "#+~FF2Ќ9ÖvmYY%rJ -f>4քWUƚ"r_#,|!F^T*UD/L(>wrE}؀L햿%7L RKKC8ݷoO-- rӓ3,*W:K[/Au vxPl֤WSN.rK9(:g*ޥ_KK%E]H Gp2.}QB~W_Y{wf?jz|gײ#@ރGQ`2xu3;ڢv{286"MQ{t<-dCY*=ͮ;,S`/4Z␺PR^yч:$i̺n6du4Z;s2`Sec=4J- $ju\//j!Y^GcZFo{-d&67l'c:5AӃ%rǷeec}5rKE+8Dձ}k+jEl57 -u[ZvqNU8(;X*R]T[1\"BQr]5}x,j9q, OWJKb:3^ /JH)/eed9RQOׄoŷGA溷uwR~iiH_#??96LDFZ:ʒ3E"N7 c e RU#bs?b((vѰ#{'H O 9}/wwSbndd.D ''g`Ua E,,(ge%ߕ8`XmsX?FoW+mu4a8 J]bymmla7$pFFL̼˶Fy_rC;:8!'+7QB&kϩٓt!vwo6.ee:ڜ;̲{2ELssWAJ~dRǺjh*UOxm/]hWhmZ8dCii<={32t:=#\UYJT*65Ģ,%OYW=u ciO10OX~s /))+,,##m׮ԦPdkSغ4_Not!@wO eWvr1FA{FWutzЅ"2Lx|kg4j#o*o־N!&LaA>Uh6:PǠ^^hpVcA:uPAmόΌ~~A$ZXE m#B&Ui"oyC쫓_z|: 1'A"$aR~͑ }ާz/qK߾>fOpq쪥>{YKtw5'Aײ]]Ojy[w@++F,榦nJy#=I8u=&яXN,a2~Xim¸?\RĒk̰L<0Ʌ] 󬰁yhffrRRfffrffRtt޽ ;JD7Ҩn8ciӃbZ> Ȍ;&Iث>fX{#sY*Gs.RHxXK3NQl,DG]^}y9y0/:ӠafkSVgm?]'ᣓ+"tF P:>%gq~s+|/8۹oer8ǃ9o P5hu>]M- lis%ULܣKwxHm#EPͲ&#FӐ E>-!`ufQȒz5n'0Cve dqW~[mX`ZMTzzm]]u+^g.qspjMwWV\_#Q( a-duh> ;|Jol<1&A&*0:Zкpk}v 1@GI~dvbsҚoCVt|*۔ɦ{x㹷ַ`W>w 5y}N4L;rS"d&N̉+Rmb-J*5uEuԣ}c#@UBG5~1"ma{iG\I <&Z/HOfp^zgXtq :Ճ-~KԚ{ ҿ{ֵ9W}Sfh oxX /; ;ahggatt#i4 6~yIciDwwG6FQΜ륏؊G س'Ks6W-b?]ݼ[O,7#;>>vv;>CjjD'ۨVV?G~ܝB)q,էTjYUw@T;v$뱚j1u)@oCL&%ʠ{!:b0d>5zw GHqȺvw<|cs7BPkLx^~4M k Ywxf3l%` RъŞ'spZ .2{GMWTpDXLF.\æG,또YCQ]g Rnyn&$QK'H&u.zPș81'##E~T 99 ջ_Ou~\ꩫ(&w<-#│v.Wdތ XKZZYE W'DϙptFl[,_5[|mكA`Ի'P+E^ȷLuS}ۆgiS~VWA1~NHHV$ fGc&-u@}w%>̂M>v"3Tc}EFu5&ӞD"5p8}1/!u:i K} 22.}2Pyyld5)y%m"8[9B3F@<*1{︦:I @;@ Ä=] PYZ>m.QukX$= +Vq4Eh=Lsus_@P]oW_R<\%>o^m005E<`xq@)n ~gM~xвHul_E"{8P}vԚ}V+LėQ+Wv^ת1AA1_'s8̇7LԑYag?u*d}.7Ĭ3 *P)pѐh&I=7EF4c|>}ǎUh#Vk4הohnGK& 4A?u㈁k Aa8yJpD[_[wӿ*SJqĮbvmx8V}3L⏫vRYWœ‚@}! j_ݻkb},^٪Np5kD"=] j;`Tml$ʈ-p8`ŞKS˺'@[`nlq<Qb<5Iy(z#CMPikIlx1QƼZkuD8NdOQ0 ޠQiXt.ZjrsC01oj5d~ӜȺ1oY2%ekCHU+TFOE"H5hiQGx(t;qJ%pRMujGs/]zPAcڥQfyV[ Hau7PP E"=~W񟞾{տ@odE%SFXr@abbN0bz<>b$x݋J'iows-GugH:kk%{Wf8GNSFbw@fcm\ \<]]`>2o zo #.] AAO˜;)嘠KniMX|q04<^s5 qv 2t4s;sK7V5-jK$fi1C|:ωӝǣ%%4U`memtj0 E3_ôo0Mذ\W BqjcC xۯep n=>]ɍc/w>c>v[ΈVfsKpaxj'>5$ k;=iLs(ooЍ굔雏W`_.<P<=FTePLKtvVRbz-͗%%j--B&L'0.&B)O7r'8fa8hn +vXݴVYniɟ]9 N xxߊ yKOpKZѡVtDO]=""s2}ZOq:tsl9 ouCDBq352iri]ZDzPPzzzPli'bKlh]{11;m(j{70%hFb2 nQB`{(~O]tzC\\" ۟=g¢Y3 fLhx֜Y%e;)zjb-Qbjxo<ȅ\P2^=9~LsyӣE8 d666i○s4~%_NSc~S{ٟdz;uzFZ^Q_>9R ֆ,4 +Q+/sr?A,DG_U!Рe&iRUZCtTj1t^;[.u//2r`WqmNgUQW1="&F:!6lW%*ͱCv{wWGU0V|hF;j4#Q(y@/-͏ϧcX1:OH`|_܌T*My9x]>n| o߰2sD[ܡZf^A)1I[qF&Lcm~r0_:x3^6;aAe0M2Y)#HK[@?MV]Ho݊mX{|ll!d23*6m6]ra>t""ni cR߈b%vF E~D,F 1S!3rY~~%4eaXa[6? sygsu{~Jb#S.g#7EJC~$)]Qs~;L&J,>Ii.4_]r`T_EYZ̓퐎.[\}Wm{xp :k,\CKC#vdr(>^n7d~Tz.n[G=+$m֓ 8T|j1@S::wAnsU[LϢTӲJ\i`rh8Й/rB%rwm\"?b4LujdTЉ})UwBm;bqb o7 ozVMO5b'{URktK.^5[vo<0@Ȅv1q 嫊Op}=Qc[]]5߲`΢YLLI`7FJky;/T} BR 6;̌&qqq"~~%zAvd2yDCPx˶` 7#2+`OT*!<|xkPa X߽?1jLVT-x}x<ޠMJs2a嬉ҧ+qkyW^;O?otmg4yM[[Y~.ܛ"-iʨkZxmyRŠ[{ʋ8*\ M5f$1N>4v]NO)6TU^GDEʢ[A1)<^{4faDFBzȤ8T,Nח^=AQw5/W;E6W\=aQus1_CNJ4P>?= + n*BIk hP gP@TiPb@` I&թ[4rW[$` hlP< Pe]rcY^ReMũsY% DlPATxԋ( nꑊ]ZiQ~)A@ Z:T*޾͎G$6,㉍=+o+YXTFH/P5JqBQa 6otGěن}ne|r21D"bq8DAqqg_dtaqgs/.(*5,7tÿfhLɄY$05a^Ą;7x27ȄD[R5V1&YϽ#V{bt:&%#RAZ*5lN "1[5wM B88b~KfbЫhxyj~1=t:MwLX,0].naQ9/-4FL̃;bp-bn3~|ϟcr6f3Xw.٘"B@/;U{=J|o7ul>[6YV1p b?>myPW#q ,@Jy0xRzrDNj6ذF^s'ˤ"z zYyR~Wmq,nܑ犧V* tڻeɦ>f233I:Jn(a@E lwbWJ=]wR^ig.i6[YF|0):]b8oI g,3%/d68mWy{Ҡ hW9v 8B>j43i(@hi̱l<vKSݣZTr)=JJsȴ^[+jzaeMpTe!u75"͹~tFO[kE$JPL^bJP3n_hzWҪyX1 vv|,ځ=Bt:?66)!M ,2pGi 1|NwmW IDAT`ذќvRL;N2s.}OX Dl9˩k74,fėz>.yW\]@΂f%> gDnYuQ&[H,t%{ʍs*խ%IMbO҉lM\\"O4ϵ0!%z, >AҮ\8ϧ{{vtzÒ%'_`[ӽ DmG FFuߜa:U1UC x6^" 1 ,6\} !d7oF XjFvv>|rb>)i%+II/=KgMD53zr\ڂ?β˧9Bg~ٵ)EzSv"OyV&.5i R`+Y$jky9 +A~}hNƼOc3;#~ru]1)(Co\$'Be~}Ξ;x3Tb oRuX)#wٰ!}]g˫FUL"Qw Z*CC,)kkhdz-l(*|ph/ ~Wq$%'0>h]m-3VDhMY亹)(;?4iіKdNۤq^n¹7oc7q6?3Z<PT˃{^}1h@!x\)Eק|UOǿn/G"Z)xpkC}k5,#5_ϡfOl9( F?_0zQ6>|RJBE@ ``!X:a~EB!aOu|KA(ڲnǑt2)#َܟ2}[LF"0<00֤X,3)"&YYBo | <*5 {s⒭(D3lXWJe- ,,A j+&!!;ʕIfZs>0͚щ't,k@.ȳ2K8,_K>,;RB8FQ=*7IBmEv>$;hP*C|r뵊/]RMn{'vs JLy5Bny ӌxp,8yYٺ]OЊ+"5 &Ff9J6N]+۩l[ 9ʽsP,(੖3JgEկoGjN]zHvܶ?inOܘē7~urF>l6ǠBWU|&$>S=H mF\d4k!{;.5j*x.#"aMMhQ/4c@xGuKZ<J[ү @3WePKM%9+gzzz?s?͗ afB7vT~)X~݉'Oh4^Zp7*-1l6_n%m,A `WVIEԜӏ̊v{KT?'Ou^o,—( ꄐs[VegG wkuju5A.FS B8)rGA[Kmj74mY]QtL ^D V<#ffV/pHs<@ 7E6E\-l[S(LʪM\pHhpJ R" 4{5#7ӧT:%An[ysh3>38Q@oZzT7@LkWK{p`ޤ=N3h ݔAT0v`5,xhب"-.=>/g_LƬJ[\cO} Q-WN~F\>sgs,yHsq{Qڧc' %(+=11.==(..,hK|[7G=w}0yv{k?h}n5TZh<mV17JJF,q -ay'Z]n'ӓ #ʒBSR\jnrVl4N895RQo8qAXc8,Ң.i%̡`6,((6w@, wL}['//Fw!G:YW}5T+󻍝yb@R;;OX:ԠbIIrD,xiiW4ӱF^ph ώnYEY7_([\lǪ>N.> RfE8 skOZ3ݲ05Zm}] RMkwLYAZ$Uݽ>2-^82KEuUMi~7wIa $;_NO  xxMjƢ(֩], *M JxQjQn Dqf&,l1ǿ.WeSS-_`+hkuqHQœ 6 ?e_)5>>9][EzZғbٱIlv|` t^+{|X8A@#޿ ݧn)[ȯ(50wDgOoONۺlfIǍ|<;|f)*7 TvZǻwzuxH㎞vCE]EprOBLH>~yK3j,l]N[ _;81ulԖe=0hcFu,+ts.s8[" 9ֶmb!/:aDCE7`gJ0WT*oX:Vk.x!dةL ؃2a]bv7ɝff0چ=!C"HHȥUC[4bNHܩgȽ4߶{K1GsK#UI{C&$ld'z.i ۊ?sQ8=} Ngf͡zU/Oɦ>)uW@f7FPl J/Y}*VR<˿J<Π/m:r 6E+K}C#wDPѠVd-mMPl, BP?oIϚM(aҰޤgVXjRVeīQʵRB+ :DYulΑRO=.CX5p3YzEiuZ䗷V"Ѹ.'93TXiӜvw;˨EsW?}|! ge mmﵳyO[,dTR%qg+k:gg%,#Bg_/X"~GT`sQ$b٠v^^@9avyTc뼐 9om_ka (olI^Uvs_2@PPz$U[ SL'O2|_+]>ڕѹkqwtaU#x._ \.A(r'̅bqq.oއa[j .OǀvʕgĬaJag9LW |2iդʰ`ఓhUwa]5 P.fp8KϹ7'<ٹhEtC 1@J)p8sp#Zs|jMN-0 8ލ{~|x#1O\9#bjTô<| /yǗ7RۢLI4|oڜw~")r`|A  8d52f1M&JE +kzJ](c_EC8e+kN ÚkdđnS(n_Y48_D GhH8QEE.!H%Q.^Y1Qi5;#vj@̝{0 05;PEQܥEq&Fߵ92kʃDoҫka:Rooms!YI:Q<(Zse>?1ŗ.gcp,]䤾>9((]{QCa!ˇ9X?CK*|\RAJdnE͍5sObm;˻V|Am;9o\eBcUkӟW\~d;69.m E2Z$$/e XT3-==aQ}XpL#+W};sP(bvmL&P#COa*xݡ&vǛ'Rw/dx6a?PPPqUPPuyI R.|tٲuޯ AE:|QR!s)|$)Yٖvv8n*k_w\.ZT @$]z:]ɊQ j9PM\o %W~ քKS-ͭrEDـ"4((ʩH&VV(ezuDf\8\mm9{wSG0(L:M!Ӭ?CTj69Fl\* =YEh i6w Um5M4~R˟4P_:B\zav7}} nZ uQM.UX>H|uTfSⶪG`G)uun7Q5*7~=@^=dIC鉅֭O  (Y߫~`2JӱQh>9imyόyb od{o5gnvO=n+p5gc$IlvB[]kӗ5$jPHbq9@E~Ϧ^Ou*hkm|x6dقVO@kd4;&Hh 4餥h6E“MnkUJ5?83ơuNgƔQbcXS?5B-\u/w(Bq~3Sk=n ^ZQ<^tvJ)99*ożKOr@Sm>" a%vKTާG4`}óE0)NojY~ñyW[ң!-墯s]_OX!^nIu!J=9vWnEa,7b'dЊrX}vosvɜ( '?\6ճ1}Id*N]3兦e٘l aay ՈhDo[ciG;;y߷K5}VC:?X&ӄL&+3gmv %%>`t~'y OҫS'/q=0Υ`B\Rk=`wу_X/z5.9(4gG7fAغs,sZn7SZvW[~^pi/Nbh.n[~=fohczn~H*5†ﰲg?4oɈ_ef2E"a%t:ܼO(9RLwP<@**c.:?ǹЛNo >cOyswuȻu+x )KR&Eo0my'g6Ttt Pێ|Pl')q_7M[P1qII{[?u0vݥu l6;>xa BUwv)ڑΏ\((3VKLmS)ÚjiFUȥV>‰fzrgZB֭TVgwN"2붗tqB|>'l_jk'veLe6cd2=O*5+10q#ǣIGDzέ7(Bm53ӑQ(6>XteF"1kX0{BRMLFhC[5Z1;S7vE,D|T ۻw߾}ayyaru.x# ]>`zߒBxl-X;aÏ0, GǢFmlr2O3sqa8Ճ12dIW[G$բɹ x=]Zp*Z?>XB? DG֥Gm\-ngrb|??Ţ䩕1J%t:]rA03.^\w;//JyXVe|<4(L@O*ehæuI=O/)OR׭[?l;|a Cq3ƒGsǭY|KvWm{[Frѯ8xwdĀ5o}e ZpF2J YY█#7^NHYN ы@NpXF9wS>"C %AmI{ 'zJMn*aMQ@u[6>m)|x p|xvǟ_?)[ e7 XY}UYNWo_T-T DjsIcHP$ϳ O2ҩ-3Tvkj(tE(?`j (vy:tH#8d[A%2O.P$"֒(dt[W~ RJȍ -(#ϊ_e\Tfu~kT{wp3V m1s̿ק\Gv %^[Ա}͎pX ,]zL&Et>Ӱ -AHb%JxoJ}?m:$%b QRJlicPY|ɟ1lCï{ny+\?=O,pXm4AKΎ-:7Őꘟs:v/Z(=zc)7r\:3IV`oAaq,+t=ƔdzL&|ɒ,'77QoXؾ'%_[4-81Ho9=?(as4cZMZUd7]m݂|uTs;b?dږe"{/7avX)ZxZa,'j8CqUk:Y7c*=Vݷ/4,,O_4򼯳soUoC&5OX? {ޫ{`DMD}FFb("&`xKbqD-M {I:--s%Wl)^.Z$_*~-'H$lWi [}QbB|Nz(gowCT 3t:?#4knt>N$jc Wŝ3 K^+޾4L{im,\祥NNC-uV*5Kbq~~~i0&3ߜSd&IdfhP`eURӶRm_C;R$Rap jRX,Ή++;US.vBRds[9GPiMfN2lcCy-a3%r3w\M4瞧t옺c rdii'R5s{B== E3.(I*/Q0*) AQX?(#S ULU.0ϠG6v[qiJH꣚~+@Tg /kEmk<[XHoXhX SƋݘlԉT"*a$Ԩ~bۊﵷG26[<`Bvmz|\GXBB.[w)<:V@B!'O. L"Q2J9M{CǏUn\zD4oS8.VQꀭqqr١*G6~sj Wڱ3=aJ05rltM9":f,7==g OkɎS!:vҌڮ(r ek,ܧiRƂW9pOPP:"h4LH`\m?L\.6ofHD3F s -0wW؃B![w>}E-CXV̷[_홞p 5oj:(.bϸܰ/斡juσL'>k7z{ʿ0<)zrłY], ou-:SJ?mn9)Pϩo-&}?';TXcDsTw6Ho+Zhm_6'PԄfutx{]Og\I6 p r\vL  +K٬P)64 jAz WQt~ڮr_E#gaٴ].ډ].bW,;}(5J H7-aU|êX9v1](FƆ(Nv[PϚ*qݘ J@hg u>lWBxh{ hlvtlKn #? )5-Ȩ0Fs[QUgd+a #^;{`VQarC“QRgq5LJd;!p8,6;͎^J9,T[>za/^cMJcQ8Iv~;`͋acD%T*/!旚FRyii%:7ES0LfSR2??_O_N"Ո@Ď"~Kaaay.ԩv?}՛{ rdW ㏢J%"gb߸֟7tiylҚHօ ۶9q{g؊OU[Vj9}qAەdWV,R 2 CR(6RRQQR+d2TP@ n}W^ :F']Z{5ua$!((al38: ɌuBl(*bE=@ @ q<<}?}yy}gZ^jjfg!*9bh#7z ^*-p+'#W-nػP|cWYu'?IäQFNZ"ݦa .;|2F@hܱ݇0cpZ@6r7U l5)*!^w-ߎƿgMe \^ ЀDk*jVm`63ou=Ⱦ~儀~_6,h_u3ݔFy[h"o;aj݂˯JJ?pvBf$"X"ND֍jƒ,|!Np|)iIoHavOuTNF 1ihh@Fdq8=&W ^ې^>3~uk,-޸(ڌwmq{YSPM?V;CԤۘO~AzeTSV^`cfіcgTwlnh5n#Fmaal:6;֣3h7mJ;R a*P(Nfh6\S87>s K~odp1e2`B TE"H]mǎ?uժFyStP(0WHB.]A<]bK̏̕U=ahf§Pϵ@I饵,UVO4Mfޭg}V=h޹ʼ/tC *~45 jrԤvFBF] \7暒j{!+PΝ At^ic,/Wb{588" ̊+*M3x`d twOҬJ5=eBvs]KnA LȜOac#h6aJ[Ih;^+ I+EX21rcg:dj!z}mn{qqkT+\Ie4l$luAHr۸f"%44Jl2 . + al8) H ߹\ָ)?D=9bP?%%m`Ey{ۃ{RW]Z? sW;_Q#7;a MLKwu07!.O]_\կE__7OqEEydc<7+B+l ;v3NI8jו589!KB+oV]fQͯN+nh&m Λ&o~77&ᤏN\Dߦƴ}eIL&kCֵ,+̀VS.jsOzEM n$J*6䡃-IۧDvvח֕c)쩒b)0lM^[au=Ђp{z>g%ZD6&Zu6.ЗaLj*mv m nCzA ^TeM/GBguiϒPv귣1 /֭jDm>R;bCgӖ86A+"32>fg= b>ɠEƧHK/E~+b\ܴVN_5|S]2@@sm;`))#5^Ub?wb5j[@7I}50fь"FF,3oMR*uu֗` V Zb:brPOC>r*S$3sMcϔ8ۮӹS ُp26||JzoGpp Sysdzi줲t1*q흃 X'BGC I>II B3, _|E4O"o6/á|kϞ(k5-&Cr7QK~YWk}yt? o>8q@zMlpKpBR=.^\.BEdJ俱=O12d#gLW^7#py}X? >2JWp25 #nSǧ䯿SZ#PNLd1l8b%~h&7rZgoڭrie|mkuz}GUŁ pVv5?G+fVߥgQp^O3v9U_G̮{ ONA-"p)2Ƕgߥ!NQe4ii\d`!g`X9,LBs||J 'c0aPoXD" ? sG Jˆ[htN v_ZlYL`$Å*_4!!˥V0J'΅ok Q,kg3"Nb1w,`?#X>kkeF>蚜EkS@t险5S賄~$_s.rJk@kJ=Bϛ7C  2x12tf>z]̢˩૚=aqo-^mɂo*mV~zr%dS,Ϛy_T~"(_Vg!c)1!-27\fHmh \2Ou$u+R:S3_i[&1U#~}]j}vre^ a0 I:uS||J|JIVH"񺺰pB3j۱)tW{[Pm2MB~q&W~"C&50{ G 3꽁c&`XsMk/Q] ?!! OQ7~MIQTl-6;/w(o4S,s_;G`77h:H:<p0G`LBßqt.'dceONeaR;!Sf$&geL&+%eO?H&H$Z39AE-oy&~p8T*efGDT0HM*xgC \(kE #yl*]^@^1?5G7c6gl) or5Bgof-[{(hmu1PT=ޯ`W{tM#r~˷Z]*_ EOwu["2V߮_,N[w6Y\ֶ߰r:ͳs薜ҙ$iU?$ۅTbaH"2h55A-l t,IbfA''dq}g_o'NmJ?B٧'[~בbio aVGmAW7ݔKt5)\]=~wbd7AKMg"x")iwPJx!FW1dl/u,oxgbpc7^ڧR~rWg $}HP}J<|asXȽAa#ffns3`BA.%gdD){o:iB*Uh;RF=q/{Fu_)&%JW &(>ÉǴZ|Ǵ8<^qj5f39YcCCs2/Iihl< ?E qh ^ Z:\ |Vs%qQ`Ng(9󭂤)~<ŏ^u+%'3t.Osa__WVV,PɁY##o~}>EHd&y<*$bfն (To17Ad8Xڈ2 |>ۻǧdHt a<y(r57{W/j]_iIXI@w??q0V܎ j@_7^Y¾WgwvsCڿ~4)@,e' T*#|rF83))ᇴڪs7dwz/a5N}KT|cKWblQǫfe,cyV=/Ӂ ܖ'Ԣ|-um7NYYp2{&uWWv.W]g׆8%JO_aO 5ACM'Ŵp[TQx`Nka*-!>>CC* *0kc232Ǯ(:F0c0ґG" KQ:?E*7JR }ed,(l6C pq\ZRp%i@j4gջGܫ+Y5y ;RE,g(Ims;$:Ij~\fa e۪[[E<~ii=iwO;hlT{=__6t9j9No.3y?j˔G,b.C^ߖH-dFh sp^Rynv_VbW6ԨҠCz8*ˑut=E?"~/-&[W1<]UhM IP@?RGE"7{Ejl_'ҭ&xl"U*-%[DBYjӡ0pbu_-4j%Jѧ'Whp%9;z<\_ ,IROhB= UʁZ>{A9/оxԕ0=\*\Wq9yQ ~a1=8 K hfe'&&)lv@; `FO$so(Uc6Y<0jpǷ&W`bzS?;Q25㏝H$/?xadL;*Εww}7N& w? a[2<. #^V֔۸-16=+[f:=m+W뇗0 ;jauPx6K)(A\ -Llvv?%~sE7r 8P(|6ao?p`=XMdfff=~5933`kVPقH$cuwWY~nxCgf}0H]P ڊLLdH\-˥,؜ ̇[!$ғ":-}+@ԊGr(qnO^^/û<|t7`N>h7O{alxZg6^N/nH$iC\:l\P|v|؟~魹+«MyW8e#T2 s\Ԃ %sݽjU9E5ֵ:rPШUrM"*3,hX'qW'Wwl,u(U} TQKfTZcNHH !t:nj PreJyDxKd{c]5V{Tw6̇Ca{ۉt y`&2'*y7l8?cätoNN(llڽ$8thlaQti>w3c JHHӷ2ii@:-Ár-"p6%_"D]?^gkJH/J q,v1zZqcl/fwrә"?t$& \.c_ F2'p#aߺF&ƢSRSCTmuGT: kToD".5qUT8( D]pH0􈻌]`~br{ų`^d <>҇"FkcW>9t`f5 "Ӯ6r̐{yMQd+B(,Z-b2vGN,tp臏-w}U?gl|NNhu51637tEK֭&3QVm>y2V8)|>E 'q搐hLD" O/(>M@4 kה;`,O8x6{Tu9|.U-t:9UX,=mO [V(glssGuU4& &4L.j^s}s]#cNf;@(w'Q@ Tk<FwUgiwOn}j+2H0zpGi}}=ylʰuyjFtI){><SGy/ @z0C},cߏmŵ좭-TtW,8~rZ֝XҭYo20mNT;\"Ңx`KiA~կZ ^X3Xr βIp'>Wp%63_Vr"kA /4jʭ7\VDg] ~;N'vNN:Nr8tPD a %??9ܷl!b6d8NRwyy/0RܚBH? 2LW4}#%#CgHdp(Y|'>uq:S)KkYލ /ls*>c{,+qyį?*L+V X,fb"Ӎ e_l4ǯ1+y#7THb%>4{{T9r2wf7|Þ7.wAz~;\*1 e2Yc)ajZQi3+#4'HkV۫45*!$a$Ο-LM:3{ͲL܈9/yK.O-S; %hF:Wq{.?u15F;4|%g9o}nwtV`WI=.9o /=VϦU h߳5/~Wa}aKr$Ax^?7h#xzʷ Oay`D|FOAʱ H!f^K[mh[YݞFBe'_d%K y<2*$9"Iip"bfJhgRh/V3x?IAF0&xn\)yD֦M)3P{W}u҃aZ4>we*W1JQEfe~wAoD d6ܓ@GOj< BV+Oex-%LN\9DOb2,X#n6rpn*+RDl"JF{{~b"ֵLɋp8!p=͠ңy3zdBۘ .ݺ }Zxuo~uNܨa_GZڈURo>ߍ41:d ʅc. SgrN-[D+wqҁr_ m_UHd[;qS%ƆZ~C5 mjSlc@ HH`PlH(rNӆZx42aq"9tDdBZkﰡ!Z +mm^yo 5 ~;n5Fӹ ;99(/lHVMRLHJw[kw^W[EN,~ 9Pһ_J I[Q;Zxk?WkI邯,˷^zi9"Y[piR.=r$h?WVm%-Ri4Mcm'bè]C-jh}X`=+Z^q\,m8: L1 IDAT!ٙ TZ$bmlon9pNH꩜> a{anUTrF @kmKTDBk6!pޤHʵĺ÷>7Q!̧ߟ<8xzLTY{Ɗ+en]^%V`غ]w9iHHRj456 ]d}-ʌB=Q\dʉs><~Á+Eq5'ukhhV Z7^ڸ|y]d F>*C0Xk=Y#R<#@ۺnxg70.qioa?QØlnQ(@w|7N'b1D317Z3\~pvۗOS. SSS,lg7 "_z'MC?}˲ \(7UiߵMώ޽iѹjprͬ+1'O~Y*aɹ+X>#U^Joqb7^gR9L 6/9 >~ع5**Ja+*Vcu/ \S(~a".a < H+ToM :ywciG.N4m'>F*^y::eF&6mXt[ :l.`{fM+"FKj!qB4 z:(:I5 ]>K`"✦ԙo_5N~/*ZE[Oj1ap75^‰Ҷd^z#P5K0bh H61T*Gkg<(Z!1qT.Jz>>ϧˀ zpHHI`pu xd'jTBoDZwʱVLMm:~a//H459Rvl)М$iKJM,=Uqӯ{3 4Ē8%ˠ'NH\iFoO:+' 7>(3G2} ro,q7雏\N^kG8΁}_p\J2ã= ߐԙ ko.$*5 QO veQͳ|96@a;='`oūm'$f9RHo-qUI/G-n+)¬;žSk[㰥&s2O==2 v8}Ui.G6|ߛS8#ymbVKUVyZ#.z3l )~޲A jnŪSjs zŕK!ihHQ Ru.=q2;rk1Y\nj5[@L;emEOka=\wE\L\c:g_5O:vi!tł9{N3϶y/WWڋu >|px* wpE݆r+Rn֟H7=dLg/TL F2Alv< JLdutXz gLdEE+ iOhR&Ç d^QVPP(|J%FNSu~K5̙Tp,en:84VVܰ-r5;WPO*[Jn'YK\F,PQ3t{-+ vE@η*\/wjȝqv)` f(9/ViE[+Vw%4XNtWn=˲il=METJ3>mRk_߁y+:+b Qmy|@(7or lpm yB?HieiN+N~v-z0fh nc̩W[-\=P[o_'kyKE(R4/]`^QmUi3O1NVdkSK+dĢNisz9^z5K5k^֛ީoOx x# ` 疠mlw<0pMCmh ^ܠpx{wr PPHMRgDTJIּ@@h[ojO}`&gF8 7*0UotMgRa))XĴss|G\$ &g./h 5al6 ;'ϧXLX3*)Ҟ.MpyUs o]y.1"ę<Ƹr~aNh} b,䄄d:`0 FrVzH 3KK}""RU*K\\ YdÇ3` **,Fg~.w䴃P`m'|ln(T+sMHH%{pdTMec=D'=绠JE)I6&yKaŝȰʋlq66ܹtaځ/MNj=|2:7Š=Ν'xEsˏ_^u Ѩ@yuIͭ{Hk˯WZd" mI".X 3 ^dG7?5V/3*5@ï:T6*i'/pEĹ;H-)xv{{ޯ2:Go|{I}{Բs-J!kr@|MCt9EiPj /:&Ei*<3dewVhR kZk^"a˼0ȕXs n"P>^?;ﺰ㍄Z ӟӓHWzی[i&;{Ͳ Vo_1?ӹl6Fxmo :2R/BKwfƱ)jG _ݥƱct"4W}Lm ڦ h%n\z||rIOBBOIMJ1vz]AϿdXÈ#Ė3tL{jhp!I]Љm\7\b1ŧcf<"ןb'3s+}>t'@gy+7-=IǒBNJV8v]39 ~jt: a0غ" b13ΦM)d2á^~#˥{{:jT UIW6]E" m!]&텱ؕt 4n:L8Y F frV混 nɶuqPw~B7=_D9-+]HF=;Y鯫甮+%Z(cWz7^v|mG7^s`M,~KcqH3ס/Škq>KZW-}\ "Z;bs yUS^~H$FiP7Ū,S,C]}")|>By,<\.ڛȤ9StV j]:1.a/lOE[6h *NU A J ^ jYI߇wx{M=g-ۿJDƶ-'=VHLd!l UL`p,pN'H 49!!`$?"#H.!lYF>w6drmffXa2G@"ҫ5tA_WW ;(.4pfldx Q!-տ;Yz߮w>:ҧį Y{(0l_+uKg|9ʼ{]ӊ&YN^+wX͸[g, TQ3?V ULJ݃CC4-i#ش% ( }dg26w^= tvN]ki5gr݈EtOMdom'twzV+jPM%A hlDw&$\MQDTZ8%Z4e c0aPr͐~&dvPϋ:ʵRD^[27E/ϷF|9'={f.ص+Сtzx CONHTzRRB~|2'Ru. +m6>RzT/;da kPZC'{ٚw>2 ;$*9t:WwA7WOO4.4~LrDhu׋ )FZ?vN7H<`Π'(Sf ntՃ+m1G˂:w CE|{8K %D&HL:bdTTѮ]ocmH)T8JGwt:VHJxu=ݽI@I]3Cw /QW,_G§Je)\t\ _Y֋iUa~*~2Aȍ ӳhtWXL6sa&?R~%4op!T'9l/ Ӯz|>&`uu#"քV q9ЫTf3g%k2DjB:ͪSۄU >pXיh3۔!_~։zV*- "@2#ٴ!!М+Ǘ,%w:ZB3z" LġFGOeZ3ll$X10.*Шa%FB=@5}a u퍡͟uMsp͗N۾&DO:)_d<;򶅧k7)1q2Y%4) A0ok"ItǁՂۨFu|O;DX8w?rrB^chhO}n*v`ϴo`y 5˫Gf3,<kYp$)jB ]`d]{ Œ9 b}n~iee&%%˿<̳xМxH.)2" \lðB=,V"nތ9 I2'1f36nP(pQQ󑅦FRhu \.%;{\@Q#!\eH4V{F?>$=YV8fpQ̠TMlR~m'OՄYs>r=Nݏͭ6 Q׉b+9@6< #n]){ZADLe5PƳ>{9D狏\075T[+Z{Ww_ErOفZGAKWALޅS{qk>"S+v6BZ♿rP۔ky]3aÚȺ$j&AX{u]ۮIֵw*ME$!PL( -tB d~5/? ><>qrg9s)t}ajvMjPW}Trf^rf 1G1ePϚ`bԪTju[֙VYq]ud;{[P8';PgUS&:jT.kC Se;ݹXP=pGQ}OY7H8ݸa464U}4lfX[i99r #Ll8+t!hjjN?Rn+y!%2B1r&g=11ҏKH 7`TvKg2l>0U`D憞ulGrAi^[=*'4 _u}?'؛=O*d2S>0?%Ū:^ * *:ׂG o#G!DFTHidZu|{9фRx|NJGdd8  o(kҹlU_7R985<_-pJv, jvr"dlf<ѶM ?tuʈ?Sά̩0wUˮJ_;ʀYUUS5n tZRb6ע :߶1j(-:>qtBv݌XVĎ@@ʌii"K20,|JiL`b!٠2+Y1EFurrN[ALA9 {Gպi&\ 7dSfl6.&( ╛=ZcaL,aoe>d0nq)_ @SiwA07j8H/|c3" h72,#@,##$[O`>o7}bOcK5o`1zŋrr_ͭϷX>r|N,iM))5Hid"ҏO_|H?~uF?Zʊ t.@%KRg־0g6hVx{=d2CC7oicʅvX#~E/59+;R f g nYd6ĞPAf2IMkޣ@TEi L@1࿐/ 3p! Ix`:jdV@BlLH.?\zDꐐK>>L(ÿOwa2Io}4dGHagWA7=;qzߕ#H+-[c[ȡ$z<&tۍd?\.̐rĤө4JQty2+ma n Bݓ $k$e-cQ4o~~ٗ.r4*62%et4,N:e?ֹrH&UQiRwX5ތJ}-`Z"cBΊL2Q]cyJ8Z=/;+4v_{kقRq\]U̪{K~,WѨ"H|@WoKzD+rg-;`xdL9agpX[Xԡ){@3y̪*GȴڱztXyWϞڟ&&My1]%Am柁[r`߳s}{4vZ[:ctEWN ,:Q?JG1/1A^ʐn]R5*wڼK72@׶_*42ha|##631hNҍ8/3ۊo vW,|}V̺c 0qXEP mJYG #oѵWuHrRR[Qoɼ,)?hg ( nϷ*."8xzN{ $mXzز) 甾gLH[ 4&`Jٔ~Qτ{{r N!z=e{.ŖMݓqwsYua@e+o4%| Α`苴iՉ IR*0Gs<$KhTfDDsl}z$P@fvߗ.g'6-EH&O|&Q!1&F3jtu9E-ӹdο'K&3Tߌ_{JRj-o .6ƅwF ƊtZhq9liFQ='#,6P:>Fd/!;:;$T/Nc%xwS9s]c4ܡ;5o|JrQUeL}#@d p2XAGOt!]Ƃ`Fh~{s DFҡV u""]p3Z-l㼆kK;磟1_Vf9]I(\WXi61ۯ)+51Dx@6V*Ŧ W_}ƾs.ɺZ'u_/&pKx5} b[fOEEz|Cx}ճ͂mhJcgɯQ<0Lx<+*Y))ZZVV$ %**| ]_ͤ?{9zO %[Uٗb [$"7A| ?k(|UN?L&رlhZ$IU L#c7$@5SQ͚Fb'8"h\gQ T[gm"^b2Y.hy7b2J#X& ŵC."@A;j'60">IpOb0H$1gdSTڱcG [7_%wd}}{2@_fwwwo05HRPV~Ћ8 i4:CSt`H(0d'L7=EkCg^&j*y VWS5ej7loCde"%?ٽ kmb:vWt$6_%Fa= :8(dnR,Xu(JѨN{ N_bӤD^P>7.zg,.j R,b u k\,EU),[Nu܎\7{@#SְX 9a o 8Ӷ7=.~qL`hʎEXLV!%AoŲ2 oy}T4؇kdlO<)d_, GC| D>ԤN(7yu?.gA0~> S?F- )uw,^|QAٽd7.Fe1 f9!{~^0b)W{P._Wis'aUgn_ެ5ܮs^s#JVgjޭa2#LX캳K8'e5K-8t*<XC0N('6Db''GI?x)VaaQ}b(Is0>kCFҊjt60 G}F$?OFF1SkG{~Ѣ]]?^ '%MQ1@.z,tPLvv7 bsle keva8[ z+d;ljbtK~[1:V@*Tp2s}U7Py1^k[Fntޘoer3> ʜ8]\ѺkBnd<8ޭ n P4γVm;ѵj*SI=A:Pu ԜI=:\WnQ7O,\xOOG36&xM-AS"%b%נEv(.il?Ky>}&}z VchLSҰcw7nau ,oTEBI"1 4ER%%Ys,)92MMϗ=G$Cz AKICLE^Fid庍z/.qQoo6/kp % q8|BQVó,[ѾA`Tk=g{1Lbم-jN ?_Y!8t:U@*լ]{<;T3Cqذ6:(:37-@0Yztǯ,-l5;`$k_|kFX,KPt* -ą2 L&dK1D[* 5 $mV^'I0 _H%X$ %jdG2EUa0 ;@VG3N }wEEMbMY6.} '[O޺[Λ+@% +:v^{/zmK簷({k›_*?bqy.hu}FIfUj*2sP*d bUI6}[?8f@zqXm{>㶘7 F;J{Ưϡk4)BGLg$[P_fFb=E{up[xۅ6^rwM87Ŧ 5m{iy_mj>y$)WԠ2XDfse7ߓxo>mr) SH\d:( 'G4sDpv~Qku&Nk Ւbe.mhr;QPk%idaz( ؆$~#!jJX$:f#WUQ~=+Ҁ5d̘rrȱdXv;gFck9\>wֳfxX"o}(hu '^qp攲^x&|LXI+jꆻznp) JPn8\zϘC !P2CE$0.i pLc >`I!GF|0A"[" ;{[f}aOcW,%_:p>z*F#h:w:۶Qik6i5I]lz`C|rM-Ã.}," l9-zdEwCS^ft(*睙p h#_>p:@\0SP̥o3@lz2f #3Y[f)_$-ȀFF˕+/ rd]r1?)Oqn}S_WN?:(*#0rv1infv>" R#2'{m8xP2 \WPtHI~,tX|t;A@+edznH]-^԰7kc[eBk&ĂMp8"6&UAOH|낹\ :2$" - ʼnH` A#), AH6aD}]:5V:b{ddKLk}3'H3W9羓vRQ^NuD9scZ"*b+fail^A+ uv3PзK,ġ}tY S9 M~e#WQW׏wר䝌8w|ypZ/@I^rz4gfpZu95Vv!Q?Fu[igݽtP彾Em2+LmWA;y.%&OkWai:17ubYڠلDa? P(V%"?SRse}E]t<$>Qm4, ԏk Msgj!fsM7l;Ϳ `.'/̙ D"7**lt_;Ǐopmm* >^SJ~9/љFMAO'> 1:)gcR٪O٪/fdFlI#4Ws0KO -)Yrrr>42J@t*ab2}"& kǹ?|u_t8u<U;$ Zr4_FGS4slX|\ WN\Z eUzr266jJtttiVØwMtDb:7a}Ĉ%ov^<`] K6&*Co&6NF#|Ν c\tx"13CCA/KEEGrD! ːI$&\- r>I:QhtqHII#:+~H,3?\ћCXonc8wo]ƽ܉%욃Kz* xUgԲϼ:O(Eٳk;3:³JLqFGW9%XQS=!/zEYȺwSoMPV70EY CqJ5sTX &ȝheUƼl]e-Zop蛷[*ܬd#8mcfЂ1(kHhn*S--4$gs;mi[Ky1P#Ϝkj*ar-媍*XϐyIC ͹8 j nx3a@K1h|-OH0 T_gee'.4];;ZDb!'!%R˗CG"{M 2-ŧx G"h4*ERGF>y;ZXH's|XN̛tzW"rfvVE豵L#aL&Bl9rfY$a`b % zTՉ@~0W,EGSF ]0?pD:zœ1A]uL41 8  IDAT0;w_޹w>Vˋ[Vp⩒IvlW;u޼wbL.=Nv#揈 v5{.L}Snjxde2+Vhw&5 ű-^~&3T# :Bn'd5ujmdbl]`EUi⌦&uOԌ7.\ިZ'YI okdY{8e=^ҵv\ٯA,x]rA?ĶwH7~M_+ND29o|Euص*+4L_Lsgݎ6^rnqL`HiqMMJ /gjj˖]NgwM&3{|}cp8~gZ'̃Q)eHi'أGF7|!9ԗ/I% K?=藆)t EiIGM;.$%„ w*p¹\bttkivez: Zζd>] 2.,-i", "%'N4FA4LMaK8qrcMt+Vgt5<1XUOg,L&i8jggDLƭ+ez=˺0;57v,NhPr. Q;I bBd䀇%^L)LhH,HD %ijb![+1WlE*xbxxy\`Wc(첵S/6F%cO/*I,ng5*6:[&lxn?'f?~RӖ<7qք`:gM)|n5s_nx7A>Z!zLΣ"l:yV+ Vdh6x̣ CڹԄ 5WwwNnESqQ]Mcsuhc֠֗Q[ g7j5]R͞&,f(t:LKDlkwu-qge|Ys1WPWHi؀ԶSIWr~GK?/1z>q$SZҀFi2Kb|$ujF &'0/zGFhy#Eu{U,(/[i`o td<.-LcS9x-goT2&tJ,y l[-^4ƶ.%c"`;r ´ĉ5 ++N]`0[k~M_7TkA4~^^8YǎɈ0֯oRlbϺN+9~j!T^Pb^!jNn;v`CșG=͸fE`mzn">UfIWTu;pPGT++ThO֡y~z'K >yrKAҥl H~Kg|4>-*z)d۾)y&e~dS]FNbi}4~>ǏPH()b2I{{~)jiOri+(Rz_Nvg#.urʙ7^ȧ0 XH (r馗sq?:s.Z<@ EAc9&`ix`B!/J;BQLf0>L&C2,iCB3$|(G8`T&iZQ_=V|Ď"~m :ZT\m ?FT߄z*y:ٸrDsGg޸c3@|d.ptRhU*Y-Zن h$ۼ8v$(Zb\by6wQѰdIlSUsQK*K-]$T*=<<2,,Db&'o?jgOg~Eʙ_K_F?O$hw+)(`y)%r?·B?BK?KrJ6P0/i" FtT :eKœjxJX&/lju=' KY+}I$fA!2?:J"1!;x{<\nt:Zʅ/3CkbBd/Q/=b6.>ډ%5U$SE1APf̾yx8)E&EIFJ4k@Fv rNѱ,Sr*%{R-ןsj9V } {]ȳܷظ ߹~9NS+6 s`ɸI]奋pi{6#2}VٺWl=:A_2#dMNsYWYp ~ ù%ż-N.(6žCC`xUUW:`0l_l$ 7uWX C[ҌF2M /~dnE^YninYS\ˣ}NK6i)"u>OϮgX7WoV?xX!lA}xxf_ h)6<7" 8X`1WV_]Tʙ_="rد^/M4< QJggF.)\%% }B H 9SBEC.vT=NCEQ+ &<04ȟi?O䲦b:4JL>q9_th5? Ď@0Br2ƽ zO\Dbbf J3ƙ)%͋ͺ'@vG\}03K7LhV"Nt2!5]ߤmtu*.ͱ]9^Eqkv7Ys[E1*/I5ә3^Db9:xrrriiގJA=Z󋏎pjDS2m3%FUX4b&>nMkHN }9b.oJho%[u:|+Rι(ѽP^qT^oQ5\ͪJzf3*JkT[K^4D EGSXd(L 4Z];h [03!n΋^^̉NZwM%:US 9i/ <:WY0; \1Iͭ 0O-"V0d8:0 9KJvܱ*Fkq]\<Y L*~=VrhNÀ^ 9QWC}`bKnUƪl+J_!} \=_<7tO/ru6wN-|PNFpC\]{s_ [tIٵ %K~ 9V\Gb$@孮ޱgx _mg Zj ڊ/~3@CԼ v)mRg53^Vw8NˮBee>ƁrX&d!wnUZ`0~{*ULHTvT0MA&Gϫ=$ ͩwXm{,If4%RU6W{q-ʩWS!8VUlO)h<^fꎉSxZZ݃VVZZZ=TggW7UA1ՙ >W*,BJNKW*G0)|Px _L4P)#ו#YKSJR[A>rvq8\N#%!b}BosF^oa*STl4`²~ɲ!܏g\'NTV"5alq jK NJ˲˩D53_Oj +4zr0 ewhq)/ U^aQz:bIp0ZL8\@5a4c^{u!r2<< ㄋ)u;Pt~i\.FwzyUǩn{ bS.ع?;pΑen)v#wQ;+=;t2CN|H4퉟[-m rj1;;u%ٷ'fF .eD4miA{28ƙG5uY^.SW=4cGJ^a ln6(0z"1 Oo_L?*T9I]\oԼP_ F'WpY_3m4Ξ2z c :>A ѯ㺴{;U&>i,}_򠎖Q=qV999x<^`m۵kH׊!TVP(Q $'f?Ա}I kd/)gΐ4MJ6r4pM~vڪ| #aG-, Wbj\廡zҗ"f~v5_[vt́~Uxx$m!nYų٥$a@U%sϜӫ'Җm ~o4֙VEvPKiũa F,KǡP>qR u.6!DbA2AjxCg=HqRQx\ao_{6hRs;q L!!KhnJѡ7ӯPn7eT8b m %ȧRGQO ) 4#K!t\^)2K[-a6C@2x5èUTS:Ϯ/~mx֙[l {D^kMwpNOe#9IY2Z/UAI;]|_E\3~usQq^o(ZZf1=z>L', b,|u(~esz-K~ծ IMEFx7t[1}N&U>0Qhj5dA;{!=7W,el'8Ӻɸ6h,UI*^5vmpQlJ@Q RT\5맖l=6G W"0]C.[whc6H,gm['$Q(lMkkom(Ф?++@q̑ ~_|SK|w&g1閏&V!ݟ;ⱣG%2~|5N%}8I._,̣ pfNlDs84 K濜://@嶋KoaVŀSTb'lg9wb%S63i4*lEU.$ 58sjV'1w3a҉vU"A"1c zF@`ᤤmxC' o`+tlٔs`2I(|$U~RR6I;G8jt}vZՒL&IוoZq$*8׬1m^;kv瞌N]Ӥ̝Ɵ|}۹<Ǭl()\W6bL(k}-[&1gH{Q@ۧN>4BvVNkt`ɢCZ}yYr̐s-#//~l))$'0 &(h /ĝ=qp˫k_zޣ@%~ꁿb= D 9ϧɌ6 /ZzWh+*Kʧ6.׮@jl}0F#iPsa:`== b8t{KzLRxxdjьGwf>Ԗk1'y-L~Ji,pcW^&9~=b,]k(>IU!6iN;hѺeݳ$;G:: =ql7+sBΜ'b4_sYl>x7k%/ dM 4G+߲* HʡN,q: eJ*>tlp]3ٮa9U迎2˟Rrr=J"~7U<ҷ@d:n{:05e_VhhLpq6 C[C9vNtt1/rFw#w5)]%aχ3lG;vuo#$-wUPh\4ZFq0XQX|ėo b'R^_K64OGb#L/IZxHnSAC'Vնp&=J{~5L&(⊽AJxzL>I"&Ԣ,p` _$9I%JRb-,`/˖гjst/$7z#4~RCcC꺅r(v\ dl\d@Br>햊!y1E \F6dx@ mYkԛ-d*|OJ=<_"ꌍx$ PeGd+H汱  O79ILu?[FbFXx:7qpun┬,8/OL\CE!+Jvq\:xl_y)CW!tBChu*lE^O:b^% `~Jb !2E_mQr3}ޱȰ/;v֛KmZVp=RjeJ= ͌CEKq7* xmjӂq0ƇтSW.? }70}ɦԚϵ{R^jW JNkHA@W)j}ThSnuL-_gR)L`o]@UB[4Pq NMj0JE]酾YRj*>-M_P=W%:Z3kߞ5jh\o.o2ы@{!2cc@")NN)wee 8H-))NDCri?5K; IN>85~Cf2;wk ,5))9mSQP(H_X,"^b\QB&:j$!RUZ͖LFzțaYW*3T\a@gXpb#-}h9jm[ qs*uTDc ʱoe9YzrD_ rצuۉFZSu7'J_tsDmsxie fxܱz%c5\w7L..6Pjs:"Vݧ9HD4%iv,ή }}EJO*P11>-`]@te岿qBg3U?VP`oedPv*[is$GYt4LO5TPpS psmuS֡sWzotTy4PT(t/BKW^|X4C$1}&c&ٻSy-<'W;wXv4D7SĢ_6G>RT5n㞞l=,.j( ǻ<FṀH"qi(+ƶ=J'.}g1AUJ5,<|caRRjËO~9k <>+Y £$W }8H8OGy'ce>-էO>xۛzgR5Ǹ`0.6PXwKV5QE\MwYzff\Wus~7.l\b5V%ްvݞeQmd0xgugci!&.ݱZl\ԍ tS׾͵= 62:o)hc'0_.0c?8XN T-GǐG'|s}}c}|bJJ\Ң=ڴo_FCA(i[gmG#SXV،¯uZkv>wnf~4Ib4TPnM͹I ^r2z/)oxdl:TT(ȫ GZy$D ۧAtv"_'ֺ'}EQΞ͍]|"Bn VOnopG8d Jt U0tīIeu+jwF%^>J%ʵ94\7t˯cbܖ&LfW2ĖZXIխ/Ȧ}eg7(MWdK:2sRTyRKj=8௜HK1:"c 49>( ԾfhY^jd٩"~Ą<(vw+IR~>x EfA"iiI>>1E_+00z5! ̬_Lz3HN??<8Okf]; wqz8a~f8s:i7*B1*RQ.I%=xr!Q=:Wj[ b xT"6G*x?}=i=[[()qbtHd FVrd:ф-7}O@$T$;8ǂ>෧?RmD61xd_Fq\དcr`n zϿr3;IRRQGRHw DkWjyJ Sn㼩ոԏ|J~[kvm쐚9wf ;ȂT-+6%:C36}Q<+N;0Ư[3[֚Hg5;CDoVČ@@϶9c3UEi1:/cOu5(SB7@B.T3jt,M5~}rhdrZS%Y饩ٯeu9[P9ԽqމwX2N~#gޖՊC&LL,FvII=`r$ڬ . $Ɠ,ݽ^>ŀ0_}uI# [{u`tInIH /zaCü'>^qƓ\0͇W^^td$İ`K;W{w;]~j7`gIU2 v&ӌ1:q|o7}[<bؤ*Y%S{ H.>%ou5Y]VZQGGǐ}IIѾP*kh:ɡMa-)G" g2ƻP#RA*`TYw7Oy͊'vI~AnG`u~)YгTס!X9-T6UуoV*8+$rn#)Pnon*bjufKoi7e޽Ea6͸FCZAt <5)iU6[B:3t567f:obd?PGFlU:7Ш`4yӳ?j-$<3^Xn⯫z}n7SFzA\|  9ty|wjm|a\o"4ez nqYەpɛSgX7 _1Xw~姩\ãR$x1OSb?])ZT&`ijݎO \n9΄F7]1(eA@M(kjgfPRRddaJĿȰ0/Vu7BS_)`j'*:>8~WUѧ1BsZs[GT*YЫ+vh4n ѤʓV9֛ ~Hѱ];ӟ'(kUmf iL6d5V "АPzT EPxY]B+ZF"!YD.z?4dSPyCL=O@QZD'E;a6|qˇpb"/N m[+{Fl ^&#gh(Kf.C#p6w{i"#"= =xxðw% tHT?hS=ʤl-^U|R KU%j\} y}=*_ILv4>w;dqY4t`fyH:K Rs6E A@@vuh_N"`09nedsZ4sTiW6%b?yeo (I8m}YήϜlCJKN{tY--jH#@hSY9((n#o%2>XAXKFڈJ&,@ִuQ)}\c w]UWiVNI8NAu"~fxt=+kXy)XQ7;%.:]KLڳ f[Mq @ h+`;wz vc 3Vo}m˓ݽL0,CS]?ni=>RED=u]7**1=@ɹ% /yF:$]:dCk=qt.m;p.‡60< tvUsh'SAa^:cRI0>*; u+50w)%Qp,%WhrB7~XM{ro@Wao D[@z[\&un#))6:BAGK;ڍi~M6#VgmoRХ;Ϧ_5C䞘Y;-g MߏDGǂk#KK!GI R(H<UenmnwspobYWM>{k~=r6{Wh` cWv%҉-[H(ftہR˾CYw)C\QAee}|b!uU0:@ 1|Q9:Rڬ*U8ֿ_~E k/p:~_A,R5!+ܞ+f]D9qeճq|iKA„2O |*\HVS|dMؖ[ j SY[cq[GYӛ*j.~&0Nm^ޘA)|Z.J}q8'a揶X/+49o _&LJd>Zвd`>Yow|{@Wq ZZ_k1cTF?1ҸEeFwٝ]|]"y%ܼC!I|Ť*M:BSKqA>nJM%H *IHЍRR saat cc̒?`{(6O6%Ya6|f98| @ #}D dKr:KYPb+$N?ըÒ_ݦ:]j~}y:l#Tvjc-.]DiP[,H N v `-ʝ˲P@x HN.C};Q^ZAsnzNƦl^;l!tuMb.uԌ ]s,}+O+mm bwmV?ߛ@^'xZ+=ЂܹCΣ 4q˅_p+G1J;W_ٻU gVt],& Y1Uxǫ>`\kүIzSZ;NLx% t5 |f ݺǃ 9aAaPéMơ{Q=Ž_^}v3*!.Bs{cS̝m&t]7B1^97< AA^~'V#dBQ||=\'&6WEЀP|||G@R!~~T6waaKwg}Uߵ_4l IDAT#?dy[M@ܻSk#qS ,CDRG\i_x̶舔O фN}n5BɏqhgPt]?Mr\j$:UV#L‡7Wsjw)EM|YF݆m"’8<96фZYCvWht֏>(ԯ|\+7.*gү뢒nt92}+αcʊ~ݺu%.y̹MGo`7e#opKU/ =lL8[nqSdы>u<ݍP 2TJF۪>{Xhu͌Crq"22iWT&yy^mx+Z-ڗ)TSC/L+31_D(9<ŭ6(*h)5[UXdekK ЋЯERf _Nv3 6x^ȵ@͟OR}(v eXڠcAH#LF;L},ق2،fOŸqfP:8e_iG,pS9pO{6:olfN BAB{S RLZ[iOϩgq.x pr"VlXa&8{\_`nb9J*F3DujNE5u@ {?؞22zɧv8Y_P9g%X+G,B-pIB勉[bxnb5F\"1 _G1y \qUvg6{ehՍ6 -6:vq8b]oq,vBczAOx#M* EGH4SHyqO`iy韪?:*9yX[PMR>_tE] k) w;(985Mz{$ʄ.%jo޴:'SV즧 q ?KCh<҇)VA%S;NxM]%.HKXe *[郴;Wxj>p~%eke )0O 2?/it'7^((uZQ%@`ͫ(舊}0Xuu;ٹg_Bp0[1nX|cd5L?k@CԴ*eQQL8AS7ÌY@ p}CyBش]}]NzvT8r9 0fe}}2XMCb}I|嘡r>O.z`bS35Fu%Q|~%J6n߸sk2Ŀ^JU[Y%$R%>{y޹JJvVTfQŸ(FL~uzX,.8G1kDuT* \t*U+- rdDZ9dP?R߉qMQL%Oo3ywiJͮͺi_Zr"G1NuO)|# P>>,"YI/?s[.Nzk_x^\ÞF=]OC\n|W9 ^.mQO/lmQ9uc:=1vunꯢ., Vb*O [,:,'.L[}EM&#&]׀Pxx?z2Rc9:TGxߧJ:I7+l͕!!&l.kbT"VT,fxC:j5ou Dmޥ-qޱ3BV8hEl읛ղF-AK|w}Go @e>o=zM:1~3&YMpN]|8Y=/Tw .)Xv Qr26ħ(EoF 8t\9׾ҋBr=j(r6&꼀5<qJg6і$4Kzڎ}UD?)^~zLܯ>ݳu|Ȉ CUTOKp8 ,[@ ҩTYmA}hiR$B oN`;%9Z>6G{6/شl;`Z^6P19*rO(5NX11>PT"^jBB.AGB^ E+rSO.Y =J# {_pn֕wv=p@GG* T[!G'WL >wN^M/} sg{7ůt|s(SP(9s;{Lb}웟EYp,$#wN0ҥ@ uō-"Eb_ts\Ke;9_IyACtwC.q{GJ<4teCsu?Һ cN#ݞUжxTLZ@uijFV-NwX'/JH%b6hA~oMg.'ڻȁq7E -F=((5{oqYlesOO6$tk)!=mb1A U-]_ڢcO.9+m󎃀!shtF$xH;7$/i#u-[w- QbUFL=@yjgU^G=[Q/-gȺ6986ɳUT6P?_\69jZ׵-" ۫vFMgEK^ө5`I*=1G mNFE)nnDUǃ=;z.bdVkH30H jBJY2S۟8b|8qv)0#pO];=oEFFƘk l93^,OB0+'ߊٔd{Td岴؜ 864P(hTpX~~I @PCYS]x "c]\|ʹ |uݏu6 FN<#sOfXyį܀ ח^l A ;0iFGM 6׭6\V?YW[^zH^n@@A1Aڊ+?][jm/,Ӄ?|m{ ~-V\%)'5M1z<~{hrqCFa1GJH"1D IYD亼LVIF2ܖ 5Awn4c>ګuqj. B]LgAKZf_U}!F]ȢHE,B,W_-6eƇpq>5ʓ.N+,gKT[J372.ˬCKOOmZ1MHHI$㰰DE t c"$]sMfAEq/<7?SyQͥ}6 0ey L8w-An$\?vlb$5˛yyӬ'԰͂8~o˦Z0M;7@@'&n߱;}}c!*&$(9&# 1H #MmYܳ?d^vyx%ʻƀCk "^YyZK]0kk2b.\?opi.x3iúW"w{&Xs8t>~eZױйYYBss 19o{,zϳ8X~;DLeq^,囨)O0\~_\t:ob6W|P.! ѐUߓ#'YnMQ}^\#c12\t5$Lƕ.l@ #^p+O&}jZv1RMVo]qQ b/c0x9b'Jj0l|4>~Vפ(If@^蚷-E]0eT~{te6u)CI[k-.zI\& ,nsv$&n9g@o # d(SrZ>-}}R a0⟝a޲pD7nS_.?[C5o0b>-EL+wmN1>Gja;~S~+V}4?@Pff CNЀz|`7dqKd [tLθ#]_1.[?c^ ع}˻yz/36JhUml5Topg6)yAkZ%$,~Ҋ /7Rޥ2I鵤i#)/S(HID)&yG/G};W$fĖBs#>].>r;$뚐c)]5 Lc^ J)mB \,*ySAZUMdR7R .FƝ?lolЛPw7YFV-bqX,.ef;VSm벰T}3#/J۷$Y\mf4s[*5Od]Z5)W;45>NkU r+@AVi7/\hoW}`4w 4N*0V:,nhbR QKR qQC*F"HqJ-PoI\`y]a qfib_Ls ?z6;*0yh߈Sse?΀!CAP EEh鐡b١:#qס.[;Wp 7JbɈWKxS}+N捾+80P _ M<Zx@V\%f^apED@I̭kur/MW\FprSxi~ xl1Ga?7\3$pH{a6'MDF3)4$bͫxo}M9IC*f}klū/ [" <2_ ޕ[FRKNjG۷= *,3:J.!6[ykn xU]fum֘U[`0^!RC"T_D= !سA=nhOL$zN~GJwy{3#I4:g61yV#.K[PhAژ'6W_H>[]_*NzwpUTeJW:{&A YAY$.9)&?pE")DulO,OFӊ_Р#%%-!!1L!VF*r:83NBDoκ=e6{ǞzQc0;Q8ciif]]]1oXN~PT hs%v'=X.xaGH(Ӏ-GGhq`\\^7tI/nOg}n_D6 ޹*F'樮isbotsoKr~뀝/ YR#bqZ1YR-fč5 1aX+$=op5yNAe7v ޤoҲQ{P XsOh -AYl¯%\QS#+ojWND2>}^`}c_CJ;gpf˯܂ah4A_]H z8LطoddDA-Cl[ffihffi8:8h]³nS L@ GS?.X `6oR(T~M?8 ;}`btWA}}c.\jg׫@$P\%Ey8͑ئEJ H]$g&\@񲚎q.TvrLJM_% G6A/R[{1tÆ״Fp;pqڈ!Mtpv&~95EelЈ<+44PԜю]Y5MuנǏj*{s@hNloSkm*E>5?a:0lIA+i刼Κ"HLl뱱F3J|HUzpnc=ޭ\T+sƭzyEPu١@.Ct@ﶝttqnIiH^X:q 1_faE *sj1G&.'cxuw2Rhe5G.J)KK\\8I3#=\tg8TkdCwťS҃I&o='X2 z,̂HZ(&5_z3 eK'E?^|9*Zs̮VPaRXi)ϳQq~ \W@iXl04SY)$XKK[4nzښAGEȿir;ōho 66~39Q]g TIv9MMȸrtP3, L8$RVYYN}({"k> 掓 u]?{pC~퍸:s\;i};9><00 PT(x?^+',u-x4-\%d.p_xzUvZ2T.A*dO~RڢٚcbQ:\ٸ/qߵ-EBע_{W$I%7 Bܱ$ "4 Em-UnЖE5"4hIjdƒ 1-AǑr{gbR>99= A?`禺;_v;KyyIzg x·i˾Z>ɣO~VZ~ignԹtѓԧΛL&|Ivso{iV>MEpl2nEWwh٘b2ZYX^gyAo~A-wWz_n kԯ~ɂ.)d?Ԙ>Ids̙mWc~ݺݛ>)Ȟ`k>b4>l+czF? x04]{O󎍎ҽ7RĜ엳gӧ71y9zAP˄T #7o\zQW;}؞oaō 4>(8"f~g}dޣD"`SEH@l@p:0 >3F(͍ݱ#JFEQ霢4ef& 6kEKJ"5B;-BChIʈѴ.ьoY^C rjy/V5ݪȁQ4C;塽+v,~x}SSS/Ͳ1W{qީ~{+]k*^`H\֥焍烔v~Yfa =[{2٫{Ejo{xv''+cGu?+Y~QתJ i̢Ek tŚWt |fڴCg:=n`^Pi1@bXD+tGDDiGVy5P~}w8!ѩ/^unp.r+mԊߛܪ[^;t'l ޷k7hvmRDD)IsGѴ&"40ѣTR^iv-5 t V("3 L83I hOPxI $p  ʞgznW&bHr"잋n },}[ՂN'8qu %>PXYb]TiZѭ˔J5è 6jӊwnwrUs_or稒ΣW;C޾yEM/xo oڸa޷O]Եӕ/n?oN8t~6%µkQOt*{S\Wvc+/Dť ,Kǘ'k ͛z~Hejؑ&;rQ7 SF׺e,}c  }NTtlP0*R`A)@ 2 2&8mADzZd:Q 80c45Q8ktb^pج0'&<U`fi{"m,̃.[7i{6K" Ӵ &JXXKQ?S O+*1c9h~޼We7m;|1wʋr}@׶-׿q/_/l_lKE*/ti??k~{|5U[1~ w~n۶lx/V_?}Mk1_=ģ۾_3lМgi^_ɕ6cnm([vOR^ˏ^9#+_z܆M۞ayZ,{sf:I܀~qq˛VOVx#Z/!A ~36o]G|A'.>g+}sIHQ,Sj62&>pač> }Ч7߽3F]wx?}n\Q=n(J!z=դ;?/;B[TUt~cǮܱ#5^VD&*s^r; :&+0͛Bxn7M jI|/nsNr =f{=ędAfu& KqMO.7дaT 8^f"=?)!^kqz73.x{Aln3upau/j4/ rӤC~Zιч*zvJ֘8"\9)O\]{ǞG[ovY%tT_/.N[ r-<5AO]RP*hM/1x9RT&&-9,:wqJ}rd T|B?Rˇ^ v¯:;rtchhUMUlᇂC~9e#QwFv[,h^q S|j44o tu" A!BjMKYhe!+Zcq~M \h` ,VEi6)2 !Pؙ#VNBVm5.Zudɫ$tO;vgRT_܎G$nys W еCJ*k7v6kwr%nyR#kei/oU/%.6{#FK"wa) th#kOӜJn E7d^t|Ǵ_}:|Btp}OR_>>I,0 }]Kl^2#S4rr/u8C))2SQDd$9~#8)?W#""BgY9"kMDŢ 'cVEx6MS1yAK:FmBH?ijM }|@BNTl=tUsVEZ6 D\(IPvEJ:dޣ4kqvW?O}l/';.^HGMǭ?S^ڰSDW CTVF _џz}x)"/+2űԷIU.7UxDE2G/2L^9>3nK~1Ķ=1ɼ# öy."p+V Цֽw?ݰ7%$(=˪RR2 yr\<7q⺔ Lztj"N7Ɯd|PZhLeO]DVH$ IE*aリChDֺxA4-oTȅFқlS1׼Tf'|2MMpꇵ?vI 2E.$8܅Cمgh"P&~!jC'+?zM7-.{A tga>:7V/+׿VCF%Pyn6٣Srb^/yu̶WTۅ׬iG$=rً?n[m⟣Z.8>Kt#>M4 h-[H?f\;|ӭ'ov^-MJ.w?&wcz=TӦEj2j͚dA0 ڃ&8R0MaP<!?_z!"<RN+ւ@9+8 "ɉo LE_FT 9RT*0w#˾䩨6k4/8'e6ݽݵ뽑oq3[0pĪ9̆vnFV0:uJyAIˢ4??հ;U(6oX9o<O-}[ƏƞlX]SYpyBي[+?K=pcz>S.T!E.jݦ-tj1- ~dLfK 9)3I.26jYӧ@7иxjBFCi#j=Uo{7mYYŽs?@{2@, O8y0g7L_ (<599o W~"~PTP˂-455)*aѲ ! ] ;xzV(ϾuFeւM99~$ l*E)*O)/evpjRݬeܶ8]ы&w$(4'+ڜ+hud^!Jأl-an_iug / U*vGO*ݺ>Nk[oL~ٵUd-;,>ݍ&3\waի;[q3F~`m7oׇSTu#S蔔Lxܖ IDAT]dl%SGA?NEaKqL@/>MARfƞ Ns̓(tP 6>@LT7-.>[F),(|E&?KT,KQ$0,ðz=Rߢinۧ)8>{I׮(+Z,<5}ͫwLG{4ykac ]^f׫wzmSAڅo|E_CTL yyG/Hy1^9y\i~Qڤ]e]r+^NNnk<_ٗ=\-ztN>l]^XgEud F(:R0S<ѳB&td,dŨ [# зSeՆuO_>"X) fYUϞPT!"rl΀D.=A)09ȫIS@}@UH ߖbo^: >rѦ,~o#@wQ9&(Jd>  x2 5}zoxhnk@;ԣ¢4 Dڎaؗ נZt+ 4jeTمOrcꏙdMߟj5ѻ9tIRHOhJ >ּDHBۊo__;L w8vU}\[Pؤ5al 2c73]tðſ{E?nPuCϦ@@?)rsc} '+QhͩC0'҈,уŚ$*)ZJ,NpC5$Eº$c‚Bv}\v?@ wQQ^U;KˏQFu Pܨ=/NNI*ۧ{v ϵNI>{O%ˋڀ 8ŞzWEm0Rpȓ -O7mhҥ'U*vɨJ0'?9vYKgG6q_6ss=&OƟ:=x˕.\y G +Nz~KduV8-|t+hhLg0PZ1 f_P16XD"3͋ׯ@7_}-EdMA|4=Eu̓RXС ʚT HQTFF ȯfyyhZ5=*PCn(qFF2t zȽi4CǗ-ɛTa {is*8xGN~iXV57qk\$}'4e\Py/6kKz;jNbg_ck'qX4o=hrz.U1^wvhyEJ/D/h mA\0h=Þ'^LTF[ \qv2,ʀ$;8)۴MYi ӱÿ7&reeСY4*EsJRTijjMӚi68FQ4I5ii驩nP"Y4Tʐ繧 #5W'>mK>>Q2ewo|V+l1ϟA70ݍ \8Pn/0e yS^6/o=Nîo~aFao}~i룃vݽOc1٠gr* }u^MAn뺿t]~ WSlo~rznZq!Mk(Jo0u~FoL⫬ ~rtUUU`5*}F*|\A(Q~?ѹC9=;;_- ().DYx<;漼Lhdn.!F .xfȊ{Z/bQϝlƒFFw5^fo-_V뫂Aѻ7nm0cѶ {2}=UotmX1.tƐ9&J1*qĒ{wG{y͝X꘷#9Ҹ{˜XYaq91ZW|nήV\>tjTOȈI#W@AIhJ{ lmt966WT*'9靽T2&-=cowܳ]eϼ2R)+l{H+toq`taaw̤qq9CE@Sp1g6<#Z 0%g'N,N2<WoLGAOVȸpFZhWZ N#<\,dVPv"ZA !~s)q g #0Pj2p`bBA$l*IvIЌQ#/U3_{iJb=0쎣DR]}gJŞ/߸`ѳF=?񲾗zNwMvJiϤ/= Բ"_~DI/ze4V^^&iaTK"Ϥȥ"zȐd)Dd:}ȈƅHRf kh*)qd]au"©"+=qdˍz=uv_Cc5FJ@V+s [e[tuxD$5j9][S0hi2->xߩBUè(JׇCM"+@3DC㐙'~`2[GԅdljZ3|fC_H9kaUEΈ#1;#E7kډ9]<).lWdk׮jiC ,jк(Jҳ@@9#*%Ecv6lhDrq͚ӧg!j,@K;h͆e-y*>4Mj?RtCr~c T>wT|ˉ?|igM ~E;A0"C`P;R S(.taMD-2 ֩ɢ?iݸXix|G Yl+y,(H,]w8thA'8g ڸ(Ull"2Uuj$;&KjPo8ؼฺֲD#Ĺ`("$" %}5Cddc ?Q[JMpJŢhn+@lP?e*1H.t( ڴ(,RAxin$9Q'.LiͲeK ̘IQk_|cZ7=XI!"+&|צ_ ÇsHt]Qb>ٱ8%5Xl.WpH2'-xj'.pWXٺPk~,T : aR5h; =qtzzCGA-8V(51|7򮒒֚L^|8N#C/܈j(6of45}[ɉˍf2 3;Ks=I='䑢H9E6oI>$ URfY8c_ԖwI"B"}=~֠hk:]gY&<\,R(tOFF*{ ty&ka9NhN'/9F[PxPsO\\WTEETTTN5gγz=UUUt2(ϼM\=~{ wZ}X8!e3#2wU11/ !"uDS)Nr"lԦ- 4N$NãB499 `ң5-\5i_Ke6,?D ͝*Q@zJV>\FF*:#bcs  dHffd҈t"(>?"RUAB#RTda=K&8o&IڳW*{7'/+B㷻E ֚/1;)3@͈dFƀLjh(%%3--Xwāryyly5keY'r_v2F+Rq:tj@'IҌkCfvNY8v_"t 64QqN4#i}(tDԜTJK)9gϊeH-d++:>:F j^O7L*E )yVt܈?!Rp̓Hf?wIp3/˹#P@cO8j r2/xH'໴֖趲%qN |8lmƁm^1}oӴ74L8L&cY?8FvN࿑Bph?"<ϙzE)֭# io$wkaa "ۓ 8$yɡ&@uXC6ɓ',9Q,E ?Y"@ռ}H1Wts%;c&(= j Ϟ?:j/"339m<6Y7%jU!Y#,{d%DRLsahF[m.FE/pтګb/+XP@a3YȺZEZ1sz 8e 7,Q K߉#h4ͽ%Sf3bcseYN`AN'ˍ@>q# CQT]@f>@2]E6u, "6eE@H4>x`dOȆcN2]j#R3bÏCĺ <'>&$y5`o.:eIuL.=YҡDD.HZ։=f rF8R?>F"u1ȹ i P<Iqʪ&$ xcΒ͡$2Xwm&e67zPQ,E3ar ;,nn/H3eK Dr\c? 3[Wa1-=09OḽH]e]=6~m@EKaoAwKa"Q0U@4y0t!KaHٻ&_Ka73&9L@XCDJ .(UW9%@o.($qXx'1%t@RDDj4&R)E4n@IU`Uݺh ned]K0MZ -RN w+o&,&LNU!CYF8RRb¤@Ml|~)R_ߞx-#"#%UW g#6Yny/|x,G'q+90\l*ܵ䷨kO"@R 8P#%-R 6oaV=rX qE,Rfɏy- DHռqʥEqy h64D)=T>,BNl?T`YXNUrX?w6Cps6#Nq.2fn ۔u0>i}hrǺw/ @eImF)Fx(iM᷐m;b C.Uc0:䷠h_X""fN"B˝i8oe[.|_D {uo!Rg Z ' :w//aYIWYhd2)VĢ[4f|&&8`vhwmWXCPpE/"wx/"~D<^D8޲Px*w65ևxݲ=x9|8{̲:^`UUO|_@VlC:$,dĂlX0;!*a˦>> #%'Nx]1q퉻WfL/tSBܭbTC ȁֽ<ޘ0]%T|v oS@D0EH{yoAF::'1/c[TB`xc8~B]e^!7]튉(c' !;ўx]̞x] w5d َtX2* mX zv=J ֲ( nCi/,^kQ}̲ 2C}Q1R,b(e <\\DBo-NTEg̶"2q$o;fK$B&5YLԈ-Ր ؼE:šR>Z6Iho}1H|xe$ΨI ?j5 }o 5)}|By+Z8"*#q7A_"7N NAMiOďs-A j7oQDޘç*wE;S,Wy5j IDAT/b.VXȺj" 5"²ד5't/Ej!(֐h!nv=)I띎M6wpnmcV)j`UIcio2歨!MYW`UU)MqRHE xcD"²]/\=X?f-+2ǒ)ϼxcۜDl3Ḓ$o NaRtDhn{",Q iAHZ0yQ^qĺچ6]튁 aq7&L [9Ũ'1t7fOD@2~eA Zt:P51C;BZġ!j0EG$b.:;8Iʺf6퍹OSD"967y=΂\*yC܇Da!yCPGWyCܝjQ `6SBFQT EQ)((((kEPQB^+*=5!''Ǟ^sߵB]Sv圜wH˪۴n5y℈5iN]teǏཷ ngV6(ykf9+ d8~lR^+b/\kR+!$*2baw9Zʰucnj :næq&O߸Q#F=TQjtո6J5y@$/͗ӯRjyt:.f@>CsuliGң"nw%t~>es.h̵؅'yoA@(';k)k88c(ٷo_$$\iUQ+2GnWees C~}z&8.^Lòo嚿pɌ {N?c:d?]Ν;9mZ߿xc!ݺvI**4ZÈ$xgoRiXزu-=nJ5_qcGJڽgo ڽw9eEa<g_|*9MǺ '.>;6m*4l:?Y^7_ߠ$u/1lȠFC½~5bе7]ظػڬip<ˎ?֭Z]ȿP黾"1j1U>_~٠K&^~ܳgD}<#Z voM=0Cz^2OgN0g}ч[(4`N; }s@ѧ)z5NS[$h-m۶]m^{`y C{om${+.X-y+/ª5/t\]RZbrѾ?|oE* k,ZS}=c[y-)֌W{L=<[ZBTNrANefYf6\͞N_].׹ٽz&UN¢Yggiټ/ί=܋o>wOzkw}EbV\CiU_|n2h칲ǟ~% ~iG/\,9UO4ͷ3 V,0n.\QQ}uXzٺByWZ3~߄cX^uf5{X^lt!ԱCvN/ᨕU6a\iҹS._@/cFl׮M*s`;vWޖި}j SSq{m$ej!m„ L&Ӷ;Vh\l(1Qؽw_=Ȥ X''']/pzO̘0n 0zn!r ufS9_cju賄s?~?z(I< sޕQ[Yd))%]qԸɣN2b,>ZFLmǻk[RL;l`1ݺ 0aXPRy򫈰<Ͽ³z!4xЀS`㦭}ƚ=h4=o*P[dqSo-7—_ϤizDYJ.s GyLgΝ:u:7*HLLf(fj\;`!~q!x`.ax 7G͚66嶸*"zڽgoRn{ǃ;V DFF?h4+fsG]_nn^h=d䄞.<%"01TDVuyy)ɽB SiZu[VY+6= ~=~vVpòlNX !ؕQYO,_XPP( BIi|U<Nx<ެ3g`Ч$o/\iV³cO\nVs ^}_ON:x]wNfrFyӞ{U}T 5~??WSgRYs8OLl $$^*O<|̯PZ)]wުDbwء}C'S5i܇7r5mJ=V1^v3&tyC~AA"Wܾcg5w߁ $\ Akǵ̜?mЫ/ _b[ڼY:RPRPPbYhɴ)nɽ  j7k9Vm͵vL ׌ǟz.77IǍ][o~p&1%k+c~}{id3\PXq\0zǽמ~?rsrEQDm1~VI׾]v:$$ñm륟dAH?z.Y_n҄q=p/˲yZMczg|ϿkOҚPCEիWWZ,%Qv$]Ҳ/Yc]:~DJ3۶m3bٙ|u}7c'KNw5-hBE^ir,Zz2#^IƏ~!^fq*sjС]z<3/9s'i:9dy>ICAf:g$$]{ FCB^ww?ٳO"R}xV~דzt66{=zſVXQV>l[BSLn0 jy-p:]%3mZ7ߦNcz!=p/EQWz_.}\o&̙;OR3iiod5/>3 Ξ;ŗߜ9{V>hB}> qS-ٙ9;7qcNj-%Ćm[Bٻ?&&ep0pIiܰZ?0r0Tt9zp4ݱCxq[Z4?u:8NשSY;RURmCƍA}$ǷoזJѰ{%IGxF ?{3g9(iƭ[R,EK~>tF lx$7// ˲)z@p!r.zڔۻvg>q\F  [n{C_qͨad4k&{OIYX6zo|[+VvmddAOs\082hA^òl`GGٳ۰Ag:w>W6Azڥ|i4'O o⥲_ƌO8! 6WjSNg~p:8)pw&OdYVV:850o6t$I'32b;vl$(8վ}`pN/w zx*"|Gҏ;a^?r]{ؿ/Bm6(!Ԯ]ۢ*", w9M zt+)):wj#۰q XmFzoܼGj5fm$~(5n+iz(gRrI7y؉My'k,j8a^xV#FCTTUk-%8 BO?߯ϧg>>Zc<|ؐsڳ{VT&i۰ %b^|~9'-\p]&ޭ Ə%w!_k4۷k+{(wfhД i6o8j5mۦ5MĆNA((,lݪXRҲEvN.ĔB,^[PXKIlLLuPTh{Q֭BQqqP-3j(kۖi[4o~!?x.IaXV}yϧV7  fSc(dz/_~1uZ4o&V+ܡi'N`Yc!Hz5k#=s .78;OdyZE6L@֘M&v1N(@ʿ%I"ŠD?T*Ur;vq}Y.kFՉ>Ѧu /[B~r5oLў8s)ڵm~c>믖KB3}4y[ᄑi AV>m[z%ֲz{iͫ5 1;ޞ~73;@uiZX,%GGE[ _"iݪw7|%\27"c6(0 زuEK A,Wjb[h޲Eclрe~$U]FM6=0>..l>-Zݺuyy2<oe+@a˄ Lx}>ؾc|I%bɷڵmw b۷kaږaڔߗBAUGj;ur<;6%WР.BA9D4HO_tyWnn~TfE?V:x?8.xYgʷziVV(1I=/\\d[U[ !>NRms;.X:rV!L(#1AT*UbrF'x˵i.;U`Ч$o/\vҗh0~}٘/ҴiQ֭Xn-P[j ss/0L\\ɌS(9,y B1OgU' z!$''j DGGedfJ™2OQTĆǎJ x*5Mq%D0:*WN0n;wMvvGEE1 `a+v+Q8 Idwٔ ˲;u\b'f;[4p\lAء} skê$6lڶ5gnp;TNLPZzq |<4wZuwϼ _~uZ3ǨtUi&iJV?ȃC w֜=po5SN;H$'t5症{rk['M?lmd-[=#c5n2Pթc7^{X}핗~3wW../Blwh4-s˲ڴ>Sn]:9~&Jݤq^NO<PyJ41jFz!7/uhn&2sXh qOاu$Ѩx Dr&w\ qC5scYaqƉcrٸ.h:+mJ|>)T?ߪ۰uVbf\x*ҀI1*,Ca#_Z#c#0G-ZE{nM7$J""{DhūTCZ{F<8셈yFˠ>+s?sɬHuڂn-1?DxLIʾoJ_dw{Y &G(*ge.1DB "ID" ""JD" JDpؼo/gdS5?%e3Bf YWwV|7!!{rԎ8#@ UX1ƒ<D.0х YDJaKi$SVoI@cDPۃB˃h ($H(j$A0BP> <Qi$!B{<eԱ ={kͬËE4RhPQ9ma&>sT5D$.n_ G3=Q1(?#`B !In#X(Wg>)uZ1i$Q7KTL0 f9o$KnWdZUjCT* "z H*bTHB@"Hu??Q1!Rrڛ2Z^Q[ 諣R;%&\eǃ1BɊ y||$|4@LaBRL$mwP>?( ፎ@U3rqIFE;^KY!(2%Ǣ dME%_{U7SFhf.^B=|V,C.jHɂ]{ZSǙPOa#S('|r.,F*zѓn( QaAZ(X< q 44{QVTx! Tߏ9ΜP~VIjD "] pF &9V MnLp\7EUs!B ȨJcq"D ;DyO8Ϗ9DX0!H0B@eSsiF66Bt f D`,|VZm^ c]9+);w7n(SEņ0Z(o_j[e/[&lDS zwO^:;u 衢By +ޗ$&4MIWWK+`hٜ=h_ѐ=HZ "DӢV#j@ ecA& "XsQw*:0:YrrrݰkEU`!t=\QDb2 #D|~)&A $іRb2aBDEcQC3ςZQQKt9%i2"YGSbs CDZT`$ ǫ Pt:;]UwhsT7 bڠUsZ}v'bՒs.;ŵޛЉ*\6UTgGV{*TP8 ( GS9e@}DQ VK=X| s|a(?(a\I rf$ݮ[4C{Ek5lXEQTuFuڈJqDf: cɓRDׇhV H@VgV՛էB I %V>PEd2"GGB$j5@ӎChV{<ծky$S9rV*$FW;k#oB[ZQ3 uZ P<0A U It B*1dd6h)!N  &R%sY8N_\FQ¢l"j*$}[I4Mq<RlAULu(jozMI|DsW;0rU uRGTV'_;xt0˅A cE)㌧ϫyQhx<ǰG`5+\|Vr8W-\QP$N!V L9*䏋pJH N!%5} UP/e"L_N!E5PCv3DUe#+tP"0R|HcZ6=pg;+(30QcBՉ _lٶ NGZiƭUt #jWQUsG[6D.߁Cc"R@&KJD[mDJa)FGbQ$( { I).QSM݁#$0#QT B `B;[$kciQ]T"tHbmؠ_auv85Q&mu|:X Tm-F"QpG?zU %IRp8؝\-mU" y*֥a #BlC_LHvac,[ܶ)uOnՐ.,7W XXTQUgi֕vuJv')CFqH'S ^sKtŘHa3N0#I$(XmA6ii$yU^902,j :yXy)"*]4^1q_Z;5x]Si5 c5Y>A-gϨʐF1>3U[H n7pyHQDyXBQUj!b%K*a^~PX|+!@ Mg4"!A7pFY4LA5zaI""rϧ֣]k=)C#M2H 7`QBi׋z)I&#'Vȵ! K"@XJ1 tGN<8 b D$>F:߯J:oc(c^0zkuxyM=Լ}߫jkJV6v.=NW U~dX WŊ Bh l݉ n ӉLODHj!8H0&FˆQú$"&E(ݸ!X %‚V#h5YO-' I@$'u՟QYm3T%VDIcA(tQ54jhǬja?ROW6 [qb\lN[b`} @hr/fʾ% +7>p[dz)um{ST6S1J8!g^{A&_˹PgcD S!|%éKD4Kj&0B5E\d%Aa/*BQ& „²_ltQ00UQ636G΄H$!"(C(TD Q<|5),! DɺV, 6mʫ-z  zejo8KRWO0Vv{A+> Qd Mgϖ>ҟrm :PT55q"IB8h  d2@ܣv @"cbC;]Bt$tfd@~Q8xTŸaJºPPs~L H`aQFϳ4z}{VMtM/5i)fZ q"/#YJAj{,0 ZjHkNHR'DmHHuyH("IĢxAlB $o" yb7!VR1sAQF`aCIΌӥΫxz%`\ڹ]Mۋ{u0l@X x!~nkǶ'N\9I V%)= q*(6vx "`ݮ!)ÙtfX/ASzU5~ePʖ} tРKzM!Q8=)X~ $}1BӄUG< `?)A(T*y](F=u+HR+8I>$Вȝ{iVnR"KaW٨M$σ(ᒢb~^8e[xEQ){º3s~ƈ$ +F=Q*3 W&̚8]F@(o@$I} M]j *k4AHbh.*EUn&0?]NSک]` iT?TҭS_G<'jh%rW-.FX, [wbc8;4B"E3{ B6(:(%FG/ΈkLLB I努kL*.KDRh[4]Lѿud\#BR&,Q1EY.+=sa%!Gu! ĔzTҰ, ].(VXMX4 r~5SFؗonwLoɧtӞ0_lb5DYPo#NRdxM!V% nbl%Jc;3neXG,Q@ټ1`u(].`p+WZU@&*D0;YF,J@bF5[4gԐ&+ן6K0ojrҼx4y%%O[\!y E#p1bt9}ށ n"oI%Ζ8rDUz<^)J P^; (W4{ޢ9P0{wמ:C9\@|*Y0>QNI>6 $;Ne]2$D(g Htk IDATSXCS|AH۴G]dB6É\"lsȖ=fb<]^XJMN' ZU8f>IBބXk~)~>F&u v_7lYңYiKo WW!A@U$1n Ghxn+oĵ[ VLh Ns\N-FiOqeg*!B$Q<^R<'bkĈL. jv@Ւ`GEeE>* %C`7tqN 慲E-DGz  mqHh?L" sfIɋ"Bv/ϸpZY~޺`mԑ%\befYFPrsB]}%:&'_/*w+QCL$߯ ).;lZ8Hƈh5FDE1@{EsG ix噩IR¶kr]QEEB0폎T dE)w'AEaA@!fJO[[(EJG-3T:Xxݞ 3#Nݞ3߱+|u@<Ы}c<^S:LrX6Ume@vm#1p93"cF}^y.7@FJ?Ԇ1dR* \ĸ܄UcF,MiUj5|l2@e#Fj B5v> >'%6|*5՗=(zE7ׁq4Ն*Erh_krõbxf^w~KV$6ZU>aVt!I*WT qfMA%7"8##T[;4Y=ƄLdl& h . xՆ[v;qgu.s9gﺵɢ){P`t-RHQ:˟'\ÑVZ7%?Iv`W!}~FhOjC((lr RZ&"0Ixup\S`AXu8]N[$ɘvܑԅ2UVv(F:7j8,-iJJ7=8<*au0VE|sL<,qz)ƫ*D>O-[ FS<"L'._=4Mر*D=;uB(+J~˼;v_үu*&$v_j~&k6rg^ $1to|Ls&p.'tFE}卍YRkwLbh;'7~I@]q\ D[PdS'.[˔, UĘӨMO&ƒb͓묨*mp2ObB0܆ ,a0B"r{e :8]Vj,B ӅDP8a}~,0*Z"4Md_0l>ܙ5=\Q uMϏ` 'c$EOx*p`V .']`V#)% =pMzf?љ|İ 0->X{&-]w^|IR /6۬'-8U4V4חis,5dwu#7-!ӸCjvœ# HHUC-hrW'E{`?%ƒ XBJ=ZV1J!oLдοiݎPJ'@RfDBi{?6_͹ўQ]!YbpM?c*c%^,f sKƽJ+ wKJzZ;~?٨pN0>9}ݖ+zHJ͍݌'nsű6ehޱg 0(-4Z*sUJˣP8(TWRF4?}v7ދ '_@S2//;{oe.'PЬWXH'7l Ez5]K|؎ `•;7 u$ O/,z=7 Z= ܚ$!2 m-M>W+e kqp)za6uG+hܵᜊD8u>|Tן UQ^R(ʇ%I*l:Q:g{`]eyQ'm3MI?oߧʣzZO_itIftAѸp9mG*挐uKeT/_W)bs3/_Ըr-Kfoa^m]IO #!`VYK.7hi6 '!rp/C_Jh6ʙ#_z: Y(=Q;; sHpng__:x4@ FW%d/.Ĵ;Yos2Y.Ke '^^SA2Jlz[c`'# j>`2! ;$ q3z͙xTԈ_Zɢfs$K[N-~ĕkX'V,Rɪ:3BػHk6զwWlC)t塩jo sS@?[73]>GTV[$om{ea$1%Heփ#[ve/l݇Ib\:~̬Go,xN݆@\ǓX6=&7g; U dk=PfkS{{zHcrv[g۬!Q; ܉.c3e\E;/|í?\smsa-thh ȇNek!ӿ|#&_b" 0Bv,J67zuk0azXy(^?F5M+A)=JN^=BQ\C4Bjň`QIS'M\ !$%B˙J|&lGI)E.N0p.W+'|'~1͑)v]p0粚#1^n`-Nr(`)e/* Q*w7ݿˏN,Owmq ds/Օ59FXV~_f#gqCxmn84i ]9))jFMzTIe۶:8$4aJO(zg )e妆+\CI!#ҘВzw]Yi.5U-\^IfP޿c>ep{Qprش*)e,7b5 bL0@]?55HE'vW7lUF#/}:!R׻h0ZMLRUB4>Rna0F@)qD--K}l#'1FR"e5ٸ2qc8KGRW41B4~~XWɶm|qnPՌ I-i{מ[v9:tiB F{"խƭpd p݊V[,KOEmhlޑྟ'X8n%!BUb\XvqĚ;BGO`.3򦷤 j CR̟ $Nr e׭:`6.oŒRKQCI[%QU}40JTJ45̲{;7ßU/ DS]/Y`SVC}XT %=K "FQ'و]v#N0TdDWc Olgܭ|5F#2x!ĨCQ"\Cp~I[3imݠ"I1S).=d=T{LFF=1;/a N$ȅhڹljhJyO'$/NPT pM;˭du+T/x=j8Ym]uHd:ߺP}<<ԞV\W* Ѕb E7q{AHVo}s9Tsy\ v 4MghڟH.Z6neu!^i%M-_={u(˧:sKN_PQ ³S]gG?ѷ߸*WŭPXw;/(R"JV_TML1ջ9JO*==n"@\S uZ6FiU.5eKNdCEc AɈ86u9 Z(VXb@ >XE0 +(ZPzʤ;W"ꌗM,V@m"44\QQ"|sR+zkUbJ.')q~%p*A `ոt}v4)(f#=;9ؓ,v81 i6g'eRrTE:/?_UOsbTꗄ\N8mIu[v -`! Ͷf Q!@lJBV}zWkq{n;nkﭮ6Z8ml2E1-7@ (Q&[GAv ܇tŴ5Ftpew5?Qko{[ )%b$zOuJq:0Fh*p)3uҸ[11BоsOU*R!rY,jHD)L|i4/\DLM8W -Svץ@$!iaYoJPc=ǏhJY!* P+m>zܮ%ᱚu6}v_hڱoqb)*/H d8 LJ\tF`4ȐBt<w9Q}~ B aR;л{F2˽ܪb5TsCA΀": e_G'rĹIWl,7QDZ>$73]\nZﭗZ>6-PH6tT&ݺfsϳK%ƾ9\NKL2bJp)O1eX9`-Im B=CMǷ,+ޔտcF8ťSXaB!k}L^rD`#KN}C5;KaY4ޕ(6Q#x7yFAdt Οۋ]h =w>aK |gbd0gnج!)=RI F͇Rs{˅ *iZĕk:^~?,5u JmH` B:EoP"}oBbXp*~с͕V!#@xiWR) !N{sF*l~>#sqQuͫ{/e+ kHi1p=A1pI_$>)lݧۇy~fr츱-i^ Ķݺns%c߅NyN] GRGhR&Nzlq'DpH%RkOkg:FI%S ƷmIlۻ"`˧1ly{H?c\#JQ%`3JEFpLöe]H&@~R!@D2I(`9C3rO͍9B)t$Uq@!X @(|ɓq *OGHR9I Uu@WbKɅDnCRR([ko:c7?fy>R\uR}i/^W_=h6+K!eW Kvu*NFrD!̱)FG\;ؾY8ѰL]Ѯes 'ZK@2@r,qŤԡ$K3: }(=qI{ # $Nt!AJ Ӻ~k))%=2"&>\bP ft&Iư1⅏ŒƈkK|sp(6Rډ?$ 1~#1k^}nGd !D0^xnC+UUJ45FIN>)~ȑ sgP_e`?~UPi@~~ԩk,7-H,3>9ThPASP"XHcjz)!ed4կPB} ԧPB  5ax+h +;>*z2]hϲERӒi+rA#\(?qu#o=Mm)㰮v?pN7ܐEm!0t%_`Po ^x*^v<~gl^Im+T(7tR*|A,1J|wH-KRUoCPRrkd Y6H\B!PP]>PiLU Ο %bʺC21̙>%RjLH)FQʯ٘N|ID @eݽiڃ:qw7mZDRT qT,3!ŮdGo2r_sg@j؏wsS.Nu* !Jm7lځK!$v,B,^à.x;kx ]ZHlےRɘlْR'? "Y  8zo {7 `)l!kmٸ8a\~d5A:8\WXDXR9?v0!I:sܰ]5f;g 9aQW5UG 3kJ-ll1ZxuHcFJ|}>$LB|RB 2k* 2O<34(ԊhNQh(H4D5%ޗLD4ѕB0_W | Cg!Sn: ,4zXXcQvXHcAyY@5 >ݯRC!wuH@Q!ڶN>pYڢCf}kdR0Fv<#P }? hWv:.KKS Rd!$tmx,0@,H$" ĶprRlقQgh}63۶PuMr,݇Rjy->ڐxa` ]aULӘ1U*]t*SFk1F **g-;w&)Tc4]u 74i,2tFTZNK`6_hoUYmRm2RwT²ew?ko 2,]N19nQN=qwP#&`:i6AqCjWK,E*v~;$ŎGCGNzvtpU@fSCpOH}BQhHe(xrB1Me8cB@8oټK92fWb!-S}ӛvtBI7=L<$RsU #P9֜4!r'4 >F.6̙0RGMWT+TQ)U(R0ܩ}<4VoA&Vqk(/gYr @4M?ηqYFQưPB`: JHgaιx҃3†YHc3uE1R-/uH }B#9@-Y&<:Ca||GR(E[g۫iH fS(F";~ZDB_"۾'t` g\xݡ$eS,j_b\$!COU\FQ,rfS}14::&ylĒy.BBVj-Eb:"h&YM˞x}l6i袵7iMq#R>(}q1`7X"Y]|{>\,c݋ߴ3j,Sb(g(Ǘ.3\!+KUPPP((Xuql ⑏,P 0Q@88 uԼAp*f+_|5Xm4)_'z`@JDWºX@e](3B~Ϻ^,#Ӻs_iUV}a^9) *. T"02>Oڴ;{O[;*+Kd(byLHr {]oZWPnƴm{j#IGfrK-LBQ"-ˉ6V0`MN_:{9R7r}]_h'4YdAHV ۢLSK\pʛJ #ǕB.>lSǮR5!:#Bp(\$ xD;F͗T_OmH\G--#@Ѯ g/WTwz)T]O'N]qsYFfZT9}Y{,ji$HQVg\yv nad# ;.׾ugo૟<[vMI ,[G;ʴ^ȗ;͉j1/{LyQamFdXe W3;j5BщJf]uFFR:~Z? Q"@%DJafT6-/?Ke*1VRoޏnޙXu,$&^7lߛzǛ p JAקOF];=rT ;e:^Yh DMI!JP\27qˆTΧǮ{)Se/B0b+ WR56޲EQT:Ug-1e!Ӽ'84cϗa*e*c}@f)0BBp% ͸ON/9HBUHku]:6u'k k6/RWq`p"ή̔Ŷ@w_fSgT&hXLA_n(óhɋP;JK#cnWg.sꍵP a,D++We#^asRMn>2*6+t v!xo\^UXS_Fk6?O + !p>Zq;.T%겞w,_6D֑˃f3dwnn~3 |&|ƭd8 1%jkmW! r=:Sj=S?F-Z;!$qbدϘ"O]` T\7Ԃ@gnӃK~ \ug2=YORUXR FVsCQ.c60tEPԻz;-d8zWoi8%;!:8j65@Y^0QjߠqB -<ٶjgTIS&EalyO,{D;1K]#VVTLl6koȎa !ybDВ)04\(z~BS{ΙުΧHHN]bq^Gg_8K(U8ZR3uEM/)kln,ed8> c!dB\D8K }ㅡ9twvAԓs vŒio )I;qfsC$$D( 6KPy},S,E;5 î0`@]`QJB\F#::|^~Uk}I!sO'Q% 2)qqK ܵcό;+8tT1"*T.4 X@1Œ{b?go5 WGGL6l\S%!R0( O3p$r9r9$Cg% Rkr߉GQ? ;}>ʊp:,T 2ı#ZqDYVͻϷXGB4@ZNoA7V_uˢ ̲B>vCT"a3ǵ;sK~ cDGr4_.sƃ 7Mpe9B5%3tIAy:M$ύ/=3 `,@7UB ΍DJ%lPLKp<0T(]G2)!CE@!0Lԙ y/AeT ӐB)U0.aN0"9-'^yUkϬX6uZiBHgjO #T)Rz'-L vW)v 6y^ݸJ^UwJ-Pfƴ!Ub,  U>=7} 5nN/;\Ju B6w@rT)HJ*/x>P)h̆Sز|g;HM 6r]r$>8̓+׍W]5zû =T`-Rwr-zzqE14ڀ39FY(]tf_]E镛$烇cz҄["UHGQ"yj$Ř 335mߛ:qvu'Z2 TC3DGZߕH~@ѣsE d*M+W:ŝK0]QW]C~W>k%1LncotђIR(˖R10# R(L۵ҢF.E ؾ`'~h260x2{Ѣ'aD'-2zGՉj{c7pIbmds=Q(ģ /[8"y9%12X7tO脂N(@fyH@Hpm`aC$#4㪎Lg=7]J΂mKiCRɗB!wx%GebeӉ1%HԴ{&~ ^a)c0g}xPLoA?\(P"4K10l+ڌBn8ᅤ @^(o6CIkҢ-8esd2pㄺ{͚ُ*]x9n@֠?@J!ZL]/X~hZw~T-sp?3:O`}|T઒(,ZB@B!0MKi #!!2ɀ!X"<ІX!``i7 =J.̄v*<0}h|5%!Jb dY/.e#FAH?7;֌Y2ٔm;f7(71\8ȢR..TQX}cq Q3r]ޟTloA%,Ƶ|&(V3<8%bj[c,,wUעDz;Lb΃}hjZ"Ac$SخD+Bl (DR]Vl 3Kݑ^35 _9H /y3'nPbRCu"<7 *8^i"umsN]41a;S%ԡS5pa7_3+φPY6vfL#|)\^+rhbږ=!lZ`oJHDg$Ri#ǡ1t+S+ץ2Y] }. }?,WPP"CAep70ds~2J?Z~: 曳[#_|#_3<tсy/5R?rܨ3Rr=Lj q/mܔsI +TnBcZbW.6`zӸ?q5w!r\-L(<GW]&BC?@g}ThE7|x%wuNPpxuH̙dZnr/P1b;BH۹ǗV,J`1BuN\[ˣˍuyiN‚.v5OF n?:JXӃu2?r5룽 #9h!?/g>am''ԡ"LYvx*J{B9Bn֣ RJ\g%]y⛉)ijjJ!jXr[s&*7y9Iy)dUvL~|8+[cDD'@]Jbl_X>/$,-z$)`/r\W^4+Krl!wf^~- c^iQsysΞ,paEK--vHL0o(rI'P: u3.>D6rG,Y:ttgBxgUͬڒ9Z]+jV۽O7ˢb!.)uEԙ7^H+Wo|oWu󞌤p; pT,;4僵٩w^y82,USy,̴5z)5-` OZo~GBqck~o_rUUʐB{ϏO[̺qky=ǥWV82U:K&rI۝6LNJɅ,=WΪOI\/Ù%y_f0FT9?Sls.JM "}LnH$~)}Ͽ錻  0nL TzR׃v:{t_SPN$љ>$h)t\.ܱ na [jEJttzNj$ N;>smp7$Lr%n{`frϘV8:t/!;yV\es8?y?(dܖp |F'et.]nAT~G/J~P&܃3ciQU+P I7)eu:mo#tmJ9(ѧCO?Y|jr0m乲,7JROlx,ržҡn:;h훿r ci8~—_O>艞9r02sY60o=8j?pww-EY4uq[XzfIg{kj;#'-Jfߟ;;`1!oōzff6]2}GiY9²ޗ1-p: B뱔xDTʌ]3Ru+pH2"1CHEHl=u Ư?q5osnq $cpan_{&qɠX%(88pkϝs,hMnK F^yZ]uVENnH<[2az[\U͟Eܱux[X:yW{o~4*=ᆳȅ.ƥ, u˓y؛(}q*|:b2wƣʦPz|v-w<8l6Ñ6l/<Q)V).RLbV] ñ|κSTOosc w&@v/ޱ:%~`]齋]zg#Oi$ IDAT8u8BCB?̖ )É`>9t>$ j"V{ !W5ݡGl85hsڍ;O]VzmU>S!?r2*X2i;V7eg%3d޼#<!8a$MgE{3%X!QajU ١+]m,nl/_ Ξ }u-7l޴w] =D cePr܅Jؗ_c)Dzcڴ ߭M68\8.zݜ;ЌPr\( ]lUxa)ilWR?EoY|N^nR;J!_41\ >szR<.m g\k5[7xFr}qn9&!?̖3Zm`^}֪/OEϯ>@?4o2eo.k]=y[Бf}ҲM_Eq8V)Q V(۵3 o&]~R];wg?)f w)$]-QRS'oީqN" px!d?;uLw"{.ʥK+ ܰzfSsS{ݿ|][)M7ڸ:[/XtTwbLjP>0Y ݿh7qvx}EE3V4,jP{2Ge~ƎX|~ cLr*P6cG[kBg'vJ\2/?V*y' E?{,G~_fVU{w$ Bagvwv۽.^}/q̎a! y BI:9}GusH VSUՙy/<(ɍnNN;3yqݸ=Z{ jnP'_w (VU@EZw 2ӽd'm=]mzLgC#i♐v〱i2*uP/>ٲr>q dBF9?ȈlV ƨdK!B!@c k+*#*v0-o@4_p<3v5`Tr޹ug:xU\Ņ`4r7orʈJ0quUgڢ"ǚ58ִ߬ N~%Ot,JՐQ:{o74u宺g'x/K3tLIVpL "D=ws1fTB cl ƔO'4ŭR0#ǿOr?u^n}TJٻoqF;k}M}mgD"vM}΃!RUh3.)MeTWIjX_f2wW?:վmN1iݨ+ WN~fEMgxʼnϫ.EЭP\ uhJf}l3]zS˶>O.-_,t3_iNdfLNh* f;LEf**z,yڭ2ʤior &W>Wqّ0lq8,)C0Pn+Jӳ */x0-*.#U\*M#" ZD*oS3C+67igOOOPCZ4~d(5ߥ]u$~L}qԩRB e~UW;1H]P6KmMtu[)|+7͗K;nHTϴf^U3`Z_Z|WV#h _!]`F{M+]=*mH)Ay2Lq/43v Cnr+7?\HY$ t900I ߖbFݧ}vbOϣ9<\t;Da{+>˅+HT16X+s)ԥRbQZ4'(h7L6cE䝧d.~PT-/r)])!,WמrinIl_qepn?8kas0T?cPbSkXq`J_ؕTk.[ѻoݮf?ThS_ EI=tt"$*"xQm5u}бFu|Lթi7</;BX+BTz7kUvrY )QS7OΎ6o7LP];>w/Ka'H{i *v/Iet́ FU%A vۡhm_;)`iNBA^z80*viN֫[L]Yݱ)-9 j*P ݶGTڊ-oܷpZ9rL̝fյwNx잽?ekpDu.Eґ=Q=r¥!eWQB͏MթU&VE%' u8ɨv HɿhY5骦(e ް "AR*qVU* zg!5'|#WQ(Q(QR 2ڷ`vMߛ;̹^ >t\jt+Q{Q%݆+iڬhH*[ⵕ/=t1M4)\k~=r,_XA+|"`SS+m\K)oX'zldLRƎVUWq''o⭡5 F83v4:5PmlќL!sӸe\'{ S7xڦ{;;{+u϶m '!Sƽ` 5 x㯞u.F>ܗt]] ;5C/(2&*3'X1sHJ__Ź=1 8jǡPPH D9SSS(Qu}E囊ONg!#YÍŽ6 ߒ떹 '# FTFF/<Χ(l;ʺ//YWWWUn1 SFs$ґim^Wqk;cC~ .@ 3E֐yvѢNt ,KƍJuL#Ӊb ?My)z8x3&['C$&5IL)67PF` t!4HM$V} a ]LS%ѣ]ZWq!|Sg4me "HF@ê* geU ZDLlBdga1EaDeDmjvTyh8]͕G~6[w~VWwk˜"[kR«B Szź7Sݨ>pر{flK {zCZ%woÁ.k Zln8ǝaJ˒}@ D[,J@)PʫT(q" qTXh6דLԃwic*4sk9X I1V N\ RU8D/v5'LdCՕ#"RqgByk׎m-WXSK9uuuWIt^u\{G()YQ.#4+j_}7?ƎKөz; 1olY~at.ﴬ5Ng5LfGN  ȟ?WN%O_v[u3wVʼ.%o7W?ky/h/.C; {ܯ *TDQq9ڪݷ"`Ձ-+7P R@TkvĦoQ"R_h5y&;=㑖.߯X땜=2Y..Q2gȅDR HJhB2(%Mӗki \JB%$@T2j|A"ԛ*<M2&pgoUU@g{x߭\Qi!(gcQ.ߐs:EJ.${7ĩe[[G9Ӵ@_,l]qQ,T1*.\>_U)P󆭻{}aLJO"7;9dG?[ aΉ~e?hH{gu/߈pMFHu5Ȇ@qsl3!LGyooν~I צjPڽ?yў/ m{Q^7" Mu:4@ĠLjՠSDns[{IoŸ9>U|c\mqx׍jRi}}`UEשǏ f?"cYIOVWչbH}ir$ZIYE%7;TE~J$@ J4W=,J ǣ$K;\PJ H?K 3>Dtj pF( kB(ED 'U pU]%7m+A}'VZC`T[xZkr7.ˣDv0 :ưBpykL@Nt8I;`I)e>_k_LM*רᎵXBu2yQt TmݥD2N@Dq]]R6kÆjS !H(5o^2[OLs/ۈv(ݷk̵7.y +oNTgV(NxBS/ڹ_f)Ԗ#?e5m#)ȶOMi(rDz߾LJ(CǼesDIۦэJ[7׭uYFS&9ۓ-E\_vc|ؽCB)x&0|kgg=_@Y6YTh|pVvڕirQv Kbn{X*P({ft{}{mT\%șv ޖWXir޳}g7Tuf);(HVs;)+>HI$Ѵ@Jw[G}wU\bM*YyfK<^7 t>D ' >/-3TkrT13DӔnWGKבtW_9 x.";F%/B$A  ݉@T*j{3 78 &|^äN8[M,ײYPѩQ$/JaY=]5%^h4JD@a4K?=ӿl?8RƗ!RSrpp RU3x}<%"P")_~J$q Vu%Aݽ/7a T`"WF[78}n.Kj)LO(`Kifaʦy?fZ6wVIY %4gLs 9^D/9=ݨf* i$-2T}')A@t8TJ2j0)t-傎|J*[-;k*-.jZU `4 )xjqІ;jhҺ8J$JR2ّS eSlu P;+3xoD- |qB"2:ܴ{{#޲K8Yc?5eR{2+-^)&t5$#'ڕgܣԏ懖\==L( 2èX> ^S!a['Iͥ4yUHIO JusJܼ+5! ¤qKg+x_8iRNY Upi ut͜ݬW-S2T#o_\.YU7j'{P$Cӱ||8fa\7^@TRKﮌ$c(Xysi⩙2HdZUKײ\aȁVyReN<1]j*5LmRחdr>WAahQ?# =]-^E,Jh~_2'>70ZNYBr,Db${`(Ads7Wf{ mO{_; H4;o m]JgG)Q}|C,58UQhPZbBB=t¤9b%9D5UF2[ 55 JiKC+ܹ [s :!t%n3Z!%!ƈfu&_eJҵ,C*@)9 TN-oH̞2lA4ƶΩ3ςKvvF5z~, ȱ/?mA3!nPeDCcgNԞ&+<4J:XY^зoVgI=zoTY0=3mD,+>H*%R(8'*rn e[AN)Fg{=S\:Rs`wBV,}\DS crRzɍdYeT7hܬ0g:rj>?}]oipjK<!o`eoR~b{XpN[C={=NgMYU7V/@<_|4AfG>lNwqp96R7Ņy{7<0'[> pa?@;EHR )y*kkkҊ Ve1x_U'~^/E=_=rWtzm,A׉3â^e5{_lbsajVeЪtWYF?xgb'N7yeNq> $sRk 쩁9 "F,%DjYssGO.esVE@%p:ӑ~)O=ˊS:ӵXT)2R uh4s|~:yӰ{IX=OkFiɘ{,(0#Clut{ۭDp۴SvڤNNp^;o`NΤhxm3v}̻L~!RbL2޿н."AH"RÑa )/# J#犮;Ri_O$(j]P |mz/}'ǿ O9'sOW,nYfYҩ!OitvohBaLfѪ*QHpv6Gt[|(ޕe𸭀ʉP$P{9Skk PJB Irޥ-ZE,N EޭBájͿ~KWa8*bf6O#4~//0 H(LQ0'S]M>/f!UΣlZ^;J}ns7{ϭO$Բ@FKeaWJRbyK|m oPמߺ?zmu#8E ׊Mjol?a)ʄ@@RRvBR7E@ 5ܴ315[j"93ee5+7} ::{JvWafpyv8P{ߋPDDIH~X}TSmsZ,b8F@ %`,^ֲ؏w{g O !($(Q z'gT>\S$O&Y$u, TAEYi;\NғmESDcuo޿zo}whg ׌\NLNdrɓGt%`!\o]n~a !.huuL${VW&[ZԄߗVcj* E9J1{K}5ݼUU[w{\T6UdDCtZVJ2MmZV[`i:= G$q!8{sS&飚]0RH4$j5+7Ex qvtTj綛A4BSꖯ/us9H Ԯ@J cZUقvQ@LzssXBI#HB!"LőB -ٛYX_`ݨlզV݊oRdX,VĬGܧ=tHp@r0d,[0+m vnbkxYE Rp ]`6Tw&Ǐ uWRP¥ZBZlz6ڭXtˡ=';WcjZ@T!WyUt#}#"{UpoL#$WWÁHWлnv lDp9^yF+74#.})sCoFXfD >i*ę!.W qnY<Hj[%ALBHHI;$xU\.|O$í].U=ivuUJg/D70ɴn"63=i@!@DxF ,]YS܃G*IĉV#oIҲ 5KNGs뗬e9BELn͖6-\ ӹfFJ0l5qu -iv82|ʲGgpx-TmC/6Ԧvbˣx1z7O6ka" " !d[B8k*VV-E!UfQD2"K A¸wG 02,% ƀDM1J`y{Y"V,y}E[;onؑ{qݱVpt~࿿rGEQK *KKy'kY&3|d_pHG7'LR-tC*E6va(@cU68)+a|.^νܰG !:$z_Ŏ4Mq3-N_=~FR͠Ѳ$@@H D[n8er)k="yBJ"p!!\[ܲQ[r( hD79"_U"sD\wWڷt=G(HgWOwV v@ \-Xwjy=z]u7# =$4_DS hr|jWaY\xzš,peT%2sjZ*(\̓խhTcd@Mv̩Hځ\A(Dp[MXVMhZpTXtMo/3jfNm~k)5 ylW5vvT+X6 !`gT#5,BN|j#˫ҩQ["K @f"?BS#X\@HM7!w0+Otv^'Ek%%% W:lp D0rDU87nTT+[}}%:$CS('< 9c=ܯ,P|UMghUQ*0;TAЬJHlH[_'fɴT륦bQ/R33jT+JY~D7=}vHPQcA ,;zD^$,aLT>.$Aּ@$@$! qDlW 3mBQ RӐ QDt69.q>?]Qn7B bry3{S!q`ZԲ\9 ;%N^R:c4)`Y B:]]*1-&*%lJ'Rt+3rN3NuP˸:vDb'sjh2}I4m |)i)QAܮYC:{Λ|O<2_U/Vl9~:4-$HH/m"^t]ksgq4N-n1׈iN=O)j;'bcPwGTVnuvTfd[ID0r.ƶg:,(m)ALgO^[31pe뺄:*6M`)C<azETJyE%HiMHUUOw;(F(R>dA"WoyNw2K) JUA+v3j|(duKJBPJb5Df>lVWi:tvolyݥoǤz}=7B\HfrRӤY dBJ %$QLǍ޹hmaTF jn3J2dxe~BDHؽ?7)ɯR:ẑr:PSwwoúc_o*:?^3_Us m6+{}:Lvk(!1b5eNka]܄1m?䂋ڞ86t3j6f^DDjYue&N!yջz:m9JbQ@ߓB)ϭǺJGyZnSw/;Y(fc1mXSi]9 \JJWxbE5!ۋZTâհiGߌFXO,ˎTSv8v+)-Pяtv̜GI}sz4J(PHWMa?\f3+MMc qU SxL3 s{imQ1r8kȬ iJ>7.\SK( f! EVbS8/b"]Ѻ5[&34zt.T I'AoPUG`O)Qrh Ta4))O٫6:HeO{` [<7>;>$T{d9!2?(J EE5pv JZ^ yTTbʍ;/̉-H/??v54<'J0J,>ãO2k܁߰,/VÆ3Fk}5/]2j͝Ng+y||Z(l{ϼhj2EP$0RyD JWU3ٹX5+vqn۬2EJ,ea CٌAT1M=i*=4\jeew/i\_|}7NK=;;%j*%@%$T8^3S_p~#CDZKT\T  _k錖Lv 0#g"#(irP}y0yTuey"xn )Jhy{@BHJڕˉ.VV!G!$1 5Bu b[ϋ$Ra'kxw Qlw :{nnâ,}aSQ5D%Dk*՞0HrˉQ&_#?kz8`)A_i$N"(pT:$?y՝{i,!>! 2aX ЄW,$̾G c3@\:uO!^O- ɳ?{$ו s},Q AD6hV f66b"kc̏ݝXffgdZjufIH; <@ WJ{3U(AjI>Qe>swws \, % P ymd]#mى9ɸW`6NRk"{-_*psb*:N}=/=̑ \r%UB~9Ԛ'>[nh?DA 5}wT\9餒b|Ug4"Ԑ+3ę 18_`lL5̹s߹#t_ksn{t* V~afx&%“Rmf%ҺxxE˪( K^=5\1RL6>^=2?~c9sK!r$"/0v+Àe.%SBnߙ#YjjH}yq;Ipb8'N|FufYCV2Gu PhO?t/ۮ<"[kBb<\"  -zˮKs_6Ou. #?57kDĹ{yJ =' "YLy۲* 2g3d&-fѴQS/zTl 0JNe/޾g`͊;s"q#ؒ"H9upc i臣0 Cg̭i0M洣'T *5SͿ"2cIr(1-!D7 ]Іխo06K=MM7ms\m7KN?Hr>(e`,_B1rmq-[qNT+l`7*Ř7(5`CGqN>ڱߩNQ|V:+ #Ln$=%B׾M&\;O?iuD7ȑ1a3b?hّAF@[ 9p eX}/+5O_EB}u\߲i"3/@Ѐ{/49߮lX]}B[ʇ|3/]sN$/v;bJ q`R] 4=,#?픨i"9 n%g.j"Cm#q %IEOܪS⢙+tޮ]q a'qlxQ,2)z/r*TP/ΧQh4%||*B3GHJ䊟ϞbrA+?\bRN4-d:-0?/Nv (C@5kH1&Kt t^I}AkKf2 3"*9*g:~J9lĩDhBOg4g:cgƘ(9CK N3&t@RѮJTRT!JU{Ċu8bq^K͡cL wM4n ctaMkB˪|noPXT8tJ޾Lȁ!C 6f8rI!5 ɘ ٭awyGO d,l` )9;&wo7g_}ƦT4eS[|)` GO nxm(U9.5DW<"_C-[l-[$ .rSꇿP$bb Yе'4="Æ^q@Ն@֯eG-5ǯtWd<@M&uJYvA[w/nj{Wh| T^lJYQ}SL .p6bLqN+Έs;I݋2{Al/~?!*z`-2+ D]_$:v:m8'/XcKqq8K&EϷ-Ke} c~*DzMu+|=vZ@x6WHO`?{;̥ LGe2ֹmK ݽx/~ @qi rUN2>E?{)$ExTUA*5ƹQ 8'* 1W@Bc;fOH/F?ְ}/sfRL+x/Nb̓o*ȑDzۖikv~*)$Ysd^eV}r#HeG֭H>Txic,b82y\*B{p4L]pud_]i^ި<}?Zhueu J̶e".4s R?? lx, o&wJڑd]DYQehU}q!70cu CԵՏ(C_ƻXcQ}r:?Af &*9ڒ]خ |wmݹ/<<:G@a9wMҒr m9m@_6@xY @!B7/Q.We_Dctsȥ}#v )v}Mc\J4J HLs*4lbnbR*7{zmgI Ԟ˽`*_kETC9Ka~åNsNіKlYeQhV'#5}U[|8C"t照CX >]EOvuj_t Ι,tdOJm͑~@ܒrW.FvEѮ67ߐBI y 6i(̦zoл]&O)"\Aqagu]K2RMB#C?tp@`)d(9: 5ENg؃`Z*ÉieP0Dfٗ :CC=nX$-$7))[cvɂxGw_f/L\\.B2"|O֍G0ED@:;fEC6\s\QNNpV~ ^؆-o Ѷi)zT"uiZQC$lZ}=7W/oʣX$@ dF2̘eb edgJvt Ӎ/>Z82팞hj~-͗bq xnQ{'tƓ ̽(ys=HpׁwoʇȭBM>xOxp|EʲHT.[{B"wU&Jw[wo闻t"we~L@UnYUɑ/}mۨ1W`T =dy&aiѮp]Seٛohz73$1V mZM37AXm۟[ht7EMR}CsX9xLIv"D`[u5e9b'qu2mt c b ɽT&1p_g/ιS[1˽Gc\t5)zKsn f^Lʦ' M2UXyP9Ea ̔vd~iuMA1 ȔhP$Gr/=dEGfol0GmoL>jlWf!`< 8A{omd,9H{;|5AL颔ٜ(`rƀa;ۘ3G=BR_R3A f31Q+DGoYj@L1XCnܶg{͆:Ǖ'NUN%LSy^r%D$v,G j#hi[9&@():AՑ߰J joI5UL G#_wZ~vїܸ"Y*wu )gDQyI>3C@C& &Hq_8'1i[%L LT(ufu@ΊMw =u#"0f>Wy;(HKNq1Ъ~%2(w]u+Iҵ4SZ.+ } +ܶLds &#{?ކښd2Ss񑂏Zh0S*h;U.i|Jw.B lbϝ IO5i:}=5*nFx.)yЏAy@ gq QP!o6]~aS\C@ |Uh o1DƜxQݓr l}]/xJ)\́ yMɋ-lCEk3ĀR[JV]CZ)@S99n=W\HM:Ir31JH]8_"B۩+ED&/Ub/)OW=7W"Ni]^ _']ܷcjp4ykhpx[Cԧ$"hBRA[VxlܬNSi孖@|A)eyGJ"GT) n\{w(ec5_^@*X _~Q4 \eqL*PZ֕N޽}+J_}g3ԁeMJUv QS}?`NċdTh/Bgu5bht2~uRU9bPl5O>rPs9}2d?H-fnZsJsD)WD|Xh$tH><>+]̲{6m߱k6'aR692 [Bfَr׶[vM #i;yfEE$"BOw3~ʧ}P$֦v[ԐΘM i\Y@01rHU>d-m1EBkRL]d,Xiw8\J.eIյ }:YF̚*%,PQD2\oh IDAT%e4$8/PJ2;䪇u҂y2aSsTJVWJpdcc=l6p`vwd"/̲㴼AKOK=Ҹ¤ 8W]1DD.l)_$mx"I+thS,?q9`(\]- l|RlkfˮC 8D '2w?%Si $vi.2]؎C}C!C A; hh~UO`c/?5Uj*¯oG ?fmZRާ-zk(*TZg Tm{EG\bMiFܲ*!\{[/223Ǚ;!UW[{8w6éeKD?d@dcpww5Cf i0%@ CBl³plɂ($*7fwVp@ $n;ĤJ[_Xp&_UQN X(:{X: 3'?Dʷ#9gUI +90o_шSSq%MN8N1tEpr$V5XŔvSM0Q,j><Ҁ4 @CriGјN\d82.s(.. 4 ^t5n Uۈn_>D1 C<ʸ&bNL$lۜߦ:/8²k#dv2N,2X,i.wQHŒ*ʴɲv-ͧ?{/IG?FCേ!Tuyy{R!wWP2ա]s2.1 "g(1:/4@;~kFtW%ȸ=족ĔTwwrc6J*kp ᭨ڪ)T*G'f-drDs׳|ZmXy/̚.ඍ~mkElӲYiB<!C++EHbߣ4aLNamUW<<5$Lk:kx[,D2TF:+fOBKKO\v̡Z(wC$0 cѮ6-زfa."e(Իf[vpӆ `םh$u|X99UJׅi@N(EM{]R_F?Ӌ٦gʜޜF!zJ$u=7'aI_@*/MI}o^yƷ }$}\&e="}9Xm>XL4)3XDضƘ͉;xT'Ɛ1Gz 9"pH%ώ?lלodRK(dnGp}?c!kj%RWTH1:Wkwde$ %d"C7,=U@pŀ! A vCt\1[XdD.%cR[z-Ѓ1fr8/4.mx"u4tʛQlj5bjptw$ۚJIγKNlXMe-);'@Y[~#:BpA?T$3?NdipUɎMO:*LGg bC^y.?Fd(eѿveL/Q]QrzʘZ6.غ34"29 {~yo1ܭ˒*S>J6~Rc=2Xz"B@ef}+ gT@vB2ƧU Ym&ƙ&.޶PtqJ%XDW'>$׶Dn7>xp >Y2sպ5ޛrE~2Bi#}槖|'wo}hmd tiU5Ds}rPM.P׳OF@)]~b2$> >Dr!HEM{8 W^޼VIB!ݲlPk 6r-&ryr1U_ ^ w#,|[sd4HDw_dd +~'͸VLHg6߾'<6q" 0£V o{\q+A)gJ%/nR֝7PT\\eV9 ÌU"Q݉3ݴc?qeTN:;e~L*#c1J|IkgyҮ斧8ɻwTϤ3֥n..|[*]_r[=#z֮4;?Q) YM>>h3Y0X$tt[S[X2 B]Y6.47P"0 eR_:v!S9m- %1_^vFI*HَLgw?s3kvP,*x{sM ]>¦b] 7|Kc֬JىE  MskDɸvBF\ь!#ʞ?qޜE[?ֲҥnuqt*C$k)Vd ʠs+//Z:]J&F);xvlm,6n?_fp rk´;zխ{y @ W$Mb,ڽqmfa…@+J'ڍ1:Tx'">04[K B:5RI3c:u+'z>ït?Npե5.0 Pxh͍˲g<i_]{S0cg?7xTq^S\EnX^wu#wnzЄ;Ex q&,(i\T$ !w"#Wen*qX(V@$s_1S_+5#_"kvv, _[-ۗ1 `fzH3aupbţT Pj Tcw !*v|Eij`_~|^7eŞ1W{Xos{CX($B=?5O~F!kU[7.,lz[VON^^{3JEs )MHM3qI8%S$.\l_|1}Ap{ܹg(3Ρw~]r5V \w u-~9g8RpK_p SL)P)p[?)_L~Tʧ%2Y_5z¯|:麫 M[泯bC~>ςY2܂qC@ O^@dl"'Z5 Wǥb3>}h[mS#Cm wnzbD) -;gBMo׹~u}A"s[;7\6@v[" /O9,;@Httܚ̺H!)r7 +<^nۡ ),dc JdK-ݲPt!b! h*" iw T̲YHGf;flfVt՟bb^*~VoefXSɮl߹/22, CL8E"O@̼Ȟ#cHor><\D TS #n[oaLPF)W'*# " jAͻE 5Z>QRJYsVWNYS k'C"J0cSX2Nd#64ZT&V#-#p9jXk;Xkm/FkYb7qg`dBoR穨s~G`޷s+9kv8̳9"R?3)3E o|Tв9b`ͣɎDw*U=  8a=`\ 1>o`{?)b)*7OQꚑrYsm418&̪PkʅX9^׸0Y4,kν]kW:{A7,j9|̍(=r7^*cMl CFhp9,55w(7`<)`!3kvB)&|͊~<?;-;ŃE‘үU!9D.'u+7ws CX@J=}nL~*ΐ J17:S9v#c|R'~!D&X\-K 4o@Θ[ΟOlP: 3jb#|OL5A2~8 <V(kdޑ_V |' ʋ\d̓q|M \oYTxN%Ȳ)ܔ)}L)UJ)ǢIi~ЩOQ8j:t [.`|T|t, ׭MJNRIED@JL%L+iC&87iz}Z|,{XB-马36U0XWs< Gec[Ž|r?r|l} W緎_Em~Q}a;pDeEĥA޾مuΥ.NTa.dCC!욪T8|yKGǃm+UW<:(`^xWX9[V0E^ܱ?:^hkaꞾ8Epk^fLRq9#2j+J*Ɗ5p~AT?n9>Ss_/jupې-凩I;BdiJ.l:giy}sr.<^ LXOA B<^[O*ۑnI*lu ?|Ԃ7kH/,-ב~B3AE 2J.KM߰hY߅#z| 1,g@ˮSGBEkPY 5(wyOA92`1lXkb !︉>Ts0S̢y0)"@D uuCp:|$65HCHyWH-+p*kU,Rw3,*M/U| o]@=yE=dh|\ʱ%tAe/YԽfchBΤ"$@B]H}t\pw'ar=`ѷ+;Պ% ||ʉF Lw_X4)/@n;q'ǧqVyiI (1\IEr_e⋋G' !w➤b|j4BskzT &y(]%@ӱ'CRABF8Ī䕮0cJM- Q+d!`Y!JS,b3sEШ 9ʏXsjR|*]lj|G^{K{7g.w#"[TT$7WCѕ6|1WPNoi"#>mZò7L=OTR*G*QT&ƶߢ?Uj* XU'Ϭz"c: 5 C 9*S*Q WBT P!*d񡍏\4~@ mcn;ws .9IJSXw\<ST爚`qw/8c4~45[8II1E<6=uuْJd~lLJrOQhp8}-F% hoƙ @@}؞#d*9)X6U`C=F"r kuٿIRa*lI'eǿ2z/)tH\wʢ}37P ]GD.em s 49kN} r#ܢnZQ(/ل8P:h~?b.~7G# EGD\ީI1Ιfx<@Μ37|υXZX0oJ|+1))*\!OϷbTFwxJ1,0Hu9hxGE)Jw RW >Pٗ/e֯]\R#%Wʽ~c[F68{n#R+=^/+j"vosоuT|,likJ aH% 7,F&Gplm:4H"K=GDW BM>=3=c31{c6={{lBB-hڗV%վG,UE7Lܙv+2##"#";wc v&dEMB蜙pT:7ogr3'aHTBc%/ q޴K&[9{ߴ>9KU_I"!h CCS`ђuy_Tޏ{/m߲8GM&$'88F(TEaģnO>Q BFת̶l|3X=h2(lks{.X}^lܢde0,kbI(b jk.9:Gy9) @Ψ$2E<5H[_d!}(i]Tǩ͚_Yاԇc߻g^g(@Ko|X07p)bDtaÃ!oۊMуO=mimͣѸ@{ur=vF! *f#ly=lyrk/}gU\^Ը{oqRsۯC^'IJwj|~8wu ʑnU! Q37n]Œ^x|h w7c. H2VLMjۯoݵM+[?AӰEզX2uc^,:8IJ\ZьeL#Bhlxh8gr\׎v"=lo4VRCBddiFNwb*l|q+{+$@WU Ut_Chd^ݞY< {na=VK#fٮX@(1Qɖ6` )t-40ܬ7Zm.=xZ2n0C\v%X2]ˠK *1WPC@׍B!ɭHHD2=s뺀wq&8r9"?=r2Rj^YMRa,aeĝ 0&P<8G.{#1cr7I6EBK# ~k VSNi@bPЭ˖>> ew0IFk*/G:x2fm5u*HR[1ͯ&xX:RfMS O>zgKl/i؞t1b ,[ ͚ %rT9P\  O׀vJ%` C)4" U|-4AU`~퐶{gj=D w5̆;ncxdXʆbM稓Hy<鯜"C*AJZ聣E|Qz6? AkyKTr]ɉݫ`&(ceCmݽh٪{@K!MnɿSKU@Svr~7V~ƮLWTMgkOI~T8Qu RfɝtX >9s^BR }Wes $1&B3}@}4!NiYLT7]TۦmV$^l2 ' D0w:lۍT! 〜iŢtvsS#_}tM!(_.\N4L,ۛS|c"?|=HKW-gYV!dAkjZ+plK I<׏}c4tXDK:GCqpD*_BF!|GEskV>^F˭3JjuA GVR{?]Q=sZ;t!hesY#0 Y7^@TnnHE{G>eS`Ob<:qSʢ?xQ5Օnj|ۍ8Om\d Z.)[}!t0G#"_M/N~S-ׄ:΢R卄Y:}DbEΩdsC=̲s, T> 6"C:S1xV-Qձ_J[HoҴ}7ǥAJ^D~v,tk&,<uccC*NgPd!CZ><.BDл&喸- 4;{umǯ-@@X|wډz^)'(_3d ))S}ym*i$5Yxabс<{>:#hssq 7 uuG3YY(elbQa5&Ϊdwz=75tڋSgʲDdgG[rB71tSE:{+}0afZ@tD#K+.;fлoL4*HXRO qe%MuWt68&9#F\{D rcSm̉xAhv4Rl/Nn-(֥h Jsq{hYzzj#Wa;4D]4a8aYx&t 8)kٞOV0CO?|t|@*Sͻen݃PoaC\.eDa `f V%`w?;vJڄkv2>ɉM;uC4[0]Wn=sVԲh|9`*U ؿ̩ N`TQs;tܺ2'#{, lqDߗ7T`nܚU [|-.@)>*gQ*y 0;$.@2RB:#!`=}dZ*h 69k#qkb ABlT*,_@eC#A6Z_؂˙FsAl )e *q3F2]#?|J g^8q)G_ [8! T@D3&%3g^5%5?}Y*_񓶔Vxr7d!./R%R~Nw>ygG76a>~8J*_]?ЩrH9*ޱDOL2Yq%D/3FB3 IטRhצ'/\Zki+?=FãF7Wyk=_J~3I*{$#ro8| mzzo?zH ?@[BWe8= ~e.0*b ̳ݖ-d*a,]f+q7jŖu}X;w{?];R VRR$4UB35UBr9/pTNfAdb9Лˊߴ#͟i;1pRd->o2io,)A@z`o8WşwrSxqu"4-Ą ܂yYQG;O%@!#T(,q +_oMܷɻVqpm## V lc_1&=VFe-=pulY~Ep|` &4Qy"4W0$t> g.Y꼠&50 N*"*֤lwi#X2d'NlA抔/\J`j(B/m1ŝ N,S%C Q~Ca|?>lM>ȆYSU.}:P9*er?)LaKƎRwD}Vbnr;>1ކ 'n@mRHHLj҆G/^ -vHEH, Q50uHU,T("B&[FR &⁑p*qǵcΰHXvI*N))`U*}Dj^c%L KBQIGf˶-@maQwҾhpn#oRiuut|NLwnN>tk{M8b Qs)#!u _1t%,hYDiSF/;iv2M[Z~_e#* x=UjJ+& nS:{oag;e,UCkjp|C+t?Yvȼi9Ik͜l X2w0'y@S[Qcqέ%r*?%b@<"ac$в8pʟ0cfI |*hF﵏t(DECȀXkIW^$)L{ sU`6ŘɹɵR"_\pDTK-Zg7BTs'aÏ ߺ4ܗ/&XVDO Cw4<^v:z WK]KN5`SU@dM+5%~4< #(K}D5(`I!#N"_F*["D"kE}'6"#NU8z'qE%qV~@"*E\ִ7HJR:h!S@/:u#mLq k7 DTHtx<_İYsSϣ]+u뭒e %ㆶVc5I/ ͝F!׽v6mQ$ ϟ){#/15UeUySvLMfلFO +`,;cJzܖMIg 9Jgg#(tL%'a)oи} 'T/ c c&㲱WblӫLb 5RYCFЈJ&QETKn^Z0KA D<j➛!cmUwnrͨŚdXbQ:n*7$iZ(Ip:z?}[L*Pr50,Ϝ7!*@ Djŋimܶ}spH  pH "_{$ce2o[8M;݉WHţd̲is@+RYB==? RcohHJ Aoٸr蔊WPsH b =i3̞Qwj=ItGCD",<רPuo0+ih5HB.] Cgc k:uQ'~$ 55_z9ܧPe3Vɋ е5Sxo~i.2ҴВ5`,gP8 KQ IDAT|zj!4-#kַ1_pK DmJwK韞 %KHS)`?J$ISʲ[͋^~X4+J)ƕE $׹ 縞*f G eRgL]8wfb]mv9|3ݧyD$j]ֺi[Ue(1-yT?r|H?@PJ@Ð-, \BJ>8"a4MBTPWw\u̶esC􃏙i:gSX0Ft*lƟE[m[SzD8$O$^2-="_p")  _wy6jhp$4~L9r8{zTTomN ܵWY'ݯneKi"[_~[BZR ø[!E;h~Lf틦\?}'έ}CYKZUS$oAyXbf/maŜ4~ȟQn-9+Q]3HGbSIX"Bh_= `w5YE5! %[Jt#i!K*gM: C@@YW/)sURљNwxQ!c^6kndnsPO#Ti+Xdt֮KFy/mr_8y~+Hw>x:<wcl\. 4=ֱ32GA{=p6oLM\J-9BE%$H2 #!&hv! ?O6yEQ 2Zu C=} >!%=*e= %P l]Y}x$c9BTMu#!|lrzkA@EIg70t_!}guJ#TdAɁKËaVyOAG0t5GLuPYSF,LS*x[%xs=Mj pl8iܺB[0>{A?w@!g[V&_xS^)[)0?{~%k(mIi eIfM־PcBm4?xQef[;w?}V|>xgߙ>,z76s.kBUَOoUxw_9t̥*T Ni7?J#@NuR.O^-)'y-ax|2#" KX,A< yϻ.,; ^B|Ёo˺P,Y@g8z3* imM;Ѳ!DUsoP },#vC,Y ]Bŝ3g}ɳnv|^*xԮ)/uOU+A @jW;EEaR l[,D.њg7 } !XR͊a|_\ܞoLT2TFW4rGeZd/t9F S㓃X[BK"'b"Hlw߬@cjS/]DaP7pEN.vIDAHrW3/Bh+@gKNJZcTAF6qPR(t=N)UⲋǸmsF,7_#w]}0 .`+tnƑL)kES=rT1랛 [~P-/97_~<}/~ﺐJ(laq[,u 4tN85'2tuNU> ng5+yksyhƴV 8n^:/UYk?"'j0*i<}uw,'bܾ '&+4|,5% l믚CX->Xs  Wk*PG7|ㅢ]N jR](i u">p l OŊu.k; 6cVr-R<.CFv*zn u  vD܎O׳Onoz=ac=!D6T (UWMҘ65 _qJȚD7ݷ?~FO畲-6  ]KOA3BNj6y҃@zmS]GE\"B&V(/_ zI?(*D8& G2W 0ї.+Qr]J?wSD"[J)MK~᎚[W$;L]\8|n78 ׿f !FG\93Oڦ6cW^=^k /с؜Ͳ]o#-w_,~T=E6j-߻kFCqR[9󍰱a*4g}ƳIؾ~yܣS陹36r)܌07M$DpUuy-`,wG6t1M6<x rklXg$JEhY2TvSaXA/R׿kD֬:΢R@(&i=}2>o3Fm48*39JWrsghܴ|3եR/_ m)wlBMyW"9-a+›ZJcse zq2D@Tmx|&IrBѶU8hA QI`dCS9ᓘ'GLW{ʼn'"tP1!̩BU8))@@Bv";|k4̱ϵ' +Pgs&NcN ҙ.D=?B|0jSONi| w>ZVK˰9SRh6'6D&zB(wOfԹ.u)K> ummL[^Ɗg'7mbUS%D|3vh{\Rr> Pw۾D_68=qbќWWݽlTaÉxvG; ^C#Spx^NG*"q0ƙ^&<+.c935nh!T -0RhU |cx.ӹyџ=+U>4"'9kHbծJ jSB(I.j!!C"oADUWÆF-Y -K:Ѳ9pDSjݓפ5<[5<1qfy⃷E~K*PPoE 4teP:nBP*x Xh(J(淑3eYx!ֵ6.@L}Q83~^GoyHg0̩PP.!g+)$@}RLس˨9$׶7.JPy&[*Reְ͜yToJ R67Bҹno :ٌ˙TM]uB*UP+l~W$4qx!_sE*zimU |By,w='Tb4MPc1*IBذUXr;&Jt~:yatbP:ilkGD*y X3s*@"qQyM'Ck X&]}NTq]2|r^SCD h$ B*FUgRX*2YF'HJKhO"HWJ.𚚵I)Ÿ=Hk7}örvk ,w~#VK~7϶XjtvkG;4]>GyК^fo:fKp'񃷨+ 25^]'דɳ\!*`,7wf~Ԇwe9K=PjS̮T*UjhBpjDd^{ExnT*x<ɻW^yqsTNJT"=xTEW3'L9C<#XG%ӟo:v#J@զDKc1-L=TqGYwިϪI|&'E^ہp92E}ˣ{JA|'U @1BSգ*ߎ*'|:"@:5`J mMpdQ&D9o*J ECm]Y  "X|^8bLr& C" >f 8s2Zp okf@.TT04,M Pv {\ T&Do5@gc/a ZTOG(5}r-(օ~da<:oK [stIҋѓHGHSe\ϻЈk+e6]?SHV /  ڿ7P; ~oh+糖K/𺎀ѷ3Jb~8çt2SMW|@UN_(&W67j/'8͟>E?A%D)$g [[]3lA F2&ol4#K6!䔟7 yrUt PX,.u4ptVM|xԹEh.6N]:O9_.Oir(U%穼=+bxW/]%>;;/kTAkjxTq>oF~[03ڋvq밻W oKn}[%wv(bL/kZhOJ/_,CɭzIsav,]<_ĢvMr*ZO>m Lg07C:YWJ]ݹn0Nb%LY66 B*%JRO xub\ɓ?"SncR" ̛{ȞyXGARfѵ>v.H&ݹ9nܭԂH͙z} +־f}Vf]Pᅴef hZ8fNvR( T0 =RwF”3=2?0mr`cHO3,9xb>Š\Aٜ S@@.rY ?HB WT f\t^D]G,_ xo?I ڳ7lEvH4!GQIGU2!QTJHru6Ț$bB**I<%Mզ zB:d @RIgV`;5e(s?z8'CF8m͡ఛnW:k6E&w_|{trDlzi_|{P'@THj\f/'bQ+`Xvfqd1K57{?e,yg&<-@I{nd-[|RBD 7$OP9!q Aݓ/Xc6x?\w@?aÿ6D~Apϟ)#Qj죃(@QWOD^* UP) ^ ҵwGJyzm[&e!h.>L,w ^5@숆էGuKU{Tvx?}=ȋh,A ]LQ bQ5pV\bER=@x]ݷDH"` IDAT_H2_ܻ?|+:W2R&Be/gG#v*2YE1h` p+T2%W'A+Jk"d `_䬌#gى1` 8StM|`GNcc@4 m">4btܼ2q[,ndۙEj|[4<]d FERs7QqɂĎ_5FG㉚>GӜ@AY"6i*G֎߽?ҷMɗxmTH+yN U,s VTFdKEΛ#+{;ת?PxW:e͵cQterG}H"Z ΂^YTSE2޾߁6c@'DvGE2LL rgZjzwtMt:/z]cdhdЭd0}jnִB]Mi`u(D eYhYU2iem$ J!# g;y߀$FN67A@%檫YcmOhl Q"U+R@Fr_K{y;="et:},tխ0%zۄ-]G)[h/1.im9i7ţ-߿'JgMmLn keR[UԤ 8(UVNeE('} ba41Xu#l}#Pbz{|(b{/3zLf22zcGN MBPbѺxq֙ \88qƿZ0ܪkY[f* z\tG(JΊwܠ|Sqmzy}`-ɏ6e4,Ե̟ާ?='5a2:Ԧۯ.,( uS+|2)BKDT2iIC/_B>UrJBUq TN-F% k7c@LN'FdT?;2UWz7BJ1c2xBD~GeF/[HꝽT,eTux|mTJqY~14d<ˋ%M]O˨x轫CP vǁB̟ڲ $gb*tew#^XBH@y11&z=Mץ((@ Iyu~/}tU?Nk+Wf~ h $,1̟ ~[JOӈʱ34?%2Ys_vfbhL&@j3MHwrEHBܹ3Q+k5 dJm oK\J+i~ KKs\H qs-O!+gF}3L5泯4`c6\)4. edGK 7G)D1xS꓌0d.oMοtj~;hM/.iE(\^ r^>N@3Z#e2nJsvWMJ~#+MU~2k@(5|]_A K\F(B@3`⩗eAn. VGBBy"ҷ^R{!K/%,[[Ir5MP&9X-gyC&;Sk*"JJ {Ke";%Xߤ5*l!]zݨ\.l;qZ:qFu ]'S'_ȋgEf}|4,h2n0H L]BB^rk$PYa3s<GԞ嶼o#¹GIJ8GuAׇGIAGJlpxD7/vkBhiJ8ީ?R"\{y|G5|f?@F,d,䈍KGIι+#d@)cs;JS oGeg<3o7s:AMc`Sz??S NuJ.^HNC?}O HJ@Fk ^u~3{hLmyWzø{1 E{aƅtop0@^bn8|!}l 鮅H|9ԉ-Ƃ^ !Di 1(rT.PaHe@h< V۰K&EnO5Yĥ^"ę73f{93"rE<.R)ucd#(fL6}LI0N^W:nsJH!l#my]oE#%= bhcr@ ;%DҳkQH@JD>쫽;ȅs@Mž٫-ؾ(E@DR YӐ v,Jh S#>!F+JsQ `.R+=/i1VVӠXQ. I֖amG&@*S>8|RS=o~0KaxYC~YTd/%VRut9s(]IA;_](Q 0 o`ٗpS#umyhhh5Ju"B6XtvAt+DJ61Cfe`+_~CXpI(HOY:u^iI>1yLyama,vv踁S’ƣb|HGjkq FtJ.W*ӶGJF[`ιnu ӰS;r7~{O}Rx%䔖_J /ӎ ~UD>ԙzà5hHLpMC{8")"8DR! ]W.Ųj̨IqWrR Up`4BqMۼpQjFzs@%!6;l@,[MV,tۑmCc̐-AT0 "N\w$~uwSJu)ɦUjy.|LϘJǻ;O /1*l.$9D\m0N^o[gqsJ g# 6 y֐Nb8&mۮ%@Nc |!sN BZDU̙9 'Тk9%E[("j'fۈ+bfnvY'j*EdkgN*QkE('tڼۗLA`2A^1_;>|#K.;ݯfhΪW:܎}PjVh S^>"D4[X!X\ 6.i5={ԲH^ hl%3oIgB_Q*?d.D߲Ͽ%,R$Α-}~Tpޚތ[Mɏ>wv{~H puԼ$8t"ĝE ܜisPzȦ!KT>G!Y-$ȦC/h)d\|B`mu4fEWa{>?[<C&5]bJWbo$[ 7,+d426P^c|0&sN5JjE!@H$JR{ ھm/ Uez/""Pz%pVoRe\'I қAKthJ5E{ "[:\V}`yjqN+WCʅ.P^ȟ>UY*s)]LSJI] sAΛE}\ߺ׽a(bOų5 L%둻0B H6\BH΍WK?}`0,UZRa\ɝ $cZ*@Jp,}7uBQ7ɤy'h+Wb/vOH^0&q L$zʓ;Aչ*5U ' A+WSI?[J4pq&ϘV d2an߉yjXBF4opL*g c.LCFȨoKJlg^1!3*@6sZk[듻{Gʪ|TpB4Nb?<x(EC.sf$)cşaO0?dk@67+.vS= .dXkmRk.;3w͛&_۠Dzğ7yr}ϑfCMg%׈𧱝ZB 1=%ѓҧP9PpK "r_J}!hE)T~B(Vmh8h|!*`LEeusl\28{1e]AC@3ETE".%_ ۶\;N +@8*FЍġI)JRoHS'D)t\i}<"LˎYhoBvōuob'1T紩Z2ҧ^Q:=e)݉ovv)oK ¯橳Բks.H c<mjB5o3mU,5ݹ?{*]Ͼv ȖdTՅ4 F + &ևF ݇.ēBG2ZoIr{P}s|.]JF= _LTX?N.56Ic&g"3jc4k$ܕ25ejV!+mD'OzhX#F{G;ФgꍆAF (7@"?ޚFP٨PINK?|}P(Tu*(z7[R+^y nBfl}sdx!ܾ8n"t?lhĞ-HϦ ]zK.ސ\FF{ڤp#4 m`euXթ7Mh 962q\`, IQ 6" Ji9(`KɹAjS.(B H>ҢFPa!k,I9FZзȡb0@c>t1~#)F T^`cْ.X C!o}P|Z|tjԛ CD;Ӷ^#ICƓw4%r"3E e0z*ݝlآęڭ<)̳; ~1X6jd2qgO!cY|s@T00Z^q[-"(>Ť-3jŊwݢ:fuWp\.$KgqOGtM?<lxW./9|ZZQ ;%7ӽ<-P`TدH/McDУ'd^| 9AM5M+wq踱`76RۨVfO~=mb(L+QWĎ ݣp :dzi-x6Êe4H9ăHce5.g zibtoS8O_D2s@V. f꫗&ֿEJT D* c/.ZxuFGV-3ʄTy:JekBHx{Հ ш * -iIqȤ]"|(8zZa IDAT?oTӰB6s)"f*JdJuW&rbFiv}L*~rFөHVǞ~Nu0`umjR1{n*ܵFT@` Ԟ%}?qɦArG;Tc):۪oMn~Gr\uiqͶuGB7?Bl# Ϯ'1xدi'U|JT?HBlx5+R\+-sFJ).esIbyE2+n~ek~dۖd+zd0~4=*K]Ҳ5rASQBfL1>+\Ӷi]03q/ * Z=˛(D3D)O?ZZ}]XO_B}[I@{ j>FL"Sq34儾;H8`@N,nO9R 6<(ZC>kv;CdDQֽDdր"ZEI^+b57N~WLe[^q)5@\뢷_ 1Fxop$5y*F*r8;cUQ`ar;ҕظUOw-% :-;\'چ/nm3Cr5ܳd\;{N<ȹ$ҡXl>}'J; ?DZx 8pȖǿt̓/#7][sXtKi46C?~~i3s=h(0{w n6]r2423@ƅT>~6?nK5 bT?>?+䎉C&~?RS7Ͻ*(a:[ظmhŇTJSaU%ھ#ltTƈ"RR3\m,2RSGKG=뎦Pju Q/)t;n{c8;+o2jF!:;so}@ Nͨ<4/ܬXv;ۤ!H&rɤa`_t>" "q0 /#\;z2J X&k#.g#%\Pwf|/Q"QP)"ܚ5;)M[F#VF@b;"gH&vXt]Asӆ$cu0j(jud"ryZͧR~bX-]_ @մ R[yRg]/_wY9!6Rjۼm޻6|:%Tl вo/ľƯE d4'wY։^SIſӻ6nVg?X+'@DFH*UD,޵>RҨu@DI?//-}8>f)?ag׽#E(rt*m{ӿT?K@hi AIј@7ESXV"xh $~%k8^ 4&Λ%# q!tQޖ3> |GY|!/iB 0rU?7lcC'4WCB v%8h}kЁaͬ9itJJ1|3GV4Oo 3Dڬ 7&Gz ஐ!N{#+[??9"j /Sƴ8M2^(d`Jq=r hP3Qd屍omooSJRh\^|vAآkrGYUU ̤Dw"kUˌ4,U{le˯}M5cRֆ7( DAr85hjS-]$3tӵXd?hMf#" "98}MWWAMCͰ=DV/MK`1&Z7)! "lӯ"  8JRO,W"N3}Dy(> H!e{uO&7nвU=@.Y\PN "pldi16P'b94RڅgA$]LQ{}-8e~z~q]{7 6#A{32BƘ7"(ك7WXf9;"9v-zRAR"tCdScb]^})]a"C]p\ hcBJ J7!Z־`)TfdO'iݝ\x!g"r{^$D{ksg [!!9|=D: .8ƀV?zȲsd;zHI5k4f .$E#eȃjOtwU|3b?_M'f#$C0/O{RZ$ԿIBS:w-t^y`kv!RJF!BE6.Z(;7DҦ+ey)]]ST} t04K+3-WĎ$ ZuTS͠QpĠe)!-"<}^FAIT]*IJjC_~=_S]V.\%fM%Rap@18޿^P("x _-oڈ%\54$wGPr)iâ..mTmҲt*dα_}+H gdrnz Uk Yh,ӑbYZAR[0?ɓNN4] TF ~rH+1`bwHhyKK<1w!|ݺsaQiͤhEt0%3L60D/MH鲋\3 -\̤kV&xA"V̄5ѽ) (7]glƄUtoAJ=pLTXtwr].瞗WBl3oG%Tr?,+iZ1) ͳ WMXBGVWcK! Q"u]΢y{o|%)%Fv디:Ͼ^PJ_&U1VMمߡ <~&E(II CɄcS M鄯6R$;rXlmt\i;97HlKooM$ c&@p#DbO 4,v\mղ7IZeT@Zys)E2ܾ  Vpx[I)4\HʅKrR)$c@r1Xo~j4)1 'F]ďwV.awH]lX8 7ƯL.1ﺔzO_be淼 5(0 طi[IF( :I--#P#6H$VW>ms tݕ٧^DaBߠQaY6lU(U-׹.Lj~R }Gsrܝ;04fx1o t9kx2p֏k9\6a4"ƺx;<32^)'~ƴlCʁB99W/Hn~gkɨ"JԖݒ甋D+Y~zkп\ v9T^thKJo}aKν~$!rp$xa$BP IHm[ʮ_bϛo?1k,#$HFcRcBDL}4h /^3Vlj9A-Zq:^qSB1@(ŒQz{4sLn(1QDj\¦\&.bA "{V0 Wި\<+~4 1RDr)H`9N!S?a/FU׉A# w#O܌. cw8^{/~BIc}7WK[B͟cM6N>Ijmgӌb)đD|H% yhi/5%Rٞ)i$(AU(n&:p:~JTg=.r5^ Bųء𑏔7ue`1!"%3ꚦ5Vo Pŋzb0b (!D'֞uQ/c MF}BFr|R ōJ$&}] K@H%xWu4HNנaAȜ"P]S %.j 1䜔+R5>&DEn @64.Ͼoʮ߬ǖw:^ -)jĆE&M׋8$$^~ۧQ"( qv;, U>X4p8rXmb;垫*H$e`NCF  BA ?~|aMȮ~IQoxWQ&wM{M01,L`#%bY0T8LFrk֨SL-__.F5ʅWYaf( 6tE.ӷPJC{:7 fr}W/(y)XP{L>ً\` өط1W@!d G~5bϼBZJŨג`m̎[ߎ2yg)rɝaTYxϦapă* @N;1{g0P<[-gPKh0Fށޥw^V F= /f݉;id<*5%AmjY@,J$c܋%7!ӝuo@.(sމ &!+)933)LU.qMLr@2'}~a:K6p- ƴbM&X!%A@˨\g"+WZ:9T#x0y!uDlS<_ϔT2 JM@r IDATR'KoQ *qtؑhYrqq9pB6%VF kh{AR • 1YoV_>爷xO;ǟVt % @HiEݩ [n*۾ɛ1*@_Hgy/bڹ]#+nn_<2KR!åp4Ǚ4,M!"z-{P CC9~ڠhFxF rեd3Ӧ66O{MJ~EFf%'_TA0jF` @0*q=߸ IA,Ub:&d2h0 ; m|R}rrEѓcZhާ:+9x7ij)丐q&pJ6 9 ۑ>Tvc1ŖWjV"5@$b==D+O@P!%1ɘ5ih<͚.,ARmsN[>4ʹ [rr`;22%Maf@Dވxjh@@ A}% ?< AS*w!01 :FE 0]2ך>8vJ9VMGp;}CȨ_)8hܙLĵ0mTۇ& ݾsoY Zm^i$3 @u񵉧9]SUotꁢP`w< RBV>_@&u" n&izLŻ}Fݞ4(R ILcN.F;|zPeL6;Cc){ϖaQ(#Q|tUr(_5|ŠG(ų2oG5*"()q-]ع~ӯA3ƭ74{ ٌRcԹXރŅ j;Js(8ѧJ@R&ډ3?q(t+o{5Zk4KSD%ͳ^^H۴(߿D D3EK#S&~F?NGp=@{1KRo#3Ͻfnj4CA(E$\otd g y]*hL't_=ZYq $g/n֎$2:ǴD6%RUF)Z ٖ3F-7>Zr"A\O02<OUTqB+UV9~oJqZ7fJcrBǕ LnMR^.Y37Em)lT[cFoJ5mA+ !Pm4lRj͚i?͊;H&Op~.T`Q P0f/_Fe ;hO?O\=ں-հ"J!^GT]umXR@x$RDHy* XHܸ6Iuttˮ^;>\K{,;I3Go֥#TB?JN{4vtMV_5F7 A?y(fU#ʭC^QjLI#˟W.ae^|M?|qUR57Xq>N59V(DpuJA/53O`LTt zoԝȽВ%7F~#yHSIR*kOW;z6rŬTnZ1#/M$bOɦUq][Z@eE@zߪmcI]#yMqOċ b&!/%~1VjSaj/JdZ۝?j(_2h`;!!P,uW,fC W/Mf!X.ϻ {E-W />zJ*Rp98h޻q.qmp/`.B>aMR& 6q0( SܙA tMρ}M/~SF/EDZy`EP%qMAi"Jgwmx($eHo<4_Y.y9wsQ1(=¤Gp oNIi: qv#+[uV_1qRIj|4,U[ 'u؉X$53o+OowycDr!J]oi泝Bfд Bn>B"uN?]q@"u T񦐌;I&Mѕ]D(U8jW rra")Q %G.8leT&>܀!g&_%* &>x^ud@_4J-˘uC }hd+$d3lhj9*-mpr@gϕ+tۂCclT1Xn:ܘx R%>J_b>4N>.7mKh,Z!"4+hj?viI~^eㅜ\}yͱMF Fյ?}y Tjg$ #%+HCwQ9:G_:g=g?k6g\ב.Um $H4=)zi4̼?f#^؈coو7;1#FEу$h:'$Z߶|]sNk 93v tխkϗ֧ymzqLj*m'^o6TS$uۤR6Rӕ8QA)\a9IҮfo*.8m pyiZTomG弻nB09n_ִ1[ eR$tJ7~l9k2i;&Tt9T ̝+򤜪@1 Xq Hx}l~H2 @qMN\ C+o@zQ<vR yV.hd tƦSz"x\^R[k` +ߡz'-[5)N}Ug{Se43mOyeUk%@f֧P*PeTf{j|N— u}8ovM>lȰ[*Y&ҎKfRV*:umҟ~:t$h bs7*Dљ6;( T\i[c/eqV''_,^j? ٢*>* DȤv< ,2uXJ;y-*H`@ P K+.Ӣw,]NP"hZq/yڱ˾( .Zu"u큢MB 90͝o }(CL;_po-mN-12Ee AE8a@@J|/Y?{nM;)DpK Q1gJ ַŚh9̾FTI#7j RgR՚z Y7l K@ǸciX IeE~I@ȫFǟG%/kp?0(Duv@=FJ]Xmthc!pK˥wy T} I38[9lQd\\FevGNHj ArQt:@#/v$": {XB ;~Lr ?MfՉ!g/m}`QqŦuZBLآBM])ACJסcy{2 ]CNL.~莾iE`ѽnE_֢UG@!@Ӕi X&:h@c2!@h PܵF64"YGz@@}`z3iK?y[XJƲ RUNyScrZ ED~iL_}ydpk!z='8mq$hɘ Tmۂ8@)$9.y-seNL˓S}۵?_rgvl@/*ov$tnY"=![Cv; }d݄SQ$c6 mp~ a؅,5NK  {lXFGOwힽ;RM yDqb'A!16qf-a$%ځ3o7.8ǚW 3ͫw.\F{w}O^̂G:ia^NXe>ظN;qӔr? )\5*'gL㞛(ެD~4qع1,ϼg5*KNJ,l:_jo )Qg*Tr@;Ƶ>Y/SΦ PDe\\yf &)b%)K0> {1ڑ-RuVʼnX *FZ81]!a̡~9urg32w,?nj#0y89 k+/1Lެk#lƻ`k;w^w/̾CU@Y]];"]Wz2 cb- pKVa {&z{ݷ`*Y[Tq9DdӨ\v|0zUbEI@l[U:tzֽNuqe/ R 7/RY4Z|2{e21 6TH4ݑ>O9auZ, 9,7o3c|pp]4(O#c25[]~?@f[@p3y$ܡ19uIiS32ERװтH 4ize.bCgq_< 2e wSfRįy#ۯd ݾ:Ks'\TB`Ajj~̏L_RqE0F4PJܜ0Xśñ tJ+|]Pu 8/ ?M'`9~@@FH r#x5u# 5AQv8B$%b 11!M\:7+[L՚2Dʤ iKVq as$ܼ kd{$VBV! GwE͡CP1-@Dֵ^' }|Їqcz`QFWW<:%6 9P`/ed;iiX'D~|PtFlŃ~)jأ1YO'䴅F/w~MeTu}qFd~UkdW; IDATr]Yȱ}}VD\>ˇjU\1cFBbC^ul&Ie>9(w zJU?oS(SdRʝ7$P)?ZHSߺ}7bj#75w;lQWEy&\5~yRFdUp7q5 -&^LޢhM#C#Z5G&&49ec?:ǀ4~k nvuQRb Yi߸n>fvŇyfb6'n,kz ;6aX) J[9ï\K$*/I BPB_Q@ ±I]odWB/9JVy`G @ߒhԌZ9BNJXP4hp!s]yaw/Zx[5 ~RgRV~J򙞴,ly Xg{ILsA1#/8:Vr;WDw4sϭx0Xdd:%cJ"+G{!D [,(ņڊ!~MAD] WDf@9)R4D"k(aU0ÅPJP`yH|IZ¯/~Ck!b?Jf0 B߰zWeF(ɌI1ƧDRlZ4+wLhyL{' AjdPJ,DO4FG'QJ:SKΓɊAQS OJ!ge^>gB{Ű5Usfɝ_lo0B^8;FPoカd.8ի,ruc.E%S_7d!ii./a$2)VAC_Ңf uu- @ e..XbUPP2LJҥ PVIY+? Y% ZǏc .FB4ߢRWެoZzzWB#)J s(.8;cWBƦ 7w˃y*EG6ha"DA$:,frx&͏'ٶuV`4{LDDNdt}ް6w|b4ȹZ}?0 ~SÓQ$X_,+9@& ,ZmNԁ,]'t0u4J<:κGn{sTybn}}s|jeQ ZeQ\к݉ܡ2xZo==e?X'"o+䦾wȯf$+=`k`rZ6~$MlKzD52sU;=7Rb_P<\J'P ,33\.Q)ȦSsUcI\i nAWM!vA]@t$2C',D@$w!=:NMe9.!3QA SOL-Ebc˞<Ek<9pF'N̢u+;wOih{dYqQ;$Y /ڢòC;+SBsӆƖ@S0{ `Sx{e]h2ͩ۝=Y27\:#:Ce:eLK: !zCz|0cn|:'0# ~unަMUb.i?+C.j=>qd5Z˝N ?$[UhB K*Tk@Jȳ#\^{X!p\}zV HWTA 4Z&jXNE@@-M]wNT89buYxɯ~x@@RV#;wf@IZ'dl |:-FP6 rŖjwf_ގ~59GTA+1Wq˘EJpSpXl$U|/lgg}e{e؃g'BmmO֯,^XbN'ko,ز#p]frFC]cNLTB#&rKE{,'"0yZetUOgH#Is@T;T@d'\z?D`]b ?_ܧ'"@@f'*¿`vCCޞ~Yq!c7TbOMJYb@BVA´ ?U~1iF$&-p! b a0_yvV_o<%zX3P'Xl&$e{-bbΚ!>yױZ!դbYP#$QG]ܸ-5>pX^ 2H%jl>cQc`G#zlrX!S"z_%՟=籐&r]eHX4alHAJ1承6Fs''^eɗ*KʤX*,uoHQ$$2`t(]ӎk 7)ǖQX/O }\d :J3swgRFU=Ӡf,G/$0KKu}櫭>0F\w 8R#q -LzGz)mv>ϸ , w l"%.# *Duy{?6Z{ӑP^},E;C;>! lzϻ(UB ,J 4 :i}ƚ#D];9PX)j#Sw>jԸbވRJ6F3c*l̤Kou & ntѷ\uYi)ctIM?=&](%zb@COJ(l|~2R*\kn>@_b}'p84f{25^_'(X<ɻ!H#$%!bbۅd/?D@2('E#OㆈiTSVuIU O0jjF(aJZe#E-c'g#t]7f:Nĕ>+ZRylM23)W3l_#ᅬizqG`C< E48 Q44+Z8)ShYP ![ӴWiJ[]ީb`gSssUCO S>`IZZːUHT٫/BO"Jɹ,/4@8n,-W|]w0)&}Ү68Τh_phFZe;ivHx}aB!u[fa(M#C%*wSFR%MׄTUʜ"kP^|u!T{!N܊wXsR |Ҋ˟$-g/B#rʊE+@"̦'g+.EKJ&bBBVRO4][cD92΂t- ?w5Qվۧ2锡3W[ օv5,';8!CW~J^6Y58gNu3%rK./ nK4ffD?A\ isS"^YfzЀ9-eUn&}bNB6,D7M4j]ŴY{9l 䮅JwP9g-3/JWAXDVE!"4Žd1W e;S0y!0\ܕ#tF<j#̤~LdÃ4=Da&i ILMxuz|)֠Jl*K 1Dewxuv$O&xVLJ-YjWEdX6W:UQIi#ƪәbv؟S1B\1y,:T;gQt`A}U7 3wu}<o3ӱq56Ԣۧ|p9:ځTq7Zl\6*IZyQߟR6ǧSQ2bܳ:+G;+Fъy=zZ$Y"NƵ#۟Od.Y2Lg6ٻoʾ艞 oܱۗ~3(ӠvB4t󺭪2O"Fijz7oMz+ C=⹫;~Ad.y^ȒEVC D抯<ɧ)|6!Ȫ@!ML[Tcd;3::l:5A/J%EJ zrdL+/zRi1Gd>l a˱qa~:HCTw`k.7fWgސuVtJhQVd?13!./:{hqW5^;̗/HThkNL]¶Q2=&U!Tj[y\zehwFt^KP-Vmײ^} )]zIV "َW*FE:sT2&gfaO_C_?Q(3i@)'JF$)k}&nY(x8+ݿ3Tns=TOpFĚ6wǍWc=Zf3,V^h\ $T; EI$d@B 8*֭BBe0%<ϡ6&J뎍,V*29bZDf4IՊ^BYDKί1~Ŷc[ٖuRɜbD:R e:h ABD?UϤu? g8]"[8_?4T7Ty7^(ukHJRJ՝WyzuB0ߏu 9ڴ.]_7Zs'?_߽X?KoBBC%Uҩ`vYfgki_y$pXPr']Շ{r͜ƖAk43&D'\\)]뵹yfrJR4Ze5(0,gf%aBwdʢZ3b)R"|6QfdRF"{WZ46gS>GQtg_8]by%*+2b ^ QFYT]0.)SOlu#r͊ܣx&~Z0I!3acۥ"Ck{%\A /F1_h, Z֔t*wR)qr B}2CgZ0T+~ V({yY'>#!- x#*|Lsz#Ls-|uH$6n_ěA峕;tcZR:tė+0$*[Q>tρYNUcwn[A:=7??9pDVzhY'N0S!DsXy[ʑg @] 55<׆փw]ХBS3w?r-Z},*\Wl)onbfN[,/ɘOX*G`E[QE{ >PqgTxdv LPJs*8lM雯NDl@T `![6S j*-$gdNt/$6uVRkA@4uvǴ&Dxu:m|uWꕹ?VLǎ  dw5r-Ujݟu|:6I!RfaSASٹ\hƌPڐU@"ghɠdF8O s鵷fl"t2(s[6l7?.=mGvI̝A'}@f*?j/eJX{$}p&7]cgriPvJ~)R ,INNƻթ>1>qR!Ajwz.Yu\ ZmVwT߫Ѝ92_m /b A vVځ{((*d>;Tj~r;֖x=˻ͤ}ɸhЧyˊ */S k=\_| sבXAw]ﺉ\:pXtlrt\t]<${Co+`0 |2SYa32gKB(ܸ֟.~P .ư/ܡ4?OQ!@\pNTzT38>")L*7%D( 9BJDM < EN]DVE2S|}f.Hs/zˆ@^(nuݑ44ul/e=zsW`f@;[@~);vM}Qoh}r{ A43}mo|Ņ0{եڅjvV "L/ .T\~;o{`{j*e!MsA1oxc/ IDATێ =l&捊vDDƵs6kPv2:.aھ5+K)'3Dc!T{7am0 |r*|"u9rvv2*`h``*Q)ްFWvg23̱(ŏ  2džJ!n*Eю -"9soߙ:vR)2z*Za*Eaq}ėLl@e")ڃLF;tZA:' @Hfhَe"N?߿)Wz9cjCsŅ4Ɔt5ЅtWpL<~k_z2XYoꞫ{^1jwVk蝽v㞼cI)}I0 f[}A"߈[_i$%dշUc@ 41ݸctp1Uwg'~_=BS }ܫK8Dopf yr(|:t}MCU ݑ,dlշ  ]4vKEcO8ͳ79PpEB |^6 ZǏÃV KU H|hb혚 DgGfDGT.wV=Zi/!($( xr um陹Ȝ pBˆ{poG cU>>>y Dz~ Ӗ5>U-v06r1kV?<) x躱ٴ4R|jqr+QLL}JI D\Ws\rgEc֛JG_xŬ֬c;v&.zR)fO aDidQ̹&·iMru+Ud*R,iRC}?2uDkaKk =D@f)UCl =_;/;ya\2ߟ<)!~hWX4˅pׯMlKD'džeAh@pF͉鞌/F-׍=DoqY^xE 1>[׎lEC.MML9bk^y@V|!*g0Q3I=xy|?B[C&hlr5@}63+ZFqb12 3*&Ŭ"uzǮl~k_>D@5MuiX'u],|(mŜ UY̢ Dcn<'0l*N^A)eB!a x/a1tK)Zm&5IM0r 8|LU*I,*A ͼ"^ү-3Д G*z7g:z2Rg~?{`X7RbGӵVkz녹gv@#@":3*ɁTb]P9h@(չ{uX RKR JӔtmj4d6:IL 1m]O V*H@cR1ˉD@/?sΛy]&X.a[ 1~^໔2ur&^yɊ_<ƗoN^WfQTc:mPUy\O-k~녫@؈>qݱoc dY=L#}P_{+䪗\/.VgMKB#Qm/3 y# ‘ts@nT /4 Li!vʥH)t 7Xh{? cw%8p*N{4IѝOySOsę -w/ 2̈́B8Dp hJ N{DVrMn/]wfNjwޘuHHt*vm\Df Zo]-5Moe*vH#:,0fU mYH(u ˜"P,AhlY*VRw={@HR*Db1ˀJex 0B&I ,'1S>#ڢ7E;!BDTW]${fVQDM7 Y*5HJU~W&C>2ij! zi8lpiqn({fbz.$N~X߻k'ޙ5A\R%Lp jT !MHޙr۟fHPš{Ν7}rgNܱac|ҏwSl 3#+P σ2o\= ޖGۣ/ذ6_btmю&H$ABZ+sR".3@"@$M@x8y٣'_J1UQdUҜ4c}Rc1OM+MpUBc:iv.Wx[*`. @)]D859W*vio =Ȧ@*I@7c|ٴRbQR4}0 |@ NY׊+N-Xҟ|>Lg"P kbG8 R-F5m kf`Qz(ҙ|( J}n,Ws]҄7PЪ54Ϝ ٫;.kc鰦|mL鵷rI8rp`AZpiKaow/8ÃĴ>W/ urD. ~!T'+7=fh2l%KiRgw1Pe56Aj˴hxX"?!(-GO?!s艹K."l@uTh(c(^17 'l#1k^>cN!5GF$2ǧYfZ(3[T[ a!p֯Nϯ L|'aȔ(}eRv6od$OZңHI?9WnRMXTFgd_tEKa$T5/ 8|8V H_@Jy^+M[E#KF'^odM0X94{>>T;&Urh6lfB4{?{J2 *] fi8]A_yM(Ȑ0Z繥b~rѨ́Rj'3#>& sqWǦ[IlEFh~!*2ɻn.JO% =a̡GNͤ`<_}%5Tg̲vĦ]O~ GbzP胷KH~C X9~/EuǞ}im,('{lPsU, 0ulu@_3BBJ!)z+/heYS3|ll|"ȁB#BkOD.dy~[Q{Z3O 8>d~̹)/LZo =2|F R!D3z^ovG,(X &J?m0wQ cb̬T.Z5bo2KYx&@RFvI\ Е"bD9cJvBYcfꭽa<:d;^!D =pH{_@\uCeY|ZG oEÆUA{n?d'زYY6;^o{T`잖܈pyA ׮*G$J)4iR,ZbD׃+`%ΣFmq͢ъqHGO;s+02leso%0pEL&D~8t}R -&C`lhttF:bŬ6|oq+FDŒ nպ=X%|n!'-*@ իuX9sϚߴ$Ɯ]}ϑ%$K/b^!6 AXݴQkw3عl~~^+flb,J{vFQrчXNLWv*v*)?C]RηdžYrдHY131>5{@a :p )ұ"A(jvTş$UL\/ʗ@#z)R wGIk~nB=g$W]i[ur7Ȍ6Q#9#)+W_z)%KybL*r-?xTSJN>d$$w}|i:[/1v=jOyk!:lԓħf >H }eCBGRO>АT tJI!_x>fP` 5žw?ę[,< B7"C;{Execb.]L]qn*KVQ<םރ~>@$bfY:_枇DzT31BŤ-ghpi\bXM2@I"|sf^2󕡫R]rA^41J^WdLϦw~P" z6m9F 7蠺qKw$V+I(iL$Hօ_Nߖi|US!Dlnڐ;2habZιkD!W;aTj3:(4 BS[S? mƑhTX8[>B,I=$(gaX-z"6w&C>Jad9ץ72{fNaPxU27 KBDS ;NTrvx`Pv!&QO_Zc8kSxb1].䩯ߚ9:@LlU /ɡݷ!xSRuS:YCn`!CjHJT6"qNLH$ʾyE q]7eo;2|oL2RW\R-%(EjB֥m[|XR2DqYw(e" 6& M-H)xfclOA(D8K}2)# d=k(ZOqNߘi+ #22 50\ N 7jV\IJNwдd$ʟ𫧵%E-ŝ8#Ƶz#~>Wl9 *Cg_mi 3,cKtͰ"slc #C|rMÜV -a"c:VT>;\YvWoߖUjbwn]M(I>6>202$ $su~a-_ Cosi1A aFJcț-JԌJYZtZ9n r_K?%t)Ċɉ$-M3` QN*yŐRP`KcF+Z) `1b34䉲4G~щhdRH:ÃHdy`#"N) E&Q UڎJK` 1T9C2$2RL#jLt pָ+Ey+@,U6lL4 84agjy& rb5cf/eƼ~l,kod415o/\K݄_*32BǍ#)mVǞY-MK7A@zOm_Ҵo}O-'TB(]k> Y4 wP;y#R>;3nj!"7zz;IId&mba3`&n|y+7FDj,zmi TON{õ+x֘>}O>RG[(Yx-aBd;2`>Hj`#2R-2"UuV ?I S. ;5H@WvRo~0kc$qD)j_qq("uҪK$T]{d_iKWyY]WZT9aON|A ="wy!w":c۶`̚l@:ZME'}܉q (0*_ۖ;6;-`o6 a(_W"@Go~0VAs8Ac #]Mb3sd0_Kood3{d`Iힳ C!Zy}1kd@ !PLlB_zcƫ/tM^R20z}#\+* loEsLe|A?- g=Ceڣ8P(j;qN5S |$}Jֻvq޼|WX^R( {9gc ʶҵ"7З:Dx-1ߖp=|`8m߲- TRO9سfgF3i IlFSvV[\%z9{VeCD&וD W' eT~A)W*eaEUϼi-U$DWQ; ¶IיTV*a(ӣ|豒D.xOQekU:Ey&*Rw?bϿy>I 7v*O,bOC?b8M#]P!s50ğ߂>_g9;L #161ӳ@Y:2 Ȣ;* ݘxuKB~_IdA$F_w|`8l<=k/1gB|HaLq}V9; RRգzǢ z0"LV; mtCsk}EDGUj侇{rx 8~SɗT*mgREpW!, ڀH7/rԑ/vxqi:L% jLFJf2/#-]n&t- *]Ϭ7lWj8|krQoANK]/~{vxOLtG\!4F7\) bXSs@RAKƎ"p3}eRU fjg2|,y>W.@q]]k𺭧%c ARoS $f{l}4]B!9?AR,XX%atXZwf?] HLS2囏b IDATU>y԰Z|Ğz:."¼'H`Y'&p?j^4Tg( P*âT*J==+kk4e|g*e^ZjuRra<BTKoV>q/dPb3B$RVKweh ՛ͻn>8rlt/:(3icj&xYM 4fZk>x% 2Ots 5[ Lm>`44mwx|Qd9 C@~&%9@P ,pbx4;+EDjd [, T*\:vy!̩?es?wJnY~Hǯ+.*nAtg3Cv7u嫟}k ŹJ'  _#Υiw 3ҩNj/ Lkt2tFS'&lZͺrCYLo4J;_y)/ցu[cqaYlZL̀a8mݵOUkkGocƚ2`S\~JqZEUT-S")LOW{e%9 U>G["!C3%D1L$g ݺKda X_L(@ݸw @rTnJm{5@}?>CBgARe4E!:w2 e~>*z~QqvVS]ccSS,GʼW}""R[T0ePzBux^~ 0UE0L Š!DlnXk>Bb+&&6.5.Wu\"kS`iZfam"!`6te2e3_f\ }ᣚ둡>e JdfA NUh9ޅGOBe[X&ޕ=cs7_7 ҟziy^ܵtxKHV"B7G!F (d|f'Asv0lkm8Y&H^_=z  8Ҵ?owf=[ѱ PVAc9q4^|Sؼp#;5M}%N/On"x*f8ƄHtKEz?#)1(ѧLH wߢ_֎S,''í ^(q%^J GN"h{)6:w$(Kz6{u|ժUe4TS g{/qr&s<2 wŹx.]>r<쏀mlt}Y+׮g}g !0王JQOEMQayf'q. Ϥlٌ(Xj =>Ҽ}['(?!4B2QcubuqaҺ &DKbi/vE`/ХG806vɰB@}ڕ=| k9gLӘQ02׮Xٶ"^{7[5.:`[ eU'^X0|~}D8+<Ǘ2ȔmOQqr5%"rYgbj67p|ziBVlma/l VG[oNSo M fnR :oR*ꙗ+Wm_yzIC7R}?n<7K$ 2Cb\jL/m>fj-g_l/RR)8Xl" bKb1P&!#'PJݑ<2A88"R#(2jL*PjקmWO va@ .s@R)5WL"}@OQוa% K~i7m%E뷤MPaBšI;{ -œK$R>tdz(ιIdC@-Q#8g6M#|YQ`tbvŃ~"ZHmvcQsJYߒl/  ڛ.%R}A4P3)qy!כm<~ 9G w>.=? #jq^zʆu,e#-1_0EEݶ'3cǴV[t4cυdJ7" (Cwʅ A1旋5Ed|smGNR5` gs{"\j5"p:r#r)cR԰lQ'N)E WL7V*=%ATԩN/H!YYr ϒ/ICa Q: qeP=u׶aWS=3wkn!^F-Ƥ 6z |}N8JHRJE;$1@2NbH?FgngꦭǟgD$74tdHu(r~xF7`}r.F8#BBQmttIW elr ,Y =Yo4y\>Jɤ Tq~PxLu\q2u6Oi2F6=ͥ +đq_tQ{z E7V1\ e:eMN8_*f߻'q3(Mˮ^ߺ- TW ]ÉF`~%X_|?z}!887tf9]c(ĪKATSЪ5DDjRvg >W~D"5?3+b3\1s@ƐhJjZ7~1|sfWz#(O^|:{&ZT mؒӮ7bKd`?w\\ڹ[!*"hymi$cfr;vr4t8+67ɽ d!T ?4 -4teo_VŴJzvЅi%el;B$ chY{dCSԖlFi^o?=п#0!Drk)DAH{\&zC AXI[$@3^z [y #muf2"xK;Zv^Y9;1-8& "!\֘^E7FTNEX RўNT| rfK &ҷ%FJ|;0g`'']#P%3+hcf6u`Lkx\1(2#=GxXhREk7U'w, sɤLrqs\d>h/aw5=}?yH:a7RLReD|dqrq+!UXc媑;Lf -'kL"cp>lR|-X&a @㾮ezirڴ\댈J;vx?̿\ ʡ%緅zZ]Z/8lWkv]՝jJץ}LZ?g5L*=Su%礤"u;n n1UۥP5Y8cҧȶP ^|Kze33r|-cONNJ ;a]~> =Ϟ:g5֮:uի{ٞE8?(4y&06P&Ћ{ #EWwo*`1Vv7/I4bƩId jkRGO0"HTLrPA)Qz}ü`*ߍ Q5znaև{ #fƎu 1YYxg^S]=K%%R ǂ4N,SR@{Z/K׸$LʵWCA~pDs @5;@gHӐfR`]]C_2gٌ\m6m-^}/-bGB؂3uT[a*X$,]# YOBz +rqO9.1&4 5 ptr,Xq;&|j\#1*ul՚Mp\GKqrF{Lv= 2cTDPӨk^7RZҎ]o L>2ED\Ǝ04X1$*u X^whq  rU#d0H*J!TjĸL(mQLa[k~c_yp pVBWl ྯ61C itИYW]@{>:F= z8 7PfE-519wNi,`E ?GXP`'u* 7Ka^(|gfuU zׅ-&'/D B!uݴ՜{uqHЕeɔ] wCfQJP C ()]_m?#x wu H7G!)IȠ>vܾR჏Y*P?1n!pPcο9+:i5'p 4|b^!Eֿ$^|SrhADCduOkևt l)E r\t W"r&-ɥU#4>F1%19>xDZ]ڰVr zMsr*g^o|Ix5xTv{իQ:"=(NT:qRIMSGWIС, t= PYiԖc}`B/e15$SOkhvښY%u~ tIY{|>h"bZaq(;[>k E>ԋ0xA7^V.˼8:g*XR06<w/_|m׎R؝[^RaC|1 2vrAږ=1IZ$n}QN*.@Ԣ_ 9'>&0B}7ո4e2eBk:w?Xz:Nz!fN|MH { #"[5ž{X72 cgZrZ-qT:e)916}U>f"eU=LBVQ)d}=!64oy EsbJԥPbBdSW_ƟxZ%nX/H#!@A>R BRBq3p leڗmƩb_J;5A+g.lҶ։ރz;󘐍 6x L6@>=HI"G7ӣ^h W鵺HzS:0T{;djՈھS ϼr^vA}Q,g|Jv˧"v]ͶF77C'NvU^@zEq!T*srbk~5;JE1RE #IFSd$lq,"G `]1mD=D>Esv#ysRTPCX2Bά,r$fS:a#+e[箼> p(Mu,eho~ "e| ]b"UH-@J/,Exn"P BEs}Qe2E&]l?q!HߺC,?ݎ4L# IDATz17)H)ms 'ic48JGw'c\pPZ1Ӌ欄ǯq!A 9"s̗RxE} i㈌u9PbQm N*v`Q5D!<>U$WW s]׃Fh('6stj="S3,e;Κ2垯lK֑ݡ\KY>z1 t$`:.S4Gs]I Ko,JOJMSgQR6MG|*W*ȶR_pٖ20T]m Ð$" `8ؚm@YģD|uC]=z<G 7ޑCq8ڒ >QlRQw?DXL#^o֮"{p,( *e[3sL.`RV>nR%!O=:"]4Zl|֬T|`/[A)@,`ظ0!~׎&aN,mШ 0 NqZj9[Yґ / lAϽvO~ E ;.!Dec==@zD|c1/fTYL"eӳ,fClZQ~w93'To!"PJr@511Q_>$ʽgj_#~0gE1?̤;Na|k0Rsuw?̲`$S=1> :.pj&dhL?b6FpS' Pwl*aLfWfF= n*is֗>ZߴsWwOs4{(CM EZ1gM5aV2qI ,wz~_INO |IrYrūHna2N}Q#$6S~ꕗ݃忼wŖIGs;#bAB0[/hﳧg͹PD~x. b vL_f~E2  @E*B|)Z֯~<~ ӸuRcLLی1JƎhx4$X¿liC#ΝZvۜvFOv-&?D*bWs];GA`aB_3gD&i)/}s3sܱuW/;5J4='m30w Hى;"#l\1 %F Vw"5PѸiK&EyWS],l{6Be؉`[7[kVƋfa[ Z JDT㼴Ϛ ^+GHd3z6%DĄLUTwq:1YQ5mN~={sE@ޕYsjšE!y" kL9s֤ƎɎ"u4?id mϿb/G9_a^r ];z< }H-%wPI-ʻ?ӗ JB%T!la tݮv\f>8s4]e*ZXoR& )D[Aw{Pc aAIT:]aC8KT XlIOD-LnX'/l=$8: âݍp3i\5lt*CS|\U_sVzvqjOoiz^꜕tU-I~:6r?z -l#[aWqK1;{|5 ,Ğ޽'&$sZ`6q~&d^okyZՁԅ0zbO0@V}%2`aʃQs4)؅qx!Ҩ֕miq9J1[NӸ|xv;Po8;f .ؐ;rL{.3M0 sǍz3/ qKs}QJŰb0ĵD.{}Ysv[`Z8dcs è7s|Ռ޻{e'KJs@){wC˧̓xHvmw1`BnoVZT ܓȰ=4fHD/Οa|miC'b`S T=0ew!CD]2k;W;JaC%#$":W]j&s 4ɡ~c`3 @&E@渱Ti7sMnZ/7~4 g晐aja[I03!*JsM,eT%A7BX!n!vEl΋وݍ t7#@ C4!]|Ufo&o4}q7o{|^h؄y7V L=U?aKic_豧 L=QZDL/B!*5sB& ''G2EgT {RG̿8J!b+^]^ *r,WD\4LJDt mXs^F4q Q_ID$Yr!gWv0а`m陓1>GP[9R@N^l m_ٛn̋g,?&$|dIFk" ^u=خH[~#F*|%*"$D@3lf Espn cT"՞"^Mm^G)nbź86q3uIjghį^ʻ/eٚDѴڱ+Z&N&aHRD ӨT=Ӵ(S%?w{:K:&TE@B*25Ai UO׬5Wuz!NAؾ߸ RȬ McC#m=wmioQ=:,N)Myp:gǿF5MB_Hm02BK z|CTVsGHas`.PfJҩFᙎ f.CGA k-\ZV Qg4g?] bϿ7ws\ 'W7""ɹk/Z6qw8xBG<@0qޓh;PJRqG[1UzTm8;1yu9w*rVkKraG2 f"Uٴɥ j]cǞ1pUxMB h\KzcQ W`SՙwL)uM]L//\~6B J%Gz $ eŕK(DM*X"3:>M ftw-x?D04Iml2<[70عvhy?5"rfy[oΜ<-37P;9Y3ޱ 5AuUgҿrh/od 7ۼRƪk,s-R IDATU"DL?2V~tS7KR͎<Һ3D+ tCO⦛kP*x̝B5cD(8C ՆVr 1_ nۻiQsBnYTV$(4yfٓsMs{>> fZ?iw$S37=~]|?6~A.,\f:db"dCE=`m+|TeWj=JZvPdγF 2ټym;L^ɘ嬚ė =O¯Df/䔅`[7vsNJ-Rs ̓"Mc" :ush=oK[(@)h/3x}/ U"jcm6U&X0R8%v#TAf  komu9 _ "i « PY^Y?yVӅv}@^iፎvȒL5˵&Vgjc{ܹKzu\ =9Qqsac?_@ܮ¸8 xތ|2b|?,K8Mv3#fŲ&三g_+?t i;mZgG|l|ښ סI9kBZ@ ruګ%"HE H=9}<F`E^DT. B5!"c3s^cf< eRw!2$]C/~"&F&BJ16)6ݨ=ƙu/5Klh4~2DdG-_S%bLq8+M+\Kꓣ.@Sbղo ꇟ-fC=s4* *fli'@˥70ɘ)J0Q@iƹ3`W EH%=1DG.[? 4=zש 3 @2) hj"ޢB A/ZM-`}8SܣOAIժ5ZƎDTl^B 'k?=Ø_DD6 l ޺SR{h~J%ǟia|9':O[Zs0<s߉kFm^:u_4hp5)松.lMK"U86 CM S`RPEQ @RNV/j]Xu󃲭E 4 D RZ 3M P_J2:W2$x=׮׽8\?r{kк{BR'×Z隑/d>>Ϥc3z, q d<{8_Φ[?>"zg~K~6ݵYjRĹʶȘ_$ F~@Bh2G|,˽И*-enɂT&0̐=j~# C{?߾+3'NvUlf? s#7]k$V!u#$gqsGKKK;B8\HMsv=̂9+>^aY| ei"""c$ *N,S8>; 8giW!kLsk酷u)Qpaά(5J9:6ɗ?0¬D n>2( Nc"_ʥd %Fd(Y,tRXP?" /ԌSC xkr5 JbnW,E"پݒ1'RŒ۲|hf/cp$<E@8J!8{Uc24_ai:~A0v:5t K{evf #UzSSߵ1‚ U+=VOǞIB$BA1R 02ޑ4z~ijтoF85Re;qjuJ{ЩNݶmjY|9ȯ T^̛1uMfl[>FjٞcRmfDAz#2t@`&\dL @/tzr@`;6:t(ze}J@"TsKX87H$cC&<5;j?zb_e0U"6]> $w?"%g]kG?!Վ;ˈ'0j=e y˃6V41mxy6GfU51JNʵa@:)+LzP)5Ɛ) L@@sT+DҦJ {fg'vC O-K(fb*'ZNcAfcVef;өj^OB/Cw# ۻ}7,|mTjz LY<=x`éKSo}cy]AZN1ᣑ|>J:pOK3Qj/ Q2˄ܟKJ3«q 1oO{ml skd^ CV(t}i^#"#9czz2g1}[+% RBkVkz&"ٶʵã(Hz;'S!*J\JY[L)}[TS>c,dHDZ}|kƫjd|o+[GN$/vJ.9s!1D/n; _DDL'Zo, ""B[3Mg74v)4 ; m3^w[(94* 3]ec:\%51(hHanࣿ'ZL ET~GrA^вU2Rnw.1QpЄ5eSg&nXlG WBd0%&Nk֍°8pBXrR7{)J]XM穷} zH Nm^'"B14_?更]=9 |oMc6XZ]7cn1@URDj$?vSHg@G-12.IͶ߼BcPX<B݆@ u bSg pgөD/A $wĞ(@!6 F:H4ocܳqˊ\hPʥw ʚD$ Wglq9@V\pm(,$yzMN1dz>,T?ÁLco$qjVh_M4# (Lq:[FYkW 3,-ZSRy[qF|Oq^],mVݘوj|v`vk\]Ϝ=e%R#Nz7bk@Źxm1:q425hS%0tm"lykh@_gMiZԹՙ]{#W]Jeg~Ol"TK\-tr$9J9RYm{7yjUAkܲupt`TUjI[#&'k; wCEKuCS匏籽_.ae\^1-Sq糖p)ˆUV~;IE “@f_x]@Rgw("hٶӝʩRywwުq^d2>v ,Yr2<%'g. ~{rƉNu< IDAT".p].kroBL 2O:rVIEDywY|B,Q'M)5PgӆD+&iIZ]DW)G"r-B A#t(@BjR *Fs=3V/dK i]+f?`nC&*!`kN@*YH"! 54D6:6fQ5WD= :Z\9r`A@yoTծ\;vWv&oJr╏Rn:y{F%tTsWܘr5c帉Z F!JI\MP_c~w>cJ'i.r3)12.ZiP -Gty=?}TkvF_V+]9Jʶl׎w): 5٨BeW֬ZAٽxu8d\eJKt9V,x5MCg_-ܾ]MSVTwUwn=~ ##88=9*QxPpRI "!NM4t Ly:A-XΤqЅ@> I!1$SQ"Q#,| 'wRI}}tj(Yr!9icv6FnVkVקݲabʵ nټR9"FZjؙ0gϾY*:lN􊛔SCGn+Zaz-(Q,/[€Z^9Ķ=~xzG[a V*U &D& dhIp` fz\ٳ ޤF}5W"@W6dw'd^16Qxվeߺwey+m;'/_QWn1"^u]ʖ|6ºgI)?e_j5Jy!Kƙe?.[L#T,G}y6m~ىXzg~2ez^7#[Rlp\PF2RG{-_(EEҖ1"Nb8W.1LT; >ikqSI3I׉󀫱ug 8e!d\0L qC0/J05 .0S z\wǞ oVN\ ,Vt_\/޵E ˗h|k cy][n[禡T 6%iQxgj;L&6z$g'l "6>NU4sIM>O\ΐi\8l>K9ʥ%?LM :Гs+:;P[)/U;Z_1tˇ37y6rcdhf:Y'GOB+̲qQI B5 ѱ/7Jm a?pС^o}}zCV_'{鳯 Dge<>vc8mO_~g'TTo {IɃw0YY,x~y>94?7PM;ҕ˰&.BD}قK{K6"'_M=KeD);!DɑN G !!CŘB꺌n"^]XbnKTd+S n1uUj<7V(j€H(.XBg URBI Apd:g6g+M3䄊TNdzK66>v:z^.1ѴjWЋ3!v6SXb !,/nY8F*`(@4?Me r %,ze75_ \)Ϝ//cCA9CƙdA;{b fg&t"3"픔gCxAuPt;[=x>КfF~=cvۗh}}#cY,qg(3utr)ޚN}p8v&]A$qMׯCÉObjh;JTܲxq}tA@@@Flx%bN&%$ňD'ﻳ{! e/xe67S~h-*}}_~4+hqؽWGź_"LVMQ1T+M.e4Tt [XqJ/vHYqVhgo,JNf;1>gp %q€ѓ zX*3эµ!ssBp3eXejAtjͮ]@  =O fO (Iջkͷ.~n+J9biǡ&Tv!S)Y^從mUHH Wp^ B DiŒ^z;N2E Z0:A(7"O”¨>`3OF+xX4JE 5 &Y(xcOD09Ι똂0n7΃-JTD@d@ DI\Sx.&bւcPD剩hcS&U]o^d${;ұG^tT+z6(WdTn:UKGϳTEH+Wbّte$+CTa͊_> r/7f&F& I"i(iޫoUz(bÓRC&T|v[sMϗ0Ln<lDdp 7 k߿WtZ'Xm<{C# g*{9뭶.e#")$wߖSR/3KYhxgc;>U#f-6ߞB]ٔ֨RuCu%b"=TV毎eׯ)e&M'?;iwx,l:}0[:ճG~ȟ޷7zU{|vRr) ,Kk ˀ{:){ӺѓLW5KlQ8G{/XCR8vAZu~-W3sIohad8'GLT-N%t\cy7}?r jm%;k&Mb&XyOO"B:L72ɕ+`~0d>h=Yժ̙Ko"C5ӶL(Lr,Yr TY,~Լw?wYŹT̶=hbZR$x_!w>bS-|6dyv_EH|zs^24Z%SoRJƯ%tI?YtR!` ,6qrB~sB )C)Ih~*~P:uk,9 Pktͭ?fᬩ0Lقsag^I r@Z矦?94 m'!c9дTG3$g!hÁa '^'QnXHnnm#D30Wry=ڷ9OF`y5ΐh3Vz9B75:GKPoՁb84r!$:U՘R)Os H7A"rw3P~QW#-d>aG*;ohX 3,9 SʩR8pNpDBHYTf}=a:[p W]o}E TTT}ƤحW ?𜵿q^9eRJ`~5ziG Ϯs*5=>eBq8>x%hJY3Dk# VF3gkxh3_[& vM<?zâ .\FӶB@ffgN-tʵ;i~xh1sǢl3͜] r'Ӗֶ]li nP[F_~ѢF[GXԖN{wH$/:Stk?&ChZGUfU߹,`"bGi`@S̢N40JѰ9U67`Mh#IJl>KFÅqDMDJ3 y'HG}300Yv>,[6m;ij-Uǹj#p\ޮe+|# **y$%˜A$Y$t_% " $N<%p~s&xC1L)71߫~E0^oϥ1dg[pex*m*cƒ)D5n+(v$D/WpTiǀd!K֟kșq=8|DDߴjbxT~a*0g7X7OF£>/(BP E$9b@ӔHHoihXxnfUS"mhX5=ռߟTssO 0XCC{nFsSx[FC@P>ni#ҩx[t寈d/Y;Fy/#b?Pmޱj[cᩗנ^q3DDU$.۴L͉hU KXu|R㓩{,~"XuQܰġQ ЍЛzc# &֢'^@G|ץ˦|#?%(T|Hș=}s p@PHm!m'3k<r.:YCǨin$>ۓ6|UW\*lp+ M&3K^cr-qQEȤ0p G3+ǒ¬OH)$ $Gb!S]ZGj>OIA!@ !24:P {ō-F)Wn},‚񩺪5$2iJ847g5Y_ X0t֮t%uM~ywZOP!" c7/`C˜X@Rxd9t#%`zy$}M~)7p5TU)7_>ǝD[4:quĢIMQ_Pۺ;\:ܬiehDp8x՞;n}O\ak?Huk]{@SνT_U+YϺcsG⿞BVge`$  |DQ|>ڶ˥ D߇F;?LJOnm쥧3FDj2# $)FScQ%Jp@U IDATvFIlhiCRV =*8o*ww%BPpQeeAל?`@NƤTLKI$x̘؀Ȩ>4ڰa3'rb=a03fg B^ej6_yrWߌ)mʅ*c gjQ|_U•E)b#Ktt #~\ e 꿒+z2%!o`g+d9C*.> `EU(}*iߧ*%Gr8 o|: je*$c/lzErD$P@n,"TFy{M-y((ă3)V[KVڳӎMc/ <yHg?b` Jc,x{V&#F׾APU_ѰHȯ75ǟj8 w߻_d]qݷBC`EDqU2Z`/Q^'/Q =;lr S&`- ËW|ӏ(H<ܴ;b {{'^d{u=f#$墋pW7*օPD-W%^yp5&bYVMq0 =qa0BI E$@ٱT89TEt'pЙ= >X@Vj\t64Pm4Q8 B͔ɪmd@#(.ym҅ZLE+e)ܩ#%ɑґd3t)0U9ÕgO]F)_*JU%;H#8G x h\Rr9ѕQ˦@J@ټXdڪ Ⱥ>߀q"cJ.+e*dp8:yW@V 4:FgMyysoI 'fy3K(y}]zg}hDٷ#"$mn+Pu9S# sәЪ}}  L5a'xdTU)XrvO3{2"uDuWUNe$oX,{VFHuqua7WHk|;цFsph4!{~. Ov 570UA>JaƬgxsy bJsoU}[Ewv|: eJeܩ}EHa`8pHϤbj2 +!gbZ֦0qn53@_2d؊P+\LSVjdRbZH iKf V"%ɽDM % (܈DB;1!.UQzݞn1H籽a0PpAS %re}o}yc*I0C-5H2/#*#DZӄZM[m߱Hi<38z^1yy-Gr#c< qCk`d]My $ nyW}Mmn(cxE%E6o NGjKCdBWӳY 'pMHBiu4hى=Žc!Dok7\V9fM}D@F'u}]'֮ݿTThX0 g3(==Z9<K;[ 쩬G`-~E#y[M f_XC# W^ ޲rJGk~j!nBiEHE~aM[԰\N9^0[E=Bye gP! T6|:dDVNy"Ʉ,n"|~`~n׊l P1)Tl+9a! yn`C=,J@)1aܽ w?#U47WN!V%)nʮ3:$qcVۨeh2mA @JI@P4܉{Nh @4>ހ8gi8FxE1;U d):%ݬl5`?Ҟ[q DB*c\_ JT[5x3v"G.*9rr@_6𫇝b7/@<(Y H KVFƑ\VjHgPkt B1e-~ϮW:fAtCv7g'>͗_|J6,?E#zCcDC.>.M A?Ţl[(Q-r)OY c9aT8bp*•5(_:c)]{Ѵ`@2 J@$@QȧSoEcIq*AK:T @}YU}{B5( fޱөz| X6I c'bF8AphxLo6ި[I)=A֭WXp~c7)MGlH B)Ƿow^q H"GS-v(:vV6=*OL*mpk(+QQc:wq+oqAUQunZ`wKR1~5ͫ M}[W ;Zc} D/-l)ͯAPWX4rZGHRw㪁_= 9,Hs0cT+ ~\䄙֖ݹތ+i~&e14U#k/i7/,QHIRU6Yx*Y7^{vyp_5"ߵ;2 ,/+Pc Q3\E%S%$ _^[xavQPT+ʿ< hۨ(ʣmևܫXp-;@ YہA+DT|3Ҁ CԻ@<5wcli\-:5h&3~_*39a9 Ύ9?+?G V:@0bq;4-:ajHy'l{M`)Oţ {вe1PȶxI/"q+Ҝ|>4W%1hvX_s_[`MKeyIMpBț55rt/"pҬ Q1RM`n ao~hf>ؚ|{30r8*}jsc29C @y j 2o_p+-/d~v}k^r!57 |4.tƾP+Nz5U+b* ֬jߑ;aq@UyEdC=q;q¸PNS7ֱ?T* &@u'0FJnTuΐMqDTSiu}gw|NCÖ)r uh:3pqՂskrDYUq7EiZi<_mό@ #vr<߈$g.`s<" $rldN?z[Eٝ}@t׵x=LⲮȜׅd1fALQI])Y·2oN~Iߟp[mGi^K3Wtl<|mWS#|+0ˊ\pjdZ%`1GcˠwJs*\gP1I޾]dJi2!iMFR▥D)e~Z`;YEHق` Gȼal!e`x.x%=OrRt"BLOj wC)@׍{lvbx8|*H6Vc+PJ>uX NʡxIdsxiɗ^_ܒ{{SG^ՖpV$꣑:[D"{2g7Qi5=kNM`nkop`?L Cꇴi;&`,O?xE7/4ھE=u vt(UƯ_/^rMG?؉1_z}.SzYSOQjEXQxȶ /Uo9'iwAu(*\F )~7FP-P8570W_q^Ag3Ӭ-,J$Ƹq Zd04 =ŜeMupb1gtgZ!հ»slѡ+Lhu>%_!{c]R0UYH%@LG.j&ED $Y["`9T6Y3;^^ BSK A <*6}y't\dޥw↡[ 6m95K`TZ yҫ?H.")s'{EQԧu:oܯjJsO1d9gUd#?Q:%+(=;f\p'w R_^O3 C LH0 yP;UBH"Eݲ1T4m;^\]$Ta8L-o؎Σ|@i[/ᒊ缥AXutz1$I<j-puɇgW?GtYZgHӯo*ɽW 4ۆ]Z 4w̤¶ރvT؏7l5[mW) >^ _ߙCeϛa zmⳬ7"Nϙj/dz ZcpR%v؛%?23FRvXRDB4zT%I04} i[!M:>< | #"Td)B 零ͯ+ %eZRZX[ h y(!ƅ[ރf>by0LHewP 1RdOy>l?*YbE;u R"Id 2l52ˑ9vv<saoke DD.[yX4-"5ڷl#j2fq kEϯk^§uBJRr%m?gI~0KыNoӯ`2_)n!N='} 9g`?WJC]1#cɲDMS[w_o< )$H`,:T ^GooR EQ0Kmt@ӥ#$mbkdt(=%nx{%4ឧi/+@a-@dg:aF [ds}EC=wKqJ]fr%'ADQ N0GE Is}-ap錈 bu0VeEq#9P`NXM-OCeb53Wqd$ہ%ti, 5N ռ6k뵅[1cƘg?L.ܒK%Bj[u]l-}f *R* ךߜYR;nMc ( i~c}r*:7ސ3m҄\7o9 "3GBV!c^rPUt97tp Zw 0T6{z)ɇ_pr^ZnHeҫ٣!jYӏ}ɾd&g6w )0Mx4k78;(pBU )u];a±339ж%HV>S{>]U_Քl .rSz*YwYh^NGyә=<dfZ {SU) ۈ3,GE_g?hxfcA jx ' ۃT!cѣBUqݔ T4\YST*J09}@?~1Ǎ{ DܾOi u4!%MF^x#nou1mr{Oͣvպ+T@bo{}ScVZ}_ܫi,o;m _pcm7~kjG臋('b*JU2}Ix9"v=OC6/ϙg 8zy˅`lWi7}POIIJ,Н{ۿUGo/_#F)KR;e8 p4Z|wmB@!hYc0:mxAdAè[6s9u)fl/<*J0@s"okixk=f1?7X7s@'Ue>*u/3T j&60[Kk#%r99vjc}5k/7-0US[Lݷ:wE" ĢZGK3PoO|qro!"E:4߲իb=|U総2sR1.82bklb3kzv(EpUcC`4CwwL@Up"j]ܹGs-_OB5ro^8+ߖ[Л. Q6ŭ 1Gu(VcXrXٱo=cKUe;]rJG`*l !HϣrUũ{'i'6? >iɕUAiP_ڧH1|`M)#8E{ /5i$W_n%DCj}l-#pXG7^zL<Ż{@X$I !I2sEK^"5B,U+))"٬Ҥ NUS?bI"RT*v4'^g,l{( '|~%8yNY`"cS;zCOҿz AQP |7.DH#VHGWk-"Mn7ސ1ny|Gd%!-4vx5g?/} Qb>u',_͍r[>ށscS cdLfsӯ\aü!rUg`녤򵹃"qFpvh-:ߥg:ZGymy}XM?yz^]A]~aF? 1r/c"l%ܗ߁/RyU`{p[fgwd?O1,_sLa H"s`4foڋ*%g;Ø2QhLjTߌ<4;qθ>Wa!V uԎW](v﷞}]wr]Y&~Lxgs **sAw]mi*7! k]fo%Bܿ#睒ygS.CZ EqSQ8:!P(T %*~v3B?Q5DNa@T}pj/jzټ64buJ[q!T19{ܑ[ҧ#JV@UW^Rߧ3)+0&lȽ;r"VeQt+0xgCqoyn@H\1%wՅTHDLȊ](7]][V  i6lAr5}*J(dLmthmnzN ~u5k}@4c}ܬnwe6OB b_woB&ȕo> D'h0)#$9u'_}Gl';}S;ҫ׫/ڛ ]Hd )V:: Vw}֑KHݾަT^Tȹ'VoKѴ$),3]X9ۖ HE%EW:o:2> Oy 0=nVw="jZ׼w׍-/aY%w2e&w9m]Ś ώՅ> "nΊ8My ێR3sBzᅱGc,xŊ` ZXJtW=B8p%L۠.H$bшv ̞| X&W$DЕ/y #UP*'f\ L2o r7e˭8kUfbG~O D:~My+o5b1;dzö8"!}҄Ȃsʾq J}au,i<R[:_i@5VHf0[}+ɲc[Qz^ JE-Wb@.e4 g .b/_cb2mwrs?3MAS*@<@)K Ī_|*M\LK.ûgW?J,b=ߡe}89'%_}E_ދ^Q,yU)(n#΅gFs,Dy,*#*a84=mD;jɋΎ 7ޏ6uu4厼8N!Z$`4\#g#7\Ls#n@orır'M?4+עIQY8vUW@d4F!\(:'HH2 Tx.M+N{w7&uѹS@{<[3 P*N$O;Z'{yo_h>9w6P?:sCB}C}'n[82qoz_GKGʃwQo0q͘i<էwF.= AiJ,*mG2{Uu^:Z~#ؽCw79#"Bؾ,+=8Sq֨u= 0!O!^ KoX B>cRv[Chڪ{4i>?gvx:J|s|MjECMc? 32oPEs<ߢ}cؿ^ [v}5/i ^1,@P1's "o3Y(X+ϻ*vi(TC L;>zo yi2VCۺ̑kS6 sg^r PIg+-PUrPᾌ2O^}i`J[CGI'u5uuO 'g sp$ % *^;evPu7'κ`ȧ;%Cips[׌hhdhm`X_۲oE_Ӥe 3ͿfpPN"|3'^c= J,<[OʽEԖ|~7~ x]s)`oQرlMO,=tOW;v)j*4)_JhooL}hHZV 4`օ̴i fbɧÔNX8^~Q0 g7xeg ݳm%,-gL8 8(1S1|7b}jcvdswvB,XDGz8=v+#R!稹SI +B>2\}Js#V^KZ{'^z{KI;QnYx#0,̚ &e+eb>ptFN}tEϯTQ^Zu6P(z#OpEcc¯&ۄ3ܠ'Ά{fNoszC (nX48u$o{$ l(@MHBKWvzǜ2"$@|߷~Ĺspγ28$|%?ևPOs֣}o7 g2p%c]Z]08N 4pC{2T|s|nAKf"*x|Ș?~g]lغ-pwuTq8Qcq-U]3L5bɁCm@ "pP_jmF_L=ґ۶ψ%mĒCn5-y!"%Z}san|5dKV18jӯ.\geshOOI_c\7(KE)] l&&BR(]N<nX zT8xoǓK_b& FMFX Z'-'v;IaxXLo?=2`.UB!Z^AӕF$RvyՎ;W2#r1y`IzCXUo%z䄴 Rj)@ܮ3[v jVK9`D{}T!(U6 ")Y^{7GW\~ygR{dZk~mHdL@ߓ{uctc6^Oͻ/4AHkEɆ*`_(i%D$~; oS/>ؖ>EƵ7Ms ZhhL3qPH=n>|@\cRS/Ewz ďy=;o< .r }P2oKecsӀ΍~2lΚ=&65& t'd (R , H(!D^/;DB(f[7$NM wLN쏽.ݱ2. 8=^Uz[隮L5j0f0YNcbs}'D *JhR4N H\uɨ_.!A#R'u!g,rTrFoѳ{Q7 (5"aC7;!ï99)YC,K9sqGk)s#ORu,gknQt|U޻6PyER* uʋhC 8:$𸌥Y[4n?®ooyvƸythKN_=٫geM34iS fJԻofm<"(T}⇛C櫹mFTnN}U,XUl!bv%w%02G/"zYXVZ.-W\WXAV?"ybTtVR}V}u_ -diED. IDAT~,%J;28[Yy̰^Rd=6* " cR,P"PÄjG#F/}zhNUe/q7n1)} J,A1LQknp)N.j~"URʓA4o Jz%*)f$D:F?ZG@g1)<+ﵬUN Č&1ꧻHkv:J4 }T鏖̀-kƯʫ!RJC>7m O#| 6`L2a C47 Eql/ ]϶=ڿ^ri 3Bw\ c[ξvӠ&I>9p7-躩L5L`zW__joq3\(R""PDA0,O/X"u|՜q5zoT >sO^Ǻ,a^4AIc肛1;E~Po]}S{oa|?RQ yƸv4֟Xe_4E_Ea+ebɵGwsy $HQvnjz7q%=SPR Dvi#gn9<sBEUwXf~#)0@ pJ:Z?ϽJ . \I_`q_\FevVޚø.u-,#u/kxEds5{3O-SJ"=7z企*^w<F>4[9}UP0<ݺ_EM8'=l*kq* j3NYV"|NyM\db2#@p^R:p{J[ ^ξvAOԦ_3տR5L+{$pZ)Z}(uUk@@Olƥj2oiӧ<Zbj"a{wŪ0s.ZNN+S(v7 y<edaR)"ZTK1uRv N"* mL! 񖫱/z4˔ ?O hs#P,"8Oǁg^DEO8sK@o#jKcdo16I]ѴxL_ϧ@i8H SKdF ~Z>ivIDj'H xxv^>zoA{ ?f\tQ G5D.3?T)SGr{>?e)7O8柿{=yvd*EJ[ʬ(m!Yys ˈT):*@f< i@H V!X9. q7d&T/("aAtX5v,%g/Z=ȼ e|e/Lc`%wb-MM; =3!P:NxW欌񥲒qtH40A}6^\@m2QJ(&puàhc7ۻPdZ[7L:@Uao {!>h:p^`qp!HfP#٫u9;4;o>śF7֛[< :yq#n\FszFtӜ6Q ;vsv?G1% Q䞹%0W@_ cV`=y(C)tIxC휱ގc _W t#.CؗU:C՗Xq?}/RpƠ)*~ƫ@<ǽmO&RpenR,.A9! 5`0Pr%BNV" $FȦ~&E v%@d+^낎X˰up%}K@5xٓu"09_qCL:+>cq> ϻKY9c<*JkWLfEA,CIWϾ"W,)&]<\>_fw ~ O,&+Eϋ)=_|10ٜ)Ͻ ._qr\xvzayǗ05i]w]uQrŇ6lUK};x̰y-qmpufM (ڋ ;y<94C΋ `*GJ(HnSƘ{,SZ*D@ChuA)'&grF52ְf]}w{v1QVpr9{JB#4sBin@ $:QVx39biVFM }вxHR#``+0 "P}iP ~UOyhŹE8Q8nòJqnΒA{s#$it9XRB$TW菥ZݟSn';my2]LXS/@,i=`|~!VbW& S`Q?$:ߟz-d6ZR^sDx=%({@~4yLKAJ(%Ωusi5*β/T] ;V-Y9 q ʤi(}0a4cLnxi]*{^6ø!H9-ê2wMQM+TgLrusc>qaa@EQ`j>Y/]5cIǧrSNYGK/u- %A_q ϙj.\f!eV"_o]?~s$׃kV)wQ#tgGvI_/㚪ScR[k7+%ڏ+ AMю(yd2SC|5`{OaZ+~3S&d43kR{`׵y,oVS#(S50ק ZﳭMw^~{9hj]f ')7FRD ݄H((j} TY۠:X_GB%=W  "rέIɌ8cAǬuZO/q<|8hf nżJ"_h^edsʋ=+ri ı-_?^UݾO&Yu]ջ2SKloL2ZsCzg鞚9ԡ\}^R;%:c*\x6 q\x}ZPTX.&Ry1ȰiB 9Rz# - ?Y0/6(Viv鴜zfIoOrU7Rk7*T{#af]xOR$洂՚f\ l2[^DB#$fJ<#r&CA: laN<µaS=غ ΂ы1ۏ;zC5}x0ڹWƈ(eL?3q@ DQ HqWL5GWw#r-M/.6CA@XﱲE=!jCGOv E,m$.p Un okG?MB$ MLhƒEg>ۙ/mǁHQ/WUXLN xՁu3.յ  % ' S4Wz c)a9M>qLmkԃTVQn*eE{) ?rg%XeWD"ThCW/ifsd_Z 0Vŭix&HPݑzܮ@Hw̼I{Z`z `*-8ի ?WTNye]Z@Qդ[ p{Ļ+(r4A =N=Ư_ a^>Hr9GπooNӜBu ]PB/3:gf # BlZaM׍yuy@9;9("&ϙ^a+ZnBRgOmXvAى9%Ro jgSuzR@t9okye9Uռݦ+•ũ̀߳}oر,'c^ISh: u~bMSW ,'(2M5*0⩐䏗Pӻ95;h٦o&Eu31d^PV{^ ""=sB">r- 70E jat9̡AQ3?k1i|E3JpSZ)[# K f=ӻvp(2nW Μ̷ɸ1'x ]fW;X oXV̦J ?CX6oDr#C2!B'fPd!@QS>Dnk}JNUtCs0KHۢO-L!}"Eܸ`nzȳK!+qDhn0%}=\׉w.SG#+$?rGfvyS 13N/Kzwx #H\h@B4K(Uՠ(Um}gYбu ܒCzEhiTf]ݭ_ c_67XrJ Q}@( [G(j|DV?9z**p]<[{ ȹvѹ7u 0NDQY0?4޶5ޅ6I^uWʛ0o؂+ԦHvҸ)By=h Qk=Ιo39 CpFjG @ۼf4s Nu` +N6‚c2H6Gh\ɡ t7>:t/@G6Q͵65UaN;,%RW4+@ W!+[U_y߼67df﷿|zz&qHnϮ!l Kg8v Ew; oQ4H)JY" d7~Ą7 4( نFWS#?5tϓӂkG,h="&B6GUZZX!\bU{U>UHHtyGM|)9qE12E| wѯ%XYjbyo[冱/)mbt)<\p䏷p-hsfgr(LCHIN3BS~3S^91c%5 @UjNRO1eP*/-ﵶ({n߿PPYta{DfL)t'c$8Jb۲w R0TkN9 ķ>XoFݰATA(Q M JWC No_]0sq΁s`STHWsE ``5g ᠯKcDTUtǥ|P# Uhxx89ww_>zt ∊W^Oh_ks.}ܖvY pJ3/8gB6gug;0NH ?*n/DG-{[(JbtJ gN4_u?Cap99_HtV3Kټ7zw?Ms>p u ʺU,ļ v5!*"Yb~XĐ9 zևGۥoSfR }nt9oQMy˲U#lWI^֫{sUtoݖݲGqE)yO1qUq˗%ZUtХ;7/YUI졩h!r, JKDOˉwx>;:k5圃irMlkG:r!(PtH!{`[:}ѸQa$iXJƫ$V֣ZW>#ahuٿ21h rh(+;CxwNr2wm/,=$U v %d,%~qJVv#T*D IDATR/ԗ Dʦ(ئ0i4vEF_$"oUSKD5̮T~40`wl탏xN!)Ol+*q)6r:ye?z_wc1⩚WxrM=Y?{ EB$ywh)% ovQQEM㒨U@Jl6Ud@؋%qB{b__zAcz ek;0ι77lu#Y#0Qbe0Շ.9ᙗ_XV K} :/ÑkFEj?$AC~;iJ"g 4 `9eklN}AbTP߆`.U>>PlȮX,QL]{WhxdF˾w|M_ۙ~$)9"(xEy@ٶ `,k1YME\=d_Qm˅+M=F/'*=OZc2%JEʓ!["h(PAaXP*`8$V'[o.mM]3?9(- 7jq<3юܦݽW?0yl $y[U%R Q8 fozzCV^Zi0܆X{rcxV%)LLf7W-;\1Q@k7{첕R4nnA+V-M,앾Z䍹~{B0ݮy_2jd=P+k# 3gN;t,8bܴ'?^}?QbS;U~2gkFw=NrzBCpIp420Y⪒q(Z)C`{q99>iY}#_{(:׍nͼH{*[<~#)+҈WrˌsUˡ[RmQtl|57:&;|oOsWaNڵo71 nEr[hȐqHQ*o Źiovٰٜ#tLjfPӐRg*{+pƔ{?{ Rk^+JrҀW㚃nIr;1<0yp8{E#I1uFSF>DV[hh~<ȁ3rRpJ3F\|ބʎM#J +d,D^J=Q+V HX0rm!Zɳ [-pUC 0:RG9ZFv0mލ~/DiiT:D. _PU`][GU|KuK%tqq'AAժbCR]v-^l ض~~ku[1L@7f;]揾._c[Wk~2 Ds\mam^KQ8Kn\>0%z:w-]w0MGB~rH{]c#Ur"yMҊہULc0!pTmTGnTzhIJ]NGאfk7x(:Yo=nޓG!Oڸap8ߍȆf6I?spGZg_Rk.9lI>#Q{3#/1aT}{rXޗ9LvNVjK'l[J*{J픣TU 8vK˺DEl_Pls.;)H /YxJkwXb I$nxB Ako St}瞖'.KǍR#ad}>݆`!UAϘٸx\_yx{uP2D6UsB䦆R*-I [Jłkot{g rr~K>R2D䆉.'  X:URxu9W=snSr՗qQNW;Q`1 D~ȯZV\-P2a>y<XWC RYy£/@X4k0\>^{b>$AAp-Y?Ԧm ?]eb\{N577h$ξw2Y yChLclJ" z봟7M35=ۉ!4/8~XfUϼNR~ԫnZWT!ҿWG/í2J>9T,8(JAaaÝUٿI"??bIwgwQJKtV,b%( >w ½.sߨ1b,wH"BnwB F_|>ot*2Gmw1hJxxh6VͦvA|1<@#bd󂜾&4 ˁJUqT?AB> LX1`rJԓOZNԠ *H ?5|-[ D0_ W&"uwZ"p =‹o$H9ZXOW}ǻ -C,d7er0y,3S D 8 9<ڡoV.k# P7@>_Jd;74f̬aSo[R;U3/ɴ_FM33ڏvv8%1RxySFN8%Nބv~LbWG'eM'.σܐ(e(َ#mz@$P9:4v5߼X-+5{Jq; j3'_ZIY;x5FnF j\R ^ _uPhh1gM\|jց˻1xF`09T͜fZE(ʷfm O`SgO[qWx7T!gedQ7DLu9Eu3M7zhb\7 q_u4F@-.,|]Ҏ[<'9gP9@ D#Uu!;?.tfM0] a; >wC6we.Z{0EQ ‰cE+T 8p Ob| K["Z{,[zWq/`eU!0jop'/x3"?z j~r}ޮZh&줄R5qF p: 2 R%uOe0}qGv=瀖'4{=@ψL\|U[Ao| aE+-` r2$w9xS] <-&F4.zY8ehNnFRh n3oF)u;6l4_~;ez)o-|#J Qq4)tH02g˪{kűP9uy_ DΧΝ4ںu< F ^ZP1Λ.Nhn߿6С$1E P@=tz}z'W5?6!5}[N<.{-nR$$kQ8g ]Ncܒz7-_^\y(N#bR< ͒FvQj~_B@C^bH.WDPV8sѣ-|t9Gk!pm^uc2ˍ׍3ɚĹy7{X:A/ ѵwϬf}21XN-`οYxEQmMy>N ێF.`<<_x|1&#cĬ=b9H4W5 Jϛg^ZzFZDSLj}'Li|a1׍7gMaXz-;γ&7wc-\F:f̈%cn1hX~; ǞwuP# S'~jpdѳZg/u?" [1t;9!z(1U8k2='z$o>8Ot9i%C/0_sܖT!AYԓ-dGz6d#û{t^o0v3qg'u>z'Nqh4~Jdu3.o UJ9 ʪ~?ڝ`-Z@'<pE~Ux!qz!9tD'kE͍puM» {e0$@F(SIFK!)ikRS=Ժ+?6q\umʇ$=uAX"`VB+>65؋+(WN fƓ/w8\@܌&vnG}i| /sL]Z% =K8ػo,>eex"R&ӯZ-9$ СPjDzݏca%6bCCOTk1jW؋+gNmsHb<=b+VӟL7n5!"u&>(M-9D.۲7 Shgc萨# mC6Ug4}@+MpP0a}yn((cp:HF8L Vw4 .H *%S2V2C`R y-.\JCP GƭדDJ\ 2Y-u;RϚ L]GRklic1k&{O,11ilӤ.& b.ӚzH]rպMWfΆ?Yy'QqM(jc*㜃aHw]{F5^Dy~`ƒ#zN?ħ-`=rP5!QjXzl\^K7:@D$NG_1OrE=ɹOwSAoݒz򵺟ɱ5WT_]Uݲ4Ikֻj$-mzY3#v3f 41+ʵZu `'I/8Dܸ57d)~_)6Oj0g5ot"Ne^Dxjm$h.}9Ӎ6ߑc-VJPaTU\2{BI*}aCɸl]~J_(4vB}FhZh@190\A$s絉m]-O5? B&l"(?PwVX(qv_W5&\jM$_;>̵-Q$";.uI0oɲ& Đ3>Yqf\2,("5OpP igUcZOcLh/VY!(Sv9n9xT ;܆SFoVqpa@KVZfV cbcS`(߻Ixm;x qiצeuJ٥ރE IDATSqjTUyDt;B]5ƕ?FZF ZYB* g{Ir;tE TtI]d%OwW]q49ꛦFZgh2-Fi|.P81MI~ꪛ\t&8J/^{-گ($0#R%t r[ XL'Cli_Oබ_=,{+*OBB,g M},#_Yi>st='BĖFd4Bd91+$ziV2#D]%v~swqWV &f10*u%?qյjv)m%8s>stN)yb|ǫcumڷ_ƸT(^"YekCZ_~r)n1B~AUQOIJcr2cbL8SmbY14U$T&FSR~["^b}գbfzEң/aq׆-Tv0Y•ۓ}½X/R+` e,&vv5c/jl.mV-YS5#b21ʫ8SQDh-p dp&Ct6gO^iھC?>CSiF,*Ey? h=!䱙GiTKelo/ymIkv|ԞҨf__m@ϣ)H*@WnAC٭Bܳ*No7D۞vt&^e!_)m^- vw F3Uof0:|j#TF+)z-b*_,w)bYXt3sBXU:٬ŠwWRjDPTf1 d&5y,iB>_BDixXa ya3n 15m 5}g쓬}g[mӵO Y4BTna>Xn,/b ڑ00id ۺz_zGI We["7V~-J~dePU(2;UP$({%lFF/ZOOgCf8 껮5uΞ|y hrҳccO4Fs0$P|R#&sOR}ELbbw4;]TN=3f/$Mgs9{|}c궽BCmUn–G_Tm*(]س2 C/LYF"!FN;vg\?4* )?+]tfhgEoMi:CSi!:!"P[17( fʨ$*IO͸e*8JAb<tu23oXǼ!͢LNaVꃊ1M = ,&~TˁSI 3dƔMTNT2q(?SvIky]t煗mc i>K: bVAJp+G|[̹(ҀCD}Ӕ~TddH#݈) "G$,|zީbK4_&ROH)ȠiC’7XCV@,ރ?3n>x4 +稪[Bo,dRk~Fs (lĕGL9,GFߓDIjbT /\voYQ E3_ŷ~<&5n)䪒jpָ Q)A`;$^$`)Di3RW~t4^c*`~xB=͹uEdk]Ŏa2 DŽzۓ!u ~v?Tuh`2 fOGz04]|k-i#UM+hS]]8YʹAw1XY\?!H3d ሻuغ'RI;p G5U6#n;Kg'^Z^w4[#4bG_r 7=;]bi$&8gU7x@6~Sn,7SD)E}'%fK8",Rv8$QQP({X`jS~p[׳oq@F̦_B6vΙ|2*K}IRoCh\vpQBlmӽx|bjݸ3(q٧R 2<9gFM?n3M(a|$.wt> x]h;Ejݼ#I"'&fXLNă^LxOѓrJfasE42%e顀i "hGICY8*u'vh8MӿT 0C+c/X;j&Ia:y'9J$QCk#x uY5j%1P5e0A@HacG?7:袚g)Ơ6л[H{t te4-̵FncN_W}iE⣽$$((TymJg7H8;O&6>,qmfoc\i{ѬN]8ۥ[bE̦FHqQ_@HOlޓ&Qlsje^1%D42<ܒTO)lo݅<٭|>a̒S{>30ᔑچȇw(MPVjF AJ jei_0F#1Re͎M(j<6 f͆Ye5nanL%+Xb Cmz+GH|4~GdȆA&tuZ+]鱗0S|2Qd5nfpwrV˯Ju. ETLb_k-nA/:=k4l}Ck 1C*VOȠ/NxcޣwN6b{|y _ %9 ?2*EMYxBzA$H8ETY1h5:O=A0tՇ^RH|A9#ƲI,Ez[F7vͽ:²ES _* sLg; ۸ w桋XL5ߝ]-~#bKC|Gj RÛO`늆<9ZaU翥E%|%1}\1(WT\ A'3 5cTm]KQ̻ΩW#MCHBZUfP12cIڳ2)Ұ霒 hB TT hZJ39'b(ϛ;fT@|ES"mngMy_zHg*CAxt(lWKNuD~F!cˌqgs-fQ CO5Du^|ؚxҪ+Z\\*cyg[PiLI B,Q7U][y?!FA+ϨX$n1>^R`7XCB,眒`^j_oPHYe*\^C9y2E>U{MׇENf9oԅXJx%6rv+YQ$-<&SD}Dk~pc_^7k;/{y] 1jx'j2!!h:VQ zcw1\22g5'_9gvьՓw!HYNZ*cւSy%O#~t8CPkWTzQ "aa,QZlV2o gI#V~6=Ҙng2d.Co܁7NTU!pzCrN!r9[7,]r306ζ7N]tLēX< 0/ !b\wBcQC"C@S`0sPV&e\8yFSh9m(4ֺo[}Tj{He5*|YjU3_{BoJkcrω搬h(5^li M-VJ8w_ DyYǜ}`~ӟ,%HCMJ@"EAP{z \}տ'k;VIY2g`Jk#(hs?02P^] T˾ƪX`b hX$|8L9ȁ#a(yyWa $&RްDQ"';U@QhK:%`<b*]~g AtRwbhoYq*BYj}fcFpV9g2Lv?=Wt !fIhwjeLxFgrܖ'V3 "U8 ,fm\1,S(7 7ح<@ڤ^?(M~0g8z|2G%RO4t!YbZ}J#b@w%;JdZ眫C/Ha3xϏ ^\Ə@9Vz@5)|vl7ܒ $!xLw ]yw(5%#To!X>9@^"R_lV~NՐH1Df(SƁ/߀Pr/jgÇ/Wu, /AItͻ"nM;,44.>` ,%JF% (STTA/ 0P"0J?uRw 5._X0X E]翥KS0n-x+&Jc@T&  .Z {#g j`ƌ0<Ԟر9EX-v8bmt6ۣ4Hs絁hJ`ZRE"Uc^9lRAiT m}z%6 0Y^i{:J%9@ Ccݎ㥦Y$W/Gh׏BsVS[rx괫%ڈѸ^"!2}d& 5=JY e5:gbjOpΙ7*[@Tq,x}<2ƒkx=y vkX8^\4%JTE '0p?sen=!vF*=NᾈWS%LR/ >m3j>9FS}CCd}}^tyY{'RvΝҲ[0gR2G :+R!?ŒLCײmSzx. La|\h4, 434Uۺ2@¡ȣ9°Fgp5.PtBRZPoq*-jM/ΟT ~w_v`W5o53;p"V41cmToB݄ 7#i*Vp q\}ҐD Q*!ҩ#FS_T41p S5s1Ơi:عd1e4#6K ED޽0~ pߕ`AK6fēLIn #D;izY$13*if0YJ[/lځ(j>?آe/X$˩#h"|_pgDBWU U91: s 4LտI>:Kpn;rT9O`ȡQaJQ*GO*&*J>(!RZډ K؄K[gE .\CAܫ4c (5;9㯮tơ{ I똳/ŝQhVm[?=כ0OPy\|Jo#oEG~?ZADU{D7|>ܿ(BKںTq0'5Ew4kV*x r@Y}Elu^{H kdQ/`AQ~@M6jzoI_{aE!HwMQ ((fe8a: zI5Z۸C|uHe&qN%?;ʘvV;x{95(ȖOE4T;nUM{a EʢJosdp"J \'f7.JPia)?#7;]Si܈륮?}y8DCt:pd*ޅf%)ߣ A(#jq#՞PEeJb/ <: +9e o^8\z 5_0&TY˸Z@:3LU:kpy=NpՎۧVyd',ߪWe0kOV[P#8fвZ -Q$˺~8qX=.PsؔnysGbU~^Me(cZ/E*4"JHo VuϜ+h4`APTlݩ7Sـ ޱ2pmڈfe̹ūh'3*cZN7F,a`*D|ž>ACٓ[*r!(R7__$p&LPowZ*@VKwdZ>[i"غ+'unbd* :X2X^4=U+zq1^'!y}6D\vݳw>_@8gOv.y~k]Kb.h&KcX-igzǧJ#B};K'0J#kگڟ]}1BWX=3jC+/3@H ,ބDH ј >PCqdqJƋ j-긋I8o܎0Y! DO9)iFCYT_8wX`jBL 9D'nsND_$vBP̻ɗKZC0(gAԔE-/ eosÇ(|1sTEVlh2wPF㪬Ezۉ' o š;wAҨfQ=ϼAs#!]5OʆG@5&~bS}j@ EJ_xf's ӓhȲK(^#sSGήu'_.4@bfEb,-qDsbvf2 >H,&iTc8^ D^\֛$SwwxY) "qo_Ok9>r羹TʜXpGyj_\S;uBҲ 뇙@ůo*C҉^/~q@98qsLk#[1B#UUֲEI<!#|AF_")Wj|4%!(ՠqI.fmE`'&Өfx"r;ڥڧGC$?pQL (T: rax6}0dlE>TO%0 ( k *Ow % e"HA8//pw, ;CL{Mb;Y1RU01"L2sTW DI")X J??#ĒSNcUM'Uts7o2$Cl7OK&UcBO.\v^ݛ{roj}Ҽ=ZgWsĹbKu<:}mT͛g[rvKxF߃/?B݋(8뿞Jw3'ζރLKKMy> kt^R \y,Ge&qXc㽜$2B7QNߟ;%RJ?N+ǭ .U`*MT9R-[;#'}, c#$LBxk-vxj¶cE!(6 9BShїpuKD5.G Я Ƚ](ô >EG xm OkMoȨcήAJEtB'@B$*qeոlό9PVZnqT<ǛU1C5/khHUIԺj&ï 'ݿ TZ|TQd/JR%oiνF><2♣ŋ=:HP[@ E͖Ɣ.o{P8vt5jveZ SLQ7!+}G m(bKPWIɠEk{[ xQ\sHU)@%6F\[Q hUE G',{5GDpuh,4]Xwv "ē,A4VmnP_Y|eȌ#n+Am(J[]i;_Ѥlhn`0l=t@2PJ[0.ρU6 (4-fg ib*'1[l߿)諐LaE Qb1Ef!'1J)DF>yNze+;j0 `UqfǷ< Kfrp(U+T)RͻF_"{p痘&_e櫣*Dͧ wUuE'ANA7]*~m' ~CLۣWp*ZOU6+#Ϝ@~0f&hV[/| sJ TE៑@xTP%fѭ`L)q;XO<΋ $~K! (or2$Fe ʉ!8g( R_ɜqRh4[Ik3[1|cW%чet^rvxۚ>AaP Do͛cO-f$RJ^ Y1|ޒk,&m31H" Wԯb3ӻL9`k_ӟI6=2rtV[ Fh*͔J$i#V8N ,ȹ{D׼aQ[LQlip^{Uo(.;΂@"ߢ+7I_pH ?C4*_S۰~x@!@e}ef1wzjV[uYU+b3,"+`G5 >j2c$R+ #jz u $is9LWrz? bwD)29_bQleD@ʰo`jM@y90Xm5 na4z(ArX>Ka@(UA*Tkͻbg`RJQ7Nemzkϫ;QpDj~t[fy_ni4*+u߶K']^~@=Z<)wRϦu؄q|}5DVmQp2 8)h ^8iĐ;= >~3EBs=%z\\=Wx3J IDAT vMe~ܕI"R}մ$(b$f$H,&@[D1,(Ttxɂ tCKc>e' t`!FG3*"V`O<\)*TPsd09GHG790-YQ5cIN`x(EDCp;SX*tx#wp'S9+dU}-F_4"?3"K~(e<W͝L,#O{KUeik}BpoD b( &@Ԟifygmۈ("b@TĈP1MuV'q*Jֽ}Ug?YYkXKIlE_PVd( a3=B[ %QlA1QQ孈. t3 F,;(pMoOϼ{I_sC*F\ӸEJA8 lpmoiNDxkسA8Jy'^x" SBFT.QceM&؏l ]0V[>).2 tv%l@+@X*!QWXY˅ =>e2d2#Ba+3LS^87}ɔ(Jk3 '³OeF_x_~vډ?yi|=orƈ˞{jƹDaE G}mM_(z!IoLζ?YXǍzDGefEw_o H&~WTX'wt}J} tS>eF.#Fl`4\Ŷ:C=.7~?MnUG/qH+VhwEmɗZx 63c* :D Nt8nWTi'?V7Lj峣w4usc J!!NKʣR'9**;DԬϽ/&`?dZ931kv2#üb-3>Ft~ s$yH*'RکjM>>TpƥO1댑%q@gEHixw?әc8=} *3iQ M{((%Zq̝+^a̘Zl7_:\@~4|ܐgt_#s{KAQ=H8vm,Ə˴waQ>~R-QJYzgDi PvTX< $Xe?Bozp8CQ(HCZp+A)`*@K |[=Ve'9f(-6\ v-ҪrxBncQ0= acM>k9p)lIq)] N omH J [--ih~$)W8hv r42~ntx)Nqj74GI8J6+oiPƍ QكWQOdNb|ss0f! ^;E{5R SIUCƉIgVDR>X;Y̙ߞ*ԗrOH98'x[LjyNc\Q-if!rιe)5]7CT~^EVWc6<벎?/QBE'IPs-e>b^嬳[li_q~|vCAΟx:#/ TX^>5i:K`}H J"uڅgf@P\w I+U!CZb?O856U(xEUPSiZd=(O#/I4Xb[eҎ%˲>jQ< D0UUUCU#; 'w@@A׳5,8u3߷tJXC|ezC#Q=^DJZ( xι!I/)Pg۠S2:W|NzP>9 I؛$y d{+&xs@zV=DY>5 /V(IveHY6Vu902sV4ovx=\PgE~i,Gs myh~ SD{"!t̙DĪu?^@bq쿘xE:t2O)>Y 3t#23x2܎;ExF k#}˜{0vӌ%/m -\-2/!鰌s\Y2vvFJQzX"bC4DdD^Ur3/v^۟~٨׭, S W@se2&Ԫx+b>8ԟy^V¨1pjGr 58F.Qv"#r)scj ]y7 ?Tj/}Tn5nkP%C<\H( Pe( @JcMCJa9rSiz, $yl0ӰOa(ٽ;Kp]pe zb_У ɣMTۻBϿyl<BsʗH>,BBu8+_;iE,cM¡{jyMjx8Rቔq9/ #@x-:_ϸ8K%YU39׉zo{ u9kDїQz8R4n %:b%,4[NXZ Yvհ(bReۗ!o vb4 bi K 2%& lpK%W6'У;|u@N`DeCT ByKKg__3q%  U+*B6>E{ t1]:KUj_ @S3/cwO$k7c 3K_8@f^H:gE32;w/}騪$z6'@7\^Vϕ1E-(Eu.+mHh m?1 ¹Ϸ־ˢ|y"Py@Yn( #nu-i% t/23xu!u#|s{R(Xƍv^xV}`<5:3Dz$hO|b:ts@ko\wXg3s@BOH؟u יlg۰I- 6^Xp4F:Wq:jyKZQ}}GΪ 4@ TW;@ץ^8^+o@;cvM/w';Rk;b51 A]7"A#1QijJfG%VmƂτ[<$Xٵ8 E!%[wq4hw\P{V.h2O`ZcpYmg97Iu`.@tPmBIĦ^f[XXYdv:ѿ5k]V6#f=St\@)0 a #8UA*UUKڄ^y8+Rpe<$ٛyS&Fq3>h؇ iPWO.#$N;~"8lഃʃ'϶u@Oe2YMɓ:9`eZة$zȿ&H-_|r`v5od:K" uvg29?h%kT?;v4RК ιw/}I E1ĐeTc<(PK1bٵ'S?޶)EYN4jhhr9ף؛ nI#Q[#|ܐ5@x!@wSbIbryFUL @J>{DG}rBwn N6Y~:#m~%P/ qF uA`8^^Rt0BJ1p\tBx MTY ^ u&z^,}kݐWtyQ]`;YK"[ÐEPlV(, /TJp׸q@$N?vpڹ,Ēg߰w?aKgtEc8)cݮ ;ΣSG'LAפsiW M_(v^vJ!,?9WB{H44J5y2HXKֹe"y-H5Sds~ǓƆJYں"@MM.}IbB9m^rϡȦ/&~Y/Zϊ{ QꮟVQrG`%2).`39ozr/VsԮON}3S+1=і1Åf(bc]wK8u TIAɈr#aXG2ꌧ$&"zzyl@M;[aqT6c^|E(FLJ!(m B(Pر "NT"fqb^d$W:{b#: F\Y7'a*z ; IDAT(cz(J|-R s-oQ>5z2ٸ)ڵny@{ E83j׼SrRZBf X`1$tmVX$1EeunXO>рz!0]v5JP # P B/}{@B|ܟʞ@1* &L8@|;llp f Ͽ\/\8ވ*A2͝v$1E^aNcL鄤ܩvv+,^I/%b%RϷ4&78OAꮻ5FZP "K#8_z pR&DNhBځ jg0d"R" nmNz{Q] s~|oԙocщEΛ^2p"WVc^$++ ~ĭp 44u]P[;AJp.{ҏw[ <͓DUtZ&I,kd^Nʪ2օHrwkQ()6ZJǚcG( >ȝv6Iz*@: m~0vxڭG1 M4"Ya2ENDA#JXdq`S/bHe lpU:l|ÕZGwtӗށjlg`=D{أN"(8-9Hr^nDY43ƇՓoOmٞE6@a%P't^>q{0-̼8ͮj+VC,(-zR~Ϝd*WewO9V$<Tt$5jgJ؜dU >σc^rĽTX60+KA: /_v>0nI_7=d8yV^"MS7VYv7~HM_E-ܙ0 KEx؊ؕ 0'}l6EfS/ u7?TIm2xMQuEa:]`D%vxe2äX`h^ο{ ʁ*U/":XN_Q;9t*(ha]7NOy;ta%a^L%U!GV2Ķl#;?R!֓F%nhWT<_B m}$FrPNfT*U/JJ?*L|*'Iz3ЈPJN܀y`OG)Ш_3M{n}b^onTƟ*&4Z?кB, F,Rt+]~-֓FϛZZf~LAӌ( 4ջN!QȒ8uځ݋y"4Ex%˜􂳠2nLPU %⠦ۯ/Ziܵ jPAvğN?@I\?56a%R$׏t Do7I_˗+*u;Q%.P+nc=\n=-r?Q+:~⮘<,PR7h(BzcYrFQr lϹDbc V(y( QsHщ ut*S98񭲃¥+QW/(TEQ+Kλ!+N XJIɩpل_=oBYjst$=+Vk ƆUP"b~ 8rF}ITzs1:F+ۧLS6e0փaqUnUA2Iҋt`EqF)7/K{nӶ s4=ף̞C( 吆4ϻ"/9\QIqq8VmbdߪKp)# -TMi?i;mOәڏkH34DAl̢<J9S֫h"kEᬓ!!̖턢g Ƴ=}{h E+ AT3`\N/{Hh6冄;\xxrpZ8V7iBj3d" ?2`8Zg7gL#jXtO>V9AlCAժU4[ >j#TjϞP?LGϼ< kj-oޝJ$? P) ($r|2-)Ke63x]%} Q(( 7htbя?|5j.cFt>2cR/]w^0ڇO*W Mt<az~;J?z*T Q/-cG;Ͽ޶%R5XfJ2Lqh Mktx8^BIP)\`$HNž٩EbvP;ofAE5tsAف/G#u?hӉg_@8Tk@B>mUӞ2v{\ ,u @у!!fWwq=۟vݿ)sYl:SA-4N"h#-LS%Qli"p5;W es.վѨ?{8Gɯ9C.(g U7(BFH[Q2:UCHrե?>#A:txU[bëXqZkP{^QFs>ŎDEvn4,~,]MlT )GeFѸof_c©/G >QϢ# vz8"uճI/8CU;PD`X>V"s9 0jʊVvQ-[HCHG40[E|2cP ۪ڥzO=}4~+/eEǓ t(jG:sQ9qD;\yRWi b;k6*ڣ?Aʀ^$`$Y븜T:mw/44jW|Z.XNzx >Xt,B& eI`^}/uk1LJyGb"y؁6 }hH;Zv+;3-!6 KSնͩPqcX2:'L}g֥zbٱ_9a;.nc˱`Nhhq'z=,飆YGMHYH+s'jfG^"%SXUQ/נNq0BP*OX*ZJ3&#tLznX?؆O2;$9Xf~DvHCn k1CΒ)wD5YN<۶# u; ^U)unՓu;\QPBfJ(XD"K7N&_hje`3kk~msy2y`9d"eɥ/0̉ Nt AOodHxs,D8#k9Rims.39GdK|%u;u+ӓHb WH^i~xeW9hKD`ѳ( i5FjZz8G\QRUtu3`s, iՀL E}trLJ</lV] O%y$D8x7!^O~ƗRSJf$ T_պD_n)"K*m R"Iz߻?A(a:ae ^4gv8>%\o)b&@O>"] B0D8Qd`haUMiYnA.W/O,gFK?U|n}nO͟vGUT >oÏ뼻HѰw e2z]=j=ieԖo IDATlV:jzy3T5 3@TM@3hKԪׂNr侧!ɂSW#6mle{l[J3NTWE>/BLB9jޢh[qn}ucj,Xql\%oH4|ٝw?D\wݔ;P ZYF_.r$$67@K;;iF2DgO:5Mϭ$BC[wY$J秮POD) K y8eɱ=BTk͚bY5Zw,X~S=O?ooM(P[!J{ߴ%HIwYϓ\3+,hhfA"q_+Sm8d@gF}cŃ^}n矖>8bcvQ"r4"U` E UC [*BR~>/ЖW =FstrEf܉j-=Cgvfpx%/Ǟx!MExZ1e5(UdH b}5tSZ7-vMٹg|B-Rj9a "HF]SUl\M)#b ɴ-t.(6]AOqUj;aLwYӝJ]Hz RyPˌK&nH@Qܯ%G#b~,s'IdK)̣=6C"͟xӌkݞؒ3[wgVkצK<1 N75i7=^W~1y:k(8vخ(aGI2i 眺쎉g'C$su7(Un%鎔2PIH51c }gl Ϩ( ? b$ F A,r9p@ CvџRe産Pň˚*Pb>nh-ϯ?-3b-uVDD,Ϳ\XJmk@Trk2n]'')%gXǍ,^}8DW(Z8mo %:bS}ݵW SGYJ]7ԜsOKxhrÐ#jS@шuP) P:iAک%t!UEW {/K5E*H1BbR^΢T!UR*`qԆO"[wO\ٲ0ɨæ[yjϛNt:<>/R/wwZr^"Ku7O0M=DDI?=uٛcaࡕʡNˉ#%ͩ7QJ1aPDhs>F#`?Fŵ*32ewG,B)Qg]a{]x})l u{Y:5^ dJ16$Co\rݏYǍqBbv#jqM>(yDΒi;.(Ze뒆t ;b6kOrECю=˅k;ϐ3Jkg(P)JrKp0 nb@0C/"!¹v@MF Λ!}_+@ Ȩ0l`qBs_㯸1XiP.@.k 3{ t-AYhz%Vo_|5-6E"^NƹS=mb>؏Ӧ.GxhrC$i2EaA0(;0!3P5B #l@ZD]hIo̓;/ V ӜL)[fvyX?of|g*:fdKcR ߢ맪Xoik]R/[~(zգך4= D u޹ ʻΎ?,i:[El\ңqH)PPP%ycŎ3OdmdA*c^:oևl^f-jvx 5uOm ރA_hBZ!! ;|T5c,#[Ʀu5ѵ2;JOW'hY̺7Q94b;4PG =)E@b:0Ed'o >᫊"!\Ո,e]+ n'ѴZ\[ŤXX,ks%'qIJ=t'hguz{VPǥޕrAJa,Q~=K; 5Π5,x(Kk&wc3IxJgLoD=.iPzS[[ƍBQDQ D~X4nd"sE㪚zOgP4H&յjMvOk{0lm|OUcV$H, "qԙ{~}~wf#@ 9G`Q%Hi:UuWW:q8г>u>Ns޽z׻'\m|JDZ/v/"iųЏD=^ I{;UB]4G׮?_U5Ԑ "g{ zz1 ,Vݝ?u~S/&q1~cs_YI>lT0Hp63/]C=EQ(ד{8cDl4aȕ]xՉ[7o@{E:Gj{? %zqic(\-i 9{&╼Qʝٓ~];w\غj!|D)1_84zH-i JqSaKI~IYNX%t0fTew|yy"HBmNKX2)ZB_Es.1_cyk|XŧRρDEI-.*fG>'v;3_SZӘ |B$W:}:daP|9J|f(Vڠ5.K`Guln^_A@uvFeQrK5SU]J)h !19@Q/9y"~Y?R٬7^N䍾45!4bKڗLV:v dlC(ׯz{h3'_}sλgy$12{pUe4dH(l,)9Njk{˽Jk (_]?UilmJ()P<1"B"n5:T}3mS4m4DTYU\YӴSF*]B^Sgg&T?8KJ/ ӃaΑW2{|˻4|BB߿y-m}aD QѪ_,l}[n My笖?;e1zI9ӹ@BՉQD~Κ2lo 5MRٔѓ(|uEF"t2אKJDf"._j}}SBSjyV3?cm(8DV`*A`bS+H )[oizۓ/F}Al/IJC8j "VsozDbê'72UctsIcwp(m0 `Gny!A]s'kmU["6[}7{"BbRtW 97s]͞5cB2BZOHj|) ,cz۟WPj-&ğ:̡y|wv0IW?[g1JWc"u$|u9^/ } ,XN:kQ;:-g9D4RPPT[̖7ΆO)`SFP*;\3'..!pɗR;%BF.wws۟CIBX ,u ~,zl2O*˼k_4/}P3?z3:^ɕO xW42/(n>J_yϣm Q &!P$$=ditdTQ_Y p .Cةf1u,]\:fJw3خY?%s;Zi(@ >)kr48-Wi2\!F@hCzxJ5qzQit>&~>N;lg_xv*drCs=ΜrGE#`ܻz =2Y_֘1pZ 0eQY&^3\ l{/8uܑHd~K?yl5"$Nqܰ'$BȔN!bU3LͶ/(pqCn7󶚋]tW:FOƻb6E-$9K*ؽd}.S=Ⱦ#,&kЯ\ PDG]P$?)ZL5Hb.~{Y`1#-*jѥBLVAﹻCKmB:mSeZTo1Ȟ1W;DF*j.{N/GQ2ؘ# "kѭ:S畆Z땦 1?̳5Wj5w-U!7h59PLǩTQʇQ1[B$BA؉mķӅ!j'\|aCLw 82fGz}dtu3/wx)z)i0ST]Q./\u9afwdAg3IEeLKچU fh{R_uJ ] AQc,l!|me tWx. }p:uS!uBYݩԼ;gE^|54S ^%b{sW!.˴[;| 7tMBɜͿ{gވ;߫xhA2.VvZVOi=GlVhe|r` 1ژp>z/#L2Mi'NZL\DSŀ h ~h(S5#)z~ z[R_ G'˟~\Βҟ-?QmvRtf  g#k>_H\iq^. .T DϺ@I vzxyL r\u}@좱H >B >-DOƢhv TȃO0u4o'!,e`Rx!XRϝK {|& ;GE,:jtDoj}dU ?yT Μ#%BjC"UM:zTV:"*P[ܞo*n ڭB*IkrGvz|?,*QI:~J:ڀ%V3_؅@@wwJGv@9~9bY\e!\uyڽ Ań!gR Q5$wz0\2gBL{־9/f˽df<Ґh5\ݔW]jlG}z3B=Q=EVU"eNDd!: Bc"O#Q؞Vk5:E 7Ma(]S(| }a]!:R~ML8wΒЋoʁ07cAt3F޹hF;'>j?xc{z !" Wvj]Arh.t0LuIV۽ǐW:YP[}4㍡/Qs( lv02f J+`(e&W9},hR~&)\b5+.iRµc/+3Az#]5FJ3}}A (Xh9JV%76/ u}guZK[拈F"h4bPtλvUYPD-*%ȟ:QܸoAQT aFu(9&F/LʊOҰMZ+dhkޫ.PzfޮQpE 0WANd/s]|'u5ɑސ꛹iciU{6e*O|,.W殶囔Sem_Min"sfy W,ޒA|S IDATE?nWL(vlx[iri 4""D6a@e(YirG9z|$Z${7+7j $ٮkC'N_,cLuyCD(F 6 < dІfvoR럈k$t'# (.%Ӗ ~F94nz|KZat2 I<>R{ͫ*R @)iE6>݁zZ,Q*9K[nK7`37O_|$(J,-|2$ (\uz Q8y":O>eKUAJAN\GWZ-kY-p^ $OyrG/WZmW^jtdi %7^Y*V?pk3SAK:tq9;JK󇘬0Uc@(@k Qif5@k4 | e6-*j\_$ĤW'_=s%ŋG QӨ o_/nz j7_%KeYk$Aԉx2SU&F ±ޔ2xh-U]ӫ>X~pf@ w9O_[>igtQm6XLf2, Z^t$rXL\èS?h0U)``d4r1ordߑ'B[!"!@ RгYl^>r.CVt[~pΒ㉣xvEAm+^): !|k{v#xB Kt(%_^Z2z[>Z4_IrgJ |RoBJX+\sf$.Q8 H6b2ԡ*SݫG%&)=PDDYTe)**tuXthQحE}3H}ev~R hminJ(@(Q'8GI/.Y=_?KcXESnrEς[3uçȵvw\eF$xt dgv5 x2pJxK?ء-9x~B)+RlM}%g2SuJR%v=QGi7[? >a/ ͣz& 0y s?QJfb/h|I#"E3ZHxËaWR{}g 2,X˧ht(G,",-v8|>Oh.tDD#ffv}X1Yp4!RgI:&Tm K RA5 :';W{T=wwzTL1Jdy>Q5dw v<P:JUgέ`muި5A(`2# HW ,WH/Sҟɔ3Lەomˇokߙ8Y,~}o>Cg3#{x) ͪ+ fvE'-hΙj ZWQv-/ׂg2 tE)/(/?%f,)r0|~,lּ~4buCOd óG#3b6qbE#ȣYJǪtIw'O\ 6`r-fm*dh2P 9~tߞG揍AP)_vSg)'@u6N+RfĊJT03@!yoL-s}}gYT"h_:Ȑ˙2Ik}j|3 eWgj چ7U@{;~Lnh9Ⰸ(n~EφfLՐwLx]xcgŲ{nnP  7{oc US?ضb3pEQbQW4 \ߌ@+_\>J zwS00{NALN)X3%ۍ"Y'hC8j-'^`D(#@* L2ԋ(^ ̗-{u-e%n/]ڼU}tu4!w}xLĭPu7ԭ9Kgu3ְbAQA)*E!>AkwMs9X:|O{!$7BwT3@D$0J©Ϲ5緲YeIqvdqM-X~`FU[#'#! 1ȷb.\;?9h孏|A9}2,ZMϾ&&sCۮAdavP[t@ž E6mx>czԶ|2#?׻R*a Jc+uDrc{*Hm/mBmz8^Ɋ00!*+7Iit+n~ƉC~[5_Ӳ֪8xĔ3Adź7#+ M_i )t [BOYA~󯢢>ŴۃKmA:\Ŷ cBhϻf Iv_]C PNj0YO#Кߟhst,w%kKCB~eޜ*K_,l}lM_x%Tk.qGWi~@:/Ѣ)aD& ՃaI[%ƹ|*YATKx4m\1j_G~p)J.۳c%DiiAD7~2Nb!G Q%ROcm1HG RqSz3bg(nFxˣrUƑ%=ua3~#<3'<U3KvEB@]kNMtzz (/mWFPXT'UkHղ+p_EU6$U:VZΜ!GMcß~ݱH}z{iHIo/ jNAM#O:ճ!f~zC\YiX,jv.$Z=*!!qzK>bDǭf#U$9KÀ1$ E &Wd!<)ǭJ7?Jki0Uд3[V@Jѓ?.e%!6E]S](AAFGJoCIB,/>I2F%9O懗c 5|wB^G窋iӠLqWgΑ?n39$4z"M?CX[mYf{Ugzw04*-PPL^Q 0 %@n P']/_BfnĬv&fqE53![Uj^m\b1-kZo1|5G8J!"8t$xcck2 0ڭFCNuiqR`Xii='6+W^Z2g0gԶ=֥"򵕖V]R&uw+{A/kaQݜp|S_@ U6 (Twa(iҵK5Rȋ:~ OU8뤞H8@էbg"*F 2ĢCP*7M@}U/6ZoÏdH{H&oO'H AUӪM@e9fI1Mg^"`ĻH;'G8l@Ko1Y~,Iq[ۥAHLr3Z~lTv2D8yϜ7+/kIGNH8l@ɴ\Om(ٷD u,x_p = R:o2S]ZB-(BD,\Yiɬq|9.ݡCJSBoQy.210eGNbU:b"_@ -fq$Oү0R<0?bTm, \1 3Q&zf`OUߑݾ5PN ZD>,7GW p_gvuRބ9|)fZt _Atw$JʝڦV8]4ڳbΝuRҺvʴ7c-fu@42QTo] h N!D6>ևpJr!g>бpk6BBao( Qm<\#*@:ttP&+Ϩ4a1R j8Sskh* -Gcʄ鸅U}ڣϡ>J30 #˱NoUjO$nb!W?PnNKB?sؐN0_ Z:h=\֛˺TgSf4@Bb0JU%[BC"b{ >ZQ UBHGHAe'&!MoQW;5bw6iY?(.8Ko|ٟ5koohU%bΕ:~vgz uoed#)G X8J0A%NY\8L"jOK6b5[?lyxg./߫c=*)gbb#PG,.>VTʩnBF|k%[+@Edmfa[ho~`t=\1iπ,!+/5?OZ:J_&v=KXī/x3vpoѺuU:&8` A;rj"|Ç_e A)c1o)Uʥe{:tA"֗p^;T }@(̘^C0CS=F= 2+:<{vۣ+!u3sal @Yiw REí_ܾeH_˅H%ózR4>jJ!"qHǂ[?( 3yGL գ{p4*SU=U yX͜du aT-.CH9t]h{g42S5N^sENJWXhݨ.TB%@g4k"6721 IDAT%2u,3:McvN_i.ӴȖxzJyG1Zc+ 3;gqr%q 1JaB)5_~a{ Ȓ{CpċFt l\pHP G!JMw##쓮n{f#Dbaf+.0DD(X0ջ;uwwcSPDɫ(1TMDնP=)=[؏']OU(sj ADbGR@^E\T`2&8˸@&0QPL]ț^{C`dziH_]^O~ͥE6д䓕Q)Q8!2LQYK2  (."%vqLyZLw?>,#6D uQMBP*QGAѨG_~sjc DQQ^ÐS/WHRa WhC-FMS3Jʲ 0Oa ]G!ޑ$a.ђ"`f1GC8/G <r;rU'? 8Cc( KFZ1*~8urwLQBđl,{E_-!tF3]D (?Ż?ۃDw~)iZ 7ݩT3h}lMɩ_=j_0UeySiw#E1twݬ1w ^^d-it3e/+#EQCN*ߡ/d2쌈) GI\Y,! c/o}'WUcw!E`#_w8Am@?@U Bt-<UMg/EEC~u@gilCvXACbxfԴPI9j7$-&=\ xΒv<R*Y/j{TĠ؅RqPm'֩nOZzq ;o_$6B:_#1X\AW ,7ٻeA }aZ GSyZ͎3YTr=p?9sD|.f L8!0ΙÏMK.|bHT5^^M nhc$+Q>@kJsTof}2٤1&!@1H4O _zE=y|>#(յJX1J?t~2bHϮGYN{t(lVt>p%]Sy~( I5]zÏ<;!@τXM;gG?JԐnHR%X F#fS]}-;)BhOEqBQ ]ޱ|H^$^ q('z]0,no It.L k@_v0.sվrtWʤ# G /'G2:߭/JcN`:pn#SGMk#]sﲓM0[uWbRA=o\O"##T#8]73 d}熚yQ-(h<5,甹@8J=eɅ)mXc`.)=NYmIxKt_`pug|Quc\1 ¨/ Q)uni@ nəqYוrcwZ~;$8N=q.z|c!#fi'ה`(z K#aҥJSv+] d?]/O:00=:e܃f+s&YO!r#z`]ΐ]m+B,W=.=#@V&RUcD Ad{xLbC3~njL{1n)/頕OU[/ZWUBk.]'^K-z^ t Yo~E9#*z{GrC`*" ج64'K[`u2M?Ca+(F 6_C R}+J󺌟RiDyDb6#v#R 99~w š|w|ps=)h}=z1C<ٻCǸ`21qז^4w"OoWB?/cڻOB jpL}%V;@*. Z4nU\3$A[xUOg5rUEJA5]_fA Q UCaع;,=WWL|d, ŲC`6g:Ib {?Eּir]1xiwws|Bϼhf7 %M$&Դ/)-.಍%B@ yQP:*RbXTCj.jt<%>et ]n!RYQg3ѷmcd 4%Ho Cm ?mkcCJA5λgVuyI$hb3 GbbL/ Ebgi?A o+Oe0Κ<2<i:x|l}XWC~}01Ay REʡ/Cw[V(PyF."O ڿ#ȫ0"@ H_|-gc0X͜o9@B8\uJQΝb.j`qjUd@xEDo`UuOY0$u|)e7J)-,r#qW[ł$>)w <dS+ozQwyR?DWxfҎH0zxUԭHSB&M@H&a o~VH˩`j02K.Fl戦uwwFm'1LQp,XL/ 9ƧkCb6ZS"yot8Gl7bm= c!'j/lhAĿp?ʎC?mLV t7&i,c|}FF@Ugٹs%_]],PYt[2TUtuyMѾ1hKת?\FX왁غ.$nzs-_Q/ֈ/&:14xy襝7˴n"M1v(;7zLy#g%Lx_43*bft@E燞X 1V8.]y3B(湓*=Χ=DjLpU̗pRd r֑e^Еu1ƀjfmx " s,1ߋ͂lfS魓˫^S"b[ٽs|Pj2D4 odAoI2zdw4-Jah8x /$e%zi1VY5L-C[ klYΊl8z(pEA0Y谁Д%z}a>Sݚ@]4C_ DucF)mm4sm;rV?qY6Ǜd&1purë{yg"ݲ3}Y5^?t&= doM&/Wlbq5@$r/0? 63ϾVYM F ?zx[AL %ENW.JSlG .1ο 89{2fI i ͣ^HX.<}ʝBBUe~EɝN,Syxڞ.ql >6(nxBx:㨗/fCFߵV?[إ}w;dpl(aܧegFxZS!%ץeΊ7+.fo~qg߸ "'FCP7^ 'O ܿ|S JFhiP4: 3^B *)D1X:]m)pd D3*r3 qc#DqtO{FQd{Gd:{M$-|7Κ7֛{[LtOӀ^BBBx' $;:ޖOȪ,wN99'+32"*3bѓ_"LH KyPemb4G;9;%eH2;L A55)\J~e[dpYzV ]8e힆}KD8Dt6TQl2DNۙ?uTۢsZUw)fC{q{0Dt oOt ʸCiRNp\(xp !Ow!w6HL`NR6G$0tV"(._%h G[vi r]fLD/@5bTBMfU@((W `q`H6vgY祊H^MGm-֣%fd)F|y゙Uo4 05㾾D[qAn&^|y\wiU@o~q;Y~lKCN/* `Ⱥwr̟ϥY]`; u)P>Kgm}l=lFՌ=oU,3G c.m@Oׄa5mU@P`$ݛqv,6șލ!\wSn:Ž⧧:ZpPו? gϳ RА@JČ1e41N˷R8Jua%HQbiZ1ſ%PTR">slgנk[AMmZuT)4{w B|x4)\qM-^3KWo2܊GO^ 2}LRc3:0v.z j<ʾiRQ ?_WjA7uiہSڸԿvхP4S0/l\_A佺w,ۂ㽺#e VW!/,pZJm}{oCEGb? `Z4D8%StX9QmxahV֢ە7Q"E]ZQs+)dL./.Ü7*!2k p4DU5Q*%4"LXC&IopKX2 DDs[0@r9VٜH0ٝvuU ˸wҵx}!Z^}fvIF`궋e[d |>鱼U~SIUdP GVo; R8/ PwNϙDE+\ޡDIE&h%j4+sQ;6؎nkHU6i}qk],9z?Mly^iXYc22%}fr"h KQ vxr2/g-'E!"*/KB[ʍZq*Nuhn]fT! _]K22yA+ #D.VR] "26If[ZʶW?qd-{9@D @jw|{R_7p9[HA!3e[A69W/Չ3~ºuwJӐ;cJ'(ofL='AFX+0՘s>/{Pt(,II2j des>tmۍ A&dI,1'0?S ގj;U 5O")ȷFRD4,{ݾ?-.WufXQSZ0EON$EyedM~zEuY$4D)SnL27pgK6Cc >%!DC3T0oua ,7o0C^]دȻ]BY)&@Z&,js)3fhg.tR2P:Y>v #Ϗ?øK WDڼ#?֍V ~"@Dh K?*fX;wgɟBM8׵~TUyCޭU԰\&>{!4OgWً,!c*A$"k˪=ȇ tl}_P8ײ-j]&FkZKJP8 L:*q67_@8)cklM7^[j xm `dm/'P\R [l҉TɟNޣD6Y>Z>^""\uhl!_bi@19E+Zb:^vv@-w-!4sib,D(o AmABEx8潑8w`o 2Z][Z!HqD>2 9kx  Bw&qѡToHGɏd5fxO0:i޶,ù$=9Ic5&=AKQ q}VTԬE$_&H&KƊ#'L} IDAT?p M]+Xf|p?~gel.v .asyt}U I0c µӛoV_0t5ͼgRIA?ܖ@R@liZZgAfCt GΪ["ns(c يR)?璪o џٙDDTVKq!qNςQQM"e&tJXQ[0U߲3cH"kQݸ57"vu2D%E 6 >?dzbnuP:Uqth#F~4!1M"E;qv~ 7 ιVT )*WHTڎ]:9[řKA M`āXYci4ɧ/?gط][#ncPy\qnĨ4ԃs~{?~GeTt*4pB&1,8mㇳeM(Q>S޼d~FQq=z/K:ߣM';KOi;@Fu dY!7W]EN%-ʹ $QJ^|ȏ(K'2JƸ670Ə ۴A DDԱ .Wcm/=9$_?7C{sM^̦@ISPeV6zV_]_yQX?+kHaҍN*e@op/̛{!`;Y}C^KSVಅU6ev>r$̩!0$D`Xt+" xE,\a|KcH] ˉZG9BN.'\7T,ȒSg8yļRJwt*SFhN4o7¼HOOVlRV_9?W]8J4ɕOcc˔-Ŧ* D%&'%.֢͋RCYۚm==q{?9[jLNe5A)u g Z*>6HcH-[Їߛš,2$U:=:kwiDQuM$9!=0/CT֝0[\ n}ц.cM6 M2S܈zK!lڹq"RRЅ`ų~whhOH*OL)D/D/ U$i+oyeEW%OTߔ $R}YtN3'u :ly0!7L%vMS?=B/31|JYf0w(N|}%(j^>a飯YmE(-16.I㞘sƊxf)_'mΎ[:k~[xo;ʑ]M*p0݈u2AqЄG[ˍMyo,Wu0׵~]׎=C]J<ί.7~xq'^z|6"Ekv;f> Yt3VՂHX'ufo`f/V퀋(Ȑ*H3P8VI1B_2-I =.i0,/Q0>%"PX\7dXfQ;~=g;,On ΁G S`JÑNZ_qbh9q}y&2%IYRqA/.~k5DΣb=|%$Hѣ_c(c|Khn6yR py.{32_Ux^[Xu3+W˥t&0 3AQ u9~zϮxw}hp"p<? '9H /~" tpxn_Ց 6džTsp& ^^B}\@ﬧ&T ~.ֳ+K|C'NS4 6"O!ˁX>PVJoÆغH ˘T X}\|+T ` K Śtsd>(5n'ZadqCDߺ_\4 &ƤtbM1ўT@ |`7v?+? )blnZ$1SY^R0 hNt+h1 ꡯy~#b—e\;7ٳx=CY(/@,+ Iҟ7%RZ:I3*/&qyUTu>/w]ѵ{Av#7i+c PPCHTͽ}`D?P 33J+PU}|hoppT~ÆJ /.3"A:|ij3pχt )* :qs3@l h {gq-qRƠK=X-@Ppg$>j8l:$Y>=_7WCUwpwĺ1—epIz\lp,+ҷ+AQMl &E2b3fʊXq^[ͰQOKKm^mKLi ʒ~jُVM3z_ЖK 'TRZ/'nWptϊ-째0 LUA^ .7$R ݠeedư*$ oطG;k M}]۱V>vn!o)G`~kB "ADgn0 !_ E;Rߵs'c/\ztAB` .*#o1~%(HMͶWqY|Z*ʗ$&!J&Uղ?/W2Y 6]F[1_yli'dgqx%5-x˭s67 '!JMdCӢ Zm$hnTX26;71挓F Wjk͆wIFSzҐo06b"$^Z֢xa"}7.TL)jhj㫴HFF`~:C.PF:B 2d'VOku(C_b$ћ&qWlhy~,.f?!o6HHa*Hlқb ~=`.`78_´njWvUj\n\μ+ys)EԫY5ǐ! u&>۴ǐ# |o,Wu8ϵ~'su55=]Ҩ]v\85w*!oŋB  ր?HbuhrةS4`6k5UԞ{%IƱɐ.@vėRB=^]Jjj$An0y} 6\̛Tp@ӝ6IA)˓qàO5h+*2Z$0:\֩yŶJ>ɴ^I5ORz)>2ܿZ^^7q%VǼnڞُK (K&o8q8q|JgT3(F\E7//Qx9Idm|hcЫ+szTMn7Sevi׭so1&z 1$`HBD҅ }2)u#D4v@qsDث+wlJp"ƅascco/WE^c۴/^!M-7V$Bg._SWIOL}ًD9 PN* SKq:̱AɸE^7>= Z(t.UF.oKH]Ɩ}&Ix ݍ6IRl ׍cAam;@UY`z ,2Ƨ,䂷йr K7]bI-q8}k~} +D pc5EÀإV`yϠ >Ȣj J+|Z(蠾˶gz a!KڂL,+&k$]:)ߖB7t$\kufWxncL1yuȥ%mOg9b{ &"0ADH4ݳ/_$AFkz7Vˊr<^龥[B "\DzN_˖ 9ʨVࡻm[C(V( #t(//!v'Tף0Nnʙx# JN`>s@q%+)-Č1l E w ]pcRan; |T֢Y)˸I4U5"+.ֱ$nOzI=q]aN#?U1V}i^p%׆\"{ݏާ8(_dxaJ+|)=){y]7=u@[hXQe#ז "ѵ;klfIֹ]"lZPy="Q%oFP*n8n`Fk#3ōД >9T.w4KqvY֫Dڦ0fH(! A^3 VoM`$S+:^_ 7ܽbsS|I~TT9]8WzD|THI·A/uoޫw, 8ױ~RM%HЅ bqqx+8yACԞbY#91 YSKg>Y|vR \*$,c}PS#-E`uؒjnÞq{KV[1>J>~jc̫әP`;iֵ!V+$^_3!oN{oNr.ʒԳk8U){FJi# Ȣ/nT5* @T;F-|{!&^ƴ=HGA$J.6qir$Ӯ+~@K*?+\Qqxb{}€TObK-嫄VϽ%WQrw ?d |ijŸ+77ΟZx 8VՋFwǤ '},۫5֗"Y0B1a|TB`2 Tˋ'Ls/lVgTw_+#,݃{g齌lmZ1i0X]G<@* ۲ߤ2R^`i`qApAؒezwd) :YcP?/PShV@+ywǥ6PDDeȢEk1R6Q}P^+2}1m@inEE6RO,Zcly^#~udk'n4œ^~9xZw̲p~8.G,'Ǚ2DƖM{'*X5A BlhBޙSG{6 O8ssjt=1YLdH1 }wk]:^YCZ4JW StM{}q8M-ji-#4> u(N^M~",d2vGDمkw4?_<.|{prVOISG7P*7Fcbm?u$7W&$iɌ `maZT&{( I/6%qt~n~'4w{}n0zjRtg@Y,)-L_}kmG˙AReV ndሡ3A$4F* >?b 䩅 ^ [n[Z$ck@qjHenVОys\ ݥ&" " ]ѻ'pflj6"U(xe뛣cx5]1Hlhf?sAzwmhcW]2DG" ͼT>0)\u~᲼tCog>Zg(DD4@HQ0HNI'ωy p$vB˟[՟,Z8ӧ$Υזc2d$,SFc . 1y}0PSmTscmӬll)e6l_05 K"|k-ٷ\6] R22̦ P}MW^_?ys%u2z9OM/ZSjnHoa Lc߰;2{ O7g%d\VL"î~>_bY:}ϵ9r7k_|u U\V|#R^R (=ўf%ⶾp/A$BBT5fMApH"!0f>w\{??=SP6Ƶr%*Q_+|H`!`WLםjOq~([c0  SW<؃S#<_0dowś!Kt]ʵ IDATvK1A\h5NU}\LK G ? [ۥn[ŜIYx5Mdmu\$tsP*~`ً=a-㽎(uu1r6q"*reM}\B G7rzɊM%U8h2T  潱\M Z+۩<b~Eh5>6FJ@]`uC֧GtxE2nPeDo KE#tܒ6Q}&^_pR)&KڔQqH_'gjc!kL"VA˪F R^XʌBRiCĹԒK*LC".EW7gG,R{;JmK-~KP*byxpssAxӓSY(,ʊ>PD/-Km{dq%YYAy ~Jn5RͰ~Czu1}yH]YJIh1 "{sY˳wkk}qc[CM8Jy!K5  XcsWE [N8yiT5gS16vbi54-Zاy%7@btĄoj`^Zyv&6a9Λd_!y ٥XARR=c9}zS=mC/ fVgieY::Ͼʌ1jc.BZE&"L,LR,'Ͽsu2XHn<{Cʻ1Z#Rj%_贻f,sҍLPYdYOwg7WiR8l[-,MΝxg =}ؔ{^] {|$c\a hT@Sf,+oo7%h /HE@4_t&lo?"_#z9OaDp&.XlNLUّcj33ݛ-\*.oѮ=b+!f3?hkjaY"y kQKQ?_{rj˕ ""0ɐZhJ+W K%2}a~[YM=?eLX@ {cl6a}#&KꔑyOV_b`ژa`Ib]c oi72Ì]dfZiVD(UĂ9Kv IdxZ2htcXR ?;C]C\bl $Fdd C_}gqx K]>C)dL18䶒-{S e(}vj{}h $ ;kw9A]<9/vWXJiWw- ZnP~⺠TF`,9ME"a'P12˨gffonAӴRVQv3܀ ڂw?yQjNq|9 3!\>fJѲb 9j긦IskcGG-[m}u;ウpA C,h{-,c@@'zQAxtm/:"ceH]T#0O5 {oLTK1˒2u?y.RO|:f(dy L$DA+LGֽ%'ZMfm[r/kl&q֧jjjIV% Lr:&>[b|f)(en=7{|z25!F6D'p~DQ.C-ZY+?c(tG^-9wSי ޫ]0tV9<@@od@D1;*۴[\R]#?yvVVܼ`Vm[K'k;hsBR2ĐaL. vF}P._~rz}c?Xd7` 4QU#T8ZNc D%E à2[ˊ[qELlsm%H*0fL%<Ï*P}꘡ K]`}Stbgjlep;3"Vz+UݞD,%,!v8d@*%%r2C1Y(fC ~&[ QذU;Rza~17JѨ*PE@z?ve'Vl&Agflgj6#V(KclbF|QPPsutkڊ 2+y"G ,YyrwgeEX] u ǸB܉G%͕5)YdՖڹ˖g0"bxp] ,1[w%ey'~&t+*\)/\ZN@Ϧ=rm=4F`Cl)N d #G] EN u.;cA@L݀2t zF~f&ȟ}W`S ( "u\Fv[9b>mH s@rm2ut $\QO=$ɶo]:$D|V֘I2:-I-rA,@@kfɖb v>steAD wҩv(X'0[߰Gn~KVYL-E%g6b@ 1)ΚXz HMu䰳ZlhRJXUC L%7PDL}5" Rv8ruknlv32S\9cZ7t9qRʪ\J i0ѡ`GXS4c`(?۪jkR`ֽ Go܅J׊ "9nNVEh|߲OBg [#0m{!R%F u5 d3M&"VuEUl:U>5l[maL'gZ1&!彺dYj];FL΃Ca4Ce{%I< gb0w_w ѩ#3ҏ'\s)clb:+n`j.jfet XIjR_`ؽcB p qnBα{'6s:22&/@4@mt UEyHԪKA)r;M-۸SEK@оbK s݋ I|{ 2BDA^)E cs_ӯ-Bq~j\ P] DeRp=ބŽjV>.U:)}fME%rBqjrDt|\U ST"5~caE,u Lq1ji[u]SӼ;H+HՐO]:6=7Sy0v@_Ei}] 2c"E DpM{ s&IF!1EHUB5dD$24>7۷d4z[mJ"O g߲ i{z:?,֍l2utt$0Q1CqXɢ3nnZSIX8Ջq%:R{y)Dr2f;p3mlHߖa8>H#c%Ӣ8HcO~n&ͧ'gnЄ*Y!Kwؚ&>\KT wbPj.&'`}Fl'E?꾹{oкz6FmE߮U._UOfl6EI8t|E?>Oa ]gN;N塇E*K-% F:k;GeU, ߌM c/EkG{xÆE"YKZiQS`j*wrX> m@Tkh5o+ fo)74յp@B"b(C l<ҷf; !(AkKAt̙]F9\j걽>dI6J:vJ:v t+.e6q]X1ûX&H fw {v @د^Π@:hcv_qoY ŎgNQ{!v$Nd \^sw+j\@<ܯzNeDd3WzjR~5(eN V"طg$|d e,cW &ZѮ53y*~X[v1˳$U+h(mGh摕tEuJRØG#ݻN]=UEx0>5,.{{U{šI $$~_uhzvöS`Ͼ;c?Po2nw-pu +-?yK?T:}A(">SM&-ȅes5l5/] E$KB*l-v^T3T/-%PZnMOm;̝ 12~ h&'e&[ )%Ѭ4v&`&y/~D+ tK{g"ˋFNY aX8#؂&QbHT ˋJ=|,3Q);)߾ aYDFRұL|{Nb,4m5L66:&IL)>_&<(w){F~#0#w7/aZkmlEuhn5jt: ?J@0zt[ b1qf*zBu>'74 U;qdɎ:4,A,tי [Mv"(ᑈ,SmWjEOMmrS u@?&!L|x^Ah FTQe_Se0?CZyo":J' cA_ AqxJd+iE$c̺'kMz:JO7> Bf?dʶMcPVON۠2m}I?S1Q3Y,s\Ӑ!D,v]k%KStqת::k#;w`p.yH\2Pt驳%FO:L1gi.q#PɰޚI?zє^o"^oO@ʑ:x){r#Dj*:;);xE Ÿ4?=+7r: 6SꡄV9<ph-CS>!YQ9BD`C9kP8yA$E{69m]pEDfj xCBHB >sϝO̽sc6g @Y$EI$m[Ik>V qfOwvVVFddf 10LsI+32ƴAyco^_h6O台jX 8{_m02Q>o)]B5t).`߇o,[&{X #?ND5||DfÖUDKv*4˧X,EH)a!x(e_=w*#?-X%1,΃Ƣ+wM'?TM,ڹժr]NFrIJe TXn8w:ոF(<&Ʈ*)% ȅ!2p$??'o 칇!%FŨe'g/&|iF-f1I3&v YhU ?- Jbo -Š`qN5)ӋW]>;z‰|  kЍ&}76|&3&4H"FѺmok ǮXC{0 ""E ;"2MÁ~+#&zd`V8,g(_qXW YJVat4,˱,%X@0;&hp|.hܤSeۧ4h+/,ցbթ=].ԌHyJ^{)O0x84ʼļL]#67o6kzT:x=tsLQOsoIR IDAT>B_v=UVƴ|8f&_xLȣ=r#z}wwX*S3έkfo0@_$}ZK/b w($P2R&B?, ߖ{^A`JS%ɨ 3~8JIWQ?"%GGbZUD®cK/W%dV $If8ͱIcY?VA"5rX\{sWh1[@AnE3v[=ymD"X% Cl/$$S•>eߞoQ7r g[I]?b47)QԢ0/ڙ GPUnο5ťo} ڛt rkq"Œm."of>y| &OtKR ?sqEβ}:s_WO'#o.Eb),L\D>M=BW%XUQq毟)ZE=涇[3əJ7V2r2fUC &`Ѻs'|esvDP\DB&GpH8:{uAl(LFLg&yq`i. <kK zBq]'}d9*pFriFs 4|?5T/(VA#@1?Yq&#e)t*\uw]SdD){!K.|/D덽4#:Ue|;,E-oXM^_]>/\to7b.n;f ܄w˒= qq(ÒЮiUA `,Ȋ|Gioy?:PY&XA,1m}|4FQ+'epKGU[~/wSYvhkA:*bg^5rrJ ]( f^OgFW1 A,;z{W9Qo"\^q}_x-܁>^|zh'i@8 3'zƴ;{L`Ƶל>g#q mo3mH6 !"r" "2x#c:-Ҿn(xpH?F~!|34py<GkY8 ~Y .jǠ?& Le}p,\\1c[W %j{%V2J@H^W.H !CX 12DBkj;xYNpΨQ#hO`0]o?ZGt* x(y{IhzʔR =M\aI 룇nifۘ(Kq љNŔİ0XY>{#Czv͗P:4/M:֒/t\8'ݤcybֿwGj,-B@'|5%^ 6_%?pWYulOg>:6l<|۽9~l?GC }/ ~^WXdA k] J#VY(.* S0uGN;:; ľ ^9B_mnd6"B)J=/zh/+i:S'I|b6G Y`o>SzX>r"x漐Qe"{kiա#gZ(eQ K SO?h`~8ת7l"Q' Q5i0A`" Z\OT80BGVfo%g s#5{7 ( (bRe{#)D]äƓ)_Gg{2(AB9)> yOth_%5Ɉ{"cV_ڢ_NODb.q}zM,$Və?""i&+==}AmoI8O2ZkW2Y %K?46b20PJs4/]XopH'sΨ$y4_œ(>E@;gv9RhK-$ 9f9\PPo.{ XQK ^-bA &X `;XUTϾjO< CXb'SUߗˌ^5 Ř0DB !H92}%&} r&D Ӫý`/d 56)3wL[>YO3Y.uUEވo+p(;'3ӟ JQDYU4*l_`@!{ׇ7 $;jG):Ҫ/IQ9.-WŞ@ Yp3U3CSaI-d%_n+thB+k?ZG4]:1=K<#]Sy5)\VEAzqkihb7b_}}\7.1r&Dt53]mHJ{Kb}5ۢ5SEyT#>!NFbD7hl+W@뺯Ą @U< ݞ~bpC,(0- On1>uI:C;NΟY*CѐwA!iO#a ޡSPWO{pȌq+u] f_YL-w$?1ȊD5ًIXepkx *y 3Y)^EF 35F[sG{~8J)Nj 7b9=MA],He wN=$-7zU$G~f*@+0d-VР;M{- ¡cLs꬙[x=.oro(4Z. q{He&ͷ5 $u73]VJjv\JI;'o]Q&!Lu9%u&_*kM\pa&}+k-E+gVR\xYP6R)({4 EQko~.M_T&kܸ#kMJdqmGCGbe4J=1jV1 nޢgp}9WmhF$X݌Z NyN_[o-pT}o7H EH'EUm{ion=mª B67B0!|8GJfupcX`O4)Urc82<?sul`s4^K,k@sxsβ~dQTi:@; RK/QUNJԊ"}N(ͪoB2%"2FDԽȋk仧6HϝZ*JH1Rm?+F)1CZk#֖vv @uxq7$cwv,J=(Zkci`L(!TV.yzmGu@Rz\5y{#,X)"CHmPړ`ͻ֦w^i^U"#9XX}:Q%'Rj ɥsCB<;ql_m]QHM3P¤"e H/xcq!rƨ $$iOL͖= ؓx#({t:AxLR9Γ X87l/V *kX#"r΅\9??rK u٥sC =,X=3tݘ0&OI:^ 30d*9FKSQc |)xv(*t2DۖΦM;쉽 TлA:ja鬥vFM8ڂ\WBaA@UۛU[ *[PUrJܗN+Yd[^,^Vyh[ mBXΟA3Y_ B5'<tD&+|=q=.񵷧O'x2p"z )u5?]Sij/G!Й )_ټu'2hHwxW_n K"*!XD`5w&ήY( IDAT(*:)J46ÿx.uj\N:vV_~ ^^0Bg<$UTj[焚+ۛClq:Ob-u@ialu@hTRU~'hwu獦z@oU53%>ZC4nKs 0 >1spKvG5EcPwQ]}ZuвR@gy/&4 ?S+&+L8瑧 C,/HsV1hlX)Mw (ga{+ʘS0 {=Fuz{3x}Ls ,BۛiN:U+!+lYIVp=%d!b \YmNEΊۼ[uvC%x]ulDG)a8 G|9 MoaIxTUC?6Dj 6!c[Y$6("]6uJzZ̀[vS]up= Zh4gK A4_8 #!"d\^ڂsrE1,oع߄+‘(48$EcU~Xb7"dT: "'/^Mβ_{{DN"o.i޼0 _쌾@hDZL PK粮^X* nk`ֳT{PMTذxU~2l ŝGv Dm{M_g fMm` P"ڟ.D`FS}v<@ҡc4@ Bio.Z>OI.O ]tyÿ(; RPZXŃ~cS|Λ8W_n]AiA=aM:Ç MsGDV '9VگȠQʩ?yc\^E03o)zLyeHPӢV܊`N'ݠSMg^_;(PיcA}5,醝B2M%efoBB$%Lg./_ɍ{e ;,ve~ .B*=KV={39tT'8]BˇN \̍kz{YÞo|ݽȜ]U'RE5E bٽuEF/wEitQU Q7}g.֜X8Tm+ 1Yf0Tmova9y{PqE-z]گBW455G!5fC~ݐł2b79?E Hlk?F[Li2-^2}⧨IÏq;8]C _UV{Vn yf)Tqt  Z[o~BBIcC'έ`51x!]c9M;`c-랙.^_0s1 Ԋ:.-.w Ty`FF0%u¢YT`q$R AZIܟQMEQ"3'~wYhqgy;P`z 4D^ DB6AgDCg//wǶvz|1],k[OC9!UsI T|dMFl 58u'Dw ѷ@hw昮JJt7ysqGkX^|Z%)JS]hHyh#W&wсpF/sۡY`,L=.lrizjڬQ*etVâ` KPA&{'|j)C'g[ D mݓym5Cs4,`pR*Ypdu'(u2&ò=ȍ|>9m{}Q eTA*|lL Ȏmc$䐐=wTo_4OCfxO\"іLv[ `[U] H"jzwRu*zfX,~7E\^aq7XMA %vhzɆ+_};#^FYN>M3 y_OE=ۚM;1 "RY \'6u]b:3):}ug_L͞. 5 yйKD7>bu`1*SPR$GyYEqm|~YE)7L:={Z^g<A9)2tL- vg _PÂ{Z4ث?S J6"r{'SU',]ϔ!A`$\;z(EG~'[Q9Ci̓D/t?6E3(VyZXk(!X\$VO_eJP \GN>S}##լLUs"]&W[>0sJ"zӸIp;Qt~k+9lt%_%_5x)'*uY$NXe\t8SYTym8ʻvU:vTQ=JW:gJCݕ@?ztj\2rsc~8x9|w z嵅z{nbsu7(e2_x~hqQgm{j*Sԉ/lE2Y!f yuj1J̔:O,>w0*3LQ)#A Z`IIg߉,iS%, .`69HVȑ>xH~ujh njB:A$CA^Fc]FcСt%H:C2Q5k-\-7w_.xm C5Gsy;PA%F@WOip QʹHh࢐;6cT+6)-hcZk>K ]Y!j F?)yb !/S]3L&]sh=h]2)NJ'nlg DOښ<\bM`g Oٜ NCJQ.UA`'xfs +DӉYQ@ʸM_ @Ҙ7LMl*p)X$Jo;XPB WGfgVxo01?P7ز#Z9f?%F4 2"g:EXpEWDRL;ǣքJi>*œ>\Zr3cWoZCg/8pH-ן75zg&/ {$FmϖW9Ⴧ ܠ҂seAhw"2ƽ*cj[zRi& 0xx'ׄswLҚKkoEs'ēB.Ou ZNBJpDߑF%+ ML܃5ڑ*;Pݏ';zBOn%@E&|qUi🽈 \cF[z`L8y$% nNLoipԓwOۺG- /F ;yeDmo8zц>-A).hNSWrD 'P -k{9 b]Ƿ~u2S`~;፻CxCҞ_ӨieeȖ!tJpd|sK]I:_ּu"2+cprm\PPnm? FZ,wlb* -FE3 -{ޯ7Ilq.Q=+W#/jb?H 2Z_8<#":51v!\_M=QW- %GOј?(^9qA@#qMNM4"KFh,nAxuR Y8P7B,JGZB{^~{Y3O?! +n ]g3#00P1x9pƸ$rWښͳҾᗳ4_["?v(0]":5?ـϝmN#ׄce'1HS@0m?~ k{ 'V.e.J`'|LlxxMk EĂO뷳MQ9rg;GLoڶ?xUg`qKDϫv.w/z v-߼ ;52hٶEVel:(7@4꣭@鼖5_5rN+0buP8g"Ȳ'^J? &鸐L3EJSYF%b:AzY =xBMIه0m.5x6,DC:0:<~ȓKإtXUWY(kۆ.GaQi@s pAܠ< C?GU&\nZS?/ ڰHBQz}aLv$6Q`Uoֿ!k- h9*>GnR7rol]]j"Ap=¹%%iioxLe Fv-peBn鬐N3.Z8ۆX\L}*cVb.ߺ}g즜#"^{΂aXLg(jxċ7>< lϼ}13N!pq]p=\67*͍M}Riy9vJ!pu*:" cc.pDU^nAx2_Ϯk[,-i3j-bئ5ɀYl;Qx;yiosG! ?x93yB3Z\Y5 70rS˜ۥxvwb^nۺ72֯vQDdb]DӜ tQZX+,h@ ФoL:j#67 pk  1)}^]ueP~J-)upUn`TUQJ_Y6TBGfʄ3Z+@@&}J3]Y0Á+$)UD %gOߋ3N] %@gAsŨ\=nAVؑL78zE紆ZjwbtH䨦Q|NT&k-4V #;)oB>U)k۱[JO p`I zCٶ rn)(1sjy'{Y uzcDf=۾ۮ?ᐐH9--IYmZc} dBiAϛ?y{ N WENƟΖ~Ib 50P@ŚۣY&WKA[Ԥ⵪rOʿM8UGQ_,:<ߑ2% P1t`nHdէu [w{qim;_z}ĕQ!=oV?{P|nFfD0]OCp˵pXŸeP(,hEb ߅JzUHnk<0?I&+rn l |KP@!-e.K**ݨlc%&RP&?S Y3TkX Zs$Uj|ry3$3ΝF,kC2 Q|*tCk Жc'0Nc5olaBhY#1tsfdKA4_Sn8&^x=6 /oMHJ\֒.A Bs9 SL{[)hTk\.[ (l LP$:w=}ֿ0V3ܧe7(7tʪً c2iaM`|YkU@H~f7+c .ַl8]a R^߯q;fQ`#Ɨg9Uo6}eDJ~Nn^?} a- (6tZ$"LyVg@_@#>|4"ms@1!e0X7r+?^Vr|O>ǚn9tB)AZqi8_Q g. @'}ۍXFf' /_1nT/}!?obZN^ V-Gɉ oh^ϭљ8)?¤Ӿ/{7~{!a)ptR,Q5qva5Dݙ^ՃUmb]g3Ī89Ea_P(G6>B > eׇ[b†NӒ0xV_tCZ{x9͇T*&!,5Զ B$FD0 }wu:^;/#.wKJXY}!Raeg[@+_>xƤ#ׂLkPFUkj""j$.r\EXYF՗R,u@*QQ!p0[و|U[|+gR@ E:#$9fܗY-/Cz{yClʬ㾘eдYh LܸEx!i;{!KY5 X6d[l^( ?\+ !a4x仩߽S}uDi=2+D(ZH gd @1>8Fzoaӝd*.)ȓ##4Us* ı޿y?p\asd E?|Jx"MHY{})K 5mV''EĨ* l*??a3;{>? ""X*CǥW>} ad` Db.Oܱꫣ#_3Dh͛Yӳ\"5 ^zgB s-f, JeK2_VUPU.G-c`[pFf,50!ǵ!o-!Oy0 |SR J( jӚmֳ͎!rk۝)|*0tC)eerW{g+3Ol7qF6)/sGGL Z[7nw-<'lJN̯,0ɍ-͙S`_]PjeĔt]iMz*KHXIĢ˨rz>T_q%}{0`VVʮ&s}C6N=B(R DA΢7K!P.-B u=c\*ͥқXz: zkP(_紙c67 ,' mEGA[nTN25$NraY":V_ZGxCl2Ũ&f C-GN̾d &޸.ɦ%&-QJD20 )ɷ2u:6Mbcbe͔SQ_$e2VCۺ##Ǒn2 >-q;_}[)cтkve%]y'LOG{ӽC)(_JvYZ&J#]QVbrfo0R*7wvR)৵讗g:0!).k]& "M[] %lz zjjo#d"<\2hv b vi9/aQŽif r5r73S)F˕s,i  ! AA%V b !OIfT-t-:Ae"%~vXs dBJ)jTUxB j{ S[]beѣ 4%iLXX.4[6 PٴH8.f%QUDYI)Bm;~W1#h;̫L&7(8ܮ mG t1_ix??͕-3fy3Qōdݛ 󄿙:js =\WŁYU.wKQ#pQ΅x9_;{ a7$T ޾ĖS>!l6 0fԍp=C ٬~CAjh^Z~>+҉(E:_99ཎDHkf V &YQFaňbL^`LYe "%E. ; bZ&.j={{m0eԳ;{Qd2!2a>ևTzGF>A>;sˆ`Nʴ {GT;nu趛[GUv|>ddi"RaҒw 3G:V;Gg)X9jGG;4bLL92`28&DGk 6\Ni& Zw;;a߿΅l̒e27 "(0Hæ%6fu\"H[H&\Y,PHՂ_}zOt|/zZW ׭ y5 {"1d¿tei{7>&͜*-a[DGh;UF~Bݿc3,RocV#)&Wg_ҡ| *s+1dc GZ(Y暊SdyD(ɞE O/iz,/dr/(FVZrm usz? XvG {.JU)*YpԚ_?s_dTxB)fh1!otMn?9z9`82Lt<ET:Dͻd=ضH;( $$7ǷXHmgZ+1Se PEP@!2 |pדٲAo'r aoI$eY)W9b?@-kl:ä%96f2H6E؅jxNGlZy3 5bgVuxteZy.%}@Ss4k͇!Ҷb De J.9BMzx^W2}HUumy]tEfU_A\N988"ׄH$Ea).Z‰GY`IV<PDgV_m&/3h4a*c$0i:CMEinx˪hF.ԂLUHD s82o hX.EI'Qq  kE;"KONqw{.ǸTU)cŕ,qMNkW|ߵ_D)Ÿ!$qcIL۽؄#Ca_eCTg]9 \zvX6f7 RżA0(Ğ܂tUHÂ-VYi}M?O : $S<X6%GDv%8EB)804XQ##gSkm\p8<_Qi:IKsCc  =HG>? ,6Uy4Ӕr65=sؠp9p3{ ו`Vwڱ`(*rXEqۙvߜ[p]3`2VlMb̓7 LFzR=j"VsU`#>3sVfWǪ' .) TjyQ{Չ%_'b,q۝|8ZckSh2MH ]zx8xgdpTKKkg71k#Z⠬Uapj}-ukz2Ⱥ%`:N;gZ/5DAml7+|"(*2!%VUO$|A ׬̞l[1B ] 9G'`D1CX!: +v-F8#04Z>bd%*] IDATa϶ `8d{.qFiPʚbeA޸ڟ k4ꖕdӭ,Ъ R,5ؒhAXToa.ox*H2lhp+7 x*:pZ*{XXp !`sb8E.k9q!hiA|~V`TG`tGN,cuO!0bUFZvZcHb%>u lF̢kDZҴP՗.8eLZjCybTM0J[Н=x3s,]RWPPȽ=MKhE@( MwD.<G .jš鱌JUI Řp.e2V8uNjvDX#n2bpVjw 6+ТF1,CAyc߬}H׹Xw"Laz/)Svۙww SHs̃6#eiZY(X'||RiH=Ր t 6]J6JVE4Zt uh$Gz%ZDaFZ907F j*CcR1.\:AGT>k2Dh~9~|ߍ>HŤɈPFQ]3s[xpU;$b]P ++hk9Oe2+eYdqOg|*sC<g-ciQ NQ cިB8rJۉӌAEjG!Yq0|g~WuF+8]fTTf@jwL3&#}wI2?Fw!#&k ]V]Q}*RتDC塣b#z&/,-+ж}|ͭg#XV~nb(TVJ>-"2Ꞗk#d8RsZwn]4n +X׭!(,nOd*%T2kyH&4;rͪp<_: #+O/"J]CcLF泊tڹoe3dq s)nGm$72 >\6Z^ z}geV Rj\2嚞3LCgZ̏B8$4 UƭR^ㇳ=x,d* ;Q ADnIE\2U Q@ mZ( FU;<z/ R>JH(äoX/?c=$?w"JKBSްFTV3gY>J]  h- ~!(U=YDC|Xw*rE5 oucjk6 X*Z XՈAᚘT[E#Bk|W:t#ݽṙ8#X34UMm}=]3_?|yb|I(B,1xNw:_9"Q6-ä́dJk-G4&t"U+,\T,Eeu(Z#7oU^!mpIJy.(Y/[0] beN湦13}e͞`(4Lg?|%#Hyݿ2mtUiPhfբ>`ʮ{I*VªMiaa+H5bC8Z[@J#MuB*+}p;#@t@ljwE)AcԦw?ܸ9|>o}2 t>H̞1ڳ@،˙}Ѷ.{GD FWBY]g,a2=Tw]^ʛe2t*PXMw· sgm+53 <7+>/ŘŃ?^^ukFQn@XŲ,:: PgЬlpeR{ӯ,\4+P)J]oOH;<./ۛQ u=He?OTD,%$瘪}L!JjtT6=ҜhDwz?%"K 4"X Ui<( @6 b #+--1e3C/?iO{|$Fk.l}z' C+B4fd([Hh0D520xf'{zdM޾XӸT9;g2wfR{\sWdБTЮNow4l.*oIpi򵶳?qieK+8!00y,*>VGLb㧈i18o~RdNQ3>!tᄏJeB2+Yn3B6i}UJ˘)WoQ퍤4A9Xf]\5 7b¹#\$^=%Yt8%,;"Q!󻓳޷fAgrOLG8r[Vgb( Ģ-¾!2P[3gB\2ͨjI!.3RFPdCԅZZ; U>LLed8Vw(~t}Xt O/xFVd43lܴEs;h<.]tTCbis<$n3.-eRD {b:kl9@S#G$xUjet,u1tQLuwOZ, n0 W=LG=w.]<}^!n}^DK#t.k,^ PnLg.d'TٞQNn by!?"DWu֣wҢ/,>N{Z8ްaP8 QʥA+wg|RH;B_EfkO qTo}vL,p`exLkWvSsi.HLt6-9W)AmDnt#+\*-⬬lcma%EW]^7 [mQTԬ;͚o PU70욙s!s8, 0P F1 vNGQlQ7Eޟ-uű}E D#]a3mO=k:GQ) l5I|,ʊwhMKLFƊ n[d&҂(íR/ S8%N1uVϾ$;q;g. ˪/>>Xs~K4feex+F䫝9;q @e{IVF,LP#IOhIFT7쯮McκYiv! [ӼnɂA%(䓏dÆWeP%S[|? GfRz%#B,]2嚞5YSXؒ[~RKlI>%-#A#o9ToخG?]HdgtQhIXCtt'֯\<ȲwpMKL&iYJ-;g YTjˣ ~vJos'(BƵ@fشIbʽj͢rgVh3t2Bڣp]/Gu7c|Z܉@""<e C,QPbQQjO,!mӢ"ABOnQFΙo߸wB,8LM\klAx뙻til/^IVVV%ԡ D2n9knUbQAh=&ڀopDiNoOnk+YV:E E(2a 18S:ړmS ObY ) ZD092_vffJn>DDe1%%$+"̎vY%ҝ}b>G8JjPآsna<2x|>.Fj#^ R{p/ rnkZJʾ 0qwi j{[j=hDeJc00R_}޵)^ik{BP&nW;ۻTQ8 `MSV O,x:B@He9kW5:7~9T/F(kߒq?ꘝ(5Y(ƉM{D^j ͅh\im1)FV,aMĖ77CsẒKنXe!ǝ횿Vp-/\ VQ^Bgtbk.g;WK HֵHy&B(#˞ɭo9cO=vgtKMd>Ep=T>+ :R>> f* xC@z:нns6~>$042HV"v8j1&LKҏ??(GmTwem<]+'+I[]U-5DNTKOՕԨګ$EtxE67w;oq`nn?ti;f[t=Gǚ?d2r.P%*kxi[bUC[xWLGo^1晊4Zp$?kbo=4Pəbt`N8.#g2z;Kl5 V7ml([bՁKU$u@cHSX\Z]^.Cퟙ}dQtwb]dވ;t 33|,u K3wo|JcaU V8P<XU6A^^|۱9zakG(¥%F1 =Hww\up/)uQ}mcT5,2i.WrMmϻ&TaSTa@QGKFB,:e9r ;oN!Y#ަza%8ŞځuJӍJnXξ+FTE[ˀꎻJoUƽTZUբw"4v]axf~tK3l28\QJeQ!$vw9^uŇ(;~H#""gd9S08{c &8048ʲ{@(q1d;.`jti 0+Y,Y1jD2v]t@gaU|z5"N4F+vbGqVqdKѾ־ljI XM"ṹC%}itre\* @uO^Q*+e|yDx[z}֢_sA9XawbW̝h\z#7 ^IDAT_M. V.iA0( ~d,l(={V}yԮQx2#և=Y#)֛&"2(*Kx'&ەi b(bGeӝ}yQ G:8+X+‘b|Er &5+vu'?uO1i)WШ(bT@UޜՌPQy_?mj0[[B{:Ekb9Ht- seb%ʞR$-5"vz io;)}ܤ?}휜ƪLjaPzuejFeϸ's=rd|!B oX{}#[&^5BX9)#Ab6y!=8s/P Bh+M^At=0<5 |wZbF%s C ]ōM (%Ez>؇)Rzaa\wl;:(Ba0e 7rnYb&8J3RiH>/H o94y=U:ߜtLLc%Xޜ}-`[>}e[Ċ+pֻ`.Y*b- {VPDj✕6oE_:`f)E%{uwbuՋj_W^3̄~M8L=.ҶFS3ܳ@5WY(C/n?w7( xuft;6~W<b8 3 pi9a(51̪ERfٴvs].X&2Z+Fh+ST j}K2VJ7}4۟RUԭ=}% wƧNd<JL_e‘LW󌬘eVLA Ė?}jvWV͜ P1>s{DJ kd(_E@&ٺEQlOlX#=bq&x32VUЬTda0 YճqafI&lxJ-7f.1᪹9ϱҋ{7(X,\i˪Xn"_*ZTU>c }EZCJ->^E EhQTO u_c{|MKHRUe'd:cM5HM<1{rfDl%RaZ5)B\\Z>ٺicL2MBh,@&C;-D1aYsCНb Ca|83 NKXV: k-؎mjGw790 @NcS˷c :|E,>)MGki "͢vآyzUԈ?;IQL_^HúK+'/9B ,>D{rz䅧6N"BaEe_ ?]M Ls-C#n5h\mhUmcޮzyӇ{FRDVp Z;" CXx} C|^s n>$x_BeJb{Җ}G0,`r\pe}3mnIsV9MCT)K.\r].gϝofٴr]u.ץru.:P]r]uEܮfm6IENDB`gammaray-2.11.3/docs/manual/images/gammaray-qt3d-geometry.png000066400000000000000000005234611413310443000240600ustar00rootroot00000000000000PNG  IHDRXL*ګ pHYs+ IDATxWp[wVeP K^׷{{]jcC =H!Fz7*Ն&Fv4ҁ-By_Y`D"JutJ$S@FFFFFFfw222222#^)`Q z7g*G> pW(‘T*- pX7#"qoDz(Q)'Vb9,/񢷯lZ4>>S'OB!PTñ!@]v*_yw7Mv5\9y {=@ ˹]nG"QNN t<gXEQQfYNy J$6(ɸ 9vBn?ܹ}aZ ,9ɓ'? yhx4:q|aqMծkoaΓ_ʭ?uFفs!####J/z)* XL0y~#FxAs?/.]矧(b10Ja+֖t:m4"EQ|Hb*1b1ST!IG1M/VUyYE1Phv̶$Iܽg1[JEK?0h2y;y*o??_xLo闽U(33uϟ$l[knx.EQ>;7۟>{aXיf)H/ilHp??xyo3x1 èB_~k j w]N@`iW 8Ll0$"J#(*DuE٬V v( WVokok #FE_mu6 (vryŜH$K,Z j_O.],"Kp.S(HEN L&"(JFCFIg2fF?w{E/EQCC-Ǫ^˩鞿axddeYNG(t*G z=@b(&755Ct6tNj޾'dY#c GY fG{k"+xijkRtPhꡐWedddE],}<_A()y@0R!\.Fl&c06_ i<}>3;MdVB!izphX*nΒ$n[}p?{;3;?lvop?|U8Í ;#7 ^/8ĹWVV`Ta0$$a CJE4^ȼF1NAgp1e'bǷ7W̎f xGcPl{rbCPx\(D@ "T'l.Wj-ϓ䶵QMBb8M3TYn{Fl6mWk22{PIi7d.haٽŞPॹHeh:]8v]=>08gDY.,{~nNFe{z`$#S ހ?H(nok}!ATvq72:Ft.j5p%It pCC}ooX\ T붽\N*< s ţ4k |+jJI%(ZfftlLP\ΞǗ/]JbWBsss]g:W xyٮbXOil~mAahm ABFh4k"[dY8U*I8EAB#EQudYv^rg9u5amBQAAEA(Urt˱S<X3("/%UaӣMjwnp åc,.B0AfmjS ^L red.>drph8rt^SyFؐ~Ǘx㛛ߝ<=>>984jQ4qKK/z`D"4MpoYwe '&c$s^]]-M CW HR$195"AEfki>VMϞsRyA_LFFꪪh,Jo߹QSC-T*'DQ٬8If29MX,o20 [+\SSNtt[~s|o!jkm W ZX 04v98AǧR'O W.O\|˲|lיad $!5Μ;{fFc WV P8`mijl b8 H[AEaȔm0v7g\.784uf}*9?m|˼RJBDXq\PBa DP00L&%I2H$< C,UjI48#ZPy9LTSiݶnw_}s+=hR*jeYaz?0ڕK{q1pރog≄tb6UUy*ΌM>{qLn|PMMu$I$&Ceˉax$QJO|xkKr}} JGP8G4LCCAf 0=3k0s`tl>4JXge-{ip8033;55HmM5q0`0PX̳sp$ѨEAL0P_7=3q|*ht,׮^bXT+;ۆG1=veXE?weYnWAkkm_XM$R3?/  jrj:s~ϓg}p$ 6-O"p_A0tazf6JB*f'GS4#PWWs_UUpe*J_aVUUh5`b6BzNVtZMRj‘H(av8xS(+TD2UvgrjI?xxxIsidjph m7~W5##s880ӹ8PEQ\  $ɵ*SðBaʱGIDQr4LqfR>kZ$1 TSS\ tZR'jk^1, eh48195q+gNbY pCC]$j2z#066AfR7()~a #EcqNfn!HX,aR@Q eV(ڬi; l$XLbƆMNN884 00 !0c,"(PT!25=+FFp2aI DDQ h1򶵶$L6 UZing2Yi zfvۛu BJL hffxG{%@e.u 3lt:WZzw#Hc$rܝ{>䣲S;%aז[Y[Bp:}d&Y޲2= %␠iZӞ:ŗ_1904TS] UW\N`xa#ǎugWA0 jTW淊ÇBr[T*h]0a]pA?Or KzQmmimFq ȗ_y޾>WM6>|$Ҹ-J>*LVA•&$QN X0q'WV6 R}(oKc222223p*R ݖA0 X%Il$5 (-=mn) #)W(-^Yh+Ia] @tqσm}!T~d*?[;mJAQ稬dWVѶwvl̼ځBIJ+bUTxb1+A#0BDP  "!P >"+sS'''m7tWKT^6KQԺq G3DP(l{heׇeYXyR.+6Ƀl(@uǕOrptc$Ad£]6ʞ\AQ稬dٛwQ坴Z[uCc l28JQh4#BJӌŲ $ L .e`duYrLRGAȥO",aXEo6t*"NQTV*JeSDth~Nr+R,XʓaZ])ZQPJ<0J%a.5VAxd1Ux$eݿK%x.ǕVfO؝b J%CQV[ 짇 ReD"i0#F|5ßT4-T"qaJ%M8^(pophk3 0OLN5mފIl6 $ eCC s~mMCR IDATJy{9ݫ9Zf.]6>9i0 }?6+EkW_eYzHϓgXð ,-YҒhv l/8ʔraҧN# eg5*X')lQ&E"(;ZfQr҆6n,8(Yz<ϗZJ0 + o%>,b޸>6>q9e|[_xa`hXźڡǣѨ3l"xhh0(JTtn=05zl6{AKy4S(R8;o=VlL&D EQR?AJf(H;kH4LBPWW?0sGDc1'&v;^b1wusyt^|?خp8j_%Q9tV*P_Vrm;em_|Σr0ǰ 70dVB~b9/^8ctbbn Ȩdۭ9hGQp ܸv5L;O>zIW/_e>XZZDc|C#^G4+uX1 kWuRO$vues-կ}mGW.}uA%1 J$ m.@]U(H m-;zKb#5$Rmgϟ>sui_FDjjJڨRzXlJf&'W?:Yh2m?Ê֨yBPEMe2R/ RYN:,[bSOWo0LV23[: QC`V:癒9+b 8XSOH$RT*K+B`.k5,*_auFFf['IcǨ̾lx I[Bhƭ26[FaǕ H8\.ߵ7`;_e+d+ ɭJ/,.z=BO>x0cb @ 22R+rduzdаښaYhu @Z6!: )JP0 AP>O)8˲ $Q(.˲Htn BAC鎻](җ?=hX[IqZ]gGE$a.e-.ehbksUhv`VkSТ(:O977ɤhr]X\}Ep0 C ^qBͰ,jUjtG{H399}m{ݯ$n߹K NP('jEQsNET*re%dPf&@V"Ѩa H vuzv^NFFFhʼCQFY'Tg@ѕA{$T*޻_W[;4 zj5=:aFAf# "j!0 wdyf~s !JE -)#~}]z JeqhY_|u]m^ݠirܲ6-5kJle'jl/D)o]}(Yޫ]ڍkWI^xaE.:I5ԉō~Q٦j6?xG[QfGnrUh][];TLNR :w_A$W-Z!e%(K7V()4V[([@;JrxjA8Bn;'*^ce]c E:.Fu׭fQu+eW^YMbG{qX+)N^)VJ.b`aA{m/]~l Ϟd^gp$r03;'w߽ TCh_#)$H`Y6HT$zu\(w?ioky9/(4]IZ<|G޽W O]xዯ~#mo:97X}]xK󱯿V?oݺ}6 4߽dgN| bj*p_ t:CQI-*<~Uijp;d2ǎ6MLNu/,8H$ /vnL'Ϟ[-\67_?_ΟUY-DZsh4f5U*R޾*')ncqi;w2" GD,,~QC!\_WiltLn4m0$D~\6`'y#O:PxZMRi͊hq 764𰭵EnOt;vED0 U*ndmK//$Y#IBRk5CI@We4~{P*m" q_WT*Qϝ{b9008yJ(DӴ^z.QOKl6 h>v؁|8G'&'P8nii>V,Vy$u9j_U2sTxj;Ҳ_A%N4R•UG#/e}c(ld2ośF zMrɵƊdHPđle_P"[|kkr0;v_SFqMPS+nt:֧dJu댴}.Ix˼5կlHdddR: r"EVNnbQYɭL]!Od,oj/?y#69@֑:bGeVBS)tJ(tx"e 3LNw8(22)FS`+`Z]l֪z(jDl*40$&QY=xЗ5M3 ['%fYvχEQp2G4. ;z!##s9 8Ml6R"+Os]zf{<|~WU6ٶ1M3 uC/^ I"ɪժBo~O,I {uz]C]~ cɳ纒g[H%&OSV*fFcgo)F&TܿJ ^1hOv={fXo D"ɰ â  W|#c^9Aպ =mVj.m#]ˁ̎"'G9(WIf lq ZE_& 0 Yd$L:h4j b^d4<52:Hړygފ}D'#^ S^fǎ5e3YF.?'8߸~mjzի(sgw? é'8K{7"&rx7:8^|,ZBQĿ0 ܖX#ym[Ff'MfVm6) 8&Vz]*:s$IitucN,EK-`vίiM/s~iuy2R,b++"B:!IA2,A(iAd9C9c&x^ 6x.QٍmC-n'[/P&VPT;Y)kbQ\m"*/(y3*wEBf<-TeYE2; P[kZzlzoq1{WQ]Ng"$J3 Z\N\ ~̲g/kÑh,;yfحw z.R2:@yeZP(dZQ eYV+uaG;OUy&Ӝp8ABjjlG[p7>9y}w p~ӑFGtz~a㸏?gDNlKK'OC!j.=T+ů"AP0˩eGe%oوҽ8rˇ@ r|!| J/.-WUyo\vk^z?Yjk\=0Щ'J UoqT|NYZAQYɲJVXR>_11Ǎg2r> R3bMAHx:;TW!'^sOz17.~h2ɤ`*"pGOզFFx⌜p( wPP( uuJe6NL8&|]]Dɓκi1F@jݣ@s$Iɋ{?_;W gOW.JuWU9>FQ1ϻm8neW}*4hf94metv:ݒ IDATg?_m(VYZ6bYSƾ>Œ\.0sE()8k4 <Z4 k|>Ai2K(Zze\.!$Ű\NIҴ$$*ǧV#WdᯯzA^O:dz~yYro^)ࡡaZH(qÑpG[òTsGgf46 )gώMMcu0,MU 01᭭-"< XXrVk8EL(m.B֋ 1&ffj%a)pZ eE|ܜ$Ic'Vo%\fsdJkW.}N"xܩdj1G"R  >;q`J00vF@,E`P Ekjz''  `q'O HJP(b1Ogf|$w}o2 %AP6:Y,@m0O&F*w(k!pͲ~.*Hr3_]hbϞ23 !ˑ.2M+idٳ`ؐ)T*FLr:p9իʲힷJ ݌8*JJKT. AT_r"&8 ڻNBdSSNj2KZ A=e( ao䫪b&HnO*Y,--eG^Z^6M+V9XM$ Vڦ^M-fcvbdazŪT(榙BCrzdٽ[:c"ϱ[QUuKƇ6 rjSH*=Ҽ4?+uOа֗jT==.W\cX!\sw^:/?٧O]Kčߤu55 JqJ.@c0o )KӚ|YDQ/9Ѳ^6qJѠL^e"'<DAϫD(5mF_YXp<9hl=<ϣ yb 'Ή_'Nl38NkiZ ϣ,b|mr?iwu:ݜs~9!gPEJdٞlcj;zf=*%r)Q$AD~xx9ߜsݷqa )SP5Nwo_8s$I'(67DͰ\cX  (u,ff* e/_>l PlƱ$Ea.W嗿7U!.W^H$ETf檷C**vzoIT*U izj뾁Lf9]Ǐ VK. !"H 5WU5b:)Ir19>{`O^|yo(X0L~Wz7_ufW{&7?h ~T(  4mtO$\[3ߟtrs^'<[ \_8~7%r[]/S-jꦪvn.YSUӠgBvP 6R3UY]W!IH- ƚo3Nײ JQT@lshv eI S(q@ BellgssjssbvvnPXͦ88ZBZnO?⏵M?O_?Cۯ OWoX|Q_mʧ$t7=;Z*2(WA'YύjMm9bA)cSNZLJ2GX4u7*dbdi+?;WuYK<(#L. ;%LO饇8.H% /<gm֔e$ >*!w}6n>ڌ1{x8 \<߾s'x~~ָqޟoZz/V@W3%Cؙp̓?}kLֶs/&k+3Y'vu HCfs?|Ƽ0UO>,l`ee7o}o2XJ$>*_np9ޚXvyo~۹\ͦ+ñG B̜i$} 8h¼{O#/CrǏY+i*RC(酅%v%g|r}=i=aYGP7qآ7=2r~wYi3_6MHٴV(Sh, 󃏯.b;b3I.ʗٮ<=!o} YG\wz*kHUQ1N2:b)U,O&ݤ*+EjTE#U 6IU%x " ߢ;7]Vk&pm6sZS-nwF5H5RAv6KQkm?BK O?ۍ)$ ;z{WYEw:Ţ9xG祗>w5Q0l3D5ffV/&s9<[>rR+8Jʹ񃌙2PEΡs2ΩR*f ɮUXqJ[7ڞ}wnh2u'I<2.|1y@?<| T4C#+*|x 6 {!Q%wP*PdY]YٛM:7epRq:-Zխ Ⱗ3y k$0KGt㕋-jpH sB^!0TG…W^M+Îdf#|@}͔b>7*U٦FOJ. \ÈUE츏T-ejk(WF-IU&IC4S jFjwגyBX _U2t ͦyh(J iYrQU{J={2=l~࢙N۾ȲnR~@'8zG8PZ-<2}2l/'.x6^[tij>pDr˖AVaYWWƽ9 ;:5j-{^=sU9{`.CYͨÑRs%D)2ж^h}_9VJ-Jy_U(+?ÿ;/86Hdf3 >H~2֝_lqoH( h2CiX((~3:]'JŹ4B)W *|3WPv)o;/LLZo]͍3ÿ}Evحd|*q~Q> y) d)3FKg8`fTM s j^+4j~uuAk< !c'\/M([v{ሟ?7FO80S`6n[C, Y^l6UvMww:4^wץ8Mw:Mdq!N=? Jo|x0jXR>}3|)OJB2v %Aa yYP-`V! 2o'v=~9`գUoRTC9Y4KN>W$/|5;Z=iڂ:h2Urٷ6ݧLפ.e>| ڴ]zY^oL'OjR! 59^0.^P7ſij(ƍb|ib5## &S{h(,,= M;ϟljM w!ޡ(J佳3K˃.i}gyZiv0L$}ssKn;}<'uQrX*8~tqqX*{=0^\$ z|o\_q|qi&a']^Ygjrb7xzJZ7%n0cc !~?8($TbjAQno?؋~/>P(Mh.F[xf1C:xK ^" 1ʵA+oLπlegصF)Hz7_0nPc^|7<Im:ɵ A<_G4EElƬc(<թT"c wփZTN+ K DBUj(VנW1Fꒊ= _ ?E\ە( j#rե1IRIJ% $9߿̄i@:JE==~q\='N\~P>1OQ7݊ }{ {5yPN#_3Z${el I2Dr|~]tn;m kibH2u6Q dTelHHKb,/*C#'iũ jړ~s*@|3\ƙDl0'O^P@}9|qRT b62 ,ml( VލFKF~j4>˯VL6q BTU Jy- A`0%"\^]N'ۓdZFC" xI/tph4g4# t7!я"~X{#~Z"EZ0YPVS= [׮9@$ڑv|coXYyU7/ESh4Xdmf,żɁQ(O~#d5!YM$&v 9ê:Tae8[4ۡcPHʽi5K8!n( vmS$x] z1 Q1Ԥ8Tmς"1Y5 eA.Ǔa8媷q^c7 n/Ubz-D:q\p98Xn4 3 @B?JEJ+6#^G$Ɏlt:<:S_=uXl\y)L[o}>*HuIj]9Wz=4:g-yo)즐I?y\pBHw뫯Ӂ4QCuzR $J [dX(+ΈH6dMZPHbvȈ ,#: B:٩O [{o /5u:k/x>oWg ' IDATժ=[,wxgiY33z[8 GcY?R&3Ҳfu$G"n~l#wÈʿ+|åCu֛?/¡`$aY%I>qcuGw@$|Q#:._( }G=pT [;s{≤AvA鴇;~?B}*"IRyZW1Lq:W^t\ss?*=?x7{q2E */|LoaMWkA tVLٹfTBh uIbZ0bn AdPAw9²ҍ0 :mS=(A@aӀC* * )F-١1ybBmvTRxv:!e't[oM>])B+l3{p^G3 Eܗ6tk{Ê?~8ǒi+c63!( ̬oÑf %(&gϭ ̬-.XfgZ-=0]-c{4LGsc|KgگeaysϠw)W^"p`~!PUk%/|A˧\7PtF(K񌂚P{ +r7c'μoh"E=\/wd2ZpqPn <_dŸȕ?xwP0xY:9rS'my_TK36Lߚ<{VfG^7:2 Q^f󭌛NvnWf?N0oX;ss/ y;օ ٳ'h4pؙ9u%~͟|鯯ۈ% :o$ |@rNl+'+abE(0>z2qhM, &v&-!kB@TU`T}ގϛB$F RP1%HPQA& hUM$^5@! e-CJQ4?xJweU]aqAEB&'\U :QzYGkt\h>J>U*6äFG˂0d0 })F#-YqDs$H !uYj4 ?ܵ{( TZXD,/ c7oB _xgl-:?!#$ŗWF<qQ_4ؿ  fy,d/0zllgBm2Mmܷ?v;|h9̱]!DM#^MZd$jO[Ř\JTCLaUjdr.'zo\z龯m/afċ+W=7B<|: PTa/$5U_}$x|ȟ3 s>? LdM96Fh0ز 8P,QUDQ$ BG>91-@ǜ#{67?׸L[GiVCNrE>6@Գ'GFܖV֕H@3 A@Jfcx2Y5Z\B/۸+LAvmiiAquZ'k@VxeqH/AP!H/0tto$ىm*<ËedtAF}67jD6z Ujw?.X֢BŶ1[ħ]Y^z%tL?Q(`(m4Z-unoGG$I2 39ẙ>SPaٻ,?BGGws)66Z CdLUz38l$T js)Z}L𼝋₠ SC'043&( !+M2ա (htPGL7^vvvnI¦@ +Te]oMzxa [?{!Z? !_)d`][67П?^}wK m y"SXjIf'1 PVN]+p,qG^x[zuϡ:Uvgo(fNZ,(샳x;R&`z7-FhCFkP`0|snQPrNvANw C7μ>}߅?ؘʯ!E!~UqUVrJcskAh4)|^WUnvvSV{^ZTUUYiܾlLKARLRXƴp ALK$$¡MM&=!⼀L/.nՖ tuJ; "EKܰ q^Z'x!()8E}nUӚd *Z] xKD..dtlAv؄tͪYG;}!)ip^y^Z$ B A9ѥl!r+4 8ªՔAL$07 QŊwPKHi̹nW\ kp ._^ںU[NeyV>%ܶ$nd* W]0[2h>{6i ̉S:6p)H4^nsb~kkO<&`W1sMmsBXz*O{5u1G)thg rk#b<1JG `e*`4_&P PeT6UTo!x SjMFN-!>mQuI}==\t{[A'Q[[[aYsȇ ʹ _OyupBFUQ/#6 ˯+InYx|vvV땫\N'j5kMw^ϳ?~!Oڋ޺İL','U;BT۝nTnJ6666:zxh+rTv9W͓$i0|\},˽8,!D"QVhZ]7z~qNq$IwU;7au/Jsfc1˕Y ҙLx+i8F#C t}-0p`\2 \Z6X&41UUvL0ٶBa4zd "M:Ŷv{R,!Bv,ݽh!-N0,& DUCa\A [DӸf0jDQaTA>7ѡIcqMFUCz"Bk@HQkLؠUk YvzNwV6*=?0aezdtt7V{to-o0N_Yr5=4XyBqH$H Ak'}t|S'Az:#䝷g>--9h4A4\ԴN4r3Fcq0C{L^&Z_HgokBblWf/6_{|6kZdYٛ,M !tç6-NvTq?Rs̯83'Uݰ^Zs͓fMB2jGt}.hЙuR@tƆ+4;zMe֙vjGml=;ClnN̗L293_#Iyd,(2]w^6޹LOv}llz!(Ja-_(#%I;cGɅE1::333*|CFcǎt,ݳR6-(c?w;˯tw߾/_XZYkl\>tBqLPVkss4y=;;\aV]T۝v,0pBe]pR 'v:'yflwpbt,jT(j4Z/-ػ`ᡅgl 0'5ՖWÕMw[%ǁE_1Rf8lȢǛm BqH-%Q8lͬX͊͢Ẍ́w1A,٭q p-pXˠ I} :bBsrHQdlwds rov{!ن;7J6ۃ-z66I 鐪,Gu^HQ1IBB QnsM6[HNOva%E]B *#xeuJ4Tx&gF#y*Um:l&0[o&Ijv6 DV|x(*6cS,wSj4Nt8^(iX]_^_J}g\K!Koc5 drlp%WwBgd]u۫)5gGv0rnl\•K01Tz64aja7~!aueVvfNمn̺MaT_(,Kކ=>xD[!I"^HI+-gN0IZFr>8*!/jhyTtӮ&b{p -fS.ol]]}`rh3~u5x59U*o ~=n9UQpH0o Rٔ8 bW* QXٍ2 FCܿ/HZf(zmCrP*Ix6Za,˩tfzRxE@ AH^|!B8!9-P g9!۝X[6v:9JqUC=(BKV3$.X% (bIl1pSS=kŐ]Fkeh)*Nw6v+::]YPŒg`l&^M%\suihqZS?sdY3X$sZ,VXvTUaACQҝ5dc'܁(A? a(~̖~w7~{> VUQt;hdJk0^޳s<fo{(:l^tqSEP*=YZ+f̃ov;Fװs >%AR Og? l,XݪR wصc7z 9%f fS7}WV#j PS >XL2j у=-PP$,|t7"7 ֛çXXi2cPQs%UkJC1k3KO8-E{$kJ0<z=/M_&a#"Nh6/_G] n\!IR髃I^4ߺRoMPm?QWl؁s3oEro}s Id8tJelrr!5?#g=s72i *xYd Et_K;T~a#[7xїqwMU{ss!j"ItHMQp~iHzbh|3o}ȗjQd2VUԁ1kϡfA-0_(ۼx/SbC߸v.Iq076;qL60N4Tv[wy3z4wv&K P#&M:toǦ={DHǡtVuJ3F5b!/q!zIJL[.Ii=;! ANŘ100Iَ'Qu(-ۓc!yN88 Mvb?]UuF7Blʜ8_NyiqUN!^w{) $Sy$BbO8 b.gq0QedeTr]HpDXhVu`B<Buj+6C)U/7} GVp F"L ßT5s f޸E$Jd{{-I0f,X}ʑqj$yj6 fx{gm3j{T|ѝIj9v+K_-%eԥƼ1~kū4@ BIcX+b}l<ѝ%ʝɽa?J(o*R ҅:Ŏq5=S˕o&E[v@xl@@vƳ mX cP$_+њ \tA>A5q@_,Hǧ^ vD2$ ﮉC1-A`OIrU0 [(Lr\D@z{@J`b;pM6!L$YSS;E1e;X (qbcbS( Y2ql!o0:hLYƾpT:fCUFx 9O8LEӌLT<:$=ɤ󭛷.b޹?PWr퍺-1_9vcMP'o(w޽I5İ:\u1 _ϺrvR1.ٶ.GNDtFиׇ =W4 CKŽ#lWAkV vJ0_;{}Q 0ߕ`#<] x.vMVΛ$\d~Aۿ˓\vyY:IO;?th}{w^w֭['ߧi88~3M|JO `Ah~g\[t>\םW0ƒ$=}Ǒ$ITF4KKd%-+}{6'*\ qV-6Vd<[8j?d{<Ñn1'Yn4[l%$GD)Ê98Z3!ҖTJPO׽  jT=E+rq␄\@XB1L4=<&A#Q` B \F7)tn@A0NW!!10x<(e]3!ꩀÐ V78p"C`*%F7@oʷ:zm12=@6"d" $)q yKׁs8jRB!AKAV( Fqz\vJ>qKqEBwU5[''yQK&?_Apry(<.p}GC{v-dxR"p@r+frdG;x^sZ^yWs{o+R'`Ar3p$4T5Bޛ)gNȟPp|/ 1N? [Z\62FaܻORY!҃= ]pQ?I >2GXykxXt{Z8t[}ɵRť2/Jvxp;|@vk/_( w77nce,3 iO?~g[?=Y =}8mfUQggn֝C~[Yn{FY8sv 4_<+ iJ:$01(kFDQзY6L!1l@V4-'e:s eZQ!KJi7WB iN4_45cj(כa~sq9!J˳҂T"A>T4=ϲN[.2I11I& 8?E>aYF(NUgvfmZOs ?%1"F1;]`ȯ Oxi)&Q !o;|Z30wߋUZvğ"P DJufbv,^TiS)3h&gWW;!?;{MMݪOkFB&':BB^xvJ`p_@~}yi?ͯ<jge$vT;8ѕUniQA谡u-L3Fѵ؅ZUE_ #w{0[kadr+/=f5&h[>[UOp>ͿEr>?Onr68FB /V㏐&7 Be9:>I)OOjϕR㝷C_GʫKE: Ν]{y|ҩxg|Y><5%}, 8VS\P BOҮSnB0LN\ƍ{}x>q3RkuDw>LM8#jĶE`KЂ cvW Y[l?H~ 6EQ)y0VzqԚƘXZ:N^Q |PAݸ< 3.?nIJx<~k,IEQ>\Nn?=<7w(w&}|[w+ 2V7OVwp.ۺ;|s`^lSp/{U&iE&$9U/تyeAPc@r=VAI5r*qI @I5!C tڱgjxX6b!LK=8`zEI =T!a12 e;11 XĭW0M 0Ht+Z7)e_k+Tm( J']+ZZ"0q_ɤf 8JxG;ϏBƙ3nhF `qA/n|g$IًgapdCݟ+ƲeJD{ p-ݎxj[7/#ڣqD? !PkUnzM\)&iۇ"s{9n,K}i8xNqyw3Z=t|SݏX ߐj6!ִƲ?8a , ?Ap⼔[ttT;BhyݶO_+8U.OJ^_rz޶mQ>2x$C?,g|rr`I:2 NLFsp}Ǒl0y_RlGK0 * ̤@>- *EuodrgnL&UuR99o$Hc:v4 Cf8Faɓ)EƮ7}@^BkcdzTdOaY:yqƒ _.ѬA# qfgӸhʶz֍P0^^R%t\Z&f{?(JX% "V7ˎEAlz1D _EyD)qzXX!6,Z",zi #{e8ft $ZڧMa $KyzE !(R>; di&(B$}2MVmsmeR,k裗._>jvժ>J M&ͦo͟-,dxۓko_:m :Z<3?)=\:B[ݺfs0FvptKovu<{f{XyeX2z,{olh7۔3B*NFWZ1 "C*t적zMQe1tv#)INa$ gvȐl AcCaNa#wN:eB0r jiQê7!_+|B$M47VK({W*5@ɰlǞ(2ݮ @y>oL3 0#(abA>K[6UnB P3 !,zK@Acf(Gq 46-w5uA\buQ@yP!B Lj:Ooh(C<8=?i, ~7Gi̥^?\viV0 nm rZ̘ x0!!UODTsk«=ovuݣn cH0߸u⿾~NA!G0_8QZP٤cٰktl:fw[.hXՑ^ QlSl 0 lPjz[V2/Sp,@(8vO?\T DI/F~a|[7C۞/R޽{?vfy8!8OLz8?O}MI{gG_qJ >=T$640 ?mj@=4O '9<,`lJ>?ebΧ|s|?Ih<8 pp̌ZLyjz#/T(A%1 f0Ñ!BIJf'Ƒ "WmiH8% Nc\: IX#9)gUKp$OFӧ@"cdaq $zb()Y$n@"G>(3i(~>ǩHan2:OB#IT-aI;V!<gYÄrM!Df4"E΄Bdy.D<  bϟ6=; *EZ (Flzcw@cBIIvk IZcHQfμl.4u궦)~ioK/|ke ylNy$,]gfN|{.^hoxɭ[/VWTq)w7|wo8Q围ļnK>G넥` 3VԱB6Kah5^ qmvo3:9ƝYNkX3gw~ï5ʧB"Yr'7Ϟxҡpo rln?r(@HEQ<&YdFjXF dǃKKқ@da& d/wDFx-~7E$iVA{?x}Y!Qg~sՃ_bi}S4N?x:ο,l$ ~nw:"]7:ݮ( <. 'g䗯t?ݫ/_Og/7 b~{ڪ n"&7E~Q{{WۙLqW_IɜyڞmLvCwIQ/мgN;f(\]i)Bc@d<&;X=ax.h0!qhG~ o=0BKmZ95;H2HN3mǝњ0# Sr1帬1F;GF0[P6 C6LYgTZS\UMzY:bTx\x9_~pkEg*Z0[b&bJ_}Qs$wW2kœ^:)__p!hkH=_UJG +w2 RN륌VXN$d[QWr6nu JQ\XjvG'1ILR>=nVVkWrBŖ1mj#nZ6oQ rh| e+>NW߹gT-渡m3-i:8k+t(JS,<~ֆ?~ 9@\Yt0vѻ{?QwOZ5":kYEmiK$=fZ(Z@"t$e#Ϥw~T>dә% æ[77CY6LALHs<U3 riM e(ҥPGe"CLh(%Q 0]&S &<ddBQ0`@PrQ±e7uA K[3 ~Y Ztr\/ LۜHhLןC\M x U8U+Xdt^ )qiKyƴoyۇ,!sxt@DIt9@[T.H֑L:FeܾI@4g7@a{o[ݿכgfvz=0}]q(߽I('xa|?|9u9u^ߤn_NbԀ?q QR}6,+k|Zˍ {WnEK&DŽރ$ň"Q3Z(iłQr%ʙN?<(]{+{CK4 LLz Dz~~Gl8W 1%qx/ZB|oCBeZ;`DvyQp9[oJ!pJO,oHO'~btlԨ$nWYǩs"axɌ ϝ6ȿ~0to|ߚg!yq9#Lβl>E930lsFӅ|lV K Y8LF+zV|ߺ~:C7 3f.GU00 AV 2iӇX}A&= @$|1bqdf a,tԥT8ʞDXSkWӬiϲű˲ @Q̪n0 ah4z4] ^xir ΩÜƇ JFȅuqQc" L]x1%]wimTe-yr7ɿ\]P-o %l;ˑ(bWW~;Kw:ǚ-z?('rF6R?|߯};T'/2'̙*eX$RHY .&=yxP)j\P,@ah<:Tۡ)IGĊshņOsd*4JK'$Co$G?ㅅ[X,߿ 8[( T*VVVr8n>QptJ1@Fڏf!DžW*6K 㕵2XDcIchBc.$|2 #KS|d҄3@˳q,܍{ӲeםŴ$^ B}kZm!} _RN !|ʇx t@`i[zzo1Uyڇ4;+$HRQ\R i=Di1ht$.y9I9c3\D JJƣptO+Xuѩ8TsPrSe9$Re.?&?PBq,O|!APȢ QH8eZbQiVѓ}"Q{C{BڴMlN?q }cZf:+XMGa <-ȥ0JcA6<'0avka8,Qe0Eʋj8y6K)N3 ;(NKCKyN0LI#DI]7邀i=+Ҟ2 H֎Th+SlW˿>v\_fsp*eFZmg>wn\oy'? K#,)H2idف$JUۉ11Aۛ]!MDwڳ.yP%+w=Od|YS)/e c7zL~'~xf(UšennzC~{:&!D?0GNnŪ*nNa<S!(*䤉-O)&}Ѩ$Bq h؈(z eQN +E7'v|ZTݬ?$/qm'K%4l@J7f6-1S;fuןxI1X#?9ڴgh)x=,#Q$~0m/c~0^pxT0gp%2EFu{N"8Bȴ gR^AGI+XP, C m1efR3M֥6uu0hE: SEmK{?Z?/]zpԦ,z)ijr|Z(#ץN#Kl$ -b-' 1s-'{ BDsdyARo.5/ }iX*F 뇝[h$'hd_mkv<{V<3pSIqkkssGQyfz=6^n0Ad &ie pgq(!Pd38c6)nT{QJblǭQSA8-k Mc?Ƣ!llYgcB BP1E&~\+P5 )|dɝM_ҽB2so|Cu\H]^|cM3ޞEp~bq[۷/t<;{K>}9bqʪ^*~KIy%.N8W ˴ҹ Fs+fk1.n;Zy|:LXB^ ekvƞiUہ4(9rl !|ƪqn\5 Ij!z|?Րe# 30[i4`59/^h`("3UIy~DǎmGX 9ZYsP he' ˉr}$h}`ϷxSr{3`aJKkW^;>߷5͹O\˲Xփ$ ^g~}f`0;~MѬAB!I3GGs7o ~rw4R^rIu & HWRe @h FH$;;Sc7Kѡa$ A0itz*yf%wN Fn2JW,D@_XX}$_U.-rn(-۪WQMӧ d[ӈfGQs-C9M0%q1(r<^fUB5B7I+^d;m:5^ {$$6+o (ڰ}͹q$ [[tׯʆ^s^;7y/?ßʵډ$Yr0MODX* x޵,4˻oXi/EЃ M)W~K[>r:`lG2`(%dg|>=- `[$ }5$7bi.;~6V;{g,;VعjW}:wO!90TDYWm 0l7[| 6l$J-Qa;>ɱN9ﵖ9H¥D P{Z}ǵS}sxdo3AppVVdTwh vn<>s=*R;[vPRrQ9"AꜦExAq'lq^HfI{GK\oH"s#HZ3/[2c&@m~q}'Db$mϋ\WC-' ~q9;(S􎣟("Ѭa&L&333e*iJW<ݭ4[o@LȩʉBs۳ǻFI WYxMU\%.4Cc dlo8j 7ZHӼ7iId q^ &B[)mЧkz&2db粂HsPtdDլJw=#[I.!>U0 sq'> ]!y^,6AR!V8 QpfK"~b-v_IR g[[7/\ʍ}9ե5m-LjγsS^Y+8s~;ؼΥ5mk(?؛$IaGyö7w!8w~v|+7dU?7:OQ+3ym%KpuwYcp$Q^b=kx(tg,ԊY:\h7\8{ɯEZԻ✪*4t4810w\jm]T9xx{.M(Kq{s2Ct0^vkp'>9_c,Ŵ68I@ רۘ+Z1аJ 3goQߝ$* YpJ7p+l̰VSp IDATcG\4wުTv_~X~am ' ~o;!dqIXr[*YVj{_~'SIo 9FW75G4n}u^/Jr&2t1 Xjb`n3Y"En{Bqx!O|.%9atJ1qiH`qq.5C1BώOL3Y=OE?_Vc?|M_ukD"}{FAYӜd+'A4>Ύ҃Qأ&EF7G?zA!is "TMpQ8@a&|˷ Li>[|.,ˁstx UqT KxZpB=<Ԝ}|w]DwJ:T`!E_l@ |VPBE&ܕݜ~^{TSY{΍ǿK/xN +[ƾ&dƾJ"i§\pԆ&D5)qb=6< #<@1J^tn-_%9 ũ`35$v\z/,|j&w^F8+O  H1EG} p^i$Znж`+QwjR R쇦q$/ q:Y@4 뺮CM `m}\*olk,jxA g2q8<5F͓p K{gG#%vmr4`Gc/6Cwui0L[(z7t5*`d˱5GS*n,ih;y|&pr~i9ZӃ`|J5EÀTV1S%8AG-d&>JRE !IY/W"sN45}b wK%N$En`9d%<~[/'~?. c߿;Ncc| GMyFywvU9<)7xp.{H)ѩf"|z0CI8P) $PeTr)X>緊cu C1&f Սw.7{_-So|ʵ R~O3"cc<=<}gIVBKzNk ׽/$P1 E Yyҳ!sRKv$|[uD'^0wy#5X7 BkCe4JK'( \ ФB  Zߦ8 f9b 5.$kyqIg^Zr?MтKS2$g~jgwBxշ~w4=X߀]yF2' D"󧉄QoV*өjΓjΡbKv1˥^;LS+ 1l98 -ŴRj2&ZjMmw3>b176H!vԨVڜ=:!pǷހ CpCN@!XaLE[fX 1YC,;h$G_zf`78QL$rc92"E8(d @ac0" )lPZO {x$YO$h&9 p'8fRdgQ2y 4KcIo|\\nDzxN4tnqRD &*-VQz\SKE&!{. ~.uq6͙6YAiFG2 kt&4vSqI}wlB|K|Ѩ(O#dsv+'_x{{9_9Qg[k\]]zoGE ~*H#!T?"Z386s/karƠN^֬I)jAkŚKuRWrVYE"Gv%QFv8Unܡ$:~Ȣs[ǨP|!Rmݍi2U %CxQQ콍9gXز!6Pu7 P$|&ER)be@A!ܝ B ^!JH1`e~V XJcیҕDs d WsI't/|qh~˿}s|"u:z7=y~eeiofzG/(~Nlvc`АekO%4wZFf ڝg٘KeRŢ@q>bgi"_Uǹfm'm2ƧƖ{y03IէgVMGdْL5@"Nj9[+ a3b뜦{3-l9;&s: <MAe|(0WŮ\1ZE!e8J7LDUW(E(a-xq5Q7,x:5Q7MybLlA6{кoLB'ᗓIPPKᕋ`^'u!0(b5!&;nX+X 0%.-ǣt\*D{׊b ]"_,fxvmol}):~};wttB7~{cݻ~F(~_eW~Bl^~hJt=w.l{+V7s BdK8vr .II! `VfzO/y`.#kȷz8Ԥ)9h7D5r=SQŬ3|eԬ 8mST3T,/(Q%<̴dd u]SjA9Cxouun}zZ̋?MU2DŚ;Gx;}>C0++@G1ʨ˳$tqA%sZ@(r>mV2VXi:AX F'=X#L)噚8`O?ēիx{Rۤ^ĚW՝WyOuVj :!P, U^6crrߺwmɲHx˜힛/wK(sv"S<'>۶MtM@ GzGP(TU2N6Ža핷޺pb#Eg;@a-̷ik8P15:asa/;eOQ BPa,η?ìli\6^;Ӫ1`QtZ}PubT*|\Os1@JTٴ黧 צƚ?BU¹գ~c cy~Pp9/Z߸ZU CgnFP #=:{(VSͻh(Ө&Oڂa_*Lhx  3eD̀?JlשEY&NVJ tnT)}@Ü:hB"Dk7japm&I%唾yr:[jowdx4[(s!` į͸{z񤾓Y[My6T1 /R[MT{V6^>{s4~^zR䤸aVkͨk؋hNW_jRV?^Z0 vz8 LS USJ)SPRH,`$KqUAE aȭfZk.EQ.oܨ`›5jB8bvsbF sɺs^ ?u˵6~qe.A;;A+>s>{&]It ێy9<փ` O?Qi&$£숳]~05iR89#oؽm԰aDpoe>)EW?9؆ Wn崝ݡ5T 32]J)+Hv.7VUx||EKP~sO?$,1͂ |hC$#F>?z꩷_]vGqG@P)`qb1&T"MUR=hL"4.hwq; U#4rjqT} Ay~TkQ* iQ"ٱꑇ$qFξ?>HǦx4"⟼P,aiP3k#a (er#-i a/^o;Q0#0@%"PJ%gwd][0d2($0Ibsh\ͮęv+\dFjR0L! 8t(W-AJ$Iu@L3A?  5XPzHM)i:S-8a RYK;wF*Xd`D !٨kG^^uIфfyF !Ud$$ 񌎈7uxr= ,:;6/[^y__Lt{gVkocpkn7MO?p4sBp77mA6],nkF67fףJr#ؿ,ͳ%4 JHd{UUrf*y ՜fTecR !#<4BvwٮiLۙԗpc.u[PJSf}n.Y9 SX$qacW^W\s{~^TcȵOO" IDATzc b&4v7Vg7]Rn7[1}C{BAƃ1 =#qaǣѰ1wᄜB ` ` aT$;(*h6(Z;}C4M&f`{K#A"YgEo${P3jՊ3IaL\t: Rς\jed5YC ΎG84d63#?<P0Bi}0|6mc\7w!x>Jv=丸3kea,Stl.r =w2ʘwO)1B X.iSݲKh/MAS[ޝ=SM]./]1]{x{lQN__{:To>9U;|=ܸ[D+::o-{#`c\mKVx)7=u7U>|Bh %:qO3Ӄ*re}ljהl*T>U+xת=Ay[[Ez7??O*d_/tC䏬'>= 6v7^g ;`~EK}I DZ (@J}(tQB3)lm>wlOEwW9'f?zCǥ1!sZW@ꘔ?ܿAX>[u_:@H&m\j:^<|D%gq,W5h!m%8D.$VNtDӒFW);.s]VLiy~X-s(i A\(((PwkG+9x8H긚O:oco5W_~ro B~i/2NBk[e:Q.C?kWtkޟ{%{iAPAF.C4߰:lq勝ZA\J +8H+#Z4)-?!wndo p]Aplғo?]Eҝoӿ 0ڗty-SV{>/X()$W$S820GRu4v.:@Ħ]AjkH4ai 5쪐ĜiǍ*Svn fY-٧ d ELkI>+0IȲ FqGd{ CL2 #͆>ýh;׾~Tɶ)%ݖ#~S)JO,bT Q'Y5M''ݩvaz`62kI1f ]X>@!zGl"KCRU+(|L$ӓ}~:.w֋s sBΞ [pR{se"K;܉M$Q<ݸ2nDU;e86"q|;Հa8& z T߸6_#CI`9q)NhAD0⍯THۘQtӟmN:.c(jbېWgn g\:}~QI J(v'.&* p8Ex!S2yB KqIKÑ4SsZuHF^(]*$tB8Ra@)$#bd,J$J>T9 =@xhXEaqyeѰˆ8:"|F0maLL!Q!'95>s6x7EٕP3sr=4ptԶo__.(!>L/ͼ!fko SaRzx঄Q~hdm=71iwŃ <#B/խ"~Z=I]9h3`DM$xf^~\)?|=x~N  z@w,-r/X?|fFổ'o^T/-}{黏7n<w~rKӕ]S&#޾k 0– 80A!$ql†p h0Ƶ eP4UVZ#4BPL7N"}!jR:4mh] j ˡiZH"0ՒK)M^xov^?"/wK{k;c8]ﴷ:qs_v:=7}A(Q 8$6FlǼ {(I>GE1GGJ52e2ӷΟ?}k׶VWZ-85Z.$jvKk}͜ځK =Q:B+yڸIN"2lH)a|1}ru;.2u+yfq1NwC(Gώ|J nt B+ sT,=fX.IR*U#R=5Wʄ@*@3IZi_ܻ^+Ec8ۨ<Ҙml]nJou: RN9&q!мT3_)I#7&wQ&l[kɝ}b3-W/g=H)4e1q A!̦iWX9Ө9a_[.ʃ`vD <RD$!J&%aAehz/r$_*Ì<ʵ<5mLJ$cE"".!paHD >2(a_}oQW_οnxCF5=.6щ{z F}Rϭ"7f .GRV7: `a3fE3̮^V:\XuMIr},IڷDAq s筷{묔ᑨ)vOfgYÁ?= 9 (TYc 2*R$)/=s,C@q*Dl  ]SCbY4n\Zi^1%`D2|uU鞞f%R$/$B]>}Xv%\RDRhf8C]ަۈ؇l I]2|B T֩e8.}= !B'gbR'3cmϕ½MJxڼR8ڿ0e :~H)oRv]!Z:Fq4cA $i]~uγ/|pgs<geZ|Ony<9,οnOC%tnӨe}u;~AR_5?&'^wo^/gYiS^r{N&;XЌrA(Q2V$ 3IJ Z"gv?x:M + :[-'7Po,Op:IXv||+wݡFP̹08 bY?9,ĒH54;sFt8.ՎCq̜(ń"__p7&'c$f&Y3~/Wgޡ Q fK`bp$## +F( Y -癱J$:zCiP?eA>eOSD @cuzZͰS Քuo&+*>% s1Kpqi80SKL*D޴DhZu&z\4N)-4a8fW5D1m8D$߆k~,5,? eL.Ao,p9d&q, WVqNo?IfrٴܴnitiŎ]Np03J<2tCxΈ)j~ ngn2ym2$YC򂸴~?kY|L%v\>M&9ss !ut?k"CuYȡZ Ha& dI D@C!!sjD&L486Z^{m!W`LOFۈ6'5y0fVA$ lwZۿ#TG"0{G[t0&!>GS!2q4=_Vg:N%HxEqCqQCd,%q@U>|ec:ғnd pF)Prߴb>Āp![d尚L])0n*M !RYB;dM `Τxؙ0Q ϳi˜ YscJz̟7 $$Υ .T /4NY&P"bQgA|UuɥMgR]dx'UnzQ\`gS_>zVYQL`ABN'& X\2I1qX}Wn'rh-? .Mre|}X!߷B!eԒF;ƚ;#r">Dx=LUrOYN.:8#%ҝN}Ͼ\}n~Gj&ȶ벶uOsK-th wMRȎ'dOc T  NR6߂P&R5̎U*rB},YA2K5ud'z:, HԾ~??kO®̋A>> 5/?*/ K"v{a#@߻yG{Qu>sh)Ujd :|dVw8'I1|X W\z-+fpo@nXpBH°Ξ1%gdKg#!9i±E$4K`z’嚌c2+k2=}tq%1lVa|6P"DHXi8vlzaݎLrbm$uzDuRI/FCtRLN2! OR$S+0ay0QppI}tܷao`4ݷXJ!M&XaS_ !pyAb%&7(>ɏp8s2?8j ?4m_]7T?B 5wxO|2R P%l9†MB2 3g>sms4νnO`Bh)?ޯ.롅2XuXwȫ7?<6`_*R ujX?ADbM_.'Q#.DeäMv80nϖR|haoGS`Q 3ָ!9a,3LUMֱR!%ִDHhʔ`ڔ)IҎg=3@Aa#>sM^LU>R [e4榓Lq'&*K%[r\ G L4✘r/7m)}̕s^?m|9sVJ fBp.~/*0Z8vO`d!csv KR]O\7}#A:ꮟ)WzTn%o(J3r~r/Z:͝}=^ ׼'{J9Zg%`l>ҋDL̶{`hI&|&Ab\r5s4  3sTΕZيYg\B /IiV(zX6dž`n{{LK1cͳѠ.P)A) Y1MSNr~K H19ƳH+8;W,a@[>s!O p4 |" AX s* DրE#tO}[n,g~\k~@Gڳ&MvֶlQlh{zK(IWcƽPO͌"Qo*v w X+[Ef1aS@P|8h N,CljtoIA]c~]29eyYJlsgߔ^c o>oB s9sڞzsAp:bF*~9,;c,m\zsgp#B#LxDDJ^EA1%\֛B?ͱ,-Y;S* ^t:{b#7 Qd0BdatC -P c@) 8,{'4zdb>/ hi# OKpccR8Kfvan9exG\"Usz8%Nuܸ#0' &5' &e _GbbӶcpI;ԥ|l^H"7zy6~kU紗%xx5_Ξ}VQEW׵S3 ^4q:\Y]vu_ȹ/IV{g];)g<.|KVqj1ƽcG;@;:˻}shlA<=+6\^h#qtijF/"0g6 (W nd T;`FB۞7fĢv rXnd8I)ΗNwz+)[0L:x0z)6{4z\f`oq 0_׏GUOg \w w6/n.&8kCgq٘t9!*0VntƴlQ|dX!d'lzUn8AϚj6p2qAM9mH(zErŐPy.SHbN[^!KyO;aS[ D F'geS? 5, y* !2p˺\x}Q4b}+ӊBcǏMA}hT*ы|t&  ˦0NgmsZ-ҥw?nYwR*( L^8|X LȚdY x4;7 "QF)JWm9m (mhdDalR@LmFW@T֛jv@L_ж\61;zf4&|DO~εPHyn}}kۋE Zs.յӓ7~۵\n^)^^x,߇g(lo_ʾo4縧?YA?J9 ItA#!ØRʜ4Az4S Q. @0$:8T-=ڼUb{qI{83+=揻AAĞ6OB,hsΞ e) M2oxD3/Lt'.`E}ҥrg֩H ,1/V(ǂqcB`\,ۻp`rь8mפ)n@[WlsL~2\IwfsFef|;KW7?By{w+FG;r(VlIɢׯA`; .Lm7p\CSv\w0BJ '}xr7|)87͎3Wj:-7!~>#6Lʭz LRClwGÚ7?OPs Ypܠ Y0ŻѨ:n߾꺢(xC0}_&~xggDzj:DXI A1&Q${1ئݝpjKEŰl.X fͬ:(vsadgχ7#Qo?$g.MGlڒ嫴,dRd-l…ǩ??'~@)#Ծ|B8NeYnUA$2L_ gH7$F7rSq,ˁ9ɭFbQ,ԦFc0b #yM3,p]w4D"!r#iH2>~n< 6  `gtޙQD.p Ȭ>F@9w?v0k#3˷a@ž8df* Q !}߈dd ÌwSx, ۍHh֗g'i"V7Ig$Eh~ɼ"tу/OWA]pBB(dXl M80@x ̦{jdĢrr D|0y( Ț- >cXAՌjP !jDBG5\÷{vŒ ĴizXu%|wn2=pSO Ť+(݊}Īfdn-BN# ZW|0Buc C`{y)N4eCOʬ65 sQq>K),n:~o0pEH}OMm~-QL#`\}|[FNPc/؇C}A `+DL<<@CB bW $q+fφ i yBw(.v@ģ'ԃ@?BJ#=j/0`LXY9['n-ݵt e%<87?GN)e$Ie=ÈON"%iYO7V)=\NC ?A/h4͎/hYj8NlEYm7>٬k JUl1jx~&P6 \lPޣriѭhƼDggObǞvA̶oZ6fo!ˌ08X ;FrAKMvcFS̃Wr˭3.py;/d{è{{g]8_ŗx?{]M l_(@5]i (Y.}~.7{BQY[W^x0[;yNan#.H_L49nw|&櫽<MB~"( Y )JwtHRzF{((U {HPeX%6:,*vV!+'`^3 %l0Qt Zo Ă:(4hjo|ֵہ6cDn"*:`yN-APc\V7np_gę3zTH7`LѷDZX.}@$zp-s7AYDi}\+ibeN*yXTpL]V_T[' 3c}F*5ʆqK|tMr>t:o?:L2>|翬~ _ j:6/HQdA<Ӱ/eόB0e5Pq*jPxRte*HEBnұ |2m=\af\'9@? HkJk[$zx 0wbGb'c(GΆbwgqvWĉr墺ڤH˚Zey_ޕV(4J_tms tap}7vrY?>^ş!8>.>_@>|17׹|À`ua>_$!])WK~ڝ~/r~6Bun»)$l8 IdDH1z}'L3Ia10@`A&jXkzED+d僓io`k|C&}Py6Z]{,;Rd3=FUFx-q%cBZ\54g?/d. p@ #=V٩^!)ˑQ6Ee]Ɯa`!m ~BjYdA ndt]gF`8 yQ"~at**qJ%/c|_5VV^/8>쬞j]'r6<Ft;ϔ\e6߉{ rKokz8a&1H4nh Ş4^#Dwp<ʄ9@J;pRۅh~vx뭺TYc /ڳiKfNJ[[k__]W=<, D>e+2Ôgl0L 0~r+t=i +67*i6_~_cN>@_'g`M7 ??pYQ72.^YNbhVmq|FqmN?`EN ƵX H1 $QA3\nyAK!3r_YcJc4SyL{Þ|?Z6wE> J d\n,UOij#( 10 7[R@l*!4TǙJ0i63B]#^z4ONRq"뗠J4"%c:ubG'LM\%oR<; _Po`* BUd$,P2}@U^HtvP8dM- ?#|?8 O5VL?鹱xCcV@84Ml`\t+$q]Ẏ$2k粂US{l=gYvs>D[I6] A4s%gq=NS)}8E=H'㳹8dlc5`gOxT p$ D xC>{ NHS5%LuH\qnŋwfzJqNƤBT?%`e#( Bi;, T xs ,4S8By(y~l&gjW8Lg/\xxnŲw^y[%Io+i2[^`##p)s7iZ{Љ՗9WRYg*;֯f"a2XuQqioKD6Ǿ~!)*5Z/ʞdd7VXM'쩩Չ|A(7Vf;iZ]_J p5?fF]󵆗N2>Ř`L<Ԩ,3&Άf ç@XJO^6ihѨ1{H$`6Ng 飕>OHSQ‚m? ۷&$ˮ+RU3hHZb0`Mիxbkby&2#!ﴲ*4޹&!!j}!7{Tcc5m)2-鉼n]|8ڽ+ؽNRN˫LW1|/JǷ䙠;B+rNqD潱᰿vaiAӔy@4 ˊT# \TNۣQ\Z(3=γoǾпcl/C9vC)Vfǃ|Pbmu<_l4Yb,{Tr+@ Pyy(@#yFh"EکhZ)%(X @زT:l#!,)шYÞCC0P>10+td$[!a0 %Xru1XdL2X9hRri"a @DlYpN!+uR,j w`KSݛH@{QmEf}bG8?QO!wy#N"`ivS& 8Vc0hȧLu/@txCv[B|OL;A uhݔHS鳘^*I1O[5#ԭ ƈՌNwc ;@ғV*h D)5!)0Fs B{$7c `" eaY%k"ώUM%ecHD \lH!~. "~TawBtVr|_eXl ]״xddh?V8?RL -hZp}}~cc{FJ;.֝˪edک')n2Oζi2  SY:pr!䥓vaҊTriLc~L$hI>;h:?^5&pĢAxܯ|0{h0ܬMZZ']CKuɾ_a\3~cN ̍i%kF̙翤[1kn{Z(O*(Bʍvku*P|AhmrฬǏ:7< ?bLɦzK/hjpdQ g}+clƉFz[ r\a ȦyE m1X F>sn/R.D4v]+u lHƢpčU"v2:kZV6ŏ? Fn Bx+ؙ,ƌi3`xs~J.so31/&grZr47ߏIW7|f4fFJʝ#t^&PQQeY߲`PK$KKۮ"kǔ{?r';/.81f'~Je@[y.8fPs}ܜͫwe/R`c4(6nD٣ZkV݅N8ݑ1eBӽĠCvP2;b&-h>hfu}-d "-77:YM&lO1LU'.zRՀ土+!93[-nokFpkeOJ"⃯W)Sgz5fTտ{6gSi691 %֨Ož&$xtrn\ & -=&783TK9M <`SFqtSu)@H rth%hlk\|ZZ 펓:T 8Ms8ȫlZn Dx46Kcڙh=Dα9 LesڍaL'x9]Ϥ CWH==h4@ɸIwe/ƃn-;:]6Ea2q"7^>2-5V]Sk_ʲ *Φ3f³,2NB88RVS5q)ݳEY z8?#)R0 钼 qez*bEghHtBVGr-c{d#_"<[ HvTd;xВ7*S13;FcJ$,-[lJ@,/kZ4%E*Jd4FE˚y?O+a?YF.y;l>;Fza:1SV}S^JD# t_^a{$ n= E`yiԙ\ƛ"$n;: :Rıƨ}Q:wjꤶ|[}R@ygku0U\ޭz9鯷R~:zxɝ8 k1*[ZiyyIՃZ'6^K;#͍-'׊ ǼLT)vk)4<2'C\m+1XoD!E۪G# w'0JݏXW\L߻`9}TI=#f^O Y4Z<){;nR "(m܌I*o^Qh]YR'V5-?170`0?V"vU3F;4BTH˝ Y =쬇_g}wm#SHux5d-xi;/חW%F߬Bc-nmɥHB$nRE'ipIU׾|_g?_lO7;?ə{ßo\ F lT`v_3ׯ0B,La/v]La}c9g+b!R ;ȇbr.dfS ,f`UU5;&Xi [V!+6|I[aK<^z(P(PB[hIV&)Ցie $_ +ZPn]K !,_ ;ih8ԧvD/0q =}S\8 dY!VpsOpWF\aǔ ̨ nl6MdXQw<A mv9WBٰČN'{y`\au'F;MgQUA0fo_ R{ߐ@13pmy(//_.>scnJfgGa67g>rtWg_<lp ?ǚ  /-vDW''ݹݐ&(v.]fغ]RB} 0yؾN=NވbDHr;8l.cria|R,7CtҎvwXtfżh%b ݩٌ KSRםxؙ<$##H$;% BJאdzFOOh(픏k❯rWU+S JŢ{WO_Y;2:؁ԠǩRwԝY9652/8rnW}Kz/G^E5>_{#+? "l.G{ Bfܢ2ǃ R& Wx,B r#-Vae7`Lx| @3sb3=׿M bA1z&{0waO7##z94ɱ֕ϩKGY(^JdCl(XuX mOVfXtM=NTJ$>v3[[htDXL#ĻOOi,a7$PU~dy˺np{{B4\(fQQ&SA/2sA?B/P> ;r]Yqr-Op'.wAUi~̭{Zy¹7`R|pgdma%QL$=r|bQ+ݾ|B1z͛G=Ql;\ed4*Enrj}=1993;N9y7g+/<Ī2!_21ݛ_QgBoVj#GnLn%=j?fw N8OZ {gLq=æX 1--Ͷ'KaTVjaWXVpd!bAϙRwJ=k*QDCr`eN&hH#Ad9?#7~5"KmRMs6:'_dzUctOL? (7eDGglůB;o}byb;wʯ{OW;VmOL^W\bT} n&i^q|*AV=cؘXV^B %^can @cD=QQy{ KQkA (-2`2~(PKd_~ᠢ A Ξ6WWPgcpa*&4n޽b.bɰz8<'Nu=T=t,IT0n:ﳓHp߿)B,ACz?({aq~v\.?8o'˲>*ih4p|.H$pn$V`4(D9}wYF4d].^H*cg].c f)EKKN=N6;Cdl(UH< DEԄ@!TI~V+ry -%rg>L=Tzqjdb*.cYԩ;wo# pa}|ߟۖ{!Bm&;7oTGHq=?Z{! *̤UY2'čy>ǶV1YwPacbo+6pp<;5)^Eַ )em-?)%5vu l SM%P5Z\w?yW{K-|o-?ʗ_!|m7]/շN|lUjhJjݩn|Vt4+Q>:,BEs(}F#`Vymsz[쉂]Eo tD^(r'PSh4%S> څɉ`@ArٵÇop'ںn`PӴp(j#Cu7}w ۶O[_?['8nqw>qWM;.K;z5F00-v[,r ֵ;#ΔmlDP=~>a>M>+YCI"e|5a\>⋏| K譯<~p#Eon}v+QJE{~z.^<#zA?e(S۱>gG wOK\Q<$M'o9f=8 Ǭ:XV `CZ~=C# %qf-c?Թ!F̽M|`ʮ0˵^xMmBxRNyRlKH!{v& p6}'7D(P:>^ =2qy3)c{knT/D=Z`,{`^mbl3bKbu2ib[%09a@b?cJQ1Z_-y_y_9 f$Դa۽G f'}󍷎+?|*GځDH~yZmFo1FSc13[ʋl*ش ?D! |8.o~\(Asƣ`o>xPٻlB 0G^7~8( Rյ'Nz1R6A3!V8#0)cAI%d}:p88lVs%Qt&ƛ.+W>> |ΤS뛃`0\|R=s;w>Bo?g*E-8X뉛7WV~Փ2 17ܙ'Yyp}'kU8!֝O̥_A?6vyHKg|ccJ)BGM>`C#*.ꍛرjNJ[OwSUo$II{[/#{IG>6H&ψҮcul ߻=.we2ÄY6<Ld6:|ɓoph# {[N_+-O4!|u9rɞZD"{]?sh5ʏl  D&x.nj B`C!agߌTaBRS*L#OXT ;a6>AwF(blΥ:Jyݥ<'Ukn"@YB{)wD/ 0)`EPC%ďE`ĉ$r~:Xm1&{bqKAowIQN I&'xvcwXL$S ˲ n,b:pcx<N%O2?>;Ymm }`T~,0}10c$|ha*UE?|SAz'`լߋF{N>-PM-@t=tfɈh~ mZT#S|e2sO♛7>#N2v'E{{YfV+vF4𩇦e:P=-wjJ*8twӹi#ivE4M[o&>f&ͩ/,cqiXV(V)] êڞZ'^n~艁h \X#f>)N(IĭnO=*;l;5i-ꗾu+W~_|")rœف">NGʭ]rKҥxscK4v,n`n^kLPLpjc1!,1B1>z B832FȏEdbyopL |Y7 !_/ܓOKV 7Rp5mVQ,0H6m;aߑJyA&bx`pIp=|1vH(@S ,K(X;2}KQ,u:/<6krp'ljZ{oO怒>r,:H>驽_<saȑ-]%ix|׷ ,q>%r/fH"x>f~Rz!p>RPg2wI$Č"Nw10_`at ({swa!io\_D?6YCKGתdo"DT5FlA0"De&}.Z<s1x9mj(g߼[{,㣱YWWORd%m+GڋcSpwsʝY}ܙJDর(l a5q6^KhMO{❺UŊ|V{vJ^yRlYB,!plL!@( B6Y` 8dI 8Uk " D8KvwOҒSuF7RS5?,@V%`xHǫcYFЛ=^ߵ}e津]"{0n J5 :̨Nl!ԧ4vN.M7#ѝЃq 6XT}PTNsyNfɯ /ȿOjMAߺ+p|Ȥ01HwYVRq_q`Ʈ_Ȳ#, 7=e\X|[px.dIޭ{13)5ZWb ӗ%xKƉ[:=;-dI*@(lڄX@RԬivmW3cO'zsTzvshU!є4Azmfobb/Fx0(|Lh677F[ǹolK3苿{ښ Ahx}W\۟[VoQg)EACu$̡b&(,2kUqq`I|]ZE3EP=~Lhϰ75VG|"Ujv!T@(PJ dI! Kf2&tN9haB(A(UJݽ"1 &$XٵcQTtv# 4z6,il.s m;1JPOdB*OZQh•dͰ5av7taz]*}3vQkNX2%Ӈ:N*ۙf=/e<ŷ'S(u! pk2[k2b%>ai1L9gL+:q {:TkwOD/O70U +E/zQ̳F(1Uk} G9iiaNf7)PRPඪ@H*R\@T$[dfpS+Ca]Ǒ\W+a5>g0HD11].WIEm) gJ2I$',oz`4 X8SKڸ+V֋Ē*̞|`FD#ٿ;ΈHow{+wô:?a|kkָv</,5Qm~0wSȲq|ߋ"m3 }޳9 '_LO6{K/=|jٗ^/d=W*fMh/;Xָtb ۅCLM)@&1Oږ†>&!4\88lld};9| 8Q&U[]Z1=sLxU^ΒpbJ| URʖĝOȏM+dX5~$FMd;`\-G-3ٱan4@n`6KyD(n$솂1ۣ" kZeh+/@;tv(eRcSa&pw";̴2</Kq /#D); ۱KC#!<T?.Jz`9_}鷣K-__ܰՆN á0LNVVV+^o{{95ݹ';> h,v=Wd1W10"Q}x&zF{ R ~8xF3yP&?2+p}.cmCN"&{J7RĈ]ǎUs(pVq~Yr(L7"B>ARhURJdM3 6Te ݾJit1%}+ 5)uAmζُ8H7KnHOIY Gh: $L#tX++K[[ ]&rwn.=Dž}  XrJw0v4v1%4]d^1鑐f磽girjR1{YBdeg-77/~gyPUřދ/>Tu^?jYُ\Q\GrKixbyj5׵nSɣځHҷĭ]Թj>XVMWĵ|^dzJ8ldy Oo>~UGͺ"ӝ;}fFvp44Ϧ.buPlz9Ah\a Ȼ cȷ{XVט, " i!RhS<1r﹀Q$Fw/k:Qo־%c;{cF؅ ZӚ[_pKTo1帕ֱTaȘ +7 HL溵MTaG1ՎPh+6ü#}1%'zKĴNplۂZOjg&⢨u:V.0 f4:v#'xx Zų"JO}>PLSu^% u ogF'O%?~߼~}~ZM+U"irw CR.g|OXo?-ڋn\ua-uiYh!Ѭmr2R a$Z!3"y1Q5``ԈfQS ͍"fcBץ !\L9abmrHU4)$%H$%HZ*M̑ YZl2 BQhXGرƅ4WHŁa4-q~º!P@\86 k^ʇZ޺5v+*YvCU i9$J!+3ue„a b?W*fCmvnDbV90Bg\$= @ʐ 1F 0u' =ؤ@ǙbM CMƩ`Hgm1M0)I;a&V? 8$iҰ0nbmL:q89I82Zhή..ny`FaY-?! pRvĶVknka*ߨtWӟe Ðp99Z۫dcdqA<G1A1̇v )j:1mie2']ha1{ rf*0\d>8I/흊;$JѝSHЉnϻ9i:0A|67ySw֍$B ͼ" TBDrޟ{p-ᅋft:7 5@꣄̈[CViCRaX`zmNeTç_7gbuo1گe\uM-jy'96uh6az¹>]m"UHB#qJ):3LT 9]z,^z)Q+$#@ңIK+a$˙4-9wkRQdE.`.V9:;D+.KHVq&08P QG-wIC' 9ȱK/OgwgS;}4zm;$YT2ۅNWCK5c!haqyUr!eyX6Kg`Z}gä<-Hq&Î!  u]Ki^Ȧ߇+u9!Tebsͱ O2M!Q3/]%1Uc;Cq`Œ_*ЎG$pbLfsff;tݰma`p̲G6wKZ.0 81;1,p !Bb! (J4Me(UU9{,, W9NXXxBcsψo].Wqِ/x{ULXAFpݧKYΘ+C=i+2:y߿\? tw+˝^}ҭ?\z3LQۨ6+ʟz^$j_Y<б@~FNK &v_tα0D5z%Nw;uj4{]sxyoylKpLU%w)!)- #zп2PXM׼lgBFH,L7;>g}-15۫!St_\7S[)bBkyϿU?h%={er*_Q'1A{aQC?wRPriѦ5W%5'G<@p>TĘsif0}-DT i\`= ִXDEPGzbA YO{ s4) /8sK""13juA(`3aC%מOU gѽ?_~%hM_C ^wjA%We)W5B f t}DxnȖk;BS#`8¾REU)f0$' 'F,kjCg\1>3IUK%F8xVĔktڟtMړv8UU-$ܐ>j08}^/l*dě/}_8!?DtcTBQMVԴϏ^ BŤvץ"+?!_;XѐRL6{xnX"[W\(|w9yoK81rw^L|,=uyp|vin𝽐ۧD+crGIwfbc4o: ,/p(ug-~2wFMTN~7sӺܺɑ>Mb^ 5H'^Hl1O|K:9\KGxHi3գivXF3#1tňo=vSFGBgskgOujWZhk;z:j;vY^;#jξ!ߞ/@_ƼdC W!ttt$[J`x 1ڤ@:65&@1Ȕ n_Go<2Qγ l(pdZnvwxS"C-鎉>ˀj)GTь}ǗȽ]"MM_?|I.VoۗwXuopդ Y bBHr7 M7]'5AF<ȶLJ۾ ǼFSyWIzU=ViW Mywf09AUMlC0i6i\BBׂmi\.nҊmց: a;o8DܩՊ|+ʲq{CwI|̱$]˲-8@.8"4B0f`0@y˭,+~?<LR7( ~YP7jd}ׯ6) xU3 : |LӰ!DɈlibD̝YXJf/dMxOU" :Uizw)RNL؅T™jyq>we~eY:yj퍥Ⱥ&CqJujtg~+]5G/3$J0H nH5?7 hwNN$`"~s g77>Tmǒv^Mc/[;\.JFᤌ`vФm,Ƽ@Qzg;:{fqɳ]iݿy7 ‘۵L#;zΧZ2 !QWB9aj|3d&UkA\|5c;*iSA FU`R, ʨA Nvs[W}!51p GM|ێ=_#i(^%%5IX fb "UU,!Qr}EG104Y%VsǹvVDY?.cnkꥣ~S ;[iݾlߥe{lrS!tXrYҀ& $a&yWw~} @ֻFP)\6BB!lwp\sΤ @4;J]-Q1`a!R1npPdŌN,fj c[S'%ÀZL"^p!Oa0b cT.8˲Cwޒ&0ƘiqInv˲z}>n84E\fFQ @<w{$Ap,((_}_O)~Ǘ,0<zvjPf2/~/4rѺ4\2vh%A/(5,o6Oո?KGky`\\;mzg2o%-H l*3 #t,t띎g4re2iQ$z['TXjl[x_I<0/43KGmM(f!qW<)29r__8u^/{nu:oۢ/1lm1oD_n0hϝvv ?(Oe9ŃUm'L>(yo_gV:3\QƂԨMGoaR`coݛ4͎GRvbҧ d;c1\ Z$)&ґ 5:\vK,uOeM79_DQ%/@w!z(Ja iȴ j&5v5)A"%SJ8Yv3w~P8 4>CKtG&G>:l#Dz0˝ڱB:b]a= b2r5,8IHgR/d$ ;Rk,i8h$OӣA?LһiP,z͠JQB ҂(G.*G)椱Ͳ.eyUCfcI L68`R $aD8@7Dv;$rܝOC^q zxX*(NI!ׇ-x@&U! #)]:xNolUGgu)t+Q"Ǧd3s;k4pV2(C? ]4n4yMUfgР+R+(wN\͟m*uKt2'zS3_]@tzɫo> RU?IOOOTJB89N{Qhdy}+7V_pխ;/^j"HS/N c(N2*^P6n7=ܸ:?a`Jb㻎v~z$\=X~ڌ5Rͳ+W@`T\ .`j$֯`@J:(Gx#"fj)Tiw޼˿ yO>TSȐvw_ ̩{gM*vsuOԍNIciܱNղ>AVi~$/[wiw.[>1iqNj9{v[3;M(/dܠA ]@0f5< ζiC$w"lʠ Xhn%U8x2%n:l _[=&OjJ\g6._\<~ƣ7|O]a<t1ٍ.Χ?ŽۗX-`%E%j⩖Kրi$xڶJ[c ZHr2M4mTs&% bfSK TszxU"`Jl2ba#W&cⳲ'UwL-J\z7j=kgo ba>Wz>_ʮjwv NI>1E~@ h8w~S! ²X[&JQ X{XtS!m@X=⁃| CҔRct*ـr (u7}Ej:r@̰ bfD{Gr3IJUd5.`$'[>OÑ~{vl`77} 5 cRR+=Z{vsQ[=Ԭ*6HM:ֵx0dY23S(| r/?_K"A$uP/̭)>f_L L6nEFƑ*tݴJާzÞXL{7sVwo2#hg`ӟ68?t7 SKکc7;vg^i)qW[;Nj-5m+1Yo'nzs?jȺN $9?AhZ;y֑#kTa &}Ducqh_ET{:u W9/%Io_Qȱ٢Q-:T "ĩ]m{R8h7482źޱRQDxڵO|˰2vԂGqk_Nc'*abo֗*3ؗǢ'6.cL8t{w?ơXSjV:FIcl&b"OBAr1Lh'oaR%bmy=L7<"h &0)j$toV xO!*ObT'gobkUtÓvcW>*wRq:|YG_Ϫ4$80V?{#uV+{t^;6Cy]o2e7dt|U${q:]o<d8G _993g+A zt fo̞8:6NQ`!6ܽq~nz_(%ȋ526`3Vtg-\ag=9%֑0Dkᗞj~Sez}ܹm]X>~ToE)nHC!>'7*b//j.(jfW?$dS;]Tp!fyUW624:4Hufu(yxS;1wr¹P�k.>+o|_#v:xb9d$GH<]t婳eV;ȿc4GKJoDmL\--XFؙpxԋ~a@ v9l6I}@O݈2lOJ*P+Y ʓ;qsT>g͎-1bl߹L<% 7SosO>^q_Z? J()l]?˰$C%q1 H:,M`|uRY ex qCC]GBkI3f J]F8)SSdvL~02}'TR!YzA@Xvq6M!InCpB0AUF& j[?P>nbGK[[ QѲ87Q8\sVC~L_{Yx?'/) BP*MO7l-kѹWf3ѵA @{oC7{Hґz}Ud_Ԅy~0nt GJvG[*yŚGxRVoB\.<\.0 ~pd8Rtzٻw67nJrO.jox1u@dİԨkk*u TK|i?\JIo"^ ]j0 rX@;G3@;6:k"]YFj{ѥJki EÕJqI`nͫذHEyz(Oi.#XoyWmc|܍S#ѨC:oxڙZc8S3a3~#}7lKi4L#BɃ*4HDI^AեeH]KAM`NRmvQDkBPTfM7\7i607,ys/ǠtVswfnJOn"uB8 *eUʅ|i(( S16< @B'>m\n2~AB""na4;kDCD꾽#ԣafp_ V т8$euFez=aL1F8H FHd *l  #;AqcV<#ZdTll &B^dKC,VQAјuׇv})؏91RT_rU5vӅn<ӻP ~q @n⑇~ޕ0pFg]ָU_ڋ(㛂rl0F`kd~L)(qj<ޓʮ<IJ' gsgcO?~1O+ߝ_WwŹ#Ko_[juS/FaYF/L&HRvf…O]~tyMOgk&dsݝ'R7#x~ z1q@S,pcr":VP P5bqȟ:FdDZ|6 U  Ww{qX;0BCxDKBv![>,oG;b㶏@[_s_}&?ӏ|O5 .iVO/_Y]T Ceʺogp wvr#a H^EX1"E#0qVRban@T[xMQv4&>,Y2">//-~ P OX_N8PEgyͰIM{*00P*lOC@6Ϋv*m!;.3 Q! (D Ȳ,Bl $ދz xh@ gSlOj:ڎC@dYjCI'lTB#4}wrr=庖Kc1 &j3 vʁ;p~bA+ h\yˉ.A!-Cĭ>9uR7߸u[V2C3y{!oŢ-?O)>omE8Ei[<6MVͿvO4?,>)m`!IkuhH v!6ն&S p EeѴjC"78K4̒M\=N~v FWf_UvDwB)DS(P(f,TEz-"^|t5αJ~翑 m&kQ3> $,BЩov,{y~q%wjE#V)?KVv(,[j7f"y+ƭ5L3FcZaZC MIޫDSN7MzcarnXBM ~dQ|fn'"' hteyl]}x@?$`WW?).=JkU_uD{T2&j-WAѼ-0>uL CVv,bԶJɵO(-}^ߠŶu贯~1N8mIod^aVB5^xڕ5_|^>y׾OVV^?ՍʼnaiO4MG囏<\n|f݉ZUܹM *tި[d=Mui_F, }"㰜Xѧ=DlP2r阳:bE˝De[H?uܾTJ|s._j<|+sQ.NQU82\j֏$2GVg27<[ז( .W>WKPqow"u>޽z=+If39Dv{0ܖE;(w ˲<#0=n8ΞH$Z(HX,<'y>xwFQʱ'Kܽ庝Cj5x݀eG\zPZGeTq,Wj9;: ^ժZ{R9%{cC`ōx4~Z( xm}v[K 2p9pʇ+' m*ϢM-WçIB^uWz=Laxy<*̲qdchYGMGE7 (h37%j3wnOeΪh[z6Նm-@V@WA5f٠H]EWdFO;M>uS ~Ͽv-rL r\hwt3oZ@J)Jd";BBs*pN W/d7!;ٻiD[3"ОԜ\*6eۼ iNUֱ3Vb8"C`@8n1д ZfE:4ɪ.d;д_cp8=:>+ΌGlhC%wµ\piTIRP?A\{yuwUwW  D:yǮ׎px#VV:HAH`0wt]}ffUޙ`H I+/:^Ψ|~>΃. G'xlj[o 'E xgB/>9#L [>`ܦF 6%z$8H39:aʹYޤl IU -2Bzy%7.Uv,ΦU~ Cr2!oS~Ȟኦ8|,K7yiB)l(Prv4k0ml-N0iFݫZn ,ff6Tm4½e#rdcOZm*{|rBDih4ө4BT*xO<g~.t,˜O8,L;Pwjc2 IDAT?>,xyZ1t |y4j ֭m$SӳԻjSW԰[lQ_فxFK1A''5#[qqâ9Vʜ۬4#eD#H 0 qNݛ8Zgop^dh;/똇3KO r9E%I۲EeEڶm!E_7nd9n`G!bM y$J{W?m|1oZN^pJa^cHYpovTz5CVzn #, J0{j ш)?_hS͹Jw_[ȓT1Vvb%O\)HmǕd Hokb ޸zz}0H;21L&崖e9RqfEQJ2%׿M`ȻǣD# < oF<+wXcd/bUaO {l4dZô)$]T<'Z_fV?[!j5i[ԉ" :E tP7e\zЃ#=읻ٸI핕sݮu}[bjŝ:ұlvZbuZӨLce!2w_ .16!@>c0®]-43d'Iи\[Wp{]Ӓu?UvS I?io:+߫p/ +oϜ9pz{CiG𕄱4ppov_ѧtCXGҟQ|B'fJ~4O !L3a0:z{Bp9KXp`]0<є҈+~/ &e$ JdѼ)¾x m 6cΌf=n_E|41+b]\'=Tv}'IL]a1VINo'Z%_ [8*3=5A`6?!+]NR-!A+H=,^7ib2l r(I$@Ñ$铬e9xBV0>P2qPެLe `ͼВ>lҧl^(5жP3pjF-f! NktHz+V6}GJ7=NӺn|܎xxZ͖bψ?iOL<$RPYr+v,~j);a}hvc<#h$N,vv/ovqaWKV:낕 #kZR=@e# e3(mޫ?,[شBT.]O4#;=R5z-HEW-[bk?x:g/{O%"/F0f|8oHr naF*ue>_)İnt:Ko|oH Pskı ^>p mi׶_a=;晝k+#aAm4S ORyCN;}u[6e"%41 p @E6V݈eqܜZoEГ7{F/b/gu Rմ``PVG Won,7Iaj 7\MX{X.kOe k;z:$F[ɱLNȶ,Y6BFoݐ 4\W7NkɉN8 $H/ܻ?_IoQt;;U1r -ƙ\s!ZRk`:AQKcciB817҄fY~!8 Bc WoR!fREGmy(,$fZ;?s(i:5)@؏?t!D\qI>l>x@M\ Y6A/?!]dg&d2?kʉXZ*FFe<3=a٣:4/W^y}|rd_o,v>Vܞ힕8r#Se}bT'ұ,e3fB;f{ MCnYy_c&(c~2)Vl.yp#Lb+޾=2{׿wWܺ:*޺v\(P|+K?+Ww*[k|{}StCU4^>I$Bc>H.8rdܹgzzC7t'SՋXN?E{P`PYi-\ۯpxz-9XmRzu8*Єn2v<3`Kq\l;F4}<x(+Duu&~87 Tꌦvybxίm5  ÎCu$l8|l-hTGv \z-gxqŴBU[SUo]% 3 .[iؖݼj 26$gU8XJ}(;9}b#Okb{կ2O<7f.6}R߾J%aLTfvpbtTC8qq]1 y00Cn`qt700M#)GBhÐlێBYG,9[BQ~n^48A؏ułCC_/%&".+bдj]N>10"=2PT!,jU{8sҍɓg/1]Ef66.B(; p lǬ&8tHA`(76as;J4T# T#Yv Ǐ ^ "9J[̠vjN3qq )ik$ ugiI5wq61կa7.SJS- ={bjQ~\5w Xݟ}A̬gDs6ZXxv59^\z vؾԱklvSXz\nFUPj eìoH~d$L颏ǹR v7AFsnp;T޼k"&J,J}"yչ7CFun%")^ 8RwQiGay7jIIt3Oܽ``t׿/޼F hq%ORTiB(Q͍']hۆXWeV ϗ{%X!cbvKpVj2.Ԛt 2*3^ErSC tcdV,mB32}T TE 'ʞ>W8{Dy/{xdByz\ /я'wҚ5"97QcY"Bq}Pquu4I>/*/~ o0 8t"5iJpBHheS5bkT1A-bg6@z螀@9 3֫ ~t]|vXBVmPʤt } ( fT_ӕXlNP (`?lZ,˖e}c<_ .rp…X$)41T:s64AobxfQe -n6#GdiZ )Or@M+Zx x[,1ᙚ(V6--9.HvY8ڟ^"j̸Sw~ ͓׮:co?/aoV%*ewsg=+}zh[[`S~f}4mIk8~~oDt0ERuJns4;I&w_f!{4;=( C{ek.q,Jnol=_=~>{"4LvVt~ 5}(]/.6'B' 6u:/%O:|vc3]Ehn4[qP}X%A&~~93C>s<ੑ=F $-9Y6ۺðC\Hnpci{0ķr/嚍PdE[#_ҫ3u{{莘םޝȗFoN!s"ӄn}Ȿ{qI f% M!lIs (~m{H'9Dpx{J)[K9+Iv#W >heg|fU߳t螇CtQ,c a[Eh>jBLj bhA!0LGb!Ll@eF6B6@V*fE\~TÓ .NQf0(޼9q|~1ۊ7 G[s'4Kel?L5M+Kj3 PT>a4?v$Ȳ, 03IM]g;CW F?p& 0-.h?0R +簍R*eA* _J&)EcW=Nw ixЃ.rQװ1P++ќA<%"$[dK=>(J=ڡT3嗀J{psׯaEQnjQu1kzbj)tsOt+do;~ƅodd7s՗̌ Z %@+SԠA;4YnX4͞g.ѢGAv{"lx-  Ur8Y;dH܅GbuG$RC&\[ %lڽ;w| ooԉCkײPiYo\2LwHF^ `MsjaA$foF%˴19j_]d'o.[Lh,mVX5DlXîߗ>a6vݰKTeo^'ny7޾tkc7a%nꑀR Q:,Z8Rԉa5VUN5H eBh;,g@ȡl/v'G=$5y'V9u3=SțMym UMw;)iҼne lr0duNM%WVzvltggoL G_r8b# Iz!t '9|dE1Z ݮscBOo•萗96KHӭbaFS4;* \EFBW8j_.PVGraz[4-`xjd쳴E/l~mvqyynO <%"vk XyՑ"vK,4G¡j )z #_%tDmZ6A=q[ګ߷KO˯TcKn,_<}qk/imԛ-lZÝ9q'09t)ȵ;7k7t@{|5;vlfc한G99ll-,5DDq v5?N!e/(:yV@JjBl:)~{M~˯|X#)gP0ݷ2ӱeexJrG-UıkߓPJAw<9m٢F@oNQSObI{iF}9$[w+(ߤFrIsDm[Kr< 'ײzZٽOoiFU  A2\M`~g8lXq́c@GDHzP>YmiG[|"{yh?/>EAh6@.TL*ajY8(paوMo f I33N^_vssWWE!mیa-.BfG IDAT!>:ф?d>oyyt$}uS'Yjۇ-6-E7Cc[$ELoj0?j^ = X<|Dcw?K%IDQD˲z(a<s0_yG>_W/?mo";}~`z?>]nhsw/Xnn; -> U abaWhF&6Tmfi9h=- *hwSP1Ur %ҥ7v0aG"_٧Hv窉vE[mvE=at߈QAF7satފw&샫G 8yrǏ=8wJv3[F3)t=g,$Bl6 =ˤx=>TOD3&d.kxa:@(2JքxQa%v5ߛF&^lN}2ts{c#N'^bA8Ό 3 whZMvr[#}KmAVBe&M=bIק `| =F'n5lӲdG`Ua$g"$@^ƝDp)= WH O~&j$glD^vƏn^=<; ,ìF4n'FpciO8-K~cF$6pBìtKȘiaA K01>eGPT|ac 8i^c` Ƞs,[h0_(,bòzG I'!RqK'OInHZihw:j-{.e^O s\BH$I0ExܮOEZ 3V0y=E> vpcc7/w7"KZdVgktը~Rf޺,PPDG!=# (n ž:VMbpM)#{d5:\S}&2NfLPo`yVݩ9KrUn[0u 1(G#=%ߊn.\}xbolA$1 RϷNV+^\9tcT7XpߺŧF{6oG.^1k=2Ԧe<kV$lŗ:jXAu=7yPbf!~PKtWxxsGHZVε|]qgU@k)f{)޶maP:#`Fcu}4n}lwlgJ~߻s^$_g/6c/o:y"YOhA.,ůۿ[?ז7]ʷ/>{A՝?_KP~ލ՟੧+ر}8 x8ClSaY]<1hAlƱ_>u{P!q;sE9`m50 鶼/H^/OIΝ?&EZ@x,(9sPH7ῩuX3A>ӛ1l *FF|6tv{'eP&I~; R ?MQZ\$V^c0E;L ;.O6 rV|za|cqjpVaz]O&ͻ<W@٦w-P fB{K酾?BŪwFI֪'*+cLLrvõC>;O y2aivc74Egzg - SO;k{RW*mWn^sa)7ޫljCDn)x~o!枘4EJ{Z)}hpZgB=s{Z펎jy]=F}ft4@ 6f"E|d{s}";}*c U[$5iܰoNN6V+_unla߈aJ7ji~mZ@Ӈu]^ PU,an).d>swv2ocם9[[ ώ7YW,ՖNgϜSOW%[?<`'f6sJ-SI0*.y"LFjN q~y`^dlq0i?JeX =£J2!j ȡBɲBpaI =n/xm3$PͶ{$y ?9?*²LưA:Ajr\! >)Mjge[We:EX^_2Jm뤫cؕ…I/Cզk$ n$?zB^O֐OmGOA?c=ƁßйtNÓWUߏjn ſpi }PC`g6NJޱIX.Zyr:,u,`}X/hebM>|J}QL+T&ڛ{5U+zrDʄ휊9L 0sgѥIeq5P2k![u^闽Kͷ"WIh2~KtG-N<}7kDЅ}cQb ?'/+|CҸZ-=Zyȹ4UJFoۧ .RlE&f{~# A& (Bz] s(]$E h|˜tR7ʽ4mp̷o{P^FV_6fcQ;4.!7G:3aen.pi'f{_?1,L+EӐ5O|8eX* e ?zкxiލ+ ͟W_oOħt]S:`Dco~.O,.}vB2C-t J]0B[4@Cu "1HaFʹZD!Ctw a' 3J7MRWѲ0a*:;$1_ksx~8f[ZL6=p8Ϗ3ví68n.;|"EG,~w:,MM=1Ɉo& NgLxtά;Kѩ6k\[7Y1+WrfyO6y\.b}YHǩr_"!t1Vtʱ lƌ{evF^KeL@O-H6ɩX!4/Za"Fh*fYm'u@ER:(W ]BxW5#7~Tm ehXJ8@u 3I+EXb\Db2J5j6Ld6M^($[>Naq)]*y%bYQ~T !zqrrO0d2v8O Rcu+0S~bc; BX([lϷ_*xqmONWͻ+Oű?}\l(6SMc҄L6 ~)55Ϗar&͖nX]̼+ߢ{]G@w%2M{`Rz0IZ;-i#iC yH{*ПMwqy<\#|6<>j0Po}01,i#&򷣑.Aof(KUq~ş|wuN>$ʉa7"zFAj|@{:[o0'[cOMBIxdћ=3(@zwvEC2Nj( jbgnS 3^ H䥌 {9?N6r3dK'46_O4krWHMc,ƓW^~tDzbvjPC?D_/LN%K<0S+k%ZߦxY IO$z}8 Bdb8c&c^q vhM2 ts|ҳoųRm/h" }^vaYq}@` 9KU B{\t ;0H Y, >JA#l\ M d1 ! B4Sq'&a k4D%@O@o#d#d$zjY{fIL윓ޕz׷o{7=qp$(BA؍V+B }2VKT\,@`LizWfGg0=3Hq?TVYuSy7 j6vLSދ"pQܹu:>&#xTA ?ׂDcNX 1~4xaSu_wdYU\$g-,쒄PؿďO?!$uTזލjX+eANA4p85($YhV)}A&)Zj*ˆҠ U[{gJCʍ{1I!xNUtQM\k^<^`>dյAZ-B;s"DVtc {$(q/($T{ꛯ )\y9Dw߲1_vF?yhO jk Ѩg~, HKPFsXLŋn4C$BX5+Uyx"b,fI85M\n{3EafY<3sm-LޕL%.Cr#>W(3adx]p㧖?˿pU;osWTFtxx?+drL*a-Ls_ݛJg(uk Eyۜe;ێl9Y'OM CėՆ1#w DPlA"FDe;AıL|Ţ@(!Q!F$ c\ni OvO_O| Cr-XÍvJ`U瞙9? 2)64Ds8 IܵM7狀1Y]YߋK-?u{K弳ujj[:yos1J-kȭ $!eēJtZ⼕57̮Ӝ%pan_x=qp"v vds/Wz/E >UF S_}~3S=Ħ%! :{pdI2)KhiF#7oŋ.U@mbk Zoiu_$lI{̕g+0NR6NTf[&…,I9Lv,n zLlTn+^žd3?;y\p̭Wlܢs})̈́nHp3˧3FϛhT*mmMՏ3s[y{x4(u*ë~ws(c TWuDKglJN 0ٙ %A\L%Z)ޢeiZa T!Lc'eˣDEga@zL:pR륅c>UAU6cƄYa<3cgj~XHB}! )5V1Bc#8ƀ"1fOc&wZ9  53SeϰSSL| LS TΝn㿖?@yĺ2~%8 /fE9W IDATa gxpNQ)_?}˧ټ>SQKFWuD>b0`1K/ŢfW~<5&jXoBSb 6CxAڏڵ}m0/ vjVS'w7Ȅ姎DDTt E} sUO|esۍ'ޚﶲDw}Ԅrc)ZSDTj/+._8LzXR1ЇIjߵmHjnsv#\($%GhsoŠeL3>eձJSDkV^qH2b判%h+$7kwRFVWx^HDY\1T\K]7WR jq{K+}uҲ,NȲ.)Xw+s3;c☮.f7~-Vr8NcIFٙZtqBb$8r?hYAs,! gA3_a1= aS Y DHވ[ڃH!k$sh8>uַ3iBsVov6=xs>bܛ]mۭA0ԉJx.z/\yPhw<'$LYڑ.Hn50h'jmtfV>˨NAgG7[‹Gysqbd o.9֪.k LVLV9/{57u髽{q KJ Ð\8b Tob_;'tҪIr;oߺwbsK/<΍kQmiz?7+^-G^"/5 SkWWU;&߻9P(|D8$ cӡ4Ӧ6z*.ȎLΥ!= R~8w11ʲNI3fik ++N)G$i !Bn*1i²e)n2 +[y]*8F 1 LIbbEX5SA}، IOI Egx\Ŷ[*kr+TagO]x15r/' ~!0el (5 ]Sic4E7M"8,ˎFm;gF5@Ȳ,OGLU}& z/>ɸ-m 9#OsxVgqu.tP@Ա=Y}wHGS+7/{X4+.XhYkV[N&Ks{ĂF" gҙkb' T$Ť_RIż[ y)|LU MDg@)HlxEn'76V޾Lv7.́Vni;|K{IӊF/^ςR-rj Fynn4grOoݿ"9A* b|?=IG{ٴJyeiDAqlȲょ$,IZdcZ/ 9`7"KX0[U aD甫vX"55iYq?Q=|#t?U:!C_acaƎX(A8u8iA be ɄiQ1QM˂< 0B\Tj ?]M}FPŒn lOσ;AWD3h>ʑ͒jF.*fKAc <#M0'Ѫ!GLXV<^w?W|ː :,[h ܌{jټ7{ͯ: .BY4՗2tة>94'Z-lz}e@ 7y!T,L{ݫT(d$/:~FV>s~,綆_^jA$l'2)f`&uX,“D`ί5b߲L?_yzm1ZBܥR4>gYnGbr;5Eb:uY){ziJQC#t@!2ɠZ*5,pA}L\{LG!fWJ$iݸ\e|@vD.{ C'(qi $C!< pcHN^ȰF(D Ӛ\BpzTBLd؍pdFI09eetd$"pvJMى)K%p- 0"bT acO( /W !_`6}WM`"$ug(*c4AD,?AXf( x< qgamK1# 0(bd".I"B(4Mq< /BL Ab1 xEAɝXi$Yy~ 1A0 }TꣷRThXyiᄏ$c{[H]pzC`r$ kr>5B!MBm*߶+ rrejP{SqcTlKwS{8Tb)|g? GEҖj,`*x=32m2o2GA"l{7^R>UL 4K%w*zo]{??מcg[T)UŌlš{!Nlnh6COv@cXԵA/ 6 \juclbOĹq]9KlGS!?_J8VJVvȬtv>\91 3nd~s$iXoBz#I4c jjdҷc${t7*YeA>nݹpž/ֶ "Lk>/tTw3֭`?[өpD1VN?BqI4d6Յ}eHL5J\wp*J^0nSlB?a#$Kgj67:ҦA3GuݬUI1 ?,& ՅKiN:x+@HӔecx覓X]R~+͵K}i]͹&18䇇ܰr阜PoJlO3uT\+bH%̦ WIY|Xl5fic˅!"EŸ&i(&bXwPևxTH$A A H rr$$;{/ɾ1h躮"Oy0b*>&yM> ϓ4Ͱ,Xl_zg:2 409/*7ޱE!|w4%YcO 2_ŚXM'ԍ f=-^d7K޼9aR l 50&̟I/`)N-vO r|,M4%! cYNexU59fa-Ή`x*hNQBtEw7:‡Έʧ H20qL_)JJ#NNMٌ"sMỎ'-å~c}%Ie{[ ;~F{sca;ցyV<1RlTUZ*wn:9H0e'#?ɬ%7;rhxAIGIeۭ {6xiC;̡w:ߴfmȰLla:I3Ic2`מkK˶%A0_?Mcˡ+{{8zؚZ&,ߴSFa\I:?3BF ꬴ3N|o.6?KtX,I+d"1@<\I{i:wЌr/n+r?:-V.BL.'|GͿoڹ:8ؠVB,#*ɴA]}YaVؘ|ڲ,xLl~˧QzE4:_* &sli} ТH @Hd6ju[Ч~8UXAI8;}t{`CD\ݻrw&}?LrgٗZob4YTK 2L{w֪]1 A`( ӶP (`KA=e1Rlj$XYN6޵0Th yFrd)՛BjOmӺYtm$楷=}gx=@T֕y3)Kw.\!olxpEcũފ@"@scArb/p4*xf!Vr /`n[Lэ:7Vbv$1&N[D1L0F}Fs*fݝn#vxkvΏw~|XBCP+W{GGwzJ&D)2!Y7*gNF;"tOu" ;ۜ>7/,Xu4iCBq:,LC'@TPn"}l+LL(_" $(` N৓B͎ƀ@<ԤU$APE.ՙ^<lJv @:G+U"@?H\h[`M"aB&e58 Faa:Bʋ00&Z^E8[ƛ~lƮ;}WWN1HRc;m9%DUqB|v/ܙFek&[Jl@s9RV9V q̔d2vcLmr6L2aH.4n IDATQ3(()!Lc+r)0Ө0 4A&ɌUaj 1O&t; 7 lw uo^.wI ! &OU^ 4 #0 h#-VD$ 5͜HH)ocI#7X:akxb*-Ϥ|ITRJ7K(  yEQiu]$s.l6{A?ƯM4hLNLflѴs}/ ۇx/ˣ[D 0+X5C;s1ֲk091᷵zI{~1);A=hx);˗V0%R-+-J{y#p⽮UIYk灒cM,, ;^"R޲BUh0}Qr{^7v_^>;S珵S9AeV"N\8Uop1K @mZĒ,Xvpbi}#Sn;a"YI[×O8<nxRT -{LA+ 1q.fb&T4#z^*fu)(Pbt##k!KOnjhpH0PvΎ{TfoT's9DzZ!A1KHS> 2BV㷏c[Ѫ B4Mv‰ BAW)1;PcilePH۱%LZ&Pi@(I;DQu2)/ϢӲs4?A.wR%-fBorQY1pSIJV&> "ͦC& )胏fA!305zn927{~9^4ei5X4-<{A >|>?nj8~c7u%/boX'2Cm4pXU}oeKWqR\hRa"a<w/F pŃ-d*#sGaEկ{F4[ѐ*kmX+/XA8<ڜ!1bB ۍ-6g:0[Y|J 1=` HX*t Avn0ȕDm~m{̚27VIbn{_8 nThБC,*tJ~ nudHaz> %H]x1MS I gnv ߠ;c?bO#R{R jT/ n >^[[\ T1Mlݏ_z/.'[BTGA2J {7ʧ:gv5zB9!b֤C D;*rƙsOSD4a_]ބYM7jߟ\!')z1A2bW˴a$7M@22+iALe()XouϜ,fm=pD5'W ch@"I%)d( C X8nAHSw-O] SpfjJQt$]'~?0ɋ˿JeɄs#`MTMiIDQG}x"u]Ae=DiЧ)>QG6de }So޻w,+Mm>oFh>cs@I娔t(/)r0޾\6a K1'`L%+Rz3Q%>g q8YOTit/nuHe/6D̈́Q WGJy&@]mQLܳtQr]a&i#}.K'.ۇ8#7ښ;m:iYr:FݸSIa TܜGL)F.f@4?-Xz@ssYI\ht\(zܻ!j F W"cLy)&̪3h0BK+hA a.C*XU;` 8őT- 7]&ǃaP$NNRx6+xFu0.9nS@3VY٣p.[DLT5z}JS9X'Dƒ(-]\۲$IzGa8D"nx'_s*0$ݢ VB n~0꽹F2Ο5UhT;Kb Nq~Q%6}ɫϋ0Fd-'IpMwnT98RYaBӞ_K 01?QF;(K$&~]QᚙhUmKՙM-хa+>Kro@ Fqreo GOUo{< jw[ SG>T33ѷom\p"U {pΟ1Κ01e0߃pBH#n5WXZIOu,E vEM"e #f`D@H LHDh^:6]oOP.@ Kw 3;;wqRhy  aJ{YaD&wĴ#\  !lCh#N=fh-B"~A( T3xؠ p=qBdx9 ȨtrA@kzȳ!C̄}fjBqA RW7`mv(EEOzHl[+^}8kAjm )/ A{SskVgrRj{APX"#J1TpaQa V_puo}<}ԣW3W( ɤ )>j.gu?3RUPT˝&ZG~lA48ϱXd'"M3~rҢ_2n2^Hw\{AĤťXΌz\MCX6r7_پ5w޿BXHm#yMkAAx3l&"l` D"Ga}x`D z>}5C}m˲z2!$I g?B~Ve>~Εퟙ1ѨfYŋkB<7_{sOnv|f7ą퍷ٸV|hKPfUϹۣ1 Esg*ޚ3 czS/Q\HPǮO8E2 ^.7R1Kg@Kv$nq4<; 1/bh4%)+l2JE~8)lpկo| ~ۂ0}vfkr*O9V!ܭL9t7s 3 QқMJӺKEGz"łMr0j`m >jtkzzXb슕/VSq<a`u%*u{cB*UCa!m7^Ei"ƌTR.pz&|6&Q6 FcaCմDbʠWkaJ 0B1ygrF >f!N FvϝJضMz#) Q^0B #dGJpA1^R |u;~M&2Ilع#G p( r_A/Kz@e[$x(>f_KxO:@(ꆙϲ>V]9O1]́q%@ep=zܤhי6 fNZ] Gŧ +tcsn,r07>w]8q1OݼacOp,uvqqGq5ֆO1FEQ>!L[-\:konlǨZ'.<]~w99;6uLZipK׏:׏ė-S0$3$]^^ƺpqv^l)qcqg ]1 2"+2RJ-\o4S N޲U4}Ni~*+Z 3-T.ߵ-f" }_FoNFʧ-1FF8cE& ~VZSθas ;g3u94ӈl6ndDvU͙u熽H8cQ9pưB8 ުn"Dcb37jW|F_ˌ\bI&0z$AZ qqqk1XW:6mKc&SN訯1#폸K&B *(=ll8SuxQ6XF siV}3~6͍TagBw`ya6/s=?Ruy/UM9FѨl[0R|QR04G#́8Ӵ9A|V1ż/ F``8LshXeX̤kc#elY6bv6R@ "7e5ރM)eٔ/I^$,nW@aX?"{E;h-rrk!m45\ߥc'|}M6L7;w#۹hT c1FY$`J,?ͦ)#Du}*vz=Vlyo'o3̉'{:TgØ۸:|C jiګ'^BV([9M.&]/M&b^4Lxjn沕%݊d7iKǽZ\'c0ڑ`9~ :8RҔaJE䔎PUIxrJ݈mv:%:!d2ݫf+֛3nj^T2KDfJaҬߚ垎rm5A$_x0UV =[6w0 PP̡ZaZ﹮n]NaG~]8l?gQ9KL?|q Jþ/FIU!U iT*lǜNkw~ߍΨV;?/XJCAj37)3I;g7'geF!0GtJ 0J~U v>+fO41tu,(˲k ȭwyW[NB4p ^Q,Lr-O]\/7)jKLNYW_ g#`,o\Jd{FvR#~@ɹGdt.Ժg[ʬ2g Hכ'ND*J\QxTHq#!a|?߄ճZ(J,+KZ7|ϛosQxggF"6bˊrDBn8:R&О?SևIG ec*5]hɗFjmsAB~*E/ ƅTͦ(bWމg2K] v\rAAsUNҒ\ m7=Ώnb9I;5<^)f7 z|0ܯ p1U(sAS#B ]ĸndjF`tt' xq]S:I!3j-ln,زÉ5{*b N={%r$S&w,ҸjqCe6'7g# O|JBqg'k;Cޟ|TwA0~qa0pG@?eهG Cc?Ǐ/ꍛoczD^w{\.hnݹe?t~xlyÏ>~PJ"B8cΪ6YqKkӌ/B򼯪PҙLOѼML尻^Bydb3&NJùSjsDUY5a]bId[ ͆.n;]16T6~ZT3 rm%זn0RLz4ɜ{#Y/4^w3߾|╻ +ӷވN~-^7Y4o׏pSShй)?`Zj(bI棝z|e#d|`wxW['jIƁN_" bjs7ZtoPIM˳ Aa='ο!rTءlcd&,h(ZB$⼢::k 9#{[R(㺼ٴ?Rtu/SXfXj+dR5d3-;tPplBMG`;+BQʇ}(Q}\Z(D*^ ,cั3B໘R B72v|O`;$+}ps1-o sb dwLv|C_=3-VU?\X_\qݩF\,Ya#=8m'}XH0a2ݐ$Kpoz.7._>wlϤ7n:G.]jr <<1 9,}Sg \W?\4p8;wPO%|*ߕѵR2,IGx0siT#;IJ ;]K';$H{n^Ϝ2Qǖ *çz'~,j禨MM;}>'D{͚|'nn'3{+E\]O٘=s}_xJyZoS!>6g.Oۃ>/+[:N;ʗ#ivtH 2q㞱sS"  و1L๠knD-DcWKǧUwg'>27+W( xU4@s/~UgD1=`l7ŷ~#kt*Enp>GE1PK<` `0<1 9نؗ"dDVw2+#hTd1JCݾMK EevlJ~J3*injc,29 |5P&b^$"o[V;͗ݩZSw3)m\}$HD`^`{!P_уW;Z{=&&'b瞻tRh>| ~#d" 'O4=N]\F$zΝ{w*rzS~ᇶm?|ٙ`ǗdYnw:8cf<8ص,KӴw) l>ӊH^'0H)}x0BGV*˪0 0vuD\>>/:v6h;o*b:%6Z\' {rd֚0k;VN_Lae{;9wp[-oVgdwM7K0?f&rjjIXڸLH=r܄aefKGj*QeO0sF%/5z,Go"֫-@f rBR `막P 3yf1z>xD1b lUnPfxqどmS9M X~W qsdSuxnG MTGG5o!T\xNͦC. $v{N6jw xdյlG ?tS q0 3^E# DQjuX1dYOV^՝Xx_MjE(A5.l'qcA|2Tʃ ;UB)=A>9UׄR'kGoZ˝M;9[[+Ư@e=ocd2D"7q"A[ 5KZy:dwvKyˌ Q)0Φsɞ@9( >G@-WJ5 ;wݵol0Nut lS ̭gNUe#o~뫡\]iq:P4hG. y # :FVw]YgyKU_0!uZ}MT4uhҜ{C8 0T4N-GBA)'^;)ۏfǒDUT&ovIasq]pn CnJa8=;jgɦ-OĨ(ހӴq#?{}KU!/}-Ih? ओQrKNK)@iSa0B@\ړ%aZ!5, 7z?6%)'KnPUzkqW5ԙIԄWl f ,K)B5]Ēk-gvQ4B^;n"'ZwՉ'Z?>G)BN'8᭭38|ɍG~WzmG`0?e7ehTBsjj_#w?99k4>ov+_wex I [;zdS.ޜ *tBQ(>Ͷxs~;# ^s%bjm5 .0TKS吇ުOf Ĭmb 5NHȋ:hJOuxʸ < PM wi,µf+f%ADMp7yz:Gֱ餰ֺ@^̾36}D&Yv/R=)Ǹ墸[CxDwaWkcltUqo}0s*3*ߒKĄPr(gmBYeɸ:PNkM8:TW V*٫Ý @~ wC0F{XjD)]eΧŭ*@\MU?`{Ts,L݁-oB0ջWnr}zzڵrDb˭z!L[#@1~L0=Qms#7aM`g^$LO>6ǺskWɿ}3Db'ĒǦZcf۾}zS pƩĂ*5u+>S?w kIgG{Ӥٵ.. Cܱ[O:)VIˢ&:_䄂QbbL!v)E}z c`&R‹1X`MJR~B0 mGp뙉܀%d7dQPl숀ȓ;d`1$am+H' |#Ŭ)*UX <wArw0f4X PZzE-)T a!N3fۮQ` <xطpg=<TQA.eYc?&a(7o,ZO4Q*(q\(NĄ5]1Jְ"{$~1#PJ+ᙋ\qbu/L)P Xӻ']D`LnPB)ΜhIٲ|{WRz5,>=*7̽Ɖ{׋›W +WN7.]JNL9NԶ/)'H  6 = J2Xc<Ư;NZ;Dm4zV[r{wW:Bql$(uXdbҥ/7Q1Nx pL<=DNG/L%"(֪Fwn_:ߘ_M_ַ~W111wff-i)EH'"w. 7;Qp~t:mi4€ f'*BqtbPqs8O CT+ae{EYY_'K` 7/E,~kL@‹6BBWBbtZ7AY"|bqDTmxƱpra@ qȚ[b%tpl/ڍEHW/1ug;U@ y[efmZ(K/՛Stah1ؽ+P;e]foG"MV߽ż*wĤG)r*6u w'{%Jgm^ )t{/77>\g[[[X^&;;Oݼ;{;w&Rݭ IDATޞ(ju_)WÇ V_ڶ}0cvC8 O| o4fGz8z7Mَ ~s4nw2^ݵLDى 4\Wh$;SS׮_?EDNG#.4KPR&!T^]=GR/?O7 `5Z|YVY[c}AeSî0P Y"ĚPnF fDZf*):9yE² ~Hd-(@R0![f>+5;f!ޫwqc;mѲᄡDv `KahfVt7WE]~Rm 6h쭝qϤq7l{6"\ES|MHٽ=*nc~_DAe?G~ڟHo#G&QJ?7 ~hT!Nnn>Dzڵ>zRlr*տwoiaac%' ,sUT58"|ݴ(w> gYG|v:=J$YdJU3v{z߸"C~>fcE,75` ` BPCwf焭 cT7m=$J) ![`D) v ͸)%V͏bϞ9>8;Mn4}g @#Zia>ƒ<U֟ZȐHƕ~wSF˦z 0+oYSeNQ2>.a%vRN6 p 00i">٬@)sgٶEcLey0Ng:JßlZ91]|=raT\0sc0M.cu-7mWmxX)Ɲ|NW"@WܮZ:d$hq^82s7+;0emG5+?ƾOx,<Y>5aL8<|lI",{8 ]!c,>Eկ%xLHy^:3ifF!o'JK0)BXm@Xjn,*nU_dPʅҲP:E힛PpaWt쩴k9-TL}i**+WJ50drr(~? \Wx2c:^/ygYV t=!4pD,cYƘPjYd8 f˲aѨjb(yD%M]/Gk?xH$'vë~/^W_}g&mm d&ҩ=y6Hnis:=y2g^U41#M40R)t2ʵDvvm 埥$1LU 2l؋ g*-&qdky(̘cOe"L G{ s76\a:J{=1wZ^J0!m=c//x6x8dĘl;sapTz4hƇ"- խhF$qs{`FoR_WrAv~"H9NE׫'7.D)BbLKdrh:+4Z? (`]EH͒4Es1~Sv$H핿ד3Z"̛'L櫯YZ^˞9SDkޥJ|4,ön7 CܷB+x;m}ɒ0}{wR<x,fۄT*9BXyxL!x/ļaJAe"4BQf X\<bHCW >%OLP*lKfywT={t_K/YXXW=wuvz4ٗ_y@о}'ܻ$d} ξD{ּqcQٝe:f[[!W(rqXmtc<.ohUd1MK4ƾ$x/vLwG98vݽo];RP(tAp$;'O^_-5UB+p!e%fp_`=1zdqta[,%T36%qc DDCDa2@4Cl4͐X}9NFPߨ'"}3a8B66die9enZ$ralT(^>A_B٦ HVn3chW bo`y`vDc(8z# :Q:% Gl<>7pkw|hϨ n"o(_0fcH4"[^R<\@`12[;ȪR*\z6,Jz\S1>A_{?ȯ/h'hq#$"ֺ+u}:M ^v`vnn)s3:y`Ljur6X@@;+5=6~`vB,v{070^w 1wCc# GZ$ ݞ+_[O;#K5XX5٭ :ѯp[y "z2G;|8&^w˗D|fLb!_\۸qr⫯blvҥSN]uV/-ܳK>Zmr!>Y K]z`w=7Wxry3.yGP^WQ(TpP< Ma^M   Tc!)1!tR5n8bN1ŖC)kq c*͍ę!8Eu~ x8 Q ԳZC_p=\ ٛru=;/&q:RG02!3c[<8ݰ !<>T cy*t 뼢wXdo|rCEmQ9UԽݛχ@O)B8ߕ7G3΋qw̰pi204幓˿vٔٛڋ}?z@N.-x饥+++G YW.GU";wnڵɓtCⓂCqF͛GLoWk@#ĸ: w752)ų!FSݦjd9MWrSo;`u Z # n7Oƃ:nd Z]M\m1G1x^E(?>BVYN_ Ǧ||Q,+ g4.PEgQRtzA|y%:/?7a/QEso&6e6 lc>^[ P[[_{gWW CN1'Gxy~BqYgi} zK/o|㇯ͩ b-'#,^)J 3{(e>\e W '/͢dCZk1G\@t+b-@`T1&ԫg CD-,$LlQ-?<瓚hLtc@ o5YqWتc|ݾ Ǝr0)}Ԑ@5XWe6B+2> ظeDviޖ. Dn4iqƘk(cY҃*T6>(o׌ݾ θ v>4e $m Z%ylLH[٩<[#? ڔV*oϿ&TE|G~{O<\ ~%Bp,VrZ>? /-mT$~Ч r߅{5rqPqh~gZ.u8ZV3gF[+?q.s,("@o $INov6[] P3rA7oobt4[(x4.ypw]54UM"˲=^Uک'LM#%.h cԸ.z2ܮʕ:uHiڈq.@R1Ȉ OEuj<7PQb-/ -^g:aX$e39"#ͭ$ np#,0-Bxe*w^ˆak̺b=tAd˒N ~{Z0 ozyAknh?s՜xR95?f4:H4gOzr8\/k04caZ펃n{~\y }>߾*Nj w= 8geQ 8hߑ/on7zo|s po޺6Nu'*V4bXC @E+wћ$ز)Ie+j#MpXz`nV*jeyc˜@8F460d m;4RqRZÈGb#_C1jJl\a<kkm39hdCfpx'rr`Q*5Z۔dtgF`ش-BE6A w|<O(zOKU'(y/A8r{ߑw2}B>k(fGZ IX3,ԚZCS?:ٻb IDAT8ĥK[6+UmmNӊnuq7<B (B b+0߼.8AP줸QĀ!j"ooP*ԛV'|GlC3$0iŔ#)V"ɒjF!Fl-=":;@$_ 1` -Ghv,˿ ڷ|-׵շ4`&wT[4F6e6ez n593" yhQv`T4hkk33joTgwfcs2.W8z˕Vp8TM{9s:!=O'&_sOQZ l۴F;FPw`GC=S*N={n6~rFS< a0GJ@ZDe sbBeRl2a@8TƁEV)l@{ˡqf `9de"dRZ8$tbt!|d VIXTI%T)e 1;FM"(BN #Tf` cQw-WO(iL딲qϫ<+Vib${Taj1f-/lp.S# pbL(gg/~7=9zz@,S~ϷOCc˻?@#tz4Ap4<1C*`L/KM x13W5E[?yjKJU@ pL̜+z?[b 5e0\(Wb Vǚ&0ڛ1iNqAذtmx^* ӮM` u%ZBowHʕ:j!d HdNXU cVAEa4{Znu3G%՛شƿ+YNQU!j8␕Lal- 4w¶2pw` ~'-Ghz~Q#_rv]Y}ÃG|+re6vmEsleYsWӈ ϿL1#Z6yff6(E'Z<p9q\uX'yGkis2ZC}F*Uy[/?ە{~AS?m'p"t3Xp?uuWfS4y(UI*F!ķ2: V!a_! %saZ ,KI#6*L4tR,U1@/0卂0\J`hdR|oy:y`xdZ>9FbEHdoWaJn1`d͢ bSH*[&oc REI'[;`x8r F (V4fc YlFe9Ha*/qLk͢cöܲ#19Q\2^~esԶǴBˋ\5MUر_\Èe9:pG}SKn Bԇ oyw^8nϻO֤yȁ!qeYnl'<~yG~IY~ITLk8tֆ+x)Xߢ[^@ SR߱u14L +TH2on#CxEeYq nfM$bQl/|%<17Sq\CND'Ϳeekym:#omcf Sg  #9_5r*pev my$"7)d똶,ثX2-[9x{00U5b0LRhm[6hCiN^#0׶qHܥsQFnJ6I?׊Ul&C|)SP HV^.]Z-,< [G(vK[[Y_hi&[ 6;|hXuww_*Ov8{q >1v]Iǎc'Ntذ^m9XrNqDdNxrp(T HN{v HR4 C2.Np8a? B|ɘև"όSD(ҍ}~\[ml%ٙ rc'ϣ"&¶T *j2cbz\` %6vDGP`l (֫OZx0d -v3MxߠlEl0u ]| ak&!zFq]9)*s:簪aBJEN!8Dq-4v(L+ME]N06,* &B˷{ح^`#mjc ϏcH3bCGF? ,_kjSiXw,F"0;y8iUs!( F(->3`61{d:ްn2zڵ+ dƴ6DLvᏟ3gg]$s9.(YNu뫌r3u/>b|p&E|RCcǾy!>8|EUu1Jq:p?wӎg}a?$ 28rѿ=;͊9zCTkZs2Vyj]?6 h8.Wya5oWq[?(8bL4#ހVMyi *r PA8y F(CRc#?R2[~2F)ChVP3Ib :!0!R7; Rƚ"Br5m:K>&G5~~љ:LY"mыe!0mjj 'I7WEs_߽_˧}_"Qx|0?8 oԵ>3.u+W縝H|?SW?[zbz4bS,.u9'W{"0=[(EhPǎ]嗒 (o32ɝlPC.?^R7QHQAѐ!U;/y91>m*Myz(P$mXKUqܴR@wĻ"MeigFbqݾ/ -Tr̀N l!oWacBxF?N2q%:2'F2+&& bAMkݷl+$>4}Lƣ oJko cvlnccf;-'vx.x[Yh)}F^/!clYapO~KOrO^|?}3ݹ2_LUDy~X/NHgM9 д^zG(7dyVs\#Yy.{bJ'6iq-obr<01q}NO,Uǽ%U:.>;b/u[N"f: 2H51ۛ퓩%.' Fc]bn`}80|qڬ j/'Iu?1N'r O:t[\q~?zc'Ns]ʒ-xi @cwCwǎ]:wZl߼=S? {U+TD9erm]XV7/Fe!0 F1o#<,Z=>}qm ʐiQ(c?U|WvんY6F)W2C*Br5!@`ZȶunS*IV9e=Jp-L;+q+e/6;xcHl+&JXD\N+`4C6|qya#:%Rt7 xk\9jȱܥIĪIrVA-ݨk,Cv\p)G+sWATMׅ G[-6;W_}de:Ɖ%wr_]?@莃w&ľ4}"l:{ ?~}'D=q9 Èz]t^cXӥ6Mq{H`tRf'f_(Tج˫ڲe [ۀ 苳qM\N3&(6R-#f,3⍍&4S_jEF1(Q5ƀJeNx5BhD/C<şyؑYm"̬7fzcW_z{Xk;=]_Zzcyke(zfgd0pv'ޮw.]~'~OH\J~ߓ߆6n?-3nZpqL`ٳ6(BP|מT7ǡ[S)J(G,abLJgͿ!dFΚ7spzۭI-r 3z%e %wA涷db͖ &p텇/1 ]p$ǮX~E?o#Ld & c_?J)Ȳ)^Z;}v{ÀadRB>76 `AtJxԊFSf lXZ*z_O=62nHl" OOX>7W}(xMQ\Z\,j~B0c\ Jh8&4b"ǧOgq= Cv8t]wwjX,S+YX ?-4ca_}q{aڞJۓ)*&ieRxܟ~o"caS4n-oNN+VMTkNgdL2q3\@ƞE㟚+ӛm0Om̦z&h]('&MeG+7);VeǎQYmfbF 7n9y2ϓSkkI+hL> vw^f/:lqC|}{[_ [-^ؼ9R߸Fq(ImNf?}Lhh6juX$x9Xh"ط?oAY^߲&'HI\O1 T_ɘ#UNklIu`L"/l3A2I%N!gdD("N)zlK(MŪ ,o5.!Dy q[$qͶ59:DCRlϏ pH=u2u9j?cG˩/N…{V,f7;`0ئ1|B<^HCa>!> O$7BEUU?7 V0ݫJc84_޼X&bC}۵AW/eN]^&SA$.PA'g^5E0!!jMpp&_{eЈsv6"s;sHU,/e2dcqmm 7)YhfNPA7xbxŮkeYUU$SN v`^:2g{FNdm=W[>W)}/%D_>o.m L||~fv_/er9Ju@U]zWUvYGۄq5ޑ?}B˲4|{v!q RN`h(XtB7I'v__l"+4F1_DMPF!tvucvz{Pi>tmZJȴqwS,fk, y@6jMO`we(_2g;:,]{osEPQ{7DeֶjhtO+/ ݄B4c*tv a ~sA\>s]7ߜy7:SC^,>xGs~B!rQ$ڷD:8 -/c/_$-zCⳃ:#P;`qlc<ӓvy#zC?#JfXh*M7   kCaanl0Y&x0Bf "6µ 膕N 4Tpuip{ ؚ4ͯl LT8&WkfO ~Ogîqjk-rg"`"SIA#cUr!X T0ˎdY6lnq;Cnj7җbqGqtz{sszzzsyym{{9*ry)qJ@?5 |T9SJ-{ua(~88z^Ղ(:&K{ޭWt5ѧ p8B!8b1tp9 QsNAa ?Ql+iGnӱ%TL.@%zdȡ\! a=EqWVoU1 PUg8q=\\|& fޜVl&tfx橮*5arlA,{R ]Q>3ꡔ1:ɑG"q\KSBDe䱩vմٳS(fg/,-UU8!8]pvss:pjM ۖ9߉B-|1]7};W*UQeZpH QdYVUUQTz#hJn, @P! Gg=9PA!s;ߋ:8yp&6XNSqT;[cK &K1᥋ 4tEDh7kԲY-4#B638r{"l˲m\Į]/ܜOϯnlme].Vk2\T12  eceYJ% alA4iڦi;n"8\.{veY/`z^ozx2`v 0qC|`®W^ ԍ{/~E;pȈ_8/=&o(t߸ŀ&y0e#q;3>P$-J5:4E: vag?KjB#Bu6L &Wp"KaEDeM$,C o T̰ j#CsX9{.ԦQFFJxĚ:ntdKt ar*˗ EۙLX( VVX;GϚS,ɖeǷZmq8Z]977n;.//4lb͇^oVx㑢8d "FxL T)=vg8!F$Q<9Džr.W֟|do/_n|{_WH.]WVTER4qi3O}ի̛tf0Tyd <.6Ada]Yu{ɠ]ArsUb`r1Eu4mX[מsnv7~ )J(K$Wݙ3;OHdR٪$3]wzƣe˲7Ed%RD>9홷.br7cc]C#Bb]3N74A3WPy,=V$϶^_/sgllz q=I;pwoj.8Mz kYㄸu4zr%Ϳ;sf?]|hj2g~wv6T3SS7l;n][K@FlmMzuTߟ74uT*AU][ QF1hDYh4TU 0m vl 5,ˣY˶f?hf %^лu\=&I ?x ~‹\wo`X=vYۏEq4R޹BYښ\ol(WWe'c:=PsK"ٟ$q@C.Ry2֕ݟ}ݽC(*&8.lwjO'OfAonAf(}Oz^r<-h(8}X1G+uHR7ko_nꫫ,#YLnXt^Fqii.aj${Jy&TXq]I7{X$Mv>`zgPWTB] =G!K58 zwe芣(9,OI[=xKZ++Ozǿ_\MV﮾Ow?]<*M椻W)#M5[/a;!HQEOo }%n='^NzHMCWpf_&d6_K-K[&ʶ__O']̏gJyϪkYTt5tjv}xd[rښkOS#EnU Wx?[y-g_HITD6ۼ~۷_g̱,s}ٳIu:J"LZnH8*G$>xpZ/~jr  BYp9>q}|`LdSTBVA +W~GwZJS.\ZP*1IL1MN+pS{7 j{$2&ZՍqL[51jj]Fzs3tPQJ8GmJe u[םeh4&ff?e۪z?|J=I(˅s>iV~iIX~~A'D,8<=9{_p\ Ё0+R/v B'JdҺwӧ?'ꈉ6cΝW~v;oiӇ4Eke%12!.l~7oՓ'PJ>q˲ 3&1F06Փ҃jŋ_[R6?|>1 LtPR:0ۮNal[Mu)z/B$Yz"1pp9I0d aT[nјN&,˲qPX/KMӵmƘqB,K$`&\`۞v:M Ðe9Jz7~㸆a4MDbEMs2He2 ljr:J%B҉tŹ B$ƾJ&'`22;ښk󆽯$8+ EETEA]Cpf>"gF!I2x˭\kb vB~{4 Cg nP7.Q!(8">#w d,/Ix%yޓ$6VVҥt]u}bB5MlfYpFRY,O$'9c'UuU Vh6M۲ֻN"hvdck+(LƾreR8iߦIe7Ŏ⥥+5MlU|irGaj?ߨ`J"ѐ1"m;`01q.TfiY,ˌcJLӵFT.j5EVT^@tluJRI"qJ1`GԙV6)_ }oCAq4plvP]_j9No&$hFݧ7G1 IכsxKnWO&|xQIPhjSQV^*SUu-KfK++ӱ lٞr?m,I1nuHӴ>5(R(z>jo!i]HRidYh"w8cv~sͦPWSgGStQ蒥P96QH$| LOo㰾|z6(yg>+8cF#L&l;ϡQ*߿f`jqZU z=_ϜY]X("|ū$CGu'zE0&Yȴm5RIy!$91atLzcfYV.Thd3NH$„`q[ 7|yHQ҅&iz aOBg8&Nz&@(4ˉ]Qq;={T8`qAUՈ^toCh#zˉn9b(ů9"Z/4==^!ېJUGl6`1.1]q\z@PQsdsA8`s NhS B]!$TeGʈ^aEO $)61#bp@plَM" SJiM}_F1Bq]ww;@06*BHnpUU#֞vt}m\o"TaLqrTzf=d2] #?8;J'D7e9Lpco."(XQ =8p2 \GQnnnBajq-7M1cE V-;GTaG4 c@#/zO(&DwOþk[bHML8HdBjU4=udamce|BC}8~GGћBjp<;imz/Aݿ6أTJQn~"RX 8h,ړ(2(!^G8JQHpqݳc9ph6}o~g`W ݆=d]q i /ϸ9QgcC߮= g 9`ι뺶<9񢾍tQpMdYc@ 8PJ#6oG_^v*,kҡŃ -JiM-St@ `0#8 2 1ƶeu[-Ƙy"O+SB$ YE'Qw$4 8-B(Fوaވ2`7,;@(`S; m8v-Xι28D0cgR׵m qV-#?U]? :XU{DqERaǵ{._3V\Y]=9w"Lo<;BX(GlJiߋRޓM`zP2'QÈƮ8q,PL~^oqYJK !@3SrcG~?~JeruuaL$ZvBLqS66jgμ_b _f,v b!J-<|Q+J:elffn\U$T*UؘTLƏOr\.Ԙt]%`!Pm(7 A`GC6 /lz`osڪ7cw~9??>uҲ'N\*joaјTϽ|v;צX(if7}ʕO];O&^tokB81kyo}kKܹ{`ltEQ8h7WQE}aB+8v--4] BΝ98ܹRimmGW^!S3/6+S!=gεZrh4Ǚ,/܉OBUv|XlZ3_|y#LN !|ͭo !6o%r_p\ ܪ(J7]"'J`G6 &Cv'>O52S=K򙽢#~E$C+h?-v=3;~wnPruf&i䶥r9>t(Vlm=ߕ{\s&&ʉ> yhZܠN T* vfz2=ʟ@ )zH>`"%ϴ?>HY?g!IzyG Z #.>@ n#IENDB`gammaray-2.11.3/docs/manual/images/gammaray-qtcreator-attach.png000066400000000000000000001242471413310443000246210ustar00rootroot00000000000000PNG  IHDRB pHYs+ IDATxwt[G($YITΒ-˲,[O'4ofzw=gnYeY9SY3Ŝ3 Hd?f%ʠ$RqQ|,խp!чL 4D)H A@ QqtqוMЂ AC ahz  0q cLT0]oMov=ac%j44Ur,>.nfƸ?\.֯{1;^A4˜$qH"(5sEsd;|Mz0G jz4Ɛ3 ^&{CD+*.Y&Z rG`faљԃMwt<#-]_(>flѱ _rV@tx`&?pb r"]s;}>߾r333Wk|9:ʅB111cccl2$)1EQ$IbS1x XA"@MQ!>cՎ'D(^.#@,ʱ t]_ݷuk~zzz iF.P(bYF.c|>/ eP|4Mc,T*A^/Hm ׻tKkko_߿bbT6po_{^믵w,5rVR;:/֡pn5LNPQR)  u8(qXu:Bhnnh4`PT  IFѨ5~:2g&0O1F)-֬ djW^:rERt6z]] v?GFFF EM|Zcs3Iɉ[6oLG7? ڠϙsҮ;NDFF]Ƙ $QYJR! ~4EzT\ KLD.'Xhugef!\NRd9'>>AFihZm?<:67nX1wO5ͦ斟ms9q/]ޱ}7=(|ez~6pMe@(jni( !r9˲6<<<<1EQ8?/G$IR I$I 0C H"FϯrB_Mu C E /\LGq?q81±q@ 'O!J$ a=OgWw_¢vcJ8ryrRL&{N=o4ƧƚLO>,-=Ξ1yp=1*D:+Hz= H%Bd^$ph Z0v8B1V0Dܕ&^dw fSOoeKYq-u y5;j:` [ N;BcRd54MB܉lx̹MO?sm[%/"T*eNvAr|111ͥ(*?oAA~^JrT*#i:Q*: DJ$$II$q(iZS-aY\.H(V)U<3 VyH$ZVxh44Mt:M%AR90Q b c||Œ)cc f9~Bl74T(RSSx7dŦ$'{T:08fʔ`(z^ǰNeYVհ,j p(JTzwء!_9UBPgWWvV4ͅvxQ ~ʿ``@.?kCܡdh4w]杄ӹqi{ݻyI驐p:- Da1EK` Ir z'$̏t[PKMB(Dg V,&A~wOoV*NSENj$B>^7!0J5@vzB`&:O3er&ftt,\ը~kZxh0; Q 4D)H!+$}|tM@`Ibu<`Q&{)8)`b}3kӛGC(S=ͻL-A9hZ JA(a8 pe(:fc%Q0Ln'{[aƦfA/[JJ|oy Rc\^^v*i Ym?ɏBA _90̊eK;:yONNR=E|>Z$,;7۷zJAac>OTTJ+7 Ls4Eer\Qr2B#h24 565'%%a}tlLCXO .n4Md={ߕd'_”v|yLRC3nڍ /,ҨU#+3%12wNneU5Bnljn$$-/]Ejrk[JTvtv޾T(QlDA\.WwOoLj%R`Xlל""!!AP$>_1t[ZZ[:G<^Offg=Onn#_#׮]o,.^鏟<22ҏ=p:??e:&o|T24dtysLO"YA ?ٵ~eUJD~ac C8E(8t8))S.؉SP(ࡤ~ RSA8{Bwwϑ/4ŗ<CPhu4^,j]z`vƔm(R)$IRIEM*A|6R{B2lՊjիVddap}CӒEIZ M`…  \՚LƒWiZ⤻ux1le Zl9Nx¢¬̸؜,$Q/,ʊ5rr)alUQQp80Fsf;ND:wNn"Dr`D.,LNJβ wtpaJ &C?uKѱ1$XAQ |~TV Y)( &Z,Rb4MNv+W=wiqR0B!++LJJN* F]^Qd"1I26lgY$)$B4MAQԝ3h&ExxiށbX!DX&~?t `$&")Jg$IF2M xhFQ~NQTJrd\j燏*/..|f'N`۶uˍ[u [zAQ M~魲Ʀa~G zMOMI' xzN_~S&照6tg HXI8;4BE"HEQ4EQdzzډ&I"I AV-wA?0 !T*) O4IQTjJrss}L r($bxFd8M|5[hy;=FCV1~<J= Dnn+jus=ź 5lTi_T]s;\ǹ@h&`ڍZƫc< 4L;W|> k 9j9hQCУE3HwMGhMT^K835 R4PA@ QNТ(Y7G=/prŲܜl#@p %l/y޾Ҳ2 Ooݜ8z+UtbTz[`V*.4IYz0n9fJ%hd+-]8 O# ϛ8b7K].7P]Cke023|>_CS ߖ≢ȈlN$t1m<4-ˇI~Z3}J8槥TJeBP[{KSx|DP Ym:..eYBqPx:0U [O:GF/o,G>{}AV[Gg\_^_'{?؉ή|;:fY+?vԚ+1ˤRq0>M0uZ IJJ UU5/S׆TTV=~ry~ú֭INJlkoe;BZfu)E7m@#3M!A>;p(%9ie2TVY1C36\n6}uk@(%'OMGGA Af믽*H(* >+:Ηz?y R)0fi"0&!J`6_J$f!4``wuw{dgt'HiIO~*Yi'#BM3' 8q9eH8:qĿb!04( . 4D)H"E1))1`x̌r1W{GGaA>B!E^5m8khl8.##]U0.ijlLko*/?rX0t7.cE⅒+=a,-U608ő@`-YueKxn]=~iz[-˖.Q*YC>k/A0F$5gGF]]y(<$t: J+(b|0"<$NT(w>###6LozjBHSϮ[f'O&LR)g::#hZ`޼!kggBhUf]=>U"'+Go` 465M۽}]]݁@0cL#\q2EQE}k7n<܅͛6|o1ơ(o`OL-b]Ck7vIw7i57n̨ _77a  ̐J$Y`EQZڔ$FHg&--ehp8?ٻ?a(GB ϥ(d4zޙ)`Μ={NKB]$zB/,"B~`pv]}JJ4k\]y _ޮS[pUh9)1++ST̝KA)2d4Z, Te{>_YE啫ז/]1zFcsKɸzՊ"B1*eFzL0ѱ{: I Jn] r9B\{n IQ 4D)H&~H-дD"׸*|gR)Lq@ QII<*y>c׾2w훚(v665_t >@ P(n^G<9׮߬mߺ%%%Ƹ\te9JO!:f;ن߸NLiF-S7!Aˤ=׿y7++;TUtvv JIIZdQsK둣8zIYi)M-Y-mAttv3 NQ4A}4M˗.75fefwtWTd%֬R(3c,a#%%Yz^VC?9N`]}B ŵ*+?c ~RFF( ( sn ="ASϭ[*..[?F~>vN B^o©ܜ,J0B'%&1wEQ\|d2 >aiɄ0[`_㸖KiYdg>.,*4Lsrs6ی0DH}}wyy`08O .$7&VUI9GGd(eY}^Rk=[m6}DכMF?0HTR seԔp]xt8 ϛ`6#AgUVՄ BϞ_8RTE,R:Ov\!TrJKk}jd?᱓::B5y YZVսm ttt~w8?mniikkCݸyc']9ý"gV,[hϯ÷{z](ݿn䤤<gucS%!ATbQ|}i۷m߶%>.nsA/]\}ۖ+ӮV5 =E˝1^x[l޸~F U"Lq|Ͽ8j׮YRAd \2d=ypxӢe,BYyų;~1ڣ!IRĸ ((!Q$IttvHqQ!Ƙ ݻv_,xB*!DQCHxta{GNٶeǎ(Uc.Iz%^_YU3܁oVFsRUu\.8t@*n۲ [e MMw3Oϝ;g{ %"I7&''uUV8}l]}C~^^jjJggCB`GaŠARRA\jŏ~}d`p(\v/Y\a!Ƣh[?{F)EQ.|=}㞞%_bٌ12ǽZwVLw0{DA<ή!-,BXlniٽ빔$߼XB}㛥/*/EiZ.B6)$Ie2A+rs B(..(A_8082tpp7~ JG[7oB%''$QG92Y ҲEc9=-d2>Xjs=!߼rZxQ0jik7wB$ɚ]59˖*  z]_?B@Pn0_BM*3Ӛ[̛`fk`Y eR)B@H"$'%>NܟXb U+W$Z$ ERREQ$I1*UZj M5Mͭ)I 11999))] &qbDZ Ra6"x2P$>: Ymwk2YAu, yf - дT*APnfH>nEkW:tؒE֮&hI`+mHDA~ބO:Ѩz90̂8n/BH[0nukbhZ*gwl/.*J㓧dg+*L;m|/<׷lⅅ_;l7o4ҖuF\Ԓkn'Mv>KQ+sssorVٕeRhmmRًfXAPceRFG߼ӫn߮ͻ?rx0i?} <'cjhʠ ߻PrUUUcb~5vtv b9ُBXBQTA^k~}y_[qqѱVNV摣'f#ZŎήKWvخi_}%@ΜUVs.Yhph7}o }n3Ep}1Z0o299RTz64EPo0z!0LffFM1#G:+/pQ*><uɈ0njn9r;RS}>E>hԶa( r\.{z{>EQDL@Ʀ@0 W|)!D ʪE S4M-ǵwLzŠ׉gY͛! a{Eǟ DګGt8ٙVrZyEZҞH|}q$v.aPqQ\.EW\tiR%9)Q.RIlbXFQ[D z]ްnjǰKϟ'B+ͭz%:§(DKL0{L\n!nH@Uʍ $hR?$?P`iQML1Ƣ(>M H0Q 4D'%A p~HƸ ޯ}__j̝&JCm.8nqysؘKP<\qʕ[64=dd`FG_,HOd CG2߯i"޿i8qxKڹC_D ?U=?wϞpիV\vcnW*YKo\$###s&%& ޾D9 ô<0Аq 1--])]W0|@v'erŋrL0KDH7 PHBݞ~obYI -mE^b23|1))ɇ8:4dt}dĩٙϞn]]Y5U5!b< <- $Ib[22lKf#9uU+z70k72/]R$rnR nR9%9I&bWXR)G Y*33#66!'%ZƅYE <Ym뺺zh_z``0<^.^XQo`F U"SvVZsUۣiM{| "$RP[ۻC |=@`tt#㎟:? Eŝ]: 0;L\n-rBFaa5lmuu $A|ήn>pvvu,ή^ @8~{ E"/D88.]hIҌ7K 2~G=%Iq\399 2s=/{bbbڿk ʪ‚7o=w%&ZjZZ/>QRɲBorսrŲ˗tD9`0tbɢEf7زycCc+Wϛk0 W#ǶҠOsd4*QٶwcQ  m aj8` _z˖\~ K97'$IaX[0?>>c\nXU];&FСPc$Inzj=C&!4'7g``PRyaB:6VYE~~^ oۼ1|D"!;ZCӢ(UT-`!$AHXΝ (/U>~ԊeB:!IJ3EH\~c܊j}i_USr8yS3@(--5d/5AtG|-!D \VUU3OCA,b Co HB\b)4qR!I]Wgήn?hIq\[[;qI }39:LF }(:NKBHKM(_.]n%i}8111i)<Ϸ4-bvvVWWTi)cA<}d$%9i; :Wn4B.AGd1@(5CB1Âx$I_zҩ BzWUvvu"z1Z6~vBǎz{^|^.KTJj/AdA~ބ{J.]Qhr]n7CG5CY,*.r=oEQR]'` IRTx<dVM&x?^ww6<.~,˞8u5dzgr Bܕk7֭].BDvVCzΜ z]vVfw0 3'7gC<&ij B$eK?p5N:j_޳;';$IA]&}n՗ `0xŋޮs{OJ$gϩKѱ1WrR"MeV-|ejk))5>d_UuhZ{z{/]ZWߐ&Jgp[#LqLƘ6gyfiYww`0h0:x.&FUUUcy;#Zm$I59zdWwO}C~&M?[UխmPjɢcӷxg_(jܹˣC!ngoګfrbG;L{fI"b{s'uw7ܯ_|veY]HvQ"{'@t`0|@6eٖhaΜ9xx583ν.Ay4xdt\[11;GG'X,3Zf1$UhzzzJ7^oNKIQ*VƐ[g/e&4 BGg1h4 3#]T.ugJoo)G~ĩ3>8q>X0LllB{A_k$޽>gW%,DGO4MYff~ooβ++ʸɩ)1>V^qݭm+VR$w)ABLqA,P$AA,Pߔ-IRwO$I?1SSS<Zɩ)evz6_NANe fɲ\[wjCʒ֖0  7oZ7޲c ar\~Z/Q7f_fۃ֭)+ i/9n I^۵s,˿SSGeUw{& `6 Onnm_<:*;==/]c=(VTVSeۓEL6]GODD''%1 r c6[.\4daG;"==X.,liZMʍg#5eIxX`j,WUP[ec|c9-757gfa6F#Ƹ? @}БOMMMz}c'GFOZH:: z}EeUKkz} BeU9VA寝D>[PEӕأ_lG[Z֯]CQv8ii_e؟CGGW\L& c~횼ܜYcQ7_liXp fd/[r OOOHH2^ 's ÔreY~tZx0 H|,.%,*$ JR()J翮iEeU!^dIf5b=^/B08ND,+"(JIA $$ba[PB1;.Э6jG?L hi+ A8ߔU vH&X(JyAP:R)HAD8 ( oJK1_K˲,˷(oIslinmD)%eqTd$Ƹ 2”B`wOopPPFFH_a|r{hbbj?3ƸzrRR`c=O`Kk[Paӆ~xBؾuW:A`AwȒx]I;LDeK*j'&|SuM]k[/o]eukW@EU236>izM /n9hA**ff019R)wl1OLNԔG◿>~⢻m 5tZYoqqZݼqw}ZONM͖qfFw{:*В‚a!8""cOmߺ{:V +$hAN:v{vl(jVjTaAP(+S,2.WdDĿG<3w'yakVX j(R(E!TJ,KWfefT*jEiy;].Myi}`@xX(BHD^ ObO)zaQxvvw+WƐںֻnd cͬj4B zA?tԋ*8.:: E1>....h֔ QQ,duqXXx1QME "L&[R\j4FEFDGEED(2 &StTTtT$M>eb˕jZN_<2"v펤eK CdI)UrMĂFDE5|zAb?~Aqg7F  OoˡAgBK Z$N 噄wLqA,P$AA,P$AA,P$h-33f86OOEQtfyz$IVmjzZ|6ͿKdYnht:?X$uZCt^S[w.A{km$QܳO_mhyO_q&e6[ We/[zWTؾuP1.=_Ʋle}-Hم @}Ppo*yv#A'dee,sݿ,[S <.ymj`J ؾfyY[E5ˎOسKڶd♙M-Z摇 P/UT64\7E?GKϗwYvۖASKK{g,wGL}b)M@R4LW-[V6֮Ym?+_}krjZR$ge{mZyANLL߷;##uwdckb1y=dKs$IukKV?ԷMpA0/{+r/\|Kc-IRC~``аbY aO>XVfF]]=x;ڹ݈6`kd/c*@>;5e MQ:( %&0LLtl1o_xk`ha|Ss_xkbb,YLӴhxjAIכ+jvlߪ,Ws-efeJ$IƐiDFDܕg&&&RSBt:VNm3[)Tt x;:cbGã=h%ICBZZBԍN>!AFRnO{GGky9,O,cU*SzFܱMӆ)=gfa74DGc MtrڒE=O\l$III x< EQi `/_F -ؔ%kꮔ/ 2]["˲^ E$9h n)ʟ rS|a+ dVoRn B4AE4A508(hhg  e93#++RBHQ DA8xxpxB?+$OLlߺ(z]WrEUʒQ qEĩ3!!w# -NN%$75=w~qrRtT^P0BQ<55گR*zqg!$<]V)Y/'T0:2Mėfr3Xk (A}$h $h j${ܜ D3w`,JwJAng2APM@}S,##,[꣟4cj準  ; =OEePB|BJ519Yz c]BQSSuFFcw8/O>Ол6y-3'OO_nw,hlj6֭)Zlڸˏ o6?t-jhITaZ/S0́G6mXF)3Ϟyw:]K/ZW58t3Tr1\ojv4[89>1ϷqqSsKFzB>S-˲l6xAػnw^ }<_S[WYUsKcֶ-A#M&ˌ}J򋗂 A:ʕ5<,fq>CNZ슉j4-7.bYvjjڿV[6 lcc'9) 0^<`U~}},c%Ifh::2rdd4 @d+Z]\}oK(Ji?FcHGg^7L**,qV )*/pQՅi4s(r=,cQnQY@dYz1'{e*e@2caЖKV_mhRymL  zjbCG۷fƺgf⢂@{l ޵|>ZrZ|YM3 a2})*Rޱᱱ Yf qqFB$ @O2t7+e:i!g8Tlt{} =:eV~+(y)|`BePNlwё*y^HTKjT*/Ἆ" IDAT f W/ưA,V׍];=KB # nvgB$+Qj̰z)硣Xy#=9JK㭡R]t~"W9^"2LԥWh`'qUieD'Ç*?!n9 rW{qV`,<3RNlsK4"9P1$^JAaA( QѐJJ1IcT'^s0ZtN  qtRVbkjsFvmuK` Wn;'5c8Gap@~.fggKEʤʨđ*yG5;,( MD,@#e$ vYJ)ҡJlP])Q<哑Y\r$Oyi=C#!|M@dN7m) U@}+#hxG^zGO(L%On=S*9T)>/=SLE2`y/(GS쌀.R”reXVV0S4ѬPUd  E Tr5S,``2e)= h44UO9DjG r* j b R56$O-%͑!wL¹ˍW'כqHI^&-&'HQñ)pfs9hZz{Aܹ4K}t!TF; &s'<0wJA]tL ( ( y4XEy^eY$^(|z<Y?-8>$Ig<㴡, םN'˲|mݕZC9W<~wsƦf!O>Sg&''9PcZ޹c[Hp̜0gJ345ݦ(*{9 ++7A䔂c7ʲqUʾ;c|\T-Y=t }?2Gf,dcYzeK:>233==յu'OHO/.*A::)*\%Sgv9FE_[$h-DžCީfY^llή`Y {vdY֠w$zޡ[6^r؉ɬ̌U+W=vpUEeR0mߺEvtt&-Jt-ͭ!AޥT*+ٵZu5nݲ!hli;"#j4oo:֥]XSWǪT7n?Ȳ,q]ykJV/ ´T X|߁Cم1~oŋ`l>[z~uw;qjxxdݺ5 qqN|o;}>?100cVeKK /JL\n ˲QQ˕iz^K =]۷%%-y>..x?u⤤LMM!"##|>ʪН;q^oQ>1M-7w\_Zn HY|𑞞\UUMgWWDD' rLqDFFRjVQ^~Ϳ300H3􅋗ۻ?{ٲWd/h41.;_ V(AAI]k8_Www)^|mK21 ü4+hY'%׮yۏb ee;_QQ/\nm BHŇ޶eӉgDI:~C{vfg?yJ͛6 tRG?\n͖O: f!*!.vxdtvvuɲh\.ױ'34'{TRmݼ=Jϝ,,J( 5WvtUK/0c65 PU][\X3O>xqSg|>~||t >أ6;vƸ%==hN9fIuW^|AP@{GgooK/^\a2,֙|x7HųB)K$Ik蜚ioPM-%%掍e'-Jcl|_P*U5E_q'Nݽs_x}yyTNYa˕UW%&~k K2TJ^z?zJSFzM^ؘQaZZڬV>ZmC y'ϗWܾRi kik9>1 bb 29v-g@ՄAA0V#J>! x0W^[4Sged]ܱujk눌صc;ssB]=e˒$cR)|[gϕi4۶nt{K' j4ơ(ZxZJHjaaaGv努}9͖ĄxBf_>oiVFzZjZjʌ_n[~RO}Ą^ݾjkgؘStTtsK/c *cDQ 6-p$UVV\P(<ό*J??944VM̶>Q[ZZ-D@[G =C#{[$iyہ:]Cu)!--ۛF20mO?j589y8΁ /^YVR\t)QMں̌۶0 hNu:(FDDSCQy9?K?}Y])K?r;:coQabrP {{5<,W^OKMW~TxrrJEeenCB_zgJ-KttTo_RJKKz!dbY+ Q=^oD)#=sA~!arn)Vs$v#=-t<}ʤĄš'O޹=88M/Q*.ԙ/UŮ-Y=ムёxeQ9++X\T89fy^!f 2$QP(<ϳ*2AizV795e2gf_\qVnAtZ $-Je˲Z-<''-$ngEEF>[tvܮR{zZZ33/WT^tqjV7@R펴ha E:YkjUJ`X)*c'.\\89/7'1!~zz=}1˖jARqމ1r\LL4%p`33ҍF$IvpBLOOX72:v|ĄeK(:ræ斨ȑ#GMOO?{Z$^XJfGRSFWȋ0YffV3-5Ed͖lJϕmڸpxkk[tt>0? qO0 |F+0W-A7aXV(A}$h $h jr(A ̧^}/  LqA,X$AA,Pߔ-bUMxb'Ƹs ߟl45uuOLLwtw23sٖ6'<_QU}ǝ˲z'IR[{ǭ1TV}954>/<{>rq[{'n(X 0.^B%zW~pOO,V[Wx cly \ojvL+xn٘z7F3qݕNYҠAÜeB''.^x`NK+3''Y 흝)Kx$G>8)^9cEP'y9խsw‚|Qީj?o<(1z1/7d|{5uia+YGY\Tm˦;BJ|̓GFFG&&>Ѓ0=m~}*jۖM۶l8׿N]P\Xy?0|C|x4/W^ݰn-EU5rttt!{{%.U*] ]ѩR!4117-U+nwUMk/UTkKgK_XW! Aٕl*pC*kcŒuQBŠ<ϟ)=zVLWޤsʹ믨.X2+#cxxNѬY]"IQ33~+G;:;-3VVڲit"#LVmUjA6[A ~mILR$--M- +p<\fZe,_mhDCWEGE pjll<::jڒZRQR\ujʒԔ%㿽vtTg ;88xuuM]_Ҭ̌4~rf_,J}GGgPP¶%u & W&5^o8_Oo_d@f?uy8l#ͺ)Kk47Еkq1pSxXGggeUME, P-3*`6eI e V:rtdtvfzl%/ x1/'|%'/Xl=W6=m KGW妦,oW M5W֖zƬ̽a?V$'%9vZEq߁)|x|V*Y{XfΜ=?19a횲 >maaakV,IN;z{_@ڟA[.^~nρCư, 89\9EQ9{RNݶespȍvsѨNwA˕_eͿm@_###'N^fZO-Zo^\\lXXhمKn}##L}xxmf; CNx{T*Kc'N\^Y++kV ?qjK}uW W-J,Yry/Kwiz\x 7';=-U`.][[R\~RWwK6mMIqXX=pHN4kkXO>kK:24Ey40 t6^oZx.UT_(GGC(408TU]uƯ(ھhQ"f‚X^oZkeY.Y]xJeTTpj☘0dq0` Ҭ'OONNu^oimH^ >~LiqW^=SzZ$䤤ȈIBǵ-Y.,%ŅQQa¥W_{^xBR*1$|̌n0W._˃oegW׫~ŒkLai)+rG (Prv6@Ͳ+)bFTL>^j IOK[3:>v\ܜY$ gʏ^/\Qk7vtve1%J[Z1Ei?Y"ĽԔ%.;@fU*)^?Sz^ήSy9cú5)Kѹ06&ziV~mIdihxwC]Or Ô_tQu˯Y7]=z2kw?˪r8uztl !ٹή񉉬 uPTy9'NDQlmk/,ȏ ²\*?*22>.f}i_^c\QY6[<)::*8(H$anq564VVV٧aQ[៾XҢDskKR,I5E(;:%I7y~dd4,4eYAQd?Q_L+W,ghztl|rrJ)hhأ )ʘGz0:*QAQ4\0tPaI IDAThx$,(Ru#sEnrRMQ'{j$Jvcrr4M+egϕ%$_'-Jߧ)} ShV(gTBd((q:ֿfs8Vlo`jsݳ'i=0E1b6_br? 74` AR @ p@ _+*O*—, @| \}'AACE [@ \Ńaـ 8fg)h¡(Jӕu:RrT(X\Q+0J7!LL&sӭ KC`0JU 7(J4!T?7PZ'ȷ&C9tBǓ(Rio`a6Qm42 aj[[ 3Qc=oo B822drYu۹s%I* ysA_EU1<2}¯2  3ӟ;u7-K;p?/|w߁*r_on`hR^+~9)C.߲mkol9x PGE׷lJ){]y-可u뙉p(\bԠ knjlinZ0bߑHln[m?ڴq,˯oٶt: \FYn\ܾy$I:ѹhcAA\.db,a JLFI{Bx"Ha|>0tC}}SFű`R)eOiVQP(:qd2y<3Ʊ2Iҟ}zl:yɦƭwlݾCgtF{#K/%i˶4Mwuwk4dl>Yv7ש ZQ+W >rtٽKOǧeY>+||x'-\3gt:ռ{O1lvמrnwkolE)HtBx'm\7+Ig.sdC}^V d6+ Mg2ͺ.stjlhξ4Mpbۧn pFp_G>h4z0E =xn '7oëS$U=.Wk5z^QuֵS$ MĨ+ׯk_a`(I]mii^׾fᢅ ֭]iER-Ŗfe}C'K-\}߯洘fyxdX,Cd4DI֮Xhܰ~X,<>uTj|<)ֵYt:≄Z࠯D$ #'|CCv/6?؆'O86mk- /vٽK[紨 1ut 'Ԩ6m՛7=vyV}| $Alڸav^FbYQNyB[5k؅ "|zo]lVZw_ZlhhD(FU| sZZ׬cǎu@= y"6mMm<cGLG牫fs8 Gmo_,rG͏oZ>PY^3'?ֲ,0$Z[IͩtrBx䩻ZN' ܼsQ1IVV0pJF[:kol e?q:(IsZ=jXI4X'gPK~C (z=j{^Pq3g3@[m;vw4EϷ݃Wlܰ']{d( G4ESa^Οk^|>!D1 iz|!ժ]CSԚՏejXf1yMH@i.Txkw.KQglG>8UAX-DÑȁC{/zIfam;Z*x4b^8Ej6-656lݾSզY5q``¥bX:;%SFcW?pJv(ɣٵg轢UycAIڷZoz=A+Wp%t\b6269z$IXba=}Wx(|x  bC`-IR{ieE1wme9۬ %}).wbf $Iõn@X %QX&I I~دl6'E٬(J, fVqhd4% Ӂx<g&_(جV ԍqS`P'g|>ORd FcL3p$ΑQIFj-=lLqAp~,(RQ#b(x^P8䩭|b1Ca $; “&ZHFd6D"I\N "]#r0Ժs\68^DϿ_{,׸\A#ډ+ ֺչPӑ"hF,/ɒ$I2Ժ\.h4|> vjV_صgysk7LD"їXŗ^o<9w))h@*޲mw-$ʟ?0G e9t[,>XDsRB Z4 ڳ o=|n&K"f [QX|: M3:C nw?9 pu@ 7 NLu7U*+hٲ}, @ 8F *)hRF *s,+"_(L˽@@ "`j45 3cdQ:Q^>eiEQG5.m]^q (}V\Ax7,ϋe.?S #mms8<ՏO&] ݐC(^XSb vFBatDܑ dbyA7+LܐV׺SqǑN6WK60#? ^]ܐlj4Ҧ*f{j!3^B k$lWhlin]ܐisZH4Zh>S)bUŮ煦iU(G'>Soc$dmN˔:ݳ<}?&7,zkz=!,ԫrNvd67kKp @ fbpER*8rE%VRŒT4+82ZA@ _4v +h@| '@T)HA#D4@T)hWoD#B67gMӴN-P(J*j [AZ3tuJ{k , !^jXRЈj' 8$ZfRb8PTEٮfO$H龞QGN'INh|7~6}5 r0uh4S"v$IZ_d)N+L6؀9e9J+\.: Q[G1`fXuZdm2n#qF܉Tsch= 8`#c,Rğ,c-+<;ӑFT?xGMY٪柍 p ,hA(^zu7"x}=tg <u:m&5_tbm%IJeVj~XY=ޑIgp>JqNTf8 _ \ 8I:ysvs(~{>8\nSGF|Tz˶g (oJ*244,(~20  ~hxDV(J{z/\t7t8k& %IfsYA Dy)|CSg)ٮnА(T~K h*vNoߕ%wwͿkl߽{oX,ϓf{寞`™]{z+dA#PTaXT?h, 7H/]hIŋssc+CcCfS׹n Ö.Yb4Ԛ&kfP`uGc1a~Ky+L&ӄ\P53٬U#$ǥiFK@E. ,Yzz9Kg28 B'Bd2rz= Je2VgcxMF^_,x\$dx Ţ8.IȰN0z%'LNEcVU$a08#Ij`80W TE1 8ry$qT(iDb`M3r\*p$?3 ( d* $YO ygT: esQq4M+rj?C3^0܊>d|e3%~{yϷi6pFV8{\w{udщ`(wƆ'Oii>u޾+3?`HA#>3N/}!JYx}KȻIDAT, !FƂ(rz3]]gvMK5nq3gqZmllil\Zɫ{Gy^EQ&_: YVb(-+Pu,L&mV``Y6&Sid0pZ6B$f7<' qh4*+2a;*-J'p$ɤ3u:-Si0dEsD"JdH4qva 0)sxbn&^ʤK~x"9Lz=VB]4 PeXnW8(h2rt&Ds|4gFф"0 v{4Kg2IL$766Y}~ s\"T 0d˪JQ.iinZ`X09mM =`S(LNh@T?׮]^a l֑Q !8fbX(ҙ$KqX(]z&Qf(`qgQbY& uL74P**IhS BH EQQ,PQh4Ժ4MCx">mmm%~z]DS0R=-0:V1 Pd%RwMM21 04 TM ` Ti* "˲Zw>B EI%P(4M,ˢ($I`bp$9͹|ե,vl6exdtxP_:NPe=z/^{ڼwnR^_gQܺo=^ERfBe$IBϟ劢hY RЈj|EQ۶8AQOm^}=Or:'dC{9tz N&= \;ɯN}hx$14m0pk8wM%4E{jkәt,Wb6KtBHQIeMԻP -&) C.khx \Ng6 G" kjjziO^ Mӊ,_ fi҂`1BZa/4Si2/^ahZf :B50I8Zb{sw$!hVy>V}jС#EYkNu^g慗l65?ޖYm_zbvKgЎ*jTK\.?35:q\Q pO`&0t۔g^mk,MM%ٮթk,TgTs<rMMM|CC͍j,RSvR֮dgR)Z (?a5EiN)!EXGGoo0+?0on(=0y: SNSY<}?r9K%zY=7Arū_F8*6bNLObynjDeu7iy xT\Sb5E~dGV}6S4,oX4ICfV 3UV\]: U{vf )hDS3_ztACu\;uaYj=Y4'$IUudYg$Iq+ESȲ\yHA#Uz0\b1GF`F74((h(?:FDK/W@$IZfvDQJmJV˒5{BeEQWAZN0e54͔/*4I!@  Ł@ U RQ @ U RQ @ UetIENDB`gammaray-2.11.3/docs/manual/images/gammaray-qtcreator-connect.png000066400000000000000000000312571413310443000250040ustar00rootroot00000000000000PNG  IHDR#eE pHYs+ IDATxyxU/9Uko$,!ٕUEQQٜ33sg{yΌS:0( $['w-Q!"t 8Byx4]]]u:_ú(4E!,  `Qh~Z_b& 7cxyEǟرsWKk׾P(FnC97UUws5HD==x<߽عBttt~+JЪnݶmj'|{B;Ye龛;pL׻lny5hGlonnoEQu%j}M+.mii=t7{$';[ADИlbqHaaMma_d[JvvVKkkѐ%#wZ~f?onYƦ_ #GɲrӍ}Ʀu^)7Ms_I_}CNnNUTRUI-^p8{߽ {zzsKGrZm`ۑr[_'w̘>m)^ݝ-[rZZ*utv<[]S[dqqqKGG}G}'Nܵs~RRr{GȒK/ BoN]]ϗe{;!-7O2y$].kB?Ѷv˕3vmg0w!ZZ[Ǎ?iA~h/gLCcSk[C;:;ukƆW_iO}'M\y11i/{h-o-.<[4pxԓ?;{ ֞LVVf4%"!D4 #h|чv!Dk[ТL|Ӷ?1DBֶo_S#??{.0̽.xlikmyYñk{eK;̙<9Uydyw~̌ѣʾ7gkol3z~lNN~x H$"H ?c?ھ#W?|ْ;#G̺}tߤ~O?8Y^Ѓ,y`(cnI~㏵w>r'_r?]Ղ|{В$i뺢DiZsKqcE)1O>!"YG*$)33rsrbPU峃[TUD"X$a'ܯͷ<]Q5kי˅Gm~[_Ɣ7ɲܷ g#n|}e߱cHuѾґ$edG.?JOO#_Qe))ɑHk-nHD^7Keff$'''''x{GǓxPH5FDDXO#]x(z]Yyt_Z8$IU斖ʲ,rGgG hii{J9pȱǏ7 "竮8a|UuM23 391"bO[~YѨfKOUT4qB,e9 !c1YÑ,OWTV7w^Yf</=,Dp'hhlKcyd6N[$HxRRHƺ$Ƙ;g#Kt]7tφ Jz{z==ֶ6éȲ$pnegeN:u%xa̦ "]';cUkֽ役s4-֞8YoOs9D$|iU5&NPU54a=IIFv1u\%8SSw1Ï>ٲ}v}O7I\ZrKn;׿b1sgߵh=6uۇÆx{ή/$idɈ[_ߴY{VrYkZ_||?^s8(=7th'%9ŋ6ͷ6\9T% s?ɲ<}ԤQfSS= ;[?Ц(cQiR$}/qq>;^s^0nM^JI?0ƦMϥ-aC ]׊,\0Ǐ/,D@XBM C0F,3t] 3fv5M3Iu!\ Ek0t]'"ι,˽if5nj. 1.R_nnVaܚpsaKd\V6 0 I="bY;:;^|oO?Dvv¬ }msh躮(y($I q#`vvj9$l¦*=BYs7 85U3/$I2w o@_"{om)˖,F|atuu.qrU44E`QhB@XTq_ s9 .d-6G^x xuف%@0XYyq8/ĉgL[p,˭mxךىH,zzzHnkǍ)2$))G@ 0a8Q^q*<f3#CA]!P(d. "b(:}f'"UU;(##-w̺-++s~iEUVjys¸Dw3%Çۯc-7GH$zή BZו(ʃ,6uW]?\F@k5HDUչ:wQEQMrr0 &_gƎwwo~ĉ|Mom~M|gL'P(X[Wlb09:nyrrrYiI4ndWRSuu#l608QlNcTY陪jf,;}$Imu ?~옩S'+2lY9Y0[b.<﮾i -ܻ\#IƦ&:K9zTYũʾ6޻ //7'GkvuuӢl6"ohu=33"/\x ]TܾkO~>ڱk!qޗ>0Y$ΧN,KMQ~i}Ix1: wtvU&I EQƍFqcnBܵgQ^'hlnY2"A;%>rςyӧMq:Dc~l麮s㑇O|ӎt]//?` ͭkhni9n<싩g3N3c醡jib(ݗ6`'pೃ2ü9|[ov+O/#n|ysggf YYi˟ґ%̘5^֛g |;{Й]ֶ6_ZZyyEaa1·orb#! B6"{$I9TrrgJGF#Q"*?UYT\t"?/O3U9cspn.<}hH<ڽg߁Ͼa74_tP(2q8Ŧ޻#%%Et'ŗ#"eF,;M'v;͹ډHUU3%m6NgJr )wG)/r˕GDݾtTee}}#mݶd%#dkpE\yPj@ F_YvOKG8o'q@gge\W ñpnf~[oȱc_"<ƚgtN8_܂w/grƍ#qC`~:o-7HOOѾ3L ;vVeyu23~'pOt.?|?&%%M?3Y\TTYy& ͘>uGT456P@`2#˵d"]7/S% qƾ#ECP`EY~/wp6x+sЀiB%YHUUi$)~H*KRd}:;;].繣I̫JĹi"xчrcyYHU5I}OSSKd.4MuΘ(W|l\gϙӖfH$#l>ںz"r8ADin{zqjlODCRSS͢PggWODY.ӜzG޾cT=v-'X,Fgk6EDux/ꚤ${+8'}P8/yEmEzIt8~o{m r@s1, !+, y/qa{`rP&+| 0gt`b#ht}#y$!E!,  `Q B\!rWTyS&nTi2gLnw UCvU`PT]ՌJۀGq1pY#`p  w Yd$(4E!,  `QhP@λ߰Ѽym~|%[_H4z`(DDal߱𑣗|TCCc$~ RsaT>sQFwTU![ZE.7 l z<N#/7WewwKs$Iy.37f4sgEnBe2]]VEsrG8nhl)))+W8aܨҼ\d2`A~^ySQX^^Qy-7HO'OV$%k_(#l6[4M{[A߿7NG8ʜ3έ>liiMMMz=# kljJK|yXeUU?ݿ_d"bgffBTUU|))α޳`u-Pc=k֭\ _Ax׊,D?@{G_Wux,.2ƴT.Iyw̺}n82s~ˌ_ݘCDpKG$"t:{RRͦȊbz(y-\qZrp~u}ͺ D vfs\x[xς^w1`]^\fff0z]푇),[Evv/O>l$K~LuJr g{¸ ;v8 ؔN 0ԃ),HNN6S70O~iq5|>9V^nNZZ⹳\n} =l= $Ԡ.YJF WTnNǓR4dX+C˗)rbܿ^=oMomnmm;. ēֆ#O4  ]x<p8ce¤ T,  `QhB@Xfj _ͦEP9`)BUrwoSy3~`QhB@XЌ(윒dr '/.A e: hJtįw.Ӓ!F-Sb+8YSD88eؙ?~ )fpG-FO-<_?Nn~D afiG%"*pqiwp*MSXLƹݖ-UADhx +K᪠N#=$3Fe@F'"JɜHQ "Cq"3" f) 8‰1(fPUKIe) d43K 暳%[9כbOJV!,vACY3FFU@ Āz9N@ʉerJ|Af-SL9k)2(STD$1J)I&89%J)Y!iF4+GNhJ3W2̈$rHK"E6N '0J-K]ҷ-+$Z^,(pDnRsrJ(Y##!8)JqCxzX+G(8厘hRet#eE{_2Dɇ d6;G c~#nPBu!QwY:$rHbCؐ$6, OI,2,Rm,|v`:,,r\lޗQfffYҌi/-Fz]9Ұd jM,iL*TkMҤ$ t-,}vvolo4J1[Ze'x]WH.R}\٭*(T"-]їJDcS ^Pa`xܠn&5\s˴ZӲ"yi.QqED7xykTE%[}cc sI'.'ݜE4A[ޛΌh~Y)Ai"ո4Έ>4wMZTкA1\cDFn}i4""FN ֬QB2$gDp6|.2j].ImQ!1V\b,ц-ڿi3ZD'!#Fm7>ڸTNdFS3՘Nˋ<S8bU泳T6cSyL.eFD1zdNn3iWGCy وΈƦrUO#R؝9RUPKVv _ :mDڰUIDAT+4"yi>$< ̒>4Nt9Y҆j}kND>;sJ2Q^vK'N-/CHn#n6& &1Əx5zo:$v,h9ҬŲ؇-z[TQ "! l'QX "lD0""1W{TB0"ĉ(T AQrJ_< ȐF2;s>6|QR+I!Ut Xڥzzfg/UzEGL$uDDΘ1_. X*B "NHuRׅίڙqF1\2#TEfۣB3hx q9ʖƧ1#"]PuP8e;)уŲNNMOQXLn\tg䳳RqgDax .l#Ba%D aAD3gFT\sVJp+Uz'MF=4Cli*9+ڐ>_89%ZX 2#BdnB涨0. ݜLD ""!zfK8cDքQ(vQf }2$j6fk]H?n1dFDzWkе 5d%?*1*p$%1LN "*p3r]wuu#&  V%ąfOcv2Zig?.z4\@5{nEN4$ tV^#'};Lx.fXP?Y|tmmݩh4Q:$%e(^d,_?anw(ZDHi}57DQy+V657_cID5u6GЃD8t;[{܅}˻{z^^I]0/-_~w~ʗλw^nxG@=~aӧNini}l6RoZ̻ۛ{9؉{?wV;~]ÙHn^8m9Y}=9)dEENVm3oq vF3OֶcFO8 _kDWh45 a !|K/61[-ټ$'v'VT2ڹkϡ#Gok:z̏coN,c*+[GcO7&';[$&qc -^ac,@`9lV]_w|ii_{C7wݱwj/] ߨo0 CG-.."]MuU\NySTU5#LUU$ /{8%9 櫚wp,l}F.׹ 55uuMD1vmdhikBI'd;OD>,>slc?-@@@Bezrtݳ0u:sgٱz|5Yozs^V:Ϙ6mWL4܇LrʗW\I%#F9{~wssguÉH\$@K?I$%$r5itoj"[S&TVV*Irכgۇ jVV,i)sZ"hCnwFzg?߹kG.{ƍΑ%#"h$;yCE%#|iyyDd3333Ӌ Ţfef,dgg%%%efgef^؞nYRRR. :;;I̬ibANUs+/7"H0Ns/LZ[Wg/ %H@9 IK5nm5"%4$hHAEǯY zpw@A| E[t0` 0qaG0 1d_+p>wY?tC'"ER)6iuo0] |a@ `2.c{<^EQ {zz Ķ s8^fKhµ8,  `QhB@XgkmIENDB`gammaray-2.11.3/docs/manual/images/gammaray-qtcreator.png000066400000000000000000002526501413310443000233570ustar00rootroot00000000000000PNG  IHDRXL*ګ pHYs+ IDATxwp#YzȄ , ޓU,wj7N[ͬFnunCĮb/"'Ev5i3MufU,z$$G¤?"Y,V7Y4?̗}e"|/?-..#IȃKq2<98Ctb<$I$ihhh6gAB! 4n_YYrr|7I T+++z` v)B$o/Ӆ5J+`9U<\.ASSAaLFnɄ`q:jwt|>Swygm722"\j*r~ɓ'< DX t:-<O> hks$Il^__jlW( p8<0r}>z766_ɤL&r9u:t<7L===rA=5I n6=55AP6zVd x<DBJxgJSjRcT1 MCCCCs T1AAycG?d0[6I Ν; }"PP8$\plRqwypHx<.t0 ?yrʦs#HPy;ryW}}}ۣ.&C*+&''++yu?aL&srr2J2?`X`…o߁ pdaq&Q(`ZvS%Ary]]/^q7͍Ur#KATWleimMHyJӃIEU*Ֆ$NJk0p8BEo|G%%%ba/v7Fq\l6{]'Xl.NgL&%I`4秦kjjwA \PSS4 <_PMOOn:w׿O?L&3D"ad3X, pGG{' Nj:u z{{) |>cyZuᑃ?g?Y{{| ?WrR1A@3`X}E&1P(UQ L&S)U$"Y@gfg|E-,'UQKyy%9hYhhhhh;h|>/XlV8ٸ_*ZJGFMM6(:NC T8zS mbRABCCCCsɬLZ[[ f4u:]558A 5ZVmll= G4ZMUeVH&S9?wP(LNM556h'C9ʷ9$`U] TVVVVVJWꪾfk`l}D` ,D"777uuw[et:#HJ}gql:ɠ/VBqzg3`Z3FÑ`ii)8\9yÝS*kk @PrTzbr왎ٹyO+!$IOLp8<4_ihhh^*Z[gK$ B+% ܮT(|UGP(K$ʊJM&jV4444/-$ aA|E6ZAFD2M$A_ mMCCCC+HnH^-Mv>rb`hn#HrffvfvjSC@leFCCCCKKd*?( ]c0 /̬D"d`M%%tlmgd2W$IFDvM&7y=klh$AhMHpNzrZ6clWUPp8u#gYYÙNN:\|>H2LRs܎Sh 鍅'@KsӦNdţs 'OX]vd"$Ʉad2xO~_O$;O1 1|m}AT΢hAʊ@0UmQ>bc`i~~A.BH(o;:3;/)YX\lX,t5ccp$p8D(ڕ;HKs`-~4{X޽3O1NjxWɤZ0ϧinTWu>tӍ%E"~vvdЕNGMr 3B$S)AQP+I /]T(++v"E"P, `0D$îԨn¹/U As%:N͢YkYY0Lӵի>B&ʊu?lX. ,d2gfgcX,^^v\&|>AQFcT*N[̦ťba  LF#U3rGFJeE9 Ô~#IG  ǰ@ Q R$iK6eX &sY$8++,q ArtBɄ&SI 7R8Y4f  kkk*jzfn #>`(VBlgv宬 zB~ӧڜQAǩ|h=ˆǽ}]_R3ښjk,x<Ekk>0TV|w<^+l,շT,fT $@0nCM%/PRuD]7n^sU>pQ+ѱꪪFy9ZV6uzfak4qf$( ;nx_W{.]xpRܾv ſ$ɵJEWW}B"Q>p8|> (Ѩ£:y<=3o2Owj_ X$YJ-! `a6aX: |~ueh4NMMRAb L6 v\E=NUi6F$I^Ie0W{+K6?CWٹY\BqF ` |5ߚZ3q<ȤRV꓈ũTJB!<D`04b{ԥ"Bf+~iiIl.W]]J%34 @4x >b0lmj]&nT)w򳤡9f@Y.jý~pĀ Ê9 6 j# k-a0 `v444v2U{ߜ77o}kJKsu88N46wxJaF칳++#0 B-Q$Ih43 ́@$fX+X}R$fz}"unKsīW$AD<6>R$oy}HҙL__'S)$ l6+b8xx|K0 3'#LLL>І4DouR*Dd*SQRI$e-:r:&"Q{lV f3D"IRLJA$0LAјH$İ}!ECC 8J-$Iee%a,P(X,j'qD=: fJ-T&AѬѠO$kinedkϣU\0 [_Gbѹ\V8s96MX,jnlt\S3%Z8}*͖ɌB!;=s:NgЌ (xIhI.6y0Z'r\5 å" v_~?(R<{ djpxpbJŃ֖yXmMlksɴlz%%S5U/L*`1;te2SYQ^_W;p &2+ !:{IG_m--'T(vL0jlp } ½}Mń:k+ V43;bC{ CfC9TÑgq8k=55-c;=FxyVv=VO㨍 $)a<+͖H$Paf=O~|!H"{$I*'$* ZgI&Bࠥ8$):lζ)0Lѻ<ּOUeee2f74x=.'&&&W aet:{y)7AKGsHY][v?r|alN&x<@ϴ.`$IrHC$x<`0 F"QBt ]Nרom`0N;.?-sw A#InE1@EGzm1'ux2z!y I/]v΀ch\[[STkh&T*|'w[kss -M#NJId(nlhjlX\\J3-͍C#v5OBdtu67ufY |/as  BD*r( Sm'D"T0Ks g`hX.ɉɩ7߸~JPtr!B)\cpAښN7~zիnVRji;]xv֎T#Pw;A<^AzWFpdkˁIy c¢l$ɉ)dzy {>r"ɜOU>% f-?L&̜NP*Y =(Fc1‚\&S*/۬ew?+* a, qkY__g2h@ٳTrbXVki&a!ǻbЗ0sL%S6+xf$(8l6Nv?::6׿#EGvܼ}b6ex!Gr gϜ?~^'-++zW`(DGpn/+N p:gϜ#cL&3 T*Fj01Ο;sP2* y:ڹu@,55֏8]E/*\c\.n޾{[zW껺UWUZZinnxC{qfs0 mJf-KFNVD dr}o4y<ީ=0Pӽup8P( Ref|J_X^X\DXTJ%N%S t \xY,hn:y9>04>j;ѪѨ|N/7&Q /--wx׿xٹw/9}i@Ƞ(ǛzԊ׾gǔ/xܭwaҾōXq@}!Fc=n?\pp6Qh6B]ٹ0rrplAvYW.]شSQ4AHS3x`475pLDDl;8Ay<$=O"FpoL۷d2B+H|a1Ht8\lQԾJ%& Mf686}ix@D*ng*0_N C! 0 0|WaD3h0klv{bp$aT"9L1G|>'PX.._ $P04 vk0])`.[RRɝf`Bb1iqt[Y0ǙLfax~_|@Q4Ţh,f1</AHaXd}rAORϟ .Q,3zf[84Ϗg2P!uhhh:VBlݹG7n~7 peE9 ۳KtCv5OÑ3u{y) Q0*[Cl2b6:6QC0ըU*j6RP($Y_W;=3{$`l6D˰'gNwV X( <^;Ӈ]= t$awO,_X\dʗ2K{{{۩S' F3㯾֒7NgD"\&ˠZ::>m!'&qרճ'ZY,b2u5ѱuBcC}@fпԞ=}QqC/ A80 պ=w ? N65OLNJJtU+c9vRE 8Σ>LzVVW%b1 &1_I&S#֗BEQ'ooL&3UUU}8A,b5"D10_~X,~;:K-H4D"ܸc0ښom=r8l\NRL8CGX+ Oh5On6MB`br5:d2**v4ܒ]xv{ul6;454Q C:K%@ pR}q +t:=6>195o޾R)NT(^ -MsUX5/,->4_@=U Z B^sc3 5 c~~^R(`0v^]]bl6$Igb8dG}۟xM$\AJlk9*r鲘MřPSc=erM!in2 h6x ,@!g2&g $IA@ HَݏzKtD"y}`pE*l.Y[M8N R lh0stlWIk_ya*W]9}rAYrT'N^*J6+ ++f$IٵA'I,jd{ ٴl)R"Iҷۍ-)ѝ:F^N2teeEQ+!\N o-FİéT*n|1d0gjuP`0 `xX"߸y[Q?YZv0ljj:O,-/ d2]Y(߼}: xQ\r;tCL&$X[[* SӃC#\.WPߚZJ$ uu}RdR^f 5KBѠר8'5&J0 O6kY$fsrb6eYFBSv˩+h4gf;ۗ8m me jm0|)z{zT[YYYըU ðl.w9 KJJ(d2Dr9m{6 9[MEѧ΀>*3'<AK R*kkk  Q;#(V?6 ߈?;>4<219e0bQUEOyfn7/,.I%P(<15e4L* 0I x&404 b6G@/]8WYQX;rQ׸!RwU 2Ge2DcC=҅Կϛn!JWڵ+[٬֭;7l6K55Ulmd2Cb-MW6+Zށ*`Z|`0$aRA$Ij4L&Sk42P jfN%<.4j̬\&d0̔d24 xsg:|5*@d2  0 OLNY&z>7Lֲҁ!p`C 8 V44byՠ~(`!ڸ`0(ܝ!I2ΰX t]Zvo|O ` iS1< p{ J>i~m=JM7&f0W^_Xd2M bo6n]; 껻l˷O䃆fF|!f0L2Lsy/M, fW^_XC`j`pػqV 4j|>_D?{0 o|Dfy+-MqZu<1͢6|f]]]MYAtXJe9c6T$P 0 sl&) s˅  {*D2TÑ]=ť]=UB!Oܽ9<0=}ŵl6[nL84 8WT)s ZB2\[[bt"M@G6a;w?ګ i^$;g7$ty[~{rj:_(ܾsotlh4 AC#(IK= Z6vjd+)d*EmP9 C(f2]LFjrkk?ؔpZ%owc?`u=|azfVP,/!b8_pPR8\&!OX JPHRhT!m$HlWV}b$XEo'oxeuU,WVScT*ANwGtFLJ]x|>Lf٧0 Okx}Oqߚ`P_I7i4jJ>;7ٓ;|VyɄ;'?XRP(hjLZ]UF>V  GVk!yl$lLYf|!v't:m4TJK^<ʛRiە +ZZceO6a@r]ijGQZV H(J$:ӯjd^ 0d]qʕ?? EP(L- vfhl79mц}bZ=kkGHT|2? gCfKn%amm]R2LE3T.IÒlLW#jܮT*n/h`@8m>`=hWW`n,b"}"KV6\.zcdغ8dP@D823۽bnnb*Z__㋋HdaH$­;\cDLtiyîkf5A~44Ǐ΀L N~[+0f2뚜tB>hJ%T<˖&@,Eᑪ޾E ;CIoݸyîr>oY'&Nٸ>(@ zmmLG{0Z54E b1 V,p(47Zv>Q\; $I&} ]Sۯ/A ;.$vd2jXޮEa=}/K+: ׿,[MsىO~u47}T:ڵ+~ "b|ah$L*r. iG1ZRfONN3(dr8cZ 'Z0 ijej,fɤ s9Q$l,zWNhYv{ݞy?{X̦kf(0 />P,_tO-Ը֨7 aeC N?X?84Lӫ/[Rtztl|zfׯ߼u; EcƆz? 1P}]݈9;7wo+/S6W$Ir` UJMA`B$~jk_yo@*|skX|R ;'zlHb-{Y 4cA?tlC}" m'[ŢB=TUVTUV|TQ\;I&S"(.=BafvNP=x3?|PY~x"J!_H$XΠXd2}5b!ID-'Ĕhp8]Μf uvKyG>͞:JO`t2J$_%do}[2eee4A0xjkX,D.E"6/  zDBͺv$8|:b6T fl՗_f:N?ꞚyXc}TS*{z,fsFf{ APp}6t+Տӑ;>{hߩ8SJp_ZZRT(2%@wOo1"\,//OF4R7{.K-)QA]/axSY4xt￐Pg$Irb_ri4tB BXR4&I2 — @~7ACCCCK)=gѹyA`0d4ɤ" BElVT#+ Gj At:8 b.+  zT <:wcշEu5]j1߹YfxVV ݳV* x<^.3ɠ(Ik~&i2,ɠ_! ^2}I6R `0p%\.ֆ ,N˗/OH?7 -hL љt/UJAIxxQk붱 nB=^fi[n@082p9Ϟy2 A 0Pknjy`MɠSMS^'bsQ---~_WR" S(HuE"rͷA  HPˤ?L& 3!xh4* gX<.Jod2aAڷO]zE9L;7E10 ~˖榥eFZv{X,VmM5:19Un 8-MBaaqi狄#Lno;98< A/}|\.W^nfs1V]i1tp 7_??ؙZ@îb}kk<.իWl=-͍uXL Jx?0Vkl|ff7߼y[T476;\.7T ֖ٹ?l]XX"b_1&V>onjJrzzl6Zr)%:&i%:Fm16H$2 Zh#iKtZl0|ĉW.^n-9p;c'|HL' B(Id"L > }Y%{}|z3C]g6նm4T*8+J Ahd0f-/jZәlit||l|BeA0 b< QuVV UJ%p?w&H^רNu8G.Je1Nbl8ڏ=&fEB@ d2, $`(ɄB` j}ݣ~nE W*H%0c_d2^o ~RŹ\P(TꉉMXΜ9C~f>l6rP8B{ vt:m/1vFXC>ϒH"][Y CLڿ,X)\7*;VB!` QwI5U@h4|*p.qَ~ZM$AJ}k4͝j;abQ(t$xm2Je#Re"o` 56X=E|~UEy:X,PXSS?k y>רxC>m^@ssk}9yRALEΆ Tl}C{nJ3,L2D2in[ycU@jo~ V\6 b؆R[&dL& 'cQ#ua@G=&,JmT:C6,V7kFeUt p8ljj^nOD6kP hin|h9{A7>ߚN5mX Mw?s2I$l}!" Tg+T+[7O^ .zX,Kjk jinBQb'~52C20^i IDATSS0as t@8A<0Ef&NV1;PjK2wqV)aˆRS#Hus`}pACOz^r>5>AсJ Xls$I\XVM!'4sYJ%_áy&2lP`Nۯ-" /;W'>Yr9e#,U! |>J!O.w8NeCLF$1=y 2?nWor4 U^ZNul:RfXT*g7Y'NUTfK}8jFd˫Fh6TbOKCCCs˙k&'?=qZ  ۬exA*ZV:6>QQnrs XK$l`pX׹ަ0d$Iy%YyqV]޷{]GwL<VZS$Q0ig(y puUe.M CՕ{ze O~T]UJәtott֭[sss;~q`h$"p$ep(b-l IT"Χ6aKHKîT:]C|<:;Lq42Ј004 Ov0A*2HdgLX,ٹ9T*]cu(~zNyԧPȳdz۬ZFR<3 |~eeH3P]XXaq@=qnܼ 0mmirE1Dl.VVW{zuZMks5KS!FJo߽7;7h|=N8v.Bth 5U?O,7߾iUeD0~Zljlz47.=j2r9R5ըTsSsinjp{ii],y`{a@P(bBA"Gc1:>P(vmǛ5:63;oܼd22hVMu?Neew}]퍛|o~a U K l.+ kk?GՕonUۇFTC8Ss"(f\n :?t$Ivwwk4>eVKoLFVUϻ?b6*+^f[* :JD"Ǔd,IDBW_~Ro߀ѠORW.}յ/@ p8H4 LaN A$߿r啲9' E< hcӖdK &NN7L4O@bqRew:_m]Z]#I2LRa;:;zv<} uV>;2: ٲX, L&#Ipv "X'8lvיH0DJFηnܼm6WD"!AUJP(p7~&m f2L$ZnܺAP 4t :,Ad2u-3fQ\X?jtloKⲒjsws`Mqs{< v}zkVZJ:T:rWWWU*`j---^W,TWyښj&)J:??2 H$jjl~y@F32:?0o$4:6'UuJQaX$0+R@ 0LWEwu'JexCjyj\qpxa;NZ-+++-/+F;B.7l6ZT"fGhG[mbIhql2zC!wT:ōm|>_*J$R3ɒɔL&r8@-M,+aZ$p8b1&#%fz]r/^8hې^4J|m}b6y}֖D"YRRDq} -l޼gp؏|BND*jin BĐI6xnPz耡K>ҩwfff,7WG!a(ytCp.m0I^wgHϒ%kпZO<6\.ZJkd][grQO $IR.@5ԉO<3;3NQgg;bnNStw6Nzsc΅{nuG(徴G/-)V(7ga@ |4I"?tr1_L&#Iq?˗/_4pz_phN*;;NZ#wr8ݳ !BKrDؗ9t?T^D2) ldrJFqŔ)= lnj ]q;8,D ̬he# y2mmlڒ֖] #ԇ7d"/IYǿ̺nܺc**!]Þ?O>El6k2VKKX</*ll@}}ͶA@ '9 \zEZL&?shjE‚5UUVuc1<2V'[wΧjp؜L&re%%L& áP0TX,n]Z.+2X}jzłuiE*LGKԲszx"I2|TŎ4C#A=W_QOHCX[Oz2#c~^{S.Kpάo y.|N$͡< FʤhsS㝻/p{%E*U8rZF8Q XX,`!,^G-_HR|m}?OxmO 304| L&C ;SOBub왎T*h|~?|t3@05=0ŤR_Ϗk[^Y}oޖ&&\S-$e2Y^z:loqLb9l?P[Sul&k1Lf:0y}yWyv4o?* {o=:`N Orp8\ [oM;_^/< ;VWZ 89wӟtfiC3bWA ++˙ ut.;?_CdY\;4Ud69UtJe";u/ён!|>I^b0 @'jW/~QKS0:7gf:oݹ}k)-,-I@@XYY3t`t2mD,.-)5tkWWWR)4At<7 3 r 3&Ҽ*k ;<+|49MsXGQT+JhEy %ś6$GB5lo_?͋lb>Ӷ-/+(ᑼ<ɴMgn~(z>RRTWZF"Riuw9:]ϧ{t<offd2q8\Vc1[\.`'%dR(|EOTaFԸ LM  ,,Z|q=%%ENɄ*F*d29heΟSfQTᦆyGn$Xfjh. A 纫** j(+M;<=|9OlQz;M)o}fxdtpWGv;y\n:fXWY^?0tY**L$ &o?j덆w}A!XfIpf$xr?zTs \d244444_f %>O?vKNW,#zf kjJ,fGUx\B!yn44444_ 'V6 z|2BXi.[YQ.4ueE֖C,cݱ' 18y$S)|`m-H 5AMH* |wU7.U_z4G\2g:(E2aMALʊܯmk}}k2Pw璞3{~uJq|tlڵ U_W+AD"QT;m[[~W.P8,~r쀹\NQa:eBR?|l^Z^(ӳm- #Ȥ~+îKo' g٬cd|bRPXE+APaBa60N(dXx".HwFC#u5S3 ucsjfrq؈?vT2 ߻P(ѱ ؐ8 j5xeuw߼'kjq/?_emljlrQmUSUF`rHқ_FdWcj3P_{`4<6Zq_\\TT8Wם.w}m-+ ֦1P(<=;k41 p8qBsd2dhmnpدQ/u*U"zNɤDyymcL&d4!w߹$IvvNM`0fxǎwjom'H" s8曷B4=3k0B3haa&@ L$`(N$Y\h7ʤ*J-/47L9b'V6|>K:hj .¹[wz}>B>8<&fRͶH$>պ\\dqj4hTV]ƅsw=@0H,XH4`0G2,B 0= ^E`8\UYT*`EToo;MFV;z]H424_0L!/,Z9l`Y,J{k3 CP ) QjoC xm}b ExВKd{jԣCnlorNVV($Ip8Q1 S)R$Oh|2a|K.>?-@X,T*a@w\,ѣGuuu$\nwWgd4HB&!N_Knzlq``W|w%8X[@X.sC#4A,崼E7.'Rx: r٪rjJ耟>&d2.{$YRRr񍋳33###}}ϟ {fz7y>Ah,((X,555dRRd0U*HXR\.jE"J|5Zfl|pwA3kovؘ~`0E2T(b8/(@ьui_ RZ?@dQ@&f@O+ ]a_ l64͍7H -w=IR Ah4&-kum=̻3NygwY |NW}#֔842:6A꣞>|Dstli{wBm+G&quidtރԚwVEсa@oZzۗL&76&gQO>%I2}]4u\WW P8|5~6όb~Oכ&fNOMݿYk}@qQa aX(oڶ*+WWBH[ks8dJbqqYk;|(hmscc0!******s~iIuim}ck'++ʍuiF*ť% i3C#%K++NrJ h5jN¢[z>B>84&SitsӖL&kkkBˍ[w ƣ"Ko`$N~l7hnwllخݸYh1\77? KCp8r}@>uNMϟNT8ۇ}d0'&'#^3t=}n{ xgyz5|:ςfX7`  b@++͍|*ym^.vQz{} ʠfRL6r00 L&@0H$'$IP\hW775 ]a IDATD"* 3FIDD<+VVVv[ >*5TT`8OH%:?umPǑ%V竜N癎;h,(a%%P$֖9u*HvwCҙ h( 8NN<33g0Qt+sl;%qiI 'k{W]Q_? : $I$,-VWr,(Mñ]UU AP:֨ձxrLLMW)p8*ZՌO<e2kov\ӥѨ  J67#@0fJKlX$ 8u,:;}>N J±qRJ0VPXdd2ah%/:L&bNmg"dPD\}XcEAUVX06ׇ۠a^hVWךX,X$V)9.SB><:R)ZG{d0%EkP((-)'NUne C*\ƆD"Qh1lX$x0}➿DQfy"(>gMMuvdR) PBa= ¹IL&3FhXq8A <ȲrQgB $I2h8R3PneGoXތɇ@X"fC!.;66v@U>MPȳ}(=$q8K YC7SZk hhhN-r+w9LIl B { s2fN=ʎL<ߴ. EQ*:;N@"p=$QV.{H.G1۝>nm  qwܩTZJv};ICCCC|^Zөnb1|g4dKoM/nܪ(MLE3x|^ ; (*<v#J"$Iq*d^ p@b8yjumrLY c$h4* T*/Orl\. H:! nZA<p8X\&J$Gw0϶xl6 W'^__@0Tdrhxtvv~~anw,/L:]յSpGR= A`@Dx\&tU* `8N   IWiu*Dc`0R% @ !{?$Ih(>%/8 /C(R07wj5jz%d+к\. OO;5LƖ/]`: CI(NMOTgٕUAdll{zC}s >xr]&7`6|>:A$AF!OX̦Mֹ]8WZ2bpl)*8].zl2HC䙹_ǫ;S48A\>nj' @43xaqed&ʠi 6v^X]Y[es.fEC+td2\{\Ї?I9S*'#(mN$XԠ?ǔr\.y% z~dd ee M(`H"oڶB0{GTA\d:"T46/=H "JD29?88p Aaan6 sw&b|՗Q7n Կju cbD< AE1ʀO*!+TBf+oJ?7e{?x+jk! St*|?t:)?T󻺺ph4\.pBX,֖݁(`0%IPt9a4z!H$bT*x0JR`ɄÑH$ #X*B80㶭-Rը֥Byuuzhd r.jh,/kkFF ͦR 1 |~ MˤR}AA"hbQ^LP@kL#HzHxT2jnw <R)%y^É’J(^8R=~%+~_Ljp0896s?BT"@$uvvnn`Zl[v `@NW`2Y,I,+U1.6m[.#a d2YR DQa]vnaQ$BdNQݲ;`,X۹$]8tp$鳖$rA(QT,v Ǩeb rr^4j@ "qEgYX,pL|&r9 |# acqAHtr9)_Tp8R<^/ y2Y$ըId,=.cPs@g xV/{{_#hX] NFs*L&aYG$tLd*f`3?%.]zX' ՓWZ/1  @QT"`%R TFh@'9(pX 3`($CT" ,'(pHq,O l$* H g2x19;7b?84<2FD0o P-zkC@uw$wvH&GG >xrK(r őѱ1YC7[uiaOo$%Ya?߀ ݱbo,c2‘ǃabd0/B| E D$v{R HAY HX*dY> ‰Dr}}pd2>OyW,/.l(/"hrjaG&ӕd*+ݡQ^O$nܺOwun?w:}9l}3K+L!I|ݾ{hvn{陙ƆjSLNˏ>iin6m[<.{}FD_47ٴJ^Y5Օ3s.G"V& bfnr;MEPwd7n &>vD( CMCC0::N>EV695I逽>_,x<>b0jkff疖W߸p9hlvZ'G6eX'!2R`O-AN#qJ%|> *xzzH0I<>ouuR.cAD"o]^Er+~n0вizv1Y,E"QiIq$c@Fclp8 w?axj,/&pB!ZY]C5% Cho;p$ɎցaFH$iTR feYbΎ;wON{ ֥By~zLk7`/rl7ƢQBa6g}"$ɶ[wZ#HFne8AP:`B~fv gbgG{:vɾZ=wL枊F2r8/r9$IAl0,Keee[[F?4_ H$M$;W<30[糟oO4ILɀ &# !,V,Hrk5j?d y$d3|>?AS4Áh,T(>ǧ2E LF(FcL$Ij!h70PWQX$"homY.u(/"_p\3yrk'G:8_TBa@L4 T @Q4͆aLxT2 #PX&A}E`(Lr@,Iab%I2 * rx\*p)66mz]"Ah&À8NP. 6M 3aE%(X,fO$q=m-7H.,Z++qX]] Kh&3n늋 KL&2 \ Qivա YGðQ K{zÑUNH&GF~Ȫ M!D$v{\. Ñy`P$0Le2a$D7<> @dcX|tlϟXvghojjlM("hC} JUWUѱqFZ^V*Jffj :;ٹOu޺sdY|> tw!I'^P_k2m 绱,VYQ6<:V\\K8 EN˺ΥFUo O#9izf6JE WWUdAP@>{o02$AKS {b=DQjp$`(808̂a67715yw~S"45\׫j6m[ t~v! SY ~?]xO9Jک| ?_Ws|H$OrϟuԒRT"J$y\3\MQQ!l+$:YUYd2߸pη.îW`2TΞx(^.XfiaJeZLd0tAP('HM!**|oNxn~`0D"qV݌iX,X '_d2sBOKs~9c:_Er@ X#u> 1 jHa4ZJ}f੥|jf >ɏfۛ/յ"A?H$"KxMVAV d(G{L #}-?`F0H E3h n.>hX,9Z'H ͑"7f#l6rymql6JdNhǍMx<^y9ihhhhh 3z]$FcnG&n;]l6?Xd1o:l6ihhhhhTox> ZG F0E"h4:15pn&?{t@ Ų/.LxW&U#-9I`(/o1' aCãL82:1:`p!SΖ.-{ښjD dMJ%Drvnpyg`I@ axbrdyDҶe0 FD7lx$AYp8;`ut<]Kr:_榆/nwrj-456CƆ5~/FL&C<.\~ҵ/n/RAeXTJ,칖ڴm]7%bqKs(x}Zffc0^O}R^Z򨯟$I6;]ؠ:ڢuZDJCCu-f6 p8-, x19%HTJݱDr‚#90lDc3 7Ds`g:++9vwjFMPu<r-`0*+JPy <K%.j@|x<@T(VVעں?(**z}84@K"v.-bյd* f" kM^D767&ui 3-ʁ7 E*9 x\7s- eqc;'?;MÒ$4 2a#5U_,i546HĶU|tÁ@bOF$A/k{8 TO'9򣖦FJ<15Cp]M[[asf۫++n޾ AP(TZ,B!h*x|N][0uϷ< [Lx"1>9e{vq2 P6U) u /tONMTW.-0, `s  JK>DceYh6Eғ _y緿p\8Mu6${Gq} $QE"ElYŖb9lY%'αqrs\'vl+ٲ) ,E]`l/efg%]A}3;;syR!J IIí囇Eh֕wfjUUEE2lfv6=4:6bجݞqI(XIqQ}C#DZ"<`5ۅ_WUn`ģPGE01aʥ B1 Ev{{c $xS/t(JHfffR=_Mwa̹yd lf<?E(ZVlC~)!~111~.CټxMֶWDQe٩I$2, ;hZt8dH$px_RH,!XX,HAF=H'`h:jY1 cM''KGnNvNv0{MѴJŹ99E^nܒ933Vvxd<+1+[Z^~/K$7<b`7deel'2%E"(%9 [QI7 L&gLUeL&7^H&}ra۷W56d2DrmZ&''Ƿnv{CVfƲ+,cf`pxxP$9] WiHJ A48'2) ח Y332YIIQ @c+h{8т.A0E1q  E _xo 8===Ba@FzDO'utvȤp];ۯJx]?RӌmgN?nx]JK_RhCN~ao.).WUޥ'}ǣVP(*BQJb'N.{jݴapB.ְ"3so|On2?O!_y.WUTp^?;;;d^hx}&Vq`KfSq}uJsT* ~q0L4zCiZBDHSpMVԔda0$q yǛ]b1\IrN LZ'7odII斃hiiu{<1a `0|>a*tCbHK O:$!Ixf%Y;jnuI *Кe_B3XI qIQ|g% p:SSS>M zOND1&SQaB@m/p8onv lsRYYY\.!qH$MŇ>hdtLާV7o1=5pDttp29eyzjj긲az*^0lkk~}ff uuGF-K(d޳3.bfjl麚V:_N$˫+qb=0}w;wv1k8u!f8]XRC3chRcSs+˲{z/\wo o)zzN:sjԀ0 (I͘FZX,1p8rjە b|R}gט@?F)Ą o70|t0?pj$[ ]Ǚswlv-hu.6M~?UUl;G&'_jj0ĩ( rʖ]m~橮=XM^OS4I,UUlHO'Irgm ;:6Vt%]+73,˰B '%&ӱzcw>z 7=8@"s ^~P+۶af|bbk쵐s(r@M^w@+BTc7Tҕ"A!K_{l,yo83Mz RTTXBEPR\r-#=}phpP%Krs) 5UiiՖtkjگPU6Htߴq)0lUeB! ^^UrZ}Y: Gah4ʸ*%> ШoBWi#,(q~@$"$a$}pp[y7fyc7Mbvo̦Ňs?*ļ|ɜ@ U$#bMLh4j;J%p::B;"HфB!Ejլ݁ahJrQ:@ FMۦSSR‘H^/=!KA4zKiuqXr0uP!=x!N]ot2z`L?}VKX]$8. 0vC.y}> Z-MXx^r, ɩ)</˲(ED2dJZ'e2)cQ`?g 8 B>[RB [!`Nq ð,0 \`Yc09_tKs0$Q! W,E'^4R>`v; !!IzuU%am"hMk¯|1AXyM,45Q5zӃEV Ǫ++l3[eR) ~q3p86_,z>V{z,yJK'>ÌLF5d;].R tN7IE5̌T" MN"G "a<$iċa{SBaX,qH DQa M"7Cx|  s-5FxV^dPky 8;;{tttgKLCO،S#22LiFcCc--)c'N8T*8۽kJj033{y^غr(pTl [mR>ʢ54䕎O do_^+=w3zR׭̴wt[?OR**[.dٌq +nlnxO}L'%-p8z4yqԋ r\Vr \Sf,@n6#/ Py&^ ly yES9۶ͷ{橮PaA>˲;*(cEݞO?ypp\@ 3#JP8㸅֯w(?", b^u| x`( 8A1 +{ʭ[zsZG^T@#/ )< b, =נ*R8h*33# V)IETorvo-444\]U !l$czЁNsb}Ű斪 a]ڂETWU4^74VUlZYK™J08 vB.lP8&W>t$-¾&ZNd̳/FhB5+u,ߍss,^8.xWׁ(r:5~;¯O?tIIB;'JŚ}sZE3CQ4MSk~1ou[+J FD1lnSD"YA8ّh4j,G媜 O9*cd 0|. (=Rᵠ M@ \"Sal"k/|ĬTˮ?`M%@(Ǯ$YF{w ,~hHE2,99YVX9.C̐w BV+JDqM$I(^(D"E ƈAJyו 73dbHF#Cbq^ 阆NC3H 5; =vVQohY9==FV(##c7olkf4ڷoR\#xDQ۽H%/ $%%EMN*`0$%%d2Ijκ\. (ǭ aA1988a@̓q́#AD4 146Ua^j|HaJL&j5NTWUvtt:$D$I2 D>xgfxJNN[]`4 RD"SN IDAT/_l>cygi`X,H$b[㢅rƌiBhZk0/pZs79u7a!@A< 0۬270 gv8e$'|QTrErϓ1Rf4Y7_f}5:T*:. ҂P~n1}]nF^K?08vᖊ/t:z}M,LZn7 í٥p\A,ň fȝ $BdAH.BabH$ áPHl^S0ߴ/=qp=$)J$=Ls$+cɣ'gkrKHQXDX SHyY b2+U0Y梛ة-$V`QQ(4D( U2̔)T1{E$\T -$ Á-e,Al^g* )me/ eYh]h(0*55U. -g0LPLH$rʕ/ $9/ŒaϗnZmֶqFJDb ǮP( tuk^-搝}' @Ybf7a0%'!L KX>$akV`5)eB%YK3RE~f6DbI%$%  Mb@<,Vp8] J$dLz(Fh4YYY={vbb4<>7oٹ~)#={O<F P-7ՌDEr$&'' aVUii+Wx#Gdgg;NK >O,'%%B7%b W19^ڢy NҐ `/ߞ9DX` c[R}GIbAy6Lb&"f`8Qa/ɛӌ0,Ct2$y7&~JRKT*333 裏].0f A򎎎?~\әLׯٳĉ2, \.455ElfYvllL"x^'ّQt;s---,ˎ$fۿwwD"T*eYw-((D*jxx'ONN$%%effBBćc&0 Bu 3^ ceZ-K@k#[XCPැj% l-Oo_iI1\h?Ǒ$H`-_z^P5x,,HҊR'|R__S>/jnn>p#G`DUOO0.\`Y`̫ͮ7tff)|ׯ_,aÆ?RU$8G?O7X?00FӴfyyyAcjD(B!ax-y^w?4׀یPҮHb^:aXcS= \̌>ٔp8u:-M`6=]w15%RSsmv7>>Q^M,[7Da|>2lW$ X,d\a:h46669sIEʖǏs~3O8\v o:yS28D277 9ydggs|tvvBIX}uA͛(aTXXV].O\ VkwlN M\&j4t}Y׿yӆ޾~F=,܁׺ ZZ$d4W>qg]Bŭ`˼L&޶mя~{n}1 ۵kqbwg?0 $ݫW"Uy4fdZ$k ?kq]]]Gnٲtmٲ}EQ[[ˇ\4=::zYd0RiLpĝ[ep-j•%%%"hӦMz>55uƍ}^p7kǬotc18kln˳vb`وD"'?۷yiRc30];.eY Izz8?WkdrVQ\.S5jL&iJr`p0`٦RROMIK֯ r{4Xx<] 6{`{]N< ^r_RP0a'^c|GOvA8}4˲;w}۷8K.KR CL w"2l_233VZu%F7$9Y&'m7d#"J;]~EDRXr }fh/**\E\N6[.8p:M^^Ȩ0<8k( HRpD^e;BӴ|_,޹un'5vŁb(B8 2Q r9^/c2kar;?ɔV]]%K<} --dYr-` \[+nRiFFh<|CChCb|||߾}Gٻwo[[`(qD"O`═*zV -E 3WjB>58"2>> :;;_9rvvp8GzzZFP$`4L#UEyfk*++aWB-,,X,.h4 p˯ܓ!$JAVkU6ןaѨ58+hܴaFJJ%z\,++Mɥ-&&&2^OKKs\4r2M֩M%ɩtSZ 0RS0M6$8PTXPY}SRSˡq?Zfzf6+##%% `0T]U p)^Og3>>r⡲(  % xEWPHRhFp0\Cٳg~?Hzy{챩)uї^z7հ"02#Zhnn???PQ^vV~饗ʾoUX8V03 1iB<`~y!d_ ( 333\ҢP(X o۶LP8q?o6X "ïJ}WȉAl۶m!>N@䷾-XbxfEc2b斯,=/ry5 $})fƗb3 W:ex`0|~gf I(nJl6ÉaV"n0EZ,@,ξAkа\.h4uuuH$))ijjJ&}_8~8deqTJQueF~鲲}eddkJ&{%0 vJI鶩Ϟ=aXGGGMMMQQl|ܤ_~DaÆԯ21>O&''p~KK MIII$hs{ogrrՂmz}Yu KKOoy`8Ƀ Ϝ=oNk9{C dņ!~W:v_uMu9wĩ#cM-Bc'NY,L{\>_wt~r<13W#j NV4lZ4$\.S cc"HT٦gp%PV+p w:]b8 =ɾ`i\/0seYJP( D"qkjjR~ԩS&IZ#X,D"\YYwwh|w,˾}$ /֭[~qq1M)ّm޳gfOv]XXop|/555;:tH",n6`Y`0|>oDQT$BH$޾~z^k q)Q/"9Kjy&⢏ϞY&TÌ+۶O٦5jUffƹulw.|'?Q{g_`poo@?>s./7zex$[lӟ|C5$Iz^ΞϲLQaay@VMVliAU?xvz֕:NXk{46HjWw֭ے'N"x7ڦ%bI0 "XFFV em+)<;k?qtiI@d =}{{tup)p$`A W((F)E"@x,xA:/@.KҶ$ , ˲7[oMMMP.Ae!KA&`Y68N|d)))d(xYmmmmib[ ^UuSSS~f$IjZvv{ կ~U__8p4UZZh o-\{ ,\Iܵ㑳벳2N0^0YjPcYܣv8uZ*:AqQL\JF+B./[W:::f0$0 2Q (AQa"qg޹9Y4E k 8S4MQ!hn])I^O {zd`H^]YE23"n[63 PMuU8^IZRUBk ',1gG\?Drڵ4A#644oŋbh4*JC\.F @*0 H$aEQ@f8Tp8bB389vܞkSt:r8v޽{vvvxxxH$)h4 Z׀z%Z2H)וD"cjJ[{Gjj\.W۶n޾lʿRxtγ/]P/YWZ(\&b㏇Ԕp8 @Q!%ِ+黵!TzGwY,#7n~֯_wl]ioyՖz~$-pvorW-#`ea}ooozz:q2lbbgtѣG322ooܸAary d8C7 d21J2==8pð֎W_}U,p8̗ĊθKtA4Ϳۿ<O0*n4R4MONNٳ'PP(@4(((P(0 8Fٍ1- %׀ Ъ5$vehp;33ѱk׮~FDPD"AY&Q+x#Pu8mhh Ir||\"9s&TW&Nf'nz>^FЦvgffNjş| ={zG^^A;wta^F0/`}TWWAxK,T DIJȗTMMk b޿wfRD+M…]A$eXٍuM2 jjJ~zAAf;z>[__qP(H KD0`0>f/qaqqȻ12J$˲KmZVp ,YIx+ [,< ^|y$IFFF`hނW]xܡ8x𐨄򀝎qPمe| r,8.V IDATa&drrgtn8W,A$4RP R4K$AOp4.\i BfYtNTOOϥKvܙ H8Z0LhN]R8sHTJ  iy/iL$;;;y8; 2;Zr9%/H{_׿.J\3_j_!FQ ⱅB KWǿ|__^|W^yE7xW^y7s999Y\\D_| _K1 wqkiw4M_X_a׺]t8nٮ 8) 'zO/r]&nkf._;k/0A0("I]x<$ }/~QRRwBo(|**55uttѣzOӗ_~C̻Zk&BPB&,ݱ#׿җZN )z뭷ۇ?77will].˲^6&otHeGc k~-~7}%%.֫T>;K~ee˖{̻^^?я6nܸ{n~na1K P`@"/1q-f2pp$33Cש xe @ agwe~Oh+v\f۸ql>|Covݺu,)R*(~BAQԣ>{NR,+J1 sg<E?i 8}7Aw0ի?ZZZ`H$r-`Oyfsmm-˲G5L,ZVV5#f%-O 5;4Ae2X\Z\4<2V^۶ Xhmkok_d<1'&֕vONN [7)r3Mm홙(+J$#cUs꒒))pB!/>nkf)PX,|bа)-MTRKxJ$xqtčY/ɔJ(d2Y,g?K.{M{530~_.d@ N4Drܿ?;v8|D" * 㰠Ks@/6 3rB2338~ / ?f@kk+0;wTT}( C` e`)QK v<H(=f=ٟ0 ^UlE99( _!ﻰ"[HO7L~p15n .`32iF#@KiGb"e^ W0,333:δ)e?xw}777AX0TJ:Pf iRr$ӧOgddٳcppĉO?tgg4g C&+ E\fJKOOBPMMMSSS0|ꩧ9266V__SO544ܲEQvɓ0Z  Ňb `ۂbIII{BD }P(BIbXtK7770UUd744n8b̓-=p8,h}P( WQpP9ن \6j>: ,ɮ]!'|r``rsso4}aeU*U8|z*D`H$0 j܅U-i?6 |0M ZVP@bis"(aL8]i> 99???g}w^X|)˲Ar|J2kwL&HJKJFF79vb-r dgi[6_nizR9>a-[W 76^EDe!K~AmM5fddf{k[L*7WWUdPT %u-=EUmmW:_ro{)jbZT$yd0'=100$IP8\XP`w8V]Fv=ϲmֿ jp~{O"k`m Bܹ766R0LRRlp[VVDa3܍FRq΄м ;}ax+V|GV Rx4 ?ig7*4 [Gm(`G}wzz:55&X#jy-?@)/К_|~Y'gNd,w9AQmz&+3cW::sss2MK?*HJaEkY7eE$d4PNWBGX,HR)Ͼ @V Av(~Sz.]%;d2ZO{<K/$944tAEQ`[d/E iVJB+2p5`.qR 3Bekiiiw~uuu?$Ivwwwww+_  esϟ>xΝ;a'Y킷a%,!D"m/@ֻܱ'- `]fٸ_~5`Zey.))afrrX`hLIIk*i4H$򕯜>}VX?~a$% q|+^|U4E ï222T*U<;J9Z`zU*L&|ˋnmmV .|ljjrÖ:'I2;;j4 ;3KƼm,ס2x UOB"ZV9 ۷/mzD[?Vap(Bj|/PyG,h4FEG(k1$ Az$~2C喖######iiigΜFn[,+W***֭[7<< D"e%~1m hn nH (ՄFH$bX,K搕醇aV%ȑ#NqL*JP4ryGGLJ~SahA$IPE< 4 `Yh4Fh_b[) >i!⽧ Q Bcw/S-]Нwo755a syyp88Y,mwr eCsC0D`.^0Lk.\I=_708gl{.6 iWxS MNMPC-?,`n?rD]}"Fo B, a事 vc*"]|d~4]]]V{zz|>e2l6$ {zzrrr. ALFQDF}P3NMM|vby`!'yx.^]՛ Z- EBb `;v8g;>s'ܜrLnܜɌ m #/[Rյ/ I mϣ~ZoS hӜ2l[CЭ4=Ν[jѣGawvvn޼999FUU8Ji<PMOOú{v:k[[O74ZkNvEQ CQ#G xYFzz`/g8(.Zcxz?$]p {?޿m(n\_{aE [( !5%%33'OE v\6 ^¥}+a;a֭]sIa/"p$" XQ^㺮S$gqW`װ_,a`0 ә!S>}@ 5%W_}_zC?c*I$IB3e@ ta|`i;@Ou-^jOUV_~Gy۷{~_Fz?8--mp$MHS 30-`*bŊ+V8Ⲳl?+},n\`+%99'g EQjm6ܷi{anIqצ$˲-]dtl >\z@R$/(hZUUUR  `.JEyYOOo/'%%9==mm,s}68n壣c-m4MɲܷqkM6 ^UQ P482:*XzuXH$"_q}׋byEgwwJrr~jֻ{zr}9$I[$$IV qhL. I2ϟ 3O<%٩)). v:8<333wEE9VWWo“aPp84Mկ~EQ 7!If2kFQ$ À՞͜g`10 KNN6SMZ$XZ4|jANz?yL~?,h[χa/''%% r ;{"EMQyy;YGVfffF=$Ɋr֞:ݐ =IIjFuUƳ `ۗǭo4E[Z[/=XAJK۠ujE[Z2330-aT) ^2x ua ׮=uqyyki8{MYVJKQ$(sWXP@QdA~c]5իGGdE1//gMSΌ(}%$A"pfF(6ڶmO?3uIrM {Z999CCCa|xO>c }E(*I4mVAJu$Iǐ}x#BQZ<=Pw+geYf\ 0X膞qʷF>aYa?ZZZ?>::v1 1 vCaÔv5fk~iȑ#pp|>X,99ҥKEEEa[љυ'N^]NH𼍦a~|6%kE(ZWZqy~7X;8n7 M ._q%QUUÇ_q֌bߪ@ʩLeD{!In}kƤ\h$IrExҥKp 0J5իp$,;QNɾ'irwz-Y9uܟS4 Wefb߿qmmmW\lfA͆aJ^F㻮kraR8HhZX0 -c0 %ؐe$2`hM LP%|;<+Wx`FYIʛ$7cXk@ƍRȬ,˚V˲[n%ٳp%a':7^Uq H$JQ$Z0 TaQXr0 s G"%ũ))G1Z w@jSS/'gጌ{0 ##G''{76O}+𾝀ִT؍y,G0vmKXN/\]WSwHSi~Θ_ IDAT2s,4alnn q߸qna߾}pa#0i,C%IuA$ `Ih>"+V5l 26 éAI7:'$i $a a鑚m6 :::`(SS1L 8555 `P {CUUJp8?is7֮;vCALNr{v>?J$D G۷mO0 3=ܽ{]\TszJ>=q =v=ϟ{ݸ~PLSq۴afz=m084 |³ق|.`IEK$۸^[4[7$I*ˉ㪪i_r^s/ hή.( +ʺ{z>BYQ P5M4QXyY l8>H>STeӛekp{3kQVR ?$esܲ¥EV)L҂|E=^S׬UVR NGNv6Aʌڟ9VWU64;xz??ͪ $, Y t:ω/=$kpL<0LE3R/I56avoC*JL)2m_0uݐ'yF,&2$]7ƛ$q~5^F4WD#oO^?Yc},0 ;Z8=t]?u$INtNlZN ukrs/g/ӨjEYWd/^v;\6o`n㿉devaP33#c23?__MXiff Yd] Ə^=aի+J1 rF[<}'$MT^!^o'jaTxttr' ?񏳲4Mhjj:x`{{;lgA?Bav5[/&,Sm>bp Vℤ)Lpă k: `QϜኊ5kT__oq ~ܹr?r:w߲eˊ4MKh)}Bx&C\r{%9KzfAw,& @4,/^\Ez꩔A֬YSWWw̙' u]7JfNAԗ~W|.h0JfϞi7=w>T|aʕ+`3GCC  /]q}WtM[^Q> p)?/bOo-%%%wt\<)Ƴ|@~~^FzځC+K/b@c'0 []OxfǾ<]+̙YK npaח:Of@֜#kDXj5TTTdee%^s%4Oy èefA`w̓@ L&&IV1$`EWΜYS D$C ~WΝUð3GRXb{w_T*;ي[!`a_$ر㣏>Zzk /$YSSsԩ 8tRŲ'|p}}`\UukPw6m8{uzqÁa )ɾ%};zwsDqѲD"100 .0EQYŋz>9FÏZo>  (TU=Twx6f;z0Vihl<{ny-Á`Rd{Cۇi EQbű'J8;}1pq.33pƹxpdԓDtSS騪\%a_`@ ?F׮]{qa\_wwMQupYY qy~H.CR5&W^=rHSSU3J$0,ʲ(˗cٳgΝ;w%T)4|Iڤ $Il6ǰЯO>Ɩ(JzlC^:.x%7iɬO'gUx/..޹s'Gy駯^:00o?p&|0H$/ a;<KvW\1)08۴a=ò^}6 dEQUAݾ4??Yx=(%͛6Hd+W,_wb~պwvcE qV,f0(,<@4Q5MiWd04Ms\6xc]]kV/:K m64̙aƼ >ymVp[[MӪrrRS4L!*WZ|9I$JAw}_~eHrPdn >d0`׺\e61~öuѧ𿾵QP %ľ}0,. u^I$C"1Jet3&6v 3\__o2oϝ;wܹ)/ M,qĉH$b=dSƭƴ@ p_as1 $_׷p)/ΆxY$˧޿9 RYZ\484ٹvM5<)<+7wb~$IX`U҂|A h{G維ck55#288 BiqqW5[&qkVW .^vݝd>cuƳN7w5:f`kڵ5x@Q򊲞>?[Wr0gcY5M*+-! " |VV( `KK[VFklW`` (..~`'AͲ,MӡPFQa {6=&KY0 IҺqk- xb7uqFrw[k4!֫<)P6ZeQ–w" < p򠽵7 Þ0siKdWG0˳Uz5550 iOӓ'OL4c>@`kz)VzKܳ~ L\dn M_ÜU4M~ ,5M[CV#6nNd׭M'A͕bϙ ϰovk$+K*6=Po0"*#Et-^$bAab%  I"@Bw_n6M=#5;9! Lu 0n={;NFFn0ldd=3…i\b0fF, œpz`9Y(|fM!KuTE^e.{DJDq>mX>l ӷ38qbEFM_51tq)An~dI? C橍k2Oo*rĺGe!)1l!jv(ZCڝb"}DJtAVl qQtŖ~ŖP<.8&noUkslUWnJ^uiMfaNQwJNْowF{ 3R[q۞ zrDQ l՛qQfEGXlzBz}T (eFz;kvS>U?蚇yn|kv\Y#tZ~ф`t2O؎o$Pm6zno,`3-׫n$sxGD^bG ,gc?߽k4c<2UP $YT,a+NtEQBL=x$UZ.&͂ޱ7zYHykuB1B񻫘 Bn4$P xb G(. e3$\H8Ik. (Ik|5tGH-Z钀n/X5$P1]6J<̤ |*k^9K :JMT"7"HF8 (]L +$:. ` u#m@*N3qƯpfh0֛ܿecڮ65򜑖d"HZ.S.Ik#x'ɳ'M/4vw8U0랏ME10+ H" hpQA UFPA %DENE!UD"\`T8U .qC) 1d @ 08q`>8fx.] 4Q0$& 0TQaW!a g@5O?\]-+a5 '3G1~eA! ^obk#kcG]A1r= \4槢:EN^ "~Q#hpq1Qǁa2:\N(fLֆa$b5-;z@4=30 cx80MܱPH%M@uQ8W' MQwּ,ǎc(v XIqQZ@%N:x582rXJJ4᭘60LL737_X_PݑbUQgװ![5B"$ nq5 #ID0MY@WƔc&efa`sy%3%|,03DlqO? ^{eoTP'S'\  CM GQ0(\3/f xưꀝ.guU }pr?yƖֶ֔dښ5mm˺l0W<2:=(j%I.3;δԔsyy,5$'-+1 ?YQVg·|Ïx1%%9#=K$ee]xm{>v:Vwtih\uUYq ǹ<ݑzAK r{  >=4Ed"h۷mq|Op ?oea&K/iix<8‘ vu,(Ak)gF׭?vuu4ŮKK?z\5o݅v{2>Z- kꚚJc t8 I^zEQ$I$I$I0L=S40;WfQhue4w1Fw'l^đ㞁^AExJ #ݪ|̕uXIߖ,% 2ay2p8rss/_^]]]UUh"0 #''gf{ꀝG?KNcuC+Ww@^]522:aY<d;iy~ڴT^\a ØWkh9c,˲(bgd}6   `躮jʕ]]ݹd_Β۶8*y_by(nkls6nlEe^*رcwacr]\WT fbe4֮9q63GzzZZZa;~0q^¥?y4$'G"婰8*Ϟ;}石+xuOSTyY)my>n@EyI%A׮AQ%3#t2AJ"EYr<'}p=A֯;XrEQ6L٭[6dYc(/+E$?ϟ HyYi<yÃF7:rcUmhw<^ v$ыxˎEŽ|  G-LzBrQCL \ŧqtgwC L]JE A+W];::`Pc.1LO·HL<՝ˁ˧N7TUrFD(R5QNP882Rz AjVf`! k?n[Y͚h4cɒ%YPP6â͊iv'b1&G!K+ IJJr.rqn1EQdHq83eJS{\MXs+._|k_s\|͛yFw9[Yjٲefy)3# XxXIh,h 7hhhhhhdP B&kךa"Xӽ۶mvdM!-W CQ?2^ˤ86Vu66؝P4a |`qucsa_zz455:~լ,8a8pG?;c0b1kiv0-V k X\a6Dc}_ݾ};;iv\ff<0)N!H(3 5 CѐKt/C/\ jS/ C''l2IyQO)0|ر,͛7q G Ǜ/_ NG5cfN k|=+h<{LC#\'C,L,˟߳>_i$9KJJ ú3 Pi(LGG4ͪM2Ӭ0GT9#FQ3}:HO:r3/_m&/H$l60 Ta==jkkSRR?i^~;wVTTaXfff0x,FIIc=vڵI 硛(9c}NNp())N$ .$h$ɜPrՊAJKZo]q} 5]w3333[ZXw7]Ȳr`a'N4_f=~byE9%NiG)j} ~nWw*W-;reK\FԕMwzV@s3 0 k_2;;[Φ&eYUUp|饗-[TUU۲, ]vҰ Ќr^EQxo`l]R/O?6x^QdMSs5a_3~_?u3% n ~ݻwoٲeddd˖-կ~aw󝴴^z n }>_jj$I:r`3`fA<_TQ·` xh+WR\ۻzGx𳈔\_+xMČʃueYVu>{jՊTZCӴ1X@ExAD,#(JQT^nZV{{ϗCX(T^Z-װzSM>(+q<˲ 0D"A˲ ׂ0 C$pieYEQdYdhbFQD$ _,#n_r,5,lAfy<˙ HyY)4]Z srWVZrݻ7X]Sg^x9}可nCCRr\_ W~[l큭 hScg &ZldujEEohh I0D"RUR,MCCCUUm6$I_i(JExxP$m6(ؚf/ŰA_׿ˊ<v'xR$^3MO?>])$)n/Jii-|;eI<ϧaÛ)lZH$288xx UUeYy0yX2fAh4 eU*brc2Yu.AuU\\lƥK0o' v9ش|Mz1 [xɓ'dYFgVNj {>:5`4eee<l6[JJJRR$ICCC###8{^I,˰ڔ(<; =L~v{++mu9u+<{q+-'NPuH$2 HsԋZ`pyy}_r5h4v@ XT qQC i)y~fEv;<Ϸ=ͯߍ@CDb҂#NO}uG2ϡZOo_Oo/c"-;ryqN7xh{izC#G 3 sm6p=H,[Zp5M_vm>Lj ,>tiq#INNѮp8f͚G}Sv{FFA`phh0L+YYY p(REә]r@WwǢak>1eoke gzg> |Z*0dYW?~;Ԕs~qxE.]t:;:m6ayy\_A~$܅ϵf'SXrW͛6(pSSR۴Gg]RIN?$sH4ZQ^?lRaxt^Ĺ2h49QdY0TH4*2vtvC 198qwy8 $IZ50li!<ϧwtt@ijj:uTyy__644Ǒ$za`0a|H$"톔H$<L[ /wϻ%I(r\ljax#I ̈́;a 1 ﻝ{(*##ڵk$LxFiϸ Eך32aoҒH$WXP8@NΒֶvX^thhvZA*i.-),#IJr!ښ5]+W,0 {h6_AE[\u^x9}可maCÁ2 "~`zךm4}Ax`tt'쀔}ܹ7Ŏ__x׿"5:NO4ep8bclX$Ap"P A|KAvLvؗ$IY_}U֭[qF_{+WPW_7Y .?}1 $ʁݹ b8w XDhHIIhhkkoopcc|.ݴPfj0-//}+**|p8,HNNVUuxxð5kִ笩񯩩Y?p#A$8 S6$I >acp8°l<i[[mL< %xޟ?v\t"{s}*SUšb@gn*X0M#xhhHCovgggWW}o|&٫rv;>/>^[UIBZZ av(k(2 DG,cX&%l yhc8dةR/ah F"HK, GEQ}eYN"sr8;zh,s݉Dbddvpp㱨D3q,rg1(iIá㥲}c˚H~ё̲3gx={|+_)--mll,kaEUVV655p8 MCm픎kxr`ķ 3tBh+JKP8]<77'e.`8+39a|~_mi׳(3SԴԌ4NN udfftvw;q:w`ݚQϗβ qkT놱$;{Adfd_x)#====F~ou*;˦=w›,nliq[W&KjJJFƽ_n^bc&;"; aj]_"㖖/M0|jpfLӴfclm۶Q6s:۶mcY ü⋺a 1MMM!0 ; n;8N]DX<Ʋv.`Kp  Agp#Y~H oH @',7]]31IHS6B]\\D\Hј6][m$›DZdއWY}@$Ҽ2o<A" ]RJ\& Fmm?_gϞ{.Qc/"H.px~͛9y`YVT"r. ]XSSN ꒊ884TRقT @7" 5>~cΝ;0L.\ɟOC`XZZyfeمѨT*}>^WT@fQ0b 5k ͪ2%-IAdadV"\.^!lł g.s'^߂#ܹ?ONF (ܯ!}74dGh "i(@QvηWt"Q(2/h$Ir}$y,"| YYY}э7R~K.}ᇑHq0V 4MWWW5554MB!ݞL&A07 IDATͫUUUx tMa*Ej41 ÔJe1 Z Rz{ޣw?οF?1 +}$!WFFF& ?^{4%IVUR!P v?I>!UX,`^9G(b?;NXh4BH$4 E  J3rPcR^h >G0@'O|G,VUU+Sl޲x­[3~׆WUs%AH$2>>~Y%b<_XX7˅az=EQ:d2-..0v= T*^Z]]L&JBMA4М(˲* 惄t($$s_ޕ )ЀUUU**ͮ{%fw]F:8tFl%I {rEGӝn DMToOҒ ]PuX,KMFi3D8a4u;% @StXN.Te O 'p8e8 ө_;>W$@%8L$i4%޽{{<p,LFI$\d2 Y555(TUUDG2yjA "p~JYQJ%0jiJŲ, )ys«+湹Πrɰ$ xiiik  5z+@2XsǏɐ^0[bGיLYV] T*ǵ?4SAfg MNM+op`d[w'u/S"p_oRL$`'[MNh4ޮc IPiz( Bu Q88`h4Z AI/c{zzP(EQH$MP~' q\,s8Ta@ 4˲z>[,h4jXb>2 <a\Eq_X Vr,5(Z`(v*fbFr0a/[^{Vʖ2======55UPd^lu 3@QB(McccٳH!L8n25*eMEUWWB!NfPZ,d23Vf( `;KPR&_'vu{aWZYR/9-#Z?r`_pZ㖥Q1';TR5 ʕNo޼oONNt(ERIfN3ɐ$t:WWWJF B555he ƿ EWWW$)۸y;=RpYZp/ vKg2R1l AH2[n(Rb90 ۯϟp[p V3/:7>1_>͕'cEhaqK# E+׮4?͝fXHWCϝDl&haFFF~ ~WWL+333?WZ-e:\|ʊVj:`aa{կ$,I(^/"IP(pD"Eu:](S7١ѨlN$Po0$IiKF<^nooORo\PYgen߾8X<)W3Hrs.!y¥,/~]D_~u\.???8D B37Pe5zm;x7t隚ꚚjWVnS'?KݵoqwCG>RbVUpA $ K\aYv_gj/xD׾Phv$绿寗W.jZVSϾ8~Ex 4>8F/ET['v8~\ ^$wg)rzNȩTG%4`ㆇϝ;J%}>q刽 T E#F9rH~#LJ_jlVA%*C0hL$VYM(%%zOƧ~+KMR1#95m60ԑ(,{&b8drxx(O^e;|]끇T^%UT߻7bZlVZѶ/f?vn2i6]=r=ɔdlGn(C^yO^Μ>jNNN?mok]X\!T*zp\U*ss'U 'h0ZbjB8HTaؑf|'O72rW޽9$HTչz:j% @jG8O>#M <$/7\.q\ApQ>w$V@W M^wfffrrR(JV aTbd2EZm4FAlJJ AZĒP/,cq555:yXo0x͇~J =zqx#|g޹whpm;5ţXsSc FTkAN`V\X#>CbMNNs7;; Kn8l"P*J2Jf93hVq8N-K" (hq Y齦WWWTb黆y4g9gQh48É7|!p%d ?~klfLhJ_P+ґnЛ]!`AVVV\.*8v8g, (J`vt: _,A04YRb peYbXEKrRjO[V H,65k`=[,.W%Bp֭h`:nr###& ]bm\20oX|>J LaV 0 ,.PB^? ˍvh0I' lPm|>Rޝ;wJjjjzBp\.DFFEJ[zj+藤q`|hn#㵵RHH&8NR%,#r8' iaYڟDgiyiM+^4TGw9[`pi ya1$Ą(>oSu`hf:x7}Q>¥,]rO`9܅+Ogf߿_V.M76Uwa~_y;.\\^)| ÑVv]Ba4FC&Y%Qk[p=00x .( \ݾ `8 U$IB ՙL[AY+ eR8ZDėCk?|X[n }$ xuuuk8`p4 #\mokr=#<Wbد?^ɡW/ka1`(B߸0l,hoy~v~sr}@0 E47L&s.\޷ewrC y֝X<{~T*O8vV ZZ;L+WrZwgnWm l5ݣ\J4MGQ_QeYRT2chmӽpVJ4_}a 7?*iie#RT*U$!kHʊdIr&,\v}o[hrd6cXZ[G`]q695dl-JWЁC}Kƿ|ZJ{VWY*FEQ|so߀AoX G"bRM(2 'ròh6|~Yin~ Thnjr:]AUTd xXRR 5I$Q 2P(hO YQУl6 ax8,1q0% /TT*ۈ͹@.n#,zBaTPT%_{kZh߻/[' Wܮp8rwN쑾d;rÇ #>lnkv~ı$Ic}|ZF<~֟c^3O!Gz12L(S_GѴh n4ݭHFV^8o*T+8:rqTj#%ߕ$EaX$ I-zR~0x>KJ$8ڟJTCaTz6t*rܰ(f66׻ݷhkpx#J,#qo DQf+W]ϼ}JR_XE ?ov e ?yd2PJnY2|8F6RSl-)l)>\Td]PײՐ|ql6× a[`os'Jcy*),}G Bo:_AR-rVqf&FxF慆nmֵEr9"K؝ 8c0ԯ$AQ&İ&pO 7A2AY#" 7_ݷ\;1Z,##Aᒛ `RHnxt!O#H˔cSyו Ҧ`3,jIJ$%1L40e53F0v4roltAH3Eնk݊m;7얖9T 㒑=ҜhQ}ِƠ{Neؒ.*̰ _J+-SZ0{CY22CEs]ޡU]/55 O6]t.DQ22<[o9}uSnP"6B9 d$ KHb8LBdp^WP&,i8$25˵sHTml(IF5dP>EXT)@%? yr7_˒|9*T*zKK{…+ik|p1׀83l[S|X_Y9`Wa* Qpiްƒ 7c DF h`YWj.ȓ(ZoQ(Je.8f*Xʟ-^S) X(jZ k+ŊoʒpaaH{l6+M|ȼ0 cdp8C)@DLZ-.ʨTm Q4MKE <[RmO}}cc|~\ZZ_[XX(^_\\\n{nn.{<-|\Gǰz}>4Β#/X#-v׼O>'0 t8^(dvH$,%W+Wm [:Y4u`Xl6[ss3EQ~f2}<_΂uvv|=AAG\.oSoε4𜸩zzzDQGQEюek ({{w?mC"ȳE(/R֩J;NU M0~\l6aN)\Fp477r<|Inͯۯ DB: 0R+Ti#ȼDnߺ~TQG<KQnր= ә i,7Phizˌ!_EQ /' ,--x$D"QUUf1Ll-8_iM,3L*$0mLFQ h(7E?OaJL(Bc2iKr x"! BoOJ ȲL:CRaX[K3>~

|->e)AG~iOW# r,|u(~UqJ]leu J‚Jx< :ܸq+KQZ-111U]]UWCjZVwo#**͞:yeCaAP(=r  pwμwμۯ<0<( h4DJ>ֽ#Ywށ`<sS07A_\VVVX(`0 ꪪL$ |~:@\MV&_~u.OL qwly< Ü;k_]Z չ>RžGO9 VKRi-A d4LFϟXny@[[+M_k;J&@p[FV X T*|PfC!Yp㫪l`\C*K~R_+ixAZ^c2/}BKV*ٲ 3NCEz3"c_,$ISt Z4?ӳk%ǼgffpsǏZsN⹉8 ENGat{ :V%z0 #$9<4Pt*bfXt:vJTW;v Þb(zO}MMRs윛[j Wm-AhRtkKR,6Yu:O}(&N0TuZ'}arW@KsS".I+DQEqν:U;2 UzJ8}zzkmihZ5n7!ljj>qTM&ZͲ@@ZŕKr[-VmXZm (D1+>6(ՍC?<֛Փ5H$?_tiSG,F?\>p]0\ J c#sCT Ր6B<26qȡ-HQ\ ǎVR_M F*w!SyW*qx~s,րAu4 EDlstFv˪8k^|nH]- 7kt" {auwͺW)}lގh4V;oN2mRIN[f^p-u^0uљ? <<>M!{ hͿk1 ' h4>\;pggx|X___.Z[[ZÇxoll԰wÑӾ ] XlBGLFG:&XDd&o!DfMh~f8vttq}T4Ln֭[Eqttbk%F+DLX,zn۷ihduuuDh4еpF*E,0`0 2DCh6{^ ĠKk1J c?~X6DW,looTW}>}4 4 @ nAg3DE0(JP.  >wS* 1Nеśa vL&s(333G) PA_3&5r}$H¼;77 ȭ[߇K/_x? KvAiYE性&''EQ .6EI<;;;;; wZ$9>>?txVVVZ[[ahӧO̫߬EY_ߓd4/" da~aee:|.v`[В٦;`%kHs]lAnwZU )wrYH0VD)#aX0g ?`dgɆׄ cP\W`XWóWoۘč7+8 (_ɂ22222o:jzL2x0-wPk4Vk6( Z;2ͦR``Xij2Jf+BGsTj%}5j۩,dp7Jrl|Swy{u5<7?' JM3v^(q\!JJ%^[kIRHxVҢTe.jhud"H$z{zffgZZZ^^u>~$_qU8?s杩ΎNU!Fnu=x`8 W_Wjw v9`VKQ`ylPJq]*cyȗ,H:Q r!;a UӍɯ&444o$3_0K_|۝KnNHHs CFFFFambIW,(voWnZ|_^RtPLKkKY(;M+̒]20h4۷og+#####Q6V6|&s0 qRP/׭ s111ŗTĔHLX9yg)d}[@Tn'O /{222220Ðg[:uTKKsGGो/^rEJ# paV|8[CH3\i)T:|4-8j 1? bVehhP*mZhV땖uv +:׫VabԹ%1 +sժ═Ύ=)7*^%ŰA^ntWwС8ï:;$JUѺX,U,ɺvvv M+##7˨[rn\ =l6[gg[&'' ʼyyѣpAhP]{e%@t$',f@ ,a`PӉFd*RTK8/..4=5=+(E#bhfl|(,R~fv>:s 6u#绸xm}Vy_gU 5 ˲lwww"x㹞T*y)nZ0 RԑÇ2CMFӻ~Rf+L;uuss= alV:|0avB̠|;^ΞsIu:} 1J6x&Sv}>vz?|P:^^^_=}E{ҙ;M%V% ڪlmZBյo`߿|!z{Pwot:ݡ0 zd6{aQgwwW2K/t:Рj{*. N~իWm6Ą)dX<:+RiJUeqKrY//{=19߅VKTWW\.@:)eٶ ,X帞nNwd/zp ^v]`pاNG~tt?J+q%b,/*8t:tf6p8Ncqi){8s\PdVE2n{\FaUuos/#>L(DvvnNF\.Wѣǵ(|nhhPAhOgf0 ː$˲}W]oin&mm𷀗;ส\YW_'/o| XTb(JQNM3qP=N8h4­, zBA3(d4 F!V++CaDݮZy\t:K>(trώZ kW\L$A!⪭hNMl[рQTRCю@ `2ɄAo8ahZ %h4IM&S4-Hu #ŰٜFc,ǪTX,H&9nnn^AE/]'R`MĒuq 5͵NG8Y uu.\Ĕ۠?y|p bLfl|a DQ CUUP(4h+ѣGjr G74x:@?Dzf3`VWez)2T6O$ 4MPTO$]YYN^Y44x<cјJ6xRFB`KK iii/"q@P*36>1h~~fP(%҉(~cر-ϒ`a_8]N& nkOgf>+ezܽҬ.^T!>N,##󲨼^cǎnN8yJZFFFFfԸuIENDB`gammaray-2.11.3/docs/manual/images/gammaray-signal-plotter.png000066400000000000000000001517101413310443000243120ustar00rootroot00000000000000PNG  IHDRXHMsBIT|d pHYs+ IDATxs\~w@7F91̓ZJR*_\ږeWqWNəa$r"rF蜓h`H 9W>(.w!BB!T0Lc]txOcee>o_~ !B6՟ɿ. ayَddaqux<Σ 0cc:K˨T*¡0}  uܻш`0h|f1u~?zZxh4`0xG"!Ft:gwhOea4_ FϿ TjXT*S3DQ2{"/_FEE9\j?@aaã1B![oץ%Z-:H$YY=J{VW(X;qB<- k{[wأG;aOwأq렧FC<Ȇjk㟙#b0QXPN8!zg19l-j\s.}=L:߸IkNrsI$ B&gNO6ee,$>^X1Lx>*+Q#7o$jj~l[S}]nߥbnݹhii z=ao/=eJ%uꘞ(JZ[[P*\uĹ"Ϳ)y:1{PBvv`K0f zK1oٟ=soLE}=o| &lBff0b( Lߜ99LOb+*$3#c,.-M-f:H4{22Q>/5ZbC;V]b BwL&2!BgR)@;v'X v? C --$M!xyH乏}a޷=x(ϧ v _RTdv7k!8Q3x 237!o>sم3<L&414<,]/HKsãt?'}N$pPd133KII1%E<^/ j|w V+=vTuB!F~N{jSGOL] QPPN#j1wq6kghj =T*((e.\3<:J,#H҈Fc$=}>0?!dz'-+W&H]n(D޾!1P_ BO-nQ*\vc -c;(+- JEiIqzRIff55j맾wQR\Œk5475JA~>rX\Z&j=q~~k|A=]BUe%3ssL&#KK[5GQLNMsȡWޛ,B-ԌRU[?ܜέg⭤8ޓS=]ON]YQ&$~ degX_(e22I&:lTj"6V W]ESS#kkk/,PZ\J;~JVc, 9u._%v4B 7l;4 qNvOWqKK;r;w!C#jղtb۽@&VˬJA~>.{W.hinq.(JVS --M,..a2KC}W.BY6o{Nޞ\VWWylk8]?օ@ŰL&z}(&P(F, E*v:*l oN/|{2jB1+456 ev{RSMLN zS7?νtw$ܽï}>_wWQXPjy,&'C3Z0޾~2LT@r2FƆ+!B4=T*"tR_,..v{0H&auuۍArF2 bq Ez~N'^x9l6N':aAa+,$ VQX-Shg*w1I{{+_}kk\rFej[{fvm-\mzwhP(x>멩`hd4D+L&VW׶S|W7loke|rյBCni!Bw-b߷qsr#Ohpcq~?vP(L}m-.RZ|t: t46t B,-%@TxQPphind2j'-q4Gt:;V8t`?uut;V!BM  7ګjYYY@-s8``;(-)fvn]ǙAN:A0q`>Ctwgm}X-';hop8ť%:VF0ܙS_籷CYϹ9ݵ׺7~:</ ^RI@oГ2VeK&9oeB{#ɵOR(ժ2LnP(F"defYD"|{|_T*dz$Ç)--ݵc,..QTd{bv:$H G !3py F>3HVD|}"IrC\n7M Vb!?J_N(~pVK<'LT(f+$hR_IFhlGHj:xN$B{AP`6z4 FXΨT*8x ϟO/{?ǕkYZ1ɹU.Wzǟ~VzfvnɩiOѨ_C13;GC}}F8zkkkʤp(LII13T[?Ko!幣8 ڵk t~b60==ǟq¡05U\nJKbj G"qfgә|=bdt|~?vJhٹ:|p$^Cqn7uDlbȷZqt:]z+8}jɩi~?s zV˖mBA?~D8Fӑ~Ntu܌qP^V]LMOS[SMgN 33,yy rAzKKtwCP2>1I[ks*'&y6Gn+WLvv6KKttF#B!BgϜBP}TVT?0HwqB0nZ[Q[(Uv>7ut'pPXX"++tt384gϜV H.v{hl ވY_[Bɹ Bt:(`xd"[!PVè*NAT*Z+w)j+ita4pܿH8Z& _gmm\7N"`eu|ff_X%B!~z?7gg$ b;hjlAO/m̢s>902: //W0Cb`vniN,dKy~&Ǐuڈ'p LSc=zzzefvT*ssn v 5U\v{t04<"Ao[a^\#ě7ow"F?yhHEJSGF;VhokERq ;s]jk mzPΞV H/\"L{gS#=^(-)FFY_w (VGPrizh$8F Dױ*S'D"|~,$Np8L(J's-^D"9`_}.D"A4/  K,/YY]oqgp.\v5a> > 0׮䋯fbrH4 @d#f1"xe|bH4'ߋx7η#x*BwS=n{v{a`p, 2xs\ vSVV,v D1;7Oqq#dee219űGȷZwc`._@rIZ-nn} ۽IA~>J?~w @C}Tɓݐ܈m|}iΝ=͕b`Z[x|W;sG 33:|>:.=7Þ^|>Z$I˯hoc}IYB!ĻfhmmG0S (UJ4 %%%Fш?৸|B:vc?P"EB~?j>(G`6簼lߏa`pGTWUtPh4T L lVXH `vnRE$erj:f= zz Fx>hDn|/Miij N}WϩB!-OkoltV%/L,I<O 8y\.75Ux^~㥪GtZ*+̤]@':|>?.h,$ j5x}ؿuC3ƯأqJ;Bɭw8z0f3oޢsNvS,PD<?@Iq;:X\Z`:=ssX;iu֊"dx -B!Oz%10. Nە=[b#B4Z~b`o J<EE.C{5k{46=DjSJJ^}܍v<_yyy{] k f>eHKxk095.cSGFǰ;VhomARq s=jx0 sgUC8K|Yܻ룾ҒbZ-Hӵk~n9Z(:*p8`H?/aOwhn |:eGtca:xрh0rm9"++㦶j%Q!x۽s e{:&L8s$7hkm'Lffho@ C2Ir]9u;:u ڂ/ϣh8v0F<~{H&!77JE" Lih0 18tD2F!Lc`N'F[avNqQ J%7o暹y6u98+؈ $ Ν9B$ Rd+HsG 8*Gc`0n5ytte6_gddL&9v ~ Jb~r{!Bj~ŗTV_Q[Sí;wr5mFFG #w tTVVp- z=6 Z<TWWJ?`ؒͅʧ׵rÇ%tH~;1hP7oš}.f++dddzwD={PS*h5,Ntu 'N\/^R J>=SVVʭw6 p`4;mpII YYY\nݻOue%uuLNNoeeu, j~!e)-)O$G$F!x/SW['wq&3ןԉnOҍ;m$;+x?YY\ (P8.I?wp8N#O0:6j¥o;s ׋h$mYW 0@jL^JɈh4ll`s]N¥o(/guu֖f?D1Pn{^L&}s[ } @!B>sdj㍿lN F10n' Ca.}{@ @cC;ܻ'I<>;TT319NjpNuwأq',-ٟZWVbV*rA1QQ^N4e||Gx HPPPأqZ[x<Ǐ2HKs#Sx<-Tk4UݿhB!x$c( QV+f:Zqݸ\.8{$X||z- !pIC^^^׳m<r磪w{Y,#$s !/ERcum \0h^2^)v]mf 4"BdL~A8 yJK_Hp8N^G]{Ab`B՟ɿ3p8h.ݿh2<:d8V&XV|^|9&p(;:p8Þ^ xK$rE͛'5֭;YJZC;44377O]m5Cx>VN`=HC HLOpi^×_}ӧRq-a4qt:22L  vhTJ:X][B!OSD%%=sҒZ-Z0L(J̷զnc66 ܽLjʦ[av~D"6[!PRZMffKKhii&PQQ&j5?HOo?EE6SӨUj"ha I$j?xHeEjacGh4Dc1TnX,zt:K˻{RBNQƯ{<~tW |SRRZv{pTD@@VJ]m ++q,<&&&euuM]]-v`Gxssܼuu.5YYYb NգjP*AlDQ픖tt/,barj_ۍh$uu5hqM{]BVST ,10 10B!˓V!p x}0:HQ.@9榧ضoB!^vc`ޮmW:xp8L__e;@j@GOo/\bqic_cϿst: z;u, Mz; O]Q,x82 2IF>iGcXZ^agk dX,(P^D4z<4z,,,c`38MD"J`0TFh,|ܝ:qRVV}mL̐dX^Yqc}BQ|q =?AAxM<1 o/ ‘JH$JE{q4pٌ\.u10 K"W(H&Dl6$I( |~?-;d,3CXfF#`x>\[n"zxrptH$imAmM T VCP`|rɕkh4\pP(h ͑Jtܹa bBr`{|  <yyܸu|N¹r$wuq}?_h,t<}I$L̲_˞@ j d2tZn~3oŝngn~GLNNcZysҙ |zIgw& L&+'DV!uut8PdY<o`^L&xpZ-=ܼu( pL6ǟ\!R]]i'<n.<T*P(i"(\ajjci#ɐds֖ lRTg``\q74 PUf\y|H4J$ jrL8AՒL&rܼ9u5i,bH4DyL&i"|AL&T*$S)t:-NHz]?s4L~>"J2:*&&&y Z-^VD"w6]y:ϝE&H&10%q,7o}gdTVl&H{u{10pJpTz#* \x r@;HO\CT%O4RD*fxt9<.]%={?B:n`yeNL~h0 I޵H4 3hnb8Wֿr3Jbh0irR[SJϧ  10M.t{mHgNSW[ˁv┊%J%jiaJ%FGP[S]X,,,PW[j,.-d2EO}:ȚG$eqyBlt2:+!Ӱ3>1R 1hRv=$!ɐe?@C}=dՂlP4  &&TWV*ij5r6D18~hVa[ژ!ͲZir\^Yn kbKW֐dCs~(t:255(rXh4J}}l3+475ưXr9glWVij05=C}]Gϲ̽n!D>^877OXv395jEReQ)@D&avn*4 d\>J"l675L0MBaB0vF(3IQk0M\q}m{q:/|>s\ښP(?CP+HLLLUS(z},/?9W?N.*JRT}hd%OHJ]H #  ޸;b`rgd)uvcWalgfvÇ1VVVZ-bq>x27+fC9&`40cq:=ٹ,e7LNMsSؗ?t: nO.&@0H:aJ`6k^?GJ":?0Ȏ&VVs.{q4׮ߠT,-AAw ^bwW{oӲx

]:f!IplłTSS]RVA#JPrf\<@R1@C}=tZ/=V0M<#ܽ׉`vzzH*qj,-PSSB.'J10uds9$$;hbZhnj$˱F,u5BgW7?џJ:Ĭ,,.RW[CPb)ϸQp8BX9I$M&2,sTWWQIRsuc8\ͺlSn6*IffH` fjzՊJT M9'b`Ap ̕k \v\ƹܸy l63o_Z.^8!ochhg淿VZ2Ib&_(pAf10  9 HĿoB8 }S4S3h&LH$DQXӶP(05=]; ֐Zq CՖgx:wL&_[꿲O+p^W]Uu ܽ׹֩.~e_.2>tO_w>WYS߼ߡMߟ.|u:Μyo{?V+UU-oG tmmˬ8sf[X,N^/d|<@byy,sm=v*]Y]ellŌB`vng!pS\~zj5Nr$Dy:l`hd;vLNM1HD(p I^$Sh:Hw}]?vRoM}}-^IOx6=VGptAA{b`M<84Rt  .]8daq J]>vlbH&nG|<`zv;yr-}Wk"ɶo\ub21v=s}vI&ϷJ?F}~v7a[k@./'l6+~>@D>_ Lbf OTӴ^P8L2D:CyZ[v?RđÇԉ8odGq|>V+~AKK p7oq{NJ%b?ǿ``p5{jr^ ?0χbass8w W|F/]T*͎&i)1 q58NH%S$ ~g?cIg2yr9Cxn&)dKE z=$DrbdNGe]JAA;݃NS*lL&uvQUB) <{gjy$\.GU*X][: Çd|'eCDz\p|@;ߒ'Y\\ϐdy\|~?LP|e:{8E(e_/t? d*/ \xw;(Ç z:JR*c $ ּ8CgW++OL22JE8\b_c4ػg7>:gfinjdl|.s?x[9/  |w<O8̸`0XXXDo#$fL|Ӓ$cccEV+p0P|) d@@ H2d*RD.a4E.RX[D4z  Szz)4ǃcTJ̦)d2* B$IH];yt:-x*R( E;:=|׮35=C>gum LRܴ  ?Ot#b z9u( æu4p*Hi#dI~!j\.R߮޾l&FP(r}?LH>bAסV8}-$ut: z'Fں (UJ:zfrFv;PGe I\x L&?\\.ٽk'\nS;< =f iun8~(bH$jazzϞݻ* #‘@$n߿)soE! ·8yyҙv7C^^;K\vQ(hj6ŕ$'!D f&|%I&Cfxak@./#|{de:ٹJJwAAxy/`n~Nn(ǻ qi>qJbeñG3=(fBΞj< dO^ tvNinjPN#L LMMr:6x<HLMOq,,.199\!S'P* ~-:  |{޸?.sm2,&sg d}bD:ıw?}JuUkk^PTO{*}\p(70H6,..c`u8tPݠm "UU.OGP( 353CSIGFQ( {~?r B(D A{šPK.t:]'6}^]]U}J%.G/,xJ$12RV$J IulmG j2IbbjU ]CÁF12ÇCF~GVA7w#0NIX^^) zr|B<^^P(XX\D&a1 G"$ l6D\6BQR)l6kk^fӦ/iVWpUؽ{T,h/g6hL6,׮Of2 #\8wLZ%Y *:{f?0Xq xrHHs<$!H\~vߣX,`X0 LFFμ} N$I8~RgGs+9  9I$RI T* GMu5ry 92IFU IX]]CӡT*G"ǧRBPtZA'L!IUUjyAAxy/J%)X,DX^YpG"@D"FƠ7?ԓNDnl6h4d2Jز%b`7–o&q c`/Bm͹qFZj1BFS}˖J%\NǦe7:-5tJET"S*8s t;B"Dq-K10›d ak@.X ̗;z_dGl|>\.C!n޼fT*WVyhلB`~nsg!ҾQLf7]ݽ%LRd2ɧW?J\t M44ԣH$cq|L'|RY6<<wO, B/^޿LgW}}l9q(;ڸz:7XA109| Pk@TR*YY]##x>.;ͮ˝ FFRFc\8wx,ί; Fm{楡deuKW|kFA=(~$IƸy&iDJ"RP(P~oJ RSSM=r9Pey"Munާ32JE6cxdRV$r9JIIX,"*RDu$IB!S*_te-;( g ym=  ?oGT*rJ]vU"U]dYj5lT:ͅsdRYC=Bh$p\|R,ʟ[ZvTnG*;pFO\.$ L6C0N ݳ\왷I&S4a40HRMcKEi w+ 10 B0Bݾj IDATMy$=9vv7CF dYWmRjerj J(bߖ\_me2Bw~_? :uN&ejzťea4 _O?@ L̠jY>`~~3BBR$W~FT;!P_ Ύ9@bjzӉrKϳn'Uau͋jaaai 9oȣ frzFy@ wA|}\xJ?H.E r9(9|@0ȵ7׶bd8XJGTJ{~Սf#R*0nAA>\qFȳ|}GV4}TW3;7GPN w2IF>gye5)ּ475cR(RjGF+CnEMwO/Bݻv{QjT/ GR!:2$Iƍ*t:,zڹ JP(/| AAxsq3 ^Á\.'NH$'T\9$Ij_#JXb6DpHx}>Y6Ggw~Tajkjp:D"l]TL`0`ZYZZr+m,/`41,.-Qk 8 l`-%ϓNg ZShg~:)ϓ10 R.Jr{"ᕒd2"FA^s }: ݀W HlF {]o_? :yN6ajz>syٽGIR~-nܺ@ XX{ I\Hgw7L10\$tQg45zFcL&J@ & B?U z=H*JqTA`:@m+ s4x^i^FT*b%r<ݸɑÇ8xߐJp?FFr rg" r-:Ξ& DY^Yaw?8R[?8Bb6DuNr:_QAMă&!T*I,&B: ]%JI.Q*X]%ǶOOmM5rBw1??s}9Iy=ijg#eX,F<=n{{M2`phZM6cphxSyIJETǎbZC<ݼ(/~+!IRʹQ^Y}( ;bB`A7xK]c fQ5,D{#ڎ@ߺ[߁/8s߇`3$D5%HH2dr9uF:g>瞛ZMWw/u5.0A$eGs3@9w.^@ `hnjϜ3nghnnƷ'0۶WQU)ds&f|r5>!`/|ρB.G\g_Ǫ_X8v2,Sȗ]Eup74P]^g7 2N_X3[.Hk\t{]=dY=n  zDbrpl<]s;c*H4nC$^uu,ǩzR7nݦvF1MmFQLF\^O\҅$ leo thٔTxPrsV~C*DL%r0wlnܺŚ׋gbbt:?X8{M (_,/py:9z`7oT*imifbr?.^8Ǎ)J& Nr p^'&$q655vr*NaTJ%k^h4Ξyd$%U]~S' tJh|ͭ K@>jj'_3;7OSt:M,CBуYzF%it߷[Mz(CwO/Ch4jCʗJQ)JJA5:p=(m4 (JH\&Ca6iinFՐhnjbCNgrj`0ĥ }>^Hvﯬ/ 퍛 i|~?ݻvd(@PP[[]Djžݻ( u$qYR4.ŌhBZS[S{ahxۍ$ڲ2;σ$fn>u8upZkC&FMM5vJE(faqKΣR)1 4z<ÑMmV+Tϲ)U*/r6D3˃VE<~Gi zPw3?^V8>=t:'O,u_^{\MVzM54Tt9:о=EBQ1{|_X I.a4kpm9 s mq hoO0+Wsg}LMM5G,^ίV:`UUo%$P_ S'ĝJa{kpm9 s10c9qp>ӿs<CӲZf]ZBPP(D"E kk8vT*VVWq-/}t:(.Ӊ^#HV: Bv|콮.Ŷ>*8 QS]E2@Ӳb$Yeh4h`ГHd2T1'E*xJ`(hڑ/"ϓNg^i }j5mٽ+sINo\MH$_y"ϓf Zhgv߸ 7ٛW# W/#MMOF,Ǹ>*y6ڵD  ygaqǏt:Hg2LMM{NGsg~&7L*G=3475Rp: DǙ[zLO$r3(xEuup74`4ʑ8l$pѨ5=x$mtFp:_$t:{X鿐'Ͽ⢗;3hZ>x$Wl2w-710}N> z=V~;`A4÷n^L6-nDV񯬐fh7F  –+%IJUO$&`XX\bYAVep8B"np:H n$}!myǏL&98DӉ`0[7`08x>vL7v0 HZ&JH$^~ҙ Dy  YFI UafS>L&K2x M2Zg̠( F2t:M4Eѐh4D1Fo$I(T1>1$u9w mOYG"<gΝYAA^pF|.V[(b(n JEII |\.W"q_oM:.VlAIηwDzϷ?I0rU؏,p"nwa9W6^v/Ϝ+ ܺuh,F6%W33[9%xL&ΫTUV7{Ȼ|ɧTVq\;809=4N  /{nk5t:$h48NZFբR_N,m^YQ˝T*ҙ4ZWW$˗;,j|c6J"(YlYZ322 @<GRΫdn: f)z *eP8bN嵝V+TW+;p*)  l"PyBXXX kUUUلn'Π֨ɤȊLSS#$*`0`4AL&td"URl_o޷x,FH$jERXZZF24| % g. z=%N' q8Z)o;T*9AC [{v=ݓ. KeA'?'?}$ItZ.(W (R (dݽ UhG6_Y+=yJXJEY2=ַUVTtzNˑ(!=Vʊ V -ۚ xʾn{}Ԁ'Aþ~m\ IDAT=ߙ'qϴ"yY³c1T[=[YJ7[\^~'y}1Ax>??jd2<M 5F(buBaR=?vuÇfz:%xkׯs;v݋@$B0U7qNxbäR)`VYQKW 104ZEJZQPI.]X,N4% --Srnf7 d2׸yΫ]z"(Wr9&l6Jχ(+$n I6%JVѨTUVQ=u55 `yyl6K2ܰ  <_{ Qlvadt:n(ܲ,L`0{06>Z;d2޽oP@0 ??EQJ v*>P_bfdPՅ8mMHD ĉ"aX*İ(JO#Z-f82 =׮)-14|RR(Fcaq&E Idi:baiiJ6[7|Q$%N'ú,}1k}|>O,h40r{`(LScCq[AAy@0 p:LOߡeoQ_WN# J%.‘}TUU=5Ёt])Š70>9ɎzyGf+f9UoyE"Qry>A2,T7S݇ p83g<ǏҕN,UTWWaXB$ ҩu10%%x}>'&U*=\v)/_ ilgrjRw)SLMM#+2{vbyُ`@VKQ l0 Nk]\%ܿ_N˾RD"6TNhlX&nz81@ww/E/ǎںvq;wJ.nq|X<0÷nssEQ0ȑ#l߾:m:xq1`pr(Ma,r<Cq3-F?~7{k16D"8ҙ4JE"@ӑXXXDQd,3^,l0??O.G!2:L*Ơ7feEQDdIfOp8۷-qʊ ZZts{t q>+߸^b633;GEy9chubWIIb`<;*V `D"Q!\{rel|2‘Ha `EMwO/M LLN)-%J Er8v$D#5 tAAxyS:Nյm}pMEEו1U\ P^VvOZ{yǗh4yבe7Soo~>P;3hZ>tjJKݼe端L0XfBpp|b ׻UDYx 2^7$ Y9y8mC!nqspYIRDq~|AAx9mX,tAp63@UU%:.!T$SIz= jT*:.QWWK,'0?/~>_=O 564vL:î7rU.]ĠDrZo6%c6d2H@Ey>o rf$ Z-J+v; `fff@~2 x|ö  |lPl6,ٳ_}]-6B6[dt I q8.j{Zv-ZAb<^뢴N0|kՂX,PdŢ$2 h?lURb.?qT ݎ@!I>{gq|xp׍zm5$m7 lO},gZ67a׻ɳ淞𬉟iAH10BY s3eY V10kgԽLt^cvnݻSZJ<`brE/MM@BნokI9lCqǙ}\%0Ϝ#s.]$QYQNuu6@0H2ě!!b`JJ|LLN!T;z *+XZ^.s'&xJfz"{כ,--c6/3=}(AAxv6sz}_v$>n%3>1I,13;;,/#.Dw7xB!2K zzzQd0;_}WYQr*HdCژ76u =}|No(/, @WW77nrr'lE`4r&n H& WPvO?;3?}sm0M,^:{T*/\Sh H0:6Ney9d`0H(N,sLNMc6Ig2b kcVo2q|yb1.^` Nsjo&%aWW.Oi)tZ~?lۣc!0DPW[dB4X'd++O <'uLMOZVKeE9@JcCv֖gtBaf-H۰`4ڬL&8dNǩJbVUD(nm<FvuQ.b1S)f %0S٠܈`ĉlO~CzVCӉN+++h$"*"z;CACZ PxtL&j$ۇh4!t:-T(`3Δ[ZZf f9U=(d2YW1AAx4,jaiu54Ak6NOxۙ/{CV߻.,뗋z10 ՉGt†u}@ X,of[sfd6͛10 ;eu*szv8S_$qCkk,17@mM5cclB%~~ wMC(g8~\!SQ^NMu6@ H2p .W FO??M]m 5,-/*qjczf,*qhѱq6^zS9  \ GS]k]xJK9/?/ 10K ..f(uAʆuKmVo_?N$?yOt]/DQ޳EK;9o/>@UU\܉,456 TTVV?pݽfggql*PAo-ЕRAD<Rb`mqZ-),f3@i`hjl\[;ϜX,F 4tf2"L:Mǥ|S.ԈFFQW\{Oe|KKt:*BQ9AakgXسu3 pɈJZ},O<fǁº+&Imb^(.de#su{t&ۣL&3^4t8ر};/ǎ|[;UtQD#,{ڂ Z'kbRM|‹c`f67֔Z$Il6KsS#Rl6+ ֢V+x> X{{._<x,;ڲ ۅKlV9K~3:6Ny>‘|K" & sZ a.^$SQ^c#L(߹3^Jssdj^ŷLEy*;475r{tΫ8o/J(|jkƷUR碗Օ_RTV3r{ ~ۏH&S/,nدUܞAɹݻ۾xGwt힆p~"W])$ jW[e~aB798D,cZZ-  c4Ղ޾~N|ݍJ8qb-fi5Xyk7 ^/t\.>sx ε`2(/B%Bb1zl8η]rQ^`iyj.]DV[Z=9s рNevn71LzF]=OLAg* J x<&_>4Fa,afH4^`Уt4QQ^ۚinjܹ6 MDc1;.b4I\|wxlV+tt:CMUG=B:&NՊbvS]YGsS#wff}{>6 WeЬ^vP_WKۅdd% GPř~{AA^n[.DQF#-d2R)O@$IEۅbрUV%SS]dS'E-Wne)/g??N|O֛|>w3^4#<FFnmAmY V?o$"RfHD:.>0UZ u~Xb:z:ɮN^`@uk$I÷F(///$LaZ7[#47524<^׮Stn8_' ?|0=1(H019M.ey%N'|93srk6eC|>;z24c8nZE_Yarj *$I:uh@MuKKKa,f3#G1kkl2m3011ɝY3\/ˑd蘂 MRP: =ƃi*WWpkpRI+MFb \.E#2LӁ߿ޠ'Hfq\Avχ^g~aƆ|%fxZBÑ.<طdY&Y O!Hۗ_ 6?8ť˝Z⮷ot]FRqc|~ C#nΞoh0`2 ;^oVC{EVVB%jAooVP8͡!zrg77oaZG"XǍUal00CՐH&I&obP[-OAe+^O$%FH$PEQ()q/OӔYYY!I]q Wp:flV++++8 äiO0\XU;ߎSb!3r{2T`0H  B"ncZ(uk(H8f&3 9{<e10˸I]m .WqrFss#ηsCHV}QE_XSYQlhchl餶t:M8.whnj$ΰ:N,1"E}TAAx:|Y5)X VwN׋h$bXZ^Ơ7D@˅F/O*U]h4].VVy;%^R|> j㡿 W ;YN[d2Dc1C1V$I,3D^O(B7RI IDAT,3d(LBhZj5XfDQv]Ёd2&yh4Rb_FC4#a2  r܆ٳkWqV1ej‘ZIWcgZ-\T*M&AVddd2E>l61cʲL,GW'yR WOtLAAQd^^l'+d7m3ܧ|\~ uVJxdbAA^!Nti+wPmP>YϢڪ<AA$[YY<|kMDlF Sd6 K;_7^C,gjzE/ @${)Z[3DQ~8~IRVؿ/ 9<Ǐ)[^桦 TE`ffWI FO??MmM55x}K.tZ-wfE$³wh̭8vlkj|v rr"(~cO18$ 0LFzQVc`>Sn_&t*UshhxS7'S'@U=>wp7z IRȕH&Sx>zzӋ(\r k]踈$Ip*w0>>A(~jWAߖ+eY6"Qe,f@ү[>μZ8nɴaxa4xPܭ<7U*a1_ǵC$d1 XAf56VEC @e>ĊO0 &3zϻ^ND( cw*7$NqJh4EY]"$bvu5ZM<d2>oٹ;fiǮlXIgD  <%10HñT$pJH,//cXpJȲL6EzӥW_7^X,44$8zx-'&Bn&e  319g~~ߡ 89`-2 Q)upJH$Rpyf3ǎ-j C|KyO9FnSYQA:`0-oEC}>  lk_BTV:R7lhW'N0ߧ׮SQ^}Zf7 O(LIRtuPA'hsM}|p=]FeN?pkd FF}:q^OO_?F!CÔ8߸}1l<ݓ* –P]IZAeΜ=ԃf#qk`jL&#dӉf)e%$Lb43C:.fN9>mMb1Νod2RtR]Uh,Lض3ڸt jBAuU%T@ h`0H<j阝{TA-10/q:*sed Hr5FDtHD4d4PVK$EQb8&qo,ZuK6e|bVKyyT:]e.`0FQ0k}SEi4CVYlL&S110 )f+e޸ub`Gũ5_o@ 횻\ܰZ]o2n|R210 )F# b`[ly10 9ewַ^h,,3slkn*,Fi[{?fSR).^$I|1!N9 996+;_Ao*IM˶fG2;;W\!;4Z!|>p1,,.RW[;`fn܏I&h4}!D"A"Dבd|  ޖ+~>.rh,^Zu.W (;>>Io&`(ʍm6fNx<εn{d2I6h0/Gyԫ1/\NzXq;ZM6#jVd2ry4kC!;NJdPST1FVR6}Zq3"FA7:f}TZú5k+=,~۵[9x`_5`^Ysw$YY+ejC$wowwh4"d -]}4# O/>çܦy5grOm}榍%[L~sOf|cc`~ӟ288H>'HN~ *Mw,:^<](m}w3:M|Zr~Sym7"zq?$IL&DVBmaq)+кmy'ШSlom?Wq2?GPW[(LNMp؋c{ &m8te͑婩BR166+;+>r[yؾ_ʻ҂a '~Fk69\<0LgpAAxl{n*D"Q|-}hCCw[PkԴw\jUVJg_n;_Am|e==}j>[t|=j5LD2I.ɤ3=z]fg(('OaٹD"{]x"VAatX jE!c=t8jLLMp{t۷Gt,aDdaqh)5nvW&3/\y=475$+\_Vf3oMk6tZ&nW ee  ryw$a_ {URV%#*4/gYj˟("T4סVed*^+u?.\FM:F IxK9+J-R6e0v.۔TUwOtGtttļgO< `0WBhі=%!vyp3/d0B|"ͼܿ2ԉs͵LLNL$ _zNcvN#GаD"_0W޾H$L*"JO(k7nk:p@ @2brj`‚{l$*fS5q 4Mcvnd2cAAxHPIr}=>3yB!oe⽺"~ I8{eӦMpn ,˘a(34U%Ny.JPUUv$I^_|e:6۶BU6l0Mom;Hҝ6m0 TUEuR8d,˭'FUU$@U%Aйm0 MS맽{ƫ0MRj9{7@ '}vB^iݿo_(@}a3ln'+/ u]׽}r3h< K#`?xWsmYt£sWAsƆ4lLare T#|~]Px010+{.AuB|߬+2P%wJt򲲧rɩ)"0h]]m"FAߺi95cc{XXX`$ex$ʶz9{_cGH ޽m蚆gtlJEEN홙ӹ;5P^VJM&"0SSX;g$Ce<@wO/[EB"ذ>jkqsX,*nbQJKDPAtcY?vA0XD~~m.q'uΦIR^[}ߒ}1{KyAAx|Gy:$GY׻W}۶.Cu@|߬[u_p'{weݫ]=&'Dܸ>uJ]+lYhCC#l[2AX^c`Ξ=K]]333pMٽ{7Q_44l ?(7nB4UallJ{{{:{PE"aukop}fn叙NCв8f&"0SXH(m5 ?u̐Lϵ^l^jk71;;K2bd$J8FSU,ˢYq/fffصswlQaj~\  f׮]Z+|aΜ=GFF?i'z/SU2La ׮]jI9|p=o[u}'Op;8D2k1altKss采8@{G}t ?0CtttK|m+UUU bYcc3ض  xb`}}}:uV׶mQ5"0䡩*ǎֺlVc *a,ӳ͜OD"Oz10}i::被ǫg$R8k\j  bȧpWccc㷙H$BWwϒAA{:rTQ^VQn޼Ȥ)bQEEQT $H2:6Nqq1"v47}_ Yv M =~)͍$IB FR$ !I\z 0Y\_zmXR  b] mn"Im{?fcW$$ɍG4 ˲oN"IDATmM0 E$ 4QŋjY$ 0h=,%;Kgq=w^YUӰLӋe٫}l I2,y/_ 3D  w:s?NOA'b`5E 10AwO;ތىIJK#+:Ӯi10 <10`۶;$I֢56QZabb۶ FGG)- STXȧǎS[SCm&U6LLNbY&U6z:/E{g奥 kk  mUf;eY1022~ҙ Td*M*ZRi>0C#p5^ؽxY BA:|LMOrndJ+LMOS^VFreY5b`>_6&NGg63IA~(m:Hg2b\t.J#iW-穬p;ZZ...R;=}LLN2:6 ?AA֓ͤw/0=;y/$Ia$6p,cd2%?|z8T17w޾~0iǙϰuK`[6>~ok|bH$쵹u˖eN?D"N]=SQVFEE9ƖU  Sq;U &S(>@2idc[t@M/XrA10N^2EQ$ qp۲PUս وӋclEΟ(ض}nqrIKy%FF؎C*;˝ wv;$IX,/^x/+Y!b`A=s10`&ฝ4-lrNaSiLa8n'Q$IB[)M:d垻PU}{_񶫫7>\w$ UU8&% U cQD+b("bEAcۤ4`Z7%<֠Lc.c]^o3il e1 [T[pL۲H&w.qfWp~u{>y10O܋${RO`d$F֭]ƺ&>aW߲?x JȑUBql蘀l[8Νg||]vA10H:&gjpM.l 筵0 _૖ hFi$BM&J#OL6CCH֋m}>$Ib-$I~@>##Qnٌ,+]K/N.\AHx/|jjjFls$cCEd[hA}~/1msk`;oHu AA~pAwD|? ,>ga`X&ql$Lv/S*Fk\[Wo uu BH@GgFinndjj qv:;裏2>0-^V8V~??~ <,3H&b&>UU7rI c{}=-fj:Mz:[hVNP\bsm (qfhnl6tMcjz/),(ٹ9~vm[K8Yɤwβ? AAM+q;nYḨe㸗o-fڶ;)$Lai,l$LK F5MϨ(ٹ9zz`&qqn݂n Gww/m%.! kˋȧ>wċqE_MRD('HH$xm:xpI }GMM5p 5ՔR Osccc]߳畗FG@N ) }1w6iNgYCϻxAAֶq-K-LpGpY |2mv@Q0-a|8Hn/{v|G?IJ,Ea}[=m[=Ξ(/_#gccr+l=εp}().ưl=/cqnf[/8},sțќo Im=0~Yy޵sI{9mIhnj\2;}  k:Kf, M21 ǶKe9($Id2i, h~?]!ItS%po\ Ŭ Y[r(=wb`6nZpo[oɋQ~ϖ-I}wZN=re=[e3 }-3 lɋ`7^\7mh6e4l3Oϰwv,$UEu$Yv;mZh~?yj#r[J ϳt;JL10+4g '>aW?iee㻭]nɌF(FW vGfnf(0ۉɮց;#{ض}/8hF2dj:Ni$Vx=\Y ),(~p߾O@:&{hTYܛ?IxRIf.a1,TE"e⳷v`>b!Ư*DB!y~ uFi(ap$FuU7•H$^m >Ξk EU'\Ԏc;/,07;G*OsG3_#Go+ٷwq˗b&Ϝu'X45.뽟9r8=}TVVqLӢ˶F 8 F16rFƗ8\% L&CWw/\j$>3C$R{@q.( /{^w,,$$$o͵DcLvPD%mf =}LLLRWyYrٶ-: dYV:cȹ1k1>^e, 8~j3  uh3Yҩ kN S );D& f!@B!L=rt:s'buY"M+?z5B eL!t:M|f۷Q\\]|.ysCoS),(Obe;۷hض_mr:EER)z9xMzzhضYSSlRGc6(H;:yR^ViY΍sG  uߏ潿׿I8"FAAw?+Z{o|f6aK>4Ef2a[`{,wdIeJ@Sx>۶qQUUe}%N>Ě8 AA$;(e,rh*%~9 8dl MNc#4dLrg KKÊ@SU]_Zc.5e|  R0۔8=fee/'J AAX/`E˲)*?Ï~: \rtZLɱmJSkX^^;w4SXPڥm۶|-;i\CVx?q{xۣc'095%:k\2dxxdx4mwvL݆ ϔ_M[[]XXω`hKXwF=GFKJӻg9c~~p8,,,Q@AT*b p@#??ߛK1?@~~`Es+B2J0q,  p/oH~~`5AAq  :( 39[IENDB`gammaray-2.11.3/docs/manual/images/gammaray-stack-trace.png000066400000000000000000002254261413310443000235550ustar00rootroot00000000000000PNG  IHDRUz>& pHYs+ IDATxg\IICzQ@Q DPA@@@bWPPX; (*vIU P%y?r)zw$ٙn @AA݃@AAAPooop8;_KM ů݁ tX~) AzX lj~w$SiÆ+H;_J-T:d _ hooa@/gN%EŎvI WuAA}  8y  8y  8y  8y  G(~D=p?ՃېFHHJQZ Aĺ^z^,JV&F&2o *%) .=X`C@BR]_΄gR]×xu؇[ w&O#Xdp\Ҡ_l6;*柛6'T_]>Yk8Դ?FOK> 0pw0Z[ нs*TMUPYYu<]t5;'C p>A3|cnjԨy XP՛93Ǐڪ 55Uj>Ww7?Ftvv޾y=vxq'J>}ЈO;f4Rشa,i ?ٴa-SiYTM B$gxNG:ϞOLV}RARxjk Çf &7os?ݍtwbs^s/]WW{9r蛷|07bk3Sfd**]Aa0` ]9dti[oA6-\RSJJfzEzSs+Q%VS5ʣǩi4@Xҕ(޶G$\9'>|,| `13g`0>"J]Y޲1G+lY[KUVV?~鳜 /St/Otظ7oe%{_1q ; X,:mJJN}3kV.?pHnn^[{v~[6PWP(u!a'^~ pt_Fk:^BBktw ;;yjko.fAhʭ;mgxp¢-HxX;֬x]s{vmWT$'~TgvNx؉ظw7wH=x<WmٸH$FB =;`'OػKJJ*ҕ?7o觥Bo8[ ̬'&S.1ӧR sSvO=f pONHL3GC¼gO04 073}Zij9/^fdf;pmqc,͙,R]kiWTV͙=SpQ vʀeW'gw4Fwచs߾tJꚕ˅*x;˳yϾÆ3O<<⌔Ԙ3W]UǎE]Dhmٴ^GqɧK,-TUT>9uÇLVyfN]x$a_ !!!&&fea~Jqq]_q:7.c0Ng=Ho`̝h2翲_?-#C k`<}ʍff=^dao 47={hg+//l8NB4ۘѣiib0qcG{~ZZZZBo:e7o fY22yZJFA$?p8%EG4MNNVYIp&O2~$ ȳc# Fh9bbbYS[ SRR4``lZ>~x-M ,k4PUU-AUU7xqq-MZ @" y¦07% `\uM-wOe.NX,VYIihC?{hAbbbKkDUH$N< Ajjj-=#4<{ּ1qlaH[?zn@EERm-v?Fhc1y w`ׯG2`(!!b ǣ[saQ ?99YYYY55/5?,?iO<)3ɏ>A]}I0]MU@+!M-[9Y9n6"hllxlʼ2jlhY~da0&wp k1~ب[u::D_% Fh!KbbbZC#E hjjszu\ w?y&'+ hhlBoQA+ `zCc#`]h:t><@W;"%11Z}RE &7Ay0#!)h`0 l6@ц "Xܑcl[V_wP>D.^ZT\p4Z Osoeh0;AuSwo s@A^F8FS/3(:T_ //pgK.H((h7/xȸLsry̹ ֭ Ə{iiiYw N J㾭RI$Z&otn ffS>9rn999uuϞ` *]X}~QNNaprH*}[EFpys|yca!\ܽt"@WfO8}nvq7ϾA*Ǐq fhmmBHDw3R/  ;wLR;;;SR>{[^^y񲺦vԨIFbTZL111IIʪ*ɘ|yfWU}`"S)d2}_\S&g<ʪ5ro`2"3QTg9l6>I&cnj~ym-~[9%J,fk-4uGquuL&L uѠݧ}v[{;éRљJ9N}=MT["Qz_E2YRsƣÇTZCCádeeЫr^h_'fIi>9ZWIQqko4!hb8uVS[K&S(=AMɡ޵{^=## TWe~л0Ndeel>2ҕ(6\F[* kopX-MM1X15bbbJ|dttD¨#. FBooksgϽ<-SNNvQo\33<䩶|xG`į?eSX VZFzRRR߯ X旘 3<(wѠ=7oe9JY3I$1ig765H+KKKhkU$JT|x[["lM}-VUQ8q|T#Ysƌ\& e}T*MEE9>,"%%s+֌9|oc'bbZGyx7\9-Y`o^lvU**=} 0 Mee?ID4TRES L}mtQ#G( ~gy  __   'Q7AAпAApAApAApAAbcr---;_mb~ZCûAп]nc~w$AAAAPAAPAAPAAPAAPAAPAAPAAP}7YAAO'~ADE%( ?)Q{'3'  AAmmmI)Ǐh"H$KKKKwQ2A¢g˥99x`0ě)uJJԈJ3de.14w" 5d yiIq8N;[kkjjW6wAy")Eϝu ;u@6fj!co G]4<.GF]B>EA7l`{vRbϞ=vʄ{whjF z;78;~`ܺs7'N8kWKWMtw$UϞkjj89KHUE>D&S/^0KKK/^0/!d AO稫N`0n)3l1j6z-j`4E]P.Ӝ1i ?Wܧ:;:޼yǻUVVI1qA4ZJw6DF/TUU8NSS3Z2A:ee[UFF?7ggggTdddJ(qξ#~^MH$"-NLkhhs/Q0D"2 jd DDAćH$)9d2H },y;q} Bs/F{4@u Aj}S?-O-t: O]722JNMCL(˗|p1b.tt0[Ξhea...އCبLNf޼}`f:99ǂBSS[K345 INI(@N Ə53lҀUwc,Mίg].+..x{y>} =767xxϜ31AAa >3#m]п+׮aV'{;kI&3w=`a֖n.d eEʠ ]ncDOeZHʘ d AG  N  zN  zN  zN  zN  zN  zN  z[ێJ8 ~"(~nMt8  m; ' ' ' ' ' ]{<}V0P H3+t ޼}SSSk`lɗN&SvXgogOHZ\0OH&ehbVU@EuqGfn'5OHZDWM#Эy ! [\*TbXP(&&6Ho`n݉s/g<?bE>KZd6Az ν̬'ǂO0Q}] K,TRTDsvT(EEU=06>}yꉈ3 FRRRSCg*GR (h+@*%ܽSi̪ͦ|خ_#N}5jg=u]dccSn)M IIIee%At#FHz`YY9iN]9gF2v{{.:|`ٔz*uY nvulw'`E2tүE<Do nR;)O UU;:ߺ~3k7n<1=~,`1B8THOըOv,wƕ gƎlܼY `1eN61~l6;h(ъmnn>~JtUkٵ=%!#,2w_sKw~yWTF^UF]4΁o h0nl6W\|UKS#-1o#!-C~%a!) 1;ܔ${mձu~耼0#uFc+[b0;v[:]5qҍ[wzT,cLfjCga~lj5hϲ'q'&Olқw>B>;e1߭뤂/k @h;:LVVFWWg- עnt4MtCbbbwc\ܧ;`0߯`Suu6=*ZLHH$sKK>Y͓ٔtJ6OLljrJzIԪ[ҍMK>!}^g7ϩ΋.R]WZFF0JJ]]fyl\-++ǨGyūX`Б.#/s82}K,f%)7m5jo6XhxJڃϦz&~%qw\oܺ=zatM}7gvcc66.Fprq<tt÷Ɣu ZMEY ٚ;((,ZeG^@|BoZX;F3s^g6~ي57lپKhz v=@ss-m\-54L{omefOvts5@`7yV0ǂK,m]="/_E#(]  #A;lfQ7g͘ pvLLN=lyѢDx زhTϙș{z]WhZE||rj_:z܋6։"4ڪvt04Z_!+FEϛ+Xi MY-tw71&E&i`l8iKd?CdݦVvh'F->@$sv%L&Ys"RK&My`|RM(EwM IDAT.!ė/7oEtIJN5jWoN*j8ɭmm;͞F1ݽf1L4=sABOD̞B_0A22L-ms^lkk+*.f2c MkM,_q%*}gpwt΂%TU}ch`P{{{sK?M@dB;::**+-2 |%Jz VC)>!iˏhY:^Bokk7w 6v]֝{޾׏3 &~ӟ;"WBAZC5޾eH> qvZ8~A{1qc MSX,VEeKRr*s@כZڢ2ML(uu̞mZCCCccUgA߹{weS,l ,HƦe]{ߋCA\=jjj --I֕UgdZ86&.aI7o#]c@sk{SK[imi/^rpqgX,XuA FAa b,Ri Ch0L16LIb0۰e"}Dwԙud2L&3}>M HsvFyK{g&Ai 8 8խ`*fjed2+*{EE(Q];s;/{1q #{UPU &f/+FfͭeX'O{x = pFD쳜yY,gML!WTsUE<%:he=p0oE0;'蛗\s1A1q&V6k6lD75knO{`=P$xX,HhnnbicO{S&  8POfOLvb>3zԨnRR 9:ئRҜw0h$!!b'M܊жhB3q?455 FE򃺺%p}jz60XYY9'eeeUAaڕrrrjwt̞7p ?&JJJjh TWYZ|b>8&>x>L:t{[k:]ڴ~W'Dž 1$D';BT~Z3>|,8̀bm٨ /!!!t4 g:ږf F`KsO:t^-|P!@p*Jt:#6>_?-y99|q@[dkjXcX-MY3+JhхpXSg+* D=5 1YVkijptwSVV`0VkBxղ I)vC`0&O2NLJQ;T  9NSSBۺxKHKV["yQOv}@QH>}me7od}YY Ig04L 7xq{9YY:!b%o'ZYKIAG_O+'LwR.Rv$%%Si~ZEo->ȼq"g󆵷lݹ[NVv 1hij@hp}=UUTUUgB]]f+ׂ.7QkݯֹS/^>yn"88 t[ d T=v=6C x܏BG!iiv w** 1qqH+, *D,L2HF}۩N{y|ʵ3,lb,4Hqc?tw.R͟}]D񵲢"i03'x+* Fkktו#NgH?lv|R;ySΝ  ?#pѠ ӧ29I$:@566~}%I"-%HI{0jpIzLwuwsILN]r8cH$f|HD`G#08xℏ+֬TQR:>%>{6*~ //Y8wF<̠H"Ւܷ55u v.|D.Xը9|Q}G гa&eV!5ܝwstyQJ]=:WWאDET:`tttlb\V^ HSS3zd!56M܋7\y@"}}zBS(uD&$-]P?n0ܣ:;;]Nv'OCijj.T ja>ͮihG[koXP^QYs>E/On 2L:q5K]߿߰GB éETKdJgggqI POh ?.q 'С |U! *^?l~YyHDckllMP]s}g~*-=pHU綶ko6`0۬L%$ʪ$Ks3ML*--KHB]G!a'[::: OetQ55:?VPwztJBRJYyxe 5i@Ee婳lutt`0aCl6hDOeY<|8tv>u?--yyvC 8]LF& BV0I5ɩl6Ku7~OhCH=G^~DqOY/Q-`_z8 F' l>+ uВG^`0g/\kܓ 6Ho zRYYf`8D$޹Nk_x~u.߳/8v=əI;y9Y: a*k+K::kVL/4<"c=;9P ǯ^{ZI )MF |p_艈lZF]]mOྃ I)kWy\ߙbtd,_DMM䩳U;::;qaCV}9bɰcþ]bq[⿢J?d٪**W/VUU!)FIGBnߍ%mmR9oUkk 3Ӳ U6YYݹ]6o;/`yynREE9쩓.[NUTmΞ]-c%$%<|4$,Ht`dЁ9 e%֏2l\Zn\ZhEzD"GKz2ڿg%G|hwٔJk::V܉5_x.7DD[[4'_ʪϦE~V,+/'s{YJހzbGÆZ[Yr?0v  JV`JJJXO5hE%cFd455o۵FFkxw 9rHnDM cA >F"m/Q]Oh l6/.V._e焎BB7!гH0Q-k0~˷MѴ45 ?6: 5+_z[BX'._ݜֶ67cᑗ t`o RWw$$@5hw95|2.7/TFo,_Ź7Aڳ_UEiߊO-=" >^UAz >z/6J`,˗jσ?,+~,(lvccSSS3 e~Aǎ .ںyҲk8Jy9Mkhbq]̜?|L޷BRQY՛sP7OO>HƇ8;¥>&v(뉥w'Lݦ PBdWO:* HOcH~(ASi٢czw  AA'OAA=Эgc\ >ZAЯԭ  PAAPAAPAAPAAPAAPAAPeſ҄I}Ug΋W}Ud M̪>b_ZcEߋOHZwGpC.\5qgg5I&Sl:::oje;TKo8I<?2yzղS,mf_a]z)/ؔdbjOCTohbbj6cs, ]LLzT #/_]nSamm^?~Xl|"`ʼnD M]EeŚS,mL-mgY32z(Y}O}[EQ7MsZik[[x :q<3cѠh))ԯ#5~l6;hh:yi#݁XyEe+Ύ3 u^M X'tŮ;ԶH (**QWS)JJ_"J =q;܅KS8;նLfjȳ]j8~\7<]L6݀9'D'r|n{o޷̟ywr~>++ QFMd|,XZ`q`?-Mn;Gq4@ɧҀU 8@)4 ۺs4i))p\r-6>B[*`!L=kP}",V΋f+QW HٔIkV._f˦{YY܋n(<̧# <)%¥J%&O2 wuܶESCyNH艚Zz(Y`h0y`o`БEnFe?{y s,/wi.]8`uh9yiӽ^|ec[UC>!,`܎u^앨hr-YYYXЁ}-^bF( ]{;Wf_2Xو! ˹SBMhCg?=}zyϞ hmk >z<q1s}xȬ_+*#)jjj79bHxw 6S{w gXEoiia*h!,7='$%)ih0n?kj:d3'C;;;xb!l6MI/'| ۯVMM-7ݘ蛷O%q Nvjԍ7nZODD1GLk(+uRTl| kE|`nZ/ yZ'۽cx9 :ra2mޜY Fܼ@л<AtM"TCoJH}}G Ly]l6䩳IiJ׬7v`BFwn3|,(<LJZe, #XQB[]{(/] &&v7&L&h†SLn$ Ir^˧ޗKWd>HA^v<":O,! xz I+9p(%މSgb6_kh0N)(WO:2`YwjŒ/47SBx/H$7'n!B-QP"Dݘ8+-%=Î>fiY9wt8uꚏEǏIHH 6tEl|"A MY-tw71&E&i`l8) VG^݋W)uu\&̞mZCCCccUgA߹{w=piM6 HU &f/+UL01#)X,{g`b>7g9/̭XϞZ},(@=z`0LM܋ H-706յZW__QY Hl'W&JK˦XYX;]Cv!g<}V ~b.CCmt:|hƦ)i,+} |h y!ri^n3f >F[di9tt yUY^U**+85 DIby͚{pp[[o#5 5]t.W}=hŧRmrJڂ%L&sBA28sGE_鉞=P$xX,HhnnFߞ9+(,cHL21TVV] /'''+ƷZw˗.222K?D)`X׭ԐTWS5?.!)]n{{$B#OHJq?7yqbR*dh4$vV F`Kff=QWWjelb]1}1RR `?7m[6N0hjjVT$EH[{{޶bdeGƖ`7vhqq7FѣFp:-9%dSE]U!tuuuu)))f<7{<_8乾3䤤$:gd,fۖ e:he=9u}N8ZLYW5u56ԄbqsNwv,YI)ivbbL }Fs.s8&4QMMYUU^Z/++~I&H] T_"J5}-!!qy8^kd Kt t mmP}MMGYcG>@Q mYYbV @k`9hxܪSRT}[l6y45A z -G]J Ź߯ r =d h [Q1{D6IP%@@˩ؐ"&go`w)( SR* %dټ1r{~#y2;L{ggf<DK,_vƌi6o$J0^mx5%߆Epv6Vv6V_>@ko_yAYnÆ @[UASdn:*Z{ D;P8~ۖM\u_!&2$rnElXb*IJMsvG>Q&?)pT$ ^|I{7򓔐ؼKc4 ;9>{4 sS))q[Φp)r9Ăi?y*$$r:!]\NO+戳myPZZ8[XQQJ`9jc+5d>6l{G&42YEI " ||f=ڨ-4 {J l,RҦLq;{ɄGRBAC 0ȼ|22noD4}p_<ıcp*,'""2e$pqq%s. %u. D"4,` ox<,OmrўU^4%5S(Ʀfe~ q!VrVv.@DjmmC,idbyٳfquqB%mTꋗ%/g1Y,M[lp{|n$"UԔWI'mmT]m-[47Q`H`a눤'mn:])!9ɩpqq:U"e(--<{M&3'+Kjnn`͔E瓉ԆkVGwDQMwfB!̘㝢kVkǻ6I"ڨ׮nޫdd)6 %^o۲t"ޛEDɡԌ@C q1q }#Âp8\}}۷5Z$ > S!Gf=I$Ca9|{=,+[do}_Wx8>Ǘ=5x,}ID[WZVں(@WWw`H{imme0H8fQ _U^ݝ_PZPFJ"lvJ5ѣF{ -# UUO{,'IsSxMTLbX̏0lgmUZVf?yz+ sD=B__ּ5El66;7VWצe@400PQYC‘<(4qeKVWF44+ygny3Ʒ ip'OaJzƫo`nll 6-)-˼mgifhX,AA$$%Ǝ !fdwt::s ήռZ[W|ںԴ :;8r!HA{vVg_DN\tA9X%`&j!/znhvA;kNN0Ȅ+hiieX  >)W/:ZUo44McjlxE e``73zTNn>ݣF..a{&z=75u <|T,&*Ks3 Ջ%8nDNx6'/sMsҒp}}CjZ`*;@U))#U'ORe0l6BӋ޷@jP'"QFYY81G\m˛@_O9=6 /y4(.\f2t:?y{z+9ں4#F(`%^TZǖTQV7v3 z߫ _{(##U#GNkeaL:q:'NETz?U)rl%K"mߏXg$'q 7q =}6yԘq9&vv.2!?mp'O ii[7 J{v^TZJݗ  q8'Vfw ̭Lwnei$**:_W{:AEY:{~&Nǧcc̞eC~4MEY98^}f9r|J^^.pt챃S&/_՛RH RSt7jlnj.~f'ϦL ϝJ8pjw{G`U˗|zM>r*Ȯ9@ҲajUA&)!nm@p')/bbFw i}\VPs35f$Ey,"Ϛpv\ LXzȏA^t z"e,T^Q>q,<04Bb]}x!dxǞʁ+V0TgǚH$ޡ.!0zF^S3HONG OSL>{ΰa$$#Uػ{Fu~ \{G狗/g}XfkNvC*n y? p_=^MCzg# ^PNcuuu߳W+Spr]gق/P o~YىI)'To16wvrҟsVpHHMHJM}@AEνRo}g>3'A=zG]{x#Q1SBCbGZÏF>?v^~Ϟ(,Wx_vގ 9pBݽ M_`` | ːO>Ko3k潂/9'LJ1Wm%'^Uwt,\@ >}~߀_^ZHDTퟙPɩ~LKJJr#_m>Z O׳v݆:G6]vk7' !OC 00000000FFF'[9xhDw̛;oJL'*(s ,̆M7vJSS3*䜡4[+K$|ڟ135 H}Ӷi:h4BHNM߽c+kFe(y5v蓧ݵً1Uo% P?<L}BGgⅮ ]]Ioo_DtLAa0awW7zH Ι9gjLJ۷lB (؉F[7oxXv̨Q1Qaw?8qlKk+_|"Nb8}.+;{G iFQ(-aG?y p޼P{~ IDATQF>zMiY^nzAax4ZL 6n҈?5xEt#-su+*!Lm0%+ܼ7mz[͙ h!A_psd!r'Amm!ʿSo AЩ7l}'O_/_fG]|um]}OOF ЫV]ٹem;"r+;';~ᝦAŲsGwo?xxNcAi Ղo<.'A nپ hܱACVR A|g(At]C m**+LfRJLMݫo@*s AC~6\VF5S(KVrs9$,\|ڊz+:1(-+ wEFrԭz[B=WBW#G>"Bѡ1WdRY 7e޽}ӌ;vb*־3.Z:2 &>Bkoohhx%}Ҳ2u;'WSװA.qLJ7161ٱmdqDsֶ6g23OYSD.<,6juޛ<>*>3+d22os- 1B0_G[^^L=.6q@И>D$tu565!2oYBEY@ 1'M͔/^z Ç߰#N! pbbJ#̞xe__`sgΙu`.d0pyy9A[*oJDDDƍՍ3Uxر5C# q**>P@n^dJJ#9yVfcnj֖ /RNfJ\T7ѱ<953)--%""RtYZZK{=jS8Pi#Lq8E^D5ODDkDNTQ)y=$w_\&$$dhfS3ldy9:ӣ"%aeƴi/pʶ1CXXi@ζsg|\PPxwϟ{T\L mƦ*O5$"]yXZVmذarK"rAsW AM) #3[W[ 8 $$4bĵn6S(De%%AW|b)IIf]z%̕˖HIJ:;p7`QcƌL&''4iYV.#Æ [d! Ï ?}i"7WlݴAJRRRBbEDn;^M OvA"⯫2So^|AFZz˶]HL,m<}nD޺@m'-# hȺ7 $ .&zs8gL&ORWVVʿS4s^rVSS>gϤ< 9c4.^~#$Dێ>b )C#aDG4nb/]mei6l` aԂowzfҾC~7ziLƠ3$%$М%%$Q ɩ&Fbb!sP@Rj=J4IIL曭(rYUUmP('H PQVFTEAnk*(|R@ALp>{el6L&,M_#<2mr;V¥ 9P䩐鄸3/>wr;AX0j*Z.J:e2bm9w<{zuLyr^F@]QM ?'| +)'es:3'_c3fL۶y#,5Hڨ ,q򽐫F{6o@2`{vNnRJуi9&BiQ8&q8"L#_iRi4y{q7dy6rTy99&o}Ʀ&$H(-+ȯfz}'C HD1cr ƍ L4  Y[<}|ά\绝22oQu/YY'M&~Z:wR~APXW_p"nWSt%GᲲ$:A3^ljjSG{^px˒Wyw&0 NK8Ղx<~CzmM[s3S8Ǡ3ſ*0 fdpok $ q> > q11@VvSpwF (CiiDaIvTnmڨ< UNVp/PQQ!!.@ $JEhkijkioٮLJI0JRJ%u̘~S=cjꐈgjys£bҜC j߻y52!dGg >$lD?VvX1 m-tԆkVGwDQAtxqn]5J$"9闼zzxj [GD>ʒ?$`͔4JI$ FOO8c765 200ð*:e?5x 6xy\ϫ[ں C=W7<~򴽣b={iMHH %&oJ2ogrӭMIˀ h`` TZȑ$"ffrSc3.R(-UUi6*FkaXUEy{zz⎟475!xMTL)F<.~(~,0$]۵ R ފxYߏ&OaFYS[Q]];z4uT <|T,&*)Bnfj,""jUS[f UF䜦p':cinx\ѮKMˀ Kayok lvMMmv^>"' WTCe0\_ߐalh'N }}}s>DNUUU))#U'ORe0l6BӋ޷@jPDee~652LIx ÍMt:W**(J4y\uiٳ9QQV7vgfz*W7ԙ0 錫}|kx ry#U'W0:W Am8V\@?tuEDB+X;qىB1 F@J}A&И;xCQ#ƍ=_SS\Rxޛ2@VvnuM;}|W'y^}} Ý]aQsgķ5e6TeUUn~k?!?mp'O ii[7==Gc7PUQNKs.Nam[DYh%ѱaBBBjj‚p.,}.ruvDyD/YϜ4q‰c1'.Y@&_>:8/4gaɳ)'>C'Xk<8 &x`hỶder*,//8:&oɓL&uk@08q,Mi8QΎ[|6xyFD߸,##meaA)ڲ}7)**bnf5x$<*uF(SQY1c:!F#6u%jhGiis0@j[sn&j(+£Frp#BLdey   547Ec^8} Y_ 8;]Ձ(#𶦖 ,g6A=$ ^n3dTQYqzϡ:(*(xyMwWe$'~[/_vYYy歩7a`` >ƆΎZ8}߃Z~g~DO_S|Oj3dH>@&w{ BGJN=QXx{ٰX{[8Fv8y,ݹu 9a``R[uY3i.[,T?+@A=6`Ibv\̙5^ǜKV O2ScpH0^+TC~tq1yskmsqt@ϿB~W|׶ߵmJ3g-4Iɫ+<̬.]:1ȼ`W$iLf /θcTUU87n&.^щ|MNIDbW:E޸;Oi4Gpen^6}iAW^72 ?Yl>W'9pkN!x!|pX| a;'WS+;uS@Ҷklake|u.JˋWf&F0 ;avҕ#}u L=[N/7qpYmK- Gئ;.'&@64\.{SZԼ~9yv\Ɵ=2\ojjsr+1j4ex'ʽͤ%+8`#,Hنh;S(-w3020[xtĂC~Ȁ4 a\E>x} L\//{mmM-7f qRRRE'ܷ{zLҜŷlBADka=md{4΂¢n:]D V:L&s odFxdwt:.HNM }[1Ǘ^KkoyxE aXwor ͬ+LfJZ\o L-++Lfإ+@prνL&dzGf>BV#TeJZ:ɬwYxun%hIM`2U6!/54Kۋ֨GcNccA kP(-X,+;|~[}4u ݵwYx+͹M):NIKkA_p$Ad!xUn%ނ07pAPC;:e:.hjjFtwu  /(46d2I)i35ui5O547w#O\u?!b0eJJ12wЗJ1 vSa E[8+;bzFrǞs 83X,V]}2) St`0k<C̬ !'N?AGVvN!kނ"}cG{{{l[&ob ]x&;wY,¥+B{{{[Z[r7AY-84BY ՟՟{7g#?|NtOMAq;."W{{zzgjV8AFx5, h IDATwt44TxC b7L,msf%oXX9 "psā y|tK\B{zz :|$0<*f˶]Hq1A< Q.]  C[yȒHB<O$tuu34̑%Ԙ(~ MHtw#HE*Ύ""" dii)ѣƌ 0^MNNYqDZf֪H$aÖ-Y.Nr8]=kbRJx55Dpbbw6Y0<#+0|9fxI iM{&5=s%RbbΎvsfػ sg -ƍf#G9X[Z3/ֶ**%_޽>_tmimcsf++47*JKqGYYIǏ32@nDH@N^StL< sfJ<񲯯_WGG h\*,7b`GΎYL&yY;z4zḱckjyߣ>ar07/D%|+ cxkKs*]}ny)'3%.*xDnkmLՑ*oJDDDƍE{vJKKc {Q p8 ƆOhfbᔕl,њ'""i"{Z7!!!e%%C;6agk UU)II$ӦILLTXX83+r aaa9ϝ=qS@A+>Rq11}**< ą.卥ek݆ &'+d+"?'| =$gΊ#5G_ũ9y˗.$n+#ADE}}CYyM$%%%$FP, dG{[!!!2Y^c4NO*hx55gd?aڽ/^芎^ZX,Vuuų'/=YV^yE@kk[~AW/}ν]ǷeWE 8/C;$-# h`no H[L,m=g@edbbmT1sAF(*r o&1=8l Q(E2)B+,|&,m"hӌ jȑBy,Ke%W߄^)xf ýߨ{a[7 HHH0:3R'OVG*?TNNvvuYJIIGxd۪v6ȯ1q ))IEPZZl T L$ʠr2YEI yƌ3"|vI>*6~EYVfÆ}6F/OY9-vM21m55}?`Lh/_0 II 4+IIA _"95PLؠ K 4RӜmTI|Dzzz ]>ٽc덛IIJHll(+#c Q>)@WՇD$>{el6L&xMAkk+eRrӜ (~THHQ[UUtBܙO`keӝ , 5J-JM9&ٶssz[7_gI?7o.%mmTI =Ed _9+eə8  @q^¥@[UQé9YY ! sA$:5N eޙ C,/F\>,KD 89\~s b9:"A|7eeZsÂcB#'O4ף_,O$"q1ƎAEDDDLU7~4Ri5H+~8`0:;>4 9D SR`ƫ,l9N9JYYRss zf)-ㆶǷ<oiay;GYIIOW[JJbymںe{_M"}ÕHZ?6=!+KZ֍+fiYF**H {]8TGDj|:ԌR GэMM\=4b 7=/8qetpYY'rtrȼFB*%..9t9/Ԍ@C q1q }#Âp8\}}۷5C$״d > q11@VvSp/pvtqrHϼiknf \1.I$bkk'eЈQ?JK,_BB\4@H$2d99!!k\wI)iv֖1cFOҶwE5C"R(Umi SxWKs@}7:p^%+K3}H5Ht [ ,W.̫?W=3oЊ5P(\ ݍ<ݍ83 H3===DF;菚MM#$e~&NGB1LƑ!35f9y F#d7xy\ϫ[ں C=W&J 呿IO }T* c jƎ$H|ںԴ :;417\)LFZJ mm,89vvvU?}F<.~0KȪgw޾XUWlv?r챲 TUTЗ|ZϜ1? ra}=Ʀ0 ol5[ɩ0 ' 54c2I)S&x8'6Q1JJaUU+57 []]A@EeUooMiYl;ڎʹn\]S3zB6u P^Nnw/\HNM735A~EUMmiarLd>Ks3ԋ%8nDNx:ZV욚'ʹBiIIˀa!5-@O@ n|xMȑ'GD2 6LtEw[[Y W5S(p"QFYY'9L S3'tUTP hҲgs棢4n"r T6n3aW ?UAtlpCA9% Fz.MX."BA0ƒȹ=jڸѱzz{kjj; r|AoH»ޔskj8Wjk9wSt:CZZjf&]9yW.Llokp4sScOMT*m`_4J$L?!1|>:Z,"Ϛ>eeavx-hn+X I!dƘѣJ<9)II^|deUU}nL ],W /?mËΠHK/_Dml6.~lICzM>}}fF6'[6y477י:eryEՖLQQs3Sy޹m5._Yte}2n`anE"8;:sce1qEDW,]'OEvvuIJH̞52x[c1*v֧ΞV3=w5^kKltxdtlXDڸ#bbbbFw i}\cל3G]]t7e-?FRJ_k..&xiSed|zo:f&FIy˘㽽rrr/ox?vN]Bx)ICnRڱ\NG 2y19.NߔFYhld}f9r֐) <c:J#wmҘ1]EggGh4A0/mUC=JK˞mTrtDW͝s3)EW{@EY@5R-68.ȡ0S+;9Yuk"?}BMed-MЖWEN`gåfV6g9ᴇ|s潄/$"B~U`{Hx"ꕈK8#< (KG-pr^!,@ :2[4,6>MY9,.WTVr=Hš{C#-lDŬ,?y|[ƍ Bz$(4"PTTDGkޱH$_Y/Vv|ڸa9߳wMFjZFRj уn y?|DQAϋgGP|ڣT4} 8}߃Z~0kxyAk䂌w_# d2AY&W <s+9ŋƑ"X,VxTϋ=QXxed6 :q_}6100~O!]/_1Wk+Q6௺ !oq1g{%6>fRL^?#KVr>pqwq$'^Ec``l^|u/..&6OsKڶ?3'K"+zkł(v7l(EA`Qi*!@BK!y^1!dv7gg@Sv@ k@ D@'@ O@ = @ z ׷ONɽlb,/'JGEӧz~!tttNrqt&eWT=iG^g6yUbrd99ٿN0 ?b"ʼn&>1ʼn39o1ŭ,~ >3|Y@Gr:zةM[;9nϮޗ͸NKMiceqK08=#kɊ@M?A'DF5}ڴߦ`,ňbpw%#lKKSfN%s^hg_] av!dڢW߾{/'+O%S|fNKKKy 7".N#OQi|m@|?YwhSո͸+n? I쾋t---6Vl6`ѣF^ZM&O=ĨgTOL^&ߔ' [6&&_߉Sg,7 c~?D l`6y[7-ZnEbDɣQ@# ۟#)):wLfֶ~}lۼAKSiמl6_߾!;H"j^޳+cF ;f'O3r6_cog{+¥+jZ}X0YlU$11?yxO_:V1;?!{ÊߔH$7gܷTj7j~sg=-tK%y+{ lطg簡C _Gpr3E0YCnۼDRa~#bxC$ֶ9y~s.]{G}>|~}Y=pM%$$RxXAA!v4~хy}F ;q%eDvI#.*~Kdzxy͞999ٌ4 X\+:xgH߾}=~ZXTLVS]$cN@ 7oN xUUԢ7%:de|bKZZZX^EE@"Ɯܼf&SX 5Ǜ)y档l6q-VWx\ں}#<!~C6yێGlv5fbns S`o߽.,*7Ol6Qv-*q~55mmmF;X,,ԙsΨ#ǧLxsl| ţf3OL:;4460g?f3?$417^[[DS7پ b#魸6MlvbR*K̜da[YfgZ9}4hҕk7lvS<7mnaK1z ;쏟e 6w,IY\\|֌ѓ;{4oo8vud[ZZtt4 %5}D2OU_/Դ07;K'[ZV."BUU+D"QGOWSSŌYC a3Y{D";d]P2RE:>:ڙ9ERRRz"a󆵊 RRR1^ѯo_+H? IDAT s`0h{kZ[-MM'}Vn2qH8@G[;[l0"r ).Ύ ,܍1)22iNcF["8~ܘO@Nygx񪵵 TE-yoIYy{UQ**X@nVfyAcLjΙe\iiiiiisXǑwwo kKlb^U|2r|fNWr11D"Q^NnaFX-MM{;"|TV~(~Sb\"ꖼ{4{;Dm-͙~÷bbbd#TD`n:i>@478@"x-mm.b{H^ I)55{9\D-)*)18N}}D[񟪨{+~ܘX2e%%@FFZ^^N'譡gbzF=!FP'cALf0걏dv .!%,SǢN>u3a䗻CRR҉b4t+~%>;3LiqB^^ADE--ddn~_fu;gמ}Ғ{5f76m!/'r#Gm\WGWW*@],-V/:s[.H&FAA܅"}8ugbbblao_\:1l>a5ox;#R&C ~qii yOKee?jҭ~ bDFc(|\!"gފgU>uut ;4TUTu¢4@JS+++}-2?_:!'+˷9OXYBA44 v?Ejjuutv()*|b{ҕkY9]%|E?egNƇg'r]17_nf2o\Œ`_ZЀyRccgG 󡬬D.C;f'GB\.VZ"{gtuضNgt\puىb'}#DK*))++?wuud@cԱȨc[:@ TV~x {D"~BElv'♖9bP|C$x{y%^"035HqO0\[[U %_ZM P?!)){+>-=K`}rsu=KEdZ[۰.7HL b $@WAXtQ{{-ϝVTT`Ȑ!'! .}=hjnp8ٹdEK\(a㖯4**U󡬬dZZZzY%%i4#Z袿(ګT//@GG܁/-ÖtRϝ̟7;>)azݲ% /\|Zlm]]SssvNnHh<[N{2Y l0hknܤБczmcqYyEbR nllR \~pC:WQI}i"#̹hlAteiiѣGk'ϞsjÇ.X\>:8D[ڧΐ#X,.KVWh{kHHH,Vk@ C<|DFZ{GMUuݚ\d[+)))[K3Ӥ .s'R̮XGg0حUWTV;y^z*@ 1p^`ޗqܲl.)!ѫW7%o[ܡjR8NeĤ+ s3  lkky;lBG[[AAXdݽH6acia]UEmf2|N&]knf '<~򬠨xqhki-WzO>pL֕o~ mq̨[rrNL''$쮮Z[Ks13g1c[[9b8@R<}p+*-E׷^CGZZ[ʅ% VQSos8OUU1~m=޽WXT i陥eM&r=Eŷ3nfΝLOccn߾2~z${w>}I&&pUSssFVΥ3]8sggct8P(U2Y-|GC헓]0dbl)jjcnjƧy(wrlm֮^;kyg`PE7>J޾M5ɳ\.isN!)!9b>3-um[6s_]fouAGcl2,,,޲q^C\BRѼLέ.1lhRJZxn+ sӊ%+;l-GRA,g1Nx*VUUUWgYӧW~hf6vFVкMD1)XZz&`55=#"oݾ{V E v pH$x{y=JJZj9,[p7㕔)vI)iݪRv6&'@wѣF}6aanJٰ%^GWW'G-Lox3.aҔGw=; ,Z8o؁]wgCqURRtRhħ?5-_zc.͞9ۜOGى X/44))Iys.IA^~KVhk;fTȨ2ҳgNF"삄~)nK/V_{PVR:t0l$e%Ow7у'$EcodА$nvSAwwVRT\tш `{Or**{woWQ!;!74|j),mVJ_}pe=\DyZ7a4O??KD"1GOD 0/0tw:{}i[i! @dF,Ĝ򐘔$&('xg:;*~߳ 3`?M,l(r߃?~̬/?p=L2h0h\򯓇@ !<{666E\;z?_7ʅ {c,,Xݭd?Y^ t!W^o۱KFfbZv@ kԲ@ h@  @ @ D@'@ O@ = @ zI)J!'&QF̚9Mt?ػ^S df\`o'D1GE$Oo'9yWutKgfX?Uw=oO} **ˤ,'_Roc~mo?tuu=k,ˬ.: hY|&[%zwYue^} ?o~}蔌ܻ_&rr?V f%7{O؁/_g(^UEpi~c͛#//zа=:tttNrqt&eݧ FO??ɓSM'u9NH .N4.NO}!..nme3If?g_;<<qޯޢoimݾ{֯SdԱYӧMm5GOnimފ68!1TwZzi,l) (m rIWs9edf|-wvxp 3?yxK;;;p–xy}P. --man_xߢV4Z ̺;46~\Mqnں, h]Seܬw kooq ^S8״q\0X}EAP_}.؟-waR +*?X`A䌬nPį'+^]m^㍛q}$gHKWݽصgߍq)AVSgoJ|.stsrۺ}$&ڳҕkGN5܆xykwuu/YQ[W7w"Z 8y>rfo mCq[SU455ݰyRF}=k7Z;S\=/]ׅFyڒD9>y9Vΐԭw`k=~abf5Ç-*~cfe>~246\selQO-hxMUXTR/u1;=# _[A޶QXTPg*NVxx_sv,_`c)_]hi8unxڳφBq=c61uR6m~Ή 3Vn ܆r02۲oG\TfeVvNNSF6oہ5HްG|E 9xy {Oo ޅ-jUh3[CxoƲKWv~bo,xE~ fחYZl5N6zzxC&}' ͜>OvG"de9vÖKѧ%G2p%NFlRKS3x]~%o[`kcigcMplӧΞ8 +o\ %%/_UUU9~ab/-+3zfٴmdž 62`:$9?;@_b̚>v(VK\lmP7]=q.EKK\3kij=H$cFu;^![H$eAI^ث2gi cFΚ`d#ay"q I|Oi.^>~8RIIZ[3/;ywpȡMIKb0h`]]dgF\5|TTRʍ<|tluAbw?:r|ߞÇ 񣖖&o54%$ z'7o~޽{^]]]/^/[_.~Q#Gmf2lZiaCk+gUZVz6 :Dgx+^WGV ]:m<}U+V]`cmw~к7_"}a 8vTyEō+1RR#˿<~ i;nZjyEEҕEa$h&=W),شtիXWQWk;aիV Ǎ[,c.=t@_O/NM[_  _x5x/dfݑpv_U޽[=yjlh,Dee&HNM324TQ!= <{'tWVR-2#G:ZRRRdEEWW BV6w,IY\\|֌ѓϏ^jj=<41vvKػk}}>u1{xnctgG5;H$ɍ>*(,19uά 22Ҟ.90p얍7oXk4~d{$vGaAR**_ZZb_NU:Ha"T1cfVA #+bo_WH$::W qBFjB¢C1{|bv),*%  7UTT6 ~}ZYA,v[ڊ@ hij:Qr`vD FG[;[l0>~zi\r ).Ύ@VSe2Y---:: XFmDjZØѣ$$$0H$7g{w/^jmm+(,%#>UQK޾/!!ARV^UTʌiޘ]~^[Aښkb;"D[q2d,[L61VSS{ *>Y9>3+KJJgEi-MM{;"|TV~(~Sb\"ꖼ{4{;Dm-͙~KXLvwu#F%u[4NO ,пwX]0K{w;yuḱ Y/j9}ne~u3a6ܷ^.fkxxYnNޗN6:dǏKK'L{J]]^N4_.+NVlnV#2EAAOs=_"8ueeb{{_0PWGPWǎUUT]XVVVZ:Ye~h;tCNVos^zZM Ng0c%$]?-x@VSctvGamiM'?z˅g_:ślپkْ/g'r]17_nf2o\Œ`_ZЀyX&UώfCYYo X]!N=4p>;cv}**lVjVK$l@mD"6v$c%+W-_D!|y۳I۷n4͕ݔ(Rv"J>d_wR{}ψbpSRCvl㵜:ul]ߗk $KG7d;ٽdd -=stb IDATİx\&S<ݽ<ܒSo/]މqO0\[[UL=._MkwBUUQ!Ǣ"$%%coŧ~xD7Ȫbbb W%%%yKHݴoǶt:#h3Ο_2Mu|;0`ܻ %%%'O2wO\xRQ!1Y6l$&ֆ [ P!U/]޾aKs,9d 18qOv@Ss3OHDiUT`7gCYYb!]x'%%i4#Z9#X_[{w.:ZYǎIIɘW**?8Ov'b9\Z׉P=xkْ.^r-9;'7$4otx'= 64rp k7n s1}}舶1ĸ"1)f766QtCN^[0!yfmXsq9M3lvteiiѣGqvtȻ{?3;ّmmm,kJ~LIQ`01NG޽/'Ϟc{ǿҭI8>kڷO~]e wx艌aCyj|Tu>'&Xa4)%Z5ܧɩ\uEe屓lA൩ AE0 68}iYNn-++OCHJHM[Vp*+?$&XYQo0hd[[Jwm: mmm} l?rTju5dݽHΪѰ JZZmm޶HHNް63|PW'ծ^573ɓL?yVPT<~74?~ +Wov}'Op8L&n÷AOP?_8fȭwq\a9'fJKRlvWWWw_]IPLfgg癳јQDp+*v>?Бrae 7USUU嫢_;2w@ZzfiYd"W^vLL&644^v޽7ymc\}6艓L&KQQaMJ[k挬֮ΎN¶X/]A3߻k;>JfLV ߻;ѐ}deL2>"m41=}@gfkcv*<?9]]]gp8CDJ~wۯ:F`d K/\"@UEEG[ _64{:s߹p TRT9C_5ɳ\.>y|=ӭIރ?޾d2CJ5?NlG=\z;W ULέ.1lhRJZxn+ sӊ%+;l-|鋗"mmmUUUuuv5}jYYy减ffngdD#*oۼApB0g VSSݳ+8"lkm0-]`0 ǏDGAaߣx[Y% 폸q3^IIbooJޭ Z!--egkc2q ~0ݻ쭱.(ѣF}hll`HJH>l AZjj6l C| ތKd<4%%%amCl(.**v}{P\l𡭠~ uuޠħ?5-_zc.͞9ۜOGى X/44))Iys.ID;fTȨ2ҳgNF"삄~)nK/V_{PVR:t0l$e%Ow7у'##EcodА$no\/{9zBUMu]P*b**$ ...''ÿ^ Wc A(Y.*~U%˗-{󆵂+S>L)Y20_wVfSgϿ/-۽}'DF;a4J9a$&('xg:;*~j!;FފO\!!6_s?Uh@ ~ O=VKc.=VOB҅=xَñc{c?sW^o۱KFfbā@#Բ@ 7@ ߃O@ = @ z1$5biCkkY  ?#!:zBtxXDV|UGY;0TpfvSE|',LԷB \LZZ' z%f?gc_ b/^_Jqtru?޹#p -Z0˦uRRRϞ ;XGϜ>K𺠐|谡C9{8Q  ?AGG'H_P2~Xa S~{eG"gbrd99ٿN0 ?yQYϥ .N4?xSb[[YLA|=a|Y@7WS_ߐtKQApܵ\bo8n?{;_7)D/qz<uli~dea~M[Z[lM&NHL:U᝖9e, [%++*Eqw9~;~՜&6vnpy )./\#OgdxxGDΨ'\<,^!9ty6> 4HKK[-)y/,*hV)lll26.+Nif2ͬ߾{e+WS\=m)n,-+ ZWEvTlvDa;'7k{f\Ԯ.5zl|;W/L6V_>?tvvފt)))Gk7n#5޸=ݧLq@1:{[9 ;kϾ7SF=*oJ|.stsrۺ}$&ڳҕkGN5܆xykwuu/YQ[W7w"Z 8y>rfo mCq[SU455ݰyRF}=k7Z;S\=/]ׅFyڒD9>y9Vΐԭw`k=~abf5Ç-*~cfe_8+\x[T` ^Sgz,mgN—nVm;8{.(S.cݽ-l)W1X}Z:N9ۥ:^m쳡P\s3)A+Cܼv ;/__-!.r,[쯥9v("H Ǝ%U>Twy0q I|Oi.^>~8ommK̾xݽ#7%-=xguuS&sYVNl dd]]]ƌGXYLjoovܻ7]6 mغiٛKV'&&v("#dvg/<\R,^ecm)..ngkqj [O޼ak7Br xS򖤬 k7n4p[4c<R,M{ً+~Q2Bf {Aԑ>l臏4y}..!QXT<|;ypz2bp9?x{y[kK|;6.a1)ƌ%#hv;;;:1D"Q^NnaUUTAaɩsfPtwcl\yZO&[ ~}qzz55?EwRUE-.)NA$?xuġp55U̘=`foʦ%v|U8n\u0(a\}t3s c D kc=_߾V`@+ {ְ"ZN;d)))"8qphk޽__>~zi\r ).Ύ@VSe2Y---:: XFmDjZØѣ$$$0H$7g{w/^jmm+(,%#>UQK޾/!!ARV^UTʌiޘ]~^[Aښk#:gފcs}M'c87^eڣGOxU 9]A^^RRw>c$Դ!.Ge7%+)!nɻw_K-HҜ9/dwWg1112Ym|]REl{Oꍵ-M[b&]wdcMJIM8Q_xjy%EU=ytw3éo H{+SZ[ZcocdH麺D5432XPϨpDyFP'cAL'Z6a<@X734}4O0dxJKSuAᾐPPXsF#>Uiyv3୫o߽GLL 8~g)d d1]ĿdkK lZcO~#'KLtsŎ nźI󝃭d^9Y&͸Mvɭ\" $ Nfuw>CMUX]N9W-mll$`ogps}\jkkkLtv'{xx&[GGԱO=LqX')!} l^7}dC?||„p_̎o"} Ky+uҢ 9V/X\ںr|m>uut uuXUE_uq(++}-2?_:!'+¡ t;ƟL@\BӢgd5:h;WLL)>!ɳ ~ͳy\*(**XjfPESO$e9zuqcn=))d޸F:1i}C慚ZbTU?;Zee%ZM %u}'8@1`**lVjVK$l"Dm -MMI KVZʼnB 6ngohg+)Ql^z1YL#6焇$ '1%5d6^˩cQQǶnu l@}@"jD!hhh|$|_)KF3G wDOw/KWf&w`L")}SWSGڝjj$xGII[iXF,"%^3.!oǶt:#h3Ο_2Mu|;0`ܻ %%%'O2wO\xRQ!1Y6l$&ֆ [ P!U/]޾aKs,9|^!C N9B]nlNSs3OHh]@*؍PVVfX---NJH7J"UhG*k7*r/`_mUT* wK'KFz}ٳf'&|Ұm222"rNzsjْ.^r-9;'7$4otx'= 64rp k7n s1}}舶1ĸ"1)f766QtCN^n?!yfmXsq9M3lvteiiѣGqvtȻg\I(*`A"EwEcWAEwwz/bCTҔ %!$KX%p~|fwg~3ٝ2s\ F= ^)*(4)J33gm? ^ @.8t8@uk{*+EA˼)IQ#Gp'nKݕk7q I6V-}ٴʪj1z4!)W]]Ss;[TۢN7`(FFp㿎l2yO99NeeUzbbҥeD&lvMMmBb9Li6:D\\`e|9WTu F:ÇhH'کO99ڣ[ՓHp%%E--MoXZ'%oX۩T}N ݾ{ltI~%1A[Ksg_Dw˭tA]fTڭ_ӻ?Occcvp̦M*>1A򊎎]?KiW]oRY,K1h0.fWU'Z[cnA#O;::;;++a#'/uunA#/]!%OU&{Hu;?{dLw b@G#cto&p IDAT55l6`щxf`_s9* a[k]N=N $)Cܷ JA EuTò22LN6sAMp8tNQc;'7[b9,[5wR6=|AdD? Azt $RäFy9A**:ZsnC,-T']>|2_*pqCI)mzYy9:c2G}R'..6|[7 kkopSL^ hhl<MiʚLo Z?4Zk-TV^zJ?7מq ٲΛtEFlkk ٲd{E矬AAVfw pҕ*/DfLjq̩$$&&$?۵7T@[(!BFzݕMoi,]T]qIK1 ?_7ֶk7 ǍNn]PCv< ?Y?ݛ1Xn}nTi)IV ^>  o~  8x  `o~n?so?{b'܅˰Gl8644roIi}Ѓ'Ϟ۸t1bZ`c.f? _M2rr}L}$Vdg(+?}ũjKW2Cq^ZP2uٓQIS3~"X9+-#7-vvI>q5ĩ+VY;zNQQQk+rFǯ'屢22KJb2iYN=SN7`(hBiqt*(!)e̿0͎8Eѱ<,.!ٱ79|wߢ~D _W~q?ym-ѓꥯ:3"OZ~OF;:v?W A Ι3k&^d=ӥZT<lt5W/.\j" y:%cv %V,)|u⣬LpPœgϜ؈g{X8Njz3+/TWC J$$&?OX :}转En[yxVHHL~_ ++4ZSU]Sb!l6PUUq߸#ǢI ]pWY9!7y\કVNvw=ٲ5~GhX;s"JQQnܾy.NB`qcGՔf AMUDDJJ IFoMHL.,z7Po,ͷn P:r,XO>tjaQўViljZ|q^0pFV%p8Av;Waa1@ΐ-Zmmm!2@W7,tL xSVBB|5Dj(,zw`.6}tpXø7nIjjjQGU@mqIUkEDDj?q} u [Yc'Z/KJ˔=\}K$ +))UUSY(Ԅy. w ~(.Yaˡ{Gѣ|W/?r5*:jĈ![Eއ9V[@P \^{q8XLTg3@?]LL,!1E^|jZ$ҹ!<3簎HdORR3<͟5'wyg:嶶6<ijoO>mqcFrCGm=֜TI7 MJ.L&hٓgiii5% ϘpOcSSGGǡÑ3̞ӟ= H=Q'N!RO"ܺsF}RG&S)X^^ ȇ }Aǎܳh4',\`0.\wqh /&Y,gfYnx1y]{Y,w Ȣeim_ԡ!;Y;X̉S-j?9~h&s. ٵR[A/LfsK̸$A?y>/-l޽gX_41..)A2}SF1yUqI)?S{YWGvpI/A7oO1cּ!m:z]Aamce|vjGG ܳ܅K|у AWY^s& 8cK|M>{aݚB_x[S">Hԩ55dZ93޺s;mahfe8`v*_mBd7h%e/݁dY;AKWz] HggMIivX8ظ`3ϼE:`0Iya}6~ "2iy=Sq4]{T*J jj8ڲM;k/p8wSe]tl5iSp8Ks!>{s:ڒҲuderG#TTgaCq8CөSEӦ1lmgb=` G@p"""B 3]dJf# qqj;yp$N_h\LOw˜9VF;zRRbbbl6[CC}l/Cttt눋3z`u{x4H^>QWG,)+?{ёm̬C552mx'; MAn^H?~4hz\Bvfv· Apx\Ȗ ?}b93:Bl~4lp8}V#4d$ <?yD_[{y0_[LDDDKS|Z\ÉOJvuqTTZ\BNіC>RRӝƏ+&&@}'/x }tzxXZJ ?/uIJ ?o111e%^3-_&**^{[sWذ:hur8d4Sʐi~6֖S)jjjyyQp\y99qqqe9.ڮiog89`.(GOiisZ̊OLF5t[4g:e-M̈́wNhb>}x6ْ b$RC3oK=SPT)7f77(++?RGvpt;?0~"AJJRNNLC:2OC'1-#36>FpfJ3!xAC~pҌI DĺzSV=%#+t1`X55&2nI>"+#3k7I$Kf:Xd:"HJ~,*&t!lܺ`k޸rUԑʪ JKKw_,v躺:؆x<~絫tK]n#֏2*ڛ/PKH{zl[V nMda"uӁMtt}-wNg޾v]gun۹GNVvj񁶖C;&*7u9rIi9immEOm{;yyWGZdscc#` 4P굈/\9銋 ?q11L1'0};(-#msOLMF 2?ӧI7ij"bhttAs6~ r͙veE.@ccSee`g'9<455Ϫ**X.ڮ_ zNHC NP=w  z3DDD\ ^#!!Njp}r%w[}Z OJJ2s s?1lۢwT꽛_X)ot/44~i4Zkk-(";}Ξ2X`⎦̣TQQop8~h@s FrJڙǰcϧMDoS)U_Jtld׭]nzY**$WYZZVϻ4?ã;Wl6kWϙ%)!FBB"v,v@p^Oa='*.l6;!9%tNG?c#p8\MMǏCʍ a!#G`e<?}[RJZ@̔x#P XYYҌ J)Ј%jh>)..~a\jL8eAZUUŹO䞴7`2y1KyD":hk3u38M8X_O"S)%fvvϯ b' BXCqѻLd޼}PEEdʤMM7f:t/_@ 2xJ>4߶:er.Ŝ:{J)(oXڪ=#+FE8>{}/ L pnl*(qpԉSl j29}t955Lho(k|-ꚻh)> 2"L\Vr'~pp8?(peb= *%ZX쫪t~Eupv8L+}TUTtCX[Yx[N \|hvP(ZZS11qcGx}<߳#1g8NGKk e-&Κ81w#Ş0 b 8s2 5ǎKHJt4=t8ރ8EEG{T@iYNIC{#F{uٚ46W;;1[[BvP(bG\p^0Fjhز}N<ƳoO2 ).._{ػsX _G۽!銊 v6Ж?~ uu3O)y\hnsߤ[JJёG"G ?{J_n3HH,]vIrr^ǖE?%)%h\tC:GRRT>~pdXQe%EOwOܽ'@Ȟ딙Lfcԉ fp<0zigȖػKIJ99{**OSo·o ]/9Ѳ]ռ~n=e#7A~~˗`ɳ#ވIv)cϜ8ևyFccy) ѧ0?d{EY:}_S;~w /DfLjqeA_kc J/^C뇅80.+sLz JNl0tw6¢?[B- pR48r>W - qcwD1B?ad8iOݍ<ްoߐ ۷vN4hc; or: AAT*N6mŽ˼YOhſ#uaCeALKruratCA+TU\r yY?3өآi&2^l6;HF" Wݨu$**jme`u%$:;&U#}P(J]#'+ (XWBB{ֶ [B,NKi۟yOT \={/r:0t ֜:s՛72+Гɳg]lhlf,?ko(b7XzycQiQ#F ٢ GhX;s"JQQnܾy.NB`qcG߸XE Ν4Sj"""%$mްPRZvp_L26ڵ}kBbraѻzΞdeiuS·Q'+>L,`@^#Ǣ LٱmEƦ%WN'Ԯ\qfkc# b lЯBQ"~DXmþ|z\B*''nͪɓ;(I'O \rpuv12_{3= f??@:&]rJ_өS,K 6[󵜿[}zBbIIɬGb/=uB[[۸1c&#f<}.9-`1?bE8Aч|dx`ѻG~&ԂW}H E~Ԭϝ^.Iɩ/S2$$=ݦ/Y4%$&w 6R̵P(5G\\|C 7bOocm psuzg/twV-9۩TAL. tʉS->{ HBb Dcyy K&LABv:q AMBvRT*/  sێfV=iofX,A_~AVRZ xK[m;vz֝{4˗:200*5=b{aÖAt8c:"~FVcIiɌOLglKs &yXt[dz/imm}2d6όKHAWv㶳ݲ`O|b՝7nYt9XYYelbD60E^cѻ,&f%%?yfFqR;!&$&3̜džLWYj+pthmmEIinF$ȱ VU􊏟N>9% AG&f>Gԙ!;Ԭz]Ə36AOyy3g_nߵ=5\HHHBlDke+-)d׭]nߍ-;ڍcG)+)U̘:p$I)& ݉뵴}Mop8ΕŅB&S7ox9{B=klX-+>/KEEyp'ycy^&YI k=w-yO-{w B7K"A/2@JRr܉j6}Rw @\B4 `i6-19p8UYhtiY91WU|+o.N{p:;;߽mX< F= ^wWɔu8N|B2"t:嫎hm`~cfиc>ۺ+~ܿ{j?YBbrlB"SEAmpAA'U_!o'  !vAABw  OAAB'  !AA  HpAA$8x   ?迗5X9,@ii١d2E_ H Gz psuZʟ'6}tL>ybXYZ3.646q^3Οص7pWY9[7Դd <ܩOA^˽^zDj:tȆukN9Y(sXя 162ܱmXBbsz_ ϻsJ1bgeeoCA'YُΝ={Ҧۯ_7o4h`[8N_oS3n]$##S[YAQpm7hij ٢7b6.Z)x3'M{bϞX6x2a 0J 57F/\ⓞho˝΍+^|e>܇Avm;x<~ɢU5555%eWAMJ%$tmy99u B$֣cG5CJJRLLG"t /!!1e!'OXOd2 ##cd8ʎ5ȟL+l@A?yRVK\\\^NLa+_sG~>|#D)l6M*BUU􃨨((A1n֓H8˗:-OMLjj؆%TA~੉LF?0ֶ6UOtt س3LovrRE =tT{wljx\O,ີ]q8: ~]OC +ztuu?h`dIzGGgggeeV$Rmg& PPTA6MPdd <{}O\LLZZp~"~AƌW" ;Oxr%!طC~`_XQgwp X˳akk[Ȯ E\L|K8>}lfkc>:mqS&OC(m-a /'?0HUEEG[I  5}k8~5hٮ*2& ?wl>)J -ͭ)v mw;ܩgD@AMZZzVfa޵7t Vsw~+Am5wT \IDAT>+!Z_o Gi[iYJJX,V=7Y_]]]ƏcX|`}:&,ȃ;_IZB (+Xt%ã',8_@ikk fu5Gx:Z .N=lKKkM#uvv֓HVAB흧EߣO3Ix<~l&qZm-MII^AԳ̬C55ZSRәL& 9% _G;3;Cq^ ocm 0C3}_? kK55t Ow%EE111?eU55U*Dut'>)(!0`eeeu`<p\4Z>:]BBD}HwnPٹ766e,wkY7nEo?uh̸$''""ٯI$%EE-MM Lq![6*(ȣv ǟ>:`芀5|_~o![6Ӟuuuسo왺3@FZ`0fA?'{;oܺ;svHᇗXwr eA?"6!N>@KK3Ʀ;6oXgkmmE|4QW'*+Md2h%EE􃔔l}%"N0Y,@"""ƌFW"/9x<NX5kû7Ο>qY4rA/[ /""p·:;;BXn.p8kKsE%%%y^)\ͳ2Nuqt g-\|Ö'nL~K|V4Sym--JHKI,Mxxu^wTWW|dx<۴*07JoL8eAZG^kOƥgb zTAK55?VZORxZƏpsMNIkj"O2YAAgzpKJI \؄eҊ~PQQ>h6h4Zkk*@Oo!d2%x󶋗c|':9`[5xyLFKJJyS(--eog?hтyq InZMMdt+&)""N~⡣9ɔ|zuMLB4ɨ#oߍ9˯Zȱm;9q p]޽z!.آ\տwqry/#= `XPVRڼa::?UX_]A}VyEz֖g0bbKC4 "..`022壠S% Rzܿy .! {xX(.IIKwuzmQgg'n0f#HI?U_;І":,!,:B)2ƒ=465 PdOš!p.fPPhyCKaԙrfϟss9Y7pg_gn?[[7??j{zZթjBIQ8|\>Hds8*zrjЅSΓ |pihfW5Bbc3o!$<Œ# Z>v2o0STsێE9GQ;!9ɪDcUNNi4 U# R9JQBd\@/ `\him/,t_i4mW; hJz7)*!>NVm;EO(ʓBWj{'$Ω BB'=H#oǾuP6B*7X(T,. JNLXf+m^NGQTm]}QaBi|`cO|?_}>N[on!IcֱX r3e}zJgr8iɉFvNaӅ]v>~υ[-m:nzfCY|P:>y'vwq'tY?;ja'/OGKőC7oCC#fX:ѱ*!:?}!z-$9}c* {i$-yxg)X#2OEEcXqvvXڛ.}q{?ע2k;OZ0#'VU5`퍎0鶶69遁`j;%]_{f5j9jT q<<<</Y73LNIENDB`gammaray-2.11.3/docs/manual/images/gammaray-standard-paths.png000066400000000000000000002256421413310443000242710ustar00rootroot00000000000000PNG  IHDRNj pHYs+ IDATxwG H}P@x^%QtjVkzfgݽwooٽV)z/zMV(4Ŧ2 U ~+22#J$S@RT3RT{SJR-SjVTeJ *JL#@'O=v" D''0qj@t|(Z\9rD,aMC4+pݾZkbrr]3SS3s+|u~zf>~=I?O >z`(\xi (r)ͶR#wנuZm,XQQag8nphxfvVQt8v89|q p1IRjݼicϭ^>5=sP(\_[c._Z]]}gs8WUUf|YILF# zNT:NS%Jast`2aY`8F|FaEQ&Q adeYĊ\ - }{3'}zat$I$I"B8 *-f(Tr8&2}qо==9&ܽyI|>?Mӳ3_70 y a\R=.>^x|gED"Ihyu'ȲJr  | =~  Ï>ݹsH$ EՑe$@t{!X,iA,aaR:v}rj AХC"@eAu:3:v;/ҎtFDYls\+$$i67=>:(RgW,+Z[yJK> b$IJ  F#"VFfs~,O-_޷T= Ay@090Q1 ! @QT%E$IBDEQ !(߄<#\믾|YFI`e EADQEQ $)y~Gk~- ,KwQҎKH C|kVEaYNe C1 exA(2 Ò,÷0 0 et&9uo~A0Ly 8< s7V$Jz#@D"m[6LƇS.|:M׭-). ʲ|[~|R*jYyxZRTߋHJR-SjVTeJ *JLZR)5@T*2xSii&{~(*JÉ _ל}!$^>:ɤdzԹxڂ Tmk2 s;2?ڀ;g0d<W=|`Df\dުm*JLZRQ%IOoJR=ZǍOL"R]UyϑQ+I&l>j[o\|kOj'._q}Ne&=p%"­>Q|,":Eq~ޗcH[,˥әoG|mkEQOކ TZʼnI/,Ȳ<:6@陙4J<|4N: zT8./+-,,{L?;ߧH4^X8tl0h0 ! `dVo_8hY?1̷a ֭]ۼ>ҙ矞Q`'޾~^X c˟~g3 {ښlPVo,3LS3P_?\NNNMzOf?qaph8 L陙Y'Pj3SS&7{OZL: -MH_IEb[}0 gt EQzd23~5rrjzzf6JY-ohh` g_|XOQ^Z-ح~^΍OLNQuk'&gg-wa/,OMLlzf@c'N0|U- U_:lY\NO>BDݓf).c 656  |0 c2  NGI (*+?ՙnWAhuux:1V{H&q^/Ag:ٵZ͏EsnNuF#Jx)_ 'OdZ-/]Iҏ:?D۽zUK&IW]p2q&)0_:t:?E]4L7ڢŋcͶ`+x<` MO=vDQٯf缷z܅GV;Ȳ#.,~y4$IpAŢ"Owϭx"q r/MN?pILa:08EQ ΌOuZ,c'NNAH$4rqqVxgY+V%峚LiΙ9A2?x O:H$ nu_QQf?qJň;#k([PN@d0HmeY919A8{kkW~EQZ*3̚UPq)Ogm ج6^VZBQT&aӬ,s,CmےJg;:%Ir:e <`jzssE7U0E7nX)+zuzzyXl2Ԭx~aaazfvhxDIJ,"eeiRxxEE’bFM3333ss܃R-sУc׮;o(zd*55=hzk5+W&Z6J1 x\qEQ efӪ>/g7=^UY X慅Q}W ѐ߯,V ڑҥtJJILEv(--)-.*))nlhx$]❟xGҕ ǯ\>95<Ev{{{'˲th4.JD2i}>?ήnwqV AVܜ\pnkzzF[{"Jӹv!WÃЍm%|<M FFNJ<:u)Ñ^ohjiZee+W=?F,d XWw0=V=cUGb)@sWWW Q yWVK(z|wRMϫ57*S'&3 +r6JR=y8 q={zkВ,Zx=6?!V$ˏ?+#;zz4a:=<|bGG\QdIHfCzMOoF v'7J9o{gW q߶c_foL9wR^8b 7m]ݡp.39yb5$yӳ`0F\fLf8#c]㓢x,IҨ,ATOxzWH$׮hhOӣcP5睟!V_Eyy<O&S!xdl $g~EukLfdl\$^FFX42:AP[=6VK) cYN|Px!1Qd2#u5gΞs~l|baaj0 349Rs޹yS%hO$c V/ZY]cX,β짟;0'8X,XB%IJ>ɩi&&FC𹋗^H$:{Golq= /,D._v.Ȳ?Hx]ms;wO$'O pȱ,,] r19543;g0 ܮ`(),d RLAG`.,p[fEs^/M%żCat#Ѩnf띯(/ C$~qP P^Vw`a[h4vd@m#ݾv|~*.,,rXi:l1KqGQ$H (jD6(y_Ue%Ei#h4t) e01 s9PT (ad٦gP-+-YXx nI.,,ecǷm),z|A,75;őH$ؽͪ!@0TXtP8 zÏ;w‚Ͼ@l6e0l~nh4<Ǔ$'\.W$Ax1uv- 3b(I%L$cΞ;+/=,-O/^jl$Iɲ,REDA` ySPTDY8) 8VyA Ib=F}bxWwϋ A_|yxCG߷~/?s;whd[Yݲyӱ'+˻{V^5::VTr믾s/556t:?p͂i808~ӡ(͓|Qga[1 z$(z JA4AEX5:?UI($I9@Y,2E&@Qfx ,$J(lXo۲캵uz=IY.-)UQQ~ٹD"^;%Y^U*JwwvM EQz8/4oz(ѐ P1[tb6L&0>>v9 (-)VTji޾m RT;} .+?)jEyY&ٰaht]>|v`6 vy^fji1 +++rMVWUUeenw.0nn64m4 vd\?|G*J[GwrPR~rPRT˔U*jRJR-SjVTeJ *JLZR)5@T*2hJZRT˔U*j;@˲@$IYNɩoRH425/JH{w IDAT,;T?ǃtj*,0 7i9Q1LTyV(TmJ'FaW%H?<2:::g_x7:J$YzHPYtC /fl[{l0LuVTb{^n?u&/Dh AښTny@`0zno C|:VeeUgw7 Zn"+stC1 7_f/_yW[o'ɱgokܹcׯTWә a ꛝzfOoFCEVd}ᑑѱɩi Ȱ,KTzz;xCCL& zn]өTlݼd2 ŁC+VTT(_fUqQQwOrԙνkϞ{X0ߴqlڲy`~QZm`0g.ݶ}fkf[VT(MnXv$Yqb=Zeyfnɒ csuk''' 56?Cz]ֺm&<xADDel0-`%1bq fU%r9I6MKQ  ^^`F$J,n7Ea'l6d4(xqSwU|KK, 4m6%Y$ A Bkʧ uk,\Q0L[7od2ݾi3VF#.YJtΥ5+ :o_Y]O0&`ۖ?R$ݻ{W&uzJ)+-G.^`u0 u-6:svaaSXdYA(ʊb6p 9Nʲia FCRcX~^pa2\scAÏ$Iڷww]_W^X(&[Etv{Ï>}`( ?~Y2?VVW>zUTF0_ޣΈ.3]VTȲRCs3 @R=-Jr:e (qaH}Ad?Z$Av*J@9@?cqrD"J%-@;T%wQT: TND"dYvV/Tf[7^T*=080̏Ɍ, d2D"fP{?t~4m6v( h4 DQr8Nh4eQx^E94E%IKi%IQq\rP} R.GcF,4CPq CpKSj$s(h4eeIY~d2_?[l j4M˲(I ?E^-EIDr8β(J#r܅w@8 H|':w`dtq$A`V!QeVE9 'tZmA:t&㝟/(p( ð 0 DQ (EQ ((( 훘lDu5kV8y-E!<DzA@80Ax>:li,ʲ,N=}ܦMhz s$I?[>b4ҙ[o~Q-E _>z|U_8X^V N_j/--I&SyшcX"Idz)]\|9L}{{#hYiIqX304\_W y1Ꮯ!Ʉ?K}{^ᕗ.ajZVo$I[l>s+˕L*ol`(mV[D}PI7q,Paa^ڿd2NS]U PEQda߷o^yp<`9n`pH-7߷'y 7nX_UYa ~Iřٹ^}tjzzbbW_^vM*xyZ ܲ$ ZGFFQ}iߞ۶x?:eW^ڿg}{^|E"Ž[ ovRTgwO.zөa߷gwqqe%׭-).Ҵ`x0--)Fr=/j)*ϯ 9q\u/ og2Am(J( atfqͪ80<ϳ +\d46 C\.L&IG2L2,zT E N}}H4j6v "E\m_x~v{Zk~h!^^ڿOO~rzZ}~bdQEzq(ExsX,>== @`P_+/7A($O$ ؿwwxaqE)L2 Ci4^¢b^|,pҚիx2D<==vA(Fc֭Y]XX0>>195v.d<~`br`U|vq80  (tiݚ5.\zЧǺ֞" Ű|qHe|A0 Rv54Կ+oԌN5MC#0I$ɒdj4EQ^"ɢ(u: ,]/Ε@$r*.pz}$]XbX2FHP # U-os7vHf+\͟GϏ:`Bn0# ɲon <[6o٬%EEzz}Iqhcc}Qh4:;EilvhxTvբh(fn΋ Јѱd*mʲҒQ^jVڪH$(0<:t:4r~@0 e%eӳoڸ-6Oh4"S۴ZnO$BNp80 s:&1 P8mfN"`8v .gII(pPtPMsSc"5 E aZvlhs4M[,fr:qYe8tZî$YX]TX"$A('p\qŲ}^GQɩw>k C`Iq1AիZ<Ng:fik׬v\fvADQ枞^[vMۥV-8:c VLV@3ݮ:'(0,I 奵Xd4~<>-WǘV݃  Q Bca!Aɲ$K I#Po ,k0TգA@ՂГ),P, tZKQBo 4;Β;_ZEQ;Y0̰ZUâ(zJKl6@`($ˊf!$r:*JS;@r9'׏vFc@( (pav-N8{ĪT*NwWPRRʷHi:GMrA SPT74 VEZVQ 9AAp:6 aY%|(Y-wTRTML&L&7@/k|_aJRD wuRTLŧRT˔U*jRJR-SjVTeJ *JLZR)5@T*2hJZRT ##4M?جTòPXyT7z'ɓzV:j9 éd}0 zAҙL i,q<Ag4 CA$YVsJH$F#,ˊ1\4.cKxDd2Sg 4648H4Z^^6=3njݓf7=Ay1-.*"17]nl\ HyYi_ch|jzf-$A\q-6rL4ݲyP_m˦.y3@O{EeǓ»o08>>yFVHRj)_7YtXl/_r{fS/9z ]ݹ\tp)Nc(q\`WϭY%IZ*n ONN|&ȒLN:s3$  754LQTscC$]\ttv566>z<O\z}-(*rf"r9:wSXp3gYfî޼pVKy_d0 Ng]MZ~bE$ Aa98'Ad2gϝo`ܲil{YY}GQ n]֬!h0h)a'f(:venqX_ޠO&Sl68I AQ40$+J$E(J&M$X YfFhZF _PәbY-eYŢHO,᰻NN?0=3kx 3,y}%IzG^}#@!G|۷珝8:v? "hScCuUN}}Zotu108cGUNc5+5J (1 4jfիZz/p0M.C 6I$@0(" d2X,Fa!It:M9l<+2c'˲( A0$(J$yA4 ü Q@QԝX438 ;S@p>NhA0->Cͪ{]< A|J˝uBmpP0Ò?X,Ju.^,Ǎ}߽ J$L r9fyJ?^ta{~Jiп~Ǐy}Lfm&ÏK33@`0h_'H$?=zY1e͇+C׮Ch,_NR} C$I:rDuu%~\k!V=&<$I?pYEQ֞,tsnn|bb2oݺ @@վms޺ڕfkNй\YiI}]m< I͞/|ѧ65HnO]@YV*WT_|y(c&֭8ͬYϿT!bN{k|~}BgWONFa:FDIjYy)InT"OsI 81̭~AoX{Hܰ~qZVDQ5xAEJ`p`prŊh4م/!ŷngh\.ݻqT=:O{ ѧ8xs_ڷg]]O$;wl{7GJkz}vcI|>/~uk1 s:#ce{v29xͦiA|պښ9`?~70 9/rwߒ$r5mܰ^Vd)|BAݎa B*B&%p7_gX束1::z zҕkX$p-E xG`8IgYPUJ2L&f4fT*(I 4J"(r\s9_ P]U p9U=z}V_6};wlV7  ve>GI 7^3D"Lj) `1eEVVWs.qE$ɲ̰,sy[e>z0" M4M88I(*m~{5+YEQ]0AXxEQyMŜ \rm-:A# 0LjH 0LVdN[R\c7^{yxdh0\ $Ie L&1$I8v^\eYeyystfcx"Ddϋ漞BA`>{NS Ğ{73={` z-wNB F(PBzz: {3{mﻪ|>4\.H!@ 3zS̸F0T! ǵ=|wY/v ;c6وX<^XaY IDATX^nNO@(~XqfغښnE{zssJKf'bx}]͝ΒRzn$I!f9uCF!''=40 k˓$,bVڷooFz:ryq,).Hdzfj6t[]U)2˲yq,,7 ;:곳2f&7'm76glV$%%fZ-4#ᨪt:36Mv=P(͚P(Zf3$'LgFzzBHa(++ѨՋKKΌ J;JmD.).ð‚^燆GkkA??=lVBD4}}f~eN޾v-eS"dz,+˒,N+JRJ8a(hD^%IAe9 +8HH ~0~{}j5v/gTVgb~}aRv,({ :ߧz]|'wTAЃgВ$ Cx^C7%Q:zP(6/;zy7ny}}Fcur/j4_;7{X| v h=nv9Y8YUQa6? A;qhl6!gp,] h] h] h] h] h] h] hX  U!zܛ>N >CУb[ۮs.F"h40,qd, ,H$I$IR DZ,h4ʲ,I&)q rGWķ~{z=IH&;WWhbYʵbxwOO |Gʲ<=3Aʵx|m}cbrzk%r$# oLNxɩi$eY^][_80>95=3 뛜d2llnmmoGѥ嵵5abx$ BD"MMφa*xEVV'h=έolJ2;7OQ?73Xl|bjum}g!Hf}-+(Dc^295Rzz^~ ~~=AB}}}C}v'N675dz;Ih L&SwO_EE9@2 EQ^{766Yep:3WVAX_Hs8 A,4;vYdYe7^0Osc`haXMR!242*KJDQAA@.+-mij߶?==-??d2a@$==M׻=^RXX\b90&p\2 xFF3#cֹbYS04/?nG\~^. BQD׍MPUQj['ɮښ^<ӶooǷ45&iztlxqiYof1YuN8j4/=27?r3 u\iI q3C+k33`'p @^n֧_s8mss+;;Kߟ'Y󧥧y4ח\،FY$Ied#iZI @AQd " dAxDQŌaX"T*Ah&pAFRmkەͲҲNI$D0m1@FFzFzNKx#I>$ 1>|;qr`hq܍[o,_yOeM&s. GQAa8~BGF9kk?@t,{c3s$I]X\lk~CÇSU^VO? $vnMmյD2YYQtfT(Z[66R:<2j1:xKK٬4MOLLf46[woN=rX_pXuFj.\DFFz=/_q<=QUU jnjXZ^Y\Z1lhxFPfuun@  {}=O0CC#˯Zk4<:WQQ~um6k lnsx ~_P$IaWVzA_\T(e.yJ)(,ǥ.]IPt~qǰLgNvZJLłaXOoqiiգP(_8rWV1 m{666Ovd_\\jinj=V 3,xyX"p=oLjm{~xYid,++9CQ4UTs*a(Id44JxÑG~ {EaT(DQLDeѨhVEa0 kXYJ"uVcER:++d2y<^$iN&,24CaRx kkknbQ)$I(QJZ2y(QR\T^VҜH&)DQTռF'i( 'OBaAedgg˲ B(nkkbq=;7O2a((&2ab&P:8xyr,YeI00QZר5$qa.^c7q 8D777Hzoi G& lnijvbvvV2LOK3b6rbf?RPZ,^w`hHR:Q~Om6[Ks㙳Zm۾A=1++b׏`(8xJ՗_r{(Ѡ /,eU7n#_9[T(WgeggtZٌbZ*//r^/bQQ^v]ޠhTW:}d2g[-A z=cP8/7%Lfge%IŌ bQ%'OufPłln|Ʀliaw  pfIeYVT<Q*Id4)8d28.JJrm8dIAnRѱ4G۾(NNOk=}JʬFc9YnܤHnm}05=dm6EuZ-VET" #67Ff͞j-3ax,?{j1 q<lVh4h5=էN--)6 e٬:sz>??oqiAVaȨsfn?QO*Qe|O?t_wolޡ=H܅KFϞW.nD Ah$YhЋq1LPAY C,yd6˲rrUJe$b rO>7 @ٵ꯿8߻sro,jnh/=ZVUǾYv- ?rn({R(]uOz r~Εz$$Sfolխm>wmmwOvdDQ|(HBdY$)Iۢ(2 ie%Iyx翵xjo>LمAb8IQ߱HgɩiI&76KWN9 ~ىH$r/Ҋ~ J}'&$Ij}g`pk'Ov,jsYO>{8ILܙs=<5=s{L&'RťU=^߶v{5݃8ǛJS ^xBdwO\- ]|53 Q8V=n⨮3YNnVQY-]Y7xM:{b,Ouf߾cCHX-TCyYi(.).qV}{5jS&<xkXfСA{,N%(QEQyF1ARC3s<ie)8iEF eY8=3p֊gf7JKTWܞ#MLNJT^Vr]ښUI#[-VK͞^J23vvPZRv:3FFǖW67}_:GMNM/..ݼuN{ờ]PYIx4:fO5NҡPc2ADQ CxŧhV5{jJ%"˲h,(/sU=$J oR N BWG"qIH\][vE10ea*A$Ž>Ixz}P("銊Bk#Ѩg&&jj`86>1Y[SsuA'j ECCIR^޺}'LloVTyL[[_gY7xm}c3564[wx @(Ztz{}^ *Rѓd# f4EVWUC,EFF.^X_W]Ui>/.gdt\TTʧ\Uڂ Hj*ASWT*xe%%(&I߯jJJ 4ʪ,8$I6 -͍Zd2'@0h2f|AW2J% [ۮ\nQjUFzqvՆ#BQ/_65=ѨqGQk0 C T*PEw< pAq0,p Ec'895ӷ3Gh0  U|8ti>e٭-WKsZ%CE\F};U г*RTfϝEhDz\EYّ#^_vv$Jnl6T*^ @cfP 9>NQVaϯj|dfO(C~nJ@^O,FAVfΌRߗ\]]FDqgF:`cc˯Δlmo}ՙb ôZ4_|Y_W 0E'OVTj*';SNVtS.IzzD"+ӹzd3Q% -(ͭmhx䄷U˲$P4ieDA0RL8: DQx@F ~&p$"#A;o w˖c*Vjs!R*Jt+_L?N0=i6k}mmjb675?C?0Qv^ホVZGfA$b#2H狢(s>@gw#ro_H%#n_߁SoIbpFsl„Y]ݽ__Ɔ>  ltny=}Z19w^:k584\W[#Ij4CNgFsSc"XX\s#>x`dtaYFaYR DX$bTUTܹt$?0 G =Aw;:S?Ȳl4{B8zo|gг)H `nnfѱ & IDATS3fOHcCԌhhii"^x3gsrkTh4);M @o ll_X,/+_b?&mnq|jzѽM7onmiuNwoᅅ7n0 C_ZZ>wRsSZgwޖ‚RGtj܎ή{O6`یF#Htqii~a);+X__;4<'LgNj5!L޾ M&ӟᑱ7%CA)h01: 0Gq 8qeu:ݽb( a$YPD 3@ ''4M(bh_ EQ@A=wcX_u R_ǻv {~}YA(!LPupCR0@CR0@CR0@CR0@CR0@CR0@CR0@CR0@C2a&vW"ȏollL(^Y]{ķ'əٹR{8t{ׅ,˾\0 xJmM‚(/]=Vqol8+/:32B;oBA~~۾;xğ=7q475 x>DjJQ&jQ_[# C[,EX,n6>|]RD r,56c(j4WV^zIJ?ᰯo({gOuiu86=J%w;7?///7Bff'& ?xV* AApaXaP Sb8c*˲d,/+{DmMJXQ2*019E 8't:]"xp$;+fOQQXaA~zcsk ԸEJ #pEP_O>*ZP, L& C*}/ V4}Ξoh4A8G/"eNwʹ4  C' #(MEWL&DU0 0Ex,W8$Iok=s>:엿0 3P?66ܴضo4932nܼxI:r5 y^/#=mfny %E;XG7NzRbq RVV&q9kޖ4Y23$45nmoMMF@IqaiuZh6ޠjjFu:Ng4M&^X6-';K)bZFcٌFVZ,JrkkfZ2P_ݫ겲2F飣S3/;%EZv~qљ 3#c‚p8hFFf0,--'15JdIJKs9yy]6% 쩮7Y%EE=y9e(d 閖gfl=BsQ vJH2O_$SWg mEQDP,+Re0dIRTߜqvp~yO>7 @|vn ?aqiS]e 0:6aY*+@pt| ?/eefMɩҞ~Z33htxd433pfv.c(ؠP<D׿8@(+Eqeuju5lQJ߿OP<т=W8x`zfVffUe㝜l3P(?/oumjSYV 832DQ0,PT34R>aΌH$*"A$IgfՂ"(P(Q]{ =wYS3#8q,su5ii;/Afs((OtgW'pT D'*+-#Iҗ_)*,}|yLA~^FzJɾtZ*++vmnn>{>;+kddtiy iǶg?R۾֮>g60k}d2}- rbUU?8߁d6 ^vOW䉓*+Μ ^d>;n|p ioyəٹ-W`oX{ƭvA66pF ݸyƭTOT*EPAjZRxpZ @?P_gZ3Ҋ &**UzZfl=';n˒l6lV^7 &ftZVk6 BAhZɔm4*R0 V‚ɩ=Օ67p KP(fS$/_MS8z Q6exu$ItD"j0 Bbј$I&e9Z%B,W4CR)5ju,jX,RR'D8k4h4F(J H4STZaXT#b5ju2Ik4$MUp$T(P0HJjIZF1Q-$I:HjǥW*DRiqeY^'VEaD"l1J*jefAMͭbFHeᰙM&JE^^(JqQqiuHCɔH$z}Th4(.{IμEQea( E  (J,ޛՁvIA=!z {o&{`D~@,  KqcS\}D"SAϥt2D2-I(ð~33 NNM,H&'GF9s= KqizyeUvt/4us B ?8 ?;qryenGgaphzfnr Ya5AЏpV({{z+k++kk,i4w_|,ˑHvKX_g6VjڝA3[nbTVb1k-s^^nIIqWP7:6^_Wxsz{o(+->t`;f A1tE$ID%ǙM`0İb049‘fFcjZTAQv{"Pb1F&n Aw7; L&@`~L@Ho7KljrBlX-'R'=Å z AK AK AK AK AK AK A( H;0@C2緶]?r#DžBԬ`O,˩Y =q 7 X1$"ɻ]C##g1 3 ZfqiaYՒH$Ñ,.ch4l2]htss>8;;k%c8i}{[P`9ՙsyM kkjL"oY#XIq$qw:܏fbP,H 0jZkQpOOsOTWWU*HQTjtFr{J`~a8 p #4MI*hn' b6|vvfCRS]}Ŷ}{=AdeH,f2?ݻq2,z>Yu5{p O*?v{=^_(^Z^%E55f7;df:qHG$Ԩu58YYQn4 Ý932߇b 4M* bZVfMKs-,fdPd2ڬVb6fj5b6+ JdTUYYYYNZ]YI%.^r:I~縜,ɔNOA~O76^~|㗮\mjrX?ZRRP* $7v> AA>UƆzAwy CQA zC$UJeuU^cXͭmZMRԫtohId}FQAZRUVT{ e{T#r 傂BUe ='p| IR@ wJ a2DV=ʏt7A?|Gi7$Ias9༚c.LT),ˋKlۍMn#ah$iqiaqi)u &,S@QqX,ZG"ÉDnmR_6A|>A_c6=ՙsgΜ$voi$WVVx_, eCp8y>NX8Ih4JIQheYp8,#mK$mmvvvx \[[[YYe9&IYÑȽ\"~ +-o% zlomk$t6HqӮ 4Ҙiaoz-|U>Mrl}`<|U#+_?f˰8zF<"UXվ={9;uSKW=n}<:6VT60( IDATw\7(<j>s6H$8) d9u xpxxKXl~>TZR|+Wl6$I]׏8933t:z&&ظ/a3S}pjiIq$eJwwScǟ쫫}wwn,tuzw|yY/_nL qG<0bZ_nfނjo;u>HTM[!TT έ\JQ(W{z''󚪭hm-knڰ~71PMѱ9?Br6ffg+*ׯ똙5Ѻ|krW[mDիWAlݲiӆ׮575^n pZ_08Gf ]/4(pO`]mM_tx򕪪ehP]]U]Uqú͛6VhM${Y{WH8Eua ~ *+6nXu˦W{/[FS,q<'Is,dB"Iif:S_ea:yF)JI\!0(eBlVE$E4 2 MbW>vA$Qw6ð z㏒$6Xqg 0l/|7cY t]'"#qiA,[٤XBN0 {8)vf6{*4L6˲,BЍb rob^UU0`FUUMhn˲BUUu]_t=at]/ !z ð%z. d"ilV[4)s,ˎʲ,DZ<g"(--%Id2hbhd2I4s~$d @2l8f eyۅ4a4B8AdY\NŅ=!Xv&)JlONMf@eeÁ(J5Il6fZ{]s ð%'x""I(MRE\.WCS@u]B #B(14 I$ qd٬AD!!es,%I:I49s1 AWVVDQ PQ^NQ]z I.˲,M&Sa}>O0 + &#~KaKaDaDaDaDaDa_!'a~\>V[ByEY/ct:s?oع94-**i*O8NQl.!9>aTM3 02Cvt&Hb1I^՞J^zM~Ww`8۹# gٰnhwOO˲ӧ;hEyob]sT*uft⥺ښD"l,eBD"Q$χHx>(k.˒(s~?ga ˲&in.`ZN>ehx'!"I$SNl57$v@6X,S_¹0 ({OzCQ^-}W*mAӴ? &S)p8K4M#*`%ϓ$$JfI a,@͇BWEaYv iJgɤrSjS [tq7 nϭ}5U|0+$EE468NUL6uINӝ4L:v"gg, P j22,qH4LMMXno(WU5WV,o /W:hPiIҒ[fA#hUUSgZ[UVT_\\TZR23;{I+57u #;o=zIJ榆O(Z]]5hjom]{dJV;~tALdYUT (ںy[7=vⱝ;Μ=GdUUEqqeYБffJ89>{,}|BHQTqPVZRVZzg;ox܅)/~mӝfQ,z%%dtx<q媭fYV$m6Ɇg2ٚ2lxvx#Xx:a?Q[]D%DQToo_[r.W}]m/].).9t-ŽطTc_mw-v@{>l޷qLGʞiaK-]_"@<fI$H"D"x!h;EtZ+Qxi<m$0lAe_G$7c6_O;c~HaDaDaD}˲l6p5aآ9#T_W/?ii!=dZ&Kҧ o|ՍIg{.kjzmEk4M Tjjj$}?66ԯjo[b=`n/]޲ p󗻯y%I,.v{C MӲMW.w[8yZd eٲŨ#aNƆۼqCQw-[6oaw&>n  Hœd6Z($I麞dׯ۹} .+ }.J=1 87heAغyS4{gjlڸS\nM6c?s6UQ?;vdVJ$I{hhs#G ~ZcCaG"ee+Z[=tzu{qg:y=Eq,G 2L4#jLO4UUUfuMu]|^yJ/0(˒t൓g(J^eظolܗ||$Ie%++}X|m )Z.^bZVcŁ=EMSevPW3߾nuU>#{~ o%ǟ?EQid{q˲'Ox2DZ33@@d2519}lﵾ+W{Gff2٬XO}'N MdYrWcqŲP(|zrTUxa ·zgOmm h칮|(K\컋'٬awvP&c1t ښbI3 IRe$E*jjG]]M* ?0TQV&(L&ya`p$˂s򋻫*xt9v$!jBt _럚瞝)@#&&&|Y-4 !qp8R؞&E+1xM>ݽ@@9eO IyH@Xt]iiii$A N]|87*IE`p~HxEQ"h2r:UE^ظYIy=T( rC#lvdt#s %߼q,KeeSSVeffv*vݔ,I$!s~W$Y Å5]N4q,:ɩi. ic,jVZVF鴦j.+IO$ Y]73Dzs~?Mq,,faTl6bqIXKR$%S)YdFc\bh&BHQT m$ B0BPuaЅ` B$Il6,3@xA ;X fpIsac|KkBX(m`2& y 0T'̲LagqS@4 C$, ].Mх/zM À *|,..A4 ~~NC.|OB˰flp,[\\I(w PU\ {P6k= ˽ .(995eM$DI$H4;;k6y>;vQAn[D!4MΙYEQHHq\#[%Y6% L&`H^}v9AtCo` x򲲅m6f[r)ʲ\Y y_{v TS8+_\!cF z=]5⺽>_JKK 4m6*+_~q7x.ss~պv횷04leYs,DtzMx!8xHӴm$e .ε\oSSO<|>߶UմKz۱^W_W_yPÆaڹEQ#bIR={l6ez3gfl6kٶly\ |'>wjd2'57沫SjkJ+W+˝N+=뱉KpL,zl'y>ut-g:>Sw\k--ݳ7LfRsL&H$۱ /6-Yx&E9zxiiIׅ dJQgzr>z'{z͇B+ڹl!/_۱cۙγW{*v,I'QSSmۖH$:8xUe哏:}).[lٲ+==eulinnZEӴg~r~>3Ou_ vvݸvO Ap:/[yQXxeˆL6W$qEyEQI )2(a\=(B:$I%VxdZ(w70b^ܴyF,4HD$(뺮i:An蚮i,˞:(D|sSSUU=/z{a v;Amm[7TWAU-u % IR4EY,fәH$GFF︦'Oٴa=IDQdVe H&$ى-a_,vGOh]דTUU%A$]֗RTyEy",/\.MNMoݼIյήkVܼySEY`_IIԴf2 71a1׬j޸a=MӢ[H$ZW[s* QZR?0499Y\TZeEy<(O$np8RVZP03UUd !4<2j*++4Mf`H$_lk[Y^VE(r^ #^SdvX}I$vuu$I&);obbEraFFFs( ed,H&Sd*595}Q\'أ仂j싑NNt-כ7$|9V ^wm\P}o~H@ׅ_x^fɩώxI~M7e۾~EN).*%YS܇K`طb-Q8@c-Q_ IxMpvZ ð{քͷa L6xa*r!$\ٮի<$PD"O$֬^UUYqБx"ks]$ڹ0 nj#3 yw={KU@ pĉkZLccÓ:uPUU9 @$izyKϼaDpP'_M0ٮtV ,clYּm߁CH^0!RǤYp=FKPҿM]lO#djoxvYjC4M\.MQ$IznsҕR=n x3c=+RRLB/5S# ;ogE];0[=::6;_xzs (+w%0 ~hA[5ߟ`a73 1 Ö(1 Ö/8xXW08ma/H~t{a-%a= $Hc,v'pZ}];Blb6 ە*Y(aK I@p711;;TW4GL&9WX؅Ű/Gq`S\\\^^V\\e&ټe@ ۶ /vI1+}mz2\&eM10ufU;2D"!,^},awŭ /w8[Ou}vbd}~e~}ou?}x2M"=}Z`ح6m\/ ۷mTuۖ͂o۲Ym[@,ffݴabþM}Ъ \BtP=oxz4uD<^o?TUUN~sm? ԰bU*е|.>r=O{˪dž#,JL6xK+. Bضal,=5׳ܘPUYqj]mlZa7vSoW7VT66,ouMNQleM'\oih2;z@XYyd_$}nbg gG?za9۫ju '4/oe2$Emzp(eҩ՛vxk!a}!$ 1 Tȟ=n+B6n4흟amwI*,9$E麦(H`ŹX$aIVdq@y:ܞ/ uȇ\6!a-fXB-;~pEǦʚuGMӒWc_@0d;F)@2Ӈ>V6fCi%B6[(&H W]St']I4M<}Eأ5{4i۷4}7pMNw\]ma"4U̖aٿT*uStׅ ۷nYb,|^9ug{d:ijlΝ;aۖ{D݅m[64=08~m+ݽ}͍ uuI%%%v>r4Nܾu7Q\u8lg2m[dI:zD<ܹ} IJ];XsND7ndD˲+iyexddy%?4tCGv6׎76OMM}~]3g}(=H&)y^W^~7J:r/<224q\2T5)( nLf3dM|8LDsS޹z=(rL&c6u?hnjl.'"sJ9~l] Rlb&Nn(.aM䵾ZF]=K6o!,).)pif3LәP$+=M ,r(aV(Sejz:OhVxm ^D4s@UUŝcZ¨I/+-xܲ$H@BEH(nܰ}RA{$nݼĩӆ6o\vhyYvMel( M {?2:fU4M%?9$B†$[6oܳ >sg$DI$("q;o3L$ O=+L!DIҒ_WEH@I7_c;Q]S]f 0KSNȲ,@7 ٜJE$H]Yu8$I4}3 :D`򇄋R쁠iZ.d2a ,VhfhX!D*Bp:HގDC=($o9 %e_eY P9ǧd?(H,4MӴwHRPӴt&DEA4d:11ed1 {5M&gDQ`h:(H0 $ijjj Z,|^8!bh$II}p8 !,l@Pe 'fX B&0 ƾ80 [D$aDaDaDaDa_CUD"Y6pҷ  WWW<D Ô߃a?ؚ~_@0o۷JOgw}شaC7)*N$966nXNg<(rryEMA@H7 %l@fs!&4vgpp&J)ɏ|<әT:x~~08uSQTf<9nvvnbr*4=::6950t,O$,rh46>3`(f{^bؗ-ht?b!:dl6CQMSA$E᰻\uU=v,:Xt??NӃCCoڎ5W{._PWW]]M.oð`>w &` Mamټtjo;|[ a g{}>l68xG?,IUoc4|?b_z C]<@2k4ӟ(ۭ(걓'7]{ EQ&:L$s}yjkj6nXw*roiN;},OvS2L'Ov\&齶uĔ,vjc^@ad*ykp5IeU创D"| Q}rb r2ZBVUc!Ƌ|)3KHDAe CbV¡l)֛+@'cTnw5SF{}n6XVoh W:AZU뛭_4!@+eq$iٳƳۨQ . j D$@Ղ^ _V/P%K @r^!ZeVw7c { 6Ӯx/9I?fW jvS Zŧ|!0 EQ +k֬iW_(g,ܾ@c~fkѫ3l1gԊ'Yp4C4p2Yː9J6#Sl4΢1fr2ZMLezQ7SH5$.3Sxlt+m g<(ː1!pPFgI~-Ir(ɄFhcgəQ/5ؓ$ukD}6O+ij. CC"kcQ,?ϸYC"8_dU(+ml)yb M$ii*FId,$UrYIt1A Ҥ uX9iFN e@(p!b bn`Cy|9i_ IDAT+) 6)qҝQrQ*^Jek5⹸}BXf-2e]U鮘SddL3NĠWQDV8Ty:^Ւ\rWԞ25egΆ Supgġ(p\]^ޠ @ΛuQ_c~*= ] j^+"~ϻD/#4=²y>nO ޸K+1<5/(bIƪb-!㍺1[ q,ڤLK ;=bFRR2ј* ^/)͆XPrn/ulBžȳQl5rQvvG%@6lgkY`aU/ʟE<=ptgGc⡡@I[J^_`^NrR3X&0Hhi1 rނ@!AALb@F-b.L">E.$HQ.$ܱlh\BAyMT.ؤ*@` $Ui[@j"$SLՈШ" T"+=B!Kr**l4d/e^W F]$5%/,ZXdaEX䅸-  *r]wa6k6\`7 ??SmrxL *?Ob&{c#p4i{r 3Ha[m3+BrT ܪбix+qV9&u/9wl9KLj'LTi^?+8;jwxE?؆ Ahsq;"d0 < eE{BU~~0 UDT U*%zkq"U2^#5N %JcᅂW ʢft0 eU`BgLB{} aB )ĸD:LF$j-24ֵ핢U6*w܍iYj ,Ʊ))wpFszD0TOC NX`FX4" S@or.J[qǑc֖e_ۧX,P(f*EѪc..~YC`T鎈hur$%u"G @P G[S %CQHp$DaJc5WN`^6TE pHu^H.]q`ɰ޷%Eeg)BQY9LHޘ3kM8QU;L|ym⣲j]Տ&*ݟ^;k#,`cݾq3A`F'jb"Ys7A~x-ko8a慅j_}@o>?{38pk4uk(rf_\rڿP._v0n3ˇGF׭x}[GG;\ruvv.9zsi9OYI,qr8"wټic_28z=o\[*n .ٺe[oFt=wcx,K&'NN%'Zϲe MM CMH$!mX,uqqq158r\("H)әeZX4P0XTU-WI$FbT(<'dYx<'3J$ J% \.W3`0P(tMb0뼮 ' PS-5I=C䞘֠hE^^ppJ)mUF#_A@j fCL#bzH#&L2c;SL(+c5";=\Lye"\6G$o b"o{=`_ܾ`  Է6D"#p};]/HKͺuO&i*FjT'C!v{#a$I]Gk)m4'Ι3˫mФ2Cp`QR'rFxxY6hAUZؠ&Chzk<6_/)bOؗ Jc_.*DY xd5"mql]Ht9S%@H]^>mzVB. 4`d&-Q9Rz)9ܮ[BlTAn/_ER|vr&gr1rL?MkdlLhLL$k\E2ȤImgIbzDZ-$@_%7ˊ)^]*B paU4"mu6Y#]^&¢MtCa.&Y]5yxJaVCШêVMl H!%Eh51qpTVx=Omy1ncYVlk<`ƹ;0Q#NXJ2Wx9 Sج!6VЕI-'BT^}1aVr?4p!H,s.-fY1Ɉ*G#AIErDVW&wy4" FU1 0o;5>*r;Td"jf,De=;RA8죲ID z&"*PADM`l2nPVec"CL$JhK1Uvz*?匮ib$QUbKoFHYk<#~*d"5\O4DTKL;2g ]\ND:$,휹2\0X@*+|C"ę/n`G J/g- [CHOJw@JpJ&5S&Y,"咤@k]ʪz_R΢`K"C]\?]i-\7pkv/|豦FR\t?cSD"UP0MLVU|PV@!_HHdvvP(tt;勥RSS̳;cwvtz<ru\\\\~@5qZ"]ސJ]zdt }~TTt=˺2ɾrG w{T*|/\կ377˹~0=3sL6ɧ࣏˕[o[(,˚:tm;91٠og^r)577S vJe&ߟNg-Z[@J9P(u˦P8mۖZ͜Run\βH$ڲ|yOKKs-vSЫWlii޼i͛_m㓓jRLNNI)`pEiH^ R</\R^to` "" = HctAJ95=`C☚nlhJ)r[[kTNBcLR<077oFG{aRi|b2L$J2>>FB4-J ٹƆ\sS|:N@@?33J%GFC`&uz n.7&Krqqqyp|#A< !U....)vqqqyHqˣ QşnGщLi^c_9Nd aJt|!}S~O^x(q<XdƩZ PI aEv{t"D *ǰ*MT XwZ-đ<#IMbQ*O/KgAaJT0;~}!wFٹD؉2੼SE!Ke%`4UH yFh2ĴI;:*V1ZaE<f5rȯ9l5x9jOk%tϛ H+|dL@ X`]^=>IMBʧd^ .MTWyML9UL{E`m_+)iBrQ9pszip-̖yhȞ}<f:MAvp*M|"-jUv"F>bFCtjU~Rd:>$~ V艜Zdؠ9Q#h('jXV rF&my_((˼tN1ʺ[`WJ5nSppm./_`&]QZIUN*hGmavH /E@*"*"xJ?PS` ccU`\h50 AE58` Ԉ +GeHԨ%K% B"k@E(2H VłOPp(9`&|-e5Diԏą"a 6ph IDATip٢EH|΂>V0cD%P T*W  h3EL MP1UD!±AdRY8 vqw23ye&)^>a!S9UYD<\ˣx,8Gդ=m2\Ԙ|\&7 w=15lGG{4t&cV /xLӴL&O$jql$J8]"|.q ߍk`=u:ӯs*=>>TQ?ݲp4hz]*(&3{'_z</uK/իG: YveMZ&m[>]]?"TL>: -o3O6H;S sw]o[ٽ{v۵Z-ygw=25557гlٷ ?/}B I?˟ϟ HAD ۳=?(9]{JL& 2j~!DҴ29 L;|n~~zfֶW?@f33Z],]\.¥귻JcR FƮ]左/^|_)e&?vC !DX RJqbijzzn~qreb:==3[~ !ҙ^s.օBds?" gW('ℐ`0J&bh<D"X, ^4陙h$ܐJ1EQkGSݵ bSSc:-0 ӲҋZv61CmN3寷mewjj;Nz1͹R3ixթ陆B,\MU BT֪øxwo311fⱘ^dKjfuBH6+ʆa-;坃)Q.^u4t&C % |Rfs9MU)\tG'Ogsp8d2TJQR:C1-˶ΙØ8msce#Zٶ8SeW.]X\ljjt'&'g{uӴTUCD<_({׭]L+ժa V-ӵy閖!kiiB(m/!oZ}D<~9ǟ~8<2:3;XT-t:zP(B߸153J&n ?}\P8{BG[[llL:zDT>}Z,sbaqos۶lN3 ]VlVU˲,۲!Js( %B/M.+w á`PHh/}FAb3F(w^L$:~8VU#pGA;{޶es{[mg]x݃7oo~W*%cOz}>lͪUow0ɜ9{ֶ[}ͅs.,[}f(>s+i؅K'.^J%_8'O#XӲ>MoOu칙Y˲?a[윔ϏOf@#;[w~mʩ3gZ[˕ǟ|'TU''rKs#G:;_8=3;;7_VKR.sGGǓĥ+Wnәѱ[ClvzvaT584?q[6Cٹkדn t&s}}>oxdsSc'֬^9<:zEǶ3|]<3|y8z罃Onjz֞xx'㱣ǎ悗˕ztfxx܅H g]hjjo^ojl|g4M{mǾzF6:s:׮Y=33;;7ԩT"y/r5\n~a፷ީToiG,˚o,[ƛlټ>s/8355sپB;#G۶}ɮHG{㠠oIZ}L6d˥rR=˺;;;^|~_< /\LSrŲb/BٹWfB899Դu˦H$8l|bW^noo ^za ߫uRΖJɳ.=w)4>1y\>_.폈e(_x9W,LaZK/B4;7l6[*o NNNCp8 cLiZTT5``qqRYl۶m\DцTJrf2U(UhGG{[kk}l~v(455^{駄'N.W*bizjWr]s_쵝oo{[ʕ/kzRxQqnܸfj˲ ]ׄM;n W^=+^ 4 [CCׯ]o˷l$' !e54?m B4u1i^?yy?c qێ85t{xݚo_X5x{U7Krf^|uWޖ {z<x»@?*[|A]BHUQ!igw=OVu]Qա|!v{x$;qrLsE nm۲B)E@߯'s;h$Ν_vڵBLhoO&kVt{S8(;m[TJX\m``ڵ=~bBe;UUMD$D#9mK)DR}~UU; uMWU5A)e= 33x;]{666x=ID|SMK|<+ (+ss**"+W>w{Jz<[l>zĦ o MMMswX\j|!qͪUOl۪*7[Z%sLf^]Z8q{d?y+LgY#V.\N A_֪M 3mBKQK/ܞ}||1nhH|>!M֏OLnڸ  %qϗL${ߺUTcS>4t{Ӧ -]]CC,jk׬^rbcCC[k˚իn]޳; & ]c훸as @׵e[6m\\L3U+W44U B֯+J#ch4|1 RX hH%nL<Bd" %p(nJ622áp{[I%Sh4L4Rk׮ijh(X4H4RcNUU|>_2BZlnnjnny}Р(JfRɆTjú5JyM##-]7lkHw/>ࢷw0&&⎧>mkSScgGǍ7xKKs<  7m ==~/Hx޳bG[XϲoM&⺮wutyD<~_R=˺`brrB1YRU\.:;:6mܐL$&w>T\>tHC*uP(JB`* MMH$&իV|P0xf455áWwt764466tvt\q |{}܏-DgI)RJӴ(%BhZ ]7M Ðʕr(RZ3Mq0 ]Ӿ߈WuwwO74Wk5``5_Ѳ7֯[ pD!hѴ~}9󁦅mj> BiYs2[ݵmغeO^9p3?IpDSUar?HieYǏ6J(ԃX~Wf2Y8uL,mkm:Sd4'vPU%k.C....?F&Y( EQR)]8]MMdRUU03VvwwfժΎ7;;չ~\>޳{E^Guc8y:J[RC,9,&KrqqdIC]\\\Rt..B. QFJyW٘9338{ҷw"8:z;{ɫz.#SospMߴ䔦iB0:6VZ&Hg2jU4iJ)-˚#)KRfڎcz6ݐJ]|͛˺s+zB:|j1v⥖?4l6-&5Kdի|zȲƆTX^q?knj2B_[YR:TP( 9s\0BtZ}SH8|ϓG_#GZssmg_Tx?Gp#o~ƚի]899UV?z)N9{s~ȱx,y;~֖MS?P$!H)Pss3!RfiR̚f- j5˴հj5sdt!b.7M1 Gr|G{\V#ds9JȻ?4>inj:qUkk LNMÏn nin~r|kk|Dr||ҲƉɩ>0h$N>95W.f߽ɩWc7pkpfvG@$hjl8u 5M;}ܩg9vԙe]}--M;~o1ƍH$goN2uڵl6wiRJ3;7OL5ep' CsRٹyCo U?8 h~ųί\c8ݏvM.&e1|L6ᣕJ7{WL[l~wغ>mgaa\x鳷nN>333~P,:|+W]?w;J9es}͏>4_(\~sO=v巯uwoj Jșsۿȑ㻟o,[¾O??$/-ML/ڇjY/۟ʻ?0M;q{5m3;njjjk?og^fWܖs̹ɳ/\|5nڸ޿zkۻ}{Pwwb:i~\wuG <'E#ʍifgw־]"."...۝ݙF#o(Ј"AЉ A$hofBGCрC(jU] |Qof\榦t&S[SG'&7_';-+Vtu<|d%lUˊ/<as;ܺyckl.g_[Kŷ{٧zУMby8=v|ZQ\.TR$0pyy 5 c&o?qPbg Np؁B%IJQ&Mô T*z6/J^'ښjE[JW7BB/bHmM5!d||룭CágyꗿLK%kkEA8p߿Ȉ^c,BâzeYVUuttzhv')@&u ?Ow֯X4MvbM^tP(JǢQA8Gʂec\,B^v:x s%;R4Hd~`U-m'OesX4 (CCAyyzphcL)yqѢ7z<,ijyGYptGg9t:^EYvo!`s\ o?w'ʲ!BzEQLB&&& Dq|!a%Q4 #"|>_(Eqlli1!d+b1 -+W˿R[S=|W,*_b2G˿"5uS$v:߽p8lD,zx߳{'NJ] D}{&b4o~K V\948_Itl$sXeGBؾK)% "(ApM5 Sr`B&)R{v3eY*˲c')>g~B};vkߣ`Y$No|>qݞb0ɾ?O珉}{v!:.tvgc#BȥKWuCD4eYSYQJ ~=nWRU`UeftvgY. P9rɱX,1;[[S !Vee-E;gY~|>?55P{SU[ڤRp'B`W^陙O&7T(/_麓0(Ԓ `jr* 'WQqiI$Q !Դ,Mr噙(!sJ0 -BBi麮r!N5<4Csf/p tx7nX߰&7DUUVCv9Gwg``ʫ-UUݛo?1;;55sdYq6ox曳˖.w\Pr}I^**vzd \h$|'K}uv_輴l[ݶ|eY֬ds+]@@T?ydrP]m}4+hQuk|_/in0t0(J@ yMӽ^/qFȲ= ^B( HdrrRqxA\."c(A4T4uc!D !"ɤ n[3I_x/^W*'NayzSJ}^##(3ߜ>SoM&>aA0믾t:3+]ov*ǿeDžv. ZVO$S)}~tumۺ`O<^oLdcmee ˴^zs:^yH~ }}?o_~oTt:;?>ײBiq}?nڴaIsSwwϊ>$nsgζKqú>UdY6M4MUռ7 J/in0>Y޾}kۉS+/;fG[9δ;jCâWN>QMӪ*+3M偙x0XfՅoᏙL?8yktu Wj-"/GÏNg0z= !F"`0095%$`0p8rI (ꪪH$,"Bzj0qPBEEQNrNe'(B!I*+Cee`0X]]zx<1A*++B_3d{_mKtP(<{O_~JwɓmBa-k׬u|uk_z9`ť ٽ,t).MӦg뚦q^{s_+;nƁM6TUU~韽5ϝuylWn{ ?{>G+/A ^{nnZv;/]33?{Ɔk1v. x>lb?56>^^9\S]W^BrDΤ ~ bS ÔRx"1yRMM8/%cy.RT*4MSU-K(.{L6N-bQJgf"p}]ݷDl{/~-)V\a._-;iv^ܲr JI-DQz/_f?y'NdswaPS VUVޥR!P}][hZV~zy}p:Ɣǽ'O=SdY7wɮDb0M @<z<ܼ8TUp3TbR ZyO3t%REQ7 fl2u) DPE1!7UffYg:<2bp8 AUӾ8ppf,;ζl_ahΤL|z&]8êmo/h$|LJv[%-CqXSUMTUG 1yØR76'^ys;VX1yR*BP(TQ,˩TZ-t]4 CK t&fƘ=n7Bhtt zUX.E1NRi;˛6mdBh6+=Oz]@Kҁe#[_t $s %͗ v ]"}8.'kmZղr5=}}.v%IEYq|>,!QM\vpKp|DžGw*/VUUX `2ϟĎێo jYYYQ֖fkkjk/[\8\fUKk[6mXoFmME,c6TM._ٶes(TFPU:[FʂeXTp8vT2|ǎUĢuu$iv73=,Z2\|skCV:nlhνeM _mݸ~m$&Ċbinܰ~tt|K)mlhp:`BeвKpXeePHŠ_SS-,eeM()JUh$(.Bpᑑ`0 45MMM^r,˱Xt5uE#.^g|r]=m'N]*ш spy ?q"TkOA"lMysxfYe=^Ev:!bc09.RJNgXL *\T*]Vy!k(pd,0<1ʍkBe4ט.yȱo@ 0)i=yD׻ybt:dvvGxP(KOOLOAhuXkOSIJ,MFlű!0(s(bxdtl|044|c ޕ7h4к3Ow\lm;! 5tu?ދNFuB82:t8$ۭjr72:&3gc_Bl6sa .\d?{KW4-n|Ïz@kۉހ{=ӻgxoEEBȴ,J) L d0u/f=n{>` Ed6= m',:Nq"1|~?T*%!#B"uvCGPx!4=3./EQn VSCC7nC)}N.#_1͹CQ\/@~x<vE,vmc%r?w_(&B :/]nXT3|'샏>˜۰~mOO_6kZv+Nǽy񉉷~WU՗^x~l|ɺZs<#;?F0?.AB=`׋1Ng2t:M()pxzffU+W,o?ױe5/^,ygDQLR$VĢ/&SƆEk׬:tpyĽ~.hْh̽rmۖϗE4!"#@iZ>KvCT^y]?d(RG9eg8\Ȍ^a<\ A*}:nܰ~hhX陙kV40}ha 0ЄDb־m8=0 }  !Tu`p3ϵ)}yhhx$2 |g$a4}d3پڶ6_8L&?,; {vN/_T٬ 0w띉'jk @1B@ a!gmټ !d%ׯx311y'1 3L;.\ѱ3gRJ'&&{zgfkVWG0 `Z$_,J _0MSxfff(J̃> 0GRH.{!vWύ)д,UMð,Bag X5OlۺephxzfO}oqccO>oVԴ.8Α͛6l޴9]y55SSӼClbBHQ5,y0rvx~Ъ+e3O?N9=5NBa@Nf5|bhoo%޾p8vͪ/>dqc㑣~k IlWB(Sx8s]jgi \ރXen:!vd&;\^؀^T_qzin޼ɲjp֭^[+]Ȩs:r9UU9|?Xt&JggŒj}Dl6W,Kj)<xk ,+  ݳ{Մfggt2_駎k5 ctlnlκ47-ް~0!/OLL]~b祡ᩩi2',;v\""_WQ(ZY*-BL@0Ajn`  vp7o(RB-#p. PW[#IbcCr8ujuK >N>@‘((B{z4Ms<73/ |Vѱx"!F"`@˗475RJw\ܱ=ɌC'|^oOo/@qGFG++* $@X,EAD(.I&&'eYu]l6._rntukatlP(.^ܨTYvʲ<66>6>ECee3xeEaQN4jkjBT_?[H к pKB]aY&EL0|>?L !@)i]UΠ!XTe40;M6&&!HzSB!"bB!A E( @yPjQ`tC*o0_-,\sJn-A}iJeR 1Q %"S0SJ-bA1Bhqo<2[RaׂꉛiI(Mu "{z5pR07bŤ"H5 iR!ТԢԤĚrݸUbѮ[\.P{[|-\P(M DtoC\B1B@ĎJ)aZbA+R07TϾa|collشa stMe篽_\v_xȯ~?4thd$UUU$]ן~竪*6oqȦ;z},˒ݻ,ӂ(d2DbjkO:u?gWMϞ;?55vC_}fwn6y%MCC֬t꺾m˦/ܰ~]Pر}_ܾmK*ڽkǾ>̃gp(343- bϊ>㉤eY(ύ= yAIr "?|> -Aͷ>͛6vzO_W*b=c/L"]޹c3޹hS)Kwte/{zeRH̦R˗ʲp躮,B=}>|l|žɾE2M@UE ^r~*/` $dY@$y!IB݇BeS.+ `F>_olX thm[ U `u^0|ӾӲ,d;/_>xk?_Ji}]`aQvәlXE(lX, o.J< :۱`Xt>E)HLJnPF@rłELb6ۓ˲E)eWLSӴ(IML.AP@ b* p9ۭ@_~}?_v[o# RWccÇ|$܊T ȭ(׭'SS塐x#GضK7nX_UU/3M)n%*۷sEq5ut\x׮]-iI۽P !\5 %od:4ӧQ\.ceػg[Q5!H>D w"N1B"EQ&׭]t:].?~f:~}544,ZlYhYz,4ey@#JkdL)˼% Z)8`ɼ% X4fO AI+aaHhP˲츪 ĔBiYB A \ G,"3 #0N(J`n߷`|kN*i69[WW|Oa!d޾ha,VUM=B h  ܱPWj ˯k`0CW_ky4:aiݻA7}oi#ao/axϠ3:1la`ֺv%[Qs)M.o90_sU|haA3 ܧXfOkOIENDB`gammaray-2.11.3/docs/manual/images/gammaray-style-controls.png000066400000000000000000001514331413310443000243510ustar00rootroot00000000000000PNG  IHDRXHMsBIT|d pHYs+ IDATxsٚTP@Ȧk6暙hvB]bol~MĆB`#6&itǏ4so{齅+EU D<'ϛsDU@  a @ 8X^k/-p}+;o~Z>@ D߱`~aP8,ˌdyښ^bqi H&Zt)?y bY p:[Y H$kְ2M,//vBL&,Xþݼ}N޾~&#YN#05=$~y1[ƭ۴h7o#N'VuW@ O>ڧfH&,p=$Ic\yW ffH&>}/z1 YXX@UUYX\p$B0FSceKc H&S &j?KdTU!ƈbdYTT*+*=T*I,Nee%n% I\8._O <KK˘L& Tئ:5=|&cc԰ g9~$@ [6}>|j0H>g|b _6rU}8~f3SSmV$\2___GEt;Ns[cEA if3pM:d2cٰZ-x^*Nxp"$VWhijbfvJnE6c=<~ 2_~~ pq랿fY@ pP/ގDS477a4ɲ  308~nܼ_#@ k @  @ h @ |`@ H$ôlTUplo'dY$X&@p41(iy|>aٴc n_E@ G3++۬.Pv@ ?\T6*T yDGggn~UE*שl_ݧ*$SK-Z'@ >LTT87/8{833,>(jP\j065y+ByYV<}dbzfVfg瘛_` Ggi-|P(EelvXGe>S[||!dYǿ}h-@*H޾~q:oB2w{108Z( EQddE!Nt8:zGMM W9~t:M MB!׵u,6j;쨨|_U*dYfffFkX[+466n@ 8l^}}VQi&IBax)6zOM]?ߠ;wѼIo_.'P]reLf3~@M $zQΓ'Nvp:({?m"( 6 @ %jkvJi .!%2 جZ d2 ᠺj'l|V=ʙ<84VVA\UU{i@ cqif@OI&(kg~jkʾ=` Ͽ+(@~Qp&|D"A0W_|x1wݧχd"J184aqa};0E&d`p ZɈ%b6 JCC=Kˤ2i6DƥX^$?.OE{OOaiεp}2\t@\UUu}>?xHMm *WVn鍿Ύ87d9[zzFX,ܽz)~?++OL nK6\cl|&{~:;N3==CSx<;b ~k&X[ 㲘-dsYL&}%UQ_Fyۇ0=3ba5룧cmmk#2M |lFUUijh.z&0=3s==D"‘nSw<{hԷq65f3 | />vu.g{&&@.FQsNSWWǙ Kc4}s ?qEj|^-㡾db~~ʪ RFfWZ^2$N$nck}fS'OzB pٷNgH&cZZ\p.]@(bj~,&x<~a2hjj'#IZ[fcow8,PQdee+NO< NdoinxsfӶ^ŲU eS(bY\ZDq]~Mv;cq|>/x<TU%(X,8~l=pѱ1t\h9~LZyyljH3y‘7seu`pcH,gnv'OZ{l׎;ۃfҏE vdYpulVot\UPO6CtFzWq݄-_1ڴ,FwAZ$oiQ9~ B{K1x070H4Aɳg8<^UUȊd4d9}\ Dm;㥹ߏnEי3C,-/ opmߺ}WsʖMioYG"Qܽ;m`QU!PU7omrlw/zKdܻ@,-/sm'&r'*}zVrbo7C!CZwJȿvPYZY__gbrRciy7o1yvs^Sd= ;ʷ½dlVDo7[6?ƭM뾸kwԗT{t+_W3<Y~Ȉvz^VWWqTT8Ki{\z,<ϟEoŗu=xb;W9"3<2chX ߦ*33'x}-;|Ur}tw>/V)TUaii|^.&RAdu5HWי=+csTF‘}s_~哏? Z[|y6\oڲu?:d2'kc+tvLg7 P2 tv޳J@u5}47n044۳J"nsl7ʩ'$I?;ףz=FF`L&CEEWCggW._~5uu> 4ױ E/o[eY-tꍱᑗtv`0N;/inj/ohnj֏h{_ss477a6{ÇϹq&0=32 ztvvmK)KK? v4KbƞKΝ?}Hk;?:wnc4=G456q"(W|/k3|wl=ښ7o I| ͍qʾ֖f~7~Ȧk-yy^N h=KKno/&p$m^p8Y\v8 67oݡ歱F]6 t] 8Z60(G455ɕ z<!E ϯ}z&>pvޕ_{E=&b460~l G. Beh 8Z6P>66A*nj -D Ɉժ dySWe2imf34.cs^VUT;]g ZvS_|q`ue*ֵumHC&/䪪J6m߻=b+%*g>LgĢ=\ M3g:%zLrg) $ ۛ^>_(Y(/:Z-8ȝwi~zI!/^6W_9 '&7G|E~5ǟ}F=qv`(&c0}($IDQ, $( AZi) >hVVVY__hzo㗃(ɜ:umx D?u@am'̪mSThrB=8fGS_L`4q͛}ۼ;ͯ**yYF =z('lh̴rg|"WG`| [i]9l>&=o5ggf9yXDx<$I)WVdqqjd@ þ8}$^O283UUfӼ za41P IANQU+ЯC׃CP;fI4EE%RUY"kUdY\.x}gqiMx'$oqql6+dpmk۱]yai6PeyW++m?Tǫz˅;oK{5*H&R뿀E-+m?Eܯ^CK4կceu,e=A&e\.G.+#^B'i1T>'.,Neͫzء 92ä r9mP؉ f/˲~NoG&\֟^z--?|=^N _K{EYio Ge˝.ZTA{:]&TEbP0 '*ߧ_x~l\eڱzcnZ}(Fp <3lpiXٳ|r2[Ɗ \xw kܾ} >1++cuuǏ}D"A*byy@zZqUZz#gb6lV^chSd3$I&I$/ğƢ$SIf? (Z~|>O"dqqh4ڈFc,-`ޟ(dB]˶;Ldbl|UUikm!ɲZk(ێ UU(er.ˠ̏K/gQbCzG=ſ\>O6cbbʊ Νabrѱq‘BWl(lPI NIkitNҨb QU}*~ q4ȾUr9Kykkerj:=^BΎz< >byy3:uB|} 05=nkea~ӧŢtk9=oh}㪪#\&RUUEC}=F ϋ$&TFWijj&<‘0]g: Fc}{Ytʫ3x|"DM _hd(uTUV.T1.*?{y0H4Txw (.$Ig.Kj|z0d2n.BQX*\t$یdVSFbYLNYK+(* lPd#x}Mʳa_=v|m-e8~L_xp)^x^W pXr>^)4te_n[ܯZ^6Y-&</h߷o޺,9q8C#q t۬TVV,c9[halB\.ljǰ٬΁m_Ea}0'SIEbȪ愸[fl}#oWG,9^u =^#Y3z>}xJ};ίjzLrk1d"YBy˭UcZugjjz!z<zIF\8߬֒Xf2n>v9 nv_ɵ+oC5eo|~ӂ<5GM @gg7:!?X%+=c.50VALrm:V2i`;vߠ*cƜУDh,lY˜(~4q`}ۼ/4ȊB.'(V7l;$Tܯ =cC>I= MZZZ{^Bo_?/_b6X,hJRTᛶ[蠪 pb9ˇ[S`E!Ϫ@(֗QcGA>|zGXocc}"Ǘߺxdqqlydgqr,~PXy86hog`U:Y0YYcCd2uqm(!=Tޠplojz#TءP͙L&||^3<2Un?Sݥ9맳4}J$Ir9f3od4v"`1kdY`0flήƑ>Y^],z 1Aj]B5L18cP ³/z psp8sOʓg݄a;p0@8媢`'sm@`6HRD"Q|^/Kb1I,fmmm΂Ai/qWLCeYQ,`5m ^~,}AY=DkB g)#j~!;/6c>oՌ鳓/>cc|e?x6:N395EMM >8vS'NDHbqŒ^7np#ܽZ0D_dd2nt&{hv0m܈|c =6"G fB6#bx~̆`[k+4C*ᑗx<j X/gee.5O@{zd=gff4ęb8g{ctGO>jrt:6>]zNq/pDP{' SG. GAXjϑpz'y x47 TOp$ќK&<=/bnV^I+l6i]L~;&~`al}Xس =6I,=Gy,H!z x?('6LIl6}m b9tbk{E,# (!bA=8X+_x)ǎJX^^4O>N`O^75GFx<}|M=pEU3cȲssQUU*+PbkfҚYD1E#*6* =JHg3h2"E=yX{r۴X~x~l_],Yѷ'gJ.l6qI&&'pFŋ O>fZ4384G.W X@ |8S,ƆFꙟ_ ʋ>:;$s&jyDΎ456F_Xؗ5fffXA@s*rUIhhv2,H JKg٬Hy[審)o?WBC!z͸\.vk k]6&6n8%: 4W)E^v6=C"L'2кWVH&~4du5,^?y8F۰mRXSa.C!z=G6et+;(1$m`f0lTu#MUyŸUV =BC_z ̻H}]݁w? .ho Ŵj/ɩ) aQ633sΦoH&SeF0zkyϷnUlWV]KK+_!xDd!!Gkk+;Eql_~+߸ɋ>߼Eoo^r9mM.#Jfy:'Oկcyy\.G.$S)?xH.#ɐdr$Ir/z 3fώbW)SMLM B m $I{UTT_c& =6Ϗ6Q}/z [G";ãGs7ʗ_\z, /zpG"8"CZ,H8ehxSq*SiL"lҾk YZ^'4773Yz/ۭTt-}iyŘ;N_L$IOXnLhjld=γJ4(ITUU10G,caqh,*ډ`m-D}f-֩߯uavZr SŌa%fz=TVV`[ב F,V]AlQr^EX[ F2 X g^}PmxNmFUۯVLF# ˜MtL,'Ʃp];գ4: Kxn\4#TVTRt =-P$)"gH$d2Y>MTTT019iK"d~~(a6ill$H i#LPOZjkk_`yy fI4^$j64٬,fXk7%IB+o]vBC!=v5OpM8v1:6!X,BȲLhst;D"QZx'O sX/_ Zy9:FvvD,`@ >pq<\[/rsuDxvzl ,L }-pѱqrȲL<cFGf<}7ooqyl.(kݎqvU)to-fds9I(Bpb8JnRCQ}"MLMv3dEy'dYt6X6AeffH&Sz\~YQp: y=%6|8z VLx?Om*@$N~hgF2J\5lp8*ׇ_i=68 zf+dK*JBNbV~&=v{E/]B6_]?0H|}D*  sx<7ŌbeaaQs3 2,~rh̾m6*+bi zZ8D0>$I"/r9~R4:PYͰzb$IzZ1ci$\#P 2>1E4f^L+r,-˱Eb85y/5FFQUf=lE__o3̠Z{HzxJC\BǛ+au=$'ϸx#?y 6\YYًX~04Ƣvf3f WU"c0L;M8&LQWW,6T*l9c݊J'|>Ϧ.oӮwm/b4Ƣ1feB0^PdXoXt Ds,-`$=7xIQYap:U?(=!WB֣‰˦lR>2zI,/hkko5'OÇrm dO~D P7<|D[[+wnQWWdbvvK/p?_^H.\O7S3.u;ۍng|b:60 igt qdYv;\V~2lVfBc}=svzΠ( 6D}^vmhƀfcUE(t;"cZ!RYQDUwg(J6lP E2h?E(!S)f37k>$=,(8 yΚDS9z2S#}+Ӫ`h7Ir}cAUL&( pts IDAT1_~('O pV8Q/x9:?|&_}97n"RYUɥxPCSc# v^x^ʻ~ѫiinޙ @oo##R)5~@ 'c Nk-VH$fƲ'I"q]UUߣT2j/ #&=b:biyO~̽{GQ'ZQTEn}pzLf,]%ƭTgS+De2xA$q}U#e}3lQ@)%G=>#nb._%#aك_m'|LSS9>v͟rt&?xH}]-dUvǏm6)~nllH$O8FSLLLT ⮪zCl}'c2𙻴 dFQOb,zK(ڸ ]ޙLvO*76t8b߫_C$$$Z6d?8=0 8J"K2M*9AZ c ܞC(\o_Vep:z@ᓙwOF*G=>BdkWy9Krۭb`1GϯmZ[n?3;˗_ctl|SF!wӫ;6NX,\HiEO/YV=j'nK_?/z=B~a4)} b X w bkLOOX5Ξ@.oƏBC!s=wm 4Rvh'a{ 9,=X=<Ȍ7{ {gzÙLFR2]4(ӓ̨h%&d8j693P{P ;x=3(_uݏEdݸ~eR5%b~W0 ݞEN|^r}tvl֛| !xG'x4ZM"1|xV^,x!x\<*1\vŮWjvngε皛Q*}—s moܸC$q;^8V!khN[(zg6r<C<#^rךb|J:!lx^>7nl B.0 jFZ'u^Υ?a/4͎5rrҺyk?~vn~mzHRWak{}\0 yoW3~hNo~qaXTud*>A\F4ժYWUjzG x!xXr]؂Lw6^>~ Njw'NP,_񰰰ȓ tVkӱW_gvfnsMk7;-M̢׮"FQU @2<4{r+^r0L̐JgT.39=͘4̭o ^$Iz|(f b4&bC?i"Y^!x<ʕ [՚͉yԚz+?rW^p1"0##)~_2>9IZpM >7wc{An7w:y®[oΥ>P,}x<.@0p8]NN:A&VǙEzdL$ bfvrl_xݸ.\nӳs IVy֛3ϓ'{mԺ'f'syNwWtwy a_6t{~8@٤+39Cuw=qR4c#$i~?χ%-|twwtp(DіrzN,[,cY)cYDzSW|[ގqk{Fz^/۷`ddp5*|_^ޕ|y9FCh㧗`>պ9l64 0]ݏZ=/xQo>_o'tɽ®vq;^o#/ȷ!xyDk)J3Srme<՝"r.WZm+} 6YWxrܭ|-/ʷ^!x!xt^$;^/`KSSSr9`o_}ejjjWx+ܼysW@<Cݻp}> 2/pVX j5TU%brw/\M~ͥ>BG|ʗ_]P(i\Ό)۽RR!EwvG:zEJ2ZVR0 >Y& R,댮T* `|rZSߺ?W,P0H,jn²`fu]gfvbaJ]U_vϖmp<-1爐vGIeqTOlAm)cuGp89f{xlUm2,'NV1;?D,"r>zcq)Ae"[pP7I<gjjQIj|@W,Rb/p飏 C!vn>1h??܃w"(>B9u8Py=˒HŢܾs in*|0 6f[m$mv^^7PIH2{>RĢQ&&)  1v?3ۭ6E;r\N'vq="BR(}x=0 b3w,.h$l;\.G<>p045$~wYvqnEk"ξvp8L>' d|9i^9uRD$Ƨ(O?s.iSs1Oy]|ti ť%\.~\Y׉E/ 9q8nܠhBNh$B @tz{z:x]۶?~X,bOAkFo_`r%FF yh.f[rl+~"TE " 33s$RIMC$ub㦻L6gWT߬b0h^WqatP*zۃ 9$c1|>V?Eh cy'7!F ]q8"<{%xx<}N@Y|W_z՚ef{~5>Ļa\nk_evvP(hW>~(N7Ξ|ŨjKeOLrHӤ3.{f kN Lq_Xd1 {  HP t]3hZAeNlq{a=."]smwXJ$QUR<`X*qQFP0hxAoj_lnc+nrJZCm4PUޞn{j__/R.Ǐ!ɑNg#TkU\Nbe" e{c/x j8$pMd7@<{eJX֋+5MT*8eswczi Aol$ u{^F߇B$ekky96NFe{ ފ=:l6ezaM/z]+{e֓GV{Q˱r]7xN~?N.\2R!(f!KOk76YvmV;N˻]6b!x[g^ xwG;V~6Ix$7,,$sZog=TU^>ӧ_Y|zWy9v:z;%x!x;cGgWU>x"Dufffvp/j]:2L(ˬd539\.WHmdim?|YЕ^>^<C<:cGoݾ|9d/x7囻w5g_G$OAB\)/pfvxw7rO'߮6zQ:cvִ _3;kn3_5x7D#Lo~Y^kW{+c{ w*l]c|ݟ[!x -lެxO?̙|}::$9xyxWvb iv^B/`=ZZ^׮Xl]*uU%NcRܡP4k2,F_?p#;|xc+k xvc;zcGfumť%?J /]rЍp{/BHSIg2?grzQ>z08O*^S(]$I,](AoGB4kyahhStew`MGi ̯L&KWW $2 (>[q;^rZޑ33z{֫L΂p8+߳> E:R OƠɯ?a4b m?߳C<cyTkk7M_ p;ĻM/cG}jg^bbj Ht:jP(`Gr-=& pHĢ1|>zS' *xe/,--144H(R)Sz=vΝwffB<`zkkcGq8n>O4^Q*WLNMqۤ}hf9iҙ R)ν6R*}rћMb(lw~ [,:x( Glz9qLٹy¡}[9<8@Xd1R ~E! en7|,ݹZF)ĢQJ=eE! #99r UUF,cY)cYDzSlK[z#/{080@(\GP̌ˢ,*b`oWOŨ`0eTUvɓ'QU/jWԩ绒o{|W!x㧗s\KlմK\n{WRG !x˼[ x9j44l5?Hp8vrm|snkr9w5z!xޮMQ$IvUVh򵋷w+z!x Vh#`]i4x^c`SUZ餄^BBBBBB=#`IPW\>aZEܼ}ZvS("HvFiSni /`SwenndҬOzuBz\zN`n^ /ğ?]0Ο7^BӴ[/o&x!x| y\uuU]}~k^ezbi9_kvwWNmb˚+caab(\٤D2a@(h`0@jVezzzt˙Fl6KZZq{e]"5r92󃵴lmf29l4go٬i8u۬z;Z }7򵋷 ']ɷ^!x+U$iMOd o`nF<@q+jXbjjP(6XX\‡`M4 eВQ" *CM:O̕ r;,55&˞wn^:4LBy4v-_xcw/x!x|;yXN/*CUU4M#zf6Vz ۯwNjzfy>Ol'I$̲ pZۢ0ѨYh~~ 0Q5<󭊟_Zm*Rm>_&#+l/(>6g7{~^vg+x!x|x$<$ Z}ypZ fsۯj~K.}1W]_|=ſsBBtF+.} W^^L0 t:kןm4y6 QU՜-4ds^KaDJa*ZujFjVN٤T*uR0%x,KXzxLMگ+2rŅyʥ:|0X_X?6 [x>qdǏ'15=OǩT*%n7D'LMO36:gɟ?X,k%dK ``m@@!J35=MwW L̝91R"~OT&pansɩ)4Mc)gƾ)j唹UrQAѤP(PVYJe/>YƯu͕+E M7~kP]֪(>PIh4^jf_se|4ZҚjvH&)x=^{QRUUj5u{ !x/?x#͢(zzi6u*MUmPW;c;u_q/,b 5pi~hMld)K\8M DW]|VM}}ju+{z~?|c;j:]JdY"{bQ@X(YQ}522d~ {ZedY&^W%ۖ>aJX,Ô౬ǁÇT*WNSV) GIԺp=7z=#Yf}նr @8Y/ ]/2n4vl|ЊؿJJZV36mz=ϯT4 **U*UR Pgz ts'+v9[R<C<<N"||豣;ΣH>]Naxb9js:v[T~xh6w-_xӹ֋<C<N//%XHӄB!4MR#Ju GZT.)D#jfO2k[ϧ_psyRq)h$l4ɲhĜt{^ ð|BV:oxC<#([ xZBٍ:VBk[ygf ~{x4Mï Q)W$|x#=~3i/31>1;oEJzSiR+^fX\ZF^ܹ7^ΟC+\v}ϼGGp]~KooG!xǷW7`H=?Lrj)ff3|>uAB? i9^?{@ ,~E_R8| ]S.#H$s۲[2낯Vڻ_בV:]q'LJC<o'|ypÐ$|-kLMHu|'+|ۈ߈n|brV=>nȈVϰ U*e-wJhM4my)XށjLBy◽w'_e/ɷ^!x+;,3`vbϣBH"aN4V4ٯue/˱5n]֋<C<N؎K?V\|>@0HT"͉ = i`>~S'O鉓J{~s.b.x= EP* CHD6#F=kBBBBBBB/^[y潀'fjzbDTH&`jj+o1^OzW,ƽ{YZJDP K%j]=rd޿,okp^[ߪ /N<@<Cj</-khp7oݪsuh'&H$ z)~<{Bt$_]lOOÀ[!+'LNM9WtdYƯl隭x9Vluz9>or<C<N^m444A5^cqq <Xg_}&H|.ϡr.](#i:dZyk>tfqp0?@ֶq mUklFC%vjlVKQW*)JȲwUqvq{!r6@9kt=dcUv>5m#U玍nmBd/M@Umm~6<}n\/{=kkny9VC<##I (bD0@ugf4 P*].38"0|rL,T.PU\..jJ,ci)A8j;p<2:qk@imcEr9f\0];oxC<C^xjFYZDX\\'$ Yfǚ8M\.},-%$ CkD\*^w[#W) @Oܜ x=^:B,3 1.޲| !xN!u:i>ɼ6sCm)/T*7Q(q9H`0@Xb~anBV#P,(Ȳe[H or9)D<^wͣՍ$7蹼ݲ`) 0k k|wa^uz(xNHx {̙Ȳ].3gNs]ɷ^!xǎ#Ԙm#myVUveS^\6fGQ|x|@R 6ٖ\Ao;^UUn݇NkoZ}tC<<[K_.M` r8$v4fxxIrl6w-_xkn[/^<C<:CxHlϓo#e29̩ⱱ!b1䓓bh,s{USJM|^*%i57J=Y/_<C<NؿM[RW?%>^ǟp=_r=,l) TUg?%>WVa,%|}3cz f"ZGaEr<S{u{Vijfd0 ɩi*ajD6XX\X[}0 t&S(1 JJTfq}_KeKKE4MC52,at^VKDz%ckzgo޾ MyOLN2::|LR!3;;G>_W0 YH26arjk44r_X$==hZz{y4ۍnohh[:A8ͦˮ^W+M_C<C{yk"~_꩓xhZrLp219,'#3'|gz ?_1=|0>1ICUs+sm^}^v548AtXJ$==Ug /9r0\r{Eu 5 7->txP8hpp~*]pٌ<?zy|O1d2^wwuՇʥ*>ه>p˙#;#:zkTe¡CCj5?$ 'r/$n޼LkMB|ʷ^!x!xtv6ymX XHHHHH軣zHxo(-3ڵm֋<C<N^Bkij'9pHvlv[iqD"Kr./* >STwws1|}rW8r f C,,.( -[$ Hoo/lYQOS'd D(ڳ .20Us*5({_ᮚ9 l6_ʲLW9HCkɡ?ʩ-kZFtņ%C<CI'Fz{[$5/}2'ijJ8X, pHL&''+ ,ۓ_$ݼu].W*d|_\|<_|pA* >ppOaY&&'y7&J%N319\WOvcz~Gux‵\g]6UVrMljə0ۗk 0e8رcn6hs}sO頧U~tmUm,^wi b!x_CxW=Y3lM.`/LXdi)q!KνCǶ܁Nkddk׮ۣQ8Ir& ORkӃ$IgXZJpA&&+ x1S4M6I~? ,T-k7~Ŭ!TTp x5EeʊuE]JsXEV_0CvZ#X^4*UԖa7V6/k_Cl^xA@a?nA]Gm7]5Z !xqYܭ Syd|Q2ANlnMÇo880:#$) &8~(=qϟ K cOV% s1H~* J.J%B=!W8ADk G2.[JkM!0?]fוQ=- ׌w SE{=t| z\;蓭 [׮<8DCT-[ʟj%cYDzSDze#bZ3CU QTx2>A674 JŒȷX*5?+gc+8vh4-yӞߓL&ܜªCTv[($2ڼ^}nP$+l;瓩Vk]USi/x!x1::Vf_~FGGGֿ+7{xy46+}}Ig%=^!xg^O+b'зO~b۾(NIr(k|>>7R<oy9Z:]5{[֋<C<N^ xwG~ȑCĻwUo`x)Kve.*b`oWjG[ȲO> h;ŋTkz;?<C<o1vڞnD[Y%I$Iۭ6060+^$Fsw:oxC<C x+Z3qbW^9E&a߾u\￷᱿˟3'&&ٷo+W/nBSX&*:kv[8$6,-t^tu.WP(m;uS]| h| !xG'x[՚y7~Ο?(Gp=s>t`~ť%ɔ}Wܺ \SN#^?snWr>E( ju+_STQhrH$~ԛ!#"d>sNdaaW!2'CkhB!<ݭ:tp(߯PTpܤR) L65^ǎQ,T*LNM1<4ǥ?vULJ9q8s ߿Ͼ杻8۫=~==qN<$IC!'BaGnPT 219ůox>s8@^t8xO=a319W?9o&JYILNN199E>_W295~̩Sp:& ~LV' s z{{Hg2jFZurluR IDAToV'ST9zRS'OdaO$LӳEHHHHHH軫DX,j1Gϕj`dv5 pq] <  23O{G#gʵjzm9N[9bD*e 2s7_V-Jm{nuuVC<i\ɱ.pjC<!x!xtp+^/wG lKy'=14.n->HuU|^=c%cY)cYDz6<^ϖfXi44ŇnXXߖj,STxwsvG7^[9o* {g={ևY!x,K$x,K$xX 5k FbȓqDR̓qjl6gUW*$H$)4tE}Շ?/ ,=tSi9Nj&Œbef)Kn"07I"0,D6c(̡C:2;;Uϯ09N8avnR4>O?ߏ,{yI4#CͷW|\|ssT*UhK}wjw mFk}}}qsݷ+%uz=Eo^j~go>䃋싯hh|ŗ8>x~7$~jH&X( w>ѣ'dN8w4s}\.Zmձ9[k*VW芭.wο{xw. Iip8as  Ӄfq)v119a4Ie2rUU$ht2߿ʜBBBBBBBBK[@?>d27?~u`8 ᥏F?S׉ü)nZQָx<ޤR)}>]dH$Z曯S*ǻ(VDs mW|y/5mMtEhep0˵VmˋԩC!<@{Ԗ0e1g?=[ylwvjXeW9@p#ſkmh~ z#^m7o!xaIX@@XVgx􏀿:-1e{MyC|AJ$ c:J<, C7E[ݚ陞>v/r;Ɔ^o8zg)R$HWPP;3" *PTɪ |T>iDOMх9ܓN-|4X߷78Çuoxu\R6 !>ćc}8p'O=c@UUQ5`8i km'13 N03>h>L\P~, Z?tr}\.s=m۩T*ć>rD21iz?O3?o-7> KVӓ$s2|)+VcԪ괩ma8mać> lŠh妭ˊkqES']vyϝ'[59|keʸ P\*zuhIzH$|z=K z119ɽ|QPrҌͲ  u#cC!~ ~?dqFFf\KZ[>cxX\\dx|ZLfk+#T*S,Zvizۮ/+x<^kՋ!>ćQQfq~m&w;4<:8 ֙>.\D 0K#bmĚ|bM099+Y>]]O02:B2I}{i55QUϋ( ;2~gwtkeNDBiDMvudI2ۮ}r^Rۨ^|!>ć}z*5..-D=ttoXlk+>oAb}^n/n)JN*Nff3+iuozUUFC|!>G-| ^3\tP(+VCL~&] 9/֭O(@SrCuu%;;>rYmvcoz{F^mT/>ćC|‡)Vp3Eb&t׾x"},..Q*ŗCkk wAu߸Eb>cU͵k_5( Ktwqox7^֖-_l54$ XA9s2&ZVVOxǃFY'N?K_zKseՁ @|<>Y ?}?d2JE-y$_JOO7/"wqq#H+/SThoo#p-^8rOGrLh-xY^^4 AJ[Yfnc/lr\^^mT. vBćx|+!>cG93G& 54[g۬T^|i;u=W]#r\Yyr|⣺y!ć,`ao,ǻw|k Ϟ}~ Zb-D4n> `GX\|eO5?w]nC,..qgPuG*& dEӾN*㓓  gzvÃLLN**\qVLLNIP4bRiR x82T.f$Ѱ!>RF<33>}+!>㙲*pwN[kcTګřHOw7(r/'('Afff?ُhVv|5]226^1f r9¡V g#x<.Ord9nR(>d*a9rhJܞa9hMoxQL]70 IZ1Z1U gZT*&&th)L-Myn>LY{6?ap#swzq\imexXH&9<8i躎iV.pe#S'X}>`ǚÃW+N?7ġf98q{@꺞Z9Y*y!,*ck...295 XGY\\$nb9c_OeV'>붽z6~`}|vۣTRLLXف>DBikl dM)m>96Z$lrWYo!>'J|x,`нyS s `i)~' ƭ[ҋG /s~TUall60! $٥% sc\9}fo~qM^y%zf'ikkٹ'tp)mi:mi>SX֫F C|T?O>Y>h$B{[P(\<Η_]A^8zɝC+^Wx3|z"3>>NT_x×o;wQp2/=J6L>_˫c P5lg\nO">5mWUX v,GZ^}ۨ;q${Gţz !ć؞z ^?an.AOO7b%2imm7=>.NgG27oߦR^/+mtuvR(Fyi~6Ҕ,#Y a+Y ., aR8 ^rJICC`-XVaPp }hz]_X֫WՋ!>ć#Yޢ\.;a:m:vNToe9uoz;˱^mT/>ćC|‡anIYdX#<`^G*lXL7Wo,aǷSog9֫,Goz!>ć>,Upi% !Q|$ ¥`ZIicllM(@8fqq;w 4b `vvFIڙa_SDt]Lo^:y]LOcw%s9[T#>V<@\vQP;XEcpP˅]@>p^}yMp ]r]a7D„BVُIg2Qk8>Nwg'ctc&<<;;k* CCC) p76MJ½C!t]wߖǏ( ׮}UUEQzgfg)WtrG :o_WͦevJ(԰zz/k׾c|L̒QT0MS|C|t+ƽ¥jرWؙE ̢sיEV2{[ f{5qfghhi5d~xN4Mcx!ntښ?4t֖ܡ'/>Jc|XcT phKr|,[Tlo!HVֳ}~K˴EvyMնTlM!>4AUխ5-uS 7P.3g ~Od!'LHX1ZY^^XF@=lok#b>%^nȻ(Q' 8D&N‘hR6ˑÇPl6GQΜ>E6L4a_eR\.*Rxb9;7eX^=bv-񱊼?,*jٺ7Of)b~tcxk}gM(|?#o:=ۦUP=ؽ2۪| vil>vNe%ord9!>ć(18zztwuNF# pdGoݤy/.H'Q|!>ć؎g8ɕjx<Ěbk>|v pY YnӴEYq~9VM¶n7^ćq؞],Y, $$ k2<ćc} 8l=;,* %+ Umn+N[Ҷ]J^}XkՋ!>ćadǑ,ǯOC|!Y{L&C$y;d9~}ŇC|Ea`KTZ|&+Y+~199IFFGq݄C!"d;wq).\ĝC_w( @$5Q,x82{w_AAѬY:]H$yjΞ[sa 'hmmyB峳,+O֩T*nYL>nܼE$!d+ <?BeNUU\.0tftl.'ojzH8_|;5}ml,YrٺG@U40n^0uk:YTTUvPU/.WoίQoe9!>ć;מ1 G$sxn**rZ[<!p`,,,Sx! IDATX0&I wNŘN?9ph_C[>VQP6C|!>j㙳55ѣpd7O[ُϱk_|EG{;trq9Lkk nqcbNy橓%xq&`eGQ(}\KKDagI"K/~c o:܈橓;*iP#q5طo_]  F<6<8@_pp`]<8@{[h9v`qi p(X]ucU`1]}u@p|Տ{54buMᰓRoSLLLmCT(MMv֡cSsv~Jc}[޾^mT/>ćC|ǎdwww7xo;oyjG&H=4mWVVcsߐ(N5hmVV|,ozzQC|Q ;,-"0--ւpغOccα|qvX[rWՋ!>ć!YAodܿYRuC vSosWɩS'tR]ۨ?+!>ćc} Nm=;,t]\.͢`ӰVtRiT*:m^uLӬ[՗JQC|Q 'yX*p躎ie<G=JVnםt:C4Zd9>OYC|!>Nx,rBP`~aXS UUH&Sr9fffiiiH&hF5L Et]L&i466H$2O"1O(@.vYб,.-q'44x<5Qp]A~O_GOLrpG݄atwpSN]u_35=<9F<  xdh  Ԛ5%%2`*aNiiiavv0hoo'HP* ry縅$P(D4A48F0M:;:Qֲ_=1 \f;49p 6,<--ܾ}Gmom(]]ܺ}# 5ŗimia!ǘs,i½{,bUUԱ콆AMPUuǏUYoΕ,֥_!>ć;n*`EQpi]]d̗dYqZW?UUۍiZg%801MH$L< D\xuviZ8/;y̥I\1{zzJdeG}Gχڰ'}!>J~sl2aa!@rW777ֵŇC|Zؑ,o 5Zt(݌0LEQ6EQv4M4sԺzC|!>G-|HhbZX{>Tյj`[[ ۳8m`/ G۷3P6C|!>jU$ x#Y A,`BB ^rv˅%]޵khhG5ca!>V<͇]`3q j7e~a?,yq4{w##,/w>Txi~022J2m<%AA<48O?7AQ0 7odp"}|Wܹ;X-rsؕ'rR*xvrAA5M%YLDONRTp\L 1D"affgؿKL6`qqϟ'Ѐ\YgC/~njsh l1%Nܿ?/H3tuvSS>4ZZZ'tws稪ʙӧvAAX{grQTh8ֶVćX!>4 Жߖu|>O0i~fx60D/h\>O81e>Ξ;kB<gjjUU>ݡ{Noُ׻wdbbUS`BOO7͚+7V UQP5JE{6>]^ťi>vyW!HPA|">,*cyݮmi lUZ[Zx3&vŗ& ۋjrAg@rf~5Μ>墥Ce'rڊsvAAx^سVNF"nm= YӨHn>.X c1.  ߈zDݾAA];*{[X*uO9v+}5؈QXՈ<ɇZm=;hKݾ]%}UQnxA|">Vci{v(Y#ЏOhn' XS477:KзS'ܿ.W'xb]ۨG?C|>^mT/>ćC|vvT*[A|֦:m:vNTioz]6C|!>jCcӋ rikii"6 @<@2v!׭#k oz!>ć>Yf9wB n*ʚ,O>CJGx<R)Ξ(G&Jru֖ft6~9299=Z[tuwa:=rxރ  `o޺,J\E>_;4D8& XYo:\+? W^#Nk1::FkK cc$S)Rwݣ\.ŗ_q!<;>4r^}eG($"OUUuڊ2nۮ߿JHW{QC|Q Ql=?<8ُm^`Ϸ7^?F<`rjf*2mP9O?cvnCC0墣ÇJeFe}>ktF tuv22:"9V"5hn^RNkiii" lR kuԸ} oz!>ć>\`Ks3ir) \.\XYpp8Lo_/Βj8qU8>3gt9sM)N什{yhinUU9sMP\Fkk n#Q.Wpl* PKX`\ylu6^;~p\\+(;t9°~N3?dll އb47[7m9va!L5o>zoz!>ć>zzz*߈z44&яw01M+:* NmY3oz_/>ćC|ƇV)46F)D}>˺a4 93C4^9JXv0L::z} oz!>ć>3ᤤ Y$k vyH347ƠLLNm:pȹTA'^p>t -Lh}${vP-ݻ;YO$s޽^}OOO… uooq~;m؞[Vwi. X,Wt|C|| "Y @׭=u\.*NV$nrWWVDoz̴b0X^3^[fۨ7eQCC|C(Je9ߤ;x4[SC| N/=Sk׉55;t׿r\I抛߉,Ǎ[yR׸2B KS6B|<)!>v|l&/~k~|O>sO>|F"1ynݺiܹ;Ç#$d_p; "o0 n߹/~+~_1c?{Rz)ʃr<0LFH3|d)gzk#,rř4Mu]׹rCQTJ2\qs Kcʕk[ف_hS`X)gfH24,f*SVmV֡j7 XG7w4x<_Ƿa,o3>1Ig{==]LM02(Ri¡M-5j+uz*.DUJcp0r( *Gy!>^CsDV^wVLڼquSQ.l ^tϝٹ9<Pӄ1nKKbe {.<E _=VvNuB* Je4˹`SD#LLkl+0Tg9n%&(ʤRi "dΎv0P(:笮_h=_{j|+ONOby9OP$N yYXHrQUeӯ/˽ * *IMnTtΤɻ@^?ć_o1>=LD4Jq+Jp(D8wR,z}ξ9*׋iT*5 R.<oU?o|޻tuv27@4"h@?toS'rhjǢxn KZ^z(hMSy,/4v\.\8nkMLN1k)p)P1ʂnZ_ & ~Ua!>Vf@`Ԩ=X{>_U՗_[ͣo^ww|-I$o.qbb2f9./o-1Y`ٙSdzt|.Wpnv_'l{؞W͵(mcAXCC|C@ HȨQܭ,G,G||k7WćJC|'|l_oBSS#QbJf)K%kʩ:PQ4'p;ӄΎ^}Cc_Cۨ`0uQѝsx4>PGy!>>|,`AA YH:ف~羂hY?}*˱͙3F?ŇC|188HVte 4{Ri6]םԛZ^X6C|!>jþ7p+pS, +5Ms4Ms6]ם6]ן^4*nWog9֫ŇC|ZNμ 85e\hYYZRVg+YSFmYoz;>Gۨ^|!>ćbqY{~d8w¡-ͼp?8|h/7ć>$ x_ty5ǜ8~x"$J%:;;d ߖf~|v (lVPVHkk X V6圕 L81ѷ>Ԅ륫h$L<`iiRyihñWm5xb uJpWog9P(>ćC|^Xvp\F4.^ XYPP}Pr9f詪J6LCםAh o~~4?Ϝv򄓩`}=twui}f|H|O?hy@VHעIC|vFg#K;;0:7F"!''\z=wUu=Uz;˱.Wog9P6C|!>jC% x , $ \אH$UF],/癟777 ])X֫歷tkF?ߊ!>ć;CǨu],0gWWկW6C|!>jCǐ,GzYC|!>v|Y:DM}bS>;w8rPϷH3~ُq=w[8ߺ}Gnݾ$7f9g?>ُ3н{f~&irmO~022\<΍fr9~4yX^^ `dtd*i<#˓Lkw k0 X^^f.`rj4_H2;'513;G*fzfbHXdiLjvՋl[TNg uIDAT[cٹڦgfo5U::p]JF@0iۊ,:ߏ5㣻'<ͣ[og} w,!煣G~&,$Ěpn0<7owfjz^TU~9 Vp(jvbbn GF#\tt:jk>i SӤ3K ,q]ONr=HͲMӤ,Ffw!.Ɓ}nܺtuv0pc;,ѿJrq6WN}ON]DQ-[Ӆ˥v{TLn>W^yyy) KKYQ(k,dzz?M~B Zھ?"PJ0!@oo7\PKey>bEAQ6î ']VУ>t?FP۽&e|l=p>0QUc\)ss GwAZZZ.\$1pJ}/]]KExw[il%11?8 N:a)hcٌ[r\\\dt|JE'JS(q]Mt#Q,6͸,b8p)BD |ۃKS7nՇ}TT*T%(lvb(>k;H嘝1C$癚1ڈKs:hjlqcN/:``徱2KKdi5:?vׇ.L0T| MDug@bx mO#k?6`YTPUO/\Ay _8uuv:/a\nh\.t]A}==NvcVh(T*\.4IoP,GQTATU(Rӽϼy !E!޸^}lvE /M([gX^Ρ(*ui*y¡:jA\&L(+$)0h%Sx^t] *H/ iok# 13xpl6O>_N#>V]}y9G"4M*3#399iŚ]XEmW}IwW'7nvlDi:x P*H/eAUaeOQU| KMӶ= X=xGoc9yu<[uF𣟣ϿͲz덾g4Ƭe}rsmKK:Y~طR$ڱ6)rmxcMN:'Sv[s{\.TU >_bO{}!&0DMՋ166E4HrOګ/:psQ$ο?Oou?oIu4 LL CT40@;EAqɧRNX$8Kw[m4\}m[=5Dٮa';w ;џعk؉]NoćC|츏: 8sotM5Tni$ˡy1i$) I$@>"0JGOf^AA]y눁i`rCQQ\`V*N>_;YYaW ? ]YZ:"NpIA^'|Czzi-f)[ K1 byefzS.WMceg`>=f}=tfAD} <I__/]]s*)r6K"oMx1զS^0MLTT`( ð&7LbQ NwA&տTU][Zw/d*!i LJaZۭ(Rr{04\.*0LlwKK43.G <>G r;5[m 'E f0U |*.nJ n&ع\.gVXrOO3SAa78xϱcNƭT( aX3NETF耉*(|>>nU#&EQmPd.r  ^R 6ؖF/,D|nh4/>5h:˅񠨪5x0*:nke X(e  י cݧ%, tÚ3MH/-113Cc8fRT0UQP+bC'b-6q y)Zc5LAA=BLэlZ6{ LcccݤP(31G\hJ4ࡧye@2SVֳ|]0!~3|k:5M3O2dj=N$y  lYa& >^B^%yL a:Veҩ B ˃lX+++,1V74M\| ]{]OZ~Nkv'iZMT ;~N]Ih( OC}!X+wu?4f5n̳+ i:eR4L,2LbrB6l*D|9R T~l"1O"1OL9 ؓ>GkɣkL 3x=SSdEA[ P KSD&Ě8,S󩔵 re4M#V wW+ , I`s8 l0dL~J2Mә#N'qh9n HnP-ĩ fxxd_MMe5{pa?# C2dzfѱq8;jk'f"*}>Bv*ssJ&gkv;|>_-C!Ns7o15=C[avY\LlW]=(Ǻ:֡;=|̱N,>@P v:XW+_ `; 1==e\33twu~صKKɵty8|9A*ਬ$X3n`kBM( b׳iC˲ IƳegۮ, I[sv"wn /rO{&܊eYRQk2(QZ[Zqm.Ln EFcUw% ˲b,,.b&UUU~Y ,B<#F{vt,8#$A.^O/vsumݏqe貝{^^okB!ܶ@EQr0 ?x?kUUyOvgA>!<2K~8h:!B>Z!B #ξleYύR麓h,&@!B6nVX,B! RB!xw>IENDB`gammaray-2.11.3/docs/manual/images/gammaray-text-documents.png000066400000000000000000002522451413310443000243360ustar00rootroot00000000000000PNG  IHDRS pHYs+ IDATxgǙ(H_Wu{a hFhtgs{1A{[m}6~H IͦSʌ|tFfVH4A}У   ! |fsC#tmz^h,>N&S/ò{{O8yt:Eqoo?<%|(OLO<8<EƲ]/3sť\.'MLӷ|u ~x'?0X^^VQ|8(/_Y]EH v) Ϟ>F]&٬RV*biIh|55Zٵa|F!HFcQp,NBan0).*ޖ@@ A8p@Tq0 3LLiND1EJt:-Y,A<s _8>g*+KGU*թ'76r9ZZN&'M&e 47mm>Oh8?T*6eH xp,wmS}whEUyT: O4omoGco2_hjlFc~@k4ꯌbq^ѨJq(l1^Drٜn8Nl.0`mh4XXNӴLFY,"@1`֛߾swq5<2d}m˻YYQR*ST^مť`0tT*-9pm˵nO*E0̕7ܛ/~ /T n߉2_\$IA P0Lp0 AQT*%s*$IAvP1HfFQC0 AP*L6 jGE>D8nAQAL&rǯB3ʪB.yN;7? EP f81,6f޸~ʠVt:H_Ӿq ~`' ADQ_RQAQ HSyAr]z.]i`#͉ (俢(J?_ )R! (*-(T{O "qEpa( Hs?Ya0W.] ?T'yGQAp cY?Kw6/| U$b\f>_<(*,d`qQClzAA+! r(AXCA0AXCA0AT3 3<2q~>G{q4MGc1$bo]kk&v$ebYJlAGY7ܛ2e <{Er*jdl<44r[3 2QVRظVwonHq A*zm$"^EQ.&L.S);;c2:>RX:q Jy &GW ^/dZ\r?un9ҒbQ>QZ[F'j&ǹiE DQhQpG$J(LF&4(A(Q$He ҙ a(8.cf1gYVP@IL&\1i q\N.gY HDx"Ie1 d2I(*GgYR0 0R <Ax&Ecd2a0 8 A c8Ks zP7xkKkg?;w?taW7N/]]PQQVWS=42z# _Aa5kח\a ÂB! I2S{.r :>FE)CW!o;^`4 յ|c:],eMɘt@AqtFCJR4Mu_8st:fR1 e{ukA V?N-٬l>(Z,iGW?8PTh4FOzuT*e=;=4-1 @w7}c@ ATcĥ zz]V|jgNZW[d,îZʆ{d4675B@ h6Nuc{oܤHr~apl|ٹd2뫫*cX,w:j*Lr9tuC2AW$+--)//zԓfJC8!"qٝݝ8l׮|uO&n\xeEI XE(R(4h5׮x}L&Fs4 EQrRqQQC}ݑ J"NLMU +/cZC) P6UT7_x0 },^v9$ pԱC}+-g+^ric+AvZjD"MӧNuEQil7A$ǯFQ47l2 Cl1Y}ݫVfHaRf`0d0wuWT75UlO!I2H& $[[//+%$ PdTCC=eEaJf{nƺJᣢBL(EQ(QED:Zn uOE)7_}#;-|ʥƃKәtb18~$6-ph4>t:oo!=׳+..r8GoQh_ppph4ګA?l ;;^$rJL&0r Ӯ啭mF#b2L$vmo_|>sS{32:vi( 55dҽ9qriy9Jӧ:9;yG u'^va(֢^t@A߅L&{ f0^A,]+߼z 5VikiHJR tZVݛAfq Y d2Uw*ի}$IWWahDQEQRy@Ai80ʊ8}ɧzx tC 8R)q HRP0|҅ښׯlBa rLV\X . 8E@_%5A7!./ M` +QNy rAb.YCr\`($5j{3Ab%YV75?k AA?? x4|Q@AI"d2Y&,Qfɚ<;aD af3lScC _;zgf]<95{űt:]ZRDQi0 glz<ּ<*+Z:ݕKa AБ{ڵFal|bdt||rƵ@S?zu{^A#(QP/)__-(H'鱺֎L:yI'΢S{;g!W(er%Dz}NˤbQEQxU4EDyA|9λa Re}Tg28E1, Ew7_,`VW8..BE]sE]gqZ2c8f6.^(j4 NjԪ f lm_<_YQ.bC}m^kHduu]zvJʳXNhojlxA}{HϻΞ9U_WDf q'N'`' ޚ/+y?;y 3ý*6IϏ?׷q͌ǣY8T-vF,t㥇p`w=wMZG1'֎\.5<^Ζt.C1xrS}~ayu&Z"e %A:uqrD7**A sG:+ip؋ xgFE EK[{66=eEY,ͦꪭ&"yKQQT4mkm8f4'uoڕK}FYJRی"HIqJzuia7Wkk}Y3-֣9^J7h4642P_aA͗wC:fONqEQj7_ :~P?9jsZ!ɨT*QsT?[(y~ ߆Dd8QAP|>AQ 5jH]Tq bgwh4#AFd2 T*%Бhl2xAt$>DQxA2Y6U)U$IiQ),*JFqZP(f )*,D$ J3٬ԊL( R4qA:H$JG7=[G w.p;($IifAwvADW4y8\][FcSώ4\+ON|(CP(d7/rEQ<8JGFr0 /\CSES ~#Ɔ{3ˍO<;  (2 [qĤEFƟ-EVA(>}ýb_'AD" j4 rM3 $d2T&@^ "LV&)J xy>J۬y/}ώT*g1?2fLOZy-_x[{{Iys4-dC#񾁡L&Igrwyy5 ' 08</.-/+}i8FL&}^L&+).bYֽizýDM&(j~aoh4jaG:Z,iZvemmmm#86R~EѷnqQѧfY d*i Cw{waO aqi98r_ ?Oko}|e٩Mݶv=EMY̞mѰ\.[8ym|b*ᑱC7Equ}ݵF zpum=8lm;흅|g8C** I+bX?D:6ͥ)=6-  k4!RyD^eaJVjMw>TToI4k~l|2Hzoldd$IJ961H$‘x>ZDQtoz\}yumvnNѰ,70d0{hmlOFavn~ɵ BaAA0pQ jj{g7LMYߏa؄5G&㳭L UpozgfzponBaZYږ+. Z zF.GA3EQVk0K?jF! B.!Vj522LrErB!q\V(JVI}EjZ0L&T**R$I p$ XV8P(^0 MpEQ~JQEAlnj\^Ye96oGv[4[^YZ&fjp繳3s5U`~޾7_iY'&;N775*_9 ܽwh,f1J+'&V/?{ΧZ=9=tVV,wv76wNhQÕ5^?=;ԤhRt_ЍkWn߹yGOQd*Bfobٝy[;ϟҊ6ovnWXPӧF'& {eu ձ⢩%ײfQ(/VWƏ~Jӌ?Xr-;vVEwvjIR4͜hk}²ܿݱ7_yaWyYY4]X\׮^~ñl:3s޽1,[ IDAT >}l0T*]=:6NNMd^R\4=3_tl/\ݻGoaY}~?,R$yAFm-߬YW/ԨXTjo"l&45$Ir?3;d[V7ke%OP(jLFXGL& kx:|zLdx>FtE, DeYe@E t:}RKE]؉fS)RPWT3aT7_ Cwjz&͉@DphԨUIEtǰ 0׮\> N,q Yjs:ϛMFe ]++ `?{Ud..*\pzSPd' ߹a˳$y|,24LmS3.tLR%(eKKJv{Ţj C[kstIINj4FAL_A?I.LF5hDcMMM+{-b6M&BֺhNdU+ZVՒj5j^/).toz 2 Ξ˳zJb}c _ӱ,V8oom1 Mυ4M8).,u*RV+ښj*WN@,Dc%E /.*J/]8gMqQBhmnjmiN%SDfN6Y,$I`8qܙɩK: 6Uhj^WZZ,*,,+-n*+JKr֨5fpH :ϝ\pƆERVUT,Y,-͛LVXP`2r`0lgg7L\08&%@T)A޽ NNMonz _$גH&{6=ɴsᣭ-^beQCoYq߀T|=t$w:f]=T*54x~罆/%xQO_eESr?6i?WWg櫫*_dzrUѱEWQarCEOxx**GӏT2D"Y#cK:V:å8|~Z%0=;Z^Yfu7=cxtlzfIK{p؟{EqrjfZt>U<7DUUDo`ӳe6~h?JL&߾e%߬闷DArIRһ@0h' \y[z(Ne yyCg{ge m#c;;NCTf]v[|P8fu:SڂأɏV;ϝu]ݽxueetx}ђK/$<ųUt#EE\at|KY`v鞾~BQYQ>2:XyY{ϿTJxSS_ů_M6!kW9zeuUDfZ\4Mvw:66nOYi㩩rpvfv^.#X_''޿~JwokWAxG{{n1Lmfv`_<z}e*&&U*N-,0>1YR\tpp8<:VR\o޸vᣢ‚ٹ .uJuBgWUVl7q|D<8Ѿpxpx(%[zz)jomsR%u1x币D"ijj9T0^^Y(*s._w:L&P)k+kk=} BTO/.\+MKeϜXwd,ΓCq`p_vu7=3s?[͊JoRPT\׮߽W/$ o?H_^YS(\"T*uܙSC#_,,.͟8m6k8.*,D#Azԓt;;PQ^&yn~kݽc x۟fϞ9gFC_յ/Aŏ?]rh,v/C}RbKD(r^/L2  C0q^/_ d2JVg٢;uJbpxd踌xAhld2d.^oͳHlzrjJ<7EL,fN}2/ðC#(W~qR^e%%7oW|Ғp$°l(8 sFJjkTmM}%$I 1 K$U<D_B3Ɔz@0w8|,rH֖&E[FDQXXt%׮_ &ܹ3E=xX\T@Ql2?{l6߼qm{g3%W6]\t77ww@ @$˲$IHqAMO$eNǣR*/_xn\2 ]T\TP/ ]{\ IZ,拝\A5VUVܸzerzf`pXE騭Z\r]x!Hr4@ \.7T0nok5 [;$I675J}kׯl&+&҅vi{{s Hxr9q QQ^T(DQRT#lkҝʏz DDeENkW/Kg NGqRAdf?zpeus`(e_~U)E󗗗}|ðCfbwUJ@*d2D2 G|>mM5/,Y,_n]]?ӿ-,~rwj qr,..*ޡib9šNMf^f?~啵?q\.(=y^.A?S)Y}SLCã7kԚw楧{H48@0h2C0@fYs,8G"IQw=())^ZZ>¯x 9tاg:=3Ԑgq{+/+U(Y(R*+:]ScZzӗLΉ)߯ѨIDQWZYUawO6{LV=궘Me^_aAqҷI"(R*>DWwZ~\nFFE (Fd{lV+AH`4gf7ܛL[[S_<Jr4PEQvmv~ago`P.ّyd2A{ץMt!雜"q0ɧ=[+me4u<@TV 0LSS3IVUV ښy%2$D2dfg_<::d*d_pϝ9=(&{$I"Jd,fw=ZY]P9gk`;%ҷJ$STOoUe&ɷ޸<˲~AJH+X JRVah?PVVrxAR"9(8a8Qq'BE鉇iEqeYZqtgYVe9QIx6Ҩ2ǫxeX) KyCP8~a)e&0) AM3(J3 .jk܃zu"$<HcTJ*ŏ8M&+04F a+DfbF4"CW.]yaYi'7-=EQT:+X}wAIqfq\(dYVpiyN'P I î ωH3 Ǥ?IDd/H||L Y6 …]p'3;wvSny!lyS&L1 ;;9l Z?ۯ"ԉɩ?ɏDQO?_][pNgͳh4ןe  rYEQjJjl<=x&#Eɢ詓'CP]mܜZ͋'ڥtߌR8wv8SUd  GFHk9{[QVjkgΞ(--M7763W\Twp@`mC#L_w8r4?0?ɦg덛7^q  4è*ċDQ|,sYibII1C3cRVc 0i&p)/S3vuRt&_Wz>?l2mz EKsrL! sRT:Uq'( k{pz@<dhD{k<>qtpΤyyʊ w"(%@llw:e&dgZTJ%A2!N(,,|8575,e6JK AOi~@ tbguU~0be9 A@eX +`E76nIIqǟ|j2j{\^OP/(d24$#z$ ^[AS$yTGUen,-)h45ּ֖k^f-/+++-j4Ng]m;|0Znx=ϿjIEqm}d2nmXfiJ*_X0l~al2JCmzh|<FkyuPReO%dcb.Guo=>=4| {'yg=7|π% $@Id\q IDATz_YKjI-f{Ξ$=gٝݻcwOOwK*{oHf2 I꺪RZfz4]Wx=sOLm'&ɝ]gZirנ׽yjU`n~jSbT&w`"76eRsiF(v:]U*bw G*a{g"=tFV}ѯx|p8lٳ^?ΨT̋"y~bj.+f孝]J%ҸA6FNjK˫guԌR*K[۟}x1?[8/Ӣ/ Ch4=>84Mlt!zD2Y8n2uq8Ds|ɉk _Ԋ`6k8 L̶66I<`Y}`v~%fX]_j4bqfn^*|\.wppH$ TAB&f-<ϯBȲd2d|@4kmm>8:kNƭmmBDw u8RL6wa~qK$`0҂aVT# /.Rf( Ck:ax{{7JچZZX\r$Wz:˫kjjk{'2Ek3HEQ1h-NG7GfR:Y]][jGJR&:z=LÑRdRJXj6[ -=^]]8:Lxy~ν"Ѩuck+Ytf9; zrB֖]soϭj&g JLMϖ+WV-GۻD2Z J}xQ>z,P(ԪyϏ L*k*HNvd2J҅B~x98_=̍1a#l2c7⨱,A]D矵D2ikz}&Ƿ 2 B@VzAz ]P8bkk߸`H_Ow2$I(Joj_nkmiQ3O񉩋=A^]skk H7&fr}@Ɗhi_fB&37FONvwsWVd +*啵7^h4m5>YRYp8]Fc~sAЛcr9uٹG'._<EW/_jrܕKf+LFQV I@\ zVŜ/=@+.mm^Y] zT*{FWg1F0ƭJ:53 88~KR ڬAsT2Dr'txp`k{Y-^W BoO Ey>AtrxrljjFp=z+rjfL&ۨ7\,2 5NLr{ z>AqkO.HlouE6g;:IDKlmy Fc˹gON]8wjgGAJK+N8@ƕKf\{E*ܙ3zV66.ӵh2q,'d2EOe@ٵZ,3s3oI[ \RՎ=>Z Sӳj=GQk/˿ =;3;w14bH$u: hj4j̩v CZDLF㩓#H`Ћd_߉CPDP4f>_8sT2BthhEQJU6T*Abm:%dF!J&AJ]|i MtlVjɑl.ðfil|sgOol]<V*ULj۬VhYJl) &fUT]HԠ') zz>f4؊cXZZJ~k)ܺ}gtn2w~s4H|4A<^ήk7/xe {b@W L o?;h{ϞsOH\.kOR ◺ãgAxFR/^qŧ{bqt ΩVçBT.ߺ}<{}/:L3K Bb,!"E?skT*7\D/$8`YڍOzl6SJw>w9_6TŧU*h4EGOopF.x_^Y[Y]O?yV?oVU@eKhήI|h4619-ר4@:Fcbp6953 ?wf6#֎=o֞B!L/ck{8 ^{݉ã/7şX E J%DEťjf ܂5u88< + ìA$7"(V~H?zdSso~eyeP(&amcs,[ORͭfypxd2ҙӧvv:;;|>ή"]ǻxb7n{._`1ff_zUZ5vD"_ .;Xv%Moln٥L& ʕʾۭhOLmF{ᛣWR#2HR!lX,UkUee2>|?]9JszvN޺}gbr ǖW=J%,eյP8p4 ck}h4 % b緵YPgr>C[5dSӳPx}c3 D";;7\9/lmVAV?|t˲?oK++Nמh}.CӿçxνI0'n04pNurƭ]Rl:w0 Us KZFZ\ZI$xbieð]S k dH4pP+I?x> z=wpڬp8H4BHkWFv>˲\jOLp{nVAD֖7G?) K+(ON4juPw7vQY\ZwT̍ѱp8blm?/5Z̜˵goݾ BH"q#흥U[ùעCT:Q.ԃGrYbOڬ p'2M;z;jd26ٹšck7Iu}{k7 "IwP\.Dj4@@#vx"H$-㖖W n障w^tGkrjñ\.w=͖~_ ŕ5aV7=}’VuNV+J[;O~sTlb; I9EMy}Ύz;NN;:vd4dΎɓ'9+^lN̡h><10>=FJ:ONubpgb/b IDATC!B9:v\{ P8Ids E~3|<1a?wt2Z^^u홌F5vL&QJW %,t( 8J rrd"7^Nףp8R*1 0LbzRV a&JFWgǵat|P<8<$Irm}A? h,.Z$I ½iZd-<(j5B.=Il4(AvJeV;<_wxAW\7lFc1T X,r< #:Hq6Ï^rNJarn6d2fS•K{ qoںT&}7::;w`^fs//FZӧ Xw^_ & eA8^DQn81XZ z=8<PV,ˊ+JH_Y^x7T*AãL&[,İWÅah()K#'zzIlrM ;]{SQ_Dj:T*Fsm}E?b7  :]@P(=\K&r97wB.ʕr6[ohmm><:x>~G)no[X\y*KןR(ĈD"Ž^?E!˕J>__`T*f#bX,Rh6޹g('vo>8FcL66!$q @NQn\.Kpn jlOw)ɰ7?{&RBH+=J$A˕c7|)Rh7}=2T4/mV+ :vxh Gc1B~MEE\.Ggfa3.ۼxM&0V^[oB*EV+U,?y7L'F#'bq}`WgG2A& jx"ѱP(..mm˫kq/Ţ^zABao}Cbѵðl. gNr~`:yo4.?h4{T AC >|d2٢79@d:{ ]6k@t҅h<}@T:14D`O0c=]]]OxoND: ]HfkhmKOO  $cDۿ [8czsNwWwW'?+JX|s{H& ( PΜ:yhT(6I8 OWyRΜ:ɵçCA `6߇xݶ~7ܩt:HLLM߹wܙFƭzB?< eGN zD" 䗙Q*o}֖ b8Q=]O8{ca(y}=>;Zf~aQԯQ7RXN9~ezv^I+GMOoR~fvqP l0,΍,xEF J{>Dh>xqNjb8(`(0`hv~~ ú:&Lzr }Z ?`uGQEQ[ n$S(ɩT:mokS33;c0 chJ3hlvz{q^czttd{\6LI%A0 ɤ6(D"櫫^oTh4 B<(+8 EAjuKŠ{( bkDDy<ޭ60Fsڍ[`ֶ8}rdumP G| 0c<ppPz0A>⢼ øj&Qb:ݾP.& GǞٹd2дa v+ ^A=^q^sW5%6Ύk7GT$-JI陹}\NX6b%t:#Sb!!22<APOWR nB.W*>RH&S3 ]]O60sϵLĂ/.BTV+ B6>1YVR)AhljfnhpƲxãJP3+≄؊K$0 !0dy{,L5 #0>l2a\vbhޞrM.Pu3s<ϣ(jf\NbAļ0 ðvZ] aM(aBQx BE7޾[,y?|reEw*0V׊^^YDCCvۗ.OLMwuv|nߕ'|#qbcQ?fӵ:zzC|w3UԿ1d*[s_} s <[lppxx kW.Dx% F#2Bbq9<'zojH$n4R%( B4?wx<#(M11x #_ð/#; -_+Tjv}M jm}= _ty5Wx_W+>Կx_|=" ʵEH */iɾๅV,&f/*J`Hp$tX#-^A'/j3/胡PX,>Kk\ttnGނy16YBX,~^;0EQN a@8yQ յ͡L6ӳss C_~3n:NB137fsV۰tOLfXlBR kZ| qXhx>JTWgG",r9B@_.uTJ?F e1.Rzuzb{|ie\~*~<|ꘙ_x㵫L{brM*aǷV }K$l6W`Zlmiyh|ieR&ՍMdjmcbȉ`˕f66B!_]_(  W6L&wa|bqV[(T*ɑ ϊ~2[76R)˲jk6݇Zzn~q{AQs|>?Dõ{V6 za{;дރG}7nv*TRM$ν}Z:[/ot:]A:LFRDp@PgG{0>:>un20T.z2I[[;HbѐSl=1 *r"(x< G}`6&g .VͦJln!EoI$D"`qCm EÑ. uL919S.+ʵN Qu 0 ;wv)wN̙޾%YLP6f)NONFN k7nuwu>|4QgJNg0.7El6;v~{l.,.%)LT(I% ðZz4>!I66=^ ZTla;,ƭޞյ F76)t8:^܊'Gv[RP(oh?LN*rpfkST,[qkjsxtb^]u܂^]\^I&S!S([y$w[#'?Z&fKTNNHڍ{nBp|STzo-R<ڲ 9AK˫I)BD۬) s|\x}(bqӥR&KrT:xJe\xF&kRѨV?/ŁVR-.X&Q|X*q7>1x0waWgݽ[ žwrzͦϚ@",-lNf 4J  FSbQG'FgG@߳psk[K՚q||r P([[`: ;6h(RԠץiZN˗әѱ?I3@uwu۬B-C *Fd* OoqdjJfY$\./˝J%Htwizzj16CD  ==6a(W/_VgO0L!\P**Vd2ښf42^X<.NT *|.1 l9 xB24T*DqX(nyJO$z]*!@?cD2 *rz1SYVL&bshG{ zg~C!b^cT*3 ޞJ*D2r9 =]v!1q\.&c:YťRP,hE&(NZ[Zꍺ^xv8aRɑoum]K$H5 }~`I9A>G|d2,[h7vz3N9`( :owwO_ӧr@/C}/H4jtxhd*n+8N04W6H.Շ㏁KVBh2{IlAv;"BdR ru,vtEY/xj4bO%trI \RI$S'I, ^aD 0{= 5J?UX*]{{-B/ 0jl6QkOthT.?}iAJDRӡ/C*  ě{= o"\ *bS')lS3s 4MRkW/+sg(/Okaem^J M%ٔ^r9Jzѱl^tAR-91?; ɊbXlm1T*VK$ϝM3$IF (ST.ghAC~= 'ޞn:`8sdomm}׎=j4jNzoݾs¹v}{g^*Nbi1tZm</fq/Y,&BdH_L3JE(q-cO.&t\\.[:['Gl,t )HJX,fFck{~AJ{Ǒy"#U IPU,V鑻kwfvvvgvvwsc3;;===S],jB%RkbUdU:O@"#<L&P{[X=2=37fnl:Fhhhh6+k|ѱl)履8poqy[bf>r)IRB\HӴfѨjJ 8.Ngtp8|. 5j5 u:\&eT z=M`HV df0Az ǎt KRjP) F>K%ѨT* BPa29J%]]F~ tڬDcC|pp`~a ð##\KQToOO2괹=^Tv@*gΌRiAf R;].{jY*Όj}= 66Hl2 B\( JbZPj2j_/ϝYt\FanCϝtOvu(S'7'eYT+'7[} Cr HBxk{3i*hpJ٨74s×x|A"ܹ;. n=u"$S)72|(BŰ0k7nPVkjM$}=K+OcsDžof/cL.{Wٷ ࡃ~V|W_@*ծeX&f i^G3 2t;M/LizZBa詓3J|MN|6mڴi>]f74nő=>/I$oD"ƅB$#=.hhÏ_y"$d2J* ֪Bݧux|%诹ݦM6|%hAdR){,01OYEևR*J" h_5=ԦM6mڼ<'BΆ؋F'HƄBR)ݽw?<^_>_Djt:$Ir~aiv~$Is훷ҟ\Dcol>]h\W뛷v{A4[?>\>l6Y ck'Sҕ+Fqw|yJLΕ˕{?#FzD߸&edݼ=4杻O`t+u B[<ۼ>nF8oTQVEջ`fkgwJ0}8B^8x`}s+ 0x\V16Wz?|U+p8TݟlZPh{gwk{ghwF?'kܽVbǶwFOP(vc@`c>rǎdsP8 037('jz*Eck'qBqd6axzv/~u@qCj5@W*UYT֥h3s :]^_Ru|sT*-.W0tlyt!~0lcӱEcD{f n:5ƦCղj*JRيM?VvNAh(_𕕵d2"Icj9aj r rB}}C֬C{Qoldz"xu$ݟt8l6b?Mͭh,V.W׵յhAS"߽7V]jUMV Lznaqy20 _˕H$3su&a~qȅek4nwR?зfvv^A\>  ⰺf/'Oۋ[ۑѠYoE $N WBD)ą:P(◖u<ЫV‘NU =2 X9hx0595sxjq^pZ:<#}RnP*aEP>rT*MMr8nN @D"9=;w3w'NvP(gόdowHT=*ܾsh l|~x}]h4ܽjvvM/;szvnQR:y;{fqi'[X,uNck{d3KEN<053t;{f5DkuVC3fr߾s\.|B!?{܅N̍=ub}sSP9} AsgF7[UIQѓӳsPh;pl:O>plm;wN?x7<B *J:6LMl6f_p|$WO ڍ[Fbr'ɛ+Wbxhʤ G,82rxzvvwuV*`0p{< K`RutwM&>e0ީYuQăh5VӖy{lkg.G.=39x+/#nw~a) UCNwOw7CbHV ~f<66^U*՛ˠ343`r4 e2)AfszKM5[͙y{5;aCә? o8`6 Ǐ;Ec2  }~ xt:iٔeJbffc{癎/NBEQ*BHEGcq{NAW_ 6}áj1'>vVTUO}`P"  V :ʪAgf_^]3'Y;s:l.W.W\OO@ \.W{|zNgq 1# V)bG*r b֦0 㸐UOq᥋oz\.JP8ʗpD80fO~b">?&)lvlm:Q{(xh ˥irEQfs_oϻ-qD(0(I<y|>?J$aWmVA堨'~hX,Hf2D,8fE3>Y0r A N;Z_^T {}~.Sn0E\Qaۋ‘=AP(ZÑ=RAq}:E("JVˏ޽Tr9aj-(R*≄Va #)h[= z>/≤X,@H*y|v'DbX~;*R*Z\~h B.J#+APĶEqE@2N񸬔j T֪rE!{<\.\&CQtj .3Oml:i{C W+ dzZ; hY-ӟtt-扩i'/FO<{zl:]Bq8bðh,.H==?%R A8y úlO8~HV[]y>߽9٩Y$Qm6[PxZ p f^^DNg${~+JwB0@&`pވ~T0P( L<f{ߔwCny}V%I1 k4s rF!=s ,OwAawA D2ifs9Bj6M\NTP!E8j|X$B$Z-@Р׉E {yd4d9DhPbFA2T.UUb,I z}frZby| ÕJERAa5aM"E,FVk4CyddJb\>OH$bNTJ)KPXMp8b0$Jgb\&*2%IKEX\*ܹ;~@DRwl23bdZZMEBѨfsXa$JIJPh fy>B!|T"j5lUUbF~/Z-aŒ@(hMB壱Hy|FL*U8Gh T*@EQiBJg$L* 8 P8b2*$I)?2zj bQoMBRE d4 A&iKj*@M H>"[izX*B!jpdfX JRTiVTAX J(P,*T:RlG&@ T"I1Lt&٬V[ՃNI3ðx"a栜@0(|~,7L$IdJ t&K$b4juX"\$IH PX!3ѨYGVS.WRfd2D 0+ًBh (r9RY*p!FPdYJU,sT*l6+- |>P(a60 |B.p8-DX,pBL`V(\N.5 ðѠ/Kb8 r0L8WTt:-ˣ( AX< R!`b=wAxmŗ(s<7J:37V) ~Yam6 Qwvٍd AۢObhu==*j#ѦM/NP_\J&<>kdxswWNpڳo6fq#C M6mڴyQj Hgw|oH #AMs5~znӦM6m^lu(ғ@JJ x}IolC!Aɤh鞞;}f=22t|㵍Ma~ARl.]|]FOwBם==u"Զ~CQT^/kar"lGvi慤h 0a,%N4MuJu@0Hd:}]g^Ǹc:], C\ +* >rMnd[;{X,>v{ CJʵgWV3Wsص776_Vdjai+Wݸj9=afkӦ͋E+WOM<]0 Cj:͎tl xVKasjU"x,<jd ?~@ajjk{ Fۼ($dd|[RE.`z&OʅFiFÀL&#J. Th48vHX0LT"/WߴX:aPZf400 K24O|t*?|7za> E}nO*ds?O"EO?iREٹ3V$I2ZEQڡvvƭ;R0Hd_8u可uuD8.Jr^o6fj^^[.WP6/?uN]\Ztp82ٳ zl6=|?p`}=4x~p" O@Lg2/=> zdj-ȾEDRV*+kx|z핗Iuu٬0 ik7nuwur܏.Q zX$b%|>q o> I?jl65M__/5ud !՚D8.i4\TD,`pqnӦ՗/\B)W*<(VIVqp[㏗g"Q,_Y,ۥA+`Сx]Fi?/~Mc[3FN;ªp1lxhpa=]O"AiӦs=Z 0,}<A1ڴiզM6m| '6mڴi[R*&!< ehDL"Id2iBFmڴi`RQTm=6|xH!4#":,mhTk. F`]*@ D#lTawvi>4p4'RP(z}L6ˤצM?f"{{3s & ":ph XqHp1.u֝FQ(ZaI>3 CRB.o4.kX\T*Ae6mڴRhjMQM]JZ^~3N3 ,_ PX(F<&)@pbR")XuMb(q|jz&biB.w=.RT*Ļ$D/w (&I򱗻Z&,_XV  24=~# eD rtdx/S(JR2t;/$V# 2T* Ve0=2<AWwCB~u@P$If0s8K0p=v[o)N? ?O*~"mڴilAQ|>g5DWgg0(pyr.`0$djZ).T)VxfSd/A9VyͷT*y7m+ڴi  ˡ=]]|{W+##l$b CϹmڴiӦ͋-M&\y[¾M6mڴ<΋ܦM6/ ͧ0 CQPvz >x3Px/j_zjvvxŘX,~kf eLr>_Vkd۱T*寖,0W\^Y}5h*ʭ;_Qwvjx.gf>zf8u.aXiJ IRK4M?~v(x}i)Kg.~'$?rdvW$Gg§nH}nHϖ'ݐ )2~}[>d[Xt܉dJ.%d2Vnfs=f.|0;lvw<ov~l2rSӳO*ܺ38vؽNXAQt{{w}sS!OL=bd0]nAw;]nR 㭮 X bĚ}C k7n < 可]H beu ߯D)B\8=;tA?`/Ec䃝]ǟ3@(fh *2:fɸaӳX0I5RHgwW(S3PX>q z \n"-v~j4Gt:ݱ#K[ۻ;ΎnW_~t^qnW$Mzz % ËK+>Džn5_5cKRݹ;r{z<\^YKD"7>xS\&w?n5Wַwv5jclnվfGsS'AT*?vC(P(P[ۻPW^&[c6kW LήP($U.^}c1 &&2:]n@jăϿiL&=pE|KR%P$rD2v].@dxȱ`6|pdR4rYtZa6 Ec7oQ(h4"[{X$&}X@_oVz{ !XLD"_ZY]~7^c;jn߹[Vj.x||/+drk{a7^{5HZ:̀a`_Xٍ.rCfu@_> /~\ b)J7S'ϭLϼʅݽhܙMK^r{RhM)C ٹ\.?;?/'N[\Zy7VV J% =2, _:w&W0K+j&tN[(ff|JtA[z{l6 Wr0jX,nmT*ճGcL&JgW.Vkftwt:'O3lTbC:+x|eu-xC==2^,-dsBR)&c0CLh0h5ϟL D1A&:K疖Wnk, 8_׼>b"p8C^4KkΞMR.8]plj˫kRYϏkP8tU_}eyuͱjsDbñU( 3 ;3:ʶr'^ϖ%Tk @0tܙ*[va&&0̽diy5O[, y.q:MFCX 8#(0tA$)`q=\Aq\ ph~p"E9h_oo_Gg4Mvv4Ctw?E+kv.=t6Q AS*u:i IR4:ؿfxnw}cZP,op\.,_0".;NdZXZ` qeRQ(]XZE\ :c\HP(L@ @> WVxyeM(ćWV#ÇS4[ijR"B! 2f=iph;JwWi03 353QI$k*R >XRtlMMj*+J&p8Fþa>t:P 7Vkm}A^g2z5X,lG|~{gWӲPlp\jU `\DžBtwY;̯2ckX()GۢR|>\(D`XPX:^:jE"j--vTZ0t2MӭVkiyE!=Ѡ7zCQ%qP]v^TzF@fA`éMAQfV s 7 g\.uoۊbj5 ydz!h Ba\.TJŢrr8B\. BAJ ՚P(Vk<7 |HT(E8^%1ILM$JPs<@ PEEQBEjUTV5/+bZ"B4ZM@P*[rF0 IQz]VW3h6eRi2(RP4B(dJZTb(* aX,ZR `$bq$QE`8NxLDA`A0 (*OVy/J2,J"l6'Jx<^:r"J1&ˊD,&I2O@V٬BpO:FM"4t&q8|>_$˕ .KFPȉfSP8JENWTϜ>v ugW^V2AEz^oԅ!$x\^dhqhuJYVE8^To~?Q%J)yrE$‹RR(jU(VUEi4 !0L4X,m5rZu.+2HDoUJDv RAMPP.W>o.I RI,=%pOR0 ph6<$ɇ Q,u'YajT() ÔeXCBh43R)fV*E"fVGZ2w'6=k'[*X,Ţb$JHie% \.JhA\dRR4 CraaʕX$b&4R*x숁 @0oB JYpѨ7jA|I%*G?/~M#[(ve=yp:Q)h\mZV<0L4M۶*ʃN{dx$h,a61 lVl֯QՏ?y^4R* y %hٜ|00LXzrSă'ko62|ɥ71L/`ӧ"cGAQl@fg5=`4?sϴ\m]>D=vr|~NplL4=3r{>ir\(O84.f_mڴiO;B0.| äR/f2vH3zf1)/EP O)]\f>ҙd8h|d2/Ra .S@٬վzsS!E}>Ao6:><4ZF0$;F*!_dz~WoHaX0 M4MCD$;4EQ1 I )f:4g;Z:((v{] lll6 ˍEww]αRFQ$Iif)}u. O)=^oIR(~㶥Ì ȓ6D1ǣc}` *e k؍[w p7rB@ ]!j= ?)& BA+ 46~'ol`Tx$Id2)S0 &Fͱ{eRL*pW0 IRLALVV̭;w/]|+oKPW__\V6E! S9N2R)@PQZ䁾##4MAC4 w=0 t:4gό*Nor:*+냔0 0g:No+[;EtH$*i=uB=᥋ÏCMi6|~?`br y.kt0̮wWݑ${#<Q3UvO{>~UɌJ33K`F}{3H,ߓ#na7'.\"\%nn]_?rɔ 725={ߺ{?( O^$n>.)),uO/hURig8).*EUfStað֖?~zkkߋa?`ie"7 f2W\&(&pBYY]s KK,<|gu:-af)r'ǟ'OxAp:}hfCt<$rСP(n޺MX,x+ jͦO/\:NEDRܹ{?Ñ9= } IDAT/\rщ)Ze4}ıήϟ>Z,}Ņ~|ra>>9iT?xLQȨ?P0*MS.WÎǮxD.^qyhxDT8NM&=?WRխjį~;w•5j5 Lbv̩%ye4]S]um뛛p0}n߹ahkK߻08yv<~rΞ}~Ȏ8`}qd4Uťe߿t`yY$IufPhzy3N( ɴgWk.soyVA˖o+(pb%_ ~@uUe4s8џ$E8Dëkkjԉc/6UV>y|}cðgMuUUUsAkZEQD(I"|kcC=@Ce2::Ȳ\*ZY])$ɲR5U΍M\.?~hc]ZRtnknlB4M _cf<`,Og8`1 X.2Mt&wIϜ:aXUQ^*ɖW"(a^$lu5ؿw5ط DZ|Mˏ='ϝ=- {w磌nkiFQ4j)+-t=LT&mmiu?&")cADQd9nyy5f4jk7Ν9qf}}ݞOxUeJ8sVyT+I'cf{nMMH(#8^osMRd6}(n?0ɔwH419%Ǔg48zug-ǒq?QJ~Qiԉ@P JҙS'JƆ;8^Xru.0<2O$*}1e ۳ x4f\KM`=Phl|A$3 eYѨU*^0 4Mjk3P1 LT(FNe J%zVT޽0_Z3 V޾~_TX{WVh4 bS[w ^^um.a:@EnW(LT?@Jjl(d%X]UT(Z3PmnltZqQ^s8l(L@hpPh0(^eXZ2 M\^PF"55U^+(pr?L&EQ4-aDQIat|bqi9=sw]~0+FFӢ$8 BxM7Uk;81(G8yͯ~,OZʅ>HWDQo7]ӻf˷<ՕU6e2ogS?Z[[/+u47~nOvY,\xDkj>p 铗\(+-u}>AMQox<\߀ >_:AQL<ɘ$R&uom;s*Lqw~Aque} ųHX s|immy(0 *d ðf6hp9B>N}pxyQmVF雷R) IN5My$AK$ibN,>P繯 ͌>=>=;6'.6ACϱ,wǎ-oԮѷ>'r2 1#Iɬ/͋s\qE|(kDT">q8$N׵Eij( ˪_gg&N}7d!7 k h(<,)MMܫKojA8wnjQ{f3{-&bx4{A_ymFos4M:O/lllݽ Nƻܢh, pi_;DZ] K+b/d4nQRQc9뚣@2k$u:|p->q1Ml&27y*Uc}$.L̎`Sv$oX6JV7Z[i9pDP>y|M Ro?xCQR)I"@չזזf+ϱ]A6ΌO˫'V̳ڕjMQy.^]&Hrvl p l6Y_+H㳿Ol& I< KıY Y6al6IQoAq( @AHEc VSH8.\*)^^Yݷgwo@:9>˲c^fcN4LN^ z)J$'8i|N_[xvl A`=eF” Rxϳ c(nWanM/SWt$&E66,KtgWgh4\zAͶڊDEE;^&AAbd5#e7]yLQT,ϲ֖*(RP[t&NA3rEk::$7DNM[Ņ ِP;\%NGyY]Ǐ7VҦ{ ~>flݻ3B:[gWWU~_ + R*쾡'sEyS_08ll N IeFhtaa)2 } 鴕%$J|ӧAMQXLEIZ[J]%H [xRUYP(81̖__W;`bJzzd}]duL*A;C7nlJq 4jumuF!⛓j|[~>6}.*֋CqR8y ~{_zAE"g|J+#aAзjΝ9KA 7VA| :LFc1(JeyAbj*NgYa@ @ӴrC%IbYs@A_=s387˲~Z+Rvo0 JtHd2VL& IZ v-L"tWV7xз 6,8rT:@QZƦ-u!hlq ðWHW($A?C$! :R_wCA0L&EIRT4M[I3AFͲ $٬XPT(`fF,G82ZT'J"ES $IQU*sw!Iy=>t>~i(艫xvn>eq/.*youP$yY\^Xܻ{ׅWx;_z! #6"Ly8/-(iZ"|6Kzy2t:H$IH$$( j1I #G[8fTJNCQeYAȅbm(r ?r\i sL&voMLMHL$,M(i(*J^_tcNGIH =БV* $pH@FJ$qj< A7G$j6G#ܣ^?}L4 ^4~\J]O:^0cx&~ٍalzMo1 S( @Q$P Mp$)h|‹;trnAT #Y,M6njcc*:xl2())D1VVMy}{wb6 `ai EcG76݂ TV4iF<>1uܙIRy3NAЏGsS䤫3&p\E  HX?_ۉӮٳm~XgwZ޽}m~ F‚^#RyıOVV> b!)A  XC/j_\$I/丝TAQ o۳$I;AZ$HS/^beY汯ˎ!b&y~O |ܗvAd28Wxg3K?ӧ>w3T啕ɩ/n7nݾynn, ŖWV>zͼ˶?/_^Y }v1r)t<~ŝ>p)?Ҕ6b?pibr* K+J|8H8图;O~ב>#ɕk7nݾ708;7?v1}#`',9o޾{Hr$ٻ{?*d2yuӧ߻iGFpdM ƕҮ^{˖ntoڎO [w!rT:/ImM=uXWwo&ٷw-?G͍ fY?}zsgAyι3r#&fd2z`p:p <ڍUzqt z^o=rٹyF408l4:z=\\T8:6>08ߞ:F~Еk7t:mMU%pw8**waa3wG}Ffo߽aX1>1{Z_[voǟ3vpwp$izfviyBO/] IDAT\b9~N&Ncn~$T:-d*uЁ:ЯN~bvmrjZPٹ9^i(u''S4q,/\"]m=2;p`:(*/,4L&+DIj?¥+Z >h"L&5=Zw>iminZZ^um WIIMQUtZRݞѽsW$IAbC}gO0o`۽I'MS-M(?gK++wu>#G'2L,[Y] {v,IeL`OEQ'O|Z<?AdܨMV*MҧN˲'e榆ӑ$iqq T*q\"DQ0cx(V*eNǞݻj*WVq_\\$ Ncx_`D.d2ԱoϮqRzCP lϞv=Zh<.gNt\>6>DBH,JJ]E(y}> {vO(&ghtaa9qðjEWW &3mC1u`ߞ*F(Z͇pmkS(. Z['>vye%`6eYؑٹy/2l,K&gOd2cAy>:y|tlyx~ h4Nәd2r/ :q, G{vBᦆzF[jkc8ѓ$I[XI L&#ݽ}[[3NԪ x8UwcssssSE)IDQL%X,uV0Q(⚝-=d4"}{sFa(637HQ'JI(J|K|$P@UUpmMUJ~4lQ&fJAO\%+kn*t:1 E1ntuNLN) F._Y]Vejjzjf6jHAE'Q\][OR2KW|~IqуGsA( pE aŅAq:.]t"(Jt޼uH J]( +0UrÉi\y.hEQ PE0 E8~νT2j/^0 |iyeYU*U Aʵ6YvFvw/^qði$ vVQQA.!:vN Նn|edt')DQ9۹(**sr(aMTU[[?􃂡q8Jt*yN]M5ȯݸH$>x;v {C3"(NM-G$d2Y9CQbpvo\NQ4tZLL%AZ^PVz A.1` #8~"wO?huyyo`NRksGR+nme{F¡'w>ބ( K^/~Ǒdڍۼ/z}wG| _oJ8gF1ZJP8aD" 8.?*i34 A̲YQ ZPT@0A0ry2T0w IJOB }l6+"/݈S??LQlbY=$r|}}CA@qE8DRT<$IelH4*rO8A$(b({\pK` 櫶LyO$ $(j~Yt|Yz^/whZ헴^|Ş_2~>מgE1"t:^ܤT*r @d՚[c6r YrrϞXG?Rh A*(HQ  6 CO+W/IR4soy*Jsfv.d$֗]y^_Z/IȲ-x\\ _ GVV׼>W]M{ex{ko;sзr Uŋ_*ze5R;atzq?\?P:ekkD0חH& ES h:t&;=3T*hD׮߼pNh+k4MLx88 T*D#J帋 .R){KT(޺xƦbr<);7*3T*J Cp`ӽ0r)fYy 8O$1BIHðյ5Xi`6eH$0r{ TJ|\~֝ }~8AQdc $p-ŢT0@00 </#:.E+woint0+עGTRFQǣV]][\]['8?V<FDc1Y83@ d،$I8H&7F^_(owjd nlyOr<lmm x}F.g @5L`:V(TJd25\&Eq}cS0p lյu)Fc($I[O6%2HbD8Umg׃P()>_W!;a k;p,d2lV& h4d*9<:V^ZO$]IGOhP*3s I+bzNFV%N:֝{AtuTWMLL`(x߼]XXxi(q\t ,uvR) g¡kkFGzAv&`Fa|b2Jmln*^Ōظ_ GFǗ^`{hG'j^ӟ/X-D2ۏdpx8 (:XVꞞ7oYY]m? n;BӮUG'-͂ tpyy^./XVVa~R*.). DhK3A_SU){V% UޒYFcHeFAo,--g2Y^W^ #/u$\D oܺIRjfC!^]UL&s/,tvlmy>_Mue\ft:Fr:Htaq1Je1pEyٳVnuLF`(T(>H$m6+@^KPEY UL&jz(Ǘd2,bqAVVV^Z-HD4L"475XiURz)u$IVcEe2WI4i6|CTMyHe9@qQEQda)\h, p:/z Zto(R]mn.^^Z^EJzF=7oJ;G@`՚*J)e6[> pb6+aOeqEF( XfF[WSc493>1qf[X\u5ITRTDT,O&fIV$*)'NJeZ\Zr #mB*2L bq{`^ImHDom6F.Z-x\P8EIbYVX%2ig3v㧝2$IE26LBἼ<<<2ܾ=GDSc#AURx=*JDc1*+@$mkiEb6gxA)ILF615q|QaZ[e4P*6[*.)*byJKJvuWVkjVT*Jݻd2YѨըNMz.yTOLL.AGZ{lb\&=m lãch=ݽ*i40 Ee)?POt.;݆x8YܷgH"hijT([Op؇GZ[xvEQS-M=ef ðٹRWظajkbѨ 1lC#s IP`Ta)F~V~ FYۉQӮD"s!|~>KۚFFáÇT<8~e{:N< HԎx"Ig8=_cdtlqib6]β샇NE[^Y;{w A;#a kG*` |}!Qu'w  AA7z^'{:KK766&jSTn:W\T`kZ666it M``~a^ AA_sCL~~g/4}޾ZMԝ{]zz{',!IR(=3y  RPQQ.,ˊHdcc^EqQQMR~h4:4 * ժP0C#<7׽^ѱq[r8_l6nv A]H4=3ʼ1YY^8ap8TÇ (J..-756 EAH4okiꞚ9t`gSSS=95]Q^iĶyLdQa!p\,Pi?[s֚[uQX;^J͑xG%LܽGnP*1ҧ:͆B}{w?~ҹ3x$W8|iZ$ uAǣ_-,Ave2(*v[Ey XP(IJN{qbL&Cd$Il,|> Lyy/ $)xA?v$ tZm(>w<~lccsvnlyǎwf3Ydۀ|~Vz}rj4L:]UYHg2ceeS5 [VKoÎǝ=NZ][?_WOߙ Kp8RXX H(f$T*‘*r}cFc8I%$M\DI B(bQj:rN&yFe76TSCt'g9q\Uesg߸z H$f?wfSn/*,ȥ&RWIQa```b1GO +N3 (@{ɔ_^Yf2Q;a(j2孯o$xIB0 `ceZ&LFgҙh,f4j+=옘q\.) b #().R0 a Z,ݽ}}d9\.nEUR4<:qܛ;K~vpkAiH |>h I5>!B0yd2ISt"lW&a zgpYc9Z$A *(NcuuMQ3$FxQQ᫕Y ıD'f~ E  āFAq\Vj0 ͉dRRiZ I6@ EQ%) dg>  ވ,+tNϜ؈=\E܇ݽݛۻ.)J)Rހހ@›L}eJR(Q>Ti^̂|e22e8KdDo^WWWcVAGy1 |0V VaY$$yo;j!TeYѸ- zַyi27 gD?a&'AA K lI$p8NIځ! AdqS)nyٻ >vxdTm yOZ_795jKJ!e%U?X]YFI~֎ O>|``hfIH htKmM,xfEX̕X⩟=#ey-DEQVׂ++ gvvnҕ99NY_L$]kA 2]Ez%Jp)7'$ nAQFGd͖Il4~w8 -:Vo')mVdB`4  IDAT6 ǰ;*@Q l6[,Wf$LdY_+.*|YQJ#ሪ5Um7wlV[S5, ^^Ҵk'2$yfg:~`ֆIAј`0d4^^ kcmH&,A‘ϧER[)DUկ=~wF9|_O)!(} NMڹ=ݺswOή¾PN`n~Za{zssr'') ]|fiܵyWAX_?mXv3H?zx3ւ@ }V yQ4m[?ݍ0 '.\%Ve/<~LK"JNh4e%>_++4]r5/TEEUE_X|O[Z֬ 9{cGGFmV5/*!Q!<r& ++۶vܺ39}P"r/h?3]R\?0DQdHrO.It2 ]Y^UXqg9sÅrsO?fsTWe8>30l  Oh`q>ypP8a5f)HMʄ B==;i ]A7 Cͮ ț `A@`urjyAoյwGIt2\_%C 7t<(, 'H<G$I.K?jfg"Ѩ&|~awG&& LLNѱ1O//{''8/*,܅ֶ[MOqD9931NQԏ( %q,&~'-,.ojfi2UU@f: a0Ҳ n=w:+K;z>H4RVZgEWgv.ϕ˧Z22e(ˊFz}ϗL.75ml`>A^kx<>@UUkV+sg{H{?q:(ʲ $mONilt:E,_zj=l0|~5 Ijl|aϩÅXslIIz5+zl|2q}MaZ0HDŒL&wlۺ|wxWVgNٽtı;dggC$˲@f͚_;(TUܱ-S,#:n9y= HRm(ڱ}dl ={gf<O͟?n(8Cmd2t~~Ĥ(:.MWWWզ^TA^-W]?}fn}NgO7o=|nW;}(ÇKE^\ZEtYBqeF$IBWnab1}Ԟ]m7$IEVޠ$S'GƗ+~2ȆY6[fX0aIBw74M kдgU­ uNΆB !T=7}{vlhJ  Tq⑷!8$I$0EQE$޾m]KK@UUeYvdt:V{L0ޓe%ؿwdz;y0s uPdtt, ɲoO㋈i>v̌$Iٶ ^ǰl}ÞYZ-XAA"+fLĪ wgwXZ^ۢa- 9NG q:J* 0?ǶoΖS4mZ Z*+\.EQqY]=PqL$A4}W+>VkfVݽk*6[~<\X\ GVGFEx$ɣGwu\ݽU_(+y=;//jdS 'p8Z-(YV u:M/(kCqD"[wzWVGQfo9 Ȧxz" I,=SJ ؑCUA  `A(# &@y?}eٽ)B@p3; ck{t(#O <3I#CnR͎1ШnA^__JO?ҿFgf@>xU%H UUH$ʥ8 #F1c Ғ9>P$I `1 8n0i6 d*H@NAA^8WVRE,>Asgڻ38xzkBc]2%<\Y=sۘ7jFyɞ<aI k3ַ!{}7>=J3L]A7=<"gZ)-m.^rtX]s~BFƿoɩg/]t]|ʁ(+>OC0?R=ܺ2skvv,-/_dϯ9?8-!ޕ'_ghKxF߷E VXl6D͎e/Zˍ?P]=.]}orj_|w~(VV|I[K||( ͮśCEUeY6`Yvummaa)˚cXǭۿ/_LQTQaaS7ۧgnٱ2 }U>KB~{}Œg6J\RXF(ܜ=cf)?xX^Y-(׮+5U /$Ixf'ZnssV!-L[cx81!V[XH&}Pxrrjrzftlt\x/ϕKRd{xdrjz=Bx[{TqqCa=Ks.//./^nH&?l[wojsQ׻hd*u(_~mCCݲw%H~{`0LW h]] 8WVVtZ0ȶٴZ ks Cn2YYYO?Hԕ{jaY>uǎ<ݵ-W;0]ݽCa+p=<˕ÐvȲpM&(P0]:6q%"HpIVk~ktl|u5x Aߨ(/yUUGFFϜ>Dt:GAPťe7++ʋ vlZVZ-{pyX,6iI& R[n? 0 `brDׇ#ʊ*ܱm[iigv?rXeg:]yY; =z$H?BFFFB(QU5D9u]8A{ٻwıյh4H&D$)n 0,AǎO&O#@`UUcGG1I+J S)nݷtJtɩd}ðwϜZ[ ʊX]eYV"O<>4?ϵNUP8kyYYy²$I($0 gZTEQU(EQ8j4Aq|Fi"YIEQZ |: !):3vMUUA4 IrGI(8) xgY$X|a!<80({,~ʁAi:3 I(Bj4(I$$a@E0 4NEjDQ49P,?|; R))80H!Mt:P+WY?_V*8A()LEʲ `hZeYZ(J4M0Z0x/ 6 SG8j0 HDQ$IApqgEU$IRUQYQN<>>> 8nKÝiAZ̆_2]99caX<`#H 65_[[ BZE[\\ڻ1q ҂008H$=277ݶ 維 G^z l )r۬}{wWUVl#t:v= ֘浵T۹}asohXVeY,?$,`b6=r,M8$YQ^VP_0 N ;>1cN6̙83 M4Tj||`ѱyCy;}o/{yub-2EI$IBh'4Ec8at:M$ 3נggeJ2EIR^$FeӂcA|j~ IDATT VH$X&3&A-!0$AM:R! &@AA6Û oNuG^QŸA1/!|`]q HH}Ed h,>S Fyd2t|wyg@\J  &hk}AF[`AydEy\(#=!J͆ ȫO'' ᑁɩOcp$22:5G^MW|]_Xuݽ}ή$~͹_A0X+ߟ>ݐeY^YUUV贺յ?!;w53 sng2ⱥeok[WZR2VN;˲=}xf MNN @d@͖L$Io`gDQ0 ?$CEUÿ:n9qX{-e4}TV_3k!7t...]Eɔe{HeY vv\C/7h,6;7_UYq%qyrr Z%A{ eӱzs.^tkqiXYYQj#y.RII+7'^rAoL.iD[{ĵdzK뾽{ xth0 /-w;oz_[ff_n2cIJK 8@1͞1+At:L^[yxo6 D2iݏ2x:"[qbvc~8LX /eY4 o^爾R x#v fקF^YK(2fZ u4_X.CA7ѽ&rȋ!\[ f4qtAPvWA ="b3g_|~ҕ&EQd*( PUUYQ EeB|5()A‹ή_<Ϸތb\ sDӵ>y&Wnn;a/+- յ!$I us<+׸ 0A3O;2}~$)*L sıҒ }J=O$gϜβX#tkU=5Uc OͱjowZ@DM]ɑL$X߷܅KZZk4.75 FcvM65_snLH4jZ Z 453 saa~^~Y7A^E4AoVHhfҪ` C͙U/bnt`eRU ;`пBdYmC0f0tiuzh <'B*0,k~ona)%wde;6⢒C@,^f}{yǎ>\TaAʊZmAWɯ<8 .>%+زsoW&-$cL -N9zfXYD =d ^xfa2t7{7ҴP@1ld QòM B*a8 pT2EA&h`wzW\ݻ++(߻oZ\\Jr5h 񪊊AX@yZ@Ð6 BE{Ð LJ!,T  W <Cm$)!b5 0 [AAF,ElVAH>~hKܿox|@ݖZǓd6<ټ`AyBXQQnZwF P`Г$k׎G%XUE?G[/^?qKIւ^ܗ\M&S[, ooVBw 0NyyF3۫ 9}rsNAA^U(+ c06. o' !<ͪ}FA6VCd S ȣ`>j 8YjDV@`@yHHh @& h2L+NF%1dhqc0'D\C5U` U!Bi5$( ȦM QR`ME3ŪhDW1VOM5z%DK*(Ҫ#dXkrPħSA8+i\ &cAp#ei#*}1rMk 22i"$IMs<qOسFyFDVy/[ z+JEd B)a xF폓>'1 B,.= BGo0l20?Ne>$'T "$aT{> +bJ[1Dl?ׇ ۶>"|/l#x`G;$ݻ"G!.Zm7qBUQUeV%e9Xse6 G"$eggc3BqMNK*x{Z8^*FyA?_ָiϏa(i:m4V5/.VkN(JUeqI,H4j4*TفU ) y.W"*ζ&fSDLƓEA 0  `$%Ybh)u:- ^w:k,ˮE1 HTXPH$$Q0O19tZ0DnB$AXYSecpJlcAŦi˦HJaz=@Ѱfheyy>0 q UU1 ~sŽ[oݾ[^Vaط/Fc?|;Aʆhz"-hkt: KKgyYi_IS;NȲLΜ>x"(r,8lw a‘荶:6 }$P(I@;㪊yg`X,wNؾuk3gEtuU%˰U,^jj'AQVV7 z}AA1 (0)Y[n)aXf9$I|79Q,GϾH4uxVD<75toX,.JRG/W|% A ƆGF3 ֆ$%T32 ͮ `XYh6 ȆAAd  {p*I^bWZ0 oɩxtQfb! gfJqX,r#IZ MӁ˕O>?|4$)JqPIqf_!A 4#Ig6阘---eJSsqq妫[jkE4:RݖZ{v6 9 B0(kkNg϶ɒ4<2iN&='=t` ˲e%cgNjj:pNgneY?m&ɱm[++EEw;wZ^ZFA,+-xA*ٗAEyMϧ+7kZY(/&JREEFY,-/-fAr9}jrjo9 ?d:sd8515 F"j鴁jmmPRKjud'S)EQlVk}յ`6;ۖ)Baom —.__H$zLUo1)8.J SVZtleAyqjh48geY(IXQQe;:n˲٣}X&1D f^Ҹk^(??zˍl[_Gs}8EQy' 9t0%EEa8^VZZR\z3DcBn0JY-(ȧi:r8`o~;~za7&)qDQŻe{,[=7yI ? @>&xLɋŲ$kVRERS &{fr|hJX e-՗շ D{./~޻G^۠i?B󤕰uVVx}ӣ`%*aMgYoBU`&&'?xK<Bfa*aYjj~< !dG.BBE!ZBEn߹ܿWJd8*<}_BU!!t_u]s]ƖW3̝;nql; w  MN&''i:8]dtlq dp(ąx7Niu}ʶ8+Zq箿ۿ],IDATB 3`f.@10{Zwڥ?0084228N({7NkindCCWt8{H&YvMwOoKsF_̀yk"T:͆Aso޺mfU"q>tzrSSSD B>4p8TW?ǢѨRޅF=i!,ı9084:۶##.L&h$RYYqP(+/Jp84<2soIMuc}},V>5RF"ebѶhh7v(UXbzzBZXڨJ$sUc1c u4ZXe=ݹ x=|.{@BƲ2y+c2B0;ϧSd2J۶-A']w;w}`{;ϴ/Þ}9ǩtqg9,زnPؽwƶ>Bίypy;aɧ\nAd D2Ft!(XK ϩ;EUPXR DHY(J&r90ڲ?򢌕ֽJX|XuБ7KwB(%Hc> R ΅J9v@J L|ǧ6D#Vظq B R$EQ+J?I!2 3THI' A#KRmjy9F6OTUy !)%1@ R ɹBr]`2!$!y sYʁK!B?w`)?s.TŢyRBf;O[۶,1Vx_Dy]Xy׵C/Og b*"Zε'@(DH Re\hp)HB!- [zdtTn!UQ'&&ԥe-ίY-oY}IshxdGf575۶(JonӿW8LRH$== Rx:njlDWt(DӴNB9}5W QE]4=bLpq|YKgukV_K,y% ɀШYޥ*%^ݩRJB*s!p! U]늢4TU"M>GF{n޼ʊ ݳO5s` p10vݟxEQ @Cx,VGP̀Q4;g`vX쬪8X>oQBkjjm2 *1FLTUG#UH$*cXcC%RW[w߁kV}NLNJ!VJ)ʣXlnttuUU]yo)w]랙ټ5JB "-],\,p0<϶)K$*\>Nkkk5U{OTc<R}kyW528nhDLtvVWT*B)BRՈBM4MWUL&c;BEԯ*|G-KN(zo8bVtzz_ 誕m##]ZHyz_ss3:6yMZ=M׺{HUؼi(xy4ZeEʕ+ XRSF榦ƆpMuX}]meEEeE*c})!%dyKoz*BϬvg˷l4:6k֥|ҥX[6~s 3,屶$ҶaUU-cQ T~EW] @5FidAQ/bRHw6 J(W5 PJJk$s(R{) ?4c% Eav EB=?T K )$0%ȢçTQ$!4I BrBHD:7tGhFUU{ @JӏrRsىd* C>zLO-v/BhqT{SJijY%sSR \x*L0iX.ڥWp_f[%di$@\cF!j/K! ` S3{]&JJe49"cLU4MuMikPBTQTJɘ1yni^Oׂ1"z͟&(e2'A 3bBxM Y L\ܜNfbqB% 'H P:m %t]>6B!R8ӃC ur`YzxH 랡D5˃j"m4UiilxszW.o(MTBrfP%b˃X($QֺuXI)On߳Ȯ}Ai'zߏ~{_Kw&&'O)  c:m*]d#33x@ov?8!{z|04u. ~Gη\ Y)OQUM#YOd] I*,RjZqYR$!RB)q1.̷CaDZoo8ѿu\ϜL&;.Yfu׭+Z )l>~եRS #ctzlf&_zϾO׮Y}WRF# /c?ѭ۷7[-l}wp칉;6+ h<}[e\!<f(HuJ4RTBq. P6ϞaZ/_ hֶP0XWW{ffr0{cfog[re--Lz_$ p0k=w&&++ u?yQJegϕ׏BORgg:=DK7lpea۶k!o%]q8׮uB!:Vo&o$ܶmm#B ^!/ɤmyd*P%+rV p8fEqU|./Jrp8t΄0H޲E1t=44MЅcοh B|q.<2E"eL&ʄa ~rB4B[okHTRSx,JM )|>PM&G B$!RBt:垼D3!$F" ?/m_D֬^CBg>`[-7>i%,BH</b!^@\r?;cN}Gq!zڥK˿0 B4͇z|~{( 'J{*>Eg<xN\0R]mwO:!г0M)z0B!;!"<&eIENDB`gammaray-2.11.3/docs/manual/images/gammaray-timertop.png000066400000000000000000001155141413310443000232130ustar00rootroot00000000000000PNG  IHDR-R pHYs+ IDATxwǞ,txpv+wj=Iqqq'lHuz+Q{||޻L @g0/̬UeUf"h ( B~BP(EJV( b(XP(UFp2%˲x)ONM?d Bg>Ͼ8]V|8=3cc`X}?OLLN-!eA.O!(~w~~BP(ֱ,˗\ɏޥiZeQF_yϿ8iQEW^ix Cwnۮ\ji C##Z|Z<͛6fge}SV :Π׏縜S8!bqQ+IEجX1 p'x!2 ,-y6CfKDNZ,FAQs<$Iff8EQHrï$AJi㯖.,,{-H$Az^R?8_oxd  E>d B,+TZn*..X3sP87Ȅ: `aȲ|ʵRE^++an= L;/,,ݹoMeYfye ť% aÇ췴7_pX- eǎ#ٹٹ\+ ٟ7ť EmVb46lrfg[,fhXLFcYYI4psfsqQ!òUHb <_H)0$IZMQZ(JR(JoGQAi MQ$8#ɒn@hO&S#ccݻvz@ v PXv[,fZe6 cQgvVYiVa0-3 03;g1 B4M޵$G8 BXa$IWV[N=v¢U* GV( BqTP(uF BP%+ B P( *PBP(`ņ\^]4r:nDQWn'!eðȷ{wQ ^[|zWʳ\vj)qM^P{Z'׌P( η BP(oJV( bEQy]#K$I̷LvȲ̲,ò,{ީss |}yI2E1Jxe{̲b́&xa!o$Ix<;c,6 8eYt: !8nyήn/8a:Aez[o|4y$e HB9D"SOMM?xaIR H˙Ǚ_(]edaXe|\޾?eXZmZ2y"x>q**Ja$1 kkxfgΞ7MJ%IA14Lg( 2,V=ul;"IAXmi1:>v `AǓd8q<$H4:eoX\\Hd(VQò$I$ɩ޾ݿ̹;ʛ|R\CSǟ~c3;KV<ϰ 8&[o޺qTYB]]Ζd9 8a˲h DZP8qg8c=[8%Ap)aIAt(*0 è(JKYi|[mh0$p`4h4;p0Bt|b24Z& {zf6QV{ʵ@ gKnۺedtl``+I46_jjAԨl"iSm:77YI!;z}t:h4  }5y:/..jqC-7nJL5ʋK[7o9}|ņgvVCcs8ikkr8K?Sa{f$IrA-MIH<}F! rcmd:R4ymCyهc?hiahYi͙\D"(N@YY ð]=`pjzfnna|bh476JLkky psyǝ>{3.084L䭶Y ,|~^^gwn8kښj^ʼn/yy ?3(*+6o۲1ZR.q p Ry& k|ERNg6Ec}{[U{]MSӋKxil|nr9KJG.))_0Mt(nYxDQ|YAG@Ue}/,8 hlnnbC;Y۶f.b‘H(e842FM5M=,H$l"ZT|b6Eϒ aww/Fi`p(DcqB3%I7m=x`V,Ç/\<11908 &*];7l(l>`nx9! (܅K㹹9(f&4a wCDnN΅KW.^R\ThCcSnn@EQՕYY[lr ܸi1Z2w%qbr2H 7oޤ**68-x## A8٤(YYQ銋 [DQlhhl&`` Nlx}Kn?0r9GGǬV+Isztnq74޼`Ʀ@(Dk42A?^Vؖ͛*+6/X-ѱ1݆a|%ݖrUUV/\G" 1133A6 6Iw("JUUYeᑊrA !9H z=`ǎmښjټs6Ѱ4]׮ח/.anjiE$IRdMMb޵cNM$M-G.--Խ|" z$IjZ,Y7omˤ˛Uy Wޯ6ձb2KpM--7dB!")۬vl7[^0MS+ueF-M8a.|~?`DCcӕu۶n]9004M(gZDIDD$iZ Itl"h4PPɔh{oIGfYd2!d͇ZiX\Nm6[9hb$!VK' $ф#(Cل? )^8ty>e~آ(b1N&S$IH$j㱘(J& L r"td2ɲdB$hi-˲$I4-Fb1Vh$5L,|  4aY%؄VH2dvۏ>7 HBKpDl~{ 8ny H$b4eYfXh0qIp9д6LIހX8(/NqUZQz^$8a6dYD" &IHcx#m ej5 P BYe 8ixhU* x>-VKZD"̙8KDD$1d2Ec1QIn\.lFQthxzÏ~{,c0H*^/r dM$4Ͳ bxgۇ4-$I)#p\!2A,@1Y:òZFQt 2,dYN$WİVFctd6rcbr7sG$c-~nVuu2qV; ð}W O:{Zb1 j'A~[#Z燁EE~UY@ BPur !%Pc?JeC"OcgoRq$(j|K_LBP(V*.V BXJV( b;Sj> M`rjzC:-nlYYw> _L&|O:Mͭ,8VdYnisgIo-DENMϘMyOϜ=?84VL A|SGRaˍ33sz^O>;Q[SH$@]D ==9.i۝yx oܼ511Y%'Q;:;zvCEqw>H$SKWUUlX`˲|V;8>84pؗ^eYnu:D]}Cyq_s\.[Zne4?pfz'9C#0`cp>zAi=}YYhwnzU$I=mf=X:Gs{f++uZ=$InTz[oJ%Z-]WPVZs6\^Z7{dYnX-⭛7]zݷ߼qmzz&ٵ¥+Ñ4EQXAtfwtorص4ryM>qуFynkujk1z˲_8UR\t}aq⥫ǎF0ۓL^:vʵpdllʙURR<!q:ܹ@0?MӇ?Z]t,-,.ޏD}> ł<EΕ}OY&f$IEIlqC]ݏ70a@>􋂂h4ۏ?q"S3yKKT}ؾ446v^^wjln}C9>-pO1.檻H3ss` A,/y>,C@2z>Hji:36$ISY`m̲ή[0,319Gk4y_Mdž@e**3zĵ KK~@a8Vju21<ɥd1L^8d[#(ðC#7mʜD"=vnj߶u,cS"-W];8q}mh,D"iB~U*jm/}a|brs_{EϷX(f9쯼t B980>3C9m6:3k# IDAT66!b"tm *GFW;wOVUeS3.43;Jjnɲy'&i&UpWWCaMp<g]"ȯd2]|rC  jjjz(J dKq(&ťh,f0bܜZy. &2MFavnel|7 y}?v/y RN20O%UhE 7b&䨤rZ|#I"6R&HcЀ:LЊߴ[ f_LK .YVR~gN4l~`!C(Yi9*AL5{AמBQ[zIMvR>`4d' =&@ U>Gh?FZO)tC9lAȦi! 5RZ:lM̈́|ܑNJHJB|iT”d2/#/ҥT^E$$$* 1qRňRZe{Z' 2S2@t8cIsƈӈqXLDFYLe[:!"JI1@-Mr!HN։j1 ( GiJJI1C!O-`&4/m#&Ӱ} 8$ 2G9g0im_ AY̦0)'%8PtLպˠ9LxAQMaVBNJ 8O߮gVJFi)ZC~]`=$Q/pJ4Rf|$Q'!qL Nޮ aR+e[-J#C V' U78Дbo,ph(xC'Q,1"!C<h3F'Vh%)[IyCR#"  H`B@St@r* @X.~0Z$ ogXत Z%"(_'v᫲Dt2YHXbMIUZf= Dn=Y/Jb/o:xOxo?}aܶ!MQ|ǮKcJ'-/ ȴf3Nt`mi W!k%fl`&n0̄,\u8cp.#D"%?jnD]eu!d#!{y4sr'!3)5ie[ *":Υ18@_AOE]&Aãoe񌄨PPJٔT&l e52ջ,hq#.\nh@ao9n矊G`9l {,0=.\n˵bW=P(<`8OMW?EQfKc?pqm1Y%/xQ( 8>dK_,Wy4IFk`KZ܅x,aصKW.gvWwO 4 Av0 ĩW*ohPQպͭ7<QZ\qs=ufCfW( Q(dلF),ibպs2NgvA~~*VUUEc1@,Y.ݻjؿؑW/,.rBgGhTU陑1AUuW[WKT8`0: #Ɲ*v‰ɩ΅BxZmO??119N &@$f A,$a A$A8[ZO:EQx1j٤j??qrcmMyY 6%(*CH{ z=('dO>pa3u=މa%IRT,s=(§uLY'ƍ5/t,?# 55}sssnwNwoss MPVQ} }f+q A%r{grW5kbY6;+kmWGk/\)7vxAki *xQTUM$V;LD2y`ޅ?ٹ.U*;7e+)*:q4AC/z `|?UynwMU%W-I$;($IZ,ix{`vn12ZQQzcG: UVVRP_S]e1kZz~a[ޙ1A)^!iTήs.uR,O#Q"R]|%38( qos=H>s?RU%Ijlkذ|rjk3ܶ"hnn |I+jzpp#[,g\_y'ToeUϫO̹k|;uvu74E1wnZ45:6~ ñ+Wpb1vP8|䩱';: IRK;ҒU@&Ze^ߵ$Id|wn~!\cLMM<ݼv`>A"\^TXxU"5ju2zљٹyNeO|Y\X(b~g_$AHB@0!d%]scE1݁aXnnxIH$'5088<2fXH^d)ΠgxǍna-vl?0-7 fxd$ }ן^Xl6x,K?{nܼEdA~i˹pbrb$ H& è($)IR8zz渆GF͛mʿJ0( ڵsǿ?AQLP K (4M#p8'uzuUC[R\<77?>1rf/,MyYG!.+-ٹchh4l"J&tmMKǎ W^_Vcnn~brҙԛNy,4,˧Μ+--TMp8X6 qMNNafZgO, 6uzf6 u/6Qh L ڿTTU}qUS S?z\'-?=::6==yY*l޴ڹ] cYhDjFN  P yg@UiUuz: p (b(iz=VU96 M۬PUa;;+kdtltl^iNX[3? 355sh40쾽{>_(*/+] ~@eY3MjZVUTl{vjPrh.).2yܛDQt:=yB$?SR\\PFN 9[m5 k9zS|,M-CãE5c#/&쬬KWI0k9 ]=y^^aX{G˙ٍ"3$<ŅťHaXsijzfiyYvuƾ5;1<0w|Տ.\ڳo`HJ{}``hhdȡ56핳]/55'-aX(40]==}zCҕkplllj=ϰ\ ek_xcz ||RCF:[+/ nZS3/]--)Yl֋&L7oq9p,dΖ^XE9rVbTZ[S==3sPhrjj9g2'>x HtfyQ`,--0՗_jmX]]󮯏 zxnuWy~aJ$U(*0bqeANgeOL4f\>08d˶IGeL5׫i֕L&أdGj4PkH$TJBLS81LٲYtJh9XNaX%pnj$Uy9`jRcHdMǎĥBX,eJXN:,޾paR6²KWrsL?=WY<3g2>tyɎ+z8tb||B&fdd$ɦ~_m(/쬳`9DǏ]__wݳ[|. F[JQ^q`ɩή|].ӇY͙ƦH$RRR}zAb: 639zF)7oS3tL*nܝaJGrr얬|MYäJK?gJvyHR26>>==L%߼m"Hn}dd vۭwvĽʊ,3u}0 S)Jrtl|a4qYAWV֘F #4[e޽] "Z:\q|dR=AS3u9V'JO8EʕlA<.r G>ؽjX,^AK˝]=aŅj!DT &#!D޽XYQ0$ITJt*RTnN{P Icǎ pIU7Y][i:jj5 uU*IkN7<2Z-g4B*))2 omsW=^o Z,FA&i4jko"̍b 0ta6HlHdiig2q2 ιʊrd*J({,[WWwO*E;z[Q\T895o C?!$HLFH$r LJEcG:A2lc#2㸡a\0LS$!ð7nD.]LΞyNCg?jp808xK)ptl|xdt]:nu !_˵(MFٜ9<2kExx_ZZ0\q\.7%%W^_^Yv8>xY.i5ArsJHԠ}zW{RղmԗH$%˼E.DH方]d ??nCı#/^Λ=Oj>xN; !L&?믾T>=K_5Uby/;z11x8v`[i3[k.Fz=B hk!JyM4 ɤ d2 BXra88 OKJ٥hZ;,Yf(x<HD# C.\h48Ip8a5?x`?:M3ᑦ斊2$GoM&uE8yD:Ani/.*DPX& @QT0 ÷P:-0A&N]HH|AZ;; ۤ` .]ܽr+Ç>RvEЇ_UYO?99vMNM_ykph8nW*bgK_h5ͅ6'M<5=PILFCWwonmhx8LɤҲD<16>ZteҸOiWܿeeCkkzݓ^ãc*5 ]=ǎH$Zp$/X{~~ARÑ?[,Y`h_M[sZ^}&W 32DJKkutYpeEyfi~US{|b { F'JKXgW[x{zgf܂KQ/./Ё R)HRGWwYz[,YY7o֮nit*EO; ڻg#;|J-aZ;QS]kqWST2F=* @x+z曯QJwo' C >=5F T}5٬~tl (/ۻ{BHme%ǏaY!DD}kkT,/WW a0:xƭ*)?44rͲlCcsYiIZ_bɩ@ vŕBB>(x8?Tw'VYY_q]eYG,m챱57nqvŴ-\^^}>$Y ׽6s-.='jP( [^::5MOΦeVEW|Ntnn~]Kˣchl6kuM&鴺lNCښjϞ3/<795c2xOo`j5Og2E~t!L/,KoH$U{ E~^.>SR*OqMB& AD:,KR$IdRD3T5|~ 9e )x\"!ѨTJp8fuFF(ˤ&$)hB2T!|\&Pm$HӁ`PVe NS<j}3|7z<̲l*EK$ 7$S))E}/7Nq\5v=7:T*x8O!DRm_-a."T[(5 AR)ZQy>9b0Bklw/Rz?7vna;vT LMM#>pኻCãkk_{so޻wtu |s=rWOo0I:]Y8~d2K; O9N-ÑWcoOCk[w-^?dl|Seݸ x 6{ο>pnqu <$P @駳9[;;`P2(:D|n^^L&S*Ds?_KAQԉG]qɓ56K?ُ~xnښ'n B?xoim?ҋuEYԟ{ Ǿt;wzKWq,[[[R64uήh4zX]fl|fG;3M]_olf ,NW,߂R4fңcX縑1 .+-}qEDh8 #Ƀu'&JKJZڟ; BH"moQ#1=.n{W|>BwۿL*YxU 099kWřHjhhbj0 ju믽f d2 I2L TJ%MA8ugttvRɅkq7_̋;N; 黍HښήՒq6;:JjRUDž8 H|ŵeBC{SW=Ygϼx-P80i|?Lvu<ԧD!ZVS|wylL,R(U윛s,KH6J"^}lKkNS#^2t3M9ub``(7'$ɍr!DbXz9 z}4_p- m8OZBgNrlu|bm7/‚d2 {z...Tzݎ+5t< I Zcť?kC##;5Vgl B?e6Sb|MݐGGDW~ݚ\K*#@RýYN719,,׻U\*-%jI\Z^>\HjVhlv͜P(2M&Nh Xx~_m7͎\9ӑt:F6 @ pnVxffVW[01NG);W|cGU76>m7ߑGQdeEk?H;yK+C|"$)djuFWw݆;]wT*}&~_{GNy.ڨe+88vT*]+BQPP*|L*ST2lkOot9!t&j-YYWTeR4хK$'&{z9v{eyyiI93s|b@‚ZZpÇBKK:Z~b5cSgd !F6G^^gW~+4JHt::# aGmkj4^y{~tv!$0ijۿohxd`hjɢiZPز{41hͱ'R'&Z׻ɱ;NIt`p]G^t <#Cux<ζ2i&Lfg[MF óXGFY-+- SUMNͬ:uczy *09v7ysE@piiԉ5{ssss˵|E-m:jtITaAyAӖ/qfTB 斺,YY[9U*epXՖ)rekksrזm&Tn=77' LP(SZRlg{ެ,c_QQA&t|!eH7TT!|~ P :ݞݻ~_{E&w<[٦(-+-u>sjWeL&˱ۢhcS={<GN̜8~]߸`dlðuu*+[%q`]Wwrz<ĥ+UVVzϜTT|_A!@P0 `@ @"D 8HF6cS76x/ AQbrop Y65cF~ HtS3hthxDP0 :taΞyA.S'_~ڑ1^]f2WU~&ðG׻\5ni!ұp8V Cq'568>tzoA~Ą˵h6gff^{Y۽&BgWJiښflQpiIɽ{ME~BBBH$rsr`XoQah,++5G"!:]EyKg^xrsr9uڍ[O|ZF\!7ٖRZmt,hTgdFw`0d"!$FVc6;p ð*T̲\Ac~tI+nD"|G݁bM^=rph$L|՗ώuw:yݤDa9v;BFevh;::JKKBzν::6f6sY'ҒxMV#$贚ݻvw1~`[Ԏjjw=;ۺvmh4ZTTH\.W32TY'!8y!FAi@;B[g?`A`X 0 `@ @"D `S<zl6wvvTmik7gf{6PQQV]U񎍍WVc&d&%d݁cZ:pȑɩ邂|\%B!d2WWUut!hmjGr9IKKv-CAN'VֲVߟn2iz׍&#ET*C CZ&   l}D 0 `@ @"Dhu/HpޱC\yehS;B [v3/9r2Fh4<ϯy(Rb4tbSTe,$Ifl#xhxd_mMEyTO__"GH<D@P]eEYZXPPYY>N3v6ݻ{o޺ս&fkZ!_T*ɶZ&Í[Wܫgzfax<JSh;d2L%rL*MR,J$d*y֝;h x,&!"NT*q\Ӫ!vG~'re\Hǎ`Vgl@H"d2BHP<;£i~S 8@"D 0 `@-A4NK)TJqx29N. 0 !1 !$)J<n% ML&? L&t:q<ϋ3o@T8 d4ANj BD·[xW.@"D 0 `@ lZR*(R1;Ճ+[Z pɱ55G#19sdtK'zĤ93s`pHP.,,T{ )njnj4Rt97o˶J$QqYJ:>ͅ6'=V^Zrڍ97眛kjnۿO\vl833? ]^r`݁?;㋗\u_MUM_`y4BP`plbR*괺zcx"jUJe:y~/B0!R@‚|Ǯʊ~D-kS5j$_}l{G׳Oq `!Vxj$-).t_DE D"ٽ¥+ιS'RIB&& I0[x;~2$@ @"D 0 `@6h8n}ǰL$nŕJ}R@eY `YvyepѠP(>4Mc8N]MHRb2Btz}GI)A8 HqNx;r9cy]gYF"L`0L&%B!q"2,# }0J&!I4gf3{@$Akh4{uUi5o4òXd2y<2,+˼7---'S) `2"(0>ϔiD2JKVi)յH$BF M|l `$sss ׻.A()E2#0 x~ZnɤR$% A 8cA|}L8N p,rN5 rZ|Mcx(0d2er<&rBHc4f0H [W(84 9ar\BJҽ<%P)JT7S$V'H$P4w@4_p2Z__ϱۿ̍},ˤLnn7ޗl}|M&J˗Fyð xo(0 `@ @"e/s:|8޸u }ns''66`T٪PV:q\Ѭ,sc})dx":qҒq\E=rXՊ3 `ٴ\]]pJ$q:]]977?15]VZRZR<08$HkGgBBR)gSSu ;;Ba0쓾 &BAo0LzKbkS3O{ʊ{`hXJQG \^Nj pPlF*@Jll;8H45|#氾* `rO dJ A%,@ @"D h4zsF?xa)~+>u}=oݹ>WHY؟}:jᑞ޾^|l6#(؊}葙YgeyYmMmwO?zy008]`窱8}dWOo0v[cS :?唭gwE'H`DQ]dS:%;7L&3{L&3yIlرj"v-gO{J^,H%Q"}?pݳ/s}DRZ7Ν;}ſ6Zz`7 1V Sk_WxA=`]_b ` b ` bpk8kE0Z_Y `&'&'<[]DeVWJYBHպp߽h޺zFӲrS)u⥮....Rťtt*eYoOa)D)5jy;kgFh8lm;KSS ۶,X,it/.JEؘL8dwn~R꺮8SJ%MZsƚFEA%l( ֖i-wuziBZ'ɍX `)eGP*j:%4ٹcrƢH$BGahL&~IG0=3qZ՚ضBH)҆aPJ Y5v|>U.nhtFitw{R;;0,%ޞrmJEBHa@.cөJeiF__b:,3VZ[cR18ťb#fӇP`0@1@ 0@ 1@ 0@ n6ƯH)oiS?_ٹ?zw6-k]1鎎'<?<:6n_8E /~+@,~IZI˝ԎӿJ}ǃ h(c8:Z4lbUރ33e/߷ۻL$vc.N=?( ØZVR_IRQ<3;=m&}zttqɣ~'Om[O9WBFOOB0cZk)%\)6 | ྾>y/˲ (8gΜޞrmvRcWAW*W&!ccc<믿y;}[JI)B];‘Ç~ ~j Z®Biv `'>pÇ9{={aFaYVc=w4کӟJgΞ=Rw۞|@?ٳ ##ÞMNMm=vn,G(X^^*ke!DرR:3==Ut' +QV+탃e_0M#a' • 탞OMMwwu-.-Y9m[{rpDp> ` b ` b ΃BA@R/_Z z,4 R)|/ Je?xh;CվrȷRh4*w8۵kT*^2&{'Jܼy~XOOnY+%| ‘1[TB9|X]΃,//GؐWpڶm{O/rebyeRY^~w=ܱ}oT,nkjjVWW !qB(cR.wؾmpw p 1NF)IңcrR*m:n6z8*IH&ժڶstZTR)'}Z- -%l4 ۶]9Z-5FlVHR*I$R[B9B0@ P f` bXjx[-0B7_pZ˱Pɴ#{K0 0@ 1@`«Օvߌsb;la&G/;} XYZV*mW˽v"(ժ˾;+LQ pSsރO]4ϜܹqN()WW*߽O_D:tmCܼoN3{)}Jeҹje1ZRD宑9921Q7~9|(JFQt LNMj|>7mK<߻gw2bfG:K]#3s/Z!'0Kgg/R)6 cl|蘐¶w=DQtѱϙLwZʫos86rc1/hob׿?-ϋrwq7ͣ=ޱB\}FgdB25=}臏 !W:;KCCd2J=~o޳O?0|}yB(c0ֵz=N۶mcR>R<0rd2'|9䡇qC;wA)5 򼋗>Za pX١0mH1@aJZ뛿RzZXkRJSJp՚PCZ !R/R 16,z plDk)] PJi \'W"k(Zhtq8`J6"ײZVk[ eR*ܦR+-B&,S)uCW`ۘ!hM4фJ&ZkRҚ"$PMTJ )ÀklJi!!B*H^OQRERA )ՒiM&G3U!əR2IrFTT)+R 0њT;;tR{j6מhvvڏJfy3S7ܖB)BDl2 RRV\p^Z\\JV*˶mTeYfsn~PmRZ;KFC!,VhrRD֤ pR&J<^Vef25!\.eJ0J{Rk Ad+JNgfgBa:ԔRJcJ!*K333Ts6779|oyyyzf&8n඾t:e%(J5kFuEThJ5M"D [κf2J%Ne|6ZҒia&D"I)`q)e\>JBoj&$NR8vR11fPʈ&"Juy:0"!BEB04&`Yi2r\!'$.BH HL&CVJ~laeҕ*:U gg\.U.JL`qÜVZO =/p]ö PJ+ERkCD+Ӈpܸ z]oK(aZ]֔;h3t& 9[B(BlZ)!^FCI9ZcMӔZ1J)mzքh.:ynA4 DtCfFwO:nWkJ(cHi-ҔQF(d3J5R+&JRD_s/c+ .i>XCJTR*E8'jF֒445aVtqWi2&iM02QJI)92BR fQԚpd͒ 1X>_ibٶRi4`zߟG@9w,۵R: g\_=uC+M9)2M,s-%Oo_xn˟[Ìr怵֝łiZA JQ.5(HH!Bo@t*eۜ1.l=x/=hx?w d !u׫4!Ig]srIFz ˯s(hh"a'Ct{R(e38笍 [ںNSr{m|>_.G_yL:L;VW,?Cv;=gN~4qzvj3(k*z-Jj [>_h4eYхٹ˗~#]ҫ%f aJR0 dom$/DZ!)-Ӣz_շoh! DoL;8`m!h @NuIENDB`gammaray-2.11.3/docs/manual/images/gammaray-timezones.png000066400000000000000000007112451413310443000233700ustar00rootroot00000000000000PNG  IHDR  TR pHYs+ IDATxutYy#"##9SI3"23373oߞ7g߾wvg33 P]]`23,ffeJJVr.Jrlwԅ~F@@@@@@@@@j_B!ͱ+F@@@`L (jqF#Aǯ ,۝+L@@@|>e0GF E_Do.[R"'}] C*W-N(bV| eZgr'ěfdtǯoB@o=) wD=c5&Za AC@F7%A!T O+TJ(xsFX*Y;:jմ|wl`x0}iϽdW EQ?)G&{xsK+Edi4W̲Y0^U8Ap,pVf`xntƛL0 0TR\::v;òDGG`04JN'77'VmnneY!FfϚI3ȈL.W)z>"BD˲}VL-`4M?tڵkV}f*}>B!a8@"|;fDEF2 ޑS[8a$5%9&&tu$3&MFFz0#s捏I388t{0<ѣv0ðexxhq\fFD5, AA3gD"^$H?09Yi)'OXhw} `Oo_~^n^O?ԋ(UU-mI d",q>=z?HpRiAeuOOM3$I646ݯmimUV񠪷qEM@@R **jE~s8,˺nFQj\.a(r8V-wUԅBBfxDžBa>{leYRTD.^VURTRFFFcڂ|4t'EQ|^Νޞ]8O>JQE"H$ AZU[_rj/ʪhnhl7ŕϜ=wlo%e]. 8|dt:ѹQ$cc6 pw\iasFiIŃJ|>RSrs˖>{ӯZZZ;:e-m1QQfepm}T"u ?i#4[7ozj=OGw$ӧflP08:j Buvv3 .:*p8R)aF@h ysV,_ oo`X&++su%%E}}2 o]P45%y Agܯx@3i6̰XS5IQe3J7o@Ԧ I$ QWrXf(Cz0aܜY3rrJKr@Zjkh`Y.::ty<޸ؘ5VFGE(R>,%%kͫW{W-@qIYrN>;qL.ܼqF1 xSoooXf˦#VLeƙeetQQ ǙLq֮B(IR۶n7wkg655yEmڰ...t]z=:**:: qӔdAܜTDh^n.&;1)11:&6&::.6fRQa ~n888Ȕ0L&er'ryFzZYiqnN6/ZjT"y9b=wRbbBttԃ길Oݺ2-]>ukVmڰ Q֯]aښzA[vu*.66&:[E =ڈ^jMqCI &Ӛ+h~֯] I׮޲iCo3y=˲ C,C,˲G4 RUYd"BŨ6HH%Ѹaڇ qyE+Z9(T!u'Zy6b1j4tw,P8,J' hʪFJ-H7Aİ / C Xfw^Tx=^ep[v$DEEQ7Zs>|ήn AaW(&HP"e 874<D|JB! T+!" `8aDR6mX'Hr!qnW&w.ߐ-79X"h4[6!w)0 i4rg{%%^{dbBο`pg4GlŃaxM)IwH%҈m[6A7uY}PU=}4L,6qL3 DzXbYeaL&!$>޴lHc0ljn8qk 8ۖa!D"!=RhceّQ+r:.6w:88aXB)==lh4jJ1^ZmDJJ`Fa8%%%A@@@ BE HH[G{ccs(JjB||wB0@ H;lP[W/"dƦfLoh4@p}U@0iIyYdљ#"4 \ta^rǫjj >@쎉i4-KiF/ueԔC]2..w5Bg{8ܲA$ +pc'>s\^ZR1&pwA AF^{rssBzN2WxtT:G&DH0l |  K,rҨ5 Ȃys%%) C$I/ M3bL,H`f7STN}kM0bFTNh@o8N rbX~hJe2~ )G*A^B>5A 9dR~/ 0~#O˕ y9e3JkEy X-18cc glRD~3 7ch`L4h< pO%*2EH" rN |ob.-_ws0vnJ؍65=ð̿uNkk;99٫V,c9wޒJ$W|Ͼu{PY}-͙U>ԙs]=4Mo߲)550|*F޵;pX":1 {j2 s¥QYdQ^nKW c}alۺyppБcr,==mS򢩨lnn9YV.W񠶮EEvl!xC(RvlC;z{NٽNfHkljfVT%&$<}>55YGjaȿ˿WuNf5zwy9A s1 {^4hiaY3< @>0!s"a ZUR\~\ X:= OJnwOo_bBs.\qžxIVabqVfF] j9K/pJNN'ݽ+?/ sg2 e3JM7nޞ]>hxdg?l110><:`\Μ`2>zHxJiI'~D wW$&$ܽ_ Bq\Kk[vfƦ k\,f:AҒb>\^[W7wάt6))6ozf|\snTVVz3^A_R\** 8xdY9Q}}?08sF޻v(%z-]?ཙ3JbkvmߺhxSJ,.nX6[tW-CtSs i4= E$A3p˛"b3%9޾tf70X$tvu-Ooٔ_,@QT&y^tbŻ@ TjU (rAjj r1A ^sR$7'K0 Nt(2 0 U(ݩP(rs]nh@$""blltFGED"DϢ(\?oj X+BD.~#HdYY}04Б7g9g_ BxKO+mccW-Σ1[SS% üOO>7}]CE!Xo ܾso75i)jRs6C|~^ v} ۷ndPeY0CE$_0r,  KLqD$B0A(J( BR1Q /qL`(qESϾ(2_tYn߁!hFqѪrzr9o1T i ړT*yk͛nݹAPzj׮^YU];I weI䤍w?rY8kRp8wP8U ,7^v=''lU7 ;wTU_~s%#-}6   JJ߸UYU#HjuqQܫHLL=~5#=uxx(.6jwtugffLeWssw復`}֝7oN6ܾs/)1app~pȜ< i9V(pFIIICƦĄں:'"# ΞpAѨG^u-a`TbA՝{OnNvCSsuMN2[jjR_8&';kxxڍ[M-a@wOok[f{ !vTB9{m`ke9i[1٧R߃qo<:w`j{&r\:`0Ddh2}D*dee 8`)N.2ٌҒA/ɂEa* IDAT|F!-5@ dZQ1h &uvn"S܃?Z_f>s\U5&SvV& u &^pEE745IbVk}}cJJrzZ*Jñn2AM-#2Ҙ;@uM@UumzZ*I{gΝq<55?{UY]n0迷vO;9x rzChjeaX208ȫ~r74d&I I5-MfA` FGF"v r6OY3.]q:>Ȉ\.Dv#!> IRVu\{H}iMzjBАv>08dް~+ OɾtjZZ;wuZFYje`o_sK[kk̲== MLLػO~X,~qb$ DؘhNw9sfuvuϟ;GR9{>-597''ց\?PWW?l3g/zxh2̍M8'J^A{sԙٳfvttmpL,{Y8Ӟ}~y̝3¥+[l;ֶvlwPVFAF!?|ψDQR\PPRSRS`\l ?*"bR?xL&exxb)/ݓʲ鬮Dyy--m[7oxw3⺺TJ$BnYf?(KLL0IDD!QA.^b٤Rz ߳=߹{` XU]HW*|ʪP( n޺3iP\l̊K^ϗ`0(Hƍ!AbҬd|3IZjj*`Xt 3g,Z8VƦ̌ >Gg_ZdqJRQwvu,[|钲}M%E+/ R)%EQ+/-**vz:.dؘes/]RZR\ۉMohlV*|ؘhA:"LIV.QZl2Ů^frLRݼ})T((ڱmF_>9|SLFfvtv544nݼpܽw??7g\tIRRvbݚ+/KIIh;mYrykk[Fzyx|Lm&%K5ԼkǶKj?,o@c`Y)4(F,YTUSCexr;x]FQ4+k?j: >H$ !1&FQ4u:|zD$ @/ JNJl-m/t!q,ˮXt-550 q0̍[^[0q'cp8wN9 55o8򣨘o;'UxMm}W{23ׯ[Ν,~SSGF[ڦMZ-/X0$yQRa\X A0*B! R>`/N>fwh#"(;g{揅?&ilAQFNNJ|Z^n`! XpaD&괺KW_fpl;Lqk׬U^3։50P(LR$CAOΣ6@eUa]>sZA>Iu 76&FVOϫnljJKM-.*Xօ RU]zν?4Oq=Ղaؼ9:aXJRRiIquu͟ [li.^w8'/H$XLtW6 Bj^A\tjr6-_&䲿ӌaax܎yxQ{藽}vR:jǔ?l㉎濤iO+EzVU֪ZK@ 7nݞTRLMN}sfERM~](ZW00ny*gݫxpyٌx鹫?LSTIIQdƭ}}$KoSI^ (==z~j 9g\{ L}#Ro0 sǰ, AGՙ|YeY@+Cx}[Md)B^`p;񶀇> rbJ Zvv>5$,"wA}YǧFUAUeYA W-׮ớO<|N9g4Wx ÈD"eYK28. ePM|u2, KҾpA2oqNƻl68{_Iŧ0,'$So(S&?_Ťǿ<e˖h$/֓ A Ó&]HRZ%!Y&QWNWġV_qU8#~R9&I vb(mOJZexdŲ%|,ǵݹ{?++c L,O /ޤ%2IE tP(nͪ}{<^Anܼs>vWV1 sMłxSsKUMex@4vlC+muu o|z.FygNYQY 8ȱdϾ,vuu߾s/r8yfu]=  bss~0 l½,1Q?zJ9<GF?ge3vؖXаfՊ^ٌ҆Ʀ{{y2,;+];GF{_a`` (e8jUOo_^aFGEyalpG0ßBT"H$BBPdfpFFzDkX>˰Iϧ4Mwtv*o2g֭]#0aG1hss:;&?22:8d}3JJNW8|9Y9}Ϩ.HaXOo)6VV&'%& Pa|0 '&()n*@rR"r=11Py6{RbbF??00uDؘh;>l{lf'GDh22zrFp|Oϕ̌ԔdJU+ogGI &aðEE!M 9zP0|R<)/A;x_oz٠(ܿxPE4ndZ~ރ!KAAА?0=|ںze޾tL* B<ފU0GdYiXLR$ð V,[܂D$)/aysgqeaf"BQIQϦ`q͛u/ =wqɢ CD"TDQ4M00,įS$[zss?۷ljik?u\Zj J$BIQ(*BP$כ޲yCgWgrsQ(E*FWe  \ohhg޹{/_ݶycBB%`FX@@AF~}oxd4!!GR׶ڬ?Ϭ̨H#yaEǭ+aWT޵C*Ĵ$i233&/j[;>z )1A,'&$Z O:0h08]P8gST_>ji#41]RD+rLf;O7?5:79 lnQѨir;M3K/LL0>s~rBA0 DEE754dHh4}9Y.{M/Y 1!ĩ3W*0񊊌4VTV$5082l<өV_)IϞ/++U*êip8i4l߶=}ް?(.gCHO;tXm}Cq F`mDa D(Bt$AVt:F"4=^bc>gftvuw%ǥ$DCPD AukW<˧(n_T*|µW:z%0tGCagXO__IQ!o]t+n_rْG} A`ݚU0 ϟ;/"0o={ϋk7n#lT:|>];ؘF>??_o?JOOyՂln޼Ӌ Ȳ1Lri(-x:4>߁^$IlvvMyŲG9s:]ޣ10 sYrs4ocY/:ҕ7n|Z]џzꚺgaYvçN;wRsK MӧΜJFc pܽ|:T%0oڻKWϞ_[ֻ1ѝ.ׇ?ecSKwOW76yٞW_r=6v=N?4E.ҕǎ_qS$,+} Lr=N=jݸű;ozp$%&?yzxdT!/Y'NcbIF/~H#,kյu񦸕+op:Ο;sF;w+j:Mџ"q0Ů_ bYW $IgwbBzð~x#B$}O^e/\p:ٽ =M/1 #yVVWNqoݹ[RT> 55o`yG b?8x;nv{?ɤo?1:*fOVjŲc_I,4}ƭߒQU&ãQQvls֬Jx];ǿtd2޾eNinJL:ƴ6h9f] m1'8[ڳ33˟0EQOްnĄƦ@ A:;: -m,C[6ޱlٴc1Ϻի{{WWWJNJ|Z0l'Nl>2 ðUsy.]ap BN l˙6dfw74c[GgWcskzZ2<2jݵc+*FFGڈ۷fgfN`K9w~.z˲^oӆuy`h捻wm{b3/ۿ+ ,ÆBP(} =~36'%&]iú@0Qnh0nwnn3 00LvS~>YRT[;ɦksZZۼ^).?e2+K/zR0|ӕ6SQ@ioܼp8%# IDAT6oX' (iM&DRoc_dggY,ãV㍉ӧT7oݝeӆ^^5+gwuM-A ?|ĉ߿9Y*>ysfya 0 ükټqҢֶjDkё!)" b905u& 7n^`ho+p" P덎D㸾%He0B!J|^"4FͰ IROb].7@V`RRݯ0{I@4/ؽkRH(2a\.2B47 BR*I%BgN.BV(4JeRߏ޾~/}{Fqe3`A%Q9G˲d˲mvOO}Yk=v۲,Y9S b DΩP>reY}"QUU@:gݿ=rs9 _>솅ʼn)pENg~^XtBk͞JAhm1H$lX_|+$+8A?LH$Mxզ@8NLTiXNV֯ϟ:Ig}xnIeJkjiP$LLMA&6 |>/aRMJlkSURR"|ovn. eK+YMͭz¦ aJz/ dJnw66N|. O)76 ۽pq mR"A`^ZZq\|;sD"T*p\} II"}j#\Xw")QvWZ2$RK[Ǯۮ,}/L&?}ɋ\A#S'OkeputR!w8{337%ɩiD"D"9~?)Q}6$huN3# @xAI@ IIOKQ)Uqq_%i4PPTdgfջhȤD"J|L& t:]*+J>v{h4P({g8TZEQ`F0z |ώ e'[a>b8h a߄6|>?F"n' DpHdG덚ij87aǃF"߽Z Q Q4xBP}> #ѥ 䱒7ay^=`0xp^oxԆ?l;Z|78C7 r@0 )ʣ@Qt𳪝"0>^; C\EeuRf׎EG_o7|x6+3=/7QFowvu`ᑆ&*{vBݰZB;ǯ,ۻ{WJyeu8v\>s_td'8?8X]SG!22+jp8^y輪`m[rGF>p)==cG--/9{Fg6--~tDuf4MI־#x^}EP]\hYEbZ[l,-\ZZ {Z_b0T*u\d0JETtiikp={ZZG(ʱGD$¡Nz}g?@$gGk>`?LB#;z,k7VWHdRFߏLOS(e啷z<{~(4uD*?$aMD~#?1gR2o' *j s<ʊ_avϮ商E219x >4zۣQ't5)-JN<z8]=?{UVZ^KRz^ް0dmҾ=ss]#&R}cBh4gG!s6"119d2֭Y#$ҭ5Eׯo~g4+{qߞ]}AQXnMTeum0jji۶yS\NSԟk7nOvV? mӳsq\Ωg9ⵏڢ/axdfBE="K/֨JRICG΃@P0 &jEIss#+x, |+z¢u|=A Xv#^*k$ xz^"GM}>?ѲQyXujiy9:y4^R"̠3.f_mñ>bfs4BH%[N@rΝhX^ٹEjnm;ojHKCF=!qwIۇC8:Ov\ssÁE0> 0g;.WNV t,>vǃa8v8~LX4 d -LM%y9ُںe*8F1H$Ԣ78| P{}{vOLFw`ϴvVV|!B$>S ?:'BNx8N&$ B~/d2@ W@0w HLJ!SpP(t- ECHH$o.#pA~m[RSu BLQEQyΝ0OxѦV5۷ݽNGUc>?Ba2^ Dƪ7n&r'/~:?1b54߳kff63#]RVԔjuBSKkM]=Lnk⩓ݷ{)dH(hmUTb2u:%כ6vMam]Zh6{mQT*UI0M&2")U'33.#&BZjJYEB `} r=jbf3 :2<2}p8MPFBQ'tvtu޹#>^BX$ڴqõebP,=V!]=a飹DT*unn>?/W.546wtON>`0XS[JGmFRj蜝?ԑGmΗs\,үAswᣵ&F1VbEH$Dr׭. R(dՎa撍2FYTX`Z>J L&D"@NPP8mfP(F\@ ,--B͛6hJxMQk** 9l6AT ErvX"fq *-H DF`ٓY}&jܮ0b++y99g0s-<W/"۶lQ$l|dz2h EA~NW(++f%B9AI%+f˺5J<}u8@窔Jh4AP#GE#FBE#F{"H$2?z8l`0t_?`^oxV|&? lvn 8&R48.WlzPDe*0 B| gyyK7{,P#F?JdV*ʊFmU8>?p8vL&Y`ֶh4)ͽc4::5o[6jl>p ؾolnvtzfVSM謺J2 _VQR*&gJbn\=Z8>:6~[,NoVx8WVsU/eqŲ8OO\|xl~^U2waػ@b1f/Y,@8wReuMCcsWm"Ƞ!PN+RS#0qOZ<:Auz./j}wO76-T:^VtjJ3<2ޑhku^RFpc6cĈsK_p  ==#Z`XXDiC#8WSSB{vT) =&xy2෿c~norDlZjrL7 "lC:2h4 x5wts8WTk.AQ߯V)P8nXX _#w:z4؄R!OID"Y[TfV̖޾> ~Zz'u:D SڤDmR$+f撒v{צa34Yd{nuk[ciy 4{bw|dR ÷{V̖k1 YV>WX=dm6)1Z}CH$RU]p:{n0`dt&%%b644=GuzаL*Ɇa3g?\1=-tMK}׎۷nYXX,(c0cN )̬b2Pvbݸ~]EMeRQAj fY~ʢј5X,V^n6Ammˆ0F1~4|ɀcpx$?7L&-/0 XRF7!(+_1[.~rrE3g?f &#+3b,p8)I2`(5%9AڵE"B"=^GγY:s%<~ffVWU,+Y۷h4t}U0 QVx{ :|p,ϳ.]NP)'zz @`vnnD"!!Q*?'0obv6f7$''@V5E3su )ƦٚISu*΍;a:wn&i4ZT}J&L56 h]b[^1 .@TB!Rk`(KJ|bj'W%ktzCum]jvN8=04MJ|ztA qhiLa$s!#-dCSNgP;CL&ohlnmH&M&MM|zn͗WZ,VyE.7¥8|BU5RE].8.~cdmEBRH%ͦPȗ^^ӨznLLN66hktz= IP)u8f]tH B*wplD%jW5CRGG)d2FP(9YD"FجKZ,gJ[VQoU&kWW9l6L|Ϩ*{DfaUbnnmnhjpͮ[^1GRq^#F w`8 A98H$n黺zfPE¦7LP)aT cc{v 05195<<:7LPx!G0*^&zAJ6;INHOK ]=fEQݱ}BpA^nL6>1b6GV+E_E1S|zzp(tg0yw|uuD" H۷mP(cK+i5u c`pn^m[6+[7hSS(C'&>_ TꅳX-mðd-1[,]=afvfph$ IC##c?yy284 An0b[Q'5hڤᑩ::6t|>=rDeF/---잞MKM!Sk 5jt ^&-[̖hA \F۶ƦdmRFz;! F۝QV^o{<`(=s8؛w>N0s EM :mlb&TmX_|\,v#=uk Yx^nH$5FF , F^NA;moz4Mr#cvQgWoH$b:AM npvnԼmbX$LMMvS_xFzZVFF}CS^n.>R6F1`"AVW_e4-C Ha8 HB!ydx}RD"  d١Oo6]7x\O_!a6@@ :nuL&9TL< BRB2L̟*A"Wy`ܖKPz}5MO IDAT|^iip0t 8`0D$J6M7H$>nM3Ǐ(xd 7ΰR) T"ٺusrR0 gge5H$ {×_|YHܼVdw8tD,.^6--%?Gd##X$+3gJ"Qrجٹ9E&J)  Ŷ-BPtp B<܅;o BPóݽS.EW3o!zA-{W#{rqT  !0AB&%:H(EJ~Ё3ǎj4K*f Fz:W `Ȱq zڵǎ>FQUlDE"}˷drFܳOK@ `;D"45nш8z㧯Dgvъũ)J_p ?pv{D80lEQUTű:pf ZdXP(hC$ a@}#F?ww&k>B& ++]SFLzfFq0Dgf[4Z:|FH/k2rx<^bٓ4ѱqN$23ZZ.|>X|g@.A>D$[ðP ki ƼlA"Dڰ~A+ }u MzB6ifvnaaD$5=v QUS~+ <pۖ\n5--UTLNM+VȠE"Q[{GWOʬkniX))}bj+HLFSs DfLOKJJ5467)h,V* ñ}$ 2 Tfu :-^&+**k4(k vlOLOfe7SNNq"дad747 ̼"Bhjic1RE1u2ɍKMI^ 1/7hn;33= zDܚ8JȤ{w)Ŕ˲2 QKNq⮗wtv8R*zpHDH$0lRxXYU;==T*8].DqyյmmD"//--dg"52a}q0,-Il6+_Y684TT **2SG HLKMZ~l).^XHQ%bqBVg7AD"ATD"DFZ2č~&F"3sEp8|>F :>?7g^gPUYY5uSYwKtE,=F!?,?& CHKMٳkC8>woz<=95uDlii2mߺE84xLNM76~`Tr+/źVTq}15޹}UنH$xvWVt_zb4lڸDFFwnҊJۓq)yܽ{v..(J$gyv~#R{B~E)"~[wxȆE!HyBZjJ^c@O-x^p:w3G?ԓ֮𣏣;,.[#v#44676xٹѱFENxN={Mu99Y.\qÏ>޲ic\\ͲhSӵ ѓ89{~MQR!|0]ruz}O_ynn^WYUcO~tqGJ6l7J"9| 446߷ [?{Uڵs'>s.^ZvL&z& :ܿ77y0|7xcEdm/zft_}y``hhxVgע+/^vc?V 9tps3g?0ΨT6{aaW^=ox׳w۾uORjJǞomڸo`zvoq`kl_s?G?b2MyȆEQ$]7mT"!1r'ƷB(C0&*Fiy}cst]2 ->? 0d͵wtwܺYV/B0 PTJ,i44.L9YY Jl|@@NHqpߩgj6%9iaqvMuѭ`yټLeR4;;I$ק|>OԨSuNFE5E%&94<*XL&!^& (.;Y7,n" H FFFJp{< *盝k?l,hj84.Vۣ6aT(>аJS]ݷ?͖e9Ah)Isz!--NEBJMLf+r*z6B&>X1bĈq];0DPB(DDB!lD&\.WĤfCȦR~ziY54*`D"AH$A#!pðE;^]0, B Dr(P((*Ѩ2 (AQH$F0ٵ# @(a` }@eѰK[ZVsǶH8YhG$b0fpxzfh41tC"N?{ D"@ 0D§N`75c#33F:zDǎ]SxF)F:(~7& hS%YmIS'OuiJl#F w;wϮ̌t?0=3s޾CD:ED"zCk5dqݧϜMNJڴqFESFf2ya853+I1 /ӻX^Yt5tz}>Ҳ\?;7 Jx`D&L&J]X\J$ܸ@ `ۇF-rtt\(V;}Pq}A^.Ag2:an^' 96-VȘF]z|B%522hLDHB(KR1=3 ,V+/:" l\oϮѱq͠q:J,-MPhv~^&*`0bp8TAѨ{vG$m[اV'~>w!ylvnaZ%rbĈqY._^4׮ յS3;o/$#=u|br y^~sMQAFږd2nU...ػ'3[^Yp:>q Dٕ=7ˆGFWV:o]aoU{w\A*6RT(!PUSS^Q=?۵sD,YY19|P[,ҊJ6usX$oHOKy|5uueU];I$b+kLOx6`lۺY,Mէ$k r86=$󷴵wuC@¥O:z Ʀ)J$_|dƈ#Ɨ,1bx`cĈ#F1~d|t=1 B>> C#\.ϧ7a{"#Ȣ5/c<rq_`Xjv#ÍM-߶S ìVk0 [Z^j[m$B2fD/ʊγFaavsN׃5Eɩi]#F1b|)_20~/*bXZ"HhWD] APfF]j>svrj_::;;:;רy>?ɴt>Owb?v_EE_6Wb2:nu}^Z>lGgH0 uM-(^Qz/8/ax}cssKYƈ#Fw"αY,t:,YYY 7_OM7_iZJrsk{'Cp7|=S{aáOרս}?}奭K*kM# ;焜NL:zpffFA ?0a_~ṧ=:7pވfblv'1ѱg>>99?05z 2?0dڸܸ>esɮJ+l.ٺ ñ@ p葜L4>s nWۯ~SY]X(Q(_}||],uzm|= ϗWT:w`X#|Nrr~@ p8d2ȡUh*H?uB>t`ozZըտx 7*kj3 ;̌O{t pq9|hU(F1bĸw~5G#A&ֶKV7-qD*~|E".[( ܼnP(=-ήv&dR(D"1Lt:myydx,jOB,v#BRy<@Zm0`(-S)Ze||>Je1Y`qñes w{ڍ۶ދiᅱJ%Zm"*z׌Fbiim#IxYJ?0xL D"vctt@ d8.7bXLE; 8qAd0aXNNA577oZSI"'WP4<^0Bp APnNVNv3ο :TVaZVFaѨ0x|>Lpv k&D--/#0lXO<U#F1-w?{z ^y?)6ΰ02:<A].OLFRZo4 & DMΞRQUR)9_;t:]$DPڢԔdD"Hr9</ ΅!&qK+K˫6m J$P/\b:~iiI,nD$J$"@w8M Mͷ{gfvH(}u]x\@mrRl4jB!H$x͞q8]Bjxٹٹ-KDL*U*oKO4 \Y][_xJ|lo΂ VdXrM%DQ*TJ,kLf@\wnu^OԨY,^ZZJ$\n 5EWnlln >ںroUO|?dO!s3K#F1bw`M&:AbڤYKdR6)1iuzƋ $ibr* 2 PabT#NOr8HynG&F 8'!AX^YYX0ɤDJ~ <N 4q<~a5nwt4*5)) ݮah~_$innDa&t:a2tiy%A FӒJ0LKK<J"MJ\)FuL=єMqb Q}>` H6 !&Jff|>H(zBF >#75j<7``$kuz&"fP 8//,,. efh 8|~C EQVRHp@bYђ>N@@lv0ǽ^et: q lw]5=ʨ\@0f(2606M"K@ xpsl"D\.W(qyn?dT"qy^N9_I8v\XѨ~J#Ivq@ Gm7p:X1>< ᨬEai驣G$b'WUV_Vohy.{MaEs7GQZn6NWvvfڵC8^xÏαج` x  ?St_8uFUT}|ҙߡ|oji!ܜ%/|rdZO:XgαX̀?pv¥+qx; (ZQYew8WVd]/-bh+fp zٹk7J,D"~Ё{_Uiiku y'& 0^Kqc'O<}?l6e>N< ^vtQ)BYLVUSљ'9q+Mm*b|U*L"h23'L@ Hs?YD"|3ǟ\Q v IDAT͛6vxVk{ IJE~n@okHP*_p8<5=a>ipLrǍKOK&/ZZZvh…%!rB.ɕ6)oX,jlnIP[|>}ffv6C#YnhjV)1 CPB*;3 zL fgJ>WSWrVTrg,Xn啈E9Y,&vo^N( L2=3{uz\::<ޝ {VW^<-::ĆRgft\Q[c:Hgf.]J5jV' |>_0I%q>EѪڗ^xN(2,,۳+3Lͭm9].&A |<!p`f1n' 5l(.nln^pFpB$jBڹq}q4?&<E`NyĉjjiADNزdCCw`x^@0>`0;S()zb.//+/>˷@l+F?8H \.;k/]>==nMLN&(-DnzfFVCUʙ9TCQ)K645/7Ш$@7DSa e|b#w[]^ HKM!I*ziiyMQfHKMԢ7,lXN(76[mv :dNMJLhnuO۳[T5=p[6|rHwqf+mVjɖ@( $@Mrs{ޞ7R&X,W}M۴E1 6Ƅ%Μ9#͙gB{OwttvUJeX4iҒ? ]#=EZ2ylwnNNvV_/DBD 18սN`E,@.Eђu<A0+TJ>SRJK#,(H( \ N$&&xܜd.#zU y,N|OP$&STHwݥ%EqqA0,J%0 tGD,(/Rp8/#S,&ST(2B.|^8޸nϿ$ R Zr jH(kB.)t}]~,b!#(Ѩ@ 1A]d2W@ E@`}Xt݁bũTTܛd02҂ 8)1AQlvdK4us9X*(QܣPEe%J(r-`(:=3aBtz fV[(_ ^|-q|n~!͞APp8<7)S.q%J(_;n`qJSgwA@@= V"(zC AM'_$r0w޿vSS!/_J3nȝp&CgP(?yiɉaK+Y__G qA#/]=S0ȱn:cُ;{ n~wY( ??lKk_̹ }>߹ }n VK:t'oq{KW~߼{ x%J(_Onv{Z%bI^Nvm}Ck[}h}1yC8M漜pk|߆N&^kxet:}`?[TQp8o4VWVȤHSci)YF 0 s45S(5"ʷI ݞ196>o߷d _W553388r;|>_qQawOwdu`VQ^JѯAHrC]B"L&siqQ힜.-.xcJbbtUk*VhrbB~^i033ol%EJr5_keXӳs`H&'%Hp8|W_yf7z{VW^ijajuhrVT^k|^t.۟GQl^\LL#OڱF<9KNkqߞ] C8O k:ùfue~~OVzz\.|۶eS(KLLLOGfw+atNvVJ*bu2t "{~ zH$v{r BUk* \^nNDGsI(Q|} HIQN7/. ¸̌W4:-##R]j=u\8NNLx1>'+Vw3.B&y}`033Jdd/htjJ2 D,HN>t;q:';bgf4^-).l蜜nmkZmg]جi)$'͋]=wj >鸸8E;t$9)Q7\km[8xh~^JLHP/_B?tDT,Zj,+))؉SwʷB&3s]ݽ~hppxAJ$҅5%EGFǚ^s\ElK($$SSSN:KP\Ω3|~;r<Б"7\huN v^k=thaA:w odtw``0܅j^xuqq1=-~L"eMNM 46^-.*<jP(dE"DZAprRҒcfvr$zJMMC88N 0Nھu&4ؘܜ%sjzXT|y9B!7-mpbq9Dd2S? ?EbceGq&p V;qjd:qkr@J>qBhtf>0?PR\X]S7|u(QDrcEĵ֮W,&ˉTD195][0==bDXd0XEG6_c *x{! RJ &&-o6D+M̓C;"JINڿg!"3#bU_N%Ņq*":Α0ET*E ;M (#=N߲ijni5'Ϝ4LyNJ#Lf<d_mimcc lL&ζy}%MN'JKJTJ!IPǧL8QV,KpX774|~+Wǩ\n Ox83#K6bJ%btAMDYIqJr'&TV$'E'buk#Ͱo?p1jX?xdǎi{ #d  4r|>ΠOPTcYS8}݃ZbdR:62:Z83;%EY*/-!p8<4rMNMML<}njza/(QDqpbe0|E ~;@q@ P]l4 P)Ad2cp@&RLfzci)cc#AyO+Wq0S({w?ya;VE’}0@T*Aw;̓wu֭ A  YBP(Q(O.d^\Zr2t %ьɼODTPR_ß  FrRSgMA~>a8D8<=/+-  %Agu?$)H$A5*;+suxBDH_Xk^`0TJ3;7WXo9<6&9)0 HCGgԷjd#GD$F l6_ B~cFF#qX`D h"a#)D HA?3;YC?6āJHP΋0:ٹ8UIqexi3_8-,l&)>t5_k}/`&A0P(rt ?o^= r}t:vd^D>-ъD"0D"E!JTV޹=~BQDYQzfFz|JC!a0MZVZl0FFEBAVVb-.*1lA]UVjXdIyu =}fH#yRZ\4ݱu "(]'p+ &C*/s8kꦦU*ҒZoً a!f>#ޑKƂdI$J %tZouǓrRf1tڥKNݴqb2 &S&SK"##=]H@0rk'JVwyqۖrylou˹jMCcSKKJdZEh6"(b4#olv{SNWXX`4^ke2L&ST8yk" ݛ\TTl^b& J))IL&S7$%&l p+W&m[ɩټܜ.#tZL*LETR42:& aQMPwtv\?ÉEn0u|+-mcy ܜ,Ve}6C*Kb Ŵ:]Qa'Nc++y\N+B`2 ,`0(J a &QTT0::XZb2 0ΨGdƽ<(tՖ)).t:] ,h4 Mbv:7`E%=@Ta2|uk BK[$$].JƫT{v juD]=-P[= Ձ`jue^nd>w"ܳ{(J߷sjni1|-*t_6M>m**,XWEƉɩMdg}݊0g[0 ݱuK\r7o>$o] Cц+Mcׯ͹Ehz}E^odt ⢂Ғk-cc`維)2 DӾ:XQ>ٷ=W{M2S%EO];yqs<シrˍl`2GF]3N+]Rzgf熆Gjjx}ު5yյ.ȱyEGO$Бc6J%/՘3~+A:z=+M^T;7?p,t:P]#߷~}EGFSg}A>7o=74<2:6~;cUVRW_yt:Lz}۷m9N#ǪTqg_$#"H Ms; Jׯ:|ao0;34Z]ksc#=矽P]c0~(ݸ~ݡ xCii|>Oo0446}g{ &Ӌ?{/;mݼ16>~oWUhu{;vpy/܃խ Uk*_z}3e%*"'I12i OP(.*\f5-m>xttMhLf4;;:k/`T(T(2Y C&S^Nv||f|P(A97@p}`W} ?Mbd‘Q cۖĄȷ.䋄\nJA,Z,KN'DDnT\'1A- B|ޔTNx(<66;J=)I>vv7~qѲ`0R4ZVd2%t_(%gO_rR?o_7/..Zx&2tL&3F*wɉɩ,RA<`zvvl|pD;{f՟h%J A\Ku[7d^IڎP"8>>1)T 9<4^?5=T]n7eYmvHGR-OJLs񸜕1 _wKINc5Z](^h19\c112y,.3dEQ642;G6ݷabrJD4riZ./jul(l<vn Wnٴajj3 C11z! )P(hF&]#%90,RjͶ-q~/޹]Rh4ڻʍU_7QDGO9?Jl6NZdpF33g6oh4T*>w`E"*#d67ts( IDATܜjgfmO&ꚺE;9=3s  *Z]Sz߻+t"' 6t]S˗ ή̌t@/"gNp8/^ɤUWfyO_~nNd.VLLNMފ2PpR`ܹ}@h5un{]wDVohT[m,J)JM]ñoXr/T׈ŢUkbcd My9y9U Wk4F]X+ =߅K5B2ljz1';0?n#՗j;{i4j(Fp؉S]ݽ|>jM%F0|zz䮹VFGDK'ZE%J(QDpp\HVf(l(D]N _9<^NGOJ_8}|gE;:?nK;^'(4OH;wz㥷0콃w% B;bAή;G0WnaFx< VyqFCҒsfvnzfffv`4޾ 'Oq<;7?7KiCWް :~vnl^q<ܢޖOftlrgݱ|M&cE%17 p_:w |q' f`vyrI0qCG >wLAd2yUyǺÕ( +恢էϜtٻ)VVR>] <|HwouchP(ev ̋Doo P(ֻ?끬6[/0_:8N\iM|P?sPOoi^Z ƫu  "z[v?t>nۼ_?Fr:J(Q>$rzfV$.9]NK]==V-*`C@07ߐcl6D,H;j)۷mFs,{͡pT=v\XU~YJG~{wTc߬]:rή{wͷ~ER+tz7%  p\o{p]qctzJr"L"55_q;o|iɹDPPArwȢ=3s34-<>,&srjs_[ Xَm[#A.`ݳĢXǎ"HIIUR*6[b:66*/N?04lXpi,K >~ A8ͧ#E:xAqh2s8lASg6;Lz9l'ˊX]r,=oNv 5%Y_ܻ{@՝:{ ?1ERN_x-g׎/O= |;?~/d[7w. rias߷8Ba͛|{՚Yw9]?knir9ؘx)8喕Q(՗̋O=~XaAnVff\n۳[zlv2wM5udۖM5uKP)+3#.N54<cJb`p( \^b=}<2R*|Vkhl aAiɻvl[hs34wzf65%0{wnO]}KA?=cid6ٵb_l@Roٜ561 Rccc4Z]Cc۞@pn AT .TOWw~٧Z:B;Zh:sBiIuv#ώOLeeONNΫʑѱ̌tǝNF}y+V}oCXrF''&RSzaAݽsP(#ȫ˶o}0>翹cۖޛ$cF b>bxU*+/XlhdC?==?'>>.91qJo<088~g߷{jz|oGrd5nAφ{۟FE W1[,>Fs:g](okmE%W,ÆGFBnk_!vu8Kd)F&Rq' aaxAxT B B.{phJ$fw\AIyg4RpFQ(HB.aF.At߆k"wP0rtzJrg'&0@"P(FŸ|jF1tMR0 [+*f2_xRn`^&x<(,"])JLa~q}`PPXDR R8w⺪5%Tjr20E9\N$0b1#_ J<>py;vd$]O,ѨT6 %HByb  TJD ClD"O=}XDљ ŢP=0;7WZRG5J(Q*x*-)~=pLLñ 0 YZh, õq 7{/%%&x4p{1Xd2<˕J$Rj23'#9OCa@@POoeiA>|mjzB(hus T*%== MRDRp8>fA,-- |69ef+O;qaAL@m˦odPYU^Jӹ\X,d2۶n\.W&@vHO`eGV$0|Rm1McWTbw,% S%&Y,p8Fx+6@f$o,y# ۗj뛮^r8_)?/'G(p9@`y().HHHDqĩ& "^[]Pp8lB<I χ{%%&NM]n>pL4R(KM[d y>?r?hF%Wu-L&6 R)91Ah}~?L&sb0VKN'T*uvn>3LHrɼq|~AR*}~` H`0kjq߽sp]2TQ7z_ٹy AIIPxn~>9)D"-,hT:!Ao^\RBy= `489(zOP `0\b45 NW*TPmu1STRvB h2v&G"4Zp `LPG%A;>/N2M0  )>NtFAbF3M(:`0x\*j2/ƩNKo0HBڂFpp E'6&.yllVb8q#&c0Dܾp8< $I&hP0$Jp\dZE FSYB DA&)1!RRĄ < I$bl$2Y)xVl^T&T"Yr.)@ `w,ccE.L&Fh|>p.DjXm>Jʤibj@R)Tf"`0" |>wK7`8t>?P($"i @_%c8s\0 %b2N' AnlzdRI cD2g t2tp`pP(HAJ X? ȿۚyz=nDH$)":CD"ax8[VE"= 8]N@c(:Kp(6&D"y^p<AKKN/X,&v\=7 6>@f JDOn=-`BG /#_WQDr!ZZr7\hpGz0t5:‚ՕٹU#xt.9k*WֻLf?c<"]nc>|) q۰>]daAsL 8owpdt쏿;x\U*+K%JGFB!߳km5u>d6>w!Ĭ_Wh^\K_(<]("EZ5Inh:;7'wlۂa؛/vS|GI%@~b,{%bw- O:LfRpx<^Zj\|-+3C*>{9666#*BRrA=VfTdZ(kCPvWVoUeec WCp[{ڪ6`y' 67m\x;:=nϚә CF ,KR@0 ɛ(2 l' `0x!󙹹yyZRT[ð|~#GxO 8u=.Noa!|> .t\n:> .;2;yhĩTU7~a[[Ue{p,G%ʧ53;f^/N($N'% )T  $)-O>Vd@0eX AH//-Yjdt,/zp8|C Rd` ,6:Ơ3^_AH@&7_{Z BYUVb0WOYLAP/4p|L&%&+Wڰn8|`0B46 }<l6O?}5 V(Q|*upxL&D\Q^v9CC 5> _wĄW{/۝?pYHs+HD{(Q|~\(*rܼJJLhuvC')Jd/ Zd&! @ $'%yFGR@FhR(1ٹy%#GqbA H' )8HI ֗N(Qp-`(:>1h ^1E xA z%8>>1/tDZl>S7 Bf@(QDrpA7p9Ue@bĢg Lؔ>=3;<<Ķ->a ylh|6 ty^yl[9+d?A]FPtuEyIq'll2t:Oןhقnڬ Z]QXy\] 6[MmPoi1S CGˏxFq<+3c󦍟+-m]=K.P HJLT._1G|>09zG:vNx<|>mꢂ陦k@,?uظa]vVhX] KKJMfDM,# .ַ߷a`n~W!]'\o0֭MINJIIaRCcYSY`0GJjO~0N8OM &eţc<WTO簾$&'/--<}mdYut>qQHt a W].wEyB!w=MH$R[  O=d2a F' 7_}>_fFNoZkTjyYP]۰o[fm"JV@42:6<2=<2 4Z=;; &SJVOMd"Gss/.rT*5 ]rA5{ڪ`0`0;۶n Go0fgfdGt1Ps0Y-m e*&1?/JVe~A{߆uZ~tl,1!!< 6nX]2??? j k25Zm^nH(\U^JpJ\Uifw\.SA<ЃOoٹg?>?Ү@D+B`Ƕ-q*%a&W}=`E%WKK d&'GV8lvIqaFvB5 yهH$Rt]=3ga^f5>fs$bquMVgDt IDATW tظb V FSaALP}i=E8(ϛ.T#y A?!Jx\goeT:>Ks :cٚ^hc^?19-JJlvG:^&;yzbR)%Egpx`4|CG9KZgf45[[9>1߿vMeGgՕG0MM(ފdeuvuwD~=q4Ɋ{`0t  ]mn(J~_`UY񓧃h[6(64<27?Ʃt:K@б}zfĩUk*GM]Umc-&dvn[޾ѱ))o^JrkVs$)5%c2_uFGNJ bd2‚&7'R?J%nG$QD, ZZ Ba$\\Tb1#0L&at:FEP!A޵Dcc5,L*T*eFF+#'H0NB˕qn(H$E/^e2C#d ytl|jz g|55_QaONN_mn߸a AI4 E8xLCTd9K-CAN{zxdB!{Dau|B]Z*\V(䙙AT^V#_|'&fϞ[+  NWh p0xO9:08tTjRb WSJbdy:>N.u:FaaA rR9?p90}7a  S$;@D7_w_6(JAd2P@&e2TvfFilLL(q޾d5L&z}AITB&S" rd29"| BY]Yᣏ?pNVfVT( rNp.s8EgSSgT* E-uʳá`(7'J\a1`rMNĩL&f(:6>p(T#H0!J\C9Yy=';+o>d2X86̹ٳsB$bF '&S&Nr8sZ#t)lph8K⩙YCѨ4*an{A=T %GƨQcYBJS sk/Ԕ<# y|>3=3T#&E5Zmm aGߞ)SԄId2i }R)VWf[212YwO_8^XiTG3RϜ'P (('")1axd42 tl>|^bDO~O=6"z}`0;+STR(C ټh4Y,VIqb23펥< FcYIcyZR!OLH(˭ohZme%X{}=RH{э׉Ţ]J<1Ab2tzzZj0x<-R $.--.kikojf3TK}f0 z'(P&7{ɻ~%׎9%WAާbzaJ(&)y9g^{sfJNoS^aㆆG2Y0[XQWj\|zlٷwwTfU0о=Bpvv-Z,J%Mͭ8Q)LF,kaA^JJr{Ggѱ 6]US{C'ǹܮꚺص%E-Vx2tdt= 068487@ 6?8pa{ppMIqU)II5uS3e;rLavMظH(΂ `HR)UI3uWJEa~L*pbi:"ݽ3tJr.wmP($DJ\.6\o b8>>(j0rs? f~ar0_ sB"D 24 ضeuW7nX{/FSw\I\XWTXpyjjZ(./3Wir%⧟| yCZ]`nji=sBy7CSgŭ).D=/V/<>19<2 htnTVTy<ɩDX nټ瞮r:}0̹ 8:}l6BQUS觟~7c;|>߫G_\]d29])C8<}vߞrHXwvWU=`4 kd2g>f:s.##m-O].|cozfS`0oO#>{gm޼191C kw݅b>zжN9w5_}yzfvddtz+~ ~a2>cۖSg87^oni{ C#=}7,뫯tNBjO??gW민15=ģM K?:y<^nN }\]>ߏ(szaX{w}m[6QuU#㢣%^/*J gf"VT7nhimz}c@ zS~MUP8fP5.-egfƨV Cqi ^ozfgW6#=bD:xfKx<R95=cb@ʜ C>aq1;+p1X'nϝk !>dR/ ~MNLX]n7FɤycwB!ڻRWݢٜd056=-bF%6~ ~Q&sUJnmk7Lfs\l,LIN_XhuiXLTmSYJNncCU.Zmֶ@ @` wNd!B 8lvnd }{)Ұ|MH ht|\(Rb?0rQ[_VR.oxENY`E{^oݝf  5$)9)>5%ǟ߾mKFr}5%L&SO r FQBN0Y̛NS`blA1ljzH$8:>+G9N=:6olv{B|w{B0!!nphlظB.>پusNv 2L #j6[Gӻ?X]qú[/5P(47T*bb%y7 :=  Œˤ:! ؘͩ陙`0d2E{ľV AP(X[RuFJxx`AH%SWffH$AZ]A~. =}R<~um>o7ǃ ReU F-/i4.֮JIN(+!JD7ssD"@UMV][R-T%vUM?NәK\XUS7HV?**_eחe陙 y5*z\Y]w)lvM~(-l޸^&/]MLAg(0ry̆t ŰՅLo*0B|90B;?("D!B_2[,vqEo~ |>LJGFN-,VSSnE`0'of qg]n[vg'&mvttvr1!?Z8,k>sӟٹ[Ta{O >Agf.qҒN7έVb q?8('&'&JDðΕGn _ve0<;7?6>1 |~GgwSs_3<:VY]K6˓nm!B?`b?rU fEХ%3q0 # aQ܅Ke<,aNwa|brl|d2KM-mD;DsD?; 1,Da7{A[Ŀ}da|~bphxrjZ#%E8^ q?X)JV/˼[Tm}Yk[`6Ro2xA&VTafв^׿_i?=>][fdӕ#u]S?ZX\\Z23ܕ:_W HeuMumĒiQ|YQdOʱ))_0\wW['`>{"1nFOyW.DI霘/~hX\nVfƊ}ō}"DnEHNr^s8kכ^/wSvCgeeT;N=ePbE۵s{0nwU*AA~Xeu-BIOK%Q0 ;}h'?-89V)_<19UdJ 0l2mټ*%(}7_g?[Fzd޴qtqky̹P(Ġz886>QQYMPRW['I%#=mmpo䯪 gq.V.B}'.]vy<#.̧ϞQ11[Y\lfWUj]Əv`hd6߽si.!ty  _}EH5Z F#ϳ@s. GxK' ѧqqV}9ĄL[^ќ=w1// f6x?/{ .ʲۛZZ'?zJїrsLC]T߿fmXWl7e[GO˟^\vMҵͭm>oMiW!V+ʚ 2\RzۖM7tcv7#>>nmza3O=ӽ}7wr_<~ `hdۂ EK֔oXW ɏ=rϏccwnj4.}-8 ƤDE+*_~Wӭm"l6N7/VToڴF?+/0==15=;=384<::>p:믾C8mXQe%QcD@NܾM$vՅ۶l0,:Z/]w&EWp8iDbZD?>~`lJp>cE$/twzz6*Jf5۶l黁 htt $}aN]K/{f9΋*mذ/"DܺBdllp9ή@ | ?xxd$DLR{/5 ^{奴Ug]_e(]S>FnNa>jYKe}-`ua@&H$& HWOoUMVGjWX\Tx`0?\x,&faxY~>Gy`0ffg*qg&) p{|b{.2+es d2yߞ5N \TBnl^nH(|`0+]C!4ZHDpH jhl~#}7FSbBJP8^LJ"A4m97A[6o]hRp8 ´.Wk:FQQQT b7DB`9l6JAQO|D  yʸd2 ?0;72띙MNJT*LJ&T TT*7nXp&g(l2 ALM",& D|GTV~=wbQ aH$$̄a6D"ɶZEQp\F&a8f1d2q# 4:` CQgM&rj!B[XkJ#*r17w8b@`P(dJ% IDATlbd2p s>Ŋ$>gϜP[ebb*'+K"OsdT6H< ?H$J>?Di!tvxd,#= EP(`)Zfy7E,>Dш|T eOyf vP B @T^sxd}RQY=<2b2ϖ]C"ADb0t}rP&b,\t:W$S(>KPR_hnYDD,v܉ Du\\  d~Dӈ<n{kO.#+_$&+w+*4WdT)Q(dr4Z=}7SS2gffb1Ngge~vdum=>R0 76TTVut*%9wp h3INJ\Uw N[٬ZZ2[.UV$R-Vk#D;sk,`2 td Z NKER(j0jҒvx\@HR4Z LC(p850LJVD902Hp7.-IbǓHK&R!ƥ% Ô 9aBǃ0 \0B V ]!WhTF ")%TmX 9 fE)d2a^O"Jє db <.7:Zd2JA bd29FEd2aFe2+  :ft2 BA&2ԸdR(Kjt\n2I$P 0 v/ N f2_2F5[ ,-H$Jl6aBEK$@J%  !7-\.Ng,˯T*>Xu:} "HbqF QQ"=ϒA$Bd2lvjh fKb dAcԪP(drVA@(@Dh0jw8dLrbU*&b.]n4: 9hju8<di4rQ p8 D"ǭ6fyT*u9 JPdE<&Y(P(T^\R~9V04Kb,+FI˥ͺRPbB1ah0:!p=kJ s~( FjovtڿFKk }n~#<^4GtvuLZ|׍ֶv\p8cRi0} 6uO?5k#c2955eۖ͟8Lw_0R[% ‚.b(*ח1ΞR*]n3O>153SW5*Ja:Aoik.K(:xJVGEض|ΜS)٧'%٧8I 31jqzE~_^~uA>X,h4QQG<|`‚1^S^7_~uS?uW]QQ:u¢f ?oLfs\lwVB޽k'M-^ l4iJvu_h/~ޠLׯ`ikJ#cBp{g׆uk,6[[TXqúw{_ե߲Sff ţGpqhxdq`pŰ~ E;wlݢT4fػGoݼ7_>"DH$A:{r2 FI_CMlƪ՝]=6 BFW q뉋p];w@0 x< ~No(.ZmZ/VT>c p8NN+ yD"ܾ\.>?Lf0r K;y<AlnAJ w(fSV/<;;?o2וiބD2?c|p(>Ŋ.b&>N_ANC[{q2uoX,*O~r6mܐsY`E{W}>߾=!ڻ{ؽEo. Zo0J`H$2|K…KxHp<03 KҒ)8tMGr9 \_ H$0L|>_ h,P8zl6B<.wd~Tr=^/aRY*>-m- 9YyKK&xp8`08la0D{rD384<8=3wmpB!V'IYLO_9nG KڒbzܼRβZmC))ID :m'&wlrPmni۰ I 87OMM'&%ѱuktzSskffFk[j[XضuD,h2nҵw#_]XLNOgff$'%ܮɩuk yõ&tlZ^$VVjz`]olR*Y#z`úؘFK56Y,VŲiz~eN633}3PW wuFFgfnѸH$t$'=N 24l(:3;aX\l qt:@T F8J!CaآF d2K$bEgqqL-.jTjB|aNK"$JtĨ-:~?É!H"᤯NgZ%p ñ161biu:`0bct:hu:AJtznW+"½qj\ZJ$2h(P(dD!cHx={E!B_×X"Dp߹map7V&άGbt_ O{=Wӈ$ nE0D!j=ߨ~=P(raxxn»~8nGu? ng~f"0 k"P ?J/W ~I*>ylvVƟkѱt*z=W22'&&&1 ۵sJ0[ qP(tԙ>oo~KXyqXLw.]^nd&2J=s+RMqJP8|e%E5;**o'x~aahx$>.˧P;wB-,۸~aOgv`9qO埾ѵ" 8}Pl~s)_NO@4xܞ+v+ WMRXl #}Ξ7-/Dl>mq W|1[tx}+ .w2Lp8>W^zء}?80<3;`ٹy&wwD&S(7v7:s>vT*dN"_btD /Wմulٲ03;ѩV ;{qhu חJIIMM ޼i\[R|L"U]ݽ̨o&7mX7<2ósii_`,V& }ۖIBP{ne2݋>8#H{GT4`05uE#cy 흁@m&nXL@|\,bkK^fْK|!wc||r͚"R|>xd`B\.׵fݱ8-mh5O=4e+s3qiZcH~} Qv{j)dr0$.GEq9k\#c}7Rr[;`1֕&ͷwtx< BfްjUk&y`ho#QOf2YSk\&[@`~~1/7;7'l\zCadww_|||&Qid2y}ۖpWhug?|`;ONNLo#šgL7PZm_ʶS(V ?`\:b;o7/ E]0 P8RŅřٷx- aw }{wy/?Eif6-ٍ KL@Ba>8}6V^8 v' p ƽ|DoߍUV<8n"D+>JVsl^f QYL(l޸zSbe1kJt9Ϝe&OJGFffT7ׅ³.3.dƪO>g\2].۱{mvh{Z F#ۺycgWҒ8 hõF*޲yk~`Ƕ.UxNw\taw86\߾uszvuk]B.Wˤy9'NAQO>/Z]H&Q]=e;''&o 4*Z7p8(7'ǻ)N'7) >䪔dTJaEQ75*.WѨ쯑 q5Z~h\\wU(jh4{o{핾/ֶ.C! D"aMm}^oS^R)bcS6o\ySPKB %9if%A/W,L[R/]'Ncg'2A e~_Y?PWÍ-@.#-ʵ0T+*g\|؃!Bin`8چbX8E :0876615=bB-,a Ǩ4*JqA&f|d8>19u`t'f56P`ЙLfOo_Ggxnv%E( ( 0 AG=\][7:>-!D NR^uqQcbhiiKLLHL'V<@}~aQ7rYrb]$ptDBBҶªϔmFz}Mm q Vr86ͮɤɩܜ%ԙs{RPNt?088<295 #ql:s_4* 4;Xv %ūȁ@d2Aɗ6|~Qa wR|7+3AO"A 6F\&MOK=}ҵD~'늋 i4N]+/)^( "!˕z>]]Sӳ33s1|O0:}2s?<2z܅ۨTpxzfnaQCly D:}ݕVHOt'Ϝ-/IFƵ:D_A~n]kPxGG/VTnڰNX,:o iZcӗWaݑH(HH ip8<2:f0,MNM}5)!ứ4BGܺ2xr?=OB媚xm9/BҨ\.w~aJ:Z0ߺe/~E,<ogW7^@DPXP8lw8"$lv?|\XV=EMTCPTE+H >ojn[o/,,w H_#Q`DfFj 2i1ex\p {'[eҵ%Ѹd2[,>dFzHo_?i]hEeuIs S.\z#@ؘk3|>?a$Do?'%ū6NoR^k_A"K&r~!b2Ac'H$ D!ǁX Ϣ"Dܚhtph$;+CPPT6e#+˵۝j*5u1d6.,xכc܆]=6}mIL&z}<<%YmĄuZaتdfXnwnNJzzKk;T*T*FzQ." =-t0- T*ϗ;5=c;DQYv#3#APQAPM]P M33 :pمEMَmBfOLz>Pt2q8E `%8O9G bMLH` v&'%z}tͱm&ˍay(6ńpFz*L6Y,驫kVD,.X֞*OݰTRl\NXmL E-[nN6˫h|_~ԖM*%b?uL۽K$H(^!nN6aK&S~^\&z}QՅ&irjF!gefΜLժ['p<.W&b684`2[dَmkwwy۳mTzE ڽkgZ 0F[G\&=tu`p(++sۖM߁ޖvp0r: )._Xa2HMͭ"ȡD6AʶoKJJ\2\mX/<4nnm?}jׯ[ -bl| ۶$+W-/> iimHK]sVAj͗-)^]x/a|F?c;gfdJaA^a~kg2xDO>~τMf 2u-H8z_p:}20 OXٹYAz?;~jbr55+_yY&_ihurߍٹ\s k֭]sB }7`euO<APK[bLrū yy4 A> .8~܅۶\!rAp.r[^S ~ >zz|F32:_奆kz~ ~p8=rls+p?~LnnM:_{%08iars|(}\]>ߧw}/G/A\lD"vID2?ůΜ9B!|>= H,H;s*ذhidP:!%9d(rqiaq1;32ނ{N`DKrFƩTʮ1j, Ph~a17'K(BpF)3#]VYm6@ p8|>O$dO FB\F{zzRy\nZ%x<({Ą$Nrh4U36~[9 RtQԒzg56~L8j4UL&S"-F&juכ ͉ɩ5Zla2I .m*&)XmTQSYrN!qC]=K&RlW5z<;)}Z`EMfՆe;JW_~arjWF+*,liLOz^QR(rA8x8nhnqA0T**%Ƞ T*u]`O^ճizbD];N @ N&8D @?[7oBhCBw!8`>c;o8; Z{"80 a b}&TQ" "|ot~~r=)7Q S諴0JL&F dR^ ff*UllL0\2w;דtl  וQ(^aoټQTQn"D!2_"\U/{Gv&z3tC3"#EPY#M${޽߽{׻޵79wa5BdB$s9UJbF${FcSuwS<眮SE"D_8^!B"D_ <2LRh~s3??0[,@08=3.`?~}x띕ߠ(ugcWwOumSb;&'.};=@i@drj?_  ;z!'y|wt10 2>Ss}TTV}bV7n? a=8?(6:670 qb97·~_|m;x;E?b a?-=$Xo_Oo_oP!B&X$IͷRE$t!I qPa yZ aaEQ(JӴ!b|F (1Ϙ;}}޾0"j}DQ81a H.È?g&8BCxߐ$ 嗘`(0Qa 88SBE(#. Wj㑚cfi0N0(/A0ɴ#INS*4a #$I2J@s;_\ZbLIdE1 F ca@QKC#fe`@Q0 /yxT(3xnz|~ɼX?1gT}=lj-20y9 0$955֎ (r8\DLs` {vvvqqbݞ+U(.XF{L0 gt(v{gb̠}N'rGF~@14-5E&2Z!B|ٹ?!`brJzO캳fu^umT"5,,5\(jl|"2""&&`u  s &9x`/``pxM7[{6LfX5cAlߺY.0 oڶy{g~[>d6dgY.l.{Q&Ƨh2Q-K&{sol'9\L X6]$e۷PkG)$O=C+f))T=u ?G?> rƦ@ v#))a޾D4ӾgNVS7<2ZfXvڱvjSSv2w*|E(DDJr9u:]^pr{x<6<|.J'$EQKFӖM%y9l6ñOUT^.*\/\cَ>(~L*OKM|}z~ۖM/D.sܓǏ>׿|4[,7R (~k {z֬ΟqAnZxϿ'J ~3!>.+3#B3-QQYŴovVJbl|b.EZݾ}~_mݲhA !BR'G{g\.gyǦ(o,*ݿo YmV eEE6t2:dR~ܱ֮֗dh8x2wt ,Vg?0(|f"4 6_WfunNVzZ'ێ:p6[6:GAqц0zaqSb2[ff4a)&xb$A$nݲ9>.66& DF}NMJLXajzf@jcÞ{hl(]w $/_]vN :~<===zS`p-ǎdefOfyO)II/<-*\GdlLK_yxc؝޾ܜ^x.!!R>ԙyCLL/<*=7( "er+aq ];^ʳA^NO$IR.m*)NII޼x@cG_'Ix/7'7'Ё<>yaW^|^.Y,k5u>}S JBjcȡM7guZk۳"D_ x8$ tuBRMӁ`;UյwcX:m8EQ/z+C;OLNAo,XbhIJHp88 |>Q 3>1Y~f/! HflE>/>Je"ht|SLA@ll̓gN/AiDt4M_V}rSԚ$?82:rhe2\A$I(~'rocٖC̅klhzsfE2*mv;ABrB!zn@A`0z˶ocX<.O!x<h44]][td<-:5hq8fSF A@ HqPAX,iji, 'pኾdDQ462A /_$Irﮝ|><, h42?Z@xfY악q{ɾp氏Odge>P驩owM$I{Oy9/9ubey<}{*I hut[" _{b+o,eX$EEk-7oT*fD4a4M(JSfxYlnT*ff?<~l6t߾(d)AQ+nfK_iX$v:$b@(@DVv{45b; brE6;jJ |>\f2[|Mjvi4.H%.+\.x\>  J@q |>L*(J&-@F-Hf <.7,,f.HXlAV*`rJP $p855Rjr<b °L*UUAZmrf1!&3:Lq82 aDl1[BM kj|>7p4 DfFHvVKffPѰ@0hڤ Ă8l͒H$Ep4H$;>_n$1EQb'\qd6= EbDLJs9XLQbX(JNZa(PrjՇKAEQBBP*3,,ri4,d*UT)|>MS(z^Eb@ ipA&2v$bX,jptZłah قa׆l H"9+H|E\.wh`:x<^X,Ff$I$yR LM0/ !>4jӨ(_1 7,X(%#Ϗ3I_c;4j5EQFG#ъ>9N.p`Y\\bX1,h2|>>7\8,V+MQzH$q|h I \.w)2[,72BǘUAӴaa0.GȒ< tJ%J 4MCAPzZ]OS  "cʇ'X]=SEy<槯|`wޓH$|orjW^H$٬s/>Cd2oc'|յAϜ>/s5 mLL⥊ʪ?; '&vB||o>nt֬ f{w O}nwTTV펧|Bqo(`XXvy\ndDĺuK**b-3On IDATpS砵bu{<"ıj,6<|˦bV_(k2yN8cO=ͼ=n>x2T~NTŅwt74div}хBFCFS'(n($%91.6Y^LNNQ$uԉcGm*xU ?woZkӝ:qlf78  #$'XE)=wut>y`65=}4hUSqGiںȡ0X-GQݻ{];RSfQ(2ttul,:z NKƚΜ>|pCG}spWZVEۻRݧ޾uk>f?&_S33QQGT ?lO:/[}sI;R*ծe qէ3죅"㭷np̌U/^03_nOX-[mIR_(_}%@@Q̬?mhlI (J$ \ FO|~TEn9X=v8.i$l6ϓI^OTlݼ6( Iq9':sί<̬nߺF 'I\.@0(hz >|>W'xJ`,V%<}; ,FH~Na?+v=2oJ͛JssRY `KAjp(۾ A޾|gNodNP(V9^\Z(DBfOVTV8vXPHR8 @Qrf>nk\$I?p$A \(x< #EE3idRLc&d2O4cLGA W_.۶F \`Z2`(*$bqb=VR3Ϟ>yf+Ǎ.\vjj>e2@P{T}Iv@ (|?+ h4g7^K].;U{\"gf&7##/.}~0REܼARXX|\lff\ޭ[7DԿVؼh4\%%`ˈP(lmd4ؾM,~<Q)]wFq; KI0NJrJDQ_8A,'jӆGE ˞"!#!B<|`X٬dcXl6\%/B].fIRdhҋbǻ 2,,.A}d$3[7,P (:.]kZ'DvZ"IҰ@ *J/AxqiDGQTJq5L^/22+|hQeXXaX,,r\6]Ph&FFd2M"Y2n##x<0|/:: -VۭirSNfSUac[}?`4H PvCR)U!/Ǚa!|x"D_8_!Mcngjj3]HQ}[X01gn`X _D ?[J?E .{=GFCȧi: zqð޾ fffbQ [xO_ "DZEHJBeSǬ4"(:==*665o߲Yq/tJ'&b$Qp1ˠvZ7?6M}ya}G*OӰ՗xi`0;+U}Ҷ=EQ/ULLNm[7?{47inmOdцukVd}IVT^SU[p9T󆅺'Ϝ|v;}._Q(aOZ+g<>QQzKWr3%:pumݼaA ۳Agebď"WTd9_`Ro~xb<+}PyB⯐LnJXjMkFaa!=-X``ZUTTY,6+|@ ع@dGg¶o"zznw~ߠ( 4M$I{wl108Նo* PU]kٶo74^oЖol 055:?wu~뭮(tݧ$t bݶuk**Pxh4eee$߿}tָl6W(onES}k Vc8^yZ,Q04ǻ:?Ё}ijjrutuo\pdc!ǻ|p%7RɆk7|>ݹ`Y l..*\τ7 eEIOrYͶؘi_ذ ;+sqX(qip;-5`u~8A岔Z[UBlttu(vulTt( E p{gWU֯}|BY"ğ/\V&6p vu'MMN,ݶfmTw m\.߿oaĄ@sͺZm8Aתk"5n#rjY鶍8\:5x]J8oOoQ۳IIi_ =`35==2:vؑ䤤hiuݻwq9M%Z@StwOO0];K}~oSEGƾS]`6 [QxbM6miug:CĥW[mk FӾ,;##}cц˝Nk5;˶tK+opi~APbb-._ꚽv_&*J?S#C#mmkjfXv8vp|~JJ{. yԔԔ{p۸\[{gf>qorjr|f aon_4}${,.cr4=;7K'~hѱM& 8^x(Q}pp}maaa1QǏR\(/).w(|ns0b%oۺiA_$JiyVfkP*;Jy|uk tZ#}dĮ\E1kak|ܜx=q[nB+9)jrjƩG&&>q2D![m4M8֯-imwllVцl6[33۷mY)pttlg;=})IL,J#$EQ|`mjA|>+W69̌U(A;F'i`5I\Eq @t?-*ry<'N15Zǥ$';K6FEh$Il('&rٲ[m''o- SLgfr@Q4۷g׵ؘ͛Y,@ Xyr76-..-M+ek5u&RI6RШB5\_Z\2[l *2֮]fȈ^ IR B!t|L*J @tؑyֶ[J8il1?o(ܰ`u^D,  ;˶K8::FR),.* KvTOia .]zQ>Qq$F/{OMO@4PpܢѨwmgY|AѴ]IMJ>qqh2o K/<7>>qp8lbZnOLNNLNQqssPaEDFllj6gfr,/'0ǻ*=(GR*cTJezj cfe|b׿}t&'%>JC+XKFB vaw`ޒE+x<^0$I [~3AQc1n().bYYr~?A3,Aٙ/<̭vEԴ646>u<*J<\. lIۿwϤ͎aR0n76UVuϮU<.EQ&2á(jUuIqQqQJ8W\zۨ=:$Ivƫ|N;?,j[2ILMN~L&3| ah48v>p^. `9L͕SSSrs&'=cE"!I<ĩzpV[:{i)!LV b@0)1amj`FK-X*.b&&h1jURS@695 @Qtj-.-$98tB Tzȡ?8vgW(}nNv|\,C#̗=}gOzů<پAPh2=^,EQȈW^nr/f ?xQ'u8]'Zk8q}~? #-7o>q,,bPd4߳[ c RI]}ctt>2B GEECQ䍖Ԕ0  pi`hhq.'+sEQ.+-5ʖֶiM;޾{h0 kmkOKMIC04;+iәr&Aq1^ S3q11\.W"0Q4BV;w~zzvؘׯ\r &<\VT*%NINV(MqQQzm'V0 3[BNݲrefμVSWp=+3cu^N-|EVժ(@0=3rU^/!!̌!5jURRI 8cp\M7nwtUvjxxXjJ;W(-5T]Q= ^zmv pJR"s܉ >r' B+^kr:QEk:D,PnGɿqudt,1>.7'K$565'$DEX@՝477o;RKOb(3cuk5L luUSquk$J2n.k>19v(hںP(Ix<ޒdXXt{<~??/gbri,NRtFioI} ))@0+3(әUԑ$k2[ Qz}NvXbkN(׫Q5j53~<^ߪ4}9l6 *\9>1-yp!B  "ė`3:m];Y,¥ ùdc^n?}e׬'IByIO(߸qsphX.OR=յn@̝>7f߽z'cbbʋQQQ+/ ̮o;;w}O<^U]˔+UOS*כoxS3fZum|\*򊒍7VT^qںƧ>nKGQ׺5׿ҋ*R>ĩתitΝYUյ4MW\zQFxOJ8s+2r5/x)Ilhј(Fy|EKoů~?]pφv 𰰼1.4fMNde) kX2VGG'IQNS!+̑qْ/$ϿδT xF`b||rRܜѱG韊0fbj1';OL7^yb BZjP(Ө.]ϛ0fjz!'+375}vnlĄyaa!55Y(1NܟccY:1ܹymh;U5n/)Wwh"D/ l[$X,A 4͸9/rׯ+rkxd4 ܺUQY,lD&'/6#zs К<`1h#4m6[rޚM-1:wMu:- hF2B@ӀlXX49FJ=4>gSq#d_߷{tr5Ĕ$7A˓J$^AJ̓gvmmhN% IDATXq*|>@qˊR)O=wΚad1aL<>͊)X)#` \.hD,v= #~ .[V645q9R 'ab08WU|Gxh4 K:mJ|>hddݺ ƻar9Vكu%MJJt$IdR^?4

=3399eEZ,־Gח*-?/wqqIlAn\&i Agqqc0 [6Dz}sr0r2N]X\dbj*:**.6f|b٢R}B?-II=} A9YYk VZ/6miVY?+{~ "De"t\嗮lmKLLذ !>n`p֎mq$iw8rY[F"ݞܱ קR)3V]n&>](BrVUD/OL.۾]@V^}p^ pt*r``عY]]wv,'5\?T"IMIIU>JKW&'%nX!Grs 9qaaqfvp:>طG*hj(</4ܼzj//8!B@ȋ0D!B%ւE>"%Mo&;:{L&GvOIUPᗌ@c;_U]QZօOd ?ή@ Yp0<2:)sNO^{|8qݽ}]=V'jn`cW>1]}cSKO)à(щ]Pgd{;=3rBj?*%׼>_`;zzX6|bؿO{Amv;vGgGgf)"_<`[\(j9 I{<^E-Vkŕ~?0 I|>(O6 (tvw9gw7A8`{O$E^ɻB$M~AQT  _Cc 7zfgge8Ax IBK($ EQ@0;w0 ð`&^$`P,-5E($~?S?c?yuB4㩾|(yˍ  ÌaڑH+iaNj8I`|*87zwaqxܞ-8*z^ *ʻ(?EQ~$ 0b_4;7,k0|ν^$qaa|?a0x>`%(qZÅtawύOL}~f2 ÈEQi0zoak-ð$}>0|tlF@0  (a aF$I;8NQ~0=a0$dFiz分ai_3|~Cw; ㆅKW0 (*)1A"p .!Ba'&"t:fr0=3;08gFF:7^W*Y 7na^}ҕ%A~Ʀ慅ŗ^9<2w;֯#I_Ƥp9N.ěY2ϝ/)RS  u[|?\*t##8N/~~"&@"Mf>{;߲N{0&11],&HoRR~b~atfIϞT\ZZ2<~4=-[?o}cӑ? Ǔɤ-m4:ֶFFG?W?ߏwX,7%b1AN+6&:=-ZZ*kߎn˭T*f_ynhVѨgffKFcm}ғOri)ɗ\ըwcx(XZR-M )/~uΗ' =bCKVnw".z;6qܼܜ̜̌;w (zm[7syaRT"YAZ2Y,H^{?tZOđC ׯmgO?u`7N8T(._~V?zLLNWs/{8+VU2`,$I0gE[lٖdz3~v̮34ȖEbȉ AC#7: "!iDI߿[ν>{7]Z6|JGtu" x0A)Ii)I1y<}{!09213Xlֺ X499- mv(fjՖА^>PBϞ:R*23''ApL.3#mpp(VҙSwtu(2LAܼU$IF.zܙؘG{޽/._pZjrlLZA^{74tQQ|ui=}AttvKNhWwOFzk$'=ubF?+璓>?GIY^tL&- m6J\A;LOO]^YzܽtEѵn>Y.ѹG7 r7ό%'%m޺%cmܰN`0&rٺtN?cX|_ɴ䤂\mzsxee՗ϝ:qedLx@Aݺ?w@1ͱ g\nxhhwo (HNJ,:P0<2R\ZAE0+ $/]6=3-?aI޻߰mKAo/"C.QE$a LaH45=CQA8Su0f]U?zGo~CQ/AJ'&NA$I:N@hHKgN]v EQP)E y<޵fqދQs|>rj }>?0 tV9E x`XUiȈ_}iKv֝2z 2AEBb8Nz~tH(d10s-Ʀ͛ V&=77+ rl|bˋG.-,.ݯ(@Q :St:aKǎnܺMRL*Ybv?piLo/?F_ڪ˥t>ӅEEd2XLVtJCH҇PԋI$b>8 ض?x"`E T H$R5}H$BQvqZGyA֎hm=E>f>tU,y(.Ï^TFzڕ7 '?!> [VQED~Ϲ""TJowRDڴq}qirp|?|'M-_ع5'׿-mm6`hKNVOo?]0Fs~X$&~r!p:tЂv jfd2Ps?{;x/$(`XNm[޸):=;7@[r8,q\0zQ0ŴXe2&i2) <b8q8Jfn.a1O",aH,q<ժP8LfL&ENl6f- qXlr9AVmLfpPp$IZVDb|>b0 3n7x^?8x|I|l2$ 5͸g02akx<. 0#bH(y<Z+3 IfK$bIZm<b0 @ l`(baU #0, 0tu#Wf) W"~۽Ժn( I9l0ÑHVM!nWf^by\a$i</@ ñn[.YVG$qB(x(|fCQx\_vl6Qew8$b1aIrlI/ gLf3IBP$:Nb2RJ%YVXrhclv;eXE6r}Y,lv l0 Pp]\.XdMdH%ALS.!b4 rvG/eX62  ðB.w\.r8\#!Id2d2%j~(<.t( O:XnG%I2$8C_X\XQQb:vOIj5U\.7ϣ;6B!jS3^7<@QhZ\ZRTAZ ŰɩŃa4 tgglvTd}Ql0 +ZFR$s8a'-OEQ+F EE2L;==N ܼnINLLpl6;b}>~vv'DZX'Ir~abȤ_@EQʲѨ5Ooxffg>R$pd@( H419E$y:XBE!ڄg',I[oF?u/|lX, (:P ^Z6465cݺ!6SM-#|O$//-**vSw'$ďO>uB,^qfcye<r/ bXoݒJdz¥ɳOLh]/8^S[A`zFy׮ v]f~v"&Z}女CC#ͭm* S'???[ffC`ť6=Hzrݼ]=׿|H(mR1 qgNx}P(ry}ccXhL& U*]]5i;jcAB%޵s[rx_qЁ];$k$f?L3:vЁ۷.)u\߽{Uw=2@Q=\wn\fX s&jmhl>sDA~n_IhVggTjUa~nlltOO/ ۶DG#.׃'vu|陣 r74撲Ȉࠠ۷b>d0r{N]ddLb"ѵ5'{AA w>y wrjo_ٛ7KNL4[,(=hI% ӻqúyLs~~6frj:,40?7,,t`p6Xǎ۳.3y1R䅝;C7oGW@, @>~Cn{ٰ"I ~fW* +9ocG"xU $Qp"CQ |0d&'%..]ݼpBR?*a?q?r8^xХ9NS&~a.x\.H `|fyU _o6nXo4,K|\lk[{_ &E& ]Q9 q:}76_gW+!!+FSEUcw\Bb0UerECfVYw}_~ˮޯʤ/@ @ ~v`2J<-%%3#ͰB I (i~?Ŧs; е&*j9$"nˇ\u8eX(uv;}>X,r8+yK<>lx9l6#I EQ. aqry|D,Ea>LQJw]8NB|\lLtdDd%v( C\H(l><}Μ:{d}C ML_>Xlw)Ǚ9ErgL&˽{W^LOM@~ӯMd/n]駦$)b?LkxjCSsxX|bBjmi{008 #}#CΜ Eͭm KKf >?<2ӟKJr ɂw&g\x<ޝҲѱh]rbtHppbB<ᴵuL3{_A74mۚbLfeUHJrb|\hlk۽kW2=_\\ jfzF֜cwJFG7_jljYX\ٱ}kppPeu^a[sT*嵛'T*eFZcUV DtIccc={v YZUS[7׻۶X<.uZ߻3ƒʤ]=Y g8JؘE}kJL0V @?K>侸x!B`8""0a!Z(l0$%Ib1]@jX|355u:]Fr Nʪ{ IDAT^Ut}Ijf;]. ðZlEfI++FJ AjHA.-/@ijEQa`h5jnX,tT*.[R $ "H.FR)jȡ~ؾ/dҦVLRÇ~Ln"GV>? |՗tcvVqA~n=A}[;eN jccܼP=__w+nX#E"#ݼz҆ƦʩiA]qTTV?rhǶQ?x;}##cx営ѱ6zHQk[;ar=NLH8ήn&tQ6]tP(76Ͽ}c\h4 h=FPxa`d:yHqi9X<66]W0;; P 3#d* /_\+ "")(rKfAs(bc?K}~ŻvlW=}.z&5X^Ye2 H۴qClLR!ר86&zh7MťefdRihh胎.z4\eUdFF^奉ɩw{@0=e݀YQXVp82D"IMIZZ^XK ++&ӨUN*?JdxL.D}(KK& gEL? C}Nh4a>_o_?Aܽ;o+)dY #~<+޻䱰k}б%'`lڸbݎ|`6Ms+Fa: !h@(HLs? }x/&&(nVH[oݏ&6& <(t0teMfzQytSOAB`XV\E_QU}a,€ HV{QtF?KTWw/2;7D@pȡk7n]vı#j> 0Em[UTh'A0 70fлÉ jhl޷wefld2NҲCt 0 CEBtQ"p0jG}B}t3qѡ4Zd2X,P(`0{j +n,..ْp4~vLf]TTrrbEeueumFzں7o Bfzx"#`NIIҨ:]EQ'B8κ{-m<OJrĤ jcb?|gm6|ߞ}~߭;z>2"Et,&pFEElrQ +.-s8G20<:}𰰠 P( jbh47olnNMMIDյu1!L#**DEF=hOMNEGF[,+H'hiʵxrٳђq8]!A"P*wtq\BT*/]661WPx=XAJKI"IRVGh8A 1!P(TᛷwtmޜjFDxbIxi)BBV1j411aXTUTJ%bB*@K@0 WOXhLJLx<2tӆ 3eUh]gh|F:XS>ú̜ͭm]|>@AJwt^v'?Lo?~OFҫV%e#>>v k7n#0o1Ѻx\rzl|B.=\r.֝eæs6Shohz >C">ZuHTp:߸mXr7o\j]tP v$'%~fﵵw446Se2C1ð];UTq8A斶BcUiWgjY^9k DswJ6ܒ`|Iu }͘Ϸkٹں{fW_y5u#t[ql~aq]fƶ-7ol0,#=`X222R˫j"QaAru Rq̩/OW @_l6'.0ܙU6bZ0x\n.]xf745 ?p:o*޵cۮۋK)SRVt0!!=k/ܽGA|7ZN$Vʏ9QS[p8[^%@ޯoeoj{9Y n~_mڰGoRp8 CgN(vqia~nfzZEeEQO jk>[ Źӧ U8q9Y?w%bm?8 ֛o/.-~Ն(3O/+) Iڍ[Y6ߓH K}} 7|k~~4R?;[RVqp[o.#caq?z{;mx=+@_Bd2dhTL&S,ݫHx vcdl,=-d0woh:r Zrx<.I+++ !ftxXhH|l,Áa~~vvU[U?;pd2fH$y<.Aɰ;iĄI٬"QRb@ gg|7[,ٹJIך|NZڒBAV˭VJ)wn;*2B_Xll6[R$~>+d2Pqiźt9<2ZQU?eCdD8͎r8\nؾjÿl2HqiyfF[맓S!<,ftR*˗iXZjJdXtoSKkmݽtNhmΟ+V6Poh*Esgbc֊/j!k7[^$k;Vx$IQ f$!p8ojxXhpVHAZ")Xշ6mXC0EIB0 0IQE$ PHpphH#$IR(; pvuwoɁ u a'f#0Z/%bZ4LS)| ?.i>((Ʉ0Bʹ3E +k`!I @$H|`T*e.'H믨ժ7n  V}N臆GBCCJ"$8kkv6Ȉǻb4ⵦ?@ /,DEF2fcte`XEE .)Hbu]GАird|brt|\ՈDB$ =SGl%0 =,H4͌~֋!!#`XyO_l6{L.9)qi  M&DѨ^tl|",,4"<|dl.-RidLttwOҲqZ)1>>=5UP8NqzJI#yxWkM*ꚕ޾ݯoSRnv<^rbھ5pVTi5ZZ(sͭSSj848v/`طK[vlۖD?jssU5{v IՖH.*2(H[Z^ H$]\*IŠRF&_?2:vh}LD'&ZtdoZY]kZ/_fөol..-XK$bJURVIeR) oމ..-WVs{|\_Zۀ;%e&XՔW`(vpT"a27n颶dk5jÊjǶ- _$V).-oi{q9l[}~EQWol{5'`$e2SS܋+'PE @7"  @@a WÓӵIcE.E'[?765-.-IUpoi})<3?ֶ֝^{2Ol1fg ?0ihX;[?f>O=7iqPY]sSIPmhj?. 0,-/>hji]1Ww\]-zG~{^Ʀ >{ZJ+~L_zlCcӽ!V~Ŋ$}>~Mj3Mgaq[Vt:NAlXA(JdRRfN/w _r$IQv&!plvEYV px< b1Ϫn޾SRA@$If&e?pl8.eHXfq p?q7V 6<2߾DIQ_x<dڼ^^6Vn2-lM&CRytl40 0-fG&.Z,VEQVd6c>IvfM&Ev.ftև28|>$yuÊnw\_h4ds8FjIQ4L.zQE Ebť&nz~Iw+8(qMow8򲁢(E_d&I_q\pqht{$9bi IDAT>^?~M&Ӆa;"BWY,`1Q(+X,AYm6bdƱ :xܙw+ĄTtlvn|? So|Րᑂܶ_"UT}W:1{ܙǟ) s|nnm3Mo|㕿ڴyӎ[?#KK6GoLw}ı#^j""]*rzF/#"·m.+ؖ 7ϗϞ(+;REu$+:XX{ޑC~EQ'??;E"r /><2:08DQG9uqdt<(H>3/ܾ-;kEQE}phZ$ }{'?MJLp:|>?6&KT/٬۶WT[rV'IbDoܰ`tvwtI$C kMLN$EwSSggDWkԪή۽ݓikoʾ}{v?wO$KWI$9^}Cс޾͛{ qݮjbcV S3K%bAE ;\vhEI./"# j9j4i\gl|AGX,>2:V)6͛6,,,l۲%>.ッV)GF7nXh{I *Y6޼],HBCsK$MFƫktQ]rizڸ؂}ͱ'XMͭ,&_K,& ؘĄ䤤w#n߶A8D4;7v@GRb_`0Zz]Je:>.vjf9tpь~o`05%gujgNh{LMw)n|u`2EEE&''&GG/zy@bBGGWψ 7^{yn~_~ڻtgO X7@D\l̙Ƿduu8{T||m}~fzډg:]Թ3R>?v;%^V+U(M# fxƟ/'&7,F_$yL&EOXVwh'&STDDDxع30 ےu!"#? 8ů~sLT;sJ燇&R=uE]ݓSSܙSg#"8KgO=8xIEO8.6;%qqnϒ,E y|lL8I6 wvq CI X&$ k}~ -V+$Eݻ_mk]P bhd$<,԰btwEBı#KKQ<dX؀|Ȉnq :j  zit5(V r8195EE~t8A\th43K֖D\.!axa>sN$Rū7nq{޻OIP`0X AVq(ZA.ؑ" g0EA0D=2J' )rI|cG9TY] A($7tuAo+Kb,&AP0A@ȏd4S0VWEQ^8)BQ1x<{ʵ+oŢݻvbߟ9LaXN-6r)핗dgUQS33~?q0 y2 X0 ֶIиbdڽFd KյwGߗ0I|~$ v}ٰCy\| Kg^/bVxKxXxTdT"x=  b:p"ya` S#EQ_3o~UXsڍĄ;b`LwkB^f"l0M{yͻ[{o?';@a:DPUNjBî߸( ͆׌hU]R|y Jjs8ՃWGHO|>3#O; '&&U[ \S#cN33=[gOX{L&sjjIܻg眸{_ܝt>%jzFp8nɶ췋K`7?488xG$ڹąB!0u$}:߲BuxN*i^NrNȺ/\KJL fwAA>X, ~x̉_p^=AiϫM|~r=L&b</A>χ86LtBDL&~AD"x^ NlH$bEQ6b2~v80,l6 CP(t]8abCR$s:]|DB nH$^zQaN$A(p(rtڸP pv'B1 s86!`||>|na3Lzg I.K Ѝ5Nv8$v~r`xt 9) \n7}6nw0L P<.0@@' U_.@HbvgXBtb$vq! : apU( P(`XN EQHp86ERl6 aA.t0fs؀Az9\n^u,&á(#)b?>ѱ0tr\y^ELg=8D0ӓg9g F03EQI^p߾ݷ{}u(L09@'ar>4 $ER"e͙Sg9%v˱"TP(.EQN9TFb'-.[TRL8.hv88< H^=h>x PRD_a|>L&x>(P~^qR.J4M(@a%1 O͸h,308ql\lL.kdFT&&&i4M[,VZfBtt@$")8OMGƃ4 qMtz1::2\iz`p&%&( x}"c#b8:qc&T\\LhHq}}^P$qq<9)EQ6<2PSr<7z=a))&x} "24ozVfzZOo߫R>z؉S3%z .ĩ3!!JRVW$%&h4Fv9"µ=oa?h`(G" ;+'wVΔ?tn2M MUwCB-3~nXS ^/˅Vz JfE7[R)kjCCC׭YeZ{32rkj-L*iV+/ML_rhO |dv֌5VLLNmڰ.*26{AQol0._NOKu{<6)-xJ EѵW2 (108tr20 7.[0oNnwoN/+w^q__a^k<7i'ISs˜Y^OcڰkVQ4vv͝dqĤ2e` D[qwuuV[{;!!v`Ybnnnd%Ξ3"``EQ ͍yC~:~̛Npp0LX b)[Pk'V,_:]fUXiZC rvK%T $߰n7[YbHA%~qYQQZmإׂ8|>+Ʉgv#IpI%nqEQBp:^QFs[;۷nq@nG.D"I^O.r6,,>.q90 \.Wi3{!=qh4vH$jnnIնy)Z͝-xWD9vg2א$'._UZ-AϞ?ҁY3g9v^B*D|`|핗RQE_q`\|iLtt]Cg&Ӿ=9={R_ .GDo޸ <55iRsb-)1'fU+8';+|T*SSnR*)9ٟ#a׈{^Pur2~ FBpbBV!BѴbrHV D v r:]J\.S4m6[P  Oil@0( )iJ"8NۭR)EB)F-NJ(l"0ð$mVcAAO%89ǃ4!JFz5 )a4jǿ2_xQ(HFQ$I͎А`rٽǣV-WpTe >I9BA3dJ`w8R)g ^PQz @ϝh`})<ϷmX]OSIu JɁG|F3<25:084>17+@4Ewt '&;ej2M*|_i @3ZQFQKĒEyC#Y32ݼe1[/Z8<:vƭ5VT*AhYhsq۶e邢}{v]Qy:Dv=5%Bm]CcFԬP(b*=k&;uY32V._%)vwvXHq^SWi:YYUbŲ7nt,~]10 -7^;wl]8o!M/Km7 _]0 5i7'7ʵwRڞ];jBBgޫ]p|326i;-  ơ疔 l\VTVԚ-V=v*[{z^;2AY(|ӵz򈈈b&&u+/2H[{GbB<dھu+ngm+aI<3 SRV۷tY#ofw;HbǶv`0fftzӔA=;T+y~`pڍ6 @>;YjӺhJ584lZ6`#Rgf͘;'wUJrӆuBo. ,5i>t`|l\8obbRx_qc#SSfhz&&wb$%߻͂/O} qݽ^ @ݎ~9Y~a轻wuƔ$Idoimd&I[nllv{<-ePuڍ[6mٴ1&:**2GopAQC""32q *a|Q?w/\ʷ;UJMME(ڶt"RaǏ<m릓I23#}ۖ.geff&$3e7ni EtTTOOoOoTVUUTۅťcg_\"3ޙ;{֑7[1bYPgvLVQU}pĄ?>'u:!嫇xa=F +XU߮APeXŲh̛vkW{F7';--u #crWQUٵf32$R) 3#]*U*xòMLLnۺEQ1.Pu)ؗ|Uub":(e^''%ʙ?~p,a`0&%&N_ypV%|m||3CzyY07nhukV߻9bw:/]1z:k7-:Pȣ""Z 0LzkaGFZ ˓=ia)-0M]\Raܹwtぽ+{'&l6;D+q IDATYXԼ}f.?pY¬r(%9Y.EFDtw~.2 s%E%RHȧd @4lq\SǪT&۳ؿ'$$8v @??Wf.OϞ(A DZD6 îõ:a`p0::*#= A)IQQ2,*2(;+aesfKe[X\ZЈ HY}UksgDϟ7\3Ĥ̌/C cɉ Be^X\Q^8<.3fwv!0CP\\,˰8VVo,hlՆ-]NIiJJJ IZj $Ix.k |UQ <K,.*. VTfaj I}DD;+gɢv?[µW_>m˦ \rm?[*r||`?+܂Do;:Fz۷nUXqW,^7?z[Rt޾|hii ÷vcMF822ZXTYFC򣷗/]Mց}{jU|CBH~_@0!"\V &SD|pˬZn `m&F]^Qz՗_\. 83MMFF[,V Zm \R?1:6"qZmw8T* B H&9õڌ)%8X3qGQttl,#=M"$R66>5#$4t=*kgdd2mXF6-!n&&OLv!32{{gRvҕss.Wwwo?0h0bcq OMIxPf=ojTJek .ϝx:; _`4D8?:66>1xHp*ofvVHp& GWV,(MLL2=Q7%> |my~phvMݖMX3MkW.#HRik[V&u;qy[0ði;p7ʤRYa9NA?<E9ya Qe A@$wv ߔܚ*|zMSf#eֻٖ2 EEm qAnmI:Ao0&%$mtSV%''56 R<]]=1тNw¥M%&Vz#r9q:NK ufINJ2TJ.h2i4aa#c>70z &bbcSSfR A6 A mX돉z}zA|B&'%za|> )1!=-MQ;NArffR/40ͣQE%efmy!o =;w2Ie>oɢ<(::*<\T*aa*=ciddthx!';+##rl|bm7C3 &%& Ն]vC.m޸a@dD`qpㆵIqr38QPqeRiFZXXի 093J+mRQyr~1rgRJWД`~|||aQܱ} "#ݸ%۶l^{T߮vܽkB!v;oSU8`^DDi|fRSi!Wݰ{v1QnbfϮ*RBH.\8oAVkZݸzԯRQj*&:굛5u RD"5P48119)qɢռ,0 s1>dj Z6eIX,G 6nwX5Mzia8qi`2M -_]=9:>1qlL_\I4x>>~bl|tv Nry( 1YI(b (|AQ4Ry19x<693-N룣'&'YߊK(l;F a9z$s Ih2;qdbXVo0&a\.pf88d0}>xzi0~CMzo|>_kn狹(wAAˤN1efhlU74qYGӴbzaJrܔ,D imը4x} H7,˚-ԔN<ϻNp:mv2 cZ&x:nwL7RvT"QT0 ''')bza2M VM{g ׈8wz<۝?P^Y33f PM]T*9!Rvrb" C%eU^`0~1w DF?а\&h~y Ȉ'O;OY,ERmXhaqiyEA^LҲААO止 ޽ۑ?5V$KLS /ah2}t8aa]kf45 .*.Ȩ]SUS326.J86 11<745-T(kk̚/~tf3KWq S*g]2[ccc8uUttvb10Laq쬹*UCcSIYEP&4$faqyET*)+t\ ._1 7w8]e%ڰKWﴶ)a3.l˗>dX=vZvgDpmXOo윙Y3~EeuUm,J?|2_?}>+c]C# ڰ5շkU*տqWU}PLtTYyeeuHIItu8ΥK%yܞc'O0{155l2'&Ŀ:nGPvMmOo_x9>((ԙ#c=߿lbA!A-;mX{!ݻƭ"ZRV;.^2L-iBީEਜ|F'Ǽ9nyrCDB<|Rڹ(oA1Rdbb qbhfc",\/{dbYyy *n<`DGDGEDGuuw3 5cmsrg͝=oN3hljf<b (,44#=-5%9>>~ӆu^oۖkVD >X!18ﱌt>ðT"axfȈw괔];i4A1Ql6pm\LLwO/1{wܱusK[t#N~70 c2t̬{wm&\fZG}>rӆ֭(kݍ㸰+|]?/Z&'%] GGh^8o]z @Q$6&a#ɍnXf{wJLēRpz^bc7_lx""b;:1a8$$8## R7_q:!6,,66F؊޷{ﴴN{+SSOa8*2rmaaAA1у#Zgvvu[,V/a6,4&&o@ݽs{p45uЁ b033&u B~=a訜KQ4qoNt1  HTpwY #ȃ4M=~ pW^Q|aIO~-88%iZ!@$P)0$lC<H?#==%%YvY9ka~:s 81yU&zH;-mշk0"JAE"aoW՗_~{܁7 pp:3J.C xy l<~a^XeY@ʤiZ {ON&iN>{y6l0^ DχM?yD"1!F`ֶj7AXa/+D:hq·NpbH z1LHqCa"AXD~_*|$ES"T˲b$!#I I)A4Ͱ,MӘqe}^Á ay<^E)߅/aY8h &A4M_Pj~<z}&z}0CHR0 aX00!1 I $I D44E,EQA)Y(^O$ByG IDAT%bGAj8NxܦBS!^WX~?Ib1.#OA0@$ q>EQ$0@DXDB&IH7$̽_Eǽ^T*D$iBPTϫ`1 3<2j2M%&ģ(rzzSh JNJ|iCqxtEb`4 $'(nkkNq}6=--%~k-a8s784L  Ƥ6eٮ۝:jt4E恁!JyucQE{QED$ŸH$+80 Av{u!PR a+\0CA!zvDbPq\$ "܈~=O ӛAiMӽ:"q1 e0 q\8iA6R27Zkb0as0XVpJ$'6~pFM+DW !k40=߸#ST^؟ZL-G_a tLO\3kƥ+fZ҆lOFDWD CBJSS3"XEp:8   \&S(AX7oi*222y )|q'xQ׮3SIE kDFo1Mӷkڹ8oM%eO.(*)*)o.^zfϞ7 aV,?wN}C# f;pe%+/ib(۶y+i$'%Z\(?1ɞbB>22Z[߸a횕˗q!GG7oܰvʆ&֮^zʪV,_0nhhvVr0v7o˗*oٹ}ՊeccBNysrW,[xHԔA mv%IW,[*:fxd46&z%11]==~n{\.۲iÒy89Y͍V( f͘1o-3:8=@LwOADE8e$9S*)ں2~t|b[6y<^B,aX&nECC2<{L A#S<ϋbBBx}>@PGGGFDh/\ 0G^W&N/ݽ$IpJ$n8B.w:]O+t9+/ \}{v^z]L&"$I'x{VaC u!Jo_f3!kDB#aE?oj<Mb|<զ }?X.-_tv%՗J`+@_8KINJ|D"qlrRn'$/Z`DԔ¥K/Q)nf)EG4" .k !n jqve2YF3e6 (BH%].jS*J$Io46AA|cU24!>."<<2"ti q>O*jjI0@/ W|5JKI1jf8f+ bpz[6vp Hhh0< ~`h,)-8DE=ZA ֜p)(Hr֯Ys>XXHHJrrww횺a\& v-7W&~QW_~N,;s(_p_EQty=NQ%c|bbppx̬MtC')s.eH=𡃹s>:z_F"DEF~pl<(:1a)yQQMGf9ݽR4"\y'OKTnYV1kf\._i`G'˱JŠί8r~q\ðpx\v{PLS<8) w\]BdH$(evfhL$R(B!Ą9rIT(DZngYVPR.:kq{$T*ya()$aX#!AA$>ca  C'@֗|sKbc| ttv}ҷ#T/޾޾~>뫮u݃C[ ˲5BOfyr /$IVV~Ūyr_QUد8,||x'ݸpI7X,GjkrƯ~; @$66b=}f}<ΰN#y<= 92:}smC#W2+wOVɭ9]&#IqI Ï}v\.'~,2|dQiٓTQqpmw8/^ﱏ||yf٬]n:KF2(l]YзjT*D"-:n߲y </[ŸxjꙎڟJ% M =KRu¥VU4͞>o?o֯i2wn2-[P\R׿n trgatt`pv2Mӕ5mmw/Λ;;oTtA${'EJzEn5r8/>o}o۬78űnBu{  :Qd[v,̝{Ϝ qΜ{9:h[}骚y**.eflS~`B\U+0 ӗ$ Ao޾s-O:A=Vm`pȠذn Mӧ^6 `2MtE$nݼ)y/]ӖV,vuܺswߞ_.[0g+ +k֯]}JkŃؼ('[!WCCفVNLLHKM=/JܼqH$ܿwT"k4 Qy4<<&'Fp~sffCk7n >>}݂I:9)Q.7޹굛cd{K+<oEEUrR⎼/AE##]^;橬y)ɉ G8TW];x8%11p}{QijiyI,>_wOߑ'644nu6 ݼum[7#t:_PCÒy-+n~Q]uWg}{v%'&^?7[O9݃ HzZ]|EE%ZfU1ix[GG^ѳ-== M$I߻У=]Q]ÕytԴԔC {vT]]=3v/8}Btq׎ > Ʀwkod'F\|>W;C_zh]}é3+*<5u/}nm"IansJ+ڮ D=ԓ[7EGccbs=D`8-5eϮor5p >XgWH(LJJܑ('[>qhsKj6F۳¥4M?yf qq\)($H4>.vNZjżZ( ꚺkV76(W魩g'MSM_N=a0e1 3eZx ˲$Id+W̕B4k^AU55##kV|w [;֬^YYU"HAbcEBaZ?`Z# zbYs8, `2#"J(i 6`H$r{<,1 SRZnQ cF=ȽeIDGd㭷de&O0lN|[KK訨 ˖WV546 nW}aYa||c'&&'!XeXw]Hi{쳳N퓦|5VR$<\4&=-5BY3.g>wgχ?&TskfFY0+/T,d!bڊJ>:vr sX^Y=>>ha6 -%A`ݲI"GDX-)+oim6YYT޴~P(\n EWCE%O=qH] h\0 {dd>텸i)C>xnAEUuMs(r$O=g <ء6sy}l6sg$b~Ca._͟eYñĄxiJ D"SgAAA q>[tݲ Hr*4Dd4Fx-֮G8Z  @{G~߾ʲpNMGCSE9˗--,.\m E-(ڱmkrRƒQ]SwNVrpn"gE` *4ruBd4ge?K\ >qXսkgW2&M&A?W_ S''Mv6SW(*-%eťe<9].XF).)]livHQdQq_߻֟x/ppߞP884<::66>NQ\.?rд  Atn*% mڸ~پ{3ryRGh"I4{vdڴMZjy|C>lH%QFxnpܼ}7/wKzZ_ F"666FdIiy{-a Fab019908DQTMmC*DyߣP3Y,( έ hni=|p/}'[[%а1"# ܯb^v)<,|ꕡ!ժδԔ(ؘIFE%D=6>KtLtAQ2H d;&:f(#78!0&GD?͇I*8 @__s V|2VwO[wHLMM^z՟}vzD"%@EUͯ~q9Ĥk.+Ҡݲitl܅(]*9)qf~-ϲԔs)+/nΞ=w63jr%e%eO87DYyeEU5߿w\&qNGg\.;oP(ffN9t:23-]b6O;q a.|Kkeٱū^ju c7oz]ol74 '9~oh˗.ʜWX\ry %PN}[=j$'YZEcSsyE_xY}%YdzOnj5a;o#܅KKrb5IEQUXUSGdzZ$HINbI$-L[w BCO: ydV IDAT)S#((|>oǵW=e )]ngWT |tN> h{ցA`'8vm&_Y|7o\pILO[, 8>Q ɂ'VPURVbْw ~3=w ֭YxʪjAXS.[7CRbKW֮Y%}tXgy٩~h4G9ҕ7F#QO^8qlsKof||yaphgH%|A&PV^I3+/p STq+/c}|z QBeAA0 AM-YڬZnHMNpޮy*UhqIYGgwgWO=}+r2xv!YƄ@rR{=0x˜tDxq>bl|btl,&:+v_D,;u|,y_=SSfVxA?2:6:>MS}t+qh\|`0&:f9v+@魪ٶe3w'D|P MMaj5Hއ eمQc7:6~jܭ"A`h/p.šiM{(ʲ,0$I((M,hZER\3AP2w<~Em˲}E%e;"AME!ð ÐaXLLTbBIgޙ[XTay"1 h  CcI S?=w-(0(ERF* PؘDe"44sO\ɿMJ? @/Woߖ ukW= pLt;q1ܶurۃӧϞ_liLtNj55 FriQ,a622eT(vfijijnmu8C.MLLΥttt\ѱ /oٴA B&`'&&CI4-0:BeAA :'BBjjhxtHCGG힚2叕]bUU}$BdώT1ӣ z}GW0G266N$8B4R}veل`ܮ/<EJ @s|2dnokhni93cg2wJ$I E-^ZmlۑCxXLVoMKMNIJ*)-߽sX$ ;U_ LO[ߺuƨH|q w`ږE$BC/^Մ]jah:=-exxxG^NIL+ۻ{TXeb@Iifꮩ_ hSPrv|>"MywTYUڳkX,6#^ \L&Μ`~xX6sƭMDE=EUMk7;"Qd!M];AR셋I rԳ77[M*J.# WߨERnaqUM-A0x.!.nYu݉ ߘx(|da||(ʗJ O7zzGXQii` e]=U:33L`߽1Ojcm}SQ%e \ 0m_p y5bHkhrں7nU~zzwf|bص7_vfIY9I\Bo{]?A|c# @0O>v䉃$Iz}>\D"6˧-l݊ a Ĥ APe\Qg;ex!JAk "=ła*4Y+$Efv;˛$ɳ/87Rm.H E{^X t:dMWtZ a3v{hH'@pl|AR)ES|xlߞ]B@'T!&0aj˲Zm8 4MR~9,޼}g\gFDʲmf$)Yefz} p7WT$)}>߅es}>ڑ{mMxؤijߞ!J';q\LL|'Nج`Ϯ.\6@=}m81>^*T - i>:r U5=}۷mMNڋ]οp8|^߼t^[fբs.eA?08Jʿq+Bmoi˦ 븭0>Ft45iK%ޞ>}`?r2l׬Z;k5C $lxWkV^0!`>B\& iB|yo5ΞJL:oIQ7nQB]N o9h>~F~ݚif )Kťe$EQ4}nI,HhdQFcLtTlL̒ Iڷ{W,!8OL @O}8-mii5B@*-,R;̚7/#-eb!( }C ;y afHbe]5EQ;2,_hoҗ,˲0 P 0 aX;_}^QZA0x@`708q CBBD"Qhh> ,^ӟxtl45EQC#o-pC9A(b @SsKyeLI(4M4M|=O<>aq`rL=a2W!(ep׸VDBa{GEQ~Chkk; Af4p߽&$'%wHۺenB!xmAC0%eh>/cc gj/\iú`b`hR,i5졩9:ʸt"6384Lx[$eT*|O$zz%b9A$ŢyYo.˲kV*Zo->kP ''Y ep&,ĄDT\GN!Znͥ+' q_TŋaXfŲU<$IL\~}ahF A𜧁qID!ea($IQ40 3 a(; 0a$aqeY1 HBl}CT\eN60 i$IE9 W $1 h"!$H ܓr[ife,OpCO,p]q&DP bhEQaHDwI,;CEP% R|E$ 0;37,$qA A(0,,{O,R) 0A8}1$ fi$ME_8ʲ,A8q92 ('?IQ00L$W/mqh.P,$LАZ:fĹ3AvNMM( q~$%{'&0KOK==}*U(ې 4Pp8-mܗ'lniq<5%s5|~aaz{&&b44M7:`Ž vlk[@OMI0ll+i)ɏ:aFGdžFF<^zZ*Eyvvvɉ!!\y R,:c6O,HIN |]Yl?::?22 c2M%$ćU߶4  ### o[put|$ @Zef`^b-%`qKEQEq8q^̵Z(5.a&qGQ{MBP( y<A0 s~#P8& R( \,͍8*74vs  < 0T yO]ប;F?58ð@fx8y_Np 0 EQA[k:+N?s:F<35+uϝbx8~ A 9{6[A|x8t\8G& 8i9`O_zC91f{Qlnx'&A[w {+!?yz._ɝŠ*T~MISSIjumzr;~ [wKWy>}zgNe/^7]E%eړVTt}nΜXuu E>~r()+okp\ϝi1 cY`ΞH3̅WGG2 C +Gƺ{n-y'Nf/ LLL~|:vs8"_2B! 岒2߯U:~l1 (1!>#=-#=͠Y")!0 V._dBoHݹ}ۂM7n-<{byEUiyecSswOU+,^4=mXm<>E2kYdq|mXN 7_j@|\dUMmWwOJrҢٲᑚkV-YKs5?26~ݚիV78].߿bU+UbQN>"imkd֭^^MX,NOK KOK%~X_h<^H$4E|>ðAR)$5uO:sY s<K[7y<ðX$r(*s8WO,(9kN|qy<^Hy!ڷg!"z<^"D,v8)ofMm]FZjq{<7nop=\EE}~(4338>>~j~zZ}v>k_x!a.]W@zc'~=@|߶8mfFWmCS%M  c?  *Sf3IQAb8 xdeY& !@heY4u5Vh5LtH2+ .?3cl U*-+0NS"()3A4MJgo:\$z}^ob~qL(/xt%eX+TP 6ix<Xx|7oݡiq䩳U5jJ!ٸ~۸~-9 yepRRb{gWqiٲ%a"AieEAR|#+ ryCV|xCx`Zjʱxm1$<}VGDB<| +s;}l(z[og}SWA:s'쳳ׯj45z}dznf:!J[6m8v@ HJL_79o^D,#~C˹{v*xW H3=8i߶4QQƊ7z'>7EJrbGgxK,N='Hg}@\RG~C P!Mnaryx<B!0^$H.Kx{^A`@A}vQaP($Ib&xUyXu{-q4v{ /$I3 ҹa~2=B!>_ Sx ;Qx ˲niEEB! ^/A\v3 # Qyx<^$ \XE`[ ${O~B m²,/k_Ʋϧ{ifq1L߶ECe*U(w(€ @(X߶8=?3cq\Ynz,Vkum]wOon94<{^x IDATJJNgOo߷-_ ME%Gndn,p8<=6m#czir  @/A|>?,LxS ,e/_}t8㩬 Olljxk~߿-,~G/>j5 =B e]Ga>IGg10`o{ѝW:jeֶ /0/wt<p\گX=q \?jzׯp˲lA_ޢRnk7n=7 ӖGi/\o_^ZVT+C>zfj|~|brhxxCC=}i)Wݘu8/vfwowɢ}VI^8nJ%۶J$ֶƦXd2Mi4\6޾Enᄁ 3&:jݚUE_91iZhaRb@Sg>vúttt Mo4_nl#c׋D"$ K*4'+z罎#^tebݑkd۶n0k p:O)zG^n6311;c.[:߾m%ƛ2EitHkk47mpߏ @$jhj^@?0;;;::p8##~8I )2wօ?U MfOvXjz}&.|\lwOؘ@V3 s֧\|+ Ϗ)(?w&&M\4tvu$QYUdں];_οFSglڐtRDO_122~?7<22<23O-[|;y<ԔEŜ}~?!H[ھiz}knnq]];;}`۷mGDW^~5uGKKM޿w\W(x۶^vt646ط'=-s^ɿѱdfݽ;~b]֭2F#_}ֶvӔGmim mmBs7Xm39-,HKMKM9t`@ ؿwF^USx^~Xt9rCnmkg}n 9/z##\ w vRu֝}v4El%|xDumAI -(*!nAQ͹[7 zV+/s.>xj{{g>24::WZQ^Q9/#Kpڍɩ s^o_gX(e0,<,ln7MM-^$`Ye8!>r(f?ye$ 9Nh4[{ؘeK?ef$AnadeTTZVaX{W-.)mnmڞ+`Y~rr򃏎 OB,0,E8=ễEntRx<̌\.פ/R]rtsy8LokxaNDDX,2O[N9r>ġg_Po$<5enlnXn7Wr~V&oD,fhAe1dYnbbrl@JR" m[7K$bV2¢ήHedeBd62AET0333 W욱)(:q쓇pwH_0?S(xޏt9]c8?ġ 6r-m6[wO{Et,Ϝ" 99 ABA$eKͷ> ZPTb+I(*Jgdq<&: P][WYUc22Y@Qb cb?WQHTP\Ð  =X:/~_?G`tLSSS.YT^Y-0LTiYEvHDCc۴pҺ~ƋAА@ HMI޼qU <+Zfbd5j>?:ڸnͪ>zm33\eK9(*ڶuDQTk[O^>Hx>rV[QqMRSw!F" ijdt[\iM3e*RH$ܿwWqIYAQѡ{'ڹkú?}/BOO_AdZ㙘q,k6On,J){vmv8|`ll\T&$?=tJJ_~@ p$-5p-@:mk{jili}4=Vs, D QYƇGF3R "X& J%5 qqMw ͛T!s|b22 ZNrL  qb)+3#$$D!EE c4FU6&:*&:5XVyAdյi}ĴZиi.66p@e4rZw- @LtTLTTM]}KKR<? &TLtZBQ$8X0A3#r;Sfj5E=1ƨH NhԪPR)HMxxUumRbNU(E "1!UPmfe%e75%9&:v~ffJrAQPTVjJ,l3^/&:::ðA!FFxݘs37/^[n΃yؘ߿L& f33/mkKqtA$x'%v_/CQ[\y<$X9+ʓ9zϞ0,,4s!bSK+~"pph?{WlL4d61 R-Y2'e֬^ I܅N¾`=;b0h4;mx5Jhᘫm]CA%6n/(lhl{V&sj˵timW^s8/|}Zk6uDδaˏںJӵ8#}QŽή+l=}vphO|jv7?35=Δ:xdJQQU]S[GdB|dr)IѹgYVPeSdXXT=)y˃5S̋/^-vhyTnLť}FEE2ܝ ($Y{WKr> H$=} HRH+W8]. 㯽k%RDutvg_H*%)!!>艓kW7]a!M--SӺ@Z܅bb֮Yu*77)12##/_LMI.ZR*N}gYֶl􄸸E G+8tݻJeIiyrR~ګEwvFGEY#/Hh/,\vW׮YzH(khL }Ϟ H5⣁f/HK j]8c_]rBpxxoߛw_AwOoa嗞߷l"??UiY=lX&Hohz՗cB?%9_7'y;XQ]O/^x= \, c|'OEdOoOvn?w!v]Z|Y\l4e44MGD:V)"h|JW"M&?9bxwZc'-L̴wt^V۷m4l jG #ѐ`6#ZUF痔}uDFlk[{Iiomm&xFf:v`0Fl6gzzQ*#ITs8:33šOD1ŋ/ήڭٛ +/=O;?5ma~5"b"۬}ͪmSSNL[f3 PW{\nws~*U|lؘ &ai];2GP6JEQ}3)mHaM4 $Ib!q1ccF$)rlFpgw婩ֶ_yX, pA6R3zfHٞ޾+W$'287掼o_l6;!.$fs ]^XE& GasЙ#I*R* ^yihx̹ .+%9Q*]^xyr:w>oStjjzzbZΝS;!*"F$B޺e'ۍ=vX@gjzzhdijM`0'דܐ?\QUq?11QUS;:fiZ,냪k{侾8WUJeW N[,=}Ldg*՟*mm-mZmP(ah`p6&qy>0MbH.;D mP~~KWwNoik6<Ώ-ccFHD,adWU*Ո0=mAmSF56,G!s\&J9o[ŋǔ}+v{WOO[[GhH m[M)I)IBĄ8arA!4EyyeU6lcs3 q .-_8&: @3UU[O__CcSZj .RX,ڸa+0?J"OLLή[7+ʪy|\.` i T*ի$@ 8w!/%9)%)>CYB<\-7r PqgRڱmP( /s؜YD,=!1!>-%_؊lܰ.$Xv{kh,(p8wl"._Adk&D,H^ȋIOKUGkW3v}/^xp(udߋ?vcq;V(fxłmٛ  `GGǸ)!̹2EYMfX,S0 3aR*j6oT)#yp*=]few:$I=&'bd6Sp8T*DlF7EӓSSS>>2Tj2$!6{?xA IDATu/5m$HOQPxiQB6#CD¤1B`!ȘѤ:NbJőc'b"##"00/A{#FOQE3mƌܳeo0"KE-]M&R*qY\#s81[bSbXk6S4Xd`0̳MhlLLA%__DE5 ƌ& ( nl6b'Rwŋnvggg#"{{JJ;:/]Bԥ+W axxdQT=~jrrR +JB0/֮Y5f456˻,__(iu6bEU5cF_ Uԩժ)fGdx000$ R)Y(cbb/ʁ!'+Wժ }}<O! t W]*#Sʍ yK/ol]rYtG$)sϞW*&RJiji-+HS*._kojUU8t 0?8$ v{`>-5#AW[+17@mOOaF,MƦKt(:~k4fxsK۹ yI o8>88T[_fs__Zڛ[Z/_o_nnQ͚իmAODK[ϟlq1х7gmiλX8caum]b|?46&fOḆVE,;r"HT -m-XX^Q/ZRQY`Δ/Yfhfgg[Zϝϋ?fg&⫋-<~2`0 oܰ셼.T"nnn5wtnٜ@`G608JJLp]ߢ(+ήG&;ZZB[*Nj/OwN4z5ưE̽G&>Hׇ/H#ݻRSH ѱ!x\aZ|>Stł^}f_+rWK>2َm[.^W[:+sLTjRCyE}6;[Xts  Aڀ˖:];-]AP(r}s-I2Ca@`{Gu>55MX,Ԕ^S $qbB(2<,>6BMuKHG&ݶ5{維=HeFfx5ο9x|>9 м9gtԔ0Ax<Ԕhj׎m[7Y,ܳOoE6xS;7>MYr;,[%q,Rz;{zLf3Ab ŋ/OXYՒz޾Yr$i4 s{&SA4A| r J"x\\]bsz^~a>H0?!.?y(0DRp8.^ha2550 F@.}<.#0 S$yC%>EQ'N\NDāCy0b=MF/].S)D"J9oA$jH|HF\.09+S"}y$)!'Jv;arOOOOx\X{ f[gl]~<(ǻq@0ay r9h/\r8 _Vvc)I*7baVWΛ_Qx2#0\f٘"&ž\nDD{~#Qlmk;4 OwWJJD"5Ϥ,L_@$MKeġ(#KNJOj.=s6 00Ï?IKM2t:DaäIdD#&q8l9>84{, #4ModZO", 5u04ߘ6;9)?b8 0I Ms;bE ? ]%449O }z˓SL;:ݜb646$fĄxٍK]]IaA /amcFc6 2"PS[?33 4=f4FSk{; I RPggWpnWV,_SpTW" &'BAJR")HQTOo_o_xXhH$y<)!>7)r9 RS8ufa!i,uhJJSrcXrbV#FLt:[-[rڟvh|\(Ez~H?X3 MNK0n)ʪ"!Fxú?`1kaFa_AaF0w=gy@=<=/W{00 :W8?P p8ǎbLO[> eI$#GFGiPwOEeb.WV5rG;.|9ؾ>t:acO* ɜϾ88MSSE=719i?w#N^QYd0=sN>{0:\YY#m EFrߑC~A:s1aR.=tb-.OQx2ah.$O~}z||B `!_dR. CQV@'ǥ$/^ܑeKDE2Pvn7D˗.I_zʼV/7u445RvXXd;jVZjvD"ޱ5#(a "##.x$t]_| -o1lŖB!%;mٰno˦,zf$+  u:>W#)DߊיrϜ\p8`ltP [`! q8l s (*vgp\N!:&?p\wmﯪ;b\n,̰l_:||^B\bά[J,<<{,(:+_0r9ǵXŢc/54P*=l ,/^<1fg8-LOs\ ?{gvT|iR)Gn V&'i&) ! }׎m>_TIMeGXliXh(sgbb$IpB<>=m L@uffhRt9r $$RJ3p:M27^_j嵒RXD"\rUJh"InEBBa0=?}Hٜ}>ݥeO%J4yW*&$Yj߰T"չ5jMQ$/~CE&w/^xy<9ձʪꤤ޾=j>?pðx^Ï`!'ĕkρZmTdvj4-mClژf/Cb^wsvwb7пc@`V$n):{MV.lE Y,VrR>r=ktyEK*j#"u_>ZS[/˕ 90 cNsn޴_WUSG"bӻ)&Oi/c9qqL~DŽsFnjN3--E(>3S;J\c8mFQ!!U~`SVέn||~jrWGBab|Z(V/^xpsf@x< p f{^H4,i$qg1 ca8f0bDAD46th3Fk ǯrcc8 k$#$I !p̎ A e IQUso0]S0$I8Pt~Go~?0 IwEa3tO"4MϽ$|"X(JSAF6D,㵇}EXŋ$G. <7 ̓Ls Y]x7='z, }҇ x37E&eӻ 7mqj0Oɹ72ݓA.y8K,i.45u,+JևR!p8h]^xLGG$K9uRulzriZgTR?N**X멡ippoFG۠q/^xybEr2'^RZ]74Z,ߪ?s|]}~8~kjgo<|4}O??p /}׻]#GsWv6mh.,]mNgYyP( Sz:>o-,]FE],,[dNyows"IQeUnvi}:M M@Q/[Z`~ML37=wEBr .~.GCS۴wvn=}/wo{TT^x-&۬ '1֭Y?՝wYy̦ a-^ȱ۷e0?WBy|>-b-%)5"#0޸a}Ggԓ9Nslr>rbc׮^?p=} oLtT^~AX}eX,#>795 HѥݻvLO[.dnX{2mIH(侾,pdge;333Ì"3֬&ɯaޱmUoCk[{V[^QdɢCn7v˯^zaoAֶvoJJ76$'fպ)sCwo_m]}B|lbB<``p{ՊeE]WS)/8c[j3!zW546/H](FI?PSs/PRVdQK4M[,VÆGtY8kh|}d>~uؘ1{/:Fm]cF?rsF ǎ8Ey 34۶l&Ibntv}t} Kt}CcYyO']r_h̍1k\.rut) =d0 ۔lbpdeeuhhȦ.tvu+Ąlji}=} M@mUy<44drrrt̨T*6DBKJ_~qðϾ8gs>m6oۚmZ;"p[f=}njzz||0c>rǰ۲{z&&&'vqq8cnܶe%[j 1gggGc;:ڭٛfl6 x_olZ@wl10ށck}ۖ&]``BpJE}C#"^J {3֡i2Bׯ=wWGrECcᨩ.t3kSg.QuĩukV/Y{\dxX^~R][]=!}{G`?002l1.HK~qs-m"];\[\4=08aD"3B~ ӻ[Zv{wO/ ._\}[v.0P[o򸜆=NJeX˖.޹}k^~쬽gv$dN'܂ HZJ3w^*vUwذamhHpHVskp[VUM9ccw<=Ywvwt",d/\,o/Xt:2/M-W>{\{o8N\,+/l3xϾ8Xx)YPxϿ(- (.&g.)-qՒMfo i4Gm]aXUMm_߀byFF . 1 IDAT7^(zՊCq\1QQo.9k%Fi`xgy+ׄBO6 qy{v z.O_{񉉉ɩnBfՊK;o+-(`U/^xyREQԵR>OeKh H2i˖,aNA$1!P|jCS+ ={vxhcWx.?~OĄ+WK CV%69)~ APUuMIYThh})fi`pp[{V64]QUe2rpphd(G$?yEA-@|l,͞a5fPN9ctBPR[fUNqM&].}}'sr%qLKylw\(EQtjrP HA /[rgر} 3ke55ucF L*umٛDBQƟ輋]ݽ}AA4M&'A4>>qM  ~}BՎ }~TyE%̣F$(vl]:t/cZ0#=8Ot\C#l6՗_(t7.?!a:] M)LDX c( kO76_:c}1ё,u\Il6;$$d24HNJd!d.Fb88(!B!J`r=/^x cVwOo^7˿_lmvh4fg!32jj|jf"+j6nX1W --[4.--%@ y zT1^F&ILLXb{lټؘ+W C#7WW  uhhp8j+7\i2!(~ɢ))Is!A5uY뙂'ZZz}{74I$֮a dgf* p` ޸a@ +gL 7t#`{7A88na(sZa߸a][{Snpp(Hܞ/{.@@7e.ft . exYTVV~m\l4 a!eUCC#87n _5uѾ\- tuqd!S5e{}Z(Osk[ ~O{zu c:.n`8#!7C$q1j\/q!({ƑѱY@ QQ> {6Guas8' I  @΂ ylCdyEիb V1f4vtv{Tr1Ql[/^xyAyU=A-c%dRAT*IZ"4l6ZRVn7mܠP*Ny<B_W[ hBaDxX,nniMKMV)z_YUm߸aB!g!HUM-b((Hg0,H|>SAరֶXR*aiim۱m Q*}}  546Lvljzz*P`DD>HZܪ P T*tM}Cc\LV੡!H"p6T(fl+/q<1>.$$iAjrLtTP_QJP 8GhP`P(AAA@>ԥxWHjko(*:*rz  ^V:08zr]ǧv\bh<.WEQ@F!j8A5uMͭmޘ)Hp3._!"<\ Pj??PP{{FcJrRxXJ؜J@anj޾A@v:bhrjq, IM_*3dbB@ 4E$'huM-\dmDwuwl1Qn DGuյ--mi:]`iyELtNXRZf4Z ObReTJHDDKk[]]#q8Ε˗vvuk4 I,KKkۢ ]xxX]}CXhVT*ñƦ@D,]C, @r\6@ HH>"< ol8gcdMj/^xykw)e-_$=-r5em۲~v5Jh< A];UU^*FQtг[ZQ}u@eUuEU\p֫J]n/'N厎Jӻv\#Nl!Y~l)kj+Ni˖.nikrp\.DN0*ٽƎ?1co{tb|||\ A-\eƫJ+tZE7?tO_Ng+-ɎiW4{,T[gRvEGE O]ȫyXQg_ر-9v<-%+c6].VzZjzZfg"y}8رmK<=99Ut8$X_^Q_\tHXhH|\!'L Y"gjz~WG757l*򕗞 Q>V=:6zKg;>P '~78Uv}}|^~?xxx>EOo_M]+/=/\nwSsKj4Ҳ ͟8BYj?Ȉ#?<9RIZJ= $&ݏ{/^LƋ['lplmO D˖-qUO(j|b' pRbv_,rcՒ+WFX,ZGdwooTdѤ ԲQTONL͕ۍ/1 2p4 LKMf,#OLN*r+brOhl Q}3Y4j]86?޸r$)!A bZ].?P1d𰾾]>|⌅33ʪwQk[y|<@㏢hF3:f4Al6[&Xg.sʗ_[nͶutVU׼G-m E@mؘ>Hf>>2^/3}1QRb1rN9tHGgWxxlI.3ov&\TUˏ ޕ1~ NSo^FU+ukVMLN9vbf7m@`rN}}:P8'̓fmXwa??Sv4>wvvNӴFQ6gX!(aEYpp> Sz Mm%O܎XERr؜YpZZBtyrմd&0E\Ad/pحQ:mλXdQ0Ӎa'G[KDB!/O}}v/HMy=< ׌{Xŋ/ߋ)Ѳ1cEBoSg"<,O|p@0ؼ)L -v<|D!/HM!B ,|%!!vdİpA\v}rjZ$X,F+J4jH4aںЩihcsKZJ OOKoh (-Er__.T't8544qHٝ۷ .lhltEѮ޺fo1axxD"  G#ήn6@T|d6wv^r746'&徏#cx CbAG::tZdZt A55uD}~PGgR r<.቉I dRT*}1'oo]Ë/^4r.IM!]N'X,^n/ZԜ0iCCCn!ĢDPH|\y|` hhhr:]흝(ȕk˖, #t]+)ohH_ 0A-ö-S|!zRyZXXȢr_߹'&&|dؘĶ,PW4)s{jl,u .{hx#%)1$X_S[GGӕWH%kW1z ٛApPPIYߘ^*ȤK㓓~*UZ^a}>C8ZZۮ8%{ ֗Up8MYbGSt8>.&5%OtcXIY5+Gi(D\ZV.|}}kjZifXEW;b0LӀo>OENzWX^xy$ZsfYac:=x.ERgXУ ],Qa~n!y܁zo'0}n>^xa%, A7&Rb>]h V~e` @Ç Qgj@P(ρs' EXC6Y2LO^<&&`$YҥR``%7QP 006B0J3S8], .AN]LF"qTYf7P :1hbҥD7KFx"5QD2 H+)䏂şH8PҐ.E-߁;RD_<-PPLOYC_QT4Vf a0?3,AǠ/0< ɂ% ,bha5},E1 rD`@#Xc> <%*8i],Β0N#o &:+ S"8"8E "ll#;mFP&,JtƏG1dJ@q8ȁ )b  $as5nǧԿ$$d>~Q@_㛼¼t"*F+)Njⴃw2'G. ʓ"FR Bxv˽`Z}B[ҥH+F,${?/TPHȮqHM\E$,'B i(l T{]1Kcd$"ȼlcRi5(LM!9 @pDps c( SS#6nes6Q`Pq!LDOn%b^(ޠ0ou~bu -QB(T&qVu%HIEm>A+-$],n .Ȥ :3|1Y!~˳ȳe ~A#rh< Ipbi.US$E! Og$PobȖ2>ĨP VA cHBd1DKg\[0Qs1iDS~||6{EdbI1d U? \sݗy* o p)(H,vǐ$*v+%CthWkc|Tdo@B2I NnrDYr10A ;7](EHHcCU2Ҋ9*R'Tɏ{bD _ԈP U&#5:8CԄ#Xu0X': #D@P7|?^͡"z_$e<'Gt)6|tGq :wY#)@N: .;Ӻ'ub" PL#yyɗTe?Cu7)eh(ύ aDcQ!C C1 N&ku1 )Rjd akHhBIbDps+i4QKώD v0-=u_1]k\85eZ&aG(A?ZD01JQcQ!286oOm~&W]B@ 81it@m0VQ$52<()) 3< Y5$1X)/C@L4: @D8!(hP(&`Now dHtPU+4ǭ69 c(XM0 z9 IDAT8 1r R$}s|QPPp׆l0~aZ}B+H'6i݊7wbo D n9C9(@I@;C@!, MёW]< #y)oFX+i@:i·!@psnۘnw Q"j =$b4zu @+F/)s )`Tx0+łb t "` A|Edǧ킀ow  ! Z"āxoB%'?#׺%"=cq;̥8>b @WТL2gmQ`oOA*ഃL&ML2Y2UL6v pb?6/ 1{_`x0x 2bonmOXGnp= 'J{[r yolnz܊?jA.,#|ںHͱl:0ͱ]|y Xb>yq8?~_H3bxTJZڸy+喖/x>xFgw;441z}]}|- N:wVeH$ݹg`<g1'`wc<6 fqמ}ccwãcc;P984|%6n٦MNohlnytL} Ͽyx(m$nMM[{ώRtێ]D@ON+5EG]{x߹go0*.*i1:kϾVkKkە+W&]nVQ34lb:|V޸ܞoH$93EQΜIKM--)NOKe%Ņm&LC0 >8& 0a~0SVZ2uʤ\K4`6FGgN$iM\UY`c'O|7ljg"HMI7gVA~BD" M̪D"PHѬyfT*>uJfF MgeΞ5cֶv(+-6:oh--)./+&' )|`2&og [?}bwd,).ß[ 斶?*?+W,?wOͱ(;+;q4Zmrrr$9k| ˗-KIۈbmjdҨշˤ;+S'O~8.ܚg$inN0|lh4q`0FWHOvw3BĜY3^ذy)˖ߴʊ2zf ۢ贩Sry~^{eu!7(rⅱhlmͭmIYYǭ&bRs~{XTR0vtv(JNߨPȫ*gϜN:Z $H] Op;2Ic].cA!DB!$A .Niw sg-qiw^~;K]o߂ q泻C{*s[j<`AG|w ')­U퓬7SOJ |5m{#YWw//b/v/LIbE7އ+;^O.Oo2'hX=pKZ~ ?eB�&?xpwX10 MnK Aw`9~I$chhȱo5??V{XvwKo x˶P_2/ct`phBu#ˈD=}A 珟ҕi/4uɤRMԶD"z|̊;H,>[wZ069yמ}S&O:w\&#Ijɻ5sl޼mB.w/˩p*Mϭ^h.^\[W_TX0kM[K۳dтW(?y%, |aSjnp8޿+/۲}L*u8FO:}ڔ /_VxxN'WTJFy]oPVM>6co8:{g<ϯYIeKM-iiVϿfk}99 ;qt s:͙ wòɓ'NB1 oU*e,ۺcו+WǜIOĚgV]RFG,}ǜ{?E{z;!JWZqq\YӷMĘswo445:l۱pG"ϧ7g 5jPek׬V*_l $q'?[ /V`56̟;[LEyG#/ylFzZ[[6*?;:P$A!a$&%?bVqٔb{q\_ ϞFEE"CvVV^Vl4 ̌ts,fctggE"Z *WWV]px lLx 8=FQ$ERR Yq,~\[KWjz>C #)RSg o}N[ cYv#ywj.P]EQTGW1Ƣ1,/(f4y`* ؏?AQmo5C'7lZ]U <|l4@<:\.cYZfZs6-^ xBU%qN+/7kZVh4 ;qyi}g |F^/tZL\N!$jUݍc0?z{{._ZZ\=iqRfמ}3ME55꺼ܜSb9+Ţ쬬krxBHtڮ ցAZi;8u:8Ҫrsu73].Wp UPf? AӧMٷL&S)zBuR(.,q t[1,JmdO746͚1m`p5 AOJҤ45ÑήMe%Ϝ@}Cʔe&Vϟ;WMD/.]62rK,  bRhZoIw/B(Djnmye2L&ohv Ģ1xҕysf546egg>VX&l^J%232F:vIsh*`JZp[jí{CvցE wt]. Օ"8O`pphXTd26+.mnio~z"c9wN<:6zz^~a78B‚3.[Uj4@QWWU._X!Keo&H j]tB  Z̦fTZVZb12!; !cwt~U"t9fj4zכדYG4ð%Ņb8)))YLF@XD م]=$bS EffBH33bA@b6=x+x޼NGdV Yz=B Ɋ KҒb1wl(4Ehڐ%23{zl!;#=2Rtֶv͞^]Y1l! ";;K" !TKRԈQTX`2(]9JJccӦ Je2Byց@ |b"֙e,\(Jss,ցT>#<_RTd6oJ,p{ ö2>/))I7by#Rd4ƶtTT/l2wt&'irsl`X")B҂JHTVV:iB^6--l2jjBQVZBT0JKK$h@"wtv)):ZT(ZLPw>3sxضp,}~^@Ÿu}VBd$aP8<`j4MӴ(K/-)EcyjoʤIlZm2By9V<0b6vfGVRSB"R(u\Tb1&cSKа-KT;ކ9-*rv~ p?VDwY $HQ6PC 4Б_<4|V䇏 \tI~1 ^\lڲ⢥R֮Yh4e2 s ^䤭vϭYMsv{}IcSn^aڼu09O/=z}ɑc'o4$W^LMIٹ{T"믪TJpm޲-qE˖,mڲ]$MsIH$2m.i,h4hnn%":XEP7ߨVUJ6200dB;c'NzۡdΝh^}奣Oiu /\0?;qb6m޺ET(oN>7FAp&58t$??wϬxz꽟|d3ORZ\U*NX]Y^޴e믾<RUU;W4RZRpP(ӦL8uI6nپgNOh]WƢցLO^>q^GRS&M P !d0d~nLW.rQX"齿ݞa1$X$0x06/$9מ:s6^v}e@QTZj ]%Eѱ,SZǫP(O*<̚Qn4bw UJU PTӧMxi|<@Vl6mn7Idi`p(99YьfI,K%͵$i4P܅UrLQ+aHfFz(fY63#=K96bXX^BDӇ۴u] -N+33(}F1j;l5j<"7o1}j hlhj768]tzc9b5M<(#Z\TR))?ڱkGlhmk/ϋѿ|q{G)^"UN ?)RigkҿvͶmaY1 |jCG1,dтVӵyv&Œ9 +Kv.ܽ/7\R\4lob_6|БP08:6x-f3H%%ݕ ‚P(,`q<˰"H"b1A ÈVD0i xq7qSK5/>Mh,R4%y>D׊}dH҅OKNJИc6/鳓'N;fQH$HQOdڟDԼIIB  :rlhXi!o(.#GjH%s'K/HOJ"7_sŲ)b?S1A?l6L $ɒkuX,w>biln&'e2;XK,nG6a>^I3 c6{m#wXͱTVGQb1.aA~^cs+Aq/^ɤjڒp$wFiIu`:0XRTt:0aʊgϷw>MssKw|Y3DHb>z]Y.KV>`~k8A"BV#D[̦̌amphb66 ݞ'LJo GAV(kniɱ3vp{ggn%/7}`phJMMyJwuu7588.qcc(Z?+A 5׮wtN,/+5dg{޶˗O2,fSFSUY!E"ass-fp$޾~HT\T(^>{x2@D|*-5l6 J26ZaْYYqӍ")$`xfFCR\$av޻qO ++sܲi˶;?z\Z} Ÿ8~ $H݇BPgW_]{zrs9뷚i4jLoD9 EtA^Bhtxt޾~뤉zHOz{d8xA3O88F-iZJM7bS9׫鲳<ϷwtqIFC6mDȍƿ/;6o[oIJmĐo iiiY~Q*f)!r4hx cN'A::0x׬޷B.<ϟxiC9sN(av[Z\; A Sa P$)Jra>ٰ" Ɇw]ݱsޞ޾Kk\;~?9>?eNCGUz]reew1g0r{uvus1󏍍}qmdXkuǜ.6;N^;w n} TBAng׿oA0 ' Czѱo BܚoH|Cal2M&c_co|d*}!a1ƑHd붝yyӦNyr|~$H {ցm;v?xorAI-YMN\|ٲ%\ys/]|JMo_1:v::vG0˵ o4X{zm#m|JN,+)1,vòh׿sL x§͙jqaH ;F93{ٳoŘӵy[}b4tA҆m)):Hư6=X,j/]zIs+2a|S7l ~ѤfϚc; A  }sssy덗_|^"#B @Q4M$E!W!gXtJL5c9}UA\hSUnhl|#v(3 y‚|ZE-[KqrDߨgc }f~c2}3MmlnD DaHJJzuYzM[^9c'63  lٶ V!^~9g-7wU&nwpi{>dÕkOz}QT>xG7NXSQ^)9ICB(AS,M:?&rՊx?dה E[{{e2˖ cX/|<}rNA&NqܾO|"J$ ARt9nK"ZDDHpBhhM4E|H[#bE|L"JҨE"шݾ{>ss,,nشFMbd2  rQuUe-fhT%ERtbulشe)X)ATb1P]$H }`8toU b,SSs-ql4d̚9=3fu}r sey9w t\S˲7s-R@^oiiaut##srwX4:mVw|s:zd2'VW SYQ/ð3Ox<ދȤӧJwRNL:{LT.^BS9hvnx6m,g1,;}'* 𰭡IiStZm(vZdL&ijn)*,(+-yFIVQ#B{Ggk[GiIQqQVKx5WÑ~Տ[H$ A_Hذi+;~r`phʤ:^v'Ŷ{?q-]]G71X]uŮ. [}=~p8{O>z<$%%mݾK޾CG8yx;^oǮ=5Wwtw9{#ö13ĩ3P ҒP(|1׻i6}fFyY)Eu ͭe%lxǎ:v_ |&IRn۱;v?eDXl;;PQ^v޾ue%wq:ti9VQ^z)NNGl&'Wes/X,S&R5e>k^?u$!<Kv3__.A Ғ,kֆC'O7W):݄ꪒE 8u߸f9X RRtfL3r,65Ξ9ep8)If*fc;QTV<_Wcɢecc 33Y".^r4MqQaeyySsK@a~~A~^Fz`]}mni]ԼꪊaenjiSN EQԤ22yV`8|j}C#흝&TˤѱE 痗n %y9,`H0L[GG_̹1۳ ss;{* U*VQ34l3Ly&qsoZS&O1 &JDR)Ӳ |#YԼP(̞5aBՖ;߆BHNN}CZmE ̚͟;'9))^s.J\adR) H^r$ UUp3y6ɤI2A&H֮Y0X,BIHDxbUXJ$EbX4 "H"3 r7l֞1octjm-ad)F,+J)|B{?=sB~~DիVdewao^l˶%wRH/۷y,PW{d[rQo3C ?ag=ssS|YMLFh[q <;?_hjnyR* ,?~hZ{LINJp\P wL*z<[]Sd2\.AS(E}6W-fRV0aoqi_t:]gw8 ])AŊ ^}@HHpHHal VJHu:jes8\.d ƀ($<ޚW, P q\*  `p8Tۧu8TFR]hEU>_fb cf4JYmcD"0 ˈyzP*-.  +/{WRj?wgS*-VK[oJNv֖/$9m*UXtt?>A@ X=ca4u5O7|bٳ0ecSg$'mڼu9 v{ؘh>+I6mg_~5wάm;vhԌ Q?[raт㵟RW^|@u }V,EF_|5{BSwO@YT*ubv[w>쇙(׻k>ÉysBCo~v=ϛD=kjrT:{P(cRipYNa,.(\40 `|Z5s4Ew=𷿯 VOZMD꣏x܄'νdz5a|BݲmM;1@P~bۏ Џ?~~EǏ?O->Џ?~ϝm`G2-nn?QP9ȇК~Y?##.]y`,VU}8~x<ǿ: rWO7я?~~ܽ>2:?3&k|LqɚW_O/A%/Mh2::&d>(bE\._dѓ_@'ֻ\n.>={v9fܿd~x"hO I.?sfϼsgyXyeUeeD8Wߪ-qgM.dwimm Kn_j v{4K.{˗q (9fDoB¿hysf}1(UZ0o ۫Q0Ue?_C?~pxƸ}r7~k^vwYt=y<^bb< ɲ 1wL$>f1 [Y6uN4t֌i\.%..WfMwںؘήO7|ĢsΝۧըUS K02<2,,7V]uu ˝=kLg NK VzQù{~VBB_D`Ja~LtTEUueU5J[`X$|ZKKR!/,?vnOIIV2=rcSsƸc; :l]/|ɹ ܉Oh0PiTqH&.?`4jo 0~o/ܱeP(t*F>u@ rNHr/]" \+mni ϛ3Nokϝrt1Badтy0%aԉYA ;x [ZkRSƍmn>gXΞQϜ-BH$0|s 0?M$ɲͫV.g0) /\z}" H:oo߹'9)aLZ v/wβX,v^gX(~vyCCBJ_d2=)?0QΟ[tet85j^! ̟{+ RN.pV7~ݷO.f1S&0W=lǮHث-3;"Bsֶ`dά)Z~#unہ 'A Z;LOMN:U\xтgK`1V,]~¥ޮ7__UW HKK[Zj #M-+-hTF~WUaJI W666Y7__cXZڐ7^[ӧ ^^g%jڶn۱i8wdgefMӊN)1;+s'Oϟ;''{©Cu,|h_jX|MQX⅗._u<ϯ\9~ovr7|Y? S*mT:a˗(+G1wygN?]\b0;_[Rpp0NngٳψŢʪ[/~>Ltݝ]>Sͭښ%, ֔p``@ 74=)mv'o޺}LZ`DRmKW1̔Y3M {y*:<<љ +nV83̛E 6&Wvvu{ܞ1iG5g) o_vv=8Xpޜs:}Ssk^fٕU0~o=fH>O+@(hjZǨTH(r=]w (*2nLIFPGgWeu  j2LF NA@&CGXgY;rxdV]}V8$25% P1t:bUҷ I p_ HV=AB%,&b">V*klv{k[ۆM$5Wpw*fz)'nx|: v HCCbcnwH$L&3\nkkS>I3[B ר;g2)t544yyoa<wd$'lݾs7n#z㵮q ƊeKn'WJجY3S (6&A* 땆8.aϿ0$͊Br)rAD"wrR Zmmm6|FD~?~Lo9Q .#WV Jntuw7WT$%*ʄS %#'dAªᚮ/ DF'$,_<0hx=c\C'z7Wbg+ UJrqYp8la6[ZY fZZ蘴TKbpЈaP(0<ش'O0x&d<|dRؘhN"~˼K}k@* , `0>bdҲ.;*"RCCBMhАp+7 Y,P(oht=L&3{CGߩ' @p84QWbw (@,v:-=}(kWN.ȫS)%AAw^5^]8jrƷG~/4s3gEkFL&l5t:c6[Al684,Z0oxtb_qAv,yf~d1LJuukﻀ^r41!~" D~Ǐ?~~+FPQqh84y)Ij/)2"5}B0wbBahHHvPp:2h8ND1LHAq]=RiR!HTX, -m#DB\&c2,K&m:}L#]%-%EQyuJsK8tOR)JwbE˟qڻBh<_E/\nw88NشGaf͙5<<\rbuM`I]F`=D6V G :="B#?~`Y Nq|Uxqc=&֨Ug]a$5%dm۱\.UVU_q3%)Jȟq%/$>z|gϜ=5axdhdd4s>Nϝ=3&*N0 {od2bcn[THu0\tebɅ'O$<|qcǖ 9{pBtXщaR]_rYք PNV&t܉{|J1i)~,v]}CRb£P(!^V:.}&ge eSk]= vQVXvU4v? \zZiq;v+ΚZ:y5Vrl +W\+- H֚^+V._6hZjljX,k^Ze2iW»FX|>kԧϔ4F>]V:6-_ήn,v#d0GFm6; Ҳ?裂J?qyvS,9wajd) hАݞDӄ|Ҋ?nTWN74Θ>pTs.M.h&JaċFG4Ph\>q1ѾX&ؽ@Κش9YYcRV?(OQ_O?~nmhlZh>By酕^mDclX,.R(䃃W]>$AZJ!m/^^b)ǣ0(JV\&]t>z  p n[BCqgXr$ :NT*5*2bhxEQ nbãA)v'##?-IuU5˞]̠! "^XL qs=Ǹg/p8sgTUȈg-+913#A`# f{`cK~zp8gPeUq1Q_:7)L&J0 cnᰇG;seP:AQ_ZlڻE/B?~h9{|vV6`0^tKccܪ+MJL+sБ8NRb\^[[W^i[[Up8l6c:`JKMVz`;{QʵB$ɦ斲򊴴B69;{BbBoˏBDE :bZZc”!!iuAXm6NۧzӧNKR dRQQUӹ F" 41gQT|accbFMASĢYLˤ*eXCc:,,+s@ U7.}LB\CZm Ѩ&?)!W`L),`2%$D;17O= t}ƴ"ilnŅ_"ܺ56-51!Z4@?jh4iEl6;2"E(L.y5IIIBilj\|%"ZzH$ T*dI i)d2GY1Y~rWSBijg*bE . A'q_,r$aI#!AQnw#ǿr=J:sFںzX,~?^fyKWА Q(zi27~EĜ8j]1/wrN.hPq*qwdGdyEUɹu~T A74H\.Tq:? _ m۱VmMm]]}*,̷u GĿ(vuz{ (PKk[|\YDDxoobbd0b(קa`$Ad4iu!! .}ሏb(:*rEfX:;bJbptT*!jfw$8bvuw3̄.zw6PHX]=l+!>jYQrmbqTdBz-'>fcW'ϝY,a-m⍊r9]==8N8Ų-Dr1QM-A1~tvu+rl EѨȈ ã"lvWwAr9VۑKPJB,AQ1p'vk'db8%0q82:IĝoOH@m|?͖`Au-3I($ۧV!}Z-A#rdc9g)LI ##ё$YUS31'[ c2&b2iɄ ^$^4&5E j4`ObhQmmkGQ,:*Wj=~5/ad2J_>(00Ekt~B.S*n #=}b*OiT6578Enr?K}@KK$?4<{`v{_4 ?wxxX* H;w̤x5L&N0=U|]}1H͟;;2\sF٢&\na~Emn`4ҲA8K&M),s$%/^Mo \`ܩ\EE'h֧P0444c‚\ig;vp8v-۲23&m8}_mmHWWwwO8iSr'P(.+3g@41{qᑑ^]b^/=]|73_C6$M (M.ϛ4 a~5e0oy󵮮O7|! h4j508844tP5cڤ,'nO[[s'AGQ@>Wܭںg˛#>Ϗ{Dkn9@tLfh4:FQbX`6 DBL6$.{ҕ1cR}>Ӧ&}H9h 0tj &B=yxْgjjtBv S*y\.aR1 QӧTa_:=aR(pT b- ANJLhÏCؘerd$(HOW(d3MP:AI4*0 l =IP(weYmGX,8r|NjeX CNyD/_>oάJR>|!sS IDATTȟ_|=T*Ay؉U-/+P(:0 pr[,V$8~%t񆆇CCB eO2yv'?/VwoofƸgEk7nǵ:,_H>7b2ct:}F&aΐffIg DQL }\&1&5/KǻK>`?nm훢Zg5Fe$B:)IB.&dm| NK1{ŢӦr@pu xDtZiKkǣRlx\^jrRbb|鍛ko@PDf"39O72*JWVx)1!A ϛ;EEFeR较4mޜYA\/-!!gJ\:Aˡi_% bt:M,]|U^$4*uƴ; 9xY~xxu{zO.~^"Kyjuo"~E~Uя`LK0 , :yz2YNK #U5}ZR?tHAnXunlgWwm]=(x]=Uշ4j.rN*:h/W N?1'ׅs`piƴ nT]_~chhhRN\.CE'M~A7r9\6m6[\N&0;0 0 O4AշjI-\7ˇGΞr[}i8WTUeeר [S^d6_/-sqwtuC]c8JR(˗.yҚ> V=FjmjُD:so(~~PF"#""#vk2M&sQaAj-x\~ijnoht={z}.QSZj ;xf?pȘ&8yEAxCcشy$k;'d߳dX?[r@^O=4ewɷX6FYZjʞv! ;x' Ǔ1 Mf"333 ]..A*,lテ= ә!?`0 l9Y쪫ohhh'O;{BvVKWzz{ Ov)9YDe7S "b<XRi\w|W #燆znٓϽh˗.m4HMJQ(F:G;;}6 -3MUes;:IGINԙ/^Fѯ1i55\.wsnǓ5a|m]]~]Kk[fFFdD8QfKsKkA^nbB`/h4|t\|>O^QVk}[‚łmz IC].x3gK`6 0ZNIN רEBȿ?0Qk*Zչ7--maʰ1i)aaJ\`XlWX $8<?(OE%w?~O\n3S&DkW=ԙb_X/$ Yc0#Լ`Icc#"bcnG  _;k&{ A,T*W @^~C,ޮmÈoJ8Πӹ47C^*JR,K~rRb|l x}3(,Fbأ1qˑc':ժ0LP^ZlSzlBTEȹݞUfyQs5c'`a2&  #*z2 W q;zR* ,?~h0-7vشTR(p|f -A"JtՆaA{ۀ:q̳,HVZL*kPCTH$ݑe:EQqWv A#B&O.%tz<\&`@ Xw<Ap9ի˝XQY  t:#HNB\&q4 (sY8FLC8,1 4$ L(jYS0!!0@)ZZNA襋 4G O_T^^Y9uSgbP(gN?]|E5wL:V}n=! e,档ǣ"#O-R)~Fzo,Q Î;14iu~a2ayҊ<2nbyZ*eZoItHǏ0 _~"7111}Z\.R(IPB|NgEĻu)L}Vې$KvRihtTSpOHajEGEICC6&A(my _|}ƴDxn#vAbc$AAHŨk|,Fd x>5urNEEF  CC#)IA-X>&ؠq:mn;**B8I ꍲּbTUr2?Ϗ7dOxaJS2i4j60 ñ;aJOcXJqfvL櫩z{z"QHH0 #O6M쎉|W)T(,d6#r98rj0Lf-VkR}tukoRU<.d2w0}v\b$@,pさ 9Fz C& EdxxEF(Az3L&TjHTM$BQI bx544v{( 6GX HڴyKbBX, TeZ^V$TaJrVRR):}?BQH`2VM& x(ڧql\ b`pE1BA^bV*Ly;jE7}_ ٝ.^iV?s4\f0NWRAt~.c2eRP(qhhx$ɲں5/~$W^za ?>ۑc' FjJc0?vHTT444%9Iq8lN .;00@,v{Rfm6r<g[a4=0`|=}|P;w"ysTaŪ StWt X"tZ`2$ @e2$ :'Bnw8 ơ;3VkK[ҲȘX*x,6~`ƴ!!bcǮi[ظy jՊKvާwɢmݾbR?u6VE-]1jVV|˟L`dт7+ پk7 ~+*9 ^0kխں7yYL?A1hNx3O+Ͽ~䳫D"/W PͶjĴd?0Xv\$JCC 'k71iY~4"0k  D?^~?)<6-/iuvì#==} h1ӊ&XLǷn۱`ޜؘw͝=l000`ٳ)egP$ݯ>hljfLtVX6!3ke2YjU[abV4%kxmZ/_dХP>~磣Wز}la26㣿 CCb1y}fyeIßǥɺ :#}LZ\lLs'uN6UX~5)'w|Ǐ}Z_Xǝ'A.+++7_=Sgκ˝;bdtUS&-9OdgwA{{?ڳ|F:B9qRɚۧUwuߋz]nIhᑑo}0nf5_Zҟ 5W^dT*?{F͜6ubvv]]= 1>n֌6q5/x2oLJ]10hШy<Ja1ntqIfFzrrr'o% {z&EDhz5t~(DfLKMMkhzѻFXo7ohl&p$I r'%%|{|ҕko敗V-9o\nOش?C/6LNɎל)> L:wLJU[@6uiKY( O-Hohlrn{bN~!nk}:_3DiԪsfJ$f Ns˗.vq{{'?NWoA.Z0fՏ?~~,Ã%I{ŕߪ9ԭs06c0;3;ټ;_ywq8s!!$S+V? ke=<σZUN{K}s}&$E#n21 42 .+JAt9^rER(y0$\ry>ex[˖|~pfͣ=|r ˅axH|<bX{7o\z(?~6 0 I$.ab0EJ%SӴȇL*@0lڰqn3 3DlE` qds8Nb\.bl@B9VtB.'p|.Y.6*&)S%"A A1,p y|6-}~EAdffQo@ɤ2U|[)ڰn͚U+n{ )$OUV.J V9LC@0rlL&LO;E"g06tb}s(aX-GOo~D?FzZZ@EBz}NbA^Qo(~Pr>bI%b?lp~-B-]RVVсCa@EQu̹u:-NeYPFel6|Ez.Zj@ X,L&uPyjO!B &XSSN sU*}>A2L׽~ñ({!#Hu 76&%%&0 6ۼ [mttOvw[##\'?o͒Rط_`f,ypX$&&X$j4+R af3(h#+[{`&) 'pH*xϯy9<4"?? X"x\Ajba^0?+91!<$-> HZJ VXT\r+)1!::j㥥8!Icclh be2Pzrc1GU*&>g2t:- AˍhPfK˓es}(0I C"/Z %9Q*ζ_"d2KDez6<,L. d2 HHdHRU#",Z2G#:oInCD驩GDrJT"#D"X,6 z9 IDATBB^+or8&É޲qP(4GDp\^)RRM&B.7T*#:mll ܜEqzN  zPj%bH$4ߨe2q)<.vg HTDGD B^pAgjJ*LjDBRd2&r90)'{aTE. ^A9"\K$m[6)޾%ϛxQ6DGE* #-&1L`XQ͛6W j:+3S*'&&&'h BDbj4I $Ee/f2EGY\]sFV+ bdRiTT$ Ksh5'>>pqFǸߢPrĄV33Ң"-Gp8-lWVU,^R*~jfOhp8+bR³-8^gkxd=eGK_{Ru¥[啕,)LO\CGm[6uuϳ{wX,Z{!~W^¢!Iec@Qd|R)ts.9)qE~+922*W z=-raإ+֭YUp׿Yzd6o7oWXf[Z/Z5/ϯ1ap3{vܷ{.2˜aT*~ +^|a1Yt$..~O1>z$gefZ{{/\4w;:!o}Mn'>.v寽pmVIIJLO?WpiՊ7WWT"J~7gΞS߀ѨH|g\ ? FQg|>=OA0 Vpwշf7C҄ad4b)aN'/=ZD"a{3hGgW|\1YMM-v61xwH322a e,x<ϷMظ\A.KdR3:u]Vk'O:A i)'HLw ,ԴX,p8&~xxTQTq`гXLrv{( gBIiYfz<.@z}>EUJF.fD0} ?yZFZlzAPv{>Љg?`n"e;zcRR" F&'&: bv&DGy<A}ɁW^n\l CV}Wwg!B|9$IVU״=6;fsd9ÑdU0EQE w'yf5(ӻ> }^RTtT-/_x>S`#|>?IR8>: $Iz~#J)qy~( X,E~Id|սq:EQ5wڹd0 `qqZ 'PV^f l>GWaB1;Uہؾ>*<::2b޶ysҎl6A@oQ|>l||'O?S0 Asq| _)}G7Cx SS"VTV=3z!=q^|nhx$ɦ%9A_;:?f - Y,s`44슏ԕ[U]]B0|NjKJ/[Pȫk}})2ŰƻMkjsr{E%'%Vݮpq\5/ʵ \]wڝ/mٸ~jj:<vrDGq\PPS{8~GhimpؓSSb>Z;:֭] +bѴөV)ml6krjJ,BUcMHj5 Z[;֮ZxIi\.sLNi4j]\\L횊jX`dt XNyߥ)?Z"D3|>pMMO(? I30*J^ 7ZF52: #-#cE\eD,h0 GZ]JbEtJaڻjyN  o4tOT,p&3::*#-p6e1|pphͪJ>nkV-&&ӋRC<tbcC<o\eZuZ ;eX;;y\\.wBFnniHOU*tXWu6axxZp"#-=VLtb@hZ[SS nӓ.S)"{zcRD3L.6>nKJLLNJG _7gQTa!B<\>E"DY"̆$I3zAo _`'{Qt:1)\.7+=%>l( ̥9#4/AĄ}DŽ"=>SSw'9)۵cc .ݩZ "߯T*f>v:_}%n_{:&IGd/:':&'z%9}uҿiz.ݮ# rٗ9<2" 9 gܼUZSwG&5RQA>F`:|,c9 {|ATd=.Jo2d5Kzĩ3 ϣ(¥˵uw|~ؗ6!IN}KW6) A</kԱxrRVW_ݽ{ w>] k_V  "tD9흝0($όO>A^qɜtǿɩ~gMz6Rc5#tZMw$qh0p8uk""q & G}a&&03@ (* NLسeZ{{##- c2**RRԌax|\X,LMO-_ '&\|upph ],EGGma&CxXmljc] b1Xik#IDQ`ݽ`0fՊd!~l|!bSN8Gr{<΄f8tolEQeUwGƏz^qr?_p EQxmeE|`0p1Nf;}<}9HpL N|\#OR@ڋ}b>zr U[AE9]#Odef Aj*!>N"h4\s##Oohd0kooCCc`Wwh044-*.0bc|>_mbBT: *USK 8EQv{"-f?<4bx/  ϲ>&N͚ږ~lQ&ko/zĩm[7q^':;Űo ;S[v  dvJ)bcAtú1^`phxxl6YCKJ+g1H8Nl\.dyúE |>?wqΊnWYuglVQ]CZJdq3bqO>Xm흷?>nS(dq1rl?9vzeKs"r_3sn_|ْXt=8vZ[2իVZ{*ooݼjy.^^r=~}{:O0*+t(R4wqAE:&ࡣ |g8$IiS* t\D242"I4pa׮I UT>usgW7@ AӰ@ jw=#Ǭ} n ?0A `Y8{<^A}MQ555=rI#czppŢeKV,ONJKAzVq\*,ۺy.xfa@0{pnhh8oi.7n,> "D/O0cdd䥗_/,?ޑeRaX`1˖~| 07mXGQaϷ]|'y\uIZU <}Z Lf2$bC0 ŖWPs9 D" R`E\.G*-Sd訳 ~W H+WwZ+b1A0 K$bHf9& nj*Pd20#XL&ׯYTp2DЅi c[6;yA˖tĄ}eX|tZodRz@3 $@{gWQMryrR"9Wpq|HxT.CCD,fXYYFbL&3g?DR?844 U۷nnnn4:*rݚU9u!LO5|t@&]R"A0  x<^bB+e28l6@ӖWV5JRݦP ~Ʀ2ݾxCG1 &&6_44]^;:ha3ӹ8gF:zyԙŋZ3g*-w=+ s-ʤ):rDD Jo<U+\),*vY29v{z(:tXTo`vѬyU$Yg6t"eKֹv#i)˖9n;\d0qq|~2-CGOqXyz>7گ IDAT4<|5VTTn謫ohmX"ĩ31wFKnv]cll|ƬYBQWݾ`͛ ŖM͛6gWwsK<xXIFGY{==^'IڶãsN{|1msDbBFj4~ @LH$[$ұ(? B!B0<2Zp b06.G$0"iT&ID0lkk߸aYHLtȍ֭EQd``%(r8&MFó<]VQ9>n!L&dB_>FVe/ l6APcaںi!fAEQA`pz_NMO٬xjA:``Æ $m݂JZEm[b#Ϝ;QXL J/2 4Gvɜ_z K.^L MBE1V瞑J$F:JK%MIˤRAL)љ D[{GKO=B!WS>ߠϾ(Ab!L&ͦO|>xdthxD?0991 ;rhX>L&sttL(0q[ZI~rwvuW$Ir8짞|b✚:\3g}_?aNK" ήnExEnݼΝHDX\*rtt ðaJi00  tEu5jac-fjjnv$I۶lԮnr5!B1}rm(+p99oWT'&'% JFqɭkVTJ6]prSsb-~I =kiݓwcBZ*UZQYf2"A9{gtu1狋JMNoQ\U.0t\]uuszz{7]T(nϥƷou} (:rұq+Cz&в%fIYT93#h4<}qyC)P(x]=yKsM&z``BX|Z][Trd2f|(E޾Gk7\N~U.NWo!LᘬbhhjiX;~q_E"De>jsBxY!B"D!fs$I+ڑ$@  [(ʵ‘чbmq<ֶOKaq=|IuYEeGg֌{+|(PEK{^ cr<}ˏq{X^a:$^k)~LںƻM_~]nEߗrjmN"7?e/ػ{0G\-d0cJt}zfnrv=VkĜ@gv:5#=uzj:&:>Aṗּu>v< E g?A/\ںyvph`_fFE3\jeWWWtTב: Ѷ/?CֶՏ*J+ .^r[6n<%_EQ%[Z,Bش~ mu|3>{ߩPQY4bX,o8z!BDFFZ~>}w׿z}ooߞy\j dӆu0 }|L&}{vx\d7nw;7oX.^b1IrOr}'o 6*-+axUҕkCC bӆuK44 H9?Zg9wE7[[!޹c;}8yl  b%O>#ֻI$bEBMcb՗@QT\nj O9Odzzjjrߗe6DzZ*IO; ̵W>v|rrj&<.A׭Q)8dq%b@ HKM9}YIoJML\TRZVAc[7U[e5uwL+W~Ra;-]0? 044$<$ >cjm[nOyeP(صO8Zնv۟.@ E7Iyrsm\̹Lvi1-]r 94<f1=ƻM7KٵJqsDm[nUTV܍֕WT~Ӱj@SKˉSgz{fEKrs4js<?eB8yv:szS mX&Dkڰn͒2ff $&$l6zwt pN'fۻ{f;Wp"ɬ́!B b_zw '''$E:SI_:59'w.ʾr:b>o)oX{}f >;99 p؉?ןuCQ;oWp ojj^ǷɤҎNѸg0{tlgE7Hjz~W?A͒'oKOKt*}@ZFr)۶NNNYjV} $E%%&L:䩳K/ڲyCaM$׬Z{׎ʪĄ=O}-\.g-u4y}/ko=844&p(Ӊa`FzچukN9a۷]+Af[zuk(q۶ly5Rwp MwIZS;+*P +ik)I%ޕp_z[$w-|>pFZZBh5/'{3w565OznܮܜE=lj+W?ض}{&f?_us።wb_ eyKۺ"x^PZ^C/L&p֬YVi|/>rOMO/ڿt`vONMzy׎'&.ש3nܰFy/D!| V\\~+C:erZxIHMMMR NzQR6Y,f@ؘW^sdd8!P$EQa ۷=}r2M7+0 ._6o\?$#^ʵI Μ!866>`zz:ba2@G(H$b>j Ť/ H866N"61QRZd"@j4N EQ q@o9].9bvEjG>AC `(.6EɩiNK$-f%R A~? +CawL}>oMR\. yF"?Idz pLE_}-@084x:v$XLH(0 $J(ҕ=y+˗%ǝ>W  "-f&A|*Q$>fhq\ | 2Gc}wzSsf%i pjA^OPQY]Y} 6(`0J@`я`|H(E,f-h4s} "D#_0 J%%19sӨU7KGjJ2vm]m]tAp~AZ2R+A$Ar\f2ݼUR*^"-##)? ILCQը :"&&&l9N^>\& gKIJ JSYub6N.sc Zs)EBhYz#-@z[)@(KLkqMFh, 9d.Țw\&k:ri bL*EeXZl6a%Rlh`0<oޒŴ%oDs[e哓SA|}y2fI E݃hTZaѺ5kp3^gǏ=A;b67o%&ėWV h~;֬ҿk.4ZZ=` h\.TUBn4*,GD\h[2G#b훱Ǡ߼unX鯺k&b.D!/4`0z=^9"`D옜x ">.a($w`i5Z^/z0X;\.77g͎X.GQkj.rRz=V(2[ |i)1Fv+rALAT(45 J\D#aؘh9Cd1GLNN..aW@E"׷IKMp8s[r8jJ"@B|Ϸ;&.j4rLpJ)ɔ hP*@p,Ah A BDZNñȰNCq\c }mx0s L{(N'f= *$gF#=tIzG\_dtl nٽO?wi_~Ϗڐ?@>/4z qZ{דm omZ6""pOq'pص?gB|_D83inmx ^8✁'O#׿o`tl,<,8ta0 ۷Q8~tYy^ĩ3::xjNSS$I>:nph,&K}f7[ D/]bc0ؘ( 䈰c'OXL6aݚ FͲʥ Ol֌# >{A'wL5/ðZ@Q[BLtd||~ͷ)|zFzNΊONJ(-X"yu1ؖM=cc@ٽOX,V=-Q3{H8t9j&_q?Yb}ںe˖#J"lnk`Vpuk6o~n(a}׿٭H$斖ܜE2ڛ KWy{s`efef92L"g}~Kzʪ<ᘼ^X7mo=9t$=-u+ז/+.)]T(BA[{ ?{64u{< +W;CCߜ*wy.^Su$bqOo?Sg8tɢwphx$%9ik(JDsk8[mF`0]Rb8񳟼{<BE'cPccbrٳ ב   1|]=+W/WLio0-V-:* 6o|13#ibb%6y<< ][M)魷=M4!B|g(֭? HլZpصgŲ%lT*o޾12:~hmkZyy 2hnmv["E~?_y/B8q߯0i!| >ϗɤ džs(/x'޾5V}u1a8  |>IIz=gXmƺʪۢ7&'&\tphfNa"prw5HeӆƦ+ɉ q+W,3 N?~?ǛL+83,G0h;Xmq{<ITYOӰ'n!W+WXf4'EU}>߽GBV)R ù[SׇۗWTV545wuZ4^npOU*X,xw de].ٹb$+^Ev#,JܭV)b1`(@0hZyӴrkM!BL`I&z^۝g2"9:6&FU$ B}xxJrhZjJLt><,Lܧ1-Bqg9\Ni\ B!g=<oLLYcJallVpd2}x`jU\lΜ> ð訨Ԕ䜬L<^|\,A&yRRt:I*[X*Z 2)셌R;Iq1&Y"Ν3 EQXT›0Gqi<72b ͜Q$ sU*%5 oQfehhX,-?GG Zf"P?T*BȨab}xӜ(H0jJ<"2b4ay~\&_hV3%+N銫Z ə3{m~ z }槠Ix/\l3FEE,^b}M;[Zo%mF cv{N>ռGq#SVZ"?H0 m3V+ǟ_2"bteRHLcy޷ xh[6m0̬k!B:;׿ SИCDsK[zZjgWJ"4ylA4<2ONJansRSxmBAdFz -miE76A\.*sή1cfFX"xq0w‚< [Zmv[tTI}ukBA`usk{~1>1%Hssh0bcbL&3aY4kۜNq|\Rz}>Om]=d= ;a?6<< a{ 0]SKĄIRe?eK/,-5$%&DD軺{S&ReJLa8bGo(%I%3#$IDըZ8\NNvֶ6' V Ș٥Q3(no0rQ?8p8) |zDTTKv?qْӧOFFFSSUJU[GG iB`phtxdp$'^QLfKKa1t/[P O23ss|~Ggڵw?OL\R_̣OleKֶv\0:Ά&HvutĄna8NLtTM]=EΚ~Ξa؂A=1 rEbh0ӥ<;:!B,󐿕>odt8>? ?z#+ >BQTPeelޱs_oSS;6fIF}^r&|`')ԙs>ԙlήX,*dizj{gA.[}v_JJ 0 5ھA<8<2:f4j*v=XSSKMm}Ww'a~ÏIR~uphh nܸ}̹ :JEeGW׹ ].7MQBnmmKk16>>2jx< (fs2pA}{G7SgF cOod'Niikdԏ#cq?p';>ڱbJ#N~q+.냏w"M~2nm]sk+C p8zaюbxppJ5v}GgWo_H,BQ=%I Ⴧ?u] vV -Ax_o!ϣhzϾBz_EQ^aavCG̚hqCJbZaNv 8 s8$GFG 0_04LX,$Y'X"<}v`8p@da=\.$%:t:w?<2*J(؉SMs&c*;0!B=!nohjmk)o1j4ųg|\<{ּ Mؘܜ7 w^Q?j# -SSWA>woAomk_,3#],L+,q|xd}hh&>>ݚںᑻ5uZ,߭xJB&%&Ӈ㩩;%\n7Ӌ KKVߺ0tjJraAfϜi<7,MO/*Hlp>ʝzu446GFG[ZچF<^\LݚZA$RD"HOkk~d68t8JKdga(Jmo\6 > Rysbyųgw"ye Kƍm7ol CCã r  *7x5|$IH 288̙5sphx|bBQ̘^Ԕ|^QaA_s2nϮ=NhǮyy9eB!l\ x]CSsKdL_5-ѩT*"zNk6[ Ee Jssq# KϜQ$Ʀ~PeӺ{,*^TbhzQN͙9hxddt}歾G!B8֬~\.bq8p8a qL0>ddTRJ[74p8g]((cJIOK{,+v]$ƌ\G$cQE(󊕠`0XxE`X ''%n|aP$H E۷ldޟ5 H!P*jka}… xFF0 (޽yJb0@uA;vٲiÓ^\jy{phh v~-0AW*EӦ-^$AΜ0y hVVî`0pX-A\.A(*Xm6322R*EbqZj dztq_0^7%9)!!awwv^?- 1 # vN&axtQ4%HF cQ&Y"WK*,Y?^,5%e*9QQg]5Dx>F(ߧN'u`BQ(>?>10{%|!p8lXDҶ-l!B b2ƛ至g|T*VADT\nق{DQtՊe$I7 ?D XrRɧ'N]Rըbc-\aXj\ΪN>hfFL*er4"BZp 9EҒy1Eeh>P*HlۼF`DR@Tt>o؅a /bKDssTTd2eb@W(fge8uF& Ew\s> sW74j4@ c$ ķ~oMW**cBt"JJ` R .]?ؾu႒w?X:l8er8tS˽e <#=m=(lɢU{ ܭkhlB$+#=B^<{֛o[Z27ܩӪT* BDP""oL*Iii}FVecF#㢔ںȈĄ,bcb֮^ HNJBN]ܚ#NaOi)% 0ry?l> a"K}>#D"QlLSgT E*롪*ha4*FFQ4,LwZpT*{C/[rȧJB;X"P Cjjd2ET("#>x'-_82"b*"DV_#gi`dl\|u&LW*/3;>.` ladą >03#qVn꛷zSO+ KflȈaz:;W,_ Uuϭ4zݼiyٍ/Lv\Ϝpe d2٨ary\&[h!dS4]WPX4 /'$ׇ&Pbb@`X_r+/C1j嵾bc 8~+[XOn hANu{</E?EQU׫{MJL 4]XSPXWm͖K''+wtdeDG8}") ߿^xor&D'n9h b%sb}q˗.9wRgW70kU7ܭ}Ϳ\.j;^WRnxa]]}c{gWp_2wCwlxwn׬\m̌ ;qskֵwtz'NUݨ(a$>?z0{(˥iҕ ?0<<[uz׮]Q}kdZr)ɒ4 rfˮ̚rRy`|nͪ}͖'OD=>S~$I/!$wnH$okj{[j7ov?cFRn효۶l*j5[7m b^-6|ۖ>6j,.z?!Hd-D'JJrR\l 0  hnie DGƄK]2y@@**2';K$r\{ aϯV_zRxX[{{ѴJ'&bcbTJebBB[r90LWO3 rSSG c^/$'vvvXbBJ֨U" {}d8NJr3  gϳY}9ٙjJ&-ή3gDGGy<2%!>AGt:ohl޽o33jJL0f4&ǩTʸoۜѕP5j5`{N \. rS$"D̹ e JQݲŮn;^wttLdeJ%ҜLX.].W)qA ͪ;bϋ8\8 %rM3\.p$ !AP \.+[C$(BS}ąR)NP +/zv T kgnA0kVT.uk3SO:s8Y4!ϒtqr.k|ܩv0m 9X!B`2wS28'+j27kv@08f[K$b'L澾~><0(NcqZ ].74E_3g&Z(A( CpTddS.GGEuvv9N_77)@QԒE WX,J%b1\.?*"b``q&mß6a:@0Z%'G\lLSSbߌ˖nX(}Ucgoך!BM0N"]8ō?tl175L+ȿZYu)lD"!ЧϞsV&-.[ G cW**g\l̙s"#eRik[Ǟ}5R/]}&?/'5%`Q4 `ښ[-܅<>`<Einji)VAPEXZ} 懇sU+4]Д7p\6xvxX؍]^vl,](B>4.vs.p$B U+5jk+^K+p!7) /\,KJt.2=nm-u8շn߭ <:* ǦG  |CI`a!B<<,!B"DrEQje4Px^bXl䁈G iv` xd2 nۭV%|\^ipw+;|,4x,V$I$ɯNGṬ(jhxd23~pX6 <˗g x^M~}VfA׌6`p}YV^3%"Dܭ6ۯoslxq=>ы0bZ5d޾usN`K:mO:j~SNz7Ձ`pŲ%f=paPu׮~OnjF./>/ZX'NY×0 cn yrRojC \.gf^l6{aAW"E]_n({{zILMNZ|Rx "D$)}fӛoWzo_yw?jyU+!ZmH(<}ؘQ*>fT*mhjhk\dQzZ;}811_}Eo۷^tY$&ͭmWʯ2 uӆ]{ժ 9/7gyu׫o2 ([0?%9ͷߣiZwɡᑟGBٹgߜY3b;UqT"6- _U*C#Vt5nڸs>Bx<^bY~2<,l29v$Àܷy_јL,;aI< [l>*>}+k4?]2̹r\*dgf|z9Yo^Xnd*+-)Z0`aakkj˗.~/tfe^ٳf P(xas\|j[roy6ozɜlɢvv'קfohl ~#nF4}rĶ--bz4p:.v`@uU]nmk/MJ;2 IDATأ+ov܋-sl1>>Xfթ3gnOTdĢ ǃ. @3ib^,;3-ϛ{r9A$Im߲yZa}1\nCS?t?Iԇ\a\n7A/^ڴᅕ+q@tTw:]Nkl^P:ӆU7n$ye ϹxeK-]()111!WyWxoyye0>/3gWz{KWlڰd;?q1^Xg6[^xY/obZ6[Kk[J^|4 |V>=vbY+/Ќf[ljjr2ӶmhZZvʂ܆Ʀ{y}xͷyodt$ɱӧX֮^h;+-Yjeye%A\]b/0~ݚv.ϭ޴ᅪpYRͷ߫rь[6e$>mV۲%^ںnM-wjj_B]Y׫o}kY9__SDQEQ׮3_=ZRdQaܾSx[7o5G&&Ly?yՑQN_+/KY3]nM}ܳfMm]0@~fWrCcFO^2, ^}钹K]Zv(Wȧع豓4d/.[$,L4ML-6,[zp- /[z峙"DO`?= aH$)+4jF"AJ%<j;<ȉSgj4Mͭq"u)˻ MARר`P$$ю]< aaR$I1>aQ H\L aOʯV\m[6Ne""z/_M bcbhfA\R|~@H #0.`3k c5OŰ/!Tb4gs:.  ZrQ5 0 trEGE".PEQ pp8:i1R0Lq|hRqḸXG;2jȤR\I$ x} z=R:vS4 \X$ ia H&Eӝᶴ0 }TXd't8==>O,K%b@ll rQa $%& y@xvMv%0LrR" NSp8"j%øf;5.'6:泫FK,\Pz܅#G(ڳAf7\` $(Q!B:X6T*.^.A"a/]ly꛷f͜LO`XVUV]_蓎.OL"xB b(rDbH JKzzI]\N3t^̞) uZD"}q1W+xð3q8蛷ٳKJL8uaV HYT_:23>`~CT2x@P"Ɣ+X1?iH$x-#aQCb|bo{$TV݈lrfϜ~ B p8]U59)I ̜^%J5ji:5%sf~qP.T*Zmm]=A^o왵u ^@(RQt:)z *+NL+tu8x˗Vߺw_mX#m)h>yVQ9Yn73# H >_-IkU7p3f¤}ݶvZioݹ{nݚ{% 23˯^ nwDCtR`&J͝b0J&nwddT*Vud6+a9@`w8Q8vJ4-S!Br(DB JLw:SU%|}xaA5%%&dgEŊ NS*QQQ"HhjXas Kr0"B#Ir֌j9NI>_RdR 0xRt\79 ӲՑDBNMIN` FF꣣" :FA;>>lAN0`֌}V0 ҺtqT"yw3Rill)Zզ$'|RDц4j5 0><\Pjz}i)qQQa:@ aXV{< TJejJEQJGD## A1$Exysjg(HijfNP4!!A_d>+35F`D(  C2ÑJ$[$ggf`6EE 2Ӽ^/ <>.Vvi0V Fu8)z@T*Qi40T( N/Q*^oZa~\lLtTNxJB!ٳj[g( hbTMӔdJ@3bq"6& _Px<gϜo'AJYpCX"1}ڽ-K(8A0 ٭5h|!0L0$HŎ208"Y+V(c&%@ }$E0 aspA ^c{ع{f|]V7|_}9/7mQP_jE}C++$s>K\.w} 3 m릩|=V^_fUV]v}AiRgF %*2bMSy_ϟ?>a\l|L]"/~oۊG?~*4$g9yD3'hJb`paq\,9. ʯ^ohLMI*=vժ‚ V.[*nlmkY֬ڹ{_wooJRb|\m4׿8t _C[[ǥ+^\/ s3ٟޭWx\nwk?ؾ̜Qt܅Ғ_5W%'%—m>9g>\dd B!OJH|&Ij;RFFzWwϯvvv]R? MV{ҕ`Vucy B$wtbۏ8v{23#ĄoJ^nδ޾3.$Uqڂ% \,urW_>yl]Ci)Cé)Oȶ?rA465oXNܺ}mݼ0 Oͼ'cPrr2ٓ| 3  a:/ ˂aXNaOABjF,)>p::TJezjȎ]{& P 틏 2  j)3׬Z0, >_sKk^nfgOեR9 ˖,zOyEر\n7 >hp8ig *j0  <FH%N+,p8]"ㅇ'lvJdVv}0kUӧx<>ܮHOB.Wvra& Y8c$}ڻxL?`/\rnP+ nZ B}ۆ?m(wӇ(b\&lzҕĄ%Ǝ8]Z2|~rDS>`Eؽym`=;w~=9w 1D]***U-_dphX*hHׇ ͝=-Dt89'/7gxt<x39)a&SRBgo߭1O/woKIw_ǛyrZՍή3yCS\&Q4򕊘hH$rOr WTΟ[M-%s*w=f'9l4[O=bċW=nOFzS*U׻{zO6A#  tZTð7odGFD\SSճv`0X^Q =L7u0  kZX, oT |ꝻmW,(r9𰰧idFZZMm+*R.:6Jq||ba(~Qw]kBbF <DGFpX8DC#Z i"#SBZJLGQT,ggeF*Va'I(PDBQlLR0͙5#2BA A**,LCPrRjczX`k|\l^n6|'t:g_Űy[ KK$bqLt %sسJk2S2E"><|h,*̏bB+baݡVHzqq1NKT̚9Y,肼g'nwLL 9%&ƛ̖i"02Bo3%) XdXy߶O111!J0,)1læM 1cA^nbBB.HfdN4'J($''ZVקT*N"0fy}Y)0 AR)tOͼ" "D'Y!Br_cr0 c25!u2FA×!Ird+|9N EC#_ކQ0D.D!{r#F?kq:]wkjQSS pvVfYŐ P8Eo I-m7ol90LWwkU8/,%-GW##g( |j2[+/} ΁@F{{R\xESh8Ovo~%m\.ǟoh1 tN>º!ܸjejJrNw܅䤆& Rn߽^] HbBK^=0Mnݡhzqق(5BR;_񿾤 A|ɿ?;~\+!bB"x A?E phhhlmkNJ% M|> /;:=^/(*ahpp3**2=-ή.c-HKM?Ŗ΂a)3#}|bM(v\0̄ɜcXkJEQE&sSsP(9lcqX< Ro1*H|!B!.WWww_8u,  <}~N2G0fRQiX?V);nݹ3j0?y:66]]E546G::;:+M9ws[9g "9g08Nؙ7;/xƞq{l09G!CK\U]cinߺ}n}M,*.^Wp:;!HJ%IQEѣOd/!IȱlkhpS@IOOߵҲGOXM7o?A/|{6_HwOEQ'O绘t:?6 ȁGx\ށCGLJ]/rl6RkCÅŗ#Tɑ$yvE*R U=vü_:\.؉S<>O,]ȹlS%^+}hjnnimsEK6;I\>~LGWNk;'0/vq$].7|pxJ%Gpݟ?(EҗS#ʪj$< X AA G1/vq;A/Ţ+Ax>>rD. IQEQV'̛dsKaa-ɉVut-<<2_v```FM'O[̦޾ݺG[GG $bёc'uL J$e7Wg IDATEB! 2 Z]QYu!7^}d:qs]n7ڼS϶tW -m)I<vyxj}5 ffN c$HV[-WKߺ]IoBQ78܃ݼu[&):f:ZvfSs"Y3"Boh\0%9Q]/kmk|8_tFtĄ8Ei)*2"9):::nUU)Z\sޓu&`RSfϚ?XD b(@g||>?6&DBWVU2l72h.^8;kkl6;bٶeN|. EQ:"6 K+ (@Q׮ >zb  @hBΥʪ;wl{}#._i5%VhAXhr!(~"p@ ᥋ np8'I厎IeҎN&I2((K*<аf |>/:*vʤRꮬk?g@D#)֬́ X"v9vW?kttE$I\ݭhDbA!%bc2Oܟ 2y"}?7gG|T(j* E-^tqzrD~?s ˗xPϜPPtYӅ,[})Ph6._iJ%!fscc3j50jP]Бc"^`G X$[(|쉒@i| A+_a޴h:v F0Ǔea.Y,VrrA'H`٩)2nڜ9-f}g(Z|u/[̦1Nh4D&9z\RB pbAP(X,C ðD,K̛{)/ji)84̦ k8 в\.b]AeK=~Ο8l~~ BPd"Qk[Gq^MNJHė Οh z=~֪,kYq@j4 bRE-F3Fehx"{=,,t Au@!!OǍVD, zA#K&]^p\P>V*rD" 討)]^Qǟ$&kԪן>w>70hݞCGp_zH(t/%'%8uF*DBRE Q*gUg_J$FŊ:vZt0~c'N `j%j4GDV 9}qeK>M2 jk︐wޜ҆MfJ$]z׿|{"ϻ- w_wC~H/_$4|=Pn^ϟ;'<,Ҳ6kltŜ\?9=3)1  =akכZZfd:!gxxdŦ`#EQM--֬74^+-cYKk$IVԖLKK9=0n<}'mvPYlL&W(IWXF+*+*gLBD~aq{{Dzt"'FdڊjPv @0<2rb.Zv5.,*iim]4;,4?y,_t/ֶk(ZtVq8}wnE?\mhl^4?*%0~AHvyeiiϻ9hhx87r$'bVw@JrR|Y  zy M},H-JsK˟?kuukMJ$Jl;9)y'訨x(ݹhŋ?p<|ti)_:B.[eƂ Eu]^vwx/'7NJ`~{ޭ_ߐ>r /WTV|>߁CG֬Zš􅺺{N. +I-A>r<&&*,43E?px,_aXGggsK͛Qzfm߶Б_ݙw#)غY՜<}nhxd߁CKnٴ]/9b޹#'NU*Ι-٪28| X,=wm?u|nvl7o765?<^o_ǟ~>{V֞];jj[7Μ˗.yskϻSf ε󟾞o7cz铳}nyW| E =V3༂"ǓD_hhh($,"C;:"׭YEtvuժĄx˕_Xv~"bxG;{VOob9}4D"{Pk]N9'W\ٙ$RوҲrR1 8ᷰMCC MFǃuY7ooݴaR;θX Pp@`XhX$ {z"#w)&:2qI2X( C8cb0˕s)o0 |"=v9sy| ubB|lLH$t r *eh',H5 aOQw,ߏ ;.p>$IPAP "@dX/Jfg$~AlA Ij֍XpM,}e"k~?ŢJbL bDY]̙= #p|w?P~?A9BQXs<&{kj?J``  } 0ڶv+WTULFcss0ʵiiBj ߏ 0c{¥@ fJc6].Z65]rͪUw55=խjJZzz8vP_&"A[[=8UJ)P4E$#kV̚YS[V- #b6|&,fq8b4םZ?˖ܻw_* X(:m>ZG%bnjzzzqU=bN?0XPXΙe+*/_o2xniJ6MK]ًI  [J|5umm]qɕ䤧"Pן_ʚo!Hs)f9]7nWT~ him H$?χp)8E`0߇"d0ԟ"d0 `ohxx``f?pð޸y{R3eڲ@ 044L'(jfl\+homsAQf>9 5?3ہ@::?00:6 ֶ¢o/?0p'kAmmO wtOb=08408yt.g ~"m`_Ѽ^oEe1 Occׂys֭ 4zz{ /ض؉ApАc2̘>ѷ ,2];\ÿY_=Z(?$I~>@ o-Ξy^fo̙5r;EQU52f/\pRty{za$I]Vѹg׎'xEq2tIO9?<2b낃3!'I~ RR&H͙1@mA11VV522<) fiۖMo?\?7td۸]W\Bя>`W^% Μ8<<" 6_+J&%*֯a⒃:NPeӆ>o^ORX}{ zu畖ݸq6}i>_o#*U[{;I=}rr\gΎEM;?):wjJ 02;kqE N>k1 Nm-fS6ZL|>wrض%7rz!'W$  |>K:r\.I%ؘ'NE/G7/jniٶe?iM?V)ZM\ xۖj z`0'كw>qx<(ӵ~7n{ ."/ޙ5#rIk[ЖMlvM]j1(0 $Ie͜{0 x0$Z| <0q=;ߺ]ܰv5}^#)2XJKǼ^Hbjf{~YGG|>s'lmpp;=i;yzzNM-EQEyX`v]RAPHzr<mX,VÜk͟;gv⒫'@3)!`2Qp~0LD~PH ~ SVÃ(*r>Tx< ^eEGr1 cX##ֲi*eЅ\?216.ii斖+׮ޭ7wN1L& aLZT\tI6 3#}.dR: 21z߯W*--n+?q܋yͦ`+B᥼;/:mWwOUTZvU+ #:=sډg-fSOO/INL8y TJRB8 Ϙ2Uwj:u:dXFc[{ǃ546i5N.p/_IL;f A+tfhm6HJL>w!8QFF=Ó]%W74uQFb)<:'DdM]@NIDAT=R%F+(DMM-]:fjrn=D2n}~ ܇%%]= M8GGE^n7B'Ňp86?qL vnūW.__aޠ Jzz՝G#FFD>{/#=jo_ J墅{Bf:v3}+*|PXE.uvu74XR<{f%'';yZ/ Ea w8~?f!(p 2폢(xPE$ e%E8nw8p(¼^ y<!Ip` aE >gX0 8n;~?)ħχ;N̋=콀r\ #EQv:]}G8;;Վ(ͦ;tQAӃaE(xN(8n=̃a8=4q!'9emd }y7G(׭YE;Ihtf/}ig_;r3u5+Wxw IMcv|_xZP@ȝ;Pt~ KY(j1-]Lپ/.x]jvy_z zYX ڰvuEU6;!!~?A|k/b?}^]ۥݺ{Uwj@VyݸE$7WPXw"椥nw\wn7O|ѩUZvtY̦+\-oh֮(y<a{vmw=o?~U[ M.+`Xfu<ކk }`؞];0/vqHЎm[×oΞ`r8!!;wX,X,V'OSe0d/oJn,YH.M&T9b(!>vJ:0~x\nJrҤ n)AP\\\&54f"[ZBR HgWwty{ii=}=^oORU?55Y,Z HxXHzZJ 0fض^} e)q艗v툎8ys89)Hn.O|>Oi[+<Vtdْ9f&ŝ8u=bs.e9w_VΛ{SAlWufӓjUrb^ʚP$EFΙu_{l.,.IMN:s/~C&ݸyb<{:%)Q"WVW$%>z|Nmmkymv{Ww`ظ~۷AA~"jUJRT*-HKБ!NW^ZGGj**dRRT?ںWNMI6u+*7oX?{LN4bܶ%)1A((;5w׿xf-|zf`EQɱ X$zޭq H|@ĩ3|>?,,燅N!bQWwOsKkXhheU5 i)ɡ!}_;{;ody_V cg/ܯoXzr=uX,\&aX.  p<*"a?ql\ cE"9cc{G)If~˲O;1኎?kii{l-ֶH@o_ǟ~VPX6}ZFHE(=尿2 AЬO^z^ ఀ ?A8N@0%2T, |M$f=jBx0[O{zJJhln޼aÁabp| ~|ll%$?0ڦiBC( 괚gQcc MD b1{~ z7bW@ NAahxƲl(.++8lxdHdyu`d-LZR\j=omMR  ޾{GFJK7drJp,V<M& bbee-7K%Eё uzfAD"C#H$H7‘YjhxT&d`0ʍ KKEEu_ϝ3-4E8t ,+-y~dtJ_X8r)LŮ^aZ8k.[*P4 ( @0owBp rJ&q?IeEP˞֛omo߾{zvh)W(Z mp$0<^(!&XE؂>bXfbjhsk wv4Cf_wʧ>D hye1a(0 A0òKKRPƵɩťeL8yK!_Q]^Y0 aEs >`eu AEyEQo`jC\<[_Dk<<_^VxmMNY[_ql~~Artu\n2z+,/\D"cy@]=>yx5iA 0eCr[aYsq\<G܏<7n\9ӠgQ#d2T*}xtZͷC(VT" 2{& @ 'N@ 8B, `@ NΚ}pGQIHr8i AͶ}IH$idç+3LdRI<^ H;$NL<8xi`xn=gK%H)Jee2I ՗VpB, `y>+6S p6u8sULeY{%76g(ʕL&x3[f2ds@yDGdžGF펃'oIQSSχ>s<19%H76'.]`6ZGZ'vvv,DڌC`?477޽&K^qFm2=$ρL&%QR8NRa8x R*l6N0d B$!J$33s.Rd2(P*lV*b ^;:ry^HgF ||C4Dbń0ϒ4jFٱYiV)X{vVg9CV4 8v)> U'9CLV/es@2yI`$bIH0f@pR@  8555<EQ+!9p|b6޾= iy~vnP4l22˲S3HLk3rS,V=Q$I>}֛A$A<(/++-9,_C.G0,Em};40 /ҙE(~G1}Q%iЈ^< ʍ?][]m4*/_t4ͱx|`pX"[SG.׵+gg;;/.^gOlnap_??z}%޾P(JK'&ϴLM"QeyY8FM FJ {pwoᎳw?hjl>W8 vv߹Ei彨$E8"D$)&0}2 >/$ͦ/]Q5/ïDӴBhhmA;;eeC#gϼH$J*+JK6Dhvv^D"Qvvx[[$bMU؍nD@ B!-WO*a]ݯ޼nw8rd*,;{']_@Ue|MuVUDbgw\D2i;;ڻ{Yk;L3:h4o2  9EQ$NK$L&9p GQ$H*r 824 ŒNQqX, \.x ǙFh-Y L_7H&(qX0&>z]mͣT`0hg-LMF=^/TV/#4Mr<a6KMLȤҭzkzzkW.y}~^g1 ;{X<&TgK$5Օ@qDQ4bh'Ig69fܸ$IBðD"FqrbAAbq EQD AA HDA8Iܨ DgK,f (p#D" Es91 \ 0ߊD" D"H$zS XBt_RiE|lnmͧ3VOS4A\d ðX<vGaF0 @Qax7+rwvJW.(NjqΤi۷kjA|~ N͒{*ow<M^4 0 `d#KQ b}c$ITx(v:YmRIfHevvhN$T*llnq,EQ1A@%ȑ˝JjEV)izn~Aw`"|ccS,AǁF\ӋaTUU|tG&0;J:Y˟Ԑ Qn0,RO$$I8Q榊wkuZGQT$jF6Ex"a J ff++QCYWUT aWo^hoN&SS{{ DbumyxgvMLR4uJʏֻ$3XGLC#C#P(2; b)-)njWUݹ!ѱ ?0s|A_zyx484bwqDz}{f)[[S{bAt&IJ9>9/ɺ{{a9pomi.*+ʻ{zyDt îI$s*8ep8Ɉ%ɩ]=A -Jv<}*7aXSCQ,LE.r:޼().\z3O~P[S (,۷r7,']N\.;r mD"j=rX-L}H0L$Z-2 $bL&= <{ommmML,iW7 "l$Q啕+fI,D"Q㭿q|$i ?JeiBrRuxDyV0L5O$≄ 4MLD"H$<ϱ,j766%bq -w S^$g[p:Z[76fS5JB* kAqB0r4ϒs.A4=<2!3g۴Z{87`X:֚gY_ߠ(XynEjXLzo˗|~?a0+^俬Gy76cؕ+PK[eyyd"UR\dM&# h4D"0/R+.[̦Ғh,V^VVQVo˫(_XlkmJ%fo+tGz]n(P]Xhu&ye=^_MU\.˷ZSTqqjfжvF$=e*7h/Ew9pP,Gu)˲@l2tga%Ba=N7GP_w\ \g'7+B2r4L^!iAX/{޾l:&,E%ɿS6s-fph}#֖ƆBhu<]Xl zzԵ m,"̐(a Dzd6 Cv&D3smgZ lSɻ3=3Ѩx핓"ꪚS9^p4h"S}y`J$G.Lzy[~>#cqXm#4M"Q*VT{>iҒ啋=e/[l{g\%sL`xtjAQچ:@8q|euM"onmζ?<զfWt@ 8yuz`FXy7}HCeΎQ|>dBD \hT>6>Yh+Ш5{ve$dY3Zl{d8)\|/DT*ͲlQ A, B姰Hx|]ըaN$Ց?8FgZMWw+>nYY_][/).z֛[fq`hצgm=L}kFh\[ߐ$I k}EzX,tqH&q8Px3s/]{dl'?î^^rhV37 Irqx<G"eͳƮ> 8/,.m?qޮrI}r eYz˰T6{txq>?HÑx"L&,@8aA (\n Cͺ=x"D2rd n\ `vR(BQ4J0 Kg2nVǁyFVr9%zz!yl6Ad6\nZ%EAɐqJr8[;UUM +kS A 2"V!B&ke%zZEht|b|ٶʊmahMFFdR5vx<ګ7}\.+/+},%APHVsKQ4 JyxHfH\DB0hb8sj 0 R$tEz IDAT:MӴNFc ÊD8 b#D,*2 D"} J$yd!$Rww,#g<),Æ^RT+>bx*]%tȾ bɓ427= הdt}`p^Ge r# UjEl^8wbccrjjQj^UZ6b.,uZJe+(0ZhЗTʚꪽ=s%%MMXf mjX)"ߚ ?JUUWVu6F...r<( L*CgZ_j/+ m*FJK-Z**>ܴj+,ã|Ux[O o:xҊA n#e2<*EJDVMr<OIPH*Ri Cu:0@aVJ3ZDH$9`$I2C5d"0N}C_8soƘW=rJ{ſ y=zcWpa)(J"oޟ"zBAIL<߹Ϟ&U/*\R,jh?Ʉhs؏EBuMD+[o츸PPR1 ܊J+6V$RԍrSpЗDJkwז}''PE.7-J-s"HrsUfd1\՘F CU* jJV8n6 ltEfhDQTPLFHd2:T"!B" BR&B$& $j:K Hg4v7V8Hd>סso[75IrF|D}u&siu}4(mYXR9j˲ktF+ʖ&GJy)jm~zciyirtw}YNsv<NƣGłcw ʂVg)Us* )!Ib!B" |<ϓD*fJ2)sANp>} a"B*W6x؂J,{<êւ͕x$ z\qHJ<5O&b5F99)b`q  g/x$t}N$ Э5Ar¹o¡[g@uAk[;6L28 @PUSA^ykgmHU˕ř7%XQy5 #}wm:{%29 m ׶ͤJe*[uM`@2U1D*W+,yT93T˩D`g$@ |֡/N:œ04Mf2>5yy~JDzmMDu*&0Gw}a}cd+IC;{ 3G7g64=19},; K3d6;=3{YKr ZC6SMd|ׄsd._ۏ ںZxuUe:sV=p:cD"9ّNgf/^8awCCIݹhffo\MD,FU4Mo8`Ko`045=S[Sp TT//@0,߸vo`7_Ä?r N0<2426 \&DScD"95c'%iH1l^ $OJz}B>7ka/뜝_w>vA+.olb`~~l6yy?xP(NNMs,{r!r ? & uLf}cszv@0DY~\zipx\1Խ}\&t%ãc#Rdn?阂oh=*}ڜ`/+ G2 J[o119M4 (L* 0@ׯ_]^]RTiInIq1> jk`(qK @pumEQAPaAk*Q(/%8  FR)Sʚ `FQ aA nijd dVn9Bǁsaq4w*s~6ȨX,N$S;'ɇ]=?shho'IƵ8?e6FH2S_[KEgZ+˞L&{xw 2s04<O;ۏ\NQPҡ@ x֞<K,eeH@.Z7_{Zr{Z}q3s >Z&޾VG 0 cwWUVʊXi#0"c8H$Q 1,NM&cZEO:œ,,.onl8 }Y8P5&1rWM,^*M& >MB"ndH|>y'緶wr9y);ǵgO$koG> APn/CGx ]zEQ?B ^ёKK#X"GG. CE(8NgyD2HLP(wv_oA׮D" 2$Yo͐d(ηt:-JZ̦t:dLoܿ0=3}P8g1CL&of`(oT*%J%b㵘MAtT2aYZ,d2 [,SL&CQt:q qJu<4MәѱB[:a96[~:6O|k^" VkE`ȚF#Ѩ`@QYD"q#G9/(ȧ)Y,d2Jd2F$I:  E " 14_m0D6#^+7{}~Bh$r5"TT֐8]_$b&JQu2%29=T*dRyȲCZ^Y]#I(D xdt pރҒ⥥T*E)+-9p:!23s,ݻ*rqQYgnanw2=;'&78s=G.WiI ÈlOӷSU{{vFf0s81[X\(۷S)eJKt)p s8h4w㉄ols8ggf4jx}zu:F-xz[;VX,{Phdl\Tj?@v vffl$%3*9("DcZy}c3 O87`C1vC"|t'ɬM3drbreյ#K&T~N/o:n6"@rèE qt0 +T6AQIP[SDܻowPUYQ뜇G{ ))*j X<. r8\.FhkKӷ{zv#5Ն#]=v{,qH~J eRiqqx,$I (JCЩٵ  (TeoRT*J$2i߷C!Ry7 T)StQQ qpr e u`qt=M "T/-HX4p: @p蕛7>sb6߽q( AJ%u5TZx/8~=p:2?7;YVt֣3 5ow]Uuu3jV^VJj9b6 gBMLz./qZ2 :D8 %bhiZLZ=\D-X,vV(h jVXb6) Z]XhPl{wak/E"QŬH\^Y+M P_<mgsJ2 aHقN}҅`(@pIIXL]dfd}mM2*ٜGG,VUVX,DVǤRYqQa<c8><2x^TejX5lT P p0HD~qѐH$ϴd6 zv& $E8.P8OD|T² nR~FTb1a?ۿO&v{˗v>h4._>dYL˱LC w>P(JރX<>T*U MX,JM,EBaZ[ox}~F70x I0h :ťUX+.v(~VeJ#cG.wkKӣRq-Gt?ںFOL!rq OӉdJ._raL&#q^F #߻汤{dxܙҒx&x~)IfvGЁy<۷A;^+kӟ$_ <p¹ُW 8%^ðLx.?p:yy!0`N@YiIeE9<<:vFTz덪J0t:>J ROiuՕ=}A"7צR7^{ew~x;lV3[z]<(/+5MxId* HW9-]OΎγ훛[B/]?8dϷ)h}{5Ae/_Lpoo+(r *).Y,:VeCLf4h9NVU@ (qۍ hc} d;v.i? W},ŲD"~_ +|'$xJXY}Tztl ;r>Ӭ/1R)rS}/Zh,,.?yhll|66GY xd2o`0Cî޵ ?N_hnCt&wϽ]~ӷBY6g75UUS35U LLMw#Օ˫kR|g0 [B˭F+7vܼqmfvVP026T*uZm8QC2|gG0k?:=;T(40CQ 8[][?{mjzeَ YZ^tV2RUz}c6 Q~74E[AA0j4t[4rCpqqёK"{GIO 4LÓ>'Ƀ^iHťa0TJffKBڥ БdYiIAuyeQ.U*To?<;70kd&SUU 76 ݹOWVMFdX[ۨ(/xlƽ}hizpxDSY*D*nmna㸪rD29=xp<҅SU,t`0`$q`PLZ*p#L/,^8yxxNa  e?iAWOQ-Jʊͭe/]87<:NDRTK+ݷo\ZRq端}TR053[[^F4QXL2wh4D"UB$ uO mlne2+%Zez^(~W/:M|<%bTVy+l'LpL^Bx?0~JUWS=6>AӌY\ZYvEQ޾aa8n{ga OVOO+ E4IomiZY];:rA0o{<γ>`j}mqlMӳ4M>}&r/-/,..Kb`4Sto~r~t|p89% ]896>ohԟ9X\wݻrh;R䤃R5*7^'>_$U<?tA IDAT2yo߀Vaa#/[hX֨Հ5jAipsk{,?jQD288 N|581 3=3ǰ + B(r DJ #Szf3hͬ؈xq7 E\~ ]iVi4=lzGU(2( Jsjd[}@$222T(4 ^v=T(*׫"h$ wTxOΦ0aO 9 4CaTUmnn=R!rӳsrCpp쓡I8:6!8  nt T*+ w&|:$ͭmD(cY6reOj2!Tj`IN8D(#QZ VT)J(H4pEeE:Ng4ﮯLS(|Ͽ P(,/ӻTCZ GbFt!_PUHeL&[JV H"hoo? N[,["r8T&Z|) $b^;8:?PR\.wl8 W*8) #r{~E+˷MF$2(pnj4ju qM{;NgR'WB/YEx%i:Ͽ9۷al6' XM"+,JpTZ,~:e\._JVH3L.{AP$r,,ˆ#Pr9- M$?{Ծ_b/u֝ٹ/ff_h,684Aں߀ hdl|dl إG}w@4:>qo$?ka 7o!t"|O?MN{` A,_؛4Jݾs?yރ૆w4| ,Hζimm@XKs4yD"C!LI3x+L[zk{'˕^l87GFOsAyyY}mX,xg!H&qJJ$+.j&,eKCg$_"rbF! u3sX #W\x20lsS#0}F^) CރR~a'C# sHXdYM75ԭqbXScPܹ3^.IfpvwuΙMƥl.3lY~}ݡT*_OL9]vdž?pPV!Xor}"7}|l2Fc7o__˗.TWd ^˝L%dm:>9UQ^"hgG>U^^AЎ˕"Ѩd|ww\Y (]8U DH;ZB88U˽r"AdwK~+)8( hT׵[w8(%q <2Z$EAqw.bqۺd2~}_9RJ}18jIfsu#hY4'$9;7}PW[{:7VVJE!zwStw8'EHwSNJ%gO"$IJ\EQeٜdzv>.{ ){7yz0L,WE3 aE#B.2nܼQ 8.d[[W(DB 8ήoO!Wp8P"_wyx G]z cee$aX(CP(HL&SȤ/V2_ogԿ >j[u>ùiZXuMH42TjwlX-fɤvoC#,ugf}{{ez]wW緿w2εW4jQ{LXfqùQj;wdY;61ըqhlqiyù9:6 GrL"s S uVp^=v®;ಲuT"a8ͦә&L6:6ql6?0E RAl6N?0Q>_,!M;AD$ |W{z Bl6y<ED0j=88(c8˕)%޿~miyET:tE߳0j.M_8wN-MVP|h?(RŎvBqLDo=;qX ð,N NӉgR4w@0L4 >b:EQ+?`X,F=L&{r{w}p>EQ0 g?룎k%/#bUB\ #0EQdv{HtߟNgRv{j2;vl^^Y EC!""煃(E KdR*|rD,r=8s8T]]N&ST*I&Sn7ϋŢUZ8D8mmRw{<:pp#w\L&Ion/.0°X$E].\JlU[C}*aTCQ +Wdwl9HΤ!'Lv[X 8L:Cpa]nQThln_oX-++<>/ }X\J 8%?p`w8ٜ"2$ }hd ʊ5$b ˰ah\r $Yܭ2N=,ӻ`5f CQ2 DAPscںR0  MUTb6=T*tn#<6>yD+[wOnL9ʤ[wNLN4d!0\^ >x+--&ɱh4JQB 8_\ip7P8d 1ldtr,ef!@453kwlS33}s"JLi4M#B06>%fӃG}  $S˲ccӷA-۳r{Sx1PD ϟ_J$,kH.god4 B,^6*R4 ð|PȋEdxjq{7'^(D"T:!20lFR"`tl"rR~&r2Ax}t|"bq@Q=x )h2\RD(*+? ,2 T*H&SDy_*W+?Xe2\> XH&n}:4ðj|ܴuVWW(*rA < b1_($IHr8P(,*Rd2PAD,lP\. 9\ ' B4:|]+'ZjT*1*xmM^j5JqB!kk L( L*29)6FcKKnHTwWehAd2=sĚݞ珵4776-,J%Oֶ4lfQtZ݆z"Jj2Y82,֨X$j4,:&4|gmP(Hjð*yնVe2|NIs}K 6 Z:uԱa^!o}!} ΨZj;<#. }>SӇE~&L<) w/?߿eHN_#B|2c'&~IgN:rk3T2% Er|rJ!?ޝdfaTZ^^D|3Wmkuxw3d2pkzs/~rOnϦw7pp$vn/};c6*YYݺs0ܺ}ܳ͡T:?=W9Q#FX |kkDOךQe6A\_[l5džSTBZO$CËB Lj42^uյ5A\nO2;7vDei: _r2aղQJc<ơcML&{ EkvGss`SzdRD,2\N_P$T*J"I'ɡQHtMUfO ]wlǃd*Ǝt-0|I3#|j۳Xw8K) Oijwnmj=kj,K+J =^޹|q|r2cd2(`6긼Γ{Jp$-g  +@$ɞ.a42tP3s KPA l5XzғQPX]]5=3 g2Q0㕕:l2@ fH(ljj,u0PY! &^%55ڙS'UjP(UUfPD"ak17^a?`xB3ɞ{#A/?O$(=^Rdj~悡ׯO>x=oo~q1j5sSӡ(`XE"*mwlH&[wd2e`PhoCQtŶfp{ 5{^ B۬idžek"έʊrE4=xW9P!KdM|{S4LEc5]QVYQ~p,/uA^0 AJG=:E0ABP(IiH(?] ^ A|Rmpi2hűXXkjep$(B2lzv^&*+ ZvjfDOccv[U"aNeee K˙lV!-, VZ hlbR" aZZYRa:uxcC}" Xk884#jjZf0ϫ*RƆz@GQbUۚZ-[5c(Pgf?~[?ij@PA j&N^;,*Htytl Xc( AB!i [aY0exHq|h3b*D4MG"Q$I6X)DB4M3LF.$,ÉD24HR!#‘(˕ɤtq AP( G"\džLkD#<EPR4˥(P((X,\&{6TP@` aP ddR."ǣ(* eE"0p1.CI ddJh(L%29Cze2D2pGQt0 Fc<O GQLx<. 8#J`" @dd1 <_EQe"B3 K8 ťekAAFk 쎍V9XF 0lMoZRÁ_#*`8xwײX,~8`Me/ZkN?x|(N|Uvb?ܸu{t|X 0|\.ðB, Ð$Y,RbP(,K\>O3L#4M$I$˲BP(_7y|G}u"˲$IH7oOn\.0 Y,yeeQ<0Ջʪ-H,Kt.im2ӳslvfvaYe e BH:4o3n>{LdXr}K'EQa8_(¹}s"AjNCI4e:'jZ lsS㹳v}>FΞ9rN8fST IoScC{͉nZu%YzU(Z}eV,eOT;[5jQxw8[;;.33;D B47 UձU˲;.7A\.˿G}}XKkuwWW)`6+z45 ϟ[XZ.PQ*mǎ%I>/T*hoe~Zn IDATVVG"QAT*Z{aKSceEA' s9P0=;t?{fv~eYWYQxWW!_e6o87>K[wJJŲX,0Y peA. DBRLã h僃 8?{ftBp~q|>/*/CCpT*%a 2pp8˲c-Gtp^;.ݮR)7CãZA`Z0 jERi0&%\.q48N̬`i"d2CpNgrB./*;{dࠡo"T&5 ;;n^wXlkkW\|qIAG|ON͸=ʊ{6xD(bX{[ښd2ZP(aXFRD* .˲l}}?rFCekk ˲2̶N?Oqʥt:(N/Mү5>vE<^/˲&1pppԉR+.97:R+/;sk 0Po;aw\l6"e{\/t&nxw??:> sss׷7>9{fx Ou/H>|&H&~jl|t|S?_u _|>/: Md1 sg*|mN;95aӹ%H'q Gw=8}ʪ- l. NY-.dbD ը'nO"K?QJ$Cã(/,675MLry\E`AoR@`xieZ]584,ZܲuRWCk zfn޹U׭;l5\>19"ݱ C?_"Rqi5U\.?8Bܼ 5jƆS iw@Ni4ͭ5B̜$T*L6ۺN G³s |^Ru",ӻ\5f CQ2 ;` (L\'UqMZr&&[ J?OL vw?xra`(n_m︖W** *Otyf$~'*ήGu;0M F aa9V\eE9=t*_Z^-/ 4= hier{<ޮv֎PsΎǃCGpI҉Dzw}nG-,MLxX Q;KKap"\0aVKJxx}c3gNdʊrww}*>oTc8^ðB. r@ dGuPC M76WWIh0TJHTV*:p4VAWGRuuUKsd&ATe2HXm6/.-H5LXo4 xUO&b826Ο=-|`rlkm(j~ak xӭ]nw}]mucyl&,IƢ1eJ5P4D0iZ ~x0 /F0郬L&K$˫V&ffIѱ ,K`gX,x/LNM3  Groo`0HUFXs߿VfffR铡QvbGG!h0(򱱉x"x ߸p<Ө\.JT:vnW*s*R!4M0T)A0' y7˲0Hc-ϟb+o0x崶4)c0֪TeRiYnwVu5" *!I2tw 啕}?MM GS8x EݽðښڧP4_?^rl2~{3DOW*.+ӗ/~~ϯzWmkJ¹ sgN/'YYmg8~߈0A=oRX,DΝ9ɤ;;jk RU/] H5}EaL&zVmk8[Vmk2`C}-[=;}ssP(9uGd0Ը o޾jd2imMT*y<8Rx/ Y #C<¹)>i`0T_W[S3Nw=\moo?_u: 00NK$mS҅sG/xO<108ܚ>uݱAQԱT*\r9@pɁ\.wG5VG]8d2pJ8~>Ʈ^46> Țݡhj=o*BpyYZZZ^)R{Wlnm/>~-Jp$Dq?쭫B8 ˮ͟9}\p8CDa^_w|0 p8,"˥06>#RRN/;k[[|{[۩T 4 kBp{<F5TVdR.z̷fw3"Y, ʞgYZ[# Ft~SLXuKKb1 knnD, kkE"ABl2)rKU0 Cj=xB.72T$}~KS#A3ϗY-UɤNqg !o޾ A AE1 d2.yVb(ái ps|+:6 LAD"p$R[S#1 {6!ppa~RMdC}skR:% $iŵV9 @"-uC3aE!iEQeiaAei"aPa.(EeYEQhK&aAQaAJ[ peskl4RUK;*̰,4MCa)m 4Ͱ,SdeKW(x + uCV:O'PeXiXEQp]xDv*U] Ç 0 AE g=,}فJ4͠(Z_[k2`~>sJ?Rzl=On|__tX?s>w:0~}p01E@v@x a8#*`xkT pp85Yq )Q:N}41_w|ao߇_F+ >3o~kݝQ͍{{>ߞB!7TVR)TR:ʊL&[]e^\ZV*B@RI$b0L&u9v,ӻ[(My57gq`LX,RAaE(Z(@$/0biahaa\ d^a&U 6$F;/j fz6Oi2-M^w |MzQG ԣp@ GTp@ _J$ge7o|LCX|hxteF_~6^;_rhlhdtvn~8͖z1o}xdtyxd:" tC^a7*бO涀/RT"ؔexqiyyeu@`X,U(/vu*`:[c.JrV}T(h5׮J%J^lokjWeC`j5.봗/]x2t4_,+A4>1ɲLiuPxp$$d2xCprF!x!( K+Ϟ^wlԉ=]i<j*K|X7JAD"QVS[c=sH(xxh#: (Kox Gu JDTJXl4T UJR2X-ȊrR*uZV+UJeScood4J$L&V*+*≄Jl=֢Vj,ki2jd*U[c-r> -UU55a 榃^3*g: LrUJdl =]T"1 b(t_6dRD"q\RLH4zpT*"a}ݽl&T*ރ$NX<WWUq8EQ L>ABa|bرfH0%Ȅ'V'i>u^6˲J|vn>/ll8^o+Ȳ,I4M`G|v0(|tUs~Q4~sP8L_qν?{;6&gq (J~Glַں=|ͭLe7>ynP( '*p$9a UD"(/snnqܺ LGh{KVWm Ü8s=Njq߯]ZZa勿K\&mjljOFY<+u폌c(勷܃alol?0[;;T^F!:y^Z}g~X]$HJmG{뭱Zd6uA\.sfnP(877/?tnr8en~Gu0 xbbj:O\pvev,Ñ h,'Gax  X ƍ}P2b^V kW/,8 uZm}]?Rt*jja~@T\]ᅲ壱h@f2Y2˱Aˣi*A+ϝq;;s( AЮoac2TeXɔ3ЮwpL$M 5VS'BS'˽~j4 d2J޾x(VTPȃD[tUW;;UjUG[k6Jׯ]˲TJQ>ybffEP( j8Bu|>hwW'еV+ /WVDB'jj>\0rL&í;8RhTBHQS33wr"1mjl ]olb V K\.w׷a@$IB';7~O_( FRϹ%-M Z\}`vnyͭɏyw} D" s9a?j5Vk7njlgG;05ֹE0&4¬j IDATRQ"bqiykTWm0|^6GEQTe6dңo!x G3p2\Z^ ¹\"eY %=Yy\& C Os\F[y[x^q@GOJ%Rt:D(faL:=;;g4/\ӳd2YkkLR+~GNhy~wϮ\.Wn?- c6BA(r@0J XZYw9$zsyeUB83;T(k,QjkkLNum\Z^h4oMD80teA?ڼPTx O$2l{[+EQx5QGoۈXJ?>𡃛 }yrq\6K$^b1kv$IR֕k}J$}v E>q ރtd?0E:x`@_O*` }{8ýJҞ=2N,I1 &܉X#=6G[ -LEFe5uRb閖&eB0IFUT b]MH$4;^ CaR X\Z&d55$IEZ][c!IP(j2;w|kkl <0K2q#~8áV 0L6u1L:d2aV[[c-`p:*&'1*Zf@^'B$Iok3JVk-@:ᰇ#QFmn\NeJ"55604][S#Ύ#cM Èr8>ٜNQ0r榚K-P[3vd2Y Ի\LW:;:*UkKj\ðeSI{e0 Z JϹXBQ3@7֫ ;wS zԩA~8P&,y4}ԉ͎A %@AM*`AFAM*`M'Iw^<!կxuh t&O>3k}&?p=σ }7nAx귎\nT* fMᇿҺ?9P',M'IR(9W\AZڬVN;z>at ɲFQܶ K:p_Vk4z"k5t9rCfw$Ijlh5`v~!/ߺҜc}{J>t㸧wa`^].;\煩@ ڤ7f5 ˱SuuRl{ ý׻uuMZm>Bss e9׽uʪc=C@W/-طbښΎv ,fwCd4;~NO&S3ggHTV Ի\nT*(޺5/ lV߯8JU_ cdRH42 TJ@6[Z^7 !|x/B^#7n޺vF\v{K++_[kܴv˲6\6GP8.^R,oX]=|heŽ|>tP,24c4R.W*r J)#JB܉/dY.KJŇ /j={4"IRP_O3;Z,RQWDzl*8{klN&I2j5(8fٽk`}{~ 877?2:2 0ښٹbi Ţde86d44 Jqqim]&l֍gfmF3`T,b8Fvqc/\" "* h46:63alL!:v~a)8P,,-/8b68 ׫I"wcxޅx4$XrFQNШPȠׇa )Rhx"NQMQJ2Ha6IT^W0̦u٬j_nD7FE&tT*$I 4MM\.h"ј( &IRd$˕FrDžaٜrmmM08VRaBiBEcj8N BQ{}k 5[taRECөtFPP*lVk4V[c'ߤlVT8dd(WS$LJ%fs x6 dR42pe GXX\NRCa$z]0Zy^=?:I՗Z/&e ǫh5ÑEQT0iI|n, `ڵuP*dY.)JA6j4jJ%,jJb2 }0Z_OA8u%Y:eNNYQ}t%l6(Hh0MD˵Bv#S6ڢ/|*_4ȷѨt'_Sg:Z-xԊ %Irc#'0A8~Qld)0@0(Nr\*0aPrPy> R)3٬VUp\8QY3,l.ʛj!I}ñgwf4::ƲރjzK'&~~b1 x<3;719ӧ. yuĢ.sOϮϧ闖ŒasW7,ˢ ֻoYF@{vk}gό?x@];ŦVKd:iinz['헿ٹ]ܱyr7WߨT*stJӟA.ϷO3I<^_>|FI&w775=ܫP<ɄU,,&S\沟]LQTwז7o)J"S'FơNlz<3׽u˃i JQWS&u9._?fP4MeV30t/0 LZM YW2=tƆ֖‘W  `@ӝ;ERϾ5?HQ @mMmG{["D Awl֬ +.gd |s(ry"ūW),WBoTbťSǏժ@0p`^dYA-.-˲D |T=?J ` d2WW +f][:9]\Z>uZjwA(C!Q D饽7a`wϟS(ýv{ϮA@EI|Ye9V\.Gm?@$t0 s9ձ&cGOM?t8uۻSZRbQlފxmMM,qܠכ-.^e(8aT* PpsGT*4M[kjhݳm^_kkskKG2 rYPTh,FDcNJP(ڌzۻBXg&)p7 {vqlVA1 tFVUsr! V[[0 8qJ̑Ç<^^Lh d447y|>Z@ķ+4O2aP&,Ȅ~&'y@ıOM@uE/>Z8xMnں? y[ A^lv C@pt|MD\}aaXLĚKX<˲ MD"h8Qc=@63/40,MS14M3 J \.k810ʊ[ՌS4H$?xpL6ٹbH$Wjj,KuZQݿVWV#X<`[O&S+D"HP4Q $966{xRugfW,!|q[KаZ'L&Ӫ37`!'mWSӏ>N?\X\J&SZftlh47a#ZDi7^o܄,+Ĥ,&k,Ϸ#/MW-~3u:VMgVؘTRhޘz`0<:ef8B7{~kmGgmmn=;'w -ԉpxfvPTM?w&L6׷47K KN]R^GĽ#zn$`em0=DgNIF' ~&@gG;ACǏ!Fk͇;wlpj&wYOBW:wm:MOϮ}~0Lףޘf{Ųj $^ P$gvRr:p$a׻lZZ $I)$]fw_rJyCw$TÖ$ 04Ee3BPYsL:STΜ> cƧ:ߞ "!P4MűZ{ _,:q{+ $I{VN69>;{ZCM7nYNYM3ljj(,۵# zZm1t:ݺ?PhL&#˲۷mkinfX6J:ӭ.CӇd91Otwmܾm~~9x^x{# 8l>֖H&ҮgZ][kIh5ZB0f޺٣GzWVL&SMmlE"ѽ{6WEQ(emΎ٬Fl6mvȟM(zu2a pw^P8~Z`yP&,yQu͎A @AM*`AFAM*`M'W/]yJօ>C|LXï\? ^z}tl?zM?7oʹRh0U|+se,b2*ʁ`k^lŁ{(>,o,Up8_>w/\295 K #$oT #ؾM A^&L&W8ьhBa>M;o뿹oS*:pRljl(jyycG.\b1u:mkKZq˲&ZFd2yR8{>assăe /,-rk} L63|Ç)JG:k~כZ\ZN$$qW(gf皛?xS'|*޽kظh0Ln0#bhɧ;{fpN=ߍ[# Yw߾; /^R].]U2 8n4dY7 &q~aBxh4c΂WΪsp7Z'T*GGF˕ IAD1z{[S'xw{htKg<0G~;X<=h1=?'\j4nO6ۿw &qmݿ{GR 4E o!b (U֖ /?^[gYNDJO&ʥýw9\JX,T*|k7oE"Uh[+LNM&p ;8xfvii[Tzvnt0 yCCG;DO?:08,ښΎvw-J#ѨdXLJܼ,gN_X~xܙL&[[BX BHR$b6Jp8$=z<p`rvkx,2 DZ Mҙ4dzKDS,_:tܼV`~xO8d0EQJ8{mC$M=|u,˓sn(VQCYswٷꪻζgH4*AGccG$ RxԪMSԙS'WM|Y{@8>-o-HnR[[dZ_=\zX,J8Q._DcH$SpzX EcCC[[Fnnn G"@>'Nkwm'G3Ւq٬x<=;:>jj,hLH$òlX[¾`8jN?nkkX,r̬h:tp$<R0=p P? Uw;]],b_-IݳEʪh4A٥rcYcQ;aZO[r;4j52Ja0 SMon1nܺMdmh0LO?NNM]zkK'`fvN$ bY[_a+f&BVUhRg?tpmbٱC|@NVTgfZ&h&]RܿuFFKWZZ^X KgJ;\q(0`;FY}[0 qjJu2W cxRY\Zy95rP(].gG[(C#|4Ͳ9)Vho{vAQð Km5˶]RA ֻu6bĝrBI$IbfӶ.2 Ad2P8P(&r]jqa0 KR@`sW֓Dl`m$ɕUw.Vlx>6 IDATOtm\uy߾O>۶mXe/hͯG1ںm:1^EQx0DcBH"8֩bt,X,H$Z*kkjZ[AB!ҡqOēVk-ݾ*N B$9ho_]uiR)AxJd(}7 Zt80 ;}ăILf޽5_4jAjh f1^ֽplt#cjSӏ V5M$Iq]Fx&vueשUJ?T,˲K++E߻GbԠiN0N}`#3@4CΞ~Q˲Gz_(BA%ɔJZ6JKP0j|kD'JBSMS$}{ ߀g QA^ZVOzDQL$,SE3t($aT*Jkj—e $(+q'phb1'HQ[VIV*ߝFA+`$lŭV7۬l6k4FyceeYh4RQRi4rP*?bY]AorBp)ELs| ,@[iPI"-fZݧF_1]J)/b <|b Bfp9%;t$S+?w!+@Ai`*r^X/HE\ÐT*,KiJT/sB lUK_-*B^)[Tt9rF(CWUR9\֒tPZJ˓"[Bc!qъѸZLXRh@2]1-bbZ"19Xhʼn )Ș,. KRAVʒ.IA/Pأ|%Fw(%#-+ ũ,YXVX-:j)$aS+K blSۡ^065)řicSX@CO_![ ?۫0iFcѐ׎M i@l׈E^1py|@8Xhc.`a-̑$7#z 08hWIq@tĜD b* ЩR"Ri\HA ۔R[Y3/M#>(=5V TIzHˊxA²"֟V ȈPKX%ˈB\-)e @15)2"6b>ڔR\;5"b15),ʀ˂ 8\6PEzA @RTBIsyb:K)E9\2&"d Z(&/ME5eE\1TR(KBZ0#Xe@`#d+h@ ႌ8\֑54:%6j@t &F$`(E+%,1Q8B!J5 %@02"֦%\15!K20QpJXRJ ae`Z8n*xRʒ z+(Ӧ<6!fcHTrNDBZ*|~}!."&ɠVEBI$IJ$Iеe&O$ AİI dIM%ތ~])-`% %IxĮ d Un:T#o:`wX LF+W(H T!B'y 0#?6=}^9U ?H:R.Bel*d;,| *_OΣa Ok58iu$1y,M 6şyyb4 *`d=gMЩp}*XJZL \+7~=Z̴T#ȟmv,t\B(b(xJ (+s ~2r{<^Br._^Yx9(?tyvnOGunwҕd2H$77?xo~ʪ~tT*elȲL~bqc:mƪarX\_8Y]v]V㶶ٲj0!\sTzȱG61ty! r}CgfJe9R)Z#h\ ݳҕǏE.^1[v-[:>0'.^l6ݺsðϝzP8|g`S'}v2ifv~\.wZfM3(8A?zWxnLu[wWz`C+:p@4#pHIΟ}$vquFwO3۳gW\X|~ѐ D?Oq5~БCDbhS#{] \}} 0 E1Js pW&^g`G#^M# 14ݽm;v^;yAe~hîT*!;:ڴM$;0F:v7o٬'kkvX[:;s(\$8vʥD2dZM$ X*yˁ{sz}dٷH|ȋi }ٕUnܼ֖dT:}iR>zDԪۣT)>w64ֻuo ~R`jı#d*Ѷ^Ϸ&zR x@4۷gw[k @tcc`>tqܷչ#ȋRWg;x`R) ];$I0@d{[+P4;*jITֻ\,`#)zEQjUkðᑑO/\ٵ$u IR[::o G]OӔ`?LNMS$qah^KR( FBqw䳋{vlnn z֮-WFc[ H$6]*fh 9K&SStFRl;C$IO>`6)$I"Q͗$׮߷ӋVR;szhxXT~vR:H 8e&agZXZ^h4z~r!-ŒR`h:fK:Yom}vne^vt:7; uXoRnn$ /= /t\?[ݫK Bܨ Cr9.%ň o0 ,KRScXyyPl2ǭ5 /jGAM*`AFt, N*ăIۏЀty iin&|Bj][[X|;01 m#DzPBtD"L6t:ut:|ktzrrZRvm鬭$i1Mcm^ORM@0(IP݃P6 ˫kF]T:>Ay[ .IGF68kgfhŘ a} ðD"LFwN:N5aBFAmycSSG?4ͲL\VT׿׶L.n`uu IP88B:I 4z?ov /@\j4:)$IzX?:TJ}4;7dUe9omaq -}7n N/^t:'WFdgfJO[ t:384T4bW?٬ff;|k?|<:?]Cˠē9b GL<|Jg2}VV*rv~akWW>_0wPJBh68[\\HrfnW*4MRtoxDV?0095=to~l|btla_iZZ 7EW-~jFj8~em[#?P[[T( }X\U*EQm Ah4vT*rFQ}~*\e `ٹX<H$.l6g;IbR%IJ9wvieշ;xo8}Jǫ :\NGXجm$Bu}ehe2Yo_\:6E"Q =v@TpA *|%NWgp8,s&٬lm--<+-4EY& C\˲<7??`a|?jk5fm6܂JRbs?04f9wzك:7I;wl nllp:PhhggGϮ 䖖rTTff,5^]Z^hBT*=84| W*DBѨ3,0V!T*X\$JRRtdTJ$* ñṟJbf{ ^&hA~ zWd<};J6+_βX8FV: P8M'Ik}Tꙧ |օ Kt& ||փKW=&DQ?:vx"qoj.dY *׆w3 .I s0o=LX`(Ӟ> < OD^[\Zro*a(W@7$I3O^t\tNN=T*Çnd9hkkK,eswD=ֽuiynܼT(jql6ݼ}Z[H$=N۳s=Q]w7 0037W(n޺ޖC}{>xAc7_^YM3G*&r}{R۳}[fDcEx?(2(o8 A(?/M"n_Y:\Ey}un= aljYd2-MSӶJwO8vNHOwkۻ}s-.- Nd7xX_9|hona{yum=rnnG?Ae__nR X"@`lb#K+]?Ho2MM .r |F'O8615dp̩̜nm)UJA@UD"β,rѱ?dYVz^M+yC#~!"ia\e8ѵlN:v׻5=;"sU]]xj\.W*u&C@:Vҙt$QFPA@&Yq յxVVfcnb1͎;<;@ (ӉIO;;du5NVWU*'N{zfvkk{G}ͭohcG{ Y,fsycsumı# = Z[5Mpgghttu}}aqtOL=ڳ}/J]):ٍeKR,|PWTWgjښw~KV*$N;ѾOP`&@.m8~eǎ  < X R4E8@:|^`ضOr >AGٹC].{g'l᳹\BT4U)AC.WUٷ}:VRd2 Q(L6R)MR#|(7JP;}QF CBd2 rhK;;MRt-Rx-͂ ,/VWw;{ЩC]?a a(Er\./uNomi Hd2P_w葎6Jð(Y`fK˫TN&MLR3 5UU?'jj矘*E9rgNhnjTU\xoinz2v{>V)@Lfiy%IN$"znmm#8v[%MϞ 1s3/TW?(J CE ֖x|2 {M-~NEcRp88xAMx"Q}~X4M"P0h<ͩժX,.ޭmŜL&ٜd2Rn1 Sf1Fr?P0 @Ӻ=LѨ]nhhinnm) $IF" #/L&c~@ fޭm\n2ɔRH$8AkE@*(5ju,eDRȋJlmil|A$IàJ29MSרՂ Dc1$a(@0dRR:6hٶGBlf X<^Q^V}>lfTn,}LCN"H}٣'eobq\ esDAO`in42qTJ0 $ ͆aX8^ ewD2j E1Oj4(L&[^^dJwk$T*m2XK \.s{w[KkyTGwT<g~y<^匜\.GQ!zTK^Q/4$FF!tkFC]@?S2V775~pn8K}iHdO(z矯VzJ^> qTT?|_|ǿX,K9nH$I,uJRtiq"RgҮRRK$D=I(bd4A |>L@i q7E8H8R^^ PY(|~?(0 L$DX,AZ&$iQTA 'Ng jˎE1HT_zy>|u9S4I,[(r{AHRJ+r9Ǒ8@'-`+˓d2R0r |P IR&I6 O0T|>`qb6 @NP*B:Fe9Zq %/ezɩv_U_@02>O}t_*]uw'mph$wB#c[(n߽S|F;B?[ݧ9[Y]LqrY H;c ǀPA`A C5A$E)  DQ,lmM’^3 n}>?{_Ůoܺt, C^~Ҫh0 Dvjgϟv_KFnokV޻_,?y燷~342 p} fs\~.¦˝JnjԻbǎ|xz4M=-"ys \ 2e媩v4M>y'{3̻?{EA9 XX\~28xMFc?vԉE+*ʫ}Tf #;\n ?{t_BXe9Q(MH$)N(`2ƆSceؕK4jo,k(_Y[Dv}@>HOw8܄a$}-}#R]mwA(/+ G{}1^ZQ-X*2BlUUvJe1uZb)nz"6Lj6n(d4Y,Y,eeZ-jJ&Y,"IAoV$H/eyE^|qqqǰ|>o6BHeyA( Z[F!I2HY,_7_²l" ;DeÈsVX,XY]=wM[>V}yK9.>m˱E=#Jq7lǎܽ׿kmizN;H\|ѳT*e9>6-Eԉ&1Lpәt:xytl'\^tqk{p"x)04/<8nuu x<1h6cYWV y5 R(&hO Lj |+3s ѨzX(L$F˅t[HXWA֊Ɔz$U*% ǎ``b� FT*Z[IMj BP_wMMFV(*Jr9 &nݹ{HRLR0 u:R#Al(av^U*x$ ZUZ%"I 8S?q\TJFTJbyNC]mC}u*At&|JO$zD"(R0 E~Ѱw\.Өzݾt:e܋ҒewzOttJ oZˍFcp'48~2ruBGvwEQOOLM;BHdwg'd]9·{/AP( y|\}e?/,QwVPɟկn_?Dd(P_Go0܂VaD"t$HdwHRM[d3 w{Z,SӳJb'vnnfY`66&A.׆sp'&Padbr:hl6VɩMHdfeΆyRz;;&qyeuw7T*}~G>>? u:UJU4]]]3 D T:iҙ Ak.h4H)+лǹ \Iqji'P>kyeU-yrp8b6nST?xძVYt{<L&w/yn^wJ 08;7r *ӧOn86=~ wF' w=K+pdvn>njl p߽=eHЧ>&gYEf{9r܍[ws =+5ɤ}֊7o?xH[wbјsDrxt pbv 8wb1t)?T*oKY%d2 po_*Pj4*jm}ciyejzvᘛ_i*N/.y^@EQkLѓ'Cw-f7f}G7s0ON}ɟO2q^B_簾g B0\(77]K+(OJK J^&7_?EҚ糹lWE 8.56eWb(du:myYYŬ8c"0 d*]ռw{^@xJ%J3B 0 E FaX*fs,?x Gܥ}|}xd)ɏVl6 \.+2L&cY$IQzh\4+(/+,DZ'gAm6ѠP(b&Q*{e%\&qxttkۗNŢR I/dsFBXgPT䝕\> ȃGÑp6ӨF0F\<;lf5vBQ +fJZ _0L&*@XT*$I l6@Zas^.h6].m.b6wvg0gD.y~phAR -vA7hpEJ FVYMTŌaXkKs:. vU. v\1 22:^S]Ed*VOV֊%=nM y{ҒP_u=HO(˲VYfkkcX.ook]ZYU*u'Ou:mKSA: ڹcG{L&Rh:aVC`h Ml Cz]|'&9T*MFaF^VUZ>nQ5=3W((XbXScC۾ֱP(oql@pghxVmԘMFaX xjk,dJdBP*d2iUb u:MݾUZ f_KNߊ'E*\RRI_I$Ogi|d6\C}'mUv[Pd` KM@XT*u=r7x/Y}p7)yѤ?fsX]m Ր$:wwcϟuh0jk zѨF[Akqw+;sCkk1 [[_ںAn޹hJEMMߧiZ?Ш\.?z< ˏ|Mu{Wz:w*L&݇'=qEQOhR* K4M/,.q, G (ӧ//FcC;G1 UZm־u<{[=rxhxTQ<~l䏦gmvvnpx?{oph12:7^s87z?|pWW{?y8s$E{}*/%%ff/?;y' ۳ --A#u#cdrna!ϝ?{fqyerj҅x¹r\6Gl6e2m_&y~zf06>u@*r87) Hi$@2aff- P|w{ĥ [ۇvƆt,?{b)gOG",;42u s~^ #O775f9$WVcss{۟dX]w8=^sHlpG$:Zu2phRiZFӵM E! DjFP)/+<`뫪lSӂ ,.-olCZ'^Kc ؄L&CD׳,n(xϔh/( dp4K$ 'Aj)pRiV395 ZٜZ+6]}D"H/kdte27 ' ($IB wuAE;>UC$ѥ,-hJG ⓵z_tR KE z]<muucT:LtJ$.gcQ(ou.dkmB`6WDQ{?ߍFkK+l37o!pl2y_yۓ? (ZvR'6Nx;L޼}ܙS7|#=Cã' 0x춍 L&3f)T/.-s73;t676ɩ_a.?/yn8blVl|}d20x<Ҽq GW?V0r\~hO.'%߇a@G@ R*1 =s̲'JqH$a8v s38jx1 ^ XX\JT$Udweٯy(ޭ\.t)m_?nu.q֊bDQEAV1#⃏y<ޅ*(eK ( "q/-b*F2,,T@oܲUZF`^P0ح;w9<;Fi=5=d_P*b#/48vf]]LFG"#:.sN_<(jddLPg,NBV@+p^8NM@3Ϝ:ɤq8K\ȑSӳ핕O(ҲHnwvT(zdtyN3,˲\^Pn2d4];  %x==sгoƆ帶}P827ѓp$rᣟcAFcC I?xW.o~r{d2ë?/qܿ};M ^C+/>~2HQG>Aٹ'y|>OQўm7n'$/pr#^ e&RsVbDcG<o4=uVXdjku:(rwIJ:ۺE6RLD|Y7_m_+I: ^|EQN8P0aj=E(04Et2_xO"[\^YT*q\)|>  <<_ f9@_}8?}x,_\rꬩGc1ރ `dQ$% B&7_9aP,p i+ㅗ|\.W,i(/<@a.= d18c֊#"u:cG$.hQ"VjVFQ̠յ*rnrܑpd}}ffFR4l0#bۢіh,jPnE14 Fg-vA7h)?gFQWWٷ}>A&B46rt:m*Fi}JNUU }_BMu5-XYeqiju:7}HPc}Vu_kdzU[SmUwK՜>ubzfN*&475bعc555.@ùZT*dTUU6hPUJ~=J+BBku\.PW[J~A yOh^XZJg=݇6].ɴv3 Sij4jEQ,ǍOL0spmMá,ֵǎ:77Rz|u Kzq"a,;04N?Fs}}ñQ[[PWH^zR$,䅆a'*ښښ]"yIӐ$D"R,H$*`<]-l:_fiw"(y+_!A _?y}GToTK^u}x7oo|~?w['xn%oMƭۣcf.޾l6](z(z%ϿG9˞L$LLN ~AXW \ý/,.$Q]UT(P8 |h=yZX<~7h5Jka.yvngOxzyYZjjhh>B҅swz=H̩7nzv~? G*}ZQO$ħKG"M [>X^YrǏ02EZ[?8vgye 8w~kmcK)m?Ohz/=x8+ϞPznܺ4Y])wyy^ycW8#%ɶ}-k1HdsMOR,ǥҙkL$oݾ~Gzў f@`um̩+gf@齗JoQ)$A\pn.Z K+oz"tMne2n4j4Kˢ(:r}E{qi.P(O$\.Qb1FKSӧrmD01 W6*U)Rȭc+˫eiE6*9^xO8&B8Q08w ښjDԡ3 Ǜ_鴏`pn괚ƆF.dX,!n4*^kinj\#|^D>70ֱIAX,ٽrs2 zѱq7is\&M3:勋KϳiѓAAGjU8^Z^amc#H\<$aAN \vEDR@0Zr Fkd􏟦RIX(C+/{vTK$b6>yaF}Z &I(ncnC1 tժ2R0lyeUšᑫntr87sO&ekpxqaթY@K+EQHޭmAԧJ#=ݝ7 u$AV "tfkk['I0 UUejZQA ޭ'NwƭD"ƕB(n+@$Iz[HdnK(lVZG((؀aXSc P eY$&ӣM (47 HӎON ( FF&yn"4-..:~,3\`lJ%pbm*b۾0 b1kuZd1 e9е ijlaPxcC="0A] S8 $iÒ}#R 78np:k0Wwk{ww|,/o\t!"qdsEDz].߼}`:Q=n:H$/+֊g#t0u@0QEѓ)h pQsdڍFd d* `0 PxlbtF}>J=4\._z7^4,`zȹ Xl~cL&ӳq?z?f{,˚U^;!"zM&dzi d2L5P:x짿6G짿;a6d2L/R: IDATWٽy%  7vwlcP6Xs+0C0F(:GVGlR**[ᅙ;S6ct*<!(hsrzmd~~tÄ|'(\'{L6{Bb~on:;Uzbxs|ۼB¢f62jkEQƪ5Y86Xĩ3-,~4M;{q=d3䡋% |Qwɸ0}NRq:"ի#Ez=FEq,r'@\VUUb|TE[~f\{c.%$uQ}y ^e7RTU]G `c1?~V&߶㥙ۼ(/>8MUT+/еas'3?pC?>bV4nGdzνơ#GwuuTޞ7ǚA5=(tw^mmmBqϮWGFj5yu,ˌOn޸an~@{gCgnPH$ڲy+}ET*z{r${[wj}{>(%=w"FFo =wٹ7#T*0L{[GDFomݲi|bZGVw9sif&z{|Tڲy(\ÄݽsX,~赃WGF&wgf4UmooJ$ܚ:ٹx<%Dwl:r}XN^,$-$SiAuaY7o(۷fzzi]_ڝ;Oэ70f˕gOޯT*BqϮ#7lVˎ6?}֑룢( 7J%HQ D=j4f@ՑJugx<>77oBs:׮6766OLtu%啕3s쟚Nү/O:xjj /Ɔťs7;3#l;S5}-[Fo!;9ukiyw޾;3ޕH$uMuK-A;쳼0+wu]c8BaX!boOb+j>3y`wF/ rB垙dzD;wOlݼ,` waqiqiihrX'Ob^?oϕ:?ɌONXZ^^XXBz;3ܭۺ@_\rcyeevnR:EA}^h16^yQv;pʖEQ'OeڹХKٵͱwiq:t-MNݲZ,۷m8|yqiyQ ;wlxp Cc}=677sl|brjqiYWuNQ]/b1][B7nONڶu !;Ri3gݽjFB4B?4|I5]ׇ.^rudWNȲ|#dyvΜV _^ ../N:/_zΝ۶M߾KS]vLO s燮\ٷw7L-./޼ygfvjz=#7$.5MQ@8jKɧGe}{/]:61q~b*:qLd5(jOwjR JVys|H?~T,tꝻ3/ܿbbb#DQp8F ë!Oi^}]Is)Ͱ!>)ui4Ue9cdlOQm[udLkΘ34|~˹izQԸ^a7-.3rGEqy%LQTsscoww}]:}v\.475_tGVUY-bB`XE;3ktϞ>{ [6O߾=61K4674^Qt:vDA X,TT8Xp$r}f}]cRÑUIuMMߞcV"++˩bt*Zm榾ޞ).B.O$xnKxQ˲D{l|]ooڴaCC}sg;qȑ+%ћ?RufvoRtIϛ)ڷwgG_;xz Q|Goׯ`fN4558GAx`Ɔ֖Ҋ( {-J뭯;zD]]jZ;?#}x!Y4/;~rgΞ G" vfF"K+ _lܰ~(oz-ܴj @`}w8YBO!|g0XZeテ?dYP(}=DRQTY꺾` 0r\* N,_X\*bsrb8c(Jd5iZ8Lݺx_Gwخ MFicJK4ޞJjOu)TUu )~]]'PS {pKUUM7{,kh6UE^;x ܄tR)ܸ90L>_0'(k罟N~8`ReyMwbWyx2Vy\*l*b1˩i]ד$!DQT]G:!DUL6* JG"txRQTBB`LBFea"T5MuEQUG#6~X¦M7hhڳ{'¸RblXgY%IZvcƆI(VKGG'G>|^"r6X#GoZ3 tZ78iZKsj4z>cV%L,+IbjZ*Cm++.`[twRjZW G"S:;%(QKjreE1&Si*DZ(d._NhoZ<ϻ\x"10xjH8{:?H<رm+MQ(ZDQX`z^J}ǒ r[:$y^!8P1Ut&kM$P9VUd%W(+JSS++ᖖ!$H4/ENG<l<TTeYݑH&eP_dy>J|oKf0}WO!MFXZY߾[unSn޴[ G7^X^E|arq[͛6<EQ!(*\./rC}]4$[\)JJEQTc^oRet@  c-jcCC8!|^UU6+ґ(n;c\.W<|t& /X,l.r93,F>%0u}I߻ކu}dzu~IٿցAXv]x !M3,t8DQT5t"ifX3.8AhXNG"!xܱXLX\NW벬d2rbt>zSQSSbad*uf =33'ΤRy; ~, !LTMC) aYr5U+b1rYUU͒GU\pmd&gfg߸Y,'oMeZr ,B=vs|b5swfiiȱxͱ5P,Vk#G[3=&Ic'*j}}fzb~?q,W,\ :z /\x!04Bj`0Hq8k?b- /B XG:ˆ@뺦\4 lw1(p[KK}]By|_>3J!n&lVmj۳{#SDQlm <'j5/"_b|b2Jt:=wsS#EQzib9jHvj7eVQ$I$%i|rjߞݢ(Bz'ZZBf~Dc˗[ZBz{Jr9 @]]R Blo*BsS筯 +i^c$)6` %c.+a=h:+ zi4 )hB EUK|:i*ePcEGJXe2^V߶3*0@R0h ` 1QUUQUc3Emc d2L//iB1!acB({w8]4I6[.Xt:8.0Ƅ蚎t$˲#D "aȽ)kuL&d=< V*4M,v-c뚱H8c!XBhEI z B28VQH麎1u( `(ri˜@#c1clЩ3GK]f?=r(W11dYcO9#\>r`|b24|bN"DL&13S T4M7 FTZm 5} T^DQtݪ1kn,@Q/Vy0x"jj00&iYݲKVdS|^,R4 ` 0 0:A,B 5`0-`SJ&S(~~!;;/]JQTG{O475[gCťD21;?~ׯ\/{vTf}{ԭiewl:19r9O:qcl|y%,I`hkC ހk#kگ/j|)#BoOf[Y^~}f }{._VTj,7__}E\$ۆ )UVS.Ñ|>r,˰,,t&)g/Z(,0bJ6an 6a[VB( ø.$ny'lV$j@EIJbiw Cьq cixfY(aXNX9^ez'<}t&v{_oO" :;g&_:;m6\nt*aRiB>S?Gw66o޼q%'o~?zl6 yjZ''FHV8ylXru$476}p8VQ*?p.G< O#},-/۳gaaط̾/Q )& r85-b<7h:pdc$ˊ(?LTT?xG3L{+&aRQ6] (w+pS}@4#z\U5]T]4t~qEvdpGQyB֖h,vv𼪪[7o:30r:/]ʲ295}͛6õ{?9q ׯ[qRTRnݼt\>61Y_WiҲ۸aYo߿8_|b6QbіLrBscCWgͱ@slOj%j4jZ ŢF|a|UfVVc-v[ǗxLJP,{ĪS@a19H0 q Xa#A2 -P{]88A~\.'!ᰋpؽOXDZ-!Q)jmmx|>(:y{3LiM5}KPܼbijٹ{vXv}>^]DQlh16!hkml6m]uDLkB4dWwl7Vp:H9CFYX\ |>Ox}HdŘF`$r&ݸq 1 qW%mk3z!o1haq;ɤbm휾v/G\Aj.EmfYl8&hP/+, d2Lgk2L&.5A$IENDB`gammaray-2.11.3/docs/manual/images/gammaray-wayland-compositor.png000066400000000000000000002110161413310443000251750ustar00rootroot00000000000000PNG  IHDRwmH pHYs+ IDATxWtI(HYޣ0f1=3ݻs^AG:zБ{ݝێMZx0{YiBd$؜XȌȪ~fTX" <_a7AA~  `A9 ,ߟsciy(ʭw߸{Ec1?FcGJ>iͭ-0vR[!y>~L&sN,D[߻s)ϝxc+NR,ˆaјJ8 #FBil&J6D658npKlPH4RUY ! #(X̖d*( aP8X۝LBl6bIX0LٜL0 s98)PsM?/]Np8 JK c8f2"+Nt?(Km-FO8& {n8n0N8v4 bL~ӟ9^U̬?<}D.eY6l.wu>+23; ӟisӳs X-'Uj=NeYU\ORxaY,KTHR]D2JE$IbYq 2$<_pHLD^(Z,H$NX4*B x>ϱfK3N3<,)yl>u"LGPd*zuGN? VN Bkss(sWA`~EQpwhSE804`8!d aãV@ťa@dkkȽƆ'UA09>g0˲ôZM9$I`i:I`H4*@,g0 d2x"jq 4CGST,PJ1 cqt'58aIT6wfB>( @i`X6L.-@VWWݽ'rm-Z Y"# =<BH&yj L8uF,u\.Ḑt&CT&8.L/,yBI(*HMQ$JD2pjȲ !TU H I,8."A dYEi ˲,SUVD2iXUe$,(BDA(Dީ\O&Zh0d2l6k2lfe8D@ SdQUUka&5\eXe9Z> z }Ay`"pXgG;˲_jGIɔf}RG$I|C})ޟ Ǐ}-ܬ>؆! /gAbh0 A  `A9/0d2%I_ _yoyXZ..+iCBo.'H1`9;n@ce "C:LLCAe-]@瀢Ha>~2Rb1.hA9(# ȳ( ~g0BȏAD,vs pye[ !e5ۂ L&{j':(nyuuϋQUU^_=$IDAPTU Y5͓:,T*4m4hNRPxzfYW-˲Ϸk6$YH4yap/Ȧi:yw{=gII:@ X 2AX=`%)ŷƇ~&'I|:j5 &$A4HĠ7L&vw/AhC{͍))JIqtliBE'Hr|E'Ncqxn^Ơ O:P_߾~֖=7ğ!ȳF_nH(:JVT [$!_^`4[=Wyw7Wf'+jv;EU -ОU^5p#NMc=fxo'6۫Z1I/< wYN#gYe.JF=%caQd 讨0LʢwuڇddXnkud#hu3c}7fw &4Mj\_7G  8. ͕l:5cٷWʪjKsU͑Jm󑾫Hѿ ewwpd-> _V aم6(0B[>)iRΦ{߭h4iF,/HyeðOlmyyaWG;@XץhJgmEQu:m2h= ͺkmi>sGDq8(zndYQ  IΞNB$q`"H;e)I Y]_in@%.Y_xcft8dsi=T5-VNo-q6VƇw3D]pJRfV[;y0\eUMtڿuWM wr4іUs9*k &H&x)닳[+ 魕d,c,I\BXp2iU,mi&vH.1 IQ$I]MSj E~7q,kي/0 l.JkJCw} Oolfs9\%pfBS*l (`F s~tui'vwUWH$S}EQ==;g1{IAI2,A COޟ)*m?\pjuzqFunr*s T *Eagc$L{ (6ulz+A:Q"=iudݩinByu=͘,6ьaqk:ike4UFun|(>wi}iY-\ 5Mm%rciHA+j yMVV0,VϦ &ԓM8`vl-m0YtF(gi e0rgA7um>1kIvWi F : ݭ TLmsϑ,+WUV*Lڿ) y*F7Zl4;`5)bY-fsiaʊ ( :][k30,֤3YZS]DjkOͨP,/EeQh4& e%},EɄo-.әT:P޾,Wy  :5=JeEU34 lnyo=y6 ޹D/^8 e[ O8^.PYY1*0M |n2[cGG\ξE"w{FCo@cCh[=961cX>/oNOVM3֖ApX< {c eϟ=}n!ՊcxCCݾDWGwg;ۏ p,{Ύk=7Z[{nZZ^fn_]m͝}8rɡc]7o P*l~Ƚ1ʪ"+^<@404<;7坝_][ܴJUWU _]_ h( XdO?6>_LƆ\ʊ tf(7ni?V(^/nLNMON/,ʒ~?h\]]i6=ƆzdHe7A^0/[޷;o.~ $}rzWGvȲF%%ABɤ :"IIAF8|Omoo+v4w}z(Barj@B\N$EU|>f+@'w0ֺNN[(ҫo(*TMx6b6[-fA\AQTөhT?c(Ad`(pF fh0 ðNZVZ8@X|0 h5b4Eԣ"af_(F#r6u5_xwff!p aoڱ]R[SaLU!Z$76;˟\{W/MuAhY-Ւ70tQ767clj,`$. j1 ӧNllz[ dhf.swP_WvQS])+][߸p ˲n7Cnl6i5Z^?53[[SܸUcc`zgyed4\oll8rszfcSӳ\80LYi675:hZ+E2ʲ,¹K++]l6 ݮhl6^l6c Nt}}m(i?qdx^ScCUUdifn9KJnmi!WVU:bZXu+kxa8,)q8&NrH&,N(/++,Ybqf}p͓D}m^oqL6 ј(nFSc666i0@0Fϝ9lZZOX-ik0a C?(/'I4y!}ġԎjÐ  B,rVAyi<|6 Ia,,dK2RbfYFé0L<mV+I@f(J4cYDZ\Z-L IH$0^F$IZC|AJ_ IDAT9|x: b*d2!@m6k4K&S\NVdŜN&0<Dt:N݃X|^Ňb~ӟ|$pqq+#~#VVffŽs!~ď\xAUՕO^_\\VU(r1u֕k=Łm2yaXEEy7fʲfJt&)$ݞ jEfSe0|^EOY)Mx\pOYi:6 6VM$,r8__4=:7?qܶχ ?$HZյRb9ÑϷfWZ-0d٩Rkiy%L h_dvnj ʥW.ܺӫ1Y^7sSqO$I=7o~Szwvhwwq+$^o7dYVUınI&O:q|tlñ@&E/??r)Wgs< ܙ}A>zܱCLV4M>/TWUd) +dK$Lfh4;}ʧLsS$z xx<Ŏp8,)Ax^?C:iy&>Iv3 jFd2 c1[l6+A:0NW\0fd2lVNjkJa0 ZzeYN+abZ,$I:v^PSR⨪x193;~U y!ol2 ]<  pɱIQCJ׻3;653fҙ֖omll>no:q<fsb6la88S坙_][n+ 뛛'ypqZΞ/7>Ken?wveu jrnllVUU:%\))q)#3T0MϺ)komi٬pXQXMu(5ShL;/ lb8LF2MQ4M8qPzeY}fu^?37_z"E98{{cmaq)Oh4NsScѮ[w$I81z斗$N7qhW'+)Q,DcqgIɭ;w=dbeuMI h8p$ɷ{ (aWG{6AgƠm EQAaV%Hj};Fa$icsSd88qP&Io0 Ij1?(F$/mALVVV`nok4Fñ,noJ"Ht: 7K#F2fiǷ[ ]Nq,pر_O1hbF!:VO$4F ,gYN AHIgʲ٬fD4M,S(kc{{G:%,oy/wv*OӉDR$G `Cv- yLuG$٬@ JoX< ff߸lJ3*TYt8ҙ TUB B~}c3J{<.3HV{Źpl v5W,)q؇GEp9K\aYD/a~-,.47=#˲Ykj$I6JȾ K!N$A|(kM ϢA^*..$Yϴߊ(}0 /BRΆ  /2A ~~h_qNv/PF~??Վ oKW|I?7f_kAσ% i{g|~`p_{. CEH4Z\i~cIDdYr֌O]C#$Iʟ?lFYQ76FS[[˝}4M|'mz~AUU݇x2,Vq֝L&{o8PW[&{[kVu9Kgf;:s׻RUY0J}mmc6 /eÐTUdsH8YVYEl?vݾ~Nõ67237_,!4zͫoɿɲ|陙x"!Ip8bL&c*ijh HpmҙL>89= KXwX(D ᵞB^q+hxFdtZ̞e`ؖw;I*&n\r) 7oQ@Qt>/))q]tOhkm(` {h|euh4.-`SV !VYY^Q^~1$?xﻇxAߛ__%ʊo֖&~:;D"f~GYUYY\.Q$Ϝ92 OR@sS;5y`Ο;(S'෿Uk4 W;o˲Yj=$h4!;ðgOmMuqh@|uAجn4 AA  rPF|.v+A!!BA$UUI$ B(2iHr陹ǻA I$ :0 qh$ɒq,,ˊFTsS#㊢ B0UUq?7yɠ.hAb&8 ՞?w&Hh5ںAo0ϟbvw Djbn,9Չa2i\zb$1ϟd2\Pm$I M /rl6 6ՋdE_Hp$ ګ}cc+WU}UU|tcxIIimV,ME:p$ZYYn2;;u:mIczfNAAtAkw~8Э e;;O?FVe1A)rDۭ(q8Trhgljc݇xJh G=LQb @:~/B Jө4h4"˲h IReAt:dR3o a CN-hYH@6K$y[i4&=4 C6Pru:AC<WT5Lu:[wmmM&A KK~U TNg#H"JOޞ? zwoQDz}66O:݋n_0`[-c]HA^LXW*جֵ\.704Ǘgfzn:Ž0 ij|~<yP(ǃ#Xp2 KK?Ʊw>`+7nh4oҩǑ$,ŏ鴲,OޟK+!D| τW]UܔJvni(Hҧ{qql.{ڥp04=ݕJhp\EE9IrOuZV,q(fs] uF}qiF$I͍ܿ?-đf㉤p\gNQR^>q `Fg!2q=+4PP&,A$$֝oo`Pښe֝#GΒJEQ߸|0No$I _xD |a羸0Io#+ |-+rPFAgB'9l"0يBAg+qcIĖ̏l8SEůy4Ayi$ʣI|>?#IR2OBd>'W666oܺ UUKӁ`NAZ.{x1Y+H$>.`d^泫A>Ӂ<XQ<_Z^D}z{v`uS'mg$>DP) \d2y!YQy.g ȳjq#Ir_X2;;\8wðٹƙQAΟ=ŖWW}{'顑ѪJlxީǧggy$AZ)76qıD2`Y:ڏ ]}6S6;v9OM,JWVG7ro,SܴFkjH6Z__epNA#2/Q,zsD;ݙٹݽ=?: >uRww 'Rɉ ª-, SUðɩ (Jݾ;wΞ==rosֶϗL&M&#qtZ I$I=}nZv <1 uʊrEZ-ssE3={W>㦦V?d5 Im-$AVWWVWU^r5/qUU& iX|i d04H&!Ɔ\<n~ql6V#+2@.ʰL"TWV^;:>QYQn4ZdRz-eeEX, sMS*T dh4by24,)(*ˉp3H8;A>`q@{CWg?Wq zDDbaqiaqٹy^xMuw{6L$E yB!a76v{l|~UeıP_vEISϞ?!L&QUalXnwoYY]Z^_pjXWS-ˊb G"NGɉ$IJ=ep8^MwՕmو%zp" pf~~`9@_*O `0aXñ?3RS]L+BK Ü%%r(Aqp?qs{ֲԅ Jā  `A9(# !@A!/( kN(S3\61y_QU@8|cEo.4 _X($2EQNMLp,D,G{t:}_{ -MTa* _﹙X<FcX,,q,~w'J'N8V̧ /t5#%Pj/eiK˫[[އ툅B0pظ?Avܙl6kf|w¹[$T[ݽR(OLNAPE^2FK1 7 CKlZQ$)I O$`(dg轱qhZ^^?h0hhѮC¡* Uq<S?O?@d4d5U(2 C׻}]eϜ>UVVJ,+,a A( "lr<B$ICd?V%ɴ,F7[[&0Hik5 .SӍOLgpȞ'O=XdѓWVխϏ۷7g>\]Njع؉ٹ1;ylZjyDz@D2%vZM&(~4rkwhi kg4_v } :cYny^'7n޺A6m665N:m_{uWOOw&g_,]nܘrRL/ IDATX,M+/ϒ.cVnmj!ۂdSi(=;ogVjF<l ``%LСPhphX%t-cġ@`w pİd+VB;o}nRx2>n\K {{g[ :s 6{<ŋvn߆rΝ|>r:2pXBt ?{+vy}{OٻdbuSl|%7`{>#^J`ɔ;+JTr:dJR,\Ot,ËJCB<B=_QUĉSlp$zX,~91q?:}WO-5Ub``x H4fI0}WVdB(_y} ?}ˎ;aZlVkT_@>_ho7Oպj#G#mڸ0OB'OٴqMM>I{[6o/WZ9+)Vǀjmf4bq.]e׭Yg[M -[3zq0;o?x^4~櫯y=T"<χ#Pb1Eq!錙`xi4`'B( r6Q^/b>O&S,62>&lV'~hQ+|}HKxD=~Bby$N9D|^ȏXgA"W^%y~DW0;/ӟltfi !$6o,aUF> 0k82t+JcTԅ2F$D6@'\.odY,|022:1~hT*EHOvF,h4F= x"!I] Ci㇆V뽁@pݣǎ;޾}l6_teV5j;oPcO= O>S( .#GB>oX,z<> ÷LbQ9zC}jO>w8u߽7V\.wPk+[^anJ<1QQڝNX^22! C^yY,JWZW1@,__=:xhyW^-vuN(ť$Ir15;QU b6K Xh4Fw?k,JVZ#<|L&q\&prƭ۫V,hP,s{6$7omݼ{>Y6loc'3 lZׯ[B ]~ݶvj8v-Ah zueo4v^sR lbfs:.Jv-*<ϿǎܹcXstt/^=ݟ~E"4~ߊhZdeRSZZ]'N. & 0W ׾w!4X*It:H9hQ> [[Z.]vڍ$J >rZNg_|es/Ύ3 <jm]zU8tJwWg.hk;q:*a100VšJ%ɚ&ÁzI^JX Ӭ59&5a``fff 0N$Ri\d2J[U*YGG ɲ,IdE^j ,'G\X-!ݾswۖ02:.WoOw8imis9]\Fc.kͪ?P,c`y'tX,>ryaF,_,h4 "h. ׭]e``mTU²Ym'OeR4t:CևAu,ڱ̹ l{Ձ=rrd2Jej劏7׮ߜ e`q&|>/F.ʲ| /O#3gϗJ[ds'O ΢ J%,6M6R)gYze_eYdd6qq\>G,c&BpX.W*J:s/|`P%3mLK/rZ69͑ݺeGf3~+sΜ9kZvb`(JdZD<TDyo`}p8YtMѱ`tPp]JbٜGTr0h,iko;qjݳ$<죻;;گ߸Z];+=ʮ];ÔoNOËf%9 6G.իVLD"b+jwo-Y4+͢ /B˖ζ / tbo5* թ2000000L,aac?r(JP(T*y߸χGF0>/D"싯30X%,ǀ(|^i<_aZBd*H(B< oX6/\v`d68q W*QV˖MN9 b2zl2mXx핝۷? ``xf`0cc1Vasn޺}o` Tᑑ˖Ï;~WvݾsLv|"~nܼu󦉉sD:v\K/uN[[k˖,cc]x &Ap:?*W(|>={ʕon48fؾQjVZ_YJdǯ]|ٲ~fۮ߸y+Xf03A300<lބe,˲` әLkKˮ9GCNscl|{ p8-Z,~s7z=r9ͺݮkVRi3 0f``x*^[ddZxȻl++WxB--C\t iwv0e;MТ(޹sGFBa gfH8 !vյwO+{d`xpXCtٳ_;~"˝=w>L>|S.^* Orĭ[ 4 CU+/\$[߿qV6?/^qN~nO$<~֒ŋnܼ9<2ZX ̿sINBb8޼Clټ۝b\9ki "~,' N`= I9C²X,kW-Z(r$7y</_ۣ!~M Ţd+a``uO?b& Dlv(%\.ܼa+W=xp%drlllڵm3x :LNXf5P֮-իV*:;;Y !42:qha` (u b$Ufd2=vf <Ȱ\._6FctzLfh4:o^%s}xdDe^o\޻gg_|%jsw /x<ޮ/|=n73 ̝4$3"008κ6UyW1N&ccEVβ,ޕeKߺJ?˿T*eAh .CzłΝdz'&~|-7'OZ|/`0BLe`xBq,Ixt2A,^r9-\pO>|"DIz=/_޲iSx_߿ .^QD"w~/ol6[*\NIf=P"m0RlVD @|[T="ɲ,J8ScdK!~ɷBHyU :$7L$/Hq&X|E$%u7'ԢGL6Sn'&B:b,c >l60ϦD@3,㓧O'ɞK IREMœԡoO~J8{7}p7OwD$1q-7 $I(&kQY 9BcڭFI0Kkk3qth4~߮rtul[_.WH?qc葾EggFF+"ňHMM^2U#r9]$]T^W$yZ"=z4BH?#R:!tc'a(O D(Jw|>kt÷Fc/^ȱ|FGB0q*[!{U 1Ʋ,Bc/$*eD߸+;M&$I_r5W=KA^<,;?'dY;0 }&X?88z?pׯ[{r|=۷n ^(&zu+a >jp8Lg]KXl^_߱'nlr۷np\>`ޟBd2UXmyotS\v96m##}Mg]cOzh0LܱұqM^rR kJ缾^xɰ%OL`-_TٻxѢ%!n)@kKKkK.uɣ`!QtGPU%Ic fLVb`xq&M /=ڬWE1s'. Q0knn"xf IDATf c<{,$& !zw8^Wi={Vfz^(q˚uTRn* Nm-*&3]7Q $*끲EJ2dp0 pQFM^q%ET&)G10;< @pطm~૯d밯Ϊ Lx?5W?: @կ@t M{"PušFKUHa<فhP@yP61Br9OrQDE>GJ۷8t:y39|&UizlP/q=6*y  #x6_BZX2/qN)k!lx.rvR}k1L6N-f/ LY,ġ|.o2 ~t:[ZZ\*Ak6TtڼyK(ԺeVu'|a(>IIlmhR9Tw{&D؝֯RJ$hELRDaD`I0)KMX \##5\J3SѨZ6JRP[#%XBU FY~:lĬfj\w2ɂFtG‘X,XggG0#rX, -l6G$IE) 6Ml.Bp8"Jbs b6Z-n+m6QDQ fq`X-H~l6NvUM,d*LX8HӔ#6?},IoK.v6mÁ0LUtZhL618#tӫFMtEaA)mvME]uVUKAX!^ބ:c@=^XXxɲL=p8F5st*!hA\!Q*&ʑϿz8dSBDN?:+ATi9riRmI4W]m Ԍykc@'ZJUZevZ!FKO֠eJeTD!H?Jerf Ǔ"d29163g9)fs |?0a9):<8411Q([C##DBr X\*~(KR y^mxr Iln+F"QH$f)Ik9O'U-q遨G#^0n5R\[Kɠ1>yߠꙚ-tjrb)CRu驔wD*mkgEUz@WqUF(էi'1`!f*JvȖV349V*b1, r$'777X,I^7!L&GWFcLކ޽KT*'}vltld֭}44tvW<'k7n~^04<\3gέY3۷man]> 걝^?ᩘ2סz{ y7(zW3Qm ,Q]Oh5p(C' é481 뼬xYYL Ku]J601J[WaZnrC'FRNtadX.^,Indd$IW^{uNv%s;׭ϕ1xd2R)ġb`Xt55  9 FOFXEQJhZ40~' <& b꨿[e쩲<72UQ.EڻT}8X:k*AӥU5ߺiH?"PSu@Tb45ݞL&s'R_[%t:+jm {{:x8+ bX,Jۏ_ӧI k99BhT|5Ngɷn#~RT( )(x',EL&[k¥h `]LZunZnDD gHpkk3qTQ<^K%D"Er?'iXA};dߩZzui4%sr$2Jc'0E'Š8n .ۺ(?._&ܤ5 cjS Z^JK7EKf߯&V*b<$7< 8IB::$4itt~BHki ,u׵:4s^ϊaL}ƈԤ^Lkp8JT*^erR)u* |Ry _xiddd2+T(. _|u}\n {rW!D-?#代IIsPfh"U+xn2U:M&Ռ FRUjSiOH=Z.F#m#Ҁ .߆郶 46NH:nVeVv]ej&6f. 1BHge(c! FXWmA<WTwt,˃BH $xb"Jv7c|V*"!`Hx$:-N(^~wë׮9{.O_|u\.mټg_lۺp 7kׯ߹;пlɕdoڰ~gB9{ltEV ⼔@:/)@TXY1xKF8eQe*U&A0|hdYT*V\ x\>?hXW~8fe>5lQi'g_fr#}3"2jGYԺ@1%RJ5z"iՉIsh- 0Djk&)W3lifD_ߧ;GF޻G~7]*Cc?{֖e`' /\fw֜g6dn'bQ9+Wsn/7nXrBwN:D}㉇z{}>H4qzݮ,%C'^T66.i6WxRɕ׿(J}o?Rd6.Y|+Wb'&MMΜ- ~S O/[nwvt A֬YE  e`Igp:+=+ V U*TJMOyvTcҏJ2 LU ^( }m~G\kh$H,]}b7 v(Vn`+M~[@z Bcd[ Qˮ52r-C1e1`UQR Z`P-`Xvn'X$IJ2~&Igp:dBHfqEQ"S E7k׮jt9s ?rx՜\NƲbeyphX*~LJ2$n}ŢSgϵ_T.bdJR#dR$rZ^|zT*}czWWguj]r}xBes{{} -nD""6*x|zyN2\R !Gl6OyFURx+=7Nw4D~=~(sG.$pbtyf[jeei[[hlvgllۺYx,v*16>Lf~ΞҲcVwivT U30<=Xf2ZU&Tv ڔ[fw͟7AD"4_ ?:u(u0 =$ T!tpQ1WVWnj Jvu֕\}VK[:6gp\3fEg"(NM;m,W"˖.Ytz|Bɤ0)=]9|>h }m2LojzW0;tI;,vRLr$U>6qeیK.&Wcddl%'՚Ϫ NFgGwMHVt*mк^bkM[ܥ`E%NeYĺuN:y*2ΉʦL:Iтѳ75f& 25jF,<;vg cL=ƈ~&;|Hbӊ!bY#>`s\2! 39B@@}<`zdt:(y.*.F9k*V5xWTIJL[0& y뺆lPyY)kV) )čKڒm+ ŋ7nxE]ylU uY=\C5W._@mdLJkh}d&8=\TyÌ{\v_TFO6{~]oKT(ZjZ;;;>|XͻHMnΉ2l@P#I#sDRyQOWQ ׌"B}0 u5BeIR3[)_MR~F_7MHDZ*.]n:%*Ht{ AU JACH['WZ]=6Ν;kAUz?,fBkc k3vWj5h4@eMf庭vj*l&Qv@xb^ezi1}LƣC459 @M|P.sj3f6?ۍ1}wmVkKKp׎ʵ_tYT:mرm3ArWws]o$I.^at|k`_oorRPoOwReP,JT(A]]7y{{Dl./&T,6 ,q -槼 O so>z,bQ$Y Ţ(cG$Y%I$\.W*Qbmh_8k@4WiVk?[A#19ufwjg0*%F~_(%tB֖t&vZZHtl|j*7Z30<eܜL&=V,*띘Z<Gbz{F?xpjL#\nU'NZr˟畐 (l9ZŢeL S!H,d4_d.2VY% ) 3pnft|uM6`Yno 3|ϖl^(T;~5 Oat\B=5N]:Ml`nifd׌`tآ͚LS\;JQ`Wk.cDoi㏡2>Äo$E1́|313Wv|i<W=fbس[:Og[3,l4鬏P(:rT4/ŪZŊ^^3YӃӦCUAŵoUզ=9K÷s'|Dy# ʲLA]cLhߛɞﶙ\Ay=p-Lii'WkMPQ>Gu/4LCizaTNkli0ףi$M5Y$6fkerEdqWQ$ID8D=ޘ[&mqx< {Iܵb10#1՗Νf0{~uD"Erj6gwa<{scb>!vZK`kxڑ.b+Ut=mX4]]U}Re"ڦF0kjJ0ZOdtRyttl6g󀛛vX*qdl'b1db`I^? ?MΡ*k6Ќu^7ѥOB?O ݻw/Jݽ{wC.,T*jp\IYTDQ<BTJE-l*J,Wʕ?OH4U*L&ySjlGxhRXܼuX,ʮ#ǎ'E^R0~F2G",fp5LV%Սۢ(ݶVZ#<p+H8u[ .D"^}eG",K,]z_Dw,s10;LWe|q"0$!_O" (G=*yf c4mY5fǯSE`2L\٬Y^<믞;+I޶by;w.X偃Xt2ްn?cٔ $Y!n~_ߍdY>+m6[=^>Fuui77uqyn2U:SmllLcMZ;@e:[>#MҨ7nMU# 骯 Xe>j$Š ESeJy'ޓG"jA #iH.-HC"i9<$J4<`POOwXDrkDQ{8NsOdH$"H%deOOg'33snE5R*BY b2Ed$HV/nxq#2{L"ϴp64BL/llddYzw~XOwg ޾gΛ'& m5<\Unܜ鈿;?aܳW_C[?沊$ݻvy}&ZI_k4<@1\eV2bg9]aٽSu.M Δ0˰]jx}g@ɏj5}h ό&[B7 6W87pR 5P 1tT1G`LL^ f~PYtzeNOvyZ6=]As5\o>{z:}GwܹZrWo5^|]v^3Ʈ@9ƮCM4,ƀdRZ&1ZY(|[[ ##DDM<.hDA)|(*J5U%1l.ْ,J9[ݻtX4 3MMMˊH$Ţadh4RVJR,eck8FٛPYOlʪ#WSLy!fh91Sžd6Mݱ[M"Q ]ňy^4@`KD2- cD>׫p23GGǾ37D~xp{{zdYu#L$%QI$˲ !Eimk!$IM H$ 9Ɛ'ѨirY4A:::*_ƕ>l8g5ǂF.F[ J^#a",l_fT L0u>7w1voN6gB;y盯ݛ< msl*;m{΋~kWr@Z;xsΞ s>ᙳ]8aƛoKWu恁Xcܾm`ԧ?>>yR|[ߌb6J(ݣkGh7-kxdDeġޞť%˴jG{;uj7JCa=YD"[qW,0! cL]prOz#9y. hw3&ך~3y Bt{(͆eL/O5. ueۍ>&z= uG1L~Lv7aD8} #w0kN.@c\,^c=^<Ƚ' 8>cjYL,ޏ1Bc,"6W6z8ٕ;*a@(aƢѼ{λG~cxTۆx=կҩg*jT,Ɋ{;wxرťX4cǶ#==sD͛uMw;v_iM<\4 AACto:)y.;o N*ڕu,+1k9$'Q?u4=KmL?e=}r쒝'_B|;{>\JÝoڸz/^R,:;5mC[ɉɻ,?@˾$;r<cc:zyյzM4UBiw6bJ0¹-- I/9/r,lNڥR+T^i++vJ2J>} YI94M[8!Ӵ0AL`+׏IƵq0gdF9sP7.UeȵqY_w!k7;cC#8 zNP eF` MOϋ@1Ӵ資r[mkK lvL&t޷P(EVUn޼ʕ\(d-b󀉜]d@)F___&;;]RgxܻYkB9& I 7% c_GVb,wekE.pW40459ItDH~n íy`Wq'zًo85b %cO4pf(gM/Ѻ}gf1Y=)CrvdHQ.WU{^v&hBAG|BR%:I|T*չ9 )t(Uը\Ď$uL0ʕjeQ\T*Յ G a;ݥAZ0naH7NBN.GElY#HT3<^ꅦ1f]l5]sT蹀&2_=l淒YAH{0|0=2*8l'_Cأ6`$4Mq>T3M7*ʫo$ffB~oejUuNc[nP,bh!FKJs.~Xx㯬}N|F;~r}K:7؝t%LD.R%ْ庺&顡^U@4,VUU#xq֭P(|=1PV䧱hds jujzzyy;~¥MB33i>.\X]-j;㷆oݽKӟrsB3g޶mjUU%UUkj},.1KMZ jj╸ 1xղ8Lt |ԏ~lIܳdשg.]ߴiәgөTGG-v&_r[6ݹ3>>55C.^mxX,EHcGGn ~GxՖeZO5U+ccJ%i3 3z:BpYON>\3WGMbeTEx)EH{=(⦍=~ͷSSSL۵7Z[oܼEC}p; Ol+S'9Ϛ7^1|bA7rcZ GV,sc_ٿj5p_'C^&0׵~Su[lN{~bHޞn1^XXt |Hlh8;ZdG(ɤI[U5:$Tr>_qT~; 2ʖeik2L@`Pt Oݳ;;z$ܼ5ž]~dtlee%c7l6 jڷ_&CC[DQliIf-#푡[bhTEӇ'&{{qʥK (Ig:D<㱃ry@(mCLVشJ:t4؏1.x\D:SK(R--F;%jۻg=Ni'9ė %Iz/v*ղlY ]E8N pYɅ:C_lT%:xCBNrZ>m@$vk>uWEcbz(h^ K2X^+֍i NtV{R,ɲ8 (C"@%q?h⫅mmTk[@DFl1rAۍTd> ;IյT۽-u&`MӮ\SO--- <nF^n rc Al3re}jek8RT|,,lYj=&h]rȲf"^RXia1[]裏?uӞ)8 f`!c*c'&&n߾ .K t13Fpx׽"Stcu;1@*:K"r"j5RE1^X_^^!M6ݼyg& I[i._M4(0B@.6h"ikK ˹RBdU(< RI6k 1)=v?w2$N^,aVdb[̹Jb{A6Cزl6KyK%q6'2Cwww}&ؖagggeYqwΞI\3NlPOa9}oETUf*Sִ,ke%GoN*|@;;4jaa<]Ν1]ȁ9QmӴȼr\nG'3\(?l7]&.!f?tM.nzYG177´ץK[_W H R;w}b0Rִe6qXdtx鏡 BNX \QbOX,{?ao_{[ucwye+ܱ=\<h|ۆCh&?"Tna M*Ӱjb֡m:49ȍrΈZY^v8{#\ZAɲ|iUU݃-0w݆mz6ĸ=&r ʔF-'+?cBg^j}( 陳!Ey~ye}u?Ϟ:|[7zP(,,.e9!S79z䳳J6WsM8{]M4Bl֝M$ܸbB"rӍ4M$!|KeYt(SeJ\k?moE.WdoRs#sN\!lrltAArommv}UO8~} ^'躑g 3r [oR,,Kt NګbѮ=0ڵ1U}r.eYJ&NOKK54Mc{~[/GR c<3Gvl߶}ݻSccwVW r/Nmpb\x.uӢ(;vZ?+D_k^5Člcp;w:,Øسw$'޾k+++svܹrs`@ n83z{`J)~>ԉ=mIҩ۷޽q}j2}{xp߁ыzIo?zlqj6̏ߑC 1xFa"^Tr˘kl~ci:}6TLɏs%}o~rO83 㓧|_o_?7 3ݳZ="˯Ev\eYp=qܿwX4Sөԑ@&FW:j5<<3;ic?リ.^:zi4S~c\~Νd2ic?4M0O|[k ִ%ϗ|O: K)`cut p  yD8!D [ r<( " pDyEIF%xQxDAICE8AL8N%q( xQD9QQjU ) dV b)bZfY3a^C'v-jC2O#`>۠?%Q;L]ڲ,"@iUyho0G%ChɃKa/}[m2 -oQU葧ޝZYnݲrŃLMMj7oݝ~c7oE[;w۷ 9}P(\.W3\=2o1ƒ$}ɹ?/!Cݼu5nkk|cٽsЖ-|T*m{ {#Y&}}yvd2L1\BFD"t$ƴ*C:/4#D,{Gg? wvv 1==mص)24{TDj: taaX%B"l n[> mܽ3ȢRv'X,nj,okƑҒPUunnZu,]4xjj38є;wƨ%`hxiZ=zzMF- kvqqXS516L0~!0ZiBonPfxӯ$'><_xOS(vv?uo\ÀMuC.4((O/)H 0OLLb-0&2ˊ, [[ز˲, [6i:, [زL4-˲Lžedr6pY]d́]qr0Ko},s}Y!al}zpp;x\l& _z o/ڹC^@ B"=rir|0q7o o&2,IҾ={QT*ٍ:S}K~8p㇙+6\fA̦!y3^;r|Č;C^i}_,آ(`3M2-YMieaiaiAeaL$LmeYĘ1wV"D~ӫq÷ P+u8]\{t](7@ܷ Rv-i!it<55NjR$nI6#j7u(Ǟ9B 5-KD륻Z[ێ٥v3*?tiРMK;?uժ;+uGAy뀙B,9+Kj Ӛƴg`lPt,SSh}aCLv`3Oh۞8A-"ieaYa Z-L4 MƤvMN,r>rn Byi惉Xv8v,*jfX,L1< 3Is V蘵JoEQy28iAK \~jK#DѲ@r/xOR?c;;?}ǎ]9q6:!{GM}ԫ/*5ėp8#a'' ieŭ`yt 9[CbVt yb YlE PV`6,kzBmjjs/Kcnt{13:1>[؋ᅦn?K//׈KaϿ6c27OnNnq\ץ'|uw0OO>5swdY.Gl0UV)󛰴Ҏ `83xWб_++Gۜ]j5=22j<`.(Z--/$I ۊ~wm_w}s cwgf禦f^- g^;~h42>>궡-S3Zʹ}_t]M4@,ظD7t>91pY_CumW`oRo˦7=Yvc;jc3S:C,oG<%`nz_F;E~k6A`bȀ)_!Eis*K󨬬dE$ɨV̓_}  zwlھ}‚aTM۽c/\iZ4EKW7-./C!xÚh!I #HGjwAo4uvx怽 aF-2vg=M/N,V8occ`рI|#6rM>xH+k =W|PEmW3 ~X@utW*0ܼs"z݃WqD8E{wx<FX(jj2hiIf2219cEaj7ē'ڮj*dYou]7MybYV\8*7M3Vlu={E)acɲE%m:f!l_),BL=Kp[ޮnILY5sqt;5d_v@p]L 38ڹZ8 3<ƈ}&C!RndI{vB_ (+뉂ve9ޠhpj`%u69`ۨ]?5֙ л4|ok 9%7O^fшөϲrzU*vVm&5R--ǿ[qhk#: A0_715PfvM>4TW.s.ԧ"5 L#?{KWo7Rϫ]wl#;;~,@&h}SӝK04$I2fy_64Mz! Dl IDATT1rj50b `7~qGK@5rãk(%)SQh=gN*s%@p@> e6)SU_5 ynOp/˲j50L3FG`IU͗$Ѳy_]7xI[ńo{{4Bԭ1&/VM4zq\_oo.O%#w٭Dܞ44Mvqjzg)##ccGtJk:mYRGG#"?0\g0N'Y.J"vfT_V)@h&Xj,`uMa'%Y!?DK.UHwl[T3@wa ߵxc*7ޥ3~{WM]p6k?e@-UUk5{WP`&ym p* FE0[@M}}|v{|[ߩD9XY1g}/ ?߭_is󊬌vWutݳT.[C[|~ও'?ݱc 4}dt7:tpzfwuvt\kkz``3u߷K/ޟOEQӘIW89,/J%HĨ_X,;UP4 J͙C"Gv9MӝjGHiZN 8ĪDT*hRZTDEi&Uh&Q 50L_%ST% ϭR(RxRR%,(r$BV(* {L:Siy9GK8+I6Jh)՚mBDQ @_\qbz͛vT*#YK3,q홾>Z3u j&xґQG~!tЁ_WݾmgϞkvݩB[?/oO(nr!n}Eh#MggH^ xrrIKӲ^|^RIjcH:m*+KKYҖeT HV]#}FzaTu}fz{]ێUj33 ׫BmRD<[[JRCsӫRU)KKE$(ǯht}4nmMի@=&h³p4ݲy)pR*բ(ݻt*muʕG5|ćtwOOψ819YV:;WW ĘD¢(rŞ:|??x%ja=zRMӤ?C0Ɓ "c9r0A*dwQJT,L#noϴ>ݲe 4D<-XF"<8@6EQL=ں;˴=m_KsM|!%X,(NQ֨C&X':M48>=3s0?hgoS>ٛh Ɠ @rq<ɴ,Lwq~<]9B" W8*7MIOW'@PJ80wӲ{D68PxՖ|XU}*q 5V̩űq 8:Xk3[Jdd`%0h8嚪l^& hО|r vpL$6-\TRmC÷GsбG_~980ۻaCW8zǘM4Bcvsyvn:Ź**RB"STţiT*IjR$=UT)/SDVIYh4LMTR6Pɠd(hRK*R8P%IZJKKY: ^,(J%A'yÆacCDNDO9̑ƛ]]KKˢ$k55^VLL޹cP^|?kḏ;$?6DtA7Do7==ɴۻ[x˖͢(PVB$ҕ+,#8x2囓M4b7D d`:/8tؿz{{B:{=N6wڹ{&xB0v @N Zhꃁ08Žk6n4{ﭪw/n#8ܽ5O}/Fțh7kfp3ٵu7n޼Oݛh+ jHlVU$I*h&tHʘjuu'rTJZ8-//G"H$iJ6{O|t`kol80N_X,rZ-/_:iꡃΝɴݹ3H/]wΞtЁϜ%r'&'ٓɴ}Iw&1rAOOϔJlVUUXXXT* 3DDM|PJcZZZR:frx\+LO7wq;z07?w7o;uW\zMŌޝ9g.\{4kzK2yk6=5=Fo߶u BO/]ٱmh/8&z9윞cHsjz0q2D>*xS|%Kݰj8?D3\340[^<`ofK'r6Xu*y6Xijr#VVr4鶱Jl:i}*, iR[[㮬tBRzo|SSs4䧫+CS O%:L>J.:;345JlN97R)YJ&bUJTJBc{f}qqLGQpXQ)rrHW*xv_~W?I߿o+'qIBT5cǶG}'&fff7SȾ 3wN)F[өΎ[M}&xp 4J*˒a\ $IDbPHD"Z,R_17-/eYl`-[p˾Rl%,6jĖ%l)"V%_@| TZj T <Վ֩{(P%WxbDZP/y#!d1r]U@LdHir !$,ˊ-,, PhuucB #Og2h5{+r㸕l3?:;;{%YZfoV9LZZnr <3˴v)BK2ysuuuh)T,+(>{hk:]*32:V*W^;d"M451&DQ$KDv XeY(ȗ&4ZL&Au]4M%Ij(R(y^RT1MP(q\Tj*B&ӆ*JJP(lF\R4R9+imLӌF#$sT*md1ƱX,hVTDQ$4l6 mmiY+JV@"y/˕JD"'N|P,ET*cI4y'$ c@u0h4( ƸRjzi ,IdYV>_,!xBQ57Us>STVyEQuUU ~T*iReiLXU5AM4^Uk`Wܵ*kMu ice:Kblpgrva &#,2Mu]u$2M9n: 0Q d3 k:&%ˬJqO,4-M80 !DT"Ӈ(F%ݧ-bl䥎d@JU !{9i[r+X80je*Rd$*{1ƖeZrdi J.nkZR6 \.ceY&jT*ۥBcKjj&B\8*KeZVVWY'?D%#qKXF BPVx<˲XVpT2Mx "B\.E!*Hk"ZV4MWQ9R'e[H$R(RRbTP(T*, WUŢ( Jdy\8 JC$)ʆ`AdYe E& ,d&8QE8Ќa j&ˊ," y1Ƣ( q!I`jjjH$ 躾64uaaa˖-CCCKKK|> ONQJfp`S,۾}(ֶd{g>wokk[_D}ii9t8?f;:;$IT--I0^oxÑ>Do<ѕTiRIC$ܥ+W0Ƴs[l^^YٵsǕ'mCC|+/~&*!බ6E1LU(VD($sdZE, Ox0L"EAD"u# B,AQx<>>>^(j5-_,\. A6Vk_E"4iڙLSwh4RzD2M5Er\׽HbQZ8BӴgt۶+J<o6MOB!O${{^wD'WPܶA)5Ms||looaDizJd2t{{qqqCtST*òkZV-<sG'R&YSضy*=mrlKiFu9m"R5/Q"e☕J!yy14ͲR0}>|MӦg"7]qs/:xT*GPE lD0295EeYZ}~~G?|sjz&ܹ#U*U!D"mvD$O nܜ<}|oܼ?qO:oٛ7'?ܕ+T]t <%lcy|j;σ7_m{u޶m;e-iGa~.)Ē'ˣrH^sin"Hc$Re;P$^H+Zp}"BHރ@]$t7yoۛ Ä!}-Bپ^uUڬڵ۶uTDr6X"|sBhxxmcmx5Qaj IDAT6`/*q֙1>֓b1Cb;v|>wyry.fJmZl69RJe PUsRtKH$~49552Sڵk?_S$^R#C%v"y6/r"qtݨTb\.'ݍF4EQBp6ނ^]Hk VߗdB0!]2kZ:?&Odw7<4?ůO7''FGS- W^o|RG#g4!|sr'I_/pdZ姦|yk}LZ׍^8<|7 ܮZqܶ.tvv9!0t۶B#^*GQ~ӭEZ)g)²VD4~_O$r_"8|> fv\vly,BVߥ"ٶΝ(J{!Dr]]ŭ" 1fZMߏ1uZb4Mu)⥰n[o[[}H[@P h4 FQЫjVrrnmo'݉ķ^zRoTUUű'GUkUJ(z}BQƉ,ڹc{ZK /^ڶm,g?߽{lBݻv Jի\=xp֑H65kOAߜVeَ g4 jAc^={|6ߋF#'8*J&_: ]g}TS(FGGWg 4bsuH^7NǙ3;SJP.\lpպqOR.|UTf Ùnc<88}!>߳nResZy]lvq׮_xPLvٳ[ӴX,sP0‘ñXq]4W.SEYXX$ɦv',/ ox˖H8Psҥݵs/۩gm>tϕHl0$Ћ/UU^mW,k5vL㮬7N Y11ގ$lx F~ʅh!@rKg@Uv#IztHK} "q. G c:B#u{h6FnDH$#`D"H.6[.hD"H 64X]V1n5I$D򘡔< R0CfH$ɓRضݾy0_G:Tr!j]v픻_jm:-FU"=O K׬v遪^Z'ֽ_bLOa"ݵyp3EQ)`3)uZooϝs|kTzܵ A֚fW!KzDȪS!oL5ңsֺK(ݸ(V"mT+cZvpnn~^]5ܴ6|>_Μ=ێin͚7e>[g̚&{ZC&'2v6⋬ޜYNjKIKݬi3&{gάOkF+,(y{r!*8R6"_斂v&f̘BKEhzJgidbu2\BM]!咳X/dy&gLKٗK=ݹp!N.cifRyw,Ya⣅]sf_M˵ػsV|Z'-}\Kgr6dS٥$7*f_LBeOeBB9rB\ե7gi3扌m3q=[g+}F^'7L@P0bgM~>׭Xn _N ~*kT&ޞ1*Yuǣt$OZtRkJC {,LoV\z:ҶŶr& @ZlgaHYsN{bWټKYhܹ:Lĕ N^,: N\[ +:x>P:A+BK%w<@zƜo(͛`\XtK0q~*&4#Ay@\o\Qw hJ5nb+Z([<>MY5-LOPA-=QV\.;4bOݛUfs Gɪp:{7]Y7F)!~@|h7vX\Egjl0@~3c|,L&1~wʖPhs[斷m0ޞ4MӜ/KlSxWoOO>_Lfaal/Kɛe/׮3Tj1T*oBBT*Z;ͪ 3T+|i)40c}kwJ,Y&=1%\ZvE> QQE* #Oy(:WS֝k}ZEo/GS֠ IBY6p!W\i1Q >#TsgĔfb&'B ʃn8b&))h$HJW1E: Şn)d[>j&f{1&\ޢV(^\ =F +t( "@`B9˽Pp{OٻP ȥ;[gi.ĵ˸^qi\ "z|ONvpQ!}Mekqe}fb-AI 0h1;Zv6d oR(˕(݉D.BD˲x& l.5 # J.,.r3 B(,@WՁ~OOoR,@}ٽ$Vώ;F8,!̿1>P4yLB-| LWe,㠊; js"b !j+bK):K嶻Tw71`n2QqLKݻҲ-*^2EW-HBAQB wޝ嵫hBAiq b "U[D4,Țu\ؠhӻ7ݖ5]n*v0șGAbGU$@h5\+Cޞ!d0$TEP(2[@1FH$B) '4MsaaEL&@@&!SJ1BBBAiz 8hyэ%>s޲|@Lj(D4o`&ՄT !… %tv)琊C*@DE#eyii5B(#8zG [sĖ ;G@R7/DuoY* A@R54.CT0 R#REEegl(KOg4DVKݻi@PA`{pްܮ#Қ^(lL>m{'u7*UBBS(FCND4`EQBg)h'@W,y%ao{hhb &Ioojkm,ѭ[1W=s`֙.ܪşOLO3 Epn S )Ros{K~ B/ĩP}A.y-6ؕ+|W-Yj=dhbvFi.1Tlф*`d1q,mTQS`Z(|}>'ʮ\{b Պ+֯fFI*O .r8sB*G#->\ @\l}UtzԆ#.SobFSq8q@T,^KE'~$իe]Oi!b6ibc\)XY.QAdyol=k\K=*r)3{sVYvgM0]#B `.K_L%޼5$hy{BR-d/Z[qxygd:sYF)x YS EdysXt-tbRd7` 2۳)xΛYrs m3;s67g єrټsf9o{VfkJɹ^qd̩{=k;>ftb/.GM5y[R+^H 5]EtB}`KYoϚ5h}`9vܴ gf*J__˼cPlBQOV"NIY6DQ&L-uD鍊M6[g*]*:35w$DF>l^?9eKQ;tWrəq?E_^iO!v \c)gA|$HI(5'[C+ (rB* *b^W MVU0/U p:B;JP1G5p*H#( a8H¤[uQc橬=]uǣT'(iwՇ~l&睽] ХA\! ATD1ڧ~0> +r!~>iFPXE´h-}{ڳJETѐEPA*0$i? }w;z.е^^>VuK6'{bĨhn+IM\:Hx)$USRp((N:GTxtis%wrXl:ұN sAD!qkN^_ hJPo4,k=W_]}c:?;2^J5 M@!؋)jXdqdq]!VQXé&i" і I7I[GQ4'!-4.%aH U~"xrJ4Rq*#zDhO<b: h ⧨ :(=1%?hG(F'UWUB(ƘR 8֖D1}M$U!8j`(;"+cǰ%00+N$V\T7#!0B ]@0!jlwl)Bh˲[cPbz'hqWT3Buv[j؛t!DySsݛ~{nVٞ2۾ʽ>=h_@xjZݿ$0=ʼn;Z굺ݽ+5ZVr0rG'.\.a0|"oT2Fwؿ<0\X< OYkWֹ8ЦU}}P0,D"ΦU1V`(D"H8Vc-Yxx##w2qn󮮮^Z|hp0 od{Zl-ޮ(+R(hmlʸܸyPWVo48ehg555}g9|McWʏKiem\.UU.mۘ7'bH}??jۻǝ7?O?Ϝ=_֛{p7}r}򩷛Ivo`ʸD"Nyyzm_~ϒH$- ~'^zȋ/F, J#0Ƹ^o^UuMǻҥBBHTߺD"H$O[k~;_(?;q g\NJ;38`IDAT_(nܜ|g;VB!q./D"H$Žq bC;3;J Ё:2|&ӓ@PQD"|exWiB4)#gϞF#`UBz^,GQ]`D"t(`Bȷ^zr7 {/xn*p'P(t˳BzJ$D؜aHLrB]ғI$D$ٜ1 .7MWH$؜i?GPU㟟]C"H$X6k׻b7D"H$;gϷZ'-D"H$9Gƶ=i)$D"+s,H$I#D"H$O[3,gcLv9g$q !2l|Ou{QJ<D"܃[p*;墑h. Eж+zFzr]O\f@Z}=|>N7==GZD"H$-=z{\00tw;qeY/];u`pi]ɱ[Ǐ~r|G ??8`e D"?d8޲!t(yB H$]ؿFx%wݾ}|-o7W_yߵDrK$DTi|sReH$v6NCB4'-D"H$il.l6`eD"H:%pF )[>"_4chPhZ)D"H:T*W y) w,(bݝhW5M["|W/Xp54m+թio{P,%5rnz=pl`% ^@];oH&&>VY>q:%Vѭ#wT4'^[?bhp+[]n"{ͪ!ғeL&BPsJxUnÓ\&4ilbe p.K$ H$%iVs9B865/̐%H$ds*`۶ HĻD"H:aQoԪF9].n;sZeͫؖy˓ ,^t޶;uR>k=N_b*J.{ J$D-u—>`G^.l٨X"YeSs3ز8;g,̧Sj_ɥO? s9Ƕ)hT@0<4]xhl۽/0w䱗{RQ1kՋmW_:}?[c7I$D򐬌]JP)|ԍaE.9͉<B3sMpU11q6L^xmYcO_<}}QC+N]9{jpdlt^ ]r1g[V.7V3w+N Kg[ ۲7nI$DADJetޛW}s'2ƒC'bPRr4c nM78BK_ulK\.8 E0±m!˜.s] y3Ra:0eEu%Eobdv<޵7J"H$'/ Eb/#>iw٨CJ0c<ӯc.Bq=jBvBJi ?/O3};8j9cC/}Xr`֫LH8Blְb 2L!H$n+EhRe]ƜREDBf9Gc9c918GBLWxką,H$IB|^JVK$ebfjV, 'BT*l69:T8ĒFa`r⸮˹J5 J`"K$Dᬌ_UUulqlMjnzT.'qP.N$FRru` (pT\cj a s10& aq@j> ~`Z"H$NՐ(%DJ*B4-ι(Fu\EQ~__a㨊 rX<_eՐ(BgT1.%!DUiX$bi奻E5=%H$pV2 ) oQ%BG)m4@ 1faq.;KZuP]9w=˹$PX\1]SuL#Ȝ1Gum#Jf3 QJ }|\AQcoܭH$ER r ^iV'h>ab9W٨p2 n;|*( ˜ `TM/0葐WB0ӜzZuCBwK"H$ baH-\n8c5e3>RweڹbY*!X42?V_Wj Q5rBOw H$SCsoKZB x_ Hqkm9 U(U4EAnZkxB|cfB pGQ YX"H$')`ۦ_+8Z Q\ E.ePR87(UFcL&0˲]v:a _ 4Mj_D"<<xqŪFh4 DnB 9D=4_,+U6sp 1`qlۢ}BUREQz݃}oD"H$Oȓ42$3 qjs@XW~@6ZnZm۶mMr#UXw"n:J$dS6`\@\|S湺p8aMp,8kHq]eO  v]ױm1 P]S}uMF>N˵zW"H$6r7 sK ^4  6ħR.9 (ҶsQ0QzFNcw߿%7>gjD"l>t] SgDݱMA[ B^/ o !eY> ~J7h1[EK$XA„`u<p8fwC B!H=wML\3 }hyX,H$4mhh{ܹ;w.Y *+K'LPH&sqa693AI_kD"?c1nVuڻ\NtBalO%.cmj 4!DK$dXS\n3o~a\IK!H$oBH$lV %JcړE"H$8L2-3z 0q'-D"H`)4{voD"H8W"H$'1,IIENDB`gammaray-2.11.3/docs/manual/images/gammaray-web-inspector.png000066400000000000000000005122331413310443000241300ustar00rootroot00000000000000PNG  IHDRsBIT|d pHYs+ IDATxwp\םo39 %L{NպΖzv׳lcwv\<3[%[,Q  H9?hEB%OUs s~*|@AAAPAA\"AAV.뫮]F_zn7rAAn7ׯ =},/e~o~eX ׋(κQ]d2EE%&I>wLOO!<ū=&nKTǻX,?+ #ţؽk'C!b& ֺp$tB*rvvj1(  ! ޶O}@0@O|~W( ms λ =ss63(Yx"A`>Vl&wjjuR)$,P 5:bׯP̒L&55v rA,8 |~jkjP(nчwb:܉j{}}Em @eU%;3O=. DHYA#ѱqzMFsbE38v4MwY,˔co2EN',SYQN8aaEsSnшU+ښe,J#h4Zv;zшl&ưZ-HL8^?$kZxX,X-|"2/]"ұn guHEyv:Ȓf'OSPࠤ b8k(.*ZuA˧Б|>Ut7]ȏu>åAVбn]+w'޵ Ź@AAAAeD( -#@AAo  |2ȷ]VQPAA!7Vg[XMp%A-IAA* T&*BUXewAAf螁L&Z-Ov߿/RAJV!df}EAAXcDx 8:6AQ$, fnn.g)cfvk/ PtRRRc. y[J8or\Jpv\l6Kjkmw"e9uk>`dd>\fM{Zt:MOo uq]`0$1L|U$Is y}<\eN m%9]#&`0lB&*c( Dd2III ͜;wyl>Xч0??a8x]HB?Wt u ^0lf ٬7]Fe>h0jba,f3##TVUhFcCA E!J%),,DRx07Uާ(PH$0!I1(¬ۍ195ʼnS0 c]'aO>b۶-t?IUt;NuU%X,o~-+9u f1$Ijp\7##l6n'Oxio[^gbrZ:;شi{z(/n =9󰦽R07B?ZZcz:@wk8v6mYv x"˃(oر?P(Ķ[4pH$B"`>҉^g ydC78wi˗bxt2<\tc<a&&&ٸa=b1>?<$!+V4S\\( EENs֭[cnGhxGW#{bdd9?Y8|"TW1xe(߃y 722F8f͜=׍(w. /fqfuc1$ ٌj?'r͸\3DQ"fYffgt_ʢT*딗=qX ݎ,˔uRW[$I #DQ,H={)m(_e}^݊=`3<2BC}%u$[[y}\JǺ 0DcQt#Isy ;~s^_;~$I–6( ~\9JVɕdhnndb8;OG<;/I"d2;dZFL酏zJO_%%%:}=E*bffQxg$)R]UEaaD]״ѱn-@0HEV? ǵ"K?ůEclܰw HRκF#kd jzm_I0 (7lY1Lx<^**q$I]8ջCQ,pcJŵwC^O,#Rpp u]觡}۷r|/{!fff:<];Qwl3<055EqI1ssl6lۺkPP˅,z3',wMfee>rM7p9޳9 ,\wظQSx=^ꨯ=W`( zIt,0 jaB!N9^Gd 7BE=m \;yFwCdy&H,#NSS]ͺukX߱Ytd4C".&4lTu ,*p82\-e40@8@7cZre{5mqfظ~#c(BO_O} Jի'Oxt7Y6NZV쫮xIHtbU˞qY!2m[j9 c-6hR\t AAzb+6@-dnZPA,do@N||  ׎ ()[Y  |TD-TEAAI~RAAE  2"AAco2AALKJ"%N9h/x+~t?zuCGbZX<_>QNsIӷ&   Fp8®wR_W(x}>Y<^cZ" ܾgDl߶M˗I$?;V3ga1룽m5l095E}}ZxH&<|t*|~JKJzc-0 84pI(VFAAi[~[=ӺukװvM;U-oGq`(Z())y٧9q4WG40@*F0jҒbzzzN>C:}|X6%O$_jٟ$`0DAA( -}$h4jfh42Hɔ_;2 & JNef|`5k<N<͞va2:Ӷ\A<Iz*wfd2$IBВ(z``q, 455}򆆆hnn6PB fgg9v555_~j_SNrJNMVZc۷Sv9wN齧΢RhnnFӱbŊzx"wv9[nsE]Fcc#vamˎaÆ[ɓ'% d><NO$Gy3A^(Nl!׋^$lVK>*.*B?;؁db1c4 ^477WMwO/#[ҒZ-B*+*pƆ0 D>(߿^… L&dY^T磠JE6?FFFXb׮]1)**brr'Nu:tH$dbllt:裏 8|0 ;;;QըT*=שXTDyV+Ӝ9s/~ RhǃJbhhNQTqN>hh4*>`׮]f4 .]R.]DQQ}}}FLMMOee%gϞellzzz8<NWZE(b444033n.]JBsQ~?ff3H3g099Iyy9/_b$IZVzzzصk]]]s$cǎaZy7r?kEEh4<.z=dÇ)--]炙z\ooǹ|2$ ^{50,E6p|s=ZFVsq\.%%%8p˗/t:q\?~EQ0 9r@ ngll ѣG$ ŲCSTlV1 LFF#Ng!F!L&ZWe%EN'* ga!P_YsJ6&,f3*Rsb̠cCCC,Hd8uۍbǎtwwp0sss\vj&&&hjj" b0ضm.\Y$iQ"$IBQcff#G_FR^ÁbappΟ?OEEȲL ˼ ?~-[P^^oM:frrJ(tp8fEuu5fL&bg_N&a||۷sIt:[l7ߤ:w:2<j5p88ɄV% f1 R)L&jǃ^h40Ȳ|D"cay H&DQFc>jHjFurR)&)D"EmPNCVH$8N"tB4%-;\h4HD$r= DX,~dYFT*N# p8o.?͒H$8bh4/_lgacrSTstx^Z-6-?bߟflz#ǃd$ H$h4Őeyτ.¹~ :A5kRQQu\ʏ4=>w磲Y  ^Gz*.X( ZY0`ᶴ  'T9 cEA,vݤjA  |20"y<@n+AnO×u;]V}šb mu:zW] Ak( nիTT矋ޣ( d-VP {i[B%/p2R;ndD"Kf EQ%Z8F"FFF nebb`00^7fV,H$gj:ӮE3q9CCC\vS-y>Z~w~BА[$L&?ط)v{y rJD"%/HZO=v~~>`xxx<,$In7D^z~(T ^,Hϗo˵hٖӋf(/dY:r? §HܹSaqӧOߏFf嗀z饗F|>jjjz*x•+W!Lr|Fׯp8pX,\.ί9::?$.\ JZfdd@ ロO8x x<&''q8d/Z$EfY܈ ۬⥗D¤i],vn7v<tT*_bJRCQQ###x^v;:VՊ(\|99$366~3 jh4\r}vj5$,nRׯjI"}#0IRi duf3Y%|Ys}ZE! !kd z=p`XPTd2cYrZί/2PIίJj hn9IBTj,HfY"x͖ 7cJKKqݸnF#EEE5ْ$$sss4 aɺdt) DǓkqdYdJPd2``03A 7(|~zݒ/ 8qTիWj<@.owww7sss9ˎ;*'l۶I~kFii)L?@ kh /,,dnn|{~q4?яPTlܸWEZ@T*`*癘ʕ+|?9555~Q%})..W_Yejj!4 Nl6m6ٻw/… 8Nvw<%g ٬8dj5.T*M:!Ldxgb1pT2E6ennT:{֍#J#ϓ aT.hhrJE"̥Y'Dc1&'\vl6T̺݄Ba TK%F] r>c$IВ$-˥+&lI2 & FN^ZH$Byy9v͆(|y3ɔoh4zwI[LQssDcQss?fcǎRD" ^O&m۶Dkk+pƦMpȲ% RQQʕ+ ôOJX,455-yGy]vF6E$GIIl{J]hk[{RVVVD^ߏhd~~1tlذ"uuu ?< ގelٲGKK w@ZE& άV+3ȒLaaD.C! 'ĢQt:D"r#FTsFlVTWUJ%xT~[!{ff# (J>$KLF荤_΢ͅX\d*`@$ 7~bzz:bzz:ropYRڦߟϚ`2q|Vd`0^x`Ncnn|Ñ ŋƢd2*+*HSκoe"Fc`_f͚5ttto܌``՘fjkkX,;|< @DSSg޽tvvY333ömuڵ+q͚5DQ>V+z{sیqgy՚_`0qFٷo]x<(T[[6jt:F6T*mmm;BIII"^~ey>֮]K[[:wdp6erjL&Zl2GɥD,Iɒ_k\ \38b4)p8%NQW[,D=8ܨnjIMII 3011IEE9].2 :쬛T*E]]ΖYf/-5/XL&Cf@n =w )?JZ}|L&5{sO-wI-_Ala߻7?[O8֭[?q>n烔o@0ܜƆ%bq. \fu_A͖fe`ǯQ[[*v/ gYFaƯMظ@0=U_A͖EVXXyvF&he-t~_<EQd FkyLpo:_#w2E*ipzklkzcLDn\Ĥwo5b!.O_fuuFd3=\- {&"DƆ ԗԣ CLsnZ`8w;ANt{^ ½ffv߾z>Y4=,z?̩T ǓmV3FY[^ eA-ʔ595M(ZfYΞ=,FyWرcEwACCf̯866ׯwn/cp8`ݎ^gzzIIIɢpjj RT<8eGt}Neͼ؂^Ǥ3UTj5f 7!U 7^87c6-)g7ͅ~yH"J(? >Emmµk:Ƀvr5g6 E!IΤQe> 奣i±o^N7%IcYxC^~~%FgGU;%ݾkuncE!{}z" |[j6mĪU-<䓬]ӧOL$aƍlܸ:ZZZ@ňFl_F={сfyv$jSEҥK.IPWWG&wԩS(Bmm-eee۷/v:ow(2)\3\觢WOFOX2F<'ZClhذXxs:EQBskܴh>`7YYJ`Aգ( dd:F78|0c1L:#zgHj3!#y|} d $H'sΤ%h"J<GRKn=]?J'-%ʤH0T]诺^X*/;ɉ\?q?uLʤ$"ddI&OsiPso|Z-goD"7o|SSXda.'ߕj:::(,,h4{y, |(2[nIZH 2VqaX:NQQ~0?KuQTTtŹ%Iey) %qb$d2{)ݣ\gS&.N^"PR?\~N_~.\G/g|91t xqZVS(Ǥ71<3¿wϏD$?ΌeEy3uuيxaJ=˝\LCI=̴:xeG>AxWNf|wwx`~z觨5nc7ɔwl_qq,Ǥ7#%gN yg[viXɺuljgm{ʂ d:5vgpSC'8y>C~*C?:N?]~ue"ɿĤ318=wo?T475x2{)=[FQ^PVefcOc5XڼA LOO*dʧrv2 >6rAeEE555.Ԓ0 jryvAԒ⢢EDc1t:?#ď'Q`esfEhe-cSFdIF'-ߜn[0 Ț ƂKʭ*̯7ܘ_؇ [C5Pdq-?{u*GT B @0$DQeE۽ynk׳̗7m(Y,RbId9*z(HQbtka9{-֮}xثn$dؘed&Ы7x5ߘ[8n Hb_W I7gaΠ4o=RdgN:F;(L.dicӸqaIW=ڧl3qO疣Q,-'^㯷Q9gs"µ9I<94,k;a_]O=@p+EY,/yj͓\6V$ɘĬ}S܇ ݤu TFyR9m$ 4;v7d3`xfˣ8N<~щ$$Gњh4 sX]6Eu_Zӻ*:nDxsB^ˉ!1!Q1A&=ccbp8ϣj~Ȋ"Ӝɴmi4&],:DFAR>G0-@1m$m>0G.k-C$1w#sYS0{/Sk$-6!{~/Q=4w|$c"ӶRcS(4bIqˣ-kXvBz&z" bLF)UQkdgcl. B­tAiZ Z]CJl :$cRĈQs($D' qy]ۇ`EJ==k?b2 @p`5w}MH;3C"WYsY^$J$xѹQ[IOkk[&> *"]>>,|'d޽( osI:;;y衇gߧCO~rWvp9wu]9qyyy455o~~a4o`Ν2::kq~S\\̾}矧" IDATV+?!}4#R !!iL&32TJ% \֐P pa,YZ+ݤsDڊ;R^$p(|dPJ P)T$Z~f6lkmw=&';bj:} U_DLwW99֘g?/Tfui'>>_p8?ϐH$'? /2> 'N ;;~,Fs|%eD iiixvoͦMHHHHOOgΝ:§k:44??}>#7xg/^eD $ D|~$b1z(gԿ4sYS)flw6}j;n,khֲH$w?@0#Z a6ettJB`tuu{thZΝ;wf~wXXTZZZ(((`xxINNfpp~N8Acc#ϟ`tt6'77N\.?%%@Á슪NT,ss8NLX<vP(DB|<.Yz=:zE5Haff&kz( t:d2T*UD`0pdN긗\$P{ '"ct4+%FgϓUc9Oeg%3vD4D"4 4ǼsƜsߜ-Si^oWØe}'TvVq(Uϭ6baLI0"죺Cso?(,r-PԋwYx,"V\Ur7wQp"zD4B֫_n>Αd_ʵ Az22D4_dhf\ z>v5u=u:*`nF93b  ..uj?`1 o3s%35&v:*' p#b,TtdۖeE,hN9Rpȶ­lLD7NĈ. buZѩul-JcE:XAc Zc`:IXO~+ߡcZʲa"Dz8"WdeJ \m1i46o>_T*g7x>ח |ʭfwuUиjk(J.Bzn |k&d555ȸ l HzLMM199IQQ]\&%'!ee' 5|.3۽[˾vy㍛?)\!@͌̎`%l,Lۦqz\Ʀߢ"ʲ?-=c+ 9z@0ZmvC3C44"VqihẂ:u"vɓa 0ݻ[^kײf>bccٰawF.e???,:NWWWDdÆ l6~oR^^NGGO<8<]4Ib 26hn}RbƞmXqd-~5ݵ(e5J V &DF` %i%'-jg)I+oOar!+._Tؿ?NkP'|ffT*jpl6ccc\.JKK)((l6cǎՄB!n7lڴm۶XV^#''gQ Μ9bt.z/))KuD0oNۓqUjĈTwװ,cv7jLƻ؜\rQʔFoC"5́n;F!F.Ǘi\YgXqoذab!7??ٳgQ*jQ*H$dƍ 2LF("99/GK$.]D0d2_ իWv#cccb4Ύ!_MBBF񦶛n[H0$ E Lm|S(فe%V% "X$?DD"^9*AͰa$bI佫 l{oAoYW_%++dכõjkk??+\/ dI A" ~>, e~Y~ *Jt9<C$E~`ƻpz_)6kŤ3)ӒLYwZih";> SjXԶ0ZZЩ¢ݏyN[@@@@6sբ _ߍƼrxfPXXHaau!pXYUC\̌tBˬل\.gnnC\Áb!6&ѱ1, +DevvM|9p20__nEF@ve`>h=v?I{G:F8thيsttuɁx>vy^o}ENSSy>c.iwhoirqf- D^^.cc!Z `hff8p@0@Dtuk.^c]W:/.iW]͞ {:M3 G.:rx({.muXeREE75RqVAZ[% >NC_Ò 4{.쥱`0H%:F;B\ho9i>qNջEh'g+sa/gQBC_]c]B!)vjflYSś${ik@{'M3>7d]g_~ ,T]?SS \e9#(\Ym_|fZ[# cqO\:mPUeS]C@..]uwP6G#cl=ekz&'qTwWs ocǩaυ̻e>d .U5;q^A">>˗;8x.޾>}$`ͪR 2x^b1{ytueDΜcNF,Dk7#BġWo8QkT)jzjM3cB^9*{a{^'JCCz5&2k[_$96DIj 9qosv}u8z:2ƾFL:/m}uYhig=|_qz>s9jͰnVbJB!S5ޅAc֗"y%blI'TwP]FF.80j1w#/m} ڀL*ߝ=C37K2=VZIVV8[V3pE!Jy]}>J%/wq{q1։L"ckEݔu JZlњ^/;R)F\.G*VH$D"r9J2XVP(H$b1* BH$BPR#RsgC J,ѩt(JbDHMHQV@$H2&!{0/h1&9Rs7GYYkѩu18=Hd=i\&'XVhw95FN(D09 9$hL$ 0-C-|~_Dx v@$5rr;JS\XF). LY:$bId`(JRKBN^:c+E$-禲uYkY]FZl7Q8+Р1P\Hv|6rb-1O?bեh5fggILLm:_E,Ñ@V<ZFz9|ȃE0zA)M/1xuSMa:IV\-K@@`y,`5 Dʱ$)Llm>$b 45*cNֹQcѶ𗁫u׵Ac~wK۾yY^.B)@(b6x &LD?Ex7Y,Cύ qi =_H)3-IƼs~Qby4&-6ەPS7E,ֹx=4bE^?j?k,\ x9ǻa)ۆ\20uWL.*.ws'. p[ٵk/_^vN@v#L"E, ƦV8,Q,)+UY@@@B7|^OEkx$ͼsLZF*//w̸Lv> <~/Yq$2sA!S0c8+a9GMw-eK̍c̸L P+WŇxAJ2Z@@@J("~^OEk`D"+z9WR:N8Kn`ח^Gcnn:JWprIęx^zzX=(S3x?HKM̙ 4 111 w$ZOz`3j$2Ģc;YWʟ_T;Vұx;%b Xʲ3Y:ǺPU\)kaqα" I&p ޝxHLLL|vFp8PgFEXZlY0--FV2!BD"y9dgeP(8s|6=PNGgrMͤ$'35=C(ğ%b~\FVV&qqfFFF9wr߳p+)1)O 0k%+.+!g=rᚾKn._d5Oiz):nI9<#sΓ\*CР+rr!bhxDL|̌ D&G|d1110kVKK'哟P$2ϮPC,S^yv3KKZD|gwDfŤ7 0gp8Hb`` YYYXV0tttDbm6LNNR__OJJ @ t:Fn"χD\G__jٌJX;z~?@NG\\RXXH[[`Ggg'4&&&x<Fٚ |J(|e0en kJgW7J];\MJ9kB:} U_DLwWC8f|zz)fggtߏD"! zYv- ^@ @}}=+WÁ^GPILLu!EXVӁ3t2>>B )) j'N@׳j*ZZZXz5H$ %%ٌb`61{Q:J>`A~lGw<|ddv͟fvu8;po8}KMQSZ̓ͼ~ . ]b1GmO,eZ?vڞZoOp^Cp; } >StuQS/(UuU#٧i/|kc}q[[#>mfxv FXnf6Cp;Q&woB`HRv˅G"p%4 \.n@ @RR6 HDll,ǟ$ IDAT'** Amm-LLL4r7yL&JJJCPP__&  1LDݎfvv6_PpJRn,^u# ar~&l.+Ŧ5gЫZe;| ΪUnLh^ Wt:Jf\ZilfU*?gx!>?g9qIN,{ } 3c呒G2eDұ!gZO1gsHL"q3V`HXq2[ n?n]_Q]@jl*`GQ+\.vS\\<ׯPRR#++ ˅e֭B!VkX q@ @jj*`ݎh׋kҮZ K~~>PB# FP(l T*v]i|p-`A4đcg'56.j>dc^92f\^ m[x#%;xثi?KarZ9qްq†Q%#NrP 5?F-Wiouea>)O^TQ<1>.>*'؜ 9#cr9.+2si% OȱNU8|y<nwQŕz`6}̏!)+X?jgo{[>]U.C"0k KЩt]pbbXamZs:ƺ)L4cyΨ5rn5kP~ j} |oQ> pxl-܂F!͔B鲧^TQj=;Ulٳ$,# uZ_xX#x_nG,'/r<^8כq8X,e !#pgY10=@`*@!(e`\EX(bxQi!F-c$7fc>Øe R(NSTlܵ^ 3P\̵͚Fx 5X@Z9E^b..gUz)=u@XP!U Ȱmvy\[Dа.{+Pʕ1zePk xөu$E'<¦;)4X+L$ÔA1iQ{csc8=N'ѫ9Ɋ" .[EAt(~ӃmYk8T# H]9"ARcSqOGSI \szhz B!δH*JQUQ Nἲ;gN5GN||K'yhC;\ڬL[N`!.z(|H=i+ xNsXJh>shZ>)( FfIN$'!l!JudF-$HNQ܄ &],I1I ό7+fCz 򉍊Y4VlT `Q(iifLXbbZ)fQl-ua8Ze$óø}n'1:.6b\Ŧ1b #FAc  ?5^ǤE)Wh+!B\KAR>YqKlLsQ[־tRڬD"T%%bg_BT"Ea֛8,m Ü%3hUZRbR`mzŤ0@F8~^U`MjrrDbu.ÜD,ftvd:;#W; 1ebk6YRƼ9-#71$H |x048gR |d>::zݰÇHQQ>|\Ϭt: Kfɹa|>, *Վ6w< ˌ|{ +.ss7Ef.Z4gb~:2j#1d{7m'C\@@qY99ᝑiZZZ0 \K.111;bKgg'*b!11i^yE ˿ ?Oc~`aVejjD4 ǣ,x@&ۋB 55Q t:IMMio߾B`X0L444 ټy3V4qqq:t;>󌍍刺Bra6IIIb0;;KFF>A]va6xOp$͊ݴ TG.sOw\7~&7!܄{bAxnpe4sm1[6do' pw^z{{Q{X,XVbccOg޽DGG鈎^ΡC_YQ._nȑ#x<r9.\SNpecn7;w&\.gΜaffcǎqa, N"؈^G,8v~RV%** X$444,ÇZ"EASSn}QYY˗3gΠQTJ\`#-6`q`n;ɅLf_1tvr AjkuIÁH,XC_#Vپ͎p.3M ]//":ty#۵jGyT233}geeIIId2-5s;V~A,/JryGlyxpA%^R^VXp\.󑜜3<`d2 >>^H$رI&''1LĄdqEȅlpL?̪UXv-111C1##@0DVsNrrrl޼-[FL%`(Zw67opy]DHD$kyTuUfnkЫi;˔mˣ#A F) Kc[%){IbuZFHE@@@@!~al,/(++(@ݻɓ'bp9x% QQQٳ.gAZ IYY}}}tu] IZV*//9Yn---~f3f:\.YYYb222n iiiARLJJ 8b1qqqdfc il _fFGG#zŘL&z=)))d2"ׯ lXvI `eU .\o`Y7|(J9JQa!i)|>|> "ZvݼR2~y>pƓHRd2>LF$P(Bnd2!=hC0DT"=:<w\`:ɎX]V hZ^9j${3oh&癲o7fi:T, ~wx_)qqde]w>=TwWLyNXNF@@@@ycJ`0ѣGϏTO]Gj0il0@l!jj)@DRbC[IJLᣄA E[&֭ ?a'|<7' L͘L'auHILL:L+asؗG;M̻|Ȏ( A0b{쎌} /²ີk9}(Vf̌ ΢te R]TtuD @񜩔J>/g~ UX5\,`xdg~ LF ;{P(d" T r?xF}cQۢ+eev޵lΩ | q_dBUYu:q=׻8C/jn#Wjj eEQ!Zֶ6ZHOK[ŊB_SgRZRj|ecD] D#!!a4FSYUM:MǙ!Swr-* U8 JGs|z q >U]U itSڒ\ݪwِח-~q+V  ߽0҄/mk][ wm-p=B!8C]^0SSS-:j9~8FJ)..Nll,;w3gMyy9gϞ$KknB0**jQ*)66\!'/7^B ?/DBbb99$%..OMA~FcN'+3)aJK5dSD&Pʌh!III&== MuW5|ȤuY,^30ʿw::x.412;ϼs @mO]#%;HMѕ0c17/1B4 ZͶmȤ23|mTfRcO}4lab~DތR8-픦D%WݔZ&&"/v`bXiiecr~88t:e(ecq yh4l76I' `!ko9|V;X˅4.YJŽj*DWxi|<ӧ R*!;nlDiY[0_ڰy8{\Ύ;H$XVj~ٴi8{9^|E>\ +.WPUE)m J(fpWʬ}vQJU}JKi}_n0qI3Id2<'qƞ86l`ih轁EZ[i+IVۭV7`;vQIuϹ{{ܳqm6I.ZA)W$3B꫓7u|<_ȳq#;#ZשuR Se+ZQf/ orڻdr6'P\%Rd[6nêN&y3|L.9mUmj^?Cxy5/CT@tr}#y,n.ĜN4mm4wAxNEy3Yt7"SP֢,l$ "Ȃ]Lƚ5kXjZ, k׮EOGG$gr 9+ ®Fz]}cAuye9$91W_>2LVG+֊k1ՔTKyW֭d;N]8<˒%e2uGֳe+WI6`AQc¼mяƊҊZ[U+u~u8޿ L!C;iq\o꧹RM2\`Y2%>*<8ϲnx|WOuV)Wc?H2Bmha+ع|J-͛q XӰFj`wͿ|×i L^X"Q\߶}oLۼ]|״oۆ~6.`~i-'_^݆ bJ=C+Xu~ݟc.OCH LYgGk7֑ʦB8l L`"ú|4`!>#e>vZH2B61&lLgX"> E,p-EtE,b.rf>Ӊv/mpwLq] ^]OOjς8% \GGl6zs_ DX, Ǔ$IKeNGHG?8!$Cl߉-s}MI^!͒H1\@აSzL0K!dR.MD&c ?yf?~jw,'~UJK;/G28qb~W\<8~6kJgۿ7 `lcgqko$?ū~J NQ20+e`Ο?~9jLF!_ q',( ~1w|NFF Re\J"XVZo&<5Dم\ d H<,v^a(P93xzf\DFF"0h4==A&'cy!Ξb6SyT:zT&/ 't N#dRfg %CG~A>}+h%z>l#lV!)/ertYsa0(dLLy>L~7f##&&bM*d2|={&p8*Nd۵\@)35bQ38fr2Nyכ(+r%RZEq႟$2 +N_@ M${9+݃L*yN fkM)Z i/:>N'SO 22y?Ǐ{8yC__A(p򤗿n$8.wǾ^/{"d'<9IgOV[2wcS?AdQ*@{7*O~"ndrj3RWb G{(+;3Q(d[3bQ(\RqG5+Vd(2T*9F+l|jʕ6R)NA6hTq%YqR3z1B.'Nl6Ou?כ4D"YiB6[ RU%rl65xAB!P(öm\.^N1ZFRq58x _DTb/x i\-HEE ]kQR%zZڂΦcx U& ÄF'`}3[֣1XZߎMNЗ8+t/M͖:-4481KmNI}LF\EHyg q妛*^2^gvç@&Ck*A7cn.a 孛(TzS {% Te8Vm' xfjQ+;VߞkXƁ.&'|S5bb3:#x(K4$kזnB 7f0<ГN9uˎd2NqM7&ٲ6 alTɓSF`` L]\.Ţ@sZ;crFLZ#٬e:#;wVQWgd;۷WJ0t)Tz3Uw1u0&RTZ#S,(:=Qd%:k9S#W0U\Yʈ8?:k0U60smFPv `kw)oۄ;ϡUаV귷Pkmb;'Nxm[\Q(ۨz,5Gΰteˬ:ed$BS,8zV5H0WO}]l^ Ȩ!+V9xKMd2yN`bv6Ɏb1ѹx1ƍvڬtw(-ղ|(۷;dTU9sGeV }}!lVA YMMhj2sgG`D 1pcc1֯ȪU% jbL ^]BIӧZ6o:k֔R]uuFfx8̆ vz{CTUYRƈR):f5suhn Cvt;ɪU%468qtjPY={&Xnánw \ӟʞ=x<)vPMMZ-8ᡩLsX ˗ػE(@]8gc weΝX,/ӵE\|i=v7^o  388 7l<^ok_ C  ijE=\.pm1㞡@_EP͛6bXx(JxuaU㷙}h;c~#]; =vUJزebiX |!r9WuvGs\j}W 9,˶ZqSCygͼxi4w]'m1^$~nz6""cٺks <_ů~;.U+좳5W 9y4-M96˖܍R$NqzzT^T"+PX-V~(#N(gϝg?qt:Y|9atlD"IMuz~6đ# S}9/pµSU.D"Pe߻?(~7po|MrVr;^ D~rd2ټ B&ݐd~uW-*l6 \_3"m-اGW(k>y=˶|׃+^Gr~U ⸋D>:t*n-gjjal+ &qGr$=np0۽al.\9g6}uS*;J%xiJK5YI$r/jH~{ ~$:#F?\ƞ=$942:Qkwzu ZjZzjv+Wq:YyA4;眄ݍ;B2)k|[oM}\FWӌXPcXv g8p`o}k9o5X/XlZNÇQ*e=ccQzzɟ,'?ՂǓ+8y@\yx` X}& ɟ-~zr-BTJgIr|k##zk ɓ**t,]j?--֮-e` L$۫9~|MòeV%<0UUz֮-eɒK+Ǐ9+?SMMn3g|XBN0'ȷ7z l\NKEF=0##d[nq曓t = DY.ΝSRDþ}lzz))r7re V͞=(rv-V[_obQ#}9l)Kv'I&sE9|xf3y 3g=c͚4##QΜo-7ϗ3 Q7gˎ{|6۝ov˗ۈŲ#KDŽÙysN{8W8p`H$C U~a&'\)?>ϗ{xqffSO hf2-XĿe,pWX>oOոTtrnY<~l|1}TU9t-ٺMs;ߟbժie"r ,]jy? XJ (+gF|KuW-1jb Ջ04f2+~v [s i in} G3l`~c*BsSr}zLO'hn6/ S/MMf51gL&:7qՒhkyFd2NgUlwUNb2)<\AݩS^B Z}{iWĺi{g߾I 9K~xcav6ŗpoHww͛9th NmUH$x7ߜ LcG55:;$زRF8ӟL*'HiR۝9?[X%kWW_kWb<]ovpႏl=`f&\.#H:p9$X,B!#mx7Λk֔rجT^Qx)vpd7ޘO[;wf3fʽ8L{67.+m|>HZM<^3== rbv"l޼p8L&χ'ͲrJb֭#illdttÁ\. ˑd(J>n78 |>HӸ\.ioo ߏbATFq\P(LOOJXt) rDQ|Cccp֒L&)--e||AXd D׋``bbM6xbףPallt:MSS\1h4Vh49snbt:~P(ҥKFLMMp8d2Q[[Kyy9333RZZJ](/j͞aӦM b6 8fggiiiA.S(x"RdttUD|tz=j—.1v泟@篧' mU*qBf\|A1^~})7X+qzzoK]O_G$|>ι/\hOڧTLO2=#j LP|-m4x]w^+Hx>i_zhoN03#Ob{o=Qzr@>tR__R711A >CCCt:)//gɒ%i$2::Jii)|\.^bCCCx^0y3 DQ0###388 rn7XIccchZd2333b1FGGe``@*?22B>GNt:E6eb͊͢mH&E^鸽,\^( #;Jq8yc؎bߊ6Gp@ # Gkcwb"e:uZ{žmeL|v:-+⵾2YXVŲd2ˉ׼؆U2xEVp2w]<>;"liԆd/;ېL\./x(؈st nF+/!)܉EXŶ^i| S.EXdD_>gGF"s,ƥE{g25/^vqúJ(J,|nz=z^:&d2466cT*lh4 r92 td2^'H|>/P(P(fږH$HRR}Ѩ@P(dY9wd2r<44$hD5b" ng۶mH444 Vt: "UUUtMfZ[[f×:6 O" LH$d2@<Γbs"5(J/?NuB2$͒dLNNV1 TUU!$jfDVdk^c+cGG92͙3^6n,?if5~{oLFqr<ϡC"i/MH䘞Nga2<^o'=jo qyxO A$Eio_$b?Q?@2)Ü?c۶yzA&&bh4 :;8Jb?ӓ EX8?./[f=<mm~^Z%Oq4hH$K/LSO 3#|GZ5 3 >NbG/ &g{EvեG9|x xA ?C/HV3 s[VG__%KL ٽ{+я.iS?E*+ӴY/tw޽ GSScOs4˗1#pfn,X3ʓOnl)G?^,5o1+U)4&#GyY֮M(捻 /0v'1դy^yeիKس'==NOOF#Ƌ/J1"ˢVEp={&P*崶ZyA@oop^*p8tF7#0#GDO?=lVq&&Tr"<;wV3<NyزtP><ϗf:;A~>Z<'N̲zu)gD]cfIyt:Ϗ|QJ\GSRϓOtk~Μ(tDVcX7sR ?"kIk$l?A4ǏJO>9h+K9\F?Hxazܵf+oNr㍕8;L&gyYz+W3W">j R)S*t:V+JDIIh4T*bh4oɄNCP`0P($IjWcT*էZ4 ZшZl6KAt JL4 H(JE$`0(9`JJ%2 VbSl!HPY)V+W'JF&U* Bfvf%GJ0;;B FERQQd" RQQA.S:LTǥiʐL&R)B ~?VB!SljFLOOhfXV(vZM$f!lVrStD"A" ϓf6^^(hT WeSm۪ Rl&&DlhT1:q忬$Q_ofd/}i)~~d22b^lb߾I,_J#O<1HC0nwV7+-,|z<窈K|ˍ92M,_oᗿEY[[-J X,j6l(V K7 쒔ç?]+m#| -!|s@Fٵ }a\½6+c˵RN/~q)55&{l`n S Q2jC.Sa*lVI̕tJ>_ YRՊA`0җy9'1nwޙA&v+O{۵YS:',^l.J rYP(C6xFG|-<VyNk$gfp睵[)+RUGUPUMb+ٸ JN{(O0>.0z)v]wNg{7&ؾ|t:^,U \\+E"YF>϶m8F"YeTrj9@znlTd)QTUrՒwײukG`͑l39) Zih0J;ab"FC%e͸RdQJhZߙLN'$eg4f r\r.>iV( t:EU R@"q@ fG]311qzj5VU:2ȥЉ*b%埯R^޿D")CqrR)ɖW;ՐJظ`,\9HN^V5/t uTrJK,Yb駇ij2S^/r o5wבH 5PZ+%#/8J*%V+6T(+b眘*4h]K2㥗FFZ%2 q۫yq< >_bz1ڀL&CV`0(9yƍvng}j&9p@2lj2SUu,/XD{^pD&陙-r-OR]D='T _XJ(ggFltA(p%`Pˉ#]K6ق JD?EY(f۶r|JN"Ka6PKllj^xa+Khm5KcCݗDlLB!:izR^dwphkSC^]š5twP(d rWUyQZZtS%2۝ ZSt_~yZǓ$RݮESQ]kqq_B4ZwQR\wTy")"$YV@Pd-Yk[[g۵omYr@$!aș 09ιQ3#ݟs8TݺuVׯ,Q1c&͝_ZˈYHN6fDPTדi,#Gttx2JK~w=< &ݦص_'?ɧ݋JZ|,jt:5z>F6"X,ZL&9TnF{{{hltti=\Ň6өZud>aQ 1x>qq:l6?,5lzy., ;ҥ'\,-nZZ<Ƴzu>_ZP2uddĠR}Ǐ&1qh;kɣ\v{x=*@J>jĊŢU >Ga){l,Ŏwڵ-twHKK@Nb„$x8?\LWv?u.J= E>s> xVڼ|h!ҥK %n-|O䵕ee_ѣuwQ;vqp7ܐsƙ(sY'Np㄄o';?ʥLB?:x>etT.):QWgqJwP/vVh4C־ϴzo/஻ n}v~LB&g;~{jDe4<9&7^ּM}~>坭iRxbL& +V4lHĊUrnv;ʺsqvԥ;w\zŋWg_=8+A$r_j`N⾄%;>xM:Ύz ѣqrAZ7 _2֭k9{7ɥ5-}#y啣B5+V4*oKqNNgpHt_o5矷sm~HV'PPv<__ۛ73>r>N]^qqɤhT_MYٿ.E{`˖-[]vp8ؾ};D"9x bϞ=m6C]]wf|>,m۶qn7l۶H$ٺu+S^^Nmm-}}}TTTsNv;| 9Nٌj">‰'Im۶qa7z烈ȡCM6ގe۶m+ *6m:m-v۶m# QQQ͛lڴuֱw^@E_nOOeZ ^|y#^o^̙itumk?ƍ@Iwީ qlAOIn] 991̙#wX_r99f&O]a<0yy>j&Er1#+HJ_&&qsJ;>_3SY@@Jزݻaժ&0OJņ 0LJ0TZ1l߾Mrr25kKJJ k׮E1c >"#GdL6;w> D.~ IDAT2Ojj*$!6Q3f mmmhZZ-~]u1&Mt8\ܴSؼe+>_%"0 ,6Fp弹ӻ9t0۶@RsFOCŌla݄a snUlG%fCӡVHKO=C;w0kV:`J #8U& =vڬIZ-pLԩ) tXKYx,^\´iv[_CZ+SzaRS̝Ac4#O>9z&"錢A/.eDM:vGSL´i)qG kzk.?q޽=LJ__ @JŬYL-Q_⪫HL4 B?H&MJ&1@n/Bj(0x*6I(Ţ%&FC($2ztӦp=vs<Yn^md>6ɓ6-Ng(,/@($裲nL\!{jہz}Ř!c 2(ٻmwu\NMVOmľ}=B@\^MB`0 w׫E5+#̌k2~"Iӟ>,-?Y)}} ժ2~=^f0${[yգw(LlI^V|LnAkf02LjD$.ի?(L:ugV~5k֐3FyĈ-{ĈC;S[TTDQQܹsI'2]]]vaee% g.(۷oWm=ERד1F z?k.T*:!۲e  p#Зʶ͞=C>;]w]6=Vrn,Xx{}򗿜Ǐ:t>pJ)qީ6 mrJ%?VZ_W=z!hX}j  aٲEbN~-733[[=44,&Pv)iSgމd$K|eժ& LYӢ=v4*Z-{IK3^.FŚ5C{~==~cΜ %5xF9[e'ZțS@|~tj+wOoo/8q"EEE;XVx^}U9s&.SLJٿO?n |#E9j|>gϦ'N0ae5##F͛QL27x1bnHJJ K,AVu)TTT F2`0dϞ=p8(..&PWWje֬Y9rLkټy3 _cǎx#//+V0l0bu\|q3 E\DO8rNK8RS w׫|{+`VlɂM$"׫;7+ Y #ȢEElɎ6&ONf֬t~iyq9cRd˥{iltm$$膬ڲ< 1,]z8%Xz ̙ɤfʦ 5;g3iR2O>\ ^Np9lA}YIK331{v;uQতĊCl0pap}{k4MR[Ŕ)̙$ |.~xA֮N/:; w_!~؀(J ѿ$//"ZZZhiika֭T*6nM7D{{;SLxFCbb"W^y%/* VE> B̟?[ vrU2m6(*SÇ2:Ȇ 6|>===$%%39]tZ 0ԨT~ڢdөT٬AUD04ax< GK q#p8Tᰜ0vl"Ӧ(vyjك3!AOD)f7'Nr.?|06q8Ne yspCĸqhuf >[UWeo_F5j/%ɉkxrr_gR+8&LHR<{ACN4N{8!j9HY0x_QŰaQxZGwowwt "߮>WcceEHqq;Cᣭ]w С>z9:5_!++/O +jhyjfLC39jb+dJ>e)Imm&ON '3D=v|J?֭ $&0 A z.㏛ٽ ZQ y8-ÇǢRmmF`P[46/Yhm`0LR/̻c3&>/DQ6/ɼnqq:x`N}z8_ʕͦM_Zo[vÄ 15/'R JTROO@o#6#m.*M8wO?(Faa!*SO){84f.]ʘ1cHJJEcc}vEg0RTaVZ$I477ޮh*666؈lJE$aժUOjJ[[ې<6m"Rhmm% UEA  ݻ`Nǚ5kPլ]ѣG~szZp}>jf„$.dg>]٬ˡC\{߯ڵ-==2m5>V7kʊ[.l_Z…h4*NpfΜoQ|u.p(_eGsC D%o"ahQ]!!93j_W659g\. ^NC$1\ Lovα[[Bޢ*7 }m;lӫ%J(-*/G [~oŊ+{Pw7H /躮^p8L8fʕ<3TWW_t9 }{gy7p:/܇z9t k =I]@I겑6ԲY# im WȕwHnJ}qr;w;inbѴq\&Luk51p*NcD^R;^mۉwF%JoPW]/*b @ekkIZƍ~?t"ᅬb!+8n8j{q~LbΥ@mhO> cQ] A͟OX?[n9Nvf.yVZZo`0rJrrr(..?FR#`T(--%77Z6nH||< .駟l6pBC]]cƌꫯVs_k.2334i˗/jr}SONee%555`e_|>s8pkL!6 a>↗Xc6Ȋ+qz-v3Aћ1 1a?º J"$Q^S_MD%ʷ65;$鮻&'?~:B$}7cHbgV?ǦMt2F/XJEDCDM"sZtYYYC Ve̙gﭷފF!j%xٰaVO JJJHMMwHY .T&n7$1̝;Jl6: y}~v8L4 $zj~?Zncfa݄B!z{{IMM%&&{M]]`nvrss>}:>,O2o$>&2z}x>220MaD%%AP&Ʋ2M\A57w.TCCCnv01e DQ$-- OGEEG9$DQd׮]l6fI#|>/K&OV%99*8q"dg V8 pP(ځQoӒ@v܍f#33#G*t\ݵ|~#cnj\zzz0 465BRb"m,-m$'%a6IRTTԥ&?Ǥ7qƝwI}uF6TX|cĥPZŎ;T0 oôi8cY-p[̛" `uʺ@/-zuUjo'34J(Q&@PZDC2^T&Aa"N'F*&@q"T&*Ӊ` сE=$~Ƴ[nX,<,^XAP@Kn@^/vt \.* B @""^H$Blli29;wdԨQJ8&VΧG IDATt)[a, :N?(bp8h4b0AiەKgعd}Ғbl671{LVT !jeݧB#a4j W_ym̍_*hiiE_6>eG*AuW%,Ѩ2QDM(#I"Hų.qr,`NVga٘Fqq.|BF-_bGFf]>FNCQc2줿1d?*A.d{t/Ñ :FzE%JlA@Pyt.9@ @KK yyyCOe矶13.}cd_'J(Q\8#Q~xm0:5q:7a,XlT*޺^N_-͏*z %7]h:o,X76B;}':pd2lG"ulsR|cڜdOFlF,U[ۃi F$QD%;qK識 6(C;c35:Ýh}m h m*hFяPi$tzZw61!H0qrZzeaB0Gҏ֨% q|M5k)e=q9ccflG%`[,|TkǎUO?~>ʌjƏύ7Hqqu4ə8tjGScxMvbR̈SHάvQ"0=GmLLzl*K|C|Ol`;IIX,hqT:5J ml: -4W4bN|A?50:jsPz(b3cf%J(!lc{U$IB㫩!F |_ĉC46޳]VMM0m 5]}5χg>bCm6SUUEYYiuJJJb?' {q1q"??Gb4III!..E_ʯ~!):TKSə.֏":3@my&7ȾlAW^!Πxh1:SH*N}o+sț7FКLzd*~?xDBS5H魣01(QDFhFz{ˌWWU]C?l|Ycֵ};.Am[$~;*cq1 kwCDvt#1`+++c۶m|t:ٲe e֭twwoog-/ʷG4Tӂ/5 ?&)iHީ$:cbir2άGg>rhvv(QPthSSPQDNH87oFlF=7ӨQ=-[]_M\ӿ~=iO>:>MR)?&!ŖĠ`$Iv/ IH$ZV|D~M$Ixx<Ӷix2ܝ.zϹ _!>WA%ЅI|[;x-3Tpug/;qW: @poVGO{=_}}}&E}6v$iH:;O{$"k> 8ADQ #\!"QxB8AG#H0 +۝ZooײyscmKO\жgBb݄Bnۻ./ :wgs^1N=[ry}^aM7݄;2{6 `5 ԩ87o@e4dslH[oųo]3ߏ }l+w=~!{Y^xOee%wqoK||<'0~x~`9v`+ESu%h44sB!yx ֯_ȑ#ILL@>\C|~*6cs#jOw _S FED;y\I;{8%pGB3YI$ێ$u!:M!1B onb(*I)OU$㧯/٬`P$IfB!NG0.&ONj#)@l+?R:M/޾zFk2n[3q#xAP 6Z=)A%ۺo}o+YSsp:tw_tOY٬ pjNgEU ELG%?exaL&@[e\r@d418͈DwpX`Zvo3F%WOUX:*Ht*.WQfc'a;LRFneK'&+ey:ĉIL^|>ڼl~֭kٍ @yy'W^fjjinPRbU FhA=*@wFPHWwSvm ''#IN;>p8vg/@ `B@r{K}8档HW;D0)-b2ihov0D@QO[7.N~QC0( YWuZ첡K__~oH\WF@~%=݄^ cDL5}}jzz$'058AN9`TpCjZpX$9@k| OXi;AM3FB9Yի͕] JJٲɓ`&pXO0(סۏ.ߧ^ի5*pXD4)v/ok /ςѨAu҂(JEz\S;|_666:oǏC__-{!hP҈0AA?`;6@>ȑXNEc2c)SPXf2cjMBS(f_f̘1ԓ2;;_Nnn.Z҈!))#F\9GzBaDDӱ`;ƤI(((as=61)f?뎢1jI.Iy{>/~q9{^ <{$ E$Ʊ JJZvB1RmD squ52w:)j͋ىFo"c܅ )Ɯ4[YִRGSzH &g&-zk.zaLDW0!Tp/ni1j)y$S:R9)9$M򹷯MjfoHŰnاoI5C o"ï-RxJb40˖k[tfh^~(FXbcO>a"jw=|5FfL%y:~́!|r$/t8-NN>ǪUM~zB!< 92\3 .DQbΜt,9AAcikp`C|i+6+~VhpU]ۂfݴycYs30!7<*`Z'Æ1+AI8,ƳfysUUvRS5*++28zCG?/VFN׎rZĉI79ivӟ*#<@!FZ[=桹٭P^D0(hQSG\8֯onnaŊF<<@mmIM5rinPXdž m?`ĈXRSywd`ӦvL暱ۃ̞Ζ-TVEXV/?0|0۶u1n\"qq:gY%ESABj22bرÆxzB'x?VRTٳ_j4oqL^n5Uhjr#Y{ʕMJ`K5>=#N.m$xq{j 'g'?Nf.Wޫ窫2ioRY)f&Z[=Rm;ӧs*b(\PϋA|: `ci9*IԬ$}B~sI>WgJt3u)%S0%mCo8D׉_ 7΀CNϙr$pЏ)~Ȃ֨%edXc5*FD$"q Ζگ"ˡWd*oɂ Ղ2֪ͥIF\/ɟuqbDuۚ1ZSzQ dM Whqu6V^RQ2IECi޼La%q2PL23c%bb4\vY *@8,s9qEqq\uq@$Z[@NIy2Ϸ h*fL(&7ϗߴ }}[KrϦMSo!nbDZZ{Gu] ?t(Q{)RHrKv&ęd&[)3ykM˚%3K2q'q-TX%R@D4X% s>^t;G>R^K@2)eK6hT tiKpͅ1.͡ H$u=`kVSVf@15fihHߴ).x~lիnzsR:>RJAp8"l`ڔp:S^w3<;K(,\h0V16bb"w vS `?sR.r?lܘlM7-*O~Z;o<.wk`0(j(v[z]yQRLOGGK05TT738@xn'L b:/A~.ex8^XLdf&ʕVV-񸐾歇Ng$͐"n^xeAH=;Ӟ}/$h |Vy);pr_ЄDKg)kJ=4LN^Ƥa欋,,նƼr"Y:j$QTXd#%Fo+FHıT,C&SZO&H#+= FdrJKasͷ6rͷ6Rt ^ozT*+VXoqYŒ 0ٴ)ݻ'WK_[عs-}녟3;ІVƼr֪UBX,ZDB"1_kU_Xh~Y80Ga0(+ӣV)(бw*jNز@ Ioc,vpyal6 *ScWVصkZNYg=Dټ9dR'e]45kRU]|8/0-r-<0˗[[{_ǣ]p]@VIEO"%%zrrLro$nw:3{NR^n$+K7bhs'L7\}[ˣ0S˖ٺu 'FS3H)eSQadl,H{/0B$ʈF ksi%fp,99֬qP(I^o.LoVT*!d2I).*+TT(.'il`r =7|Zn&eyscTV*+xz;_N]]JIFm ᰰ!RYɤgq8=LOE*!Μ 䠤DOAA##AËn~oȮ],]jWWgf׮#lR<0Pj[xj*|^Q_8ަMyuW)eg1T*rsuL*f ??­7bk7CW׫2),";[7C2dܱ'e { /.V nQ$Օ#*DQ۶E0YA@Rafu|p8],=?4kVbwTDR@$Ly-(upb.Fv>C^zc< YWBsVQ/dz:L$DO'gΤ|ImuIh/]iUWǧXնPUR&/y \ g~kJQ_o1gA0FU~}+0qoL'B? ׀)ȵZdo{scX#=LJ(2dӾb R[kJ1N!so͓Hw) g R@$T>ɤPTlJԸ>;)y^f%_D$).^vn֬)m_bݢI7J E^kQH$D46 R^nhTd2Q̩yj#jVz$QB&_< 2dxQZ,ȵ8~+'&~C:,r>qfx1G?BP("SOL0/ոr&&ɾ? Sa?A.G]\wv3H$c=Ɨ644@GGgΜjb=yyyyzoߎf_2#//eUgPm2a00جV\A^rr9NƆfuuXrr  p:aFZ-v LFn%5̹=X,9d/K9Ν1c):|kt䓃9㡼ȯ~MsF ~sP9W;ٽ{^aI>Ç jk_w~rP]SD^|q'\<8Zgqc(T"<ٳr/r-&(-5Sx> |G9p`2?I>fgc,Y{;I{{nc\lؐˮ]l>Νfq;&0Tr8xQFG;Xf_2B!#N 13ɉ.֯7Ţe` ~כv+dQe9T$t IDATv;͛QHڪ*oWúuvV~v~6VH?s指XL`I(A6l_CSȹ }dQ.é(ú:3z$ee͆ &6.-5/b/Ԧ-jVkj *(,bÆ\6l(`f&ݓb&U6văR񴷻).΢J5.{d R"AfglJ|j UAΈłL@24;vHf~_"###l۶禛nb||lڴ.n6> mzg3\]NAVYy|R)q^0JY()sMTT),!2kC?fC:K/CS`iQ mc" 9#r%jk 製}w:|&,egxxZrr4l>'?YB!f*+O?=H4*Ri) %Ŗ~s rzO`6h&~zHun.lV13eϞ r|v-Kٿ ?N^#GLsQ*#緝"&EtTe"4!C>~կW a% |ge?i~*d~;U)_+N~y6o͛8Ν[g͚׃`VXAyyepeYu.7 GES#M:w\b K:7^ 25p3dx;\, 8cƍlVV-_ҷn!C 2dbd`pArۋ1'w%>{oqu:븤.n=4w6p#mͼY/Xr׿~sqcz-3dȐ!C*~ԧ>oK%IFw[Lf{g0뛥p’cØ4C>FېOS+8|UV֬q(kԶc֍ֆrz;o[?㽌܁f h݋g&0~zVo!b eevr$T_;gعs+37u5klq$"^]wRٻwF/a %ו\r#&r o &4b83N֕ Hƒj2e92d&Դ[n%]4_ U"LCv\,ei]T^\a&-<%84Bg:8A8~~G8hha۶1u(2~."~n<J4ܷ| [/, ?Pi8HB ~ % x ے?G8ボUʒFYyf{-c2 2ʪUm/㥦Ě5tni(>G}p8Uj3<@.,==^~p8ə3te0X cC,e9'|0F vc v d52֜*gx+?9ti7R 2b$L̙=F ?)?~_"ܫI$$ňF(D"( DHD" H$D"A@E(pv2\2fB?Sa:0 N9fƃq8Q_k[)nOa<#P$>$QDcTAdrDJD0@䤢HAAV:!r$RJK ~{16LLq2XL.O1gɲ`+Gx7=2dǾTAv?ּ!.6QVBGR$2Z.W"=Or 80E,&$dhH&E**uW)VXLHui MYl۶ HE7q17댃Sr9`$HF$ ³!lzb(J !(rtUYS 2H "l6D1Dia}B'O؈$$r1DQ 5 ⒀$-P{ٹs'D:|0sss<<n:N8$Idggcl6P(tAy2\]%>ek᝔yӟ2L&`,45<ιPIl4F kQ'NA3"W(X Yrl _.f57P@uVYN <}zIϿxTǙ#(5Y+\h 9dw aY!ֽ oLIdkZ `ؕFNEvs|xcvEͦjRRMMM6ZT.h4I,&0=!YMn Kd2\. U='g}{}. D"hn7.1AwMTI#cDĤHx6ֆΒ֎Ƥ%oYyhS 6t" Ӊ A@MbrF">1n'~YY  @(_$`߾}:tD"׳m6JKKgٲe?~C RibnF(v;F~~>?i<K}40>Ábf3Oҥ`x>E?h\n+CC*+M$"j*!C W+&r627=1#= ݻQdglj"+__R#;_]crrc;N>YV^xo3;;Kee%wh4LLLk1>>~~ӟ^ϯk 6l@>1(?8 }'+c<cTVsinu s^OIq3$14ښŔ?e,$2dȐ)+#x$? ;tK2cX?Yf3iCC׬a'R54iO}S h[H&T**{VKCC2'|H$>1~dž ĉXVnJ {a֭x<n&J%=uuuSO=wwm3XdlmkGRˑ#8].tk9pW`lbBIn ZK]TUVbr_7ްZ[[%ÇLd?s$uٶ+_w~ol hm~AN pf D}+=L{/$I 8I ɷ8>7NP C!uv"oP+O7?sL{SY%w" sjα.%{'!$ܷsᶫ~ Z 2 {;f=I0b_3"'N3ك tvrzx2Τ{''>N _0V<uNBљQtOt3umc}lj%b9BhNV]#HĠsvQw'=ca{9{`4H9tT0(D)x'p*D$O#~ 9wocOGp\xBEJ+-[Aޣ؎ΌOƙL׮o)49dȈ%ctv2B<0i5sǟV(?KT/$26;FH9j{?OD7}G\`6]#$Opf s㴍1wv&ݓ 1^vd9HRH;87uc}GjůEH;? z&{hi` I!^yBm;?ޮk6 !L'I G9p`:IB@$xB>'tOt#J"==<{Y,íS)b#3Ht]c]~:F;t""]c]>D8Ƿbbn"3|עdQE&ݓࣜ?JBF*R/z@$U>g{=K4wo /v-[9rm;So;Kcgs/3~עvi4:Ml3x+ύ85/B$ag.]Ü?sǟGD`ߙLà5R}8{'p\l;m;?,tO6=ŒZn~]x9p ސWZ2㟥eީEBQpO <_=müte,N "!&h(=7L,E̎9ɑsMӹ_ؗN:|e ֮k ݓ^9@4}>Z|.0@Z!f?áZ9{BJh]7E&m9sЩu،6Lsx)A7'N0Lq^ L`p+Asȭ@BJ["Ve$̖Zl&{zڂZkifb3(K2o\r=K ,MD̫sQb->ݝ]]+|TVr۸sGPs % ݖ/XM 7c !|q'.Zw9lހ7*CF]*JbŠ1Pb-&M=e{IDtK I-C-\Z[e Dܳnu7)qی'ASn+ʫ$H,]YތZnNU~eSk:0R0j d2 % TUR~kgxkߎ2>ҼzJ z=nCNC) H$ s<R\Rp7vKä{{A  [we- d0#,_;C\b{#`X"ߖy+ b0>ɒ%#j/6}?bA/o>_l5UyUXwu|Ͽ;p5ᯕыbwl$D"ZN)":b?࿄ HtI;c*w8F.#ݱ 5`0JB\4gy%h_"@Q9rg}^`>"{a[8ajzCsk{;Ͻ"wpE233ʅL@i7zfQqnWkq۝ `-w 2\M4 X,Kk4zzz_E.$I E-}?lx||m(hsF.e-fff@.1/ Ņ=cr=V;:t ? IDAT #jg~tY:V,[ ;~[;>$TNRJۏ(v-)eSqۭp2}e2nFLcKK {ool6#"q9f3'|XtkɓL`'۳?ȱ#8D)ExC)e_l6FEضmNw3CIW^Mo^R)c1͔ػw\g?[Eٴ)3ٳl=(rΝяkNg.%7'ͭ[G +gرc`0'>Qc!ز^_Z{)KI$ReZkCmx MDn܃n9dqvM}F=aF q?߂gM!4tcaZcCcDeȐ2ohD"FGGQ*T*.r9UUU H$ V222JbffAeppH$B@ XVzzz0222fC099L&C i9GFFpqZ-deeVI&"bz^>H__ JZ-333\.EÁfL&Ziool6cGTRTTD$AEbhh(mL&)**" rKK qE.c0p@"`hhh4Jee%|>r9K.MAyy9}}}t:DQA***P(8N555DQ&&&(,,r!IMwq GR~!"uuu 333TVVhXj%#L;RQ@`0ĴɉS-\Ku8th4FMۀ׬_ǎ]rk@KK+wܶ 8ޗ>֎[nF37f}X:L$_ e\mfffؽ{7ݴ=B<VK`O?ހO}Y9"<8In_$M%hÒ>d1A"ē,2~r@*7Xѣ.Ƃ=A$\(6AE +x5OOW%HPQafvǘ a2ٴzkJe6*(Y+rEp:#xn8cc!q@ T#GBI>f|<$IwoS"ƚGhSn6J99fXkl( f}\F 4.˦5wԃBpm1h}2dȐB$I$ xe˖111$IzLOOljFe˘nS^^N,cvvH$B<'PUU($Iϧ$tٲe؈"Jh4@"h4`QTP(0xǃBHkL&,]4}|ۉ\C8HQ֢T*xŤQ BD")SYi sss gL|G rX,N4E`|Yeh4F#ZQ1 qæz6]8]_9`0O|kSJʆo\ld8TεჃ8:?6n`{M/1ف6LU{IMU,J}z%JBYӫ2bkY܂B!@G#l^@UgBI"&Z$Ai+Jo'\tv9|AN KdRil̡ʄժ׾)Q*dg6dI6YY* zdRf?3=xuI]3g Pu_VG4kRif%[IS ڋƨ!5P)pvNS:5WfȐ!CA@EbD"&Fmd8== t:t:](ӓV8xH$oXhmmM[$D"B j,#399Vorsvq\bX MbÆ HddH$d2!p\|ϗBSRR4nVrss1ʹK+d*ujERP(PtttH$vb``Z[[)--tL&1>}ߏEE(f<@z ݼbE8BΦC2DG%I\/]gp\|+kfcdXb9SS,_ȡGPլ\˅R"O384Dε׬p PQ^Ncx}>nش!֬^0Kp'[().bFiFBbߵ$$ 8HT*/*IP!!)ID1V6[KCW!}7$tzjRF DBD 'ZFQRY>U 7^BC* 'LR*C$ѨZlV &j(2"P(ɤFQ YY B$pEC,& P*eIT*9FSf$ T6N9917Kᚔ"&HD( $Q"NPYHȕr9C o.5 XKd2r92d2{6z2P(A@Ѥ{ 0+@z0ӅrAH#EZ(~_!5omOiH$$}!۫T*5o/B6<;;nQH$s( d2X LZ^f^iL$1bQPP^Ix^fszX, h4ǞsX,5ɱ/?/F$;X aKL 2dθTP检/Lh_%]歐^wFVkڂn0E}1 0eg/zL&cLڅ(q˳h>L:ShQ[r*WWAkX !VrP]LK |! ΄B`˴'\Ņ$@4Ak  hH"FP!B{:G;i(ix5w 6Icth"~}1e}z!/rtJ/W7;(O~߆ca4Y4Ɠqd29(7@p*V]sdNX~{-BbBy2HN|}?'!!$e4$:inK?17 DQ&ݓ-m׉(xC^BdGڲ}] DύcaFgFqxAuڹg=?fӂM!m3AO*Hbv~sht6 <tv2|/y4v=S쿸BTp#bEr Zݓ= ys\*dj'Φit6f(3xO;G#4ݎ; )z'?A=i wkצQa  ;˚ռvuN $3gt wPa෇$MSo/drR4gG;KT?SI΍v>V6{/~t6CQ;Nd}:ڇڙ {a?Jޟ0?3_V'>y=B2$j^Vԯ~Sv.3a֚GNbqS}8 wxNXv9r >=+@RDzW-R)<dZM0D"a$A&!x<:O,CRqyz{{'ū/g,_T*E8.:FRy.l6[)&pff***pd2J%ΝCVRfm1?CF BTҵ̓'ORUUuM)k>dlXT,E&4DsT䍈ds]F; pz4IކIk 0|~I#s-;ڲU/O(Vgz,Q*"Uj ƂzMeMkZAe(r,ZEUF|8 \ e(35хNcU8NvbEr㬨_ 6O/n"& wpMkYݰmbPD$3{h V].~? ,[,q3zKgN:p)<LLL:.A6epp]@ @$azzY|>z{{KvI?~t]P]=[Mo3kaKmT:b1U10Ӡ6im)>ju*esڨVO}%5Kؼ`3/Ĩw|!B`\?gM^> sŬ2\Q(M&l4x#s{slYx'. {&|8[5* Fd +[ip#w1`&/cUJLeȥr.T`ك g#ul[1H%Ruf(3җe˚Y۴&Wc]pSa`mZvI&{9 JK%F2gujFDb,: :ĢyelVGr6 bM;/`Щtle(4TYIҗn`z_Ϩw:{ Qɕ8 9Xݰ\wסV \ K%.s'Nt XJ2d7N(cV.OCdd!Le* {νi=OdUJ\&j:{]d7ZʎS;F:\*etae2]t/Z+,\NsXQJetjڦHj=/G>E7Q( b1X2֯__orruՄT ijjB2::J<bP[[X,F*ڊf#L` ԰zuẼI:Fa6YLyy9ϟg׮]vLmm-l%%Y2H$BUUTFӑJPTTWWժ.DX*.-q\%RGP`rrY;,+Wuu5.\ n:TVVvxgfBcA\Q_ۯk,z #XS9x 6m*N$vO/$F~?>,<V}'/`TLͼebDRzt:B z9=Lӥl ^SxÇyGXlٕ 4\'Nj\O&3dZN75jkP1$Ç' K5-R몙+8*~â6q<7;e2?ży 32e͚7Z6Z)2  "JΜQ[#H >36粌ރw (oCnD_С?Yb"!*W܍\U4** n$r` |hkkc޼yhZ"?N Hp\,^3, ={c1.j,_J\.' !/"6ltdFH$E^zFC<'͢RH$r9ZmInO.Q7ܞRD*FťL&d*)(JxfCT2<gƍ۷|>ŋ  B㦧q /?~itL&db1zY}ػw/J:|TUUaZikkpyf%3,12<hdtt"زӧ{m+fTRj)\.. b`0HUUA FL&2 SSS8***x}fAeqK?oqdj9a87έ-eNRWWuvYqg^&4ދBoAv`tK?# 3m.1-Cm1}9K}R"pc*r\LN'{[G",X`… V7qQjkYcCrd$$I~33y\K_dY#shIוD& 埬bBת8g X*r9"RYԤdhRJBT J%hjJ,]]nO.QH$cdAr)p~VJ:O16CbI)^yeWVgOQ]9?z0f>M6űpq6}CŤBןBiRV/ٺBLq&PބHreD";PZ/d\̩S^r1OT*Ϗ`*f` NL&??Ls^Zٳ~N YcTY8D!EiPqwxgp-+g-p+89Ҡ%2/ )f- pyKo\,'WV"h_Px[vQo]~zћoF}8\k x(p|A֯w⋣,YbA7CC-pt:O:G,36e"3F'<\iC,7SX, .^ rB-v[ѲJ?Pv۞ٲ6-ɥ7IG$.<;!24&՛jq-+K(#Mr;FP($]`#SSq>󙢁S,Zd?ѣ3w_/0ʪU6*V1rsa[ȑT dȋDBe:F:Gerraѹꐫu$B3zJh fCF.>wqwLr'98L%ùE-uLhkܹVt2^{mfWصkueej¤R9T*);w/Գ}(F97:)/01jUرc`0Me_/IOOu$Bi|qV%{nݻdŌ2 Zݻ31'/РgϞ ~84a0 nsaxA֭⋣X,JyF-wSNо.<[4ڢ/{IxTB2u5pTboA$ $0TCd2R90gbn^.{FlJԋSdd2 $dQ:H0?իɹOE"r9U* P|2Yl/%"$ɫ~>>}5kꫯGrxٳg`~ӞZ0aT* R1V/٬DbkX DRSS\JMrBi ]]A.XēO/Կ{*HHJIė/7тϗbr2˥RCww\\R_/R_˰UTUiĨTKW' tw*jWJ$"bTVjŌŨ֡VKN W**4iٓ>3 [̡6D`֭,+<=ĉQN=f+RFNt:B>#_ 9MVz_fރ-%ͳEf@('@,UQg݈5r9Rm0 ϯM>@5oRO/I6SD_Sd&'Q/[~F~կ~uN|A~1<<뮻x/~<7[FSjZ ~Ha{kku DشɉN'bQl@QYzCɤ@)>i2,eeR1f&=q5\_W32D]'Xq'2@&Fc-Ger@_VNy bbee &5YZY'pٔy֮c6+Xr,k{kTF;Tk2fJL*'"c(oDe#K/Bs(S&ch안%2T+hmd*WnTBh CE*oֹj N52#,Ei˖2.\b)YNEEU45fC &NeK9lc$AOySͦFGWWt:OY#P*c2F9˖Y8>T*Q_zF~55Yq~R0##Q.PQT14Zͦbph?ƍvfjL&``0`QU84٘<=AJJ: >/e+PT}PCc S֩Pi@Ж K/o%f DHvǎ_1" 011xǧV3ŋ۴Z&nN"u+InڄW&c||XL08tttR8s n,333D"h4<䓨jp|yYW8&\g}45YEgt4JeF! |x?@bg"HP{YX}X}SOa}Qc sg%52fr$3?V,(=s爟=n:t7SO裏?0H$bΝs=_\.ǟٟNϗ%J%*=ػWwƵ@:|T d(I.CcvMC"Ro! Bϵ *w:;;}ˬT!E%$y]yhZT1s3rh]A@@@@`0Ga-}?ZGyR=oK25 3uzB' [xXkfWpSTaϷ^DeQt>S }AmFN.C"/ju4lmheނҨbgKصƎ˫K]RPr\==xgp.)CӿmM$bJ)lTL`\GZ(sKG] RC_^t2€҈&N}#. v~8Z hfvz??}@4 tqҙ \P8@,Eq:$S) xӉŠr:˯ğGF"N#Hp:x<^</fBA"$L$Q8NfffJ_*" jo2Wޓw# 0-[ "p08v*(["L>KW,HDqZ&JF.Zb7RQ(sy Jr96l6K006 r41Ĝ힞V c}O{{l4=ǩS* I">n\5e1Lx)7v &RDd&''1(rij r HD*bxd\^C,.ڜjԔL6^CQˑ^Sk Ff<33y$ XD2|ď1OL,M6{qBR\x<ʬ"<dulh exzO9/="DN*\트w*d8%LYu=w5܀gh(H@ L&O{7ŭp!@"cZ;7mL4dfظq9\.|,bKf+m+ uR@u"ƫfP( !BբRPQ^^fXX,?2G츈D*:ׁɆH"Pmo)_UI"=kfxcцzqrgO))Zcwd?#-O2@,/ֱ+H~Erl6%C~{9CC^~yo/C&{oOQ]}EL.3<O7cf;(pz=wr;FJfgkٹs\@ggrK9d;R_rJ@@@f׮],Y q`yυ+*zt:D+Lhl $ cADb1GрҹHE{`]]ŏ \/s!t*poCA"S $@o/X洧2Pw&SԋDL 9uu:>^{m/R)LC$A,JÜ=맾^ɤ@ zŹ5$Ԭ\iCjeh4ZVd|_/W7R}b>w}}v7nS!hHT*qnJoZ2)5,0N@>_(DŚ**VWic.$?\c@TF_րgD"?G﫿徯Ѱ jKKko۶ ۽^mO=ϖ-e80V,1w򤇇E$v1Vs(7Bͦ,=f/\چHT]U/ @U5k^Ul45عs0=T3 D$<)  -Q,s-1“m~ߍg},^|eL&/s]H$& pvͦ7b*~I( 5L$* IDAT2[2{znv^( DruS= |0di"ť#|dSYfλj N$ IdH}P(K UR-26V,xFsݳ$k#N:ܓ"HP9?c{xyb p?:Jh4D".=N:*Uٽ{7u,&H~tuu8z(x|;=z~N:E?X \p'OJ>3 q1 |.O&fӍmTLbu Qmtơe}{LGaP! K ebN  E1.'? ;vGbX~G*bll O?h$SO1>>Qx1qJ)'ΑIdNZYXxH$" R[[dbݺuٳ\.) l۶zq8|oNӌP}}/Zc,l2D!>, B l6rtO$\x˗3::H$"H֭[D"'t=3yj]ҹwZx=W>h \auGbb^s32/2>>ɧ>Cj;@cC/O?He2TjXilhE:F|߿kעj) Ľ1C~$r n( ۚAC`5Ӱc RJg4ݻ}S?;N!i|=^Tf+t>}B=܅i%#X˩Z}^$r$-jF% D5SO LضÇ rݕLNƉŲH$"b(rud2y|u<0d ͜>ر ,]jaxhx1T`ÇUlV]cLO'җy |k XSF)/ K kybFy&yf5"n:"8$<a[3'PCM.cW0j^/l!6e}K\A]AmUcnr'Gt/1~lL֌Ҥn7)^'g<i,f3#WvGr\'H$WТ(cc475JX,f$C87' yr9r lE9u7r@c~aisc7֣4hk(8LYaŗWӷg`k^I٢ʹ>u,/ś vl8ZS?ZU&V۶uwxZX?i~7ɺuVJC^`S(3 ~ Yi 0x1vrF=gU|5D"#|R #v <`M<>|exޗX LuWU| #w# K \RF2$8`"jıȉނڪa0Ꮳs$3hZ6ֲҸ8CώZ >nXWB!hL~\9KV J%=x?j ְb2 z\[n-a2cmN>\AO}}55լX= *;3fӦM-,*z%ޮVTAm0Z?ӝ%KUƦ%O3~d$ 84 <[9{1,` ,K2ʗ܊M4lml^6"\ҥ G(/00P,R 8|x f?bQHQ Z-uu:rb>ͦ7޽dy$N^N{0>*8vlfN<]Өjy5/o[~p.v11ۃƌT)PiDcE xWhlZV >/^/#mC\&"Uyy{Wfa&!,[hlYk;8&7=O/ȲO$Ŷ,˲XF Blbffߗ}FH RΙszz뭷z>z+_=mo^`7a%K=!IرcGE8&022B,C{5ڽΊKN~&Փ_TUe(zGq. a:Űzmd5Yg,u"" cj?)%6֏QW~r2L2 !ccIiB+p>AʾR n8:i7vg` oդRbX,### iE$i,8Bir Sx<9ƈ$rsj+WqLLLF%JJ~n FosyfFr"Al$ĉQЛzb1c1tFɉ KANʄ{xjYdx^m(uuu̙3L(⩧JA`tte˖qf̘隣̢Eĉ\MMMDQNI$>}oP(Dcc#uuu̘qY>\- O"ET$~}cJF1m &d ь ~@Ursb1 E(~4P2iU%%'jXhg&oJ~ypyv;G矧 3 2f"Ly`^ϬYx'0,\d2 ̜9E}fq50rPUdFͷ*W>\45 ~회K}__q]̙37OI S\N|5@Q?{/ϒ%'A|{O.Xo}[TWWSPP@uu5$rP.[,, ,p87ҥK1̟?ߏf4n7-ŋ)))ڹg`7+]HL:&1'Q2 `bVo*"JdDPLG0>NӇɤƄȩM;RqTUEN%P9 NmQթ$rBf?e QѨ-!J(*TtZ!NkўfhoHLLLH&3d2*ttZ{ %I!LHJ,*L$"!IHRb16Nv"'dLFHqu9$&%%q2RLZ&N!'%4R$X9)s3(%$9+ %Kr-w}Of-[ŢXA{imm `Z_"L`bbۍ_2wa2b͚5g}{Uz{ulll䩧B|>H/0pQ'r?$&'N**N~O 7f}!2 z>5DBN䱃gi߳Qᮜ٭OddFZ1v<"m܆"9b7}zȤ38]s͸*=oY(+'ΰk Fp_YVTV:((46r;ƍZUA~&;hk ز;$-daqIǯN:OrpמNomEJ( i7cyyFοzEQo(`=F"8u,Y?Mo_ߔϟG8 999bz=(ih:z|>:j,X(2w\l6d{ٳ)..br;w.fżyd$gx鯘D, yyyX,q % 1LX}6JWVhh@9Br V`Ld" _O?@Re)uW?s 3xAV֧\ @td$@았SzEgxXsݬXevwG?CQ\̚{Khhp3oUU9yr9swri1 F̈́i4~9;b\7$1/c30DL&G̝aB|I0W ,Yg<wܡ׸gx(_zmmFB=Az.0448 (D N&)c/r[$2AJHs|wsw& ͼc]g; ?DRBzWZ,Y>PVVa#DUUKMMͤ(e/%1GK&ЛufJsxj|T! @2wgPu~1Nu0X GΏM̫[MO0AN^6orCׁMJ1rɯ_Ad W=+۷OfM1a** 'x2ΟQ\l#'#,^ 5T$]^,.&NR3t:B+RTdcp0A2Ɯ9.^~Gk)*Nw=Dٱ-[z)+Ӳ~+*lCO;;f`qYxA㖯.DNɄ 6S0A$ vL3&Oz^+< ?ۄV}{pWzm-.,@=2d"+fr5!l;QnJ}u-tۈo|[yF(*@O~X'Eo;wre&ӍYNF, #e}гdsYr@;::ohlj5*o}o|n(LlM7;W]Yd&TU)jt^x^{mR̙3Rǎ… S<7ndÆ ]hmm}voo{[c6lp)6m~36ivŗ7qL4H$Juao [ӫ\yH0֦$?nh(АzIfp:Q-9=|]\y;C[R&K,FFFؾ};'HZa1rssY~=]]]M(}M;1l"Oa~U,")qIhwϳlz>=m}TV:8w.D(;ʞ=C)(PRR7 nl%P]KS/܅( X,:zzIt:^褹yJ## ֯碌;JM;ضy><ʑ#l"IS[ر J)='ZEsF;M}/)e0RBt,B2P 7M}IDoNfEhvC:޸ L0xlDœ{`t4;o(KJ? licȒ%?כ /  X,D*Egg'k֬L&8r* e姟~qDQ$??֭[}fvɬYذa˗/ꫯR^^NSStľ}^{n ݎfbJJJ6ל\XX@ [nb13E PVVJ 0Β%hmm%s>n<}̜YKZaV]Gر̟7QEpLQb "H+]̪cL&urN"Lr9H͝DQZ[[ώ;-ld0j/_9Kbe.lO _K؂wEV9=L^C}UTHwEXJn|9PLR{ h5#^+VO{=ub#weɒNGqq1֭"Dhnnmll… ZUUU]+VLo֬Y| _짶Dqq1_W[ٲe ˗/j 555:ի Op 1$2ccITU7X2Z_;wP[mn7" adA/C֬)h//`ttDP^d >8@amdp0%g h5oge%P`˖-lٲ__͟Cߡn/80ڌ-.[ Dx{`R0Y"I<'O.'qxQ/b=-'@ԋ-|WUU IDAT%KO ,{vOmK0<,YկbZYr%˖-ň38p=̜9G}V|>֭clluQ]]?L]]dP(ă>H~~> TVVR^^]wߎN㮻M:&|m$GiH}#I30B!{p\xUL bc3(,ԞL/\S^ç>uy~M#phH3z4MĤ,%PjCjӤiwhvT8Y'K19L̼cgGz-.s(^RBsc@blf>P@c/y ZqΒ%'Ax<ӶFy)멯ƌS~++??;}ǃftpꦽTE'#EV(z\_DO+6O!F[.fK--Xxn`ߌ}dHR$NԒ%KOE&Kd`BCA^ocr"FxRj:{7llC/I͉,oZxds= "+fr5!e] &RJ 0 :MW/%Mu:," qjzsy1>I_@bϙ5v *-ez$%I~\﷎gU}nIu*65dznWd`dsSor15oU&[蠹#!Q3yF:ҬK*mQSWd@m1zUrDd^Cb-:j M$lfUrMܙ3ܙv*FJ Ʃ(f':v] \6^#CAq_Q ] 8|>NS"3?6ƺt:ׯ!Jf6oL0GUUnŋ3gs}]1%ˍ̙3'n5pJe/p~P3Aioog޽455qsN,XWU &HRF,Xի_~۷344Dss3k׮eyNsy`(;/A3!R߽e[}]")JP108}(MֿeNKNp4+MaZdCY=KdESUUq83f`ŊݻjkkYf tttLVX,;wߏ(HD[[^XXH$a̟?@ $I̛7zN'yyyx<4̙309iJCC---DQV\ @qq1@`Еah67K,Y\?5~kQUp8nR(A$v0;66Fmm-CCC3|KSSz+:3gׇ9v^j(--% ihh2z{{'ۗfB(beI8x $qwNnEEx5|Ttc#Q5e{&-(.hQ8=S(Ltc:P^>})]0yNMMc,XڮD¤ci\n8ѩy+]n{.ŗ]+JoByޞaƌkޞRDǵ/}c}x.i֮5` ӣ/`4k|YHi!P8{6՗ޮKB ^~6w,rdקPR2L$Tvրf8:W3uw&nl~͜c>#>*8%Q/te9K|%^$T()QUGNa2$u/r}ٳ]:4BID EQz <-aho}{ ٻW V]^`ϞaTQG}S&쌰dN`۶>.Lfgϳl={qK]|k5ؘ9SuPdye?BNʖC>ٷ"يg<Eɉ^dɷs.EFUE~4cSEfԾ|e3eS׌ݻeVvn5K޽2J" GdT}lL<QяRhl06i~Ud2LlaȈʫJ<]੧,_c4ݚp,X)Ü9:";eVhj08p}F֯OoS̞Z䥗$yĀ))/ֱaC;4qcMS.ɤ ذ!={D˵v,C^?s^>^ PR"iwؼY_7ys{5, +̝Ǔ|K&<<~\L*^x!hӘFu̟?Op y.gdX>:99ytÐ۷KY3at<O?b=6'd2rpk͚W'#q8D2if2KWIWeI뿾d L֦=Q=Uxlcn>׼< Lrr %H&3~En@UUfr? 1zu!q\.#@r9|x/jxNV.fSP`E`Yԇ  ^sM(en7{Q"veog\DgI-#έ@ϑX^g~o ,\cNb3bANfj7rI֮kCۮ*--Mk)/y O?_4R^/2Nm~PSVA\뵾aTJ©*47{hTE|ǵ\_Ab^3 DzOLJsfQ, =g?khx\E.-Zg(tKؽ[:wq8,^c.msska.;#9狜<_:y*BILFiס4oPHBU¦M""l$j絥g_d|\;ς+*&OS]kՎ(Z7fvS2xvt*/O@9]tfmu:ضM{ x<"N/Ԕ>K6n{n!p'x0.o-ĦM_ [n.^|H4m%`ل,ˌ |(O"$,ߏ @:j`蹋F(B,,3d`(% S^^h$R^VF*b1GRzt=pK(h0p8& V(nqU){6aqY\]Džgp ERg_>Ml$@crRbg`GocL5~mb:n_t,IS|]]8Fbb9(B> Ou 3jxiXC*1AAW^bѓN+}w!L~dRfh(A^GX@ IkkE|wyrc,׌N's-=1g۷1oQGai|^apX",]'/ςfȑI/u`y3஘h0Ӳٜ5tLtVSS3$I%ז4ϓ@[B*y$Ia2m$B!7 I*.hKGhFtIH(.qmzI͘=[Dzezjjt,XBHҥzDRfֱSF$QFZ[^Ȗ- :E**t8_fbBܞ;-}G 'G[ Kc4Bqfȹ\+/LS|2M kxdL!矗ٺU R~ٵK^O^,kͥ&dRԩ -PYN3PT^A$sK2wdH<%)Ӓ@$d eeSRRLOo/.aJ ::<M^~D^^z{ׇh`hp?Vp8, v;r&37P(D*h0vO7\$2|rg.uX{ΒWfkOmm.978gjF%oA_+LʒFsDc撫y/FxlkMYp3,2$IbEt:$I ?p$`0q{HRx=BJ&3cyXf$Id4bb(,( a,Nˉ^4M#EUq8C! "sz&EQSFU)_B(2ktҘ/ʽqrt&()|lJ<]xO 7O]mQoY|,OY,YnȢ(r:|X,Ara61}Z"vfqp\S2M. 0 L&V>\l'[,t:6mr`d4z<x IDAT+f|Ԙ ^LFOIFTAe::VD`>'3\YLOWҖA'ԯsWH&:Q2]Ê0~a UUIN$8,a=8}z iO 0Fl4F*DЋ(ᓃ"Lpg9)Ϫgɒ%K,J  ##*Otu)|&z=<A3,]cVh,?7c #`TO̙Z<ؕN=i-پrG&_OtӵR'rJ&>YU`%˘7'(YVQJ!B!Z{UX\V:^oV:HhQ PѠC4 @מN3|Β%K,Y|0|f#DΝ4 M̂j;tH̛/Dyԕhw^^@ͨ$qt&=9y9zhL:C{w2UIcPLJpA19yvU*c@bpfT5><^&=H)b0eɒ%K,WN`|="[%@Q&o]IT0:Úq840g;%QgI񕯘P0E@G?J <)sB4Ki|3Z[1DŽp1[/^!H^ s-TSrk%23G,&잠zR4/(v[ꚦǗ%K,Yd`Ȗ{nt/}X|k*- reENɌ Q=%K,d|,,7/AU4՛ӌDBb9Z)>l4DBda[U2MjBhykzQqQ rEwJ').afdd= NM۞=}}zl}_̤e6H/;w\~oow\(5wj}u<}{i|LZb}yw/gY*~ikk|Uinu U8vsG v in zұs8luF}g?kiSVa*/8z-LB!Sؾ]KN,_2wحx χ_2H($rr%qNs^y%6͕ٳGdpd& ]t?t~1y޽:g+lfl!>|Kb-6o;hj2x[np}v$%z}4~6Mab?!Lj̊*=FUab R\:'D?Ĺt Ν>'?iN8}}&Ox<yyckKaM$֯!NJ ۿ1M.cTVi;G:3$ \di`--&w-j-Wj&#Jnv7kYl$&cŦ9?7.Q9iT(򕋄C,zh)6%8xZ4ai33ZAGba0g=6rTVXDB~"vO1`0D*6qWIF_IuӰ̡xCAf|ro[8)nw XDq@jBfd#2`QIl1 w55gM ;Z+VA z.v:U:NaÆ\,Ic:?DU^z<WȒ%i,]@K.NghH{<6L⥗pU*+VxT0;w4rl?1<4v6l%+mMVʢrUX$;x '~>r"l ֐f&<ao*y Oϡsۿ:5kTJKʕ6C8y}}c ^cB$#C&7W7u{,uu=6?Lӟ&okg+hq{Ўf#Q[k[as 42e>EOI~judĢ͢X8-&,mcsN45KSâ A~Ύ &|n7L p6zȁ&[R\iSM߯sIypV'mX!~6izF8Gi:_35B4}~l.v>TJBAMȘu__P S;MD^|T:NSr4&]ϔ1/Fz(QD eY Ӄ4M{z{1kERQ2 WR Uݤ̠R5<\QMǩ6bbIqi:POky^^"!/Yc;Xm h.ގzg~vڝ$!}RJ(u:%N'uFtq^)-MbDb2i!TT `wlıIH""RU5ĤI "??I;pq ւ(DZX[U5ș3"sPgiPUw]GlNA$ )4JWQ n6}0\XDY3uÇŃ4 Oo~g18h%c^mkԩ2kx :oq[o̙#ɧ)x lddMmeAEΤIbܡ!m۴dpV_~YOԠm@A%\. UMM&S((>mpΊ*##۷kᇝ,[&4zxCQnsJJdokpTL6m/2۶ixB׷Hf3.^4 7GH;Dd @a65ezKghBU)-K4JM,4׬/II211bd[5t]~NN'4~UUb|y zM** LDq+L֥_8{茌X$%Jցs%e-Sq^S:s*K̜bJbnd%=YR%7WK_v xe~ã5bޣzEڲ6nܩ~m y+ Xb<z*}KH.NL5H&.]~iS 4;b'iQ6Rgm&ǚ.ۍ|t]>Lt^T Y(rjPp8>k"q:M_~aQDᐈh~V7pkq:a& 磨 6 OFRSSN8Fdl6 &???sc_Z@#!/qMʲ,,zGSאU=&.jwM`Y$dUFLlv߾a(.ɓ̟r]$v*+c&%A8&XvBuޖecf'|G#GRvrm:Epp1uSL|>i̛7탲( CD$)1!] /ōֹ=ds{4L]#^ڎ0{-KF &FL{A[Zhmkt"2χ-qqq(ai@EU0vC:?LdS+ #8$~>ᴘ !ua[É_{Z`#N,B `ZYa0Ao95V>.n:`<~' b~N(d3<,cYv8Lb|]]|>]7  A]7 # , 7LWWӴ  (*uC:^o7+Ⰽnon^2uA3B10`^NZƈPH54q.A2ӗ54aKА0DS s4M<Ȉѣ:!~dmYsz-z{̕6z޽:>~ޚ&RiiR> ,:;Mz{MAtGqE`0h%:2b[pxq^"92^aX㮧H(BhAT!4aBiGCȽ=B4B >?FX_7eFQD~p6GeP^u\"2xPUSNp(H:ϴ:T+'63}ut@$r̈́!fqXEbA;~;/L׹+Ș3~L>^KͨN7yө8uvrq9H*JƝYՉ' W1y <|/|Go͕ypD YP0<,"Eyy$9YgLFZZL_Ctw(ۆ#8))Sll5G($^himݼys?1(Ji:94˲p\ŽQ#4_e"4_Xu3RrO sN Aej8f;~1y&;NbtR&ϧ;$$i%0麛=@ 3& ƐUٙ$$q̜u׉m,YF~~,qtt<9nfƌDJK,g=; IX*3Ҵ8BN?.0.|yRܹ꣭sO>6á;$/ 9y'])̜)H,H"cv愔;%z[1 Iq냤O[pW3ū>C3uz:Es^̃:hm>;)+Zec\˅cPP s6EtgÆѴ1Y@eTS ߏAl,dET,;2bے~ׁW̚v'>F6>Y;ɢc[bWHyMfR;Plՙȗ9DC;`ŋ b`@Dvk !ABmg6^ ֬~mFb̺uc7uC -Y(d+lY# ֮edH|+, 3f.eTz{ǢZ@ed=ɑ=[B_OD]fc@@ێct;$IUaz*zLl]q9\%$0wNe IDATΙԸ=;gPv i33H DLC3Hۣ"ѽ/ Yq_ )HP\Cuc[^gOQBх PƃaPtdln;}z'+c2.n}wZIxh>E8 X`k54\Fފ ٵKPԬ[CC0asOoȑntݢrYyhGAA,CCaOO@Q$&OgxE"7׃eI|nfJa'$64ں6wJQxQ<9W_}gQ}Cz[IBVleFjrDP s8\ƢNH4l hӫxgŋ/j'L RSenAsl˗\ytߢQ2\słJd e$ nFuАEA¡C:&yyb2&/gF[yKy,%<:3::L~P wvnP),#.01C87jL+)Qx0k(,t48 .;G"!ADf.m"FOyu/*v*,ij2Idjk N4Bh| qsѴpN0hQ_o4&6j/Ӵ"!V, Jl16lܿ]@ziᅫI 0u}NL$63K|3Ϝ )r>rmCtiGTn:OLp\@f~J'EQ`Bpc8v_z&н^&>ٴGJ<ii10D6on$;;E>%izhh0ظ޺L'w  *ezW|Pv͛ɳ%Kq* DBeiK,Xf{7"IĕtuZDkb G0(xhj2-IM'F〫99!`Gl*] 7S k۷kox?W~SE ݕm'Zi;! }4\,N4./o!+Xli1imX{-r>oCxi3gHk۷ki1#4/ؼYc=GPx;L:۸1w뿦㌢'x1x5Ҏ͈{eBnO֥Ǥ6NE׊ ]Q,KmT^™(?.Iɜi:NșgN<%WR ZLB~"~qꭕL'&y,%S}#~R,A$zkOh~/\BEA#iQy,xc[8sٹXjj Ny.rsۖ-M̝-:%%<˖-ELJC0==AEfN%??654BqqwkW+s$qH7oՃۭr`!MKĉ^$Щ'*e6- t {a8޳}/z|'Esx e(Թ9F[q'uPr6/ sټ9|AW  ee:?yGbTQ^n0mۿ_#?_غUc"ӄ^HK,$YEAaLχ3G4A/g/]h/\g=ʠX5MqiqK/iZ͛L,sѣ:B$x `*>N28|X'1QH}ƍa%-6m SP $ zzLN2)\\,:.,x"DvLAaXuשlۦQV%sM6YLS8׽w`E]]-d.T4x Jiӄܫj$% u!љ:U׿1mBrSO8w@Qf,K^mI#drkiL,K)hvTUϜ1()5/!9kk ~0&28rI8eF{Œ%*=f`@Ԥn!lݸ1̴i 'N>mxٹS(ls *]Q|'wG񧣿C'Rs@ :)XU3E 5 9XL,x`kI<\9d!YV2󎇑d@5B՟%1ombu0 8k:XecV2R6leɒ4֭EO?I*}t::bGk 'sNASO&*U ^~Y8ǎg"&˰r֭±(/\i_v#sv%;Rf _4כl߮1m̌ CC<Έȼ!FF1W_XRto٢qOfy\Bk"3Sm44x% EQ;I̸waSŌ{g~=jz O@}?h~ =G IuNL\zkO:y>]GH. HW#e!)*iSc6miI Ê8pӦ%p],_AooD;b~Ngg˲(+b c}15o†{{ԉ4Ip&\K*|RS%-Ry٦SɓbX UU:)Iʌ;k0,>Y; &۶)\`bN :G.Crʕ*B(.Vؿ_ j8 K t X%iRɌdRHRqyp{p%0B#^}tDPkl^pv%.^42E']]6JH)-U8|@EıۤYuvX;pA D8Q9 \'Wq++3ubNa‘8v`xآG((ΝNDV:=="Jw̙*"98((cpA$?__\۷Oj&:uI3N3 $:f^PJÇ316CcI~.ze)Rrr][DSaa0e28}1JH L4sDf#&[jȴ44ݫ$ÇETEdv$nO\#〫't޳jb;g6g^uo57FeάYL\Rx瑜~#DZ xKlG=v Wb:8]cQԏmz4'=8Y?ŋl|QD$ 8 WKiHH*Ijj ¢B hniaCdg1m:|ŋJ(vas457sYjY0ΤI,7]oI0búO=5 ST8/ O>_~oՄD|ہ+q|J>իmɣv5 ?/Fi/(~WQR2;82bg#Hz1vX4Acqu4}tۼ$/! 2_:DEQD{=Z{C(+;̴%ܵ +Ě̞Uqi~̝3TTVr]wD"æ*;B{{ y044DLīM~K//DO<%:_ crO9yr"an S_oPVGhZ[E kwa_zSEڤ)O}8RwƋ/#Vu0/0")Ul$'w,)9uvPwYp#l,0<ׁχە5r꩷#0Ƴ4j*7_ Rj".=Tl:Gx$̉_s} B'EEQDׁtXyr֯[K__?)A Ѝqy唔dΞ;MMNWW7Eή.&r ˹8'tb&q?b8sʕ*"~ &ԩ27ʒ0p6oZCCUU㝴+U.\sZ[EZ!ٳΞ5عSݥh<(=ř3?Q(:o)((rr.j~:Ù3^}5̾}nڸ*^>ϡU{0FPH9'!g;Eʴtr⹸ s#:ʵ;I.Iq>+a{%iTziw$\'Lt)7ju"(";\\#nyGCUU֭ 觨KVݰg~bbbXl)=8fΘΚի8s,ZaG  ?.1ؿ_DzDqgkb%"fR]-8$m`zpP8S($&J<(/KBUEnZ_o?]vf()Qؿ_Tpy"5Uf|XApH(hZtɤLǢ\ﷰw=BwOeg?km44(v.Z6!+2??} RK-nY L]' wQ31u0uI{a˴&uF:WAQDEQ#*.@ٺ5̔) ˖w Wa}uS=LZONK3 |O_&s^6EbS]Ctr "M Q|~{5z{)]ݱ WK0(@kÍ7{w@x$]>rrcc_S9Q16m?5-d)BvD=!]9%.'7"(ȟ$YB~^`K;qIԃ::ix饆k?CC >jc_97j8q _<0 :ظqLgB<φ0M%>|Xҥ&?a4G߽[V}  Ng]{{;c!DT3ɫ'~.رCu%6>O~(x0?i6DGuWWg9sF/A}4Hy/lߣ~/ w!_pnj"6D#!Lä^i:P+x )^;J\v<1y} 9'vs2Tmqz=ǩa3n{7%c՟,{~j|oh IFtbR8❼z [4kcݺliǚ5Yᢿ?̶mdgǰti:| lHKLNntS`Y0kV";w12lY:}}!*+ʊaL6n0,x瞫7X"&55C|ۥrİeK*q8t=';"$1қ{ɘ 8"+23?51u8E*ǎ锗LPSc B";|>22d>9; Me&$AStU>dvΙ3B aU޻W#+KD淿 GdߦN#Z Ǟ|2Lŭ&rjrAlď~뵨3p89{oufr ş~:DWźu* aX,] zgM-S8@ӄ qصK㡇lbx`0$:n˖0B?5%nɓ:橼jÀ;'glۦ/sM66n  ;W88}ZdT%C ve^}5LE 7ءQQaPR08heFJC9/B| b>iӄyU9ٿ_h '%<[oA, ~+VtvBqdd~nEhVb7KA0g MnpJ7nԘ=[5J9#IFm6Q|c͘, IDATKh#ageOvɘElv - 0Wr 5+͌3s6E-'q03s9oAy 6 R$ggRyҥ˂~ Эu]48sFTel-[4 ?׾P=L^gkw`lg {ﵡ(f+m|ݺUpW p02bM]{IJ@{$>11qv55&_a__#%E8)btR߯?{uN> QI7F-ْ"[vkO8_;͏ďHqdV,KXD(b` M Td&E}^gsY{r/98vLhtB^yw9xPg5'طO6m -/zYb1nRΖ,"urr]b>&t`]"WAk`K/i$V:#;hn6hn6FEOQW(foUu"y *OwRŮ?pФsyst31 _yͯб2Ȟ1 WMϲ,^yŋPUCqsٷ/@0b.?agOU kX8 vǣ cc)Y,Q'̙l/yT@Ue\.Mؿ-[zxɳHs֯k4P/!+*Ӄɞ>c}F}})W}w5+I}\1;uHGS,D Ltp訉aa _~?i;?agIrʞ=:{#bHD8 kצFUO<)j˿$QPU C8ɤѣK(df([U dgoIuZ_L.ڻ\|>_J}*I:/WrImM"mKqMהi& +,x$\{ MGHedC%عSrbHI|> GBS)oXDMvl%ȪU* .mcbmlؠ!rNXCs( R( y ag ^|152 U%혗(HlI\sap~?p1sg6ӧ S(~ō7شI?Np󟇚q]p#WWr_(3C(^X*$,$;Fɒin?-tU ˆ$Kxx/ݻ: 4 s{*н=GX1 'SX+Ǣ/@mμ[Yy/[l8>^6>=F}XY MD?N3[D 3W^шǭ< 0'ՠx{q Mb11 RL?,"?w1۽L8s`xsu 7twÅG=gG#zWhd+D>~Ma SpP?$Ie ÁeYaTEAudY4MQ﷫׃&B]]]8NcccdddJD"$Iq8vD"f##IhX,,D",BU }N,#ch9ۊͦbw8G"$Iv{:@UUToGbGb`sKro%J2p 7lmfNnh< ?[XAx!:ԃn44,Bp z\g/wK0_$Ӧyw9y2H~?A*e02%&lv^zB!\.|']]AQ$TUft4I4#I I$8pBg΄8yrR]]:;# gφPUQ˜EEE.ܜt7Rv~kS 42yO~@VdE\$#s&\_ ly9uR8vȏ1 LE%%bwtuiƼy ~+ζlYBe6@PbZ S"+Kò,::L2iGJtv㏧Ζύ:;Mz{p:'erAVD8lqA~DwI{` wvlgNCkilj2OPW+cY֥p$~--&s(-XdgK>mk/qobܽ l6Q*!' **)aݶ͠V&p:a^7XJeV1|6.,9Q^.sIe.?8b8arA_E8,rKۤ@bply$ \pASi~锨RDĵ en]y9v@RAE dZ6s[ӯd;X۽Qg88i9nNc/Z4;׃-C*n">#A>\>7\7m#GU!|9x= 9'<î$褻g.l022BIIbxS= M[232xdeer:bG4~?H(]JNN6aa&$ GFu1_XӴhmImI 0H0d4uQf#1<<i]qc[;XH(vc$u^|=GbHD|8~=@|$ךqe{:P.TGF}Gߢ fB4o5'6N>NE-ddp^PhbÆlel,O~r`0E"aPT"3/w"A~Ax8Λo}b: 6oaӦNSl2N9thρ$IlЁߟ LaKT^{8>ۆǣr)ddf辥ΉNh w2 $Iu3FI Ҷ}z"͝ɑb;bC82b \Nu g?K20`e\Xo.+@:ן FS5)b^{ vF-~XȊ>,^ѡnO:АTCCk|)L6mxY1N0sBo hac%]' B!xiѷ.䓢5~:O&9SOxEO16fS)|>q#]p-`tL O+ߤxߟ'Sc 'IgkyZTJ,; ɟSЋ7BW'HDŜ})g. ZLo~ dW̞Ї$KdgqSQᥠEix@; W5i#̴SSE^eo K#I89^{X,IN,^NI-[z 5 ̝H 7TBee&ӧgL }TVf`YPZꡰŴiB+MFq&<1:]>sh Z<$+/4sHEdO'rx͍RnR?_P&7WJXb4qeeI̞O[p,wf̐9q/".ءfO;gRa ˓,D^JedDNVxTJ8 (x*ܹ23"h1 _oqeY"IbCQwwPI^;o%6#2'-oayh$HdW)Q";DVdrjr'ݧS.I[HӉen#idfd 2a ap:Dj 2v  q9dfe fd022B8&eaZ&n`pl߹fi+#N08ng,tCu&D&?,.PɝO.N;#I3ȩʥSDTb&l8''"2=$Yk'p"cǝp.gfm;Q0k1m- &"Ձewt+yqeUdO{*e99 P[cll6REEnIPH*!V*b\ZZBlKCC9M6|B~hT'44RVA%f,%Kt3LH;|t~`uw\OrʖNԇAeB*eQU%s&QX(LmY6mJ>g8y` uTUNȹl٢OېeUF yyK,X <-SYXHp6::,/Vٽ['#Cb"%6{%$e|= ra֭:kGU0pXHAQD$nJSym`[l|it:0`zR9ۥgP&./ObfL|yqEI$᫯ AeY՚&PmөSllݪg%: (̙i]wm6f}}&+Vɓrq=:;M23dسǠNX\F2=D C$ءlBN }|Y z)m )}Q[pBI^B43]P %qNUܹ*/!q-6l74V12baFU̒%J$ɫu;3r\|C$i !;F:;Q]ܪ9KB$.(nnfzQ2ik /t^qGhpnIuJKel8yX"XN5f̐ٵKDK/ilެs &;vz99B5nصKGU!]0Y_~YwڵBdf3g {5^(*ظQcn!}23fSTV*F+U4hn69~ܠVaw `n!EJ r8lpZӷ IDATH<`L/Y"ڵ)ƞ=: g<`^ʃ&9vLJfOHdg45 &m̔x$[ddkm' :"Wp.Aؾ]7t<;f03?]GlH cq??7ZXeTXMnIJnK(~Iӯ<#2E=C2}zN[Z9ξvATʂ?^L5v4V([Qn-Lql 6n줲2{dŊVlEE.NvU bN^c47zL$Pr/GPQߨnWr唕yr r%93uוbEw9kXNj'b!Q2KU6*̒N|WI׎ξv7Ds<@{Olݪo9Fs6>Y'[s͛-.,ey- )/Ofӟ&OxuFG-|2ܹ TXu|.ibw㠴Ta~K/itw ~@ٱCg*Yd~?q@?vA!X7`t U;7ku6c}4Ȉ&==&_Gu5W۸:!S4nj{gltutuNsL&&޺u)Eٷ`"{{MrrdnEuNukisBK3hi1˓W[ޭs5* +WD""* eq6z&XBel"jܽ$t 7nԐe&8/!u,2s i{,Ǟ:gY P moF,!, ewo7%l7շԒ]Cbi`}9(uSeZ~ c>)L "*Orkmauoa7H޻]rܤ"I8Kvb16 2Y>3Hg W٭"/&b_5TV5;sR8xRngm7/L˽93:#~pR_gφΞ˥BRii Q\0̞=JJ;6ƍhH9zmĉQ@z/է[KĕSD$`d-F붵A-3}\ԇ+%c jkN</ߍ5ŋ}|+0%JJ$LbVn mV9snoktuz#G  QNml߮S\,sY47Ș`_JGgdg:s3ϤlcS8:\S#SQpAcת=JK|NJ,y&%dD3)OϱeTU:n"dVV"61il'ki'+7WfgK̞-xBfv:4a<2xVT ^}U[U^.РpAuƍ9F[P diL֬:Yx!fR49qB8(io~ AiJo:%eoڤޭsr 혏M:y󾸀1|fp IGe#,s;vSN0tZ<\.\.!zwiL`mmdfaN"`uS)LT-w,,dy,"6C+r$C NL 2 |wI E{ 0RZˢj(u+aseBc8}v% u+ &̴#I(2nM"nZ%^dl,ŗ\ %p8t "cc)tnW0 X +ˎۭ tD:bCC IK;x6"D /sS Í#3-leS׈ "6\Y/PmS)23!L,< Ƅ#+t/VUZ8|kTU ;uN8+x$TU)DFiIxx%CH$,R)[Ct]8 -aQz@Hdf:  '( 'itԢ@"cC!@抿 [|ͅǍXȲӅuq-.w0(*lI/HxDYL!G8[|-".* DI*ۜq1UA $37OI_CK:+KTe41TJ:[n7$cd66^2-6Q#'H$i!XH?( zCH'741434cj0 HD# S x"̲,6o;gt1P(Ewwp8Rpf7$eY$`cc)8`<]$@a$X,^롸MQ_|' kluQ$W%DAi++([(a$gx_Y-7'^%\ t]>Mݢ,*WR}kmW^p:EUgvlMZZL d+W,ZrU*6h|k%v sH I&uu /rڵg栥S|"ʻs׾v~ $l.+:`N}N ͜{=>x$ӧ+to~BQVa>4G.Μ1)-9p@g4Ql]]&f΢E*N'TT;fPUqQA?#kh\ucgIgdgߎaKuBuRs7sba$uRMS}K-ɐ(c6%JR;ӯJ)23sem(WLfjZr(_)$I&f͛M~ތ|s&2eU_E6˖SWKo&XL**q*+3Y\}_eq 9HUW)03fx$5tuE͢=̲edg =spl2—TM}}6+Vs~\rfrAUdO_P+Ben:aUwSTդWO^{tgq]vS1Cm̝|p:e>gϊ7oî]BN`~KUfΔil4cy_.}Ŷo"!$KJJʕ*uu %idzzL|>p v#˓m$kz^AسGgJo_$7L.Iy-M@ L"~m?/ݩS&wi!ڧRV:/"bӦsn8cS=[\*o!6cuU-+ǍPQ*X -U"'=.HRq8uM׸z*^~U232,Fc45`tt/|Zζ"E%@~-ʠ᫋IhqSl.O髫8arrԈD0NK0tMk13y$BI[&iϻ/>;O^)3V}U|e8<>f8Ug&;9㨨rKVƩSA]$[Egg /vti>A5HQduk?s(slb tbƌ "Bfe1~/ő#w_-3fd^xXǖ- x֬,6m⮻_rnHoAAVL$Yl-3[~CF r%9SDjjdl6>NBU /hyA5% 8S^."GDV M"qu*b#?_̙p6$ -^,4暚SO4q7ERQgMq %$?ɗdgBOq6:-LTUeYbJ!L} *YkcfѨO…ۭZ)<s0{yەʼC6Mg|ͪU6S=jpu*'Oxyy2oqնtvo4jbRX(ƍ:dYS!B49ضMg KD@ƛo8`v)v 'NQ,VV.dgKzJQX(jRV&tkkuIƍ)z{M6lHQ^.tX~pN"й. 7x%!sB`b1'IZ^{1=8p@ɣp:[&ΖRGȮaJQ̅B $wf,'tFZqqd:Ïxa)s*0@7,DZ0yEǶ6 P*WMǕ):)_\ƛ[7<]˲⽟(Be8\x{5/0 #Jt0+iV\ D0D>";G'R,4д# :hDb }1. 4 б C\ݠ't}6].L)La S~PzW*TUEU?T/?NNN("#-CBf.NcTrC7=##"&wgҳr3/wyv`=g.VlZ$#dM; 6b(fA',IyG:#Gټ /g΄tuEرOŴi^ dӦ..套 5fq?߁x<6Z[ TUf͚vKPUͽlKCC;vٷo[e? ffAvjCeѵ&?%4IűmDh#%*&2}]b=8T?{]..{w'7 $Pw[H4BAz 7 3oۈ#-Fn5y S#tL… x=FFF()*7cđEhkk?9۷opΟlcuܚ|:n̲,No:Ioe 6E5E@7)}7VO".dj?M$Y&c:#MVͧvQNNk^?{W6s uwsmX[n SDٹOQ:E^{dŊV8YY6<w5qXTB$} vLc IDATC2n<\{zm,\8 Fv‘#,_^g;ZeMM,\Ν~+ YʿF^m>C O[LB `yBMh18ǒo kg*{9gzߝٙ٦v{$`0;aڱҜ{mKlplcczQ-P*{G;Ҳ$!K/vfg9;;aio_Up˗cHp9)X%j%Azu߶菟k/OE%~[[Uhj`>RV חcy }{kN۱ ^|1\fvgb?fM1fOXMr0c2(,b]nj?65;؃ee,r5'OLy HhI$5^sWWhԉ*ё!n3Ѩx\&Oz=uA8&8-LYD}v3 x+f*v-H8D}AiЂ07VħG`V_^Fj*WOx?42tl%*cfA`u_г?IXw#ʂNݓM{3;OUeDb/lC4䪫Mr Ajj [0{իC$ 1֭+'[alҍ,r͕=ÊElPիC inQQb͚M6Au֭ ݝv|g q2X,sز 7TwNm?ʱf wR,yP)rcMeGR&_^M“ *wߝb|GUv4VWټY\䓟pqjj$n̂&'WjQR""Re*\~۫oۨWw'FvKNBUu~8K8,PZ*rY>+NTJ-iߧXLNޠq{M#If'tth\W^ii1t_kls2˗K"?Y7d!1@U{҈yE&2s,L̝+Mڻ(MW'>age<x ͛eϗذL"sʕW\rl?jNT䥗dR)X~嗿tl*JwkLxYDoWX޽ wߝ\GiuO2uO6_҈"\y!L|v22\} Y/2,X d#GTE4^Vjkl>+ڪ{B238h흪2[z H<TkXܸM|FFtO!ԹN>+եd' Ѩ!{_Nr;qJ֦e(%(/yU/~sTTwYz(wc?3M,?3,[f4ߧy'cc_Z +[*h:?Hmebw)CnRKJ 5lYYt X=2χΔ+0͒dd2(ʹyۧ6+L6Q 5FNAM +sm`Ke|vB8eW^ʻ>Fn޾˪ޱ ̜뾌 -Yk̼ 9g?dT[|ⷈ# q8L̟_HssKz4V,bժ sfyo8XE~Pp vpc2NC86җ60f]we]WF0h|kvt@ϵzC98K*̓=5VO!v_M=q SߦdzFڎ0濡rT2+ A*Рկڸ2Ϻu&͞-qV1<,ƞqc 3 psH> @@೟MY+wݕk_K|( 9/ hhи3kkQ8~\XoFcqM(-?J$"HegЭutn3^k{2| A@@_jCY3n:T5+V0exC!6{^` _nM:pXoơC}>yedRGuY|ņƍffΔN\O:ljjĜ'mpP'14,1vk44hD"^ך뿶b6 X,p]6FGuR)C3Q#Ito,X`[Ic, ]7W]eTϛ'QV&0:Oڻ={M[)˜x}F5ĺo7zE &>Oݻj17p]VnlR)4w_oavO sľ Ǐktvj|k6|>={Tʼy%%}}:ee"tfY,ƞrE+FY-qb L|.1KJDnŒxa ɟ}j% }abb1ZZZ'JNIRJ2$͢:L&gN:!J>`u]'JJ4R4Ir󩪊iyNyJd~S0Ϛ_Bɒɡ$LNxa ?Ip Nz4ő'iz8ct'Tbkaa FaޒX><*KhjlOʻֲ򮵔_R9e>Fۥɨ(N}^ܱoя7IJe ׸\r$d L&یaXI2t\)`Ϟ~#d*' =6mjQ^~ k0B!;O=I)\n@%4{OKX%k-+zEE"( L۫QWg5|>2*ݗݔ~h8۹SajVܹ"^j⦛,"ȢE&**$EjjIUWK|KV^]f<^iiQEk xu ]]ѨKBАF:mAnaTTWK\qQ$͂VV0BkZ[U*+?1$j3a'@`%wkקYx[o)BowjB?I׫7n]#D$oN+L9DY dD2i]55ߟA 2TVX`6C$""I'_#OqbGSHjxoASw^j{$:ѨᝬS1V2L6N^U5֡Lڻ~Q4o6*PP `6 o[?q)hS9s$~ &.L7W.I$C{"`yBn~}¾}*ޛdBt8Mя9nȜ(5a \Ątq~8mpoo/>ʁ)-" -E8\̱c cqQ:;p\݃'Jfqtww)8ZQ+@4vxBq:X\i,70΢(sҶY.@+4PU$F㏷Y=sϵs%l$ᴴ<șy h͆:mmӦ]^Q Y6 NB;5:eegv|KJi3V=Ǟ˞j8' pNk3p/ dR_bkrF;[=;QAwdyt:Uy.8TwIϋgdtD,F"@e\n"R&Ifs$T8(8N|>/6AZ -a!`vBѩPTUl2~ұFnP'--F&4#駳 ] sжgv>oyN۶)HxmqXD]IAE!%KuһմnmYLϾnvf^?y MX}$B۶.4p}XVpC΂QLxq_EA^"O~~)Ud5!D_Ew KeNs}D<1</>[۫0T5΄hȿ;&;ݭ Jڞ@dCීN:b29}}:]]sH;r u]AOڐJ$tfϖdtӘ9S`߿_FʥILIO 1}!^uLyF ϻ;+hHT䤌:y-yG7=i&|d%&W7ɡ$cεQAf̸Yuv2:sg; ntqla;XN㈒Az|d '," dXOSZ*RR"kBczBAA7Ey,Wf=%ٱC!ٷO?̰hK2:&s%4mSrZ46|Ԥޮq'b4t]I7UujkaC⫹YclL'Q_*ˢE*8mhש3^f:GٰLm/+D"F5|CȈNm֭jn'*43, 9Y4W`xǟ;Fdnm69Nޗ B4d'0Xo+"3vHb]ڎm|+m?B{A1=_g(]vfmeqpK<2rMCfuf{[5б5`+ӵX8Y(d3ѽa9@fsCȉ,5_dυqb߅BA,3Ônx<YV4m-߇X(Mڷ"E 4Up:DDE[FIɈ& %w~ %а߁1Zbuh{FZ|e=Wdc{_8Oi~uv`;;-l&ݱWϤȆg%0n~==K/uH(dh(M8lRP`xFFd2*PK/Rm&ȁ|{u [eKB<.+%2{,=G1̚%a }w2CC>apдj3##:n7̜ix,0O=΢(e+ds08h 26VJB3[0[LlXHN[#wymF%1ptH8D=q|tl'0ƹXVcq'}!>Fpr49C61Ftɕ 5#1ApJ|:H]Y6%Gǘ>C `嗻q̤R*օdJJeH ٱ.+A Y`o3gN55>l6R'E0c c[fBcR "MoӦv0`K80Ejk975M}sᯜ+W3;uݥDW]>9RtDE(*fAj瞓I&-LpwWS#N+(\u]gչj3^@K[Q!ب ̝+QQ!ʬ\)k*\qb [n75kLYck5r 8Y%E[o5D" ##: J:l$ ".;|XAN SDncef+m;EɦX}=Jb57:6OՁ3 X]>쾢|*v>Uhrη^[RA4ܡrjJEk_xa NE ,@N)~WҕTr oADzb4|іfi{%09XVzu8qx-hvrڽsCy.)kVؘHzfΥ"NŌA{Œ9 &Mv~?s7ztC6흋@wANJDjL+ DngmgӦyA]03g0?8vkO-RR"OQӵ P9 sɷs1pFm`DQdx@([;^c t9^ގV,^7bXXJ,'dph:N:dudiM?}50[ݱ•[(1m/l!.I%t47(.9oŎQ5|Neˮ\X9|.'˾X¼yDE׿>9ĘMa?yDnpyy;S$t5cZjm Ǒ{( Gx(SY3=;&dع#u8^G0[\wppvL&tc8{;W/z믹t2AWk V~o0ߋό9 >9g@%%ekM;}zwr:KizVc>14u+>O13tlW؍xp?#>ΜLˎ' \pK(0v{AKny,${xqGh=;Ν:Y2HC83fxxNi.+ad$޽Bv"3ϴ1:kxvƲ^1FOO=b' A;.l$R Ԍ4ݵdb xI!$CI%fݵ8\ 7 qtMg+8c̺actvR4+iq16|JptENsBZXtц*gpɓ'O<)@I(VMj%]E+jsPv3`8=NtRZ5ܥ+Yb- 0>2`en{Nf̞ώᤰ(Ğ0kR~?/"G`i}ENd)$5ЁN|v 7V: n[gYSuާF޾iOdQX2=s?DtUzZ$Fi4.FZhD˖&1YM,b*/?^ʪUA#=h3YZ[TUyvOpX*!bVo 4 LUBQt6mj60FCnE$WxnD"͗4 E'ɍ7VPQʐeëՕd(16l(aժ 7P@YŘ&fȌyݹV^ENɌ 3FI}Ҥj> TPU& & gE*YWFuipn18?Ib`rx~n WQu ɭ|ɓ'O ɤ*؅JRk  XeL&;u]'9@4_7r$c#bqZTPXO]`b@H1Srlr ?d[8~ G @I'P ,$;WL& XdTb1:ds8Lb2`xd O>J2LGQ4Ʋ"t l񸌪FLF2ctZ﷒N8ƲdV㏷rQ|> vTJ!s:'WGwhvӂ|"0cvZPR D׎fQ&ȉ,IdyPX\4E#35Γ'\Uy.ީ 5/6hh:'"#(*(~9!taϫy.Q]3|~_<=`w2J 2$}=yh;zeqDl"{bA4FH$޻ VuFF2bW2?p3_,N2L T%|7x9Ilܞlf;4ǽFA3B69aMՐ'f9M%ypA'ix׌!ݿ9d3􎕓᷿m0 /qxj/NjU 1N^3CC6mj3'l6fMh+ts͕v/܅( |s3rkeK55^3]G[p=+RW7̆ %/-8NdEQ2 EFk=jm:Lz4M?F6a/kY׫qx-sq(A ӯy :Otû sj( h ɤf3(i@W5Ԭ Gj"0"Kշ%K"h Jt,*AO`W8oh ).<¢EUL"٬Jaٳ} g6@p؁m/P*7idY0:afk?M3?EHSR`x8CuMXXC`fÕkn@}mDn?}!bUh۹ WDH[B ZbȢ(9éj_FfN/8N&8X&DwwAr-fZ[62>XD ESfM3}R*.aC  n37X,Ȋ 2 A8.3fDIDDTYCU f^BsC@7|v\!7 Nh F1Q^& h}qɓ'O?~؋0TdEEV5T0TM#ɢ!qFhXdɤDG9+:(Z,L&Lb[C2d34kw'ÍhFa]6krH?'ۂ椨z1-۞*` ]YFtee,g;O 9xprgch(ڵ92ҥR)%K<|lMJiHĉ*Q_?B `(Bqauw'RZꢵ5*2c";֮-~mshJU}AznQ 0p|/ko`:h{seX~ADf]2JNd4zkHIܞOq yvR)jmFG,_^D 7n|'kR*6 U"i|ed23fxΰj5E;L$2ZXW53D; +`Z!YF_="ah| {F+  ,kGN*#^TB’..ʓ'ϻccb{}BA,3Ô|A4mjy b@4G#Dz%h'tMG8-QI%I4 ٬G].Yc+O=ҥE[[R[taû ' O=7Wb2v$QKc|y1Wx*3roaLGZ__?>G*h`@U E% Ajz"(F!O$|Lq+ g1{N|f[55?Iɓ'O3d{Au:::PUdY&L244nGuTUEUUt]7-xv1OJC,.e^u9sfSUV (bk*YI$ g,:89CC{ouzЍ}$HqEZFv<~xO89g䝙ONyyv<ĉ-o$k%J(qwQ$AA{}ﮮGEBQ,[Ctխ[nݪvD'# . " gԙ Lܶ'Wx.9sWm^}\~諭wKKKb~\O O]qScԩ N޶.9baŠ o_{nwS SxTJ>sWzNF?:Jj!u7 3oWL2DUT.YDh^T# XE4% Ô|i&&'zʘ%L yEj136>N.C  ŸZt0 DADZ\rlRt@($TfYl1ldl^ߋ y'q\TUU!IDɹ9Nav ^_ ORUwA97ջށMU4u >#q*#ɐK0M1Lz!EzfgSijjl<h}jKcGyʕ2M]I?fd$k9"sc.O=8֫]=d3riJ PATvL8Sdׇ1 X?AOO]OFx4598xpG}ldݲ\ :s OZ>cO|fd#fS1d|nOa0YPv dӼ=W8ztX,( =`* '8~|uc\b2֬qYpg#RWg LA9s8|^/B[K .N… Le"S Q_o'<bZ<IvaٳF+I&OQQId4D|:͵0K-\E^eLb GB& [g*dXlW\.b6t:*HS:Qm+" u8Nd@:FU5ҙ,Nښj  łi&Dр(PU0$2aɣNDUZĂ\ ϓPl9/E=UMJe011??q)#!|$]QBFR2qbŠ:;fzk\\22[_>ͧ>c5sl^7^''9xpEјϰwo n @ ùsgtc5hq0u7R .C#0[p^.}mz~paE{N(E;g/,dJeK96H}8n6axa$IY?ǎ鹢kjV涎Fs!1=̙d=L塇j9~8th\N`um+VN&O2ŷXos Gj4UeMc+s-} |CCr< \g{c5$ǗeB4LyV+tOYٍmF`X,yS ¤RiF#.˅BD"143ɀ(*hIXFUe EUBA&.NS/f3{//ybxO_܂(XG-]bQ£!܍e_QzEK$$Xܕ, G$\_E&v6!;%x߅YbQR$k+8wm9щT ^-Ų>h4b0l%J܉Us^ "`][Ad<ε"0E 4&jאvݲ-l8V5k\LmH$\ <9[W(ƶ(褰X 8=~l .jș$k^kL]lwc)$2y Aq֬!2wM7lQ8kY.$qT\2J7BYzVљ!fzIR 3x[1\5 Uk׺|9j: NbhQT\N8 S:֬z(E&QݹlMaJSQ<.ozԧٰ( hh(%~=,߽ΣQ}..NJ|i6nW|ijj%slVNooJCr5o56.xgޚTօFy{s=hkdws/b XQFƉF 먮Bp[ys\., ºN(J$ Hfy2KdYl(`0*`@$B,'c0k j1hdL$,,lAn22K|ghjr`|tL|*}M YU547;Jr%~, PJ\M&>>K`[ ii:NG1mJDSq wqzV]1E+  lX |-c*(?d`4".jUm㇅\"Gd8;ej/իỮݲTiIK NӊڻXTU#P@4cTYFSEiSi*(" "h}@HF b6[0;lt-bٱX8N4]9<===fL(94Mb]+U4oC+ʽgy $-KW۶sͭ,}梁￿^Ǖ_׏ߢT~Kyiy^>̈́Ӝq^x)=V;M21qëw\[:n\1~ 5/,QD_Sy!F0`0ZlFg饒r/ b&Dٌ(I$#FI"N#J&#VAH'i?LOOSOE֮]9;Et*BjF 515M_?ıѢ';I';G]>FraMEG;q'?a~>æMܲO=5B0_O\#*/(.ؼKM}o~AzzBGP a~$>;F:]/ee&:O02gFMMvO>U,j6i[ 4vzӃ\:d_g >7ڇ?@ZN'}oS17GFjUfg#yZxbQ_^ƍfcEزh}\n?E+m_!gdfMPl.=wx|2w1_ L3 fZzf25RU{_DXz-!)(%M`_*TWѾsQT@``ƥ)a He\%fdeU+Ưі oKE (ddT.G:G4JF>'ôPQRs5{0:Ll\1ڈaw3S&zWXؗ0ZyE z>ܥIY.>hƊd2 2?_6>fv c l^A2Y@!n|iF;Xh4a m[=[]K-+ykpT4Twct!"M\?To TTpwz }iiBy{>_e׮b̾/}CMGGBXov+&Sb"H$&£pMm+_#K@60:3i(-Y>ׂϗʕ0vS;^ZA`0pp8L:&O2eqܵxXrC5 $'0lqwq,YV Y?MզQD3?(B&n?}T I>d7102#XYyJoҁsWOR1_4Mnٖpl6`20E**, "Mn7`[c<[V a2TVZx1˗;lRXh4(ވtxLDy\dx8~0~;vӟ^).!i$lW(kRV(!wM7[ɧ"߶'j[g I"/Ĺv-B8..˓zbp D&PUe1c6ع86KTVZb\͛<8GQ#4oMWd?_ʆ l\9ÓO>xMw^}YLwP+?fb9.<9 FJ@omh{)JS[[ #N+BÔ(As^Dˠ dE  A!O@=\JeÎ(AqZ$lvF3yE!4N153K&'kVλ,yN15\G#yN*_OG}lVmmgby~7ޗvLN\MӈME)kܹp%FEE9u7(J^%on|dJhb(4 h**lH$F: IHQ䱃u{ty(˅r^ R4dwٺ[+ſ~mw?|wi;zܫkTJE[Ԕ(Aަo\!h 6̢X ,N{ (dY Q`H Q€h1-,pnGQEEQ (U V;rwO>( Ypꮚ8PL wx!ի ]]i߉ W庳 kðm[1:bVRٴ֡Q^~9ϙ3oDI:]{ZΞ-{}WjʣJ%J(QW{U蘱6UMlR1O8Q@CiB@ф>ڢo6!_0 HE5a͙+K/f(ddYF.P5ٌnGS5v+]i$X^!31}Tj$;B2UUi"w WxV`PezZY9*+vs'$2~N]2<`4y`ks$8ի--J(P|m$amC EDSU$ aXAP熙4I B0Hx"\(,рg LA۬X$ITWUX_jyWQ0߽ xw .㷄Ah+Y2hаJya1򒷯JR$S)ry9O>/SPT؂(" 6Qd2b41 v=\Ҹ෿ND"A(* %J(>PYY?q>|O%R06+d2b1^z%ڵkW4sacoϽ4$Lb ,sF@.h3*T*p$-O./IUrU((,TWU`1$I1MlV6;ÁaicٰZX&6 n ouU47@E|7 / sx\Fb1<E/N1L4 ǹsrخiLͦ Q]]iD"fq9BKK a2PD"l^q|>ӧijjeǛՒfH9&''m=T U-fYm{?B~ZnwZ,|^%⊾|il6233 F8>ł# C-g00++2TjmKOMM͊mütuunpaaanYfrrߏ뽫vEUr],8[pVL*ߦ];i|>,vwꋞ$I"ݱE!c2SNQjm_Zg;399I ܱvu%ۭ4 {,J m}RVUWJ,_Dey***]zܳHċo rF,/! ,hRb9d`LDAQqnBۍ(a1Zجv+NÊaS㦼CMUuUVS[]EUe9nٌ$IȽs#[ig?ctt6?~ӧOǗl3̊(ַp\8pQYf͊f @3gϷ-:::XXX`nnBtk}v}_DP H,SӊrL&ӏ388ȑ#Ghkkfʒ$f$Q(R)|>& Q$Jl6PN& isUU t:󌎎Jl6c0r4X,):M&,,,iKGGY%t:㌏p8 R(8O,;w-[dhhL&(LNN|H``hhX,(1i?8tsssd2yq:D"q:|_FN71߯9_ bZbq#Ȳ SX+ʕ+TWW“?~?vzLLLq:afgg$Il6~} ottE `~~^Ϟ=;w6FFFX,p1frrRץ15kq=~?W^%vnBǒ0( cccr9, e?333C6舘7QB$LMMQVVF6ydYl6낐}qell477#"PYQUh44nx!b3MIDATn7pX~A:;;$ DQdbbχ(?33<-< J>gff\.DZcz >H&t:M4՟Of֭ iŏց@ 8ny抳Lg6jFGG=p8VB &H$B0$Lt:#L""/5552_FQ*>&XB|p8xgy7<۷t:B6x{xhhT*Ŷmۨ[1Wx<`@E#1YKnQ2b4Y1 Y<6(؍ݑDeNMP g26L&}`е|] M|k֬ԩS|>5kְqF:::p\zW^y~vy~#;EQD"?~`0(ŷmV+G! lٲEWQ$IBQ]{> /P(cǎ 366Foo/t:9uNαcǸxcl6zzzLLLӟ-[F9y$ΝcnnNE_fxx|>O(BEx n70??ρhllpSOqe2 VZW] B s)֬Y}]st'H`Z9tH)FGG%ɐ $In7?<|T*ngjj.z{{ٲe ⳟ,/_Ǐc2Xn\|O}S444`٘c(™3g8uT COOf .N駟féS+WGGGϟ300$oٳg|l6 `aai֮]K4+Wf$IbdٳtwwOJlL&^z%'ax TUUQWW(|k_t2>>sssQ]]ͥKpɓ%Dkk+dYr^{8N}{(⡇]oCGGrd2ٻw/Յbѧ8'յrFM6fÆ Ȳ(ڵyTUnUUUH$tww3;;˚5khjjbhhrtM,ttt ˲~L&fclll6(w}8Kcxxe\~]r\ݻH$4{tcbaan0|ݻwS^^N{{;[lV o[larrZZ[[1fmۆ$ILNNRSSC{{;*7n$ ؾ}I^nr9ٳ`0 LB@<5r9:͛P(~z}9NqKRtttfH&aJmm-k֬?BBe:;;&}i{EUU PVVFMM ---;w.1zۧqLQBu#2pXJcY~=TTTw^6fx, lL&_WQٻw/,SQQAKK fӹfdY$$?{!Ѐ"rj{n$IZJmm-NbvA?\.dž ]v(n#< #"ׯ_Gt:q:9rVL&|GUU:;;EE6lؠ?#>f36MoO<Hcc#BӉĉX,:::\pkRSSC*隚V+555}vS]]n7Nr477S^^ `08v>}3`Mhnnׯt:1x<DQt200륽D4)m5XHh[{9&''yX,F?ttt,mhhLMMqq]vqQ\U00V[N+ `2t pSW܌ӧZZZسg^.L*Y$I}k}{h4zz-oXKS7o;s V͛7/;ˮ3<Æ >'Jq~]>/^mou|y!ozPlm͆~sKyٯ__QOkWpS whܼ|AY *:6&;۟ loǜ;vx@224J ܝ˲¥+;nm&2`wWgN{IƆbbjtlc %E7o ji Y=iC/;p){pTOw^v^?Μ1`2tB?wVss󋗯hwcX/^^r9gKJJJJJ'ۊb҃~QI=q ~!hoO$VTfT{',,t08d~ȱ"®#ٝ˲qjEA{| &6^WW'"2X,VpHCX/8sBG!BP`<./'! !` +zaJoE|DF;'>Pd:)`3uQ!!!!?}>GiIMMWWS7V^^!t",\A^7y$2d2{Te`0wG?|PTT62j]x<~%^Izp%s_\RRB&x뫇zITLlmmu7sƆ }G "+**mW3f~>ޞjjx<>Iڵ둍jS'ܼ~\(",lm5b=vߌKs~cc/8wRII);e? /[= [U F<n22~؟ҫϜ~AHPrr24ެgsވ$IGut&?z dd8dX׳@ _*.)9wRa!dӛ 0l$ pW$=CUUu",\NÇEEE\[uNQv124m;vjii ' ͚>yy$M&vlvTtڲwz}tLs˗ڻ/J ޻un32%X`D<+]ZKWUUA98 ڥG!EKWF^ 9tgN͞9rW8s!*3oL#!,6豓 466Q( 2X#渣78$mE+-QQQ>.+K}mNn$5ܳ)R,1*pqvEABB kݯ2Ju7D546>{\vy8?swW.Kug[6oD݌te%Ōg9ez E:"*Ç[6'CEDFc,&u&!!!B.\bLnIMMfe8zdߛDEENYjRMMmcSMD()*g<jjŒNwҘc>|hcm9!TFXonf;.hXoS2ED,3>N|ꓴ #.]jlh`gkRRRVSS_0q:@nY0o6IJFdtsylnz?GUUUvooݾK^NvY_\ƜYӵ45JJJ/g)B>I ;קz ܳ%/ -=!D$uuu6o$-cPV= .V`g;KWTp[x())yF ҲܼgplC#ڽb%%%BGi |32G@ ݵn?!Tys?+ ~K'ܺu):WB\36m9y|#sgx򕧇;vRYY|% X[&<^~Ӆa$:qD\LӧϽ1I[w-^x8ܳskii٦-ی _v]򯈰p>fEEq>UUU=r݉O,J?ppm~9 VZ{?SUWhkZ[ ч 662Hz4x@MM Pzbb%]6pHUUUNn.BѣT8=ъ| +,,=]R7"=\B.Nwwk~jFhy QR/har%Bl)UA7(++IJ/_w9ؿv?N 4PYY 2b99x99Y%EŢ2kh._]vc?mڼ>j./'Y. KK䪚jlUR_dRUUB*<b؜JvиF[l%ЀVXymmcZ}F/^)-+SWW#_ri5&*iJuu5BJC]?DDz{~A} Um^9am+*+Bk7l }B՚iaG!D&;=ugQsrso*/PiY制;vtruKW"Z 1QQ7''sy͆ Mz( g,^~% a=՝\X_(/x(-=xX8B!Qn.]=S*$$$))Aջ7[mnjSVV:t s9>e`߆ќ;v?Vsng/p8 aò8vƽOVJJjCed>v7H,//nlvyy9t)LYBH$N<^EII5V,[*]\\cW?|"^5!!!c#S>~̱NcPƷ I㬖X'S刔ǩ{߻{:dB )6]H;nnW@RR}Y圢vS`00;nGS/]ĉ;e=1x~t#,޾lhcC#6ڊf?yVEEco77;{ 9%"*fKi{>:mӿ\MM5 !$ o^w&-M'dj׬,/X~S'|v/b9sq}tF<}CM"ӿnF),>u2\gƹ f2q񷄄tgϲrsBO iEBfC߈,Buu>-S@@@DD$w.!}.=LNa2,SSS3#TVV{ hEEpx~',SXҲ[-̇! NIy\TTf32qN[Zp8*j׷=NȨҲ/d.^ft,$'?f }Sinnnhlp؛oÓtE-M 237viW 5Bt~,6;2:ՙJ9t(\ }=v V7:߈`2NUSSņK(c- ƍNf))"֟b[܌g0L&3⒒O>#H$)EEƦ9lWɽ{so]i $==SKK!$/.8QEpwsaX6oej 5:ʿ7rdӰ{$$$B~yί%\pW]L ̚ ]Fa!M@@*C #šF&hu3ncG:<豯feJJJ,Z0K_$JZY0 njj616 I}v002iAfόp"%+K&sfNpUkY, EG&/Z0WLLϺSԑq0bbb^ RQX}1`nB?naΝ|ȨMo 1&`B+׬VW.[B&ڽb8y$baC{0"¶6V7"Θ:y}{tu-&M?r2x -u6oXX0on'ټx ?o{oߚ?g͞B@{[wWT x'\~ulE9۞;v_bzz}_ˏ9ltFX-]XBWQY KNKI~P^ ow$AɫV,Aӊ,[ͽ ݊_ oK種n|_Wtz׎A4ZλM590!dJQ(| B>%bYuݒ(IV<v޴v~?"ų q-M-Pmm]Φg9x߂OA DS)Q(| B>8j9M x5!"yktQsLd4%~ HJ5Wru:ua'=RIwb>! OA DS)Q(|CԴs@صDm !`&X,NJUUur#꽤>yM mU wۉɏSpOJNy-XC:QLM$N_5w-~K]O#ۉt,GXDFjxxi9l:>wb<;=#WjjΜnd A-]:;4y)LMBm?gVgp|N--fNZ^^| Kaa^^}f Q1c< B+)-Rm>|̩^xByGH^ G- ƙ"Ju;qߞjOo|QYYj!qZ.+))3^{G_Nn^QQPQQQ+1q -[H$ d>lBPG[;qw i/_]TdtLUuqq .b@hw$YP(tzE<&o/BLV- c#cjjjuξ;_-]qٰkCsK _ 74455q d& 0!wf~]խ9tn_t;ԿN;4OA DS)^޺[`-|X]2$Po:KZچd$!.pÈU*X R /}UL7B][cZxBa:&S:[E?ųG`'zyEˌ,߾Nilfj~#45nDFs܈lo577cy%d8u朗o@ʣT,'.kswN/]v(/c wvwvrO:f!JIQQSSN=lzD(7ɠ?pqxޱ;~ҕ#o]z=gvWY/''}h聓$w™Wο𱱱6~bY\ظwg60d40ZHqҽFI$AAӦ}ajsa3O!JV#Gݹ{o"($$4uğz㈾egcg_WۗJlb2agS2 _G HJJ  }tF c BN/e(<6Q"""6Q1_;Ɠ{Sw&}8JJKj x<@v&iirii64w@w+nY NWHl6;2*{,+$JG骪*RWWfibՈSgֶ8y,կ,ydƍSD+2cg̹ F/N7]黶mucexG嫢5ʪgYYC:n>;:Ι9K3JJJ6=}֭^aldt|%4DzyҒENΘ&]";kDZx.l.5/"UgatUZD1w#tu ^BDq7932ec#ފq9@ H~&#;!_F=4w2OA DS)Q(| B>OwNwSJ/",C-p::|'OϘ6 xъM 3A[Z IDATQiZx|MrY>$!tvE:ɖ;nڲ-ȱkU?B/hw܉Q17,8ME5~5'Σe:82;{?sfglf^`xiij܈^d!'FdE{Ǚ-plitn^^ 2 F]xf#пBzйM\fl57/t Μwt Hy%,[y߁XЉSmkWa;m=]̚(**WO_>;FEN9[|8uǭۉ^~+<O ek|jhh\thQX-ۢbnnos+q ---95yŹyXEKW9wS[wB[wvw>w]ڭ~!55$VGDrsaX  =}}eJHJD\辰_zZV;)VZQ(yR 55DUTVyչ;,.Yt'.#FMsr\Ƶ=SGcOt;.j;vWT`ӯo[6V'3Ž1x%MMMyɜܼ\x6'7cNNCz#2!a җ&BC i4P~~,GPPr2 M 7ohEOe͝5CHHH\\|ެUVT"deΤ*J^G JIҒZРF 7n KJH|)**"((}3nR`4Y@@ `/JMK̄w''3MNugQ!;=?z߾T g ?(`0 ,t%%$U81`I.~}cqq8g<Ū"I!JJI$.I+˷M?pp]}=(e T*, (`0e55Nzqqܗee۹D2LHs?:U]BHDD&"*߈Q.ܛϞO{aX"Lo4a`\! E'eedZLl|wI&KJڦϟ;k񂹫o<n:t EK-l6@̯__j)-M.+cuuuLPG/BBʵyfø*d2͎T474455*MKS#`PlUEYiMMM9ho'$(;k+׎8sacccAOmpsܼXQUUMaTQn.moI޸8<N3DD RSU|^tsL&3 ~}d.#[7vLʌ9ZX8vp񈎍k[``x)"c檪*'7Oo2r",}p=;>Ij:gmZ`de{vlx嚥ϸ o޾޺jON=='!4mJq';{gf߳ ǍEyL6ICC?kTyyo?7O"QRCFpUϲ t#],0hӖ֮fl:)@II黚 qȸds=*eڔB聺NT9H_(Y*|L^^fK]`0SWWm2oRq)/'7zVIiipHhqI-{Ecx Q"3Nr~Xqqq"ߏٰVLD"Ao)Q(| B>! OA DSt9YZQq9eI"06oK ?=ϧa?I;qi[b/}4ZAw/756*-P ?Ϣ]6h"c3K׮aWSٴe[cOog]=zI#p[w:{ a|,~CEˌ,߾srC/[Yr=ǫ3;ƆKWݹus{7oߎnIԸ|BNʍ(mo-<WS:<b~ZٴeӬ/%$ħN>I,Uf-B44?}9hiI}fkm`l͹Λ-$$/.)fj© >R/gblpoOwd7o|#*&1C̹ "EU! 9ȱ%x{fo߾P͙9ܬɌ9^]]gݪ O;iiiO/O2nB+w*~¢gNrNƻ;ێvwv O!%EEMM;\X,ZQYp w̚l򊊶0W/pĩ pK'ddtkBetUN3ظ.܅KLnunN7[}3}{v:|ˁZpE ZI}|04h׶[7#Z!fWݠ}Hhp}&k+D0ؿBPRBn5BG[XYh?LNbl(7ظwg6mPAA!4܌J[BBBB#,srBIxXGjv68N%w9&ODZXs}=T`gPWPGhPh/'jBh^sBbn8P|s!]Ӧ Jɾ>t㣎>,gY@P2z_Y*v߯}v6V{.,|M}TΦҲ\𶦦 rҶ96Bet']NNcg#gQ?.Y uFdt]]=(`4'*CiuBJKK%=TUWIJHHH|lOx\%eB!$ YW__؈-cZy1ܛ(~􎆆ڥm9x|N4w'V$/'rOmF@<m6;& iuD~>3ON,QTT!TYUewWG_G?xP\9͇!TU22b_,E1`p6a<]\ga6;rǏѱ d~I9-܌gX ^26ǜlvNNn;{1trr I M uPrc:~f'=L~?G|z/lvSSӋBJZGd0UU/]jn' =z+l;Y0SΦyg8l۲u051Z`m;-Fp۷l|捽ԙsxn]\EeճV=Eҧplڲ}ڕݬ`ӖS'())M[ʶk2mJ`lY@'*J],IcZ|L^^fKju:OߌeNy96D"Zx Q"3Nr~Xqqq"v 7ٰvs@"H\?Wz֯1B>! OA DI""ܫb<! @O^(ge~uؤB::Z%^BT=auvUg/hJK  @ LL(^B‡6 #.燍x_5//.,*u|BkK}P ?ϢdA$OLNs"ٹgUFfDit7G-^ᣜՙsL6z #R8m47oVUUmfiǝ9v|Tt,"dvBjD+>Iǖkjk߼˧˱ԴtS+׌})oOYY*B?t~|И/_X[ Zwc_>j2~:!u /Bbg<jb\YYlok-,,LHXY w!>"N9(!6/'Na 89ų_^s3rVdlfd2Y,#9ggEY`nĵKV!>kAmTTV.\o9/[m͆666fd>C1w\3d>͊KuLح]۶IZz!G8r(n'rof3^](I:LNy<|XZzf}} #AAA<^YsN1qjZ /M&#,̇hm3Oz oognnFRS+JPP6fB(9屠!wYLmm]DTL}}2QRU v8NIQy䝻xhK>zYsIJMU!85-=lGLTtƴS'Ok5{wm364.dMy`0 F۾5PiiYNN\[],X|ظx'GVoWQQ>~SN9;͚9MHP;)1Ţ5@/(c57%Nnan$Y%q$.Nfd>564~|A|o,;"ݯЮ}ח*CsރG6e֞<>cde/.)Pvz()WO|m /L#,o'%MU~~斖gY_8!D"޾FX,zy4f#b9sacndt,`2744v*瑎TWShBL&)))666P\\b cGe LCM,֐AتPScl>8$esf 35AM 0=>chE^SfҔ#M&cj;(&9t(^rǘIƷk{'mkcŽ8\EeճNf {W7K?Ut]1lTRRffu;H/=#s>/Ϣ$qܵSФKwg6bNNQSSٶyc/5@k`syIA:KįҪ'xGNV/!JLpĊ\SSSՀiqqq"Qոm;xtN H$ү>9~7(| B>! OA DS)^~v,˜T@aaj%D8yzƴsGtgrSc2zMM ,ޯl[/Z6x7u?@wpW:|:sfqV\>atPʣY9͜zmߝ{.Ow| Р Љ^Q߼C%Ig2)[YKKϬad` ((CC3k)38$esf 35AM 0=>chE^SfҔ#M&cj;(6S2t1+{]{Oyz\Eeճ,C!{W7Ki kWvM[O]{ ͢v8QQzF }}^ERu33yyo-aXӸSTmK/!jRq)/'7H$Pˏϝ>ۭ!^BjjjV\\H$ /AD"Hm 7(| B>! OA DS)^~v,˜T@aaj%D8yzƴsGtgrSc2zMM ,ޯl}.QѱSg~z>{Mid{ERrV̚0g ?{蟣8P(fLM PBKz:=CBh|aCvoonn?qGT UFh8W8!3~_Xؿ:񲍃w!];ۯB̰qpL}ު"!!!mmPSU=vcXCc!TPi¥ΣGWKK˗;vwvw>f[Un3 >}=ooׯ̹JE,Av IDATwH]z}t3Xsp6B---^>/"" 9`]۷ п'%%ŊʅKV\&akr^LnU> ?w;1iV\­g >a  0iZD􌧞˗,*ѦϞdog)fIUk7 4pޝ5K~}u)GWߛc\Fግ [c9qxܺ+7\] *(cbJMMMkFYLeeUںze$BH\\KJHPXH2+(;p8%EEWw.f|.~?sTAAAi2ǻ"z.j_IIKhs*bJJW.,''3Rbi|vsv5sW^(*.p?*y)b̨22OlFY]RBBB0H9Yn~P9tQ()))`8q dirqq w9t~zGCC:bEjj*]CC}utzUkO 41`΂%qwupkYFsKJ[ h>544 **;o$}8Eƽ~f?Ix<ұm6{Ș/_XBZMms#c |{/^.\bʤ ܛd2IIIn@Ggp2DFDzXX[k+m' FUUKW{Z162\4ڍ˕vlK]Mm6k%%odۭO{mm۷`w-SZXy9Ysa6!WUUnӖr!A!&MO;{Ɯe(e%w7Nfoϗ^ >(,"Y*˵왼7͖`x",fܭ.sɍt!<K1uRMMMÊ4%D"wЃOA DS)Q(| B>OwNwSJ/",C-p::|'OϘ6\xъM 3QiZx|Mr?MqqgcccY%/Zvlg܄3/~S L&ԙs>'ZX;؎t5waqqhiOP,'"" //ohh\df\[Ӳ [W_*#l{byKK˲Uk7cd,N/_bH7Q^/^FM1[L>KطPLL*#ckmgͳutu=&,,FhE>\sei_z0i>by\G__C<%oe_r i\G<tv[7oH8up 3PPA{[W/kU6J%#]zf֝v|ӱXl։S{/CԹ>jjjpXʧυY_̜>@ ()*Z[HwƪN5B(f0i2RNVmIxX dVRTV#p$JJN8N=,eEJ"""6@ (*(\oQq1DRRTD(Naaam-MvҊ>˚;kY3ݯ2d """"""V#I_Aχ|u5,!qvb}}HF*6>!D"I-;t92ilA@@@L,3 NtYYjEe%)RSU餑x<gX@  (@ ((ME@EAT"muoo(kEEz-kBRr"J4췪Ze2%"Ғ!&xhB6]]VVaa0oNFef=:3dAV^c0jԘ{B8mN9ke4sQ.JQL&X/ױzhǝ ~ve<~\◟R7Lb׬*))wv3~xV_8)ZQqIzFƠ*2P_r/na+W 𝨩Y٨ Qb&5q=xe}z79QQJjZ?I rkIdZzzfK¢=N&}#IKxl͖|7WK ϯ $)%'[VV l6M D.wo&$t`08iaDH)(RHQ @J!EB)R ) $VWW6x$hqH]] {N/ BZ_[[;Q\R\\r^pqcϙbə 3K.]SWGݔ;ön\{1^%?upv߾}v#?qWc}zΛuK3HOQa{YX{M{d̫ntn=Ҋ2Ԅ޹s vh6#ټϞOd2{i5UUQ CU;ulKڽztgGZZxP8x:jEtlx_J222' w89'$D"BHl|=!BL]Ϟ=t8PLjhbnhbn<̒bk=ɓ}dlBpSG|5M]TpToK ^|%WUU]@UU.!dB :vjvo0u>___O\.B\Νп߹VXVrÇJjEqIqu/*++}GB)--m0sL^BD"QNnNKa6̄fScMoɹ;9o 9tч&C?}WBkI3 A͆x|tlmmm]]g?|l*v^{-))=~tai;B>{ 6"%7`@?B7._qvt"Μz <WRWU2ب#6TZp8O>#ƻ(e%А[CB7m a0::ڛ֭nj$:F[56Y;tVQ Xik 0۠Ys+++M41復X7(SWW5O( ټA$]MM577o!il4xnaɱ̆~OSREt;CG~?9=5yׯkGϟ=ζaЊK32 TDSAJ|q Xz]DMMW;%h縑V O&'*JIMg` Ƀ>U.Yiꭚ-k 3֊D+מMw6!Ƀ>_qTWSF HwzzOjjOŒ -++kv~X6h:i] h% p8m1)@)R ) RH)(RHQ @JInuuuH^Nn\7KNNV'I ՙ;pp?!Q%Ƀ>/h_nl88_VV&A+]Thv6t?=ɕv?5]_;6 ΫΚZzSutM7<(_GG[3<DvV04ZT5mj>FK-۪]@jgN_RRzYFij nj":&Լzfي| CmۻÑG͆ E"itȞ)ggB2Lqsn 5kfgεq3~Gu{P^U7mvpvpYiP(b"pvazk7\^1L]L>ND]qie8}</RZZvn#oz銕..NF555VBaC7ԙ3?ӭ8[Lz}KHG+m/HED|~ZZ݊sܥAS.^F'~D ͇Y:v{}vOĤˍ6[W7/'_KzrC6O9s% r5ӡC!wluڋqz/Bȱi4Z̹ ϙT5uDԙ7Ɲd6ZIco={V]f;ovpԙ#,-Z?Efa5wle2^ѥH+BpS.{=*c;ۙFٌz5!}6Ӧ0Le%cmEMUm3PUg@x񒣽vt:Ѿ8ÇJS&#'N7ʼnr!&ޮgt:kǃ v|cc_z5sƴ*&M5417417fIS26!q ت?EM_$?WyUU TUR2Lf]]!ߩcǎah+\ng Y[WK)GOv=.!QSRZjgkf}dV/'wێ;C6U սOrl6lil\b丄fOk1}~OEE\ dQ\Rhtz#BJKKΘ6%$4li,eO1Ӧ73jl2'-Us⟄BϿ޺:7ZI:C!ƤBkI3R}l؋G=}Çʦjjhgڒ'Nѱo߾#\yڌ쬢rp!٨̬"*CBȳϋK!ݺveٕ#'Lwh%zFql.!#/6fgcf%h+ں}W !jW@$IQ,9߲fUPP`ٍ]޽I8\`p8Ӷ(RHQ @J!EB)R ) $VWW6x$hqH]] {N/\ BIϣQb>6h홢JKK,^6vGyqWU6|ҕo/9=˧Eo0kޢ*%P(ܼu@PAz&ɪ6Ԟ)z !/\vF=~g_K.!;k'EED/O@>;rI~S\F>rF.ĸq>kSWW7mߩA.w[80-R[[cH{57UUU}K&v쫯lůsf]K\}z&D ꮥek=ٳ%%˗,_H$*,,:z$YK!DSCcOK{I~Smwlw׎ݵv޳_ lja}pOupޜ q@xzfɣL?pi_@{Emz}θ}!̬iS 䫍gdޱM$ѽg޺:;x9ho׳g:5G䅘8؝:urJ6מwQMK`-01㽩# IDATښ!F4k|„IG 4H(5???DjCVIi"=,(f]P(d2(Z;( pM|%558愬ll=YoƽC{B({z6տ~auMw;JJpV|ۻ)E55iY ~^K[_}-!@$x99E/^ ¦*vԱWn Μz <$2O>#D"bj>l~aII?_| ]b{h؛u՜5#h!d՛pv 5/^8O]M&5[GW?.hC$\v8dqcsgXvCNNKe6BY'{v=rDŽIjO%(s'yO/ B$=#cME?/YzNnKXߧw30A*{Dj$$|}G_l6R]MƊԐ9FIXrreeeft,&p p8N@ 7 @J!EB)R ) RH)(R|[]]~8l*͒I#"uuu&xz4;?<8ŏZ$y ؒˍ $hqj6 \ur?wB~{굼|^+.hdqqIFfpSjw܅n:#h m=!FX @:g*--]xym陙ܦ/^q"̜>m-zBpv#EHLQ%#,,̩r>pirr{g_mM͝FXZ̞9Bx֐?_ ado/v!:{;*\t2b9"((xN~A oOdGh홢v쫯lůsf]K\}z&D ꮥek=ٳ%%˗,j9 ̝ik=23{"y!IK_0w6_8f!M- CrN.N ݦ'(xwB޽̚6՟`hjhXZ]N{urk7njjt]55=ǝ"TUF93 UU~/_z' j:N pqe[6a0!c{S5C=k1nԶ"ƭUWWR\ngq Y[W%V;( pM|%558愬l(yye񮊲rN?^x9jjv)>J{>{ݔԢꚚ̬?|խޖP@ x*(r|񲶶V(6Zٰl^NL\P(|y섋'Ze2<}FD_sgTTl sieaY3B6]]VVaa0oђ  O='\BLϿu\|'Og::Q?4sHHDr=>nWH߮U޲Ι5Gw-qV魛}6'77 gϞ,_RT\Sjv7yedsX19 ̝ik=23{"y!y{ܵ}kg6)yEB 8{w32Mg0f68ڪޡdjutGt{BݸֆNww>::sjjX68mhv.!ߡCq ,^eZ2f7P[S;بQEBH~A>Ew&^h?-eU̚1An^^/,XS`8&]vƚ`?sBVV~ Fo++)=&6aLU vEEEeee5Ƹ%Ϟw^EY9'2xu5F=ynJjQqquMMZzFfW_oKH@ D<^99xY[[+ !AOwлٕo߾^)uꖚv9zTSMgdVUUhz}Bammٰl^NL\P(|y섋׺ЈTTl {]]]]f 56"l\znuBF 穫9$_fjc=s͆nް&p䑣ǫ;(ϝܭqZ3s"=x={4tK /3N~iiǎ m6n ٰyg+R@gd 8 %+Wy6r߉Mw nrԴ~$^Sϟ?_|'ꫫ;~xB|Ǔ^tIS3Y,VKzAŻL&3bbKlVZ:˵+.Ҟ Sv-X"h߽=g~EEŃ;wk4^FFҲaX166N ;v_]/}OQ&Oڷw6ɓ';Cw|C^4֭[)#B-̩xq)Ҿ}***.]3~KI l=zg+P?t}uޝR^^ݻw߻goBBB]]y`99ٳf{M=]t)͞;gnAAt:=tghQQ߿'.]v !֮?jJ+W.Zhmm0%Ą LMM}qΝ,XpMc !mr>|c7n0L޲M3&&Hđ}z/ZH]]+$aE)**MڴqCtႅm {YBȝwLmG">sFh#jǎ[nmw-6mbj@^rgϞڇeee8|7o߄ rss=ztgN cM]a8rwiai??6s'.o׋:eaat WRa'O c:t;:}j箝Ǐ߷G"~?/{gJJk֮:էwl|4:''' }{/yzukk:1#66R\\TmLׯ Klkk۫W/:nmcݭ[7O͛GxQ$B !qqq^:ubFzI&X,5lFЭk+ɏ?իUXUUFoկ ~pwwgXL&3>>ήw4mࠁ&&$NSmmm6|:///ѭ[={}.Zr l6[VVvϸ8[;=zc=zTPP+P_?,\҂F[~^~jEEEEܹ3a6Ļ\Uxb626xѪU֯[O= L&ڸOayΝ[juǎϘn``@цڴi睊uTTT yyy#8 E+,,߇M~ܸ~bEjb˷'-)JMM-pZee^rx_7n~JA~xԝVŤ.]LLLl6!DIIiٲe5|h nnn /:xweeORhtBZ3g&MII@[[R^^.3H~*++3߿999-o"E2UUUq5f  O8i`O177٫ ߿_^^^ZZڶ H$vڋ?_o7oLL*ٿ?ŋiL&S^^ٳgHUUUE# ?,QO<D[o\433oGH$zQkH]eᢅ>6ٸi!B8(Q:y]e˖V5dffWVVZYYىa5Խ{w*7?/Аׇ<#\QQkRLydۓ.%-_Nwi%^AgXjUaa_Wjii5!.Z!۽nXߧw@}LڣҖD>/R˷b$$캳^6$GAЩS Zo$)Jp攗UT>1:uq\  K0 EE"m{ RHQ @J]| @J!EB)RJOwGrrTr}}Yrr8IRTxDOfW}ށQ$x\[ʑ~YY7NwQ-\"$k5?~z㍇YzsZl/]g^YYIZdH{gω.ϚJy|59\bޢ%~+ޝu欬,U^SSs^,mϚG=%=QxD$նN"#KPHip {Mn^'9_tJ['vΫ֬?ɼuxog-)kab/ļno{.'{ܼuBtl`q ~\6aO~홢KM%\~c0S*E=y)WcV,]~¢"BHo^z}ؑ>ևr <"%%5|Ę!FsWUU 12LII#ݹsOQQDŽ)Q/jjhdQG9;Rً_ٝΟ9uXiY٪5!|%?L3+8(:6ANqu:sdq~\BMM5!ްy' Οs7Ç 3z/%2fL/!pP߾}Gt%w7͖ ,$##3qxPx^#)555 v&!^JPc#@H8ىzWSS@lBx93eeef^NZ^.q?=kYhz}z4oEx>;#3kTaiav9' )LGVg^n߹Jj@UUU-Ô#jTag:yjh4uU¢={t1kMM fr͚!(BH+Oξ|;}}.ܼ 1]ԅBaqII/!DMퟮ~Z~~>!dxo['W['׋I+*>|. V -[21aTp;wS32ϝq<Ȳ Jpr9圜<]BH$SWS#ݿvF ݻk]rUMUKbGXE]z8n{TTrAǎ GUU*^eeq̉o:/oD;9S'}> ILR%fL._aݵZX١#8xX\pP_X[[yŒ0!dab%lϞ=tɐ!ᇏ 5nxt̹W^3Z[W~߇VUUUQQs^[둲L!xy5HĤK-쿖Vz[BBH$rrrs?q^߈v4ȰN(πVPPQS45z|n]QS} 5=%--dY&bl8/-͆SafL멩r>iU%%%cOTp/Kr;~gaqKȤ ieen-y|#hE%h*" @~~^ \.wgu]2>(%5$wQ\Zުْ( >Hu55;+6-A+$E|ʚVAAfc4H@A p8 o@J!EB)R ) RH)(RHQ $tV8F鳒f#{u0Um8S8@$IQsnZqQKf^+cz&Ƀg%u:'B LV'}T~"_L /4ŤQbo(q ,Gѽ+W#aj+.u!ݩviS7P(~@{j}L&C.X\Cˏb8hvcٞcvy>[WG/\asZzDB..)by1Btv=~TUm7ZIŇnzS3QVV=O&-)J =qFZ[7!lNno@Pw--]ݺ::E[\IDAT7@'N7h`F+1j۾^>vbm lfа=#-mg e2߽̚6՟`hjhXZ]N`04t9qꏜ\%GSC^~g_^.##h%KW}'ylYY_t.*8hSgΞ<}sgBH~~>!dxo*vN?g#Oڿ9TU멭]r)F+!jTag,Z ?EQF98u?{QQVf0ϜhfϜ>-0o忬ڳk{_WRqwvDYY)?@WGRV^. ` ڳ`QqVz[BBH$rrrskjj22BvwHYF]XtJ!Vf^V^^SSs}bҒ!Lq겲rg7 ky ]Y;wvO\;xCi..I4Z !dz.nc=G߭[W-nqݭ?m6RfI |$IQ:<={`0$|$y ؒˍ $hqj65|M{%f]hhbb MCvj^4h}ڽz;] :ퟢGZ'\v_~߉=~25h[''"(dǮ%?"ؾs% !Y&Z8s7ÇUk[;8>u欬;7 ퟢ454zqr2{6(gGPH햔Y;4|t3g^AD]sܥAS.^VXTD mWO;rl3Vi% G9;Qjjjb]D"755ޞGSWϤɄԴZWEz!d ˽s!ҕdIl6[VV6or(VmrTUŻjBBCl\C\B-/x򕭓+YUU].BBuu5 V_!)bYpX?(+3FmǮl^ҥ+IQQQWooXhʕ eG ԙs^f2~ٰl^NL\P(|y섋!ݩa&[BBك|ںϞPI0;t򚚚SHKꫯutk0Umө3glfY>ʅJQGkם]e%А/Čuwٴu{MM5!dz.nc=G߭[W-n_@2A4TdRdu?/_V^;QSS .a|MNT@(U.{-LKOWWWolIIKxl͖IXrreeef1$ sp8N@ 7 @J!EB)R ) RH)(R|[]]~8l*͒I#"uuu&xz4;?<8ů͒Ak4x`K/76_/++I.Uk4;liJm{MO՚n SF{RT5w0Ka#흧ϜՆhKpz/I|e!⒫naC.-Ӛ k~ZreEHԦo 6<Ԫ`o]@ (RHQ @J!EB)R ) "/߁wO!ϗ'??%~t?b۶*>zR::HR$ ?yV\۸r:۪6>y2'OmH]%@6,s;:8qdh4[( vp5>LwODQvӋ$ILLN Fe*+*^ 'fUc2 ;:Q2|~'v{uJy1?zvXʰXJ_XC:ɟٿTsp>"( ܻ x`0Ν{~lI\{x^h4y x){x1v{L Y6wNytf3,8v $S),R Fe jQ:-hz:HwO/pZ^FQ,x]v:^  315EBqq{p{@\tGq9ۍGFǐey@ή}YRdYfllڵԖ@ WWGgOs]NYzGFޛmFEOpQ_{x$I"͢P(ΕRֿh,J6RZ|0F`4پp$r hi޻ $Gncc?z_TVV_7nަ4*ANh4Ӣ`ӧ_xGJ|h4&&'f|~j}.w3:sjw|0D"Q3e(=}m6/lݻ{>B6ׇjExSS 34mڍ ٱQvʩQZ"Iwuy|~?xFC] ?jEӞFRp4?0@J2\vFFGAp8LMv{e֖WS Y P*IP[7oķTZRB6!1 =s7`5>zD"V%07G*b>n+GRHlU*lܯ@ r Ѐv._ƩtuPRZB:v#)$Nl90ӊb/=_iI XVb="s}mtvu?yJ[k ](JK VUh$8t]ݔP(x ,YMuv:Nуe lt:Z׸e_H0brjJ45#c>tOΝZ˓S ?H4Jii JGB&]B pܜj?Ayl6K6E$ L&@9ei$R _#3rc)-գ#cTowc| Nfff9~>Q(Xh\u="ȳIWq8wT8Vb  SRR  ?x'pK*.]V%ɢVEOos>Ng>α`sp\cC=]tQUU}h4~z*xOi ͘L& a,_ӑQo'ITUV03"b4#CcO18B0X |8~Z*+\~{m[ٵs[ITUVP^nETo'+gz6 ԈɯXJN(^ jũSN;JeeCXʰЛP(^W*H 1`籕B0f J? F#VknGK`1530|S8"^fyroCRXt$)ۏs+@@ LT_ڍŎMwɥ2%%(c\rS'(@ Ȳ̋46Z2Po 6E/l t:lTjfHF f@l*`xBG2W'Q@^H51 m@ |P6,F2G3QS0(FE'ynݾL0vuu,{ǹvfn*u > Dko2;bff/]'/tK뛙^}hjj9/Q`#b0͒JQ)/go~K3dj F}ܺ}fԚܹmBDZlNa.xilڵ8vB0mdo.]J:\~ ee%Z[co^R`Oc==4 ˜8v.RXhS#\.7L]VYYp]T8OP};ߞ?'^BLREqq=}$IBpɤ (ZQ9_kʭbFÛ&ܑҩ4*yg65l>r6FYy8%\,Q_9޷NM&JJJj4$SI@B$ b&&& F6 ZͬE[k /nc]a %%% =`V::46͎jb:ބ|?%$b)bsg*%%OLr ~Ao vGRrgObh$=srEh4?[PYYOJoo?N@sgNFw%ϋ;wQT\ի)/7nIg{$=$=JKJik oNe9V&D %0V@le65 ='PR_5h@Z]' WS\FO_?MM4q^T= IǏ[h|}v[9h BɃ8~(_;4 *I R)<ӧ.MpRVVe L,"&z;/_c6*++r|n N[k3nV^ӧjͧ~f].vTR|gϞzގ&;p8LcC=?^B8~Yⴶ6s `crq *r1o|;:\`X,Fh0RUUz{D"Go2.ldd2؉,puѷ[ Y_kuN$JJKP(rcGWfNl"͠P*I$t:חJ@\>ώD"*+G"xT8Ȳ!>sFjdd\gw;UU|٧Oϝ/[~e ><̬ |YÇ揿a-^~C`ـb@>rDV.^BEݻvQY [y{j혞ad{:5er(M#L&f[e%=޲}Ոb|4SSSҼ+~wkϖ\%Ɔ%Т۲K/@@'H.s m 6 'OH$p{D&zii޻MYQyؾ4?@0$p}ʶ坿ήnoƭwh48T*%c:Djʵ9|T2 &;w$Gs}?j-{ .՜KdeRW&+F|~?O0r~Cf"zQ*l?AC}=N?GxϽNT2 Jz|0ȍ[1 ;zY޾TJ%36ﵑ$ ^ٜǑ45hFC$Y_Ckh49R6 p˵qrg..[*+"|>/u Yd2ȲL6%άXw:}+~eߋ<9}Ǐa+T*ƭݳ'OjԄVjM' ,eFN 9V1 4סP6m6l6+e 7nfr`-Qi1`mHĭ;w6JՆvgOc#\lV kej :Z^gKd+:Um% fg'p0==Tl|n~xz=*`0RC<@PD"z]>`4xDTj5lSpX Ϳ-d 'SSRiL 0}IgH&$I24SJNnT*J%Kب3W@y!dׇ¼[WEDRP(lf>$-tp8LQa!>??#I:ɩ)J%H„JѨ)jIRISp8B$ɠip{?oK ޮu|* ͹(4KC"qmmVl<ﲅsL&2 h6G!j1T|zÁشfA'5KcC(N˘Yw.]"fyss\fb5_ ^,S3Yffg^}3\Y181>]; C#B X4l2133KYY +K_( {p܄a m̺p0=3jEO饨烌r!gf`|bmU|Ïq-_;饲wO_]`0=3P=Ӹf5/]ı#EHF#Vk^I4 ?x ~/vN.\`bX`d~~T2d"ɢP(0L$I a{bFe1[&!GKG't: ~3 .Qos?{hcGt:sg0L|' p8ltZ|>ߪy/UFF!07OWw/JY|^la?X"1Kji|hr zZ[ fVcfv6/x dp{+F++*eۃy,R)AdZBl6Qt255MdA@TPagzz?OIwřd2W-~Gs2 7ox9z0oDTP_WGu8t`?#c`oS#wݧv7X L0dhΞ&Lr~'v$S[x,aWz_.sw( *+Hn[y s#Jϧ96Ftd$2OBa'rf_[+/^Vp](Tf޾~6EF޽| {u26>JTV8{g~vmM5gj(**B$sӼjE反FÊt3κxQ IDAT$م Nncph=)i s|Ï@n=np\x^ (((v.;&S ɹ3ܾ{p(L]TWo{tZ.DcQ?B "!QWwQm|>>iDC}ݪ_|_<޾ CNO|ML<}+*/q`۰m߭ǎۺz^|rF٬#|EΟRJM{~Zbb1*N~̺$ɷZ-xXJ-|ϣT*9v0T)GՒH$PYu쨩LhERRRZl"Řnq} 9\xa DcC=Ajjew/T)\K%؀46oЛEBKeK>Oon>S'g I/@g_[ ,C[k 'xo珗,TUVVj'_@ XsˆX=;szŮZM >.$I;U6*c߸E,`3 9VMq֝M%:Lr 8~+W3=3q^ SȲcff +V-?5-d`X!Ӌ! a4ikiffv}wKi)N3Y;w/>#L1==Co_?Tm۪C5kEWO/ܺ}/?)**dnnܺ}= tӇ|z /_%sQlO;:7oQtizR.^ʉGż\۝s20e</Sh5~"WȷK-[.Jf|l +ś;0sy9:_H$N邍ґї[p$i|~?;lɸ q:u:|~y^.תi' >CR.s׊X,FQ3mUo䟲}[J"b{#jHӧxt~|>?Ņm?'W"I`kmW->Y3R}Bfk|]ko1n.!DH1T1X>}jz陼3fx}>H9uIbxP(X,.x?x}/X,珳>`zYz&JR)\nw>O`nnYx<^yQh6_jzUNӉaa.#IUӞ>Ae\]cLrAT*Vkٚk[+W`j'`GM W`_pmoصi"U8v>z吝NV澒潜#pBZZRR\ām8vo͛q8hZ<|}EEs2|.]S'dbbG݃RR`bbZCTɤٹsChٻ|i]dnn G}  ۯ~%H@h"E6EfeB*;wONM#fBAd@p:9m‘0 !-IFhee DӱF355   \W2Dp$Bm*hD"bN)*4 D uqe$KiG/O/B7=YV>SO:n^Go`a4ıd}rƭqm.,Ώ9aS:/v\8LZ"(22}h4dYlךp=>F$I8:x9 ~p$lʗYn4).*" /K=YY^/O.ȸddL&rr?:d2E&FTb4().&OPXXH<GTL0M(eub1J%z>)>NT*զrךmle& Wٶm7o#j5m!ɔh4>ݎwS(eZVT11Ժg!J%w$ jDc1lDQT*%drYtIbb\(X;̦@LxFZ-Hb :I[~|;jjH&Db@ x3 fEw𡃯̩CLʏ&‘eEh\^ӡ(\rRV\LO)X[$ DqqXenx.~+[:!IR~SQyy0gۄznmCUOMOt8FR-˳&DQ::%L2:6r:L>9j~tӑQjwz#6"3<~Fd(Uʏr3D,޽vAe;>d*$ImP\+ʿͽ%`ˠR()ɭz)ۥu6/t^"ڲY{, NU,||٧$S)d`R420l6?j~GNKWΞ>ŏ/03#Gt:]I.]J#<|LoS+rt7o177ϕϔ,7n&eK\vcAngYWnwWw+vvp\.!U1WNݱjB"tdl67=pdjfGRZ8~Çд!kT߄3ڵ۷h|B|`OΞhxL?T p8(3C(f[UN'|>ZZ667sB3͔e`s];:݂ ̒ - G;r8/4`ZqݤM5ڝ_-H;wPh63 RW ?^6߼VzcToNo?S3pQG4SZRB |~ l6H$Yx -{266oRZ餭ήn7޵ o~;~g#B^t:|0_p=+6=>0^b'f`Md`6+hLFs7vVũ ??VUOcX,ȲL].jwՍ㥸'v7,1jg'(/+R+737v\C!{צ^VOp=ˢcZJKiz)y?@k^\N}]-N+W*dYnݾC}}]~[}b1dGM522NG*BR?}ͿK{b_I{VUŝ;B?355P8LӞȥ+h?~~(c)2Tf|?_pm/ ) gǏ™e6in133Kqq.rcbbRnν{D"vAqQQnja_uzydY&J.[h4e2;Caa!_|)C*N z=@`<^Y{EazfZ4 AN쬩v{(++쓳{8 a2088N̩mUUnZ[z6=~Cx)^o餪)N':GO G7= T* f2,JEY\TSSMwO/}rKR_WK 00۷QZZJ" 8 >v[R?dYtBzj紐}?#imݵs{x=|zsgn`d`&F28PJ ~`(/STTDQa!zuzv.tvu3::Ə.SWL6VcGjJ%.7ߟ1>9wn~os~t:P{.ǽ2=3rvI_::پ55?z`00 u<}:“# ?xj:t|~z<} }|w\zO[k, [?EVc6r**~n}H]Hc d2ϢB!B0S;sIz~IaƆz7_3;"J2?DTt:s>PqT*uoh4P;p,V)).fbrVK*&12:Fo_??:yCII1SAwwV~?܊BA,K%ݽwSAO9|jݻ(.*y/^R)61KjlDl6K,˿߈6:mv ^- E*T?0wwT?=Fެ04_S=W^d5J|H ~LR>ZM~KbڏWF˗Ÿ{#'#CӁ#JP(k_Ԯɩ)*bțDZƗ;M{iZmN^%L s`>/ȷ B߷~ƿ!4rU$*qtOϝYoS?(]dY?vd{s;=LNMd`~ ~qm;/_20[5^*"J4˿VFf2C>NqqpY߿MXrk{4 ٨jeXoޅO`fT ۼ7Fr~Z-mm-7 20Ej008 20[?:We‘tnJbbݺ}?\`dtadY&#Hs# n޺M6% f?{`庻{Gn=qϿy&Hy&ޗIul2_jzU֖f߼Maa!:Nfې;IM:8T*ZAGq@f3H&)((n5ܼ} ٗ/ ?.okp:Ts]Nzڌ`aQ\R^f3+d~>zƩoɩ)|9~pŽTUU1>>!r'&q: ?l6Gz]Jk?l6|~O,áF1LB!vKI ?s":I- G"zҩ4lBAOo?Z).)&Hj$x Vݻ BaJjܹw;wb=7&Hb2P4 +475QZZBViDB$Hc ?DRc4P(zjKWVk(0޾-G0zl6bF#D.&`=/`x FpOϝ]:}XO⦭lD˭|'x<^>O[lbdYȡt:Ξ9M#+Fgfhjj$H$RUUIAAOGFMFќRZH$՝;hӁ ݆fcjjAp{ рV%ΠhH$h4g@d#cQuz,R'i9LgW7h:-pVBRdH||\v 'faBUzgffVUlۜ8vtM֯mf94j5Pf9zcmjkLII1&#sA乯!,< &&&p:9wߟ@MR#CRITJW\CFf[U]ݽLLN_g]@ غY^X"؈nmng? 46Գiwd=N?|ivssl?AivA_= il$ɠjl2t:AkhmUhH&$IZ-dJE<ȗkjlv733$ F#d A8I*asG Z%O?J$D#LLNP*ѨTUTѨ/Ki?q ˞<c3O ZF>qޛǕfw~DbKlI,$} $,tKjc˚ 8~c3;XXj%U]b7 wHDf" @VE /"7{]dB6rrr=&SBp'8VNǏxܫ91R(N0v>(^~ۮaN.ƎAoYiɮ'IKK#--TK^n.Ɔz EjxK:}{TkQѠm_ #/E*`Cx:X__Na&3#Z͂Bmu4_h"; u67sDc}=  ]n櫯oDB.Cۨ( W`S_\u IDAT_KFz:e;vkcLLNP(8TcY\vq n~s JEYY)KK|xZ*f(riJ jb,-? % Q__ֶv]o T*Sí OzHt5N(B,y*<EoDưg>»T߸\}i2iz}j{K49Ѩxgu2S~8"!8?w&[=nFzzI^eG5l-WFGPPvpwRz>5C`xVfk8Nt:-Ռ23i~z:z{Wu. >l*`~©z<&ǘM4νVd2)ˬf',//c̡ɫ0:>Sf~ 8HxGahӍ wva69d`sb720 ~B&+3UK faOǩ{ NUy7z|RX$S9^V.\SjG$aI720b1p:B!VVZ .ѱ Bv=u _榛X4(h'XZ^&{ӍuŦ&666^]Ikaʤ CضR 8/Q((rjgP)lll`]|)>;;k=+B[Hví-ddd 9DQ23(..& B8B NTP)4#0cO8e'qn :\F}]\N$ӛ\NH$TJT*yy8Ng8w `P(FA,c2 Ҋ!f}IVwPT;TB!G|<-1 Pcv65UutQrMYX_ZnݹCsRIYi)  RLXk k{pG(vdeG(v}IH$2˟[Fɲׄ3?'²l*/~_"F$bjz\OǺb{Q)/VÙS)fSAn p8y8yyo<,&XxR ߕu^ <>1n?K;JZ 3s HO701WF xܨkS}q&D"aΝHUehe >'++FM}]-_n̦B0SA>-/O2[t\r 'vR)>O1 Bd34x݊x}|OPcͳ0w`gbq[ܽNFxh5Mj MODUw= &XZ7e2*U'YMőLb6ШGrVV v,? <˻|>66x)}`&cZP(ZScbx#vxx‘IH`0ק \fŗ7˯p:]C#ןa7`4f#ɹrXgsoal|_nן&Q<}9v9۶K*|l]]?D1 O{cՍT@$dqG()>Jgw/u5ML p88vu5tuRR|t4rm8\l1pn ӳb2V(.@m탢B"ubf TOkfFV+z4.#f2c4rmRZRcG~=2;Fk{XQZ:R"/C2uDhQwl|.8Xiim?0HKk;~9mGKKk; E,b15xjֱFώ%3O Ξ>$P'1cjzm?OWWO<റvӱjqm_FQ y|͗BB!22ⱒ'm8Yq)cǶmua絓c4kg/>XP(44ԥfk޻ܜzlec'2RO|0įGTbY\Y>g!q8uZj ID"Pq@ @w}HXDab1&?/-|on`4f?8șS/m!=Jo_?}p=}cD<]rTcsqhiW:ZT(NjO_LK*p !ꪕ'qo1`᧿r1xQ?;vj4ZM8tz:VjcGS?h&)!~ٳeLBƵ555q.p:&iOK;]b` {#nYj]R')4+\ I\NC׋!= y84ng|KB}ZFR166A(nHM;{Dz DRTkN_8aA&P(Dx<- ii&Nb|#Kf u S]9͎dlxi  /2=3-6)yk<7h/P8B8yF$Bx<[-hL& [A,.R1(--h,JuU%</u5wtRR\L(E]h.ҙz}Zb)y;Z9q4_k=+޿p8al~ .^8G82Fƹt K}m-흝٥~n SP)`A&Y{*='8A&' ?̨ps`m?D ( RUY%4v.ZM,Cz<^JrYWB_ ̳ ~J0d|bLZfffgNpWX~,h56 ɠN*+K{MgwbU4 ޻m0>1sR~?:;Z?-: YXN:&E_ 2oښ>CT*=T202@&qG(++%#DHOg=P fˤRWVhk@Pp zܺ}@0b~v5LNMxĀZ㥣xZI|p7ͩz`pp}I J@gnQH:uáa>>]hjĝqBɸ.;3Hrxx'I*TJeJCSfN= sR|Oj_}Fc}='Odyp(̪Պ\!' 1;(uF+LNOsvX㡶NGWOo"FcqR/&77/ntum.2331 E(.>֝ bY\B&QqT|y&6-m[3""+ʗ7nցSyc,Xچj]^Kťmv=/ZXX`X,_t 3 Z-%;T2RKiʧ>L&O?}m?x.R]}nk1nU+~ܜmz^ޠw**}k˲$g[yD"QjyQ6P #󹸴sX,KD8@GW7 bYLNM337 ;+u Á^)‘aoM$A,sHOπolť%$b1MM bىEf45P,! rUZ jG&_|/)ØmfO=Y~hnMFK[;.^[qmnMLh?p$BVf& H,j]ú&#L8#o# b۱Yǵߚ#y ŏwhdj`H򥋌Op8x8 svC#8lLzzB6f*u7M܎R;\.Pjf(i͆um\jk69 20rD"/`][cÔJٌ\.yNjp8w#zz~%S闕U.7_ h^  oN0dqq} щgjzq|^K_ ?@wO/kkv: B[7F ÄT쟀Fզ2zz)-.&3#^jkE\DAA>p:D"M&jf)^RɄX,HT:ƛֶv\'-MO]m ZZIKzth4Bٜj_,R" RTh~ JKacEV ;#EAOP_;|0>RW[C8AA}m-::_} -}}/X0 ! D|nDQ<^oJ 2W \'k6r*e2 lGO3Ӂ#պdbA*b4fp\0CJ<~<Ϧ ]x~,"n\boJi,$3^$26>3I ŷ&1ph' -Kη_o{x"?Tg9HR8ߎ -(JJˎA띛1:]ܱk/4;KNcsM>-)uعr)tZu<_`H{WD"dgenH$fa*(8t^ge`j]#?/\Ɔ:,E> 2xTUV C"PSU sX\\Tc=]=)ɘMyxӧ׏kM$^]cTUVpϏL.y'hcxt|~'.tcc\ Dӹ7 |mt:-W.5s7(zp8|"q˗.CnNHcG{޿JGW 8߂X,F,SQQAYY2,LB8Yj ";+ퟰѮ8Y]ID'O&<=V*8N.6NG4addh4JaHNd9wT*ynmA1ѱ |>OP4_8Zfպl2qB3d5_sihC'x^GwJK!b|bK/"tᑱ>FD|EK&miuXL&c|b`0$OLw~9,wD$]7Iܭ맴C ??oz1̉$ċպ(J&!pHJYX(>v$4Y_'fSt:-eeM&2)JN>wFIKSS]E{G >.6K-sGiIq*'U(kiGMA~>OVVP*i袮杽_ ;d` oKf|| uh4FP(yABu|YI6Ο=V6@ |^F楓@,>?k ~A}/} !pv{ ˎ~ЂGqOEXp:!xf,bQff(gJ-v9u \~yG_ ˹׏Z!;+ٹG~:9vݽ̢ςe͎^CTd1S }:Kk{/p35ҊG.ō"JX9sqZu#Rϥit^՞vwXn}tett;:ן_e ::J7n20'O7]A-b*#  095çdeeCRRUYAQa!ZӉאxs;p[ɿ[ m .=u#E"2 kkvc !Zj5;C  ?:12YZ|{Idb IDATdge|'SXDj`!??/{ZXhyjS2PXh&`81G;.4'Ru8^Fټ|>VLjr~1t;sBIi1,KdTZw'8Af N3&FFQ#R_uSν L)8#񲐆g~2119ƆsgOx .N| vK.''q{(**|LmD"H$|>Bd2$ PH$BN{$! "Jw${G** ȉeoL#@"Jӹj(r@ __ Q*8NHOOgeuUb&nP*FTJzHĆI1É#`fgNgffpRol\.YYYE&fHön#33CLVW%b</9FmvT*%1`þAZצCH$VvtP)U>TJ% ǎaպjD,f|E݇&WCHyL J1xHH|(Jz=rA,@RRPOFz:Mٸ\.4l6%~?vFT ӵI^^.yy\<*tN6C6 K01%|B0ldR)Nܜ"1M7`K!u E,t Y_z<^,¬!ffh{g~aIi/X},m\Ah4rݒ!qM۝Pi|<;,B!=S~!)x7x @|7D"n7@`G: DH$H$d2)*`P*P*U45nkS [1h4jl6;XuJvvVU.# Ш,X BYZZB&VQ)hVW(UJ"KȤR\.7LF$ATlo CZx%p\f`:y(qZ~4_lbnXJik_?ks1=3gS >$/7p8Ϟ对x<3HR*c~B ѱq::)*2cpt,-=I_}FVV&UMLX_ON&bbr]ăJ #TDi^ozq6ް~v_b 9'UC}xB$d? tv$^R*o^7f3e%(TUV0==CnNn9JsXNdggO%c!ÏS/6gNSThfxtB32lq!ST*{v=;zs4=Z8v2,3s4_egevS oS ܽ׊z[,XI7pmnR_Ww %]=T,gxt=BMC˯n{@ Oy#77BQTutRy$RVV#Gʉ g\ Rv"X,HΛ dbr 3C굊D"ݞDƳ<-n #cwqhd`9B[{'?3O x<j顺X4L&COFrJ$E)r9x)--ꕫs$wмi'rPD"yHgyL{^/;SY+*{1D5uGdW@pȤRΞnܖGIEiu)+g.*ɺx@ސβ1u.19ըx<rFb. LF0Bx{xu Cqw-u^ED| gƉe'ΪXT*err`0" phm(yy{k+#8 u(;NYImbq|N$a:Z!32AhJ3) ]<k[pաK %Ɨҳgŗ.}g>hDp/^MBz'-!peB!D"EfngP!Vvsrr߫ iOEp<xMk+zH|8ngj{T`+miecr~^ܺs7~GW7V} >8xIv8Ř "qM677zg|q&XZ9Y~NǗ_ݤy ] % 70ht>3sbT6/nO=̎OޯlLJ`зumicqii>8 b~~^G0 /F HRBxY4%vIӧ QUD"qGOEc8+U.V%===5c̦[_#8GD\_\葢Tf0R'ZXLYi MWE H=O*Y äH%Rzve`gfB237ʲJY~,KH%"C_ן}AVV&>Aʥf eaa.W\7_~Zc6HcǸ}޿{m+% wM؍ܧiemU mMدdddv1x^VWWSNER,(7++SȈ':ֶD\h4 l?u`x}'~?Hx[ExjJ^k\3l< T*W\&=* ̶vG@@`"˼Npd`e形(SSSD"Z-fHtn^ #p?!Mb~yecc.FYY]NȪz3<{].+D"o3ciys%* .ywɿW 7zm%~4nkB;yDAVV$lZO~zD"233y<"bo::{I<?JbxtLs|>? V+~ѱq"06)&~u7xX$F*ՍB!g ͎zxB.ɉewvۮۈDZUz}lׅf^R@0xcK/<;78^VʪLTyKK8N|J^{>s<fzfܜ٪ՊvK [ڤX4* ;>V @Kk;vI< <byVV&YG"m|T%R<(-)& ~4Mp;3;F=~kłe պFBkkRWS~*+1u.PƸ(f^G&י>zܣln,.Qy$}ȤR tG"2t9LMioge`Y_X]Z~gu'O(ɐs;!5 >xȩ .55=X$+lnn8C#|U!ɨdP|'YcÁVO~ cΧ20Xh4 i,/?aeeB7n2~wOn}ma_X__' G6bkkqR97vq\D"VVW#v BTVDPQdzI=R* ^ǪՊL&G$b" e% 򫛈1]= bV+*P(D(NOՒm̅a*7q:];XVtZ-9F#G&ݐT*׷PT=" R@YچBaN(roH$z$Οe}ں MLMϐf}%DT&%M|OrX][#Hkk P]YH$F$!=|>?9J4o@բQtmn\]nN3q]T@'1I4efvѱq 9pZCAA>^\DwO/34)-)7_~E,G8w Y{awFrۏ?|unn.g~j䶭zZ'xk]vɒ82{MeI*N36>Vǿm~o޳Ͽݜ8Q@ Mrm3Irph: *+fS_viҒb_.?G,K%d%j'gAb1EE|>V}$9ȽvZMQ(;eq \C YZHww/|}ma {-T}A~>zNj$`F(4YYY ''p8l_#gOo`|^ # mb1!^Ifff!kPDx*qH-?ɩiN'gN5x|/ƆzT*@NO\,JK8646Z-"z)+-|p|>߶x 4AF@ (.5#H|p-~dK+^<]1ŧJHe%),4J"oeՃ8* ՊayyO>vz},Ǐ9rjZm% wKCrkeff h4^n!Md2)3ssY_`YѣQPݾј- a&{R ++>Nn(1HtXH7chdJV Zf~j t:-lבH$df9"> @'+~N/A@a::)?qܜiqWO/Jc >QKTUU(%042^%#3#T&ebb M^*m3;똘rTîB\.PThF,s'++! 9yήn]c`{ǑdyȄք$,jk{loݮ~9[1wc#v̴ꮪ.AYU (Zk Rȸ@P$,D{Fz Y3XXp8 ô>@aY"Hp͎C@U"惏>fD"1Mܹw.aj IDAT]a$3keWNk坷P#`U;o|~ _{-|^sf *il+,PD}R3둇y͆$1uaަ ';wl#a0qf8|6M:Ye2Wed4^O`phXO]S'(u;w~Ν9E"!vSJJLOزBJXzjkjꜥ20Uȹ3顤P(EF2i ukmQ(*,@QTcǎLMOL"' Y|jjz}8ٴ=lgP{[9uS}VG?1Eokm =\8OӾ\=l#WD6Nli3EXRPPH(ț (zI<b fcB^OiI >A-cq8HA b2Dk2^[+$Css/,cnn iu߸>rK0<2qQiچ_ly<=^k2x|~ H$4lf DBˣ>ǃC+|,[ HRNB mfgbr|lV+nF l6aF8QR0v^L$d2`` tOJ8V  s]GF8дۃf#'Ȩm? vͨvw219Ç}.eeq>v=|/35¼Fߏ{\wul |흷57#p[ZTuh\3K cCrBDQ`n~ >5NߪXOQQ!zܜR ,;%b6U=,;E8Ό&‚|- EEF ~!$ B'2$zZ/dH.< ,3=<#cSˬ[Y~2_086G H"!Y{A}|~zEE~޸pp8Yfbrh4>*ry8<Ўkb,?gWN meim{CCmvZnkiߞvw1<<=lX1a[35 ?@346t(|RәȺ  ~:X,~"(F};o6e|<8 ʝAJ ;ۑ(~ۺ5UOLP_RVZ̘&Wo(X,K[pb6sΐ_ A lzi @Qq8smU҉bMoÁb! =yJXe9#piI ,cۑe‚Ӣ()*Nmhhl6r,˽{T\L(_g~7zNwO{wQ=}Դ?@OoU[ڬZ{<ܽϖrDJ&&'Fc&5FM.?!^j5^>+A{ h}C+%] 477O^^Rk= <{ZF#F񥟻TSTƵ$H~%t:ݪ+20Ctvus~p8̖ &&'9дo( n~kO`0g^/a9 5JT399) Nz<_Iعc;YYY0fos6Ezڍ;sO>u2`pdg366K࣏~WW׮ݐƯƓzz,ݴwtF q '&FM~oxA0dbrR:enR?KbgA4嫫טdbro3^WW@ђjFp 2XlD> H⋢c48{ڀ{dDYVdB4nښj@kJ"f) <ֆ@HJ;zD"A˭~,fOo )1drRcjj˯> v ͆g~~QbHQ\\1]lp/cZ~k33cX8q(p]\F(fܳo={vs~ }im57??  5eO/*"'j[Cc#4hj(=`hxgCIq+ dOnzzEY@!evs a;o_~3;a|b:q)EԉDQJKK+|y:X ƙS'1TWW1>>`ULMO7:P(]4e20_$IIWOzBmm wާH$㡤X,N(PLf8!:(*w[3s8kUWUqښ֬d`4444瑁|HD(zbD =}:x2FfSjhhh<)x-u'P(EYGMef1]IV8XZ{Z{Z{밽[Z`Гd&Z 0 $ID1b1o}dr ו@ ٩46Ƣj^0ė2 ))j]K?l2ehhA 77@`QbdZKD"QfGF QS]%!WVjjbq77 7yC~_g``P={_^}Ѵo/~*+,ݻ(GEj$[*Ӳ zjX6.e,#@4 ݓR*T019Ł}4lAv vD~~o4ּOh`km p F=e` ;Ă s&`vtv20m΢i ׮n޺MaAoKe`VU&q]3M&~oO 1nlñ(Ԝ~\Y1X,x,[ً1EdFh4uH۾eh yl8,1*ѩ20#ǎW|Dnn n7 YFȲ n\ #VJ74H¹vQA{;۶.;?ɩM *sarHnn.gNp8Ͽd`7eWNlV~$qܽ^8c| 1CaZ(,Sˆl dff^-*W\s֕֞֞jd6("$81Qf[n HN'bNZ B$.1X,e`G8 ]]=s!jkEH*Y^p6AP$]~VrۣQo;v20qv"355M(Rd`bьlK땣URfq)F#5UO`4i{*e` 5ׁU 'qVSihhh $4Eʞy3,pp)rNU48"1Y^{WlROA#:Ʋl߶ښjB0v~N8*8p,^(%5r%J$N.mJ~^.m)++ehhX"rave׽uK& LIL,j[blVS~~֞֞:lHfVyYddHRjf 54 ]ymYfn ?x0y'{wss  H$QRm߾!vս:Y(((Hn2Y6ANb.u:Yb рͺ;l &&&&y۶mAdBG˨'IҚG ?Zf]@yozWe'u ?@__?l v{st:j aUGgOc,))&QZRL0`7$dϯn~춍>/5v{D~U0//3AO? +)L&2 ѕ\ٲ)7e``O>D~~>gNf-~>O`hx‚|FF0LADQԽu[-3qyck;QZRBgW7Ȁu;w!q~+X^ttu$ ֐MooR ˫rMΞ>']Td`dldge1:6+]fx:oh20aN>Ž]f/l6*+PQ^J˗LMN`2)M0>>>RW(R444Y+ߩH$J8! c,Fbq0흈Q G$Hx^cG>$77GuZ[  plV+[p,/]V.-{Y1%_ * "sU&/7gNe\Nj[`>u;wbcGs}z=Ed`6.+eB2#~D*u+D).0 8Dӕ(j,˪(?, ȲқÔ LaAzֶ sY:))))/+zM4fkm5Ģ1JKU}ڤJe5Օ tLӾ=|y_? ٳ:ql_{=Ss?8~ѱqv7ǘ& q :w9k׉lVΝ9h #SSSԔ.ͯ 4ʄ|I$#9m@ [BHq:Q)?7gN)r+L 'UmT"| ϞQJii _nQe`a6[Fծz%Ǚ))S%Μ:`0XŅ}7nff8/%O?ƈFE C4SJ7,'0deDqeA(jtMxi6ME||fV=Hw:% C<]i$)bHmˌFr~Ky@$\',cOuFz6R_Sl>k-<vgeh4J0ZG$g~ZA@EDQdM\+>;ǚozdWA >bjF曷V<~K2nHq+,,/|[i-Z/\|^eqZ z?0 Y| GcnUuZb7S֭ۜ=s9Kϔqee&gǟOCEmۖ8a\3k-Dp:<"[<]iH<.KLLLH$2.˨f*cH 1+tsP8̖r&ٿo/vɩi].nܦ8800 nsN?to084ӧ'q]vla2yށ$2<7o17uFN@hm{9s$C#ѩdJ=22ʎ굌z2Y&cF6tu織ZSᠧi W]-PP1ٹ9@ DZZnSQQh`nncGd)hop"L20vFGW~wMy-XC=|GGgVILf;w젣ήnwclV(]ݪhYfffV{~ﻴ=xazz{9Daj+0V#c4n 35=M0j*c,.V*ehnȑ_9v0W 9 FJ'&1;5=MgWͯ 5ׁeH]u/jaOn P]Y%,+(,(%/:kPUUQLF\ut 6񧡱qHnAPV(cceeYA< ^ q$mF(VJuL&,`XD+_o(XLFaOWGӭiVŒ\󯂬'ٶUy]420:`@H7 $z2G8,qߌQ@dQzk0D׫21tׁÇb2qE۶mn yĢ1=r` 9moǯ?X1v ;rPC308H(d`^6 ?_|e()) }]x1޾~F1 zBxV^L]>OWhx<,'b~~Ѩ˥All6;j৕6t89.HI;)c҆KɊˋhP]ZUi([~uUnGv6[Ϋpp5cwO3XVgN)20'4׊e[rGX ˅?`p8‚ Ս$Itwug\3Hq%ɸϧl355E,SۊDLOOgnOCN"ʊXLLLGxYYPD=Spq, MRZRHjWCc}˞ߺ}%yqy ÈbVpD}x?:^>7+n#(*YHT˓zA te(.*g7L̠7-ޠn:-}x|}4GIRN޾>>"Mr~$B4x5LUr20?/)+-Q}>, ,999;d`nܙ3|" -o͚ k$i1GBDŌ!/ ɽf"ecyc0Lf5G.I^_L${)iƆzo;"P[[C-\~@ FD<ZBk,'@:bɐپm+YYvzMIIȉ ^>720`i20   k&*k ϟ`N$b8X ͙& JqQX,Jh~^5z$Avt&%CjhllҒx^n ka.~_pa>?#ݳחU릮z9~ד20V7Οh4RY EUXbȆx66hY#x4#UJJc/@<6mYcYG3 (+km&*xј:WX^G`.gQE}y) =tlUS=\RfiS'2N(1wexxMZT*0TWUxp8lΟ;?)Ӑ!,r9{:%sUձi @͂3PAff.ל_jK rsxYV YV˲fyBiւX<#{e_aPǣ̓o "b0DFbM&% HdQ^f Q[lid`ΜzNz20'@I~ΫcZ)).i4`'A!F =%%%5n@~Fc(FƋ Ajk_T_x,' w(eshhlvDQ$;;W^Mӕo0d&xF H>3$)1M ",bllJܼu{UWA"f@fZoRtdj\,KX4勯Qc;{x|緾Q*˸J*Ӌnl6Qsۍ{T{)}{p~+>`(q˫TSVZrR{>eؾ-y]+MajR?wl^/###8$1*VLYX09YJKe`'W,஡:c9pe8hp8ͶGU?Ef6JܱO/~ΛF1ܼu`(~1u;wǟrIGFn1<2JܽGgg׺3>J&+΅sgO58~.Zۘ_XCWw/A{;uA֝ `4ٹ}*Dѩ΂ۍcXYL* 6~/]~߼ϯore|Ef. u;w0:6F<.qAvtb9"?ߴo}k}nq>"PXp= ywG</M5ad] !'fdd_;p{<xqb(H*T :A-;)))k;o# Lft`4](:H,#/nu?Rr!{&7'P(D-mEW؊Lw 'd&R<΃D"Jt`0 H(7Kbzyzш kVGyEH{Gjk5TVVJ,xJRQ9>1\O~ U9*Bv"# 7QXX@Ggv.Fb;fR\C355EQa!34ۣ^g6޸[[ n xeaM, ?Y33:xPH:D"8ff)_iE1;;3p@2W-+**P{<^MM(yD̿6r?R0y)ܽ{Xy Dz,,,/ 4 K$a6_Jnr}& e yaMNL j <~0" ={vX,XWfH_6ۊe]ݪΙ29Yͫ>Vӿ XԼİەTqQ_: qzNqDBVlJEjh5s Xu4e9N(BessʶGjPB**T3ȉ3+m<"\fT*,:h0O0y9Yvw^`M`" LlàOtN}X6r@07g\4`0›̰aZ(,\LTF"5hokhl* ꧲MƒXA`&,1ǻr-KĢ{@WH@f4 s _,BX̀_pcYKDA1~Zc?O^_L,— "d1ˏ|, &D$]N_Nè2`)b,ysA<6Q Pm4oB>/9Ռ" #={@Vo"1䣏?U}#gOHryHHt $ |>(d; jA,E(l+F` 2S!,1*^e+0 `|>Ʋty|\ǝV@MfПb<y" p[VrEO+(0'pC2My"&Q`Cd/sXdЯ|6f d:=v=5#pÓa\34l!++gN-?/,R.I&}ν:EeA5+,O&?7 -+x}xvFFG٬|[d2 1 hn +Ώsbs|~?03;ˮƅM% #^iF"15 j5 Bj=ɀɤ,Y*`YRwl鶱O0-7!s4d4(36"at8U#} -@\6$?݋W_Ȍ%=Һ @5޻kf*^hKaaSHD˭;p[wC}}w'//Az}T),,xD44S]]EGg;VÇc60 s!wq '&ǟM{IoO/{4ahhx<$)((`~~e&v84a٫i6VÙzfW]]&Ix͑C`0P]]("D9v`^ @.>']D}}|A޻']$K=4zA }%FRz^D,3kk8,]]v~ </hk9̭w(g:9|]=ffػxtwSؾ g36> k,:Wcccn @`0ŲʼnF;N( 7OBo_?ۖh}Bٺ }WKGW7ۈg`hr uݧ] s]wz=۶22:F,f-pey<,3lQ8q IDATz<2)Eũ 9|H}ZK?g-QV#>rF (e3AxX,77ߴ wt0;7G3?RQ~UA)H+_لb&X/$3|(Ȳ9<ɏueJ/l2i}P8L0ܺ,6qMhl~JUoۡ7s+@yY)y?{R M&Lfss8N;Aۜ?{O>@e::ڬlVGl6_}qM~޻x64ǖ? .I,b'%[V).$77k8*,kx V֒Қjdr>O2}v?6sswZf5?ev BlXǙ3PۇEJ;mcꥩ]AZmҺh9~.*$ѓ$I>iPTH>2re\(*R(3VVy--lڸ62&}SfUo?55Փw\o~&7n}k׬nw`RV&QUYAD LWܰM?QO$AqwR*)))fxxFMkk;U ikoG& Q[PSIudd_n2( &$!:.*L . q)):dk4).w\MiiO2Y:IWۗ`;02X3 1_HOKcRFe1p2 صc;J6mdθm $I,4 *JIaa!v}J!Io!˯)00##[8)zTNj&Y|6`h2'( bry\Ɛ$I?biv|2 #qӺTdrY[G nvC[$B"y.n @˖8V3h``CIq]F4&&R^GAAnz2,q*G0˴Wd]]r**"Y'O~~v+i>d`P('%ewHRFFTƥ;:bihe`:::i褿} ]񥳽<:JGG[_ѫsEەBabsRD~ 120 e`nsdv @ZM[{6108Dgg'˖"8.8(e9"w\CG?QOyrRYQNgW7Ӊ^B$f58@Yi Q\.7%E |?;Xq\~ثTaSo~g %''O Zd9.Z-fQKKKt^nhhh:v36)s` ̧y_SVVJjj w`ppgL):].J'7'>k Ghh:EvVTԩܜΟx߼AvVH1f &vd`ZZa矢YL'##)s:j1dd˧ǎ@abb嵵V}3gϱv*:::iknZ>V+[܅fsy-XVܵarz}oPYI#)c>j~_Z0Y?Y?Y?~}^jR&5蔌(8N^/>T*˧d`l6;7mJZQQP'xPT8].FE93^ΊZ|~}}l޸ݎh不OV Q]H~|,"˔)]녌 @("njݷNGzZ>.j e`^_Rf }LLNf4`6Q\\t &''}hjTWWr|r9 Օ\hn& 5i:u76QXP͜9wJJ/q wWbgb!ϟJ;71ïYQ^Gٟˢ1_*WqYS(*YMֿ2zW%ۿoW݋7I0ߋDwbB GT3r9u^b`\ +ij/Q^Q) X7y Fo ub!hdӆ:ZHOO޾~gMdddEvv+W,NʊrJK<:JQa!=RTWU}0x$@o_P\R颡 ~6T*%E ѨtvuS\\Oͯ 1 {cm(pHJld`E+*Vn$x Ս;V~FJr+ ∆bs3nB\.rXr)ɼiG^Ą3gM SGdgSl2vdf˾vM|~AdD{ 1I/|`620DA\.V F h̐>rՆZcb.GCQ~?gϝǔ ulXdžulXGaagϞHHZw9q2GnA8xG?YpR?̌}cldf֮YŦظa䧎H$BsK-Iv=$jxboZZjNjDVJHD'eEn7S0s.v^o~~$Wmcz=^`0DFF;.T*T~\pS A8u ,YRFAPP(ШՔS^^N}C#?QW0rtrY*/U&AOA@Om'W{B^/#qp^~*D6pyV\Azz:j\RDqx^.]ۗ⌁p8G|J0/UΞ;/m71a3cd??Nl?M!n}CP\F$" BD"uP!r#Nym[:rj:::MJ'52>nPTXxml6STTZ'g.^K/R+*#gmvkR^ -шZֶI|1sCOo-ܹr%x5*++=<l?2ѠHBWN7n|iZRRRhBl6앮W̴Vzܵ?=F)))ԭ[ˉZڹc .4ը9HFihrFM80 p\TƄmŋ-<,Y-wm&;;nN:EΜ=?O?pp?Ϥo.gΞM_?}@EEz/t:ayyW&)x=TP(Xb9/4Ke.6.ӹgnJKK'޹\~M#ڿX蔋# ~\n\.'Á\.#HLr2Y:ǏJ'e`B0nq *6mFMElv;. -FKk^Sf&*dg݄aGFzwvR^FlܰATĔqܘG⎵~6oncټiC\BAĀp85xI4>h(,ȏ20ˎ.1c2`q=IGZhAQ *+Hr\glDdd;1efJ u+ͯ11'd5lP磸#|UwΨ8| ?~Wt`ub^'Z0FAEy9}ŔgalٳWL}cuGˋDX/!\Y\X.#i;V/ϏKع}+gΞ#+$mw ?7t%W<ڿX2>_Éo(AOAR2:ΈRPX~1 X`TP`0Jd2aXftzvn@Fz:%Q+W,'*sǶ:۶m(/?MƸ KӛZlٵCZ޶.i9w/Tv.i_.EK~ ޹RH0''ʊ v>d|59tzN-m( oxM7ܶ`U ++ q:E?D8.;kgɯ_߽k׬f<륦{|:\&lN ˉopÏT*Rz_SS۷߽5?ycOpvn(-}OlX'2 yF>N8gL{Y2]f[n>EҨt4&4mB.4Nk8},w/)# CCøn?GM+ ,djuh4jT*%.% Hxx_G@L/hh4`02Rs:2>!EjFe1 :>VXn Ϝ;VbȈfΊ ;m[ɦ4> ))z*+;}#{S#Owq~) Oo_?˖ְdIYdLq-wm晧ܤaQ[zч Ϝe͚լXQKWW7<9ܽk']]j4h066FuUU\'2{.g<q9֯q{{,)+MhT*OM}C#}}:R=HL&c׮ \_T1q^pĄ`0`@@P(q<e] ..'Y8vnz~$W 0:]V՘2Mhl՝+)ʹ$H? Md2iݵdXb7 ,S˅e|KkI8%VsJ\^<-ZxF:\n*vʊuѿo8s2\~N:7ERtqLwD)+-t2.4VǾ'簢2Be(ҪJz{ط?j5դ_ާ5FaktPiSqFQb6._TꜽS4QB!0LLL OWď,Xw#ԱQQΟȞ;hl<%-G;:fT%:qΜ9G~~* '\]{% 0juCB"y{x*&&ćL&#;;۷ׇ:A8h0PivIOo/)z=O>Mg`_fە+SơR| Fa5KJD͛tpQbۿ7nȧOn&.W V'g-|ɧer{k׬n70[IwG8=tu44LL*TUUtSf&6.>If(c~ݚn5t=v! e`t:.]bd`zy駤{D}= ۜF055Q4FA"}  fFNj ]]2N !բ `ʘdC#z;5tv$dZ] d"~~~|\)2~-J}8QHMME&c6QUR_Nɔf#e2%";; M&"W\f 唗[ﰱn=-244R -56m R]^'t.efVXξx\Foov@ \.'55 t: |y-VU *%u$PF$7dΧrۄCϟJՕ$>ګx2**2Po^36&clLjcQQNֿuuSF{~Gۿ".7ptO,8PfEH}8Q"RH$ "?AÌ[Q(h>_pꧣcg$Hޛل.-s 222f'mIUe%cfܭb||aj*!Q/Lב3k$eR e;>?{d%7]#ɍs_pgw#Jżi#o {݋7'mޞ qt`08Q#OQn/_W{E],֟MNБ5S:Cj3'ć$\\颧B1y5](nllgϑJ޾~d`>P&^NTVoMeG=.扁mUJ;#HoߠvY ͎dV( 29cc4Vi:u ::8Q2g֋4|{&G.5 ىdYY%L\Ǐikq8t\&>^GpݨT*Le20e|\ <}c|-7X[ rIZc'NhɡP(Z&77޾~cD";ش!=v+ާ3ڵkc$GOo޽CG"CFNN69Y_`0dگ~Caa99=n*5Zq~S20ae`Zcd`zX'iͷ٧i23o`H\ ΔZGL `ϮD"J0O摵l.ZZ[]l? Z}z7HQQ>EEZO('{&dH‡W{G']TVV}vJuu547$G oym񗡸 -3gY|wŦM0 \lnflz˵U:-)))* ̬0<2BYYɌc&;֬^|tv]nZvQjm>pIٶ."ZRIqQ!#f4 흝TWWI20O<(p "aX4)`;=j4&ض3e]ng8{V墷=wJ`cQ(,eCe2ŭom6cV࣏CkTx/8)[F im##8/ZMyzz{͉3 RSg|~FF<#Ҷ]74c^#%%`0ĥ,ٳA{hiiO>+W\/bL)W{GGbY_;3 *v o6Z KJ&S/)--7y FJp"'F#οL!))ܱ Ox~`pRF(,fnӍ2SOh9 QX+!\DNHOOOhŒib|l|MvÏ-QTrywjF#O=]8x9vΆib!:u%W!5 47 N,+_^TJ%ݗzxw|'qݤyAٷ˸kFT*PPH*c62<4c*z?ϑ edq@E# r5LOOwN[N@Rfrz}8nN7`T-w:ݨTJ\uzϝjB^7k=VNN.^lfy2@L]T*q:]\顨˅)ӈBN J~OI>k8Cё'c#h, %hjr9%E.RX#G?% YYSn-j.~o?vTWQS]xup՟~BA;K(FRJHD4D"uP!rsA'lI,1o55>[Z;C=+!N}C#EEWtvu^摇 RI +3;; ńe\w#aC}}GG9s '>?=Бq9kq̑^|}Yz=bVߵ` dkj:M[{<߱X>]sQN'o5͛3biz.dF9p0/\H$¥K=wĥ={;$sLT  y\ج pd*cULM2 ?`:CNnԟSRR0tttRQQL&#𓞞<ؘ%'0Iەؗ|IRfgd2 n2Z\.̣[Ct7j"AHgtl Sf&`LFGGpSF"%B%ɤu)/٪Y*P^ҥ#jZ[ʦ"mBH2>Ykp"Сi#gcʌB__?1OT~t: Óٲߩ^.]#Lܤ8r$IwfDI&[!XeB.;PX DY^>bsRT0{b~g8|H||*B\ /ob|7?xQk< _En7VVEJ̨b,q|>/HJoرQMNSTTҚjNf#)+-ԇkvzz{D",n!m rr ذ~iWmsznc9{\j5Q]=;C! %o/nzg%5-EDf |7B=ZZć_Ƿ$68Y?Y?Y_PÉ\`0 ɱX ClbJTQ'3ӈN JٍZ5 &s$jﻛƦӒ hC#|>NthԸp( </Ku^6cb~@0e`Nd`Z ;ocC!zY^޾9kv!Sg0dd `C4>Mݺulh$#WNr620:t1fsHHx<\D0tu~s{>*̇_~~>WK0_J !flv^JB0~.@$wJO?'ǎrrټ~P~RSߤ@ @uU%}}W}n55(J>\.۶i@e `0V჏>aݚ՜pCgg ΣQ"@AI&JII1l[OjJ*mm's[/Xd60bu fz"{z.aLkv{ q9p^F x_~yq:I#¬esU_~?NeD3MA&oy}LF %X3ο5\Z?>慽j_hE$bppƆ& q jr:Q=ƨ%%EHyEF#".~G~z=y?YOшJ"##]H3`vsj-RO/6+;gmD@ZZ* ZPЈfg5=wi.Xp:],]ZCv&1 hdC+W,lf~ݚIm(paԱYj9 u1w CwNQC? .u׸478|X7r By/0J^ qgr il:Ec)>vy"55))@u0&,?uLֶv9ythvuG[9u:|Ν1Fk[|<:9{<-mD"/Ȉݕ$?qS՜ Ȑ!.hq;\l:A^Q)glcVgFhIejLYF^/.{@lVՎ"lfa0^ygٷOⅽS# ~F=UaʑXa-a]nv^u+ށT}}<7;rq}9y^8s?,eTN-1|>*B^шNp8n^Erg4 Q$#GTj5LLiѿ.>nZT*/\߿G("==MMJtdL|z\6~A*{vp={hc\~+~گ)*, '';lvRzS120Fֶv;h/ݳ=W  qYZZۤQ#CsK+-R tuwsY,VUDx^~7HO_|G?|u:~*_ZwaN='MAZZ8},CCaAbrY/]9 ?YOK[<$<$-mm?Y?/F`ll~ed$???L&,( N'} )))oQ\\L0{ tذ~ k5+˩('5%dp[[4rGnjp8NVHHrk ~V>SV֒Ê0 3{5O]}j1d Y‚Bq{'a(J CTbʡvN;g)a C!Z-BI^Q袅Z/uaG` B;?/9#B*5*gOAh8Oaa[eq9@`{ۿ(ܳg7PRR#?HOo|uJK$yaصc;*%Kرm ==TVZg/? 'Wr9~OgN~mpzB{vq^xY.6[́CG{noaXo nqjtlzA~j`Ŋ<#?{Ǖi?UэԈDf(fIQadx<^x ifd{ߌeiF4T#@ H$9U{?w>_ex/000/#گ2>Y~ܳOby/].tm曾$An"+yT;|!tb{ ?~K{G8t$OrYutGuCfo(?o;u ?4 <c_٤*MZwq8bqJJ\N-]şof$I ssM/bf?q &H*+3O=ny|iN]m cgn;|gԯ9|ԣW^z_f!eƛe7<2bYL|jn7 IxJJf*30X@}F: ̰pn24d0DɔnېeE>xL~|bC?RyzCf]"Wu756rh^5WY|y^+Cb9z f˒HF\'qئH3ج6 )-)f$#E=H֯ghh.RYqk~"7O壏;ogwy ;f&}rX,c4cRY-@^쾟aB0Q_m騨J:gu7u1=- ʫtD 24AEdY_0>*B޾mʔ20Sȶtvk)dC${2<|w˯qNGcC=/\W_>ϹN_Зgbԓ;m+}oˋ=~W& 3fcYf5/;֖/گyW6l`ul6v_~jkyu(**d=|ŗg4|suo~ѣ3/Ri} O6~EBii)Hdt:Kv*:;;پ};= FhlldddP(8C\casMw;Ȫ+H&fMfh8_T/9q۷nCeЁ7w#o?(,mfzܬX:>uCayS###Qƒlo#}uʼ<9z8ض[|\\чwbD'4Yʼn())ڵ>^-tN)*,<.vk206==>׾W<ɶv 9v%%Fgw Ah:cx1ɮ*+?BK(t):WUEI۬V+gͿ/wvB .'ãfX_Xd2q`>;_{o).._|>o&o:&kضe}y}v-mo?Q\\);o嫴 Dt:^%ΐJtŝ(AgW;oSyjZzL-7;Z[)*,$Hf>۩YVM_X i#,7g.A`[$BI>ɼ|Ban-`nчYKO%|mvlʚukFUWUsm4yVᛷ.AP1Mttt @Aii t:meppUV!IsQfmk ^8FUA`͚m6.vfj]@!|UeGg]k>cvs-N'>kO6Y=*k:Oxdx1LlfK,PUTQiUդPr$a|l_?̅2R;N' IFc 320,c2ͶL( ˭ill0X̯2"`6޾BAEp1fM&lvaJ*a6ۿ`? $` ,@Ei( xtdRWYu)nƒ Q']^[hs E] gTKq3ߣt:Ưl""CCX\横j<SFOu~² F ǩ3~6lXHkׯSUY _L>q>ӧ(/jf*z{/f)**R >kY2a#ȸƕכ/#1!(IikikߘtwsaY| &r@c}y1$I9 C),3b1A 6E$9t3h8X-V*++xYrd Ө ИƫֹT/ݜhd`>!9>w}Yޤl2s$HuO=o.h㡺oӷ())5˪)//܇)p=݄8o ^xQkgE4뿞57gߟ=E :{윝DQ]P˺lXIt X4ƶ9svrcoB@/{1ǥ8f3׮]g6NI&5aM#ZGP֭mE?X,ζQ)ji{%cz%Zd_"ʕuuQpD(+r 8wӑ'1< RIME>! ff?dYt1o=[ϔ20&s4{|ӟ??T;ֵ\Gg+5\꽌(Ωڽ8~?D|ի{ 2*cr+v?O{˰lS>k, /}(-?7:3 &*t[XXDQQ!f UU'r^)OȸেcZj20& 20z|D}}>$I棏1pߦ>rn֯]َṆigyۑ$ YVyp޼{q+ `XQ=PoiI8tXͦٱm+O188ȹN\Ja$20]/PZZtf9MFགྷR|ygvU8vb- "co~cl߶GnO۩q ٗsd`rd`JKK % `14s~~˥f3n@ +AC}Ɂ@,cۖvv{^˛Xj%r:bl߶; Ìѡ$ukp".붍1vCӉ"+Y*L;orqub8=zN&DU%PUU-mͷnS\T$IO?>3`ǶDc1=zI$t?KKimia۶-|o-7T{4&3IssT`h4FV20Kf0:#ڈHiv͊ápf'cZ1߯<20tƐ0d`TUa}nwS.$M("v### щjeժ"Hdx2 536-TUE%r\+77%? Rm`X I,9:~L6+a6I$6.]jexxHdJCCH\6RQa-AXP֖:~&axYX6Z-y>rʲ21de YVl nͦ 5OF8l!"3c?x< =ߏ㦢AL&A9f Ô\p,W^c9;x:bgTRmX2A E*+˨,M_c++HT*=600q:ZmTTTP[[CaF:!rT% ""pN\n1i;uz]t@:?zsq=~2wOue6[|~|Nj\G's.|>t}`df "XXXH08b920NjPYY;Ϫ+IR&v]RUse`dYÏDo9L&3nA>9|Yٶu3'N"<88Mmg+Wk  )..-%F5 IDAT.vkv>9]'-8Idh۷r}T?F[420 {+i20?K۩z8~!sdf 6Gc>3axpp~, .TWWSYY𜟿_fM ߠyT6E8[np_bTM&}|8|e_ۊ,I\M6+10cm\sҮk\r3gi**l! u8|l߶X,9-6T }920EȒgN h4$Ɇ =’ syrs`8o#s LJ_I˝>}Pٿo ӡoOOPlVbppx<9)NgI&)o_*c'd`* y}{Q*2!sd ?U1{xA7nw >O _:I$  Q[[_/}=O?&IsΝ}`|6`oqq1IH$LC&YVjk&JTWWPmtw ڟ];w\ea[6ߗ}]_8v{ߏ}6d`nZ7t&ã?(l#I1 6]/'gbq7iP&p:ijO?E }O>nۆD" HD"ƒdXf!kcܻ,.3(= 7+H:%*1X\gr\L&$)|>3g{Nh#2A4Hob%cxkکBatF/'&j>Ղh{P8אXL[jRkͱd@n ۅl"K诱n I4dq:lݲEU{?ӏ>ooP_W&Ʉ~x]>G7C8l|HDQb1cE1obfxf95,˼3uY _~厞rjIv2 7zm8;*sg T}NA4Xڬ\Ж}?G8{gz^~EQX*++x7ח_g駞䭷?`SRR cZ9sfN?<#tOX,TUVλYjT$baw+>G@'࣏ilgݜd1&f"NJ1L466Yۼ(W^bwVT-+P)..v =BT}om8Iw%MŞ>{ _gv?yאY,0ΐHhQ, };h>#_z^_l5˴;5_WUV/W`N߁A( ߇$ɘL_3$9/d2 eWDQk7Ce}!} x[6sl,[3z?㦶fNP8>صc;N5kH&8sw;###\.|7!ӣ20oMQ 20.vW6EcKF::#$$믱 6Un3 $t[[[C]]-@"xPXA,+}}|~B ^H3(Qn[f =B!^} QMFE[G8N#zKKimi12C!"ΓL*[;.a:=gPf48T-7[`(..B>rR[[ {%3Jǵ N"}ߘϟ 40X&]o;0O $FcH˥=ie`4BIQ@n)\Byy97n 5fv/L,4EQet9+WP_GEE9RD߹7~cvl *:}{ghh @kK`rse#GSQ r1L ʳO?(~zzzyI20KTU% L%ANiiWmp())fdjfc2!۷m/He~V#;͂/3(ddTWU)/+cfUgͦ= g3 4Œ16k^YAdYr$ N(2v$L$.6߷ }^ܫ68l|~" 1ļނC}& έGz-pعc3j[, %' 5@ L"9xgnZDS455PYQ+jg s?ܫ6} O6~'. D<#M206E@$tz|\Y,ee^ $ƷM*'N*DQD]5=vl6;T*;ϟWys_~:PU.::}ݕ6N|텿\r?_zZ &PWru<|WgQ//}oLF?ߟǚ 'osDgu( .vsO/~f=0%3xBEEŔe^/oB*7~#, ©CR4_w^*-kcO'amv5W}Cov=3>Pwi?};/v,L^/={v?t Lww.7#e¿zNhc;V˹N/RVVlBVE+Qw(*,z.\fdd\j?D!s/d `p~@yc22ͱtp \.׌KN'#8F}}/}}aN/ʕ+&#N|UQ!t~x nGfi=6\9cx{E D(4H<Lڒd'g @ii ~jNڷ$PxPWEQd ׁ Ҳf|WPUEI&y}|"GHlrut: 02*0d`TUU3+Ɩg8c;߭,kA@MĝK(+?%TU;/+?xL_r8d2K}?H?\G֯z/ _[np-`dY&E>#j{ݻv٥I㴶gpuk[[Fǁ9r&&O"D*+ v.^즵z.3d {y1UUEe) @QPT:wAɔԒږfV4i3]c%Aj-#Qxe;\qlpqv;o(Jww7O?.vOYhNg1Wk 3@{H& ]TTlV/aYQ$$:撣" YI"J#IYl6j>~ D[v~:?Naa!N'D#4SVV6XAA۷MflX.v&l+**d-v,<XvΜ\`"gi>rֶP?ϵ_rΞXK9KFkK3=fЁ_ ^eZ`Kpyb$a2'sUQH3i@Edҙ OYAL&S^Ɛ[A ٷ322,˔vm36mIEWmp>bA```I=/تOdTu(|>);o<<bAs* O(f.qΞ[~2/&EU ̙|_ Dq8˯{2?sqsee^#ds @B]U)** e(`6$2dR7dE3U6Rxx8gp:TT(#rz:R΂qb[14}\@"tm2Y) de EUQU$ h Fz8$IԩS|~}A8 ׯ_@n5\6a^4r~,fjd`~{jiudi>͆ bЃab8QsSf͠ $32ۡj-Jj~<HY Ŋ ȪlFRT=CQUY`h_D2o(7+H088./QVA&G<>>'d2jAUN;+xDv Q8|$I&L (ZY|U[[g.d`O!Zgvs0L%319E-WQdD%2Y ͆|24BJdW60Ml|c+ hFQƃNv>XVec081IJ,DVPEdYL&,ɀ v,v;фDVUV .h@O $f}3Ξ Ju{2U2ko2-Z(+ȲBR2&08ĺ5+QfUF0VQԌ?E8̣`*,,02#ԢM&^ool;h)rmUdNRD2cYf]6frG& AS[[K]m 6٬l:V2  H2ln"IZAR @Et$Qf,9&xVAV;Uhk{ܣ> *$aB(Ir"…U, T`2o˹TZr:BiN|n ͺD3 (PJDX"t5Σp@2d\NA|#lڸqIb6((d㷯@l6QtL"fI:Y0Dt&3sA(pL#2 8isX, ld$zWf4M* >pڂ!EaPPecY%rm d$9z#f5?A[F|<ϞncN_οf8Ծhxʅa3?Ӎ?X߼f ee^TU$`*F(ą UScIDATf *~YU-觵ׯ{ }lXBEk׮sv*O?dҒ>9r۷LYnhhp8@mMmvOgW˪9~(E0M }ә 7;v|ݼPP1 3 n`qpFQcfϴrUAϖޥY)sK"mKd")IM1PAUPTEY'i,+6El( "fl&IɤIg2d,vF=mP(Hu%%|>JK!jjB}}Xb%+W*JZ^jիȒիWIe$eKDz.]bMSCCW ]!2p tb3 y714<ĊF|<ƍZX,fN>Cs>O?^ʷ3gB6fi(@`r\8ْDm u'zpm^DEEńvGU˼{gpUl6㯦ӧL9~**`YYhIIǗC].Ǽ@A8CSctH4JyY?Ec[Jee%#$>m =S\T̀MQU׮! P[[>DQU75H&/}I_?˛M"]]oFNGٷw%x^"ʤe lܰ?@jkqb7Vłb081 t?!r=wY{kRiFFFXbYVFYW *Kf%ھkx^ܿSX@ ϺSS.zJQ\T4Kezpp0LUUu޶!$ "nwCCCRi3Lu_ifjD&bēYҲjUU*TT:C&!=zeYF$Pl)))CIq;Դ`ِ$jl6LZnFsBqA  H$l6BV'x<,x\.x\NN\L ꒈ8N6~.^fyr8} lP8Lxpmm0SP"J144DWWXD"0Z^'~qDz@QAt$|MT"2>3/ #ᴹtwJ*>BhߺږP9j];q"}~xEjX+ذ((\V YV.g(k \X4%Zʝ%jAkHdfV;e}H~QxBIn5~?NuHLj}vw Z/u8P(i&N'r]pi+4#hI$_3"6Q5 K4,|( S)-O4ЍkdIB%DQhޏ 4:j,;h6u&&b$dimm z---z6w.k⣆S(W(Fi>qE'3///L& JΝ;KD%& IFC˗._9~IVWҋ=?)b˗/poe` BcF#LM"X~F:7/!"?ȲLbԁEa:gV#?m퇼 _^Lx%iWnwQx<|>"E9k¿wW\)#L(I>m=%&'O,)ӧc ]Z8}pSLMуHdo[[9, d࣏I9{vTl0 W]ʇ⧹y/,,;M8vc)NX,b1ZCѤV'S,:.!P z\>jMddhEZfzI2zW۴?4/]*dQ`B٨4P$ƽ";nʠS;"jĜ::ܻd: }&bQ<:(QちolD+oI}!܎YY>S(Xkgg|Z\.KO>=5uZg#VslFA^gee1- b5O.\ ? 0y pMtݘ_r<I&|^y Wt|v}O~|u~Y7?|>h…=o& do]E^x'97ٟfgqG}_(9ӵ+ssv1Ck򐗩~ \C׺[/t֥[(b顃_unz B*>n E3Oz:۪@%ȆՆNP9DQzF,pszè\.3[|3[[9oΎe{6 X5 q5Hˊc> e|AxxH$N&$c([{FGd2Y !? !IW(Ý)Gl,mq>hKtekpppCUe0aL&.x| JM ?oFI9e{?;_zd}b=yt]?#\N];}}Z1ܣ`hh6'^FGXYYez:A:^j 񸱩Zh\ -i2;B*Xu+f74m1WYhԙN$H&Sd҆i%W^J?n%622=LǍ1t]=rcc/(UA# r`izSln>7v"|23sY{Ϻqy\.gϜyzP@9韫06>+))Ȳ̙3gXYYp I㑡omm!IP葕H$f](tc޿Zoyr܋p8G# G<'r,A@Qh{(t:MR+]T=BQVn.~M,9Arm(cWlU?M'VgoP#Q}v+DKsu*V"n=bƗG>rL\3j+MYglkmź4m,2/{Τ kRee7"|'4ŔŒ|'{0mr/Oի;ٽj؟D;2囼Ug|P6@rk܆qMrbܤIЈfE1lo| ~SdhVzZ2GN`I._5of; 2͟qzq5A8k JL`#'f3l!5hV#7x.k5sx5㞯_ },g~6|?g3l"5r[p%~yw@㞭ONa``>l!Y3W?2A@/da Lτ_KxC#:4CzˡFҬY+L,Ř|/loz. j (XuVö|޳ДU0}ӈjxS 3g%oO zbmw.665W0<:cx"5;P_ ޕ1}t9Hj9kt!:8q24Hg:ra6*"BIrSOD&Sz #뛚cF;MMi~5"BIYohjyҌ \;?_$%Ǩ6nn'`C󇛼Uۑ~  ,D 4i$d(m[ |fPR~z~ !g~V_xDٸNihi 3lkug1#g 7YJgO,]P ag~>5գ/S]{jJQON?rzk- &e/>3A@( .8bng廜] D'<%wGCS һ௏BI (La3" ;hi֬m kie-^Zh2k[Yv޵9~ł#ASDF!;Qᒇ%0Iazasg>Բtغ[~; 1t{us) ߲7eC鉀0",GŹW8"]iqޜut/zڿ]i%)}%o55lvգ@{g\'m7DJ]JolpޜؽcsKǹnsem%}Ou:łt8b?1{sd㈟a9qas2F^oC_P]# @*ۼ=YaqyҍD]8fUz&[[ѥ{t!2lb@l[| {}o[:-Fk6 M Q/ݧ6hpĹ}flJr;Ď UsDϱ|E| =x G}Rn6{޿O~G@A>Ƕ CN =y̍ u8D~nfv)W)a5j%BA:H~]2͌|ϝ? "}ܰhD+yH됢i{{Wƫ6 5{10PKQ7^ĭ$i643Kn^Z/_ƽ|_SPuڈjڛ6]/Oaj]6g'i1=ngC͆r"P~q8Lk167޻vqnkWz C)>=q:B߲+ >c'? '|KDi'7~C.[\Ih5l2} thٶV0%Xm"md pH[XͶo2tYw.6~x}xIcy8rA|mt͇9i6PXC_xJG~Fip0_oݯgz!2,`9e֏ nN8)6u9LkХWo/*v@$؜yƎJt"d|_tRW%Z"O oOkثfYύg%,+@mڞ,c ,cǧvF<D b|c3_mS`hpPǜ` +ʬ=Snf 1FG$ٮw3#&f#.Bإ-iy}}FzVpԥOu%2nPTic^jz=[mjVD#kQ~e+-`5[wj#<.p1&0f3 8f0`| ofblsrso9b_#ӌ2ߟ~^};#. + 5VobwpQGr5ew*qq%fdI(250}?;/l7jjMݰd0_A` J Ketfv*&UMn6t`li6UJ :1t(5;iA5KաGMyXT4zv=|G6&Y2J€>6^g/lLT `Y=8.tqGQ\ظ/9i6fHD**}3P&?|e g<WPRr m0߈6hjb] . ?R3f쨌UgK6kie˦6jg VπSEɛ- щI(w.YA  XŇV__;Wq+ X p襏 1@6ٻot]6~'sC~~f,ў!R;"Nl8 |a'ZkV/꛽hh9YƎlE| zR20G[ ,">qAVd f[+dXȒ[Oc7ωw,{~LCw($ u+Wl2ZF^#?>ݕy23!{&c(W> lnQQ>ɯVt1ʆUn UGIvTݜemX~nΧXzgL|*ʼn@e+{e]o=^ ۚJ"9F|Ѣ Ča`:*}3h2>˲38+WᵏY H]'[2=r)5^c v|x|Ɵϸ˼j~U/1#ߟC3_dܟvW'W:;&lvX|]̷dl~q-`P!{ C36g숰)_7rd~}#z?n̳ub[1Y)0 ]`_=mՕO"c锪;Ot ׈rz@v-*8丩/·Aw8dǧr2 /~aE;T(yv80I/T7``c?"fA^SCشт^]@"s^=hqΌ@lavb6-[,>fgQQ͙1^܍#ӏmG^hjeٰ^,a)ypZ @?`y^ 60o;6[ @+%*ֻݫHXc| K_#R_Y-52Ϧe?s $ݘ?hrm.t1눝~bIlɋNa#O=92CR0ZmahrڑP 1 Q&@.{k•xw[H0I>hsEc%ZR@2V/-;,x#70Hv'afb踵AA75 003Z*w~. u%p]zH:7O?hBb~I j,M*{ES?+;2zكJJQk[1o'.|tʁ?I 'o5.g6wp! fԷJl*eY{F۴?je9?8pnI^jc¿܃-+6,"!ο*pH[ eYnн(7p`}Tx&5O27LNXD!bmkhAu.{SlĔ8,{0CQ l_,е‰z[Iy KqUKa]6¨\޳s\- u L.Bjv^=zVb^س.aS[v\}LorZXcg 9Tku9|76l\/PL/[.G>M_f1. Uev~WW>lo޳!z/Cf XBl>Ѱ=:k |Z3yQu1(|C#6c$MP+dÎ~;w`tn`4{4}AM>#6Ռ1n7|%h6TlZ+߻h KV~8a&{!no@m)7",P[6^z D"p~Y{ma rWW:Z7k m[p5l-50~_0S*-ٰM0%Wu]_ik_8`H Wf$Dew^>c_ 58FQ9u^9ܸ7ƋM?BTj׍ǽ0}0{=MM֭GlZ6f8=j|@xՐ/ٞIcrdsGt\{̰X-ً㧅'_7Đ=m:f^Od.S=u!3GyP}XYǚ3x3x`1e,WtvIXmmdj>,#oM;b~mil-6,wL;j/JDp?͊ ~߅چ|dfm? ]0Ϭ xǃQf!. 2CK Qb= /Uhpԉ3)aS: G$~JlG}Utk33Xc1]zX/ D| Nv+']LC;+ A{9!hke~Dց/vDbveO ` Ml'9r~| E"gS5\tttIH̤)0@͵Wv<%.J ޸!`0XG_%2Lk `x(̦+Q7Ex|O`H РI`ؓeT lL?_F"wFǣCD? Q׾ܕqJ7o|!Xnb?i7@U:7izX V+Jp%jYOٍh̷y2z!X֦Orqv5{$PѹHF?䬘 9GB,}j+#%o#tֵξgl\<1*,KA`(IKǴd2B^XG_$ 6 8gOWN`="AXڞQ ?1?nLVDj0V?gX7xaD8?? 2~Oa6kwtg=?*݈K8,D,ɫ[UZ_f F7"%y{zfRvm`g ..*s_g=#`Y(Fy3߹cf9@zBN69s{VMUa,OVbCsסfH0vOOw#0;kۯD(,O֍G9]֓÷L ~;y!X&@|6l@D~3dG,-j?lZ7@DIo=߹]"Ac G\N57?cZ,Q;{!^A[}ZPڈX]\lн021bFmyZ"Xt-*_5J>7?O}]Y6vW 3eIDAT2pW<PtF?~9Z'"g9|yemyDȶޖ^h?[-N~S!O`xaٴZ"@okV ݢ3v 3@#LwCr D55lkvj@}A?| tc;Qy5X  2L \ŽdgD:2tm##}ηy1R;"/{(;&8*'L>%Of @ژ3ޙq<(3p@pIl6' @ؙ$NA{޽gԦ<) s%O좁p!6ƴ) S< 41w' uU7#`Q;6Zf17i_<,B8OE,Nc&leD,Ht6nݜoz,ބ@ 7^@[.  k`ȍ :.R+ iXpg&"|sa LR2" \j&G^*7_):)L"Fx"ӣn ̰6H" Dؕ{"oi?`uEcn\ܟ:9aXf #P 3 8>ݱf {d#&(qXcqzLispu/R)۰dTW @ ]_bn߱j?8AL|AXSc4v237Ns`/ﯔ]lƏяZ-"% 8os`{Mmo=MVd;r<X堋g``z߶2 #{O´ɢ ED'4yml:c7M2iwɚBQ!P|_F~(x[/^w=^KfZ1%َIDn V+ZoJP1VhY̢~xO0F}QEU}Q O_)î" GL*"%+= { G_xQ{P kh|vi <&Á-@ q3"zS34q1P5㣕bjFu#(pߦD,@ ARb adz.:..i6|tqxЭKeE(]MN ]!;LjX/ŷka7гSY,@P@'?c?K!G?ihO2(VET`qt_ P?NZ_ ?oی{tnDEqDG.]2amʈ =B (ХZY &E," o3یXW0N]aǶ&Z'^TIMbEHpύqltU˗7z⍌1NX`UDe?NK:'v@Xpߔqt|^ OaZjEqQBG'Dz{|FJ ^X `YDu-{H7?yfĢDm*D!щ$h'{V`[(-DcfU8-y;8.3p̮3 _@()D osiRSmW/=L/h6DF}\g&n(2T[2{EDـ6E"E7NN6:Tm߸_MC+ư@ -`QD o8ilkMOQZ|4ab?k%`QD O~!~@dI~ N@"P$q;cFcݛi>s`2oՁ\R("EQڃMx;*疟ӱK(D2zt+ xmP 3@Z>$2Ê#ҵK(D_o|i]>Iyj_h"zq( >Mbz_b`$&"E1OpύUX"o, vp_0?@ `aD _vh4!|btIFEUXFTvUz0&au>>餣BǁitmJXYo6vn]M'>4(0̪_fMoץMK90MGPzg$Db&AupEE: 4ۄA!FOS P 5H(=D Of5P]F> DW"}&ZjVG{ѝ:g,)x&6t( 2?0;MNvQ`bM$~.CBi#P@X1:ikj"8=T߶LjmB) ۃ վ}71|݄FM{z e)xLیcPA͜Zn.tc0~G&n̆aBq#M mABEF3"D| mxO!-#f6OB&uD _?OY6D#"gY 6C"'s ON)cR"(g6s_@Y6fv߲|PC vU9 v!llQ]N `Hob>yl|Z @jEUq󜬏 V>9QwPDz6۩V)V&铓SzB"PD3au0skW>q\#?ThD00q [$^b~hq]j,+("ÈsM.M Q|$=HhQ *4^EjYLњ`6nG~q 35!W[2(/힗QcyHS5ϊ!@Ǩy9\(gtfQp BZHyKClJR&6c_?+[KdcthM0)DMxߓhthkAw'qkeHn[n~F}҇/}Dfm%PmLܴ)gW_Uǟ>m zhőFgCr}h2?ɀ$*l*ީ!GI0ÃGeKbm*GhB]ٞL9F9H+ HÀ~ɲR`(["Q}vlQM(G]Xr _Q}p? km!U:tG`?A>Z#i:ݿh/)p(tN+ 0G2]2 I+Ӏ/ 7 G2EmV]~ 4}|H1pKROXEAKd"d CKXI╍cVL,5~E+_tz~@{ѸxC>% c¸v54~@B%͗ qs-0МdQAf#] _r`s@At>@}(lCr,Yϣ6"z`@?b`hQ!):AOx4=EZd%ZOkz-ڔi<{ɣn/끓w%d`;$@?iO% {@!i@kt\,I!bp菗%g?_2`*!㽢֯#mI=EW+q$[eʿiˇL+7_6p Bt@0UD[7`n2 .c+]Ъhd͠?^v@zgìfWр5y[Pn(N>zWǑ%5phD #9BVljK?H,ȇ 2˿-6}eRbDB8w%C;j 4t'&zG "nL,F v,< Ae&RD+PDw>*:rwdG?fǫ*kPc]-jS{Gj!c`!`XQ2V@bURM(ĪFLP5RCj e`B2`~aoG&N5}b@_eAYaatɞ9Z :?NѐiAr'1h5wN3m{ xHzW":ۧ^Kr$ ԭTSIl Y|@2 _o0<_,6hi%\Ye6i${Q' ~9vb`AR`Q3廌H`OX0_0@*kqkP}/lƴݿeS,H?vdTө? ЬF[A}GǓyVY*&(1BR஢$q5Xu{yK\b,|#cLUaf=̉PrI_˖* "@OLtgLI=?2ItiL[oiْӼP2됅 s[dE{9xd67NL}bo$I)ۥ~}l)kv*TB] ؗ:_+S%EpFȃ h$0zhߒޢv1zo Bۭ҂B&d!%S~5^{膞9zb`+--c:|GNTo[=᷆E^rg퍶8BH2vBVpxCQ,"I2v+"nD i^N8Ab?Z/tk@g61M7C2BQ]_1&kA"=,/ kKf uT"taz3Ath=-|' *ڠ4]El} fa& a:Ψd?VUsz1ŭ͢UozezxGGc`5>h1&R`(5%6-\m; Rl[29':@9tXBͯL.U" _J"ؖ%mN2GT@oɀ= dnqC ? Xj{xY9~[LK̆g' 6JlX)0CVZ"$_UuЯꉨW&6+_wS5%Jup,2cQ-GLvV`?=2->xfv.V*7(UYV؊0Pl ;#l)5#z !P#Lp}m)A} m^w׵4TCIfhARYrk2YA >ZY-f*6TŷC?x[\F&KYOLJ|r_:B GTb,/p?&*0>sZ M&AB>jX Pp2`fĽPc՟W4h1CȺG-^zƸ"\9Nb*!~;@7&'VԀ"B4C طv߹NQ|D|v;-YyjdY&zDV^'oPgs7WyV'P&k:{L"P3b:@5>Xk 6HYr*˷ $c`Y{<sua*V]2y1Y/*!NfD}Toߘ XXA/nʖTS@R`+ }` xg0iCJ#hx2` K*㜃eQQ>y?Kn> ?Bj+5N&s R>cnȂ =ZR`( B Km:M`}ʶ.* lvw;*xK~TLo 77v!@$.oGme՘s2!aj&&X!1ZRQΛtm8.*rпfg[)WnruC弑2£n)K*2ʏQX>j`y(XM=9@o!, n/x8 K0mUU_~RA5,j@4x򻲯E E kBWw"6S=ŤIRZG눑m83Fba-At7 y;Ul`V\e1Ie5updUU;JF Q|Thj|1)p1T Lq=NKM  T?>p}?WO`ˠz@.3gAn,<%52"E|Mdvt ug*0qoH 7g:()&)Q b柈*>"ң̻NIBZj/Y"Ble@qteGjڟj* -ŴDdM4e@~֞+j x~;b`faqI .Bur(TJdf5 }VM`3-Ñ4H 6Uro'#h@;+Lt'yKg/qNt_dAYq2V-#J`~o;,@\t)G{cp B(fjHWHh@x3KZu(?,V*@β`_UTЇzmJ"Dגt~ރ"&# &% ĨͧH*=3^AL$s4YC GHA h.%~/À?JGk+@\X2 _D};Yܔľ[yI y֨8':(o鼌@HY-X`3 †yӊʌTnjC(s׺ M%f2'I 5F 0H} +d fx/?h"km?ATbAe*>V. (+%{vL^=Rŝdx'{i0Gq?)_go,sUJ5hY xZdl`iIE`+Q[ (60zJUtp(cusu+U>lU@xF?Ep!%&dk97xr.oe ;M4j;%0A3>ߣo P4teݤ wKJ%n[#Mˑ78!PlW+{CBAaQ}`C3õn p\J/9וJƜZzxK}Cr)d& .WR2RA Qک!-b|L-7 } **?a a3gK~J(o^Y,&C  Y%L/@|4)(Uq6 `" Ly)/ڠ1n7ɮ:Awm 6kevK Ju>O'$T.R@LP*((ytr(+jA* <3@Eا(z8Q 8, ۮ H??ƒdAzw 7Oh޲Zd Y!`-.mb'P 8NnJtha?e&+X=2K05C5QJTjIY`{~,Puy;c 9G? tQB!e O@CJ 2G~B/f/er" ̦ %hWNyz$`dZ.[Wy lIl4#*~× 2w\Y{[bnh!ENn(h#@q#H8.6ۥygք/"nJ&uiDW lm"XwF6tKPWJ35_kKd| HUvy|p$ zOSt16$ Qn 8~zzuH)@*g{'oqq5I$B]*f̬?`3u}mܙ~RY^6` Px 6~O *j*dQsuXAc?6ScH;쟏\@ VW J)()!pEA@".0Z*%$07c$ fVۤ LvޒH5nhd.mRv`_4|Ksk$Ts?\Qr]䦠+mQZߩi\#/ȳl=e4>70=Ive~ŇWjc{H'fAw@m)0}Z]wx=a.MďX[z4d/-ڝ_ͪoQ*=Ur̪Aw =Lx 8Gr81u61Q(~;%bWV*WPh` /}&9 Bvdw\TeFIؤ+ %JJ$y~P|sY >U#( fux> '먻>ǾUևߥ*"Inm@[4Wۻ;,.;pԞ ߏ T;ujBMee=}~Q$f $RZi#]z~|3Fp![}pH QA D wD h7˕A~/L`(#M5'1sRU>?,G% ]`>+Z:,(o W֚Dl& IOD^_^,U!L^UB}74i|VBhDv!Ehh`X+T J;1do'$ *t򞍾٭ؓ %YMm6hݵӠ0]^6)X/;͢4Y Ugn-on6b D,ѺtEe0 0˯\2_љ̌WzC6 8~Qfl ډFo|B`Dy=R&.SA'h'G0.襜6X}BQMT j qm [ +\@=!+*ٛ5l),R2XJ8,I d _AeT+%iQ l")} N#@i$`\f,M8! J)$عW:zw狍Xf զ"+&mY <|K0AܰvӨEV4pJ,rT0<Ё>GUzٻvs߫{iNf$=ln ǩD:B0Qcy_ҧsā\x(1N|c@F9dMG#\h$!$g@$2&mF+XK|v>?TKD4Et'B~!г &P`"ߓ IDAT1 aPĠ]_BBWsLmʪ4,ӗ(ńY~F|@Afd~RSۨ7UYP iŧoS#z`K:TL@o-P+Dre}";hcTcm6%tNw? vR3(*Oo̔g"M"d gv@pUoeŐglʈ(l.a6fk"g`6#+Z1R`Bnn85lF$^n ĩ?uvPOU`ɩhޝa|Zۖ1% 8" o B@dmv5gL3g}_ydr1V+~$p[HoF/ g|- ̉Rek800jXr澪 uZ{cXkj;M5`!?Url +ԖK Y6ޛsժ\lC4I 5ZO3zh@EwKv]&9 u>ӷo}@fAdŻzzȊdl_Ɔw Ȳ>Y]QxI ,Kg'&,Py}}D="-*rA_A M p[@@ҶFޯm*(uεUAm? eyG$<^r3-vg3 @~z eM֮J\@KS+#8ׄ !D +GdMjf׎&3REOtp9 GDy(`(>0Oǒ >H/x#ev1?ݍqn>|\SV'Ƭ7?&*+|9]IJՒk!€t/|?M Pg݇ %{vEY]W7UK(XA7o 9ШqUJ"%q( PdBD+A!2\!\Tn=Γ[98H{((Yo'(peңƝķAR;[Hew?d _MD,̊iEyZzB~<A ė~4W+ԙqO/Jc!~&u"#!NhʂٮDxKFۉ%HB?H+GY[͍iA\kݯկ)$0FX=KOߕ|u 胞&r;GɭvQP[`R^m4yb6M_֔~l\0o@qdGۃ pp@i$)$z7 D@X#vr3Q|rd^Fj)络mdLB/#L~Rǧ'˿;>}=.\. ]v׿N0q|yngA3r!KSǿn~ o^KRg| }KdIeY3}\8":u';JZvкWo>;p|X5dy%u%K `bIY/SHm2W8m"=Pf8MT?Y-IX!mO6 e4U'U$8T2)v}=/0; _iA){2 `8r6 -;Gۦ^fE:7Do)7YO=k;b;r־&ow2[Ib_@cRLiJ7Q~|d382gTqS)6)&\ԡKn7&]R`(5sG'ެu#ۣ5f;MHm{ՌCVBi2by!{}C~@/3)`W L" bq^~K8_Zw}[N?#O{/H_cud.1>#~?p?xN,"ϟ÷~'>#.Vnw?_?z|:ׯߐ$ 2!MwMm ~ jֿg]mӕ{1gݒI(dE>znBaG@V/r{O޾l22F ,`MK}OB 8dkВL=6'8}8/)'`/ێ#{=\bEҟm"珷.Tۯ{́4AyD.c᧟e;^^W0F8>?UȀ/_wHx!rZ" l(0G.ݕg  [5׼^ #l8!߻]9>?!;߃~&T>BkD ]>!`'c; XD U{4+R#KD0֠2*,|U{bg⦵@/+-GCoP]Hi=j*Klqvi|9ys?t!"vi)" 9?p?E^_ p9_ AD.Z5Sz wDw%gؠ bׅAIG\Xk$ASg!je,XjP"*c8/!#ܤP ruˤ@+!P0wJ_L,9ӳ7*W7#PQ,y6f;$FʑYe0$AM٨&*xxb@zE~лa߲Oi8r:AI$ȅq=~~{e&v#$޾jp8>|zo{$I98 :d@RD "ϐeΟe%PUZ v=I"^!A*H)Bh"Mv K"L@֘[nƾ#fڔ;"CA AfpzHada J|Wji I/2}@"RRRd&*>c-O /?'?jZg_CdeZͳӫ*ou:]Ma3'U L}ϻͳVbP,(g{ݡJUǸzW{ʠwoCeV)Y=nEj_30K3A d฾:M;5OD#)g R* \N'\q]AfA(JG. Kf?sY Q#r)d,~N<ޙ 0C@?ztFc<-4p, (Sk~߭@[\/; _U]\=s 1Ƙ>iF\7|Cq{Qqۑj5ho9h놺UH[~Jiܘ7R`t}gΧq"4gxs36 X`b*f̺~kֿHgo1:(Ǝh2kˬ]ZPѣ>Q9jPTg H} Jޭr3\2t\YL$5d$B 8푃\4W#˂ h}Ja?MX˵^k*ԃS Cg=)!J#yw'2?.$޳P%H$7(%!\N[`Zmg-cםvhb A _" y$&'J pVy5\ `p-,{=7kg*u,:wN(((˛ ~X&Hv `6 H/2BYucH+ %-@nxh ;t6: 7#%3 B3]j(=-v<[S>_]//xQ 4Iגq&Hx 5d$p)khϔҟWVXK /N*& Տ'n_[@o0#:DN&: vx4ʙ9X5;h-2,=&-l::ZwsbZKҡh@(ϏBqL6tSE}-eBh`b`@oFcJpuO6G-;A[̾#}wnիL8raF7+r*TaRggQ3.DL9пJd>^Hb?X3SS{NR{7 d#9#>h[yzH<1VLb&֯Qs9{SD#ެ*`N'dN"fo]_!@o8xw%쿪6xc^JIH;vpH!ɔmݧPq[:VɁ+3kX!0gt@Mk:nE̚Qvg׫,m;_ejO?n&jbo`Hz҄"n@?%Aٹ_yaadQ1|C~B)nvܙxYWղ%<3ItG !_mOxʱ?p9_ 5 "b` cqV4ƠδlR :sG4uc*f3 3)}7TF0(wN@j-_ Y2q =UzRD*%,@Xŭ*ȻXg*{V5ƒ6h&9`m BiHhQ㺆I;_<|r]X-}]& 8;΂:<ћr`Ġd2o`5zEk"p9ZFdt`7тT;_w+3/<٤7yk@zj uY*Rz^)XwRk==!E_ q>A QZJ0?Fmq}fˤY߇bѾCm$ Ӭu)НDe Zk~#q;лˆoocLL/vO8OMV9C;4g\Gw+#*7 }T~G3_1wIeEdjh }뻍:.{ś<&L 9!+ hޙm/U9 y@o1o;C {~a:؃]+_6+ |@GJ^!}~!syh@)͖qLAp{P‘C\IƓ#!#sJiW\r] =jKAv_7Z\)v5aCEhNՈ"ܧxxeJ6s[-Fޤ[Lf"xR[X_E'Z?!~6,8^o7I[D p4PGgm}gj=- jyfCq<ȾA\ 9[R@y;u{o2ԅ9Kq4H;Ib"z B@:̆1ÙBeX2ƵM)$m2@@ Α 8cz<:t9IiHĒ!쥂so`P"8_ nXD='c2'Zt2#\#Ia7FH${DwޟwR2F Lu,Yv\)|@,sF` Gq0 ULC)3ylgHI 3A"(<0PFҧTC\[ tC!3g.#3֗MIIMXRG6MHv֟3 ` ` $a6IA[@)i6qmnBcNp#JwtщITX2J h5=UO ;/@irn8'sJ)doPD??#y>BJe + Qejqt\0P ;Y2DX̔c8RlAd2P~j;FjbzP^/r bB3O9-`LKC\ <׳H)!s {?vIJ) ? B"$DC 2r>wS=;|Bۯ~@4XH) Ġ7쳄1d_k5 $n{M)rIQ&5 oB jP!}dde Ji]E+1vB<) hӃ|` H a|'!Ȉax{+;K q ?q^^<=A~0WKiDlUqS׷7W!w8޵J\MADw?oQt25Ow^n^ E"ls$Iʍ"$B9%X^ 5ˑ_xF?EiJ* Hz! !f_a;!7A"\R2[I=X\đ[GLOG@Ib+}YBH;(!CzBYJ|q% DW'^2 !99ٞ0lN0ɁPRgJw5/3v{{>.3 s=[e_8 {cFLd6SR SL&@ 2Of3P{)H2s צLrQQ0kA2) *PR@B 4Տ$mAKUO/ȮVq@%& !!HsGuiio@t߆dųXD<@ Rj"ȰJV pofQCBzƷ/_j)q+i*za#q25ب~<z(gݕU}C,R`* _>'a}n`P+if%}~CQspy(wj6cHw{+ Gӌ|x۳(귩7Ez#MDPT%6k$9Y}eGH%! y~EeY w!q̑9ra dh!y,21Y `9>b`\3m^B`3 E+=d&~N>?3egg$ $T O sLM3T2C II;`]Yv2 r} J \/W^@#No)b a<|>Cv#./זd-^~ۆ򅔵[n4V}QԐj֭(+nߧ> or@p1Fp8/O ;ӄZiIU.2bCN$ȳ+Y~T9lߔRA,U|۽IV?YS5 h͓aND6 3(5 5f%0 *JP!;_R`g[I qؿ<#}~⚡ z}z:J}cuA0#Q{=z=ww%](@J"E d"Tܷ/_RYRUX"$].G"蕖ߚ7w 9 \(G̸f*[}ī~~s_a*dĉUe áY#q|10b(M23hYXǯ$Q%1#"P8<ѠX2ScQ[hkLnqoL@ h,~Ym?fr Bۋ Gu27V%s)% vOOHOP2˫ s잟& 2QGy{Qz!=@!EtlInziBb/3R?~j0`n }8xWސΑIfA5sGjkFB6z4,Ɍu6Yޛ 2'6:Za K8J aSDMR8Uq7h+5tRR|S2`6>CdlHފBP"oir TX4bnd;Om x D )#J঄DE]BLf pU.Er!\kk}A̦GN IΑBAYKq2Ref=]ǿJ)_?_/^zwg\g||G _\R|F/K݄lP w A䟭 r>%MTϺM 2 )_Dz F HssiـJAh--(6P2:5! ܛ̈́.)R8ߋ,Uσwe:\!0r~bd@@$!`y;RJr3vOxr{Ӊ9?aIWtFtmBnGv:#;_̩om> O< ~N=k*[{ N+e.ǔ%ItC#٥HT2p\W}X±ͺcMP:8!Q)HA@CMSOol oq%=d*%1HT*bW)& D 'IR)`@ 1"!r<3$ܣ;j7<ޥw#>Ǜch %rঅ[#)0) E<ȀʋRwmٕR ?8GzܝծJZY nz4DBoCnF |DB`˃z7EY j6E&=Tk qɠi77u?'=Pv ~N4}p.sΑ`I7^N$ӱݿL6 1H Ntӥ{z&{]310`fR :Ϝ3$09 @$t( YQ%!3qGRy }:B@]Ï'0K;grA~>O5ԗN>=`F?p|Y?qkeqS˹yb R*så^,0Ǐ dLeOPR!3>ZgT yg3MoD͏ѡN3a;3[=WSȄ@?(JilY^6$ 2&׬GE]o(xss \XkR4Y ,ߣLN녳IL G$n"M}I5G D{0"݇1փt8ͬ*W'* Ng$0CWU=cȮ9`b_?< WWл^>MKf[~3TٵI"u8sPȢؓqF5\}$Ip^pz}J^tƺJ4Ѧk]itJAd.fRҀ|f~l PJ=j81k/淡`lo׊Gj(?"NʚY|Gޜ+^ B&ǬGLmrZ,ɑge~y1̆g?c)! YvFW"G! IDATP A[DʇgM@!ؾ4 H;wI3:`^2@_fL8QWNfvϯڌKʱOUbm]юF0Lba,LDHv)y~酥׹./P±{zBr|ofSJ.=?OM@dam@%0 TgJtDV;!~D_~JJ|[J!3k:O֫*kHےggUF,}{eOҽ:C2kz"K kP1:ZdKgM q:XmuJW>7C5u2n9adA!<\` u?yq @JA|7_ {hHm7oSNOrSgGxhsph'a%``a`SÐ꯲uUxak M$; WT;u {˼kc+yg%a,nN hd}%v.=d <^#d@ssҰj0nJfafۺ2Z8aFaNj%JA/P6Žy}̿Cj49 G'&x+N@fi|N73#DRH)dE|*]_?ȘTecRkl_6n7y8=TCO]p*h:i~!$g*ٯ4[!doδ ^Cr"=Rq=^D~H.TX`jٺH`0E@,dE3Yh)@X>ֶ&?F_Fa7f9$Y^ [.1͊myo@2+J`f`W&N@ԓ*Y/r!ޛ1}hv(Bjſbok.:*ud0c+?Lw6e?d2"pQ;,60({:ϴ{=ļYۺu I9΢%#pqվ1#@iTG6 ꪆkc\!f!oYp! |TbA 9ru0F]PBv 93bo@:L)& mAJ?qrop A?~㖘ƓNu].w!7ØUﻑ1U_Y5zOޣ̭V@d3'2 _G S[s30!p&L"O(8=[M-e!MuWhsפXH$8a_6~wO~'61C({lbPo7'd:p)"X)J) (A:WP;SY&{w;uY{5RD4CdiyX@k%ogpuV;3E,A>O) Fih+=:2J'1\RZ{k9(J9r!/r(}t KA;P*Lb}{}0pеz,1ւc`?yIO^N&H>H',i-[aA m.~K \L1*تF,QvLE[z%r30wJC+& T:7b`B7Bp"8>I_~ο0$)A >RzJH4p %hk]?$"<8u[K8_ ŤbyFl Œ~7H]_NGBp鬁*hgDt\$Q5\$E!7S\=]{kILk{ڄbXU5>e Ru+ nF %rHad)B~ъd\4xc˜b D^5:{oҥ iȠ;[buY9Tz%M9⬃ٕ PVpk~qpp΢@Z#_;QJ Z![/f W!9+Be7SC _4IOei>Ly(7l%ZA+`ٗGZf@97kej=V `ni`1v6!+ tAicS00j>}1{(2nRM փs@Tȃ1B~Ak lmd7[T9zçemEdH w–"Xd */귿ГF.TS)(հ5{Η!ay1Y*r MW W|hEgn9cⅭ˝( yXjL<Ŀ6:t66u$}mJǴU-wΩ2]wA&|j_~!p~99v AAh]ώYU\2d09ݴ SʍxHtnAilUٷBCsx8c`6[qXq[YMgAو/Rtr 1_B T lU% cMwR% j[e eJ @t0t`d_V#?! p3y},fZ(:$ pq2%as7jhk@v<( ։C@S"u=G@Mt0<u#~預=248T3yxg%wH:7$s$9)(m`j ]ըtR~k űő'RDZc"`E)Be1\)*kT^V5ʪ:){ژ#^o2_捯 ΅.,$"j޾F 4HT㤹lٷ4Wm Z҃|3Z0es*e ]kE8K _#֎ZDy:)20n'kJqC)t(Rc: ևI#[ŪxA>@WKZpo3v4T#[./xaʆnyOӑAtWlu=+nzH($.17l@<C+kGKBPMa 19@)IG}JkP$u0d,)%f|8=L4$MC6C\^(LsfkJHd5R2ˡ؝!$U䉵?;!o&e^G?sw>%ng56 ~?ȁEd}) d|ւta3ڦ鼖f\5F}A`՛PXxCk9lUTe }Y!9mZp]5N ,x;H)T7pU\D@6 BO}.;4}9ҡI#Gzy g^A@IVާQZKx>ϱod $JZɪ}_ sz"L~8 8-e(-&犔r1hY L[k{.i̙}#>Zy/p>8PG s2;ȁA@p@3XAbDPZ)r ߤ4twN@ɛBiK[9GƧ*pG@BdKmYTc$sd@ QH^7:̀t'?YN08ZRgrN3Ps@-5[: va5%TDR X@K-LYux>" K;m(?Ǐ篭t H$X>(VKk[>-{ȅ*#HbHjz>PXLHAg @X@k sHiY$^h⥃4>D ZgY|/8kj!{nAdYAe7t ѧVZ0`')ȗty|t#0%pN']7xz=WTSyAL;l /4!;q [^&o&疹#q}p"rCt#ߟpց˔>XH=5ιGǀhmp]mr?@4D}B}nʸ45. ejO6.~G0o]bgBXE nƇy gV X[m=a{_g/yǬézP~z #>};jW5q WTĭAľO=L *C{ePV,L-q-NdY&&Ց灝 r0#kh0Zr}| y %8'JH5wlL?Жn[ .zr^<_y38A&&_Í|p_xNWzgQ|K8:g=@ @G%VU0bHچk(`}N2CrW觔^-Տ 8IT\`#fݣgOkkf;[Ή}Y J8 nl60HkX@By{UF"هne<~x,p/_@<}*|_,/j i/Ȟ;vfVx__?aTx<},/{yAYkL{~?~ [C@@ )O$11Æ1Dv7ցfKI+K [O1M'}>85 ʠ* ?y[רZXꔊM5NNևo WgcDWKjQH,m[jp>ؕᜅbQdؖ4uenu)qHJsMXG|v\Crم c8t}>~YTl5}9]C ȷLX㧒Um^6`c%JN@@&9زe[,?_%N| 5d%e 8J42lê>R!{u*/2U/wǀKLûw(wx{SofUm}*F.[/գW7{aN<[\8ޑ?y x!|^ Jo) ر7Cd N9UI8@.B6I J!8_ )8=Mn.9|t˅ ;Cd߿ {ẅaAQb'XqjS;iaor=9fM)nu8Ap^d~9,6.p-K}-o7alpY LWY!F̧ 6 =\ U+"b4BB 42I@JXB7 ăb[9`kB(+(Q޷?Y<_>cb\cECfuU"r8*Kf|Q. @?:{\r̡|MrTJ3^{`XUyEo@\yȃZ{HXغB]@m`W.u =1%X9݊cL/SuC!lt7y#eF# ƈ;ԛګ]@^yr")BfuYȀ=}?ڕ k**?P %>iu:ro?$/yb5Hy'! ;AKp"=<ϑy+ +2OG8'fF_~D@LCʉX{1;8>f[lPi"Џ3!?"=8oh^,dgVP虲$'ُc$yuMX0_x7P9~NWq/@\8s׷BG3NgdyJ !9rD8:!@,X4teo8GKq Vxh>΁ٽ4 p,f~񒭃(+T΁d("`0̶SD@3ț~4vۭ8cs8k?5˭x{epǎ)OFilZfqXf}4 VD`do~읓Ȫ}l VV1J)GY"" Ĕ)+a_o5a0Vih砍J)ᴬjJ] L]K^F 8wk~q/PY^OIJ!rO'd 𖺧}6^MFZuxǞGnVg2W&&.Ӭ :%ݣu+!N)gڏCָv#~OHO J^UO*Tl"HEjnǝѧ*lK n^ [UY| ›>3\4dkr@B5ҙ^-̶07vӉ 8-Gbp/ț5]OW%1ڛ$n9X,[c$G XtwxY&-g"(c9\c;۬?ˑeNb`|(Agltz3w3 %g)Gd1̸ ʬU4)u_ZUC`8u|cR.MQvWY* 8~Qt@Nފ;S: #v]PmHtyl[,2"웗ƠAO;ہ z bl7tXfݶ81xVV+!8t" knkwZc|w9w ' n#a:Cࢍ1!!b!g šXhxnƨ~39o ;[ ?B8vsMBfXk$}S\#h 5B(a`7 \БM>'U@\j۳.}G;M1^]e x 1W(b൐o@WdpN2 q+͟? u_Dq] _qR j*dIi@?fHGɯ5<%S"!#ȋӿ ك+% OT&8LvR ȑ-Wg6 ]rA" #ro~|>[ \)|_Px||Cn~@(q$(zX/rMM+I+bnAt%#ZɀApAT8;a͇E1HID:C|mJ3I!F o);Ǐ_]G#4/X`C 1&Tޖ EV+z}9ޒїr-lYJi%rbvT; x{ze"`^w-GF49i%|\g&̗% BgtnN(jGy*ˠ *SijkG7I ^õA,&>tɖ"R7I"ۆ2pd{@{XwTuf$wrq %.REëo&"}w|-(9pJ ?Ӻ-)pva^17&Bxed@L ]s;VoxzEI^J)"m,YD1 f.+qW  fF uYь(7٫m,~X9 }dz✢ed3"uf҉Q8$J/8aֈYsts8 aN4p0 Χ 03~03jA8SUsI WЧHȥυV_U*e\(D=ucs})pR}Κ"xDy~3 l'2]f3xݞFwP)Q P:Cӄy=3&CO$m6q J4JlifXX 5Rȍ])e<*d˥+pJ03fkNz-B&zci ,:OH{SsWtΣ XΗ>|0U (kIk vy;Z0e"IIh=-)ZC9n^awR왌0Ņj SARP@4eN.QX\gDp 7SYHg03|9 8 k}HAfp@U_X#N:^/N6pw"3 ??O^:ܭx>~?Kc~ԑ[ʬsod{wK;1p")0tϏ˅ 75WOȀHvK}ܻIaau9}Ƥ7E.NīRwkkls+4YC[IP,JWߺlaJ9k~p̮Iݺ/i$n(1y,e}}VgB8sg j=T!@88$Rc8&Eɇ\1{HT j Z&:8{_5 j(M8'Ͽ2Py@T< Uɹ}܃dN:_'ApIWGt U˔ ȀyNk.琰۸e ^ov6 " =@.)8KHMU $iz'3Pנ^ù~XI۲D4 CSYl-5MU#v |&5nZ/ hk0S+t'c??`PRs( [[렴jxU˻c}3}G~|_* Hve;>x||—a(3?}·x|_[I(Z."|3?bSo7to3}J+σz]Ovmtk[fý&޹~NyR\wA ؿ_o;PdG<>?LT᛼69zƾ5gX\+/''$ !k&-|olQF3$"զ (,@ ߿rlޗ;a3)@ʏw!+)I CslQ7Ӻ3AG  ?s{(wRMO˾qQ9V`$TvW<|O9\ .] 8[M{S~BxaE/b8G #JK,e٢1j85@Rʃu%ه@OAPB1i:4H+54Bց$`; k ƊVc&<|7]QP v~H@ [+&R}oX?<@ѰR[ggTwWkvorC,X.Qvq< o_ؾu/O;D퀌h)y0A){MaK,Ip Ea@ˆyvK "y!8<o4vF~3q:.Xam9^v%ȋl^^^P?H: Y;l7%1o_akq 担^m}sOP/|Gc71>ծ7s k~0?? t1ҿ!yee (AgĴ?4"8g8P,1}a8O$A#jIzh) gdLȒ'gArw?z:4SVbT3yV\ټbV99zT^3)rb2VS9 9pER`{!^)!p;u Bٗ]*Bf8Ov=2uOx)`"`-3`#!tLq X?Jګ祈9env"uj ÙQX.lf0NBfrЋP%@_[ǝ9ւ6/;Iؿlt0g-ߞ1?LskM+ |ïB4|Ƞui(1lmPUYV:+['E~HT!3qtSxH9: Rg寉q'z~ oBZ]K,d#or2qu_DApUr` I_u*5A): l@QO(7y4SLYH'Bu3tUa.dUfZ9۶;,GpUiVK03]s;" ۼ d:܉}#w?M%@75@*iyX /rdY8ЭDc tQ,Ȋ4}KM.>epDO Y:S.KF9 k\1Wmx7WU $ ,G0MM~ﳺ¯DLg$~b`m-i6oyb3qdUՠA gk`.F΂O1d̢r8r-^CAp``ۄ:3A@AZ,,lYW'(z[NCAd|=#}e:'"NFjEo6&7KdYb]YJN-a۵}t9F\=Wx:RǖFC8PhjP&3MÆh0R Eej2Di\'k#Ayhf-uPFE2/'!!UBAyHA6ˡlGZu;`j?\f$D7UԺ?},(*$G߿Y{2(4X;/1b AT ԤtV @yPC_q_!+x{10#4  7g i sw?N9\ƐNA*e9Ҩu.:l[<=>`^9ژߣE@RwI]:״Xaa[Uؕ[qX>)J0ilzT7No6d.eF=_AeJK8P~W 289x" Ȋ'݌/@4aN90+!\pX2o0ͦޗ)@Bo?܈DK9:uKAe}P/%Һ4kOED"ᆳ<ksq{uƣ`Zj1p [ݹu IrodU@J* E#ƗqQ6w0+E=H&z@YW0 ~kl5V5&!"`yf:bA&AsJ(ɐmb(sL5RX{vcl02ىp-+N.CX#.,C0ɷ9Hi G¤; N8f(%)eNö́g?92Ƣ0y@;~WB $n\VGfMjgNrN'~*2E̅D:@4)dE"65?BrB7q"֢kv ;ugɁ5#1~Gߎ3Y(ceOx}] s2,9@2``ZrV 9v$C">GO /H.@BgȋJ+o_I=Ey<[|!#robEfypbuT N|ƨ|{0m@3c~ހ$'6u椌f?YEbQ~-@uK7z?@2PtykAyuvۊ*9XgVΪ_!$"!EaXktSe\>\bZWLo%hE!)t~2>G3̯ yaY]W`k;i%8lw[dYr g;U@tvf_LǺGڄ(V aV U>0oSJ!sWkԵEWBd+4% ? fnb׏oBiS7eKN%&@_V׈T $_b((MPJޫ?`q"(@L "g^4ur9r\z5 xMx(5W)GԉEk@-@ ;kTM꾵q 'K "Y% ?Ԟ\>宄:e`_2O#iӏ5J#F 1_nZ d́c ;S 3;|ju]&{OtTp=5?RXf!ϰgm #n Çe ڴk%M&5o[$ 瘓ewZCfb6+=h"$ QÀLYX [a׭FLIXf{D'JHH>kO {řkkqQpRO /)3tTqSҜ³d*Ovxy7S9>nєȂ̴~4EECikBRN%u@Ruj c-%Rb\0Ơ 52yƶ8H Ȁ9": 8O ;4HiC,v=gR~nWJ8k5\1uD5Q,<ܾ_j*Aw'DHțG>#9"O"nX.X6-rn;0g\X[aUb34[d5ٸ71)J3W߳E@ ]wvt9ҥ~(IɡcZuS2vc Gp$Nm=}Rj:GhpȟsO*@xvd#\€M) fDcH匙d@&~] w>M>xpԺ@/qYI)Ʈ,u=`n@Bg]Ba׸jHCb[:+bz [2UHE`!0H=dE L+!zD 犟sMs - J0qV@x?D:v+ ؅u[XS'tHS]f|d;ov.Q?!Ec,v_a pwBd~{dHޥy"LcX@O]keZñ?cZ$AJs,yCٷn-ꪒl( 9G*RI'<:O5,, -> k2?sf?PW!џ3 ?̲GZa=#"(iP [SH[Q0'J ,f(#X+ދ_sO&gΘF-OǠIydY7۔|lq?}BI+1 s##D@Ԇ/VBieLa%vEۘ6_؛m>(*0GA4[9>Æy^`X*+uQ=vC5V%(_.ZpV:7>º&>t&zyHpq1 5 pyL\VLX\/ŹLdP0,^:ːକk`- &Yñk&/X, ۉXUM6G7ih/S=& CJAE *0t~p92c@'rGTohT+)J)hh&I=?c@у'[:4$esHB9V`"sU 5nGBF]JSNަr @;^͜s 87A,ޥKK.{GdUeu7G r%q!ȍ~6jY9䑸s$N7*3 - r7#+'#<..`0a1 u]!4] W85ǜiAtК*KXphtm' Qgr7RMq_i8S1=[eozdi nK(LJjS|?6:K9S5<C2ZX{#yQGCUU MӠmi$U؄>À`*H}cb;!06JZ D ;p *QzsbPo{!H+#>ܥI蚅y׎9y)32Y%/ò*N@?5|fmr3"EY^$1H ,6S(6IJ ']R# AoƦDB%wlu{tm*g}XqOlWVp=|yC)h]SLA3ow_yfnRnٖwH&܌$8 ȁU X9r~'qv؀!a|sp֊%D(PU{,tm0,M14:xWRui޴wFLz{ɆFjh<žPu]3)k #W;/Aa rݨBywDn4~g$vA]!i+:2LSR.&C9X]17~Ϭ0@i'EsFܔVv󭵒[V|Yٵ"x% } k?#u@3,%^:D8DHZABX^p08RC$av`Z n2Ѕ&-=Y'N(vp.ra]{%Zs0`Xƍ`ҘrbDX5)7z&^ J;-4 _I+˥YN; PNꪂ)x۱ `ԧE)bby߅PD,Ѵ-ڦM"5:ٌ\ 9!po2 fm눟N"Luc^ӻ_P$xvIXڹۮTkBd{d8@ݔ.xLsݮ4B\V8?0[<._ a6 0_b])wUs4XA~Ơ*lha]/~>߫U8n-F+~;tGgCYUU`hmD`[q-~Y!pTMu/B H%NGɑϼhU 0~"Ye#kt֥MH6䥇N #p+Ear$ߍ9*r%[%1RvO+!bK$zQZpx _wg{=j݇l8MHQFS-X2 y& #(0u=&ɜKJA-ML.r}@b4R04(ey IDAT鈹X2f/ Si _M{`-O; $r\5zmB.@eQ fXYꡌ^)!qkGq4C:q?kDSٝA#f@)v!HH@ߧZHpNLssD2x`k;eo@;jڛ*/ooeYt!Q5B~mr`Dp~ 2"2\dGYTEUWxnPmh\^)Bud&}}d Lh0~/CڛMY.K΢mځP{2 LGٶCt-pޡ=U]qֵޕD50ڴ/Vǹr> }[g )(<=5 EgD@ ƬLJYdr)fql+y&1б'au;O@)iy961I$DM'3y*(Xg>ic4(f@o">`#ᩧ|~?B !J&CJ <ŔH;@! Btvϟ&F2b@:ǧXhTa%s5B)4G|X`;9uv:rM>2^ڝy*4|6#%S5I߿f\ZN5m ˵^ D5fsъzٟz,6l4#b`‹4ZB<:kQ#i8QKX$/E6X_E~4ZȎ-MK\RREk8xzzBg E k-ka",::}{Y2Gx88nK9#y"?xc0uYд8wr蟿|J,0w,e,ah'f D@е|((%?Ŵ}!ɵZ&$ GHT<>q %ZFFFV/9ǘ61LAysH %{Rz081-V,B $H( bQѶ-Z(vKhӄ߸r^ 5QQt%bpi}B{/i#լAg)B ~mST{vՏd}l/Ï1oCYUl7(TXQe {A/2 $V}D}.f6w!eеltNBԕ)x9{԰ChSFVP(>Hn`*ڳۏ .0 8Ũ9ڐ K(Ak) Hm֡Jļ}f98E1\2?RB|ԕVt U2 {>{Wty&%"&Ee8#A O dr ٹgTPP$fr@ !ZGKB $n2#'hb1Ly ?/'yfWz}7f_@-k^.n@ҴvGDMۇ-áىYsݚt vN,L爁"1hJ/hr﹯vv=j^E\a725׷-dI2AK=~ ڠ*خCuBͼ_x [&=(ó4A3:yfܧ)eńhsCP1@]D?diXOy?>ODY0py1.EUAi1)rĐ%s:8Wm=e5+YNvId8Oh(sNvN:8kNNZk8M7= Ⱦ_&9fCA^qp"8<|,ZЖy8&|-,RS89뜤YVsMKz>^MTʷxr(on7>`w8HӾAHGSM[ ܓX0"pq UUAiM۲MȖ32/nB jZ.Q'u<-3G'Q"%ֿ l~mC,aEgZl`Ƀ^ac`v[CiIOtͮYSh2,) 7G?U Ч^ >J=twp"-ECQN 5:oᏵ2xo*3)y~]mzb=!y8 ?>Ѵ9g9-Cib+36$˂蜷=Zؘ#1$eT)n렔N}cCc@&'$eRNccB)8:1; 1p4)9rO_-UO@yȦ,R{o' G' 8^FBIs=>lluԝ 6D"=WGVyR`]9k-v{^TULJv 8ψ pޜ!0s~d@Eo"pbEH.PWX"uU9^"v#$ nH s4=8+1H`z|8p\dG*]z+Z"1KR<")%& v+"Ĝ\Q@}m] qv=ږSA*z SvfcJ)p妆s<ŸrHc93vg]G=h)Z> MunRxZJ@H>Pl hϩ@S8r?g^>Q$QߙJy#r9k{sbRC=J"us;X i}ZMW*}E@|ê_]hYcYpP̈́*֑[l mbs_\f-fu&TyΑזuDah|fS#H+woY+~GO:^וfy/DP&dIN|NDCs!8 PV% SyC>1Wwv~maw8aE?{t}Qg7{.J)euh CKL+͓31A_*S"`8)#nB4hJ50ZC瘜y7A7$w :Qwb/K$TDHAI@i-~gxi ,SDosJL)F/f|GŠN:kݱ_/Ƙy")hz@B/c4r9]c& {3WUۇ"st|+o~–R EQU]i<^DTN8M.X]EDa8Қ}wl6xny'A LO7=nL̊Ȁ^"0[] E~-RJ\@4]h7KOׅuXGBF^Z'Á%cl)ԏ9{#w!c(!T75 Cb1Dž(2$tA>DN@8OKM6-11Uɀ{(n4w\\p`>s85l@  P 0%00{JP]O{xa S)}4&xPD$ڒ { @Sͩ,{&Zi1%%"|,r (CNAEiC^D8ضEs8w9 0jF, Da T2qOg>xɼ`1H$}\LLhșc&BlؾQ_>S#z.lڐ@ʤ]a+ _?nP嫐F\n>wIV`\[ u9`͡Yg^4\݆kJoB(!0v#?`>|CN|dF,ޔ~?C'B]Vw8xU!n7I5/g5iK߻?_~ϟ/?ϟж #); m4~_O+&{ǏS:rnDk/ړ ~xm٪RDCP (Qo|Q MiYM98Xh`Zk֬ƶ)J ^c=;Z1$Jzv K QID'a+F4G`"qf%?qԏn&\`%`~8{;r8)L_w. `& [J[r{"HPW%pw1Mn i|-p>%>rs]"lgCPWӄ~&oܕtb^ %˽UƧTwv<[ a"ІPlۡiZeCGiГ4Vl,AxMp Ggv%5XRyϴl< EBU솰;K}FC,3 9~B֧ro+72p-`ڦAUoPlesi0~&8c LU;4x0}y:wAEazBwКs/V PЅA٢5wĽub]?<ݞsKӳD:\XAaL)& RD|/]<:@Dp');k* 佐 r}u7o>⎣[|PEncM'0*p;,s֥ DrNYās{f_S`h4%}dϙwl{g MaAXoQ"C/[o>V_pB4_X)_I9i[+;#1xxآOgA}Tܛ;G8J3 =?=UU!%@.zs]WgkBk[ޓvѾ?IȏG]$#[J???;qf0g;~1E]#,y4mW><`សa4&z>y:,_nm:͵p0$Bu7w&3&?|;N)8΂dpB\(Qn"nzhɀ]N>AH4T$R" 8f 2's2Z#{&T~cmD\"#8U!ܢ*ՠ% hyOy`]o r%MVT80sy|Eety>7 戀1 PUx~z>3;t'!0^U _^јW%nO#bgM`IJQEQ(K잟9(Rshǜ5ΐc( mEٰe@Ӱl!Or-A`lQAg]Bh|;\lxnUL ̉1w6lpO)4 mÇ^<<*{l~FQl7}~1 ^uQ}#CtJAi%T#$+ @OJv'cpzs˜^r*ȘB6C9 $(.d'5Mmڗ/?rT{{`E{ ˓|C IDAT}+ R UUbE;^OLdGr= I \bp+*#eInk2D-F"eEeizB&%c6l6n#P6#-}Rl4};yRŸ[bED=1KqG{|W]Ƃv_AO?y|/ᗿvOx_>}‡¯?C z"xk4( l/lis@s/'l'48^2o7 maV`$ov u٩t`s`Ϝ-32<&CfY8YJQ1Y~޵}AALa{ TVP\&Evhq`7c Ln"k ;pp.龾ayao{s}Zz4Lu\(D)Jk{Xgok FfX Z2%\w^;Bb J|[~lnkћ ݤ|2`]"EkXp >puXNFjxu6OM)Ywǂ_|sș `"V|8`+߇~y>`q[ ^wg+o@>O{C!)t~/?%`~q715>}ܯ8nOPĩ8ʽJ})ta~ .m!xX8sA) Sp;;I_+U?/=I3>)%rے/嶳ϿW $}@ t8}@=B>GKH< en;6 `*p`}xذBggf@Z0fS0O;9 fm "ϖc.@sez4NYDZta{SWuw?kZ<J+K#)jT#gpFy63lF1sΡk[CÝu]zVm v"!)8m!d}` ]\(x PRNAN5w\n]58-U"l65 ڮ,F ȁ,zEYz uqsqh wXo,tTfQWܦ滺l2"_g0Re /s** \O V ;O"Uv{8ۥIRuyy{hؤmtZY5s6cjnyJ465=p,hL vMmfD9D:v |88SW'EyHT x>qr ^p jAz@\$m]tW"$.ވ(ƘXgC7ZyQ[~;v96]-@8Gp!x8˦oZkhm8R\@zf32{P i^9$)MXpˈHh DkfKu=stCw+w'̓0kR`v 4Zi]f*{֕I .ܐ> nR]G;(P5vYrۗTx2*Qsd_x qVI@p% ֫ʌ!%)7m lũKpWY7:nFC:R eUB+nC4p>ε$Cz]Ji@'|Fq^f0>k 7xq&HO,ᜃ1 ش0% ۮCvp&dPJ'8Fwq97(eVT<)6FR*?k;tmieWy*Ū>[]fAh&9d Z@^L SJK:ob\W`1ZqSVX"AA\`KkܫKz$ eYPS?my褃] ʪk|4n@YQ +WroD ~W| .(ԨMtJ+WW9kM&p8mYtm.3uuȀ4VȀY"?p[2"@)sIʜX`hMp\D!DuiYNKvhQ*$BZ Zd&g٭ ݃ AUD=p. q 4:@(8}aY0 9,<$e U??f*ݸmg[.)Kw|+-kc\BQn7ޡmۙ_QsMA%:rܜX/0kvIfl"^ Xj2+ = ֯aC̲UQ(;/RKUU=QG_-/hCh۹Q~#"QUMz'Xڜ(= ~TN>A   SnM25D!#qH 6 چSg5-7}5BQe6VE ch6]';0xgAOB qqp(9ʽVpEkJ+Nthc!>hȥ Ǎ7Z9Ӣ⠋J@4+O}pJ+S;w@B(Tc3DE NyxL34(=AZ @} JKr JdBwx="cI"t x&c]^L7fCi[52؋#͈z[`uE=(>@9!p=M 5ap륬M䫛[3'Dΰ Hjbv6%=Dw@zz$eDlQ4"@~f,kSζi`fSs̡~ڠnPojv?,7uR^_|ɥy %`ڷ-}u[qzHސuyR3Q.)3D@"ڶ<>`{s36w'$kM!mYW<>jr+-)qzx1/v{(I7hBMr\Z4MY+R'AM@Kei0?ׁ-vȪZ1Lx|űxggŪ9& @d:RqpS6`a,ֱVb ̨9')]!8X xD#>ι)\Y^0]R%XZF|V xmSU6unMo"j^K <#gBύg|5ZEs80 hZk.>)^ 8yΪd@~yD@n֊w h;jOqrHn9(=||hsj>٠PRXhEaQ%m;7˲L۶ˬť֒B"L򒻩q$2ʇ6&#s< =@ApPdѷ"fbwUf7|_O]~ @a :|G4B@sT;y%O|i@&@A䜗aymdHfx}G@d-8LB (8Ӗ#ipN= @na; RB02"煘"6*89FHE<|׻SXgᜅw%áDxIGp Mtj;d 9J;E$c˘ DWe[ Q&;zit: wog4yrBd\U.o4 E0Aڮ l Nw {cׄLNFYa:MvMUnmY^:d6\/bN;$/>(3 Sb٠94]+kyvV9j~U )0yGtSY?¤C[l5w]hl H);؈l@)e"k҇ob䚎c":.wPK ${wˮ;9@#V{x-6ƃsM]n=m;~{r=:Khg)+ Y_Sb9 oXd  ^?G.AJ;4w?{˜ff}@׶pY &>~OH8چ<{ HGƒqG\ۡU=[,w|J]gEB+I/K>sH9ARߐ ?I?M=[ת'W|d$J~L.KyGGa (誁*(@K| f4/ sNiLJ-S5M+{f!{;6ڪuڎ7VK&) l[8o^"nC ܔ kafUbB UhEQla;RB$Izd@sF%K_+4ڶtKV5g20K.BX赐ẢSssnUkKimXk C1ʌDnA0dL[$F}͚,C 1ҘDy9d͖ `s??لm {gԛ ڮ/X3! X`( t!7>Q%P,HK3u-yD@>BWܢćKI @](ʘRPI[i絵>h%^%cR%@vdDž`Nbqs"C!"紃Zѧ@K#Ə!up$`!!!ᜃ l AeILe1",@f;Vziy H5̰V7~—ݠst_MGJmӠ*TU c@+{ӾD܀ \\_/bNԹP%8za>gNp~6[EOKfIXv;hEn6”<>7M Tmɔ;V$e3 Sfx"g0€8ULʉ|_'gQ"榙x[F@ ڽ)DzFU6쭇u6@`j)ى h%$tV A S)$8{AWL:n>fW|WiۿoT/a-5M3:z_D{;ΐu솇#UΩؕw 꺂& IDAT] Hu`iMxgrbb@{.k%$J` k;ce/GӶ/B`Džd-_-0GW"z6 @Y:2^|mfl@Pڰ5AJ)SZvRbCˮmgTMfz,nH]FD SDםv?m05E:B@ sJ;; xG _JqD|gL> qUF E̕gZZzyyDJ3A7/SK<~9wL%bq#DS Ox#o_s?0fE{hc?/Gyoxr*`m5g)e:Xw$=RI1pt;H4U 7JSD0$ ]%Č ޫ;^|c=SP+Ϲ/?\( 60C`ʗ@NvR<Y.K. Nju>B15Rp܇Wn9N _:gz[VR98,ȉw*ga2X2(+H1+p~PXA#w Mmĝ 5]TvmF'\C< FfWhS=) TSmۑQ> BZ+ٹW75цwݏVQ|jwS;8-W)K<=8Gc^I9"ILh],3o{%S @be) #kx@[ĸ`Si~e>K1Z f]gq{qc)8xsEкOi*gmz2^A`p#{J .[&LOwof*ޝa)) ;mfא\B GI=L"u yjM[V_|IuX|ƹmݛ x̢Al*|8u>xky|H/ pj[=k @wREUfW+0G4+/}VɾPe\{? #~tߡk;;>,0A`FQ۳,Y'[>UMK]@@ 3{&0v8Ǝ?1<WI#$K }QUo+2jWO>ro]r[|6C;t"QVXGϊYHz)n5[BdSQF.ъM*ӛ{r#@\ 5(!*mix7/: Z 7<7F1Rp D_9km;g~)lcIMS$A٢Jn,kCQ?AA& & |Ю< ֨JkhTݸsM@1痃@P">4TUa6#K92 \ \D52hm7hk0`bkeTZg_X?9jݸ1g&hF𿈶Y[B5>10cݻjB^H lNOqGQ!Ľnm΅tɾg$HKf֐Rr !@FA$.X"$O$2qYAkh6>u`|5qPMB5|=)!H9ZXT#I5ՅqB{0"ڋ[y(eBإIpBt#d0\)ì>8D .\[a|pS=D!wƚfC͇1$WC[o]o}k8X.L7o=wo=ߛNk ȟ-zYqap+vr\<$ P+ZA1oԡX)!!k+@YzS%aQpBd~Ar6/k[·uQq :5,d7 I&7VC( )= * 4Z )@7c$ZAut0FC3%A )_&?DQcLy1 ڋfG' ex2 /ì>GAoKw*f*gƀNZ54Ae( y*TU@& 9D~: 5~lGwO`i ľ޺ARr,G*S^>hF8w!s FM⠀dA&l >!Uk@4 ֌'2A -Ϲ X.6B|f z8+"ڨ@6,IY؏Z @"űͅX\e \XZn0BuXld.{\PCyvaK&;H L)tۯܻK)|7j}Kkן}{f?)^o7; 9݇<ڂt~cS{+,"eժl mQo{{UV,EY Vh'Q]skzlk-*PUY5W ~hoFtvLZؔ3-"+$w}H)fE3+̎Z ٖ6 ۗI68I9 A@JH |$# ,K A#-XXuQ竲VuKh+b- sm\#8/5iݍ% ^׽i\=H O#2K 4`(`u.-"59ewS4G<-@;X4x7pE?b?]qڏ>'|~`w#f/wٽ{/A~=ܻP%3Iߡ^4E8}>w(ureY9ۭlϧvg{~@eKNI|ŚhX 6MN#VsD f02R0P@G Lȹ!M3yʹ6ue)tb@.V R +rdErU4'Fr o<L`RFxhu`?`S \qM 81re)|[9l%Υ \G3i8RzP`,8%T5VѲ[p@U, ֥ >x#Gs2{;_z!@w?imk7RnC$Ԁk9DW2ִXkj9X( M,\D'(aqi+^B)vR뿑b{oDdesZ->C? O[e<'~ǁW_/ſ ~_ l{?s "|/jf3HS@ӷ}jJt*mn_}k)$,C^YfSUFuc{Z@f10P Q[o3a%V58s(Pp!fGmE`@@o <'),V5YXp1rդ ԟ ﭘ=4w*^rx B]HԵBe!.ys`0k<[`l|ˇbj9fy$K7sMA}[%|)G'G ",NϐP.W"I`l;r[O=|6&--Lߋв0ZUAH il c55\8h]0ih ^y0d 0 _'`8^;G, l# G4M'~KR}P҇nQ,tXTc-GXn  ڗymhy QΟކo4OB x p3`5 !Dw]6F=iewbŁb3 U{@n_I7xo_~'SO}m__\A׸{} e7g+"_q|0R"KSV8hݢXcPJeYNw`KrUBU5t]`~4cAXp<_@w\罷D :R> zxp L$kDU-`Y4&cx VΥՎ{bY"`YcVV%lO^F;Hk\-"A $|4q~z7o;cqH ) IDATl9O߹9^ R$裸w.IHye]/D #v:5@B@ʄMecz5l,(IMi"-.d!R< ՞qAB[# CZ@Jq[W-ON ς_4&ZuL}/j~ծ4Z pQU/\BEgܴ/jsmlȂ``̆=ڌ#M}[<m!$jy#o4O|Oceyj6/*wtaPS̟ј U]60@vmN8`Q9JͶ  رhՇ*`*(9fQS>8u^Ȳeif ZJDHM-,+i @h}:`wR\qXrd~|,C]b|_ͧw㏁,+RAѵXx  `Fq7u+" ha7׶'EJNG™{{qwhF3A"\?UX/P5Tk@B޹4%! dW͖]M;.+;uls@L)o "q _ ejYx4h-,IVQOm E1okjw~rϜG^Ͻ^}oeUE ASCro4LPUl-Hnd.X%ʪvFb@vYl#% cB>YΟ&n.@]Sx(0]@KKּJ!HJ%g1sj{>c|0-c9IV+>ڮr! `Sj'nn{dJ)d``9Ț۪H;dC?6 $ a*58vSA8m#U],t׫wW%n>dbv4iͻRkջSNA&2<Hɹe"GBB!kp.";\rykRVkNpr'4J95n3,?dL,ˑ9BZEϲNq@e2Cn#|jG'ΤV Ŭ@\av] DB ܺ^4OR Jo`~r X+bҚ9Y T}L_lj7k;2I$ LXC}߾ uŒr{f8;l؈ d>33#ISH!],\4Oas$x+m> t$Kԛ>`:\60m4߱CsRuY{J? =haFϷ֒֏D)Ϳosy?7B~/~|ܼ9/~<nX[}a|_o/ޝken|<.噷&tHݯqs,\& 4k7x}p.V}`]p= P`ZB[EQ@ >/>{@ `$6}aֺp]cHS;5a/Lxm ؅ oHC<Ӧo@^"?? ɮ"K6B&ugr7%#DlZpgsrSϱU0)Z"X].@DJE `UrO `{>غީM~&Q.\~'7n Q.WXT&xE |,tUwȣݾz(ZGrj9.MR^}l9{Z`4Td7!;v%R~oO)X' hr`tn  n4#Bg `$)[#wn@BX0οڮI{KB*N meʇ(CqkRn[ez"Q=&ܯ"S&伬ZoLJc5)|o/?WBʋ?GxwM |O/bMG1|??W4Oqj-SM+yoyϾx]óz—媪u}nk;|M|G~7n=n>8{-w^_^ywNt6~w53S1 AY4MPV5%j;銀 .+xxd-_;l>|c 3,O|Ņbp])NeB]U1& sN Tw@ȾNV1]VrmzGZKY"{k8Lm\\\ 1:!oh6X&u]k|~Xؽ<#S#Yo =_|ri%Nk~1J / Y×BJ)cq@2S5?iR I3N($E X.&I!Q+!`RXK)9o]U5d"3?4pD^Xm@R M@0x >v<Ȳ iC& UUCU,N؇"lvP=rƤ{# uaAkD{8ί%|/,|?ޅ>hg[uV]~7}۝_o+%laLt wW5u),rtA.1?}x @8n>{NKY9bڻƝrwB[gjx۞h! R p/6Bp7 `_Pŀ_EEQ@ u>ӝ1ۍÁʓ$awbe3rTv;'$m Awlmt,ۭ V@E -Ý4}hu}`qvho_ޣra[pֆttVgnkT0!2`VCUTJo"٬2?BXhM)oj) "Ź"kAF7xB@D0`u$KZa帯]t)!s5}2R= Cv7[Kv<{xb_a'A?^!*|O?ȲA3:u-jCr/ Im/~'"Ȕek>g<ˑ%)G_U.A?-PڠŢ"@Ũs2M},XiYޗ&w ]  aP@|byͼD\A)΢Sދ] u-0h aP 5Q p_6%)Y4ͱ\.P-{.2kgZ7B}B0٘[ 8hAGKҥ9>VF/%!B&R["NHat9rn :#nZk2ºLM| Ѱ ZBU"@|>zA.U!ϟ0))wom;s޺΂A {i~F!A PJОF? {)|vKֻ No~xoTMz#є5%DHB85`;m$HJ$KmϧK' 0J;;_Z|fb /;l7t7vt|V(UYØrL"s^7qݱ∀`4Fh,~ZqzO) YM$MY)[wPGm3iB|4 hd Aq eY9( ,7m;V'jD5{<yժіK~~9Gō"l'GLxd{O NaGl2`+%7˪)ѳ/'y ZHA-_vIGVӰ = kjͨި A"z oB@ƃ8{jԨ1vgMu#dC)o(@.4Lh??whoۿ ~=*P<&1 n o17z'R CUi҉>J\ٜ5.>,wBݣ輷Vs@ilʫ֘p_lе,5XM, auruBSFA(NQbwj}oesOe 7,hÂٲbdM=ך6ˊV܁v[k y2!jm&9µݻy>i[O6/C?"kWtfvp T=ewzl8(_L-Iʃwߩ^ir&[.On޺"k Bˇ L`kD7R(r=&iti=ص[A1XV-5vHc:X3u$-akVr$I,P+Y~Nl,ۓ(̀ 44Ǒ0~5kFJ`l}?-h!D>!/r,+vwvGw@2*B~wDqlaqz)߼ !%N݃j $ $$VcaL8pmvDB˂?>Xk)sݴZm1hc6/ rƺf#ԯɠ ?1p@BH7@x c91=9l濷F<[X(72p VFE#^$Og)WP9,lG/_i=\fB*Wxƶn5߃ _Ue4I Mx $E mlrBS}aG@ʟ/@Ds[7MAi,vgT JH IU.׳\p. _u @$JՍqdkh| a Ҕu$5Xxh]y$\aQMI(`FU@L4B~l /`0==ax pJ#?hkd~|;~)Z,1;:W_[mgCBx^| ʛSy᷌]߼{ vӂU]sD~-\&gR5ꪂ&K>.ZNcMpRxhh}. iB1o8x5O]IVA$p} 馬C΍kXȘH$cطjœFm-޽7ւX`,݀㊿GCH{\пA*WA;6o]4GJy>ۺt[=4;1W}Lk6ܣBN;5ݒȵ2h5kʦjjC+ l B scQGFc?luȺmfyD  N 3,-NkYwgkm9#yPXPBY Sِi-0h֔϶%)@`yCJb> sv&!} :>2lv3hlt@phӓP 5@rz'7AUɧ\1dp f'MsBp>' ߣYNkmqњ '̛G^r!NgoE,S8&kC?)u5jQ[.o!-.B&IR$DıD`5*eVRB а5nlhA\ZE & Jp}5/ G\H{ 9ЮoioK^^"@H* uYv+899A6O0jZj2k߯O=l/n\7^ VׯqCF*W(W%L[&el $G'4z0jҠ ue ؑO'nZ1~'NPVȊs[v@T"wDmwIxӗmvx6tȱZUc\l2@-4(>igs[NB9#(0X.VVu.8~Rg/q¹ "bZ1C^Ue7hO U?؈obɍ]N^saM#s SOv0}˭ۇw@ۯ}IexmeYAHr`qtO0aXȋ9wѵk8>fO` IDATZ`%ȏH$#MYV͛(W˵H4Dg v6>(ڥcAE(PThB(FCHf1BHY¥0F#<1_xS_k,SHH6/]$1P5Rе"K! kkx{ю\65LP; U+(B.on )P RaKY6LlG02ǟX3m} $goS>GxLB"eD89>%5b;uݞCx KbL[/~}h\alV@[1zli86'O<5puvS"dys'=@!+rZC({hoS`8txniރD1r)2quLaJ<xzgh #H! e1zi nDJq ~|3iWThTZg-@II;ǝ@fg\^7k^wCXšA@A%.Ŭb U Xoml~ydN[Gw6̋ EUa`ڰR߾C]w]Gå{8BhO} $K0`-A+t xw}xDuϺ}. ^b N<""~I٬XV(sF{ ݒ,jr W QVԝ*|;o5u%\ղͯ '\VBIf-$Ţ3= @Zp2a)WsOB[¼Jt 4X{ Hc GHD9s!KBLRdy HhmPY8p42_=GaRHBXa`ϲV"zRsob,YT3Cpi-Eػo7r:M-mpt ~`ͯPOp~3yFEk|sW^ޤI=ʦU7?t%gWGĽwd[<6E0 D`^̀0v8(R[d4)֘0\,O$ZoiKA ˈWx]܊֕%!ZB ,#c A 05/Y;]efB RĩJhj 7(wkmzB .Ʒ^z 咃)Ai4 !VH&S$Ilʯv:,8n>ϑ׉EH(80Htj+N1!ټ@0ڠv ,EYhK)hȐ Zԫ 9ꪆL̎kS=a>{~ڰ>m۾mSe\FYH1T,^gUwf8;C6h"v ߟ4ha;M<oѷf8 ~⨨BJO+Łsi#؏S?i )e8x"IR6w٤,u[Ted!81V%*a l3 oh6Oqӎ`S4AB~̎` 6!!M6i92 6ΊB1@d4.ccLpֶl]G!؊  ԙ$SOn{wЩ7'` 1H#JԊOOQ$UhB!ZUҏF$a*\.F~O~y<yW[o>MGn?O y_SIi=kMޤNBNk,k<O3v<;=/Lq@`:;F2ggg899FgX-W(Մ\i}٨ _{xVY,X8ww} r},{^8ؑ !p4jP2N;y wU]Ak {h ]\MU8\$Ma8Mpvza]jX~5M4J`mkLd*dօFl6$ M?t5jʱ8R}5  ›+a Z&10DMaDLuZԾ?? HX9x!5l;9w` LY+6hθ]DF۶R rc @i M. GH >V S- Ffz6 *KGx1߭N[7/_&w7H"z_Ŀ~a>?Z+S+?? ;p3 `@e^&%B)>XUC]XL=3r9:pv`w~w|fb )%fEZ k.'?k ^4,?J+TUQeg젉h?im=zڦ#=+3+rl>R,}a`dfX9"3YyB 2X-QW[;%Ȏ3ߑ 7 w;;7)DhDk.4܏*6>Lbj7ikS{kJđ-3^2$y)HJ`/<˂ׄ[j qDߺH0@sO՗R@HA ;k'MEcʙ tsI58" _@‚ )uR >1|8J\ZHe@!  vZA&S<-o'+ma~ D͟Vp÷9 tIa }oSџ/M?}Ggy-o};~~#w|wF'*LFDhtL>\}5[ˍw bk\#L[j`KFuw}r)qtt<ϡq;~6)Ƿ o2`h)4Rp@Y'!8@bl?Zm5 4$NIMv}MP+ٵ(r(Lf]@0\zH ߾M l ؎z=MvmzM)biFD&R@k%YJ5Pu>eH$ |5pit]X$8`x*a4,E1/ꪄR T׀l @,k`0Nd!,T.ڿ*9nL~pT!\Ӵi Hoea!?1N 5p `EH)O`"5nmTX@g`wΦN\,±W~o|_Ƈ?Q?{qz>ͯg, y#Үm~5]|Aua9O~D2I%n o-$=[/־1 BJ̊yaZڀN| 4-l@S^XwڳU~* #H MRyq ^ ,k8YjJ,`M8H7'^S4k|`K [UeY`Ѻa}H@X KCۀ]~,N *mօ֫t&~pqɭGp~?ĵG{PUn>X{lBq'-;Z@FɅd,T_kMMZk4`}:2aaTZ}!kF&$DA " Za]?? 9|_CbG}B $Y4ϑluj t+A 1-kmCπ5<&da|"RdYK<?bwX kjFyĹwꃗG=)_ S6w>i, >?Ol1|/?OOO>x ~W^?M:m\PD0ry;BL.-zr,MQd(_\ д0 QJ]8G; w ;VT,Ͱ\,\^0'r%=UC@.k6ɀcsY֑)m MD|6*8nia\r  Y ,=aisa1Dklœl 6 N[q-$}uRJp~zKw#|Q~۰౧ī/?;~D=_`W-_Z e'B H֚#k )% TU*V5P5P.W.nu0 V@(ORB!_p&B&&hɺߐ}6KW*Ƴe)e ev=v,6Ԋ`'ʞE ~MXQh y_$">m7.h qX-/ j 8!V?$C<( ňF=@k vswmZc8㗳PZwXK0?WkDT"yyQpݟukl/\ۛۖl㾹]jp8@@>iV 1Hݼ)%n<õ7ƱIU ! 7㏣+$Izե;ݻXܤ,DKXF5אɒOϠF]0ֆtҰ02}UaB&,|pAVr}ut"`a?B1!`ƚM\A >/n0S|, FK (=uޯx50<_9in4 4 RU5jL!U lAA(gclѵ!4;r8T0 mabsDž)}?_ԣg^_'{ޤC,\#fiŲ'@|ϷX"=64 cdc ʲ"͑g40` ?k8e.;!lVߢǂ.`4K zܳFȵ/; Dv>ZiyJ_cz"~G4W+ i5$岂r2S0 |Y&kAıQ 04 7x}@AEv*m\ m< !p ֫"3_"kdtQ۷rPuӻw3C=i!z(ݑ[B I"FdfS(h#a4@[4N+A5M_q9"$ >?5Ӭ%IΚ@U ȹ0w'TUxo{}׍DTU&ޡ,\ta4s,S`ab ~AH$'+ h%Z0Uv8KuS 7H|ὁT+iel>Gvbg9߸~u_={i` )!t֧&ԻZQG ('@`Xw'58gjar7r"+pq-e8Ak㴎HS38@r8勼Yjg+/uY!DVʀS*,A$,=g}58Q8Q,_Y#M,W+UI1#Rw"7cmpZgr{ L#Buj5@ɍGdigɭ*h'\f)n=8T];~JB,q \@pzp메(ٌ@ >z?q jr{G"V a )t+YCؼ0 p }UjgE 0Z:w!FZɍ9%s A.z7ᇯ'cyC1dAm:݇8 ^dpߌ5,{A~tƘ(VH<[(\ÿ]A>p-%oK{G]sMB<g=8w"6qv ^$8 Nu g* 6"h͙\كXtwc',T]c\yA;S 0"@s'ug,80F$e|q2 DYJ3s4XxUy8>+N+2kd&̎ߡYrJ$Kg9jUc\A)NںG\?-őu-Xgk;AXna>2,m۸6P52DPrz.yxxygW5I?+rV+ݿkPay~:tqah$iRHIA@ 4| o&AP,  H8ZHd9u 65Y wZb.pP:έ!6u7ڴLphZ{#Ds!!Dx L< pKQ>Ag7dȬnVլYs5gc5WʑMeUhw`Oy~DedShpy?d=K;ǺWQ=y уS"KV#kIU[tW4`هw'u[%'bw8vڱCL2Af ä@G -UHYCGG+I %h )3t&V0:hϕ9\BIxz IDAT`Z8.cxek 堤Ѧ;3+qpsA缁ז([8Lˮ+:UpN 6<ȔuW9,-0f$T.2Pg_h~p\~_>!syɳPQT~y>*Rx[8[HYey[lnn+Pby`d_yPKӾ/J}Z 0` Q0ϓ03j$ Oe眏Py_z^z-<8YB$JAo}7$ (1. Dw1\S|cs=c0`LoP *RL ՃO@n:e5 "/|[f{(X:7pC=aa]eSxç.RAd ;Z>܍/"(Wo~Wln^unuW?O/;OLf0wO;GsCtHr")$5an^Ӣfok<˰J$q]h=u Y)U%xk]H^{ s% ZDq{HYGc%Ru } R_qCJ$RlRY c9W'%J$ p榆*=[>{\d@*"v:ku͞-Y~':qsnD]ר_m59@_a ~e!\7? o.pg'O8[>>,rfU;.up00o#65A)ޕg3Pudy.s`"_#[i\Cԅu,w )ck!EI Z'vl\73"O!9>`EQ_k B"gogK$ȩF=Av a1 ]R"q\翋?>~6~~y?o }+S~_{ipQ1p% l2igmln܅^/]$R`&/Zk9n7Xk7d\kg-uJAx[k.JOm+PR!NVNUn k)w10FCJ c z\³!B"vsl t̬71MCOMD;ݖǕ;DFj)HEDL'F<"DUe9c~[] jkׄj/Rj> V xs x3z6+s(`|j;{ bʼo5+:jSHZ1Z-I )y]$ؤg4\5 ߃aG`@13j;QLӢx:C:k'mK a7YWQ N)JRa%Ϳ98=_[pz9c4("hIG jfu"Bl_DbVjLJƤkVIm@XX(њwT lYy|ݟO!F{AbKvplJ( q*`EFɆe׸maРwFG{БGÖ5g\2K=Ɵo?G _'~;7ٟ ɓq]$w'su|Uup4\>8f"Dρ!f,I q#"^V+52 E.I@ {^m>Hz>wWJJI'q S4 WQ@ͺEs%󦩶J){@I lg[»wZ;E`'v>u[Q'иSge PѥE]r7ԙ@.9t( 0m2Q-`}(0$bRU)A`}. S1o{L9у}tB/zuna2°ɾ$ 0~..!0{Xc6LD;qJ@%.?nZsyo}EQ=l!DgFAdM-->Kޢv]77ƒđƜE]nn=W~/S/jgoG~.pzr:Xoe{<;&.؆Co>#'\.bH6!Ь0@ss M#+(a2S6ꛬ}x6QqCѹGg :B{"x?GGӷ Vʛ>as K=`]g lQXt;eU^wXcf{zTjX:-uۺ(]/` PנQZr=/ ڤ;Z>wksCxp{ h -55֛[W۱5Asz@a_ f *Y>jSbE·7 E͉K"D宻d*:3ZS~| Jb%S?| |؂!?\пЌ-M?ݰŚ|zSG(jǽj87v?93Z= ~GXN1C8l_6?:H+8&~W:X,͐$ XAUVCXo\ 3%7ZXXRdi;ԢyMzl"`lE㍫zܰfSzVT]@x Žw#?52{Gmح[X84c['l}{{U .vtIl!?vi p_3w9j ^\P>9` #p}y %d #RBMĭPRA(2% jr:^4dٲ$gVK].R1_$ٯB4zk PI\'pֲ/6]x`( )dEK!?w-| )&hMauQ+daorϓ;`P~Cڃڃ2OЍ#Մ^xq_z`Z? 5q?O%~oC~?m"K7xşKD7qozk#>7d3Hjl[M9P[~P@ "'ZkQ4QפZ?nMn Ds( Y̷_6p` Y6A!b$It(tg۫`]D׽\aeaϠPt[H!3-X%1UYl#j({xFnID8>=/ހןo>9L&B,EP F/2kK**wCP8)+pZ9A>%Q۵Qk]TŽՎ6آЂӞO 'I Sl6) cJ#`{Nᣅp!JV+9WW_g/q{}g |/_ 2m+Oms s|x'ONq*Շ4`}tT*M\]#K3U^ H!A 62AHLu;a3<; bS* D Qm`,[]oomTC*8!#96ܐMmX N8Wț-&Ws24zWY.SS[)YŁOACLƛYSḥ_cxzo-FN;;X|}y{I=K_wT8/|[m' cݸTX8$   uYZe}ޡp=[ !_Mcn*[CCʹ95Aȼ?4w5`eJ w̭uӌcXNg[};/O`]lȮؒp >Eæ%E&ښ.] mS2h]qn*?z)#@ ]o~W& _O1&,z{͒{h_5ՎsȋQHSs$h\Kh[-uB44پMqΡ9Tm6z dI#St+_ 3삽DD@_% ıD@9 j@ .ΗW@L LigIrPRj>_D_kܤ9%3kG`}rkQ[rB)8RIp7[:mQ@[8*VЅFz78:9յ'<<L4{Z,8 'h !ǝoɍ$@uUie+ ͡*f^w1wir<}hC weC Z"65*$Y2.0TB'Xo6KL,.}&a6c$v_Iە+C7۽r=jRSc,aC) H @ 'j'HB(HbH !z^a#XAK`?*cEduI C9bt4%D,|@!}|Ly]C,4*9"''\ٯ5Z:->ߩ}Q?4zT o#GB,[ ,sx{2J[}IXgyQa2vs &D$%n;kiC~@/!pDlPPJ{N(e ] jx)2>Ϛs*I+ ڷgGZu 2z;H= =zU<] P?'p < DQdB K3[x^y5@o}Wguߔ6z2}[,|Ry1ZcwسMluՍZ,yBzr.@R[;^.j'x\Nw?K&)("!OieT8*-j+.6k[!wj]BmOϛn} {Qi<%]tXLqo!]2s@Ġ  ;p QPm  1dRBVZtB)٧ cօn=nQ} U?VɂyTb78e '%!uQ`؏حw~n3^_h1Q qH-)շ L^&h|;s@:oe "U/}Y'lo->Fj{̜}\a8YU|Hq?&p~?;)DL`l㜃q|? %1VIUmЂޱ^2>}$Bڄ ~d pΡ( i*͎܍ lfAռ-х.|7T_/D7N@Qet T#De dgy"RH΀FU2埵H3E1" &(bdW5lխTsZժ닋q:^x>rp 3`w^6r57/Y! IDATƂ=+owZA>1ZC4ڋ D'] CA(wuQᅬS`Meݏ/g|WM*B_w'3Ay0;%=:=? F="/gʯlkaIn6~kNG ˋ!R6:^fz,O}~:wsV'c4o"LRz8g<~ٽ/1a,D4g RǛI $@kTi ǣ/L LT~:=!@j pAk<ː<ESQUSkE YW gk+wk\_="@JɾDuK,M& [B~j Oo6 }Wz]4@A-k9B "Akoy`~ -If̓`mf g` GYz9/_t|2C3a! A J4BĻրHmjD| @+:[UpZH!~Bc/[y7 VG$w!S $S *H}jfAR D?mdr_$I7]i2(bI#9+':?"`*#" 2{?#o`}TP-w=c:#&lY Gq M7o~KEI“N#@|B7 ]"JUHk@`u|jBg5."/8yr7W|*ϞAJ"q}yd.4. D$(gϟ#Z'nS J F3 N?En#^qy~(>>Wp7j[x,uMZB/q{s7_MZV"p2~  B(^o@A]>]OBi#db9Mj@PBP>G e! Xk!DS=|<-9nNX kPC2SFr;˞Ac$hb͸ /h2vhYI_}hT}:t="%Qd}E@RrGz?#tW \C;u ˡ qM3R5z+W weKLkB`Jass<;ď`݆Q~a?qtq 'Ƴϛ t(6k[$#Tyn..+diVz,g?y> G'ǰ AH7*ׯVxՇZ%8>9x@9 㐑skGư}Ҭ5-SEhN:a{yοlp!epfP/E3ShVv|p@?rW#@?3ގX&cԏMS ?J;g$1VqȲESRA(ȡǃw{SRsoh<&@:a)t!gkh71, {;p25~Dllee1B5$dxF^(t^"|5܄l5C~7PMTnprv E0#re2^g/ADxmI4\J?~֡5;>Ͼ-l%1noE)@,GZAH''R^G'ǸxZ`ut"/'3huڡhFKϩ (6rz[IG9 k,E])c/ p H`,qh}cNed%}\~8ꍷ0O Tq@~u}cΪO؆Y[w|z'ח׸<Mx7AS;K(8WT`9YXSVZ׺g YXFd%FuMIhVӆ=8O߭/6'YQ/RAe>ȎV:^p)@1h?;hX zs/Ma8e9@/7- v;%8(9VuytikiNy3+ZZ@Hs}m+.fzDICP+A`LT&9灣//HbnnnQ7t[:h5 T7mR_Cg9@祕.M~}y pCy%s@^c}r >@<Ξo9 !Ga;Xka y"fs"M!0i~sXa>û7_C BkSl- # +Q~N.4VާMk&r4b! JrFc`):8`>pDぎft['>w L`?I>޲k|7xh,7ݤ*AE]}{8„M8q4`lUm@6of/Z2QĘ";:K;D~=,"}cVl2_tߜqcTZԿo]xʵ0@Lj+dElÍIF}=_Pw'[ OD&v @n?F0)+7Wln9pOaEj]q^d9673$14i9ps^b@0Ѕ8%G' #Y۽nk}h3YfVw>ՑOOdnfK([WX{5ǸO1Rka=8P0FԷcje>'-; ޗ,uYZke9XA B^ Y ]bdŨ~ma9дP߃ۺU@m-{'Rq A9N}5^IƵmJTJޘo5]WH$cB  @o:PA)` 1'/j ձ|v֮;! l9E 2Z!8B=IY$)`esk">WUJA awW{8hTqp@z" 2vuB*) # %SlR[,V1%]`zoBB*Ix |NsjͶѧ)7RlV,05Rj?:}&;ѨS\?,S1Aw%H- v 9,:b}8g :H  dl7y#"Q}6?ls.lV WA PnGJ_+GXVȋyQiCv;{+M8(J)$BomhOFV$Zk߻ a@ ̡vW9 dKDk}`\Aj@L R&ЂXln7`0FƑe Fmf Gj # I!$1H T3՞Rs p`\#9vem]I%%8FeȲZPmx6ckK@j*WD'. sds:m La8NaI !6e)9@>O!@=0EhZdd09C_2O;`PiֺqCkycor&8-AS_bMu ׿5[Qyc3ޢO_J)4ተW?v^" tLjd9O0 cVIAmO`pҧ74T."(Ŗ7v`4AlD#֩C60B)ɄЈ#T#<)0(- GK7 ƺMX=.@DPB$EBy"D;>%P_[$$34EQ-Fp> |&0*pP]n,ѐv}C%hvO8:Z#cdygmi,ûT)Bcs.L+fC)qH29RAƺ;4TB3o7. B ˲\ƨA5lqN՘jH p&=XYG%2S;}i`؇y8qV+fdEq㐊b7[qY<ၶ^߅(4ґ $rzMf/ƨݏ ﻼȲY DD)dEM`[:O$F] Vy^ӒwzZ.3(e~a+$)tG#"`,D SUZ6﮸g|xTnZC;f0 u26[+n6Mc̘7[$|[GsbAx`j޿Y$5 C0wY‹_ݗoX% iۛ[! \o܏*`'q N9 穷A{/ ?C]`40˕ 1ݻwS8nPRVd5S}wJ4΋l$ ptT}Hxi#Oڃ&R+'y*Np><eٴFKwSx<筷:Xh),sP;  r}ydB IDATy5(;NTJ: ]·(sXҫGG@p+( ,Yy@2[AJ%!#I]qUtG$Ey{ :/+! OZ=`l̼Ob@DE3,wQ11߉1C4'vus.ob_(pssM[:ds ik6^d@E4m H/ujƁqV4`=KA/%ͦǢb"v\y$ r.ی5\ .[}7.ukE Z~(@G'0w%xHk*RWX6)t9~ZD((| ,ʛ#A$6ywicW=81[a)9l6h 0xk}%`4 _DW.WȲܲ<}،}rxBǸ*Y/_B* y'xYlYd7F4Ž6@eп40AQ\ZtLf(+$ۍ7M2% X S*` ع<wYMZ*D:DŽpqhܫJЮ`ZN?AWa׳UPDcR㯞K08ljUV< ثIw֟ڤ{}G Sɂftn9hm){Dno7ȻLm%tS›aS[`p^C)e"J0;pkk]L"D~zf ؒB)lnoaLl~"`hlj3!8$VI$q 5ka+Fۮ5]=8om5w\=!vυg wEp~nBQ#;FV/!tጅOER HM'C/e)%TA*!8 뼏z , WQt[^B sw5`ٗX>i.DEi#T&;~d9XaBK&MTFXgO:s>r`܈8ߏCN2gTCi<;z̀z"TL?$({js@^@wqaIk]9 Q6ڻ':`EJbȘ0/ L&аi.J_H]l.vklY<1@PZZ\H!aU XQd-l \fͱlt$UX^@-(dd y:EGS]j z{Dпpzn0sf"?7Uvͩe拃0ZXBJof8:>R:QPx!  j슥-f`8Fe c7H4Yh) c6_O_{H7o7iqFVi ka4MD;KJu@H onTMrUe^ɰ:~ZXl>cϻx Me#<4m[TհoVkDƖY kAr>_Bo@U7 zÎz$n+3.c-HJ _Jc2H!S1-sX1aźO=0!@pXNR8`{ZH y|Dđd;AjjB YJ yC{ Wj@PRlN*Yk,VJH]H#X "aZl1Ih)ƋIqQdyݟkxrs5T P秪:jm V߉Rk~ú՘; u*W0Fn@auY [ ܝuꢀѺAvA-s[Xga u G#Nڳ""yNv-99f9@9ec1%C)b5@T:j͹LZE)[v+@GZZ#0286[x#8 ^5 ߮laE$4rY3XDH)DQ,ˑL8v5B 1!Oꚁ8HǺUk mtRZ{ɭjgؓtUH]snOD!8iʻSu@= ٵ7AUc,FH)a-@h(n׵N;tn\YW{:"#}l=zOSai' @ގC{(/9:G-'2R(= _9 ۱7֡Гu, ن%`=GE| [һlKˇTB6Mkww8yG}];zHA[];/ z#G{7.O.=;?|fϴ HJh)au@I(D,?#Bu8\U]um!ekrzm2#R7Əі5@N k2S^aߔ>#juFvjH!^%D0y'Wf;$ڀ$#!X8\Gl c(.HtL+{L=ﻟ*k=|s=.r J2h] rco, uh;o}8^6awxg8rD^!ރ1ZkxH& {Wv,Y2X:o5Stuu2s}Q qsϩs^E/D@ .1{mq "`=߃#h5'5F\c]YOk讃xW@,%U56i]R,wC6KߏfG'2w)e; _)@˗x$< B*ī|!^~xcO=-ϓJx JE8yz | z)qݣ,EfHo7ȳEQ`}|(y^ lp 2RpKjӧO8n/ =G''8yr !~ ˗??$8-a4u~/ݓĮwQS2iCĮyoc(f,{9+[+RiʴwyǚofUH40ZS;R$(ypP*$ Q\lCzY56(1Np8 Y6yogZc-epСVGO[w1%pZp+ `:< KR9WE8 ` Z+ K} rBy~x o6`"?n =>.)ȳ2gH{ ~dAf R)hi?n %o@a5D^qUr ]]z[qFk }vEq.֥bjm8fYcfݒS*2n?L0(B$"078ҥ{#j3(u*OiMV+IU~Cw__oRJyQl_d!()t!;' W&y(pI ?{lp @@im7du%&9Tbo!$1Wi+z6 >'I7Ȕrv>sq2c|=9Q6y C4N?%.@' x{}ŗ? lY^( t_,ۣ5^_mR, mp{O_>%ݮ޿ XOO)Hp!rwu7 ˛(%R?T +E£1(T"ϫH ןp{Wb$&lMl?3y G@&'Fèdx/1Jݟhpx{ 0@v? mC/wߚ?߃q,M|dmn a׷FkE"-p188xzRᶲTgȔeH3(awNRx^EQ.ZkB Bu0nKV#10D @i9 !]ƺ$AIHb%lvWFpaldȸ,-[O>ܡqYk9JFB 6Hܶ9S4:j ;z.d椔¦+$./" ӭݔl88Xɠm]2eکD@L x"Vy P"lhum%^5n{q R@e'0v>J⹾T=n3#J` hB`@)"/`SX&DPR:v4i%"lz2dH򣣖/1gwG?61.E_}Lʬd i%a51sE5c \ AcMλw ]c6ӓYh!@iY}vNgTq8j{_ҕc*=?$/oا)YJW w{_B`t$\!%s8lk(5RA"`<2Ҧb~wV,TYPjƮR"=uV8gH}Zrk^Qiw'XٞU&k=7vl(U0hG>$ȠuؙwwX ֝( m՝SqwNv Dߧ4Lh,P8ȥNjj_P6Q0ϰ # hȇVQ1*׀NƸ$ ?@7#Vhg}0^RZKn_l~/R` q:sduFWxW1 r<<>BIe-AN D l퍶%D: Xg+B4MOԥD@G)Ds$w/QxӝuI1M N" YBpW yovkV7 IDAT8=M QdYm}XBz#T_\pML6N?K>m.Z0^y>g?O @")=W_# 4Fk(av>KY"F;օ0qETΎ(lQJYσNAqRuUL7[M0YgM//~^x5Q2pZ@i #9#ua4}f[?LЇRHғE-38@1;A]࿎PuU?ˆ$;_$/Ɂ1ҏ{> FFT{72Tb 4c*$1[8dRr+[S=B0mJlX757FJE`x0Ƒ 8"s~ԍ2vOuj?V@>WUד]-@oiDNs}W#H?kw W"lpqCe˛k`x{}7U_Dn na#1^\ N>=CI,*`w .v;Egmwun!?Q Olz?65x~xR \\C)(6gq_t =@qpq}8HU&kг8fQҽrlMBKQ !y!mTqp LsPJAR[II6 1OAse4T!: Gb[gbyhE{,եohͱBwMqt2n9m>Ni!"p>p\;>`ZX~ˀu 91L4@EQ46{4xr*&"' ҥ]٭vG #7F]_LNFlkc鑀NLpIIφh5z&"<£sl.vZ-.? Q K3(pw;n}o`FG*olwJן?a݂s ?"I\\$ 9/~<7[(oaYXE\\@*$I=>,^D#m?al"cDB+T^ Ɯ{PgN8EM7=&m"4h Jʸ.`iz@QJډ4rMݧ쿻ayIT#~>R/3Lg*#2-%H VdH4NR%'fP)F I ql^N,P"{74y֐S MG(: /gDs9^! , dufg4]"Z^.|e"BIp=>7x~xc mD 3) 唷.E{yi!^__\K()m lZ#l\k$IϿc48V60fs ]y6f"nqa@vHo66 800ml6t*A/0)܎:4tk&U(Dk0Y2P ٶ[` hФA <%!DslCF\?뜁3b}ƪr܀Y%X>Kψ(MB.Ж!?82 h+z4-tnx0q `Hr" { h0i!דmSqưcT8;ǣ[+$IC#MSh˵zXx5DxB8~Ly Ԟ\Cd5;k4G%:bW@^\4 uQTȲkd+hIbyUH皣'`sֿ>,s!b PJyQTRB+`2x{~0˷< r !Q7/9+w ٝvUA)6Ye\PFPWZdZu ,l  ԙ t>S߀nZc{d ??K%꫈~cϲ@a9j~uNw?Tt6!pdhiZOo6Crf-armٽ^X3>Ho(' ,18{i7MP]J͸j 7002 sӷ@j)2 |p,%quT :Μm#ޞvVuY45ޞqogW? cCO_>ۏ>=*N ;Y b dQ q8Bf]uH ;^)Hpmj#yV #>>AHyo;=?_/_TtNwy ) 5Yʺȡ6Iϴ03ҮV=%WS@3OޅY@΃j}A0ߏE |PeH)\H{ `q5a"#r!-"`A"`2 #ޕ JѵV^@`$ o{yn7.ke 6/sYHLncԉ}ĬJYt@5uТӶ%ȅ1 А.vFF݀HF sz0 =p+{{y,W7`۲kA],j,vàWEg_EzwhyT?=?_c6{Wc*j?@BȀU'F+#f1sD؍X!fGm҃ܯ׫yd zǽ$[ccUIY[O_Վւf14dK{]]b%e^*^`kKeqKHYobޢ%PJiY@Ea/Ѡ' i2X|to?!38cI+M(IA`?`$d0`c#vUܺYCKٸAdY6b__qx}-.VMSJrriѺX5abf!2X]K|,|udH81g= 䟰_r.2zMXLM#J9_6᝽жJwls7V4dr)]bYkM4?TہuQ)6˞=&T:rƐl6[2S'"s$q e4<+IF;u묀u Lۢ@g[ |9[x`i&~Nnc/`@&)]૝h;qw@kcB ϶3fD,T M,cj@uwxUz3!/?&)5=/_O>=}_E*g^ ntu~ R\wϧY VAI]M+ٺ`W1 ;HA+6^$T{@T{] |6MRr 8Cـs׷7wVmM_Awi>@-5aTSCɤz}ׯWBf-8O ~$8w_!{65]^MkSQQiqz g\@P " i+e<48?T+,3jvkF]D=gya F!qa%Gb5e4ji+sg4A=y%s z`<1&g>B;nhѣV4q Zk(}u DR Ri.u(*PȖY*&ҝ50&"DQ$4o^Fi Pm"Ŕlro|Jߊ1P:/AE->z2i ؞㩆df$TY3/ZICIiS1f2 3(P)Yv1t'Y[t{[/ > *F Q'XM9#b _d࿪ Ή?_{mV]RuǓ#/ 쑺,UUI) C6 5qTX$P`Ӗ:|Z8ˢ@=zs$㮑Qp$Ia\?j~nUzHtr]CHύQO/F/831 KsO_?o>?BI˛k>=1֦5Џkfy. 1\]_cez8%Tl p)7ˈl_l, Bh %F)09$ldI`9@o%UhD#^O!֒=d,Qukԍ[柨wư'<ʄr%7sMn%VQiȀJɀ ]M AmGiEOR {W"'fC^8e*9c BQHc9z`7V.5j s@6ȁBY/P h)o*WfB-@%=.p/8ZG5`w׿O_Pƺۦpxwk;P1`:k^[wv h nlLBD|dj,KdMqĀ( ݧ`%m$N"p! %S0-SsB 8R(_>W?!ZI?cP<1Gt0ASݿʽY{ CU%?*GXj,l$6d '+ *I~@H2HA=Wj:#sxk>൒_lX:6(f,$c"$DZq!cy4͚0j);Co Wk@.@OQ}іc[۝T>^}Va,DxeZœAqϿ}yx{QBo//P9F/^\`{_P+ap%>łv|&> clUF.8n~˛/&> Zk<=g{%%øa חvH6%`` Y^8G$1=| 칂Z1\` &< QFqQ˛+ ?9o ȵW7C+#%WN@qgI*4Ǒ6zh%#{PŪb J}nhXn N=_A=W}>KHn(?/sFAxo)d+cY}o%F0n n1b Vc7}+^t߷_~vKhl {#$2~RH6\ܸ3M˫+,=4YxkI\}V EZk\}x?an Rx{ ZiAZoQ H'-} 3؉vCM^.B3R0> 0?~}ɀ&\G mG / dY$9v[1 (Bʘ;z#*w6I(cM"aMQ(ȹgǛD fTػ%tLg^[me?P 0OZ҂8i @\_(QegB ";45EfA `ˆݠM."yE <݁ +EGqϟlΡDLwH27(EAsWq;\}Ydx~z*'\" !*reH_`4@,`-@DȳEl..鷯Vv GqI $Y}E.U+( Ƨ 8oR8,U، =RPNZl)5mfֺe+;e'A|GMLQUGX0cij"V$&a 0RZ֦t+t-#ƓR.$ʪu40=1∦ 4) 8QEߤjݤkS"2 2޻Rq|ƺ_wf1F$@/$!Ařե 7?ᰯw}^Y.;|O}%%(1@3yQKI $ '@ w̞YIy XK*.onV IDATbw91, 7 ..w^]2/l IR066`Ŋy8]|*pιFX Fk#0ͪj@?,T]5QYm:zuc;GJ{0;Rjk LW5VAu 3j^IsSD;Wb ,)-ryfebGZ9[Z*L ޅ^!F͏V0,zԵlF(AN ^Zv oΒtOxwBɀn_l{-%1(D績8QFX0z4W IKlEN7zU#!1d! +S#ؿ 7ju77D3Ni3:P$X_$@@cPQbR!td_Jh{>]k<ɨ 0"y%!e:I8%)of"ˑr!x + ~`ېEQ~=`de9sei᩹˼ܿƵ=7lvI'ؒ9D73b̺3Q eDž mLyԂ 8q8y1ynS.]r۞VP[-uݯ >%A4476 L> !:R1r`Pzcpg( H% @cFd!vD`=v5࿊Y,lʴFV>pT3w@#Q`Fn =: 'վrG1(Ybvl-"ǽ@i$I[ig qqKGחi 2뿔H~@Hf 1Ӿ#뭻v0(_HLbB;N{\ {~FٳBs:`BӋt=sFj -(n;[Z͕kumi0oaA @@JT)$& @AqWsB+3&4n~ṛ4Xy@2ݡs 4@d330kwuA֥{['##{PͩhPbb-Tmb4jrJ9xSqp~ؾJȁwsƹ'Vu `[60Mű%@ePH,Aa (Zi74Ǯ'cYB/- v [@`tV:SRcy}=0JͻtmF#'&7ɎUPz Uic ޺眘 ^mmTvk>>YIXZQ!NdYC-E@ߜ~/!>c8f\`ds&zD{$MqX[ ,Zv@ismFϘz@]c -T~ׯ69} H. "BsМs*0ZA6@Vʺ ΅8+S/<l9 La%F._26p!#w9\ȥsp +ܗ ]db2{Cؿ{xN+BQ8PF0gD`eߓm \wFU`9qe&F,VtR YY>; _`'ɻ0_dEjwmkZnb"@iڝ.hO;OT`ln& Hߘ[ʺ3xsƶ1GG"/`}N ^_~ D4u:˗7nhSN#0Kׅ/#rwIl8.cVHkR;;{S0wN8S@1%i甞x&jy!C`̦;ڎH q' ]3@J]P#0 ,;p}<'V#[9b#QRkD..!܄|HX$I(U3Ifd=F`= 1PRf(6qfc0ɼ,,I#%D,\r@`8+%g9C<͐T-6HS # .*D'Hnj=5D FN/@"|HsuQ0F`-tbѐt-HǷ?sO#R*<<>3T-OvnV.YC536k_yD@)D. i cO"<v,|&OCEoϿ@BI mJ=9Tmo̜ D8N 1+:暮F^˰p ",d~ϛ@JjI:X ik<*I}/{ݼ࿍9GMfTF!ImZc7t2vI;eu_`$S#@ u*PP.H, (/\Qpa7ˀ"2W̥!"KW+q4ց͍%"tQgҎ}c$]s(ec,{(Ø\{֍>L95&pN*DunZ4M}ﮙ2_tyrMT?U.z:Z2`QckTiDgt+vN#ϱ}W EVչ_!]Af` {@80C6F |?#]78CEaevCqic‚ $19 TzjZ$@@Q}u }`A{K?7SNȜ 1D{~ _i@? `^}zǷOݵR65Ƹ`40 ebm@rn% 1(s;@ppAPZٝ((ОbdWځ 4J*A ;<7mМq.7U}&\7?8o R0ځ~?n 3PbkC9owMgqO5"X}J`` A0 A`(e8m(3 L JJr?`d)G'?0 E o,@Gr3hۋ  q=D@ch"*ӭvlӎV1C#MS9†)1*׳4Tm(J5Z^`|]̇jQxg  uò;XAD GV9d!cs]l]&s{q*\yO 쩔6vw8v^+1i3 ? yEgׁܺM_xHmwLGji]un2&COd3>(zrnIum|5( ((_fXx܋hV7f篫、9,Òs:`41~W&LUޙ(UU##eШz\=e[ 77\_`8LvpkزH٧/cʋ~$}g"F!5RhDb % ]u"-#$-8C`$ ʺdsBa%]8G2hQ9ʂW)Yw({i#ZvKEʫ. #%Qb`wЍqA8E HU P@#zݔT#191;>>iMgz w ?/u zn5{ǶAD(bj.ƚOȀU?P`.miQD@W1@DQ #m&jc'G>c/#7lfyjE~`?#diqc6ޖvikц"1ǹ=$PU!O)86|dSbeI)%ql)mNdk7Bpb))c oiZ>;=rG0RdHUn"n +qoys{*YMb` 9W b\3zrYczQ{?aدu_2C1mLb׵hϑ"'}kt`M`,$,R#`ȦB8ڣnDQ7%\-JAp "`ײ"In $ 8G'PJ#˲fv#53yB niFX@7ڕy Ԅȼ(ϔ+)Qit1(KmuZ \`:e޸sJB9Wsc})kQKxS ذ/ 5^򨃖 I83dI2+ckBIYׯn3rɻ|Pm Uס@cvз/ڵK螮CGcp?k=4?(Z7zqR9#՚>\Vcs̮M~ȾVa&-Fׁg"[˚ԇE6䊌P>ԑ^t@Q)P$DD0FئKr?֓ ."ABD#8I΅s7RA+uO1 ƫ>-_iw 7TI~. JJd:|Oa8/?]2>=/; 4p9s92wjʾPݩLBf[$źZ& E~"5ЗA IDATk,D44A_ߎA?/#*"~].RU6߼fta Gy!=PG5X5C`5E*Z@TSԘ2P \?bsWġݹRY 66⻁Ž| r0eTz"!D  $fUp26(1ƅuW y#3̢1:0<@uWn#Z+ mg&9Sn-W0ڎA);߸ F5 ;J n*#O .ɹ_LjPz/ۼζo0] kdsWɅ'( |{twO:>]EƎM~!`BP՚ ڏ∅޹T Y^ s2UX8#*?n G`ݰ# ɈJ=`3B'[C/2Rp$a!lX`@maukCuQ9yd_$O. +6,vni y2$F.p ?3{@؇ ng wmq(T0҂a!ϸ&SETKUMi."`j(iMLY16.+.@ʎd 0-B>{FHLutITcī^Eg  .[rG; q* *v8B@L`cq\E1~cMkRTp.Rclr>)Q2i D}zGwD1,$ȞYn{6HDP۫KvQ;1s7](FG<bt///Ӏo'c Dc)J/ q1VyFȍ)qLjcme h%u"w'>KX2eT91Hg:b``F! 0!$FE 4&3j/Wve s)ޭ5gm|1ZY59r5;L| 0 eyNAؓع`@s?[D Q@hg ;3dìyz"82(kp B@q $]9[NPx`p 6 _Hh8ڝ#ϑ;)R0 )/^ p?2d@I$ nj' 8C!U,c 4<ln@tQQv)BeXv<\"sbc\Tfdߊ $RY;HiJf @\k77dzހקgICF`@k- TU xǩUkLE>&B#=`k5bIP׀ֲeK@k ᴄ@u]cd2f,) q7ɳOkhz #LHu} b?HzA2i<Ԡ$dt1ƴw› ȽօA2+Enهm ŸۅOz}MvBD ' {%w'@/9Łc'`2d0@B[iP(YOm2b:Z(b"j]OZMHinNYluwX@0L(K+˸@!  I hN QfoIand]g $f 0vU3o`0}(Yvσx7::8k` 388;SrhD)fJQl:#KP=i88玃Z$fGpTJN7| 6SPʳi)&( sֲ_KG[TĖJ{x2њϰS`D% wJ;takm2XO 1 V2D0@{@| G }B>u y~g$@6h¤_?<l٬`t9 YCg p0>~Z; $0 |ε&8zʓ2 Ā#; Jx(E u%Z!tϑ}\=9+cΝgp09ԩi\V(1.@X#9''<"~| 0t)gu(2$]^KЧ_^1''"._:ƭסI7wnzm]cREM!ɀzo>y I-ʍ"Zzl)ƌɸ9҇Jc* lDSy,a? $1ЊprFUU4@ ^;@h`^1>YYf JHU""P}6E_;jxx6Qt$@ёe  ~[h]AQ4[(ݶ ߖTWVU78Mَ'YV@&KԆvR-o[-8Jzl>{ǣ<2Z(,'XKp{!:*T&:8X8뙉L}@ & qql !RD{"hڪ #(0r]v rmOa5 |Ly/C|-3/pXo wn۷xÝ۸t_("`2 p~<{"w}ۂl:Ms ЁQVG+kPZ6s|lx4$pay25a$r C)dy(PX¡ /ז Nvo>hHIZ֬RBpf;>-y%U:Ij^ B5pH]!r"تº4~N[/}|`ز&. UGgsxć1]+ ̥>{;ဖ{:],:Zv-f+ +R -1Blcqi-?-jA=WCW'0ݞ,}he3-_i5wkk-G>Ж^b!pyb0q tʾw[8c-eq &JnȮ[KHe=B Aa Ne(YQm}V6w"BfEo5Rj 5`F3_BcCj[T]R]w6e*vfwxu Pv[@G[y|Y %볊w5p2Wv}4S^A!($K8APxa@L40Qƍ/hbZCN'E D ,@$db~1PU6Ϡ3(I,J` OйF(uJZc>` pX+V sD2Zw8³ۂ5UBAM0 $ˁSVJ1o|ށS U[>[ݧrx\>j|W}ykw;HZ>ԜS>>LH@BƔd~}lpV@C-TeYb>Ak;㣛DV- p[S8D|}sdM(4v<9lPh?d19LI}𝏛0$/<Fɏ&۰Ǝ-xӇnqz4tV\N#:( $."3@'i+ȋ䎗>.)Ek=!%  )otP`+yK1 #D $N1`土9tZr7@R|;}֋Kd?qB&G;o9`~/+{,SB)Ł%PkLR"G+LH,S'KL(@$.$!ϩ#r͠#C8 gRAqd%T|j]nxͦmb y^?E5ܣ |/vJK/<a{'G>e}QW98!pQda@a2..g+ $ս|x4<=hҦ5nǃ& N!0syuY%氎kmVoTVou5m#x9|}[تs$l7)ETe\l '$}d "ZFVQucUeuxwIZJv55x/],*|6|)1G ~Ѭ /_iRybycZzZkhIg*#]d1wU<,QȊ [1!>Dkׯ!Q< UYBif\e`vTO{՚}Y(qdrb@Jk)F '롵l fZx١a1 TuAp΂@2i' 2c`Fe8˱<%qA,Erwpᡗ5VRO9#"3N}#%r}FWЬI@L8d?^"?w1ZR8U륻K]K7|;-B@]=Џ|C89=ۢk7p'-~2`)m *`Qy%7&xMi`v f38xTU%nan^׫ޑoJf[PI~UzH$C0U5 -YƳ?(AKLXa-/iC17*1K Fn(|%Gxp.uȊ7^W>()L†yx(0Ͱ^9߅@ܵ=[[jr7v2fjpVvXu`acgR:zd2뜜_2f{%?u>Ah @KPbT4zyJeט"#᫿˺6_{??SNXظ&ȀvÀ(upn u:{y2NNU=zY ֜01#12ZǵnC' !A(PVbgYi$ȋuP t߼/|s\HK;^gB飵P=CSy :8"X4܍{Чw>$+Y*n(iȆHm@iD&pt|yfh1e}gy+7sP5c#_'vGcGիNGX-:?aZaq|Ta\cqtiXסuO=GbP |KW| 0Hn0=T%*8s ֫5[#?Ħ|{mnXxr(+x@|@܁@} vboMb0>@r焵KТPrՊ8.HC5SPd Ø{'hl`!oHyz"7 1ڋ=zg$[g \QM_/2 x[ߌ7xѱ&B|XӂqtI DJH-~_= r0"l"{,eKЕ`Ķ9@{Hz5acmb!E߆qM-}!¥J7M]oTώ$²`Zav4j1OOi}oWo;gON ]8pU,f0UOpm9Λވ'~&ݽ<ߺ{/ KLY\w1Ԇsc:79>hZxALP&c9cv^O{9צMx(&Dd D5 ޫ('?+HIJ'1SE ${#"# @Ji@&TM XSB1a,T9ߖy˵,}VE Ŭ@iW1ߐOIl6 &&^kחױlБ8Dתڥm4#R vO8o7,:J~XqF h5n ߾j]sS|?xwoE,j@ `yz׮GWV/]l1Gv& )-fG{g l>CϠs/]ucGXW(+dJc~t[o|*d# hwux0R /FnX5 IDATS)1'rUj]Y1?Z@,dyFpR+ RG)`͠Gw#Xyu ~Z:`~`12P$"Ąa/rֻ OfIGE/z2ƞO|mt ;<1J<G-et֥͞SܡG m= A0\| _D.k<"n|NvT}jd:8Mvw S!ɈglFIc?&$,wqu*lø c xCn7st`<8zS>M{Hќ>$-BU lAb9U7\b\`ƱH#J@z{Sټ$CݡAˬik}f+8}rriw|(QeO 0[̡%2>+0_,0_9Ui ̎$-G1 w`3+G^e9X:_)iVyƩWQ9 *%lyԆ3΢Zkxa` @PKJPv%@sB-Qq8ГuYsA<"DP5KIߤ$T<VE@u <`0k iJFҊ3 $}SI qG)/'^4VY($z  d 1@g _s4e$؟ŞD.`:ǎ(_Ȳub.h1?ao^\@sQFU/ 1 &Nw E6u Ye*S?=J~Hv;YӱDBd" 0PSx_{}RV|?Қ"",W+TAm>AU0'꬜e %鄈"^Qk:vhX" Պc.⣭ծHb $091LiK|P"U8Ʉ< 0)ʚD8[IM*/P:?q_cC "iOV ׮]+LpCȊY'>o-!@50w(xI`@"]񽳒6x'S#C5A% O΋Ь:EHHmYt`0#|sE! JݡMXw^k g+-U {ٔ%?-xg}ҌK_5~>G~W,cc ۄߊoJZ#:]:e$=~lai^6ex ؓh؏`'[@`9;.y$0nÔL40֢( kX'O$bqYy1X`aSISB2(ؚxU>$ -#;ct G ~#}G+?ٰѢa o"NQG!c_Ǡ[7VUbsa@j]zVp)K7V|:;yGYU(s YcAA/)V% hq 5ߺٙvR|(w9 Psp֮"&Hq@P ր*8ow/b $ %`m 4tax~pspb4R0W.axP(wH'/x. QeqqEvPZE ckG3hf$i| rłe0ss"Y"s unG}Rkn >7L Jqx֖@((Z2f W^is6%9z"h:Y8Ū"ܢ{; lw,1ZAxu!9ZCi&~\݂o ܩOܞS'7?<\x^UV)//߈w|g#әm@اE^z۾)2 q* >w,( ʰ[^]d@'%{T DKˣ1fCDrBT 1F@&@@E2o}$TezŻ[k |rW-c8kWl}B 5fE}oHϡ=CH<RlѥO~g>e #ldGT6z6\+W$l]O!:P$@0 [&}5XGsVGpn؇\ @ʎ0DptQ1?'p>SUPy.=rJi(]L-H8:Nc@wC99OݐHyϐmFGszD:ϐIZAx;֞S|Ǧn@?BJ^&S̭Cj#@k/!Q IOr?Hf f8)zY%H.fы$T_p:u|pl7}ͪbއD7^zwX3ybmX?%"|g?\T"|W~)~N8=#,gE쨲ho;Dxy;DUiH׾ $X(nW-i1cm_DHi Y׷<8Pq K( y9۶ߒ @wS5(m_ eS.Be (A@6Ah+O>)_ Uſ۞x 'na޸'5_MCiu]{\yO*KxO)Z6ΖeYA%>Zs,ˡs dXVBtQOZ/A* VzW& 'PZ82 Hi(y5Gyō @Ee]σy П! y&L"T H͚H|R6'a:OO slU88! kF 8*r*dȠ`j0fb9.{mS7:~^H?  Y}W1%_&=}`w,X"Vk|G=on߼Qk@^?_įoaopQןؖSi"mzh"$/hrv[xmttGvh0&hgX)]d[L<@)lgڇ!jL(8{jUn75Č?=d|PCՖ.LUϡde6?5is,t ' Z z |- ܚ6'J"+`"VRP^@ XKr YQ@8^:v.8Fd'{Y9aŘ"7XDhNd=|c+]yRl2:H0Yj- `]26x h-i @86ሠ'9fC BӾOaP,AX^|ϯH_R@j[˯=l#=̿x}|'-]#1N}'?#meY{ @'2` 2Guh" sokb-#@Kxc *sdY렞.%8S`@-R89=)V]M#xz?5$$BmY ER˫ZVǜo&$^B*@$ }];qyV4m/wsX@ggx sdr<}zxpWo^G*!RLg-?|o9u:g3" 81C"y,@*`ZT22PGsx-8.yTgB0VbhP@>3X;Dc88ҩ֧[o>Fa-P)^^ PyWgHz4'Y9A;EL u TXx eRqQ%(%ϰI|:[Alt uX'T r|&g {Et4U7ʺ;ݫʡܷӋ'o_ `77p!m7vb?1v? 'n߼?-I| R#ZGa|Pфף1Xk, Xw:7`kuތ9 w KO yQ,+$zHO˴C"tChXGAX<)PRi7LۙsۥOrv 63TwoxsygHnE(Eȋ_~pM'4}n >yMo˿; ˳s,ϡ/䔣A*,P]@,gA) K҃ N-"eƒ-t#sd2IL3wyc (w0ʐRP.4n Nu1q]Ry8 h-kxA+-1 |<9IL\1mS|#ԇErv=Z Y L  asɪSơ~uIPn_[:ܫ~.TŞ,cI%?65DA7߂uW::HIr&$.5:^R>z|`G@AXswI?IlJO{wiW&!cCmFtGUY\:VTm^Qm+Y)N9&1bڿ|Tl 7Uo{X@eyQ$_X>5@ֳSF ǐfBB"#5=ǩ@GZ<=iu^- sARMeMy0ɄdYÙ:J m0i h<\T;xkP&Su:DW0J\ leb\ Dd $m`H="@k7/ 4Cdʫם$کA|@NPBJG '@y3 [ohTV4P9 ?yalH2$ d8?N5X'1 \#[_MB4cä -AGkN{~r;eO a;^U¾^&;l Vu㉀O{ _?SUChޯ!57q ??w\+ZipI==2VWlu"`.=?AH{cƜ$nb* 0^$/0V-,K l6ٱ5 G=sXc#hQw*UA3<,+b?Ԕ9a %Ѕ~~pxG{C RX-'D|@~~pMi5|),`;RHY6]wzTG4\RR9gaōylLnz=EclOIfZ("d:Oe!d !r}@Q`'o^Qaѱy7w~ WOA櫙 p;Nls|Do%=qd{LIAx'x?t}L}jmU@<_"9V28e:D9 %{mLFb?S[-&(9tNq|n-8FM IDATM2(2]BCPolf{_/Ӡf@}tCpΡ\8|7܆F~ L,2^-q6[jjiy9̀zdZyKf^U!2-;€vrFV+ǷQm!&k$?P']&:cQ N<o]d _rD"15)vHY%kD@p^TA[H\,iŁ<$KXddLwο1de&Y5F fBN:xqa!wBo}7f1q`4-!oLcZ~.w!1;];Si[8 g CN {]C#wD}}v`2D@VAVerdN&?7[?ʪD2h3}P܃u / u~b' ]go |;޶P퀪;AkvJ UUջς"ׄ|Oj֩2B WG26 " g֓{' 1۾$2)U'Nu6m {oJw,1@rW˺{ϧ$] >Hݻ+|}ib>c|QTHW+A :ϑ+b0 9mbod<8efG x%5B^]f}dlqYE=A]. >"#቏DHp7uA9م\B:0zPC}3Pͯ=m2m7 /ȏ?njDQ^s 9o t{uz_¯ڿ耄V@n:Y ͟fe[ȑW56( 3h2s[;5Ӈ˳E!SI)9GAkg[ηWAg9T6*ILWb pz4{@,*ûw5$E mr%Aqpq#SdO$Xɀ7u *gRZ]Ώ u|?C1Kq~,g}@ eTjUi'&K9Ǧ`2heO?xg SItL @~!zPK'AIe;ĶJhC99_Us·|*i΢n.j`7n sN|,%D :VZ0nч?kU/\S e:5 RP 0pl` J^BBL28N!{_0Ĕh"LBAx(%.+20q` yf+k\č-uDiӤ֯luQ+[l@ݚ:wˁ ]Z['/7[1.{— 0Z#Ӛ +BAibe.qa2s-)H!$13hY [c`Jc-X` ̏~i˷!2iai=+}dlSf1:\[$[&_͆|;^@ywpMMj? _ºlZ89=_(>?_GE$wZay{\XP~gm& E : PxSgB1q.= n{5ʖ }If{egj;12Ueە;y:n۹j1:)ʲLwy^4 ϧIiC|LxE5=p+1.c8tJq բwV26{bt $8zyLqa\Q+@D5m|Рvp5XcZ.G bMhM8v 1Xyȡ4dJ@@0Zʍޓ:]d+AP۷^.1/P|?.)Fr]b\/Ayh"SU Ub3)>ySyl+Wo\ #(f9,BHW)iOZl<s~bj{0H&%OVO9_ sYjy2N\e R :#Pg @A)NsBxqL L#/̲*ъ,+JYv2xo9HihI2)@2y  f\?s]K~xwj#om|_O훱!Aܾ?yo?<Z|PXv"w>q|~0?~@}Ða}"`{O~$)[$9`MKlOq{K&*Yfժ^Km9-4iB,٪ti/oaS(sUzA?Az$6D)7^7-:aQs0oGVdȲBȏY.:V,V:GTƚ: FS:ܐVǠMBt)1ikz= r#hP74aLw>Щ|V/ ̇Qи;#k?*5LCñvW={;oNtq~ͣw1joxHu>ȵ"^*j2^FL=ylX[5XJ[Ek ξZCJs٭t^rf b}jµޘA NY-LO࿝/پSoʑn?J>1?8&UH j$Xz-lF\|_uA /l|vMAp)"A2rM;'j@7j(Tpl[xCSSU(ya\"r<{ 秧9-p=xga :}Tg''fO / <|9kq~v , 'ݿMꝵЙ*TUsgXC>z(p~r|6C\\z5#t{ xurd׹7(Wk\u$ Ǘ.>nܺr=\bb^Ix| 8?9E1* t!/fXc~Rn:˱82%KTe)U꿊s܅6:1%*XòY2.RXZ8LNβx;ca1,xN@Xb,Qk>]! 2{S|Pj}SN1} }p\m.'^e+L oFaOi-*qONk۾ WU=9=_ ??OX>%fp" <{K;u 6*/}K}6*{E'{~0g~ j2^F̽q3m[v)woxev_+c٥ɶ>aP kAg1t V5Np;6 W3/K}\jBtOE#~A/[ADx c|4?{olR}fUi{{/3 ĤhBt4R8~b;f#;V-[E$P\!@p9{k1YU5='ke?dVVV~'0xm ;&Rw`ϩci+~uem(]dX/gG9Z>R.ͣKl€2-3!q-SYF ,|K(]:vQQLwy)%Ǚr?R8ԉS.0M!QF8 !CXbQRdBi 2R$C$Ȓ IRh" V+ab3$ $ܭ^A8d6"-:vyP-"hcD^RA@qxyA*k@@ocFC*~ %c5kTVT" A { ɷ4 EhFn|{R o {2% {ݎ@s<8=v"oW5eB?x- |lp{=:񳔇?W!ZA*eKc@#x͔6tXP: -Mld8 %&)8d2i5QR~ ,Qo46*3M,?]FCFb `p\!N}81:0qUBT=WM:j~Ɂ\RY=n [ڴ{`Ե \iB騉f_`5lE(NM1(c+$ql( 1_,^0'1,|a6n+ qi!IY>8v* Fl0g]dE. 3$ar=\]}_+SYqpz6I!pwu p2c\"fHib:A `>σ ! (n6a:ΈAՁ=σH}ج^PSd&0es IDATv b&t>1eq 7!0b3pm6xp$%X$EeڳBy$Q>^֌D6I˞muhJ1$8,]#`KXI;KHde-00+Pŷ6`wo;oQ)fx|m(X(ctbo|>둺ڬd絯yo*MUM25~_z/]mc3HGV]:j1iӋ6`b70#t@&:4g136|!w7rj?'xUe40</I3&ߠn2ް;Y~q1'1TQse+ Mv^s],T7d3\Q\m.y8T6im |l@D{:FM&M5g8{x榨NBJ9q<;CRY7جVi(Tp|v!2^^Pg=Ipl @]y 0еj9CFQ]" C$w N!;X݁{OO$ǧf\_ ! RBJ˫0qtz(^)BgجXw778:=HvW5I4fu iȒSqGGPPyR$)8I"Dqy\yVΦ`G^#\o@P `H}OR̋;Y5(J?j8JnKRix:Cm,,@*u{P:/v;{8Ѫ%qokxL876U׿"~>Ol]{!8o~vC[{ۣI~A2|W-n6ݥ=w\JlawKbZpk>k1` "R~Gx &yHҬXo Θ '  cG:x.I(QIXͽ$"V̋`4+B}᫽ ̖\Vֿ<fL]Le@t~z!͔BYѻ*iBhH F]˴iuwr, IUlG1r] (qw}TVvnk lyw'tOc;Č[1Sǯ|G,:\[f'nVT;ΓEG+fSUrӧiGJg>| _rb* +]S?B>/ࣟTkugQib<ר /՘ ~; f-rI$IL?8Vf3L(]1 {֛ GUyDw|9$;w]~djT5g7X ZWxLlſ:nkK \<Ȟs)`x-rPI/j ɢִ#6 >"DPg23;{[twsADfB{zLm";HD/#LCw@?/,3%5진sJU*AŜ~L%o+׵);EqO|"wЯVQS+{uD-Ƿwxw=[ё'!O/Ts{ԽH-th(7@%cñzI( uyВtf?3 {zU0 [ݡJy%14EF QjYl1/,D`#&] 藈j#9T'dLЏ=ճƫڛIk`k<(ٶn3tyo_qu3Z:[*zhҩMon #Ʀ>M%K' /:{6^R389OjVzzlzhr0V{ot(쮜N[;^۠@ t~7M'f(`{z[cv a $I7H6}ȑ+lB! i9pP( q|疩ŔjSQ* Ԫu 댲D'0 煉h*oƒ'˜L^wݿa@3\zZ8^k#p9)(Fֱt,l;T^[$Kk[O!vl6x,ܱWw%GO?/|+3eBWoPjj;1 D {t$ߵiۄ~ 5*<emz^v{]5QGvfb%k9c9]jS+>^)uW' e़eeJN'e2=x)4] Y羇8ﺑVK:|때z{Iq_3YY pgP@3g']2G-{.^&[`%"סܷ\iXVKc^9]#=ťEtX %*K*zxD8@;0+}  8R2''$cNjbd4Mqw}spG`@ |Q ' K \ jC$Vn/1_g ibyw3D .>TQa00 0c\)0|@[#1qw <<9:?u>Y?Fhd5N."0RTXeO0<>BmŤ)N>='}+_}Tq_g1ڼÀ7'>m'}`o>J~WW721kh-1 o Ab8XK_7BP$ME:9jzẑ&cзveHS{AR" uFnY "!z)PZ(3hj}KEJQ-FRsO~a Ǧt N {'<߇}LfSSsa"Cp#\_`8/)ptzrˇ||>a2`cx_L=ÛL Nqpr,M1)fNz3LSpO= 'gg<8p2n.qxzү?L:l(hm0/-IG@AcdSӅQ+|[ŵ3ͫ]U`|ֿ#Me-SW ׆FD޷y=V'kxGWW;RѢ^5^ɝdy5괓/m=׊oHRsV*CQ9z{BΈI"-ȫJQME3`C_յm5Hb@ecmִ]ּ>rr Z@nڒu&-hͭ;e nóSx~ˇ0/ !Z.qptp4U;wKN&%Y!"$MU8F Mz",otK,X,X-W ƐD1Ng,Ȳ q4OgSx#\m F~CH{ qp=$B |߇cD>R#݈fۗIL!D.X*9^ =7 sUCJ7Vk}}jCfzm'M}[ɇ_ ݌:;xqㅵ~qn+mU>C]ӿ,/zXmouz^7=zXQCc 8NQvKr67''{sπ4Rgmո7{hw 0G׿MJ a*gMZ}VWef"K}42zq?eonPW(\Z7> ΁VE=:I݈mKԼe_@ps9ޥ!CN&moܴ%ͥҦ/[. y\z1 ]֕vB7Nus FVJR{8yp5),I&)/.~'!D1.|mt8 /c. DTg` վS+`SvUNC@# "mCJ #sBct?㸽["c1&()]J[`$'o}\v˴!R@/`lkCIV @vP&*ToX$0m۱!-Ռ MM5W32l^r;e)`&* rDGkr[kry fy;<@8<9sl2d1fFE!D"j#,on0 + d`# 7=i#B`UC Y_j6qGǐ w`G%HSq!TmIeL@/ R"K3Hm0,+e!i\⑻ /973ߨ A9aTKU_!~b0@tO1ےcއ3YC+՗ozu {:}إe9Mc>|(wRBѠƀ龌=оfpޕ!%PDo px20YPRƩbZ6gőjN (jYod }&xyXkZQT38(LWz+F@CU:kzo T*Ԭ N^A;T|+!j3hK:HF?›lW^%SY׹R7]`2!&y;o/r^%@P@Y0d:GȲ W痘-@xo`2!\Σr!i"I.Gy$Yb\!MiW1GHW1],m["Dы$a]quC9PI\a7g*(ZR i4NePF^P!3R:1(OR" 8t0B{1n:DYeHS zUw'Ն]<txuxqtPRk..yFZO.mŨBXkĥ٧s dė8Jc64HKkRms1uN K Ytc+sGjč|JRQKbg6m 7.4?2.es ج4v3J=էKfI rcS%"I@ disK8K ?8VfF2}iXI(w˜.\QC.SX4j_c &7 lFQN֚#AF RTm"ːD! IH$gv+Mc(uA`;8A=B$q$Q]1GR#(4U]H]<e !I3T1bx4WF@J4C$"ʣ6ӹa8`Ġ} ľ\']'w_*Y'?*O+s~׳pbG< P1I/rQƀfdUc>0 7&8A8ii076F2ޤ9HSoq`s]"J]%$L Ɋ*#W0=LS@{+m>kb_}Ic ,4t,YgdWߑ3if{ L9t IDATlF(rm-v8+"X:j)P>@1uج]Y$^Bgi,IB|" 8d& P;_ yiFer|!y|x-"R(0,ЀBMY3vtdҮv6.cEڽ(~;BS? 4ۃڋ-PQ$5g9@}"˜Ɠ]6[5b5T eHDxׄ駅[@zTnOHȄmoLE64d)`BI55hS"$Ei}/w6 sйh:'ٚǪCnx i]&YCy;m;)vV ai؝ϐ2??ұ7>?'k~Wܹ^gᗱ^o^..j]J;`yɝ§Mtm)?F=#6#.W*ޟ)w *7{;ldAǪ-1#Bi@hҜ %Z с| #)fh UhƐw^ЏGA٬+o4^Jb=e谉։w0׵« [mWa(ibԠ UB% >N'4]S>sN(I2:20DH*">8xGGvkh7rǸ*U{>`V,f6"AJHD&xBP c[B[8RK>R`jc#x) 䧏q$"dY< hHHd2Frnj L"} 0#9ͼ2;۩%KIogT j0 HX$5h?!}f{cc-Ⱥ+tgᆀ@ӏj#Crap@B0 NCdH;pp|Zj>K۫Bj{Wkj.]\;@ 4E"%Hd.h,u& -ڑ*0.+Eqo1dD%4+#*`wV=QSO6A5cD*@0 9k"裣R8<[hd1`ȰJ},wAJ_󸾹ѡ:;oNO}ejvf}BQmzZM!`M QƦ 4&g@,ޟ[kHFԪg hkPE!Z8M1s㊩ѫ}$ej$;Fr{}Q ##a:nW4%@Rm0n5>@HkCcG!Fr.ԬWah:Q SN\G u_Oшc V M}ywiU{ hy2 8;>%65NNqu~S0Fˇ/Dpy8ypGX!L*a\b\a~@Hi+1 , &֫X3KH%Da2 0n a2qau{Cx6565Oq}yÓcqDcc ̦S0q{q߄)$de ړ Α-5e{GijYF;AwQwȫq5 ᭀ >@oX~;4,c:ּJHԿIJO}9؏|p7=:0FEAܶ7}e.T5љ?tv!0 fŷ:]zV?mA^$=/)N/"VP_J 1+B`]:a"MRBRӭ Sq}yipW_S@M_o-ouUڬ6EP, F>m0Fɀbu$3ۚMXF|IDD0Pe0r؟oQ{dx<~._|^- O}W:Wqk\=)w7W:/C /bq@w1ۛC 9}4E*.^x/><C".>`Q#Q3}5n./1[A+\_*8+#//չw{s dqy;%cZ¥&h|P aбL`;_*`ɪٮ yj$m-+/Fr#{q]$UṱNo!_t}'4R\ꗺPRCr}I'"ݗf ^;Sx%p7-[[eKnY;$ǻ܏t׎gW {)m{46Կm/~\An`\:JCSM$$80ALi`U(L}9U/4c1 cnT-<{ʿvܓѲ+JOYѹ%^ÖUmPl\ ]JC)fv}w1=[4Ir$)g @E8 YD&JGTՋÂ^df! m6| yi7 `>d>p/qO-js Q Ό1@PG -dVX{//W/.ҹcix T 97_Udw]ݭxQ7B|'i#N;f܎i_c3{ V*wb_;j>哆Zo|koCDMX"Gį|Wy˂Yw1,њZ6Ts8*e qK6 0[J~LBB:%+d 0N`Lm9K%8)?e]ac=Y&n"8+!#"T`Z; m$-bH3_9.m+mX#@_1('wQLW9ʰ:ER))Hm}t VJIb/$ f9}@>$@*}Oy aX(`9 P#\o3to}*A0b"8Ϧ?8@i>$? Z! <@Ȓӽ}4uŝp>mÙ{i$Ws\rĊ5Kع>m~K7vx/Ÿ8(E[_V Ӑ =|NO܆" ÇVA@.9.oj׿N8 p'0N+C@NIz9'cAyIaC ̳~;&*|~Wp{*Qhƀm:F@AU0P\&02y @9C6ssʩs~Ns3C b)jiuw,{Ha!qf yKƬYIb}T.--j6s#7$![அ<(蹵\mG.#+s5am!({4P&= ?cDA-X@il2\SʃfB"ţse>1ˇ`auDBg G9+u2Ifen?nnGQ L>| F$0~tG̦R #䤔Ȳ &uzt|G*.-׏!텊/D()qs~F jǸI@xt3MQG&&qp鰼LL rpU}y>)(4S{{¥Vξң- 4v ORqvSNw¬p7G1>p Gg_W5}ߌ׼ _G:80 ,Sc@+ .Q;KS0zưFi;`%96wȫ Ҍqal3ҫuts*BBX\1@z4M*cHY#Tۮ^4jQݠ%"zX=XnhK5]d0Rmk&z7)L"d@>jl \jM:׌-X>-ͱ]B*qrkzϐIY0+(e)D&) R CH/!%DALשpܚHBL MYڸIhPUK}3Q|4g 110وywq@fHYn6@`#LC8t>_K}k- _Vg: lJRs~/*I  _ T-*97yC<(mgaR Yhp`,09B)fF׽g{gI38QlMgj+p#2D=#bD;߽҃[ 3sN7 *H^u^A6Ȗ\bZ:ZK%^mZZ7YET ]ڐ;,yE2(_no~I<= *6c`CH)M< Ij[KYD#G[.;]:04M֓ ֨6sW{`q-*㠠m궪=zމD5?̝=xs@vwd翉{su1g|w0t2.G^+O~O~f@mZi1VU%'0~/!%5۵`T_FňY۹CՒdKHDq uDI`L6Z:1P/@[Zߘ`tlf l n02D]2fpsRJU;o^} J]Lh#jl(~@=**]YʿG|;z9%QsC0 LG@rB MěP3++%2 qU$Y.2J?WJ}%<oQ&k忒 rL5:}熡v.u~+@BKTvpi:t{(vlҔmL+"}pfV':xRZowճi?}WkQ\:l{=D乤i#,/c3c-bL-Z^ŜnA6wɷx>WeY9.S"`4Duomq%VFW;wսȽk[4*1n1m }u:@#dLOF=0O$A&2psi~,a#-XG]uNgSL3VjF@DX#cQT1@~Tf,gΫ>a}li}6ddIcyijJh"{u2$q !8T'#dI ) `ЖEij?2S7g+L`+\K?Gr5q}d们oM?k(F4ߏMvP)*K' p$sB{^Sg}C s>1Lg3i$ժ<1x~( p! 7 "˕ Rg R8!Tn pȄAqqs!bN |*xeNWg s(+yIe)88&q"bU7sa# CX=&DAӭ u`Hs_]| b?2G{-RAYFcwU5*tת,2 F.>SEZ 81D7^+0z\>   9<}i}}d(ps}e! N~ />R kÁ  $~Y<H'Rj]@s2b%<_>nonp|v0 3$_NG1Vww;8PY}ނ0ߛ#\qW=0NX.9?`:a!%1M8<=Dك3\<:d6L3Q'bp\>@U;se?|p *,u" @B(8jIL4_u  ZJmhǀ iv^_mê{?vp5oM0%_o\o׽ g_Z|ϡD?u~!a=^ܛ1y,8:FぱKe@]`pb-) rQme=<#cDq; c:hݲ^ҙ۸m1ʴ:}`L_G*R{xi@hkAj\>`-9IP>U2FQ :RJjc83@kږ^Ftm74x}q.pt>t6 / B`us( q8Q]' L{%}L3^boO-*h8|1G% a>_`^c6c7` qsq 8{)^h99GHgO?-<߃Ms C\-S>=@0b: UtU~oNT<~੠:\Y i B{|_Z`)G7EdB Db@!4S'K;{ 4-,[co7 n/J~`X*k|vZ /u~E} {6)h>цiAͰPd?%$Ia\*5:N00glB6k!V5$)/>PgsG7[Ì70_ܞ<#!I3II08HtvQ!Kռ1$98P,& MHR=0L&J"A8|?P+ۜ TPBy-MBJLI Po?9d bLC$ dn pȔ^& JO(Zyj5q/H31t&><Υi8,R2ODB&G3G3rOo1 |+L{%H!<[!͹mz[@6paNAz}k׈='`տ(Q=(v3CПJPpEF#F^ƃa.|}C؄4/ToNh JLР${Xֈho˛|WRk{Qc ^ol97qUiҠ[krB\3${v}b{IH}S=Dz,"^kT7tH@m&p p Wůnu 6@F\v5)#1'\?!t ";=]?ibZ&NVֲ(èLh%l)%*7mhnc.\6K.öOFG{~r=pī$4ƺĭ(~3өݜ!gM_X[BP,D ;<=Xz7S =|Xeň( ֚FAChIP<'FL|KьR8רikjUSTхɗX$ Ajq_@} Bl! ڍ*nMDId'Y!贚45/ߨ79tZ-j4"@HΫ`h5 ~nO8 LlſQmbت @AtS=Jb)@B` fMPBm6ӗ@+@>0Xa_A! !2Ub#eMe hƤO}9n3n> E>vAK UQ<>0V!lriӴi2 6nrr z7z;7}veI1($Kk+Oʁ\兕XK#>T+~u:1!0YYm C[Dgc]@1kj"?A)WndLT,P]Hq{ ؾv)T&`˦;~/}f>/6lԒDFǑruDT5G[} }:C60T @ρ:i[8Φey)qP?851XmY8p>"ƿWk^vˏ978m\zy+8`系&'ʰDnɃE5%Ծ2"y46þ_^!=NyArq1GQY)LMUxɧm?Ǟ$ b|U}HKGі 2eOc`(!@Lg›=yoE 8ۙYFثSi7MN adi(0ȅ`J`K %0f<bP  0Jҙhuosip/E=.  n{VAk/, Ű1PI 3U\ #a(<*K<<{WZ}?Aa(1FzR`."/[_t+¨̿\5Nr.a׼n0UQA0i[[O!Xώ)?o'Ԃš5/(Uܫ7g KFn|.˟[8S)4,VhtLƦQ)f2IF8J0xuWr-n!t\;eF.,)f=)1N1 NVMz^. 8.|0FҐ`[=hNضzH?xoNxkTfK'Om~xKx?;c7$v#&ΨzrM׌p4՞; 96RxW%wQ|vx5$msq/={O~^_ >@ אaK%L y=գD(\b2Dk*- :0:|Og蛡)4dH9]xPT}WV=ld SnZ$H$KЙu:Jh+=Hp]\!MatvS00 𕯎yZx S6m(bЅU0l3#7R &&o ]o/]X"'GT삑G8r<;TI;6(l"H_#€r_#MOl g~}?]w_<(۟yUؼIT%8Uy o῿}JShlʈw0`̣qlc)䤏pqrPP&B1,^"L񑘖};87UrHY;$ C(lVo"&yaxQ=oRq*%b< GL222ݔ];-ŠQmrJiMr;= u__ˮ?WZ6^1⼟ c=={ c/F/ {,dlí);џZ2B8pݱdw.1Nɗ٢C[%I<JJvgs{x'p }5p}TI>봓*g7Z=:?LVxmdE0c R{|ʓC}l!M ! NW/Ll6h[߹x ZzT4M2i',Y\Z=կ^Ioj򁊡nyEyV\r HAq)4Z^݆U/Z|3E.j/ZM^yu>1H`J._(H-q%6~zW b h7-fgig *E\ob#'D1;xA 沔biae2ضJO|Ёnogl6n9Bƃ3V] }\#c:]{J;U\.2U8KőNj\B̆!X Bߣ8Uf(OUm[<'.uP?)1aPA-DHXLr )o IDAT,A UA Adq2C*}BC%1d}r]'*A y A-tdz0Mn捜uꉪm0L&{#FN!`褪o9c@?8o<{`њow !{R~BK9SǕ:TQ:bӵLsGwo>I1IXIĿ#88ף!OOǵ4*}e*ퟜ!=v-wn3N=n1qFT  }[Y:+d-m߾gPխƃu ',瑁\*kأ␃䐃b!q/6Co|r 1LX:)bC;e8JƤP#ۆw`Ï~jIK<5e4juv!23at-&|!eҨ(O+V!JE@R]Z0L,|ED 7 4RBRf`Z6RӶ]YMVV kdYi`el,\b *@q))b?LWgYBJHuRb2'W*aYV (MOEAiiYtZm&b0ЙԍLW0,vIAAej4)Z )UJԫu\?dsy쌍@Ь)K! l6zx% vEZðLf6mPYLC3f{:@ dLSYJ|j~vM0BvMb6_"(G;ۥH?4i "$8/]/s*gTU"0uUƉ=p=VǃK|[ak;LV] ?X SHbK'sqGg36o%1(rmwz&X{9skqx?~/p=m?x'y;$1|GiZI#cR.j}*uӟduXj=x88KƶG諫&y.E>Hb@ae&ez>v;?<荻-`߾{\zyuƩ<܍ #H$Wq7~ω/d4H;j Y(t䕔\ oq҆+`ΝQS4r|# _ƍrNo_w ﱱʩ:>qs~F( dЋϲOR "W*X4@efal@`y\\>O6%WӨ֨9KJ.T4uf0-V%j n +*e2GdlV}|D٢~d.  GضM62=̆9Ţv0Z L6zs`zn+Kn4vq8eMg\E4, ´,,+X^ckѱ)c@}>ӥŴmLXi+x c1aG#f9(p z,f/љu,U?wy^{[Kк1 _C1==yg_8bAeq.J?u}nȸFl?'>U}eOC=nL_4UÈkỴ{JFt)ǤeM:"ׁM2⁥09q\} L>{_-!mjpWiBGY{ FÏ>I3=F$EZd4(JGA7Xztq}z󻯯pRX#RDLOW"=[lϧ b{ d7^g8L"oZy s'1\ ׂ%Jx>V@SҬr`:`Oui|ߧQ!;cS]T|:B\!@yz ;ZV6mĘ`!&*oQa hVH$NE.\0 mvn*C60 ŎdPnǡ\>vp]W٨\bl2,FC8Ӷm4\Ҳ#%v!i6+D=|C 'z |%_S]ZNǥ< sf.` V[tcF Y/ ~d$@ UDPbh-bH&RG S }f3LF WC&O(1 a+oT z4DZʘ,7No=k=|i6"$$-)9u#Q7%zTBaigPI ,;|mL&}wqMtL|4Mo]1'P 1>U ]R3Oᨀka'%;>=0 Szض.؅T$Bp3?^?֫} /}|ƱpRDq ̔x5oBNa@JұڿFF/)GfsFY$c v5 " Agf2(-鱓h6 ]\w@ڿArFohFhÏW^GTVoLlE,`{y L+qj)H޶ZJvzCۋbKE;H~|=Q} %;yɧ8su ]U$į2Wiss/=\Q@Bo V\Am0& RF9[:͆Ij ˶=E"1!ңRai-/*|i!TRu:tmШ֩T |Î4ju*`_ڸIR0B8I<}H{ %jNl$auɃ%:`0.*ĴLLTز&YJ8#ML',@"1ء05t#8ܐ72NHbC0ԚBF*7k7b(^OӲt;覙4LmQ&Y$7-0tH4vDWY?FW8Q8P,0T]!B ;P@: <^8ED?xCgw{MӾ^W~F1,On,Hr`쥿ri*/\㟿aL?zx˳O;qlA>/T&} KV$))s\v9|zz!NzRxtԾC!Dr&BIxE]0q'i<;24XZf[\K?ke=kB6nPB@gNBvϳ`x^@;HUJaprO~׾qSbLzb]}L6+Μi~_ӾZQoy@ y+U%(,ڭiYj\_ h7ThTk?^Hs[6nd2KZ#?M2M\u)V4M%ЬZ\c|ߣjj}a*ɡ Z&bJl>>C(= %e*aefv! ,JD]۶Y/q6%=?S34y0me.B\N%c V ZM̓!bL#057Ғ;g2d$ cg2dsY CP e@6ŗ22$BG HF>|? P4#3.-K`*4a E~flmj1P [%T9d ˌ]f[ϧbZ7AeC̷)̵Ƨ,|d'&BH`EzCms` PL0 a @@`3l |<FqJ ر0#*$UD V^_fΟO|#;DU0[l"nOkT?|4=~$gr-€Fno]~@, *_cOqQŚt/9G0䥳#% 6od~o¾֑  $HNKhѣBg3 cL2&#5[m)iC"n}扦t\TCW nf fJѢ-N<‚F7Lfũ'o} Qb@deXM3 5b;F]}d0-ŽRHT\)S.v)yQ+)NUhtm)Y7Oi,/,bC =[_F7a.[ _*bg34Mdj4_H26r:Ks<<9m3aՕ|%ٜhH$rCVžy2XܳDn(U `i<i *4ju<ǡQuD_ߵӶ)d,/ 2BRLZq܈5 zFTQQV)JxKu!-mHd@S~SjwۢYkPT/?Lc뀃?-/RM ?D&Jr;B\ϑ >Rr@+]!z%|v{;pVr9\7&(ٳotk~rMJ:pPngr7!{@@/O8H>Pmm\*P,eJ|;Ȏgw#O#OضE6Q51P8IPQO8HN?x; nH!#ll,?8?/; ɲeF9d90>*%44[m&+5 O<}jmÅ|."Vo}LU89#8p6NdRtسo;!ORl'}$g~2G)2)}Cf}<.j;vt&a`HSc{v5+x[6si-G G:D>X]|ض抋9Sfiypa8ӐH\ ߾GzpC y^7}O&`r,W_q1W_~1SkۧXS,9`F9~_]?9Wٽoaأ^ N8Թ( y6m#y7yj3}q.v>Ly!ҫ^Y-M\÷ci 7~nP-|>K/K/R.a&vR.AR1r8cBP7Gwݧ(B)'9g'ٶll˦3;=X?BL_˺zȊڕ5 3S\ 9SiSi4[b駨S<|qO~g[~/op0'fL&ӟWĵo>wNT:SKy17}綑Xph=لnVxj"Lו,$vIUw\P@%Ky #u22t] Gv`Q4.~F>).Џˆa"LCC((S@EDMQld W{O "lNBsDIŰoB|ަHB"|Ngh{$|%|"" ru/;@P涉 {A tj1Xߘq`7j&mu<;QhSa?@jKl7ܲ^uiS<Ϯ2 .:t2;6.3nwF`cHA1]w5 h\7W]ǥf]}'4 0 ;~7_æ Q]2KBd'bV {?I==H+e~ E/8+au_LN=O>o=>Fd=e26}u1bh콇3S^k/3N￾s'׼i%L~{A*exᅜt1>cOR*ͯ\~ WnwMpiJ%R2]6W_v5l{≧k. IDATwZ=0 n >zv =%8;~Ӊǘ3N=l>wɧSy+^'?e\(+ēOsi'c[/zA?E *>lQ`zAT />BH)iK Ҁ0I?|GX"ʸcc-hG%K0ټ[a]3^Lj!6i_8(@)cP-F#kBh%P#q|`vi-Z| 5Ca`XNڈW JLcm50cH~z!-$4185vݍ_:HHP9'mBOF5`%6im߇Nֽ #HA^UlmrFcяPHhw:_d9bTObEWoN0:i2?U*l_w4[8)Ӌ]wElL8ĽS;:*{/tA@M`Wυ/8C9&s@^yUb#g,z9&Kry~ wNJ}R4 {LUʼO9`S+`fzmE߫cXd87ZˮnّbD-#q$y6@}}4x&0$eorM;-cZOΧ)BP)yvn}n77z;|՗{ L8-<({-psg~}&fp9ܳصg/O>=~}_ͼ/_:G;ɭUW\6QGqB?h>y_ݒh*} +*4VjQ `iHذ5JGjTǣ'+ʌ0Ud1k5:mL&W,qb1B i`gXe I&j!?C0 l;͐f2L4[f}8HWe+i7 *!Rg23Cuy%+e25+MHRd-m%ϑeA\Gef0 ON.-3*2Nn ĿJE>^{S5V&K$H)ÞǬ9h7)W`ܬ:D}5#Q0 MW!$M6F5r%6G,?G4nޫlbNn_˱ Øl!KN('}سw[6!tHJ #H7qA['t6 O? + ~-o ;L_9gW_嗜|ۢMݯ|NAHeFJ"o7yf}=Cy~O}^q\  Ad 'Ё=-@JI6?ޛ>JN2d)!/rdّ<ื\p;uPJR.d!sݧBɀ>ٻ88 s9~an{}اy쉧tבY O>~裼-oo{3j1M-ட˯{1',w>Cۍ։$LsB>w=(W?C[xGUţ8kxsf-1Xzԥđ^JRrL)484jT.р~AG\2lLT=L'cj39V Ͱs0ظy#fv4Uq蠈!-S,̆9WL|<һU)# 6ϱw s V&CƲł )Ӷrj0D}ܡ3]d(h!)!fvʤk ٸ]?"c̮ld kc<04i`=x&];>zr]۷NHtDQ2Ht|1qp}MFN=~_G1F1GJ6I% RJuTS=1)M !L=(G^urK1ǎ6W5۽VCӡT,P)K=r }V K+=g;)Gʋ/@hN2`[q]ʥ"fIi\r|s_Min|EATk 6b)6vEoť3d Nn~qzݫ*G&;ynZ0*sMض#_ht/?s8U7Iby~@1cÆ(8Ҵx^7¿4yq'?zCd\'2jq%siiť(qR.3=U,KyR?Z㰰D6f8ӜsDrQG073?垠|DZD B׿8=`6cs>W_ySoǻ۹8Sc8D>/|廷Iذn(}$W]B^{>R-7w<ߺ=fi; [5el&RJx*OMa6R:&z JjR,W>C ɒ~7l@S zl0M|E?L&.uʹŁ+ KLt%rX^\IJ,搾KLˢI2ffTu4 3 EdP֘QMyLT@J܎)`z㬒&KxCyv0 9 xHpea)%a>bJXY\ff,ViKˑ9ڹbfb>$0=| äTfTa{ !U34UkHaP`6\vidya j+U9;kceJ岊/ި-j>a! A W(`gH)i7Ni@)hjxI/KG %%,´L:RJʕ ^Cyf iYH).`XRH)- d/4\UQg +eR*oq)JC% Tc=ȸ(?.!%Ċ]f3v Wy\P׼\?TRBΏラVAq49ù8CRЩMN;x*_-oaKMj{ =?=<}B4e3AXSNs"ͤWe'>uJW\w+:wӑ^"l_O!8x986\%܎LE:JgOe"d..e1mTbv6R[ eei[QPahD/Q핶:މk
K(q!6!{gF!u`.-Х'kn'1\t߸{ݪx՞} |9?A<` |;vg,8luQuw7|Vs_w@J݃+x^ġV ݸaw.wSxz{Hv;5/O>'tͷnu"887gq g3{k ֠Ob^DdMi:.LƫjpBLyzBB`g28%mu;N,86\OPk;\&ۡ23MyzL6 b\@\T)Sdc\@T$SQff,m3e3ӳ3#/(NCfnFdTfg(U*HftI)q:<3"LEǕYM!d{~o |2JRfp=zE#Ez fNMZq:MUv\W}:Bl!<<\:-4mqX_"W(iTkR]Z.6w:.Ҭq:vD-ta2570 |DzLJ2ieFfY6q:\]Tf(_|>΅W֫KsMf? ֋9W53bT$EzqN,o~Z痲Ĺ cyh>a}5j>"wX@?=9܏ДabS1k?ᯍiU{r÷oZ\-Zy5QoD"zO|a(/߿'|[Iezٵ?o[ggۿD[6i\ARiL.gz,q\*?>o?zeeU)4M!-4nGP..f)?#8Q*||*=￞;EMTy۝aϾy?B,1U)ǿ.[:mc\x.׼ExmVS yEb~!T5ھN8n`g#=% d/8Z_<`xd;>McZt%zBi}eznJ ͺ |Ri/cu\JJ7]~Oӏiifg.-SӖa4CXMT!ӟɐtC0L`*_Q<ǡi0)TJ86 d9Lˢ\jSw]&%Ktm|ǶΥ@5tKulؖ'RF0,J2vZAC^1z|2B{hYN~iwwX06`ܦ40C>s0xf@ `7m{F`ے$YjWZr?"ru}SVeF|edd2yr.21{4arz V A0BR@S`}izxrΡRI!|>qH11Iem7#HM\琖SX!\&Za?o{ULo9\ݑې 8½| {0?+lo9pO ")v}ݵc&2wOZyw,؅6z'op}! zPOo&_RI}2a sMUѡHB<"~$OUg_+.ww~5 z*Rip=ogUyD&  :My眙 Hw?} ^,TZ Aӕ9VpsJr}m$o}L.ރ;u|8w.e)/A{0 Gz!e1 _^sJ䞞nY&.ÑAt(c#Rxi5fИ_9CVC}n&\AmLe*\[ʵ*fw̧ARXobfaòHY@)Ec~zVF7 r4PFF̎y2, k<s-JN+˰e2v;> ]iB t7:h7QPU10967PR6;rðKvu:TkU4g& q"2B^6 DzYTlð,tmTUg>Qנ1vkFjE;Z*BɶQװŔR{`:e[0Ul,Fe´- d&4Ӏ].1? 0,1- e`U T5*66֚p{]ضRrY֨ǮNljz\%uM)PVPm^ՙMrß 2 \9pr.#q7#O`V:ǖ 5O?K9W1AQa~ö 'tTPB*a%CC2_/`ja3{V jm| RY^9g+X_q nS?Wc),5X`<2O?;|Wx @hwxx78|t1۵GHHY'Mܯ?2ފ=6 3K'ጮB4ƤR/lI ^<8}WDH1M)$z*4 <4=*;yTLНRk b IDAT_?gw~0;wba~=tЭA{DQXZ^Ad4+$O>յu|5CgK.e8pixo'R5SVA&DP^]^tl4[ 6MP*p&+X:|Lin@@`myLee5s$\{&inrmq ++ Vrzte9(Up:],eb1B7|ǓeFfAʥzVJX[kI͎]0,ep (h xtrф^n/$ a$LLuK*,"TA'H+#=@@w4@[8oŮ;pb"eۇpW^#GsZoƘm;dh;ܾd48WfvUVUP. әs$1CԞqח$SBd} | Je".D' ۟b8Gب܀].EyRaQHwq03;'+RY93B@&ipi*D\G@%!E*#$448_J8KN@ ." +'BZ <Z>(5]*I%0ŁO&fK('CNa܃#O*Or]ǟ?sC;3LF y*Xòmx(;zSAaxtBEr|&ڧ䔲ʒSL0q18?8"WA?Lp)'@ӘJt(ru? ,\4H..ZJ'Q'w@%@C)h >@fO>l(..%p>Qu1L Ցc ;C8Kq*,CNWWפ깆/N@S 쵘ܳ5\W ϟ\_$S<=(, %BZ;sa^<">wǞ5ą-,BJ؎KF'|%FB` q4@ qAp7^w>w($w!L`<8d|&5~R1l{YX9vlT{iwb(LBtdyۆy0-P|4WW iVi(RyL-2˄5L@\iU&.T0Qd.SS6,ˉY AA@T2a )Jd>2MXPĊ!a WbN:Or(?DBc+tz7=nDž?&4WB tCA2x*yD! QڏxTT"N&yOGea)Hd>>Hfʖ#Q|xY[eIDn(*i!/dxT*oWLed {RB yPc" ) 薕h3bY+_٠7sLS",AJ3`d<ӨvSk|BK "!61iLoW 6!"_A3KD嬓Bx"L13Qz^liJ~M>4xqݞr߉'Ň^r9OVFٮs !ב9Ww@"5lj'ŭ7]睸~/i"wIx.ܿPF($u7$Lok\BREBfgkk7{P 0(1a8~v s<s[\u^^ "7dMkg$ 9=PMʱEqaN.X[Z薅Z[L]2ƄPt|`fw,`XB@ >nJp" <2;i<Bd2Cps Fd&B7tru@Pm`}eVɆs+JB(Zy*HG @dD2R?A(b'9@i!Ⰴ`YB@(P x WiNz-dϾ.Eq70 )v&Цt?'캀M ̠ {G dQ"@$Çq$Z6ar kHȰ1k3յ&f!,ί܍7!@VZUHP6tpUN `׎y۳'ۃ}wℽnU~O y8( +ׇբcEwӜ}xH3\#x!BAǖvJ),Ð<& 9Cױo1Jv\;`F%Qw^y ?įҿG>~VVֆBbM >+g$Ult>\tҋ<0n @$2,?O7^{%^|UEGޒǣP!%矃/}A$0x,aBv;Ly^uQi <64Jv96aF*:(|zuF >#6 gu-/63(۹ŗ@R1oj2lNVSm.X .ڨ}=33Xk"LVG)=U Ӳx0vDӆyʱ%躆r^مy,/QLv 5-6(`:?JtY-#r LcEn@ gE@I%* \zhQ\z`PB5L=>|4@OtY[rs} $I3鳥y,g+#!#`NJ@_Q[Q")b Dih'ZB옟MW-fB/)#l|Nʗ`(ÖfHG^8tts3\dqGH |T J N޿~Pvznj`~ae5Myg?y*N?e?JvNٶ9O/GW%B!$;c.bcqu76`W+Z`#9B4N,@XY\t4@*5cvE̜nej3u/cau0t-BN8ݞ==44W].ô-0MCkm<@)i4,NNp (hKkZ U!`l膉RեpI<v\Tr|Oxk LbR쟴̫p> b9 ?| x3 zG]$Rj ̉a֤rX5ȑb6?a'&L( ]/58X8UަHݢsOTے(SY492-B&[Mҭ6駎g\*Ear |QiO?yH#+y ̀Ʉ\R\pW^ 2sqfKeHc[DZU4`Df_q ^%<YdHX}3ъNiF|zq?}53;̓"TqeϽf GtG>zsb)|χf蹍[t0sJ}IJ7;03?0**'8fJ.<d*|?xiw`,*j8@>5 !NO jq xzp{84 1K/dq%1FeTΠ:v0Xy0NB> Ck% hq8~/Ͼ%(Le~yjC@[(B |U^1\B(ADw P.HηQG ષJ&M/{kaͳyǒ )m, TypriM/ A`׮9r1~ N\A@!H"; @>d2E/o6-ԪsnŔ&Q[)&1u}g~r<O .{q|Ꭿ#ؽsa`TIB ?b=?랇SM@d-AЗF!_r![ѨsK~W{[cQ< ptn |o|(#H> t7_wnZT*~7O=cKX[oaccnݞ^AE%H"Q B$$*x۸qW}Nv)'ǟa3O&ۡ.nHBtÀ繨@ur"h`QXrAN##sQ6! Po@ &D$vsvsue<A,L`:"J+{ld!t3( zp;]Ba4]CQEDqe7XYZP*i.8l.٠B h,5LXؽ&tÐ3Lej#0'LnZLO$zJaRpWRK+ҌZ9q.U <1Iz@bE\HI`Lg w}EtnܸAF*)lsދD"nIJ{ï|#scOej:Kh'DwnSԏy'Zۡj$+G>?SFE9~eBZ|maFZd:S*\EUc}uY#(Z JiصwW A^G݃ 48Jᇚ:%+&Q)$M/P* )w]x '@a\\)r؅/,Jb̗=?sbfdtMб]U_ ӜLw*9BI#_-ytG ? SdB,pA@&$`'ۃT"$"3G !!BA@ґM*XY]L=W'P$iia8Pp[oS:Գ/oyo>CGs2X c3݉$g$u:s8*;Y t@3^o\l^Imˌnchd~gsqޕ5gom7P%矃~lw~Oe<ك"`g%JRn'H="BeX[qntHZ`eQˣm͵lP4VaJ2.tx=dKxD{.PRF0K%i(פ@qmn Bd*eJ]ծ 4z z]*t!e[r|r(JՊHTDjY:AU! şɰtrw*yYv$B1ɖ"qH&QNҋz ɭ1HdH2צOMÛLfuS[៤ Jr_@/0 "uɉ+ & !->eG)T(WyQ8FMvJv N'@2o=wvC"!vU_m28>SS>zgeNRcw U NdxWxŗe}sm)ssjLLm^YP٩cP$ "te!f!Cx/NܿO -1]n> /l7`סCG@) x7rصc?)I<&3_"61?d !oޏ~{{:t4Fb!ܳB^Áo=>'&H7ƖP 9:+w ! KՙR9_ \HF" ƮhH &p! PkU(d,1)tk`U0zIhdccJ*QO .r9e8ɧY Q?enDa>q" +Ɵ( `jx"w{R4+LD ӸԷ~MUNgGtnXH'(r fعH6ט@K2a/nTV%1/KRPM*(#@) S j(N;y?YL.WlF(dTV 剠vpqr|7H"{v-+.|,Bɣr)G>cj?S뫂wϿTP@ye~M"Ϙ ASNc3fd~hB")bA޺owY I?SGD{%U[ $[,.Kj^}?N0JWX[od?cr>څ^߹0d !at{=|5}抷⍃琠}Ja(|hCnL2 b~qRz ɒEjq3i=A;(JV(- FkCfv\`!P9T0 \% @GHCYPe(IPB&DxFz.=;CA %Y={ԱwP18vOzc 9kpp0M6ADM2SC0,q7m:kL1r}G5.,HA ];p9gί|ŠV 83{==2|_B-[J ? _Tاq#nc|q1I@9 |$7n!Wpe!7 QK(蔠!3 6QZ()ww~ Vq*~B|0Z M久KH<^qq?%jS] h,e?'F')M CM)$5%ciamx~ˬC8i3NK ?i z|ߟϔ1-oæ'@:aVu<Bm!/O0ʯsnj\}mC~||B &>%0MX *T*٩%^A۹r8GT(qUJ fTk2_1@ T\8$sDw<Ǒ%T6{4w=hЍ]Ba4sσP !)  C)LGSHZJ LDӋfV74%|P@A{shi=:* ʺ(".S+#(b*E1J/Hxx 2wa )ErR3P6+H|7 5R=LbhHxMD&ܓXI_J5¯)XUU(DJ$Kؚ^?۔7~/d]ױm TU!CeJC* =w$ LNeH]:/߅Lu 8Ͻ<4 sR&m Gnw9αC@)pƩ'Z=Cٶ_,3:6&c 47%9b\Ai{35ntbA[ Lݮd"IaQ cQ8X4j @44a/^c9/r0q՗ghTј[xkszT%'F8;㴓a PD #O~9,.ƳϿ暫. {7l)ʶp͎ȗ*X}ϓVt(U]mgcCsyFJ:Lӌ4jQ4fX]^E˲qQ4 R#X[Z . ıGtl-Csp{ R ZRd6SCkZò`K(U˨khî̒ S7P#}lRC7uPJM00N^ bvۀp<eZT`f<<߃1ڻvQؽe\.A4 dKYmIOR 3r)# \].R rVنe0tKz(k7\?ub {ђNЧ$?|SOfogNa'PJrd)c DSI& ?}*sN ~.HGU!9)s1,k`M1Иi-$A"j,Qx?_x'~?wOTnm?K8M6!p=o`FZQpg+/Qe|M˵]&69ݔ }m LDd}C~BѨU!t8A6F):E y$n(!7^{Ts܏ ;3cKpʈg-! ?.(r5H#TJ*(dbGsu- 0Cw]76Z_CF%ke Ε⁀H8t$!3p_yXf`:*J t4z(!rG xۖD҃QZB YTUU 2` sU>0)%0@i[ C 037S o0,z ^A8ki iHwz˄aiq\j5,̣RF.*(KݹR öaWJ(*tmðM4Ge50+1tC2.4ߺa1 i@748e `By0((l3d 7PjS cPӦخDLHhYW=?hFxp- U0@A-vd" `$]@oH @HA}~ Pe8@^)ŀ,}>QH@V @i `ꚕr&2ebJ|?_F1˓+O\tYrQC%/wV> kڷg>˿Cc7P]D,ܒ3Z+c裨?ΐHߐ9`${J}kӇ%zp<+.8lT%cb|_+-T|:HS& V*ii7E^%fKBEtz+x$; -0,߫) 2(@4iY kJ~SwS;UkD#S}"7r)Q{/)L! ,Z`]8S D~CGqe~ *Ô '*+H hPH4P.E~ߕpNgQ󪎓"__pik.Q˜읷3Ooŋq,\V-纩׏\v=jLvnBP-n;6;4|DJܓG 4|$)V*ڳN('c=OqHzBިFgk:=>TP֚ؽk@b?MVf UxߎVk>8=M6<+pǗ*+/aiyeslLnNHdAWBQA E^Ů|χU*U.Bt aȏ&V+eTU:aXRP)W'LIzaK2 ӒWB(s3--=KZƵzJ@HV IDATh$Le7 a=膎jÒ׮2It!PUaL*e@qӴ`X ӔꁦY**ɡeԡti2qiY T*yevR{dK7պ!;\ǁuxsA(C rs_?Lˈ~ ġL()!c@zx1MUUz4MzPAKR0 y5&_z)+K!RP&I=|._z})M/O8inF)n2|?_yԪh xqeC?lbϾqۯvG^#11?i?IGKt GJ-8F8%Kc(4hXkP\ sQ5-P|g?O!1Xrطgs歳:v#?ӄwGz(V_|A o냄ʬXH؏nu&K_Ls$/y^֖V`*B`}yes76@.Kӹ=Yfp0PQ}?@g%_a]["#<N A#cdɘuGVlG6**(X[Z!@{p8NB*Vnuٮe u;u\{(W*`0mN m!6Z\+(W+ X^\Zp]\]뤕)zp]A.9xc<kK+(ת`8H9*,BᣠCsR ە |ׁaY N0t]Q]f]<^σh9 `"* (%XY\+K(Uʀ X>zLmPV/KHTNwK-T`a| c2oᜎ&:NLsIǔ{$K y0BcP}DgN{,3OM aN{ \pvA]%^#Qs*$ ) J4j.D X8?/⼳O,OQwv~3/ǟ+)Kt .8tr31ӨEAA^~ ~/į".4uCxW4?c >_%K6^.<7_wE*i[H"b+9gF&@5}s\lly@%@YB|QF ;DU0l[`?}Ǚ[!yir5-ӄ}sF{/^ΫR5vXgK.<v:iqz7U\49ÓW;:RPcO?t+n:3 ޛGݒ]a}N{CV5&@23ec#!W!Y,V YC +`g! 2 !BPK-5Кznꜝ?ާNխ;~~^[{LU޿=mX/?ߍ/}v|o~bBnnMo'?}3jYӝ\-alrhdq&vm'׸*`@(kj9ګ`lmagY7r0 98FoA!TebafFH1z&FQM`bD(K~27y0jd 5h83׿& ةR,FUqv'_ BD|dF#cQI H $!KV/͔b`wo_oVpK/xK~q=O@w!k^~EPn^hs(ɺ;uϻ6;^Ux 82xa<rʻo?|qIW7y}xڙӸsWߛi8c PٞoS3;b];0?9$%>>s(2)(s9[_Z XV o{]ߎ?8wPUNllc!)7|fl x_:6Ɨȣ+w??sΞ;<??7x˛7t vzNIU/z<#w9};~>&|3EiMWiaI͌33uXYC8P˱*H$9:aH{.sх",6h2ODQVo㙦܏=Sf=1Ǟxm PL*Z}q J7LS~qO /{\39viu*O<׼eu)~g_u<2G|'V7mV|oas{[ OI(}߆y3[w+i{Y:jy5=f%^@՘bi_*e9X(P8@+$$?GK@oF  4>L=$/3gMhs?~7?~S7.56S ׿{qg7xvwvӹ<*«^q=kf> Oq{ Miq:25EoeGMZBxueyD$Y1ˊ`(  gRIHGzrb%qmys2O O/2@p!ȹİ+h/$, uX{"\&|r)@ )SK &8Ro`EВE#4ԧ,/TNq?Ώ[I%ySDfL@> x4F?!;Gxë_YOޜ>:u: rfV9bȌATI.bx'Xԧn,H$F ۠d?s7?ߎg]m%hc+}7~p]Mٮ_E?x3@ƯOݒ~qço_sΧ<Ͽcw7ۻT`'at<٤,|"`pJc!2]z#图;kwJԡP#7|_-93fБ ~x5WY\=6g[P)C!B5dg7'\Jy^ (0!N 3q̗À@{G~ o|ݫq̃WQ[>+/6hTw'?dž}UXf=|>/)>/N=yR=˼>B|(V7Ȅv6bWtG~̌? _:YlY1Y8CRn4DqDp=zj)U/7zp5^sQ%As4>q皙5l:׿x/ l\s8uN< C<|~/G!~{߂7U@D8w~w|ӟŇ?@f.KDYL{>fޗuσdvm'66pgCh4xai;hOb|xK_k} 鈑q J|?|Wnng9]qe׿:\zTUλO~N`\W:4MO>W]_|Ӗ"j?A^L d#NX]4-eGm!^uiS¿Oݒ.>}dc bݜ(ړ@N&`(dsӥf(Q`wd&l9fUX~&>O$]yeKpeYak{?ymYlg3}N8|W_%S.'ɟ_ԁqIqyWzb:9B9?=}3s4GN XoaoT&/n#9z^A=$@Yȣݿ?>Ƈ~=>zç4d&AfL$v~""F%Fe:uWz-/\r7x<Sh 8kO qMZEB@0+nHl2 BdD- p8Z PaT\ObYq@EUUT}8/^,A*":F xZ/Y; UwbX{LZž.FP %b:j8-D e J֢RdKB)_x=YZa^Ze}NZh-S_EP=n;g 1~, gNI)9kZ]'BQ @"$_IXAP{h&)Ï?YBx֦%OoxRh ˴`kC UxXDp/UBȉ]U;{{I'"B3 E,ޢCO aV4%Ylh!d%N׃4g0IfJxɳWno6\: -ó,]\z8KE)%Ϻ IDAT YŢ KV0R+  ]&/hB /E!NQ r5|@Iª(Fd\f`+!#J.PnnΡqBcXe u QhCi;QU 1$ʿSBgdQ ZVv$U z=o+]QǧU:,5L~ֹ&8c>je.bi(o,c3KrtvuTxTfݙ4,FBL`@dcIK@e~^8qƛƸ?&[uC n"༴XPm{ 8sE7K\y,NvifOlS7D1+7u'6y~0%5DLn4d,A*yzu&L4E,D}M14|ƙӧ_SxZ}rҀzX8t1?͊:Č5eқC@U!"$+ν°szڪ¨-ҵ$,@pZ]@Cj#$ b$ )_UewBUI*MhBXbG1&DD|'ns経2VXPE8/I9 jB$V$сRgx&ԕ*B DN BI YGrʄҩ> H<2e~T!XrV"l\%YY]L8ŖSD  DRf98uW=1 cKsq-0W\(Wo[J 9A:oZ#Z(+Ao&fm.Rb_VB3HXCG} TU*= RUղj&x8qO[IIu-K#l !x_3]qU <>@2XJ앣0`ip0*XB:/q9°ܪ)&53%$^VSz%Nd&iv0W{ʁGЬb (խOoVC.>Y i+MWZ*,TUtTj@Bd- k*CZ yػB,'qQ@4kvfRT]c-, WH1+T!vOV1ٖuZ8 Ȯ}{]fY%Ԣ֐t!ABYsD0@oR;Ȉ 88UО^r ,3[˷ߍs7˒u>䂢 άRb8'^ D]+3 HV<[?O}qg/2ߢ}Be?n J (3Zqvo'zM}s+LI)4ˉ׼ w }5<ȣi8.RBrn"X{Hnʩ# A1XB!3('„WJ.3"#Fήe6$njBj_[K A:>f8"Pb-TAxуSW| TU2.^67nI\ra_ s /:C4+ UY0HQ!^KބfT/֪=O nqrΤR !ixe֧|꤈M[e ۠ux y!UF)*?*|buQP4%!`t`D{=^XnT(3ێ?[[%n—A$167^ddz;ü e|.厡}|3Rm{w[V \+uNY^1̍ 3GrT™m1,]C] AˀE3!~i?x:+&etvzJKI(kBZ@c|2߱cB~r{>bAJB.ed6s5V ,#40 LE cs%1)n-`VV1!v?LF ho"~{lVFg(Hϧ {X "h_LGt_˰n 1-i?_m%ӰjؾCipcb|ӥZg}SeN; .Aj=g 㪧_KϜn1.}!PnN\(KPPk^^/\ȪǹT*)2#no#Foި+,œ^[Bӕ[f&pHJQQ&@ Ƞ9 ωQ(\QHYq'kE2nuN)τZx\ p9boav^m^=BUU$SƙA ߪX;M"^f8Аd00gIn|SqEQb#!R2:#pY\~i`ZSϽ5#sjK0}?Zֆ5ys^xg81KˀaHN(~9Q,@.9ed"GQx;)3PSSciy2c&NSPfJ% UU}u @1uК^e@"YAM_w+]  JJ}=p` IXiX>Sc@WVWdO[]r3~p }Pnjgb#d ?0bjTT"iEfqC+e<)hBp$V<ַS3JQ¬jr.lSAΣQex՗#;f= |3)_zO;S+HQThn@O`R#e޳ʆS+d| ҔO{5h0Qi>O#s0ĝTˑj &Ȳ*Q@4fQH0']|0/ho@s=ʲT+R>'e?))XkeW hAi Wl0Nj{?“gc:V-]$5Sah2Վa wto48MDNI9/]Po ktHKX+wR F\ceUǢJE ˒buBPj4+ Ҫ$o]py@Ԫ:1$ARUOSάe .Zڂ@uE|Y6fb T`")xqLС{r'sn!dԪ8@křbyN\_'[ x#ϼ*y;<EQ%/|^ ryt=GW@z0TK${Dj` :{ %@2%Qt.>VLK>")X",EMǶY3SMj/ P˶u,DNI؉C<GÍ!.=} {x^KNğG?~cf?х/:R4QƹZ8~cFk5DI9@B W nj75p6Lr99\P PcRM4 ިxG9R2%$:%"K|U!T[c PiNPɎz>1`;N΢"Ԉ :D≢` -Ya*k QB\c1P\C? ^$cPr}@tB*'r{k&i_4/*^l'Ϫ(Wʼn07 A1$K~GkF5@POozDiZ9j6Ħ>Bh_^r^ц)Tr~RHx}f꾮Ev7@YUҗo|q],=Б#0_ot/kF, %7%Z"a!8XuŔ\ĀʿCb*NV)Nd[:67>wuvBY]mEWR'˺4>;~U {I\%'Ӳ&Ӗkx)6FDT['+K=\}pK$( ,-` !jrؚota0`RoõQwwpE Kr0`-Gfx%榦ޯrYķ&RG*T&}e*)9S>#EG9^5R|$hqwKB%}EŽ3 *k9@S ±K=cfic̕޶ꯊkBofu=֣i!:+5.dr7 P$ȢNGBCc:큺`&kp2E_źTk  -^k!@[]@\wy/Eǵ'bM km&c}S~6%+3h #R -!I?X}!吷o[} CTU5?tL Q}&@5C/M~rYc*ru Ӟ1.[wk]7`Uw[M7Ȓoo֥Z8H+.\-<)Xş/BM8yZY.6NT*RpƳg>\⭹gΞP m^ON+ppS̙eIK&Xv"2^=V㽜yi3&a^HyKOqzLinC eoa4*@pS2O#~Zh$0~`FIp5l^N6b5Ik9 Tݩ}Ku$;AC@ǘGTD H9Zj,|.'7Ɨq)ڇ{ d kgs_"B[L/T<->дG *sI 5刲@RASAS%e\Dyx}9( yEqi(@"\sY"98"x 4QZIČipO® ׹V]W!TU!g^X8>iyai־c! Y]2 b]g W ϲ/%HPV ¨,c˟^alm|e IJnb5;cBl][g9S IDATfyD)uA<ИXs̓t80^k-Yqn^ncy=T>\&ej W]慇(u  (Y9(m*q/g5'+8|n`IVWNWg #@j 1"J'c82IB(@ΉR"ҐspyBY,ˤ98_P=z$!U$//< E! ur(PVP΃T(As hKuɛk*e|4TB#P)[O _^q XA*P6 Hc"+M|խR 3{HXÚ*V$A.TZ$ࣟ s~|ICni\beisk O 6XOv2]'@>y;ekw /d`&rhWA=`!MB t@ ʈb ;[PLjҗ JEsZzIf0 JsR lZ!T(]%\ +z(E;1y'DDeT Szwʸz@Ku P$׀@p^/7/?E%Hs9De82=;}rMǵB^*9q1J% #8:Q=@;=nx`d|&f \K!k$FT=k(Dk52! 03E9S ToLDe&Dp,H*h 1Tl9< :0}}<Kc=+=}ـR]NG;qe`(:PE=S~TU~]}o_΁AĹs?x/k`AD᎐FReMBYL|vȓrv>;Ɛ엝R{p1*PUWI@;` s^'OY<-:UT lnmgO"K,C#^MsNURJ:5藮V~J9;r8uLjh6 P+EOMwIz$71lZ̻Ƶ*s2P{ ָsπٕrH/mZ?}.ٷZR9oY͗鬋}?Od ^4gu Nl,J%'K? r 'N ϸW^~6;gor*+e0\+s5>n&19$oT-%! /[HY~q^CϏ.:yG0.},2 "`8Ɔ$&wy,/KVJ{]Q7euzćDk|4?pCx(Lo*n8?<]}[q aZx3Bsz?wI;}Y'Ӹj녕/nS1Vy$w>e|f$O^EK("nb b'yϴq2~EL 9O4Kw|%Y+O9#` : %}F#mTj'p7_K%0̌I:tR4s`'t?+3|{Cvd@@~}9u$"Âٙ?@L A`ev;mw;6$iR]_)Qu6*!V(>8}1y\E@Z?zr)c*6lxqtJ+C U&L],eyh4WٿH7b$xbjhy̨"g>KtתFt̻/0`‰fNm;=n˻=)ӌAe wEC?H 3CL!g~/m@6`qGE=y^dw^v#bJky ;aKp:6wj^[<;e~'J+C |ތx%)VVXN9?/ 'a?lBx|5bb#AR_J2~ T@4&%A_ 'ÜxyqAy5+eYh`)5ׇ[_r1A>.r5k:{pD PzKz( sBP»"(/!P"Zb. '@4Il%BRU5?^ 1W( cĀڎ}l-'>K @B 8-e֭m#sXY߯\*VDyG,'1I@Aͷԯyx,5&/XorirDJ껱$M>;qa4jY{= d5I"Xd JJv1Вh.:L2[SIm{-s7=a͂X"yڜdR<I'a9#LdSB%Vq)΍$PU*4^T!V"HbaF3")@iϘ1mL|{ HBt$r 9Alj!l،1s==~؇43 E3߄VrhwHZGa?av?Ks~u01Җ' 4gU"11C @;#IZ3‘WD-/L}2);L{d"6_n)i` qN\KytUS~xpHU >X-&1xΉ~ Gcu4 ޟ֦HהOeŞ[tBogEU+))x1x6-dc:1q@b1: OW'qg'%'ϡbF8LJw{C,82Ï4Zl ݡ`SI@!Yq4>6&"lS]69dY:eI^}kHqfwށHIFhT"}^(Vz(dVwg>zRDRVԛPKƪ⻔_@)礟c.P=%H2n&;%VhfkU %/ ,AR`JPY! fF`ѬXPᙛ _ @g$M:q-:w1󢎍y~s0w>Ca[W!8A$it. ^^i[0H~ea VR$uΤeLjYVQ/(!>D5g$G7Gܙݿ}f̌pI {DVUy k/u?lchuMeGI\ bdj$3:d6׾.D+Olel.8xzW߱Q(JouUeԖ99OM1?ds (~G^\f$A_5Mj]-ɩG+"#g^ `2fM2/p*$R ȥ P2]vjuPA,0SMamۂj9{P3dI^ kU"2 U&rW Zg6 Q{r!o9T.c IDATِp <2]lKV{+F'DZ`Q wf:y/g:5 5/laJLRR WW@@'t.l- @vlp@6 令NDyV}%dH_amZd "lznA$,LLu4@ w77j`IX„PB3]}w)?JV hI_G稡(;/K.@F{J7[4@u:c\qcټ"G!P9-oqwf,6 > oE^ / ]"{?8n} r g.ER?>n^R38X}p[xůk9z:PnK0F$%O |iPW< y6 Χ3w2\u>C帹Bq$xf(2 BY}0||ϐiVVOΠЬܭk9G"TBtVk9x+#ߌe\P9]ː 3meЧm-@LթQhLShyd- M$Gid/RޡUuZGԵm.ș:Jp7vF]/G_HIķ'NqqOӚAr .ʵ ݩW÷NqR!"`6.`mCJd0t^lwGqa-с[08TBJ^ɉ]_J(^b)rwݎE-]33b8Ohz;]z4ϯxoO F82`&Do&MF:N.ٛQ2h`?$f[u H֫Էо?$Z?:Mz%drj2Ǐn,uJ[GeѻAc*{F7@\t8t<E rhP7powǏU-ĵeh8odp[Y۶;nNU^ h~al??RٻFSv 9߶-S`X/_nç 5+UKu: E\g1,p8U zUzޚe3+KژڕΙ|a7u#s@n{ɍ1rjrJ%X!h!j/m墕NGH{syJVXJ⡌@s VgI|͐| +@Kp[2 EBw2' ̾jԟNyZg: cN'r*.TB7+L!`B7諃TrE(V O 4Q1WEx˃ el(sZ’&-GĈ@U8U82FYw镾}uWht9f=FoOEoWFfE)/i6:xNE) IsnEYթ=iO8nw$չ*{ ic^B`{!8ޟ_w=,Kf[onC'w`j_@%NJ;ʽ!R+ԏ GC>}aD sg 9KQ]S& ]>σui*Bj+^mP!5Vz[ L@tj/3`k]z :NjtSAhf\ZJ {kWص `$Y@۾X[B< H'\{!I]t!wA0k獬,*V[On%Eg@u=.e]k9I` P:~E[?Ŏ@Q78{k!MWp. oAwkgК)D}__n Ќ#L3y$){DO2.s-_>·#~ PgSXcQf K@o/oDx{yCYȲ?Bp|jYMU!s"gl8Ψ D M-mjo-8oqxm~ m`$r!/?(KG /l[U.IBzsz]ϙ^ p،r~C ݙ ~PhXެEu;nd{A;f:VDIr>:0ߑj,_wzad;9^º3zMٿ*&tR t kWى4hHhbuN/)} |g/B\uE8 "5@~Aם9考f,J̪mQWYb$_iZOۖ[K4>keYf!K<<>t:ьPN'GvPHp< {aR'GߎELy&=_GpG2kjl5>Vș~IOTNMa%+i/?݉zu,[$œх.ѭKƮck5?7Bg}C=e_zD_W&qIEeQ[TJTzz?fKjK@O@UhFl4sUD˸L]Jɵ\?n'D@ݚQWSDB.cxyyŹJ]tk9fJ8:}NVB%?Pf=ZYߦ |xz2qɣ>ƪdy\%(WTo'y=7x%:jB-4<`|Ϛn#CPe.c"(ѻ/MKSIk[r꛹b Դ:}Asa 9Y; <50(:TWx vN Go}jc e'Sd:y[^z}HoMjG [Ѫ:I3dG@ol])תH%e|&@C| @xzz~n+5|Y_3Z:%`^Owͣh̃!|p t~ش>k[ִnK'^ہ1rfF}Zjt\0~nvz'NÑ$ty 2?\/yC[A3'qםH8fQ5)4#y W`y@45j$ 'K O;dL:L$s@:DĤ܃1/8σ @KWV1,V e .ڼ8Y3`+l"\"'e0/kRKL׼-؅Ѯi**?#h LYW]K@aT2B]."$a,)$ b (J 7ջ%Vq@d)o@urVZ0_IJն л@+~&~C>%/5ծ'BĄs_hkK \is мF}1l6gnǠQPb,CehZ8u l Hx/!ݏQ&|| _p:f68_*"NH:쥰ֱ$ZBA1`1VN!V?iۭu^{|":xy~xb3f BdTPw޽ =7RM$ᴑA 8 zڧz;Onb^׋8GիۯLi?58\>Y!RHJ{@@ 2;:ֳN|f7gf R5ZcbB`kǎ-$WvR Ā'a?\Fdǫ)¢x}!g9 l\&0f3y]]Pk[;ĸ~!d]N՟ig\x:/份#S38zZnq oN^= :(Lfy.My&P<<0B;s\ʯIWlxȲ|"WsT1H![QsS-uI'3F?c! Â$Dv?:9A>VG"fWT/bxḘQ;e|p| ̪IA|x0%A?_ uȚگ;WNWHb گS#ݗ3*KB&ufv#Ge)_\M#Eot%W1Mkg\b lUc(7Enz.r*Y*[ϕ9 Ƭn~\t>g!pw b 8g빌ヨ~u鹈0 z#+= }RA{"Z#g S/oe ,〼(r`_/}G^f wx%|^~>ohYQonںk򶳻Y (zICyMj UPMc\r!N8p;dDOU8P9"ر>{iA=!֬L|jPc @ ۘ)l'0a Lovgp=Q1g, -r:!*$ 91եΛӢKªgHKuK`$$#H< :3]$X&s]rum[ne H/IN 93zDsֈḠy3'a)~ H^υhZ) %*sE$KI0YWq`_k;_7e ?`=_:23I_uHn>߀3@Ք[5Gn/_ *'JJzfrNc(EQiG]|n1pq<Ѷd2e2y38 _y{` ñ\Ddޟ5I8]t_Dp8\k+W>vILNm@]uj9MӠBN-jYt:abOniX挆v;S>9ۭMЖ)ISV[3)EҝYL7)}d#V=,y/|gY!ؐpPE{ u]Ҫ@L2*L0F3;H~;4[hupw9A u8c]]J1ׁ&`WT]W?y m ANC{ecAƛ|/#e\'Tuˈ!3[Nkʼaw,c\./iG/BgEZ !kʫ=H[VEhhǃT%^w@ $'Z99) FA^Q7o8ih(Pzjg . 4}33+Ǎƙ\O2LN,h wে@di1y6]zء3EOOWJ\k,Ke)G7]1m 2Y_ SG/o\)KEsUj>}YFrȪaO 'EBXKHf{ @J>ipQ̀<7Q\aws#p l[dUN O|W M}>?ox mӂ^BFQJ vg[`_}!d5J`\翺f{È^_|NdA;71鿺8wܽ2h0xLH_%X/h蚁 @S`}z'/](Q%_JZ #h1̉] Γ~L?\fP3 34ׯ . \p|Gpy3 .P#~6&](АPm[Д_ ckSGhpuQs]uzZ~iaʇ= + acLKVV5G=I ]rBTQKR~` '=+]F\\jgv SrQҷ~N5=% IDAT p,ޟ N 9#! `Pw RA@P!={zZ[*}u O&(6=n@У=c Yp>QmeU' ~<߁o&4~r~A9f=-~{t;Np^|,r5ܥ4JhHuUg'DML2!Ƈe+6Pa ټmK%aΠ5 .y8Y/r?IϚ>ɠ| Z<9og8K )ԱU6W ^F7Em&!qEk !,{N 2 p`\Me9_G rY<ݟ=i?ld`(PĚSfDT.g P6-ZުdziuNX"錺߿Nh[񄗗7<=?APskQ-Րssޅ#EʴIuq9VE`,Ӡusld(";ASG̓i?W5 9[:\)YX-^v5eF< LgHH$e/GaE>#q`3UZ[hHd`j60k;:484K][Bm.F]$/%jezO $y=|)?_Z8~ҕ(LqA2ƐFUŎn@^7o9#ڶŷoOO(?~j?rA^teĕp]lpύ!&~sZZaUA,,1g||p?CY 3EXAv e,x3NrHIv}_U>":Hg=?1a߃a:h=;?=%hhwٙmzWIS|l:8İfA٨_cKlnnL/518:VH BiP9v`?\-_ P0~ab⍄NfNq`P2i}z7<ܽGOr?Y:ykx l@~I? ?-imsPݮ˰)Kdy,C۶8WZxP?_>6  1E10^VC8ոV>@縹e!2 5!sooP r${Pt(~=kfhi/ Oȋvu-Mͮies|AY'4u --v7hO2lovuU7:G bEVx;x˱mVn퐗%nP+ 7乲in 7÷' {B]Uٔ68O "ooQ v7;{LbFk0<|˵6ߞ%:e96 ݣ.كeYʀpnnQ Y-=>6J<|{B۴`ppHNw䴷nAez6÷G=v7;m,1 1?݃m4M3c_#Z[AZt![c=|zq* D#a`Tp c6 >u_Ƶ-vQ6U3 A2'^ךי{\Ygaꂘf*Z.Gc(EkMe)#1pޢ:Uౣ؁R9no+ T,x>.E/jY' ^=,@G棁lFGFtp"rԙ ~q|?򶑧 x}yFSרy#{3|lWq{xN3A`ޞ_r9J\//hMU1R#BJ%rDZQB=x>>zںmݢmZubS|>:U,crAO'mႛ6{~)p:Qڶoh M]>>qtn?v@[7(LUk46urp@Ԡ|:Nos63U,6r*q:p@#sT5(ȋBS5ƨ.; "XtA=0)3#Ejp0)BUIm<]zњƅg'rX_G?bJlY}C}@ ̴^P 8 {Veda|a)+ 8i2jj+^ϡg1Yy,6"B\xFS0eȳ #NK;7%19#v,9q@v0 [E]f w?yR>1ZhK43 bYeQYlE_x=5f[0 rdLN3mDj)t:.E֥_bS`x<*vh$B`j=xwg(Kbs+A\X{@޺ .\ !sj] hB̘Z/@ Tuiqs{#CZvp!d)PnJdE|FSˀx-xrDj@Kc]'MHncex\n{K,(^/OB# m1ωA;fQ=`d +%0z~']2`ɟPej}xN%  8W3f?Jp-Gڅ[A&Gs^%I !MYm|,cN3&q遀YJxx=~z 2nUXpM׸V@`|\4䉹uz cy.} P_`:RQȵox{yfASUت)FNCǁtwMQ&#s{w' b e !IL}yŵuI7n Χq|^_q>{O\Ue@nO߿r9 :Qri;_eոuUc-ԍ.^(|>&eFk\v @ .GQ3@Vnm/l׵\o<)zO;N[%UWBB[PiGr8(+Xȴr܉H7ԉϥI's2ǭZy7@p!3m!\yzZS"ba2-rNy2OųGDrk\7KAp:QUk/"9"g==Lr5(sIyFh\O\=kCܵsTy+ HΪ|ns2 ~~#DǧK #NY Ė_/fh9^lw{<<)Z$N(Op Ee(7aft OrX:EhJ}h Zt8npx9-*O\jw,3wS%HxyU~srܔNnnor-+ |6v>Pn6ѴmSF}gy B]W 7ʠ6ut>qs{7Og߫>ppx{qDfgLQ@ŦDs8-W0>WrQ1{6 SDJNH곂 JR%>a&j ԛgFE ~Bg5t/ +PJ`THk@u:_ Tn b :p*|+N?~4WbDI12[Y#b\vy:a[ @ "e w(ohhF hJ)0D3O{M!`I>ۭ =ڲÔ [ _kψj/嵉+M1&==ƶ=r-@RAъEItx;ؖh5۶xȬM 9>~( h{6?xsHۏ#@.r:QUACԓvAP7w\|<$GO88e .6x; >q>U˫Ց:uU/ƣo xRu>ZekZ\٧LX%>> v[{ȴ]^V流y-/H0` Sp 4W;v`$z*%$1;2<]H8t_5HvUԨ *q2~荵e&"?\?vԩ GI۸ \꺟Ԏ3$a 10P7 lrHK~esV yfD_/o8Y5kFn' %o)OYZOqWQuGmjp܀_ E)Fj/yu Go}n b]mj<#^uiNȧ<y4WS*´LG](6j߶xy({xBΨmb < n6Lݔ͍L,xJ,C^8Nh4;uk @蜧pt/ފ.\оgU0 @y^wJ_B}9@ ۽X;m9t$WZC T f뚑oduh"gurNn0`XDnz v/-G"|#璞_^GЬ:zd:@xGoi^fpuBZ8 ;9#v- ~Xiw0\-^34ԍ|8e}):?똎89) v_y&D({Eɾ5 x^bg9<CU\Ƞ^%cVA!‹DU$`f gQS( DP:IFwN"ELJ{N'Tjp"cn B^>G<L~YBS7&?%s;.pnp!!Vp̓F4,.[Hm]⏁N;Q8 8+z%ɇsq&) S;'wAuKʾ֝(H&d쌶S݉ B uewӊpHҿ$bG0>uO aFZN(.Bqܸk}M`W?vG왉UKiˋ 0SPV_;K,DNUfiѶկ׭,y u8XvIc"~vq8q:W=r \'f] g}c挎IS<zQ=ɭ&="ﮭe}>HE6Wg(P>lD/{"X,r}y8reEiu)uc\}+|'XI/<˰nmQ9>G3J7>3 IDATX?Q<\Cv;8㺇Tv č=Gi!u [;0AtpLuqY9tW68|NM}'Q5@pFDDyPyc 6ЎiW݇e1K4QxU~`"+2DװL( xJ&I{ WI]zCBѝ ?an@pp`['S[U7zFbQ@xD!W @O 48u15a,p>W8Nhfz7"t[M;pA`rW@,(#eFIM -*$3wf}fN|g%Ԛ/|9z~E Pw Bky(c@+ Ȟ} AsvIt2e++/:, w[p.rg_(W4rJu=+M;n,v[bSdM X#z~5A|mM2~.@̅a$wy&"3 ZkBF ajW\ւ.'CV);3Ƶ ץ2S]H3ŽFbxSUlRPK[zXy!SѻF 9`1:~ۿgyymY( 4mPU5sp&|} `p*Ixޛz6BFYЁR߇^y %sWJb4\*uR0z-BwA˂hW0-80L+uP  @gFF ,]5c'Ul}wgT 6R"H57/ԂN{E )_ >_qhCY šE@[t _ +ɵ jX-j#ep:HKʕ;@Y,1pk4?[Ro_]o$=Ŵ~(Ϡfa+(zo|p93q74y@aAbtsC (cŕӢ=g< NT`%XIX/f8;3/NZye %*:t-3x-UpqO4~ZɳhL + 0V46:J@^n6-W $(ڶfTfIП182_O7 l{5&5[ԭ^x'zBx& T_A^d1Bs<%*?^&mGmNUiÀ")^ӹeI͞X_+{Ł\Ϥ@TOD]Y^X\. zzMPtBаۜ@`({HIC"ZㆊŪC Yu:OHtӶxvȵx(dBXw]@ro ES}7 |7,yǪK\`j〦cQi6$V\> 9B_vsC u;Y-0oh"zŨt8,%(i 39+Yi Fs|<\`O0zB|ʥ=DlU)A`F}OqAL .HN{<[`@p54w#m_]AܫMu8Ҧ? _'Yn [cbNoN'UUq~޺WF u]t>5;8͓yDS{O U,܋3%l+Y\ȝ$z[!\,z~^(a uݶ@`>O]@K7L90J^[{Lc9I)Q^-R]fE[Ѷ]X8@[+ jA(/ 5~ =+©r@,f$&OHpW!/Е 9bzO +G`(rl,X(2U% $=-oQ7ϚwG`yG+ ٢ᴝGMBj2Z`sj&m_wd 3 |#ص[ b Jע B]&D+.pAy\r"u.0ųanKJj׺>, \:k Z òb̩A8 ǬKCkC|Jo쑶9}\ÀWD[7hwVZdcK'x)"CF (&_p|;rdw I-g}ƐhJy&R 8E&ѹ;ti$Œ+)j=H' FY-Nst7W `]]2VkbipxJ 5:1dܥA88)}ߎexad;w2=h [n0 Y_AXOWKo['yvyP=rxd UFk܊ϗ'ѻN-ډXtS8X( fOi1ov701*䀨02O#d|=D:g$80JfxIWdMP>/ @}ڬ^QR6 2ٯ8dD0 'E!/Qv8"QZ´6O@W,AE?` yMw?i2@p:WhT$b Y9 E_Y\. <4^}b˥n=-&"*>bWdv; A=Իυ)+=v?d Hej7μ+Rb #\ssc_.b*ײe ^J6d!0)]: @80WM<.쭯SsnTq+fM|'W2t^P DTY砯 g=DzOv"BmdrOI]^PO"1B4h:qye KeayfLWJGGe0]zG%V&ǧ;/),Ԏ t$9eX^ Kq܆ 4P{]bR= &_ /P-'%j[$ [7\&#mBf+pp|F Y``y+=bP6 v=/t{b(4Qr᥀1y] @V( h-;@||])ӆ2j1>MTǞM1L8ߺ@Y磐u\>.s^b^yI#q/x0̳ bwD bV[Y"'`46k7z H4c\*u61,O,9N d`8鴘-g!UU8^vY֮>#^ l>! sEbEr{Fm:y 1! sh/> ` `9UlzkAd K t B {씙"H4~ a' X pӋQby^нÁp Ȋy<v mm;_ԗ[z0Gs꿵zu`S? 4#fW R69().4 to%A. Dk 5"U_ ecݢmZ4QHfS`!r4M'2e!7T|gvOs2ї8=!ҁczA&$u/af~աM.ς1g T=-f [NTۉد}3LtšO>뵨'AS1q̃hX,t!?կ˩'ꤧ hM3${(uZqQ:}~0m0ërZ+I4X_7-trF3Zzaeh{i iП@l/^?@Lyfze V5>\dDӶ-Z?L8"APʵ[x^^ @%&f&MiG}Ȁj|R6>P]3T &=@ҳ&|lj1) A]H~褔m]Ϭ4=IhrɢH9 LmIdYR7:XREhT5q'LG >& foBO"}m/H&×@M3E%,wн8ז]4HA#Gtԭ1yFbBN"iZLG;LY"6pc/;yiB(}]UwD2]3P'pwLwj |]9Ap1\-/ %<z@nKhxDLOLך), ,] vye{׺9k\ϳt2:?X TaJ 26@}\zU%3 ܢDZ>g$iB*!\O[SԸ8n9_"<&N"žvЅo?~"aK`wPL?n2htN` vuBb"#Džd%G_ 8>}ܺnzLX;)t6XJsoEOu8\Y&VsOV*b^\&unW=Н2N2+V%ABcQ칎(Jr0m}e$p rP;`@(T-dtip2sok:ޖ+-/ $-WX#4aVX IDATtl@߹ D 2=˦#qh'It( izzd02=u*zXhBט 'P)K٧ST^>c:䟻hewS?O/׾;G^T ?dO Y1Mi6\Qhnc;e6_ygd!}]擀B7ę(ٷl2.ӭg:1PK Gx|*[VO pI- "W:t&bN4?uxq9_0`}@Ih`&?&[LO0`ݮe~./g=wGM$%4n7K\jv,$Jo){{s  dGEI~XH65 k"\+G("Pzތc]Zc!J%,S&x&L~ÏdA~sR`d*^J\Cd?;H3>"qH CfNSG(a.ڐ $L|t5Ͼˆno@EYu#s`,WJ^_`7XlZ43'DcKmrIa :["[?KLLfH\R7i1> >='wڎO}< '=]B״aG,2Xa`waȲ( uS&\HeAy~Ma]YA*@D  RJG6wx:06"/83_D?%Bs6pv)߫->[roL Jv wLqCć ^E @_cn9k_y׻p5Jqg~-kEzg-)Hv ‘o4 )z5I tgtZ @S~mXX~B3"ȁYX+'i@`Hx3i'W߅rvߑgBVEfn/k{7w0ƴXb8ܶ ^5^?ktǴuxHҠpD3|^dl0$:1N tZT ɇ=c%0Z`VW0_pP謨4ְi ({%J6x^m1 ש懕k5G/ *`r˃؏108Aj2d@R!J9 #>_'gN+{-:i`3R'x."UB]dyZP @aIM3P%J `TËALNNԷ(^Ŝ8͏_#@kv"\ݺki,RFLgWFwt6|ʱ vrA^Q)U^E%~AۮŷÝ's<[ /dAw M=w V{6e3{\AyWi]6(Fֿr8bPb.P^ i'u Segy E5_`|bāX]u$1/ = B\ ¢ԘlIwv"62Hx"T+OO)!4E^3>t/1fqzð) )C;>!)YJN7]&ɿR n'> --;}߇l%Z)x9 `_ 7=y{2c [b yOL;tyBH D0H:ip`Oʤ]J\3ėτ <z9lbo(rF@ӎ Fe5ýƣdDZ^ &9-Ȝ6`.i $UZ u _e~E  3 '`6k5/p>8nO$rZA;Khc9H4IZomg9Em*xRU"J)n Ƹ(a[-qְmy{~oW5OҫtTCazE_k!$),BvE_bD#GYlV(8}쫡%\mc゠F>%/5h=ho){Hކ]%V6}os.Z%N$nJ*:G严oм%x6t&ÅR"!!q,^@۔OR ;Z'Cy" FSi JM4_JvgwcG9DgȪNM 6l(`ǚ#{&f}&ϒhgcЉ`ݓ23YY V:,)poȝ*!ҡ^t=Uk!R?#cAo֎>}X`42 F, 7I4] BBf ؔD&(eTz!$m;~[H#TX̒HW&IYP<++ϖ9D8,(4,UCI9O}J~O bPDTׄ"Ǐ~@6&v&s)?7.8ϕrn+ ptSH4sp~d"<# Hy *A_OHEdRF[!D٩RS@ʺG焻 ?^$ .-=o ,&ڽVN=kH Q?xDm-{u%~&)2xЄMkشw;`mYbgP#` It@@RBR?#["c^{W! L\`MQ>%;| zIѲ^# u:Qmo.hAZUVyBsP(*{ v eVDŽ @,pz#R"! EM;ɿp fNԟ:FPKlu"0SK4x)"N)ߙKY߉_^wث+՗5%'L'Mπ~ wǸ:eYCn)a|<Si@Z j. %Mt@mq`0PnHW@8zyZC}0~ U,"fE\Sێ$(pm-Ƕ6kkw y󌮦N$^(Ɍ v e6OQ +E`zWW3`T)ȅ]Rge pn6.=*0{,G#ϤruO*@z݁QV":Uaԁ@[,N+zj[X _5 J![˜!"×`TaBzc;^o;39Hȸ[}]r:nSZi ~%_#=jYUTnhvݟmoZV cZ)! Ҁ7{gok&VpC'((U`QP)"QFY֑7GM/9,@qT@Thȼptxp@N@D. t( M*<70?%¹g3c, Tx6Yӆi]s:`zH>M/N݈%}wchvƋkW+p{z߯vC֔%9`6d%~?vS_V-ч'#Sѭ (d<2RN)R/D'dm61#Ψ\[E R%QHjmYMGq!v=U^\B[ T@'А*5t4&R"\Xf[ +/2 iu]ST *6G&i$EW˯Cp+v`vETkaQ4n<< ^HyƓfC :."5ώckZ^H pj %QmY4?k0˱G|L8xVR$ iTA(mXY9  +$yb- v4 k(HE@M[ZWF/} _;%hjw Xj`0:4nj`@a9E;J" JZ] >ܵAP?R^bZ@ sup4p蝸߲:`u,"ܖ@*n?ځ-Tg+C6>#tKyF,'!::B{~Zr;gS;a'5xrf6Ry@4`S(z= =M9rA ֶL8x&L !3 U3e4ZT״GC\x3Jy{RO; .+*)/KI*Mk/R8n1>F8i "dcM ~gŪ ?@///`_5aD4hZ{u8U?QapW⮕^~0<ӳ9<;t7hJ9N pm@do -թ䛥*}s#:@F\6yw[Hv;&H _B !\  ]Wx}@ ڊ@@.遀hL;m9[*G_vVUbs.Bc팦Z6X`+"#~ '0;*49LJaG7`ZP ((>i=#BbKqOʔ넠.%>dg)9w6>]/| ,QNRaP6#*O-|,sB&Gd !}RL]tTbUHy \[džf=aLa~p揾6=x q73]p*P^r~&r/U%jˈ?+6CmiOO70w`4Wค/A6Л /@cü}/ ɳfv;TTYoN0*~/QF;Nԅ 8":R I>ހ1Fo&n-*Kn%7<3Ea";$(Ұy ٠ؽ<ڡN8(lQ@) @>QA$ t/Tq!Tw(a_2a6Hmo$NS#0ȃR=n?k*íU%].vMoTr IDATcovPUɊ^__a?>"v4hw3? !Jѯo#+TҤ-TB+}dN}^TAtN m3i x,f0)6!"o֠ݶHHF)ۜ -T(NK(DKAVںE4x/AwQ2D[@ַba9MH>~>) RW q(j ӕyrv{_U?=|-̞E~(-)=//Ͻ}/Q֛}ϟ72b2w؂IF%'a<ɩC-+Gbחl#%>>`7דK`x}!&̑(Ay6/rP Q&)ft&;(=a s<# ɚ5LH"A׶ u⍇v/] 6D3S$Cny҃ܠ. n@o֝S hDT 0h}}:s7)-/`If&/.uOI]lrOVӤ7oUYa+]#;`1pVC;qB̷jTwoڤIOy'|]x֙!B|4:|2G䜀ao_@<], @OHN 0!mi& TAWD89;r^&KD;HOh8WtiҺ~|.4/u6<$oyDK5/Yiat+dǿ(]_/>=߼$3.hmv[V/(?l LƓmt3Z$Wu C:"H'rv:B1l'tmIu- Ilˉ84 <4u @;jj:{D59d,qF($-@pٔ'4\ ( vOH>ѣ*QJbgG rK]dl2%+8gMʊ3>Y ~.XvCeJcnOOooض ~~ui@kp+Rai}mLb\7Ls/~[R~NU47Eч[)dIkO_@0A3q)\xppDǙ Y ~1&Z$B͟;a}%°?6: 1EDjeGOPh ^L 2HDPUIrkT0LUn+AX$`B$]P+J˩]RL>_ 1*ۥ'=)FWoք /\0JN/`v4"¾;쭑Iڴ_^ @q=|[w oۦa4c?f_2aR%gѐ.ZA}r_RLnf':J?Y39VF#!6r?F0R0h1ԋFe74ihD6p=I['4T O[ pS` 9A4heNaxU?k4# P(6Z>ا\BG%$K?1A>OςL;R 6^<5}`}g}ß?oַo0 h/OOƿ9$NbG>5 w"Q׵=/na:&95O=ж;1(TA}7Β'8RWeⶌ,; rY1&mIENDB`gammaray-2.11.3/docs/manual/style/kdab-footer.html000066400000000000000000000303331413310443000220170ustar00rootroot00000000000000

gammaray-2.11.3/docs/manual/style/kdab-header.html000066400000000000000000001057331413310443000217600ustar00rootroot00000000000000 gammaray-2.11.3/docs/manual/style/kdab-online.css000066400000000000000000000062041413310443000216310ustar00rootroot00000000000000/* main content area */ div.content { display: inline-block; float: left; width: 80%; padding-right: 2em; padding-top: 2em; } /* right sidebar */ div.sidebar { display: block; float: right; width: 20%; padding-top: 2em; } div.sidebar ul { list-style: none; padding-left: 1.3em; margin-top: 0px; /** Site style has a way too big 50py here. */ } div.sidebar h2, div.sidebar h3 { margin-bottom: 0.5em; } div.sidebar li:before { content: "\2023"; color: #7f9cad; display: inline-block; float: left; margin-left: -1.3em; width: 1.3em; } .toc,.sectionlist { padding: 1em; background-color: #eef2f4; margin-bottom: 3em } .toc:after { content: ''; position: relative; border-style: solid; border-width: 0 0 3em 3em; border-color: transparent #eef2f4; display: block; width: 0%; bottom: -3em; left: -1em; } .sectionlist:after { content: ''; position: relative; border-style: solid; border-width: 0 3em 3em 0; border-color: transparent #eef2f4; display: block; width: 100%; bottom: -3em; left: 1em; } .sectionlist:last-child::after { background-image: url("gammaray-logo-256.png"); background-size: contain; background-repeat: no-repeat; background-position: center bottom; border-width: 0; width: 100%; height: 256px; bottom: 1em; left: 0; } /* navigation breadcrumb at the top */ nav.breadcrumbHolder ul { list-style: none; padding-left: 0px; font-size: small; font-weight: 400; padding-top: 5px; padding-bottom: 5px; margin: 0px; } nav.breadcrumbHolder li { display: inline-block; position: relative; padding-right: 0.25em; color: #7F9CAD; } nav.breadcrumbHolder li a { color: #7F9CAD; } nav.breadcrumbHolder li a:hover { color: #4C758D; } nav.breadcrumbHolder li:after { color: #7F9CAD; content: "\2023"; padding-left: 0.5em; } nav.breadcrumbHolder ul li:nth-last-child(2) { color: #7F9CAD; font-weight: 700; } #buildversion { font-style:italic; font-size:small; float:right } #buildversion:after { content:none; } /* forward/back links */ .naviNextPrevious.headerNavi { display: none; } .naviNextPrevious.footerNavi { margin-top: 25px; max-width: 100%; } .nextPage { float: right; } /* TODO use KDAB-style triangles instead of <> */ .prevPage::before { content: "< "; } .nextPage::after { content: " >"; } /* content styling */ table { border-collapse:collapse; border-spacing:0; margin-bottom:25px; width:100% } table .odd { background-color:#f9f9f9 } table thead { text-align:left; padding-left:20px } table,table td,table th { border:1px solid #eef2f4 } table td,table th { padding:5px 20px; line-height:1.3 } h1 { /* prevent base style from upper-casing our titles */ text-transform: none; } .content ul { list-style: none; } .content li:before { content: "\2023"; color: #7f9cad; margin-left: -1em; display: inline-block; width: 1em; } /* title page splash screen image */ #splashscreen { width: 100%; padding-bottom: 2em; } gammaray-2.11.3/docs/manual/style/qt5-sidebar.html000066400000000000000000000047001413310443000217410ustar00rootroot00000000000000 gammaray-2.11.3/examples/000077500000000000000000000000001413310443000152035ustar00rootroot00000000000000gammaray-2.11.3/examples/CMakeLists.txt000066400000000000000000000004461413310443000177470ustar00rootroot00000000000000add_subdirectory(quick-batching) add_subdirectory(quick-event-handling) add_subdirectory(state-machine) if(Qt5Widgets_FOUND) add_subdirectory(signal-slot) add_subdirectory(timer) add_subdirectory(widget-layouting) endif() if(Qt53DExtras_FOUND) add_subdirectory(qt3d-geometry) endif() gammaray-2.11.3/examples/qt3d-geometry/000077500000000000000000000000001413310443000177075ustar00rootroot00000000000000gammaray-2.11.3/examples/qt3d-geometry/CMakeLists.txt000066400000000000000000000003061413310443000224460ustar00rootroot00000000000000add_executable(example-qt3d-geometry qt3d-geometry.cpp mycylinder.cpp) target_link_libraries(example-qt3d-geometry Qt5::3DExtras) if(QNXNTO) target_link_libraries(example-qt3d-geometry m) endif() gammaray-2.11.3/examples/qt3d-geometry/mycylinder.cpp000066400000000000000000000205751413310443000226030ustar00rootroot00000000000000/* mycylinder.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "mycylinder.h" #include #include #include #include #include class MyCylinderGeometry : public Qt3DRender::QGeometry { Q_OBJECT public: explicit MyCylinderGeometry(QNode *parent = nullptr); ~MyCylinderGeometry(); private: void createVertexData(); void createSidesVertices(float *&verticesPtr); void createDiscVertices(float *&verticesPtr, float yPosition); void createIndexData(); void createSidesIndices(quint16 *&indicesPtr); void createDiscIndices(quint16 *&indicesPtr, int discCenterIndex, float yPosition); Qt3DRender::QAttribute *m_positionAttribute; Qt3DRender::QAttribute *m_normalAttribute; Qt3DRender::QAttribute *m_indexAttribute; Qt3DRender::QBuffer *m_vertexBuffer; Qt3DRender::QBuffer *m_indexBuffer; int m_rings = 7; int m_slices = 16; float m_radius = 1.0f; float m_length = 4.0f; }; MyCylinderGeometry::MyCylinderGeometry(QNode* parent) : QGeometry(parent) { m_positionAttribute = new Qt3DRender::QAttribute(this); m_normalAttribute = new Qt3DRender::QAttribute(this); m_indexAttribute = new Qt3DRender::QAttribute(this); m_vertexBuffer = new Qt3DRender::QBuffer(Qt3DRender::QBuffer::VertexBuffer, this); m_indexBuffer = new Qt3DRender::QBuffer(Qt3DRender::QBuffer::IndexBuffer, this); // vec3 pos, vec3 normal const quint32 elementSize = 3 + 3; const quint32 stride = elementSize * sizeof(float); const int nVerts = (m_slices + 1) * m_rings + 2 * (m_slices + 1) + 2; const int faces = (m_slices * 2) * (m_rings - 1) + (m_slices * 2); m_positionAttribute->setName(Qt3DRender::QAttribute::defaultPositionAttributeName()); m_positionAttribute->setDataType(Qt3DRender::QAttribute::Float); m_positionAttribute->setDataSize(3); m_positionAttribute->setAttributeType(Qt3DRender::QAttribute::VertexAttribute); m_positionAttribute->setBuffer(m_vertexBuffer); m_positionAttribute->setByteStride(stride); m_positionAttribute->setCount(nVerts); m_normalAttribute->setName(Qt3DRender::QAttribute::defaultNormalAttributeName()); m_normalAttribute->setDataType(Qt3DRender::QAttribute::Float); m_normalAttribute->setDataSize(3); m_normalAttribute->setAttributeType(Qt3DRender::QAttribute::VertexAttribute); m_normalAttribute->setBuffer(m_vertexBuffer); m_normalAttribute->setByteStride(stride); m_normalAttribute->setByteOffset(3 * sizeof(float)); m_normalAttribute->setCount(nVerts); m_indexAttribute->setAttributeType(Qt3DRender::QAttribute::IndexAttribute); m_indexAttribute->setDataType(Qt3DRender::QAttribute::UnsignedShort); m_indexAttribute->setBuffer(m_indexBuffer); m_indexAttribute->setCount(faces * 3); createVertexData(); createIndexData(); addAttribute(m_positionAttribute); addAttribute(m_normalAttribute); addAttribute(m_indexAttribute); } MyCylinderGeometry::~MyCylinderGeometry() { } void MyCylinderGeometry::createVertexData() { const int verticesCount = (m_slices + 1) * m_rings + 2 * (m_slices + 1) + 2; // vec3 pos, vec3 normal const quint32 vertexSize = (3 + 3) * sizeof(float); QByteArray verticesData; verticesData.resize(vertexSize * verticesCount); float *verticesPtr = reinterpret_cast(verticesData.data()); createSidesVertices(verticesPtr); createDiscVertices(verticesPtr, -m_length * 0.5f); createDiscVertices(verticesPtr, m_length * 0.5f); m_vertexBuffer->setData(verticesData); } void MyCylinderGeometry::createSidesVertices(float*& verticesPtr) { //! [Wrong side normals] const float dY = m_length / static_cast(m_rings - 1); const float dTheta = (M_PI * 2) / static_cast(m_slices); for (int ring = 0; ring < m_rings; ++ring) { const float y = -m_length / 2.0f + static_cast(ring) * dY; for (int slice = 0; slice <= m_slices; ++slice) { const float theta = static_cast(slice) * dTheta; *verticesPtr++ = m_radius * qCos(theta);; *verticesPtr++ = y; *verticesPtr++ = m_radius * qSin(theta); QVector3D n(qCos(theta), -y *0.5f, qSin(theta)); n.normalize(); *verticesPtr++ = n.x(); *verticesPtr++ = n.y(); *verticesPtr++ = n.z(); } } //! [Wrong side normals] } void MyCylinderGeometry::createDiscVertices(float *&verticesPtr, float yPosition) { const float dTheta = (M_PI * 2) / static_cast(m_slices); const float yNormal = (yPosition < 0.0f) ? -1.0f : 1.0f; *verticesPtr++ = 0.0f; *verticesPtr++ = yPosition; *verticesPtr++ = 0.0f; *verticesPtr++ = 0.0f; *verticesPtr++ = yNormal; *verticesPtr++ = 0.0f; for (int slice = 0; slice <= m_slices; ++slice) { const float theta = static_cast(slice) * dTheta; *verticesPtr++ = m_radius * qCos(theta); *verticesPtr++ = yPosition; *verticesPtr++ = m_radius * qSin(theta); *verticesPtr++ = 0.0f; *verticesPtr++ = yNormal; *verticesPtr++ = 0.0f; } } void MyCylinderGeometry::createIndexData() { const int facesCount = (m_slices * 2) * (m_rings - 1) // two tris per side, for each pair of adjacent rings + m_slices * 2; // two caps const int indicesCount = facesCount * 3; const int indexSize = sizeof(quint16); Q_ASSERT(indicesCount < 65536); QByteArray indicesBytes; indicesBytes.resize(indicesCount * indexSize); quint16 *indicesPtr = reinterpret_cast(indicesBytes.data()); createSidesIndices(indicesPtr); createDiscIndices(indicesPtr, m_rings * (m_slices + 1), -m_length * 0.5); createDiscIndices(indicesPtr, m_rings * (m_slices + 1) + m_slices + 2, m_length * 0.5); m_indexBuffer->setData(indicesBytes); } void MyCylinderGeometry::createSidesIndices(quint16 *&indicesPtr) { for (auto ring = 0; ring < m_rings - 1; ++ring) { const auto ringIndexStart = ring * (m_slices + 1); const auto nextRingIndexStart = (ring + 1) * (m_slices + 1); for (auto slice = 0; slice < m_slices; ++slice) { const auto nextSlice = slice + 1; *indicesPtr++ = (ringIndexStart + slice); *indicesPtr++ = (nextRingIndexStart + slice); *indicesPtr++ = (ringIndexStart + nextSlice); *indicesPtr++ = (ringIndexStart + nextSlice); *indicesPtr++ = (nextRingIndexStart + slice); *indicesPtr++ = (nextRingIndexStart + nextSlice); } } } void MyCylinderGeometry::createDiscIndices(quint16 *&indicesPtr, int discCenterIndex, float /*yPosition*/) { //! [Wrong bottom disc indexes] //const auto yNormal = (yPosition < 0.0f) ? -1.0f : 1.0f; for (auto slice = 0; slice < m_slices; ++slice) { const auto nextSlice = slice + 1; *indicesPtr++ = discCenterIndex; *indicesPtr++ = (discCenterIndex + 1 + nextSlice); *indicesPtr++ = (discCenterIndex + 1 + slice); //if (yNormal < 0.0f) // qSwap(*(indicesPtr -1), *(indicesPtr - 2)); } //! [Wrong bottom disc indexes] } MyCylinder::MyCylinder(Qt3DCore::QNode* parent) : QGeometryRenderer(parent) { setGeometry(new MyCylinderGeometry(this)); } MyCylinder::~MyCylinder() { } #include "mycylinder.moc" gammaray-2.11.3/examples/qt3d-geometry/mycylinder.h000066400000000000000000000025751413310443000222500ustar00rootroot00000000000000/* mycylinder.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef MYCYLINDER_H #define MYCYLINDER_H #include class MyCylinder : public Qt3DRender::QGeometryRenderer { Q_OBJECT public: explicit MyCylinder(Qt3DCore::QNode *parent = nullptr); ~MyCylinder(); }; #endif // MYCYLINDER_H gammaray-2.11.3/examples/qt3d-geometry/qt3d-geometry.cpp000066400000000000000000000061361413310443000231250ustar00rootroot00000000000000/* qt3d-geometry.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "mycylinder.h" #include #include #include #include #include #include #include #include #include int main(int argc, char* argv[]) { QGuiApplication app(argc, argv); Qt3DExtras::Qt3DWindow view; auto rootEntity = new Qt3DCore::QEntity; rootEntity->setObjectName("rootEntity"); auto cylinderEntity = new Qt3DCore::QEntity(rootEntity); cylinderEntity->setObjectName("cylinderEntity"); auto mesh = new MyCylinder; auto material = new Qt3DExtras::QPhongMaterial(rootEntity); material->setAmbient(QColor(0, 85, 128)); material->setDiffuse(QColor(0, 128, 192)); material->setSpecular(QColor(0, 170, 255)); auto transform = new Qt3DCore::QTransform; transform->setRotation(QQuaternion::fromAxisAndAngle(QVector3D(1, 0, 0), 45.0f)); auto animation = new QPropertyAnimation(transform); animation->setTargetObject(transform); animation->setPropertyName("rotationZ"); animation->setStartValue(QVariant::fromValue(0)); animation->setEndValue(QVariant::fromValue(360)); animation->setDuration(10000); animation->setLoopCount(-1); animation->start(); cylinderEntity->addComponent(mesh); cylinderEntity->addComponent(transform); cylinderEntity->addComponent(material); // Camera auto camera = view.camera(); camera->setObjectName("camera"); camera->lens()->setPerspectiveProjection(45.0f, 16.0f/9.0f, 0.1f, 1000.0f); camera->setPosition(QVector3D(0, 0, 6.0f)); camera->setViewCenter(QVector3D(0, 0, 0)); auto camController = new Qt3DExtras::QOrbitCameraController(rootEntity); camController->setLinearSpeed( 50.0f ); camController->setLookSpeed( 180.0f ); camController->setCamera(camera); view.setRootEntity(rootEntity); view.show(); return app.exec(); } gammaray-2.11.3/examples/qt3d-geometry/qt3d-geometry.pro000066400000000000000000000001301413310443000231270ustar00rootroot00000000000000# dummy file for qdoc SOURCES = qt3d-geometry.cpp mycylinder.cpp HEADERS = mycylinder.h gammaray-2.11.3/examples/quick-batching/000077500000000000000000000000001413310443000200745ustar00rootroot00000000000000gammaray-2.11.3/examples/quick-batching/CMakeLists.txt000066400000000000000000000000501413310443000226270ustar00rootroot00000000000000qml_lint(quick-batching.qml Slider.qml) gammaray-2.11.3/examples/quick-batching/Slider.qml000066400000000000000000000065501413310443000220370ustar00rootroot00000000000000/* Slider.qml This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ import QtQuick 2.0 Item { id: root property bool mirrorSlider: false property real minValue: 0 property real maxValue: 100 property real value: 50 ListView { id: view anchors.fill: parent anchors.rightMargin: mirrorSlider ? 0 : handle.width anchors.leftMargin: mirrorSlider ? handle.width : 0 orientation: Qt.Vertical interactive: false property int itemHeight: 7 model: height/itemHeight currentIndex: (root.value - root.minValue) / (root.maxValue - root.minValue) * view.count delegate: Item { width: view.width height: view.itemHeight property int entry: index //! [Slider delegate] Rectangle { property bool active: view.currentIndex <= index anchors.right: parent.right width: parent.width height: parent.height - 3 color: Qt.hsva((index/view.count)/3, active ? 1.0 : 0.5, active ? 1.0 : 0.75) opacity: active ? 1.0 : 1.0 - (view.currentIndex - index) / view.model scale: active ? 1.0 : 0.9 } //! [Slider delegate] } Rectangle { id: handle property real currentItemY: view.currentItem ? view.currentItem.y : 0 anchors.left: mirrorSlider ? undefined : parent.right anchors.right: mirrorSlider ? parent.left : undefined width: 10 height: width radius: 5 color: "darkgray" y: currentItemY - height / 2 + 3 } MouseArea { id: dragArea anchors.fill: view hoverEnabled: false preventStealing: true onClicked: updateCurrentIndex(mouse.x, mouse.y) onPositionChanged: updateCurrentIndex(mouse.x, mouse.y) function updateCurrentIndex(x, y) { var item = view.itemAt(x, y); if (item) { var newValue = item.entry / view.count * (root.maxValue - root.minValue) + root.minValue; root.value = Math.min(root.maxValue, Math.max(root.minValue, newValue)); } } } } } gammaray-2.11.3/examples/quick-batching/quick-batching.pro000066400000000000000000000000771413310443000235130ustar00rootroot00000000000000# dummy file for qdoc SOURCES += quick-batching.qml Slider.qml gammaray-2.11.3/examples/quick-batching/quick-batching.qml000066400000000000000000000034471413310443000235100ustar00rootroot00000000000000/* quick-batching.qml This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ import QtQuick 2.0 import QtQuick.Window 2.0 Window { id: myWindow height: 600 width: 100 Row { id: myRow anchors.fill: parent anchors.margins: spacing spacing: 5 //! [Slider setup] Slider { id: leftSlider anchors.top: parent.top anchors.bottom: parent.bottom width: myWindow.width / 2 - 1.5*myRow.spacing } Slider { id: rightSlider anchors.top: parent.top anchors.bottom: parent.bottom width: myWindow.width / 2 - 1.5*myRow.spacing mirrorSlider: true } //! [Slider setup] } } gammaray-2.11.3/examples/quick-event-handling/000077500000000000000000000000001413310443000212205ustar00rootroot00000000000000gammaray-2.11.3/examples/quick-event-handling/CMakeLists.txt000066400000000000000000000000431413310443000237550ustar00rootroot00000000000000qml_lint(quick-event-handling.qml) gammaray-2.11.3/examples/quick-event-handling/quick-event-handling.pro000066400000000000000000000000711413310443000257550ustar00rootroot00000000000000# dummy file for qdoc SOURCES = quick-event-handling.qml gammaray-2.11.3/examples/quick-event-handling/quick-event-handling.qml000066400000000000000000000032741413310443000257560ustar00rootroot00000000000000/* quick-event-handling.qml This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ import QtQuick 2.0 import QtQuick.Controls 1.0 import QtQuick.Window 2.0 Window { id: myWindow height: 100 width: 2 * height //! [Button setup] Button { id: myButton anchors.fill: parent anchors.margins: 20 text: "Click me!" onClicked: console.log("Button clicked!") } //! [Button setup] //! [Hidden button] Button { id: hiddenButton anchors.fill: parent anchors.leftMargin: parent.width / 2 text: "Hidden button" opacity: 0 } //! [Hidden button] } gammaray-2.11.3/examples/signal-slot/000077500000000000000000000000001413310443000174375ustar00rootroot00000000000000gammaray-2.11.3/examples/signal-slot/CMakeLists.txt000066400000000000000000000001541413310443000221770ustar00rootroot00000000000000add_executable(example-signal-slot signal-slot.cpp) target_link_libraries(example-signal-slot Qt5::Widgets) gammaray-2.11.3/examples/signal-slot/signal-slot.cpp000066400000000000000000000050221413310443000223760ustar00rootroot00000000000000/* signal-slot.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include #include #include #include class Widget : public QWidget { Q_OBJECT public: explicit Widget(QWidget *parent = nullptr) : QWidget(parent) { //! [Widget setup] auto layout = new QVBoxLayout(this); m_emitButton = new QPushButton("Emit Signal", this); layout->addWidget(m_emitButton); connect(m_emitButton, &QPushButton::clicked, this, &Widget::signalEmitted); auto reconnectButton = new QPushButton("Reconnect Signal", this); layout->addWidget(reconnectButton); connect(reconnectButton, &QPushButton::clicked, this, &Widget::reconnectSignal); m_counter = new QLCDNumber(this); layout->addWidget(m_counter); //! [Widget setup] } public slots: void reconnectSignal() { //! [Mismatching reconnect] disconnect(m_emitButton, &QPushButton::clicked, this, &Widget::signalEmitted); connect(m_emitButton, &QAbstractButton::clicked, this, &Widget::signalEmitted); //! [Mismatching reconnect] } void signalEmitted() { m_counter->display(m_counter->intValue() + 1); } private: QPushButton *m_emitButton; QLCDNumber *m_counter; }; int main(int argc, char **argv) { QApplication app(argc, argv); Widget w; w.show(); return app.exec(); } #include "signal-slot.moc" gammaray-2.11.3/examples/signal-slot/signal-slot.pro000066400000000000000000000000631413310443000224140ustar00rootroot00000000000000# dummy file for qdoc... SOURCES = signal-slot.cpp gammaray-2.11.3/examples/state-machine/000077500000000000000000000000001413310443000177255ustar00rootroot00000000000000gammaray-2.11.3/examples/state-machine/CMakeLists.txt000066400000000000000000000000341413310443000224620ustar00rootroot00000000000000qml_lint(state-machine.qml) gammaray-2.11.3/examples/state-machine/state-machine.pro000066400000000000000000000000621413310443000231670ustar00rootroot00000000000000# dummy file for qdoc SOURCES = state-machine.qml gammaray-2.11.3/examples/state-machine/state-machine.qml000066400000000000000000000111531413310443000231630ustar00rootroot00000000000000/* state-machine.qml This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ import QtQuick 2.5 import QtQuick.Window 2.0 import QtQml.StateMachine 1.0 Rectangle { width: 120 height: 4*width color: "lightsteelblue" Rectangle { id: brokenTrafficLight color: "black" width: parent.width height: 3*width Column { anchors.fill: parent anchors.margins: spacing spacing: 5 Rectangle { id: redLight color: red.active || redToGreen.active ? "red" : "gray" anchors.left: parent.left anchors.right: parent.right height: width radius: width/2 } Rectangle { id: yellowLight color: redToGreen.active || greenToRed.active ? "yellow" : "gray" anchors.left: parent.left anchors.right: parent.right height: width radius: width/2 } Rectangle { id: greenLight color: green.active ? "green" : "gray" anchors.left: parent.left anchors.right: parent.right height: width radius: width/2 } } } Rectangle { id: stand color: brokenTrafficLight.color width: brokenTrafficLight.width / 3 anchors.top: brokenTrafficLight.bottom anchors.horizontalCenter: brokenTrafficLight.horizontalCenter anchors.bottom: parent.bottom Rectangle { id: buttonBase anchors.left: parent.left anchors.right: parent.right anchors.verticalCenter: parent.verticalCenter anchors.margins: 5 height: width color: "gray" MouseArea { id: onOffSwitch anchors.fill: parent } Rectangle { id: button color: "cyan" width: parent.width height: parent.height/2 y: off.active ? height : 0 } } } StateMachine { id: brokenTrafficLightMachine running: true initialState: running State { id: running initialState: red State { id: red TimeoutTransition { timeout: 5000 targetState: redToGreen } } State { id: redToGreen TimeoutTransition { timeout: 1500 targetState: green } } //! [Wrong transition] State { id: green TimeoutTransition { timeout: 5000 targetState: red } } //! [Wrong transition] State { id: greenToRed TimeoutTransition { timeout: 1500 targetState: red } } HistoryState { id: pause defaultState: red } SignalTransition { signal: onOffSwitch.clicked targetState: off } } State { id: off SignalTransition { signal: onOffSwitch.clicked targetState: pause } } } } gammaray-2.11.3/examples/timer/000077500000000000000000000000001413310443000163235ustar00rootroot00000000000000gammaray-2.11.3/examples/timer/CMakeLists.txt000066400000000000000000000001321413310443000210570ustar00rootroot00000000000000add_executable(example-timer timer.cpp) target_link_libraries(example-timer Qt5::Widgets) gammaray-2.11.3/examples/timer/timer.cpp000066400000000000000000000050531413310443000201520ustar00rootroot00000000000000/* timer.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include #include #include #include #include class MyUpdateTimer : public QObject { Q_OBJECT public: explicit MyUpdateTimer(QWidget *widget, QObject *parent = nullptr) : QObject(parent) , m_widget(widget) { } void start() { startTimer(0); } protected: //! [Missing killTimer] void timerEvent(QTimerEvent *event) override { Q_UNUSED(event); m_widget->repaint(); // killTimer(event->timerId()); } //! [Missing killTimer] private: QWidget *m_widget; }; int main(int argc, char **argv) { QApplication app(argc, argv); QWidget w; auto layout = new QVBoxLayout(&w); auto btn = new QPushButton("Create QTimer", &w); QObject::connect(btn, &QPushButton::clicked, &w, [&w]() { //! [Missing setSingleShot] auto timer = new QTimer(&w); QObject::connect(timer, SIGNAL(timeout()), &w, SLOT(repaint())); timer->setInterval(0); // timer->setSingleShot(true); timer->start(); //! [Missing setSingleShot] }); layout->addWidget(btn); btn = new QPushButton("Start Timer", &w); QObject::connect(btn, &QPushButton::clicked, &w, [&w]() { auto timer = new MyUpdateTimer(&w, &w); timer->start(); }); layout->addWidget(btn); w.show(); return app.exec(); } #include "timer.moc" gammaray-2.11.3/examples/timer/timer.pro000066400000000000000000000000521413310443000201620ustar00rootroot00000000000000# dummy file for qdoc SOURCES = timer.cpp gammaray-2.11.3/examples/widget-layouting/000077500000000000000000000000001413310443000204775ustar00rootroot00000000000000gammaray-2.11.3/examples/widget-layouting/CMakeLists.txt000066400000000000000000000002631413310443000232400ustar00rootroot00000000000000set(widget_layouting_srcs widget-layouting.cpp ) add_executable(example-widget-layouting ${widget_layouting_srcs}) target_link_libraries(example-widget-layouting Qt5::Widgets) gammaray-2.11.3/examples/widget-layouting/contactform.ui000066400000000000000000000421301413310443000233550ustar00rootroot00000000000000 ContactForm 0 0 1052 524 Form Details Sa&lutation: salutation First Na&me: first_name Last Name: last_name &Title: title &Department: department &Account Name: account_id Select Account... .. Lead Source: lead_source Reports to: reports_to_id Primary Email: email1 Send Email Other Email: email2 Send Email Assi&gned to: assigned_user_id View Notes Qt::Horizontal 40 20 100 100 - true Other Details Office Phone: phone_work Mobile: phone_mobile Home: phone_home Fa&x: phone_fax Other Phone: phone_other Do &Not Call: do_not_call B&irthdate: birthdate Clear &Edit Assistant: assistant Assistant Phone: phoneAssistant Primary Address Street: primaryAddressStreet &City: primaryAddressCity State: primaryAddressState Postal Code: primaryAddressPostalcode Co&untry: primaryAddressCountry Other Address Street: altAddressStreet Cit&y: altAddressCity State: altAddressState Postal Code: altAddressPostalcode Country: altAddressCountry salutation first_name last_name title department account_id buttonOpenAccount lead_source reports_to_id email1 email2 assigned_user_id phone_work phone_mobile phone_home phone_fax phone_other do_not_call birthdate clearDateButton calendarButton assistant phoneAssistant primaryAddressStreet primaryAddressCity primaryAddressState primaryAddressPostalcode primaryAddressCountry altAddressStreet altAddressCity altAddressState altAddressPostalcode altAddressCountry gammaray-2.11.3/examples/widget-layouting/widget-layouting.cpp000066400000000000000000000031711413310443000245010ustar00rootroot00000000000000/* widget-layouting.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "ui_contactform.h" #include class ContactForm : public QWidget { Q_OBJECT public: explicit ContactForm(QWidget *parent = nullptr) : QWidget(parent), ui(new Ui::ContactForm) { ui->setupUi(this); } private: QScopedPointer ui; }; int main(int argc, char** argv) { QApplication app(argc, argv); QWidget w1; QWidget w2; ContactForm form; form.show(); return app.exec(); } #include "widget-layouting.moc" gammaray-2.11.3/examples/widget-layouting/widget-layouting.pro000066400000000000000000000001211413310443000245070ustar00rootroot00000000000000# dummy .pro file for qdoc SOURCES = widget-layouting.cpp FORMS = contactform.ui gammaray-2.11.3/examples/yocto/000077500000000000000000000000001413310443000163405ustar00rootroot00000000000000gammaray-2.11.3/examples/yocto/gammaray_git.bb000066400000000000000000000012011413310443000213000ustar00rootroot00000000000000SUMMARY = "GammaRay Qt introspection probe" HOMEPAGE = "https://www.kdab.com/gammaray" LICENSE = "GPLv2" LIC_FILES_CHKSUM = "file://LICENSE.GPL.txt;md5=2abfe5daa817fd4346b6d55293941415" inherit cmake_qt5 SRC_URI = "git://github.com/KDAB/GammaRay;branch=master" SRCREV = "1728dff87163c370fa6903a6d739d1a132b4c62f" PV = "2.7.0+git${SRCPV}" DEPENDS = "qtdeclarative" S = "${WORKDIR}/git" EXTRA_OECMAKE += " -DGAMMARAY_BUILD_UI=OFF" FILES_${PN}-dev += " \ /usr/lib/cmake/* \ /usr/mkspecs/modules/* \ " FILES_${PN}-dbg += " \ /usr/lib/.debug/* \ /usr/lib/gammaray/*/*/.debug \ /usr/lib/gammaray/*/*/styles/.debug \ " gammaray-2.11.3/format.config.uncrustify.4_spaces000066400000000000000000002151671413310443000217720ustar00rootroot00000000000000# Uncrustify 0.60 # Qt style from https://gist.github.com/trollixx/11204806 # # Qt specific options # set FOR foreach set FOR forever # # General options # # The type of line endings newlines = auto # auto/lf/crlf/cr # The original size of tabs in the input input_tab_size = 4 # number # The size of tabs in the output (only used if align_with_tabs=true) output_tab_size = 4 # number # The ASCII value of the string escape char, usually 92 (\) or 94 (^). (Pawn) string_escape_char = 92 # number # Alternate string escape char for Pawn. Only works right before the quote char. string_escape_char2 = 0 # number # Allow interpreting '>=' and '>>=' as part of a template in 'void f(list>=val);'. # If true (default), 'assert(x<0 && y>=3)' will be broken. # Improvements to template detection may make this option obsolete. tok_split_gte = false # false/true # Control what to do with the UTF-8 BOM (recommend 'remove') utf8_bom = ignore # ignore/add/remove/force # If the file contains bytes with values between 128 and 255, but is not UTF-8, then output as UTF-8 utf8_byte = false # false/true # Force the output encoding to UTF-8 utf8_force = false # false/true # # Indenting # # The number of columns to indent per level. # Usually 2, 3, 4, or 8. indent_columns = 4 # number # The continuation indent. If non-zero, this overrides the indent of '(' and '=' continuation indents. # For FreeBSD, this is set to 4. Negative value is absolute and not increased for each ( level indent_continue = 0 # number # How to use tabs when indenting code # 0=spaces only # 1=indent with tabs to brace level, align with spaces # 2=indent and align with tabs, using spaces when not on a tabstop indent_with_tabs = 0 # number # Comments that are not a brace level are indented with tabs on a tabstop. # Requires indent_with_tabs=2. If false, will use spaces. indent_cmt_with_tabs = false # false/true # Whether to indent strings broken by '\' so that they line up indent_align_string = true # false/true # The number of spaces to indent multi-line XML strings. # Requires indent_align_string=True indent_xml_string = 0 # number # Spaces to indent '{' from level indent_brace = 0 # number # Whether braces are indented to the body level indent_braces = false # false/true # Disabled indenting function braces if indent_braces is true indent_braces_no_func = false # false/true # Disabled indenting class braces if indent_braces is true indent_braces_no_class = false # false/true # Disabled indenting struct braces if indent_braces is true indent_braces_no_struct = false # false/true # Indent based on the size of the brace parent, i.e. 'if' => 3 spaces, 'for' => 4 spaces, etc. indent_brace_parent = false # false/true # Whether the 'namespace' body is indented indent_namespace = false # false/true # The number of spaces to indent a namespace block indent_namespace_level = 0 # number # If the body of the namespace is longer than this number, it won't be indented. # Requires indent_namespace=true. Default=0 (no limit) indent_namespace_limit = 0 # number # Whether the 'extern "C"' body is indented indent_extern = false # false/true # Whether the 'class' body is indented indent_class = true # false/true # Whether to indent the stuff after a leading class colon indent_class_colon = false # false/true indent_constr_colon = true # Virtual indent from the ':' for member initializers. Default is 2 indent_ctor_init_leading = 0 # number # Additional indenting for constructor initializer list indent_ctor_init = 0 # number # False=treat 'else\nif' as 'else if' for indenting purposes # True=indent the 'if' one level indent_else_if = false # false/true # Amount to indent variable declarations after a open brace. neg=relative, pos=absolute indent_var_def_blk = 0 # number # Indent continued variable declarations instead of aligning. indent_var_def_cont = false # false/true # True: force indentation of function definition to start in column 1 # False: use the default behavior indent_func_def_force_col1 = false # false/true # True: indent continued function call parameters one indent level # False: align parameters under the open paren indent_func_call_param = false # false/true # Same as indent_func_call_param, but for function defs indent_func_def_param = false # false/true # Same as indent_func_call_param, but for function protos indent_func_proto_param = false # false/true # Same as indent_func_call_param, but for class declarations indent_func_class_param = false # false/true # Same as indent_func_call_param, but for class variable constructors indent_func_ctor_var_param = false # false/true # Same as indent_func_call_param, but for templates indent_template_param = false # false/true # Double the indent for indent_func_xxx_param options indent_func_param_double = false # false/true # Indentation column for standalone 'const' function decl/proto qualifier indent_func_const = 0 # number # Indentation column for standalone 'throw' function decl/proto qualifier indent_func_throw = 0 # number # The number of spaces to indent a continued '->' or '.' # Usually set to 0, 1, or indent_columns. indent_member = 0 # number # Spaces to indent single line ('//') comments on lines before code indent_sing_line_comments = 0 # number # If set, will indent trailing single line ('//') comments relative # to the code instead of trying to keep the same absolute column indent_relative_single_line_comments = false # false/true # Spaces to indent 'case' from 'switch' # Usually 0 or indent_columns. indent_switch_case = 0 # number # Spaces to shift the 'case' line, without affecting any other lines # Usually 0. indent_case_shift = 0 # number # Spaces to indent '{' from 'case'. # By default, the brace will appear under the 'c' in case. # Usually set to 0 or indent_columns. indent_case_brace = 0 # number # Whether to indent comments found in first column indent_col1_comment = false # false/true # How to indent goto labels # >0 : absolute column where 1 is the leftmost column # <=0 : subtract from brace indent indent_label = 1 # number # Same as indent_label, but for access specifiers that are followed by a colon indent_access_spec = -4 # number # Indent the code after an access specifier by one level. # If set, this option forces 'indent_access_spec=0' indent_access_spec_body = false # false/true # If an open paren is followed by a newline, indent the next line so that it lines up after the open paren (not recommended) indent_paren_nl = false # false/true # Controls the indent of a close paren after a newline. # 0: Indent to body level # 1: Align under the open paren # 2: Indent to the brace level indent_paren_close = 0 # number # Controls the indent of a comma when inside a paren.If TRUE, aligns under the open paren indent_comma_paren = false # false/true # Controls the indent of a BOOL operator when inside a paren.If TRUE, aligns under the open paren indent_bool_paren = false # false/true # If 'indent_bool_paren' is true, controls the indent of the first expression. If TRUE, aligns the first expression to the following ones indent_first_bool_expr = false # false/true # If an open square is followed by a newline, indent the next line so that it lines up after the open square (not recommended) indent_square_nl = false # false/true # Don't change the relative indent of ESQL/C 'EXEC SQL' bodies indent_preserve_sql = false # false/true # Align continued statements at the '='. Default=True # If FALSE or the '=' is followed by a newline, the next line is indent one tab. indent_align_assign = true # false/true # Indent OC blocks at brace level instead of usual rules. indent_oc_block = false # false/true # Indent OC blocks in a message relative to the parameter name. # 0=use indent_oc_block rules, 1+=spaces to indent indent_oc_block_msg = 0 # number # Minimum indent for subsequent parameters indent_oc_msg_colon = 0 # number # # Spacing options # # Add or remove space around arithmetic operator '+', '-', '/', '*', etc sp_arith = ignore # ignore/add/remove/force # Add or remove space around assignment operator '=', '+=', etc sp_assign = force # ignore/add/remove/force # Add or remove space around '=' in C++11 lambda capture specifications. Overrides sp_assign sp_cpp_lambda_assign = remove # ignore/add/remove/force # Add or remove space after the capture specification in C++11 lambda. sp_cpp_lambda_paren = ignore # ignore/add/remove/force # Add or remove space around assignment operator '=' in a prototype sp_assign_default = force # ignore/add/remove/force # Add or remove space before assignment operator '=', '+=', etc. Overrides sp_assign. sp_before_assign = ignore # ignore/add/remove/force # Add or remove space after assignment operator '=', '+=', etc. Overrides sp_assign. sp_after_assign = ignore # ignore/add/remove/force # Add or remove space around assignment '=' in enum sp_enum_assign = force # ignore/add/remove/force # Add or remove space before assignment '=' in enum. Overrides sp_enum_assign. sp_enum_before_assign = ignore # ignore/add/remove/force # Add or remove space after assignment '=' in enum. Overrides sp_enum_assign. sp_enum_after_assign = ignore # ignore/add/remove/force # Add or remove space around preprocessor '##' concatenation operator. Default=Add sp_pp_concat = add # ignore/add/remove/force # Add or remove space after preprocessor '#' stringify operator. Also affects the '#@' charizing operator. sp_pp_stringify = ignore # ignore/add/remove/force # Add or remove space before preprocessor '#' stringify operator as in '#define x(y) L#y'. sp_before_pp_stringify = ignore # ignore/add/remove/force # Add or remove space around boolean operators '&&' and '||' sp_bool = force # ignore/add/remove/force # Add or remove space around compare operator '<', '>', '==', etc sp_compare = force # ignore/add/remove/force # Add or remove space inside '(' and ')' sp_inside_paren = remove # ignore/add/remove/force # Add or remove space between nested parens: '((' vs ') )' sp_paren_paren = remove # ignore/add/remove/force # Add or remove space between back-to-back parens: ')(' vs ') (' sp_cparen_oparen = ignore # ignore/add/remove/force # Whether to balance spaces inside nested parens sp_balance_nested_parens = false # false/true # Add or remove space between ')' and '{' sp_paren_brace = force # ignore/add/remove/force # Add or remove space before pointer star '*' sp_before_ptr_star = force # ignore/add/remove/force # Add or remove space before pointer star '*' that isn't followed by a variable name # If set to 'ignore', sp_before_ptr_star is used instead. sp_before_unnamed_ptr_star = ignore # ignore/add/remove/force # Add or remove space between pointer stars '*' sp_between_ptr_star = remove # ignore/add/remove/force # Add or remove space after pointer star '*', if followed by a word. sp_after_ptr_star = remove # ignore/add/remove/force # Add or remove space after pointer star '*', if followed by a qualifier. sp_after_ptr_star_qualifier = force # Add or remove space after a pointer star '*', if followed by a func proto/def. sp_after_ptr_star_func = remove # ignore/add/remove/force # Add or remove space after a pointer star '*', if followed by an open paren (function types). sp_ptr_star_paren = remove # ignore/add/remove/force # Add or remove space before a pointer star '*', if followed by a func proto/def. sp_before_ptr_star_func = force # ignore/add/remove/force # Add or remove space before a reference sign '&' sp_before_byref = force # ignore/add/remove/force # Add or remove space before a reference sign '&' that isn't followed by a variable name # If set to 'ignore', sp_before_byref is used instead. sp_before_unnamed_byref = ignore # ignore/add/remove/force # Add or remove space after reference sign '&', if followed by a word. sp_after_byref = remove # ignore/add/remove/force # Add or remove space after a reference sign '&', if followed by a func proto/def. sp_after_byref_func = ignore # ignore/add/remove/force # Add or remove space before a reference sign '&', if followed by a func proto/def. sp_before_byref_func = ignore # ignore/add/remove/force # Add or remove space between type and word. Default=Force sp_after_type = force # ignore/add/remove/force # Add or remove space before the paren in the D constructs 'template Foo(' and 'class Foo('. sp_before_template_paren = ignore # ignore/add/remove/force # Add or remove space in 'template <' vs 'template<'. # If set to ignore, sp_before_angle is used. sp_template_angle = remove # ignore/add/remove/force # Add or remove space before '<>' sp_before_angle = ignore # ignore/add/remove/force # Add or remove space inside '<' and '>' sp_inside_angle = ignore # ignore/add/remove/force # Add or remove space after '<>' sp_after_angle = ignore # ignore/add/remove/force # Add or remove space between '<>' and '(' as found in 'new List();' sp_angle_paren = remove # ignore/add/remove/force # Add or remove space between '<>' and a word as in 'List m;' sp_angle_word = force # ignore/add/remove/force # Add or remove space between '>' and '>' in '>>' (template stuff C++/C# only). Default=Add sp_angle_shift = add # ignore/add/remove/force # Permit removal of the space between '>>' in 'foo >' (C++11 only). Default=False # sp_angle_shift cannot remove the space without this option. sp_permit_cpp11_shift = false # false/true # Add or remove space before '(' of 'if', 'for', 'switch', and 'while' sp_before_sparen = force # ignore/add/remove/force # Add or remove space inside if-condition '(' and ')' sp_inside_sparen = remove # ignore/add/remove/force # Add or remove space before if-condition ')'. Overrides sp_inside_sparen. sp_inside_sparen_close = ignore # ignore/add/remove/force # Add or remove space before if-condition '('. Overrides sp_inside_sparen. sp_inside_sparen_open = ignore # ignore/add/remove/force # Add or remove space after ')' of 'if', 'for', 'switch', and 'while' sp_after_sparen = force # ignore/add/remove/force # Add or remove space between ')' and '{' of 'if', 'for', 'switch', and 'while' sp_sparen_brace = force # ignore/add/remove/force # Add or remove space between 'invariant' and '(' in the D language. sp_invariant_paren = ignore # ignore/add/remove/force # Add or remove space after the ')' in 'invariant (C) c' in the D language. sp_after_invariant_paren = ignore # ignore/add/remove/force # Add or remove space before empty statement ';' on 'if', 'for' and 'while' sp_special_semi = ignore # ignore/add/remove/force # Add or remove space before ';'. Default=Remove sp_before_semi = remove # ignore/add/remove/force # Add or remove space before ';' in non-empty 'for' statements sp_before_semi_for = remove # ignore/add/remove/force # Add or remove space before a semicolon of an empty part of a for statement. sp_before_semi_for_empty = remove # ignore/add/remove/force # Add or remove space after ';', except when followed by a comment. Default=Add sp_after_semi = force # ignore/add/remove/force # Add or remove space after ';' in non-empty 'for' statements. Default=Force sp_after_semi_for = force # ignore/add/remove/force # Add or remove space after the final semicolon of an empty part of a for statement: for ( ; ; ). sp_after_semi_for_empty = remove # ignore/add/remove/force # Add or remove space before '[' (except '[]') sp_before_square = ignore # ignore/add/remove/force # Add or remove space before '[]' sp_before_squares = ignore # ignore/add/remove/force # Add or remove space inside a non-empty '[' and ']' sp_inside_square = ignore # ignore/add/remove/force # Add or remove space after ',' sp_after_comma = force # ignore/add/remove/force # Add or remove space before ',' sp_before_comma = remove # ignore/add/remove/force # Add or remove space between an open paren and comma: '(,' vs '( ,' sp_paren_comma = force # ignore/add/remove/force # Add or remove space before the variadic '...' when preceded by a non-punctuator sp_before_ellipsis = ignore # ignore/add/remove/force # Add or remove space after class ':' sp_after_class_colon = force # ignore/add/remove/force # Add or remove space before class ':' sp_before_class_colon = force # ignore/add/remove/force # Add or remove space before case ':'. Default=Remove sp_before_case_colon = remove # ignore/add/remove/force # Add or remove space between 'operator' and operator sign sp_after_operator = ignore # ignore/add/remove/force # Add or remove space between the operator symbol and the open paren, as in 'operator ++(' sp_after_operator_sym = ignore # ignore/add/remove/force # Add or remove space after C/D cast, i.e. 'cast(int)a' vs 'cast(int) a' or '(int)a' vs '(int) a' sp_after_cast = remove # ignore/add/remove/force # Add or remove spaces inside cast parens sp_inside_paren_cast = remove # ignore/add/remove/force # Add or remove space between the type and open paren in a C++ cast, i.e. 'int(exp)' vs 'int (exp)' sp_cpp_cast_paren = remove # ignore/add/remove/force # Add or remove space between 'sizeof' and '(' sp_sizeof_paren = remove # ignore/add/remove/force # Add or remove space after the tag keyword (Pawn) sp_after_tag = ignore # ignore/add/remove/force # Add or remove space inside enum '{' and '}' sp_inside_braces_enum = ignore # ignore/add/remove/force # Add or remove space inside struct/union '{' and '}' sp_inside_braces_struct = ignore # ignore/add/remove/force # Add or remove space inside '{' and '}' sp_inside_braces = ignore # ignore/add/remove/force # Add or remove space inside '{}' sp_inside_braces_empty = ignore # ignore/add/remove/force # Add or remove space between return type and function name # A minimum of 1 is forced except for pointer return types. sp_type_func = ignore # ignore/add/remove/force # Add or remove space between function name and '(' on function declaration sp_func_proto_paren = remove # ignore/add/remove/force # Add or remove space between function name and '(' on function definition sp_func_def_paren = remove # ignore/add/remove/force # Add or remove space inside empty function '()' sp_inside_fparens = remove # ignore/add/remove/force # Add or remove space inside function '(' and ')' sp_inside_fparen = remove # ignore/add/remove/force # Add or remove space inside the first parens in the function type: 'void (*x)(...)' sp_inside_tparen = remove # ignore/add/remove/force # Add or remove between the parens in the function type: 'void (*x)(...)' sp_after_tparen_close = remove # ignore/add/remove/force # Add or remove space between ']' and '(' when part of a function call. sp_square_fparen = remove # ignore/add/remove/force # Add or remove space between ')' and '{' of function sp_fparen_brace = ignore # ignore/add/remove/force # Add or remove space between function name and '(' on function calls sp_func_call_paren = remove # ignore/add/remove/force # Add or remove space between function name and '()' on function calls without parameters. # If set to 'ignore' (the default), sp_func_call_paren is used. sp_func_call_paren_empty = ignore # ignore/add/remove/force # Add or remove space between the user function name and '(' on function calls # You need to set a keyword to be a user function, like this: 'set func_call_user _' in the config file. sp_func_call_user_paren = force # ignore/add/remove/force # Add or remove space between a constructor/destructor and the open paren sp_func_class_paren = ignore # ignore/add/remove/force # Add or remove space between 'return' and '(' sp_return_paren = force # ignore/add/remove/force # Add or remove space between '__attribute__' and '(' sp_attribute_paren = ignore # ignore/add/remove/force # Add or remove space between 'defined' and '(' in '#if defined (FOO)' sp_defined_paren = ignore # ignore/add/remove/force # Add or remove space between 'throw' and '(' in 'throw (something)' sp_throw_paren = ignore # ignore/add/remove/force # Add or remove space between 'throw' and anything other than '(' as in '@throw [...];' sp_after_throw = ignore # ignore/add/remove/force # Add or remove space between 'catch' and '(' in 'catch (something) { }' # If set to ignore, sp_before_sparen is used. sp_catch_paren = ignore # ignore/add/remove/force # Add or remove space between 'version' and '(' in 'version (something) { }' (D language) # If set to ignore, sp_before_sparen is used. sp_version_paren = ignore # ignore/add/remove/force # Add or remove space between 'scope' and '(' in 'scope (something) { }' (D language) # If set to ignore, sp_before_sparen is used. sp_scope_paren = ignore # ignore/add/remove/force # Add or remove space between macro and value sp_macro = ignore # ignore/add/remove/force # Add or remove space between macro function ')' and value sp_macro_func = ignore # ignore/add/remove/force # Add or remove space between 'else' and '{' if on the same line sp_else_brace = force # ignore/add/remove/force # Add or remove space between '}' and 'else' if on the same line sp_brace_else = force # ignore/add/remove/force # Add or remove space between '}' and the name of a typedef on the same line sp_brace_typedef = ignore # ignore/add/remove/force # Add or remove space between 'catch' and '{' if on the same line sp_catch_brace = ignore # ignore/add/remove/force # Add or remove space between '}' and 'catch' if on the same line sp_brace_catch = ignore # ignore/add/remove/force # Add or remove space between 'finally' and '{' if on the same line sp_finally_brace = ignore # ignore/add/remove/force # Add or remove space between '}' and 'finally' if on the same line sp_brace_finally = ignore # ignore/add/remove/force # Add or remove space between 'try' and '{' if on the same line sp_try_brace = ignore # ignore/add/remove/force # Add or remove space between get/set and '{' if on the same line sp_getset_brace = ignore # ignore/add/remove/force # Add or remove space between a variable and '{' for C++ uniform initialization sp_word_brace = add # ignore/add/remove/force # Add or remove space between a variable and '{' for a namespace sp_word_brace_ns = add # ignore/add/remove/force # Add or remove space before the '::' operator sp_before_dc = ignore # ignore/add/remove/force # Add or remove space after the '::' operator sp_after_dc = ignore # ignore/add/remove/force # Add or remove around the D named array initializer ':' operator sp_d_array_colon = ignore # ignore/add/remove/force # Add or remove space after the '!' (not) operator. Default=Remove sp_not = remove # ignore/add/remove/force # Add or remove space after the '~' (invert) operator. Default=Remove sp_inv = remove # ignore/add/remove/force # Add or remove space after the '&' (address-of) operator. Default=Remove # This does not affect the spacing after a '&' that is part of a type. sp_addr = remove # ignore/add/remove/force # Add or remove space around the '.' or '->' operators. Default=Remove sp_member = remove # ignore/add/remove/force # Add or remove space after the '*' (dereference) operator. Default=Remove # This does not affect the spacing after a '*' that is part of a type. sp_deref = remove # ignore/add/remove/force # Add or remove space after '+' or '-', as in 'x = -5' or 'y = +7'. Default=Remove sp_sign = remove # ignore/add/remove/force # Add or remove space before or after '++' and '--', as in '(--x)' or 'y++;'. Default=Remove sp_incdec = remove # ignore/add/remove/force # Add or remove space before a backslash-newline at the end of a line. Default=Add sp_before_nl_cont = add # ignore/add/remove/force # Add or remove space after the scope '+' or '-', as in '-(void) foo;' or '+(int) bar;' sp_after_oc_scope = ignore # ignore/add/remove/force # Add or remove space after the colon in message specs # '-(int) f:(int) x;' vs '-(int) f: (int) x;' sp_after_oc_colon = ignore # ignore/add/remove/force # Add or remove space before the colon in message specs # '-(int) f: (int) x;' vs '-(int) f : (int) x;' sp_before_oc_colon = ignore # ignore/add/remove/force # Add or remove space after the colon in immutable dictionary expression # 'NSDictionary *test = @{@"foo" :@"bar"};' sp_after_oc_dict_colon = ignore # ignore/add/remove/force # Add or remove space before the colon in immutable dictionary expression # 'NSDictionary *test = @{@"foo" :@"bar"};' sp_before_oc_dict_colon = ignore # ignore/add/remove/force # Add or remove space after the colon in message specs # '[object setValue:1];' vs '[object setValue: 1];' sp_after_send_oc_colon = ignore # ignore/add/remove/force # Add or remove space before the colon in message specs # '[object setValue:1];' vs '[object setValue :1];' sp_before_send_oc_colon = ignore # ignore/add/remove/force # Add or remove space after the (type) in message specs # '-(int)f: (int) x;' vs '-(int)f: (int)x;' sp_after_oc_type = ignore # ignore/add/remove/force # Add or remove space after the first (type) in message specs # '-(int) f:(int)x;' vs '-(int)f:(int)x;' sp_after_oc_return_type = ignore # ignore/add/remove/force # Add or remove space between '@selector' and '(' # '@selector(msgName)' vs '@selector (msgName)' # Also applies to @protocol() constructs sp_after_oc_at_sel = ignore # ignore/add/remove/force # Add or remove space between '@selector(x)' and the following word # '@selector(foo) a:' vs '@selector(foo)a:' sp_after_oc_at_sel_parens = ignore # ignore/add/remove/force # Add or remove space inside '@selector' parens # '@selector(foo)' vs '@selector( foo )' # Also applies to @protocol() constructs sp_inside_oc_at_sel_parens = ignore # ignore/add/remove/force # Add or remove space before a block pointer caret # '^int (int arg){...}' vs. ' ^int (int arg){...}' sp_before_oc_block_caret = ignore # ignore/add/remove/force # Add or remove space after a block pointer caret # '^int (int arg){...}' vs. '^ int (int arg){...}' sp_after_oc_block_caret = ignore # ignore/add/remove/force # Add or remove space between the receiver and selector in a message. # '[receiver selector ...]' sp_after_oc_msg_receiver = ignore # ignore/add/remove/force # Add or remove space after @property. sp_after_oc_property = ignore # ignore/add/remove/force # Add or remove space around the ':' in 'b ? t : f' sp_cond_colon = force # ignore/add/remove/force # Add or remove space around the '?' in 'b ? t : f' sp_cond_question = force # ignore/add/remove/force # Fix the spacing between 'case' and the label. Only 'ignore' and 'force' make sense here. sp_case_label = ignore # ignore/add/remove/force # Control the space around the D '..' operator. sp_range = ignore # ignore/add/remove/force # Control the spacing after ':' in 'for (TYPE VAR : EXPR)' (Java) sp_after_for_colon = ignore # ignore/add/remove/force # Control the spacing before ':' in 'for (TYPE VAR : EXPR)' (Java) sp_before_for_colon = ignore # ignore/add/remove/force # Control the spacing in 'extern (C)' (D) sp_extern_paren = ignore # ignore/add/remove/force # Control the space after the opening of a C++ comment '// A' vs '//A' sp_cmt_cpp_start = force # ignore/add/remove/force # Controls the spaces between #else or #endif and a trailing comment sp_endif_cmt = ignore # ignore/add/remove/force # Controls the spaces after 'new', 'delete', and 'delete[]' sp_after_new = ignore # ignore/add/remove/force # Controls the spaces before a trailing or embedded comment sp_before_tr_emb_cmt = ignore # ignore/add/remove/force # Number of spaces before a trailing or embedded comment sp_num_before_tr_emb_cmt = 0 # number # Control space between a Java annotation and the open paren. sp_annotation_paren = ignore # ignore/add/remove/force # # Code alignment (not left column spaces/tabs) # # Whether to keep non-indenting tabs align_keep_tabs = false # false/true # Whether to use tabs for aligning align_with_tabs = false # false/true # Whether to bump out to the next tab when aligning align_on_tabstop = false # false/true # Whether to left-align numbers align_number_left = false # false/true # Whether to keep whitespace not required for alignment. align_keep_extra_space = false # false/true # Align variable definitions in prototypes and functions align_func_params = false # false/true # Align parameters in single-line functions that have the same name. # The function names must already be aligned with each other. align_same_func_call_params = false # false/true # The span for aligning variable definitions (0=don't align) align_var_def_span = 0 # number # How to align the star in variable definitions. # 0=Part of the type 'void * foo;' # 1=Part of the variable 'void *foo;' # 2=Dangling 'void *foo;' align_var_def_star_style = 0 # number # How to align the '&' in variable definitions. # 0=Part of the type # 1=Part of the variable # 2=Dangling align_var_def_amp_style = 0 # number # The threshold for aligning variable definitions (0=no limit) align_var_def_thresh = 0 # number # The gap for aligning variable definitions align_var_def_gap = 0 # number # Whether to align the colon in struct bit fields align_var_def_colon = false # false/true # Whether to align any attribute after the variable name align_var_def_attribute = false # false/true # Whether to align inline struct/enum/union variable definitions align_var_def_inline = false # false/true # The span for aligning on '=' in assignments (0=don't align) align_assign_span = 0 # number # The threshold for aligning on '=' in assignments (0=no limit) align_assign_thresh = 0 # number # The span for aligning on '=' in enums (0=don't align) align_enum_equ_span = 0 # number # The threshold for aligning on '=' in enums (0=no limit) align_enum_equ_thresh = 0 # number # The span for aligning struct/union (0=don't align) align_var_struct_span = 0 # number # The threshold for aligning struct/union member definitions (0=no limit) align_var_struct_thresh = 0 # number # The gap for aligning struct/union member definitions align_var_struct_gap = 0 # number # The span for aligning struct initializer values (0=don't align) align_struct_init_span = 0 # number # The minimum space between the type and the synonym of a typedef align_typedef_gap = 0 # number # The span for aligning single-line typedefs (0=don't align) align_typedef_span = 0 # number # How to align typedef'd functions with other typedefs # 0: Don't mix them at all # 1: align the open paren with the types # 2: align the function type name with the other type names align_typedef_func = 0 # number # Controls the positioning of the '*' in typedefs. Just try it. # 0: Align on typedef type, ignore '*' # 1: The '*' is part of type name: typedef int *pint; # 2: The '*' is part of the type, but dangling: typedef int *pint; align_typedef_star_style = 0 # number # Controls the positioning of the '&' in typedefs. Just try it. # 0: Align on typedef type, ignore '&' # 1: The '&' is part of type name: typedef int &pint; # 2: The '&' is part of the type, but dangling: typedef int &pint; align_typedef_amp_style = 0 # number # The span for aligning comments that end lines (0=don't align) align_right_cmt_span = 0 # number # If aligning comments, mix with comments after '}' and #endif with less than 3 spaces before the comment align_right_cmt_mix = false # false/true # If a trailing comment is more than this number of columns away from the text it follows, # it will qualify for being aligned. This has to be > 0 to do anything. align_right_cmt_gap = 0 # number # Align trailing comment at or beyond column N; 'pulls in' comments as a bonus side effect (0=ignore) align_right_cmt_at_col = 0 # number # The span for aligning function prototypes (0=don't align) align_func_proto_span = 0 # number # Minimum gap between the return type and the function name. align_func_proto_gap = 0 # number # Align function protos on the 'operator' keyword instead of what follows align_on_operator = false # false/true # Whether to mix aligning prototype and variable declarations. # If true, align_var_def_XXX options are used instead of align_func_proto_XXX options. align_mix_var_proto = false # false/true # Align single-line functions with function prototypes, uses align_func_proto_span align_single_line_func = false # false/true # Aligning the open brace of single-line functions. # Requires align_single_line_func=true, uses align_func_proto_span align_single_line_brace = false # false/true # Gap for align_single_line_brace. align_single_line_brace_gap = 0 # number # The span for aligning ObjC msg spec (0=don't align) align_oc_msg_spec_span = 0 # number # Whether to align macros wrapped with a backslash and a newline. # This will not work right if the macro contains a multi-line comment. align_nl_cont = false # false/true # # Align macro functions and variables together align_pp_define_together = false # false/true # The minimum space between label and value of a preprocessor define align_pp_define_gap = 0 # number # The span for aligning on '#define' bodies (0=don't align) align_pp_define_span = 0 # number # Align lines that start with '<<' with previous '<<'. Default=true align_left_shift = true # false/true # Span for aligning parameters in an Obj-C message call on the ':' (0=don't align) align_oc_msg_colon_span = 0 # number # If true, always align with the first parameter, even if it is too short. align_oc_msg_colon_first = false # false/true # Aligning parameters in an Obj-C '+' or '-' declaration on the ':' align_oc_decl_colon = false # false/true # # Newline adding and removing options # # Whether to collapse empty blocks between '{' and '}' nl_collapse_empty_body = false # false/true # Don't split one-line braced assignments - 'foo_t f = { 1, 2 };' nl_assign_leave_one_liners = true # false/true # Don't split one-line braced statements inside a class xx { } body nl_class_leave_one_liners = false # false/true # Don't split one-line enums: 'enum foo { BAR = 15 };' nl_enum_leave_one_liners = false # false/true # Don't split one-line get or set functions nl_getset_leave_one_liners = false # false/true # Don't split one-line function definitions - 'int foo() { return 0; }' nl_func_leave_one_liners = true # false/true # Don't split one-line C++11 lambdas - '[]() { return 0; }' nl_cpp_lambda_leave_one_liners = false # false/true # Don't split one-line if/else statements - 'if(a) b++;' nl_if_leave_one_liners = false # false/true # Don't split one-line OC messages nl_oc_msg_leave_one_liner = false # false/true # Add or remove newlines at the start of the file nl_start_of_file = remove # ignore/add/remove/force # The number of newlines at the start of the file (only used if nl_start_of_file is 'add' or 'force' nl_start_of_file_min = 0 # number # Add or remove newline at the end of the file nl_end_of_file = force # ignore/add/remove/force # The number of newlines at the end of the file (only used if nl_end_of_file is 'add' or 'force') nl_end_of_file_min = 1 # number # Add or remove newline between '=' and '{' nl_assign_brace = remove # ignore/add/remove/force # Add or remove newline between '=' and '[' (D only) nl_assign_square = ignore # ignore/add/remove/force # Add or remove newline after '= [' (D only). Will also affect the newline before the ']' nl_after_square_assign = ignore # ignore/add/remove/force # The number of blank lines after a block of variable definitions at the top of a function body # 0 = No change (default) nl_func_var_def_blk = 0 # number # The number of newlines before a block of typedefs # 0 = No change (default) nl_typedef_blk_start = 0 # number # The number of newlines after a block of typedefs # 0 = No change (default) nl_typedef_blk_end = 0 # number # The maximum consecutive newlines within a block of typedefs # 0 = No change (default) nl_typedef_blk_in = 0 # number # The number of newlines before a block of variable definitions not at the top of a function body # 0 = No change (default) nl_var_def_blk_start = 0 # number # The number of newlines after a block of variable definitions not at the top of a function body # 0 = No change (default) nl_var_def_blk_end = 0 # number # The maximum consecutive newlines within a block of variable definitions # 0 = No change (default) nl_var_def_blk_in = 0 # number # Add or remove newline between a function call's ')' and '{', as in: # list_for_each(item, &list) { } nl_fcall_brace = ignore # ignore/add/remove/force # Add or remove newline between 'enum' and '{' nl_enum_brace = ignore # ignore/add/remove/force # Add or remove newline between 'struct and '{' nl_struct_brace = ignore # ignore/add/remove/force # Add or remove newline between 'union' and '{' nl_union_brace = ignore # ignore/add/remove/force # Add or remove newline between 'if' and '{' nl_if_brace = remove # ignore/add/remove/force # Add or remove newline between '}' and 'else' nl_brace_else = remove # ignore/add/remove/force # Add or remove newline between 'else if' and '{' # If set to ignore, nl_if_brace is used instead nl_elseif_brace = remove # ignore/add/remove/force # Add or remove newline between 'else' and '{' nl_else_brace = remove # ignore/add/remove/force # Add or remove newline between 'else' and 'if' nl_else_if = remove # ignore/add/remove/force # Add or remove newline between '}' and 'finally' nl_brace_finally = ignore # ignore/add/remove/force # Add or remove newline between 'finally' and '{' nl_finally_brace = ignore # ignore/add/remove/force # Add or remove newline between 'try' and '{' nl_try_brace = ignore # ignore/add/remove/force # Add or remove newline between get/set and '{' nl_getset_brace = ignore # ignore/add/remove/force # Add or remove newline between 'for' and '{' nl_for_brace = remove # ignore/add/remove/force # Add or remove newline between 'catch' and '{' nl_catch_brace = ignore # ignore/add/remove/force # Add or remove newline between '}' and 'catch' nl_brace_catch = ignore # ignore/add/remove/force # Add or remove newline between 'while' and '{' nl_while_brace = ignore # ignore/add/remove/force # Add or remove newline between 'scope (x)' and '{' (D) nl_scope_brace = ignore # ignore/add/remove/force # Add or remove newline between 'unittest' and '{' (D) nl_unittest_brace = ignore # ignore/add/remove/force # Add or remove newline between 'version (x)' and '{' (D) nl_version_brace = ignore # ignore/add/remove/force # Add or remove newline between 'using' and '{' nl_using_brace = ignore # ignore/add/remove/force # Add or remove newline between two open or close braces. # Due to general newline/brace handling, REMOVE may not work. nl_brace_brace = ignore # ignore/add/remove/force # Add or remove newline between 'do' and '{' nl_do_brace = remove # ignore/add/remove/force # Add or remove newline between '}' and 'while' of 'do' statement nl_brace_while = remove # ignore/add/remove/force # Add or remove newline between 'switch' and '{' nl_switch_brace = remove # ignore/add/remove/force # Add a newline between ')' and '{' if the ')' is on a different line than the if/for/etc. # Overrides nl_for_brace, nl_if_brace, nl_switch_brace, nl_while_switch, and nl_catch_brace. nl_multi_line_cond = false # false/true # Force a newline in a define after the macro name for multi-line defines. nl_multi_line_define = false # false/true # Whether to put a newline before 'case' statement nl_before_case = false # false/true # Add or remove newline between ')' and 'throw' nl_before_throw = ignore # ignore/add/remove/force # Whether to put a newline after 'case' statement nl_after_case = true # false/true # Add or remove a newline between a case ':' and '{'. Overrides nl_after_case. nl_case_colon_brace = ignore # ignore/add/remove/force # Newline between namespace and { nl_namespace_brace = remove # ignore/add/remove/force # Add or remove newline between 'template<>' and whatever follows. nl_template_class = ignore # ignore/add/remove/force # Add or remove newline between 'class' and '{' nl_class_brace = force # ignore/add/remove/force # Add or remove newline after each ',' in the class base list nl_class_init_args = remove # ignore/add/remove/force # Add or remove newline after each ',' in the constructor member initialization nl_constr_init_args = force # ignore/add/remove/force # Add or remove newline between return type and function name in a function definition nl_func_type_name = ignore # ignore/add/remove/force # Add or remove newline between return type and function name inside a class {} # Uses nl_func_type_name or nl_func_proto_type_name if set to ignore. nl_func_type_name_class = ignore # ignore/add/remove/force # Add or remove newline between function scope and name in a definition # Controls the newline after '::' in 'void A::f() { }' nl_func_scope_name = ignore # ignore/add/remove/force # Add or remove newline between return type and function name in a prototype nl_func_proto_type_name = ignore # ignore/add/remove/force # Add or remove newline between a function name and the opening '(' nl_func_paren = remove # ignore/add/remove/force # Add or remove newline between a function name and the opening '(' in the definition nl_func_def_paren = ignore # ignore/add/remove/force # Add or remove newline after '(' in a function declaration nl_func_decl_start = ignore # ignore/add/remove/force # Add or remove newline after '(' in a function definition nl_func_def_start = ignore # ignore/add/remove/force # Overrides nl_func_decl_start when there is only one parameter. nl_func_decl_start_single = ignore # ignore/add/remove/force # Overrides nl_func_def_start when there is only one parameter. nl_func_def_start_single = ignore # ignore/add/remove/force # Add or remove newline after each ',' in a function declaration nl_func_decl_args = remove # ignore/add/remove/force # Add or remove newline after each ',' in a function definition nl_func_def_args = remove # ignore/add/remove/force # Add or remove newline before the ')' in a function declaration nl_func_decl_end = remove # ignore/add/remove/force # Add or remove newline before the ')' in a function definition nl_func_def_end = remove # ignore/add/remove/force # Overrides nl_func_decl_end when there is only one parameter. nl_func_decl_end_single = ignore # ignore/add/remove/force # Overrides nl_func_def_end when there is only one parameter. nl_func_def_end_single = ignore # ignore/add/remove/force # Add or remove newline between '()' in a function declaration. nl_func_decl_empty = remove # ignore/add/remove/force # Add or remove newline between '()' in a function definition. nl_func_def_empty = remove # ignore/add/remove/force # Whether to put each OC message parameter on a separate line # See nl_oc_msg_leave_one_liner nl_oc_msg_args = false # false/true # Add or remove newline between function signature and '{' nl_fdef_brace = force # ignore/add/remove/force # Add or remove newline between C++11 lambda signature and '{' nl_cpp_ldef_brace = ignore # ignore/add/remove/force # Add or remove a newline between the return keyword and return expression. nl_return_expr = ignore # ignore/add/remove/force # Whether to put a newline after semicolons, except in 'for' statements nl_after_semicolon = true # false/true # Whether to put a newline after brace open. # This also adds a newline before the matching brace close. nl_after_brace_open = true # false/true # If nl_after_brace_open and nl_after_brace_open_cmt are true, a newline is # placed between the open brace and a trailing single-line comment. nl_after_brace_open_cmt = false # false/true # Whether to put a newline after a virtual brace open with a non-empty body. # These occur in un-braced if/while/do/for statement bodies. nl_after_vbrace_open = false # false/true # Whether to put a newline after a virtual brace open with an empty body. # These occur in un-braced if/while/do/for statement bodies. nl_after_vbrace_open_empty = false # false/true # Whether to put a newline after a brace close. # Does not apply if followed by a necessary ';'. nl_after_brace_close = false # false/true # Whether to put a newline after a virtual brace close. # Would add a newline before return in: 'if (foo) a++; return;' nl_after_vbrace_close = false # false/true # Control the newline between the close brace and 'b' in: 'struct { int a; } b;' # Affects enums, unions, and structures. If set to ignore, uses nl_after_brace_close nl_brace_struct_var = ignore # ignore/add/remove/force # Whether to alter newlines in '#define' macros nl_define_macro = false # false/true # Whether to not put blanks after '#ifxx', '#elxx', or before '#endif' nl_squeeze_ifdef = false # false/true # Add or remove blank line before 'if' nl_before_if = ignore # ignore/add/remove/force # Add or remove blank line after 'if' statement nl_after_if = ignore # ignore/add/remove/force # Add or remove blank line before 'for' nl_before_for = ignore # ignore/add/remove/force # Add or remove blank line after 'for' statement nl_after_for = ignore # ignore/add/remove/force # Add or remove blank line before 'while' nl_before_while = ignore # ignore/add/remove/force # Add or remove blank line after 'while' statement nl_after_while = ignore # ignore/add/remove/force # Add or remove blank line before 'switch' nl_before_switch = ignore # ignore/add/remove/force # Add or remove blank line after 'switch' statement nl_after_switch = ignore # ignore/add/remove/force # Add or remove blank line before 'do' nl_before_do = ignore # ignore/add/remove/force # Add or remove blank line after 'do/while' statement nl_after_do = ignore # ignore/add/remove/force # Whether to double-space commented-entries in struct/enum nl_ds_struct_enum_cmt = false # false/true # Whether to double-space before the close brace of a struct/union/enum # (lower priority than 'eat_blanks_before_close_brace') nl_ds_struct_enum_close_brace = false # false/true # Add or remove a newline around a class colon. # Related to pos_class_colon, nl_class_init_args, and pos_comma. nl_class_colon = remove # ignore/add/remove/force # Change simple unbraced if statements into a one-liner # 'if(b)\n i++;' => 'if(b) i++;' nl_create_if_one_liner = false # false/true # Change simple unbraced for statements into a one-liner # 'for (i=0;i<5;i++)\n foo(i);' => 'for (i=0;i<5;i++) foo(i);' nl_create_for_one_liner = false # false/true # Change simple unbraced while statements into a one-liner # 'while (i<5)\n foo(i++);' => 'while (i<5) foo(i++);' nl_create_while_one_liner = false # false/true # # Positioning options # # The position of arithmetic operators in wrapped expressions pos_arith = lead # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force # The position of assignment in wrapped expressions. # Do not affect '=' followed by '{' pos_assign = lead # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force # The position of boolean operators in wrapped expressions pos_bool = lead # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force # The position of comparison operators in wrapped expressions pos_compare = lead # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force # The position of conditional (b ? t : f) operators in wrapped expressions pos_conditional = lead # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force # The position of the comma in wrapped expressions pos_comma = trail # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force # The position of the comma in the constructor initialization list pos_class_comma = trail # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force # The position of colons between constructor and member initialization pos_class_colon = lead # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force pos_constr_comma = lead pos_constr_colon = lead # # Line Splitting options # # Try to limit code width to N number of columns code_width = 100 # number # Whether to fully split long 'for' statements at semi-colons ls_for_split_full = false # false/true # Whether to fully split long function protos/calls at commas ls_func_split_full = false # false/true # Whether to split lines as close to code_width as possible and ignore some groupings ls_code_width = false # false/true # # Blank line options # # The maximum consecutive newlines nl_max = 2 # number # The number of newlines after a function prototype, if followed by another function prototype nl_after_func_proto = 0 # number # The number of newlines after a function prototype, if not followed by another function prototype nl_after_func_proto_group = 0 # number # The number of newlines after '}' of a multi-line function body nl_after_func_body = 2 # number # The number of newlines after '}' of a multi-line function body in a class declaration nl_after_func_body_class = 0 # number # The number of newlines after '}' of a single line function body nl_after_func_body_one_liner = 0 # number # The minimum number of newlines before a multi-line comment. # Doesn't apply if after a brace open or another multi-line comment. nl_before_block_comment = 0 # number # The minimum number of newlines before a single-line C comment. # Doesn't apply if after a brace open or other single-line C comments. nl_before_c_comment = 0 # number # The minimum number of newlines before a CPP comment. # Doesn't apply if after a brace open or other CPP comments. nl_before_cpp_comment = 0 # number # Whether to force a newline after a multi-line comment. nl_after_multiline_comment = false # false/true # The number of newlines after '}' or ';' of a struct/enum/union definition nl_after_struct = 0 # number # The number of newlines after '}' or ';' of a class definition nl_after_class = 0 # number # The number of newlines before a 'private:', 'public:', 'protected:', 'signals:', or 'slots:' label. # Will not change the newline count if after a brace open. # 0 = No change. nl_before_access_spec = 0 # number # The number of newlines after a 'private:', 'public:', 'protected:', 'signals:', or 'slots:' label. # 0 = No change. nl_after_access_spec = 0 # number # The number of newlines between a function def and the function comment. # 0 = No change. nl_comment_func_def = 0 # number # The number of newlines after a try-catch-finally block that isn't followed by a brace close. # 0 = No change. nl_after_try_catch_finally = 0 # number # The number of newlines before and after a property, indexer or event decl. # 0 = No change. nl_around_cs_property = 0 # number # The number of newlines between the get/set/add/remove handlers in C#. # 0 = No change. nl_between_get_set = 0 # number # Add or remove newline between C# property and the '{' nl_property_brace = ignore # ignore/add/remove/force # Whether to remove blank lines after '{' eat_blanks_after_open_brace = true # false/true # Whether to remove blank lines before '}' eat_blanks_before_close_brace = true # false/true # How aggressively to remove extra newlines not in preproc. # 0: No change # 1: Remove most newlines not handled by other config # 2: Remove all newlines and reformat completely by config nl_remove_extra_newlines = 0 # number # Whether to put a blank line before 'return' statements, unless after an open brace. nl_before_return = false # false/true # Whether to put a blank line after 'return' statements, unless followed by a close brace. nl_after_return = false # false/true # Whether to put a newline after a Java annotation statement. # Only affects annotations that are after a newline. nl_after_annotation = ignore # ignore/add/remove/force # Controls the newline between two annotations. nl_between_annotation = ignore # ignore/add/remove/force nl_constr_colon = add # # Code modifying options (non-whitespace) # # Add or remove braces on single-line 'do' statement mod_full_brace_do = ignore # ignore/add/remove/force # Add or remove braces on single-line 'for' statement mod_full_brace_for = remove # ignore/add/remove/force # Add or remove braces on single-line function definitions. (Pawn) mod_full_brace_function = ignore # ignore/add/remove/force # Add or remove braces on single-line 'if' statement. Will not remove the braces if they contain an 'else'. mod_full_brace_if = remove # ignore/add/remove/force # Make all if/elseif/else statements in a chain be braced or not. Overrides mod_full_brace_if. # If any must be braced, they are all braced. If all can be unbraced, then the braces are removed. mod_full_brace_if_chain = true # false/true # Don't remove braces around statements that span N newlines mod_full_brace_nl = 2 # number # Add or remove braces on single-line 'while' statement mod_full_brace_while = remove # ignore/add/remove/force # Add or remove braces on single-line 'using ()' statement mod_full_brace_using = ignore # ignore/add/remove/force # Add or remove unnecessary paren on 'return' statement mod_paren_on_return = remove # ignore/add/remove/force # Whether to change optional semicolons to real semicolons mod_pawn_semicolon = false # false/true # Add parens on 'while' and 'if' statement around bools mod_full_paren_if_bool = false # false/true # Whether to remove superfluous semicolons mod_remove_extra_semicolon = true # false/true # If a function body exceeds the specified number of newlines and doesn't have a comment after # the close brace, a comment will be added. mod_add_long_function_closebrace_comment = 0 # number # If a switch body exceeds the specified number of newlines and doesn't have a comment after # the close brace, a comment will be added. mod_add_long_switch_closebrace_comment = 0 # number # If an #ifdef body exceeds the specified number of newlines and doesn't have a comment after # the #endif, a comment will be added. mod_add_long_ifdef_endif_comment = 0 # number # If an #ifdef or #else body exceeds the specified number of newlines and doesn't have a comment after # the #else, a comment will be added. mod_add_long_ifdef_else_comment = 0 # number # If TRUE, will sort consecutive single-line 'import' statements [Java, D] mod_sort_import = false # false/true # If TRUE, will sort consecutive single-line 'using' statements [C#] mod_sort_using = false # false/true # If TRUE, will sort consecutive single-line '#include' statements [C/C++] and '#import' statements [Obj-C] # This is generally a bad idea, as it may break your code. mod_sort_include = false # false/true # If TRUE, it will move a 'break' that appears after a fully braced 'case' before the close brace. mod_move_case_break = true # false/true # Will add or remove the braces around a fully braced case statement. # Will only remove the braces if there are no variable declarations in the block. mod_case_brace = remove # remove/add/remove/force # If TRUE, it will remove a void 'return;' that appears as the last statement in a function. mod_remove_empty_return = true # false/true # # Comment modifications # # Try to wrap comments at cmt_width columns cmt_width = 0 # number # Set the comment reflow mode (default: 0) # 0: no reflowing (apart from the line wrapping due to cmt_width) # 1: no touching at all # 2: full reflow cmt_reflow_mode = 0 # number # If false, disable all multi-line comment changes, including cmt_width. keyword substitution, and leading chars. # Default is true. cmt_indent_multi = false # false/true # Whether to group c-comments that look like they are in a block cmt_c_group = false # false/true # Whether to put an empty '/*' on the first line of the combined c-comment cmt_c_nl_start = false # false/true # Whether to put a newline before the closing '*/' of the combined c-comment cmt_c_nl_end = false # false/true # Whether to group cpp-comments that look like they are in a block cmt_cpp_group = false # false/true # Whether to put an empty '/*' on the first line of the combined cpp-comment cmt_cpp_nl_start = false # false/true # Whether to put a newline before the closing '*/' of the combined cpp-comment cmt_cpp_nl_end = false # false/true # Whether to change cpp-comments into c-comments cmt_cpp_to_c = false # false/true # Whether to put a star on subsequent comment lines cmt_star_cont = false # false/true # The number of spaces to insert at the start of subsequent comment lines cmt_sp_before_star_cont = 0 # number # The number of spaces to insert after the star on subsequent comment lines cmt_sp_after_star_cont = 0 # number # For multi-line comments with a '*' lead, remove leading spaces if the first and last lines of # the comment are the same length. Default=True cmt_multi_check_last = true # false/true # The filename that contains text to insert at the head of a file if the file doesn't start with a C/C++ comment. # Will substitute $(filename) with the current file's name. cmt_insert_file_header = "" # string # The filename that contains text to insert at the end of a file if the file doesn't end with a C/C++ comment. # Will substitute $(filename) with the current file's name. cmt_insert_file_footer = "" # string # The filename that contains text to insert before a function implementation if the function isn't preceded with a C/C++ comment. # Will substitute $(function) with the function name and $(javaparam) with the javadoc @param and @return stuff. # Will also substitute $(fclass) with the class name: void CFoo::Bar() { ... } cmt_insert_func_header = "" # string # The filename that contains text to insert before a class if the class isn't preceded with a C/C++ comment. # Will substitute $(class) with the class name. cmt_insert_class_header = "" # string # The filename that contains text to insert before a Obj-C message specification if the method isn't preceeded with a C/C++ comment. # Will substitute $(message) with the function name and $(javaparam) with the javadoc @param and @return stuff. cmt_insert_oc_msg_header = "" # string # If a preprocessor is encountered when stepping backwards from a function name, then # this option decides whether the comment should be inserted. # Affects cmt_insert_oc_msg_header, cmt_insert_func_header and cmt_insert_class_header. cmt_insert_before_preproc = false # false/true # # Preprocessor options # # Control indent of preprocessors inside #if blocks at brace level 0 pp_indent = ignore # ignore/add/remove/force # Whether to indent #if/#else/#endif at the brace level (true) or from column 1 (false) pp_indent_at_level = false # false/true # If pp_indent_at_level=false, specifies the number of columns to indent per level. Default=1. pp_indent_count = 1 # number # Add or remove space after # based on pp_level of #if blocks pp_space = ignore # ignore/add/remove/force # Sets the number of spaces added with pp_space pp_space_count = 0 # number # The indent for #region and #endregion in C# and '#pragma region' in C/C++ pp_indent_region = 0 # number # Whether to indent the code between #region and #endregion pp_region_indent_code = false # false/true # If pp_indent_at_level=true, sets the indent for #if, #else, and #endif when not at file-level pp_indent_if = 0 # number # Control whether to indent the code between #if, #else and #endif when not at file-level pp_if_indent_code = false # false/true # Whether to indent '#define' at the brace level (true) or from column 1 (false) pp_define_at_level = false # false/true # You can force a token to be a type with the 'type' option. # Example: # type myfoo1 myfoo2 # # You can create custom macro-based indentation using macro-open, # macro-else and macro-close. # Example: # macro-open BEGIN_TEMPLATE_MESSAGE_MAP # macro-open BEGIN_MESSAGE_MAP # macro-close END_MESSAGE_MAP # # You can assign any keyword to any type with the set option. # set func_call_user _ N_ # # The full syntax description of all custom definition config entries # is shown below: # # define custom tokens as: # - embed whitespace in token using '' escape character, or # put token in quotes # - these: ' " and ` are recognized as quote delimiters # # type token1 token2 token3 ... # ^ optionally specify multiple tokens on a single line # define def_token output_token # ^ output_token is optional, then NULL is assumed # macro-open token # macro-close token # macro-else token # set id token1 token2 ... # ^ optionally specify multiple tokens on a single line # ^ id is one of the names in token_enum.h sans the CT_ prefix, # e.g. PP_PRAGMA # # all tokens are separated by any mix of ',' commas, '=' equal signs # and whitespace (space, tab) # gammaray-2.11.3/format_sources000066400000000000000000000003111413310443000163360ustar00rootroot00000000000000# Ignore copied-in code, by matching it with an empty formatting command 3rdparty/* : # Default: 4-space indentation * : uncrustify -l CPP -f $TMPFILE -c format.config.uncrustify.4_spaces -o $TMPFILE gammaray-2.11.3/gammaray-qt-ci-only.pro000066400000000000000000000026331413310443000177030ustar00rootroot00000000000000# qmake -> cmake wrapper for the Qt CI system # # DO NOT USE THIS UNLESS YOU KNOW WHAT YOU ARE DOING! # # This only supports a tiny subset of the CMake build system, # hardcoded for the needs of integration with the Qt CI system. # # If you are building GammaRay yourself, please use CMake as # described in Install. txt requires(linux:!android|win32:!winphone:!winrt|osx) requires(!integrity) requires(!cross_compile) win32: requires(!contains(QMAKE_HOST.version_string, $$re_escape(8.1))) message("QMake wrapper for the GammaRay build system is only supported for the Qt CI.") message("If you are seeing this and you are not the Qt CI, please refer to Install.txt!") # additional indirection is needed as the above requires conditions don't work correctly # in aux templates, on some platforms TEMPLATE = subdirs SUBDIRS += qt-ci-wrapper # forward non-built-in targets too QMAKE_EXTRA_TARGETS += gammaray_build_docs gammaray_build_qch_docs gammaray_build_docs.CONFIG = recursive gammaray_build_docs.recurse_target = docs gammaray_build_docs.target = docs gammaray_build_qch_docs.target = qch_docs gammaray_build_qch_docs.depends = docs # add dummy qmake targets for doc build - only qch_docs triggers the build DOC_TARGETS = \ prepare_docs generate_docs html_docs \ install_html_docs uninstall_html_docs \ install_qch_docs uninstall_qch_docs \ install_docs uninstall_docs QMAKE_EXTRA_TARGETS += $$DOC_TARGETS gammaray-2.11.3/gammaray.dsc000066400000000000000000000007321413310443000156600ustar00rootroot00000000000000Format: 1.0 Source: gammaray Version: 2.11.3 Binary: gammaray Maintainer: Allen Winter Architecture: any Build-Depends: debhelper (>=9), cdbs, cmake, qtbase5-dev, qtbase5-private-dev, qtdeclarative5-dev, qtdeclarative5-private-dev, qtdeclarative5-dev-tools, qtscript5-dev, qttools5-dev, qttools5-dev-tools, libqt5svg5-dev, libqt5webkit5-dev, libwayland-dev, kdstatemachineeditor Files: 00000000000000000000000000000000 00000 gammaray-2.11.3.tar.gz gammaray-2.11.3/gammaray.spec000066400000000000000000000213171413310443000160430ustar00rootroot00000000000000Name: gammaray Version: 2.11.3 Release: 1 Summary: An introspection tool for Qt applications Source0: %{name}-%{version}.tar.gz Source1: %{name}-%{version}.tar.gz.asc Url: https://github.com/KDAB/GammaRay Group: Development/Tools License: GPL-2.0-or-later BuildRoot: %{_tmppath}/%{name}-%{version}-build Vendor: Klaralvdalens Datakonsult AB (KDAB) Packager: Klaralvdalens Datakonsult AB (KDAB) BuildRequires: cmake kdstatemachineeditor-devel %if %{defined suse_version} BuildRequires: libqt5-qtbase-private-headers-devel libqt5-qtdeclarative-private-headers-devel libQt5Concurrent-devel libqt5-qttools-devel libqt5-qtsvg-devel libQt5PrintSupport-devel libqt5-qtscript-devel libQt5WebKitWidgets-devel libqt5-qtconnectivity-devel libqt5-qt3d-devel wayland-devel libqt5-qtwayland-devel bluez-qt-devel # TODO: this seems only to be in the update repo? BuildRequires: kcoreaddons-devel %endif %if %{defined fedora} BuildRequires: qt5-qtdeclarative-devel qt5-qtconnectivity-devel qt5-qttools-devel qt5-qtsvg-devel qt5-qtscript-devel qt5-qtwebkit-devel %if 0%{?fedora} >= 21 BuildRequires: kf5-kcoreaddons-devel %endif %if 0%{?fedora} > 23 BuildRequires: ghostscript %endif %if 0%{?fedora} > 24 BuildRequires: qt5-qt3d-devel wayland-devel qt5-qtwayland-devel kf5-syndication-devel %endif %if 0%{?fedora} > 30 BuildRequires: qt5-qtbase-private-devel %endif %endif %if %{defined suse_version} BuildRequires: update-desktop-files %endif %if %{defined fedora} BuildRequires: desktop-file-utils %endif %description GammaRay is a tool for examining the internals of a Qt application and to some extent also manipulate it. GammaRay uses injection methods to hook into an application at runtime and provide access to a wide variety of interesting information. It provides easy ways of navigating through the complex internal structures you find in some Qt frameworks, such as QGraphicsView, model/view, QTextDocument, state machines and more. Authors: -------- Klaralvdalens Datakonsult AB (KDAB) %define debug_package %{nil} %global __debug_install_post %{nil} %package kf5-plugins Summary: GammaRay plug-ins to introspect KF5 applications Group: Development/Tools Requires: %{name} = %{version} %description kf5-plugins Plug-ins for the GammaRay introspection tool to debug KF5 applications, such as a KJob tracker. %package devel Summary: Development files for %{name} Group: Development/Libraries Requires: %{name} = %{version} %description devel The %{name}-devel package contains libraries and header files for developing GammaRay plug-ins. %prep %autosetup %build cmake . -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Release -DKDE_INSTALL_USE_QT_SYS_PATHS=ON %__make %{?_smp_mflags} %post -p /sbin/ldconfig %postun -p /sbin/ldconfig %install %make_install %if %{defined suse_version} %suse_update_desktop_file GammaRay Development Qt Debugger %endif %clean %__rm -rf "%{buildroot}" %files %defattr(-,root,root) %{_prefix}/share/applications/GammaRay.desktop %dir %{_prefix}/share/metainfo/ %{_prefix}/share/metainfo/com.kdab.GammaRay.metainfo.xml %{_prefix}/share/icons/hicolor %{_prefix}/share/doc/gammaray %dir %{_prefix}/share/gammaray/ %dir %{_prefix}/share/gammaray/translations/ %{_prefix}/share/gammaray/translations/gammaray_*.qm %{_mandir}/man1/gammaray.1.gz %{_bindir}/gammaray %{_libdir}/gammaray/libexec/gammaray-client %{_libdir}/gammaray/libexec/gammaray-launcher %{_libdir}/libgammaray_common-*.so.* %{_libdir}/libgammaray_core-*.so.* %{_libdir}/libgammaray_ui-*.so.* %{_libdir}/libgammaray_client.so.* %{_libdir}/libgammaray_kitemmodels* %{_libdir}/libgammaray_kuserfeedback* %{_libdir}/libgammaray_launcher* %dir %{_libdir}/gammaray/ %dir %{_libdir}/gammaray/*/ %dir %{_libdir}/gammaray/*/*/ %{_libdir}/gammaray/*/*/libgammaray_widget_export_actions.so %{_libdir}/gammaray/*/*/gammaray_probe.so %{_libdir}/gammaray/*/*/gammaray_inprocessui.so %{_libdir}/gammaray/*/*/gammaray_actioninspector* %{_libdir}/gammaray/*/*/gammaray_bluetooth* %{_libdir}/gammaray/*/*/gammaray_codecbrowser* %{_libdir}/gammaray/*/*/gammaray_eventmonitor* %{_libdir}/gammaray/*/*/gammaray_fontbrowser* %{_libdir}/gammaray/*/*/gammaray_guisupport* %{_libdir}/gammaray/*/*/gammaray_localeinspector* %{_libdir}/gammaray/*/*/gammaray_mimetypes* %{_libdir}/gammaray/*/*/gammaray_modelinspector* %{_libdir}/gammaray/*/*/gammaray_network* %{_libdir}/gammaray/*/*/gammaray_objectvisualizer* %{_libdir}/gammaray/*/*/gammaray_qmlsupport* %{_libdir}/gammaray/*/*/gammaray_qtivi_ui* %{_libdir}/gammaray/*/*/gammaray_quickinspector* %{_libdir}/gammaray/*/*/gammaray_quickwidgetsupport* %{_libdir}/gammaray/*/*/gammaray_sceneinspector* %{_libdir}/gammaray/*/*/gammaray_scriptenginedebugger* %{_libdir}/gammaray/*/*/gammaray_signalmonitor* %{_libdir}/gammaray/*/*/gammaray_statemachineviewer* %{_libdir}/gammaray/*/*/gammaray_styleinspector* %{_libdir}/gammaray/*/*/gammaray_sysinfo* %{_libdir}/gammaray/*/*/gammaray_textdocumentinspector* %{_libdir}/gammaray/*/*/gammaray_timertop* %{_libdir}/gammaray/*/*/gammaray_translatorinspector* %{_libdir}/gammaray/*/*/gammaray_webinspector* %{_libdir}/gammaray/*/*/gammaray_widgetinspector* %{_libdir}/gammaray/*/*/gammaray_wlcompositorinspector* %{_libdir}/gammaray/*/*/styles/ %if 0%{?fedora} > 24 %{_libdir}/gammaray/*/*/gammaray_3dinspector* %endif %if 0%{?suse_version} > 1320 %{_libdir}/gammaray/*/*/gammaray_3dinspector* %endif %files kf5-plugins %defattr(-,root,root) %{_libdir}/gammaray/*/*/gammaray_kjobtracker* %files devel %defattr(-,root,root) %{_includedir}/gammaray %{_libdir}/libgammaray_common-*.so %{_libdir}/libgammaray_core-*.so %{_libdir}/libgammaray_ui-*.so %{_libdir}/libgammaray_client.so %{_libdir}/cmake/GammaRay/ %dir %{_prefix}/mkspecs/ %dir %{_prefix}/mkspecs/modules/ %{_prefix}/mkspecs/modules/*.pri %changelog * Fri Oct 15 2021 Allen Winter 2.11.3 2.11.3 patchlevel release * Fri Sep 18 2020 Allen Winter 2.11.2 2.11.2 patchlevel release * Tue Mar 03 2020 Allen Winter 2.11.1 2.11.1 patchlevel release * Wed Jul 03 2019 Allen Winter 2.11.0 2.11.0 final * Sun Dec 16 2018 Allen Winter 2.10.0 2.10.0 final * Wed Aug 01 2018 Allen Winter 2.9.1 2.9.1 final * Wed Feb 07 2018 Allen Winter 2.9.0 2.9.0 final * Tue Sep 05 2017 Allen Winter 2.8.1 2.8.1 bugfix release * Wed Jun 07 2017 Allen Winter 2.8.0 2.8.0 final * Tue Feb 14 2017 Allen Winter 2.7.0 2.7.0 final * Thu Sep 29 2016 Allen Winter 2.6.0 2.6.0 final * Wed Jul 27 2016 Allen Winter 2.5.1 2.5.1 bugfix release * Fri Jul 08 2016 Allen Winter 2.5.0 2.5.0 final * Fri Mar 11 2016 Allen Winter 2.4.1 2.4.1 final * Tue Dec 22 2015 Allen Winter 2.4.0 2.4.0 final * Sun Dec 20 2015 Allen Winter 2.3.99 2.4.0 release candidate 1 * Tue Jan 27 2015 Allen Winter 2.2.1 2.2.1 final * Fri Oct 31 2014 Allen Winter 2.2.0 2.2.0 final * Thu Oct 23 2014 Allen Winter 2.1.2 2.1.2 final * Sat Aug 30 2014 Allen Winter 2.1.1 2.1.1 final * Fri Jun 27 2014 Allen Winter 2.1.0 2.1.0 final * Thu Jun 26 2014 Allen Winter 2.0.98 2.1.0 release candidate 1 * Fri Apr 18 2014 Allen Winter 2.0.2 2.0.2 final * Fri Feb 28 2014 Allen Winter 2.0.1 2.0.1 final * Fri Jan 17 2014 Allen Winter 2.0.0 2.0.0 final * Sat Jan 11 2014 Allen Winter 1.9.96 Second 2.0 beta release * Fri Dec 20 2013 Allen Winter 1.9.95 First 2.0 beta release * Thu Oct 03 2013 Allen Winter 1.3.2 Second 1.3 bugfix release * Tue Apr 30 2013 Allen Winter 1.3.1 First 1.3 bugfix release * Sun Jan 27 2013 Allen Winter 1.3.0 Update to latest release * Wed Jun 27 2012 Allen Winter 1.1.98 - Rename version to use all integers * Tue Jun 26 2012 Allen Winter 1.2beta - Update to beta release * Wed Jun 20 2012 Allen Winter 1.2prebeta - Update to prebeta release * Tue Jan 10 2012 Kevin Ottens 1.1.0 - Update to latest release * Sun Jan 8 2012 Kevin Ottens 1.0.98.20120108 - Grabbing a new snapshot * Wed Dec 21 2011 Kevin Ottens 1.0.1 - First packaging gammaray-2.11.3/inprocessui/000077500000000000000000000000001413310443000157305ustar00rootroot00000000000000gammaray-2.11.3/inprocessui/CMakeLists.txt000066400000000000000000000015441413310443000204740ustar00rootroot00000000000000add_library(gammaray_inprocessui MODULE main.cpp) target_link_libraries(gammaray_inprocessui gammaray_core gammaray_ui_internal) set_target_properties(gammaray_inprocessui PROPERTIES PREFIX "" LIBRARY_OUTPUT_DIRECTORY "${GAMMARAY_OUTPUT_PREFIX}/${PROBE_PLUGIN_INSTALL_DIR}" ) gammaray_set_rpath(gammaray_inprocessui ${PROBE_PLUGIN_INSTALL_DIR}) target_compile_features(gammaray_inprocessui PUBLIC ${GAMMARAY_REQUIRED_CXX_FEATURES}) if(GAMMARAY_INSTALL_QT_LAYOUT) set_target_properties(gammaray_inprocessui PROPERTIES OUTPUT_NAME gammaray_inprocessui-${GAMMARAY_PROBE_ABI}) endif() install(TARGETS gammaray_inprocessui DESTINATION ${PROBE_PLUGIN_INSTALL_DIR}) if(MSVC) install(FILES "$/$" DESTINATION ${PROBE_PLUGIN_INSTALL_DIR} CONFIGURATIONS Debug RelWithDebInfo) endif() gammaray-2.11.3/inprocessui/main.cpp000066400000000000000000000030711413310443000173610ustar00rootroot00000000000000/* main.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include extern "C" { void Q_DECL_EXPORT gammaray_create_inprocess_mainwindow() { auto *window = new GammaRay::MainWindow; window->setAttribute(Qt::WA_DeleteOnClose); GammaRay::Probe::instance()->setWindow(window); GammaRay::Probe::instance()->setParent(window); window->show(); QMetaObject::invokeMethod(window, "selectTool", Q_ARG(QString, QStringLiteral("GammaRay::ObjectInspector"))); } } gammaray-2.11.3/launcher/000077500000000000000000000000001413310443000151665ustar00rootroot00000000000000gammaray-2.11.3/launcher/CMakeLists.txt000066400000000000000000000004061413310443000177260ustar00rootroot00000000000000if(NOT GAMMARAY_PROBE_ONLY_BUILD) add_subdirectory(core) add_subdirectory(cli) if(HAVE_QT_CONCURRENT AND GAMMARAY_BUILD_UI) add_subdirectory(ui) add_subdirectory(app) endif() endif() if(GAMMARAY_BUILD_CLI_INJECTOR) add_subdirectory(win-injector) endif() gammaray-2.11.3/launcher/app/000077500000000000000000000000001413310443000157465ustar00rootroot00000000000000gammaray-2.11.3/launcher/app/CMakeLists.txt000066400000000000000000000012321413310443000205040ustar00rootroot00000000000000set(gammaray_launcher_ui_srcs main.cpp ${CMAKE_SOURCE_DIR}/resources/gammaray.qrc ) gammaray_add_win_icon(gammaray_launcher_ui_srcs) add_executable(gammaray-launcher WIN32 ${gammaray_launcher_ui_srcs}) target_link_libraries(gammaray-launcher Qt5::Gui Qt5::Widgets gammaray_common gammaray_launcher_ui ) gammaray_embed_info_plist(gammaray-launcher ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist.in) set_target_properties(gammaray-launcher PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/${LIBEXEC_INSTALL_DIR}" ) gammaray_set_rpath(gammaray-launcher ${LIBEXEC_INSTALL_DIR}) install(TARGETS gammaray-launcher DESTINATION ${LIBEXEC_INSTALL_DIR}) gammaray-2.11.3/launcher/app/Info.plist.in000066400000000000000000000007371413310443000203320ustar00rootroot00000000000000 NSPrincipalClass NSApplication CFBundleIdentifier com.kdab.GammaRay.launcherUi CFBundleInfoDictionaryVersion 6.0 CFBundleName GammaRay CFBundleVersion @GAMMARAY_VERSION_STRING@ CFBundleShortVersion @GAMMARAY_VERSION@ gammaray-2.11.3/launcher/app/main.cpp000066400000000000000000000046131413310443000174020ustar00rootroot00000000000000/* main.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include #include #include #include #include #include #include using namespace GammaRay; int main(int argc, char **argv) { std::cout << "Startup: gammaray-launcher" << std::endl; QCoreApplication::setOrganizationName(QStringLiteral("KDAB")); QCoreApplication::setOrganizationDomain(QStringLiteral("kdab.com")); QCoreApplication::setApplicationName(QStringLiteral("GammaRay")); #if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0) QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); #endif #if QT_VERSION >= QT_VERSION_CHECK(5, 7, 0) QGuiApplication::setDesktopFileName(QStringLiteral("GammaRay.desktop")); #endif QApplication app(argc, argv); Paths::setRelativeRootPath(GAMMARAY_INVERSE_LIBEXEC_DIR); Translator::loadStandAloneTranslations(); LauncherWindow launcher; launcher.show(); const int result = app.exec(); if (launcher.result() == QDialog::Accepted) { const LaunchOptions opts = launcher.launchOptions(); if (opts.isValid()) opts.execute(LauncherFinder::findLauncher(LauncherFinder::Injector)); } return result; } gammaray-2.11.3/launcher/cli/000077500000000000000000000000001413310443000157355ustar00rootroot00000000000000gammaray-2.11.3/launcher/cli/CMakeLists.txt000066400000000000000000000012261413310443000204760ustar00rootroot00000000000000# command line launcher set(gammaray_runner_srcs main.cpp ) gammaray_add_win_icon(gammaray_runner_srcs) add_executable(gammaray ${gammaray_runner_srcs}) target_link_libraries(gammaray gammaray_launcher gammaray_common) if(HAVE_QT_WIDGETS) target_link_libraries(gammaray Qt5::Gui Qt5::Widgets) endif() gammaray_embed_info_plist(gammaray ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist.in) gammaray_set_rpath(gammaray ${BIN_INSTALL_DIR}) install(TARGETS gammaray ${INSTALL_TARGETS_DEFAULT_ARGS}) if(MSVC) install(FILES "$/$" DESTINATION ${BIN_INSTALL_DIR} CONFIGURATIONS Debug RelWithDebInfo) endif() gammaray-2.11.3/launcher/cli/Info.plist.in000066400000000000000000000007141413310443000203140ustar00rootroot00000000000000 CFBundleIdentifier com.kdab.GammaRay.injector CFBundleInfoDictionaryVersion 6.0 CFBundleName gammaray CFBundleVersion @GAMMARAY_VERSION_STRING@ CFBundleShortVersion @GAMMARAY_VERSION@ LSUIElement 1 gammaray-2.11.3/launcher/cli/main.cpp000066400000000000000000000326271413310443000173770ustar00rootroot00000000000000/* main.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include "config-gammaray-version.h" #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_QT_WIDGETS #include #else #include #endif #include #include #include #include #include #include #include namespace { void shutdownGracefully(int sig) { static volatile std::sig_atomic_t handlingSignal = 0; if (!handlingSignal) { handlingSignal = 1; qDebug() << "Signal" << sig << "received, shutting down gracefully."; QCoreApplication *app = QCoreApplication::instance(); app->quit(); return; } // re-raise signal with default handler and trigger program termination std::signal(sig, SIG_DFL); std::raise(sig); } void installSignalHandler() { #ifdef SIGHUP std::signal(SIGHUP, shutdownGracefully); #endif #ifdef SIGINT std::signal(SIGINT, shutdownGracefully); #endif #ifdef SIGTERM std::signal(SIGTERM, shutdownGracefully); #endif } } using namespace GammaRay; QTextStream out(stdout); QTextStream err(stderr); static void usage(const char *argv0) { out << "Usage: " << argv0 << " [options] [--pid | | --connect [:]]" << endl; out << "" << endl; out << "Inspect runtime internals of a Qt-application, such as:" << endl; out << " QObject tree, properties, signal/slots, widgets, models," << endl; out << " graphics views, javascript debugger, resources," << endl; out << " state machines, meta types, fonts, codecs, text documents" << endl; out << "" << endl; out << "Options:" << endl; out << " -i, --injector \tset injection type, possible values:" << endl; out << " \t" << InjectorFactory::availableInjectors().join(QStringLiteral( ", ")) << endl; out << " -o, --injector-override \tOverride the injector executable if handled (requires -i/--injector)" << endl; out << " -p, --pid \tattach to running Qt application" << endl; out << " --inprocess \tuse in-process UI" << endl; out << " --inject-only \tonly inject the probe, don't show the UI" << endl; out << " --listen
\tspecify the address the server should listen on [default: " << GAMMARAY_DEFAULT_ANY_TCP_URL << "]" << endl; out << " --no-listen \tdisables remote access entirely (implies --inprocess)" << endl; out << " --list-probes \tlist all installed probes" << endl; out << " --probe \tspecify which probe to use" << endl; out << " --connect [:port] \tconnect to an already injected target" << endl; out << " --self-test [injector] \trun self tests, of everything or the specified injector" << endl; out << " -h, --help \tprint program help and exit" << endl; out << " -v, --version \tprint program version and exit" << endl; #ifdef HAVE_QT_WIDGETS out << endl << "When run without any options, " << argv0 << " will present a list of running\n" << "Qt-applications from which you can attach the selected injector. Else,\n" << "you can attach to a running process by specifying its pid, or you can\n" << "start a new Qt-application by specifying its name (and optional arguments)." << endl; #endif } static bool startLauncher() { const QString launcherPath = LauncherFinder::findLauncher(LauncherFinder::LauncherUI); QProcess proc; proc.setProcessChannelMode(QProcess::ForwardedChannels); proc.start(launcherPath); if (!proc.waitForFinished(-1)) return false; return proc.exitCode() == 0; } static QUrl urlFromUserInput(const QString &s) { QUrl url(s); if (url.scheme().isEmpty()) { // backward compat: map input without a scheme to tcp + hostname url.setScheme(QStringLiteral("tcp")); QString host = url.path(); int port = -1; const int pos = host.lastIndexOf(':'); if (pos > 0) { port = host.mid(pos + 1).toUShort(); // clazy:exclude=qstring-ref due to Qt4 support host = host.left(pos); } url.setHost(host); url.setPort(port); url.setPath(QString()); } return url; } int main(int argc, char **argv) { QCoreApplication::setOrganizationName(QStringLiteral("KDAB")); QCoreApplication::setOrganizationDomain(QStringLiteral("kdab.com")); QCoreApplication::setApplicationName(QStringLiteral("GammaRay")); #if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0) QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); #endif installSignalHandler(); QStringList args; args.reserve(argc); for (int i = 1; i < argc; ++i) args.push_back(QString::fromLocal8Bit(argv[i])); #ifndef GAMMARAY_CORE_ONLY_LAUNCHER QApplication app(argc, argv); // for style inspector #else QCoreApplication app(argc, argv); #endif Paths::setRelativeRootPath(GAMMARAY_INVERSE_BIN_DIR); QStringList builtInArgs = QStringList() << QStringLiteral("-style") << QStringLiteral("-stylesheet") << QStringLiteral("-graphicssystem"); LaunchOptions options; while (!args.isEmpty() && args.first().startsWith('-')) { const QString arg = args.takeFirst(); if ((arg == QLatin1String("-i") || arg == QLatin1String("--injector")) && !args.isEmpty()) { options.setInjectorType(args.takeFirst()); continue; } if ((arg == QLatin1String("-o") || arg == QLatin1String("--injector-override")) && !args.isEmpty()) { options.setInjectorTypeExecutableOverride(args.takeFirst()); continue; } if ((arg == QLatin1String("-p") || arg == QLatin1String("--pid")) && !args.isEmpty()) { options.setPid(args.takeFirst().toInt()); continue; } if (arg == QLatin1String("-h") || arg == QLatin1String("--help")) { usage(argv[0]); return 0; } if (arg == QLatin1String("-v") || arg == QLatin1String("--version")) { out << "GammaRay version " << GAMMARAY_VERSION_STRING << endl; out << "Copyright (C) 2010-2021 Klaralvdalens Datakonsult AB, " << "a KDAB Group company, info@kdab.com" << endl; out << "Protocol version " << Protocol::version() << endl; out << "Broadcast version " << Protocol::broadcastFormatVersion() << endl; return 0; } if (arg == QLatin1String("--inprocess")) options.setUiMode(LaunchOptions::InProcessUi); if (arg == QLatin1String("--inject-only")) options.setUiMode(LaunchOptions::NoUi); if (arg == QLatin1String("--listen") && !args.isEmpty()) options.setProbeSetting(QStringLiteral("ServerAddress"), urlFromUserInput(args.takeFirst()).toString()); if (arg == QLatin1String("--no-listen")) { options.setProbeSetting(QStringLiteral("RemoteAccessEnabled"), false); options.setUiMode(LaunchOptions::InProcessUi); } if (arg == QLatin1String("--list-probes")) { foreach (const ProbeABI &abi, ProbeFinder::listProbeABIs()) out << abi.id() << " (" << abi.displayString() << ")" << endl; return 0; } if (arg == QLatin1String("--probe") && !args.isEmpty()) { const ProbeABI abi = ProbeABI::fromString(args.takeFirst()); if (!abi.isValid()) { out << "Invalid probe ABI specified, see --list-probes for valid ones." << endl; return 1; } if (ProbeFinder::findProbe(abi).isEmpty()) { out << abi.id() << " is not a known probe, see --list-probes." << endl; return 1; } options.setProbeABI(abi); } if (arg == QLatin1String("--connect") && !args.isEmpty()) { const QUrl url = urlFromUserInput(args.takeFirst()); ClientLauncher client; client.launch(url); client.waitForFinished(); return 0; } if (arg == QLatin1String("--self-test")) { SelfTest selfTest; QObject::connect(&selfTest, &SelfTest::information, [](const QString &msg) { out << msg << endl; }); QObject::connect(&selfTest, &SelfTest::error, [](const QString &msg) { err << "Error: " << msg << endl; }); if (args.isEmpty() || args.first().startsWith('-')) return selfTest.checkEverything() ? 0 : 1; const auto injectorType = args.takeFirst(); return selfTest.checkInjector(injectorType) ? 0 : 1; } // debug/test options if (arg == QLatin1String("-unittest")) qputenv("GAMMARAY_UNITTEST", "1"); // built-in arguments of QApp, could be meant for us if we are showing the launcher window foreach (const QString &builtInArg, builtInArgs) { if (arg == builtInArg && !args.isEmpty()) args.takeFirst(); } } options.setLaunchArguments(args); if (!options.isValid()) { if (startLauncher()) return 0; usage(argv[0]); return 1; } if (!options.isValid()) return 0; Q_ASSERT(options.isValid()); // attempt to autodetect probe ABI, if not set explicitly if (!options.probeABI().isValid()) { ProbeABIDetector detector; if (options.isLaunch()) options.setProbeABI(detector.abiForExecutable(options.absoluteExecutablePath())); else options.setProbeABI(detector.abiForProcess(options.pid())); } // find a compatible probe if (options.probeABI().isValid()) { const ProbeABI bestABI = ProbeFinder::findBestMatchingABI(options.probeABI()); if (!bestABI.isValid()) { out << "No probe found for ABI " << options.probeABI().id() << endl; return 1; } out << "Detected ABI " << options.probeABI().id() << ", using ABI " << bestABI.id() << "." << endl; options.setProbeABI(bestABI); } else { const QVector availableProbes = ProbeFinder::listProbeABIs(); if (availableProbes.isEmpty()) { out << "No probes found, this is likely an installation problem." << endl; return 1; } if (availableProbes.size() > 1) { out << "No probe ABI specified and ABI auto-detection failed, picking " << availableProbes.first().id() << " at random." << endl; out << "To specify the probe ABI explicitly use --probe , available probes are listed using the --list-probes option." << endl; } options.setProbeABI(availableProbes.first()); } // use a local connection when starting gui locally with this launcher if (!options.probeSettings().contains(QStringLiteral("ServerAddress").toUtf8()) && options.uiMode() != LaunchOptions::NoUi && !LauncherFinder::findLauncher(LauncherFinder::LauncherUI).isEmpty()) { options.setProbeSetting(QStringLiteral("ServerAddress"), "tcp://127.0.0.1"); } Launcher launcher(options); QObject::connect(&launcher, SIGNAL(finished()), &app, SLOT(quit())); QObject::connect(&launcher, SIGNAL(attached()), &app, SLOT(quit())); if (!launcher.start()) return launcher.exitCode(); auto result = app.exec(); return result == 0 ? launcher.exitCode() : result; } gammaray-2.11.3/launcher/core/000077500000000000000000000000001413310443000161165ustar00rootroot00000000000000gammaray-2.11.3/launcher/core/CMakeLists.txt000066400000000000000000000054331413310443000206630ustar00rootroot00000000000000add_subdirectory(injector) set(gammaray_launcher_shared_srcs injector/abstractinjector.cpp injector/processinjector.cpp injector/injectorfactory.cpp injector/styleinjector.cpp libraryutil.cpp probeabi.cpp probeabidetector.cpp probefinder.cpp launchoptions.cpp networkdiscoverymodel.cpp clientlauncher.cpp launcherfinder.cpp launcher.cpp selftest.cpp ) if(WIN32) list(APPEND gammaray_launcher_shared_srcs probeabidetector_win.cpp pefile.cpp injector/basicwindllinjector.cpp injector/windllinjector.cpp ) else() list(APPEND gammaray_launcher_shared_srcs injector/debuggerinjector.cpp injector/gdbinjector.cpp injector/lldbinjector.cpp injector/preloadinjector.cpp ) if(APPLE) list(APPEND gammaray_launcher_shared_srcs probeabidetector_mac.cpp) elseif(UNIX) list(APPEND gammaray_launcher_shared_srcs probeabidetector_elf.cpp) else() list(APPEND gammaray_launcher_shared_srcs probeabidetector_dummy.cpp) endif() endif() add_library(gammaray_launcher SHARED ${gammaray_launcher_shared_srcs}) generate_export_header(gammaray_launcher) set_target_properties(gammaray_launcher PROPERTIES ${GAMMARAY_DEFAULT_LIBRARY_PROPERTIES} ) gammaray_set_rpath(gammaray_launcher ${LIB_INSTALL_DIR}) target_compile_features(gammaray_launcher PUBLIC ${GAMMARAY_REQUIRED_CXX_FEATURES}) gammaray_target_relocatable_interfaces(gammaray_launcher_ipaths) target_include_directories(gammaray_launcher PUBLIC $ $) target_link_libraries(gammaray_launcher LINK_PUBLIC Qt5::Core LINK_PRIVATE gammaray_common Qt5::Network ${CMAKE_DL_LIBS}) if(HAVE_QT_WIDGETS) target_link_libraries(gammaray_launcher LINK_PRIVATE Qt5::Gui Qt5::Widgets) endif() if(WIN32) target_link_libraries(gammaray_launcher LINK_PRIVATE version) elseif(APPLE) target_link_libraries(gammaray_launcher LINK_PRIVATE "-framework CoreFoundation") endif() install(TARGETS gammaray_launcher EXPORT GammaRayTargets ${INSTALL_TARGETS_DEFAULT_ARGS}) gammaray_install_headers(DESTINATION launcher ${CMAKE_CURRENT_BINARY_DIR}/gammaray_launcher_export.h launcher.h launchoptions.h launcherfinder.h probeabi.h probeabidetector.h probefinder.h networkdiscoverymodel.h ) ecm_generate_pri_file(BASE_NAME GammaRayLauncher LIB_NAME gammaray_launcher DEPS "core gui GammaRayCommon" FILENAME_VAR PRI_FILENAME INCLUDE_INSTALL_DIR ${INCLUDE_INSTALL_DIR} ) install(FILES ${PRI_FILENAME} DESTINATION ${ECM_MKSPECS_INSTALL_DIR}) if(MSVC) install(FILES "$/$" DESTINATION ${BIN_INSTALL_DIR} CONFIGURATIONS Debug RelWithDebInfo) endif() gammaray-2.11.3/launcher/core/clientlauncher.cpp000066400000000000000000000043671413310443000216340ustar00rootroot00000000000000/* clientlauncher.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "clientlauncher.h" #include "launcherfinder.h" #include #include using namespace GammaRay; ClientLauncher::ClientLauncher() { m_process.setProcessChannelMode(QProcess::ForwardedChannels); } ClientLauncher::~ClientLauncher() = default; QString ClientLauncher::clientPath() { return LauncherFinder::findLauncher(LauncherFinder::Client); } QStringList ClientLauncher::makeArgs(const QUrl &url) { QStringList args; args.push_back(url.toString()); return args; } bool ClientLauncher::launch(const QUrl &url) { m_process.start(clientPath(), makeArgs(url)); return m_process.waitForStarted(); } void ClientLauncher::launchDetached(const QUrl &url) { const auto args = makeArgs(url); std::cout << "Detaching: " << qPrintable(clientPath()) << " " << qPrintable(args.join(" ")) << std::endl; QProcess::startDetached(clientPath(), args); } void ClientLauncher::terminate() { if (m_process.state() == QProcess::Running) m_process.terminate(); } void ClientLauncher::waitForFinished() { if (m_process.state() == QProcess::Running) m_process.waitForFinished(-1); } gammaray-2.11.3/launcher/core/clientlauncher.h000066400000000000000000000034761413310443000213010ustar00rootroot00000000000000/* clientlauncher.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_CLIENTLAUNCHER_H #define GAMMARAY_CLIENTLAUNCHER_H #include "gammaray_launcher_export.h" #include QT_BEGIN_NAMESPACE class QUrl; QT_END_NAMESPACE namespace GammaRay { /** Launching/monitoring of the GammaRay client for out-of-process use. */ class GAMMARAY_LAUNCHER_EXPORT ClientLauncher { public: ClientLauncher(); ~ClientLauncher(); bool launch(const QUrl &url); void terminate(); void waitForFinished(); static void launchDetached(const QUrl &url); private: Q_DISABLE_COPY(ClientLauncher) static QString clientPath(); static QStringList makeArgs(const QUrl &url); private: QProcess m_process; }; } #endif // GAMMARAY_CLIENTLAUNCHER_H gammaray-2.11.3/launcher/core/injector/000077500000000000000000000000001413310443000177335ustar00rootroot00000000000000gammaray-2.11.3/launcher/core/injector/CMakeLists.txt000066400000000000000000000022471413310443000225000ustar00rootroot00000000000000if(Qt5Widgets_FOUND) set(gammaray_injector_style_srcs injectorstyleplugin.cpp) add_library(gammaray_injector_style MODULE ${gammaray_injector_style_srcs}) target_include_directories(gammaray_injector_style SYSTEM PRIVATE ${Qt5Gui_PRIVATE_INCLUDE_DIRS}) target_link_libraries(gammaray_injector_style Qt5::Core Qt5::Gui Qt5::Widgets ) set_target_properties(gammaray_injector_style PROPERTIES PREFIX "" LIBRARY_OUTPUT_DIRECTORY ${GAMMARAY_OUTPUT_PREFIX}/${PROBE_PLUGIN_INSTALL_DIR}/styles ) gammaray_set_rpath(gammaray_injector_style ${PROBE_PLUGIN_INSTALL_DIR}/styles) target_compile_features(gammaray_injector_style PUBLIC ${GAMMARAY_REQUIRED_CXX_FEATURES}) if(GAMMARAY_INSTALL_QT_LAYOUT) set_target_properties(gammaray_injector_style PROPERTIES OUTPUT_NAME gammaray_injector_style-${GAMMARAY_PROBE_ABI}) endif() install( TARGETS gammaray_injector_style DESTINATION ${PROBE_PLUGIN_INSTALL_DIR}/styles ) if(MSVC) install(FILES "$/$" DESTINATION "${PROBE_PLUGIN_INSTALL_DIR}/styles" CONFIGURATIONS Debug RelWithDebInfo) endif() endif() gammaray-2.11.3/launcher/core/injector/abstractinjector.cpp000066400000000000000000000045471413310443000240120ustar00rootroot00000000000000/* abstractinjector.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "abstractinjector.h" #include #include #include using namespace GammaRay; AbstractInjector::~AbstractInjector() = default; bool AbstractInjector::launch(const QStringList &programAndArgs, const QString &probeDll, const QString &probeFunc, const QProcessEnvironment &env) { Q_UNUSED(programAndArgs); Q_UNUSED(probeDll); Q_UNUSED(probeFunc); Q_UNUSED(env); qWarning() << "Injection on launch not supported by this injector."; return false; } bool AbstractInjector::attach(int pid, const QString &probeDll, const QString &probeFunc) { Q_UNUSED(pid); Q_UNUSED(probeDll); Q_UNUSED(probeFunc); qWarning() << "Attaching to a running process is not supported by this injector."; return false; } bool AbstractInjector::selfTest() { return true; } QString AbstractInjector::workingDirectory() const { if (m_workingDir.isEmpty()) return QDir::currentPath(); return m_workingDir; } void AbstractInjector::setWorkingDirectory(const QString &path) { m_workingDir = path; } ProbeABI AbstractInjector::targetAbi() const { return m_targetAbi; } void AbstractInjector::setTargetAbi(const ProbeABI& abi) { m_targetAbi = abi; } gammaray-2.11.3/launcher/core/injector/abstractinjector.h000066400000000000000000000076241413310443000234560ustar00rootroot00000000000000/* abstractinjector.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_ABSTRACTINJECTOR_H #define GAMMARAY_ABSTRACTINJECTOR_H #include #include #include QT_BEGIN_NAMESPACE class QProcessEnvironment; class QString; class QStringList; QT_END_NAMESPACE namespace GammaRay { class AbstractInjector : public QObject { Q_OBJECT public: using Ptr = QSharedPointer; ~AbstractInjector() override; /** * Injector Name */ virtual QString name() const = 0; /** * Launch the application @p program and inject @p probeDll and call @p probeFunc on it. * Assuming the launcher supports this, @p env allows you to customize the environment * variables of the started application. * * @return True if the launch succeeded, false otherwise. */ virtual bool launch(const QStringList &programAndArgs, const QString &probeDll, const QString &probeFunc, const QProcessEnvironment &env); /** * Attach to the running application with process id @p pid * and inject @p probeDll and call @p probeFunc on it. * * @return True if attaching succeeded, false otherwise. */ virtual bool attach(int pid, const QString &probeDll, const QString &probeFunc); /** * Return the exit code from the application launch or attach. */ virtual int exitCode() = 0; /** * Return the QProcess::ExitStatus from the application launch or attach. */ virtual QProcess::ExitStatus exitStatus() = 0; /** * Return the QProcess::ProcessError from the application launch or attach. */ virtual QProcess::ProcessError processError() = 0; /** * @return Descriptional error message when launch/attach/self-test failed. */ virtual QString errorString() = 0; /** * Perform tests to ensure the injector is operational, such as testing if * all necessary runtime dependencies are available. * @return @c true on success, @c false otherwise. * @note Make sure to set errorString() when returning @c false. */ virtual bool selfTest(); /** Attempts to stop a running process. */ virtual void stop() = 0; /** Returns the working directory supposed to be used for launching. */ QString workingDirectory() const; /** Set working directory for launching. */ void setWorkingDirectory(const QString &path); /*! Returns the ABI of the target. */ ProbeABI targetAbi() const; /*! Sets the ABI of the target. */ void setTargetAbi(const ProbeABI &abi); signals: void started(); void finished(); void attached(); void stdoutMessage(const QString &message); void stderrMessage(const QString &message); private: QString m_workingDir; ProbeABI m_targetAbi; }; } #endif // ABSTRACTINJECTOR_H gammaray-2.11.3/launcher/core/injector/basicwindllinjector.cpp000066400000000000000000000107241413310443000244740ustar00rootroot00000000000000/* basicwindllinjector.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Hannah von Reth Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ //krazy:excludeall=null,captruefalse since this file uses the WinAPI #include "basicwindllinjector.h" #include #define ERROR_ASSERT(condition, action) if (condition) {} else {\ std::wstringstream stream;\ stream << L"Error: " << #condition;\ BasicWinDllInjector::log(stream.str().c_str());\ BasicWinDllInjector::logError(); action; } do {} while(false) #define ERROR_CHECK(condition) ERROR_ASSERT(condition, do {} while(false)); BOOL BasicWinDllInjector::addDllDirectory(HANDLE destProcess, wchar_t *dllDirPath) { if (!remoteKernel32Call(destProcess, "SetDllDirectoryW", dllDirPath)) { std::wstringstream stream; stream << L"Failed to call SetDllDirectoryW" << dllDirPath; log(stream.str().c_str()); return FALSE; } return TRUE; } void BasicWinDllInjector::inject(HANDLE destProcess, wchar_t *dllPath) { // the probe loader will return false to get unloaded so don't check for success remoteKernel32Call(destProcess, "LoadLibraryW", dllPath); } BOOL BasicWinDllInjector::injectProcess(wchar_t *pidString, wchar_t *path, wchar_t *probePath) { const int pid = std::stoi(pidString); HANDLE destProcess = OpenProcess(PROCESS_ALL_ACCESS, false, pid); if (!destProcess) { std::wstringstream stream; stream << L"Failed to open process" << pidString; log(stream.str().c_str()); BasicWinDllInjector::logError(); return FALSE; } if(!addDllDirectory(destProcess, path)) return FALSE; inject(destProcess, probePath); return TRUE; } DWORD BasicWinDllInjector::remoteKernel32Call(HANDLE destProcess, const char *funcName, const wchar_t *argument) { // resolve function pointer auto kernel32handle = GetModuleHandleW(L"kernel32"); auto func = GetProcAddress(kernel32handle, funcName); if (!func) { log(L"Unable to resolve symbol in kernel32.dll!"); logError(); return FALSE; } // write argument into target process memory const int strsize = (static_cast(wcslen(argument)) * 2) + 2; void *mem = VirtualAllocEx(destProcess, NULL, strsize, MEM_COMMIT, PAGE_READWRITE); if (!mem) { log(L"Failed to allocate memory in target process!"); logError(); return FALSE; } ERROR_ASSERT(WriteProcessMemory(destProcess, mem, (void*)argument, strsize, NULL), return FALSE); // call function pointer in remote process auto thread = CreateRemoteThread(destProcess, NULL, 0, (LPTHREAD_START_ROUTINE)func, mem, 0, NULL); if (!thread) { log(L"Filed to creare thread in target process!"); logError(); return FALSE; } WaitForSingleObject(thread, INFINITE); DWORD result; ERROR_CHECK(GetExitCodeThread(thread, &result)); // cleanup ERROR_CHECK(VirtualFreeEx(destProcess, mem, 0, MEM_RELEASE)); ERROR_CHECK(CloseHandle(thread)); return result; } void BasicWinDllInjector::logError(DWORD error) { wchar_t *string = 0; FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM, NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&string, 0, NULL); log(string); LocalFree((HLOCAL)string); } void BasicWinDllInjector::log(const wchar_t *msg) { OutputDebugStringW(msg); } gammaray-2.11.3/launcher/core/injector/basicwindllinjector.h000066400000000000000000000033301413310443000241340ustar00rootroot00000000000000/* basicwindllinjector.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Hannah von Reth Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef BASICWINDLLINJECTOR_H #define BASICWINDLLINJECTOR_H #include #include class BasicWinDllInjector { public: static BOOL addDllDirectory(HANDLE destProcess, wchar_t *dllDirPath); static void inject(HANDLE destProcess, wchar_t *dllPath); static BOOL injectProcess(wchar_t *pid, wchar_t *path, wchar_t *probePath); private: static DWORD remoteKernel32Call(HANDLE destProcess, const char *funcName, const wchar_t *argument); static void logError(DWORD error=GetLastError()); static void log(const wchar_t* msg); }; #endif // BASICWINDLLINJECTOR_H gammaray-2.11.3/launcher/core/injector/debuggerinjector.cpp000066400000000000000000000170271413310443000237700ustar00rootroot00000000000000/* debuggerinjector.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "debuggerinjector.h" #include #include #include #include #include #include #include #include using namespace GammaRay; DebuggerInjector::~DebuggerInjector() { stop(); } QString DebuggerInjector::filePath() const { return m_filePath; } void DebuggerInjector::setFilePath(const QString &filePath) { m_filePath = filePath; } void DebuggerInjector::stop() { if (m_process) { if (!mManualError) { mManualError = true; mErrorString = tr("Process stopped."); } m_process->terminate(); if (!m_process->waitForFinished(1000)) m_process->kill(); // kill it softly } } QString DebuggerInjector::errorString() { return mErrorString; } int DebuggerInjector::exitCode() { return mExitCode; } QProcess::ExitStatus DebuggerInjector::exitStatus() { return mExitStatus; } QProcess::ProcessError DebuggerInjector::processError() { return mProcessError; } void DebuggerInjector::execCmd(const QByteArray &cmd, bool waitForWritten) { processLog(DebuggerInjector::Out, false, QString::fromLatin1(cmd)); m_process->write(cmd + '\n'); if (waitForWritten) m_process->waitForBytesWritten(-1); } void DebuggerInjector::readyReadStandardOutput() { m_process->setReadChannel(QProcess::StandardOutput); while (m_process->canReadLine()) { const QString output = QString::fromLocal8Bit(m_process->readLine()); processLog(DebuggerInjector::In, false, output); emit stdoutMessage(output); } } void DebuggerInjector::setManualError(const QString &msg) { mManualError = true; mErrorString = msg; m_process->kill(); disconnect(m_process.data(), &QProcess::readyReadStandardError, this, nullptr); disconnect(m_process.data(), &QProcess::readyReadStandardOutput, this, nullptr); mProcessError = QProcess::FailedToStart; } void DebuggerInjector::processFinished() { mExitCode = m_process->exitCode(); mExitStatus = m_process->exitStatus(); if (!mManualError) { mProcessError = m_process->error(); if (mProcessError != QProcess::UnknownError) mErrorString = m_process->errorString(); emit attached(); } else { emit finished(); } } void DebuggerInjector::readyReadStandardError() { m_process->setReadChannel(QProcess::StandardError); while (m_process->canReadLine()) { const auto line = m_process->readLine(); parseStandardError(line); const auto error = QString::fromLocal8Bit(line); processLog(DebuggerInjector::In, true, error); emit stderrMessage(error); } } bool DebuggerInjector::startDebugger(const QStringList &args, const QProcessEnvironment &env) { m_process.reset(new QProcess); if (!env.isEmpty()) m_process->setProcessEnvironment(env); connect(m_process.data(), &QProcess::readyReadStandardError, this, &DebuggerInjector::readyReadStandardError); connect(m_process.data(), &QProcess::readyReadStandardOutput, this, &DebuggerInjector::readyReadStandardOutput); connect(m_process.data(), &QProcess::started, this, &AbstractInjector::started); connect(m_process.data(), static_cast(&QProcess::finished), this, &DebuggerInjector::processFinished); m_process->setProcessChannelMode(QProcess::SeparateChannels); m_process->start(filePath(), args); bool status = m_process->waitForStarted(-1); if (!status) { mExitCode = m_process->exitCode(); mExitStatus = m_process->exitStatus(); if (!mManualError) { mProcessError = m_process->error(); mErrorString = m_process->errorString(); } } else { emit started(); } return status; } bool DebuggerInjector::selfTest() { if (QStandardPaths::findExecutable(filePath()).isEmpty()) { mErrorString = tr("The debugger executable '%1' could not be found").arg(filePath()); return false; } // check for the Yama prtrace_scope setting, which can prevent attaching to work QFile file(QStringLiteral("/proc/sys/kernel/yama/ptrace_scope")); if (file.open(QFile::ReadOnly)) { if (file.readAll().trimmed() != "0") { mErrorString = tr( "Yama security extension is blocking runtime attaching, see /proc/sys/kernel/yama/ptrace_scope"); return false; } } if (startDebugger(QStringList() << QStringLiteral("--version"))) return m_process->waitForFinished(-1); return false; } void DebuggerInjector::waitForMain() { addFunctionBreakpoint("main"); execCmd("run"); loadSymbols("Qt5Core"); addMethodBreakpoint("QCoreApplication::exec"); execCmd("continue"); } bool DebuggerInjector::injectAndDetach(const QString &probeDll, const QString &probeFunc) { Q_ASSERT(m_process); loadSymbols("dl"); execCmd(QStringLiteral("call (void) dlopen(\"%1\", %2)"). arg(probeDll).arg(RTLD_NOW).toUtf8()); loadSymbols(probeDll.toUtf8()); execCmd(QStringLiteral("call (void) %1()").arg(probeFunc).toUtf8()); if (qgetenv("GAMMARAY_UNITTEST") != "1") { execCmd("detach"); execCmd("quit"); } else { // delete all breakpoints before we continue, so we don't hit another one and abort there clearBreakpoints(); execCmd("continue"); // if we hit a crash or anything, print backtrace and quit printBacktrace(); execCmd("quit", false); } return true; } void DebuggerInjector::loadSymbols(const QByteArray &library) { Q_UNUSED(library); } void DebuggerInjector::processLog(DebuggerInjector::Orientation orientation, bool isError, const QString &text) { if (qgetenv("GAMMARAY_UNITTEST") == "1") { // clazy:exclude=qgetenv due to Qt4 support const QString output = QString::fromLatin1("%1 [%2] %3: %4") .arg(orientation == DebuggerInjector::In ? "<<<" : ">>>") .arg(QString::fromLatin1(isError ? "ERROR" : "OUTPUT")) .arg(QTime::currentTime().toString(QStringLiteral("HH:mm:ss:zzz"))) .arg(text.trimmed()); if (isError) std::cerr << qPrintable(output) << std::endl; else std::cout << qPrintable(output) << std::endl; } } gammaray-2.11.3/launcher/core/injector/debuggerinjector.h000066400000000000000000000075021413310443000234320ustar00rootroot00000000000000/* debuggerinjector.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_DEBUGGERINJECTOR_H #define GAMMARAY_DEBUGGERINJECTOR_H #include "abstractinjector.h" #include #include namespace GammaRay { /** Base class for debugger-based injectors. */ class DebuggerInjector : public AbstractInjector { Q_OBJECT public: DebuggerInjector() = default; ~DebuggerInjector(); // The debugger executable location QString filePath() const; void setFilePath(const QString &filePath); void stop() override; QProcess::ExitStatus exitStatus() override; QProcess::ProcessError processError() override; int exitCode() override; QString errorString() override; bool selfTest() override; protected: /** Execute a raw command on the debugger. */ virtual void execCmd(const QByteArray &cmd, bool waitForWritten = true); /** Break in the function @p function, specify name without parenthesis. */ virtual void addFunctionBreakpoint(const QByteArray &function) = 0; /** Break in the method @p method, specify name without parenthesis. */ virtual void addMethodBreakpoint(const QByteArray &method) = 0; /** Clear all breakpoints */ virtual void clearBreakpoints() = 0; /** Print current thread backtrace */ virtual void printBacktrace() = 0; /** Load symbols for the given shared library. */ virtual void loadSymbols(const QByteArray &library); /** Start the debugger with the given command line arguments. */ bool startDebugger(const QStringList &args, const QProcessEnvironment &env = QProcessEnvironment()); /** Add a breakpoint in common entry points and wait until they are hit. */ void waitForMain(); /** Given an interrupted process, this injects the probe and continues the process. */ bool injectAndDetach(const QString &probeDll, const QString &probeFunc); /** Set an error that was detected manually rather than by process monitoring. * This will terminate the debugger. */ void setManualError(const QString &msg); /** stderr lines for debugger-specific parsing */ virtual void parseStandardError(const QByteArray &line) = 0; private slots: void readyReadStandardOutput(); void readyReadStandardError(); void processFinished(); protected: QScopedPointer m_process; int mExitCode = -1; QProcess::ProcessError mProcessError = QProcess::UnknownError; QProcess::ExitStatus mExitStatus = QProcess::NormalExit; QString m_filePath; QString mErrorString; private: bool mManualError = false; enum Orientation { In, Out }; void processLog(DebuggerInjector::Orientation orientation, bool isError, const QString &text); }; } #endif // GAMMARAY_DEBUGGERINJECTOR_H gammaray-2.11.3/launcher/core/injector/gdbinjector.cpp000066400000000000000000000110161413310443000227300ustar00rootroot00000000000000/* gdbinjector.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include "gdbinjector.h" #include #include using namespace GammaRay; GdbInjector::GdbInjector(const QString &executableOverride) { setFilePath(executableOverride.isEmpty() ? QStringLiteral("gdb") : executableOverride); } QString GdbInjector::name() const { return QStringLiteral("gdb"); } bool GdbInjector::launch(const QStringList &programAndArgs, const QString &probeDll, const QString &probeFunc, const QProcessEnvironment &env) { QStringList gdbArgs; gdbArgs.push_back(QStringLiteral("--args")); gdbArgs.append(programAndArgs); if (!startDebugger(gdbArgs, env)) return false; setupGdb(); waitForMain(); return injectAndDetach(probeDll, probeFunc); } bool GdbInjector::attach(int pid, const QString &probeDll, const QString &probeFunc) { Q_ASSERT(pid > 0); QStringList gdbArgs; // disable symbol loading early, otherwise it would happen directly after the attach // before we could execute the setupGdb commands below if (supportsAutoSolibAddOff()) { gdbArgs << QStringLiteral("-iex") << QStringLiteral("set auto-solib-add off"); } gdbArgs << QStringLiteral("-pid") << QString::number(pid); if (!startDebugger(gdbArgs)) return false; setupGdb(); return injectAndDetach(probeDll, probeFunc); } void GdbInjector::setupGdb() { execCmd("set confirm off"); if (supportsAutoSolibAddOff()) { execCmd("set auto-solib-add off"); } } void GdbInjector::parseStandardError(const QByteArray &line) { if (line.startsWith("Function \"main\" not defined.")) { setManualError(tr("The debuggee application is missing debug symbols which are required\n" "for GammaRay's GDB injector. Please recompile the debuggee.\n\n" "GDB error was: %1").arg(QString::fromLocal8Bit(line))); } else if (line.startsWith("Can't find member of namespace, class, struct, or union named \"QCoreApplication::exec\"")) { setManualError(tr("Your QtCore library is missing debug symbols which are required\n" "for GammaRay's GDB injector. Please install the required debug symbols.\n\n" "GDB error was: %1").arg(QString::fromLocal8Bit(line))); } else if (line.startsWith("warning: Unable to restore previously selected frame")) { setManualError(tr("The debuggee application seems to have an invalid stack trace\n" "This can be caused by the executable being updated on disk after launching it.\n\n" "GDB error was: %1").arg(QString::fromLocal8Bit(line))); } } void GdbInjector::addFunctionBreakpoint(const QByteArray &function) { execCmd("break " + function); } void GdbInjector::addMethodBreakpoint(const QByteArray &method) { #ifdef Q_OS_MAC execCmd("break " + method + "()"); #else execCmd("break " + method); #endif } void GdbInjector::clearBreakpoints() { execCmd("delete"); } void GdbInjector::printBacktrace() { execCmd("backtrace", false); } void GdbInjector::loadSymbols(const QByteArray &library) { #ifndef Q_OS_MAC execCmd("sha " + library); #else Q_UNUSED(library); #endif } bool GdbInjector::supportsAutoSolibAddOff() const { #ifdef Q_OS_LINUX // 32bit Linux crashes with this return targetAbi().architecture() != QLatin1String("i686"); #endif return true; } gammaray-2.11.3/launcher/core/injector/gdbinjector.h000066400000000000000000000042301413310443000223750ustar00rootroot00000000000000/* gdbinjector.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_GDBINJECTOR_H #define GAMMARAY_GDBINJECTOR_H #include "injector/debuggerinjector.h" namespace GammaRay { class GdbInjector : public DebuggerInjector { Q_OBJECT public: explicit GdbInjector(const QString &executableOverride = QString()); QString name() const override; bool launch(const QStringList &programAndArgs, const QString &probeDll, const QString &probeFunc, const QProcessEnvironment &env) override; bool attach(int pid, const QString &probeDll, const QString &probeFunc) override; protected: void addFunctionBreakpoint(const QByteArray &function) override; void addMethodBreakpoint(const QByteArray &method) override; void clearBreakpoints() override; void printBacktrace() override; void loadSymbols(const QByteArray &library) override; void parseStandardError(const QByteArray & line) override; private: void setupGdb(); /** Certain configurations crash with auto-solib-add off. */ bool supportsAutoSolibAddOff() const; }; } #endif // GAMMARAY_GDBINJECTOR_H gammaray-2.11.3/launcher/core/injector/injectorfactory.cpp000066400000000000000000000104701413310443000236460ustar00rootroot00000000000000/* injectorfactory.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "injectorfactory.h" #include "styleinjector.h" #ifdef Q_OS_WIN #include "windllinjector.h" #else #include "gdbinjector.h" #include "lldbinjector.h" #include "preloadinjector.h" #endif #include #include namespace GammaRay { namespace InjectorFactory { AbstractInjector::Ptr createInjector(const QString &name, const QString &executableOverride) { #ifndef Q_OS_WIN if (name == QLatin1String("gdb")) { return AbstractInjector::Ptr(new GdbInjector(executableOverride)); } if (name == QLatin1String("lldb")) { return AbstractInjector::Ptr(new LldbInjector(executableOverride)); } #else Q_UNUSED(executableOverride); #endif if (name == QLatin1String("style")) { return AbstractInjector::Ptr(new StyleInjector); } #ifndef Q_OS_WIN if (name == QLatin1String("preload")) { return AbstractInjector::Ptr(new PreloadInjector); } #else if (name == QLatin1String("windll")) { return AbstractInjector::Ptr(new WinDllInjector); } #endif return AbstractInjector::Ptr(nullptr); } #if !defined(Q_OS_WIN) static AbstractInjector::Ptr findFirstWorkingInjector(const QStringList &types, QStringList *errorStrings) { for (const QString &type : types) { AbstractInjector::Ptr injector = createInjector(type); if (injector) { if (injector->selfTest()) { return injector; } else if (errorStrings) { *errorStrings << QStringLiteral("%1: %2").arg(type, injector->errorString()); } } else if (errorStrings) { *errorStrings << QStringLiteral("No valid injector type: %1").arg(type); } } return AbstractInjector::Ptr(nullptr); } #endif AbstractInjector::Ptr defaultInjectorForLaunch(const ProbeABI &abi, QStringList *errorStrings) { #if defined(Q_OS_MAC) if (abi.majorQtVersion() >= 5 && abi.minorQtVersion() >= 4) { return createInjector(QStringLiteral("preload")); } return findFirstWorkingInjector(QStringList() << QStringLiteral("lldb") << QStringLiteral("gdb"), errorStrings); #elif defined(Q_OS_UNIX) Q_UNUSED(abi); Q_UNUSED(errorStrings); return createInjector(QStringLiteral("preload")); #else Q_UNUSED(abi); Q_UNUSED(errorStrings); return createInjector(QStringLiteral("windll")); #endif } AbstractInjector::Ptr defaultInjectorForAttach(QStringList *errorStrings) { #if defined(Q_OS_MAC) return findFirstWorkingInjector(QStringList() << QStringLiteral("lldb") << QStringLiteral("gdb"), errorStrings); #elif !defined(Q_OS_WIN) return findFirstWorkingInjector(QStringList() << QStringLiteral("gdb") << QStringLiteral("lldb"), errorStrings); #else Q_UNUSED(errorStrings); return createInjector(QStringLiteral("windll")); #endif } QStringList availableInjectors() { QStringList types; #ifndef Q_OS_WIN types << QStringLiteral("preload") << QStringLiteral("gdb") << QStringLiteral("lldb"); #else types << QStringLiteral("windll"); #endif types << QStringLiteral("style"); return types; } } } gammaray-2.11.3/launcher/core/injector/injectorfactory.h000066400000000000000000000036471413310443000233230ustar00rootroot00000000000000/* injectorfactory.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_INJECTORFACTORY_H #define GAMMARAY_INJECTORFACTORY_H #include "gammaray_launcher_export.h" #include "abstractinjector.h" namespace GammaRay { class ProbeABI; namespace InjectorFactory { GAMMARAY_LAUNCHER_EXPORT AbstractInjector::Ptr createInjector(const QString &name, const QString &executableOverride = QString()); AbstractInjector::Ptr defaultInjectorForLaunch(const ProbeABI &abi, QStringList *errorStrings = nullptr); AbstractInjector::Ptr defaultInjectorForAttach(QStringList *errorStrings = nullptr); /** * Returns the list of available injector types. */ GAMMARAY_LAUNCHER_EXPORT QStringList availableInjectors(); } } #endif // GAMMARAY_INJECTORFACTORY_H gammaray-2.11.3/launcher/core/injector/injectorstyle.json000066400000000000000000000000441413310443000235220ustar00rootroot00000000000000{ "Keys": [ "gammaray-injector" ] } gammaray-2.11.3/launcher/core/injector/injectorstyleplugin.cpp000066400000000000000000000052671413310443000245660ustar00rootroot00000000000000/* injectorstyleplugin.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "injectorstyleplugin.h" #include #include #include #include #include using namespace GammaRay; QStyle *InjectorStylePlugin::create(const QString &) { inject(); const QStringList styleNameList = QGuiApplicationPrivate::platform_theme->themeHint( QPlatformTheme::StyleNames).toStringList(); for (const QString &styleName : styleNameList) { if (QStyle *style = QStyleFactory::create(styleName)) return style; } return nullptr; } QStringList InjectorStylePlugin::keys() const { return QStringList() << QStringLiteral("gammaray-injector"); } void InjectorStylePlugin::inject() { const QByteArray probeDllPath = qgetenv("GAMMARAY_STYLEINJECTOR_PROBEDLL"); if (probeDllPath.isEmpty()) { qWarning("No probe DLL specified."); return; } QLibrary probeDll(QString::fromLocal8Bit(probeDllPath)); probeDll.setLoadHints(QLibrary::ResolveAllSymbolsHint); if (!probeDll.load()) { qWarning() << "Loading probe DLL failed:" << probeDll.errorString(); return; } const QByteArray probeFunc = qgetenv("GAMMARAY_STYLEINJECTOR_PROBEFUNC"); if (probeFunc.isEmpty()) { qWarning("No probe function specified."); return; } QFunctionPointer probeFuncHandle = probeDll.resolve(probeFunc); if (probeFuncHandle) reinterpret_cast(probeFuncHandle)(); else qWarning() << "Resolving probe function failed:" << probeDll.errorString(); } gammaray-2.11.3/launcher/core/injector/injectorstyleplugin.h000066400000000000000000000030651413310443000242250ustar00rootroot00000000000000/* injectorstyleplugin.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_INJECTORSTYLEPLUGIN_H #define GAMMARAY_INJECTORSTYLEPLUGIN_H #include namespace GammaRay { class InjectorStylePlugin : public QStylePlugin { Q_OBJECT Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QStyleFactoryInterface" FILE "injectorstyle.json") public: QStyle *create(const QString &) override; QStringList keys() const; private: void inject(); }; } #endif // GAMMARAY_INJECTORSTYLEPLUGIN_H gammaray-2.11.3/launcher/core/injector/lldbinjector.cpp000066400000000000000000000115721413310443000231200ustar00rootroot00000000000000/* lldbinjector.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "lldbinjector.h" #include using namespace GammaRay; LldbInjector::LldbInjector(const QString &executableOverride) : m_scriptSupportIsRequired(false) { setFilePath(executableOverride.isEmpty() ? QStringLiteral("lldb") : executableOverride); } bool LldbInjector::selfTest() { if (!DebuggerInjector::selfTest()) return false; #ifndef Q_OS_MAC QProcess process; process.setProcessChannelMode(QProcess::MergedChannels); process.start(filePath(), QStringList(QStringLiteral("--version"))); if (process.waitForStarted(-1)) { if (process.waitForFinished(-1)) { const QString output = QString::fromLocal8Bit(process.readAll()).trimmed(); const auto targetMajor = 3; const auto targetMinor = 6; QRegExp rx(QStringLiteral("\\b([\\d]+\\.[\\d]+\\.[\\d]+)\\b")); // lldb version 3.7.0 ( revision ) if (rx.indexIn(output) == -1) { mErrorString = tr("The debugger version can't be read (%1)").arg(output); return false; } const QString version = rx.cap(1); const QStringList parts = version.split(QLatin1Char('.')); if (parts.count() >= 2) { const auto major = parts.at(0).toInt(); const auto minor = parts.at(1).toInt(); if (major > targetMajor || (major == targetMajor && minor >= targetMinor)) return true; } mErrorString = tr("The LLDB version is not compatible: %1 (%2.%3 or higher required)") .arg(version).arg(targetMajor).arg(targetMinor); return false; } } mErrorString = process.errorString(); return false; #else return true; #endif } QString LldbInjector::name() const { return QStringLiteral("lldb"); } void LldbInjector::disableConfirmations() { // Make the debugger synchronous, important so commands are executed in blocking mode // This avoid lldb to quit the app just after running it execCmd("script lldb.debugger.SetAsync(False)"); // Enable auto confirmations on commands (like breakpoint delete) execCmd("settings set auto-confirm true"); // Allow to quit without confirmation execCmd("settings set interpreter.prompt-on-quit false"); } void LldbInjector::addFunctionBreakpoint(const QByteArray &function) { execCmd("breakpoint set -b " + function); } void LldbInjector::addMethodBreakpoint(const QByteArray &method) { execCmd("breakpoint set -M " + method); } void LldbInjector::clearBreakpoints() { execCmd("breakpoint delete"); } void LldbInjector::printBacktrace() { execCmd("thread backtrace"); } bool LldbInjector::launch(const QStringList &programAndArgs, const QString &probeDll, const QString &probeFunc, const QProcessEnvironment &env) { m_scriptSupportIsRequired = true; // launching fails with async command mode, which we can only disable with scripting QStringList args; args.push_back(QStringLiteral("--")); args.append(programAndArgs); if (!startDebugger(args, env)) return false; disableConfirmations(); waitForMain(); return injectAndDetach(probeDll, probeFunc); } bool LldbInjector::attach(int pid, const QString &probeDll, const QString &probeFunc) { Q_ASSERT(pid > 0); if (!startDebugger(QStringList() << QStringLiteral("-p") << QString::number(pid))) return false; disableConfirmations(); return injectAndDetach(probeDll, probeFunc); } void LldbInjector::parseStandardError(const QByteArray& line) { if (m_scriptSupportIsRequired && line.startsWith("error: your copy of LLDB does not support scripting")) setManualError(tr("LLDB does not support scripting. Install lldb python support please.")); } gammaray-2.11.3/launcher/core/injector/lldbinjector.h000066400000000000000000000041071413310443000225610ustar00rootroot00000000000000/* lldbinjector.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_LLDBINJECTOR_H #define GAMMARAY_LLDBINJECTOR_H #include "injector/debuggerinjector.h" namespace GammaRay { class LldbInjector : public DebuggerInjector { Q_OBJECT public: explicit LldbInjector(const QString &executableOverride = QString()); bool selfTest() override; QString name() const override; bool launch(const QStringList &programAndArgs, const QString &probeDll, const QString &probeFunc, const QProcessEnvironment &env) override; bool attach(int pid, const QString &probeDll, const QString &probeFunc) override; protected: void disableConfirmations(); void addFunctionBreakpoint(const QByteArray &function) override; void addMethodBreakpoint(const QByteArray &method) override; void clearBreakpoints() override; void printBacktrace() override; void parseStandardError(const QByteArray & line) override; private: bool m_scriptSupportIsRequired; }; } #endif // GAMMARAY_LLDBINJECTOR_H gammaray-2.11.3/launcher/core/injector/preloadinjector.cpp000066400000000000000000000054151413310443000236300ustar00rootroot00000000000000/* preloadinjector.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "preloadinjector.h" #include #include #include #include #include #include using namespace GammaRay; PreloadInjector::PreloadInjector() = default; QString PreloadInjector::name() const { return QStringLiteral("preload"); } bool PreloadInjector::launch(const QStringList &programAndArgs, const QString &probeDll, const QString &probeFunc, const QProcessEnvironment &_env) { Q_UNUSED(probeFunc); QProcessEnvironment env(_env); #ifdef Q_OS_MAC env.insert(QStringLiteral("DYLD_INSERT_LIBRARIES"), probeDll); env.insert(QStringLiteral("GAMMARAY_UNSET_DYLD"), QStringLiteral("1")); // Make sure Qt do load it's correct libs/plugins. if (probeDll.contains(QStringLiteral("_debug"), Qt::CaseInsensitive)) env.insert(QStringLiteral("DYLD_IMAGE_SUFFIX"), QStringLiteral("_debug")); #else auto exePath = programAndArgs.first(); exePath = QStandardPaths::findExecutable(exePath); // ASAN requires to be loaded first, so check if the target uses that // and if so inject it before GammaRay QStringList ldPreload; foreach (const auto &lib, LibraryUtil::dependencies(exePath)) { if (lib.contains("libasan.so") || lib.contains("libclang_rt.asan")) { ldPreload.push_back(QString::fromLocal8Bit(lib)); break; } } ldPreload.push_back(probeDll); env.insert(QStringLiteral("LD_PRELOAD"), ldPreload.join(QLatin1String(":"))); env.insert(QStringLiteral("GAMMARAY_UNSET_PRELOAD"), QStringLiteral("1")); #endif return launchProcess(programAndArgs, env); } gammaray-2.11.3/launcher/core/injector/preloadinjector.h000066400000000000000000000031201413310443000232640ustar00rootroot00000000000000/* preloadinjector.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_PRELOADINJECTOR_H #define GAMMARAY_PRELOADINJECTOR_H #include "processinjector.h" #include namespace GammaRay { class PreloadInjector : public ProcessInjector { Q_OBJECT public: PreloadInjector(); QString name() const override; bool launch(const QStringList &programAndArgs, const QString &probeDll, const QString &probeFunc, const QProcessEnvironment &_env) override; }; } #endif // GAMMARAY_PRELOADINJECTOR_H gammaray-2.11.3/launcher/core/injector/processinjector.cpp000066400000000000000000000113071413310443000236550ustar00rootroot00000000000000/* processinjector.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "processinjector.h" #include #include #include using namespace GammaRay; ProcessInjector::ProcessInjector() : mExitCode(-1) , mProcessError(QProcess::UnknownError) , mExitStatus(QProcess::NormalExit) { m_proc.setInputChannelMode(QProcess::ForwardedInputChannel); connect(&m_proc, static_cast(&QProcess::error), this, &ProcessInjector::processFailed); connect(&m_proc, static_cast(&QProcess::finished), this, &ProcessInjector::processFinished); connect(&m_proc, &QProcess::readyReadStandardError, this, &ProcessInjector::readStdErr); connect(&m_proc, &QProcess::readyReadStandardOutput, this, &ProcessInjector::readStdOut); } ProcessInjector::~ProcessInjector() { stop(); } void ProcessInjector::stop() { disconnect(&m_proc, static_cast(&QProcess::error), this, &ProcessInjector::processFailed); if (m_proc.state() != QProcess::Running) return; m_proc.terminate(); if (!m_proc.waitForFinished(1000)) // don't wait here. m_proc.kill(); // kill it softly } bool ProcessInjector::launchProcess(const QStringList &programAndArgs, const QProcessEnvironment &_env) { auto env = _env; #ifdef Q_OS_WIN // add location of GammaRay DLLs to PATH, so the probe can find them env.insert(QStringLiteral("PATH"), env.value(QStringLiteral("PATH")) + QLatin1Char(';') + Paths::binPath()); #endif m_proc.setProcessEnvironment(env); m_proc.setWorkingDirectory(workingDirectory()); QStringList args = programAndArgs; if (!env.value(QStringLiteral("GAMMARAY_TARGET_WRAPPER")).isEmpty()) { const QString fullWrapperCmd = env.value(QStringLiteral("GAMMARAY_TARGET_WRAPPER")); // ### TODO properly handle quoted arguments! QStringList newArgs = fullWrapperCmd.split(' '); newArgs += args; args = newArgs; qDebug() << "Launching with target wrapper:" << args; } else if (env.value(QStringLiteral("GAMMARAY_GDB")).toInt()) { QStringList newArgs; newArgs << QStringLiteral("gdb"); #ifndef Q_OS_MAC newArgs << QStringLiteral("--eval-command") << QStringLiteral("run"); #endif newArgs << QStringLiteral("--args"); newArgs += args; args = newArgs; } const QString program = args.takeFirst(); m_proc.start(program, args); bool ret = m_proc.waitForStarted(-1); if (ret) emit started(); return ret; } void ProcessInjector::processFailed() { mProcessError = m_proc.error(); mErrorString = m_proc.errorString(); } void ProcessInjector::processFinished() { mExitCode = m_proc.exitCode(); mExitStatus = m_proc.exitStatus(); if (mProcessError == QProcess::FailedToStart) mErrorString.prepend(QStringLiteral("Could not start '%1': ").arg(m_proc.program())); emit finished(); } int ProcessInjector::exitCode() { return mExitCode; } QProcess::ExitStatus ProcessInjector::exitStatus() { return mExitStatus; } QProcess::ProcessError ProcessInjector::processError() { return mProcessError; } QString ProcessInjector::errorString() { return mErrorString; } void ProcessInjector::readStdErr() { QString error = m_proc.readAllStandardError(); std::cerr << qPrintable(error); emit stderrMessage(error); } void ProcessInjector::readStdOut() { QString message = m_proc.readAllStandardOutput(); std::cout << qPrintable(message); emit stdoutMessage(message); } gammaray-2.11.3/launcher/core/injector/processinjector.h000066400000000000000000000042151413310443000233220ustar00rootroot00000000000000/* processinjector.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_PROCESSINJECTOR_H #define GAMMARAY_PROCESSINJECTOR_H #include "abstractinjector.h" #include namespace GammaRay { /** Convenience base class for injectors using QProcess * to launch the target process. * Provides support for using gdb or valgrind for debugging the target. */ class ProcessInjector : public AbstractInjector { Q_OBJECT public: ProcessInjector(); ~ProcessInjector() override; void stop() override; int exitCode() override; QProcess::ExitStatus exitStatus() override; QProcess::ProcessError processError() override; QString errorString() override; protected: bool launchProcess(const QStringList &programAndArgs, const QProcessEnvironment &_env); int mExitCode; QString mErrorString; private slots: void processFailed(); void processFinished(); void readStdOut(); void readStdErr(); private: QProcess::ProcessError mProcessError; QProcess::ExitStatus mExitStatus; QProcess m_proc; }; } #endif // GAMMARAY_PROCESSINJECTOR_H gammaray-2.11.3/launcher/core/injector/styleinjector.cpp000066400000000000000000000063441413310443000233440ustar00rootroot00000000000000/* styleinjector.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include "styleinjector.h" #include #include #include #include #include #include #ifdef HAVE_QT_WIDGETS #include #endif #include using namespace GammaRay; StyleInjector::StyleInjector() = default; QString StyleInjector::name() const { return QStringLiteral("style"); } static QChar listSeparator() { #if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0) return QDir::listSeparator(); #elif defined(Q_OS_WIN) return ';'; #else return ':'; #endif } bool StyleInjector::launch(const QStringList &programAndArgs, const QString &probeDll, const QString &probeFunc, const QProcessEnvironment &_env) { auto env = _env.isEmpty() ? QProcessEnvironment::systemEnvironment() : _env; env.insert(QStringLiteral("GAMMARAY_STYLEINJECTOR_PROBEDLL"), probeDll); env.insert(QStringLiteral("GAMMARAY_STYLEINJECTOR_PROBEFUNC"), probeFunc); QString qtPluginPath = env.value(QStringLiteral("QT_PLUGIN_PATH")); if (!qtPluginPath.isEmpty()) qtPluginPath.append(listSeparator()); qtPluginPath.append(Paths::pluginPaths(targetAbi().id()).join(listSeparator())); env.insert(QStringLiteral("QT_PLUGIN_PATH"), qtPluginPath); QStringList args = programAndArgs; args << QStringLiteral("-style") << QStringLiteral("gammaray-injector"); return launchProcess(args, env); } bool StyleInjector::selfTest() { #ifndef GAMMARAY_CORE_ONLY_LAUNCHER foreach (const auto &path, Paths::pluginPaths(GAMMARAY_PROBE_ABI)) { // we have no way yet of running a self-test for a specific ABI... QCoreApplication::addLibraryPath(path); } if (!QStyleFactory::keys().contains(QStringLiteral("gammaray-injector"))) { mErrorString = tr("Injector style plugin is not found in the Qt style " "plug-in search path or cannot be loaded"); return false; } return true; #else mErrorString = tr( "GammaRay was compiled without QtWidget support, style injector is not available."); return false; #endif } gammaray-2.11.3/launcher/core/injector/styleinjector.h000066400000000000000000000031311413310443000230000ustar00rootroot00000000000000/* styleinjector.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_STYLEINJECTOR_H #define GAMMARAY_STYLEINJECTOR_H #include "processinjector.h" namespace GammaRay { class StyleInjector : public GammaRay::ProcessInjector { Q_OBJECT public: StyleInjector(); QString name() const override; bool launch(const QStringList &programAndArgs, const QString &probeDll, const QString &probeFunc, const QProcessEnvironment &_env) override; bool selfTest() override; }; } #endif // GAMMARAY_STYLEINJECTOR_H gammaray-2.11.3/launcher/core/injector/windllinjector.cpp000066400000000000000000000146251413310443000234760ustar00rootroot00000000000000/* windllinjector.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Patrick Spendrin Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ // krazy:excludeall=null,captruefalse #include "windllinjector.h" #include "basicwindllinjector.h" #include #include #include #include #include #include #include namespace GammaRay { class FinishWaiter : public QThread { public: FinishWaiter(WinDllInjector *injector) : m_injector(injector) {} ~FinishWaiter() { stop(); } void stop() { if (isRunning()) { terminate(); wait(); } } protected: void run() { WaitForSingleObject(m_injector->m_destProcess, INFINITE); DWORD exitCode; GetExitCodeProcess(m_injector->m_destProcess, &exitCode); QMetaObject::invokeMethod(m_injector, "processExited", Qt::QueuedConnection, Q_ARG(int, exitCode)); } private: WinDllInjector *m_injector; }; WinDllInjector::WinDllInjector() : mExitCode(-1) , mProcessError(QProcess::UnknownError) , mExitStatus(QProcess::NormalExit) , m_destProcess(INVALID_HANDLE_VALUE) , m_injectThread(new FinishWaiter(this)) { } WinDllInjector::~WinDllInjector() { stop(); m_injectThread->wait(1000); delete m_injectThread; } QString WinDllInjector::name() const { return QString("windll"); } bool WinDllInjector::launch(const QStringList &programAndArgs, const QString &probeDll, const QString & /*probeFunc*/, const QProcessEnvironment &_env) { // add location of GammaRay DLLs to PATH, so the probe can find them auto env = _env; env.insert(QStringLiteral("PATH"), env.value(QStringLiteral("PATH")) + QLatin1Char(';') + Paths::binPath()); // https://msdn.microsoft.com/en-us/library/windows/desktop/ms682425%28v=vs.85%29.aspx QByteArray buffer; char null[2] = {0, 0}; foreach (const QString &kv, env.toStringList()) { buffer.append((const char *)kv.utf16(), kv.size() * sizeof(ushort)); buffer.append(null, 2); } if (!buffer.isEmpty()) buffer.append(null, 2); // windows needs double \0 at the end of env vars DWORD dwCreationFlags = CREATE_NO_WINDOW; dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT; dwCreationFlags |= CREATE_SUSPENDED; STARTUPINFOW startupInfo = { sizeof(STARTUPINFO), 0, 0, 0, (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT, 0, 0, 0, STARTF_USESTDHANDLES, 0, 0, 0, GetStdHandle(STD_INPUT_HANDLE), GetStdHandle(STD_OUTPUT_HANDLE), GetStdHandle(STD_ERROR_HANDLE) }; PROCESS_INFORMATION pid; memset(&pid, 0, sizeof(PROCESS_INFORMATION)); const QString applicationName = programAndArgs.join(QLatin1String(" ")); WIN_ERROR_ASSERT(CreateProcess(0, (wchar_t *)applicationName.utf16(), 0, 0, TRUE, dwCreationFlags, buffer.isEmpty() ? 0 : buffer.data(), (wchar_t *)workingDirectory().utf16(), &startupInfo, &pid), return false); m_destProcess = pid.hProcess; QString dllPath = fixProbeDllPath(probeDll); BasicWinDllInjector::inject(m_destProcess, (wchar_t*)dllPath.utf16()); m_injectThread->stop(); emit started(); ResumeThread(pid.hThread); m_injectThread->start(); return m_injectThread->isRunning(); } bool WinDllInjector::attach(int pid, const QString &probeDll, const QString & /*probeFunc*/) { const bool isX64 = probeDll.contains(QLatin1String("x86_64"), Qt::CaseInsensitive); QString application = QString (QLatin1String("gammaray-wininjector-%1")).arg( isX64 ? QLatin1String("x86_64") : QLatin1String("i686")); QStringList args; args << QString::number(pid) << QDir::toNativeSeparators(Paths::binPath()) << fixProbeDllPath(probeDll); QProcess p; p.setProcessChannelMode(QProcess::ForwardedChannels); p.start(application, args); p.waitForFinished(-1); qDebug() << "Calling:" << application << args.join(" "); if (p.error() != QProcess::UnknownError){ qDebug() << "Injection failed:" << p.errorString(); return false; } if (p.exitCode() != 0) { qDebug() << "Injection failed:" << p.readAll(); qDebug() << "Exit code:" << p.exitCode(); return false; } emit started(); return true; } int WinDllInjector::exitCode() { return mExitCode; } QProcess::ProcessError WinDllInjector::processError() { return mProcessError; } QProcess::ExitStatus WinDllInjector::exitStatus() { return mExitStatus; } QString WinDllInjector::errorString() { return mErrorString; } void WinDllInjector::stop() { if (m_destProcess != INVALID_HANDLE_VALUE) TerminateProcess(m_destProcess, 0xff); } void WinDllInjector::processExited(int exitCode) { mExitCode = exitCode; emit finished(); } QString WinDllInjector::fixProbeDllPath(const QString &probeDll) { QString dllPath = QDir::toNativeSeparators(probeDll); dllPath.replace(QLatin1String(GAMMARAY_PROBE_BASENAME), QLatin1String("gammaray_winloader")); Q_ASSERT_X(QFileInfo::exists(dllPath), Q_FUNC_INFO, qPrintable(QString(QLatin1String("%1 does not exists!")).arg(dllPath))); return dllPath; } }// namespace GammaRay gammaray-2.11.3/launcher/core/injector/windllinjector.h000066400000000000000000000043711413310443000231400ustar00rootroot00000000000000/* windllinjector.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Patrick Spendrin Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_WINDLLINJECTOR_H #define GAMMARAY_WINDLLINJECTOR_H #include "abstractinjector.h" #include #include #include namespace GammaRay { class FinishWaiter; class WinDllInjector : public AbstractInjector { Q_OBJECT public: WinDllInjector(); ~WinDllInjector(); QString name() const override; bool launch(const QStringList &programAndArgs, const QString &probeDll, const QString &probeFunc, const QProcessEnvironment &env) override; bool attach(int pid, const QString &probeDll, const QString &probeFunc) override; int exitCode() override; QProcess::ExitStatus exitStatus() override; QProcess::ProcessError processError() override; QString errorString() override; void stop(); private slots: void processExited(int exitCode); private: QString fixProbeDllPath(const QString &probeDll); int mExitCode; QProcess::ProcessError mProcessError; QProcess::ExitStatus mExitStatus; QString mErrorString; HANDLE m_destProcess; FinishWaiter *m_injectThread; friend class FinishWaiter; }; } #endif // GAMMARAY_WINDLLINJECTOR_H gammaray-2.11.3/launcher/core/launcher.cpp000066400000000000000000000306621413310443000204320ustar00rootroot00000000000000/* launcher.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "launcher.h" #include "probefinder.h" #include "probeabi.h" #include "clientlauncher.h" #include "launchoptions.h" #include "injector/abstractinjector.h" #include "injector/injectorfactory.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace GammaRay { enum State { Initial = 0, InjectorFinished = 1, InjectorFailed = 2, ClientStarted = 4, Complete = InjectorFinished | ClientStarted }; struct LauncherPrivate { explicit LauncherPrivate(const LaunchOptions &options) : options(options) , server(nullptr) , socket(nullptr) , state(Initial) , exitCode(0) {} AbstractInjector::Ptr createInjector(QStringList *errorStrings = nullptr) const { if (options.injectorType().isEmpty()) { if (options.isAttach()) { return InjectorFactory::defaultInjectorForAttach(errorStrings); } else { return InjectorFactory::defaultInjectorForLaunch(options.probeABI(), errorStrings); } } return InjectorFactory::createInjector( options.injectorType(), options.injectorTypeExecutableOverride()); } LaunchOptions options; QLocalServer *server; QLocalSocket *socket; ClientLauncher client; QTimer safetyTimer; AbstractInjector::Ptr injector; QUrl serverAddress; QString errorMessage; int state; int exitCode; }; } using namespace GammaRay; Launcher::Launcher(const LaunchOptions &options, QObject *parent) : QObject(parent) , d(new LauncherPrivate(options)) { Q_ASSERT(options.isValid()); const auto timeout = qgetenv("GAMMARAY_LAUNCHER_TIMEOUT").toInt(); d->safetyTimer.setInterval(std::max(60, timeout) * 1000); d->safetyTimer.setSingleShot(true); connect(&d->safetyTimer, &QTimer::timeout, this, &Launcher::timeout); } Launcher::~Launcher() { stop(); d->client.waitForFinished(); delete d; } qint64 Launcher::instanceIdentifier() const { if (d->options.isAttach()) return d->options.pid(); return QCoreApplication::applicationPid(); } void Launcher::stop() { if (d->injector) d->injector->stop(); } static QProcessEnvironment addTargetPluginPaths(QProcessEnvironment env, const ProbeABI &abi) { #if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0) QString qtPluginPath = env.value(QStringLiteral("QT_PLUGIN_PATH")); if (!qtPluginPath.isEmpty()) { qtPluginPath.append(QDir::listSeparator()); } qtPluginPath.append(Paths::targetPluginPaths(abi.id()).join(QDir::listSeparator())); env.insert(QStringLiteral("QT_PLUGIN_PATH"), qtPluginPath); #else Q_UNUSED(abi); #endif return env; } bool Launcher::start() { auto probeDll = d->options.probePath(); if (probeDll.isEmpty()) { probeDll = ProbeFinder::findProbe(d->options.probeABI()); d->options.setProbePath(QFileInfo(probeDll).absolutePath()); } sendLauncherId(); setupProbeSettingsServer(); if (d->options.uiMode() != LaunchOptions::InProcessUi) d->safetyTimer.start(); QStringList errorStrings; d->injector = d->createInjector(&errorStrings); if (!d->injector) { Q_ASSERT(!errorStrings.isEmpty()); std::cerr << "Potential errors:" << std::endl; for (const QString &errorString : qAsConst(errorStrings)) { std::cerr << " Error: " << qPrintable(errorString) << std::endl; } std::cerr << std::endl; if (d->options.injectorType().isEmpty()) { if (d->options.isAttach()) { injectorError(-1, tr("Uh-oh, there is no default attach injector on this platform.")); } else { injectorError(-1, tr("Uh-oh, there is no default launch injector on this platform.")); } } else { injectorError(-1, tr("Injector %1 not found.").arg(d->options.injectorType())); } return false; } d->injector->setTargetAbi(d->options.probeABI()); connect(d->injector.data(), &AbstractInjector::started, this, &Launcher::restartTimer); connect(d->injector.data(), &AbstractInjector::finished, this, &Launcher::injectorFinished, Qt::QueuedConnection); if (d->options.isLaunch()) { connect(d->injector.data(), &AbstractInjector::attached, this, &Launcher::injectorFinished, Qt::QueuedConnection); } connect(d->injector.data(), &AbstractInjector::stderrMessage, this, &Launcher::stderrMessage); connect(d->injector.data(), &AbstractInjector::stdoutMessage, this, &Launcher::stdoutMessage); bool success = false; if (d->options.isLaunch()) { d->injector->setWorkingDirectory(d->options.workingDirectory()); success = d->injector->launch(d->options.launchArguments(), probeDll, QStringLiteral("gammaray_probe_inject"), addTargetPluginPaths(d->options.processEnvironment(), d->options.probeABI())); } else if (d->options.isAttach()) { success = d->injector->attach(d->options.pid(), probeDll, QStringLiteral("gammaray_probe_attach")); } if (!success) { QString errorMessage; if (d->options.isLaunch()) { errorMessage = tr("Failed to launch target '%1'."). arg(d->options.launchArguments().join(QStringLiteral(" "))); } if (d->options.isAttach()) { errorMessage = tr("Failed to attach to target with PID %1.").arg(d->options.pid()); } if (!d->injector->errorString().isEmpty()) { errorMessage += tr("\nError: %1").arg(d->injector->errorString()); } injectorError(d->injector->exitCode() ? d->injector->exitCode() : 1, errorMessage); return false; } return true; } int Launcher::exitCode() const { return d->exitCode; } QString Launcher::errorMessage() const { return d->errorMessage; } QUrl Launcher::serverAddress() const { return d->serverAddress; } void Launcher::sendLauncherId() { // if we are launching a new process, make sure it knows how to talk to us if (d->options.isLaunch()) d->options.setProbeSetting(QStringLiteral("LAUNCHER_ID"), instanceIdentifier()); } void Launcher::setupProbeSettingsServer() { d->server = new QLocalServer(this); d->server->setMaxPendingConnections(1); connect(d->server, &QLocalServer::newConnection, this, &Launcher::newConnection); const QString serverName = QStringLiteral("gammaray-") + QString::number(instanceIdentifier()); d->server->removeServer(serverName); if (!d->server->listen(serverName)) qWarning() << "Unable to send probe settings:" << d->server->errorString(); } void Launcher::printAllAvailableIPs() { std::cout << "GammaRay server listening on:" << std::endl; foreach (const QNetworkInterface &inter, QNetworkInterface::allInterfaces()) { if (!(inter.flags() & QNetworkInterface::IsUp) || !(inter.flags() & QNetworkInterface::IsRunning) || (inter.flags() & QNetworkInterface::IsLoopBack)) { continue; } foreach (const QNetworkAddressEntry &addrEntry, inter.addressEntries()) { const QHostAddress addr = addrEntry.ip(); std::cout << " interface " << qPrintable(inter.name()) << ": " << qPrintable(addr.toString()) << ":" << d->serverAddress.port() << std::endl; } } } void Launcher::startClient(const QUrl &serverAddress) { if (!d->client.launch(serverAddress)) { qCritical("Unable to launch gammaray-client!"); QCoreApplication::exit(1); } } void Launcher::injectorFinished() { d->exitCode = d->injector->exitCode(); if (d->errorMessage.isEmpty()) { d->errorMessage = d->injector->errorString(); if (!d->errorMessage.isEmpty()) { d->state |= InjectorFailed; std::cerr << "Injector error: " << qPrintable(d->errorMessage) << std::endl; } } if ((d->state & InjectorFailed) == 0) { d->state |= InjectorFinished; } checkDone(); } void Launcher::injectorError(int exitCode, const QString &errorMessage) { d->exitCode = exitCode; d->errorMessage = errorMessage; d->state |= InjectorFailed; std::cerr << qPrintable(errorMessage) << std::endl; std::cerr << "See for troubleshooting" << std::endl; checkDone(); } void Launcher::timeout() { d->state |= InjectorFailed; std::cerr << "Target not responding - timeout. Try setting the env variable GAMMARAY_LAUNCHER_TIMEOUT to a bigger value (in seconds)." << std::endl; std::cerr << "See for troubleshooting" << std::endl; checkDone(); } void Launcher::restartTimer() { d->safetyTimer.stop(); d->safetyTimer.start(); } void Launcher::checkDone() { if (d->state == Complete || d->state == InjectorFinished) { emit finished(); } else if ((d->state & InjectorFailed) != 0) { d->client.terminate(); if (d->exitCode == 0) { d->exitCode = 1; } emit finished(); } } void Launcher::newConnection() { if (d->socket) return; d->socket = d->server->nextPendingConnection(); connect(d->socket, &QIODevice::readyRead, this, &Launcher::readyRead); { Message msg(Protocol::LauncherAddress, Protocol::ServerVersion); msg << Protocol::version(); msg.write(d->socket); } { Message msg(Protocol::LauncherAddress, Protocol::ProbeSettings); msg << d->options.probeSettings(); msg.write(d->socket); } } void Launcher::readyRead() { while (Message::canReadMessage(d->socket)) { const auto msg = Message::readMessage(d->socket); switch (msg.type()) { case Protocol::ServerAddress: { msg >> d->serverAddress; break; } case Protocol::ServerLaunchError: { QString reason; msg >> reason; std::cerr << "Failed to start server: " << qPrintable(reason) << std::endl; // TODO emit error signal to also notify qtcreator, etc break; } default: continue; } } if (d->serverAddress.isEmpty()) return; d->safetyTimer.stop(); const auto listenAddress = d->options.probeSettings().value("ServerAddress"); if (listenAddress.isEmpty() || listenAddress.startsWith("tcp://0.0.0.0")) { printAllAvailableIPs(); } else { std::cout << "GammaRay server listening on: " << qPrintable(d->serverAddress.toString()) << std::endl; } if (d->options.uiMode() == LaunchOptions::OutOfProcessUi) startClient(d->serverAddress); if (d->options.isAttach()) emit attached(); d->state |= ClientStarted; checkDone(); emit started(); } gammaray-2.11.3/launcher/core/launcher.h000066400000000000000000000054371413310443000201010ustar00rootroot00000000000000/* launcher.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_LAUNCHER_H #define GAMMARAY_LAUNCHER_H #include "gammaray_launcher_export.h" #include #include QT_BEGIN_NAMESPACE class QProcessEnvironment; class QUrl; QT_END_NAMESPACE namespace GammaRay { class LaunchOptions; struct LauncherPrivate; /*! Manages launching a target process, injecting the probe and if needed also starting the client application. */ class GAMMARAY_LAUNCHER_EXPORT Launcher : public QObject { Q_OBJECT public: explicit Launcher(const LaunchOptions &options, QObject *parent = nullptr); ~Launcher() override; /*! This is used to identify the communication channels used by the launcher and the target process. */ qint64 instanceIdentifier() const; bool start(); void stop(); /*! Target exit code, in case we launched it. */ int exitCode() const; /*! Error message from attaching/launching the target, if any. */ QString errorMessage() const; /*! Address for the client to connect to, valid once received from the target. */ QUrl serverAddress() const; signals: void started(); void finished(); void attached(); void stdoutMessage(const QString &message); void stderrMessage(const QString &message); protected: virtual void startClient(const QUrl &serverAddress); private slots: void injectorError(int exitCode, const QString &errorMessage); void injectorFinished(); void timeout(); void restartTimer(); void newConnection(); void readyRead(); private: void sendLauncherId(); void setupProbeSettingsServer(); void printAllAvailableIPs(); void checkDone(); private: LauncherPrivate * const d; }; } #endif // GAMMARAY_LAUNCHER_H gammaray-2.11.3/launcher/core/launcherfinder.cpp000066400000000000000000000046561413310443000216260ustar00rootroot00000000000000/* launcherfinder.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "launcherfinder.h" #include "probeabi.h" #include #include #include #include #include #include using namespace GammaRay; const char *executableNames[] = { "gammaray", // the Injector "gammaray-launcher", // the LauncherUI "gammaray-client" // the Client }; QString LauncherFinder::findLauncher(LauncherFinder::Type type) { QString fileName = executableNames[type]; #ifdef Q_OS_WIN fileName += ".exe"; #endif QStringList appPaths; // a list of all the paths we have searched QString appPath = Paths::binPath() + QDir::separator() + fileName; QFileInfo fi(appPath); if (fi.isExecutable()) return fi.absoluteFilePath(); appPaths.append(appPath); appPath = Paths::libexecPath() + QDir::separator() + fileName; if (!appPaths.contains(appPath)) { fi.setFile(appPath); if (fi.isExecutable()) return fi.absoluteFilePath(); appPaths.append(appPath); } qWarning() << fileName << "not found in the expected location(s):"; qWarning() << appPaths.join(QStringLiteral(", ")) << endl << "continuing anyway, hoping for it to be in PATH."; qWarning() << "This is likely a setup problem." << endl; return fileName; } gammaray-2.11.3/launcher/core/launcherfinder.h000066400000000000000000000032371413310443000212650ustar00rootroot00000000000000/* launcherfinder.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_LAUNCHERFINDER_H #define GAMMARAY_LAUNCHERFINDER_H #include "gammaray_launcher_export.h" #include "probeabi.h" #include namespace GammaRay { /*! Utility function to find the various executables related to the launcher. */ namespace LauncherFinder { /*! Identifies a launcher helper executable type. */ enum Type { Injector, LauncherUI, Client }; /*! Returns the path to the helper executable of type @p type. */ GAMMARAY_LAUNCHER_EXPORT QString findLauncher(Type type); } } #endif // GAMMARAY_LAUNCHERFINDER_H gammaray-2.11.3/launcher/core/launchoptions.cpp000066400000000000000000000153151413310443000215150ustar00rootroot00000000000000/* launchoptions.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "launchoptions.h" #include "probeabi.h" #include #include #include #include #include #include namespace GammaRay { class LaunchOptionsPrivate : public QSharedData { public: LaunchOptionsPrivate() : env(QProcessEnvironment::systemEnvironment()) {} QStringList launchArguments; QString injectorType; QString injectorTypeExecutableOverride; ProbeABI probeABI; int pid = -1; LaunchOptions::UiMode uiMode = LaunchOptions::OutOfProcessUi; QHash probeSettings; QProcessEnvironment env; QString workingDir; }; } using namespace GammaRay; LaunchOptions::LaunchOptions() : d(new LaunchOptionsPrivate) { } LaunchOptions::~LaunchOptions() = default; LaunchOptions::LaunchOptions(const LaunchOptions &) = default; LaunchOptions &LaunchOptions::operator=(const LaunchOptions &) = default; bool LaunchOptions::isLaunch() const { return !d->launchArguments.isEmpty(); } bool LaunchOptions::isAttach() const { return pid() > 0; } bool LaunchOptions::isValid() const { return isLaunch() != isAttach(); } QStringList LaunchOptions::launchArguments() const { return d->launchArguments; } void LaunchOptions::setLaunchArguments(const QStringList &args) { d->launchArguments = args; Q_ASSERT(d->pid <= 0 || d->launchArguments.isEmpty()); } QString LaunchOptions::absoluteExecutablePath() const { if (d->launchArguments.isEmpty()) return QString(); QString path = d->launchArguments.first(); const QFileInfo fi(path); if (fi.isFile() && fi.isExecutable()) return path; path = QStandardPaths::findExecutable(d->launchArguments.first()); if (!path.isEmpty()) return path; return d->launchArguments.first(); } int LaunchOptions::pid() const { return d->pid; } void LaunchOptions::setPid(int pid) { d->pid = pid; Q_ASSERT(d->pid <= 0 || d->launchArguments.isEmpty()); } LaunchOptions::UiMode LaunchOptions::uiMode() const { return d->uiMode; } void LaunchOptions::setUiMode(LaunchOptions::UiMode mode) { d->uiMode = mode; setProbeSetting(QStringLiteral("InProcessUi"), mode == InProcessUi); } QString LaunchOptions::injectorType() const { return d->injectorType; } void LaunchOptions::setInjectorType(const QString &injectorType) { d->injectorType = injectorType; } QString LaunchOptions::injectorTypeExecutableOverride() const { return d->injectorTypeExecutableOverride; } void LaunchOptions::setInjectorTypeExecutableOverride(const QString &filePath) { d->injectorTypeExecutableOverride = filePath; } ProbeABI LaunchOptions::probeABI() const { return d->probeABI; } void LaunchOptions::setProbeABI(const ProbeABI &abi) { d->probeABI = abi; } void LaunchOptions::setProcessEnvironment(const QProcessEnvironment &env) { d->env = env; } QProcessEnvironment LaunchOptions::processEnvironment() const { QProcessEnvironment env = d->env; for (auto it = d->probeSettings.constBegin(); it != d->probeSettings.constEnd(); ++it) env.insert("GAMMARAY_" + it.key(), it.value()); return env; } void LaunchOptions::setProbePath(const QString &path) { setProbeSetting(QStringLiteral("ProbePath"), path); } QString LaunchOptions::probePath() const { return d->probeSettings.value("ProbePath"); } void LaunchOptions::setWorkingDirectory(const QString &path) { d->workingDir = path; } QString LaunchOptions::workingDirectory() const { return d->workingDir; } void LaunchOptions::setProbeSetting(const QString &key, const QVariant &value) { QByteArray v; switch (value.type()) { case QVariant::String: v = value.toString().toUtf8(); break; case QVariant::Bool: v = value.toBool() ? "true" : "false"; break; case QVariant::Int: case QVariant::UInt: case QVariant::LongLong: case QVariant::ULongLong: v = QByteArray::number(value.toInt()); break; default: qFatal("unsupported probe settings type"); } d->probeSettings.insert(key.toUtf8(), v); } QHash< QByteArray, QByteArray > LaunchOptions::probeSettings() const { return d->probeSettings; } bool LaunchOptions::execute(const QString &launcherPath) const { Q_ASSERT(!launcherPath.isEmpty()); Q_ASSERT(isValid()); QStringList args; switch (uiMode()) { case InProcessUi: args.push_back(QStringLiteral("--inprocess")); break; case OutOfProcessUi: args.push_back(QStringLiteral("--no-inprocess")); break; case NoUi: args.push_back(QStringLiteral("--inject-only")); break; } if (d->probeABI.isValid()) { args.push_back(QStringLiteral("--probe")); args.push_back(d->probeABI.id()); } if (d->probeSettings.contains("ServerAddress")) { args.push_back(QStringLiteral("--listen")); args.push_back(d->probeSettings.value("ServerAddress")); } if (d->probeSettings.value("RemoteAccessEnabled", "true") == "false") args.push_back(QStringLiteral("--no-listen")); if (isAttach()) { args.push_back(QStringLiteral("--pid")); args.push_back(QString::number(pid())); } else { args += launchArguments(); } auto workingDir = d->workingDir; if (workingDir.isEmpty()) { workingDir = QDir::currentPath(); } std::cout << "Detaching: " << qPrintable(launcherPath) << " " << qPrintable(args.join(" ")) << "\n" << "Working Directory: " << qPrintable(workingDir) << std::endl; return QProcess::startDetached(launcherPath, args, workingDir); } gammaray-2.11.3/launcher/core/launchoptions.h000066400000000000000000000100101413310443000211450ustar00rootroot00000000000000/* launchoptions.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_LAUNCHOPTIONS_H #define GAMMARAY_LAUNCHOPTIONS_H #include "gammaray_launcher_export.h" #include #include QT_BEGIN_NAMESPACE class QStringList; class QVariant; class QProcessEnvironment; QT_END_NAMESPACE namespace GammaRay { class ProbeABI; class LaunchOptionsPrivate; /** @brief Describes the injection and probe options used for launching/attacing to a host process. */ class GAMMARAY_LAUNCHER_EXPORT LaunchOptions { public: LaunchOptions(); LaunchOptions(const LaunchOptions &other); ~LaunchOptions(); LaunchOptions &operator=(const LaunchOptions &other); enum UiMode { InProcessUi, OutOfProcessUi, NoUi }; /** Returns @c true if this is valid and has launch arguments set. */ bool isLaunch() const; /** Returns @c true if we are supposed to attach rather than start a new process. */ bool isAttach() const; /** Returns @c true if no valid launch arguments or process id are set. */ bool isValid() const; /** Generic key/value settings send to the probe. */ void setProbeSetting(const QString &key, const QVariant &value); QHash probeSettings() const; /** Program and command line arguments to launch. */ void setLaunchArguments(const QStringList &args); QStringList launchArguments() const; /** Absolute path (as far as it can be determined) of the executable to launch. * Only valid if isLaunch() returns @c true. */ QString absoluteExecutablePath() const; /** Process id for the process to attach to. */ void setPid(int pid); int pid() const; /** UI mode. */ UiMode uiMode() const; void setUiMode(UiMode mode); /** Injector type. */ QString injectorType() const; void setInjectorType(const QString &injectorType); /** Injector executable override. */ QString injectorTypeExecutableOverride() const; void setInjectorTypeExecutableOverride(const QString &filePath); /** Probe ABI. */ ProbeABI probeABI() const; void setProbeABI(const ProbeABI &abi); /** Full path to the probe being used. This overrides specifying a probe ABI and * can be useful on non-standard installation layouts of the probes. */ void setProbePath(const QString &path); QString probePath() const; /** Working directory for launching the target. */ void setWorkingDirectory(const QString &path); QString workingDirectory() const; /** Process environment for the launched target. By default the environment of the launcher process is used. */ void setProcessEnvironment(const QProcessEnvironment &env); QProcessEnvironment processEnvironment() const; /** execute this launch options with the given command-line launcher. */ bool execute(const QString &launcherPath) const; private: QSharedDataPointer d; }; } #endif // GAMMARAY_LAUNCHOPTIONS_H gammaray-2.11.3/launcher/core/libraryutil.cpp000066400000000000000000000053721413310443000211730ustar00rootroot00000000000000/* libraryutil.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "libraryutil.h" using namespace GammaRay; #include #include #include #include static QVector dependenciesInternal(const QString &fileName, bool fallback = false) { QVector result; QProcess proc; proc.setProcessChannelMode(QProcess::SeparateChannels); proc.setReadChannel(QProcess::StandardOutput); if (!fallback) { // first try to use ldd proc.start(QStringLiteral("ldd"), QStringList() << fileName); if (!proc.waitForStarted()) // if that is not available, run the fallback return dependenciesInternal(fileName, true); } else { // see https://man7.org/linux/man-pages/man8/ld.so.8.html // by setting LD_TRACE_LOADED_OBJECTS=1 we make ld.so behave like ldd // this works even on embedded systems where ldd is not available QProcessEnvironment env = proc.processEnvironment(); env.insert(QStringLiteral("LD_TRACE_LOADED_OBJECTS"), QStringLiteral("1")); proc.setProcessEnvironment(env); proc.start(fileName); } proc.waitForFinished(); while (proc.canReadLine()) { const QByteArray line = proc.readLine(); if (line.isEmpty()) break; const int begin = line.indexOf("=> "); const int end = line.lastIndexOf(" ("); if (begin <= 0 || end <= 0 || end <= begin) { continue; } result.push_back(line.mid(begin + 3, end - begin - 3).trimmed()); } return result; } QVector LibraryUtil::dependencies(const QString &fileName) { return dependenciesInternal(fileName); } gammaray-2.11.3/launcher/core/libraryutil.h000066400000000000000000000031451413310443000206340ustar00rootroot00000000000000/* libraryutil.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_LIBRARYUTIL_H #define GAMMARAY_LIBRARYUTIL_H #include QT_BEGIN_NAMESPACE class QByteArray; template class QVector; QT_END_NAMESPACE namespace GammaRay { /*! Utilities for inspecting library dependencies. */ namespace LibraryUtil { /*! Returns a list of dependencies for @p fileName. * @note This is currently only available on Linux. */ QVector dependencies(const QString &fileName); } } #endif // GAMMARAY_LIBRARYUTIL_H gammaray-2.11.3/launcher/core/networkdiscoverymodel.cpp000066400000000000000000000142011413310443000232620ustar00rootroot00000000000000/* networkdiscoverymodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "networkdiscoverymodel.h" #include #include #include #include #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) #include #endif #include using namespace GammaRay; bool NetworkDiscoveryModel::ServerInfo::operator==(const NetworkDiscoveryModel::ServerInfo &other) { return url == other.url; } NetworkDiscoveryModel::NetworkDiscoveryModel(QObject *parent) : QAbstractTableModel(parent) , m_socket(new QUdpSocket(this)) { m_socket->bind(Endpoint::broadcastPort(), QUdpSocket::ShareAddress); connect(m_socket, &QIODevice::readyRead, this, &NetworkDiscoveryModel::processPendingDatagrams); auto *expireTimer = new QTimer(this); expireTimer->setInterval(15 * 1000); expireTimer->setSingleShot(false); connect(expireTimer, &QTimer::timeout, this, &NetworkDiscoveryModel::expireEntries); expireTimer->start(); } NetworkDiscoveryModel::~NetworkDiscoveryModel() = default; void NetworkDiscoveryModel::processPendingDatagrams() { while (m_socket->hasPendingDatagrams()) { #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) const auto datagram = m_socket->receiveDatagram(); const auto buffer = datagram.data(); const auto senderAddr = datagram.senderAddress(); #else QByteArray buffer; buffer.resize(m_socket->pendingDatagramSize()); m_socket->readDatagram(buffer.data(), buffer.size()); const QHostAddress senderAddr; #endif QDataStream stream(buffer); qint32 broadcastVersion; stream >> broadcastVersion; if (broadcastVersion != Protocol::broadcastFormatVersion()) continue; ServerInfo info; stream >> info.version >> info.url >> info.label; info.lastSeen = QDateTime::currentDateTime(); // sender address is more reliable in case the probe runs on // a system with multiple public network interfaces if (!senderAddr.isNull() && info.url.scheme() == QLatin1String("tcp")) { switch (senderAddr.protocol()) { case QAbstractSocket::IPv4Protocol: info.url.setHost(senderAddr.toString()); break; case QAbstractSocket::IPv6Protocol: info.url.setHost(QLatin1Char('[') + senderAddr.toString() + QLatin1Char(']')); break; default: break; } } auto it = std::find(m_data.begin(), m_data.end(), info); if (it == m_data.end()) { beginInsertRows(QModelIndex(), m_data.size(), m_data.size()); m_data.push_back(info); endInsertRows(); } else { it->lastSeen = info.lastSeen; } } } void NetworkDiscoveryModel::expireEntries() { const QDateTime threshold = QDateTime::currentDateTime().addSecs(-30); for (auto it = m_data.begin(); it != m_data.end();) { if (it->lastSeen >= threshold) { ++it; } else { const int currentRow = std::distance(m_data.begin(), it); beginRemoveRows(QModelIndex(), currentRow, currentRow); it = m_data.erase(it); endRemoveRows(); } } } QVariant NetworkDiscoveryModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); const ServerInfo &info = m_data.at(index.row()); if (role == Qt::DisplayRole) { switch (index.column()) { case 0: return info.label; case 1: return info.url.toString(); } } else if (role == Qt::ToolTipRole) { if (info.version != Protocol::version()) return tr("Incompatible GammaRay version."); } else if (role == HostNameRole) { return info.url.host(); } else if (role == PortRole) { return info.url.port(); } else if (role == CompatibleRole) { return info.version == Protocol::version(); } else if (role == UrlStringRole) { return info.url.toString(); } return QVariant(); } int NetworkDiscoveryModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); return 2; } int NetworkDiscoveryModel::rowCount(const QModelIndex &parent) const { if (parent.isValid()) return 0; return m_data.size(); } QVariant NetworkDiscoveryModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { switch (section) { case 0: return tr("Name"); case 1: return tr("Host"); } } return QVariant(); } Qt::ItemFlags NetworkDiscoveryModel::flags(const QModelIndex &index) const { const Qt::ItemFlags baseFlags = QAbstractTableModel::flags(index); if (!index.isValid()) return baseFlags; const ServerInfo &info = m_data.at(index.row()); if (info.version != Protocol::version()) return baseFlags & ~(Qt::ItemIsSelectable|Qt::ItemIsEnabled); return baseFlags; } gammaray-2.11.3/launcher/core/networkdiscoverymodel.h000066400000000000000000000050011413310443000227250ustar00rootroot00000000000000/* networkdiscoverymodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_NETWORKDISCOVERYMODEL_H #define GAMMARAY_NETWORKDISCOVERYMODEL_H #include "gammaray_launcher_export.h" #include #include #include #include QT_BEGIN_NAMESPACE class QUdpSocket; QT_END_NAMESPACE namespace GammaRay { /** \brief Lists all active server instances found via network autodiscovery. */ class GAMMARAY_LAUNCHER_EXPORT NetworkDiscoveryModel : public QAbstractTableModel { Q_OBJECT public: enum Role { HostNameRole = Qt::UserRole + 1, PortRole, CompatibleRole, UrlStringRole }; explicit NetworkDiscoveryModel(QObject *parent); ~NetworkDiscoveryModel() override; QVariant data(const QModelIndex &index, int role) const override; int columnCount(const QModelIndex &parent) const override; int rowCount(const QModelIndex &parent) const override; QVariant headerData(int section, Qt::Orientation orientation, int role) const override; Qt::ItemFlags flags(const QModelIndex &index) const override; private slots: void processPendingDatagrams(); void expireEntries(); private: QUdpSocket *m_socket; struct ServerInfo { bool operator==(const ServerInfo &other); qint32 version; QUrl url; QString label; QDateTime lastSeen; }; QVector m_data; }; } #endif // GAMMARAY_NETWORKDISCOVERYMODEL_H gammaray-2.11.3/launcher/core/pefile.cpp000066400000000000000000000126341413310443000200740ustar00rootroot00000000000000/* pefile.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "pefile.h" using namespace GammaRay; PEFile::PEFile(const QString &filePath) : m_file(filePath) , m_begin(nullptr) , m_end(nullptr) , m_fileHeader(nullptr) , m_importDesc(nullptr) { if (!m_file.open(QFile::ReadOnly)) return; m_begin = m_file.map(0, m_file.size()); m_end = m_begin + m_file.size(); if (!parse()) close(); } PEFile::~PEFile() { close(); } void PEFile::close() { if (!m_begin) return; m_file.close(); m_begin = nullptr; m_end = nullptr; m_fileHeader = nullptr; m_importDesc = nullptr; } bool PEFile::parse() { const uchar *data = m_begin; if (!data || m_file.size() < sizeof(IMAGE_DOS_HEADER)) return false; // check signatures const IMAGE_DOS_HEADER *dosHdr = reinterpret_cast(data); if (dosHdr->e_magic != IMAGE_DOS_SIGNATURE) return false; data += dosHdr->e_lfanew; if (data + sizeof(quint32) >= m_end) return false; const quint32 *peHdr = reinterpret_cast(data); if (*peHdr != IMAGE_NT_SIGNATURE) return false; data += sizeof(quint32); if (data + sizeof(IMAGE_FILE_HEADER) >= m_end) return false; // file header (for architecture) m_fileHeader = reinterpret_cast(data); data += sizeof(IMAGE_FILE_HEADER); if (data + sizeof(IMAGE_OPTIONAL_HEADER64) >= m_end) return false; // optional headers (for import descriptor) const IMAGE_OPTIONAL_HEADER32 *optHdr32 = reinterpret_cast(data); if (optHdr32->Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) { data = rvaToFile(m_fileHeader, optHdr32->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress); } else { const IMAGE_OPTIONAL_HEADER64 *optHdr64 = reinterpret_cast(data); if (optHdr64->Magic != IMAGE_NT_OPTIONAL_HDR64_MAGIC) return false; data = rvaToFile(m_fileHeader, optHdr64->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress); } if (data + sizeof(IMAGE_IMPORT_DESCRIPTOR) >= m_end) return false; m_importDesc = reinterpret_cast(data); return true; } bool PEFile::isValid() const { return m_begin != nullptr; } QString PEFile::architecture() const { if (!m_fileHeader) return QString(); switch (m_fileHeader->Machine) { case IMAGE_FILE_MACHINE_I386: return QStringLiteral("i686"); case IMAGE_FILE_MACHINE_AMD64: return QStringLiteral("x86_64"); } return QString(); } QStringList PEFile::imports() const { QStringList libs; if (!m_importDesc) return libs; auto importDesc = m_importDesc; while (importDesc->Name) { const char *libraryName = reinterpret_cast(rvaToFile(m_fileHeader, importDesc->Name)); if (libraryName) libs.push_back(QString::fromLatin1(libraryName)); importDesc++; if (reinterpret_cast(importDesc) + sizeof(IMAGE_IMPORT_DESCRIPTOR) >= m_end) return QStringList(); } return libs; } const uchar *PEFile::rvaToFile(const IMAGE_FILE_HEADER *hdr, DWORD rva) const { Q_ASSERT(m_begin); Q_ASSERT(m_end); Q_ASSERT(hdr); const IMAGE_SECTION_HEADER *sectionHdr = sectionForRVA(hdr, rva); if (!sectionHdr) return nullptr; return m_begin + rva - sectionHdr->VirtualAddress + sectionHdr->PointerToRawData; } const IMAGE_SECTION_HEADER *PEFile::sectionForRVA(const IMAGE_FILE_HEADER *hdr, DWORD rva) const { Q_ASSERT(hdr); Q_ASSERT(m_end); const uchar *data = reinterpret_cast(hdr); auto sectionHdr = reinterpret_cast(data + sizeof(IMAGE_FILE_HEADER) + hdr->SizeOfOptionalHeader); for (int i = 0; i < hdr->NumberOfSections; ++i, ++sectionHdr) { if (reinterpret_cast(sectionHdr +1) >= m_end) return nullptr; if (rva >= sectionHdr->VirtualAddress && rva < sectionHdr->VirtualAddress + sectionHdr->Misc.VirtualSize) return sectionHdr; } return nullptr; } gammaray-2.11.3/launcher/core/pefile.h000066400000000000000000000036521413310443000175410ustar00rootroot00000000000000/* pefile.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_PEFILE_H #define GAMMARAY_PEFILE_H #include #include #include namespace GammaRay { /** Convenience API to deal with extracting information from PE files. */ class PEFile { public: explicit PEFile(const QString &filePath); ~PEFile(); bool isValid() const; QString architecture() const; QStringList imports() const; private: Q_DISABLE_COPY(PEFile) void close(); bool parse(); const uchar *rvaToFile(const IMAGE_FILE_HEADER *hdr, DWORD rva) const; const IMAGE_SECTION_HEADER *sectionForRVA(const IMAGE_FILE_HEADER *hdr, DWORD rva) const; QFile m_file; const uchar *m_begin; const uchar *m_end; const IMAGE_FILE_HEADER *m_fileHeader; const IMAGE_IMPORT_DESCRIPTOR *m_importDesc; }; } #endif // GAMMARAY_PEFILE_H gammaray-2.11.3/launcher/core/probeabi.cpp000066400000000000000000000153301413310443000204070ustar00rootroot00000000000000/* probeabi.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include "probeabi.h" #include #include #include #include #include #include namespace GammaRay { class ProbeABIContext { Q_DECLARE_TR_FUNCTIONS(GammaRay::ProbeABIContext) }; class ProbeABIPrivate : public QSharedData { public: ProbeABIPrivate() = default; ProbeABIPrivate(const ProbeABIPrivate &) = default; QString architecture; QString compiler; QString compilerVersion; int majorQtVersion = -1; int minorQtVersion = -1; bool isDebug = false; }; } using namespace GammaRay; ProbeABI::ProbeABI() : d(new ProbeABIPrivate) { } ProbeABI::ProbeABI(const ProbeABI &) = default; ProbeABI::~ProbeABI() = default; ProbeABI &ProbeABI::operator=(const ProbeABI &) = default; int ProbeABI::majorQtVersion() const { return d->majorQtVersion; } int ProbeABI::minorQtVersion() const { return d->minorQtVersion; } void ProbeABI::setQtVersion(int major, int minor) { d->majorQtVersion = major; d->minorQtVersion = minor; } bool ProbeABI::hasQtVersion() const { return d->majorQtVersion > 0 && d->minorQtVersion >= 0; } QString ProbeABI::architecture() const { return d->architecture; } void ProbeABI::setArchitecture(const QString &architecture) { d->architecture = architecture; } QString ProbeABI::compiler() const { return d->compiler; } void ProbeABI::setCompiler(const QString &compiler) { d->compiler = compiler; } QString ProbeABI::compilerVersion() const { return d->compilerVersion; } void ProbeABI::setCompilerVersion(const QString &compilerVersion) { d->compilerVersion = compilerVersion; } bool ProbeABI::isVersionRelevant() const { return compiler() == QLatin1String("MSVC"); } bool ProbeABI::isDebug() const { return d->isDebug; } void ProbeABI::setIsDebug(bool debug) { d->isDebug = debug; } bool ProbeABI::isDebugRelevant() const { #if defined(Q_OS_MAC) || defined(Q_OS_WIN) return true; #else return false; #endif } bool ProbeABI::isValid() const { return hasQtVersion() && !d->architecture.isEmpty() #ifdef Q_OS_WIN && !d->compiler.isEmpty() && (!isVersionRelevant() || !d->compilerVersion.isEmpty()) #endif ; } bool ProbeABI::isCompatible(const ProbeABI &referenceABI) const { return d->majorQtVersion == referenceABI.majorQtVersion() && d->minorQtVersion >= referenceABI.minorQtVersion() // we can work with older probes, since the target defines the Qt libraries being used && d->architecture == referenceABI.architecture() #ifdef Q_OS_WIN && d->compiler == referenceABI.compiler() && d->compilerVersion == referenceABI.compilerVersion() #endif && (isDebugRelevant() ? d->isDebug == referenceABI.isDebug() : true) ; } QString ProbeABI::id() const { if (!isValid()) return QString(); QStringList idParts; idParts.push_back(QStringLiteral("qt%1_%2").arg(majorQtVersion()).arg(minorQtVersion())); #ifdef Q_OS_WIN idParts.push_back(compiler()); if (isVersionRelevant()) idParts.push_back(compilerVersion()); #endif idParts.push_back(architecture()); return idParts.join(QStringLiteral("-")).append(isDebugRelevant() && isDebug() ? QStringLiteral( GAMMARAY_DEBUG_POSTFIX) : QString()); } ProbeABI ProbeABI::fromString(const QString &id) { QStringList idParts = id.split('-'); if (idParts.size() < 2) return ProbeABI(); int index = 0; ProbeABI abi; // version static QRegExp versionRegExp(R"(^qt(\d+)\_(\d+)$)"); if (versionRegExp.indexIn(idParts.value(index++)) != 0) return ProbeABI(); abi.setQtVersion(versionRegExp.cap(1).toInt(), versionRegExp.cap(2).toInt()); // compiler #ifdef Q_OS_WIN abi.setCompiler(idParts.value(index++)); if (abi.isVersionRelevant()) abi.setCompilerVersion(idParts.value(index++)); #endif if (idParts.size() != index + 1) return ProbeABI(); // architecture / debug/release const QString postfix = QStringLiteral(GAMMARAY_DEBUG_POSTFIX); QString arch = idParts.value(index); if (!postfix.isEmpty()) { if (arch.endsWith(postfix, Qt::CaseInsensitive)) { arch.chop(postfix.length()); if (abi.isDebugRelevant()) abi.setIsDebug(true); } } abi.setArchitecture(arch); return abi; } QString ProbeABI::displayString() const { if (!isValid()) return QString(); QStringList details; #ifdef Q_OS_WIN details.push_back(compiler()); if (isVersionRelevant()) details.push_back(compilerVersion()); #endif if (isDebugRelevant()) details.push_back(isDebug() ? ProbeABIContext::tr("debug") : ProbeABIContext::tr("release")); details.push_back(architecture()); return ProbeABIContext::tr("Qt %1.%2 (%3)") .arg(majorQtVersion()) .arg(minorQtVersion()) .arg(details.join(QStringLiteral(", "))); } bool ProbeABI::operator==(const ProbeABI &rhs) const { return majorQtVersion() == rhs.majorQtVersion() && minorQtVersion() == rhs.minorQtVersion() && architecture() == rhs.architecture() && compiler() == rhs.compiler() && compilerVersion() == rhs.compilerVersion() && isDebug() == rhs.isDebug(); } bool ProbeABI::operator<(const ProbeABI &rhs) const { if (majorQtVersion() == rhs.majorQtVersion()) return minorQtVersion() < rhs.minorQtVersion(); return majorQtVersion() < rhs.majorQtVersion(); } gammaray-2.11.3/launcher/core/probeabi.h000066400000000000000000000062711413310443000200600ustar00rootroot00000000000000/* probeabi.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_PROBEABI_H #define GAMMARAY_PROBEABI_H #include "gammaray_launcher_export.h" #include namespace GammaRay { class ProbeABIPrivate; /*! Describes a probe ABI. * The probe ABI is everything that determines if a probe is compatible * with a given target or not. */ class GAMMARAY_LAUNCHER_EXPORT ProbeABI { public: ProbeABI(); ProbeABI(const ProbeABI &other); ~ProbeABI(); ProbeABI &operator=(const ProbeABI &other); /*! Qt version. */ int majorQtVersion() const; int minorQtVersion() const; void setQtVersion(int major, int minor); bool hasQtVersion() const; /*! Processor architecture. */ QString architecture() const; void setArchitecture(const QString &architecture); /*! Debug vs. release. */ bool isDebug() const; void setIsDebug(bool debug); /*! Returns @c true if debug vs. release is changing the ABI. */ bool isDebugRelevant() const; /*! Compiler ABI is currently only relevant for MSVC vs. Mingw. */ QString compiler() const; void setCompiler(const QString &compiler); /*! The compiler ABI version is currently only relevant for MSVC */ QString compilerVersion() const; void setCompilerVersion(const QString &compilerVersion); /*! Returns @c true if the version is changing the ABI. */ bool isVersionRelevant() const; /*! Returns if this probe ABI is fully specified. */ bool isValid() const; /*! Checks if this is ABI is compatible with @p referenceABI. */ bool isCompatible(const ProbeABI &referenceABI) const; bool operator==(const ProbeABI &rhs) const; /*! @brief Orders probes by Qt version. * This is useful to pick the best matching one of multiple compatible ones. */ bool operator<(const ProbeABI &rhs) const; /*! Conversion from and to probe ABI identifiers. */ QString id() const; static ProbeABI fromString(const QString &id); /*! User-visible name of this ABI. */ QString displayString() const; private: QSharedDataPointer d; }; } #endif // GAMMARAY_PROBEABI_H gammaray-2.11.3/launcher/core/probeabidetector.cpp000066400000000000000000000117301413310443000221410ustar00rootroot00000000000000/* probeabidetector.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include "probeabidetector.h" #include #include #include using namespace GammaRay; ProbeABIDetector::ProbeABIDetector() = default; ProbeABI ProbeABIDetector::abiForExecutable(const QString &path) const { return abiForQtCore(qtCoreForExecutable(path)); } ProbeABI ProbeABIDetector::abiForProcess(qint64 pid) const { return abiForQtCore(qtCoreForProcess(pid)); } ProbeABI ProbeABIDetector::abiForQtCore(const QString &path) const { QFileInfo fi(path); if (!fi.exists()) return ProbeABI(); auto it = m_abiForQtCoreCache.constFind(fi.canonicalFilePath()); if (it != m_abiForQtCoreCache.constEnd()) return it.value(); const ProbeABI abi = detectAbiForQtCore(fi.canonicalFilePath()); m_abiForQtCoreCache.insert(fi.canonicalFilePath(), abi); return abi; } QString ProbeABIDetector::qtCoreFromLsof(qint64 pid) const { QString lsofExe; lsofExe = QStandardPaths::findExecutable(QStringLiteral("lsof")); // on OSX it's in sbin, which usually but not always is in PATH... if (lsofExe.isEmpty()) { lsofExe = QStandardPaths::findExecutable(QStringLiteral("lsof"), QStringList() << QStringLiteral( "/usr/sbin") << QStringLiteral("/sbin")); } if (lsofExe.isEmpty()) { lsofExe = QStringLiteral("lsof"); // maybe QProcess has more luck } QProcess proc; proc.setProcessChannelMode(QProcess::SeparateChannels); proc.setReadChannel(QProcess::StandardOutput); proc.start(lsofExe, QStringList() << QStringLiteral("-Fn") << QStringLiteral( "-n") << QStringLiteral("-p") << QString::number(pid)); proc.waitForFinished(); forever { const QByteArray line = proc.readLine(); if (line.isEmpty()) break; if (containsQtCore(line)) return QString::fromLocal8Bit(line.mid(1).trimmed()); // strip the field identifier } return QString(); } static bool checkQtCorePrefix(const QByteArray &line, int index) { Q_ASSERT(index >= 0); if (index == 0) return true; // we either have a "lib" prefix, or some sort of separator if (index >= 3 && line.indexOf("lib", index - 3) == index - 3) return true; if ((line.at(index - 1) >= 'a' && line.at(index - 1) <= 'z') || (line.at(index - 1) >= 'A' && line.at(index - 1) <= 'Z')) return false; return true; } static bool checkQtCoreSuffix(const QByteArray &line, int index) { if (index >= line.size()) return false; Q_ASSERT(line.at(index - 2) == 'Q' && line.at(index - 1) == 't'); // skip version numbers while (index < line.size() && line.at(index) >= '0' && line.at(index) <= '9') ++index; const auto core = QByteArrayLiteral("Core" QT_LIBINFIX); if (line.indexOf(core, index) != index) return false; // skip Core + QT_LIBINFIX index += core.size(); // deal with the "d" and "_debug" debug suffixes if (index < line.size() && line.at(index) == 'd') ++index; // "Core" must not be followed by another part of the name, so we don't trigger on eg. "QtCoreAddon" if (index < line.size() && ((line.at(index) >= 'a' && line.at(index) <= 'z') || (line.at(index) >= 'A' && line.at(index) <= 'Z'))) return false; return true; } bool ProbeABIDetector::containsQtCore(const QByteArray &line) { // Unix: libQt[X]Core.so[.X.Y.Z]$ // Mac: [^/]Qt[X]Core[_debug]$, [lib]Qt[X]Core[_debug].dylib[.X.Y.Z]$ // Windows Qt[X]Core[d].dll for (int index = 0; (index = line.indexOf("Qt", index)) >= 0; ++index) { if (!checkQtCorePrefix(line, index)) continue; if (checkQtCoreSuffix(line, index + 2)) return true; } return false; } gammaray-2.11.3/launcher/core/probeabidetector.h000066400000000000000000000055051413310443000216110ustar00rootroot00000000000000/* probeabidetector.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_PROBEABIDETECTOR_H #define GAMMARAY_PROBEABIDETECTOR_H #include "gammaray_launcher_export.h" #include "probeabi.h" #include #include namespace GammaRay { /*! Detect the probe ABI required for a given target. * A target can be specified as either a process id or a path to an executable to be launched. */ class GAMMARAY_LAUNCHER_EXPORT ProbeABIDetector { public: ProbeABIDetector(); /*! Detect the ABI of the executable at @p path. */ ProbeABI abiForExecutable(const QString &path) const; /*! Detect the ABI of the process running with PID @p pid. */ ProbeABI abiForProcess(qint64 pid) const; /*! Returns the full path to QtCore used by the given executable. */ QString qtCoreForExecutable(const QString &path) const; /*! Returns the full path to QtCore used by the process with PID @p pid. */ QString qtCoreForProcess(quint64 pid) const; ///@cond internal /*! Check if the given line contains a mention of the QtCore library. * @internal */ static bool containsQtCore(const QByteArray &line); ///@endcond private: /*! Returns the ABI of the given QtCore DLL. * Implementation of abiForXXX() should call this as it implements caching. */ ProbeABI abiForQtCore(const QString &path) const; /*! Detect the ABI of the given QtCore DLL. * This needs to be implemented for every platform. */ ProbeABI detectAbiForQtCore(const QString &path) const; /*! Path to the QtCore DLL for @p pid, using the lsof tool * on UNIX-like systems. */ QString qtCoreFromLsof(qint64 pid) const; mutable QHash m_abiForQtCoreCache; }; } #endif // GAMMARAY_PROBEABIDETECTOR_H gammaray-2.11.3/launcher/core/probeabidetector_dummy.cpp000066400000000000000000000031721413310443000233550ustar00rootroot00000000000000/* probeabidetector_dummy.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include "probeabidetector.h" #include "probeabi.h" #include using namespace GammaRay; QString ProbeABIDetector::qtCoreForExecutable(const QString &path) const { Q_UNUSED(path); return QString(); } QString ProbeABIDetector::qtCoreForProcess(quint64 pid) const { Q_UNUSED(pid); return QString(); } ProbeABI ProbeABIDetector::detectAbiForQtCore(const QString &path) const { Q_UNUSED(path); return ProbeABI::fromString(GAMMARAY_PROBE_ABI); } gammaray-2.11.3/launcher/core/probeabidetector_elf.cpp000066400000000000000000000127201413310443000227670ustar00rootroot00000000000000/* probeabidetector_elf.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include "probeabidetector.h" #include "probeabi.h" #include "libraryutil.h" #include #include #include #include #include #include #ifdef HAVE_ELF_H #include #endif // on Linux sys/elf.h is not what we want, on QNX we cannot add "sys" to the include dir without messing other stuff up... #if defined(HAVE_SYS_ELF_H) && !defined(HAVE_ELF_H) #include #endif using namespace GammaRay; static QString qtCoreFromLdd(const QString &path) { foreach (const auto &lib, LibraryUtil::dependencies(path)) { if (ProbeABIDetector::containsQtCore(lib)) return QString::fromLocal8Bit(lib); } return QString(); } QString ProbeABIDetector::qtCoreForExecutable(const QString &path) const { // TODO: add fast version reading the ELF file directly? return qtCoreFromLdd(path); } static bool qtCoreFromProc(qint64 pid, QString &path) { const QString mapsPath = QStringLiteral("/proc/%1/maps").arg(pid); QFile f(mapsPath); if (!f.open(QFile::ReadOnly)) { path.clear(); return false; } forever { const QByteArray line = f.readLine(); if (line.isEmpty()) break; if (ProbeABIDetector::containsQtCore(line)) { const int pos = line.indexOf('/'); if (pos <= 0) continue; path = QString::fromLocal8Bit(line.mid(pos).trimmed()); return true; } } path.clear(); return true; } QString ProbeABIDetector::qtCoreForProcess(quint64 pid) const { QString qtCorePath; if (!qtCoreFromProc(pid, qtCorePath)) qtCorePath = qtCoreFromLsof(pid); return qtCorePath; } static ProbeABI qtVersionFromFileName(const QString &path) { ProbeABI abi; const QStringList parts = path.split('.'); if (parts.size() < 4 || parts.at(parts.size() - 4) != QLatin1String("so")) return abi; abi.setQtVersion(parts.at(parts.size() - 3).toInt(), parts.at(parts.size() - 2).toInt()); return abi; } static ProbeABI qtVersionFromExec(const QString &path) { ProbeABI abi; // yep, you can actually execute QtCore.so... QProcess proc; proc.setReadChannelMode(QProcess::SeparateChannels); proc.setReadChannel(QProcess::StandardOutput); proc.start(path); proc.waitForFinished(); const QByteArray line = proc.readLine(); const int pos = line.indexOf("Qt "); const QList version = line.mid(pos + 2).split('.'); if (version.size() < 3) return abi; abi.setQtVersion(version.at(0).toInt(), version.at(1).toInt()); return abi; } #ifdef HAVE_ELF template static QString archFromELFHeader(const uchar *data, quint64 size) { if (size <= sizeof(ElfEHdr)) return QString(); const ElfEHdr *hdr = reinterpret_cast(data); switch (hdr->e_machine) { case EM_386: return QStringLiteral("i686"); #ifdef EM_X86_64 case EM_X86_64: return QStringLiteral("x86_64"); #endif case EM_ARM: return QStringLiteral("arm"); #ifdef EM_AARCH64 case EM_AARCH64: return QStringLiteral("aarch64"); #endif } qWarning() << "Unsupported ELF machine type:" << hdr->e_machine; return QString(); } #endif static QString archFromELF(const QString &path) { #ifdef HAVE_ELF QFile f(path); if (!f.open(QFile::ReadOnly)) return QString(); const uchar *data = f.map(0, f.size()); if (!data || f.size() < EI_NIDENT) return QString(); if (qstrncmp(reinterpret_cast(data), ELFMAG, SELFMAG) != 0) // no ELF signature return QString(); switch (data[EI_CLASS]) { case ELFCLASS32: return archFromELFHeader(data, f.size()); case ELFCLASS64: return archFromELFHeader(data, f.size()); } #else Q_UNUSED(path); #endif return QString(); } ProbeABI ProbeABIDetector::detectAbiForQtCore(const QString &path) const { if (path.isEmpty()) return ProbeABI(); // try to find the version ProbeABI abi = qtVersionFromFileName(path); if (!abi.hasQtVersion()) abi = qtVersionFromExec(path); // TODO: architecture detection fallback without elf.h? const QString arch = archFromELF(path); abi.setArchitecture(arch); return abi; } gammaray-2.11.3/launcher/core/probeabidetector_mac.cpp000066400000000000000000000172771413310443000227750ustar00rootroot00000000000000/* probeabidetector_mac.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ // krazy:excludeall=null since used by Darwin internals #include #include "probeabidetector.h" #include "probeabi.h" #include #include #include #include #include #include #include using namespace GammaRay; template static QString readMachOHeader(const uchar *data, quint64 size, quint32 &offset, qint32 &ncmds, qint32 &cmdsize); static QString resolveBundlePath(const QString &bundlePath) { const QFileInfo fi(bundlePath); if (!fi.isBundle()) return bundlePath; const QByteArray utf8Bundle = fi.absoluteFilePath().toUtf8(); CFURLRef bundleUrl = CFURLCreateFromFileSystemRepresentation(NULL, reinterpret_cast(utf8Bundle.data()), utf8Bundle.length(), true); CFBundleRef bundle = CFBundleCreate(NULL, bundleUrl); if (bundle) { CFURLRef url = CFBundleCopyExecutableURL(bundle); char executableFile[FILENAME_MAX]; CFURLGetFileSystemRepresentation(url, true, reinterpret_cast(executableFile), FILENAME_MAX); CFRelease(url); CFRelease(bundle); CFRelease(bundleUrl); return QString::fromUtf8(executableFile); } CFRelease(bundle); CFRelease(bundleUrl); return bundlePath; } static QString qtCoreFromOtool(const QString &path) { QProcess proc; proc.setProcessChannelMode(QProcess::SeparateChannels); proc.setReadChannel(QProcess::StandardOutput); proc.start("otool", QStringList() << "-L" << path); proc.waitForFinished(); forever { const QByteArray line = proc.readLine(); if (line.isEmpty()) break; if (ProbeABIDetector::containsQtCore(line)) { const int pos = line.lastIndexOf(" ("); if (pos <= 0) continue; return QString::fromLocal8Bit(line.left(pos).trimmed()); } } return QString(); } static QStringList readRPaths(const QString &path) { QStringList rpaths; QFile f(path); if (!f.open(QFile::ReadOnly)) return rpaths; auto size = f.size(); const uchar *data = f.map(0, size); if (!data || (uint)size <= sizeof(quint32)) return rpaths; quint32 offset = 0; qint32 ncmds = 0; qint32 cmdsize = 0; const quint32 magic = *reinterpret_cast(data); switch (magic) { case MH_MAGIC: readMachOHeader(data, size, offset, ncmds, cmdsize); break; case MH_MAGIC_64: readMachOHeader(data, size, offset, ncmds, cmdsize); break; } if (offset >= size || ncmds <= 0 || cmdsize <= 0 || size <= offset + cmdsize) return rpaths; // read load commands const auto pathBase = QFileInfo(path).absolutePath(); for (int i = 0; i < ncmds; ++i) { const load_command *cmd = reinterpret_cast(data + offset); if (cmd->cmd == LC_RPATH) { const rpath_command *rpcmd = reinterpret_cast(data + offset); auto rpath = QString::fromUtf8(reinterpret_cast(rpcmd) + rpcmd->path.offset); rpath.replace(QStringLiteral("@executable_path"), pathBase); rpath.replace(QStringLiteral("@loader_path"), pathBase); rpaths.push_back(rpath); } offset += cmd->cmdsize; } return rpaths; } static QString resolveRPath(const QString &path, const QStringList &rpaths) { for (const auto &rpath : rpaths) { auto resolvedPath = path; resolvedPath.replace("@rpath", rpath); if (QFile::exists(resolvedPath)) return resolvedPath; } return path; } QString ProbeABIDetector::qtCoreForExecutable(const QString &path) const { auto qtCorePath = qtCoreFromOtool(resolveBundlePath(path)); qtCorePath = resolveRPath(qtCorePath, readRPaths(path)); return qtCorePath; } QString ProbeABIDetector::qtCoreForProcess(quint64 pid) const { return qtCoreFromLsof(pid); } template static QString readMachOHeader(const uchar *data, quint64 size, quint32 &offset, qint32 &ncmds, qint32 &cmdsize) { if (size <= sizeof(T)) return QString(); const T *header = reinterpret_cast(data); offset = sizeof(T); ncmds = header->ncmds; cmdsize = header->sizeofcmds; switch (header->cputype) { case CPU_TYPE_I386: return "i686"; case CPU_TYPE_X86_64: return "x86_64"; } return QString(); } static ProbeABI abiFromMachO(const QString &path, const uchar *data, qint64 size) { ProbeABI abi; const quint32 magic = *reinterpret_cast(data); quint32 offset = 0; qint32 ncmds = 0; qint32 cmdsize = 0; switch (magic) { case MH_MAGIC: abi.setArchitecture(readMachOHeader(data, size, offset, ncmds, cmdsize)); break; case MH_MAGIC_64: abi.setArchitecture(readMachOHeader(data, size, offset, ncmds, cmdsize)); break; } if (offset >= size || ncmds <= 0 || cmdsize <= 0 || size <= offset + cmdsize) return ProbeABI(); // read load commands for (int i = 0; i < ncmds; ++i) { const load_command *cmd = reinterpret_cast(data + offset); if (cmd->cmd == LC_ID_DYLIB) { const dylib_command *dlcmd = reinterpret_cast(data + offset); const int majorVersion = (dlcmd->dylib.current_version & 0x00ff0000) >> 16; const int minorVersion = (dlcmd->dylib.current_version & 0x0000ff00) >> 8; abi.setQtVersion(majorVersion, minorVersion); } offset += cmd->cmdsize; } if (QFileInfo(path).baseName().endsWith(QStringLiteral("_debug"), Qt::CaseInsensitive)) { // We can probably also look for a S_ATTR_DEBUG segment, in the data, but that might not prove it's a debug // build as we can add debug symbols to release builds. abi.setIsDebug(true); } return abi; } ProbeABI ProbeABIDetector::detectAbiForQtCore(const QString &path) const { if (path.isEmpty()) return ProbeABI(); QFile f(path); if (!f.open(QFile::ReadOnly)) return ProbeABI(); const uchar *data = f.map(0, f.size()); if (!data || (uint)f.size() <= sizeof(quint32)) return ProbeABI(); return abiFromMachO(path, data, f.size()); } gammaray-2.11.3/launcher/core/probeabidetector_win.cpp000066400000000000000000000216771413310443000230310ustar00rootroot00000000000000/* probeabidetector_win.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include "probeabidetector.h" #include "probeabi.h" #include "pefile.h" #include #include #include #include #include #include #include #include #include #include #include using namespace GammaRay; // see https://msdn.microsoft.com/en-us/library/ms682586%28v=vs.85%29.aspx static QStringList dllSearchPaths(const QString &exePath) { QStringList paths; // (1) directory containing the executable QFileInfo fi(exePath); paths.push_back(fi.absolutePath()); /* We don't want use wchar because Qt on Windows with MSVC can be built optionally with or without /Zc:wchar_t. And we can't know which. https://stackoverflow.com/questions/4521252/qt-msvc-and-zcwchar-t-i-want-to-blow-up-the-world has a long discussion and options. We chose option3: don't use wchar. */ /* This code assumes UNICODE is defined */ // (2) system directory TCHAR syspath[256]; UINT len = GetSystemDirectoryW(syspath, sizeof(syspath)); Q_ASSERT(len <= sizeof(syspath) && len > 0); paths.push_back(QString::fromUtf16(reinterpret_cast(syspath), len)); // (3) windows directory TCHAR dirpath[256]; len = GetWindowsDirectoryW(dirpath, sizeof(dirpath)); Q_ASSERT(len <= sizeof(dirpath)); paths.push_back(QString::fromUtf16(reinterpret_cast(dirpath), len)); // (4) current working dir paths.push_back(QDir::currentPath()); // (5) PATH const auto envPaths = QString::fromLocal8Bit(qgetenv("PATH")); paths += envPaths.split(';'); return paths; } /** Resolves imports given a list of search paths. */ static QString resolveImport(const QString &import, const QStringList &searchPaths, const QString &arch) { for (const auto &path : searchPaths) { const QString absPath = path + '/' + import; if (!QFile::exists(absPath)) { continue; } PEFile f(absPath); if (!f.isValid() || f.architecture() != arch) { continue; } return absPath; } qDebug() << "Could not resolve import" << import << "in" << searchPaths; return QString(); } struct Version { Version(int major, int minor) : major(major), minor(minor) {} int major; int minor; }; static Version fileVersion(const QString &path) { // version DWORD pointlessHandle; DWORD fileVersionInfoSize = GetFileVersionInfoSize( reinterpret_cast(path.utf16()), &pointlessHandle); if (fileVersionInfoSize) { QScopedArrayPointer buffer(new BYTE[fileVersionInfoSize]); if (GetFileVersionInfoW(reinterpret_cast(path.utf16()), pointlessHandle, fileVersionInfoSize, buffer.data())) { void *versionInfoData; unsigned int versionInfoSize; if (VerQueryValue(buffer.data(), TEXT("\\"), &versionInfoData, &versionInfoSize) && versionInfoSize) { VS_FIXEDFILEINFO *versionInfo = reinterpret_cast(versionInfoData); if (versionInfo->dwSignature == VS_FFI_SIGNATURE) return Version(versionInfo->dwFileVersionMS >> 16, versionInfo->dwFileVersionMS & 0xFFFF); } } } return Version(-1, -1); } QString absoluteExecutablePath(const QString &path) { if (QFile::exists(path)) { return path; } // see if Qt can find the executable (this can still fail for relative paths without extension) const auto searchedPath = QStandardPaths::findExecutable(path); if (!searchedPath.isEmpty()) { return searchedPath; } // attempt to appends missing .exe extensions const auto pathExt = QString::fromLocal8Bit(qgetenv("PATHEXT")).toLower().split(QLatin1Char(';')); for (const auto &ext : pathExt) { const auto extendedPath = path + ext; if (QFile::exists(extendedPath)) { return extendedPath; } } return path; } QString ProbeABIDetector::qtCoreForExecutable(const QString &path) const { const auto exe = absoluteExecutablePath(path); const auto searchPaths = dllSearchPaths(exe); QStringList resolvedImports = QStringList(exe); QSet checkedImports; while (!resolvedImports.isEmpty()) { for (const auto &import : qAsConst(resolvedImports)) { if (containsQtCore(import.toUtf8())) return import; } QStringList resolvedSubImports; for (const auto &import : qAsConst(resolvedImports)) { PEFile f(import); if (!f.isValid()) continue; foreach (const auto &import, f.imports()) { const auto absPath = resolveImport(import, searchPaths, f.architecture()); if (!absPath.isEmpty() && !checkedImports.contains(import)) { resolvedSubImports.push_back(absPath); } checkedImports.insert(import); } } resolvedImports = resolvedSubImports; } return QString(); } QString ProbeABIDetector::qtCoreForProcess(quint64 pid) const { MODULEENTRY32 me; me.dwSize = sizeof(MODULEENTRY32); HANDLE snapshot = INVALID_HANDLE_VALUE; // https://msdn.microsoft.com/en-us/library/windows/desktop/ms682489%28v=vs.85%29.aspx // If the function fails with ERROR_BAD_LENGTH, retry the function until it succeeds. do { snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, pid); } while(GetLastError() == ERROR_BAD_LENGTH); if (GetLastError() == ERROR_ACCESS_DENIED) { return QString(); } WIN_ERROR_ASSERT(snapshot != INVALID_HANDLE_VALUE, return QString()); for (bool hasNext = Module32First(snapshot, &me); hasNext; hasNext = Module32Next(snapshot, &me)) { const QString module = QString::fromUtf16(reinterpret_cast(me.szModule)); if (containsQtCore(module.toUtf8())) { const QString path = QString::fromUtf16(reinterpret_cast(me.szExePath)); CloseHandle(snapshot); return path; } } CloseHandle(snapshot); return QString(); } static QString compilerFromLibraries(const QStringList &libraries) { for (const QString &lib : libraries) { if (lib.startsWith(QLatin1String("libgcc"), Qt::CaseInsensitive)) return "GNU"; } return "MSVC"; } static QString compilerVersionFromLibraries(const QStringList &libraries) { for (const QString &lib : libraries) { if (lib.startsWith(QLatin1String("msvcp"), Qt::CaseInsensitive) || lib.startsWith(QLatin1String("vcruntime"), Qt::CaseInsensitive)) { return QString::number(fileVersion(lib).major * 10); } } return QString(); } static bool isDebugBuild(const QString &qtCoreDll) { return qtCoreDll.endsWith(QLatin1String("d.dll"), Qt::CaseInsensitive); } ProbeABI ProbeABIDetector::detectAbiForQtCore(const QString &path) const { ProbeABI abi; if (path.isEmpty()) return abi; Version version = fileVersion(path); if (version.major == -1) return abi; abi.setQtVersion(version.major, version.minor); // architecture and dependent libraries PEFile f(path); if (!f.isValid()) return ProbeABI(); // architecture abi.setArchitecture(f.architecture()); if (abi.architecture().isEmpty()) return ProbeABI(); // compiler and debug mode QStringList libs = f.imports(); abi.setCompiler(compilerFromLibraries(libs)); abi.setIsDebug(isDebugBuild(path)); if (abi.compiler() == "MSVC") { abi.setCompilerVersion(compilerVersionFromLibraries(libs)); } return abi; } gammaray-2.11.3/launcher/core/probefinder.cpp000066400000000000000000000124001413310443000211160ustar00rootroot00000000000000/* probefinder.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include "probefinder.h" #include "probeabi.h" #include #include #include #include #include #include #include #include namespace GammaRay { namespace ProbeFinder { static QString findProbeInternalNormalLayout(const ProbeABI &abi, const QString &rootPath) { #ifndef GAMMARAY_INSTALL_QT_LAYOUT const QString path = rootPath + QLatin1String("/" GAMMARAY_PLUGIN_INSTALL_DIR "/" GAMMARAY_PLUGIN_VERSION "/") + abi.id(); const QString wildcard = QLatin1String(GAMMARAY_PROBE_BASENAME "*") + Paths::libraryExtension(); const auto fi = QDir(path).entryInfoList(QStringList(wildcard)).value(0); if (fi.isFile() && fi.isReadable()) return fi.canonicalFilePath(); #else Q_UNUSED(abi); Q_UNUSED(rootPath); #endif return QString(); } static QString findProbeInternalQtLayout(const ProbeABI &abi, const QString &rootPath) { #ifndef GAMMARAY_INSTALL_QT_LAYOUT #ifdef Q_OS_WIN const QString path = rootPath + QLatin1String("/bin/" GAMMARAY_PROBE_BASENAME "-") + abi.id() + Paths::libraryExtension(); #else const QString path = rootPath + QLatin1String("/lib/libgammaray_probe-") + abi.id() + Paths::libraryExtension(); #endif #else const QString path = rootPath + QLatin1String("/" GAMMARAY_PROBE_INSTALL_DIR "/" GAMMARAY_PROBE_BASENAME "-") + abi.id() + Paths::libraryExtension(); #endif const auto fi = QFileInfo(path); if (fi.isFile() && fi.isReadable()) return fi.canonicalFilePath(); return QString(); } static QString findProbeInternal(const ProbeABI &probeAbi, const QString &rootPath) { #ifndef GAMMARAY_INSTALL_QT_LAYOUT const auto probe = findProbeInternalNormalLayout(probeAbi, rootPath); if (!probe.isEmpty()) return probe; return findProbeInternalQtLayout(probeAbi, rootPath); #else const auto probe = findProbeInternalQtLayout(probeAbi, rootPath); if (!probe.isEmpty()) return probe; return findProbeInternalNormalLayout(probeAbi, rootPath); #endif } QString findProbe(const ProbeABI &probeAbi, const QStringList &searchRoots) { for (const auto &searchRoot : searchRoots) { const auto path = findProbeInternal(probeAbi, searchRoot); if (!path.isEmpty()) return path; } const auto path = findProbeInternal(probeAbi, Paths::rootPath()); if (!path.isEmpty()) return path; qWarning() << "Cannot locate probe for ABI" << probeAbi.displayString() << " in " << searchRoots << Paths::rootPath(); qWarning() << "This is likely a setup problem, due to an incomplete or partially moved installation."; return QString(); } ProbeABI findBestMatchingABI(const ProbeABI &targetABI) { return findBestMatchingABI(targetABI, listProbeABIs()); } ProbeABI findBestMatchingABI(const ProbeABI &targetABI, const QVector &availableABIs) { QVector compatABIs; for (const ProbeABI &abi : availableABIs) { if (targetABI.isCompatible(abi)) compatABIs.push_back(abi); } if (compatABIs.isEmpty()) return ProbeABI(); std::sort(compatABIs.begin(), compatABIs.end()); return compatABIs.last(); } QVector listProbeABIs() { QVector abis; const QDir dir(Paths::probePath(QString())); #if defined(GAMMARAY_INSTALL_QT_LAYOUT) const QString filter = QStringLiteral("*gammaray_probe*"); foreach (const QFileInfo &abiId, dir.entryInfoList(QStringList(filter), QDir::Files)) { // OSX has broken QLibrary::isLibrary() - QTBUG-50446 if (!QLibrary::isLibrary(abiId.fileName()) && !abiId.fileName().endsWith(Paths::libraryExtension(), Qt::CaseInsensitive)) continue; const ProbeABI abi = ProbeABI::fromString(abiId.baseName().section(QStringLiteral("-"), 1)); if (abi.isValid()) abis.push_back(abi); } #else foreach (const QString &abiId, dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) { const ProbeABI abi = ProbeABI::fromString(abiId); if (abi.isValid()) abis.push_back(abi); } #endif return abis; } } } gammaray-2.11.3/launcher/core/probefinder.h000066400000000000000000000047061413310443000205750ustar00rootroot00000000000000/* probefinder.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_PROBEFINDER_H #define GAMMARAY_PROBEFINDER_H #include "gammaray_launcher_export.h" #include #include QT_BEGIN_NAMESPACE class QString; QT_END_NAMESPACE namespace GammaRay { class ProbeABI; /*! Functions to locate a suitable probe. */ namespace ProbeFinder { /*! * Attempts to find the full path of the probe DLL with ABI @p probeAbi, considering * the additional search paths @p searchRoots. */ GAMMARAY_LAUNCHER_EXPORT QString findProbe(const ProbeABI &probeAbi, const QStringList &searchRoots = QStringList()); /*! * Find the best matching probe for the given @p targetABI out of all installed ones. * An invalid ProbeABI instance is returned if there is no compatible probe installed. */ GAMMARAY_LAUNCHER_EXPORT ProbeABI findBestMatchingABI(const ProbeABI &targetABI); /*! * Find the best matching probe for the given @p targetABI out of @p availableABIs. * An invalid ProbeABI instance is returned if there is no compatible probe installed. */ GAMMARAY_LAUNCHER_EXPORT ProbeABI findBestMatchingABI(const ProbeABI &targetABI, const QVector &availableABIs); /*! * List all available probe ABIs. */ GAMMARAY_LAUNCHER_EXPORT QVector listProbeABIs(); } } #endif // GAMMARAY_PROBEFINDER_H gammaray-2.11.3/launcher/core/selftest.cpp000066400000000000000000000066031413310443000204600ustar00rootroot00000000000000/* selftest.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "selftest.h" #include "probeabi.h" #include "probefinder.h" #include "injector/injectorfactory.h" #include using namespace GammaRay; SelfTest::SelfTest(QObject *parent) : QObject(parent) { } SelfTest::~SelfTest() = default; bool SelfTest::checkEverything() { auto result = checkProbes(); result |= checkInjectors(); return result; } bool SelfTest::checkProbes() { int validProbeCount = 0; const QVector probeABIs = ProbeFinder::listProbeABIs(); for (const ProbeABI &abi : probeABIs) { const QString probePath = ProbeFinder::findProbe(abi); if (probePath.isEmpty()) { emit error(tr("No probe found for ABI %1.").arg(abi.id())); continue; } QFileInfo fi(probePath); if (!fi.exists() || !fi.isFile() || !fi.isReadable()) { emit error(tr("Probe at %1 is invalid.")); continue; } // TODO: we could do some more elaborate check for plugins etc here? emit information(tr("Found valid probe for ABI %1 at %2.").arg(abi.id(), probePath)); ++validProbeCount; } if (validProbeCount == 0) emit error(tr("No probes found - GammaRay not functional.")); return validProbeCount; } bool SelfTest::checkInjectors() { const auto injectors = InjectorFactory::availableInjectors(); int viableInjectors = 0; for (const auto &injector : injectors) { if (checkInjector(injector)) ++viableInjectors; } if (!viableInjectors) { emit error(tr("No injectors available - GammaRay not functional.")); return false; } return true; } bool SelfTest::checkInjector(const QString &injectorType) { AbstractInjector::Ptr injector = InjectorFactory::createInjector(injectorType); if (!injector) { emit error(tr("Unable to create instance of injector %1.").arg(injectorType)); return false; } if (!injector->selfTest()) { emit error(tr("Injector %1 failed to pass its self-test: %2.").arg(injectorType, injector->errorString())); return false; } emit information(tr("Injector %1 successfully passed its self-test.").arg(injectorType)); return true; } gammaray-2.11.3/launcher/core/selftest.h000066400000000000000000000036331413310443000201250ustar00rootroot00000000000000/* selftest.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_SELFTEST_H #define GAMMARAY_SELFTEST_H #include "gammaray_launcher_export.h" #include namespace GammaRay { /** @brief Self-test functions for probes and injectors. */ class GAMMARAY_LAUNCHER_EXPORT SelfTest : public QObject { Q_OBJECT public: explicit SelfTest(QObject *parent = nullptr); ~SelfTest() override; /** Run all available tests. */ bool checkEverything(); /** Run tests for one specific injector only. */ bool checkInjector(const QString &injectorType); signals: /** Emitted for informational messages, ie. passing tests. */ void information(const QString &msg); /** Emitted for failed tests. */ void error(const QString &msg); private: bool checkProbes(); bool checkInjectors(); }; } #endif // GAMMARAY_SELFTEST_H gammaray-2.11.3/launcher/ui/000077500000000000000000000000001413310443000156035ustar00rootroot00000000000000gammaray-2.11.3/launcher/ui/CMakeLists.txt000066400000000000000000000034301413310443000203430ustar00rootroot00000000000000set(gammaray_launcher_ui_srcs promolabel.cpp launcherwindow.cpp launchpage.cpp selftestpage.cpp connectpage.cpp attachdialog.cpp processmodel.cpp processfiltermodel.cpp probeabimodel.cpp resources/ui.qrc ) if(NOT WIN32) list(APPEND gammaray_launcher_ui_srcs processlist_unix.cpp) else() list(APPEND gammaray_launcher_ui_srcs processlist_win.cpp) endif() add_library(gammaray_launcher_ui SHARED ${gammaray_launcher_ui_srcs}) generate_export_header(gammaray_launcher_ui) set_target_properties(gammaray_launcher_ui PROPERTIES ${GAMMARAY_DEFAULT_LIBRARY_PROPERTIES}) gammaray_set_rpath(gammaray_launcher_ui ${LIB_INSTALL_DIR}) target_compile_features(gammaray_launcher_ui PUBLIC ${GAMMARAY_REQUIRED_CXX_FEATURES}) target_include_directories(gammaray_launcher_ui PUBLIC $) target_link_libraries(gammaray_launcher_ui LINK_PUBLIC gammaray_launcher LINK_PRIVATE Qt5::Core Qt5::Concurrent Qt5::Gui Qt5::Widgets Qt5::Network gammaray_ui ) install(TARGETS gammaray_launcher_ui EXPORT GammaRayTargets ${INSTALL_TARGETS_DEFAULT_ARGS}) gammaray_install_headers(DESTINATION launcher ${CMAKE_CURRENT_BINARY_DIR}/gammaray_launcher_ui_export.h connectpage.h launcherwindow.h attachdialog.h ) ecm_generate_pri_file(BASE_NAME GammaRayLauncherUi LIB_NAME gammaray_launcher_ui DEPS "core gui GammaRayLauncher" FILENAME_VAR PRI_FILENAME INCLUDE_INSTALL_DIR ${INCLUDE_INSTALL_DIR} ) install(FILES ${PRI_FILENAME} DESTINATION ${ECM_MKSPECS_INSTALL_DIR}) if(MSVC) install(FILES "$/$" DESTINATION ${BIN_INSTALL_DIR} CONFIGURATIONS Debug RelWithDebInfo) endif() gammaray-2.11.3/launcher/ui/attachdialog.cpp000066400000000000000000000141611413310443000207360ustar00rootroot00000000000000/* attachdialog.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include "attachdialog.h" #include "processfiltermodel.h" #include "processmodel.h" #include "probeabimodel.h" #include "processlist.h" #include "ui_attachdialog.h" #include #include #include #include #include #include #include #include #include #include #include using namespace GammaRay; AttachDialog::AttachDialog(QWidget *parent, Qt::WindowFlags f) : QWidget(parent, f) , ui(new Ui::AttachDialog) , m_abiModel(new ProbeABIModel(this)) { ui->setupUi(this); #if defined(Q_OS_MAC) QMargins margins = ui->formLayout->contentsMargins(); margins.setRight(margins.right() +2); margins.setBottom(margins.bottom() +2); ui->formLayout->setContentsMargins(margins); #endif m_model = new ProcessModel(this); m_proxyModel = new ProcessFilterModel(this); m_proxyModel->setSourceModel(m_model); m_proxyModel->setDynamicSortFilter(true); ui->view->setModel(m_proxyModel); // hide state ui->view->hideColumn(ProcessModel::StateColumn); ui->view->sortByColumn(ProcessModel::NameColumn, Qt::AscendingOrder); ui->view->setSortingEnabled(true); ui->view->setEditTriggers(QAbstractItemView::NoEditTriggers); ui->view->setSelectionBehavior(QAbstractItemView::SelectRows); ui->view->setSelectionMode(QAbstractItemView::SingleSelection); connect(ui->view->selectionModel(), &QItemSelectionModel::currentRowChanged, this, &AttachDialog::updateButtonState); connect(ui->view->selectionModel(), &QItemSelectionModel::currentChanged, this, &AttachDialog::selectABI); connect(ui->view, &QAbstractItemView::activated, this, &AttachDialog::activate); new SearchLineController(ui->filter, m_proxyModel); ui->probeBox->setModel(m_abiModel); QSettings settings; ui->accessMode->setCurrentIndex(settings.value(QStringLiteral( "Launcher/AttachAccessMode")).toInt()); setWindowTitle(tr("GammaRay - Attach to Process")); setWindowIcon(QIcon(QStringLiteral(":/gammaray/GammaRay-128x128.png"))); ui->stackedWidget->setCurrentWidget(ui->loadingLabel); updateProcesses(); } AttachDialog::~AttachDialog() = default; bool AttachDialog::isValid() const { return ui->view->currentIndex().isValid(); } void AttachDialog::writeSettings() { QSettings settings; settings.setValue(QStringLiteral("Launcher/AttachAccessMode"), ui->accessMode->currentIndex()); } void AttachDialog::setSettingsVisible(bool visible) { ui->settingsWidget->setVisible(visible); } LaunchOptions AttachDialog::launchOptions() const { LaunchOptions opt; opt.setPid(pid()); opt.setProbeABI(ui->probeBox->itemData(ui->probeBox->currentIndex()).value()); switch (ui->accessMode->currentIndex()) { case 0: // local, out-of-process opt.setProbeSetting(QStringLiteral("RemoteAccessEnabled"), true); opt.setProbeSetting(QStringLiteral("ServerAddress"), GAMMARAY_DEFAULT_LOCAL_TCP_URL); opt.setUiMode(LaunchOptions::OutOfProcessUi); break; case 1: // remote, out-of-process opt.setProbeSetting(QStringLiteral("RemoteAccessEnabled"), true); opt.setProbeSetting(QStringLiteral("ServerAddress"), GAMMARAY_DEFAULT_ANY_TCP_URL); opt.setUiMode(LaunchOptions::OutOfProcessUi); break; case 2: // in-process opt.setProbeSetting(QStringLiteral("RemoteAccessEnabled"), false); opt.setUiMode(LaunchOptions::InProcessUi); break; } return opt; } int AttachDialog::pid() const { return ui->view->currentIndex().data(ProcessModel::PIDRole).toInt(); } QString GammaRay::AttachDialog::absoluteExecutablePath() const { return ui->view->currentIndex().data(ProcessModel::NameRole).toString(); } void AttachDialog::updateProcesses() { auto *watcher = new QFutureWatcher(this); connect(watcher, &QFutureWatcherBase::finished, this, &AttachDialog::updateProcessesFinished); watcher->setFuture(QtConcurrent::run(processList, m_model->processes())); } void AttachDialog::updateProcessesFinished() { QFutureWatcher *watcher = dynamic_cast *>(sender()); Q_ASSERT(watcher); ui->stackedWidget->setCurrentWidget(ui->listViewPage); const int oldPid = pid(); m_model->mergeProcesses(watcher->result()); if (oldPid != pid()) ui->view->setCurrentIndex(QModelIndex()); watcher->deleteLater(); QTimer::singleShot(1000, this, &AttachDialog::updateProcesses); } void AttachDialog::selectABI(const QModelIndex &processIndex) { if (!processIndex.isValid()) return; const ProbeABI abi = processIndex.data(ProcessModel::ABIRole).value(); const int abiIndex = m_abiModel->indexOfBestMatchingABI(abi); if (abiIndex >= 0) ui->probeBox->setCurrentIndex(abiIndex); } gammaray-2.11.3/launcher/ui/attachdialog.h000066400000000000000000000045601413310443000204050ustar00rootroot00000000000000/* attachdialog.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_ATTACHDIALOG_H #define GAMMARAY_ATTACHDIALOG_H #include #include #include "gammaray_launcher_ui_export.h" QT_BEGIN_NAMESPACE class QModelIndex; QT_END_NAMESPACE namespace GammaRay { class LaunchOptions; class ProcessModel; class ProcessFilterModel; class ProbeABIModel; namespace Ui { class AttachDialog; } /*! Widget for selecting a process to attach GammaRay to. */ class GAMMARAY_LAUNCHER_UI_EXPORT AttachDialog : public QWidget { Q_OBJECT public: explicit AttachDialog(QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); ~AttachDialog() override; LaunchOptions launchOptions() const; int pid() const; QString absoluteExecutablePath() const; /// Returns @c true if a valid process is selected. bool isValid() const; void writeSettings(); void setSettingsVisible(bool visible); signals: void updateButtonState(); void activate(); private slots: void updateProcesses(); void updateProcessesFinished(); void selectABI(const QModelIndex &processIndex); private: std::unique_ptr ui; ProcessModel *m_model; ProcessFilterModel *m_proxyModel; ProbeABIModel *m_abiModel; }; } // namespace GammaRay #endif // ATTACHDIALOG_H gammaray-2.11.3/launcher/ui/attachdialog.ui000066400000000000000000000101351413310443000205660ustar00rootroot00000000000000 GammaRay::AttachDialog 0 0 400 323 :/gammaray/GammaRay-128x128.png:/gammaray/GammaRay-128x128.png 1 <html><head/><body><p align="center"><img src=":/gammaray/GammaRay-48x48.png"/></p><p align="center">Retrieving the list of processes which use Qt ...</p></body></html> 0 0 0 0 false 0 0 0 Pro&be: probeBox &Access mode: accessMode 0 Out-of-process, local debugging only Out-of-process, remote debugging enabled In-process listViewPage loadingLabel gammaray-2.11.3/launcher/ui/connectpage.cpp000066400000000000000000000224771413310443000206110ustar00rootroot00000000000000/* connectpage.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "connectpage.h" #include "ui_connectpage.h" #include #include #include #include #include #include #include #include #ifdef Q_OS_LINUX #include #endif using namespace GammaRay; ConnectPage::ConnectPage(QWidget *parent) : QWidget(parent) , ui(new Ui::ConnectPage) , m_valid(false) { ui->setupUi(this); m_implicitPortWarningSign = new QAction(QIcon(":/launcher/warning.png"), tr("No port given, will use 11732"), this); m_fileIsNotASocketWarning = new QAction(qApp->style()->standardIcon(QStyle::SP_MessageBoxCritical), tr("File is not a socket"), this); connect(ui->host, &QLineEdit::textChanged, this, &ConnectPage::validateHostAddress); connect(ui->host, &QLineEdit::textChanged, this, &ConnectPage::updateButtonState); auto *model = new NetworkDiscoveryModel(this); ui->instanceView->setModel(model); connect(ui->instanceView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &ConnectPage::instanceSelected); connect(ui->instanceView, &QAbstractItemView::activated, this, &ConnectPage::activate); QSettings settings; ui->host->setText(settings.value(QStringLiteral("Connect/Url"), QString()).toString()); } const QString ConnectPage::localPrefix = QStringLiteral("local://"); const QString ConnectPage::tcpPrefix = QStringLiteral("tcp://"); void ConnectPage::validateHostAddress(const QString &address) { QString stillToParse = address; m_valid = false; bool correctSoFar = false; m_currentUrl.clear(); // Initially, set the text to red QPalette errorPalette; errorPalette.setColor(QPalette::Text, Qt::red); ui->host->setPalette(errorPalette); clearWarnings(); handleLocalAddress(stillToParse, correctSoFar); handleIPAddress(stillToParse, correctSoFar); QRegExp hostNameFormat("^([a-zA-Z][a-zA-Z0-9\\-\\.]+[a-zA-Z0-9](:[0-9]{1,5})?)$"); if (hostNameFormat.exactMatch(stillToParse)) handleHostName(stillToParse); // if we came down here and there's nothing more to parse, we are good // set text back to black again if (correctSoFar && stillToParse.isEmpty()) { m_valid = true; ui->host->setPalette(this->style()->standardPalette()); } emit userInputParsed(); } void ConnectPage::handleLocalAddress(QString &stillToParse, bool &correctSoFar) { #ifdef Q_OS_UNIX if (stillToParse.startsWith(localPrefix)) stillToParse.remove(localPrefix); //don't remove second slash // It's okay if only a path to an existing file is given QFileInfo localSocketFile(stillToParse); if (localSocketFile.exists() && !localSocketFile.isDir() && !localSocketFile.isSymLink()) { QT_STATBUF statbuf; if (QT_STAT(QFile::encodeName(localSocketFile.filePath()), &statbuf) == 0) { if(!S_ISSOCK(statbuf.st_mode)) { showFileIsNotSocketWarning(); } else { stillToParse = ""; correctSoFar = true; m_currentUrl.setScheme("local"); m_currentUrl.setPath(localSocketFile.filePath()); } } } #else Q_UNUSED(stillToParse); Q_UNUSED(correctSoFar); #endif } void ConnectPage::handleIPAddress(QString &stillToParse, bool &correctSoFar) { // handle tcp prefix if (stillToParse.startsWith(tcpPrefix)) stillToParse.remove(0, tcpPrefix.size()); // Speculate on the address format const auto possibleIPv4Address = QHostAddress(stillToParse.split(QLatin1Char(':')).first()); QHostAddress possibleIPv6Address; if (!stillToParse.contains(QLatin1Char('%'))) possibleIPv6Address = QHostAddress(stillToParse); QHostAddress possibleIPv6BracketAddress; QRegExp bracketFormat(R"(^\[([0-9a-f\:\.]*)\].*$)"); if (bracketFormat.exactMatch(stillToParse)) possibleIPv6BracketAddress = QHostAddress(bracketFormat.cap(1)); QHostAddress possibleIPv6InterfaceAddress; QRegExp interfaceFormat(R"(^([^\%]*)(\%[^\:]+)(:[0-9]+)?$)"); if (interfaceFormat.exactMatch(stillToParse)) possibleIPv6InterfaceAddress = QHostAddress(interfaceFormat.cap(1)); const auto skipPort = true; if (!possibleIPv4Address.isNull()) { handleAddressAndPort(stillToParse, correctSoFar, possibleIPv4Address.toString()); } if (!possibleIPv6Address.isNull()) { handleAddressAndPort(stillToParse, correctSoFar, possibleIPv6Address.toString(), skipPort); } if (!possibleIPv6BracketAddress.isNull()) { handleAddressAndPort(stillToParse, correctSoFar, QLatin1Char('[') + possibleIPv6BracketAddress.toString() + QLatin1Char(']')); } if (!possibleIPv6InterfaceAddress.isNull()){ stillToParse.replace(interfaceFormat.cap(2), QString()); handleAddressAndPort(stillToParse, correctSoFar, possibleIPv6InterfaceAddress.toString()); } } void ConnectPage::handleHostName(QString &stillToParse) { // handle tcp prefix if (stillToParse.startsWith(tcpPrefix)) { stillToParse.remove(0, tcpPrefix.size()); } m_currentUrl.setScheme("tcp"); // cut off port first and handle port auto portStart = stillToParse.indexOf(QLatin1Char(':')); bool portCorrectSoFar = true; if (portStart > -1) { auto portString = stillToParse.right(portStart); handlePortString(portString, portCorrectSoFar); stillToParse = stillToParse.left(portStart); } else { showStandardPortAssumedWarning(); } // don't do lookup if port was wrong if (!portCorrectSoFar) return; QHostInfo::lookupHost(stillToParse, this, SLOT(hostResponse(QHostInfo))); } void ConnectPage::hostResponse(const QHostInfo &hostInfo) { if (hostInfo.error() != QHostInfo::NoError) return; if(hostInfo.addresses().empty()) return; m_currentUrl.setHost(hostInfo.hostName()); m_valid = true; ui->host->setPalette(this->style()->standardPalette()); emit dnsResolved(); emit updateButtonState(); } void ConnectPage::handleAddressAndPort(QString &stillToParse, bool &correctSoFar, const QString &possibleAddress, bool skipPort) { stillToParse.replace(possibleAddress, QString()); if (stillToParse.isEmpty()) { correctSoFar = true; m_currentUrl.setScheme("tcp"); m_currentUrl.setHost(possibleAddress); m_currentUrl.setPort(Endpoint::defaultPort()); showStandardPortAssumedWarning(); } else if(!skipPort) { m_currentUrl.setScheme("tcp"); m_currentUrl.setHost(possibleAddress); handlePortString(stillToParse, correctSoFar); } } void ConnectPage::handlePortString(QString &stillToParse, bool &correctSoFar) { QRegExp r("\\:[0-9]{1,5}"); if (r.exactMatch(stillToParse)) { auto portString = r.cap(0); stillToParse = stillToParse.replace(portString, QString()); auto portNumber = portString.replace(QLatin1Char(':'), QString()).toInt(); if (portNumber <= 65535){ m_currentUrl.setPort(portNumber); correctSoFar = true; } } } void ConnectPage::showStandardPortAssumedWarning() { ui->host->addAction(m_implicitPortWarningSign, QLineEdit::TrailingPosition); } void ConnectPage::showFileIsNotSocketWarning() { ui->host->addAction(m_fileIsNotASocketWarning, QLineEdit::TrailingPosition); } void ConnectPage::clearWarnings() { ui->host->removeAction(m_implicitPortWarningSign); ui->host->removeAction(m_fileIsNotASocketWarning); } QUrl ConnectPage::currentUrl() const { return m_currentUrl; } ConnectPage::~ConnectPage() = default; bool ConnectPage::isValid() const { return m_valid; } void ConnectPage::launchClient() { ClientLauncher::launchDetached(m_currentUrl); } void ConnectPage::writeSettings() { QSettings settings; settings.setValue(QStringLiteral("Connect/Url"), ui->host->text()); } void ConnectPage::instanceSelected() { const QModelIndexList rows = ui->instanceView->selectionModel()->selectedRows(); if (rows.size() != 1) return; ui->host->setText(rows.first().data(NetworkDiscoveryModel::UrlStringRole).toString()); } gammaray-2.11.3/launcher/ui/connectpage.h000066400000000000000000000054521413310443000202500ustar00rootroot00000000000000/* connectpage.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_CONNECTPAGE_H #define GAMMARAY_CONNECTPAGE_H #include "gammaray_launcher_ui_export.h" #include #include #include #include #include class LauncherUiIPTest; namespace GammaRay { namespace Ui { class ConnectPage; } /*! UI for connecting to a running GammaRay instance. */ class GAMMARAY_LAUNCHER_UI_EXPORT ConnectPage : public QWidget { Q_OBJECT friend class ::LauncherUiIPTest; public: explicit ConnectPage(QWidget *parent = nullptr); ~ConnectPage() override; bool isValid() const; void writeSettings(); QUrl currentUrl() const; public slots: void launchClient(); signals: void userInputParsed(); void dnsResolved(); void updateButtonState(); void activate(); private slots: void instanceSelected(); void hostResponse(const QHostInfo &hostInfo); void validateHostAddress(const QString &address); private: void handleLocalAddress(QString &stillToParse, bool &correctSoFar); void handleIPAddress(QString &stillToParse, bool &correctSoFar); void handleHostName(QString &stillToParse); void handleAddressAndPort(QString &stillToParse, bool &correctSoFar, const QString &possibleAddress, bool skipPort = false); void handlePortString(QString &stillToParse, bool &correctSoFar); void showStandardPortAssumedWarning(); void showFileIsNotSocketWarning(); void clearWarnings(); static const QString localPrefix; static const QString tcpPrefix; QScopedPointer ui; QUrl m_currentUrl; bool m_valid; QAction *m_implicitPortWarningSign; QAction *m_fileIsNotASocketWarning; }; } #endif // GAMMARAY_CONNECTPAGE_H gammaray-2.11.3/launcher/ui/connectpage.ui000066400000000000000000000023321413310443000204300ustar00rootroot00000000000000 GammaRay::ConnectPage 0 0 400 300 Connect to a running remote instance. Url: host false true gammaray-2.11.3/launcher/ui/launcherwindow.cpp000066400000000000000000000115241413310443000213430ustar00rootroot00000000000000/* launcherwindow.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "launcherwindow.h" #include "ui_launcherwindow.h" #include #include #include #include #include #include #include using namespace GammaRay; LauncherWindow::LauncherWindow(QWidget *parent) : QDialog(parent) , ui(new Ui::LauncherWindow) { UIResources::setTheme(UiIntegration::hasDarkUI() ? UIResources::Light : UIResources::Dark); ui->setupUi(this); ui->aboutPage->setThemeLogo(QStringLiteral("gammaray-trademark.png")); ui->aboutPage->setTitle(AboutData::aboutTitle()); ui->aboutPage->setHeader(AboutData::aboutHeader()); ui->aboutPage->setAuthors(AboutData::aboutAuthors()); ui->aboutPage->setFooter(AboutData::aboutFooter()); ui->aboutPage->layout()->setContentsMargins(ui->selfTestPage->layout()->contentsMargins()); ui->aboutPage->setBackgroundWindow(this); connect(ui->tabWidget, &QTabWidget::currentChanged, this, &LauncherWindow::tabChanged); connect(ui->attachPage, &AttachDialog::updateButtonState, this, &LauncherWindow::tabChanged); connect(ui->launchPage, &LaunchPage::updateButtonState, this, &LauncherWindow::tabChanged); connect(ui->connectPage, &ConnectPage::updateButtonState, this, &LauncherWindow::tabChanged); connect(ui->attachPage, &AttachDialog::activate, ui->buttonBox->button(QDialogButtonBox::Ok), &QAbstractButton::click); connect(ui->connectPage, &ConnectPage::activate, ui->buttonBox->button(QDialogButtonBox::Ok), &QAbstractButton::click); connect(ui->buttonBox, &QDialogButtonBox::helpRequested, this, &LauncherWindow::help); setWindowTitle(tr("GammaRay Launcher")); ui->buttonBox->button(QDialogButtonBox::Help)->setEnabled(HelpController::isAvailable()); ui->buttonBox->button(QDialogButtonBox::Help)->setShortcut(QKeySequence::HelpContents); QSettings settings; ui->tabWidget->setCurrentIndex(settings.value(QStringLiteral("Launcher/TabIndex")).toInt()); } LauncherWindow::~LauncherWindow() { delete ui; } LaunchOptions LauncherWindow::launchOptions() const { QWidget *current = ui->tabWidget->currentWidget(); if (current == ui->launchPage) return ui->launchPage->launchOptions(); else if (current == ui->attachPage) return ui->attachPage->launchOptions(); return LaunchOptions(); } void LauncherWindow::tabChanged() { if (ui->tabWidget->currentWidget() == ui->attachPage) { ui->buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Attach")); ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(ui->attachPage->isValid()); } else if (ui->tabWidget->currentWidget() == ui->launchPage) { ui->buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Launch")); ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(ui->launchPage->isValid()); } else if (ui->tabWidget->currentWidget() == ui->connectPage) { ui->buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Connect")); ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(ui->connectPage->isValid()); } else { ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); } } void LauncherWindow::accept() { QSettings settings; settings.setValue(QStringLiteral("Launcher/TabIndex"), ui->tabWidget->currentIndex()); ui->launchPage->writeSettings(); ui->attachPage->writeSettings(); ui->connectPage->writeSettings(); if (ui->tabWidget->currentWidget() == ui->connectPage) ui->connectPage->launchClient(); QDialog::accept(); } void LauncherWindow::help() { HelpController::openPage("gammaray/gammaray-launcher-gui.html"); } gammaray-2.11.3/launcher/ui/launcherwindow.h000066400000000000000000000035211413310443000210060ustar00rootroot00000000000000/* launcherwindow.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_LAUNCHERWINDOW_H #define GAMMARAY_LAUNCHERWINDOW_H #include "gammaray_launcher_ui_export.h" #include namespace GammaRay { class LaunchOptions; namespace Ui { class LauncherWindow; } /*! Widget for launching a new process with GammaRay injected. */ class GAMMARAY_LAUNCHER_UI_EXPORT LauncherWindow : public QDialog { Q_OBJECT public: explicit LauncherWindow(QWidget *parent = nullptr); ~LauncherWindow() override; /// returns all information required to perform the launch/attach LaunchOptions launchOptions() const; void accept() override; private slots: void tabChanged(); void help(); private: Ui::LauncherWindow *ui; }; } #endif // GAMMARAY_LAUNCHERWINDOW_H gammaray-2.11.3/launcher/ui/launcherwindow.ui000066400000000000000000000077501413310443000212040ustar00rootroot00000000000000 GammaRay::LauncherWindow 0 0 748 482 :/gammaray/GammaRay-128x128.png:/gammaray/GammaRay-128x128.png 3 Attach Launch Connect Self Test About 0 Qt::Horizontal QDialogButtonBox::Close|QDialogButtonBox::Help|QDialogButtonBox::Ok GammaRay::PromoLabel QWidget
promolabel.h
1
GammaRay::AttachDialog QWidget
attachdialog.h
1
GammaRay::LaunchPage QWidget
launchpage.h
1
GammaRay::SelfTestPage QWidget
selftestpage.h
1
GammaRay::ConnectPage QWidget
connectpage.h
1
GammaRay::AboutWidget QWidget
ui/aboutwidget.h
1
buttonBox accepted() GammaRay::LauncherWindow accept() 248 254 157 274 buttonBox rejected() GammaRay::LauncherWindow reject() 316 260 286 274
gammaray-2.11.3/launcher/ui/launchpage.cpp000066400000000000000000000164361413310443000204300ustar00rootroot00000000000000/* launchpage.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include "launchpage.h" #include "ui_launchpage.h" #include "probeabimodel.h" #include #include #include #include #include #include #include using namespace GammaRay; LaunchPage::LaunchPage(QWidget *parent) : QWidget(parent) , ui(new Ui::LaunchPage) , m_argsModel(new QStringListModel(this)) , m_abiModel(new ProbeABIModel(this)) , m_abiIsValid(true) { ui->setupUi(this); #if defined(Q_OS_MAC) QMargins margins = ui->formLayout->contentsMargins(); margins.setRight(margins.right() +2); margins.setBottom(margins.bottom() +2); ui->formLayout->setContentsMargins(margins); #endif connect(ui->progSelectButton, &QAbstractButton::clicked, this, &LaunchPage::showFileDialog); connect(ui->workDirSelectButton, &QAbstractButton::clicked, this, &LaunchPage::showDirDialog); connect(ui->addArgButton, &QAbstractButton::clicked, this, &LaunchPage::addArgument); connect(ui->removeArgButton, &QAbstractButton::clicked, this, &LaunchPage::removeArgument); connect(ui->progEdit, &QLineEdit::textChanged, this, &LaunchPage::detectABI); connect(ui->progEdit, &QLineEdit::textChanged, this, &LaunchPage::updateButtonState); ui->argsBox->setModel(m_argsModel); auto *pathCompleter = new QCompleter(this); auto *fsModel = new QFileSystemModel(this); fsModel->setRootPath(QDir::rootPath()); pathCompleter->setModel(fsModel); ui->progEdit->setCompleter(pathCompleter); ui->probeBox->setModel(m_abiModel); QSettings settings; ui->progEdit->setText(settings.value(QStringLiteral("Launcher/Program")).toString()); ui->workDirEdit->setText(settings.value(QStringLiteral("Launcher/WorkingDirectory")).toString()); m_argsModel->setStringList(settings.value(QStringLiteral("Launcher/Arguments")).toStringList()); ui->accessMode->setCurrentIndex(settings.value(QStringLiteral("Launcher/AccessMode")).toInt()); updateArgumentButtons(); } LaunchPage::~LaunchPage() { delete ui; } void LaunchPage::writeSettings() { QSettings settings; settings.setValue(QStringLiteral("Launcher/Program"), ui->progEdit->text()); settings.setValue(QStringLiteral("Launcher/WorkingDirectory"), ui->workDirEdit->text()); settings.setValue(QStringLiteral("Launcher/Arguments"), notEmptyString( m_argsModel->stringList())); settings.setValue(QStringLiteral("Launcher/AccessMode"), ui->accessMode->currentIndex()); } QStringList LaunchPage::notEmptyString(const QStringList &list) const { QStringList notEmptyStringList; const int numberOfArguments = list.count(); for (int i = 0; i < numberOfArguments; ++i) { if (!list.at(i).trimmed().isEmpty()) notEmptyStringList << list.at(i); } return notEmptyStringList; } LaunchOptions LaunchPage::launchOptions() const { LaunchOptions opt; QStringList l; l.push_back(ui->progEdit->text()); l.append(notEmptyString(m_argsModel->stringList())); opt.setLaunchArguments(l); opt.setProbeABI(ui->probeBox->itemData(ui->probeBox->currentIndex()).value()); opt.setWorkingDirectory(ui->workDirEdit->text()); switch (ui->accessMode->currentIndex()) { case 0: // local, out-of-process opt.setProbeSetting(QStringLiteral("RemoteAccessEnabled"), true); opt.setProbeSetting(QStringLiteral("ServerAddress"), GAMMARAY_DEFAULT_LOCAL_TCP_URL); opt.setUiMode(LaunchOptions::OutOfProcessUi); break; case 1: // remote, out-of-process opt.setProbeSetting(QStringLiteral("RemoteAccessEnabled"), true); opt.setProbeSetting(QStringLiteral("ServerAddress"), GAMMARAY_DEFAULT_ANY_TCP_URL); opt.setUiMode(LaunchOptions::OutOfProcessUi); break; case 2: // in-process opt.setProbeSetting(QStringLiteral("RemoteAccessEnabled"), false); opt.setUiMode(LaunchOptions::InProcessUi); break; } return opt; } void LaunchPage::showFileDialog() { QString exeFilePath = QFileDialog::getOpenFileName( this, tr("Executable to Launch"), ui->progEdit->text() #ifdef Q_OS_WIN , tr("Executable (*.exe)") #endif ); if (exeFilePath.isEmpty()) return; { const QFileInfo fileInfo(exeFilePath); if (fileInfo.isBundle()) { const QString bundleTarget = QString::fromLatin1("%1/Contents/MacOS/%2") .arg(exeFilePath, fileInfo.completeBaseName()) .replace(QLatin1String("/"), QDir::separator()); if (QFile::exists(bundleTarget)) exeFilePath = bundleTarget; } } ui->progEdit->setText(exeFilePath); } void LaunchPage::showDirDialog() { QString workingDirPath = QFileDialog::getExistingDirectory( this, tr("Working Directory"), ui->workDirEdit->text() ); if (!workingDirPath.isEmpty()) { ui->workDirEdit->setText(workingDirPath); } } void LaunchPage::addArgument() { m_argsModel->insertRows(m_argsModel->rowCount(), 1); const QModelIndex newIndex = m_argsModel->index(m_argsModel->rowCount() - 1, 0); ui->argsBox->edit(newIndex); updateArgumentButtons(); } void LaunchPage::removeArgument() { // TODO check if there's a selection at all and update button state accordingly m_argsModel->removeRows(ui->argsBox->currentIndex().row(), 1); updateArgumentButtons(); } bool LaunchPage::isValid() { if (ui->progEdit->text().isEmpty() || !m_abiIsValid) return false; const QFileInfo fi(ui->progEdit->text()); #ifdef Q_OS_MAC if (fi.isBundle() && (fi.suffix() == "app")) return true; #endif return fi.exists() && fi.isFile() && fi.isExecutable(); } void LaunchPage::updateArgumentButtons() { ui->removeArgButton->setEnabled(m_argsModel->rowCount() > 0); } void LaunchPage::detectABI(const QString &path) { const ProbeABI abi = m_abiDetector.abiForExecutable(path); const int index = m_abiModel->indexOfBestMatchingABI(abi); if (index >= 0) ui->probeBox->setCurrentIndex(index); m_abiIsValid = index >= 0; emit updateButtonState(); } gammaray-2.11.3/launcher/ui/launchpage.h000066400000000000000000000041411413310443000200630ustar00rootroot00000000000000/* launchpage.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_LAUNCHPAGE_H #define GAMMARAY_LAUNCHPAGE_H #include #include QT_BEGIN_NAMESPACE class QStringListModel; QT_END_NAMESPACE namespace GammaRay { class ProbeABIModel; class LaunchOptions; namespace Ui { class LaunchPage; } class LaunchPage : public QWidget { Q_OBJECT public: explicit LaunchPage(QWidget *parent = nullptr); ~LaunchPage() override; LaunchOptions launchOptions() const; bool isValid(); void writeSettings(); signals: void updateButtonState(); private slots: void showFileDialog(); void showDirDialog(); void addArgument(); void removeArgument(); void updateArgumentButtons(); void detectABI(const QString &path); private: QStringList notEmptyString(const QStringList &list) const; Ui::LaunchPage *ui; QStringListModel *m_argsModel; ProbeABIModel *m_abiModel; ProbeABIDetector m_abiDetector; bool m_abiIsValid; }; } #endif // GAMMARAY_LAUNCHPAGE_H gammaray-2.11.3/launcher/ui/launchpage.ui000066400000000000000000000102721413310443000202530ustar00rootroot00000000000000 GammaRay::LaunchPage 0 0 401 305 &Executable: progEdit ... Working Directory: ... &Program arguments: argsBox &Add &Remove Qt::Vertical 20 170 Pro&be: probeBox 0 0 &Access Mode: accessMode 0 Out-of-process, local-only Out-of-process, remote debugging enabled In-process gammaray-2.11.3/launcher/ui/probeabimodel.cpp000066400000000000000000000041631413310443000211170ustar00rootroot00000000000000/* probeabimodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "probeabimodel.h" #include using namespace GammaRay; ProbeABIModel::ProbeABIModel(QObject *parent) : QAbstractListModel(parent) { m_abis = ProbeFinder::listProbeABIs(); } ProbeABIModel::~ProbeABIModel() = default; QVariant ProbeABIModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); const ProbeABI &abi = m_abis.at(index.row()); switch (role) { case Qt::DisplayRole: return abi.displayString(); case Qt::UserRole: return QVariant::fromValue(abi); } return QVariant(); } int ProbeABIModel::rowCount(const QModelIndex &parent) const { if (parent.isValid()) return 0; return m_abis.count(); } int ProbeABIModel::indexOfBestMatchingABI(const ProbeABI &targetABI) const { if (!targetABI.isValid()) return -1; const ProbeABI bestMatchingABI = ProbeFinder::findBestMatchingABI(targetABI, m_abis); return m_abis.indexOf(bestMatchingABI); } gammaray-2.11.3/launcher/ui/probeabimodel.h000066400000000000000000000036011413310443000205600ustar00rootroot00000000000000/* probeabimodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_PROBEABIMODEL_H #define GAMMARAY_PROBEABIMODEL_H #include #include #include namespace GammaRay { /** Model for selecting a probe ABI in the launcher UI. */ class ProbeABIModel : public QAbstractListModel { Q_OBJECT public: explicit ProbeABIModel(QObject *parent = nullptr); ~ProbeABIModel() override; QVariant data(const QModelIndex &index, int role) const override; int rowCount(const QModelIndex &parent) const override; int indexOfBestMatchingABI(const ProbeABI &targetABI) const; private: QVector m_abis; }; } // not in its own header to not pollute the metatype space in the target Q_DECLARE_METATYPE(GammaRay::ProbeABI) #endif // GAMMARAY_PROBEABIMODEL_H gammaray-2.11.3/launcher/ui/processfiltermodel.cpp000066400000000000000000000067071413310443000222260ustar00rootroot00000000000000/* processfiltermodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "processfiltermodel.h" #include "processmodel.h" #include #if defined(Q_OS_WIN) #include #include static QString qGetLogin() { char winUserName[UNLEN + 1]; // UNLEN is defined in LMCONS.H DWORD winUserNameSize = sizeof(winUserName); GetUserNameA(winUserName, &winUserNameSize); return QString::fromLocal8Bit(winUserName); } #else #include #include #include static QString qGetLogin() { struct passwd *pw = getpwuid(getuid()); if (!pw || !pw->pw_name) return QString(); return QString::fromLocal8Bit(pw->pw_name); } #endif using namespace GammaRay; ProcessFilterModel::ProcessFilterModel(QObject *parent) : QSortFilterProxyModel(parent) { m_currentProcId = QString::number(qApp->applicationPid()); m_currentUser = qGetLogin(); #ifndef Q_OS_WIN if (m_currentUser == QLatin1String("root")) { // empty current user == no filter. as root we want to show all m_currentUser.clear(); } #endif } bool ProcessFilterModel::lessThan(const QModelIndex &left, const QModelIndex &right) const { const QString l = sourceModel()->data(left).toString(); const QString r = sourceModel()->data(right).toString(); if (left.column() == ProcessModel::PIDColumn) return l.toInt() < r.toInt(); return l.compare(r, Qt::CaseInsensitive) <= 0; } bool ProcessFilterModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const { ProcessModel *source = dynamic_cast(sourceModel()); if (!source) return true; const ProcData &data = source->dataForRow(source_row); if (!data.abi.isValid()) return false; // hide ourselves as well as the process that launched us if (data.ppid == m_currentProcId || data.name.startsWith(QLatin1String("gammaray"), Qt::CaseInsensitive)) return false; if (!m_currentUser.isEmpty() && data.user != m_currentUser) return false; return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent); } bool ProcessFilterModel::filterAcceptsColumn(int source_column, const QModelIndex & /*source_parent*/) const { // hide user row if current user was found return m_currentUser.isEmpty() || source_column != ProcessModel::UserColumn; } gammaray-2.11.3/launcher/ui/processfiltermodel.h000066400000000000000000000035071413310443000216660ustar00rootroot00000000000000/* processfiltermodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_PROCESSFILTERMODEL_H #define GAMMARAY_PROCESSFILTERMODEL_H #include namespace GammaRay { // A filterable and sortable process model class ProcessFilterModel : public QSortFilterProxyModel { Q_OBJECT public: explicit ProcessFilterModel(QObject *parent); bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override; bool filterAcceptsColumn(int source_column, const QModelIndex &source_parent) const override; private: bool lessThan(const QModelIndex &left, const QModelIndex &right) const override; QString m_currentProcId; QString m_currentUser; }; } #endif // GAMMARAY_PROCESSFILTERMODEL_H gammaray-2.11.3/launcher/ui/processlist.h000066400000000000000000000037211413310443000203310ustar00rootroot00000000000000/************************************************************************** ** ** This code is part of Qt Creator ** ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). ** ** Contact: Nokia Corporation (info@qt.nokia.com) ** ** ** GNU Lesser General Public License Usage ** ** This file may be used under the terms of the GNU Lesser General Public ** License version 2.1 as published by the Free Software Foundation and ** appearing in the file LICENSE.LGPL included in the packaging of this file. ** Please review the following information to ensure the GNU Lesser General ** Public License version 2.1 requirements will be met: ** https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional ** rights. These rights are described in the Nokia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** Other Usage ** ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. ** ** If you have questions regarding the use of this file, please contact ** Nokia at info@qt.nokia.com. ** **************************************************************************/ #ifndef GAMMARAY_PROCESSLIST_H #define GAMMARAY_PROCESSLIST_H #include #include #include struct ProcData { QString ppid; QString name; QString image; QString state; QString user; GammaRay::ProbeABI abi; ProcData() {} inline bool equals(const ProcData &other) const { return ppid == other.ppid && name == other.name && image == other.image && state == other.state && user == other.user && abi == other.abi; } }; typedef QList ProcDataList; extern ProcDataList processList(const ProcDataList &previous); #endif // PROCESSLIST_H gammaray-2.11.3/launcher/ui/processlist_unix.cpp000066400000000000000000000132651413310443000217330ustar00rootroot00000000000000/************************************************************************** ** ** This code is part of Qt Creator ** ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). ** ** Contact: Nokia Corporation (info@qt.nokia.com) ** ** ** GNU Lesser General Public License Usage ** ** This file may be used under the terms of the GNU Lesser General Public ** License version 2.1 as published by the Free Software Foundation and ** appearing in the file LICENSE.LGPL included in the packaging of this file. ** Please review the following information to ensure the GNU Lesser General ** Public License version 2.1 requirements will be met: ** https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional ** rights. These rights are described in the Nokia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** Other Usage ** ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. ** ** If you have questions regarding the use of this file, please contact ** Nokia at info@qt.nokia.com. ** **************************************************************************/ #include "processlist.h" #include #include #include #include #include static GammaRay::ProbeABIDetector s_abiDetector; static bool isUnixProcessId(const QString &procname) { for (int i = 0; i != procname.size(); ++i) { if (!procname.at(i).isDigit()) return false; } return true; } struct PidAndNameMatch : public std::unary_function { explicit PidAndNameMatch(const QString &ppid, const QString &name) : m_ppid(ppid) , m_name(name) { } bool operator()(const ProcData &p) const { return p.ppid == m_ppid && m_name == p.name; } const QString m_ppid; const QString m_name; }; // Determine UNIX processes by running ps static ProcDataList unixProcessListPS(const ProcDataList &previous) { #ifdef Q_OS_MAC // command goes last, otherwise it is cut off static const char formatC[] = "pid state user command"; #else static const char formatC[] = "pid,state,user,cmd"; #endif ProcDataList rc; QProcess psProcess; QStringList args; args << QStringLiteral("-e") << QStringLiteral("-o") << QLatin1String(formatC); psProcess.start(QStringLiteral("ps"), args); if (!psProcess.waitForStarted()) return rc; psProcess.waitForFinished(); QByteArray output = psProcess.readAllStandardOutput(); // Split "457 S+ /Users/foo.app" const QStringList lines = QString::fromLocal8Bit(output).split(QLatin1Char('\n')); const int lineCount = lines.size(); const QChar blank = QLatin1Char(' '); for (int l = 1; l < lineCount; l++) { // Skip header const QString line = lines.at(l).simplified(); // we can't just split on blank as the process name might // contain them const int endOfPid = line.indexOf(blank); const int endOfState = line.indexOf(blank, endOfPid+1); const int endOfUser = line.indexOf(blank, endOfState+1); if (endOfPid >= 0 && endOfState >= 0 && endOfUser >= 0) { ProcData procData; procData.ppid = line.left(endOfPid); procData.state = line.mid(endOfPid+1, endOfState-endOfPid-1); procData.user = line.mid(endOfState+1, endOfUser-endOfState-1); procData.name = line.right(line.size()-endOfUser-1); ProcDataList::ConstIterator it = std::find_if(previous.constBegin(), previous.constEnd(), PidAndNameMatch(procData.ppid, procData.name)); if (it != previous.constEnd()) procData.abi = it->abi; else procData.abi = s_abiDetector.abiForProcess(procData.ppid.toLongLong()); rc.push_back(procData); } } return rc; } // Determine UNIX processes by reading "/proc". Default to ps if // it does not exist ProcDataList processList(const ProcDataList &previous) { const QDir procDir(QStringLiteral("/proc/")); if (!procDir.exists()) return unixProcessListPS(previous); ProcDataList rc; const QStringList procIds = procDir.entryList(); if (procIds.isEmpty()) return rc; for (const QString &procId : procIds) { if (!isUnixProcessId(procId)) continue; QString filename = QStringLiteral("/proc/"); filename += procId; filename += QLatin1String("/stat"); QFile file(filename); if (!file.open(QIODevice::ReadOnly)) continue; // process may have exited const QStringList data = QString::fromLocal8Bit(file.readAll()).split(' '); ProcData proc; proc.ppid = procId; proc.name = data.at(1); if (proc.name.startsWith(QLatin1Char('(')) && proc.name.endsWith(QLatin1Char(')'))) { proc.name.truncate(proc.name.size() - 1); proc.name.remove(0, 1); } proc.state = data.at(2); // PPID is element 3 proc.user = QFileInfo(file).owner(); file.close(); QFile cmdFile(QLatin1String("/proc/") + procId + QLatin1String("/cmdline")); if (cmdFile.open(QFile::ReadOnly)) { QByteArray cmd = cmdFile.readAll(); cmd.replace('\0', ' '); if (!cmd.isEmpty()) proc.name = QString::fromLocal8Bit(cmd).trimmed(); } cmdFile.close(); proc.abi = s_abiDetector.abiForProcess(procId.toLongLong()); rc.push_back(proc); } return rc; } gammaray-2.11.3/launcher/ui/processlist_win.cpp000066400000000000000000000124521413310443000215420ustar00rootroot00000000000000/************************************************************************** ** ** This code is part of Qt Creator ** ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). ** ** Contact: Nokia Corporation (info@qt.nokia.com) ** ** ** GNU Lesser General Public License Usage ** ** This file may be used under the terms of the GNU Lesser General Public ** License version 2.1 as published by the Free Software Foundation and ** appearing in the file LICENSE.LGPL included in the packaging of this file. ** Please review the following information to ensure the GNU Lesser General ** Public License version 2.1 requirements will be met: ** https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional ** rights. These rights are described in the Nokia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** Other Usage ** ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. ** ** If you have questions regarding the use of this file, please contact ** Nokia at info@qt.nokia.com. ** **************************************************************************/ #include "processlist.h" #include #include // Enable Win API of XP SP1 and later #ifdef Q_OS_WIN # if !defined(_WIN32_WINNT) # define _WIN32_WINNT 0x0502 # endif # include # if !defined(PROCESS_SUSPEND_RESUME) // Check flag for MinGW # define PROCESS_SUSPEND_RESUME (0x0800) # endif // PROCESS_SUSPEND_RESUME #endif // Q_OS_WIN #include #include static GammaRay::ProbeABIDetector s_abiDetector; // Resolve QueryFullProcessImageNameW out of kernel32.dll due // to incomplete MinGW import libs and it not being present // on Windows XP. static inline BOOL queryFullProcessImageName(HANDLE h, DWORD flags, LPWSTR buffer, DWORD *size) { // Resolve required symbols from the kernel32.dll typedef BOOL (WINAPI *QueryFullProcessImageNameWProtoType)(HANDLE, DWORD, LPWSTR, PDWORD); static QueryFullProcessImageNameWProtoType queryFullProcessImageNameW = 0; if (!queryFullProcessImageNameW) { QLibrary kernel32Lib(QLatin1String("kernel32.dll"), 0); if (kernel32Lib.isLoaded() || kernel32Lib.load()) { queryFullProcessImageNameW = (QueryFullProcessImageNameWProtoType)kernel32Lib.resolve( "QueryFullProcessImageNameW"); } } if (!queryFullProcessImageNameW) return FALSE; // Read out process return (*queryFullProcessImageNameW)(h, flags, buffer, size); } struct ProcessInfo { QString imageName; QString processOwner; }; static inline ProcessInfo processInfo(DWORD processId) { ProcessInfo pi; HANDLE handle = OpenProcess(PROCESS_QUERY_INFORMATION, TOKEN_READ, processId); if (handle == INVALID_HANDLE_VALUE) return pi; WCHAR buffer[MAX_PATH]; DWORD bufSize = MAX_PATH; if (queryFullProcessImageName(handle, 0, buffer, &bufSize)) pi.imageName = QString::fromUtf16(reinterpret_cast(buffer)); HANDLE processTokenHandle = NULL; if (!OpenProcessToken(handle, TOKEN_READ, &processTokenHandle) || !processTokenHandle) return pi; DWORD size = 0; GetTokenInformation(processTokenHandle, TokenUser, NULL, 0, &size); if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { QByteArray buf; buf.resize(size); PTOKEN_USER userToken = reinterpret_cast(buf.data()); if (userToken && GetTokenInformation(processTokenHandle, TokenUser, userToken, size, &size)) { SID_NAME_USE sidNameUse; TCHAR user[MAX_PATH] = { 0 }; DWORD userNameLength = MAX_PATH; TCHAR domain[MAX_PATH] = { 0 }; DWORD domainNameLength = MAX_PATH; if (LookupAccountSid(NULL, userToken->User.Sid, user, &userNameLength, domain, &domainNameLength, &sidNameUse)) pi.processOwner = QString::fromUtf16(reinterpret_cast(user)); } } CloseHandle(processTokenHandle); CloseHandle(handle); return pi; } ProcDataList processList(const ProcDataList & /*previous*/) { ProcDataList rc; PROCESSENTRY32 pe; pe.dwSize = sizeof(PROCESSENTRY32); HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (snapshot == INVALID_HANDLE_VALUE) return rc; for (bool hasNext = Process32First(snapshot, &pe); hasNext; hasNext = Process32Next(snapshot, &pe)) { ProcData procData; procData.ppid = QString::number(pe.th32ProcessID); procData.name = QString::fromUtf16(reinterpret_cast(pe.szExeFile)); const ProcessInfo processInf = processInfo(pe.th32ProcessID); procData.image = processInf.imageName; procData.user = processInf.processOwner; procData.abi = s_abiDetector.abiForProcess(pe.th32ProcessID); rc.push_back(procData); } CloseHandle(snapshot); return rc; } gammaray-2.11.3/launcher/ui/processmodel.cpp000066400000000000000000000155131413310443000210130ustar00rootroot00000000000000/* processmodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "processmodel.h" #include "probeabimodel.h" #include #include #include #include using namespace GammaRay; bool operator<(const ProcData &l, const ProcData &r) { return l.ppid < r.ppid; } bool operator==(const ProcData &l, const ProcData &r) { return l.ppid == r.ppid; } QDebug operator<<(QDebug d, const ProcData &data) { d << "ProcData{.ppid=" << data.ppid << ", .name=" << data.name << ", .image=" << data.image << ", .state=" << data.state << ", .user=" << data.user << ", .type=" << data.abi.id() << "}"; return d; } ProcessModel::ProcessModel(QObject *parent) : QAbstractTableModel(parent) { m_availableABIs = ProbeFinder::listProbeABIs(); } ProcessModel::~ProcessModel() = default; void ProcessModel::setProcesses(const ProcDataList &processes) { beginResetModel(); m_data = processes; // sort for merging to work properly std::stable_sort(m_data.begin(), m_data.end()); endResetModel(); } void ProcessModel::mergeProcesses(const ProcDataList &processes) { // sort like m_data ProcDataList sortedProcesses = processes; std::stable_sort(sortedProcesses.begin(), sortedProcesses.end()); // iterator over m_data int i = 0; for (const ProcData &newProc : qAsConst(sortedProcesses)) { bool shouldInsert = true; while (i < m_data.count()) { const ProcData &oldProc = m_data.at(i); if (oldProc < newProc) { // remove old proc, seems to be outdated beginRemoveRows(QModelIndex(), i, i); m_data.removeAt(i); endRemoveRows(); continue; } else if (newProc == oldProc) { // already contained, hence increment and break. // Update entry before if something changed (like state), // this make sure m_data match exactly sortedProcesses for later Q_ASSERT check. if (!newProc.equals(oldProc)) { m_data[i] = newProc; emit dataChanged(index(i, 0), index(i, columnCount() - 1)); } ++i; shouldInsert = false; break; } else { // newProc < oldProc // new entry, break and insert it break; } } if (shouldInsert) { beginInsertRows(QModelIndex(), i, i); m_data.insert(i, newProc); endInsertRows(); // let i point to old element again ++i; } } // make sure the new data is properly inserted Q_ASSERT(m_data == sortedProcesses); } void ProcessModel::clear() { beginRemoveRows(QModelIndex(), 0, m_data.count()); m_data.clear(); endRemoveRows(); } ProcData ProcessModel::dataForIndex(const QModelIndex &index) const { return m_data.at(index.row()); } ProcData ProcessModel::dataForRow(int row) const { return m_data.at(row); } QModelIndex ProcessModel::indexForPid(const QString &pid) const { for (int i = 0; i < m_data.size(); ++i) { if (m_data.at(i).ppid == pid) return index(i, 0); } return {}; } QVariant ProcessModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role != Qt::DisplayRole || orientation != Qt::Horizontal) return QVariant(); if (section == PIDColumn) return tr("Process ID"); else if (section == NameColumn) return tr("Name"); else if (section == StateColumn) return tr("State"); else if (section == UserColumn) return tr("User"); return QVariant(); } QVariant ProcessModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); const ProcData &data = m_data.at(index.row()); if (role == Qt::DisplayRole) { if (index.column() == PIDColumn) return data.ppid; else if (index.column() == NameColumn) return data.image.isEmpty() ? data.name : data.image; else if (index.column() == StateColumn) return data.state; else if (index.column() == UserColumn) return data.user; } else if (role == Qt::ToolTipRole) { const ProbeABI bestABI = ProbeFinder::findBestMatchingABI(data.abi, m_availableABIs); return tr("Name: %1\nPID: %2\nOwner: %3\nQt ABI: %4\nProbe available: %5").arg( data.image.isEmpty() ? data.name : data.image, data.ppid, data.user, data.abi.displayString(), bestABI.isValid() ? tr("yes") : tr("no") ); } else if (role == PIDRole) { return data.ppid.toInt(); // why is this a QString in the first place!? } else if (role == NameRole) { return data.image.isEmpty() ? data.name : data.image; } else if (role == StateRole) { return data.state; } else if (role == UserRole) { return data.user; } else if (role == ABIRole) { return QVariant::fromValue(data.abi); } return QVariant(); } int ProcessModel::columnCount(const QModelIndex &parent) const { return parent.isValid() ? 0 : COLUMN_COUNT; } int ProcessModel::rowCount(const QModelIndex &parent) const { return parent.isValid() ? 0 : m_data.count(); } ProcDataList ProcessModel::processes() const { return m_data; } Qt::ItemFlags ProcessModel::flags(const QModelIndex &index) const { const ProbeABI abi = index.data(ABIRole).value(); const ProbeABI bestABI = ProbeFinder::findBestMatchingABI(abi, m_availableABIs); const Qt::ItemFlags f = QAbstractTableModel::flags(index); if (!bestABI.isValid()) return f & ~(Qt::ItemIsEnabled | Qt::ItemIsSelectable); return f; } gammaray-2.11.3/launcher/ui/processmodel.h000066400000000000000000000051121413310443000204520ustar00rootroot00000000000000/* processmodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_PROCESSMODEL_H #define GAMMARAY_PROCESSMODEL_H #include #include #include #include "processlist.h" namespace GammaRay { class ProcessModel : public QAbstractTableModel { Q_OBJECT public: explicit ProcessModel(QObject *parent = nullptr); ~ProcessModel() override; void setProcesses(const ProcDataList &processes); void mergeProcesses(const ProcDataList &processes); ProcData dataForIndex(const QModelIndex &index) const; ProcData dataForRow(int row) const; QModelIndex indexForPid(const QString &pid) const; ProcDataList processes() const; void clear(); enum Columns { PIDColumn, NameColumn, StateColumn, UserColumn, COLUMN_COUNT }; int columnCount(const QModelIndex &parent = QModelIndex()) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; enum CustomRoles { PIDRole = Qt::UserRole, NameRole, StateRole, UserRole, ABIRole }; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; Qt::ItemFlags flags(const QModelIndex &index) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; private: ProcDataList m_data; QVector m_availableABIs; }; } #endif // GAMMARAY_PROCESSMODEL_H gammaray-2.11.3/launcher/ui/promolabel.cpp000066400000000000000000000043161413310443000204470ustar00rootroot00000000000000/* mainwindow.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "promolabel.h" #include #include #include #include #include using namespace GammaRay; PromoLabel::PromoLabel(QWidget *parent, Qt::WindowFlags f) : ThemedImageLabel(parent, f) { setCursor(QCursor(Qt::PointingHandCursor)); setToolTip(tr("Visit KDAB Website")); setThemeFileName(QStringLiteral("kdab-products.png")); } bool PromoLabel::event(QEvent *e) { if (e->type() == QEvent::PaletteChange) updatePixmap(); return ThemedImageLabel::event(e); } void PromoLabel::mouseReleaseEvent(QMouseEvent *ev) { if (ev->button() == Qt::LeftButton && ev->modifiers() == Qt::NoModifier) { QDesktopServices::openUrl(QUrl(QStringLiteral("https://www.kdab.com"))); ev->accept(); return; } ThemedImageLabel::mouseReleaseEvent(ev); } void PromoLabel::updatePixmap() { // load image and adapt it to user's foreground color const QImage image = UIResources::themedImage(themeFileName(), this); setPixmap(UIResources::tintedPixmap(image, palette().foreground().color())); } gammaray-2.11.3/launcher/ui/promolabel.h000066400000000000000000000030611413310443000201100ustar00rootroot00000000000000/* mainwindow.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_PROMOLABEL_H #define GAMMARAY_PROMOLABEL_H #include namespace GammaRay { class PromoLabel : public ThemedImageLabel { Q_OBJECT public: explicit PromoLabel(QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); protected: bool event(QEvent *e) override; void mouseReleaseEvent(QMouseEvent *ev) override; private: void updatePixmap() override; }; } #endif // PROMOLABEL_H gammaray-2.11.3/launcher/ui/resources/000077500000000000000000000000001413310443000176155ustar00rootroot00000000000000gammaray-2.11.3/launcher/ui/resources/launcher/000077500000000000000000000000001413310443000214165ustar00rootroot00000000000000gammaray-2.11.3/launcher/ui/resources/launcher/warning.png000066400000000000000000000010551413310443000235720ustar00rootroot00000000000000PNG  IHDRasBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<IDAT8=Q}E YVl&"n0/!$)E"eA?LNZf`!S8^;!)+ @M&] $ߵ~GV#'-˲RӲZ)Fp]1櫈48IJ(8}?hl2B]p V\n*6=[[ Ȳ I|)(: R@պ@WbBm"r] |fyq+$ض}wUBz_$EkYk}ei8Ha$nk pi<-"6zّ(+)A,a{qHtNgY'"I]XT R]޳KeIENDB`gammaray-2.11.3/launcher/ui/resources/ui.qrc000066400000000000000000000001431413310443000207370ustar00rootroot00000000000000 launcher/warning.png gammaray-2.11.3/launcher/ui/selftestpage.cpp000066400000000000000000000045541413310443000210050ustar00rootroot00000000000000/* selftestpage.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include "selftestpage.h" #include "ui_selftestpage.h" #include #include #include using namespace GammaRay; SelfTestPage::SelfTestPage(QWidget *parent) : QWidget(parent) , ui(new Ui::SelfTestPage) , m_resultModel(new QStandardItemModel(this)) { ui->setupUi(this); ui->resultView->setModel(m_resultModel); run(); } SelfTestPage::~SelfTestPage() { delete ui; } void SelfTestPage::run() { m_resultModel->clear(); SelfTest selfTest; connect(&selfTest, &SelfTest::information, this, &SelfTestPage::information); connect(&selfTest, &SelfTest::error, this, &SelfTestPage::error); selfTest.checkEverything(); } void SelfTestPage::error(const QString &msg) { auto *item = new QStandardItem; item->setEditable(false); item->setText(msg); item->setIcon(style()->standardIcon(QStyle::SP_MessageBoxCritical)); m_resultModel->appendRow(item); } void SelfTestPage::information(const QString &msg) { auto *item = new QStandardItem; item->setEditable(false); item->setText(msg); item->setIcon(style()->standardIcon(QStyle::SP_MessageBoxInformation)); m_resultModel->appendRow(item); } gammaray-2.11.3/launcher/ui/selftestpage.h000066400000000000000000000033001413310443000204360ustar00rootroot00000000000000/* selftestpage.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_SELFTESTPAGE_H #define GAMMARAY_SELFTESTPAGE_H #include QT_BEGIN_NAMESPACE class QStandardItemModel; QT_END_NAMESPACE namespace GammaRay { namespace Ui { class SelfTestPage; } class SelfTestPage : public QWidget { Q_OBJECT public: explicit SelfTestPage(QWidget *parent = nullptr); ~SelfTestPage() override; public slots: void run(); private slots: void error(const QString &msg); void information(const QString &msg); private: Ui::SelfTestPage *ui; QStandardItemModel *m_resultModel; }; } #endif // GAMMARAY_SELFTESTPAGE_H gammaray-2.11.3/launcher/ui/selftestpage.ui000066400000000000000000000007321413310443000206320ustar00rootroot00000000000000 GammaRay::SelfTestPage 0 0 400 300 gammaray-2.11.3/launcher/win-injector/000077500000000000000000000000001413310443000175765ustar00rootroot00000000000000gammaray-2.11.3/launcher/win-injector/CMakeLists.txt000066400000000000000000000007301413310443000223360ustar00rootroot00000000000000if (WIN32) add_executable(gammaray_wininjector wininjector-cli.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../core/injector/basicwindllinjector.cpp) if(CMAKE_SIZEOF_VOID_P EQUAL 8) set_target_properties(gammaray_wininjector PROPERTIES OUTPUT_NAME "gammaray-wininjector-x86_64") else() set_target_properties(gammaray_wininjector PROPERTIES OUTPUT_NAME "gammaray-wininjector-i686") endif() install(TARGETS gammaray_wininjector ${INSTALL_TARGETS_DEFAULT_ARGS}) endif() gammaray-2.11.3/launcher/win-injector/wininjector-cli.cpp000066400000000000000000000027561413310443000234140ustar00rootroot00000000000000/* wininjector-cli.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Hannah von Reth Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "../core/injector/basicwindllinjector.h" int main() { int argc; wchar_t **argv = CommandLineToArgvW(GetCommandLineW(), &argc); if (argc != 4) { return -1; } wchar_t *pidString(argv[1]); wchar_t *path(argv[2]); wchar_t *probePath(argv[3]); return BasicWinDllInjector::injectProcess(pidString, path, probePath) ? 0 : -1; } gammaray-2.11.3/multibuild/000077500000000000000000000000001413310443000155375ustar00rootroot00000000000000gammaray-2.11.3/multibuild/CMakeLists.txt000066400000000000000000000030231413310443000202750ustar00rootroot00000000000000include(ExternalProject) # on Windows we need to build the debug/release counter-part as well if(NOT WIN32) return() endif() if(CMAKE_BUILD_TYPE MATCHES "^[Rr]el") set(_build_type "Debug") else() set(_build_type "Release") endif() ExternalProject_Add( GammaRay-${_build_type} SOURCE_DIR ${CMAKE_SOURCE_DIR} BINARY_DIR ${CMAKE_BINARY_DIR}/${_build_type} EXCLUDE_FROM_ALL 1 STEP_TARGETS build install CMAKE_ARGS -DCMAKE_BUILD_TYPE=${_build_type} -DGAMMARAY_MULTI_BUILD=OFF -DGAMMARAY_PROBE_ONLY_BUILD=ON -DGAMMARAY_BUILD_CLI_INJECTOR=OFF -DGAMMARAY_INSTALL_QT_LAYOUT=${GAMMARAY_INSTALL_QT_LAYOUT} -DGAMMARAY_BUILD_UI=${GAMMARAY_BUILD_UI} -DGAMMARAY_OUTPUT_PREFIX=${CMAKE_BINARY_DIR} -DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX} -DCMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH} -DBUILD_TESTING=OFF -DCMAKE_OBJECT_PATH_MAX=512 ) # hacky way to add a dependency from the default target to the build step add_custom_target(multibuild_build ALL DEPENDS GammaRay-${_build_type}-build) # even hackier way to postpone external project installation to install time add_custom_target(multibuild_install) add_dependencies(multibuild_install GammaRay-${_build_type}-install) _ep_get_step_stampfile(GammaRay-${_build_type} "install" install_stamp_file) if(install_stamp_file) install(CODE "execute_process(COMMAND \"${CMAKE_COMMAND}\" -E remove \"${install_stamp_file}\")") endif() install(CODE "execute_process(COMMAND \"${CMAKE_COMMAND}\" --build \"${CMAKE_BINARY_DIR}\" --target multibuild_install)") gammaray-2.11.3/plugins/000077500000000000000000000000001413310443000150465ustar00rootroot00000000000000gammaray-2.11.3/plugins/CMakeLists.txt000066400000000000000000000027661413310443000176210ustar00rootroot00000000000000add_subdirectory(codecbrowser) add_subdirectory(eventmonitor) add_subdirectory(fontbrowser) add_subdirectory(kjobtracker) add_subdirectory(localeinspector) add_subdirectory(modelinspector) add_subdirectory(quickinspector) add_subdirectory(signalmonitor) add_subdirectory(statemachineviewer) add_subdirectory(timertop) add_subdirectory(mimetypes) add_subdirectory(network) add_subdirectory(objectvisualizer) add_subdirectory(qtivi) add_subdirectory(sysinfo) add_subdirectory(translatorinspector) add_subdirectory(webinspector) add_subdirectory(wlcompositorinspector) if(Qt5Gui_FOUND) add_subdirectory(guisupport) add_subdirectory(textdocumentinspector) endif() if(Qt5Widgets_FOUND) add_subdirectory(actioninspector) add_subdirectory(widgetinspector) add_subdirectory(sceneinspector) add_subdirectory(styleinspector) endif() # just checking whether Qt5::ScriptTools is found should be enough, but it isn't. # thus check both that Qt5::Script & Qt5::ScriptTools are found to fix https://github.com/KDAB/GammaRay/issues/492 if((Qt5Script_FOUND AND Qt5ScriptTools_FOUND) OR (Qt5Script_FOUND AND Qt5ScriptTools_FOUND)) add_subdirectory(scriptenginedebugger) endif() if(Qt5Qml_FOUND) add_subdirectory(qmlsupport) endif() if(Qt5QuickWidgets_FOUND) add_subdirectory(quickwidgetsupport) endif() if(Qt5Bluetooth_FOUND) add_subdirectory(bluetooth) endif() if(Qt5Positioning_FOUND) add_subdirectory(positioning) endif() if(Qt53DRender_FOUND AND Qt53DExtras_FOUND) add_subdirectory(qt3dinspector) endif() gammaray-2.11.3/plugins/actioninspector/000077500000000000000000000000001413310443000202525ustar00rootroot00000000000000gammaray-2.11.3/plugins/actioninspector/CMakeLists.txt000066400000000000000000000014201413310443000230070ustar00rootroot00000000000000# probe part if (NOT GAMMARAY_CLIENT_ONLY_BUILD) set(gammaray_actioninspector_plugin_srcs actioninspector.cpp actionvalidator.cpp actionmodel.cpp ) gammaray_add_plugin(gammaray_actioninspector_plugin JSON gammaray_actioninspector.json SOURCES ${gammaray_actioninspector_plugin_srcs} ) target_link_libraries(gammaray_actioninspector_plugin Qt5::Gui Qt5::Widgets gammaray_core ) endif() # ui part if(GAMMARAY_BUILD_UI) set(gammaray_actioninspector_ui_plugin_srcs actioninspectorwidget.cpp clientactionmodel.cpp ) gammaray_add_plugin(gammaray_actioninspector_ui_plugin JSON gammaray_actioninspector.json SOURCES ${gammaray_actioninspector_ui_plugin_srcs} ) target_link_libraries(gammaray_actioninspector_ui_plugin gammaray_ui ) endif() gammaray-2.11.3/plugins/actioninspector/actioninspector.cpp000066400000000000000000000102041413310443000241570ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Kevin Funk Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "actioninspector.h" #include "actionmodel.h" #include #include #include #include #include #include #include #include #include #include #include using namespace GammaRay; using namespace std; ActionInspector::ActionInspector(Probe *probe, QObject *parent) : QObject(parent) { registerMetaTypes(); ObjectBroker::registerObject(QStringLiteral("com.kdab.GammaRay.ActionInspector"), this); auto *actionModel = new ActionModel(this); connect(probe, &Probe::objectCreated, actionModel, &ActionModel::objectAdded); connect(probe, &Probe::objectDestroyed, actionModel, &ActionModel::objectRemoved); connect(probe, &Probe::objectSelected, this, &ActionInspector::objectSelected); auto proxy = new ServerProxyModel(this); proxy->setSourceModel(actionModel); proxy->addRole(ActionModel::ObjectIdRole); probe->registerModel(QStringLiteral("com.kdab.GammaRay.ActionModel"), proxy); m_selectionModel = ObjectBroker::selectionModel(proxy); } ActionInspector::~ActionInspector() = default; void ActionInspector::triggerAction(int row) { auto model = ObjectBroker::model(QStringLiteral("com.kdab.GammaRay.ActionModel")); const QModelIndex index = model->index(row, 0); if (!index.isValid()) return; QObject *obj = index.data(ActionModel::ObjectRole).value(); QAction *action = qobject_cast(obj); if (action) action->trigger(); } void GammaRay::ActionInspector::objectSelected(QObject *obj) { QAction *action = qobject_cast(obj); if (!action) return; const QAbstractItemModel *model = m_selectionModel->model(); const auto indexList = model->match(model->index(0, 0), ActionModel::ObjectIdRole, QVariant::fromValue(ObjectId(action)), 1, Qt::MatchExactly | Qt::MatchRecursive | Qt::MatchWrap); if (indexList.isEmpty()) return; const QModelIndex index = indexList.first(); m_selectionModel->select(index, QItemSelectionModel::Select |QItemSelectionModel::Clear |QItemSelectionModel::Rows |QItemSelectionModel::Current); } void ActionInspector::registerMetaTypes() { MetaObject *mo = nullptr; MO_ADD_METAOBJECT1(QAction, QObject); MO_ADD_PROPERTY_RO(QAction, actionGroup); MO_ADD_PROPERTY(QAction, data, setData); MO_ADD_PROPERTY(QAction, isSeparator, setSeparator); MO_ADD_PROPERTY_RO(QAction, menu); MO_ADD_PROPERTY_RO(QAction, parentWidget); MO_ADD_PROPERTY_RO(QAction, associatedGraphicsWidgets); MO_ADD_PROPERTY_RO(QAction, associatedWidgets); MO_ADD_METAOBJECT1(QActionGroup, QObject); MO_ADD_PROPERTY_RO(QActionGroup, actions); } gammaray-2.11.3/plugins/actioninspector/actioninspector.h000066400000000000000000000041061413310443000236300ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Kevin Funk Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_ACTIONINSPECTOR_ACTIONINSPECTOR_H #define GAMMARAY_ACTIONINSPECTOR_ACTIONINSPECTOR_H #include #include QT_BEGIN_NAMESPACE class QItemSelectionModel; QT_END_NAMESPACE namespace GammaRay { class ActionInspector : public QObject { Q_OBJECT public: explicit ActionInspector(Probe *probe, QObject *parent = nullptr); ~ActionInspector() override; public Q_SLOTS: void triggerAction(int row); private Q_SLOTS: void objectSelected(QObject *obj); private: void registerMetaTypes(); QItemSelectionModel *m_selectionModel; }; class ActionInspectorFactory : public QObject, public StandardToolFactory { Q_OBJECT Q_INTERFACES(GammaRay::ToolFactory) Q_PLUGIN_METADATA(IID "com.kdab.GammaRay.ToolFactory" FILE "gammaray_actioninspector.json") public: explicit ActionInspectorFactory(QObject *parent = nullptr) : QObject(parent) { } }; } #endif // GAMMARAY_ACTIONINSPECTOR_H gammaray-2.11.3/plugins/actioninspector/actioninspectorwidget.cpp000066400000000000000000000100271413310443000253660ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Kevin Funk Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "actioninspectorwidget.h" #include "ui_actioninspectorwidget.h" #include "actionmodel.h" // for column enum only #include "clientactionmodel.h" #include #include #include #include #include #include #include using namespace GammaRay; ActionInspectorWidget::ActionInspectorWidget(QWidget *parent) : QWidget(parent) , ui(new Ui::ActionInspectorWidget) , m_stateManager(this) { setObjectName("ActionInspectorWidget"); ui->setupUi(this); auto sourceModel = ObjectBroker::model(QStringLiteral("com.kdab.GammaRay.ActionModel")); auto actionModel = new ClientActionModel(this); actionModel->setSourceModel(sourceModel); new SearchLineController(ui->actionSearchLine, actionModel); ui->actionView->header()->setObjectName("objectTreeViewHeader"); ui->actionView->setDeferredResizeMode(0, QHeaderView::ResizeToContents); ui->actionView->setDeferredResizeMode(2, QHeaderView::ResizeToContents); ui->actionView->setDeferredResizeMode(3, QHeaderView::ResizeToContents); ui->actionView->setDeferredResizeMode(4, QHeaderView::ResizeToContents); ui->actionView->setModel(actionModel); ui->actionView->sortByColumn(ActionModel::ShortcutsPropColumn); connect(ui->actionView, &QWidget::customContextMenuRequested, this, &ActionInspectorWidget::contextMenu); auto selectionModel = ObjectBroker::selectionModel(actionModel); ui->actionView->setSelectionModel(selectionModel); connect(selectionModel, &QItemSelectionModel::selectionChanged, this, &ActionInspectorWidget::selectionChanged); m_stateManager.setDefaultSizes(ui->actionView->header(), UISizeVector() << -1 << 200 << -1 << -1 << -1 << 200); connect(ui->actionView, &QAbstractItemView::doubleClicked, this, &ActionInspectorWidget::triggerAction); } ActionInspectorWidget::~ActionInspectorWidget() = default; void ActionInspectorWidget::triggerAction(const QModelIndex &index) { if (!index.isValid()) return; Endpoint::instance()->invokeObject(QStringLiteral( "com.kdab.GammaRay.ActionInspector"), "triggerAction", QVariantList() << index.row()); } void ActionInspectorWidget::contextMenu(QPoint pos) { auto index = ui->actionView->indexAt(pos); if (!index.isValid()) return; index = index.sibling(index.row(), 0); const auto objectId = index.data(ActionModel::ObjectIdRole).value(); if (objectId.isNull()) return; QMenu menu; ContextMenuExtension ext(objectId); ext.populateMenu(&menu); menu.exec(ui->actionView->viewport()->mapToGlobal(pos)); } void ActionInspectorWidget::selectionChanged(const QItemSelection& selection) { if (selection.isEmpty()) return; const auto idx = selection.at(0).topLeft(); ui->actionView->scrollTo(idx); } gammaray-2.11.3/plugins/actioninspector/actioninspectorwidget.h000066400000000000000000000042461413310443000250410ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Kevin Funk Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_ACTIONINSPECTOR_ACTIONINSPECTORWIDGET_H #define GAMMARAY_ACTIONINSPECTOR_ACTIONINSPECTORWIDGET_H #include #include #include #include QT_BEGIN_NAMESPACE class QItemSelection; class QModelIndex; QT_END_NAMESPACE namespace GammaRay { namespace Ui { class ActionInspectorWidget; } class ActionInspectorWidget : public QWidget { Q_OBJECT public: explicit ActionInspectorWidget(QWidget *parent = nullptr); ~ActionInspectorWidget() override; private Q_SLOTS: void triggerAction(const QModelIndex &index); void contextMenu(QPoint pos); void selectionChanged(const QItemSelection &selection); private: QScopedPointer ui; UIStateManager m_stateManager; }; class ActionInspectorUiFactory : public QObject, public StandardToolUiFactory { Q_OBJECT Q_INTERFACES(GammaRay::ToolUiFactory) Q_PLUGIN_METADATA(IID "com.kdab.GammaRay.ToolUiFactory" FILE "gammaray_actioninspector.json") }; } #endif // GAMMARAY_ACTIONINSPECTORWIDGET_H gammaray-2.11.3/plugins/actioninspector/actioninspectorwidget.ui000066400000000000000000000020541413310443000252220ustar00rootroot00000000000000 GammaRay::ActionInspectorWidget 0 0 400 300 Qt::CustomContextMenu false true GammaRay::DeferredTreeView QTreeView
ui/deferredtreeview.h
gammaray-2.11.3/plugins/actioninspector/actionmodel.cpp000066400000000000000000000173211413310443000232600ustar00rootroot00000000000000/* actionmodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2012-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Kevin Funk Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "actionmodel.h" #include "actionvalidator.h" #include #include #include #include #include #include #include #include #include #include #include Q_DECLARE_METATYPE(QAction::Priority) using namespace GammaRay; static QString toString(const QList &list) { QStringList items; items.reserve(list.size()); for (const auto &item : list) { items << item.toString(); } return items.join(QStringLiteral(", ")); } ActionModel::ActionModel(QObject *parent) : QAbstractTableModel(parent) , m_duplicateFinder(new ActionValidator(this)) { ProblemCollector::registerProblemChecker("gammaray_actioninspector.ShortcutDuplicates", "Shortcut Duplicates", "Scans for potential shortcut conflicts in QActions", [this]() { scanForShortcutDuplicates(); }); } ActionModel::~ActionModel() = default; void ActionModel::objectAdded(QObject *object) { // see Probe::objectCreated, that promises a valid object in the main thread Q_ASSERT(QThread::currentThread() == thread()); Q_ASSERT(object); QAction * const action = qobject_cast(object); if (!action) return; auto it = std::lower_bound(m_actions.begin(), m_actions.end(), action); Q_ASSERT(it == m_actions.end() || *it != action); const int row = std::distance(m_actions.begin(), it); Q_ASSERT(row >= 0 && row <= m_actions.size()); beginInsertRows(QModelIndex(), row, row); m_actions.insert(it, action); Q_ASSERT(m_actions.at(row) == action); m_duplicateFinder->insert(action); connect(action, &QAction::changed, this, &ActionModel::actionChanged); endInsertRows(); } void ActionModel::objectRemoved(QObject *object) { Q_ASSERT(thread() == QThread::currentThread()); QAction * const action = reinterpret_cast(object); // never dereference this, just use for comparison auto it = std::lower_bound(m_actions.begin(), m_actions.end(), reinterpret_cast(object)); if (it == m_actions.end() || *it != action) return; const int row = std::distance(m_actions.begin(), it); Q_ASSERT(row >= 0 && row < m_actions.size()); Q_ASSERT(m_actions.at(row) == action); beginRemoveRows(QModelIndex(), row, row); m_actions.erase(it); m_duplicateFinder->remove(action); endRemoveRows(); } int ActionModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); return ColumnCount; } int ActionModel::rowCount(const QModelIndex &parent) const { if (parent.isValid()) return 0; return m_actions.size(); } QVariant ActionModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); QMutexLocker lock(Probe::objectLock()); QAction *action = m_actions.at(index.row()); if (!Probe::instance()->isValidObject(action)) return QVariant(); const int column = index.column(); if (role == Qt::DisplayRole) { switch (column) { case AddressColumn: return Util::shortDisplayString(action); case NameColumn: return action->text(); case CheckablePropColumn: return action->isCheckable(); case PriorityPropColumn: return EnumUtil::enumToString(action->priority(), nullptr, action->metaObject()); case ShortcutsPropColumn: return toString(action->shortcuts()); default: return QVariant(); } } else if (role == Qt::DecorationRole) { if (column == NameColumn) return action->icon(); } else if (role == Qt::CheckStateRole) { switch (column) { case AddressColumn: return action->isEnabled() ? Qt::Checked : Qt::Unchecked; case CheckedPropColumn: if (action->isCheckable()) return action->isChecked() ? Qt::Checked : Qt::Unchecked; return QVariant(); } } else if (role == ShortcutConflictRole && column == ShortcutsPropColumn) { return m_duplicateFinder->hasAmbiguousShortcut(action); } else if (role == ActionModel::ObjectRole) { return QVariant::fromValue(action); } else if (role == ActionModel::ObjectIdRole && index.column() == 0) { return QVariant::fromValue(ObjectId(action)); } return QVariant(); } Qt::ItemFlags ActionModel::flags(const QModelIndex& index) const { const auto f = QAbstractTableModel::flags(index); if (!index.isValid()) return f; if (index.column() == AddressColumn) return f | Qt::ItemIsUserCheckable; if (index.column() == CheckedPropColumn && m_actions.at(index.row())->isCheckable()) return f | Qt::ItemIsUserCheckable; return f; } bool ActionModel::setData(const QModelIndex& index, const QVariant& value, int role) { if (role == Qt::CheckStateRole && index.isValid()) { auto action = m_actions.at(index.row()); switch (index.column()) { case AddressColumn: action->setEnabled(value.toInt() == Qt::Checked); return true; case CheckedPropColumn: action->setChecked(value.toInt() == Qt::Checked); return true; } } return QAbstractItemModel::setData(index, value, role); } void ActionModel::actionChanged() { auto action = qobject_cast(sender()); if (!action) return; auto row = m_actions.indexOf(action); emit dataChanged(index(row, 0), index(row, ActionModel::ShortcutsPropColumn)); } void ActionModel::scanForShortcutDuplicates() const { for (QAction *action : m_actions) { Q_FOREACH (const QKeySequence &sequence, m_duplicateFinder->findAmbiguousShortcuts(action)) { Problem p; p.severity = Problem::Error; p.description = QStringLiteral("Key sequence %1 is ambiguous.").arg(sequence.toString(QKeySequence::NativeText)); p.problemId = QStringLiteral("gammaray_actioninspector.ShortcutDuplicates:%1").arg(sequence.toString(QKeySequence::PortableText)); p.object = ObjectId(action); p.locations.push_back(ObjectDataProvider::creationLocation(action)); p.findingCategory = Problem::Scan; ProblemCollector::addProblem(p); } } } gammaray-2.11.3/plugins/actioninspector/actionmodel.h000066400000000000000000000051361413310443000227260ustar00rootroot00000000000000/* actionmodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2012-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Kevin Funk Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_ACTIONINSPECTOR_ACTIONMODEL_H #define GAMMARAY_ACTIONINSPECTOR_ACTIONMODEL_H #include #include #include QT_BEGIN_NAMESPACE class QAction; QT_END_NAMESPACE namespace GammaRay { class ActionValidator; class ActionModel : public QAbstractTableModel { Q_OBJECT public: enum Column { AddressColumn, NameColumn, CheckablePropColumn, CheckedPropColumn, PriorityPropColumn, ShortcutsPropColumn, /** Mark column count */ ColumnCount }; enum Role { ObjectIdRole = UserRole + 1, ObjectRole, ShortcutConflictRole }; explicit ActionModel(QObject *parent = nullptr); ~ActionModel() override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; Qt::ItemFlags flags(const QModelIndex& index) const override; bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override; public slots: void objectAdded(QObject *object); void objectRemoved(QObject *object); private slots: void actionChanged(); private: void scanForShortcutDuplicates() const; // sorted vector of QActions QVector m_actions; ActionValidator *m_duplicateFinder; }; } #endif // GAMMARAY_ACTIONMODEL_H gammaray-2.11.3/plugins/actioninspector/actionvalidator.cpp000066400000000000000000000141121413310443000241400ustar00rootroot00000000000000/* actionvalidator.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2012-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Kevin Funk Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "actionvalidator.h" #include #include #include #include #include #include using namespace GammaRay; QT_BEGIN_NAMESPACE uint qHash(const QKeySequence &sequence) { return qHash(sequence.toString(QKeySequence::PortableText)); } QT_END_NAMESPACE ActionValidator::ActionValidator(QObject *parent) : QObject(parent) { } QList ActionValidator::actions() const { return m_shortcutActionMap.values(); } QList ActionValidator::actions(const QKeySequence &sequence) const { return m_shortcutActionMap.values(sequence); } void ActionValidator::setActions(const QList &actions) { clearActions(); m_shortcutActionMap.reserve(actions.size()); for (QAction *action : actions) { insert(action); } } void ActionValidator::clearActions() { m_shortcutActionMap.clear(); } void ActionValidator::insert(QAction *action) { Q_ASSERT(action); Q_FOREACH(const QKeySequence &sequence, action->shortcuts()) { if (m_shortcutActionMap.values(sequence).contains(action)) continue; m_shortcutActionMap.insertMulti(sequence, action); } // also track object destruction connect(action, &QObject::destroyed, this, &ActionValidator::handleActionDestroyed); } void ActionValidator::remove(QAction *action) { Q_ASSERT(action); safeRemove(action); } void ActionValidator::safeRemove(QAction *action) { Q_FOREACH(const QKeySequence &sequence, m_shortcutActionMap.keys()) { if (!m_shortcutActionMap.values(sequence).contains(action)) continue; QList oldValues = m_shortcutActionMap.values(sequence); const bool success = oldValues.removeOne(action); Q_UNUSED(success); Q_ASSERT(success); m_shortcutActionMap[sequence] = action; } } void ActionValidator::handleActionDestroyed(QObject *object) { QAction *action = static_cast(object); safeRemove(action); } bool ActionValidator::hasAmbiguousShortcut(const QAction *action) const { QList shortcuts = action->shortcuts(); return std::any_of(shortcuts.constBegin(), shortcuts.constEnd(), [action, this](const QKeySequence &seq) { return isAmbigous(action, seq); }); } QVector GammaRay::ActionValidator::findAmbiguousShortcuts(const QAction* action) const { QVector shortcuts; if (!action) return shortcuts; Q_FOREACH(const QKeySequence &sequence, action->shortcuts()) { if (isAmbigous(action, sequence)) { shortcuts.push_back(sequence); } } return shortcuts; } bool GammaRay::ActionValidator::isAmbigous(const QAction *action, const QKeySequence &sequence) const { Q_ASSERT(action); QMutexLocker lock(Probe::objectLock()); if (!Probe::instance()->isValidObject(action)) { return false; } Q_FOREACH(const QAction *other, m_shortcutActionMap.values(sequence)) { if (!other || other == action || !Probe::instance()->isValidObject(other)) { continue; } if (action->shortcutContext() == Qt::ApplicationShortcut || other->shortcutContext() == Qt::ApplicationShortcut) return true; if (action->shortcutContext() == Qt::WindowShortcut || other->shortcutContext() == Qt::WindowShortcut) { Q_FOREACH (QWidget *w1, action->associatedWidgets()) { Q_FOREACH (QWidget *w2, other->associatedWidgets()) { if (w1->window() == w2->window()) return true; } } } if (action->shortcutContext() == Qt::WidgetWithChildrenShortcut) { Q_FOREACH (QWidget *w1, action->associatedWidgets()) { Q_FOREACH (QWidget *w2, other->associatedWidgets()) { for (QWidget *ancestor = w2; ancestor; ancestor = ancestor->parentWidget()) { if (w1 == ancestor) return true; } } } } if (other->shortcutContext() == Qt::WidgetWithChildrenShortcut) { Q_FOREACH (QWidget *w1, other->associatedWidgets()) { Q_FOREACH (QWidget *w2, action->associatedWidgets()) { for (QWidget *ancestor = w2; ancestor; ancestor = ancestor->parentWidget()) { if (w1 == ancestor) return true; } } } } if (action->shortcutContext() == Qt::WidgetShortcut && other->shortcutContext() == Qt::WidgetShortcut) { Q_FOREACH (QWidget *w1, action->associatedWidgets()) { Q_FOREACH (QWidget *w2, other->associatedWidgets()) { if (w1 == w2) return true; } } } } return false; } gammaray-2.11.3/plugins/actioninspector/actionvalidator.h000066400000000000000000000044071413310443000236130ustar00rootroot00000000000000/* actionvalidator.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2012-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Kevin Funk Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_ACTIONINSPECTOR_ACTIONVALIDATOR_H #define GAMMARAY_ACTIONINSPECTOR_ACTIONVALIDATOR_H #include #include #include QT_BEGIN_NAMESPACE class QAction; QT_END_NAMESPACE namespace GammaRay { class ActionValidator : public QObject { Q_OBJECT public: explicit ActionValidator(QObject *parent = nullptr); QList actions() const; QList actions(const QKeySequence &sequence) const; void setActions(const QList &actions); void clearActions(); void insert(QAction *action); void remove(QAction *action); /// helper method to find out if action has an ambiguous shortcut bool hasAmbiguousShortcut(const QAction *action) const; bool isAmbigous(const QAction *action, const QKeySequence &sequence) const; QVector findAmbiguousShortcuts(const QAction *action) const; private Q_SLOTS: void handleActionDestroyed(QObject *object); private: /// Does not deref the action pointer void safeRemove(QAction *action); // Multi-Map QHash m_shortcutActionMap; }; } #endif // GAMMARAY_ACTIONVALIDATOR_H gammaray-2.11.3/plugins/actioninspector/clientactionmodel.cpp000066400000000000000000000065031413310443000244570ustar00rootroot00000000000000/* clientactionmodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "clientactionmodel.h" #include "actionmodel.h" // for column/role enums #include #include using namespace GammaRay; ClientActionModel::ClientActionModel(QObject* parent) : QIdentityProxyModel(parent) { } ClientActionModel::~ClientActionModel() = default; QVariant ClientActionModel::data(const QModelIndex& index, int role) const { if (role == ActionModel::ObjectIdRole && index.column() != ActionModel::AddressColumn) { return index.sibling(index.row(), ActionModel::AddressColumn).data(ActionModel::ObjectIdRole); } else if (role == Qt::DecorationRole && index.column() == ActionModel::ShortcutsPropColumn) { const auto v = index.data(ActionModel::ShortcutConflictRole); const auto b = v.type() == QVariant::Bool ? v.toBool() : false; return b ? qApp->style()->standardIcon(QStyle::SP_MessageBoxWarning) : QVariant(); } else if (role == Qt::ToolTipRole && index.column() == ActionModel::ShortcutsPropColumn) { const auto v = index.data(ActionModel::ShortcutConflictRole); const auto b = v.type() == QVariant::Bool ? v.toBool() : false; return b ? tr("Warning: Ambiguous shortcut detected.") : QVariant(); } return QIdentityProxyModel::data(index, role); } QVariant ClientActionModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal) { if (role == Qt::DisplayRole) { switch (section) { case ActionModel::AddressColumn: return tr("Object"); case ActionModel::NameColumn: return tr("Name"); case ActionModel::CheckablePropColumn: return tr("Checkable"); case ActionModel::CheckedPropColumn: return tr("Checked"); case ActionModel::PriorityPropColumn: return tr("Priority"); case ActionModel::ShortcutsPropColumn: return tr("Shortcut(s)"); default: return QVariant(); } } return QVariant(); } return QIdentityProxyModel::headerData(section, orientation, role); } gammaray-2.11.3/plugins/actioninspector/clientactionmodel.h000066400000000000000000000032261413310443000241230ustar00rootroot00000000000000/* clientactionmodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_CLIENTACTIONMODEL_H #define GAMMARAY_CLIENTACTIONMODEL_H #include namespace GammaRay { class ClientActionModel : public QIdentityProxyModel { Q_OBJECT public: explicit ClientActionModel(QObject *parent = nullptr); ~ClientActionModel() override; QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; }; } #endif // GAMMARAY_CLIENTACTIONMODEL_H gammaray-2.11.3/plugins/actioninspector/gammaray_actioninspector.json000066400000000000000000000002661413310443000262330ustar00rootroot00000000000000{ "id": "gammaray_actioninspector", "name": "Actions", "name[de]": "Aktionen", "selectableTypes": [ "QAction" ], "types": [ "QAction" ] } gammaray-2.11.3/plugins/bluetooth/000077500000000000000000000000001413310443000170535ustar00rootroot00000000000000gammaray-2.11.3/plugins/bluetooth/CMakeLists.txt000066400000000000000000000003351413310443000216140ustar00rootroot00000000000000# probe plugin if (NOT GAMMARAY_CLIENT_ONLY_BUILD) gammaray_add_plugin(gammaray_bluetooth JSON gammaray_bluetooth.json SOURCES bluetooth.cpp) target_link_libraries(gammaray_bluetooth gammaray_core Qt5::Bluetooth) endif() gammaray-2.11.3/plugins/bluetooth/bluetooth.cpp000066400000000000000000000112421413310443000215640ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "bluetooth.h" #include #include #include #include #include #include #include #include #include #include #include Q_DECLARE_METATYPE(QBluetooth::SecurityFlags) Q_DECLARE_METATYPE(QBluetoothDeviceDiscoveryAgent::Error) Q_DECLARE_METATYPE(QBluetoothDeviceDiscoveryAgent::InquiryType) Q_DECLARE_METATYPE(QBluetoothServer::Error) Q_DECLARE_METATYPE(QBluetoothServiceInfo::Protocol) using namespace GammaRay; static QString bluetoothInquiryTypeToString(QBluetoothDeviceDiscoveryAgent::InquiryType type) { switch (type) { case QBluetoothDeviceDiscoveryAgent::GeneralUnlimitedInquiry: return QStringLiteral("Unlimited"); case QBluetoothDeviceDiscoveryAgent::LimitedInquiry: return QStringLiteral("Limited"); } Q_UNREACHABLE(); return QString(); } Bluetooth::Bluetooth(Probe *probe, QObject *parent) : QObject(parent) { Q_UNUSED(probe); qRegisterMetaType(); MetaObject *mo = nullptr; MO_ADD_METAOBJECT1(QBluetoothDeviceDiscoveryAgent, QObject); MO_ADD_PROPERTY_RO(QBluetoothDeviceDiscoveryAgent, error); MO_ADD_PROPERTY_RO(QBluetoothDeviceDiscoveryAgent, errorString); MO_ADD_PROPERTY_RO(QBluetoothDeviceDiscoveryAgent, isActive); MO_ADD_METAOBJECT1(QBluetoothLocalDevice, QObject); MO_ADD_PROPERTY_RO(QBluetoothLocalDevice, address); MO_ADD_PROPERTY_RO(QBluetoothLocalDevice, connectedDevices); MO_ADD_PROPERTY(QBluetoothLocalDevice, hostMode, setHostMode); MO_ADD_PROPERTY_RO(QBluetoothLocalDevice, isValid); MO_ADD_PROPERTY_RO(QBluetoothLocalDevice, name); MO_ADD_METAOBJECT1(QBluetoothServer, QObject); MO_ADD_PROPERTY_RO(QBluetoothServer, error); MO_ADD_PROPERTY_RO(QBluetoothServer, hasPendingConnections); MO_ADD_PROPERTY_RO(QBluetoothServer, isListening); MO_ADD_PROPERTY(QBluetoothServer, maxPendingConnections, setMaxPendingConnections); MO_ADD_PROPERTY(QBluetoothServer, securityFlags, setSecurityFlags); MO_ADD_PROPERTY_RO(QBluetoothServer, serverPort); MO_ADD_PROPERTY_RO(QBluetoothServer, serverType); MO_ADD_METAOBJECT1(QBluetoothServiceDiscoveryAgent, QObject); MO_ADD_PROPERTY_RO(QBluetoothServiceDiscoveryAgent, error); MO_ADD_PROPERTY_RO(QBluetoothServiceDiscoveryAgent, errorString); MO_ADD_PROPERTY_RO(QBluetoothServiceDiscoveryAgent, isActive); MO_ADD_PROPERTY_RO(QBluetoothServiceDiscoveryAgent, remoteAddress); MO_ADD_PROPERTY_O2(QBluetoothServiceDiscoveryAgent, uuidFilter, setUuidFilter); MO_ADD_METAOBJECT1(QBluetoothSocket, QIODevice); MO_ADD_PROPERTY_RO(QBluetoothSocket, error); MO_ADD_PROPERTY_RO(QBluetoothSocket, errorString); MO_ADD_PROPERTY_RO(QBluetoothSocket, localAddress); MO_ADD_PROPERTY_RO(QBluetoothSocket, localName); MO_ADD_PROPERTY_RO(QBluetoothSocket, localPort); MO_ADD_PROPERTY_RO(QBluetoothSocket, peerAddress); MO_ADD_PROPERTY_RO(QBluetoothSocket, peerName); MO_ADD_PROPERTY_RO(QBluetoothSocket, peerPort); MO_ADD_PROPERTY_RO(QBluetoothSocket, socketDescriptor); MO_ADD_PROPERTY_RO(QBluetoothSocket, socketType); MO_ADD_PROPERTY_RO(QBluetoothSocket, state); VariantHandler::registerStringConverter(std::mem_fn(&QBluetoothAddress::toString)); VariantHandler::registerStringConverter( bluetoothInquiryTypeToString); } gammaray-2.11.3/plugins/bluetooth/bluetooth.h000066400000000000000000000032761413310443000212410ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_BLUETOOTH_BLUETOOTH_H #define GAMMARAY_BLUETOOTH_BLUETOOTH_H #include namespace GammaRay { class Bluetooth : public QObject { Q_OBJECT public: explicit Bluetooth(Probe *probe, QObject *parent = nullptr); }; class BluetoothFactory : public QObject, public StandardToolFactory { Q_OBJECT Q_INTERFACES(GammaRay::ToolFactory) Q_PLUGIN_METADATA(IID "com.kdab.GammaRay.ToolFactory" FILE "gammaray_bluetooth.json") public: explicit BluetoothFactory(QObject *parent = nullptr) : QObject(parent) { } }; } #endif gammaray-2.11.3/plugins/bluetooth/gammaray_bluetooth.json000066400000000000000000000003231413310443000236270ustar00rootroot00000000000000{ "id": "gammaray_bluetooth", "name": "Bluetooth", "types": [ "QBluetoothDeviceDiscoveryAgent", "QBluetoothLocalDevice", "QBluetoothServiceDiscoveryAgent", "QBluetoothSocket" ], "hidden": true } gammaray-2.11.3/plugins/codecbrowser/000077500000000000000000000000001413310443000175275ustar00rootroot00000000000000gammaray-2.11.3/plugins/codecbrowser/CMakeLists.txt000066400000000000000000000012711413310443000222700ustar00rootroot00000000000000# probe part if (NOT GAMMARAY_CLIENT_ONLY_BUILD) set(gammaray_codecbrowser_plugin_srcs codecbrowser.cpp codecmodel.cpp ) gammaray_add_plugin(gammaray_codecbrowser_plugin JSON gammaray_codecbrowser.json SOURCES ${gammaray_codecbrowser_plugin_srcs} ) target_link_libraries(gammaray_codecbrowser_plugin gammaray_core ) endif() # ui part if(GAMMARAY_BUILD_UI) set(gammaray_codecbrowser_plugin_ui_srcs codecbrowserwidget.cpp ) gammaray_add_plugin(gammaray_codecbrowser_ui_plugin JSON gammaray_codecbrowser.json SOURCES ${gammaray_codecbrowser_plugin_ui_srcs} ) target_link_libraries(gammaray_codecbrowser_ui_plugin gammaray_common gammaray_ui ) endif() gammaray-2.11.3/plugins/codecbrowser/codecbrowser.cpp000066400000000000000000000050521413310443000227160ustar00rootroot00000000000000/* codecbrowser.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Stephen Kelly Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "codecbrowser.h" #include "codecmodel.h" #include #include #include using namespace GammaRay; CodecBrowser::CodecBrowser(Probe *probe, QObject *parent) : QObject(parent) { ObjectBroker::registerObject(QStringLiteral("com.kdab.GammaRay.CodecBrowser"), this); auto *model = new AllCodecsModel(this); probe->registerModel(QStringLiteral("com.kdab.GammaRay.AllCodecsModel"), model); m_codecSelectionModel = ObjectBroker::selectionModel(model); connect(m_codecSelectionModel, &QItemSelectionModel::selectionChanged, this, &CodecBrowser::updateCodecs); m_selectedCodecsModel = new SelectedCodecsModel(this); probe->registerModel(QStringLiteral( "com.kdab.GammaRay.SelectedCodecsModel"), m_selectedCodecsModel); } void CodecBrowser::textChanged(const QString &text) { m_selectedCodecsModel->updateText(text); } void CodecBrowser::updateCodecs(const QItemSelection &selected, const QItemSelection &deselected) { Q_UNUSED(selected); Q_UNUSED(deselected); const auto rows = m_codecSelectionModel->selectedRows(); QStringList currentCodecNames; currentCodecNames.reserve(rows.size()); for (const QModelIndex &index : rows) { const QString codecName = index.data().toString(); currentCodecNames.append(codecName); } m_selectedCodecsModel->setCodecs(currentCodecNames); } gammaray-2.11.3/plugins/codecbrowser/codecbrowser.h000066400000000000000000000042551413310443000223670ustar00rootroot00000000000000/* codecbrowser.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Stephen Kelly Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_CODECBROWSER_CODECBROWSER_H #define GAMMARAY_CODECBROWSER_CODECBROWSER_H #include QT_BEGIN_NAMESPACE class QItemSelection; class QItemSelectionModel; QT_END_NAMESPACE namespace GammaRay { class SelectedCodecsModel; namespace Ui { class CodecBrowser; } class CodecBrowser : public QObject { Q_OBJECT public: explicit CodecBrowser(Probe *probe, QObject *parent = nullptr); public slots: void textChanged(const QString &text); private slots: void updateCodecs(const QItemSelection &selected, const QItemSelection &deselected); private: SelectedCodecsModel *m_selectedCodecsModel; QItemSelectionModel *m_codecSelectionModel; }; class CodecBrowserFactory : public QObject, public StandardToolFactory { Q_OBJECT Q_INTERFACES(GammaRay::ToolFactory) Q_PLUGIN_METADATA(IID "com.kdab.GammaRay.ToolFactory" FILE "gammaray_codecbrowser.json") public: explicit CodecBrowserFactory(QObject *parent = nullptr) : QObject(parent) { } }; } #endif // GAMMARAY_CODECBROWSER_H gammaray-2.11.3/plugins/codecbrowser/codecbrowserwidget.cpp000066400000000000000000000051241413310443000241220ustar00rootroot00000000000000/* codecbrowserwidget.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Stephen Kelly Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "codecbrowserwidget.h" #include "ui_codecbrowserwidget.h" #include #include using namespace GammaRay; CodecBrowserWidget::CodecBrowserWidget(QWidget *parent) : QWidget(parent) , ui(new Ui::CodecBrowserWidget) , m_stateManager(this) { ui->setupUi(this); ui->codecList->header()->setObjectName("codecListHeader"); ui->codecList->setDeferredResizeMode(0, QHeaderView::ResizeToContents); ui->codecList->setModel(ObjectBroker::model(QStringLiteral("com.kdab.GammaRay.AllCodecsModel"))); ui->codecList->setSelectionModel(ObjectBroker::selectionModel(ui->codecList->model())); ui->selectedCodecs->header()->setObjectName("selectedCodecsHeader"); ui->selectedCodecs->setDeferredResizeMode(0, QHeaderView::ResizeToContents); ui->selectedCodecs->setModel(ObjectBroker::model(QStringLiteral( "com.kdab.GammaRay.SelectedCodecsModel"))); connect(ui->codecText, &QLineEdit::textChanged, this, &CodecBrowserWidget::textChanged); m_stateManager.setDefaultSizes(ui->mainSplitter, UISizeVector() << "50%" << "50%"); } CodecBrowserWidget::~CodecBrowserWidget() = default; void CodecBrowserWidget::textChanged(const QString &text) { Endpoint::instance()->invokeObject(QStringLiteral( "com.kdab.GammaRay.CodecBrowser"), "textChanged", QVariantList() << text); } gammaray-2.11.3/plugins/codecbrowser/codecbrowserwidget.h000066400000000000000000000037371413310443000235770ustar00rootroot00000000000000/* codecbrowserwidget.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Stephen Kelly Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_CODECBROWSERWIDGET_CODECBROWSERWIDGET_H #define GAMMARAY_CODECBROWSERWIDGET_CODECBROWSERWIDGET_H #include #include #include namespace GammaRay { namespace Ui { class CodecBrowserWidget; } class CodecBrowserWidget : public QWidget { Q_OBJECT public: explicit CodecBrowserWidget(QWidget *parent = nullptr); ~CodecBrowserWidget() override; private slots: void textChanged(const QString &text); private: QScopedPointer ui; UIStateManager m_stateManager; }; class CodecBrowserUiFactory : public QObject, public StandardToolUiFactory { Q_OBJECT Q_INTERFACES(GammaRay::ToolUiFactory) Q_PLUGIN_METADATA(IID "com.kdab.GammaRay.ToolUiFactory" FILE "gammaray_codecbrowser.json") }; } #endif // GAMMARAY_CODECBROWSERWIDGET_H gammaray-2.11.3/plugins/codecbrowser/codecbrowserwidget.ui000066400000000000000000000032701413310443000237550ustar00rootroot00000000000000 GammaRay::CodecBrowserWidget 0 0 400 300 Qt::Horizontal QAbstractItemView::ExtendedSelection false Enter text for preview... false GammaRay::DeferredTreeView QTreeView
ui/deferredtreeview.h
gammaray-2.11.3/plugins/codecbrowser/codecmodel.cpp000066400000000000000000000102261413310443000223320ustar00rootroot00000000000000/* codecmodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Stephen Kelly Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "codecmodel.h" #include using namespace GammaRay; AllCodecsModel::AllCodecsModel(QObject *parent) : QAbstractTableModel(parent) { m_codecs = QTextCodec::availableCodecs(); } int AllCodecsModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); return 2; } QVariant AllCodecsModel::data(const QModelIndex &index, int role) const { if (role == Qt::DisplayRole) { if (index.column() == 0) return m_codecs.at(index.row()); if (index.column() == 1) { const QList aliases = QTextCodec::codecForName(m_codecs.at(index.row()))->aliases(); QString result; int size = aliases.size(); int i = 0; for (const QByteArray &ba : aliases) { result.append(ba); ++i; if (i != size) result.append(", "); } return result; } } return QVariant(); } QVariant AllCodecsModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { if (section == 0) return tr("Codec"); else if (section == 1) return tr("Aliases"); } return QVariant(); } int AllCodecsModel::rowCount(const QModelIndex &parent) const { if (parent.isValid()) return 0; return m_codecs.size(); } SelectedCodecsModel::SelectedCodecsModel(QObject *parent) : QAbstractTableModel(parent) { } void SelectedCodecsModel::setCodecs(const QStringList &codecs) { beginResetModel(); m_codecs = codecs; endResetModel(); } QStringList SelectedCodecsModel::currentCodecs() const { return m_codecs; } void SelectedCodecsModel::updateText(const QString &text) { m_text = text; if (!m_codecs.isEmpty()) { emit dataChanged(index(0, 1), index(m_codecs.size() - 1, 1)); } } QVariant SelectedCodecsModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { if (section == 0) return tr("Codec"); if (section == 1) return tr("Encoded Data"); } return QAbstractTableModel::headerData(section, orientation, role); } int SelectedCodecsModel::rowCount(const QModelIndex &parent) const { if (parent.isValid()) return 0; return m_codecs.size(); } int SelectedCodecsModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); return 2; } QVariant SelectedCodecsModel::data(const QModelIndex &index, int role) const { if (index.column() == 0) { if (role == Qt::DisplayRole) return m_codecs.at(index.row()); } else if (index.column() == 1) { if (role == Qt::DisplayRole) { const QByteArray ba = QTextCodec::codecForName(m_codecs.at(index.row()).toLatin1())->fromUnicode(m_text); // QString result; // foreach () return ba.toHex(); } } return QVariant(); } gammaray-2.11.3/plugins/codecbrowser/codecmodel.h000066400000000000000000000050561413310443000220040ustar00rootroot00000000000000/* codecmodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Stephen Kelly Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_CODECBROWSER_CODECMODEL_H #define GAMMARAY_CODECBROWSER_CODECMODEL_H #include #include #include namespace GammaRay { class AllCodecsModel : public QAbstractTableModel { Q_OBJECT public: explicit AllCodecsModel(QObject *parent = nullptr); QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; private: QList m_codecs; }; class SelectedCodecsModel : public QAbstractTableModel { Q_OBJECT public: explicit SelectedCodecsModel(QObject *parent = nullptr); void setCodecs(const QStringList &codecs); QStringList currentCodecs() const; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; public slots: void updateText(const QString &text); private: QStringList m_codecs; QString m_text; }; } #endif gammaray-2.11.3/plugins/codecbrowser/gammaray_codecbrowser.json000066400000000000000000000001351413310443000247600ustar00rootroot00000000000000{ "id": "gammaray_codecbrowser", "name": "Text Codecs", "types": [ "QObject" ] } gammaray-2.11.3/plugins/eventmonitor/000077500000000000000000000000001413310443000175775ustar00rootroot00000000000000gammaray-2.11.3/plugins/eventmonitor/CMakeLists.txt000066400000000000000000000016711413310443000223440ustar00rootroot00000000000000# probe part if (NOT GAMMARAY_CLIENT_ONLY_BUILD) set(gammaray_eventmonitor_plugin_srcs eventmonitor.cpp eventmodel.cpp eventmonitorinterface.cpp eventtypemodel.cpp eventtypefilter.cpp ) gammaray_add_plugin(gammaray_eventmonitor_plugin JSON gammaray_eventmonitor.json SOURCES ${gammaray_eventmonitor_plugin_srcs} ) target_include_directories(gammaray_eventmonitor_plugin SYSTEM PRIVATE ${Qt5Core_PRIVATE_INCLUDE_DIRS}) target_link_libraries(gammaray_eventmonitor_plugin Qt5::Gui gammaray_core ) endif() # ui part if(GAMMARAY_BUILD_UI) set(gammaray_eventmonitor_ui_plugin_srcs eventmonitorwidget.cpp eventmonitorinterface.cpp eventmonitorclient.cpp eventtypeclientproxymodel.cpp ) gammaray_add_plugin(gammaray_eventmonitor_ui_plugin JSON gammaray_eventmonitor.json SOURCES ${gammaray_eventmonitor_ui_plugin_srcs} ) target_link_libraries(gammaray_eventmonitor_ui_plugin gammaray_ui ) endif() gammaray-2.11.3/plugins/eventmonitor/eventmodel.cpp000066400000000000000000000143271413310443000224540ustar00rootroot00000000000000/* eventmodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Tim Henning Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "eventmodel.h" #include "eventmodelroles.h" #include #include #include #include #include #include #include #include #include #include using namespace GammaRay; static const int TopLevelId = std::numeric_limits::max(); EventModel::EventModel(QObject *parent) : QAbstractItemModel(parent) , m_pendingEventTimer(new QTimer(this)) { qRegisterMetaType(); m_pendingEventTimer->setSingleShot(true); m_pendingEventTimer->setInterval(200); connect(m_pendingEventTimer, &QTimer::timeout, this, [this]() { Q_ASSERT(!m_pendingEvents.isEmpty()); beginInsertRows(QModelIndex(), m_events.count(), m_events.count() + m_pendingEvents.size() - 1); m_events += m_pendingEvents; m_pendingEvents.clear(); endInsertRows(); }); } EventModel::~EventModel() = default; void EventModel::addEvent(const EventData &event) { m_pendingEvents.push_back(event); if (!m_pendingEventTimer->isActive()) { m_pendingEventTimer->start(); } } void EventModel::clear() { beginResetModel(); m_events.clear(); endResetModel(); } int EventModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); return EventModelColumn::COUNT; } int EventModel::rowCount(const QModelIndex &parent) const { if (!parent.isValid()) return m_events.size(); if (parent.internalId() == TopLevelId && parent.column() == 0) { const EventData &event = m_events.at(parent.row()); return event.propagatedEvents.size(); } return 0; } QVariant EventModel::data(const QModelIndex &index, int role) const { if (!index.isValid() || index.column() >= columnCount()) return QVariant(); bool isPropagatedEvent = index.internalId() != TopLevelId; int rootEventIndex = isPropagatedEvent ? int(index.internalId()) : index.row(); Q_ASSERT(rootEventIndex >= 0 && rootEventIndex < m_events.size()); const EventData &event = isPropagatedEvent ? m_events.at(rootEventIndex).propagatedEvents.at(index.row()) : m_events.at(rootEventIndex); if (role == Qt::DisplayRole) { switch (index.column()) { case EventModelColumn::Time: return isPropagatedEvent ? "" : event.time.toString("hh:mm:ss.zzz"); case EventModelColumn::Type: { const auto s = VariantHandler::displayString(event.type); if (!s.isEmpty()) { return s; } return event.type; } case EventModelColumn::Receiver: { QMutexLocker lock(Probe::objectLock()); if (Probe::instance()->isValidObject(event.receiver)) { return Util::displayString(event.receiver); } return Util::addressToString(event.receiver); } } } else if (role == EventModelRole::AttributesRole) { QVariantMap attributesMap; for (const QPair& pair: event.attributes) { attributesMap.insert(QString::fromUtf8(pair.first), pair.second); } return attributesMap; } else if (role == EventModelRole::ReceiverIdRole && index.column() == EventModelColumn::Receiver) { return QVariant::fromValue(ObjectId(event.receiver)); } else if (role == EventModelRole::EventTypeRole) { return QVariant::fromValue(event.type); } return QVariant(); } QVariant EventModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role == Qt::DisplayRole && orientation == Qt::Horizontal) { switch (section) { case EventModelColumn::Time: return tr("Time"); case EventModelColumn::Type: return tr("Type"); case EventModelColumn::Receiver: return tr("Receiver"); } } return QVariant(); } QModelIndex EventModel::index(int row, int column, const QModelIndex &parent) const { if (row < 0 || column < 0 || column >= columnCount()) return {}; if (parent.isValid()) { if (row >= m_events.at(parent.row()).propagatedEvents.size()) return QModelIndex(); return createIndex(row, column, static_cast(parent.row())); } return createIndex(row, column, TopLevelId); } QModelIndex EventModel::parent(const QModelIndex &child) const { if (!child.isValid() || child.internalId() == TopLevelId) return {}; return createIndex(int(child.internalId()), 0, TopLevelId); } QMap EventModel::itemData(const QModelIndex& index) const { auto d = QAbstractItemModel::itemData(index); if (index.column() == EventModelColumn::Receiver) { d.insert(EventModelRole::ReceiverIdRole, index.data(EventModelRole::ReceiverIdRole)); } return d; } bool EventModel::hasEvents() const { return !m_events.empty() || !m_pendingEvents.empty(); } EventData& EventModel::lastEvent() { if (!m_pendingEvents.empty()) { return m_pendingEvents.last(); } return m_events.last(); } gammaray-2.11.3/plugins/eventmonitor/eventmodel.h000066400000000000000000000054771413310443000221270ustar00rootroot00000000000000/* eventmodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Tim Henning Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_EVENTMONITOR_EVENTMODEL_H #define GAMMARAY_EVENTMONITOR_EVENTMODEL_H #include #include #include #include #include #include QT_BEGIN_NAMESPACE class QTimer; QT_END_NAMESPACE namespace GammaRay { struct EventData { QTime time; QEvent::Type type; QObject* receiver; QVector> attributes; QEvent* eventPtr; QVector propagatedEvents; }; } Q_DECLARE_METATYPE(GammaRay::EventData) QT_BEGIN_NAMESPACE Q_DECLARE_TYPEINFO(GammaRay::EventData, Q_MOVABLE_TYPE); QT_END_NAMESPACE namespace GammaRay { class EventModel : public QAbstractItemModel { Q_OBJECT public: explicit EventModel(QObject *parent = nullptr); ~EventModel() override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; QModelIndex parent(const QModelIndex &child) const override; QMap itemData(const QModelIndex & index) const override; bool hasEvents() const; EventData& lastEvent(); public slots: void addEvent(const GammaRay::EventData &event); void clear(); private: QVector m_events; QVector m_pendingEvents; QTimer *m_pendingEventTimer; }; } #endif // GAMMARAY_EVENTMONITOR_EVENTMODEL_H gammaray-2.11.3/plugins/eventmonitor/eventmodelroles.h000066400000000000000000000030601413310443000231560ustar00rootroot00000000000000/* eventmodelroles.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Tim Henning Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_EVENTMODELROLES #define GAMMARAY_EVENTMODELROLES #include namespace GammaRay { /** Additional roles for EventModel. */ namespace EventModelRole { enum Role { AttributesRole = UserRole + 1, ReceiverIdRole, EventTypeRole }; } /** Column indexes of EventModel. */ namespace EventModelColumn { enum Columns { Time, Type, Receiver, COUNT }; } } #endif // GAMMARAY_EVENTMODELROLES gammaray-2.11.3/plugins/eventmonitor/eventmonitor.cpp000066400000000000000000000361731413310443000230460ustar00rootroot00000000000000/* styleinspector.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2012-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Tim Henning Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "eventmonitor.h" #include "eventmodel.h" #include "eventmodelroles.h" #include "eventmonitorinterface.h" #include "eventtypefilter.h" #include "eventtypemodel.h" #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace GammaRay; static EventModel *s_model = nullptr; static EventTypeModel *s_eventTypeModel = nullptr; static EventMonitor *s_eventMonitor = nullptr; QString eventTypeToClassName(QEvent::Type type) { switch (type) { case QEvent::NonClientAreaMouseMove: case QEvent::NonClientAreaMouseButtonPress: case QEvent::NonClientAreaMouseButtonRelease: case QEvent::NonClientAreaMouseButtonDblClick: case QEvent::MouseButtonDblClick: case QEvent::MouseButtonPress: case QEvent::MouseButtonRelease: case QEvent::MouseMove: return QStringLiteral("QMouseEvent"); case QEvent::TouchBegin: case QEvent::TouchUpdate: case QEvent::TouchEnd: case QEvent::TouchCancel: return QStringLiteral("QTouchEvent"); case QEvent::ScrollPrepare: return QStringLiteral("QScrollPrepareEvent"); case QEvent::Scroll: return QStringLiteral("QScrollEvent"); case QEvent::TabletMove: case QEvent::TabletPress: case QEvent::TabletRelease: case QEvent::TabletEnterProximity: case QEvent::TabletLeaveProximity: return QStringLiteral("QTabletEvent"); case QEvent::NativeGesture: return QStringLiteral("QNativeGestureEvent"); case QEvent::KeyPress: case QEvent::KeyRelease: case QEvent::ShortcutOverride: return QStringLiteral("QKeyEvent"); case QEvent::Shortcut: return QStringLiteral("QShortcutEvent"); case QEvent::InputMethod: return QStringLiteral("QInputMethodEvent"); case QEvent::InputMethodQuery: return QStringLiteral("QInputMethodQueryEvent"); case QEvent::OrientationChange: return QStringLiteral("QScreenOrientationChangeEvent"); case QEvent::WindowStateChange: return QStringLiteral("QWindowStateChangeEvent"); case QEvent::ApplicationStateChange: return QStringLiteral("QApplicationStateChangeEvent"); case QEvent::Expose: return QStringLiteral("QExposeEvent"); case QEvent::Resize: return QStringLiteral("QResizeEvent"); case QEvent::FocusIn: case QEvent::FocusOut: case QEvent::FocusAboutToChange: return QStringLiteral("QFocusEvent"); case QEvent::Move: return QStringLiteral("QMoveEvent"); case QEvent::Paint: return QStringLiteral("QPaintEvent"); case QEvent::Enter: return QStringLiteral("QEnterEvent"); case QEvent::Wheel: return QStringLiteral("QWheelEvent"); case QEvent::HoverEnter: case QEvent::HoverMove: case QEvent::HoverLeave: return QStringLiteral("QHoverEvent"); case QEvent::DynamicPropertyChange: return QStringLiteral("QDynamicPropertyChangeEvent"); case QEvent::DeferredDelete: return QStringLiteral("QDeferredDeleteEvent"); case QEvent::ChildAdded: case QEvent::ChildPolished: case QEvent::ChildRemoved: return QStringLiteral("QChildEvent"); case QEvent::Timer: return QStringLiteral("QTimerEvent"); case QEvent::MetaCall: return QStringLiteral("QMetaCallEvent"); // about to change in 5.14? see https://code.qt.io/cgit/qt/qtbase.git/commit/?h=dev&id=999c26dd83ad37fcd7a2b2fc62c0281f38c8e6e0 case QEvent::ActionAdded: case QEvent::ActionChanged: case QEvent::ActionRemoved: return QStringLiteral("QActionEvent"); case QEvent::ContextMenu: return QStringLiteral("QContextMenuEvent"); case QEvent::Drop: return QStringLiteral("QDropEvent"); case QEvent::DragEnter: case QEvent::DragMove: return QStringLiteral("QDragMoveEvent"); case QEvent::GraphicsSceneHelp: case QEvent::QueryWhatsThis: case QEvent::ToolTip: return QStringLiteral("QHelpEvent"); case QEvent::StatusTip: return QStringLiteral("QStatusTip"); default: return QString(); } } bool isInputEvent(QEvent::Type type) { switch (type) { case QEvent::NonClientAreaMouseMove: case QEvent::NonClientAreaMouseButtonPress: case QEvent::NonClientAreaMouseButtonRelease: case QEvent::NonClientAreaMouseButtonDblClick: case QEvent::MouseButtonDblClick: case QEvent::MouseButtonPress: case QEvent::MouseButtonRelease: case QEvent::MouseMove: case QEvent::TouchBegin: case QEvent::TouchUpdate: case QEvent::TouchEnd: case QEvent::TouchCancel: case QEvent::Scroll: case QEvent::TabletMove: case QEvent::TabletPress: case QEvent::TabletRelease: case QEvent::TabletEnterProximity: case QEvent::TabletLeaveProximity: case QEvent::NativeGesture: case QEvent::KeyPress: case QEvent::KeyRelease: case QEvent::Wheel: case QEvent::HoverEnter: case QEvent::HoverMove: case QEvent::HoverLeave: case QEvent::Drop: case QEvent::DragEnter: case QEvent::DragMove: return true; default: return false; } } bool shouldBeRecorded(QObject* receiver, QEvent* event) { if (!s_model || !s_eventTypeModel || !s_eventMonitor || !Probe::instance()) { return false; } if (s_eventMonitor->isPaused()) { return false; } if (!event || !receiver) { return false; } if (!s_eventTypeModel->isRecording(event->type())) { return false; } if (Probe::instance()->filterObject(receiver)) { return false; } return true; } EventData createEventData(QObject* receiver, QEvent* event) { EventData eventData; eventData.time = QTime::currentTime(); eventData.type = event->type(); eventData.receiver = receiver; eventData.attributes << QPair{"receiver", QVariant::fromValue(receiver)}; eventData.eventPtr = event; // the receiver of a deferred delete event is almost always invalid when shown in the UI // we therefore store the name of the receiver as a string to provide at least // some useful information: if (event->type() == QEvent::DeferredDelete) { eventData.attributes << QPair{"[receiver type]", Util::displayString(receiver)}; } // try to extract the method name, arguments and return value from a meta call event: if (event->type() == QEvent::MetaCall) { eventData.attributes << QPair{"[receiver type]", Util::displayString(receiver)}; // QMetaCallEvent about to change in 5.14? see https://code.qt.io/cgit/qt/qtbase.git/commit/?h=dev&id=999c26dd83ad37fcd7a2b2fc62c0281f38c8e6e0 QMetaCallEvent* metaCallEvent = static_cast(event); if (metaCallEvent) { int methodIndex = metaCallEvent->id(); if (methodIndex == int(ushort(-1))) { // TODO: this is a slot call, but QMetaCall::slotObj is private eventData.attributes << QPair{"[method name]", "[unknown slot]"}; } else { // TODO: should first check if nargs and types is set, but both are private const QMetaObject *meta = receiver->metaObject(); if (meta) { QMetaMethod method = meta->method(metaCallEvent->id()); eventData.attributes << QPair{"[method name]", method.name()}; void** argv = metaCallEvent->args(); if (argv) { // nullptr e.g. for QDBusCallDeliveryEvent if (method.returnType() != QMetaType::Void) { void* returnValueCopy = QMetaType::create(method.returnType(), argv[0]); eventData.attributes << QPair{"[return value]", QVariant(method.returnType(), returnValueCopy)}; } int argc = method.parameterCount(); QVariantMap vargs; for (int i = 0; i < argc; ++i) { int type = method.parameterType(i); void* argumentDataCopy = QMetaType::create(type, argv[i+1]); vargs.insert(method.parameterNames().at(i), QVariant(type, argumentDataCopy)); } if (argc > 0) eventData.attributes << QPair{"[arguments]", vargs}; } } } } } // store all other meta properties: QString className = eventTypeToClassName(event->type()); if (!className.isEmpty()) { MetaObject *metaObj = MetaObjectRepository::instance()->metaObject(className); if (metaObj) { for (int i=0; ipropertyCount(); ++i) { MetaProperty* prop = metaObj->propertyAt(i); if (strcmp(prop->name(), "type") == 0) continue; eventData.attributes << QPair{prop->name(), prop->value(event)}; } } } return eventData; } void EventMonitor::addEvent(const GammaRay::EventData &event) { m_eventModel->addEvent(event); m_eventTypeModel->increaseCount(event.type); } static bool eventCallback(void **data) { QEvent *event = reinterpret_cast(data[1]); QObject *receiver = reinterpret_cast(data[0]); if (!shouldBeRecorded(receiver, event)) return false; EventData eventData = createEventData(receiver, event); if (!event->spontaneous() && isInputEvent(event->type()) && s_model->hasEvents() && s_model->lastEvent().eventPtr == eventData.eventPtr && s_model->lastEvent().type == event->type()) { // this is an event propagated by a QQuickWindow to a child item: s_model->lastEvent().propagatedEvents.append(eventData); return false; } // add directly from foreground thread, delay from background thread QMetaObject::invokeMethod(s_eventMonitor, "addEvent", Qt::AutoConnection, Q_ARG(GammaRay::EventData, eventData)); return false; } EventPropagationListener::EventPropagationListener(QObject *parent) : QObject(parent) {} bool EventPropagationListener::eventFilter(QObject *receiver, QEvent *event) { if (!s_model) return false; if (!s_model->hasEvents()) return false; EventData& lastEvent = s_model->lastEvent(); if (lastEvent.eventPtr == event && lastEvent.receiver == receiver) { // this is the same event we already recorded in the event callback return false; } if (!lastEvent.propagatedEvents.isEmpty() && lastEvent.propagatedEvents.last().eventPtr == event) { // this is an event propagated by QML that is already recorded in the event callback return false; } if (!shouldBeRecorded(receiver, event)) return false; if (event->type() != lastEvent.type) { // a new event was created during the propagation EventData newEvent = createEventData(receiver, event); s_model->addEvent(newEvent); s_eventTypeModel->increaseCount(event->type()); return false; } EventData propagatedEvent = createEventData(receiver, event); lastEvent.propagatedEvents.append(propagatedEvent); return false; } EventMonitor::EventMonitor(Probe *probe, QObject *parent) : EventMonitorInterface(parent) , m_eventModel(new EventModel(this)) , m_eventTypeModel(new EventTypeModel(this)) , m_eventPropertyModel(new AggregatedPropertyModel(this)) { Q_ASSERT(s_model == nullptr); s_model = m_eventModel; Q_ASSERT(s_eventTypeModel == nullptr); s_eventTypeModel = m_eventTypeModel; Q_ASSERT(s_eventMonitor == nullptr); s_eventMonitor = this; QInternal::registerCallback(QInternal::EventNotifyCallback, eventCallback); QCoreApplication::instance()->installEventFilter(new EventPropagationListener(this)); auto filterProxy = new ServerProxyModel(this); filterProxy->setEventTypeModel(m_eventTypeModel); filterProxy->setSourceModel(m_eventModel); connect(m_eventTypeModel, &EventTypeModel::typeVisibilityChanged, filterProxy, &QSortFilterProxyModel::invalidate); probe->registerModel(QStringLiteral("com.kdab.GammaRay.EventModel"), filterProxy); auto evenTypeProxy = new ServerProxyModel(this); evenTypeProxy->setSourceModel(m_eventTypeModel); probe->registerModel(QStringLiteral("com.kdab.GammaRay.EventTypeModel"), evenTypeProxy); probe->registerModel(QStringLiteral("com.kdab.GammaRay.EventPropertyModel"), m_eventPropertyModel); QItemSelectionModel *selectionModel = ObjectBroker::selectionModel(filterProxy); connect(selectionModel, &QItemSelectionModel::selectionChanged, this, &EventMonitor::eventSelected); } void EventMonitor::eventSelected(const QItemSelection &selection) { if (selection.isEmpty()) return; const QModelIndex index = selection.first().topLeft(); QVariant eventAttributes = QVariant(index.data(EventModelRole::AttributesRole).value()); m_eventPropertyModel->setObject(eventAttributes); } EventMonitor::~EventMonitor() { s_model = nullptr; s_eventTypeModel = nullptr; s_eventMonitor = nullptr; QInternal::unregisterCallback(QInternal::EventNotifyCallback, eventCallback); } void EventMonitor::clearHistory() { m_eventModel->clear(); m_eventTypeModel->resetCounts(); } void EventMonitor::recordAll() { m_eventTypeModel->recordAll(); } void EventMonitor::recordNone() { m_eventTypeModel->recordNone(); } void EventMonitor::showAll() { m_eventTypeModel->showAll(); } void EventMonitor::showNone() { m_eventTypeModel->showNone(); } gammaray-2.11.3/plugins/eventmonitor/eventmonitor.h000066400000000000000000000052561413310443000225110ustar00rootroot00000000000000/* eventmonitor.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2012-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Tim Henning Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_EVENTMONITOR_EVENTMONITOR_H #define GAMMARAY_EVENTMONITOR_EVENTMONITOR_H #include #include "eventmonitorinterface.h" #include QT_BEGIN_NAMESPACE class QItemSelection; QT_END_NAMESPACE namespace GammaRay { class AggregatedPropertyModel; struct EventData; class EventModel; class EventTypeModel; class EventPropagationListener : public QObject { Q_OBJECT public: explicit EventPropagationListener(QObject *parent); virtual bool eventFilter(QObject *receiver, QEvent *event) override; }; class EventMonitor : public EventMonitorInterface { Q_OBJECT Q_INTERFACES(GammaRay::EventMonitorInterface) public: explicit EventMonitor(Probe *probe, QObject *parent = nullptr); ~EventMonitor() override; public slots: void clearHistory() override; void recordAll() override; void recordNone() override; void showAll() override; void showNone() override; void addEvent(const GammaRay::EventData &event); private slots: void eventSelected(const QItemSelection &selection); private: EventModel *m_eventModel; EventTypeModel *m_eventTypeModel; AggregatedPropertyModel *m_eventPropertyModel; }; class EventMonitorFactory : public QObject, public StandardToolFactory { Q_OBJECT Q_INTERFACES(GammaRay::ToolFactory) Q_PLUGIN_METADATA(IID "com.kdab.GammaRay.ToolFactory" FILE "gammaray_eventmonitor.json") public: explicit EventMonitorFactory(QObject *parent = nullptr) : QObject(parent) { } }; } #endif // GAMMARAY_EVENTMONITOR_H gammaray-2.11.3/plugins/eventmonitor/eventmonitorclient.cpp000066400000000000000000000036261413310443000242420ustar00rootroot00000000000000 /* eventmonitorclient.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2019-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Tim Henning Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "eventmonitorclient.h" #include using namespace GammaRay; GammaRay::EventMonitorClient::EventMonitorClient(QObject *parent) : EventMonitorInterface(parent) { } GammaRay::EventMonitorClient::~EventMonitorClient() = default; void GammaRay::EventMonitorClient::clearHistory() { Endpoint::instance()->invokeObject(objectName(), "clearHistory"); } void EventMonitorClient::recordAll() { Endpoint::instance()->invokeObject(objectName(), "recordAll"); } void EventMonitorClient::recordNone() { Endpoint::instance()->invokeObject(objectName(), "recordNone"); } void EventMonitorClient::showAll() { Endpoint::instance()->invokeObject(objectName(), "showAll"); } void EventMonitorClient::showNone() { Endpoint::instance()->invokeObject(objectName(), "showNone"); } gammaray-2.11.3/plugins/eventmonitor/eventmonitorclient.h000066400000000000000000000033351413310443000237040ustar00rootroot00000000000000 /* eventmonitorclient.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2019-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Tim Henning Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_EVENTMONITORCLIENT_H #define GAMMARAY_EVENTMONITORCLIENT_H #include "eventmonitorinterface.h" namespace GammaRay { class EventMonitorClient : public EventMonitorInterface { Q_OBJECT Q_INTERFACES(GammaRay::EventMonitorInterface) public: explicit EventMonitorClient(QObject *parent = nullptr); ~EventMonitorClient() override; public slots: virtual void clearHistory() override; virtual void recordAll() override; virtual void recordNone() override; virtual void showAll() override; virtual void showNone() override; }; } #endif // GAMMARAY_EVENTMONITORCLIENT_H gammaray-2.11.3/plugins/eventmonitor/eventmonitorinterface.cpp000066400000000000000000000030631413310443000247170ustar00rootroot00000000000000/* eventmonitorinterface.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2019-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Tim Henning Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "eventmonitorinterface.h" #include using namespace GammaRay; EventMonitorInterface::EventMonitorInterface(QObject *parent) : QObject(parent) , m_isPaused(false) { ObjectBroker::registerObject(this); } void EventMonitorInterface::setIsPaused(bool value) { m_isPaused = value; emit isPausedChanged(); } EventMonitorInterface::~EventMonitorInterface() = default; gammaray-2.11.3/plugins/eventmonitor/eventmonitorinterface.h000066400000000000000000000041251413310443000243640ustar00rootroot00000000000000/* eventmonitorinterface.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2019-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Tim Henning Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_EVENTMONITOR_EVENTMONITORINTERFACE_H #define GAMMARAY_EVENTMONITOR_EVENTMONITORINTERFACE_H #include QT_BEGIN_NAMESPACE class QSize; QT_END_NAMESPACE namespace GammaRay { class EventMonitorInterface : public QObject { Q_OBJECT Q_PROPERTY(bool isPaused READ isPaused WRITE setIsPaused NOTIFY isPausedChanged) public: explicit EventMonitorInterface(QObject *parent = nullptr); ~EventMonitorInterface() override; public slots: virtual void clearHistory() = 0; virtual void recordAll() = 0; virtual void recordNone() = 0; virtual void showAll() = 0; virtual void showNone() = 0; bool isPaused() const { return m_isPaused; } void setIsPaused(bool value); signals: void isPausedChanged(); private: bool m_isPaused; }; } QT_BEGIN_NAMESPACE Q_DECLARE_INTERFACE(GammaRay::EventMonitorInterface, "com.kdab.GammaRay.EventMonitorInterface") QT_END_NAMESPACE #endif // GAMMARAY_EVENTMONITOR_EVENTMONITORINTERFACE_H gammaray-2.11.3/plugins/eventmonitor/eventmonitorwidget.cpp000066400000000000000000000131721413310443000242440ustar00rootroot00000000000000/* eventmonitorwidget.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2012-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Tim Henning Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "eventmonitorwidget.h" #include "ui_eventmonitorwidget.h" #include "eventmodelroles.h" #include "eventmonitorclient.h" #include "eventtypemodel.h" #include "eventtypeclientproxymodel.h" #include #include #include #include #include #include #include #include static QObject *createEventMonitorClient(const QString & /*name*/, QObject *parent) { return new GammaRay::EventMonitorClient(parent); } using namespace GammaRay; EventMonitorWidget::EventMonitorWidget(QWidget *parent) : QWidget(parent) , ui(new Ui::EventMonitorWidget) , m_interface(nullptr) { ObjectBroker::registerClientObjectFactoryCallback(createEventMonitorClient); m_interface = ObjectBroker::object(); ui->setupUi(this); QAbstractItemModel * const eventModel = ObjectBroker::model(QStringLiteral("com.kdab.GammaRay.EventModel")); new SearchLineController(ui->eventSearchLine, eventModel); ui->eventTree->setDeferredResizeMode(EventModelColumn::Time, QHeaderView::ResizeToContents); ui->eventTree->setModel(eventModel); ui->eventTree->setSelectionModel(ObjectBroker::selectionModel(ui->eventTree->model())); ui->eventTree->header()->setSectionsClickable(false); // we do not want sorting here connect(ui->eventTree, &QTreeView::customContextMenuRequested, this, &EventMonitorWidget::eventTreeContextMenu); connect(ui->pauseButton, &QAbstractButton::toggled, this, &EventMonitorWidget::pauseAndResume); connect(ui->clearButton, &QAbstractButton::pressed, m_interface, &EventMonitorInterface::clearHistory); auto clientPropModel = new ClientPropertyModel(this); clientPropModel->setSourceModel(ObjectBroker::model(QStringLiteral("com.kdab.GammaRay.EventPropertyModel"))); ui->eventInspector->setModel(clientPropModel); ui->eventInspector->setItemDelegate(new PropertyEditorDelegate(this)); connect(ui->eventInspector, &QTreeView::customContextMenuRequested, this, &EventMonitorWidget::eventInspectorContextMenu); QAbstractItemModel * const eventTypeModel = ObjectBroker::model(QStringLiteral("com.kdab.GammaRay.EventTypeModel")); new SearchLineController(ui->typeSearchLine, eventTypeModel); EventTypeClientProxyModel * const eventTypeProxyModel = new EventTypeClientProxyModel(this); eventTypeProxyModel->setSourceModel(eventTypeModel); ui->eventTypeTree->sortByColumn(EventTypeModel::Columns::Type, Qt::AscendingOrder); ui->eventTypeTree->setDeferredResizeMode(EventTypeModel::Columns::Type, QHeaderView::Stretch); ui->eventTypeTree->setModel(eventTypeProxyModel); connect(ui->recordAllButton, &QAbstractButton::pressed, m_interface, &EventMonitorInterface::recordAll); connect(ui->recordNoneButton, &QAbstractButton::pressed, m_interface, &EventMonitorInterface::recordNone); connect(ui->showAllButton, &QAbstractButton::pressed, m_interface, &EventMonitorInterface::showAll); connect(ui->showNoneButton, &QAbstractButton::pressed, m_interface, &EventMonitorInterface::showNone); } EventMonitorWidget::~EventMonitorWidget() { delete ui; } void EventMonitorWidget::pauseAndResume(bool pause) { m_interface->setIsPaused(pause); } void EventMonitorWidget::eventTreeContextMenu(QPoint pos) { auto index = ui->eventTree->indexAt(pos); if (!index.isValid()) return; index = index.sibling(index.row(), EventModelColumn::Receiver); const auto objectId = index.data(EventModelRole::ReceiverIdRole).value(); if (objectId.isNull()) return; QMenu menu; ContextMenuExtension ext(objectId); ext.populateMenu(&menu); menu.exec(ui->eventTree->viewport()->mapToGlobal(pos)); } void EventMonitorWidget::eventInspectorContextMenu(QPoint pos) { const auto idx = ui->eventInspector->indexAt(pos); if (!idx.isValid()) return; const auto actions = idx.data(PropertyModel::ActionRole).toInt(); const auto objectId = idx.data(PropertyModel::ObjectIdRole).value(); ContextMenuExtension ext(objectId); const bool canShow = (actions == PropertyModel::NavigateTo && !objectId.isNull()) || ext.discoverPropertySourceLocation(ContextMenuExtension::GoTo, idx); if (!canShow) return; QMenu contextMenu; ext.populateMenu(&contextMenu); contextMenu.exec(ui->eventInspector->viewport()->mapToGlobal(pos)); } gammaray-2.11.3/plugins/eventmonitor/eventmonitorwidget.h000066400000000000000000000040401413310443000237030ustar00rootroot00000000000000/* eventmonitorwidget.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2012-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Tim Henning Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_EVENTMONITOR_EVENTMONITORWIDGET_H #define GAMMARAY_EVENTMONITOR_EVENTMONITORWIDGET_H #include "eventmonitorinterface.h" #include #include namespace GammaRay { namespace Ui { class EventMonitorWidget; } class EventMonitorWidget : public QWidget { Q_OBJECT public: explicit EventMonitorWidget(QWidget *parent = nullptr); ~EventMonitorWidget() override; private slots: void pauseAndResume(bool pause); private: void eventTreeContextMenu(QPoint pos); void eventInspectorContextMenu(QPoint pos); Ui::EventMonitorWidget *ui; EventMonitorInterface *m_interface; }; class EventMonitorUiFactory : public QObject, public StandardToolUiFactory { Q_OBJECT Q_INTERFACES(GammaRay::ToolUiFactory) Q_PLUGIN_METADATA(IID "com.kdab.GammaRay.ToolUiFactory" FILE "gammaray_eventmonitor.json") }; } #endif // GAMMARAY_EVENTMONITORWIDGET_H gammaray-2.11.3/plugins/eventmonitor/eventmonitorwidget.ui000066400000000000000000000131351413310443000240760ustar00rootroot00000000000000 GammaRay::EventMonitorWidget 0 0 689 300 0 0 0 Log Qt::Horizontal 6 Pause true Clear History Qt::CustomContextMenu true Qt::CustomContextMenu QAbstractItemView::ContiguousSelection Types 6 Record All Record None Qt::Horizontal QSizePolicy::Maximum 50 0 Show All Show None false true GammaRay::DeferredTreeView QTreeView
ui/deferredtreeview.h
gammaray-2.11.3/plugins/eventmonitor/eventtypeclientproxymodel.cpp000066400000000000000000000061341413310443000256540ustar00rootroot00000000000000/* eventtypeclientproxymodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2019-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Tim Henning Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "eventtypeclientproxymodel.h" #include "eventtypemodel.h" #include #include using namespace GammaRay; EventTypeClientProxyModel::EventTypeClientProxyModel(QObject *parent) : QIdentityProxyModel(parent) { } EventTypeClientProxyModel::~EventTypeClientProxyModel() = default; // 1 / GRADIENT_SCALE_FACTOR is yellow, 2 / GRADIENT_SCALE_FACTOR and beyond is red static const int GRADIENT_SCALE_FACTOR = 4; static QColor colorForRatio(double ratio) { const auto red = qBound(0.0, ratio * GRADIENT_SCALE_FACTOR, 0.5); const auto green = qBound(0.0, 1 - ratio * GRADIENT_SCALE_FACTOR, 0.5); auto color = QColor(int(255 * red), int(255 * green), 0); if (!UiIntegration::hasDarkUI()) return color.lighter(300); return color; } QVariant EventTypeClientProxyModel::data(const QModelIndex &index, int role) const { if (!sourceModel() || !index.isValid()) return QVariant(); if (role != Qt::BackgroundRole || index.column() != EventTypeModel::Count) return QIdentityProxyModel::data(index, role); const int maxCount = QIdentityProxyModel::data(index, EventTypeModel::MaxEventCount).toInt(); const int count = QIdentityProxyModel::data(index, Qt::DisplayRole).toInt(); if (maxCount <= 0 || count <= 0) { return QVariant(); } const double ratio = double(count) / double(maxCount); return colorForRatio(ratio); } QVariant EventTypeClientProxyModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role == Qt::DisplayRole && orientation == Qt::Horizontal) { switch (section) { case EventTypeModel::Columns::Type: return tr("Type"); case EventTypeModel::Columns::Count: return tr("Count"); case EventTypeModel::Columns::RecordingStatus: return tr("Record"); case EventTypeModel::Columns::Visibility: return tr("Show"); } } return QVariant(); } gammaray-2.11.3/plugins/eventmonitor/eventtypeclientproxymodel.h000066400000000000000000000034461413310443000253240ustar00rootroot00000000000000/* eventtypeclientproxymodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2019-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Tim Henning Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_EVENTMONITOR_EVENTTYPECLIENTPROXYMODEL_H #define GAMMARAY_EVENTMONITOR_EVENTTYPECLIENTPROXYMODEL_H #include namespace GammaRay { /** Colors the event counts based on the max events of a single type. */ class EventTypeClientProxyModel : public QIdentityProxyModel { Q_OBJECT public: explicit EventTypeClientProxyModel(QObject *parent = nullptr); ~EventTypeClientProxyModel() override; QVariant data(const QModelIndex &index, int role) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; }; } #endif // GAMMARAY_EVENTMONITOR_EVENTTYPECLIENTPROXYMODEL_H gammaray-2.11.3/plugins/eventmonitor/eventtypefilter.cpp000066400000000000000000000045351413310443000235430ustar00rootroot00000000000000/* eventtypefilter.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2019-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Tim Henning Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "eventtypefilter.h" #include "eventtypemodel.h" #include "eventmodelroles.h" using namespace GammaRay; EventTypeFilter::EventTypeFilter(QObject *parent) : QSortFilterProxyModel(parent) { } void EventTypeFilter::setEventTypeModel(const EventTypeModel *typeModel) { m_eventTypeModel = typeModel; } bool EventTypeFilter::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const { QModelIndex typeIndex = sourceModel()->index(sourceRow, 0, sourceParent); QEvent::Type type = sourceModel()->data(typeIndex, EventModelRole::EventTypeRole).value(); if (m_eventTypeModel && m_eventTypeModel->isVisible(type)) { return QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent); } return false; } void EventTypeFilter::sort(int, Qt::SortOrder) { QSortFilterProxyModel::sort(0, Qt::DescendingOrder); } bool EventTypeFilter::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const { // we want newest event on top, but propagated events in order of occurrence if (source_left.parent().isValid() && source_right.parent().isValid()) { return source_left.row() > source_right.row(); } return source_left.row() < source_right.row(); } gammaray-2.11.3/plugins/eventmonitor/eventtypefilter.h000066400000000000000000000035671413310443000232140ustar00rootroot00000000000000/* eventtypefilter.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2019-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Tim Henning Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_EVENTMONITOR_EVENTTYPEFILTER_H #define GAMMARAY_EVENTMONITOR_EVENTTYPEFILTER_H #include namespace GammaRay { class EventTypeModel; class EventTypeFilter : public QSortFilterProxyModel { Q_OBJECT public: explicit EventTypeFilter(QObject *parent); ~EventTypeFilter() override = default; void setEventTypeModel(const EventTypeModel *typeModel); void sort(int, Qt::SortOrder) override; protected: bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override; bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const override; private: const EventTypeModel* m_eventTypeModel = nullptr; }; } #endif // GAMMARAY_EVENTMONITOR_EVENTTYPEFILTER_H gammaray-2.11.3/plugins/eventmonitor/eventtypemodel.cpp000066400000000000000000000166241413310443000233600ustar00rootroot00000000000000/* eventtypemodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2019-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Tim Henning Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "eventtypemodel.h" #include #include #include using namespace GammaRay; EventTypeModel::EventTypeModel(QObject *parent) : QAbstractTableModel(parent) , m_pendingUpdateTimer(new QTimer(this)) { initEventTypes(); m_pendingUpdateTimer->setSingleShot(true); m_pendingUpdateTimer->setInterval(500); connect(m_pendingUpdateTimer, &QTimer::timeout, this, &EventTypeModel::emitPendingUpdates); } EventTypeModel::~EventTypeModel() = default; int EventTypeModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); return EventTypeModel::Columns::COUNT; } int EventTypeModel::rowCount(const QModelIndex &parent) const { if (!parent.isValid()) return m_data.size(); return 0; } QVariant EventTypeModel::data(const QModelIndex &index, int role) const { if (!index.isValid() || index.row() >= rowCount() || index.column() >= columnCount()) return QVariant(); if (role == Qt::DisplayRole) { switch (index.column()) { case Columns::Type: { const auto s = VariantHandler::displayString(m_data[index.row()].type); if (s.isEmpty()) { return m_data[index.row()].type; } return QString(s + QLatin1String(" [") + QString::number(m_data[index.row()].type) + QLatin1Char(']')); } case Columns::Count: return m_data[index.row()].count; } } else if (role == Qt::CheckStateRole) { switch (index.column()) { case Columns::RecordingStatus: return m_data[index.row()].recordingEnabled ? Qt::Checked : Qt::Unchecked; case Columns::Visibility: return m_data[index.row()].isVisibleInLog ? Qt::Checked : Qt::Unchecked; } } else if (role == Role::MaxEventCount) { return m_maxEventCount; } return QVariant(); } Qt::ItemFlags EventTypeModel::flags(const QModelIndex &index) const { if (!index.isValid()) return Qt::ItemFlag::NoItemFlags; Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable; if (index.column() == Columns::RecordingStatus || index.column() == Columns::Visibility) flags |= Qt::ItemIsUserCheckable; return flags; } bool EventTypeModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (!index.isValid() || role != Qt::CheckStateRole || (index.column() != Columns::RecordingStatus && index.column() != Columns::Visibility)) return false; const auto enabled = value.toInt() == Qt::Checked; if (index.column() == Columns::RecordingStatus) { m_data[index.row()].recordingEnabled = enabled; } else if (index.column() == Columns::Visibility) { m_data[index.row()].isVisibleInLog = enabled; emit typeVisibilityChanged(); } emit dataChanged(index, index, { Qt::CheckStateRole }); return true; } QMap EventTypeModel::itemData(const QModelIndex& index) const { auto d = QAbstractItemModel::itemData(index); if (index.column() == Columns::Count) { d.insert(Role::MaxEventCount, index.data(Role::MaxEventCount)); } return d; } void EventTypeModel::increaseCount(QEvent::Type type) { const auto it = std::lower_bound(m_data.begin(), m_data.end(), type); if (it != m_data.end() && (*it).type == type) { (*it).count++; m_maxEventCount = std::max((*it).count, m_maxEventCount); m_pendingUpdates.insert(type); if (!m_pendingUpdateTimer->isActive()) { m_pendingUpdateTimer->start(); } } else { const auto row = std::distance(m_data.begin(), it); beginInsertRows(QModelIndex(), row, row); EventTypeData item; item.type = type; item.count++; m_maxEventCount = std::max(item.count, m_maxEventCount); m_data.insert(it, item); endInsertRows(); } } void EventTypeModel::resetCounts() { beginResetModel(); for (auto &eventTypeData: m_data) { eventTypeData.count = 0; } m_maxEventCount = 0; endResetModel(); } bool EventTypeModel::isRecording(QEvent::Type type) const { const auto it = std::lower_bound(m_data.begin(), m_data.end(), type); if (it != m_data.end() && (*it).type == type) { return (*it).recordingEnabled; } return true; } void EventTypeModel::recordAll() { beginResetModel(); for (auto &eventTypeData: m_data) { eventTypeData.recordingEnabled = true; } endResetModel(); } void EventTypeModel::recordNone() { beginResetModel(); for (auto &eventTypeData: m_data) { eventTypeData.recordingEnabled = false; } endResetModel(); } bool EventTypeModel::isVisible(QEvent::Type type) const { const auto it = std::lower_bound(m_data.begin(), m_data.end(), type); if (it != m_data.end() && (*it).type == type) { return (*it).isVisibleInLog; } return true; } void EventTypeModel::showAll() { beginResetModel(); for (auto &eventTypeData: m_data) { eventTypeData.isVisibleInLog = true; } endResetModel(); emit typeVisibilityChanged(); } void EventTypeModel::showNone() { beginResetModel(); for (auto &eventTypeData: m_data) { eventTypeData.isVisibleInLog = false; } endResetModel(); emit typeVisibilityChanged(); } void EventTypeModel::initEventTypes() { QMetaEnum e = QMetaEnum::fromType(); beginInsertRows(QModelIndex(), 0, e.keyCount() - 1); m_data.reserve(e.keyCount()); for (int i = 0; i < e.keyCount(); ++i) { EventTypeData ev; ev.type = static_cast(e.value(i)); if (ev.type == QEvent::MetaCall) { // expensive to record types are off by default ev.recordingEnabled = false; } m_data.push_back(ev); } std::sort(m_data.begin(), m_data.end()); endInsertRows(); } void EventTypeModel::emitPendingUpdates() { for (auto type : m_pendingUpdates) { const auto it = std::lower_bound(m_data.begin(), m_data.end(), static_cast(type)); Q_ASSERT(it != m_data.end()); const auto row = std::distance(m_data.begin(), it); const auto idx = createIndex(row, EventTypeModel::Columns::Count); emit dataChanged(idx, idx); } m_pendingUpdates.clear(); } gammaray-2.11.3/plugins/eventmonitor/eventtypemodel.h000066400000000000000000000064261413310443000230240ustar00rootroot00000000000000/* eventtypemodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2019-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Tim Henning Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_EVENTMONITOR_EVENTTYPEMODEL_H #define GAMMARAY_EVENTMONITOR_EVENTTYPEMODEL_H #include #include #include #include #include QT_BEGIN_NAMESPACE class QTimer; QT_END_NAMESPACE namespace GammaRay { struct EventTypeData { QEvent::Type type = QEvent::None; int count = 0; bool recordingEnabled = true; bool isVisibleInLog = true; inline bool operator<(const EventTypeData &other) const { return type < other.type; } inline bool operator<(QEvent::Type otherType) const { return type < otherType; } }; } Q_DECLARE_METATYPE(GammaRay::EventTypeData) QT_BEGIN_NAMESPACE Q_DECLARE_TYPEINFO(GammaRay::EventTypeData, Q_MOVABLE_TYPE); QT_END_NAMESPACE namespace GammaRay { class EventTypeModel : public QAbstractTableModel { Q_OBJECT public: enum Columns { Type = 0, Count, RecordingStatus, Visibility, COUNT }; enum Role { MaxEventCount = GammaRay::UserRole + 1, }; public: explicit EventTypeModel(QObject *parent = nullptr); ~EventTypeModel() override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; Qt::ItemFlags flags(const QModelIndex &index) const override; bool setData(const QModelIndex &index, const QVariant &value, int role) override; QMap itemData(const QModelIndex& index) const override; public slots: void increaseCount(QEvent::Type type); void resetCounts(); bool isRecording(QEvent::Type type) const; void recordAll(); void recordNone(); bool isVisible(QEvent::Type type) const; void showAll(); void showNone(); signals: void typeVisibilityChanged(); private: void initEventTypes(); void emitPendingUpdates(); private: std::vector m_data; std::unordered_set m_pendingUpdates; int m_maxEventCount = 0; QTimer *m_pendingUpdateTimer = nullptr; }; } #endif // GAMMARAY_EVENTMONITOR_EVENTTYPEMODEL_H gammaray-2.11.3/plugins/eventmonitor/gammaray_eventmonitor.json000066400000000000000000000001301413310443000250730ustar00rootroot00000000000000{ "id": "gammaray_eventmonitor", "name": "Events", "types": [ "QObject" ] } gammaray-2.11.3/plugins/fontbrowser/000077500000000000000000000000001413310443000174205ustar00rootroot00000000000000gammaray-2.11.3/plugins/fontbrowser/CMakeLists.txt000066400000000000000000000015471413310443000221670ustar00rootroot00000000000000# probe part if (NOT GAMMARAY_CLIENT_ONLY_BUILD) set(gammaray_fontbrowser_plugin_srcs fontbrowser.cpp fontbrowserinterface.cpp fontmodel.cpp fontdatabasemodel.cpp fontbrowserserver.cpp ) gammaray_add_plugin(gammaray_fontbrowser_plugin JSON gammaray_fontbrowser.json SOURCES ${gammaray_fontbrowser_plugin_srcs} ) target_link_libraries(gammaray_fontbrowser_plugin gammaray_core) target_link_libraries(gammaray_fontbrowser_plugin Qt5::Gui) endif() # ui part if(GAMMARAY_BUILD_UI) set(gammaray_fontbrowser_plugin_ui_srcs fontbrowserwidget.cpp fontbrowserinterface.cpp fontbrowserclient.cpp ) gammaray_add_plugin(gammaray_fontbrowser_ui_plugin JSON gammaray_fontbrowser.json SOURCES ${gammaray_fontbrowser_plugin_ui_srcs} ) target_link_libraries(gammaray_fontbrowser_ui_plugin gammaray_common gammaray_ui ) endif() gammaray-2.11.3/plugins/fontbrowser/fontbrowser.cpp000066400000000000000000000023531413310443000225010ustar00rootroot00000000000000/* fontbrowser.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Stephen Kelly Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "fontbrowser.h" #include using namespace GammaRay; gammaray-2.11.3/plugins/fontbrowser/fontbrowser.h000066400000000000000000000033721413310443000221500ustar00rootroot00000000000000/* fontbrowser.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Stephen Kelly Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_FONTBROWSER_FONTBROWSER_H #define GAMMARAY_FONTBROWSER_FONTBROWSER_H #include #include "fontbrowserserver.h" #include namespace GammaRay { class FontBrowserFactory : public QObject , public StandardToolFactory { Q_OBJECT Q_INTERFACES(GammaRay::ToolFactory) Q_PLUGIN_METADATA(IID "com.kdab.GammaRay.ToolFactory" FILE "gammaray_fontbrowser.json") public: explicit FontBrowserFactory(QObject *parent = nullptr) : QObject(parent) { } }; } #endif // GAMMARAY_FONTBROWSER_H gammaray-2.11.3/plugins/fontbrowser/fontbrowserclient.cpp000066400000000000000000000036371413310443000237060ustar00rootroot00000000000000/* fontbrowserclient.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "fontbrowserclient.h" #include #include using namespace GammaRay; FontBrowserClient::FontBrowserClient(QObject *parent) : FontBrowserInterface(parent) { } #define WRAP_REMOTE(func, type) \ void FontBrowserClient::func(type arg) \ { \ Endpoint::instance()->invokeObject(objectName(), #func, QVariantList() << arg); \ } WRAP_REMOTE(setPointSize, int) WRAP_REMOTE(toggleBoldFont, bool) WRAP_REMOTE(toggleItalicFont, bool) WRAP_REMOTE(toggleUnderlineFont, bool) WRAP_REMOTE(updateText, const QString &) void FontBrowserClient::setColors(const QColor &foreground, const QColor &background) { Endpoint::instance()->invokeObject(objectName(), "setColors", QVariantList() << foreground << background); } gammaray-2.11.3/plugins/fontbrowser/fontbrowserclient.h000066400000000000000000000035101413310443000233410ustar00rootroot00000000000000/* fontbrowserclient.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_FONTBROWSER_FONTBROWSERCLIENT_H #define GAMMARAY_FONTBROWSER_FONTBROWSERCLIENT_H #include "fontbrowserinterface.h" namespace GammaRay { class FontBrowserClient : public FontBrowserInterface { Q_OBJECT Q_INTERFACES(GammaRay::FontBrowserInterface) public: explicit FontBrowserClient(QObject *parent = nullptr); public slots: void setPointSize(int size) override; void toggleBoldFont(bool bold) override; void toggleItalicFont(bool italic) override; void toggleUnderlineFont(bool underline) override; void updateText(const QString &text) override; void setColors(const QColor &foreground, const QColor &background) override; }; } #endif // GAMMARAY_FONTBROWSERCLIENT_H gammaray-2.11.3/plugins/fontbrowser/fontbrowserinterface.cpp000066400000000000000000000026521413310443000243640ustar00rootroot00000000000000/* fontbrowserinterface.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "fontbrowserinterface.h" #include using namespace GammaRay; FontBrowserInterface::FontBrowserInterface(QObject *parent) : QObject(parent) { ObjectBroker::registerObject(this); } FontBrowserInterface::~FontBrowserInterface() = default; gammaray-2.11.3/plugins/fontbrowser/fontbrowserinterface.h000066400000000000000000000037371413310443000240360ustar00rootroot00000000000000/* fontbrowserinterface.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_FONTBROWSER_FONTBROWSERINTERFACE_H #define GAMMARAY_FONTBROWSER_FONTBROWSERINTERFACE_H #include QT_BEGIN_NAMESPACE class QColor; QT_END_NAMESPACE namespace GammaRay { class FontBrowserInterface : public QObject { Q_OBJECT public: explicit FontBrowserInterface(QObject *parent); ~FontBrowserInterface() override; public slots: virtual void updateText(const QString &text) = 0; virtual void toggleBoldFont(bool bold) = 0; virtual void toggleItalicFont(bool italic) = 0; virtual void toggleUnderlineFont(bool underline) = 0; virtual void setPointSize(int size) = 0; virtual void setColors(const QColor &foreground, const QColor &background) = 0; }; } QT_BEGIN_NAMESPACE Q_DECLARE_INTERFACE(GammaRay::FontBrowserInterface, "com.kdab.GammaRay.FontBrowser") QT_END_NAMESPACE #endif // FONTBROWSERINTERFACE_H gammaray-2.11.3/plugins/fontbrowser/fontbrowserserver.cpp000066400000000000000000000057421413310443000237350ustar00rootroot00000000000000/* fontbrowserserver.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Stephen Kelly Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "fontbrowserserver.h" #include "fontmodel.h" #include "fontdatabasemodel.h" #include #include #include #include using namespace GammaRay; FontBrowserServer::FontBrowserServer(Probe *probe, QObject *parent) : FontBrowserInterface(parent) , m_selectedFontModel(new FontModel(this)) { auto model = new FontDatabaseModel(this); probe->registerModel(QStringLiteral("com.kdab.GammaRay.FontModel"), model); m_fontSelectionModel = ObjectBroker::selectionModel(model); connect(m_fontSelectionModel, &QItemSelectionModel::selectionChanged, this, &FontBrowserServer::updateFonts); probe->registerModel(QStringLiteral("com.kdab.GammaRay.SelectedFontModel"), m_selectedFontModel); } void FontBrowserServer::updateFonts() { const auto rows = m_fontSelectionModel->selectedRows(); QVector currentFonts; currentFonts.reserve(rows.size()); for (const QModelIndex &index : rows) currentFonts << index.data(Qt::UserRole + 1).value(); m_selectedFontModel->updateFonts(currentFonts); } void FontBrowserServer::setPointSize(int size) { m_selectedFontModel->setPointSize(size); } void FontBrowserServer::toggleBoldFont(bool bold) { m_selectedFontModel->toggleBoldFont(bold); } void FontBrowserServer::toggleItalicFont(bool italic) { m_selectedFontModel->toggleItalicFont(italic); } void FontBrowserServer::toggleUnderlineFont(bool underline) { m_selectedFontModel->toggleUnderlineFont(underline); } void FontBrowserServer::updateText(const QString &text) { m_selectedFontModel->updateText(text); } void FontBrowserServer::setColors(const QColor &foreground, const QColor &background) { m_selectedFontModel->setColors(foreground, background); } gammaray-2.11.3/plugins/fontbrowser/fontbrowserserver.h000066400000000000000000000041241413310443000233730ustar00rootroot00000000000000/* fontbrowserserver.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Stephen Kelly Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_FONTBROWSER_FONTBROWSERSERVER_H #define GAMMARAY_FONTBROWSER_FONTBROWSERSERVER_H #include "fontbrowserinterface.h" QT_BEGIN_NAMESPACE class QItemSelectionModel; QT_END_NAMESPACE namespace GammaRay { class FontModel; class Probe; class FontBrowserServer : public FontBrowserInterface { Q_OBJECT Q_INTERFACES(GammaRay::FontBrowserInterface) public: explicit FontBrowserServer(Probe *probe, QObject *parent = nullptr); private slots: void updateFonts(); void setPointSize(int size) override; void toggleBoldFont(bool bold) override; void toggleItalicFont(bool italic) override; void toggleUnderlineFont(bool underline) override; void updateText(const QString &text) override; void setColors(const QColor &foreground, const QColor &background) override; private: FontModel *m_selectedFontModel; QItemSelectionModel *m_fontSelectionModel; }; } #endif // FONTBROWSERSERVER_H gammaray-2.11.3/plugins/fontbrowser/fontbrowserwidget.cpp000066400000000000000000000102351413310443000237030ustar00rootroot00000000000000/* fontbrowserwidget.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Stephen Kelly Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "fontbrowserwidget.h" #include "ui_fontbrowserwidget.h" #include "fontbrowserclient.h" #include #include #include using namespace GammaRay; static QObject *fontBrowserClientFactory(const QString & /*name*/, QObject *parent) { return new FontBrowserClient(parent); } FontBrowserWidget::FontBrowserWidget(QWidget *parent) : QWidget(parent) , ui(new Ui::FontBrowserWidget) , m_stateManager(this) , m_selectedFontModel(nullptr) , m_fontBrowser(nullptr) { ObjectBroker::registerClientObjectFactoryCallback( fontBrowserClientFactory); m_fontBrowser = ObjectBroker::object(); ui->setupUi(this); m_selectedFontModel = ObjectBroker::model(QStringLiteral("com.kdab.GammaRay.SelectedFontModel")); ui->selectedFontsView->header()->setObjectName("selectedFontsViewHeader"); ui->selectedFontsView->setDeferredResizeMode(0, QHeaderView::ResizeToContents); ui->selectedFontsView->setDeferredResizeMode(1, QHeaderView::ResizeToContents); ui->selectedFontsView->setModel(m_selectedFontModel); connect(ui->fontText, &QLineEdit::textChanged, m_fontBrowser, &FontBrowserInterface::updateText); connect(ui->boldBox, &QAbstractButton::toggled, m_fontBrowser, &FontBrowserInterface::toggleBoldFont); connect(ui->italicBox, &QAbstractButton::toggled, m_fontBrowser, &FontBrowserInterface::toggleItalicFont); connect(ui->underlineBox, &QAbstractButton::toggled, m_fontBrowser, &FontBrowserInterface::toggleUnderlineFont); connect(ui->pointSize, static_cast(&QSpinBox::valueChanged), m_fontBrowser, &FontBrowserInterface::setPointSize); QAbstractItemModel *fontModel = ObjectBroker::model(QStringLiteral( "com.kdab.GammaRay.FontModel")); ui->fontTree->header()->setObjectName("fontTreeHeader"); ui->fontTree->setDeferredResizeMode(0, QHeaderView::ResizeToContents); ui->fontTree->setSelectionMode(QAbstractItemView::ExtendedSelection); ui->fontTree->setModel(fontModel); ui->fontTree->setSelectionModel(ObjectBroker::selectionModel(fontModel)); ui->pointSize->setValue(font().pointSize()); // init m_fontBrowser->updateText(ui->fontText->text()); m_fontBrowser->toggleBoldFont(ui->boldBox->isChecked()); m_fontBrowser->toggleItalicFont(ui->italicBox->isChecked()); m_fontBrowser->toggleUnderlineFont(ui->underlineBox->isChecked()); m_fontBrowser->setPointSize(ui->pointSize->value()); m_stateManager.setDefaultSizes(ui->mainSplitter, UISizeVector() << "50%" << "50%"); QMetaObject::invokeMethod(this, "delayedInit", Qt::QueuedConnection); } FontBrowserWidget::~FontBrowserWidget() = default; void FontBrowserWidget::delayedInit() { m_fontBrowser->setColors(palette().color(QPalette::Foreground), palette().color(QPalette::Base)); } gammaray-2.11.3/plugins/fontbrowser/fontbrowserwidget.h000066400000000000000000000042511413310443000233510ustar00rootroot00000000000000/* fontbrowserwidget.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Stephen Kelly Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_FONTBROWSER_FONTBROWSERWIDGET_H #define GAMMARAY_FONTBROWSER_FONTBROWSERWIDGET_H #include #include #include QT_BEGIN_NAMESPACE class QAbstractItemModel; class QItemSelection; QT_END_NAMESPACE namespace GammaRay { class FontBrowserInterface; namespace Ui { class FontBrowserWidget; } class FontBrowserWidget : public QWidget { Q_OBJECT public: explicit FontBrowserWidget(QWidget *parent = nullptr); ~FontBrowserWidget() override; private slots: void delayedInit(); private: QScopedPointer ui; UIStateManager m_stateManager; QAbstractItemModel *m_selectedFontModel; FontBrowserInterface *m_fontBrowser; }; class FontBrowserUiFactory : public QObject, public StandardToolUiFactory { Q_OBJECT Q_INTERFACES(GammaRay::ToolUiFactory) Q_PLUGIN_METADATA(IID "com.kdab.GammaRay.ToolUiFactory" FILE "gammaray_fontbrowser.json") }; } #endif // GAMMARAY_FONTBROWSERWIDGET_H gammaray-2.11.3/plugins/fontbrowser/fontbrowserwidget.ui000066400000000000000000000062511413310443000235410ustar00rootroot00000000000000 GammaRay::FontBrowserWidget 0 0 742 300 Qt::Horizontal true The quick brown fox jumps over the lazy dog Enter some text here... 0 Point Size: Bold Italic Underline Qt::Horizontal 40 20 false GammaRay::DeferredTreeView QTreeView
ui/deferredtreeview.h
gammaray-2.11.3/plugins/fontbrowser/fontdatabasemodel.cpp000066400000000000000000000123231413310443000236010ustar00rootroot00000000000000/* fontdatabasemodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "fontdatabasemodel.h" #include #include #include #include using namespace GammaRay; static const int TopLevelId = std::numeric_limits::max(); FontDatabaseModel::FontDatabaseModel(QObject *parent) : QAbstractItemModel(parent) { } FontDatabaseModel::~FontDatabaseModel() = default; int FontDatabaseModel::rowCount(const QModelIndex &parent) const { ensureModelPopulated(); if (!parent.isValid()) return m_families.size(); if (parent.internalId() == TopLevelId && parent.column() == 0) return m_styles.at(parent.row()).size(); return 0; } int FontDatabaseModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); return 2; } QVariant FontDatabaseModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); int family = -1; if (index.internalId() == TopLevelId) family = index.row(); else family = index.internalId(); Q_ASSERT(family >= 0 && family < m_families.size() && family < m_styles.size()); if (role == Qt::DisplayRole) { if (index.internalId() == TopLevelId) { if (index.column() == 0) return m_families.at(family); } else { switch (index.column()) { case 0: return m_styles.at(family).at(index.row()); case 1: return smoothSizeString(m_families.at(family), m_styles.at(family).at(index.row())); } } } else if (role == Qt::ToolTipRole) { if (index.internalId() != TopLevelId && index.column() == 1) return smoothSizeString(m_families.at(family), m_styles.at(family).at(index.row())); } else if (role == Qt::UserRole + 1) { if (index.internalId() == TopLevelId) { return QFont(m_families.at(family)); } else { QFontDatabase database; return database.font(m_families.at(family), m_styles.at(family).at(index.row()), 10); } } return QVariant(); } QVariant FontDatabaseModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { switch (section) { case 0: return tr("Fonts"); case 1: return tr("Smooth Sizes"); } } return QAbstractItemModel::headerData(section, orientation, role); } QModelIndex FontDatabaseModel::index(int row, int column, const QModelIndex &parent) const { if (row < 0 || column < 0 || column >= columnCount()) return {}; if (parent.isValid()) { if (row >= m_styles.at(parent.row()).size()) return QModelIndex(); return createIndex(row, column, parent.row()); } return createIndex(row, column, TopLevelId); } QModelIndex FontDatabaseModel::parent(const QModelIndex &child) const { if (!child.isValid() || child.internalId() == TopLevelId) return {}; return createIndex(child.internalId(), 0, TopLevelId); } QString FontDatabaseModel::smoothSizeString(const QString &family, const QString &style) const { QFontDatabase database; const auto smoothSizes = database.smoothSizes(family, style); QStringList sizes; sizes.reserve(smoothSizes.size()); for (auto points : smoothSizes) sizes.push_back(QString::number(points)); return sizes.join(QStringLiteral(" ")); } void FontDatabaseModel::ensureModelPopulated() const { if (!m_families.isEmpty()) return; const_cast(this)->populateModel(); } void FontDatabaseModel::populateModel() { QFontDatabase database; const auto families = database.families(); m_families.reserve(families.size()); m_styles.resize(families.size()); for (int i = 0; i < families.size(); ++i) { const auto &family = families.at(i); m_families.push_back(family); const auto styles = database.styles(family); m_styles[i].reserve(styles.size()); foreach (const auto &style, database.styles(family)) m_styles[i].push_back(style); } } gammaray-2.11.3/plugins/fontbrowser/fontdatabasemodel.h000066400000000000000000000044671413310443000232600ustar00rootroot00000000000000/* fontdatabasemodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_FONTDATABASEMODEL_H #define GAMMARAY_FONTDATABASEMODEL_H #include #include #include namespace GammaRay { /** Font families and font styles. */ class FontDatabaseModel : public QAbstractItemModel { Q_OBJECT public: explicit FontDatabaseModel(QObject *parent = nullptr); ~FontDatabaseModel() override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; QModelIndex parent(const QModelIndex &child) const override; private: void ensureModelPopulated() const; void populateModel(); QString smoothSizeString(const QString &family, const QString &style) const; QVector m_families; QVector > m_styles; }; } #endif // GAMMARAY_FONTDATABASEMODEL_H gammaray-2.11.3/plugins/fontbrowser/fontmodel.cpp000066400000000000000000000121551413310443000221170ustar00rootroot00000000000000/* fontmodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Stephen Kelly Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "fontmodel.h" #include #include #include using namespace GammaRay; FontModel::FontModel(QObject *parent) : QAbstractTableModel(parent) , m_size(12) , m_bold(false) , m_italic(false) , m_underline(false) { } QVector FontModel::currentFonts() const { return m_fonts; } void FontModel::updateFonts(const QVector &fonts) { if (!m_fonts.isEmpty()) { beginRemoveRows(QModelIndex(), 0, m_fonts.size() - 1); m_fonts.clear(); endRemoveRows(); } if (fonts.isEmpty()) return; beginInsertRows(QModelIndex(), 0, fonts.size() - 1); m_fonts = fonts; for (int i = 0; i < m_fonts.size(); ++i) { QFont &font = m_fonts[i]; font.setPointSize(m_size); font.setBold(m_bold); font.setItalic(m_italic); font.setUnderline(m_underline); } endInsertRows(); } void FontModel::updateText(const QString &text) { if (text == m_text) return; m_text = text; fontDataChanged(); } QVariant FontModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { if (section == 0) return "Font Family"; else if (section == 1) return "Style Name"; else if (section == 2) return "Text Preview"; } return QAbstractTableModel::headerData(section, orientation, role); } int FontModel::rowCount(const QModelIndex &parent) const { if (parent.isValid()) return 0; return m_fonts.size(); } int FontModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); return 3; } QVariant FontModel::data(const QModelIndex &index, int role) const { if (index.column() == 0) { if (role == Qt::DisplayRole) return m_fonts.at(index.row()).family(); } else if (index.column() == 1) { if (role == Qt::DisplayRole) return m_fonts.at(index.row()).styleName(); } else if (index.column() == 2) { if (role == Qt::DecorationRole || role == Qt::SizeHintRole) { const QFont &font = m_fonts.at(index.row()); QFontMetrics metrics(font); const QString text = m_text.isEmpty() ? tr("") : m_text; const QRect rect = metrics.boundingRect(text.left(100)); if (role == Qt::SizeHintRole) return rect.size(); QPixmap pixmap(rect.size()); pixmap.fill(m_background); QPainter painter(&pixmap); painter.setPen(m_foreground); painter.setFont(font); painter.drawText(0, -rect.y(), text); return pixmap; } } return QVariant(); } void FontModel::setPointSize(int size) { if (size == m_size) return; m_size = size; for (int i = 0; i < m_fonts.size(); ++i) m_fonts[i].setPointSize(size); fontDataChanged(); } void FontModel::toggleItalicFont(bool italic) { if (italic == m_italic) return; m_italic = italic; for (int i = 0; i < m_fonts.size(); ++i) m_fonts[i].setItalic(italic); fontDataChanged(); } void FontModel::toggleUnderlineFont(bool underline) { if (underline == m_underline) return; m_underline = underline; for (int i = 0; i < m_fonts.size(); ++i) m_fonts[i].setUnderline(underline); fontDataChanged(); } void FontModel::toggleBoldFont(bool bold) { if (bold == m_bold) return; m_bold = bold; for (int i = 0; i < m_fonts.size(); ++i) m_fonts[i].setBold(bold); fontDataChanged(); } void FontModel::setColors(const QColor &foreground, const QColor &background) { if (foreground == m_foreground && background == m_background) return; m_foreground = foreground; m_background = background; fontDataChanged(); } void FontModel::fontDataChanged() { if (m_fonts.isEmpty()) return; emit dataChanged(index(0, 2), index(rowCount() - 1, 2)); } gammaray-2.11.3/plugins/fontbrowser/fontmodel.h000066400000000000000000000045571413310443000215730ustar00rootroot00000000000000/* fontmodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Stephen Kelly Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_FONTBROWSER_FONTMODEL_H #define GAMMARAY_FONTBROWSER_FONTMODEL_H #include #include #include #include namespace GammaRay { class FontModel : public QAbstractTableModel { Q_OBJECT public: explicit FontModel(QObject *parent); void updateFonts(const QVector &fonts); QVector currentFonts() const; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; public slots: void updateText(const QString &text); void toggleBoldFont(bool bold); void toggleItalicFont(bool italic); void toggleUnderlineFont(bool underline); void setPointSize(int size); void setColors(const QColor &foreground, const QColor &background); private: void fontDataChanged(); QVector m_fonts; QString m_text; int m_size; bool m_bold; bool m_italic; bool m_underline; QColor m_foreground; QColor m_background; }; } #endif gammaray-2.11.3/plugins/fontbrowser/gammaray_fontbrowser.json000066400000000000000000000002511413310443000245410ustar00rootroot00000000000000{ "id": "gammaray_fontbrowser", "name": "Font Browser", "name[de]": "Schriftarten", "types": [ "QGuiApplication", "QApplication" ] } gammaray-2.11.3/plugins/guisupport/000077500000000000000000000000001413310443000172675ustar00rootroot00000000000000gammaray-2.11.3/plugins/guisupport/CMakeLists.txt000066400000000000000000000013401413310443000220250ustar00rootroot00000000000000# probe plugin if (NOT GAMMARAY_CLIENT_ONLY_BUILD) set(gammaray_guisupport_srcs guisupport.cpp ui.qrc ) gammaray_add_plugin(gammaray_guisupport JSON gammaray_guisupport.json SOURCES ${gammaray_guisupport_srcs}) target_link_libraries(gammaray_guisupport gammaray_core) target_include_directories(gammaray_guisupport SYSTEM PRIVATE ${Qt5Gui_PRIVATE_INCLUDE_DIRS}) target_link_libraries(gammaray_guisupport Qt5::Gui) endif() # ui plugin if(GAMMARAY_BUILD_UI) set(gammaray_guisupport_ui_srcs guisupportuifactory.cpp paintanalyzertab.cpp ) gammaray_add_plugin(gammaray_guisupport_ui JSON gammaray_guisupport.json SOURCES ${gammaray_guisupport_ui_srcs}) target_link_libraries(gammaray_guisupport_ui gammaray_ui) endif() gammaray-2.11.3/plugins/guisupport/gammaray_guisupport.json000066400000000000000000000002411413310443000242560ustar00rootroot00000000000000{ "hidden": true, "id": "gammaray_guisupport", "name": "GUI Support", "name[de]": "GUI Unterstützung", "types": [ "QObject" ] } gammaray-2.11.3/plugins/guisupport/guisupport.cpp000066400000000000000000001351441413310443000222240ustar00rootroot00000000000000/* guisupport.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "guisupport.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace GammaRay; Q_DECLARE_METATYPE(QFont::Capitalization) Q_DECLARE_METATYPE(QFont::HintingPreference) Q_DECLARE_METATYPE(QFont::SpacingType) Q_DECLARE_METATYPE(QFont::Style) Q_DECLARE_METATYPE(QFont::StyleHint) Q_DECLARE_METATYPE(QImage*) Q_DECLARE_METATYPE(QPlatformPixmap*) Q_DECLARE_METATYPE(QPlatformPixmap::ClassId) Q_DECLARE_METATYPE(QSurface::SurfaceClass) Q_DECLARE_METATYPE(QSurface::SurfaceType) Q_DECLARE_METATYPE(QSurfaceFormat::FormatOptions) Q_DECLARE_METATYPE(const QMimeData*) Q_DECLARE_METATYPE(QImage::Format) Q_DECLARE_METATYPE(const QGradient*) Q_DECLARE_METATYPE(QPixelFormat) Q_DECLARE_METATYPE(QPixelFormat::AlphaUsage) Q_DECLARE_METATYPE(QPixelFormat::AlphaPosition) Q_DECLARE_METATYPE(QPixelFormat::AlphaPremultiplied) Q_DECLARE_METATYPE(QPixelFormat::ByteOrder) Q_DECLARE_METATYPE(QPixelFormat::ColorModel) Q_DECLARE_METATYPE(QPixelFormat::TypeInterpretation) Q_DECLARE_METATYPE(QPixelFormat::YUVLayout) Q_DECLARE_METATYPE(Qt::MouseButton) Q_DECLARE_METATYPE(QFlags) Q_DECLARE_METATYPE(QTouchEvent::TouchPoint) Q_DECLARE_METATYPE(QList) Q_DECLARE_METATYPE(Qt::TouchPointState) Q_DECLARE_METATYPE(QFlags) Q_DECLARE_METATYPE(QFlags) Q_DECLARE_METATYPE(QTouchDevice*) Q_DECLARE_METATYPE(const QTouchDevice*) Q_DECLARE_METATYPE(QScrollEvent::ScrollState) Q_DECLARE_METATYPE(QList) Q_DECLARE_METATYPE(QContextMenuEvent::Reason) // QGradient is pseudo-polymorphic, make it introspectable nevertheless #define MAKE_GRADIENT_CAST(Type) \ template<> Q ## Type *DynamicCast(QGradient *g) { \ if (g->type() == QGradient:: Type) return static_cast(g); \ return nullptr; \ } namespace GammaRay { MAKE_GRADIENT_CAST(ConicalGradient) MAKE_GRADIENT_CAST(LinearGradient) MAKE_GRADIENT_CAST(RadialGradient) template <> bool IsPolymorphic() { return true; } } #undef MAKE_GRADIENT_CAST static bool isAcceptableWindow(QWindow *w) { return w && w->isTopLevel() && w->surfaceClass() != QSurface::Offscreen // Offscreen windows can have a surface different than Offscreen, // but they contains a window title 'Offscreen' && w->title() != QStringLiteral("Offscreen") ; } GuiSupport::GuiSupport(Probe *probe, QObject *parent) : QObject(parent) , m_probe(probe) { registerMetaTypes(); registerVariantHandler(); m_iconAndTitleOverrider.titleSuffix = tr(" (Injected by GammaRay)"); connect(m_probe, &Probe::objectCreated, this, &GuiSupport::objectCreated); if (auto guiApp = qobject_cast(QCoreApplication::instance())) { QTimer::singleShot(0, this, [this]() { updateWindowIcon(); }); m_probe->installGlobalEventFilter(this); foreach (auto w, guiApp->topLevelWindows()) { if (isAcceptableWindow(w)) updateWindowTitle(w); } connect(m_probe, &Probe::aboutToDetach, this, &GuiSupport::restoreIconAndTitle, Qt::DirectConnection); } } void GuiSupport::registerMetaTypes() { MetaObject *mo; MO_ADD_METAOBJECT1(QMimeData, QObject); MO_ADD_PROPERTY (QMimeData, colorData, setColorData); MO_ADD_PROPERTY_RO(QMimeData, formats); MO_ADD_PROPERTY_RO(QMimeData, hasColor); MO_ADD_PROPERTY_RO(QMimeData, hasHtml); MO_ADD_PROPERTY_RO(QMimeData, hasText); MO_ADD_PROPERTY_RO(QMimeData, hasUrls); MO_ADD_PROPERTY (QMimeData, html, setHtml); MO_ADD_PROPERTY (QMimeData, imageData, setImageData); MO_ADD_PROPERTY (QMimeData, text, setText); MO_ADD_PROPERTY (QMimeData, urls, setUrls); #ifndef QT_NO_CLIPBOARD MO_ADD_METAOBJECT1(QClipboard, QObject); MO_ADD_PROPERTY_LD(QClipboard, clipboardMimeData, [](QClipboard *cb) { return cb->mimeData(QClipboard::Clipboard); }); MO_ADD_PROPERTY_LD(QClipboard, findBufferMimeData, [](QClipboard *cb) { return cb->mimeData(QClipboard::FindBuffer); }); MO_ADD_PROPERTY_RO(QClipboard, ownsClipboard); MO_ADD_PROPERTY_RO(QClipboard, ownsFindBuffer); MO_ADD_PROPERTY_RO(QClipboard, ownsSelection); MO_ADD_PROPERTY_LD(QClipboard, selectionMimeData, [](QClipboard *cb) { return cb->mimeData(QClipboard::Selection); }); MO_ADD_PROPERTY_RO(QClipboard, supportsFindBuffer); MO_ADD_PROPERTY_RO(QClipboard, supportsSelection); #endif MO_ADD_METAOBJECT1(QGuiApplication, QCoreApplication); MO_ADD_PROPERTY_ST(QGuiApplication, applicationState); #ifndef QT_NO_CLIPBOARD MO_ADD_PROPERTY_ST(QGuiApplication, clipboard); #endif MO_ADD_PROPERTY_ST(QGuiApplication, desktopSettingsAware); MO_ADD_PROPERTY_RO(QGuiApplication, devicePixelRatio); MO_ADD_PROPERTY_ST(QGuiApplication, focusObject); MO_ADD_PROPERTY_ST(QGuiApplication, focusWindow); MO_ADD_PROPERTY_ST(QGuiApplication, font); MO_ADD_PROPERTY_ST(QGuiApplication, isLeftToRight); MO_ADD_PROPERTY_ST(QGuiApplication, isRightToLeft); MO_ADD_PROPERTY_ST(QGuiApplication, palette); MO_ADD_PROPERTY_ST(QGuiApplication, primaryScreen); #ifndef QT_NO_SESSIONMANAGER MO_ADD_PROPERTY_RO(QGuiApplication, isSavingSession); MO_ADD_PROPERTY_RO(QGuiApplication, isSessionRestored); MO_ADD_PROPERTY_RO(QGuiApplication, sessionId); MO_ADD_PROPERTY_RO(QGuiApplication, sessionKey); #endif MO_ADD_PROPERTY_ST(QGuiApplication, allWindows); MO_ADD_PROPERTY_ST(QGuiApplication, topLevelWindows); MO_ADD_METAOBJECT0(QPaintDevice); MO_ADD_PROPERTY_RO(QPaintDevice, colorCount); MO_ADD_PROPERTY_RO(QPaintDevice, depth); MO_ADD_PROPERTY_RO(QPaintDevice, devicePixelRatio); #if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0) MO_ADD_PROPERTY_RO(QPaintDevice, devicePixelRatioF); #endif MO_ADD_PROPERTY_RO(QPaintDevice, height); MO_ADD_PROPERTY_RO(QPaintDevice, heightMM); MO_ADD_PROPERTY_RO(QPaintDevice, logicalDpiX); MO_ADD_PROPERTY_RO(QPaintDevice, logicalDpiY); MO_ADD_PROPERTY_RO(QPaintDevice, paintingActive); MO_ADD_PROPERTY_RO(QPaintDevice, physicalDpiX); MO_ADD_PROPERTY_RO(QPaintDevice, physicalDpiY); MO_ADD_PROPERTY_RO(QPaintDevice, width); MO_ADD_PROPERTY_RO(QPaintDevice, widthMM); MO_ADD_METAOBJECT1(QImage, QPaintDevice); MO_ADD_PROPERTY_RO(QImage, allGray); MO_ADD_PROPERTY_RO(QImage, bitPlaneCount); MO_ADD_PROPERTY_RO(QImage, byteCount); MO_ADD_PROPERTY_RO(QImage, bytesPerLine); MO_ADD_PROPERTY_RO(QImage, cacheKey); MO_ADD_PROPERTY (QImage, dotsPerMeterX, setDotsPerMeterX); MO_ADD_PROPERTY (QImage, dotsPerMeterY, setDotsPerMeterY); MO_ADD_PROPERTY_RO(QImage, format); MO_ADD_PROPERTY_RO(QImage, hasAlphaChannel); MO_ADD_PROPERTY_RO(QImage, isGrayscale); MO_ADD_PROPERTY_RO(QImage, isNull); MO_ADD_PROPERTY (QImage, offset, setOffset); MO_ADD_PROPERTY_RO(QImage, pixelFormat); MO_ADD_PROPERTY_RO(QImage, rect); MO_ADD_PROPERTY_RO(QImage, size); MO_ADD_PROPERTY_RO(QImage, textKeys); MO_ADD_METAOBJECT1(QPixmap, QPaintDevice); MO_ADD_PROPERTY_RO(QPixmap, cacheKey); MO_ADD_PROPERTY_RO(QPixmap, hasAlpha); MO_ADD_PROPERTY_RO(QPixmap, hasAlphaChannel); MO_ADD_PROPERTY_RO(QPixmap, isNull); MO_ADD_PROPERTY_RO(QPixmap, isQBitmap); MO_ADD_PROPERTY_RO(QPixmap, rect); MO_ADD_PROPERTY_RO(QPixmap, size); MO_ADD_PROPERTY_RO(QPixmap, handle); MO_ADD_METAOBJECT0(QPlatformPixmap); MO_ADD_PROPERTY_NC(QPlatformPixmap, buffer); MO_ADD_PROPERTY_RO(QPlatformPixmap, classId); qRegisterMetaType(); MO_ADD_METAOBJECT0(QSurface); MO_ADD_PROPERTY_RO(QSurface, format); MO_ADD_PROPERTY_RO(QSurface, size); MO_ADD_PROPERTY_RO(QSurface, supportsOpenGL); MO_ADD_PROPERTY_RO(QSurface, surfaceClass); MO_ADD_PROPERTY_RO(QSurface, surfaceType); #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) // not a gadget previously MO_ADD_METAOBJECT0(QSurfaceFormat); MO_ADD_PROPERTY(QSurfaceFormat, alphaBufferSize, setAlphaBufferSize); MO_ADD_PROPERTY(QSurfaceFormat, blueBufferSize, setBlueBufferSize); MO_ADD_PROPERTY_RO(QSurfaceFormat, defaultFormat); MO_ADD_PROPERTY(QSurfaceFormat, depthBufferSize, setDepthBufferSize); MO_ADD_PROPERTY(QSurfaceFormat, greenBufferSize, setGreenBufferSize); MO_ADD_PROPERTY_RO(QSurfaceFormat, hasAlpha); MO_ADD_PROPERTY(QSurfaceFormat, majorVersion, setMajorVersion); MO_ADD_PROPERTY(QSurfaceFormat, minorVersion, setMinorVersion); MO_ADD_PROPERTY(QSurfaceFormat, options, setOptions); MO_ADD_PROPERTY(QSurfaceFormat, profile, setProfile); MO_ADD_PROPERTY(QSurfaceFormat, redBufferSize, setRedBufferSize); MO_ADD_PROPERTY(QSurfaceFormat, renderableType, setRenderableType); MO_ADD_PROPERTY(QSurfaceFormat, samples, setSamples); MO_ADD_PROPERTY(QSurfaceFormat, stencilBufferSize, setStencilBufferSize); MO_ADD_PROPERTY(QSurfaceFormat, stereo, setStereo); MO_ADD_PROPERTY(QSurfaceFormat, swapBehavior, setSwapBehavior); MO_ADD_PROPERTY(QSurfaceFormat, swapInterval, setSwapInterval); #endif MO_ADD_METAOBJECT2(QWindow, QObject, QSurface); MO_ADD_PROPERTY(QWindow, baseSize, setBaseSize); #ifndef QT_NO_CURSOR MO_ADD_PROPERTY(QWindow, cursor, setCursor); #endif MO_ADD_PROPERTY_RO(QWindow, devicePixelRatio); MO_ADD_PROPERTY(QWindow, filePath, setFilePath); MO_ADD_PROPERTY_RO(QWindow, focusObject); MO_ADD_PROPERTY_RO(QWindow, frameGeometry); MO_ADD_PROPERTY_RO(QWindow, frameMargins); MO_ADD_PROPERTY(QWindow, framePosition, setFramePosition); MO_ADD_PROPERTY(QWindow, geometry, setGeometry); MO_ADD_PROPERTY(QWindow, icon, setIcon); MO_ADD_PROPERTY_RO(QWindow, isExposed); MO_ADD_PROPERTY_RO(QWindow, isTopLevel); MO_ADD_PROPERTY(QWindow, mask, setMask); MO_ADD_PROPERTY(QWindow, position, setPosition); MO_ADD_PROPERTY_RO(QWindow, requestedFormat); MO_ADD_PROPERTY_RO(QWindow, screen); MO_ADD_PROPERTY(QWindow, sizeIncrement, setSizeIncrement); MO_ADD_PROPERTY(QWindow, windowState, setWindowState); MO_ADD_PROPERTY_RO(QWindow, transientParent); MO_ADD_PROPERTY_RO(QWindow, type); #ifndef QT_NO_OPENGL MO_ADD_METAOBJECT1(QOpenGLShader, QObject); MO_ADD_PROPERTY_RO(QOpenGLShader, isCompiled); MO_ADD_PROPERTY_RO(QOpenGLShader, log); MO_ADD_PROPERTY_RO(QOpenGLShader, shaderId); MO_ADD_PROPERTY_RO(QOpenGLShader, shaderType); MO_ADD_PROPERTY_RO(QOpenGLShader, sourceCode); MO_ADD_METAOBJECT1(QOpenGLShaderProgram, QObject); MO_ADD_PROPERTY_RO(QOpenGLShaderProgram, isLinked); MO_ADD_PROPERTY_RO(QOpenGLShaderProgram, log); // FIXME calling this asserts in debug builds of some newer Qt versions // MO_ADD_PROPERTY_RO(QOpenGLShaderProgram, maxGeometryOutputVertices); MO_ADD_PROPERTY(QOpenGLShaderProgram, patchVertexCount, setPatchVertexCount); MO_ADD_PROPERTY_RO(QOpenGLShaderProgram, programId); MO_ADD_METAOBJECT1(QOpenGLContext, QObject); MO_ADD_PROPERTY_RO(QOpenGLContext, defaultFramebufferObject); // crashes if context isn't current // MO_ADD_PROPERTY_RO(QOpenGLContext, extensions); MO_ADD_PROPERTY_RO(QOpenGLContext, format); MO_ADD_PROPERTY_RO(QOpenGLContext, isValid); MO_ADD_PROPERTY_RO(QOpenGLContext, screen); MO_ADD_PROPERTY_RO(QOpenGLContext, shareContext); MO_ADD_PROPERTY_RO(QOpenGLContext, shareGroup); // MO_ADD_PROPERTY_RO(QOpenGLContext, surface); #endif // QT_NO_OPENGL MO_ADD_METAOBJECT0(QGradient); MO_ADD_PROPERTY (QGradient, coordinateMode, setCoordinateMode); MO_ADD_PROPERTY (QGradient, spread, setSpread); MO_ADD_PROPERTY (QGradient, stops, setStops); MO_ADD_PROPERTY_RO(QGradient, type); MO_ADD_METAOBJECT1(QConicalGradient, QGradient); MO_ADD_PROPERTY (QConicalGradient, angle, setAngle); MO_ADD_PROPERTY_O2(QConicalGradient, center, setCenter); MO_ADD_METAOBJECT1(QLinearGradient, QGradient); MO_ADD_PROPERTY_O2(QLinearGradient, finalStop, setFinalStop); MO_ADD_PROPERTY_O2(QLinearGradient, start, setStart); MO_ADD_METAOBJECT1(QRadialGradient, QGradient); MO_ADD_PROPERTY_O2(QRadialGradient, center, setCenter); MO_ADD_PROPERTY (QRadialGradient, centerRadius, setCenterRadius); MO_ADD_PROPERTY_O2(QRadialGradient, focalPoint, setFocalPoint); MO_ADD_PROPERTY (QRadialGradient, focalRadius, setFocalRadius); MO_ADD_PROPERTY (QRadialGradient, radius, setRadius); MO_ADD_METAOBJECT0(QBrush); MO_ADD_PROPERTY_O2(QBrush, color, setColor); MO_ADD_PROPERTY_RO(QBrush, gradient); MO_ADD_PROPERTY_RO(QBrush, isOpaque); MO_ADD_PROPERTY(QBrush, matrix, setMatrix); MO_ADD_PROPERTY(QBrush, style, setStyle); MO_ADD_PROPERTY(QBrush, texture, setTexture); MO_ADD_PROPERTY(QBrush, transform, setTransform); MO_ADD_METAOBJECT0(QFont); MO_ADD_PROPERTY(QFont, bold, setBold); MO_ADD_PROPERTY(QFont, capitalization, setCapitalization); MO_ADD_PROPERTY_RO(QFont, defaultFamily); MO_ADD_PROPERTY_RO(QFont, exactMatch); MO_ADD_PROPERTY(QFont, family, setFamily); MO_ADD_PROPERTY(QFont, fixedPitch, setFixedPitch); MO_ADD_PROPERTY(QFont, hintingPreference, setHintingPreference); MO_ADD_PROPERTY(QFont, italic, setItalic); MO_ADD_PROPERTY(QFont, kerning, setKerning); MO_ADD_PROPERTY_RO(QFont, key); MO_ADD_PROPERTY_RO(QFont, lastResortFamily); // MO_ADD_PROPERTY_RO(QFont, lastResortFont); asserts at runtime!? MO_ADD_PROPERTY_RO(QFont, letterSpacing); MO_ADD_PROPERTY_RO(QFont, letterSpacingType); MO_ADD_PROPERTY(QFont, overline, setOverline); MO_ADD_PROPERTY(QFont, pixelSize, setPixelSize); MO_ADD_PROPERTY(QFont, pointSize, setPointSize); MO_ADD_PROPERTY(QFont, pointSizeF, setPointSizeF); MO_ADD_PROPERTY(QFont, stretch, setStretch); MO_ADD_PROPERTY(QFont, strikeOut, setStrikeOut); MO_ADD_PROPERTY(QFont, style, setStyle); MO_ADD_PROPERTY_RO(QFont, styleHint); MO_ADD_PROPERTY(QFont, styleName, setStyleName); MO_ADD_PROPERTY(QFont, styleStrategy, setStyleStrategy); MO_ADD_PROPERTY_RO(QFont, substitutions); MO_ADD_PROPERTY(QFont, underline, setUnderline); MO_ADD_PROPERTY(QFont, weight, setWeight); MO_ADD_PROPERTY(QFont, wordSpacing, setWordSpacing); MO_ADD_METAOBJECT0(QIcon); MO_ADD_PROPERTY_RO(QIcon, cacheKey); #if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0) MO_ADD_PROPERTY(QIcon, isMask, setIsMask); #endif MO_ADD_PROPERTY_RO(QIcon, isNull); MO_ADD_PROPERTY_RO(QIcon, name); MO_ADD_PROPERTY_RO(QIcon, themeName); MO_ADD_PROPERTY_RO(QIcon, themeSearchPaths); MO_ADD_METAOBJECT0(QPen); MO_ADD_PROPERTY(QPen, brush, setBrush); MO_ADD_PROPERTY(QPen, capStyle, setCapStyle); MO_ADD_PROPERTY(QPen, color, setColor); MO_ADD_PROPERTY(QPen, dashOffset, setDashOffset); MO_ADD_PROPERTY(QPen, dashPattern, setDashPattern); MO_ADD_PROPERTY(QPen, isCosmetic, setCosmetic); MO_ADD_PROPERTY_RO(QPen, isSolid); MO_ADD_PROPERTY(QPen, joinStyle, setJoinStyle); MO_ADD_PROPERTY(QPen, miterLimit, setMiterLimit); MO_ADD_PROPERTY(QPen, style, setStyle); MO_ADD_PROPERTY(QPen, width, setWidth); MO_ADD_PROPERTY(QPen, widthF, setWidthF); MO_ADD_METAOBJECT0(QPixelFormat); MO_ADD_PROPERTY_RO(QPixelFormat, alphaPosition); MO_ADD_PROPERTY_RO(QPixelFormat, alphaSize); MO_ADD_PROPERTY_RO(QPixelFormat, alphaUsage); MO_ADD_PROPERTY_RO(QPixelFormat, bitsPerPixel); MO_ADD_PROPERTY_RO(QPixelFormat, blackSize); MO_ADD_PROPERTY_RO(QPixelFormat, blueSize); MO_ADD_PROPERTY_RO(QPixelFormat, brightnessSize); MO_ADD_PROPERTY_RO(QPixelFormat, byteOrder); MO_ADD_PROPERTY_RO(QPixelFormat, channelCount); MO_ADD_PROPERTY_RO(QPixelFormat, colorModel); MO_ADD_PROPERTY_RO(QPixelFormat, cyanSize); MO_ADD_PROPERTY_RO(QPixelFormat, greenSize); MO_ADD_PROPERTY_RO(QPixelFormat, hueSize); MO_ADD_PROPERTY_RO(QPixelFormat, lightnessSize); MO_ADD_PROPERTY_RO(QPixelFormat, magentaSize); MO_ADD_PROPERTY_RO(QPixelFormat, premultiplied); MO_ADD_PROPERTY_RO(QPixelFormat, redSize); MO_ADD_PROPERTY_RO(QPixelFormat, saturationSize); MO_ADD_PROPERTY_RO(QPixelFormat, typeInterpretation); MO_ADD_PROPERTY_RO(QPixelFormat, yellowSize); MO_ADD_PROPERTY_RO(QPixelFormat, yuvLayout); MO_ADD_METAOBJECT1(QDropEvent, QEvent); MO_ADD_PROPERTY_RO(QDropEvent, dropAction); MO_ADD_PROPERTY_RO(QDropEvent, keyboardModifiers); MO_ADD_PROPERTY_RO(QDropEvent, mimeData); MO_ADD_PROPERTY_RO(QDropEvent, mouseButtons); MO_ADD_PROPERTY_RO(QDropEvent, pos); MO_ADD_PROPERTY_RO(QDropEvent, posF); MO_ADD_PROPERTY_RO(QDropEvent, possibleActions); MO_ADD_PROPERTY_RO(QDropEvent, proposedAction); MO_ADD_PROPERTY_RO(QDropEvent, source); MO_ADD_METAOBJECT1(QDragMoveEvent, QDropEvent); MO_ADD_PROPERTY_RO(QDragMoveEvent, answerRect); MO_ADD_METAOBJECT1(QInputEvent, QEvent); MO_ADD_PROPERTY_RO(QInputEvent, modifiers); MO_ADD_PROPERTY_RO(QInputEvent, timestamp); MO_ADD_METAOBJECT1(QContextMenuEvent, QInputEvent); MO_ADD_PROPERTY_RO(QContextMenuEvent, globalPos); MO_ADD_PROPERTY_RO(QContextMenuEvent, pos); MO_ADD_PROPERTY_RO(QContextMenuEvent, reason); MO_ADD_METAOBJECT1(QMouseEvent, QInputEvent); MO_ADD_PROPERTY_RO(QMouseEvent, button); MO_ADD_PROPERTY_RO(QMouseEvent, buttons); MO_ADD_PROPERTY_RO(QMouseEvent, flags); MO_ADD_PROPERTY_RO(QMouseEvent, globalPos); MO_ADD_PROPERTY_RO(QMouseEvent, localPos); MO_ADD_PROPERTY_RO(QMouseEvent, pos); MO_ADD_PROPERTY_RO(QMouseEvent, screenPos); MO_ADD_PROPERTY_RO(QMouseEvent, source); MO_ADD_PROPERTY_RO(QMouseEvent, windowPos); MO_ADD_METAOBJECT1(QHoverEvent, QInputEvent); MO_ADD_PROPERTY_RO(QHoverEvent, posF); MO_ADD_PROPERTY_RO(QHoverEvent, oldPosF); MO_ADD_METAOBJECT1(QWheelEvent, QInputEvent); MO_ADD_PROPERTY_RO(QWheelEvent, pixelDelta); MO_ADD_PROPERTY_RO(QWheelEvent, angleDelta); MO_ADD_PROPERTY_RO(QWheelEvent, delta); MO_ADD_PROPERTY_RO(QWheelEvent, orientation); MO_ADD_PROPERTY_RO(QWheelEvent, posF); MO_ADD_PROPERTY_RO(QWheelEvent, globalPosF); MO_ADD_PROPERTY_RO(QWheelEvent, buttons); MO_ADD_PROPERTY_RO(QWheelEvent, phase); #if QT_VERSION >= QT_VERSION_CHECK(5, 7, 0) MO_ADD_PROPERTY_RO(QWheelEvent, inverted); #endif MO_ADD_PROPERTY_RO(QWheelEvent, source); MO_ADD_METAOBJECT1(QTabletEvent, QInputEvent); MO_ADD_PROPERTY_RO(QTabletEvent, posF); MO_ADD_PROPERTY_RO(QTabletEvent, globalPosF); MO_ADD_PROPERTY_RO(QTabletEvent, device); MO_ADD_PROPERTY_RO(QTabletEvent, pointerType); MO_ADD_PROPERTY_RO(QTabletEvent, uniqueId); MO_ADD_PROPERTY_RO(QTabletEvent, pressure); MO_ADD_PROPERTY_RO(QTabletEvent, z); MO_ADD_PROPERTY_RO(QTabletEvent, tangentialPressure); MO_ADD_PROPERTY_RO(QTabletEvent, rotation); MO_ADD_PROPERTY_RO(QTabletEvent, xTilt); MO_ADD_PROPERTY_RO(QTabletEvent, yTilt); MO_ADD_PROPERTY_RO(QTabletEvent, button); MO_ADD_PROPERTY_RO(QTabletEvent, buttons); MO_ADD_METAOBJECT1(QNativeGestureEvent, QInputEvent); MO_ADD_PROPERTY_RO(QNativeGestureEvent, gestureType); MO_ADD_PROPERTY_RO(QNativeGestureEvent, value); MO_ADD_PROPERTY_RO(QNativeGestureEvent, localPos); MO_ADD_PROPERTY_RO(QNativeGestureEvent, windowPos); MO_ADD_PROPERTY_RO(QNativeGestureEvent, screenPos); #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) MO_ADD_PROPERTY_RO(QNativeGestureEvent, device); #endif MO_ADD_METAOBJECT1(QKeyEvent, QInputEvent); MO_ADD_PROPERTY_RO(QKeyEvent, key); MO_ADD_PROPERTY_RO(QKeyEvent, modifiers); MO_ADD_PROPERTY_RO(QKeyEvent, text); MO_ADD_PROPERTY_RO(QKeyEvent, isAutoRepeat); MO_ADD_PROPERTY_RO(QKeyEvent, count); MO_ADD_PROPERTY_RO(QKeyEvent, nativeScanCode); MO_ADD_PROPERTY_RO(QKeyEvent, nativeVirtualKey); MO_ADD_PROPERTY_RO(QKeyEvent, nativeModifiers); MO_ADD_METAOBJECT1(QFocusEvent, QEvent); MO_ADD_PROPERTY_RO(QFocusEvent, gotFocus); MO_ADD_PROPERTY_RO(QFocusEvent, lostFocus); MO_ADD_PROPERTY_RO(QFocusEvent, reason); MO_ADD_METAOBJECT1(QPaintEvent, QEvent); MO_ADD_PROPERTY_RO(QPaintEvent, rect); MO_ADD_PROPERTY_RO(QPaintEvent, region); MO_ADD_METAOBJECT1(QInputMethodEvent, QEvent); MO_ADD_PROPERTY_RO(QInputMethodEvent, attributes); MO_ADD_PROPERTY_RO(QInputMethodEvent, preeditString); MO_ADD_PROPERTY_RO(QInputMethodEvent, commitString); MO_ADD_PROPERTY_RO(QInputMethodEvent, replacementStart); MO_ADD_PROPERTY_RO(QInputMethodEvent, replacementLength); MO_ADD_METAOBJECT1(QInputMethodQueryEvent, QEvent); MO_ADD_PROPERTY_RO(QInputMethodQueryEvent, queries); MO_ADD_METAOBJECT1(QHelpEvent, QEvent) MO_ADD_PROPERTY_RO(QHelpEvent, globalPos) MO_ADD_PROPERTY_RO(QHelpEvent, pos) MO_ADD_METAOBJECT1(QMoveEvent, QEvent); MO_ADD_PROPERTY_RO(QMoveEvent, pos); MO_ADD_PROPERTY_RO(QMoveEvent, oldPos); MO_ADD_METAOBJECT1(QExposeEvent, QEvent); MO_ADD_PROPERTY_RO(QExposeEvent, region); MO_ADD_METAOBJECT1(QResizeEvent, QEvent); MO_ADD_PROPERTY_RO(QResizeEvent, size); MO_ADD_PROPERTY_RO(QResizeEvent, oldSize); MO_ADD_METAOBJECT1(QWindowStateChangeEvent, QEvent); MO_ADD_PROPERTY_RO(QWindowStateChangeEvent, oldState); MO_ADD_PROPERTY_RO(QWindowStateChangeEvent, isOverride); MO_ADD_METAOBJECT1(QTouchEvent, QInputEvent); MO_ADD_PROPERTY_RO(QTouchEvent, device); MO_ADD_PROPERTY_RO(QTouchEvent, target); MO_ADD_PROPERTY_RO(QTouchEvent, touchPoints); MO_ADD_PROPERTY_RO(QTouchEvent, window); MO_ADD_METAOBJECT0(QTouchEvent::TouchPoint); MO_ADD_PROPERTY_RO(QTouchEvent::TouchPoint, id); MO_ADD_PROPERTY_RO(QTouchEvent::TouchPoint, state); MO_ADD_PROPERTY_RO(QTouchEvent::TouchPoint, pos); MO_ADD_PROPERTY_RO(QTouchEvent::TouchPoint, startPos); MO_ADD_PROPERTY_RO(QTouchEvent::TouchPoint, lastPos); // TODO: add other pos values? MO_ADD_PROPERTY_RO(QTouchEvent::TouchPoint, pressure); MO_ADD_PROPERTY_RO(QTouchEvent::TouchPoint, velocity); MO_ADD_PROPERTY_RO(QTouchEvent::TouchPoint, flags); MO_ADD_PROPERTY_RO(QTouchEvent::TouchPoint, rawScreenPositions); #if QT_VERSION < QT_VERSION_CHECK(5, 9, 0) MO_ADD_PROPERTY_RO(QTouchEvent::TouchPoint, rect); #endif #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) MO_ADD_PROPERTY_RO(QTouchEvent::TouchPoint, uniqueId); MO_ADD_PROPERTY_RO(QTouchEvent::TouchPoint, rotation); #endif #if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0) MO_ADD_PROPERTY_RO(QTouchEvent::TouchPoint, ellipseDiameters); #endif MO_ADD_METAOBJECT0(QTouchDevice); MO_ADD_PROPERTY_RO(QTouchDevice, capabilities); MO_ADD_PROPERTY_RO(QTouchDevice, maximumTouchPoints); MO_ADD_PROPERTY_RO(QTouchDevice, name); MO_ADD_PROPERTY_RO(QTouchDevice, type); MO_ADD_METAOBJECT1(QScrollPrepareEvent, QEvent); MO_ADD_PROPERTY_RO(QScrollPrepareEvent, startPos); MO_ADD_PROPERTY_RO(QScrollPrepareEvent, viewportSize); MO_ADD_PROPERTY_RO(QScrollPrepareEvent, contentPosRange); MO_ADD_PROPERTY_RO(QScrollPrepareEvent, contentPos); MO_ADD_METAOBJECT1(QScrollEvent, QEvent); MO_ADD_PROPERTY_RO(QScrollEvent, contentPos); MO_ADD_PROPERTY_RO(QScrollEvent, overshootDistance); MO_ADD_PROPERTY_RO(QScrollEvent, scrollState); MO_ADD_METAOBJECT1(QScreenOrientationChangeEvent, QEvent); MO_ADD_PROPERTY_RO(QScreenOrientationChangeEvent, screen); MO_ADD_PROPERTY_RO(QScreenOrientationChangeEvent, orientation); MO_ADD_METAOBJECT1(QShortcutEvent, QEvent); MO_ADD_PROPERTY_RO(QShortcutEvent, isAmbiguous); MO_ADD_PROPERTY_RO(QShortcutEvent, key); MO_ADD_PROPERTY_RO(QShortcutEvent, shortcutId); MO_ADD_METAOBJECT1(QStatusTipEvent, QEvent); MO_ADD_PROPERTY_RO(QStatusTipEvent, tip); MO_ADD_METAOBJECT1(QApplicationStateChangeEvent, QEvent); MO_ADD_PROPERTY_RO(QApplicationStateChangeEvent, applicationState); MO_ADD_METAOBJECT1(QEnterEvent, QEvent); MO_ADD_PROPERTY_RO(QEnterEvent, globalPos); MO_ADD_PROPERTY_RO(QEnterEvent, localPos); MO_ADD_PROPERTY_RO(QEnterEvent, pos); MO_ADD_PROPERTY_RO(QEnterEvent, screenPos); MO_ADD_PROPERTY_RO(QEnterEvent, windowPos); } static QString surfaceFormatToString(const QSurfaceFormat &format) { QString s; switch (format.renderableType()) { case QSurfaceFormat::DefaultRenderableType: s += QStringLiteral("Default"); break; case QSurfaceFormat::OpenGL: s += QStringLiteral("OpenGL"); break; case QSurfaceFormat::OpenGLES: s += QStringLiteral("OpenGL ES"); break; case QSurfaceFormat::OpenVG: s += QStringLiteral("OpenVG"); break; } s += " (" + QString::number(format.majorVersion()) +'.' + QString::number(format.minorVersion()); switch (format.profile()) { case QSurfaceFormat::CoreProfile: s += QStringLiteral(" core"); break; case QSurfaceFormat::CompatibilityProfile: s += QStringLiteral(" compat"); break; case QSurfaceFormat::NoProfile: break; } s += ')'; s += " RGBA: " + QString::number(format.redBufferSize()) +'/' + QString::number(format.greenBufferSize()) +'/' + QString::number(format.blueBufferSize()) +'/' + QString::number(format.alphaBufferSize()); return s; } #define E(x) { QSurface:: x, #x } static const MetaEnum::Value surface_class_table[] = { E(Window), E(Offscreen) }; static const MetaEnum::Value surface_type_table[] = { E(RasterSurface), E(OpenGLSurface), E(RasterGLSurface), #if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0) E(OpenVGSurface), #endif #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) E(VulkanSurface) #endif }; #undef E #ifndef QT_NO_OPENGL static QString shaderTypeToString(const QOpenGLShader::ShaderType type) { QStringList types; #define ST(t) if (type & QOpenGLShader::t) types.push_back(QStringLiteral(#t)); ST(Vertex) ST(Fragment) ST(Geometry) ST(TessellationControl) ST(TessellationEvaluation) ST(Compute) #undef ST if (types.isEmpty()) return QStringLiteral(""); return types.join(QStringLiteral(" | ")); } #endif // QT_NO_OPENGL static QString textLengthToString(const QTextLength &l) { QString typeStr; switch (l.type()) { case QTextLength::VariableLength: typeStr = GuiSupport::tr("variable"); break; case QTextLength::FixedLength: typeStr = GuiSupport::tr("fixed"); break; case QTextLength::PercentageLength: typeStr = GuiSupport::tr("percentage"); break; } return QStringLiteral("%1 (%2)").arg(l.rawValue()).arg(typeStr); } static QString painterPathToString(const QPainterPath &path) { if (path.isEmpty()) return GuiSupport::tr(""); return GuiSupport::tr("<%1 elements>").arg(path.elementCount()); } #define E(x) { QContextMenuEvent::x , #x } static const MetaEnum::Value context_menu_reason_table[] = { E(Mouse), E(Keyboard), E(Other) }; #undef E #define E(x) { QSurfaceFormat:: x, #x } static const MetaEnum::Value surface_format_option_table[] = { E(StereoBuffers), E(DebugContext), E(DeprecatedFunctions), E(ResetNotification) }; #undef E #define E(x) { QFont:: x, #x } static const MetaEnum::Value font_capitalization_table[] = { E(MixedCase), E(AllUppercase), E(AllLowercase), E(SmallCaps), E(Capitalize) }; static const MetaEnum::Value font_hinting_pref_table[] = { E(PreferDefaultHinting), E(PreferNoHinting), E(PreferVerticalHinting), E(PreferFullHinting) }; static const MetaEnum::Value font_spacing_type_table[] = { E(PercentageSpacing), E(AbsoluteSpacing) }; static const MetaEnum::Value font_style_table[] = { E(StyleNormal), E(StyleItalic), E(StyleOblique) }; static const MetaEnum::Value font_style_hint_table[] = { E(AnyStyle), E(SansSerif), E(Helvetica), E(Serif), E(Times), E(TypeWriter), E(Courier), E(OldEnglish), E(Decorative), E(Monospace), E(Fantasy), E(Cursive), E(System) }; #undef E #define E(x) { Qt:: x, #x } static const MetaEnum::Value mouse_event_flag_table[] = { E(MouseEventCreatedDoubleClick), E(MouseEventFlagMask) }; #undef E #define E(x) { QPainter:: x, #x } static const MetaEnum::Value painter_composition_mode_table[] = { E(CompositionMode_SourceOver), E(CompositionMode_DestinationOver), E(CompositionMode_Clear), E(CompositionMode_Source), E(CompositionMode_Destination), E(CompositionMode_SourceIn), E(CompositionMode_DestinationIn), E(CompositionMode_SourceOut), E(CompositionMode_DestinationOut), E(CompositionMode_SourceAtop), E(CompositionMode_DestinationAtop), E(CompositionMode_Xor), E(CompositionMode_Plus), E(CompositionMode_Multiply), E(CompositionMode_Screen), E(CompositionMode_Overlay), E(CompositionMode_Darken), E(CompositionMode_Lighten), E(CompositionMode_ColorDodge), E(CompositionMode_ColorBurn), E(CompositionMode_HardLight), E(CompositionMode_SoftLight), E(CompositionMode_Difference), E(CompositionMode_Exclusion), E(RasterOp_SourceOrDestination), E(RasterOp_SourceAndDestination), E(RasterOp_SourceXorDestination), E(RasterOp_NotSourceAndNotDestination), E(RasterOp_NotSourceOrNotDestination), E(RasterOp_NotSourceXorDestination), E(RasterOp_NotSource), E(RasterOp_NotSourceAndDestination), E(RasterOp_SourceAndNotDestination), E(RasterOp_NotSourceOrDestination), E(RasterOp_ClearDestination), E(RasterOp_SetDestination), E(RasterOp_NotDestination), E(RasterOp_SourceOrNotDestination) }; static const MetaEnum::Value painter_render_hint_table[] = { E(Antialiasing), E(TextAntialiasing), E(SmoothPixmapTransform), E(HighQualityAntialiasing), E(NonCosmeticDefaultPen), E(Qt4CompatiblePainting) }; #undef E #define E(x) { QPaintEngine:: x, #x } static const MetaEnum::Value paintengine_polygon_draw_mode_table[] = { E(OddEvenMode), E(WindingMode), E(ConvexMode), E(PolylineMode) }; #undef E #define E(x) { QPlatformPixmap:: x, #x } static const MetaEnum::Value platformpixmap_classid_table[] = { E(RasterClass), E(DirectFBClass), E(BlitterClass), E(Direct2DClass), E(CustomClass) }; #undef E #define E(x) { QImage:: x, #x } static const MetaEnum::Value image_format_table[] = { E(Format_Invalid), E(Format_Mono), E(Format_MonoLSB), E(Format_Indexed8), E(Format_RGB32), E(Format_ARGB32), E(Format_ARGB32_Premultiplied), E(Format_RGB16), E(Format_ARGB8565_Premultiplied), E(Format_RGB666), E(Format_ARGB6666_Premultiplied), E(Format_RGB555), E(Format_ARGB8555_Premultiplied), E(Format_RGB888), E(Format_RGB444), E(Format_ARGB4444_Premultiplied), E(Format_RGBX8888), E(Format_RGBA8888), E(Format_RGBA8888_Premultiplied), E(Format_BGR30), E(Format_A2BGR30_Premultiplied), E(Format_RGB30), E(Format_A2RGB30_Premultiplied), E(Format_Alpha8), E(Format_Grayscale8) }; #undef E #define E(x) { QPixelFormat:: x, #x } static const MetaEnum::Value pixelformat_alphaposition_table[] = { E(AtBeginning), E(AtEnd) }; static const MetaEnum::Value pixelformat_alphapremultiplied_table[] = { E(NotPremultiplied), E(Premultiplied) }; static const MetaEnum::Value pixelformat_alphausage_table[] = { E(UsesAlpha), E(IgnoresAlpha) }; static const MetaEnum::Value pixelformat_byteorder_table[] = { E(LittleEndian), E(BigEndian), E(CurrentSystemEndian) }; static const MetaEnum::Value pixelformat_colormodel_table[] = { E(RGB), E(BGR), E(Indexed), E(Grayscale), E(CMYK), E(HSL), E(HSV), E(YUV), E(Alpha) }; static const MetaEnum::Value pixelformat_typeinterpretation_table[] = { E(UnsignedInteger), E(UnsignedShort), E(UnsignedByte), E(FloatingPoint) }; static const MetaEnum::Value pixelformat_yuvlayout_table[] = { E(YUV444), E(YUV422), E(YUV411), E(YUV420P), E(YUV420SP), E(YV12), E(UYVY), E(YUYV), E(NV12), E(NV21), E(IMC1), E(IMC2), E(IMC3), E(IMC4), E(Y8), E(Y16) }; #undef E #define E(x) { QTouchEvent::TouchPoint:: x, #x } static const MetaEnum::Value touch_point_info_flag_table[] = { E(Pen), #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) E(Token) #endif }; #undef E #define E(x) { QTouchDevice:: x, #x } static const MetaEnum::Value touch_device_capabilitites_flag_table[] = { E(Position), E(Area), E(Pressure), E(Velocity), E(RawPositions), E(NormalizedPosition), E(MouseEmulation) }; #undef E static QString brushToString(const QBrush &b) { return VariantHandler::displayString(b.color()) + QLatin1String(", ") + EnumUtil::enumToString(QVariant::fromValue(b.style())); } static QString penToString(const QPen &p) { QStringList l; l.reserve(8); l.push_back(GuiSupport::tr("width: %1").arg(p.width())); l.push_back(GuiSupport::tr("brush: %1").arg(brushToString(p.brush()))); l.push_back(EnumUtil::enumToString(QVariant::fromValue(p.style()))); l.push_back(EnumUtil::enumToString(QVariant::fromValue(p.capStyle()))); l.push_back(EnumUtil::enumToString(QVariant::fromValue(p.joinStyle()))); if (p.joinStyle() == Qt::MiterJoin) l.push_back(GuiSupport::tr("miter limit: %1").arg(p.miterLimit())); if (!p.dashPattern().isEmpty()) { QStringList dashes; dashes.reserve(p.dashPattern().size()); foreach (auto v, p.dashPattern()) dashes.push_back(QString::number(v)); l.push_back(GuiSupport::tr("dash pattern: (%1)").arg(dashes.join(QLatin1String(", ")))); } if (p.dashOffset() != 0.0) l.push_back(GuiSupport::tr("dash offset: %1").arg(p.dashOffset())); return l.join(QLatin1String(", ")); } static QString regionToString(const QRegion ®ion) { if (region.isNull()) return QStringLiteral(""); if (region.isEmpty()) return QStringLiteral(""); if (region.rectCount() == 1) return VariantHandler::displayString(region.rects().at(0)); QStringList rects; rects.reserve(region.rectCount()); foreach (const auto &r, region.rects()) rects.push_back(VariantHandler::displayString(r)); return GuiSupport::tr("[%1]: %2").arg( VariantHandler::displayString(region.boundingRect()), rects.join(QLatin1String("; ")) ); } static QString imageToString(const QImage &image) { return VariantHandler::displayString(image.size()); } static QString pixmapToString(const QPixmap &pixmap) { return VariantHandler::displayString(pixmap.size()); } void GuiSupport::registerVariantHandler() { VariantHandler::registerStringConverter(Util::displayString); VariantHandler::registerStringConverter(Util::displayString); VariantHandler::registerStringConverter(surfaceFormatToString); ER_REGISTER_ENUM(QContextMenuEvent, Reason, context_menu_reason_table); ER_REGISTER_ENUM(QSurface, SurfaceClass, surface_class_table); ER_REGISTER_ENUM(QSurface, SurfaceType, surface_type_table); ER_REGISTER_FLAGS(QSurfaceFormat, FormatOptions, surface_format_option_table); #ifndef QT_NO_OPENGL VariantHandler::registerStringConverter(shaderTypeToString); #endif ER_REGISTER_ENUM(QFont, Capitalization, font_capitalization_table); ER_REGISTER_ENUM(QFont, HintingPreference, font_hinting_pref_table); ER_REGISTER_ENUM(QFont, SpacingType, font_spacing_type_table); ER_REGISTER_ENUM(QFont, Style, font_style_table); ER_REGISTER_ENUM(QFont, StyleHint, font_style_hint_table); ER_REGISTER_ENUM(QImage, Format, image_format_table); ER_REGISTER_ENUM(QPainter, CompositionMode, painter_composition_mode_table); ER_REGISTER_FLAGS(QPainter, RenderHints, painter_render_hint_table); ER_REGISTER_ENUM(QPaintEngine, PolygonDrawMode, paintengine_polygon_draw_mode_table); ER_REGISTER_FLAGS(Qt, MouseEventFlags, mouse_event_flag_table); VariantHandler::registerStringConverter(brushToString); VariantHandler::registerStringConverter(Util::addressToString); VariantHandler::registerStringConverter(imageToString); VariantHandler::registerStringConverter(painterPathToString); VariantHandler::registerStringConverter(penToString); VariantHandler::registerStringConverter(pixmapToString); VariantHandler::registerStringConverter(regionToString); VariantHandler::registerStringConverter(textLengthToString); VariantHandler::registerStringConverter >([](const QPair &p) { return QString(VariantHandler::displayString(p.first) + QLatin1String(": ") + VariantHandler::displayString(p.second)); }); ER_REGISTER_ENUM(QPlatformPixmap, ClassId, platformpixmap_classid_table); VariantHandler::registerStringConverter(Util::addressToString); VariantHandler::registerStringConverter(Util::addressToString); ER_REGISTER_ENUM(QPixelFormat, AlphaPosition, pixelformat_alphaposition_table); ER_REGISTER_ENUM(QPixelFormat, AlphaPremultiplied, pixelformat_alphapremultiplied_table); ER_REGISTER_ENUM(QPixelFormat, AlphaUsage, pixelformat_alphausage_table); ER_REGISTER_ENUM(QPixelFormat, ByteOrder, pixelformat_byteorder_table); ER_REGISTER_ENUM(QPixelFormat, ColorModel, pixelformat_colormodel_table); ER_REGISTER_ENUM(QPixelFormat, TypeInterpretation, pixelformat_typeinterpretation_table); ER_REGISTER_ENUM(QPixelFormat, YUVLayout, pixelformat_yuvlayout_table); ER_REGISTER_FLAGS(QTouchEvent::TouchPoint, InfoFlags, touch_point_info_flag_table); ER_REGISTER_FLAGS(QTouchDevice, Capabilities, touch_device_capabilitites_flag_table); } QObject *GuiSupport::targetObject(QObject *object) const { return object ? object : qobject_cast(qApp); } QIcon GuiSupport::createIcon(const QIcon &oldIcon, QWindow *w) { static QIcon gammarayIcon; if (gammarayIcon.availableSizes().isEmpty()) { gammarayIcon.addFile(QLatin1String(":/gammaray/images/gammaray-inject-16.png")); gammarayIcon.addFile(QLatin1String(":/gammaray/images/gammaray-inject-22.png")); gammarayIcon.addFile(QLatin1String(":/gammaray/images/gammaray-inject-24.png")); gammarayIcon.addFile(QLatin1String(":/gammaray/images/gammaray-inject-32.png")); gammarayIcon.addFile(QLatin1String(":/gammaray/images/gammaray-inject-48.png")); gammarayIcon.addFile(QLatin1String(":/gammaray/images/gammaray-inject-64.png")); gammarayIcon.addFile(QLatin1String(":/gammaray/images/gammaray-inject-128.png")); } QObject *target = targetObject(w); // As windows icon can be provided by the qXXApplication::windowIcon() // We need to compute this one first. if (target != qApp) { // There is no real notification to track application window icon change. // Then make sure the application icon is up-to-date first. const auto ait = m_iconAndTitleOverrider.objectsIcons.find(qApp); if (ait != m_iconAndTitleOverrider.objectsIcons.end()) { // The application window icon changed... rebuild it. if (ait.value().gammarayIcon.cacheKey() != qApp->windowIcon().cacheKey()) { m_iconAndTitleOverrider.objectsIcons.erase(ait); m_iconAndTitleOverrider.updatingObjectsIcon.remove(w); updateWindowIcon(); m_iconAndTitleOverrider.updatingObjectsIcon << w; return oldIcon; } } else { // Build the application icon m_iconAndTitleOverrider.updatingObjectsIcon.remove(w); updateWindowIcon(); m_iconAndTitleOverrider.updatingObjectsIcon << w; return oldIcon; } } const auto it = m_iconAndTitleOverrider.objectsIcons.constFind(target); if (it != m_iconAndTitleOverrider.objectsIcons.constEnd() && it.value().gammarayIcon.cacheKey() == oldIcon.cacheKey()) { return oldIcon; } const bool highDpiEnabled = qApp->testAttribute(Qt::AA_UseHighDpiPixmaps); QIcon newIcon; foreach (const QSize &size, gammarayIcon.availableSizes()) { QPixmap pix = oldIcon.pixmap(oldIcon.actualSize(size)); if (pix.isNull()) { const qreal ratio = highDpiEnabled ? (w ? w->devicePixelRatio() : qApp->devicePixelRatio()) : 1.0; pix = QPixmap(size * ratio); pix.setDevicePixelRatio(ratio); pix.fill(Qt::transparent); } { QPainter p(&pix); gammarayIcon.paint(&p, QRect(QPoint(), pix.size() / pix.devicePixelRatio())); } newIcon.addPixmap(pix); } return newIcon; } void GuiSupport::updateWindowIcon(QWindow *w) { QObject *target = targetObject(w); Q_ASSERT(!m_iconAndTitleOverrider.updatingObjectsIcon.contains(target)); m_iconAndTitleOverrider.updatingObjectsIcon << target; const QIcon oldIcon = w ? w->icon() : qApp->windowIcon(); const QIcon newIcon = createIcon(oldIcon, w); if (oldIcon.cacheKey() != newIcon.cacheKey()) { m_iconAndTitleOverrider.objectsIcons.insert(target, IconAndTitleOverriderData::Icons(oldIcon, newIcon)); if (w) w->setIcon(newIcon); else qApp->setWindowIcon(newIcon); } m_iconAndTitleOverrider.updatingObjectsIcon.remove(target); if (!w && m_iconAndTitleOverrider.updatingObjectsIcon.isEmpty()) { foreach (auto w, qApp->topLevelWindows()) { if (isAcceptableWindow(w)) updateWindowIcon(w); } } } void GuiSupport::updateWindowTitle(QWindow *w) { QObject *target = targetObject(w); Q_ASSERT(!m_iconAndTitleOverrider.updatingObjectsTitle.contains(target)); m_iconAndTitleOverrider.updatingObjectsTitle << target; if (!w->title().endsWith(m_iconAndTitleOverrider.titleSuffix)) w->setTitle(w->title() + m_iconAndTitleOverrider.titleSuffix); m_iconAndTitleOverrider.updatingObjectsTitle.remove(target); } void GuiSupport::restoreWindowIcon(QWindow *w) { QObject *target = targetObject(w); Q_ASSERT(!m_iconAndTitleOverrider.updatingObjectsIcon.contains(target)); m_iconAndTitleOverrider.updatingObjectsIcon << target; auto it = m_iconAndTitleOverrider.objectsIcons.find(target); if (it != m_iconAndTitleOverrider.objectsIcons.end()) { if (w) w->setIcon(it.value().originalIcon); else qApp->setWindowIcon(it.value().originalIcon); m_iconAndTitleOverrider.objectsIcons.erase(it); } m_iconAndTitleOverrider.updatingObjectsIcon.remove(target); if (!w && m_iconAndTitleOverrider.updatingObjectsIcon.isEmpty()) { foreach (auto w, qApp->topLevelWindows()) { if (isAcceptableWindow(w)) restoreWindowIcon(w); } } } void GuiSupport::restoreWindowTitle(QWindow *w) { Q_ASSERT(!m_iconAndTitleOverrider.updatingObjectsTitle.contains(w)); m_iconAndTitleOverrider.updatingObjectsTitle << w; w->setTitle(w->title().remove(m_iconAndTitleOverrider.titleSuffix)); m_iconAndTitleOverrider.updatingObjectsTitle.remove(w); } void GuiSupport::restoreIconAndTitle() { if (qApp->closingDown()) return; restoreWindowIcon(); foreach (auto w, qApp->topLevelWindows()) { if (isAcceptableWindow(w)) restoreWindowTitle(w); } } void GuiSupport::discoverObjects() { foreach (QWindow *window, qApp->topLevelWindows()) m_probe->discoverObject(window); } void GuiSupport::objectCreated(QObject *object) { if (qobject_cast(object)) discoverObjects(); } bool GuiSupport::eventFilter(QObject *watched, QEvent *event) { if (event->type() == QEvent::WindowIconChange) { if (auto w = qobject_cast(watched)) { if (!m_iconAndTitleOverrider.updatingObjectsIcon.contains(qApp) && !m_iconAndTitleOverrider.updatingObjectsIcon.contains(w)) { if (isAcceptableWindow(w)) updateWindowIcon(w); } } } else if (event->type() == QEvent::WindowTitleChange) { if (auto w = qobject_cast(watched)) { if (!m_iconAndTitleOverrider.updatingObjectsTitle.contains(qApp) && !m_iconAndTitleOverrider.updatingObjectsTitle.contains(w)) { if (isAcceptableWindow(w)) updateWindowTitle(w); } } } return QObject::eventFilter(watched, event); } GuiSupportFactory::GuiSupportFactory(QObject *parent) : QObject(parent) { } gammaray-2.11.3/plugins/guisupport/guisupport.h000066400000000000000000000056071413310443000216710ustar00rootroot00000000000000/* guisupport.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_GUISUPPORT_H #define GAMMARAY_GUISUPPORT_H #include #include #include QT_BEGIN_NAMESPACE class QWindow; QT_END_NAMESPACE namespace GammaRay { class GuiSupport : public QObject { Q_OBJECT public: explicit GuiSupport(Probe *probe, QObject *parent = nullptr); bool eventFilter(QObject *watched, QEvent *event) override; private: void registerMetaTypes(); void registerVariantHandler(); void discoverObjects(); QObject *targetObject(QObject *object) const; QIcon createIcon(const QIcon &oldIcon, QWindow *w=nullptr); void updateWindowIcon(QWindow *w=nullptr); void updateWindowTitle(QWindow *w); void restoreWindowIcon(QWindow *w=nullptr); void restoreWindowTitle(QWindow *w); struct IconAndTitleOverriderData { struct Icons { explicit Icons(const QIcon &originalIcon = QIcon(), const QIcon &gammarayIcon = QIcon()) : originalIcon(originalIcon) , gammarayIcon(gammarayIcon) { } QIcon originalIcon; QIcon gammarayIcon; }; QSet updatingObjectsIcon; QSet updatingObjectsTitle; QHash objectsIcons; QString titleSuffix; } m_iconAndTitleOverrider; private: Probe *m_probe; private slots: void objectCreated(QObject *object); void restoreIconAndTitle(); }; class GuiSupportFactory : public QObject, public StandardToolFactory { Q_OBJECT Q_INTERFACES(GammaRay::ToolFactory) Q_PLUGIN_METADATA(IID "com.kdab.GammaRay.ToolFactory" FILE "gammaray_guisupport.json") public: explicit GuiSupportFactory(QObject *parent = nullptr); }; } #endif // GAMMARAY_GUISUPPORT_H gammaray-2.11.3/plugins/guisupport/guisupportuifactory.cpp000066400000000000000000000032421413310443000241430ustar00rootroot00000000000000/* guisupportuifactory.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "guisupportuifactory.h" #include "paintanalyzertab.h" #include using namespace GammaRay; QString GuiSupportUiFactory::id() const { return QString(); } void GuiSupportUiFactory::initUi() { PropertyWidget::registerTab(QStringLiteral("painting"), tr( "Paint Analyzer"), PropertyWidgetTabPriority::Advanced); } QWidget *GuiSupportUiFactory::createWidget(QWidget *) { return nullptr; } gammaray-2.11.3/plugins/guisupport/guisupportuifactory.h000066400000000000000000000031661413310443000236150ustar00rootroot00000000000000/* guisupportuifactory.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_GUISUPPORTUIFACTORY_H #define GAMMARAY_GUISUPPORTUIFACTORY_H #include namespace GammaRay { class GuiSupportUiFactory : public QObject, public ToolUiFactory { Q_OBJECT Q_INTERFACES(GammaRay::ToolUiFactory) Q_PLUGIN_METADATA(IID "com.kdab.GammaRay.ToolUiFactory" FILE "gammaray_guisupport.json") public: QString id() const override; void initUi() override; QWidget *createWidget(QWidget *) override; }; } #endif // GAMMARAY_GUISUPPORTUIFACTORY_H gammaray-2.11.3/plugins/guisupport/images/000077500000000000000000000000001413310443000205345ustar00rootroot00000000000000gammaray-2.11.3/plugins/guisupport/images/gammaray-inject-128.png000066400000000000000000000100111413310443000246130ustar00rootroot00000000000000PNG  IHDR>asBIT|d pHYs+tEXtSoftwarewww.inkscape.org<IDATx[$Wy_Ue;/ hCAHH$1"nqlaL/esK^bH"?ٻ&rDP؀oZ/kε/U|yvO\gM]N}UMHS>^%v@Oϴ(]3y{Ŏي5>0'NGL[;bMs'T@$:uUqx(bFpǫ , t筺Vt`wMHAѸ%e%xǪL@O`wMHAѦ%;Spw:pHAmT Hr+zN'U ]ДRkCy*8@5v !hf-AvAEYKAЮAHQhRkwA]I h!Y!2BV0 Dn!ڊBt &xAg2=tA^W!ϔqxBySh_AIFN9x >>}- Pi̩D 4ANY:a0bdN FQ MdNAN, Ui)_*4I Bi$s K!O+"t*‹Bo("j!hrL (E ^z*"j ps* @"hT? L $R. k1{!*r +Aг(4c9 <zAУ(4c9(:9I7CsKt&v 7R$euzzR0 λ%Mz`0V,op|D1R*afjf/JX>)xdtmoF9{DxFz`pܷ1 kA:4M`d M Y`_[ծ&}ʼnsVa0DMT<GˣMB \d,yf6=[ xBadiڇh'4:$R0>vE`rȧ@8%%d!7~b1jJXlX~7w@!T WYcCɩB#@oA2 ;9TdĘK,9S>=3yx4zjFK_nPAAQ'5 BV덍W=|b0wG@oMl@(ib^F?pw½=zT.ު2 7l:'=KOp,ǐCs22`WfdVApC?@G`mNvZ]4(']pH 腫WO!@AhV_ť_0/ |?р cγW7%`1;#(i^\BX]jxm~+ E G}d~v#2b. U8ص+ PZf(TA(gxI*W/]YO'BE G9B@>Ð0{msK̴kuuBXBr@;r"w e}٬׋?\_!еimtћ/5(Y<Wsk{T p ڟ%ˠO<}A'J{)?-.>N G@P(~rte[ ^֌]]_6}Sh 5q@}L|*R]k @5k4_Y\)4EEX$ ?{}'T[[B|#h53?xۇ+ń@/O5nD4wʖ,ûւMg)2x?gP!;~I"i_}q%_Î`[)E]L50j ¿7@ bC0~̸90]mȯy[Cð i eM?DEB_(kIahrP2cElhDMMiؔR1 >y3ڞ%|[)kʛ>4i,42%Ԙd 4%siqnTk,ʶ54a~jQBS:;svN]K?ulIТ)ui㟇ݍ,hD1Eߚqɗ)Y0 3}xSh{/c}U'S 07Qݮ#i&+B, 3@Ьg!0F? =qD$vʦ!Y0 &j%UEf4u #GfCa-8X k]k\ߦ!PY<: C\: P[mo\X6دkt!]h'߬p+;ϴwNPA$qi4G}ry峣g g4V|KQ`@ASbl_9#xjfEgJ%ihfEJO8?UU!A@MOmIDIh* i~ٺl%%4@׊AB}˳h&nYGo.=~+bXn=.n'n'0H=+I!KF(&.bu'=-涀VYf6P}wL>К DAd\ёu3:NҼSbEHO@ ~)tK#: t\vnBI_/?.6 XP}d!zXE cGǠgWܼί} pN$TYxO7 @/wrynem=g*,u I1랟 V׸13*m8}S[LZϘxOuo7c8c5_9dԪ/v D.brj"n[`Ny;DD1I6 &oS&z`%v^=;Al l46})qObNIENDB`gammaray-2.11.3/plugins/guisupport/images/gammaray-inject-128@2x.png000066400000000000000000000213701413310443000251770ustar00rootroot00000000000000PNG  IHDR\rfsBIT|d pHYsetEXtSoftwarewww.inkscape.org< IDATx{$W}߿tξ$jjAXQծ`Or8cH "Dc'cC?pIb]=LqbY*}LL?GOOwcVݿLwսu& ;sDαg(OQjYe! jph567o&fv?isk3Ј|{Z;sj=0+kS$`೚mIconH@M6Hg5gTmH@M6Hg۷;s kAa:R( t'K@ߔ E+jr5Jſ`?_-~ڣܡ`N 4a MYp+ }i_bF "~qFaա by%x00 j0zԊ'@ށ:R(U]by%x`"??ǠcWZ_C$`"s,n)2T6gv XH@$H:DK8طs^3S5Y"0G, cߞ?cLqIb14f7̆H:DQFP- ~UPA XH1Io~snU/AёkC$`$З?ƭxH:,ڗCq ',jo\7rb,"t_Ztx?zGcn΃Q5D!0GD-Ï>7-CX kC$`^ y0PO." ðpg<1z'H``Gn=@y[X5 W"5n-?$U X^5D!0 l)j7!9JtgMC!9@[k9_C$0^XvG ܉yhH*sfђo8P5DV!0KH l ܉ހ xĊH*Df !kr,'=DcA ? p_#<_c$)"aT?B^v Us57NU:n|aH*D"͋5 l UvmBAX UA-rDV!0G:7\zl XH mCA c"'xv  "q[DZBAE XH/: HBD}9T$uz);h@$񖀥z)T$˸JWHgBAEC$OK(h0PE XH@PЦ?"D!Ч H$PGhDŽB$`K;i ڃZ΁q"t_ZtzM(ht XH mL$;"  H@u45o a-X ZPH,(x4@$`"BAEQ @M˿H@u6"T"²[(ht XŸ~n@+D:B$E(ht XŘI \(ht= !p= a$)"K(ht XJ@?I`\ XŘI \(hK3DV1 "DV1&h "DV1 "DV1r "hA!BA@$ЍqDq"4 D XHP( tC$`.~BA@$ u  [2")4]*JWHHPPӈt XǐKPPӈt XKPPӈt XH PPӄEgqâ}ّBAM#":#}iBAM#A$`C$㡠 K%ДM(iD:޸EQ4,4"F+K Dx# 5H@uX,CAM#A$`H`  %[(iD:Ƌ54"DV1)5H@UX$F$H*,BAM#A$`U 0zC$PPӈt- oH`BA{@$H*$+BA U @քF,:cG$`˶Mt XEC(EG"DVپh@$H*ؗBA-: ĄEO$ІpMah>@…Zt4 H: >BA-: tC$`FeS(hYt4 H: K:x%%",ŠST]G"JW@S(H` D X n$EGOD:O hH@uڗZt4$HFg XG0 j A$`=Hp(EGOD:#"hH@ul1̟BA-:}"a=@G ::'H@CZvD@$H:H PPˎht Xh  "Da!? !"Dv > U j+F"2Բm;"Dֱe(h;۲'"D6 *H@ѓ6I L(H "FKbB!A$0x„BNH`pT`1%0ZH vZ?\L@(D:b' 2P$ "{o"Pt G{ B$a@"Pah>%(H "nO<@(D D7MH "n"A/,:Q#xK ba*[Κ]7]G"J  jEH "DQM(H "Df+T$ "3Dm "HFg ES{@D} T B$H}*pB!A$H x@$`"FoqJ{(iD 0ZY@| 5H "D  j@(D:6s#H@@WZBAA@@(D::)T$04tGID!/ S2*Ht [d#q=ɿ59]Q8?SQKbdsenN @=8@<@frkk;V\C*¶lDXR?Uf>St*IDD~V!tvD~x˅ADw\8 @fzwevzfSS ^~mW TW<^!K B$C<{O .\H&czb%V|WKbk*-o'NsSN ,2,JjI`# 7 x7ʧ\XrgNx0zH <[s9U)X~$3ܶw^>" @V],C$pW{_\EEr - {r۷zLq e"K`$0L3|^o0c+VWWysSS= ;8O9 (+z[H "KLI_cV**3ڍs3Sӹ]D!OTW _\ h&D `VTK衘8ŬWʥg/~vNIg'(\3Eqt+"H\}Z]^j ܡ@ lmƇ@(D&R_y&,}kW\\Yn|~Ou@wu-h H{} g +uO_Y2ʍٗLFѿ1Wb- B$Ѝ'\J$S_ Rᛗ^>ռ=G6}nt4 X t#oݻ^fɵ啧^Yܴcd(9Q0}Q ="PnNy͏4DCρp>p%9c/De N8yJvYͶ?\@3CrHH(D$qroy݉t(>ijmf/gHN@(D:l=}#/j|cC-':Zrtֳ׊?y !@$ qSjjF%w`쥇rzg,$dtU& L(~#eecLS FBH$n|Uж91 Dʕ/ahWCk B$N} `23g>zr`B׃A$`U' 茛ޖt|#nnsc`B̵ Q' hA&n/%s"R̛zX !/킼_b:2'ڶk"$Du8}tYlƥ7\\|%n QDu "*z WνrIVoV5]|@(^SC7i/Ø]x+' 1g`$& "I>z%Jrbⵅ|IB! XI,xr]D+R%z²IBշM y'0npX}o%s/g~F~/Ɠ@f8OLS+Saj+ oD1l A=ܮ+}Oq R_l , d%PBpW\q xkWv"k:Q@"3YFO97./MRyTaqc, VFM9L20X%9Ͼ|ALbxm3a*0?Hb%,gb3 LD6az1IhUT*/'ڼ hHbE_KLq>i'=xJcMW t@$a@%`LGM\$!9fUmPPӈ VUnz[tLSO3eG_z6~m(iDt+(?C?"%-eG͕奫|ZCAM#X& vzwL/'&&(& # 6=5ʰH'vE( N>u ]ٹKg6o76 j5PHM~D#Kn.,Lb7?XZ XA,^1[q?+?wk~D]ڡ XAX+!#ȡY<{>' rnH T(iFF3Fbpf^sKgѯw1IkŇW/,l^BAM3@PtIP~9N=1QP®|.9-//_-fy*H<"P!'q :nzh+IBr.^Y/­ۼ XI,D-5 _JO6IH1率m&CA-9DVu QI9'.bzmH/ԒsJ$`U'xb& GJ;ؕs|~:1K[()QBrX˥> UhS~BaJ*zM'+>P()|^C$ uw/ĂsjM`u _ |ރ;ӟ*t @>.;Œ e(V\+5*(VxTaUԅs0x D"XGw}S/,VX6- ,:~K@o\g)À\ I~t.#w{uDx j9%OJ6~b˕7ޒYn"Lb[^j}PP N\5J ='vO%2 BsK_j祴ܜoHbEGk}?pԟM{ȽJcW\Eg֫0 jy%X&DbS]@E`9%xRX+j]!\.h|ԋ:;?4O 5xODؑZH51o}뫋W-ع[ *1@~X8pr/NάɠO 0Dmߏ9boy h)u {zW X:%zn_uB>ZL}g%g~6~p[gO|kP#Vq9x Ɗ|P$ЌHN(7l ɵ׾`!ŏ7V^c4N E$`U'y}EjQ;`zrz`/~y*pE3B'"@X:Ծb6U |ӛům?p" pjvՎ_Z]z h)϶/iIDATh8HW:C.?`>oݚrB>J5}g#3" ] D!`h%Pq_Q2'}س+{y6ci[Mt: W{{#-~ PP 8ý}EN['$7DJ¥˫ʝN07MHCAE3DP)/J}HcaO\{;6ԝ7xU@oo? 0 >D- ԟ ~lYbڬ/16jjHKcoc?wW6"TxjP X9S 0vf{?64Kyn3hY_b0mof?v6 fG&vY.N/θȥ (\vǃیc/~](H6KL ~r &b UW_). (-w;3}?x+O?~|P`r\}fh`uElxK@Նmxb>pVA U%_j;՞f{ĵUw-m޶|c6h`5uWIENDB`gammaray-2.11.3/plugins/guisupport/images/gammaray-inject-128@3x.png000066400000000000000000000363141413310443000252040ustar00rootroot00000000000000PNG  IHDRǵsBIT|d pHYs,K,K=tEXtSoftwarewww.inkscape.org< IDATx{UϽFeY'1yybزI `ɗ.,ay@6a‰_ NB@,yk4h~T޷qn}ww=ku׭#@c !%qo \|-wCZ޾AڏSnsS5H ݿMǾ۰^~s)/ݶY 퇵H;髳پX,@BoH}-X,N^ɿK HD` JAɿK HD` F[?$ͱm_D,K fZ: 2^v#7'fwX6AfLwi<(CnT<\C<濉'ubv-O?ZQfLwi5(S9gBp&QɿS!ɿK@@T0EK SgQC8@'9Պ;Ta @jIS\ "$s l%FLXB 5礂).9yóq'~o$,UX!sR%@J;Ta @jIS\4;_!#vCPQϡe%;Ta @jI3D!&. $;Ta @jISL<r;w~NP%9'L1 \g狕\p/$8vɿK@@T0$͙M,q6sh$,UX!sR$:;2NXB 5礂)&H s'=)w` Ԝ h L;؈9',UX!sRLw39H w` Ԝ @&8xߗgP7;%@/w` Љ *;InP%&P (Tp#C@;lGr% K RsN*⒠<Л}opK@@T0%! p_;NTa @jIS\C/pO*,H9`KD x8?0g'D` Ԝ  ߽;'a Ԝ f蓀g.; 9?XB 5礂)&QCҶIqO*,H9`IWAQ~挖?XB 5礂)&1%#pK9g K@@T0$JC5,UX!sR!J#NyP%9'L1 [A "7L` Ԝ H B=|` Љx% K rM.Y7Ta @jI#N tOR>Ta @jI#%0ܰTa @jI#]%0̰Ta @jI#>0߄YP%9'Ln  Ta DO$ Ta @ny^mL H%&PJ@Jt$1/ 7Jt=/ `a K rM.dGQxK`Xor%K{It%,!xu%#,UK {40^p` 0ܩE.闀:P%9'"@a K@v)Y qK@vT+B 7R)|%ą% K rsN. E8^f 0qa ܜ (޿2K K@@\@" _X:P%9'P4ǭ *,9ѓ ą% K y6Sg=%&{XB 5礂 -w=,UX!sR_k#KI *,H9`BYп",a Ԝ &u*,H9`4%#,UX!sRl%m*,H9`|ޕ %#,UX!sJ4@dX` ԜS -n=%0-#|` ԜS )g=I&XB 5甂J]%dK@@R0+YLTa @j)@߆%+,UX!s:h Ta D'z}XP%_oCQ` 0pzYBP@X:pzY$W%,2%GEY K`X% / %=^+ t%`a dOvp` a \@Nž%%@^# K rsT)`+~i` K@2(rs%#,UX!t$K,UX!Lؿ%*,yX < K@GXB 7I{g>X:P%9z>t%`9'w ,UX!%4KA!};k*,sFTa @jU*,H͹|³` K@@\M>`` K@@?,X:P%9 %};h&Ta @jνAO l %K@GXB 5$ %*,BTa @j%ЭPb[F0P%9K@ oTa @j%P%+,UX!suO>zrt% K sxa K@@<:%+,UX2hc)*,H̷;ϻBZ`)s(b<P%BJp` !x̳% K $ =7&&.,a !a xO3Ta <ݘHr'%#,U( B. -` K@a B. -` K@BIxTOr'%#,U.H%#zK`́ !4BLI0nBppe5n.<<$#@O. -` 舾(W̏@@! 0eFv;།xD,_WW\^! ]I/"_- _XX619:1}4;(%kIšBəY iXL k;z1=a(}l[:5i~nU$V(o%Gpn,6$$" K@G{aEO[6#h۟_r]X>?vNQm+ LSKC >ɠHБ|bjqi XB/_\x`&*cwI! !^ t?; 'iX:ޘO#@1x* ͠w暚Y~n f)z-J`O~'iX:ΘOON~l6ڇϟ]GZ i 4F̳+yDSċ8{#qK C~>ɠHБ1ZmF}-Y|eAS@|HvdPa HON=_TLNϝ{mť@yO~'i1sY~k#q˲em]` (B$"MW@#0skt {b} g~)!tV!` Hw z689sj}e}G@?8~:Oo,Y{lj5}(>.:w٨<"F$bAn,(c풋= Lj鳥Sm#:b@n,}]Ӷ?˳Z pC#FY@n,=[a4d+wy}S42 b* @zriKlq=IDAT6bb( ~Q +K7"1XCω ,Ʈ7鈻X.sؖ4c#K eHvdPŁ%-c̔xsA}-)'mٿV|d]H'ie4k`_gLM Eb} ,>ɠH eNտGC&f^HL,cjU@DHvdPa Р1p`~G'oxܩf}0$+K eHvdPa Zm)珟:lgX@B"B$" K ?pM fO>n8_Ebz  4,|tO|Mu$K "$O2(Ұ@|_ǕFbb%CH s+W%'iX! =)FSX흟;ղ>vr}/ 'iXcz:HyGj %*1<."1+/|b DdIE@{ ƭ"u`N@%?$O2(Ұҡ5sh0k7VWh7 :y`  4,1S_RU Ϟh4,-   4,]_o*O'N>,Oeon@JBHaBB$" K 񷂀Z"S#ӿi/_:- .--n?δ  4,c5! 0v)꥕H `[Lgi 4,ulcb|I!&g~R":u~vE0~ K$O2(ҰcT'$nMvӏZG(*\ ,>ɠH]ZxUql1mY=߲m( Kpŵ%@'iXRK*xN~DoR '/_z]@@'iX}%<50~v7cj~ГrN!8 }A%];_11yN=Qo gP76̟3|:>>ɠHƞ8 2kS;N?qA۲3XYXSX@$"#V17J |IkZʧDb~a˺ҥ |I! "rs{ zJ`V,An&sd]` 䮟^^^ܸ!͟;@PdKpriH@TvM#GsolfPI acecXL&0-/)aBt:rX-Xnťskt%($O2⠡JJ_t]N~e̓Oߏ_NDs*_W=@ }Af[FХ96z@;n6'>u!Ebvތ[a DdIE"7h[#-Pw]^pnqss!Ebe~!(o 4E5G |I4_<9VӚMpacL:@!}A(HQa|J'm=dҥ˞]eF` DdIE"HG |IqloZ憸t~zbԟAz 4K@|I!ŝrw,/X^W$X][Ѵ“vgz}~,dIEm%0@iZ{Cq` DdIE]$`Cg2i0BXuѺxil6Zti]޹H=@DHvdP!/ۈ.7[plqcs#K̠֕ק{^HvdP!-{AÂ/iPw'ͦu>V㾕 k2Ѐ~ c DdIEhB:Q~VTvMz>տH mxg~/%m@{,>ɠHCMĻA/ 'Ej\|gֆVccMV=:طɗX!}A#W͠0~,!o'oiam}AղStS{(K = 4$`VjoՁT`;wf񦕕)2P@2{'i@Q/i E 1z"GI%@}DX"aN*>ɠHkV`|#l85ZP) %ly^eѪ1=bJ)XZo}Wm#%t{$R@2{'iQ(ۢkщaJZY@͔P+ 00Z,IۮJk"fزK╦m_#֛QoqazXYGVqi!7lXаZel|+%׆4。t'iU:HKo1۟k(I+͒Db{*ZYڵ2- 쒁Rn!oZDP6,(-ynɍ=~晋ްaдimZ\~Kד@oCd ދ׍tZ'i(U 4Jo$vn%mSHY)cea(5j ) 1Hh'maJ06Fƍ([mlZ8ryͳ 4כ6l4lhZ-`%Ѥ `6V(mEA{,>ɠHje+K  B !- 6M[[seuRDsmηl"[ Vq!དྷ@oCl c ĀdIEG7(>{jbmd$q? [+u VdIE#yyVͫnw]_[P m8埍G٠LA{F6`%` |\7zi稡[rJ͗ xX  ) ˽/в~VN6dú9#޷NX1 7҂$$,}š7]$x_ZeD4rABA,ri 0?~{rcdǘYL, Xqd枵ʈh{ %U)>Q c Ā H T%А=w=wh#'=fw:_$О@$X1 7҂X^(_zÆ??sCk@Kح`k3 m'5%[~wk;` Ā H HYؔvEՆvB⯕ qvu\߿OGz,Ht$-A(>𝵩,/\0H7HLa] y"B$"MCsۄ(dz_09_2 [4?Vɿ}C^/1,x"B$"MЈ5*;[Bd]Z6gnы SfskIוBo'uj ?@DHvdPѝoFjd=mw9`h慏%}TH'%@DHvdPdOOwM+6O`JMB}_DNY-%'izGlCXM?ھyWEYu{7I=3 ?@DHvdPYVdQ~ǚ[j[% `61>$ ?c%ps ?@DHvdPR}ʏ"$u䳮gXF+G@;y< VwQ%'Iʈ;:}y|T/y Ǚm_K "$O2(rl^*$yyÂecg_H򸹠Tߓ{ 1{8$H@DHvdP4geR<*/ hR+?K jH@DHvdP$ nLىUĄNXɿnoWr? c DdI;p|I&O ~Hȋ3.GOXI *7p/.&g[7'^rJm%IRE^_e/="` $K "$O2\hjcvT֮2)|9d}j{8,` DdI)8Bȗg7V~Ս*/ MK}:$cW@R"B$ {{LM;{ Y$*{%'TSBܴ@>ko5'AE^*_?@߭ %@'BR"B$JrO_>>׏4t"/x%- %:@n"B$J{cZ4-?굯y$sE^* ˝;K л%C.$O2;D%8ί]?c"/V#;Z.h_'> ^7 iUHvdPhȟ;bctDi偮ID|jwosO%% ,>ɠ&@;+{x骙wA#*Lij>@i@>b@$ktB({L +^y?HR'x=-Ԓ?MǃO $` ĀdI Vw~rir|i\9WW6n[ȋ3G>"+BHt#xRîL=׍.dyijw|GC)'D@H $Cc@nU{k²葵ki┑ŵE^J^O $` Ā (FeĐLc\b?j|FS4TKx"%@@ "9zP߇_ۨMҽ4q&۲؈ipg P%rC@.HU1-[{>{bVNm*،U?C:{2M,b@n5y,Mw8q&_$XOD'w@^K4,gNR9ri2Ԕ/۵UVz8E^gsM^Xa DdIBy1G2}K3cܸUꑋ8yc7"W N~!$K "$O2m,67uҀ _3e_Goz>{%T1С|?@:8 ,>ɠ5~ͳ,&7,㛻oatQqx&O4?:>"B23/&t^ )3ŸG.Fo.4?@_Q6,]a Dd ʪMB4SSYvi(yq$8֡I'ǭ zB @DHv?sZ'3o8cl*92H,T7Ǚ@[O \ȓߑ$,~AQ+&6|ᱍ0=ȋ;{\߹_$č&<,w?noxmԾi?J6f=}D {($X; ( o ? _nFs'";/O^BQw?no ^CD;8G*.vMﻳ}l?אH<vb<=QTw]xܸׄΥh/@ظzųo?:@v@=vc ~ڮc-?έ~%Z%u,,m{ޮLH3<~1R.@;߮Up-@p|q3>KVlm]zR@Q+3#ﻳ}l?Bm?%p̾i_u;o2ڟ[RE`om@ @CY.{$V;9}\Җ@7]?@)$%K%DcoeTxnaw[~[ c$i@-Ն: Ĺl8vvŽ{ m?,['IENDB`gammaray-2.11.3/plugins/guisupport/images/gammaray-inject-16.png000066400000000000000000000011431413310443000245350ustar00rootroot00000000000000PNG  IHDRasBIT|d pHYs+tEXtSoftwarewww.inkscape.org<IDAT8?hSQ{K"(B R."n`]tT`}tW\ftEE*FZ{Kb"==s{r -;R 0b[^_ž06: eTQA]u.p#<8Xn΄RM9URXjÊTNs'W]뀨VQw~wIE5DVE-R0  k* +߾b TN8^,oxxy) g{Cf+g}wzyVڷwƷ*_E&iMڦeW$"X׺K>4 5]{F)}žA73BУk ͉'_>,UyNߟP<1+^Ȏ±p_MkM {g,l|(CHh2moIENDB`gammaray-2.11.3/plugins/guisupport/images/gammaray-inject-16@2x.png000066400000000000000000000022101413310443000251030ustar00rootroot00000000000000PNG  IHDR szzsBIT|d pHYsetEXtSoftwarewww.inkscape.org<IDATXkhUgvmIdZ6)}Z Zw BQTjm~ "~(*ش(jP4hUZCABafٝc&;٤a8s=3g \4- h}VGfP/CozH w2M:}}!b7N YpUC5LDZvl/ T)b⮪shӔ$HZ5%i.}2굜TTO%UgP&sbot֜`=onT"r+x ~nߦƯ>le~vYB-G}"x1eȊk7]7V%}>6\]!E\'عz<ռ0ھ8X)t?h VSk_9gN-^EQl|g-{6ֻɔh $h!T@\+xe"/Bgk[DD_ۭiz4w!2|Jǃ[ؾ^PwDr5>oQ}eSZ:AܫǜETE`et,rkx-;pq,ђ+ l"U!] 1IiSNb?hO 4;DbtG[c۷!tS;pjU==^Cʄ^: ^KW Lh$~ﳺ@3z?S|KIENDB`gammaray-2.11.3/plugins/guisupport/images/gammaray-inject-16@3x.png000066400000000000000000000035431413310443000251160ustar00rootroot00000000000000PNG  IHDR00WsBIT|d pHYs,K,K=tEXtSoftwarewww.inkscape.org<IDAThZklUNl["!*XE 5hDB$F?"MHm!b(1$hMݵl{?;;mו;ws{ι3 MU@D?"vSU@Cz.lh?V@>Z:Tb]41dױHd׃إeۦO62-< ,R;Tż2pVv= Adu|\eP-(42E)Eo _0Ӱ U  <$)2 а3`f"˛Q@]D 6I2cf@Y4)S[˭]&u{T !8PeS:yj8a;sH} կۣ<;%Hh$!8f@>O` zg3pX@ H'SM%'; !fHW$K3ÉmQ-185;Uu"ኲ\̣3~:h6c$Sľ-i?'+H89XH"[IBf7&PpA9*,7rU]Z]ߟl7϶eÂkڥ͉T%˕!g>P*Z]'m՞嘱9qHL'jG/"KWx!aYKdN\Gǰ$҂g̎VF5Ͽ OM}8-. {SV=|Vs/"0<@H@ϟX7:"ieU|gz&\s9 ο1Hdk_ OugAf~ޡ_pWۯAd­֞1׉}-BpdJepl~evӝ7nWܯupRbp;˜pR&{*ɕ/FZz4[b݃q XY[7O91 33,Gozw$2bˢ ?: S5(t&9\քsW+t=g;AvGb"ᩛAHo}ȏ}sfW0 m $ cò(2:()7t6H#aSJẉwA52qC_v=Q!Ycbu٩`@ N m ĮM%#1kRo⢨o=ճ僙LHD/͞ $BCJedp  7NBP"v#??2;lM%BR(J 1Dj"C|(c_+vߩ:?AL:ERpB+FDju-27.T\ K.`ɈIx>n}GRHkY 2F#ٲ`=š;oͫlE(sI3lje}@>5p 4Z*۸%[Y6E 9*28"H@!a˶TT]N ,Iܔ V`^F>Ss՘z%\ 2Y s;xs<&=7 @ٝDgY_hH=y'xc0<IENDB`gammaray-2.11.3/plugins/guisupport/images/gammaray-inject-22.png000066400000000000000000000015231413310443000245340ustar00rootroot00000000000000PNG  IHDRĴl;sBIT|d pHYs+tEXtSoftwarewww.inkscape.org<IDAT8]HQ̘;V F)>Y$e!TX}I, C҃TcYDPԣD %OEQ"nj01;큁{=ws=36;1P) "AAW&~A9"삖:ѝhvZY4&m Jf?IUֲ42X2N4Q694KfǸ S14w=pdEd^'ܰ-T%\r $aAe"d'G2Cvhyқ!}nt۳mWX6V)uErm*3e3vVoNL/ku S%@D14וbt}y_kuT1"a~D0?I!b Ho|ҺMvƇ3x1868/3E IENDB`gammaray-2.11.3/plugins/guisupport/images/gammaray-inject-22@2x.png000066400000000000000000000033011413310443000251020ustar00rootroot00000000000000PNG  IHDR,,ZsBIT|d pHYsetEXtSoftwarewww.inkscape.org<>IDATX͙klULv>(t`}$TPbP, 5$4N5(DAPH "`hi#% A"ϥXac;3 -|d=sνs 6c* "^5ޞV̴"~/Y~zTeB#~OƿIm}i)dՕqѦ]F"@f( }A&B ۪2  ~|D卅ޟ*n @KpD dhf0sʌFf-Z^FK4=\hfbFf$wv?ExҊzd R/)a't>3h[^"GBE>{o`0Dln(eWZNtM|YELK]3PG {S3_:ϣ Ge&䤮moM)c=t4Y&yQNEoh˧&IݳZ%.Y:%d@0+Hj,zbekCH~{;<$"'HϨqtOt(ۿ^5:ai֢,*]:T[C~ 0в5fp&Й&QkhWRtXC6YUżq?UN LE%6)Y젓*=ް$al9r wiͦ7[¢4tBSWwR#lׂX̓+ߨ X>~}\_P}A-m< r%+ze=Hʖ틆^\+}a\4;s( qH뷂G4`HvH' ބ":v_\,aS鲘 4#%,źYhRB,r``NÉ,s-t4BEaʅ ИX/F]m֔8Q k23'}k課E:P%6Ps8u6AX4:OgnKc^؜o{@{WvǏ=Z^57poْoOAj"=$#Mh[`&*^"n ro~IlAA0If0}fmmF9OJi+[p3UgCo2}Kk`kز^sLqC:vndfebl㵁b0]O\۟Y֘_?dԉgoB<8w玝G.5YfzÉM=ѴE=~4LFۂ ˻K?Fփ0fp@X?o4^bs p)@56pbP bgms?z%<XWlnD4H?l ԉIENDB`gammaray-2.11.3/plugins/guisupport/images/gammaray-inject-22@3x.png000066400000000000000000000052041413310443000251070ustar00rootroot00000000000000PNG  IHDRBBTsBIT|d pHYs,K,K=tEXtSoftwarewww.inkscape.org< IDATxkWd( > HwJ4F,$B$Pd{HZP|/ TBC(E °L|Yv6{{}νӄ4D %/V$K?{x}zXD~ !j !/ Ru㊩tTXDU%܁Uc%3W-t(y `QCkX?`(vIL3 {2('`C1M)17! cQ9U>3qjLvJTϱ;(py`b`FHΙj0oJHnX8CC@, ok_p:.8~! '<B6i0bN DTt9n) [sk0H%v0جN01`}ͱeҟaC e%F|-O]`a#febCRJ[-d #&KsC hQ}~^`$؈HUmZuo ۲~#]9k؇F\zn >vfWHH9ތ>MC4& G#Elu CT8P]ՅmEZ*sIQE 3F͚PA,`B m#gnuք>qIO+ ;!voi딱r[ Baj؀?sCǺL- s@o܅1c>8)z] QkU7[e&F& 1-`p>כvEL7~\`)SؙUCmܭ17޿ aoQnmk =^?4@ik<E\7Ǭ.; k+  7q!)T~WjFmC,B&G= K`;?4a!xE/7J5C'5t!>ao0zi]"C_@<З1bpBUO{g.QY[lAݡutEa #1}`tE7~`@df n0JrH Vl>KZޤ?c$> )sP/z0|xl@T  bk{'Z וc!:|28g?/K]؈ Fry K4ۻf1l)^|B#|,vgp-@ @$.qGXkl8׾CȌO7 1QYȳ7oY0CmK` m뛑 )VǪ;3ohkt/Ho-b٢]@D]~ԁEE0@8t<&ԉ^\l(@?.$!$1i8ђwyDrNV%PH&0#T$'TwZRbrHTwXK RV Q:o׌BTVE]_Hkt(CT+9ADQT:R 9A%BR!2rȪ'֬6O߻?,ƶ]܆} Qi rl])f?=Qֈw[Pp4==qύՈ+زcb,4@ݰvPq,{ f Enhr:?*K{`5zQ@nHQ]/MC$]u{h Y10q{jԏ=p^ǴJ|h\Ƿ8W.]癱[3)b:x8yU+֘~ޞONy {x>7V6~)i`_SOs:<ۋ8pw>ަU~66 zϏy_ŁK0sYc璿+5/ky܂9EŜ6ǽl!v 8=ss&!i?.\J,DĪH Lrx0ZAMv7L||l 1D!oV%F $HS% tdi&9'NB1^{/o^ ߹ i>PWh!/rl&CҚ'˃\t2du=/k3a{IENDB`gammaray-2.11.3/plugins/guisupport/images/gammaray-inject-24@3x.png000066400000000000000000000056331413310443000251170ustar00rootroot00000000000000PNG  IHDRHHUGsBIT|d pHYs,K,K=tEXtSoftwarewww.inkscape.org< IDATx{pU{ι7<@xRUJڂ-Sֱu@ۢ2NurJQNڴL;:>FGDby )I !=_8{nr\!odϷ~! WJ@) T`l%);-9u}(jF4VلM+M7ѵ{sj o )ւ{߉TכR[vJ}2޷` +I՛ci8mlJ[vJ},#'Vޔ%D/A )+y3n"zɀA ׼ZDBgsz@4&[31?!( GZWzqɷ_U֚o15;B; 9۫^Aak3؛20 =7h%RE8>Ɲ$ IsDhp8>Ɲb,&V1usw*A46KGg2'Ƹ{S T; @HZ%G{ hO.LN୞=i V`"^iO&olX,'S;\4b^@ ]U6tʓm9$_G( F,"W7޶w!!w!-2Ti*Ňg9kGX]S U'0K"^ʩ$~.>,.VCsgv`֦2.\bhXrJi7P֓szbqHgkU{³cGi 8r5=(p?rWugGP]9$#50oht8@=8zRh,Fݵgf6s!&7|0jv@IRrx̻S^tmu_?{hgQqF}:N5: }w2l.KXvp<6EK4yU̝<֎ԃ?&,w88̽O2!͜4`i@^yѕb+{x=$|5Zs>ɔY6$Ϟ4@TQ@96kfrM+.Y{ CkٴزG:qQSڭc<F? Ǭ5Y~qHd||(k=M)T*[Z쌦RgA) :iV~xG672{ϫӞH iD(ZHQqOاɃi*tswr<??J4[_9iD%1kRw|eUFނ~}Iyvb 㖛>i83ׁYHR1sgO iWmJmє2nV^P^9|y|l8V'b֠@W|u[Fo[P.K̜8.@{gOm{,8:u5`B 5{!3#ܻx: ՝w Gs}Q7^3fΖk=5Ng̰!nZNLFt f + C ϞtC5Jz90ohZ+ya.x24#u5Tppaʁ.1ebnDVg*ƍ!@PC4*~[W%ue~hܳ\$aMUy!.26k}͠-.Jvh275#԰V'OouADD{]׽PD3qGDbj;j_yiVUtޑ-Uz1`{2w1C>jkIyIENDB`gammaray-2.11.3/plugins/guisupport/images/gammaray-inject-32.png000066400000000000000000000022661413310443000245420ustar00rootroot00000000000000PNG  IHDR szzsBIT|d pHYs+tEXtSoftwarewww.inkscape.org<3IDATXŗ]lUn?̶ݖ-lJS)R@1DK5 jJLC5}јh|`C֏4F> $OBbX0RD,Bl-2vnwguaۙݝR'dΜ۹3!ɸr1菁qh\I7W ː(BrNcd *Y,s1!iǜWD= IsU>A&gVo2U 9<~Sfp!Bw/:%^:)®Cp"y{N 2'Y$D >gr~j"@ήc:q#KVH)!%$+1;D[))Xi @A @2\975 I$ M9:ŋh'’wx\3W{#)^ P,~>7TL6Z︽/fhehj(y(}G{AX,'m6C/%[uhͱUHO~ro]ڔ xԯ#'HJp$%[_8qgsLgccU]L#[*γgS )9" OUo#Aۅ>w< "sU*])Va?0EG n;n2+9y2Lȗ:/VEWe4lڰg}^k/uvd ;7#mܩFb| a=^DŽh%`2c99 }.4%d۹7+['DsbOt;^^=pFܿ3|D$j*XGcBxX?~koؙ7ez#?h'Fm7l=]E!0΢G0lƴ%3=|{dLr{SmZO381rf/܏P0wQ.R Jmi'c*#VT1!\ypzloaD,-Q'h{?sPCD*#Z=u#&yN@ aM!1o YC{?u,PuQ|bn1 PODT@V+?^ pjTƔjRoLR@BIj@2RIѢō0>ǜ'V ę$  (՜gwf"@nΰ3!8oaq"@`^w09!p@DA5;U|Bw@HM2@n 6!+3H"ULv\K`I >N" I(1 BvP1 %"TL4\I!MTECw4՝B 6IzгCmt(| D 'CBD6h̲Ggw⯑3~#_ $DR@"@,F^$ZIxWeREQ#ϵSz}Z&HD16c ClR2>oӲ\_T1xQm;`j p;0󶁜݁NŶ橙>}s7Ξw7g+^9/˖ay`}_ѣ|I A[HS,=,pqA 602!|{MYM_ӟk$i`=)v>"E@ B_{M}j4tGu3bMkҾnC ,W+^O"k&A¦Kis+Xrkk{6v8xCxh`wQ\GC5hlԥ+G[;"FA/3}-O8 `~62;rnUI*Y٭LIr&2f].Jn'/]6dX ¦y ~S}^W4}ۯM5+[:Ub7 U>vE eCD-!&HjAxDӸ<('f:ҬU'ҟZ!LuTYyW 3-~AL ՂH.6ӷ Qvڝ&; lVv=n_ T ohn6h[݁vq^#Ͷ)O~W34m\EiMϽahK*'yxǽCCC(ލmkV@S"P4Ͽ5g˲%?̹>3vN.VZ-FpqpaStb@_fHo䴹GvjQYiM).Yg|wk]ޗt9 ҨDbzJ`c` N66MO3i8TKcq mJbϤ'4c{2mjKJ?Vݻ]ݻ$콿}眽"HRu(i !$yl_܎ҥYdڰ4N`"1O|zb(lu p{2RS6 B"ĈJ?$ۼqh!CU] }Fo400Zۨxw`8n3mڳ!_<kX&Fm_L¤2)/scӚǒQA1 !!/d` `4^J.rF"Sa(@ G!0&7ȦUg&^8Hc a $E62f> LPa'ƺ@Ӛ &_ٴʘ̂Pa}ɴ&N#&lZe|@GJMNTLuM &_Wj5 bu"3Bu;JM$t%_ns#R5YҚ8'm4!4Tj?fÉR hx~xDN "KrUuz$5 tozc 0b:(a<0?Z!xԔxq~@y6JvS.pӮm  A|3 lw/ x5;֧),_ UUG.[t1%뀰`̶5 #srA~M%?ҩVShb~pe_`'dun۫|!&a4\Gn/3>&yN>ZT .)lU<WfY↷1 drr707.XuS-/SbnpqWďZאb?ѭW{GpB7a+X7amM\k $'35֖" 5Ot^V|-MKe(ހd<b 8?:>Q?u8F$ښP߬_ky$p_iV [{BDS!LښPWM5I>%[-<ݫz&]'fPcmM\!מGWT;6kf-F$&PFҳ6jLJ {ar%BOɄ3YIЙ_.MVJF:lZ(K/Oq*hh'2U#ɓJ3\H+ [OHٻ _]#\YQ_J/!`&jS4MFu]G+_oq羒7>t\j9_>$wM|B~Rpo\Uv,/]g.KyG9dWXl"uDz`p@ԤxaR>!4<#(yS ^<f֥3q>P8%R%=)&Bլ4;Y C}ԥ@AǥdHuh{q){ioM>ZU% `ɱ'#oon8ѪXEzlX v-| Ks y ` _TȵkRa,`bͳ_pڮ<Y#m,;x P=)C tK!k'ݕflXV_N_nhj֊C2"~1h-{~pNfBu˳*(11#se4@+$O ]:hdblyEB[< U)pKL!Wm(s<۶EB, Uƃݿ gmGM jD!ʿ+Kok?,X#Yh`dblk aYM_ >ԟu}OHsioD?i;e(KP&nm[ƽOUC\>._x`G0;ugB* `2 z@jYxXZ,ST@U+nJ@Thꇪ/9!|cUUNqm=cof%N\XeF?{S=6 v@|pyJT3!|/=.pt6P/LFP*h@F|ܨ_F k[8O@|F _#_Ek3E uB 28jVޖ½<~+*p9J j>INzP,;Njid#& ԙ 5yz:Ǹoiw(lX;8 }Glu4L`ܞ;d2/Ų4^dV 7de e56_;yM鄁4#RϬ/+/?mi,b##Fo¤ǐԗg[8URSOܲ?Zi1>cƓwlELs7ϯmQW%__-j#J;?9Ì}? !|>t10[ eFLcw/ ;N}Uc;Soȵ|,'pcb{E95k"xuѾb<n~ʭU^b}}7;ۈ'ו/|OF8 Z}dWqa)\kl; Z𿎸Jڲ<;Վ-k ?~ܯz8MCm _| h̀wgPv/_Y[l_H6u@g痿zcLJ `ᑻoH ~>$[;EoOftWĉ qBeEI걿~# 42J|%aCepϲ[Z/5M:xyT>+7YO-9[Tau=^UF~ݡ,6Q*5?sǤ |mI!p(V#{|ޯfS͕Gvktn<(XF+5L>h(c @~-C?",! r]qAX)uwJ @R}jKRceu 4x~NX g!Ј)Ϯ6| wGWFb~mQ :oJEW[6 Ddgu^YJ-YpyenDfEtOoWB|]oi`J,<,X..1& @0Bopv9=/IENDB`gammaray-2.11.3/plugins/guisupport/images/gammaray-inject-48.png000066400000000000000000000045311413310443000245460ustar00rootroot00000000000000PNG  IHDR00WbKGD pHYsetIME  7rNIDATh՚{pǿ> B`)V8XV`S Nձ0JUq`vNZ>Ʃu3֊U;hi ASRbm{9cߛM6If2{Ϲ{ܻPv@j+D "ORȔg(,=VWћӯ{Q Xa@aY XFX!!ւ%e-L|V[1q0e1lAba5Ha` H7`# sTO_AYpRmUVE)( %>ۍŽmd|xN(k<Hꭰ3\j鎋$ 8UΙ!9ƹ8Ŵ  W5 A]Ml6T3Һ$DcK@D5a|>a)z\@'(s A9I @t<pdh&@HHYq@&71V"@\+@xY0JÇ(@L@AXLW-13"l!қp]O GDN*w nݤ".PD " .@!ֆ}hF >;LNuY߽xϷw²}i7LnT@4Tn3%À?m}w>=4ƹY-{*rou]nBGDf9X"%[xⶅՏH\YB:{/u^-a h s|3Nm*ߗ$ҝR(|:}v~i_{!c !|꣓=q;>W'&3SE8*B`pCrK!cC]hө>޸cNvhBftRNf`IsQKM C7-.Is=D6_e ^&Ot&~`Btz/ت?|%T2n~hӧ\# .h!|"@Âޑ+:\8~.=ӳ+2h7!ꁷ{<&Mu+CB(Kʏrʹ=WwZ{\5G ЖN![СA?C9gJ e C$c }mݼr)ly]e`eS;Ն4I'V9{, i5!Ax~7Q+{u)!:vݧ:%Fr9Ϧ,1\. !tKXɿq|kıuKf>VuԻ-轻U>q>s\*7"M]87+܄LNX)Ձsˍ˧4d˛Qsi,# մ[lKNWj\lrWh4VWޭ6tN~m^>pUXDRMyEe"|~ЮGtKEdU 裎/;,)9BJ5={g D#/ݖ;@!b=2!a""s-#d l`A6 baRÀ1xQy=xzضuU;ku:jg q+B3)]mɋ+ )yek.a_QWsYLH L$䞁2.|ZC=? >% IENDB`gammaray-2.11.3/plugins/guisupport/images/gammaray-inject-48@2x.png000066400000000000000000000075451413310443000251300ustar00rootroot00000000000000PNG  IHDR``w8sBIT|d pHYsetEXtSoftwarewww.inkscape.org<IDATxypםǿ-Y d@H MYfv+ޔlwح[[)VJCy.zכVʅS,!8LF1 dsl.Ё-ݿcng{i;^>MàA6fc͌1 > m'KmM-i mѩV+CPC j K*L_mǚH`&cDɀF1ԙhw:nS6[wI^)6zJ X{/lpR횼a+H&sݘ&JqA O 4$1YĻUusDzT$?"fm25!Dhx 9_5yd-ɏY!OjAb2kȞ]fɏY)@ODh 1QU7k4Y!@G 0Uu6OJxIBHD8!GA @2WR'y@\:!Y?[!Lko6Tu!=! :!Y%E!)d `^(PWt=!$(i6TiB64z]g $ @HQ2JU$_uBr,11g $y@23R_N&~B Jdg hTU u^!p ܏&##jp\bW8#shd䶁 9Bmh5S8 #4@kIL^ ~^iI QX1.>4:3<@P6IPᓑ)r-i";z9z!Ll.'╣<5I$>@9/ϲC˥T)O69'Zܮ,QP\h_ rlbxߡ[l-/|[^5(E7fdAmNQ 9d[0zuC9`33 9&m 7uřLs?DƩQI.3{d]]װ.]l0.М~M^f^V[?4[hFhXl9wLf8#C#@8Ԫ=-;jxI~D"9o Wr+˸eyl8a9pĥ24֯GtHKFc3%&BCMA֨P3*((`g|Z 0V_`u},ecڽ#Cz]P"" + >W}8l 9eKk'v Rݰj@>H} x h%iA9a"MK:nM Կ}< 6Vmg.S1)Ə3Jå¼ЛuT/-k]*^xMVmw8 Mfmͯ;҃' Ty%3؉lK;(E)V=wd3-RKs`*~GzOP*G'S7\y奺gx Gw]Bܒ=_|xw.dd|fY {:.79EU 2AsW.g13 Wߝ>Y ;@c==!oVIpuh2M'ª UQ[1rig<%6b=ݴI 2?]'n>͂wɵsZxD$/ w`6؝]kG{K7w;}PhӲбG> b=ĭx7,Ge @x%eOxprqM&l=;;0v-5 c:=jPAV<j3QB#\ cm~ж{ZgaҳϮQ3" ?/޻9ЯY.}cMJ=X5y-zn Ggow.(?# c'Y_nR(ѩ"!,S3?\v^d~bY[ӻ G/x/UzC=A /3XwzX(BϗEŻr7+bYY+-Wi(O3xЕj yz9pY?VH>Xaܪ=ݍj,R-#^\WaQw)^ k$xZ[>u['2m'QL>ns߽oVZkԺSˮ_]E1gI.vSe8+D$2pzA uvڋLzzkGCۻ=вU˒(?~_QO{.f>coo0`2ZX_Zl[aXm` bQ YޯFw|E< cp̺GM>6xmML'7Zv֗[Vm`Iwt6ȖK>^0 0 g<BxO>?1بCIENDB`gammaray-2.11.3/plugins/guisupport/images/gammaray-inject-48@3x.png000066400000000000000000000146321413310443000251240ustar00rootroot00000000000000PNG  IHDRFsBIT|d pHYs,K,K=tEXtSoftwarewww.inkscape.org<IDATxypםǿuL4@ !@F61`^G6؉;n6J[)R^6ˮ^8Y[Ɔ6a@B7H1Wg{GHE1~_>A`|zo V ʂuMy߯6QU%*b~|qe\d?D畤-;"f9׌*6ƾ3b?>X#=lt{`L]Se/TUevmYcf*ُ/Gyeiˎ٠ΧwؘI |-Fo[ `@|{J>C:镶swTȊ/h Χv0 l<gLg161IzGh0@բEy*#ɂ$ ]-@y ,LhLH7@ U:ɂ)Ot0m1J'& 1+DF?n[OAxb 2Bd4D޶"myӕH[DI Ɏ޶ OLQzSDI2& ~#,xb 22Q ~)<1YT|e(Hbݓ[X2 OLD&U*4 R`{r{rzUI'3eAB2D,5O'V'}^2$4X~,RCPC5OnJ`IifLY :*'Q4 R'"{ei@TPzgd!$:Qu7jW+FY AݳvsAƥ#7K̕{,M+?Oc ȸ,Ld} ,_ `΂(!NeaAUjUJ ł("3ق(7sW+D[Qnh!" 2_RYCs~# T2 2bꃈ&V3 &>DYA"oa: 2b@~TS^J*Kt(1P,2QC `nB.W(DM,22 ł( f*#P  "+B=gTF+W!d%Lcט 2bľ[/׉TFΡXe(TFΡXe15!JNeDYU,?S"TseAdH{XI 2jj@Ne$=ȂȨU5 % "6D.0X[@A~ S5 - "#phs-̄VS / E*椉 }U.~! !ENUUDzz_'3If>QV} {zg/.tT' 6BjNc']Wwl@dXdDhUcO{N]f@. (lIˎ2]Ube1?5ZV-h0(C;~ᄓ r;9zﲲɳ'պ8t}&M`c1pM&DVW^,pS?<$ )<J&(`y `#}6tO/Cۚz~ *o}ڿJn4efvռ}BjfU<i>|bA4X/K^!#BT?NfDKOi=BVs!u]-}A.8K7@4=^ U:Kܔ2HhSѵ}?L)i/RdAdX#.q3eJDޮpO鎹t/_:/Ǝ>1(.ȠsZ!e?~?`-&uo4ϙDΪ]o=6"V |,tugvr'4'GφܶcKک[< eDi=DYi:Wn TsI-}ݳ^"Txf{IQSM>%Di=D*>l m;OK:#Muhj Y5tǧ|FvMiPfAc!N!ޮޮ0f{a|i1yΚ雧ķ{}<$-5"ÂHAxVIg>e#(♝GeT||<5/";{>M[(y Xh2D-.֣;ҕ+piL'\z|]/}JXP F'+grmw  PNo!=e*frG.19z`gz Ȉ]P kPۺzub+Hc߂HӛY}ͬPx*W GoK-Ŏ̍SFm)z_ŶYCfɷ0 "s DSW]᜽`pFq>C2sbid1}#ˆsw?8-{J)[Ig߂H[]=k().sfa_}wFU34 ʙd(9֘r\l%} "Moi l>EhxP`yPFY{䵯-3HIɍuduSIǎCRSҌy,ξ7 8Zݓmz\'[ˋp7{^wzP J:cU6fU  ]瓔)f+pfK~"P &ZV?z=MJ ?!;)o>ѲgB,b|"QR. 5OU.H3h "=7B*wyzKGx*È,{|xI>'{K|tu*#26Ĭ9al1U9/pL<6׻v!]YJdৄINoQ"H~v.;xRI1iF9i5{GI A5YW,{,'J!ع`@nvr`@ 1/uvr^sJ1 , Ϋ` :;tp~0ްs(0,İ#('.EAEqe8Cais] KwSD޺FdA{CG{䩡,dIq$80AY)8d%,aRNYa3p\Fg=j$+y^pԷ0͡aDe9xO.$3"(6^$@ Ud*Yieٟʝ\&,w=9tdOމ8oXsMS=R_;~渵yim~u~&vyn=qô2Y.mINO0Jg*SN{G|i'+m"|Ub.!ݷ%CSo%On"JeQ oARz<@b+dAILpwy'ԫ-3P}Ƣx ߜ*m+*)a(Of%'*CizDSESCOz)p_fg~3`n}앳VpWJ%{(bfJx`&e[H^7^aM^A`Le-j{D]6!V64,qU2lj;B fq6QSVPWyuSG^2x Edw炶lۘ<,̖_ UIENDB`gammaray-2.11.3/plugins/guisupport/images/gammaray-inject-64.png000066400000000000000000000041471413310443000245470ustar00rootroot00000000000000PNG  IHDR@@iqsBIT|d pHYs+tEXtSoftwarewww.inkscape.org<IDATx_$Gǿ&3]. $(f! q "EASap^ )"I'!>K"c@[ntWׇ?7̦`ٮW]%}% @D@´(^N"$f4 }K"MtI}V埻RyQ򄢬nPb)T/]&?dBD*})U"WoGB_c}~#@4w BcI' hw tP35a/0@4DPb 4/" Bhhin=C~!DKb(n(ڳ#=QXK2NU 9P8 4E) ]! 3Z#C̍")f3wEd2fa*_o-e(o֒C^.@ii ;_EO&S?A(.7M JP# J {+2k Nc^CbE3$Kؔ O#!`W%ڭRwH%7 vh5>@d>е_%s?9γJvk3}x's^EXPU"{={E29s;e jDBQ߆˫yG}İA(~(ܵK{+0Lە3[_؎ m~$IxK[ޱ% @_0vCp?֚W$6֣/Cu@(Y |߫밸vsZۺ(wd!Ǿ NFw$vƘ}lmJ}[;ڜ)Wl`>z JseT]j+Km@Di%$NKpl@ TF r<VjҺ b7ތ"ci ^ɓXuZӽz^/۱=UIscg`ekg2@RmNķ!`'>"G 0UUHRnk [l !>bK7ޛ4tt\Žv C @H :qkMgw?ruy"4zŧ^e0$N|)T `&sf>{1V%/B=]( rjcGȗUSAMŝX"dG)L? ԭ z\Q?n+/VH,^qD6꣢hxh_='v8[ SPBcFUh1(<]*}"4x=׀z ;Cq@ cD`b6!$CndеbBgy8ysR+J:Ϥ$_IENDB`gammaray-2.11.3/plugins/guisupport/images/gammaray-inject-64@2x.png000066400000000000000000000113011413310443000251070ustar00rootroot00000000000000PNG  IHDR>asBIT|d pHYsetEXtSoftwarewww.inkscape.org<>IDATxklu.\R\(YUvl8jQ] A7 I&F4PE7pk}6EI(@HdņcKH֥-DI%;{a;;;]Cvw=ܹB}|ByJmy)X*OkRH?=>Ab}vgٱ*g}@N=nq>7"8K f )EB!#/ ZgU]9V ,`pOz DQ3T=50b˔L hvؕ| Jn_%(ڭO`Y9͢&k;Ԝj('[(&?jA2"Eh;G(qBVO9 ;$Z=2- 0E@/6=3ff^ [5;J`r~Y5?i71Gx,cNժ)%,mSdn<vx"!4~V7狢 `^xÍg u }U(Íg/sMCʼ9_-ۤ6CR:>ύY*#jNleA ɁN 3l|Cj~s([,^ QIub^sS){Cސa=ր N'?0r ?QS[.Z,t0;,X$(kmećU )IA@^z@A0ER':&@Aԍ~CEV8>@Q)`  (q|&#׃ Q#IB+qb2Q pD& 1 .6'B p p۞HL o$Vw?@,5;YC+;v`}/A~R 'z!RfZǤ7 +e=(!ਤAA*; _ȤМRtkcBOBI+7i!Z wf0s&p`R,A6IEKAЭKO -Z nm X)4Z_XD$[*6zB3LU4yH3 *uӋ["8H M M!62w3::W yL 46XyCqR5u%2s c-( pI@9 Foh wDh ME"=UR IHÁI}~?)(ɍ! <&" H <&" A;C"\ IHCГz*@A$[b J ̅ A[^AlR FP ܣ=cP$@IPf P$愠')4r,XAP gǥ)jA`w H\tPKUǬEX09J;evm]M=%YE%ךhk@ir?s `|b 4{+{@sI2x,_Fw:dIa9 )A(Rwh$>"pH&4TKeTko0:δVO/][ze[w.P ^`ԗAK\aEnZ3>~]?A+Ss `JB ޵k~"$hciVV~H0m;*+ʺsH{ Ď9Qk?"Jzӂ Zsڟ}3>VQ$Ti?Q浥q/ 3JR gA"6);Ŗ0qw7_ԨP%y\'^½箷ٶ|fqUS- ! QJDn$w~犎Kx=^mF.idkڱ>&5/]\z _'p ! ~׈1g֖(*mٽ(\c~Ab?mYW0Y" M ܝx++NثM4k)Rν\DƵ Ip p5xrAt_%嬃Dk+fBUPi82N2oѰ5$b/cϑL }h {ۿrHfip UV92zXcg/7' d._>Rh)\@ ,J@Fs_$b/.]})@$Y&NA+#@T)J Rvye~D4_X^{}+yP)[,e 3pMKQJON? `ja6}X l.=]J9<Rk}b*H<92B=Ej%/ 2-ɉ֣0Z{ HP"HA*rme\GJZɮhm\vD1,"61ɰ|ܟEp`9d̑UXӎZ"h(i0&Zs&,[ڶi$5f6i:FDZ۔%lvմl}%ǯ-J[L;t_xq#MDLΣ"s NFH} pBppoݩ~XlYnqyKn~+F֓zk]&&{18b}AfEAE3 4f[<&\VN o0;;[`yg~$~q!#l, zo=>vs]w;34׿V39|B0|-N9&q~dqp"kZ{kmŰ!!5zQt%|n혩/Z'[cݖ CulCJAJvumLQ^y}]{ޚvJIf4P IA\/!fTM2ڼy b~1Ȍ*6O2pȽ#~Dg:8ժDfiM,ZEɃGh3"R[I ?xWN$}rԍApj7X7ߺҐ?ngȨqC΂p7>JRQ!Mu&I_>>Je[pIO1*uK7N2|pT񁞤Є;AA){|wA:'fݓz~[6;r~hOtBS +дI[Ғ=](͗uJ| &).GvR7`r>))@  _\0Sn=Ґ ?N9u8)*`!Aiblk<1K(d+zl\4uv@S@`ޔ`s%Uv@3в!-[ɊDu{ d}4>|vp hh{uwα)pUZWa 2>38}[ƍ*0o~DO@?=ocOYZI n:>P>@X vE#"=}dLg̛fJWYRۖ cg7>NRi[vƺ@+CbҸU3Z"hW[ZDF[wbq/`>tey]',iy񹶵P06$&.gQ8=T t^{NXD.?g`K&mF e+.M`"w慫N5̢ܟzGrEAg˝ @YOxYt>Y`|: w $4ԑgKזA,L vىW0_|ٮfy(0#ks~U1l9IENDB`gammaray-2.11.3/plugins/guisupport/images/gammaray-inject-64@3x.png000066400000000000000000000165301413310443000251210ustar00rootroot00000000000000PNG  IHDRRlsBIT|d pHYs,K,K=tEXtSoftwarewww.inkscape.org<IDATxy$W}ǿW==bclHD|(1WZC m8,x#B2dH^GQ$D!]|bI;xxfgGwuWUWu]=>gի߷{";iHq%[ 8 rnfa qv/{"wio{r%<>̞ș}|e4''kD_"HEvoE SJH GaO1 ҹv6bB`uMp~`4bVfc3F$IZx.6ҍw=o.u=KNs-cl4eϕhn;?At?BJa#k LCWD c>}ڊJP"pnFDpJP"pnFc,o`}F$P"s3 ,m=:g˰7goD֚.J;x u3P"Lb׽vD `m=ߎ.3H-D0"0;vt v~u[d[Q"!E M+nA(JLJX"*[,QLf%pO!V[?M-UJQ"@_n-'`;}u:A`%fRqwvkgFB 0J%hl wSƜB 0J[:-QP""hd[bL2JY"!d9) g8A`֪::ʸtO1?>$oD5/ vVy4?6~rLD5,&i e3ТD5*V?@ l>EF +O{<4XsDxMy4D bQ05Zc;p\T'JXk"νMֲ` n8BkUk5X3"`:A1P"o%>p {Y uEJLzE!B4&T"g),JGL%H$*J>@GMX ITR`<:%Pl[䅞)%P4A|($cx%&Qɜaұ>@:A|dJCQLlc)DE  D$&D$ OD x(7Bꐈ@~G<GTA|(d> A|((Io<Dq zY "- wDL' )DA7o09( JMDЛnD$ JO$(7A7|B I:u=RSA|((tUH@ ((p?}B"du J1:7OV"[o v 1K4kA}(}B`0eX!~ %x&QY"hJLE ״(q9 & De-A<%)}A<^ ~ A(vg&L:@FQ"gU/|;Bpꣁ¡D$*^lP \BZC!!uVRgu@?2<De ɾ0sVI IT2'8H ΤD$*Afy4w] t&%&QY "h:t&%&Q] QVH37%&QT}w^ӣ7WHꡂl:A|( a w ˦3.U6 %n)2LwD.1=:CA|( .]GR"oa~"(:A|( ɞ!(70 A|(dUfmΐAD!"pV]ӣAvI I&knP x8dבV"FE@L )!zEkM2]GR"H,OJ~)l{"`Z"WR ٭>U"DvH/D^R JxHּ[R JkAvtC/DЉG< DbP"_ٶ¯DbP">gבD%uQq2G`Y"Z:`[8K, gZ05 ^Xn4a&z+D6j}`nh@JZ '鍛B\b)\xy.͗Gꤊ&"In͇4B%n)`rlg<^nCt 7Dg`yy|x`P'h `툸VAC@E0= \؀(OUDtH⿴;epMbvId㥛,"Y,W~xV̉[.Awѻ tin߈%CB KG>T]v6m]麜H{7[.h`t0ߍ_!!\g;4p1AoFL?DbHVȺoD&/[8}&J~)e_~60w lJP"H&=L I+[99I0\~>JD6E<~5 vilT+}pkfXc__l+FDgͺRꅭևJ~)2^JT(=v=={ |%gdnT 9Yz̹3g)XoW6M`$c!n[,_X֓j5D08IZqND'ҾtjyeN2fƹs?2+k+f.iGgMD P3 [?7C"]dR>"-Bcbqag;Z s x. ( ,@-ːjT^87;ΝsˤhP%UMh}y2cuը/I|2e{D0&6BT*=|tZ&5Ο=F- J!K"'6mdN!M豳?ܗdfJnWa{?T" YӈI|:Փm·NK/UYXxduè~&jlԬ]p( Aeީ>wzvYN2Ghgp=7Ja"׼KK'N/(MHsr'=S7x|~>"n, dbND0z*~ Z6QA:* =fs$A6Is)^ 9`=t"^iͺU:53 VD I+|Iƿ9}w.ԈJ!iMDEJ?=N2YV?\\`@aJ!IhX/i8wW$K{a%hdbZ˄,B3h\mTWB̙z.ׯLgH%W\Z1 $s7pȬ˿awvn&0(,J!R!>B1*=y4:zdVo1ҬW87; %T۴K$909B)O(őG~?&7k.CԵ`.#:P sF(訕bA$ `ffbiպ/֤X2P1'*x&z[ۤJ!h.OB ł&ѼD0L2/)% `ĊaV$rF]jKrBs5'k&cj"j3zD'87 PvSܯO`0A}±jMTk2bʑEY˼arި3VXQf7=D~\3+d%&S-̿,oB@ay+퇨O uh0Iu~I檦m-\Aa<իsxyAڸK67q[Z#Mox4 lhW4Y هJi(Y>w}/-]y/D`<[>zsxy%@f]d`qof5Ƌ36 h^,`I=y+ XP DP 1n<8Q_g7psn+Om5WVn//Bcճ o׸繒!7kvhREPx(xˮɛ0̆0+kdD+l㝍<Eٸ؆^.,e/җQ.7D10m^8 1aת=E $/l|Öұw!c('}`|S{o߹>@XY(yHP/.]yc/U&Lnݔ /%="(?~x(AJY,0Fa L0~K*aX>7"Ydba/Ɇ&%tE kGAQl]FTQ T\֬n<-]>Ӣ(d-E%`2U_{IM$JJwVf.\DׁA2DY7"5-^ 'T*3*,z.M Z{u:?^#Y+9n>U)f*U4*X~?ʼnX6kOS-xCJɘHKJh"B}dz_$5ex3|K2ЉAd? $K W^t(i  5}f S"LQK{$MŐ9m]Q rv٫L@'8h;Ad(so{LCgkMf .cg땪p4u2s{?3d`% JzމW/b\or~#!"gGv&u֠%cˮp/M  5f[g=pn8Ad)Ѯirb% ZWV:r1*$@"T6lic+i!4j\ޭ}F`{$vBXxYO]nq>!V: )@݁~\l'M %vW46 {d* ٗGrs/5&k©]]β򴍘Kj =$@o4hrm;58{ړ@6ܩdK>wܙt7z5SŜ*0=ڙE/'p?[b9ĀݩlK>wGTݠeq`#ke;DTp`64j,qž';q]%J=P{A7lhH l>u-h#)yZVs I854!dqOw6O_/63} o|M O}OSq~ǕnIENDB`gammaray-2.11.3/plugins/guisupport/paintanalyzertab.cpp000066400000000000000000000027101413310443000233430ustar00rootroot00000000000000/* paintanalyzertab.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "paintanalyzertab.h" #include #include using namespace GammaRay; PaintAnalyzerTab::PaintAnalyzerTab(PropertyWidget *parent) : PaintAnalyzerWidget(parent) { setBaseName(parent->objectBaseName() + ".painting.analyzer"); } PaintAnalyzerTab::~PaintAnalyzerTab() = default; gammaray-2.11.3/plugins/guisupport/paintanalyzertab.h000066400000000000000000000027401413310443000230130ustar00rootroot00000000000000/* paintanalyzertab.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_PAINTANALYZERTAB_H #define GAMMARAY_PAINTANALYZERTAB_H #include namespace GammaRay { class PropertyWidget; class PaintAnalyzerTab : public PaintAnalyzerWidget { Q_OBJECT public: explicit PaintAnalyzerTab(PropertyWidget *parent); ~PaintAnalyzerTab() override; }; } #endif // GAMMARAY_PAINTANALYZERTAB_H gammaray-2.11.3/plugins/guisupport/ui.qrc000066400000000000000000000022351413310443000204150ustar00rootroot00000000000000 images/gammaray-inject-16.png images/gammaray-inject-22.png images/gammaray-inject-24.png images/gammaray-inject-32.png images/gammaray-inject-48.png images/gammaray-inject-64.png images/gammaray-inject-16@2x.png images/gammaray-inject-16@3x.png images/gammaray-inject-22@2x.png images/gammaray-inject-22@3x.png images/gammaray-inject-24@2x.png images/gammaray-inject-24@3x.png images/gammaray-inject-32@2x.png images/gammaray-inject-32@3x.png images/gammaray-inject-48@2x.png images/gammaray-inject-48@3x.png images/gammaray-inject-64@2x.png images/gammaray-inject-64@3x.png images/gammaray-inject-128.png images/gammaray-inject-128@2x.png images/gammaray-inject-128@3x.png gammaray-2.11.3/plugins/kjobtracker/000077500000000000000000000000001413310443000173475ustar00rootroot00000000000000gammaray-2.11.3/plugins/kjobtracker/CMakeLists.txt000066400000000000000000000017001413310443000221050ustar00rootroot00000000000000find_package(KF5CoreAddons NO_MODULE QUIET) set_package_properties(KF5CoreAddons PROPERTIES TYPE OPTIONAL DESCRIPTION "KDE KCoreAddons Framework" URL "https://www.kde.org/" PURPOSE "Required for the KJob tracker plug-in." ) # probe part if (NOT GAMMARAY_CLIENT_ONLY_BUILD) if(KF5CoreAddons_FOUND) set(gammaray_kjob_plugin_srcs kjobtracker.cpp kjobmodel.cpp ) gammaray_add_plugin(gammaray_kjobtracker_plugin JSON gammaray_kjobtracker.json SOURCES ${gammaray_kjob_plugin_srcs} ) target_link_libraries(gammaray_kjobtracker_plugin gammaray_core KF5::CoreAddons Qt5::Gui ) endif() endif() # ui part if(GAMMARAY_BUILD_UI) set(gammaray_kjob_ui_plugin_srcs kjobtrackerwidget.cpp ) gammaray_add_plugin(gammaray_kjobtracker_ui_plugin JSON gammaray_kjobtracker.json SOURCES ${gammaray_kjob_ui_plugin_srcs} ) target_link_libraries(gammaray_kjobtracker_ui_plugin gammaray_ui ) endif() gammaray-2.11.3/plugins/kjobtracker/gammaray_kjobtracker.json000066400000000000000000000001231413310443000244150ustar00rootroot00000000000000{ "id": "gammaray_kjobtracker", "name": "KJobs", "types": [ "KJob" ] } gammaray-2.11.3/plugins/kjobtracker/kjobmodel.cpp000066400000000000000000000132411413310443000220220ustar00rootroot00000000000000/* kjobmodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2012-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "kjobmodel.h" #include #include #include #include using namespace GammaRay; /* * TODO * - show job hierarchy * - show all job info messages * - show progress information * - allow to cancel/suspend if job supports that * - track runtime * - allow to clear the model * - tooltips with additional information (capabilities etc) */ KJobModel::KJobModel(QObject *parent) : QAbstractTableModel(parent) { } QVariant KJobModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); const KJobInfo &job = m_data.at(index.row()); if (role == Qt::DisplayRole) { switch (index.column()) { case 0: return job.name; case 1: return job.type; case 2: return job.statusText; } } else if (role == Qt::ForegroundRole) { switch (job.state) { case KJobInfo::Finished: case KJobInfo::Deleted: return qApp->palette().brush(QPalette::Disabled, QPalette::Foreground); case KJobInfo::Error: return QVariant::fromValue(Qt::red); case KJobInfo::Killed: return qApp->palette().link(); default: return QVariant(); } } return QVariant(); } int KJobModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); return 3; } int KJobModel::rowCount(const QModelIndex &parent) const { if (parent.isValid()) return 0; return m_data.size(); } QVariant KJobModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { switch (section) { case 0: return tr("Job"); case 1: return tr("Type"); case 2: return tr("Status"); } } return QAbstractItemModel::headerData(section, orientation, role); } void KJobModel::objectAdded(QObject *obj) { KJob *job = qobject_cast(obj); if (!job) return; beginInsertRows(QModelIndex(), rowCount(), rowCount()); KJobInfo jobInfo; jobInfo.job = job; connect(job, &KJob::result, this, &KJobModel::jobResult); connect(job, &KJob::finished, this, &KJobModel::jobFinished); connect(job, &KJob::infoMessage, this, &KJobModel::jobInfo); jobInfo.name = obj->objectName().isEmpty() ? Util::addressToString(obj) : obj->objectName(); jobInfo.type = obj->metaObject()->className(); jobInfo.state = KJobInfo::Running; m_data.push_back(jobInfo); endInsertRows(); } void KJobModel::objectRemoved(QObject *obj) { const int pos = indexOfJob(obj); if (pos < 0) return; // KJob dtor emits finished, so this shouldn't happen, in theory // We however seem to get here for very short-lived jobs that emit before objectAdded() // is called (while we wait for the vtable to be complete), so we only see the result // of their deleteLater(). if (m_data.at(pos).state == KJobInfo::Running) { m_data[pos].state = KJobInfo::Deleted; m_data[pos].statusText = tr("Deleted"); emit dataChanged(index(pos, 0), index(pos, columnCount() - 1)); } } void KJobModel::jobResult(KJob *job) { const int pos = indexOfJob(job); if (pos < 0) return; if (job->error()) { m_data[pos].state = KJobInfo::Error; m_data[pos].statusText = job->errorString(); } else { if (m_data.at(pos).state == KJobInfo::Killed) { // we can get finished() before result(), which is perfectly fine m_data[pos].statusText.clear(); } m_data[pos].state = KJobInfo::Finished; } emit dataChanged(index(pos, 0), index(pos, columnCount()-1)); } void KJobModel::jobFinished(KJob *obj) { const int pos = indexOfJob(obj); if (pos < 0) return; if (m_data.at(pos).state == KJobInfo::Running) { m_data[pos].state = KJobInfo::Killed; m_data[pos].statusText = tr("Killed"); } emit dataChanged(index(pos, 0), index(pos, columnCount()-1)); } void KJobModel::jobInfo(KJob *job, const QString &plainMessage) { const int pos = indexOfJob(job); if (pos < 0) return; if (m_data.at(pos).state == KJobInfo::Running) m_data[pos].statusText = plainMessage; emit dataChanged(index(pos, 0), index(pos, columnCount()-1)); } int KJobModel::indexOfJob(QObject *obj) const { for (int i = 0; i < m_data.size(); ++i) { if (m_data.at(i).job == obj) return i; } return -1; } gammaray-2.11.3/plugins/kjobtracker/kjobmodel.h000066400000000000000000000045411413310443000214720ustar00rootroot00000000000000/* kjobmodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2012-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_KJOBTRACKER_KJOBMODEL_H #define GAMMARAY_KJOBTRACKER_KJOBMODEL_H #include #include class KJob; namespace GammaRay { class KJobModel : public QAbstractTableModel { Q_OBJECT public: explicit KJobModel(QObject *parent = nullptr); QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; public slots: void objectAdded(QObject *obj); void objectRemoved(QObject *obj); private slots: void jobResult(KJob *job); void jobFinished(KJob *obj); void jobInfo(KJob *job, const QString &plainMessage); private: int indexOfJob(QObject *obj) const; struct KJobInfo { KJob *job; QString name; QString type; QString statusText; enum { Running, Finished, Error, Killed, Deleted } state; }; QVector m_data; }; } #endif // GAMMARAY_KJOBMODEL_H gammaray-2.11.3/plugins/kjobtracker/kjobtracker.cpp000066400000000000000000000033661413310443000223640ustar00rootroot00000000000000/* kjobtracker.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2012-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "kjobtracker.h" #include "kjobmodel.h" #include #include #include using namespace GammaRay; KJobTracker::KJobTracker(Probe *probe, QObject *parent) : QObject(parent) , m_jobModel(new KJobModel(this)) { connect(probe, &Probe::objectCreated, m_jobModel, &KJobModel::objectAdded); connect(probe, &Probe::objectDestroyed, m_jobModel, &KJobModel::objectRemoved); auto proxy = new QSortFilterProxyModel(this); proxy->setSourceModel(m_jobModel); probe->registerModel(QStringLiteral("com.kdab.GammaRay.KJobModel"), proxy); } KJobTracker::~KJobTracker() = default; gammaray-2.11.3/plugins/kjobtracker/kjobtracker.h000066400000000000000000000035531413310443000220270ustar00rootroot00000000000000/* kjobtracker.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2012-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_KJOBTRACKER_KJOBTRACKER_H #define GAMMARAY_KJOBTRACKER_KJOBTRACKER_H #include #include class KJob; namespace GammaRay { class KJobModel; class KJobTracker : public QObject { Q_OBJECT public: explicit KJobTracker(Probe *probe, QObject *parent = nullptr); ~KJobTracker() override; private: KJobModel *m_jobModel; }; class KJobTrackerFactory : public QObject, public StandardToolFactory { Q_OBJECT Q_INTERFACES(GammaRay::ToolFactory) Q_PLUGIN_METADATA(IID "com.kdab.GammaRay.ToolFactory" FILE "gammaray_kjobtracker.json") public: explicit KJobTrackerFactory(QObject *parent = nullptr) : QObject(parent) { } }; } #endif // GAMMARAY_KJOBTRACKER_H gammaray-2.11.3/plugins/kjobtracker/kjobtrackerwidget.cpp000066400000000000000000000035021413310443000235600ustar00rootroot00000000000000/* kjobtrackerwidget.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2012-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "kjobtrackerwidget.h" #include "ui_kjobtrackerwidget.h" #include #include using namespace GammaRay; #include #include #include using namespace GammaRay; KJobTrackerWidget::KJobTrackerWidget(QWidget *parent) : QWidget(parent) , ui(new Ui::KJobTrackerWidget) , m_stateManager(this) { ui->setupUi(this); auto model = ObjectBroker::model(QStringLiteral("com.kdab.GammaRay.KJobModel")); new SearchLineController(ui->searchLine, model); ui->jobView->header()->setObjectName("jobViewHeader"); ui->jobView->setModel(model); } KJobTrackerWidget::~KJobTrackerWidget() = default; gammaray-2.11.3/plugins/kjobtracker/kjobtrackerwidget.h000066400000000000000000000036121413310443000232270ustar00rootroot00000000000000/* kjobtrackerwidget.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2012-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_KJOBTRACKER_KJOBTRACKERWIDGET_H #define GAMMARAY_KJOBTRACKER_KJOBTRACKERWIDGET_H #include #include #include namespace GammaRay { namespace Ui { class KJobTrackerWidget; } class KJobTrackerWidget : public QWidget { Q_OBJECT public: explicit KJobTrackerWidget(QWidget *parent = nullptr); ~KJobTrackerWidget() override; private: QScopedPointer ui; UIStateManager m_stateManager; }; class KJobTrackerUiFactory : public QObject, public StandardToolUiFactory { Q_OBJECT Q_INTERFACES(GammaRay::ToolUiFactory) Q_PLUGIN_METADATA(IID "com.kdab.GammaRay.ToolUiFactory" FILE "gammaray_kjobtracker.json") }; } #endif // GAMMARAY_KJOBTRACKERWIDGET_H gammaray-2.11.3/plugins/kjobtracker/kjobtrackerwidget.ui000066400000000000000000000020141413310443000234100ustar00rootroot00000000000000 GammaRay::KJobTrackerWidget 0 0 400 300 false true true GammaRay::DeferredTreeView QTreeView
ui/deferredtreeview.h
gammaray-2.11.3/plugins/localeinspector/000077500000000000000000000000001413310443000202345ustar00rootroot00000000000000gammaray-2.11.3/plugins/localeinspector/CMakeLists.txt000066400000000000000000000017641413310443000230040ustar00rootroot00000000000000# probe part if (NOT GAMMARAY_CLIENT_ONLY_BUILD) set(gammaray_localeinspector_plugin_srcs localeinspector.cpp localemodel.cpp localedataaccessor.cpp localeaccessormodel.cpp ) list(APPEND gammaray_localeinspector_plugin_srcs timezonemodel.cpp timezoneoffsetdatamodel.cpp ) gammaray_add_plugin(gammaray_localeinspector_plugin JSON gammaray_localeinspector.json SOURCES ${gammaray_localeinspector_plugin_srcs} ) target_link_libraries(gammaray_localeinspector_plugin gammaray_core) endif() # ui part if(GAMMARAY_BUILD_UI) set(gammaray_localeinspector_plugin_ui_srcs localeinspectorwidget.cpp localetab.cpp timezonetab.cpp timezoneclientmodel.cpp timezoneoffsetdataclientmodel.cpp ) gammaray_add_plugin(gammaray_localeinspector_ui_plugin JSON gammaray_localeinspector.json SOURCES ${gammaray_localeinspector_plugin_ui_srcs} ) target_link_libraries(gammaray_localeinspector_ui_plugin gammaray_ui) endif() gammaray-2.11.3/plugins/localeinspector/gammaray_localeinspector.json000066400000000000000000000001501413310443000261670ustar00rootroot00000000000000{ "id": "gammaray_localeinspector", "name": "Locales", "types": [ "QObject" ] } gammaray-2.11.3/plugins/localeinspector/localeaccessormodel.cpp000066400000000000000000000060561413310443000247520ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Stephen Kelly Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "localeaccessormodel.h" #include "localedataaccessor.h" #include #include using namespace GammaRay; LocaleAccessorModel::LocaleAccessorModel(LocaleDataAccessorRegistry *registry, QObject *parent) : QAbstractTableModel(parent) , m_registry(registry) { } int LocaleAccessorModel::columnCount(const QModelIndex &parent) const { if (parent.isValid()) return 0; int area = m_registry->accessors().size(); return qSqrt(area); } int LocaleAccessorModel::rowCount(const QModelIndex &parent) const { if (parent.isValid()) return 0; int area = m_registry->accessors().size(); return qCeil((float)area / (int)qSqrt(area)); } Qt::ItemFlags LocaleAccessorModel::flags(const QModelIndex &index) const { return QAbstractTableModel::flags(index) | Qt::ItemIsUserCheckable; } QVariant LocaleAccessorModel::data(const QModelIndex &index, int role) const { QVector acc = m_registry->accessors(); int offset = (index.row() * columnCount()) + index.column(); if (offset >= acc.size()) return QVariant(); LocaleDataAccessor *accessor = acc.at(offset); switch (role) { case Qt::DisplayRole: return accessor->accessorName(); case Qt::CheckStateRole: return m_registry->enabledAccessors().contains(accessor) ? Qt::Checked : Qt::Unchecked; case AccessorRole: return QVariant::fromValue(accessor); default: return QVariant(); } } bool LocaleAccessorModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (role != Qt::CheckStateRole) return QAbstractItemModel::setData(index, value, role); bool enabled = value.toInt() == Qt::Checked; LocaleDataAccessor *accessor = index.data(AccessorRole).value(); m_registry->setAccessorEnabled(accessor, enabled); emit dataChanged(index, index); return true; } gammaray-2.11.3/plugins/localeinspector/localeaccessormodel.h000066400000000000000000000042451413310443000244150ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Stephen Kelly Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_LOCALEINSPECTOR_LOCALEACCESSORMODEL_H #define GAMMARAY_LOCALEINSPECTOR_LOCALEACCESSORMODEL_H #include "common/modelroles.h" #include #include namespace GammaRay { class LocaleDataAccessorRegistry; struct LocaleDataAccessor; class LocaleAccessorModel : public QAbstractTableModel { Q_OBJECT Q_ENUMS(CustomRoles) public: enum CustomRoles { AccessorRole = UserRole + 1 }; explicit LocaleAccessorModel(LocaleDataAccessorRegistry *registry, QObject *parent = nullptr); int rowCount(const QModelIndex &parent = QModelIndex()) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; Qt::ItemFlags flags(const QModelIndex &index) const override; bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; private: QVector enabledAccessors; LocaleDataAccessorRegistry *m_registry; }; } #endif gammaray-2.11.3/plugins/localeinspector/localedataaccessor.cpp000066400000000000000000000262021413310443000245560ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Stephen Kelly Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "localedataaccessor.h" #include using namespace GammaRay; static QString dayNamesToString(const QLocale &locale, QString (QLocale::*accessor)(int, QLocale::FormatType) const, QLocale::FormatType type) { QStringList result; result.reserve(7); for (int i = 1; i <= 7; ++i) result.push_back((locale.*accessor)(i, type)); return result.join(QStringLiteral(", ")); } static QString monthNamesToString(const QLocale &locale, QString (QLocale::*accessor)(int, QLocale::FormatType) const, QLocale::FormatType type) { QStringList result; result.reserve(12); for (int i = 1; i <= 12; ++i) result.push_back((locale.*accessor)(i, type)); return result.join(QStringLiteral(", ")); } LocaleDataAccessorRegistry::LocaleDataAccessorRegistry(QObject *parent) : QObject(parent) { init(); } LocaleDataAccessorRegistry::~LocaleDataAccessorRegistry() { qDeleteAll(m_accessors); } QVector< LocaleDataAccessor * > LocaleDataAccessorRegistry::accessors() { return m_accessors; } QVector< LocaleDataAccessor * > LocaleDataAccessorRegistry::enabledAccessors() { return m_enabledAccessors; } void LocaleDataAccessorRegistry::registerAccessor(LocaleDataAccessor *accessor) { m_accessors.push_back(accessor); } void LocaleDataAccessorRegistry::setAccessorEnabled(LocaleDataAccessor *accessor, bool enabled) { QVector< LocaleDataAccessor * > &accessors = m_enabledAccessors; if (enabled && !accessors.contains(accessor)) { accessors.push_back(accessor); emit accessorAdded(); } else { int idx = accessors.indexOf(accessor); if (idx >= 0) { accessors.remove(idx); emit accessorRemoved(idx); } } } void LocaleDataAccessorRegistry::init() { LOCALE_SIMPLE_DEFAULT_ACCESSOR(Name, return locale.name(); ) LOCALE_SIMPLE_DEFAULT_ACCESSOR(Language, return QLocale::languageToString(locale.language()); ) LOCALE_SIMPLE_DEFAULT_ACCESSOR(Country, return QLocale::countryToString(locale.country()); ) LOCALE_SIMPLE_ACCESSOR(Script, return QLocale::scriptToString(locale.script()); ) LOCALE_SIMPLE_ACCESSOR(Currency, return locale.currencySymbol(QLocale::CurrencySymbol) +QLatin1String(" (") +locale.currencySymbol(QLocale::CurrencyIsoCode) +QLatin1String(") - ") +locale.currencySymbol(QLocale::CurrencyDisplayName); ) LOCALE_SIMPLE_ACCESSOR(TextDirection, return locale.textDirection() == Qt::LeftToRight ? QStringLiteral( "LTR") : QStringLiteral("RTL"); ) LOCALE_SIMPLE_DEFAULT_ACCESSOR(TimeFormatLong, return locale.timeFormat(QLocale::LongFormat); ) LOCALE_SIMPLE_ACCESSOR(TimeFormatShort, return locale.timeFormat(QLocale::ShortFormat); ) LOCALE_SIMPLE_ACCESSOR(TimeFormatNarrow, return locale.timeFormat(QLocale::NarrowFormat); ) LOCALE_SIMPLE_DEFAULT_ACCESSOR(DateFormatLong, return locale.dateFormat(QLocale::LongFormat); ) LOCALE_SIMPLE_ACCESSOR(DateFormatShort, return locale.dateFormat(QLocale::ShortFormat); ) LOCALE_SIMPLE_ACCESSOR(DateFormatNarrow, return locale.dateFormat(QLocale::NarrowFormat); ) LOCALE_SIMPLE_DEFAULT_ACCESSOR(DateTimeFormatLong, return locale.dateTimeFormat(QLocale::LongFormat); ) LOCALE_SIMPLE_ACCESSOR(DateTimeFormatShort, return locale.dateTimeFormat(QLocale::ShortFormat); ) LOCALE_SIMPLE_ACCESSOR(DateTimeFormatNarrow, return locale.dateTimeFormat(QLocale::NarrowFormat); ) LOCALE_SIMPLE_ACCESSOR(AmText, return locale.amText(); ) LOCALE_SIMPLE_ACCESSOR(PmText, return locale.pmText(); ) LOCALE_SIMPLE_ACCESSOR(DayNamesLong, return dayNamesToString(locale, &QLocale::dayName, QLocale::LongFormat); ) LOCALE_SIMPLE_ACCESSOR(DayNamesNarrow, return dayNamesToString(locale, &QLocale::dayName, QLocale::NarrowFormat); ) LOCALE_SIMPLE_ACCESSOR(DayNamesShort, return dayNamesToString(locale, &QLocale::dayName, QLocale::ShortFormat); ) LOCALE_SIMPLE_ACCESSOR(StandaloneDayNamesLong, return dayNamesToString(locale, &QLocale::standaloneDayName, QLocale::LongFormat); ) LOCALE_SIMPLE_ACCESSOR(StandaloneDayNamesNarrow, return dayNamesToString(locale, &QLocale::standaloneDayName, QLocale::NarrowFormat); ) LOCALE_SIMPLE_ACCESSOR(StandaloneDayNamesShort, return dayNamesToString(locale, &QLocale::standaloneDayName, QLocale::ShortFormat); ) LOCALE_SIMPLE_DEFAULT_ACCESSOR(FirstDayOfWeek, return QLocale().dayName(locale.firstDayOfWeek()); ) LOCALE_SIMPLE_DEFAULT_ACCESSOR(WeekDays, const auto wds = locale.weekdays(); QStringList resultList; resultList.reserve(wds.size()); for (Qt::DayOfWeek dayNumber : wds) { resultList << QLocale().dayName(dayNumber); } return QLocale().createSeparatedList(resultList); ) LOCALE_SIMPLE_ACCESSOR(MonthNamesLong, return monthNamesToString(locale, &QLocale::monthName, QLocale::LongFormat); ) LOCALE_SIMPLE_ACCESSOR(MonthNamesNarrow, return monthNamesToString(locale, &QLocale::monthName, QLocale::NarrowFormat); ) LOCALE_SIMPLE_ACCESSOR(MonthNamesShort, return monthNamesToString(locale, &QLocale::monthName, QLocale::ShortFormat); ) LOCALE_SIMPLE_ACCESSOR(StandaloneMonthNamesLong, return monthNamesToString(locale, &QLocale::standaloneMonthName, QLocale::LongFormat); ) LOCALE_SIMPLE_ACCESSOR(StandaloneMonthNamesNarrow, return monthNamesToString(locale, &QLocale::standaloneMonthName, QLocale::NarrowFormat); ) LOCALE_SIMPLE_ACCESSOR(StandaloneMonthNamesLongShort, return monthNamesToString(locale, &QLocale::standaloneMonthName, QLocale::ShortFormat); ) LOCALE_SIMPLE_ACCESSOR(BCP47, return locale.bcp47Name(); ) LOCALE_SIMPLE_ACCESSOR(NativeCountry, return locale.nativeCountryName(); ) LOCALE_SIMPLE_ACCESSOR(NativeLanguage, return locale.nativeLanguageName(); ) LOCALE_SIMPLE_ACCESSOR(UiLanguages, return locale.uiLanguages().join(QStringLiteral(", ")); ) LOCALE_SIMPLE_DEFAULT_ACCESSOR(MeasurementSystem, return locale.measurementSystem() == QLocale::ImperialSystem ? QStringLiteral( "Imperial") : QStringLiteral("Metric"); ) LOCALE_SIMPLE_DEFAULT_ACCESSOR(FloatFormat, return locale.toString(10000.1); ) LOCALE_SIMPLE_ACCESSOR(DecimalPoint, return locale.decimalPoint(); ) LOCALE_SIMPLE_ACCESSOR(GroupSeparator, return locale.groupSeparator(); ) LOCALE_SIMPLE_ACCESSOR(Exponential, return locale.exponential(); ) LOCALE_SIMPLE_ACCESSOR(Percent, return locale.percent(); ) LOCALE_SIMPLE_ACCESSOR(PositiveSign, return locale.positiveSign(); ) LOCALE_SIMPLE_ACCESSOR(NegativeSign, return locale.negativeSign(); ) LOCALE_SIMPLE_ACCESSOR(ZeroDigit, return locale.zeroDigit(); ) } gammaray-2.11.3/plugins/localeinspector/localedataaccessor.h000066400000000000000000000077211413310443000242300ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation toolocale. Copyright (C) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Stephen Kelly Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_LOCALEINSPECTOR_LOCALEDATAACCESSOR_H #define GAMMARAY_LOCALEINSPECTOR_LOCALEDATAACCESSOR_H #include #include #include #include #include namespace GammaRay { struct LocaleDataAccessor; class LocaleDataAccessorRegistry : public QObject { Q_OBJECT public: explicit LocaleDataAccessorRegistry(QObject *parent = nullptr); ~LocaleDataAccessorRegistry() override; void registerAccessor(LocaleDataAccessor *accessor); void setAccessorEnabled(LocaleDataAccessor *accessor, bool enabled); QVector accessors(); QVector enabledAccessors(); Q_SIGNALS: void accessorAdded(); void accessorRemoved(int idx); private: void init(); private: QVector m_accessors; QVector m_enabledAccessors; }; struct LocaleDataAccessor { LocaleDataAccessor(LocaleDataAccessorRegistry *registry, bool defaultAccessor = false) { registry->registerAccessor(this); if (defaultAccessor) registry->setAccessorEnabled(this, true); } virtual ~LocaleDataAccessor() = default; virtual QString accessorName() = 0; QVariant data(const QLocale &locale, int role) { if (role == Qt::DisplayRole) return display(locale); return QVariant(); } virtual QString display(const QLocale &) { return QString(); } Q_DISABLE_COPY(LocaleDataAccessor) }; #define LOCALE_DISPLAY_ACCESSOR(NAME) \ struct Locale ## NAME ## Accessor : LocaleDataAccessor \ { \ explicit Locale ## NAME ## Accessor(LocaleDataAccessorRegistry *registry) : LocaleDataAccessor( \ registry) {} \ QString accessorName() { return QStringLiteral(#NAME); } \ QString display(const QLocale &locale) \ { \ #define LOCALE_DEFAULT_DISPLAY_ACCESSOR(NAME) \ struct Locale ## NAME ## Accessor : LocaleDataAccessor \ { \ explicit Locale ## NAME ## Accessor(LocaleDataAccessorRegistry *registry) : LocaleDataAccessor( \ registry, true) {} \ \ QString accessorName() \ { \ return QStringLiteral(#NAME); \ } \ QString display(const QLocale &locale) \ { \ #define LOCALE_DISPLAY_ACCESSOR_END(NAME) \ return QString(); \ } \ }; new Locale ## NAME ## Accessor(this); #define LOCALE_SIMPLE_ACCESSOR(NAME, IMPLEMENTATION) \ LOCALE_DISPLAY_ACCESSOR(NAME) \ IMPLEMENTATION \ LOCALE_DISPLAY_ACCESSOR_END(NAME) #define LOCALE_SIMPLE_DEFAULT_ACCESSOR(NAME, IMPLEMENTATION) \ LOCALE_DEFAULT_DISPLAY_ACCESSOR(NAME) \ IMPLEMENTATION \ LOCALE_DISPLAY_ACCESSOR_END(NAME) } Q_DECLARE_METATYPE(GammaRay::LocaleDataAccessor *) #endif gammaray-2.11.3/plugins/localeinspector/localeinspector.cpp000066400000000000000000000056201413310443000241310ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Stephen Kelly Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "localeinspector.h" #include "localemodel.h" #include "localeaccessormodel.h" #include "localedataaccessor.h" #include "timezonemodel.h" #include "timezonemodelroles.h" #include "timezoneoffsetdatamodel.h" #include #include #include #include #include using namespace GammaRay; LocaleInspector::LocaleInspector(Probe *probe, QObject *parent) : QObject(parent) { auto *registry = new LocaleDataAccessorRegistry(this); auto *model = new LocaleModel(registry, this); auto proxy = new ServerProxyModel(this); proxy->setSourceModel(model); probe->registerModel(QStringLiteral("com.kdab.GammaRay.LocaleModel"), proxy); auto *accessorModel = new LocaleAccessorModel(registry, this); probe->registerModel(QStringLiteral("com.kdab.GammaRay.LocaleAccessorModel"), accessorModel); auto tzModel = new TimezoneModel(this); proxy = new ServerProxyModel(this); proxy->setSourceModel(tzModel); proxy->addRole(TimezoneModelRoles::LocalZoneRole); probe->registerModel(QStringLiteral("com.kdab.GammaRay.TimezoneModel"), proxy); auto tzSelModel = ObjectBroker::selectionModel(proxy); connect(tzSelModel, &QItemSelectionModel::selectionChanged, this, &LocaleInspector::timezoneSelected); m_offsetModel = new TimezoneOffsetDataModel(this); probe->registerModel(QStringLiteral("com.kdab.GammaRay.TimezoneOffsetDataModel"), m_offsetModel); } void LocaleInspector::timezoneSelected(const QItemSelection& selection) { if (selection.isEmpty()) return; auto idx = selection.first().topLeft(); idx = idx.sibling(idx.row(), 0); m_offsetModel->setTimezone(QTimeZone(idx.data().toString().toUtf8())); } gammaray-2.11.3/plugins/localeinspector/localeinspector.h000066400000000000000000000037461413310443000236050ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Stephen Kelly Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_LOCALEINSPECTOR_LOCALEINSPECTOR_H #define GAMMARAY_LOCALEINSPECTOR_LOCALEINSPECTOR_H #include QT_BEGIN_NAMESPACE class QItemSelection; QT_END_NAMESPACE namespace GammaRay { class TimezoneOffsetDataModel; class LocaleInspector : public QObject { Q_OBJECT public: explicit LocaleInspector(Probe *probe, QObject *parent = nullptr); private: void timezoneSelected(const QItemSelection &selection); TimezoneOffsetDataModel *m_offsetModel; }; class LocaleInspectorFactory : public QObject, public StandardToolFactory { Q_OBJECT Q_INTERFACES(GammaRay::ToolFactory) Q_PLUGIN_METADATA(IID "com.kdab.GammaRay.ToolFactory" FILE "gammaray_localeinspector.json") public: explicit LocaleInspectorFactory(QObject *parent = nullptr) : QObject(parent) { } }; } #endif // GAMMARAY_LOCALEINSPECTOR_H gammaray-2.11.3/plugins/localeinspector/localeinspectorwidget.cpp000066400000000000000000000032401413310443000253310ustar00rootroot00000000000000/* localeinspectorwidget.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "localeinspectorwidget.h" #include "ui_localeinspectorwidget.h" #include using namespace GammaRay; LocaleInspectorWidget::LocaleInspectorWidget(QWidget *parent) : QWidget(parent) , ui(new Ui::LocaleInspectorWidget) { ui->setupUi(this); ui->tabWidget->setTabEnabled(ui->tabWidget->indexOf(ui->timezoneTab), Endpoint::instance()->objectAddress(QLatin1String("com.kdab.GammaRay.TimezoneModel")) != Protocol::InvalidObjectAddress); } LocaleInspectorWidget::~LocaleInspectorWidget() = default; gammaray-2.11.3/plugins/localeinspector/localeinspectorwidget.h000066400000000000000000000036251413310443000250050ustar00rootroot00000000000000/* localeinspectorwidget.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_LOCALEINSPECTOR_LOCALEINSPECTORWIDGET_H #define GAMMARAY_LOCALEINSPECTOR_LOCALEINSPECTORWIDGET_H #include #include #include namespace GammaRay { namespace Ui { class LocaleInspectorWidget; } class LocaleInspectorWidget : public QWidget { Q_OBJECT public: explicit LocaleInspectorWidget(QWidget *parent = nullptr); ~LocaleInspectorWidget() override; private: std::unique_ptr ui; }; class LocaleInspectorUiFactory : public QObject, public StandardToolUiFactory { Q_OBJECT Q_INTERFACES(GammaRay::ToolUiFactory) Q_PLUGIN_METADATA(IID "com.kdab.GammaRay.ToolUiFactory" FILE "gammaray_localeinspector.json") }; } #endif // GAMMARAY_LOCALEINSPECTORWIDGET_H gammaray-2.11.3/plugins/localeinspector/localeinspectorwidget.ui000066400000000000000000000024641413310443000251730ustar00rootroot00000000000000 GammaRay::LocaleInspectorWidget 0 0 654 506 0 0 Locales Timezones GammaRay::LocaleTab QWidget
localetab.h
1
GammaRay::TimezoneTab QWidget
timezonetab.h
1
gammaray-2.11.3/plugins/localeinspector/localemodel.cpp000066400000000000000000000064661413310443000232340ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Stephen Kelly Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "localemodel.h" #include "localedataaccessor.h" #include #include using namespace GammaRay; LocaleModel::LocaleModel(LocaleDataAccessorRegistry *registry, QObject *parent) : QAbstractTableModel(parent) , m_registry(registry) { init(); connect(registry, &LocaleDataAccessorRegistry::accessorAdded, this, &LocaleModel::accessorAdded); connect(registry, &LocaleDataAccessorRegistry::accessorRemoved, this, &LocaleModel::accessorRemoved); } int LocaleModel::columnCount(const QModelIndex &parent) const { if (parent.isValid()) return 0; return m_localeData.size(); } QVariant LocaleModel::data(const QModelIndex &index, int role) const { if (!index.isValid() || index.row() >= m_locales.size() || index.column() >= m_localeData.size()) return QVariant(); const QLocale l = m_locales.at(index.row()); return m_localeData.at(index.column())->data(l, role); } QVariant LocaleModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role != Qt::DisplayRole) return QAbstractItemModel::headerData(section, orientation, role); if (orientation == Qt::Vertical) return QAbstractItemModel::headerData(section, orientation, role); LocaleDataAccessor *d = m_localeData.at(section); return d->accessorName(); } void LocaleModel::init() { m_localeData = m_registry->enabledAccessors(); m_locales = QLocale::matchingLocales(QLocale::AnyLanguage, QLocale::AnyScript, QLocale::AnyCountry).toVector(); } void LocaleModel::accessorAdded() { Q_ASSERT(m_localeData.size() + 1 == m_registry->enabledAccessors().size()); beginInsertColumns(QModelIndex(), m_localeData.size(), m_localeData.size()); m_localeData = m_registry->enabledAccessors(); endInsertColumns(); } void LocaleModel::accessorRemoved(int idx) { Q_ASSERT(m_localeData.size() - 1 == m_registry->enabledAccessors().size()); beginRemoveColumns(QModelIndex(), idx, idx); m_localeData = m_registry->enabledAccessors(); endRemoveColumns(); } int LocaleModel::rowCount(const QModelIndex &parent) const { if (parent.isValid()) return 0; return m_locales.size(); } gammaray-2.11.3/plugins/localeinspector/localemodel.h000066400000000000000000000041561413310443000226730ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Stephen Kelly Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_LOCALEINSPECTOR_LOCALEMODEL_H #define GAMMARAY_LOCALEINSPECTOR_LOCALEMODEL_H #include #include #include namespace GammaRay { class LocaleDataAccessorRegistry; struct LocaleDataAccessor; class LocaleModel : public QAbstractTableModel { Q_OBJECT public: explicit LocaleModel(LocaleDataAccessorRegistry *registry, QObject *parent = nullptr); int rowCount(const QModelIndex &parent = QModelIndex()) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; private: void init(); private slots: void accessorAdded(); void accessorRemoved(int idx); private: QVector m_locales; QVector m_localeData; LocaleDataAccessorRegistry *m_registry; }; } #endif gammaray-2.11.3/plugins/localeinspector/localetab.cpp000066400000000000000000000062001413310443000226640ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Stephen Kelly Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "localetab.h" #include "ui_localetab.h" #include #include using namespace GammaRay; LocaleTab::LocaleTab(QWidget *parent) : QWidget(parent) , ui(new Ui::LocaleTab) , m_stateManager(this) { QAbstractItemModel *localeModel = ObjectBroker::model(QStringLiteral("com.kdab.GammaRay.LocaleModel")); QAbstractItemModel *accessorModel = ObjectBroker::model(QStringLiteral("com.kdab.GammaRay.LocaleAccessorModel")); ui->setupUi(this); ui->accessorTable->horizontalHeader()->setObjectName("accessorTableHHeader"); ui->accessorTable->verticalHeader()->setObjectName("accessorTableVHeader"); ui->localeTable->horizontalHeader()->setObjectName("localeTableHHeader"); ui->localeTable->verticalHeader()->setObjectName("localeTableVHeader"); ui->localeTable->setModel(localeModel); ui->accessorTable->setModel(accessorModel); new SearchLineController(ui->localeSearchLine, localeModel); ui->accessorTable->resizeColumnsToContents(); ui->localeTable->resizeColumnsToContents(); connect(localeModel, &QAbstractItemModel::modelReset, ui->localeTable, &QTableView::resizeColumnsToContents); connect(accessorModel, &QAbstractItemModel::dataChanged, ui->accessorTable, &QTableView::resizeColumnsToContents); QMetaObject::invokeMethod(this, "initSplitterPosition", Qt::QueuedConnection); connect(accessorModel, &QAbstractItemModel::rowsInserted, this, &LocaleTab::initSplitterPosition); } LocaleTab::~LocaleTab() = default; void LocaleTab::initSplitterPosition() { const int accessorHeight = ui->accessorTable->model()->rowCount() * (ui->accessorTable->rowHeight(0) + 1) // + grid line + 2 * ui->accessorTable->frameWidth(); m_stateManager.setDefaultSizes(ui->mainSplitter, UISizeVector() << accessorHeight << height() - accessorHeight); m_stateManager.restoreState(); } gammaray-2.11.3/plugins/localeinspector/localetab.h000066400000000000000000000031141413310443000223320ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Stephen Kelly Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_LOCALETAB_H #define GAMMARAY_LOCALETAB_H #include #include #include namespace GammaRay { namespace Ui { class LocaleTab; } class LocaleTab : public QWidget { Q_OBJECT public: explicit LocaleTab(QWidget *parent = nullptr); ~LocaleTab() override; private slots: void initSplitterPosition(); private: std::unique_ptr ui; UIStateManager m_stateManager; }; } #endif // GAMMARAY_LOCALETAB_H gammaray-2.11.3/plugins/localeinspector/localetab.ui000066400000000000000000000031061413310443000225210ustar00rootroot00000000000000 GammaRay::LocaleTab 0 0 654 506 Qt::Vertical QAbstractItemView::NoSelection false false false 0 true false gammaray-2.11.3/plugins/localeinspector/timezoneclientmodel.cpp000066400000000000000000000066741413310443000250270ustar00rootroot00000000000000/* timezoneclientmodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "timezoneclientmodel.h" #include "timezonemodelroles.h" #include #include #include using namespace GammaRay; TimezoneClientModel::TimezoneClientModel(QObject* parent) : QIdentityProxyModel(parent) { } TimezoneClientModel::~TimezoneClientModel() = default; QVariant TimezoneClientModel::data(const QModelIndex& index, int role) const { if (role == Qt::ToolTipRole && index.column() > 0) { return QIdentityProxyModel::data(index.sibling(index.row(), 0), role); } else if (role == Qt::DisplayRole && index.column() == TimezoneModelColumns::DSTColumn) { const auto v = QIdentityProxyModel::data(index, Qt::DisplayRole); const auto b = v.type() == QVariant::Bool && v.toBool(); if (b && qApp->style()->standardIcon(QStyle::SP_DialogYesButton).isNull()) return tr("yes"); return QVariant(); } else if (role == Qt::DecorationRole && index.column() == TimezoneModelColumns::DSTColumn) { const auto v = QIdentityProxyModel::data(index, Qt::DisplayRole); const auto b = v.type() == QVariant::Bool && v.toBool(); return b ? qApp->style()->standardIcon(QStyle::SP_DialogYesButton) : QVariant(); } else if (role == Qt::FontRole) { const auto v = QIdentityProxyModel::data(index.sibling(index.row(), 0), TimezoneModelRoles::LocalZoneRole); const auto b = v.type() == QVariant::Bool && v.toBool(); if (b) { QFont f; f.setBold(true); return f; } } return QIdentityProxyModel::data(index, role); } QVariant TimezoneClientModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { switch (section) { case TimezoneModelColumns::IanaIdColumn: return tr("IANA Id"); case TimezoneModelColumns::CountryColumn: return tr("Country"); case TimezoneModelColumns::StandardDisplayNameColumn: return tr("Standard Display Name"); case TimezoneModelColumns::DSTColumn: return tr("DST"); case TimezoneModelColumns::WindowsIdColumn: return tr("Windows Id"); } } return QAbstractItemModel::headerData(section, orientation, role); } gammaray-2.11.3/plugins/localeinspector/timezoneclientmodel.h000066400000000000000000000032021413310443000244540ustar00rootroot00000000000000/* timezoneclientmodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_TIMEZONECLIENTMODEL_H #define GAMMARAY_TIMEZONECLIENTMODEL_H #include namespace GammaRay { class TimezoneClientModel : public QIdentityProxyModel { Q_OBJECT public: explicit TimezoneClientModel(QObject *parent = nullptr); ~TimezoneClientModel() override; QVariant data(const QModelIndex & index, int role) const override; QVariant headerData(int section, Qt::Orientation orientation, int role) const override; }; } #endif // GAMMARAY_TIMEZONECLIENTMODEL_H gammaray-2.11.3/plugins/localeinspector/timezonemodel.cpp000066400000000000000000000055531413310443000236230ustar00rootroot00000000000000/* timezonemodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "timezonemodel.h" #include "timezonemodelroles.h" #include #include using namespace GammaRay; TimezoneModel::TimezoneModel(QObject* parent) : QAbstractTableModel(parent) { } TimezoneModel::~TimezoneModel() = default; int TimezoneModel::columnCount(const QModelIndex& parent) const { Q_UNUSED(parent); return TimezoneModelColumns::COUNT; } int TimezoneModel::rowCount(const QModelIndex& parent) const { if (parent.isValid()) return 0; if (m_ids.isEmpty()) m_ids = QTimeZone::availableTimeZoneIds(); return m_ids.count(); } QVariant TimezoneModel::data(const QModelIndex& index, int role) const { if (!index.isValid()) return QVariant(); if (role == Qt::DisplayRole) { const QTimeZone tz(m_ids.at(index.row())); switch (index.column()) { case TimezoneModelColumns::IanaIdColumn: return tz.id(); case TimezoneModelColumns::CountryColumn: return QLocale::countryToString(tz.country()); case TimezoneModelColumns::StandardDisplayNameColumn: return tz.displayName(QTimeZone::StandardTime); case TimezoneModelColumns::DSTColumn: return tz.hasDaylightTime(); case TimezoneModelColumns::WindowsIdColumn: return QTimeZone::ianaIdToWindowsId(tz.id()); } } else if (role == Qt::ToolTipRole && index.column() == 0) { const QTimeZone tz(m_ids.at(index.row())); return tz.comment(); } else if (role == TimezoneModelRoles::LocalZoneRole && index.column() == 0) { if (m_ids.at(index.row()) == QTimeZone::systemTimeZoneId()) return true; return QVariant(); } return QVariant(); } gammaray-2.11.3/plugins/localeinspector/timezonemodel.h000066400000000000000000000033171413310443000232640ustar00rootroot00000000000000/* timezonemodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_TIMEZONEMODEL_H #define GAMMARAY_TIMEZONEMODEL_H #include #include #include namespace GammaRay { class TimezoneModel : public QAbstractTableModel { Q_OBJECT public: explicit TimezoneModel(QObject *parent = nullptr); ~TimezoneModel() override; int columnCount(const QModelIndex & parent) const override; int rowCount(const QModelIndex & parent) const override; QVariant data(const QModelIndex & index, int role) const override; private: mutable QList m_ids; }; } #endif // GAMMARAY_TIMEZONEMODEL_H gammaray-2.11.3/plugins/localeinspector/timezonemodelroles.h000066400000000000000000000030601413310443000243240ustar00rootroot00000000000000/* timezonemodelroles.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_TIMEZONEMODELROLES_H #define GAMMARAY_TIMEZONEMODELROLES_H #include namespace GammaRay { namespace TimezoneModelRoles { enum Role { LocalZoneRole = GammaRay::UserRole }; } namespace TimezoneModelColumns { enum Column { IanaIdColumn, CountryColumn, StandardDisplayNameColumn, DSTColumn, WindowsIdColumn, COUNT }; } } #endif gammaray-2.11.3/plugins/localeinspector/timezoneoffsetdataclientmodel.cpp000066400000000000000000000036571413310443000270660ustar00rootroot00000000000000/* timezoneoffsetdataclientmodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "timezoneoffsetdataclientmodel.h" using namespace GammaRay; TimezoneOffsetDataClientModel::TimezoneOffsetDataClientModel(QObject* parent) : QIdentityProxyModel(parent) { } TimezoneOffsetDataClientModel::~TimezoneOffsetDataClientModel() = default; QVariant TimezoneOffsetDataClientModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role == Qt::DisplayRole && orientation == Qt::Horizontal) { switch (section) { case 0: return tr("Transition Time (UTC)"); case 1: return tr("Offset to UTC"); case 2: return tr("Standard Time Offset"); case 3: return tr("DST Offset"); case 4: return tr("Abbreviation"); } } return QAbstractItemModel::headerData(section, orientation, role); } gammaray-2.11.3/plugins/localeinspector/timezoneoffsetdataclientmodel.h000066400000000000000000000032011413310443000265140ustar00rootroot00000000000000/* timezoneoffsetdataclientmodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_TIMEZONEOFFSETDATACLIENTMODEL_H #define GAMMARAY_TIMEZONEOFFSETDATACLIENTMODEL_H #include namespace GammaRay { class TimezoneOffsetDataClientModel : public QIdentityProxyModel { Q_OBJECT public: explicit TimezoneOffsetDataClientModel(QObject *parent = nullptr); ~TimezoneOffsetDataClientModel() override; QVariant headerData(int section, Qt::Orientation orientation, int role) const override; }; } #endif // GAMMARAY_TIMEZONEOFFSETDATACLIENTMODEL_H gammaray-2.11.3/plugins/localeinspector/timezoneoffsetdatamodel.cpp000066400000000000000000000062511413310443000256600ustar00rootroot00000000000000/* timezoneoffsetdatamodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "timezoneoffsetdatamodel.h" #include using namespace GammaRay; TimezoneOffsetDataModel::TimezoneOffsetDataModel(QObject* parent) : QAbstractTableModel(parent) { } TimezoneOffsetDataModel::~TimezoneOffsetDataModel() = default; void TimezoneOffsetDataModel::setTimezone(const QTimeZone& tz) { if (!m_offsets.isEmpty()) { beginRemoveRows(QModelIndex(), 0, m_offsets.size() - 1); m_offsets.clear(); endRemoveRows(); } QVector offsets; offsets.reserve(60); const auto start = QDateTime::currentDateTime(); auto offset = tz.offsetData(start); for (int i = 0; i < 30; ++i) { offset = tz.previousTransition(offset.atUtc); if (!offset.atUtc.isValid()) break; offsets.prepend(offset); } offset = tz.offsetData(start); for (int i = 0; i < 30; ++i) { offset = tz.nextTransition(offset.atUtc); if (!offset.atUtc.isValid()) break; offsets.push_back(offset); } if (!offsets.isEmpty()) { beginInsertRows(QModelIndex(), 0, offsets.size() - 1); m_offsets = offsets; endInsertRows(); } } int TimezoneOffsetDataModel::columnCount(const QModelIndex& parent) const { Q_UNUSED(parent); return 5; } int TimezoneOffsetDataModel::rowCount(const QModelIndex& parent) const { if (parent.isValid()) return 0; return m_offsets.size(); } QVariant TimezoneOffsetDataModel::data(const QModelIndex& index, int role) const { if (!index.isValid()) return QVariant(); if (role == Qt::DisplayRole) { const auto offset = m_offsets.at(index.row()); switch (index.column()) { case 0: return offset.atUtc; case 1: return offset.offsetFromUtc; case 2: return offset.standardTimeOffset; case 3: return offset.daylightTimeOffset; case 4: return offset.abbreviation; } } return QVariant(); } gammaray-2.11.3/plugins/localeinspector/timezoneoffsetdatamodel.h000066400000000000000000000034671413310443000253330ustar00rootroot00000000000000/* timezoneoffsetdatamodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_TIMEZONEOFFSETDATAMODEL_H #define GAMMARAY_TIMEZONEOFFSETDATAMODEL_H #include #include namespace GammaRay { class TimezoneOffsetDataModel : public QAbstractTableModel { Q_OBJECT public: explicit TimezoneOffsetDataModel(QObject *parent = nullptr); ~TimezoneOffsetDataModel() override; void setTimezone(const QTimeZone &tz); int columnCount(const QModelIndex & parent) const override; int rowCount(const QModelIndex & parent) const override; QVariant data(const QModelIndex & index, int role) const override; private: QVector m_offsets; }; } #endif // GAMMARAY_TIMEZONEOFFSETDATAMODEL_H gammaray-2.11.3/plugins/localeinspector/timezonetab.cpp000066400000000000000000000045651413310443000232730ustar00rootroot00000000000000/* timezonetab.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "timezonetab.h" #include "ui_timezonetab.h" #include "timezoneclientmodel.h" #include "timezoneoffsetdataclientmodel.h" #include #include #include using namespace GammaRay; TimezoneTab::TimezoneTab(QWidget *parent) : QWidget(parent) , ui(new Ui::TimezoneTab) { ui->setupUi(this); if (Endpoint::instance()->objectAddress(QLatin1String("com.kdab.GammaRay.TimezoneModel")) == Protocol::InvalidObjectAddress) return; auto tzModel = ObjectBroker::model(QLatin1String("com.kdab.GammaRay.TimezoneModel")); auto tzProxy = new TimezoneClientModel(this); tzProxy->setSourceModel(tzModel); ui->tzView->setModel(tzProxy); new SearchLineController(ui->tzSearchLine, tzModel); auto selModel = ObjectBroker::selectionModel(tzProxy); ui->tzView->setSelectionModel(selModel); auto offsetModel = ObjectBroker::model(QLatin1String("com.kdab.GammaRay.TimezoneOffsetDataModel")); auto offsetProxy = new TimezoneOffsetDataClientModel(this); offsetProxy->setSourceModel(offsetModel); ui->offsetView->setModel(offsetProxy); ui->offsetView->header()->setSectionResizeMode(QHeaderView::ResizeToContents); } TimezoneTab::~TimezoneTab() = default; gammaray-2.11.3/plugins/localeinspector/timezonetab.h000066400000000000000000000027711413310443000227350ustar00rootroot00000000000000/* timezonetab.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_TIMEZONETAB_H #define GAMMARAY_TIMEZONETAB_H #include #include namespace GammaRay { namespace Ui { class TimezoneTab; } class TimezoneTab : public QWidget { Q_OBJECT public: explicit TimezoneTab(QWidget *parent = nullptr); ~TimezoneTab() override; private: std::unique_ptr ui; }; } #endif // GAMMARAY_TIMEZONETAB_H gammaray-2.11.3/plugins/localeinspector/timezonetab.ui000066400000000000000000000024371413310443000231220ustar00rootroot00000000000000 GammaRay::TimezoneTab 0 0 400 300 Qt::Horizontal false true false true gammaray-2.11.3/plugins/mimetypes/000077500000000000000000000000001413310443000170625ustar00rootroot00000000000000gammaray-2.11.3/plugins/mimetypes/CMakeLists.txt000066400000000000000000000011231413310443000216170ustar00rootroot00000000000000# probe plugin if (NOT GAMMARAY_CLIENT_ONLY_BUILD) set(gammaray_mimetypes_srcs mimetypes.cpp mimetypesmodel.cpp ) gammaray_add_plugin(gammaray_mimetypes JSON gammaray_mimetypes.json SOURCES ${gammaray_mimetypes_srcs}) target_link_libraries(gammaray_mimetypes gammaray_core Qt5::Gui gammaray_kitemmodels) endif() # ui plugin if(GAMMARAY_BUILD_UI) set(gammaray_mimetypes_ui_srcs mimetypeswidget.cpp ) gammaray_add_plugin(gammaray_mimetypes_ui JSON gammaray_mimetypes.json SOURCES ${gammaray_mimetypes_ui_srcs}) target_link_libraries(gammaray_mimetypes_ui gammaray_ui) endif() gammaray-2.11.3/plugins/mimetypes/gammaray_mimetypes.json000066400000000000000000000002301413310443000236420ustar00rootroot00000000000000{ "hidden": false, "id": "gammaray_mimetypes", "name": "Mime Types", "name[de]": "Mime Typen", "types": [ "QObject" ] } gammaray-2.11.3/plugins/mimetypes/mimetypes.cpp000066400000000000000000000031001413310443000215740ustar00rootroot00000000000000/* mimetypes.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2012-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "mimetypes.h" #include "mimetypesmodel.h" #include <3rdparty/kde/krecursivefilterproxymodel.h> using namespace GammaRay; MimeTypes::MimeTypes(Probe *probe, QObject *parent) : QObject(parent) { auto model = new MimeTypesModel(this); auto proxy = new KRecursiveFilterProxyModel(this); proxy->setSourceModel(model); probe->registerModel(QStringLiteral("com.kdab.GammaRay.MimeTypeModel"), proxy); } MimeTypes::~MimeTypes() = default; gammaray-2.11.3/plugins/mimetypes/mimetypes.h000066400000000000000000000034761413310443000212610ustar00rootroot00000000000000/* mimetypes.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2012-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_MIMETYPES_MIMETYPES_H #define GAMMARAY_MIMETYPES_MIMETYPES_H #include "core/toolfactory.h" QT_BEGIN_NAMESPACE class QStandardItemModel; QT_END_NAMESPACE namespace GammaRay { class MimeTypes : public QObject { Q_OBJECT public: explicit MimeTypes(Probe *probe, QObject *parent = nullptr); ~MimeTypes() override; }; class MimeTypesFactory : public QObject, public StandardToolFactory { Q_OBJECT Q_INTERFACES(GammaRay::ToolFactory) Q_PLUGIN_METADATA(IID "com.kdab.GammaRay.ToolFactory" FILE "gammaray_mimetypes.json") public: explicit MimeTypesFactory(QObject *parent = nullptr) : QObject(parent) { } }; } #endif // GAMMARAY_MIMETYPES_H gammaray-2.11.3/plugins/mimetypes/mimetypesmodel.cpp000066400000000000000000000141411413310443000226240ustar00rootroot00000000000000/* mimetypesmodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "mimetypesmodel.h" #include #include using namespace GammaRay; MimeTypesModel::MimeTypesModel(QObject *parent) : QStandardItemModel(parent) , m_modelFilled(false) { } MimeTypesModel::~MimeTypesModel() = default; QVariant MimeTypesModel::data(const QModelIndex &index, int role) const { if (index.isValid() && role == Qt::DecorationRole && index.column() == 3) { QStandardItem *item = itemFromIndex(index); if (!item) return QVariant(); // on-demand lookup of icons, too slow to do that all in one go const QVariant v = item->data(Qt::DecorationRole); if (v.isNull()) { const QString iconName = item->data(IconNameRole).toString(); const QString genericIconName = item->data(GenericIconNameRole).toString(); if (iconName.isEmpty() && genericIconName.isEmpty()) return QVariant(); QIcon icon = QIcon::fromTheme(item->data(IconNameRole).toString()); if (icon.isNull()) icon = QIcon::fromTheme(item->data(GenericIconNameRole).toString()); const_cast(this)->blockSignals(true); item->setIcon(icon); item->setData(QVariant(), IconNameRole); // reset to avoid trying to look up an icon multiple times item->setData(QVariant(), GenericIconNameRole); const_cast(this)->blockSignals(false); return icon; } else { return v; } } return QStandardItemModel::data(index, role); } Qt::ItemFlags MimeTypesModel::flags(const QModelIndex &index) const { return QStandardItemModel::flags(index) & ~Qt::ItemIsEditable; } int MimeTypesModel::rowCount(const QModelIndex &parent) const { const_cast(this)->fillModel(); return QStandardItemModel::rowCount(parent); } QVector MimeTypesModel::itemsForType(const QString &mimeTypeName) { if (m_mimeTypeNodes.contains(mimeTypeName)) return m_mimeTypeNodes.value(mimeTypeName); makeItemsForType(mimeTypeName); return m_mimeTypeNodes.value(mimeTypeName); } void MimeTypesModel::makeItemsForType(const QString &mimeTypeName) { const QMimeType mt = m_db.mimeTypeForName(mimeTypeName); if (mt.parentMimeTypes().isEmpty()) { const QList row = makeRowForType(mt); appendRow(row); m_mimeTypeNodes[mt.name()].push_back(row.first()); } else { // parentMimeTypes contains duplicates and aliases const QSet parentMimeTypeNames = normalizedMimeTypeNames(mt.parentMimeTypes()); for (const QString &parentTypeName : parentMimeTypeNames) { foreach (QStandardItem *parentItem, itemsForType(parentTypeName)) { const QList row = makeRowForType(mt); parentItem->appendRow(row); m_mimeTypeNodes[mt.name()].push_back(row.first()); } } } } QSet< QString > MimeTypesModel::normalizedMimeTypeNames(const QStringList &typeNames) const { QSet res; for (const QString &typeName : typeNames) { const QMimeType mt = m_db.mimeTypeForName(typeName); res.insert(mt.name()); } return res; } QList MimeTypesModel::makeRowForType(const QMimeType &mt) { QList row; auto *item = new QStandardItem; item->setText(mt.name()); row.push_back(item); item = new QStandardItem; item->setText(mt.comment()); row.push_back(item); item = new QStandardItem; item->setText(mt.globPatterns().join(QStringLiteral(", "))); row.push_back(item); item = new QStandardItem; item->setText(mt.iconName() + QLatin1String(" / ") + mt.genericIconName()); item->setData(mt.iconName(), IconNameRole); item->setData(mt.genericIconName(), GenericIconNameRole); row.push_back(item); item = new QStandardItem; QString s = mt.suffixes().join(QStringLiteral(", ")); if (!mt.preferredSuffix().isEmpty() && mt.suffixes().size() > 1) s += QLatin1String(" (") + mt.preferredSuffix() + QLatin1Char(')'); item->setText(s); row.push_back(item); item = new QStandardItem; item->setText(mt.aliases().join(QStringLiteral(", "))); row.push_back(item); return row; } void MimeTypesModel::fillModel() { if (m_modelFilled) return; m_modelFilled = true; blockSignals(true); setHorizontalHeaderLabels(QStringList() << tr("Name") << tr("Comment") << tr("Glob Patterns") << tr("Icons") << tr("Suffixes") << tr("Aliases")); foreach (const QMimeType &mt, m_db.allMimeTypes()) { if (!m_mimeTypeNodes.contains(mt.name())) makeItemsForType(mt.name()); } m_mimeTypeNodes.clear(); blockSignals(false); } gammaray-2.11.3/plugins/mimetypes/mimetypesmodel.h000066400000000000000000000043531413310443000222750ustar00rootroot00000000000000/* mimetypesmodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_MIMETYPES_MIMETYPESMODEL_H #define GAMMARAY_MIMETYPES_MIMETYPESMODEL_H #include "common/modelroles.h" #include #include namespace GammaRay { class MimeTypesModel : public QStandardItemModel { Q_OBJECT public: enum Roles { IconNameRole = UserRole + 1, GenericIconNameRole }; explicit MimeTypesModel(QObject *parent = nullptr); ~MimeTypesModel() override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; Qt::ItemFlags flags(const QModelIndex &index) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; private: void fillModel(); QVector itemsForType(const QString &mimeTypeName); void makeItemsForType(const QString &mimeTypeName); static QList makeRowForType(const QMimeType &mt); QSet normalizedMimeTypeNames(const QStringList &typeNames) const; QHash > m_mimeTypeNodes; QMimeDatabase m_db; bool m_modelFilled; }; } #endif // GAMMARAY_MIMETYPESMODEL_H gammaray-2.11.3/plugins/mimetypes/mimetypeswidget.cpp000066400000000000000000000036741413310443000230200ustar00rootroot00000000000000/* mimetypeswidget.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2012-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "mimetypeswidget.h" #include "ui_mimetypeswidget.h" #include #include using namespace GammaRay; MimeTypesWidget::MimeTypesWidget(QWidget *parent) : QWidget(parent) , ui(new Ui::MimeTypesWidget) , m_stateManager(this) { ui->setupUi(this); auto model = ObjectBroker::model(QStringLiteral("com.kdab.GammaRay.MimeTypeModel")); ui->mimeTypeView->header()->setObjectName("mimeTypeViewHeader"); ui->mimeTypeView->setDeferredResizeMode(0, QHeaderView::ResizeToContents); ui->mimeTypeView->setDeferredResizeMode(1, QHeaderView::ResizeToContents); ui->mimeTypeView->setModel(model); ui->mimeTypeView->sortByColumn(0, Qt::AscendingOrder); new SearchLineController(ui->searchLine, model); } MimeTypesWidget::~MimeTypesWidget() = default; gammaray-2.11.3/plugins/mimetypes/mimetypeswidget.h000066400000000000000000000035371413310443000224630ustar00rootroot00000000000000/* mimetypeswidget.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2012-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_MIMETYPESWIDGET_H #define GAMMARAY_MIMETYPESWIDGET_H #include #include #include namespace GammaRay { namespace Ui { class MimeTypesWidget; } class MimeTypesWidget : public QWidget { Q_OBJECT public: explicit MimeTypesWidget(QWidget *parent = nullptr); ~MimeTypesWidget() override; private: QScopedPointer ui; UIStateManager m_stateManager; }; class MimeTypesWidgetFactory : public QObject, public StandardToolUiFactory { Q_OBJECT Q_INTERFACES(GammaRay::ToolUiFactory) Q_PLUGIN_METADATA(IID "com.kdab.GammaRay.ToolUiFactory" FILE "gammaray_mimetypes.json") }; } #endif // GAMMARAY_MIMETYPESWIDGET_H gammaray-2.11.3/plugins/mimetypes/mimetypeswidget.ui000066400000000000000000000015131413310443000226410ustar00rootroot00000000000000 GammaRay::MimeTypesWidget 0 0 400 300 0 GammaRay::DeferredTreeView QTreeView
ui/deferredtreeview.h
gammaray-2.11.3/plugins/modelinspector/000077500000000000000000000000001413310443000200755ustar00rootroot00000000000000gammaray-2.11.3/plugins/modelinspector/CMakeLists.txt000066400000000000000000000015001413310443000226310ustar00rootroot00000000000000# probe part if (NOT GAMMARAY_CLIENT_ONLY_BUILD) set(gammaray_models_srcs modelinspector.cpp modelinspectorinterface.cpp modelmodel.cpp modelcellmodel.cpp modelcontentproxymodel.cpp selectionmodelmodel.cpp ) gammaray_add_plugin(gammaray_modelinspector JSON gammaray_modelinspector.json SOURCES ${gammaray_models_srcs} ) target_link_libraries(gammaray_modelinspector gammaray_core gammaray_kitemmodels ) endif() # ui part if(GAMMARAY_BUILD_UI) set(gammaray_models_ui_srcs modelinspectorwidget.cpp modelinspectorinterface.cpp modelinspectorclient.cpp modelcontentdelegate.cpp ) gammaray_add_plugin(gammaray_modelinspector_ui JSON gammaray_modelinspector.json SOURCES ${gammaray_models_ui_srcs} ) target_link_libraries(gammaray_modelinspector_ui gammaray_ui ) endif() gammaray-2.11.3/plugins/modelinspector/gammaray_modelinspector.json000066400000000000000000000004071413310443000256760ustar00rootroot00000000000000{ "id": "gammaray_modelinspector", "name": "Models", "name[de]": "Modelle", "selectableTypes": [ "QAbstractItemModel", "QItemSelectionModel" ], "types": [ "QAbstractItemModel", "QItemSelectionModel" ] } gammaray-2.11.3/plugins/modelinspector/modelcellmodel.cpp000066400000000000000000000143341413310443000235670ustar00rootroot00000000000000/* modelcellmodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "modelcellmodel.h" #include #include #include using namespace GammaRay; ModelCellModel::ModelCellModel(QObject *parent) : QAbstractTableModel(parent) { } static bool sourceIsQQmlListModel(const QAbstractItemModel *model) { Q_ASSERT(model); while (auto proxy = qobject_cast(model)) model = proxy->sourceModel(); return model->inherits("QQmlListModel"); } void ModelCellModel::setModelIndex(const QModelIndex &idx) { const auto newRoles = rolesForModel(idx.model()); if (newRoles != m_roles) { if (!m_roles.isEmpty()) { beginRemoveRows(QModelIndex(), 0, m_roles.size() - 1); m_roles.clear(); endRemoveRows(); } m_index = idx; if (!newRoles.isEmpty()) { beginInsertRows(QModelIndex(), 0, newRoles.size() - 1); m_roles = newRoles; endInsertRows(); } } else { m_index = idx; if (!m_roles.isEmpty()) { emit dataChanged(index(0, 1), index(rowCount() - 1, 1)); } } } QVector ModelCellModel::rolesForModel(const QAbstractItemModel *model) { QVector roles; if (!model) return roles; // add built-in roles const auto hasDefaultRoles = !sourceIsQQmlListModel(model); if (hasDefaultRoles) { #define R(x) qMakePair(x, QStringLiteral(#x)) roles << R(Qt::DisplayRole) << R(Qt::DecorationRole) << R(Qt::EditRole) << R(Qt::ToolTipRole) << R(Qt::StatusTipRole) << R(Qt::WhatsThisRole) << R(Qt::FontRole) << R(Qt::TextAlignmentRole) << R(Qt::BackgroundRole) << R(Qt::ForegroundRole) << R(Qt::CheckStateRole) << R(Qt::AccessibleTextRole) << R(Qt::AccessibleDescriptionRole) << R(Qt::SizeHintRole) << R(Qt::InitialSortOrderRole) ; #undef R } // add custom roles const auto roleNames = model->roleNames(); for (auto it = roleNames.constBegin(); it != roleNames.constEnd(); ++it) { bool roleFound = false; for (const auto &role : qAsConst(roles)) { if (role.first == it.key()) { roleFound = true; break; } } if (!roleFound) { const auto name = it.value().isEmpty() ? tr("Role #%1").arg(it.key()) : QString::fromLatin1(it.value()); roles.push_back(qMakePair(it.key(), name)); } } std::sort(roles.begin(), roles.end(), [] (const RoleInfo& lhs, const RoleInfo& rhs){return lhs.first < rhs.first;}); return roles; } QVariant ModelCellModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); Q_ASSERT(index.row() < m_roles.size()); const QVariant value = m_index.data(m_roles.at(index.row()).first); if (role == Qt::DisplayRole) { switch (index.column()) { case 0: return m_roles.at(index.row()).second; case 1: return VariantHandler::displayString(value); case 2: return value.typeName(); } } else if (role == Qt::EditRole) { if (index.column() == 1) return value; } else if (role == Qt::DecorationRole && index.column() == 1) { return VariantHandler::decoration(value); } return QVariant(); } bool ModelCellModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (index.isValid() && m_index.isValid() && (m_index.flags() & Qt::ItemIsEditable) && role == Qt::EditRole && index.column() == 1) { const Qt::ItemDataRole sourceRole = static_cast(m_roles.at(index.row()).first); QAbstractItemModel *sourceModel = const_cast(m_index.model()); return sourceModel->setData(m_index, value, sourceRole); } return QAbstractItemModel::setData(index, value, role); } Qt::ItemFlags ModelCellModel::flags(const QModelIndex &index) const { Qt::ItemFlags flags = QAbstractTableModel::flags(index); if (index.isValid() && m_index.isValid() && index.column() == 1) { if (m_index.flags() & Qt::ItemIsEditable) return flags | Qt::ItemIsEditable; } return flags; } int ModelCellModel::columnCount(const QModelIndex &parent) const { if (parent.isValid()) return 0; return 3; } int ModelCellModel::rowCount(const QModelIndex &parent) const { if (parent.isValid() || !m_index.isValid()) return 0; return m_roles.size(); } QVariant ModelCellModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role == Qt::DisplayRole && orientation == Qt::Horizontal) { switch (section) { case 0: return tr("Role"); case 1: return tr("Value"); case 2: return tr("Type"); } } return QAbstractItemModel::headerData(section, orientation, role); } gammaray-2.11.3/plugins/modelinspector/modelcellmodel.h000066400000000000000000000043601413310443000232320ustar00rootroot00000000000000/* modelcellmodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_MODELINSPECTOR_MODELCELLMODEL_H #define GAMMARAY_MODELINSPECTOR_MODELCELLMODEL_H #include #include namespace GammaRay { class ModelCellModel : public QAbstractTableModel { Q_OBJECT public: explicit ModelCellModel(QObject *parent = nullptr); void setModelIndex(const QModelIndex &idx); QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; Qt::ItemFlags flags(const QModelIndex &index) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; private: typedef QPair RoleInfo; static QVector rolesForModel(const QAbstractItemModel *model); QPersistentModelIndex m_index; QVector m_roles; }; } #endif // GAMMARAY_MODELCELLMODEL_H gammaray-2.11.3/plugins/modelinspector/modelcontentdelegate.cpp000066400000000000000000000047371413310443000250020ustar00rootroot00000000000000/* modelcontentdelegate.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "modelcontentdelegate.h" #include "modelcontentproxymodel.h" #include #include #include using namespace GammaRay; ModelContentDelegate::ModelContentDelegate(QObject* parent) : QStyledItemDelegate(parent) { } ModelContentDelegate::~ModelContentDelegate() = default; void ModelContentDelegate::paint(QPainter *painter, const QStyleOptionViewItem &origOption, const QModelIndex &index) const { Q_ASSERT(index.isValid()); auto option = origOption; initStyleOption(&option, index); if (index.data(ModelContentProxyModel::DisabledRole).toBool()) option.state = option.state & ~QStyle::State_Enabled; if (index.data(ModelContentProxyModel::SelectedRole).toBool()) { // for non-selected cells option.backgroundBrush = option.palette.highlight(); option.backgroundBrush.setStyle(Qt::BDiagPattern); // TODO also render selection for currently selected cells } if (index.data(ModelContentProxyModel::IsDisplayStringEmptyRole).toBool()) { option.palette.setColor(QPalette::Text, option.palette.color(QPalette::Disabled, QPalette::Text)); option.text = tr("").arg(index.row()).arg(index.column()); } QStyle *style = QApplication::style(); style->drawControl(QStyle::CE_ItemViewItem, &option, painter, nullptr); } gammaray-2.11.3/plugins/modelinspector/modelcontentdelegate.h000066400000000000000000000031321413310443000244330ustar00rootroot00000000000000/* modelcontentdelegate.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_MODELCONTENTDELEGATE_H #define GAMMARAY_MODELCONTENTDELEGATE_H #include namespace GammaRay { class ModelContentDelegate : public QStyledItemDelegate { Q_OBJECT public: explicit ModelContentDelegate(QObject *parent = nullptr); ~ModelContentDelegate() override; void paint(QPainter *painter, const QStyleOptionViewItem &origOption, const QModelIndex &index) const override; }; } #endif // GAMMARAY_MODELCONTENTDELEGATE_H gammaray-2.11.3/plugins/modelinspector/modelcontentproxymodel.cpp000066400000000000000000000112011413310443000254120ustar00rootroot00000000000000/* safetyfilterproxymodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "modelcontentproxymodel.h" #include #include using namespace GammaRay; ModelContentProxyModel::ModelContentProxyModel(QObject *parent) : QIdentityProxyModel(parent) , m_selectionModel(nullptr) { } ModelContentProxyModel::~ModelContentProxyModel() = default; void ModelContentProxyModel::setSourceModel(QAbstractItemModel *model) { setSelectionModel(nullptr); QIdentityProxyModel::setSourceModel(model); } void ModelContentProxyModel::setSelectionModel(QItemSelectionModel *selectionModel) { Q_ASSERT(!selectionModel || selectionModel->model() == sourceModel()); if (m_selectionModel == selectionModel) return; if (m_selectionModel) { disconnect(m_selectionModel.data(), &QItemSelectionModel::selectionChanged, this, &ModelContentProxyModel::selectionChanged); emitDataChangedForSelection(m_selectionModel->selection()); } m_selectionModel = selectionModel; if (m_selectionModel) { connect(m_selectionModel.data(), &QItemSelectionModel::selectionChanged, this, &ModelContentProxyModel::selectionChanged); emitDataChangedForSelection(m_selectionModel->selection()); } } QVariant ModelContentProxyModel::data(const QModelIndex &proxyIndex, int role) const { // Work around crash in QQmlListModel for unknown roles #if QT_VERSION < QT_VERSION_CHECK(5, 6, 1) if (sourceModel() && sourceModel()->inherits("QQmlListModel")) { // data on anything not in roleNames() crashes if (!sourceModel()->roleNames().contains(role)) return QVariant(); } #endif // we override this below, so convey enabled state via a custom role // since disabled is less common then enabled, only transfer disabled states if (role == DisabledRole) { if (QIdentityProxyModel::flags(proxyIndex) & Qt::ItemIsEnabled) return QVariant(); return true; } if (role == SelectedRole) { if (m_selectionModel && m_selectionModel->isSelected(mapToSource(proxyIndex))) return true; return QVariant(); } // we set the EmptyNameRole to signal this index is unnamed to the delegate if (role == IsDisplayStringEmptyRole) { return QIdentityProxyModel::data(proxyIndex, Qt::DisplayRole).toString().isNull(); } return QIdentityProxyModel::data(proxyIndex, role); } Qt::ItemFlags ModelContentProxyModel::flags(const QModelIndex &index) const { const auto f = QIdentityProxyModel::flags(index); if (!index.isValid()) return f; return f | Qt::ItemIsEnabled | Qt::ItemIsSelectable; // always enable items for inspection } QMap ModelContentProxyModel::itemData(const QModelIndex &index) const { auto d = QIdentityProxyModel::itemData(index); auto v = data(index, DisabledRole); if (!v.isNull()) d.insert(DisabledRole, v); v = data(index, SelectedRole); if (!v.isNull()) d.insert(SelectedRole, v); v = data(index, IsDisplayStringEmptyRole); if (!v.isNull()) d.insert(IsDisplayStringEmptyRole, v); return d; } void ModelContentProxyModel::emitDataChangedForSelection(const QItemSelection &selection) { for (const auto &range : selection) { if (!range.isValid()) continue; emit dataChanged(range.topLeft(), range.bottomRight()); } } void ModelContentProxyModel::selectionChanged(const QItemSelection& selected, const QItemSelection& deselected) { emitDataChangedForSelection(deselected); emitDataChangedForSelection(selected); } gammaray-2.11.3/plugins/modelinspector/modelcontentproxymodel.h000066400000000000000000000050741413310443000250720ustar00rootroot00000000000000/* safetyfilterproxymodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_MODELINSPECTOR_MODELCONTENTPROXYMODEL_H #define GAMMARAY_MODELINSPECTOR_MODELCONTENTPROXYMODEL_H #include #include #include QT_BEGIN_NAMESPACE class QItemSelection; class QItemSelectionModel; QT_END_NAMESPACE namespace GammaRay { /*! Proxies source model content to the client. * This does some safety checks for known broken sources. */ class ModelContentProxyModel : public QIdentityProxyModel { Q_OBJECT public: enum Roles { DisabledRole = GammaRay::UserRole + 1, SelectedRole, IsDisplayStringEmptyRole }; explicit ModelContentProxyModel(QObject *parent = nullptr); ~ModelContentProxyModel() override; /*! Will provide the selection via a custom role for rendering in ModelContentDelegate. */ void setSelectionModel(QItemSelectionModel *selectionModel); void setSourceModel(QAbstractItemModel *model) override; QVariant data(const QModelIndex &proxyIndex, int role = Qt::DisplayRole) const override; Qt::ItemFlags flags(const QModelIndex &index) const override; QMap itemData(const QModelIndex &index) const override; private slots: void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected); private: void emitDataChangedForSelection(const QItemSelection &selection); QPointer m_selectionModel; }; } #endif // SAFETYFILTERPROXYMODEL_H gammaray-2.11.3/plugins/modelinspector/modelinspector.cpp000066400000000000000000000163741413310443000236430ustar00rootroot00000000000000/* modelinspector.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "modelinspector.h" #include "modelmodel.h" #include "modelcellmodel.h" #include "modelcontentproxymodel.h" #include "selectionmodelmodel.h" #include #include #include <3rdparty/kde/krecursivefilterproxymodel.h> #include #include using namespace GammaRay; ModelInspector::ModelInspector(Probe *probe, QObject *parent) : ModelInspectorInterface(parent) , m_probe(probe) , m_modelModel(nullptr) , m_selectionModelsModel(new SelectionModelModel(this)) , m_selectionModelsSelectionModel(nullptr) , m_modelContentSelectionModel(nullptr) , m_modelContentProxyModel(new ModelContentProxyModel(this)) { auto modelModelSource = new ModelModel(this); connect(probe, &Probe::objectCreated, modelModelSource, &ModelModel::objectAdded); connect(probe, &Probe::objectDestroyed, modelModelSource, &ModelModel::objectRemoved); auto modelModelProxy = new ServerProxyModel(this); modelModelProxy->setSourceModel(modelModelSource); m_modelModel = modelModelProxy; probe->registerModel(QStringLiteral("com.kdab.GammaRay.ModelModel"), m_modelModel); m_modelSelectionModel = ObjectBroker::selectionModel(m_modelModel); connect(m_modelSelectionModel, &QItemSelectionModel::selectionChanged, this, &ModelInspector::modelSelected); connect(probe, &Probe::objectSelected, this, &ModelInspector::objectSelected); connect(probe, &Probe::objectCreated, m_selectionModelsModel, &SelectionModelModel::objectCreated); connect(probe, &Probe::objectDestroyed, m_selectionModelsModel, &SelectionModelModel::objectDestroyed); probe->registerModel(QStringLiteral("com.kdab.GammaRay.SelectionModels"), m_selectionModelsModel); m_selectionModelsSelectionModel = ObjectBroker::selectionModel(m_selectionModelsModel); connect(m_selectionModelsSelectionModel, &QItemSelectionModel::selectionChanged, this, &ModelInspector::selectionModelSelected); probe->registerModel(QStringLiteral("com.kdab.GammaRay.ModelContent"), m_modelContentProxyModel); m_modelContentSelectionModel = ObjectBroker::selectionModel(m_modelContentProxyModel); connect(m_modelContentSelectionModel, &QItemSelectionModel::selectionChanged, this, &ModelInspector::cellSelectionChanged); m_cellModel = new ModelCellModel(this); probe->registerModel(QStringLiteral("com.kdab.GammaRay.ModelCellModel"), m_cellModel); if (m_probe->needsObjectDiscovery()) connect(m_probe, &Probe::objectCreated, this, &ModelInspector::objectCreated); } void ModelInspector::modelSelected(const QItemSelection &selected) { QModelIndex index; if (!selected.isEmpty()) index = selected.first().topLeft(); if (index.isValid()) { QObject *obj = index.data(ObjectModel::ObjectRole).value(); QAbstractItemModel *model = qobject_cast(obj); Q_ASSERT(model); m_selectionModelsModel->setModel(model); m_modelContentProxyModel->setSourceModel(model); } else { m_selectionModelsModel->setModel(nullptr); m_modelContentProxyModel->setSourceModel(nullptr); } // clear the cell info box setCurrentCellData(ModelCellData()); m_cellModel->setModelIndex(QModelIndex()); m_modelContentSelectionModel->clear(); } void ModelInspector::objectSelected(QObject *object) { if (auto model = qobject_cast(object)) { if (model == m_modelContentProxyModel->sourceModel()) return; const auto indexList = m_modelModel->match(m_modelModel->index(0, 0), ObjectModel::ObjectRole, QVariant::fromValue(model), 1, Qt::MatchExactly | Qt::MatchRecursive | Qt::MatchWrap); if (indexList.isEmpty()) return; const auto index = indexList.first(); m_modelSelectionModel->select(index, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); } if (auto selModel = qobject_cast(object)) { if (!selModel->model()) return; objectSelected(const_cast(selModel->model())); const auto indexList = m_selectionModelsModel->match(m_selectionModelsModel->index(0, 0), ObjectModel::ObjectRole, QVariant::fromValue(selModel), 1, Qt::MatchExactly | Qt::MatchRecursive | Qt::MatchWrap); if (indexList.isEmpty()) return; const auto index = indexList.first(); m_selectionModelsSelectionModel->select(index, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); } } void ModelInspector::cellSelectionChanged(const QItemSelection &selection) { QModelIndex index; if (!selection.isEmpty()) index = selection.at(0).topLeft(); const QModelIndex sourceIndex = m_modelContentProxyModel->mapToSource(index); m_cellModel->setModelIndex(sourceIndex); if (!sourceIndex.isValid()) { setCurrentCellData(ModelCellData()); return; } ModelCellData cellData; cellData.row = sourceIndex.row(); cellData.column = sourceIndex.column(); cellData.internalId = QString::number(sourceIndex.internalId()); cellData.internalPtr = Util::addressToString(sourceIndex.internalPointer()); cellData.flags = sourceIndex.flags(); setCurrentCellData(cellData); } void ModelInspector::objectCreated(QObject *object) { if (!object) return; if (auto proxy = qobject_cast(object)) m_probe->discoverObject(proxy->sourceModel()); } void ModelInspector::selectionModelSelected(const QItemSelection &selected) { QModelIndex idx; if (!selected.isEmpty()) idx = selected.at(0).topLeft(); if (!idx.isValid()) { m_modelContentProxyModel->setSelectionModel(nullptr); return; } m_modelContentProxyModel->setSelectionModel(qobject_cast(idx.data(ObjectModel::ObjectRole).value())); } gammaray-2.11.3/plugins/modelinspector/modelinspector.h000066400000000000000000000054311413310443000233000ustar00rootroot00000000000000/* modelinspector.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_MODELINSPECTOR_MODELINSPECTOR_H #define GAMMARAY_MODELINSPECTOR_MODELINSPECTOR_H #include "modelinspectorinterface.h" #include #include QT_BEGIN_NAMESPACE class QAbstractItemModel; class QItemSelection; class QItemSelectionModel; class QModelIndex; QT_END_NAMESPACE namespace GammaRay { class ModelCellModel; class ModelContentProxyModel; class SelectionModelModel; class ModelInspector : public ModelInspectorInterface { Q_OBJECT Q_INTERFACES(GammaRay::ModelInspectorInterface) public: explicit ModelInspector(Probe *probe, QObject *parent = nullptr); private slots: void modelSelected(const QItemSelection &selected); void cellSelectionChanged(const QItemSelection &selection); void selectionModelSelected(const QItemSelection &selected); void objectSelected(QObject *object); void objectCreated(QObject *object); private: Probe *m_probe; QAbstractItemModel *m_modelModel; QItemSelectionModel *m_modelSelectionModel; SelectionModelModel *m_selectionModelsModel; QItemSelectionModel *m_selectionModelsSelectionModel; QItemSelectionModel *m_modelContentSelectionModel; ModelContentProxyModel *m_modelContentProxyModel; ModelCellModel *m_cellModel; }; class ModelInspectorFactory : public QObject, public StandardToolFactory { Q_OBJECT Q_INTERFACES(GammaRay::ToolFactory) Q_PLUGIN_METADATA(IID "com.kdab.GammaRay.ToolFactory" FILE "gammaray_modelinspector.json") public: explicit ModelInspectorFactory(QObject *parent = nullptr) : QObject(parent) { } }; } #endif // GAMMARAY_MODELINSPECTOR_H gammaray-2.11.3/plugins/modelinspector/modelinspectorclient.cpp000066400000000000000000000025301413310443000250270ustar00rootroot00000000000000/* modelinspectorclient.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "modelinspectorclient.h" using namespace GammaRay; ModelInspectorClient::ModelInspectorClient(QObject *parent) : ModelInspectorInterface(parent) { } ModelInspectorClient::~ModelInspectorClient() = default; gammaray-2.11.3/plugins/modelinspector/modelinspectorclient.h000066400000000000000000000030371413310443000244770ustar00rootroot00000000000000/* modelinspectorclient.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_MODELINSPECTORCLIENT_H #define GAMMARAY_MODELINSPECTORCLIENT_H #include "modelinspectorinterface.h" namespace GammaRay { class ModelInspectorClient : public ModelInspectorInterface { Q_OBJECT Q_INTERFACES(GammaRay::ModelInspectorInterface) public: explicit ModelInspectorClient(QObject *parent = nullptr); ~ModelInspectorClient() override; }; } #endif // GAMMARAY_MODELINSPECTORCLIENT_H gammaray-2.11.3/plugins/modelinspector/modelinspectorinterface.cpp000066400000000000000000000050731413310443000255160ustar00rootroot00000000000000/* modelinspectorinterface.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "modelinspectorinterface.h" #include #include using namespace GammaRay; QT_BEGIN_NAMESPACE GAMMARAY_ENUM_STREAM_OPERATORS(Qt::ItemFlags) static QDataStream &operator<<(QDataStream &out, const ModelCellData &data) { out << data.row << data.column << data.internalId << data.internalPtr << data.flags; return out; } static QDataStream &operator>>(QDataStream &in, ModelCellData &data) { in >> data.row >> data.column >> data.internalId >> data.internalPtr >> data.flags; return in; } QT_END_NAMESPACE bool ModelCellData::operator==(const ModelCellData& other) const { return row == other.row && column == other.column && internalId == other.internalId && internalPtr == other.internalPtr && flags == other.flags; } ModelInspectorInterface::ModelInspectorInterface(QObject *parent) : QObject(parent) { qRegisterMetaType(); qRegisterMetaTypeStreamOperators(); ObjectBroker::registerObject(this); } ModelInspectorInterface::~ModelInspectorInterface() = default; ModelCellData ModelInspectorInterface::currentCellData() const { return m_currentCellData; } void ModelInspectorInterface::setCurrentCellData(const ModelCellData& cellData) { if (m_currentCellData == cellData) return; m_currentCellData = cellData; emit currentCellDataChanged(); } gammaray-2.11.3/plugins/modelinspector/modelinspectorinterface.h000066400000000000000000000044341413310443000251630ustar00rootroot00000000000000/* modelinspectorinterface.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_MODELINSPECTORINTERFACE_H #define GAMMARAY_MODELINSPECTORINTERFACE_H #include #include namespace GammaRay { struct ModelCellData { ModelCellData() = default; bool operator==(const ModelCellData &other) const; int row = -1; int column = -1; QString internalId; QString internalPtr; Qt::ItemFlags flags = Qt::NoItemFlags; }; class ModelInspectorInterface : public QObject { Q_OBJECT Q_PROPERTY(GammaRay::ModelCellData cellData READ currentCellData WRITE setCurrentCellData NOTIFY currentCellDataChanged) public: explicit ModelInspectorInterface(QObject *parent = nullptr); ~ModelInspectorInterface() override; ModelCellData currentCellData() const; void setCurrentCellData(const ModelCellData &cellData); signals: void currentCellDataChanged(); private: ModelCellData m_currentCellData; }; } Q_DECLARE_METATYPE(GammaRay::ModelCellData) QT_BEGIN_NAMESPACE Q_DECLARE_TYPEINFO(GammaRay::ModelCellData, Q_MOVABLE_TYPE); Q_DECLARE_INTERFACE(GammaRay::ModelInspectorInterface, "com.kdab.GammaRay.ModelInspectorInterface") QT_END_NAMESPACE #endif // GAMMARAY_MODELINSPECTORINTERFACE_H gammaray-2.11.3/plugins/modelinspector/modelinspectorwidget.cpp000066400000000000000000000166601413310443000250450ustar00rootroot00000000000000/* modelinspectorwidget.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "modelinspectorwidget.h" #include "ui_modelinspectorwidget.h" #include "modelinspectorclient.h" #include "modelcontentdelegate.h" #include #include #include #include #include #include #include #include #include #include using namespace GammaRay; static QObject *createModelInspectorClient(const QString & /*name*/, QObject *parent) { return new ModelInspectorClient(parent); } ModelInspectorWidget::ModelInspectorWidget(QWidget *parent) : QWidget(parent) , ui(new Ui::ModelInspectorWidget) , m_stateManager(this) , m_interface(nullptr) { ui->setupUi(this); ui->modelView->header()->setObjectName("modelViewHeader"); ui->modelView->setDeferredResizeMode(0, QHeaderView::ResizeToContents); connect(ui->modelView, &QWidget::customContextMenuRequested, this, &ModelInspectorWidget::modelContextMenu); auto selectionModels = ObjectBroker::model(QStringLiteral("com.kdab.GammaRay.SelectionModels")); ui->selectionModelsView->setModel(selectionModels); ui->selectionModelsView->header()->setObjectName("selectionModelsViewHeader"); ui->selectionModelsView->header()->setSectionResizeMode(QHeaderView::ResizeToContents); connect(ui->selectionModelsView, &QWidget::customContextMenuRequested, this, &ModelInspectorWidget::selectionModelContextMenu); ui->selectionModelsView->setSelectionModel(ObjectBroker::selectionModel(selectionModels)); auto contentModel = ObjectBroker::model(QStringLiteral("com.kdab.GammaRay.ModelContent")); ui->modelContentView->setModel(contentModel); ui->modelContentView->setSelectionModel(ObjectBroker::selectionModel(contentModel)); ui->modelContentView->header()->setObjectName("modelContentViewHeader"); ui->modelContentView->setItemDelegate(new ModelContentDelegate(this)); ui->modelCellView->header()->setObjectName("modelCellViewHeader"); ui->modelCellView->setItemDelegate(new PropertyEditorDelegate(this)); ObjectBroker::registerClientObjectFactoryCallback( createModelInspectorClient); m_interface = ObjectBroker::object(); connect(m_interface, &ModelInspectorInterface::currentCellDataChanged, this, &ModelInspectorWidget::cellDataChanged); auto modelModel = ObjectBroker::model(QStringLiteral("com.kdab.GammaRay.ModelModel")); ui->modelView->setModel(modelModel); ui->modelView->setSelectionModel(ObjectBroker::selectionModel(modelModel)); new SearchLineController(ui->modelSearchLine, modelModel); connect(ui->modelView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &ModelInspectorWidget::modelSelected); ui->modelCellView->setModel(ObjectBroker::model(QStringLiteral( "com.kdab.GammaRay.ModelCellModel"))); m_stateManager.setDefaultSizes(ui->mainSplitter, UISizeVector() << "33%" << "33%" << "33%"); cellDataChanged(); } ModelInspectorWidget::~ModelInspectorWidget() = default; void ModelInspectorWidget::modelSelected(const QItemSelection &selected) { QModelIndex index; if (!selected.isEmpty()) index = selected.first().topLeft(); if (index.isValid()) // in case selection is not directly triggered by the user ui->modelView->scrollTo(index, QAbstractItemView::EnsureVisible); } #define F(x) { Qt:: x, #x } static const MetaEnum::Value item_flag_table[] = { { Qt::ItemIsSelectable, "Selectable" }, { Qt::ItemIsEditable, "Editable" }, { Qt::ItemIsDragEnabled, "DragEnabled" }, { Qt::ItemIsDropEnabled, "DropEnabled" }, { Qt::ItemIsUserCheckable, "UserCheckable" }, { Qt::ItemIsEnabled, "Enabled" }, #if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0) { Qt::ItemIsAutoTristate, "AutoTristate" }, #else { Qt::ItemIsTristate, "AutoTristate" }, #endif { Qt::ItemNeverHasChildren, "ItemNeverHasChildren" }, { Qt::ItemIsUserTristate, "UserTristate" } }; #undef F void ModelInspectorWidget::cellDataChanged() { const auto cellData = m_interface->currentCellData(); ui->indexLabel->setText(cellData.row != -1 ? tr("Row: %1 Column: %2").arg(cellData.row).arg(cellData.column) : tr("Invalid")); ui->internalIdLabel->setText(cellData.internalId); ui->internalPtrLabel->setText(cellData.internalPtr); ui->flagsLabel->setText(MetaEnum::flagsToString(cellData.flags, item_flag_table)); } void ModelInspectorWidget::objectRegistered(const QString &objectName) { if (objectName == QLatin1String("com.kdab.GammaRay.ModelContent.selection")) // delay, since it's not registered yet when the signal is emitted QMetaObject::invokeMethod(this, "setupModelContentSelectionModel", Qt::QueuedConnection); } void ModelInspectorWidget::modelContextMenu(QPoint pos) { const auto index = ui->modelView->indexAt(pos); if (!index.isValid()) return; const auto objectId = index.data(ObjectModel::ObjectIdRole).value(); QMenu menu; ContextMenuExtension ext(objectId); ext.setLocation(ContextMenuExtension::Creation, index.data( ObjectModel::CreationLocationRole).value()); ext.setLocation(ContextMenuExtension::Declaration, index.data(ObjectModel::DeclarationLocationRole).value()); ext.populateMenu(&menu); menu.exec(ui->modelView->viewport()->mapToGlobal(pos)); } void ModelInspectorWidget::selectionModelContextMenu(QPoint pos) { const auto index = ui->selectionModelsView->indexAt(pos); if (!index.isValid()) return; const auto objectId = index.data(ObjectModel::ObjectIdRole).value(); QMenu menu; ContextMenuExtension ext(objectId); ext.setLocation(ContextMenuExtension::Creation, index.data(ObjectModel::CreationLocationRole).value()); ext.setLocation(ContextMenuExtension::Declaration, index.data(ObjectModel::DeclarationLocationRole).value()); ext.populateMenu(&menu); menu.exec(ui->selectionModelsView->viewport()->mapToGlobal(pos)); } gammaray-2.11.3/plugins/modelinspector/modelinspectorwidget.h000066400000000000000000000044751413310443000245130ustar00rootroot00000000000000/* modelinspectorwidget.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_MODELINSPECTOR_MODELINSPECTORWIDGET_H #define GAMMARAY_MODELINSPECTOR_MODELINSPECTORWIDGET_H #include #include #include QT_BEGIN_NAMESPACE class QItemSelection; class QModelIndex; QT_END_NAMESPACE namespace GammaRay { class ModelInspectorInterface; namespace Ui { class ModelInspectorWidget; } class ModelInspectorWidget : public QWidget { Q_OBJECT public: explicit ModelInspectorWidget(QWidget *parent = nullptr); ~ModelInspectorWidget() override; private slots: void cellDataChanged(); void objectRegistered(const QString &objectName); void modelSelected(const QItemSelection &selected); void modelContextMenu(QPoint pos); void selectionModelContextMenu(QPoint pos); private: QScopedPointer ui; UIStateManager m_stateManager; ModelInspectorInterface *m_interface; }; class ModelInspectorUiFactory : public QObject, public StandardToolUiFactory { Q_OBJECT Q_INTERFACES(GammaRay::ToolUiFactory) Q_PLUGIN_METADATA(IID "com.kdab.GammaRay.ToolUiFactory" FILE "gammaray_modelinspector.json") }; } #endif // GAMMARAY_MODELINSPECTORWIDGET_H gammaray-2.11.3/plugins/modelinspector/modelinspectorwidget.ui000066400000000000000000000132041413310443000246670ustar00rootroot00000000000000 GammaRay::ModelInspectorWidget 0 0 788 479 Qt::Horizontal Qt::Vertical Models Qt::AlignCenter Qt::CustomContextMenu true Selection Models Qt::AlignCenter Qt::CustomContextMenu false true Model Content Qt::AlignCenter QAbstractItemView::SelectItems Cell Content Qt::AlignCenter Model Index: Invalid Internal Id: Internal Pointer: Flags: false true GammaRay::DeferredTreeView QTreeView
ui/deferredtreeview.h
gammaray-2.11.3/plugins/modelinspector/modelmodel.cpp000066400000000000000000000126461413310443000227330ustar00rootroot00000000000000/* modelmodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "modelmodel.h" using namespace GammaRay; ModelModel::ModelModel(QObject *parent) : ObjectModelBase(parent) { } QVariant ModelModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); QAbstractItemModel *model = static_cast(index.internalPointer()); if (!model) return QVariant(); return dataForObject(model, index, role); } int ModelModel::rowCount(const QModelIndex &parent) const { if (parent.isValid()) { if (parent.column() > 0) return 0; QAbstractItemModel *sourceModel = static_cast(parent.internalPointer()); Q_ASSERT(sourceModel); const QVector proxies = proxiesForModel(sourceModel); return proxies.size(); } return m_models.size(); } QModelIndex ModelModel::parent(const QModelIndex &child) const { QAbstractItemModel *model = static_cast(child.internalPointer()); Q_ASSERT(model); if (m_models.contains(model)) return {}; QAbstractProxyModel *proxy = qobject_cast(model); Q_ASSERT(proxy); return indexForModel(proxy->sourceModel()); } QModelIndex ModelModel::index(int row, int column, const QModelIndex &parent) const { if (parent.isValid()) { QAbstractItemModel *model = static_cast(parent.internalPointer()); const QVector proxies = proxiesForModel(model); if (proxies.size() <= row) return {}; return createIndex(row, column, proxies.at(row)); } return createIndex(row, column, m_models.at(row)); } void ModelModel::objectAdded(QObject *obj) { QAbstractProxyModel *proxy = qobject_cast(obj); if (proxy) { beginResetModel(); // FIXME if (proxy->sourceModel()) m_proxies.push_back(proxy); else m_models.push_back(proxy); connect(proxy, &QAbstractProxyModel::sourceModelChanged, this, [this, proxy]() { beginResetModel(); // FIXME if (proxy->sourceModel()) { const auto i = m_models.indexOf(proxy); if (i >= 0) m_models.remove(i); m_proxies.push_back(proxy); } else { const auto i = m_proxies.indexOf(proxy); if (i >= 0) m_proxies.remove(i); m_models.push_back(proxy); } endResetModel(); }); endResetModel(); return; } QAbstractItemModel *model = qobject_cast(obj); if (model) { beginInsertRows(QModelIndex(), m_models.size(), m_models.size()); m_models.push_back(model); endInsertRows(); } } void ModelModel::objectRemoved(QObject *obj) { int index = m_models.indexOf(static_cast(obj)); if (index >= 0 && index < m_models.size()) { beginRemoveRows(QModelIndex(), index, index); m_models.remove(index); endRemoveRows(); } for (auto it = m_proxies.begin(); it != m_proxies.end(); ++it) { if (*it == obj) { beginResetModel(); // FIXME m_proxies.erase(it); endResetModel(); return; } } } QModelIndex ModelModel::indexForModel(QAbstractItemModel *model) const { if (!model) return {}; QAbstractProxyModel *proxy = qobject_cast(model); if (!proxy) { Q_ASSERT(m_models.contains(model)); return index(m_models.indexOf(model), 0, QModelIndex()); } QAbstractItemModel *sourceModel = proxy->sourceModel(); const QModelIndex parentIndex = indexForModel(sourceModel); const QVector proxies = proxiesForModel(sourceModel); Q_ASSERT(proxies.contains(proxy)); return index(proxies.indexOf(proxy), 0, parentIndex); } QVector ModelModel::proxiesForModel(QAbstractItemModel *model) const { QVector proxies; if (!model) return proxies; for (QAbstractProxyModel *proxy : m_proxies) { if (proxy && proxy->sourceModel() == model) proxies.push_back(proxy); } return proxies; } gammaray-2.11.3/plugins/modelinspector/modelmodel.h000066400000000000000000000042551413310443000223750ustar00rootroot00000000000000/* modelmodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_MODELINSPECTOR_MODELMODEL_H #define GAMMARAY_MODELINSPECTOR_MODELMODEL_H #include #include #include #include namespace GammaRay { class ModelModel : public ObjectModelBase { Q_OBJECT public: explicit ModelModel(QObject *parent); QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; QModelIndex parent(const QModelIndex &child) const override; QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; public slots: void objectAdded(QObject *obj); void objectRemoved(QObject *obj); private: QModelIndex indexForModel(QAbstractItemModel *model) const; QVector proxiesForModel(QAbstractItemModel *model) const; private: QVector m_models; QVector m_proxies; }; } #endif // MODELMODEL_H gammaray-2.11.3/plugins/modelinspector/selectionmodelmodel.cpp000066400000000000000000000150771413310443000246420ustar00rootroot00000000000000/* selectionmodelmodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "selectionmodelmodel.h" #include #include #include using namespace GammaRay; SelectionModelModel::SelectionModelModel(QObject *parent) : ObjectModelBase(parent), m_model(nullptr) { } SelectionModelModel::~SelectionModelModel() = default; void SelectionModelModel::objectCreated(QObject* obj) { Q_ASSERT(obj); auto model = qobject_cast(obj); if (!model) return; auto it = std::lower_bound(m_selectionModels.begin(), m_selectionModels.end(), model); if (it != m_selectionModels.end() && *it == model) return; m_selectionModels.insert(it, model); connect(model, &QItemSelectionModel::selectionChanged, this, &SelectionModelModel::selectionChanged); connect(model, &QItemSelectionModel::modelChanged, this, &SelectionModelModel::sourceModelChanged); if (!m_model || model->model() != m_model) return; it = std::lower_bound(m_currentSelectionModels.begin(), m_currentSelectionModels.end(), model); const auto row = std::distance(m_currentSelectionModels.begin(), it); beginInsertRows(QModelIndex(), row, row); m_currentSelectionModels.insert(it, model); endInsertRows(); } void SelectionModelModel::objectDestroyed(QObject* obj) { Q_ASSERT(obj); auto model = static_cast(obj); // do not dereference! auto it = std::lower_bound(m_selectionModels.begin(), m_selectionModels.end(), model); if (it == m_selectionModels.end() || *it != model) return; m_selectionModels.erase(it); it = std::lower_bound(m_currentSelectionModels.begin(), m_currentSelectionModels.end(), model); if (it == m_currentSelectionModels.end() || *it != model) return; const auto row = std::distance(m_currentSelectionModels.begin(), it); beginRemoveRows(QModelIndex(), row, row); m_currentSelectionModels.erase(it); endRemoveRows(); } void SelectionModelModel::sourceModelChanged() { auto model = qobject_cast(sender()); Q_ASSERT(model); auto it = std::lower_bound(m_currentSelectionModels.begin(), m_currentSelectionModels.end(), model); if (it != m_currentSelectionModels.end() && *it == model && model->model() != m_model && m_model) { const auto row = std::distance(m_currentSelectionModels.begin(), it); beginRemoveRows(QModelIndex(), row, row); m_currentSelectionModels.erase(it); endRemoveRows(); } if (model->model() == m_model && m_model) { auto it = std::lower_bound(m_currentSelectionModels.begin(), m_currentSelectionModels.end(), model); if (it != m_currentSelectionModels.end() && *it == model) return; const auto row = std::distance(m_currentSelectionModels.begin(), it); beginInsertRows(QModelIndex(), row, row); m_currentSelectionModels.insert(it, model); endInsertRows(); } } void SelectionModelModel::selectionChanged() { auto model = qobject_cast(sender()); Q_ASSERT(model); if (model->model() != m_model) return; const auto it = std::lower_bound(m_currentSelectionModels.constBegin(), m_currentSelectionModels.constEnd(), model); Q_ASSERT(it != m_currentSelectionModels.constEnd() && *it == model); const auto row = std::distance(m_currentSelectionModels.constBegin(), it); emit dataChanged(index(row, 1), index(row, 3)); } void SelectionModelModel::setModel(QAbstractItemModel* model) { if (model == m_model) return; if (!m_currentSelectionModels.isEmpty()) { beginRemoveRows(QModelIndex(), 0, m_currentSelectionModels.size() - 1); m_currentSelectionModels.clear(); endRemoveRows(); } m_model = model; QVector models; std::copy_if(m_selectionModels.constBegin(), m_selectionModels.constEnd(), std::back_inserter(models), [this](QItemSelectionModel* model) { return model->model() == m_model; }); if (models.isEmpty()) return; beginInsertRows(QModelIndex(), 0, models.size() - 1); m_currentSelectionModels = models; endInsertRows(); } int SelectionModelModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); return 5; } int SelectionModelModel::rowCount(const QModelIndex &parent) const { if (parent.isValid()) return 0; return m_currentSelectionModels.size(); } QVariant SelectionModelModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); auto model = m_currentSelectionModels.at(index.row()); if (role == Qt::DisplayRole) { switch (index.column()) { case 1: return model->selectedIndexes().size(); case 2: return model->selectedRows().size(); case 3: return model->selectedColumns().size(); case 4: return ObjectDataProvider::typeName(model); } } return dataForObject(model, index, role); } QVariant SelectionModelModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { switch (section) { case 0: return tr("Object"); case 1: return tr("#Items"); case 2: return tr("#Rows"); case 3: return tr("#Columns"); case 4: return tr("Type"); } } return QAbstractTableModel::headerData(section, orientation, role); } gammaray-2.11.3/plugins/modelinspector/selectionmodelmodel.h000066400000000000000000000045741413310443000243070ustar00rootroot00000000000000/* selectionmodelmodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_SELECTIONMODELMODEL_H #define GAMMARAY_SELECTIONMODELMODEL_H #include #include QT_BEGIN_NAMESPACE class QItemSelectionModel; QT_END_NAMESPACE namespace GammaRay { /*! Selection models for a specific model. */ class SelectionModelModel : public ObjectModelBase { Q_OBJECT public: explicit SelectionModelModel(QObject *parent = nullptr); ~SelectionModelModel() override; /*! show only selection models for @p model */ void setModel(QAbstractItemModel *model); int columnCount(const QModelIndex &parent = QModelIndex()) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; public slots: void objectCreated(QObject *obj); void objectDestroyed(QObject *obj); private slots: void sourceModelChanged(); void selectionChanged(); private: QVector m_selectionModels; QVector m_currentSelectionModels; QAbstractItemModel *m_model; }; } #endif // GAMMARAY_SELECTIONMODELMODEL_H gammaray-2.11.3/plugins/network/000077500000000000000000000000001413310443000165375ustar00rootroot00000000000000gammaray-2.11.3/plugins/network/CMakeLists.txt000066400000000000000000000016011413310443000212750ustar00rootroot00000000000000# probe plugin if (NOT GAMMARAY_CLIENT_ONLY_BUILD) set(gammaray_network_srcs networksupport.cpp networkinterfacemodel.cpp networkconfigurationmodel.cpp networkreplymodel.cpp cookies/cookieextension.cpp cookies/cookiejarmodel.cpp ) gammaray_add_plugin(gammaray_network JSON gammaray_network.json SOURCES ${gammaray_network_srcs}) target_link_libraries(gammaray_network gammaray_core Qt5::Network) endif() # ui plugin if(GAMMARAY_BUILD_UI) set(gammaray_network_ui_srcs networkwidget.cpp networkinterfacewidget.cpp networkconfigurationwidget.cpp clientnetworkconfigurationmodel.cpp networkreplywidget.cpp clientnetworkreplymodel.cpp cookies/cookietab.cpp ) gammaray_add_plugin(gammaray_network_ui JSON gammaray_network.json SOURCES ${gammaray_network_ui_srcs}) target_link_libraries(gammaray_network_ui gammaray_ui Qt5::Network) endif() gammaray-2.11.3/plugins/network/clientnetworkconfigurationmodel.cpp000066400000000000000000000047021413310443000257470ustar00rootroot00000000000000/* clientnetworkconfigurationmodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "clientnetworkconfigurationmodel.h" #include "networkconfigurationmodelroles.h" #include using namespace GammaRay; ClientNetworkConfigurationModel::ClientNetworkConfigurationModel(QObject* parent) : QIdentityProxyModel(parent) { } ClientNetworkConfigurationModel::~ClientNetworkConfigurationModel() = default; QVariant ClientNetworkConfigurationModel::data(const QModelIndex& index, int role) const { if (role == Qt::FontRole) { if (index.sibling(index.row(), 0).data(NetworkConfigurationModelRoles::DefaultConfigRole).toBool()) { QFont f; f.setBold(true); return f; } } return QIdentityProxyModel::data(index, role); } QVariant ClientNetworkConfigurationModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { switch (section) { case 0: return tr("Name"); case 1: return tr("Identifier"); case 2: return tr("Bearer"); case 3: return tr("Timeout"); case 4: return tr("Roaming"); case 5: return tr("Purpose"); case 6: return tr("State"); case 7: return tr("Type"); } } return QIdentityProxyModel::headerData(section, orientation, role); } gammaray-2.11.3/plugins/network/clientnetworkconfigurationmodel.h000066400000000000000000000033701413310443000254140ustar00rootroot00000000000000/* clientnetworkconfigurationmodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_CLIENTNETWORKCONFIGURATIONMODEL_H #define GAMMARAY_CLIENTNETWORKCONFIGURATIONMODEL_H #include namespace GammaRay { class ClientNetworkConfigurationModel : public QIdentityProxyModel { Q_OBJECT public: explicit ClientNetworkConfigurationModel(QObject *parent = nullptr); ~ClientNetworkConfigurationModel() override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; }; } #endif // GAMMARAY_CLIENTNETWORKCONFIGURATIONMODEL_H gammaray-2.11.3/plugins/network/clientnetworkreplymodel.cpp000066400000000000000000000124451413310443000242360ustar00rootroot00000000000000/* clientnetworkreplymodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2019-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "clientnetworkreplymodel.h" #include "networkreplymodeldefs.h" #include #include #include #include #include #include using namespace GammaRay; ClientNetworkReplyModel::ClientNetworkReplyModel(QObject* parent) : QIdentityProxyModel(parent) { } ClientNetworkReplyModel::~ClientNetworkReplyModel() = default; QVariant ClientNetworkReplyModel::data(const QModelIndex& index, int role) const { if (role == Qt::DisplayRole && index.column() == NetworkReplyModelColumn::OpColumn) { const auto op = QIdentityProxyModel::data(index, role).toInt(); switch (op) { case QNetworkAccessManager::HeadOperation: return QStringLiteral("HEAD"); case QNetworkAccessManager::GetOperation: return QStringLiteral("GET"); case QNetworkAccessManager::PutOperation: return QStringLiteral("PUT"); case QNetworkAccessManager::PostOperation: return QStringLiteral("POST"); case QNetworkAccessManager::DeleteOperation: return QStringLiteral("DELETE"); case QNetworkAccessManager::CustomOperation: return tr("Custom"); } } if (role == Qt::ForegroundRole) { const auto state = QIdentityProxyModel::data(index.sibling(index.row(), NetworkReplyModelColumn::ObjectColumn), NetworkReplyModelRole::ReplyStateRole).toInt(); if (state & NetworkReply::Deleted) { return QGuiApplication::palette().color(QPalette::Disabled, QPalette::Text); } if (state & NetworkReply::Error) { return QColor(Qt::red); } } if (role == Qt::DecorationRole && index.parent().isValid()) { const auto state = QIdentityProxyModel::data(index.sibling(index.row(), NetworkReplyModelColumn::ObjectColumn), NetworkReplyModelRole::ReplyStateRole).toInt(); switch (index.column()) { case NetworkReplyModelColumn::ObjectColumn: if (state & NetworkReply::Error) { return QApplication::style()->standardIcon(QStyle::SP_DialogCancelButton); } else if (state & NetworkReply::Finished) { return QApplication::style()->standardIcon(QStyle::SP_DialogOkButton); } return QApplication::style()->standardIcon(QStyle::SP_BrowserReload); case NetworkReplyModelColumn::UrlColumn: { const auto url = QIdentityProxyModel::data(index, Qt::DisplayRole).toString(); if ((state & NetworkReply::Encrypted) || ((state & NetworkReply::Unencrypted) == 0 && url.startsWith(QLatin1String("https")))) { return UIResources::themedIcon(QLatin1String("lock.png")); } return UIResources::themedIcon(QLatin1String("lock-open.png")); } } } if (role == Qt::ToolTipRole) { if (index.column() == NetworkReplyModelColumn::UrlColumn) { return index.data(Qt::DisplayRole); } const auto errMsgs = QIdentityProxyModel::data(index.sibling(index.row(), NetworkReplyModelColumn::ObjectColumn), NetworkReplyModelRole::ReplyErrorRole).toStringList(); if (errMsgs.isEmpty()) { return {}; } if (errMsgs.size() == 1) { return errMsgs.at(0); } QString tt; for (const auto &msg : errMsgs) { tt += QLatin1String("
  • ") + msg + QLatin1String("
  • "); } return tt; } return QIdentityProxyModel::data(index, role); } QVariant ClientNetworkReplyModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { switch (section) { case NetworkReplyModelColumn::ObjectColumn: return tr("Reply"); case NetworkReplyModelColumn::OpColumn: return tr("Operation"); case NetworkReplyModelColumn::TimeColumn: return tr("Duration"); case NetworkReplyModelColumn::SizeColumn: return tr("Size"); case NetworkReplyModelColumn::UrlColumn: return tr("URL"); } } return QIdentityProxyModel::headerData(section, orientation, role); } gammaray-2.11.3/plugins/network/clientnetworkreplymodel.h000066400000000000000000000033041413310443000236750ustar00rootroot00000000000000/* clientnetworkreplymodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2019-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_CLIENTNETWORKREPLYMODEL_H #define GAMMARAY_CLIENTNETWORKREPLYMODEL_H #include namespace GammaRay { /** Client side of the QNetworkReply model. */ class ClientNetworkReplyModel : public QIdentityProxyModel { Q_OBJECT public: explicit ClientNetworkReplyModel(QObject *parent = nullptr); ~ClientNetworkReplyModel(); QVariant data(const QModelIndex & index, int role) const override; QVariant headerData(int section, Qt::Orientation orientation, int role) const override; }; } #endif // GAMMARAY_CLIENTNETWORKREPLYMODEL_H gammaray-2.11.3/plugins/network/cookies/000077500000000000000000000000001413310443000201735ustar00rootroot00000000000000gammaray-2.11.3/plugins/network/cookies/cookieextension.cpp000066400000000000000000000040021413310443000241010ustar00rootroot00000000000000/* cookieextension.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "cookieextension.h" #include "cookiejarmodel.h" #include #include #include using namespace GammaRay; CookieExtension::CookieExtension(PropertyController *controller) : PropertyControllerExtension(controller->objectBaseName() + ".cookieJar") , m_cookieJarModel(new CookieJarModel(controller)) { controller->registerModel(m_cookieJarModel, QStringLiteral("cookieJarModel")); } CookieExtension::~CookieExtension() = default; bool CookieExtension::setQObject(QObject *object) { if (auto cookieJar = qobject_cast(object)) { m_cookieJarModel->setCookieJar(cookieJar); return true; } else if (auto nam = qobject_cast(object)) { return setQObject(nam->cookieJar()); } m_cookieJarModel->setCookieJar(nullptr); return false; } gammaray-2.11.3/plugins/network/cookies/cookieextension.h000066400000000000000000000030751413310443000235570ustar00rootroot00000000000000/* cookieextension.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_COOKIEEXTENSION_H #define GAMMARAY_COOKIEEXTENSION_H #include namespace GammaRay { class CookieJarModel; class CookieExtension : public PropertyControllerExtension { public: explicit CookieExtension(PropertyController *controller); ~CookieExtension(); bool setQObject(QObject *object) override; private: CookieJarModel *m_cookieJarModel; }; } #endif // GAMMARAY_COOKIEEXTENSION_H gammaray-2.11.3/plugins/network/cookies/cookiejarmodel.cpp000066400000000000000000000076601413310443000236770ustar00rootroot00000000000000/* cookiejarmodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "cookiejarmodel.h" #include #include #include using namespace GammaRay; // protected member access control violation helper class CookieMonster : public QNetworkCookieJar { public: static QList pillageJar(QNetworkCookieJar *jar); }; QList CookieMonster::pillageJar(QNetworkCookieJar *jar) { auto openJar = reinterpret_cast(jar); return openJar->allCookies(); } CookieJarModel::CookieJarModel(QObject *parent) : QAbstractTableModel(parent) , m_cookieJar(nullptr) { } CookieJarModel::~CookieJarModel() = default; void CookieJarModel::setCookieJar(QNetworkCookieJar *cookieJar) { if (m_cookieJar == cookieJar) return; beginResetModel(); m_cookieJar = cookieJar; if (cookieJar) m_cookies = CookieMonster::pillageJar(cookieJar); else m_cookies.clear(); endResetModel(); } int CookieJarModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); return 8; } int CookieJarModel::rowCount(const QModelIndex &parent) const { if (parent.isValid() || !m_cookieJar) return 0; return m_cookies.size(); } QVariant CookieJarModel::data(const QModelIndex &index, int role) const { if (!index.isValid() || !m_cookieJar) return QVariant(); if (role == Qt::DisplayRole) { const auto &cookie = m_cookies.at(index.row()); switch (index.column()) { case 0: return cookie.name(); case 1: return cookie.domain(); case 2: return cookie.path(); case 3: return cookie.value(); case 4: return cookie.expirationDate(); } } else if (role == Qt::CheckStateRole) { const auto &cookie = m_cookies.at(index.row()); switch (index.column()) { case 5: return cookie.isHttpOnly() ? Qt::Checked : Qt::Unchecked; case 6: return cookie.isSecure() ? Qt::Checked : Qt::Unchecked; case 7: return cookie.isSessionCookie() ? Qt::Checked : Qt::Unchecked; } } return QVariant(); } QVariant CookieJarModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { switch (section) { case 0: return tr("Name"); case 1: return tr("Domain"); case 2: return tr("Path"); case 3: return tr("Value"); case 4: return tr("Expiration Date"); case 5: return tr("Http Only"); case 6: return tr("Secure"); case 7: return tr("Session Cookie"); } } return QAbstractTableModel::headerData(section, orientation, role); } gammaray-2.11.3/plugins/network/cookies/cookiejarmodel.h000066400000000000000000000037111413310443000233350ustar00rootroot00000000000000/* cookiejarmodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_COOKIEJARMODEL_H #define GAMMARAY_COOKIEJARMODEL_H #include #include #include QT_BEGIN_NAMESPACE class QNetworkCookieJar; QT_END_NAMESPACE namespace GammaRay { class CookieJarModel : public QAbstractTableModel { Q_OBJECT public: explicit CookieJarModel(QObject *parent = nullptr); ~CookieJarModel() override; void setCookieJar(QNetworkCookieJar *cookieJar); int columnCount(const QModelIndex &parent) const override; int rowCount(const QModelIndex &parent) const override; QVariant data(const QModelIndex &index, int role) const override; QVariant headerData(int section, Qt::Orientation orientation, int role) const override; private: QNetworkCookieJar *m_cookieJar; QList m_cookies; }; } #endif // GAMMARAY_COOKIEJARMODEL_H gammaray-2.11.3/plugins/network/cookies/cookietab.cpp000066400000000000000000000031211413310443000226340ustar00rootroot00000000000000/* cookietab.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "cookietab.h" #include "ui_cookietab.h" #include #include using namespace GammaRay; CookieTab::CookieTab(GammaRay::PropertyWidget *parent) : QWidget(parent) , ui(new Ui::CookieTab) { ui->setupUi(this); ui->cookieJarView->setModel(ObjectBroker::model(parent->objectBaseName() + QStringLiteral(".cookieJarModel"))); } CookieTab::~CookieTab() = default; gammaray-2.11.3/plugins/network/cookies/cookietab.h000066400000000000000000000030111413310443000222770ustar00rootroot00000000000000/* cookietab.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_COOKIETAB_H #define GAMMARAY_COOKIETAB_H #include #include namespace GammaRay { class PropertyWidget; namespace Ui { class CookieTab; } class CookieTab : public QWidget { Q_OBJECT public: explicit CookieTab(PropertyWidget *parent = nullptr); ~CookieTab() override; private: QScopedPointer ui; }; } #endif // GAMMARAY_COOKIETAB_H gammaray-2.11.3/plugins/network/cookies/cookietab.ui000066400000000000000000000012071413310443000224720ustar00rootroot00000000000000 GammaRay::CookieTab 0 0 400 300 false true gammaray-2.11.3/plugins/network/gammaray_network.json000066400000000000000000000002211413310443000227740ustar00rootroot00000000000000{ "hidden": false, "id": "gammaray_network", "name": "Network", "name[de]": "Netzwerk", "types": [ "QObject" ] } gammaray-2.11.3/plugins/network/networkconfigurationmodel.cpp000066400000000000000000000135531413310443000245540ustar00rootroot00000000000000/* networkconfigurationmodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "networkconfigurationmodel.h" #include #include #include using namespace GammaRay; NetworkConfigurationModel::NetworkConfigurationModel(QObject *parent) : QAbstractTableModel(parent) , m_mgr(nullptr) { } NetworkConfigurationModel::~NetworkConfigurationModel() = default; int NetworkConfigurationModel::columnCount(const QModelIndex& parent) const { Q_UNUSED(parent); return 8; } int NetworkConfigurationModel::rowCount(const QModelIndex& parent) const { // delayed lazy loading, not just for performance, creating QNetworkConfigurationManager // has tons of side-effects such as D-Bus communication on Linux, that will possibly // fail when holding the Probe::objectLock if (!m_mgr) { QTimer::singleShot(0, this, &NetworkConfigurationModel::init); return 0; } if (parent.isValid()) return 0; return m_configs.size(); } Qt::ItemFlags NetworkConfigurationModel::flags(const QModelIndex& index) const { const auto baseFlags = QAbstractTableModel::flags(index); #if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0) if (index.column() == 3) return baseFlags | Qt::ItemIsEditable; #endif return baseFlags; } QVariant NetworkConfigurationModel::data(const QModelIndex& index, int role) const { if (!m_mgr || !index.isValid()) return QVariant(); const auto conf = m_configs.at(index.row()); if (role == Qt::DisplayRole) { switch (index.column()) { case 0: return conf.name(); case 1: return conf.identifier(); case 2: return conf.bearerTypeName(); case 3: #if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0) return conf.connectTimeout(); #else return QStringLiteral("?"); #endif case 4: return conf.isRoamingAvailable(); case 5: return VariantHandler::displayString(QVariant::fromValue(conf.purpose())); case 6: return VariantHandler::displayString(QVariant::fromValue(conf.state())); case 7: return VariantHandler::displayString(QVariant::fromValue(conf.type())); } } else if (role == Qt::EditRole && index.column() == 3) { #if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0) return conf.connectTimeout(); #endif } else if (role == NetworkConfigurationModelRoles::DefaultConfigRole && index.column() == 0) { if (conf == m_mgr->defaultConfiguration()) return true; } return QVariant(); } bool NetworkConfigurationModel::setData(const QModelIndex& index, const QVariant& value, int role) { if (!m_mgr || !index.isValid() || role != Qt::EditRole || index.column() != 3 || value.isNull()) return false; #if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0) auto conf = m_configs[index.row()]; conf.setConnectTimeout(value.toInt()); #endif emit dataChanged(index, index); return true; } void NetworkConfigurationModel::init() { if (m_mgr) return; beginResetModel(); m_mgr = new QNetworkConfigurationManager(this); const auto configs = m_mgr->allConfigurations(); m_configs.reserve(configs.size()); std::copy(configs.begin(), configs.end(), std::back_inserter(m_configs)); connect(m_mgr, &QNetworkConfigurationManager::configurationAdded, this, &NetworkConfigurationModel::configurationAdded); connect(m_mgr, &QNetworkConfigurationManager::configurationChanged, this, &NetworkConfigurationModel::configurationChanged); connect(m_mgr, &QNetworkConfigurationManager::configurationRemoved, this, &NetworkConfigurationModel::configurationRemoved); endResetModel(); } void NetworkConfigurationModel::configurationAdded(const QNetworkConfiguration& config) { const auto it = std::find(m_configs.begin(), m_configs.end(), config); if (it != m_configs.end()) return; beginInsertRows(QModelIndex(), m_configs.size(), m_configs.size()); m_configs.push_back(config); endInsertRows(); } void NetworkConfigurationModel::configurationChanged(const QNetworkConfiguration& config) { const auto it = std::find(m_configs.begin(), m_configs.end(), config); if (it == m_configs.end()) return; const auto idx = std::distance(m_configs.begin(), it); emit dataChanged(index(idx, 0), index(idx, columnCount() - 1)); } void NetworkConfigurationModel::configurationRemoved(const QNetworkConfiguration& config) { const auto it = std::find(m_configs.begin(), m_configs.end(), config); if (it == m_configs.end()) return; const auto idx = std::distance(m_configs.begin(), it); beginRemoveRows(QModelIndex(), idx, idx); m_configs.erase(it); endRemoveRows(); } gammaray-2.11.3/plugins/network/networkconfigurationmodel.h000066400000000000000000000050531413310443000242150ustar00rootroot00000000000000/* networkconfigurationmodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_NETWORKCONFIGURATIONMODEL_H #define GAMMARAY_NETWORKCONFIGURATIONMODEL_H #include "networkconfigurationmodelroles.h" #include #include #include QT_BEGIN_NAMESPACE class QNetworkConfigurationManager; QT_END_NAMESPACE Q_DECLARE_METATYPE(QNetworkConfiguration::Purpose) Q_DECLARE_METATYPE(QNetworkConfiguration::StateFlags) Q_DECLARE_METATYPE(QNetworkConfiguration::Type) namespace GammaRay { class NetworkConfigurationModel : public QAbstractTableModel { Q_OBJECT public: explicit NetworkConfigurationModel(QObject *parent = nullptr); ~NetworkConfigurationModel() override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; Qt::ItemFlags flags(const QModelIndex & index) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; bool setData(const QModelIndex & index, const QVariant & value, int role) override; private slots: void init(); private: void configurationAdded(const QNetworkConfiguration &config); void configurationChanged(const QNetworkConfiguration &config); void configurationRemoved(const QNetworkConfiguration &config); QNetworkConfigurationManager *m_mgr; std::vector m_configs; }; } #endif // GAMMARAY_NETWORKCONFIGURATIONMODEL_H gammaray-2.11.3/plugins/network/networkconfigurationmodelroles.h000066400000000000000000000026771413310443000252730ustar00rootroot00000000000000/* networkconfigurationmodelroles.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_NETWORKCONFIGURATIONMODELROLES_H #define GAMMARAY_NETWORKCONFIGURATIONMODELROLES_H #include namespace GammaRay { namespace NetworkConfigurationModelRoles { enum Role { DefaultConfigRole = UserRole }; } } #endif // GAMMARAY_NETWORKCONFIGURATIONMODELROLES_H gammaray-2.11.3/plugins/network/networkconfigurationwidget.cpp000066400000000000000000000037271413310443000247410ustar00rootroot00000000000000/* networkconfigurationwidget.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "networkconfigurationwidget.h" #include "ui_networkconfigurationwidget.h" #include "clientnetworkconfigurationmodel.h" #include #include using namespace GammaRay; NetworkConfigurationWidget::NetworkConfigurationWidget(QWidget *parent) : QWidget(parent) , ui(new Ui::NetworkConfigurationWidget) { ui->setupUi(this); auto srcModel = ObjectBroker::model(QStringLiteral("com.kdab.GammaRay.NetworkConfigurationModel")); auto model = new ClientNetworkConfigurationModel(this); model->setSourceModel(srcModel); ui->networkConfigView->setModel(model); ui->networkConfigView->header()->setSectionResizeMode(QHeaderView::ResizeToContents); new SearchLineController(ui->searchLine, model); } NetworkConfigurationWidget::~NetworkConfigurationWidget() = default; gammaray-2.11.3/plugins/network/networkconfigurationwidget.h000066400000000000000000000031771413310443000244050ustar00rootroot00000000000000/* networkconfigurationwidget.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_NETWORKCONFIGURATIONWIDGET_H #define GAMMARAY_NETWORKCONFIGURATIONWIDGET_H #include #include namespace GammaRay { namespace Ui { class NetworkConfigurationWidget; } class NetworkConfigurationWidget : public QWidget { Q_OBJECT public: explicit NetworkConfigurationWidget(QWidget *parent = nullptr); ~NetworkConfigurationWidget() override; private: std::unique_ptr ui; }; } #endif // GAMMARAY_NETWORKCONFIGURATIONWIDGET_H gammaray-2.11.3/plugins/network/networkconfigurationwidget.ui000066400000000000000000000020161413310443000245620ustar00rootroot00000000000000 GammaRay::NetworkConfigurationWidget 0 0 400 300 0 0 0 0 false true gammaray-2.11.3/plugins/network/networkinterfacemodel.cpp000066400000000000000000000101741413310443000236410ustar00rootroot00000000000000/* networkinterfacemodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "networkinterfacemodel.h" #include #include #include using namespace GammaRay; #define F(x) { QNetworkInterface:: x, #x } static const MetaEnum::Value interface_flag_table[] = { F(IsUp), F(IsRunning), F(CanBroadcast), F(IsLoopBack), F(IsPointToPoint), F(CanMulticast) }; #undef F static const quintptr IFACE_ID = std::numeric_limits::max(); NetworkInterfaceModel::NetworkInterfaceModel(QObject *parent) : QAbstractItemModel(parent) { m_interfaces = QNetworkInterface::allInterfaces(); } NetworkInterfaceModel::~NetworkInterfaceModel() = default; int NetworkInterfaceModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); return 3; } int NetworkInterfaceModel::rowCount(const QModelIndex &parent) const { if (parent.isValid()) { if (parent.internalId() == IFACE_ID) return m_interfaces.at(parent.row()).addressEntries().size(); return 0; } return m_interfaces.size(); } QVariant NetworkInterfaceModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); if (role == Qt::DisplayRole) { if (index.internalId() == IFACE_ID) { const auto &iface = m_interfaces.at(index.row()); switch (index.column()) { case 0: if (iface.name() == iface.humanReadableName()) return iface.name(); return QStringLiteral("%1 (%2)").arg(iface.name(), iface.humanReadableName()); case 1: return iface.hardwareAddress(); case 2: return MetaEnum::flagsToString(iface.flags(), interface_flag_table); } } else if (index.column() == 0) { const auto &iface = m_interfaces.at(index.internalId()); const auto &addr = iface.addressEntries().at(index.row()); return QString(addr.ip().toString() + QLatin1Char('/') + addr.netmask().toString()); } } return QVariant(); } QVariant NetworkInterfaceModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { switch (section) { case 0: return tr("Interface"); case 1: return tr("Hardware Address"); case 2: return tr("Flags"); } } return QAbstractItemModel::headerData(section, orientation, role); } QModelIndex NetworkInterfaceModel::parent(const QModelIndex &child) const { if (!child.isValid() || child.internalId() == IFACE_ID) return {}; return createIndex(child.internalId(), 0, IFACE_ID); } QModelIndex NetworkInterfaceModel::index(int row, int column, const QModelIndex &parent) const { if (!parent.isValid()) return createIndex(row, column, IFACE_ID); if (parent.internalId() == IFACE_ID) return createIndex(row, column, parent.row()); return {}; } gammaray-2.11.3/plugins/network/networkinterfacemodel.h000066400000000000000000000042431413310443000233060ustar00rootroot00000000000000/* networkinterfacemodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_NETWORKINTERFACEMODEL_H #define GAMMARAY_NETWORKINTERFACEMODEL_H #include #include QT_BEGIN_NAMESPACE class QNetworkInterface; QT_END_NAMESPACE namespace GammaRay { class NetworkInterfaceModel : public QAbstractItemModel { Q_OBJECT public: explicit NetworkInterfaceModel(QObject *parent = nullptr); ~NetworkInterfaceModel() override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; QModelIndex parent(const QModelIndex &child) const override; QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; private: QList m_interfaces; }; } #endif // GAMMARAY_NETWORKINTERFACEMODEL_H gammaray-2.11.3/plugins/network/networkinterfacewidget.cpp000066400000000000000000000033401413310443000240210ustar00rootroot00000000000000/* networkinterfacewidget.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "networkinterfacewidget.h" #include "ui_networkinterfacewidget.h" #include using namespace GammaRay; NetworkInterfaceWidget::NetworkInterfaceWidget(QWidget *parent) : QWidget(parent) , ui(new Ui::NetworkInterfaceWidget) { ui->setupUi(this); ui->interfaceView->setModel(ObjectBroker::model(QStringLiteral( "com.kdab.GammaRay.NetworkInterfaceModel"))); ui->interfaceView->header()->setSectionResizeMode(QHeaderView::ResizeToContents); } NetworkInterfaceWidget::~NetworkInterfaceWidget() = default; gammaray-2.11.3/plugins/network/networkinterfacewidget.h000066400000000000000000000031401413310443000234640ustar00rootroot00000000000000/* networkinterfacewidget.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_NETWORKINTERFACEWIDGET_H #define GAMMARAY_NETWORKINTERFACEWIDGET_H #include #include namespace GammaRay { namespace Ui { class NetworkInterfaceWidget; } class NetworkInterfaceWidget : public QWidget { Q_OBJECT public: explicit NetworkInterfaceWidget(QWidget *parent = nullptr); ~NetworkInterfaceWidget() override; private: QScopedPointer ui; }; } #endif // GAMMARAY_NETWORKINTERFACEWIDGET_H gammaray-2.11.3/plugins/network/networkinterfacewidget.ui000066400000000000000000000014131413310443000236530ustar00rootroot00000000000000 GammaRay::NetworkInterfaceWidget 0 0 400 300 0 0 0 0 gammaray-2.11.3/plugins/network/networkreplymodel.cpp000066400000000000000000000277571413310443000230530ustar00rootroot00000000000000/* networkreplymodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2019-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "networkreplymodel.h" #include #include #include #include #include #include using namespace GammaRay; static const auto TopIndex = std::numeric_limits::max(); Q_DECLARE_METATYPE(GammaRay::NetworkReplyModel::ReplyNode) NetworkReplyModel::NetworkReplyModel(QObject *parent) : QAbstractItemModel(parent) { m_time.start(); qRegisterMetaType(); qRegisterMetaType(); } NetworkReplyModel::~NetworkReplyModel() = default; int NetworkReplyModel::columnCount(const QModelIndex& parent) const { Q_UNUSED(parent); return NetworkReplyModelColumn::COLUMN_COUNT; } int NetworkReplyModel::rowCount(const QModelIndex& parent) const { if (!parent.isValid()) { return m_nodes.size(); } if (parent.internalId() == TopIndex) { return m_nodes[parent.row()].replies.size(); } return 0; } QVariant NetworkReplyModel::data(const QModelIndex& index, int role) const { if (!index.isValid()) { return {}; } // top-level if (index.internalId() == TopIndex) { const auto &nam = m_nodes[index.row()]; if (index.column() == NetworkReplyModelColumn::ObjectColumn && role == Qt::DisplayRole) { return nam.displayName; } else if (role == NetworkReplyModelRole::ObjectIdRole && index.column() == NetworkReplyModelColumn::ObjectColumn) { return QVariant::fromValue(ObjectId(nam.nam)); } return {}; } // reply level const auto &reply = m_nodes[index.internalId()].replies[index.row()]; if (role == Qt::DisplayRole) { switch (index.column()) { case NetworkReplyModelColumn::ObjectColumn: return reply.displayName; case NetworkReplyModelColumn::OpColumn: return reply.op; case NetworkReplyModelColumn::SizeColumn: if (reply.duration == 0 && reply.size == 0) { return {}; // cached reply, we don't have proper data for that } return reply.size; case NetworkReplyModelColumn::TimeColumn: if (reply.state & NetworkReply::Finished) { return reply.duration; } return {}; case NetworkReplyModelColumn::UrlColumn: return reply.url; } } else if (role == NetworkReplyModelRole::ReplyStateRole && index.column() == NetworkReplyModelColumn::ObjectColumn) { return reply.state; } else if (role == NetworkReplyModelRole::ReplyErrorRole && index.column() == NetworkReplyModelColumn::ObjectColumn) { return reply.errorMsgs; } else if (role == NetworkReplyModelRole::ObjectIdRole && index.column() == NetworkReplyModelColumn::ObjectColumn) { return QVariant::fromValue(ObjectId(reply.reply)); } return {}; } QModelIndex NetworkReplyModel::index(int row, int column, const QModelIndex& parent) const { // top-level if (!parent.isValid()) { return createIndex(row, column, TopIndex); } return createIndex(row, column, parent.row()); } QModelIndex NetworkReplyModel::parent(const QModelIndex& child) const { if (child.internalId() == TopIndex) { return {}; } return createIndex(child.internalId(), 0, TopIndex); } void NetworkReplyModel::objectCreated(QObject *obj) { if (auto nam = qobject_cast(obj)) { beginInsertRows({}, m_nodes.size(), m_nodes.size()); NAMNode node; node.nam = nam; node.displayName = Util::displayString(nam); m_nodes.push_back(node); endInsertRows(); connect(nam, &QNetworkAccessManager::finished, this, [this, nam](QNetworkReply *reply) { replyFinished(reply, nam); }, Qt::DirectConnection); #ifndef QT_NO_SSL connect(nam, &QNetworkAccessManager::encrypted, this, [this, nam](QNetworkReply *reply) { replyEncrypted(reply, nam); }, Qt::DirectConnection); connect(nam, &QNetworkAccessManager::sslErrors, this, [this, nam](QNetworkReply *reply, const QList &errors) { replySslErrors(reply, errors, nam); }); #endif } if (auto reply = qobject_cast(obj)) { auto nam = reply->manager(); auto namIt = std::find_if(m_nodes.begin(), m_nodes.end(), [nam](const NAMNode &node) { return node.nam == nam; }); if (namIt == m_nodes.end()) { // TODO return; } ReplyNode replyNode; replyNode.reply = reply; replyNode.displayName = Util::displayString(reply); replyNode.op = reply->operation(); replyNode.url = reply->url(); if (reply->isFinished()) { replyNode.state |= NetworkReply::Finished; replyNode.duration = 0; } else { replyNode.duration = m_time.elapsed(); } updateReplyNode(nam, replyNode); // capture nam, as we cannot deref reply anymore when this triggers connect(reply, &QNetworkReply::downloadProgress, this, [this, reply, nam](qint64 received, qint64 total) { replyProgress(reply, received, total, nam); }); connect(reply, &QNetworkReply::uploadProgress, this, [this, reply, nam](qint64 received, qint64 total) { replyProgress(reply, received, total, nam); }); connect(reply, &QNetworkReply::destroyed, this, [this, reply, nam]() { replyDeleted(reply, nam); }); } } QMap NetworkReplyModel::itemData(const QModelIndex& index) const { auto m = QAbstractItemModel::itemData(index); if (index.column() == 0) { m.insert(NetworkReplyModelRole::ReplyStateRole, data(index, NetworkReplyModelRole::ReplyStateRole)); m.insert(NetworkReplyModelRole::ReplyErrorRole, data(index, NetworkReplyModelRole::ReplyErrorRole)); m.insert(NetworkReplyModelRole::ObjectIdRole, data(index, NetworkReplyModelRole::ObjectIdRole)); } return m; } void NetworkReplyModel::replyFinished(QNetworkReply* reply, QNetworkAccessManager *nam) { /// WARNING this runs in the thread of the reply, not the thread of this! ReplyNode node; node.reply = reply; node.displayName = Util::displayString(reply); node.url = reply->url(); node.op = reply->operation(); node.state |= NetworkReply::Finished; node.duration = m_time.elapsed() - node.duration; if (reply->error() != QNetworkReply::NoError) { node.state |= NetworkReply::Error; node.errorMsgs.push_back(reply->errorString()); } QMetaObject::invokeMethod(this, "updateReplyNode", Qt::AutoConnection, Q_ARG(QNetworkAccessManager*, nam), Q_ARG(GammaRay::NetworkReplyModel::ReplyNode, node)); if (reply->thread() != thread()) { connect(reply, &QNetworkReply::destroyed, this, [this, reply, nam]() { replyDeleted(reply, nam); }, Qt::DirectConnection); } } void NetworkReplyModel::replyProgress(QNetworkReply* reply, qint64 progress, qint64 total, QNetworkAccessManager *nam) { ReplyNode node; node.reply = reply; node.size = std::max(progress, total); updateReplyNode(nam, node); } #ifndef QT_NO_SSL void NetworkReplyModel::replyEncrypted(QNetworkReply* reply, QNetworkAccessManager *nam) { /// WARNING this runs in the thread of the reply, not the thread of this! ReplyNode node; node.reply = reply; node.displayName = Util::displayString(reply); node.url = reply->url(); node.op = reply->operation(); node.state |= NetworkReply::Encrypted; QMetaObject::invokeMethod(this, "updateReplyNode", Qt::AutoConnection, Q_ARG(QNetworkAccessManager*, nam), Q_ARG(GammaRay::NetworkReplyModel::ReplyNode, node)); } void NetworkReplyModel::replySslErrors(QNetworkReply* reply, const QList& errors, QNetworkAccessManager *nam) { /// WARNING this runs in the thread of the reply, not the thread of this! ReplyNode node; node.reply = reply; node.displayName = Util::displayString(reply); node.url = reply->url(); node.op = reply->operation(); node.state |= NetworkReply::Error | NetworkReply::Unencrypted; for (const auto &err : errors) { node.errorMsgs.push_back(err.errorString()); } QMetaObject::invokeMethod(this, "updateReplyNode", Qt::AutoConnection, Q_ARG(QNetworkAccessManager*, nam), Q_ARG(GammaRay::NetworkReplyModel::ReplyNode, node)); } #endif void NetworkReplyModel::replyDeleted(QNetworkReply* reply, QNetworkAccessManager* nam) { /// WARNING this runs in the thread of the reply, not the thread of this! ReplyNode node; node.reply = reply; node.state |= NetworkReply::Deleted; QMetaObject::invokeMethod(this, "updateReplyNode", Qt::AutoConnection, Q_ARG(QNetworkAccessManager*, nam), Q_ARG(GammaRay::NetworkReplyModel::ReplyNode, node)); } void NetworkReplyModel::updateReplyNode(QNetworkAccessManager* nam, const NetworkReplyModel::ReplyNode& newNode) { // WARNING reply is no longer safe to deref here! const auto namIt = std::find_if(m_nodes.begin(), m_nodes.end(), [nam](const NAMNode &node) { return node.nam == nam; }); if (namIt == m_nodes.end()) { return; } auto replyIt = std::find_if((*namIt).replies.rbegin(), (*namIt).replies.rend(), [newNode](const ReplyNode &node) { // be careful with considering past deleted nodes, address can be reused return node.reply == newNode.reply && ((node.state & NetworkReply::Deleted) == 0 || newNode.state & NetworkReply::Deleted); }); if (replyIt == (*namIt).replies.rend()) { const auto parentIdx = createIndex(std::distance(m_nodes.begin(), namIt), 0, TopIndex); beginInsertRows(parentIdx, (*namIt).replies.size(), (*namIt).replies.size()); ReplyNode replyNode = newNode; if (replyNode.displayName.isEmpty()) { replyNode.displayName = Util::addressToString(newNode.reply); } (*namIt).replies.push_back(replyNode); endInsertRows(); replyIt = (*namIt).replies.rbegin(); } else { if (!newNode.displayName.isEmpty()) { (*replyIt).displayName = newNode.displayName; } (*replyIt).state |= newNode.state; if ((*replyIt).state & NetworkReply::Unencrypted) { (*replyIt).state &= ~ NetworkReply::Encrypted; } if (!newNode.url.isEmpty()) { (*replyIt).url = newNode.url; (*replyIt).op = newNode.op; } (*replyIt).errorMsgs += newNode.errorMsgs; if ((*replyIt).duration > 0 && newNode.duration > 0 && (newNode.state & NetworkReply::Finished)) { (*replyIt).duration = newNode.duration > (*replyIt).duration ? newNode.duration - (*replyIt).duration : 0; } (*replyIt).size = std::max((*replyIt).size, newNode.size); const auto idx = createIndex(std::distance(replyIt, (*namIt).replies.rend()) - 1, 0, std::distance(m_nodes.begin(), namIt)); emit dataChanged(idx, idx.sibling(idx.row(), columnCount() - 1)); } } gammaray-2.11.3/plugins/network/networkreplymodel.h000066400000000000000000000062401413310443000225000ustar00rootroot00000000000000/* networkreplymodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2019-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_NETWORKREPLYMODEL_H #define GAMMARAY_NETWORKREPLYMODEL_H #include "networkreplymodeldefs.h" #include #include #include #include #include QT_BEGIN_NAMESPACE class QNetworkReply; QT_END_NAMESPACE namespace GammaRay { /** QNetworkReply tracking. */ class NetworkReplyModel : public QAbstractItemModel { Q_OBJECT public: explicit NetworkReplyModel(QObject *parent = nullptr); ~NetworkReplyModel(); int columnCount(const QModelIndex &parent = {}) const override; int rowCount(const QModelIndex &parent) const override; QVariant data(const QModelIndex &index, int role) const override; QModelIndex index(int row, int column, const QModelIndex &parent) const override; QModelIndex parent(const QModelIndex &child) const override; QMap itemData(const QModelIndex & index) const override; void objectCreated(QObject *obj); struct ReplyNode { QNetworkReply *reply; QString displayName; QUrl url; QStringList errorMsgs; qint64 size = 0; quint64 duration = 0; QNetworkAccessManager::Operation op; int state = NetworkReply::Running; }; private: struct NAMNode { QNetworkAccessManager *nam; QString displayName; std::vector replies; }; void replyFinished(QNetworkReply *reply, QNetworkAccessManager *nam); void replyProgress(QNetworkReply *reply, qint64 progress, qint64 total, QNetworkAccessManager *nam); #ifndef QT_NO_SSL void replyEncrypted(QNetworkReply *reply, QNetworkAccessManager *nam); void replySslErrors(QNetworkReply *reply, const QList &errors, QNetworkAccessManager *nam); #endif void replyDeleted(QNetworkReply *reply, QNetworkAccessManager *nam); Q_INVOKABLE void updateReplyNode(QNetworkAccessManager *nam, const GammaRay::NetworkReplyModel::ReplyNode &newNode); std::vector m_nodes; QElapsedTimer m_time; }; } #endif // GAMMARAY_NETWORKREPLYMODEL_H gammaray-2.11.3/plugins/network/networkreplymodeldefs.h000066400000000000000000000035471413310443000233510ustar00rootroot00000000000000/* networkreplymodeldefs.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2019-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_NETWORKREPLYMODELDEFS_H #define GAMMARAY_NETWORKREPLYMODELDEFS_H #include namespace GammaRay { namespace NetworkReply { enum State { Running = 0, Error = 1, Finished = 2, Encrypted = 4, // explicitly known to be encrypted Unencrypted = 8, // explicitly known to not be encrypted, if none are set, we guess from URL Deleted = 16 }; } namespace NetworkReplyModelRole { enum Role { ReplyStateRole = GammaRay::UserRole, ReplyErrorRole, ObjectIdRole }; } namespace NetworkReplyModelColumn { enum Column { ObjectColumn = 0, OpColumn = 1, TimeColumn = 2, SizeColumn = 3, UrlColumn = 4, COLUMN_COUNT }; } } #endif // GAMMARAY_NETWORKREPLYMODELDEFS_H gammaray-2.11.3/plugins/network/networkreplywidget.cpp000066400000000000000000000062111413310443000232140ustar00rootroot00000000000000/* networkreplywidget.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2019-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "networkreplywidget.h" #include "clientnetworkreplymodel.h" #include "networkreplymodeldefs.h" #include "ui_networkreplywidget.h" #include #include #include #include #include using namespace GammaRay; NetworkReplyWidget::NetworkReplyWidget(QWidget* parent) : QWidget(parent) , ui(new Ui::NetworkReplyWidget) { ui->setupUi(this); auto srcModel = ObjectBroker::model(QStringLiteral("com.kdab.GammaRay.NetworkReplyModel")); auto proxy = new ClientNetworkReplyModel(this); proxy->setSourceModel(srcModel); ui->replyView->setModel(proxy); ui->replyView->header()->setSectionResizeMode(QHeaderView::ResizeToContents); ui->replyView->expandAll(); // auto-expand parents of new replies connect(proxy, &QAbstractItemModel::rowsInserted, this, [this](const QModelIndex &parent, int, int) { if (!parent.isValid()) { return; } ui->replyView->expand(parent); }); connect(ui->replyView, &QWidget::customContextMenuRequested, this, &NetworkReplyWidget::contextMenu); } NetworkReplyWidget::~NetworkReplyWidget() = default; void NetworkReplyWidget::contextMenu(QPoint pos) { const auto index = ui->replyView->indexAt(pos); if (!index.isValid()) return; const auto objectId = index.sibling(index.row(), NetworkReplyModelColumn::ObjectColumn).data(NetworkReplyModelRole::ObjectIdRole).value(); const auto url = index.sibling(index.row(), NetworkReplyModelColumn::UrlColumn).data(Qt::DisplayRole).toString(); QMenu menu; if (!url.isEmpty()) { auto action = menu.addAction(QIcon::fromTheme(QStringLiteral("edit-copy")), tr("Copy URL")); connect(action, &QAction::triggered, this, [url]() { QGuiApplication::clipboard()->setText(url); }); menu.addSeparator(); } ContextMenuExtension ext(objectId); ext.populateMenu(&menu); menu.exec(ui->replyView->viewport()->mapToGlobal(pos)); } gammaray-2.11.3/plugins/network/networkreplywidget.h000066400000000000000000000031221413310443000226570ustar00rootroot00000000000000/* networkreplywidget.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2019-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_NETWORKREPLYWIDGET_H #define GAMMARAY_NETWORKREPLYWIDGET_H #include #include namespace GammaRay { namespace Ui { class NetworkReplyWidget; } class NetworkReplyWidget : public QWidget { Q_OBJECT public: explicit NetworkReplyWidget(QWidget *parent = nullptr); ~NetworkReplyWidget(); private: void contextMenu(QPoint pos); std::unique_ptr ui; }; } #endif // GAMMARAY_NETWORKREPLYWIDGET_H gammaray-2.11.3/plugins/network/networkreplywidget.ui000066400000000000000000000012471413310443000230530ustar00rootroot00000000000000 GammaRay::NetworkReplyWidget 0 0 400 300 Qt::CustomContextMenu true gammaray-2.11.3/plugins/network/networksupport.cpp000066400000000000000000000563021413310443000223770ustar00rootroot00000000000000/* networksupport.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "networksupport.h" #include "networkinterfacemodel.h" #include "networkconfigurationmodel.h" #include "networkreplymodel.h" #include "cookies/cookieextension.h" #include #include #include #include #include #include #include #include #include #if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0) #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace GammaRay; Q_DECLARE_METATYPE(QAbstractSocket::PauseModes) Q_DECLARE_METATYPE(QHostAddress) #if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0) Q_DECLARE_METATYPE(QHstsPolicy) #endif Q_DECLARE_METATYPE(QLocalSocket::LocalSocketError) Q_DECLARE_METATYPE(QLocalSocket::LocalSocketState) Q_DECLARE_METATYPE(QNetworkAccessManager::NetworkAccessibility) #if QT_VERSION < QT_VERSION_CHECK(5, 6, 0) Q_DECLARE_METATYPE(QNetworkAddressEntry) #endif #if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0) Q_DECLARE_METATYPE(QNetworkAddressEntry::DnsEligibilityStatus) #endif Q_DECLARE_METATYPE(QNetworkConfiguration::BearerType) Q_DECLARE_METATYPE(QNetworkConfigurationManager::Capabilities) #if QT_VERSION < QT_VERSION_CHECK(5, 6, 0) Q_DECLARE_METATYPE(QNetworkInterface) #endif #if QT_VERSION < QT_VERSION_CHECK(5, 11, 0) Q_DECLARE_METATYPE(QNetworkInterface::InterfaceFlags) #endif Q_DECLARE_METATYPE(QNetworkProxy::Capabilities) Q_DECLARE_METATYPE(QNetworkProxy::ProxyType) #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) Q_DECLARE_METATYPE(QSocketNotifier::Type) #endif #ifndef QT_NO_SSL Q_DECLARE_METATYPE(QSsl::KeyAlgorithm) Q_DECLARE_METATYPE(QSsl::KeyType) Q_DECLARE_METATYPE(QSsl::SslProtocol) Q_DECLARE_METATYPE(QSslCertificateExtension) Q_DECLARE_METATYPE(QSslCipher) Q_DECLARE_METATYPE(QSslConfiguration::NextProtocolNegotiationStatus) Q_DECLARE_METATYPE(QSslError) Q_DECLARE_METATYPE(QSslKey) Q_DECLARE_METATYPE(QSslSocket::PeerVerifyMode) Q_DECLARE_METATYPE(QSslSocket::SslMode) #endif // QT_NO_SSL NetworkSupport::NetworkSupport(Probe *probe, QObject *parent) : QObject(parent) { registerMetaTypes(); registerVariantHandler(); probe->registerModel(QStringLiteral("com.kdab.GammaRay.NetworkInterfaceModel"), new NetworkInterfaceModel(this)); auto configProxy = new ServerProxyModel(this); configProxy->setSourceModel(new NetworkConfigurationModel(this)); configProxy->addRole(NetworkConfigurationModelRoles::DefaultConfigRole); probe->registerModel(QStringLiteral("com.kdab.GammaRay.NetworkConfigurationModel"), configProxy); auto replyModel = new NetworkReplyModel(this); connect(probe, &Probe::objectCreated, replyModel, &NetworkReplyModel::objectCreated); probe->registerModel(QStringLiteral("com.kdab.GammaRay.NetworkReplyModel"), replyModel); PropertyController::registerExtension(); } NetworkSupport::~NetworkSupport() = default; void NetworkSupport::registerMetaTypes() { MetaObject *mo = nullptr; MO_ADD_METAOBJECT1(QAbstractSocket, QIODevice); MO_ADD_PROPERTY_RO(QAbstractSocket, isValid); MO_ADD_PROPERTY_RO(QAbstractSocket, localPort); MO_ADD_PROPERTY_RO(QAbstractSocket, localAddress); MO_ADD_PROPERTY_RO(QAbstractSocket, peerPort); MO_ADD_PROPERTY_RO(QAbstractSocket, peerAddress); MO_ADD_PROPERTY_RO(QAbstractSocket, peerName); MO_ADD_PROPERTY(QAbstractSocket, readBufferSize, setReadBufferSize); MO_ADD_PROPERTY(QAbstractSocket, pauseMode, setPauseMode); MO_ADD_PROPERTY_RO(QAbstractSocket, socketDescriptor); MO_ADD_PROPERTY_RO(QAbstractSocket, socketType); MO_ADD_PROPERTY_RO(QAbstractSocket, state); MO_ADD_PROPERTY_RO(QAbstractSocket, error); #ifndef QT_NO_NETWORKPROXY MO_ADD_PROPERTY_RO(QAbstractSocket, proxy); #endif // FIXME: QAbstractSocket::setSocketOption() would be nice to have // FIXME: QQAbstractSocket::socketOption() would be nice to have MO_ADD_METAOBJECT0(QHostAddress); MO_ADD_PROPERTY_RO(QHostAddress, isLoopback); #if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0) MO_ADD_PROPERTY_RO(QHostAddress, isMulticast); #endif MO_ADD_PROPERTY_RO(QHostAddress, isNull); MO_ADD_PROPERTY_RO(QHostAddress, protocol); MO_ADD_PROPERTY(QHostAddress, scopeId, setScopeId); #if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0) MO_ADD_METAOBJECT0(QHstsPolicy); MO_ADD_PROPERTY (QHstsPolicy, expiry, setExpiry); MO_ADD_PROPERTY_LD(QHstsPolicy, host, [](QHstsPolicy *policy) { return policy->host(); }); MO_ADD_PROPERTY (QHstsPolicy, includesSubDomains, setIncludesSubDomains); #endif MO_ADD_METAOBJECT1(QLocalSocket, QIODevice); MO_ADD_PROPERTY_RO(QLocalSocket, error); MO_ADD_PROPERTY_RO(QLocalSocket, fullServerName); MO_ADD_PROPERTY_RO(QLocalSocket, isValid); MO_ADD_PROPERTY_RO(QLocalSocket, serverName); MO_ADD_PROPERTY_RO(QLocalSocket, socketDescriptor); MO_ADD_PROPERTY_RO(QLocalSocket, state); MO_ADD_METAOBJECT1(QNetworkAccessManager, QObject); MO_ADD_PROPERTY_RO(QNetworkAccessManager, activeConfiguration); MO_ADD_PROPERTY_RO(QNetworkAccessManager, cache); MO_ADD_PROPERTY (QNetworkAccessManager, configuration, setConfiguration); MO_ADD_PROPERTY_RO(QNetworkAccessManager, cookieJar); #if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0) MO_ADD_PROPERTY (QNetworkAccessManager, isStrictTransportSecurityEnabled, setStrictTransportSecurityEnabled); #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) MO_ADD_PROPERTY_RO(QNetworkAccessManager, isStrictTransportSecurityStoreEnabled); #endif MO_ADD_PROPERTY (QNetworkAccessManager, proxy, setProxy); MO_ADD_PROPERTY (QNetworkAccessManager, redirectPolicy, setRedirectPolicy); MO_ADD_PROPERTY_RO(QNetworkAccessManager, strictTransportSecurityHosts); #endif MO_ADD_PROPERTY_RO(QNetworkAccessManager, supportedSchemes); #if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0) MO_ADD_METAOBJECT0(QNetworkAddressEntry); MO_ADD_PROPERTY (QNetworkAddressEntry, broadcast, setBroadcast); MO_ADD_PROPERTY (QNetworkAddressEntry, dnsEligibility, setDnsEligibility); MO_ADD_PROPERTY (QNetworkAddressEntry, ip, setIp); MO_ADD_PROPERTY_RO(QNetworkAddressEntry, isLifetimeKnown); MO_ADD_PROPERTY_RO(QNetworkAddressEntry, isPermanent); MO_ADD_PROPERTY_RO(QNetworkAddressEntry, isTemporary); MO_ADD_PROPERTY (QNetworkAddressEntry, netmask, setNetmask); //MO_ADD_PROPERTY_RO(QNetworkAddressEntry, preferredLifetime); MO_ADD_PROPERTY (QNetworkAddressEntry, prefixLength, setPrefixLength); //MO_ADD_PROPERTY_RO(QNetworkAddressEntry, validityLifetime); #endif MO_ADD_METAOBJECT0(QNetworkConfiguration); MO_ADD_PROPERTY_RO(QNetworkConfiguration, bearerType); MO_ADD_PROPERTY_RO(QNetworkConfiguration, bearerTypeFamily); MO_ADD_PROPERTY_RO(QNetworkConfiguration, bearerTypeName); MO_ADD_PROPERTY_RO(QNetworkConfiguration, children); #if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0) MO_ADD_PROPERTY_RO(QNetworkConfiguration, connectTimeout); #endif MO_ADD_PROPERTY_RO(QNetworkConfiguration, identifier); MO_ADD_PROPERTY_RO(QNetworkConfiguration, isRoamingAvailable); MO_ADD_PROPERTY_RO(QNetworkConfiguration, isValid); MO_ADD_PROPERTY_RO(QNetworkConfiguration, name); MO_ADD_PROPERTY_RO(QNetworkConfiguration, purpose); MO_ADD_PROPERTY_RO(QNetworkConfiguration, state); MO_ADD_PROPERTY_RO(QNetworkConfiguration, type); MO_ADD_METAOBJECT1(QNetworkConfigurationManager, QObject); MO_ADD_PROPERTY_RO(QNetworkConfigurationManager, capabilities); MO_ADD_PROPERTY_RO(QNetworkConfigurationManager, isOnline); MO_ADD_METAOBJECT0(QNetworkInterface); MO_ADD_PROPERTY_RO(QNetworkInterface, addressEntries); MO_ADD_PROPERTY_ST(QNetworkInterface, allAddresses); MO_ADD_PROPERTY_ST(QNetworkInterface, allInterfaces); MO_ADD_PROPERTY_RO(QNetworkInterface, flags); MO_ADD_PROPERTY_RO(QNetworkInterface, hardwareAddress); MO_ADD_PROPERTY_RO(QNetworkInterface, index); MO_ADD_PROPERTY_RO(QNetworkInterface, isValid); #if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0) MO_ADD_PROPERTY_RO(QNetworkInterface, maximumTransmissionUnit); #endif MO_ADD_PROPERTY_RO(QNetworkInterface, name); #if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0) MO_ADD_PROPERTY_RO(QNetworkInterface, type); #endif MO_ADD_METAOBJECT0(QNetworkProxy); MO_ADD_PROPERTY_ST(QNetworkProxy, applicationProxy); MO_ADD_PROPERTY (QNetworkProxy, capabilities, setCapabilities); MO_ADD_PROPERTY (QNetworkProxy, hostName, setHostName); MO_ADD_PROPERTY_RO(QNetworkProxy, isCachingProxy); MO_ADD_PROPERTY_RO(QNetworkProxy, isTransparentProxy); MO_ADD_PROPERTY (QNetworkProxy, password, setPassword); MO_ADD_PROPERTY (QNetworkProxy, port, setPort); //MO_ADD_PROPERTY_RO(QNetworkProxy, rawHeaderList); MO_ADD_PROPERTY (QNetworkProxy, type, setType); MO_ADD_PROPERTY (QNetworkProxy, user, setUser); MO_ADD_METAOBJECT1(QNetworkSession, QObject); MO_ADD_PROPERTY_RO(QNetworkSession, activeTime); MO_ADD_PROPERTY_RO(QNetworkSession, bytesReceived); MO_ADD_PROPERTY_RO(QNetworkSession, bytesWritten); MO_ADD_PROPERTY_RO(QNetworkSession, configuration); MO_ADD_PROPERTY_RO(QNetworkSession, error); MO_ADD_PROPERTY_RO(QNetworkSession, errorString); MO_ADD_PROPERTY_RO(QNetworkSession, interface); MO_ADD_PROPERTY_RO(QNetworkSession, isOpen); MO_ADD_PROPERTY_RO(QNetworkSession, state); MO_ADD_PROPERTY_RO(QNetworkSession, usagePolicies); MO_ADD_METAOBJECT1(QTcpServer, QObject); MO_ADD_PROPERTY_RO(QTcpServer, isListening); MO_ADD_PROPERTY(QTcpServer, maxPendingConnections, setMaxPendingConnections); MO_ADD_PROPERTY_RO(QTcpServer, serverPort); MO_ADD_PROPERTY_RO(QTcpServer, serverAddress); MO_ADD_PROPERTY_RO(QTcpServer, socketDescriptor); MO_ADD_PROPERTY_RO(QTcpServer, hasPendingConnections); MO_ADD_PROPERTY_RO(QTcpServer, serverError); MO_ADD_PROPERTY_RO(QTcpServer, errorString); #ifndef QT_NO_NETWORKPROXY MO_ADD_PROPERTY_RO(QTcpServer, proxy); #endif MO_ADD_METAOBJECT1(QTcpSocket, QAbstractSocket); #ifndef QT_NO_SSL MO_ADD_METAOBJECT0(QSslCertificate); MO_ADD_PROPERTY_RO(QSslCertificate, effectiveDate); MO_ADD_PROPERTY_RO(QSslCertificate, expiryDate); MO_ADD_PROPERTY_RO(QSslCertificate, extensions); MO_ADD_PROPERTY_RO(QSslCertificate, isBlacklisted); MO_ADD_PROPERTY_RO(QSslCertificate, isNull); MO_ADD_PROPERTY_RO(QSslCertificate, isSelfSigned); MO_ADD_PROPERTY_RO(QSslCertificate, issuerInfoAttributes); MO_ADD_PROPERTY_RO(QSslCertificate, publicKey); MO_ADD_PROPERTY_RO(QSslCertificate, serialNumber); MO_ADD_PROPERTY_RO(QSslCertificate, subjectInfoAttributes); MO_ADD_PROPERTY_RO(QSslCertificate, version); MO_ADD_METAOBJECT0(QSslCertificateExtension); MO_ADD_PROPERTY_RO(QSslCertificateExtension, isCritical); MO_ADD_PROPERTY_RO(QSslCertificateExtension, isSupported); MO_ADD_PROPERTY_RO(QSslCertificateExtension, name); MO_ADD_PROPERTY_RO(QSslCertificateExtension, oid); MO_ADD_PROPERTY_RO(QSslCertificateExtension, value); MO_ADD_METAOBJECT0(QSslCipher); MO_ADD_PROPERTY_RO(QSslCipher, authenticationMethod); MO_ADD_PROPERTY_RO(QSslCipher, encryptionMethod); MO_ADD_PROPERTY_RO(QSslCipher, isNull); MO_ADD_PROPERTY_RO(QSslCipher, keyExchangeMethod); MO_ADD_PROPERTY_RO(QSslCipher, name); MO_ADD_PROPERTY_RO(QSslCipher, protocol); MO_ADD_PROPERTY_RO(QSslCipher, protocolString); MO_ADD_PROPERTY_RO(QSslCipher, usedBits); MO_ADD_METAOBJECT0(QSslConfiguration); MO_ADD_PROPERTY(QSslConfiguration, allowedNextProtocols, setAllowedNextProtocols); MO_ADD_PROPERTY(QSslConfiguration, caCertificates, setCaCertificates); MO_ADD_PROPERTY(QSslConfiguration, ciphers, setCiphers); // TODO 5.5 ellipticCurves #if QT_VERSION >= QT_VERSION_CHECK(5, 7, 0) MO_ADD_PROPERTY_RO(QSslConfiguration, ephemeralServerKey); #endif MO_ADD_PROPERTY_RO(QSslConfiguration, isNull); MO_ADD_PROPERTY(QSslConfiguration, localCertificate, setLocalCertificate); MO_ADD_PROPERTY(QSslConfiguration, localCertificateChain, setLocalCertificateChain); MO_ADD_PROPERTY_RO(QSslConfiguration, nextNegotiatedProtocol); MO_ADD_PROPERTY_RO(QSslConfiguration, nextProtocolNegotiationStatus); // TODO enum lookup table MO_ADD_PROPERTY_RO(QSslConfiguration, peerCertificate); MO_ADD_PROPERTY_RO(QSslConfiguration, peerCertificateChain); MO_ADD_PROPERTY(QSslConfiguration, peerVerifyDepth, setPeerVerifyDepth); MO_ADD_PROPERTY(QSslConfiguration, peerVerifyMode, setPeerVerifyMode); MO_ADD_PROPERTY(QSslConfiguration, privateKey, setPrivateKey); MO_ADD_PROPERTY(QSslConfiguration, protocol, setProtocol); MO_ADD_PROPERTY_RO(QSslConfiguration, sessionCipher); MO_ADD_PROPERTY_RO(QSslConfiguration, sessionProtocol); MO_ADD_PROPERTY(QSslConfiguration, sessionTicket, setSessionTicket); MO_ADD_PROPERTY_RO(QSslConfiguration, sessionTicketLifeTimeHint); MO_ADD_PROPERTY_ST(QSslConfiguration, systemCaCertificates); MO_ADD_METAOBJECT0(QSslKey); MO_ADD_PROPERTY_RO(QSslKey, algorithm); MO_ADD_PROPERTY_RO(QSslKey, isNull); MO_ADD_PROPERTY_RO(QSslKey, length); MO_ADD_PROPERTY_RO(QSslKey, type); MO_ADD_METAOBJECT1(QSslSocket, QTcpSocket); MO_ADD_PROPERTY_RO(QSslSocket, isEncrypted); MO_ADD_PROPERTY_RO(QSslSocket, localCertificate); MO_ADD_PROPERTY_RO(QSslSocket, localCertificateChain); MO_ADD_PROPERTY_RO(QSslSocket, mode); MO_ADD_PROPERTY_RO(QSslSocket, peerCertificate); MO_ADD_PROPERTY_RO(QSslSocket, peerCertificateChain); MO_ADD_PROPERTY(QSslSocket, peerVerifyDepth, setPeerVerifyDepth); MO_ADD_PROPERTY(QSslSocket, peerVerifyMode, setPeerVerifyMode); MO_ADD_PROPERTY(QSslSocket, peerVerifyName, setPeerVerifyName); MO_ADD_PROPERTY(QSslSocket, privateKey, setPrivateKey); MO_ADD_PROPERTY_RO(QSslSocket, protocol); MO_ADD_PROPERTY_RO(QSslSocket, sessionProtocol); MO_ADD_PROPERTY_RO(QSslSocket, sessionCipher); MO_ADD_PROPERTY(QSslSocket, sslConfiguration, setSslConfiguration); MO_ADD_PROPERTY_RO(QSslSocket, sslErrors); #endif // QT_NO_SSL MO_ADD_METAOBJECT1(QSocketNotifier, QObject); MO_ADD_PROPERTY_RO(QSocketNotifier, socket); MO_ADD_PROPERTY_RO(QSocketNotifier, type); MO_ADD_PROPERTY(QSocketNotifier, isEnabled, setEnabled); } #define E(x) { QAbstractSocket:: x, #x } static const MetaEnum::Value socket_pause_mode_table[] = { E(PauseNever), E(PauseOnSslErrors), }; #undef E #define E(x) { QNetworkAccessManager:: x, #x } static const MetaEnum::Value network_accessibility_table[] = { E(UnknownAccessibility), E(NotAccessible), E(Accessible) }; #undef E #ifndef QT_NO_SSL #define E(x) { QSslSocket:: x, #x } static const MetaEnum::Value ssl_mode_table[] = { E(UnencryptedMode), E(SslClientMode), E(SslServerMode) }; #undef E #define E(x) { QSslSocket:: x, #x } static const MetaEnum::Value ssl_peer_verify_mode_table[] = { E(VerifyNone), E(QueryPeer), E(VerifyPeer), E(AutoVerifyPeer) }; #undef E #define E(x) { QSsl:: x, #x } static const MetaEnum::Value ssl_key_algorithm_table[] = { E(Opaque), E(Rsa), E(Dsa), E(Ec) }; #undef E #define E(x) { QSsl:: x, #x } static const MetaEnum::Value ssl_key_type_table[] = { E(PrivateKey), E(PublicKey) }; #undef E #define E(x) { QSsl:: x, #x } static const MetaEnum::Value ssl_protocol_table[] = { E(SslV3), E(SslV2), E(TlsV1_0), E(TlsV1_1), E(TlsV1_2), E(AnyProtocol), E(TlsV1SslV3), E(SecureProtocols), E(TlsV1_0OrLater), E(TlsV1_1OrLater), E(TlsV1_2OrLater), E(UnknownProtocol) }; #undef E static QString sslCertificateToString(const QSslCertificate &cert) { if (cert.isNull()) return QStringLiteral(""); return cert.digest().toHex(); } #endif // QT_NO_SSL #define E(x) { QNetworkConfiguration:: x, #x } static const MetaEnum::Value network_config_bearer_type_table[] = { E(BearerUnknown), E(BearerEthernet), E(BearerWLAN), E(Bearer2G), E(Bearer3G), E(Bearer4G), E(BearerCDMA2000), E(BearerWCDMA), E(BearerHSPA), E(BearerBluetooth), E(BearerWiMAX), E(BearerEVDO), E(BearerLTE) }; static const MetaEnum::Value network_config_purpose_table[] = { E(UnknownPurpose), E(PublicPurpose), E(PrivatePurpose), E(ServiceSpecificPurpose) }; static const MetaEnum::Value network_config_state_table[] = { E(Undefined), E(Defined), E(Discovered), E(Active) }; static const MetaEnum::Value network_config_type_table[] = { E(InternetAccessPoint), E(ServiceNetwork), E(UserChoice), E(Invalid) }; #undef E #if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0) #define E(x) { QNetworkAddressEntry:: x, #x } static const MetaEnum::Value network_address_entry_dns_eligibility_table[] = { E(DnsEligibilityUnknown), E(DnsEligible), E(DnsIneligible) }; #undef E #endif #define E(x) { QNetworkConfigurationManager:: x, #x } static const MetaEnum::Value network_config_manager_capabilities_table[] = { E(CanStartAndStopInterfaces), E(DirectConnectionRouting), E(SystemSessionSupport), E(ApplicationLevelRoaming), E(ForcedRoaming), E(DataStatistics), E(NetworkSessionRequired) }; #undef E #if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0) #define E(x) { QNetworkRequest:: x, #x } static const MetaEnum::Value network_redirect_policy_table[] = { E(ManualRedirectPolicy), E(NoLessSafeRedirectPolicy), E(SameOriginRedirectPolicy), E(UserVerifiedRedirectPolicy) }; #undef E #endif #define E(x) { QNetworkProxy:: x, #x } static const MetaEnum::Value network_proxy_capabilitiy_table[] = { E(TunnelingCapability), E(ListeningCapability), E(UdpTunnelingCapability), E(CachingCapability), E(HostNameLookupCapability), #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) E(SctpTunnelingCapability), E(SctpListeningCapability) #endif }; static const MetaEnum::Value network_proxy_type_table[] = { E(NoProxy), E(DefaultProxy), E(Socks5Proxy), E(HttpProxy), E(HttpCachingProxy), E(FtpCachingProxy) }; #undef E #define E(x) { QNetworkSession:: x, #x } static const MetaEnum::Value network_session_error_table[] = { E(UnknownSessionError), E(SessionAbortedError), E(RoamingError), E(OperationNotSupportedError), E(InvalidConfigurationError) }; static const MetaEnum::Value network_session_state_table[] = { E(Invalid), E(NotAvailable), E(Connecting), E(Connected), E(Closing), E(Disconnected), E(Roaming) }; static const MetaEnum::Value network_session_usage_policy_table[] = { E(NoPolicy), E(NoBackgroundTrafficPolicy) }; #undef E static QString proxyToString(const QNetworkProxy &proxy) { return VariantHandler::displayString(proxy.type()); } void NetworkSupport::registerVariantHandler() { ER_REGISTER_FLAGS(QAbstractSocket, PauseModes, socket_pause_mode_table); VariantHandler::registerStringConverter(std::mem_fn(&QHostAddress::toString)); ER_REGISTER_ENUM(QNetworkAccessManager, NetworkAccessibility, network_accessibility_table); #ifndef QT_NO_SSL ER_REGISTER_ENUM(QSslSocket, PeerVerifyMode, ssl_peer_verify_mode_table); ER_REGISTER_ENUM(QSslSocket, SslMode, ssl_mode_table); ER_REGISTER_ENUM(QSsl, KeyAlgorithm, ssl_key_algorithm_table); ER_REGISTER_ENUM(QSsl, KeyType, ssl_key_type_table); ER_REGISTER_ENUM(QSsl, SslProtocol, ssl_protocol_table); VariantHandler::registerStringConverter(sslCertificateToString); VariantHandler::registerStringConverter(std::mem_fn(&QSslCertificateExtension::name)); VariantHandler::registerStringConverter(std::mem_fn(&QSslCipher::name)); VariantHandler::registerStringConverter(std::mem_fn(&QSslError::errorString)); #endif #if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0) ER_REGISTER_ENUM(QNetworkAddressEntry, DnsEligibilityStatus, network_address_entry_dns_eligibility_table); #endif ER_REGISTER_ENUM(QNetworkConfiguration, BearerType, network_config_bearer_type_table); ER_REGISTER_ENUM(QNetworkConfiguration, Purpose, network_config_purpose_table); ER_REGISTER_FLAGS(QNetworkConfiguration, StateFlags, network_config_state_table); ER_REGISTER_ENUM(QNetworkConfiguration, Type, network_config_type_table); VariantHandler::registerStringConverter(std::mem_fn(&QNetworkConfiguration::name)); ER_REGISTER_FLAGS(QNetworkConfigurationManager, Capabilities, network_config_manager_capabilities_table); VariantHandler::registerStringConverter(std::mem_fn(&QNetworkInterface::name)); #if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0) ER_REGISTER_ENUM(QNetworkRequest, RedirectPolicy, network_redirect_policy_table); #endif ER_REGISTER_FLAGS(QNetworkProxy, Capabilities, network_proxy_capabilitiy_table); ER_REGISTER_ENUM(QNetworkProxy, ProxyType, network_proxy_type_table); VariantHandler::registerStringConverter(proxyToString); ER_REGISTER_ENUM(QNetworkSession, SessionError, network_session_error_table); ER_REGISTER_ENUM(QNetworkSession, State, network_session_state_table); ER_REGISTER_FLAGS(QNetworkSession, UsagePolicies, network_session_usage_policy_table); } NetworkSupportFactory::NetworkSupportFactory(QObject *parent) : QObject(parent) { } gammaray-2.11.3/plugins/network/networksupport.h000066400000000000000000000035021413310443000220360ustar00rootroot00000000000000/* networksupport.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_NETWORKSUPPORT_H #define GAMMARAY_NETWORKSUPPORT_H #include namespace GammaRay { class NetworkSupport : public QObject { Q_OBJECT public: explicit NetworkSupport(Probe *probe, QObject *parent = nullptr); ~NetworkSupport() override; private: void registerMetaTypes(); void registerVariantHandler(); }; class NetworkSupportFactory : public QObject, public StandardToolFactory { Q_OBJECT Q_INTERFACES(GammaRay::ToolFactory) Q_PLUGIN_METADATA(IID "com.kdab.GammaRay.ToolFactory" FILE "gammaray_network.json") public: explicit NetworkSupportFactory(QObject *parent = nullptr); }; } #endif // GAMMARAY_NETWORKSUPPORT_H gammaray-2.11.3/plugins/network/networkwidget.cpp000066400000000000000000000032071413310443000221420ustar00rootroot00000000000000/* networkwidget.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "networkwidget.h" #include "ui_networkwidget.h" #include "cookies/cookietab.h" #include using namespace GammaRay; NetworkWidget::NetworkWidget(QWidget *parent) : QWidget(parent) , ui(new Ui::NetworkWidget) { ui->setupUi(this); } NetworkWidget::~NetworkWidget() = default; void NetworkWidgetFactory::initUi() { PropertyWidget::registerTab(QStringLiteral("cookieJar"), tr( "Cookies"), PropertyWidgetTabPriority::Basic); } gammaray-2.11.3/plugins/network/networkwidget.h000066400000000000000000000035021413310443000216050ustar00rootroot00000000000000/* networkwidget.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_NETWORKWIDGET_H #define GAMMARAY_NETWORKWIDGET_H #include #include #include namespace GammaRay { namespace Ui { class NetworkWidget; } class NetworkWidget : public QWidget { Q_OBJECT public: explicit NetworkWidget(QWidget *parent = nullptr); ~NetworkWidget() override; private: QScopedPointer ui; }; class NetworkWidgetFactory : public QObject, public StandardToolUiFactory { Q_OBJECT Q_INTERFACES(GammaRay::ToolUiFactory) Q_PLUGIN_METADATA(IID "com.kdab.GammaRay.ToolUiFactory" FILE "gammaray_network.json") public: void initUi() override; }; } #endif // GAMMARAY_NETWORKWIDGET_H gammaray-2.11.3/plugins/network/networkwidget.ui000066400000000000000000000042141413310443000217740ustar00rootroot00000000000000 GammaRay::NetworkWidget 0 0 400 300 0 Interfaces Configurations Operations GammaRay::NetworkInterfaceWidget QWidget
    networkinterfacewidget.h
    1
    GammaRay::NetworkConfigurationWidget QWidget
    networkconfigurationwidget.h
    1
    GammaRay::NetworkReplyWidget QWidget
    networkreplywidget.h
    1
    gammaray-2.11.3/plugins/objectvisualizer/000077500000000000000000000000001413310443000204325ustar00rootroot00000000000000gammaray-2.11.3/plugins/objectvisualizer/CMakeLists.txt000066400000000000000000000043521413310443000231760ustar00rootroot00000000000000# probe part set(gammaray_objectvisualizer_plugin_srcs objectvisualizer.cpp objectvisualizermodel.cpp ) gammaray_add_plugin(gammaray_objectvisualizer_plugin JSON gammaray_objectvisualizer.json SOURCES ${gammaray_objectvisualizer_plugin_srcs} ) target_link_libraries(gammaray_objectvisualizer_plugin gammaray_kitemmodels gammaray_core ) ######## START VTK VISUALIZATION if(CMAKE_VERSION VERSION_LESS "3.1") #VTK discovery works a lot better if you give CMake a hint using the VTK_DIR variable find_path(VTK_DIR VTKConfig.cmake /usr/lib64/vtk /usr/lib/vtk /usr/local/lib64/vtk /usr/local/lib/vtk ) set(VTK_LIBRARIES vtkGUISupportQt vtkRenderingFreeType vtkInfovisLayout vtkViewsInfovis # vtkGraphLayoutView & friends ) find_package(VTK 7.1 COMPONENTS ${VTK_LIBRARIES} CONFIG) else() find_package(VTK) endif() set_package_properties(VTK PROPERTIES TYPE OPTIONAL DESCRIPTION "Visualization Toolkit" PURPOSE "Needed for the object visualizer plugin (required Qt5). Just for toying around, highly experimental plugin." URL "https://www.vtk.org" ) if(VTK_FOUND) find_path(VTK_QT_INCLUDE_DIR NAMES QVTKWidget.h HINTS ${VTK_INCLUDE_DIRS}) if(NOT VTK_QT_INCLUDE_DIR) message(STATUS "Cannot locate QVTKWidget.h in ${VTK_INCLUDE_DIRS}") set(VTK_FOUND FALSE) add_feature_info("QVTKWidget header" VTK_FOUND "Object visualizer plugin requires QVTKWidget.h header.") endif() endif() # Only possibly build the plugin under Qt5 (and when VTK was built with Qt5 support as well) if(VTK_FOUND AND VTK_QT_VERSION EQUAL 5 AND GAMMARAY_BUILD_UI) set(OPTIONAL_VTK_LIBRARIES) add_definitions(-DVTKGUI_ENABLED -DVTK_EXCLUDE_STRSTREAM_HEADERS) include(${VTK_USE_FILE}) set(gammaray_objectvisualizer_ui_plugin_srcs objectvisualizerwidget.cpp vtkcontainer.cpp vtkpanel.cpp vtkwidget.cpp ) gammaray_add_plugin(gammaray_objectvisualizer_ui_plugin JSON gammaray_objectvisualizer.json SOURCES ${gammaray_objectvisualizer_ui_plugin_srcs} ) target_include_directories(gammaray_objectvisualizer_ui_plugin SYSTEM PUBLIC ${VTK_INCLUDE_DIRS}) target_link_libraries(gammaray_objectvisualizer_ui_plugin ${VTK_LIBRARIES} gammaray_common gammaray_ui ) endif() ######## END VTK VISUALIZATION gammaray-2.11.3/plugins/objectvisualizer/gammaray_objectvisualizer.json000066400000000000000000000002361413310443000265700ustar00rootroot00000000000000{ "id": "gammaray_objectvisualizer", "name": "Object Visualization", "name[de]": "Objektvisualisierung", "types": [ "QObject" ] } gammaray-2.11.3/plugins/objectvisualizer/objectvisualizer.cpp000066400000000000000000000033561413310443000245310ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Kevin Funk Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "objectvisualizer.h" #include "objectvisualizermodel.h" #include #include using namespace GammaRay; GraphViewer::GraphViewer(Probe *probe, QObject *parent) : QObject(parent) { auto model = new ServerProxyModel(this); model->setSourceModel(probe->objectTreeModel()); model->addProxyRole(ObjectVisualizerModel::ObjectId); model->addProxyRole(ObjectVisualizerModel::ObjectDisplayName); model->addProxyRole(ObjectVisualizerModel::ClassName); probe->registerModel("com.kdab.GammaRay.ObjectVisualizerModel", model); } GraphViewer::~GraphViewer() = default; gammaray-2.11.3/plugins/objectvisualizer/objectvisualizer.h000066400000000000000000000034351413310443000241740ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Kevin Funk Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_OBJECTVISUALIZER_OBJECTVISUALIZER_H #define GAMMARAY_OBJECTVISUALIZER_OBJECTVISUALIZER_H #include namespace GammaRay { class GraphViewer : public QObject { Q_OBJECT public: explicit GraphViewer(Probe *probe, QObject *parent = nullptr); ~GraphViewer() override; }; class GraphViewerFactory : public QObject, public StandardToolFactory { Q_OBJECT Q_INTERFACES(GammaRay::ToolFactory) Q_PLUGIN_METADATA(IID "com.kdab.GammaRay.ToolFactory" FILE "gammaray_objectvisualizer.json") public: explicit GraphViewerFactory(QObject *parent = nullptr) : QObject(parent) { } }; } #endif // GAMMARAY_GRAPHVIEWER_H gammaray-2.11.3/plugins/objectvisualizer/objectvisualizermodel.cpp000066400000000000000000000040751413310443000255510ustar00rootroot00000000000000/* objectvisualizermodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "objectvisualizermodel.h" #include using namespace GammaRay; ObjectVisualizerModel::ObjectVisualizerModel(QObject *parent) : KRecursiveFilterProxyModel(parent) { } ObjectVisualizerModel::~ObjectVisualizerModel() = default; QVariant ObjectVisualizerModel::data(const QModelIndex &proxyIndex, int role) const { if (role == ObjectDisplayName) { QObject *obj = data(proxyIndex, ObjectModel::ObjectRole).value(); return Util::displayString(obj); } else if (role == ObjectId) { QObject *obj = data(proxyIndex, ObjectModel::ObjectRole).value(); return static_cast(reinterpret_cast(obj)); } else if (role == ClassName) { QObject *obj = data(proxyIndex, ObjectModel::ObjectRole).value(); Q_ASSERT(obj); return obj->metaObject()->className(); } return KRecursiveFilterProxyModel::data(proxyIndex, role); } gammaray-2.11.3/plugins/objectvisualizer/objectvisualizermodel.h000066400000000000000000000035741413310443000252210ustar00rootroot00000000000000/* objectvisualizermodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_OBJECTVISUALIZER_OBJECTVISUALIZERMODEL_H #define GAMMARAY_OBJECTVISUALIZER_OBJECTVISUALIZERMODEL_H #include #include <3rdparty/kde/krecursivefilterproxymodel.h> namespace GammaRay { /** Augment the regular object tree by some information needed for the visualization * on the client side. */ class ObjectVisualizerModel : public KRecursiveFilterProxyModel { Q_OBJECT public: enum Role { ObjectId = ObjectModel::UserRole, ObjectDisplayName, ClassName }; explicit ObjectVisualizerModel(QObject *parent); ~ObjectVisualizerModel() override; QVariant data(const QModelIndex &proxyIndex, int role = Qt::DisplayRole) const override; }; } #endif // GAMMARAY_OBJECTVISUALIZERMODEL_H gammaray-2.11.3/plugins/objectvisualizer/objectvisualizerwidget.cpp000066400000000000000000000051411413310443000257270ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Kevin Funk Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "objectvisualizerwidget.h" #include "vtkcontainer.h" #include "vtkpanel.h" #include "vtkwidget.h" #include #include #include #include #include #include #include #include #include using namespace GammaRay; GraphViewerWidget::GraphViewerWidget(QWidget *parent) : QWidget(parent) , m_stateManager(this) , mWidget(new GraphWidget(this)) { mModel = ObjectBroker::model("com.kdab.GammaRay.ObjectVisualizerModel"); QVBoxLayout *vbox = new QVBoxLayout; auto objectSearchLine = new QLineEdit(this); new SearchLineController(objectSearchLine, mModel); vbox->addWidget(objectSearchLine); DeferredTreeView *objectTreeView = new DeferredTreeView(this); objectTreeView->header()->setObjectName("objectTreeViewHeader"); objectTreeView->setModel(mModel); objectTreeView->setSortingEnabled(true); vbox->addWidget(objectTreeView); mObjectTreeView = objectTreeView; QWidget *treeViewWidget = new QWidget(this); treeViewWidget->setLayout(vbox); QSplitter *splitter = new QSplitter(this); splitter->addWidget(treeViewWidget); splitter->addWidget(mWidget); QHBoxLayout *hbox = new QHBoxLayout(this); hbox->addWidget(splitter); mWidget->vtkWidget()->setModel(mModel); mWidget->vtkWidget()->setSelectionModel(mObjectTreeView->selectionModel()); } GraphViewerWidget::~GraphViewerWidget() { } gammaray-2.11.3/plugins/objectvisualizer/objectvisualizerwidget.h000066400000000000000000000040121413310443000253700ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Kevin Funk Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_OBJECTVISUALIZER_OBJECTVISUALIZERWIDGET_H #define GAMMARAY_OBJECTVISUALIZER_OBJECTVISUALIZERWIDGET_H #include #include #include QT_BEGIN_NAMESPACE class QAbstractItemModel; class QModelIndex; QT_END_NAMESPACE namespace GammaRay { class DeferredTreeView; class GraphWidget; class GraphViewerWidget : public QWidget { Q_OBJECT public: explicit GraphViewerWidget(QWidget *parent = nullptr); virtual ~GraphViewerWidget(); private: UIStateManager m_stateManager; QAbstractItemModel *mModel; DeferredTreeView *mObjectTreeView; GraphWidget *mWidget; }; class ObjectVisualizerUiFactory : public QObject, public StandardToolUiFactory { Q_OBJECT Q_INTERFACES(GammaRay::ToolUiFactory) Q_PLUGIN_METADATA(IID "com.kdab.GammaRay.ToolUiFactory" FILE "gammaray_objectvisualizer.json") }; } #endif // GAMMARAY_GRAPHVIEWER_H gammaray-2.11.3/plugins/objectvisualizer/vtkcontainer.cpp000066400000000000000000000030101413310443000236370ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Kevin Funk Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "vtkcontainer.h" #include #include "vtkwidget.h" #include "vtkpanel.h" using namespace GammaRay; GraphWidget::GraphWidget(QWidget *parent) : QWidget(parent) { QVBoxLayout *vbox = new QVBoxLayout(this); m_vtkWidget = new VtkWidget(this); m_vtkPanel = new VtkPanel(m_vtkWidget, this); vbox->addWidget(m_vtkPanel); vbox->addWidget(m_vtkWidget); } GraphWidget::~GraphWidget() { } gammaray-2.11.3/plugins/objectvisualizer/vtkcontainer.h000066400000000000000000000032141413310443000233120ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Kevin Funk Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_OBJECTVISUALIZER_VTKCONTAINER_H #define GAMMARAY_OBJECTVISUALIZER_VTKCONTAINER_H #include namespace GammaRay { class VtkPanel; class VtkWidget; class GraphWidget : public QWidget { Q_OBJECT public: explicit GraphWidget(QWidget *parent = nullptr); virtual ~GraphWidget(); VtkWidget *vtkWidget() const { return m_vtkWidget; } VtkPanel *vtkPanel() const { return m_vtkPanel; } private: VtkWidget *m_vtkWidget; VtkPanel *m_vtkPanel; }; } #endif // GAMMARAY_GRAPHWIDGET_H gammaray-2.11.3/plugins/objectvisualizer/vtkpanel.cpp000066400000000000000000000110531413310443000227620ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Kevin Funk Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "vtkpanel.h" #include "vtkwidget.h" #include #include #include #include #include #include #include #include #include using namespace GammaRay; VtkPanel::VtkPanel(VtkWidget *vtkWidget, QWidget *parent) : QToolBar(parent) , m_vtkWidget(vtkWidget) , m_currentLayout("spanTree") { addWidget(new QLabel(tr("Layout:"))); m_layoutBox = new QComboBox; #if 0 m_layoutBox->addItem(tr("Tree Layout"), "tree"); #endif m_layoutBox->addItem(tr("Span Tree Layout"), "spanTree"); m_layoutBox->addItem(tr("Force Directed Layout"), "forceDirected"); m_layoutBox->addItem(tr("Force Directed Layout (3D)"), "forceDirected3D"); m_layoutBox->addItem(tr("Simple 2D Layout"), "simple2D"); connect(m_layoutBox, SIGNAL(currentIndexChanged(int)), SLOT(layoutChanged(int))); addWidget(m_layoutBox); addWidget(new QLabel(tr("Stereo:"))); m_stereoBox = new QComboBox; m_stereoBox->addItem(tr("Off"), 0); m_stereoBox->addItem(tr("Crystal Eyes"), VTK_STEREO_CRYSTAL_EYES); m_stereoBox->addItem(tr("Red/Blue"), VTK_STEREO_RED_BLUE); m_stereoBox->addItem(tr("Interlaced"), VTK_STEREO_INTERLACED); m_stereoBox->addItem(tr("Left"), VTK_STEREO_LEFT); m_stereoBox->addItem(tr("Right"), VTK_STEREO_RIGHT); m_stereoBox->addItem(tr("Dresden"), VTK_STEREO_DRESDEN); m_stereoBox->addItem(tr("Anaglyph"), VTK_STEREO_ANAGLYPH); m_stereoBox->addItem(tr("Checkboard"), VTK_STEREO_CHECKERBOARD); connect(m_stereoBox, SIGNAL(currentIndexChanged(int)), SLOT(stereoModeChanged(int))); addWidget(m_stereoBox); } static vtkGraphLayoutStrategy *layoutStrategyForName(const QString &layoutName) { if (layoutName == "tree") { vtkTreeLayoutStrategy *strategy = vtkTreeLayoutStrategy::New(); strategy->SetRadial(true); return strategy; } else if (layoutName == "spanTree") { return vtkSpanTreeLayoutStrategy::New(); } else if (layoutName == "forceDirected") { return vtkForceDirectedLayoutStrategy::New(); } else if (layoutName == "forceDirected3D") { vtkForceDirectedLayoutStrategy *strategy = vtkForceDirectedLayoutStrategy::New(); strategy->SetThreeDimensionalLayout(true); return strategy; } else if (layoutName == "simple2D") { return vtkSimple2DLayoutStrategy::New(); } else { return nullptr; } } void VtkPanel::layoutChanged(int index) { const QString layoutName = m_layoutBox->itemData(index).toString(); if (m_currentLayout == layoutName) return; // update vtkGraphLayoutStrategy *strategy = layoutStrategyForName(layoutName); m_vtkWidget->layoutView()->SetLayoutStrategy(strategy); m_vtkWidget->layoutView()->ResetCamera(); m_vtkWidget->layoutView()->Render(); m_vtkWidget->GetInteractor()->Start(); m_currentLayout = layoutName; } void VtkPanel::stereoModeChanged(int index) { const int stereoMode = m_stereoBox->itemData(index).toInt(); if (stereoMode <= 0) { m_vtkWidget->layoutView()->GetRenderWindow()->SetStereoRender(false); } else { m_vtkWidget->layoutView()->GetRenderWindow()->SetStereoRender(true); m_vtkWidget->layoutView()->GetRenderWindow()->SetStereoType(stereoMode); } m_vtkWidget->layoutView()->GetRenderWindow()->StereoUpdate(); } VtkPanel::~VtkPanel() { } gammaray-2.11.3/plugins/objectvisualizer/vtkpanel.h000066400000000000000000000032561413310443000224350ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Kevin Funk Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_OBJECTVISUALIZER_VTKPANEL_H #define GAMMARAY_OBJECTVISUALIZER_VTKPANEL_H #include QT_BEGIN_NAMESPACE class QComboBox; QT_END_NAMESPACE namespace GammaRay { class VtkWidget; class VtkPanel : public QToolBar { Q_OBJECT public: explicit VtkPanel(VtkWidget *vtkWidget, QWidget *parent = nullptr); virtual ~VtkPanel(); public slots: void layoutChanged(int); void stereoModeChanged(int); private: VtkWidget *m_vtkWidget; QComboBox *m_layoutBox; QComboBox *m_stereoBox; QString m_currentLayout; }; } #endif // GAMMARAY_VTKPANEL_H gammaray-2.11.3/plugins/objectvisualizer/vtkwidget.cpp000066400000000000000000000341031413310443000231470ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Kevin Funk Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "vtkwidget.h" #include "objectvisualizermodel.h" #include "common/objectmodel.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace GammaRay; // #define WITH_DEBUG #ifdef WITH_DEBUG #define DEBUG(msg) std::cout << Q_FUNC_INFO << " " << msg << std::endl; #else #define DEBUG(msg) qt_noop(); #endif #define VTK_CREATE(type, name) \ vtkSmartPointer name = vtkSmartPointer::New() VtkWidget::VtkWidget(QWidget *parent) : QVTKWidget(parent) , m_mousePressed(false) , m_updateTimer(new QTimer(this)) , m_model(nullptr) , m_selectionModel(nullptr) , m_repopulateTimer(new QTimer(this)) , m_colorIndex(0) { setupRenderer(); setupGraph(); show(); m_updateTimer->setInterval(0); m_updateTimer->setSingleShot(true); connect(m_updateTimer, SIGNAL(timeout()), SLOT(renderViewImpl())); m_repopulateTimer->setInterval(100); m_repopulateTimer->setSingleShot(true); connect(m_repopulateTimer, SIGNAL(timeout()), SLOT(doRepopulate())); } VtkWidget::~VtkWidget() { clear(); DEBUG("") } void VtkWidget::setModel(QAbstractItemModel *model) { m_model = model; connect(m_model, SIGNAL(rowsInserted(QModelIndex,int,int)), SLOT(objectRowsInserted(QModelIndex,int,int))); connect(m_model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), SLOT(objectRowsAboutToBeRemoved(QModelIndex,int,int))); connect(m_model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), SLOT(objectDataChanged(QModelIndex,QModelIndex))); doRepopulate(); // no delay here, otherwise we race against the signals } void VtkWidget::setSelectionModel(QItemSelectionModel *selectionModel) { m_selectionModel = selectionModel; connect(selectionModel, SIGNAL(selectionChanged(QItemSelection,QItemSelection)), SLOT(selectionChanged())); } void VtkWidget::setupRenderer() { } void VtkWidget::resetCamera() { m_view->ResetCamera(); } void VtkWidget::mousePressEvent(QMouseEvent *event) { m_mousePressed = true; QVTKWidget::mousePressEvent(event); } void VtkWidget::mouseReleaseEvent(QMouseEvent *event) { m_mousePressed = false; QVTKWidget::mouseReleaseEvent(event); } void VtkWidget::setupGraph() { DEBUG("start") VTK_CREATE(vtkMutableDirectedGraph, graph); m_graph = graph; VTK_CREATE(vtkVariantArray, vertexPropertyArr); vertexPropertyArr->SetNumberOfValues(3); m_vertexPropertyArr = vertexPropertyArr; VTK_CREATE(vtkStringArray, vertexProp0Array); vertexProp0Array->SetName("labels"); m_graph->GetVertexData()->AddArray(vertexProp0Array); // currently not used VTK_CREATE(vtkIntArray, vertexProp1Array); vertexProp1Array->SetName("weight"); m_graph->GetVertexData()->AddArray(vertexProp1Array); // coloring vtkSmartPointer vertexColors = vtkSmartPointer::New(); vertexColors->SetName("Color"); m_graph->GetVertexData()->AddArray(vertexColors); vtkSmartPointer colorLookupTable = vtkSmartPointer::New(); colorLookupTable->Build(); vtkSmartPointer theme = vtkSmartPointer::New(); theme->SetPointLookupTable(colorLookupTable); vtkGraphLayoutView *graphLayoutView = vtkGraphLayoutView::New(); graphLayoutView->AddRepresentationFromInput(graph); graphLayoutView->SetVertexLabelVisibility(true); graphLayoutView->SetVertexLabelArrayName("labels"); graphLayoutView->SetLayoutStrategyToSpanTree(); graphLayoutView->SetVertexColorArrayName("Color"); graphLayoutView->SetColorVertices(true); graphLayoutView->ApplyViewTheme(theme); m_view = graphLayoutView; VTK_CREATE(vtkInteractorStyleTrackballCamera, style); vtkSmartPointer renderWindowInteractor = vtkSmartPointer::New(); renderWindowInteractor->SetRenderWindow(graphLayoutView->GetRenderWindow()); renderWindowInteractor->SetInteractorStyle(style); renderWindowInteractor->Initialize(); SetRenderWindow(graphLayoutView->GetRenderWindow()); // code for generating edge arrow heads, needs some love // currently it modifies the layouting // how to use: // comment the AddRepresentationFromInput call to vtkGraphLayoutView and uncomment this #if 0 VTK_CREATE(vtkGraphLayout, layout); layout->SetInput(graph); layout->SetLayoutStrategy(strategy); // Tell the view to use the vertex layout we provide graphLayoutView->SetLayoutStrategyToPassThrough(); // The arrows will be positioned on a straight line between two // vertices so tell the view not to draw arcs for parallel edges graphLayoutView->SetEdgeLayoutStrategyToPassThrough(); // Add the graph to the view. This will render vertices and edges, // but not edge arrows. graphLayoutView->AddRepresentationFromInputConnection(layout->GetOutputPort()); // Manually create an actor containing the glyphed arrows. VTK_CREATE(vtkGraphToPolyData, graphToPoly); graphToPoly->SetInputConnection(layout->GetOutputPort()); graphToPoly->EdgeGlyphOutputOn(); // Set the position (0: edge start, 1: edge end) where // the edge arrows should go. graphToPoly->SetEdgeGlyphPosition(0.98); // Make a simple edge arrow for glyphing. VTK_CREATE(vtkGlyphSource2D, arrowSource); arrowSource->SetGlyphTypeToEdgeArrow(); arrowSource->SetScale(0.001); arrowSource->Update(); // Use Glyph3D to repeat the glyph on all edges. VTK_CREATE(vtkGlyph3D, arrowGlyph); arrowGlyph->SetInputConnection(0, graphToPoly->GetOutputPort(1)); arrowGlyph->SetInputConnection(1, arrowSource->GetOutputPort()); // Add the edge arrow actor to the view. VTK_CREATE(vtkPolyDataMapper, arrowMapper); arrowMapper->SetInputConnection(arrowGlyph->GetOutputPort()); VTK_CREATE(vtkActor, arrowActor); arrowActor->SetMapper(arrowMapper); graphLayoutView->GetRenderer()->AddActor(arrowActor); #endif graphLayoutView->ResetCamera(); graphLayoutView->Render(); graphLayoutView->GetInteractor()->Start(); DEBUG("end") } qulonglong VtkWidget::addObject(const QModelIndex &index) { // ignore new objects during scene interaction // TODO: Add some code to add the objects later on => queue objects if (m_mousePressed) { DEBUG("Ignoring new object during scene interaction: " << object << " " << object->metaObject()->className()) return 0; } qulonglong objectId = index.data(ObjectVisualizerModel::ObjectId).toULongLong(); const QString className = index.data(ObjectVisualizerModel::ClassName).toString(); #if 0 // FIXME this breaks the graph structure since this will cause orphan children! if (className == "QVTKInteractorInternal") return 0; #endif if (!objectId || m_objectIdMap.contains(objectId)) return 0; if (!filterAcceptsObject(index)) return 0; const QString label = index.data(ObjectVisualizerModel::ObjectDisplayName).toString(); const int weight = 1; // TODO: Make weight somewhat usable? m_vertexPropertyArr->SetValue(0, vtkUnicodeString::from_utf16(label.utf16())); m_vertexPropertyArr->SetValue(1, weight); static int colorIndex = 0; colorIndex = colorIndex % 10; auto it = m_typeColorMap.constFind(className); if (it != m_typeColorMap.constEnd()) { m_vertexPropertyArr->SetValue(2, it.value()); } else { m_vertexPropertyArr->SetValue(2, m_colorIndex); m_typeColorMap.insert(className, m_colorIndex); ++m_colorIndex; } const vtkIdType type = m_graph->AddVertex(m_vertexPropertyArr); DEBUG("Add: " << type << " " << object->metaObject()->className()) m_objectIdMap[objectId] = type; // recursively add our children for (int i = 0; i < index.model()->rowCount(index); ++i) addObject(index.child(i, 0)); // add edge to parent if (index.parent().isValid()) { const qulonglong parentId = index.parent().data(ObjectVisualizerModel::ObjectId).toULongLong(); if (parentId) { Q_ASSERT(m_objectIdMap.contains(parentId)); const vtkIdType parentType = m_objectIdMap.value(parentId); m_graph->AddEdge(parentType, type); } } renderView(); return objectId; } bool VtkWidget::removeObject(const QModelIndex &index) { for (int i = 0; i < index.model()->rowCount(index); ++i) removeObject(index.child(i, 0)); const qulonglong objectId = index.data(ObjectVisualizerModel::ObjectId).toULongLong(); return removeObjectInternal(objectId); } bool VtkWidget::removeObjectInternal(qulonglong objectId) { if (!m_objectIdMap.contains(objectId)) return false; // Remove id-for-object from VTK's graph data structure const vtkIdType type = m_objectIdMap[objectId]; const int size = m_graph->GetNumberOfVertices(); m_graph->RemoveVertex(type); // VTK re-orders the vertex IDs after removal! // we have to copy this behavior to track the associated QObject instances const vtkIdType lastId = m_objectIdMap.size() - 1; DEBUG("Type: " << type << " Last: " << lastId) if (type != lastId) { qulonglong lastObjectId = m_objectIdMap.key(lastId); Q_ASSERT(lastObjectId); m_objectIdMap[lastObjectId] = type; } // Remove object from our map if (size > m_graph->GetNumberOfVertices()) Q_ASSERT(m_objectIdMap.remove(objectId) == 1); else DEBUG("Warning: Should not happen: Could not remove vertice with id: " << type) renderView(); return true; } /// Schedules the re-rendering of the VTK view void VtkWidget::renderView() { m_updateTimer->start(); } void VtkWidget::clear() { // TODO: there must be an easier/faster way to clean the graph data // Just re-create the vtk graph data object? for (const qulonglong &objectId : qAsConst(m_objectIdMap)) { removeObjectInternal(objectId); } m_objectIdMap.clear(); renderView(); } void VtkWidget::renderViewImpl() { DEBUG("") m_view->Render(); m_view->ResetCamera(); } void VtkWidget::selectionChanged() { repopulate(); resetCamera(); } void VtkWidget::repopulate() { if (!m_repopulateTimer->isActive()) m_repopulateTimer->start(); } void VtkWidget::doRepopulate() { DEBUG("") clear(); for (int i = 0; i < m_model->rowCount(); ++i) { const QModelIndex index = m_model->index(i, 0); addObject(index); } } // TODO: Move to Util.h? static bool descendantOf(const QModelIndex &ascendant, const QModelIndex &index) { const QModelIndex parent = index.parent(); if (!parent.isValid()) return false; if (parent == ascendant) return true; return descendantOf(ascendant, parent); } static QModelIndex mapToSource(const QModelIndex &proxyIndex) { if (proxyIndex.model()->inherits("GammaRay::ObjectVisualizerModel")) return proxyIndex; const QAbstractProxyModel *proxyModel = qobject_cast(proxyIndex.model()); if (proxyModel) return mapToSource(proxyModel->mapToSource(proxyIndex)); else return proxyIndex; } bool VtkWidget::filterAcceptsObject(const QModelIndex &index) const { if (!m_selectionModel) return true; const QModelIndexList rows = m_selectionModel->selectedRows(); for (const QModelIndex &row : rows) { const QModelIndex sourceRow = mapToSource(row); if (index == sourceRow) return true; return descendantOf(sourceRow, index); } return true; // empty selection } void VtkWidget::objectRowsInserted(const QModelIndex &parent, int start, int end) { for (int i = start; i <= end; ++i) { const QModelIndex index = m_model->index(i, 0, parent); addObject(index); } } void VtkWidget::objectRowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) { for (int i = start; i <= end; ++i) { const QModelIndex index = m_model->index(i, 0, parent); removeObject(index); } } void VtkWidget::objectDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) { for (int i = topLeft.row(); i <= bottomRight.row(); ++i) { const QModelIndex index = m_model->index(i, 0, topLeft.parent()); addObject(index); } } gammaray-2.11.3/plugins/objectvisualizer/vtkwidget.h000066400000000000000000000064161413310443000226220ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Kevin Funk Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_OBJECTVISUALIZER_VTKWIDGET_H #define GAMMARAY_OBJECTVISUALIZER_VTKWIDGET_H #include #include #include #include QT_BEGIN_NAMESPACE class QItemSelectionModel; class QModelIndex; class QAbstractItemModel; class QMouseEvent; class QTimer; QT_END_NAMESPACE class vtkGraphLayoutStrategy; class vtkVariantArray; class vtkGraphLayoutView; class vtkMutableDirectedGraph; namespace GammaRay { class VtkWidget : public QVTKWidget { Q_OBJECT public: explicit VtkWidget(QWidget *parent = nullptr); virtual ~VtkWidget(); vtkGraphLayoutView *layoutView() const { return m_view; } void setModel(QAbstractItemModel *model); void setSelectionModel(QItemSelectionModel *selectionModel); public Q_SLOTS: void resetCamera(); qulonglong addObject(const QModelIndex &index); bool removeObject(const QModelIndex &index); void clear(); void repopulate(); private Q_SLOTS: bool removeObjectInternal(qulonglong objectId); void doRepopulate(); void selectionChanged(); void renderViewImpl(); void renderView(); void objectRowsInserted(const QModelIndex &parent, int start, int end); void objectRowsAboutToBeRemoved(const QModelIndex &parent, int start, int end); void objectDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight); protected: virtual void mousePressEvent(QMouseEvent *event); virtual void mouseReleaseEvent(QMouseEvent *event); bool filterAcceptsObject(const QModelIndex &index) const; private: void setupGraph(); void setupRenderer(); bool m_mousePressed; QTimer *m_updateTimer; QAbstractItemModel *m_model; QItemSelectionModel *m_selectionModel; QTimer *m_repopulateTimer; // TODO: Instead of tracking all available objects, make Probe::m_validObjects public? QMap m_objectIdMap; int m_colorIndex; QMap m_typeColorMap; vtkSmartPointer m_vertexPropertyArr; vtkGraphLayoutView *m_view; vtkSmartPointer m_graph; }; } #endif // GAMMARAY_VTKWIDGET_H gammaray-2.11.3/plugins/positioning/000077500000000000000000000000001413310443000174105ustar00rootroot00000000000000gammaray-2.11.3/plugins/positioning/CMakeLists.txt000066400000000000000000000032711413310443000221530ustar00rootroot00000000000000# probe plugin if (NOT GAMMARAY_CLIENT_ONLY_BUILD) set(gammaray_positioning_srcs positioning.cpp positioninginterface.cpp positioninfopropertyadaptor.cpp ) gammaray_add_plugin(gammaray_positioning JSON gammaray_positioning.json SOURCES ${gammaray_positioning_srcs}) target_link_libraries(gammaray_positioning gammaray_core Qt5::Positioning) # proxy geo position info source factory if (NOT Qt5Core_VERSION VERSION_LESS 5.7) set(gammaray_geopositioninfosource_srcs geopositioninfosourcefactory.cpp geopositioninfosource.cpp positioninginterface.cpp ) add_library(gammaray_geopositioninfosource MODULE ${gammaray_geopositioninfosource_srcs}) target_compile_features(gammaray_geopositioninfosource PUBLIC ${GAMMARAY_REQUIRED_CXX_FEATURES}) target_include_directories(gammaray_geopositioninfosource SYSTEM PRIVATE ${Qt5Core_PRIVATE_INCLUDE_DIRS}) target_link_libraries(gammaray_geopositioninfosource gammaray_common Qt5::Positioning ) install(TARGETS gammaray_geopositioninfosource DESTINATION ${TARGET_PLUGIN_INSTALL_DIR}/position) endif() endif() # ui plugin if(GAMMARAY_BUILD_UI AND Qt5Location_FOUND AND Qt5QuickWidgets_FOUND AND NOT Qt5Core_VERSION VERSION_LESS 5.7) set(gammaray_positioning_ui_srcs positioningwidget.cpp positioninginterface.cpp positioningclient.cpp mapcontroller.cpp positioningwidget.qrc ) qml_lint(mapview.qml) gammaray_add_plugin(gammaray_positioning_ui JSON gammaray_positioning.json SOURCES ${gammaray_positioning_ui_srcs}) target_link_libraries(gammaray_positioning_ui gammaray_ui Qt5::Widgets Qt5::Positioning Qt5::Location Qt5::QuickWidgets ) endif() gammaray-2.11.3/plugins/positioning/direction_marker.png000066400000000000000000000004661413310443000234450ustar00rootroot00000000000000PNG  IHDR sBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<IDAT8Ք 0E?.؅HsTY2CxAIS$'3ɗ,5$3Z0IoĚ_eYc粬3-kL#vE#vEKLrʝ-˒]"y%ԷҞ݃+t<+%d-ϝ^-g[ z9tfv[$J 0^kIIENDB`gammaray-2.11.3/plugins/positioning/direction_marker.svg000066400000000000000000000055341413310443000234610ustar00rootroot00000000000000 image/svg+xml gammaray-2.11.3/plugins/positioning/gammaray_positioning.json000066400000000000000000000003211413310443000245170ustar00rootroot00000000000000{ "hidden": false, "id": "gammaray_positioning", "name": "Positioning", "name[de]": "Positionierung", "types": [ "QGeoPositionInfoSource", "QGeoSatelliteInfoSource" ] } gammaray-2.11.3/plugins/positioning/geopositioninfosource.cpp000066400000000000000000000133351413310443000245550ustar00rootroot00000000000000/* geopositioninfosource.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "geopositioninfosource.h" #include "positioninginterface.h" #include using namespace GammaRay; GeoPositionInfoSource::GeoPositionInfoSource(QObject* parent) : QGeoPositionInfoSource(parent), m_source(nullptr), m_interface(nullptr) { } GeoPositionInfoSource::~GeoPositionInfoSource() = default; void GeoPositionInfoSource::setSource(QGeoPositionInfoSource* source) { Q_ASSERT(!m_source); m_source = source; if (source && !overrideEnabled()) connectSource(); setupSourceUpdate(); } QGeoPositionInfoSource::Error GeoPositionInfoSource::error() const { if (m_source) return m_source->error(); return UnknownSourceError; } QGeoPositionInfo GeoPositionInfoSource::lastKnownPosition(bool fromSatellitePositioningMethodsOnly) const { if (m_source && !overrideEnabled()) return m_source->lastKnownPosition(fromSatellitePositioningMethodsOnly); if (m_interface) return m_interface->positionInfoOverride(); return QGeoPositionInfo(); } int GeoPositionInfoSource::minimumUpdateInterval() const { if (m_source) return m_source->minimumUpdateInterval(); return 10000; } void GeoPositionInfoSource::setPreferredPositioningMethods(PositioningMethods methods) { if (m_source) { m_source->setPreferredPositioningMethods(methods); QGeoPositionInfoSource::setPreferredPositioningMethods(m_source->preferredPositioningMethods()); } else { QGeoPositionInfoSource::setPreferredPositioningMethods(methods); } } void GeoPositionInfoSource::setUpdateInterval(int msec) { if (m_source) { m_source->setUpdateInterval(msec); QGeoPositionInfoSource::setUpdateInterval(m_source->updateInterval()); } else { QGeoPositionInfoSource::setUpdateInterval(msec); } } QGeoPositionInfoSource::PositioningMethods GeoPositionInfoSource::supportedPositioningMethods() const { if (m_source) return m_source->supportedPositioningMethods(); return AllPositioningMethods; } void GeoPositionInfoSource::requestUpdate(int timeout) { if (m_source) m_source->requestUpdate(timeout); } void GeoPositionInfoSource::startUpdates() { if (m_source) m_source->startUpdates(); } void GeoPositionInfoSource::stopUpdates() { if (m_source) m_source->stopUpdates(); } void GeoPositionInfoSource::setInterface(PositioningInterface* iface) { Q_ASSERT(iface); m_interface = iface; m_interface->setPositioningOverrideAvailable(true); connect(m_interface, &PositioningInterface::positioningOverrideEnabledChanged, this, &GeoPositionInfoSource::overrideChanged); connect(m_interface, &PositioningInterface::positionInfoOverrideChanged, this, &GeoPositionInfoSource::positionInfoOverrideChanged); if (overrideEnabled()) emit positionUpdated(lastKnownPosition()); setupSourceUpdate(); } bool GeoPositionInfoSource::overrideEnabled() const { return m_interface && m_interface->positioningOverrideEnabled(); } void GeoPositionInfoSource::overrideChanged() { if (!overrideEnabled()) connectSource(); else disconnectSource(); emit positionUpdated(lastKnownPosition()); } void GeoPositionInfoSource::positionInfoOverrideChanged() { if (overrideEnabled()) emit positionUpdated(lastKnownPosition()); } void GeoPositionInfoSource::connectSource() { if (!m_source) return; connect(m_source, QOverload::of(&QGeoPositionInfoSource::error), this, &GeoPositionInfoSource::error, Qt::UniqueConnection); connect(m_source, &QGeoPositionInfoSource::positionUpdated, this, &GeoPositionInfoSource::positionUpdated, Qt::UniqueConnection); connect(m_source, &QGeoPositionInfoSource::updateTimeout, this, &GeoPositionInfoSource::updateTimeout, Qt::UniqueConnection); QGeoPositionInfoSource::setPreferredPositioningMethods(m_source->preferredPositioningMethods()); QGeoPositionInfoSource::setUpdateInterval(m_source->updateInterval()); } void GeoPositionInfoSource::disconnectSource() { if (!m_source) return; disconnect(m_source, QOverload::of(&QGeoPositionInfoSource::error), this, &GeoPositionInfoSource::error); disconnect(m_source, &QGeoPositionInfoSource::positionUpdated, this, &GeoPositionInfoSource::positionUpdated); disconnect(m_source, &QGeoPositionInfoSource::updateTimeout, this, &GeoPositionInfoSource::updateTimeout); } void GeoPositionInfoSource::setupSourceUpdate() { if (!m_source || !m_interface) return; connect(m_source, &QGeoPositionInfoSource::positionUpdated, m_interface, &PositioningInterface::setPositionInfo); } gammaray-2.11.3/plugins/positioning/geopositioninfosource.h000066400000000000000000000046471413310443000242300ustar00rootroot00000000000000/* geopositioninfosource.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_GEOPOSITIONINFOSOURCE_H #define GAMMARAY_GEOPOSITIONINFOSOURCE_H #include namespace GammaRay { class PositioningInterface; class GeoPositionInfoSource : public QGeoPositionInfoSource { Q_OBJECT public: explicit GeoPositionInfoSource(QObject *parent = nullptr); ~GeoPositionInfoSource() override; void setSource(QGeoPositionInfoSource *source); Error error() const override; QGeoPositionInfo lastKnownPosition(bool fromSatellitePositioningMethodsOnly = false) const override; int minimumUpdateInterval() const override; void setPreferredPositioningMethods(PositioningMethods methods) override; void setUpdateInterval(int msec) override; PositioningMethods supportedPositioningMethods() const override; public slots: void requestUpdate(int timeout = 0) override; void startUpdates() override; void stopUpdates() override; void setInterface(PositioningInterface* iface); private: bool overrideEnabled() const; void connectSource(); void disconnectSource(); void setupSourceUpdate(); private slots: void overrideChanged(); void positionInfoOverrideChanged(); private: QGeoPositionInfoSource *m_source; PositioningInterface *m_interface; }; } #endif // GAMMARAY_GEOPOSITIONINFOSOURCE_H gammaray-2.11.3/plugins/positioning/geopositioninfosourcefactory.cpp000066400000000000000000000072031413310443000261420ustar00rootroot00000000000000/* geopositioninfosourcefactory.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "geopositioninfosourcefactory.h" #include "geopositioninfosource.h" #include #include #include #include using namespace GammaRay; GeoPositionInfoSourceFactory::GeoPositionInfoSourceFactory(QObject* parent): QObject(parent), m_factoryLoader(new QFactoryLoader("org.qt-project.qt.position.sourcefactory/5.0", QStringLiteral("/position"))) { } GeoPositionInfoSourceFactory::~GeoPositionInfoSourceFactory() { delete m_factoryLoader; } QGeoPositionInfoSource* GeoPositionInfoSourceFactory::positionInfoSource(QObject *parent) { auto proxy = new GeoPositionInfoSource(parent); auto metaData = m_factoryLoader->metaData(); QVector indexes; indexes.resize(metaData.size()); std::iota(indexes.begin(), indexes.end(), 0); // filter anything not applicable for (auto it = indexes.begin(); it != indexes.end();) { const auto data = metaData.at(*it).value(QStringLiteral("MetaData")).toObject(); const auto correctType = data.value(QStringLiteral("Position")).toBool(); const auto isGammaray = data.value(QStringLiteral("Provider")).toString() == QLatin1String("gammaray"); if (correctType && !isGammaray) ++it; else it = indexes.erase(it); } // sort by priority std::sort(indexes.begin(), indexes.end(), [metaData](int lhs, int rhs) { const auto lData = metaData.at(lhs).value(QStringLiteral("MetaData")).toObject(); const auto rData = metaData.at(rhs).value(QStringLiteral("MetaData")).toObject(); return lData.value(QStringLiteral("Priority")).toInt() > rData.value(QStringLiteral("Priority")).toInt(); }); // actually try the plugins QGeoPositionInfoSource *source = nullptr; for (auto it = indexes.constBegin(); it != indexes.constEnd(); ++it) { const auto data = metaData.at(*it).value(QStringLiteral("MetaData")).toObject(); const auto provider = data.value(QStringLiteral("Provider")).toString(); if (provider.isEmpty()) continue; if (auto s = QGeoPositionInfoSource::createSource(provider, proxy)) { source = s; break; } } proxy->setSource(source); return proxy; } QGeoSatelliteInfoSource* GeoPositionInfoSourceFactory::satelliteInfoSource(QObject *parent) { Q_UNUSED(parent); return nullptr; } QGeoAreaMonitorSource* GeoPositionInfoSourceFactory::areaMonitor(QObject *parent) { Q_UNUSED(parent); return nullptr; } gammaray-2.11.3/plugins/positioning/geopositioninfosourcefactory.h000066400000000000000000000040631413310443000256100ustar00rootroot00000000000000/* geopositioninfosourcefactory.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_GEOPOSITIONINFOSOURCEFACTORY_H #define GAMMARAY_GEOPOSITIONINFOSOURCEFACTORY_H #include #include QT_BEGIN_NAMESPACE class QFactoryLoader; QT_END_NAMESPACE namespace GammaRay { class GeoPositionInfoSourceFactory : public QObject, QGeoPositionInfoSourceFactory { Q_OBJECT Q_PLUGIN_METADATA(IID "org.qt-project.qt.position.sourcefactory/5.0" FILE "geopositioninfosourcefactory.json") Q_INTERFACES(QGeoPositionInfoSourceFactory) public: explicit GeoPositionInfoSourceFactory(QObject *parent = nullptr); ~GeoPositionInfoSourceFactory() override; QGeoPositionInfoSource *positionInfoSource(QObject *parent) override; QGeoSatelliteInfoSource *satelliteInfoSource(QObject *parent) override; QGeoAreaMonitorSource *areaMonitor(QObject *parent) override; private: QFactoryLoader *m_factoryLoader; }; } #endif // GAMMARAY_GEOPOSITIONINFOSOURCEFACTORY_H gammaray-2.11.3/plugins/positioning/geopositioninfosourcefactory.json000066400000000000000000000002551413310443000263310ustar00rootroot00000000000000{ "Keys": ["gammaray"], "Provider": "gammaray", "Position": true, "Satellite": false, "Monitor" : false, "Priority": 999999, "Testable": false } gammaray-2.11.3/plugins/positioning/mapcontroller.cpp000066400000000000000000000065331413310443000230040ustar00rootroot00000000000000/* mapcontroller.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "mapcontroller.h" using namespace GammaRay; MapController::MapController(QObject* parent): QObject(parent), m_sourceHorizontalAccuracy(.0), m_sourceDirection(.0), m_overrideHorizontalAccuracy(.0), m_overrideDirection(.0), m_overrideEnabled(false) { } MapController::~MapController() = default; QGeoCoordinate MapController::sourceCoordinate() const { return m_sourceCoordinate; } void MapController::setSourceCoordinate(const QGeoCoordinate& coord) { if (m_sourceCoordinate == coord) return; m_sourceCoordinate = coord; emit sourceChanged(); } double MapController::sourceHorizontalAccuracy() const { return m_sourceHorizontalAccuracy; } void MapController::setSourceHorizontalAccuracy(double accu) { if (m_sourceHorizontalAccuracy == accu) return; m_sourceHorizontalAccuracy = accu; emit sourceChanged(); } double MapController::sourceDirection() const { return m_sourceDirection; } void MapController::setSourceDirection(double dir) { if (m_sourceDirection == dir) return; m_sourceDirection = dir; emit sourceChanged(); } bool MapController::overrideEnabled() const { return m_overrideEnabled; } void MapController::setOverrideEnabled(bool enabled) { if (m_overrideEnabled == enabled) return; m_overrideEnabled = enabled; emit overrideEnabledChanged(); } QGeoCoordinate MapController::overrideCoordinate() const { return m_overrideCoordinate; } void MapController::setOverrideCoordinate(const QGeoCoordinate& coord) { if (m_overrideCoordinate == coord) return; m_overrideCoordinate = coord; emit overrideCoordinateChanged(); } double MapController::overrideHorizontalAccuracy() const { return m_overrideHorizontalAccuracy; } void MapController::setOverrideHorizontalAccuracy(double accu) { if (m_overrideHorizontalAccuracy == accu) return; m_overrideHorizontalAccuracy = accu; emit overrideHorizontalAccuracyChanged(); } double MapController::overrideDirection() const { return m_overrideDirection; } void MapController::setOverrideDirection(double dir) { if (m_overrideDirection == dir) return; m_overrideDirection = dir; emit overrideDirectionChanged(); } gammaray-2.11.3/plugins/positioning/mapcontroller.h000066400000000000000000000066271413310443000224550ustar00rootroot00000000000000/* mapcontroller.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_MAPCONTROLLER_H #define GAMMARAY_MAPCONTROLLER_H #include #include namespace GammaRay { class MapController : public QObject { Q_OBJECT Q_PROPERTY(QGeoCoordinate sourceCoordinate READ sourceCoordinate WRITE setSourceCoordinate NOTIFY sourceChanged) Q_PROPERTY(double sourceHorizontalAccuracy READ sourceHorizontalAccuracy WRITE setSourceHorizontalAccuracy NOTIFY sourceChanged) Q_PROPERTY(double sourceDirection READ sourceDirection WRITE setSourceDirection NOTIFY sourceChanged) Q_PROPERTY(bool overrideEnabled READ overrideEnabled WRITE setOverrideEnabled NOTIFY overrideEnabledChanged) Q_PROPERTY(QGeoCoordinate overrideCoordinate READ overrideCoordinate WRITE setOverrideCoordinate NOTIFY overrideCoordinateChanged) Q_PROPERTY(double overrideHorizontalAccuracy READ overrideHorizontalAccuracy WRITE setOverrideHorizontalAccuracy NOTIFY overrideHorizontalAccuracyChanged) Q_PROPERTY(double overrideDirection READ overrideDirection WRITE setOverrideDirection NOTIFY overrideDirectionChanged) public: explicit MapController(QObject *parent = nullptr); ~MapController() override; QGeoCoordinate sourceCoordinate() const; void setSourceCoordinate(const QGeoCoordinate &coord); double sourceHorizontalAccuracy() const; void setSourceHorizontalAccuracy(double accu); double sourceDirection() const; void setSourceDirection(double dir); bool overrideEnabled() const; void setOverrideEnabled(bool enabled); QGeoCoordinate overrideCoordinate() const; void setOverrideCoordinate(const QGeoCoordinate &coord); double overrideHorizontalAccuracy() const; void setOverrideHorizontalAccuracy(double accu); double overrideDirection() const; void setOverrideDirection(double dir); signals: void centerOnPosition(); void sourceChanged(); void overrideEnabledChanged(); void overrideCoordinateChanged(); void overrideHorizontalAccuracyChanged(); void overrideDirectionChanged(); private: QGeoCoordinate m_sourceCoordinate; QGeoCoordinate m_overrideCoordinate; double m_sourceHorizontalAccuracy; double m_sourceDirection; double m_overrideHorizontalAccuracy; double m_overrideDirection; bool m_overrideEnabled; }; } #endif // GAMMARAY_MAPCONTROLLER_H gammaray-2.11.3/plugins/positioning/mapview.qml000066400000000000000000000141321413310443000215740ustar00rootroot00000000000000/* mapview.qml This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ import QtPositioning 5.3 import QtLocation 5.3 import QtQuick 2.0 Item { Plugin { id: mapPlugin required.mapping: Plugin.AnyMappingFeatures preferred: [ "osm" ] } Map { id: map anchors.fill: parent plugin: mapPlugin onCopyrightLinkActivated: Qt.openUrlExternally(link) MapPolyline { id: sourceTrace line { width: 3; color: "blue" } opacity: 0.5 Connections { target: _controller onOverrideCoordinateChanged: sourceTrace.addCoordinate(_controller.sourceCoordinate) } } MapPolyline { id: overrideTrace visible: _controller.overrideEnabled line { width: 3; color: "red" } opacity: 0.5 Connections { target: _controller onOverrideCoordinateChanged: overrideTrace.addCoordinate(_controller.overrideCoordinate) } } MapQuickItem { coordinate: _controller.sourceCoordinate anchorPoint { x: sourceMarker.width / 2; y: sourceMarker.height / 2 } sourceItem: Item { id: sourceMarker property color color: "blue" width: Math.abs(map.fromCoordinate(_controller.sourceCoordinate, false).x - map.fromCoordinate(_controller.sourceCoordinate.atDistanceAndAzimuth(_controller.sourceHorizontalAccuracy, 90), false).x) * 2 + 0 * map.zoomLevel height: width Rectangle { anchors.fill: parent color: parent.color opacity: 0.25 radius: width/2 } Rectangle { anchors.fill: parent color: "transparent" opacity: 0.75 radius: width/2 border.width: 1 border.color: parent.color } Rectangle { anchors.centerIn: parent color: parent.color width: 16 height: width opacity: 1 radius: width / 2 Image { anchors.fill: parent source: "qrc:/gammaray/positioning/direction_marker.png" smooth: true rotation: _controller.sourceDirection } } } } MapQuickItem { coordinate: _controller.overrideCoordinate anchorPoint { x: overrideMarker.width / 2; y: overrideMarker.height / 2 } sourceItem: Item { id: overrideMarker property color color: "red" width: Math.abs(map.fromCoordinate(_controller.overrideCoordinate, false).x - map.fromCoordinate(_controller.overrideCoordinate.atDistanceAndAzimuth(_controller.overrideHorizontalAccuracy, 90), false).x) * 2 + 0 * map.zoomLevel height: width visible: _controller.overrideEnabled Rectangle { id: accuracyBackground anchors.fill: parent color: parent.color opacity: 0.25 radius: width/2 } Rectangle { id: accuracyBorder anchors.fill: parent color: "transparent" opacity: 0.75 radius: width/2 border.width: 1 border.color: parent.color } Rectangle { id: positionMarker anchors.centerIn: parent color: parent.color width: 16 height: width opacity: 1 radius: width / 2 Image { id: directionMarker anchors.fill: positionMarker source: "qrc:/gammaray/positioning/direction_marker.png" smooth: true rotation: _controller.overrideDirection } } } } MouseArea { acceptedButtons: Qt.LeftButton anchors.fill: parent enabled: _controller.overrideEnabled onClicked: _controller.overrideCoordinate = map.toCoordinate(Qt.point(mouseX, mouseY), false); onWheel: { if (wheel.modifiers & Qt.ControlModifier) _controller.overrideDirection += wheel.angleDelta.y/24 else wheel.accepted = false } } } Connections { target: _controller onCenterOnPosition: map.center = _controller.overrideEnabled ? _controller.overrideCoordinate : _controller.sourceCoordinate } } gammaray-2.11.3/plugins/positioning/positioninfopropertyadaptor.cpp000066400000000000000000000056471413310443000260300ustar00rootroot00000000000000/* positioninfopropertyadaptor.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2018-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "positioninfopropertyadaptor.h" #include #include #include #include #if QT_VERSION < QT_VERSION_CHECK(5, 11, 0) Q_DECLARE_METATYPE(QGeoPositionInfo) #endif using namespace GammaRay; #define E(x) { QGeoPositionInfo:: x, #x } static const MetaEnum::Value attribute_table[] = { E(Direction), E(GroundSpeed), E(VerticalSpeed), E(MagneticVariation), E(HorizontalAccuracy), E(VerticalAccuracy) }; #undef E PositionInfoPropertyAdaptor::PositionInfoPropertyAdaptor(QObject *parent) : PropertyAdaptor(parent) { } PositionInfoPropertyAdaptor::~PositionInfoPropertyAdaptor() = default; int PositionInfoPropertyAdaptor::count() const { return MetaEnum::count(attribute_table); } PropertyData PositionInfoPropertyAdaptor::propertyData(int index) const { PropertyData pd; pd.setName(MetaEnum::enumToString(static_cast(index), attribute_table)); pd.setValue(object().variant().value().attribute(static_cast(index))); pd.setClassName(QStringLiteral("QGeoPositionInfo")); return pd; } PositionInfoPropertyAdaptorFactory* PositionInfoPropertyAdaptorFactory::s_instance = nullptr; PropertyAdaptor* PositionInfoPropertyAdaptorFactory::create(const ObjectInstance &oi, QObject *parent) const { if (oi.type() != ObjectInstance::Value) return nullptr; if (oi.variant().userType() != qMetaTypeId()) return nullptr; return new PositionInfoPropertyAdaptor(parent); } PositionInfoPropertyAdaptorFactory *PositionInfoPropertyAdaptorFactory::instance() { if (!s_instance) s_instance = new PositionInfoPropertyAdaptorFactory; return s_instance; } gammaray-2.11.3/plugins/positioning/positioninfopropertyadaptor.h000066400000000000000000000037271413310443000254720ustar00rootroot00000000000000/* positioninfopropertyadaptor.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2018-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_POSITIONINFOPROPERTYADAPTOR_H #define GAMMARAY_POSITIONINFOPROPERTYADAPTOR_H #include #include namespace GammaRay { class PositionInfoPropertyAdaptor : public PropertyAdaptor { Q_OBJECT public: explicit PositionInfoPropertyAdaptor(QObject *parent = nullptr); ~PositionInfoPropertyAdaptor() override; int count() const override; PropertyData propertyData(int index) const override; }; class PositionInfoPropertyAdaptorFactory : public AbstractPropertyAdaptorFactory { public: PropertyAdaptor *create(const ObjectInstance &oi, QObject *parent = nullptr) const override; static PositionInfoPropertyAdaptorFactory* instance(); private: static PositionInfoPropertyAdaptorFactory *s_instance; }; } #endif // GAMMARAY_POSITIONINFOPROPERTYADAPTOR_H gammaray-2.11.3/plugins/positioning/positioning.cpp000066400000000000000000000130051413310443000224550ustar00rootroot00000000000000/* positioning.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "positioning.h" #include "positioninfopropertyadaptor.h" #include #include #include #include #include #include #include #if QT_VERSION < QT_VERSION_CHECK(5, 11, 0) Q_DECLARE_METATYPE(QGeoPositionInfo) #endif Q_DECLARE_METATYPE(QGeoPositionInfoSource::Error) Q_DECLARE_METATYPE(QGeoPositionInfoSource::PositioningMethods) Q_DECLARE_METATYPE(QGeoSatelliteInfoSource::Error) Q_DECLARE_METATYPE(QGeoAreaMonitorSource::Error) Q_DECLARE_METATYPE(QGeoAreaMonitorSource::AreaMonitorFeatures) using namespace GammaRay; static QString positioningMethodsToString(QGeoPositionInfoSource::PositioningMethods methods) { if (methods == QGeoPositionInfoSource::NoPositioningMethods) return QStringLiteral("NoPositioningMethods"); if (methods == QGeoPositionInfoSource::AllPositioningMethods) return QStringLiteral("AllPositioningMethods"); QStringList l; if (methods & QGeoPositionInfoSource::SatellitePositioningMethods) l.push_back(QStringLiteral("SatellitePositioningMethods")); if (methods & QGeoPositionInfoSource::NonSatellitePositioningMethods) l.push_back(QStringLiteral("NonSatellitePositioningMethods")); return l.join(QLatin1Char('|')); } Positioning::Positioning(Probe *probe, QObject *parent) : PositioningInterface(parent) { qRegisterMetaTypeStreamOperators("QGeoPositionInfo"); registerMetaTypes(); connect(probe, &Probe::objectCreated, this, &Positioning::objectAdded); } void Positioning::objectAdded(QObject* obj) { if (auto geoInfoSource = qobject_cast(obj)) { if (geoInfoSource->sourceName() != QLatin1String("gammaray")) { if (positioningOverrideAvailable()) // we already have a proxy source taking care of things return; // until we have a proxy, just forward the position from the real source connect(geoInfoSource, &QGeoPositionInfoSource::positionUpdated, this, &Positioning::setPositionInfo); setPositionInfo(geoInfoSource->lastKnownPosition()); m_nonProxyPositionInfoSources.push_back(geoInfoSource); } else { // we previously got non-proxied sources, disconnect those if (!m_nonProxyPositionInfoSources.empty()) { std::for_each(m_nonProxyPositionInfoSources.begin(), m_nonProxyPositionInfoSources.end(), [this](QGeoPositionInfoSource *source) { disconnect(source, &QGeoPositionInfoSource::positionUpdated, this, &Positioning::setPositionInfo); }); m_nonProxyPositionInfoSources.clear(); } QMetaObject::invokeMethod(geoInfoSource, "setInterface", Q_ARG(PositioningInterface*, this)); } } } void Positioning::registerMetaTypes() { MetaObject *mo = nullptr; MO_ADD_METAOBJECT0(QGeoPositionInfo); MO_ADD_PROPERTY_RO(QGeoPositionInfo, coordinate); MO_ADD_PROPERTY (QGeoPositionInfo, timestamp, setTimestamp); MO_ADD_METAOBJECT1(QGeoPositionInfoSource, QObject); MO_ADD_PROPERTY_RO(QGeoPositionInfoSource, error); MO_ADD_PROPERTY_LD(QGeoPositionInfoSource, lastKnownPosition, [](QGeoPositionInfoSource *s) { return s->lastKnownPosition(); }); MO_ADD_PROPERTY_RO(QGeoPositionInfoSource, preferredPositioningMethods); MO_ADD_PROPERTY_RO(QGeoPositionInfoSource, supportedPositioningMethods); MO_ADD_METAOBJECT1(QGeoSatelliteInfoSource, QObject); MO_ADD_PROPERTY_RO(QGeoSatelliteInfoSource, error); MO_ADD_PROPERTY_RO(QGeoSatelliteInfoSource, sourceName); MO_ADD_METAOBJECT1(QGeoAreaMonitorSource, QObject); MO_ADD_PROPERTY_RO(QGeoAreaMonitorSource, error); MO_ADD_PROPERTY_RO(QGeoAreaMonitorSource, sourceName); MO_ADD_PROPERTY_RO(QGeoAreaMonitorSource, supportedAreaMonitorFeatures); VariantHandler::registerStringConverter(positioningMethodsToString); VariantHandler::registerStringConverter([](const QGeoPositionInfo &info) { return VariantHandler::displayString(info.coordinate()); }); VariantHandler::registerStringConverter([](const QGeoCoordinate &coord) { return coord.toString(); }); PropertyAdaptorFactory::registerFactory(PositionInfoPropertyAdaptorFactory::instance()); } gammaray-2.11.3/plugins/positioning/positioning.h000066400000000000000000000041311413310443000221220ustar00rootroot00000000000000/* positioning.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_POSITIONING_H #define GAMMARAY_POSITIONING_H #include "positioninginterface.h" #include #include #include #include QT_BEGIN_NAMESPACE class QGeoPositionInfoSource; QT_END_NAMESPACE namespace GammaRay { class Positioning : public PositioningInterface { Q_OBJECT public: explicit Positioning(Probe *probe, QObject *parent = nullptr); private slots: void objectAdded(QObject *obj); private: void registerMetaTypes(); std::vector m_nonProxyPositionInfoSources; }; class PositioningFactory : public QObject, public StandardToolFactory { Q_OBJECT Q_INTERFACES(GammaRay::ToolFactory) Q_PLUGIN_METADATA(IID "com.kdab.GammaRay.ToolFactory" FILE "gammaray_positioning.json") public: explicit PositioningFactory(QObject *parent = nullptr) : QObject(parent) { } }; } #endif // GAMMARAY_POSITIONING_H gammaray-2.11.3/plugins/positioning/positioningclient.cpp000066400000000000000000000024201413310443000236530ustar00rootroot00000000000000/* positioningclient.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "positioningclient.h" using namespace GammaRay; PositioningClient::PositioningClient(QObject* parent): PositioningInterface(parent) { } gammaray-2.11.3/plugins/positioning/positioningclient.h000066400000000000000000000027421413310443000233270ustar00rootroot00000000000000/* positioningclient.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_POSITIONINGCLIENT_H #define GAMMARAY_POSITIONINGCLIENT_H #include "positioninginterface.h" namespace GammaRay { class PositioningClient : public PositioningInterface { Q_OBJECT Q_INTERFACES(GammaRay::PositioningInterface) public: explicit PositioningClient(QObject* parent = nullptr); }; } #endif // GAMMARAY_POSITIONINGCLIENT_H gammaray-2.11.3/plugins/positioning/positioninginterface.cpp000066400000000000000000000053231413310443000243420ustar00rootroot00000000000000/* positioninginterface.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "positioninginterface.h" #include using namespace GammaRay; PositioningInterface::PositioningInterface(QObject* parent) : QObject(parent) , m_positioningOverrideAvailable(false) , m_positioningOverrideEnabled(false) { ObjectBroker::registerObject(this); } QGeoPositionInfo PositioningInterface::positionInfo() const { return m_postionInfo; } void PositioningInterface::setPositionInfo(const QGeoPositionInfo& info) { if (m_postionInfo == info) return; m_postionInfo = info; emit positionInfoChanged(); } bool PositioningInterface::positioningOverrideAvailable() const { return m_positioningOverrideAvailable; } void PositioningInterface::setPositioningOverrideAvailable(bool available) { if (m_positioningOverrideAvailable == available) return; m_positioningOverrideAvailable = available; emit positioningOverrideAvailableChanged(); } bool PositioningInterface::positioningOverrideEnabled() const { return m_positioningOverrideEnabled; } void PositioningInterface::setPositioningOverrideEnabled(bool enabled) { if (m_positioningOverrideEnabled == enabled) return; m_positioningOverrideEnabled = enabled; emit positioningOverrideEnabledChanged(); } QGeoPositionInfo PositioningInterface::positionInfoOverride() const { return m_postionInfoOverride; } void PositioningInterface::setPositionInfoOverride(const QGeoPositionInfo& info) { if (m_postionInfoOverride == info) return; m_postionInfoOverride = info; emit positionInfoOverrideChanged(); } gammaray-2.11.3/plugins/positioning/positioninginterface.h000066400000000000000000000062141413310443000240070ustar00rootroot00000000000000/* positioninginterface.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_POSITIONINGINTERFACE_H #define GAMMARAY_POSITIONINGINTERFACE_H #include #include namespace GammaRay { class PositioningInterface : public QObject { Q_OBJECT Q_PROPERTY(QGeoPositionInfo positionInfo READ positionInfo WRITE setPositionInfo NOTIFY positionInfoChanged) Q_PROPERTY(bool positioningOverrideAvailable READ positioningOverrideAvailable WRITE setPositioningOverrideAvailable NOTIFY positioningOverrideAvailableChanged) Q_PROPERTY(bool positioningOverrideEnabled READ positioningOverrideEnabled WRITE setPositioningOverrideEnabled NOTIFY positioningOverrideEnabledChanged) Q_PROPERTY(QGeoPositionInfo positionInfoOverride READ positionInfoOverride WRITE setPositionInfoOverride NOTIFY positionInfoOverrideChanged) public: explicit PositioningInterface(QObject* parent = nullptr); /** Position info from the source. */ QGeoPositionInfo positionInfo() const; /** Overriding the position information is possible. */ bool positioningOverrideAvailable() const; void setPositioningOverrideAvailable(bool available); /** Override source position info with the override provided by the client. */ bool positioningOverrideEnabled() const; void setPositioningOverrideEnabled(bool enabled); /** Override position info provided by the client. */ QGeoPositionInfo positionInfoOverride() const; void setPositionInfoOverride(const QGeoPositionInfo &info); public slots: void setPositionInfo(const QGeoPositionInfo &info); signals: void positionInfoChanged(); void positioningOverrideAvailableChanged(); void positioningOverrideEnabledChanged(); void positionInfoOverrideChanged(); private: QGeoPositionInfo m_postionInfo; QGeoPositionInfo m_postionInfoOverride; bool m_positioningOverrideAvailable; bool m_positioningOverrideEnabled; }; } QT_BEGIN_NAMESPACE Q_DECLARE_INTERFACE(GammaRay::PositioningInterface, "com.kdab.GammaRay.PositioningInterface") QT_END_NAMESPACE #endif // GAMMARAY_POSITIONINGINTERFACE_H gammaray-2.11.3/plugins/positioning/positioningwidget.cpp000066400000000000000000000250621413310443000236670ustar00rootroot00000000000000/* positioningwidget.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "positioningwidget.h" #include "ui_positioningwidget.h" #include "positioningclient.h" #include "mapcontroller.h" #include #include #include #include #include #include #include #include #include #include #if QT_VERSION < QT_VERSION_CHECK(5, 11, 0) Q_DECLARE_METATYPE(QGeoPositionInfo) #endif using namespace GammaRay; static QObject *createPositioningClient(const QString &name, QObject *parent) { Q_UNUSED(name); return new PositioningClient(parent); } PositioningWidget::PositioningWidget(QWidget* parent): QWidget(parent), ui(new Ui::PositioningWidget), m_mapController(new MapController(this)), m_replaySource(nullptr), m_updateLock(false) { ui->setupUi(this); auto mapView = new QQuickWidget; mapView->rootContext()->setContextProperty(QStringLiteral("_controller"), m_mapController); ui->topLayout->addWidget(mapView); ObjectBroker::registerClientObjectFactoryCallback(createPositioningClient); m_interface = ObjectBroker::object(); Q_ASSERT(m_interface); connect(m_interface, &PositioningInterface::positionInfoChanged, this, [this]() { m_mapController->setSourceCoordinate(m_interface->positionInfo().coordinate()); m_mapController->setSourceDirection(m_interface->positionInfo().attribute(QGeoPositionInfo::Direction)); m_mapController->setSourceHorizontalAccuracy(m_interface->positionInfo().attribute(QGeoPositionInfo::HorizontalAccuracy)); if (ui->overrideBox->isChecked()) return; setUiToPosition(m_interface->positionInfo()); }); connect(ui->overrideBox, &QCheckBox::toggled, this, [this](bool enabled) { if (enabled && m_interface->positionInfoOverride().isValid()) { setUiToPosition(m_interface->positionInfoOverride()); } else if (!enabled && m_interface->positionInfo().isValid()) { setUiToPosition(m_interface->positionInfo()); } updateWidgetState(); updatePosition(); }); connect(ui->latitude, QOverload::of(&QDoubleSpinBox::valueChanged), this, &PositioningWidget::updatePosition); connect(ui->longitude, QOverload::of(&QDoubleSpinBox::valueChanged), this, &PositioningWidget::updatePosition); connect(ui->horizontalSpeed, QOverload::of(&QDoubleSpinBox::valueChanged), this, &PositioningWidget::updatePosition); connect(ui->horizontalAccuracy, QOverload::of(&QSpinBox::valueChanged), this, &PositioningWidget::updatePosition); connect(ui->altitude, QOverload::of(&QSpinBox::valueChanged), this, &PositioningWidget::updatePosition); connect(ui->verticalSpeed, QOverload::of(&QDoubleSpinBox::valueChanged), this, &PositioningWidget::updatePosition); connect(ui->verticalAccuracy, QOverload::of(&QSpinBox::valueChanged), this, &PositioningWidget::updatePosition); connect(ui->direction, QOverload::of(&QSpinBox::valueChanged), this, &PositioningWidget::updatePosition); connect(ui->magneticVariation, QOverload::of(&QSpinBox::valueChanged), this, &PositioningWidget::updatePosition); connect(ui->timestamp, &QDateTimeEdit::dateTimeChanged, this, &PositioningWidget::updatePosition); connect(m_mapController, &MapController::overrideCoordinateChanged, this, [this]() { if (m_updateLock) return; m_updateLock = true; ui->latitude->setValue(m_mapController->overrideCoordinate().latitude()); ui->longitude->setValue(m_mapController->overrideCoordinate().longitude()); ui->timestamp->setDateTime(QDateTime::currentDateTime()); m_updateLock = false; updatePosition(); }); connect(m_mapController, &MapController::overrideDirectionChanged, this, [this]() { if (m_updateLock) return; m_updateLock = true; ui->direction->setValue(m_mapController->overrideDirection()); ui->timestamp->setDateTime(QDateTime::currentDateTime()); m_updateLock = false; updatePosition(); }); mapView->setResizeMode(QQuickWidget::SizeRootObjectToView); mapView->setSource(QUrl(QStringLiteral("qrc:/gammaray/positioning/mapview.qml"))); connect(ui->actionCenterOn, &QAction::triggered, m_mapController, &MapController::centerOnPosition); addAction(ui->actionCenterOn); connect(ui->actionLoadNMEA, &QAction::triggered, this, &PositioningWidget::loadNmeaFile); addAction(ui->actionLoadNMEA); new PropertyBinder(m_interface, "positioningOverrideAvailable", ui->overrideBox, "enabled"); new PropertyBinder(m_interface, "positioningOverrideEnabled", ui->overrideBox, "checked"); new PropertyBinder(m_interface, "positioningOverrideEnabled", m_mapController, "overrideEnabled"); updateWidgetState(); } PositioningWidget::~PositioningWidget() = default; void PositioningWidget::updatePosition() { if (m_updateLock || !ui->overrideBox->isChecked()) return; m_updateLock = true; QGeoPositionInfo info; info.setCoordinate(QGeoCoordinate(ui->latitude->value(), ui->longitude->value(), ui->altitude->value())); info.setTimestamp(ui->timestamp->dateTime()); info.setAttribute(QGeoPositionInfo::Direction, ui->direction->value()); info.setAttribute(QGeoPositionInfo::GroundSpeed, ui->horizontalSpeed->value()); info.setAttribute(QGeoPositionInfo::VerticalSpeed, ui->verticalSpeed->value()); info.setAttribute(QGeoPositionInfo::MagneticVariation, ui->magneticVariation->value()); info.setAttribute(QGeoPositionInfo::HorizontalAccuracy, ui->horizontalAccuracy->value()); info.setAttribute(QGeoPositionInfo::VerticalAccuracy, ui->verticalAccuracy->value()); m_interface->setPositionInfoOverride(info); m_mapController->setOverrideCoordinate(QGeoCoordinate(ui->latitude->value(), ui->longitude->value())); m_mapController->setOverrideHorizontalAccuracy(ui->horizontalAccuracy->value()); m_mapController->setOverrideDirection(ui->direction->value()); m_updateLock = false; } void PositioningWidget::replayPosition() { const auto pos = m_replaySource->lastKnownPosition(); setUiToPosition(pos); updatePosition(); } void PositioningWidget::loadNmeaFile() { auto fileName = QFileDialog::getOpenFileName(this, tr("Load NMEA file")); if (fileName.isEmpty()) return; QScopedPointer file(new QFile(fileName, this)); if (!file->open(QFile::ReadOnly)) { QMessageBox::critical(this, tr("Failed to open NMEA file"), tr("Could not open '%1': %2.").arg(fileName, file->errorString())); return; } if (m_replaySource) { auto dev = m_replaySource->device(); delete m_replaySource; delete dev; } m_replaySource = new QNmeaPositionInfoSource(QNmeaPositionInfoSource::SimulationMode, this); m_replaySource->setDevice(file.take()); connect(m_replaySource, &QGeoPositionInfoSource::positionUpdated, this, &PositioningWidget::replayPosition); m_replaySource->startUpdates(); // TODO error handling connect(m_replaySource, &QNmeaPositionInfoSource::updateTimeout, this, []() { qDebug() << "NMEA source update timeout!"; }); connect(m_replaySource, QOverload::of(&QGeoPositionInfoSource::error), this, &PositioningWidget::nmeaError); } void PositioningWidget::nmeaError() { qDebug() << m_replaySource->error(); } void PositioningWidget::updateWidgetState() { const auto e = ui->overrideBox->isEnabled() && ui->overrideBox->isChecked(); ui->latitude->setEnabled(e); ui->longitude->setEnabled(e); ui->horizontalSpeed->setEnabled(e); ui->horizontalAccuracy->setEnabled(e); ui->altitude->setEnabled(e); ui->verticalSpeed->setEnabled(e); ui->verticalAccuracy->setEnabled(e); ui->direction->setEnabled(e); ui->magneticVariation->setEnabled(e); ui->timestamp->setEnabled(e); ui->actionLoadNMEA->setEnabled(e); } void PositioningWidget::setUiToPosition(const QGeoPositionInfo &pos) { m_updateLock = true; if (pos.coordinate().type() != QGeoCoordinate::InvalidCoordinate) { ui->latitude->setValue(pos.coordinate().latitude()); ui->longitude->setValue(pos.coordinate().longitude()); } if (pos.coordinate().type() == QGeoCoordinate::Coordinate3D) ui->altitude->setValue(pos.coordinate().altitude()); if (pos.hasAttribute(QGeoPositionInfo::Direction)) ui->direction->setValue(pos.attribute(QGeoPositionInfo::Direction)); if (pos.hasAttribute(QGeoPositionInfo::GroundSpeed)) ui->horizontalSpeed->setValue(pos.attribute(QGeoPositionInfo::GroundSpeed)); if (pos.hasAttribute(QGeoPositionInfo::VerticalSpeed)) ui->verticalSpeed->setValue(pos.attribute(QGeoPositionInfo::VerticalSpeed)); if (pos.hasAttribute(QGeoPositionInfo::MagneticVariation)) ui->magneticVariation->setValue(pos.attribute(QGeoPositionInfo::MagneticVariation)); if (pos.hasAttribute(QGeoPositionInfo::HorizontalAccuracy)) ui->horizontalAccuracy->setValue(pos.attribute(QGeoPositionInfo::HorizontalAccuracy)); if (pos.hasAttribute(QGeoPositionInfo::VerticalAccuracy)) ui->verticalAccuracy->setValue(pos.attribute(QGeoPositionInfo::VerticalAccuracy)); ui->timestamp->setDateTime(pos.timestamp()); m_updateLock = false; } void PositioningUiFactory::initUi() { qRegisterMetaTypeStreamOperators("QGeoPositionInfo"); } gammaray-2.11.3/plugins/positioning/positioningwidget.h000066400000000000000000000045301413310443000233310ustar00rootroot00000000000000/* positioningwidget.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_POSITIONINGWIDGET_H #define GAMMARAY_POSITIONINGWIDGET_H #include #include #include QT_BEGIN_NAMESPACE class QGeoPositionInfo; class QNmeaPositionInfoSource; QT_END_NAMESPACE namespace GammaRay { namespace Ui { class PositioningWidget; } class MapController; class PositioningInterface; class PositioningWidget : public QWidget { Q_OBJECT public: explicit PositioningWidget(QWidget *parent = nullptr); ~PositioningWidget() override; private slots: void updatePosition(); void replayPosition(); void loadNmeaFile(); void nmeaError(); void updateWidgetState(); private: void setUiToPosition(const QGeoPositionInfo &pos); QScopedPointer ui; PositioningInterface *m_interface; MapController *m_mapController; QNmeaPositionInfoSource *m_replaySource; bool m_updateLock; }; class PositioningUiFactory : public QObject, public StandardToolUiFactory { Q_OBJECT Q_INTERFACES(GammaRay::ToolUiFactory) Q_PLUGIN_METADATA(IID "com.kdab.GammaRay.ToolUiFactory" FILE "gammaray_positioning.json") public: void initUi() override; }; } #endif // GAMMARAY_POSITIONINGWIDGET_H gammaray-2.11.3/plugins/positioning/positioningwidget.qrc000066400000000000000000000002131413310443000236610ustar00rootroot00000000000000 mapview.qml direction_marker.png gammaray-2.11.3/plugins/positioning/positioningwidget.ui000066400000000000000000000210561413310443000235210ustar00rootroot00000000000000 GammaRay::PositioningWidget 0 0 1131 150 Override &Latitude: latitude ° 6 -90.000000000000000 90.000000000000000 0.500000000000000 &Longitude: longitude true ° 6 -180.000000000000000 180.000000000000000 0.500000000000000 Speed: horizontalSpeed m/s 100.000000000000000 0.500000000000000 Acc&uracy: horizontalAccuracy m 100000 10 10 Timestamp: timestamp yyyy-MM-dd hh:mm:ss.zzz t Qt::Horizontal 17 20 Al&titude: altitude m -500 10000 50 Speed: verticalSpeed m/s 100.000000000000000 0.500000000000000 Accurac&y: verticalAccuracy m 1000 10 10 &Direction: direction true ° 360 5 Ma&gnetic variation: magneticVariation ° 360 Load NMEA file... Load a GPS recording from an NMEA file. Center on position Center map on current position. F3 gammaray-2.11.3/plugins/qmlsupport/000077500000000000000000000000001413310443000172745ustar00rootroot00000000000000gammaray-2.11.3/plugins/qmlsupport/CMakeLists.txt000066400000000000000000000017711413310443000220420ustar00rootroot00000000000000# probe plugin if (NOT GAMMARAY_CLIENT_ONLY_BUILD) set(gammaray_qmlsupport_srcs qmlsupport.cpp qmllistpropertyadaptor.cpp qmlattachedpropertyadaptor.cpp qjsvaluepropertyadaptor.cpp qmlcontextpropertyadaptor.cpp qmlcontextextension.cpp qmlcontextmodel.cpp qmltypeextension.cpp ) gammaray_add_plugin(gammaray_qmlsupport JSON gammaray_qmlsupport.json SOURCES ${gammaray_qmlsupport_srcs}) target_include_directories(gammaray_qmlsupport SYSTEM PRIVATE ${Qt5Qml_PRIVATE_INCLUDE_DIRS}) target_link_libraries(gammaray_qmlsupport gammaray_core Qt5::Qml) endif() if(NOT Qt5Qml_VERSION VERSION_LESS 5.10) target_sources(gammaray_qmlsupport PUBLIC qmlbindingprovider.cpp) endif() # ui plugin if(GAMMARAY_BUILD_UI) set(gammaray_qmlsupport_ui_srcs qmlsupportuifactory.cpp qmlcontexttab.cpp qmltypetab.cpp ) gammaray_add_plugin(gammaray_qmlsupport_ui JSON gammaray_qmlsupport.json SOURCES ${gammaray_qmlsupport_ui_srcs}) target_link_libraries(gammaray_qmlsupport_ui gammaray_ui) endif() gammaray-2.11.3/plugins/qmlsupport/gammaray_qmlsupport.json000066400000000000000000000002431413310443000242720ustar00rootroot00000000000000{ "hidden": true, "id": "gammaray_qmlsupport", "name": "QML Support", "name[de]": "QML Unterstützung", "types": [ "QJSEngine" ] } gammaray-2.11.3/plugins/qmlsupport/qjsvaluepropertyadaptor.cpp000066400000000000000000000053411413310443000250150ustar00rootroot00000000000000/* qjsvaluepropertyadaptor.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "qjsvaluepropertyadaptor.h" #include #include #include using namespace GammaRay; QJSValuePropertyAdaptor::QJSValuePropertyAdaptor(QObject *parent) : PropertyAdaptor(parent) { } QJSValuePropertyAdaptor::~QJSValuePropertyAdaptor() = default; int QJSValuePropertyAdaptor::count() const { auto jsValue = object().variant().value(); if (jsValue.isArray()) return jsValue.toVariant().value().size(); return 0; } PropertyData QJSValuePropertyAdaptor::propertyData(int index) const { PropertyData pd; auto jsValue = object().variant().value(); if (jsValue.isArray()) { auto list = jsValue.toVariant().value(); if (index < 0 || index >= list.size()) return pd; pd.setName(QString::number(index)); pd.setValue(list.at(index)); pd.setClassName(QStringLiteral("QJSValue")); } return pd; } QJSValuePropertyAdaptorFactory *QJSValuePropertyAdaptorFactory::s_instance = nullptr; PropertyAdaptor *QJSValuePropertyAdaptorFactory::create(const ObjectInstance &oi, QObject *parent) const { if (oi.type() != ObjectInstance::QtVariant) return nullptr; if (!oi.variant().isValid() || !oi.variant().canConvert()) return nullptr; return new QJSValuePropertyAdaptor(parent); } QJSValuePropertyAdaptorFactory *QJSValuePropertyAdaptorFactory::instance() { if (!s_instance) s_instance = new QJSValuePropertyAdaptorFactory; return s_instance; } gammaray-2.11.3/plugins/qmlsupport/qjsvaluepropertyadaptor.h000066400000000000000000000036231413310443000244630ustar00rootroot00000000000000/* qjsvaluepropertyadaptor.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef QJSVALUEPROPERTYADAPTOR_H #define QJSVALUEPROPERTYADAPTOR_H #include #include namespace GammaRay { class QJSValuePropertyAdaptor : public PropertyAdaptor { Q_OBJECT public: explicit QJSValuePropertyAdaptor(QObject *parent = nullptr); ~QJSValuePropertyAdaptor() override; int count() const override; PropertyData propertyData(int index) const override; }; class QJSValuePropertyAdaptorFactory : public AbstractPropertyAdaptorFactory { public: PropertyAdaptor *create(const ObjectInstance &oi, QObject *parent = nullptr) const override; static QJSValuePropertyAdaptorFactory *instance(); private: static QJSValuePropertyAdaptorFactory *s_instance; }; } #endif // QJSVALUEPROPERTYADAPTOR_H gammaray-2.11.3/plugins/qmlsupport/qmlattachedpropertyadaptor.cpp000066400000000000000000000072471413310443000254610ustar00rootroot00000000000000/* qmlattachedpropertyadaptor.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "qmlattachedpropertyadaptor.h" #include "qmltypeutil.h" #include #include #include #include using namespace GammaRay; QmlAttachedPropertyAdaptor::QmlAttachedPropertyAdaptor(QObject *parent) : PropertyAdaptor(parent) { } QmlAttachedPropertyAdaptor::~QmlAttachedPropertyAdaptor() = default; void QmlAttachedPropertyAdaptor::doSetObject(const ObjectInstance &oi) { auto data = QQmlData::get(oi.qtObject()); Q_ASSERT(data); Q_ASSERT(data->hasExtendedData()); Q_ASSERT(data->attachedProperties()); m_attachedTypes.reserve(data->attachedProperties()->size()); for (auto it = data->attachedProperties()->constBegin(); it != data->attachedProperties()->constEnd(); ++it) m_attachedTypes.push_back(it.key()); } int QmlAttachedPropertyAdaptor::count() const { return m_attachedTypes.size(); } PropertyData QmlAttachedPropertyAdaptor::propertyData(int index) const { PropertyData pd; if (!object().isValid()) return pd; Q_ASSERT(index >= 0); Q_ASSERT(index < m_attachedTypes.size()); auto data = QQmlData::get(object().qtObject()); if (!data) return pd; auto it = data->attachedProperties()->constFind(m_attachedTypes.at(index)); if (it == data->attachedProperties()->constEnd()) return pd; auto qmlType = QQmlMetaType::qmlType(it.value()->metaObject()); if (QmlType::isValid(qmlType) && !QmlType::callable(qmlType)->elementName().isEmpty()) pd.setName(QmlType::callable(qmlType)->elementName()); else pd.setName(it.value()->metaObject()->className()); pd.setValue(QVariant::fromValue(it.value())); pd.setClassName(it.value()->metaObject()->className()); return pd; } QmlAttachedPropertyAdaptorFactory *QmlAttachedPropertyAdaptorFactory::s_instance = nullptr; PropertyAdaptor *QmlAttachedPropertyAdaptorFactory::create(const ObjectInstance &oi, QObject *parent) const { if (oi.type() != ObjectInstance::QtObject || !oi.qtObject()) return nullptr; auto data = QQmlData::get(oi.qtObject()); if (!data || !data->hasExtendedData() || !data->attachedProperties() || data->attachedProperties()->isEmpty()) return nullptr; return new QmlAttachedPropertyAdaptor(parent); } QmlAttachedPropertyAdaptorFactory *QmlAttachedPropertyAdaptorFactory::instance() { if (!s_instance) s_instance = new QmlAttachedPropertyAdaptorFactory; return s_instance; } gammaray-2.11.3/plugins/qmlsupport/qmlattachedpropertyadaptor.h000066400000000000000000000043571413310443000251250ustar00rootroot00000000000000/* qmlattachedpropertyadaptor.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_QMLATTACHEDPROPERTYADAPTOR_H #define GAMMARAY_QMLATTACHEDPROPERTYADAPTOR_H #include #include #include namespace GammaRay { class QmlAttachedPropertyAdaptor : public PropertyAdaptor { Q_OBJECT public: explicit QmlAttachedPropertyAdaptor(QObject *parent = nullptr); ~QmlAttachedPropertyAdaptor() override; int count() const override; PropertyData propertyData(int index) const override; protected: void doSetObject(const ObjectInstance &oi) override; private: #if QT_VERSION >= QT_VERSION_CHECK(5, 12, 4) QVector m_attachedTypes; #else QVector m_attachedTypes; #endif }; /** QML attached property adaptor. */ class QmlAttachedPropertyAdaptorFactory : public AbstractPropertyAdaptorFactory { public: PropertyAdaptor *create(const ObjectInstance &oi, QObject *parent = nullptr) const override; static QmlAttachedPropertyAdaptorFactory *instance(); private: static QmlAttachedPropertyAdaptorFactory *s_instance; }; } #endif // GAMMARAY_QMLATTACHEDPROPERTYADAPTOR_H gammaray-2.11.3/plugins/qmlsupport/qmlbindingprovider.cpp000066400000000000000000000106501413310443000237010ustar00rootroot00000000000000/* qmlbindingprovider.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ // Own #include "qmlbindingprovider.h" #include #include // Qt #include #include #include #include #include #include using namespace GammaRay; QQmlAbstractBinding *QmlBindingProvider::bindingForProperty(QObject *obj, int propertyIndex) const { auto data = QQmlData::get(obj); if (!data || !data->hasBindingBit(propertyIndex)) return nullptr; auto b = data->bindings; while (b) { int index; index = b->targetPropertyIndex().coreIndex(); if (index == propertyIndex) { return b; } b = b->nextBinding(); } return nullptr; } bool QmlBindingProvider::canProvideBindingsFor(QObject *object) const { return QQmlData::get(object); } void QmlBindingProvider::fetchSourceLocationFor(BindingNode *node, QQmlBinding *binding) const { QV4::Function *function = binding->function(); if (function) { QQmlSourceLocation loc = function->sourceLocation(); node->setSourceLocation(SourceLocation::fromOneBased(QUrl(loc.sourceFile), loc.line, loc.column)); } } std::vector> QmlBindingProvider::findDependenciesFor(BindingNode *node) const { std::vector> dependencies; if (node->hasFoundBindingLoop()) // Don't look for further dependencies, if this is already known to be part of a loop. return dependencies; QQmlAbstractBinding *abstractBinding = QQmlPropertyPrivate::binding(node->object(), QQmlPropertyIndex::fromEncoded(node->propertyIndex())); QQmlBinding *binding = dynamic_cast(abstractBinding); if (!binding) return dependencies; fetchSourceLocationFor(node, binding); // While we have the QQmlBinding at hand, let's grab the source location const auto bindingDependencies = binding->dependencies(); for (const auto &dependency : bindingDependencies) { BindingNode *childNode = new BindingNode(dependency.object(), dependency.index(), node); QQmlContext *ctx = QQmlEngine::contextForObject(dependency.object()); if (ctx) { QString id = ctx->nameForObject(dependency.object()); if (!id.isEmpty()) childNode->setCanonicalName(QStringLiteral("%1.%2").arg(id, childNode->canonicalName())); } dependencies.push_back(std::unique_ptr(childNode)); } return dependencies; } std::vector> QmlBindingProvider::findBindingsFor(QObject *obj) const { std::vector> bindings; auto data = QQmlData::get(obj); if (!data) return bindings; auto b = data->bindings; while (b) { BindingNode *node = new BindingNode(obj, b->targetPropertyIndex().coreIndex()); QQmlContext *ctx = QQmlEngine::contextForObject(obj); if (ctx) { QString id = ctx->nameForObject(obj); if (!id.isEmpty()) node->setCanonicalName(QStringLiteral("%1.%2").arg(id, node->canonicalName())); } bindings.push_back(std::unique_ptr(node)); b = b->nextBinding(); } return bindings; } gammaray-2.11.3/plugins/qmlsupport/qmlbindingprovider.h000066400000000000000000000042771413310443000233560ustar00rootroot00000000000000/* qmlbindingprovider.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_QMLBINDINGPROVIDER_H #define GAMMARAY_QMLBINDINGPROVIDER_H // Own #include #include // Qt #include // Std #include #include QT_BEGIN_NAMESPACE class QQmlAbstractBinding; class QQmlBinding; QT_END_NAMESPACE namespace GammaRay { class QmlBindingProvider : public AbstractBindingProvider { public: std::vector> findBindingsFor(QObject * obj) const override; std::vector> findDependenciesFor(GammaRay::BindingNode * binding) const override; bool canProvideBindingsFor(QObject *object) const override; private: std::unique_ptr bindingNodeFromQmlProperty(QQmlProperty property, BindingNode *parent) const; BindingNode *bindingNodeFromBinding(QQmlAbstractBinding *binding) const; void fetchSourceLocationFor(BindingNode *node, QQmlBinding *binding) const; QQmlAbstractBinding *bindingForProperty(QObject *obj, int propertyIndex) const; }; } #endif // GAMMARAY_QMLBINDINGPROVIDER_H gammaray-2.11.3/plugins/qmlsupport/qmlcontextextension.cpp000066400000000000000000000057411413310443000241420ustar00rootroot00000000000000/* qmlcontextextension.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "qmlcontextextension.h" #include "qmlcontextmodel.h" #include #include #include #include #include #include #include #include #include using namespace GammaRay; QmlContextExtension::QmlContextExtension(PropertyController *controller) : PropertyControllerExtension(controller->objectBaseName() + ".qmlContext") , m_contextModel(new QmlContextModel(controller)) , m_propertyModel(new AggregatedPropertyModel(controller)) { controller->registerModel(m_contextModel, QStringLiteral("qmlContextModel")); auto contextSelectionModel = ObjectBroker::selectionModel(m_contextModel); QObject::connect(contextSelectionModel, &QItemSelectionModel::selectionChanged, [this](const QItemSelection &selection) { contextSelected(selection); }); controller->registerModel(m_propertyModel, QStringLiteral("qmlContextPropertyModel")); } QmlContextExtension::~QmlContextExtension() = default; bool QmlContextExtension::setQObject(QObject *object) { if (!object) return false; auto context = qobject_cast(object); if (!context) { auto data = QQmlData::get(object); if (data && data->context) context = data->context->asQQmlContext(); } m_contextModel->setContext(context); return context; } void QmlContextExtension::contextSelected(const QItemSelection &selection) { if (selection.isEmpty()) { m_propertyModel->setObject(nullptr); return; } const auto idx = selection.first().topLeft(); const auto context = idx.data(ObjectModel::ObjectRole).value(); m_propertyModel->setObject(context); } gammaray-2.11.3/plugins/qmlsupport/qmlcontextextension.h000066400000000000000000000034351413310443000236050ustar00rootroot00000000000000/* qmlcontextextension.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_QMLCONTEXTEXTENSION_H #define GAMMARAY_QMLCONTEXTEXTENSION_H #include QT_BEGIN_NAMESPACE class QItemSelection; QT_END_NAMESPACE namespace GammaRay { class AggregatedPropertyModel; class QmlContextModel; class QmlContextExtension : public PropertyControllerExtension { public: explicit QmlContextExtension(PropertyController *controller); ~QmlContextExtension(); bool setQObject(QObject *object) override; private: void contextSelected(const QItemSelection &selection); QmlContextModel *m_contextModel; AggregatedPropertyModel *m_propertyModel; }; } #endif // GAMMARAY_QMLCONTEXTEXTENSION_H gammaray-2.11.3/plugins/qmlsupport/qmlcontextmodel.cpp000066400000000000000000000067041413310443000232260ustar00rootroot00000000000000/* qmlcontextmodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "qmlcontextmodel.h" #include #include #include #include using namespace GammaRay; QmlContextModel::QmlContextModel(QObject *parent) : QAbstractTableModel(parent) { } QmlContextModel::~QmlContextModel() = default; void QmlContextModel::clear() { beginRemoveRows(QModelIndex(), 0, rowCount() - 1); m_contexts.clear(); endRemoveRows(); } void QmlContextModel::setContext(QQmlContext *leafContext) { if (!m_contexts.isEmpty()) { if (m_contexts.last() != leafContext) clear(); else return; } if (!leafContext) return; Q_ASSERT(m_contexts.isEmpty()); QVector tmp; auto context = leafContext; do { tmp.push_back(context); context = context->parentContext(); } while (context); std::reverse(tmp.begin(), tmp.end()); beginInsertRows(QModelIndex(), 0, tmp.size() - 1); m_contexts = tmp; endInsertRows(); } int QmlContextModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); return 2; } int QmlContextModel::rowCount(const QModelIndex &parent) const { if (parent.isValid()) return 0; return m_contexts.size(); } QVariant QmlContextModel::data(const QModelIndex &index, int role) const { if (m_contexts.isEmpty() || !index.isValid()) return QVariant(); if (role == Qt::DisplayRole) { auto context = m_contexts.at(index.row()); switch (index.column()) { case 0: return Util::shortDisplayString(context); case 1: if (context->baseUrl().scheme() == QLatin1String("file")) // ### use SourceLocation for this! return context->baseUrl().path(); return context->baseUrl().toString(); } } else if (role == ObjectModel::ObjectRole) { return QVariant::fromValue(m_contexts.at(index.row())); } return QVariant(); } QVariant QmlContextModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { switch (section) { case 0: return tr("Context"); case 1: return tr("Location"); } } return QAbstractTableModel::headerData(section, orientation, role); } gammaray-2.11.3/plugins/qmlsupport/qmlcontextmodel.h000066400000000000000000000037711413310443000226740ustar00rootroot00000000000000/* qmlcontextmodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_QMLCONTEXTMODEL_H #define GAMMARAY_QMLCONTEXTMODEL_H #include #include QT_BEGIN_NAMESPACE class QQmlContext; QT_END_NAMESPACE namespace GammaRay { class QmlContextModel : public QAbstractTableModel { Q_OBJECT public: explicit QmlContextModel(QObject *parent = nullptr); ~QmlContextModel() override; void clear(); void setContext(QQmlContext *leafContext); int rowCount(const QModelIndex &parent = QModelIndex()) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; private: QVector m_contexts; }; } #endif // GAMMARAY_QMLCONTEXTMODEL_H gammaray-2.11.3/plugins/qmlsupport/qmlcontextpropertyadaptor.cpp000066400000000000000000000077441413310443000253720ustar00rootroot00000000000000/* qmlcontextpropertyadaptor.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "qmlcontextpropertyadaptor.h" #include #include #include #include #include using namespace GammaRay; QmlContextPropertyAdaptor::QmlContextPropertyAdaptor(QObject *parent) : PropertyAdaptor(parent) { } QmlContextPropertyAdaptor::~QmlContextPropertyAdaptor() = default; int QmlContextPropertyAdaptor::count() const { return m_contextPropertyNames.size(); } PropertyData QmlContextPropertyAdaptor::propertyData(int index) const { PropertyData pd; if (!object().isValid()) return pd; Q_ASSERT(index >= 0); Q_ASSERT(index < m_contextPropertyNames.size()); auto context = qobject_cast(object().qtObject()); if (!context) return pd; pd.setName(m_contextPropertyNames.at(index)); pd.setValue(context->contextProperty(m_contextPropertyNames.at(index))); pd.setClassName(tr("QML Context Property")); pd.setAccessFlags(PropertyData::Writable); return pd; } void QmlContextPropertyAdaptor::writeProperty(int index, const QVariant &value) { Q_ASSERT(index >= 0); Q_ASSERT(index < m_contextPropertyNames.size()); const auto name = m_contextPropertyNames.at(index); auto context = qobject_cast(object().qtObject()); if (name.isEmpty() || !context) return; context->setContextProperty(name, value); } void QmlContextPropertyAdaptor::doSetObject(const ObjectInstance &oi) { auto context = qobject_cast(oi.qtObject()); Q_ASSERT(context); auto contextData = QQmlContextData::get(context); Q_ASSERT(contextData); const auto &propNames = contextData->propertyNames(); m_contextPropertyNames.clear(); m_contextPropertyNames.reserve(propNames.count()); QV4::IdentifierHashEntry *e = propNames.d->entries; QV4::IdentifierHashEntry *end = e + propNames.d->alloc; while (e < end) { #if QT_VERSION < QT_VERSION_CHECK(5, 12, 0) if (e->identifier) m_contextPropertyNames.push_back(e->identifier->string); #else if (e->identifier.isValid()) m_contextPropertyNames.push_back(e->identifier.toQString()); #endif ++e; } } QmlContextPropertyAdaptorFactory *QmlContextPropertyAdaptorFactory::s_instance = nullptr; PropertyAdaptor *QmlContextPropertyAdaptorFactory::create(const ObjectInstance &oi, QObject *parent) const { if (oi.type() != ObjectInstance::QtObject || !oi.qtObject()) return nullptr; if (qobject_cast(oi.qtObject())) return new QmlContextPropertyAdaptor(parent); return nullptr; } QmlContextPropertyAdaptorFactory *QmlContextPropertyAdaptorFactory::instance() { if (!s_instance) s_instance = new QmlContextPropertyAdaptorFactory; return s_instance; } gammaray-2.11.3/plugins/qmlsupport/qmlcontextpropertyadaptor.h000066400000000000000000000041541413310443000250270ustar00rootroot00000000000000/* qmlcontextpropertyadaptor.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_QMLCONTEXTPROPERTYADAPTOR_H #define GAMMARAY_QMLCONTEXTPROPERTYADAPTOR_H #include #include #include namespace GammaRay { class QmlContextPropertyAdaptor : public PropertyAdaptor { Q_OBJECT public: explicit QmlContextPropertyAdaptor(QObject *parent = nullptr); ~QmlContextPropertyAdaptor() override; int count() const override; PropertyData propertyData(int index) const override; void writeProperty(int index, const QVariant &value) override; protected: void doSetObject(const ObjectInstance &oi) override; private: QVector m_contextPropertyNames; }; class QmlContextPropertyAdaptorFactory : public AbstractPropertyAdaptorFactory { public: PropertyAdaptor *create(const ObjectInstance &oi, QObject *parent = nullptr) const override; static QmlContextPropertyAdaptorFactory *instance(); private: static QmlContextPropertyAdaptorFactory *s_instance; }; } #endif gammaray-2.11.3/plugins/qmlsupport/qmlcontexttab.cpp000066400000000000000000000103131413310443000226630ustar00rootroot00000000000000/* qmlcontexttab.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "qmlcontexttab.h" #include "ui_qmlcontexttab.h" #include #include #include #include #include #include #include #include #include using namespace GammaRay; QmlContextTab::QmlContextTab(PropertyWidget *parent) : QWidget(parent) , ui(new Ui::QmlContextTab) { ui->setupUi(this); auto contextModel = ObjectBroker::model(parent->objectBaseName() + QStringLiteral(".qmlContextModel")); ui->contextView->header()->setObjectName("contextViewHeader"); ui->contextView->setModel(contextModel); ui->contextView->setSelectionModel(ObjectBroker::selectionModel(contextModel)); ui->contextView->setDeferredResizeMode(0, QHeaderView::ResizeToContents); connect(ui->contextView, &QWidget::customContextMenuRequested, this, &QmlContextTab::contextContextMenu); auto propertyModel = ObjectBroker::model(parent->objectBaseName() + QStringLiteral(".qmlContextPropertyModel")); auto propertyClient = new ClientPropertyModel(this); propertyClient->setSourceModel(propertyModel); auto propertyProxy = new QSortFilterProxyModel(this); propertyProxy->setSourceModel(propertyClient); propertyProxy->setSortCaseSensitivity(Qt::CaseInsensitive); ui->contextPropertyView->header()->setObjectName("contextPropertyViewHeader"); ui->contextPropertyView->setModel(propertyProxy); ui->contextPropertyView->sortByColumn(0, Qt::AscendingOrder); ui->contextPropertyView->setDeferredResizeMode(0, QHeaderView::ResizeToContents); ui->contextPropertyView->setItemDelegate(new PropertyEditorDelegate(this)); connect(ui->contextPropertyView, &QWidget::customContextMenuRequested, this, &QmlContextTab::propertiesContextMenu); } QmlContextTab::~QmlContextTab() = default; void QmlContextTab::contextContextMenu(QPoint pos) { auto idx = ui->contextView->indexAt(pos); ContextMenuExtension cme; if (!cme.discoverSourceLocation(ContextMenuExtension::GoTo, idx.sibling(idx.row(), 1).data().toUrl())) return; QMenu contextMenu; cme.populateMenu(&contextMenu); contextMenu.exec(ui->contextView->viewport()->mapToGlobal(pos)); } void QmlContextTab::propertiesContextMenu(QPoint pos) { const auto idx = ui->contextPropertyView->indexAt(pos); if (!idx.isValid()) return; const auto actions = idx.data(PropertyModel::ActionRole).toInt(); const auto objectId = idx.data(PropertyModel::ObjectIdRole).value(); ContextMenuExtension ext(objectId); const bool canShow = (actions == PropertyModel::NavigateTo && !objectId.isNull()) || ext.discoverPropertySourceLocation(ContextMenuExtension::GoTo, idx); if (!canShow) return; QMenu contextMenu; ext.populateMenu(&contextMenu); contextMenu.exec(ui->contextPropertyView->viewport()->mapToGlobal(pos)); } gammaray-2.11.3/plugins/qmlsupport/qmlcontexttab.h000066400000000000000000000031701413310443000223330ustar00rootroot00000000000000/* qmlcontexttab.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_QMLCONTEXTTAB_H #define GAMMARAY_QMLCONTEXTTAB_H #include #include namespace GammaRay { namespace Ui { class QmlContextTab; } class QmlContextTab : public QWidget { Q_OBJECT public: explicit QmlContextTab(PropertyWidget *parent = nullptr); ~QmlContextTab() override; private: void contextContextMenu(QPoint pos); void propertiesContextMenu(QPoint pos); QScopedPointer ui; }; } #endif // GAMMARAY_QMLCONTEXTTAB_H gammaray-2.11.3/plugins/qmlsupport/qmlcontexttab.ui000066400000000000000000000031031413310443000225150ustar00rootroot00000000000000 GammaRay::QmlContextTab 0 0 400 300 Qt::Vertical Qt::CustomContextMenu Qt::ElideMiddle false true Qt::CustomContextMenu true true GammaRay::DeferredTreeView QTreeView
    ui/deferredtreeview.h
    gammaray-2.11.3/plugins/qmlsupport/qmllistpropertyadaptor.cpp000066400000000000000000000060271413310443000246520ustar00rootroot00000000000000/* qmllistpropertyadaptor.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "qmllistpropertyadaptor.h" #include #include using namespace GammaRay; QmlListPropertyAdaptor::QmlListPropertyAdaptor(QObject *parent) : PropertyAdaptor(parent) { } QmlListPropertyAdaptor::~QmlListPropertyAdaptor() = default; int QmlListPropertyAdaptor::count() const { auto var = object().variant(); // we need to keep that alive for the runtime of this method QQmlListProperty *prop = reinterpret_cast *>(const_cast(var.data())); if (!prop || !prop->count) return 0; return prop->count(prop); } PropertyData QmlListPropertyAdaptor::propertyData(int index) const { PropertyData pd; auto var = object().variant(); // we need to keep that alive for the runtime of this method QQmlListProperty *prop = reinterpret_cast *>(const_cast(var.data())); if (!prop || !prop->at || !prop->count || index >= prop->count(prop)) return pd; auto obj = prop->at(prop, index); pd.setName(QString::number(index)); pd.setValue(QVariant::fromValue(obj)); if (obj) pd.setTypeName(obj->metaObject()->className()); pd.setClassName(var.typeName()); return pd; } QmlListPropertyAdaptorFactory *QmlListPropertyAdaptorFactory::s_instance = nullptr; PropertyAdaptor *QmlListPropertyAdaptorFactory::create(const ObjectInstance &oi, QObject *parent) const { if (oi.type() != ObjectInstance::QtVariant) return nullptr; if (!oi.variant().isValid() || qstrncmp(oi.typeName(), "QQmlListProperty<", 17) != 0) return nullptr; return new QmlListPropertyAdaptor(parent); } QmlListPropertyAdaptorFactory *QmlListPropertyAdaptorFactory::instance() { if (!s_instance) s_instance = new QmlListPropertyAdaptorFactory; return s_instance; } gammaray-2.11.3/plugins/qmlsupport/qmllistpropertyadaptor.h000066400000000000000000000036441413310443000243210ustar00rootroot00000000000000/* qmllistpropertyadaptor.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_QMLLISTPROPERTYADAPTOR_H #define GAMMARAY_QMLLISTPROPERTYADAPTOR_H #include #include namespace GammaRay { class QmlListPropertyAdaptor : public PropertyAdaptor { Q_OBJECT public: explicit QmlListPropertyAdaptor(QObject *parent = nullptr); ~QmlListPropertyAdaptor() override; int count() const override; PropertyData propertyData(int index) const override; }; class QmlListPropertyAdaptorFactory : public AbstractPropertyAdaptorFactory { public: PropertyAdaptor *create(const ObjectInstance &oi, QObject *parent = nullptr) const override; static QmlListPropertyAdaptorFactory *instance(); private: static QmlListPropertyAdaptorFactory *s_instance; }; } #endif // GAMMARAY_QMLLISTPROPERTYADAPTOR_H gammaray-2.11.3/plugins/qmlsupport/qmlsupport.cpp000066400000000000000000000331711413310443000222330ustar00rootroot00000000000000/* qmlsupport.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "qmlsupport.h" #include "qmllistpropertyadaptor.h" #include "qmlattachedpropertyadaptor.h" #include "qjsvaluepropertyadaptor.h" #include "qmlcontextpropertyadaptor.h" #include "qmlcontextextension.h" #include "qmltypeextension.h" #include "qmltypeutil.h" #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) #include "qmlbindingprovider.h" #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if QT_VERSION < QT_VERSION_CHECK(5, 8, 0) #include #endif #include #include #include #if QT_VERSION < QT_VERSION_CHECK(5, 12, 0) #include // removed in qtdeclarative commit fd6321c03e2d63997078bfa41332dbddefbb86b0 #endif Q_DECLARE_METATYPE(QQmlError) using namespace GammaRay; #if defined(QT_DEPRECATED) static QString metaMethodToString(const QObject *object, const QMetaMethod &method) { return QStringLiteral("%1 bound on %2").arg(method.methodSignature(), Util::displayString( object)); } #endif static QString callableQjsValueToString(const QJSValue &v) { #if defined(QT_DEPRECATED) // note: QJSValue::engine() is deprecated // note: QJSValuePrivate::convertedToValue got introduced in Qt 5.5.0 #if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0) // QJSEngine::handle() changed signature in 5.12 QV4::ExecutionEngine *jsEngine = v.engine()->handle(); #else QV4::ExecutionEngine *jsEngine = QV8Engine::getV4(v.engine()); #endif QV4::Scope scope(jsEngine); QV4::Scoped qobjectMethod(scope, QJSValuePrivate::convertedToValue(jsEngine, v)); if (!qobjectMethod) return QStringLiteral(""); QObject *sender = qobjectMethod->object(); Q_ASSERT(sender); QMetaMethod metaMethod = sender->metaObject()->method(qobjectMethod->methodIndex()); return metaMethodToString(sender, metaMethod); #else Q_UNUSED(v); return QStringLiteral(""); #endif } static QString qmlErrorToString(const QQmlError &error) { return QStringLiteral("%1:%2:%3: %4") .arg(error.url().toString()) .arg(error.line()) .arg(error.column()) .arg(error.description()); } static QString qmlListPropertyToString(const QVariant &value, bool *ok) { if (qstrncmp(value.typeName(), "QQmlListProperty<", 17) != 0 || !value.isValid()) return QString(); *ok = true; QQmlListProperty *prop = reinterpret_cast *>(const_cast(value.data())); if (!prop || !prop->count) return QString(); const int count = prop->count(prop); if (!count) return QmlSupport::tr(""); return QmlSupport::tr("<%1 entries>").arg(count); } static QString qjsValueToString(const QJSValue &v) { if (v.isArray()) { return QStringLiteral(""); } else if (v.isBool()) { return v.toBool() ? QStringLiteral("true") : QStringLiteral("false"); #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) // note: v.isQMetaObject() == true => v.isCallable() == true, because QV4::QMetaObjectWrapper inherits // QV4::FunctionObject and isCallable just checks whether the object is a function object. // thus the isQMetaObject check needs to come before the isCallable check } else if (v.isQMetaObject()) { return QStringLiteral("QMetaObject[className=%1]").arg(v.toQMetaObject()->className()); #endif } else if (v.isCallable()) { return callableQjsValueToString(v); } else if (v.isDate()) { return v.toDateTime().toString(); } else if (v.isError()) { return QStringLiteral(""); } else if (v.isNull()) { return QStringLiteral(""); } else if (v.isNumber()) { return QString::number(v.toNumber()); } else if (v.isObject()) { return QStringLiteral(""); } else if (v.isQObject()) { return Util::displayString(v.toQObject()); } else if (v.isRegExp()) { return QStringLiteral(""); } else if (v.isString()) { return v.toString(); } else if (v.isUndefined()) { return QStringLiteral(""); } else if (v.isVariant()) { return VariantHandler::displayString(v.toVariant()); } return QStringLiteral(""); } static QString qqmlScriptStringToString(const QQmlScriptString &v) { // QQmlScriptStringPrivate::get is not guaranteed to be exported, inline auto scriptStringPriv = reinterpret_cast *>(&v)->constData(); return scriptStringPriv->script; } namespace GammaRay { class QmlObjectDataProvider : public AbstractObjectDataProvider { public: QString name(const QObject *obj) const override; QString typeName(QObject *obj) const override; QString shortTypeName(QObject *obj) const override; SourceLocation creationLocation(QObject *obj) const override; SourceLocation declarationLocation(QObject *obj) const override; }; } QString QmlObjectDataProvider::name(const QObject *obj) const { QQmlContext *ctx = QQmlEngine::contextForObject(obj); if (!ctx || !ctx->engine()) return QString(); // nameForObject crashes for contexts that have no engine (yet) return ctx->nameForObject(const_cast(obj)); } QString QmlObjectDataProvider::typeName(QObject *obj) const { Q_ASSERT(obj); // C++ QML type auto qmlType = QQmlMetaType::qmlType(obj->metaObject()); // QQC2 has some weird types with only the namespace (that is, ending in '/') // we get better results below, so ignore this case here if (QmlType::isValid(qmlType) && !QmlType::callable(qmlType)->qmlTypeName().endsWith(QLatin1Char('/'))) return QmlType::callable(qmlType)->qmlTypeName(); // QML defined type auto data = QQmlData::get(obj); #if QT_VERSION < QT_VERSION_CHECK(5, 8, 0) if (!data || !data->compiledData) return QString(); qmlType = QQmlMetaType::qmlType(data->compiledData->url()); #else if (!data || !data->compilationUnit) return QString(); qmlType = QQmlMetaType::qmlType(data->compilationUnit->url()); #endif if (QmlType::isValid(qmlType)) { // we get the same type for top-level types and inline types, with no known way to tell those apart... if (QString::fromLatin1(obj->metaObject()->className()).startsWith(QmlType::callable(qmlType)->qmlTypeName() + QStringLiteral("_QMLTYPE_"))) return QmlType::callable(qmlType)->qmlTypeName(); } return QString(); } QString QmlObjectDataProvider::shortTypeName(QObject *obj) const { auto n = typeName(obj); const auto isQmlType = !n.isEmpty(); if (isQmlType) { n = n.section(QLatin1Char('/'), -1, -1); // strip off the namespace } else { n = obj->metaObject()->className(); } auto idx = n.indexOf(QLatin1String("_QMLTYPE_")); if (idx > 0) return n.left(idx); idx = n.indexOf(QLatin1String("_QML_")); if (idx > 0) return n.left(idx); return isQmlType ? n : QString(); // let somebody else handle shortening of non-QML names } SourceLocation QmlObjectDataProvider::creationLocation(QObject *obj) const { SourceLocation loc; auto objectData = QQmlData::get(obj); if (!objectData) { if (auto context = qobject_cast(obj)) { loc.setUrl(context->baseUrl()); } return loc; } auto context = objectData->outerContext; if (!context) return loc; loc.setUrl(context->url()); loc.setOneBasedLine(static_cast(objectData->lineNumber)); loc.setOneBasedColumn(static_cast(objectData->columnNumber)); return loc; } SourceLocation QmlObjectDataProvider::declarationLocation(QObject *obj) const { Q_ASSERT(obj); // C++ QML type auto qmlType = QQmlMetaType::qmlType(obj->metaObject()); if (QmlType::isValid(qmlType)) return SourceLocation(QmlType::callable(qmlType)->sourceUrl()); // QML-defined type auto data = QQmlData::get(obj); #if QT_VERSION < QT_VERSION_CHECK(5, 8, 0) if (!data || !data->compiledData) return SourceLocation(); qmlType = QQmlMetaType::qmlType(data->compiledData->url()); #else if (!data || !data->compilationUnit) return SourceLocation(); qmlType = QQmlMetaType::qmlType(data->compilationUnit->url()); #endif if (QmlType::isValid(qmlType)) return SourceLocation(QmlType::callable(qmlType)->sourceUrl()); return SourceLocation(); } QmlSupport::QmlSupport(Probe *probe, QObject *parent) : QObject(parent) { Q_UNUSED(probe); MetaObject *mo = nullptr; MO_ADD_METAOBJECT1(QQmlComponent, QObject); MO_ADD_PROPERTY_RO(QQmlComponent, errors); MO_ADD_PROPERTY_RO(QQmlComponent, isError); MO_ADD_PROPERTY_RO(QQmlComponent, isLoading); MO_ADD_PROPERTY_RO(QQmlComponent, isNull); MO_ADD_PROPERTY_RO(QQmlComponent, isReady); MO_ADD_METAOBJECT1(QQmlContext, QObject); MO_ADD_PROPERTY(QQmlContext, baseUrl, setBaseUrl); MO_ADD_PROPERTY(QQmlContext, contextObject, setContextObject); MO_ADD_PROPERTY_RO(QQmlContext, engine); MO_ADD_PROPERTY_RO(QQmlContext, isValid); MO_ADD_PROPERTY_RO(QQmlContext, parentContext); MO_ADD_METAOBJECT1(QJSEngine, QObject); MO_ADD_PROPERTY_RO(QJSEngine, globalObject); MO_ADD_METAOBJECT1(QQmlEngine, QJSEngine); MO_ADD_PROPERTY(QQmlEngine, baseUrl, setBaseUrl); MO_ADD_PROPERTY(QQmlEngine, importPathList, setImportPathList); MO_ADD_PROPERTY(QQmlEngine, outputWarningsToStandardError, setOutputWarningsToStandardError); MO_ADD_PROPERTY(QQmlEngine, pluginPathList, setPluginPathList); MO_ADD_PROPERTY_RO(QQmlEngine, rootContext); MO_ADD_METAOBJECT0(QQmlType); MO_ADD_PROPERTY_RO(QQmlType, typeName); MO_ADD_PROPERTY_RO(QQmlType, qmlTypeName); MO_ADD_PROPERTY_RO(QQmlType, elementName); MO_ADD_PROPERTY_RO(QQmlType, majorVersion); MO_ADD_PROPERTY_RO(QQmlType, minorVersion); #if QT_VERSION < QT_VERSION_CHECK(5, 13, 0) MO_ADD_PROPERTY_RO(QQmlType, createSize); // got removed in v5.13.0-alpha1 #endif MO_ADD_PROPERTY_RO(QQmlType, isCreatable); MO_ADD_PROPERTY_RO(QQmlType, isExtendedType); MO_ADD_PROPERTY_RO(QQmlType, isSingleton); MO_ADD_PROPERTY_RO(QQmlType, isInterface); MO_ADD_PROPERTY_RO(QQmlType, isComposite); MO_ADD_PROPERTY_RO(QQmlType, isCompositeSingleton); MO_ADD_PROPERTY_RO(QQmlType, noCreationReason); MO_ADD_PROPERTY_RO(QQmlType, typeId); MO_ADD_PROPERTY_RO(QQmlType, qListTypeId); MO_ADD_PROPERTY_RO(QQmlType, metaObjectRevision); MO_ADD_PROPERTY_RO(QQmlType, containsRevisionedAttributes); // MO_ADD_PROPERTY_RO(QQmlType, interfaceIId); MO_ADD_PROPERTY_RO(QQmlType, index); MO_ADD_PROPERTY_RO(QQmlType, metaObject); MO_ADD_PROPERTY_RO(QQmlType, baseMetaObject); MO_ADD_PROPERTY_RO(QQmlType, sourceUrl); VariantHandler::registerStringConverter(qjsValueToString); VariantHandler::registerStringConverter(qqmlScriptStringToString); VariantHandler::registerStringConverter(qmlErrorToString); VariantHandler::registerGenericStringConverter(qmlListPropertyToString); PropertyAdaptorFactory::registerFactory(QmlListPropertyAdaptorFactory::instance()); PropertyAdaptorFactory::registerFactory(QmlAttachedPropertyAdaptorFactory::instance()); PropertyAdaptorFactory::registerFactory(QJSValuePropertyAdaptorFactory::instance()); PropertyAdaptorFactory::registerFactory(QmlContextPropertyAdaptorFactory::instance()); PropertyController::registerExtension(); PropertyController::registerExtension(); #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) BindingAggregator::registerBindingProvider(std::unique_ptr(new QmlBindingProvider)); #endif static auto dataProvider = new QmlObjectDataProvider; ObjectDataProvider::registerProvider(dataProvider); } gammaray-2.11.3/plugins/qmlsupport/qmlsupport.h000066400000000000000000000033601413310443000216750ustar00rootroot00000000000000/* qmlsupport.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_QMLSUPPORT_QMLSUPPORT_H #define GAMMARAY_QMLSUPPORT_QMLSUPPORT_H #include #include namespace GammaRay { class QmlSupport : public QObject { Q_OBJECT public: explicit QmlSupport(Probe *probe, QObject *parent = nullptr); }; class QmlSupportFactory : public QObject, public StandardToolFactory { Q_OBJECT Q_INTERFACES(GammaRay::ToolFactory) Q_PLUGIN_METADATA(IID "com.kdab.GammaRay.ToolFactory" FILE "gammaray_qmlsupport.json") public: explicit QmlSupportFactory(QObject *parent = nullptr) : QObject(parent) { } }; } #endif gammaray-2.11.3/plugins/qmlsupport/qmlsupportuifactory.cpp000066400000000000000000000034521413310443000241600ustar00rootroot00000000000000/* qmlsupportuifactory.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "qmlsupportuifactory.h" #include "qmlcontexttab.h" #include "qmltypetab.h" #include using namespace GammaRay; QString QmlSupportUiFactory::id() const { return QString(); } void QmlSupportUiFactory::initUi() { PropertyWidget::registerTab(QStringLiteral("qmlContext"), tr("QML Context"), PropertyWidgetTabPriority::Advanced); PropertyWidget::registerTab(QStringLiteral("qmlType"), tr("QML Type"), PropertyWidgetTabPriority::Exotic); } QWidget *GammaRay::QmlSupportUiFactory::createWidget(QWidget *) { return nullptr; } gammaray-2.11.3/plugins/qmlsupport/qmlsupportuifactory.h000066400000000000000000000031661413310443000236270ustar00rootroot00000000000000/* qmlsupportuifactory.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_QMLSUPPORTUIFACTORY_H #define GAMMARAY_QMLSUPPORTUIFACTORY_H #include namespace GammaRay { class QmlSupportUiFactory : public QObject, public ToolUiFactory { Q_OBJECT Q_INTERFACES(GammaRay::ToolUiFactory) Q_PLUGIN_METADATA(IID "com.kdab.GammaRay.ToolUiFactory" FILE "gammaray_qmlsupport.json") public: QString id() const override; void initUi() override; QWidget *createWidget(QWidget *) override; }; } #endif // GAMMARAY_QMLSUPPORTUIFACTORY_H gammaray-2.11.3/plugins/qmlsupport/qmltypeextension.cpp000066400000000000000000000060611413310443000234330ustar00rootroot00000000000000/* qmltypeextension.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "qmltypeextension.h" #include "qmltypeutil.h" #include #include #include #if QT_VERSION < QT_VERSION_CHECK(5, 8, 0) #include #else #include #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) #include // created in qtdeclarative commit fd6321c03e2d63997078bfa41332dbddefbb86b0 #endif #endif #include #include using namespace GammaRay; QmlTypeExtension::QmlTypeExtension(PropertyController *controller) : PropertyControllerExtension(controller->objectBaseName() + ".qmlType") , m_typePropertyModel(new AggregatedPropertyModel(controller)) { controller->registerModel(m_typePropertyModel, QStringLiteral("qmlTypeModel")); } QmlTypeExtension::~QmlTypeExtension() = default; bool QmlTypeExtension::setQObject(QObject *object) { if (!object) return false; // C++ QML type if (setMetaObject(object->metaObject())) return true; // QML-defined type auto data = QQmlData::get(object); #if QT_VERSION < QT_VERSION_CHECK(5, 8, 0) if (!data || !data->compiledData) return false; const auto qmlType = QQmlMetaType::qmlType(data->compiledData->url()); #else if (!data || !data->compilationUnit) return false; const auto qmlType = QQmlMetaType::qmlType(data->compilationUnit->url()); #endif if (!QmlType::isValid(qmlType)) return false; m_typePropertyModel->setObject(QmlType::toObjectInstance(qmlType)); return true; } bool QmlTypeExtension::setMetaObject(const QMetaObject *metaObject) { if (!metaObject) return false; const auto qmlType = QQmlMetaType::qmlType(metaObject); if (!QmlType::isValid(qmlType)) return false; m_typePropertyModel->setObject(QmlType::toObjectInstance(qmlType)); return true; } gammaray-2.11.3/plugins/qmlsupport/qmltypeextension.h000066400000000000000000000032311413310443000230740ustar00rootroot00000000000000/* qmltypeextension.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_QMLTYPEEXTENSION_H #define GAMMARAY_QMLTYPEEXTENSION_H #include namespace GammaRay { class AggregatedPropertyModel; class QmlTypeExtension : public PropertyControllerExtension { public: explicit QmlTypeExtension(PropertyController *controller); ~QmlTypeExtension(); bool setQObject(QObject *object) override; bool setMetaObject(const QMetaObject *metaObject) override; private: AggregatedPropertyModel *m_typePropertyModel; }; } #endif // GAMMARAY_QMLTYPEEXTENSION_H gammaray-2.11.3/plugins/qmlsupport/qmltypetab.cpp000066400000000000000000000052371413310443000221710ustar00rootroot00000000000000/* qmltypetab.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "qmltypetab.h" #include "ui_qmltypetab.h" #include #include #include #include #include #include using namespace GammaRay; QmlTypeTab::QmlTypeTab(PropertyWidget *parent) : QWidget(parent) , ui(new Ui::QmlTypeTab) { ui->setupUi(this); ui->typeView->header()->setObjectName("qmlTypeViewHeader"); ui->typeView->setDeferredResizeMode(0, QHeaderView::ResizeToContents); auto clientPropModel = new ClientPropertyModel(this); clientPropModel->setSourceModel(ObjectBroker::model(parent->objectBaseName() + QStringLiteral(".qmlTypeModel"))); ui->typeView->setModel(clientPropModel); connect(ui->typeView, &QWidget::customContextMenuRequested, this, &QmlTypeTab::contextMenu); } QmlTypeTab::~QmlTypeTab() = default; void QmlTypeTab::contextMenu(QPoint pos) { // TODO share this with PropertiesTab const auto idx = ui->typeView->indexAt(pos); if (!idx.isValid()) return; const auto actions = idx.data(PropertyModel::ActionRole).toInt(); const auto objectId = idx.data(PropertyModel::ObjectIdRole).value(); ContextMenuExtension ext(objectId); const bool canShow = actions != PropertyModel::NoAction || ext.discoverPropertySourceLocation(ContextMenuExtension::GoTo, idx); if (!canShow) return; QMenu contextMenu; ext.populateMenu(&contextMenu); contextMenu.exec(ui->typeView->viewport()->mapToGlobal(pos)); } gammaray-2.11.3/plugins/qmlsupport/qmltypetab.h000066400000000000000000000030651413310443000216330ustar00rootroot00000000000000/* qmltypetab.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_QMLTYPETAB_H #define GAMMARAY_QMLTYPETAB_H #include #include namespace GammaRay { class PropertyWidget; namespace Ui { class QmlTypeTab; } class QmlTypeTab : public QWidget { Q_OBJECT public: explicit QmlTypeTab(PropertyWidget *parent = nullptr); ~QmlTypeTab() override; private: void contextMenu(QPoint pos); QScopedPointer ui; }; } #endif // GAMMARAY_QMLTYPETAB_H gammaray-2.11.3/plugins/qmlsupport/qmltypetab.ui000066400000000000000000000014441413310443000220200ustar00rootroot00000000000000 GammaRay::QmlTypeTab 0 0 400 300 Qt::CustomContextMenu GammaRay::DeferredTreeView QTreeView
    ui/deferredtreeview.h
    gammaray-2.11.3/plugins/qmlsupport/qmltypeutil.h000066400000000000000000000042351413310443000220420ustar00rootroot00000000000000/* qmltypeutil.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef QMLTYPEUTIL_H #define QMLTYPEUTIL_H #include #include #if QT_VERSION >= QT_VERSION_CHECK(5, 9, 2) #define GAMMARAY_QQMLTYPE_IS_VALUE_TYPE #else #undef GAMMARAY_QQMLTYPE_IS_VALUE_TYPE #endif #ifdef GAMMARAY_QQMLTYPE_IS_VALUE_TYPE Q_DECLARE_METATYPE(QQmlType) #endif namespace GammaRay { /** Helper functions to deal with QQmlType changing from pointer to value type in Qt 5.9.2 */ namespace QmlType { inline bool isValid(QQmlType *type) { return type; } #ifdef GAMMARAY_QQMLTYPE_IS_VALUE_TYPE inline bool isValid(const QQmlType &type) { return type.isValid(); } #endif inline QQmlType* callable(QQmlType *type) { return type; } inline const QQmlType* callable(const QQmlType &type) { return &type; } inline ObjectInstance toObjectInstance(QQmlType *type) { return ObjectInstance(type, "QQmlType"); } #ifdef GAMMARAY_QQMLTYPE_IS_VALUE_TYPE inline ObjectInstance toObjectInstance(const QQmlType &type) { return ObjectInstance(QVariant::fromValue(type)); } #endif } } #endif gammaray-2.11.3/plugins/qt3dinspector/000077500000000000000000000000001413310443000176505ustar00rootroot00000000000000gammaray-2.11.3/plugins/qt3dinspector/3dinspector.cpp000066400000000000000000000400011413310443000226040ustar00rootroot00000000000000/* 3dinspector.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "3dinspector.h" #include "qt3dentitytreemodel.h" #include "framegraphmodel.h" #include "geometryextension/qt3dgeometryextension.h" #include "paintanalyzerextension/qt3dpaintedtextureanalyzerextension.h" #include #include #include #include #include #include #include #include #include #include <3rdparty/kde/krecursivefilterproxymodel.h> #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0) #include #include #include #include #include #endif #include #include #include #include #include #include using namespace GammaRay; Qt3DInspector::Qt3DInspector(Probe *probe, QObject *parent) : Qt3DInspectorInterface(parent) , m_engine(nullptr) , m_entityModel(new Qt3DEntityTreeModel(this)) , m_currentEntity(nullptr) , m_entitryPropertyController(new PropertyController(QStringLiteral( "com.kdab.GammaRay.Qt3DInspector.entityPropertyController"), this)) , m_frameGraphModel(new FrameGraphModel(this)) , m_currentFrameGraphNode(nullptr) , m_frameGraphPropertyController(new PropertyController(QStringLiteral( "com.kdab.GammaRay.Qt3DInspector.frameGraphPropertyController"), this)) { registerCoreMetaTypes(); registerInputMetaTypes(); registerRenderMetaTypes(); registerAnimationMetaTypes(); registerExtensions(); auto engineFilterModel = new ObjectTypeFilterProxyModel(this); engineFilterModel->setSourceModel(probe->objectListModel()); auto proxy = new SingleColumnObjectProxyModel(this); proxy->setSourceModel(engineFilterModel); m_engineModel = proxy; probe->registerModel(QStringLiteral( "com.kdab.GammaRay.Qt3DInspector.engineModel"), m_engineModel); connect(probe, &Probe::objectCreated, m_entityModel, &Qt3DEntityTreeModel::objectCreated); connect(probe, &Probe::objectDestroyed, m_entityModel, &Qt3DEntityTreeModel::objectDestroyed); connect(probe, &Probe::objectReparented, m_entityModel, &Qt3DEntityTreeModel::objectReparented); auto entityProxy = new ServerProxyModel(this); entityProxy->setSourceModel(m_entityModel); probe->registerModel(QStringLiteral("com.kdab.GammaRay.Qt3DInspector.sceneModel"), entityProxy); m_entitySelectionModel = ObjectBroker::selectionModel(entityProxy); connect(m_entitySelectionModel, &QItemSelectionModel::selectionChanged, this, &Qt3DInspector::entitySelectionChanged); connect(probe, &Probe::objectCreated, m_frameGraphModel, &FrameGraphModel::objectCreated); connect(probe, &Probe::objectDestroyed, m_frameGraphModel, &FrameGraphModel::objectDestroyed); connect(probe, &Probe::objectReparented, m_frameGraphModel, &FrameGraphModel::objectReparented); auto frameGraphProxy = new ServerProxyModel(this); frameGraphProxy->setSourceModel(m_frameGraphModel); probe->registerModel(QStringLiteral( "com.kdab.GammaRay.Qt3DInspector.frameGraphModel"), frameGraphProxy); m_frameGraphSelectionModel = ObjectBroker::selectionModel(frameGraphProxy); connect(m_frameGraphSelectionModel, &QItemSelectionModel::selectionChanged, this, &Qt3DInspector::frameGraphSelectionChanged); connect(probe, &Probe::objectSelected, this, &Qt3DInspector::objectSelected); } Qt3DInspector::~Qt3DInspector() { } void Qt3DInspector::selectEngine(int row) { Qt3DCore::QAspectEngine *engine = nullptr; const auto idx = m_engineModel->index(row, 0); if (idx.isValid()) { engine = qobject_cast( idx.data(ObjectModel::ObjectRole).value()); } selectEngine(engine); } void Qt3DInspector::selectEngine(Qt3DCore::QAspectEngine *engine) { if (m_engine == engine) return; m_engine = engine; m_entityModel->setEngine(engine); if (!engine) return; // TODO watch for changes auto rootEntity = engine->rootEntity(); if (!rootEntity) return; foreach (auto component, rootEntity->components()) { if (auto renderSettings = qobject_cast(component)) { m_frameGraphModel->setRenderSettings(renderSettings); break; } } } void Qt3DInspector::entitySelectionChanged(const QItemSelection &selection) { if (selection.isEmpty()) return; const auto index = selection.first().topLeft(); auto entity = index.data(ObjectModel::ObjectRole).value(); selectEntity(entity); } void Qt3DInspector::selectEntity(Qt3DCore::QEntity *entity) { if (m_currentEntity == entity) return; m_currentEntity = entity; m_entitryPropertyController->setObject(entity); // update selelction if we got here via object navigation const auto model = m_entitySelectionModel->model(); Model::used(model); const auto indexList = model->match(model->index(0, 0), ObjectModel::ObjectRole, QVariant::fromValue( entity), 1, Qt::MatchExactly | Qt::MatchRecursive | Qt::MatchWrap); if (indexList.isEmpty()) return; const auto index = indexList.first(); m_entitySelectionModel->select(index, QItemSelectionModel::Select | QItemSelectionModel::Clear | QItemSelectionModel::Rows | QItemSelectionModel::Current); } void Qt3DInspector::frameGraphSelectionChanged(const QItemSelection &selection) { if (selection.isEmpty()) return; const auto index = selection.first().topLeft(); auto node = index.data(ObjectModel::ObjectRole).value(); selectFrameGraphNode(node); } void Qt3DInspector::selectFrameGraphNode(Qt3DRender::QFrameGraphNode *node) { if (m_currentFrameGraphNode == node) return; m_currentFrameGraphNode = node; m_frameGraphPropertyController->setObject(node); // update selelction if we got here via object navigation const auto model = m_frameGraphSelectionModel->model(); Model::used(model); const auto indexList = model->match(model->index(0, 0), ObjectModel::ObjectRole, QVariant::fromValue( node), 1, Qt::MatchExactly | Qt::MatchRecursive | Qt::MatchWrap); if (indexList.isEmpty()) return; const auto index = indexList.first(); m_frameGraphSelectionModel->select(index, QItemSelectionModel::Select | QItemSelectionModel::Clear | QItemSelectionModel::Rows | QItemSelectionModel::Current); } void Qt3DInspector::objectSelected(QObject *obj) { if (auto engine = qobject_cast(obj)) { selectEngine(engine); // TODO check if the engine matches, otherwise switch that too } else if (auto entity = qobject_cast(obj)) { selectEntity(entity); } else if (auto node = qobject_cast(obj)) { selectFrameGraphNode(node); } } void Qt3DInspector::registerCoreMetaTypes() { MetaObject *mo = nullptr; MO_ADD_METAOBJECT1(Qt3DCore::QNode, QObject); MO_ADD_PROPERTY_RO(Qt3DCore::QNode, notificationsBlocked); #if QT_VERSION < QT_VERSION_CHECK(5, 7, 0) MO_ADD_PROPERTY_RO(Qt3DCore::QNode, childrenNodes); #else MO_ADD_PROPERTY_RO(Qt3DCore::QNode, childNodes); #endif MO_ADD_METAOBJECT1(Qt3DCore::QComponent, Qt3DCore::QNode); MO_ADD_PROPERTY_RO(Qt3DCore::QComponent, entities); MO_ADD_METAOBJECT1(Qt3DCore::QEntity, Qt3DCore::QNode); MO_ADD_PROPERTY_RO(Qt3DCore::QEntity, components); MO_ADD_PROPERTY_RO(Qt3DCore::QEntity, parentEntity); } void Qt3DInspector::registerInputMetaTypes() { qRegisterMetaType(); } static QString attributeToString(Qt3DRender::QAttribute *attr) { if (!attr || attr->name().isEmpty()) return Util::displayString(attr); const auto name = ObjectDataProvider::name(attr); if (name.isEmpty()) return attr->name() + QLatin1String(" [") + Util::addressToString(attr) + QLatin1Char(']'); return Util::displayString(attr); } static QString filterKeyToString(Qt3DRender::QFilterKey *key) { if (!key || key->name().isEmpty()) return Util::displayString(key); const auto value = VariantHandler::displayString(key->value()); if (value.isEmpty()) return Util::displayString(key); return key->name() + QLatin1String(" = ") + value; } static QString graphicsApiFilterToString(Qt3DRender::QGraphicsApiFilter *filter) { if (!filter) return Util::displayString(filter); using namespace Qt3DRender; QString s; switch (filter->api()) { case QGraphicsApiFilter::OpenGLES: s = QStringLiteral("OpenGL ES "); break; case QGraphicsApiFilter::OpenGL: s = QStringLiteral("OpenGL "); break; default: return Util::displayString(filter); } s += QString::fromLatin1("%1.%2").arg(filter->majorVersion()).arg(filter->minorVersion()); switch (filter->profile()) { case QGraphicsApiFilter::NoProfile: break; case QGraphicsApiFilter::CoreProfile: s += QStringLiteral(" core"); break; case QGraphicsApiFilter::CompatibilityProfile: s += QStringLiteral(" compat"); break; } return s; } static QString parameterToString(Qt3DRender::QParameter *parameter) { if (!parameter || parameter->name().isEmpty()) return Util::displayString(parameter); const auto value = VariantHandler::displayString(parameter->value()); if (value.isEmpty()) return Util::displayString(parameter); return parameter->name() + QLatin1String(" = ") + value; } void Qt3DInspector::registerRenderMetaTypes() { qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); MetaObject *mo = nullptr; MO_ADD_METAOBJECT1(Qt3DRender::QMaterial, Qt3DCore::QComponent); MO_ADD_PROPERTY_RO(Qt3DRender::QMaterial, parameters); MO_ADD_METAOBJECT1(Qt3DRender::QEffect, Qt3DCore::QNode); MO_ADD_PROPERTY_RO(Qt3DRender::QEffect, parameters); MO_ADD_PROPERTY_RO(Qt3DRender::QEffect, techniques); MO_ADD_METAOBJECT1(Qt3DRender::QGeometry, Qt3DCore::QNode); MO_ADD_PROPERTY_RO(Qt3DRender::QGeometry, attributes); MO_ADD_METAOBJECT1(Qt3DRender::QTechnique, Qt3DCore::QNode); MO_ADD_PROPERTY_RO(Qt3DRender::QTechnique, filterKeys); MO_ADD_PROPERTY_RO(Qt3DRender::QTechnique, parameters); MO_ADD_PROPERTY_RO(Qt3DRender::QTechnique, renderPasses); MO_ADD_METAOBJECT1(Qt3DRender::QRenderPass, Qt3DCore::QNode); MO_ADD_PROPERTY_RO(Qt3DRender::QRenderPass, filterKeys); MO_ADD_PROPERTY_RO(Qt3DRender::QRenderPass, parameters); MO_ADD_PROPERTY_RO(Qt3DRender::QRenderPass, renderStates); MO_ADD_METAOBJECT1(Qt3DRender::QAbstractTexture, Qt3DCore::QNode); MO_ADD_PROPERTY_RO(Qt3DRender::QAbstractTexture, textureImages); MO_ADD_METAOBJECT1(Qt3DRender::QSceneLoader, Qt3DCore::QComponent); #if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0) MO_ADD_PROPERTY_RO(Qt3DRender::QSceneLoader, entityNames); #endif VariantHandler::registerStringConverter(attributeToString); VariantHandler::registerStringConverter(filterKeyToString); VariantHandler::registerStringConverter(graphicsApiFilterToString); VariantHandler::registerStringConverter(parameterToString); } #if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0) static QString channelMappingToString(Qt3DAnimation::QChannelMapping *mapping) { if (!mapping || mapping->channelName().isEmpty() || mapping->property().isEmpty()) return Util::displayString(mapping); return mapping->channelName() + QLatin1String(" -> ") + Util::displayString(mapping->target()) + QLatin1Char('.') + mapping->property(); } #endif void Qt3DInspector::registerAnimationMetaTypes() { #if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0) MetaObject *mo = nullptr; MO_ADD_METAOBJECT0(Qt3DAnimation::QAnimationClipData); MO_ADD_PROPERTY_RO(Qt3DAnimation::QAnimationClipData, channelCount); MO_ADD_PROPERTY_RO(Qt3DAnimation::QAnimationClipData, isValid); MO_ADD_PROPERTY (Qt3DAnimation::QAnimationClipData, name, setName); MO_ADD_METAOBJECT1(Qt3DAnimation::QAnimationController, QObject); MO_ADD_PROPERTY_NC(Qt3DAnimation::QAnimationController, animationGroupList); MO_ADD_METAOBJECT1(Qt3DAnimation::QAnimationGroup, QObject); MO_ADD_PROPERTY_NC(Qt3DAnimation::QAnimationGroup, animationList); MO_ADD_METAOBJECT1(Qt3DAnimation::QChannelMapper, Qt3DCore::QNode); MO_ADD_PROPERTY_RO(Qt3DAnimation::QChannelMapper, mappings); VariantHandler::registerStringConverter(channelMappingToString); #endif } void Qt3DInspector::registerExtensions() { PropertyController::registerExtension(); PropertyController::registerExtension(); } gammaray-2.11.3/plugins/qt3dinspector/3dinspector.h000066400000000000000000000063331413310443000222630ustar00rootroot00000000000000/* 3dinspector.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_3DINSPECTOR_3DINSPECTOR_H #define GAMMARAY_3DINSPECTOR_3DINSPECTOR_H #include "qt3dinspectorinterface.h" #include #include QT_BEGIN_NAMESPACE class QItemSelection; class QItemSelectionModel; namespace Qt3DCore { class QAspectEngine; class QEntity; } namespace Qt3DRender { class QFrameGraphNode; } QT_END_NAMESPACE namespace GammaRay { class PropertyController; class Qt3DEntityTreeModel; class FrameGraphModel; class Qt3DInspector : public Qt3DInspectorInterface { Q_OBJECT Q_INTERFACES(GammaRay::Qt3DInspectorInterface) public: explicit Qt3DInspector(Probe *probe, QObject *parent = nullptr); ~Qt3DInspector(); public slots: void selectEngine(int row) override; private slots: void objectSelected(QObject *obj); private: void entitySelectionChanged(const QItemSelection &selection); void selectEngine(Qt3DCore::QAspectEngine *engine); void selectEntity(Qt3DCore::QEntity *entity); void frameGraphSelectionChanged(const QItemSelection &selection); void selectFrameGraphNode(Qt3DRender::QFrameGraphNode *node); void registerCoreMetaTypes(); void registerInputMetaTypes(); void registerRenderMetaTypes(); void registerAnimationMetaTypes(); void registerExtensions(); private: QAbstractItemModel *m_engineModel; Qt3DCore::QAspectEngine *m_engine; Qt3DEntityTreeModel *m_entityModel; QItemSelectionModel *m_entitySelectionModel; Qt3DCore::QEntity *m_currentEntity; PropertyController *m_entitryPropertyController; FrameGraphModel *m_frameGraphModel; QItemSelectionModel *m_frameGraphSelectionModel; Qt3DRender::QFrameGraphNode *m_currentFrameGraphNode; PropertyController *m_frameGraphPropertyController; }; class Qt3DInspectorFactory : public QObject, public StandardToolFactory { Q_OBJECT Q_INTERFACES(GammaRay::ToolFactory) Q_PLUGIN_METADATA(IID "com.kdab.GammaRay.ToolFactory" FILE "gammaray_3dinspector.json") public: explicit Qt3DInspectorFactory(QObject *parent = nullptr) : QObject(parent) { } }; } #endif gammaray-2.11.3/plugins/qt3dinspector/CMakeLists.txt000066400000000000000000000030261413310443000224110ustar00rootroot00000000000000add_subdirectory(geometryextension/es2) add_subdirectory(geometryextension/gl3) # shared stuff set(gammaray_3dinspector_shared_srcs qt3dinspectorinterface.cpp geometryextension/qt3dgeometryextensioninterface.cpp ) # probe plugin if (NOT GAMMARAY_CLIENT_ONLY_BUILD) set(gammaray_3dinspector_srcs 3dinspector.cpp qt3dentitytreemodel.cpp framegraphmodel.cpp geometryextension/qt3dgeometryextension.cpp paintanalyzerextension/qt3dpaintedtextureanalyzerextension.cpp ${gammaray_3dinspector_shared_srcs} ) gammaray_add_plugin(gammaray_3dinspector JSON gammaray_3dinspector.json SOURCES ${gammaray_3dinspector_srcs}) target_link_libraries(gammaray_3dinspector gammaray_core gammaray_kitemmodels Qt5::3DInput Qt5::3DRender) if (TARGET Qt5::3DAnimation) target_link_libraries(gammaray_3dinspector Qt5::3DAnimation) endif() endif() # ui plugin if(GAMMARAY_BUILD_UI) set(gammaray_3dinspector_ui_srcs qt3dinspectorwidget.cpp qt3dinspectorclient.cpp treeexpander.cpp geometryextension/qt3dgeometryextensionclient.cpp geometryextension/qt3dgeometrytab.cpp geometryextension/attribute.cpp geometryextension/boundingvolume.cpp geometryextension/buffermodel.cpp geometryextension/cameracontroller.cpp ${gammaray_3dinspector_shared_srcs} qt3dinspector.qrc ) gammaray_add_plugin(gammaray_3dinspector_ui JSON gammaray_3dinspector.json SOURCES ${gammaray_3dinspector_ui_srcs}) target_link_libraries(gammaray_3dinspector_ui gammaray_ui Qt5::3DRender Qt5::3DInput Qt5::3DLogic Qt5::3DExtras) endif() gammaray-2.11.3/plugins/qt3dinspector/framegraphmodel.cpp000066400000000000000000000217001413310443000235110ustar00rootroot00000000000000/* framegraphmodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "framegraphmodel.h" #include #include #include using namespace GammaRay; FrameGraphModel::FrameGraphModel(QObject *parent) : ObjectModelBase(parent) , m_settings(nullptr) { } FrameGraphModel::~FrameGraphModel() { } void FrameGraphModel::setRenderSettings(Qt3DRender::QRenderSettings *settings) { beginResetModel(); clear(); m_settings = settings; // TODO monitor m_settings->activeFrameGraph changed if (m_settings) populateFromNode(m_settings->activeFrameGraph()); endResetModel(); } void FrameGraphModel::clear() { for (auto it = m_childParentMap.constBegin(); it != m_childParentMap.constEnd(); ++it) disconnectNode(it.key()); m_childParentMap.clear(); m_parentChildMap.clear(); } void FrameGraphModel::populateFromNode(Qt3DRender::QFrameGraphNode *node) { if (!node) return; m_childParentMap[node] = node->parentFrameGraphNode(); m_parentChildMap[node->parentFrameGraphNode()].push_back(node); connectNode(node); foreach (auto child, node->childNodes()) { if (auto childNode = qobject_cast(child)) populateFromNode(childNode); } auto &children = m_parentChildMap[node->parentFrameGraphNode()]; std::sort(children.begin(), children.end()); } int FrameGraphModel::rowCount(const QModelIndex &parent) const { auto parentNode = reinterpret_cast(parent.internalPointer()); return m_parentChildMap.value(parentNode).size(); } QVariant FrameGraphModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); auto node = reinterpret_cast(index.internalPointer()); if (role == ObjectModel::ObjectIdRole) return QVariant::fromValue(ObjectId(node)); else if (role == Qt::CheckStateRole && index.column() == 0) return node->isEnabled() ? Qt::Checked : Qt::Unchecked; return dataForObject(node, index, role); } QModelIndex FrameGraphModel::parent(const QModelIndex &child) const { auto childNode = reinterpret_cast(child.internalPointer()); return indexForNode(m_childParentMap.value(childNode)); } QModelIndex FrameGraphModel::index(int row, int column, const QModelIndex &parent) const { auto parentNode = reinterpret_cast(parent.internalPointer()); const auto children = m_parentChildMap.value(parentNode); if (row < 0 || column < 0 || row >= children.size() || column >= columnCount()) return QModelIndex(); return createIndex(row, column, children.at(row)); } Qt::ItemFlags FrameGraphModel::flags(const QModelIndex &index) const { auto baseFlags = QAbstractItemModel::flags(index); if (index.isValid() && index.column() == 0) return baseFlags | Qt::ItemIsUserCheckable; return baseFlags; } bool FrameGraphModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (!m_settings || !index.isValid() || role != Qt::CheckStateRole || index.column() != 0) return false; auto node = reinterpret_cast(index.internalPointer()); node->setEnabled(value.toInt() == Qt::Checked); emit dataChanged(index, index); return true; } QModelIndex FrameGraphModel::indexForNode(Qt3DRender::QFrameGraphNode *node) const { if (!node) return QModelIndex(); auto parent = m_childParentMap.value(node); const auto parentIndex = indexForNode(parent); if (!parentIndex.isValid() && parent) return QModelIndex(); const auto &siblings = m_parentChildMap[parent]; auto it = std::lower_bound(siblings.constBegin(), siblings.constEnd(), node); if (it == siblings.constEnd() || *it != node) return QModelIndex(); const int row = std::distance(siblings.constBegin(), it); return index(row, 0, parentIndex); } static bool isRenderSettingsForNode(Qt3DRender::QRenderSettings *settings, Qt3DRender::QFrameGraphNode *node) { Q_ASSERT(settings); Q_ASSERT(node); if (node == settings->activeFrameGraph()) return true; if (!node->parentFrameGraphNode()) return false; return isRenderSettingsForNode(settings, node->parentFrameGraphNode()); } void FrameGraphModel::objectCreated(QObject *obj) { auto node = qobject_cast(obj); if (!node || !m_settings) return; if (!isRenderSettingsForNode(m_settings, node)) return; if (m_childParentMap.contains(node)) return; auto parentNode = node->parentFrameGraphNode(); if (parentNode) { // add parent first, if we don't know that yet if (!m_childParentMap.contains(parentNode)) { objectCreated(parentNode); return; } } const auto index = indexForNode(parentNode); Q_ASSERT(index.isValid() || !parentNode); auto &children = m_parentChildMap[parentNode]; auto it = std::lower_bound(children.begin(), children.end(), node); const int row = std::distance(children.begin(), it); beginInsertRows(index, row, row); children.insert(it, node); m_childParentMap.insert(node, parentNode); connectNode(node); foreach (auto child, node->childNodes()) { if (auto childNode = qobject_cast(child)) populateFromNode(childNode); } endInsertRows(); } void FrameGraphModel::objectDestroyed(QObject *obj) { auto node = static_cast(obj); // never dereference this! if (!m_childParentMap.contains(node)) { Q_ASSERT(!m_parentChildMap.contains(node)); return; } removeNode(node, true); } void FrameGraphModel::removeNode(Qt3DRender::QFrameGraphNode *node, bool danglingPointer) { if (!danglingPointer) disconnectNode(node); auto parentNode = m_childParentMap.value(node); const QModelIndex parentIndex = indexForNode(parentNode); if (parentNode && !parentIndex.isValid()) return; auto &siblings = m_parentChildMap[parentNode]; auto it = std::lower_bound(siblings.begin(), siblings.end(), node); if (it == siblings.end() || *it != node) return; const int row = std::distance(siblings.begin(), it); beginRemoveRows(parentIndex, row, row); siblings.erase(it); removeSubtree(node, danglingPointer); endRemoveRows(); } void FrameGraphModel::removeSubtree(Qt3DRender::QFrameGraphNode *node, bool danglingPointer) { const auto children = m_parentChildMap.value(node); for (auto child : children) removeSubtree(child, danglingPointer); m_childParentMap.remove(node); m_parentChildMap.remove(node); } void FrameGraphModel::objectReparented(QObject *obj) { auto node = qobject_cast(obj); if (!node) return; if (m_childParentMap.contains(node)) { if (isRenderSettingsForNode(m_settings, node)) { // TODO reparented inside our tree } else { // moved to outside of our tree removeNode(node, false); } } else { // possibly reparented into our tree objectCreated(obj); } } void FrameGraphModel::connectNode(Qt3DRender::QFrameGraphNode *node) { connect(node, &Qt3DRender::QFrameGraphNode::enabledChanged, this, &FrameGraphModel::nodeEnabledChanged); } void FrameGraphModel::disconnectNode(Qt3DRender::QFrameGraphNode *node) { disconnect(node, &Qt3DRender::QFrameGraphNode::enabledChanged, this, &FrameGraphModel::nodeEnabledChanged); } void FrameGraphModel::nodeEnabledChanged() { auto node = qobject_cast(sender()); if (!node) return; const auto idx = indexForNode(node); if (!idx.isValid()) return; emit dataChanged(idx, idx); } gammaray-2.11.3/plugins/qt3dinspector/framegraphmodel.h000066400000000000000000000057161413310443000231670ustar00rootroot00000000000000/* framegraphmodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_FRAMEGRAPHMODEL_H #define GAMMARAY_FRAMEGRAPHMODEL_H #include #include #include #include QT_BEGIN_NAMESPACE namespace Qt3DRender { class QFrameGraphNode; class QRenderSettings; } QT_END_NAMESPACE namespace GammaRay { class FrameGraphModel : public ObjectModelBase { Q_OBJECT public: explicit FrameGraphModel(QObject *parent = nullptr); ~FrameGraphModel(); void setRenderSettings(Qt3DRender::QRenderSettings *settings); QVariant data(const QModelIndex &index, int role) const override; int rowCount(const QModelIndex &parent) const override; QModelIndex parent(const QModelIndex &child) const override; QModelIndex index(int row, int column, const QModelIndex &parent) const override; Qt::ItemFlags flags(const QModelIndex &index) const override; bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; public slots: void objectCreated(QObject *obj); void objectDestroyed(QObject *obj); void objectReparented(QObject *obj); private: void clear(); void populateFromNode(Qt3DRender::QFrameGraphNode *node); void removeNode(Qt3DRender::QFrameGraphNode *node, bool danglingPointer); void removeSubtree(Qt3DRender::QFrameGraphNode *node, bool danglingPointer); QModelIndex indexForNode(Qt3DRender::QFrameGraphNode *node) const; void connectNode(Qt3DRender::QFrameGraphNode *node); void disconnectNode(Qt3DRender::QFrameGraphNode *node); void nodeEnabledChanged(); private: Qt3DRender::QRenderSettings *m_settings; QHash m_childParentMap; QHash > m_parentChildMap; }; } #endif // GAMMARAY_FRAMEGRAPHMODEL_H gammaray-2.11.3/plugins/qt3dinspector/gammaray_3dinspector.json000066400000000000000000000005231413310443000246560ustar00rootroot00000000000000{ "hidden": false, "id": "gammaray_3dinspector", "name": "Qt3D Inspector", "name[de]": "Qt3D Inspektor", "selectableTypes": [ "Qt3DCore::QAspectEngine", "Qt3DCore::QEntity", "Qt3DRender::QFrameGraphNode" ], "types": [ "Qt3DCore::QAspectEngine", "Qt3DCore::QNode" ] } gammaray-2.11.3/plugins/qt3dinspector/geometryextension/000077500000000000000000000000001413310443000234405ustar00rootroot00000000000000gammaray-2.11.3/plugins/qt3dinspector/geometryextension/attribute.cpp000066400000000000000000000053731413310443000261570ustar00rootroot00000000000000/* attribute.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ // krazy:excludeall=typedefs since we need int8_t and friends in here #include "attribute.h" #include using namespace GammaRay; // ### careful: order must match qattribute.h static const int size_table[] = { sizeof(int8_t), // Byte sizeof(uint8_t), // UnsignedByte sizeof(int16_t), // Short sizeof(uint16_t), // UnsignedShort sizeof(int32_t), // Int sizeof(uint32_t), // UnsignedInt 16, // HalfFloat sizeof(float), // Float sizeof(double), // Double }; int Attribute::size(Qt3DRender::QAttribute::VertexBaseType type) { return size_table[type]; } template static QVariant toVariant(const char *data) { // cppcheck-suppress invalidPointerCast return QVariant::fromValue(*reinterpret_cast(data)); } QVariant Attribute::variant(Qt3DRender::QAttribute::VertexBaseType type, const char *data) { switch (type) { case Qt3DRender::QAttribute::Byte: return toVariant(data); case Qt3DRender::QAttribute::UnsignedByte: return toVariant(data); case Qt3DRender::QAttribute::Short: return toVariant(data); case Qt3DRender::QAttribute::UnsignedShort: return toVariant(data); case Qt3DRender::QAttribute::Int: return toVariant(data); case Qt3DRender::QAttribute::UnsignedInt: return toVariant(data); case Qt3DRender::QAttribute::HalfFloat: return QVariant("TODO"); case Qt3DRender::QAttribute::Float: return toVariant(data); case Qt3DRender::QAttribute::Double: return toVariant(data); } return QVariant(); } gammaray-2.11.3/plugins/qt3dinspector/geometryextension/attribute.h000066400000000000000000000027221413310443000256170ustar00rootroot00000000000000/* attribute.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_ATTRIBUTE_H #define GAMMARAY_ATTRIBUTE_H #include namespace GammaRay { /** Utilities for decoding attribute data. */ namespace Attribute { int size(Qt3DRender::QAttribute::VertexBaseType type); QVariant variant(Qt3DRender::QAttribute::VertexBaseType type, const char *data); } } #endif // GAMMARAY_ATTRIBUTE_H gammaray-2.11.3/plugins/qt3dinspector/geometryextension/boundingvolume.cpp000066400000000000000000000040031413310443000271760ustar00rootroot00000000000000/* boundingvolume.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "boundingvolume.h" #include using namespace GammaRay; void BoundingVolume::addPoint(const QVector3D &p) { if (m_null) { m_null = false; m_p1 = p; m_p2 = p; } else { m_p1.setX(std::max(m_p1.x(), p.x())); m_p1.setY(std::max(m_p1.y(), p.y())); m_p1.setZ(std::max(m_p1.z(), p.z())); m_p2.setX(std::min(m_p2.x(), p.x())); m_p2.setY(std::min(m_p2.y(), p.y())); m_p2.setZ(std::min(m_p2.z(), p.z())); } } QVector3D BoundingVolume::center() const { return QVector3D( (m_p1.x() + m_p2.x()) * 0.5f, (m_p1.y() + m_p2.y()) * 0.5f, (m_p1.z() + m_p2.z()) * 0.5f ); } float BoundingVolume::radius() const { const auto dx = m_p1.x() - m_p2.x(); const auto dy = m_p1.y() - m_p2.y(); const auto dz = m_p1.z() - m_p2.z(); return std::sqrt(dx*dx + dy*dy + dz*dz) * 0.5; } gammaray-2.11.3/plugins/qt3dinspector/geometryextension/boundingvolume.h000066400000000000000000000027671413310443000266620ustar00rootroot00000000000000/* boundingvolume.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_BOUNDINGVOLUME_H #define GAMMARAY_BOUNDINGVOLUME_H #include namespace GammaRay { class BoundingVolume { public: BoundingVolume() = default; void addPoint(const QVector3D &p); QVector3D center() const; float radius() const; private: QVector3D m_p1; QVector3D m_p2; bool m_null = true; }; } #endif // GAMMARAY_BOUNDINGVOLUME_H gammaray-2.11.3/plugins/qt3dinspector/geometryextension/buffermodel.cpp000066400000000000000000000100021413310443000264270ustar00rootroot00000000000000/* buffermodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "buffermodel.h" #include "attribute.h" #include using namespace GammaRay; BufferModel::BufferModel(QObject *parent) : QAbstractTableModel(parent) , m_bufferIndex(-1) , m_rowSize(0) { } BufferModel::~BufferModel() { } void BufferModel::setGeometryData(const GammaRay::Qt3DGeometryData &data) { beginResetModel(); m_data = data; m_bufferIndex = 0; updateAttributes(); endResetModel(); } void BufferModel::setBufferIndex(int index) { beginResetModel(); m_bufferIndex = index; updateAttributes(); endResetModel(); } void BufferModel::updateAttributes() { m_attrs.clear(); m_buffer.clear(); if (m_data.buffers.isEmpty() || m_bufferIndex < 0) return; Q_ASSERT(m_data.buffers.size() >= m_bufferIndex); m_buffer = m_data.buffers.at(m_bufferIndex).data; for (const auto &attr : qAsConst(m_data.attributes)) { if (attr.bufferIndex == (uint)m_bufferIndex) updateAttribute(attr); } std::sort(m_attrs.begin(), m_attrs.end(), [](const ColumnData &lhs, const ColumnData &rhs) { return lhs.offset < rhs.offset; }); } void BufferModel::updateAttribute(const GammaRay::Qt3DGeometryAttributeData &attrData) { m_rowSize = m_buffer.size() / attrData.count; for (uint i = 0; i < std::max(attrData.vertexSize, 1u); ++i) { ColumnData col; col.name = attrData.name; if (attrData.vertexSize > 1) col.name += QLatin1Char('[') + QString::number(i) + QLatin1Char(']'); col.offset = attrData.byteOffset + i * Attribute::size(attrData.vertexBaseType); col.type = attrData.vertexBaseType; col.stride = std::max(attrData.byteStride, (uint)Attribute::size(attrData.vertexBaseType) * attrData.vertexSize); m_attrs.push_back(col); } } int BufferModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); return m_attrs.size(); } int BufferModel::rowCount(const QModelIndex &parent) const { if (parent.isValid() || m_attrs.isEmpty()) return 0; return m_buffer.size() / m_rowSize; } QVariant BufferModel::data(const QModelIndex &index, int role) const { if (!index.isValid() || m_attrs.isEmpty()) return QVariant(); if (role == Qt::DisplayRole) { const auto &attr = m_attrs.at(index.column()); const char *c = m_buffer.constData() + (attr.stride * index.row()) + attr.offset; return Attribute::variant(m_attrs.at(index.column()).type, c); } return QVariant(); } QVariant BufferModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) return m_attrs.at(section).name; else if (orientation == Qt::Vertical && role == Qt::DisplayRole) return QString::number(section); // 0-based rather than 1-based return QAbstractTableModel::headerData(section, orientation, role); } gammaray-2.11.3/plugins/qt3dinspector/geometryextension/buffermodel.h000066400000000000000000000043061413310443000261060ustar00rootroot00000000000000/* buffermodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_BUFFERMODEL_H #define GAMMARAY_BUFFERMODEL_H #include "qt3dgeometryextensioninterface.h" #include namespace GammaRay { class BufferModel : public QAbstractTableModel { Q_OBJECT public: explicit BufferModel(QObject *parent = nullptr); ~BufferModel(); void setGeometryData(const Qt3DGeometryData &data); void setBufferIndex(int index); int columnCount(const QModelIndex &parent) const override; int rowCount(const QModelIndex &parent) const override; QVariant data(const QModelIndex &index, int role) const override; QVariant headerData(int section, Qt::Orientation orientation, int role) const override; private: void updateAttributes(); void updateAttribute(const Qt3DGeometryAttributeData &attrData); Qt3DGeometryData m_data; struct ColumnData { QString name; Qt3DRender::QAttribute::VertexBaseType type; uint offset; uint stride; }; QVector m_attrs; QByteArray m_buffer; int m_bufferIndex; int m_rowSize; }; } #endif // GAMMARAY_BUFFERMODEL_H gammaray-2.11.3/plugins/qt3dinspector/geometryextension/cameracontroller.cpp000066400000000000000000000135011413310443000275000ustar00rootroot00000000000000/* cameracontroller.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "cameracontroller.h" #include #include #include #include #include #include #include #include #include #include #include #include using namespace GammaRay; CameraController::CameraController(Qt3DCore::QNode *parent) : Qt3DCore::QEntity(parent) , m_camera(nullptr) , m_leftMouseButtonAction(new Qt3DInput::QAction(this)) , m_midMouseButtonAction(new Qt3DInput::QAction(this)) , m_keyboardDevice(new Qt3DInput::QKeyboardDevice) { for (int i = 0; i < AXISCOUNT; ++i) m_axis[i] = new Qt3DInput::QAxis(this); // LMB for first person control auto mouseDevice = new Qt3DInput::QMouseDevice(this); auto leftMouseButtonInput = new Qt3DInput::QActionInput(this); leftMouseButtonInput->setButtons({Qt3DInput::QMouseEvent::LeftButton}); leftMouseButtonInput->setSourceDevice(mouseDevice); m_leftMouseButtonAction->addInput(leftMouseButtonInput); // MMB for orbit control auto midMouseButtonInput = new Qt3DInput::QActionInput(this); midMouseButtonInput->setButtons({Qt3DInput::QMouseEvent::MiddleButton}); midMouseButtonInput->setSourceDevice(mouseDevice); m_midMouseButtonAction->addInput(midMouseButtonInput); // mouse wheel zoom/strafe #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) auto xWheelInput = new Qt3DInput::QAnalogAxisInput(this); xWheelInput->setAxis(Qt3DInput::QMouseDevice::WheelX); xWheelInput->setSourceDevice(mouseDevice); m_axis[TX]->addInput(xWheelInput); auto yWheelInput = new Qt3DInput::QAnalogAxisInput(this); yWheelInput->setAxis(Qt3DInput::QMouseDevice::WheelY); yWheelInput->setSourceDevice(mouseDevice); m_axis[TZ]->addInput(yWheelInput); #endif // TODO: shift for slow motion, ctrl for fast motion // X rotation auto mouseRxInput = new Qt3DInput::QAnalogAxisInput; mouseRxInput->setAxis(Qt3DInput::QMouseDevice::X); mouseRxInput->setSourceDevice(mouseDevice); m_axis[RX]->addInput(mouseRxInput); // Y rotation auto mouseRyInput = new Qt3DInput::QAnalogAxisInput; mouseRyInput->setAxis(Qt3DInput::QMouseDevice::Y); mouseRyInput->setSourceDevice(mouseDevice); m_axis[RY]->addInput(mouseRyInput); // X translation addKeyboardInput(TX, Qt::Key_Right, 1.0f); addKeyboardInput(TX, Qt::Key_D, 1.0f); addKeyboardInput(TX, Qt::Key_Left, -1.0f); addKeyboardInput(TX, Qt::Key_A, -1.0f); // Y translation addKeyboardInput(TY, Qt::Key_PageUp, 1.0f); addKeyboardInput(TY, Qt::Key_R, 1.0f); addKeyboardInput(TY, Qt::Key_PageDown, -1.0f); addKeyboardInput(TY, Qt::Key_F, -1.0f); // Z translation addKeyboardInput(TZ, Qt::Key_Up, 1.0f); addKeyboardInput(TZ, Qt::Key_W, 1.0f); addKeyboardInput(TZ, Qt::Key_Down, -1.0f); addKeyboardInput(TZ, Qt::Key_S, -1.0f); // logical device auto m_logicalDevice = new Qt3DInput::QLogicalDevice; m_logicalDevice->addAction(m_leftMouseButtonAction); m_logicalDevice->addAction(m_midMouseButtonAction); for (int i = 0; i < AXISCOUNT; ++i) m_logicalDevice->addAxis(m_axis[i]); addComponent(m_logicalDevice); auto m_frameAction = new Qt3DLogic::QFrameAction; connect(m_frameAction, &Qt3DLogic::QFrameAction::triggered, this, &CameraController::frameActionTriggered); addComponent(m_frameAction); } CameraController::~CameraController() { } void CameraController::setCamera(Qt3DRender::QCamera *camera) { m_camera = camera; } void CameraController::frameActionTriggered(float dt) { if (!m_camera) return; m_camera->translate(QVector3D(m_axis[TX]->value() * m_linearSpeed, m_axis[TY]->value() * m_linearSpeed, m_axis[TZ]->value() * m_linearSpeed) * dt); if (m_leftMouseButtonAction->isActive()) { m_camera->pan(m_axis[RX]->value() * m_lookSpeed * dt, QVector3D(0.0f, 1.0f, 0.0f)); m_camera->tilt(m_axis[RY]->value() * m_lookSpeed * dt); } else if (m_midMouseButtonAction->isActive()) { m_camera->panAboutViewCenter(m_axis[RX]->value() * m_lookSpeed * dt, QVector3D(0.0f, 1.0f, 0.0f)); m_camera->tiltAboutViewCenter(m_axis[RY]->value() * m_lookSpeed * dt); } } void CameraController::addKeyboardInput(Axis axis, Qt::Key key, float scale) { auto keyboardInput = new Qt3DInput::QButtonAxisInput; keyboardInput->setButtons({key}); keyboardInput->setScale(scale); keyboardInput->setSourceDevice(m_keyboardDevice); m_axis[axis]->addInput(keyboardInput); } gammaray-2.11.3/plugins/qt3dinspector/geometryextension/cameracontroller.h000066400000000000000000000041651413310443000271530ustar00rootroot00000000000000/* cameracontroller.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_CAMERACONTROLLER_H #define GAMMARAY_CAMERACONTROLLER_H #include QT_BEGIN_NAMESPACE namespace Qt3DRender { class QCamera; } namespace Qt3DInput { class QAction; class QAxis; class QKeyboardDevice; } QT_END_NAMESPACE namespace GammaRay { class CameraController : public Qt3DCore::QEntity { Q_OBJECT public: explicit CameraController(Qt3DCore::QNode *parent = nullptr); ~CameraController(); void setCamera(Qt3DRender::QCamera *camera); private: enum Axis { RX, RY, RZ, TX, TY, TZ, AXISCOUNT }; void frameActionTriggered(float dt); void addKeyboardInput(Axis axis, Qt::Key key, float scale); Qt3DRender::QCamera *m_camera; Qt3DInput::QAction *m_leftMouseButtonAction; Qt3DInput::QAction *m_midMouseButtonAction; Qt3DInput::QAxis *m_axis[AXISCOUNT]; Qt3DInput::QKeyboardDevice *m_keyboardDevice; float m_linearSpeed = 10.0f; // TODO float m_lookSpeed = 360.0f; // TODO }; } #endif // GAMMARAY_CAMERACONTROLLER_H gammaray-2.11.3/plugins/qt3dinspector/geometryextension/es2/000077500000000000000000000000001413310443000241315ustar00rootroot00000000000000gammaray-2.11.3/plugins/qt3dinspector/geometryextension/es2/CMakeLists.txt000066400000000000000000000002361413310443000266720ustar00rootroot00000000000000glslang_validate_program(skybox.vert skybox.frag) glslang_validate_program(surface.vert surface.frag) glslang_validate_program(wireframe.vert wireframe.frag) gammaray-2.11.3/plugins/qt3dinspector/geometryextension/es2/skybox.frag000066400000000000000000000005041413310443000263100ustar00rootroot00000000000000precision highp float; varying vec3 texCoord0; void main() { float fieldCount = 99.0; float colorOffset = 0.1 * mod(floor(fieldCount * texCoord0.x) + floor(fieldCount * texCoord0.y) + floor(fieldCount * texCoord0.z), 2.0); gl_FragColor = vec4( 0.5 + colorOffset, 0.5 + colorOffset, 0.5 + colorOffset, 1.0 ); } gammaray-2.11.3/plugins/qt3dinspector/geometryextension/es2/skybox.vert000066400000000000000000000004721413310443000263550ustar00rootroot00000000000000attribute vec3 vertexPosition; varying vec3 texCoord0; uniform mat4 mvp; uniform mat4 inverseProjectionMatrix; uniform mat4 inverseModelView; uniform mat4 viewMatrix; uniform mat4 projectionMatrix; void main() { texCoord0 = vertexPosition.xyz; gl_Position = vec4(mvp * vec4(vertexPosition, 1.0)).xyww; } gammaray-2.11.3/plugins/qt3dinspector/geometryextension/es2/surface.frag000066400000000000000000000045321413310443000264260ustar00rootroot00000000000000precision highp float; varying vec3 position; varying vec3 normal; varying vec4 color; const int MAX_LIGHTS = 8; const int TYPE_POINT = 0; const int TYPE_DIRECTIONAL = 1; const int TYPE_SPOT = 2; struct Light { int type; vec3 position; vec3 color; float intensity; vec3 direction; float constantAttenuation; float linearAttenuation; float quadraticAttenuation; float cutOffAngle; }; uniform Light lights[MAX_LIGHTS]; uniform int lightCount; const vec3 Ka = vec3(0.45, 0.25, 0.15); // Ambient reflectivity const vec3 Kd = vec3(0.9, 0.5, 0.3); // Diffuse reflectivity const vec3 Ks = vec3(0.09, 0.05, 0.03); // Specular reflectivity const float shininess = 25.0; // Specular shininess factor uniform highp int shadingMode; const int ShadingModePhong = 1; const int ShadingModeWireframe = 6; vec3 adsModel( const in vec3 pos, const in vec3 normal ) { // Calculate the vector from the fragment to the light vec3 s = normalize( vec3( lights[0].position ) - pos ); // Calculate the vector from the fragment to the eye position (the // origin since this is in "eye" or "camera" space vec3 v = normalize( -pos ); // Refleft the light beam using the normal at this fragment vec3 r = reflect( -s, normal ); // Calculate the diffus component vec3 diffuse = vec3( max( dot( s, normal ), 0.0 ) ); // Calculate the specular component vec3 specular = vec3( pow( max( dot( r, v ), 0.0 ), shininess ) ) * ( shininess + 2.0 ) / 2.0; // Calculate distance from the fragment to the light float dist = distance( vec3( lights[0].position ), pos ); // Calculate light intensity when attenuation is applied float intensity = lights[0].intensity / ( lights[0].quadraticAttenuation * pow( dist, 2.0 ) + lights[0].linearAttenuation * dist + lights[0].constantAttenuation); // Combine the ambient, diffuse and specular contributions return intensity * ( Ka + Kd * diffuse + Ks * specular ); } void main() { if (gl_FrontFacing) { if (shadingMode == ShadingModePhong) { gl_FragColor = vec4(adsModel(position, normalize(normal)), 1.0); } else { gl_FragColor = color; } } else { if (shadingMode == ShadingModeWireframe) { gl_FragColor = color; } else { gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); } } } gammaray-2.11.3/plugins/qt3dinspector/geometryextension/es2/surface.vert000066400000000000000000000027041413310443000264660ustar00rootroot00000000000000attribute vec3 vertexPosition; attribute vec3 vertexNormal; attribute vec4 vertexTangent; attribute vec2 vertexTexCoord; attribute vec4 vertexColor; varying vec3 position; varying vec3 normal; varying vec4 color; uniform mat4 modelMatrix; uniform mat3 modelNormalMatrix; uniform mat4 modelViewProjection; uniform int shadingMode; const int ShadingModeFlat = 0; const int ShadingModePhong = 1; const int ShadingModeTexture = 2; const int ShadingModeNormal = 3; const int ShadingModeTangent = 4; const int ShadingModeColor = 5; const int ShadingModeWireframe = 6; void main() { position = vertexPosition; normal = normalize(modelNormalMatrix * vertexNormal); if (shadingMode == ShadingModeFlat) { color = vec4(0.45, 0.25, 0.15, 1.0); } else if (shadingMode == ShadingModePhong) { // handling in fragment shader } else if (shadingMode == ShadingModeTexture) { color = vec4(0.0, vertexTexCoord.x, vertexTexCoord.y, 1.0); } else if (shadingMode == ShadingModeNormal) { color = vec4(normalize(vertexNormal + vec3(1.0, 1.0, 1.0)), 1.0); } else if (shadingMode == ShadingModeTangent) { color = vec4(normalize(vertexTangent.xyz + vec3(1.0, 1.0, 1.0)), 1.0); } else if (shadingMode == ShadingModeColor) { color = vertexColor; } else if (shadingMode == ShadingModeWireframe) { color = vec4(1.0, 1.0, 1.0, 0.0); } gl_Position = modelViewProjection * vec4( vertexPosition, 1.0 ); } gammaray-2.11.3/plugins/qt3dinspector/geometryextension/es2/wireframe.frag000066400000000000000000000000751413310443000267550ustar00rootroot00000000000000void main() { gl_FragColor = vec4(0.4, 1.0, 0.8, 1.0); } gammaray-2.11.3/plugins/qt3dinspector/geometryextension/es2/wireframe.vert000066400000000000000000000002301413310443000270070ustar00rootroot00000000000000attribute vec3 vertexPosition; uniform mat4 modelViewProjection; void main() { gl_Position = modelViewProjection * vec4( vertexPosition, 1.0 ); } gammaray-2.11.3/plugins/qt3dinspector/geometryextension/gl3/000077500000000000000000000000001413310443000241255ustar00rootroot00000000000000gammaray-2.11.3/plugins/qt3dinspector/geometryextension/gl3/CMakeLists.txt000066400000000000000000000002761413310443000266720ustar00rootroot00000000000000glslang_validate_program(passthrough.vert normals.geom normals.frag) glslang_validate_program(wireframe.vert wireframe.geom wireframe.frag) glslang_validate_program(skybox.vert skybox.frag) gammaray-2.11.3/plugins/qt3dinspector/geometryextension/gl3/normals.frag000066400000000000000000000002151413310443000264370ustar00rootroot00000000000000#version 330 core in ColorFormat { vec3 color; } fs_in; out vec4 fragColor; void main() { fragColor = vec4( fs_in.color, 1.0 ); } gammaray-2.11.3/plugins/qt3dinspector/geometryextension/gl3/normals.geom000066400000000000000000000040011413310443000264440ustar00rootroot00000000000000#version 330 core layout (triangles) in; layout (line_strip, max_vertices = 8) out; in VertexFormat { vec3 position; vec3 normal; } gs_in[]; out ColorFormat { vec3 color; } gs_out; uniform mat4 mvp; uniform float normalLength = 0.1; uniform vec3 faceNormalColor = vec3( 1.0, 1.0, 0.0 ); uniform vec3 vertexNormalColor = vec3( 0.0, 0.0, 1.0 ); void main() { // We are working in model space here since the positions were // just passed through form the vertex shader. // First, calculate the face normal and construct a line vec3 ba = vec3( gs_in[1].position - gs_in[0].position ); vec3 ac = vec3( gs_in[2].position - gs_in[0].position ); vec3 faceNormal = normalize( cross( ba, ac ) ); // Calculate the center location of the triangle vec3 triangleCenter = ( gs_in[0].position + gs_in[1].position + gs_in[2].position ) / 3.0; // Emit a vertex from the triangle center and another from the // triangle center displaced in the direction of the face normal. // Remember to transform into clip-space gs_out.color = faceNormalColor; gl_Position = mvp * vec4( triangleCenter, 1.0 ); EmitVertex(); gs_out.color = faceNormalColor; gl_Position = mvp * vec4( triangleCenter + normalLength * faceNormal, 1.0 ); EmitVertex(); // Finish off the face normal line EndPrimitive(); // Now do similar for each of the input triangle vertices but use the vertex normal // instead of the face normal for ( int i = 0; i < gl_in.length(); i++ ) { // Start point of line at vertex position gs_out.color = vertexNormalColor; gl_Position = mvp * vec4( gs_in[i].position, 1.0 ); EmitVertex(); // End point of line offset in normal direction gs_out.color = vertexNormalColor; gl_Position = mvp * vec4( gs_in[i].position + normalLength * gs_in[i].normal, 1.0 ); EmitVertex(); // Finish that line off EndPrimitive(); } } gammaray-2.11.3/plugins/qt3dinspector/geometryextension/gl3/passthrough.vert000066400000000000000000000006401413310443000273760ustar00rootroot00000000000000#version 330 core in vec3 vertexPosition; in vec3 vertexNormal; out VertexFormat { vec3 position; vec3 normal; } vs_out; uniform mat4 modelMatrix; uniform mat3 modelNormalMatrix; uniform mat4 modelViewProjection; void main() { vs_out.normal = normalize( modelNormalMatrix * vertexNormal ); vs_out.position = vertexPosition; gl_Position = modelViewProjection * vec4( vertexPosition, 1.0 ); } gammaray-2.11.3/plugins/qt3dinspector/geometryextension/gl3/skybox.frag000066400000000000000000000005131413310443000263040ustar00rootroot00000000000000#version 330 core in vec3 texCoord0; out vec4 fragColor; void main() { float fieldCount = 99.0; float colorOffset = 0.1 * mod(floor(fieldCount * texCoord0.x) + floor(fieldCount * texCoord0.y) + floor(fieldCount * texCoord0.z), 2.0); fragColor = vec4( 0.5 + colorOffset, 0.5 + colorOffset, 0.5 + colorOffset, 1.0 ); } gammaray-2.11.3/plugins/qt3dinspector/geometryextension/gl3/skybox.vert000066400000000000000000000005021413310443000263430ustar00rootroot00000000000000#version 330 core in vec3 vertexPosition; out vec3 texCoord0; uniform mat4 mvp; uniform mat4 inverseProjectionMatrix; uniform mat4 inverseModelView; uniform mat4 viewMatrix; uniform mat4 projectionMatrix; void main() { texCoord0 = vertexPosition.xyz; gl_Position = vec4(mvp * vec4(vertexPosition, 1.0)).xyww; } gammaray-2.11.3/plugins/qt3dinspector/geometryextension/gl3/wireframe.frag000066400000000000000000000057761413310443000267660ustar00rootroot00000000000000#version 330 core in VertexFormat { vec3 position; vec3 normal; noperspective vec3 edgeDistance; vec4 color; } fs_in; out vec4 fragColor; const int MAX_LIGHTS = 8; const int TYPE_POINT = 0; const int TYPE_DIRECTIONAL = 1; const int TYPE_SPOT = 2; struct Light { int type; vec3 position; vec3 color; float intensity; vec3 direction; float constantAttenuation; float linearAttenuation; float quadraticAttenuation; float cutOffAngle; }; uniform Light lights[MAX_LIGHTS]; uniform int lightCount; uniform vec3 Ka = vec3(0.45, 0.25, 0.15); // Ambient reflectivity uniform vec3 Kd = vec3(0.9, 0.5, 0.3); // Diffuse reflectivity uniform vec3 Ks = vec3(0.09, 0.05, 0.03); // Specular reflectivity uniform float shininess = 25; // Specular shininess factor uniform float lineWidth = 0.4; uniform vec4 lineColor = vec4(0.4, 1.0, 0.8, 1.0); uniform int shadingMode = 0; const int ShadingModePhong = 1; const int ShadingModeWireframe = 6; vec3 adsModel( const in vec3 pos, const in vec3 normal ) { // Calculate the vector from the fragment to the light vec3 s = normalize( vec3( lights[0].position ) - pos ); // Calculate the vector from the fragment to the eye position (the // origin since this is in "eye" or "camera" space vec3 v = normalize( -pos ); // Refleft the light beam using the normal at this fragment vec3 r = reflect( -s, normal ); // Calculate the diffus component vec3 diffuse = vec3( max( dot( s, normal ), 0.0 ) ); // Calculate the specular component vec3 specular = vec3( pow( max( dot( r, v ), 0.0 ), shininess ) ) * ( shininess + 2.0 ) / 2.0; // Calculate distance from the fragment to the light float dist = distance( vec3( lights[0].position ), pos ); // Calculate light intensity when attenuation is applied float intensity = lights[0].intensity / ( lights[0].quadraticAttenuation * pow( dist, 2.0 ) + lights[0].linearAttenuation * dist + lights[0].constantAttenuation); // Combine the ambient, diffuse and specular contributions return intensity * ( Ka + Kd * diffuse + Ks * specular ); } void main() { vec4 color; if (gl_FrontFacing) { if (shadingMode == ShadingModePhong) color = vec4( adsModel( fs_in.position, normalize( fs_in.normal ) ), 1.0 ); else color = fs_in.color; } else { if (shadingMode == ShadingModeWireframe) color = fs_in.color; else color = vec4(1.0, 0.0, 0.0, 1.0); } // Find the smallest distance between the fragment and a triangle edge float d = min( fs_in.edgeDistance.x, fs_in.edgeDistance.y ); d = min( d, fs_in.edgeDistance.z ); // Blend between line color and phong color float mixVal; if ( d < lineWidth - 1 ) { mixVal = 1.0; } else if ( d > lineWidth + 1 ) { mixVal = 0.0; } else { float x = d - ( lineWidth - 1 ); mixVal = exp2( -2.0 * ( x * x ) ); } fragColor = mix( color, lineColor, mixVal ); } gammaray-2.11.3/plugins/qt3dinspector/geometryextension/gl3/wireframe.geom000066400000000000000000000041001413310443000267520ustar00rootroot00000000000000#version 330 core layout (triangles) in; layout (triangle_strip, max_vertices = 3) out; in VertexFormat { vec3 position; vec3 normal; vec4 color; } gs_in[]; out VertexFormat { vec3 position; vec3 normal; noperspective vec3 edgeDistance; vec4 color; } gs_out; uniform mat4 viewportMatrix; void main() { // Transform each vertex into viewport space vec2 p0 = vec2( viewportMatrix * ( gl_in[0].gl_Position / gl_in[0].gl_Position.w ) ); vec2 p1 = vec2( viewportMatrix * ( gl_in[1].gl_Position / gl_in[1].gl_Position.w ) ); vec2 p2 = vec2( viewportMatrix * ( gl_in[2].gl_Position / gl_in[2].gl_Position.w ) ); // Calculate lengths of 3 edges of triangle float a = length( p1 - p2 ); float b = length( p2 - p0 ); float c = length( p1 - p0 ); // Calculate internal angles using the cosine rule float alpha = acos( ( b * b + c * c - a * a ) / ( 2.0 * b * c ) ); float beta = acos( ( a * a + c * c - b * b ) / ( 2.0 * a * c ) ); // Calculate the perpendicular distance of each vertex from the opposing edge float ha = abs( c * sin( beta ) ); float hb = abs( c * sin( alpha ) ); float hc = abs( b * sin( alpha ) ); // Now add this perpendicular distance as a per-vertex property in addition to // the position and normal calculated in the vertex shader. // Vertex 0 (a) gs_out.edgeDistance = vec3( ha, 0, 0 ); gs_out.normal = gs_in[0].normal; gs_out.position = gs_in[0].position; gs_out.color = gs_in[0].color; gl_Position = gl_in[0].gl_Position; EmitVertex(); // Vertex 1 (b) gs_out.edgeDistance = vec3( 0, hb, 0 ); gs_out.normal = gs_in[1].normal; gs_out.position = gs_in[1].position; gs_out.color = gs_in[1].color; gl_Position = gl_in[1].gl_Position; EmitVertex(); // Vertex 2 (c) gs_out.edgeDistance = vec3( 0, 0, hc ); gs_out.normal = gs_in[2].normal; gs_out.position = gs_in[2].position; gs_out.color = gs_in[2].color; gl_Position = gl_in[2].gl_Position; EmitVertex(); // Finish the primitive off EndPrimitive(); } gammaray-2.11.3/plugins/qt3dinspector/geometryextension/gl3/wireframe.vert000066400000000000000000000030641413310443000270130ustar00rootroot00000000000000#version 330 core in vec3 vertexPosition; in vec3 vertexNormal; in vec4 vertexTangent; in vec2 vertexTexCoord; in vec4 vertexColor; out VertexFormat { vec3 position; vec3 normal; vec4 color; } vs_out; uniform mat4 modelMatrix; uniform mat3 modelNormalMatrix; uniform mat4 modelViewProjection; uniform int shadingMode = 0; const int ShadingModeFlat = 0; const int ShadingModePhong = 1; const int ShadingModeTexture = 2; const int ShadingModeNormal = 3; const int ShadingModeTangent = 4; const int ShadingModeColor = 5; const int ShadingModeWireframe = 6; void main() { vs_out.normal = normalize( modelNormalMatrix * vertexNormal ); vs_out.position = vertexPosition; switch (shadingMode) { case ShadingModeFlat: vs_out.color = vec4(0.45, 0.25, 0.15, 1.0); break; case ShadingModePhong: break; // handling in fragment shader case ShadingModeTexture: vs_out.color = vec4(0.0, vertexTexCoord.x, vertexTexCoord.y, 1.0); break; case ShadingModeNormal: vs_out.color = vec4(normalize(vertexNormal + vec3(1.0, 1.0, 1.0)), 1.0); break; case ShadingModeTangent: vs_out.color = vec4(normalize(vertexTangent.xyz + vec3(1.0, 1.0, 1.0)), 1.0); break; case ShadingModeColor: vs_out.color = vertexColor; break; case ShadingModeWireframe: vs_out.color = vec4(1.0, 1.0, 1.0, 0.0); break; } gl_Position = modelViewProjection * vec4( vertexPosition, 1.0 ); } gammaray-2.11.3/plugins/qt3dinspector/geometryextension/qt3dgeometryextension.cpp000066400000000000000000000107701413310443000305350ustar00rootroot00000000000000/* qt3dgeometryextension.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "qt3dgeometryextension.h" #include #include #include #include #include #include #include #include #include using namespace GammaRay; Qt3DGeometryExtension::Qt3DGeometryExtension(GammaRay::PropertyController *controller) : Qt3DGeometryExtensionInterface(controller->objectBaseName() + ".qt3dGeometry", controller) , PropertyControllerExtension(controller->objectBaseName() + ".qt3dGeometry") , m_geometry(nullptr) { } Qt3DGeometryExtension::~Qt3DGeometryExtension() { } bool Qt3DGeometryExtension::setQObject(QObject *object) { Qt3DRender::QGeometryRenderer *geometry = nullptr; // try to find a half-way related QGeometryRenderer instance if ((geometry = qobject_cast(object))) { } else if (auto entity = qobject_cast(object)) { foreach (auto component, entity->components()) { if ((geometry = qobject_cast(component))) break; } } else if (auto geo = qobject_cast(object)) { return setQObject(geo->parent()); } else if (auto attr = qobject_cast(object)) { return setQObject(attr->parent()); } else if (auto buffer = qobject_cast(object)) { return setQObject(buffer->parent()); } if (geometry == m_geometry) return geometry; m_geometry = geometry; if (!geometry) return false; updateGeometryData(); return true; } void Qt3DGeometryExtension::updateGeometryData() { Qt3DGeometryData data; if (!m_geometry || !m_geometry->geometry()) { setGeometryData(data); return; } QHash bufferMap; data.attributes.reserve(m_geometry->geometry()->attributes().size()); foreach (auto attr, m_geometry->geometry()->attributes()) { if (attr->count() == 0) // ignore empty/invalid attributes continue; Qt3DGeometryAttributeData attrData; attrData.name = attr->name(); attrData.attributeType = attr->attributeType(); attrData.byteOffset = attr->byteOffset(); attrData.byteStride = attr->byteStride(); attrData.count = attr->count(); attrData.divisor = attr->divisor(); attrData.vertexBaseType = attr->vertexBaseType(); attrData.vertexSize = attr->vertexSize(); const auto bufferIt = bufferMap.constFind(attr->buffer()); if (bufferIt != bufferMap.constEnd()) { attrData.bufferIndex = bufferIt.value(); } else { Qt3DGeometryBufferData buffer; buffer.name = Util::displayString(attr->buffer()); buffer.type = attr->buffer()->type(); auto generator = attr->buffer()->dataGenerator(); if (generator) buffer.data = (*generator.data())(); else buffer.data = attr->buffer()->data(); attrData.bufferIndex = data.buffers.size(); bufferMap.insert(attr->buffer(), attrData.bufferIndex); data.buffers.push_back(buffer); } data.attributes.push_back(attrData); } setGeometryData(data); } gammaray-2.11.3/plugins/qt3dinspector/geometryextension/qt3dgeometryextension.h000066400000000000000000000035601413310443000302010ustar00rootroot00000000000000/* qt3dgeometryextension.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_QT3DGEOMETRYEXTENSION_H #define GAMMARAY_QT3DGEOMETRYEXTENSION_H #include "qt3dgeometryextensioninterface.h" #include QT_BEGIN_NAMESPACE namespace Qt3DRender { class QGeometryRenderer; } QT_END_NAMESPACE namespace GammaRay { class Qt3DGeometryExtension : public Qt3DGeometryExtensionInterface, public PropertyControllerExtension { Q_OBJECT Q_INTERFACES(GammaRay::Qt3DGeometryExtensionInterface) public: explicit Qt3DGeometryExtension(PropertyController *controller); ~Qt3DGeometryExtension(); bool setQObject(QObject *object) override; private: void updateGeometryData(); Qt3DRender::QGeometryRenderer *m_geometry; }; } #endif // GAMMARAY_QT3DGEOMETRYEXTENSION_H gammaray-2.11.3/plugins/qt3dinspector/geometryextension/qt3dgeometryextensionclient.cpp000066400000000000000000000025361413310443000317350ustar00rootroot00000000000000/* qt3dgeometryextensionclient.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "qt3dgeometryextensionclient.h" using namespace GammaRay; Qt3DGeometryExtensionClient::Qt3DGeometryExtensionClient(const QString &name, QObject *parent) : Qt3DGeometryExtensionInterface(name, parent) { } gammaray-2.11.3/plugins/qt3dinspector/geometryextension/qt3dgeometryextensionclient.h000066400000000000000000000031051413310443000313730ustar00rootroot00000000000000/* qt3dgeometryextensionclient.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_QT3DGEOMETRYEXTENSIONCLIENT_H #define GAMMARAY_QT3DGEOMETRYEXTENSIONCLIENT_H #include "qt3dgeometryextensioninterface.h" namespace GammaRay { class Qt3DGeometryExtensionClient : public Qt3DGeometryExtensionInterface { Q_OBJECT Q_INTERFACES(GammaRay::Qt3DGeometryExtensionInterface) public: explicit Qt3DGeometryExtensionClient(const QString &name, QObject *parent); }; } #endif // GAMMARAY_QT3DGEOMETRYEXTENSIONCLIENT_H gammaray-2.11.3/plugins/qt3dinspector/geometryextension/qt3dgeometryextensioninterface.cpp000066400000000000000000000100361413310443000324110ustar00rootroot00000000000000/* qt3dgeometryextensioninterface.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "qt3dgeometryextensioninterface.h" #include #include #include using namespace GammaRay; QT_BEGIN_NAMESPACE GAMMARAY_ENUM_STREAM_OPERATORS(Qt3DRender::QAttribute::AttributeType) GAMMARAY_ENUM_STREAM_OPERATORS(Qt3DRender::QAttribute::VertexBaseType) GAMMARAY_ENUM_STREAM_OPERATORS(Qt3DRender::QBuffer::BufferType) static QDataStream &operator<<(QDataStream &out, const Qt3DGeometryAttributeData &data) { out << data.name << data.attributeType << data.byteOffset << data.byteStride << data.count << data.divisor << data.vertexBaseType << data.vertexSize << data.bufferIndex; return out; } static QDataStream &operator>>(QDataStream &in, Qt3DGeometryAttributeData &data) { in >> data.name >> data.attributeType >> data.byteOffset >> data.byteStride >> data.count >> data.divisor >> data.vertexBaseType >> data.vertexSize >> data.bufferIndex; return in; } QT_END_NAMESPACE bool Qt3DGeometryAttributeData::operator==(const Qt3DGeometryAttributeData &rhs) const { return name == rhs.name && attributeType == rhs.attributeType && byteOffset == rhs.byteOffset && byteStride == rhs.byteStride && count == rhs.count && divisor == rhs.divisor && vertexBaseType == rhs.vertexBaseType && vertexSize == rhs.vertexSize && bufferIndex == rhs.bufferIndex; } QT_BEGIN_NAMESPACE static QDataStream &operator<<(QDataStream &out, const Qt3DGeometryBufferData &data) { out << data.name << data.data << data.type; return out; } static QDataStream &operator>>(QDataStream &in, Qt3DGeometryBufferData &data) { in >> data.name >> data.data >> data.type; return in; } QT_END_NAMESPACE bool Qt3DGeometryBufferData::operator==(const Qt3DGeometryBufferData &rhs) const { return name == rhs.name && data == rhs.data; } QT_BEGIN_NAMESPACE static QDataStream &operator<<(QDataStream &out, const Qt3DGeometryData &data) { out << data.attributes << data.buffers; return out; } static QDataStream &operator>>(QDataStream &in, Qt3DGeometryData &data) { in >> data.attributes >> data.buffers; return in; } QT_END_NAMESPACE bool Qt3DGeometryData::operator==(const Qt3DGeometryData &rhs) const { return attributes == rhs.attributes && buffers == rhs.buffers; } Qt3DGeometryExtensionInterface::Qt3DGeometryExtensionInterface(const QString &name, QObject *parent) : QObject(parent) { qRegisterMetaType(); qRegisterMetaTypeStreamOperators(); ObjectBroker::registerObject(name, this); } Qt3DGeometryExtensionInterface::~Qt3DGeometryExtensionInterface() { } Qt3DGeometryData Qt3DGeometryExtensionInterface::geometryData() const { return m_data; } void Qt3DGeometryExtensionInterface::setGeometryData(const Qt3DGeometryData &data) { if (m_data == data) return; m_data = data; emit geometryDataChanged(); } gammaray-2.11.3/plugins/qt3dinspector/geometryextension/qt3dgeometryextensioninterface.h000066400000000000000000000060221413310443000320560ustar00rootroot00000000000000/* qt3dgeometryextensioninterface.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_QT3DGEOMETRYEXTENSIONINTERFACE_H #define GAMMARAY_QT3DGEOMETRYEXTENSIONINTERFACE_H #include #include namespace GammaRay { struct Qt3DGeometryAttributeData { Qt3DGeometryAttributeData() = default; bool operator==(const Qt3DGeometryAttributeData &rhs) const; QString name; Qt3DRender::QAttribute::AttributeType attributeType = Qt3DRender::QAttribute::VertexAttribute; uint byteOffset = 0; uint byteStride = 0; uint count = 0; uint divisor = 0; Qt3DRender::QAttribute::VertexBaseType vertexBaseType = Qt3DRender::QAttribute::UnsignedShort; uint vertexSize = 0; uint bufferIndex = 0; }; struct Qt3DGeometryBufferData { Qt3DGeometryBufferData() = default; bool operator==(const Qt3DGeometryBufferData &rhs) const; QString name; QByteArray data; Qt3DRender::QBuffer::BufferType type = Qt3DRender::QBuffer::VertexBuffer; }; struct Qt3DGeometryData { bool operator==(const Qt3DGeometryData &rhs) const; QVector attributes; QVector buffers; }; class Qt3DGeometryExtensionInterface : public QObject { Q_OBJECT Q_PROPERTY( GammaRay::Qt3DGeometryData geometryData READ geometryData WRITE setGeometryData NOTIFY geometryDataChanged) public: explicit Qt3DGeometryExtensionInterface(const QString &name, QObject *parent = nullptr); ~Qt3DGeometryExtensionInterface(); Qt3DGeometryData geometryData() const; void setGeometryData(const Qt3DGeometryData &data); signals: void geometryDataChanged(); private: Qt3DGeometryData m_data; }; } Q_DECLARE_METATYPE(GammaRay::Qt3DGeometryData) QT_BEGIN_NAMESPACE Q_DECLARE_INTERFACE(GammaRay::Qt3DGeometryExtensionInterface, "com.kdab.GammaRay.Qt3DGeometryExtensionInterface/1.0") QT_END_NAMESPACE #endif // GAMMARAY_QT3DGEOMETRYEXTENSIONINTERFACE_H gammaray-2.11.3/plugins/qt3dinspector/geometryextension/qt3dgeometrytab.cpp000066400000000000000000000760251413310443000272740ustar00rootroot00000000000000/* qt3dgeometrytab.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "qt3dgeometrytab.h" #include "ui_qt3dgeometrytab.h" #include "qt3dgeometryextensioninterface.h" #include "cameracontroller.h" #include "attribute.h" #include "buffermodel.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace GammaRay; // ### keep in sync with wireframe.vert/wireframe.frag enum ShadingMode { ShadingModeFlat = 0, ShadingModePhong = 1, ShadingModeTexture = 2, ShadingModeNormal = 3, ShadingModeTangent = 4, ShadingModeColor = 5, ShadingModeWireframe = 6 }; Qt3DGeometryTab::Qt3DGeometryTab(PropertyWidget *parent) : QWidget(parent) , ui(new Ui::Qt3DGeometryTab) , m_surface(nullptr) , m_aspectEngine(nullptr) , m_camera(nullptr) , m_geometryRenderer(nullptr) , m_es2lineRenderer(nullptr) , m_geometryTransform(nullptr) , m_cullMode(nullptr) , m_depthTest(nullptr) , m_normalsRenderPass(nullptr) , m_normalLength(nullptr) , m_shadingMode(nullptr) , m_bufferModel(new BufferModel(this)) { ui->setupUi(this); auto toolbar = new QToolBar(this); ui->topLayout->insertWidget(0, toolbar); toolbar->addAction(ui->actionViewGeometry); toolbar->addAction(ui->actionViewBuffers); toolbar->addSeparator(); toolbar->addAction(ui->actionResetCam); toolbar->addSeparator(); toolbar->addAction(ui->actionShowNormals); toolbar->addAction(ui->actionShowTangents); toolbar->addAction(ui->actionCullBack); toolbar->addSeparator(); auto shadingModeLabel = toolbar->addWidget(new QLabel(tr("Shading:"), toolbar)); m_shadingModeCombo = new QComboBox(toolbar); m_shadingModeCombo->setSizeAdjustPolicy(QComboBox::AdjustToContents); auto shadingModeAction = toolbar->addWidget(m_shadingModeCombo); connect(ui->actionResetCam, &QAction::triggered, this, &Qt3DGeometryTab::resetCamera); connect(ui->actionShowNormals, &QAction::toggled, this, [this]() { if (m_normalsRenderPass) { m_normalsRenderPass->setEnabled(ui->actionShowNormals->isChecked()); } }); connect(ui->actionCullBack, &QAction::toggled, this, [this]() { if (m_cullMode) { m_cullMode->setMode(ui->actionCullBack->isChecked() ? Qt3DRender::QCullFace::Back : Qt3DRender::QCullFace::NoCulling); } }); connect(m_shadingModeCombo, QOverload::of(&QComboBox::currentIndexChanged), this, [this]() { const auto shadingMode = m_shadingModeCombo->currentData(); if (!shadingMode.isValid() || !m_shadingMode || !m_cullMode) return; m_shadingMode->setValue(shadingMode); if (shadingMode.toInt() == ShadingModeWireframe) { ui->actionCullBack->setEnabled(false); m_cullMode->setMode(Qt3DRender::QCullFace::NoCulling); m_depthTest->setDepthFunction(Qt3DRender::QDepthTest::Always); } else { ui->actionCullBack->setEnabled(true); m_cullMode->setMode(ui->actionCullBack->isChecked() ? Qt3DRender::QCullFace::Back : Qt3DRender::QCullFace::NoCulling); m_depthTest->setDepthFunction(Qt3DRender::QDepthTest::Less); } }); auto viewGroup = new QActionGroup(this); viewGroup->setExclusive(true); viewGroup->addAction(ui->actionViewGeometry); viewGroup->addAction(ui->actionViewBuffers); connect(viewGroup, &QActionGroup::triggered, this, [this, shadingModeLabel, shadingModeAction]() { const auto geoView = ui->actionViewGeometry->isChecked(); ui->stackedWidget->setCurrentWidget(geoView ? ui->geometryPage : ui->bufferPage); ui->actionResetCam->setVisible(geoView); ui->actionShowNormals->setVisible(geoView); ui->actionShowTangents->setVisible(geoView); ui->actionCullBack->setVisible(geoView); shadingModeLabel->setVisible(geoView); shadingModeAction->setVisible(geoView); }); ui->bufferView->setModel(m_bufferModel); ui->bufferView->horizontalHeader()->setObjectName(QStringLiteral("bufferViewHeader")); connect(ui->bufferBox, QOverload::of(&QComboBox::currentIndexChanged), m_bufferModel, &BufferModel::setBufferIndex); m_surface = new QWindow; m_surface->setFlags(Qt::Window | Qt::FramelessWindowHint); m_surface->setSurfaceType(QSurface::OpenGLSurface); const auto format = probeFormat(); m_surface->setFormat(format); QSurfaceFormat::setDefaultFormat(format); m_surface->create(); auto container = QWidget::createWindowContainer(m_surface, this); container->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); ui->geometryPage->layout()->addWidget(container); m_surface->installEventFilter(this); m_interface = ObjectBroker::object( parent->objectBaseName() + ".qt3dGeometry"); connect(m_interface, &Qt3DGeometryExtensionInterface::geometryDataChanged, this, &Qt3DGeometryTab::updateGeometry); } Qt3DGeometryTab::~Qt3DGeometryTab() = default; bool Qt3DGeometryTab::eventFilter(QObject *receiver, QEvent *event) { if (receiver != m_surface || event->type() != QEvent::Expose || m_aspectEngine) return QWidget::eventFilter(receiver, event); m_aspectEngine = new Qt3DCore::QAspectEngine(this); m_aspectEngine->registerAspect(new Qt3DRender::QRenderAspect); auto rootEntity = new Qt3DCore::QEntity; m_camera = new Qt3DRender::QCamera; resetCamera(); // rendering auto forwardRenderer = new Qt3DExtras::QForwardRenderer; forwardRenderer->setClearColor(Qt::black); forwardRenderer->setCamera(m_camera); forwardRenderer->setSurface(m_surface); auto renderSettings = new Qt3DRender::QRenderSettings; renderSettings->setActiveFrameGraph(forwardRenderer); #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) renderSettings->pickingSettings()->setFaceOrientationPickingMode(Qt3DRender::QPickingSettings::FrontFace); renderSettings->pickingSettings()->setPickMethod(Qt3DRender::QPickingSettings::TrianglePicking); renderSettings->pickingSettings()->setPickResultMode(Qt3DRender::QPickingSettings::NearestPick); #endif rootEntity->addComponent(renderSettings); auto skyboxEntity = new Qt3DCore::QEntity(rootEntity); auto skyBoxGeometry = new Qt3DExtras::QCuboidMesh; skyBoxGeometry->setXYMeshResolution(QSize(2, 2)); skyBoxGeometry->setXZMeshResolution(QSize(2, 2)); skyBoxGeometry->setYZMeshResolution(QSize(2, 2)); auto skyboxTransform = new Qt3DCore::QTransform; skyboxTransform->setTranslation(m_camera->position()); connect(m_camera, &Qt3DRender::QCamera::positionChanged, skyboxTransform, &Qt3DCore::QTransform::setTranslation); skyboxEntity->addComponent(skyBoxGeometry); skyboxEntity->addComponent(createSkyboxMaterial(rootEntity)); skyboxEntity->addComponent(skyboxTransform); auto geometryEntity = new Qt3DCore::QEntity(rootEntity); m_geometryRenderer = new Qt3DRender::QGeometryRenderer; geometryEntity->addComponent(m_geometryRenderer); geometryEntity->addComponent(createMaterial(rootEntity)); m_geometryTransform = new Qt3DCore::QTransform; geometryEntity->addComponent(m_geometryTransform); auto picker = new Qt3DRender::QObjectPicker; connect(picker, &Qt3DRender::QObjectPicker::clicked, this, &Qt3DGeometryTab::trianglePicked); geometryEntity->addComponent(picker); // fallback wireframe rendering with ES2 if (m_usingES2Fallback) { auto es2lineEntity = new Qt3DCore::QEntity(rootEntity); m_es2lineRenderer = new Qt3DRender::QGeometryRenderer; es2lineEntity->addComponent(m_es2lineRenderer); es2lineEntity->addComponent(createES2WireframeMaterial(rootEntity)); es2lineEntity->addComponent(m_geometryTransform); auto label = new QLabel(tr("Using OpenGL ES2 fallback, wireframe rendering will be inaccurate.")); label->setAlignment(Qt::AlignRight); label->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum); ui->geometryPage->layout()->addWidget(label); ui->actionShowNormals->setToolTip(tr("Visualizing normals not available when running in OpenGL ES2 fallback mode.")); } updateGeometry(); auto lightEntity = new Qt3DCore::QEntity(rootEntity); auto light = new Qt3DRender::QPointLight(lightEntity); lightEntity->addComponent(light); auto lightTransform = new Qt3DCore::QTransform(lightEntity); lightTransform->setTranslation(m_camera->position()); connect(m_camera, &Qt3DRender::QCamera::positionChanged, lightTransform, &Qt3DCore::QTransform::setTranslation); lightEntity->addComponent(lightTransform); // input handling m_aspectEngine->registerAspect(new Qt3DLogic::QLogicAspect); m_aspectEngine->registerAspect(new Qt3DInput::QInputAspect); auto inputSettings = new Qt3DInput::QInputSettings; inputSettings->setEventSource(m_surface); rootEntity->addComponent(inputSettings); auto camController = new CameraController(rootEntity); camController->setCamera(m_camera); m_aspectEngine->setRootEntity(Qt3DCore::QEntityPtr(rootEntity)); return QWidget::eventFilter(receiver, event); } Qt3DCore::QComponent *Qt3DGeometryTab::createMaterial(Qt3DCore::QNode *parent) { auto material = new Qt3DRender::QMaterial(parent); // wireframe render pass m_cullMode = new Qt3DRender::QCullFace; m_cullMode->setMode(ui->actionCullBack->isChecked() ? Qt3DRender::QCullFace::Back : Qt3DRender::QCullFace::NoCulling); m_shadingMode = new Qt3DRender::QParameter(QStringLiteral("shadingMode"), m_shadingModeCombo->currentData(), material); material->addParameter(m_shadingMode); auto gl3WireframeShader = new Qt3DRender::QShaderProgram; gl3WireframeShader->setVertexShaderCode(Qt3DRender::QShaderProgram::loadSource(QUrl(QStringLiteral("qrc:/gammaray/qt3dinspector/geometryextension/gl3/wireframe.vert")))); gl3WireframeShader->setGeometryShaderCode(Qt3DRender::QShaderProgram::loadSource(QUrl(QStringLiteral("qrc:/gammaray/qt3dinspector/geometryextension/gl3/wireframe.geom")))); gl3WireframeShader->setFragmentShaderCode(Qt3DRender::QShaderProgram::loadSource(QUrl(QStringLiteral("qrc:/gammaray/qt3dinspector/geometryextension/gl3/wireframe.frag")))); auto es2WireframeShader = new Qt3DRender::QShaderProgram; es2WireframeShader->setVertexShaderCode(Qt3DRender::QShaderProgram::loadSource(QUrl(QStringLiteral("qrc:/gammaray/qt3dinspector/geometryextension/es2/surface.vert")))); es2WireframeShader->setFragmentShaderCode(Qt3DRender::QShaderProgram::loadSource(QUrl(QStringLiteral("qrc:/gammaray/qt3dinspector/geometryextension/es2/surface.frag")))); auto blendEquationArgs = new Qt3DRender::QBlendEquationArguments; blendEquationArgs->setSourceRgb(Qt3DRender::QBlendEquationArguments::SourceAlpha); blendEquationArgs->setDestinationRgb(Qt3DRender::QBlendEquationArguments::OneMinusSourceAlpha); auto blendEquation = new Qt3DRender::QBlendEquation; blendEquation->setBlendFunction(Qt3DRender::QBlendEquation::Add); m_depthTest = new Qt3DRender::QDepthTest; m_depthTest->setDepthFunction(Qt3DRender::QDepthTest::Less); auto gl3WireframeRenderPass = new Qt3DRender::QRenderPass; gl3WireframeRenderPass->setShaderProgram(gl3WireframeShader); gl3WireframeRenderPass->addRenderState(m_cullMode); gl3WireframeRenderPass->addRenderState(blendEquationArgs); gl3WireframeRenderPass->addRenderState(blendEquation); gl3WireframeRenderPass->addRenderState(m_depthTest); auto es2WireframeRenderPass = new Qt3DRender::QRenderPass; es2WireframeRenderPass->setShaderProgram(es2WireframeShader); es2WireframeRenderPass->addRenderState(m_cullMode); es2WireframeRenderPass->addRenderState(blendEquationArgs); es2WireframeRenderPass->addRenderState(blendEquation); es2WireframeRenderPass->addRenderState(m_depthTest); // normal render pass m_normalLength = new Qt3DRender::QParameter(QStringLiteral("normalLength"), 0.1, material); material->addParameter(m_normalLength); auto normalsShader = new Qt3DRender::QShaderProgram; normalsShader->setVertexShaderCode(Qt3DRender::QShaderProgram::loadSource(QUrl(QStringLiteral( "qrc:/gammaray/qt3dinspector/geometryextension/gl3/passthrough.vert")))); normalsShader->setGeometryShaderCode(Qt3DRender::QShaderProgram::loadSource(QUrl(QStringLiteral( "qrc:/gammaray/qt3dinspector/geometryextension/gl3/normals.geom")))); normalsShader->setFragmentShaderCode(Qt3DRender::QShaderProgram::loadSource(QUrl(QStringLiteral( "qrc:/gammaray/qt3dinspector/geometryextension/gl3/normals.frag")))); m_normalsRenderPass = new Qt3DRender::QRenderPass; m_normalsRenderPass->setShaderProgram(normalsShader); m_normalsRenderPass->setEnabled(ui->actionShowNormals->isChecked()); auto filterKey = new Qt3DRender::QFilterKey(material); filterKey->setName(QStringLiteral("renderingStyle")); filterKey->setValue(QStringLiteral("forward")); auto gl3Technique = new Qt3DRender::QTechnique; gl3Technique->graphicsApiFilter()->setApi(Qt3DRender::QGraphicsApiFilter::OpenGL); gl3Technique->graphicsApiFilter()->setMajorVersion(3); gl3Technique->graphicsApiFilter()->setMinorVersion(3); gl3Technique->graphicsApiFilter()->setProfile(Qt3DRender::QGraphicsApiFilter::CoreProfile); gl3Technique->addRenderPass(gl3WireframeRenderPass); gl3Technique->addRenderPass(m_normalsRenderPass); gl3Technique->addFilterKey(filterKey); auto es2Technique = new Qt3DRender::QTechnique; es2Technique->graphicsApiFilter()->setApi(Qt3DRender::QGraphicsApiFilter::OpenGLES); es2Technique->graphicsApiFilter()->setMajorVersion(2); es2Technique->graphicsApiFilter()->setMinorVersion(0); es2Technique->graphicsApiFilter()->setProfile(Qt3DRender::QGraphicsApiFilter::NoProfile); es2Technique->addRenderPass(es2WireframeRenderPass); es2Technique->addFilterKey(filterKey); auto effect = new Qt3DRender::QEffect; effect->addTechnique(gl3Technique); effect->addTechnique(es2Technique); material->setEffect(effect); return material; } Qt3DCore::QComponent* Qt3DGeometryTab::createES2WireframeMaterial(Qt3DCore::QNode *parent) { auto material = new Qt3DRender::QMaterial(parent); auto shader = new Qt3DRender::QShaderProgram; shader->setVertexShaderCode(Qt3DRender::QShaderProgram::loadSource(QUrl(QStringLiteral("qrc:/gammaray/qt3dinspector/geometryextension/es2/wireframe.vert")))); shader->setFragmentShaderCode(Qt3DRender::QShaderProgram::loadSource(QUrl(QStringLiteral("qrc:/gammaray/qt3dinspector/geometryextension/es2/wireframe.frag")))); auto renderPass = new Qt3DRender::QRenderPass; renderPass->setShaderProgram(shader); renderPass->addRenderState(m_cullMode); renderPass->addRenderState(m_depthTest); auto filterKey = new Qt3DRender::QFilterKey(material); filterKey->setName(QStringLiteral("renderingStyle")); filterKey->setValue(QStringLiteral("forward")); auto technique = new Qt3DRender::QTechnique; technique->graphicsApiFilter()->setApi(Qt3DRender::QGraphicsApiFilter::OpenGLES); technique->graphicsApiFilter()->setMajorVersion(2); technique->graphicsApiFilter()->setMinorVersion(0); technique->graphicsApiFilter()->setProfile(Qt3DRender::QGraphicsApiFilter::NoProfile); technique->addRenderPass(renderPass); technique->addFilterKey(filterKey); auto effect = new Qt3DRender::QEffect; effect->addTechnique(technique); material->setEffect(effect); return material; } Qt3DCore::QComponent *Qt3DGeometryTab::createSkyboxMaterial(Qt3DCore::QNode *parent) { auto material = new Qt3DRender::QMaterial(parent); auto cullFront = new Qt3DRender::QCullFace; cullFront->setMode(Qt3DRender::QCullFace::Front); auto depthTest = new Qt3DRender::QDepthTest; depthTest->setDepthFunction(Qt3DRender::QDepthTest::LessOrEqual); auto gl3Shader = new Qt3DRender::QShaderProgram; gl3Shader->setVertexShaderCode(Qt3DRender::QShaderProgram::loadSource(QUrl(QStringLiteral("qrc:/gammaray/qt3dinspector/geometryextension/gl3/skybox.vert")))); gl3Shader->setFragmentShaderCode(Qt3DRender::QShaderProgram::loadSource(QUrl(QStringLiteral("qrc:/gammaray/qt3dinspector/geometryextension/gl3/skybox.frag")))); auto es2Shader = new Qt3DRender::QShaderProgram; es2Shader->setVertexShaderCode(Qt3DRender::QShaderProgram::loadSource(QUrl(QStringLiteral("qrc:/gammaray/qt3dinspector/geometryextension/es2/skybox.vert")))); es2Shader->setFragmentShaderCode(Qt3DRender::QShaderProgram::loadSource(QUrl(QStringLiteral("qrc:/gammaray/qt3dinspector/geometryextension/es2/skybox.frag")))); auto gl3RenderPass = new Qt3DRender::QRenderPass; gl3RenderPass->setShaderProgram(gl3Shader); gl3RenderPass->addRenderState(cullFront); gl3RenderPass->addRenderState(depthTest); auto es2RenderPass = new Qt3DRender::QRenderPass; es2RenderPass->setShaderProgram(es2Shader); es2RenderPass->addRenderState(cullFront); es2RenderPass->addRenderState(depthTest); auto filterKey = new Qt3DRender::QFilterKey(material); filterKey->setName(QStringLiteral("renderingStyle")); filterKey->setValue(QStringLiteral("forward")); auto gl3Technique = new Qt3DRender::QTechnique; gl3Technique->graphicsApiFilter()->setApi(Qt3DRender::QGraphicsApiFilter::OpenGL); gl3Technique->graphicsApiFilter()->setMajorVersion(3); gl3Technique->graphicsApiFilter()->setMinorVersion(3); gl3Technique->graphicsApiFilter()->setProfile(Qt3DRender::QGraphicsApiFilter::CoreProfile); gl3Technique->addRenderPass(gl3RenderPass); gl3Technique->addFilterKey(filterKey); auto es2Technique = new Qt3DRender::QTechnique; es2Technique->graphicsApiFilter()->setApi(Qt3DRender::QGraphicsApiFilter::OpenGLES); es2Technique->graphicsApiFilter()->setMajorVersion(2); es2Technique->graphicsApiFilter()->setMinorVersion(0); es2Technique->graphicsApiFilter()->setProfile(Qt3DRender::QGraphicsApiFilter::NoProfile); es2Technique->addRenderPass(es2RenderPass); es2Technique->addFilterKey(filterKey); auto effect = new Qt3DRender::QEffect; effect->addTechnique(gl3Technique); effect->addTechnique(es2Technique); material->setEffect(effect); return material; } static void setupAttribute(Qt3DRender::QAttribute *attr, const Qt3DGeometryAttributeData &attrData) { attr->setByteOffset(attrData.byteOffset); attr->setByteStride(attrData.byteStride); attr->setCount(attrData.count); attr->setDivisor(attrData.divisor); attr->setDataType(attrData.vertexBaseType); attr->setDataSize(attrData.vertexSize); } void Qt3DGeometryTab::updateGeometry() { ui->actionShowNormals->setEnabled(false); ui->actionShowTangents->setEnabled(false); ui->bufferBox->clear(); const auto prevShadingMode = m_shadingModeCombo->currentData(); m_shadingModeCombo->clear(); m_shadingModeCombo->addItem(tr("Flat"), ShadingModeFlat); m_shadingModeCombo->addItem(tr("Wireframe"), ShadingModeWireframe); if (!m_geometryRenderer) return; const auto geo = m_interface->geometryData(); m_bufferModel->setGeometryData(geo); auto geometry = new Qt3DRender::QGeometry(); QVector buffers; buffers.reserve(geo.buffers.size()); for (const auto &bufferData : geo.buffers) { auto buffer = new Qt3DRender::QBuffer(bufferData.type, geometry); buffer->setData(bufferData.data); buffers.push_back(buffer); ui->bufferBox->addItem(bufferData.name, QVariant::fromValue(buffer)); } for (const auto &attrData : geo.attributes) { if (attrData.name == Qt3DRender::QAttribute::defaultPositionAttributeName()) { auto posAttr = new Qt3DRender::QAttribute(); posAttr->setAttributeType(Qt3DRender::QAttribute::VertexAttribute); posAttr->setBuffer(buffers.at(attrData.bufferIndex)); setupAttribute(posAttr, attrData); posAttr->setName(Qt3DRender::QAttribute::defaultPositionAttributeName()); geometry->addAttribute(posAttr); geometry->setBoundingVolumePositionAttribute(posAttr); computeBoundingVolume(attrData, posAttr->buffer()->data()); m_geometryTransform->setTranslation(-m_boundingVolume.center()); m_normalLength->setValue(0.025 * m_boundingVolume.radius()); } else if (attrData.name == Qt3DRender::QAttribute::defaultNormalAttributeName()) { auto normalAttr = new Qt3DRender::QAttribute(); normalAttr->setAttributeType(Qt3DRender::QAttribute::VertexAttribute); normalAttr->setBuffer(buffers.at(attrData.bufferIndex)); setupAttribute(normalAttr, attrData); normalAttr->setName(Qt3DRender::QAttribute::defaultNormalAttributeName()); geometry->addAttribute(normalAttr); ui->actionShowNormals->setEnabled(!m_usingES2Fallback); m_shadingModeCombo->addItem(tr("Phong"), ShadingModePhong); m_shadingModeCombo->addItem(tr("Normal"), ShadingModeNormal); } else if (attrData.attributeType == Qt3DRender::QAttribute::IndexAttribute) { auto indexAttr = new Qt3DRender::QAttribute(); indexAttr->setAttributeType(Qt3DRender::QAttribute::IndexAttribute); indexAttr->setBuffer(buffers.at(attrData.bufferIndex)); setupAttribute(indexAttr, attrData); geometry->addAttribute(indexAttr); } else if (attrData.name == Qt3DRender::QAttribute::defaultTextureCoordinateAttributeName()) { auto texCoordAttr = new Qt3DRender::QAttribute(); texCoordAttr->setAttributeType(Qt3DRender::QAttribute::VertexAttribute); texCoordAttr->setBuffer(buffers.at(attrData.bufferIndex)); setupAttribute(texCoordAttr, attrData); texCoordAttr->setName(Qt3DRender::QAttribute::defaultTextureCoordinateAttributeName()); geometry->addAttribute(texCoordAttr); m_shadingModeCombo->addItem(tr("Texture Coordinate"), ShadingModeTexture); } else if (attrData.name == Qt3DRender::QAttribute::defaultTangentAttributeName()) { auto tangentAttr = new Qt3DRender::QAttribute(); tangentAttr->setAttributeType(Qt3DRender::QAttribute::VertexAttribute); tangentAttr->setBuffer(buffers.at(attrData.bufferIndex)); setupAttribute(tangentAttr, attrData); tangentAttr->setName(Qt3DRender::QAttribute::defaultTangentAttributeName()); geometry->addAttribute(tangentAttr); m_shadingModeCombo->addItem(tr("Tangent"), ShadingModeTangent); } else if (attrData.name == Qt3DRender::QAttribute::defaultColorAttributeName()) { auto colorAttr = new Qt3DRender::QAttribute(); colorAttr->setAttributeType(Qt3DRender::QAttribute::VertexAttribute); colorAttr->setBuffer(buffers.at(attrData.bufferIndex)); setupAttribute(colorAttr, attrData); colorAttr->setName(Qt3DRender::QAttribute::defaultColorAttributeName()); geometry->addAttribute(colorAttr); m_shadingModeCombo->addItem(tr("Color"), ShadingModeColor); } } m_geometryRenderer->setInstanceCount(1); m_geometryRenderer->setIndexOffset(0); m_geometryRenderer->setFirstInstance(0); m_geometryRenderer->setPrimitiveType(Qt3DRender::QGeometryRenderer::Triangles); if (m_es2lineRenderer) { m_es2lineRenderer->setInstanceCount(1); m_es2lineRenderer->setIndexOffset(0); m_es2lineRenderer->setFirstInstance(0); m_es2lineRenderer->setPrimitiveType(Qt3DRender::QGeometryRenderer::LineLoop); m_es2lineRenderer->setGeometry(geometry); } auto oldGeometry = m_geometryRenderer->geometry(); m_geometryRenderer->setGeometry(geometry); delete oldGeometry; const auto prevShadingModeIdx = m_shadingModeCombo->findData(prevShadingMode); if (prevShadingModeIdx >= 0) m_shadingModeCombo->setCurrentIndex(prevShadingModeIdx); resetCamera(); } void Qt3DGeometryTab::resizeEvent(QResizeEvent *event) { QWidget::resizeEvent(event); if (m_surface && m_camera) m_camera->lens()->setAspectRatio(float(m_surface->width()) / float(m_surface->height())); } void Qt3DGeometryTab::resetCamera() { m_camera->lens()->setPerspectiveProjection(45.0f, float(m_surface->width()) / float(m_surface->height()), 0.1f, 1000.0f); m_camera->setViewCenter(QVector3D(0.0f, 0.0f, 0.0f)); m_camera->setUpVector(QVector3D(0.0f, 1.0f, 0.0f)); m_camera->setPosition(QVector3D(0, 0, m_boundingVolume.radius() * 2.5f)); } void Qt3DGeometryTab::computeBoundingVolume(const Qt3DGeometryAttributeData &vertexAttr, const QByteArray &bufferData) { m_boundingVolume = BoundingVolume(); QVector3D v; const auto vertexSize = std::max(vertexAttr.vertexSize, 1u); const auto stride = std::max(vertexAttr.byteStride, (uint)Attribute::size(vertexAttr.vertexBaseType) * vertexSize); for (unsigned int i = 0; i < vertexAttr.count; ++i) { const char *c = bufferData.constData() + vertexAttr.byteOffset + i * stride; switch (vertexAttr.vertexBaseType) { case Qt3DRender::QAttribute::Float: { // cppcheck-suppress invalidPointerCast auto f = reinterpret_cast(c); v.setX(*f); ++f; v.setY(*f); ++f; v.setZ(*f); break; } default: qWarning() << "Vertex type" << vertexAttr.vertexBaseType << "not implemented yet"; return; } m_boundingVolume.addPoint(v); } } bool Qt3DGeometryTab::isIndexBuffer(unsigned int bufferIndex) const { foreach (const auto &attr, m_interface->geometryData().attributes) { if (attr.bufferIndex == bufferIndex) return attr.attributeType == Qt3DRender::QAttribute::IndexAttribute; } return false; } void Qt3DGeometryTab::trianglePicked(Qt3DRender::QPickEvent* pick) { #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) if (pick->button() != Qt3DRender::QPickEvent::LeftButton) return; const auto trianglePick = qobject_cast(pick); qDebug() << trianglePick << trianglePick->vertex1Index() << trianglePick->vertex2Index() << trianglePick->vertex3Index() << trianglePick->localIntersection() << trianglePick->triangleIndex() << m_interface->geometryData().buffers.at(ui->bufferBox->currentIndex()).type << ui->bufferBox->currentIndex(); auto selModel = ui->bufferView->selectionModel(); selModel->clear(); if (isIndexBuffer(ui->bufferBox->currentIndex())) { selModel->select(selModel->model()->index(trianglePick->triangleIndex() * 3, 0), QItemSelectionModel::Rows | QItemSelectionModel::Select); selModel->select(selModel->model()->index(trianglePick->triangleIndex() * 3 + 1, 0), QItemSelectionModel::Rows | QItemSelectionModel::Select); selModel->select(selModel->model()->index(trianglePick->triangleIndex() * 3 + 2, 0), QItemSelectionModel::Rows | QItemSelectionModel::Select); } else { // TODO we could pick one here based on pick->localIntersection() and smallest distance to one of the three candidates? selModel->select(selModel->model()->index(trianglePick->vertex1Index(), 0), QItemSelectionModel::Rows | QItemSelectionModel::Select); selModel->select(selModel->model()->index(trianglePick->vertex2Index(), 0), QItemSelectionModel::Rows | QItemSelectionModel::Select); selModel->select(selModel->model()->index(trianglePick->vertex3Index(), 0), QItemSelectionModel::Rows | QItemSelectionModel::Select); } foreach (const auto &row, selModel->selectedRows()) ui->bufferView->scrollTo(row, QAbstractItemView::EnsureVisible); #else Q_UNUSED(pick); #endif } QSurfaceFormat Qt3DGeometryTab::probeFormat() const { QSurfaceFormat format; format.setDepthBufferSize(24); format.setSamples(4); // ??? format.setStencilBufferSize(8); // ??? // try GL3 first format.setRenderableType(QSurfaceFormat::OpenGL); format.setMajorVersion(3); format.setMinorVersion(0); format.setProfile(QSurfaceFormat::CoreProfile); QOpenGLContext context; context.setScreen(window()->windowHandle()->screen()); context.setFormat(format); if (context.create()) { qDebug() << "Tried GL3, got:" << context.format() << context.format().renderableType(); m_usingES2Fallback = context.format().renderableType() == QSurfaceFormat::OpenGLES; return format; } // fall back to ES2 m_usingES2Fallback = true; format.setRenderableType(QSurfaceFormat::OpenGLES); format.setMajorVersion(2); format.setMinorVersion(0); format.setProfile(QSurfaceFormat::NoProfile); return format; } gammaray-2.11.3/plugins/qt3dinspector/geometryextension/qt3dgeometrytab.h000066400000000000000000000065331413310443000267360ustar00rootroot00000000000000/* qt3dgeometrytab.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_QT3DGEOMETRYTAB_H #define GAMMARAY_QT3DGEOMETRYTAB_H #include "boundingvolume.h" #include #include QT_BEGIN_NAMESPACE namespace Qt3DCore { class QAspectEngine; class QComponent; class QNode; class QTransform; } namespace Qt3DRender { class QCamera; class QCullFace; class QDepthTest; class QGeometryRenderer; class QParameter; class QPickEvent; class QRenderPass; } class QComboBox; class QSurfaceFormat; QT_END_NAMESPACE namespace GammaRay { class BufferModel; class PropertyWidget; class Qt3DGeometryExtensionInterface; struct Qt3DGeometryAttributeData; namespace Ui { class Qt3DGeometryTab; } class Qt3DGeometryTab : public QWidget { Q_OBJECT public: explicit Qt3DGeometryTab(PropertyWidget *parent); ~Qt3DGeometryTab(); protected: void resizeEvent(QResizeEvent *event) override; bool eventFilter(QObject *receiver, QEvent *event) override; private: Qt3DCore::QComponent *createMaterial(Qt3DCore::QNode *parent); Qt3DCore::QComponent *createES2WireframeMaterial(Qt3DCore::QNode *parent); Qt3DCore::QComponent *createSkyboxMaterial(Qt3DCore::QNode *parent); void updateGeometry(); void resetCamera(); void computeBoundingVolume(const Qt3DGeometryAttributeData &vertexAttr, const QByteArray &bufferData); void trianglePicked(Qt3DRender::QPickEvent *pick); bool isIndexBuffer(unsigned int bufferIndex) const; QSurfaceFormat probeFormat() const; std::unique_ptr ui; QComboBox *m_shadingModeCombo; Qt3DGeometryExtensionInterface *m_interface; QWindow *m_surface; Qt3DCore::QAspectEngine *m_aspectEngine; Qt3DRender::QCamera *m_camera; Qt3DRender::QGeometryRenderer *m_geometryRenderer; Qt3DRender::QGeometryRenderer *m_es2lineRenderer; // ES2 wireframe fallback renderer Qt3DCore::QTransform *m_geometryTransform; Qt3DRender::QCullFace *m_cullMode; Qt3DRender::QDepthTest *m_depthTest; Qt3DRender::QRenderPass *m_normalsRenderPass; Qt3DRender::QParameter *m_normalLength; Qt3DRender::QParameter *m_shadingMode; BoundingVolume m_boundingVolume; mutable bool m_usingES2Fallback = false; BufferModel *m_bufferModel; }; } #endif // GAMMARAY_QT3DGEOMETRYTAB_H gammaray-2.11.3/plugins/qt3dinspector/geometryextension/qt3dgeometrytab.ui000066400000000000000000000074711413310443000271260ustar00rootroot00000000000000 GammaRay::Qt3DGeometryTab 0 0 400 300 0 0 0 0 0 0 0 0 0 B&uffer: bufferBox Reset Camera Reset camera. true Show Normals Visualize normal vectors. true Show Tangents Visualize tangent vectors. true true View Geometry Visualize geometry. true View Buffers View raw buffer data. true true Cull Back Enable back face culling. gammaray-2.11.3/plugins/qt3dinspector/paintanalyzerextension/000077500000000000000000000000001413310443000244665ustar00rootroot00000000000000gammaray-2.11.3/plugins/qt3dinspector/paintanalyzerextension/qt3dpaintedtextureanalyzerextension.cpp000066400000000000000000000063321413310443000345420ustar00rootroot00000000000000/* qt3dpaintedtextureanalyzerextension.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2018-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "qt3dpaintedtextureanalyzerextension.h" #include #include #include #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) #include #endif #include using namespace GammaRay; #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) class QPaintedTextureImagePrivacyViolater : public Qt3DRender::QPaintedTextureImage { public: using Qt3DRender::QPaintedTextureImage::paint; }; #endif Qt3DPaintedTextureAnalyzerExtension::Qt3DPaintedTextureAnalyzerExtension(PropertyController* controller) : PropertyControllerExtension(controller->objectBaseName() + ".painting") , m_paintAnalyzer(nullptr) { // check if the paint analyzer already exists before creating it, // as we share the UI with the other plugins. const QString aName = controller->objectBaseName() + QStringLiteral(".painting.analyzer"); if (ObjectBroker::hasObject(aName)) { m_paintAnalyzer = qobject_cast(ObjectBroker::object(aName)); } else { m_paintAnalyzer = new PaintAnalyzer(aName, controller); } } Qt3DPaintedTextureAnalyzerExtension::~Qt3DPaintedTextureAnalyzerExtension() { } bool Qt3DPaintedTextureAnalyzerExtension::setQObject(QObject* object) { if (!PaintAnalyzer::isAvailable()) return false; #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) if (auto tex = qobject_cast(object)) { if (tex->textureImages().size() == 1) return setQObject(tex->textureImages().at(0)); } auto tex = qobject_cast(object); if (!tex || tex->size() == QSize(0, 0)) return false; m_paintAnalyzer->beginAnalyzePainting(); m_paintAnalyzer->setBoundingRect(QRect(QPoint(0, 0), tex->size())); { QPainter p(m_paintAnalyzer->paintDevice()); reinterpret_cast(tex)->paint(&p); } m_paintAnalyzer->endAnalyzePainting(); return true; #else Q_UNUSED(object); return false; #endif } gammaray-2.11.3/plugins/qt3dinspector/paintanalyzerextension/qt3dpaintedtextureanalyzerextension.h000066400000000000000000000033411413310443000342040ustar00rootroot00000000000000/* qt3dpaintedtextureanalyzerextension.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2018-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_QT3DPAINTEDTEXTUREANALYZEREXTENSION_H #define GAMMARAY_QT3DPAINTEDTEXTUREANALYZEREXTENSION_H #include namespace GammaRay { class PaintAnalyzer; class PropertyController; class Qt3DPaintedTextureAnalyzerExtension : public PropertyControllerExtension { public: explicit Qt3DPaintedTextureAnalyzerExtension(PropertyController *controller); ~Qt3DPaintedTextureAnalyzerExtension(); bool setQObject(QObject *object) override; private: PaintAnalyzer *m_paintAnalyzer; }; } #endif // GAMMARAY_QT3DPAINTEDTEXTUREANALYZEREXTENSION_H gammaray-2.11.3/plugins/qt3dinspector/qt3dentitytreemodel.cpp000066400000000000000000000222371413310443000243730ustar00rootroot00000000000000/* qt3dentitytreemodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "qt3dentitytreemodel.h" #include #include #include using namespace GammaRay; Qt3DEntityTreeModel::Qt3DEntityTreeModel(QObject *parent) : ObjectModelBase(parent) , m_engine(nullptr) { } Qt3DEntityTreeModel::~Qt3DEntityTreeModel() { } void Qt3DEntityTreeModel::setEngine(Qt3DCore::QAspectEngine *engine) { beginResetModel(); clear(); m_engine = engine; populateFromEntity(engine->rootEntity().data()); endResetModel(); } void Qt3DEntityTreeModel::clear() { for (auto it = m_childParentMap.constBegin(); it != m_childParentMap.constEnd(); ++it) disconnectEntity(it.key()); m_childParentMap.clear(); m_parentChildMap.clear(); } void Qt3DEntityTreeModel::populateFromNode(Qt3DCore::QNode *node) { // the entity tree can have intermediate nodes, so we need to do this // recursively without a depth limit auto entity = qobject_cast(node); if (entity) { populateFromEntity(entity); } else { foreach (auto child, node->childNodes()) populateFromNode(child); } } void Qt3DEntityTreeModel::populateFromEntity(Qt3DCore::QEntity *entity) { if (!entity) return; m_childParentMap[entity] = entity->parentEntity(); m_parentChildMap[entity->parentEntity()].push_back(entity); connectEntity(entity); foreach (auto child, entity->childNodes()) { populateFromNode(child); } auto &children = m_parentChildMap[entity->parentEntity()]; std::sort(children.begin(), children.end()); } QVariant Qt3DEntityTreeModel::data(const QModelIndex &index, int role) const { if (!index.isValid() || !m_engine) return QVariant(); auto entity = reinterpret_cast(index.internalPointer()); if (role == ObjectModel::ObjectIdRole) return QVariant::fromValue(ObjectId(entity)); else if (role == Qt::CheckStateRole && index.column() == 0) return entity->isEnabled() ? Qt::Checked : Qt::Unchecked; return dataForObject(entity, index, role); } int Qt3DEntityTreeModel::rowCount(const QModelIndex &parent) const { if (!m_engine || !m_engine->rootEntity()) return 0; auto parentEntity = reinterpret_cast(parent.internalPointer()); return m_parentChildMap.value(parentEntity).size(); } QModelIndex Qt3DEntityTreeModel::parent(const QModelIndex &child) const { auto childEntity = reinterpret_cast(child.internalPointer()); return indexForEntity(m_childParentMap.value(childEntity)); } QModelIndex Qt3DEntityTreeModel::index(int row, int column, const QModelIndex &parent) const { auto parentEntity = reinterpret_cast(parent.internalPointer()); const auto children = m_parentChildMap.value(parentEntity); if (row < 0 || column < 0 || row >= children.size() || column >= columnCount()) return QModelIndex(); return createIndex(row, column, children.at(row)); } Qt::ItemFlags Qt3DEntityTreeModel::flags(const QModelIndex &index) const { auto baseFlags = QAbstractItemModel::flags(index); if (index.isValid() && index.column() == 0) return baseFlags | Qt::ItemIsUserCheckable; return baseFlags; } bool Qt3DEntityTreeModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (!m_engine || !index.isValid() || role != Qt::CheckStateRole || index.column() != 0) return false; auto entity = reinterpret_cast(index.internalPointer()); entity->setEnabled(value.toInt() == Qt::Checked); emit dataChanged(index, index); return true; } QModelIndex Qt3DEntityTreeModel::indexForEntity(Qt3DCore::QEntity *entity) const { if (!entity) return QModelIndex(); auto parent = m_childParentMap.value(entity); const auto parentIndex = indexForEntity(parent); if (!parentIndex.isValid() && parent) return QModelIndex(); const auto &siblings = m_parentChildMap[parent]; auto it = std::lower_bound(siblings.constBegin(), siblings.constEnd(), entity); if (it == siblings.constEnd() || *it != entity) return QModelIndex(); const int row = std::distance(siblings.constBegin(), it); return index(row, 0, parentIndex); } static bool isEngineForEntity(Qt3DCore::QAspectEngine *engine, Qt3DCore::QEntity *entity) { Q_ASSERT(engine); Q_ASSERT(entity); if (entity == engine->rootEntity()) return true; if (!entity->parentEntity()) return false; return isEngineForEntity(engine, entity->parentEntity()); } void Qt3DEntityTreeModel::objectCreated(QObject *obj) { if (!m_engine) return; auto entity = qobject_cast(obj); if (!entity) return; if (!isEngineForEntity(m_engine, entity)) return; if (m_childParentMap.contains(entity)) return; auto parentEntity = entity->parentEntity(); if (parentEntity) { // add parent first, if we don't know that yet if (!m_childParentMap.contains(parentEntity)) { objectCreated(parentEntity); return; } } const auto index = indexForEntity(parentEntity); Q_ASSERT(index.isValid() || !parentEntity); auto &children = m_parentChildMap[parentEntity]; auto it = std::lower_bound(children.begin(), children.end(), entity); const int row = std::distance(children.begin(), it); beginInsertRows(index, row, row); children.insert(it, entity); m_childParentMap.insert(entity, parentEntity); connectEntity(entity); foreach (auto child, entity->childNodes()) { populateFromNode(child); } endInsertRows(); } void Qt3DEntityTreeModel::objectDestroyed(QObject *obj) { auto entity = static_cast(obj); // never dereference this! if (!m_childParentMap.contains(entity)) { Q_ASSERT(!m_parentChildMap.contains(entity)); return; } removeEntity(entity, true); } void Qt3DEntityTreeModel::removeEntity(Qt3DCore::QEntity *entity, bool danglingPointer) { auto parentEntity = m_childParentMap.value(entity); const QModelIndex parentIndex = indexForEntity(parentEntity); if (parentEntity && !parentIndex.isValid()) return; auto &siblings = m_parentChildMap[parentEntity]; auto it = std::lower_bound(siblings.begin(), siblings.end(), entity); if (it == siblings.end() || *it != entity) return; const int row = std::distance(siblings.begin(), it); beginRemoveRows(parentIndex, row, row); siblings.erase(it); removeSubtree(entity, danglingPointer); endRemoveRows(); } void Qt3DEntityTreeModel::removeSubtree(Qt3DCore::QEntity *entity, bool danglingPointer) { if (!danglingPointer) disconnectEntity(entity); const auto children = m_parentChildMap.value(entity); for (auto child : children) removeSubtree(child, danglingPointer); m_childParentMap.remove(entity); m_parentChildMap.remove(entity); } void Qt3DEntityTreeModel::objectReparented(QObject *obj) { auto entity = qobject_cast(obj); if (!entity) return; if (m_childParentMap.contains(entity)) { // moved out of our tree if (!isEngineForEntity(m_engine, entity)) { removeEntity(entity, false); } else { // TODO reparented within our tree } } else { // possibly reparented into our tree objectCreated(obj); } } void Qt3DEntityTreeModel::connectEntity(Qt3DCore::QEntity *entity) { connect(entity, &Qt3DCore::QEntity::enabledChanged, this, &Qt3DEntityTreeModel::entityEnabledChanged); } void Qt3DEntityTreeModel::disconnectEntity(Qt3DCore::QEntity *entity) { disconnect(entity, &Qt3DCore::QEntity::enabledChanged, this, &Qt3DEntityTreeModel::entityEnabledChanged); } void Qt3DEntityTreeModel::entityEnabledChanged() { auto entity = qobject_cast(sender()); if (!entity) return; const auto idx = indexForEntity(entity); if (!idx.isValid()) return; emit dataChanged(idx, idx); } gammaray-2.11.3/plugins/qt3dinspector/qt3dentitytreemodel.h000066400000000000000000000057261413310443000240440ustar00rootroot00000000000000/* qt3dentitytreemodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_QT3DENTITYTREEMODEL_H #define GAMMARAY_QT3DENTITYTREEMODEL_H #include #include #include QT_BEGIN_NAMESPACE namespace Qt3DCore { class QAspectEngine; class QEntity; class QNode; } QT_END_NAMESPACE namespace GammaRay { /** Model for the entity tree of an QAspectEngine. */ class Qt3DEntityTreeModel : public ObjectModelBase { Q_OBJECT public: explicit Qt3DEntityTreeModel(QObject *parent = nullptr); ~Qt3DEntityTreeModel(); void setEngine(Qt3DCore::QAspectEngine *engine); QVariant data(const QModelIndex &index, int role) const override; int rowCount(const QModelIndex &parent) const override; QModelIndex parent(const QModelIndex &child) const override; QModelIndex index(int row, int column, const QModelIndex &parent) const override; Qt::ItemFlags flags(const QModelIndex &index) const override; bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; public slots: void objectCreated(QObject *obj); void objectDestroyed(QObject *obj); void objectReparented(QObject *obj); private: void clear(); void populateFromNode(Qt3DCore::QNode *node); void populateFromEntity(Qt3DCore::QEntity *entity); void removeEntity(Qt3DCore::QEntity *entity, bool danglingPointer); void removeSubtree(Qt3DCore::QEntity *entity, bool danglingPointer); QModelIndex indexForEntity(Qt3DCore::QEntity *entity) const; void connectEntity(Qt3DCore::QEntity *entity); void disconnectEntity(Qt3DCore::QEntity *entity); void entityEnabledChanged(); private: Qt3DCore::QAspectEngine *m_engine; QHash m_childParentMap; QHash > m_parentChildMap; }; } #endif // GAMMARAY_QT3DENTITYTREEMODEL_H gammaray-2.11.3/plugins/qt3dinspector/qt3dinspector.qrc000066400000000000000000000014551413310443000231660ustar00rootroot00000000000000 geometryextension/gl3/passthrough.vert geometryextension/gl3/wireframe.vert geometryextension/gl3/wireframe.geom geometryextension/gl3/wireframe.frag geometryextension/gl3/normals.geom geometryextension/gl3/normals.frag geometryextension/gl3/skybox.vert geometryextension/gl3/skybox.frag geometryextension/es2/skybox.vert geometryextension/es2/skybox.frag geometryextension/es2/surface.vert geometryextension/es2/surface.frag geometryextension/es2/wireframe.vert geometryextension/es2/wireframe.frag gammaray-2.11.3/plugins/qt3dinspector/qt3dinspectorclient.cpp000066400000000000000000000030741413310443000243610ustar00rootroot00000000000000/* qt3dinspectorclient.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "qt3dinspectorclient.h" #include using namespace GammaRay; Qt3DInspectorClient::Qt3DInspectorClient(QObject *parent) : Qt3DInspectorInterface(parent) { } Qt3DInspectorClient::~Qt3DInspectorClient() { } void Qt3DInspectorClient::selectEngine(int index) { Endpoint::instance()->invokeObject(objectName(), "selectEngine", QVariantList() << QVariant::fromValue(index)); } gammaray-2.11.3/plugins/qt3dinspector/qt3dinspectorclient.h000066400000000000000000000031101413310443000240150ustar00rootroot00000000000000/* qt3dinspectorclient.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_QT3DINSPECTORCLIENT_H #define GAMMARAY_QT3DINSPECTORCLIENT_H #include "qt3dinspectorinterface.h" namespace GammaRay { class Qt3DInspectorClient : public Qt3DInspectorInterface { Q_OBJECT Q_INTERFACES(GammaRay::Qt3DInspectorInterface) public: explicit Qt3DInspectorClient(QObject *parent = nullptr); ~Qt3DInspectorClient(); public slots: void selectEngine(int index) override; }; } #endif // GAMMARAY_QT3DINSPECTORCLIENT_H gammaray-2.11.3/plugins/qt3dinspector/qt3dinspectorinterface.cpp000066400000000000000000000026631413310443000250460ustar00rootroot00000000000000/* qt3dinspectorinterface.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "qt3dinspectorinterface.h" #include using namespace GammaRay; Qt3DInspectorInterface::Qt3DInspectorInterface(QObject *parent) : QObject(parent) { ObjectBroker::registerObject(this); } Qt3DInspectorInterface::~Qt3DInspectorInterface() { } gammaray-2.11.3/plugins/qt3dinspector/qt3dinspectorinterface.h000066400000000000000000000032541413310443000245100ustar00rootroot00000000000000/* qt3dinspectorinterface.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_QT3DINSPECTORINTERFACE_H #define GAMMARAY_QT3DINSPECTORINTERFACE_H #include namespace GammaRay { class Qt3DInspectorInterface : public QObject { Q_OBJECT public: explicit Qt3DInspectorInterface(QObject *parent = nullptr); ~Qt3DInspectorInterface(); public slots: virtual void selectEngine(int index) = 0; }; } QT_BEGIN_NAMESPACE Q_DECLARE_INTERFACE(GammaRay::Qt3DInspectorInterface, "com.kdab.GammaRay.Qt3DInspectorInterface/1.0") QT_END_NAMESPACE #endif // GAMMARAY_QT3DINSPECTORINTERFACE_H gammaray-2.11.3/plugins/qt3dinspector/qt3dinspectorwidget.cpp000066400000000000000000000174511413310443000243720ustar00rootroot00000000000000/* qt3dinspectorwidget.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "qt3dinspectorwidget.h" #include "ui_qt3dinspectorwidget.h" #include "qt3dinspectorclient.h" #include "treeexpander.h" #include "geometryextension/qt3dgeometrytab.h" #include "geometryextension/qt3dgeometryextensionclient.h" #include #include #include #include #include #include #include #include using namespace GammaRay; static QObject *create3DInsepctorClient(const QString & /*name*/, QObject *parent) { return new Qt3DInspectorClient(parent); } Qt3DInspectorWidget::Qt3DInspectorWidget(QWidget *parent) : QWidget(parent) , ui(new Ui::Qt3DInspectorWidget) , m_stateManager(this) { ObjectBroker::registerClientObjectFactoryCallback( create3DInsepctorClient); m_interface = ObjectBroker::object(); ui->setupUi(this); ui->engineComboBox->setModel(ObjectBroker::model(QStringLiteral( "com.kdab.GammaRay.Qt3DInspector.engineModel"))); connect(ui->engineComboBox, SIGNAL(currentIndexChanged(int)), m_interface, SLOT(selectEngine(int))); auto sceneModel = ObjectBroker::model(QStringLiteral("com.kdab.GammaRay.Qt3DInspector.sceneModel")); auto *clientSceneModel = new ClientDecorationIdentityProxyModel(this); clientSceneModel->setSourceModel(sceneModel); ui->sceneTreeView->header()->setObjectName("sceneTreeViewHeader"); ui->sceneTreeView->setModel(clientSceneModel); auto sceneSelectionModel = ObjectBroker::selectionModel(clientSceneModel); ui->sceneTreeView->setSelectionModel(sceneSelectionModel); connect(sceneSelectionModel, &QItemSelectionModel::selectionChanged, this, &Qt3DInspectorWidget::entitySelectionChanged); new SearchLineController(ui->sceneSearchLine, clientSceneModel); connect(ui->sceneTreeView, &QWidget::customContextMenuRequested, this, &Qt3DInspectorWidget::entityContextMenu); new TreeExpander(ui->sceneTreeView); ui->scenePropertyWidget->setObjectBaseName(QStringLiteral( "com.kdab.GammaRay.Qt3DInspector.entityPropertyController")); auto frameGraphModel = ObjectBroker::model(QStringLiteral("com.kdab.GammaRay.Qt3DInspector.frameGraphModel")); auto *clientFrameGraphModel = new ClientDecorationIdentityProxyModel(this); clientFrameGraphModel->setSourceModel(frameGraphModel); ui->frameGraphView->header()->setObjectName("frameGraphViewHeader"); ui->frameGraphView->setModel(clientFrameGraphModel); auto frameGraphSelectionModel = ObjectBroker::selectionModel(clientFrameGraphModel); ui->frameGraphView->setSelectionModel(frameGraphSelectionModel); connect(frameGraphSelectionModel, &QItemSelectionModel::selectionChanged, this, &Qt3DInspectorWidget::frameGraphSelectionChanged); new SearchLineController(ui->frameGraphSearchLine, clientFrameGraphModel); connect(ui->frameGraphView, &QWidget::customContextMenuRequested, this, &Qt3DInspectorWidget::frameGraphContextMenu); new TreeExpander(ui->frameGraphView); ui->frameGraphNodePropertyWidget->setObjectBaseName(QStringLiteral( "com.kdab.GammaRay.Qt3DInspector.frameGraphPropertyController")); connect(ui->tabWidget, &QTabWidget::currentChanged, ui->stack, &QStackedWidget::setCurrentIndex); connect(ui->scenePropertyWidget, SIGNAL(tabsUpdated()), this, SLOT(propertyWidgetTabsChanged())); connect(ui->frameGraphNodePropertyWidget, SIGNAL(tabsUpdated()), this, SLOT(propertyWidgetTabsChanged())); } Qt3DInspectorWidget::~Qt3DInspectorWidget() { } void Qt3DInspectorWidget::entityContextMenu(QPoint pos) { const auto index = ui->sceneTreeView->indexAt(pos); if (!index.isValid()) return; const auto objectId = index.data(ObjectModel::ObjectIdRole).value(); QMenu menu(tr("Entity @ %1").arg(QLatin1String("0x") + QString::number(objectId.id(), 16))); ContextMenuExtension ext(objectId); ext.setLocation(ContextMenuExtension::Creation, index.data( ObjectModel::CreationLocationRole).value()); ext.setLocation(ContextMenuExtension::Declaration, index.data(ObjectModel::DeclarationLocationRole).value()); ext.populateMenu(&menu); menu.exec(ui->sceneTreeView->viewport()->mapToGlobal(pos)); } void Qt3DInspectorWidget::frameGraphContextMenu(QPoint pos) { const auto index = ui->frameGraphView->indexAt(pos); if (!index.isValid()) return; const auto objectId = index.data(ObjectModel::ObjectIdRole).value(); QMenu menu(tr("Frame Graph Node @ %1").arg(QLatin1String("0x") + QString::number( objectId.id(), 16))); ContextMenuExtension ext(objectId); ext.setLocation(ContextMenuExtension::Creation, index.data( ObjectModel::CreationLocationRole).value()); ext.setLocation(ContextMenuExtension::Declaration, index.data(ObjectModel::DeclarationLocationRole).value()); ext.populateMenu(&menu); menu.exec(ui->frameGraphView->viewport()->mapToGlobal(pos)); } void Qt3DInspectorWidget::entitySelectionChanged(const QItemSelection &selection, const QItemSelection &deselected) { if (selection.isEmpty()) return; const auto index = selection.first().topLeft(); ui->sceneTreeView->scrollTo(index); if (!deselected.isEmpty()) // external change, not initial selection ui->tabWidget->setCurrentWidget(ui->sceneTab); } void Qt3DInspectorWidget::frameGraphSelectionChanged(const QItemSelection &selection, const QItemSelection &deselected) { if (selection.isEmpty()) return; const auto index = selection.first().topLeft(); ui->frameGraphView->scrollTo(index); if (!deselected.isEmpty()) // external change, not initial selection ui->tabWidget->setCurrentWidget(ui->renderSettingsTab); } void Qt3DInspectorWidget::propertyWidgetTabsChanged() { m_stateManager.saveState(); m_stateManager.reset(); } static QObject *createGeometryExtension(const QString &name, QObject *parent) { return new Qt3DGeometryExtensionClient(name, parent); } void Qt3DInspectorUiFactory::initUi() { ObjectBroker::registerClientObjectFactoryCallback( createGeometryExtension); PropertyWidget::registerTab(QStringLiteral("qt3dGeometry"), tr("Geometry"), PropertyWidgetTabPriority::Advanced); } gammaray-2.11.3/plugins/qt3dinspector/qt3dinspectorwidget.h000066400000000000000000000046151413310443000240350ustar00rootroot00000000000000/* qt3dinspectorwidget.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_QT3DINSPECTOR_QT3DINSPECTORWIDGET_H #define GAMMARAY_QT3DINSPECTOR_QT3DINSPECTORWIDGET_H #include #include #include #include QT_BEGIN_NAMESPACE class QItemSelection; QT_END_NAMESPACE namespace GammaRay { class Qt3DInspectorInterface; namespace Ui { class Qt3DInspectorWidget; } class Qt3DInspectorWidget : public QWidget { Q_OBJECT public: explicit Qt3DInspectorWidget(QWidget *parent = nullptr); ~Qt3DInspectorWidget(); private: void entityContextMenu(QPoint pos); void frameGraphContextMenu(QPoint pos); void entitySelectionChanged(const QItemSelection &selection, const QItemSelection &deselected); void frameGraphSelectionChanged(const QItemSelection &selection, const QItemSelection &deselected); std::unique_ptr ui; UIStateManager m_stateManager; Qt3DInspectorInterface *m_interface; private slots: void propertyWidgetTabsChanged(); }; class Qt3DInspectorUiFactory : public QObject, public StandardToolUiFactory { Q_OBJECT Q_INTERFACES(GammaRay::ToolUiFactory) Q_PLUGIN_METADATA(IID "com.kdab.GammaRay.ToolUiFactory" FILE "gammaray_3dinspector.json") public: void initUi() override; }; } #endif gammaray-2.11.3/plugins/qt3dinspector/qt3dinspectorwidget.ui000066400000000000000000000116351413310443000242230ustar00rootroot00000000000000 GammaRay::Qt3DInspectorWidget 0 0 1005 721 &Engine: engineComboBox 0 0 Qt::Horizontal QTabWidget::South 0 Scene Search true Qt::CustomContextMenu true Render Settings Search true Qt::CustomContextMenu true 0 0 0 0 0 0 0 0 0 GammaRay::DeferredTreeView QTreeView
    ui/deferredtreeview.h
    GammaRay::PropertyWidget QWidget
    ui/propertywidget.h
    1
    gammaray-2.11.3/plugins/qt3dinspector/treeexpander.cpp000066400000000000000000000036121413310443000230440ustar00rootroot00000000000000/* treeexpander.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "treeexpander.h" #include #include using namespace GammaRay; TreeExpander::TreeExpander(QTreeView *view) : QObject(view), m_view(view) { Q_ASSERT(m_view); Q_ASSERT(m_view->model()); connect(m_view->model(), &QAbstractItemModel::rowsInserted, this, &TreeExpander::rowsInserted); } TreeExpander::~TreeExpander() { } void TreeExpander::rowsInserted(const QModelIndex &index, int start, int end) { if (m_view->horizontalScrollBar()->isVisible()) { deleteLater(); return; } if (index.isValid() && !m_view->isExpanded(index)) { return; } for (auto row = start; row <= end; ++row) { const auto idx = m_view->model()->index(row, 0, index); m_view->setExpanded(idx, true); } } gammaray-2.11.3/plugins/qt3dinspector/treeexpander.h000066400000000000000000000030361413310443000225110ustar00rootroot00000000000000/* treeexpander.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_TREEEXPANDER_H #define GAMMARAY_TREEEXPANDER_H #include QT_BEGIN_NAMESPACE class QTreeView; QT_END_NAMESPACE namespace GammaRay { class TreeExpander : public QObject { Q_OBJECT public: explicit TreeExpander(QTreeView *view); ~TreeExpander(); private: void rowsInserted(const QModelIndex &index, int start, int end); QTreeView *m_view; }; } #endif // GAMMARAY_TREEEXPANDER_H gammaray-2.11.3/plugins/qtivi/000077500000000000000000000000001413310443000162025ustar00rootroot00000000000000gammaray-2.11.3/plugins/qtivi/CMakeLists.txt000066400000000000000000000022711413310443000207440ustar00rootroot00000000000000# probe plugin if (NOT GAMMARAY_CLIENT_ONLY_BUILD) if(Qt5IviCore_FOUND) set(plugin_files qtivisupport.cpp) if(Qt5IviCore_VERSION VERSION_GREATER 1.1) add_definitions(-DQTIVI_DISABLE_IVIPROPERTIES) set(plugin_files ${plugin_files} qtiviobjectmodel.cpp) else() set(plugin_files ${plugin_files} qtivipropertymodel.cpp qtivipropertyoverrider.cpp) endif() gammaray_add_plugin(gammaray_qtivi JSON gammaray_qtivi.json SOURCES ${plugin_files}) target_include_directories(gammaray_qtivi SYSTEM PRIVATE ${Qt5IviCore_PRIVATE_INCLUDE_DIRS}) target_link_libraries(gammaray_qtivi gammaray_core Qt5::IviCore) if(Qt5IviVehicleFunctions_FOUND) target_link_libraries(gammaray_qtivi Qt5::IviVehicleFunctions) endif() if(Qt5IviMedia_FOUND) target_link_libraries(gammaray_qtivi Qt5::IviMedia) endif() endif() endif() # ui part if(GAMMARAY_BUILD_UI) gammaray_add_plugin(gammaray_qtivi_ui JSON gammaray_qtivi.json SOURCES qtivisupportwidget.cpp qtiviconstrainedvaluedelegate.cpp qtivipropertyclientmodel.cpp ) target_link_libraries(gammaray_qtivi_ui gammaray_ui) endif() gammaray-2.11.3/plugins/qtivi/gammaray_qtivi.json000066400000000000000000000003321413310443000221050ustar00rootroot00000000000000{ "id": "gammaray_qtivi", "name": "Qt IVI", "types": ["QIviServiceObject", "QIviAbstractFeature"], "selectableTypes": ["QIviProperty", "QIviServiceObject", "QIviAbstractFeature"], "hidden": false } gammaray-2.11.3/plugins/qtivi/qtiviconstrainedvaluedelegate.cpp000066400000000000000000000112711413310443000250260ustar00rootroot00000000000000/* qtiviconstrainedvaluedelegate.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "qtiviconstrainedvaluedelegate.h" #include "qtivipropertymodel.h" #include #include #include #include using namespace GammaRay; // The difference between this class and QComboBox is that the "user" property, from which the value to // send to the model is taken when the delegate is closed, is not currentText like in QComboBox, // but (a "forwarded copy" of) currentData(). The origin of that data is the second argument to // QComboBox::addItem(const QString &text, const QVariant &userData) and similar methods. class SplitDataComboBox : public QComboBox { Q_OBJECT Q_PROPERTY(QVariant modelData READ currentData USER true) public: explicit SplitDataComboBox(QWidget *parent = nullptr) : QComboBox(parent) {} }; QtIviConstrainedValueDelegate::QtIviConstrainedValueDelegate(QObject *parent) : PropertyEditorDelegate(parent) { } QtIviConstrainedValueDelegate::~QtIviConstrainedValueDelegate() = default; QWidget *QtIviConstrainedValueDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const { int constraintsType = -1; QVariantList constraintValues; const QVariant constraintsContainer = index.data(QtIviPropertyModel::ValueConstraintsRole); if (constraintsContainer.isValid() && constraintsContainer.canConvert()) { QVariantList constraints = constraintsContainer.value(); if (!constraints.isEmpty()) { const QVariant type = constraints.takeFirst(); if (type.isValid() && type.canConvert()) { constraintsType = type.toUInt(); constraintValues = constraints; } } } // Conceptually, what we are doing below should probably be done in setEditorData(), but then we'd // have to repeat most of the work above. The current approach should also work because constraints // don't change very often (or at all, currently). switch (constraintsType) { case QtIviPropertyModel::RangeConstraints: if (constraintValues.size() == 2) { QWidget *editor = PropertyEditorDelegate::createEditor(parent, option, index); if (QSpinBox *const intSb = qobject_cast(editor)) { intSb->setMinimum(constraintValues.at(0).toInt()); intSb->setMaximum(constraintValues.at(1).toInt()); } else if (QDoubleSpinBox *const doubleSb = qobject_cast(editor)) { doubleSb->setMinimum(constraintValues.at(0).toDouble()); doubleSb->setMaximum(constraintValues.at(1).toDouble()); } else { qWarning() << "Unhandled numeric editing delegate, cannot apply value constraints!"; } return editor; } break; case QtIviPropertyModel::AvailableValuesConstraints: { auto *combo = new SplitDataComboBox(parent); combo->setAutoFillBackground(true); for (int i = 0; i + 1 < constraintValues.size(); i += 2) { combo->addItem(constraintValues.at(i).toString(), constraintValues.at(i + 1)); if (constraintValues.at(i + 1) == index.data(Qt::EditRole)) combo->setCurrentIndex(i / 2); } return combo; } break; default: qWarning() << "Unhandled type of value constraints" << constraintsType; break; } return PropertyEditorDelegate::createEditor(parent, option, index); } #include "qtiviconstrainedvaluedelegate.moc" gammaray-2.11.3/plugins/qtivi/qtiviconstrainedvaluedelegate.h000066400000000000000000000036511413310443000244760ustar00rootroot00000000000000/* qtiviconstrainedvaluedelegate.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_QTIVICONSTRAINEDVALUEDELEGATE_H #define GAMMARAY_QTIVICONSTRAINEDVALUEDELEGATE_H #include namespace GammaRay { class QtIviConstrainedValueDelegate : public PropertyEditorDelegate { Q_OBJECT public: explicit QtIviConstrainedValueDelegate(QObject *parent); ~QtIviConstrainedValueDelegate() override; // Override or modify the editor for certain cases: // - Set limits in spinboxes for numeric ranges // - Show a special combobox for properties which have a list of allowed values, e.g. // QFlags with only some allowed combinations, like AirflowDirections QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override; }; } #endif // GAMMARAY_QTIVICONSTRAINEDVALUEDELEGATE_H gammaray-2.11.3/plugins/qtivi/qtiviobjectmodel.cpp000066400000000000000000000630701413310443000222600ustar00rootroot00000000000000/* qtiviobjectmodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "qtiviobjectmodel.h" #include #include #include #include #include #include #include #include #include #include #if defined(QT_IVIMEDIA_LIB) #include #endif #include #include #include #include #include #include #include #include #include #include static const quintptr PropertyCarrierIndex = ~quintptr(0); //#define IF_DEBUG(x) (x) #define IF_DEBUG(x) using namespace GammaRay; QT_USE_NAMESPACE class QIviGammarayPropertyOverrider : public QIviDefaultPropertyOverrider { public: explicit QIviGammarayPropertyOverrider(QIviAbstractFeature *carrier, QObject *parent = nullptr); bool setOverridenValue(int index, const QVariant &value) override; QString displayTextAt(int index) const override; QVariant editValueAt(int index) const override; QVariant iviConstraintsAt(int index) const override; }; QIviGammarayPropertyOverrider::QIviGammarayPropertyOverrider(QIviAbstractFeature *carrier, QObject *parent) : QIviDefaultPropertyOverrider(carrier, parent) { } bool QIviGammarayPropertyOverrider::setOverridenValue(int index, const QVariant &value) { if (m_carriers.empty()) return {}; const PropertyOverride &property = m_properties.at(index); QVariant toSet = value; if (value.userType() == qMetaTypeId()) { QVariant typeReference = property.cppValue(); if (typeReference.type() == QVariant::Int) { toSet = value.value().value(); } else { *(static_cast(typeReference.data())) = value.value().value(); toSet = typeReference; } } return QIviDefaultPropertyOverrider::setOverridenValue(index, toSet); } QString QIviGammarayPropertyOverrider::displayTextAt(int index) const { if (m_carriers.empty()) return {}; const PropertyOverride &property = m_properties.at(index); const QVariant value = property.cppValue(); const QMetaObject *const mo(QMetaType::metaObjectForType(value.userType())); const QString enumStr(EnumUtil::enumToString(value, nullptr, mo)); if (!enumStr.isEmpty()) return enumStr; return VariantHandler::displayString(value); } QVariant QIviGammarayPropertyOverrider::editValueAt(int index) const { if (m_carriers.empty()) return {}; const PropertyOverride &property = m_properties.at(index); const QVariant value = property.cppValue(); const QMetaObject *const mo(QMetaType::metaObjectForType(value.userType())); const QMetaEnum me(EnumUtil::metaEnum(value, nullptr, mo)); if (me.isValid()) { const int num(EnumUtil::enumToInt(value, me)); return QVariant::fromValue(EnumRepositoryServer::valueFromMetaEnum(num, me)); } return VariantHandler::serializableVariant(value); } QVariant QIviGammarayPropertyOverrider::iviConstraintsAt(int index) const { if (m_carriers.empty()) return {}; auto carrier = m_carriers.front(); const PropertyOverride &property = m_properties.at(index); QByteArray constraintsJSON; for (int i=0; imetaObject()->classInfoCount(); i++) { QMetaClassInfo ci = carrier->metaObject()->classInfo(i); if (QLatin1String(ci.name()) == QLatin1String("IviPropertyDomains")) { constraintsJSON = QByteArray(ci.value()); break; } } if (constraintsJSON.isEmpty()) return {}; QJsonDocument loadDoc = QJsonDocument::fromJson(constraintsJSON); if (loadDoc.isNull() || loadDoc.isEmpty() || !loadDoc.isObject()) return {}; if (!loadDoc.object().contains(property.name())) return {}; QJsonValue constraints = loadDoc.object().value(property.name()); QLatin1String range("range"); if (constraints.toObject().contains(range)) { QJsonArray vals = constraints.toObject().value(range).toArray(); return QVariantList() << static_cast(QtIviObjectModel::RangeConstraints) << vals.at(0).toDouble() << vals.at(1).toDouble(); } QVariantList result; QLatin1String minimum("minimum"); if (constraints.toObject().contains(minimum)) { double val = constraints.toObject().value(minimum).toDouble(); result << static_cast(QtIviObjectModel::MinRangeConstraints) << val; } QLatin1String maximum("maximum"); if (constraints.toObject().contains(maximum)) { double val = constraints.toObject().value(maximum).toDouble(); result << static_cast(QtIviObjectModel::MaxRangeConstraints) << val; } if (!result.isEmpty()) return result; QLatin1String domain("domain"); if (constraints.toObject().contains(domain)) { const QVariantList vals = constraints.toObject().value(domain).toArray().toVariantList(); QVariantList res; res << static_cast(QtIviObjectModel::AvailableValuesConstraints) << vals; return res; } return {}; } QtIviObjectModel::QtIviObjectModel(Probe *probe) : QAbstractItemModel(probe) { // Known wrongly qualified types qRegisterMetaType(); qRegisterMetaType(); #if defined(QT_IVIMEDIA_LIB) qRegisterMetaType(); #endif connect(probe, SIGNAL(objectCreated(QObject*)), this, SLOT(objectAdded(QObject*))); connect(probe, SIGNAL(objectDestroyed(QObject*)), this, SLOT(objectRemoved(QObject*))); connect(probe, SIGNAL(objectReparented(QObject*)), this, SLOT(objectReparented(QObject*))); connect(probe, SIGNAL(objectSelected(QObject*,QPoint)), this, SLOT(objectSelected(QObject*))); } int QtIviObjectModel::rowOfCarrier(const QObject *carrier) const { for (uint i = 0; i < m_serviceCarriers.size(); i++) { if (m_serviceCarriers.at(i).m_service->handles(carrier)) { return i; } } return -1; } QModelIndex QtIviObjectModel::indexOfCarrier(const QObject *carrier, int column) const { const int row(rowOfCarrier(carrier)); return row == -1 ? QModelIndex() : index(row, column); } QModelIndex QtIviObjectModel::indexOfProperty(const QObject *c, const QByteArray &property, int column) const { const int carrierRow = rowOfCarrier(c); if (carrierRow < 0) return QModelIndex(); const auto &carrier = m_serviceCarriers.at(carrierRow).m_service; const int propertyIndex = carrier->indexOfProperty(property); if (propertyIndex < 0) return QModelIndex(); return createIndex(propertyIndex, column, carrierRow); } void QtIviObjectModel::objectAdded(QObject *obj) { IF_DEBUG(std::cout << "QtIviObjectModel::objectAdded() " << obj << std::endl); // see Probe::objectCreated, that promises a valid object in the main thread here Q_ASSERT(thread() == QThread::currentThread()); Q_ASSERT(Probe::instance()->isValidObject(obj)); QIviAbstractFeature *featureObj = qobject_cast(obj); QString serviceId = featureObj && featureObj->serviceObject() ? featureObj->serviceObject()->id() : QString(); QString zoneId; if (const QIviAbstractZonedFeature *zoned = qobject_cast(featureObj)) { zoneId = zoned->zone(); } if (!serviceId.isEmpty()) { IF_DEBUG(std::cout << "QtIviObjectModel::objectAdded() - object is an Ivi Object: " << obj << std::endl); Q_ASSERT(!m_handledObjects.contains(obj)); m_handledObjects << obj; auto it = std::find_if(m_serviceCarriers.begin(), m_serviceCarriers.end(), [serviceId, zoneId](const ServiceZone &p) { return p.m_service->serviceId() == serviceId && p.m_zone == zoneId; }); if (it != m_serviceCarriers.end()) { (*it).m_service->addCarrier(featureObj); } else { beginInsertRows(QModelIndex(), m_serviceCarriers.size(), m_serviceCarriers.size()); ServiceZone sz; sz.m_zone = zoneId; sz.m_service = std::make_shared(featureObj); m_serviceCarriers.push_back(sz); endInsertRows(); const QMetaObject *mo(obj->metaObject()); const int propertyOffset = QIviAbstractFeature::staticMetaObject.propertyCount(); const int propertyCount(mo->propertyCount() - propertyOffset); if (propertyCount > 0) { const int featureRow(m_serviceCarriers.size() - 1); const QModelIndex featureIndex(createIndex(featureRow, 0, -1)); beginInsertRows(featureIndex, 0, propertyCount - 1); sz.m_service->addCarrier(featureObj); endInsertRows(); connect(sz.m_service.get(), &QIviDefaultPropertyOverrider::propertyChanged, this, &QtIviObjectModel::propertyChanged); } } } } void QtIviObjectModel::objectRemoved(QObject *obj) { // slot, hence should always land in main thread due to auto connection Q_ASSERT(thread() == QThread::currentThread()); if (m_handledObjects.contains(obj)) { QIviAbstractFeature *featureObj = qobject_cast(obj); for(auto &c : m_serviceCarriers) { if(c.m_service->handles(obj)) { c.m_service->removeCarrier(featureObj); if (c.m_service->numCarriers() == 0) { const int row(rowOfCarrier(obj)); beginRemoveRows(QModelIndex(), row, row); m_serviceCarriers.erase(m_serviceCarriers.begin() + row); endRemoveRows(); } break; } } } } void QtIviObjectModel::objectReparented(QObject *obj) { // slot, hence should always land in main thread due to auto connection Q_ASSERT(thread() == QThread::currentThread()); if (m_handledObjects.contains(obj)) { IF_DEBUG(std::cout << "QtIviObjectModel::objectReparented() " << obj << std::endl); // We don't need reparenting: // * Plain Qt properties can't move // * QIviProperty's are stuck with their parent } } void QtIviObjectModel::objectSelected(QObject *obj) { const ObjectId id(obj); const QModelIndex index = match(this->index(0, 0), ObjectModel::ObjectIdRole, QVariant::fromValue(id), 1, Qt::MatchExactly | Qt::MatchRecursive | Qt::MatchWrap).value(0); if (!index.isValid()) { return; } QItemSelectionModel *const selectionModel = ObjectBroker::selectionModel(this); selectionModel->select(index, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows | QItemSelectionModel::Current); } QVariant QtIviObjectModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { switch (section) { case NameColumn: return tr("Name"); case ValueColumn: return tr("Value"); case WritableColumn: return tr("Writable"); case OverrideColumn: return tr("Override"); case TypeColumn: return tr("Type"); } } return QAbstractItemModel::headerData(section, orientation, role); } Qt::ItemFlags QtIviObjectModel::flags(const QModelIndex &index) const { Qt::ItemFlags flags = QAbstractItemModel::flags(index); if (index.isValid() && index.internalId() != PropertyCarrierIndex) { const QIviDefaultPropertyOverriderPtr &carrier(m_serviceCarriers.at(index.parent().row()).m_service); // We have two levels, this is the second flags |= Qt::ItemNeverHasChildren; switch (index.column()) { case ValueColumn: { if (carrier->isWritableAt(index.row()) || carrier->isOverridableAt(index.row())) flags |= Qt::ItemIsEditable; break; } case WritableColumn: { flags |= Qt::ItemIsUserCheckable; if (carrier->isOverriddenAt(index.row()) && !carrier->isWritableAt(index.row())) flags |= Qt::ItemIsEditable; else flags &= ~Qt::ItemIsEnabled; break; } case OverrideColumn: { flags |= Qt::ItemIsUserCheckable; if (carrier->isOverridableAt(index.row()) || carrier->isOverriddenAt(index.row())) flags |= Qt::ItemIsEditable; else flags &= ~Qt::ItemIsEnabled; break; } default: break; } if (!carrier->isAvailableAt(index.row())) { flags &= ~Qt::ItemIsEnabled; flags &= ~Qt::ItemIsEditable; } } return flags; } QVariant QtIviObjectModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) { return QVariant(); } const auto parentRow = index.internalId(); if (parentRow == PropertyCarrierIndex) { // Carrier if (index.row() >= 0 && uint(index.row()) < m_serviceCarriers.size()) { const auto &carrier = m_serviceCarriers.at(index.row()).m_service; switch (role) { case Qt::DisplayRole: { // The columns are a bit awkward here. They are assigned that way for compatibility // with the header data meant for the properties. switch (index.column()) { case NameColumn: return carrier->label(); case TypeColumn: return carrier->typeName(); default: break; } break; } case Qt::ToolTipRole: return carrier->description(); // case ObjectModel::ObjectIdRole: // return QVariant::fromValue(carrier->objectId()); default: break; } } } else { // Property if (parentRow < m_serviceCarriers.size()) { const QIviDefaultPropertyOverriderPtr &carrier = m_serviceCarriers.at(parentRow).m_service; if (index.row() >= 0 && index.row() < carrier->propertyCount()) { switch (role) { case Qt::DisplayRole: { switch (index.column()) { case NameColumn: return carrier->nameAt(index.row()); case ValueColumn: return carrier->displayTextAt(index.row()); case TypeColumn: { QString result = carrier->typeNameAt(index.row()); QVariant v = carrier->iviConstraintsAt(index.row()); if (v.isValid() && v.type() == QVariant::List) result += QLatin1String(" (!)"); return result; } default: break; } break; } case Qt::EditRole: { switch (index.column()) { case ValueColumn: return carrier->editValueAt(index.row()); default: break; } break; } case Qt::CheckStateRole: { switch (index.column()) { case WritableColumn: return carrier->isWritableAt(index.row()) || carrier->isOverriddenAt(index.row()) ? Qt::Checked : Qt::Unchecked; case OverrideColumn: return carrier->isOverriddenAt(index.row()) ? Qt::Checked : Qt::Unchecked; default: break; } break; } case Qt::ToolTipRole: { switch (index.column()) { case TypeColumn: case ValueColumn: { QVariant v = carrier->iviConstraintsAt(index.row()); if (!v.isValid() || v.type() != QVariant::List) return {}; QVariantList vl = v.toList(); QString result; while (!vl.isEmpty()) { switch (vl.front().toUInt()) { case QtIviObjectModel::RangeConstraints: result += QString(QLatin1String("Range: [%1 - %2]\n")).arg(vl.at(1).toDouble()).arg(vl.at(2).toDouble()); vl.pop_front(); vl.pop_front(); vl.pop_front(); break; case QtIviObjectModel::MinRangeConstraints: result += QString(QLatin1String("Minimum: %1\n")).arg(vl.at(1).toDouble()); vl.pop_front(); vl.pop_front(); break; case QtIviObjectModel::MaxRangeConstraints: result += QString(QLatin1String("Maximum: %1\n")).arg(vl.at(1).toDouble()); vl.pop_front(); vl.pop_front(); break; case QtIviObjectModel::AvailableValuesConstraints: { vl.pop_front(); QStringList valstrings; for(const auto &vi: qAsConst(vl)) valstrings << vi.toString(); result += QString(QLatin1String("Valid Values: [%1]\n")).arg(valstrings.join(QLatin1String(", "))); vl.clear(); break; } default: break; } } return result.trimmed(); } default: break; } break; } case IsIviProperty: { return carrier->isOverridableAt(index.row()); } case ValueConstraintsRole: { switch (index.column()) { case ValueColumn: return carrier->iviConstraintsAt(index.row()); default: break; } break; } // case ObjectModel::ObjectIdRole: // return QVariant::fromValue(property.objectId()); // case RawValue: // return property.cppValue(); } } } } return QVariant(); } QMap QtIviObjectModel::itemData(const QModelIndex &index) const { QMap ret = QAbstractItemModel::itemData(index); QVariant maybeConstraints = data(index, ValueConstraintsRole); if (maybeConstraints.isValid()) { ret.insert(ValueConstraintsRole, maybeConstraints); } ret.insert(IsIviProperty, data(index, IsIviProperty)); ret.insert(ObjectModel::ObjectIdRole, data(index, ObjectModel::ObjectIdRole)); return ret; } bool QtIviObjectModel::setData(const QModelIndex &index, const QVariant &value, int role) { const auto parentRow = index.internalId(); if (!index.isValid() || parentRow == PropertyCarrierIndex || parentRow >= m_serviceCarriers.size() || !flags(index).testFlag(Qt::ItemIsEditable)) { return false; } QIviDefaultPropertyOverriderPtr &carrier = m_serviceCarriers[parentRow].m_service; if (index.row() >= 0 && index.row() < carrier->propertyCount()) { switch (index.column()) { case ValueColumn: { if (role == Qt::DisplayRole || role == Qt::EditRole) { if (carrier->setOverridenValue(index.row(), value)) { if (!carrier->hasNotifySignalAt(index.row())) { emitRowDataChanged(index); } return true; } } break; } case WritableColumn: { if (role == Qt::CheckStateRole) { // This should only ever be checkable because of a not-really-writable property. // Reset the not really writable flag. if (!carrier->isWritableAt(index.row()) && carrier->isOverriddenAt(index.row())) { carrier->setOverride(index.row(), false); emitRowDataChanged(index); return true; } } break; } case OverrideColumn: { if (role == Qt::CheckStateRole) { const bool isOverride = value.value() == Qt::Checked; if(carrier->setOverride(index.row(), isOverride)) { emitRowDataChanged(index); return true; } } break; } default: break; } } return false; } void QtIviObjectModel::propertyChanged() { if (QIviDefaultPropertyOverrider * carrier = qobject_cast(sender())) { // A plain Qt property changed in a service or feature // Let's update the complete children as we can not known the property that changed for (int row = 0; row < int(m_serviceCarriers.size()); ++row) { if (m_serviceCarriers.at(row).m_service.get() == carrier) { const QModelIndex parent(index(row, 0)); const int ccount(columnCount(parent)); const int rcount(rowCount(parent)); if (ccount > 0 && rcount > 0) { emit dataChanged(index(0, 0, parent), index(rcount - 1, ccount - 1, parent)); } break; } } } } void QtIviObjectModel::emitRowDataChanged(const QModelIndex &index) { emit dataChanged(index.sibling(index.row(), 0), index.sibling(index.row(), columnCount() - 1)); } int QtIviObjectModel::rowCount(const QModelIndex &parent) const { if (parent.column() >= 1) { return 0; } if (parent.isValid()) { // only carriers have another level of children if (parent.internalId() == PropertyCarrierIndex) { const QIviDefaultPropertyOverriderPtr &carrier = m_serviceCarriers.at(parent.row()).m_service; return carrier->propertyCount(); } return 0; } else { return m_serviceCarriers.size(); } } int QtIviObjectModel::columnCount(const QModelIndex &) const { return ColumnCount; } QModelIndex QtIviObjectModel::parent(const QModelIndex &child) const { if (child.isValid()) { const auto parentRow = child.internalId(); if (parentRow != PropertyCarrierIndex) { return createIndex(parentRow, 0, PropertyCarrierIndex); } } return QModelIndex(); } /* Usage of QModelIndex::internalId() aka internalPointer(): - toplevel (carrier): PropertyCarrierIndex - second level (property): index of carrier property (parent) */ QModelIndex QtIviObjectModel::index(int row, int column, const QModelIndex &parent) const { if (column >= 0 && column < ColumnCount) { if (parent.isValid()) { // create an index to a property const auto grandparentRow = parent.internalId(); // only carriers have another level of children if (grandparentRow == PropertyCarrierIndex && parent.row() >= 0 && uint(parent.row()) < m_serviceCarriers.size()) { const QIviDefaultPropertyOverriderPtr &carrier = m_serviceCarriers.at(parent.row()).m_service; if (row >= 0 && row < carrier->propertyCount()) { return createIndex(row, column, parent.row()); } } } else { // create an index to a carrier if (row >= 0 && uint(row) < m_serviceCarriers.size()) { return createIndex(row, column, PropertyCarrierIndex); } } } return QModelIndex(); } gammaray-2.11.3/plugins/qtivi/qtiviobjectmodel.h000066400000000000000000000077601413310443000217310ustar00rootroot00000000000000/* qtiviobjectmodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_QTIVI_QTIVIOBJECTMODEL_H #define GAMMARAY_QTIVI_QTIVIOBJECTMODEL_H #include #include #include #include #include #include class QtIviObjectModelTest; QT_BEGIN_NAMESPACE class QIviDefaultPropertyOverrider; QT_END_NAMESPACE namespace GammaRay { class Probe; class ObjectId; // The QtIviObjectModel is a model that expose QIviServiceObject and // QIviAbstractFeature objects with any if their properties being plain // Qt properties or QIviProperty. class QtIviObjectModel : public QAbstractItemModel { Q_OBJECT friend class ::QtIviObjectModelTest; public: enum Roles { ValueConstraintsRole = ObjectModel::UserRole + 1, // transmits the following constraints types RangeConstraints, // min / max MinRangeConstraints, // min MaxRangeConstraints, // max AvailableValuesConstraints, // list of possible values RawValue, // the raw variant value without any transformation IsIviProperty }; enum Columns { NameColumn = 0, // The carrier label or property name ValueColumn, WritableColumn, OverrideColumn, TypeColumn, ColumnCount }; explicit QtIviObjectModel(Probe *probe); QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; Qt::ItemFlags flags(const QModelIndex &index) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; QMap itemData(const QModelIndex &index) const override; bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; QModelIndex parent(const QModelIndex &child) const override; QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; private slots: void objectAdded(QObject *obj); void objectRemoved(QObject *obj); void objectReparented(QObject *obj); void objectSelected(QObject *obj); void propertyChanged(); private: void emitRowDataChanged(const QModelIndex &index); int rowOfCarrier(const QObject *carrier) const; QModelIndex indexOfCarrier(const QObject *carrier, int column = 0 /*NameColumn*/) const; QModelIndex indexOfProperty(const QObject *carrier, const QByteArray &property, int column = 0 /*NameColumn*/) const; typedef std::shared_ptr QIviDefaultPropertyOverriderPtr; struct ServiceZone { QString m_zone; QIviDefaultPropertyOverriderPtr m_service; }; std::vector m_serviceCarriers; QSet m_handledObjects; }; } #endif gammaray-2.11.3/plugins/qtivi/qtivipropertyclientmodel.cpp000066400000000000000000000037011413310443000240700ustar00rootroot00000000000000/* qtivipropertyclientmodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Filipe Azevedo Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "qtivipropertyclientmodel.h" #include "qtivipropertymodel.h" #include #include using namespace GammaRay; QtIviPropertyClientModel::QtIviPropertyClientModel(QObject *parent) : QSortFilterProxyModel(parent) { } QtIviPropertyClientModel::~QtIviPropertyClientModel() = default; QVariant QtIviPropertyClientModel::data(const QModelIndex &index, int role) const { // QIviProperty's appears in italic to distinguish from plain qt properties if (role == Qt::FontRole) { const bool isIviProperty = QSortFilterProxyModel::data(index, QtIviPropertyModel::IsIviProperty).toBool(); QFont font = QApplication::font("QAbstractItemView"); font.setItalic(isIviProperty); return QVariant::fromValue(font); } return QSortFilterProxyModel::data(index, role); } gammaray-2.11.3/plugins/qtivi/qtivipropertyclientmodel.h000066400000000000000000000032541413310443000235400ustar00rootroot00000000000000/* qtivipropertyclientmodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Filipe Azevedo Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_QTIVI_PROPERTYCLIENTMODEL_H #define GAMMARAY_QTIVI_PROPERTYCLIENTMODEL_H #include namespace GammaRay { /** UI-dependent (and thus client-side) bits of the ivi property model. */ class QtIviPropertyClientModel : public QSortFilterProxyModel { Q_OBJECT public: explicit QtIviPropertyClientModel(QObject *parent = nullptr); ~QtIviPropertyClientModel() override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; }; } #endif // GAMMARAY_QTIVI_PROPERTYCLIENTMODEL_H gammaray-2.11.3/plugins/qtivi/qtivipropertymodel.cpp000066400000000000000000000726771413310443000227130ustar00rootroot00000000000000/* qtivipropertymodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "qtivipropertymodel.h" #include #include #include #include #include #include #include #include #include #include #if defined(QT_IVIMEDIA_LIB) #include #endif #include #include #include #include #include #include #include #include static const quint64 PropertyCarrierIndex = ~quint64(0); // there is no QUINT64_MAX //#define IF_DEBUG(x) (x) #define IF_DEBUG(x) using namespace GammaRay; // Note: convertToCppType() could be used instead of or to implement QtIviPropertyOverrider::cppValue() - // the latter just seems more direct and less likely to go wrong for now. // Convert back to C++ type if it was sanitized for QML - trying to imitate what // QIviProperty::setValue(const QVariant &value) does; note that the interesting // part of setValue() is the int * -> void * conversion for the args array. // The rest of setValue() checks to see if it is okay to do that :) static QVariant convertToCppType(QVariant qmlVar, int cppTypeId) { if (qmlVar.userType() != cppTypeId) { if (qmlVar.canConvert(cppTypeId)) { qmlVar.convert(cppTypeId); } else { const QMetaEnum me = EnumUtil::metaEnum(QVariant(cppTypeId, nullptr)); if (me.isValid() && qmlVar.canConvert()) { const int rawValue = qmlVar.toInt(); // An int is not "naturally" convertible to an enum or flags unless // a conversion is explicitly registered. // ### If a Q_ENUM could inherit long long, this could be an uninitialized memory read. // I think it can't be long long, due to QMetaEnum API being int-based. return QVariant(cppTypeId, &rawValue); } } } return qmlVar; } QtIviPropertyModel::QtIviPropertyModel(Probe *probe) : QAbstractItemModel(probe) { // Known wrongly qualified types qRegisterMetaType(); qRegisterMetaType(); #if defined(QT_IVIMEDIA_LIB) qRegisterMetaType(); #endif connect(probe, SIGNAL(objectCreated(QObject*)), this, SLOT(objectAdded(QObject*))); connect(probe, SIGNAL(objectDestroyed(QObject*)), this, SLOT(objectRemoved(QObject*))); connect(probe, SIGNAL(objectReparented(QObject*)), this, SLOT(objectReparented(QObject*))); connect(probe, SIGNAL(objectSelected(QObject*,QPoint)), this, SLOT(objectSelected(QObject*))); } QtIviPropertyModel::IviCarrierProperty::IviCarrierProperty(QtIviPropertyModel::IviCarrierProperty &&other) { *this = std::move(other); } QtIviPropertyModel::IviCarrierProperty::IviCarrierProperty(QIviProperty *iviProperty, const QMetaProperty &metaProperty) : m_metaProperty(metaProperty) , m_iviProperty(iviProperty) , m_iviOverrider(iviProperty) { } QtIviPropertyModel::IviCarrierProperty::IviCarrierProperty(const QMetaProperty &metaProperty) : m_metaProperty(metaProperty) , m_iviProperty(nullptr) { } bool QtIviPropertyModel::IviCarrierProperty::isValid() const { return m_metaProperty.isValid(); } bool QtIviPropertyModel::IviCarrierProperty::isAvailable() const { return m_iviProperty ? m_iviProperty->isAvailable() : m_metaProperty.isValid(); } bool QtIviPropertyModel::IviCarrierProperty::hasNotifySignal() const { return m_iviProperty ? true : m_metaProperty.hasNotifySignal(); } bool QtIviPropertyModel::IviCarrierProperty::isWritable() const { return (m_iviProperty ? m_iviOverrider.userWritable() : m_metaProperty.isWritable() && !QMetaType(m_metaProperty.userType()).flags().testFlag(QMetaType::PointerToQObject)); } bool QtIviPropertyModel::IviCarrierProperty::isOverridable() const { return m_iviProperty; } bool QtIviPropertyModel::IviCarrierProperty::isOverrided() const { return m_iviProperty && m_iviOverrider.isOverride(); } QString QtIviPropertyModel::IviCarrierProperty::name() const { return QString::fromUtf8(m_metaProperty.name()); } QString QtIviPropertyModel::IviCarrierProperty::typeName() const { const int userType(m_iviProperty ? m_iviOverrider.cppValue().userType() : m_metaProperty.userType()); return QString::fromLatin1(QMetaType::typeName(userType)); } ObjectId QtIviPropertyModel::IviCarrierProperty::objectId() const { return ObjectId(m_iviProperty); } QString QtIviPropertyModel::IviCarrierProperty::displayText(const QObject *carrier) const { const QVariant value(cppValue(carrier)); const QMetaObject *const mo(QMetaType::metaObjectForType(value.userType())); const QString enumStr(EnumUtil::enumToString(value, nullptr, mo)); if (!enumStr.isEmpty()) return enumStr; return VariantHandler::displayString(value); } QVariant QtIviPropertyModel::IviCarrierProperty::editValue(const QObject *carrier) const { const QVariant value(cppValue(carrier)); const QMetaObject *const mo(QMetaType::metaObjectForType(value.userType())); const QMetaEnum me(EnumUtil::metaEnum(value, nullptr, mo)); if (me.isValid()) { const int num(EnumUtil::enumToInt(value, me)); return QVariant::fromValue(EnumRepositoryServer::valueFromMetaEnum(num, me)); } return VariantHandler::serializableVariant(value); } QVariant QtIviPropertyModel::IviCarrierProperty::cppValue(const QObject *carrier) const { return m_iviProperty ? m_iviOverrider.cppValue() : m_metaProperty.read(carrier); } QVariant QtIviPropertyModel::IviCarrierProperty::iviConstraints() const { if (!m_iviProperty) return QVariant(); // value range? const QVariant min = m_iviProperty->minimumValue(); const QVariant max = m_iviProperty->maximumValue(); if (min != max) return QVariantList() << quint32(QtIviPropertyModel::RangeConstraints) << min << max; // list of allowed values? const QVariantList rawAvail = m_iviProperty->availableValues(); if (!rawAvail.isEmpty()) { QVariantList avail; avail << uint(QtIviPropertyModel::AvailableValuesConstraints); for (const QVariant &qmlVar : rawAvail) { const QVariant v = convertToCppType(qmlVar, QIviPropertyPrivate::get(m_iviProperty)->m_type); // For convenience on the view side, send for each allowed value: // 1. Display string to display to the user // 2. "Raw" value for passing to setData() after editing const QMetaEnum me = EnumUtil::metaEnum(v); if (me.isValid()) { const int num = EnumUtil::enumToInt(v, me); if (me.isFlag()) avail << QString::fromLatin1(me.valueToKeys(num)); else avail << QString::fromLatin1(me.valueToKey(num)); avail << QVariant::fromValue(EnumRepositoryServer::valueFromMetaEnum(num, me)); } else { avail << VariantHandler::displayString(v); avail << VariantHandler::serializableVariant(v); } } return avail; } return QVariant(); } void QtIviPropertyModel::IviCarrierProperty::setOverrided(bool override) { if (override != m_iviOverrider.isOverride()) { if (override) { // for the initial override value, keep the "real" value from original backend const QVariant originalValue = m_iviOverrider.cppValue(); m_iviOverrider.setOverride(true); m_iviOverrider.setValue(originalValue); } else { // revert to value from original backend m_iviOverrider.setOverride(false); emit m_iviProperty->valueChanged(m_iviProperty->value()); } } } bool QtIviPropertyModel::IviCarrierProperty::setValue(const QVariant &value, QObject *carrier) { Q_ASSERT(isAvailable()); QVariant toSet = value; if (value.userType() == qMetaTypeId()) { QVariant typeReference = cppValue(carrier); if (typeReference.type() == QVariant::Int) { toSet = value.value().value(); } else { *(static_cast(typeReference.data())) = value.value().value(); toSet = typeReference; } } if (m_iviProperty) { const bool wasOverride = isOverrided(); bool isOverride = wasOverride || !isWritable(); if (isOverride && !wasOverride && isOverridable()) { setOverrided(isOverride); } m_iviOverrider.setValue(toSet); // Hack: some properties reject value changes with no general way to know that // up front, so check and compensate similarly to "proper" read-only properties. if (!isOverride && m_iviOverrider.cppValue() != toSet) { isOverride = true; setOverrided(isOverride); m_iviOverrider.setValue(toSet); } // Make sure the model can update itself if (isOverride) { emit m_iviProperty->valueChanged(m_iviProperty->value()); } // ... else the valueChanged() signal was hopefully emitted from the setter return true; } else { if (!m_metaProperty.isWritable()) { return false; } return m_metaProperty.write(carrier, toSet); } } QtIviPropertyModel::IviCarrierProperty &QtIviPropertyModel::IviCarrierProperty::operator=(QtIviPropertyModel::IviCarrierProperty &&other) { m_metaProperty = other.m_metaProperty; m_iviProperty = other.m_iviProperty; m_iviOverrider = std::move(other.m_iviOverrider); return *this; } bool QtIviPropertyModel::IviCarrierProperty::operator==(const QIviProperty *property) const { return this->m_iviProperty == property; } bool QtIviPropertyModel::IviCarrierProperty::operator==(const QByteArray &property) const { return this->m_metaProperty.name() == property; } QtIviPropertyModel::IviCarrier::IviCarrier(QObject *carrier) : m_carrier(carrier) { } QString QtIviPropertyModel::IviCarrier::label() const { QString name = m_carrier->objectName(); if (name.isEmpty()) { if (const QIviAbstractZonedFeature *zoned = qobject_cast(m_carrier)) { // not translated; the zone API is fixed to English, too name = QString::fromLatin1("Zone: %1").arg(zoned->zone()); } } if (name.isEmpty()) { name = QString::fromLatin1("%1(0x%2)") .arg(typeName()) .arg(QString::number(quintptr(m_carrier), 16)); } return name; } QString QtIviPropertyModel::IviCarrier::typeName() const { return QString::fromLatin1(m_carrier->metaObject()->className()); } ObjectId QtIviPropertyModel::IviCarrier::objectId() const { return ObjectId(m_carrier); } int QtIviPropertyModel::IviCarrier::propertyCount() const { return m_properties.size(); } void QtIviPropertyModel::IviCarrier::pushProperty(IviCarrierProperty &&property) { m_properties.push_back(std::move(property)); } const QtIviPropertyModel::IviCarrierProperty &QtIviPropertyModel::IviCarrier::propertyAt(int index) const { return m_properties.at(index); } QtIviPropertyModel::IviCarrierProperty &QtIviPropertyModel::IviCarrier::propertyAt(int index) { return m_properties.at(index); } int QtIviPropertyModel::IviCarrier::indexOfProperty(const QIviProperty *property) const { for (uint i = 0; i < m_properties.size(); i++) { if (m_properties.at(i) == property) { return i; } } return -1; } int QtIviPropertyModel::IviCarrier::indexOfProperty(const QByteArray &property) const { for (uint i = 0; i < m_properties.size(); i++) { if (m_properties.at(i) == property) { return i; } } return -1; } bool QtIviPropertyModel::IviCarrier::operator==(const QObject *carrier) const { return m_carrier == carrier; } int QtIviPropertyModel::rowOfCarrier(const QObject *carrier) const { for (uint i = 0; i < m_carriers.size(); i++) { if (m_carriers.at(i) == carrier) { return i; } } return -1; } QModelIndex QtIviPropertyModel::indexOfCarrier(const QObject *carrier, int column) const { const int row(rowOfCarrier(carrier)); return row == -1 ? QModelIndex() : index(row, column); } QModelIndex QtIviPropertyModel::indexOfProperty(const QIviProperty *property, int column) const { Q_ASSERT(property->parent()); const int carrierRow = rowOfCarrier(property->parent()); if (carrierRow < 0) return QModelIndex(); const IviCarrier &carrier = m_carriers.at(carrierRow); const int propertyIndex = carrier.indexOfProperty(property); if (propertyIndex < 0) return QModelIndex(); return createIndex(propertyIndex, column, carrierRow); } QModelIndex QtIviPropertyModel::indexOfProperty(const QObject *c, const QByteArray &property, int column) const { const int carrierRow = rowOfCarrier(c); if (carrierRow < 0) return QModelIndex(); const IviCarrier &carrier = m_carriers.at(carrierRow); const int propertyIndex = carrier.indexOfProperty(property); if (propertyIndex < 0) return QModelIndex(); return createIndex(propertyIndex, column, carrierRow); } void QtIviPropertyModel::objectAdded(QObject *obj) { IF_DEBUG(std::cout << "QtIviPropertyModel::objectAdded() " << obj << std::endl); // see Probe::objectCreated, that promises a valid object in the main thread here Q_ASSERT(thread() == QThread::currentThread()); Q_ASSERT(Probe::instance()->isValidObject(obj)); int propertyOffset = -1; if (qobject_cast(obj)) { propertyOffset = QIviServiceObject::staticMetaObject.propertyOffset(); } else if (qobject_cast(obj)) { propertyOffset = QIviAbstractFeature::staticMetaObject.propertyOffset(); } if (propertyOffset != -1) { IF_DEBUG(std::cout << "QtIviPropertyModel::objectAdded() - object is an Ivi Object: " << obj << std::endl); Q_ASSERT(!m_seenCarriers.contains(obj)); m_seenCarriers << obj; beginInsertRows(QModelIndex(), m_carriers.size(), m_carriers.size()); m_carriers.push_back(IviCarrier(obj)); endInsertRows(); const QMetaObject *mo(obj->metaObject()); const int propertyCount(mo->propertyCount() - propertyOffset); const int featureRow(m_carriers.size() - 1); auto &feature(m_carriers.back()); if (propertyCount > 0) { const QModelIndex featureIndex(createIndex(featureRow, 0, -1)); const int row(feature.propertyCount()); beginInsertRows(featureIndex, row, row + propertyCount - 1); for (int i = 0; i < propertyCount; ++i) { const QMetaProperty metaProperty(mo->property(i + propertyOffset)); QIviProperty *property(metaProperty.read(obj).value()); if (property) { feature.pushProperty(QtIviPropertyModel::IviCarrierProperty(property, metaProperty)); connect(property, &QIviProperty::valueChanged, this, &QtIviPropertyModel::propertyChanged); connect(property, &QIviProperty::availableChanged, this, &QtIviPropertyModel::propertyChanged); } else { feature.pushProperty(QtIviPropertyModel::IviCarrierProperty(metaProperty)); if (metaProperty.hasNotifySignal()) { const QByteArray normalizedSlot(QMetaObject::normalizedSignature("propertyChanged()")); const int propertyChangedSlotIndex(staticMetaObject.indexOfSlot(normalizedSlot)); QMetaObject::connect(obj, metaProperty.notifySignalIndex(), this, propertyChangedSlotIndex); } } } endInsertRows(); } } } void QtIviPropertyModel::objectRemoved(QObject *obj) { // slot, hence should always land in main thread due to auto connection Q_ASSERT(thread() == QThread::currentThread()); if (m_seenCarriers.contains(obj)) { const int row(rowOfCarrier(obj)); if (row == -1) { IF_DEBUG(std::cout << "QtIviPropertyModel::objectRemoved(): we don't know this Ivi Object. " << obj << std::endl); return; } IF_DEBUG(std::cout << "QtIviPropertyModel::objectRemoved(): removing an Ivi Object. " << obj << std::endl); beginRemoveRows(QModelIndex(), row, row); m_carriers.erase(m_carriers.begin() + row); endRemoveRows(); } } void QtIviPropertyModel::objectReparented(QObject *obj) { // slot, hence should always land in main thread due to auto connection Q_ASSERT(thread() == QThread::currentThread()); if (m_seenCarriers.contains(obj)) { IF_DEBUG(std::cout << "QtIviPropertyModel::objectReparented() " << obj << std::endl); // We don't need reparenting: // * Plain Qt properties can't move // * QIviProperty's are stuck with their parent } } void QtIviPropertyModel::objectSelected(QObject *obj) { const ObjectId id(obj); const QModelIndex index = match(this->index(0, 0), ObjectModel::ObjectIdRole, QVariant::fromValue(id), 1, Qt::MatchExactly | Qt::MatchRecursive | Qt::MatchWrap).value(0); if (!index.isValid()) { return; } QItemSelectionModel *const selectionModel = ObjectBroker::selectionModel(this); selectionModel->select(index, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows | QItemSelectionModel::Current); } QVariant QtIviPropertyModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { switch (section) { case NameColumn: return tr("Name"); case ValueColumn: return tr("Value"); case WritableColumn: return tr("Writable"); case OverrideColumn: return tr("Override"); case TypeColumn: return tr("Type"); } } return QAbstractItemModel::headerData(section, orientation, role); } Qt::ItemFlags QtIviPropertyModel::flags(const QModelIndex &index) const { Qt::ItemFlags flags = QAbstractItemModel::flags(index); if (index.isValid() && index.internalId() != PropertyCarrierIndex) { const auto &carrier(m_carriers.at(index.parent().row())); const auto &property(carrier.propertyAt(index.row())); // We have two levels, this is the second flags |= Qt::ItemNeverHasChildren; switch (index.column()) { case ValueColumn: { if (property.isWritable() || property.isOverridable()) flags |= Qt::ItemIsEditable; break; } case WritableColumn: { flags |= Qt::ItemIsUserCheckable; if (property.isOverrided() && !property.isWritable()) flags |= Qt::ItemIsEditable; else flags &= ~Qt::ItemIsEnabled; break; } case OverrideColumn: { flags |= Qt::ItemIsUserCheckable; if (property.isOverridable() || property.isOverrided()) flags |= Qt::ItemIsEditable; else flags &= ~Qt::ItemIsEnabled; break; } default: break; } if (!property.isAvailable()) { flags &= ~Qt::ItemIsEnabled; flags &= ~Qt::ItemIsEditable; } } return flags; } QVariant QtIviPropertyModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) { return QVariant(); } const quint64 parentRow = index.internalId(); if (parentRow == PropertyCarrierIndex) { // Carrier if (index.row() >= 0 && uint(index.row()) < m_carriers.size()) { const IviCarrier &carrier = m_carriers.at(index.row()); switch (role) { case Qt::DisplayRole: { // The columns are a bit awkward here. They are assigned that way for compatibility // with the header data meant for the properties. switch (index.column()) { case NameColumn: return carrier.label(); case TypeColumn: return carrier.typeName(); default: break; } break; } case ObjectModel::ObjectIdRole: return QVariant::fromValue(carrier.objectId()); default: break; } } } else { // Property if (parentRow < m_carriers.size()) { const IviCarrier &carrier = m_carriers.at(parentRow); if (index.row() >= 0 && index.row() < carrier.propertyCount()) { const IviCarrierProperty &property = carrier.propertyAt(index.row()); switch (role) { case Qt::DisplayRole: { switch (index.column()) { case NameColumn: return property.name(); case ValueColumn: return property.displayText(carrier.m_carrier); case TypeColumn: return property.typeName(); default: break; } break; } case Qt::EditRole: { switch (index.column()) { case ValueColumn: return property.editValue(carrier.m_carrier); default: break; } break; } case Qt::CheckStateRole: { switch (index.column()) { case WritableColumn: return property.isWritable() || property.isOverrided() ? Qt::Checked : Qt::Unchecked; case OverrideColumn: return property.isOverrided() ? Qt::Checked : Qt::Unchecked; default: break; } break; } case IsIviProperty: { return property.isOverridable(); } case ValueConstraintsRole: { switch (index.column()) { case ValueColumn: return property.iviConstraints(); default: break; } break; } case ObjectModel::ObjectIdRole: return QVariant::fromValue(property.objectId()); case RawValue: return property.cppValue(carrier.m_carrier); } } } } return QVariant(); } QMap QtIviPropertyModel::itemData(const QModelIndex &index) const { QMap ret = QAbstractItemModel::itemData(index); QVariant maybeConstraints = data(index, ValueConstraintsRole); if (maybeConstraints.isValid()) { ret.insert(ValueConstraintsRole, maybeConstraints); } ret.insert(IsIviProperty, data(index, IsIviProperty)); ret.insert(ObjectModel::ObjectIdRole, data(index, ObjectModel::ObjectIdRole)); return ret; } bool QtIviPropertyModel::setData(const QModelIndex &index, const QVariant &value, int role) { const quint64 parentRow = index.internalId(); if (!index.isValid() || parentRow == PropertyCarrierIndex || parentRow >= m_carriers.size() || !flags(index).testFlag(Qt::ItemIsEditable)) { return false; } IviCarrier &carrier = m_carriers[parentRow]; if (index.row() >= 0 && index.row() < carrier.propertyCount()) { IviCarrierProperty &property = carrier.propertyAt(index.row()); switch (index.column()) { case ValueColumn: { if (role == Qt::DisplayRole || role == Qt::EditRole) { if (property.setValue(value, carrier.m_carrier)) { if (!property.hasNotifySignal()) { emitRowDataChanged(index); } return true; } } break; } case WritableColumn: { if (role == Qt::CheckStateRole) { // This should only ever be checkable because of a not-really-writable property. // Reset the not really writable flag. if (!property.isWritable() && property.isOverrided()) { property.setOverrided(false); emitRowDataChanged(index); return true; } } break; } case OverrideColumn: { if (role == Qt::CheckStateRole) { const bool isOverride = value.value() == Qt::Checked; if (property.isOverridable() && isOverride != property.isOverrided()) { property.setOverrided(isOverride); emitRowDataChanged(index); return true; } } break; } default: break; } } return false; } void QtIviPropertyModel::propertyChanged() { if (QIviProperty *property = qobject_cast(sender())) { // An Ivi property changed emitRowDataChanged(indexOfProperty(property, NameColumn)); } else if (qobject_cast(sender()) || qobject_cast(sender())) { // A plain Qt property changed in a service or feature // Let's update the complete children as we can not known the property that changed const QModelIndex parent(indexOfCarrier(sender())); Q_ASSERT(parent.isValid()); const int ccount(columnCount(parent)); const int rcount(rowCount(parent)); if (ccount > 0 && rcount > 0) { emit dataChanged(index(0, 0, parent), index(rcount - 1, ccount - 1, parent)); } } } void QtIviPropertyModel::emitRowDataChanged(const QModelIndex &index) { emit dataChanged(index.sibling(index.row(), 0), index.sibling(index.row(), columnCount() - 1)); } int QtIviPropertyModel::rowCount(const QModelIndex &parent) const { if (parent.column() >= 1) { return 0; } if (parent.isValid()) { // only carriers have another level of children if (parent.internalId() == PropertyCarrierIndex) { const IviCarrier &carrier = m_carriers.at(parent.row()); return carrier.m_properties.size(); } return 0; } else { return m_carriers.size(); } } int QtIviPropertyModel::columnCount(const QModelIndex &) const { return ColumnCount; } QModelIndex QtIviPropertyModel::parent(const QModelIndex &child) const { if (child.isValid()) { const quint64 parentRow = child.internalId(); if (parentRow != PropertyCarrierIndex) { return createIndex(parentRow, 0, PropertyCarrierIndex); } } return QModelIndex(); } /* Usage of QModelIndex::internalId() aka internalPointer(): - toplevel (carrier): PropertyCarrierIndex - second level (property): index of carrier property (parent) */ QModelIndex QtIviPropertyModel::index(int row, int column, const QModelIndex &parent) const { if (column >= 0 && column < ColumnCount) { if (parent.isValid()) { // create an index to a property const quint64 grandparentRow = parent.internalId(); // only carriers have another level of children if (grandparentRow == PropertyCarrierIndex && parent.row() >= 0 && uint(parent.row()) < m_carriers.size()) { const IviCarrier &carrier = m_carriers.at(parent.row()); if (row >= 0 && uint(row) < carrier.m_properties.size()) { return createIndex(row, column, parent.row()); } } } else { // create an index to a carrier if (row >= 0 && uint(row) < m_carriers.size()) { return createIndex(row, column, PropertyCarrierIndex); } } } return QModelIndex(); } gammaray-2.11.3/plugins/qtivi/qtivipropertymodel.h000066400000000000000000000135121413310443000223370ustar00rootroot00000000000000/* qtivipropertymodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_QTIVI_QTIVIPROPERTYMODEL_H #define GAMMARAY_QTIVI_QTIVIPROPERTYMODEL_H #include "qtivipropertyoverrider.h" #include #include #include #include #include class QIviProperty; class QtIviPropertyModelTest; namespace GammaRay { class Probe; class ObjectId; // The QtIviPropertyModel is a model that expose QIviServiceObject and // QIviAbstractFeature objects with any if their properties being plain // Qt properties or QIviProperty. class QtIviPropertyModel : public QAbstractItemModel { Q_OBJECT friend class ::QtIviPropertyModelTest; public: enum Roles { ValueConstraintsRole = ObjectModel::UserRole + 1, // transmits the following constraints types RangeConstraints, // min / max AvailableValuesConstraints, // list of possible values RawValue, // the raw variant value without any transformation IsIviProperty }; enum Columns { NameColumn = 0, // The carrier label or property name ValueColumn, WritableColumn, OverrideColumn, TypeColumn, ColumnCount }; explicit QtIviPropertyModel(Probe *probe); QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; Qt::ItemFlags flags(const QModelIndex &index) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; QMap itemData(const QModelIndex &index) const override; bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; QModelIndex parent(const QModelIndex &child) const override; QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; private slots: void objectAdded(QObject *obj); void objectRemoved(QObject *obj); void objectReparented(QObject *obj); void objectSelected(QObject *obj); void propertyChanged(); private: void emitRowDataChanged(const QModelIndex &index); int rowOfCarrier(const QObject *carrier) const; QModelIndex indexOfCarrier(const QObject *carrier, int column = 0 /*NameColumn*/) const; QModelIndex indexOfProperty(const QIviProperty *property, int column = 0 /*NameColumn*/) const; QModelIndex indexOfProperty(const QObject *carrier, const QByteArray &property, int column = 0 /*NameColumn*/) const; class IviCarrierProperty { friend class ::QtIviPropertyModelTest; public: IviCarrierProperty() = default; IviCarrierProperty(IviCarrierProperty &&other); explicit IviCarrierProperty(QIviProperty *iviProperty, const QMetaProperty &metaProperty); explicit IviCarrierProperty(const QMetaProperty &metaProperty); bool isValid() const; bool isAvailable() const; bool hasNotifySignal() const; bool isWritable() const; bool isOverridable() const; bool isOverrided() const; QString name() const; QString typeName() const; ObjectId objectId() const; QString displayText(const QObject *carrier) const; QVariant editValue(const QObject *carrier) const; QVariant cppValue(const QObject *carrier) const; QVariant iviConstraints() const; void setOverrided(bool override); bool setValue(const QVariant &editValue, QObject *carrier); IviCarrierProperty &operator=(IviCarrierProperty &&other); bool operator==(const QIviProperty *property) const; bool operator==(const QByteArray &property) const; private: QMetaProperty m_metaProperty; QIviProperty *m_iviProperty = nullptr; QtIviPropertyOverrider m_iviOverrider; }; class IviCarrier { friend class QtIviPropertyModel; public: IviCarrier() = default; explicit IviCarrier(QObject *carrier); QString label() const; QString typeName() const; ObjectId objectId() const; int propertyCount() const; void pushProperty(IviCarrierProperty &&property); const IviCarrierProperty &propertyAt(int index) const; IviCarrierProperty &propertyAt(int index); int indexOfProperty(const QIviProperty *property) const; int indexOfProperty(const QByteArray &property) const; bool operator==(const QObject *carrier) const; private: QObject *m_carrier = nullptr; std::vector m_properties; }; std::vector m_carriers; QSet m_seenCarriers; }; } #endif gammaray-2.11.3/plugins/qtivi/qtivipropertyoverrider.cpp000066400000000000000000000206161413310443000235760ustar00rootroot00000000000000/* qtivipropertyoverrider.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "qtivipropertyoverrider.h" #include #include #include #include #include using namespace GammaRay; namespace GammaRay { /* For the purpose of overriding, there is a big difference between read-only and read-write properties. Read-write properties are effectively settings for lower layers from higher layers, with reading them back as a convenience function. Read-only properties are information about lower layers for higher layers. We handle them as follows: - Read-write properties: - Non-override mode: set them like user input would - Override mode: set them and make them return the set value, but don't pass it along to the backend (e.g. when setting the target temperature of air conditioning, only the target temperature as displayed would change, but the air in the car wouldn't be regulated warmer or colder). - Read-only properties: - Non-override mode: does not change any value - Override mode: As with read-write properties, only that the backend value couldn't be changed anyway (e.g. outside temperature). */ // Note: Those subclasses of QSlotObjectBase are not invoked through the ReadProperty / WriteProperty // "opcodes" of the metaobject system, they are just regular methods, for which the calling convention // is that a[0] points to the return value, a[1] to the first argument, a[2] to the second etc. class OverrideValueSetter : public QtPrivate::QSlotObjectBase { static void impl(int which, QSlotObjectBase *this_, QObject *, void **a, bool *ret) { switch (which) { case Destroy: delete static_cast(this_); break; case Call: { // store the value for later when we restore the original getters and setters - then we // can also restore the last value received from the real backend. OverrideValueSetter *const self = static_cast(this_); const QMetaType::Type mt = static_cast(self->m_stashedValue.userType()); void *const firstArgument = a[1]; self->m_stashedValue = QVariant(mt, firstArgument); break; } case Compare: *ret = false; // not implemented break; case NumOperations: ; } } public: QVariant m_stashedValue; explicit OverrideValueSetter(const QVariant &value) : QSlotObjectBase(&impl), m_stashedValue(value) {} }; class OverrideValueGetter : public QtPrivate::QSlotObjectBase { static void impl(int which, QSlotObjectBase *this_, QObject *, void **a, bool *ret) { switch (which) { case Destroy: delete static_cast(this_); break; case Call: { QtIviPropertyOverrider *const overrider = static_cast(this_)->m_overrider; const QMetaType::Type mt = static_cast(overrider->m_overrideValue.userType()); void *const returnValue = a[0]; // There is no QMetaType::assign(), but we can still avoid re-allocating the storage. // No reallocation is a hard requirement in case the instance is not heap-allocated. QMetaType::destruct(mt, returnValue); QMetaType::construct(mt, returnValue, overrider->m_overrideValue.constData()); break; } case Compare: *ret = false; // not implemented break; case NumOperations: ; } } public: QtIviPropertyOverrider *m_overrider; explicit OverrideValueGetter(QtIviPropertyOverrider *overrider) : QSlotObjectBase(&impl), m_overrider(overrider) {} }; } QtIviPropertyOverrider::QtIviPropertyOverrider(QIviProperty *overriddenProperty) : m_prop(overriddenProperty) { } QtIviPropertyOverrider::QtIviPropertyOverrider(QtIviPropertyOverrider &&other) { *this = std::move(other); } QtIviPropertyOverrider &QtIviPropertyOverrider::operator=(QtIviPropertyOverrider &&other) { m_prop = other.m_prop; m_overrideValue = other.m_overrideValue; m_originalValueGetter = other.m_originalValueGetter; m_originalValueSetter = other.m_originalValueSetter; if (isOverride()) { // fix up the backlink QIviPropertyPrivate *pPriv = QIviPropertyPrivate::get(m_prop); static_cast(pPriv->m_valueGetter)->m_overrider = this; } return *this; } QtIviPropertyOverrider::~QtIviPropertyOverrider() { if (m_prop) { setOverride(false); } } void QtIviPropertyOverrider::setValue(const QVariant &value) { if (isOverride()) { m_overrideValue = value; } else { if (!userWritable()) { qWarning("QtIviPropertyOverrider::setValue(): cannot set value on " "non-writable non overridden property."); return; } m_prop->setValue(value); } } void QtIviPropertyOverrider::setOverride(bool doOverride) { Q_ASSERT(m_prop); QIviPropertyPrivate *const pPriv = QIviPropertyPrivate::get(m_prop); const bool wasOverride = isOverride(); if (!doOverride && wasOverride) { pPriv->m_valueGetter->destroyIfLastRef(); pPriv->m_valueGetter = m_originalValueGetter; m_originalValueGetter = nullptr; if (pPriv->m_valueSetter) { // if it was nullptr, we left it nullptr // restore stashed value from the fake setter we injected const QVariant value = static_cast(pPriv->m_valueSetter)->m_stashedValue; pPriv->m_valueSetter->destroyIfLastRef(); pPriv->m_valueSetter = m_originalValueSetter; m_originalValueSetter = nullptr; m_prop->setValue(value); } } else if (doOverride && !wasOverride) { const QVariant value = m_prop->value(); m_originalValueGetter = pPriv->m_valueGetter; pPriv->m_valueGetter = new OverrideValueGetter(this); if (pPriv->m_valueSetter) { // if it is nullptr (-> read-only property), leave it nullptr m_originalValueSetter = pPriv->m_valueSetter; pPriv->m_valueSetter = new OverrideValueSetter(value); } } Q_ASSERT(isOverride() == doOverride); } bool QtIviPropertyOverrider::isOverride() const { return m_originalValueGetter; } bool QtIviPropertyOverrider::userWritable() const { return QIviPropertyPrivate::get(m_prop)->m_valueSetter; } QVariant QtIviPropertyOverrider::cppValue() const { // QIviProperty::value() unfortunately "sanitizes" the type to avoid confusing the QML engine with // C++ types like enums and flags. We prefer the real types so we can show suitable display and // editing delegates for them. So reimplement QIviProperty[Factory]::value() without the type munging. QIviPropertyPrivate *const pPriv = QIviPropertyPrivate::get(m_prop); // use quint64 to get a healthy alignment - hopefully it is enough! QVarLengthArray storage; const int typeSize = QMetaType::sizeOf(pPriv->m_type); storage.resize((typeSize + sizeof(quint64) - 1) / sizeof(quint64)); void *const rawStorage = reinterpret_cast(storage.data()); void *args[] = { rawStorage, nullptr }; pPriv->m_valueGetter->call(m_prop->parent(), args); return QVariant(pPriv->m_type, rawStorage); } gammaray-2.11.3/plugins/qtivi/qtivipropertyoverrider.h000066400000000000000000000041321413310443000232360ustar00rootroot00000000000000/* qtivipropertyoverrider.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_QTIVIPROPERTYOVERRIDER_H #define GAMMARAY_QTIVIPROPERTYOVERRIDER_H #include #include #include class QIviProperty; namespace GammaRay { class QtIviPropertyOverrider { public: QtIviPropertyOverrider() = default; explicit QtIviPropertyOverrider(QIviProperty *property); explicit QtIviPropertyOverrider(QtIviPropertyOverrider &&other); QtIviPropertyOverrider &operator=(QtIviPropertyOverrider &&other); ~QtIviPropertyOverrider(); void setValue(const QVariant &value); bool userWritable() const; void setOverride(bool); bool isOverride() const; QVariant cppValue() const; private: Q_DISABLE_COPY(QtIviPropertyOverrider) friend class OverrideValueSetter; friend class OverrideValueGetter; QPointer m_prop; QtPrivate::QSlotObjectBase *m_originalValueGetter = nullptr; QtPrivate::QSlotObjectBase *m_originalValueSetter = nullptr; QVariant m_overrideValue; }; } #endif // GAMMARAY_QTIVIPROPERTYOVERRIDER_H gammaray-2.11.3/plugins/qtivi/qtivisupport.cpp000066400000000000000000000047161413310443000215070ustar00rootroot00000000000000/* qtivisupport.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "qtivisupport.h" #ifdef QTIVI_DISABLE_IVIPROPERTIES #include "qtiviobjectmodel.h" #else #include "qtivipropertymodel.h" #endif #include #include #include #include #include #include #include #include using namespace GammaRay; static void registerMetaTypes() { qRegisterMetaType(); MetaObject *mo = nullptr; MO_ADD_METAOBJECT0(QIviServiceInterface); MO_ADD_PROPERTY_RO(QIviServiceInterface, interfaces); MO_ADD_METAOBJECT2(QIviServiceObject, QObject, QIviServiceInterface); MO_ADD_METAOBJECT1(QIviAbstractFeature, QObject); MO_ADD_PROPERTY_RO(QIviAbstractFeature, error); MO_ADD_METAOBJECT1(QIviZonedFeatureInterface, QObject); MO_ADD_PROPERTY_RO(QIviZonedFeatureInterface, availableZones); } QtIviSupport::QtIviSupport(Probe* probe, QObject* parent) : QObject(parent) { Q_UNUSED(probe); registerMetaTypes(); #ifdef QTIVI_DISABLE_IVIPROPERTIES auto propertyModel = new QtIviObjectModel(Probe::instance()); #else auto propertyModel = new QtIviPropertyModel(Probe::instance()); #endif probe->registerModel(QStringLiteral("com.kdab.GammaRay.PropertyModel"), propertyModel); //m_selectionModel = ObjectBroker::selectionModel(filterModel); } gammaray-2.11.3/plugins/qtivi/qtivisupport.h000066400000000000000000000032761413310443000211540ustar00rootroot00000000000000/* qtivisupport.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_QTIVISUPPORT_H #define GAMMARAY_QTIVISUPPORT_H #include #include namespace GammaRay { class QtIviSupport : public QObject { Q_OBJECT public: explicit QtIviSupport(Probe *probe, QObject *parent = nullptr); }; class QtIviSupportFactory : public QObject, public StandardToolFactory { Q_OBJECT Q_INTERFACES(GammaRay::ToolFactory) Q_PLUGIN_METADATA(IID "com.kdab.GammaRay.ToolFactory" FILE "gammaray_qtivi.json") public: explicit QtIviSupportFactory(QObject *parent = nullptr) : QObject(parent) { } }; } #endif gammaray-2.11.3/plugins/qtivi/qtivisupportwidget.cpp000066400000000000000000000101701413310443000227020ustar00rootroot00000000000000/* qtivisupportwidget.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "qtivisupportwidget.h" #include "qtivipropertyclientmodel.h" #include "qtiviconstrainedvaluedelegate.h" #include #include #include #include #include #include #include #include #include #include #include using namespace GammaRay; QtIVIWidget::QtIVIWidget(QWidget *parent) : QWidget(parent) , m_stateManager(this) { setObjectName("QtIVIWidget"); QAbstractItemModel *propertyModel = ObjectBroker::model(QStringLiteral("com.kdab.GammaRay.PropertyModel")); auto *clientModel = new QtIviPropertyClientModel(this); clientModel->setSourceModel(propertyModel); QItemSelectionModel *selectionModel = ObjectBroker::selectionModel(clientModel); connect(propertyModel, &QAbstractItemModel::destroyed, this, [clientModel]() { clientModel->setSourceModel(nullptr); }); auto *vbox = new QVBoxLayout(this); auto *searchLineEdit = new QLineEdit(this); vbox->addWidget(searchLineEdit); new SearchLineController(searchLineEdit, clientModel); m_objectTreeView = new DeferredTreeView(this); m_objectTreeView->header()->setObjectName("objectTreeViewHeader"); m_objectTreeView->setDeferredResizeMode(0, QHeaderView::ResizeToContents); m_objectTreeView->setDeferredResizeMode(1, QHeaderView::Interactive); m_objectTreeView->setDeferredResizeMode(2, QHeaderView::ResizeToContents); m_objectTreeView->setDeferredResizeMode(3, QHeaderView::ResizeToContents); m_objectTreeView->setDeferredResizeMode(4, QHeaderView::ResizeToContents); m_objectTreeView->setExpandNewContent(true); vbox->addWidget(m_objectTreeView); m_objectTreeView->setSortingEnabled(true); m_objectTreeView->setModel(clientModel); m_objectTreeView->setItemDelegateForColumn(1, new QtIviConstrainedValueDelegate(this)); m_objectTreeView->setSelectionModel(selectionModel); connect(selectionModel, &QItemSelectionModel::selectionChanged, this, &QtIVIWidget::objectSelected); setContextMenuPolicy(Qt::CustomContextMenu); m_objectTreeView->setContextMenuPolicy(Qt::CustomContextMenu); connect(m_objectTreeView, &QWidget::customContextMenuRequested, this, &QtIVIWidget::contextMenu); } void QtIVIWidget::objectSelected(const QItemSelection &selection) { QModelIndex index; if (!selection.isEmpty()) index = selection.first().topLeft(); if (index.isValid()) { m_objectTreeView->scrollTo(index); } } void QtIVIWidget::contextMenu(QPoint pos) { QModelIndex index = m_objectTreeView->indexAt(pos); if (!index.isValid()) return; index = index.sibling(index.row(), 0); const ObjectId objectId = index.data(ObjectModel::ObjectIdRole).value(); if (objectId.isNull()) return; QMenu menu; ContextMenuExtension ext(objectId); ext.populateMenu(&menu); menu.exec(m_objectTreeView->viewport()->mapToGlobal(pos)); } gammaray-2.11.3/plugins/qtivi/qtivisupportwidget.h000066400000000000000000000036271413310443000223600ustar00rootroot00000000000000/* qtivisupportwidget.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_QTIVISUPPORTWIDGET_H #define GAMMARAY_QTIVISUPPORTWIDGET_H #include #include #include QT_BEGIN_NAMESPACE class QModelIndex; class QTreeView; class QItemSelection; QT_END_NAMESPACE namespace GammaRay { class DeferredTreeView; class QtIVIWidget : public QWidget { Q_OBJECT public: explicit QtIVIWidget(QWidget *parent = nullptr); private slots: void objectSelected(const QItemSelection &selection); void contextMenu(QPoint pos); private: DeferredTreeView *m_objectTreeView; UIStateManager m_stateManager; }; class QtIVIUiFactory : public QObject, public StandardToolUiFactory { Q_OBJECT Q_INTERFACES(GammaRay::ToolUiFactory) Q_PLUGIN_METADATA(IID "com.kdab.GammaRay.ToolUiFactory" FILE "gammaray_qtivi.json") }; } #endif gammaray-2.11.3/plugins/quickinspector/000077500000000000000000000000001413310443000201115ustar00rootroot00000000000000gammaray-2.11.3/plugins/quickinspector/CMakeLists.txt000066400000000000000000000067301413310443000226570ustar00rootroot00000000000000if(Qt5Quick_FOUND) # shared stuff set(gammaray_quickinspector_shared_srcs quickinspectorinterface.cpp quickitemgeometry.cpp quickdecorationsdrawer.cpp materialextension/materialextensioninterface.cpp ) add_library(gammaray_quickinspector_shared STATIC ${gammaray_quickinspector_shared_srcs}) target_link_libraries(gammaray_quickinspector_shared gammaray_common Qt5::Gui Qt5::Quick) set_target_properties(gammaray_quickinspector_shared PROPERTIES POSITION_INDEPENDENT_CODE ON) target_compile_features(gammaray_quickinspector_shared PUBLIC ${GAMMARAY_REQUIRED_CXX_FEATURES}) if(NOT GAMMARAY_CLIENT_ONLY_BUILD) set(gammaray_quickinspector_srcs quickinspector.cpp quickanchorspropertyadaptor.cpp quickitemmodel.cpp quickscenegraphmodel.cpp quickpaintanalyzerextension.cpp quickscreengrabber.cpp ) if(NOT QT_NO_OPENGL) list(APPEND gammaray_quickinspector_srcs materialextension/materialextension.cpp materialextension/materialshadermodel.cpp materialextension/qquickopenglshadereffectmaterialadaptor.cpp geometryextension/sggeometryextension.cpp geometryextension/sggeometrymodel.cpp textureextension/textureextension.cpp textureextension/qsgtexturegrabber.cpp) endif() gammaray_add_plugin(gammaray_quickinspector JSON gammaray_quickinspector.json SOURCES ${gammaray_quickinspector_srcs} ) if(NOT Qt5Quick_VERSION VERSION_LESS 5.7) target_sources(gammaray_quickinspector PUBLIC ${CMAKE_CURRENT_LIST_DIR}/quickimplicitbindingdependencyprovider.cpp) endif() target_include_directories(gammaray_quickinspector SYSTEM PRIVATE ${Qt5Quick_PRIVATE_INCLUDE_DIRS}) target_link_libraries(gammaray_quickinspector gammaray_quickinspector_shared gammaray_core Qt5::Quick gammaray_kitemmodels ) endif() if(TARGET Qt5::Gui_GLESv2) include(CheckCXXSourceCompiles) set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} Qt5::Gui_GLESv2) check_cxx_source_compiles("int main() {return 0;}" CAN_LINK_GUI_GLESV2) if(CAN_LINK_GUI_GLESV2) set(HAVE_GLESv2 TRUE) target_link_libraries(gammaray_quickinspector Qt5::Gui_GLESv2) endif() endif() #the Qt5::Gui_GLESv2 target is not set on Android (up to qt5.13, at least) if(ANDROID AND NOT TARGET Qt5::Gui_GLESv2) target_link_libraries(gammaray_quickinspector GLESv2) endif() if(GAMMARAY_BUILD_UI) # ui plugin set(gammaray_quickinspector_ui_srcs quickinspectorwidget.cpp quickinspectorclient.cpp quickclientitemmodel.cpp quickitemdelegate.cpp quickitemtreewatcher.cpp quickscenepreviewwidget.cpp quickscenecontrolwidget.cpp quickoverlaylegend.cpp gridsettingswidget.cpp materialextension/materialextensionclient.cpp materialextension/materialtab.cpp textureextension/texturetab.cpp textureextension/textureviewwidget.cpp textureextension/resources.qrc ) if(NOT QT_NO_OPENGL) list(APPEND gammaray_quickinspector_ui_srcs geometryextension/sggeometrytab.cpp geometryextension/sgwireframewidget.cpp ) endif() gammaray_add_plugin(gammaray_quickinspector_ui JSON gammaray_quickinspector.json SOURCES ${gammaray_quickinspector_ui_srcs} ) target_link_libraries(gammaray_quickinspector_ui gammaray_quickinspector_shared gammaray_ui Qt5::Quick ) endif() endif() gammaray-2.11.3/plugins/quickinspector/gammaray_quickinspector.json000066400000000000000000000004321413310443000257240ustar00rootroot00000000000000{ "id": "gammaray_quickinspector", "name": "Quick Scenes", "name[de]": "Quick Szenen", "selectableTypes": [ "QQuickItem", "QSGNode", "QQuickWindow" ], "types": [ "QQuickWindow", "QQuickItem", "QSGNode" ] } gammaray-2.11.3/plugins/quickinspector/geometryextension/000077500000000000000000000000001413310443000237015ustar00rootroot00000000000000gammaray-2.11.3/plugins/quickinspector/geometryextension/sggeometryextension.cpp000066400000000000000000000045651413310443000305410ustar00rootroot00000000000000/* sggeometryextension.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "sggeometryextension.h" #include "sggeometrymodel.h" #include #include #include #include #include using namespace GammaRay; SGGeometryExtension::SGGeometryExtension(PropertyController *controller) : PropertyControllerExtension(controller->objectBaseName() + ".sgGeometry") , m_node(nullptr) , m_vertexModel(new SGVertexModel(controller)) , m_adjacencyModel(new SGAdjacencyModel(controller)) { controller->registerModel(m_vertexModel, QStringLiteral("sgGeometryVertexModel")); controller->registerModel(m_adjacencyModel, QStringLiteral("sgGeometryAdjacencyModel")); } SGGeometryExtension::~SGGeometryExtension() = default; bool SGGeometryExtension::setObject(void *object, const QString &typeName) { if (typeName == QStringLiteral("QSGGeometryNode")) { m_node = static_cast(object); auto geometry = m_node->geometry(); // the QSG software renderer puts 0x1 into geometry, so consider that as no geometry too if (Util::isNullish(geometry)) return false; m_vertexModel->setNode(m_node); m_adjacencyModel->setNode(m_node); return true; } return false; } gammaray-2.11.3/plugins/quickinspector/geometryextension/sggeometryextension.h000066400000000000000000000034651413310443000302040ustar00rootroot00000000000000/* sggeometryextension.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_QUICKINSPECTOR_SGGEOMETRYEXTENSION_H #define GAMMARAY_QUICKINSPECTOR_SGGEOMETRYEXTENSION_H #include QT_BEGIN_NAMESPACE class QSGGeometryNode; QT_END_NAMESPACE namespace GammaRay { class PropertyController; class SGVertexModel; class SGAdjacencyModel; class SGGeometryExtension : public PropertyControllerExtension { public: explicit SGGeometryExtension(PropertyController *controller); ~SGGeometryExtension(); bool setObject(void *object, const QString &typeName) override; private: QSGGeometryNode *m_node; SGVertexModel *m_vertexModel; SGAdjacencyModel *m_adjacencyModel; }; } #endif // SGGEOMETRYEXTENSION_H gammaray-2.11.3/plugins/quickinspector/geometryextension/sggeometrymodel.cpp000066400000000000000000000252221413310443000276160ustar00rootroot00000000000000/* sggeometrymodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "sggeometrymodel.h" #include #include #include #include #include #include using namespace GammaRay; Q_DECLARE_METATYPE(QVector) Q_DECLARE_METATYPE(QVector) Q_DECLARE_METATYPE(QVector) Q_DECLARE_METATYPE(QVector) Q_DECLARE_METATYPE(QVector) Q_DECLARE_METATYPE(QVector) template static QStringList toStringList(void *data, int size) { QStringList list; T *typedData = static_cast(data); for (int i = 0; i < size; i++) { list << QString::number(*typedData); ++typedData; } return list; } template static QVariantList toVariantList(void *data, int size) { QVariantList list; T *typedData = static_cast(data); for (int i = 0; i < size; i++) { list << QVariant::fromValue(*typedData); ++typedData; } return list; } GammaRay::SGVertexModel::SGVertexModel(QObject *parent) : QAbstractTableModel(parent) , m_geometry(nullptr) , m_node(nullptr) { } int SGVertexModel::rowCount(const QModelIndex &parent) const { if (!m_geometry || parent.isValid()) return 0; return m_geometry->vertexCount(); } int GammaRay::SGVertexModel::columnCount(const QModelIndex &parent) const { if (!m_geometry || parent.isValid()) return 0; return m_geometry->attributeCount(); } QVariant SGVertexModel::data(const QModelIndex &index, int role) const { if (!index.isValid() || !m_geometry || !index.internalPointer() || index.row() >= m_geometry->vertexCount() || index.column() >= m_geometry->attributeCount()) return QVariant(); if (role == Qt::DisplayRole) { const QSGGeometry::Attribute *attrInfo = m_geometry->attributes(); attrInfo += index.column(); switch (attrInfo->type) { case GL_BYTE: return toStringList(index.internalPointer(), attrInfo->tupleSize).join(QStringLiteral( ", ")); case GL_UNSIGNED_BYTE: return toStringList(index.internalPointer(), attrInfo->tupleSize).join(QStringLiteral( ", ")); case GL_UNSIGNED_SHORT: return toStringList(index.internalPointer(), attrInfo->tupleSize).join(QStringLiteral( ", ")); case GL_SHORT: return toStringList(index.internalPointer(), attrInfo->tupleSize).join(QStringLiteral( ", ")); case GL_INT: return toStringList(index.internalPointer(), attrInfo->tupleSize).join(QStringLiteral( ", ")); case GL_UNSIGNED_INT: return toStringList(index.internalPointer(), attrInfo->tupleSize).join(QStringLiteral( ", ")); case GL_FLOAT: return toStringList(index.internalPointer(), attrInfo->tupleSize).join(QStringLiteral( ", ")); #if defined(GL_DOUBLE) && GL_DOUBLE != GL_FLOAT case GL_DOUBLE: return toStringList(index.internalPointer(), attrInfo->tupleSize).join(QStringLiteral( ", ")); #endif #ifndef QT_OPENGL_ES_2 case GL_2_BYTES: return "2Bytes"; case GL_3_BYTES: return "3Bytes"; case GL_4_BYTES: return "4Bytes"; #endif default: return QStringLiteral("Unknown %1 byte data: 0x"). arg(attrInfo->tupleSize). append(QByteArray((char *)index.internalPointer(), attrInfo->tupleSize). toHex()); } } else if (role == IsCoordinateRole) { const QSGGeometry::Attribute *attrInfo = m_geometry->attributes(); attrInfo += index.column(); return (bool)attrInfo->isVertexCoordinate; } else if (role == RenderRole) { const QSGGeometry::Attribute *attrInfo = m_geometry->attributes(); attrInfo += index.column(); switch (attrInfo->type) { case GL_BYTE: return toVariantList(index.internalPointer(), attrInfo->tupleSize); case GL_UNSIGNED_BYTE: return toVariantList(index.internalPointer(), attrInfo->tupleSize); case GL_UNSIGNED_SHORT: return toVariantList(index.internalPointer(), attrInfo->tupleSize); case GL_SHORT: return toVariantList(index.internalPointer(), attrInfo->tupleSize); case GL_INT: return toVariantList(index.internalPointer(), attrInfo->tupleSize); case GL_UNSIGNED_INT: return toVariantList(index.internalPointer(), attrInfo->tupleSize); case GL_FLOAT: return toVariantList(index.internalPointer(), attrInfo->tupleSize); #if defined(GL_DOUBLE) && GL_DOUBLE != GL_FLOAT case GL_DOUBLE: return toVariantList(index.internalPointer(), attrInfo->tupleSize); #endif default: return QVariant(); } } return QVariant(); } QMap< int, QVariant > SGVertexModel::itemData(const QModelIndex &index) const { QMap map = QAbstractItemModel::itemData(index); map.insert(IsCoordinateRole, data(index, IsCoordinateRole)); map.insert(RenderRole, data(index, RenderRole)); return map; } QVariant SGVertexModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role == Qt::DisplayRole && orientation == Qt::Horizontal && m_geometry) { char const * const *attributeNames = m_node->material()->createShader()->attributeNames(); for (int i = 0; i <= section; i++) { if (!attributeNames[i]) break; if (i == section) return attributeNames[section]; } } return QAbstractItemModel::headerData(section, orientation, role); } void SGVertexModel::setNode(QSGGeometryNode *node) { beginResetModel(); m_node = node; m_geometry = node->geometry(); endResetModel(); } QModelIndex GammaRay::SGVertexModel::index(int row, int column, const QModelIndex &parent) const { if (!m_geometry || row >= m_geometry->vertexCount() || column >= m_geometry->attributeCount() || parent.isValid()) return {}; char *attr = static_cast(m_geometry->vertexData()); attr += m_geometry->sizeOfVertex() * row; const QSGGeometry::Attribute *attrInfo = m_geometry->attributes(); int tupleItemSize = 0; switch (attrInfo->type) { case GL_BYTE: tupleItemSize = sizeof(char); break; case GL_UNSIGNED_BYTE: tupleItemSize = sizeof(unsigned char); break; case GL_UNSIGNED_SHORT: tupleItemSize = sizeof(unsigned short); break; case GL_SHORT: tupleItemSize = sizeof(short); break; case GL_INT: tupleItemSize = sizeof(int); break; case GL_UNSIGNED_INT: tupleItemSize = sizeof(unsigned int); break; case GL_FLOAT: tupleItemSize = sizeof(float); break; #if defined(GL_DOUBLE) && GL_DOUBLE != GL_FLOAT case GL_DOUBLE: tupleItemSize = sizeof(double); break; #endif default: return createIndex(row, column); } for (int i = 0; i < column; i++) { attr += tupleItemSize * attrInfo->tupleSize; attrInfo++; } return createIndex(row, column, attr); } GammaRay::SGAdjacencyModel::SGAdjacencyModel(QObject *parent) : QAbstractListModel(parent) , m_geometry(nullptr) , m_node(nullptr) { } int SGAdjacencyModel::rowCount(const QModelIndex &parent) const { if (!m_geometry || parent.isValid()) return 0; return m_geometry->indexCount(); } QVariant SGAdjacencyModel::data(const QModelIndex &index, int role) const { if (!index.isValid() || !m_geometry || index.row() >= m_geometry->indexCount() || index.column() != 0) return QVariant(); if (role == DrawingModeRole) { return m_geometry->drawingMode(); } if (role == RenderRole) { int indexType = m_geometry->indexType(); if (indexType == GL_UNSIGNED_INT) return *(static_cast(m_geometry->indexData()) + index.row()); else if (indexType == GL_UNSIGNED_SHORT) return *(static_cast(m_geometry->indexData()) + index.row()); else if (indexType == GL_UNSIGNED_BYTE) return *(static_cast(m_geometry->indexData()) + index.row()); } return QVariant(); } void SGAdjacencyModel::setNode(QSGGeometryNode *node) { beginResetModel(); m_node = node; m_geometry = node->geometry(); endResetModel(); } QMap< int, QVariant > SGAdjacencyModel::itemData(const QModelIndex &index) const { QMap map = QAbstractItemModel::itemData(index); map.insert(DrawingModeRole, data(index, DrawingModeRole)); map.insert(RenderRole, data(index, RenderRole)); return map; } gammaray-2.11.3/plugins/quickinspector/geometryextension/sggeometrymodel.h000066400000000000000000000055261413310443000272700ustar00rootroot00000000000000/* sggeometrymodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_QUICKINSPECTOR_SGGEOMETRYMODEL_H #define GAMMARAY_QUICKINSPECTOR_SGGEOMETRYMODEL_H #include #include QT_BEGIN_NAMESPACE class QSGGeometryNode; class QSGGeometry; QT_END_NAMESPACE namespace GammaRay { class SGVertexModel : public QAbstractTableModel { Q_OBJECT public: enum Role { IsCoordinateRole = 257, RenderRole = 258 }; explicit SGVertexModel (QObject *parent = nullptr); int rowCount(const QModelIndex &parent = QModelIndex()) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; QMap itemData(const QModelIndex &index) const override; QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; void setNode(QSGGeometryNode *node); private: QSGGeometry *m_geometry; QSGGeometryNode *m_node; }; class SGAdjacencyModel : public QAbstractListModel { Q_OBJECT public: enum Role { DrawingModeRole = 257, RenderRole = 258 }; explicit SGAdjacencyModel (QObject *parent = nullptr); int rowCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; QMap itemData(const QModelIndex &index) const override; void setNode(QSGGeometryNode *node); private: QSGGeometry *m_geometry; QSGGeometryNode *m_node; }; } #endif // GAMMARAY_SGGEOMETRYMODEL_H gammaray-2.11.3/plugins/quickinspector/geometryextension/sggeometrytab.cpp000066400000000000000000000046701413310443000272700ustar00rootroot00000000000000/* sggeometrytab.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "sggeometrytab.h" #include "common/objectbroker.h" #include "ui/propertywidget.h" #include "ui_sggeometrytab.h" #include using namespace GammaRay; SGGeometryTab::SGGeometryTab(PropertyWidget *parent) : QWidget(parent) , m_ui(new Ui_SGGeometryTab) { m_ui->setupUi(this); const QString viewName = m_ui->tableView->objectName(); m_ui->tableView->horizontalHeader()->setObjectName(QString::fromLatin1("%1Horizontal").arg(viewName)); m_ui->tableView->verticalHeader()->setObjectName(QString::fromLatin1("%1Vertical").arg(viewName)); setObjectBaseName(parent->objectBaseName()); } SGGeometryTab::~SGGeometryTab() = default; void SGGeometryTab::setObjectBaseName(const QString &baseName) { m_vertexModel = ObjectBroker::model(baseName + '.' + "sgGeometryVertexModel"); m_adjacencyModel = ObjectBroker::model(baseName + '.' + "sgGeometryAdjacencyModel"); auto *proxy = new QSortFilterProxyModel(this); proxy->setDynamicSortFilter(true); proxy->setSourceModel(m_vertexModel); m_ui->tableView->setModel(proxy); auto *selectionModel = new QItemSelectionModel(proxy); m_ui->tableView->setSelectionModel(selectionModel); m_ui->wireframeWidget->setModel(m_vertexModel, m_adjacencyModel); m_ui->wireframeWidget->setHighlightModel(selectionModel); } gammaray-2.11.3/plugins/quickinspector/geometryextension/sggeometrytab.h000066400000000000000000000034161413310443000267320ustar00rootroot00000000000000/* sggeometrytab.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_QUICKINSPECTOR_SGGEOMETRYTAB_H #define GAMMARAY_QUICKINSPECTOR_SGGEOMETRYTAB_H #include #include #include QT_BEGIN_NAMESPACE class QAbstractItemModel; QT_END_NAMESPACE namespace GammaRay { class Ui_SGGeometryTab; class PropertyWidget; class SGGeometryTab : public QWidget { Q_OBJECT public: explicit SGGeometryTab(PropertyWidget *parent); ~SGGeometryTab() override; private: void setObjectBaseName(const QString &baseName); private: std::unique_ptr m_ui; QAbstractItemModel *m_vertexModel; QAbstractItemModel *m_adjacencyModel; }; } #endif // SGGEOMETRYTAB_H gammaray-2.11.3/plugins/quickinspector/geometryextension/sggeometrytab.ui000066400000000000000000000036251413310443000271220ustar00rootroot00000000000000 GammaRay::SGGeometryTab 0 0 561 362 Qt::Horizontal Raw Vertex Data Preview 0 0 SGWireframeWidget QWidget
    plugins/quickinspector/geometryextension/sgwireframewidget.h
    1
    gammaray-2.11.3/plugins/quickinspector/geometryextension/sgwireframewidget.cpp000066400000000000000000000353121413310443000301300ustar00rootroot00000000000000/* sgwireframewidget.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "sgwireframewidget.h" #include "sggeometrymodel.h" #include #include #include #include using namespace GammaRay; SGWireframeWidget::SGWireframeWidget(QWidget *parent, Qt::WindowFlags f) : QWidget(parent, f) , m_vertexModel(nullptr) , m_adjacencyModel(nullptr) , m_highlightModel(nullptr) , m_positionColumn(-1) , m_drawingMode(0) , m_geometryWidth(0) , m_geometryHeight(0) , m_zoom(1) , m_offset(10, 10) { } SGWireframeWidget::~SGWireframeWidget() = default; void SGWireframeWidget::paintEvent(QPaintEvent *) { if (!m_vertexModel || m_vertices.isEmpty() || m_positionColumn == -1) return; // Prepare painting m_zoom = qMin((width() - 20) / m_geometryWidth, (height() - 20) / m_geometryHeight); QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); painter.setPen(qApp->palette().color(QPalette::WindowText)); painter.setBrush(QBrush(Qt::black, Qt::SolidPattern)); // Paint for (int i = 0; i < m_adjacencyList.size(); i++) { // Calculate the index of the vertex we're supposed to draw a line from const int index = m_adjacencyList[i]; if (index >= m_vertices.size()) continue; // Draw highlighted faces if ((m_drawingMode == GL_TRIANGLES && i % 3 == 2) || (m_drawingMode == GL_TRIANGLE_STRIP && i >= 2)) { drawHighlightedFace(&painter, QVector() << index << m_adjacencyList[i - 1] << m_adjacencyList[i - 2]); } else if (m_drawingMode == GL_TRIANGLE_FAN && i >= 2) { drawHighlightedFace(&painter, QVector() << index << m_adjacencyList[i - 1] << m_adjacencyList.first()); } #ifndef QT_OPENGL_ES_2 else if ((m_drawingMode == GL_QUADS || m_drawingMode == GL_QUAD_STRIP) && i % 4 == 3) { drawHighlightedFace(&painter, QVector() << index << m_adjacencyList[i - 1] << m_adjacencyList[i - 2] << m_adjacencyList[i - 3]); } else if (m_drawingMode == GL_POLYGON && i == m_adjacencyList.size() - 1) { QVector vertices; vertices.reserve(m_adjacencyList.size()); for (int j = 0; j < m_adjacencyList.size(); j++) vertices << j; drawHighlightedFace(&painter, vertices); } #endif // Draw wires // Draw a connection to the previous vertex if (((m_drawingMode == GL_LINES && i % 2) || m_drawingMode == GL_LINE_LOOP || m_drawingMode == GL_LINE_STRIP || (m_drawingMode == GL_TRIANGLES && i % 3) || m_drawingMode == GL_TRIANGLE_STRIP || m_drawingMode == GL_TRIANGLE_FAN #ifndef QT_OPENGL_ES_2 || (m_drawingMode == GL_QUADS && i % 4 != 0) || (m_drawingMode == GL_QUAD_STRIP && i % 2) || m_drawingMode == GL_POLYGON #endif ) && i > 0) { drawWire(&painter, index, m_adjacencyList[i - 1]); } // Draw a connection to the second previous vertex if ((m_drawingMode == GL_TRIANGLE_STRIP || (m_drawingMode == GL_TRIANGLES && i % 3 == 2) #ifndef QT_OPENGL_ES_2 || m_drawingMode == GL_QUAD_STRIP #endif ) && i > 1) { drawWire(&painter, index, m_adjacencyList[i - 2]); } // draw a connection to the third previous vertex #ifndef QT_OPENGL_ES_2 if (m_drawingMode == GL_QUADS && i % 4 == 3) { drawWire(&painter, index, m_adjacencyList[i - 3]); } #endif // Draw a connection to the very first vertex if ((m_drawingMode == GL_LINE_LOOP && i == m_adjacencyList.size() - 1) #ifndef QT_OPENGL_ES_2 || (m_drawingMode == GL_POLYGON && i == m_adjacencyList.size() - 1) #endif || m_drawingMode == GL_TRIANGLE_FAN) drawWire(&painter, index, m_adjacencyList.first()); } // Paint the vertices for (int i = 0; i < m_vertices.size(); ++i) { if (m_highlightedVertices.contains(i)) { painter.save(); // Glow QRadialGradient radialGrad(m_vertices.at(i) * m_zoom + m_offset, 6); radialGrad.setColorAt(0, qApp->palette().color(QPalette::Highlight)); radialGrad.setColorAt(1, Qt::transparent); painter.setBrush(QBrush(radialGrad)); painter.setPen(Qt::NoPen); painter.drawEllipse(m_vertices.at(i) * m_zoom + m_offset, 12, 12); // Highlighted point painter.setBrush(QBrush(qApp->palette().color(QPalette::Highlight))); painter.drawEllipse(m_vertices.at(i) * m_zoom + m_offset, 3, 3); painter.restore(); } else { // Normal unhighlighted point painter.drawEllipse(m_vertices.at(i) * m_zoom + m_offset, 3, 3); } } // Paint hint about which draw mode is used QString drawingMode = m_drawingMode == GL_POINTS ? QStringLiteral("GL_POINTS") : m_drawingMode == GL_LINES ? QStringLiteral("GL_LINES") : m_drawingMode == GL_LINE_STRIP ? QStringLiteral("GL_LINE_STRIP") : m_drawingMode == GL_LINE_LOOP ? QStringLiteral("GL_LINE_LOOP") : #ifndef QT_OPENGL_ES_2 m_drawingMode == GL_POLYGON ? QStringLiteral("GL_POLYGON") : m_drawingMode == GL_QUADS ? QStringLiteral("GL_QUADS") : m_drawingMode == GL_QUAD_STRIP ? QStringLiteral("GL_QUAD_STRIP") : #endif m_drawingMode == GL_TRIANGLES ? QStringLiteral("GL_TRIANGLES") : m_drawingMode == GL_TRIANGLE_STRIP ? QStringLiteral("GL_TRIANGLE_STRIP") : m_drawingMode == GL_TRIANGLE_FAN ? QStringLiteral("GL_TRIANGLE_FAN") : tr("Unknown"); QString text = tr("Drawing mode: %1").arg(drawingMode); painter.drawText(contentsRect().width() - painter.fontMetrics().width(text), contentsRect().height() - painter.fontMetrics().height(), text); } void SGWireframeWidget::drawWire(QPainter *painter, int vertexIndex1, int vertexIndex2) { // Draw wire if (m_highlightedVertices.contains(vertexIndex1) && m_highlightedVertices.contains(vertexIndex2)) { painter->save(); painter->setPen(qApp->palette().color(QPalette::Highlight)); painter->drawLine(m_vertices.at(vertexIndex1) * m_zoom + m_offset, m_vertices.at(vertexIndex2) * m_zoom + m_offset); painter->restore(); } else if (vertexIndex1 != -1 && vertexIndex2 != -1) { painter->drawLine(m_vertices.at(vertexIndex1) * m_zoom + m_offset, m_vertices.at(vertexIndex2) * m_zoom + m_offset); } } void SGWireframeWidget::drawHighlightedFace(QPainter *painter, const QVector &vertexIndices) { QVector vertices; for (int index : vertexIndices) { if (!m_highlightedVertices.contains(index)) return; // There is one vertex that is not highlighted. Don't highlight the face. vertices << m_vertices.at(index) * m_zoom + m_offset; } painter->save(); QColor color = qApp->palette().color(QPalette::Highlight).lighter(); color.setAlphaF(0.8); painter->setBrush(QBrush(color)); painter->setPen(Qt::NoPen); painter->drawPolygon(QPolygonF(vertices)); painter->restore(); } QAbstractItemModel *SGWireframeWidget::model() const { return m_vertexModel; } void SGWireframeWidget::setModel(QAbstractItemModel *vertexModel, QAbstractItemModel *adjacencyModel) { if (m_vertexModel) { disconnect(m_vertexModel, nullptr, this, nullptr); } m_vertexModel = vertexModel; m_vertexModel->rowCount(); connect(m_vertexModel, &QAbstractItemModel::modelReset, this, &SGWireframeWidget::onVertexModelReset); connect(m_vertexModel, &QAbstractItemModel::dataChanged, this, &SGWireframeWidget::onVertexModelDataChanged); connect(m_vertexModel, &QAbstractItemModel::rowsInserted, this, &SGWireframeWidget::onVertexModelRowsInserted); if (m_adjacencyModel) { disconnect(m_adjacencyModel, nullptr, this, nullptr); } m_adjacencyModel = adjacencyModel; m_adjacencyModel->rowCount(); connect(m_adjacencyModel, &QAbstractItemModel::modelReset, this, &SGWireframeWidget::onAdjacencyModelReset); connect(m_adjacencyModel, &QAbstractItemModel::dataChanged, this, &SGWireframeWidget::onAdjacencyModelDataChanged); connect(m_adjacencyModel, &QAbstractItemModel::rowsInserted, this, &SGWireframeWidget::onAdjacencyModelRowsInserted); } void SGWireframeWidget::setHighlightModel(QItemSelectionModel *selectionModel) { if (m_highlightModel) disconnect(m_highlightModel, nullptr, this, nullptr); m_highlightModel = selectionModel; connect(m_highlightModel, &QItemSelectionModel::selectionChanged, this, &SGWireframeWidget::onHighlightDataChanged); } void SGWireframeWidget::onVertexModelReset() { fetchVertices(); update(); } void SGWireframeWidget::onAdjacencyModelReset() { fetchAdjacencyList(); update(); } void SGWireframeWidget::onVertexModelRowsInserted(const QModelIndex &parent, int first, int last) { Q_UNUSED(first); Q_UNUSED(last); if (!parent.isValid()) { fetchVertices(); update(); } } void SGWireframeWidget::onAdjacencyModelRowsInserted(const QModelIndex &parent, int first, int last) { Q_UNUSED(first); Q_UNUSED(last); if (!parent.isValid()) { fetchAdjacencyList(); update(); } } void SGWireframeWidget::onVertexModelDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) { if (!topLeft.isValid() || !bottomRight.isValid() || m_positionColumn == -1 || (topLeft.column() <= m_positionColumn && bottomRight.column() >= m_positionColumn)) { fetchVertices(); update(); } } void SGWireframeWidget::onAdjacencyModelDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) { if (!topLeft.isValid() || !bottomRight.isValid() || m_positionColumn == -1 || (topLeft.column() <= m_positionColumn && bottomRight.column() >= m_positionColumn)) { fetchAdjacencyList(); update(); } } void SGWireframeWidget::fetchVertices() { // Get the column in which the vertex position data is stored in if (m_positionColumn == -1) { for (int j = 0; j < m_vertexModel->columnCount(); j++) { if (m_vertexModel->data(m_vertexModel->index(0, j), SGVertexModel::IsCoordinateRole).toBool()) { m_positionColumn = j; break; } } } // Get all the vertices const int verticesCount = m_vertexModel->rowCount(); m_vertices.clear(); m_vertices.reserve(verticesCount); m_geometryWidth = 0; m_geometryHeight = 0; for (int i = 0; i < verticesCount; i++) { const QModelIndex index = m_vertexModel->index(i, m_positionColumn); const QVariantList data = m_vertexModel->data(index, SGVertexModel::RenderRole).toList(); if (data.isEmpty()) { continue; // The rest of the data will be incomplete as well, // but we need to fetch everything. } if (data.size() >= 2) { const qreal x = data[0].toReal(); const qreal y = data[1].toReal(); m_vertices << QPointF(x, y); if (x > m_geometryWidth) m_geometryWidth = x; if (y > m_geometryHeight) m_geometryHeight = y; } } } void SGWireframeWidget::fetchAdjacencyList() { m_drawingMode = m_adjacencyModel->index(0, 0).data(SGAdjacencyModel::DrawingModeRole).toUInt(); // Get all the wires m_adjacencyList.clear(); for (int i = 0; i < m_adjacencyModel->rowCount(); i++) { const QModelIndex index = m_adjacencyModel->index(i, 0); const QVariant data = m_adjacencyModel->data(index, SGAdjacencyModel::RenderRole); if (!data.isValid()) { continue; // The rest of the data will be incomplete as well, // but we need to fetch everything. } const quint32 value = data.value(); m_adjacencyList << value; } } void SGWireframeWidget::onHighlightDataChanged(const QItemSelection &selected, const QItemSelection &deselected) { foreach (const QModelIndex &index, deselected.indexes()) { m_highlightedVertices.remove(index.row()); } foreach (const QModelIndex &index, selected.indexes()) { if (!m_highlightedVertices.contains(index.row())) m_highlightedVertices << index.row(); } update(); } void SGWireframeWidget::mouseReleaseEvent(QMouseEvent *e) { if (~e->modifiers() & Qt::ControlModifier) m_highlightModel->clear(); for (int i = 0; i < m_vertices.size(); i++) { int distance = QLineF(e->pos(), m_vertices.at(i) * m_zoom + m_offset).length(); if (distance <= 5) { if (e->modifiers() & Qt::ControlModifier) { m_highlightModel->select(m_vertexModel->index(i, m_positionColumn), QItemSelectionModel::Toggle); } else { m_highlightModel->select(m_vertexModel->index(i, m_positionColumn), QItemSelectionModel::Select); } } } QWidget::mouseReleaseEvent(e); } gammaray-2.11.3/plugins/quickinspector/geometryextension/sgwireframewidget.h000066400000000000000000000061461413310443000276000ustar00rootroot00000000000000/* sgwireframewidget.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_QUICKINSPECTOR_SGWIREFRAMEWIDGET_H #define GAMMARAY_QUICKINSPECTOR_SGWIREFRAMEWIDGET_H #include #include #include QT_BEGIN_NAMESPACE class QItemSelection; class QPainter; class QItemSelectionModel; class QAbstractItemModel; class QModelIndex; QT_END_NAMESPACE namespace GammaRay { class SGWireframeWidget : public QWidget { Q_OBJECT public: explicit SGWireframeWidget(QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); ~SGWireframeWidget() override; QAbstractItemModel *model() const; void setModel(QAbstractItemModel *vertexModel, QAbstractItemModel *adjacencyModel); void setHighlightModel(QItemSelectionModel *selectionModel); protected: void paintEvent(QPaintEvent *) override; void mouseReleaseEvent(QMouseEvent *) override; private slots: void onVertexModelReset(); void onAdjacencyModelReset(); void onVertexModelRowsInserted(const QModelIndex &parent, int first, int last); void onAdjacencyModelRowsInserted(const QModelIndex &parent, int first, int last); void onVertexModelDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight); void onAdjacencyModelDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight); void onHighlightDataChanged(const QItemSelection &selected, const QItemSelection &deselected); private: void drawWire(QPainter *painter, int vertexIndex1, int vertexIndex2); void drawHighlightedFace(QPainter *painter, const QVector &vertexIndices); void fetchVertices(); void fetchAdjacencyList(); private: QAbstractItemModel *m_vertexModel; QAbstractItemModel *m_adjacencyModel; QItemSelectionModel *m_highlightModel; int m_positionColumn; GLenum m_drawingMode; QVector m_vertices; QSet m_highlightedVertices; QVector m_adjacencyList; qreal m_geometryWidth; qreal m_geometryHeight; qreal m_zoom; const QPointF m_offset; }; } #endif // SGWIREFRAMEWIDGET_H gammaray-2.11.3/plugins/quickinspector/gridsettingswidget.cpp000066400000000000000000000051511413310443000245310ustar00rootroot00000000000000/* gridsettingswidget.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Filipe Azevedo Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "gridsettingswidget.h" #include "ui_gridsettingswidget.h" #include "quickscreengrabber.h" namespace GammaRay { GridSettingsWidget::GridSettingsWidget(QWidget *parent) : QWidget(parent) , ui(new Ui::GridSettingsWidget) { ui->setupUi(this); connect(ui->gbEnabled, &QGroupBox::clicked, this, &GridSettingsWidget::enabledChanged); connect(ui->sbXOffset, &QAbstractSpinBox::editingFinished, this, &GridSettingsWidget::offsetUserChanged); connect(ui->sbYOffset, &QAbstractSpinBox::editingFinished, this, &GridSettingsWidget::offsetUserChanged); connect(ui->sbCellWidth, &QAbstractSpinBox::editingFinished, this, &GridSettingsWidget::cellSizeUserChanged); connect(ui->sbCellHeight, &QAbstractSpinBox::editingFinished, this, &GridSettingsWidget::cellSizeUserChanged); } GridSettingsWidget::~GridSettingsWidget() { delete ui; } void GridSettingsWidget::setOverlaySettings(const QuickDecorationsSettings &settings) { ui->gbEnabled->setChecked(settings.gridEnabled); ui->sbXOffset->setValue(settings.gridOffset.x()); ui->sbYOffset->setValue(settings.gridOffset.y()); ui->sbCellWidth->setValue(settings.gridCellSize.width()); ui->sbCellHeight->setValue(settings.gridCellSize.height()); } void GridSettingsWidget::offsetUserChanged() { emit offsetChanged(QPoint(ui->sbXOffset->value(), ui->sbYOffset->value())); } void GridSettingsWidget::cellSizeUserChanged() { emit cellSizeChanged(QSize(ui->sbCellWidth->value(), ui->sbCellHeight->value())); } } gammaray-2.11.3/plugins/quickinspector/gridsettingswidget.h000066400000000000000000000036101413310443000241740ustar00rootroot00000000000000/* gridsettingswidget.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Filipe Azevedo Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_QUICKINSPECTOR_GRIDSETTINGSWIDGET_H #define GAMMARAY_QUICKINSPECTOR_GRIDSETTINGSWIDGET_H #include namespace GammaRay { struct QuickDecorationsSettings; namespace Ui { class GridSettingsWidget; } class GridSettingsWidget : public QWidget { Q_OBJECT public: explicit GridSettingsWidget(QWidget *parent = nullptr); ~GridSettingsWidget() override; void setOverlaySettings(const QuickDecorationsSettings &settings); signals: void enabledChanged(bool enabled); void offsetChanged(const QPoint &value); void cellSizeChanged(const QSize &value); private: Ui::GridSettingsWidget *ui; private slots: void offsetUserChanged(); void cellSizeUserChanged(); }; } #endif // GAMMARAY_QUICKINSPECTOR_GRIDSETTINGSWIDGET_H gammaray-2.11.3/plugins/quickinspector/gridsettingswidget.ui000066400000000000000000000052371413310443000243710ustar00rootroot00000000000000 GammaRay::GridSettingsWidget 0 0 199 190 Grid enabled true false X Offset: 0 9999 Y Offset: 0 9999 Cell Width: 0 9999 Cell Height: 0 9999 gammaray-2.11.3/plugins/quickinspector/materialextension/000077500000000000000000000000001413310443000236445ustar00rootroot00000000000000gammaray-2.11.3/plugins/quickinspector/materialextension/materialextension.cpp000066400000000000000000000063501413310443000301070ustar00rootroot00000000000000/* materialextension.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include "materialextension.h" #include "materialshadermodel.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) #include #endif using namespace GammaRay; MaterialExtension::MaterialExtension(PropertyController *controller) : MaterialExtensionInterface(controller->objectBaseName() + ".material", controller) , PropertyControllerExtension(controller->objectBaseName() + ".material") , m_node(nullptr) , m_materialPropertyModel(new AggregatedPropertyModel(this)) , m_shaderModel(new MaterialShaderModel(this)) { controller->registerModel(m_materialPropertyModel, QStringLiteral("materialPropertyModel")); controller->registerModel(m_shaderModel, QStringLiteral("shaderModel")); } MaterialExtension::~MaterialExtension() = default; bool MaterialExtension::setObject(void *object, const QString &typeName) { QSGMaterial *material = nullptr; m_shaderModel->setMaterialShader(nullptr); m_materialShader.reset(); if (typeName == QStringLiteral("QSGGeometryNode")) { m_node = static_cast(object); material = m_node->material(); } // the QSG software renderer puts 0x1 into material, so consider that as no material too if (Util::isNullish(material)) { m_materialPropertyModel->setObject(nullptr); return false; } m_materialPropertyModel->setObject(ObjectInstance(material, "QSGMaterial")); m_materialShader.reset(material->createShader()); m_shaderModel->setMaterialShader(m_materialShader.get()); return true; } void MaterialExtension::getShader(int row) { emit gotShader(m_shaderModel->shaderForRow(row)); } gammaray-2.11.3/plugins/quickinspector/materialextension/materialextension.h000066400000000000000000000042221413310443000275500ustar00rootroot00000000000000/* materialextension.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_QUICKINSPECTOR_MATERIALEXTENSION_H #define GAMMARAY_QUICKINSPECTOR_MATERIALEXTENSION_H #include #include "materialextensioninterface.h" #include QT_BEGIN_NAMESPACE class QSGGeometryNode; class QSGMaterialShader; QT_END_NAMESPACE namespace GammaRay { class AggregatedPropertyModel; class MaterialShaderModel; class PropertyController; class ObjectEnumModel; class MaterialExtension : public MaterialExtensionInterface, public PropertyControllerExtension { Q_OBJECT Q_INTERFACES(GammaRay::MaterialExtensionInterface) public: explicit MaterialExtension(PropertyController *controller); ~MaterialExtension() override; bool setObject(void *object, const QString &typeName) override; public slots: void getShader(int row) override; private: QSGGeometryNode *m_node; AggregatedPropertyModel *m_materialPropertyModel; MaterialShaderModel *m_shaderModel; std::unique_ptr m_materialShader; }; } #endif // MATERIALEXTENSION_H gammaray-2.11.3/plugins/quickinspector/materialextension/materialextensionclient.cpp000066400000000000000000000031161413310443000313030ustar00rootroot00000000000000/* materialextensionclient.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "materialextensionclient.h" #include using namespace GammaRay; MaterialExtensionClient::MaterialExtensionClient(const QString &name, QObject *parent) : MaterialExtensionInterface(name, parent) { } MaterialExtensionClient::~MaterialExtensionClient() = default; void MaterialExtensionClient::getShader(int row) { Endpoint::instance()->invokeObject( name(), "getShader", QVariantList() << QVariant::fromValue(row)); } gammaray-2.11.3/plugins/quickinspector/materialextension/materialextensionclient.h000066400000000000000000000032541413310443000307530ustar00rootroot00000000000000/* materialextensionclient.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_QUICKINSPECTOR_MATERIALEXTENSIONCLIENT_H #define GAMMARAY_QUICKINSPECTOR_MATERIALEXTENSIONCLIENT_H #include "materialextensioninterface.h" namespace GammaRay { class MaterialExtensionClient : public MaterialExtensionInterface { Q_OBJECT Q_INTERFACES(GammaRay::MaterialExtensionInterface) public: explicit MaterialExtensionClient(const QString &name, QObject *parent = nullptr); ~MaterialExtensionClient() override; public slots: void getShader(int row) override; }; } #endif // GAMMARAY_MATERIALEXTENSIONCLIENT_H gammaray-2.11.3/plugins/quickinspector/materialextension/materialextensioninterface.cpp000066400000000000000000000030701413310443000317640ustar00rootroot00000000000000/* methodsextensioninterface.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "materialextensioninterface.h" #include using namespace GammaRay; MaterialExtensionInterface::MaterialExtensionInterface(const QString &name, QObject *parent) : QObject(parent) , m_name(name) { ObjectBroker::registerObject(name, this); } MaterialExtensionInterface::~MaterialExtensionInterface() = default; const QString &MaterialExtensionInterface::name() const { return m_name; } gammaray-2.11.3/plugins/quickinspector/materialextension/materialextensioninterface.h000066400000000000000000000037001413310443000314310ustar00rootroot00000000000000/* materialextensioninterface.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_QUICKINSPECTOR_MATERIALEXTENSIONINTERFACE_H #define GAMMARAY_QUICKINSPECTOR_MATERIALEXTENSIONINTERFACE_H #include namespace GammaRay { /** @brief Client/Server interface of the material viewer. */ class MaterialExtensionInterface : public QObject { Q_OBJECT public: explicit MaterialExtensionInterface(const QString &name, QObject *parent = nullptr); ~MaterialExtensionInterface() override; const QString &name() const; signals: void gotShader(const QString &shaderSource); public slots: virtual void getShader(int row) = 0; private: QString m_name; }; } QT_BEGIN_NAMESPACE Q_DECLARE_INTERFACE(GammaRay::MaterialExtensionInterface, "com.kdab.GammaRay.MaterialExtensionInterface") QT_END_NAMESPACE #endif // GAMMARAY_MATERIALEXTENSIONINTERFACE_H gammaray-2.11.3/plugins/quickinspector/materialextension/materialshadermodel.cpp000066400000000000000000000115471413310443000303660ustar00rootroot00000000000000/* materialshadermodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "materialshadermodel.h" #include #include #include #include using namespace GammaRay; #define S(x) { QOpenGLShader:: x, #x } static const MetaEnum::Value qopengl_shader_type[] = { S(Vertex), S(Fragment), S(Geometry), S(TessellationControl), S(TessellationEvaluation), S(Compute) }; #undef S class SGMaterialShaderThief : public QSGMaterialShader { public: using QSGMaterialShader::vertexShader; using QSGMaterialShader::fragmentShader; const QHash& getShaderSources() { return d_func()->m_sourceFiles; } }; MaterialShaderModel::MaterialShaderModel(QObject* parent) : QAbstractListModel(parent) , m_shader(nullptr) , m_shaderFileCount(0) { } MaterialShaderModel::~MaterialShaderModel() = default; void MaterialShaderModel::setMaterialShader(QSGMaterialShader* shader) { if (m_shader) { beginRemoveRows(QModelIndex(), 0, rowCount() - 1); m_shader = nullptr; endRemoveRows(); } if (shader) { m_shaderFileCount = shaderFileCount(shader); beginInsertRows(QModelIndex(), 0, m_shaderFileCount == 0 ? 1 : m_shaderFileCount - 1); m_shader = shader; endInsertRows(); } } QByteArray MaterialShaderModel::shaderForRow(int row) const { if (row < 0 || row >= rowCount() || !m_shader) return QByteArray(); if (m_shaderFileCount == 0) { switch (row) { case 0: return reinterpret_cast(m_shader)->vertexShader(); case 1: return reinterpret_cast(m_shader)->fragmentShader(); } return QByteArray(); } const auto fileName = shaderFileForRow(row); QFile shaderFile(fileName); if (!shaderFile.open(QIODevice::ReadOnly | QIODevice::Text)) return QByteArray(); return shaderFile.readAll(); } int MaterialShaderModel::rowCount(const QModelIndex& parent) const { if (parent.isValid() || !m_shader) return 0; return m_shaderFileCount == 0 ? 2 : m_shaderFileCount; } QVariant MaterialShaderModel::data(const QModelIndex& index, int role) const { if (!index.isValid() || !m_shader || role != Qt::DisplayRole) return QVariant(); if (m_shaderFileCount > 0) { const auto &files = reinterpret_cast(m_shader)->getShaderSources(); int idx = index.row(); for (auto it = files.begin(); it != files.end(); ++it) { if (idx < it.value().size()) return QString(MetaEnum::flagsToString(it.key(), qopengl_shader_type) + QLatin1String(": ") + it.value().at(idx)); idx -= it.value().size(); } Q_ASSERT(false); } else { return MetaEnum::flagsToString((1 << index.row()), qopengl_shader_type); } return QVariant(); } int MaterialShaderModel::shaderFileCount(QSGMaterialShader *shader) { Q_ASSERT(shader); const auto &files = reinterpret_cast(shader)->getShaderSources(); int fileCount = 0; for (auto it = files.begin(); it != files.end(); ++it) fileCount += it.value().size(); return fileCount; } QString MaterialShaderModel::shaderFileForRow(int row) const { Q_ASSERT(m_shader); Q_ASSERT(m_shaderFileCount > 0); Q_ASSERT(row < m_shaderFileCount); const auto &files = reinterpret_cast(m_shader)->getShaderSources(); for (auto it = files.begin(); it != files.end(); ++it) { if (row < it.value().size()) return it.value().at(row); row -= it.value().size(); } Q_ASSERT(false); return QString(); } gammaray-2.11.3/plugins/quickinspector/materialextension/materialshadermodel.h000066400000000000000000000037211413310443000300260ustar00rootroot00000000000000/* materialshadermodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_MATERIALSHADERMODEL_H #define GAMMARAY_MATERIALSHADERMODEL_H #include QT_BEGIN_NAMESPACE class QSGMaterialShader; QT_END_NAMESPACE namespace GammaRay { class MaterialShaderModel : public QAbstractListModel { Q_OBJECT public: explicit MaterialShaderModel(QObject *parent = nullptr); ~MaterialShaderModel() override; void setMaterialShader(QSGMaterialShader *shader); QByteArray shaderForRow(int row) const; int rowCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; private: static int shaderFileCount(QSGMaterialShader *shader); QString shaderFileForRow(int row) const; QSGMaterialShader *m_shader; int m_shaderFileCount; }; } #endif // GAMMARAY_MATERIALSHADERMODEL_H gammaray-2.11.3/plugins/quickinspector/materialextension/materialtab.cpp000066400000000000000000000076351413310443000266500ustar00rootroot00000000000000/* materialtab.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "materialtab.h" #include "materialextensioninterface.h" #include "ui_materialtab.h" #include #include #include #include #include #include #include #include using namespace GammaRay; MaterialTab::MaterialTab(PropertyWidget *parent) : QWidget(parent) , m_ui(new Ui_MaterialTab) , m_interface(nullptr) { m_ui->setupUi(this); m_ui->materialPropertyView->setItemDelegate(new PropertyEditorDelegate(this)); m_ui->materialPropertyView->header()->setObjectName("materialPropertyViewHeader"); connect(m_ui->materialPropertyView, &QTreeView::customContextMenuRequested, this, &MaterialTab::propertyContextMenu); setObjectBaseName(parent->objectBaseName()); connect(m_ui->shaderList, static_cast(&QComboBox::currentIndexChanged), this, &MaterialTab::shaderSelectionChanged); m_ui->shaderEdit->setSyntaxDefinition(QLatin1String("GLSL")); m_ui->splitter->setStretchFactor(0, 1); m_ui->splitter->setStretchFactor(1, 3); } MaterialTab::~MaterialTab() = default; void MaterialTab::setObjectBaseName(const QString &baseName) { if (m_interface) disconnect(m_interface, nullptr, this, nullptr); m_interface = ObjectBroker::object(baseName + ".material"); connect(m_interface, &MaterialExtensionInterface::gotShader, this, &MaterialTab::showShader); auto clientPropModel = new ClientPropertyModel(this); clientPropModel->setSourceModel(ObjectBroker::model(baseName + ".materialPropertyModel")); m_ui->materialPropertyView->setModel(clientPropModel); m_ui->shaderList->setModel(ObjectBroker::model(baseName + ".shaderModel")); } void MaterialTab::shaderSelectionChanged(int idx) { m_ui->shaderEdit->clear(); if (idx < 0) return; m_interface->getShader(idx); } void MaterialTab::showShader(const QString &shaderSource) { m_ui->shaderEdit->setPlainText(shaderSource); } void MaterialTab::propertyContextMenu(QPoint pos) { const auto idx = m_ui->materialPropertyView->indexAt(pos); if (!idx.isValid()) return; const auto actions = idx.data(PropertyModel::ActionRole).toInt(); const auto objectId = idx.data(PropertyModel::ObjectIdRole).value(); ContextMenuExtension ext(objectId); const bool canShow = (actions == PropertyModel::NavigateTo && !objectId.isNull()) || ext.discoverPropertySourceLocation(ContextMenuExtension::GoTo, idx); if (!canShow) return; QMenu contextMenu; ext.populateMenu(&contextMenu); contextMenu.exec(m_ui->materialPropertyView->viewport()->mapToGlobal(pos)); } gammaray-2.11.3/plugins/quickinspector/materialextension/materialtab.h000066400000000000000000000034471413310443000263120ustar00rootroot00000000000000/* materialtab.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_QUICKINSPECTOR_MATERIALTAB_H #define GAMMARAY_QUICKINSPECTOR_MATERIALTAB_H #include namespace GammaRay { class MaterialExtensionInterface; class Ui_MaterialTab; class PropertyWidget; class MaterialTab : public QWidget { Q_OBJECT public: explicit MaterialTab(PropertyWidget *parent); ~MaterialTab() override; private: void setObjectBaseName(const QString &baseName); void propertyContextMenu(QPoint pos); private slots: void shaderSelectionChanged(int idx); void showShader(const QString &shaderSource); private: QScopedPointer m_ui; MaterialExtensionInterface *m_interface; }; } #endif // MATERIALTAB_H gammaray-2.11.3/plugins/quickinspector/materialextension/materialtab.ui000066400000000000000000000044071413310443000264750ustar00rootroot00000000000000 GammaRay::MaterialTab 0 0 727 544 Qt::Vertical false Qt::CustomContextMenu &Shader: shaderList 0 0 true GammaRay::DeferredTreeView QTreeView
    ui/deferredtreeview.h
    GammaRay::CodeEditor QPlainTextEdit
    ui/codeeditor/codeeditor.h
    gammaray-2.11.3/plugins/quickinspector/materialextension/qquickopenglshadereffectmaterialadaptor.cpp000066400000000000000000000110271413310443000345110ustar00rootroot00000000000000/* qquickopenglshadereffectmaterialadaptor.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "qquickopenglshadereffectmaterialadaptor.h" #include #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) #include Q_DECLARE_METATYPE(QQuickOpenGLShaderEffectMaterial::UniformData) #endif #include using namespace GammaRay; QQuickOpenGLShaderEffectMaterialAdaptor::QQuickOpenGLShaderEffectMaterialAdaptor(QObject *parent) : PropertyAdaptor(parent) { } QQuickOpenGLShaderEffectMaterialAdaptor::~QQuickOpenGLShaderEffectMaterialAdaptor() = default; int QQuickOpenGLShaderEffectMaterialAdaptor::count() const { #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) if (object().type() == ObjectInstance::Object) { return 2; } if (object().type() == ObjectInstance::QtVariant) { return 1; } #endif return 0; } PropertyData QQuickOpenGLShaderEffectMaterialAdaptor::propertyData(int index) const { PropertyData pd; #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) if (object().type() == ObjectInstance::Object) { auto mat = reinterpret_cast(object().object()); switch (index) { case 0: pd.setName(QStringLiteral("Vertex Uniforms")); break; case 1: pd.setName(QStringLiteral("Fragment Uniforms")); break; } pd.setValue(QVariant::fromValue(mat->uniforms[index])); pd.setClassName(QStringLiteral("QQuickOpenGLShaderEffectMaterial")); return pd; } if (object().type() == ObjectInstance::QtVariant) { const auto ud = object().variant().value(); pd.setName(ud.name); pd.setValue(ud.value); pd.setTypeName(ud.value.typeName()); pd.setClassName(QStringLiteral("QQuickOpenGLShaderEffectMaterial::UniformData")); // special values are only filled in dynamically from the owning node, so we won't see those switch (ud.specialType) { case QQuickOpenGLShaderEffectMaterial::UniformData::Opacity: pd.setTypeName(QStringLiteral("double")); pd.setValue(QStringLiteral("")); break; case QQuickOpenGLShaderEffectMaterial::UniformData::Matrix: pd.setTypeName(QStringLiteral("QMatrix4x4")); pd.setValue(QStringLiteral("")); break; default: break; } return pd; } #else Q_UNUSED(index); #endif return pd; } QQuickOpenGLShaderEffectMaterialAdaptorFactory *QQuickOpenGLShaderEffectMaterialAdaptorFactory::s_instance = nullptr; PropertyAdaptor *QQuickOpenGLShaderEffectMaterialAdaptorFactory::create(const ObjectInstance &oi, QObject *parent) const { if (oi.type() == ObjectInstance::Object && oi.typeName() == "QQuickOpenGLShaderEffectMaterial" && oi.object()) return new QQuickOpenGLShaderEffectMaterialAdaptor(parent); if (oi.type() == ObjectInstance::QtVariant && oi.typeName() == "QQuickOpenGLShaderEffectMaterial::UniformData" && oi.variant().isValid()) return new QQuickOpenGLShaderEffectMaterialAdaptor(parent); return nullptr; } QQuickOpenGLShaderEffectMaterialAdaptorFactory *QQuickOpenGLShaderEffectMaterialAdaptorFactory::instance() { if (!s_instance) s_instance = new QQuickOpenGLShaderEffectMaterialAdaptorFactory; return s_instance; } gammaray-2.11.3/plugins/quickinspector/materialextension/qquickopenglshadereffectmaterialadaptor.h000066400000000000000000000041201413310443000341520ustar00rootroot00000000000000/* qquickopenglshadereffectmaterialadaptor.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_QQUICKOPENGLSHADEREFFECTMATERIALADAPTOR_H #define GAMMARAY_QQUICKOPENGLSHADEREFFECTMATERIALADAPTOR_H #include #include namespace GammaRay { class QQuickOpenGLShaderEffectMaterialAdaptor : public PropertyAdaptor { Q_OBJECT public: explicit QQuickOpenGLShaderEffectMaterialAdaptor(QObject *parent = nullptr); ~QQuickOpenGLShaderEffectMaterialAdaptor() override; int count() const override; PropertyData propertyData(int index) const override; }; class QQuickOpenGLShaderEffectMaterialAdaptorFactory : public AbstractPropertyAdaptorFactory { public: PropertyAdaptor *create(const ObjectInstance &oi, QObject *parent = nullptr) const override; static QQuickOpenGLShaderEffectMaterialAdaptorFactory *instance(); private: static QQuickOpenGLShaderEffectMaterialAdaptorFactory *s_instance; }; } #endif // GAMMARAY_QQUICKOPENGLSHADEREFFECTMATERIALADAPTOR_H gammaray-2.11.3/plugins/quickinspector/quickanchorspropertyadaptor.cpp000066400000000000000000000106331413310443000264720ustar00rootroot00000000000000/* quickanchorspropertyadaptor.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "quickanchorspropertyadaptor.h" #include #include #include #include using namespace GammaRay; QuickAnchorsPropertyAdaptor::QuickAnchorsPropertyAdaptor(QObject *parent) : PropertyAdaptor(parent) , m_anchorsPropertyIndex(-1) , m_notifyGuard(false) { } QuickAnchorsPropertyAdaptor::~QuickAnchorsPropertyAdaptor() = default; void QuickAnchorsPropertyAdaptor::doSetObject(const ObjectInstance &oi) { m_anchorsPropertyIndex = -1; auto mo = oi.metaObject(); if (!mo || oi.type() != ObjectInstance::QtObject || !oi.qtObject()) return; int propertyIndex = mo->indexOfProperty("anchors"); if (propertyIndex == -1) return; const auto prop = mo->property(propertyIndex); if (QString::compare(prop.typeName(), "QQuickAnchors*") != 0) return; m_anchorsPropertyIndex = propertyIndex; } int QuickAnchorsPropertyAdaptor::count() const { if (!object().isValid()) return 0; return m_anchorsPropertyIndex == -1 ? 0 : 1; } PropertyData QuickAnchorsPropertyAdaptor::propertyData(int index) const { Q_ASSERT(index == 0); PropertyData data; if (!object().isValid()) return data; m_notifyGuard = true; const auto mo = object().metaObject(); Q_ASSERT(mo); const auto prop = mo->property(m_anchorsPropertyIndex); data.setName(prop.name()); data.setTypeName(prop.typeName()); auto pmo = mo; while (pmo->propertyOffset() > m_anchorsPropertyIndex) pmo = pmo->superClass(); data.setClassName(pmo->className()); data.setValue(QVariant::fromValue(QQuickItemPrivate::get(qobject_cast(object().qtObject()))->_anchors)); PropertyModel::PropertyFlags f(PropertyModel::None); if (prop.isConstant()) f |= PropertyModel::Constant; if (prop.isDesignable(object().qtObject())) f |= PropertyModel::Designable; if (prop.isFinal()) f |= PropertyModel::Final; if (prop.isResettable()) f |= PropertyModel::Resetable; if (prop.isScriptable(object().qtObject())) f |= PropertyModel::Scriptable; if (prop.isStored(object().qtObject())) f |= PropertyModel::Stored; if (prop.isUser(object().qtObject())) f |= PropertyModel::User; if (prop.isWritable()) f |= PropertyModel::Writable; data.setPropertyFlags(f); data.setRevision(prop.revision()); if (prop.hasNotifySignal()) data.setNotifySignal(Util::prettyMethodSignature(prop.notifySignal())); data.setAccessFlags(PropertyData::Readable); m_notifyGuard = false; return data; } QuickAnchorsPropertyAdaptorFactory *QuickAnchorsPropertyAdaptorFactory::s_instance = nullptr; PropertyAdaptor *QuickAnchorsPropertyAdaptorFactory::create(const ObjectInstance &oi, QObject *parent) const { if (oi.type() != ObjectInstance::QtObject || !oi.qtObject()) return nullptr; if (qobject_cast(oi.qtObject())) return new QuickAnchorsPropertyAdaptor(parent); return nullptr; } QuickAnchorsPropertyAdaptorFactory *QuickAnchorsPropertyAdaptorFactory::instance() { if (!s_instance) s_instance = new QuickAnchorsPropertyAdaptorFactory; return s_instance; } gammaray-2.11.3/plugins/quickinspector/quickanchorspropertyadaptor.h000066400000000000000000000041221413310443000261330ustar00rootroot00000000000000/* quickanchorspropertyadaptor.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_QUICKANCHORSPROPERTYADAPTOR_H #define GAMMARAY_QUICKANCHORSPROPERTYADAPTOR_H #include #include #include namespace GammaRay { class QuickAnchorsPropertyAdaptor : public PropertyAdaptor { Q_OBJECT public: explicit QuickAnchorsPropertyAdaptor(QObject *parent = nullptr); ~QuickAnchorsPropertyAdaptor() override; int count() const override; PropertyData propertyData(int index) const override; protected: void doSetObject(const ObjectInstance &oi) override; private: int m_anchorsPropertyIndex; mutable bool m_notifyGuard; }; class QuickAnchorsPropertyAdaptorFactory : public AbstractPropertyAdaptorFactory { public: PropertyAdaptor *create(const ObjectInstance &oi, QObject *parent = nullptr) const override; static QuickAnchorsPropertyAdaptorFactory *instance(); private: static QuickAnchorsPropertyAdaptorFactory *s_instance; }; } #endif gammaray-2.11.3/plugins/quickinspector/quickclientitemmodel.cpp000066400000000000000000000117021413310443000250310ustar00rootroot00000000000000/* quickclientitemmodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "quickclientitemmodel.h" #include "quickitemmodelroles.h" #include #include #include #include using namespace GammaRay; QuickClientItemModel::QuickClientItemModel(QObject *parent) : ClientDecorationIdentityProxyModel(parent) { } QuickClientItemModel::~QuickClientItemModel() = default; QVariant QuickClientItemModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); if (role == Qt::ForegroundRole || role == Qt::ToolTipRole) { int flags = ClientDecorationIdentityProxyModel::data(index, QuickItemModelRole::ItemFlags).value(); // Grey out invisible items if (role == Qt::ForegroundRole && (flags & (QuickItemModelRole::Invisible | QuickItemModelRole::ZeroSize))) return qApp->palette().color(QPalette::Disabled, QPalette::Text); // Adjust tooltip to show information about items if (role == Qt::ToolTipRole && flags) { QString tooltip = ClientDecorationIdentityProxyModel::data(index, role).toString(); tooltip.append("

    "); //if flags has OutOfView it has also PartiallyOutOfView, no need to test both if ((flags &QuickItemModelRole::PartiallyOutOfView) && (~flags & QuickItemModelRole::Invisible)) { QByteArray byteArray; QBuffer buffer(&byteArray); QIcon::fromTheme(QStringLiteral("dialog-warning")).pixmap(16, 16).save(&buffer, "PNG"); tooltip.append(" Item is visible, but out of view."); else tooltip.append("\"> Item is visible, but partially out of view."); flags &= ~QuickItemModelRole::OutOfView; flags &= ~QuickItemModelRole::PartiallyOutOfView; if (flags) tooltip.append("\n"); } if (flags) { QStringList flagStrings; if (flags & QuickItemModelRole::Invisible) flagStrings << tr("is invisible"); if (flags & QuickItemModelRole::ZeroSize) flagStrings << tr("has a size of zero"); if (flags & QuickItemModelRole::OutOfView) flagStrings << tr("is out of view"); else if (flags & QuickItemModelRole::PartiallyOutOfView) flagStrings << tr("is partially out of view"); if (flags & QuickItemModelRole::HasFocus && ~flags & QuickItemModelRole::HasActiveFocus) flagStrings << tr("has inactive focus"); if (flags & QuickItemModelRole::HasActiveFocus) flagStrings << tr("has active focus"); if (flags & QuickItemModelRole::JustRecievedEvent) flagStrings << tr("just received an event"); QByteArray byteArray; QBuffer buffer(&byteArray); QIcon::fromTheme(QStringLiteral("dialog-information")).pixmap(16, 16).save(&buffer, "PNG"); tooltip.append(QStringLiteral(" Item %1.").arg(flagStrings.join(QStringLiteral(", ")))); } tooltip.append("

    "); return tooltip; } } return ClientDecorationIdentityProxyModel::data(index, role); } gammaray-2.11.3/plugins/quickinspector/quickclientitemmodel.h000066400000000000000000000033051413310443000244760ustar00rootroot00000000000000/* quickclientitemmodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_QUICKINSPECTOR_QUICKCLIENTITEMMODEL_H #define GAMMARAY_QUICKINSPECTOR_QUICKCLIENTITEMMODEL_H #include namespace GammaRay { /** UI-dependent (and thus client-side) bits of the item tree model. */ class QuickClientItemModel : public ClientDecorationIdentityProxyModel { Q_OBJECT public: explicit QuickClientItemModel(QObject *parent = nullptr); ~QuickClientItemModel() override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; }; } #endif // GAMMARAY_QUICKCLIENTITEMMODEL_H gammaray-2.11.3/plugins/quickinspector/quickdecorationsdrawer.cpp000066400000000000000000000611731413310443000254010ustar00rootroot00000000000000/* quickdecorationsdrawer.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Filipe Azevedo Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "quickdecorationsdrawer.h" #include #include #include #include #ifndef GL_BGRA #define GL_BGRA GL_BGRA_EXT #endif using namespace GammaRay; QuickDecorationsDrawer::QuickDecorationsDrawer(QuickDecorationsDrawer::Type type, QPainter &painter, const QuickDecorationsBaseRenderInfo &renderInfo) : m_type(type) , m_renderInfo(&renderInfo) , m_painter(&painter) { Q_ASSERT(m_painter); Q_ASSERT(m_renderInfo); } void QuickDecorationsDrawer::render() { // Draw the grid if needed drawGrid(); switch (m_type) { case QuickDecorationsDrawer::Decorations: { drawDecorations(); break; } case QuickDecorationsDrawer::Traces: { drawTraces(); break; } } } void QuickDecorationsDrawer::drawDecorations() { QuickItemGeometry itemGeometry(this->itemGeometry()); if (!itemGeometry.isValid()) return; const qreal &zoom(m_renderInfo->zoom); DrawTextInfoList texts; itemGeometry.scaleTo(zoom); m_painter->save(); // bounding box if (itemGeometry.boundingRect.isValid()) { m_painter->setPen(m_renderInfo->settings.boundingRectColor); m_painter->setBrush(m_renderInfo->settings.boundingRectBrush); m_painter->drawRect(itemGeometry.boundingRect); } // original geometry if (itemGeometry.itemRect.isValid() && itemGeometry.itemRect != itemGeometry.boundingRect) { m_painter->setPen(m_renderInfo->settings.geometryRectColor); m_painter->setBrush(m_renderInfo->settings.geometryRectBrush); m_painter->drawRect(itemGeometry.itemRect); } // children rect if (itemGeometry.childrenRect.isValid() && itemGeometry.itemRect != itemGeometry.boundingRect && itemGeometry.transform.isIdentity()) { // If this item is transformed the children rect will be painted wrongly, // so for now skip painting it. m_painter->setPen(m_renderInfo->settings.childrenRectColor); m_painter->setBrush(m_renderInfo->settings.childrenRectBrush); m_painter->drawRect(itemGeometry.childrenRect); } // transform origin if (itemGeometry.itemRect != itemGeometry.boundingRect) { m_painter->setPen(m_renderInfo->settings.transformOriginColor); m_painter->drawEllipse(itemGeometry.transformOriginPoint, 2.5, 2.5); m_painter->drawLine(itemGeometry.transformOriginPoint - QPointF(0, 6), itemGeometry.transformOriginPoint + QPointF(0, 6)); m_painter->drawLine(itemGeometry.transformOriginPoint - QPointF(6, 0), itemGeometry.transformOriginPoint + QPointF(6, 0)); } // x and y values m_painter->setPen(m_renderInfo->settings.coordinatesColor); if (!itemGeometry.left && !itemGeometry.horizontalCenter && !itemGeometry.right && itemGeometry.x != 0) { QPointF parentEnd = (QPointF(itemGeometry.itemRect.x() - itemGeometry.x, itemGeometry.itemRect.y())); QPointF itemEnd = itemGeometry.itemRect.topLeft(); drawArrow(parentEnd, itemEnd); texts << DrawTextInfo(m_painter->pen(), QRectF(parentEnd.x(), parentEnd.y() + 10, itemEnd.x() - parentEnd.x(), 50), QStringLiteral("x: %1px").arg(itemGeometry.x / zoom), Qt::AlignHCenter | Qt::TextDontClip); } if (!itemGeometry.top && !itemGeometry.verticalCenter && !itemGeometry.bottom && !itemGeometry.baseline && itemGeometry.y != 0) { QPointF parentEnd = (QPointF(itemGeometry.itemRect.x(), itemGeometry.itemRect.y() - itemGeometry.y)); QPointF itemEnd = itemGeometry.itemRect.topLeft(); drawArrow(parentEnd, itemEnd); texts << DrawTextInfo(m_painter->pen(), QRectF(parentEnd.x() + 10, parentEnd.y(), 100, itemEnd.y() - parentEnd.y()), QStringLiteral("y: %1px").arg(itemGeometry.y / zoom), Qt::AlignVCenter | Qt::TextDontClip); } // anchors m_painter->setPen(m_renderInfo->settings.marginsColor); if (itemGeometry.left) { drawHorizontalAnchor(itemGeometry, itemGeometry.itemRect.left(), itemGeometry.leftMargin); texts << drawHorizontalAnchorLabel(itemGeometry, itemGeometry.itemRect.left(), itemGeometry.leftMargin, QStringLiteral("%1px").arg(itemGeometry.leftMargin / zoom), Qt::AlignBottom | Qt::AlignHCenter); } if (itemGeometry.horizontalCenter) { drawHorizontalAnchor(itemGeometry, (itemGeometry.itemRect.left() + itemGeometry.itemRect.right()) / 2, itemGeometry.horizontalCenterOffset); texts << drawHorizontalAnchorLabel(itemGeometry, (itemGeometry.itemRect.left() + itemGeometry.itemRect.right()) / 2, itemGeometry.horizontalCenterOffset, QStringLiteral("offset: %1px").arg(itemGeometry.horizontalCenterOffset / zoom), Qt::AlignBottom | Qt::AlignHCenter); } if (itemGeometry.right) { drawHorizontalAnchor(itemGeometry, itemGeometry.itemRect.right(), -itemGeometry.rightMargin); texts << drawHorizontalAnchorLabel(itemGeometry, itemGeometry.itemRect.right(), -itemGeometry.rightMargin, QStringLiteral("%1px").arg(itemGeometry.rightMargin / zoom), Qt::AlignTop | Qt::AlignHCenter); } if (itemGeometry.top) { drawVerticalAnchor(itemGeometry, itemGeometry.itemRect.top(), itemGeometry.topMargin); texts << drawVerticalAnchorLabel(itemGeometry, itemGeometry.itemRect.top(), itemGeometry.topMargin, QStringLiteral("%1px").arg(itemGeometry.topMargin / zoom), Qt::AlignVCenter | Qt::AlignRight); } if (itemGeometry.verticalCenter) { drawVerticalAnchor(itemGeometry, (itemGeometry.itemRect.top() + itemGeometry.itemRect.bottom()) / 2, itemGeometry.verticalCenterOffset); texts << drawVerticalAnchorLabel(itemGeometry, (itemGeometry.itemRect.top() + itemGeometry.itemRect.bottom()) / 2, itemGeometry.verticalCenterOffset, QStringLiteral("offset: %1px").arg(itemGeometry.verticalCenterOffset / zoom), Qt::AlignVCenter | Qt::AlignRight); } if (itemGeometry.bottom) { drawVerticalAnchor(itemGeometry, itemGeometry.itemRect.bottom(), -itemGeometry.bottomMargin); texts << drawVerticalAnchorLabel(itemGeometry, itemGeometry.itemRect.bottom(), -itemGeometry.bottomMargin, QStringLiteral("%1px").arg(itemGeometry.bottomMargin / zoom), Qt::AlignVCenter | Qt::AlignLeft); } if (itemGeometry.baseline) { drawVerticalAnchor(itemGeometry, itemGeometry.itemRect.top(), itemGeometry.baselineOffset); texts << drawVerticalAnchorLabel(itemGeometry, itemGeometry.itemRect.top(), itemGeometry.baselineOffset, QStringLiteral("offset: %1px").arg(itemGeometry.baselineOffset / zoom), Qt::AlignVCenter | Qt::AlignLeft); } // padding m_painter->setPen(m_renderInfo->settings.paddingColor); if (!qIsNaN(itemGeometry.leftPadding)) { drawHorizontalAnchor(itemGeometry, itemGeometry.itemRect.left(), -itemGeometry.leftPadding); texts << drawHorizontalAnchorLabel(itemGeometry, itemGeometry.itemRect.left(), -itemGeometry.leftPadding, QStringLiteral("%1px").arg(itemGeometry.leftPadding / zoom), Qt::AlignTop | Qt::AlignHCenter); } if (!qIsNaN(itemGeometry.rightPadding)) { drawHorizontalAnchor(itemGeometry, itemGeometry.itemRect.right(), itemGeometry.rightPadding); texts << drawHorizontalAnchorLabel(itemGeometry, itemGeometry.itemRect.right(), itemGeometry.rightPadding, QStringLiteral("%1px").arg(itemGeometry.rightPadding / zoom), Qt::AlignBottom | Qt::AlignHCenter); } if (!qIsNaN(itemGeometry.topPadding)) { drawVerticalAnchor(itemGeometry, itemGeometry.itemRect.top(), -itemGeometry.topPadding); texts << drawVerticalAnchorLabel(itemGeometry, itemGeometry.itemRect.top(), -itemGeometry.topPadding, QStringLiteral("%1px").arg(itemGeometry.topPadding / zoom), Qt::AlignVCenter | Qt::AlignLeft); } if (!qIsNaN(itemGeometry.bottomPadding)) { drawVerticalAnchor(itemGeometry, itemGeometry.itemRect.bottom(), itemGeometry.bottomPadding); texts << drawVerticalAnchorLabel(itemGeometry, itemGeometry.itemRect.bottom(), itemGeometry.bottomPadding, QStringLiteral("%1px").arg(itemGeometry.bottomPadding / zoom), Qt::AlignVCenter | Qt::AlignRight); } // Finally draw texts over the traced rectangles and lines // This make sure texts are always readable for (const auto &t : qAsConst(texts)) { if (t.label.isEmpty()) continue; m_painter->setPen(t.pen); m_painter->drawText(t.rect, t.align, t.label); } m_painter->restore(); } void QuickDecorationsDrawer::drawTraces() { const QVector itemsGeometry(this->itemsGeometry()); if (itemsGeometry.isEmpty()) return; m_painter->save(); for (auto itemGeometry : itemsGeometry) { itemGeometry.scaleTo(m_renderInfo->zoom); // bounding box m_painter->setPen(itemGeometry.traceColor); m_painter->setBrush(itemGeometry.traceColor.lighter()); m_painter->drawRect(itemGeometry.boundingRect); // bounding class box m_painter->setPen(QColor(60, 60, 60, 70)); m_painter->setBrush(m_painter->pen().color()); const int margin = m_painter->fontMetrics().width(QLatin1Char('X')) / 2; const QRectF classRect = itemGeometry.boundingRect.adjusted( 0, 0, 0, -(itemGeometry.boundingRect.height() - (m_painter->fontMetrics().height() * 1.6))); m_painter->drawRect(classRect); // type name label m_painter->setPen(QColor(250, 250, 250, 120)); m_painter->drawText(classRect.adjusted(margin, margin, -margin, -margin), Qt::AlignVCenter | Qt::AlignLeft | Qt::TextDontClip, itemGeometry.traceTypeName); // draw bounding box corners m_painter->setPen(QPen(QColor(30, 30, 30), 3)); m_painter->setBrush(m_painter->pen().color()); const QVector points = QVector() << itemGeometry.boundingRect.topLeft() << itemGeometry.boundingRect.topRight() << itemGeometry.boundingRect.bottomRight() << itemGeometry.boundingRect.bottomLeft(); m_painter->drawPoints(points); // name label m_painter->setPen(QColor(250, 250, 250, 120)); m_painter->drawText(itemGeometry.boundingRect.adjusted(margin, margin, -margin, -margin), Qt::AlignVCenter | Qt::AlignLeft | Qt::TextDontClip, itemGeometry.traceName); } m_painter->restore(); } QuickItemGeometry QuickDecorationsDrawer::itemGeometry() const { switch (m_type) { case QuickDecorationsDrawer::Decorations: return static_cast(m_renderInfo)->itemGeometry; case QuickDecorationsDrawer::Traces: break; } return QuickItemGeometry(); } QVector QuickDecorationsDrawer::itemsGeometry() const { switch (m_type) { case QuickDecorationsDrawer::Decorations: break; case QuickDecorationsDrawer::Traces: return static_cast(m_renderInfo)->itemsGeometry; } return QVector(); } void QuickDecorationsDrawer::drawGrid() { const QRectF &viewRect(m_renderInfo->viewRect); const QPointF &gridOffset(m_renderInfo->settings.gridOffset); const QSizeF &gridCellSize(m_renderInfo->settings.gridCellSize); if (!m_renderInfo->settings.gridEnabled || gridCellSize.isEmpty()) return; m_painter->save(); m_painter->setPen(m_renderInfo->settings.gridColor); QVector lines; lines.reserve((viewRect.width() / gridCellSize.width()) + (viewRect.height() / gridCellSize.height())); for (qreal x = viewRect.left() + gridOffset.x(); x < viewRect.right(); x += gridCellSize.width()) { if (x < viewRect.left()) continue; lines << QLineF(QPointF(x, viewRect.top()) * m_renderInfo->zoom, QPointF(x, viewRect.bottom()) * m_renderInfo->zoom); } for (qreal y = viewRect.top() + gridOffset.y(); y < viewRect.bottom(); y += gridCellSize.height()) { if (y < viewRect.top()) continue; lines << QLineF(QPointF(viewRect.left(), y) * m_renderInfo->zoom, QPointF(viewRect.right(), y) * m_renderInfo->zoom); } m_painter->drawLines(lines); m_painter->restore(); } void QuickDecorationsDrawer::drawArrow(const QPointF &first, const QPointF &second) { m_painter->drawLine(first, second); QPointF vector(second - first); QMatrix m; m.rotate(30); QVector2D v1 = QVector2D(m.map(vector)).normalized() * 10; m.rotate(-60); QVector2D v2 = QVector2D(m.map(vector)).normalized() * 10; m_painter->drawLine(first, first + v1.toPointF()); m_painter->drawLine(first, first + v2.toPointF()); m_painter->drawLine(second, second - v1.toPointF()); m_painter->drawLine(second, second - v2.toPointF()); } void QuickDecorationsDrawer::drawAnchor(const QuickItemGeometry &itemGeometry, Qt::Orientation orientation, qreal ownAnchorLine, qreal offset) { const QRectF &viewRect(m_renderInfo->viewRect); const qreal &zoom(m_renderInfo->zoom); m_painter->save(); const qreal foreignAnchorLine = ownAnchorLine - offset; // Arrow if (offset) { if (orientation == Qt::Horizontal) { drawArrow(QPointF(foreignAnchorLine, (itemGeometry.itemRect.top() + itemGeometry.itemRect.bottom()) / 2), QPointF(ownAnchorLine, (itemGeometry.itemRect.top() + itemGeometry.itemRect.bottom()) / 2)); } else { drawArrow(QPointF((itemGeometry.itemRect.left() + itemGeometry.itemRect.right()) / 2, foreignAnchorLine), QPointF((itemGeometry.itemRect.left() + itemGeometry.itemRect.right()) / 2, ownAnchorLine)); } } // Own Anchor line QPen pen(m_painter->pen()); pen.setWidth(2); m_painter->setPen(pen); if (orientation == Qt::Horizontal) { m_painter->drawLine(ownAnchorLine, itemGeometry.itemRect.top(), ownAnchorLine, itemGeometry.itemRect.bottom()); } else { m_painter->drawLine( itemGeometry.itemRect.left(), ownAnchorLine, itemGeometry.itemRect.right(), ownAnchorLine); } // Foreign Anchor line pen.setStyle(Qt::DotLine); m_painter->setPen(pen); if (orientation == Qt::Horizontal) { m_painter->drawLine(foreignAnchorLine, 0, foreignAnchorLine, viewRect.height() * zoom); } else { m_painter->drawLine(0, foreignAnchorLine, viewRect.width() * zoom, foreignAnchorLine); } m_painter->restore(); } void QuickDecorationsDrawer::drawVerticalAnchor(const QuickItemGeometry &itemGeometry, qreal ownAnchorLine, qreal offset) { drawAnchor(itemGeometry, Qt::Vertical, ownAnchorLine, offset); } void QuickDecorationsDrawer::drawHorizontalAnchor(const QuickItemGeometry &itemGeometry, qreal ownAnchorLine, qreal offset) { drawAnchor(itemGeometry, Qt::Horizontal, ownAnchorLine, offset); } QuickDecorationsDrawer::DrawTextInfo QuickDecorationsDrawer::drawAnchorLabel(const QuickItemGeometry &itemGeometry, Qt::Orientation orientation, qreal ownAnchorLine, qreal offset, const QString &label, Qt::Alignment align) { if (align.testFlag(Qt::AlignCenter)) { qWarning("%s: You can not use Qt::AlignCenter!", Q_FUNC_INFO); return QuickDecorationsDrawer::DrawTextInfo(); } if (align.testFlag(Qt::AlignJustify)) { qWarning("%s: You can not use Qt::AlignJustify!", Q_FUNC_INFO); return QuickDecorationsDrawer::DrawTextInfo(); } if (align.testFlag(Qt::AlignBaseline)) { qWarning("%s: You can not use Qt::AlignBaseline!", Q_FUNC_INFO); return QuickDecorationsDrawer::DrawTextInfo(); } if (offset) { const qreal foreignAnchorLine = ownAnchorLine - offset; const int margin = 10; if (orientation == Qt::Horizontal) { QRectF rect(m_painter->fontMetrics().boundingRect(label)); QPointF center(foreignAnchorLine + ((ownAnchorLine - foreignAnchorLine) / 2), itemGeometry.itemRect.center().y()); if (align.testFlag(Qt::AlignLeft)) { rect.moveRight(center.x()); if (align.testFlag(Qt::AlignVCenter)) { rect.moveRight(foreignAnchorLine - margin); } } else if (align.testFlag(Qt::AlignRight)) { rect.moveLeft(center.x()); if (align.testFlag(Qt::AlignVCenter)) { rect.moveLeft(foreignAnchorLine + offset + margin); } } else if (align.testFlag(Qt::AlignHCenter)) { rect.moveCenter(QPointF(center.x(), rect.center().y())); } if (align.testFlag(Qt::AlignTop)) { rect.moveBottom(center.y() - margin); } else if (align.testFlag(Qt::AlignBottom)) { rect.moveTop(center.y() + margin); } else if (align.testFlag(Qt::AlignVCenter)) { rect.moveCenter(QPointF(rect.center().x(), center.y())); } return QuickDecorationsDrawer::DrawTextInfo( m_painter->pen(), rect, label ); } else { QRectF rect(m_painter->fontMetrics().boundingRect(label)); QPointF center(itemGeometry.itemRect.center().x(), foreignAnchorLine + ((ownAnchorLine - foreignAnchorLine) / 2)); if (align.testFlag(Qt::AlignLeft)) { rect.moveRight(center.x() - margin); } else if (align.testFlag(Qt::AlignRight)) { rect.moveLeft(center.x() + margin); } else if (align.testFlag(Qt::AlignHCenter)) { rect.moveCenter(QPointF(center.x(), rect.center().y())); } if (align.testFlag(Qt::AlignTop)) { rect.moveBottom(center.y()); } else if (align.testFlag(Qt::AlignBottom)) { rect.moveTop(center.y()); } else if (align.testFlag(Qt::AlignVCenter)) { rect.moveCenter(QPointF(rect.center().x(), center.y())); } return QuickDecorationsDrawer::DrawTextInfo( m_painter->pen(), rect, label ); } } return QuickDecorationsDrawer::DrawTextInfo(); } QuickDecorationsDrawer::DrawTextInfo QuickDecorationsDrawer::drawHorizontalAnchorLabel(const QuickItemGeometry &itemGeometry, qreal ownAnchorLine, qreal offset, const QString &label, Qt::Alignment align) { return drawAnchorLabel(itemGeometry, Qt::Horizontal, ownAnchorLine, offset, label, align); } QuickDecorationsDrawer::DrawTextInfo QuickDecorationsDrawer::drawVerticalAnchorLabel(const QuickItemGeometry &itemGeometry, qreal ownAnchorLine, qreal offset, const QString &label, Qt::Alignment align) { return drawAnchorLabel(itemGeometry, Qt::Vertical, ownAnchorLine, offset, label, align); } QDataStream &GammaRay::operator<<(QDataStream &stream, const GammaRay::QuickDecorationsSettings &settings) { stream << settings.boundingRectColor << settings.boundingRectBrush << settings.geometryRectColor << settings.geometryRectBrush << settings.childrenRectColor << settings.childrenRectBrush << settings.transformOriginColor << settings.coordinatesColor << settings.marginsColor << settings.paddingColor << settings.gridOffset << settings.gridCellSize << settings.gridColor << settings.componentsTraces << settings.gridEnabled ; return stream; } QDataStream &GammaRay::operator>>(QDataStream &stream, GammaRay::QuickDecorationsSettings &settings) { stream >> settings.boundingRectColor >> settings.boundingRectBrush >> settings.geometryRectColor >> settings.geometryRectBrush >> settings.childrenRectColor >> settings.childrenRectBrush >> settings.transformOriginColor >> settings.coordinatesColor >> settings.marginsColor >> settings.paddingColor >> settings.gridOffset >> settings.gridCellSize >> settings.gridColor >> settings.componentsTraces >> settings.gridEnabled ; return stream; } gammaray-2.11.3/plugins/quickinspector/quickdecorationsdrawer.h000066400000000000000000000167561413310443000250550ustar00rootroot00000000000000/* quickdecorationsdrawer.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Filipe Azevedo Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_QUICKINSPECTOR_QUICKDECORATIONSDRAWER_H #define GAMMARAY_QUICKINSPECTOR_QUICKDECORATIONSDRAWER_H #include #include "quickitemgeometry.h" namespace GammaRay { struct QuickDecorationsSettings { QuickDecorationsSettings() : boundingRectColor(QColor(232, 87, 82, 170)) , boundingRectBrush(QBrush(QColor(232, 87, 82, 95))) , geometryRectColor(QColor(Qt::gray)) , geometryRectBrush(QBrush(QColor(Qt::gray), Qt::BDiagPattern)) , childrenRectColor(QColor(0, 99, 193, 170)) , childrenRectBrush(QBrush(QColor(0, 99, 193, 95))) , transformOriginColor(QColor(156, 15, 86, 170)) , coordinatesColor(QColor(136, 136, 136)) , marginsColor(QColor(139, 179, 0)) , paddingColor(QColor(Qt::darkBlue)) , gridOffset(QPointF(0, 0)) , gridCellSize(QSizeF(0, 0)) , gridColor(QColor(Qt::red)) , componentsTraces(false) , gridEnabled(false) { } bool operator==(const QuickDecorationsSettings &other) const { return boundingRectColor == other.boundingRectColor && boundingRectBrush == other.boundingRectBrush && geometryRectColor == other.geometryRectColor && geometryRectBrush == other.geometryRectBrush && childrenRectColor == other.childrenRectColor && childrenRectBrush == other.childrenRectBrush && transformOriginColor == other.transformOriginColor && coordinatesColor == other.coordinatesColor && marginsColor == other.marginsColor && paddingColor == other.paddingColor && gridOffset == other.gridOffset && gridCellSize == other.gridCellSize && gridColor == other.gridColor && componentsTraces == other.componentsTraces && gridEnabled == other.gridEnabled ; } bool operator!=(const QuickDecorationsSettings &other) const { return !operator==(other); } QColor boundingRectColor; QBrush boundingRectBrush; QColor geometryRectColor; QBrush geometryRectBrush; QColor childrenRectColor; QBrush childrenRectBrush; QColor transformOriginColor; QColor coordinatesColor; QColor marginsColor; QColor paddingColor; QPointF gridOffset; QSizeF gridCellSize; QColor gridColor; bool componentsTraces; bool gridEnabled; }; QDataStream &operator<<(QDataStream &stream, const GammaRay::QuickDecorationsSettings &settings); QDataStream &operator>>(QDataStream &stream, GammaRay::QuickDecorationsSettings &settings); struct QuickDecorationsBaseRenderInfo { QuickDecorationsBaseRenderInfo(const QuickDecorationsSettings &settings = QuickDecorationsSettings(), const QRectF &viewRect = QRectF(), qreal zoom = 1.0) : settings(settings) , viewRect(viewRect) , zoom(zoom) { } const QuickDecorationsSettings settings; const QRectF viewRect; const qreal zoom; }; struct QuickDecorationsRenderInfo : QuickDecorationsBaseRenderInfo { QuickDecorationsRenderInfo(const QuickDecorationsSettings &settings = QuickDecorationsSettings(), const QuickItemGeometry &itemGeometry = QuickItemGeometry(), const QRectF &viewRect = QRectF(), qreal zoom = 1.0) : QuickDecorationsBaseRenderInfo(settings, viewRect, zoom) , itemGeometry(itemGeometry) { } const QuickItemGeometry itemGeometry; }; struct QuickDecorationsTracesInfo : QuickDecorationsBaseRenderInfo { QuickDecorationsTracesInfo(const QuickDecorationsSettings &settings = QuickDecorationsSettings(), const QVector &itemsGeometry = QVector(), const QRectF &viewRect = QRectF(), qreal zoom = 1.0) : QuickDecorationsBaseRenderInfo(settings, viewRect, zoom) , itemsGeometry(itemsGeometry) { } const QVector itemsGeometry; }; class QuickDecorationsDrawer { public: enum Type { Decorations, Traces }; QuickDecorationsDrawer(QuickDecorationsDrawer::Type type, QPainter &painter, const QuickDecorationsBaseRenderInfo &renderInfo); void render(); private: struct DrawTextInfo { DrawTextInfo(const QPen &pen = QPen(), const QRectF &rect = QRectF(), const QString &label = QString(), int align = Qt::AlignCenter | Qt::TextDontClip) : pen(pen) , rect(rect) , label(label) , align(align) { } QPen pen; QRectF rect; QString label; int align; }; typedef QVector DrawTextInfoList; QuickItemGeometry itemGeometry() const; QVector itemsGeometry() const; void drawDecorations(); void drawTraces(); void drawGrid(); void drawArrow(const QPointF &first, const QPointF &second); void drawAnchor(const QuickItemGeometry &itemGeometry, Qt::Orientation orientation, qreal ownAnchorLine, qreal offset); void drawVerticalAnchor(const QuickItemGeometry &itemGeometry, qreal ownAnchorLine, qreal offset); void drawHorizontalAnchor(const QuickItemGeometry &itemGeometry, qreal ownAnchorLine, qreal offset); DrawTextInfo drawAnchorLabel(const QuickItemGeometry &itemGeometry, Qt::Orientation orientation, qreal ownAnchorLine, qreal offset, const QString &label, Qt::Alignment align); DrawTextInfo drawHorizontalAnchorLabel(const QuickItemGeometry &itemGeometry, qreal ownAnchorLine, qreal offset, const QString &label, Qt::Alignment align); DrawTextInfo drawVerticalAnchorLabel(const QuickItemGeometry &itemGeometry, qreal ownAnchorLine, qreal offset, const QString &label, Qt::Alignment align); const QuickDecorationsDrawer::Type m_type; const QuickDecorationsBaseRenderInfo *const m_renderInfo; QPainter *const m_painter; }; } Q_DECLARE_METATYPE(GammaRay::QuickDecorationsSettings) #endif gammaray-2.11.3/plugins/quickinspector/quickimplicitbindingdependencyprovider.cpp000066400000000000000000000402341413310443000306340ustar00rootroot00000000000000/* quickimplicitbindingdependencyprovider.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ // Own #include "quickimplicitbindingdependencyprovider.h" #include #include // Qt #include #include #include #include using namespace GammaRay; std::unique_ptr GammaRay::QuickImplicitBindingDependencyProvider::createBindingNode(QObject* obj, const char *propertyName, BindingNode *parent) const { if (!obj || !obj->metaObject()) return {}; QQmlProperty qmlProperty(obj, propertyName); auto node = std::unique_ptr(new BindingNode(qmlProperty.object(), qmlProperty.index(), parent)); QQmlContext *ctx = QQmlEngine::contextForObject(obj); QString canonicalName = propertyName; if (ctx) { QString id = ctx->nameForObject(obj); if (!id.isEmpty()) canonicalName = QStringLiteral("%1.%2").arg(id, canonicalName); } node->setCanonicalName(canonicalName); return node; } bool QuickImplicitBindingDependencyProvider::canProvideBindingsFor(QObject *object) const { return object->inherits("QQuickAnchors") || object->inherits("QQuickItem"); } std::vector> QuickImplicitBindingDependencyProvider::findBindingsFor(QObject *obj) const { std::vector> bindings; if (QQuickItem *item = qobject_cast(obj)) { //FIXME: Check for QQuickAnchors directly here, as soon as we show properties of object-properties. QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(item); if (!itemPriv) return bindings; QQuickAnchors *anchors = itemPriv->_anchors; if (!anchors) return bindings; auto usedAnchors = anchors->usedAnchors(); if (usedAnchors & QQuickAnchors::TopAnchor) bindings.push_back(createBindingNode(item, "anchors.top")); if (usedAnchors & QQuickAnchors::BottomAnchor) bindings.push_back(createBindingNode(item, "anchors.bottom")); if (usedAnchors & QQuickAnchors::LeftAnchor) bindings.push_back(createBindingNode(item, "anchors.left")); if (usedAnchors & QQuickAnchors::RightAnchor) bindings.push_back(createBindingNode(item, "anchors.right")); if (usedAnchors & QQuickAnchors::HCenterAnchor) bindings.push_back(createBindingNode(item, "anchors.horizontalCenter")); if (usedAnchors & QQuickAnchors::VCenterAnchor) bindings.push_back(createBindingNode(item, "anchors.verticalCenter")); if (usedAnchors & QQuickAnchors::BaselineAnchor) bindings.push_back(createBindingNode(item, "anchors.baseline")); } return bindings; } std::vector> QuickImplicitBindingDependencyProvider::findDependenciesFor(BindingNode *binding) const { std::vector> dependencies; // So far, we can only hard code implicit dependencies. QObject *object = binding->object(); if (!object) return dependencies; auto addDependency = [this, binding, object, &dependencies](const char *propName, QObject *depObj, const char *depName) { if (depObj && binding->propertyIndex() == object->metaObject()->indexOfProperty(propName)) { dependencies.push_back(createBindingNode(depObj, depName, binding)); } }; if (QQuickAnchors *anchors = qobject_cast(object)) { anchorBindings(dependencies, anchors, binding->propertyIndex(), binding); } if (QQuickItem *item = qobject_cast(object)) { implicitSizeDependencies(item, addDependency); anchoringDependencies(item, addDependency); if (binding->propertyIndex() == item->metaObject()->indexOfProperty("childrenRect")) { childrenRectDependencies(item, addDependency); } if (item->inherits("QQuickBasePositioner")) { positionerDependencies(item, addDependency); } } return dependencies; } void QuickImplicitBindingDependencyProvider::anchorBindings(std::vector> &dependencies, QQuickAnchors *anchors, int propertyIndex, BindingNode *parent) const { QQuickAnchorLine anchorLine = anchors->metaObject()->property(propertyIndex).read(anchors).value(); auto dependencyPropertyName = anchorLine.anchorLine == QQuickAnchors::TopAnchor ? "top" : anchorLine.anchorLine == QQuickAnchors::BottomAnchor ? "bottom" : anchorLine.anchorLine == QQuickAnchors::LeftAnchor ? "left" : anchorLine.anchorLine == QQuickAnchors::RightAnchor ? "right" : anchorLine.anchorLine == QQuickAnchors::HCenterAnchor ? "horizontalCenter" : anchorLine.anchorLine == QQuickAnchors::VCenterAnchor ? "verticalCenter" : anchorLine.anchorLine == QQuickAnchors::BaselineAnchor ? "baseline" : ""; if (anchorLine.item) { dependencies.push_back(createBindingNode(anchorLine.item, dependencyPropertyName, parent)); } } template void QuickImplicitBindingDependencyProvider::implicitSizeDependencies(QQuickItem *item, Func addDependency) const { QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(item); if (!itemPriv) return; if (!itemPriv->widthValid) { addDependency("width", item, "implicitWidth"); } if (!itemPriv->heightValid) { addDependency("height", item, "implicitHeight"); } } template void QuickImplicitBindingDependencyProvider::childrenRectDependencies(QQuickItem *item, Func addDependency) const { foreach (auto &&child, item->childItems()) { addDependency("childrenRect", child, "width"); addDependency("childrenRect", child, "height"); } } template void QuickImplicitBindingDependencyProvider::positionerDependencies(QQuickItem *item, Func addDependency) const { foreach (QQuickItem *child, item->childItems()) { addDependency("implicitWidth", child, "width"); addDependency("implicitHeight", child, "height"); } } template void QuickImplicitBindingDependencyProvider::anchoringDependencies(QQuickItem *item, Func addDependency) const { QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(item); if (!itemPriv) return; QQuickAnchors *anchors = itemPriv->_anchors; // Horizontal if (anchors && anchors->fill()) { QQuickItem *fill = anchors->fill(); addDependency("width", fill, "width"); addDependency("width", item, "anchors.leftMargin"); addDependency("width", item, "anchors.rightMargin"); addDependency("x", fill, "left"); addDependency("x", item, "anchors.leftMargin"); addDependency("left", fill, "left"); addDependency("left", item, "anchors.leftMargin"); addDependency("right", fill, "right"); addDependency("right", item, "anchors.rightMargin"); addDependency("horizontalCenter", item, "left"); addDependency("horizontalCenter", item, "right"); } else if (anchors && anchors->centerIn()) { QQuickItem *centerIn = anchors->centerIn(); addDependency("horizontalCenter", centerIn, "horizontalCenter"); addDependency("x", centerIn, "horizontalCenter"); addDependency("x", item, "width"); addDependency("left", centerIn, "horizontalCenter"); addDependency("left", item, "width"); addDependency("right", centerIn, "horizontalCenter"); addDependency("right", item, "width"); } else if (anchors && anchors->left().anchorLine != QQuickAnchors::InvalidAnchor) { addDependency("left", item, "anchors.left"); addDependency("x", item, "anchors.left"); if (anchors->right().anchorLine != QQuickAnchors::InvalidAnchor) { addDependency("right", item, "anchors.right"); addDependency("right", item, "anchors.rightMargin"); addDependency("width", item, "anchors.left"); addDependency("width", item, "anchors.right"); addDependency("horizontalCenter", item, "anchors.left"); addDependency("horizontalCenter", item, "anchors.right"); } else if (anchors->horizontalCenter().anchorLine != QQuickAnchors::InvalidAnchor) { addDependency("horizontalCenter", item, "anchors.horizontalCenter"); addDependency("width", item, "anchors.left"); addDependency("width", item, "anchors.horizontalCenter"); addDependency("right", item, "anchors.left"); addDependency("right", item, "anchors.horizontalCenter"); } else { addDependency("right", item, "anchors.left"); addDependency("right", item, "width"); addDependency("horizontalCenter", item, "anchors.left"); addDependency("horizontalCenter", item, "width"); } } else if (anchors && anchors->right().anchorLine != QQuickAnchors::InvalidAnchor) { addDependency("right", item, "anchors.right"); if (anchors->horizontalCenter().anchorLine != QQuickAnchors::InvalidAnchor) { addDependency("horizontalCenter", item, "anchors.horizontalCenter"); addDependency("width", item, "anchors.right"); addDependency("width", item, "anchors.horizontalCenter"); addDependency("x", item, "anchors.horizontalCenter"); addDependency("x", item, "anchors.right"); addDependency("left", item, "anchors.horizontalCenter"); addDependency("left", item, "anchors.right"); } else { addDependency("x", item, "anchors.right"); addDependency("x", item, "width"); addDependency("left", item, "anchors.right"); addDependency("left", item, "width"); addDependency("horizontalCenter", item, "anchors.right"); addDependency("horizontalCenter", item, "width"); } } else if (anchors && anchors->horizontalCenter().anchorLine != QQuickAnchors::InvalidAnchor) { addDependency("horizontalCenter", item, "anchors.horizontalCenter"); addDependency("x", item, "anchors.horizontalCenter"); addDependency("x", item, "width"); addDependency("left", item, "anchors.horizontalCenter"); addDependency("left", item, "width"); addDependency("right", item, "anchors.horizontalCenter"); addDependency("right", item, "width"); } else { addDependency("left", item, "x"); addDependency("right", item, "x"); addDependency("right", item, "width"); addDependency("horizontalCenter", item, "x"); addDependency("horizontalCenter", item, "width"); } // Vertical TODO: Bottomline if (anchors && anchors->fill()) { QQuickItem *fill = anchors->fill(); addDependency("height", fill, "height"); addDependency("height", item, "anchors.topMargin"); addDependency("height", item, "anchors.bottomMargin"); addDependency("y", fill, "top"); addDependency("y", item, "anchors.topMargin"); addDependency("top", fill, "top"); addDependency("top", item, "anchors.topMargin"); addDependency("bottom", fill, "bottom"); addDependency("bottom", item, "anchors.bottomMargin"); addDependency("verticalCenter", item, "top"); addDependency("verticalCenter", item, "bottom"); } else if (anchors && anchors->centerIn()) { QQuickItem *centerIn = anchors->centerIn(); addDependency("verticalCenter", centerIn, "verticalCenter"); addDependency("y", centerIn, "verticalCenter"); addDependency("y", item, "height"); addDependency("top", centerIn, "verticalCenter"); addDependency("top", item, "height"); addDependency("bottom", centerIn, "verticalCenter"); addDependency("bottom", item, "height"); } else if (anchors && anchors->top().anchorLine != QQuickAnchors::InvalidAnchor) { addDependency("top", item, "anchors.top"); addDependency("y", item, "anchors.top"); if (anchors->bottom().anchorLine != QQuickAnchors::InvalidAnchor) { addDependency("bottom", item, "anchors.bottom"); addDependency("bottom", item, "anchors.bottomMargin"); addDependency("height", item, "anchors.top"); addDependency("height", item, "anchors.bottom"); addDependency("verticalCenter", item, "anchors.top"); addDependency("verticalCenter", item, "anchors.bottom"); } else if (anchors->verticalCenter().anchorLine != QQuickAnchors::InvalidAnchor) { addDependency("verticalCenter", item, "anchors.verticalCenter"); addDependency("height", item, "anchors.top"); addDependency("height", item, "anchors.verticalCenter"); addDependency("y", item, "anchors.top"); addDependency("bottom", item, "anchors.top"); addDependency("bottom", item, "anchors.verticalCenter"); } else { addDependency("bottom", item, "anchors.top"); addDependency("bottom", item, "height"); addDependency("verticalCenter", item, "anchors.top"); addDependency("verticalCenter", item, "height"); } } else if (anchors && anchors->bottom().anchorLine != QQuickAnchors::InvalidAnchor) { addDependency("bottom", item, "anchors.bottom"); if (anchors->verticalCenter().anchorLine != QQuickAnchors::InvalidAnchor) { addDependency("verticalCenter", item, "anchors.verticalCenter"); addDependency("height", item, "anchors.bottom"); addDependency("height", item, "anchors.verticalCenter"); addDependency("y", item, "anchors.verticalCenter"); addDependency("y", item, "anchors.bottom"); addDependency("top", item, "anchors.verticalCenter"); addDependency("top", item, "anchors.bottom"); } else { addDependency("y", item, "anchors.bottom"); addDependency("y", item, "height"); addDependency("top", item, "anchors.bottom"); addDependency("top", item, "height"); addDependency("verticalCenter", item, "anchors.bottom"); addDependency("verticalCenter", item, "height"); } } else if (anchors && anchors->verticalCenter().anchorLine != QQuickAnchors::InvalidAnchor) { addDependency("verticalCenter", item, "anchors.verticalCenter"); addDependency("y", item, "anchors.verticalCenter"); addDependency("y", item, "height"); addDependency("top", item, "anchors.verticalCenter"); addDependency("top", item, "height"); addDependency("bottom", item, "anchors.verticalCenter"); addDependency("bottom", item, "height"); } else { addDependency("top", item, "y"); addDependency("bottom", item, "y"); addDependency("bottom", item, "height"); addDependency("verticalCenter", item, "y"); addDependency("verticalCenter", item, "height"); } } gammaray-2.11.3/plugins/quickinspector/quickimplicitbindingdependencyprovider.h000066400000000000000000000051521413310443000303010ustar00rootroot00000000000000/* quickimplicitbindingdependencyprovider.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_QUICKIMPLICITBINDINGDEPENDENCYPROVIDER_H #define GAMMARAY_QUICKIMPLICITBINDINGDEPENDENCYPROVIDER_H // Own #include #include // Qt #include // Std #include #include QT_BEGIN_NAMESPACE class QQuickItem; class QQuickAnchors; QT_END_NAMESPACE namespace GammaRay { class QuickImplicitBindingDependencyProvider : public AbstractBindingProvider { public: std::vector> findBindingsFor(QObject * obj) const override; std::vector> findDependenciesFor(GammaRay::BindingNode * binding) const override; bool canProvideBindingsFor(QObject *object) const override; private: std::unique_ptr createBindingNode(QObject *obj, const char *propertyName, BindingNode *parent = nullptr) const; void anchorBindings(std::vector> &dependencies, QQuickAnchors *anchors, int propertyIndex, BindingNode *parent = nullptr) const; template void childrenRectDependencies(QQuickItem *item, Func addDependency) const; template void positionerDependencies(QQuickItem *item, Func addDependency) const; template void implicitSizeDependencies(QQuickItem *item, Func addDependency) const; template void anchoringDependencies(QQuickItem *item, Func addDependency) const; }; } #endif // GAMMARAY_QUICKIMPLICITBINDINGDEPENDENCYPROVIDER_H gammaray-2.11.3/plugins/quickinspector/quickinspector.cpp000066400000000000000000001354401413310443000236670ustar00rootroot00000000000000/* qmlsupport.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "quickinspector.h" #include "quickanchorspropertyadaptor.h" #include "quickitemmodel.h" #include "quickscenegraphmodel.h" #include "quickscreengrabber.h" #include "quickpaintanalyzerextension.h" #include "geometryextension/sggeometryextension.h" #include "materialextension/materialextension.h" #include "materialextension/qquickopenglshadereffectmaterialadaptor.h" #include "textureextension/qsgtexturegrabber.h" #include "textureextension/textureextension.h" #if QT_VERSION >= QT_VERSION_CHECK(5, 7, 0) #include "quickimplicitbindingdependencyprovider.h" #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include <3rdparty/kde/krecursivefilterproxymodel.h> #include #include #include #include #include #include #include #include #include #ifndef QT_NO_OPENGL #include #endif #include #include #include #include #include #include #include #include #include #include #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) #include #include #ifndef QT_NO_OPENGL #include #endif #include #include #include #endif #include #include #ifndef QT_NO_OPENGL #include #endif #include #include Q_DECLARE_METATYPE(QQmlError) Q_DECLARE_METATYPE(QQuickItem::Flags) Q_DECLARE_METATYPE(QQuickPaintedItem::PerformanceHints) Q_DECLARE_METATYPE(QSGNode *) Q_DECLARE_METATYPE(QSGBasicGeometryNode *) Q_DECLARE_METATYPE(QSGGeometryNode *) Q_DECLARE_METATYPE(QSGClipNode *) Q_DECLARE_METATYPE(QSGTransformNode *) Q_DECLARE_METATYPE(QSGRootNode *) Q_DECLARE_METATYPE(QSGOpacityNode *) Q_DECLARE_METATYPE(QSGNode::Flags) Q_DECLARE_METATYPE(QSGNode::DirtyState) Q_DECLARE_METATYPE(QSGGeometry *) Q_DECLARE_METATYPE(QMatrix4x4 *) Q_DECLARE_METATYPE(const QMatrix4x4 *) Q_DECLARE_METATYPE(const QSGClipNode *) Q_DECLARE_METATYPE(const QSGGeometry *) Q_DECLARE_METATYPE(QSGMaterial *) Q_DECLARE_METATYPE(QSGMaterial::Flags) Q_DECLARE_METATYPE(QSGTexture::WrapMode) Q_DECLARE_METATYPE(QSGTexture::Filtering) #if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0) Q_DECLARE_METATYPE(QSGTexture::AnisotropyLevel) #endif #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) Q_DECLARE_METATYPE(QSGRenderNode *) Q_DECLARE_METATYPE(QSGRenderNode::RenderingFlags) Q_DECLARE_METATYPE(QSGRenderNode::StateFlags) #endif #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) Q_DECLARE_METATYPE(QSGRendererInterface*) Q_DECLARE_METATYPE(QSGRendererInterface::GraphicsApi) Q_DECLARE_METATYPE(QSGRendererInterface::ShaderCompilationTypes) Q_DECLARE_METATYPE(QSGRendererInterface::ShaderSourceTypes) Q_DECLARE_METATYPE(QSGRendererInterface::ShaderType) #endif using namespace GammaRay; #define E(x) { QQuickItem:: x, #x } static const MetaEnum::Value qqitem_flag_table[] = { E(ItemClipsChildrenToShape), E(ItemAcceptsInputMethod), E(ItemIsFocusScope), E(ItemHasContents), E(ItemAcceptsDrops) }; #undef E static QString qQuickPaintedItemPerformanceHintsToString(QQuickPaintedItem::PerformanceHints hints) { QStringList list; if (hints & QQuickPaintedItem::FastFBOResizing) list << QStringLiteral("FastFBOResizing"); if (list.isEmpty()) return QStringLiteral(""); return list.join(QStringLiteral(" | ")); } #define E(x) { QSGNode:: x, #x } static const MetaEnum::Value qsg_node_flag_table[] = { E(OwnedByParent), E(UsePreprocess), E(OwnsGeometry), E(OwnsMaterial), E(OwnsOpaqueMaterial) }; static const MetaEnum::Value qsg_node_dirtystate_table[] = { E(DirtySubtreeBlocked), E(DirtyMatrix), E(DirtyNodeAdded), E(DirtyNodeRemoved), E(DirtyGeometry), E(DirtyMaterial), E(DirtyOpacity), E(DirtyForceUpdate), E(DirtyUsePreprocess), E(DirtyPropagationMask) }; #undef E static QString qsgMaterialFlagsToString(QSGMaterial::Flags flags) { QStringList list; #define F(f) if (flags & QSGMaterial::f) list.push_back(QStringLiteral(#f)); F(Blending) F(RequiresDeterminant) F(RequiresFullMatrixExceptTranslate) F(RequiresFullMatrix) F(CustomCompileStep) #undef F if (list.isEmpty()) return QStringLiteral(""); return list.join(QStringLiteral(" | ")); } #define E(x) { QSGTexture:: x, #x } #if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0) static const MetaEnum::Value qsg_texture_anisotropy_table[] = { E(AnisotropyNone), E(Anisotropy2x), E(Anisotropy4x), E(Anisotropy8x), E(Anisotropy16x) }; #endif static const MetaEnum::Value qsg_texture_filtering_table[] = { E(None), E(Nearest), E(Linear) }; static const MetaEnum::Value qsg_texture_wrapmode_table[] = { E(Repeat), E(ClampToEdge), #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) E(MirroredRepeat) #endif }; #undef E static bool itemHasContents(QQuickItem *item) { return item->flags().testFlag(QQuickItem::ItemHasContents); } static bool isGoodCandidateItem(QQuickItem *item, bool ignoreItemHasContents = false) { if (!item->isVisible() || qFuzzyCompare(item->opacity() + qreal(1.0), qreal(1.0)) || (!ignoreItemHasContents && !itemHasContents(item))) { return false; } return true; } static QByteArray renderModeToString(QuickInspectorInterface::RenderMode customRenderMode) { switch (customRenderMode) { case QuickInspectorInterface::VisualizeClipping: return QByteArray("clip"); case QuickInspectorInterface::VisualizeOverdraw: return QByteArray("overdraw"); case QuickInspectorInterface::VisualizeBatches: return QByteArray("batches"); case QuickInspectorInterface::VisualizeChanges: return QByteArray("changes"); case QuickInspectorInterface::VisualizeTraces: case QuickInspectorInterface::NormalRendering: break; } return QByteArray(); } QMutex RenderModeRequest::mutex; RenderModeRequest::RenderModeRequest(QObject *parent) : QObject(parent) , mode(QuickInspectorInterface::NormalRendering) { } RenderModeRequest::~RenderModeRequest() { QMutexLocker lock(&mutex); window.clear(); if (connection) disconnect(connection); } void RenderModeRequest::applyOrDelay(QQuickWindow *toWindow, QuickInspectorInterface::RenderMode customRenderMode) { if (toWindow) { QMutexLocker lock(&mutex); // Qt does some performance optimizations that break custom render modes. // Thus the optimizations are only applied if there is no custom render mode set. // So we need to make the scenegraph recheck whether a custom render mode is set. // We do this by simply cleaning the scene graph which will recreate the renderer. // We need however to do that at the proper time from the gui thread. if (!connection || (mode != customRenderMode || window != toWindow)) { if (connection) disconnect(connection); mode = customRenderMode; window = toWindow; connection = connect(window.data(), &QQuickWindow::afterRendering, this, &RenderModeRequest::apply, Qt::DirectConnection); // trigger window update so afterRendering is emitted QMetaObject::invokeMethod(window, "update", Qt::QueuedConnection); } } } void RenderModeRequest::apply() { QMutexLocker lock(&mutex); if (connection) disconnect(connection); #if QT_VERSION == QT_VERSION_CHECK(5, 14, 0) || QT_VERSION == QT_VERSION_CHECK(5, 14, 1) // there's a regression in Qt 5.14... return; #endif #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) if (window && window->rendererInterface()->graphicsApi() != QSGRendererInterface::OpenGL) return; #endif if (window) { emit aboutToCleanSceneGraph(); const QByteArray mode = renderModeToString(RenderModeRequest::mode); QQuickWindowPrivate *winPriv = QQuickWindowPrivate::get(window); QMetaObject::invokeMethod(window, "cleanupSceneGraph", Qt::DirectConnection); winPriv->customRenderMode = mode; emit sceneGraphCleanedUp(); } QMetaObject::invokeMethod(this, "preFinished", Qt::QueuedConnection); } void RenderModeRequest::preFinished() { QMutexLocker lock(&mutex); if (window) window->update(); emit finished(); } QuickInspector::QuickInspector(Probe *probe, QObject *parent) : QuickInspectorInterface(parent) , m_probe(probe) , m_currentSgNode(nullptr) , m_itemModel(new QuickItemModel(this)) , m_sgModel(new QuickSceneGraphModel(this)) , m_itemPropertyController(new PropertyController(QStringLiteral("com.kdab.GammaRay.QuickItem"), this)) , m_sgPropertyController(new PropertyController(QStringLiteral( "com.kdab.GammaRay.QuickSceneGraph"), this)) , m_remoteView(new RemoteViewServer(QStringLiteral("com.kdab.GammaRay.QuickRemoteView"), this)) , m_pendingRenderMode(new RenderModeRequest(this)) , m_renderMode(QuickInspectorInterface::NormalRendering) , m_paintAnalyzer(new PaintAnalyzer(QStringLiteral("com.kdab.GammaRay.QuickPaintAnalyzer"), this)) , m_slowDownEnabled(false) { registerMetaTypes(); registerVariantHandlers(); probe->installGlobalEventFilter(this); QAbstractProxyModel *windowModel = new ObjectTypeFilterProxyModel(this); windowModel->setSourceModel(probe->objectListModel()); QAbstractProxyModel *proxy = new SingleColumnObjectProxyModel(this); proxy->setSourceModel(windowModel); m_windowModel = proxy; probe->registerModel(QStringLiteral("com.kdab.GammaRay.QuickWindowModel"), m_windowModel); auto filterProxy = new ServerProxyModel(this); filterProxy->setSourceModel(m_itemModel); filterProxy->addRole(ObjectModel::ObjectIdRole); probe->registerModel(QStringLiteral("com.kdab.GammaRay.QuickItemModel"), filterProxy); if (m_probe->needsObjectDiscovery()) { connect(m_probe, &Probe::objectCreated, this, &QuickInspector::objectCreated); } connect(probe, &Probe::objectCreated, m_itemModel, &QuickItemModel::objectAdded); connect(probe, &Probe::objectDestroyed, m_itemModel, &QuickItemModel::objectRemoved); connect(probe, &Probe::objectSelected, this, &QuickInspector::qObjectSelected); connect(probe, &Probe::nonQObjectSelected, this, &QuickInspector::nonQObjectSelected); m_itemSelectionModel = ObjectBroker::selectionModel(filterProxy); connect(m_itemSelectionModel, &QItemSelectionModel::selectionChanged, this, &QuickInspector::itemSelectionChanged); filterProxy = new ServerProxyModel(this); filterProxy->setSourceModel(m_sgModel); probe->registerModel(QStringLiteral("com.kdab.GammaRay.QuickSceneGraphModel"), filterProxy); m_sgSelectionModel = ObjectBroker::selectionModel(filterProxy); connect(m_sgSelectionModel, &QItemSelectionModel::selectionChanged, this, &QuickInspector::sgSelectionChanged); connect(m_sgModel, &QuickSceneGraphModel::nodeDeleted, this, &QuickInspector::sgNodeDeleted); connect(m_remoteView, &RemoteViewServer::elementsAtRequested, this, &QuickInspector::requestElementsAt); connect(this, &QuickInspector::elementsAtReceived, m_remoteView, &RemoteViewServer::elementsAtReceived); connect(m_remoteView, &RemoteViewServer::doPickElementId, this, &QuickInspector::pickElementId); connect(m_remoteView, &RemoteViewServer::requestUpdate, this, &QuickInspector::slotGrabWindow); connect(m_pendingRenderMode, &RenderModeRequest::aboutToCleanSceneGraph, this, &QuickInspector::aboutToCleanSceneGraph); connect(m_pendingRenderMode, &RenderModeRequest::sceneGraphCleanedUp, this, &QuickInspector::sceneGraphCleanedUp); #ifndef QT_NO_OPENGL auto texGrab = new QSGTextureGrabber(this); connect(probe, &Probe::objectCreated, texGrab, &QSGTextureGrabber::objectCreated); #endif connect(Endpoint::instance(), &Endpoint::disconnected, this, [this]() { if (m_overlay) m_overlay->placeOn(ItemOrLayoutFacade()); }); ProblemCollector::registerProblemChecker("com.kdab.GammaRay.QuickItemChecker", "QtQuick Item check", "Warns about items that are visible but out of view.", &QuickInspector::scanForProblems); // needs to be last, extensions require some of the above to be set up correctly registerPCExtensions(); } QuickInspector::~QuickInspector() { if (m_overlay) { disconnect(m_overlay.get(), &QObject::destroyed, this, &QuickInspector::recreateOverlay); } } void QuickInspector::selectWindow(int index) { const QModelIndex mi = m_windowModel->index(index, 0); QQuickWindow *window = mi.data(ObjectModel::ObjectRole).value(); selectWindow(window); } void QuickInspector::selectWindow(QQuickWindow *window) { if (m_window == window) { return; } if (m_window) { const QByteArray mode = QQuickWindowPrivate::get(m_window)->customRenderMode; if (!mode.isEmpty()) { auto reset = new RenderModeRequest(m_window); connect(reset, &RenderModeRequest::finished, reset, &RenderModeRequest::deleteLater); reset->applyOrDelay(m_window, QuickInspectorInterface::NormalRendering); } } m_window = window; m_itemModel->setWindow(window); m_sgModel->setWindow(window); m_remoteView->setEventReceiver(m_window); m_remoteView->resetView(); recreateOverlay(); if (m_window) { // make sure we have selected something for the property editor to not be entirely empty selectItem(m_window->contentItem()); m_window->update(); } checkFeatures(); if (m_window) setCustomRenderMode(m_renderMode); } void QuickInspector::selectItem(QQuickItem *item) { const QAbstractItemModel *model = m_itemSelectionModel->model(); Model::used(model); Model::used(m_sgSelectionModel->model()); const QModelIndexList indexList = model->match(model->index(0, 0), ObjectModel::ObjectRole, QVariant::fromValue(item), 1, Qt::MatchExactly | Qt::MatchRecursive | Qt::MatchWrap); if (indexList.isEmpty()) return; const QModelIndex index = indexList.first(); m_itemSelectionModel->select(index, QItemSelectionModel::Select |QItemSelectionModel::Clear |QItemSelectionModel::Rows |QItemSelectionModel::Current); } void QuickInspector::selectSGNode(QSGNode *node) { const QAbstractItemModel *model = m_sgSelectionModel->model(); Model::used(model); const QModelIndexList indexList = model->match(model->index(0, 0), ObjectModel::ObjectRole, QVariant::fromValue( node), 1, Qt::MatchExactly | Qt::MatchRecursive | Qt::MatchWrap); if (indexList.isEmpty()) return; const QModelIndex index = indexList.first(); m_sgSelectionModel->select(index, QItemSelectionModel::Select |QItemSelectionModel::Clear |QItemSelectionModel::Rows |QItemSelectionModel::Current); } void QuickInspector::qObjectSelected(QObject *object) { if (auto item = qobject_cast(object)) selectItem(item); else if (auto window = qobject_cast(object)) selectWindow(window); } void QuickInspector::nonQObjectSelected(void *object, const QString &typeName) { auto metaObject = MetaObjectRepository::instance()->metaObject(typeName); if (metaObject && metaObject->inherits(QStringLiteral("QSGNode"))) selectSGNode(reinterpret_cast(object)); } void QuickInspector::objectCreated(QObject *object) { if (QQuickWindow *window = qobject_cast(object)) { if (QQuickView *view = qobject_cast(object)) { m_probe->discoverObject(view->engine()); } else { QQmlContext *context = QQmlEngine::contextForObject(window); QQmlEngine *engine = context ? context->engine() : nullptr; if (!engine) { engine = qmlEngine(window->contentItem()->childItems().value(0)); } m_probe->discoverObject(engine); } } } void QuickInspector::recreateOverlay() { ProbeGuard guard; if (m_overlay) disconnect(m_overlay.get(), &QObject::destroyed, this, &QuickInspector::recreateOverlay); m_overlay = AbstractScreenGrabber::get(m_window); connect(m_overlay.get(), &AbstractScreenGrabber::grabberReadyChanged, m_remoteView, &RemoteViewServer::setGrabberReady); connect(m_overlay.get(), &AbstractScreenGrabber::sceneChanged, m_remoteView, &RemoteViewServer::sourceChanged); connect(m_overlay.get(), &AbstractScreenGrabber::sceneGrabbed, this, &QuickInspector::sendRenderedScene); // the target application might have destroyed the overlay widget // (e.g. because the parent of the overlay got destroyed). // just recreate a new one in this case connect(m_overlay.get(), &QObject::destroyed, this, &QuickInspector::recreateOverlay); //FIXME Is it really needed? // It is for the widget inspector, but for qt quick? connect(this, &QuickInspectorInterface::serverSideDecorationChanged, m_overlay.get(), &AbstractScreenGrabber::setDecorationsEnabled); m_overlay->setDecorationsEnabled(serverSideDecorationEnabled()); m_remoteView->setGrabberReady(true); } void QuickInspector::aboutToCleanSceneGraph() { m_sgModel->setWindow(nullptr); m_currentSgNode = nullptr; m_sgPropertyController->setObject(nullptr, QString()); } void QuickInspector::sceneGraphCleanedUp() { m_sgModel->setWindow(m_window); } void QuickInspector::sendRenderedScene(const GammaRay::GrabbedFrame &grabbedFrame) { RemoteViewFrame frame; frame.setImage(grabbedFrame.image, grabbedFrame.transform); frame.setSceneRect(grabbedFrame.itemsGeometryRect); frame.setViewRect(QRect(0, 0, m_window->width(), m_window->height())); if (m_overlay && m_overlay->settings().componentsTraces) frame.setData(QVariant::fromValue(grabbedFrame.itemsGeometry)); else if (!grabbedFrame.itemsGeometry.isEmpty()) frame.setData(QVariant::fromValue(grabbedFrame.itemsGeometry.at(0))); m_remoteView->sendFrame(frame); } void QuickInspector::slotGrabWindow() { if (!m_remoteView->isActive() || !m_window) return; Q_ASSERT(QThread::currentThread() == QCoreApplication::instance()->thread()); if (m_overlay) { m_overlay->requestGrabWindow(m_remoteView->userViewport()); } } void QuickInspector::setCustomRenderMode( GammaRay::QuickInspectorInterface::RenderMode customRenderMode) { #if QT_VERSION == QT_VERSION_CHECK(5, 14, 0) || QT_VERSION == QT_VERSION_CHECK(5, 14, 1) // there's a regression in Qt 5.14... return; #endif m_renderMode = customRenderMode; m_pendingRenderMode->applyOrDelay(m_window, customRenderMode); const bool tracing = customRenderMode == QuickInspectorInterface::VisualizeTraces; if (m_overlay && m_overlay->settings().componentsTraces != tracing) { auto settings = m_overlay->settings(); settings.componentsTraces = tracing; setOverlaySettings(settings); } } void QuickInspector::checkFeatures() { Features f; if (!m_window) { emit features(f); return; } #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) #if QT_VERSION != QT_VERSION_CHECK(5, 14, 0) && QT_VERSION != QT_VERSION_CHECK(5, 14, 1) if (m_window->rendererInterface()->graphicsApi() == QSGRendererInterface::OpenGL) f = AllCustomRenderModes; else #endif if (m_window->rendererInterface()->graphicsApi() == QSGRendererInterface::Software) f = AnalyzePainting; #else f = AllCustomRenderModes; #endif emit features(f); } void QuickInspector::setOverlaySettings(const GammaRay::QuickDecorationsSettings &settings) { if (!m_overlay) { emit overlaySettings(QuickDecorationsSettings()); // Let's not leave the client without an answer. return; } m_overlay->setSettings(settings); emit overlaySettings(m_overlay->settings()); } void QuickInspector::checkOverlaySettings() { emit overlaySettings(m_overlay ? m_overlay->settings() : QuickDecorationsSettings()); } #if QT_VERSION >= QT_VERSION_CHECK(5, 9, 3) // only with 5.9.3 the SW renderer got exported class SGSoftwareRendererPrivacyViolater : public QSGAbstractSoftwareRenderer { public: #if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0) && QT_VERSION < QT_VERSION_CHECK(5, 14, 0) using QSGAbstractSoftwareRenderer::renderableNodes; #endif using QSGAbstractSoftwareRenderer::renderNodes; using QSGAbstractSoftwareRenderer::buildRenderList; using QSGAbstractSoftwareRenderer::optimizeRenderList; }; #endif void QuickInspector::analyzePainting() { #if QT_VERSION >= QT_VERSION_CHECK(5, 9, 3) if (!m_window || m_window->rendererInterface()->graphicsApi() != QSGRendererInterface::Software || !PaintAnalyzer::isAvailable()) return; m_paintAnalyzer->beginAnalyzePainting(); m_paintAnalyzer->setBoundingRect(QRect(QPoint(), m_window->size())); { auto w = QQuickWindowPrivate::get(m_window); auto renderer = static_cast(w->renderer); // this replicates what QSGSoftwareRender is doing QPainter painter(m_paintAnalyzer->paintDevice()); painter.setRenderHint(QPainter::Antialiasing); auto rc = static_cast(w->renderer->context()); auto prevPainter = rc->m_activePainter; rc->m_activePainter = &painter; renderer->markDirty(); renderer->buildRenderList(); renderer->optimizeRenderList(); #if QT_VERSION < QT_VERSION_CHECK(5, 12, 0) || QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) renderer->renderNodes(&painter); #else auto iterator = renderer->renderableNodes().begin(); // First node is the background and needs to painted without blending auto backgroundNode = *iterator; backgroundNode->renderNode(&painter, /*force opaque painting*/ true); iterator++; for (; iterator != renderer->renderableNodes().end(); ++iterator) { auto node = *iterator; QQuickItem *origin = m_sgModel->itemForSgNode(node->handle()); m_paintAnalyzer->setOrigin(ObjectId(origin)); node->renderNode(&painter); } #endif rc->m_activePainter = prevPainter; } m_paintAnalyzer->endAnalyzePainting(); #endif } void QuickInspector::checkSlowMode() { // We can't check that for now as there is no getter for the property... emit slowModeChanged(m_slowDownEnabled); } void QuickInspector::setSlowMode(bool slow) { if (m_slowDownEnabled == slow) return; static QHash connections; m_slowDownEnabled = slow; for (int i = 0; i < m_windowModel->rowCount(); ++i) { const QModelIndex index = m_windowModel->index(i, 0); QQuickWindow *window = index.data(ObjectModel::ObjectRole).value(); auto it = connections.find(window); if (it == connections.end()) { connections.insert(window, connect(window, &QQuickWindow::beforeRendering, this, [this, window]() { auto it = connections.find(window); QUnifiedTimer::instance()->setSlowModeEnabled(m_slowDownEnabled); QObject::disconnect(it.value()); connections.erase(it); }, Qt::DirectConnection)); } } emit slowModeChanged(m_slowDownEnabled); } void QuickInspector::itemSelectionChanged(const QItemSelection &selection) { const QModelIndex index = selection.value(0).topLeft(); m_currentItem = index.data(ObjectModel::ObjectRole).value(); m_itemPropertyController->setObject(m_currentItem); // It might be that a sg-node is already selected that belongs to this item, but isn't the root // node of the Item. In this case we don't want to overwrite that selection. if (m_sgModel->itemForSgNode(m_currentSgNode) != m_currentItem) { m_currentSgNode = m_sgModel->sgNodeForItem(m_currentItem); const auto sourceIdx = m_sgModel->indexForNode(m_currentSgNode); auto proxy = qobject_cast(m_sgSelectionModel->model()); m_sgSelectionModel->select(proxy->mapFromSource(sourceIdx), QItemSelectionModel::Select |QItemSelectionModel::Clear |QItemSelectionModel::Rows |QItemSelectionModel::Current); } if (m_overlay) m_overlay->placeOn(m_currentItem.data()); } void QuickInspector::sgSelectionChanged(const QItemSelection &selection) { if (selection.isEmpty()) return; const QModelIndex index = selection.first().topLeft(); m_currentSgNode = index.data(ObjectModel::ObjectRole).value(); if (!m_sgModel->verifyNodeValidity(m_currentSgNode)) return; // Apparently the node has been deleted meanwhile, so don't access it. void *obj = m_currentSgNode; auto mo = MetaObjectRepository::instance()->metaObject(QStringLiteral("QSGNode"), obj); m_sgPropertyController->setObject(m_currentSgNode, mo->className()); m_currentItem = m_sgModel->itemForSgNode(m_currentSgNode); selectItem(m_currentItem); } void QuickInspector::sgNodeDeleted(QSGNode *node) { if (m_currentSgNode == node) m_sgPropertyController->setObject(nullptr, QString()); } void QuickInspector::requestElementsAt(const QPoint &pos, GammaRay::RemoteViewInterface::RequestMode mode) { if (!m_window) return; int bestCandidate; const ObjectIds objects = recursiveItemsAt(m_window->contentItem(), pos, mode, bestCandidate); if (!objects.isEmpty()) { emit elementsAtReceived(objects, bestCandidate); } } void QuickInspector::pickElementId(const GammaRay::ObjectId &id) { QQuickItem *item = id.asQObjectType(); if (item) m_probe->selectObject(item); } ObjectIds QuickInspector::recursiveItemsAt(QQuickItem *parent, const QPointF &pos, GammaRay::RemoteViewInterface::RequestMode mode, int &bestCandidate, bool parentIsGoodCandidate) const { Q_ASSERT(parent); ObjectIds objects; bestCandidate = -1; if (parentIsGoodCandidate) { // inherit the parent item opacity when looking for a good candidate item // i.e. QQuickItem::isVisible is taking the parent into account already, but // the opacity doesn't - we have to do this manually // Yet we have to ignore ItemHasContents apparently, as the QQuickRootItem // at least seems to not have this flag set. parentIsGoodCandidate = isGoodCandidateItem(parent, true); } auto childItems = parent->childItems(); std::stable_sort(childItems.begin(), childItems.end(), [](QQuickItem *lhs, QQuickItem *rhs){return lhs->z() < rhs->z();} ); for (int i = childItems.size() - 1; i >= 0; --i) { // backwards to match z order const auto child = childItems.at(i); const auto requestedPoint = parent->mapToItem(child, pos); if (!child->childItems().isEmpty() && (child->contains(requestedPoint) || child->childrenRect().contains(requestedPoint))) { const int count = objects.count(); int bc; // possibly better candidate among subChildren objects << recursiveItemsAt(child, requestedPoint, mode, bc, parentIsGoodCandidate); if (bestCandidate == -1 && parentIsGoodCandidate && bc != -1) { bestCandidate = count + bc; } } if (child->contains(requestedPoint)) { if (bestCandidate == -1 && parentIsGoodCandidate && isGoodCandidateItem(child)) { bestCandidate = objects.count(); } objects << ObjectId(child); } if (bestCandidate != -1 && mode == RemoteViewInterface::RequestBest) { break; } } if (bestCandidate == -1 && parentIsGoodCandidate && itemHasContents(parent)) { bestCandidate = objects.count(); } objects << ObjectId(parent); if (bestCandidate != -1 && mode == RemoteViewInterface::RequestBest) { objects = ObjectIds() << objects[bestCandidate]; bestCandidate = 0; } return objects; } void QuickInspector::scanForProblems() { const QVector &allObjects = Probe::instance()->allQObjects(); QMutexLocker lock(Probe::objectLock()); for (QObject *obj : allObjects) { QQuickItem *item; if (!Probe::instance()->isValidObject(obj) || !(item = qobject_cast(obj))) continue; QQuickItem *ancestor = item->parentItem(); auto rect = item->mapRectToScene(QRectF(0, 0, item->width(), item->height())); while (ancestor && item->window() && ancestor != item->window()->contentItem()) { if (ancestor->parentItem() == item->window()->contentItem() || ancestor->clip()) { auto ancestorRect = ancestor->mapRectToScene(QRectF(0, 0, ancestor->width(), ancestor->height())); if (!ancestorRect.contains(rect) && !rect.intersects(ancestorRect)) { Problem p; p.severity = Problem::Info; p.description = QStringLiteral("QtQuick: %1 %2 (0x%3) is visible, but out of view.").arg( ObjectDataProvider::typeName(item), ObjectDataProvider::name(item), QString::number(reinterpret_cast(item), 16) ); p.object = ObjectId(item); p.locations.push_back(ObjectDataProvider::creationLocation(item)); p.problemId = QStringLiteral("com.kdab.GammaRay.QuickItemChecker.OutOfView:%1").arg(reinterpret_cast(item)); p.findingCategory = Problem::Scan; ProblemCollector::addProblem(p); break; } } ancestor = ancestor->parentItem(); } } } bool QuickInspector::eventFilter(QObject *receiver, QEvent *event) { if (event->type() == QEvent::MouseButtonRelease) { QMouseEvent *mouseEv = static_cast(event); if (mouseEv->button() == Qt::LeftButton && mouseEv->modifiers() == (Qt::ControlModifier | Qt::ShiftModifier)) { QQuickWindow *window = qobject_cast(receiver); if (window && window->contentItem()) { int bestCandidate; const ObjectIds objects = recursiveItemsAt(window->contentItem(), mouseEv->pos(), RemoteViewInterface::RequestBest, bestCandidate); m_probe->selectObject(objects.value(bestCandidate == -1 ? 0 : bestCandidate).asQObject()); } } } return QObject::eventFilter(receiver, event); } void QuickInspector::registerMetaTypes() { MetaObject *mo = nullptr; MO_ADD_METAOBJECT1(QQuickWindow, QWindow); MO_ADD_PROPERTY(QQuickWindow, clearBeforeRendering, setClearBeforeRendering); MO_ADD_PROPERTY_RO(QQuickWindow, effectiveDevicePixelRatio); #ifndef QT_NO_OPENGL MO_ADD_PROPERTY(QQuickWindow, isPersistentOpenGLContext, setPersistentOpenGLContext); #endif MO_ADD_PROPERTY(QQuickWindow, isPersistentSceneGraph, setPersistentSceneGraph); MO_ADD_PROPERTY_RO(QQuickWindow, mouseGrabberItem); #ifndef QT_NO_OPENGL MO_ADD_PROPERTY_RO(QQuickWindow, openglContext); #endif MO_ADD_PROPERTY_RO(QQuickWindow, renderTargetId); #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) MO_ADD_PROPERTY_RO(QQuickWindow, rendererInterface); MO_ADD_METAOBJECT0(QSGRendererInterface); MO_ADD_PROPERTY_RO(QSGRendererInterface, graphicsApi); MO_ADD_PROPERTY_RO(QSGRendererInterface, shaderCompilationType); MO_ADD_PROPERTY_RO(QSGRendererInterface, shaderSourceType); MO_ADD_PROPERTY_RO(QSGRendererInterface, shaderType); #endif MO_ADD_METAOBJECT1(QQuickView, QQuickWindow); MO_ADD_PROPERTY_RO(QQuickView, engine); MO_ADD_PROPERTY_RO(QQuickView, errors); MO_ADD_PROPERTY_RO(QQuickView, initialSize); MO_ADD_PROPERTY_RO(QQuickView, rootContext); MO_ADD_PROPERTY_RO(QQuickView, rootObject); MO_ADD_METAOBJECT1(QQuickItem, QObject); MO_ADD_PROPERTY(QQuickItem, acceptHoverEvents, setAcceptHoverEvents); MO_ADD_PROPERTY(QQuickItem, acceptedMouseButtons, setAcceptedMouseButtons); MO_ADD_PROPERTY(QQuickItem, cursor, setCursor); MO_ADD_PROPERTY(QQuickItem, filtersChildMouseEvents, setFiltersChildMouseEvents); MO_ADD_PROPERTY(QQuickItem, flags, setFlags); MO_ADD_PROPERTY_RO(QQuickItem, isFocusScope); MO_ADD_PROPERTY_RO(QQuickItem, isTextureProvider); MO_ADD_PROPERTY(QQuickItem, keepMouseGrab, setKeepMouseGrab); MO_ADD_PROPERTY(QQuickItem, keepTouchGrab, setKeepTouchGrab); MO_ADD_PROPERTY_LD(QQuickItem, nextItemInFocusChain, [](QQuickItem *item) { return item->isVisible() ? item->nextItemInFocusChain() : nullptr; }); MO_ADD_PROPERTY_LD(QQuickItem, previousItemInFocusChain, [](QQuickItem *item) { return item->isVisible() ? item->nextItemInFocusChain(false) : nullptr; }); MO_ADD_PROPERTY_RO(QQuickItem, scopedFocusItem); MO_ADD_PROPERTY_RO(QQuickItem, window); MO_ADD_METAOBJECT1(QQuickPaintedItem, QQuickItem); MO_ADD_PROPERTY_RO(QQuickPaintedItem, contentsBoundingRect); MO_ADD_PROPERTY(QQuickPaintedItem, mipmap, setMipmap); MO_ADD_PROPERTY(QQuickPaintedItem, opaquePainting, setOpaquePainting); MO_ADD_PROPERTY(QQuickPaintedItem, performanceHints, setPerformanceHints); MO_ADD_METAOBJECT1(QSGTexture, QObject); #if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0) MO_ADD_PROPERTY(QSGTexture, anisotropyLevel, setAnisotropyLevel); #endif MO_ADD_PROPERTY(QSGTexture, filtering, setFiltering); MO_ADD_PROPERTY_RO(QSGTexture, hasAlphaChannel); MO_ADD_PROPERTY_RO(QSGTexture, hasMipmaps); MO_ADD_PROPERTY(QSGTexture, horizontalWrapMode, setHorizontalWrapMode); MO_ADD_PROPERTY_RO(QSGTexture, isAtlasTexture); MO_ADD_PROPERTY(QSGTexture, mipmapFiltering, setMipmapFiltering); MO_ADD_PROPERTY_RO(QSGTexture, normalizedTextureSubRect); // crashes without a current GL context //MO_ADD_PROPERTY_RO(QSGTexture, textureId); MO_ADD_PROPERTY_RO(QSGTexture, textureSize); MO_ADD_PROPERTY(QSGTexture, verticalWrapMode, setVerticalWrapMode); MO_ADD_METAOBJECT0(QSGNode); MO_ADD_PROPERTY_RO(QSGNode, parent); MO_ADD_PROPERTY_RO(QSGNode, childCount); MO_ADD_PROPERTY_RO(QSGNode, flags); MO_ADD_PROPERTY_RO(QSGNode, isSubtreeBlocked); MO_ADD_PROPERTY(QSGNode, dirtyState, markDirty); MO_ADD_METAOBJECT1(QSGBasicGeometryNode, QSGNode); MO_ADD_PROPERTY_O1(QSGBasicGeometryNode, geometry); MO_ADD_PROPERTY_RO(QSGBasicGeometryNode, matrix); MO_ADD_PROPERTY_RO(QSGBasicGeometryNode, clipList); MO_ADD_METAOBJECT1(QSGGeometryNode, QSGBasicGeometryNode); MO_ADD_PROPERTY(QSGGeometryNode, material, setMaterial); MO_ADD_PROPERTY(QSGGeometryNode, opaqueMaterial, setOpaqueMaterial); MO_ADD_PROPERTY_RO(QSGGeometryNode, activeMaterial); MO_ADD_PROPERTY(QSGGeometryNode, renderOrder, setRenderOrder); MO_ADD_PROPERTY(QSGGeometryNode, inheritedOpacity, setInheritedOpacity); MO_ADD_METAOBJECT1(QSGClipNode, QSGBasicGeometryNode); MO_ADD_PROPERTY(QSGClipNode, isRectangular, setIsRectangular); MO_ADD_PROPERTY(QSGClipNode, clipRect, setClipRect); MO_ADD_METAOBJECT1(QSGTransformNode, QSGNode); MO_ADD_PROPERTY(QSGTransformNode, matrix, setMatrix); MO_ADD_PROPERTY(QSGTransformNode, combinedMatrix, setCombinedMatrix); MO_ADD_METAOBJECT1(QSGRootNode, QSGNode); MO_ADD_METAOBJECT1(QSGOpacityNode, QSGNode); MO_ADD_PROPERTY(QSGOpacityNode, opacity, setOpacity); MO_ADD_PROPERTY(QSGOpacityNode, combinedOpacity, setCombinedOpacity); #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) MO_ADD_METAOBJECT1(QSGRenderNode, QSGNode); MO_ADD_PROPERTY_RO(QSGRenderNode, changedStates); MO_ADD_PROPERTY_RO(QSGRenderNode, flags); MO_ADD_PROPERTY_RO(QSGRenderNode, rect); MO_ADD_PROPERTY_RO(QSGRenderNode, inheritedOpacity); MO_ADD_PROPERTY_RO(QSGRenderNode, matrix); MO_ADD_PROPERTY_RO(QSGRenderNode, clipList); #endif MO_ADD_METAOBJECT0(QSGMaterial); MO_ADD_PROPERTY_RO(QSGMaterial, flags); MO_ADD_METAOBJECT1(QSGFlatColorMaterial, QSGMaterial); MO_ADD_PROPERTY(QSGFlatColorMaterial, color, setColor); MO_ADD_METAOBJECT1(QSGOpaqueTextureMaterial, QSGMaterial); MO_ADD_PROPERTY(QSGOpaqueTextureMaterial, filtering, setFiltering); MO_ADD_PROPERTY(QSGOpaqueTextureMaterial, horizontalWrapMode, setHorizontalWrapMode); MO_ADD_PROPERTY(QSGOpaqueTextureMaterial, mipmapFiltering, setMipmapFiltering); MO_ADD_PROPERTY(QSGOpaqueTextureMaterial, texture, setTexture); MO_ADD_PROPERTY(QSGOpaqueTextureMaterial, verticalWrapMode, setVerticalWrapMode); MO_ADD_METAOBJECT1(QSGTextureMaterial, QSGOpaqueTextureMaterial); MO_ADD_METAOBJECT1(QSGVertexColorMaterial, QSGMaterial); #ifndef QT_NO_OPENGL MO_ADD_METAOBJECT1(QSGDistanceFieldTextMaterial, QSGMaterial); MO_ADD_PROPERTY_RO(QSGDistanceFieldTextMaterial, color); MO_ADD_PROPERTY_RO(QSGDistanceFieldTextMaterial, fontScale); MO_ADD_PROPERTY_RO(QSGDistanceFieldTextMaterial, textureSize); MO_ADD_METAOBJECT1(QSGDistanceFieldStyledTextMaterial, QSGDistanceFieldTextMaterial); MO_ADD_PROPERTY_RO(QSGDistanceFieldStyledTextMaterial, styleColor); MO_ADD_METAOBJECT1(QSGDistanceFieldShiftedStyleTextMaterial, QSGDistanceFieldStyledTextMaterial); MO_ADD_PROPERTY_RO(QSGDistanceFieldShiftedStyleTextMaterial, shift); #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) MO_ADD_METAOBJECT1(QQuickOpenGLShaderEffectMaterial, QSGMaterial); MO_ADD_PROPERTY_MEM(QQuickOpenGLShaderEffectMaterial, attributes); MO_ADD_PROPERTY_MEM(QQuickOpenGLShaderEffectMaterial, cullMode); MO_ADD_PROPERTY_MEM(QQuickOpenGLShaderEffectMaterial, geometryUsesTextureSubRect); MO_ADD_PROPERTY_MEM(QQuickOpenGLShaderEffectMaterial, textureProviders); #endif #endif } #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) #define E(x) { QSGRendererInterface:: x, #x } static const MetaEnum::Value qsg_graphics_api_table[] = { E(Unknown), E(Software), E(OpenGL), E(Direct3D12), #if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0) E(OpenVG) #endif }; static const MetaEnum::Value qsg_shader_compilation_type_table[] = { E(RuntimeCompilation), E(OfflineCompilation) }; static const MetaEnum::Value qsg_shader_source_type_table[] = { E(ShaderSourceString), E(ShaderSourceFile), E(ShaderByteCode) }; static const MetaEnum::Value qsg_shader_type_table[] = { E(UnknownShadingLanguage), E(GLSL), E(HLSL) }; #undef E #endif #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) #define E(x) { QSGRenderNode:: x, #x } static const MetaEnum::Value render_node_state_flags_table[] = { E(DepthState), E(StencilState), E(ScissorState), E(ColorState), E(BlendState), E(CullState), E(CullState), E(ViewportState), E(RenderTargetState) }; static const MetaEnum::Value render_node_rendering_flags_table[] = { E(BoundedRectRendering), E(DepthAwareRendering), E(OpaqueRendering) }; #undef E #endif static QString anchorLineToString(const QQuickAnchorLine &line) { if (!line.item #if QT_VERSION >= QT_VERSION_CHECK(5, 7, 0) || line.anchorLine == QQuickAnchors::InvalidAnchor #endif ) { return QStringLiteral(""); } const auto s = Util::shortDisplayString(line.item); switch (line.anchorLine) { case QQuickAnchors::LeftAnchor: return s + QStringLiteral(".left"); case QQuickAnchors::RightAnchor: return s + QStringLiteral(".right"); case QQuickAnchors::TopAnchor: return s + QStringLiteral(".top"); case QQuickAnchors::BottomAnchor: return s + QStringLiteral(".bottom"); case QQuickAnchors::HCenterAnchor: return s + QStringLiteral(".horizontalCenter"); case QQuickAnchors::VCenterAnchor: return s + QStringLiteral(".verticalCenter"); case QQuickAnchors::BaselineAnchor: return s + QStringLiteral(".baseline"); default: break; } return s; } void QuickInspector::registerVariantHandlers() { ER_REGISTER_FLAGS(QQuickItem, Flags, qqitem_flag_table); ER_REGISTER_FLAGS(QSGNode, DirtyState, qsg_node_dirtystate_table); ER_REGISTER_FLAGS(QSGNode, Flags, qsg_node_flag_table); #if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0) ER_REGISTER_ENUM(QSGTexture, AnisotropyLevel, qsg_texture_anisotropy_table); #endif ER_REGISTER_ENUM(QSGTexture, Filtering, qsg_texture_filtering_table); ER_REGISTER_ENUM(QSGTexture, WrapMode, qsg_texture_wrapmode_table); VariantHandler::registerStringConverter( qQuickPaintedItemPerformanceHintsToString); VariantHandler::registerStringConverter(anchorLineToString); VariantHandler::registerStringConverter(Util::addressToString); VariantHandler::registerStringConverter(Util::addressToString); VariantHandler::registerStringConverter(Util::addressToString); VariantHandler::registerStringConverter(Util::addressToString); VariantHandler::registerStringConverter(Util::addressToString); VariantHandler::registerStringConverter(Util::addressToString); VariantHandler::registerStringConverter(Util::addressToString); VariantHandler::registerStringConverter(Util::addressToString); VariantHandler::registerStringConverter(Util::addressToString); VariantHandler::registerStringConverter(Util::addressToString); VariantHandler::registerStringConverter(Util::addressToString); VariantHandler::registerStringConverter(qsgMaterialFlagsToString); #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) VariantHandler::registerStringConverter(Util::addressToString); VariantHandler::registerStringConverter(MetaEnum::flagsToString_fn(render_node_state_flags_table)); VariantHandler::registerStringConverter(MetaEnum::flagsToString_fn(render_node_rendering_flags_table)); #endif #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) VariantHandler::registerStringConverter(Util::addressToString); VariantHandler::registerStringConverter(MetaEnum::enumToString_fn(qsg_graphics_api_table)); VariantHandler::registerStringConverter(MetaEnum::flagsToString_fn(qsg_shader_compilation_type_table)); VariantHandler::registerStringConverter(MetaEnum::flagsToString_fn(qsg_shader_source_type_table)); VariantHandler::registerStringConverter(MetaEnum::enumToString_fn(qsg_shader_type_table)); #endif } void QuickInspector::registerPCExtensions() { #ifndef QT_NO_OPENGL PropertyController::registerExtension(); PropertyController::registerExtension(); PropertyController::registerExtension(); PropertyController::registerExtension(); PropertyAdaptorFactory::registerFactory(QQuickOpenGLShaderEffectMaterialAdaptorFactory::instance()); #endif PropertyAdaptorFactory::registerFactory(QuickAnchorsPropertyAdaptorFactory::instance()); PropertyFilters::registerFilter(PropertyFilter("QQuickItem", "anchors")); #if QT_VERSION >= QT_VERSION_CHECK(5, 7, 0) BindingAggregator::registerBindingProvider(std::unique_ptr(new QuickImplicitBindingDependencyProvider)); #endif } gammaray-2.11.3/plugins/quickinspector/quickinspector.h000066400000000000000000000132151413310443000233270ustar00rootroot00000000000000/* qmlsupport.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_QUICKINSPECTOR_QUICKINSPECTOR_H #define GAMMARAY_QUICKINSPECTOR_QUICKINSPECTOR_H #include "quickinspectorinterface.h" #include #include #include #include #include #include QT_BEGIN_NAMESPACE class QQuickShaderEffectSource; class QAbstractItemModel; class QItemSelection; class QItemSelectionModel; class QSGNode; // class QSGBasicGeometryNode; // class QSGGeometryNode; // class QSGClipNode; // class QSGTransformNode; // class QSGRootNode; // class QSGOpacityNode; QT_END_NAMESPACE namespace GammaRay { class PropertyController; class AbstractScreenGrabber; class GrabbedFrame; struct QuickDecorationsSettings; class QuickItemModel; class QuickSceneGraphModel; class RemoteViewServer; class ObjectId; class PaintAnalyzer; using ObjectIds = QVector; class RenderModeRequest : public QObject { Q_OBJECT public: explicit RenderModeRequest(QObject *parent = nullptr); ~RenderModeRequest() override; void applyOrDelay(QQuickWindow *toWindow, QuickInspectorInterface::RenderMode customRenderMode); signals: void aboutToCleanSceneGraph(); void sceneGraphCleanedUp(); void finished(); private slots: void apply(); void preFinished(); private: static QMutex mutex; QuickInspectorInterface::RenderMode mode; QMetaObject::Connection connection; QPointer window; }; class QuickInspector : public QuickInspectorInterface { Q_OBJECT Q_INTERFACES(GammaRay::QuickInspectorInterface) public: explicit QuickInspector(Probe *probe, QObject *parent = nullptr); ~QuickInspector() override; signals: void elementsAtReceived(const GammaRay::ObjectIds &ids, int bestCandidate); public slots: void selectWindow(int index) override; void setCustomRenderMode(GammaRay::QuickInspectorInterface::RenderMode customRenderMode) override; void checkFeatures() override; void setOverlaySettings(const GammaRay::QuickDecorationsSettings &settings) override; void checkOverlaySettings() override; void requestElementsAt(const QPoint &pos, GammaRay::RemoteViewInterface::RequestMode mode); void pickElementId(const GammaRay::ObjectId& id); void sendRenderedScene(const GammaRay::GrabbedFrame &grabbedFrame); void analyzePainting() override; void checkSlowMode() override; void setSlowMode(bool slow) override; protected: bool eventFilter(QObject *receiver, QEvent *event) override; private slots: void slotGrabWindow(); void itemSelectionChanged(const QItemSelection &selection); void sgSelectionChanged(const QItemSelection &selection); void sgNodeDeleted(QSGNode *node); void qObjectSelected(QObject *object); void nonQObjectSelected(void *object, const QString &typeName); void objectCreated(QObject *object); void recreateOverlay(); void aboutToCleanSceneGraph(); void sceneGraphCleanedUp(); private: void selectWindow(QQuickWindow *window); void selectItem(QQuickItem *item); void selectSGNode(QSGNode *node); void registerMetaTypes(); void registerVariantHandlers(); void registerPCExtensions(); QString findSGNodeType(QSGNode *node) const; static void scanForProblems(); GammaRay::ObjectIds recursiveItemsAt(QQuickItem *parent, const QPointF &pos, GammaRay::RemoteViewInterface::RequestMode mode, int& bestCandidate, bool parentIsGoodCandidate = true) const; Probe *m_probe; std::unique_ptr m_overlay; QPointer m_window; QPointer m_currentItem; QSGNode *m_currentSgNode; QAbstractItemModel *m_windowModel; QuickItemModel *m_itemModel; QItemSelectionModel *m_itemSelectionModel; QuickSceneGraphModel *m_sgModel; QItemSelectionModel *m_sgSelectionModel; PropertyController *m_itemPropertyController; PropertyController *m_sgPropertyController; RemoteViewServer *m_remoteView; RenderModeRequest *m_pendingRenderMode; QuickInspectorInterface::RenderMode m_renderMode; PaintAnalyzer* m_paintAnalyzer; bool m_slowDownEnabled; }; class QuickInspectorFactory : public QObject, public StandardToolFactory { Q_OBJECT Q_INTERFACES(GammaRay::ToolFactory) Q_PLUGIN_METADATA(IID "com.kdab.GammaRay.ToolFactory" FILE "gammaray_quickinspector.json") public: explicit QuickInspectorFactory(QObject *parent = nullptr) : QObject(parent) { } }; } #endif gammaray-2.11.3/plugins/quickinspector/quickinspectorclient.cpp000066400000000000000000000060121413310443000250560ustar00rootroot00000000000000/* quickinspectorclient.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "quickinspectorclient.h" #include "quickdecorationsdrawer.h" #include #include using namespace GammaRay; QuickInspectorClient::QuickInspectorClient(QObject *parent) : QuickInspectorInterface(parent) { } QuickInspectorClient::~QuickInspectorClient() = default; void QuickInspectorClient::selectWindow(int index) { Endpoint::instance()->invokeObject(objectName(), "selectWindow", QVariantList() << index); } void QuickInspectorClient::setCustomRenderMode( GammaRay::QuickInspectorInterface::RenderMode customRenderMode) { Endpoint::instance()->invokeObject(objectName(), "setCustomRenderMode", QVariantList() << QVariant::fromValue(customRenderMode)); } void QuickInspectorClient::checkFeatures() { Endpoint::instance()->invokeObject(objectName(), "checkFeatures"); } void QuickInspectorClient::setOverlaySettings(const GammaRay::QuickDecorationsSettings &settings) { Endpoint::instance()->invokeObject(objectName(), "setOverlaySettings", QVariantList() << QVariant::fromValue(settings)); } void QuickInspectorClient::checkOverlaySettings() { Endpoint::instance()->invokeObject(objectName(), "checkOverlaySettings"); } void QuickInspectorClient::analyzePainting() { Endpoint::instance()->invokeObject(objectName(), "analyzePainting"); } void QuickInspectorClient::checkSlowMode() { Endpoint::instance()->invokeObject(objectName(), "checkSlowMode"); } void QuickInspectorClient::setSlowMode(bool slow) { Endpoint::instance()->invokeObject(objectName(), "setSlowMode", QVariantList() << slow); } gammaray-2.11.3/plugins/quickinspector/quickinspectorclient.h000066400000000000000000000041311413310443000245230ustar00rootroot00000000000000/* quickinspectorclient.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_QUICKINSPECTOR_QUICKINSPECTORCLIENT_H #define GAMMARAY_QUICKINSPECTOR_QUICKINSPECTORCLIENT_H #include "quickinspectorinterface.h" QT_BEGIN_NAMESPACE class QEvent; QT_END_NAMESPACE namespace GammaRay { struct QuickDecorationsSettings; class QuickInspectorClient : public QuickInspectorInterface { Q_OBJECT Q_INTERFACES(GammaRay::QuickInspectorInterface) public: explicit QuickInspectorClient(QObject *parent = nullptr); ~QuickInspectorClient() override; public slots: void selectWindow(int index) override; void setCustomRenderMode(GammaRay::QuickInspectorInterface::RenderMode customRenderMode) override; void checkFeatures() override; void setOverlaySettings(const GammaRay::QuickDecorationsSettings &settings) override; void checkOverlaySettings() override; void analyzePainting() override; void checkSlowMode() override; void setSlowMode(bool slow) override; }; } #endif // GAMMARAY_QUICKINSPECTORCLIENT_H gammaray-2.11.3/plugins/quickinspector/quickinspectorinterface.cpp000066400000000000000000000055271413310443000255520ustar00rootroot00000000000000/* quickinspectorinterface.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "quickinspectorinterface.h" #include "quickitemmodelroles.h" #include "quickdecorationsdrawer.h" #include #include using namespace GammaRay; namespace GammaRay { QDataStream &operator<<(QDataStream &out, QuickInspectorInterface::Features value) { out << qint32(value); return out; } QDataStream &operator>>(QDataStream &in, QuickInspectorInterface::Features &value) { qint32 t; in >> t; value = static_cast(t); return in; } QDataStream &operator<<(QDataStream &out, QuickInspectorInterface::RenderMode value) { out << qint32(value); return out; } QDataStream &operator>>(QDataStream &in, QuickInspectorInterface::RenderMode &value) { qint32 t; in >> t; value = static_cast(t); return in; } } QuickInspectorInterface::QuickInspectorInterface(QObject *parent) : QObject(parent) , m_serverSideDecoration(false) { ObjectBroker::registerObject(this); qRegisterMetaTypeStreamOperators(); qRegisterMetaTypeStreamOperators(); qRegisterMetaTypeStreamOperators(); qRegisterMetaTypeStreamOperators>(); qRegisterMetaTypeStreamOperators(); } QuickInspectorInterface::~QuickInspectorInterface() = default; bool QuickInspectorInterface::serverSideDecorationEnabled() const { return m_serverSideDecoration; } void QuickInspectorInterface::setServerSideDecorationsEnabled(bool enabled) { if (m_serverSideDecoration == enabled) return; m_serverSideDecoration = enabled; emit serverSideDecorationChanged(enabled); } gammaray-2.11.3/plugins/quickinspector/quickinspectorinterface.h000066400000000000000000000073261413310443000252160ustar00rootroot00000000000000/* quickinspectorinterface.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_QUICKINSPECTOR_QUICKINSPECTORINTERFACE_H #define GAMMARAY_QUICKINSPECTOR_QUICKINSPECTORINTERFACE_H #include #include "quickitemgeometry.h" #include #include #include #include QT_BEGIN_NAMESPACE class QImage; QT_END_NAMESPACE namespace GammaRay { struct QuickDecorationsSettings; class QuickInspectorInterface : public QObject { Q_OBJECT Q_PROPERTY(bool serverSideDecoration READ serverSideDecorationEnabled WRITE setServerSideDecorationsEnabled NOTIFY serverSideDecorationChanged) public: enum Feature { NoFeatures = 0, CustomRenderModeClipping = 1, CustomRenderModeOverdraw = 2, CustomRenderModeBatches = 4, CustomRenderModeChanges = 8, CustomRenderModeTraces = 16, AllCustomRenderModes = CustomRenderModeClipping | CustomRenderModeOverdraw | CustomRenderModeBatches | CustomRenderModeChanges | CustomRenderModeTraces, AnalyzePainting }; enum RenderMode { NormalRendering, VisualizeClipping, VisualizeOverdraw, VisualizeBatches, VisualizeChanges, VisualizeTraces, }; Q_ENUMS(RenderMode) Q_DECLARE_FLAGS(Features, Feature) explicit QuickInspectorInterface(QObject *parent = nullptr); ~QuickInspectorInterface() override; bool serverSideDecorationEnabled() const; void setServerSideDecorationsEnabled(bool enabled); public slots: virtual void selectWindow(int index) = 0; virtual void setCustomRenderMode( GammaRay::QuickInspectorInterface::RenderMode customRenderMode) = 0; virtual void checkFeatures() = 0; virtual void setOverlaySettings(const GammaRay::QuickDecorationsSettings &settings) = 0; virtual void checkOverlaySettings() = 0; virtual void analyzePainting() = 0; virtual void checkSlowMode() = 0; virtual void setSlowMode(bool slow) = 0; signals: void features(GammaRay::QuickInspectorInterface::Features features); void serverSideDecorationChanged(bool enabled); void overlaySettings(const GammaRay::QuickDecorationsSettings &settings); void slowModeChanged(bool slow); private: bool m_serverSideDecoration; }; } Q_DECLARE_METATYPE(GammaRay::QuickInspectorInterface::Features) Q_DECLARE_METATYPE(GammaRay::QuickInspectorInterface::RenderMode) QT_BEGIN_NAMESPACE Q_DECLARE_INTERFACE(GammaRay::QuickInspectorInterface, "com.kdab.GammaRay.QuickInspectorInterface/1.0") QT_END_NAMESPACE #endif // GAMMARAY_QUICKINSPECTORINTERFACE_H gammaray-2.11.3/plugins/quickinspector/quickinspectorwidget.cpp000066400000000000000000000341311413310443000250660ustar00rootroot00000000000000/* quickinspectorwidget.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "quickinspectorwidget.h" #include "quickinspectorclient.h" #include "quickclientitemmodel.h" #include "quickitemtreewatcher.h" #include "quickitemmodelroles.h" #include "quickscenepreviewwidget.h" #ifndef QT_NO_OPENGL #include "geometryextension/sggeometrytab.h" #include "materialextension/materialextensionclient.h" #include "materialextension/materialtab.h" #include "textureextension/texturetab.h" #endif #include "quickitemdelegate.h" #include "ui_quickinspectorwidget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace GammaRay; static QObject *createQuickInspectorClient(const QString & /*name*/, QObject *parent) { return new QuickInspectorClient(parent); } #ifndef QT_NO_OPENGL static QObject *createMaterialExtension(const QString &name, QObject *parent) { return new MaterialExtensionClient(name, parent); } #endif static QAction *createSeparator(QObject *parent) { QAction *action = new QAction(parent); action->setSeparator(true); return action; } QuickInspectorWidget::QuickInspectorWidget(QWidget *parent) : QWidget(parent) , ui(new Ui::QuickInspectorWidget) , m_state(QuickInspectorWidget::WaitingAll) , m_stateManager(this) { qRegisterMetaType(); ui->setupUi(this); ObjectBroker::registerClientObjectFactoryCallback( createQuickInspectorClient); m_interface = ObjectBroker::object(); ui->windowComboBox->setModel(ObjectBroker::model(QStringLiteral( "com.kdab.GammaRay.QuickWindowModel"))); connect(ui->windowComboBox, static_cast(&QComboBox::currentIndexChanged), m_interface, &QuickInspectorInterface::selectWindow); if (ui->windowComboBox->currentIndex() >= 0) m_interface->selectWindow(ui->windowComboBox->currentIndex()); auto model = ObjectBroker::model(QStringLiteral("com.kdab.GammaRay.QuickItemModel")); auto proxy = new QuickClientItemModel(this); proxy->setSourceModel(model); ui->itemTreeView->header()->setObjectName("quickItemTreeViewHeader"); ui->itemTreeView->setDeferredResizeMode(0, QHeaderView::ResizeToContents); ui->itemTreeView->setModel(proxy); ui->itemTreeView->setItemDelegate(new QuickItemDelegate(ui->itemTreeView)); new SearchLineController(ui->itemTreeSearchLine, proxy); QItemSelectionModel *selectionModel = ObjectBroker::selectionModel(proxy); ui->itemTreeView->setSelectionModel(selectionModel); connect(selectionModel, &QItemSelectionModel::selectionChanged, this, &QuickInspectorWidget::itemSelectionChanged); connect(proxy, &QAbstractItemModel::dataChanged, this, &QuickInspectorWidget::itemModelDataChanged); model = ObjectBroker::model(QStringLiteral("com.kdab.GammaRay.QuickSceneGraphModel")); auto *clientSceneGraphModel = new ClientDecorationIdentityProxyModel(this); clientSceneGraphModel->setSourceModel(model); ui->sgTreeView->header()->setObjectName("sceneGraphTreeViewHeader"); ui->sgTreeView->setDeferredResizeMode(0, QHeaderView::ResizeToContents); ui->sgTreeView->setModel(clientSceneGraphModel); new SearchLineController(ui->sgTreeSearchLine, clientSceneGraphModel); QItemSelectionModel *sgSelectionModel = ObjectBroker::selectionModel(clientSceneGraphModel); ui->sgTreeView->setSelectionModel(sgSelectionModel); connect(sgSelectionModel, &QItemSelectionModel::selectionChanged, this, &QuickInspectorWidget::sgSelectionChanged); new QuickItemTreeWatcher(ui->itemTreeView, ui->sgTreeView, this); m_scenePreviewWidget = new QuickSceneControlWidget(m_interface, this); m_scenePreviewWidget->previewWidget()->setPickSourceModel(proxy); m_scenePreviewWidget->previewWidget()->setFlagRole(QuickItemModelRole::ItemFlags); m_scenePreviewWidget->previewWidget()->setInvisibleMask(QuickItemModelRole::Invisible | QuickItemModelRole::ZeroSize); ui->itemPropertyWidget->setObjectBaseName(QStringLiteral("com.kdab.GammaRay.QuickItem")); ui->sgPropertyWidget->setObjectBaseName(QStringLiteral("com.kdab.GammaRay.QuickSceneGraph")); ui->previewTreeSplitter->addWidget(m_scenePreviewWidget); connect(m_interface, &QuickInspectorInterface::features, this, &QuickInspectorWidget::setFeatures); connect(m_interface, &QuickInspectorInterface::serverSideDecorationChanged, m_scenePreviewWidget, &QuickSceneControlWidget::setServerSideDecorationsEnabled); connect(m_interface, &QuickInspectorInterface::overlaySettings, this, &QuickInspectorWidget::setOverlaySettings); connect(m_interface, &QuickInspectorInterface::slowModeChanged, this, &QuickInspectorWidget::setSlowMode); connect(ui->itemTreeView, &QWidget::customContextMenuRequested, this, &QuickInspectorWidget::itemContextMenu); m_interface->checkFeatures(); m_interface->checkOverlaySettings(); m_interface->checkSlowMode(); addActions(m_scenePreviewWidget->actions()); addAction(createSeparator(this)); addAction(ui->actionAnalyzePainting); addAction(createSeparator(this)); addAction(ui->actionSaveAsImage); addAction(ui->actionSaveAsImageWithDecoration); addAction(createSeparator(this)); addAction(ui->actionSlowDownMode); m_stateManager.setDefaultSizes(ui->mainSplitter, UISizeVector() << "50%" << "50%"); m_stateManager.setDefaultSizes(ui->previewTreeSplitter, UISizeVector() << "50%" << "50%"); connect(ui->actionSaveAsImage, &QAction::triggered, this, &QuickInspectorWidget::saveAsImage); connect(ui->actionSaveAsImageWithDecoration, &QAction::triggered, this, &QuickInspectorWidget::saveAsImage); connect(ui->actionAnalyzePainting, &QAction::triggered, this, [this]() { m_interface->analyzePainting(); auto viewer = new PaintBufferViewer(QStringLiteral("com.kdab.GammaRay.QuickPaintAnalyzer"), this); viewer->show(); }); connect(ui->actionSlowDownMode, &QAction::triggered, m_interface, &QuickInspectorInterface::setSlowMode); connect(ui->itemPropertyWidget, &PropertyWidget::tabsUpdated, this, &QuickInspectorWidget::resetState); connect(ui->sgPropertyWidget, &PropertyWidget::tabsUpdated, this, &QuickInspectorWidget::resetState); connect(m_scenePreviewWidget, &QuickSceneControlWidget::stateChanged, this, &QuickInspectorWidget::saveState); connect(ui->tabWidget, &QTabWidget::currentChanged, this, &QuickInspectorWidget::saveState); connect(m_scenePreviewWidget->previewWidget(), &RemoteViewWidget::frameChanged, this, &QuickInspectorWidget::updateActions); updateActions(); } QuickInspectorWidget::~QuickInspectorWidget() = default; void QuickInspectorWidget::saveTargetState(QSettings *settings) const { if (m_state != QuickInspectorWidget::Ready) return; settings->setValue("tabIndex", ui->tabWidget->currentIndex()); settings->setValue("remoteViewState", m_scenePreviewWidget->previewWidget()->saveState()); } void QuickInspectorWidget::restoreTargetState(QSettings *settings) { if (m_state != QuickInspectorWidget::Ready) return; ui->tabWidget->setCurrentIndex(settings->value("tabIndex", 0).toInt()); // Delay those changes as they can lead to recursive save/load issue (ie, speak to server) QMetaObject::invokeMethod(m_scenePreviewWidget->previewWidget(), "restoreState", Qt::QueuedConnection, Q_ARG(QByteArray, settings->value("remoteViewState").toByteArray())); } void QuickInspectorWidget::setFeatures(QuickInspectorInterface::Features features) { m_scenePreviewWidget->setSupportsCustomRenderModes(features); ui->actionAnalyzePainting->setEnabled(features & QuickInspectorInterface::AnalyzePainting); stateReceived(QuickInspectorWidget::WaitingFeatures); } void QuickInspectorWidget::setOverlaySettings(const GammaRay::QuickDecorationsSettings &settings) { m_scenePreviewWidget->setOverlaySettingsState(settings); stateReceived(QuickInspectorWidget::WaitingOverlaySettings); } void QuickInspectorWidget::setSlowMode(bool slow) { ui->actionSlowDownMode->setChecked(slow); } void QuickInspectorWidget::itemSelectionChanged(const QItemSelection &selection) { if (selection.isEmpty()) return; const QModelIndex &index = selection.first().topLeft(); ui->itemTreeView->scrollTo(index); } void QuickInspectorWidget::sgSelectionChanged(const QItemSelection &selection) { if (selection.isEmpty()) return; const auto &index = selection.first().topLeft(); ui->sgTreeView->scrollTo(index); } void QuickInspectorWidget::itemModelDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector &roles) { if (!roles.contains(QuickItemModelRole::ItemEvent)) return; for (int i = topLeft.row(); i <= bottomRight.row(); i++) { const QModelIndex index = ui->itemTreeView->model()->index(i, 0, topLeft.parent()); const auto state = index.data(RemoteModelRole::LoadingState).value(); if (state & RemoteModelNodeState::Empty || ~state & RemoteModelNodeState::Outdated) continue; auto *colorAnimation = new QVariantAnimation(this); QPersistentModelIndex persistentIndex(index); connect(colorAnimation, &QVariantAnimation::valueChanged, ui->itemTreeView->itemDelegate(), [persistentIndex, this](const QVariant &value) { qobject_cast(ui->itemTreeView->itemDelegate())->setTextColor(value, persistentIndex); }); colorAnimation->setStartValue(QColor(129, 0, 129)); colorAnimation->setEndValue(QColor(129, 0, 129, 0)); colorAnimation->setDuration(2000); colorAnimation->start(QAbstractAnimation::DeleteWhenStopped); } } void QuickInspectorUiFactory::initUi() { #ifndef QT_NO_OPENGL ObjectBroker::registerClientObjectFactoryCallback( createMaterialExtension); PropertyWidget::registerTab(QStringLiteral("material"), tr("Material")); PropertyWidget::registerTab(QStringLiteral("sgGeometry"), tr("Geometry")); PropertyWidget::registerTab(QStringLiteral("texture"), tr("Texture")); #endif } void GammaRay::QuickInspectorWidget::itemContextMenu(const QPoint &pos) { const QModelIndex index = ui->itemTreeView->indexAt(pos); if (!index.isValid()) return; QMenu contextMenu; const auto objectId = index.data(ObjectModel::ObjectIdRole).value(); ContextMenuExtension ext(objectId); ext.setLocation(ContextMenuExtension::Creation, index.data( ObjectModel::CreationLocationRole).value()); ext.setLocation(ContextMenuExtension::Declaration, index.data(ObjectModel::DeclarationLocationRole).value()); ext.populateMenu(&contextMenu); contextMenu.exec(ui->itemTreeView->viewport()->mapToGlobal(pos)); } void QuickInspectorWidget::stateReceived(GammaRay::QuickInspectorWidget::StateFlag flag) { if (!m_state.testFlag(flag)) { return; } m_state &= ~flag; if (m_state == QuickInspectorWidget::WaitingApply) { QMetaObject::invokeMethod(this, "stateReceived", Qt::QueuedConnection, Q_ARG(GammaRay::QuickInspectorWidget::StateFlag, GammaRay::QuickInspectorWidget::WaitingApply)); } else if (m_state == QuickInspectorWidget::Ready) { resetState(); } } void QuickInspectorWidget::resetState() { if (m_state != QuickInspectorWidget::Ready) return; m_stateManager.reset(); } void QuickInspectorWidget::saveState() { if (m_state != QuickInspectorWidget::Ready) return; m_stateManager.saveState(); } void QuickInspectorWidget::saveAsImage() { const QString fileName = QFileDialog::getSaveFileName( this, tr("Save As Image"), QString(), tr("Image Files (*.png *.jpg)")); if (fileName.isEmpty()) return; const CompleteFrameRequest request(fileName, sender() == ui->actionSaveAsImageWithDecoration); m_scenePreviewWidget->previewWidget()->requestCompleteFrame(request); } void QuickInspectorWidget::updateActions() { ui->actionSaveAsImage->setEnabled(m_scenePreviewWidget->previewWidget()->hasValidFrame()); ui->actionSaveAsImageWithDecoration->setEnabled(ui->actionSaveAsImage->isEnabled()); } gammaray-2.11.3/plugins/quickinspector/quickinspectorwidget.h000066400000000000000000000066761413310443000245500ustar00rootroot00000000000000/* quickinspectorwidget.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_QUICKINSPECTOR_QUICKINSPECTORWIDGET_H #define GAMMARAY_QUICKINSPECTOR_QUICKINSPECTORWIDGET_H #include #include #include #include #include "quickinspectorinterface.h" #include #include QT_BEGIN_NAMESPACE class QLabel; class QImage; class QItemSelection; QT_END_NAMESPACE namespace GammaRay { class QuickSceneControlWidget; struct QuickDecorationsSettings; namespace Ui { class QuickInspectorWidget; } class QuickInspectorWidget : public QWidget { Q_OBJECT public: enum StateFlag { Ready = 0x0, WaitingApply = 0x1, WaitingFeatures = 0x2, WaitingOverlaySettings = 0x8, WaitingAll = WaitingApply | WaitingFeatures | WaitingOverlaySettings }; Q_DECLARE_FLAGS(State, StateFlag) explicit QuickInspectorWidget(QWidget *parent = nullptr); ~QuickInspectorWidget() override; Q_INVOKABLE void saveTargetState(QSettings *settings) const; Q_INVOKABLE void restoreTargetState(QSettings *settings); private slots: void itemSelectionChanged(const QItemSelection &selection); void sgSelectionChanged(const QItemSelection &selection); void setFeatures(GammaRay::QuickInspectorInterface::Features features); void setOverlaySettings(const GammaRay::QuickDecorationsSettings &settings); void setSlowMode(bool slow); void itemModelDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector &roles); void itemContextMenu(const QPoint &pos); void stateReceived(GammaRay::QuickInspectorWidget::StateFlag flag); void resetState(); void saveState(); void saveAsImage(); void updateActions(); private: QScopedPointer ui; QuickInspectorWidget::State m_state; UIStateManager m_stateManager; QuickSceneControlWidget *m_scenePreviewWidget; QuickInspectorInterface *m_interface; }; class QuickInspectorUiFactory : public QObject, public StandardToolUiFactory { Q_OBJECT Q_INTERFACES(GammaRay::ToolUiFactory) Q_PLUGIN_METADATA(IID "com.kdab.GammaRay.ToolUiFactory" FILE "gammaray_quickinspector.json") void initUi() override; }; } Q_DECLARE_METATYPE(GammaRay::QuickInspectorWidget::StateFlag) #endif gammaray-2.11.3/plugins/quickinspector/quickinspectorwidget.ui000066400000000000000000000135341413310443000247250ustar00rootroot00000000000000 GammaRay::QuickInspectorWidget 0 0 813 300 Qt::Horizontal Qt::Vertical 400 0 QTabWidget::South 0 Items Qt::CustomContextMenu true Scene Graph true 0 0 0 0 400 100 0 0 0 0 400 0 Save as &Image... Save current frame as image. Save as &Image (with decoration)... Save current frame with decoration rendered as image. Analyze Painting... Analyzer software renderer paint operations. F9 true Enable slow down mode Enable slow down mode for animations GammaRay::DeferredTreeView QTreeView
    ui/deferredtreeview.h
    GammaRay::PropertyWidget QWidget
    ui/propertywidget.h
    1
    tabWidget currentChanged(int) stackedWidget setCurrentIndex(int) 188 122 381 163
    gammaray-2.11.3/plugins/quickinspector/quickitemdelegate.cpp000066400000000000000000000127101413310443000243040ustar00rootroot00000000000000/* quickitemdelegate.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "quickitemdelegate.h" #include "quickitemmodelroles.h" #include #include #include #include #include #include using namespace GammaRay; QuickItemDelegate::QuickItemDelegate(QAbstractItemView *view) : QStyledItemDelegate(view) , m_view(view) { } void QuickItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { painter->save(); int flags = index.data(QuickItemModelRole::ItemFlags).value(); QStyleOptionViewItem opt = option; initStyleOption(&opt, index); // Disable foreground painting so we can do ourself opt.text.clear(); opt.icon = QIcon(); QApplication::style()->drawControl(QStyle::CE_ItemViewItem, &opt, painter); QRect drawRect = option.rect; painter->setClipRect(option.rect); painter->setClipping(true); // avoid the icons leaking into the next column const auto foregroundData = index.data(Qt::ForegroundRole); const auto inactiveColor = foregroundData.isNull() ? option.palette.text().color() : foregroundData.value(); const auto base = option.state & QStyle::State_Selected ? option.palette.highlightedText().color() : inactiveColor; if (m_colors.contains(index.sibling(index.row(), 0))) { QColor blend = m_colors.value(index.sibling(index.row(), 0)); QColor blended = QColor::fromRgbF( base.redF() * (1 - blend.alphaF()) + blend.redF() * blend.alphaF(), base.greenF() * (1 - blend.alphaF()) + blend.greenF() * blend.alphaF(), base.blueF() * (1 - blend.alphaF()) + blend.blueF() * blend.alphaF()); painter->setPen(blended); } else { painter->setPen(base); } if (index.column() == 0) { auto deco = index.data(Qt::DecorationRole); QVector icons; if (deco.canConvert()) icons.push_back(deco.value()); else if (deco.canConvert()) icons.push_back(deco.value().pixmap(16, 16)); if ((flags &QuickItemModelRole::PartiallyOutOfView) && (~flags & QuickItemModelRole::Invisible)) icons << UIResources::themedIcon(QStringLiteral("warning.png")).pixmap(16, 16); if (flags & QuickItemModelRole::HasActiveFocus) icons << UIResources::themedIcon(QStringLiteral("active-focus.png")).pixmap(16, 16); if (flags & QuickItemModelRole::HasFocus && ~flags & QuickItemModelRole::HasActiveFocus) icons << UIResources::themedIcon(QStringLiteral("focus.png")).pixmap(16, 16); for (int i = 0; i < icons.size() && drawRect.left() < opt.rect.right(); i++) { painter->drawPixmap(drawRect.topLeft(), icons.at(i)); drawRect.setTopLeft(drawRect.topLeft() + QPoint(20, 0)); } } painter->drawText(drawRect, Qt::AlignVCenter, index.data(Qt::DisplayRole).toString()); painter->restore(); } QSize QuickItemDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const { Q_UNUSED(option); // this gets us the cached value for empty cells const auto sh = index.data(Qt::SizeHintRole); if (sh.isValid()) return sh.toSize(); QSize textSize = m_view->fontMetrics().size(Qt::TextSingleLine, index.data(Qt::DisplayRole).toString()); QSize decorationSize; if (index.column() == 0) { int flags = index.data(QuickItemModelRole::ItemFlags).value(); int icons = 1; if ((flags &QuickItemModelRole::OutOfView) && (~flags & QuickItemModelRole::Invisible)) icons++; if (flags & (QuickItemModelRole::HasFocus | QuickItemModelRole::HasActiveFocus)) icons++; decorationSize = QSize(icons * 20, 16); } return {textSize.width() + decorationSize.width() + 5, qMax(textSize.height(), decorationSize.height())}; } void QuickItemDelegate::setTextColor(const QVariant &textColor, const QPersistentModelIndex &index) { if (!index.isValid()) return; m_colors[index] = textColor.value(); for (int i = 0; i < m_view->model()->columnCount(); i++) m_view->update(index.sibling(index.row(), i)); } gammaray-2.11.3/plugins/quickinspector/quickitemdelegate.h000066400000000000000000000037741413310443000237630ustar00rootroot00000000000000/* quickitemdelegate.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_QUICKINSPECTOR_QUICKITEMDELEGATE_H #define GAMMARAY_QUICKINSPECTOR_QUICKITEMDELEGATE_H #include #include QT_BEGIN_NAMESPACE class QAbstractItemView; QT_END_NAMESPACE namespace GammaRay { class QuickItemDelegate : public QStyledItemDelegate { Q_OBJECT public: explicit QuickItemDelegate(QAbstractItemView *view); public Q_SLOTS: void setTextColor(const QVariant &textColor, const QPersistentModelIndex &index); protected: void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override; private: QHash m_colors; QAbstractItemView *m_view; }; } Q_DECLARE_METATYPE(QPersistentModelIndex) #endif // QUICKITEMDELEGATE_H gammaray-2.11.3/plugins/quickinspector/quickitemgeometry.cpp000066400000000000000000000154121413310443000243670ustar00rootroot00000000000000/* quickitemgeometry.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "quickitemgeometry.h" #include #include namespace GammaRay { bool GammaRay::QuickItemGeometry::isValid() const { return !qIsNaN(x) && !qIsNaN(y); } void QuickItemGeometry::scaleTo(qreal factor) { if (!isValid()) return; itemRect = QRectF( itemRect.topLeft() * factor, itemRect.bottomRight() * factor); boundingRect = QRectF( boundingRect.topLeft() * factor, boundingRect.bottomRight() * factor); childrenRect = QRectF( childrenRect.topLeft() * factor, childrenRect.bottomRight() * factor); backgroundRect = QRectF( backgroundRect.topLeft() * factor, backgroundRect.bottomRight() * factor); contentItemRect = QRectF( contentItemRect.topLeft() * factor, contentItemRect.bottomRight() * factor); transformOriginPoint = transformOriginPoint * factor; leftMargin = leftMargin * factor; horizontalCenterOffset = horizontalCenterOffset * factor; rightMargin = rightMargin * factor; topMargin = topMargin * factor; verticalCenterOffset = verticalCenterOffset * factor; bottomMargin = bottomMargin * factor; baselineOffset = baselineOffset * factor; x = x * factor; y = y * factor; if (!qIsNaN(padding)) { padding = padding * factor; leftPadding = leftPadding * factor; rightPadding = rightPadding * factor; topPadding = topPadding * factor; bottomPadding = bottomPadding * factor; } } bool QuickItemGeometry::operator==(const QuickItemGeometry &other) const { return itemRect == other.itemRect && boundingRect == other.boundingRect && childrenRect == other.childrenRect && backgroundRect == other.backgroundRect && contentItemRect == other.contentItemRect && transformOriginPoint == other.transformOriginPoint && transform == other.transform && parentTransform == other.parentTransform && x == other.x && y == other.y && left == other.left && right == other.right && top == other.top && bottom == other.bottom && horizontalCenter == other.horizontalCenter && verticalCenter == other.verticalCenter && baseline == other.baseline && margins == other.margins && leftMargin == other.leftMargin && horizontalCenterOffset == other.horizontalCenterOffset && rightMargin == other.rightMargin && topMargin == other.topMargin && verticalCenterOffset == other.verticalCenterOffset && bottomMargin == other.bottomMargin && baselineOffset == other.baselineOffset && padding == other.padding && leftPadding == other.leftPadding && rightPadding == other.rightPadding && topPadding == other.topPadding && bottomPadding == other.bottomPadding && traceColor == other.traceColor && traceTypeName == other.traceTypeName && traceName == other.traceName ; } bool QuickItemGeometry::operator!=(const QuickItemGeometry &other) const { return !operator==(other); } QDataStream &operator<<(QDataStream &stream, const GammaRay::QuickItemGeometry &geometry) { stream << geometry.itemRect << geometry.boundingRect << geometry.childrenRect << geometry.backgroundRect << geometry.contentItemRect << geometry.transformOriginPoint << geometry.transform << geometry.parentTransform << geometry.x << geometry.y << geometry.left << geometry.right << geometry.top << geometry.bottom << geometry.horizontalCenter << geometry.verticalCenter << geometry.baseline << geometry.margins << geometry.leftMargin << geometry.horizontalCenterOffset << geometry.rightMargin << geometry.topMargin << geometry.verticalCenterOffset << geometry.bottomMargin << geometry.baselineOffset << geometry.padding << geometry.leftPadding << geometry.rightPadding << geometry.topPadding << geometry.bottomPadding << geometry.traceColor << geometry.traceTypeName << geometry.traceName ; return stream; } QDataStream &operator>>(QDataStream &stream, GammaRay::QuickItemGeometry &geometry) { stream >> geometry.itemRect >> geometry.boundingRect >> geometry.childrenRect >> geometry.backgroundRect >> geometry.contentItemRect >> geometry.transformOriginPoint >> geometry.transform >> geometry.parentTransform >> geometry.x >> geometry.y >> geometry.left >> geometry.right >> geometry.top >> geometry.bottom >> geometry.horizontalCenter >> geometry.verticalCenter >> geometry.baseline >> geometry.margins >> geometry.leftMargin >> geometry.horizontalCenterOffset >> geometry.rightMargin >> geometry.topMargin >> geometry.verticalCenterOffset >> geometry.bottomMargin >> geometry.baselineOffset >> geometry.padding >> geometry.leftPadding >> geometry.rightPadding >> geometry.topPadding >> geometry.bottomPadding >> geometry.traceColor >> geometry.traceTypeName >> geometry.traceName ; return stream; } } gammaray-2.11.3/plugins/quickinspector/quickitemgeometry.h000066400000000000000000000066241413310443000240410ustar00rootroot00000000000000/* quickitemgeometry.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_QUICKINSPECTOR_QUICKITEMGEOMETRY_H #define GAMMARAY_QUICKINSPECTOR_QUICKITEMGEOMETRY_H #include #include #include #include #include QT_BEGIN_NAMESPACE class QQuickItem; QT_END_NAMESPACE namespace GammaRay { struct QuickItemGeometry { QuickItemGeometry() : x(qQNaN()) , y(qQNaN()) , left(false) , right(false) , top(false) , bottom(false) , horizontalCenter(false) , verticalCenter(false) , baseline(false) , margins(qQNaN()) , leftMargin(qQNaN()) , horizontalCenterOffset(qQNaN()) , rightMargin(qQNaN()) , topMargin(qQNaN()) , verticalCenterOffset(qQNaN()) , bottomMargin(qQNaN()) , baselineOffset(qQNaN()) , padding(qQNaN()) , leftPadding(qQNaN()) , rightPadding(qQNaN()) , topPadding(qQNaN()) , bottomPadding(qQNaN()) { } // basic geometry QRectF itemRect; QRectF boundingRect; QRectF childrenRect; QRectF backgroundRect; QRectF contentItemRect; // transform QPointF transformOriginPoint; QTransform transform; QTransform parentTransform; // simple position qreal x; qreal y; // anchors bool left; bool right; bool top; bool bottom; bool horizontalCenter; bool verticalCenter; bool baseline; // margins qreal margins; qreal leftMargin; qreal horizontalCenterOffset; qreal rightMargin; qreal topMargin; qreal verticalCenterOffset; qreal bottomMargin; qreal baselineOffset; // padding qreal padding; qreal leftPadding; qreal rightPadding; qreal topPadding; qreal bottomPadding; QColor traceColor; QString traceTypeName; QString traceName; bool isValid() const; void scaleTo(qreal factor); bool operator==(const QuickItemGeometry &other) const; bool operator!=(const QuickItemGeometry &other) const; }; QDataStream &operator<<(QDataStream &stream, const GammaRay::QuickItemGeometry &geometry); QDataStream &operator>>(QDataStream &stream, GammaRay::QuickItemGeometry &geometry); } Q_DECLARE_METATYPE(GammaRay::QuickItemGeometry) Q_DECLARE_METATYPE(QVector) #endif gammaray-2.11.3/plugins/quickinspector/quickitemmodel.cpp000066400000000000000000000374631413310443000236460ustar00rootroot00000000000000/* quickitemmodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "quickitemmodel.h" #include "quickitemmodelroles.h" #include #include #include #include #include #include #include #include #include #include using namespace GammaRay; QuickItemModel::QuickItemModel(QObject *parent) : ObjectModelBase(parent) , m_dataChangeTimer(new QTimer(this)) { m_clickEventFilter = new QuickEventMonitor(this); m_dataChangeTimer->setSingleShot(true); m_dataChangeTimer->setInterval(500); connect(m_dataChangeTimer, &QTimer::timeout, this, &QuickItemModel::emitPendingDataChanges); } QuickItemModel::~QuickItemModel() = default; void QuickItemModel::setWindow(QQuickWindow *window) { beginResetModel(); clear(); m_window = window; populateFromItem(window->contentItem()); endResetModel(); } QVariant QuickItemModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); QQuickItem *item = reinterpret_cast(index.internalPointer()); if (role == QuickItemModelRole::ItemFlags) return m_itemFlags[item]; if (role == ObjectModel::ObjectIdRole) return QVariant::fromValue(ObjectId(item)); return dataForObject(item, index, role); } int QuickItemModel::rowCount(const QModelIndex &parent) const { if (parent.column() == 1) return 0; QQuickItem *parentItem = reinterpret_cast(parent.internalPointer()); return m_parentChildMap.value(parentItem).size(); } QModelIndex QuickItemModel::parent(const QModelIndex &child) const { QQuickItem *childItem = reinterpret_cast(child.internalPointer()); return indexForItem(m_childParentMap.value(childItem)); } QModelIndex QuickItemModel::index(int row, int column, const QModelIndex &parent) const { QQuickItem *parentItem = reinterpret_cast(parent.internalPointer()); const QVector children = m_parentChildMap.value(parentItem); if (row < 0 || column < 0 || row >= children.size() || column >= columnCount()) return {}; return createIndex(row, column, children.at(row)); } QMap QuickItemModel::itemData(const QModelIndex &index) const { QMap d = ObjectModelBase::itemData(index); d.insert(QuickItemModelRole::ItemFlags, data(index, QuickItemModelRole::ItemFlags)); d.insert(QuickItemModelRole::ItemActions, data(index, QuickItemModelRole::ItemActions)); return d; } void QuickItemModel::clear() { for (auto it = m_childParentMap.constBegin(); it != m_childParentMap.constEnd(); ++it) disconnect(it.key(), nullptr, this, nullptr); m_childParentMap.clear(); m_parentChildMap.clear(); } void QuickItemModel::populateFromItem(QQuickItem *item) { if (!item) return; connectItem(item); updateItemFlags(item); m_childParentMap[item] = item->parentItem(); m_parentChildMap[item->parentItem()].push_back(item); foreach (QQuickItem *child, item->childItems()) populateFromItem(child); QVector &children = m_parentChildMap[item->parentItem()]; std::sort(children.begin(), children.end()); if (Probe::instance()) { // Make sure every items are known to the objects model as this is not always // the case when attaching to running process (OSX) Probe::instance()->discoverObject(item); } } void QuickItemModel::connectItem(QQuickItem *item) { Q_ASSERT(item); auto itemUpdatedFunc = [this, item]() { itemUpdated(item); }; std::array connections = {{ connect(item, &QQuickItem::parentChanged, this, [this, item]() { itemReparented(item); }), connect(item, &QQuickItem::visibleChanged, this, itemUpdatedFunc), connect(item, &QQuickItem::focusChanged, this, itemUpdatedFunc), connect(item, &QQuickItem::activeFocusChanged, this, itemUpdatedFunc), connect(item, &QQuickItem::widthChanged, this, itemUpdatedFunc), connect(item, &QQuickItem::heightChanged, this, itemUpdatedFunc), connect(item, &QQuickItem::xChanged, this, itemUpdatedFunc), connect(item, &QQuickItem::yChanged, this, itemUpdatedFunc) }}; m_itemConnections.emplace(std::make_pair(item, std::move(connections))); // can't construct in-place, fails to compile under MSVC2010 :( item->installEventFilter(m_clickEventFilter); } void QuickItemModel::disconnectItem(QQuickItem *item) { Q_ASSERT(item); auto it = m_itemConnections.find(item); if (it != m_itemConnections.end()) { for (const auto &connection : it->second) { disconnect(connection); } m_itemConnections.erase(it); } item->removeEventFilter(m_clickEventFilter); } QModelIndex QuickItemModel::indexForItem(QQuickItem *item) const { if (!item) return {}; QQuickItem *parent = m_childParentMap.value(item); const QVector &siblings = m_parentChildMap[parent]; const auto it = std::lower_bound(siblings.constBegin(), siblings.constEnd(), item); if (it == siblings.constEnd() || *it != item) return QModelIndex(); const int row = std::distance(siblings.constBegin(), it); return createIndex(row, 0, item); } void QuickItemModel::objectAdded(QObject *obj) { Q_ASSERT(thread() == QThread::currentThread()); QQuickItem *item = qobject_cast(obj); if (!item) return; // detect if item is added to scene later connect(item, &QQuickItem::windowChanged, this, [this, item]() { itemWindowChanged(item); }); addItem(item); } void QuickItemModel::addItem(QQuickItem *item) { Q_ASSERT(item); if (!item->window()) return; // item not (yet) added to a scene if (item->window() != m_window) return; // item for a different scene if (m_childParentMap.contains(item)) return; // already known QQuickItem *parentItem = item->parentItem(); if (parentItem) { // add parent first, if we don't know that yet if (!m_childParentMap.contains(parentItem)) objectAdded(parentItem); } connectItem(item); const QModelIndex index = indexForItem(parentItem); if (!index.isValid() && parentItem) return; QVector &children = m_parentChildMap[parentItem]; auto it = std::lower_bound(children.begin(), children.end(), item); const int row = std::distance(children.begin(), it); beginInsertRows(index, row, row); children.insert(it, item); m_childParentMap.insert(item, parentItem); endInsertRows(); } void QuickItemModel::objectRemoved(QObject *obj) { Q_ASSERT(thread() == QThread::currentThread()); QQuickItem *item = static_cast(obj); // this is fine, we must not deref // obj/item at this point anyway removeItem(item, true); } void QuickItemModel::removeItem(QQuickItem *item, bool danglingPointer) { if (!m_childParentMap.contains(item)) { // not an item of our current scene Q_ASSERT(!m_parentChildMap.contains(item)); return; } if (item && !danglingPointer) disconnectItem(item); QQuickItem *parentItem = m_childParentMap.value(item); const QModelIndex parentIndex = indexForItem(parentItem); if (parentItem && !parentIndex.isValid()) return; QVector &siblings = m_parentChildMap[parentItem]; auto it = std::lower_bound(siblings.begin(), siblings.end(), item); if (it == siblings.end() || *it != item) return; const int row = std::distance(siblings.begin(), it); beginRemoveRows(parentIndex, row, row); siblings.erase(it); doRemoveSubtree(item, danglingPointer); endRemoveRows(); } void QuickItemModel::doRemoveSubtree(QQuickItem *item, bool danglingPointer) { m_childParentMap.remove(item); m_parentChildMap.remove(item); if (!danglingPointer) { foreach (QQuickItem *child, item->childItems()) doRemoveSubtree(child, false); } } void QuickItemModel::itemReparented(QQuickItem *item) { Q_ASSERT(item); if (!item->parentItem()) { // Item was not deleted, but removed from the scene. removeItem(item, false); return; } Q_ASSERT(item && item->window() == m_window); QQuickItem *sourceParent = m_childParentMap.value(item); Q_ASSERT(sourceParent); if (sourceParent == item->parentItem()) return; const QModelIndex sourceParentIndex = indexForItem(sourceParent); auto &sourceSiblings = m_parentChildMap[sourceParent]; auto sit = std::lower_bound(sourceSiblings.begin(), sourceSiblings.end(), item); Q_ASSERT(sit != sourceSiblings.end() && *sit == item); const int sourceRow = std::distance(sourceSiblings.begin(), sit); QQuickItem *destParent = item->parentItem(); Q_ASSERT(destParent); const QModelIndex destParentIndex = indexForItem(destParent); if (!destParentIndex.isValid()) { // item was moved to a parent we don't know (yet?) removeItem(item, false); return; } QVector &destSiblings = m_parentChildMap[destParent]; auto dit = std::lower_bound(destSiblings.begin(), destSiblings.end(), item); const int destRow = std::distance(destSiblings.begin(), dit); // as of Qt 5.10, QSFPM translates moves into layout changes, which is way worse for us than remove/insert #if 0 beginMoveRows(sourceParentIndex, sourceRow, sourceRow, destParentIndex, destRow); destSiblings.insert(dit, item); sourceSiblings.erase(sit); m_childParentMap.insert(item, destParent); endMoveRows(); #else beginRemoveRows(sourceParentIndex, sourceRow, sourceRow); sourceSiblings.erase(sit); m_childParentMap.remove(item); endRemoveRows(); beginInsertRows(destParentIndex, destRow, destRow); destSiblings.insert(dit, item); m_childParentMap.insert(item, destParent); endInsertRows(); #endif } void QuickItemModel::itemWindowChanged(QQuickItem *item) { Q_ASSERT(item); if (!item->window() || item->window() != m_window) removeItem(item); else if (m_window && item->window() == m_window) addItem(item); } void QuickItemModel::itemUpdated(QQuickItem *item) { Q_ASSERT(item); recursivelyUpdateItem(item); } void QuickItemModel::recursivelyUpdateItem(QQuickItem *item) { Q_ASSERT(item); if (item->parent() == QObject::parent()) // skip items injected by ourselves return; int oldFlags = m_itemFlags.value(item); updateItemFlags(item); if (oldFlags != m_itemFlags.value(item)) updateItem(item, QuickItemModelRole::ItemFlags); foreach (QQuickItem *child, item->childItems()) recursivelyUpdateItem(child); } void QuickItemModel::updateItem(QQuickItem *item, int role) { if (!item || item->window() != m_window) return; auto it = std::lower_bound(m_pendingDataChanges.begin(), m_pendingDataChanges.end(), item); if (it == m_pendingDataChanges.end() || (*it).item != item) { PendingDataChange c; c.item = item; it = m_pendingDataChanges.insert(it, c); } if (role == QuickItemModelRole::ItemEvent) (*it).eventChange = true; if (role == QuickItemModelRole::ItemFlags) (*it).flagChange = true; if (!m_dataChangeTimer->isActive()) m_dataChangeTimer->start(); } void QuickItemModel::updateItemFlags(QQuickItem *item) { QQuickItem *ancestor = item->parentItem(); bool outOfView = false; bool partiallyOutOfView = false; auto rect = item->mapRectToScene(QRectF(0, 0, item->width(), item->height())); if (item->isVisible()) { while (ancestor && ancestor != m_window->contentItem()) { if (ancestor->parentItem() == m_window->contentItem() || ancestor->clip()) { auto ancestorRect = ancestor->mapRectToScene(QRectF(0, 0, ancestor->width(), ancestor->height())); partiallyOutOfView |= !ancestorRect.contains(rect); outOfView = partiallyOutOfView && !rect.intersects(ancestorRect); if (outOfView) { break; } } ancestor = ancestor->parentItem(); } } m_itemFlags[item] = (!item->isVisible() || item->opacity() == 0 ? QuickItemModelRole::Invisible : QuickItemModelRole::None) |(item->width() == 0 || item->height() == 0 ? QuickItemModelRole::ZeroSize : QuickItemModelRole::None) |(partiallyOutOfView ? QuickItemModelRole::PartiallyOutOfView : QuickItemModelRole::None) |(outOfView ? QuickItemModelRole::OutOfView : QuickItemModelRole::None) |(item->hasFocus() ? QuickItemModelRole::HasFocus : QuickItemModelRole::None) |(item->hasActiveFocus() ? QuickItemModelRole::HasActiveFocus : QuickItemModelRole::None); } QuickEventMonitor::QuickEventMonitor(QuickItemModel *parent) : QObject(parent) , m_model(parent) { } bool QuickEventMonitor::eventFilter(QObject *obj, QEvent *event) { switch (event->type()) { // exclude some unsafe event types case QEvent::DeferredDelete: case QEvent::Destroy: // exclude some event types which occur far too often and thus cost us bandwidth case QEvent::HoverMove: case QEvent::MouseMove: case QEvent::TouchUpdate: case QEvent::Wheel: // due to high frequency creation from touch events // exclude event types that are unrelated to user interaction case QEvent::MetaCall: case QEvent::ChildAdded: case QEvent::ChildPolished: case QEvent::ChildRemoved: case QEvent::Timer: return false; default: break; } m_model->updateItem(qobject_cast(obj), QuickItemModelRole::ItemEvent); return false; } void QuickItemModel::emitPendingDataChanges() { QVector roles; roles.reserve(2); for (const auto &change : m_pendingDataChanges) { const auto left = indexForItem(change.item); if (!left.isValid()) { continue; } const auto right = left.sibling(left.row(), columnCount() - 1); Q_ASSERT(left.isValid()); Q_ASSERT(right.isValid()); roles.clear(); if (change.eventChange) { roles.push_back(QuickItemModelRole::ItemEvent); } if (change.flagChange) { roles.push_back(QuickItemModelRole::ItemFlags); } emit dataChanged(left, right, roles); } m_pendingDataChanges.clear(); } gammaray-2.11.3/plugins/quickinspector/quickitemmodel.h000066400000000000000000000111101413310443000232700ustar00rootroot00000000000000/* quickitemmodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_QUICKINSPECTOR_QUICKITEMMODEL_H #define GAMMARAY_QUICKINSPECTOR_QUICKITEMMODEL_H #include #include #include #include #include #include #include #include QT_BEGIN_NAMESPACE class QQuickItem; class QQuickWindow; QT_END_NAMESPACE namespace GammaRay { //forward class QuickEventMonitor; /** QQ2 item tree model. */ class QuickItemModel : public ObjectModelBase { Q_OBJECT public: explicit QuickItemModel(QObject *parent = nullptr); ~QuickItemModel() override; void setWindow(QQuickWindow *window); QVariant data(const QModelIndex &index, int role) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; QModelIndex parent(const QModelIndex &child) const override; QModelIndex index(int row, int column, const QModelIndex &parent) const override; QMap< int, QVariant > itemData(const QModelIndex &index) const override; public slots: void objectAdded(QObject *obj); void objectRemoved(QObject *obj); private slots: void itemReparented(QQuickItem *item); void itemWindowChanged(QQuickItem *item); void itemUpdated(QQuickItem *item); private: friend class QuickEventMonitor; void updateItem(QQuickItem *item, int role); void recursivelyUpdateItem(QQuickItem *item); void updateItemFlags(QQuickItem *item); void clear(); void populateFromItem(QQuickItem *item); /** * Reports problems (e.g. visible but out of view) about all items of this * model. Uses the item flags from the model. */ void reportProblems(); /// Track all changes to item @p item in this model (parentChanged, windowChanged, ...) void connectItem(QQuickItem *item); /// Untrack item @p item void disconnectItem(QQuickItem *item); QModelIndex indexForItem(QQuickItem *item) const; /// Add item @p item to this model void addItem(QQuickItem *item); /// Remove item @p item from this model. /// Set @p danglingPointer to true if the item has already been destructed void removeItem(QQuickItem *item, bool danglingPointer = false); /** * Remove item @p item from the internal data set. * This function won't cause rowsRemoved to be emitted. * Set @p danglingPointer to true if the item has already been destructed. */ void doRemoveSubtree(QQuickItem *item, bool danglingPointer = false); QPointer m_window; QHash m_childParentMap; QHash > m_parentChildMap; // TODO: Merge these two? QHash m_itemFlags; std::unordered_map> m_itemConnections; // dataChange signal compression struct PendingDataChange { QQuickItem *item = nullptr; bool eventChange = false; bool flagChange = false; inline bool operator<(QQuickItem *rhs) const { return item < rhs; } }; std::vector m_pendingDataChanges; QTimer *m_dataChangeTimer = nullptr; void emitPendingDataChanges(); QuickEventMonitor *m_clickEventFilter; }; class QuickEventMonitor : public QObject { Q_OBJECT public: explicit QuickEventMonitor(QuickItemModel *parent); protected: bool eventFilter(QObject *obj, QEvent *event) override; private: QuickItemModel *m_model; }; } #endif // GAMMARAY_QUICKITEMMODEL_H gammaray-2.11.3/plugins/quickinspector/quickitemmodelroles.h000066400000000000000000000033131413310443000243430ustar00rootroot00000000000000/* quickitemmodelroles.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_QUICKINSPECTOR_QUICKITEMMODELROLES_H #define GAMMARAY_QUICKINSPECTOR_QUICKITEMMODELROLES_H #include namespace GammaRay { /** Model roles shared between client and server. */ namespace QuickItemModelRole { enum Roles { ItemFlags = ObjectModel::UserRole, ItemEvent, ItemActions }; enum ItemFlag { None = 0, Invisible = 1, ZeroSize = 2, PartiallyOutOfView = 4, OutOfView = 8, HasFocus = 16, HasActiveFocus = 32, JustRecievedEvent = 64 }; } } Q_DECLARE_METATYPE(GammaRay::QuickItemModelRole::ItemFlag) #endif gammaray-2.11.3/plugins/quickinspector/quickitemtreewatcher.cpp000066400000000000000000000056371413310443000250610ustar00rootroot00000000000000/* quickitemtreewatcher.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "quickitemtreewatcher.h" #include "quickitemmodelroles.h" #include #include using namespace GammaRay; QuickItemTreeWatcher::QuickItemTreeWatcher(QTreeView *itemView, QTreeView *sgView, QObject *parent) : QObject(parent) , m_itemView(itemView) , m_sgView(sgView) { connect(itemView->model(), &QAbstractItemModel::rowsInserted, this, &QuickItemTreeWatcher::itemModelRowsInserted); connect(sgView->model(), &QAbstractItemModel::rowsInserted, this, &QuickItemTreeWatcher::sgModelRowsInserted); } QuickItemTreeWatcher::~QuickItemTreeWatcher() = default; void QuickItemTreeWatcher::itemModelRowsInserted(const QModelIndex &parent, int start, int end) { if (parent.isValid() && !m_itemView->isExpanded(parent)) return; const int siblingCount = m_itemView->model()->rowCount(parent); for (int row = start; row <= end; ++row) { const QModelIndex index = m_itemView->model()->index(row, 0, parent); const bool invisible = index.data(QuickItemModelRole::ItemFlags).value() &(QuickItemModelRole::Invisible | QuickItemModelRole::ZeroSize); if (!invisible && siblingCount < 5) m_itemView->setExpanded(index, true); } m_itemView->resizeColumnToContents(0); } void QuickItemTreeWatcher::sgModelRowsInserted(const QModelIndex &parent, int start, int end) { if (parent.isValid() && !m_sgView->isExpanded(parent)) return; const int siblingCount = m_sgView->model()->rowCount(parent); for (int row = start; row <= end; ++row) { const QModelIndex index = m_sgView->model()->index(row, 0, parent); if (siblingCount < 5) m_sgView->setExpanded(index, true); } m_sgView->resizeColumnToContents(0); } gammaray-2.11.3/plugins/quickinspector/quickitemtreewatcher.h000066400000000000000000000035631413310443000245220ustar00rootroot00000000000000/* quickitemtreewatcher.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_QUICKINSPECTOR_QUICKITEMTREEWATCHER_H #define GAMMARAY_QUICKINSPECTOR_QUICKITEMTREEWATCHER_H #include QT_BEGIN_NAMESPACE class QModelIndex; class QTreeView; QT_END_NAMESPACE namespace GammaRay { /** Auto-expand the visible item sub-tree. */ class QuickItemTreeWatcher : public QObject { Q_OBJECT public: explicit QuickItemTreeWatcher(QTreeView *itemView, QTreeView *sgView, QObject *parent = nullptr); ~QuickItemTreeWatcher() override; private slots: void itemModelRowsInserted(const QModelIndex &parent, int start, int end); void sgModelRowsInserted(const QModelIndex &parent, int start, int end); private: QTreeView *m_itemView; QTreeView *m_sgView; }; } #endif // GAMMARAY_QUICKITEMTREEWATCHER_H gammaray-2.11.3/plugins/quickinspector/quickoverlaylegend.cpp000066400000000000000000000201051413310443000245100ustar00rootroot00000000000000/* quickoverlaylegend.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Filipe Azevedo Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "quickoverlaylegend.h" #include "quickdecorationsdrawer.h" #include #include #include #include #include #include #include namespace GammaRay { class LegendModel : public QAbstractListModel { Q_OBJECT struct Item { enum Mode { Rect, Line, Last = Line + 1 }; explicit Item(const QBrush &brush = QBrush(), const QPen &pen = QPen(), const QByteArray &label = QByteArray()) : brush(brush) , pen(pen) , label(label) { } QBrush brush; QPen pen; QByteArray label; QPixmap pixmap; static void createPixmap(Item &item) { static qreal dpr = 2.0; static QSize iconSize(44, 44); QPixmap pixmap(iconSize.width() * Last * dpr, iconSize.height() * dpr); pixmap.setDevicePixelRatio(dpr); pixmap.fill(QColor(Qt::white)); { const int margin(2); QPainter painter(&pixmap); QPen pen(item.pen); pen.setWidth(pen.width() * 2); painter.setPen(pen); painter.setBrush(item.brush); for (int i = Rect; i < Last; i++) { const QRect rect(QPoint(iconSize.width() * i, 0), iconSize); switch (i) { case Rect: { painter.drawRect(rect.adjusted(margin, margin, -margin, -margin)); break; } case Line:{ painter.drawLine(QLine(QPoint(rect.left() + margin, rect.center().y() - 2), QPoint(rect.right() - margin, rect.center().y() - 2))); pen.setStyle(Qt::DotLine); painter.setPen(pen); painter.drawLine(QLine(QPoint(rect.left() + margin, rect.center().y() + 2), QPoint(rect.right() - margin, rect.center().y() + 2))); break; } } } } item.pixmap = pixmap; } }; public: explicit LegendModel(QObject *parent = nullptr) : QAbstractListModel(parent) { } Qt::ItemFlags flags(const QModelIndex &index) const override { return QAbstractListModel::flags(index) & ~Qt::ItemIsSelectable; } int rowCount(const QModelIndex &parent = QModelIndex()) const override { return parent.isValid() ? 0 : m_items.count(); } QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override { if (hasIndex(index.row(), index.column(), index.parent())) { const Item &item(m_items[index.row()]); switch (role) { // Return a pixmap so the view will draw it at the given size instead of the view icon size case Qt::DecorationRole: return item.pixmap; case Qt::ForegroundRole: return item.pen.brush(); case Qt::DisplayRole: return LegendModel::tr(item.label.constData()); case Qt::SizeHintRole: return QSize(-1, (item.pixmap.height() / item.pixmap.devicePixelRatio()) + (2 * 2)); } } return QVariant(); } void setSettings(const QuickDecorationsSettings &settings) { beginResetModel(); m_items.clear(); m_items << Item( settings.boundingRectBrush, settings.boundingRectColor, QT_TR_NOOP("Bounding rect") ); m_items << Item( settings.geometryRectBrush, settings.geometryRectColor, QT_TR_NOOP("Geometry rect") ); m_items << Item( settings.childrenRectBrush, settings.childrenRectColor, QT_TR_NOOP("Children rect") ); m_items << Item( QBrush(), settings.transformOriginColor, QT_TR_NOOP("Transform origin") ); m_items << Item( QBrush(), settings.coordinatesColor, QT_TR_NOOP("Coordinates (x, y...)") ); m_items << Item( QBrush(), settings.marginsColor, QT_TR_NOOP("Margins/Anchors") ); m_items << Item( QBrush(), settings.paddingColor, QT_TR_NOOP("Padding") ); m_items << Item( QBrush(), settings.gridColor, QT_TR_NOOP("Grid") ); for (int i = 0; i < m_items.count(); ++i) { Item::createPixmap(m_items[i]); } endResetModel(); } private: QVector m_items; }; QuickOverlayLegend::QuickOverlayLegend(QWidget *parent) : QWidget(parent, Qt::Tool) , m_model(new LegendModel(this)) { setWindowTitle(tr("Legend")); auto *view = new QListView(this); view->setUniformItemSizes(true); view->setModel(m_model); auto *layout = new QVBoxLayout(this); layout->addWidget(view); m_visibilityAction = new QAction(UIResources::themedIcon(QLatin1String("legend.png")), tr("Show Legend"), this); m_visibilityAction->setObjectName("aShowLegend"); m_visibilityAction->setCheckable(true); m_visibilityAction->setToolTip(tr("Show Legend
    " "This shows a legend explaining the various diagnostic decorations.")); connect(m_visibilityAction, &QAction::triggered, this, [this](bool toggled) { setVisible(toggled); }); } QAction *QuickOverlayLegend::visibilityAction() const { return m_visibilityAction; } void QuickOverlayLegend::setOverlaySettings(const QuickDecorationsSettings &settings) { m_model->setSettings(settings); const int titleBarHeight = style()->pixelMetric(QStyle::PM_TitleBarHeight); const QMargins margins(layout()->contentsMargins()); const int viewHeight = m_model->index(0, 0).data(Qt::SizeHintRole).toSize().height() * m_model->rowCount(); resize(280, titleBarHeight + margins.top() + margins.bottom() + viewHeight); } void QuickOverlayLegend::showEvent(QShowEvent *event) { QWidget::showEvent(event); m_visibilityAction->setChecked(true); } void QuickOverlayLegend::hideEvent(QHideEvent *event) { QWidget::hideEvent(event); m_visibilityAction->setChecked(false); } } #include "quickoverlaylegend.moc" gammaray-2.11.3/plugins/quickinspector/quickoverlaylegend.h000066400000000000000000000035071413310443000241640ustar00rootroot00000000000000/* quickoverlaylegend.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Filipe Azevedo Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_QUICKINSPECTOR_QUICKOVERLAYLEGEND_H #define GAMMARAY_QUICKINSPECTOR_QUICKOVERLAYLEGEND_H #include QT_BEGIN_NAMESPACE class QAction; QT_END_NAMESPACE namespace GammaRay { struct QuickDecorationsSettings; class LegendModel; class QuickOverlayLegend : public QWidget { Q_OBJECT public: explicit QuickOverlayLegend(QWidget *parent = nullptr); QAction *visibilityAction() const; void setOverlaySettings(const QuickDecorationsSettings &settings); void showEvent(QShowEvent *event) override; void hideEvent(QHideEvent *event) override; private: LegendModel *m_model; QAction *m_visibilityAction; }; } #endif // GAMMARAY_QUICKINSPECTOR_QUICKOVERLAYLEGEND_H gammaray-2.11.3/plugins/quickinspector/quickpaintanalyzerextension.cpp000066400000000000000000000050331413310443000264710ustar00rootroot00000000000000/* quickpaintanalyzerextension.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "quickpaintanalyzerextension.h" #include #include #include #include #include using namespace GammaRay; QuickPaintAnalyzerExtension::QuickPaintAnalyzerExtension(PropertyController *controller) : PropertyControllerExtension(controller->objectBaseName() + ".painting") , m_paintAnalyzer(nullptr) { // check if the paint analyzer already exists before creating it, // as we share the UI with the other plugins. const QString aName = controller->objectBaseName() + QStringLiteral(".painting.analyzer"); if (ObjectBroker::hasObject(aName)) { m_paintAnalyzer = qobject_cast(ObjectBroker::object(aName)); } else { m_paintAnalyzer = new PaintAnalyzer(aName, controller); } } QuickPaintAnalyzerExtension::~QuickPaintAnalyzerExtension() = default; bool QuickPaintAnalyzerExtension::setQObject(QObject *object) { auto item = qobject_cast(object); if (!PaintAnalyzer::isAvailable() || !item) return false; m_paintAnalyzer->beginAnalyzePainting(); m_paintAnalyzer->setBoundingRect(item->contentsBoundingRect()); { QPainter painter(m_paintAnalyzer->paintDevice()); item->paint(&painter); } m_paintAnalyzer->endAnalyzePainting(); return true; } gammaray-2.11.3/plugins/quickinspector/quickpaintanalyzerextension.h000066400000000000000000000032501413310443000261350ustar00rootroot00000000000000/* quickpaintanalyzerextension.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_QUICKPAINTANALYZEREXTENSION_H #define GAMMARAY_QUICKPAINTANALYZEREXTENSION_H #include namespace GammaRay { class PaintAnalyzer; class PropertyController; class QuickPaintAnalyzerExtension : public PropertyControllerExtension { public: explicit QuickPaintAnalyzerExtension(PropertyController *controller); ~QuickPaintAnalyzerExtension(); bool setQObject(QObject *object) override; private: PaintAnalyzer *m_paintAnalyzer; }; } #endif // GAMMARAY_QUICKPAINTANALYZEREXTENSION_H gammaray-2.11.3/plugins/quickinspector/quickscenecontrolwidget.cpp000066400000000000000000000377011413310443000255640ustar00rootroot00000000000000/* quickscenepreviewwidget.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Christoph Sterz Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "quickscenecontrolwidget.h" #include "quickoverlaylegend.h" #include "quickscenepreviewwidget.h" #include "gridsettingswidget.h" #include #include #include #include #include #include #include #include #include using namespace GammaRay; namespace { static QAction *checkedAction(QActionGroup *group) { // For some reason QActionGroup::checkedAction() is not yet synced when needed // Might be because of our direct QAction::setChecked() calls in between foreach (QAction *action, group->actions()) { if (action->isChecked()) return action; } return nullptr; } } QuickSceneControlWidget::QuickSceneControlWidget(QuickInspectorInterface *inspector, QWidget *parent) : QWidget(parent) , m_gridSettingsWidget(new GridSettingsWidget) // Owned by the QWidgetAction , m_legendTool(new QuickOverlayLegend(this)) , m_inspectorInterface(inspector) { m_layout = new QVBoxLayout(this); m_layout->setContentsMargins(QMargins()); m_previewWidget = new QuickScenePreviewWidget(this, this); m_toolBar = new QToolBar(this); m_toolBar->setAutoFillBackground(true); // Our icons are 16x16 and support hidpi, so let force iconSize on every styles m_toolBar->setIconSize(QSize(16, 16)); m_toolBar->setToolButtonStyle(Qt::ToolButtonIconOnly); m_visualizeGroup = new QActionGroup(this); m_visualizeGroup->setExclusive(false); // we need 0 or 1 selected, not exactly 1 m_visualizeClipping = new QAction(UIResources::themedIcon(QLatin1String("visualize-clipping.png")), tr("Visualize Clipping"), this); m_visualizeClipping->setObjectName("aVisualizeClipping"); m_visualizeClipping->setActionGroup(m_visualizeGroup); m_visualizeClipping->setData(QuickInspectorInterface::VisualizeClipping); m_visualizeClipping->setCheckable(true); m_visualizeClipping->setToolTip(tr("Visualize Clipping
    " "Items with the property clip set to true, will cut off their and their " "children's rendering at the items' bounds. While this is a handy feature it " "comes with quite some cost, like disabling some performance optimizations.
    " "With this tool enabled the QtQuick renderer highlights items, that have clipping " "enabled, so you can check for items, that have clipping enabled unnecessarily. ")); m_visualizeOverdraw = new QAction(UIResources::themedIcon(QLatin1String("visualize-overdraw.png")), tr("Visualize Overdraw"), this); m_visualizeOverdraw->setObjectName("aVisualizeOverdraw"); m_visualizeOverdraw->setActionGroup(m_visualizeGroup); m_visualizeOverdraw->setData(QuickInspectorInterface::VisualizeOverdraw); m_visualizeOverdraw->setCheckable(true); m_visualizeOverdraw->setToolTip(tr("Visualize Overdraw
    " "The QtQuick renderer doesn't detect if an item is obscured by another " "opaque item, is completely outside the scene or outside a clipped ancestor and " "thus doesn't need to be rendered. You thus need to take care of setting " "visible: false for hidden items, yourself.
    " "With this tool enabled the QtQuick renderer draws a 3D-Box visualizing the " "layers of items that are drawn.")); m_visualizeBatches = new QAction(UIResources::themedIcon(QLatin1String("visualize-batches.png")), tr("Visualize Batches"), this); m_visualizeBatches->setObjectName("aVisualizeBatches"); m_visualizeBatches->setActionGroup(m_visualizeGroup); m_visualizeBatches->setData(QuickInspectorInterface::VisualizeBatches); m_visualizeBatches->setCheckable(true); m_visualizeBatches->setToolTip(tr("Visualize Batches
    " "Where a traditional 2D API, such as QPainter, Cairo or Context2D, is written to " "handle thousands of individual draw calls per frame, OpenGL is a pure hardware " "API and performs best when the number of draw calls is very low and state " "changes are kept to a minimum. Therefore the QtQuick renderer combines the " "rendering of similar items into single batches.
    " "Some settings (like clip: true) will cause the batching to fail, though, " "causing items to be rendered separately. With this tool enabled the QtQuick " "renderer visualizes those batches, by drawing all items that are batched using " "the same color. The fewer colors you see in this mode the better.")); m_visualizeChanges = new QAction(UIResources::themedIcon(QLatin1String("visualize-changes.png")), tr("Visualize Changes"), this); m_visualizeChanges->setObjectName("aVisualizeChanges"); m_visualizeChanges->setActionGroup(m_visualizeGroup); m_visualizeChanges->setData(QuickInspectorInterface::VisualizeChanges); m_visualizeChanges->setCheckable(true); m_visualizeChanges->setToolTip(tr("Visualize Changes
    " "The QtQuick scene is only repainted, if some item changes in a visual manner. " "Unnecessary repaints can have a bad impact on the performance. With this tool " "enabled, the QtQuick renderer will thus on each repaint highlight the item(s), " "that caused the repaint.")); m_visualizeTraces = new QAction(UIResources::themedIcon(QLatin1String("visualize-traces.png")), tr("Visualize Controls"), this); m_visualizeTraces->setObjectName("aVisualizeTraces"); m_visualizeTraces->setActionGroup(m_visualizeGroup); m_visualizeTraces->setData(QuickInspectorInterface::VisualizeTraces); m_visualizeTraces->setCheckable(true); m_visualizeTraces->setToolTip(tr("Visualize Controls
    " "The QtQuick scene is rendered normally, in addition overlays will " "highlight Qt Quick Controls.")); m_serverSideDecorationsEnabled = new QAction(UIResources::themedIcon(QLatin1String("server-decorations.png")), tr("Decorate Target"), this); m_serverSideDecorationsEnabled->setObjectName("aServerSideDecorationsEnabled"); m_serverSideDecorationsEnabled->setCheckable(true); m_serverSideDecorationsEnabled->setToolTip(tr("Decorate Target
    " "This enables diagnostic decorations on the target application, " "such as anchor and layout information, helper grids or control overlays.")); auto *gridSettingsAction = new QWidgetAction(this); gridSettingsAction->setObjectName("aGridSettingsAction"); gridSettingsAction->setDefaultWidget(m_gridSettingsWidget); m_gridSettingsMenu = new QMenu(tr("Layout Grid"), this); m_gridSettingsMenu->setObjectName("mLayoutGrid"); m_gridSettingsMenu->menuAction()->setObjectName("aLayoutGrid"); m_gridSettingsMenu->setIcon(UIResources::themedIcon(QLatin1String("grid-settings.png"))); m_gridSettingsMenu->setToolTip(tr("Layout Grid
    " "Configure the layout helper grid.")); m_gridSettingsMenu->setToolTipsVisible(true); m_gridSettingsMenu->addAction(gridSettingsAction); #if defined(Q_OS_MAC) // Workaround QTBUG-60424 connect(m_gridSettingsMenu, &QMenu::aboutToShow, m_gridSettingsWidget, static_cast(&QWidget::update)); #endif m_toolBar->addActions(m_visualizeGroup->actions()); connect(m_visualizeGroup, &QActionGroup::triggered, this, &QuickSceneControlWidget::visualizeActionTriggered); m_toolBar->addSeparator(); m_toolBar->addActions(m_previewWidget->interactionModeActions()->actions()); m_toolBar->addSeparator(); m_toolBar->addAction(m_serverSideDecorationsEnabled); connect(m_serverSideDecorationsEnabled, &QAction::triggered, this, &QuickSceneControlWidget::serverSideDecorationsTriggered); m_toolBar->addSeparator(); m_toolBar->addAction(m_previewWidget->zoomOutAction()); m_zoomCombobox = new QComboBox(this); m_zoomCombobox->setModel(m_previewWidget->zoomLevelModel()); // macOS and some platforms expect to use *small* controls in such small toolbar m_zoomCombobox->setAttribute(Qt::WA_MacSmallSize); connect(m_zoomCombobox, static_cast(&QComboBox::currentIndexChanged), m_previewWidget, &RemoteViewWidget::setZoomLevel); connect(m_previewWidget, &RemoteViewWidget::zoomLevelChanged, m_zoomCombobox, &QComboBox::setCurrentIndex); m_zoomCombobox->setCurrentIndex(m_previewWidget->zoomLevelIndex()); QAction *zoomComboBox = m_toolBar->addWidget(m_zoomCombobox); zoomComboBox->setObjectName("aZoomComboBox"); m_toolBar->addAction(m_previewWidget->zoomInAction()); connect(m_gridSettingsWidget, &GridSettingsWidget::enabledChanged, this, &QuickSceneControlWidget::gridEnabledChanged); connect(m_gridSettingsWidget, &GridSettingsWidget::offsetChanged, this, &QuickSceneControlWidget::gridOffsetChanged); connect(m_gridSettingsWidget, &GridSettingsWidget::cellSizeChanged, this, &QuickSceneControlWidget::gridCellSizeChanged); setMinimumWidth(std::max(minimumWidth(), m_toolBar->sizeHint().width())); m_layout->setMenuBar(m_toolBar); m_layout->addWidget(m_previewWidget); connect(m_previewWidget, &RemoteViewWidget::stateChanged, this, &QuickSceneControlWidget::stateChanged); auto *menuSeparator = new QAction(this); menuSeparator->setSeparator(true); addActions(m_toolBar->actions() << menuSeparator << m_legendTool->visibilityAction() << m_gridSettingsMenu->menuAction()); } void QuickSceneControlWidget::resizeEvent(QResizeEvent *e) { m_toolBar->setGeometry(0, 0, width(), m_toolBar->sizeHint().height()); QWidget::resizeEvent(e); } void QuickSceneControlWidget::visualizeActionTriggered(QAction *current) { if (!current || !current->isChecked()) { m_inspectorInterface->setCustomRenderMode(QuickInspectorInterface::NormalRendering); } else { // QActionGroup requires exactly one selected, but we need 0 or 1 selected foreach (auto action, m_visualizeGroup->actions()) { if (action != current) action->setChecked(false); } m_inspectorInterface->setCustomRenderMode(static_cast(current->data().toInt())); } emit m_previewWidget->stateChanged(); } void QuickSceneControlWidget::serverSideDecorationsTriggered(bool enabled) { m_serverSideDecorationsEnabled->setChecked(enabled); m_inspectorInterface->setServerSideDecorationsEnabled(enabled); emit m_previewWidget->stateChanged(); } void QuickSceneControlWidget::gridEnabledChanged(bool enabled) { QuickDecorationsSettings settings = m_previewWidget->overlaySettings(); settings.gridEnabled = enabled; setOverlaySettings(settings); } void QuickSceneControlWidget::gridOffsetChanged(const QPoint &value) { QuickDecorationsSettings settings = m_previewWidget->overlaySettings(); settings.gridOffset = value; setOverlaySettings(settings); } void QuickSceneControlWidget::gridCellSizeChanged(const QSize &value) { QuickDecorationsSettings settings = m_previewWidget->overlaySettings(); settings.gridCellSize = value; setOverlaySettings(settings); } void QuickSceneControlWidget::setOverlaySettings(const QuickDecorationsSettings &settings) { m_inspectorInterface->setOverlaySettings(settings); } void QuickSceneControlWidget::setSupportsCustomRenderModes( QuickInspectorInterface::Features supportedCustomRenderModes) { m_visualizeClipping->setEnabled( supportedCustomRenderModes & QuickInspectorInterface::CustomRenderModeClipping); m_visualizeBatches->setEnabled( supportedCustomRenderModes & QuickInspectorInterface::CustomRenderModeBatches); m_visualizeOverdraw->setEnabled( supportedCustomRenderModes & QuickInspectorInterface::CustomRenderModeOverdraw); m_visualizeChanges->setEnabled( supportedCustomRenderModes & QuickInspectorInterface::CustomRenderModeChanges); m_visualizeTraces->setEnabled( supportedCustomRenderModes & QuickInspectorInterface::CustomRenderModeTraces); } void QuickSceneControlWidget::setServerSideDecorationsState(bool enabled) { m_serverSideDecorationsEnabled->setChecked(enabled); } void QuickSceneControlWidget::setOverlaySettingsState(const QuickDecorationsSettings &settings) { m_previewWidget->setOverlaySettings(settings); m_gridSettingsWidget->setOverlaySettings(settings); m_legendTool->setOverlaySettings(settings); emit m_previewWidget->stateChanged(); } QuickInspectorInterface::RenderMode QuickSceneControlWidget::customRenderMode() const { const QAction *checkedAction = ::checkedAction(m_visualizeGroup); if (checkedAction) { return static_cast(checkedAction->data().toInt()); } return QuickInspectorInterface::NormalRendering; } void QuickSceneControlWidget::setCustomRenderMode(QuickInspectorInterface::RenderMode customRenderMode) { if (this->customRenderMode() == customRenderMode) return; foreach (auto action, m_visualizeGroup->actions()) { if (action) action->setChecked(static_cast(action->data().toInt()) == customRenderMode); } visualizeActionTriggered(checkedAction(m_visualizeGroup)); } QuickScenePreviewWidget *QuickSceneControlWidget::previewWidget() { return m_previewWidget; } bool QuickSceneControlWidget::serverSideDecorationsEnabled() const { return m_serverSideDecorationsEnabled->isChecked(); } void QuickSceneControlWidget::setServerSideDecorationsEnabled(bool enabled) { if (m_serverSideDecorationsEnabled->isChecked() == enabled) return; m_serverSideDecorationsEnabled->setChecked(enabled); serverSideDecorationsTriggered(enabled); } gammaray-2.11.3/plugins/quickinspector/quickscenecontrolwidget.h000066400000000000000000000064571413310443000252350ustar00rootroot00000000000000/* quickscenepreviewwidget.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Christoph Sterz Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef QUICKSCENECONTROLWIDGET_H #define QUICKSCENECONTROLWIDGET_H #include #include "quickdecorationsdrawer.h" #include "quickinspectorinterface.h" QT_BEGIN_NAMESPACE class QAction; class QActionGroup; class QComboBox; class QLabel; class QMenu; class QToolBar; class QVBoxLayout; QT_END_NAMESPACE namespace GammaRay { class QuickInspectorInterface; class GridSettingsWidget; class QuickScenePreviewWidget; class QuickOverlayLegend; class QuickSceneControlWidget : public QWidget { Q_OBJECT public: explicit QuickSceneControlWidget(QuickInspectorInterface *inspector, QWidget *parent = nullptr); Q_INVOKABLE void setOverlaySettings(const GammaRay::QuickDecorationsSettings &settings); void setSupportsCustomRenderModes(QuickInspectorInterface::Features supportedCustomRenderModes); void setServerSideDecorationsState(bool enabled); void setOverlaySettingsState(const QuickDecorationsSettings &settings); bool serverSideDecorationsEnabled() const; void setServerSideDecorationsEnabled(bool enabled); QuickInspectorInterface::RenderMode customRenderMode() const; void setCustomRenderMode(QuickInspectorInterface::RenderMode customRenderMode); QuickScenePreviewWidget *previewWidget(); signals: void stateChanged(); private Q_SLOTS: void visualizeActionTriggered(QAction* current); void serverSideDecorationsTriggered(bool enabled); void gridEnabledChanged(bool enabled); void gridOffsetChanged(const QPoint &value); void gridCellSizeChanged(const QSize &value); private: void resizeEvent(QResizeEvent *e) override; QVBoxLayout *m_layout; QToolBar *m_toolBar; QComboBox *m_zoomCombobox; QActionGroup *m_visualizeGroup; QAction *m_visualizeClipping; QAction *m_visualizeOverdraw; QAction *m_visualizeBatches; QAction *m_visualizeChanges; QAction *m_visualizeTraces; QAction *m_serverSideDecorationsEnabled; QMenu *m_gridSettingsMenu; QuickScenePreviewWidget *m_previewWidget; GridSettingsWidget *m_gridSettingsWidget; QuickOverlayLegend *m_legendTool; QuickInspectorInterface *m_inspectorInterface; }; } // namespace GammaRay #endif // QUICKSCENECONTROLWIDGET_H gammaray-2.11.3/plugins/quickinspector/quickscenegraphmodel.cpp000066400000000000000000000335501413310443000250200ustar00rootroot00000000000000/* quickscenegraphmodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "quickscenegraphmodel.h" #include #include "quickitemmodelroles.h" #include #include #include #include Q_DECLARE_METATYPE(QSGNode *) using namespace GammaRay; QuickSceneGraphModel::QuickSceneGraphModel(QObject *parent) : ObjectModelBase(parent) , m_rootNode(nullptr) { } QuickSceneGraphModel::~QuickSceneGraphModel() = default; void QuickSceneGraphModel::setWindow(QQuickWindow *window) { beginResetModel(); clear(); if (m_window) disconnect(m_window.data(), &QQuickWindow::afterRendering, this, nullptr); m_window = window; m_rootNode = currentRootNode(); if (m_window && m_rootNode) { updateSGTree(false); connect(m_window.data(), &QQuickWindow::afterRendering, this, [this]{ updateSGTree(); }); } endResetModel(); } void QuickSceneGraphModel::updateSGTree(bool emitSignals) { auto root = currentRootNode(); if (root != m_rootNode) { // everything changed, reset beginResetModel(); clear(); m_rootNode = root; if (m_window && m_rootNode) updateSGTree(false); endResetModel(); } else { m_childParentMap[m_rootNode] = nullptr; m_parentChildMap[nullptr].resize(1); m_parentChildMap[nullptr][0] = m_rootNode; populateFromNode(m_rootNode, emitSignals); collectItemNodes(m_window->contentItem()); } } QSGNode *QuickSceneGraphModel::currentRootNode() const { if (!m_window) return nullptr; QQuickItem *item = m_window->contentItem(); QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(item); QSGNode *root = itemPriv->itemNode(); while (root->parent()) // Ensure that we really get the very root node. root = root->parent(); return root; } QVariant QuickSceneGraphModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); QSGNode *node = reinterpret_cast(index.internalPointer()); if (role == Qt::DisplayRole) { if (index.column() == 0) { return Util::addressToString(node); } else if (index.column() == 1) { switch (node->type()) { case QSGNode::BasicNodeType: return "Node"; case QSGNode::GeometryNodeType: return "Geometry Node"; case QSGNode::TransformNodeType: return "Transform Node"; case QSGNode::ClipNodeType: return "Clip Node"; case QSGNode::OpacityNodeType: return "Opacity Node"; case QSGNode::RootNodeType: return "Root Node"; case QSGNode::RenderNodeType: return "Render Node"; } } } else if (role == ObjectModel::ObjectRole) { return QVariant::fromValue(node); } return QVariant(); } int QuickSceneGraphModel::rowCount(const QModelIndex &parent) const { if (parent.column() == 1) return 0; QSGNode *parentNode = reinterpret_cast(parent.internalPointer()); return m_parentChildMap.value(parentNode).size(); } QModelIndex QuickSceneGraphModel::parent(const QModelIndex &child) const { QSGNode *childNode = reinterpret_cast(child.internalPointer()); return indexForNode(m_childParentMap.value(childNode)); } QModelIndex QuickSceneGraphModel::index(int row, int column, const QModelIndex &parent) const { QSGNode *parentNode = reinterpret_cast(parent.internalPointer()); const QVector children = m_parentChildMap.value(parentNode); if (row < 0 || column < 0 || row >= children.size() || column >= columnCount()) return {}; return createIndex(row, column, children.at(row)); } void QuickSceneGraphModel::clear() { m_childParentMap.clear(); m_parentChildMap.clear(); } // indexForNode() is expensive, so only use it when really needed #define GET_INDEX if (emitSignals && !hasMyIndex) { myIndex = indexForNode(node); hasMyIndex = true; \ } void QuickSceneGraphModel::populateFromNode(QSGNode *node, bool emitSignals) { if (!node) return; QVector &childList = m_parentChildMap[node]; QVector newChildList; newChildList.reserve(node->childCount()); for (QSGNode *childNode = node->firstChild(); childNode; childNode = childNode->nextSibling()) newChildList.append(childNode); QModelIndex myIndex; // don't call indexForNode(node) here yet, in the common case of few changes we waste a lot of time here bool hasMyIndex = false; std::sort(newChildList.begin(), newChildList.end()); auto i = childList.begin(); auto j = newChildList.constBegin(); while (i != childList.end() && j != newChildList.constEnd()) { if (*i < *j) { // handle deleted node emit nodeDeleted(*i); GET_INDEX if (emitSignals) { const auto idx = std::distance(childList.begin(), i); beginRemoveRows(myIndex, idx, idx); } pruneSubTree(*i); i = childList.erase(i); if (emitSignals) endRemoveRows(); } else if (*i > *j) { // handle added node GET_INDEX const auto idx = std::distance(childList.begin(), i); if (m_childParentMap.contains(*j)) { // move from elsewhere in our tree const auto sourceIdx = indexForNode(*j); Q_ASSERT(sourceIdx.isValid()); #if 0 if (emitSignals) beginMoveRows(sourceIdx.parent(), sourceIdx.row(), sourceIdx.row(), myIndex, idx); m_parentChildMap[m_childParentMap.value(*j)].remove(sourceIdx.row()); m_childParentMap.insert(*j, node); i = childList.insert(i, *j); if (emitSignals) endMoveRows(); #else if (emitSignals) { beginRemoveRows(sourceIdx.parent(), sourceIdx.row(), sourceIdx.row()); } m_parentChildMap[m_childParentMap.value(*j)].remove(sourceIdx.row()); m_childParentMap.remove(*j); if (emitSignals) { endRemoveRows(); beginInsertRows(myIndex, idx, idx); } m_childParentMap.insert(*j, node); i = childList.insert(i, *j); if (emitSignals) { endInsertRows(); } #endif populateFromNode(*j, emitSignals); } else { // entirely new if (emitSignals) beginInsertRows(myIndex, idx, idx); m_childParentMap.insert(*j, node); i = childList.insert(i, *j); populateFromNode(*j, false); if (emitSignals) endInsertRows(); } ++i; ++j; } else { // already known node, no change populateFromNode(*j, emitSignals); ++i; ++j; } } if (i == childList.end() && j != newChildList.constEnd()) { // Add remaining new items to list and inform the client // process the remaining items in pairs of n entirely new ones and 0-1 moved ones GET_INDEX while (j != newChildList.constEnd()) { const auto newBegin = j; while (j != newChildList.constEnd() && !m_childParentMap.contains(*j)) ++j; // newBegin to j - 1 is new, j is either moved or end if (newBegin != j) { // new elements if (emitSignals) { const auto idx = childList.size(); const auto count = std::distance(newBegin, j); beginInsertRows(myIndex, idx, idx + count - 1); } for (auto it = newBegin; it != j; ++it) { m_childParentMap.insert(*it, node); childList.append(*it); } for (auto it = newBegin; it != j; ++it) populateFromNode(*it, false); if (emitSignals) endInsertRows(); } if (j != newChildList.constEnd() && m_childParentMap.contains(*j)) { // one moved element, important to recheck if this is still a move, in case the above has removed it meanwhile... const auto sourceIdx = indexForNode(*j); Q_ASSERT(sourceIdx.isValid()); #if 0 if (emitSignals) { const auto idx = childList.size(); beginMoveRows(sourceIdx.parent(), sourceIdx.row(), sourceIdx.row(), myIndex, idx); } m_parentChildMap[m_childParentMap.value(*j)].remove(sourceIdx.row()); m_childParentMap.insert(*j, node); childList.append(*j); if (emitSignals) endMoveRows(); #else if (emitSignals) { beginRemoveRows(sourceIdx.parent(), sourceIdx.row(), sourceIdx.row()); } m_parentChildMap[m_childParentMap.value(*j)].remove(sourceIdx.row()); m_childParentMap.remove(*j); if (emitSignals) { endRemoveRows(); const auto idx = childList.size(); beginInsertRows(myIndex, idx, idx); } m_childParentMap.insert(*j, node); childList.append(*j); if (emitSignals) { endInsertRows(); } #endif populateFromNode(*j, emitSignals); ++j; } } } else if (i != childList.end()) { // Inform the client about the removed rows GET_INDEX const auto idx = std::distance(childList.begin(), i); const auto count = std::distance(i, childList.end()); for (auto it = i; it != childList.end(); ++it) emit nodeDeleted(*it); if (emitSignals) beginRemoveRows(myIndex, idx, idx + count - 1); for (; i != childList.end(); ++i) pruneSubTree(*i); childList.remove(idx, count); if (emitSignals) endRemoveRows(); } Q_ASSERT(childList == newChildList); } #undef GET_INDEX void QuickSceneGraphModel::collectItemNodes(QQuickItem *item) { if (!item) return; QQuickItemPrivate *priv = QQuickItemPrivate::get(item); if (!priv->itemNodeInstance) // Explicitly avoid calling priv->itemNode() here, which would create a new node outside the scenegraph's behavior. return; QSGNode *itemNode = priv->itemNodeInstance; m_itemItemNodeMap[item] = itemNode; m_itemNodeItemMap[itemNode] = item; foreach (QQuickItem *child, item->childItems()) collectItemNodes(child); } QModelIndex QuickSceneGraphModel::indexForNode(QSGNode *node) const { if (!node) return {}; QSGNode *parent = m_childParentMap.value(node); const QVector &siblings = m_parentChildMap[parent]; auto it = std::lower_bound(siblings.constBegin(), siblings.constEnd(), node); if (it == siblings.constEnd() || *it != node) return QModelIndex(); const int row = std::distance(siblings.constBegin(), it); return createIndex(row, 0, node); } QSGNode *QuickSceneGraphModel::sgNodeForItem(QQuickItem *item) const { return m_itemItemNodeMap[item]; } QQuickItem *QuickSceneGraphModel::itemForSgNode(QSGNode *node) const { while (node && !m_itemNodeItemMap.contains(node)) { // If there's no entry for node, take its parent node = m_childParentMap[node]; } return m_itemNodeItemMap[node]; } bool QuickSceneGraphModel::verifyNodeValidity(QSGNode *node) { if (node == m_rootNode) return true; QQuickItem *item = itemForSgNode(node); QSGNode *itemNode = QQuickItemPrivate::get(item)->itemNode(); bool valid = itemNode == node || recursivelyFindChild(itemNode, node); if (!valid) { // The tree got dirty without us noticing. Expecting more to be invalid, // so update the whole tree to ensure it's current. setWindow(m_window); } return valid; } bool QuickSceneGraphModel::recursivelyFindChild(QSGNode *root, QSGNode *child) const { for (QSGNode *childNode = root->firstChild(); childNode; childNode = childNode->nextSibling()) { if (childNode == child || recursivelyFindChild(childNode, child)) return true; } return false; } void QuickSceneGraphModel::pruneSubTree(QSGNode *node) { foreach (auto child, m_parentChildMap.value(node)) pruneSubTree(child); m_parentChildMap.remove(node); m_childParentMap.remove(node); } gammaray-2.11.3/plugins/quickinspector/quickscenegraphmodel.h000066400000000000000000000056411413310443000244650ustar00rootroot00000000000000/* quickscenegraphmodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_QUICKINSPECTOR_QUICKSCENEGRAPHMODEL_H #define GAMMARAY_QUICKINSPECTOR_QUICKSCENEGRAPHMODEL_H #include #include "core/objectmodelbase.h" #include #include #include QT_BEGIN_NAMESPACE class QSGNode; class QQuickItem; class QQuickWindow; QT_END_NAMESPACE namespace GammaRay { /** QQ2 scene graph model. */ class QuickSceneGraphModel : public ObjectModelBase { Q_OBJECT public: explicit QuickSceneGraphModel(QObject *parent = nullptr); ~QuickSceneGraphModel() override; void setWindow(QQuickWindow *window); QVariant data(const QModelIndex &index, int role) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; QModelIndex parent(const QModelIndex &child) const override; QModelIndex index(int row, int column, const QModelIndex &parent) const override; QModelIndex indexForNode(QSGNode *node) const; QSGNode *sgNodeForItem(QQuickItem *item) const; QQuickItem *itemForSgNode(QSGNode *node) const; bool verifyNodeValidity(QSGNode *node); signals: void nodeDeleted(QSGNode *node); private slots: void updateSGTree(bool emitSignals = true); private: void clear(); QSGNode *currentRootNode() const; void populateFromNode(QSGNode *node, bool emitSignals); void collectItemNodes(QQuickItem *item); bool recursivelyFindChild(QSGNode *root, QSGNode *child) const; void pruneSubTree(QSGNode *node); QPointer m_window; QSGNode *m_rootNode; QHash m_childParentMap; QHash > m_parentChildMap; QHash m_itemItemNodeMap; QHash m_itemNodeItemMap; }; } #endif // GAMMARAY_QUICKSCENEGRAPHMODEL_H gammaray-2.11.3/plugins/quickinspector/quickscenepreviewwidget.cpp000066400000000000000000000166111413310443000255620ustar00rootroot00000000000000/* quickscenepreviewwidget.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "quickscenepreviewwidget.h" #include "quickinspectorinterface.h" #include #include #include #include using namespace GammaRay; static const qint32 QuickScenePreviewWidgetStateVersion = 4; QT_BEGIN_NAMESPACE GAMMARAY_ENUM_STREAM_OPERATORS(GammaRay::QuickInspectorInterface::RenderMode) QT_END_NAMESPACE QuickScenePreviewWidget::QuickScenePreviewWidget(QuickSceneControlWidget *control, QWidget *parent) : RemoteViewWidget(parent) , m_control(control) { connect(this, &RemoteViewWidget::frameChanged, this, &QuickScenePreviewWidget::saveScreenshot); setName(QStringLiteral("com.kdab.GammaRay.QuickRemoteView")); setUnavailableText(tr("No remote view available.\n(This happens e.g. when the window is minimized or the scene is hidden)")); } QuickScenePreviewWidget::~QuickScenePreviewWidget() = default; void QuickScenePreviewWidget::restoreState(const QByteArray &state) { if (state.isEmpty()) return; QDataStream stream(state); qint32 version; QuickInspectorInterface::RenderMode mode = m_control->customRenderMode(); bool drawDecorations = m_control->serverSideDecorationsEnabled(); QPointF gridOffset = m_overlaySettings.gridOffset; QSizeF gridCellSize = m_overlaySettings.gridCellSize; bool gridEnabled = m_overlaySettings.gridEnabled; RemoteViewWidget::restoreState(stream); stream >> version; switch (version) { case 1: { stream >> mode ; break; } case 2: { stream >> mode >> drawDecorations ; break; } case 3: { stream >> mode >> drawDecorations >> gridOffset >> gridCellSize ; break; } case QuickScenePreviewWidgetStateVersion: { stream >> mode >> drawDecorations >> gridOffset >> gridCellSize >> gridEnabled ; break; } } m_control->setCustomRenderMode(mode); m_control->setServerSideDecorationsEnabled(drawDecorations); QuickDecorationsSettings settings = m_overlaySettings; settings.gridOffset = gridOffset; settings.gridCellSize = gridCellSize; settings.componentsTraces = (mode == QuickInspectorInterface::VisualizeTraces); settings.gridEnabled = gridEnabled; if (settings != m_overlaySettings) { m_control->setOverlaySettings(settings); } } QByteArray QuickScenePreviewWidget::saveState() const { QByteArray data; { QDataStream stream(&data, QIODevice::WriteOnly); RemoteViewWidget::saveState(stream); stream << QuickScenePreviewWidgetStateVersion; switch (QuickScenePreviewWidgetStateVersion) { case 1: { stream << m_control->customRenderMode() ; break; } case 2: { stream << m_control->customRenderMode() << m_control->serverSideDecorationsEnabled() ; break; } case 3: { stream << m_control->customRenderMode() << m_control->serverSideDecorationsEnabled() << m_overlaySettings.gridOffset << m_overlaySettings.gridCellSize ; break; } case 4: { stream << m_control->customRenderMode() << m_control->serverSideDecorationsEnabled() << m_overlaySettings.gridOffset << m_overlaySettings.gridCellSize << m_overlaySettings.gridEnabled ; break; } } } return data; } void QuickScenePreviewWidget::resizeEvent(QResizeEvent *e) { RemoteViewWidget::resizeEvent(e); } void QuickScenePreviewWidget::renderDecoration(QPainter *p, double zoom) const { // Scaling and translations on QuickItemGeometry will be done on demand if (frame().data().userType() == qMetaTypeId()) { // scaled and translated const auto itemGeometry = frame().data().value(); const QuickDecorationsRenderInfo renderInfo(m_overlaySettings, itemGeometry, frame().viewRect(), zoom); QuickDecorationsDrawer drawer(QuickDecorationsDrawer::Decorations, *p, renderInfo); drawer.render(); } else if (frame().data().userType() == qMetaTypeId>()) { const auto itemsGeometry = frame().data().value>(); const QuickDecorationsTracesInfo tracesInfo(m_overlaySettings, itemsGeometry, frame().viewRect(), zoom); QuickDecorationsDrawer drawer(QuickDecorationsDrawer::Traces, *p, tracesInfo); drawer.render(); } } void QuickScenePreviewWidget::drawDecoration(QPainter *p) { renderDecoration(p, zoom()); } QuickDecorationsSettings QuickScenePreviewWidget::overlaySettings() const { return m_overlaySettings; } void QuickScenePreviewWidget::setOverlaySettings(const QuickDecorationsSettings &settings) { m_overlaySettings = settings; update(); } void QuickScenePreviewWidget::requestCompleteFrame(const CompleteFrameRequest &request) { if (m_pendingCompleteFrame.isValid()) { qWarning("A pending complete frame request is already running."); return; } m_pendingCompleteFrame = request; remoteViewInterface()->requestCompleteFrame(); } void QuickScenePreviewWidget::saveScreenshot() { if (!m_pendingCompleteFrame.isValid() || !hasValidCompleteFrame()) return; const QImage &source(frame().image()); QImage img(source.size(), source.format()); img.setDevicePixelRatio(source.devicePixelRatio()); QPainter p(&img); p.save(); p.setTransform(frame().transform(), true); p.drawImage(QPoint(), source); p.restore(); if (m_pendingCompleteFrame.drawDecoration) renderDecoration(&p, 1.0); p.end(); img.save(m_pendingCompleteFrame.filePath); m_pendingCompleteFrame.reset(); } gammaray-2.11.3/plugins/quickinspector/quickscenepreviewwidget.h000066400000000000000000000057221413310443000252300ustar00rootroot00000000000000/* quickscenepreviewwidget.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_QUICKINSPECTOR_QUICKSCENEPREVIEWWIDGET_H #define GAMMARAY_QUICKINSPECTOR_QUICKSCENEPREVIEWWIDGET_H #include "quickdecorationsdrawer.h" #include "quickitemgeometry.h" #include "quickinspectorinterface.h" #include "quickscenecontrolwidget.h" #include namespace GammaRay { class QuickInspectorInterface; struct CompleteFrameRequest { CompleteFrameRequest(const QString &filePath = QString(), bool drawDecoration = false) : filePath(filePath) , drawDecoration(drawDecoration) { } bool isValid() const { return !filePath.isEmpty(); } void reset() { filePath.clear(); drawDecoration = false; } CompleteFrameRequest &operator=(const CompleteFrameRequest &other) { if (&other != this) { filePath = other.filePath; drawDecoration = other.drawDecoration; } return *this; } QString filePath; bool drawDecoration; }; class QuickScenePreviewWidget : public RemoteViewWidget { Q_OBJECT public: explicit QuickScenePreviewWidget(QuickSceneControlWidget *control, QWidget *parent = nullptr); ~QuickScenePreviewWidget() override; Q_INVOKABLE void restoreState(const QByteArray &state) override; QByteArray saveState() const override; QuickDecorationsSettings overlaySettings() const; void setOverlaySettings(const QuickDecorationsSettings &settings); void requestCompleteFrame(const CompleteFrameRequest &request); private slots: void saveScreenshot(); private: void drawDecoration(QPainter *p) override; void resizeEvent(QResizeEvent *e) override; void renderDecoration(QPainter *p, double zoom) const; QuickSceneControlWidget *m_control; QuickDecorationsSettings m_overlaySettings; CompleteFrameRequest m_pendingCompleteFrame; }; } // namespace GammaRay #endif gammaray-2.11.3/plugins/quickinspector/quickscreengrabber.cpp000066400000000000000000000655221413310443000244700ustar00rootroot00000000000000/* quickoverlay.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Filipe Azevedo Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "quickscreengrabber.h" #include #include #include #include #include #ifndef QT_NO_OPENGL #include #include #include #endif #include #include #include #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) #include #endif #include #include #include namespace GammaRay { class QQuickItemPropertyCache { public: static const QQuickItemPropertyCache &getPropertyCache(QQuickItem *item) { static QHash s_cache; const QMetaObject* meta = item->metaObject(); const auto it = s_cache.constFind(meta); if (it != s_cache.cend()) return *it; else return *s_cache.insert(meta, QQuickItemPropertyCache(meta)); } explicit QQuickItemPropertyCache(const QMetaObject *meta) : background(property(meta, "background")) , contentItem(property(meta, "contentItem")) , padding(property(meta, "padding")) { if (padding.isValid()) { leftPadding = property(meta, "leftPadding"); rightPadding = property(meta, "rightPadding"); topPadding = property(meta, "topPadding"); bottomPadding = property(meta, "bottomPadding"); } } QMetaProperty background; QMetaProperty contentItem; QMetaProperty padding; QMetaProperty leftPadding; QMetaProperty rightPadding; QMetaProperty topPadding; QMetaProperty bottomPadding; private: static inline QMetaProperty property(const QMetaObject *meta, const char *name) { return meta->property(meta->indexOfProperty(name)); } }; // We need random colors, but we also want the item // to keep its random color during scene changes to avoid // flickering due to color change. static QHash s_itemsColor; static QColor colorForItem(QQuickItem *item) { QColor color = s_itemsColor.value(item, QColor()); if (!color.isValid()) { const auto h = qHash(ObjectDataProvider::shortTypeName(item)); color = QColor::fromHsv(h % 360, 64 + h % 192, 128 + h % 128, 64); s_itemsColor[item] = color; } return color; } static bool quickItemZLessThan(QQuickItem *lhs, QQuickItem *rhs) { return lhs->z() < rhs->z(); } static bool quickItemZGreaterThan(QQuickItem *lhs, QQuickItem *rhs) { return lhs->z() > rhs->z(); } static QVector findItemByClassName(const char *className, QQuickItem *parent, const std::function &walker) { Q_ASSERT(parent); QVector items; if (!parent->window()) { return items; } if (parent != parent->window()->contentItem() && parent->inherits(className)) { items << parent; walker(parent); } QList childItems = parent->childItems(); // direct children of contentItem need to be sorted the over way so overlay is draw on top of the rest if (parent == parent->window()->contentItem()) { std::sort(childItems.begin(), childItems.end(), quickItemZGreaterThan); } else { std::sort(childItems.begin(), childItems.end(), quickItemZLessThan); } for (int i = childItems.size() - 1; i >= 0; --i) { // backwards to match z order const auto childItemChildItems = findItemByClassName(className, childItems.at(i), walker); if (!childItemChildItems.isEmpty()) items << childItemChildItems; } return items; } static QQuickItem *toplevelItem(QQuickItem *item) { Q_ASSERT(item); return item->window()->contentItem(); } static QPointF itemPos(QQuickItem *item) { Q_ASSERT(item); return {item->x(), item->y()}; } static QSizeF itemSize(QQuickItem *item) { Q_ASSERT(item); QSizeF size = QSizeF(item->width(), item->height()); // Fallback to children rect if needed if (size.isNull()) { size = item->childrenRect().size(); } return size; } static QRectF itemGeometry(QQuickItem *item) { return QRectF(itemPos(item), itemSize(item)); } static bool itemIsLayout(QQuickItem *item) { Q_ASSERT(item); return item->inherits("QQuickLayout"); } } using namespace GammaRay; ItemOrLayoutFacade::ItemOrLayoutFacade(QQuickItem *item) : m_object(item) { } QQuickItem *ItemOrLayoutFacade::layout() const { return isLayout() ? asLayout() : asItem(); } QQuickItem *ItemOrLayoutFacade::item() const { return isLayout() ? asLayout()->parentItem() : asItem(); } QRectF ItemOrLayoutFacade::geometry() const { return itemGeometry(isLayout() ? asLayout() : asItem()); } bool ItemOrLayoutFacade::isVisible() const { return item() ? item()->isVisible() : false; } QPointF ItemOrLayoutFacade::pos() const { return isLayout() ? itemGeometry(asLayout()).topLeft() : QPoint(0, 0); } bool ItemOrLayoutFacade::isLayout() const { return itemIsLayout(m_object); } std::unique_ptr AbstractScreenGrabber::get(QQuickWindow* window) { switch (graphicsApiFor(window)) { #ifndef QT_NO_OPENGL case RenderInfo::OpenGL: return std::unique_ptr(new OpenGLScreenGrabber(window)); #endif #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) case RenderInfo::Software: return std::unique_ptr(new SoftwareScreenGrabber(window)); #endif default: return nullptr; } } AbstractScreenGrabber::RenderInfo::GraphicsApi AbstractScreenGrabber::graphicsApiFor(QQuickWindow* window) { if (!window) { return RenderInfo::Unknown; } #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) return static_cast(window->rendererInterface()->graphicsApi()); #else return RenderInfo::OpenGL; #endif } AbstractScreenGrabber::AbstractScreenGrabber(QQuickWindow *window) : m_window(window) , m_currentToplevelItem(nullptr) { qRegisterMetaType(); placeOn(ItemOrLayoutFacade()); } GammaRay::AbstractScreenGrabber::~AbstractScreenGrabber() = default; QQuickWindow *AbstractScreenGrabber::window() const { return m_window; } QuickDecorationsSettings AbstractScreenGrabber::settings() const { return m_settings; } void AbstractScreenGrabber::setSettings(const QuickDecorationsSettings &settings) { if (m_settings == settings) return; m_settings = settings; updateOverlay(); } bool AbstractScreenGrabber::decorationsEnabled() const { return m_decorationsEnabled; } void AbstractScreenGrabber::setDecorationsEnabled(bool enabled) { if (m_decorationsEnabled == enabled) return; m_decorationsEnabled = enabled; updateOverlay(); } void AbstractScreenGrabber::placeOn(const ItemOrLayoutFacade &item) { if (item.isNull()) { if (!m_currentItem.isNull()) disconnectItemChanges(m_currentItem.data()); if (m_currentToplevelItem) { disconnectTopItemChanges(m_currentToplevelItem); // Ensure the overlay is cleared if (m_currentToplevelItem->window()) m_currentToplevelItem->window()->update(); } m_currentToplevelItem = nullptr; m_currentItem.clear(); updateOverlay(); return; } Q_ASSERT(item.item()->window() == m_window); if (!m_currentItem.isNull()) disconnectItemChanges(m_currentItem.data()); m_currentItem = item; QQuickItem *toplevel = toplevelItem(item.item()); Q_ASSERT(toplevel); if (toplevel != m_currentToplevelItem) { if (m_currentToplevelItem) { disconnectTopItemChanges(m_currentToplevelItem); // Ensure the overlay is cleared m_currentToplevelItem->window()->update(); } m_currentToplevelItem = toplevel; connectTopItemChanges(m_currentToplevelItem); } connectItemChanges(m_currentItem.data()); updateOverlay(); } QuickItemGeometry AbstractScreenGrabber::initFromItem(QQuickItem *item) { QuickItemGeometry itemGeometry; if (!item) { Q_ASSERT(false); return itemGeometry; } QQuickItem *parent = item->parentItem(); if (parent) { itemGeometry.itemRect = item->parentItem()->mapRectToScene( QRectF(item->x(), item->y(), item->width(), item->height())); } else { itemGeometry.itemRect = QRectF(0, 0, item->width(), item->height()); } itemGeometry.boundingRect = item->mapRectToScene(item->boundingRect()); itemGeometry.childrenRect = item->mapRectToScene(item->childrenRect()); const QQuickItemPropertyCache &cache = QQuickItemPropertyCache::getPropertyCache(item); QQuickItem *background = cache.background.read(item).value(); if (background) itemGeometry.backgroundRect = background->mapRectToScene(background->boundingRect()); QQuickItem *contentItem = cache.contentItem.read(item).value(); if (contentItem) itemGeometry.contentItemRect = contentItem->mapRectToScene(contentItem->boundingRect()); itemGeometry.transformOriginPoint = item->mapToScene(item->transformOriginPoint()); QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(item); QQuickAnchors *anchors = itemPriv->_anchors; if (anchors) { QQuickAnchors::Anchors usedAnchors = anchors->usedAnchors(); itemGeometry.left = (bool)(usedAnchors &QQuickAnchors::LeftAnchor) || anchors->fill(); itemGeometry.right = (bool)(usedAnchors &QQuickAnchors::RightAnchor) || anchors->fill(); itemGeometry.top = (bool)(usedAnchors &QQuickAnchors::TopAnchor) || anchors->fill(); itemGeometry.bottom = (bool)(usedAnchors &QQuickAnchors::BottomAnchor) || anchors->fill(); itemGeometry.baseline = (bool)(usedAnchors & QQuickAnchors::BaselineAnchor); itemGeometry.horizontalCenter = (bool)(usedAnchors &QQuickAnchors::HCenterAnchor) || anchors->centerIn(); itemGeometry.verticalCenter = (bool)(usedAnchors &QQuickAnchors::VCenterAnchor) || anchors->centerIn(); itemGeometry.leftMargin = anchors->leftMargin(); itemGeometry.rightMargin = anchors->rightMargin(); itemGeometry.topMargin = anchors->topMargin(); itemGeometry.bottomMargin = anchors->bottomMargin(); itemGeometry.horizontalCenterOffset = anchors->horizontalCenterOffset(); itemGeometry.verticalCenterOffset = anchors->verticalCenterOffset(); itemGeometry.baselineOffset = anchors->baselineOffset(); itemGeometry.margins = anchors->margins(); } itemGeometry.x = item->x(); itemGeometry.y = item->y(); if (cache.padding.isValid()) { itemGeometry.padding = cache.padding.read(item).toReal(); itemGeometry.leftPadding = cache.leftPadding.read(item).toReal(); itemGeometry.rightPadding = cache.rightPadding.read(item).toReal(); itemGeometry.topPadding = cache.topPadding.read(item).toReal(); itemGeometry.bottomPadding = cache.bottomPadding.read(item).toReal(); } else { itemGeometry.padding = qQNaN(); itemGeometry.leftPadding = qQNaN(); itemGeometry.rightPadding = qQNaN(); itemGeometry.topPadding = qQNaN(); itemGeometry.bottomPadding = qQNaN(); } itemGeometry.transform = itemPriv->itemToWindowTransform(); if (parent) { QQuickItemPrivate *parentPriv = QQuickItemPrivate::get(parent); itemGeometry.parentTransform = parentPriv->itemToWindowTransform(); } itemGeometry.traceColor = colorForItem(item); itemGeometry.traceTypeName = ObjectDataProvider::shortTypeName(item); itemGeometry.traceName = ObjectDataProvider::name(item); return itemGeometry; } void AbstractScreenGrabber::gatherRenderInfo() { // We are in the rendering thread at this point // And the gui thread is locked m_renderInfo.dpr = 1.0; // See QTBUG-53795 m_renderInfo.dpr = m_window->effectiveDevicePixelRatio(); m_renderInfo.windowSize = m_window->size(); #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) m_renderInfo.graphicsApi = static_cast(m_window->rendererInterface()->graphicsApi()); #else m_renderInfo.graphicsApi = RenderInfo::OpenGL; #endif m_grabbedFrame.itemsGeometry.clear(); m_grabbedFrame.itemsGeometryRect = QRectF(); if (m_window) { m_grabbedFrame.itemsGeometryRect = QRect(QPoint(), m_renderInfo.windowSize); if (m_settings.componentsTraces) { findItemByClassName("QQuickControl", m_window->contentItem(), [this](QQuickItem *item) { if (!item->isVisible()) return; QuickItemGeometry itemGeometry = initFromItem(item); m_grabbedFrame.itemsGeometry << itemGeometry; m_grabbedFrame.itemsGeometryRect |= itemGeometry.itemRect | itemGeometry.childrenRect | itemGeometry.boundingRect; }); } else { QuickItemGeometry itemGeometry; if (!m_currentItem.isNull()) itemGeometry = initFromItem(m_currentItem.data()); m_grabbedFrame.itemsGeometry << itemGeometry; m_grabbedFrame.itemsGeometryRect |= itemGeometry.itemRect | itemGeometry.childrenRect | itemGeometry.boundingRect; } } } void AbstractScreenGrabber::doDrawDecorations(QPainter &painter) { if (!m_decorationsEnabled) return; if (m_settings.componentsTraces) { const QuickDecorationsTracesInfo tracesInfo(m_settings, m_grabbedFrame.itemsGeometry, QRectF(QPointF(), m_renderInfo.windowSize), 1.0); QuickDecorationsDrawer drawer(QuickDecorationsDrawer::Traces, painter, tracesInfo); drawer.render(); } else { const QuickDecorationsRenderInfo renderInfo(m_settings, m_grabbedFrame.itemsGeometry.value(0), QRectF(QPointF(), m_renderInfo.windowSize), 1.0); QuickDecorationsDrawer drawer(QuickDecorationsDrawer::Decorations, painter, renderInfo); drawer.render(); } } void AbstractScreenGrabber::updateOverlay() { if (m_window) { if (!m_currentItem.isNull()) Q_ASSERT(m_currentItem.item()->window() == m_window); m_window->update(); } } void AbstractScreenGrabber::itemParentChanged(QQuickItem *parent) { Q_UNUSED(parent); if (!m_currentItem.isNull()) placeOn(m_currentItem); } void AbstractScreenGrabber::itemWindowChanged(QQuickWindow *window) { if (m_window == window) { if (!m_currentItem.isNull()) placeOn(m_currentItem); } else { placeOn(ItemOrLayoutFacade()); } } void AbstractScreenGrabber::connectItemChanges(QQuickItem *item) { connect(item, &QQuickItem::childrenRectChanged, this, &AbstractScreenGrabber::updateOverlay); connect(item, &QQuickItem::rotationChanged, this, &AbstractScreenGrabber::updateOverlay); connect(item, &QQuickItem::scaleChanged, this, &AbstractScreenGrabber::updateOverlay); connect(item, &QQuickItem::widthChanged, this, &AbstractScreenGrabber::updateOverlay); connect(item, &QQuickItem::heightChanged, this, &AbstractScreenGrabber::updateOverlay); connect(item, &QQuickItem::xChanged, this, &AbstractScreenGrabber::updateOverlay); connect(item, &QQuickItem::yChanged, this, &AbstractScreenGrabber::updateOverlay); connect(item, &QQuickItem::zChanged, this, &AbstractScreenGrabber::updateOverlay); connect(item, &QQuickItem::visibleChanged, this, &AbstractScreenGrabber::updateOverlay); connect(item, &QQuickItem::parentChanged, this, &AbstractScreenGrabber::itemParentChanged); connect(item, &QQuickItem::windowChanged, this, &AbstractScreenGrabber::itemWindowChanged); } void AbstractScreenGrabber::disconnectItemChanges(QQuickItem *item) { disconnect(item, &QQuickItem::childrenRectChanged, this, &AbstractScreenGrabber::updateOverlay); disconnect(item, &QQuickItem::rotationChanged, this, &AbstractScreenGrabber::updateOverlay); disconnect(item, &QQuickItem::scaleChanged, this, &AbstractScreenGrabber::updateOverlay); disconnect(item, &QQuickItem::widthChanged, this, &AbstractScreenGrabber::updateOverlay); disconnect(item, &QQuickItem::heightChanged, this, &AbstractScreenGrabber::updateOverlay); disconnect(item, &QQuickItem::xChanged, this, &AbstractScreenGrabber::updateOverlay); disconnect(item, &QQuickItem::yChanged, this, &AbstractScreenGrabber::updateOverlay); disconnect(item, &QQuickItem::zChanged, this, &AbstractScreenGrabber::updateOverlay); disconnect(item, &QQuickItem::visibleChanged, this, &AbstractScreenGrabber::updateOverlay); disconnect(item, &QQuickItem::parentChanged, this, &AbstractScreenGrabber::itemParentChanged); disconnect(item, &QQuickItem::windowChanged, this, &AbstractScreenGrabber::itemWindowChanged); } void AbstractScreenGrabber::connectTopItemChanges(QQuickItem *item) { connect(item, &QQuickItem::childrenRectChanged, this, &AbstractScreenGrabber::updateOverlay); connect(item, &QQuickItem::rotationChanged, this, &AbstractScreenGrabber::updateOverlay); connect(item, &QQuickItem::scaleChanged, this, &AbstractScreenGrabber::updateOverlay); connect(item, &QQuickItem::widthChanged, this, &AbstractScreenGrabber::updateOverlay); connect(item, &QQuickItem::heightChanged, this, &AbstractScreenGrabber::updateOverlay); } void AbstractScreenGrabber::disconnectTopItemChanges(QQuickItem *item) { disconnect(item, &QQuickItem::childrenRectChanged, this, &AbstractScreenGrabber::updateOverlay); disconnect(item, &QQuickItem::rotationChanged, this, &AbstractScreenGrabber::updateOverlay); disconnect(item, &QQuickItem::scaleChanged, this, &AbstractScreenGrabber::updateOverlay); disconnect(item, &QQuickItem::widthChanged, this, &AbstractScreenGrabber::updateOverlay); disconnect(item, &QQuickItem::heightChanged, this, &AbstractScreenGrabber::updateOverlay); } #ifndef QT_NO_OPENGL OpenGLScreenGrabber::OpenGLScreenGrabber(QQuickWindow *window) : AbstractScreenGrabber(window) , m_isGrabbing(false) { // Force DirectConnection else Auto lead to Queued which is not good. connect(m_window.data(), &QQuickWindow::afterSynchronizing, this, &OpenGLScreenGrabber::windowAfterSynchronizing, Qt::DirectConnection); connect(m_window.data(), &QQuickWindow::afterRendering, this, &OpenGLScreenGrabber::windowAfterRendering, Qt::DirectConnection); } OpenGLScreenGrabber::~OpenGLScreenGrabber() = default; void OpenGLScreenGrabber::requestGrabWindow(const QRectF &userViewport) { setGrabbingMode(true, userViewport); } void OpenGLScreenGrabber::setGrabbingMode(bool isGrabbing, const QRectF &userViewport) { QMutexLocker locker(&m_mutex); if (m_isGrabbing == isGrabbing) return; m_isGrabbing = isGrabbing; m_userViewport = userViewport; emit grabberReadyChanged(!m_isGrabbing); if (m_isGrabbing) updateOverlay(); } void OpenGLScreenGrabber::windowAfterSynchronizing() { // We are in the rendering thread at this point // And the gui thread is locked gatherRenderInfo(); } void OpenGLScreenGrabber::windowAfterRendering() { QMutexLocker locker(&m_mutex); // We are in the rendering thread at this point // And the gui thread is NOT locked Q_ASSERT(QOpenGLContext::currentContext() == m_window->openglContext()); if (m_isGrabbing) { const auto window = QRectF(QPoint(0,0), m_renderInfo.windowSize); const auto intersect = m_userViewport.isValid() ? window.intersected(m_userViewport) : window; // readout parameters // when in doubt, round x and y to floor--> reads one pixel more const int x = static_cast(std::floor(intersect.x() * m_renderInfo.dpr)); // correct y for gpu-flipped textures being read from the bottom const int y = static_cast(std::floor((m_renderInfo.windowSize.height() - intersect.height() - intersect.y()) * m_renderInfo.dpr)); // when in doubt, round up w and h --> also reads one pixel more int w = static_cast(std::ceil(intersect.width() * m_renderInfo.dpr)); int h = static_cast(std::ceil(intersect.height() * m_renderInfo.dpr)); // cap to viewport size (which we can overshoot due to rounding errors in highdpi scaling) QOpenGLFunctions *glFuncs = QOpenGLContext::currentContext()->functions(); int viewport[4]; glFuncs->glGetIntegerv(GL_VIEWPORT, viewport); if (x + w > viewport[2]) w = viewport[2] - x; if (y + h > viewport[3]) h = viewport[3] - y; m_grabbedFrame.transform.reset(); if (m_grabbedFrame.image.size() != QSize(w, h)) m_grabbedFrame.image = QImage(w, h, QImage::Format_RGBA8888); glFuncs->glReadPixels(x, y, w, h, GL_RGBA, GL_UNSIGNED_BYTE, m_grabbedFrame.image.bits()); // set transform to flip the read texture later, when displayed // Keep in mind that transforms are local coordinate (ie, not impacted by the device pixel ratio) m_grabbedFrame.transform.scale(1.0, -1.0); m_grabbedFrame.transform.translate(intersect.x() , -intersect.y() - intersect.height()); m_grabbedFrame.image.setDevicePixelRatio(m_renderInfo.dpr); // Let emit the signal even if our image is possibly null, this way we make perfect ping/pong // requests making it easier to unit test. emit sceneGrabbed(m_grabbedFrame); } drawDecorations(); m_window->resetOpenGLState(); if (m_isGrabbing) { locker.unlock(); setGrabbingMode(false, QRectF()); } else { emit sceneChanged(); } } void OpenGLScreenGrabber::drawDecorations() { // We are in the rendering thread at this point // And the gui thread is NOT locked QOpenGLPaintDevice device(m_renderInfo.windowSize * m_renderInfo.dpr); device.setDevicePixelRatio(m_renderInfo.dpr); QPainter p(&device); doDrawDecorations(p); } #endif #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) SoftwareScreenGrabber::SoftwareScreenGrabber(QQuickWindow* window) : AbstractScreenGrabber(window) { connect(m_window.data(), &QQuickWindow::afterRendering, this, &SoftwareScreenGrabber::windowAfterRendering, Qt::DirectConnection); connect(m_window.data(), &QQuickWindow::beforeRendering, this, &SoftwareScreenGrabber::windowBeforeRendering, Qt::DirectConnection); } SoftwareScreenGrabber::~SoftwareScreenGrabber() = default; void SoftwareScreenGrabber::updateOverlay() { if (m_window) { if (!m_currentItem.isNull()) Q_ASSERT(m_currentItem.item()->window() == m_window); #if QT_VERSION >= QT_VERSION_CHECK(5, 9, 3) auto renderer = softwareRenderer(); if (renderer) renderer->markDirty(); #endif m_window->update(); } } void SoftwareScreenGrabber::requestGrabWindow(const QRectF& userViewport) { Q_UNUSED(userViewport); m_isGrabbing = true; qreal dpr = 1.0; // See QTBUG-53795 dpr = m_window->effectiveDevicePixelRatio(); #if QT_VERSION >= QT_VERSION_CHECK(5, 9, 3) m_grabbedFrame.image = QImage(m_window->size() * dpr, QImage::Format_ARGB32_Premultiplied); m_grabbedFrame.image.setDevicePixelRatio(dpr); m_grabbedFrame.image.fill(Qt::white); QQuickWindowPrivate *winPriv = QQuickWindowPrivate::get(m_window); QSGSoftwareRenderer *renderer = softwareRenderer(); if (!renderer) return; QPaintDevice *regularRenderDevice = renderer->currentPaintDevice(); renderer->setCurrentPaintDevice(&m_grabbedFrame.image); renderer->markDirty(); winPriv->polishItems(); winPriv->syncSceneGraph(); winPriv->renderSceneGraph(m_window->size()); renderer->setCurrentPaintDevice(regularRenderDevice); #else m_grabbedFrame.image = m_window->grabWindow(); m_grabbedFrame.image.setDevicePixelRatio(dpr); #endif m_isGrabbing = false; emit sceneGrabbed(m_grabbedFrame); } void SoftwareScreenGrabber::drawDecorations() { #if QT_VERSION >= QT_VERSION_CHECK(5, 9, 3) auto renderer = softwareRenderer(); if (!renderer) return; QPainter p(renderer->currentPaintDevice()); p.setClipRegion(renderer->flushRegion()); doDrawDecorations(p); #endif } void SoftwareScreenGrabber::windowBeforeRendering() { #if QT_VERSION >= QT_VERSION_CHECK(5, 9, 3) QuickItemGeometry oldItemRect = m_grabbedFrame.itemsGeometry.size() ? m_grabbedFrame.itemsGeometry.front() : QuickItemGeometry(); // So far the vector never has more than one element... gatherRenderInfo(); QuickItemGeometry newItemRect = m_grabbedFrame.itemsGeometry.size() ? m_grabbedFrame.itemsGeometry.front() : QuickItemGeometry(); if (m_decorationsEnabled && newItemRect != oldItemRect) { // The item's scene coordinates can change unrecognizedly. If they do, we need a // full window repaint though, for the overlay to be correct. softwareRenderer()->markDirty(); } #endif } void SoftwareScreenGrabber::windowAfterRendering() { #if QT_VERSION >= QT_VERSION_CHECK(5, 9, 3) if (!m_isGrabbing) { drawDecorations(); emit sceneChanged(); } else { m_isGrabbing = false; } #endif } QSGSoftwareRenderer *SoftwareScreenGrabber::softwareRenderer() const { QQuickWindowPrivate *winPriv = QQuickWindowPrivate::get(m_window); if (!winPriv) return nullptr; QSGSoftwareRenderer *softwareRenderer = dynamic_cast(winPriv->renderer); return softwareRenderer; } #endif gammaray-2.11.3/plugins/quickinspector/quickscreengrabber.h000066400000000000000000000135051413310443000241270ustar00rootroot00000000000000/* quickscreengrabber.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Filipe Azevedo Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_QUICKINSPECTOR_QUICKSCREENGRABBER_H #define GAMMARAY_QUICKINSPECTOR_QUICKSCREENGRABBER_H #include "quickdecorationsdrawer.h" #include #include #include #include #include QT_BEGIN_NAMESPACE class QQuickWindow; #ifndef QT_NO_OPENGL class QOpenGLPaintDevice; #endif class QSGSoftwareRenderer; QT_END_NAMESPACE namespace GammaRay { class ItemOrLayoutFacade { public: ItemOrLayoutFacade() = default; ItemOrLayoutFacade(QQuickItem *item); //krazy:exclude=explicit /// Get either the layout of the widget or the this-pointer QQuickItem *layout() const; /// Get either the parent widget of the layout or the this-pointer QQuickItem *item() const; QRectF geometry() const; bool isVisible() const; QPointF pos() const; inline bool isNull() const { return !m_object; } inline QQuickItem *data() { return m_object; } inline QQuickItem *operator->() const { Q_ASSERT(!isNull()); return m_object; } inline void clear() { m_object = nullptr; } private: bool isLayout() const; inline QQuickItem *asLayout() const { return m_object; } inline QQuickItem *asItem() const { return m_object; } QPointer m_object; }; class GrabbedFrame { public: QImage image; QTransform transform; QRectF itemsGeometryRect; QVector itemsGeometry; }; class AbstractScreenGrabber : public QObject { Q_OBJECT public: struct RenderInfo { // Keep in sync with QSGRendererInterface::GraphicsApi enum GraphicsApi { Unknown, Software, OpenGL, Direct3D12 }; RenderInfo() : dpr(qQNaN()) { } qreal dpr; QSize windowSize; GraphicsApi graphicsApi = Unknown; }; explicit AbstractScreenGrabber(QQuickWindow *window); ~AbstractScreenGrabber() override; static RenderInfo::GraphicsApi graphicsApiFor(QQuickWindow *window); static std::unique_ptr get(QQuickWindow *window); QQuickWindow *window() const; QuickDecorationsSettings settings() const; void setSettings(const QuickDecorationsSettings &settings); bool decorationsEnabled() const; void setDecorationsEnabled(bool enabled); /** * Place the overlay on @p item * * @param item The overlay can be cover a widget or a layout of the current window */ void placeOn(const ItemOrLayoutFacade &item); virtual void requestGrabWindow(const QRectF &userViewport) = 0; signals: void grabberReadyChanged(bool ready); void sceneChanged(); void sceneGrabbed(const GammaRay::GrabbedFrame &frame); protected: void doDrawDecorations(QPainter &painter); void gatherRenderInfo(); virtual void drawDecorations() = 0; virtual void updateOverlay(); static QuickItemGeometry initFromItem(QQuickItem *item); private: void itemParentChanged(QQuickItem *parent); void itemWindowChanged(QQuickWindow *window); void connectItemChanges(QQuickItem *item); void disconnectItemChanges(QQuickItem *item); void connectTopItemChanges(QQuickItem *item); void disconnectTopItemChanges(QQuickItem *item); protected: QPointer m_window; QPointer m_currentToplevelItem; ItemOrLayoutFacade m_currentItem; QuickDecorationsSettings m_settings; bool m_decorationsEnabled = true; QRectF m_userViewport; GrabbedFrame m_grabbedFrame; RenderInfo m_renderInfo; }; #ifndef QT_NO_OPENGL class OpenGLScreenGrabber : public AbstractScreenGrabber { Q_OBJECT public: explicit OpenGLScreenGrabber(QQuickWindow *window); ~OpenGLScreenGrabber() override; void requestGrabWindow(const QRectF &userViewport) override; void drawDecorations() override; private: void setGrabbingMode(bool isGrabbingMode, const QRectF &userViewport); void windowAfterSynchronizing(); void windowAfterRendering(); bool m_isGrabbing; QMutex m_mutex; }; #endif #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) class SoftwareScreenGrabber : public AbstractScreenGrabber { Q_OBJECT public: explicit SoftwareScreenGrabber(QQuickWindow *window); ~SoftwareScreenGrabber() override; void requestGrabWindow(const QRectF &userViewport) override; void drawDecorations() override; private: void windowAfterRendering(); void windowBeforeRendering(); void updateOverlay() override; QSGSoftwareRenderer *softwareRenderer() const; bool m_isGrabbing = false; QPointF m_lastItemPosition; }; #endif } Q_DECLARE_METATYPE(GammaRay::GrabbedFrame) #endif gammaray-2.11.3/plugins/quickinspector/textureextension/000077500000000000000000000000001413310443000235465ustar00rootroot00000000000000gammaray-2.11.3/plugins/quickinspector/textureextension/qsgtexturegrabber.cpp000066400000000000000000000213111413310443000300100ustar00rootroot00000000000000/* qsgtexturegrabber.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "qsgtexturegrabber.h" #include #include #include #include #include #include #include #include #include #if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0) #include #endif #ifndef GL_TEXTURE_WIDTH #define GL_TEXTURE_WIDTH 0x1000 #endif #ifndef GL_TEXTURE_HEIGHT #define GL_TEXTURE_HEIGHT 0x1001 #endif #ifndef GL_BGRA #define GL_BGRA 0x80E1 #endif #include using namespace GammaRay; QSGTextureGrabber *QSGTextureGrabber::s_instance = nullptr; QSGTextureGrabber::QSGTextureGrabber(QObject *parent) : QObject(parent) , m_grabData(nullptr) , m_textureId(-1) { Q_ASSERT(!s_instance); s_instance = this; } QSGTextureGrabber::~QSGTextureGrabber() { s_instance = nullptr; } QSGTextureGrabber *QSGTextureGrabber::instance() { return s_instance; } void QSGTextureGrabber::objectCreated(QObject *obj) { if (auto window = qobject_cast(obj)) addQuickWindow(window); } void QSGTextureGrabber::addQuickWindow(QQuickWindow *window) { connect(window, &QQuickWindow::afterRendering, this, [this, window]() { windowAfterRendering(window); }, Qt::DirectConnection); m_windows.emplace_back(window); } void QSGTextureGrabber::windowAfterRendering(QQuickWindow *window) { QMutexLocker lock(&m_mutex); if (!m_pendingTexture && m_textureId <= 0) { return; } #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) if (window->rendererInterface()->graphicsApi() != QSGRendererInterface::OpenGL) { return; } #else Q_UNUSED(window); #endif auto context = QOpenGLContext::currentContext(); // This check is only correct with the threaded render loop, for the basic one this // can fail with a non-shared context (if that is even a plausible scenario) // We can't detect this, so we rely on our safety checks in grabTexture and accept // a minimal chance of showing texture content from the wrong context. if (m_pendingTexture && QThread::currentThread() == m_pendingTexture->thread()) { if (m_pendingTexture->textureId() > 0) { const auto img = grabTexture(context, m_pendingTexture->textureId()); if (!img.isNull()) { emit textureGrabbed(m_pendingTexture, img); } } resetRequest(); } // See below, this is missing a context check here. So we rely purely on the // safety and plausibility checks in grabTexture. if (m_textureId > 0) { const auto img = grabTexture(context, m_textureId); if (!img.isNull()) { emit textureGrabbed(m_grabData, img); } resetRequest(); } window->resetOpenGLState(); } QImage QSGTextureGrabber::grabTexture(QOpenGLContext *context, int textureId) const { if (context->isOpenGLES()) { auto glFuncs = context->functions(); Q_ASSERT(glFuncs); // Reset the error flag, it can be a pending error that will conflict with our later check glFuncs->glGetError(); glFuncs->glBindTexture(GL_TEXTURE_2D, textureId); if (const auto err = glFuncs->glGetError()) { qWarning() << "Unable to bind texture for grabbing:" << err; return QImage(); } #if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0) // check if the size matches our expectations (requires ES3.1, so we might have to skip this auto glExtraFuncs = context->extraFunctions(); if (glExtraFuncs) { int w = 0, h = 0; glExtraFuncs->glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &w); glExtraFuncs->glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &h); if (m_textureSize.width() != w || m_textureSize.height() != h) { qWarning() << "OpenGL reported texture sizes doesn't match our assumption, aborting texture grab!" << m_textureSize << w << h; return QImage(); } } else { qDebug() << "Can't validate texture size (OpenGL ES < 3.1), things might go wrong in a multi-context scenario..."; } #endif // bind texture to an FBO, and read that, direct texture reading is not supported with OpenGL ES int prev_fbo = -1; unsigned int fbo = -1; glFuncs->glGetIntegerv(GL_FRAMEBUFFER_BINDING, &prev_fbo); glFuncs->glGenFramebuffers(1, &fbo); glFuncs->glBindFramebuffer(GL_FRAMEBUFFER, fbo); glFuncs->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureId, 0); QImage img(m_textureSize.width(), m_textureSize.height(), QImage::Format_RGBA8888_Premultiplied); glFuncs->glReadPixels(0, 0, m_textureSize.width(), m_textureSize.height(), GL_RGBA, GL_UNSIGNED_BYTE, img.bits()); glFuncs->glBindFramebuffer(GL_FRAMEBUFFER, prev_fbo); glFuncs->glDeleteFramebuffers(1, &fbo); return img; } else { #if !defined(QT_NO_OPENGL) && !defined(QT_OPENGL_ES_2) auto glFuncs = context->versionFunctions(); if (!glFuncs) { qWarning() << "unable to obtain OpenGL2 functions, too old GL version?"; return QImage(); } // Reset the error flag, it can be a pending error that will conflict with our later check glFuncs->glGetError(); glFuncs->glBindTexture(GL_TEXTURE_2D, textureId); if (const auto err = glFuncs->glGetError()) { qWarning() << "Unable to bind texture for grabbing:" << err; return QImage(); } // check if the size matches our expectations int w = 0, h = 0; glFuncs->glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &w); glFuncs->glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &h); if (m_textureSize.width() != w || m_textureSize.height() != h) { qWarning() << "OpenGL reported texture sizes doesn't match our assumption, aborting texture grab!" << m_textureSize << w << h; return QImage(); } // actually read the texture content QImage img(m_textureSize.width(), m_textureSize.height(), QImage::Format_ARGB32_Premultiplied); glFuncs->glGetTexImage(GL_TEXTURE_2D, 0, GL_BGRA, GL_UNSIGNED_BYTE, img.bits()); return img; #endif } return QImage(); } void QSGTextureGrabber::requestGrab(QSGTexture *tex) { QMutexLocker lock(&m_mutex); m_pendingTexture = tex; const int w = std::ceil(m_pendingTexture->textureSize().width() / m_pendingTexture->normalizedTextureSubRect().width()); const int h = std::ceil(m_pendingTexture->textureSize().height() / m_pendingTexture->normalizedTextureSubRect().height()); m_textureSize = QSize(w, h); triggerUpdate(); } void QSGTextureGrabber::requestGrab(int textureId, const QSize &texSize, void *data) { if (textureId < 0 || !texSize.isValid()) return; QMutexLocker lock(&m_mutex); // ### we are missing some context here to check if we are in the right GL context/render thread for grabbing // best idea so far: use the QQItem from the texture extension, making this unavailable from the QSG view though m_textureId = textureId; m_textureSize = texSize; m_grabData = data; triggerUpdate(); } void QSGTextureGrabber::triggerUpdate() { for (auto it = m_windows.begin(); it != m_windows.end();) { if (*it) { (*it)->update(); ++it; } else { it = m_windows.erase(it); } } } void QSGTextureGrabber::resetRequest() { m_pendingTexture = nullptr; m_textureId = -1; } gammaray-2.11.3/plugins/quickinspector/textureextension/qsgtexturegrabber.h000066400000000000000000000045741413310443000274710ustar00rootroot00000000000000/* qsgtexturegrabber.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_QSGTEXTUREGRABBER_H #define GAMMARAY_QSGTEXTUREGRABBER_H #include #include #include #include #include QT_BEGIN_NAMESPACE class QImage; class QOpenGLContext; class QQuickWindow; QT_END_NAMESPACE namespace GammaRay { class QSGTextureGrabber : public QObject { Q_OBJECT public: explicit QSGTextureGrabber(QObject *parent = nullptr); ~QSGTextureGrabber() override; static QSGTextureGrabber* instance(); public slots: void objectCreated(QObject *obj); void requestGrab(QSGTexture *tex); void requestGrab(int textureId, const QSize &texSize, void *data); signals: void textureGrabbed(QSGTexture *tex, const QImage &img); void textureGrabbed(void *data, const QImage &img); private: void addQuickWindow(QQuickWindow *window); void windowAfterRendering(QQuickWindow *window); QImage grabTexture(QOpenGLContext *context, int textureId) const; void triggerUpdate(); void resetRequest(); static QSGTextureGrabber *s_instance; QMutex m_mutex; QPointer m_pendingTexture; std::vector > m_windows; void *m_grabData; int m_textureId; QSize m_textureSize; }; } #endif // GAMMARAY_QSGTEXTUREGRABBER_H gammaray-2.11.3/plugins/quickinspector/textureextension/resources.qrc000066400000000000000000000002161413310443000262660ustar00rootroot00000000000000 resources/warning.png resources/picker.png gammaray-2.11.3/plugins/quickinspector/textureextension/resources/000077500000000000000000000000001413310443000255605ustar00rootroot00000000000000gammaray-2.11.3/plugins/quickinspector/textureextension/resources/picker.png000077500000000000000000000011421413310443000275440ustar00rootroot00000000000000PNG  IHDRasBIT|d pHYs+tEXtSoftwarewww.inkscape.org<IDAT8Ap8E;B~?4< B:`J0v;TmIbt,n ,(13lr*jRB`06"^^v],o]`,˗E9B`y`05 <Ey&ߚ =VfN>/ض}wVNzX,uh4R:D"κז_#bF@Ӵ#˲Nᣕ^T*G|hY ">'d2y?SUF9wFcl47gdYŮuݎx8NN&]Ƙ / 0Lmj8nZWM<^VѸ^(ޖJc!Dg-_"jx}DZR.BP(f9gnZ{tAD P$\w_bauIENDB`gammaray-2.11.3/plugins/quickinspector/textureextension/resources/warning.png000066400000000000000000000010551413310443000277340ustar00rootroot00000000000000PNG  IHDRasBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<IDAT8=Q}E YVl&"n0/!$)E"eA?LNZf`!S8^;!)+ @M&] $ߵ~GV#'-˲RӲZ)Fp]1櫈48IJ(8}?hl2B]p V\n*6=[[ Ȳ I|)(: R@պ@WbBm"r] |fyq+$ض}wUBz_$EkYk}ei8Ha$nk pi<-"6zّ(+)A,a{qHtNgY'"I]XT R]޳KeIENDB`gammaray-2.11.3/plugins/quickinspector/textureextension/textureextension.cpp000066400000000000000000000154251413310443000277160ustar00rootroot00000000000000/* textureextension.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "textureextension.h" #include "qsgtexturegrabber.h" #include #include #include #include #include #include #include #include #include #include #include #include using namespace GammaRay; TextureExtension::TextureExtension(PropertyController *controller) : QObject(controller) , PropertyControllerExtension(controller->objectBaseName() + ".texture") , m_currentTexture(nullptr) , m_currentMaterial(nullptr) , m_remoteView(new RemoteViewServer(controller->objectBaseName() + ".texture.remoteView", controller)) , m_connected(false) { } TextureExtension::~TextureExtension() = default; static QSGGeometryNode *findGeometryNode(QSGNode *node) { while(node) { if (node->type() == QSGNode::GeometryNodeType) return static_cast(node); if (node->childCount() == 0) return nullptr; if (node->childCount() > 1 && node->firstChild()->type() != QSGNode::GeometryNodeType) return nullptr; node = node->firstChild(); } return nullptr; } bool TextureExtension::setQObject(QObject* obj) { m_currentTexture = nullptr; m_currentMaterial = nullptr; if (!obj || !ensureSetup()) return false; if (auto qsgTexture = qobject_cast(obj)) { m_remoteView->resetView(); m_currentTexture = qsgTexture; m_remoteView->sourceChanged(); return true; } if (auto item = qobject_cast(obj)) { if (item->metaObject() == &QQuickItem::staticMetaObject) return false; auto priv = QQuickItemPrivate::get(item); if (!priv->itemNodeInstance) return false; auto geometryNode = findGeometryNode(priv->itemNodeInstance); if (geometryNode) return setObject(geometryNode, "QSGGeometryNode"); } // now it gets really dirty: to find the associated QSGLayer for a QQuickShaderEffectSource // we look at its incoming signal/slot connections, it's watching the layer that way... if (obj->inherits("QQuickShaderEffectSource")) { auto d = QObjectPrivate::get(obj); #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) QObjectPrivate::ConnectionData *cd = d->connections.load(); if (cd && cd->senders) { auto *senders = cd->senders; #else if (d->senders) { auto *senders = d->senders; #endif for (QObjectPrivate::Connection *s = senders; s; s = s->next) { if (!s->sender) continue; if (s->sender->inherits("QSGLayer")) return setQObject(s->sender); } } } return false; } bool TextureExtension::setObject(void* object, const QString& typeName) { m_currentTexture = nullptr; m_currentMaterial = nullptr; if (typeName == QLatin1String("QSGGeometryNode")) { auto node = static_cast(object); if (Util::isNullish(node->activeMaterial())) return false; auto material = node->activeMaterial(); if (auto mat = dynamic_cast(material)) return setQObject(mat->texture()); if (auto mat = dynamic_cast(material)) { if (!mat->texture() || mat->texture()->textureId <= 0) return false; m_remoteView->resetView(); m_currentMaterial = mat; m_remoteView->sourceChanged(); return true; } } return false; } void TextureExtension::textureGrabbed(QSGTexture* tex, const QImage& img) { if (tex != m_currentTexture || !m_remoteView->isActive()) return; RemoteViewFrame f; f.setImage(img); if (m_currentTexture && m_currentTexture->isAtlasTexture()) { QRect subRect(img.width() * m_currentTexture->normalizedTextureSubRect().x(), img.height() * m_currentTexture->normalizedTextureSubRect().y(), m_currentTexture->textureSize().width(), m_currentTexture->textureSize().height()); f.setData(subRect); } m_remoteView->sendFrame(f); } void TextureExtension::textureGrabbed(void* data, const QImage& img) { if (m_currentMaterial != data || !m_remoteView->isActive()) return; RemoteViewFrame f; f.setImage(img); m_remoteView->sendFrame(f); } void TextureExtension::triggerGrab() { if (m_currentTexture) QSGTextureGrabber::instance()->requestGrab(m_currentTexture); else if (m_currentMaterial) QSGTextureGrabber::instance()->requestGrab(m_currentMaterial->texture()->textureId, m_currentMaterial->texture()->size, m_currentMaterial); } bool GammaRay::TextureExtension::ensureSetup() { if (m_connected) return true; if (!QSGTextureGrabber::instance()) return false; connect(QSGTextureGrabber::instance(), static_cast(&QSGTextureGrabber::textureGrabbed), this, static_cast(&TextureExtension::textureGrabbed)); connect(QSGTextureGrabber::instance(), static_cast(&QSGTextureGrabber::textureGrabbed), this, static_cast(&TextureExtension::textureGrabbed)); connect(m_remoteView, &RemoteViewServer::requestUpdate, this, &TextureExtension::triggerGrab); return m_connected = true; } gammaray-2.11.3/plugins/quickinspector/textureextension/textureextension.h000066400000000000000000000041171413310443000273570ustar00rootroot00000000000000/* textureextension.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_TEXTUREEXTENSION_H #define GAMMARAY_TEXTUREEXTENSION_H #include #include QT_BEGIN_NAMESPACE class QImage; class QSGDistanceFieldTextMaterial; class QSGTexture; QT_END_NAMESPACE namespace GammaRay { class RemoteViewServer; class TextureExtension : public QObject, public PropertyControllerExtension { Q_OBJECT public: explicit TextureExtension(PropertyController *controller); ~TextureExtension() override; bool setQObject(QObject *obj) override; bool setObject(void * object, const QString & typeName) override; private slots: void textureGrabbed(QSGTexture *tex, const QImage &img); void textureGrabbed(void *data, const QImage &img); private: bool ensureSetup(); void triggerGrab(); QSGTexture *m_currentTexture; QSGDistanceFieldTextMaterial *m_currentMaterial; RemoteViewServer *m_remoteView; bool m_connected; }; } #endif // GAMMARAY_TEXTUREEXTENSION_H gammaray-2.11.3/plugins/quickinspector/textureextension/texturetab.cpp000066400000000000000000000127671413310443000264560ustar00rootroot00000000000000/* texturetab.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "texturetab.h" #include "ui_texturetab.h" #include #include #include #include using namespace GammaRay; QString formatBytes(qint64 bytes) { QVector sizes; sizes.push_back(TextureTab::tr(" GiB")); sizes.push_back(TextureTab::tr(" MiB")); sizes.push_back(TextureTab::tr(" KiB")); sizes.push_back(TextureTab::tr(" B")); static const qint64 startMultiplier = std::pow(1024.0f, sizes.size() - 1); qint64 multiplier = startMultiplier; for (int i = 0; i < sizes.size(); ++i, multiplier /= 1024) { if (bytes < multiplier) continue; else if (bytes % multiplier == 0) return QString::number(bytes / multiplier) + sizes.at(i); else return QString::number(bytes / static_cast(multiplier), 'f', 2) + sizes.at(i); } return TextureTab::tr("0 B"); } TextureTab::TextureTab(PropertyWidget *parent) : QWidget(parent) , ui(new Ui::TextureTab) { ui->setupUi(this); ui->textureView->setName(parent->objectBaseName() + QStringLiteral(".texture.remoteView")); auto toolbar = new QToolBar; toolbar->setIconSize(QSize(16, 16)); toolbar->setToolButtonStyle(Qt::ToolButtonIconOnly); toolbar->layout()->setContentsMargins(9,9,9,9); toolbar->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Minimum); ui->layout->setMenuBar(toolbar); foreach (auto action, ui->textureView->interactionModeActions()->actions()) toolbar->addAction(action); toolbar->addSeparator(); toolbar->addAction(ui->textureView->zoomOutAction()); auto zoom = new QComboBox; zoom->setModel(ui->textureView->zoomLevelModel()); toolbar->addWidget(zoom); toolbar->addAction(ui->textureView->zoomInAction()); toolbar->addSeparator(); const auto warningImage = QIcon(":/resources/warning.png"); auto toggleTextureWasteAction = new QAction(warningImage, tr("Visualize Texture Problems"), nullptr); toggleTextureWasteAction->setObjectName("aVisualizeTextureProblems"); toggleTextureWasteAction->setCheckable(true); toggleTextureWasteAction->setChecked(true); toolbar->addAction(toggleTextureWasteAction); ui->textureView->setSupportedInteractionModes(RemoteViewWidget::ViewInteraction | RemoteViewWidget::Measuring | RemoteViewWidget::ColorPicking); connect(zoom, static_cast(&QComboBox::currentIndexChanged), ui->textureView, &RemoteViewWidget::setZoomLevel); connect(ui->textureView, &RemoteViewWidget::zoomLevelChanged, zoom, &QComboBox::setCurrentIndex); connect(toggleTextureWasteAction, &QAction::toggled, ui->textureView, &TextureViewWidget::setTextureWasteVisualizationEnabled); // Texture issues infobar connect(ui->textureView, &TextureViewWidget::textureInfoNecessary, this, [&](bool infoNecessary) { ui->textureInfo->setVisible(infoNecessary); if (!infoNecessary) ui->infoLabel->setText(QString()); }); connect(ui->textureView, &TextureViewWidget::textureWasteFound, this, [&](bool isProblem, int percent, int bytes) { addInfoLine(isProblem, tr("Transparency waste: %1% / %2.").arg(QString::number(percent), formatBytes(bytes))); }); connect(ui->textureView, &TextureViewWidget::textureIsUnicolor, this, [&](bool isProblem) { addInfoLine(isProblem, tr("Texture has only one color, consider using a widget or a rectangle.")); }); connect(ui->textureView, &TextureViewWidget::textureIsFullyTransparent, this, [&](bool isProblem) { addInfoLine(isProblem, tr("Texture is fully transparent, consider using margins or anchoring.")); }); connect(ui->textureView, &TextureViewWidget::textureHasBorderImageSavings, this, [&](bool isProblem, int percent, int bytes) { addInfoLine(isProblem, tr("Using a BorderImage for this texture would save %1% / %2.") .arg(QString::number(percent), formatBytes(bytes))); }); zoom->setCurrentIndex(ui->textureView->zoomLevelIndex()); } void TextureTab::addInfoLine(bool isProblem, const QString& newLine) { if (!isProblem) return; auto text = ui->infoLabel->text(); if (!text.isEmpty()) text = text + QStringLiteral("
    "); ui->infoLabel->setText(text + newLine); } TextureTab::~TextureTab() = default; gammaray-2.11.3/plugins/quickinspector/textureextension/texturetab.h000066400000000000000000000031011413310443000261010ustar00rootroot00000000000000/* texturetab.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_TEXTURETAB_H #define GAMMARAY_TEXTURETAB_H #include #include namespace GammaRay { class PropertyWidget; namespace Ui { class TextureTab; } class TextureTab : public QWidget { Q_OBJECT public: explicit TextureTab(PropertyWidget *parent); ~TextureTab() override; private: void addInfoLine(bool isProblem, const QString &newLine); std::unique_ptr ui; }; } #endif // GAMMARAY_TEXTURETAB_H gammaray-2.11.3/plugins/quickinspector/textureextension/texturetab.ui000066400000000000000000000045131413310443000262770ustar00rootroot00000000000000 GammaRay::TextureTab 0 0 1172 300 0 0 0 6 0 0 <b>Identified Problems:</b> Qt::RichText Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop Qt::RichText Qt::Horizontal 40 20 GammaRay::TextureViewWidget QWidget
    textureextension/textureviewwidget.h
    1
    gammaray-2.11.3/plugins/quickinspector/textureextension/textureviewwidget.cpp000066400000000000000000000247301413310443000300570ustar00rootroot00000000000000/* textureviewwidget.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "textureviewwidget.h" #include #include #include #include using namespace GammaRay; TextureViewWidget::TextureViewWidget(QWidget *parent) : RemoteViewWidget(parent) , m_visualizeTextureProblems(true) , m_pixelWasteInPercent(0) , m_pixelWasteInBytes(0) , m_horizontalBorderImageSavingsInPercent(0) , m_verticalBorderImageSavings(0) { connect(this, &RemoteViewWidget::frameChanged, this, &TextureViewWidget::analyzeImageFlaws); } TextureViewWidget::~TextureViewWidget() = default; void TextureViewWidget::drawPixelWasteDecoration(QPainter *p) const { //Draw Warning if more than 30% or 1KB are wasted const auto hasTextureWasteProblem = (m_pixelWasteInPercent > transparencyWasteLimitInPercent || m_pixelWasteInBytes > transparencyWasteLimitInBytes); if (!hasTextureWasteProblem) return; p->save(); auto scaleTransform = QTransform::fromScale(zoom(), zoom()); p->setTransform(scaleTransform, true); //Draw Wasted Area QPen pen(Qt::red); pen.setCosmetic(true); p->setPen(pen); QBrush brush = QBrush(Qt::red, Qt::FDiagPattern); brush.setTransform(scaleTransform.inverted()); p->setBrush(brush); auto outerRect = m_analyzedRect; auto wasteArea = QPainterPath(); wasteArea.addRect(outerRect); auto innerRect = QPainterPath(); auto translatedBoundingRect = m_opaqueBoundingRect.translated(m_analyzedRect.topLeft()); innerRect.addRect(translatedBoundingRect); wasteArea = wasteArea.subtracted(innerRect); p->drawPath(wasteArea); p->restore(); } void TextureViewWidget::drawBorderImageCutouts(QPainter *p) const { p->save(); auto scaleTransform = QTransform::fromScale(zoom(), zoom()); p->setTransform(scaleTransform, true); QPen pen(Qt::white); pen.setCosmetic(true); p->setPen(pen); QBrush brush = QBrush(Qt::white, Qt::FDiagPattern); brush.setTransform(scaleTransform.inverted()); p->setBrush(brush); if (m_horizontalBorderImageSavingsInPercent > minimumBorderImageSavingsPercent) { auto horizontalCutout = m_horizontalBorderRectMidCut.translated(m_analyzedRect.topLeft()); p->drawRect(horizontalCutout); } if (m_verticalBorderImageSavings > minimumBorderImageSavingsPercent) { auto verticalCutout = m_verticalBorderRectMidCut.translated(m_analyzedRect.topLeft()); p->drawRect(verticalCutout); } p->restore(); } void TextureViewWidget::drawActiveAtlasTile(QPainter *p) const { auto rect = frame().data().toRect(); if (!rect.isValid()) return; rect = rect.adjusted(-1, -1, 1, 1); p->save(); p->setTransform(QTransform().scale(zoom(), zoom()), true); QPen pen(Qt::red); pen.setCosmetic(true); p->setPen(pen); p->drawRect(rect); p->restore(); } void TextureViewWidget::drawDecoration(QPainter *p) { if (m_visualizeTextureProblems){ drawBorderImageCutouts(p); drawPixelWasteDecoration(p); } drawActiveAtlasTile(p); } void TextureViewWidget::setTextureWasteVisualizationEnabled(bool enabled) { if (m_visualizeTextureProblems != enabled) { m_visualizeTextureProblems = enabled; update(); } } void TextureViewWidget::analyzeImageFlaws() { emit textureInfoNecessary(false); if (frame().image().isNull()) return; // For AtlasTiles analyze subrect, else analyze the whole image QImage analyzedTexture; QRect analyzedRect; int atlasTextureOffset = 0; // atlas textures are 1 pixel bigger auto atlasSubTile = frame().data().toRect(); if (atlasSubTile.isValid()) { //Atlas-Case analyzedTexture = frame().image().copy(atlasSubTile); analyzedRect = atlasSubTile; analyzedRect = analyzedRect.adjusted(-1, -1, 1, 1); atlasTextureOffset = 1; } else { // Whole-Texture-Case analyzedTexture = frame().image(); analyzedRect = frame().image().rect(); atlasTextureOffset = 0; } m_analyzedRect = analyzedRect; QRgb possibleSingularColor = analyzedTexture.pixel(0, 0); ImageFlags imageFlags = FullyTransparent | Unicolor; int top = analyzedTexture.height(), bottom = 0, left = analyzedTexture.width(), right = 0; for (int y = 0; y < analyzedTexture.height(); y++) { for (int x = 0; x < analyzedTexture.width(); x++) { auto pixel = analyzedTexture.pixel(x, y); if (Q_UNLIKELY(imageFlags.testFlag(Unicolor)) && (possibleSingularColor != pixel)) imageFlags &=~ Unicolor; if (qAlpha(pixel) != 0) { imageFlags &=~ FullyTransparent; top = std::min(top, y); bottom = std::max(bottom, y); left = std::min(left, x); right = std::max(right, x); } } } m_opaqueBoundingRect = QRect(QPoint(left, top), QPoint(right, bottom)).translated(QPoint(atlasTextureOffset, atlasTextureOffset)); // Calculate Waste const float imagePixelSize = (analyzedTexture.width() * analyzedTexture.height()); const auto pixelWaste = 1.0 - ((m_opaqueBoundingRect.height() * m_opaqueBoundingRect.width()) / imagePixelSize); m_pixelWasteInPercent = qRound(pixelWaste * 100.0f); m_pixelWasteInBytes = (imagePixelSize - (m_opaqueBoundingRect.height() * m_opaqueBoundingRect.width())) * frame().image().depth() / 8; // Emit all possible Problems so far auto hasTextureWasteProblem = (m_pixelWasteInPercent > transparencyWasteLimitInPercent || m_pixelWasteInBytes > transparencyWasteLimitInBytes); emit textureWasteFound(hasTextureWasteProblem, m_pixelWasteInPercent, m_pixelWasteInBytes); if (hasTextureWasteProblem) imageFlags |= TextureWaste; emit textureIsUnicolor(imageFlags.testFlag(Unicolor)); emit textureIsFullyTransparent(imageFlags.testFlag(FullyTransparent)); // Border Image checks // horizontal mid cut auto textureWidth = analyzedTexture.width(); auto textureHeight = analyzedTexture.height(); auto midCol = textureWidth / 2; auto breakout = false; int leftCol; for (leftCol = midCol; (leftCol >= 0) && !breakout; leftCol--) { for (int row = 0; row < textureHeight - 1; row++) { if (analyzedTexture.pixel(leftCol, row) != analyzedTexture.pixel(midCol, row)) { leftCol += 2; breakout = true; break; } } } breakout = false; int rightCol; for (rightCol = midCol; (rightCol < textureWidth) && !breakout; rightCol++) { for (int row = 0; row < textureHeight; row++) { if (analyzedTexture.pixel(rightCol, row) != analyzedTexture.pixel(midCol, row)) { rightCol -= 2; breakout = true; break; } } } m_horizontalBorderImageSavingsInPercent = qRound(((rightCol - leftCol + 1) * textureHeight) / imagePixelSize * 100); m_horizontalBorderRectMidCut = QRect(leftCol + atlasTextureOffset, 0, rightCol - leftCol + 1, analyzedRect.height()); // vertical mid cut auto midRow = textureHeight / 2; int upperRow; breakout = false; for (upperRow = midRow; (upperRow >= 0) && !breakout; upperRow--) { for (int col = 0; col < textureWidth; col++) { if (analyzedTexture.pixel(col, upperRow) != analyzedTexture.pixel(col, midRow)) { upperRow += 2; breakout = true; break; } } } breakout = false; int lowerRow; for (lowerRow = midRow; (lowerRow < textureHeight - 1) && !breakout; lowerRow++) { for (int col = 0; col < textureWidth; col++) { if (analyzedTexture.pixel(col, lowerRow) != analyzedTexture.pixel(col, midRow)) { lowerRow -= 2; breakout = true; break; } } } m_verticalBorderImageSavings = qRound(((lowerRow - upperRow + 1) * textureWidth) / imagePixelSize * 100); m_verticalBorderRectMidCut = QRect(0, upperRow + atlasTextureOffset, analyzedRect.width(), lowerRow - upperRow + 1); auto overallSavingsInPercent = 0; auto area = [](const QRect &r){ return r.width() * r.height(); }; const auto hs = (m_horizontalBorderImageSavingsInPercent > minimumBorderImageSavingsPercent); const auto vs = (m_verticalBorderImageSavings > minimumBorderImageSavingsPercent); if (!hs && !vs) overallSavingsInPercent = 0; if ( hs && !vs) overallSavingsInPercent = m_horizontalBorderImageSavingsInPercent; if (!hs && vs) overallSavingsInPercent = m_verticalBorderImageSavings; if ( hs && vs) { const auto overlapRect = m_horizontalBorderRectMidCut.intersected(m_verticalBorderRectMidCut); overallSavingsInPercent = area(m_horizontalBorderRectMidCut) + area(m_verticalBorderRectMidCut) - area(overlapRect); overallSavingsInPercent = qRound(overallSavingsInPercent / ((float) area(m_analyzedRect)) * 100); } if (overallSavingsInPercent > minimumBorderImageSavingsPercent) imageFlags |= BorderImageCandidate; auto overallSavingsInBytes = overallSavingsInPercent / 100.0f * area(m_analyzedRect) * frame().image().depth() / 8; emit textureHasBorderImageSavings((overallSavingsInPercent > minimumBorderImageSavingsPercent), overallSavingsInPercent, overallSavingsInBytes); // Show or hide the infobar depending on found issues emit textureInfoNecessary(imageFlags != None); } gammaray-2.11.3/plugins/quickinspector/textureextension/textureviewwidget.h000066400000000000000000000056001413310443000275170ustar00rootroot00000000000000/* textureviewwidget.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_TEXTUREVIEWWIDGET_H #define GAMMARAY_TEXTUREVIEWWIDGET_H #include namespace GammaRay { class TextureViewWidget : public RemoteViewWidget { Q_OBJECT public: explicit TextureViewWidget(QWidget *parent = nullptr); ~TextureViewWidget() override; enum ImageFlag { None = 0, FullyTransparent = 1, Unicolor = 2, TextureWaste = 4, BorderImageCandidate = 8 }; Q_DECLARE_FLAGS(ImageFlags, ImageFlag) void drawDecoration(QPainter *p) override; void drawPixelWasteDecoration(QPainter *p) const; void drawBorderImageCutouts(QPainter *p) const; void drawActiveAtlasTile(QPainter *p) const; const static int transparencyWasteLimitInPercent = 30; const static int transparencyWasteLimitInBytes = 16 * 1024; const static int minimumBorderImageSavingsPercent = 25; signals: void textureInfoNecessary(bool isNecessary); void textureWasteFound(bool, int percent, int bytes); void textureIsUnicolor(bool uniColor); void textureIsFullyTransparent(bool fullyTransparent); void textureHasBorderImageSavings(bool isBorderImage, int percent, int bytes); public slots: void setTextureWasteVisualizationEnabled(bool enabled); private slots: void analyzeImageFlaws(); private: bool m_visualizeTextureProblems; int m_pixelWasteInPercent; int m_pixelWasteInBytes; QRect m_analyzedRect; QRect m_opaqueBoundingRect; //area actually occupied by opaque pixels int m_horizontalBorderImageSavingsInPercent; QRect m_horizontalBorderRectMidCut; int m_verticalBorderImageSavings; QRect m_verticalBorderRectMidCut; }; } Q_DECLARE_OPERATORS_FOR_FLAGS(GammaRay::TextureViewWidget::ImageFlags) #endif // GAMMARAY_TEXTUREVIEWWIDGET_H gammaray-2.11.3/plugins/quickwidgetsupport/000077500000000000000000000000001413310443000210235ustar00rootroot00000000000000gammaray-2.11.3/plugins/quickwidgetsupport/CMakeLists.txt000066400000000000000000000005211413310443000235610ustar00rootroot00000000000000# probe plugin if (NOT GAMMARAY_CLIENT_ONLY_BUILD) set(gammaray_quickwidgetsupport_srcs quickwidgetsupport.cpp ) gammaray_add_plugin(gammaray_quickwidgetsupport JSON gammaray_quickwidgetsupport.json SOURCES ${gammaray_quickwidgetsupport_srcs}) target_link_libraries(gammaray_quickwidgetsupport gammaray_core Qt5::QuickWidgets) endif() gammaray-2.11.3/plugins/quickwidgetsupport/gammaray_quickwidgetsupport.json000066400000000000000000000002061413310443000275470ustar00rootroot00000000000000{ "id": "gammaray_quickwidgetsupport", "name": "Quick Widgets Support", "types": [ "QQuickWidget" ], "hidden": true } gammaray-2.11.3/plugins/quickwidgetsupport/quickwidgetsupport.cpp000066400000000000000000000046621413310443000255140ustar00rootroot00000000000000/* quickwidgetsupport.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "quickwidgetsupport.h" #include #include #include #include #include #include #include using namespace GammaRay; static QuickWidgetSupport *s_quickWidgetSupportInstance = nullptr; QuickWidgetSupport::QuickWidgetSupport(Probe *probe, QObject *parent) : QObject(parent) , m_probe(probe) { Q_ASSERT(s_quickWidgetSupportInstance == nullptr); s_quickWidgetSupportInstance = this; connect(probe, &Probe::objectCreated, this, &QuickWidgetSupport::objectAdded); MetaObject *mo = nullptr; MO_ADD_METAOBJECT1(QQuickWidget, QWidget); MO_ADD_PROPERTY_RO(QQuickWidget, engine); MO_ADD_PROPERTY_RO(QQuickWidget, format); MO_ADD_PROPERTY_RO(QQuickWidget, initialSize); MO_ADD_PROPERTY_RO(QQuickWidget, quickWindow); MO_ADD_PROPERTY_RO(QQuickWidget, rootContext); MO_ADD_PROPERTY_RO(QQuickWidget, rootObject); } GammaRay::QuickWidgetSupport::~QuickWidgetSupport() { s_quickWidgetSupportInstance = nullptr; } void GammaRay::QuickWidgetSupport::objectAdded(QObject *obj) { auto qqw = qobject_cast(obj); if (!qqw) return; if (m_probe->needsObjectDiscovery()) m_probe->discoverObject(qqw->quickWindow()); } gammaray-2.11.3/plugins/quickwidgetsupport/quickwidgetsupport.h000066400000000000000000000037061413310443000251570ustar00rootroot00000000000000/* quickwidgetsupport.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_QUICKWIDGETSUPPRT_QUICKWIDGETSUPPORT_H #define GAMMARAY_QUICKWIDGETSUPPRT_QUICKWIDGETSUPPORT_H #include #include namespace GammaRay { class QuickWidgetSupport : public QObject { Q_OBJECT public: explicit QuickWidgetSupport(Probe *probe, QObject *parent = nullptr); ~QuickWidgetSupport() override; private slots: void objectAdded(QObject *obj); private: Probe *m_probe; }; class QuickWidgetSupportFactory : public QObject, public StandardToolFactory { Q_OBJECT Q_INTERFACES(GammaRay::ToolFactory) Q_PLUGIN_METADATA(IID "com.kdab.GammaRay.ToolFactory" FILE "gammaray_quickwidgetsupport.json") public: explicit QuickWidgetSupportFactory(QObject *parent = nullptr) : QObject(parent) { } }; } #endif gammaray-2.11.3/plugins/sceneinspector/000077500000000000000000000000001413310443000200725ustar00rootroot00000000000000gammaray-2.11.3/plugins/sceneinspector/CMakeLists.txt000066400000000000000000000016211413310443000226320ustar00rootroot00000000000000# probe part if (NOT GAMMARAY_CLIENT_ONLY_BUILD) set(gammaray_sceneinspector_plugin_srcs sceneinspector.cpp scenemodel.cpp sceneinspectorinterface.cpp paintanalyzerextension.cpp ) gammaray_add_plugin(gammaray_sceneinspector_plugin JSON gammaray_sceneinspector.json SOURCES ${gammaray_sceneinspector_plugin_srcs} ) target_link_libraries(gammaray_sceneinspector_plugin Qt5::Gui Qt5::Widgets gammaray_kitemmodels gammaray_core ) endif() # ui part if(GAMMARAY_BUILD_UI) set(gammaray_sceneinspector_ui_plugin_srcs sceneinspectorwidget.cpp graphicsview.cpp graphicssceneview.cpp sceneinspectorinterface.cpp sceneinspectorclient.cpp ) gammaray_add_plugin(gammaray_sceneinspector_ui_plugin JSON gammaray_sceneinspector.json SOURCES ${gammaray_sceneinspector_ui_plugin_srcs} ) target_link_libraries(gammaray_sceneinspector_ui_plugin gammaray_ui ) endif() gammaray-2.11.3/plugins/sceneinspector/gammaray_sceneinspector.json000066400000000000000000000002511413310443000256650ustar00rootroot00000000000000{ "id": "gammaray_sceneinspector", "name": "Graphics Scenes", "types": [ "QGraphicsScene" ], "selectableTypes": [ "QGraphicsObject", "QGraphicsItem" ] } gammaray-2.11.3/plugins/sceneinspector/graphicssceneview.cpp000066400000000000000000000052551413310443000243160ustar00rootroot00000000000000/* graphicssceneview.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "graphicssceneview.h" #include "ui_graphicssceneview.h" using namespace GammaRay; GraphicsSceneView::GraphicsSceneView(QWidget *parent) : QWidget(parent) , ui(new Ui::GraphicsSceneView) { ui->setupUi(this); QFontMetrics fm(ui->sceneCoordLabel->font()); ui->sceneCoordLabel->setFixedWidth(fm.width(QStringLiteral("00000.00 x 00000.00"))); ui->itemCoordLabel->setFixedWidth(fm.width(QStringLiteral("00000.00 x 00000.00"))); connect(ui->graphicsView, &GraphicsView::sceneCoordinatesChanged, this, &GraphicsSceneView::sceneCoordinatesChanged); connect(ui->graphicsView, &GraphicsView::itemCoordinatesChanged, this, &GraphicsSceneView::itemCoordinatesChanged); } GraphicsSceneView::~GraphicsSceneView() { delete ui; } GraphicsView *GraphicsSceneView::view() const { return ui->graphicsView; } void GraphicsSceneView::showGraphicsItem(QGraphicsItem *item) { ui->graphicsView->showItem(item); } void GraphicsSceneView::setGraphicsScene(QGraphicsScene *scene) { ui->graphicsView->setScene(scene); } void GraphicsSceneView::sceneCoordinatesChanged(const QPointF &coord) { ui->sceneCoordLabel->setText(QStringLiteral("%1 x %2"). arg(coord.x(), 0, 'f', 2). arg(coord.y(), 0, 'f', 2)); } void GraphicsSceneView::itemCoordinatesChanged(const QPointF &coord) { ui->itemCoordLabel->setText(QStringLiteral("%1 x %2"). arg(coord.x(), 0, 'f', 2). arg(coord.y(), 0, 'f', 2)); } gammaray-2.11.3/plugins/sceneinspector/graphicssceneview.h000066400000000000000000000036201413310443000237550ustar00rootroot00000000000000/* graphicssceneview.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_SCENEINSPECTOR_GRAPHICSSCENEVIEW_H #define GAMMARAY_SCENEINSPECTOR_GRAPHICSSCENEVIEW_H #include QT_BEGIN_NAMESPACE class QGraphicsScene; class QGraphicsItem; QT_END_NAMESPACE namespace GammaRay { class GraphicsView; namespace Ui { class GraphicsSceneView; } class GraphicsSceneView : public QWidget { Q_OBJECT public: explicit GraphicsSceneView(QWidget *parent = nullptr); ~GraphicsSceneView() override; GraphicsView *view() const; void showGraphicsItem(QGraphicsItem *item); void setGraphicsScene(QGraphicsScene *scene); private slots: void sceneCoordinatesChanged(const QPointF &coord); void itemCoordinatesChanged(const QPointF &coord); private: Ui::GraphicsSceneView *ui; }; } #endif // GAMMARAY_GRAPHICSSCENEVIEW_H gammaray-2.11.3/plugins/sceneinspector/graphicssceneview.ui000066400000000000000000000032751413310443000241510ustar00rootroot00000000000000 GammaRay::GraphicsSceneView 0 0 400 300 0 Scene Coordinates: 0x0 Item Coordinates: 0x0 false QGraphicsView::ScrollHandDrag GammaRay::GraphicsView QGraphicsView
    graphicsview.h
    gammaray-2.11.3/plugins/sceneinspector/graphicsview.cpp000066400000000000000000000055561413310443000233040ustar00rootroot00000000000000/* graphicsview.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "graphicsview.h" #include "sceneinspectorinterface.h" #include #include using namespace GammaRay; GraphicsView::GraphicsView(QWidget *parent) : QGraphicsView(parent) , m_currentItem(nullptr) { setMouseTracking(true); } void GraphicsView::showItem(QGraphicsItem *item) { m_currentItem = item; if (!item) return; fitInView(item, Qt::KeepAspectRatio); scale(0.8f, 0.8f); emit transformChanged(); } void GraphicsView::keyPressEvent(QKeyEvent *event) { if (event->modifiers() == Qt::CTRL) { switch (event->key()) { case Qt::Key_Plus: scale(1.2, 1.2); emit transformChanged(); event->accept(); return; case Qt::Key_Minus: scale(0.8, 0.8); emit transformChanged(); event->accept(); return; case Qt::Key_Left: rotate(-5); emit transformChanged(); event->accept(); break; case Qt::Key_Right: rotate(5); emit transformChanged(); event->accept(); break; } } QGraphicsView::keyPressEvent(event); } void GraphicsView::mouseMoveEvent(QMouseEvent *event) { emit sceneCoordinatesChanged(mapToScene(event->pos())); if (m_currentItem) emit itemCoordinatesChanged(m_currentItem->mapFromScene(mapToScene(event->pos()))); QGraphicsView::mouseMoveEvent(event); } void GraphicsView::drawForeground(QPainter *painter, const QRectF &rect) { QGraphicsView::drawForeground(painter, rect); if (m_currentItem) SceneInspectorInterface::paintItemDecoration(m_currentItem, transform(), painter); } gammaray-2.11.3/plugins/sceneinspector/graphicsview.h000066400000000000000000000036001413310443000227350ustar00rootroot00000000000000/* graphicsview.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_SCENEINSPECTOR_GRAPHICSVIEW_H #define GAMMARAY_SCENEINSPECTOR_GRAPHICSVIEW_H #include namespace GammaRay { class GraphicsView : public QGraphicsView { Q_OBJECT public: explicit GraphicsView(QWidget *parent = nullptr); void showItem(QGraphicsItem *item); signals: void sceneCoordinatesChanged(const QPointF &sceneCoord); void itemCoordinatesChanged(const QPointF &itemCoord); void transformChanged(); protected: void keyPressEvent(QKeyEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override; void drawForeground(QPainter *painter, const QRectF &rect) override; private: QGraphicsItem *m_currentItem; }; } #endif // GAMMARAY_GRAPHICSVIEW_H gammaray-2.11.3/plugins/sceneinspector/paintanalyzerextension.cpp000066400000000000000000000074341413310443000254240ustar00rootroot00000000000000/* paintanalyzerextension.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "paintanalyzerextension.h" #include #include #include #include #include #include #include #include #include #include using namespace GammaRay; PaintAnalyzerExtension::PaintAnalyzerExtension(PropertyController *controller) : PropertyControllerExtension(controller->objectBaseName() + ".painting") , m_paintAnalyzer(nullptr) { // check if the paint analyzer already exists before creating it, // as we share the UI with the other plugins. const QString aName = controller->objectBaseName() + QStringLiteral(".painting.analyzer"); if (ObjectBroker::hasObject(aName)) { m_paintAnalyzer = qobject_cast(ObjectBroker::object(aName)); } else { m_paintAnalyzer = new PaintAnalyzer(aName, controller); } } PaintAnalyzerExtension::~PaintAnalyzerExtension() = default; bool PaintAnalyzerExtension::setQObject(QObject *object) { if (!PaintAnalyzer::isAvailable()) return false; if (auto qgvObj = qobject_cast(object)) return analyzePainting(qgvObj); return false; } bool PaintAnalyzerExtension::setObject(void *object, const QString &typeName) { if (!PaintAnalyzer::isAvailable()) return false; const auto mo = MetaObjectRepository::instance()->metaObject(typeName); if (!mo) return false; if (const auto item = mo->castTo(object, QStringLiteral("QGraphicsItem"))) return analyzePainting(static_cast(item)); return false; } bool PaintAnalyzerExtension::analyzePainting(QGraphicsItem *item) { if (item->flags() & QGraphicsItem::ItemHasNoContents) return false; m_paintAnalyzer->beginAnalyzePainting(); m_paintAnalyzer->setBoundingRect(item->boundingRect()); QStyleOptionGraphicsItem option; option.state = QStyle::State_None; option.rect = item->boundingRect().toRect(); option.levelOfDetail = 1; option.exposedRect = item->boundingRect(); option.styleObject = item->toGraphicsObject(); if (!option.styleObject) option.styleObject = item->scene(); if (item->isSelected()) option.state |= QStyle::State_Selected; if (item->isEnabled()) option.state |= QStyle::State_Enabled; if (item->hasFocus()) option.state |= QStyle::State_HasFocus; { QPainter p(m_paintAnalyzer->paintDevice()); item->paint(&p, &option); } m_paintAnalyzer->endAnalyzePainting(); return true; } gammaray-2.11.3/plugins/sceneinspector/paintanalyzerextension.h000066400000000000000000000034621413310443000250660ustar00rootroot00000000000000/* paintanalyzerextension.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_PAINTANALYZEREXTENSION_H #define GAMMARAY_PAINTANALYZEREXTENSION_H #include QT_BEGIN_NAMESPACE class QGraphicsItem; QT_END_NAMESPACE namespace GammaRay { class PaintAnalyzer; class PropertyController; class PaintAnalyzerExtension : public PropertyControllerExtension { public: explicit PaintAnalyzerExtension(PropertyController *controller); ~PaintAnalyzerExtension(); bool setQObject(QObject *object) override; bool setObject(void *object, const QString &typeName) override; private: bool analyzePainting(QGraphicsItem *item); PaintAnalyzer *m_paintAnalyzer; }; } #endif // GAMMARAY_PAINTANALYZEREXTENSION_H gammaray-2.11.3/plugins/sceneinspector/sceneinspector.cpp000066400000000000000000000446501413310443000236330ustar00rootroot00000000000000/* sceneinspector.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "sceneinspector.h" #include "scenemodel.h" #include "paintanalyzerextension.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace GammaRay; using namespace std; Q_DECLARE_METATYPE(QGraphicsEffect *) Q_DECLARE_METATYPE(QGraphicsItemGroup *) Q_DECLARE_METATYPE(QGraphicsLayoutItem *) Q_DECLARE_METATYPE(QGraphicsLayout *) Q_DECLARE_METATYPE(QGraphicsObject *) Q_DECLARE_METATYPE(QGraphicsWidget *) Q_DECLARE_METATYPE(QGraphicsItem::CacheMode) Q_DECLARE_METATYPE(QGraphicsItem::GraphicsItemFlags) Q_DECLARE_METATYPE(QGraphicsItem::PanelModality) Q_DECLARE_METATYPE(QGraphicsPixmapItem::ShapeMode) SceneInspector::SceneInspector(Probe *probe, QObject *parent) : SceneInspectorInterface(parent) , m_propertyController(new PropertyController(QStringLiteral("com.kdab.GammaRay.SceneInspector"), this)) , m_clientConnected(false) { Server::instance()->registerMonitorNotifier(Endpoint::instance()->objectAddress( objectName()), this, "clientConnectedChanged"); PropertyController::registerExtension(); registerGraphicsViewMetaTypes(); registerVariantHandlers(); connect(probe, &Probe::objectSelected, this, &SceneInspector::qObjectSelected); connect(probe, &Probe::nonQObjectSelected, this, &SceneInspector::nonQObjectSelected); auto *sceneFilterProxy = new ObjectTypeFilterProxyModel(this); sceneFilterProxy->setSourceModel(probe->objectListModel()); auto *singleColumnProxy = new SingleColumnObjectProxyModel(this); singleColumnProxy->setSourceModel(sceneFilterProxy); probe->registerModel(QStringLiteral("com.kdab.GammaRay.SceneList"), singleColumnProxy); QItemSelectionModel *sceneSelection = ObjectBroker::selectionModel(singleColumnProxy); connect(sceneSelection, &QItemSelectionModel::selectionChanged, this, &SceneInspector::sceneSelected); m_sceneModel = new SceneModel(this); auto sceneProxy = new ServerProxyModel(this); sceneProxy->setSourceModel(m_sceneModel); sceneProxy->addRole(ObjectModel::ObjectIdRole); probe->registerModel(QStringLiteral("com.kdab.GammaRay.SceneGraphModel"), sceneProxy); m_itemSelectionModel = ObjectBroker::selectionModel(sceneProxy); connect(m_itemSelectionModel, &QItemSelectionModel::selectionChanged, this, &SceneInspector::sceneItemSelectionChanged); } void SceneInspector::sceneSelected(const QItemSelection &selection) { if (selection.isEmpty()) return; const QModelIndex index = selection.first().topLeft(); QObject *obj = index.data(ObjectModel::ObjectRole).value(); QGraphicsScene *scene = qobject_cast(obj); if (m_sceneModel->scene()) disconnect(m_sceneModel->scene(), nullptr, this, nullptr); m_sceneModel->setScene(scene); connectToScene(); // TODO remote support when a different graphics scene was selected // ui->graphicsSceneView->setGraphicsScene(scene); } void SceneInspector::connectToScene() { QGraphicsScene *scene = m_sceneModel->scene(); if (!scene || !m_clientConnected) return; connect(scene, &QGraphicsScene::sceneRectChanged, this, &SceneInspectorInterface::sceneRectChanged); connect(scene, &QGraphicsScene::changed, this, &SceneInspectorInterface::sceneChanged); initializeGui(); } void SceneInspector::initializeGui() { if (!Endpoint::isConnected()) { // only do something if we are connected to a remote client return; } QGraphicsScene *scene = m_sceneModel->scene(); if (!scene) return; emit sceneRectChanged(scene->sceneRect()); } void SceneInspector::clientConnectedChanged(bool clientConnected) { m_clientConnected = clientConnected; connectToScene(); } void SceneInspector::renderScene(const QTransform &transform, const QSize &size) { if (!Endpoint::isConnected()) { // only do something if we are connected to a remote client return; } QGraphicsScene *scene = m_sceneModel->scene(); if (!scene) return; // initialize transparent pixmap QPixmap view(size); view.fill(Qt::transparent); // setup painter and apply transformation of client view QPainter painter(&view); painter.setWorldTransform(transform); // the area we want to paint has the size of the client's viewport _after_ applying // the transformation. Thus first apply the inverse to yield the desired area afterwards QRectF area(QPointF(0, 0), size); area = transform.inverted().mapRect(area); scene->render(&painter, area, area, Qt::IgnoreAspectRatio); QGraphicsItem *currentItem = m_itemSelectionModel->currentIndex().data(SceneModel::SceneItemRole).value(); if (currentItem) paintItemDecoration(currentItem, transform, &painter); emit sceneRendered(view); } void SceneInspector::sceneItemSelectionChanged(const QItemSelection &selection) { QModelIndex index; if (!selection.isEmpty()) index = selection.first().topLeft(); if (index.isValid()) { QGraphicsItem *item = index.data(SceneModel::SceneItemRole).value(); QGraphicsObject *obj = item->toGraphicsObject(); if (obj) m_propertyController->setObject(obj); else m_propertyController->setObject(item, QStringLiteral("QGraphicsItem")); emit itemSelected(item->mapRectToScene(item->boundingRect())); } else { m_propertyController->setObject(nullptr); emit sceneChanged(); } } void SceneInspector::qObjectSelected(QObject *object, const QPoint &pos) { QWidget *widget = qobject_cast(object); QGraphicsView *qgv = Util::findParentOfType(object); if (qgv) { // TODO: select qgv->scene() first, right now this only works for a single scene QGraphicsItem *item = qgv->itemAt(widget ? widget->mapTo(qgv, pos) : pos); if (item) sceneItemSelected(item); } if (auto item = qobject_cast(object)) sceneItemSelected(item); } void SceneInspector::nonQObjectSelected(void *obj, const QString &typeName) { if (typeName == QLatin1String("QGraphicsItem*")) // TODO: can we get sub-classes here? sceneItemSelected(reinterpret_cast(obj)); } void SceneInspector::sceneItemSelected(QGraphicsItem *item) { const auto indexList = m_itemSelectionModel->model()->match( m_itemSelectionModel->model()->index(0, 0), SceneModel::SceneItemRole, QVariant::fromValue(item), 1, Qt::MatchExactly | Qt::MatchRecursive | Qt::MatchWrap); if (indexList.isEmpty()) return; const QModelIndex index = indexList.first(); m_itemSelectionModel->setCurrentIndex(index, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); } void SceneInspector::sceneClicked(const QPointF &pos) { QGraphicsItem *item = m_sceneModel->scene()->itemAt(pos, QTransform()); if (item) sceneItemSelected(item); } void SceneInspector::registerGraphicsViewMetaTypes() { qRegisterMetaType(); qRegisterMetaType(); MetaObject *mo = nullptr; MO_ADD_METAOBJECT0(QGraphicsItem); MO_ADD_PROPERTY(QGraphicsItem, acceptDrops, setAcceptDrops); MO_ADD_PROPERTY(QGraphicsItem, acceptHoverEvents, setAcceptHoverEvents); MO_ADD_PROPERTY(QGraphicsItem, acceptTouchEvents, setAcceptTouchEvents); MO_ADD_PROPERTY(QGraphicsItem, acceptedMouseButtons, setAcceptedMouseButtons); MO_ADD_PROPERTY_RO(QGraphicsItem, boundingRect); MO_ADD_PROPERTY(QGraphicsItem, boundingRegionGranularity, setBoundingRegionGranularity); MO_ADD_PROPERTY_RO(QGraphicsItem, cacheMode); MO_ADD_PROPERTY_RO(QGraphicsItem, childrenBoundingRect); MO_ADD_PROPERTY_RO(QGraphicsItem, clipPath); MO_ADD_PROPERTY(QGraphicsItem, cursor, setCursor); MO_ADD_PROPERTY_RO(QGraphicsItem, effectiveOpacity); MO_ADD_PROPERTY(QGraphicsItem, filtersChildEvents, setFiltersChildEvents); MO_ADD_PROPERTY(QGraphicsItem, flags, setFlags); MO_ADD_PROPERTY_RO(QGraphicsItem, focusItem); MO_ADD_PROPERTY_RO(QGraphicsItem, focusProxy); MO_ADD_PROPERTY_RO(QGraphicsItem, graphicsEffect); MO_ADD_PROPERTY_RO(QGraphicsItem, group); MO_ADD_PROPERTY_RO(QGraphicsItem, hasCursor); MO_ADD_PROPERTY_RO(QGraphicsItem, hasFocus); MO_ADD_PROPERTY(QGraphicsItem, inputMethodHints, setInputMethodHints); MO_ADD_PROPERTY_RO(QGraphicsItem, isActive); MO_ADD_PROPERTY_RO(QGraphicsItem, isClipped); MO_ADD_PROPERTY(QGraphicsItem, isEnabled, setEnabled); MO_ADD_PROPERTY_RO(QGraphicsItem, isPanel); MO_ADD_PROPERTY(QGraphicsItem, isSelected, setSelected); MO_ADD_PROPERTY_RO(QGraphicsItem, isUnderMouse); MO_ADD_PROPERTY(QGraphicsItem, isVisible, setVisible); MO_ADD_PROPERTY_RO(QGraphicsItem, isWidget); MO_ADD_PROPERTY_RO(QGraphicsItem, isWindow); MO_ADD_PROPERTY(QGraphicsItem, opacity, setOpacity); MO_ADD_PROPERTY_RO(QGraphicsItem, opaqueArea); MO_ADD_PROPERTY_RO(QGraphicsItem, panel); MO_ADD_PROPERTY(QGraphicsItem, panelModality, setPanelModality); MO_ADD_PROPERTY_RO(QGraphicsItem, parentItem); MO_ADD_PROPERTY_RO(QGraphicsItem, parentObject); MO_ADD_PROPERTY_RO(QGraphicsItem, parentWidget); MO_ADD_PROPERTY(QGraphicsItem, pos, setPos); MO_ADD_PROPERTY(QGraphicsItem, rotation, setRotation); MO_ADD_PROPERTY(QGraphicsItem, scale, setScale); MO_ADD_PROPERTY_RO(QGraphicsItem, sceneBoundingRect); MO_ADD_PROPERTY_RO(QGraphicsItem, scenePos); MO_ADD_PROPERTY_RO(QGraphicsItem, sceneTransform); MO_ADD_PROPERTY_RO(QGraphicsItem, shape); MO_ADD_PROPERTY(QGraphicsItem, toolTip, setToolTip); MO_ADD_PROPERTY_RO(QGraphicsItem, topLevelItem); MO_ADD_PROPERTY_RO(QGraphicsItem, topLevelWidget); MO_ADD_PROPERTY_RO(QGraphicsItem, transform /*, setTransform*/); // TODO: support setTransform MO_ADD_PROPERTY(QGraphicsItem, transformOriginPoint, setTransformOriginPoint); MO_ADD_PROPERTY_RO(QGraphicsItem, type); MO_ADD_PROPERTY_RO(QGraphicsItem, window); MO_ADD_PROPERTY(QGraphicsItem, x, setX); MO_ADD_PROPERTY(QGraphicsItem, y, setY); MO_ADD_PROPERTY(QGraphicsItem, zValue, setZValue); MO_ADD_METAOBJECT1(QAbstractGraphicsShapeItem, QGraphicsItem); MO_ADD_PROPERTY(QAbstractGraphicsShapeItem, brush, setBrush); MO_ADD_PROPERTY(QAbstractGraphicsShapeItem, pen, setPen); MO_ADD_METAOBJECT1(QGraphicsEllipseItem, QAbstractGraphicsShapeItem); MO_ADD_PROPERTY(QGraphicsEllipseItem, rect, setRect); MO_ADD_PROPERTY(QGraphicsEllipseItem, spanAngle, setSpanAngle); MO_ADD_PROPERTY(QGraphicsEllipseItem, startAngle, setStartAngle); MO_ADD_METAOBJECT1(QGraphicsPathItem, QAbstractGraphicsShapeItem); MO_ADD_PROPERTY(QGraphicsPathItem, path, setPath); MO_ADD_METAOBJECT1(QGraphicsPolygonItem, QAbstractGraphicsShapeItem); MO_ADD_PROPERTY(QGraphicsPolygonItem, fillRule, setFillRule); MO_ADD_PROPERTY(QGraphicsPolygonItem, polygon, setPolygon); MO_ADD_METAOBJECT1(QGraphicsSimpleTextItem, QAbstractGraphicsShapeItem); MO_ADD_PROPERTY(QGraphicsSimpleTextItem, font, setFont); MO_ADD_PROPERTY(QGraphicsSimpleTextItem, text, setText); MO_ADD_METAOBJECT1(QGraphicsRectItem, QAbstractGraphicsShapeItem); MO_ADD_PROPERTY(QGraphicsRectItem, rect, setRect); MO_ADD_METAOBJECT1(QGraphicsLineItem, QGraphicsItem); MO_ADD_PROPERTY(QGraphicsLineItem, line, setLine); MO_ADD_PROPERTY(QGraphicsLineItem, pen, setPen); MO_ADD_METAOBJECT1(QGraphicsPixmapItem, QGraphicsItem); MO_ADD_PROPERTY(QGraphicsPixmapItem, offset, setOffset); MO_ADD_PROPERTY(QGraphicsPixmapItem, pixmap, setPixmap); MO_ADD_PROPERTY(QGraphicsPixmapItem, shapeMode, setShapeMode); MO_ADD_PROPERTY(QGraphicsPixmapItem, transformationMode, setTransformationMode); // no extra properties, but we need the inheritance connection for anything above to work MO_ADD_METAOBJECT2(QGraphicsObject, QObject, QGraphicsItem); MO_ADD_METAOBJECT1(QGraphicsTextItem, QGraphicsObject); MO_ADD_PROPERTY(QGraphicsTextItem, defaultTextColor, setDefaultTextColor); MO_ADD_PROPERTY(QGraphicsTextItem, document, setDocument); MO_ADD_PROPERTY(QGraphicsTextItem, font, setFont); MO_ADD_PROPERTY(QGraphicsTextItem, tabChangesFocus, setTabChangesFocus); MO_ADD_PROPERTY(QGraphicsTextItem, textInteractionFlags, setTextInteractionFlags); MO_ADD_PROPERTY(QGraphicsTextItem, textWidth, setTextWidth); MO_ADD_METAOBJECT0(QGraphicsLayoutItem); MO_ADD_PROPERTY_RO(QGraphicsLayoutItem, contentsRect); MO_ADD_PROPERTY(QGraphicsLayoutItem, geometry, setGeometry); MO_ADD_PROPERTY_RO(QGraphicsLayoutItem, graphicsItem); MO_ADD_PROPERTY_RO(QGraphicsLayoutItem, isLayout); MO_ADD_PROPERTY(QGraphicsLayoutItem, maximumHeight, setMaximumHeight); MO_ADD_PROPERTY(QGraphicsLayoutItem, maximumSize, setMaximumSize); MO_ADD_PROPERTY(QGraphicsLayoutItem, maximumWidth, setMaximumWidth); MO_ADD_PROPERTY_RO(QGraphicsLayoutItem, ownedByLayout); MO_ADD_PROPERTY(QGraphicsLayoutItem, minimumHeight, setMinimumHeight); MO_ADD_PROPERTY(QGraphicsLayoutItem, minimumSize, setMinimumSize); MO_ADD_PROPERTY(QGraphicsLayoutItem, minimumWidth, setMinimumWidth); MO_ADD_PROPERTY_RO(QGraphicsLayoutItem, parentLayoutItem); MO_ADD_PROPERTY(QGraphicsLayoutItem, preferredHeight, setPreferredHeight); MO_ADD_PROPERTY(QGraphicsLayoutItem, preferredSize, setPreferredSize); MO_ADD_PROPERTY(QGraphicsLayoutItem, preferredWidth, setPreferredWidth); MO_ADD_PROPERTY(QGraphicsLayoutItem, sizePolicy, setSizePolicy); MO_ADD_METAOBJECT1(QGraphicsLayout, QGraphicsLayoutItem); MO_ADD_PROPERTY_RO(QGraphicsLayout, count); MO_ADD_PROPERTY_RO(QGraphicsLayout, isActivated); MO_ADD_METAOBJECT2(QGraphicsWidget, QGraphicsObject, QGraphicsLayoutItem); MO_ADD_PROPERTY_RO(QGraphicsWidget, windowFrameGeometry); MO_ADD_PROPERTY_RO(QGraphicsWidget, windowFrameRect); MO_ADD_METAOBJECT1(QGraphicsProxyWidget, QGraphicsWidget); MO_ADD_PROPERTY_RO(QGraphicsProxyWidget, widget); } #define E(x) { QGraphicsItem:: x, #x } static const MetaEnum::Value graphics_item_flags_table[] = { E(ItemIsMovable), E(ItemIsSelectable), E(ItemIsFocusable), E(ItemClipsToShape), E(ItemClipsChildrenToShape), E(ItemIgnoresTransformations), E(ItemIgnoresParentOpacity), E(ItemDoesntPropagateOpacityToChildren), E(ItemStacksBehindParent), E(ItemUsesExtendedStyleOption), E(ItemHasNoContents), E(ItemSendsGeometryChanges), E(ItemAcceptsInputMethod), E(ItemNegativeZStacksBehindParent), E(ItemIsPanel), E(ItemIsFocusScope), E(ItemSendsScenePositionChanges), E(ItemStopsClickFocusPropagation), E(ItemStopsFocusHandling), E(ItemContainsChildrenInShape) }; static QString graphicsItemFlagsToString(QGraphicsItem::GraphicsItemFlags flags) { return MetaEnum::flagsToString(flags, graphics_item_flags_table); } static const MetaEnum::Value graphics_item_cache_mode_table[] = { E(NoCache), E(ItemCoordinateCache), E(DeviceCoordinateCache) }; static QString graphicsItemCacheModeToString(QGraphicsItem::CacheMode mode) { return MetaEnum::enumToString(mode, graphics_item_cache_mode_table); } static const MetaEnum::Value graphics_item_panel_modality_table[] = { E(NonModal), E(PanelModal), E(SceneModal) }; static QString graphicsItemPanelModalityToString(QGraphicsItem::PanelModality modality) { return MetaEnum::enumToString(modality, graphics_item_panel_modality_table); } #undef E void SceneInspector::registerVariantHandlers() { VariantHandler::registerStringConverter(Util::addressToString); VariantHandler::registerStringConverter(Util::addressToString); VariantHandler::registerStringConverter(Util::addressToString); VariantHandler::registerStringConverter(Util::addressToString); VariantHandler::registerStringConverter( graphicsItemFlagsToString); VariantHandler::registerStringConverter(graphicsItemCacheModeToString); VariantHandler::registerStringConverter( graphicsItemPanelModalityToString); } gammaray-2.11.3/plugins/sceneinspector/sceneinspector.h000066400000000000000000000057241413310443000232770ustar00rootroot00000000000000/* sceneinspector.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_SCENEINSPECTOR_SCENEINSPECTOR_H #define GAMMARAY_SCENEINSPECTOR_SCENEINSPECTOR_H #include #include "sceneinspectorinterface.h" #include QT_BEGIN_NAMESPACE class QItemSelectionModel; class QItemSelection; class QModelIndex; QT_END_NAMESPACE namespace GammaRay { class PropertyController; class SceneModel; class SceneInspector : public SceneInspectorInterface { Q_OBJECT Q_INTERFACES(GammaRay::SceneInspectorInterface) public: explicit SceneInspector(Probe *probe, QObject *parent = nullptr); private slots: void initializeGui() override; void renderScene(const QTransform &transform, const QSize &size) override; void sceneSelected(const QItemSelection &selection); void sceneItemSelectionChanged(const QItemSelection &selection); void sceneItemSelected(QGraphicsItem *item); void qObjectSelected(QObject *object, const QPoint &pos); void nonQObjectSelected(void *obj, const QString &typeName); void sceneClicked(const QPointF &pos) override; void clientConnectedChanged(bool clientConnected); private: QString findBestType(QGraphicsItem *item); void registerGraphicsViewMetaTypes(); void registerVariantHandlers(); void connectToScene(); private: SceneModel *m_sceneModel; QItemSelectionModel *m_itemSelectionModel; PropertyController *m_propertyController; bool m_clientConnected; }; class SceneInspectorFactory : public QObject, public StandardToolFactory { Q_OBJECT Q_INTERFACES(GammaRay::ToolFactory) Q_PLUGIN_METADATA(IID "com.kdab.GammaRay.ToolFactory" FILE "gammaray_sceneinspector.json") public: explicit SceneInspectorFactory(QObject *parent = nullptr) : QObject(parent) { } }; } #endif // GAMMARAY_SCENEINSPECTOR_H gammaray-2.11.3/plugins/sceneinspector/sceneinspectorclient.cpp000066400000000000000000000036171413310443000250300ustar00rootroot00000000000000/* sceneinspectorclient.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "sceneinspectorclient.h" #include #include using namespace GammaRay; SceneInspectorClient::SceneInspectorClient(QObject *parent) : SceneInspectorInterface(parent) { } SceneInspectorClient::~SceneInspectorClient() = default; void SceneInspectorClient::initializeGui() { Endpoint::instance()->invokeObject(objectName(), "initializeGui"); } void SceneInspectorClient::renderScene(const QTransform &transform, const QSize &size) { Endpoint::instance()->invokeObject(objectName(), "renderScene", QVariantList() << transform << size); } void SceneInspectorClient::sceneClicked(const QPointF &pos) { Endpoint::instance()->invokeObject(objectName(), "sceneClicked", QVariantList() << pos); } gammaray-2.11.3/plugins/sceneinspector/sceneinspectorclient.h000066400000000000000000000033441413310443000244720ustar00rootroot00000000000000/* sceneinspectorclient.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_SCENEINSPECTOR_SCENEINSPECTORCLIENT_H #define GAMMARAY_SCENEINSPECTOR_SCENEINSPECTORCLIENT_H #include "sceneinspectorinterface.h" namespace GammaRay { class SceneInspectorClient : public SceneInspectorInterface { Q_OBJECT Q_INTERFACES(GammaRay::SceneInspectorInterface) public: explicit SceneInspectorClient(QObject *parent = nullptr); ~SceneInspectorClient() override; void initializeGui() override; void renderScene(const QTransform &transform, const QSize &size) override; void sceneClicked(const QPointF &pos) override; }; } #endif // GAMMARAY_SCENEINSPECTORCLIENT_H gammaray-2.11.3/plugins/sceneinspector/sceneinspectorinterface.cpp000066400000000000000000000053611413310443000255100ustar00rootroot00000000000000/* sceneinspectorinterface.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "sceneinspectorinterface.h" #include #include #include using namespace GammaRay; SceneInspectorInterface::SceneInspectorInterface(QObject *parent) : QObject(parent) { ObjectBroker::registerObject(this); } SceneInspectorInterface::~SceneInspectorInterface() = default; void SceneInspectorInterface::paintItemDecoration(QGraphicsItem *item, const QTransform &transform, QPainter *painter) { const QRectF itemBoundingRect = item->boundingRect(); // coord system, TODO: nicer axis with arrows, tics, markers for current mouse position etc. painter->setPen(Qt::black); const qreal maxX = qMax(qAbs(itemBoundingRect.left()), qAbs(itemBoundingRect.right())); const qreal maxY = qMax(qAbs(itemBoundingRect.top()), qAbs(itemBoundingRect.bottom())); const qreal maxXY = qMax(maxX, maxY) * 1.5f; painter->drawLine(item->mapToScene(-maxXY, 0), item->mapToScene(maxXY, 0)); painter->drawLine(item->mapToScene(0, -maxXY), item->mapToScene(0, maxXY)); painter->setPen(Qt::blue); const QPolygonF boundingBox = item->mapToScene(itemBoundingRect); painter->drawPolygon(boundingBox); painter->setPen(Qt::green); const QPainterPath shape = item->mapToScene(item->shape()); painter->drawPath(shape); painter->setPen(Qt::red); const QPointF transformOrigin = item->mapToScene(item->transformOriginPoint()); painter->drawEllipse(transformOrigin, 5.0 / transform.m11(), 5.0 / transform.m22()); } gammaray-2.11.3/plugins/sceneinspector/sceneinspectorinterface.h000066400000000000000000000044211413310443000251510ustar00rootroot00000000000000/* sceneinspectorinterface.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_SCENEINSPECTOR_SCENEINSPECTORINTERFACE_H #define GAMMARAY_SCENEINSPECTOR_SCENEINSPECTORINTERFACE_H #include QT_BEGIN_NAMESPACE class QPainter; class QGraphicsItem; class QSize; class QTransform; class QRectF; class QPixmap; class QPointF; QT_END_NAMESPACE namespace GammaRay { class SceneInspectorInterface : public QObject { Q_OBJECT public: explicit SceneInspectorInterface(QObject *parent = nullptr); ~SceneInspectorInterface() override; virtual void initializeGui() = 0; static void paintItemDecoration(QGraphicsItem *item, const QTransform &transform, QPainter *painter); public slots: virtual void renderScene(const QTransform &transform, const QSize &size) = 0; virtual void sceneClicked(const QPointF &pos) = 0; signals: void sceneRectChanged(const QRectF &rect); void sceneChanged(); void sceneRendered(const QPixmap &view); void itemSelected(const QRectF &boundingRect); }; } QT_BEGIN_NAMESPACE Q_DECLARE_INTERFACE(GammaRay::SceneInspectorInterface, "com.kdab.GammaRay.SceneInspector") QT_END_NAMESPACE #endif // GAMMARAY_SCENEINSPECTORINTERFACE_H gammaray-2.11.3/plugins/sceneinspector/sceneinspectorwidget.cpp000066400000000000000000000230371413310443000250330ustar00rootroot00000000000000/* sceneinspectorwidget.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "sceneinspectorwidget.h" #include "scenemodel.h" #include "sceneinspectorclient.h" #include "graphicsview.h" #include "ui_sceneinspectorwidget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace GammaRay; using namespace std; static QObject *createClientSceneInspector(const QString & /*name*/, QObject *parent) { return new SceneInspectorClient(parent); } SceneInspectorWidget::SceneInspectorWidget(QWidget *parent) : QWidget(parent) , ui(new Ui::SceneInspectorWidget) , m_stateManager(this) , m_interface(nullptr) , m_scene(new QGraphicsScene(this)) , m_pixmap(new QGraphicsPixmapItem) , m_updateTimer(new QTimer(this)) { ObjectBroker::registerClientObjectFactoryCallback( createClientSceneInspector); m_interface = ObjectBroker::object(); ui->setupUi(this); ui->sceneTreeView->header()->setObjectName("sceneTreeViewHeader"); ui->scenePropertyWidget->setObjectBaseName(QStringLiteral("com.kdab.GammaRay.SceneInspector")); ui->sceneComboBox->setModel(ObjectBroker::model(QStringLiteral("com.kdab.GammaRay.SceneList"))); connect(ui->sceneComboBox, static_cast(&QComboBox::currentIndexChanged), this, &SceneInspectorWidget::sceneSelected); auto sceneModel = ObjectBroker::model(QStringLiteral("com.kdab.GammaRay.SceneGraphModel")); ui->sceneTreeView->setDeferredResizeMode(0, QHeaderView::ResizeToContents); ui->sceneTreeView->setModel(sceneModel); new SearchLineController(ui->sceneTreeSearchLine, sceneModel); QItemSelectionModel *itemSelection = ObjectBroker::selectionModel(sceneModel); ui->sceneTreeView->setSelectionModel(itemSelection); connect(itemSelection, &QItemSelectionModel::selectionChanged, this, &SceneInspectorWidget::sceneItemSelected); connect(ui->sceneTreeView, &QWidget::customContextMenuRequested, this, &SceneInspectorWidget::sceneContextMenu); ui->graphicsSceneView->setGraphicsScene(m_scene); connect(m_interface, &SceneInspectorInterface::sceneRectChanged, this, &SceneInspectorWidget::sceneRectChanged); connect(m_interface, &SceneInspectorInterface::sceneChanged, this, &SceneInspectorWidget::sceneChanged); connect(m_interface, &SceneInspectorInterface::sceneRendered, this, &SceneInspectorWidget::sceneRendered); connect(m_interface, &SceneInspectorInterface::itemSelected, this, &SceneInspectorWidget::itemSelected); m_interface->initializeGui(); m_pixmap->setFlag(QGraphicsItem::ItemIgnoresTransformations); m_scene->addItem(m_pixmap); connect(ui->graphicsSceneView->view(), &GraphicsView::transformChanged, this, &SceneInspectorWidget::visibleSceneRectChanged); connect(ui->graphicsSceneView->view()->horizontalScrollBar(), &QAbstractSlider::valueChanged, this, &SceneInspectorWidget::visibleSceneRectChanged); connect(ui->graphicsSceneView->view()->verticalScrollBar(), &QAbstractSlider::valueChanged, this, &SceneInspectorWidget::visibleSceneRectChanged); if (Endpoint::instance()->isRemoteClient()) ui->graphicsSceneView->view()->viewport()->installEventFilter(this); QItemSelectionModel *selection = ObjectBroker::selectionModel(ui->sceneComboBox->model()); if (selection->currentIndex().isValid()) sceneSelected(selection->currentIndex().row()); else if (ui->sceneComboBox->currentIndex() >= 0) // no server-side selection yet, but there's data available sceneSelected(ui->sceneComboBox->currentIndex()); m_stateManager.setDefaultSizes(ui->mainSplitter, UISizeVector() << "50%" << "50%"); m_stateManager.setDefaultSizes(ui->previewSplitter, UISizeVector() << "50%" << "50%"); connect(ui->scenePropertyWidget, &PropertyWidget::tabsUpdated, this, &SceneInspectorWidget::propertyWidgetTabsChanged); // limit fps to prevent bad performance, and to group update requests which is esp. required // for scrolling and similar high-frequency update requests m_updateTimer->setSingleShot(true); m_updateTimer->setInterval(100); connect(m_updateTimer, &QTimer::timeout, this, &SceneInspectorWidget::requestSceneUpdate); } SceneInspectorWidget::~SceneInspectorWidget() = default; bool SceneInspectorWidget::eventFilter(QObject *obj, QEvent *event) { Q_ASSERT(obj == ui->graphicsSceneView->view()->viewport()); if (event->type() == QEvent::Resize) { QMetaObject::invokeMethod(this, "visibleSceneRectChanged", Qt::QueuedConnection); } else if (event->type() == QEvent::MouseButtonRelease) { QMouseEvent *e = static_cast(event); if (e->button() == Qt::LeftButton && e->modifiers() == (Qt::ControlModifier | Qt::ShiftModifier)) m_interface->sceneClicked(ui->graphicsSceneView->view()->mapToScene(e->pos())); } return QObject::eventFilter(obj, event); } void SceneInspectorWidget::itemSelected(const QRectF &boundingRect) { ui->graphicsSceneView->view()->fitInView(boundingRect, Qt::KeepAspectRatio); ui->graphicsSceneView->view()->scale(0.8, 0.8); visibleSceneRectChanged(); } void SceneInspectorWidget::sceneRectChanged(const QRectF &rect) { m_scene->setSceneRect(rect); visibleSceneRectChanged(); } void SceneInspectorWidget::sceneChanged() { if (!m_updateTimer->isActive()) m_updateTimer->start(); } void SceneInspectorWidget::requestSceneUpdate() { if (!Endpoint::instance()->isRemoteClient()) return; if (ui->graphicsSceneView->view()->rect().isEmpty()) { // when the splitter is moved to hide the view, don't request updates return; } m_interface->renderScene(ui->graphicsSceneView->view()->viewportTransform(), ui->graphicsSceneView->view()->viewport()->rect().size()); } void SceneInspectorWidget::sceneRendered(const QPixmap &view) { m_pixmap->setPixmap(view); } void SceneInspectorWidget::visibleSceneRectChanged() { m_pixmap->setPos(ui->graphicsSceneView->view()->mapToScene(0, 0)); sceneChanged(); } void SceneInspectorWidget::sceneSelected(int index) { const QModelIndex mi = ui->sceneComboBox->model()->index(index, 0); ObjectBroker::selectionModel(ui->sceneComboBox->model())->select(mi, QItemSelectionModel::ClearAndSelect); if (!Endpoint::instance()->isRemoteClient()) { // for in-process mode, use the user scene directly. This is much more performant and we can // skip the pixmap conversions and fps limitations thereof. QObject *obj = ui->sceneComboBox->itemData(index, ObjectModel::ObjectRole).value(); QGraphicsScene *scene = qobject_cast(obj); cout << Q_FUNC_INFO << ' ' << scene << ' ' << obj << endl; if (scene) ui->graphicsSceneView->setGraphicsScene(scene); } } void SceneInspectorWidget::sceneItemSelected(const QItemSelection &selection) { if (selection.isEmpty()) return; const auto index = selection.first().topLeft(); if (!index.isValid()) return; ui->sceneTreeView->scrollTo(index); // in case selection does not come from us if (!Endpoint::instance()->isRemoteClient()) { QGraphicsItem *item = index.data(SceneModel::SceneItemRole).value(); ui->graphicsSceneView->showGraphicsItem(item); } } void SceneInspectorWidget::sceneContextMenu(QPoint pos) { const auto index = ui->sceneTreeView->indexAt(pos); if (!index.isValid()) return; const auto objectId = index.data(ObjectModel::ObjectIdRole).value(); QMenu menu(tr("QGraphicsItem @ %1").arg(QLatin1String("0x") + QString::number( objectId.id(), 16))); ContextMenuExtension ext(objectId); ext.populateMenu(&menu); menu.exec(ui->sceneTreeView->viewport()->mapToGlobal(pos)); } void SceneInspectorWidget::propertyWidgetTabsChanged() { m_stateManager.saveState(); m_stateManager.reset(); } void SceneInspectorUiFactory::initUi() { } gammaray-2.11.3/plugins/sceneinspector/sceneinspectorwidget.h000066400000000000000000000053661413310443000245050ustar00rootroot00000000000000/* sceneinspectorwidget.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_SCENEINSPECTOR_SCENEINSPECTORWIDGET_H #define GAMMARAY_SCENEINSPECTOR_SCENEINSPECTORWIDGET_H #include #include #include QT_BEGIN_NAMESPACE class QGraphicsPixmapItem; class QGraphicsScene; class QItemSelection; QT_END_NAMESPACE namespace GammaRay { class SceneInspectorInterface; namespace Ui { class SceneInspectorWidget; } class SceneInspectorWidget : public QWidget { Q_OBJECT public: explicit SceneInspectorWidget(QWidget *parent = nullptr); ~SceneInspectorWidget() override; private slots: void sceneSelected(int index); void sceneItemSelected(const QItemSelection &selection); void sceneRectChanged(const QRectF &rect); void sceneChanged(); void requestSceneUpdate(); void sceneRendered(const QPixmap &view); void visibleSceneRectChanged(); void itemSelected(const QRectF &boundingRect); void sceneContextMenu(QPoint pos); void propertyWidgetTabsChanged(); private: bool eventFilter(QObject *obj, QEvent *event) override; QScopedPointer ui; UIStateManager m_stateManager; SceneInspectorInterface *m_interface; QGraphicsScene *m_scene; QGraphicsPixmapItem *m_pixmap; QTimer *m_updateTimer; }; class SceneInspectorUiFactory : public QObject, public StandardToolUiFactory { Q_OBJECT Q_INTERFACES(GammaRay::ToolUiFactory) Q_PLUGIN_METADATA(IID "com.kdab.GammaRay.ToolUiFactory" FILE "gammaray_sceneinspector.json") public: void initUi() override; }; } #endif // GAMMARAY_SCENEINSPECTOR_H gammaray-2.11.3/plugins/sceneinspector/sceneinspectorwidget.ui000066400000000000000000000040171413310443000246630ustar00rootroot00000000000000 GammaRay::SceneInspectorWidget 0 0 400 300 Qt::Horizontal Qt::Vertical Qt::CustomContextMenu GammaRay::DeferredTreeView QTreeView
    ui/deferredtreeview.h
    GammaRay::PropertyWidget QWidget
    ui/propertywidget.h
    1
    GammaRay::GraphicsSceneView QWidget
    graphicssceneview.h
    1
    gammaray-2.11.3/plugins/sceneinspector/scenemodel.cpp000066400000000000000000000134311413310443000227160ustar00rootroot00000000000000/* scenemodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "scenemodel.h" #include #include #include #include #include #include using namespace GammaRay; #define QGV_ITEMTYPE(Type) \ { \ Type t; \ m_typeNames.insert(t.type(), QStringLiteral(#Type)); \ } SceneModel::SceneModel(QObject *parent) : QAbstractItemModel(parent) , m_scene(nullptr) { QGV_ITEMTYPE(QGraphicsLineItem) QGV_ITEMTYPE(QGraphicsPixmapItem) QGV_ITEMTYPE(QGraphicsRectItem) QGV_ITEMTYPE(QGraphicsEllipseItem) QGV_ITEMTYPE(QGraphicsPathItem) QGV_ITEMTYPE(QGraphicsPolygonItem) QGV_ITEMTYPE(QGraphicsSimpleTextItem) QGV_ITEMTYPE(QGraphicsItemGroup) } void SceneModel::setScene(QGraphicsScene *scene) { beginResetModel(); m_scene = scene; endResetModel(); } QGraphicsScene *SceneModel::scene() const { return m_scene; } QVariant SceneModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); QGraphicsItem *item = static_cast(index.internalPointer()); if (item && role == Qt::DisplayRole) { QGraphicsObject *obj = item->toGraphicsObject(); if (index.column() == 0) { if (obj && !obj->objectName().isEmpty()) return obj->objectName(); return QStringLiteral("0x%1"). arg(QString::number(reinterpret_cast(item), 16)); } else if (index.column() == 1) { if (obj) return obj->metaObject()->className(); return typeName(item->type()); } } else if (role == SceneItemRole) { return QVariant::fromValue(item); } else if (item && role == Qt::ForegroundRole) { if (!item->isVisible()) return qApp->palette().color(QPalette::Disabled, QPalette::Text); } else if (item && role == ObjectModel::ObjectIdRole) { // TODO also handle the non-QObject case return QVariant::fromValue(ObjectId(item->toGraphicsObject())); } return QVariant(); } int SceneModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); return 2; } int SceneModel::rowCount(const QModelIndex &parent) const { if (!m_scene) return 0; if (parent.isValid()) { if (parent.column() != 0) return 0; QGraphicsItem *item = static_cast(parent.internalPointer()); if (item) return item->childItems().size(); else return 0; } return topLevelItems().size(); } QModelIndex SceneModel::parent(const QModelIndex &child) const { if (!child.isValid()) return {}; QGraphicsItem *item = static_cast(child.internalPointer()); if (!item->parentItem()) return QModelIndex(); int row = 0; if (item->parentItem()->parentItem()) row = item->parentItem()->parentItem()->childItems().indexOf(item->parentItem()); return createIndex(row, 0, item->parentItem()); } QModelIndex SceneModel::index(int row, int column, const QModelIndex &parent) const { if (column < 0 || column >= columnCount()) return {}; if (!parent.isValid() && row >= 0 && row < topLevelItems().size()) return createIndex(row, column, topLevelItems().at(row)); QGraphicsItem *parentItem = static_cast(parent.internalPointer()); if (!parentItem || row < 0 || row >= parentItem->childItems().size()) return QModelIndex(); return createIndex(row, column, parentItem->childItems().at(row)); } QList SceneModel::topLevelItems() const { QList topLevel; if (!m_scene) return topLevel; Q_FOREACH(QGraphicsItem *item, m_scene->items()) { if (!item->parentItem()) topLevel.push_back(item); } return topLevel; } QVariant SceneModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role == Qt::DisplayRole && orientation == Qt::Horizontal) { switch (section) { case 0: return tr("Item"); case 1: return tr("Type"); } } return QAbstractItemModel::headerData(section, orientation, role); } QString SceneModel::typeName(int itemType) const { auto it = m_typeNames.find(itemType); if (it != m_typeNames.end()) return it.value(); if (itemType == QGraphicsItem::UserType) return QStringLiteral("UserType"); if (itemType > QGraphicsItem::UserType) { return QStringLiteral("UserType + %1"). arg(itemType - static_cast(QGraphicsItem::UserType)); } return QString::number(itemType); } gammaray-2.11.3/plugins/sceneinspector/scenemodel.h000066400000000000000000000046661413310443000223750ustar00rootroot00000000000000/* scenemodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_SCENEINSPECTOR_SCENEMODEL_H #define GAMMARAY_SCENEINSPECTOR_SCENEMODEL_H #include #include QT_BEGIN_NAMESPACE class QGraphicsScene; class QGraphicsItem; QT_END_NAMESPACE namespace GammaRay { class SceneModel : public QAbstractItemModel { Q_OBJECT public: enum Role { SceneItemRole = UserRole + 1 }; explicit SceneModel(QObject *parent = nullptr); void setScene(QGraphicsScene *scene); QGraphicsScene *scene() const; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; QModelIndex parent(const QModelIndex &child) const override; QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; private: QList topLevelItems() const; /// Returns a string type name for the given QGV item type id QString typeName(int itemType) const; QGraphicsScene *m_scene; QHash m_typeNames; }; } #endif // GAMMARAY_SCENEMODEL_H gammaray-2.11.3/plugins/scriptenginedebugger/000077500000000000000000000000001413310443000212455ustar00rootroot00000000000000gammaray-2.11.3/plugins/scriptenginedebugger/CMakeLists.txt000066400000000000000000000014741413310443000240130ustar00rootroot00000000000000# probe plugin if (NOT GAMMARAY_CLIENT_ONLY_BUILD) set(gammaray_scriptenginedebugger_plugin_srcs scriptenginedebugger.cpp ) gammaray_add_plugin(gammaray_scriptenginedebugger_plugin JSON gammaray_scriptenginedebugger.json SOURCES ${gammaray_scriptenginedebugger_plugin_srcs} ) target_link_libraries(gammaray_scriptenginedebugger_plugin Qt5::Script Qt5::ScriptTools gammaray_core ) endif() # UI plugin if(GAMMARAY_BUILD_UI) set(gammaray_scriptenginedebugger_ui_plugin_srcs scriptenginedebuggerwidget.cpp ) gammaray_add_plugin(gammaray_scriptenginedebugger_ui_plugin JSON gammaray_scriptenginedebugger.json SOURCES ${gammaray_scriptenginedebugger_ui_plugin_srcs} ) target_link_libraries(gammaray_scriptenginedebugger_ui_plugin Qt5::Script Qt5::ScriptTools gammaray_ui ) endif() gammaray-2.11.3/plugins/scriptenginedebugger/gammaray_scriptenginedebugger.json000066400000000000000000000002121413310443000302100ustar00rootroot00000000000000{ "id": "gammaray_scriptenginedebugger", "name": "Script Engines", "types": [ "QScriptEngine" ], "remoteSupport": false } gammaray-2.11.3/plugins/scriptenginedebugger/scriptenginedebugger.cpp000066400000000000000000000042021413310443000261460ustar00rootroot00000000000000/* scriptenginedebugger.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "scriptenginedebugger.h" #include #include #include #include #include using namespace GammaRay; ///NOTE: for crashes related to script engine debugger on shutdown, see: /// https://bugreports.qt.nokia.com/browse/QTBUG-21548 /// Also it seems that we get another crash when the interrupt action /// was triggered and we close the mainwindow. ScriptEngineDebugger::ScriptEngineDebugger(Probe *probe, QObject *parent) : QObject(parent) { auto *scriptEngineFilter = new ObjectTypeFilterProxyModel(this); scriptEngineFilter->setSourceModel(probe->objectListModel()); auto *singleColumnProxy = new SingleColumnObjectProxyModel(this); singleColumnProxy->setSourceModel(scriptEngineFilter); probe->registerModel(QStringLiteral("com.kdab.GammaRay.ScriptEngines"), singleColumnProxy); } ScriptEngineDebugger::~ScriptEngineDebugger() = default; gammaray-2.11.3/plugins/scriptenginedebugger/scriptenginedebugger.h000066400000000000000000000036631413310443000256250ustar00rootroot00000000000000/* scriptenginedebugger.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_SCRIPTENGINEDEBUGGER_SCRIPTENGINEDEBUGGER_H #define GAMMARAY_SCRIPTENGINEDEBUGGER_SCRIPTENGINEDEBUGGER_H #include #include namespace GammaRay { class ScriptEngineDebugger : public QObject { Q_OBJECT public: explicit ScriptEngineDebugger(Probe *probe, QObject *parent = nullptr); ~ScriptEngineDebugger() override; }; class ScriptEngineDebuggerFactory : public QObject, public StandardToolFactory { Q_OBJECT Q_INTERFACES(GammaRay::ToolFactory) Q_PLUGIN_METADATA(IID "com.kdab.GammaRay.ToolFactory" FILE "gammaray_scriptenginedebugger.json") public: explicit ScriptEngineDebuggerFactory(QObject *parent = nullptr) : QObject(parent) { } }; } #endif // GAMMARAY_SCRIPTENGINEDEBUGGER_H gammaray-2.11.3/plugins/scriptenginedebugger/scriptenginedebuggerwidget.cpp000066400000000000000000000056261413310443000273650ustar00rootroot00000000000000/* scriptenginedebuggerwidget.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "scriptenginedebuggerwidget.h" #include "ui_scriptenginedebuggerwidget.h" #include #include #include #include #include #include #include using namespace GammaRay; ///NOTE: for crashes related to script engine debugger on shutdown, see: /// https://bugreports.qt.nokia.com/browse/QTBUG-21548 /// Also it seems that we get another crash when the interrupt action /// was triggered and we close the mainwindow. ScriptEngineDebuggerWidget::ScriptEngineDebuggerWidget(QWidget *parent) : QWidget(parent) , ui(new Ui::ScriptEngineDebuggerWidget) , m_stateManager(this) , debugger(new QScriptEngineDebugger(this)) { ui->setupUi(this); ui->scriptEngineComboBox->setModel(ObjectBroker::model(QStringLiteral( "com.kdab.GammaRay.ScriptEngines"))); connect(ui->scriptEngineComboBox, static_cast(&QComboBox::activated), this, &ScriptEngineDebuggerWidget::scriptEngineSelected); ui->verticalLayout_10->addWidget(debugger->standardWindow()); if (ui->scriptEngineComboBox->count()) scriptEngineSelected(0); } ScriptEngineDebuggerWidget::~ScriptEngineDebuggerWidget() { debugger->detach(); } void ScriptEngineDebuggerWidget::scriptEngineSelected(int index) { QObject *obj = ui->scriptEngineComboBox->itemData(index, ObjectModel::ObjectRole).value(); QScriptEngine *engine = qobject_cast(obj); if (engine) { debugger->attachTo(engine); // FIXME: if we'd do that, we'd get crashes on shutdown. // debugger->action(QScriptEngineDebugger::InterruptAction)->trigger(); } } gammaray-2.11.3/plugins/scriptenginedebugger/scriptenginedebuggerwidget.h000066400000000000000000000043071413310443000270250ustar00rootroot00000000000000/* scriptenginedebuggerwidget.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_SCRIPTENGINEDEBUGGER_SCRIPTENGINEDEBUGGERWIDGET_H #define GAMMARAY_SCRIPTENGINEDEBUGGER_SCRIPTENGINEDEBUGGERWIDGET_H #include #include #include #include QT_BEGIN_NAMESPACE class QScriptEngineDebugger; QT_END_NAMESPACE namespace GammaRay { namespace Ui { class ScriptEngineDebuggerWidget; } class ScriptEngineDebuggerWidget : public QWidget { Q_OBJECT public: explicit ScriptEngineDebuggerWidget(QWidget *parent = nullptr); ~ScriptEngineDebuggerWidget() override; private slots: void scriptEngineSelected(int index); private: QScopedPointer ui; UIStateManager m_stateManager; QScriptEngineDebugger *debugger; }; class ScriptEngineDebuggerUiFactory : public QObject, public StandardToolUiFactory { Q_OBJECT Q_INTERFACES(GammaRay::ToolUiFactory) Q_PLUGIN_METADATA( IID "com.kdab.GammaRay.ToolUiFactory" FILE "gammaray_scriptenginedebugger.json") }; } #endif // GAMMARAY_SCRIPTENGINEDEBUGGER_H gammaray-2.11.3/plugins/scriptenginedebugger/scriptenginedebuggerwidget.ui000066400000000000000000000010341413310443000272050ustar00rootroot00000000000000 GammaRay::ScriptEngineDebuggerWidget 0 0 400 300 gammaray-2.11.3/plugins/signalmonitor/000077500000000000000000000000001413310443000177335ustar00rootroot00000000000000gammaray-2.11.3/plugins/signalmonitor/CMakeLists.txt000066400000000000000000000024751413310443000225030ustar00rootroot00000000000000# shared part set(gammaray_signalmonitor_shared_srcs signalmonitorinterface.cpp signalmonitorcommon.cpp ) add_library(gammaray_signalmonitor_shared STATIC ${gammaray_signalmonitor_shared_srcs}) target_link_libraries(gammaray_signalmonitor_shared LINK_PRIVATE gammaray_common) set_target_properties(gammaray_signalmonitor_shared PROPERTIES POSITION_INDEPENDENT_CODE ON) target_compile_features(gammaray_signalmonitor_shared PUBLIC ${GAMMARAY_REQUIRED_CXX_FEATURES}) # probe plugin if (NOT GAMMARAY_CLIENT_ONLY_BUILD) set(gammaray_signalmonitor_srcs signalmonitor.cpp signalhistorymodel.cpp relativeclock.cpp ) gammaray_add_plugin(gammaray_signalmonitor JSON gammaray_signalmonitor.json SOURCES ${gammaray_signalmonitor_srcs} ) target_link_libraries(gammaray_signalmonitor gammaray_core gammaray_signalmonitor_shared ) target_link_libraries(gammaray_signalmonitor Qt5::Gui) endif () if(GAMMARAY_BUILD_UI) # ui plugin set(gammaray_signalmonitor_ui_srcs signalmonitorwidget.cpp signalhistorydelegate.cpp signalhistoryview.cpp signalmonitorclient.cpp ) gammaray_add_plugin(gammaray_signalmonitor_ui JSON gammaray_signalmonitor.json SOURCES ${gammaray_signalmonitor_ui_srcs} ) target_link_libraries(gammaray_signalmonitor_ui gammaray_ui gammaray_signalmonitor_shared ) endif() gammaray-2.11.3/plugins/signalmonitor/gammaray_signalmonitor.json000066400000000000000000000002631413310443000253720ustar00rootroot00000000000000{ "id": "gammaray_signalmonitor", "name": "Signals", "name[de]": "Signale", "selectableTypes": [ "QObject" ], "types": [ "QObject" ] } gammaray-2.11.3/plugins/signalmonitor/relativeclock.cpp000066400000000000000000000036661413310443000233010ustar00rootroot00000000000000/* relativeclock.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Mathias Hasselmann Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "relativeclock.h" #ifdef Q_OS_LINUX #include #include #endif // Q_OS_LINUX #include using namespace GammaRay; static qint64 appStartTime() { #ifdef Q_OS_LINUX // On Linux the application start time can be read by procfs. const QString &self = QStringLiteral("/proc/%1").arg(qApp->applicationPid()); return QFileInfo(self).lastModified().toMSecsSinceEpoch(); #else // !Q_OS_LINUX // On other platforms this is a rough estimation if called early. return QDateTime::currentMSecsSinceEpoch(); #endif // !Q_OS_LINUX } const RelativeClock *RelativeClock::sinceAppStart() { static const RelativeClock clock(appStartTime()); return &clock; } qint64 RelativeClock::currentMSecsSinceEpoch() { return QDateTime::currentMSecsSinceEpoch(); } gammaray-2.11.3/plugins/signalmonitor/relativeclock.h000066400000000000000000000035371413310443000227430ustar00rootroot00000000000000/* relativeclock.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Mathias Hasselmann Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_RELATIVECLOCK_H #define GAMMARAY_RELATIVECLOCK_H #include namespace GammaRay { class RelativeClock { public: explicit RelativeClock(qint64 offset = currentMSecsSinceEpoch()) : m_offset(offset) { } qint64 mSecs() const { return currentMSecsSinceEpoch() - offset(); } qint64 mSecs(qint64 alignment) const; qint64 offset() const { return m_offset; } static const RelativeClock *sinceAppStart(); private: static qint64 currentMSecsSinceEpoch(); private: const qint64 m_offset; }; inline qint64 RelativeClock::mSecs(qint64 alignment) const { const qint64 t = mSecs(); return t - t % alignment; } } // namespace GammaRay #endif // GAMMARAY_RELATIVECLOCK_H gammaray-2.11.3/plugins/signalmonitor/signalhistorydelegate.cpp000066400000000000000000000143251413310443000250360ustar00rootroot00000000000000/* signalhistorydelegate.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Mathias Hasselmann Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "signalhistorydelegate.h" #include "signalhistorymodel.h" #include "signalmonitorinterface.h" #include "signalmonitorcommon.h" #include #include #include #include #include #include using namespace GammaRay; SignalHistoryDelegate::SignalHistoryDelegate(QObject *parent) : QStyledItemDelegate(parent) , m_updateTimer(new QTimer(this)) , m_visibleOffset(0) , m_visibleInterval(15000) , m_totalInterval(0) { connect(m_updateTimer, &QTimer::timeout, this, &SignalHistoryDelegate::onUpdateTimeout); m_updateTimer->start(1000 / 25); onUpdateTimeout(); SignalMonitorInterface *iface = ObjectBroker::object(); connect(iface, &SignalMonitorInterface::clock, this, &SignalHistoryDelegate::onServerClockChanged); iface->sendClockUpdates(true); } void SignalHistoryDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { QStyledItemDelegate::paint(painter, option, index); const qint64 interval = m_visibleInterval; const qint64 startTime = m_visibleOffset; const qint64 endTime = startTime + interval; const QAbstractItemModel * const model = index.model(); const QVector &events = model->data(index, SignalHistoryModel::EventsRole).value >(); const qint64 t0 = qMax(static_cast(0), model->data(index, SignalHistoryModel::StartTimeRole).value() - startTime); qint64 t1 = model->data(index, SignalHistoryModel::EndTimeRole).value(); if (t1 < 0) // still alive t1 = m_totalInterval; t1 -= startTime; const qint64 dt = qMax(0LL, t1) - t0; const int x0 = option.rect.x() + 1; const int y0 = option.rect.y(); const int dx = option.rect.width() - 2; const int dy = option.rect.height(); const int x1 = x0 + dx * t0 / interval; const int x2 = dx * dt / interval + 1; if (t1 >= 0) painter->fillRect(x1, y0 + 1, x2, dy - 2, option.palette.window()); painter->setPen(option.palette.color(QPalette::WindowText)); for (qint64 ev : events) { const qint64 ts = SignalHistoryModel::timestamp(ev); if (ts >= startTime && ts < endTime) { const int x = x0 + dx * (ts - startTime) / interval; painter->drawLine(x, y0 + 1, x, y0 + dy - 2); } } } QSize SignalHistoryDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &) const { return {0, option.fontMetrics.lineSpacing()}; // FIXME: minimum height } void SignalHistoryDelegate::setVisibleInterval(qint64 interval) { if (m_visibleInterval != interval) { m_visibleInterval = interval; emit visibleIntervalChanged(m_visibleInterval); } } void SignalHistoryDelegate::setVisibleOffset(qint64 offset) { setActive(false); if (m_visibleOffset != offset) { m_visibleOffset = offset; emit visibleOffsetChanged(m_visibleOffset); } } void SignalHistoryDelegate::onUpdateTimeout() { // move the visible region to show the most recent samples m_visibleOffset = m_totalInterval - m_visibleInterval; emit visibleOffsetChanged(m_visibleOffset); } void SignalHistoryDelegate::onServerClockChanged(qint64 msecs) { m_totalInterval = msecs; emit totalIntervalChanged(); } void SignalHistoryDelegate::setActive(bool active) { if (m_updateTimer->isActive() != active) { if (active) m_updateTimer->start(); else m_updateTimer->stop(); emit isActiveChanged(isActive()); } } bool SignalHistoryDelegate::isActive() const { return m_updateTimer->isActive(); } QString SignalHistoryDelegate::toolTipAt(const QModelIndex &index, int position, int width) { const QAbstractItemModel * const model = index.model(); const QVector &events = model->data(index, SignalHistoryModel::EventsRole).value >(); const qint64 t = m_visibleInterval * position / width + m_visibleOffset; qint64 dtMin = std::numeric_limits::max(); int signalIndex = -1; qint64 signalTimestamp = -1; for (long long event : events) { signalTimestamp = SignalHistoryModel::timestamp(event); const qint64 dt = qAbs(signalTimestamp - t); if (dt < dtMin) { signalIndex = SignalHistoryModel::signalIndex(event); dtMin = dt; } } if (signalIndex < 0) return QString(); const auto signalNames = index.data(SignalHistoryModel::SignalMapRole).value >(); const auto it = signalNames.constFind(signalIndex); QString signalName; // see SignalHistoryModel, we store this with offset 1 to fit unknown ones into an unsigned value if (signalIndex == 0 || it == signalNames.constEnd() || it.value().isEmpty()) signalName = tr(""); else signalName = it.value(); const QString &ts = QLocale().toString(signalTimestamp); return tr("%1 at %2 ms").arg(signalName, ts); } gammaray-2.11.3/plugins/signalmonitor/signalhistorydelegate.h000066400000000000000000000055051413310443000245030ustar00rootroot00000000000000/* signalhistorydelegate.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Mathias Hasselmann Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_SIGNALHISTORYDELEGATE_H #define GAMMARAY_SIGNALHISTORYDELEGATE_H #include namespace GammaRay { class SignalHistoryDelegate : public QStyledItemDelegate { Q_OBJECT Q_PROPERTY( qint64 visibleInterval READ visibleInterval WRITE setVisibleInterval NOTIFY visibleIntervalChanged) Q_PROPERTY( qint64 visibleOffset READ visibleOffset NOTIFY setVisibleOffset NOTIFY visibleOffsetChanged) Q_PROPERTY(bool isActive READ isActive WRITE setActive NOTIFY isActiveChanged) public: explicit SignalHistoryDelegate(QObject *parent = nullptr); void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override; void setVisibleInterval(qint64 interval); qint64 visibleInterval() const { return m_visibleInterval; } void setVisibleOffset(qint64 offset); qint64 visibleOffset() const { return m_visibleOffset; } qint64 totalInterval() const { return m_totalInterval; } void setActive(bool active); bool isActive() const; QString toolTipAt(const QModelIndex &index, int position, int width); signals: void visibleIntervalChanged(qint64 value); void visibleOffsetChanged(qint64 value); void isActiveChanged(bool value); void totalIntervalChanged(); private slots: void onUpdateTimeout(); void onServerClockChanged(qlonglong msecs); private: QTimer * const m_updateTimer; qint64 m_visibleOffset; qint64 m_visibleInterval; qint64 m_totalInterval; }; } // namespace GammaRay #endif // GAMMARAY_SIGNALHISTORYDELEGATE_H gammaray-2.11.3/plugins/signalmonitor/signalhistorymodel.cpp000066400000000000000000000203711413310443000243620ustar00rootroot00000000000000/* signalhistorymodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Mathias Hasselmann Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "signalhistorymodel.h" #include "relativeclock.h" #include "signalmonitorcommon.h" #include #include #include #include #include #include #include #include using namespace GammaRay; /// Tries to reuse an already existing instances of @p str by checking /// a global string pool. If no instance of @p str is interned yet the /// string will be added to the pool. template static T internString(const T &str) { static QSet pool; // Check if the pool already contains the string... auto it = pool.find(str); // ...and return it if possible. if (it != pool.end()) return *it; // Otherwise add the string to the pool. pool.insert(str); return str; } static SignalHistoryModel *s_historyModel = nullptr; static void signal_begin_callback(QObject *caller, int method_index, void **argv) { Q_UNUSED(argv); if (s_historyModel) { const int signalIndex = method_index + 1; // offset 1, so unknown signals end up at 0 static const QMetaMethod m = s_historyModel->metaObject()->method( s_historyModel->metaObject()->indexOfMethod("onSignalEmitted(QObject*,int)")); Q_ASSERT(m.isValid()); m.invoke(s_historyModel, Qt::AutoConnection, Q_ARG(QObject*, caller), Q_ARG(int, signalIndex)); } } SignalHistoryModel::SignalHistoryModel(Probe *probe, QObject *parent) : QAbstractTableModel(parent) { connect(probe, &Probe::objectCreated, this, &SignalHistoryModel::onObjectAdded); connect(probe, &Probe::objectDestroyed, this, &SignalHistoryModel::onObjectRemoved); SignalSpyCallbackSet spy; spy.signalBeginCallback = signal_begin_callback; probe->registerSignalSpyCallbackSet(spy); s_historyModel = this; } SignalHistoryModel::~SignalHistoryModel() { s_historyModel = nullptr; qDeleteAll(m_tracedObjects); } int SignalHistoryModel::rowCount(const QModelIndex &parent) const { if (parent.isValid()) return 0; return m_tracedObjects.size(); } int SignalHistoryModel::columnCount(const QModelIndex &) const { return 3; } SignalHistoryModel::Item *SignalHistoryModel::item(const QModelIndex &index) const { if (!index.isValid()) return nullptr; return m_tracedObjects.at(index.row()); } QVariant SignalHistoryModel::data(const QModelIndex &index, int role) const { switch (static_cast(index.column())) { case ObjectColumn: if (role == Qt::DisplayRole) return item(index)->objectName; if (role == Qt::ToolTipRole) return tr("Address: %1").arg(Util::addressToString(item(index)->object)); if (role == ObjectModel::DecorationIdRole) return item(index)->decorationId; if (role == ObjectModel::ObjectIdRole && item(index)->object) return QVariant::fromValue(ObjectId(item(index)->object)); break; case TypeColumn: if (role == Qt::DisplayRole) return item(index)->objectType; break; case EventColumn: if (role == EventsRole) return QVariant::fromValue(item(index)->events); if (role == StartTimeRole) return item(index)->startTime; if (role == EndTimeRole) return item(index)->endTime(); if (role == SignalMapRole) return QVariant::fromValue(item(index)->signalNames); break; } return QVariant(); } QVariant SignalHistoryModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role == Qt::DisplayRole && orientation == Qt::Horizontal) { switch (section) { case ObjectColumn: return tr("Object"); case TypeColumn: return tr("Type"); case EventColumn: return tr("Events"); } } return QVariant(); } QMap< int, QVariant > SignalHistoryModel::itemData(const QModelIndex &index) const { QMap d = QAbstractItemModel::itemData(index); d.insert(EventsRole, data(index, EventsRole)); d.insert(StartTimeRole, data(index, StartTimeRole)); d.insert(EndTimeRole, data(index, EndTimeRole)); d.insert(SignalMapRole, data(index, SignalMapRole)); d.insert(ObjectModel::ObjectIdRole, data(index, ObjectModel::ObjectIdRole)); d.insert(ObjectModel::DecorationIdRole, data(index, ObjectModel::DecorationIdRole)); return d; } void SignalHistoryModel::onObjectAdded(QObject *object) { Q_ASSERT(thread() == QThread::currentThread()); // blacklist event dispatchers if (qstrncmp(object->metaObject()->className(), "QPAEventDispatcher", 18) == 0 || qstrncmp(object->metaObject()->className(), "QGuiEventDispatcher", 19) == 0 || qstrncmp(object->metaObject()->className(), "QEventDispatcher", 16) == 0) return; beginInsertRows(QModelIndex(), m_tracedObjects.size(), m_tracedObjects.size()); auto * const data = new Item(object); m_itemIndex.insert(object, m_tracedObjects.size()); m_tracedObjects.push_back(data); endInsertRows(); } void SignalHistoryModel::onObjectRemoved(QObject *object) { Q_ASSERT(thread() == QThread::currentThread()); const auto it = m_itemIndex.find(object); if (it == m_itemIndex.end()) return; const int itemIndex = *it; m_itemIndex.erase(it); Item *data = m_tracedObjects.at(itemIndex); Q_ASSERT(data->object == object); data->object = nullptr; emit dataChanged(index(itemIndex, ObjectColumn), index(itemIndex, ObjectColumn)); // for ObjectIdRole emit dataChanged(index(itemIndex, EventColumn), index(itemIndex, EventColumn)); } void SignalHistoryModel::onSignalEmitted(QObject *sender, int signalIndex) { Q_ASSERT(thread() == QThread::currentThread()); const qint64 timestamp = RelativeClock::sinceAppStart()->mSecs(); const auto it = m_itemIndex.constFind(sender); if (it == m_itemIndex.constEnd()) return; const int itemIndex = *it; Item *data = m_tracedObjects.at(itemIndex); Q_ASSERT(data->object == sender); // ensure the item is known if (signalIndex > 0 && !data->signalNames.contains(signalIndex)) { // protect dereferencing of sender here QMutexLocker lock(Probe::objectLock()); if (!Probe::instance()->isValidObject(sender)) return; const QByteArray signalName = sender->metaObject()->method(signalIndex - 1).methodSignature(); data->signalNames.insert(signalIndex, internString(signalName)); } data->events.push_back((timestamp << 16) | signalIndex); emit dataChanged(index(itemIndex, EventColumn), index(itemIndex, EventColumn)); } SignalHistoryModel::Item::Item(QObject *obj) : object(obj) , startTime(RelativeClock::sinceAppStart()->mSecs()) { objectName = Util::shortDisplayString(object); objectType = internString(QByteArray(obj->metaObject()->className())); decorationId = Util::iconIdForObject(object); } qint64 SignalHistoryModel::Item::endTime() const { if (object) return -1; // still alive if (!events.isEmpty()) return timestamp(events.size() - 1); return startTime; } gammaray-2.11.3/plugins/signalmonitor/signalhistorymodel.h000066400000000000000000000064121413310443000240270ustar00rootroot00000000000000/* signalhistorymodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Mathias Hasselmann Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_SIGNALHISTORYMODEL_H #define GAMMARAY_SIGNALHISTORYMODEL_H #include #include #include #include #include #include namespace GammaRay { class Probe; class SignalHistoryModel : public QAbstractTableModel { Q_OBJECT private: struct Item { Item(QObject *obj); QObject *object; // never dereference, might be invalid! QHash signalNames; QString objectName; QByteArray objectType; int decorationId; QVector events; const qint64 startTime; // FIXME: make them all methods qint64 endTime() const; qint64 timestamp(int i) const { return SignalHistoryModel::timestamp(events.at(i)); } int signalIndex(int i) const { return SignalHistoryModel::signalIndex(events.at(i)); } }; public: enum ColumnId { ObjectColumn, TypeColumn, EventColumn }; enum RoleId { EventsRole = ObjectModel::UserRole + 1, StartTimeRole, EndTimeRole, SignalMapRole }; explicit SignalHistoryModel(Probe *probe, QObject *parent = nullptr); ~SignalHistoryModel() override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; QMap itemData(const QModelIndex &index) const override; static qint64 timestamp(qint64 ev) { return ev >> 16; } static int signalIndex(qint64 ev) { return ev & 0xffff; } private: Item *item(const QModelIndex &index) const; private slots: void onObjectAdded(QObject *object); void onObjectRemoved(QObject *object); void onSignalEmitted(QObject *sender, int signalIndex); private: QVector m_tracedObjects; QHash m_itemIndex; }; } // namespace GammaRay #endif // GAMMARAY_SIGNALHISTORYMODEL_H gammaray-2.11.3/plugins/signalmonitor/signalhistoryview.cpp000066400000000000000000000110521413310443000242300ustar00rootroot00000000000000/* signalhistoryview.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Mathias Hasselmann Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "signalhistoryview.h" #include "signalhistorydelegate.h" #include "signalhistorymodel.h" #include #include #include using namespace GammaRay; SignalHistoryView::SignalHistoryView(QWidget *parent) : DeferredTreeView(parent) , m_eventDelegate(new SignalHistoryDelegate(this)) , m_eventScrollBar(nullptr) { setDeferredResizeMode(0, QHeaderView::Interactive); setDeferredResizeMode(1, QHeaderView::Interactive); setDeferredResizeMode(2, QHeaderView::Stretch); setItemDelegateForColumn(SignalHistoryModel::EventColumn, m_eventDelegate); connect(m_eventDelegate, &SignalHistoryDelegate::visibleOffsetChanged, this, &SignalHistoryView::eventDelegateChanged); connect(m_eventDelegate, &SignalHistoryDelegate::visibleIntervalChanged, this, &SignalHistoryView::eventDelegateChanged); connect(m_eventDelegate, &SignalHistoryDelegate::totalIntervalChanged, this, &SignalHistoryView::eventDelegateChanged); } void SignalHistoryView::eventDelegateChanged() { viewport()->update(eventColumnPosition(), 0, eventColumnWidth(), height()); if (m_eventScrollBar) { const bool signalsBlocked = m_eventScrollBar->blockSignals(true); // With 31 bits we cover more than 24 days when counting milliseconds. // That's much more time than this tool can handle. IMHO. m_eventScrollBar->setMaximum( qMax(m_eventDelegate->totalInterval() - m_eventDelegate->visibleInterval(), 0LL)); m_eventScrollBar->setSingleStep(m_eventDelegate->visibleInterval() / 10); m_eventScrollBar->setPageStep(m_eventDelegate->visibleInterval()); if (m_eventDelegate->isActive()) m_eventScrollBar->setValue(m_eventScrollBar->maximum()); m_eventScrollBar->blockSignals(signalsBlocked); } } void SignalHistoryView::setEventScrollBar(QScrollBar *scrollBar) { if (m_eventScrollBar != scrollBar) { if (m_eventScrollBar) disconnect(m_eventScrollBar, nullptr, this, nullptr); m_eventScrollBar = scrollBar; if (m_eventScrollBar) { connect(m_eventScrollBar, &QAbstractSlider::sliderMoved, this, &SignalHistoryView::eventScrollBarSliderMoved); } } } int SignalHistoryView::eventColumnPosition() const { return columnViewportPosition(SignalHistoryModel::EventColumn); } int SignalHistoryView::eventColumnWidth() const { return columnWidth(SignalHistoryModel::EventColumn); } void SignalHistoryView::eventScrollBarSliderMoved(int value) { m_eventDelegate->setActive(false); m_eventDelegate->setVisibleOffset(value); } bool SignalHistoryView::viewportEvent(QEvent *event) { if (event->type() == QEvent::ToolTip) { const QHelpEvent * const help = static_cast(event); const QModelIndex index = indexAt(help->pos()); if (index.isValid() && index.column() == SignalHistoryModel::EventColumn) { const int x0 = help->pos().x() - eventColumnPosition(); const int dx = eventColumnWidth(); const QString &toolTipText = m_eventDelegate->toolTipAt(index, x0, dx); if (!toolTipText.isEmpty()) { QToolTip::showText(help->globalPos(), toolTipText); } else { QToolTip::hideText(); event->ignore(); } return true; } } return DeferredTreeView::viewportEvent(event); } gammaray-2.11.3/plugins/signalmonitor/signalhistoryview.h000066400000000000000000000044061413310443000237020ustar00rootroot00000000000000/* signalhistoryview.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Mathias Hasselmann Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_SIGNALHISTORYVIEW_H #define GAMMARAY_SIGNALHISTORYVIEW_H #include "ui/deferredtreeview.h" namespace GammaRay { class SignalHistoryDelegate; class SignalHistoryView : public DeferredTreeView { Q_OBJECT Q_PROPERTY( QScrollBar *eventScrollBar READ eventScrollBar WRITE setEventScrollBar NOTIFY eventScrollBarChanged) Q_PROPERTY(SignalHistoryDelegate *eventDelegate READ eventDelegate FINAL CONSTANT) public: explicit SignalHistoryView(QWidget *parent = nullptr); void setEventScrollBar(QScrollBar *scrollBar); QScrollBar *eventScrollBar() const { return m_eventScrollBar; } SignalHistoryDelegate *eventDelegate() const { return m_eventDelegate; } int eventColumnPosition() const; int eventColumnWidth() const; signals: void eventScrollBarChanged(QScrollBar *scrollBar); protected: bool viewportEvent(QEvent *event) override; private slots: void eventDelegateChanged(); void eventScrollBarSliderMoved(int value); private: SignalHistoryDelegate * const m_eventDelegate; QScrollBar *m_eventScrollBar; }; } // namespace GammaRay #endif // GAMMARAY_SIGNALHISTORYVIEW_H gammaray-2.11.3/plugins/signalmonitor/signalmonitor.cpp000066400000000000000000000060151413310443000233260ustar00rootroot00000000000000/* signalmonitor.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Mathias Hasselmann Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "signalmonitor.h" #include "signalhistorymodel.h" #include "relativeclock.h" #include "signalmonitorcommon.h" #include #include #include #include #include using namespace GammaRay; SignalMonitor::SignalMonitor(Probe *probe, QObject *parent) : SignalMonitorInterface(parent) { StreamOperators::registerSignalMonitorStreamOperators(); auto *model = new SignalHistoryModel(probe, this); auto proxy = new ServerProxyModel(this); proxy->setDynamicSortFilter(true); proxy->setSourceModel(model); m_objModel = proxy; probe->registerModel(QStringLiteral("com.kdab.GammaRay.SignalHistoryModel"), proxy); m_objSelectionModel = ObjectBroker::selectionModel(proxy); m_clock = new QTimer(this); m_clock->setInterval(1000/25); // update frequency of the delegate, we could slow this down a lot, and let the client interpolate, if necessary m_clock->setSingleShot(false); connect(m_clock, &QTimer::timeout, this, &SignalMonitor::timeout); connect(probe, &Probe::objectSelected, this, &SignalMonitor::objectSelected); } SignalMonitor::~SignalMonitor() = default; void SignalMonitor::timeout() { emit clock(RelativeClock::sinceAppStart()->mSecs()); } void SignalMonitor::sendClockUpdates(bool enabled) { if (enabled) m_clock->start(); else m_clock->stop(); } void SignalMonitor::objectSelected(QObject* obj) { const auto indexList = m_objModel->match(m_objModel->index(0, 0), ObjectModel::ObjectIdRole, QVariant::fromValue(ObjectId(obj)), 1, Qt::MatchExactly | Qt::MatchRecursive | Qt::MatchWrap); if (indexList.isEmpty()) return; const auto index = indexList.first(); m_objSelectionModel->select(index, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); } gammaray-2.11.3/plugins/signalmonitor/signalmonitor.h000066400000000000000000000044031413310443000227720ustar00rootroot00000000000000/* signalmonitor.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Mathias Hasselmann Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_SIGNALMONITOR_H #define GAMMARAY_SIGNALMONITOR_H #include "signalmonitorinterface.h" #include QT_BEGIN_NAMESPACE class QAbstractItemModel; class QItemSelectionModel; class QTimer; QT_END_NAMESPACE namespace GammaRay { class SignalMonitor : public SignalMonitorInterface { Q_OBJECT Q_INTERFACES(GammaRay::SignalMonitorInterface) public: explicit SignalMonitor(Probe *probe, QObject *parent = nullptr); ~SignalMonitor() override; public slots: void sendClockUpdates(bool enabled) override; private slots: void timeout(); void objectSelected(QObject *obj); private: QTimer *m_clock; QAbstractItemModel *m_objModel; QItemSelectionModel *m_objSelectionModel; }; class SignalMonitorFactory : public QObject, public StandardToolFactory { Q_OBJECT Q_INTERFACES(GammaRay::ToolFactory) Q_PLUGIN_METADATA(IID "com.kdab.GammaRay.ToolFactory" FILE "gammaray_signalmonitor.json") public: explicit SignalMonitorFactory(QObject *parent = nullptr) : QObject(parent) { } }; } // namespace GammaRay #endif // GAMMARAY_SIGNALMONITOR_H gammaray-2.11.3/plugins/signalmonitor/signalmonitorclient.cpp000066400000000000000000000031201413310443000245170ustar00rootroot00000000000000/* signalmonitorclient.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "signalmonitorclient.h" #include using namespace GammaRay; SignalMonitorClient::SignalMonitorClient(QObject *parent) : SignalMonitorInterface(parent) { } SignalMonitorClient::~SignalMonitorClient() = default; void SignalMonitorClient::sendClockUpdates(bool enabled) { Endpoint::instance()->invokeObject(objectName(), "sendClockUpdates", QVariantList() << QVariant::fromValue(enabled)); } gammaray-2.11.3/plugins/signalmonitor/signalmonitorclient.h000066400000000000000000000031301413310443000241650ustar00rootroot00000000000000/* signalmonitorclient.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_SIGNALMONITORCLIENT_H #define GAMMARAY_SIGNALMONITORCLIENT_H #include "signalmonitorinterface.h" namespace GammaRay { class SignalMonitorClient : public SignalMonitorInterface { Q_OBJECT Q_INTERFACES(GammaRay::SignalMonitorInterface) public: explicit SignalMonitorClient(QObject *parent = nullptr); ~SignalMonitorClient() override; public slots: void sendClockUpdates(bool enabled) override; }; } #endif // GAMMARAY_SIGNALMONITORCLIENT_H gammaray-2.11.3/plugins/signalmonitor/signalmonitorcommon.cpp000066400000000000000000000025301413310443000245350ustar00rootroot00000000000000/* signalmonitorcommon.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "signalmonitorcommon.h" #include using namespace GammaRay; void GammaRay::StreamOperators::registerSignalMonitorStreamOperators() { qRegisterMetaTypeStreamOperators >(); } gammaray-2.11.3/plugins/signalmonitor/signalmonitorcommon.h000066400000000000000000000026441413310443000242100ustar00rootroot00000000000000/* signalmonitorcommon.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_SIGNALMONITORCOMMON_H #define GAMMARAY_SIGNALMONITORCOMMON_H #include #include #include #include namespace GammaRay { namespace StreamOperators { void registerSignalMonitorStreamOperators(); } } #endif // GAMMARAY_SIGNALMONITORCOMMON_H gammaray-2.11.3/plugins/signalmonitor/signalmonitorinterface.cpp000066400000000000000000000026721413310443000252140ustar00rootroot00000000000000/* signalmonitorinterface.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "signalmonitorinterface.h" #include using namespace GammaRay; SignalMonitorInterface::SignalMonitorInterface(QObject *parent) : QObject(parent) { ObjectBroker::registerObject(this); } SignalMonitorInterface::~SignalMonitorInterface() = default; gammaray-2.11.3/plugins/signalmonitor/signalmonitorinterface.h000066400000000000000000000033471413310443000246610ustar00rootroot00000000000000/* signalmonitorinterface.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_SIGNALMONITORINTERFACE_H #define GAMMARAY_SIGNALMONITORINTERFACE_H #include namespace GammaRay { class SignalMonitorInterface : public QObject { Q_OBJECT public: explicit SignalMonitorInterface(QObject *parent = nullptr); ~SignalMonitorInterface() override; public slots: virtual void sendClockUpdates(bool enabled) = 0; signals: void clock(qlonglong msecs); }; } QT_BEGIN_NAMESPACE Q_DECLARE_INTERFACE(GammaRay::SignalMonitorInterface, "com.kdab.GammaRay.SignalMonitorInterface/1.0") QT_END_NAMESPACE #endif // GAMMARAY_SIGNALMONITORINTERFACE_H gammaray-2.11.3/plugins/signalmonitor/signalmonitorwidget.cpp000066400000000000000000000135121413310443000245320ustar00rootroot00000000000000/* signalmonitorwidget.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Mathias Hasselmann Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "signalmonitorwidget.h" #include "ui_signalmonitorwidget.h" #include "signalhistorydelegate.h" #include "signalhistorymodel.h" #include "signalmonitorclient.h" #include "signalmonitorcommon.h" #include #include #include #include #include #include using namespace GammaRay; static QObject *signalMonitorClientFactory(const QString &, QObject *parent) { return new SignalMonitorClient(parent); } SignalMonitorWidget::SignalMonitorWidget(QWidget *parent) : QWidget(parent) , ui(new Ui::SignalMonitorWidget) , m_stateManager(this) { StreamOperators::registerSignalMonitorStreamOperators(); ObjectBroker::registerClientObjectFactoryCallback( signalMonitorClientFactory); ui->setupUi(this); ui->pauseButton->setIcon(qApp->style()->standardIcon(QStyle::SP_MediaPause)); QAbstractItemModel * const signalHistory = ObjectBroker::model(QStringLiteral("com.kdab.GammaRay.SignalHistoryModel")); auto *signalHistoryProxyModel = new ClientDecorationIdentityProxyModel(this); signalHistoryProxyModel->setSourceModel(signalHistory); new SearchLineController(ui->objectSearchLine, signalHistoryProxyModel); ui->objectTreeView->header()->setObjectName("objectTreeViewHeader"); ui->objectTreeView->setModel(signalHistoryProxyModel); ui->objectTreeView->setEventScrollBar(ui->eventScrollBar); connect(ui->objectTreeView, &QWidget::customContextMenuRequested, this, &SignalMonitorWidget::contextMenu); auto selectionModel = ObjectBroker::selectionModel(signalHistoryProxyModel); ui->objectTreeView->setSelectionModel(selectionModel); connect(selectionModel, &QItemSelectionModel::selectionChanged, this, &SignalMonitorWidget::selectionChanged); connect(ui->pauseButton, &QAbstractButton::toggled, this, &SignalMonitorWidget::pauseAndResume); connect(ui->intervalScale, &QAbstractSlider::valueChanged, this, &SignalMonitorWidget::intervalScaleValueChanged); connect(ui->objectTreeView->eventDelegate(), &SignalHistoryDelegate::isActiveChanged, this, &SignalMonitorWidget::eventDelegateIsActiveChanged); connect(ui->objectTreeView->header(), &QHeaderView::sectionResized, this, &SignalMonitorWidget::adjustEventScrollBarSize); m_stateManager.setDefaultSizes(ui->objectTreeView->header(), UISizeVector() << 200 << 200 << -1); } SignalMonitorWidget::~SignalMonitorWidget() = default; void SignalMonitorWidget::intervalScaleValueChanged(int value) { // FIXME: Define a more reasonable formula. qint64 i = 5000 / std::pow(1.07, value); ui->objectTreeView->eventDelegate()->setVisibleInterval(i); } void SignalMonitorWidget::adjustEventScrollBarSize() { // FIXME: Would like to have this in SignalHistoryView, but letting that // widget manage layouts of this widget would be nasty. Still I also I don't // feel like hooking a custom scrollbar into QTreeView. Sleeping between a // rock and a hard place. const QWidget * const scrollBar = ui->objectTreeView->verticalScrollBar(); const QWidget * const viewport = ui->objectTreeView->viewport(); const int eventColumnLeft = ui->objectTreeView->eventColumnPosition(); const int scrollBarLeft = scrollBar->mapTo(this, scrollBar->pos()).x(); const int viewportLeft = viewport->mapTo(this, viewport->pos()).x(); const int viewportRight = viewportLeft + viewport->width(); ui->eventScrollBarLayout->setContentsMargins(eventColumnLeft, scrollBarLeft - viewportRight, width() - viewportRight, 0); } void SignalMonitorWidget::pauseAndResume(bool pause) { ui->objectTreeView->eventDelegate()->setActive(!pause); } void SignalMonitorWidget::eventDelegateIsActiveChanged(bool active) { ui->pauseButton->setChecked(!active); } void SignalMonitorWidget::contextMenu(QPoint pos) { auto index = ui->objectTreeView->indexAt(pos); if (!index.isValid()) return; index = index.sibling(index.row(), 0); const auto objectId = index.data(ObjectModel::ObjectIdRole).value(); if (objectId.isNull()) return; QMenu menu; ContextMenuExtension ext(objectId); ext.populateMenu(&menu); menu.exec(ui->objectTreeView->viewport()->mapToGlobal(pos)); } void SignalMonitorWidget::selectionChanged(const QItemSelection& selection) { if (selection.isEmpty()) return; const auto idx = selection.at(0).topLeft(); ui->objectTreeView->scrollTo(idx); } gammaray-2.11.3/plugins/signalmonitor/signalmonitorwidget.h000066400000000000000000000044601413310443000242010ustar00rootroot00000000000000/* signalmonitorwidget.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Mathias Hasselmann Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_SIGNALMONITORWIDGET_H #define GAMMARAY_SIGNALMONITORWIDGET_H #include #include "ui/tooluifactory.h" #include QT_BEGIN_NAMESPACE class QItemSelection; QT_END_NAMESPACE namespace GammaRay { namespace Ui { class SignalMonitorWidget; } class SignalMonitorWidget : public QWidget { Q_OBJECT public: explicit SignalMonitorWidget(QWidget *parent = nullptr); ~SignalMonitorWidget() override; private slots: void intervalScaleValueChanged(int value); void adjustEventScrollBarSize(); void pauseAndResume(bool pause); void eventDelegateIsActiveChanged(bool active); void contextMenu(QPoint pos); void selectionChanged(const QItemSelection &selection); private: static const QString ITEM_TYPE_NAME_OBJECT; QScopedPointer ui; UIStateManager m_stateManager; }; class SignalMonitorUiFactory : public QObject, public StandardToolUiFactory { Q_OBJECT Q_INTERFACES(GammaRay::ToolUiFactory) Q_PLUGIN_METADATA(IID "com.kdab.GammaRay.ToolUiFactory" FILE "gammaray_signalmonitor.json") }; } // namespace GammaRay #endif // GAMMARAY_SIGNALMONITORWIDGET_H gammaray-2.11.3/plugins/signalmonitor/signalmonitorwidget.ui000066400000000000000000000062771413310443000243770ustar00rootroot00000000000000 GammaRay::SignalMonitorWidget 0 0 400 300 0 6 Pause true Qt::Horizontal 40 20 Zoom Level: -100 100 0 Qt::Horizontal Qt::CustomContextMenu Qt::ScrollBarAlwaysOff QAbstractItemView::SingleSelection false true 0 0 true Qt::Horizontal GammaRay::SignalHistoryView QTreeView
    signalhistoryview.h
    gammaray-2.11.3/plugins/statemachineviewer/000077500000000000000000000000001413310443000207355ustar00rootroot00000000000000gammaray-2.11.3/plugins/statemachineviewer/CMakeLists.txt000066400000000000000000000033321413310443000234760ustar00rootroot00000000000000# shared part set(gammaray_statemachineviewer_shared_srcs statemachineviewerinterface.cpp ) # probe part if (NOT GAMMARAY_CLIENT_ONLY_BUILD) set(gammaray_statemachineviewer_plugin_srcs statemachineviewerserver.cpp transitionmodel.cpp statemodel.cpp statemachinewatcher.cpp statemachinedebuginterface.cpp qsmstatemachinedebuginterface.cpp ) if(Qt5Scxml_FOUND) list(APPEND gammaray_statemachineviewer_plugin_srcs qscxmlstatemachinedebuginterface.cpp) endif() gammaray_add_plugin(gammaray_statemachineviewer_plugin JSON gammaray_statemachineviewer.json SOURCES ${gammaray_statemachineviewer_plugin_srcs} ${gammaray_statemachineviewer_shared_srcs} ) target_link_libraries(gammaray_statemachineviewer_plugin gammaray_core ) if(Qt5Scxml_FOUND) target_link_libraries(gammaray_statemachineviewer_plugin Qt5::Scxml) target_include_directories(gammaray_statemachineviewer_plugin SYSTEM PRIVATE ${Qt5Scxml_PRIVATE_INCLUDE_DIRS}) endif() endif() # UI part - dependencies set_package_properties(KDSME PROPERTIES URL "https://github.com/KDAB/KDStateMachineEditor" DESCRIPTION "KDAB State Machine Editor framework" TYPE RECOMMENDED PURPOSE "Graphical state machine debugging.") find_package(KDSME 1.2 CONFIG QUIET) # UI part if(GAMMARAY_BUILD_UI AND KDSME_FOUND) set(gammaray_statemachineviewer_ui_plugin_srcs statemachineviewerwidget.cpp statemachineviewerclient.cpp statemodeldelegate.cpp ) gammaray_add_plugin(gammaray_statemachineviewer_ui_plugin JSON gammaray_statemachineviewer.json SOURCES ${gammaray_statemachineviewer_ui_plugin_srcs} ${gammaray_statemachineviewer_shared_srcs} ) target_link_libraries(gammaray_statemachineviewer_ui_plugin KDSME::Core KDSME::View gammaray_common gammaray_ui ) endif() gammaray-2.11.3/plugins/statemachineviewer/gammaray_statemachineviewer.json000066400000000000000000000003651413310443000274010ustar00rootroot00000000000000{ "id": "gammaray_statemachineviewer", "name": "State Machines", "name[de]": "Zustandsautomaten", "selectableTypes": [ "QAbstractState" ], "types": [ "QAbstractState", "QScxmlStateMachine" ] } gammaray-2.11.3/plugins/statemachineviewer/qscxmlstatemachinedebuginterface.cpp000066400000000000000000000213561413310443000302350ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Jan Arne Petersen Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "qscxmlstatemachinedebuginterface.h" #include #include namespace GammaRay { static QScxmlStateMachineInfo::StateId fromState(State state) { return static_cast(state); } static State toState(QScxmlStateMachineInfo::StateId state) { return State(static_cast(state)); } static QScxmlStateMachineInfo::TransitionId fromTransition(Transition transition) { return static_cast(transition); } static Transition toTransition(QScxmlStateMachineInfo::TransitionId transition) { return Transition(static_cast(transition)); } QScxmlStateMachineDebugInterface::QScxmlStateMachineDebugInterface(QScxmlStateMachine *stateMachine, QObject *parent) : StateMachineDebugInterface(parent) , m_stateMachine(stateMachine) , m_info(new QScxmlStateMachineInfo(stateMachine)) { connect(stateMachine, SIGNAL(runningChanged(bool)), this, SIGNAL(runningChanged(bool))); connect(stateMachine, SIGNAL(log(QString,QString)), this, SIGNAL(logMessage(QString,QString))); connect(m_info.data(), SIGNAL(statesEntered(QVector)), this, SLOT(statesEntered(QVector))); connect(m_info.data(), SIGNAL(statesExited(QVector)), this, SLOT(statesExited(QVector))); connect(m_info.data(), SIGNAL(transitionsTriggered(QVector)), this, SLOT(transitionsTriggered(QVector))); } QScxmlStateMachineDebugInterface::~QScxmlStateMachineDebugInterface() { delete m_info; } bool QScxmlStateMachineDebugInterface::isRunning() const { return m_stateMachine->isRunning(); } void QScxmlStateMachineDebugInterface::start() { m_stateMachine->start(); } void QScxmlStateMachineDebugInterface::stop() { m_stateMachine->stop(); } QVector QScxmlStateMachineDebugInterface::configuration() const { const QVector configuration = m_info->configuration(); QVector result; result.reserve(configuration.size()); for (auto stateId : configuration) result.append(toState(stateId)); std::sort(result.begin(), result.end()); return result; } State QScxmlStateMachineDebugInterface::rootState() const { return toState(QScxmlStateMachineInfo::InvalidStateId); } QVector QScxmlStateMachineDebugInterface::stateChildren(State state) const { const QVector children = m_info->stateChildren(fromState(state)); QVector result; result.reserve(children.size()); for (auto stateId : children) result.append(toState(stateId)); return result; } State QScxmlStateMachineDebugInterface::parentState(State state) const { return toState(m_info->stateParent(fromState(state))); } bool QScxmlStateMachineDebugInterface::isInitialState(State state) const { const auto stateId = fromState(state); const auto parentState = m_info->stateParent(state); const auto parentInitialTransition = m_info->initialTransition(parentState); if (parentInitialTransition == QScxmlStateMachineInfo::InvalidTransitionId) return false; return m_info->transitionTargets(parentInitialTransition).contains(stateId); } QString QScxmlStateMachineDebugInterface::transitions(State state) const { Q_UNUSED(state); return QString(); // FIXME } QString QScxmlStateMachineDebugInterface::stateLabel(State state) const { auto stateId = fromState(state); if (stateId == QScxmlStateMachineInfo::InvalidStateId) return m_stateMachine->name(); return QStringLiteral("%1 (%2)").arg(m_info->stateName(stateId)).arg(stateId); } QString QScxmlStateMachineDebugInterface::stateDisplay(State state) const { return stateLabel(state); } QString QScxmlStateMachineDebugInterface::stateDisplayType(State state) const { switch (m_info->stateType(fromState(state))) { case QScxmlStateMachineInfo::InvalidState: return QStringLiteral("StateMachineState"); case QScxmlStateMachineInfo::NormalState: return QStringLiteral("NormalState"); case QScxmlStateMachineInfo::ParallelState: return QStringLiteral("ParallelState"); case QScxmlStateMachineInfo::FinalState: return QStringLiteral("FinalState"); case QScxmlStateMachineInfo::ShallowHistoryState: return QStringLiteral("ShallowHistoryState"); case QScxmlStateMachineInfo::DeepHistoryState: return QStringLiteral("DeepHistoryState"); } return QString(); } StateType QScxmlStateMachineDebugInterface::stateType(State state) const { switch (m_info->stateType(fromState(state))) { case QScxmlStateMachineInfo::InvalidState: return StateMachineState; case QScxmlStateMachineInfo::NormalState: return OtherState; case QScxmlStateMachineInfo::ParallelState: return OtherState; // FIXME: No ParallelState. Bug. case QScxmlStateMachineInfo::FinalState: return FinalState; case QScxmlStateMachineInfo::ShallowHistoryState: return ShallowHistoryState; case QScxmlStateMachineInfo::DeepHistoryState: return DeepHistoryState; } return OtherState; } QVector QScxmlStateMachineDebugInterface::stateTransitions(State state) const { const auto stateId = fromState(state); const auto transitions = m_info->allTransitions(); const auto initialTransition = m_info->initialTransition(stateId); QVector result; for (auto transition : transitions) { if (transition != initialTransition && m_info->transitionSource(transition) == stateId) result.append(toTransition(transition)); } return result; } QString QScxmlStateMachineDebugInterface::transitionLabel(Transition transition) const { auto transitionId = fromTransition(transition); if (transitionId == QScxmlStateMachineInfo::InvalidTransitionId) { return QString(); } auto events = m_info->transitionEvents(transition); if (events.empty()) { return QString(); } return QStringLiteral("%1 (%2)").arg(events.first()).arg(transition); } State QScxmlStateMachineDebugInterface::transitionSource(Transition transition) const { return toState(m_info->transitionSource(fromTransition(transition))); } QVector QScxmlStateMachineDebugInterface::transitionTargets(Transition transition) const { const auto targets = m_info->transitionTargets(fromTransition(transition)); QVector result; result.reserve(targets.size()); for (auto stateId : targets) result.append(toState(stateId)); return result; } void QScxmlStateMachineDebugInterface::statesEntered(const QVector &states) { for (auto state : states) { emit stateEntered(toState(state)); } } void QScxmlStateMachineDebugInterface::statesExited(const QVector &states) { for (auto state : states) { emit stateExited(toState(state)); } } void QScxmlStateMachineDebugInterface::transitionsTriggered(const QVector &transitions) { for (auto transition : transitions) { emit transitionTriggered(toTransition(transition)); } } bool QScxmlStateMachineDebugInterface::stateValid(State state) const { Q_UNUSED(state); return true; } QObject *QScxmlStateMachineDebugInterface::stateObject(State state) const { Q_UNUSED(state); return m_stateMachine; } } gammaray-2.11.3/plugins/statemachineviewer/qscxmlstatemachinedebuginterface.h000066400000000000000000000060471413310443000277020ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Jan Arne Petersen Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_STATEMACHINEVIEWER_QSCXMLSTATEMACHINEDEBUGINTERFACE_H #define GAMMARAY_STATEMACHINEVIEWER_QSCXMLSTATEMACHINEDEBUGINTERFACE_H #include "statemachinedebuginterface.h" #include #include QT_BEGIN_NAMESPACE class QScxmlStateMachine; class QScxmlStateMachineInfo; QT_END_NAMESPACE namespace GammaRay { class QScxmlStateMachineDebugInterface: public StateMachineDebugInterface { Q_OBJECT public: explicit QScxmlStateMachineDebugInterface(QScxmlStateMachine *stateMachine, QObject *parent = nullptr); ~QScxmlStateMachineDebugInterface(); bool isRunning() const override; void start() override; void stop() override; QVector configuration() const override; State rootState() const override; bool stateValid(State state) const override; QVector stateChildren(State state) const override; State parentState(State state) const override; bool isInitialState(State state) const override; QString transitions(State state) const override; QString stateLabel(State state) const override; QString stateDisplay(State state) const override; QString stateDisplayType(State state) const override; StateType stateType(State state) const override; QObject *stateObject(State state) const override; QVector stateTransitions(State state) const override; QString transitionLabel(Transition transition) const override; State transitionSource(Transition transition) const override; QVector transitionTargets(Transition transition) const override; private slots: void statesEntered(const QVector &states); void statesExited(const QVector &states); void transitionsTriggered(const QVector &transitions); private: QScxmlStateMachine *m_stateMachine; QPointer m_info; }; } #endif gammaray-2.11.3/plugins/statemachineviewer/qsmstatemachinedebuginterface.cpp000066400000000000000000000226441413310443000275270ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Jan Arne Petersen Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "qsmstatemachinedebuginterface.h" #include "statemachinewatcher.h" #include #include #include #include #include #include #include #include #include #include Q_DECLARE_METATYPE(Qt::KeyboardModifiers) namespace GammaRay { static QAbstractState *fromState(State state) { return reinterpret_cast(static_cast(state)); } static State toState(QAbstractState *state = nullptr) { return State(reinterpret_cast(state)); } static QAbstractTransition *fromTransition(Transition transition) { return reinterpret_cast(static_cast(transition)); } static Transition toTransition(QAbstractTransition *transition) { return Transition(reinterpret_cast(transition)); } template static QVector childrenOfType(QObject *parent) { Q_ASSERT(parent); QVector v; foreach (auto obj, parent->children()) { if (auto child = qobject_cast(obj)) v.push_back(child); } return v; } QSMStateMachineDebugInterface::QSMStateMachineDebugInterface(QStateMachine *stateMachine, QObject *parent) : StateMachineDebugInterface(parent) , m_stateMachine(stateMachine) , m_stateMachineWatcher(new StateMachineWatcher(this)) { connect(stateMachine, &QStateMachine::started, this, &QSMStateMachineDebugInterface::updateRunning); connect(stateMachine, &QStateMachine::stopped, this, &QSMStateMachineDebugInterface::updateRunning); connect(stateMachine, &QState::finished, this, &QSMStateMachineDebugInterface::updateRunning); connect(m_stateMachineWatcher, &StateMachineWatcher::stateEntered, this, &StateMachineDebugInterface::stateEntered); connect(m_stateMachineWatcher, &StateMachineWatcher::stateExited, this, &StateMachineDebugInterface::stateExited); connect(m_stateMachineWatcher, &StateMachineWatcher::transitionTriggered, this, &StateMachineDebugInterface::transitionTriggered); m_stateMachineWatcher->setWatchedStateMachine(stateMachine); } QSMStateMachineDebugInterface::~QSMStateMachineDebugInterface() = default; bool QSMStateMachineDebugInterface::isRunning() const { return m_stateMachine->isRunning(); } void QSMStateMachineDebugInterface::start() { m_stateMachine->start(); } void QSMStateMachineDebugInterface::stop() { m_stateMachine->stop(); } QVector QSMStateMachineDebugInterface::configuration() const { const QSet configuration = m_stateMachine->configuration(); QVector result; result.reserve(configuration.size()); for (QAbstractState *state : configuration) { result.append(toState(state)); } std::sort(result.begin(), result.end()); return result; } State QSMStateMachineDebugInterface::rootState() const { return toState(m_stateMachine); } QVector QSMStateMachineDebugInterface::stateChildren(State parentId) const { QAbstractState *parent = fromState(parentId); if (!parent) parent = m_stateMachine; QVector result; foreach (auto state, childrenOfType(parent)) { result.append(toState(state)); } std::sort(result.begin(), result.end()); return result; } State QSMStateMachineDebugInterface::parentState(State stateId) const { QAbstractState *state = fromState(stateId); if (!state) return toState(); return toState(state->parentState()); } bool QSMStateMachineDebugInterface::isInitialState(State stateId) const { QAbstractState *state = fromState(stateId); if (!state) return false; QState *parentState = state->parentState(); return parentState && state == parentState->initialState(); } QString QSMStateMachineDebugInterface::transitions(State stateId) const { QState *state = qobject_cast(fromState(stateId)); if (!state) return QString(); QState *parent = state->parentState() ? state->parentState() : m_stateMachine; const auto l = childrenOfType(parent); QStringList nums; const QList trs = state->transitions(); nums.reserve(trs.size()); for (auto t : trs) { QAbstractState *child = t->targetState(); nums << QString::number(l.indexOf(child) - l.indexOf(state)); } return nums.join(QStringLiteral(",")); } QString QSMStateMachineDebugInterface::stateLabel(State state) const { return Util::displayString(fromState(state)); } StateType QSMStateMachineDebugInterface::stateType(State stateId) const { QAbstractState *state = fromState(stateId); StateType type = OtherState; if (qobject_cast(state)) { type = FinalState; } else if (auto historyState = qobject_cast(state)) { type = historyState->historyType() == QHistoryState::ShallowHistory ? ShallowHistoryState : DeepHistoryState; } else if (qobject_cast(state)) { type = StateMachineState; } return type; } QString QSMStateMachineDebugInterface::stateDisplay(State state) const { return Util::shortDisplayString(fromState(state)); } QString QSMStateMachineDebugInterface::stateDisplayType(State state) const { return ObjectDataProvider::typeName(fromState(state)); } QVector QSMStateMachineDebugInterface::stateTransitions(State state) const { QVector result; foreach (auto object, fromState(state)->children()) { if (auto transition = qobject_cast(object)) result.append(toTransition(transition)); } return result; } QString QSMStateMachineDebugInterface::transitionLabel(Transition t) const { QAbstractTransition *transition = fromTransition(t); const QString objectName = transition->objectName(); if (!objectName.isEmpty()) return objectName; // try to find descriptive labels for built-in transitions if (auto signalTransition = qobject_cast(transition)) { QString str; if (signalTransition->senderObject() != transition->sourceState()) { str += Util::displayString(signalTransition->senderObject()) + "\n / "; } auto signal = signalTransition->signal(); if (signal.startsWith('0' + QSIGNAL_CODE)) { // from QStateMachinePrivate::registerSignalTransition signal.remove(0, 1); } str += signal; return str; } // QKeyEventTransition is in QtWidgets, so this is a bit dirty to avoid a hard dependency else if (transition->inherits("QKeyEventTransition")) { QString s; const auto modifiers = transition->property("modifierMask").value(); if (modifiers != Qt::NoModifier) { const auto modIdx = staticQtMetaObject.indexOfEnumerator("KeyboardModifiers"); if (modIdx < 0) { return Util::displayString(transition); } const auto modEnum = staticQtMetaObject.enumerator(modIdx); s += modEnum.valueToKey(modifiers) + QStringLiteral(" + "); } const auto key = transition->property("key").toInt(); const auto keyIdx = staticQtMetaObject.indexOfEnumerator("Key"); if (keyIdx < 0) { return Util::displayString(transition); } const auto keyEnum = staticQtMetaObject.enumerator(keyIdx); s += keyEnum.valueToKey(key); return s; } return Util::displayString(transition); } State QSMStateMachineDebugInterface::transitionSource(Transition t) const { QAbstractTransition *transition = fromTransition(t); return toState(transition->sourceState()); } QVector QSMStateMachineDebugInterface::transitionTargets(Transition t) const { QAbstractTransition *transition = fromTransition(t); return QVector() << toState(transition->targetState()); } void QSMStateMachineDebugInterface::updateRunning() { emit runningChanged(m_stateMachine->isRunning()); } bool QSMStateMachineDebugInterface::stateValid(State state) const { return fromState(state) != nullptr; } QObject *QSMStateMachineDebugInterface::stateObject(State state) const { return fromState(state); } } gammaray-2.11.3/plugins/statemachineviewer/qsmstatemachinedebuginterface.h000066400000000000000000000054261413310443000271730ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Jan Arne Petersen Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_STATEMACHINEVIEWER_QSMSTATEMACHINEDEBUGINTERFACE_H #define GAMMARAY_STATEMACHINEVIEWER_QSMSTATEMACHINEDEBUGINTERFACE_H #include "statemachinedebuginterface.h" QT_BEGIN_NAMESPACE class QAbstractState; class QAbstractTransition; class QStateMachine; QT_END_NAMESPACE namespace GammaRay { class StateMachineWatcher; class QSMStateMachineDebugInterface : public StateMachineDebugInterface { Q_OBJECT public: explicit QSMStateMachineDebugInterface(QStateMachine *stateMachine, QObject *parent = nullptr); ~QSMStateMachineDebugInterface() override; bool isRunning() const override; void start() override; void stop() override; QVector configuration() const override; State rootState() const override; bool stateValid(State state) const override; QVector stateChildren(State parentId) const override; State parentState(State stateId) const override; bool isInitialState(State stateId) const override; QString transitions(State stateId) const override; QString stateLabel(State state) const override; StateType stateType(State stateId) const override; QObject *stateObject(State state) const override; QVector stateTransitions(State state) const override; QString transitionLabel(Transition t) const override; State transitionSource(Transition t) const override; QVector transitionTargets(Transition t) const override; QString stateDisplay(State state) const override; QString stateDisplayType(State state) const override; private slots: void updateRunning(); private: QStateMachine *m_stateMachine; StateMachineWatcher *m_stateMachineWatcher; }; } #endif gammaray-2.11.3/plugins/statemachineviewer/statemachinedebuginterface.cpp000066400000000000000000000031761413310443000270050ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Jan Arne Petersen Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "statemachinedebuginterface.h" using namespace GammaRay; namespace GammaRay { StateMachineDebugInterface::StateMachineDebugInterface(QObject *parent) : QObject(parent) { } StateMachineDebugInterface::~StateMachineDebugInterface() = default; bool StateMachineDebugInterface::isDescendantOf(State ascendant, State state) const { if (state == rootState()) return false; State parent = parentState(state); if (parent == ascendant) return true; return isDescendantOf(ascendant, parent); } } gammaray-2.11.3/plugins/statemachineviewer/statemachinedebuginterface.h000066400000000000000000000066201413310443000264470ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Jan Arne Petersen Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_STATEMACHINEVIEWER_STATEMACHINEDEBUGINTERFACE_H #define GAMMARAY_STATEMACHINEVIEWER_STATEMACHINEDEBUGINTERFACE_H #include #include "statemachineviewerinterface.h" namespace GammaRay { struct State { explicit State(quintptr id = 0) : m_id(id) {} operator quintptr() const { return m_id; } quintptr m_id; }; struct Transition { explicit Transition(quintptr id = 0) : m_id(id) {} operator quintptr() const { return m_id; } quintptr m_id; }; class StateMachineDebugInterface : public QObject { Q_OBJECT public: explicit StateMachineDebugInterface(QObject *parent = nullptr); ~StateMachineDebugInterface() override; virtual bool isRunning() const = 0; virtual void start() = 0; virtual void stop() = 0; virtual QVector configuration() const = 0; virtual State rootState() const = 0; virtual bool stateValid(State state) const = 0; virtual QVector stateChildren(State state) const = 0; virtual State parentState(State state) const = 0; virtual bool isInitialState(State state) const = 0; virtual QString transitions(State state) const = 0; virtual QString stateLabel(State state) const = 0; virtual QString stateDisplay(State state) const = 0; virtual QString stateDisplayType(State state) const = 0; virtual StateType stateType(State state) const = 0; virtual QVector stateTransitions(State state) const = 0; virtual QObject *stateObject(State state) const = 0; virtual QString transitionLabel(Transition transition) const = 0; virtual State transitionSource(Transition transition) const = 0; virtual QVector transitionTargets(Transition transition) const = 0; bool isDescendantOf(State ascendant, State state) const; signals: void runningChanged(bool running); void stateEntered(State state); void stateExited(State state); void transitionTriggered(Transition transition); void logMessage(const QString &label, const QString &message); }; } Q_DECLARE_METATYPE(GammaRay::State) Q_DECLARE_METATYPE(GammaRay::Transition) QT_BEGIN_NAMESPACE Q_DECLARE_TYPEINFO(GammaRay::State, Q_PRIMITIVE_TYPE); Q_DECLARE_TYPEINFO(GammaRay::Transition, Q_PRIMITIVE_TYPE); QT_END_NAMESPACE #endif gammaray-2.11.3/plugins/statemachineviewer/statemachineviewerclient.cpp000066400000000000000000000033201413310443000265250ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "statemachineviewerclient.h" #include using namespace GammaRay; StateMachineViewerClient::StateMachineViewerClient(QObject *parent) : StateMachineViewerInterface(parent) { } void StateMachineViewerClient::selectStateMachine(int index) { Endpoint::instance()->invokeObject(objectName(), "selectStateMachine", QVariantList() << index); } void StateMachineViewerClient::toggleRunning() { Endpoint::instance()->invokeObject(objectName(), "toggleRunning"); } void StateMachineViewerClient::repopulateGraph() { Endpoint::instance()->invokeObject(objectName(), "repopulateGraph"); } gammaray-2.11.3/plugins/statemachineviewer/statemachineviewerclient.h000066400000000000000000000032371413310443000262010ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_STATEMACHINEVIEWER_STATEMACHINEVIEWERCLIENT_H #define GAMMARAY_STATEMACHINEVIEWER_STATEMACHINEVIEWERCLIENT_H #include "statemachineviewerinterface.h" namespace GammaRay { class StateMachineViewerClient : public StateMachineViewerInterface { Q_OBJECT Q_INTERFACES(GammaRay::StateMachineViewerInterface) public: explicit StateMachineViewerClient(QObject *parent = nullptr); void selectStateMachine(int index) override; void toggleRunning() override; void repopulateGraph() override; }; } #endif // GAMMARAY_STATEMACHINEVIEWERCLIENT_H gammaray-2.11.3/plugins/statemachineviewer/statemachineviewerinterface.cpp000066400000000000000000000034651413310443000272210ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "statemachineviewerinterface.h" #include using namespace GammaRay; StateMachineViewerInterface::StateMachineViewerInterface(QObject *parent) : QObject(parent) { qRegisterMetaType(); qRegisterMetaTypeStreamOperators(); qRegisterMetaType(); qRegisterMetaTypeStreamOperators(); qRegisterMetaType(); qRegisterMetaTypeStreamOperators(); qRegisterMetaType(); qRegisterMetaTypeStreamOperators(); ObjectBroker::registerObject(this); } StateMachineViewerInterface::~StateMachineViewerInterface() = default; gammaray-2.11.3/plugins/statemachineviewer/statemachineviewerinterface.h000066400000000000000000000105311413310443000266560ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_STATEMACHINEVIEWER_STATEMACHINEVIEWERINTERFACE_H #define GAMMARAY_STATEMACHINEVIEWER_STATEMACHINEVIEWERINTERFACE_H #include #include #include #include namespace GammaRay { // note: typedef bring major pain, on the client side i.e. it would always look for // signal/slots with the base type (or actually, the first type which was registered // to the meta type system)... struct TransitionId { explicit TransitionId(quintptr transition = 0) : id(static_cast(transition)) {} operator quint64() const { return id; } quint64 id; }; inline QDataStream &operator<<(QDataStream &out, TransitionId value) { out << value.id; return out; } inline QDataStream &operator>>(QDataStream &in, TransitionId &value) { in >> value.id; return in; } struct StateId { explicit StateId(quintptr state = 0) : id(static_cast(state)) {} operator quint64() const { return id; } quint64 id; }; inline QDataStream &operator<<(QDataStream &out, StateId value) { out << value.id; return out; } inline QDataStream &operator>>(QDataStream &in, StateId &value) { in >> value.id; return in; } enum StateType { OtherState, FinalState, ShallowHistoryState, DeepHistoryState, StateMachineState }; inline QDataStream &operator<<(QDataStream &out, StateType value) { out << int(value); return out; } inline QDataStream &operator>>(QDataStream &in, StateType &value) { int val; in >> val; value = static_cast(val); return in; } using StateMachineConfiguration = QVector; class StateMachineViewerInterface : public QObject { Q_OBJECT public: explicit StateMachineViewerInterface(QObject *parent = nullptr); ~StateMachineViewerInterface() override; public slots: virtual void selectStateMachine(int index) = 0; virtual void toggleRunning() = 0; virtual void repopulateGraph() = 0; signals: void statusChanged(bool haveStateMachine, bool running); void message(const QString &message); void aboutToRepopulateGraph(); void graphRepopulated(); void stateConfigurationChanged(const GammaRay::StateMachineConfiguration &config); void maximumDepthChanged(int depth); void transitionTriggered(GammaRay::TransitionId transition, const QString &label); void stateAdded(GammaRay::StateId state, GammaRay::StateId parent, bool hasChildren, const QString &label, GammaRay::StateType type, bool connectToInitial); void stateEntered(GammaRay::StateId state); void stateExited(GammaRay::StateId state); void transitionAdded(GammaRay::TransitionId state, GammaRay::StateId source, GammaRay::StateId target, const QString &label); }; } Q_DECLARE_METATYPE(GammaRay::StateId) QT_BEGIN_NAMESPACE Q_DECLARE_TYPEINFO(GammaRay::StateId, Q_PRIMITIVE_TYPE); QT_END_NAMESPACE Q_DECLARE_METATYPE(GammaRay::TransitionId) Q_DECLARE_METATYPE(GammaRay::StateMachineConfiguration) Q_DECLARE_METATYPE(GammaRay::StateType) QT_BEGIN_NAMESPACE Q_DECLARE_INTERFACE(GammaRay::StateMachineViewerInterface, "com.kdab.GammaRay.StateMachineViewer") QT_END_NAMESPACE #endif // GAMMARAY_STATEMACHINEVIEWERINTERFACE_H gammaray-2.11.3/plugins/statemachineviewer/statemachineviewerserver.cpp000066400000000000000000000274211413310443000265650ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Kevin Funk Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "statemachineviewerserver.h" #include "qsmstatemachinedebuginterface.h" #ifdef HAVE_QT_SCXML #include "qscxmlstatemachinedebuginterface.h" #endif #include "statemodel.h" #include "statemachinedebuginterface.h" #include "statemachinewatcher.h" #include "transitionmodel.h" #include #include #include #include #include #include #include #ifdef HAVE_QT_SCXML #include #endif #include #include using namespace GammaRay; using namespace std; StateMachineViewerServer::StateMachineViewerServer(Probe *probe, QObject *parent) : StateMachineViewerInterface(parent) , m_stateModel(new StateModel(this)) , m_transitionModel(new TransitionModel(this)) { auto proxyModel = new ServerProxyModel(this); proxyModel->setSourceModel(m_stateModel); proxyModel->addRole(StateModel::StateIdRole); probe->registerModel(QStringLiteral("com.kdab.GammaRay.StateModel"), proxyModel); m_stateSelectionModel = ObjectBroker::selectionModel(proxyModel); connect(m_stateSelectionModel, &QItemSelectionModel::selectionChanged, this, &StateMachineViewerServer::stateSelectionChanged); connect(probe, &Probe::objectSelected, this, &StateMachineViewerServer::objectSelected); #ifdef HAVE_QT_SCXML auto stateMachineFilter = new ObjectTypeFilterProxyModel(this); #else auto stateMachineFilter = new ObjectTypeFilterProxyModel(this); #endif stateMachineFilter->setSourceModel(probe->objectListModel()); m_stateMachinesModel = new ServerProxyModel(this); m_stateMachinesModel->setSourceModel(stateMachineFilter); probe->registerModel(QStringLiteral("com.kdab.GammaRay.StateMachineModel"), m_stateMachinesModel); updateStartStop(); } void StateMachineViewerServer::repopulateGraph() { if (!m_stateModel->stateMachine()) return; emit aboutToRepopulateGraph(); // just to be sure the client has the same setting than we do updateStartStop(); if (m_filteredStates.isEmpty()) { addState(m_stateModel->stateMachine()->rootState()); } else { for (State state : qAsConst(m_filteredStates)) addState(state); } m_recursionGuard.clear(); emit graphRepopulated(); } StateMachineDebugInterface *StateMachineViewerServer::selectedStateMachine() const { return m_stateModel->stateMachine(); } bool StateMachineViewerServer::mayAddState(State state) { if (!selectedStateMachine()->stateValid(state)) return false; if (m_recursionGuard.contains(state)) return false; for (State filter : qAsConst(m_filteredStates)) { if (filter == state || selectedStateMachine()->isDescendantOf(filter, state)) { return true; } } return m_filteredStates.isEmpty(); } void StateMachineViewerServer::setFilteredStates(const QVector &states) { if (m_filteredStates == states) return; if (states.isEmpty()) { emit message(tr("Clearing filter.")); } else { QStringList stateNames; stateNames.reserve(states.size()); for (State state : states) stateNames << selectedStateMachine()->stateLabel(state); emit message(tr("Setting filter on: %1").arg(stateNames.join(QStringLiteral(", ")))); } m_filteredStates = states; } void StateMachineViewerServer::setSelectedStateMachine(StateMachineDebugInterface *machine) { StateMachineDebugInterface *oldMachine = selectedStateMachine(); if (oldMachine == machine) return; if (oldMachine) { oldMachine->disconnect(this); } m_stateModel->setStateMachine(machine); setFilteredStates(QVector()); repopulateGraph(); stateConfigurationChanged(); if (machine) { machine->setParent(this); connect(machine, &StateMachineDebugInterface::runningChanged, this, &StateMachineViewerServer::updateStartStop); connect(machine, &StateMachineDebugInterface::stateEntered, this, &StateMachineViewerServer::stateEntered); connect(machine, &StateMachineDebugInterface::stateExited, this, &StateMachineViewerServer::stateExited); connect(machine, &StateMachineDebugInterface::transitionTriggered, this, &StateMachineViewerServer::handleTransitionTriggered); connect(machine, &StateMachineDebugInterface::logMessage, this, &StateMachineViewerServer::handleLogMessage); } updateStartStop(); delete oldMachine; } void StateMachineViewerServer::selectStateMachine(int row) { Q_ASSERT(m_stateMachinesModel); const auto index = m_stateMachinesModel->index(row, 0); if (!index.isValid()) { setSelectedStateMachine(nullptr); return; } QObject *stateMachineObject = index.data(ObjectModel::ObjectRole).value(); QStateMachine *machine = qobject_cast(stateMachineObject); if (machine) { setSelectedStateMachine(new QSMStateMachineDebugInterface(machine, this)); return; } #ifdef HAVE_QT_SCXML QScxmlStateMachine *qscxmlMachine = qobject_cast(stateMachineObject); if (qscxmlMachine) { setSelectedStateMachine(new QScxmlStateMachineDebugInterface(qscxmlMachine, this)); return; } #endif setSelectedStateMachine(nullptr); } void StateMachineViewerServer::stateSelectionChanged() { const QModelIndexList &selection = m_stateSelectionModel->selectedRows(); qDebug() << selection; QVector filter; filter.reserve(selection.size()); for (const QModelIndex &index : selection) { State state = index.data(StateModel::StateValueRole).value(); bool addState = true; /// only pick the top-level items of the selection // NOTE: this might be slow for large selections, if someone wants to come up with a better // algorithm, please - go for it! for (State potentialParent : qAsConst(filter)) { if (selectedStateMachine()->isDescendantOf(potentialParent, state)) { addState = false; break; } } if (addState) filter << state; } setFilteredStates(filter); } void StateMachineViewerServer::handleTransitionTriggered(Transition transition) { emit transitionTriggered(TransitionId(transition), selectedStateMachine()->transitionLabel(transition)); } void StateMachineViewerServer::stateEntered(State state) { emit message(tr("State entered: %1").arg(selectedStateMachine()->stateLabel(state))); stateConfigurationChanged(); } void StateMachineViewerServer::stateExited(State state) { emit message(tr("State exited: %1").arg(selectedStateMachine()->stateLabel(state))); stateConfigurationChanged(); } void StateMachineViewerServer::stateConfigurationChanged() { QVector newConfig; if (selectedStateMachine()) newConfig = selectedStateMachine()->configuration(); if (newConfig == m_lastStateConfig) return; m_lastStateConfig = newConfig; StateMachineConfiguration config; config.reserve(newConfig.size()); for (State state : qAsConst(newConfig)) config << StateId(state); emit stateConfigurationChanged(config); } void StateMachineViewerServer::addState(State state) { if (!selectedStateMachine()->stateValid(state)) return; if (!mayAddState(state)) return; Q_ASSERT(!m_recursionGuard.contains(state)); m_recursionGuard.append(state); State parentState = selectedStateMachine()->parentState(state); addState(parentState); // be sure that parent is added first const bool hasChildren = !selectedStateMachine()->stateChildren(state).isEmpty(); const QString &label = selectedStateMachine()->stateLabel(state); // add a connection from parent state to initial state if // parent state is valid and parent state has an initial state const bool connectToInitial = parentState && selectedStateMachine()->isInitialState(state); StateType type = selectedStateMachine()->stateType(state); emit stateAdded(StateId(state), StateId(parentState), hasChildren, label, type, connectToInitial); // add outgoing transitions Q_FOREACH(auto transition, selectedStateMachine()->stateTransitions(state)) { addTransition(transition); } // add sub-states Q_FOREACH(auto childState, selectedStateMachine()->stateChildren(state)) { addState(childState); } } void StateMachineViewerServer::addTransition(Transition transition) { const QString label = selectedStateMachine()->transitionLabel(transition); const State sourceState = selectedStateMachine()->transitionSource(transition); addState(sourceState); foreach (auto targetState, selectedStateMachine()->transitionTargets(transition)) { addState(targetState); emit transitionAdded(TransitionId(transition), StateId(sourceState), StateId(targetState), label); } } void StateMachineViewerServer::updateStartStop() { emit statusChanged(selectedStateMachine() != nullptr, selectedStateMachine() && selectedStateMachine()->isRunning()); } void StateMachineViewerServer::toggleRunning() { if (!selectedStateMachine()) return; if (selectedStateMachine()->isRunning()) selectedStateMachine()->stop(); else selectedStateMachine()->start(); } void StateMachineViewerServer::handleLogMessage(const QString &label, const QString &msg) { emit message(tr("Log [label=%1]: %2").arg(label, msg)); } void StateMachineViewerServer::objectSelected(QObject *obj) { if (auto state = qobject_cast(obj)) { auto model = m_stateSelectionModel->model(); const auto idxs = model->match(model->index(0, 0), StateModel::StateValueRole, QVariant::fromValue(GammaRay::State(quintptr(state))), 1, Qt::MatchExactly | Qt::MatchRecursive | Qt::MatchWrap); qDebug() << idxs; if (idxs.isEmpty()) return; const auto idx = idxs.first(); m_stateSelectionModel->select(idx, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows | QItemSelectionModel::Current); } } StateMachineViewerFactory::StateMachineViewerFactory(QObject *parent) : QObject(parent) { setSupportedTypes(QVector() << QByteArrayLiteral("QStateMachine") << QByteArrayLiteral("QScxmlStateMachine")); } gammaray-2.11.3/plugins/statemachineviewer/statemachineviewerserver.h000066400000000000000000000070541413310443000262320ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Kevin Funk Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_STATEMACHINEVIEWER_STATEMACHINEVIEWERSERVER_H #define GAMMARAY_STATEMACHINEVIEWER_STATEMACHINEVIEWERSERVER_H #include "statemachineviewerutil.h" #include "statemachineviewerinterface.h" #include "statemachinedebuginterface.h" #include #include #include #include #include #include #include QT_BEGIN_NAMESPACE class QAbstractItemModel; class QAbstractProxyModel; class QItemSelectionModel; class QModelIndex; QT_END_NAMESPACE namespace GammaRay { class StateModel; class TransitionModel; class StateMachineDebugInterface; class StateMachineViewerServer : public StateMachineViewerInterface { Q_OBJECT Q_INTERFACES(GammaRay::StateMachineViewerInterface) public: explicit StateMachineViewerServer(Probe *probe, QObject *parent = nullptr); void addState(State state); void addTransition(Transition transition); StateMachineDebugInterface *selectedStateMachine() const; using StateMachineViewerInterface::stateConfigurationChanged; private slots: void stateEntered(State state); void stateExited(State state); void stateConfigurationChanged(); void handleTransitionTriggered(Transition transition); void stateSelectionChanged(); void setFilteredStates(const QVector &states); void selectStateMachine(int row) override; /// Takes ownership of @p machine void setSelectedStateMachine(StateMachineDebugInterface *machine); void updateStartStop(); void toggleRunning() override; void repopulateGraph() override; void handleLogMessage(const QString &label, const QString &msg); void objectSelected(QObject *obj); private: bool mayAddState(State state); QAbstractProxyModel *m_stateMachinesModel; StateModel *m_stateModel; QItemSelectionModel *m_stateSelectionModel; TransitionModel *m_transitionModel; // filters QVector m_filteredStates; QVector m_recursionGuard; QVector m_lastStateConfig; }; class StateMachineViewerFactory : public QObject, public StandardToolFactory { Q_OBJECT Q_INTERFACES(GammaRay::ToolFactory) Q_PLUGIN_METADATA(IID "com.kdab.GammaRay.ToolFactory" FILE "gammaray_statemachineviewer.json") public: explicit StateMachineViewerFactory(QObject *parent = nullptr); }; } #endif // GAMMARAY_STATEMACHINEVIEWERSERVER_H gammaray-2.11.3/plugins/statemachineviewer/statemachineviewerutil.h000066400000000000000000000036611413310443000257010ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Kevin Funk Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_STATEMACHINEVIEWER_STATEMACHINEVIEWERUTIL_H #define GAMMARAY_STATEMACHINEVIEWER_STATEMACHINEVIEWERUTIL_H #include template class RingBuffer { public: RingBuffer() : m_size(5) { } void resize(int size) { Q_ASSERT(size > 0); m_size = size; cleanup(); } void enqueue(T t) { m_entries.enqueue(t); cleanup(); } void clear() { m_entries.clear(); } int size() const { return m_entries.size(); } T tail() const { return m_entries.last(); } QList entries() const { return m_entries; } private: void cleanup() { while (m_entries.size() > m_size) m_entries.dequeue(); } QQueue m_entries; int m_size; }; #endif gammaray-2.11.3/plugins/statemachineviewer/statemachineviewerwidget.cpp000066400000000000000000000432451413310443000265440ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Kevin Funk Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "statemachineviewerwidget.h" #include "ui_statemachineviewerwidget.h" #include "statemachineviewerclient.h" #include "statemodeldelegate.h" #include "statemodel.h" #include "statemachinedebuginterface.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define IF_DEBUG(x) using namespace GammaRay; namespace { class SelectionModelSyncer : public QObject { public: SelectionModelSyncer(StateMachineViewerWidget *widget); private Q_SLOTS: void handle_objectInspector_currentChanged(const QModelIndex &index); void handle_stateMachineView_currentChanged(const QModelIndex &index); StateMachineViewerWidget *m_widget; bool m_updatesEnabled; }; SelectionModelSyncer::SelectionModelSyncer(StateMachineViewerWidget *widget) : QObject(widget) , m_widget(widget) , m_updatesEnabled(true) { connect(widget->objectInspector()->selectionModel(), &QItemSelectionModel::currentChanged, this, &SelectionModelSyncer::handle_objectInspector_currentChanged); connect( widget->stateMachineView()->scene()->selectionModel(), &QItemSelectionModel::currentChanged, this, &SelectionModelSyncer::handle_stateMachineView_currentChanged); } void SelectionModelSyncer::handle_objectInspector_currentChanged(const QModelIndex &index) { if (!m_updatesEnabled) return; QScopedValueRollback block(m_updatesEnabled); m_updatesEnabled = false; const auto stateId = index.data(StateModel::StateIdRole).value(); const auto model = m_widget->stateMachineView()->scene()->model(); const auto matches = model->match(model->index(0, 0), KDSME::StateModel::InternalIdRole, static_cast(stateId), 1, Qt::MatchExactly | Qt::MatchRecursive | Qt::MatchWrap); auto selectionModel = m_widget->stateMachineView()->scene()->selectionModel(); selectionModel->setCurrentIndex(matches.value(0), QItemSelectionModel::SelectCurrent); } void SelectionModelSyncer::handle_stateMachineView_currentChanged(const QModelIndex &index) { if (!m_updatesEnabled) return; QScopedValueRollback block(m_updatesEnabled); m_updatesEnabled = false; const auto internalId = index.data(KDSME::StateModel::InternalIdRole).value(); const auto model = m_widget->objectInspector()->model(); const auto matches = model->match(model->index(0, 0), StateModel::StateIdRole, QVariant::fromValue(StateId(internalId)), 1, Qt::MatchExactly | Qt::MatchRecursive | Qt::MatchWrap); auto selectionModel = m_widget->objectInspector()->selectionModel(); selectionModel->setCurrentIndex(matches.value( 0), QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows); } QObject *createStateMachineViewerClient(const QString & /*name*/, QObject *parent) { return new StateMachineViewerClient(parent); } KDSME::RuntimeController::Configuration toSmeConfiguration(const StateMachineConfiguration &config, const QHash &map) { KDSME::RuntimeController::Configuration result; for (const StateId &id : config) { if (auto state = map.value(id)) result << state; } return result; } } StateMachineViewerWidget::StateMachineViewerWidget(QWidget *parent, Qt::WindowFlags f) : QWidget(parent, f) , m_ui(new Ui::StateMachineViewerWidget) , m_stateManager(this) , m_machine(nullptr) , m_showLog(false) { ObjectBroker::registerClientObjectFactoryCallback( createStateMachineViewerClient); m_interface = ObjectBroker::object(); m_ui->setupUi(this); // set up log expanding widget connect(m_ui->hideLogPushButton, &QPushButton::clicked, this, [this]() { setShowLog(false); }); connect(m_ui->showLogPushButton, &QPushButton::clicked, this, [this]() { setShowLog(true); }); setShowLog(false); QAbstractItemModel *stateMachineModel = ObjectBroker::model(QStringLiteral("com.kdab.GammaRay.StateMachineModel")); m_ui->stateMachinesView->setModel(stateMachineModel); connect(m_ui->stateMachinesView, SIGNAL(currentIndexChanged(int)), m_interface, SLOT(selectStateMachine(int))); QAbstractItemModel *stateModel = ObjectBroker::model(QStringLiteral("com.kdab.GammaRay.StateModel")); ClientDecorationIdentityProxyModel *stateProxyModel = new ClientDecorationIdentityProxyModel(this); stateProxyModel->setSourceModel(stateModel); connect(stateProxyModel, SIGNAL(modelReset()), this, SLOT(stateModelReset())); m_ui->singleStateMachineView->header()->setObjectName("singleStateMachineViewHeader"); m_ui->singleStateMachineView->setExpandNewContent(true); m_ui->singleStateMachineView->setDeferredResizeMode(0, QHeaderView::Stretch); m_ui->singleStateMachineView->setDeferredResizeMode(1, QHeaderView::ResizeToContents); m_ui->singleStateMachineView->setItemDelegate(new StateModelDelegate(this)); m_ui->singleStateMachineView->setModel(stateProxyModel); m_ui->singleStateMachineView->setSelectionModel(ObjectBroker::selectionModel(stateProxyModel)); connect(m_ui->singleStateMachineView, &QWidget::customContextMenuRequested, this, &StateMachineViewerWidget::objectInspectorContextMenu); connect(m_ui->actionStartStopStateMachine, SIGNAL(triggered()), m_interface, SLOT(toggleRunning())); addAction(m_ui->actionStartStopStateMachine); auto separatorAction = new QAction(this); separatorAction->setSeparator(true); addAction(separatorAction); m_stateMachineView = new KDSME::StateMachineView; m_ui->horizontalSplitter->addWidget(m_stateMachineView); m_stateMachineView->scene()->setContextMenuPolicy(Qt::CustomContextMenu); connect(m_stateMachineView->scene(), &KDSME::StateMachineScene::customContextMenuEvent, this, [this](KDSME::AbstractSceneContextMenuEvent *event) { const auto objectId = ObjectId(reinterpret_cast(event->elementUnderCursor()->internalId())); const auto model = objectInspector()->model(); const auto matches = model->match( model->index(0, 0), ObjectModel::ObjectIdRole, QVariant::fromValue(objectId), 1, Qt::MatchExactly | Qt::MatchRecursive | Qt::MatchWrap); showContextMenuForObject(matches.value(0), event->globalPos()); }); connect(m_interface, SIGNAL(message(QString)), this, SLOT(showMessage(QString))); connect(m_interface, SIGNAL(stateConfigurationChanged(GammaRay::StateMachineConfiguration)), this, SLOT(stateConfigurationChanged(GammaRay::StateMachineConfiguration))); connect(m_interface, SIGNAL(stateAdded(GammaRay::StateId,GammaRay::StateId,bool,QString,GammaRay::StateType,bool)), this, SLOT(stateAdded(GammaRay::StateId,GammaRay::StateId,bool,QString,GammaRay::StateType,bool))); connect(m_interface, SIGNAL(transitionAdded(GammaRay::TransitionId,GammaRay::StateId,GammaRay::StateId,QString)), this, SLOT(transitionAdded(GammaRay::TransitionId,GammaRay::StateId,GammaRay::StateId,QString))); connect(m_interface, SIGNAL(statusChanged(bool,bool)), this, SLOT(statusChanged(bool,bool))); connect(m_interface, SIGNAL(transitionTriggered(GammaRay::TransitionId,QString)), this, SLOT(transitionTriggered(GammaRay::TransitionId,QString))); connect(m_interface, SIGNAL(aboutToRepopulateGraph()), this, SLOT(clearGraph())); connect(m_interface, SIGNAL(graphRepopulated()), this, SLOT(repopulateView())); // append actions for the state machine view KDSME::StateMachineToolBar *toolBar = new KDSME::StateMachineToolBar(m_stateMachineView, this); toolBar->setHidden(true); addActions(toolBar->actions()); m_interface->repopulateGraph(); // share selection model new SelectionModelSyncer(this); m_stateManager.setDefaultSizes(m_ui->verticalSplitter, UISizeVector() << "50%" << "50%"); m_stateManager.setDefaultSizes(m_ui->horizontalSplitter, UISizeVector() << "30%" << "70%"); loadSettings(); } StateMachineViewerWidget::~StateMachineViewerWidget() { saveSettings(); } void StateMachineViewerWidget::saveTargetState(QSettings *settings) const { settings->setValue("ShowLog", m_showLog); const auto scene = m_stateMachineView->scene(); settings->setValue("ShowTransitionsLabel", scene->layoutProperties()->showTransitionLabels()); settings->setValue("MaximumDepth", scene->maximumDepth()); } void StateMachineViewerWidget::restoreTargetState(QSettings *settings) { setShowLog(settings->value("ShowLog", m_showLog).toBool()); auto scene = m_stateMachineView->scene(); scene->layoutProperties()->setShowTransitionLabels(settings->value("ShowTransitionsLabel", true).toBool()); scene->setMaximumDepth(settings->value("MaximumDepth", 3).toInt()); } void StateMachineViewerWidget::showContextMenuForObject(const QModelIndex &index, const QPoint &globalPos) { if (!index.isValid()) return; Q_ASSERT(index.model() == objectInspector()->model()); const auto objectId = index.data(ObjectModel::ObjectIdRole).value(); QMenu menu(tr("Entity @ %1").arg(QLatin1String("0x") + QString::number(objectId.id(), 16))); ContextMenuExtension ext(objectId); ext.setLocation(ContextMenuExtension::Creation, index.data( ObjectModel::CreationLocationRole).value()); ext.setLocation(ContextMenuExtension::Declaration, index.data(ObjectModel::DeclarationLocationRole).value()); ext.populateMenu(&menu); menu.exec(globalPos); } KDSME::StateMachineView *StateMachineViewerWidget::stateMachineView() const { return m_stateMachineView; } DeferredTreeView *StateMachineViewerWidget::objectInspector() const { return m_ui->singleStateMachineView; } void StateMachineViewerWidget::loadSettings() { QSettings settings; settings.beginGroup("Plugin_StateMachineViewer"); m_stateMachineView->setThemeName(settings.value("ThemeName", "SystemTheme").toString()); settings.endGroup(); settings.sync(); } void StateMachineViewerWidget::saveSettings() { QSettings settings; settings.beginGroup("Plugin_StateMachineViewer"); settings.setValue("ThemeName", m_stateMachineView->themeName()); settings.endGroup(); settings.sync(); } void StateMachineViewerWidget::showMessage(const QString &message) { // update log auto logTextEdit = m_ui->logTextEdit; logTextEdit->appendPlainText(message); // auto-scroll hack QScrollBar *sb = logTextEdit->verticalScrollBar(); sb->setValue(sb->maximum()); } void StateMachineViewerWidget::stateConfigurationChanged(const StateMachineConfiguration &config) { if (m_machine) m_machine->runtimeController()->setActiveConfiguration(toSmeConfiguration(config, m_idToStateMap)); } void StateMachineViewerWidget::stateAdded(const StateId stateId, const StateId parentId, const bool hasChildren, const QString &label, const StateType type, const bool connectToInitial) { Q_UNUSED(hasChildren); IF_DEBUG(qDebug() << "stateAdded" << stateId << parentId << label << type); if (m_idToStateMap.contains(stateId)) return; KDSME::State *parentState = m_idToStateMap.value(parentId); KDSME::State *state = nullptr; if (type == StateMachineState) state = m_machine = new KDSME::StateMachine; else if (type == GammaRay::FinalState) state = new KDSME::FinalState(parentState); else if (type == GammaRay::ShallowHistoryState) state = new KDSME::HistoryState(KDSME::HistoryState::ShallowHistory, parentState); else if (type == GammaRay::DeepHistoryState) state = new KDSME::HistoryState(KDSME::HistoryState::DeepHistory, parentState); else state = new KDSME::State(parentState); if (connectToInitial && parentState) { KDSME::State *initialState = new KDSME::PseudoState(KDSME::PseudoState::InitialState, parentState); initialState->setFlags(KDSME::Element::ElementIsSelectable); KDSME::Transition *transition = new KDSME::Transition(initialState); transition->setTargetState(state); transition->setFlags(KDSME::Element::ElementIsSelectable); } Q_ASSERT(state); state->setLabel(label); state->setInternalId(stateId); state->setFlags(KDSME::Element::ElementIsSelectable); m_idToStateMap[stateId] = state; } void StateMachineViewerWidget::transitionAdded(const TransitionId transitionId, const StateId sourceId, const StateId targetId, const QString &label) { if (m_idToTransitionMap.contains(transitionId)) return; IF_DEBUG(qDebug() << "transitionAdded" << transitionId << label << sourceId << targetId); KDSME::State *source = m_idToStateMap.value(sourceId); KDSME::State *target = m_idToStateMap.value(targetId); if (!source || !target) { qDebug() << "Null source or target for transition:" << transitionId; return; } KDSME::Transition *transition = new KDSME::Transition(source); transition->setTargetState(target); transition->setLabel(label); transition->setFlags(KDSME::Element::ElementIsSelectable); m_idToTransitionMap[transitionId] = transition; } void StateMachineViewerWidget::statusChanged(const bool haveStateMachine, const bool running) { if (m_machine) m_machine->runtimeController()->setIsRunning(running); m_ui->actionStartStopStateMachine->setEnabled(haveStateMachine); if (!running) { m_ui->actionStartStopStateMachine->setText(tr("Start State Machine")); m_ui->actionStartStopStateMachine->setIcon(style()->standardIcon(QStyle::SP_MediaPlay)); } else { m_ui->actionStartStopStateMachine->setText(tr("Stop State Machine")); m_ui->actionStartStopStateMachine->setIcon(style()->standardIcon(QStyle::SP_MediaStop)); } } void StateMachineViewerWidget::transitionTriggered(TransitionId transitionId, const QString &label) { Q_UNUSED(label); if (m_machine) m_machine->runtimeController()->setLastTransition(m_idToTransitionMap.value(transitionId)); } void StateMachineViewerWidget::clearGraph() { IF_DEBUG(qDebug() << Q_FUNC_INFO); m_stateMachineView->scene()->setRootState(nullptr); m_idToStateMap.clear(); m_idToTransitionMap.clear(); } void StateMachineViewerWidget::repopulateView() { IF_DEBUG(qDebug() << Q_FUNC_INFO); m_stateMachineView->scene()->setRootState(m_machine); if (!m_machine) return; m_stateMachineView->scene()->layout(); IF_DEBUG(m_machine->dumpObjectTree();) m_stateMachineView->fitInView(); } void StateMachineViewerWidget::stateModelReset() { m_ui->singleStateMachineView->expandAll(); if (m_machine) m_machine->runtimeController()->clear(); } void StateMachineViewerWidget::objectInspectorContextMenu(QPoint pos) { const auto index = m_ui->singleStateMachineView->indexAt(pos); if (!index.isValid()) return; const auto globalPos = m_ui->singleStateMachineView->viewport()->mapToGlobal(pos); showContextMenuForObject(index, globalPos); } void StateMachineViewerWidget::setShowLog(bool show) { m_showLog = show; m_ui->logExpandingWidget->setVisible(show); m_ui->showLogPushButton->setVisible(!show); m_ui->verticalSplitter->handle(0)->setEnabled(show); } gammaray-2.11.3/plugins/statemachineviewer/statemachineviewerwidget.h000066400000000000000000000073651413310443000262140ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Kevin Funk Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef STATEMACHINEVIEWERWIDGETNG_H #define STATEMACHINEVIEWERWIDGETNG_H #include #include #include "statemachineviewerinterface.h" #include namespace KDSME { class State; class StateMachine; class StateMachineView; class Transition; } namespace GammaRay { class DeferredTreeView; namespace Ui { class StateMachineViewerWidget; } class StateMachineViewerWidget : public QWidget { Q_OBJECT public: explicit StateMachineViewerWidget(QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); virtual ~StateMachineViewerWidget(); Q_INVOKABLE void saveTargetState(QSettings *settings) const; Q_INVOKABLE void restoreTargetState(QSettings *settings); KDSME::StateMachineView *stateMachineView() const; DeferredTreeView *objectInspector() const; private slots: void showMessage(const QString &message); void stateAdded(const GammaRay::StateId stateId, const GammaRay::StateId parentId, const bool hasChildren, const QString &label, const GammaRay::StateType type, const bool connectToInitial); void stateConfigurationChanged(const GammaRay::StateMachineConfiguration &config); void transitionAdded(const GammaRay::TransitionId transitionId, const GammaRay::StateId sourceId, const GammaRay::StateId targetId, const QString &label); void statusChanged(const bool haveStateMachine, const bool running); void transitionTriggered(GammaRay::TransitionId transitionId, const QString &label); void stateModelReset(); void repopulateView(); void clearGraph(); void setShowLog(bool show); void objectInspectorContextMenu(QPoint pos); private: /** * Show context menu for index @p index (a object inspector model index) * at global position @p globalPos. */ void showContextMenuForObject(const QModelIndex &index, const QPoint &globalPos); void loadSettings(); void saveSettings(); QScopedPointer m_ui; UIStateManager m_stateManager; KDSME::StateMachineView *m_stateMachineView; StateMachineViewerInterface *m_interface; QHash m_idToStateMap; QHash m_idToTransitionMap; KDSME::StateMachine *m_machine; bool m_showLog; }; class StateMachineViewerUiFactory : public QObject, public StandardToolUiFactory { Q_OBJECT Q_INTERFACES(GammaRay::ToolUiFactory) Q_PLUGIN_METADATA(IID "com.kdab.GammaRay.ToolUiFactory" FILE "gammaray_statemachineviewer.json") }; } #endif // STATEMACHINEVIEWERWIDGETNG_H gammaray-2.11.3/plugins/statemachineviewer/statemachineviewerwidget.ui000066400000000000000000000074351413310443000264000ustar00rootroot00000000000000 GammaRay::StateMachineViewerWidget 0 0 809 594 Qt::Horizontal false Qt::Vertical S&tate machine: stateMachinesView QComboBox::AdjustToContents Qt::CustomContextMenu true false Show Log 0 0 0 0 0 Hide Log true 100 false Start/Stop State Machine GammaRay::DeferredTreeView QTreeView
    ui/deferredtreeview.h
    gammaray-2.11.3/plugins/statemachineviewer/statemachinewatcher.cpp000066400000000000000000000115301413310443000254640ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Kevin Funk Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "statemachinewatcher.h" #include #include #include #include #include #include using namespace GammaRay; static State toState(QAbstractState *state = nullptr) { return State(reinterpret_cast(state)); } static Transition toTransition(QAbstractTransition *transition) { return Transition(reinterpret_cast(transition)); } StateMachineWatcher::StateMachineWatcher(QObject *parent) : QObject(parent) , m_watchedStateMachine(nullptr) , m_lastEnteredState(nullptr) , m_lastExitedState(nullptr) { } StateMachineWatcher::~StateMachineWatcher() = default; void StateMachineWatcher::setWatchedStateMachine(QStateMachine *machine) { if (m_watchedStateMachine == machine) return; m_watchedStateMachine = machine; clearWatchedStates(); Q_FOREACH(QAbstractState* state, machine->findChildren()) { watchState(state); } emit watchedStateMachineChanged(machine); } QStateMachine *StateMachineWatcher::watchedStateMachine() const { return m_watchedStateMachine; } void StateMachineWatcher::watchState(QAbstractState *state) { if (state->machine() != m_watchedStateMachine) return; connect(state, &QAbstractState::entered, this, &StateMachineWatcher::handleStateEntered, Qt::UniqueConnection); connect(state, &QAbstractState::exited, this, &StateMachineWatcher::handleStateExited, Qt::UniqueConnection); connect(state, &QObject::destroyed, this, &StateMachineWatcher::handleStateDestroyed, Qt::UniqueConnection); Q_FOREACH(QAbstractTransition *transition, state->findChildren()) { connect(transition, &QAbstractTransition::triggered, this, &StateMachineWatcher::handleTransitionTriggered, Qt::UniqueConnection); } m_watchedStates << state; } void StateMachineWatcher::clearWatchedStates() { for (QAbstractState *state : qAsConst(m_watchedStates)) { disconnect(state, &QAbstractState::entered, this, &StateMachineWatcher::handleStateEntered); disconnect(state, &QAbstractState::exited, this, &StateMachineWatcher::handleStateExited); disconnect(state, &QObject::destroyed, this, &StateMachineWatcher::handleStateDestroyed); Q_FOREACH(QAbstractTransition *transition, state->findChildren()) { disconnect(transition, &QAbstractTransition::triggered, this, &StateMachineWatcher::handleTransitionTriggered); } } m_watchedStates.clear(); } void StateMachineWatcher::handleTransitionTriggered() { QAbstractTransition *transition = qobject_cast(QObject::sender()); Q_ASSERT(transition); emit transitionTriggered(toTransition(transition)); } void StateMachineWatcher::handleStateEntered() { QAbstractState *state = qobject_cast(QObject::sender()); Q_ASSERT(state); if (state->machine() != m_watchedStateMachine) return; if (state == m_lastEnteredState) return; m_lastEnteredState = state; emit stateEntered(toState(state)); } void StateMachineWatcher::handleStateExited() { QAbstractState *state = qobject_cast(QObject::sender()); Q_ASSERT(state); if (state->machine() != m_watchedStateMachine) return; if (state == m_lastExitedState) return; m_lastExitedState = state; emit stateExited(toState(state)); } void StateMachineWatcher::handleStateDestroyed() { QAbstractState *state = static_cast(QObject::sender()); Q_ASSERT(state); const int index = m_watchedStates.indexOf(state); Q_ASSERT(index != -1); m_watchedStates.remove(index); } gammaray-2.11.3/plugins/statemachineviewer/statemachinewatcher.h000066400000000000000000000045251413310443000251370ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Kevin Funk Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_STATEMACHINEVIEWER_STATEMACHINEWATCHER_H #define GAMMARAY_STATEMACHINEVIEWER_STATEMACHINEWATCHER_H #include #include #include "statemachinedebuginterface.h" QT_BEGIN_NAMESPACE class QAbstractState; class QAbstractState; class QAbstractTransition; class QState; class QStateMachine; QT_END_NAMESPACE namespace GammaRay { class StateMachineWatcher : public QObject { Q_OBJECT public: explicit StateMachineWatcher(QObject *parent = nullptr); ~StateMachineWatcher() override; void setWatchedStateMachine(QStateMachine *machine); QStateMachine *watchedStateMachine() const; Q_SIGNALS: void stateEntered(State state); void stateExited(State state); void transitionTriggered(Transition transition); void watchedStateMachineChanged(QStateMachine *); private Q_SLOTS: void watchState(QAbstractState *state); void clearWatchedStates(); void handleStateEntered(); void handleStateExited(); void handleStateDestroyed(); void handleTransitionTriggered(); private: QStateMachine *m_watchedStateMachine; QVector m_watchedStates; QAbstractState *m_lastEnteredState; QAbstractState *m_lastExitedState; }; } #endif // GAMMARAY_STATEMACHINEWATCHER_H gammaray-2.11.3/plugins/statemachineviewer/statemodel.cpp000066400000000000000000000237551413310443000236160ustar00rootroot00000000000000/* statemodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Stephen Kelly 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, see . */ #include "statemodel.h" #include "statemachinedebuginterface.h" #include "statemachinewatcher.h" #include #include #include #include #include #include #include #include // #include using namespace GammaRay; namespace GammaRay { class StateModelPrivate { explicit StateModelPrivate(StateModel *qq) : q_ptr(qq) , m_stateMachine(nullptr) { } void emitDataChangedForState(State state) { const auto left = indexForState(state); const auto right = left.sibling(left.row(), q_ptr->columnCount() - 1); if (!left.isValid() || !right.isValid()) return; emit q_ptr->dataChanged(left, right); } Q_DECLARE_PUBLIC(StateModel) StateModel * const q_ptr; StateMachineDebugInterface *m_stateMachine; QVector m_lastConfiguration; QVector children(State parent) const; State mapModelIndex2State(const QModelIndex &) const; QModelIndex indexForState(State state) const; // private slots: void stateConfigurationChanged(); void handleMachineDestroyed(QObject *); }; } QVector StateModelPrivate::children(State parent) const { if (!m_stateMachine) return QVector(); return m_stateMachine->stateChildren(parent); } State StateModelPrivate::mapModelIndex2State(const QModelIndex &index) const { if (!m_stateMachine) return State(); if (index.isValid()) { QVector c = children(State(index.internalId())); return c[index.row()]; } return m_stateMachine->rootState(); } QModelIndex StateModelPrivate::indexForState(State state) const { if (!m_stateMachine) return {}; if (state == m_stateMachine->rootState()) return QModelIndex(); Q_Q(const StateModel); State parentState = m_stateMachine->parentState(state); int row = m_stateMachine->stateChildren(parentState).indexOf(state); if (row == -1) return QModelIndex(); return q->index(row, 0, indexForState(parentState)); } void StateModelPrivate::stateConfigurationChanged() { QVector newConfig = m_stateMachine->configuration(); // states which became active QVector difference; std::set_difference(newConfig.begin(), newConfig.end(), m_lastConfiguration.begin(), m_lastConfiguration.end(), std::back_inserter(difference)); for (State state : qAsConst(difference)) emitDataChangedForState(state); // states which became inactive difference.clear(); std::set_difference(m_lastConfiguration.begin(), m_lastConfiguration.end(), newConfig.begin(), newConfig.end(), std::back_inserter(difference)); for (State state : qAsConst(difference)) emitDataChangedForState(state); m_lastConfiguration = newConfig; } void StateModelPrivate::handleMachineDestroyed(QObject *) { Q_Q(StateModel); q->beginResetModel(); m_stateMachine = nullptr; q->endResetModel(); } StateModel::StateModel(QObject *parent) : QAbstractItemModel(parent) , d_ptr(new StateModelPrivate(this)) { } StateModel::~StateModel() { delete d_ptr; } void StateModel::setStateMachine(StateMachineDebugInterface *stateMachine) { Q_D(StateModel); if (d->m_stateMachine == stateMachine) return; if (d->m_stateMachine) { disconnect(d->m_stateMachine, nullptr, this, nullptr); } beginResetModel(); d->m_stateMachine = stateMachine; d->m_lastConfiguration = (stateMachine ? stateMachine->configuration() : QVector()); endResetModel(); if (d->m_stateMachine) { connect(d->m_stateMachine, &QObject::destroyed, this, [this](QObject *obj) { Q_D(StateModel); d->handleMachineDestroyed(obj); }); connect(d->m_stateMachine, &StateMachineDebugInterface::stateEntered, this, [this] { Q_D(StateModel); d->stateConfigurationChanged(); }); connect(d->m_stateMachine, &StateMachineDebugInterface::stateEntered, this, [this] { Q_D(StateModel); d->stateConfigurationChanged(); }); } } StateMachineDebugInterface *StateModel::stateMachine() const { Q_D(const StateModel); return d->m_stateMachine; } QVariant StateModel::data(const QModelIndex &index, int role) const { Q_D(const StateModel); if (!index.isValid()) return QVariant(); State state = d->mapModelIndex2State(index); QObject *object = d->m_stateMachine->stateObject(state); if (role == TransitionsRole) { return d->m_stateMachine->transitions(state); } else if (role == IsInitialStateRole) { return d->m_stateMachine->isInitialState(state); } else if (role == StateValueRole) { return QVariant::fromValue(state); } else if (role == StateIdRole) { return QVariant::fromValue(GammaRay::StateId(static_cast(state.m_id))); } else if (role == Qt::CheckStateRole && index.column() == 0) { return d->m_stateMachine->configuration().contains(state) ? Qt::Checked : Qt::Unchecked; } else if (role == Qt::DisplayRole && index.column() == 0) { return d->m_stateMachine->stateDisplay(state); } else if (role == Qt::DisplayRole && index.column() == 1) { return d->m_stateMachine->stateDisplayType(state); } else if (role == ObjectModel::ObjectRole) { return QVariant::fromValue(object); } else if (role == ObjectModel::ObjectIdRole) { return QVariant::fromValue(ObjectId(object)); } else if (role == Qt::ToolTipRole) { return Util::tooltipForObject(object); } else if (role == ObjectModel::DecorationIdRole && index.column() == 0) { return Util::iconIdForObject(object); } else if (role == ObjectModel::CreationLocationRole) { const auto loc = ObjectDataProvider::creationLocation(object); if (loc.isValid()) return QVariant::fromValue(loc); } else if (role == ObjectModel::DeclarationLocationRole) { const auto loc = ObjectDataProvider::declarationLocation(object); if (loc.isValid()) return QVariant::fromValue(loc); } return QVariant(); } int StateModel::rowCount(const QModelIndex &parent) const { Q_D(const StateModel); return d->children(d->mapModelIndex2State(parent)).count(); } QModelIndex StateModel::index(int row, int column, const QModelIndex &parent) const { Q_D(const StateModel); if (row < 0 || column < 0 || column > 1) return {}; State internalPointer(0); if (!parent.isValid()) { internalPointer = d->m_stateMachine->rootState(); } else { State s = State(parent.internalId()); QVector c = d->m_stateMachine->stateChildren(s); internalPointer = c.at(parent.row()); } QVector c = d->children(internalPointer); if (row >= c.size()) return QModelIndex(); return createIndex(row, column, internalPointer); } QModelIndex StateModel::parent(const QModelIndex &index) const { Q_D(const StateModel); if (!index.isValid() || !d->m_stateMachine) return {}; State state = d->mapModelIndex2State(index); State parent = d->m_stateMachine->parentState(state); if (parent == d->m_stateMachine->rootState()) return QModelIndex(); State grandParent = d->m_stateMachine->parentState(parent); int row = d->children(grandParent).indexOf(parent); return createIndex(row, 0, grandParent); } QVariant StateModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role == Qt::DisplayRole && orientation == Qt::Horizontal) { switch (section) { case 0: return tr("State"); case 1: return tr("Type"); } } return QAbstractItemModel::headerData(section, orientation, role); } int StateModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); return 2; } QMap StateModel::itemData(const QModelIndex &index) const { QMap map = QAbstractItemModel::itemData(index); map.insert(ObjectModel::ObjectIdRole, this->data(index, ObjectModel::ObjectIdRole)); map.insert(ObjectModel::DecorationIdRole, this->data(index, ObjectModel::DecorationIdRole)); auto loc = this->data(index, ObjectModel::CreationLocationRole); if (loc.isValid()) map.insert(ObjectModel::CreationLocationRole, loc); loc = this->data(index, ObjectModel::DeclarationLocationRole); if (loc.isValid()) map.insert(ObjectModel::DeclarationLocationRole, loc); return map; } QHash StateModel::roleNames() const { QHash roleNames = QAbstractItemModel::roleNames(); roleNames.insert(TransitionsRole, "transitions"); roleNames.insert(IsInitialStateRole, "isInitial"); return roleNames; } #include "moc_statemodel.cpp" gammaray-2.11.3/plugins/statemachineviewer/statemodel.h000066400000000000000000000057151413310443000232570ustar00rootroot00000000000000/* statemodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Stephen Kelly 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, see . */ #ifndef GAMMARAY_STATEMACHINEVIEWER_STATEMODEL_H #define GAMMARAY_STATEMACHINEVIEWER_STATEMODEL_H #include #include QT_BEGIN_NAMESPACE class QAbstractTransition; class QStateMachine; QT_END_NAMESPACE namespace GammaRay { class StateModelPrivate; class StateMachineDebugInterface; class StateModel : public QAbstractItemModel { Q_OBJECT public: enum Roles { TransitionsRole = ObjectModel::UserRole + 1, ///< return bool, see StateMachineDebugInterface::transitions() IsInitialStateRole, ///< return bool, see StateMachineDebugInterface::isInitialState() StateValueRole, ///< return GammaRay::State StateIdRole ///< return GammaRay::StateId }; explicit StateModel(QObject *parent = nullptr); ~StateModel() override; void setStateMachine(StateMachineDebugInterface *stateMachine); StateMachineDebugInterface *stateMachine() const; int columnCount(const QModelIndex &parent = QModelIndex()) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; QModelIndex parent(const QModelIndex &index) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; QMap itemData(const QModelIndex &index) const override; QHash roleNames() const override; protected: Q_DECLARE_PRIVATE(StateModel) StateModelPrivate * const d_ptr; private: Q_PRIVATE_SLOT(d_func(), void stateConfigurationChanged()) Q_PRIVATE_SLOT(d_func(), void handleMachineDestroyed(QObject*)) }; } #endif gammaray-2.11.3/plugins/statemachineviewer/statemodeldelegate.cpp000066400000000000000000000034621413310443000253020ustar00rootroot00000000000000/* statemodeldelegate.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "statemodeldelegate.h" using namespace GammaRay; StateModelDelegate::StateModelDelegate(QObject *parent) : QStyledItemDelegate(parent) { } void StateModelDelegate::initStyleOption(QStyleOptionViewItem *option, const QModelIndex &index) const { QStyledItemDelegate::initStyleOption(option, index); // turn check marks into bold text, to save space and avoid the impression you can interact with this option->features = option->features & ~QStyleOptionViewItem::HasCheckIndicator; const auto active = index.sibling(index.row(), 0).data(Qt::CheckStateRole).toInt() == Qt::Checked; option->font.setBold(active); } gammaray-2.11.3/plugins/statemachineviewer/statemodeldelegate.h000066400000000000000000000030671413310443000247500ustar00rootroot00000000000000/* statemodeldelegate.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_STATEMODELDELEGATE_H #define GAMMARAY_STATEMODELDELEGATE_H #include namespace GammaRay { class StateModelDelegate : public QStyledItemDelegate { Q_OBJECT public: explicit StateModelDelegate(QObject *parent = nullptr); protected: void initStyleOption(QStyleOptionViewItem *option, const QModelIndex &index) const override; }; } #endif // GAMMARAY_STATEMODELDELEGATE_H gammaray-2.11.3/plugins/statemachineviewer/transitionmodel.cpp000066400000000000000000000120061413310443000246530ustar00rootroot00000000000000/* transitionmodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Stephen Kelly 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, see . */ #include "transitionmodel.h" #include #include #include #include #include #include #include // #include using namespace GammaRay; namespace GammaRay { class TransitionModelPrivate { explicit TransitionModelPrivate(TransitionModel *qq) : q_ptr(qq) , m_state(nullptr) { } Q_DECLARE_PUBLIC(TransitionModel) TransitionModel * const q_ptr; QAbstractState *m_state; QList children(QObject *parent) const; QObject *mapModelIndex2QObject(const QModelIndex &) const; }; } QList TransitionModelPrivate::children(QObject *parent) const { QList result; if (parent == nullptr) parent = m_state; foreach (QObject *o, parent->children()) { if (o->inherits("QAbstractTransition")) result.append(o); } std::sort(result.begin(), result.end()); return result; } QObject *TransitionModelPrivate::mapModelIndex2QObject(const QModelIndex &index) const { if (index.isValid()) { QObjectList c = children(reinterpret_cast(index.internalPointer())); return c[index.row()]; } return m_state; } TransitionModel::TransitionModel(QObject *parent) : ObjectModelBase(parent) , d_ptr(new TransitionModelPrivate(this)) { } TransitionModel::~TransitionModel() { delete d_ptr; } void TransitionModel::setState(QAbstractState *state) { Q_D(TransitionModel); beginResetModel(); d->m_state = state; endResetModel(); } QVariant TransitionModel::headerData(int section, Qt::Orientation orientation, int role) const { if (section < 2) return ObjectModelBase::headerData(section, orientation, role); if (section == 2) return "Signal"; if (section == 3) return "Target"; return QVariant(); } QVariant TransitionModel::data(const QModelIndex &index, int role) const { Q_D(const TransitionModel); if (!index.isValid() || !d->m_state) return QVariant(); QObject *obj = d->mapModelIndex2QObject(index); if (obj) { if (index.column() == 2 && role == Qt::DisplayRole) { QSignalTransition *sigTransition = qobject_cast(obj); if (sigTransition) return sigTransition->signal(); } else if (index.column() == 3 && role == Qt::DisplayRole) { QAbstractTransition *transition = qobject_cast(obj); if (transition) return Util::displayString(transition->targetState()); } return dataForObject(obj, index, role); } return QVariant(); } int TransitionModel::columnCount(const QModelIndex &parent) const { return ObjectModelBase::columnCount(parent) + 2; } int TransitionModel::rowCount(const QModelIndex &parent) const { Q_D(const TransitionModel); if (!d->m_state) return 0; return d->children(d->mapModelIndex2QObject(parent)).count(); } QModelIndex TransitionModel::index(int row, int column, const QModelIndex &parent) const { Q_D(const TransitionModel); if (parent.isValid() || !d->m_state) return {}; if (row < 0 || column < 0 || column > 3) return QModelIndex(); QObject *internalPointer = nullptr; if (!parent.isValid()) { internalPointer = d->m_state; } else { QObject *o = reinterpret_cast(parent.internalPointer()); QObjectList c = d->children(o); internalPointer = c.at(parent.row()); } QObjectList c = d->children(internalPointer); if (row >= c.size()) return QModelIndex(); return createIndex(row, column, internalPointer); } QModelIndex TransitionModel::parent(const QModelIndex &index) const { Q_UNUSED(index); // Q_D(const TransitionModel); return {}; } gammaray-2.11.3/plugins/statemachineviewer/transitionmodel.h000066400000000000000000000043611413310443000243250ustar00rootroot00000000000000/* transitionmodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Stephen Kelly 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, see . */ #ifndef GAMMARAY_STATEMACHINEVIEWER_TRANSITIONMODEL_H #define GAMMARAY_STATEMACHINEVIEWER_TRANSITIONMODEL_H #include QT_BEGIN_NAMESPACE class QAbstractState; QT_END_NAMESPACE namespace GammaRay { class TransitionModelPrivate; class TransitionModel : public ObjectModelBase { Q_OBJECT public: explicit TransitionModel(QObject *parent = nullptr); ~TransitionModel() override; void setState(QAbstractState *state); int columnCount(const QModelIndex &parent = QModelIndex()) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; QModelIndex parent(const QModelIndex &index) const override; protected: Q_DECLARE_PRIVATE(TransitionModel) TransitionModelPrivate *const d_ptr; }; } #endif gammaray-2.11.3/plugins/styleinspector/000077500000000000000000000000001413310443000201355ustar00rootroot00000000000000gammaray-2.11.3/plugins/styleinspector/CMakeLists.txt000066400000000000000000000021401413310443000226720ustar00rootroot00000000000000# probe part if (NOT GAMMARAY_CLIENT_ONLY_BUILD) set(gammaray_styleinspector_plugin_srcs styleinspector.cpp pixelmetricmodel.cpp standardiconmodel.cpp primitivemodel.cpp controlmodel.cpp styleoption.cpp abstractstyleelementmodel.cpp abstractstyleelementstatetable.cpp complexcontrolmodel.cpp dynamicproxystyle.cpp styleinspectorinterface.cpp stylehintmodel.cpp ${CMAKE_SOURCE_DIR}/ui/palettemodel.cpp ) gammaray_add_plugin(gammaray_styleinspector_plugin JSON gammaray_styleinspector.json SOURCES ${gammaray_styleinspector_plugin_srcs} ) target_link_libraries(gammaray_styleinspector_plugin Qt5::Gui Qt5::Widgets gammaray_core ) endif() # ui part if(GAMMARAY_BUILD_UI) set(gammaray_styleinspector_ui_plugin_srcs styleinspectorwidget.cpp styleelementstatetablepage.cpp styleinspectorinterface.cpp styleinspectorclient.cpp ) gammaray_add_plugin(gammaray_styleinspector_ui_plugin JSON gammaray_styleinspector.json SOURCES ${gammaray_styleinspector_ui_plugin_srcs} ) target_link_libraries(gammaray_styleinspector_ui_plugin gammaray_ui ) endif() gammaray-2.11.3/plugins/styleinspector/abstractstyleelementmodel.cpp000066400000000000000000000047601413310443000261270ustar00rootroot00000000000000/* abstractstyleelementmodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2012-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "abstractstyleelementmodel.h" #include "dynamicproxystyle.h" #include #include #include using namespace GammaRay; AbstractStyleElementModel::AbstractStyleElementModel(QObject *parent) : QAbstractTableModel(parent) { } void AbstractStyleElementModel::setStyle(QStyle *style) { beginResetModel(); m_style = QPointer(style); endResetModel(); } QVariant AbstractStyleElementModel::data(const QModelIndex &index, int role) const { if (!index.isValid() || !m_style) return QVariant(); return doData(index.row(), index.column(), role); } int AbstractStyleElementModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); return doColumnCount(); } int AbstractStyleElementModel::rowCount(const QModelIndex &parent) const { if (parent.isValid() || !m_style) return 0; return doRowCount(); } bool AbstractStyleElementModel::isMainStyle() const { QStyle *style = qApp->style(); forever { if (style == m_style) return true; QProxyStyle *proxy = qobject_cast(style); if (!proxy) return false; style = proxy->baseStyle(); } } QStyle* AbstractStyleElementModel::effectiveStyle() const { return (isMainStyle() && DynamicProxyStyle::exists()) ? DynamicProxyStyle::instance() : m_style; } gammaray-2.11.3/plugins/styleinspector/abstractstyleelementmodel.h000066400000000000000000000047501413310443000255730ustar00rootroot00000000000000/* abstractstyleelementmodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2012-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_STYLEINSPECTOR_ABSTRACTSTYLEELEMENTMODEL_H #define GAMMARAY_STYLEINSPECTOR_ABSTRACTSTYLEELEMENTMODEL_H #include #include QT_BEGIN_NAMESPACE class QStyle; QT_END_NAMESPACE namespace GammaRay { /** * Base class for all models showing style elements. */ class AbstractStyleElementModel : public QAbstractTableModel { Q_OBJECT public: explicit AbstractStyleElementModel(QObject *parent = nullptr); void setStyle(QStyle *style); QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; protected: virtual QVariant doData(int row, int column, int role) const = 0; virtual int doColumnCount() const = 0; virtual int doRowCount() const = 0; /** Returns @c true if we are looking at the primary style of the application * ie. the one set in QApplication. This takes proxy styles into account. */ bool isMainStyle() const; /*! Returns the style of which we display the values, ie. the one set via * setStyle, or if present its wrapping DynamicProxyStyle. */ QStyle* effectiveStyle() const; protected: QPointer m_style; }; } #endif // GAMMARAY_ABSTRACTSTYLEELEMENTMODEL_H gammaray-2.11.3/plugins/styleinspector/abstractstyleelementstatetable.cpp000066400000000000000000000054601413310443000271550ustar00rootroot00000000000000/* abstractstyleelementstatetable.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2012-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "abstractstyleelementstatetable.h" #include "styleoption.h" #include "styleinspectorinterface.h" #include #include #include #include #include using namespace GammaRay; AbstractStyleElementStateTable::AbstractStyleElementStateTable(QObject *parent) : AbstractStyleElementModel(parent) , m_interface(ObjectBroker::object()) { connect(m_interface, &StyleInspectorInterface::cellSizeChanged, this, &AbstractStyleElementStateTable::cellSizeChanged); } void AbstractStyleElementStateTable::cellSizeChanged() { emit dataChanged(index(0, 0), index(rowCount() - 1, columnCount() - 1)); } int AbstractStyleElementStateTable::doColumnCount() const { return StyleOption::stateCount(); } QVariant AbstractStyleElementStateTable::doData(int row, int column, int role) const { Q_UNUSED(column); Q_UNUSED(row); if (role == Qt::SizeHintRole) return m_interface->cellSizeHint(); return QVariant(); } QVariant AbstractStyleElementStateTable::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal && (role == Qt::DisplayRole || role == Qt::ToolTipRole)) return StyleOption::stateDisplayName(section); return QAbstractItemModel::headerData(section, orientation, role); } void AbstractStyleElementStateTable::fillStyleOption(QStyleOption *option, int column) const { option->rect = QRect(0, 0, m_interface->cellWidth(), m_interface->cellHeight()); option->palette = qApp->palette(); option->state = StyleOption::prettyState(column); } gammaray-2.11.3/plugins/styleinspector/abstractstyleelementstatetable.h000066400000000000000000000045111413310443000266160ustar00rootroot00000000000000/* abstractstyleelementstatetable.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2012-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_STYLEINSPECTOR_ABSTRACTSTYLEELEMENTSTATETABLE_H #define GAMMARAY_STYLEINSPECTOR_ABSTRACTSTYLEELEMENTSTATETABLE_H #include "abstractstyleelementmodel.h" #include QT_BEGIN_NAMESPACE class QStyleOption; class QRect; class QPainter; QT_END_NAMESPACE namespace GammaRay { class StyleInspectorInterface; /** * Base class for style element x style option state tables. * Covers the state part, sub-classes need to fill in the corresponding rows. */ class AbstractStyleElementStateTable : public GammaRay::AbstractStyleElementModel { Q_OBJECT public: explicit AbstractStyleElementStateTable(QObject *parent = nullptr); QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; protected: int doColumnCount() const override; QVariant doData(int row, int column, int role) const override; /// standard setup for the style option used in a cell in column @p column void fillStyleOption(QStyleOption *option, int column) const; protected: StyleInspectorInterface *m_interface; private slots: void cellSizeChanged(); }; } #endif // GAMMARAY_ABSTRACTSTYLEELEMENTSTATETABLE_H gammaray-2.11.3/plugins/styleinspector/complexcontrolmodel.cpp000066400000000000000000000131731413310443000247370ustar00rootroot00000000000000/* complexcontrolmodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2012-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "complexcontrolmodel.h" #include "styleoption.h" #include "styleinspectorinterface.h" #include #include #include #include using namespace GammaRay; struct complex_control_element_t { const char *name; QStyle::ComplexControl control; QStyleOption *(*styleOptionFactory)(); QStyle::SubControls subControls; }; #define MAKE_CC2(control, factory) { #control, QStyle:: control, &StyleOption:: factory, QStyle::SubControls() } #define MAKE_CC3(control, factory, subControls) { #control, QStyle:: control, \ &StyleOption:: factory, subControls } static const complex_control_element_t complexControlElements[] = { MAKE_CC3(CC_SpinBox, makeSpinBoxStyleOption, QStyle::SC_SpinBoxUp | QStyle::SC_SpinBoxDown | QStyle::SC_SpinBoxFrame | QStyle::SC_SpinBoxEditField), MAKE_CC3(CC_ComboBox, makeComboBoxStyleOption, QStyle::SC_ComboBoxFrame | QStyle::SC_ComboBoxArrow | QStyle::SC_ComboBoxEditField | QStyle::SC_ComboBoxListBoxPopup), MAKE_CC3(CC_ScrollBar, makeSliderStyleOption, QStyle::SC_ScrollBarAddLine | QStyle::SC_ScrollBarSubLine | QStyle::SC_ScrollBarAddPage | QStyle::SC_ScrollBarSubPage | QStyle::SC_ScrollBarFirst | QStyle::SC_ScrollBarLast | QStyle::SC_ScrollBarSlider | QStyle::SC_ScrollBarGroove), MAKE_CC3(CC_Slider, makeSliderStyleOption, QStyle::SC_SliderGroove | QStyle::SC_SliderHandle | QStyle::SC_SliderTickmarks), MAKE_CC3(CC_ToolButton, makeToolButtonStyleOption, QStyle::SC_ToolButton | QStyle::SC_ToolButtonMenu), MAKE_CC3(CC_TitleBar, makeTitleBarStyleOption, QStyle::SC_TitleBarSysMenu | QStyle::SC_TitleBarMinButton | QStyle::SC_TitleBarMaxButton | QStyle::SC_TitleBarCloseButton | QStyle::SC_TitleBarLabel | QStyle::SC_TitleBarNormalButton | QStyle::SC_TitleBarShadeButton | QStyle::SC_TitleBarUnshadeButton | QStyle::SC_TitleBarContextHelpButton), MAKE_CC3(CC_Dial, makeSliderStyleOption, QStyle::SC_DialHandle | QStyle::SC_DialGroove | QStyle::SC_DialTickmarks), MAKE_CC2(CC_GroupBox, makeStyleOptionComplex), // MAKE_CC2(CC_GroupBox, makeGroupBoxStyleOption), // TODO: oxygen crashes with that due to widget access MAKE_CC3(CC_MdiControls, makeStyleOptionComplex, QStyle::SC_MdiNormalButton | QStyle::SC_MdiMinButton | QStyle::SC_MdiCloseButton) }; ComplexControlModel::ComplexControlModel(QObject *parent) : AbstractStyleElementStateTable(parent) { } QVariant ComplexControlModel::doData(int row, int column, int role) const { if (role == Qt::DecorationRole) { QPixmap pixmap(m_interface->cellSizeHint()); QPainter painter(&pixmap); Util::drawTransparencyPattern(&painter, pixmap.rect()); painter.scale(m_interface->cellZoom(), m_interface->cellZoom()); QScopedPointer opt( qstyleoption_cast( complexControlElements[row].styleOptionFactory())); Q_ASSERT(opt); fillStyleOption(opt.data(), column); m_style->drawComplexControl(complexControlElements[row].control, opt.data(), &painter); int colorIndex = 7; unsigned int nshifts = sizeof(unsigned int) * 8; for (unsigned int i = 0; i < nshifts; ++i) { QStyle::SubControl sc = static_cast(1U << i); if (sc & complexControlElements[row].subControls) { QRectF scRect = m_style->subControlRect(complexControlElements[row].control, opt.data(), sc); scRect.adjust(0, 0, -1.0 / m_interface->cellZoom(), -1.0 / m_interface->cellZoom()); if (scRect.isValid() && !scRect.isEmpty()) { // HACK: add some real color mapping painter.setPen(static_cast(colorIndex++)); painter.drawRect(scRect); } } } return pixmap; } return AbstractStyleElementStateTable::doData(row, column, role); } int ComplexControlModel::doRowCount() const { return sizeof(complexControlElements) / sizeof(complexControlElements[0]); } QVariant ComplexControlModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Vertical && role == Qt::DisplayRole) return complexControlElements[section].name; return AbstractStyleElementStateTable::headerData(section, orientation, role); } gammaray-2.11.3/plugins/styleinspector/complexcontrolmodel.h000066400000000000000000000033501413310443000244000ustar00rootroot00000000000000/* complexcontrolmodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2012-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_STYLEINSPECTOR_COMPLEXCONTROLMODEL_H #define GAMMARAY_STYLEINSPECTOR_COMPLEXCONTROLMODEL_H #include "abstractstyleelementstatetable.h" namespace GammaRay { class ComplexControlModel : public AbstractStyleElementStateTable { Q_OBJECT public: explicit ComplexControlModel(QObject *parent = nullptr); QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; protected: QVariant doData(int row, int column, int role) const override; int doRowCount() const override; }; } #endif // GAMMARAY_COMPLEXCONTROLMODEL_H gammaray-2.11.3/plugins/styleinspector/controlmodel.cpp000066400000000000000000000120431413310443000233420ustar00rootroot00000000000000/* controlmodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2012-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "controlmodel.h" #include "styleoption.h" #include "styleinspectorinterface.h" #include #include #include #include using namespace GammaRay; struct control_element_t { const char *name; QStyle::ControlElement control; QStyleOption *(*styleOptionFactory)(); }; #define MAKE_CE(control) { #control, QStyle:: control, &StyleOption::makeStyleOption } #define MAKE_CE_X(control, factory) { #control, QStyle:: control, &StyleOption:: factory } static const control_element_t controlElements[] = { MAKE_CE_X(CE_PushButton, makeButtonStyleOption), MAKE_CE_X(CE_PushButtonBevel, makeButtonStyleOption), MAKE_CE_X(CE_PushButtonLabel, makeButtonStyleOption), MAKE_CE_X(CE_CheckBox, makeButtonStyleOption), MAKE_CE_X(CE_CheckBoxLabel, makeButtonStyleOption), MAKE_CE_X(CE_RadioButton, makeButtonStyleOption), MAKE_CE_X(CE_RadioButtonLabel, makeButtonStyleOption), MAKE_CE_X(CE_TabBarTab, makeTabStyleOption), MAKE_CE_X(CE_TabBarTabShape, makeTabStyleOption), MAKE_CE_X(CE_TabBarTabLabel, makeTabStyleOption), MAKE_CE_X(CE_ProgressBar, makeProgressBarStyleOption), MAKE_CE_X(CE_ProgressBarGroove, makeProgressBarStyleOption), MAKE_CE_X(CE_ProgressBarContents, makeProgressBarStyleOption), MAKE_CE_X(CE_ProgressBarLabel, makeProgressBarStyleOption), MAKE_CE_X(CE_MenuItem, makeMenuStyleOption), MAKE_CE(CE_MenuScroller), MAKE_CE(CE_MenuVMargin), MAKE_CE(CE_MenuHMargin), MAKE_CE(CE_MenuTearoff), MAKE_CE(CE_MenuEmptyArea), MAKE_CE_X(CE_MenuBarItem, makeMenuStyleOption), MAKE_CE(CE_MenuBarEmptyArea), MAKE_CE_X(CE_ToolButtonLabel, makeToolButtonStyleOption), MAKE_CE_X(CE_Header, makeHeaderStyleOption), MAKE_CE_X(CE_HeaderSection, makeHeaderStyleOption), MAKE_CE_X(CE_HeaderLabel, makeHeaderStyleOption), MAKE_CE_X(CE_ToolBoxTab, makeToolBoxStyleOption), MAKE_CE(CE_SizeGrip), MAKE_CE(CE_Splitter), MAKE_CE(CE_RubberBand), MAKE_CE(CE_DockWidgetTitle), MAKE_CE_X(CE_ScrollBarAddLine, makeSliderStyleOption), MAKE_CE_X(CE_ScrollBarSubLine, makeSliderStyleOption), MAKE_CE_X(CE_ScrollBarAddPage, makeSliderStyleOption), MAKE_CE_X(CE_ScrollBarSubPage, makeSliderStyleOption), MAKE_CE_X(CE_ScrollBarSlider, makeSliderStyleOption), MAKE_CE_X(CE_ScrollBarFirst, makeSliderStyleOption), MAKE_CE_X(CE_ScrollBarLast, makeSliderStyleOption), MAKE_CE(CE_FocusFrame), MAKE_CE_X(CE_ComboBoxLabel, makeComboBoxStyleOption), MAKE_CE(CE_ToolBar), MAKE_CE_X(CE_ToolBoxTabShape, makeToolBoxStyleOption), MAKE_CE_X(CE_ToolBoxTabLabel, makeToolBoxStyleOption), MAKE_CE_X(CE_HeaderEmptyArea, makeHeaderStyleOption), MAKE_CE(CE_ColumnViewGrip), MAKE_CE_X(CE_ItemViewItem, makeItemViewStyleOption), MAKE_CE_X(CE_ShapedFrame, makeFrameStyleOption) }; ControlModel::ControlModel(QObject *parent) : AbstractStyleElementStateTable(parent) { } QVariant ControlModel::doData(int row, int column, int role) const { if (role == Qt::DecorationRole) { QPixmap pixmap(m_interface->cellSizeHint()); QPainter painter(&pixmap); Util::drawTransparencyPattern(&painter, pixmap.rect()); painter.scale(m_interface->cellZoom(), m_interface->cellZoom()); QScopedPointer opt(controlElements[row].styleOptionFactory()); fillStyleOption(opt.data(), column); m_style->drawControl(controlElements[row].control, opt.data(), &painter); return pixmap; } return AbstractStyleElementStateTable::doData(row, column, role); } int ControlModel::doRowCount() const { return sizeof(controlElements) / sizeof(controlElements[0]); } QVariant ControlModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Vertical && role == Qt::DisplayRole) return controlElements[section].name; return AbstractStyleElementStateTable::headerData(section, orientation, role); } gammaray-2.11.3/plugins/styleinspector/controlmodel.h000066400000000000000000000033761413310443000230200ustar00rootroot00000000000000/* controlmodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2012-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_STYLEINSPECTOR_CONTROLMODEL_H #define GAMMARAY_STYLEINSPECTOR_CONTROLMODEL_H #include "abstractstyleelementstatetable.h" namespace GammaRay { /** * Model for listing all controls provided by a QStyle. */ class ControlModel : public AbstractStyleElementStateTable { Q_OBJECT public: explicit ControlModel(QObject *parent = nullptr); QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; protected: QVariant doData(int row, int column, int role) const override; int doRowCount() const override; }; } #endif // GAMMARAY_CONTROLMODEL_H gammaray-2.11.3/plugins/styleinspector/dynamicproxystyle.cpp000066400000000000000000000052701413310443000244540ustar00rootroot00000000000000/* dynamicproxystyle.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2012-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "dynamicproxystyle.h" #include using namespace GammaRay; QPointer DynamicProxyStyle::s_instance; DynamicProxyStyle::DynamicProxyStyle(QStyle *baseStyle) : QProxyStyle(baseStyle) { s_instance = QPointer(this); } DynamicProxyStyle *DynamicProxyStyle::instance() { if (!s_instance) insertProxyStyle(); return s_instance.data(); } bool DynamicProxyStyle::exists() { return s_instance; } void DynamicProxyStyle::insertProxyStyle() { // TODO: if the current style is a CSS proxy, add us underneath // to avoid Qt adding yet another CSS proxy on top qApp->setStyle(new DynamicProxyStyle(qApp->style())); } void DynamicProxyStyle::setPixelMetric(QStyle::PixelMetric metric, int value) { m_pixelMetrics.insert(metric, value); } void DynamicProxyStyle::setStyleHint(QStyle::StyleHint hint, int value) { m_styleHints.insert(hint, value); } int DynamicProxyStyle::pixelMetric(QStyle::PixelMetric metric, const QStyleOption *option, const QWidget *widget) const { auto it = m_pixelMetrics.find(metric); if (it != m_pixelMetrics.end()) return it.value(); return QProxyStyle::pixelMetric(metric, option, widget); } int DynamicProxyStyle::styleHint(QStyle::StyleHint hint, const QStyleOption *option, const QWidget *widget, QStyleHintReturn *returnData) const { const auto it = m_styleHints.find(hint); if (it != m_styleHints.end()) return it.value(); return QProxyStyle::styleHint(hint, option, widget, returnData); } gammaray-2.11.3/plugins/styleinspector/dynamicproxystyle.h000066400000000000000000000042661413310443000241250ustar00rootroot00000000000000/* dynamicproxystyle.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2012-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_STYLEINSPECTOR_DYNAMICPROXYSTYLE_H #define GAMMARAY_STYLEINSPECTOR_DYNAMICPROXYSTYLE_H #include #include #include namespace GammaRay { /** * A proxy style that allows runtime-editing of various parameters. */ class DynamicProxyStyle : public QProxyStyle { Q_OBJECT public: explicit DynamicProxyStyle(QStyle *baseStyle); static DynamicProxyStyle *instance(); static bool exists(); static void insertProxyStyle(); void setPixelMetric(PixelMetric metric, int value); void setStyleHint(StyleHint hint, int value); int pixelMetric(PixelMetric metric, const QStyleOption *option = nullptr, const QWidget *widget = nullptr) const override; int styleHint(QStyle::StyleHint hint, const QStyleOption *option, const QWidget *widget, QStyleHintReturn *returnData) const override; private: QHash m_pixelMetrics; QHash m_styleHints; static QPointer s_instance; }; } #endif // GAMMARAY_DYNAMICPROXYSTYLE_H gammaray-2.11.3/plugins/styleinspector/gammaray_styleinspector.json000066400000000000000000000001311413310443000257700ustar00rootroot00000000000000{ "id": "gammaray_styleinspector", "name": "Styles", "types": [ "QStyle" ] } gammaray-2.11.3/plugins/styleinspector/pixelmetricmodel.cpp000066400000000000000000000144601413310443000242140ustar00rootroot00000000000000/* pixelmetricmodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2012-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "pixelmetricmodel.h" #include "dynamicproxystyle.h" #include using namespace GammaRay; struct pixel_metric_t { const char *name; QStyle::PixelMetric pixelMetric; }; #define MAKE_PM(metric) { #metric, QStyle:: metric } static const pixel_metric_t pixelMetrics[] = { MAKE_PM(PM_ButtonMargin), MAKE_PM(PM_ButtonDefaultIndicator), MAKE_PM(PM_MenuButtonIndicator), MAKE_PM(PM_ButtonShiftHorizontal), MAKE_PM(PM_ButtonShiftVertical), MAKE_PM(PM_DefaultFrameWidth), MAKE_PM(PM_SpinBoxFrameWidth), MAKE_PM(PM_ComboBoxFrameWidth), MAKE_PM(PM_MaximumDragDistance), MAKE_PM(PM_ScrollBarExtent), MAKE_PM(PM_ScrollBarSliderMin), MAKE_PM(PM_SliderThickness), MAKE_PM(PM_SliderControlThickness), MAKE_PM(PM_SliderLength), MAKE_PM(PM_SliderTickmarkOffset), MAKE_PM(PM_SliderSpaceAvailable), MAKE_PM(PM_DockWidgetSeparatorExtent), MAKE_PM(PM_DockWidgetHandleExtent), MAKE_PM(PM_DockWidgetFrameWidth), MAKE_PM(PM_TabBarTabOverlap), MAKE_PM(PM_TabBarTabHSpace), MAKE_PM(PM_TabBarTabVSpace), MAKE_PM(PM_TabBarBaseHeight), MAKE_PM(PM_TabBarBaseOverlap), MAKE_PM(PM_ProgressBarChunkWidth), MAKE_PM(PM_SplitterWidth), MAKE_PM(PM_TitleBarHeight), MAKE_PM(PM_MenuScrollerHeight), MAKE_PM(PM_MenuHMargin), MAKE_PM(PM_MenuVMargin), MAKE_PM(PM_MenuPanelWidth), MAKE_PM(PM_MenuTearoffHeight), MAKE_PM(PM_MenuDesktopFrameWidth), MAKE_PM(PM_MenuBarPanelWidth), MAKE_PM(PM_MenuBarItemSpacing), MAKE_PM(PM_MenuBarVMargin), MAKE_PM(PM_MenuBarHMargin), MAKE_PM(PM_IndicatorWidth), MAKE_PM(PM_IndicatorHeight), MAKE_PM(PM_ExclusiveIndicatorWidth), MAKE_PM(PM_ExclusiveIndicatorHeight), MAKE_PM(PM_DialogButtonsSeparator), MAKE_PM(PM_DialogButtonsButtonWidth), MAKE_PM(PM_DialogButtonsButtonHeight), MAKE_PM(PM_MdiSubWindowFrameWidth), MAKE_PM(PM_MdiSubWindowMinimizedWidth), MAKE_PM(PM_HeaderMargin), MAKE_PM(PM_HeaderMarkSize), MAKE_PM(PM_HeaderGripMargin), MAKE_PM(PM_TabBarTabShiftHorizontal), MAKE_PM(PM_TabBarTabShiftVertical), MAKE_PM(PM_TabBarScrollButtonWidth), MAKE_PM(PM_ToolBarFrameWidth), MAKE_PM(PM_ToolBarHandleExtent), MAKE_PM(PM_ToolBarItemSpacing), MAKE_PM(PM_ToolBarItemMargin), MAKE_PM(PM_ToolBarSeparatorExtent), MAKE_PM(PM_ToolBarExtensionExtent), MAKE_PM(PM_SpinBoxSliderHeight), MAKE_PM(PM_DefaultTopLevelMargin), MAKE_PM(PM_DefaultChildMargin), MAKE_PM(PM_DefaultLayoutSpacing), MAKE_PM(PM_ToolBarIconSize), MAKE_PM(PM_ListViewIconSize), MAKE_PM(PM_IconViewIconSize), MAKE_PM(PM_SmallIconSize), MAKE_PM(PM_LargeIconSize), MAKE_PM(PM_FocusFrameVMargin), MAKE_PM(PM_FocusFrameHMargin), MAKE_PM(PM_ToolTipLabelFrameWidth), MAKE_PM(PM_CheckBoxLabelSpacing), MAKE_PM(PM_TabBarIconSize), MAKE_PM(PM_SizeGripSize), MAKE_PM(PM_DockWidgetTitleMargin), MAKE_PM(PM_MessageBoxIconSize), MAKE_PM(PM_ButtonIconSize), MAKE_PM(PM_DockWidgetTitleBarButtonMargin), MAKE_PM(PM_RadioButtonLabelSpacing), MAKE_PM(PM_LayoutLeftMargin), MAKE_PM(PM_LayoutTopMargin), MAKE_PM(PM_LayoutRightMargin), MAKE_PM(PM_LayoutBottomMargin), MAKE_PM(PM_LayoutHorizontalSpacing), MAKE_PM(PM_LayoutVerticalSpacing), MAKE_PM(PM_TabBar_ScrollButtonOverlap), MAKE_PM(PM_TextCursorWidth), MAKE_PM(PM_TabCloseIndicatorWidth), MAKE_PM(PM_TabCloseIndicatorHeight), MAKE_PM(PM_ScrollView_ScrollBarSpacing), MAKE_PM(PM_SubMenuOverlap) }; PixelMetricModel::PixelMetricModel(QObject *parent) : AbstractStyleElementModel(parent) { } QVariant PixelMetricModel::doData(int row, int column, int role) const { if (role == Qt::DisplayRole || role == Qt::EditRole) { switch (column) { case 0: return pixelMetrics[row].name; case 1: return effectiveStyle()->pixelMetric(pixelMetrics[row].pixelMetric); } } return QVariant(); } int PixelMetricModel::doColumnCount() const { return 2; } int PixelMetricModel::doRowCount() const { return sizeof(pixelMetrics) / sizeof(pixel_metric_t); } QVariant PixelMetricModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { switch (section) { case 0: return tr("Metric"); case 1: return tr("Default Value"); } } return QAbstractItemModel::headerData(section, orientation, role); } Qt::ItemFlags PixelMetricModel::flags(const QModelIndex &index) const { const Qt::ItemFlags baseFlags = QAbstractItemModel::flags(index); if (index.isValid() && index.column() == 1 && isMainStyle()) return baseFlags | Qt::ItemIsEditable; return baseFlags; } bool PixelMetricModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (!index.isValid() || index.column() != 1 || !value.isValid() || !value.canConvert(QVariant::Int) || role != Qt::EditRole) return false; DynamicProxyStyle::instance()->setPixelMetric( pixelMetrics[index.row()].pixelMetric, value.toInt()); emit dataChanged(index, index); return true; } gammaray-2.11.3/plugins/styleinspector/pixelmetricmodel.h000066400000000000000000000037521413310443000236630ustar00rootroot00000000000000/* pixelmetricmodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2012-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_STYLEINSPECTOR_PIXELMETRICMODEL_H #define GAMMARAY_STYLEINSPECTOR_PIXELMETRICMODEL_H #include "abstractstyleelementmodel.h" namespace GammaRay { /** * Lists all pixel metric values of a given QStyle. */ class PixelMetricModel : public AbstractStyleElementModel { Q_OBJECT public: explicit PixelMetricModel(QObject *parent = nullptr); QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; Qt::ItemFlags flags(const QModelIndex &index) const override; bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; protected: QVariant doData(int row, int column, int role) const override; int doColumnCount() const override; int doRowCount() const override; }; } #endif // GAMMARAY_PIXELMETRICMODEL_H gammaray-2.11.3/plugins/styleinspector/primitivemodel.cpp000066400000000000000000000117401413310443000236750ustar00rootroot00000000000000/* primitivemodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2012-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "primitivemodel.h" #include "styleoption.h" #include "styleinspectorinterface.h" #include #include #include #include using namespace GammaRay; struct primitive_element_t { const char *name; QStyle::PrimitiveElement primitive; QStyleOption *(*styleOptionFactory)(); }; #define MAKE_PE(primitive) { #primitive, QStyle:: primitive, &StyleOption::makeStyleOption } #define MAKE_PE_X(primitive, factory) { #primitive, QStyle:: primitive, &StyleOption:: factory } static const primitive_element_t primititveElements[] = { MAKE_PE_X(PE_Frame, makeFrameStyleOption), MAKE_PE(PE_FrameDefaultButton), MAKE_PE_X(PE_FrameDockWidget, makeFrameStyleOption), MAKE_PE(PE_FrameFocusRect), MAKE_PE_X(PE_FrameGroupBox, makeFrameStyleOption), MAKE_PE_X(PE_FrameLineEdit, makeFrameStyleOption), MAKE_PE_X(PE_FrameMenu, makeFrameStyleOption), MAKE_PE(PE_FrameStatusBarItem), MAKE_PE_X(PE_FrameTabWidget, makeTabWidgetFrameStyleOption), MAKE_PE_X(PE_FrameWindow, makeFrameStyleOption), MAKE_PE(PE_FrameButtonBevel), MAKE_PE(PE_FrameButtonTool), MAKE_PE_X(PE_FrameTabBarBase, makeTabBarBaseStyleOption), MAKE_PE_X(PE_PanelButtonCommand, makeButtonStyleOption), MAKE_PE(PE_PanelButtonBevel), MAKE_PE(PE_PanelButtonTool), MAKE_PE(PE_PanelMenuBar), MAKE_PE(PE_PanelToolBar), MAKE_PE_X(PE_PanelLineEdit, makeFrameStyleOption), MAKE_PE(PE_IndicatorArrowDown), MAKE_PE(PE_IndicatorArrowLeft), MAKE_PE(PE_IndicatorArrowRight), MAKE_PE(PE_IndicatorArrowUp), MAKE_PE(PE_IndicatorBranch), MAKE_PE(PE_IndicatorButtonDropDown), MAKE_PE(PE_IndicatorViewItemCheck), MAKE_PE_X(PE_IndicatorCheckBox, makeButtonStyleOption), MAKE_PE(PE_IndicatorDockWidgetResizeHandle), MAKE_PE_X(PE_IndicatorHeaderArrow, makeHeaderStyleOption), MAKE_PE(PE_IndicatorMenuCheckMark), MAKE_PE(PE_IndicatorProgressChunk), MAKE_PE_X(PE_IndicatorRadioButton, makeButtonStyleOption), MAKE_PE_X(PE_IndicatorSpinDown, makeSpinBoxStyleOption), MAKE_PE_X(PE_IndicatorSpinMinus, makeSpinBoxStyleOption), MAKE_PE_X(PE_IndicatorSpinPlus, makeSpinBoxStyleOption), MAKE_PE_X(PE_IndicatorSpinUp, makeSpinBoxStyleOption), MAKE_PE(PE_IndicatorToolBarHandle), MAKE_PE(PE_IndicatorToolBarSeparator), MAKE_PE(PE_PanelTipLabel), MAKE_PE_X(PE_IndicatorTabTear, makeTabStyleOption), MAKE_PE(PE_PanelScrollAreaCorner), MAKE_PE(PE_Widget), MAKE_PE(PE_IndicatorColumnViewArrow), MAKE_PE(PE_IndicatorItemViewItemDrop), MAKE_PE_X(PE_PanelItemViewItem, makeItemViewStyleOption), MAKE_PE(PE_PanelItemViewRow), MAKE_PE(PE_PanelStatusBar), MAKE_PE(PE_IndicatorTabClose), MAKE_PE(PE_PanelMenu) }; PrimitiveModel::PrimitiveModel(QObject *parent) : AbstractStyleElementStateTable(parent) { } QVariant PrimitiveModel::doData(int row, int column, int role) const { if (role == Qt::DecorationRole) { QPixmap pixmap(m_interface->cellSizeHint()); QPainter painter(&pixmap); Util::drawTransparencyPattern(&painter, pixmap.rect()); painter.scale(m_interface->cellZoom(), m_interface->cellZoom()); QScopedPointer opt((primititveElements[row].styleOptionFactory)()); fillStyleOption(opt.data(), column); m_style->drawPrimitive(primititveElements[row].primitive, opt.data(), &painter); return pixmap; } return AbstractStyleElementStateTable::doData(row, column, role); } int PrimitiveModel::doRowCount() const { return sizeof(primititveElements) / sizeof(primititveElements[0]); } QVariant PrimitiveModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Vertical && role == Qt::DisplayRole) return primititveElements[section].name; return AbstractStyleElementStateTable::headerData(section, orientation, role); } gammaray-2.11.3/plugins/styleinspector/primitivemodel.h000066400000000000000000000033711413310443000233430ustar00rootroot00000000000000/* primitivemodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2012-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_STYLEINSPECTOR_PRIMITIVEMODEL_H #define GAMMARAY_STYLEINSPECTOR_PRIMITIVEMODEL_H #include "abstractstyleelementstatetable.h" namespace GammaRay { /** * Model for primitive style elements. */ class PrimitiveModel : public AbstractStyleElementStateTable { Q_OBJECT public: explicit PrimitiveModel(QObject *parent = nullptr); QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; protected: QVariant doData(int row, int column, int role) const override; int doRowCount() const override; }; } #endif // GAMMARAY_PRIMITIVEMODEL_H gammaray-2.11.3/plugins/styleinspector/standardiconmodel.cpp000066400000000000000000000116231413310443000243360ustar00rootroot00000000000000/* standardiconmodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2012-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "standardiconmodel.h" #include using namespace GammaRay; StandardIconModel::StandardIconModel(QObject *parent) : AbstractStyleElementModel(parent) { } QVariant StandardIconModel::dataForStandardIcon(QStyle::StandardPixmap stdPix, const QString &name, int column, int role) const { if (column == 0) { if (role == Qt::DisplayRole) return name; } else if (column == 1) { if (role == Qt::DecorationRole) return m_style->standardIcon(stdPix); else if (role == Qt::DisplayRole) return VariantHandler::displayString(m_style->standardIcon(stdPix)); } return QVariant(); } #define MAKE_SP(stdPix) \ if (row == QStyle:: stdPix) \ return dataForStandardIcon(QStyle:: stdPix, QStringLiteral(#stdPix), column, role) QVariant StandardIconModel::doData(int row, int column, int role) const { MAKE_SP(SP_TitleBarMenuButton); MAKE_SP(SP_TitleBarMinButton); MAKE_SP(SP_TitleBarMaxButton); MAKE_SP(SP_TitleBarCloseButton); MAKE_SP(SP_TitleBarNormalButton); MAKE_SP(SP_TitleBarShadeButton); MAKE_SP(SP_TitleBarUnshadeButton); MAKE_SP(SP_TitleBarContextHelpButton); MAKE_SP(SP_DockWidgetCloseButton); MAKE_SP(SP_MessageBoxInformation); MAKE_SP(SP_MessageBoxWarning); MAKE_SP(SP_MessageBoxCritical); MAKE_SP(SP_MessageBoxQuestion); MAKE_SP(SP_DesktopIcon); MAKE_SP(SP_TrashIcon); MAKE_SP(SP_ComputerIcon); MAKE_SP(SP_DriveFDIcon); MAKE_SP(SP_DriveHDIcon); MAKE_SP(SP_DriveCDIcon); MAKE_SP(SP_DriveDVDIcon); MAKE_SP(SP_DriveNetIcon); MAKE_SP(SP_DirOpenIcon); MAKE_SP(SP_DirClosedIcon); MAKE_SP(SP_DirLinkIcon); MAKE_SP(SP_FileIcon); MAKE_SP(SP_FileLinkIcon); MAKE_SP(SP_ToolBarHorizontalExtensionButton); MAKE_SP(SP_ToolBarVerticalExtensionButton); MAKE_SP(SP_FileDialogStart); MAKE_SP(SP_FileDialogEnd); MAKE_SP(SP_FileDialogToParent); MAKE_SP(SP_FileDialogNewFolder); MAKE_SP(SP_FileDialogDetailedView); MAKE_SP(SP_FileDialogInfoView); MAKE_SP(SP_FileDialogContentsView); MAKE_SP(SP_FileDialogListView); MAKE_SP(SP_FileDialogBack); MAKE_SP(SP_DirIcon); MAKE_SP(SP_DirLinkOpenIcon); MAKE_SP(SP_DialogOkButton); MAKE_SP(SP_DialogCancelButton); MAKE_SP(SP_DialogHelpButton); MAKE_SP(SP_DialogOpenButton); MAKE_SP(SP_DialogSaveButton); MAKE_SP(SP_DialogCloseButton); MAKE_SP(SP_DialogApplyButton); MAKE_SP(SP_DialogResetButton); MAKE_SP(SP_DialogDiscardButton); MAKE_SP(SP_DialogYesButton); MAKE_SP(SP_DialogNoButton); MAKE_SP(SP_ArrowUp); MAKE_SP(SP_ArrowDown); MAKE_SP(SP_ArrowLeft); MAKE_SP(SP_ArrowRight); MAKE_SP(SP_ArrowBack); MAKE_SP(SP_ArrowForward); MAKE_SP(SP_DirHomeIcon); MAKE_SP(SP_CommandLink); MAKE_SP(SP_VistaShield); MAKE_SP(SP_BrowserReload); MAKE_SP(SP_BrowserStop); MAKE_SP(SP_MediaPlay); MAKE_SP(SP_MediaStop); MAKE_SP(SP_MediaPause); MAKE_SP(SP_MediaSkipForward); MAKE_SP(SP_MediaSkipBackward); MAKE_SP(SP_MediaSeekForward); MAKE_SP(SP_MediaSeekBackward); MAKE_SP(SP_MediaVolume); MAKE_SP(SP_MediaVolumeMuted); MAKE_SP(SP_LineEditClearButton); return QVariant(); } int StandardIconModel::doColumnCount() const { return 2; } int StandardIconModel::doRowCount() const { return QStyle::SP_LineEditClearButton + 1; } QVariant StandardIconModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { switch (section) { case 0: return tr("Name"); case 1: return tr("Icon"); } } return QAbstractItemModel::headerData(section, orientation, role); } gammaray-2.11.3/plugins/styleinspector/standardiconmodel.h000066400000000000000000000037321413310443000240050ustar00rootroot00000000000000/* standardiconmodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2012-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_STYLEINSPECTOR_STANDARDICONMODEL_H #define GAMMARAY_STYLEINSPECTOR_STANDARDICONMODEL_H #include "abstractstyleelementmodel.h" #include namespace GammaRay { /** * Lists all standard icons of a style. */ class StandardIconModel : public AbstractStyleElementModel { Q_OBJECT public: explicit StandardIconModel(QObject *parent = nullptr); QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; protected: QVariant doData(int row, int column, int role) const override; int doColumnCount() const override; int doRowCount() const override; private: QVariant dataForStandardIcon(QStyle::StandardPixmap stdPix, const QString &name, int column, int role) const; }; } #endif // GAMMARAY_STANDARDICONMODEL_H gammaray-2.11.3/plugins/styleinspector/styleelementproxymodel.cpp000066400000000000000000000032641413310443000255030ustar00rootroot00000000000000/* styleelementproxymodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "styleelementproxymodel.h" using namespace GammaRay; StyleElementProxyModel::StyleModelProxy(QObject *parent) : QIdentityProxyModel(parent), m_sizeHint( 64, 64) { } void StyleElementProxyModel::setWidth(int width) { m_sizeHint.setWidth(width); } void StyleElementProxyModel::setHeight(int height) { m_sizeHint.setHeight(height); } QVariant StyleElementProxyModel::data(const QModelIndex &proxyIndex, int role) const { if (role == Qt::SizeHintRole) return m_sizeHint; return QAbstractProxyModel::data(proxyIndex, role); } gammaray-2.11.3/plugins/styleinspector/styleelementproxymodel.h000066400000000000000000000033341413310443000251460ustar00rootroot00000000000000/* styleelementproxymodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_STYLEINSPECTOR_STYLEELEMENTPROXYMODEL_H #define GAMMARAY_STYLEINSPECTOR_STYLEELEMENTPROXYMODEL_H #include #include namespace GammaRay { class StyleElementProxyModel : public QIdentityProxyModel { Q_OBJECT public: explicit StyleModelProxy(QObject *parent = nullptr); virtual QVariant data(const QModelIndex &proxyIndex, int role = Qt::DisplayRole) const; public slots: void setWidth(int width); void setHeight(int height); void setZoomFactor(int zoom); private: QSize m_sizeHint; }; } #endif // GAMMARAY_STYLEELEMENTPROXYMODEL_H gammaray-2.11.3/plugins/styleinspector/styleelementstatetablepage.cpp000066400000000000000000000072611413310443000262670ustar00rootroot00000000000000/* styleelementstatetablepage.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2012-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "styleelementstatetablepage.h" #include "ui_styleelementstatetablepage.h" #include "abstractstyleelementstatetable.h" #include "styleinspectorclient.h" #include static QObject *createStyleInspectorClient(const QString & /*name*/, QObject *parent) { return new GammaRay::StyleInspectorClient(parent); } using namespace GammaRay; StyleElementStateTablePage::StyleElementStateTablePage(QWidget *parent) : QWidget(parent) , ui(new Ui::StyleElementStateTablePage) , m_interface(nullptr) { ObjectBroker::registerClientObjectFactoryCallback( createStyleInspectorClient); m_interface = ObjectBroker::object(); ui->setupUi(this); ui->tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Fixed); ui->tableView->verticalHeader()->setSectionResizeMode(QHeaderView::Fixed); connect(ui->widthBox, static_cast(&QSpinBox::valueChanged), m_interface, &StyleInspectorInterface::setCellWidth); connect(ui->widthBox, static_cast(&QSpinBox::valueChanged), this, &StyleElementStateTablePage::updateCellSize); connect(ui->heightBox, static_cast(&QSpinBox::valueChanged), m_interface, &StyleInspectorInterface::setCellHeight); connect(ui->heightBox, static_cast(&QSpinBox::valueChanged), this, &StyleElementStateTablePage::updateCellSize); connect(ui->zoomSlider, &QAbstractSlider::valueChanged, m_interface, &StyleInspectorInterface::setCellZoom); connect(ui->zoomSlider, &QAbstractSlider::valueChanged, this, &StyleElementStateTablePage::updateCellSize); updateCellSize(); } void StyleElementStateTablePage::showEvent(QShowEvent *show) { ui->widthBox->setValue(m_interface->cellWidth()); ui->heightBox->setValue(m_interface->cellHeight()); ui->zoomSlider->setValue(m_interface->cellZoom()); QWidget::showEvent(show); } StyleElementStateTablePage::~StyleElementStateTablePage() { delete ui; } void StyleElementStateTablePage::setModel(QAbstractItemModel *model) { ui->tableView->setModel(model); } void StyleElementStateTablePage::updateCellSize() { ui->tableView->verticalHeader()->setDefaultSectionSize(m_interface->cellSizeHint().height() + 4); ui->tableView->horizontalHeader()->setDefaultSectionSize(m_interface->cellSizeHint().width() + 4); } gammaray-2.11.3/plugins/styleinspector/styleelementstatetablepage.h000066400000000000000000000037611413310443000257350ustar00rootroot00000000000000/* styleelementstatetablepage.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2012-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_STYLEINSPECTOR_STYLEELEMENTSTATETABLEPAGE_H #define GAMMARAY_STYLEINSPECTOR_STYLEELEMENTSTATETABLEPAGE_H #include QT_BEGIN_NAMESPACE class QAbstractItemModel; QT_END_NAMESPACE namespace GammaRay { class StyleInspectorInterface; namespace Ui { class StyleElementStateTablePage; } /** * Tab page for showing a style element x state table and corresponding config UI. */ class StyleElementStateTablePage : public QWidget { Q_OBJECT public: explicit StyleElementStateTablePage(QWidget *parent = nullptr); ~StyleElementStateTablePage() override; void setModel(QAbstractItemModel *model); protected: void showEvent(QShowEvent *show) override; private slots: void updateCellSize(); private: Ui::StyleElementStateTablePage *ui; StyleInspectorInterface *m_interface; }; } #endif // GAMMARAY_STYLEELEMENTSTATETABLEPAGE_H gammaray-2.11.3/plugins/styleinspector/styleelementstatetablepage.ui000066400000000000000000000054761413310443000261300ustar00rootroot00000000000000 GammaRay::StyleElementStateTablePage 0 0 400 300 0 QAbstractItemView::NoSelection false Cell &Width: widthBox px 999 Cell &Height: heightBox px 999 &Zoom: zoomSlider 1 8 1 Qt::Horizontal Qt::Horizontal 40 20 gammaray-2.11.3/plugins/styleinspector/stylehintmodel.cpp000066400000000000000000000476241413310443000237220ustar00rootroot00000000000000/* stylehintmodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "stylehintmodel.h" #include "styleoption.h" #include "dynamicproxystyle.h" #include #include #include #include #include #include #include #include #include #include #include #include using namespace GammaRay; namespace StyleHintType { enum Type { Bool, Int, Color, Char, FrameStyle, // this one is merged from two enums, thus not working out of the box with our enum editor Alignment, EventType, FocusPolicy, ColorRole, ElideMode, MouseButtons, LayoutDirection, TextInteractionFlags, WizardStyle, TabPosition, FormWrapPolicy, FormGrowthPolicy, ToolButtonStyle, RequestInputPanel, ScrollMode, LastBasicType = Char, FirstEnumType = Alignment }; } struct StyleHintTypeInfo { const char *name; const QMetaObject *metaObject; }; // ### must be the same order as the above enum static const struct StyleHintTypeInfo style_hint_type_table[] = { { "Qt::Alignment", nullptr }, { "QEvent::Type", &QEvent::staticMetaObject }, { "Qt::FocusPolicy", nullptr }, { "QPalette::ColorRole", &QPalette::staticMetaObject }, { "Qt::TextElideMode", nullptr }, { "Qt::MouseButtons", nullptr }, { "Qt::LayoutDirection", nullptr }, { "Qt::TextInteractionFlags", nullptr }, { "QWizard::WizardStyle", &QWizard::staticMetaObject }, { "QTabWidget::TabPosition", &QTabWidget::staticMetaObject }, { "QFormLayout::RowWrapPolicy", &QFormLayout::staticMetaObject }, { "QFormLayout::FieldGrowthPolicy", &QFormLayout::staticMetaObject }, { "Qt::ToolButtonStyle", nullptr }, { "QStyle::RequestSoftwareInputPanel", &QStyle::staticMetaObject }, { "QAbstractItemView::ScrollMode", &QAbstractItemView::staticMetaObject } }; namespace StyleHintExtraType { enum Type { None, Region, Variant }; } struct StyleHintInfo { QStyle::StyleHint hint; const char *name; StyleHintType::Type type; StyleHintExtraType::Type extraType; }; #define SH(h, t, x) { QStyle::h, #h, StyleHintType::t, StyleHintExtraType::x }, static const StyleHintInfo style_hint_table[] = { SH(SH_EtchDisabledText, Bool, None ) SH(SH_DitherDisabledText, Bool, None ) SH(SH_ScrollBar_MiddleClickAbsolutePosition, Bool, None ) SH(SH_ScrollBar_ScrollWhenPointerLeavesControl, Bool, None ) SH(SH_TabBar_SelectMouseType, EventType, None ) SH(SH_TabBar_Alignment, Alignment, None ) SH(SH_Header_ArrowAlignment, Alignment, None ) SH(SH_Slider_SnapToValue, Bool, None ) SH(SH_Slider_SloppyKeyEvents, Bool, None ) SH(SH_ProgressDialog_CenterCancelButton, Bool, None ) SH(SH_ProgressDialog_TextLabelAlignment, Alignment, None ) SH(SH_PrintDialog_RightAlignButtons, Bool, None ) SH(SH_MainWindow_SpaceBelowMenuBar, Int, None ) SH(SH_FontDialog_SelectAssociatedText, Bool, None ) SH(SH_Menu_AllowActiveAndDisabled, Bool, None ) SH(SH_Menu_SpaceActivatesItem, Bool, None ) SH(SH_Menu_SubMenuPopupDelay, Int, None ) SH(SH_ScrollView_FrameOnlyAroundContents, Bool, None ) SH(SH_MenuBar_AltKeyNavigation, Bool, None ) SH(SH_ComboBox_ListMouseTracking, Bool, None ) SH(SH_Menu_MouseTracking, Bool, None ) SH(SH_MenuBar_MouseTracking, Bool, None ) SH(SH_ItemView_ChangeHighlightOnFocus, Bool, None ) SH(SH_Widget_ShareActivation, Bool, None ) SH(SH_Workspace_FillSpaceOnMaximize, Bool, None ) SH(SH_ComboBox_Popup, Bool, None ) SH(SH_TitleBar_NoBorder, Bool, None ) SH(SH_Slider_StopMouseOverSlider, Bool, None ) SH(SH_BlinkCursorWhenTextSelected, Bool, None ) SH(SH_RichText_FullWidthSelection, Bool, None ) SH(SH_Menu_Scrollable, Bool, None ) SH(SH_GroupBox_TextLabelVerticalAlignment, Alignment, None ) SH(SH_GroupBox_TextLabelColor, Color, None ) SH(SH_Menu_SloppySubMenus, Bool, None ) SH(SH_Table_GridLineColor, Color, None ) SH(SH_LineEdit_PasswordCharacter, Char, None ) SH(SH_DialogButtons_DefaultButton, Int, None ) // TODO QDialogButtonBox::ButtonRole SH(SH_ToolBox_SelectedPageTitleBold, Bool, None ) SH(SH_TabBar_PreferNoArrows, Bool, None ) SH(SH_ScrollBar_LeftClickAbsolutePosition, Bool, None ) SH(SH_ListViewExpand_SelectMouseType, EventType, None ) SH(SH_UnderlineShortcut, Bool, None ) SH(SH_SpinBox_AnimateButton, Bool, None ) SH(SH_SpinBox_KeyPressAutoRepeatRate, Int, None ) SH(SH_SpinBox_ClickAutoRepeatRate, Int, None ) SH(SH_Menu_FillScreenWithScroll, Bool, None ) SH(SH_ToolTipLabel_Opacity, Int, None ) SH(SH_DrawMenuBarSeparator, Bool, None ) SH(SH_TitleBar_ModifyNotification, Bool, None ) SH(SH_Button_FocusPolicy, FocusPolicy, None ) SH(SH_MessageBox_UseBorderForButtonSpacing, Bool, None ) SH(SH_TitleBar_AutoRaise, Bool, None ) SH(SH_ToolButton_PopupDelay, Int, None ) SH(SH_FocusFrame_Mask, Bool, Region ) SH(SH_RubberBand_Mask, Bool, Region ) SH(SH_WindowFrame_Mask, Bool, Region ) SH(SH_SpinControls_DisableOnBounds, Bool, None ) SH(SH_Dial_BackgroundRole, ColorRole, None ) SH(SH_ComboBox_LayoutDirection, LayoutDirection, None ) SH(SH_ItemView_EllipsisLocation, Alignment, None ) SH(SH_ItemView_ShowDecorationSelected, Bool, None ) SH(SH_ItemView_ActivateItemOnSingleClick, Bool, None ) SH(SH_ScrollBar_ContextMenu, Bool, None ) SH(SH_ScrollBar_RollBetweenButtons, Bool, None ) SH(SH_Slider_AbsoluteSetButtons, MouseButtons, None ) SH(SH_Slider_PageSetButtons, MouseButtons, None ) SH(SH_Menu_KeyboardSearch, Bool, None ) SH(SH_TabBar_ElideMode, ElideMode, None ) SH(SH_DialogButtonLayout, Int, None ) // TODO QDialogButtonBox::ButtonLayout SH(SH_ComboBox_PopupFrameStyle, FrameStyle, None ) SH(SH_MessageBox_TextInteractionFlags, TextInteractionFlags, None ) SH(SH_DialogButtonBox_ButtonsHaveIcons, Bool, None ) SH(SH_SpellCheckUnderlineStyle, Int, None ) // TODO QTextCharFormat::UnderlineStyle SH(SH_MessageBox_CenterButtons, Bool, None ) SH(SH_Menu_SelectionWrap, Bool, None ) SH(SH_ItemView_MovementWithoutUpdatingSelection, Bool, None ) SH(SH_ToolTip_Mask, Bool, Region ) SH(SH_FocusFrame_AboveWidget, Bool, None ) SH(SH_TextControl_FocusIndicatorTextCharFormat, Int, Variant) SH(SH_WizardStyle, WizardStyle, None ) SH(SH_ItemView_ArrowKeysNavigateIntoChildren, Bool, None ) SH(SH_Menu_Mask , Bool, Region ) SH(SH_Menu_FlashTriggeredItem, Bool, None ) SH(SH_Menu_FadeOutOnHide, Bool, None ) SH(SH_SpinBox_ClickAutoRepeatThreshold, Int, None ) SH(SH_ItemView_PaintAlternatingRowColorsForEmptyArea, Bool, None ) SH(SH_FormLayoutWrapPolicy, FormWrapPolicy, None ) SH(SH_TabWidget_DefaultTabPosition, TabPosition, None ) SH(SH_ToolBar_Movable, Bool, None ) SH(SH_FormLayoutFieldGrowthPolicy, FormGrowthPolicy, None ) SH(SH_FormLayoutFormAlignment, Alignment, None ) SH(SH_FormLayoutLabelAlignment, Alignment, None ) SH(SH_ItemView_DrawDelegateFrame, Bool, None ) SH(SH_TabBar_CloseButtonPosition, Int, None ) // TODO QTabBar::ButtonPosition SH(SH_DockWidget_ButtonsHaveFrame, Bool, None ) SH(SH_ToolButtonStyle, ToolButtonStyle, None ) SH(SH_RequestSoftwareInputPanel, RequestInputPanel, None ) SH(SH_ScrollBar_Transient, Bool, None ) SH(SH_Menu_SupportsSections, Bool, None ) SH(SH_ToolTip_WakeUpDelay, Int, None ) SH(SH_ToolTip_FallAsleepDelay, Int, None ) SH(SH_Widget_Animate, Bool, None ) SH(SH_Splitter_OpaqueResize, Bool, None ) SH(SH_ComboBox_UseNativePopup, Bool, None ) SH(SH_LineEdit_PasswordMaskDelay, Int, None ) SH(SH_TabBar_ChangeCurrentDelay, Int, None ) SH(SH_Menu_SubMenuUniDirection, Bool, None ) SH(SH_Menu_SubMenuUniDirectionFailCount, Int, None ) SH(SH_Menu_SubMenuSloppySelectOtherActions, Bool, None ) SH(SH_Menu_SubMenuSloppyCloseTimeout, Int, None ) SH(SH_Menu_SubMenuResetWhenReenteringParent, Bool, None ) SH(SH_Menu_SubMenuDontStartSloppyOnLeave, Bool, None ) #if QT_VERSION >= QT_VERSION_CHECK(5, 7, 0) SH(SH_ItemView_ScrollMode, ScrollMode, None ) #endif }; #undef SH static const int style_hint_count = sizeof(style_hint_table) / sizeof(StyleHintInfo); StyleHintModel::StyleHintModel(QObject *parent) : AbstractStyleElementModel(parent) { } StyleHintModel::~StyleHintModel() = default; QVariant StyleHintModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { switch (section) { case 0: return tr("Style Hint"); case 1: return tr("Value"); case 2: return tr("Return Data"); } } return AbstractStyleElementModel::headerData(section, orientation, role); } #define MAKE_SH(hint) case QStyle:: hint: return QStringLiteral(#hint); QVariant StyleHintModel::doData(int row, int column, int role) const { Q_ASSERT(row >= 0 && row < style_hint_count); if (role == Qt::DisplayRole && column == 0) { return style_hint_table[row].name; } if (column == 1) { const auto hint = static_cast(row); QStyleOption option; const auto value = effectiveStyle()->styleHint(hint, &option, nullptr, nullptr); switch (role) { case Qt::DisplayRole: if (style_hint_table[row].type == StyleHintType::Bool) return QVariant(); return VariantHandler::displayString(styleHintToVariant(hint, value)); case Qt::EditRole: { const auto type = style_hint_table[row].type; if (type <= StyleHintType::LastBasicType) return styleHintToVariant(hint, value); const auto enumType = type - StyleHintType::FirstEnumType; if (type >= StyleHintType::FirstEnumType ) { if (!style_hint_type_table[enumType].name) return value; // fallback to int for older Qt without meta enums for these types const auto me = EnumUtil::metaEnum(value, style_hint_type_table[enumType].name, style_hint_type_table[enumType].metaObject); return QVariant::fromValue(EnumRepositoryServer::valueFromMetaEnum(value, me)); } return QVariant(); } case Qt::DecorationRole: return VariantHandler::decoration(styleHintToVariant(hint, value)); case Qt::CheckStateRole: if (style_hint_table[row].type == StyleHintType::Bool) return value ? Qt::Checked : Qt::Unchecked; return QVariant(); } } if (role == Qt::DisplayRole && column == 2) { return VariantHandler::displayString(styleHintData(static_cast(row))); } return QVariant(); } int StyleHintModel::doColumnCount() const { return 3; } int StyleHintModel::doRowCount() const { return style_hint_count; } QVariant StyleHintModel::styleHintToVariant(QStyle::StyleHint hint, int value) const { const auto type = style_hint_table[hint].type; switch (type) { case StyleHintType::Bool: return QVariant::fromValue(value); case StyleHintType::Int: return value; case StyleHintType::Color: return QVariant::fromValue(QColor(value)); case StyleHintType::Char: return QChar(value); case StyleHintType::FrameStyle: return QString(EnumUtil::enumToString(value & QFrame::Shadow_Mask, "QFrame::Shadow", &QFrame::staticMetaObject) + " / " + EnumUtil::enumToString(value & QFrame::Shape_Mask, "QFrame::Shape", &QFrame::staticMetaObject)); default: break; } const auto enumType = type - StyleHintType::FirstEnumType; if (type >= StyleHintType::FirstEnumType && style_hint_type_table[enumType].name) { return EnumUtil::enumToString(value, style_hint_type_table[enumType].name, style_hint_type_table[enumType].metaObject); } return value; } QVariant StyleHintModel::styleHintData(QStyle::StyleHint hint) const { switch (hint) { case QStyle::SH_TextControl_FocusIndicatorTextCharFormat: { const auto opt = StyleOption::makeFrameStyleOption(); QStyleHintReturnVariant data; effectiveStyle()->styleHint(hint, opt, nullptr, &data); delete opt; return data.variant; } case QStyle::SH_RubberBand_Mask: { QStyleOptionRubberBand opt; opt.shape = QRubberBand::Rectangle; opt.rect = QRect(0, 0, 100, 100); QStyleHintReturnMask data; effectiveStyle()->styleHint(hint, &opt, nullptr, &data); return data.region; } case QStyle::SH_FocusFrame_Mask: case QStyle::SH_WindowFrame_Mask: case QStyle::SH_ToolTip_Mask: case QStyle::SH_Menu_Mask: { QStyleOption opt; opt.rect = QRect(0, 0, 100, 100); QStyleHintReturnMask data; effectiveStyle()->styleHint(hint, &opt, nullptr, &data); return data.region; } default: break; } return QVariant(); } Qt::ItemFlags StyleHintModel::flags(const QModelIndex &index) const { const auto baseFlags = QAbstractTableModel::flags(index); if (!index.isValid() || index.column() != 1 || !isMainStyle() || style_hint_table[index.row()].extraType != StyleHintExtraType::None) { return baseFlags; } if (style_hint_table[index.row()].type != StyleHintType::FrameStyle && style_hint_table[index.row()].type != StyleHintType::Bool) { return baseFlags | Qt::ItemIsEditable; } if (style_hint_table[index.row()].type == StyleHintType::Bool) { return baseFlags | Qt::ItemIsUserCheckable; } return baseFlags; } bool StyleHintModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (!index.isValid() || index.column() != 1) return false; if (role != Qt::EditRole && role != Qt::CheckStateRole) return false; int i = value.toInt(); if (value.type() == QVariant::Color) i = value.value().rgba(); else if (value.userType() == qMetaTypeId()) i = value.value().value(); else if (role == Qt::CheckStateRole) i = (i == Qt::Checked) ? 1 : 0; DynamicProxyStyle::instance()->setStyleHint(static_cast(index.row()), i); emit dataChanged(index, index); return true; } gammaray-2.11.3/plugins/styleinspector/stylehintmodel.h000066400000000000000000000040151413310443000233520ustar00rootroot00000000000000/* stylehintmodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_STYLEHINTMODEL_H #define GAMMARAY_STYLEHINTMODEL_H #include "abstractstyleelementmodel.h" #include namespace GammaRay { class StyleHintModel : public AbstractStyleElementModel { Q_OBJECT public: explicit StyleHintModel(QObject *parent = nullptr); ~StyleHintModel() override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; Qt::ItemFlags flags(const QModelIndex &index) const override; bool setData(const QModelIndex &index, const QVariant &value, int role) override; protected: QVariant doData(int row, int column, int role) const override; int doColumnCount() const override; int doRowCount() const override; private: QVariant styleHintToVariant(QStyle::StyleHint hint, int value) const; QVariant styleHintData(QStyle::StyleHint hint) const; }; } #endif // GAMMARAY_STYLEHINTMODEL_H gammaray-2.11.3/plugins/styleinspector/styleinspector.cpp000066400000000000000000000104161413310443000237320ustar00rootroot00000000000000/* styleinspector.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2012-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "styleinspector.h" #include "complexcontrolmodel.h" #include "controlmodel.h" #include "pixelmetricmodel.h" #include "primitivemodel.h" #include "standardiconmodel.h" #include "stylehintmodel.h" #include #include #include #include #include #include using namespace GammaRay; StyleInspector::StyleInspector(Probe *probe, QObject *parent) : StyleInspectorInterface(parent) , m_primitiveModel(new PrimitiveModel(this)) , m_controlModel(new ControlModel(this)) , m_complexControlModel(new ComplexControlModel(this)) , m_pixelMetricModel(new PixelMetricModel(this)) , m_standardIconModel(new StandardIconModel(this)) , m_standardPaletteModel(new PaletteModel(this)) , m_styleHintModel(new StyleHintModel(this)) { auto *styleFilter = new ObjectTypeFilterProxyModel(this); styleFilter->setSourceModel(probe->objectListModel()); auto *singleColumnProxy = new SingleColumnObjectProxyModel(this); singleColumnProxy->setSourceModel(styleFilter); probe->registerModel(QStringLiteral("com.kdab.GammaRay.StyleList"), singleColumnProxy); QItemSelectionModel *selectionModel = ObjectBroker::selectionModel(singleColumnProxy); connect(selectionModel, &QItemSelectionModel::selectionChanged, this, &StyleInspector::styleSelected); probe->registerModel(QStringLiteral( "com.kdab.GammaRay.StyleInspector.PrimitiveModel"), m_primitiveModel); probe->registerModel(QStringLiteral( "com.kdab.GammaRay.StyleInspector.ControlModel"), m_controlModel); probe->registerModel(QStringLiteral( "com.kdab.GammaRay.StyleInspector.ComplexControlModel"), m_complexControlModel); probe->registerModel(QStringLiteral( "com.kdab.GammaRay.StyleInspector.PixelMetricModel"), m_pixelMetricModel); probe->registerModel(QStringLiteral( "com.kdab.GammaRay.StyleInspector.StandardIconModel"), m_standardIconModel); probe->registerModel(QStringLiteral( "com.kdab.GammaRay.StyleInspector.PaletteModel"), m_standardPaletteModel); probe->registerModel(QStringLiteral("com.kdab.GammaRay.StyleInspector.StyleHintModel"), m_styleHintModel); } StyleInspector::~StyleInspector() = default; void StyleInspector::styleSelected(const QItemSelection &selection) { if (selection.isEmpty()) return; const QModelIndex index = selection.first().topLeft(); QObject *obj = index.data(ObjectModel::ObjectRole).value(); QStyle *style = qobject_cast(obj); m_primitiveModel->setStyle(style); m_controlModel->setStyle(style); m_complexControlModel->setStyle(style); m_pixelMetricModel->setStyle(style); m_standardIconModel->setStyle(style); m_standardPaletteModel->setPalette(style ? style->standardPalette() : qApp->palette()); m_styleHintModel->setStyle(style); } gammaray-2.11.3/plugins/styleinspector/styleinspector.h000066400000000000000000000050341413310443000233770ustar00rootroot00000000000000/* styleinspector.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2012-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_STYLEINSPECTOR_STYLEINSPECTOR_H #define GAMMARAY_STYLEINSPECTOR_STYLEINSPECTOR_H #include #include "styleinspectorinterface.h" #include #include QT_BEGIN_NAMESPACE class QItemSelection; QT_END_NAMESPACE namespace GammaRay { class ComplexControlModel; class ControlModel; class PaletteModel; class PixelMetricModel; class PrimitiveModel; class StandardIconModel; class StyleHintModel; class StyleInspector : public StyleInspectorInterface { Q_OBJECT Q_INTERFACES(GammaRay::StyleInspectorInterface) public: explicit StyleInspector(Probe *probe, QObject *parent = nullptr); ~StyleInspector() override; private slots: void styleSelected(const QItemSelection &selection); private: PrimitiveModel *m_primitiveModel; ControlModel *m_controlModel; ComplexControlModel *m_complexControlModel; PixelMetricModel *m_pixelMetricModel; StandardIconModel *m_standardIconModel; PaletteModel *m_standardPaletteModel; StyleHintModel *m_styleHintModel; }; class StyleInspectorFactory : public QObject, public StandardToolFactory { Q_OBJECT Q_INTERFACES(GammaRay::ToolFactory) Q_PLUGIN_METADATA(IID "com.kdab.GammaRay.ToolFactory" FILE "gammaray_styleinspector.json") public: explicit StyleInspectorFactory(QObject *parent = nullptr) : QObject(parent) { } }; } #endif // GAMMARAY_STYLEINSPECTOR_H gammaray-2.11.3/plugins/styleinspector/styleinspectorclient.cpp000066400000000000000000000041551413310443000251340ustar00rootroot00000000000000/* styleinspectorclient.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "styleinspectorclient.h" #include using namespace GammaRay; StyleInspectorClient::StyleInspectorClient(QObject *parent) : StyleInspectorInterface(parent) { // make sure the remote server side uses our initial values setCellHeight(cellHeight()); setCellWidth(cellWidth()); setCellZoom(cellZoom()); } StyleInspectorClient::~StyleInspectorClient() = default; void StyleInspectorClient::setCellHeight(int height) { StyleInspectorInterface::setCellHeight(height); Endpoint::instance()->invokeObject(objectName(), "setCellHeight", QVariantList() << height); } void StyleInspectorClient::setCellWidth(int width) { StyleInspectorInterface::setCellWidth(width); Endpoint::instance()->invokeObject(objectName(), "setCellWidth", QVariantList() << width); } void StyleInspectorClient::setCellZoom(int zoom) { StyleInspectorInterface::setCellZoom(zoom); Endpoint::instance()->invokeObject(objectName(), "setCellZoom", QVariantList() << zoom); } gammaray-2.11.3/plugins/styleinspector/styleinspectorclient.h000066400000000000000000000032771413310443000246050ustar00rootroot00000000000000/* styleinspectorclient.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_STYLEINSPECTOR_STYLEINSPECTORCLIENT_H #define GAMMARAY_STYLEINSPECTOR_STYLEINSPECTORCLIENT_H #include "styleinspectorinterface.h" namespace GammaRay { class StyleInspectorClient : public StyleInspectorInterface { Q_OBJECT Q_INTERFACES(GammaRay::StyleInspectorInterface) public: explicit StyleInspectorClient(QObject *parent = nullptr); ~StyleInspectorClient() override; void setCellHeight(int height) override; void setCellWidth(int width) override; void setCellZoom(int zoom) override; }; } #endif // GAMMARAY_STYLEINSPECTORCLIENT_H gammaray-2.11.3/plugins/styleinspector/styleinspectorinterface.cpp000066400000000000000000000042771413310443000256230ustar00rootroot00000000000000/* styleinspectorinterface.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "styleinspectorinterface.h" #include #include using namespace GammaRay; StyleInspectorInterface::StyleInspectorInterface(QObject *parent) : QObject(parent) , m_cellHeight(64) , m_cellWidth(64) , m_cellZoom(1) { ObjectBroker::registerObject(this); } StyleInspectorInterface::~StyleInspectorInterface() = default; int StyleInspectorInterface::cellHeight() const { return m_cellHeight; } int StyleInspectorInterface::cellWidth() const { return m_cellWidth; } int StyleInspectorInterface::cellZoom() const { return m_cellZoom; } QSize StyleInspectorInterface::cellSizeHint() const { return {m_cellWidth * m_cellZoom, m_cellHeight * m_cellZoom}; } void StyleInspectorInterface::setCellHeight(int height) { m_cellHeight = height; emit cellSizeChanged(); } void StyleInspectorInterface::setCellWidth(int width) { m_cellWidth = width; emit cellSizeChanged(); } void StyleInspectorInterface::setCellZoom(int zoom) { m_cellZoom = zoom; emit cellSizeChanged(); } gammaray-2.11.3/plugins/styleinspector/styleinspectorinterface.h000066400000000000000000000040451413310443000252610ustar00rootroot00000000000000/* styleinspectorinterface.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_STYLEINSPECTOR_STYLEINSPECTORINTERFACE_H #define GAMMARAY_STYLEINSPECTOR_STYLEINSPECTORINTERFACE_H #include QT_BEGIN_NAMESPACE class QSize; QT_END_NAMESPACE namespace GammaRay { class StyleInspectorInterface : public QObject { Q_OBJECT public: explicit StyleInspectorInterface(QObject *parent = nullptr); ~StyleInspectorInterface() override; int cellHeight() const; int cellWidth() const; int cellZoom() const; QSize cellSizeHint() const; signals: void cellSizeChanged(); public slots: virtual void setCellHeight(int height); virtual void setCellWidth(int width); virtual void setCellZoom(int zoom); private: int m_cellHeight; int m_cellWidth; int m_cellZoom; }; } QT_BEGIN_NAMESPACE Q_DECLARE_INTERFACE(GammaRay::StyleInspectorInterface, "com.kdab.GammaRay.StyleInspectorInterface") QT_END_NAMESPACE #endif // GAMMARAY_STYLEINSPECTORINTERFACE_H gammaray-2.11.3/plugins/styleinspector/styleinspectorwidget.cpp000066400000000000000000000111451413310443000251360ustar00rootroot00000000000000/* styleinspectorwidget.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2012-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "styleinspectorwidget.h" #include "ui_styleinspectorwidget.h" #include #include using namespace GammaRay; StyleInspectorWidget::StyleInspectorWidget(QWidget *parent) : QWidget(parent) , ui(new Ui::StyleInspectorWidget) { ui->setupUi(this); ui->styleSelector->setModel(ObjectBroker::model(QStringLiteral("com.kdab.GammaRay.StyleList"))); connect(ui->styleSelector, static_cast(&QComboBox::currentIndexChanged), this, &StyleInspectorWidget::styleSelected); ui->primitivePage->setModel(ObjectBroker::model(QStringLiteral( "com.kdab.GammaRay.StyleInspector.PrimitiveModel"))); ui->controlPage->setModel(ObjectBroker::model(QStringLiteral( "com.kdab.GammaRay.StyleInspector.ControlModel"))); ui->complexControlPage->setModel(ObjectBroker::model(QStringLiteral( "com.kdab.GammaRay.StyleInspector.ComplexControlModel"))); ui->pixelMetricView->header()->setObjectName("pixelMetricViewHeader"); ui->pixelMetricView->setDeferredResizeMode(0, QHeaderView::ResizeToContents); ui->pixelMetricView->setDeferredResizeMode(1, QHeaderView::Stretch); ui->pixelMetricView->setModel(ObjectBroker::model(QStringLiteral( "com.kdab.GammaRay.StyleInspector.PixelMetricModel"))); ui->standardIconView->header()->setObjectName("standardIconViewHeader"); ui->standardIconView->setDeferredResizeMode(0, QHeaderView::ResizeToContents); ui->standardIconView->setDeferredResizeMode(1, QHeaderView::Stretch); ui->standardIconView->setModel(ObjectBroker::model(QStringLiteral( "com.kdab.GammaRay.StyleInspector.StandardIconModel"))); ui->standardPaletteView->header()->setObjectName("standardPaletteViewHeader"); ui->standardPaletteView->setDeferredResizeMode(0, QHeaderView::ResizeToContents); ui->standardPaletteView->setDeferredResizeMode(1, QHeaderView::ResizeToContents); ui->standardPaletteView->setDeferredResizeMode(2, QHeaderView::ResizeToContents); ui->standardPaletteView->setDeferredResizeMode(3, QHeaderView::ResizeToContents); ui->standardPaletteView->setModel(ObjectBroker::model(QStringLiteral( "com.kdab.GammaRay.StyleInspector.PaletteModel"))); ui->styleHintView->header()->setObjectName("styleHintViewHeader"); ui->styleHintView->setDeferredResizeMode(0, QHeaderView::ResizeToContents); ui->styleHintView->setDeferredResizeMode(1, QHeaderView::ResizeToContents); ui->styleHintView->setModel(ObjectBroker::model(QStringLiteral("com.kdab.GammaRay.StyleInspector.StyleHintModel"))); ui->styleHintView->setItemDelegate(new PropertyEditorDelegate(this)); // TODO this will fail due to lazy model population if (ui->styleSelector->count()) styleSelected(0); } StyleInspectorWidget::~StyleInspectorWidget() { delete ui; } void StyleInspectorWidget::styleSelected(int index) { QItemSelectionModel *selectionModel = ObjectBroker::selectionModel(ui->styleSelector->model()); selectionModel->select(ui->styleSelector->model()->index(index, 0), QItemSelectionModel::ClearAndSelect); } gammaray-2.11.3/plugins/styleinspector/styleinspectorwidget.h000066400000000000000000000036251413310443000246070ustar00rootroot00000000000000/* styleinspectorwidget.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2012-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_STYLEINSPECTOR_STYLEINSPECTORWIDGET_H #define GAMMARAY_STYLEINSPECTOR_STYLEINSPECTORWIDGET_H #include #include namespace GammaRay { namespace Ui { class StyleInspectorWidget; } class StyleInspectorWidget : public QWidget { Q_OBJECT public: explicit StyleInspectorWidget(QWidget *parent = nullptr); ~StyleInspectorWidget() override; private slots: void styleSelected(int index); private: Ui::StyleInspectorWidget *ui; }; class StyleInspectorUiFactory : public QObject, public StandardToolUiFactory { Q_OBJECT Q_INTERFACES(GammaRay::ToolUiFactory) Q_PLUGIN_METADATA(IID "com.kdab.GammaRay.ToolUiFactory" FILE "gammaray_styleinspector.json") }; } #endif // GAMMARAY_STYLEINSPECTORWIDGET_H gammaray-2.11.3/plugins/styleinspector/styleinspectorwidget.ui000066400000000000000000000117621413310443000247760ustar00rootroot00000000000000 GammaRay::StyleInspectorWidget 0 0 400 300 0 0 &Style: styleSelector 0 Primitives Controls Complex Controls Pixel Metric false true false Standard Icons false true false Standard Palette false true false Style Hints false true GammaRay::DeferredTreeView QTreeView
    ui/deferredtreeview.h
    GammaRay::StyleElementStateTablePage QWidget
    styleelementstatetablepage.h
    1
    gammaray-2.11.3/plugins/styleinspector/styleoption.cpp000066400000000000000000000154141413310443000232370ustar00rootroot00000000000000/* styleoption.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2012-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "styleoption.h" #include using namespace GammaRay; // The following commit in qtbase.git deprecated versioned style class in 5.7.0 // 95a6dc1 - QStyleOption*V: mark as Q_DECL_DEPRECATED #if QT_VERSION >= QT_VERSION_CHECK(5, 7, 0) using QStyleOptionFrameCV = QStyleOptionFrame; using QStyleOptionToolBoxCV = QStyleOptionToolBox; using QStyleOptionTabCV = QStyleOptionTab; using QStyleOptionTabBarBaseCV = QStyleOptionTabBarBase; using QStyleOptionTabWidgetFrameCV = QStyleOptionTabWidgetFrame; using QStyleOptionViewItemCV = QStyleOptionViewItem; using QStyleOptionProgressBarCV = QStyleOptionProgressBar; #else typedef QStyleOptionFrameV3 QStyleOptionFrameCV; typedef QStyleOptionToolBoxV2 QStyleOptionToolBoxCV; typedef QStyleOptionTabBarBaseV2 QStyleOptionTabBarBaseCV; typedef QStyleOptionTabV3 QStyleOptionTabCV; typedef QStyleOptionTabWidgetFrameV2 QStyleOptionTabWidgetFrameCV; typedef QStyleOptionViewItemV4 QStyleOptionViewItemCV; typedef QStyleOptionProgressBarV2 QStyleOptionProgressBarCV; #endif struct style_state_t { const char *name; QStyle::State state; }; #define MAKE_STATE(state) { #state, QStyle:: state } static const style_state_t styleStates[] = { MAKE_STATE(State_None), MAKE_STATE(State_Enabled), MAKE_STATE(State_Raised), MAKE_STATE(State_Sunken), MAKE_STATE(State_Off), MAKE_STATE(State_NoChange), MAKE_STATE(State_On), MAKE_STATE(State_DownArrow), MAKE_STATE(State_Horizontal), MAKE_STATE(State_HasFocus), MAKE_STATE(State_Top), MAKE_STATE(State_Bottom), MAKE_STATE(State_FocusAtBorder), MAKE_STATE(State_AutoRaise), MAKE_STATE(State_MouseOver), MAKE_STATE(State_UpArrow), MAKE_STATE(State_Selected), MAKE_STATE(State_Active), MAKE_STATE(State_Window), MAKE_STATE(State_Open), MAKE_STATE(State_Children), MAKE_STATE(State_Item), MAKE_STATE(State_Sibling), MAKE_STATE(State_Editing), MAKE_STATE(State_KeyboardFocusChange), MAKE_STATE(State_ReadOnly), MAKE_STATE(State_Small), MAKE_STATE(State_Mini) }; int StyleOption::stateCount() { return sizeof(styleStates) / sizeof(style_state_t); } QString StyleOption::stateDisplayName(int index) { return QString::fromLatin1(styleStates[index].name).mid(6); // remove the State_ prefix } QStyle::State StyleOption::prettyState(int index) { QStyle::State s = styleStates[index].state; if (s == QStyle::State_None) return s; return s | QStyle::State_Enabled; // enable by default, else we usually see disabled stuff only } QStyleOption *StyleOption::makeStyleOption() { return new QStyleOption; } QStyleOption *StyleOption::makeStyleOptionComplex() { return new QStyleOptionComplex; } QStyleOption *StyleOption::makeButtonStyleOption() { auto *opt = new QStyleOptionButton; opt->features = QStyleOptionButton::None; opt->text = QStringLiteral("Label"); return opt; } QStyleOption *StyleOption::makeComboBoxStyleOption() { auto *opt = new QStyleOptionComboBox; opt->frame = true; opt->currentText = QStringLiteral("Current Text"); return opt; } QStyleOption *StyleOption::makeFrameStyleOption() { auto opt = new QStyleOptionFrameCV; opt->lineWidth = 1; opt->midLineWidth = 0; opt->frameShape = QFrame::StyledPanel; return opt; } QStyleOption *StyleOption::makeGroupBoxStyleOption() { auto *opt = new QStyleOptionGroupBox; opt->lineWidth = 1; opt->midLineWidth = 0; opt->text = QStringLiteral("Label"); return opt; } QStyleOption *StyleOption::makeHeaderStyleOption() { auto *opt = new QStyleOptionHeader; opt->orientation = Qt::Horizontal; opt->text = QStringLiteral("Label"); return opt; } QStyleOption *StyleOption::makeItemViewStyleOption() { auto opt = new QStyleOptionViewItemCV; opt->text = QStringLiteral("Text"); opt->features = QStyleOptionViewItemCV::HasDisplay; return opt; } QStyleOption *StyleOption::makeMenuStyleOption() { auto *opt = new QStyleOptionMenuItem; opt->text = QStringLiteral("Label"); return opt; } QStyleOption *StyleOption::makeProgressBarStyleOption() { auto opt = new QStyleOptionProgressBarCV; opt->minimum = 0; opt->maximum = 100; opt->progress = 42; return opt; } QStyleOption *StyleOption::makeSliderStyleOption() { auto *opt = new QStyleOptionSlider; opt->minimum = 0; opt->maximum = 100; opt->sliderValue = 42; opt->tickInterval = 5; return opt; } QStyleOption *StyleOption::makeSpinBoxStyleOption() { auto *opt = new QStyleOptionSpinBox; opt->frame = true; return opt; } QStyleOption *StyleOption::makeTabStyleOption() { auto opt = new QStyleOptionTabCV; opt->text = QStringLiteral("Label"); return opt; } QStyleOption *StyleOption::makeTabBarBaseStyleOption() { return new QStyleOptionTabBarBaseCV; } QStyleOption *StyleOption::makeTabWidgetFrameStyleOption() { auto opt = new QStyleOptionTabWidgetFrameCV; opt->lineWidth = 1; return opt; } QStyleOption *StyleOption::makeTitleBarStyleOption() { auto *opt = new QStyleOptionTitleBar; opt->text = QStringLiteral("Title"); opt->titleBarFlags = Qt::WindowMinMaxButtonsHint |Qt::WindowTitleHint |Qt::WindowSystemMenuHint |Qt::WindowCloseButtonHint; return opt; } QStyleOption *StyleOption::makeToolBoxStyleOption() { auto opt = new QStyleOptionToolBoxCV; opt->text = QStringLiteral("Label"); return opt; } QStyleOption *StyleOption::makeToolButtonStyleOption() { auto *opt = new QStyleOptionToolButton; opt->text = QStringLiteral("Label"); opt->toolButtonStyle = Qt::ToolButtonFollowStyle; return opt; } gammaray-2.11.3/plugins/styleinspector/styleoption.h000066400000000000000000000043561413310443000227070ustar00rootroot00000000000000/* styleoption.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2012-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_STYLEINSPECTOR_STYLEOPTION_H #define GAMMARAY_STYLEINSPECTOR_STYLEOPTION_H #include QT_BEGIN_NAMESPACE class QStyleOption; class QString; QT_END_NAMESPACE namespace GammaRay { /** * Various helper methods for dealing with QStyleOption. */ namespace StyleOption { int stateCount(); QString stateDisplayName(int index); QStyle::State prettyState(int index); QStyleOption *makeStyleOption(); QStyleOption *makeStyleOptionComplex(); QStyleOption *makeButtonStyleOption(); QStyleOption *makeComboBoxStyleOption(); QStyleOption *makeFrameStyleOption(); QStyleOption *makeGroupBoxStyleOption(); QStyleOption *makeHeaderStyleOption(); QStyleOption *makeItemViewStyleOption(); QStyleOption *makeMenuStyleOption(); QStyleOption *makeProgressBarStyleOption(); QStyleOption *makeSliderStyleOption(); QStyleOption *makeSpinBoxStyleOption(); QStyleOption *makeTabStyleOption(); QStyleOption *makeTabBarBaseStyleOption(); QStyleOption *makeTabWidgetFrameStyleOption(); QStyleOption *makeTitleBarStyleOption(); QStyleOption *makeToolBoxStyleOption(); QStyleOption *makeToolButtonStyleOption(); } } #endif // GAMMARAY_STYLEOPTION_H gammaray-2.11.3/plugins/sysinfo/000077500000000000000000000000001413310443000165405ustar00rootroot00000000000000gammaray-2.11.3/plugins/sysinfo/CMakeLists.txt000066400000000000000000000012721413310443000213020ustar00rootroot00000000000000# probe part if (NOT GAMMARAY_CLIENT_ONLY_BUILD) set(sysinfo_probe_srcs sysinfo.cpp environmentmodel.cpp libraryinfomodel.cpp standardpathsmodel.cpp sysinfomodel.cpp ) gammaray_add_plugin(gammaray_sysinfo_plugin JSON gammaray_sysinfo.json SOURCES ${sysinfo_probe_srcs}) target_link_libraries(gammaray_sysinfo_plugin gammaray_core) endif() # ui part if(GAMMARAY_BUILD_UI) set(sysinfo_ui_srcs sysinfowidget.cpp standardpathswidget.cpp ) gammaray_add_plugin(gammaray_sysinfo_ui_plugin JSON gammaray_sysinfo.json SOURCES ${sysinfo_ui_srcs}) target_link_libraries(gammaray_sysinfo_ui_plugin gammaray_ui) endif() gammaray-2.11.3/plugins/sysinfo/environmentmodel.cpp000066400000000000000000000045751413310443000226440ustar00rootroot00000000000000/* environmentmodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2018-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "environmentmodel.h" using namespace GammaRay; EnvironmentModel::EnvironmentModel(QObject* parent) : QAbstractTableModel(parent) , m_env(QProcessEnvironment::systemEnvironment()) { } EnvironmentModel::~EnvironmentModel() = default; int EnvironmentModel::columnCount(const QModelIndex& parent) const { Q_UNUSED(parent); return 2; } int EnvironmentModel::rowCount(const QModelIndex& parent) const { if (parent.isValid()) return 0; return m_env.keys().size(); } QVariant EnvironmentModel::data(const QModelIndex& index, int role) const { if (!index.isValid()) return QVariant(); if (role == Qt::DisplayRole) { switch (index.column()) { case 0: return m_env.keys().at(index.row()); case 1: return m_env.value(m_env.keys().at(index.row())); } } return QVariant(); } QVariant EnvironmentModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role == Qt::DisplayRole && orientation == Qt::Horizontal) { switch (section) { case 0: return tr("Environment Variable"); case 1: return tr("Value"); } } return QAbstractTableModel::headerData(section, orientation, role); } gammaray-2.11.3/plugins/sysinfo/environmentmodel.h000066400000000000000000000034611413310443000223020ustar00rootroot00000000000000/* environmentmodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2018-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_ENVIRONMENTMODEL_H #define GAMMARAY_ENVIRONMENTMODEL_H #include #include namespace GammaRay { class EnvironmentModel : public QAbstractTableModel { Q_OBJECT public: explicit EnvironmentModel(QObject *parent = nullptr); ~EnvironmentModel() override; int columnCount(const QModelIndex & parent) const override; int rowCount(const QModelIndex & parent) const override; QVariant data(const QModelIndex & index, int role) const override; QVariant headerData(int section, Qt::Orientation orientation, int role) const override; private: QProcessEnvironment m_env; }; } #endif // GAMMARAY_ENVIRONMENTMODEL_H gammaray-2.11.3/plugins/sysinfo/gammaray_sysinfo.json000066400000000000000000000001721413310443000230030ustar00rootroot00000000000000{ "id": "gammaray_sysinfo", "name": "System", "types": [ "QObject" ], "selectableTypes": [] } gammaray-2.11.3/plugins/sysinfo/libraryinfomodel.cpp000066400000000000000000000056031413310443000226110ustar00rootroot00000000000000/* libraryinfomodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2018-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "libraryinfomodel.h" #include using namespace GammaRay; struct location_info_t { QLibraryInfo::LibraryLocation loc; const char* name; }; #define L(x) { QLibraryInfo:: x, #x } static const location_info_t locInfoTable[] = { L(PrefixPath), L(DocumentationPath), L(HeadersPath), L(LibrariesPath), L(LibraryExecutablesPath), L(BinariesPath), L(PluginsPath), L(ImportsPath), L(Qml2ImportsPath), L(ArchDataPath), L(DataPath), L(TranslationsPath), L(ExamplesPath), L(TestsPath), L(SettingsPath) }; #undef L static const auto locInfoTableSize = sizeof(locInfoTable) / sizeof(location_info_t); LibraryInfoModel::LibraryInfoModel(QObject* parent) : QAbstractTableModel(parent) { } int LibraryInfoModel::columnCount(const QModelIndex& parent) const { Q_UNUSED(parent); return 2; } int LibraryInfoModel::rowCount(const QModelIndex& parent) const { if (parent.isValid()) return 0; return locInfoTableSize; } QVariant LibraryInfoModel::data(const QModelIndex& index, int role) const { if (!index.isValid()) return QVariant(); if (role == Qt::DisplayRole) { switch (index.column()) { case 0: return locInfoTable[index.row()].name; case 1: return QLibraryInfo::location(locInfoTable[index.row()].loc); } } return QVariant(); } QVariant LibraryInfoModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role == Qt::DisplayRole && orientation == Qt::Horizontal) { switch (section) { case 0: return tr("Location"); case 1: return tr("Path"); } } return QAbstractTableModel::headerData(section, orientation, role); } gammaray-2.11.3/plugins/sysinfo/libraryinfomodel.h000066400000000000000000000033101413310443000222470ustar00rootroot00000000000000/* libraryinfomodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2018-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_LIBRARYINFOMODEL_H #define GAMMARAY_LIBRARYINFOMODEL_H #include namespace GammaRay { class LibraryInfoModel : public QAbstractTableModel { Q_OBJECT public: explicit LibraryInfoModel(QObject *parent = nullptr); int columnCount(const QModelIndex & parent) const override; int rowCount(const QModelIndex & parent) const override; QVariant data(const QModelIndex & index, int role) const override; QVariant headerData(int section, Qt::Orientation orientation, int role) const override; }; } #endif // GAMMARAY_LIBRARYINFOMODEL_H gammaray-2.11.3/plugins/sysinfo/standardpathsmodel.cpp000066400000000000000000000070441413310443000231320ustar00rootroot00000000000000/* standardpathsmodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2012-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "standardpathsmodel.h" #include using namespace GammaRay; struct standard_path_t { QStandardPaths::StandardLocation location; const char *locationName; }; #define P(x) { QStandardPaths:: x, #x } static const standard_path_t standard_paths[] = { P(DesktopLocation), P(DocumentsLocation), P(FontsLocation), P(ApplicationsLocation), P(MusicLocation), P(MoviesLocation), P(PicturesLocation), P(TempLocation), P(HomeLocation), P(DataLocation), P(CacheLocation), P(GenericDataLocation), P(RuntimeLocation), P(ConfigLocation), P(DownloadLocation), P(GenericCacheLocation), P(GenericConfigLocation), P(AppDataLocation), P(AppConfigLocation), }; #undef P static const int standard_path_count = sizeof(standard_paths) / sizeof(standard_path_t); StandardPathsModel::StandardPathsModel(QObject *parent) : QAbstractTableModel(parent) { } StandardPathsModel::~StandardPathsModel() = default; QVariant StandardPathsModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); if (role == Qt::TextAlignmentRole) return static_cast(Qt::AlignLeft | Qt::AlignTop); if (role == Qt::DisplayRole) { const QStandardPaths::StandardLocation loc = standard_paths[index.row()].location; switch (index.column()) { case 0: return QString::fromLatin1(standard_paths[index.row()].locationName); case 1: return QStandardPaths::displayName(loc); case 2: return QStandardPaths::standardLocations(loc).join(QLatin1Char('\n')); case 3: return QStandardPaths::writableLocation(loc); } } return QVariant(); } int StandardPathsModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); return 4; } int StandardPathsModel::rowCount(const QModelIndex &parent) const { if (parent.isValid()) return 0; return standard_path_count; } QVariant StandardPathsModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Vertical || role != Qt::DisplayRole) return QVariant(); switch (section) { case 0: return tr("Type"); case 1: return tr("Display Name"); case 2: return tr("Standard Locations"); case 3: return tr("Writable Location"); } return QVariant(); } gammaray-2.11.3/plugins/sysinfo/standardpathsmodel.h000066400000000000000000000034211413310443000225720ustar00rootroot00000000000000/* standardpathsmodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2012-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_STANDARDPATHS_STANDARDPATHSMODEL_H #define GAMMARAY_STANDARDPATHS_STANDARDPATHSMODEL_H #include namespace GammaRay { class StandardPathsModel : public QAbstractTableModel { Q_OBJECT public: explicit StandardPathsModel(QObject *parent = nullptr); ~StandardPathsModel() override; QVariant data(const QModelIndex &index, int role) const override; int columnCount(const QModelIndex &parent) const override; int rowCount(const QModelIndex &parent) const override; QVariant headerData(int section, Qt::Orientation orientation, int role) const override; }; } #endif // GAMMARAY_STANDARDPATHSMODEL_H gammaray-2.11.3/plugins/sysinfo/standardpathswidget.cpp000066400000000000000000000112531413310443000233120ustar00rootroot00000000000000/* standardpathswidget.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2012-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "standardpathswidget.h" #include "ui_standardpathswidget.h" #include #include #include #include #include #include using namespace GammaRay; namespace GammaRay { class StandardPathsProxy : public QIdentityProxyModel { Q_OBJECT public: explicit StandardPathsProxy(QObject *parent = nullptr) : QIdentityProxyModel(parent) { } QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override { if (section == 2 && role == Qt::DisplayRole) return tr("Locations Standard / Writable"); return QIdentityProxyModel::headerData(section, orientation, role); } }; class StandardPathsDelegate : public QStyledItemDelegate { Q_OBJECT public: explicit StandardPathsDelegate(QObject *parent = nullptr) : QStyledItemDelegate(parent) { } void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override { if (index.column() == 2) { QStyleOptionViewItem opt = option; initStyleOption(&opt, index); const QWidget *widget = opt.widget; QStyle *style = widget ? widget->style() : QApplication::style(); style->drawControl(QStyle::CE_ItemViewItem, &opt, painter, widget); const int textMargin = style->pixelMetric(QStyle::PM_FocusFrameHMargin, nullptr, widget) + 1; const QRect textRect = style->subElementRect(QStyle::SE_ItemViewItemText, &opt, widget) .adjusted(textMargin, 1, -textMargin, -1); painter->setPen(((opt.state & QStyle::State_Selected) ? opt.palette.highlightedText() : opt. palette.text()).color()); painter->drawText(textRect, Qt::AlignBottom | Qt::AlignLeft, index.sibling(index.row(), 3).data().toString()); } else { QStyledItemDelegate::paint(painter, option, index); } } QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override { if (index.column() == 2) { QSize s1 = QStyledItemDelegate::sizeHint(option, index.sibling(index.row(), 2)); QSize s2 = QStyledItemDelegate::sizeHint(option, index.sibling(index.row(), 3)); return {qMax(s1.width(), s2.width()), s1.height() + s2.height() + option.fontMetrics.height()}; } else { return QStyledItemDelegate::sizeHint(option, index); } } }; } StandardPathsWidget::StandardPathsWidget(QWidget *parent) : QWidget(parent) , ui(new Ui::StandardPathsWidget) , m_stateManager(this) { ui->setupUi(this); auto *proxy = new StandardPathsProxy(this); proxy->setSourceModel(ObjectBroker::model(QStringLiteral( "com.kdab.GammaRay.StandardPathsModel"))); ui->pathView->header()->setObjectName("pathViewHeader"); ui->pathView->setUniformRowHeights(false); ui->pathView->setDeferredResizeMode(0, QHeaderView::ResizeToContents); ui->pathView->setDeferredResizeMode(1, QHeaderView::ResizeToContents); ui->pathView->setDeferredHidden(3, true); ui->pathView->setItemDelegateForColumn(2, new StandardPathsDelegate(this)); ui->pathView->setModel(proxy); } StandardPathsWidget::~StandardPathsWidget() = default; #include "standardpathswidget.moc" gammaray-2.11.3/plugins/sysinfo/standardpathswidget.h000066400000000000000000000032021413310443000227520ustar00rootroot00000000000000/* standardpathswidget.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2012-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_STANDARDPATHSWIDGET_H #define GAMMARAY_STANDARDPATHSWIDGET_H #include #include #include namespace GammaRay { namespace Ui { class StandardPathsWidget; } class StandardPathsWidget : public QWidget { Q_OBJECT public: explicit StandardPathsWidget(QWidget *parent = nullptr); ~StandardPathsWidget() override; private: std::unique_ptr ui; UIStateManager m_stateManager; }; } #endif // GAMMARAY_STANDARDPATHSWIDGET_H gammaray-2.11.3/plugins/sysinfo/standardpathswidget.ui000066400000000000000000000016731413310443000231520ustar00rootroot00000000000000 GammaRay::StandardPathsWidget 0 0 400 300 0 true false GammaRay::DeferredTreeView QTreeView
    ui/deferredtreeview.h
    gammaray-2.11.3/plugins/sysinfo/sysinfo.cpp000066400000000000000000000034551413310443000207450ustar00rootroot00000000000000/* sysinfo.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2018-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "sysinfo.h" #include "environmentmodel.h" #include "libraryinfomodel.h" #include "standardpathsmodel.h" #include "sysinfomodel.h" using namespace GammaRay; SysInfo::SysInfo(Probe *probe, QObject *parent) : QObject(parent) { probe->registerModel(QStringLiteral("com.kdab.GammaRay.SysInfoModel"), new SysInfoModel(this)); probe->registerModel(QStringLiteral("com.kdab.GammaRay.LibraryInfoModel"), new LibraryInfoModel(this)); probe->registerModel(QStringLiteral("com.kdab.GammaRay.EnvironmentModel"), new EnvironmentModel(this)); probe->registerModel(QStringLiteral("com.kdab.GammaRay.StandardPathsModel"), new StandardPathsModel(this)); } SysInfo::~SysInfo() = default; gammaray-2.11.3/plugins/sysinfo/sysinfo.h000066400000000000000000000033411413310443000204040ustar00rootroot00000000000000/* sysinfo.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2018-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_SYSINFO_H #define GAMMARAY_SYSINFO_H #include namespace GammaRay { class SysInfo : public QObject { Q_OBJECT public: explicit SysInfo(Probe *probe, QObject *parent = nullptr); ~SysInfo() override; private: }; class SysInfoFactory : public QObject, public StandardToolFactory { Q_OBJECT Q_INTERFACES(GammaRay::ToolFactory) Q_PLUGIN_METADATA(IID "com.kdab.GammaRay.ToolFactory" FILE "gammaray_sysinfo.json") public: explicit SysInfoFactory(QObject *parent = nullptr) : QObject(parent) { } }; } #endif // GAMMARAY_SYSINFO_H gammaray-2.11.3/plugins/sysinfo/sysinfomodel.cpp000066400000000000000000000056531413310443000217700ustar00rootroot00000000000000/* sysinfomodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2018-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "sysinfomodel.h" #include #include using namespace GammaRay; struct sysinfo_t { QString(*func)(); const char* name; }; #define S(x) { QSysInfo:: x, #x } static const sysinfo_t sysInfoTable[] = { #if !defined(Q_CC_MSVC) || _MSC_VER > 1600 //krazy:exclude=cpp to deal with older MS compilers { []() { return QString::fromLatin1(QLibraryInfo::build()); }, "build" }, #endif S(buildAbi), S(buildCpuArchitecture), S(currentCpuArchitecture), S(kernelType), S(kernelVersion), #if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0) S(machineHostName), #endif S(prettyProductName), S(productType), S(productVersion) }; #undef S static const auto sysInfoTableSize = sizeof(sysInfoTable) / sizeof(sysinfo_t); SysInfoModel::SysInfoModel(QObject* parent) : QAbstractTableModel(parent) { } int SysInfoModel::columnCount(const QModelIndex& parent) const { Q_UNUSED(parent); return 2; } int SysInfoModel::rowCount(const QModelIndex& parent) const { if (parent.isValid()) return 0; return sysInfoTableSize; } QVariant SysInfoModel::data(const QModelIndex& index, int role) const { if (!index.isValid()) return QVariant(); if (role == Qt::DisplayRole) { switch (index.column()) { case 0: return sysInfoTable[index.row()].name; case 1: return sysInfoTable[index.row()].func(); } } return QVariant(); } QVariant SysInfoModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role == Qt::DisplayRole && orientation == Qt::Horizontal) { switch (section) { case 0: return tr("Key"); case 1: return tr("Value"); } } return QAbstractTableModel::headerData(section, orientation, role); } gammaray-2.11.3/plugins/sysinfo/sysinfomodel.h000066400000000000000000000032571413310443000214330ustar00rootroot00000000000000/* sysinfomodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2018-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_SYSINFOMODEL_H #define GAMMARAY_SYSINFOMODEL_H #include namespace GammaRay { class SysInfoModel : public QAbstractTableModel { Q_OBJECT public: explicit SysInfoModel(QObject *parent = nullptr); int columnCount(const QModelIndex & parent) const override; int rowCount(const QModelIndex & parent) const override; QVariant data(const QModelIndex & index, int role) const override; QVariant headerData(int section, Qt::Orientation orientation, int role) const override; }; } #endif // GAMMARAY_SYSINFOMODEL_H gammaray-2.11.3/plugins/sysinfo/sysinfowidget.cpp000066400000000000000000000037061413310443000221500ustar00rootroot00000000000000/* sysinfowidget.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2018-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "sysinfowidget.h" #include "ui_sysinfowidget.h" #include using namespace GammaRay; SysInfoWidget::SysInfoWidget(QWidget* parent) : QWidget(parent) , ui(new Ui::SysInfoWidget) { ui->setupUi(this); ui->sysInfoView->setModel(ObjectBroker::model(QStringLiteral("com.kdab.GammaRay.SysInfoModel"))); ui->sysInfoView->header()->setSectionResizeMode(QHeaderView::ResizeToContents); ui->libInfoView->setModel(ObjectBroker::model(QStringLiteral("com.kdab.GammaRay.LibraryInfoModel"))); ui->libInfoView->header()->setSectionResizeMode(QHeaderView::ResizeToContents); ui->envView->setModel(ObjectBroker::model(QStringLiteral("com.kdab.GammaRay.EnvironmentModel"))); ui->envView->header()->setSectionResizeMode(QHeaderView::ResizeToContents); } SysInfoWidget::~SysInfoWidget() = default; gammaray-2.11.3/plugins/sysinfo/sysinfowidget.h000066400000000000000000000034261413310443000216140ustar00rootroot00000000000000/* sysinfowidget.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2018-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_SYSINFOWIDGET_H #define GAMMARAY_SYSINFOWIDGET_H #include #include #include namespace GammaRay { namespace Ui { class SysInfoWidget; } class SysInfoWidget : public QWidget { Q_OBJECT public: explicit SysInfoWidget(QWidget *parent = nullptr); ~SysInfoWidget() override; private: std::unique_ptr ui; }; class SysInfoUiFactory : public QObject, public StandardToolUiFactory { Q_OBJECT Q_INTERFACES(GammaRay::ToolUiFactory) Q_PLUGIN_METADATA(IID "com.kdab.GammaRay.ToolUiFactory" FILE "gammaray_sysinfo.json") }; } #endif // GAMMARAY_SYSINFOWIDGET_H gammaray-2.11.3/plugins/sysinfo/sysinfowidget.ui000066400000000000000000000057571413310443000220130ustar00rootroot00000000000000 GammaRay::SysInfoWidget 0 0 400 300 Form 0 Qt System information: sysInfoView false true Library information: libInfoView false true Standard Paths Environment false true GammaRay::StandardPathsWidget QWidget
    standardpathswidget.h
    1
    gammaray-2.11.3/plugins/textdocumentinspector/000077500000000000000000000000001413310443000215205ustar00rootroot00000000000000gammaray-2.11.3/plugins/textdocumentinspector/CMakeLists.txt000066400000000000000000000015661413310443000242700ustar00rootroot00000000000000# probe plugin if (NOT GAMMARAY_CLIENT_ONLY_BUILD) set(gammaray_textdocumentinspector_srcs textdocumentinspector.cpp textdocumentformatmodel.cpp textdocumentmodel.cpp ) gammaray_add_plugin(gammaray_textdocumentinspector JSON gammaray_textdocumentinspector.json SOURCES ${gammaray_textdocumentinspector_srcs} ) target_link_libraries(gammaray_textdocumentinspector gammaray_core) target_link_libraries(gammaray_textdocumentinspector Qt5::Gui) endif() # ui plugin if(GAMMARAY_BUILD_UI) set(gammaray_textdocumentinspector_ui_srcs textdocumentinspectorwidget.cpp textdocumentcontentview.cpp ) gammaray_add_plugin(gammaray_textdocumentinspector_ui JSON gammaray_textdocumentinspector.json SOURCES ${gammaray_textdocumentinspector_ui_srcs} ) target_link_libraries(gammaray_textdocumentinspector_ui gammaray_ui) endif() gammaray-2.11.3/plugins/textdocumentinspector/gammaray_textdocumentinspector.json000066400000000000000000000004231413310443000307420ustar00rootroot00000000000000{ "id": "gammaray_textdocumentinspector", "name": "Text Documents", "name[de]": "Text Dokumente", "selectableTypes": [ "QTextDocument", "QTextObject", "QAbstractTextDocumentLayout" ], "types": [ "QTextDocument" ] } gammaray-2.11.3/plugins/textdocumentinspector/textdocumentcontentview.cpp000066400000000000000000000032651413310443000272430ustar00rootroot00000000000000/* textdocumentcontentview.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "textdocumentcontentview.h" #include using namespace GammaRay; TextDocumentContentView::TextDocumentContentView(QWidget *parent) : QTextEdit(parent) { } void TextDocumentContentView::setShowBoundingBox(const QRectF &boundingBox) { m_boundingBox = boundingBox; viewport()->update(); } void TextDocumentContentView::paintEvent(QPaintEvent *e) { QTextEdit::paintEvent(e); if (!m_boundingBox.isEmpty()) { QPainter painter(viewport()); painter.setPen(Qt::red); painter.drawRect(m_boundingBox); } } gammaray-2.11.3/plugins/textdocumentinspector/textdocumentcontentview.h000066400000000000000000000031761413310443000267110ustar00rootroot00000000000000/* textdocumentcontentview.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_TEXTDOCUMENTINSPECTOR_TEXTDOCUMENTCONTENTVIEW_H #define GAMMARAY_TEXTDOCUMENTINSPECTOR_TEXTDOCUMENTCONTENTVIEW_H #include namespace GammaRay { class TextDocumentContentView : public QTextEdit { Q_OBJECT public: explicit TextDocumentContentView(QWidget *parent = nullptr); void setShowBoundingBox(const QRectF &boundingBox); protected: void paintEvent(QPaintEvent *e) override; private: QRectF m_boundingBox; }; } #endif // GAMMARAY_TEXTDOCUMENTCONTENTVIEW_H gammaray-2.11.3/plugins/textdocumentinspector/textdocumentformatmodel.cpp000066400000000000000000000063721413310443000272110ustar00rootroot00000000000000/* textdocumentformatmodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "textdocumentformatmodel.h" #include "core/varianthandler.h" #include #include using namespace GammaRay; static QMetaEnum propertyEnum() { const int index = QTextFormat::staticMetaObject.indexOfEnumerator("Property"); Q_ASSERT(index >= 0); return QTextFormat::staticMetaObject.enumerator(index); } TextDocumentFormatModel::TextDocumentFormatModel(QObject *parent) : QAbstractTableModel(parent) { } void TextDocumentFormatModel::setFormat(const QTextFormat &format) { beginResetModel(); m_format = format; endResetModel(); } int TextDocumentFormatModel::rowCount(const QModelIndex &parent) const { if (!m_format.isValid() || parent.isValid()) return 0; return propertyEnum().keyCount(); } int TextDocumentFormatModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); return 3; } QVariant TextDocumentFormatModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); if (role == Qt::DisplayRole) { const int enumValue = propertyEnum().value(index.row()); switch (index.column()) { case 0: return QString::fromLatin1(propertyEnum().key(index.row())); case 1: return VariantHandler::displayString(m_format.property(enumValue)); case 2: return QString::fromLatin1(m_format.property(enumValue).typeName()); } } else if (role == Qt::DecorationRole && index.column() == 1) { const int enumValue = propertyEnum().value(index.row()); return VariantHandler::decoration(m_format.property(enumValue)); } return QVariant(); } QVariant TextDocumentFormatModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { switch (section) { case 0: return tr("Property"); case 1: return tr("Value"); case 2: return tr("Type"); } } return QAbstractItemModel::headerData(section, orientation, role); } gammaray-2.11.3/plugins/textdocumentinspector/textdocumentformatmodel.h000066400000000000000000000036721413310443000266560ustar00rootroot00000000000000/* textdocumentformatmodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_TEXTDOCUMENTINSPECTOR_TEXTDOCUMENTFORMATMODEL_H #define GAMMARAY_TEXTDOCUMENTINSPECTOR_TEXTDOCUMENTFORMATMODEL_H #include #include namespace GammaRay { class TextDocumentFormatModel : public QAbstractTableModel { Q_OBJECT public: explicit TextDocumentFormatModel(QObject *parent = nullptr); void setFormat(const QTextFormat &format); int rowCount(const QModelIndex &parent = QModelIndex()) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; private: QTextFormat m_format; }; } #endif gammaray-2.11.3/plugins/textdocumentinspector/textdocumentinspector.cpp000066400000000000000000000131771413310443000267070ustar00rootroot00000000000000/* textdocumentinspector.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "textdocumentinspector.h" #include "textdocumentformatmodel.h" #include "textdocumentmodel.h" #include #include #include #include #include #include #include #include #include using namespace GammaRay; TextDocumentInspector::TextDocumentInspector(Probe *probe, QObject *parent) : QObject(parent) { registerMetaTypes(); auto documentFilter = new ObjectTypeFilterProxyModel(this); documentFilter->setSourceModel(probe->objectListModel()); probe->registerModel(QStringLiteral("com.kdab.GammaRay.TextDocumentsModel"), documentFilter); m_documentsModel = documentFilter; m_documentSelectionModel = ObjectBroker::selectionModel(documentFilter); connect(m_documentSelectionModel, &QItemSelectionModel::selectionChanged, this, &TextDocumentInspector::documentSelected); m_textDocumentModel = new TextDocumentModel(this); probe->registerModel(QStringLiteral("com.kdab.GammaRay.TextDocumentModel"), m_textDocumentModel); auto selectionModel = ObjectBroker::selectionModel(m_textDocumentModel); connect(selectionModel, &QItemSelectionModel::selectionChanged, this, &TextDocumentInspector::documentElementSelected); m_textDocumentFormatModel = new TextDocumentFormatModel(this); probe->registerModel(QStringLiteral("com.kdab.GammaRay.TextDocumentFormatModel"), m_textDocumentFormatModel); connect(probe, &Probe::objectSelected, this, &TextDocumentInspector::objectSelected); } void TextDocumentInspector::objectSelected(QObject* obj) { if (auto doc = qobject_cast(obj)) { const auto indexList = m_documentsModel->match(m_documentsModel->index(0, 0), ObjectModel::ObjectRole, QVariant::fromValue(doc), 1, Qt::MatchExactly | Qt::MatchRecursive | Qt::MatchWrap); if (indexList.isEmpty()) return; const auto index = indexList.first(); m_documentSelectionModel->select(index, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); } else if (auto docObj = qobject_cast(obj)) { objectSelected(docObj->document()); } else if (auto layout = qobject_cast(obj)) { objectSelected(layout->document()); } } void TextDocumentInspector::documentSelected(const QItemSelection &selected, const QItemSelection &deselected) { Q_UNUSED(deselected); if (selected.isEmpty()) { m_textDocumentModel->setDocument(nullptr); return; } const QModelIndex selectedRow = selected.first().topLeft(); QObject *selectedObj = selectedRow.data(ObjectModel::ObjectRole).value(); QTextDocument *doc = qobject_cast(selectedObj); m_textDocumentModel->setDocument(doc); } void TextDocumentInspector::documentElementSelected(const QItemSelection &selected, const QItemSelection &deselected) { Q_UNUSED(deselected); if (selected.isEmpty()) return; const QModelIndex selectedRow = selected.first().topLeft(); const QTextFormat f = selectedRow.data(TextDocumentModel::FormatRole).value(); m_textDocumentFormatModel->setFormat(f); } void TextDocumentInspector::registerMetaTypes() { MetaObject *mo = nullptr; MO_ADD_METAOBJECT1(QTextObject, QObject); MO_ADD_PROPERTY_RO(QTextObject, document); MO_ADD_PROPERTY_RO(QTextObject, formatIndex); MO_ADD_PROPERTY_RO(QTextObject, objectIndex); MO_ADD_METAOBJECT1(QTextFrame, QTextObject); MO_ADD_PROPERTY_RO(QTextFrame, firstPosition); MO_ADD_PROPERTY_RO(QTextFrame, lastPosition); MO_ADD_PROPERTY_RO(QTextFrame, parentFrame); MO_ADD_METAOBJECT1(QTextTable, QTextFrame); MO_ADD_PROPERTY_RO(QTextTable, columns); MO_ADD_PROPERTY_RO(QTextTable, rows); MO_ADD_METAOBJECT1(QTextBlockGroup, QTextObject); MO_ADD_METAOBJECT1(QTextList, QTextBlockGroup); MO_ADD_PROPERTY_RO(QTextList, count); MO_ADD_METAOBJECT1(QAbstractTextDocumentLayout, QObject); MO_ADD_PROPERTY_RO(QAbstractTextDocumentLayout, document); MO_ADD_PROPERTY_RO(QAbstractTextDocumentLayout, documentSize); MO_ADD_PROPERTY_RO(QAbstractTextDocumentLayout, pageCount); } gammaray-2.11.3/plugins/textdocumentinspector/textdocumentinspector.h000066400000000000000000000050671413310443000263530ustar00rootroot00000000000000/* textdocumentinspector.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_TEXTDOCUMENTINSPECTOR_TEXTDOCUMENTINSPECTOR_H #define GAMMARAY_TEXTDOCUMENTINSPECTOR_TEXTDOCUMENTINSPECTOR_H #include #include #include QT_BEGIN_NAMESPACE class QAbstractItemModel; class QItemSelection; class QItemSelectionModel; QT_END_NAMESPACE namespace GammaRay { class TextDocumentModel; class TextDocumentFormatModel; class TextDocumentInspector : public QObject { Q_OBJECT public: explicit TextDocumentInspector(Probe *probe, QObject *parent = nullptr); private slots: void documentSelected(const QItemSelection &selected, const QItemSelection &deselected); void documentElementSelected(const QItemSelection &selected, const QItemSelection &deselected); void objectSelected(QObject *obj); private: void registerMetaTypes(); QAbstractItemModel *m_documentsModel; QItemSelectionModel *m_documentSelectionModel; TextDocumentModel *m_textDocumentModel; TextDocumentFormatModel *m_textDocumentFormatModel; }; class TextDocumentInspectorFactory : public QObject, public StandardToolFactory { Q_OBJECT Q_INTERFACES(GammaRay::ToolFactory) Q_PLUGIN_METADATA(IID "com.kdab.GammaRay.ToolFactory" FILE "gammaray_textdocumentinspector.json") public: explicit TextDocumentInspectorFactory(QObject *parent = nullptr) : QObject(parent) { } }; } #endif // GAMMARAY_TEXTDOCUMENTINSPECTOR_H gammaray-2.11.3/plugins/textdocumentinspector/textdocumentinspectorwidget.cpp000066400000000000000000000137321413310443000301100ustar00rootroot00000000000000/* textdocumentinspectorwidget.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "textdocumentinspectorwidget.h" #include "ui_textdocumentinspectorwidget.h" #include "textdocumentmodel.h" #include #include #include #include #include #include #include using namespace GammaRay; TextDocumentInspectorWidget::TextDocumentInspectorWidget(QWidget *parent) : QWidget(parent) , ui(new Ui::TextDocumentInspectorWidget) , m_stateManager(this) { ui->setupUi(this); ui->documentList->header()->setObjectName("documentListHeader"); ui->documentList->setDeferredResizeMode(0, QHeaderView::Stretch); ui->documentList->setDeferredResizeMode(1, QHeaderView::Stretch); ui->documentList->setModel(ObjectBroker::model(QStringLiteral("com.kdab.GammaRay.TextDocumentsModel"))); ui->documentList->setSelectionModel(ObjectBroker::selectionModel(ui->documentList->model())); connect(ui->documentList->selectionModel(), &QItemSelectionModel::selectionChanged, this, &TextDocumentInspectorWidget::documentSelected); connect(ui->documentList, &QWidget::customContextMenuRequested, this, &TextDocumentInspectorWidget::documentContextMenu); ui->documentTree->header()->setObjectName("documentTreeHeader"); ui->documentTree->setDeferredResizeMode(0, QHeaderView::Stretch); ui->documentTree->setDeferredResizeMode(1, QHeaderView::ResizeToContents); ui->documentTree->setModel(ObjectBroker::model(QStringLiteral( "com.kdab.GammaRay.TextDocumentModel"))); ui->documentTree->setSelectionModel(ObjectBroker::selectionModel(ui->documentTree->model())); connect(ui->documentTree->selectionModel(), &QItemSelectionModel::selectionChanged, this, &TextDocumentInspectorWidget::documentElementSelected); ui->documentFormatView->header()->setObjectName("documentFormatViewHeader"); ui->documentFormatView->setDeferredResizeMode(0, QHeaderView::ResizeToContents); ui->documentFormatView->setDeferredResizeMode(1, QHeaderView::Stretch); ui->documentFormatView->setDeferredResizeMode(2, QHeaderView::ResizeToContents); ui->documentFormatView->setModel(ObjectBroker::model(QStringLiteral( "com.kdab.GammaRay.TextDocumentFormatModel"))); if (Endpoint::instance()->isRemoteClient()) // FIXME: content preview doesn't work remotely yet ui->tabWidget->hide(); m_stateManager.setDefaultSizes(ui->mainSplitter, UISizeVector() << 280 << -1 << -1); m_stateManager.setDefaultSizes(ui->structureSplitter, UISizeVector() << "50%" << "50%"); } TextDocumentInspectorWidget::~TextDocumentInspectorWidget() = default; void TextDocumentInspectorWidget::documentSelected(const QItemSelection &selected, const QItemSelection &deselected) { Q_UNUSED(deselected); if (selected.isEmpty()) { return; } const QModelIndex selectedRow = selected.first().topLeft(); QObject *selectedObj = selectedRow.data(ObjectModel::ObjectRole).value(); QTextDocument *doc = qobject_cast(selectedObj); if (m_currentDocument) { disconnect(m_currentDocument.data(), &QTextDocument::contentsChanged, this, &TextDocumentInspectorWidget::documentContentChanged); } m_currentDocument = QPointer(doc); if (doc) { ui->documentView->setDocument(doc); connect(doc, &QTextDocument::contentsChanged, this, &TextDocumentInspectorWidget::documentContentChanged); documentContentChanged(); } } void TextDocumentInspectorWidget::documentElementSelected(const QItemSelection &selected, const QItemSelection &deselected) { Q_UNUSED(deselected); const QModelIndex selectedRow = selected.first().topLeft(); const QRectF boundingBox = selectedRow.data(TextDocumentModel::BoundingBoxRole).toRectF(); ui->documentView->setShowBoundingBox(boundingBox); } void TextDocumentInspectorWidget::documentContentChanged() { ui->htmlView->setPlainText(m_currentDocument->toHtml()); } void TextDocumentInspectorWidget::documentContextMenu(QPoint pos) { const auto index = ui->documentList->indexAt(pos); if (!index.isValid()) return; const auto objectId = index.data(ObjectModel::ObjectIdRole).value(); QMenu menu; ContextMenuExtension ext(objectId); ext.setLocation(ContextMenuExtension::Creation, index.data(ObjectModel::CreationLocationRole).value()); ext.setLocation(ContextMenuExtension::Declaration, index.data(ObjectModel::DeclarationLocationRole).value()); ext.populateMenu(&menu); menu.exec(ui->documentList->viewport()->mapToGlobal(pos)); } gammaray-2.11.3/plugins/textdocumentinspector/textdocumentinspectorwidget.h000066400000000000000000000050171413310443000275520ustar00rootroot00000000000000/* textdocumentinspectorwidget.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_TEXTDOCUMENTINSPECTOR_TEXTDOCUMENTINSPECTORWIDGET_H #define GAMMARAY_TEXTDOCUMENTINSPECTOR_TEXTDOCUMENTINSPECTORWIDGET_H #include #include #include #include #include QT_BEGIN_NAMESPACE class QItemSelection; QT_END_NAMESPACE namespace GammaRay { class TextDocumentModel; class TextDocumentFormatModel; namespace Ui { class TextDocumentInspectorWidget; } class TextDocumentInspectorWidget : public QWidget { Q_OBJECT public: explicit TextDocumentInspectorWidget(QWidget *parent = nullptr); ~TextDocumentInspectorWidget() override; private slots: void documentSelected(const QItemSelection &selected, const QItemSelection &deselected); void documentElementSelected(const QItemSelection &selected, const QItemSelection &deselected); void documentContentChanged(); void documentContextMenu(QPoint pos); private: QScopedPointer ui; UIStateManager m_stateManager; QPointer m_currentDocument; }; class TextDocumentInspectorWidgetFactory : public QObject, public StandardToolUiFactory { Q_OBJECT Q_INTERFACES(GammaRay::ToolUiFactory) Q_PLUGIN_METADATA( IID "com.kdab.GammaRay.ToolUiFactory" FILE "gammaray_textdocumentinspector.json") }; } #endif // GAMMARAY_TEXTDOCUMENTINSPECTOR_H gammaray-2.11.3/plugins/textdocumentinspector/textdocumentinspectorwidget.ui000066400000000000000000000105351413310443000277410ustar00rootroot00000000000000 GammaRay::TextDocumentInspectorWidget 0 0 762 631 Qt::Horizontal All documents: Qt::CustomContextMenu false true false Qt::Vertical Document structure: false true Element format: false true false 0 Content 40 HTML 40 GammaRay::DeferredTreeView QTreeView
    ui/deferredtreeview.h
    GammaRay::TextDocumentContentView QTextEdit
    textdocumentcontentview.h
    gammaray-2.11.3/plugins/textdocumentinspector/textdocumentmodel.cpp000066400000000000000000000155111413310443000257730ustar00rootroot00000000000000/* textdocumentmodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "textdocumentmodel.h" #include #include #include #include #include using namespace GammaRay; static QString formatTypeToString(int type) { switch (type) { case QTextFormat::InvalidFormat: return QStringLiteral("Invalid"); case QTextFormat::BlockFormat: return QStringLiteral("Block"); case QTextFormat::CharFormat: return QStringLiteral("Char"); case QTextFormat::ListFormat: return QStringLiteral("List"); case QTextFormat::FrameFormat: return QStringLiteral("Frame"); case QTextFormat::UserFormat: return QStringLiteral("User"); } return QStringLiteral("Unknown format: %1").arg(type); } TextDocumentModel::TextDocumentModel(QObject *parent) : QStandardItemModel(parent) , m_document(nullptr) { } void TextDocumentModel::setDocument(QTextDocument *doc) { if (m_document) disconnect(m_document, &QTextDocument::contentsChanged, this, &TextDocumentModel::documentChanged); m_document = doc; fillModel(); if (m_document) connect(m_document, &QTextDocument::contentsChanged, this, &TextDocumentModel::documentChanged); } void TextDocumentModel::documentChanged() { // TODO fillModel(); } void TextDocumentModel::fillModel() { clear(); if (!m_document) return; QStandardItem *item = new QStandardItem(tr("Root Frame")); const QTextFormat f = m_document->rootFrame()->frameFormat(); item->setData(QVariant::fromValue(f), FormatRole); item->setEditable(false); QStandardItemModel::appendRow(QList() << item << formatItem(m_document->rootFrame()->frameFormat())); fillFrame(m_document->rootFrame(), item); setHorizontalHeaderLabels(QStringList() << tr("Element") << tr("Format")); } void TextDocumentModel::fillFrame(QTextFrame *frame, QStandardItem *parent) { for (auto it = frame->begin(); it != frame->end(); ++it) fillFrameIterator(it, parent); } void TextDocumentModel::fillFrameIterator(const QTextFrame::iterator &it, QStandardItem *parent) { if (QTextFrame *frame = it.currentFrame()) { const QRectF b = m_document->documentLayout()->frameBoundingRect(frame); QTextTable *table = qobject_cast(frame); auto item = new QStandardItem; if (table) { item->setText(tr("Table")); appendRow(parent, item, table->format(), b); fillTable(table, item); } else { item->setText(tr("Frame")); appendRow(parent, item, frame->frameFormat(), b); fillFrame(frame, item); } } const QTextBlock block = it.currentBlock(); if (block.isValid()) { auto item = new QStandardItem; item->setText(tr("Block: %1").arg(block.text())); const QRectF b = m_document->documentLayout()->blockBoundingRect(block); appendRow(parent, item, block.blockFormat(), b); fillBlock(block, item); } } void TextDocumentModel::fillTable(QTextTable *table, QStandardItem *parent) { for (int row = 0; row < table->rows(); ++row) { for (int col = 0; col < table->columns(); ++col) { QTextTableCell cell = table->cellAt(row, col); auto *item = new QStandardItem; item->setText(tr("Cell %1x%2").arg(row).arg(col)); appendRow(parent, item, cell.format()); for (auto it = cell.begin(); it != cell.end(); ++it) fillFrameIterator(it, item); } } } void TextDocumentModel::fillBlock(const QTextBlock &block, QStandardItem *parent) { for (auto it = block.begin(); it != block.end(); ++it) { QStandardItem *item = new QStandardItem(tr("Fragment: %1").arg(it.fragment().text())); const QRectF b = m_document->documentLayout()->blockBoundingRect(block); appendRow(parent, item, it.fragment().charFormat(), b); if (!block.layout()) continue; #if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0) foreach (const auto &range, block.layout()->formats()) { const auto start = std::max(range.start, it.fragment().position() - block.position()); const auto end = std::min(range.start + range.length, it.fragment().position() + it.fragment().length() - block.position()); if (start >= end) continue; auto child = new QStandardItem(tr("Layout Range: %1").arg(it.fragment().text().mid(start, end -start))); appendRow(item, child, range.format, QRectF()); } #endif } } QStandardItem *TextDocumentModel::formatItem(const QTextFormat &format) { auto *item = new QStandardItem; if (!format.isValid()) { item->setText(tr("no format")); } else if (format.isImageFormat()) { const QTextImageFormat imgformat = format.toImageFormat(); item->setText(tr("Image: %1").arg(imgformat.name())); } else { item->setText(formatTypeToString(format.type())); } item->setEditable(false); return item; } void TextDocumentModel::appendRow(QStandardItem *parent, QStandardItem *item, const QTextFormat &format, const QRectF &boundingBox) { item->setData(QVariant::fromValue(format), FormatRole); item->setData(boundingBox, BoundingBoxRole); item->setEditable(false); parent->appendRow(QList() << item << formatItem(format)); } gammaray-2.11.3/plugins/textdocumentinspector/textdocumentmodel.h000066400000000000000000000044551413310443000254450ustar00rootroot00000000000000/* textdocumentmodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_TEXTDOCUMENTINSPECTOR_TEXTDOCUMENTMODEL_H #define GAMMARAY_TEXTDOCUMENTINSPECTOR_TEXTDOCUMENTMODEL_H #include "common/modelroles.h" #include #include QT_BEGIN_NAMESPACE class QTextTable; class QTextBlock; class QTextFrame; class QTextDocument; QT_END_NAMESPACE namespace GammaRay { class TextDocumentModel : public QStandardItemModel { Q_OBJECT public: explicit TextDocumentModel(QObject *parent = nullptr); enum Roles { FormatRole = UserRole, BoundingBoxRole }; void setDocument(QTextDocument *doc); private: void fillModel(); void fillFrame(QTextFrame *frame, QStandardItem *parent); void fillFrameIterator(const QTextFrame::iterator &it, QStandardItem *parent); void fillTable(QTextTable *table, QStandardItem *parent); void fillBlock(const QTextBlock &block, QStandardItem *parent); QStandardItem *formatItem(const QTextFormat &format); void appendRow(QStandardItem *parent, QStandardItem *item, const QTextFormat &format, const QRectF &boundingBox = QRectF()); private slots: void documentChanged(); private: QTextDocument *m_document; }; } #endif gammaray-2.11.3/plugins/timertop/000077500000000000000000000000001413310443000167115ustar00rootroot00000000000000gammaray-2.11.3/plugins/timertop/CMakeLists.txt000066400000000000000000000013471413310443000214560ustar00rootroot00000000000000# probe part if (NOT GAMMARAY_CLIENT_ONLY_BUILD) set(gammaray_timertop_plugin_srcs timertop.cpp timertopinterface.cpp timermodel.cpp timerinfo.cpp ) gammaray_add_plugin(gammaray_timertop_plugin JSON gammaray_timertop.json SOURCES ${gammaray_timertop_plugin_srcs} ) target_link_libraries(gammaray_timertop_plugin gammaray_core ) endif() # ui part if(GAMMARAY_BUILD_UI) set(gammaray_timertop_plugin_ui_srcs timertopwidget.cpp timertopinterface.cpp timertopclient.cpp clienttimermodel.cpp ) gammaray_add_plugin(gammaray_timertop_ui_plugin JSON gammaray_timertop.json SOURCES ${gammaray_timertop_plugin_ui_srcs} ) target_link_libraries(gammaray_timertop_ui_plugin gammaray_ui ) endif() gammaray-2.11.3/plugins/timertop/clienttimermodel.cpp000066400000000000000000000146561413310443000227710ustar00rootroot00000000000000/* clienttimermodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "clienttimermodel.h" #include "timerinfo.h" #include "timermodel.h" #include #include #include using namespace GammaRay; ClientTimerModel::ClientTimerModel(QObject *parent) : QSortFilterProxyModel(parent) { } ClientTimerModel::~ClientTimerModel() = default; QVariant ClientTimerModel::data(const QModelIndex &index, int role) const { if (hasIndex(index.row(), index.column())) { if (role == Qt::DisplayRole) { switch (static_cast(index.column())) { case TimerModel::ColumnCount: Q_ASSERT(false); case TimerModel::ObjectNameColumn: case TimerModel::TimerIdColumn: case TimerModel::TotalWakeupsColumn: // Use source model data break; case TimerModel::StateColumn: return stateToString(QSortFilterProxyModel::data(index, role).toInt(), QSortFilterProxyModel::data(index, TimerModel::TimerIntervalRole).toInt()); case TimerModel::WakeupsPerSecColumn: return wakeupsPerSecToString(QSortFilterProxyModel::data(index, role).toReal()); case TimerModel::TimePerWakeupColumn: return timePerWakeupToString(QSortFilterProxyModel::data(index, role).toReal()); case TimerModel::MaxTimePerWakeupColumn: return maxWakeupTimeToString(QSortFilterProxyModel::data(index, role).toUInt()); } } else if (role == Qt::ToolTipRole) { const QModelIndex sibling = index.sibling(index.row(), TimerModel::ObjectNameColumn); const TimerId::Type type = TimerId::Type(sibling.data(TimerModel::TimerTypeRole).toInt()); switch (type) { case TimerId::InvalidType: return tr("Invalid"); case TimerId::QQmlTimerType: return tr("QQmlTimer"); case TimerId::QTimerType: return tr("QTimer"); case TimerId::QObjectType: return tr("Free Timer"); } } else if (role == Qt::FontRole) { const QModelIndex stateSibling = index.sibling(index.row(), TimerModel::StateColumn); const TimerIdInfo::State state = TimerIdInfo::State(QSortFilterProxyModel::data(stateSibling, Qt::DisplayRole).toInt()); const QModelIndex typeSibling = index.sibling(index.row(), TimerModel::ObjectNameColumn); const TimerId::Type type = TimerId::Type(typeSibling.data(TimerModel::TimerTypeRole).toInt()); QFont font = QApplication::font("QAbstractItemView"); font.setStrikeOut(type == TimerId::InvalidType || state == TimerIdInfo::InvalidState); return QVariant::fromValue(font); } else if(role == Qt::BackgroundRole) { const QModelIndex sibling = index.sibling(index.row(), TimerModel::ObjectNameColumn); const TimerId::Type type = TimerId::Type(sibling.data(TimerModel::TimerTypeRole).toInt()); switch (type) { case TimerId::InvalidType: return QColor(255, 0, 0, 80); case TimerId::QQmlTimerType: return QColor(80, 0, 0, 40); case TimerId::QTimerType: return QColor(0, 80, 0, 40); case TimerId::QObjectType: return QColor(0, 0, 80, 40); } } } return QSortFilterProxyModel::data(index, role); } QVariant ClientTimerModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { switch (section) { case TimerModel::ObjectNameColumn: return tr("Object Name"); case TimerModel::StateColumn: return tr("State"); case TimerModel::TotalWakeupsColumn: return tr("Total Wakeups"); case TimerModel::WakeupsPerSecColumn: return tr("Wakeups/Sec"); case TimerModel::TimePerWakeupColumn: return tr("Time/Wakeup [uSecs]"); case TimerModel::MaxTimePerWakeupColumn: return tr("Max Wakeup Time [uSecs]"); case TimerModel::TimerIdColumn: return tr("Timer ID"); case TimerModel::ColumnCount: break; } } return QSortFilterProxyModel::headerData(section, orientation, role); } QString ClientTimerModel::stateToString(int state, int interval) { switch (static_cast(state)) { case TimerIdInfo::InvalidState: // None return tr("None (%1 ms)").arg(interval); case TimerIdInfo::InactiveState: // Not Running return tr("Inactive (%1 ms)").arg(interval); case TimerIdInfo::SingleShotState: // Single Shot return tr("Singleshot (%1 ms)").arg(interval); case TimerIdInfo::RepeatState: // Repeat return tr("Repeating (%1 ms)").arg(interval); } return QString(); } QString ClientTimerModel::wakeupsPerSecToString(qreal value) { return qFuzzyIsNull(value) ? tr("0") : QString::number(value, 'f', 1); } QString ClientTimerModel::timePerWakeupToString(qreal value) { return qFuzzyIsNull(value) ? QStringLiteral("N/A") : QString::number(value, 'f', 1); } QString ClientTimerModel::maxWakeupTimeToString(uint value) { return value == 0 ? tr("N/A") : QString::number(value); } gammaray-2.11.3/plugins/timertop/clienttimermodel.h000066400000000000000000000035531413310443000224300ustar00rootroot00000000000000/* clienttimermodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_CLIENTTIMERMODEL_H #define GAMMARAY_CLIENTTIMERMODEL_H #include namespace GammaRay { class ClientTimerModel : public QSortFilterProxyModel { Q_OBJECT public: explicit ClientTimerModel(QObject *parent = nullptr); ~ClientTimerModel() override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; QVariant headerData(int section, Qt::Orientation orientation, int role) const override; private: static QString stateToString(int state, int interval); static QString wakeupsPerSecToString(qreal value); static QString timePerWakeupToString(qreal value); static QString maxWakeupTimeToString(uint value); }; } #endif // GAMMARAY_CLIENTTIMERMODEL_H gammaray-2.11.3/plugins/timertop/gammaray_timertop.json000066400000000000000000000003521413310443000233250ustar00rootroot00000000000000{ "id": "gammaray_timertop", "name": "Timers", "name[de]": "Timer", "selectableTypes": [ "QTimer" ], "types": [ "QTimer", "QQmlTimer", "QUnifiedTimer", "QObject" ] } gammaray-2.11.3/plugins/timertop/timerinfo.cpp000066400000000000000000000127401413310443000214150ustar00rootroot00000000000000/* timerinfo.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Thomas McGuire Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "timerinfo.h" #include "timermodel.h" #include #include #include #include #include #include #include using namespace GammaRay; namespace GammaRay { uint qHash(const TimerId &id) { switch (id.m_type) { case TimerId::InvalidType: Q_UNREACHABLE(); break; case TimerId::QQmlTimerType: case TimerId::QTimerType: return ::qHash(id.m_timerAddress); case TimerId::QObjectType: return ::qHash(id.m_timerId) + ::qHash(id.m_timerAddress); } return 0; } } TimerId::TimerId(QObject *timer) : m_type(QQmlTimerType) , m_timerAddress(timer) { Q_ASSERT(timer); if (qobject_cast(timer)) { m_type = QTimerType; } } TimerId::TimerId(int timerId, QObject *receiver) : m_type(QObjectType) , m_timerAddress(receiver) , m_timerId(timerId) { Q_ASSERT(m_timerId != -1); } TimerId::Type TimerId::type() const { return m_type; } QObject* TimerId::address() const { return m_timerAddress; } int TimerId::timerId() const { return m_timerId; } bool TimerId::operator==(const TimerId &other) const { if (m_type != other.m_type) return false; switch (m_type) { case TimerId::InvalidType: Q_UNREACHABLE(); break; case TimerId::QQmlTimerType: case TimerId::QTimerType: return m_timerAddress == other.m_timerAddress; case TimerId::QObjectType: return m_timerId == other.m_timerId && m_timerAddress == other.m_timerAddress; } return false; } bool TimerId::operator<(const TimerId &other) const { if (m_type == other.m_type) { switch (m_type) { case TimerId::InvalidType: { Q_UNREACHABLE(); return false; } case TimerId::QQmlTimerType: case TimerId::QTimerType: return m_timerAddress < other.m_timerAddress; case TimerId::QObjectType: { if (m_timerId == other.m_timerId) return m_timerAddress < other.m_timerAddress; return m_timerId < other.m_timerId; } } } return m_type < other.m_type; } void TimerIdInfo::update(const TimerId &id, QObject *receiver) { QObject *object = receiver ? receiver : id.address(); type = id.type(); state = InvalidState; // The timer became invalid if (!object || (lastReceiverAddress == object && !lastReceiverObject)) { type = TimerId::InvalidType; return; } interval = 0; switch (id.type()) { case TimerId::InvalidType: { Q_UNREACHABLE(); break; } case TimerId::QQmlTimerType: { timerId = -1; interval = object->property("interval").toInt(); lastReceiverAddress = id.address(); lastReceiverObject = object; objectName = Util::displayString(object); if (!object->property("running").toBool()) state = InactiveState; else if (!object->property("repeat").toBool()) state = SingleShotState; else state = RepeatState; break; } case TimerId::QTimerType: { const QTimer *const timer = qobject_cast(object); timerId = timer->timerId(); interval = timer->interval(); lastReceiverAddress = id.address(); lastReceiverObject = object; objectName = Util::displayString(object); if (!timer->isActive()) state = InactiveState; else if (timer->isSingleShot()) state = SingleShotState; else state = RepeatState; break; } case TimerId::QObjectType: { timerId = id.timerId(); lastReceiverAddress = object; lastReceiverObject = receiver; objectName = Util::displayString(object); const QAbstractEventDispatcher *dispatcher = QAbstractEventDispatcher::instance(object->thread()); const QList timers = dispatcher->registeredTimers(object); const auto it = std::find_if(timers.constBegin(), timers.constEnd(), [this](const QAbstractEventDispatcher::TimerInfo &timer) { return timer.timerId == timerId; }); if (it != timers.constEnd()) { interval = (*it).interval; state = RepeatState; } break; } } } gammaray-2.11.3/plugins/timertop/timerinfo.h000066400000000000000000000061741413310443000210660ustar00rootroot00000000000000/* timerinfo.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Thomas McGuire Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_TIMERTOP_TIMERINFO_H #define GAMMARAY_TIMERTOP_TIMERINFO_H #include #include #include QT_BEGIN_NAMESPACE class QTimer; QT_END_NAMESPACE namespace GammaRay { class TimerId { friend uint qHash(const TimerId &); public: enum Type { InvalidType, // A QObject, no timer id QQmlTimerType, // A QObject, unstable timerId across stop/start QTimerType, // A timerId attached to a QObject QObjectType }; TimerId() = default; explicit TimerId(QObject *timer); explicit TimerId(int timerId, QObject *receiver); Type type() const; QObject* address() const; int timerId() const; bool operator==(const TimerId &other) const; bool operator<(const TimerId &other) const; private: Type m_type = InvalidType; QObject *m_timerAddress = nullptr; int m_timerId = -1; }; struct TimerIdInfo { enum State { InvalidState, InactiveState, SingleShotState, RepeatState }; TimerIdInfo() : type(TimerId::InvalidType) , timerId(-1) , interval(0) , totalWakeups(0) , lastReceiverAddress(nullptr) , state(InvalidState) , wakeupsPerSec(0.0) , timePerWakeup(0.0) , maxWakeupTime(0) { } ~TimerIdInfo() { } /** Update timer information to the current state. * It's the callers responsibility to ensure the receiver or timer object * is and stays valid during this call, if necessary by using Probe::objectLock(). */ void update(const TimerId &id, QObject *receiver = nullptr); TimerId::Type type; int timerId; int interval; uint totalWakeups; QObject* lastReceiverAddress; // The QTimer/QQmlTimer or last known receiver address QPointer lastReceiverObject; QString objectName; State state; qreal wakeupsPerSec; qreal timePerWakeup; uint maxWakeupTime; }; uint qHash(const TimerId &id); } #endif // GAMMARAY_TIMERINFO_H gammaray-2.11.3/plugins/timertop/timermodel.cpp000066400000000000000000000623231413310443000215640ustar00rootroot00000000000000 /* timermodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Thomas McGuire Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "timermodel.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #define QOBJECT_METAMETHOD(Object, Method) \ Object::staticMetaObject.method(Object::staticMetaObject.indexOfSlot(#Method)) using namespace GammaRay; using namespace std; static QPointer s_timerModel; static const char s_qmlTimerClassName[] = "QQmlTimer"; static const int s_maxTimeoutEvents = 1000; static const int s_maxTimeSpan = 10000; namespace GammaRay { struct TimeoutEvent { explicit TimeoutEvent(const QTime &timeStamp = QTime(), int executionTime = -1) : timeStamp(timeStamp) , executionTime(executionTime) { } QTime timeStamp; int executionTime; }; struct TimerIdData { TimerIdData() = default; void update(const TimerId &id, QObject *receiver = nullptr) { info.update(id, receiver); } void addEvent(const GammaRay::TimeoutEvent &event) { timeoutEvents.append(event); if (timeoutEvents.size() > s_maxTimeoutEvents) timeoutEvents.removeFirst(); totalWakeupsEvents++; changed = true; } TimerIdInfo &toInfo(TimerId::Type type) { info.totalWakeups = totalWakeups(); info.wakeupsPerSec = wakeupsPerSec(); info.timePerWakeup = timePerWakeup(type); info.maxWakeupTime = maxWakeupTime(type); return info; } int totalWakeups() const { return totalWakeupsEvents; } qreal wakeupsPerSec() const { int wakeups = 0; int start = 0; int end = timeoutEvents.size() - 1; for (int i = end; i >= 0; i--) { const TimeoutEvent &event = timeoutEvents.at(i); if (event.timeStamp.msecsTo(QTime::currentTime()) > s_maxTimeSpan) { start = i; break; } wakeups++; } if (wakeups > 0 && end > start) { const QTime startTime = timeoutEvents[start].timeStamp; const QTime endTime = timeoutEvents[end].timeStamp; const int timeSpan = startTime.msecsTo(endTime); const qreal wakeupsPerSec = wakeups / (qreal)timeSpan * (qreal)1000; return wakeupsPerSec; } return 0; } qreal timePerWakeup(TimerId::Type type) const { if (type == TimerId::QObjectType) return 0; int wakeups = 0; int totalTime = 0; for (int i = timeoutEvents.size() - 1; i >= 0; i--) { const TimeoutEvent &event = timeoutEvents.at(i); if (event.timeStamp.msecsTo(QTime::currentTime()) > s_maxTimeSpan) break; wakeups++; totalTime += event.executionTime; } if (wakeups > 0) return (qreal)totalTime / (qreal)wakeups; return 0; } int maxWakeupTime(TimerId::Type type) const { if (type == TimerId::QObjectType) return 0; int max = 0; for (auto event : timeoutEvents) { if (event.executionTime > max) max = event.executionTime; } return max; } TimerIdInfo info; int totalWakeupsEvents = 0; QElapsedTimer functionCallTimer; QList timeoutEvents; bool changed = false; }; } Q_DECLARE_METATYPE(GammaRay::TimeoutEvent) TimerModel::TimerModel(QObject *parent) : QAbstractTableModel(parent) , m_sourceModel(nullptr) , m_pushTimer(new QTimer(this)) , m_triggerPushChangesMethod(QOBJECT_METAMETHOD(TimerModel, triggerPushChanges())) , m_timeoutIndex(QTimer::staticMetaObject.indexOfSignal("timeout()")) , m_qmlTimerTriggeredIndex(-1) , m_qmlTimerRunningChangedIndex(-1) { Q_ASSERT(m_triggerPushChangesMethod.methodIndex() != -1); m_pushTimer->setSingleShot(true); m_pushTimer->setInterval(5000); connect(m_pushTimer, &QTimer::timeout, this, &TimerModel::pushChanges); QInternal::registerCallback(QInternal::EventNotifyCallback, eventNotifyCallback); } const TimerIdInfo *TimerModel::findTimerInfo(const QModelIndex &index) const { if (index.row() < m_sourceModel->rowCount()) { const QModelIndex sourceIndex = m_sourceModel->index(index.row(), 0); QObject * const timerObject = sourceIndex.data(ObjectModel::ObjectRole).value(); // The object might have already be deleted even if our index is valid if (!timerObject) return nullptr; const TimerId id = TimerId(timerObject); auto it = m_timersInfo.find(id); if (it == m_timersInfo.end()) { it = m_timersInfo.insert(id, TimerIdInfo()); // safe, as timerObject has been validated by the source model it.value().update(id); } return &it.value(); } else { if (index.row() < m_sourceModel->rowCount() + m_freeTimersInfo.count()) return &m_freeTimersInfo[index.row() - m_sourceModel->rowCount()]; } return nullptr; } bool TimerModel::canHandleCaller(QObject *caller, int methodIndex) const { const bool isQTimer = qobject_cast(caller) != nullptr; const bool isQQmlTimer = caller->inherits(s_qmlTimerClassName); if (isQQmlTimer && m_qmlTimerTriggeredIndex < 0) { m_qmlTimerTriggeredIndex = caller->metaObject()->indexOfMethod("triggered()"); Q_ASSERT(m_qmlTimerTriggeredIndex != -1); m_qmlTimerRunningChangedIndex = caller->metaObject()->indexOfMethod("runningChanged()"); Q_ASSERT(m_qmlTimerRunningChangedIndex != -1); } return (isQTimer && m_timeoutIndex == methodIndex) || (isQQmlTimer && (m_qmlTimerTriggeredIndex == methodIndex || m_qmlTimerRunningChangedIndex == methodIndex)); } void TimerModel::checkDispatcherStatus(QObject *object) { // m_mutex have to be locked!! static QHash dispatcherChecks; QAbstractEventDispatcher *dispatcher = QAbstractEventDispatcher::instance(object->thread()); auto it = dispatcherChecks.find(dispatcher); if (it == dispatcherChecks.end()) { it = dispatcherChecks.insert(dispatcher, QTime()); it.value().start(); } if (it.value().elapsed() < m_pushTimer->interval()) return; for (auto gIt = m_gatheredTimersData.begin(), end = m_gatheredTimersData.end(); gIt != end; ++gIt) { QObject *gItObject = gIt.value().info.lastReceiverObject; QAbstractEventDispatcher *gItDispatcher = QAbstractEventDispatcher::instance(gItObject ? gItObject->thread() : nullptr); if (gItDispatcher != dispatcher) { if (!gItObject) gIt.value().update(gIt.key(), gItObject); continue; } switch(gIt.key().type()) { case TimerId::InvalidType: case TimerId::QQmlTimerType: continue; case TimerId::QTimerType: case TimerId::QObjectType: break; } switch(gIt.value().info.state) { case TimerIdInfo::InactiveState: gIt.value().update(gIt.key(), gItObject); case TimerIdInfo::InvalidState: continue; case TimerIdInfo::SingleShotState: case TimerIdInfo::RepeatState: break; } int remaining = -1; if (gIt.key().timerId() > -1) { remaining = dispatcher->remainingTime(gIt.key().timerId()); } // Timer inactive or invalid, or free timer if (remaining == -1 || gIt.key().type() == TimerId::QObjectType) gIt.value().update(gIt.key(), gItObject); } it.value().restart(); } bool TimerModel::eventNotifyCallback(void *data[]) { Q_ASSERT(TimerModel::isInitialized()); /* * data[0] == receiver * data[1] == event * data[2] == bool result ref, what is returned by caller if this function return true * (QCoreApplication::notifyInternal / QCoreApplication::notifyInternal2) */ QObject *receiver = static_cast(data[0]); QEvent *event = static_cast(data[1]); // bool *result = static_cast(data[2]); if (event->type() == QEvent::Timer) { const QTimerEvent *const timerEvent = static_cast(event); const QTimer *const timer = qobject_cast(receiver); // If there is a QTimer associated with this timer ID, don't handle it here, it will be handled // by the signal hooks preSignalActivate/postSignalActivate. if (timer && timer->timerId() == timerEvent->timerId()) { return false; } { QMutexLocker locker(&s_timerModel->m_mutex); const TimerId id(timerEvent->timerId(), receiver); auto it = s_timerModel->m_gatheredTimersData.find(id); if (it == s_timerModel->m_gatheredTimersData.end()) { it = s_timerModel->m_gatheredTimersData.insert(id, TimerIdData()); } const TimeoutEvent timeoutEvent(QTime::currentTime(), -1); // safe, we are called from the receiver thread it.value().update(id, receiver); it.value().addEvent(timeoutEvent); s_timerModel->checkDispatcherStatus(receiver); s_timerModel->m_triggerPushChangesMethod.invoke(s_timerModel, Qt::QueuedConnection); } } return false; } TimerModel::~TimerModel() { QMutexLocker locker(&m_mutex); QInternal::unregisterCallback(QInternal::EventNotifyCallback, eventNotifyCallback); m_gatheredTimersData.clear(); m_timersInfo.clear(); m_freeTimersInfo.clear(); } bool TimerModel::isInitialized() { return s_timerModel != nullptr; } TimerModel *TimerModel::instance() { if (!s_timerModel) s_timerModel = new TimerModel; Q_ASSERT(s_timerModel); return s_timerModel; } void TimerModel::preSignalActivate(QObject *caller, int methodIndex) { // We are in the thread of the caller emitting the signal // The probe did NOT locked the objectLock at this point. Q_ASSERT(TimerModel::isInitialized()); if (!canHandleCaller(caller, methodIndex)) return; QMutexLocker locker(&m_mutex); const TimerId id(caller); auto it = m_gatheredTimersData.find(id); if (it == m_gatheredTimersData.end()) { it = m_gatheredTimersData.insert(id, TimerIdData()); // safe, we are called from the receiver thread, before a slot had a chance to delete caller it.value().update(id); } if (methodIndex != m_qmlTimerRunningChangedIndex) { if (it.value().functionCallTimer.isValid()) { cout << "TimerModel::preSignalActivate(): Recursive timeout for timer " << (void *)caller << "!" << endl; return; } it.value().functionCallTimer.start(); } } void TimerModel::postSignalActivate(QObject *caller, int methodIndex) { // We are in the thread of the caller emitting the signal // The probe did unlock the objectLock at this point again but validated caller Q_ASSERT(TimerModel::isInitialized()); if (!canHandleCaller(caller, methodIndex)) return; QMutexLocker locker(&m_mutex); const TimerId id(caller); auto it = m_gatheredTimersData.find(id); if (it == m_gatheredTimersData.end()) { // A postSignalActivate can be triggered without a preSignalActivate first // and/or the caller is not yet gathered. return; } if (methodIndex != m_qmlTimerRunningChangedIndex) { if (!it.value().functionCallTimer.isValid()) { cout << "TimerModel::postSignalActivate(): Timer not active: " << (void *)caller << "!" << endl; return; } } // safe, nobody in this thread had a chance to delete caller since Probe validated it it.value().update(id); if (methodIndex != m_qmlTimerRunningChangedIndex) { const TimeoutEvent timeoutEvent(QTime::currentTime(), it.value().functionCallTimer.nsecsElapsed() / 1000); // expected unit is µs it.value().addEvent(timeoutEvent); it.value().functionCallTimer.invalidate(); } checkDispatcherStatus(caller); m_triggerPushChangesMethod.invoke(this, Qt::QueuedConnection); } void TimerModel::setSourceModel(QAbstractItemModel *sourceModel) { Q_ASSERT(!m_sourceModel); beginResetModel(); m_sourceModel = sourceModel; connect(m_sourceModel, &QAbstractItemModel::rowsAboutToBeInserted, this, &TimerModel::slotBeginInsertRows); connect(m_sourceModel, &QAbstractItemModel::rowsInserted, this, &TimerModel::slotEndInsertRows); connect(m_sourceModel, &QAbstractItemModel::rowsAboutToBeRemoved, this, &TimerModel::slotBeginRemoveRows); connect(m_sourceModel, &QAbstractItemModel::rowsRemoved, this, &TimerModel::slotEndRemoveRows); connect(m_sourceModel, &QAbstractItemModel::modelAboutToBeReset, this, &TimerModel::slotBeginReset); connect(m_sourceModel, &QAbstractItemModel::modelReset, this, &TimerModel::slotEndReset); connect(m_sourceModel, &QAbstractItemModel::layoutAboutToBeChanged, this, &TimerModel::slotBeginReset); connect(m_sourceModel, &QAbstractItemModel::layoutChanged, this, &TimerModel::slotEndReset); endResetModel(); } QModelIndex TimerModel::index(int row, int column, const QModelIndex &parent) const { if (hasIndex(row, column, parent)) { if (row < m_sourceModel->rowCount()) { const QModelIndex sourceIndex = m_sourceModel->index(row, 0); QObject *const timerObject = sourceIndex.data(ObjectModel::ObjectRole).value(); return createIndex(row, column, timerObject); } else { return createIndex(row, column, row - m_sourceModel->rowCount()); } } return {}; } int TimerModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); return ColumnCount; } int TimerModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent); if (!m_sourceModel || parent.isValid()) return 0; return m_sourceModel->rowCount() + m_freeTimersInfo.count(); } QVariant TimerModel::data(const QModelIndex &index, int role) const { if (!m_sourceModel || !index.isValid()) return QVariant(); if (role == Qt::DisplayRole) { const TimerIdInfo *const timerInfo = findTimerInfo(index); if (!timerInfo) return QVariant(); switch (index.column()) { case ObjectNameColumn: return timerInfo->objectName; case StateColumn: return int(timerInfo->state); case TotalWakeupsColumn: return timerInfo->totalWakeups; case WakeupsPerSecColumn: return timerInfo->wakeupsPerSec; case TimePerWakeupColumn: return timerInfo->timePerWakeup; case MaxTimePerWakeupColumn: return timerInfo->maxWakeupTime; case TimerIdColumn: return timerInfo->timerId; case ColumnCount: break; } } else if (role == TimerIntervalRole && index.column() == StateColumn) { const TimerIdInfo *const timerInfo = findTimerInfo(index); if (timerInfo) { return timerInfo->interval; } } else if (index.column() == 0) { auto timerInfo = findTimerInfo(index); if (!timerInfo) return QVariant(); auto object = timerInfo->lastReceiverObject.data(); if (!object) return QVariant(); switch (role) { case ObjectModel::ObjectIdRole: { Q_ASSERT(index.row() >= m_sourceModel->rowCount() || object == index.internalPointer()); return QVariant::fromValue(ObjectId(object)); } case ObjectModel::CreationLocationRole: { const auto loc = ObjectDataProvider::creationLocation(object); return loc.isValid() ? QVariant::fromValue(loc) : QVariant(); } case ObjectModel::DeclarationLocationRole: { const auto loc = ObjectDataProvider::declarationLocation(object); return loc.isValid() ? QVariant::fromValue(loc) : QVariant(); } case TimerTypeRole: return int(timerInfo->type); } } return QVariant(); } QMap TimerModel::itemData(const QModelIndex &index) const { auto d = QAbstractTableModel::itemData(index); if (index.column() == 0) { d.insert(ObjectModel::ObjectIdRole, index.data(ObjectModel::ObjectIdRole)); auto v = index.data(ObjectModel::CreationLocationRole); if (v.isValid()) d.insert(ObjectModel::CreationLocationRole, v); v = index.data(ObjectModel::DeclarationLocationRole); if (v.isValid()) d.insert(ObjectModel::DeclarationLocationRole, v); d.insert(TimerTypeRole, index.data(TimerTypeRole)); } if (index.column() == StateColumn) d.insert(TimerModel::TimerIntervalRole, index.data(TimerModel::TimerIntervalRole)); return d; } void TimerModel::clearHistory() { QMutexLocker locker(&m_mutex); m_gatheredTimersData.clear(); locker.unlock(); const int count = m_sourceModel->rowCount(); m_timersInfo.clear(); if (count > 0) { const QModelIndex tl = index(0, 0); const QModelIndex br = index(count - 1, columnCount() - 1); emit dataChanged(tl, br); } if (!m_freeTimersInfo.isEmpty()) { beginRemoveRows(QModelIndex(), m_sourceModel->rowCount(), m_sourceModel->rowCount() + m_freeTimersInfo.count() - 1); m_freeTimersInfo.clear(); endRemoveRows(); } } void TimerModel::triggerPushChanges() { if (!m_pushTimer->isActive()) m_pushTimer->start(); } void TimerModel::pushChanges() { QMutexLocker locker(&m_mutex); TimerIdInfoContainer changes; QSet activeQTimers; // TimerId are sort by types matching the TimerId::Type order first // so we garranty that free timers checks are done after any qqmltimer/qtimer. for (auto it = m_gatheredTimersData.begin(); it != m_gatheredTimersData.end();) { TimerIdData &itInfo = it.value(); // If a free timer did not changed and own an active QTimer id, // then make it invalid. if (!itInfo.changed) { if (it.key().type() == TimerId::QObjectType) { if (itInfo.info.state > TimerIdInfo::InactiveState) { if (activeQTimers.contains(itInfo.info.timerId) || !itInfo.info.lastReceiverObject) { itInfo.info.type = TimerId::InvalidType; itInfo.info.state = TimerIdInfo::InvalidState; itInfo.changed = true; } } } } if (itInfo.changed) { // If a TimerId of type TimerId::QObjectType just changed, // then this free timer id is still valid, remove it from activeQTimers. if (it.key().type() == TimerId::QObjectType) { if (itInfo.info.state > TimerIdInfo::InactiveState) { if (itInfo.info.lastReceiverObject) { activeQTimers.remove(itInfo.info.timerId); } else { itInfo.info.type = TimerId::InvalidType; itInfo.info.state = TimerIdInfo::InvalidState; } } } changes.insert(it.key(), itInfo.toInfo(it.key().type())); itInfo.changed = false; } // Remember active QTimer id to detect invalid free timers if (it.key().type() == TimerId::QTimerType && itInfo.info.state > TimerIdInfo::InactiveState) { activeQTimers << itInfo.info.timerId; } ++it; } locker.unlock(); applyChanges(changes); } void TimerModel::applyChanges(const TimerIdInfoContainer &changes) { QSet updatedIds; QVector> dataChangedRanges; // pair of first/last // Update QQmlTimer / QTimer entries for (int i = 0; i < m_sourceModel->rowCount(); ++i) { const QModelIndex sourceIndex = m_sourceModel->index(i, 0); QObject *const timerObject = sourceIndex.data(ObjectModel::ObjectRole).value(); // The object might have already be deleted even if our index is valid if (!timerObject) return; const TimerId id(timerObject); const auto cit = changes.constFind(id); auto it = m_timersInfo.find(id); if (it == m_timersInfo.end()) { it = m_timersInfo.insert(id, TimerIdInfo()); if (cit == changes.constEnd()) // safe, validated by the source model it.value().update(id); } if (cit != changes.constEnd()) { updatedIds << id; it.value() = cit.value(); if (dataChangedRanges.isEmpty() || dataChangedRanges.last().second != i - 1) { dataChangedRanges << qMakePair(i, i); } else { dataChangedRanges.last().second = i; } } } // Update existing free timers entries for (auto it = m_freeTimersInfo.begin(), end = m_freeTimersInfo.end(); it != end; ++it) { const TimerId id((*it).timerId, (*it).lastReceiverAddress); const auto cit = changes.constFind(id); if (cit != changes.constEnd()) { const int i = m_sourceModel->rowCount() + std::distance(m_freeTimersInfo.begin(), it); updatedIds << id; (*it) = cit.value(); if (dataChangedRanges.isEmpty() || dataChangedRanges.last().second != i - 1) { dataChangedRanges << qMakePair(i, i); } else { dataChangedRanges.last().second = i; } } } // Fill new free timers to insert QVector freeTimersToInsert; freeTimersToInsert.reserve(changes.count() - updatedIds.count()); for (auto it = changes.constBegin(), end = changes.constEnd(); it != end; ++it) { if (updatedIds.contains(it.key())) continue; // Changes to an object not (yet/longer) in the model, ignore it. if (it.key().type() != TimerId::QObjectType) continue; freeTimersToInsert << it.value(); } // Inform model about data changes for (const auto &range : qAsConst(dataChangedRanges)) { emit dataChanged(index(range.first, 0), index(range.second, columnCount() - 1)); } // Inform model about new rows if (!freeTimersToInsert.isEmpty()) { const int first = m_sourceModel->rowCount() + m_freeTimersInfo.count(); const int last = m_sourceModel->rowCount() + m_freeTimersInfo.count() + freeTimersToInsert.count() - 1; beginInsertRows(QModelIndex(), first, last); m_freeTimersInfo << freeTimersToInsert; endInsertRows(); } } void TimerModel::slotBeginRemoveRows(const QModelIndex &parent, int start, int end) { Q_UNUSED(parent); QMutexLocker locker(&m_mutex); beginRemoveRows(QModelIndex(), start, end); // TODO: Use a delayed timer for that so the hash is iterated once only for a // group of successive removal ? for (auto it = m_timersInfo.begin(); it != m_timersInfo.end();) { if (it.value().lastReceiverObject) { ++it; } else { m_gatheredTimersData.remove(it.key()); it = m_timersInfo.erase(it); } } } void TimerModel::slotEndRemoveRows() { endRemoveRows(); triggerPushChanges(); } void TimerModel::slotBeginInsertRows(const QModelIndex &parent, int start, int end) { Q_UNUSED(parent); beginInsertRows(QModelIndex(), start, end); } void TimerModel::slotEndInsertRows() { endInsertRows(); } void TimerModel::slotBeginReset() { QMutexLocker locker(&m_mutex); beginResetModel(); m_gatheredTimersData.clear(); m_timersInfo.clear(); m_freeTimersInfo.clear(); } void TimerModel::slotEndReset() { endResetModel(); } gammaray-2.11.3/plugins/timertop/timermodel.h000066400000000000000000000077741413310443000212420ustar00rootroot00000000000000/* timermodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Thomas McGuire Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_TIMERTOP_TIMERMODEL_H #define GAMMARAY_TIMERTOP_TIMERMODEL_H #include "timerinfo.h" #include #include #include #include #include #include QT_BEGIN_NAMESPACE class QTimer; QT_END_NAMESPACE namespace GammaRay { struct TimerIdData; class TimerModel : public QAbstractTableModel { Q_OBJECT typedef QMap TimerIdInfoContainer; typedef QMap TimerIdDataContainer; public: ~TimerModel() override; /// @return True in case instance() would return a valid pointer, else false static bool isInitialized(); static TimerModel *instance(); // For the spy callbacks void preSignalActivate(QObject *caller, int methodIndex); void postSignalActivate(QObject *caller, int methodIndex); enum Columns { ObjectNameColumn, StateColumn, TotalWakeupsColumn, WakeupsPerSecColumn, TimePerWakeupColumn, MaxTimePerWakeupColumn, TimerIdColumn, ColumnCount }; enum Roles { TimerIntervalRole = ObjectModel::UserRole, TimerTypeRole }; void setSourceModel(QAbstractItemModel *sourceModel); QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; QMap itemData(const QModelIndex &index) const override; public slots: void clearHistory(); private slots: void triggerPushChanges(); void pushChanges(); void applyChanges(const GammaRay::TimerModel::TimerIdInfoContainer &changes); void slotBeginRemoveRows(const QModelIndex &parent, int start, int end); void slotEndRemoveRows(); void slotBeginInsertRows(const QModelIndex &parent, int start, int end); void slotEndInsertRows(); void slotBeginReset(); void slotEndReset(); private: explicit TimerModel(QObject *parent = nullptr); const TimerIdInfo *findTimerInfo(const QModelIndex &index) const; bool canHandleCaller(QObject *caller, int methodIndex) const; void checkDispatcherStatus(QObject *object); static bool eventNotifyCallback(void *data[]); // model data QAbstractItemModel *m_sourceModel; mutable TimerIdInfoContainer m_timersInfo; QVector m_freeTimersInfo; QTimer *m_pushTimer; const QMetaMethod m_triggerPushChangesMethod; // the method index of the timeout() signal of a QTimer const int m_timeoutIndex; mutable int m_qmlTimerTriggeredIndex; mutable int m_qmlTimerRunningChangedIndex; TimerIdDataContainer m_gatheredTimersData; QMutex m_mutex; // protects m_gatheredTimersData }; } #endif gammaray-2.11.3/plugins/timertop/timertop.cpp000066400000000000000000000113241413310443000212610ustar00rootroot00000000000000/* timertop.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Thomas McGuire Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "timertop.h" #include "timermodel.h" #include #include #include #include #include #include #include #include using namespace GammaRay; // // TODO list for timer plugin // // BUG: QTimeLine class name not shown // Related: Protect against timer deletion // BUG: Only top-level timers are shown (bug in probe) // ! Wakeup time for QTimers // ! Add button to view object info // ! Test timer added/removed at runtime // Buttons to kill or slow down timer and start timer // Add big fat "total wakeups / sec" label to the status bar // Retrieve receiver name from connection model // Add to view as column: receivers: slotXYZ and 3 others (shown in tooltip) // Move signal hook to probe interface // Then maybe add general signal profiler plugin, or even visualization // Flash delegate when timer triggered // Color cell in view redish, depending on how active the timer is class TimerFilterModel : public ObjectTypeFilterProxyModel { public: explicit TimerFilterModel(QObject *parent) : ObjectTypeFilterProxyModel(parent) {} bool filterAcceptsObject(QObject *object) const override { if (object && object->inherits("QQmlTimer")) return true; return ObjectTypeFilterProxyModel::filterAcceptsObject(object); } }; static bool processCallback() { return TimerModel::isInitialized(); } static void signal_begin_callback(QObject *caller, int method_index, void **argv) { Q_UNUSED(argv); if (!processCallback()) return; TimerModel::instance()->preSignalActivate(caller, method_index); } static void signal_end_callback(QObject *caller, int method_index) { // NOTE: here and below the caller may be invalid, e.g. if it was deleted from a slot if (!processCallback()) return; TimerModel::instance()->postSignalActivate(caller, method_index); } TimerTop::TimerTop(Probe *probe, QObject *parent) : TimerTopInterface(parent) { Q_ASSERT(probe); QSortFilterProxyModel * const filterModel = new TimerFilterModel(this); filterModel->setDynamicSortFilter(true); filterModel->setSourceModel(probe->objectListModel()); TimerModel::instance()->setParent(this); // otherwise it's not filtered out TimerModel::instance()->setSourceModel(filterModel); SignalSpyCallbackSet callbacks; callbacks.signalBeginCallback = signal_begin_callback; callbacks.signalEndCallback = signal_end_callback; probe->registerSignalSpyCallbackSet(callbacks); probe->registerModel(QStringLiteral("com.kdab.GammaRay.TimerModel"), TimerModel::instance()); m_selectionModel = ObjectBroker::selectionModel(TimerModel::instance()); connect(probe, &Probe::objectSelected, this, &TimerTop::objectSelected); } void TimerTop::clearHistory() { TimerModel::instance()->clearHistory(); } void TimerTop::objectSelected(QObject* obj) { auto timer = qobject_cast(obj); if (!timer) return; const auto model = m_selectionModel->model(); const auto indexList = model->match(model->index(0, 0), ObjectModel::ObjectIdRole, QVariant::fromValue(ObjectId(timer)), 1, Qt::MatchExactly | Qt::MatchRecursive | Qt::MatchWrap); if (indexList.isEmpty()) return; const auto index = indexList.first(); m_selectionModel->select(index, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); } TimerTopFactory::TimerTopFactory(QObject *parent) : QObject(parent) { } gammaray-2.11.3/plugins/timertop/timertop.h000066400000000000000000000040451413310443000207300ustar00rootroot00000000000000/* timertop.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Thomas McGuire Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_TIMERTOP_TIMERTOP_H #define GAMMARAY_TIMERTOP_TIMERTOP_H #include "timertopinterface.h" #include #include QT_BEGIN_NAMESPACE class QItemSelectionModel; QT_END_NAMESPACE namespace GammaRay { namespace Ui { class TimerTop; } class TimerTop : public TimerTopInterface { Q_OBJECT Q_INTERFACES(GammaRay::TimerTopInterface) public: explicit TimerTop(Probe *probe, QObject *parent = nullptr); public slots: void clearHistory() override; private slots: void objectSelected(QObject *obj); private: QItemSelectionModel *m_selectionModel; }; class TimerTopFactory : public QObject, public StandardToolFactory { Q_OBJECT Q_INTERFACES(GammaRay::ToolFactory) Q_PLUGIN_METADATA(IID "com.kdab.GammaRay.ToolFactory" FILE "gammaray_timertop.json") public: explicit TimerTopFactory(QObject *parent = nullptr); }; } #endif // GAMMARAY_SCENEINSPECTOR_H gammaray-2.11.3/plugins/timertop/timertopclient.cpp000066400000000000000000000026771413310443000224730ustar00rootroot00000000000000/* timertopclient.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Filipe Azevedo Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "timertopclient.h" #include using namespace GammaRay; TimerTopClient::TimerTopClient(QObject *parent) : TimerTopInterface(parent) { } TimerTopClient::~TimerTopClient() = default; void TimerTopClient::clearHistory() { Endpoint::instance()->invokeObject(objectName(), "clearHistory"); } gammaray-2.11.3/plugins/timertop/timertopclient.h000066400000000000000000000030641413310443000221270ustar00rootroot00000000000000/* timertopclient.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Filipe Azevedo Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_TIMERTOP_TIMERTOPCLIENT_H #define GAMMARAY_TIMERTOP_TIMERTOPCLIENT_H #include "timertopinterface.h" namespace GammaRay { class TimerTopClient : public TimerTopInterface { Q_OBJECT Q_INTERFACES(GammaRay::TimerTopInterface) public: explicit TimerTopClient(QObject *parent = nullptr); ~TimerTopClient() override; public slots: void clearHistory() override; }; } #endif // GAMMARAY_TIMERTOP_TIMERTOPCLIENT_H gammaray-2.11.3/plugins/timertop/timertopinterface.cpp000066400000000000000000000026251413310443000231460ustar00rootroot00000000000000/* timertopinterface.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Filipe Azevedo Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "timertopinterface.h" #include namespace GammaRay { TimerTopInterface::TimerTopInterface(QObject *parent) : QObject(parent) { ObjectBroker::registerObject(this); } TimerTopInterface::~TimerTopInterface() = default; } gammaray-2.11.3/plugins/timertop/timertopinterface.h000066400000000000000000000032351413310443000226110ustar00rootroot00000000000000/* timertopinterface.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Filipe Azevedo Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_TIMERTOP_TIMERTOPINTERFACE_H #define GAMMARAY_TIMERTOP_TIMERTOPINTERFACE_H #include namespace GammaRay { class TimerTopInterface : public QObject { Q_OBJECT public: explicit TimerTopInterface(QObject *parent = nullptr); ~TimerTopInterface() override; public slots: virtual void clearHistory() = 0; }; } QT_BEGIN_NAMESPACE Q_DECLARE_INTERFACE(GammaRay::TimerTopInterface, "com.kdab.GammaRay.TimerTopInterface/1.0") QT_END_NAMESPACE #endif // GAMMARAY_TIMERTOP_TIMERTOPINTERFACE_H gammaray-2.11.3/plugins/timertop/timertopwidget.cpp000066400000000000000000000074561413310443000225000ustar00rootroot00000000000000/* timertopwidget.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Thomas McGuire Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "timertopwidget.h" #include "ui_timertopwidget.h" #include "timermodel.h" #include "timertopclient.h" #include "clienttimermodel.h" #include #include #include #include #include #include using namespace GammaRay; static QObject *createTimerTopClient(const QString & /*name*/, QObject *parent) { return new TimerTopClient(parent); } TimerTopWidget::TimerTopWidget(QWidget *parent) : QWidget(parent) , ui(new Ui::TimerTopWidget) , m_stateManager(this) { ui->setupUi(this); ObjectBroker::registerClientObjectFactoryCallback( createTimerTopClient); m_interface = ObjectBroker::object(); ui->timerView->header()->setObjectName("timerViewHeader"); ui->timerView->setDeferredResizeMode(0, QHeaderView::Stretch); ui->timerView->setDeferredResizeMode(1, QHeaderView::ResizeToContents); ui->timerView->setDeferredResizeMode(2, QHeaderView::ResizeToContents); ui->timerView->setDeferredResizeMode(3, QHeaderView::ResizeToContents); ui->timerView->setDeferredResizeMode(4, QHeaderView::ResizeToContents); ui->timerView->setDeferredResizeMode(5, QHeaderView::ResizeToContents); connect(ui->timerView, &QWidget::customContextMenuRequested, this, &TimerTopWidget::contextMenu); connect(ui->clearTimers, &QAbstractButton::clicked, m_interface, &TimerTopInterface::clearHistory); auto * const sortModel = new ClientTimerModel(this); sortModel->setSourceModel(ObjectBroker::model(QStringLiteral("com.kdab.GammaRay.TimerModel"))); sortModel->setDynamicSortFilter(true); ui->timerView->setModel(sortModel); ui->timerView->setSelectionModel(ObjectBroker::selectionModel(sortModel)); new SearchLineController(ui->timerViewFilter, sortModel); ui->timerView->sortByColumn(TimerModel::WakeupsPerSecColumn, Qt::DescendingOrder); } TimerTopWidget::~TimerTopWidget() = default; void TimerTopWidget::contextMenu(QPoint pos) { auto index = ui->timerView->indexAt(pos); if (!index.isValid()) return; index = index.sibling(index.row(), 0); const auto objectId = index.data(ObjectModel::ObjectIdRole).value(); if (objectId.isNull()) return; QMenu menu; ContextMenuExtension ext(objectId); ext.setLocation(ContextMenuExtension::Creation, index.data(ObjectModel::CreationLocationRole).value()); ext.setLocation(ContextMenuExtension::Declaration, index.data(ObjectModel::DeclarationLocationRole).value()); ext.populateMenu(&menu); menu.exec(ui->timerView->viewport()->mapToGlobal(pos)); } gammaray-2.11.3/plugins/timertop/timertopwidget.h000066400000000000000000000040031413310443000221260ustar00rootroot00000000000000/* timertopwidget.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Thomas McGuire Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_TIMERTOP_TIMERTOPWIDGET_H #define GAMMARAY_TIMERTOP_TIMERTOPWIDGET_H #include #include #include QT_BEGIN_NAMESPACE class QTimer; QT_END_NAMESPACE namespace GammaRay { class TimerTopInterface; namespace Ui { class TimerTopWidget; } class TimerTopWidget : public QWidget { Q_OBJECT public: explicit TimerTopWidget(QWidget *parent = nullptr); ~TimerTopWidget() override; private slots: void contextMenu(QPoint pos); private: QScopedPointer ui; UIStateManager m_stateManager; TimerTopInterface *m_interface; }; class TimerTopUiFactory : public QObject, public StandardToolUiFactory { Q_OBJECT Q_INTERFACES(GammaRay::ToolUiFactory) Q_PLUGIN_METADATA(IID "com.kdab.GammaRay.ToolUiFactory" FILE "gammaray_timertop.json") }; } #endif // GAMMARAY_TIMERTOPWIDGET_H gammaray-2.11.3/plugins/timertop/timertopwidget.ui000066400000000000000000000040521413310443000223200ustar00rootroot00000000000000 GammaRay::TimerTopWidget 0 0 400 300 0 0 0 0 ... :/gammaray/icons/ui/classes/QCheckBox/default.png:/gammaray/icons/ui/classes/QCheckBox/default.png Qt::CustomContextMenu true false true false GammaRay::DeferredTreeView QTreeView
    ui/deferredtreeview.h
    gammaray-2.11.3/plugins/translatorinspector/000077500000000000000000000000001413310443000211665ustar00rootroot00000000000000gammaray-2.11.3/plugins/translatorinspector/CMakeLists.txt000066400000000000000000000014101413310443000237220ustar00rootroot00000000000000# probe part if (NOT GAMMARAY_CLIENT_ONLY_BUILD) gammaray_add_plugin(gammaray_translatorinspector JSON gammaray_translatorinspector.json SOURCES translatorinspector.cpp translatorinspectorinterface.cpp translatorwrapper.cpp translatorsmodel.cpp ) target_include_directories(gammaray_translatorinspector SYSTEM PRIVATE ${Qt5Core_PRIVATE_INCLUDE_DIRS}) target_link_libraries(gammaray_translatorinspector gammaray_core Qt5::Core) endif() # ui part if(GAMMARAY_BUILD_UI) gammaray_add_plugin(gammaray_translatorinspector_ui JSON gammaray_translatorinspector.json SOURCES translatorinspectorwidget.cpp translatorinspectorinterface.cpp translatorwrapperproxy.cpp ) target_link_libraries(gammaray_translatorinspector_ui gammaray_ui) endif() gammaray-2.11.3/plugins/translatorinspector/gammaray_translatorinspector.json000066400000000000000000000003161413310443000300570ustar00rootroot00000000000000{ "id": "gammaray_translationinspector", "name": "Translations", "name[de]": "Übersetzungen", "selectableTypes": [ "QTranslator" ], "types": [ "QTranslator" ] } gammaray-2.11.3/plugins/translatorinspector/translatorinspector.cpp000066400000000000000000000145251413310443000260210ustar00rootroot00000000000000/* translatorinspector.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Jan Dalheimer Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "translatorinspector.h" #include "translatorwrapper.h" #include "translatorsmodel.h" #include #include #include #include #include #include #include #include #include #include using namespace GammaRay; TranslatorInspector::TranslatorInspector(Probe *probe, QObject *parent) : TranslatorInspectorInterface(QStringLiteral("com.kdab.GammaRay.TranslatorInspector"), parent) , m_probe(probe) { registerMetaTypes(); m_translatorsModel = new TranslatorsModel(this); probe->registerModel(QStringLiteral("com.kdab.GammaRay.TranslatorsModel"), m_translatorsModel); m_translationsModel = new ServerProxyModel(this); probe->registerModel(QStringLiteral("com.kdab.GammaRay.TranslationsModel"), m_translationsModel); m_selectionModel = ObjectBroker::selectionModel(m_translatorsModel); connect(m_selectionModel, &QItemSelectionModel::selectionChanged, this, &TranslatorInspector::selectionChanged); m_translationsSelectionModel = ObjectBroker::selectionModel(m_translationsModel); m_fallbackWrapper = new TranslatorWrapper(new FallbackTranslator(this), this); m_translatorsModel->registerTranslator(m_fallbackWrapper); QCoreApplicationPrivate *obj = static_cast( QCoreApplicationPrivate::get(qApp)); obj->translators.append(m_fallbackWrapper); qApp->installEventFilter(this); sendLanguageChangeEvent(); connect(probe, &Probe::objectSelected, this, &TranslatorInspector::objectSelected); } void TranslatorInspector::sendLanguageChangeEvent() { QEvent event(QEvent::LanguageChange); qApp->sendEvent(qApp, &event); } void TranslatorInspector::resetTranslations() { const QItemSelection translatorsSelection = m_selectionModel->selection(); if (translatorsSelection.isEmpty()) return; TranslatorWrapper *translator = m_translatorsModel->translator(translatorsSelection.first().topLeft()); Q_ASSERT(translator); const QItemSelection translationsProxySelection = m_translationsSelectionModel->selection(); const QItemSelection translationsSourceSelection = m_translationsModel->mapSelectionToSource(translationsProxySelection); if (translationsSourceSelection.isEmpty()) return; translator->model()->resetTranslations(translationsSourceSelection); } bool TranslatorInspector::eventFilter(QObject *object, QEvent *event) { if (event->type() == QEvent::LanguageChange) { QCoreApplicationPrivate *obj = static_cast( QCoreApplicationPrivate::get(qApp)); for (int i = 0; i < obj->translators.size(); ++i) { if (obj->translators.at(i)->metaObject() == &TranslatorWrapper::staticMetaObject) { continue; // it's already setup correctly } else { /* wrap the translator set with installTranslator in a TranslatorWrapper * and make sure we use the TranslatorWrapper instead of the original * translator */ auto wrapper = new TranslatorWrapper(obj->translators.at(i), this); obj->translators[i] = wrapper; m_translatorsModel->registerTranslator(wrapper); connect(wrapper, &TranslatorWrapper::destroyed, m_translationsModel, [wrapper, this](QObject*) { m_translatorsModel->unregisterTranslator(wrapper); }); } } for (auto it = obj->translators.constBegin(); it != obj->translators.constEnd(); ++it) { TranslatorWrapper *wrapper = qobject_cast(*it); Q_ASSERT(wrapper); wrapper->model()->resetAllUnchanged(); } } return QObject::eventFilter(object, event); } void TranslatorInspector::selectionChanged(const QItemSelection &selection) { m_translationsModel->setSourceModel(nullptr); if (!selection.isEmpty()) { TranslatorWrapper *translator = m_translatorsModel->translator(selection.first().topLeft()); if (translator) m_translationsModel->setSourceModel(translator->model()); } } void TranslatorInspector::objectSelected(QObject* obj) { auto t = qobject_cast(obj); if (!t) return; const auto indexList = m_translatorsModel->match(m_translationsModel->index(0, 0), TranslatorsModel::ObjectIdRole, QVariant::fromValue(ObjectId(t)), 1, Qt::MatchExactly | Qt::MatchRecursive | Qt::MatchWrap); if (indexList.isEmpty()) return; const auto index = indexList.first(); m_selectionModel->select(index, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows | QItemSelectionModel::Current); } void TranslatorInspector::registerMetaTypes() { MetaObject *mo = nullptr; MO_ADD_METAOBJECT1(QTranslator, QObject); MO_ADD_PROPERTY_RO(QTranslator, isEmpty); } gammaray-2.11.3/plugins/translatorinspector/translatorinspector.h000066400000000000000000000052571413310443000254700ustar00rootroot00000000000000/* translatorinspector.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Jan Dalheimer Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef TRANSLATORINSPECTOR_H #define TRANSLATORINSPECTOR_H #include "translatorinspectorinterface.h" #include #include #include QT_BEGIN_NAMESPACE class QItemSelectionModel; class QItemSelection; class QAbstractProxyModel; QT_END_NAMESPACE namespace GammaRay { class TranslatorsModel; class TranslatorWrapper; class FallbackTranslator; class TranslatorInspector : public TranslatorInspectorInterface { Q_OBJECT Q_INTERFACES(GammaRay::TranslatorInspectorInterface) public: explicit TranslatorInspector(Probe *probe, QObject *parent = nullptr); public slots: void sendLanguageChangeEvent() override; void resetTranslations() override; private slots: void selectionChanged(const QItemSelection &selection); void objectSelected(QObject *obj); protected: bool eventFilter(QObject *object, QEvent *event) override; private: void registerMetaTypes(); QItemSelectionModel *m_selectionModel; QItemSelectionModel *m_translationsSelectionModel; TranslatorsModel *m_translatorsModel; QAbstractProxyModel *m_translationsModel; Probe *m_probe; TranslatorWrapper *m_fallbackWrapper; }; class TranslatorInspectorFactory : public QObject, public StandardToolFactory { Q_OBJECT Q_INTERFACES(GammaRay::ToolFactory) Q_PLUGIN_METADATA(IID "com.kdab.GammaRay.ToolFactory" FILE "gammaray_translatorinspector.json") public: explicit TranslatorInspectorFactory(QObject *parent = nullptr) : QObject(parent) {} }; } #endif gammaray-2.11.3/plugins/translatorinspector/translatorinspectorinterface.cpp000066400000000000000000000027621413310443000277020ustar00rootroot00000000000000/* translatorinspectorinterface.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Jan Dalheimer Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "translatorinspectorinterface.h" #include using namespace GammaRay; TranslatorInspectorInterface::TranslatorInspectorInterface(const QString &name, QObject *parent) : QObject(parent) , m_name(name) { ObjectBroker::registerObject(name, this); } TranslatorInspectorInterface::~TranslatorInspectorInterface() = default; gammaray-2.11.3/plugins/translatorinspector/translatorinspectorinterface.h000066400000000000000000000034641413310443000273470ustar00rootroot00000000000000/* translatorinspectorinterface.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Jan Dalheimer Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef TRANSLATORINSPECTORINTERFACE_H #define TRANSLATORINSPECTORINTERFACE_H #include namespace GammaRay { class TranslatorInspectorInterface : public QObject { Q_OBJECT public: explicit TranslatorInspectorInterface(const QString &name, QObject *parent); ~TranslatorInspectorInterface() override; const QString &name() const { return m_name; } public slots: virtual void sendLanguageChangeEvent() = 0; virtual void resetTranslations() = 0; private: QString m_name; }; } QT_BEGIN_NAMESPACE Q_DECLARE_INTERFACE(GammaRay::TranslatorInspectorInterface, "com.kdab.GammaRay.TranslatorInspectorInterface") QT_END_NAMESPACE #endif gammaray-2.11.3/plugins/translatorinspector/translatorinspectorwidget.cpp000066400000000000000000000130251413310443000272170ustar00rootroot00000000000000/* translatorinspectorwidget.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Jan Dalheimer Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "translatorinspectorwidget.h" #include "ui_translatorinspectorwidget.h" #include "translatorsmodel.h" // use only for its role enums #include "translatorwrapperproxy.h" #include #include #include #include #include #include #include using namespace GammaRay; TranslatorInspectorClient::TranslatorInspectorClient(const QString &name, QObject *parent) : TranslatorInspectorInterface(name, parent) { } void TranslatorInspectorClient::sendLanguageChangeEvent() { Endpoint::instance()->invokeObject(name(), "sendLanguageChangeEvent"); } void TranslatorInspectorClient::resetTranslations() { Endpoint::instance()->invokeObject(name(), "resetTranslations"); } TranslatorInspectorWidget::TranslatorInspectorWidget(QWidget *parent) : QWidget(parent) , ui(new Ui::TranslatorInspectorWidget) , m_stateManager(this) { ui->setupUi(this); m_inspector = ObjectBroker::object(QStringLiteral( "com.kdab.GammaRay.TranslatorInspector")); ui->translatorList->header()->setObjectName("translatorListHeader"); ui->translatorList->setDeferredResizeMode(0, QHeaderView::ResizeToContents); ui->translatorList->setDeferredResizeMode(1, QHeaderView::ResizeToContents); ui->translatorList->setDeferredResizeMode(2, QHeaderView::ResizeToContents); ui->translatorList->setModel(ObjectBroker::model(QStringLiteral("com.kdab.GammaRay.TranslatorsModel"))); ui->translatorList->setSelectionModel(ObjectBroker::selectionModel(ui->translatorList->model())); connect(ui->translatorList, &QTreeView::customContextMenuRequested, this, &TranslatorInspectorWidget::translatorContextMenu); // searching for translations { auto proxy = new TranslatorWrapperProxy(this); proxy->setSourceModel(ObjectBroker::model(QStringLiteral("com.kdab.GammaRay.TranslationsModel"))); ui->translationsView->header()->setObjectName("translationsViewHeader"); ui->translationsView->setDeferredResizeMode(0, QHeaderView::ResizeToContents); ui->translationsView->setModel(proxy); connect(ui->translationsView, &QTreeView::customContextMenuRequested, this, &TranslatorInspectorWidget::translationsContextMenu); const auto selectionModel = ObjectBroker::selectionModel(ui->translationsView->model()); ui->translationsView->setSelectionModel(selectionModel); connect(selectionModel, &QItemSelectionModel::selectionChanged, this, &TranslatorInspectorWidget::updateActions); new SearchLineController(ui->translationsSearchLine, ui->translationsView->model()); } m_stateManager.setDefaultSizes(ui->mainSplitter, UISizeVector() << "50%" << "50%"); connect(ui->actionSendLanguageChange, &QAction::triggered, m_inspector, &TranslatorInspectorInterface::sendLanguageChangeEvent); connect(ui->actionReset, &QAction::triggered, m_inspector, &TranslatorInspectorInterface::resetTranslations); addAction(ui->actionReset); addAction(ui->actionSendLanguageChange); updateActions(); } TranslatorInspectorWidget::~TranslatorInspectorWidget() = default; void TranslatorInspectorWidget::translatorContextMenu(QPoint pos) { auto index = ui->translatorList->indexAt(pos); if (!index.isValid()) return; index = index.sibling(index.row(), 0); const auto objectId = index.data(TranslatorsModel::ObjectIdRole).value(); if (objectId.isNull()) return; QMenu menu; ContextMenuExtension ext(objectId); ext.populateMenu(&menu); menu.exec(ui->translatorList->viewport()->mapToGlobal(pos)); } void TranslatorInspectorWidget::translationsContextMenu(QPoint pos) { QMenu menu; menu.addAction(ui->actionReset); menu.exec(ui->translationsView->viewport()->mapToGlobal(pos)); } void TranslatorInspectorWidget::updateActions() { ui->actionReset->setEnabled(!ui->translationsView->selectionModel()->selectedRows().isEmpty()); } static QObject *translatorInspectorClientFactory(const QString &name, QObject *parent) { return new TranslatorInspectorClient(name, parent); } void TranslatorInspectorWidgetFactory::initUi() { ObjectBroker::registerClientObjectFactoryCallback( translatorInspectorClientFactory); } gammaray-2.11.3/plugins/translatorinspector/translatorinspectorwidget.h000066400000000000000000000047401413310443000266700ustar00rootroot00000000000000/* translatorinspectorwidget.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Jan Dalheimer Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef TRANSLATORINSPECTORWIDGET_H #define TRANSLATORINSPECTORWIDGET_H #include #include #include #include "translatorinspectorinterface.h" namespace GammaRay { namespace Ui { class TranslatorInspectorWidget; } class TranslatorInspectorClient : public TranslatorInspectorInterface { Q_OBJECT Q_INTERFACES(GammaRay::TranslatorInspectorInterface) public: explicit TranslatorInspectorClient(const QString &name, QObject *parent = nullptr); public slots: void sendLanguageChangeEvent() override; void resetTranslations() override; }; class TranslatorInspectorWidget : public QWidget { Q_OBJECT public: explicit TranslatorInspectorWidget(QWidget *parent); ~TranslatorInspectorWidget() override; private: void translatorContextMenu(QPoint pos); void translationsContextMenu(QPoint pos); void updateActions(); QScopedPointer ui; UIStateManager m_stateManager; TranslatorInspectorInterface *m_inspector; }; class TranslatorInspectorWidgetFactory : public QObject, public StandardToolUiFactory { Q_OBJECT Q_INTERFACES(GammaRay::ToolUiFactory) Q_PLUGIN_METADATA(IID "com.kdab.GammaRay.ToolUiFactory" FILE "gammaray_translatorinspector.json") public: void initUi() override; }; } #endif gammaray-2.11.3/plugins/translatorinspector/translatorinspectorwidget.ui000066400000000000000000000050031413310443000270470ustar00rootroot00000000000000 GammaRay::TranslatorInspectorWidget 0 0 400 300 Qt::Horizontal Qt::CustomContextMenu false true Qt::CustomContextMenu QAbstractItemView::ContiguousSelection false Reset Selected Translations Reset manually changed translations back to their original content. Send LanguageChange Event Sends a QEvent::LanguageChange event to the application to reload changed translations. GammaRay::DeferredTreeView QTreeView
    ui/deferredtreeview.h
    gammaray-2.11.3/plugins/translatorinspector/translatorsmodel.cpp000066400000000000000000000104741413310443000252750ustar00rootroot00000000000000/* translatorsmodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Jan Dalheimer Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "translatorsmodel.h" #include #include #include "translatorwrapper.h" using namespace GammaRay; TranslatorsModel::TranslatorsModel(QObject *parent) : QAbstractTableModel(parent) { } int TranslatorsModel::columnCount(const QModelIndex &) const { return 3; } int TranslatorsModel::rowCount(const QModelIndex &parent) const { if (parent.isValid()) return 0; return m_translators.size(); } QVariant TranslatorsModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); if (role == ObjectIdRole) return QVariant::fromValue(ObjectId(m_translators.at(index.row())->translator())); TranslatorWrapper *trans = m_translators.at(index.row()); Q_ASSERT(trans); if (role == Qt::DisplayRole) { if (index.column() == 0) return Util::shortDisplayString(trans->translator()); else if (index.column() == 1) return QString(trans->translator()->metaObject()->className()); else if (index.column() == 2) return trans->model()->rowCount(QModelIndex()); } else if (role == Qt::ToolTipRole) { return Util::tooltipForObject(trans->translator()); } return QVariant(); } QVariant TranslatorsModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { if (section == 0) return tr("Object"); else if (section == 1) return tr("Type"); else if (section == 2) return tr("Translations"); } return QVariant(); } QMap TranslatorsModel::itemData(const QModelIndex &index) const { auto d = QAbstractTableModel::itemData(index); d.insert(ObjectIdRole, data(index, ObjectIdRole)); return d; } TranslatorWrapper *TranslatorsModel::translator(const QModelIndex &index) const { return m_translators.at(index.row()); } void TranslatorsModel::sourceDataChanged() { const int row = m_translators.indexOf(qobject_cast(sender())->translator()); if (row == -1) return; const QModelIndex index = this->index(row, 2, QModelIndex()); if (!index.isValid()) return; // needed to make sure these things also update emit dataChanged(index, index, QVector() << Qt::DisplayRole << Qt::EditRole); } void TranslatorsModel::registerTranslator(TranslatorWrapper *translator) { beginInsertRows(QModelIndex(), 0, 0); m_translators.prepend(translator); endInsertRows(); connect(translator->model(), &TranslationsModel::rowCountChanged, this, &TranslatorsModel::sourceDataChanged); } void TranslatorsModel::unregisterTranslator(TranslatorWrapper *translator) { const int index = m_translators.indexOf(translator); if (index == -1) { qWarning("TranslatorsModel::unregisterTranslator: translator %s is not registered", qPrintable(Util::addressToString(translator))); return; } disconnect(translator->model(), nullptr, this, nullptr); beginRemoveRows(QModelIndex(), index, index); m_translators.removeAt(index); endRemoveRows(); } gammaray-2.11.3/plugins/translatorinspector/translatorsmodel.h000066400000000000000000000042261413310443000247400ustar00rootroot00000000000000/* translatorsmodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Jan Dalheimer Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef TRANSLATORSMODEL_H #define TRANSLATORSMODEL_H #include namespace GammaRay { class TranslatorWrapper; class TranslatorsModel : public QAbstractTableModel { Q_OBJECT public: explicit TranslatorsModel(QObject *parent = nullptr); enum ExtraRoles { ObjectIdRole = Qt::UserRole + 1 }; int columnCount(const QModelIndex &parent = QModelIndex()) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role) const override; QVariant headerData(int section, Qt::Orientation orientation, int role) const override; QMap itemData(const QModelIndex & index) const override; TranslatorWrapper *translator(const QModelIndex &index) const; public slots: void registerTranslator(TranslatorWrapper *translator); void unregisterTranslator(TranslatorWrapper *translator); private slots: void sourceDataChanged(); private: QList m_translators; }; } #endif gammaray-2.11.3/plugins/translatorinspector/translatorwrapper.cpp000066400000000000000000000177711413310443000255010ustar00rootroot00000000000000/* translatorwrapper.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Jan Dalheimer Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "translatorwrapper.h" #include using namespace GammaRay; TranslationsModel::TranslationsModel(TranslatorWrapper *translator) : QAbstractTableModel(translator) , m_translator(translator) { connect(this, &QAbstractItemModel::rowsInserted, this, &TranslationsModel::rowCountChanged); connect(this, &QAbstractItemModel::rowsRemoved, this, &TranslationsModel::rowCountChanged); } int TranslationsModel::rowCount(const QModelIndex &parent) const { if (parent.isValid()) return 0; return m_nodes.size(); } int TranslationsModel::columnCount(const QModelIndex &) const { return 4; } QVariant TranslationsModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); Row node = m_nodes.at(index.row()); if (role == Qt::DisplayRole || role == Qt::EditRole) { switch (index.column()) { case 0: return node.context; case 1: return node.sourceText; case 2: return node.disambiguation; case 3: return node.translation; } } if (role == IsOverriddenRole && index.column() == 3) { return node.isOverridden; } return QVariant(); } bool TranslationsModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (role == Qt::EditRole && index.column() == 3) { Row &node = m_nodes[index.row()]; if (node.translation == value.toString()) return true; node.translation = value.toString(); node.isOverridden = true; emit dataChanged(index, index, QVector() << Qt::DisplayRole << Qt::EditRole); return true; } return false; } Qt::ItemFlags TranslationsModel::flags(const QModelIndex &index) const { const auto f = QAbstractTableModel::flags(index); if (index.column() == 3) return f | Qt::ItemIsEditable; return f; } QMap TranslationsModel::itemData(const QModelIndex &index) const { auto data = QAbstractTableModel::itemData(index); if (hasIndex(index.row(), index.column(), index.parent())) { if (index.column() == 3) data[IsOverriddenRole] = m_nodes.at(index.row()).isOverridden; } return data; } void TranslationsModel::resetTranslations(const QItemSelection &selection) { if (selection.isEmpty()) return; // The mapping to source make the linear selection ... non linear // Let rebuild linear ranges to avoid overflood... const QModelIndexList indexes = selection.indexes(); QMap rows; for (const QModelIndex &index : indexes) rows[index.row()] = -1; QVector> ranges; // pair of first/last for (auto it = rows.constBegin(), end = rows.constEnd(); it != end; ++it) { const int &row = it.key(); Q_ASSERT(row >= 0); if (ranges.isEmpty() || ranges.last().second != row - 1) { ranges << qMakePair(row, row); } else { ranges.last().second = row; } } for (int i = ranges.count() -1; i >= 0; --i) { const auto &range = ranges[i]; beginRemoveRows(QModelIndex(), range.first, range.second); m_nodes.remove(range.first, range.second - range.first + 1); endRemoveRows(); } } QString TranslationsModel::translation(const char *context, const char *sourceText, const char *disambiguation, const int n, const QString &default_) { QModelIndex existingIndex = findNode(context, sourceText, disambiguation, n, true); Row &row = m_nodes[existingIndex.row()]; if (!row.isOverridden) setTranslation(existingIndex, default_); return row.translation; } void TranslationsModel::resetAllUnchanged() { for (int i = 0; i < m_nodes.size(); ++i) { if (!m_nodes[i].isOverridden) resetTranslations(QItemSelection(index(i, 0), index(i, 0))); } } void TranslationsModel::setTranslation(const QModelIndex &index, const QString &translation) { if (!index.isValid()) return; auto &row = m_nodes[index.row()]; if (row.isOverridden || row.translation == translation) return; row.translation = translation; emit dataChanged(index, index); } QModelIndex TranslationsModel::findNode(const char *context, const char *sourceText, const char *disambiguation, const int n, const bool create) { Q_UNUSED(n); // QUESTION make use of n? for (int i = 0; i < m_nodes.size(); ++i) { const Row &node = m_nodes.at(i); if (node.context == context && node.sourceText == sourceText && node.disambiguation == disambiguation) return index(i, 0); } if (create) { Row node; node.context = context; node.sourceText = sourceText; node.disambiguation = disambiguation; const int newRow = m_nodes.size(); beginInsertRows(QModelIndex(), newRow, newRow); m_nodes.append(node); endInsertRows(); return index(newRow, 0); } return {}; } TranslatorWrapper::TranslatorWrapper(QTranslator *wrapped, QObject *parent) : QTranslator(parent) , m_wrapped(wrapped) , m_model(new TranslationsModel(this)) { Q_ASSERT(wrapped); // not deleteLater(), otherwise we end up with a dangling pointer in here! connect(wrapped, &QObject::destroyed, this, [this]() { delete this; }); } bool TranslatorWrapper::isEmpty() const { return translator()->isEmpty(); } QString TranslatorWrapper::translate(const char *context, const char *sourceText, const char *disambiguation, int n) const { const QString translation = translateInternal(context, sourceText, disambiguation, n); if (context && strncmp(context, "GammaRay::", 10) == 0) return translation; // it's not for this translator if (translation.isNull()) return translation; return m_model->translation(context, sourceText, disambiguation, n, translation); } QString TranslatorWrapper::translateInternal(const char *context, const char *sourceText, const char *disambiguation, int n) const { return translator()->translate(context, sourceText, disambiguation, n); } QTranslator *TranslatorWrapper::translator() const { Q_ASSERT(m_wrapped); return m_wrapped; } FallbackTranslator::FallbackTranslator(QObject *parent) : QTranslator(parent) { setObjectName(QStringLiteral("Fallback Translator")); } QString FallbackTranslator::translate(const char *context, const char *sourceText, const char *disambiguation, int n) const { Q_UNUSED(context); Q_UNUSED(disambiguation); Q_UNUSED(n); return QString::fromUtf8(sourceText); } gammaray-2.11.3/plugins/translatorinspector/translatorwrapper.h000066400000000000000000000077311413310443000251410ustar00rootroot00000000000000/* translatorwrapper.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Jan Dalheimer Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef TRANSLATORWRAPPER_H #define TRANSLATORWRAPPER_H #include #include #include QT_BEGIN_NAMESPACE class QItemSelection; QT_END_NAMESPACE namespace GammaRay { class TranslatorWrapper; class TranslationsModel : public QAbstractTableModel { Q_OBJECT public: enum CustomRoles { IsOverriddenRole = GammaRay::UserRole }; explicit TranslationsModel(TranslatorWrapper *translator); int rowCount(const QModelIndex &parent = QModelIndex()) const override; int columnCount(const QModelIndex &) const override; QVariant data(const QModelIndex &index, int role) const override; bool setData(const QModelIndex &index, const QVariant &value, int role) override; Qt::ItemFlags flags(const QModelIndex &index) const override; QMap itemData(const QModelIndex &index) const override; void resetTranslations(const QItemSelection &selection); QString translation(const char *context, const char *sourceText, const char *disambiguation, const int n, const QString &default_); void resetAllUnchanged(); TranslatorWrapper *translator() const { return m_translator; } signals: void rowCountChanged(); private: friend class TranslatorWrapper; TranslatorWrapper *m_translator; struct Row { Row() = default; QByteArray context; QByteArray sourceText; QByteArray disambiguation; QString translation; bool isOverridden = false; }; QVector m_nodes; QModelIndex findNode(const char *context, const char *sourceText, const char *disambiguation, const int n, const bool create); void setTranslation(const QModelIndex &index, const QString &translation); }; class TranslatorWrapper : public QTranslator { Q_OBJECT public: explicit TranslatorWrapper(QTranslator *wrapped, QObject *parent = nullptr); TranslationsModel *model() const { return m_model; } bool isEmpty() const override; QString translate(const char *context, const char *sourceText, const char *disambiguation, int n) const override; QTranslator *translator() const; private: QTranslator *m_wrapped; TranslationsModel *m_model; QString translateInternal(const char *context, const char *sourceText, const char *disambiguation, int n) const; }; class FallbackTranslator : public QTranslator { Q_OBJECT public: explicit FallbackTranslator(QObject *parent = nullptr); bool isEmpty() const override { return false; } QString translate(const char *context, const char *sourceText, const char *disambiguation, int n) const override; private: #ifndef Q_NO_USING_KEYWORD // hide using QTranslator::load; #endif }; } #endif gammaray-2.11.3/plugins/translatorinspector/translatorwrapperproxy.cpp000066400000000000000000000046571413310443000266020ustar00rootroot00000000000000/* translatorwrapperproxy.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2018-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "translatorwrapperproxy.h" #include "translatorwrapper.h" // use only for its role enums #include using namespace GammaRay; TranslatorWrapperProxy::TranslatorWrapperProxy(QObject* parent) : QIdentityProxyModel(parent) { } TranslatorWrapperProxy::~TranslatorWrapperProxy() = default; QVariant TranslatorWrapperProxy::data(const QModelIndex& proxyIndex, int role) const { if (hasIndex(proxyIndex.row(), proxyIndex.column(), proxyIndex.parent())) { if (role == Qt::FontRole) { const bool overridden = proxyIndex.sibling(proxyIndex.row(), 3) .data(TranslationsModel::IsOverriddenRole).toBool(); QFont font; font.setItalic(overridden); return font; } } return QIdentityProxyModel::data(proxyIndex, role); } QVariant TranslatorWrapperProxy::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { switch (section) { case 0: return tr("Context"); case 1: return tr("Source Text"); case 2: return tr("Disambiguation"); case 3: return tr("Translation"); } } return QAbstractItemModel::headerData(section, orientation, role); } gammaray-2.11.3/plugins/translatorinspector/translatorwrapperproxy.h000066400000000000000000000032551413310443000262400ustar00rootroot00000000000000/* translatorwrapperproxy.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2018-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_TRANSLATORWRAPPERPROXY_H #define GAMMARAY_TRANSLATORWRAPPERPROXY_H #include namespace GammaRay { class TranslatorWrapperProxy : public QIdentityProxyModel { Q_OBJECT public: explicit TranslatorWrapperProxy(QObject *parent = nullptr); ~TranslatorWrapperProxy() override; QVariant data(const QModelIndex &proxyIndex, int role = Qt::DisplayRole) const override; QVariant headerData(int section, Qt::Orientation orientation, int role) const override; }; } #endif // GAMMARAY_TRANSLATORWRAPPERPROXY_H gammaray-2.11.3/plugins/webinspector/000077500000000000000000000000001413310443000175525ustar00rootroot00000000000000gammaray-2.11.3/plugins/webinspector/CMakeLists.txt000066400000000000000000000014661413310443000223210ustar00rootroot00000000000000# probe part if (NOT GAMMARAY_CLIENT_ONLY_BUILD) set(gammaray_webinspector_plugin_srcs webinspector.cpp webviewmodel.cpp ) gammaray_add_plugin(gammaray_webinspector_plugin JSON gammaray_webinspector.json SOURCES ${gammaray_webinspector_plugin_srcs} ) target_link_libraries(gammaray_webinspector_plugin gammaray_core ) endif() # ui part if(GAMMARAY_BUILD_UI AND TARGET Qt5::WebEngineWidgets) set(gammaray_webinspector_ui_plugin_srcs webinspectorwidget.cpp ) gammaray_add_plugin(gammaray_webinspector_ui_plugin JSON gammaray_webinspector.json SOURCES ${gammaray_webinspector_ui_plugin_srcs} ) target_link_libraries(gammaray_webinspector_ui_plugin Qt5::WebEngineWidgets gammaray_ui ) endif() gammaray-2.11.3/plugins/webinspector/gammaray_webinspector.json000066400000000000000000000003131413310443000250240ustar00rootroot00000000000000{ "id": "gammaray_webinspector", "name": "Web Inspector", "name[de]": "Web Inspektor", "types": [ "QWebEnginePage", "QQuickWebView", "QQuickWebEngineView" ] } gammaray-2.11.3/plugins/webinspector/webinspector.cpp000066400000000000000000000065311413310443000227670ustar00rootroot00000000000000/* webinspector.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include "webinspector.h" #include "webviewmodel.h" #include #include #include #include #include #include using namespace GammaRay; WebInspector::WebInspector(Probe *probe, QObject *parent) : QObject(parent) { auto *webViewModel = new WebViewModel(this); webViewModel->setSourceModel(probe->objectListModel()); probe->registerModel(QStringLiteral("com.kdab.GammaRay.WebPages"), webViewModel); connect(probe, &Probe::objectCreated, this, &WebInspector::objectAdded); const QUrl serverUrl = Endpoint::instance()->serverAddress(); QString serverAddress(GAMMARAY_DEFAULT_ANY_ADDRESS); if (serverUrl.scheme() == QLatin1String("tcp")) serverAddress = serverUrl.host(); // see also probecreator.cpp qputenv("QTWEBKIT_INSPECTOR_SERVER", serverAddress.toLocal8Bit() + ':' + QByteArray::number(Endpoint::defaultPort() + 1)); qputenv("QTWEBENGINE_REMOTE_DEBUGGING", QByteArray::number(Endpoint::defaultPort() + 1)); } void WebInspector::objectAdded(QObject *obj) { // both of the following cases seem to be needed, the web view object changes depending on // you have "import QtWebKit.experimental 1.0" or not... QObject *experimental = nullptr; if (obj->inherits("QQuickWebView")) experimental = obj->property("experimental").value(); if (obj->inherits("QQuickWebViewExperimental")) experimental = obj; if (!experimental) return; // FIXME: this conversion fails with "QMetaProperty::read: Unable to handle unregistered datatype 'QWebPreferences*' for property 'QQuickWebViewExperimental::preferences'" // if we don't have "import QtWebKit.experimental 1.0" QObject *prefs = experimental->property("preferences").value(); if (!prefs) return; prefs->setProperty("developerExtrasEnabled", true); } WebInspectorFactory::WebInspectorFactory(QObject *parent) : QObject(parent) { } QString WebInspectorFactory::id() const { return WebInspector::staticMetaObject.className(); } void WebInspectorFactory::init(Probe *probe) { new WebInspector(probe, probe); } gammaray-2.11.3/plugins/webinspector/webinspector.h000066400000000000000000000035001413310443000224250ustar00rootroot00000000000000/* webinspector.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_WEBINSPECTOR_WEBINSPECTOR_H #define GAMMARAY_WEBINSPECTOR_WEBINSPECTOR_H #include namespace GammaRay { class WebInspector : public QObject { Q_OBJECT public: explicit WebInspector(Probe *probe, QObject *parent = nullptr); private slots: void objectAdded(QObject *obj); }; class WebInspectorFactory : public QObject, public ToolFactory { Q_OBJECT Q_INTERFACES(GammaRay::ToolFactory) Q_PLUGIN_METADATA(IID "com.kdab.GammaRay.ToolFactory" FILE "gammaray_webinspector.json") public: explicit WebInspectorFactory(QObject *parent = nullptr); QString id() const override; void init(Probe *probe) override; }; } #endif // GAMMARAY_WEBINSPECTOR_H gammaray-2.11.3/plugins/webinspector/webinspectorwidget.cpp000066400000000000000000000043451413310443000241740ustar00rootroot00000000000000/* webinspectorwidget.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "webinspectorwidget.h" #include "ui_webinspectorwidget.h" #include "webviewmodelroles.h" #include #include #include using namespace GammaRay; WebInspectorWidget::WebInspectorWidget(QWidget *parent) : QWidget(parent) , ui(new Ui::WebInspectorWidget) { ui->setupUi(this); ui->webPageComboBox->setModel(ObjectBroker::model(QStringLiteral("com.kdab.GammaRay.WebPages"))); connect(ui->webPageComboBox, static_cast(&QComboBox::activated), this, &WebInspectorWidget::webPageSelected); webPageSelected(0); } WebInspectorWidget::~WebInspectorWidget() = default; void WebInspectorWidget::webPageSelected(int index) { Q_UNUSED(index); const QUrl serverUrl = Endpoint::instance()->serverAddress(); if (serverUrl.scheme() == QLatin1String("tcp")) { QUrl inspectorUrl; inspectorUrl.setScheme(QStringLiteral("http")); inspectorUrl.setHost(serverUrl.host()); inspectorUrl.setPort(Endpoint::defaultPort() + 1); ui->webView->setUrl(inspectorUrl); } } gammaray-2.11.3/plugins/webinspector/webinspectorwidget.h000066400000000000000000000035521413310443000236400ustar00rootroot00000000000000/* webinspectorwidget.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_WEBINSPECTOR_WEBINSPECTORWIDGET_H #define GAMMARAY_WEBINSPECTOR_WEBINSPECTORWIDGET_H #include #include namespace GammaRay { namespace Ui { class WebInspectorWidget; } class WebInspectorWidget : public QWidget { Q_OBJECT public: explicit WebInspectorWidget(QWidget *parent = nullptr); ~WebInspectorWidget() override; private slots: void webPageSelected(int index); private: QScopedPointer ui; }; class WebInspectorUiFactory : public QObject, public StandardToolUiFactory { Q_OBJECT Q_INTERFACES(GammaRay::ToolUiFactory) Q_PLUGIN_METADATA(IID "com.kdab.GammaRay.ToolUiFactory" FILE "gammaray_webinspector.json") }; } #endif gammaray-2.11.3/plugins/webinspector/webinspectorwidget.ui000066400000000000000000000015671413310443000240320ustar00rootroot00000000000000 GammaRay::WebInspectorWidget 0 0 727 530 about:blank QWebEngineView QWidget
    QtWebEngineWidgets/QWebEngineView
    gammaray-2.11.3/plugins/webinspector/webviewmodel.cpp000066400000000000000000000051111413310443000227450ustar00rootroot00000000000000/* webviewmodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "webviewmodel.h" #include "webviewmodelroles.h" #include "common/objectmodel.h" using namespace GammaRay; WebViewModel::WebViewModel(QObject *parent) : ObjectFilterProxyModelBase(parent) { } WebViewModel::~WebViewModel() = default; QVariant WebViewModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); if ((role != Qt::DisplayRole && role != WebViewModelRoles::WebKitVersionRole) || index.column() != 0) return QSortFilterProxyModel::data(index, role); const QObject *obj = index.data(ObjectModel::ObjectRole).value(); const bool isWk2 = obj->inherits("QQuickWebView"); if (role == Qt::DisplayRole) return QString(Util::displayString(obj) + (isWk2 ? QStringLiteral(" [WebKit2]") : QStringLiteral(" [WebEngine]"))); if (role == WebViewModelRoles::WebKitVersionRole) return isWk2 ? 2 : 3; Q_ASSERT(!"WTF?"); return QVariant(); } QMap< int, QVariant > WebViewModel::itemData(const QModelIndex &index) const { QMap d; d.insert(Qt::DisplayRole, data(index, Qt::DisplayRole)); d.insert(WebViewModelRoles::WebKitVersionRole, data(index, WebViewModelRoles::WebKitVersionRole)); return d; } bool WebViewModel::filterAcceptsObject(QObject *object) const { return object->inherits("QQuickWebView") || object->inherits("QQuickWebEngineView") || object->inherits("QWebEnginePage"); } gammaray-2.11.3/plugins/webinspector/webviewmodel.h000066400000000000000000000032761413310443000224240ustar00rootroot00000000000000/* webviewmodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_WEBINSPECTOR_WEBVIEWMODEL_H #define GAMMARAY_WEBINSPECTOR_WEBVIEWMODEL_H #include namespace GammaRay { class WebViewModel : public ObjectFilterProxyModelBase { Q_OBJECT public: explicit WebViewModel(QObject *parent = nullptr); ~WebViewModel() override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; QMap< int, QVariant > itemData(const QModelIndex &index) const override; bool filterAcceptsObject(QObject *object) const override; }; } #endif // GAMMARAY_WEBVIEWMODEL_H gammaray-2.11.3/plugins/webinspector/webviewmodelroles.h000066400000000000000000000025371413310443000234700ustar00rootroot00000000000000/* webviewmodelroles.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_WEBVIEWMODELROLES_H #define GAMMARAY_WEBVIEWMODELROLES_H #include namespace GammaRay { namespace WebViewModelRoles { enum Role { WebKitVersionRole = ObjectModel::UserRole }; } } #endif gammaray-2.11.3/plugins/widgetinspector/000077500000000000000000000000001413310443000202605ustar00rootroot00000000000000gammaray-2.11.3/plugins/widgetinspector/CMakeLists.txt000066400000000000000000000057631413310443000230330ustar00rootroot00000000000000# probe plugin if (NOT GAMMARAY_CLIENT_ONLY_BUILD) set(gammaray_widgetinspector_plugin_srcs widgetinspector.cpp widgetinspectorinterface.cpp widgetinspectorserver.cpp overlaywidget.cpp widgettreemodel.cpp widgetpaintanalyzerextension.cpp widget3dmodel.cpp waextension/widgetattributeextension.cpp ) gammaray_add_plugin(gammaray_widgetinspector_plugin JSON gammaray_widgetinspector.json SOURCES ${gammaray_widgetinspector_plugin_srcs} ) target_link_libraries(gammaray_widgetinspector_plugin Qt5::Gui Qt5::Widgets gammaray_kitemmodels gammaray_core ) endif() # probe export actions add_library(gammaray_widget_export_actions SHARED widgetinspector_export_actions.cpp uiextractor.cpp ) set_target_properties(gammaray_widget_export_actions PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${GAMMARAY_OUTPUT_PREFIX}/${PROBE_PLUGIN_INSTALL_DIR}" RUNTIME_OUTPUT_DIRECTORY "${GAMMARAY_OUTPUT_PREFIX}/${PROBE_PLUGIN_INSTALL_DIR}" PREFIX "lib" ) gammaray_set_rpath(gammaray_widget_export_actions ${PROBE_PLUGIN_INSTALL_DIR}) target_compile_features(gammaray_widget_export_actions PUBLIC ${GAMMARAY_REQUIRED_CXX_FEATURES}) if(GAMMARAY_INSTALL_QT_LAYOUT) set_target_properties(gammaray_widget_export_actions PROPERTIES OUTPUT_NAME gammaray_widget_export_actions-${GAMMARAY_PROBE_ABI}) endif() target_link_libraries(gammaray_widget_export_actions Qt5::Widgets) if (TARGET Qt5::Designer) target_link_libraries(gammaray_widget_export_actions Qt5::Designer) endif() if (TARGET Qt5::PrintSupport) target_link_libraries(gammaray_widget_export_actions Qt5::PrintSupport) endif() if (TARGET Qt5::Svg) target_link_libraries(gammaray_widget_export_actions Qt5::Svg) endif() install(TARGETS gammaray_widget_export_actions DESTINATION ${PROBE_PLUGIN_INSTALL_DIR}) if(MSVC) install(FILES "$/$" DESTINATION ${PROBE_PLUGIN_INSTALL_DIR} CONFIGURATIONS Debug RelWithDebInfo) endif() # UI plugin if(GAMMARAY_BUILD_UI) set(gammaray_widgetinspector_ui_plugin_srcs widgetinspectorwidget.cpp widgetinspectorinterface.cpp widgetinspectorclient.cpp widgetclientmodel.cpp widgetremoteview.cpp waextension/widgetattributetab.cpp assets.qrc ) if (GAMMARAY_WITH_WIDGET3D) add_subdirectory(assets) set(gammaray_widgetinspector_ui_plugin_srcs ${gammaray_widgetinspector_ui_plugin_srcs} widget3dview.cpp widget3dwindowmodel.cpp widget3dsubtreemodel.cpp widget3dimagetextureimage.cpp ) endif() gammaray_add_plugin(gammaray_widgetinspector_ui_plugin JSON gammaray_widgetinspector.json SOURCES ${gammaray_widgetinspector_ui_plugin_srcs} ) target_link_libraries(gammaray_widgetinspector_ui_plugin gammaray_ui ) if (GAMMARAY_WITH_WIDGET3D) target_link_libraries(gammaray_widgetinspector_ui_plugin Qt5::Qml Qt5::Quick Qt5::3DRender Qt5::3DLogic Qt5::3DInput Qt5::3DQuick ) endif() endif() gammaray-2.11.3/plugins/widgetinspector/assets.qrc000066400000000000000000000014731413310443000222760ustar00rootroot00000000000000 assets/qml/main.qml assets/qml/Horizontals.qml assets/qml/WidgetMaterial.qml assets/qml/WidgetDelegate.qml assets/qml/WidgetInfo.qml assets/qml/CameraController.qml assets/qml/CheckBox.qml assets/shaders/widget.vert assets/shaders/widget.frag assets/shaders/widget_wireframe.vert assets/shaders/widget_wireframe.geom assets/shaders/widget_wireframe.frag assets/shaders/horizontal.vert assets/shaders/horizontal.geom assets/shaders/horizontal.frag gammaray-2.11.3/plugins/widgetinspector/assets/000077500000000000000000000000001413310443000215625ustar00rootroot00000000000000gammaray-2.11.3/plugins/widgetinspector/assets/CMakeLists.txt000066400000000000000000000000601413310443000243160ustar00rootroot00000000000000add_subdirectory(qml) add_subdirectory(shaders) gammaray-2.11.3/plugins/widgetinspector/assets/qml/000077500000000000000000000000001413310443000223535ustar00rootroot00000000000000gammaray-2.11.3/plugins/widgetinspector/assets/qml/CMakeLists.txt000066400000000000000000000002551413310443000251150ustar00rootroot00000000000000set(WIDGET3D_QML_FILES main.qml CheckBox.qml Horizontals.qml WidgetDelegate.qml WidgetInfo.qml WidgetMaterial.qml ) qml_lint(${WIDGET3D_QML_FILES}) gammaray-2.11.3/plugins/widgetinspector/assets/qml/CameraController.qml000066400000000000000000000131411413310443000263220ustar00rootroot00000000000000/* CameraController.qml This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Daniel Vrátil Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ import Qt3D.Core 2.0 import Qt3D.Input 2.0 import Qt3D.Logic 2.0 import Qt3D.Render 2.0 Entity { id: root property Camera camera property vector3d _originalUpVector onCameraChanged: { if (root.camera) { // we don't want to create a binding, but a copy of the initial value _originalUpVector = root.camera.upVector; } } KeyboardDevice { id: keyboard } MouseDevice { id: mouse sensitivity: 0.1 } components: [ LogicalDevice { id: logicalDevice; actions: [ Action { id: moveAction inputs: [ ActionInput { sourceDevice: mouse buttons: [ MouseEvent.LeftButton ] } ] }, Action { id: rotateAction inputs: [ ActionInput { sourceDevice: mouse buttons: [ MouseEvent.RightButton ] } ] }, Action { id: zoomInAction inputs: [ ActionInput { sourceDevice: keyboard buttons: [ Qt.Key_Plus ] } ] }, Action { id: zoomOutAction inputs: [ ActionInput { sourceDevice: keyboard buttons: [ Qt.Key_Minus ] } ] } ] axes: [ Axis { id: rotateXAxis inputs: [ AnalogAxisInput { sourceDevice: mouse axis: MouseDevice.X } ] }, Axis { id: rotateYAxis inputs: [ AnalogAxisInput { sourceDevice: mouse axis: MouseDevice.Y } ] }, Axis { id: translateXAxis inputs: [ AnalogAxisInput { sourceDevice: mouse axis: MouseDevice.X } ] }, Axis { id: translateYAxis inputs: [ AnalogAxisInput { sourceDevice: mouse axis: MouseDevice.Y } ] } ] }, FrameAction { property real translationSpeed : 150.0 property real rotationSpeed : 260.0 property real zoomFactor : 0.1 onTriggered: { if (zoomInAction.active || zoomOutAction.active) { var zoom = 1.0 - zoomFactor; if (zoomOutAction.active) { zoom = 1.0 + zoomFactor; } var viewVector = root.camera.position.minus(root.camera.viewCenter) viewVector = viewVector.times(zoom); root.camera.position = root.camera.viewCenter.plus(viewVector); } else if (moveAction.active) { root.camera.translate(Qt.vector3d(translationSpeed * -translateXAxis.value, translationSpeed * -translateYAxis.value, 0).times(dt)); } else if (rotateAction.active) { root.camera.panAboutViewCenter(rotationSpeed * rotateXAxis.value * dt); root.camera.tiltAboutViewCenter(rotationSpeed * rotateYAxis.value * dt); // lock the camera roll angle root.camera.setUpVector(_originalUpVector); } } } ] } gammaray-2.11.3/plugins/widgetinspector/assets/qml/CheckBox.qml000066400000000000000000000031271413310443000245570ustar00rootroot00000000000000/* CheckBox.qml This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Daniel Vrátil Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ import QtQuick 2.5 import QtQuick.Layouts 1.2 import QtQuick.Controls 1.2 as QQControls RowLayout { spacing: -2 property alias checked : chkBox.checked property alias text: lbl.text QQControls.CheckBox { id: chkBox } Text { id: lbl color: "white" MouseArea { anchors.fill: parent; onClicked: chkBox.checked = !chkBox.checked } Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter } } gammaray-2.11.3/plugins/widgetinspector/assets/qml/Horizontals.qml000066400000000000000000000130741413310443000254070ustar00rootroot00000000000000/* Horizontals.qml This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Daniel Vrátil Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ import Qt3D.Core 2.0 import Qt3D.Render 2.0 import Qt3D.Extras 2.0 Entity { id: root property real geomWidth property real geomHeight property real geomX property real geomY property real geomZ property real explosionFactor: 0.0 property real highlightFactor: 0.0 readonly property real _scaleFactor: 10.0 enabled: false GeometryRenderer { id: mesh instanceCount: 1 indexOffset: 0 firstInstance: 0 primitiveType: GeometryRenderer.Points enabled: parent.enabled // FIXME: remove once "enabled" propagates correctly in Qt3D geometry: Geometry { attributes: [ Attribute { readonly property int floatSize: 4 attributeType: Attribute.VertexAttribute vertexBaseType: Attribute.Float count: 4 vertexSize: 3 // number of values per vertex byteOffset: 0 byteStride: 3 * floatSize name: defaultPositionAttributeName() buffer: Buffer { function generateVertexBufferData() { var v0 = Qt.vector2d(-geomWidth / 2.0, geomHeight / 2.0); // bottom let var v1 = Qt.vector2d(-geomWidth / 2.0, -geomHeight / 2.0); // top left var v2 = Qt.vector2d(geomWidth / 2.0, geomHeight / 2.0); // bottom right var v3 = Qt.vector2d(geomWidth / 2.0, -geomHeight / 2.0); // top right // FIXME: Without the Z-axis Qt3D constantly complains // that this Attribute is not suitable for volumetric object // picking, despite having no ObjectPicker installed on this // Entity... return new Float32Array([v0.x, v0.y, 0.0, v1.x, v1.y, 0.0, v2.x, v2.y, 0.0, v3.x, v3.y, 0.0]); } type: Buffer.VertexBuffer data: generateVertexBufferData() } } ] } } Material { id: material enabled: parent.enabled // FIXME: remove once "enabled" propagates correctly in Qt3D effect: Effect { techniques: [ Technique { graphicsApiFilter { api: GraphicsApiFilter.OpenGL profile: GraphicsApiFilter.CoreProfile majorVersion: 3 minorVersion: 2 } filterKeys: [ FilterKey { name: "renderingStyle" value: "forward" } ] parameters: [ Parameter { name: "widget.explosionFactor" value: root.explosionFactor }, Parameter { name: "highlightFactor" value: root.highlightFactor } ] renderPasses: [ RenderPass { shaderProgram: ShaderProgram { vertexShaderCode: loadSource("qrc:/gammaray/assets/shaders/horizontal.vert") geometryShaderCode: loadSource("qrc:/gammaray/assets/shaders/horizontal.geom") fragmentShaderCode: loadSource("qrc:/gammaray/assets/shaders/horizontal.frag") } } ] } ] } } Transform { id: transform enabled: parent.enabled // FIXME: remove once "enabled" propagates correctly in Qt3D translation: Qt.vector3d( geomWidth / 2.0 + geomX - topLevelGeometry.width / 2.0 / _scaleFactor, -geomHeight / 2.0 - geomY + topLevelGeometry.height / 2.0 / _scaleFactor, geomZ ) } components: [ mesh, material, transform ] } gammaray-2.11.3/plugins/widgetinspector/assets/qml/WidgetDelegate.qml000066400000000000000000000073321413310443000257510ustar00rootroot00000000000000/* WidgetDelegate.qml This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Daniel Vrátil Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ import Qt3D.Core 2.0 import Qt3D.Render 2.0 import Qt3D.Extras 2.0 import QtQuick 2.5 as QQ2 Entity { id: root; property rect topLevelGeometry property real explosionFactor: 0 property string objectId property var metaData property bool isWindow : false property alias wireframe: material.wireframe property bool horizontals: false property int depth property var frontTextureImage property var backTextureImage property rect geometry property bool selected : objectPicker.containsMouse readonly property real _scaleFactor : 10.0 readonly property real _geomWidth: root.geometry.width / _scaleFactor readonly property real _geomHeight: root.geometry.height / _scaleFactor readonly property real _geomX: root.geometry.x / _scaleFactor readonly property real _geomY: root.geometry.y / _scaleFactor property real _geomZ: root.depth / (_scaleFactor * 2.0) + root.depth * root.explosionFactor QQ2.Behavior on explosionFactor { QQ2.NumberAnimation { duration: 200 easing.type: Easing.OutQuart } } property real _highlightFactor : objectPicker.containsMouse ? 0.5 : 0.0; QQ2.Behavior on _highlightFactor { QQ2.NumberAnimation { duration: 100; } } Entity { id: widgetCube CuboidMesh { id: cubeMesh xExtent: root._geomWidth yExtent: root._geomHeight zExtent: 1 } WidgetMaterial { id: material frontTextureImage: root.frontTextureImage backTextureImage: root.backTextureImage explosionFactor: root.explosionFactor highlightFactor: root._highlightFactor level: root.depth } Transform { id: transform translation: Qt.vector3d( _geomWidth / 2.0 + _geomX - topLevelGeometry.width / 2.0 / _scaleFactor, -_geomHeight / 2.0 - _geomY + topLevelGeometry.height / 2.0 / _scaleFactor, _geomZ ) } ObjectPicker { id: objectPicker hoverEnabled: true } components: [ cubeMesh, material, transform, objectPicker ] } Horizontals { id: horizontals enabled: !root.isWindow && root.horizontals geomWidth: _geomWidth geomHeight: _geomHeight geomX: _geomX geomY: _geomY geomZ: _geomZ explosionFactor: root.explosionFactor highlightFactor: root._highlightFactor } } gammaray-2.11.3/plugins/widgetinspector/assets/qml/WidgetInfo.qml000066400000000000000000000060071413310443000251300ustar00rootroot00000000000000/* WidgetDelegate.qml This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Daniel Vrátil Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ import QtQuick 2.5 Item { id: wi property variant metaData: null function parseObjectName(o) { if (o.objectName != '') { return o.objectName + " (0x" + o.address.toString(16) + ")"; } else { return "0x" + o.address.toString(16); } } opacity: metaData ? 1.0 : 0.0 Behavior on opacity { NumberAnimation { duration: 100 } } Rectangle { id: background anchors.fill: parent color: "darkgray" border { width: 2 color: "black" } opacity: 0.8 radius: 4 } height: column.height + 20 width: column.width + 20 Behavior on height { NumberAnimation { duration: 100 } } Behavior on width { NumberAnimation { duration: 100 } } Column { id: column height: childrenRect.height width: childrenRect.width anchors.left: parent.left anchors.top: parent.top anchors.margins: 10 spacing: 2 Text { text: "" + (metaData ? parseObjectName(metaData) : "") + "" } Text { text: "" + qsTranslate("GammaRay::WidgetInspector::QML", "Class:") + " " + (metaData ? metaData.className : "") } Text { text: "" + qsTranslate("GammaRay::WidgetInspector::QML", "Parent:") + " " + (metaData ? parseObjectName(metaData.parent) : "") } Text { text: "" + qsTranslate("GammaRay::WidgetInspector::QML", "Position:") + " " + (metaData ? metaData.geometry.x + ", " + metaData.geometry.y : "") } Text { text: "" + qsTranslate("GammaRay::WidgetInspector::QML", "Size:") + " " + (metaData ? metaData.geometry.width + " x " + metaData.geometry.height : "") } } } gammaray-2.11.3/plugins/widgetinspector/assets/qml/WidgetMaterial.qml000066400000000000000000000100651413310443000257720ustar00rootroot00000000000000/* WidgetMaterial.qml This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Daniel Vrátil Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ import Qt3D.Core 2.0 import Qt3D.Render 2.0 import QtQuick 2.5 as QQ2 import com.kdab.GammaRay 1.0 Material { id: root property real explosionFactor: 0 property real highlightFactor: 0 property int level: 0 property bool wireframe: false property var frontTextureImage property var backTextureImage Texture2D { id: frontTexture; minificationFilter: Texture2D.LinearMipMapLinear magnificationFilter: Texture2D.Linear maximumAnisotropy: 16.0 generateMipMaps: true Widget3DImageTextureImage { image: root.frontTextureImage } } Texture2D { id: backTexture; minificationFilter: Texture2D.LinearMipMapLinear magnificationFilter: Texture2D.Linear maximumAnisotropy: 16.0 generateMipMaps: true Widget3DImageTextureImage { image: root.backTextureImage } } effect: Effect { id: effect; techniques: [ Technique { graphicsApiFilter { api: GraphicsApiFilter.OpenGL profile: GraphicsApiFilter.CoreProfile majorVersion: 3 minorVersion: 2 } filterKeys: [ FilterKey { name: "renderingStyle" value: "forward" } ] parameters: [ Parameter { name: "frontTexture" value: frontTexture }, Parameter { name: "backTexture" value: root.backTextureImage ? backTexture : frontTexture }, Parameter { name: "highlightFactor" value: root.highlightFactor } ] renderPasses: [ RenderPass { id: texturePass shaderProgram: ShaderProgram { vertexShaderCode: loadSource("qrc:/gammaray/assets/shaders/widget.vert") fragmentShaderCode: loadSource("qrc:/gammaray/assets/shaders/widget.frag") } }, RenderPass { id: wireframePass enabled: wireframe shaderProgram: ShaderProgram { vertexShaderCode: loadSource("qrc:/gammaray/assets/shaders/widget_wireframe.vert") geometryShaderCode: loadSource("qrc:/gammaray/assets/shaders/widget_wireframe.geom") fragmentShaderCode: loadSource("qrc:/gammaray/assets/shaders/widget_wireframe.frag") } } ] } ] } } gammaray-2.11.3/plugins/widgetinspector/assets/qml/main.qml000066400000000000000000000134451413310443000240210ustar00rootroot00000000000000/* main.qml This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Daniel Vrátil Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ import QtQuick 2.5 as QQ2 import QtQml 2.2 as Qml2 import QtQuick.Controls 1.2 as QQControls import QtQuick.Scene3D 2.0 import Qt3D.Core 2.0 import Qt3D.Render 2.0 import Qt3D.Input 2.0 import Qt3D.Extras 2.0 import com.kdab.GammaRay 1.0 QQ2.Item { function resetView() { mainCamera.resetView() root.explosionFactor = 0 } Scene3D { id: scene3d anchors.fill: parent focus: true hoverEnabled: true aspects: [ "input", "logic", "render" ] cameraAspectRatioMode: Scene3D.AutomaticAspectRatio multisample: true Entity { id: root property real explosionFactor: 0 Camera { id: mainCamera projectionType: CameraLens.PerspectiveProjection fieldOfView: 45 nearPlane: 0.1 farPlane: 1000.0 viewCenter: Qt.vector3d(0.0, 0.0, 0.0) upVector: Qt.vector3d(0.0, 1.0, 0.0) position: Qt.vector3d(0.0, 0.0, 80.0) function resetView() { viewCenter = Qt.vector3d(0.0, 0.0, 0.0); upVector = Qt.vector3d(0.0, 1.0, 0.0); position = Qt.vector3d(0.0, 0.0, 80.0); } } // Render from the mainCamera components: [ RenderSettings { activeFrameGraph: ForwardRenderer { camera: mainCamera clearColor: "black" } pickingSettings.pickMethod: PickingSettings.TrianglePicking // TODO: Does not work with Scene3D //renderPolicy: RenderSettings.OnDemand renderPolicy: RenderSettings.Always }, InputSettings { eventSource: _renderWindow } ] CameraController { id: cameraController camera: mainCamera } QQ2.Connections { target: _renderWindow onWheel: { var newFactor = root.explosionFactor + delta / 80.0; root.explosionFactor = newFactor < 0 ? 0 : newFactor; } } NodeInstantiator { id: instantiator model: _widgetModel asynchronous: false property var selectedWidget property var topLevelWindow : null delegate: WidgetDelegate { id: windowDelegate topLevelGeometry: instantiator.topLevelWindow !== null ? instantiator.topLevelWindow.geometry : model.geometry objectId: model.objectId geometry: model.geometry frontTextureImage: model.frontTexture backTextureImage: model.backTexture explosionFactor: root.explosionFactor depth: model.depth metaData: model.metaData isWindow: model.isWindow wireframe: renderWireframe.checked horizontals: renderHorizontals.checked onSelectedChanged: if (selected) instantiator.selectedWidget = this } onObjectAdded: { if (object.isWindow) { topLevelWindow = object; } } onObjectRemoved: { if (topLevelWindow == object) { topLevelWindow = null; } } } Qml2.Binding { target: _selectionHelper property: "currentObject" value: instantiator.selectedWidget ? instantiator.selectedWidget.objectId : "" } } } WidgetInfo { id: widgetInfo; metaData: instantiator.selectedWidget && instantiator.selectedWidget.selected ? instantiator.selectedWidget.metaData : null anchors { top: parent.top right: parent.right margins: 20 } } QQ2.Column { anchors { bottom: parent.bottom right: parent.right margins: 20 } CheckBox { id: renderWireframe text: qsTranslate("GammaRay::WidgetInspector::QML", "Wireframe") } CheckBox { id: renderHorizontals text: qsTranslate("GammaRay::WidgetInspector::QML", "Horizontals") } } } gammaray-2.11.3/plugins/widgetinspector/assets/shaders/000077500000000000000000000000001413310443000232135ustar00rootroot00000000000000gammaray-2.11.3/plugins/widgetinspector/assets/shaders/CMakeLists.txt000066400000000000000000000007611413310443000257570ustar00rootroot00000000000000set(WIDGET3D_SHADERS_HORIZONTALS_PROGRAM horizontal.frag horizontal.geom horizontal.vert ) set(WIDGET3D_SHADERS_WIDGET_PROGRAM widget.frag widget.vert ) set(WIDGET3D_SHADERS_WIDGET_WIREFRAME_PROGRAM widget_wireframe.frag widget_wireframe.geom widget_wireframe.vert ) glslang_validate_program(${WIDGET3D_SHADERS_HORIZONTALS_PROGRAM}) glslang_validate_program(${WIDGET3D_SHADERS_WIDGET_PROGRAM}) glslang_validate_program(${WIDGET3D_SHADERS_WIDGET_WIREFRAME_PROGRAM}) gammaray-2.11.3/plugins/widgetinspector/assets/shaders/horizontal.frag000066400000000000000000000024521413310443000262500ustar00rootroot00000000000000#version 150 core /* horizontal.frag This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Daniel Vrátil Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ out vec4 fragColor; uniform float highlightFactor; void main(void) { fragColor = mix(vec4(0.0, 1.0, 0.0, 1.0), vec4(1.0, 0.0, 0.0, 1.0), highlightFactor); } gammaray-2.11.3/plugins/widgetinspector/assets/shaders/horizontal.geom000066400000000000000000000031601413310443000262550ustar00rootroot00000000000000#version 150 core /* horizontal.geom This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Daniel Vrátil Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ layout(points) in; layout(line_strip, max_vertices = 2) out; in VertexData { vec3 vertexPosition; } gs_in[]; struct Widget { float explosionFactor; }; uniform mat4 mvp; uniform Widget widget; void main() { vec4 pos = vec4(gs_in[0].vertexPosition, 1.0); pos.z -= 0.5; // half thickness of the widget cuboid gl_Position = mvp * pos; EmitVertex(); pos.z -= widget.explosionFactor - 1.0; gl_Position = mvp * pos; EmitVertex(); EndPrimitive(); } gammaray-2.11.3/plugins/widgetinspector/assets/shaders/horizontal.vert000066400000000000000000000025311413310443000263070ustar00rootroot00000000000000#version 150 core /* horizontal.vert This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Daniel Vrátil Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ in vec3 vertexPosition; out VertexData { vec3 vertexPosition; } vs_out; uniform mat4 mvp; void main(void) { vs_out.vertexPosition = vertexPosition; gl_Position = mvp * vec4(vertexPosition, 1.0); } gammaray-2.11.3/plugins/widgetinspector/assets/shaders/widget.frag000066400000000000000000000034061413310443000253420ustar00rootroot00000000000000#version 150 core /* widget.frag This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Daniel Vrátil Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ in VertexData { vec3 position; vec3 normal; vec2 texCoord; } fs_in; out vec4 fragColor; uniform sampler2D frontTexture; uniform sampler2D backTexture; uniform float highlightFactor; void main(void) { vec2 coords = vec2(fs_in.texCoord.x, 1 - fs_in.texCoord.y); // front face if (fs_in.normal.z > 0) { fragColor = texture(frontTexture, coords); // back face } else if (fs_in.normal.z < 0) { fragColor = texture(backTexture, coords); // side face } else { fragColor = vec4(0.0, 0.4, 0.0, 1.0); } fragColor = mix(fragColor, vec4(0.5, 0.5, 0.5, 1.0), highlightFactor); } gammaray-2.11.3/plugins/widgetinspector/assets/shaders/widget.vert000066400000000000000000000030471413310443000254040ustar00rootroot00000000000000#version 150 core /* widget.vert This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Daniel Vrátil Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ in vec3 vertexPosition; in vec3 vertexNormal; in vec2 vertexTexCoord; out VertexData { vec3 position; vec3 normal; vec2 texCoord; } vs_out; uniform mat4 modelView; uniform mat3 modelViewNormal; uniform mat4 mvp; void main(void) { vs_out.position = vertexPosition; vs_out.normal = normalize(vertexNormal); vs_out.texCoord = vertexTexCoord; gl_Position = mvp * vec4(vertexPosition, 1.0); } gammaray-2.11.3/plugins/widgetinspector/assets/shaders/widget_wireframe.frag000066400000000000000000000024601413310443000274020ustar00rootroot00000000000000#version 150 core /* widget_wireframe.frag This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Daniel Vrátil Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ out vec4 fragColor; uniform float highlightFactor; void main(void) { fragColor = mix(vec4(0.0, 1.0, 0.0, 1.0), vec4(1.0, 0.0, 0.0, 1.0), highlightFactor); } gammaray-2.11.3/plugins/widgetinspector/assets/shaders/widget_wireframe.geom000066400000000000000000000043231413310443000274120ustar00rootroot00000000000000#version 150 core /* widget_wireframe.geom This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Daniel Vrátil Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ layout(triangles) in; layout(line_strip, max_vertices = 4 /* 2 lines = 4 vertices */) out; in VertexData { vec3 position; } gs_in[]; void drawLine(vec4 a, vec4 b) { gl_Position = a; EmitVertex(); gl_Position = b; EmitVertex(); EndPrimitive(); } void main() { // This GS takes all three sides of a rectangle, but only emits vertices for // its catheti, which gives us a nice wireframe. vec3 aSide = (gs_in[0].position - gs_in[1].position); vec3 bSide = (gs_in[0].position - gs_in[2].position); vec3 cSide = (gs_in[1].position - gs_in[2].position); if (dot(aSide, bSide) == 0) { drawLine(gl_in[0].gl_Position, gl_in[1].gl_Position); drawLine(gl_in[0].gl_Position, gl_in[2].gl_Position); } else if (dot(aSide, cSide) == 0) { drawLine(gl_in[0].gl_Position, gl_in[1].gl_Position); drawLine(gl_in[1].gl_Position, gl_in[2].gl_Position); } else if (dot(bSide, cSide) == 0) { drawLine(gl_in[0].gl_Position, gl_in[2].gl_Position); drawLine(gl_in[1].gl_Position, gl_in[2].gl_Position); } } gammaray-2.11.3/plugins/widgetinspector/assets/shaders/widget_wireframe.vert000066400000000000000000000025261413310443000274460ustar00rootroot00000000000000#version 150 core /* widget_wireframe.vert This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Daniel Vrátil Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ in vec3 vertexPosition; out VertexData { vec3 position; } vs_out; uniform mat4 mvp; void main(void) { vs_out.position = vertexPosition; gl_Position = mvp * vec4(vertexPosition, 1.0); } gammaray-2.11.3/plugins/widgetinspector/gammaray_widgetinspector.json000066400000000000000000000002351413310443000262430ustar00rootroot00000000000000{ "id": "gammaray_widgetinspector", "name": "Widgets", "types": [ "QWidget", "QApplication" ], "selectableTypes": [ "QWidget", "QLayout" ] } gammaray-2.11.3/plugins/widgetinspector/overlaywidget.cpp000066400000000000000000000144401413310443000236540ustar00rootroot00000000000000/* overlaywidget.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Tobias Koenig Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "overlaywidget.h" #include #include #include #include #include #include using namespace GammaRay; static QWidget *toplevelWidget(QWidget *widget) { Q_ASSERT(widget); QWidget *parent = widget; auto isTopLevel = [](QWidget *widget) { return widget->isWindow(); }; auto lastSuitableParent = parent; while (parent->parentWidget() && !isTopLevel(parent->parentWidget()) && !isTopLevel(parent)) { parent = parent->parentWidget(); // don't pick parents that can't take the overlay as a children if (!qobject_cast(parent)) { lastSuitableParent = parent; } } return lastSuitableParent; } OverlayWidget::OverlayWidget() : m_currentToplevelWidget(nullptr), m_drawLayoutOutlineOnly(true) { setAttribute(Qt::WA_TransparentForMouseEvents); setFocusPolicy(Qt::NoFocus); } void OverlayWidget::placeOn(const WidgetOrLayoutFacade &item) { if (item.isNull()) { if (!m_currentItem.isNull()) m_currentItem->removeEventFilter(this); if (m_currentToplevelWidget) m_currentToplevelWidget->removeEventFilter(this); m_currentToplevelWidget = nullptr; m_currentItem.clear(); m_outerRect = QRect(); m_layoutPath = QPainterPath(); update(); return; } if (!m_currentItem.isNull()) m_currentItem->removeEventFilter(this); m_currentItem = item; QWidget *toplevel = toplevelWidget(item.widget()); Q_ASSERT(toplevel); if (toplevel != m_currentToplevelWidget) { if (m_currentToplevelWidget) m_currentToplevelWidget->removeEventFilter(this); m_currentToplevelWidget = toplevel; setParent(toplevel); move(0, 0); resize(toplevel->size()); m_currentToplevelWidget->installEventFilter(this); show(); } m_currentItem->installEventFilter(this); updatePositions(); } bool OverlayWidget::eventFilter(QObject *receiver, QEvent *event) { if (!m_currentItem.isNull() && m_currentToplevelWidget != m_currentItem.widget()->window()) { // detect (un)docking placeOn(m_currentItem); return false; } if (receiver == m_currentItem.data()) { if (event->type() == QEvent::Resize || event->type() == QEvent::Move || event->type() == QEvent::Show || event->type() == QEvent::Hide) { resizeOverlay(); updatePositions(); } } else if (receiver == m_currentToplevelWidget) { if (event->type() == QEvent::Resize) { resizeOverlay(); updatePositions(); } } return false; } void OverlayWidget::updatePositions() { if (m_currentItem.isNull() || !m_currentToplevelWidget) return; if (!m_currentItem.isVisible()) m_outerRectColor = Qt::green; else m_outerRectColor = Qt::red; const QPoint parentPos = m_currentItem.widget()->mapTo(m_currentToplevelWidget, m_currentItem.pos()); m_outerRect = QRect(parentPos.x(), parentPos.y(), m_currentItem.geometry().width(), m_currentItem.geometry().height()).adjusted(0, 0, -1, -1); m_layoutPath = QPainterPath(); if (m_currentItem.layout() && qstrcmp(m_currentItem.layout()->metaObject()->className(), "QMainWindowLayout") != 0) { const QRect layoutGeometry = m_currentItem.layout()->geometry(); const QRect mappedOuterRect = QRect(m_currentItem.widget()->mapTo(m_currentToplevelWidget, layoutGeometry.topLeft()), layoutGeometry.size()); QPainterPath outerPath; outerPath.addRect(mappedOuterRect.adjusted(1, 1, -2, -2)); QPainterPath innerPath; for (int i = 0; i < m_currentItem.layout()->count(); ++i) { QLayoutItem *item = m_currentItem.layout()->itemAt(i); if (item->widget() && !item->widget()->isVisible()) continue; const QRect mappedInnerRect = QRect(m_currentItem.widget()->mapTo(m_currentToplevelWidget, item->geometry().topLeft()), item->geometry().size()); innerPath.addRect(mappedInnerRect); } m_layoutPath.setFillRule(Qt::OddEvenFill); m_layoutPath = outerPath.subtracted(innerPath); if (m_layoutPath.isEmpty()) { m_layoutPath = outerPath; m_layoutPath.addPath(innerPath); m_drawLayoutOutlineOnly = true; } else { m_drawLayoutOutlineOnly = false; } } update(); } void OverlayWidget::resizeOverlay() { if (m_currentToplevelWidget) { move(0, 0); resize(m_currentToplevelWidget->size()); } } void OverlayWidget::paintEvent(QPaintEvent *) { QPainter p(this); p.setPen(m_outerRectColor); p.drawRect(m_outerRect); QBrush brush(Qt::BDiagPattern); brush.setColor(Qt::blue); if (!m_drawLayoutOutlineOnly) p.fillPath(m_layoutPath, brush); p.setPen(Qt::blue); p.drawPath(m_layoutPath); } gammaray-2.11.3/plugins/widgetinspector/overlaywidget.h000066400000000000000000000067151413310443000233270ustar00rootroot00000000000000/* overlaywidget.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Tobias Koenig Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_WIDGETINSPECTOR_OVERLAYWIDGET_H #define GAMMARAY_WIDGETINSPECTOR_OVERLAYWIDGET_H #include #include #include #include namespace GammaRay { class WidgetOrLayoutFacade { public: WidgetOrLayoutFacade() : m_object(nullptr) {} WidgetOrLayoutFacade(QWidget *widget) : m_object(widget) {} //krazy:exclude=explicit WidgetOrLayoutFacade(QLayout *layout) : m_object(layout) {} //krazy:exclude=explicit /// Get either the layout of the widget or the this-pointer inline QLayout *layout() const { return isLayout() ? asLayout() : asWidget()->layout(); } /// Get either the parent widget of the layout or the this-pointer QWidget *widget() const { return isLayout() ? asLayout()->parentWidget() : asWidget(); } QRect geometry() const { return isLayout() ? asLayout()->geometry() : asWidget()->geometry(); } bool isVisible() const { return widget() ? widget()->isVisible() && !widget()->isHidden() : false; } QPoint pos() const { return isLayout() ? asLayout()->geometry().topLeft() : QPoint(0, 0); } inline bool isNull() const { return !m_object; } inline QObject* data() { return m_object; } inline QObject* operator->() const { Q_ASSERT(!isNull()); return m_object; } inline void clear() { m_object = nullptr; } private: inline bool isLayout() const { Q_ASSERT(!isNull()); return qobject_cast(m_object); } inline QLayout *asLayout() const { return static_cast(m_object.data()); } inline QWidget *asWidget() const { return static_cast(m_object.data()); } QPointer m_object; }; class OverlayWidget : public QWidget { Q_OBJECT public: OverlayWidget(); /** * Place the overlay on @p item * * @param item The overlay can be cover a widget or a layout */ void placeOn(const WidgetOrLayoutFacade &item); bool eventFilter(QObject *receiver, QEvent *event) override; protected: void paintEvent(QPaintEvent *event) override; private: void resizeOverlay(); void updatePositions(); QWidget *m_currentToplevelWidget; WidgetOrLayoutFacade m_currentItem; QRect m_outerRect; QColor m_outerRectColor; QPainterPath m_layoutPath; bool m_drawLayoutOutlineOnly; }; } #endif gammaray-2.11.3/plugins/widgetinspector/uiextractor.cpp000066400000000000000000000044501413310443000233400ustar00rootroot00000000000000/* uiextractor.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "uiextractor.h" #ifdef HAVE_QT_DESIGNER #include #include #include #include using namespace GammaRay; bool UiExtractor::checkProperty(QObject *obj, const QString &prop) const { const QMetaObject *mo = obj->metaObject(); const QMetaProperty mp = mo->property(mo->indexOfProperty(prop.toLatin1())); // TODO come up with some more aggressive filtering if (mp.isValid() && mp.isDesignable(obj) && mp.isStored(obj) && mp.isWritable()) { const QVariant value = mp.read(obj); // try to figure out the default by resetting to it if (mp.isResettable()) { mp.reset(obj); if (mp.read(obj) == value) return false; mp.write(obj, value); return true; } // some guessing for non-resettable properties if (value.isNull() || !value.isValid()) return false; if (value.type() == QVariant::String) return !value.toString().isEmpty(); else if (value.type() == QVariant::Locale) return value.toLocale() != QLocale::system(); return true; } return false; } #endif gammaray-2.11.3/plugins/widgetinspector/uiextractor.h000066400000000000000000000027421413310443000230070ustar00rootroot00000000000000/* uiextractor.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_WIDGETINSPECTOR_UIEXTRACTOR_H #define GAMMARAY_WIDGETINSPECTOR_UIEXTRACTOR_H #include #ifdef HAVE_QT_DESIGNER #include namespace GammaRay { class UiExtractor : public QFormBuilder { protected: bool checkProperty(QObject *obj, const QString &prop) const override; }; } #endif #endif // GAMMARAY_UIEXTRACTOR_H gammaray-2.11.3/plugins/widgetinspector/waextension/000077500000000000000000000000001413310443000226245ustar00rootroot00000000000000gammaray-2.11.3/plugins/widgetinspector/waextension/widgetattributeextension.cpp000066400000000000000000000037631413310443000305050ustar00rootroot00000000000000/* widgetattributeextension.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "widgetattributeextension.h" #include #include #include using namespace GammaRay; WidgetAttributeExtension::WidgetAttributeExtension(PropertyController *controller) : PropertyControllerExtension(controller->objectBaseName() + ".widgetAttributes") , m_attributeModel(new AttributeModel(controller)) { m_attributeModel->setAttributeType("WidgetAttribute"); controller->registerModel(m_attributeModel, QStringLiteral("widgetAttributeModel")); } WidgetAttributeExtension::~WidgetAttributeExtension() = default; bool WidgetAttributeExtension::setQObject(QObject *object) { if (auto widget = qobject_cast(object)) { m_attributeModel->setObject(widget); return true; } m_attributeModel->setObject(nullptr); return false; } gammaray-2.11.3/plugins/widgetinspector/waextension/widgetattributeextension.h000066400000000000000000000033531413310443000301450ustar00rootroot00000000000000/* widgetattributeextension.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_WIDGETATTRIBUTEEXTENSION_H #define GAMMARAY_WIDGETATTRIBUTEEXTENSION_H #include QT_BEGIN_NAMESPACE class QWidget; QT_END_NAMESPACE namespace GammaRay { template class AttributeModel; class WidgetAttributeExtension : public PropertyControllerExtension { public: explicit WidgetAttributeExtension(PropertyController *controller); ~WidgetAttributeExtension(); bool setQObject(QObject *object) override; private: AttributeModel *m_attributeModel; }; } #endif // GAMMARAY_WIDGETATTRIBUTEEXTENSION_H gammaray-2.11.3/plugins/widgetinspector/waextension/widgetattributetab.cpp000066400000000000000000000033341413310443000272310ustar00rootroot00000000000000/* widgetattributetab.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "widgetattributetab.h" #include "ui_widgetattributetab.h" #include #include using namespace GammaRay; WidgetAttributeTab::WidgetAttributeTab(PropertyWidget *parent) : QWidget(parent) , ui(new Ui::WidgetAttributeTab) { ui->setupUi(this); ui->attributeView->header()->setObjectName("attributeViewHeader"); ui->attributeView->setModel(ObjectBroker::model(parent->objectBaseName() + QStringLiteral(".widgetAttributeModel"))); } WidgetAttributeTab::~WidgetAttributeTab() = default; gammaray-2.11.3/plugins/widgetinspector/waextension/widgetattributetab.h000066400000000000000000000031321413310443000266720ustar00rootroot00000000000000/* widgetattributetab.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_WIDGETATTRIBUTETAB_H #define GAMMARAY_WIDGETATTRIBUTETAB_H #include #include namespace GammaRay { class PropertyWidget; namespace Ui { class WidgetAttributeTab; } class WidgetAttributeTab : public QWidget { Q_OBJECT public: explicit WidgetAttributeTab(PropertyWidget *parent = nullptr); ~WidgetAttributeTab() override; private: QScopedPointer ui; }; } #endif // GAMMARAY_WIDGETATTRIBUTETAB_H gammaray-2.11.3/plugins/widgetinspector/waextension/widgetattributetab.ui000066400000000000000000000015711413310443000270650ustar00rootroot00000000000000 GammaRay::WidgetAttributeTab 0 0 400 300 false true GammaRay::DeferredTreeView QTreeView
    ui/deferredtreeview.h
    gammaray-2.11.3/plugins/widgetinspector/widget3dimagetextureimage.cpp000066400000000000000000000057241413310443000261350ustar00rootroot00000000000000/* widget3dimagetextureimage.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Daniel Vrátil Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "widget3dimagetextureimage.h" #include namespace GammaRay { class Widget3DImageTextureDataGenerator : public Qt3DRender::QTextureImageDataGenerator { public: explicit Widget3DImageTextureDataGenerator(const QImage &image) : Qt3DRender::QTextureImageDataGenerator() , mImage(image) { } ~Widget3DImageTextureDataGenerator() { } Qt3DRender::QTextureImageDataPtr operator()() override { Qt3DRender::QTextureImageDataPtr dataPtr(new Qt3DRender::QTextureImageData); dataPtr->setImage(mImage); return dataPtr; } bool operator==(const Qt3DRender::QTextureImageDataGenerator &other) const override { const auto otherFtor = Qt3DRender::functor_cast(&other); return (otherFtor != nullptr && otherFtor->mImage == mImage); } QT3D_FUNCTOR(Widget3DImageTextureDataGenerator) private: QImage mImage; }; } using namespace GammaRay; Widget3DImageTextureImage::Widget3DImageTextureImage(Qt3DCore::QNode *parent) : Qt3DRender::QAbstractTextureImage(parent) { } Widget3DImageTextureImage::~Widget3DImageTextureImage() { } QImage Widget3DImageTextureImage::image() const { return mImage; } void Widget3DImageTextureImage::setImage(const QImage &image) { if (mImage != image) { if (image.format() == QImage::Format_RGBA8888) { mImage = image; } else { // Qt4 does not have RGBA8888 so the probe sends us ARGB32 mImage = image.convertToFormat(QImage::Format_RGBA8888); } Q_EMIT imageChanged(); notifyDataGeneratorChanged(); } } Qt3DRender::QTextureImageDataGeneratorPtr Widget3DImageTextureImage::dataGenerator() const { return Qt3DRender::QTextureImageDataGeneratorPtr(new Widget3DImageTextureDataGenerator(mImage)); } gammaray-2.11.3/plugins/widgetinspector/widget3dimagetextureimage.h000066400000000000000000000036711413310443000256010ustar00rootroot00000000000000/* widget3dimagetextureimage.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Daniel Vrátil Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_WIDGET3DIMAGETEXTUREIMAGE_H #define GAMMARAY_WIDGET3DIMAGETEXTUREIMAGE_H #include #include #include namespace GammaRay { class Widget3DTextureDataFunctor; class Widget3DImageTextureImage : public Qt3DRender::QAbstractTextureImage { Q_OBJECT Q_PROPERTY(QImage image READ image WRITE setImage NOTIFY imageChanged) public: explicit Widget3DImageTextureImage(Qt3DCore::QNode *parent = nullptr); ~Widget3DImageTextureImage(); QImage image() const; Qt3DRender::QTextureImageDataGeneratorPtr dataGenerator() const override; public Q_SLOTS: void setImage(const QImage &image); Q_SIGNALS: void imageChanged(); private: QImage mImage; }; } #endif // GAMMARAY_WIDGET3DIMAGETEXTUREIMAGE_H gammaray-2.11.3/plugins/widgetinspector/widget3dmodel.cpp000066400000000000000000000302251413310443000235210ustar00rootroot00000000000000/* widget3dmodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Daniel Vrátil Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "widget3dmodel.h" #include #include #include #include #include #include #include #include #include using namespace GammaRay; Widget3DWidget::Widget3DWidget(QWidget *qWidget, const QPersistentModelIndex &modelIndex, Widget3DWidget *parent) : QObject(parent) , mModelIndex(modelIndex) , mQWidget(qWidget) , mUpdateTimer(nullptr) , mDepth(0) , mIsPainting(false) , mGeomDirty(true) , mTextureDirty(true) { connect(qWidget, &QObject::destroyed, this, &QObject::deleteLater); mUpdateTimer = new QTimer(this); mUpdateTimer->setSingleShot(true); mUpdateTimer->setInterval(200); connect(mUpdateTimer, &QTimer::timeout, this, &Widget3DWidget::updateTimeout); if (qWidget->isVisible()) { updateTimeout(); } Widget3DWidget *w = this; while (w && !isWindow()) { ++mDepth; w = qobject_cast(w->parent()); } if (!isWindow()) { --mDepth; } mQWidget->installEventFilter(this); mMetaData[QLatin1String("className")] = QString::fromUtf8(mQWidget->metaObject()->className()); mMetaData[QLatin1String("objectName")] = mQWidget->objectName(); mMetaData[QLatin1String("address")] = quintptr(mQWidget.data()); mMetaData[QLatin1String("geometry")] = mQWidget->geometry(); if (mQWidget->parent()) { QVariantMap parentMap; parentMap[QLatin1String("className")] = mQWidget->parent()->metaObject()->className(); parentMap[QLatin1String("objectName")] = mQWidget->parent()->objectName(); parentMap[QLatin1String("address")] = quintptr(mQWidget->parent()); mMetaData[QLatin1String("parent")] = parentMap; } else { mMetaData[QLatin1String("parent")] = QVariant(); } } Widget3DWidget::~Widget3DWidget() = default; bool Widget3DWidget::isWindow() const { if (!mQWidget->isWindow()) { return false; } // Those are technically windows, but we don't want them listed in the window // list // TODO: any more exceptions? if (qobject_cast(mQWidget) || qstrcmp(mQWidget->metaObject()->className(), "QTipLabel") == 0) { return false; } return true; } bool Widget3DWidget::eventFilter(QObject *obj, QEvent *ev) { if (obj == mQWidget) { switch (ev->type()) { case QEvent::Resize: { QResizeEvent *re = static_cast(ev); if (re->oldSize() != re->size()) { mMetaData[QStringLiteral("geometry")] = mQWidget->geometry(); mGeomDirty = true; startUpdateTimer(); } return false; } case QEvent::Paint: { if (!mIsPainting) { mTextureDirty = true; startUpdateTimer(); } return false; } case QEvent::Show: { mGeomDirty = true; mTextureDirty = true; updateTimeout(); return false; } case QEvent::Hide: { mTextureImage = QImage(); mBackTextureImage = QImage(); mUpdateTimer->stop(); Q_EMIT changed(QVector() << Widget3DModel::TextureRole << Widget3DModel::BackTextureRole); return false; } case QEvent::ParentChange: { // TODO: Handle ParentChange! } default: return false; } } return false; } void Widget3DWidget::startUpdateTimer() { if (mQWidget->isVisible() && !mUpdateTimer->isActive()) { mUpdateTimer->start(); } } void GammaRay::Widget3DWidget::updateTimeout() { QVector changedRoles; if (mGeomDirty && updateGeometry()) { changedRoles << Widget3DModel::GeometryRole; } if (mTextureDirty && updateTexture()) { changedRoles << Widget3DModel::TextureRole << Widget3DModel::BackTextureRole; } if (!changedRoles.isEmpty()) { Q_EMIT changed(changedRoles); } } bool Widget3DWidget::updateGeometry() { if (!mGeomDirty || !mQWidget) { return false; } QWidget *w = mQWidget; if (!w->isVisible()) { mGeomDirty = false; mTextureDirty = false; return false; } QPoint mappedPos(0, 0); // TODO: Use mapTo(), but it behaved somewhat weird... while (w->parentWidget()) { mappedPos += w->geometry().topLeft(); w = w->parentWidget(); } QRect textureGeometry(0, 0, mQWidget->width(), mQWidget->height()); QRect geometry(mappedPos, QSize(mQWidget->width(), mQWidget->height())); QRect parentGeom; if (parent()) { // Artificial clipping - don't shrink texture coordinates parentGeom = parentWidget()->geometry(); if (mGeometry.x() < parentGeom.x()) { mTextureGeometry.setRight(parentGeom.x() - mGeometry.x()); mGeometry.setRight(parentGeom.x()); } if (mGeometry.y() < parentGeom.y()) { mTextureGeometry.setTop(parentGeom.y() - mGeometry.y()); mGeometry.setTop(parentGeom.y()); } if (mGeometry.x() + mGeometry.width() > parentGeom.x() + parentGeom.width()) { mGeometry.setRight(parentGeom.x() + parentGeom.width()); mTextureGeometry.setWidth(mGeometry.width()); } if (mGeometry.y() + mGeometry.height() > parentGeom.y() + parentGeom.height()) { mGeometry.setBottom(parentGeom.y() + parentGeom.height()); mTextureGeometry.setHeight(mGeometry.height()); } } bool changed = false; if (textureGeometry != mTextureGeometry) { mTextureGeometry = textureGeometry; mTextureDirty = true; changed = true; } if (geometry != mGeometry) { mGeometry = geometry; changed = true; } mGeomDirty = false; return changed; } bool Widget3DWidget::updateTexture() { if (!mTextureDirty || !mQWidget) { return false; } if (!mQWidget->isVisible()) { mTextureDirty = false; return false; } mIsPainting = true; const QImage::Format format = QImage::Format_RGBA8888; mTextureImage = QImage(mTextureGeometry.size(), format); mTextureImage.fill(mQWidget->palette().button().color()); if (isWindow()) { mQWidget->render(&mTextureImage, QPoint(0, 0), QRegion(mTextureGeometry)); mBackTextureImage = QImage(mTextureGeometry.size(), format); mQWidget->render(&mBackTextureImage, QPoint(0, 0), QRegion(mTextureGeometry)); } else { mQWidget->render(&mTextureImage, QPoint(0, 0), QRegion(mTextureGeometry), QWidget::DrawWindowBackground); mBackTextureImage = mTextureImage; } mIsPainting = false; mTextureDirty = false; return true; } Widget3DModel::Widget3DModel(QObject *parent) : QSortFilterProxyModel(parent) { } Widget3DModel::~Widget3DModel() = default; QHash Widget3DModel::roleNames() const { auto roles = QSortFilterProxyModel::roleNames(); roles[IdRole] = "objectId"; roles[TextureRole] = "frontTexture"; roles[BackTextureRole] = "backTexture"; roles[IsWindowRole] = "isWindow"; roles[GeometryRole] = "geometry"; roles[MetaDataRole] = "metaData"; roles[DepthRole] = "depth"; return roles; } QVariant Widget3DModel::data(const QModelIndex &index, int role) const { if (index.column() == 0) { switch (role) { case IdRole: { auto w = widgetForIndex(index); return w ? w->id() : QString(); } case TextureRole: { auto w = widgetForIndex(index); return w ? w->texture() : QImage(); } case BackTextureRole: { auto w = widgetForIndex(index); return w ? w->backTexture() : QImage(); } case IsWindowRole: { auto w = widgetForIndex(index); return w ? w->isWindow() : false; } case GeometryRole: { auto w = widgetForIndex(index); return w ? w->geometry() : QRect(); } case MetaDataRole: { auto w = widgetForIndex(index); return w ? w->metaData() : QVariant(); } case DepthRole: { auto w = widgetForIndex(index); return w ? w->depth() : 0; } } } return QSortFilterProxyModel::data(index, role); } QMap Widget3DModel::itemData(const QModelIndex &index) const { QMap data = QSortFilterProxyModel::itemData(index); if (index.column() == 0) { auto w = widgetForIndex(index); Q_ASSERT(w); // see comment in data() data[ObjectModel::ObjectIdRole] = this->data(index, ObjectModel::ObjectIdRole); data[IdRole] = w->id(); data[TextureRole] = w->texture(); data[BackTextureRole] = w->backTexture(); data[IsWindowRole] = w->isWindow(); data[GeometryRole] = w->geometry(); data[MetaDataRole] = w->metaData(); data[DepthRole] = w->depth(); } return data; } Widget3DWidget *Widget3DModel::widgetForObject(QObject *obj, const QModelIndex &idx, bool createWhenMissing) const { Widget3DWidget *widget = mDataCache.value(obj, nullptr); if (!widget && createWhenMissing) { Widget3DWidget *parent = nullptr; if (obj->parent() && idx.parent().isValid()) { parent = widgetForObject(obj->parent(), idx.parent(), createWhenMissing); } widget = new Widget3DWidget(qobject_cast(obj), idx, parent); connect(widget, &Widget3DWidget::changed, this, &Widget3DModel::onWidgetChanged); connect(obj, &QObject::destroyed, this, &Widget3DModel::onWidgetDestroyed); mDataCache.insert(obj, widget); } return widget; } Widget3DWidget *Widget3DModel::widgetForIndex(const QModelIndex &idx, bool createWhenMissing) const { QObject *obj = this->QSortFilterProxyModel::data(idx, ObjectModel::ObjectRole).value(); Q_ASSERT(obj); // bug in model? Q_ASSERT(obj->isWidgetType()); // this should be already filtered out by filterAcceptsRow() return widgetForObject(obj, idx, createWhenMissing); } bool Widget3DModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const { const QModelIndex sourceIdx = sourceModel()->index(source_row, 0, source_parent); QObject *sourceObj = sourceModel()->data(sourceIdx, ObjectModel::ObjectRole).value(); return qobject_cast(sourceObj); } void Widget3DModel::onWidgetChanged(const QVector &roles) { const auto widget = qobject_cast(sender()); Q_ASSERT(widget); const QModelIndex idx = widget->modelIndex(); if (!idx.isValid()) { // ???? return; } Q_EMIT dataChanged(idx, idx, roles); } void Widget3DModel::onWidgetDestroyed(QObject *obj) { mDataCache.remove(obj); } gammaray-2.11.3/plugins/widgetinspector/widget3dmodel.h000066400000000000000000000104631413310443000231700ustar00rootroot00000000000000/* widget3dmodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Daniel Vrátil Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef WIDGET3DMODEL_H #define WIDGET3DMODEL_H #include #include #include #include #include #include #include #include namespace GammaRay { class Widget3DWidget : public QObject { Q_OBJECT public: Widget3DWidget(QWidget *qWidget, const QPersistentModelIndex &modelIndex, Widget3DWidget *parent); ~Widget3DWidget() override; inline QImage texture() const { return mTextureImage; } inline QImage backTexture() const { return mBackTextureImage; } inline QRect geometry() const { return mGeometry; } inline QWidget *qWidget() const { return mQWidget; } inline Widget3DWidget *parentWidget() const { return static_cast(parent()); } inline bool isVisible() const { return mQWidget->isVisible(); } inline QVariantMap metaData() const { return mMetaData; } inline bool isWindow() const; inline int depth() const { return mDepth; } inline QPersistentModelIndex modelIndex() const { return mModelIndex; } // QML does not handle 64bit integers, so use string instead, we only need // the value for comparison, we can convert back to quintptr in C++ inline QString id() const { QString str(8, QLatin1Char('0')); quint64 ptr = reinterpret_cast(mQWidget.data()); std::memcpy(str.data(), static_cast(&ptr), 8); return str; }; protected: bool eventFilter(QObject *obj, QEvent *ev) override; Q_SIGNALS: void changed(const QVector &roles); private Q_SLOTS: void updateTimeout(); bool updateTexture(); bool updateGeometry(); private: void startUpdateTimer(); private: QPersistentModelIndex mModelIndex; QPointer mQWidget; QImage mTextureImage; QImage mBackTextureImage; QRect mTextureGeometry; QRect mGeometry; QVariantMap mMetaData; QTimer *mUpdateTimer; int mDepth; bool mIsPainting; bool mGeomDirty; bool mTextureDirty; }; class Widget3DModel : public QSortFilterProxyModel { Q_OBJECT public: enum Roles { IdRole = ObjectModel::UserRole + 1, TextureRole, BackTextureRole, IsWindowRole, GeometryRole, MetaDataRole, DepthRole, UserRole }; explicit Widget3DModel(QObject *parent = nullptr); ~Widget3DModel() override; QHash roleNames() const override; QVariant data(const QModelIndex &index, int role) const override; QMap itemData(const QModelIndex &index) const override; protected: bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override; private Q_SLOTS: void onWidgetChanged(const QVector &roles); void onWidgetDestroyed(QObject *obj); private: Widget3DWidget *widgetForObject(QObject *obj, const QModelIndex &idx, bool createWhenMissing = true) const; Widget3DWidget *widgetForIndex(const QModelIndex &idx, bool createWhenMissing = true) const; // mutable because we populate it lazily from data() const mutable QHash mDataCache; }; } Q_DECLARE_METATYPE(GammaRay::Widget3DWidget *) #endif // WIDGET3DMODEL_H gammaray-2.11.3/plugins/widgetinspector/widget3dsubtreemodel.cpp000066400000000000000000000354411413310443000251200ustar00rootroot00000000000000/* widget3dsubtreemodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Daniel Vrátil Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "widget3dsubtreemodel.h" using namespace GammaRay; class Widget3DSubtreeModel::Node { public: explicit Node(const QModelIndex &idx) : sourceIdx(idx), parent(nullptr) {} int realChildrenCount() const { int count = 0; for (int i = 0, c = children.count(); i < c; ++i) { if (children[i]) { count += 1 + children[i]->realChildrenCount(); } } return count; } QPersistentModelIndex sourceIdx; Node *parent; QList children; }; Widget3DSubtreeModel::Widget3DSubtreeModel(QObject *parent) : QAbstractProxyModel(parent) { } Widget3DSubtreeModel::~Widget3DSubtreeModel() { qDeleteAll(mNodeList); mNodeList.clear(); mNodeLookup.clear(); } void Widget3DSubtreeModel::setSourceModel(QAbstractItemModel *newSource) { if (sourceModel()) { disconnect(sourceModel(), nullptr, this, nullptr); } QAbstractProxyModel::setSourceModel(newSource); connect(newSource, &QAbstractItemModel::rowsInserted, this, &Widget3DSubtreeModel::sourceRowsInserted); connect(newSource, &QAbstractItemModel::rowsAboutToBeRemoved, this, &Widget3DSubtreeModel::sourceRowsAboutToBeRemoved); connect(newSource, &QAbstractItemModel::dataChanged, this, &Widget3DSubtreeModel::sourceDataChanged); connect(newSource, &QAbstractItemModel::modelReset, this, &Widget3DSubtreeModel::sourceModelReset); connect(newSource, &QAbstractItemModel::layoutChanged, this, &Widget3DSubtreeModel::sourceLayoutChanged); resetModel(); } void Widget3DSubtreeModel::setRootObjectId(const QString &rootObject) { if (m_rootObject == rootObject) { return; } m_rootObject = rootObject; m_rootIndex = findIndexForObject(rootObject); Q_EMIT rootObjectIdChanged(); resetModel(); } QString Widget3DSubtreeModel::rootObjectId() const { return m_rootObject; } void Widget3DSubtreeModel::resetModel() { beginResetModel(); m_foreignWindows.clear(); qDeleteAll(mNodeList); mNodeList.clear(); mNodeLookup.clear(); if (!m_rootObject.isEmpty()) { populate(); } endResetModel(); } void Widget3DSubtreeModel::populate() { if (!sourceModel()) { return; } QVector toVisit = { m_rootIndex }; while (!toVisit.isEmpty()) { const auto index = toVisit.takeLast(); Node *parent = nullptr; if (index != m_rootIndex) { parent = mNodeLookup.value(index.parent().data(Widget3DModel::IdRole).toString()); } if (index.data(Widget3DModel::IdRole).toString().isEmpty()) { continue; } if (index.data(Widget3DModel::IsWindowRole).toBool()) { m_foreignWindows.insert(index); if (index != m_rootIndex) { if (parent) { parent->children.push_back(nullptr); } continue; } } auto *node = new Node(index); node->parent = parent; if (parent) { parent->children.push_back(node); } const int idx = parent ? mNodeList.indexOf(parent) + 1 : mNodeList.size(); Q_ASSERT(idx > (parent ? 0 : -1)); mNodeList.insert(idx, node); mNodeLookup.insert(index.data(Widget3DModel::IdRole).toString(), node); for (int i = 0, c = sourceModel()->rowCount(index); i < c; ++i) { const auto child = index.child(i, 0); Q_ASSERT(child.isValid()); toVisit.push_back(child); } } } QModelIndex Widget3DSubtreeModel::findIndexForObject(const QString &objectId) const { if (!sourceModel()) { return QModelIndex(); } QVector toVisit = { QModelIndex() }; while (!toVisit.isEmpty()) { const auto idx = toVisit.takeLast(); const QString v = sourceModel()->data(idx, Widget3DModel::IdRole).toString(); if (v == objectId) { return idx; } for (int i = 0, c = sourceModel()->rowCount(idx); i < c; ++i) { toVisit.push_back(sourceModel()->index(i, 0, idx)); } } return QModelIndex(); } bool Widget3DSubtreeModel::isParentOf(const QModelIndex &parent, const QModelIndex &child) const { QModelIndex c = child; while (c.isValid()) { if (c == parent) { return true; } if (m_foreignWindows.contains(c)) { return false; } if (c.data(Widget3DModel::IsWindowRole).toBool()) { m_foreignWindows.insert(c); return false; } c = c.parent(); } return false; } bool Widget3DSubtreeModel::belongsToModel(const QModelIndex &idx) const { if (!m_rootIndex.isValid()) { return true; } return isParentOf(m_rootIndex, idx); } int Widget3DSubtreeModel::sourceRowsInserted(const QModelIndex &sourceParent, int first, int last) { Q_ASSERT(sourceModel()); Q_ASSERT(!sourceParent.isValid() || !sourceParent.data(Widget3DModel::IdRole).toString().isEmpty()); Node *parentNode = sourceParent.isValid() ? mNodeLookup.value(sourceParent.data(Widget3DModel::IdRole).toString()) : nullptr; // Not for our current window if (sourceParent.isValid() && !parentNode) { return 0; } // Get index of parent const int parentIdx = parentNode ? mNodeList.indexOf(parentNode) : mNodeList.size() - 1; Q_ASSERT(parentIdx > (parentNode ? -1 : -2)); // Recursively count all descendants of "parent" between 0 and "first" int realSiblings = 0; Q_ASSERT(!parentNode || first <= parentNode->children.count()); for (int i = 0; parentNode && i < first; ++i) { if (Node *node = parentNode->children.at(i)) { realSiblings += 1 + node->realChildrenCount(); } } // This is the position where we insert the first item. It includes all // descendants of our "above" siblings. int insertIndex = parentIdx + realSiblings + 1; for (int i = first; i <= last; ++i) { const QModelIndex idx = sourceModel()->index(i, 0, sourceParent); Q_ASSERT(idx.isValid()); // Is window? Skip... if (idx.data(Widget3DModel::IsWindowRole).toBool()) { m_foreignWindows.insert(idx); if (parentNode) { parentNode->children.insert(i, nullptr); } continue; } const auto idRole = idx.data(Widget3DModel::IdRole).toString(); if (mNodeLookup.contains(idRole)) { // Could happen if populate() is "too fast" qWarning() << "Insert for object I already know!"; } else { Q_ASSERT(insertIndex > parentIdx); beginInsertRows(QModelIndex(), insertIndex, insertIndex); auto *node = new Node(idx); node->parent = parentNode; if (parentNode) { parentNode->children.insert(i, node); } mNodeList.insert(insertIndex, node); mNodeLookup.insert(idRole, node); endInsertRows(); ++insertIndex; } // Recursively check if the newly inserted row may have descendants. // When a subtree becomes visible we sometimes only get insert notification // for the root of the subtree, but not for the descendants const int childCnt = sourceModel()->rowCount(idx); if (childCnt) { insertIndex += sourceRowsInserted(idx, 0, childCnt - 1); } } if (parentNode) { Q_ASSERT(parentNode->children.count() == sourceModel()->rowCount(sourceParent)); } return insertIndex - parentIdx - realSiblings; } void GammaRay::Widget3DSubtreeModel::sourceRowsAboutToBeRemoved(const QModelIndex &parent, int first, int last) { Q_ASSERT(sourceModel()); // Check if a parent of our root index is being removed QModelIndex idx = m_rootIndex.parent(); while (idx.isValid()) { if (idx == parent) { resetModel(); return; } idx = idx.parent(); } // Did not happen within our subtree Node *parentNode = mNodeLookup.value(parent.data(Widget3DModel::IdRole).toString()); if (!parentNode) { return; } for (int i = last; i >= first; --i) { QModelIndex idx = parent.child(i, 0); // Find the node that we are removing and its position in mNodeList if (!parentNode->children.at(i)) { Q_ASSERT(!mNodeLookup.contains(idx.data(Widget3DModel::IdRole).toString())); parentNode->children.removeAt(i); continue; } const int beginPos = mNodeList.indexOf(parentNode->children.at(i)); Q_ASSERT(beginPos > -1); // TODO: Don't use indexes and has lookup, instead look at node->children.at(i + 1) // and check if it's null or not. If "i + 1" is out of bounds, than go up one // level etc. // Find idx's sibling, or its parent's sibling, or its parent's parent's // sibling etc. Everything between beginPos and the sibling are descendants // of the removed item and must be removed too. int endPos = -1; auto lookupIt = mNodeLookup.constEnd(); QModelIndex next = idx.sibling(idx.row() + 1, 0); while (lookupIt == mNodeLookup.constEnd() && idx.isValid()) { lookupIt = mNodeLookup.constFind(next.data(Widget3DModel::IdRole).toString()); next = idx.sibling(idx.row() + 1, 0); while (!next.isValid()) { const QModelIndex p = idx.parent(); if (!p.isValid()) { break; } next = p.sibling(p.row() + 1, 0); idx = p; } idx = next; } if (lookupIt == mNodeLookup.constEnd()) { endPos = mNodeList.size() - 1; } else { endPos = mNodeList.indexOf(*lookupIt) - 1; } Q_ASSERT(endPos >= beginPos); beginRemoveRows(QModelIndex(), beginPos, endPos); for (int j = endPos; j >= beginPos; --j) { auto node = mNodeList.takeAt(j); if (Node *p = node->parent) { p->children.removeOne(node); } // Expensive assert that makes sure that we never remove anything // that is not a descendant of "parent" Q_ASSERT([parent](QModelIndex si) { while (si.isValid()) { if (si == parent) { return si; } si = si.parent(); } return si; }(node->sourceIdx) == parent); mNodeLookup.remove(node->sourceIdx.data(Widget3DModel::IdRole).toString()); delete node; } endRemoveRows(); } // TODO: Maybe rowCount() is not reliable at this point? Maybe compare in // sourceRowsRemoved() Q_ASSERT(parentNode->children.count() == sourceModel()->rowCount(parent) - (last - first + 1)); } void Widget3DSubtreeModel::sourceDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) { Q_ASSERT(sourceModel()); for (int i = topLeft.row(); i < bottomRight.row(); ++i) { const QModelIndex idx = topLeft.sibling(i, 0); Node *node = mNodeLookup.value(idx.data(Widget3DModel::IdRole).toString()); if (!node) { continue; } const QModelIndex nodeIdx = indexForNode(node); Q_EMIT dataChanged(nodeIdx, nodeIdx); } } void Widget3DSubtreeModel::sourceLayoutChanged() { resetModel(); } void Widget3DSubtreeModel::sourceModelReset() { resetModel(); } int Widget3DSubtreeModel::columnCount(const QModelIndex &) const { return 1; } int Widget3DSubtreeModel::rowCount(const QModelIndex &parent) const { return parent.isValid() ? 0 : mNodeList.count(); } QModelIndex Widget3DSubtreeModel::index(int row, int column, const QModelIndex &parent) const { auto parentNode = static_cast(parent.internalPointer()); const int parentPos = parentNode ? mNodeList.indexOf(parentNode) : 0; Q_ASSERT(parentPos > -1); const int pos = parentPos + row; if (row < 0 || pos >= mNodeList.size() || column != 0) { return QModelIndex(); } return createIndex(pos, column, mNodeList.at(pos)); } QModelIndex Widget3DSubtreeModel::parent(const QModelIndex &) const { return QModelIndex(); // flat list } bool Widget3DSubtreeModel::hasChildren(const QModelIndex &parent) const { return !parent.isValid(); } QVariant Widget3DSubtreeModel::data(const QModelIndex &index, int role) const { Q_ASSERT(index.isValid()); auto node = static_cast(index.internalPointer()); Q_ASSERT(node); return node->sourceIdx.data(role); } QModelIndex Widget3DSubtreeModel::mapFromSource(const QModelIndex &sourceIndex) const { const auto node = mNodeLookup.value(sourceIndex.data(Widget3DModel::IdRole).toString()); if (!node) { return QModelIndex(); } return indexForNode(node); } QModelIndex Widget3DSubtreeModel::mapToSource(const QModelIndex &proxyIndex) const { if (!proxyIndex.isValid()) { return QModelIndex(); } auto node = static_cast(proxyIndex.internalPointer()); Q_ASSERT(node); return node->sourceIdx; } QModelIndex Widget3DSubtreeModel::indexForNode(Node *node) const { const int pos = mNodeList.indexOf(node); Q_ASSERT(pos > -1); return createIndex(pos, 0, node); } ObjectId Widget3DSubtreeModel::realObjectId(const QString &objectId) const { Node *node = mNodeLookup[objectId]; if (!node) { return ObjectId(); } return node->sourceIdx.data(ObjectModel::ObjectIdRole).value(); } gammaray-2.11.3/plugins/widgetinspector/widget3dsubtreemodel.h000066400000000000000000000063531413310443000245650ustar00rootroot00000000000000/* widget3dsubtreemodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Daniel Vrátil Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef WIDGET3DSUBTREEMODEL_H #define WIDGET3DSUBTREEMODEL_H #include #include #include "widget3dmodel.h" #include namespace GammaRay { class Widget3DSubtreeModel : public QAbstractProxyModel { Q_OBJECT Q_PROPERTY(QString rootObjectId READ rootObjectId WRITE setRootObjectId NOTIFY rootObjectIdChanged) public: explicit Widget3DSubtreeModel(QObject *parent = nullptr); ~Widget3DSubtreeModel(); void setSourceModel(QAbstractItemModel *newSource) override; void setRootObjectId(const QString &rootObject); QString rootObjectId() const; int rowCount(const QModelIndex &parent) const override; int columnCount(const QModelIndex &parent) const override; QModelIndex index(int row, int column, const QModelIndex &parent) const override; QModelIndex parent(const QModelIndex &child) const override; bool hasChildren(const QModelIndex &parent) const override; QVariant data(const QModelIndex &index, int role) const override; QModelIndex mapFromSource(const QModelIndex &sourceIndex) const override; QModelIndex mapToSource(const QModelIndex &proxyIndex) const override; ObjectId realObjectId(const QString &objectId) const; Q_SIGNALS: void rootObjectIdChanged(); private Q_SLOTS: int sourceRowsInserted(const QModelIndex &sourceParent, int first, int last); void sourceRowsAboutToBeRemoved(const QModelIndex &parent, int first, int last); void sourceDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight); void sourceModelReset(); void sourceLayoutChanged(); private: class Node; QModelIndex indexForNode(Node *node) const; void populate(); void resetModel(); bool belongsToModel(const QModelIndex &idx) const; bool isParentOf(const QModelIndex &parent, const QModelIndex &child) const; QModelIndex findIndexForObject(const QString &objectId) const; QString m_rootObject; QModelIndex m_rootIndex; QList mNodeList; QHash mNodeLookup; mutable QSet m_foreignWindows; }; } #endif gammaray-2.11.3/plugins/widgetinspector/widget3dview.cpp000066400000000000000000000240521413310443000233740ustar00rootroot00000000000000/* widget3dview.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Daniel Vrátil Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "widget3dview.h" #include "widget3dmodel.h" #include "widget3dimagetextureimage.h" #include "widget3dwindowmodel.h" #include "widget3dsubtreemodel.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace GammaRay { class Widget3DWindow : public QQuickView { Q_OBJECT public: explicit Widget3DWindow(QWindow *parent = nullptr) : QQuickView(parent) { resize(800, 600); setResizeMode(QQuickView::SizeRootObjectToView); } ~Widget3DWindow() { } Q_SIGNALS: void wheel(float delta); protected: void wheelEvent(QWheelEvent *ev) override { Q_EMIT wheel(ev->delta()); } }; class Widget3DClientModel : public QSortFilterProxyModel { Q_OBJECT public: explicit Widget3DClientModel(QObject *parent = nullptr) : QSortFilterProxyModel(parent) { } // Map the server-side role names for easy use from QML QHash roleNames() const override { QHash roles = QSortFilterProxyModel::roleNames(); roles[Widget3DModel::IdRole] = "objectId"; roles[Widget3DModel::GeometryRole] = "geometry"; roles[Widget3DModel::TextureRole] = "frontTexture"; roles[Widget3DModel::BackTextureRole] = "backTexture"; roles[Widget3DModel::IsWindowRole] = "isWindow"; roles[Widget3DModel::MetaDataRole] = "metaData"; roles[Widget3DModel::DepthRole] = "depth"; return roles; } ~Widget3DClientModel() = default; bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override { // Filter out rows that we don't have additional roles for yet (since data // are retrieved lazily by the RemoteModel). This greatly simplifies all // the other models (and the QML code), since we don't have to care // about null data everywhere. const QModelIndex source_idx = sourceModel()->index(source_row, 0, source_parent); if (source_idx.data(Widget3DModel::IdRole).isNull()) { // HACK: RemoteModel will reset the "Loading" flag of any node after calling // endInsertRows(). This method is invoked from endInsertRows() so even // though we accessed data() above, no data will be retrieved (well, they // will be retrieved, but then discarded upon retrieval, because the // corresponding Node does not have "Loading" flag set). So we do the sneaky // trick with a timer to force RemoteModel to fetch the data from the server, // because we really want them. QTimer::singleShot(0, [source_idx]() { source_idx.data(Widget3DModel::IdRole); }); return false; } // Filter out rows that don't have a (valid) texture. This basically // filters out all invisible widgets, which we don't want to render and // deal with in the models. if (source_idx.data(Widget3DModel::TextureRole).value().isNull() || source_idx.data(Widget3DModel::GeometryRole).isNull()) { return false; } // We accept this row, now force-populate its subtree sourceModel()->index(0, 0, source_idx); return true; } }; class Widget3DSelectionHelper : public QObject { Q_OBJECT Q_PROPERTY(QString currentObject READ currentObject WRITE setCurrentObject NOTIFY currentObjectChanged) public: explicit Widget3DSelectionHelper(Widget3DSubtreeModel *widgetModel, QObject *parent = nullptr) : QObject(parent) , mModel(widgetModel) {} QString currentObject() const { return mCurrentObject; } void setCurrentObject(const QString ¤tObject) { if (mCurrentObject != currentObject) { mCurrentObject = currentObject; mObjectId = ObjectId(); Q_EMIT currentObjectChanged(); } } ObjectId currentObjectId() const { if (mObjectId.isNull()) { const_cast(this)->mObjectId = mModel->realObjectId(mCurrentObject); } return mObjectId; } Q_SIGNALS: void currentObjectChanged(); private: QString mCurrentObject; ObjectId mObjectId; Widget3DSubtreeModel *mModel; }; } Q_DECLARE_METATYPE(GammaRay::Widget3DWindow*) Q_DECLARE_METATYPE(GammaRay::Widget3DSelectionHelper*) using namespace GammaRay; Widget3DView::Widget3DView(QWidget* parent) : QWidget(parent) { auto remoteModel = ObjectBroker::model(QStringLiteral("com.kdab.GammaRay.Widget3DModel")); // Filter out rows without data and invisible widgets and expose C++ roles // to QML. auto clientModel = new Widget3DClientModel(this); clientModel->setSourceModel(remoteModel); // WINDOWS // Filter out everything except for window-type widgets auto windowModel = new Widget3DWindowModel(this); windowModel->setSourceModel(clientModel); // WIDGETS // Filter out widget subtrees that don't belong to currently selected window // and flatten the tree auto widgetModel = new Widget3DSubtreeModel(this); widgetModel->setSourceModel(clientModel); mSelectionHelper = new Widget3DSelectionHelper(widgetModel, this); auto vbox = new QVBoxLayout(this); auto hbox = new QHBoxLayout(); hbox->addWidget(new QLabel(tr("Window: "))); auto *combo = new QComboBox; combo->setModel(windowModel); hbox->addWidget(combo, 1); vbox->addLayout(hbox); hbox = new QHBoxLayout(); vbox->addLayout(hbox); #if 0 // widget model debugging QTreeView *tv = new QTreeView; tv->setModel(widgetModel); hbox->addWidget(tv); #endif mRenderWindow = new Widget3DWindow(); mRenderWindow->installEventFilter(this); hbox->addWidget(QWidget::createWindowContainer(mRenderWindow, this), 1); qmlRegisterType("com.kdab.GammaRay", 1, 0, "Widget3DImageTextureImage"); auto engine = mRenderWindow->engine(); engine->rootContext()->setContextProperty(QStringLiteral("_renderWindow"), mRenderWindow); engine->rootContext()->setContextProperty(QStringLiteral("_widgetModel"), widgetModel); engine->rootContext()->setContextProperty(QStringLiteral("_selectionHelper"), mSelectionHelper); mRenderWindow->setSource(QUrl(QStringLiteral("qrc:/gammaray/assets/qml/main.qml"))); connect(combo, static_cast(&QComboBox::currentIndexChanged), this, [widgetModel, combo, this]() { widgetModel->setRootObjectId(combo->currentData(Widget3DModel::IdRole).toString()); QMetaObject::invokeMethod(mRenderWindow->rootObject(), "resetView"); }); } Widget3DView::~Widget3DView() { delete mRenderWindow; } bool Widget3DView::eventFilter(QObject *o, QEvent *e) { if (o == mRenderWindow) { if (e->type() == QEvent::MouseButtonDblClick) { selectCurrentObject(); } else if (e->type() == QEvent::MouseButtonPress) { // Widget3DWindow is not a QWidget, so it does not handle closing // popups when clicked if (auto p = QApplication::activePopupWidget()) { p->close(); } const QMouseEvent *me = static_cast(e); if (me->button() == Qt::RightButton) { mLastRightClick = me->globalPos(); } } else if (e->type() == QEvent::MouseButtonRelease) { const QMouseEvent *me = static_cast(e); if (me->button() == Qt::RightButton) { if (me->globalPos() == mLastRightClick) { showContextMenu(me->globalPos()); } mLastRightClick = QPoint(); } } } return false; } void GammaRay::Widget3DView::showContextMenu(const QPoint &pos) { const auto objectId = mSelectionHelper->currentObjectId(); if (objectId.isNull()) { return; } QMenu menu(tr("Widget @ %1").arg(QLatin1String("0x") + QString::number(objectId.id(), 16))); ContextMenuExtension ext(objectId); ext.populateMenu(&menu); menu.exec(pos); } void GammaRay::Widget3DView::selectCurrentObject() { const auto objectId = mSelectionHelper->currentObjectId(); if (objectId.isNull()) { return; } // TODO: How to select the ObjectId in the PropertyWidget? } #include "widget3dview.moc" gammaray-2.11.3/plugins/widgetinspector/widget3dview.h000066400000000000000000000032611413310443000230400ustar00rootroot00000000000000/* widget3dview.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Daniel Vrátil Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef WIDGET3DVIEW_H #define WIDGET3DVIEW_H #include namespace GammaRay { class Widget3DWindow; class Widget3DSelectionHelper; class Widget3DView : public QWidget { Q_OBJECT public: explicit Widget3DView(QWidget *parent = nullptr); ~Widget3DView(); protected: bool eventFilter(QObject *o, QEvent *e) override; private: void showContextMenu(const QPoint &pos); void selectCurrentObject(); Widget3DWindow *mRenderWindow; Widget3DSelectionHelper *mSelectionHelper; QPoint mLastRightClick; }; } #endif // WIDGET3DVIEW_H gammaray-2.11.3/plugins/widgetinspector/widget3dwindowmodel.cpp000066400000000000000000000123371413310443000247550ustar00rootroot00000000000000/* widget3dview.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Daniel Vrátil Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "widget3dwindowmodel.h" #include "widget3dmodel.h" #include namespace GammaRay { class Widget3DWindowModel::WindowNode { public: explicit WindowNode(const QModelIndex &idx) : sourceIdx(idx) {} QPersistentModelIndex sourceIdx; }; } using namespace GammaRay; Widget3DWindowModel::Widget3DWindowModel(QObject *parent) : QAbstractProxyModel(parent) { } Widget3DWindowModel::~Widget3DWindowModel() { } void Widget3DWindowModel::setSourceModel(QAbstractItemModel *sourceModel_) { if (sourceModel()) { disconnect(sourceModel(), nullptr, this, nullptr); } QAbstractProxyModel::setSourceModel(sourceModel_); connect(sourceModel_, &QAbstractItemModel::rowsInserted, this, &Widget3DWindowModel::sourceModelRowsInserted); connect(sourceModel_, &QAbstractItemModel::rowsRemoved, this, &Widget3DWindowModel::sourceModelRowsRemoved); connect(sourceModel_, &QAbstractItemModel::modelReset, this, &Widget3DWindowModel::sourceModelReset); beginResetModel(); qDeleteAll(mNodes); mNodes.clear(); populate(); endResetModel(); } QModelIndex Widget3DWindowModel::index(int row, int column, const QModelIndex &parent) const { if (parent.isValid()) { return QModelIndex(); } if (row < 0 || row >= mNodes.count() || column != 0) { return QModelIndex(); } return createIndex(row, column, mNodes.at(row)); } QModelIndex Widget3DWindowModel::parent(const QModelIndex &) const { return QModelIndex(); // list } int Widget3DWindowModel::rowCount(const QModelIndex &parent) const { return parent.isValid() ? 0 : mNodes.count(); } int Widget3DWindowModel::columnCount(const QModelIndex &) const { return 1; } QModelIndex Widget3DWindowModel::indexForNode(WindowNode *node) const { int row = mNodes.indexOf(node); Q_ASSERT(row > -1); return createIndex(row, 0, node); } QModelIndex Widget3DWindowModel::mapFromSource(const QModelIndex &sourceIndex) const { for (WindowNode *node : mNodes) { if (node->sourceIdx == sourceIndex) { return indexForNode(node); } } return QModelIndex(); } QModelIndex Widget3DWindowModel::mapToSource(const QModelIndex &proxyIndex) const { if (!proxyIndex.isValid()) { return QModelIndex(); } auto node = static_cast(proxyIndex.internalPointer()); if (!node) { return QModelIndex(); } return node->sourceIdx; } void Widget3DWindowModel::sourceModelRowsInserted(const QModelIndex &parent, int first, int last) { for (int i = first; i <= last; ++i) { const QModelIndex srcIdx = sourceModel()->index(i, 0, parent); if (srcIdx.data(Widget3DModel::IsWindowRole).toBool()) { const int pos = mNodes.size(); beginInsertRows(QModelIndex(), pos, pos); mNodes.push_back(new WindowNode(srcIdx)); endInsertRows(); } } } void Widget3DWindowModel::sourceModelRowsRemoved() { // Iterate over all windows we know and check if any of the indexes has // become invalid. We won't ever have that many windows for this to become // too slow and we don't have to have a complex code to traverse the entire // removed subtree just to check it happened to contain one of our windows. for (int i = mNodes.size() - 1; i >= 0; --i) { if (!mNodes.at(i)->sourceIdx.isValid()) { beginRemoveRows(QModelIndex(), i, i); delete mNodes.takeAt(i); endRemoveRows(); } } } void Widget3DWindowModel::sourceModelReset() { beginResetModel(); qDeleteAll(mNodes); mNodes.clear(); populate(); endResetModel(); } void Widget3DWindowModel::populate() { QVector toVisit = { QModelIndex() }; while (!toVisit.isEmpty()) { const auto idx = toVisit.takeFirst(); if (idx.data(Widget3DModel::IsWindowRole).toBool()) { mNodes.push_back(new WindowNode(idx)); } for (int i = 0, cnt = sourceModel()->rowCount(idx); i < cnt; ++i) { toVisit.push_front(sourceModel()->index(i, 0, idx)); } } } gammaray-2.11.3/plugins/widgetinspector/widget3dwindowmodel.h000066400000000000000000000043761413310443000244260ustar00rootroot00000000000000/* widget3dview.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Daniel Vrátil Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef WIDGET3DWINDOWMODEL_H #define WIDGET3DWINDOWMODEL_H #include #include #include "widget3dmodel.h" namespace GammaRay { class Widget3DWindowModel : public QAbstractProxyModel { Q_OBJECT public: explicit Widget3DWindowModel(QObject *parent = nullptr); ~Widget3DWindowModel(); void setSourceModel(QAbstractItemModel *sourceModel_) override; int rowCount(const QModelIndex &parent) const override; int columnCount(const QModelIndex &parent) const override; QModelIndex index(int row, int column, const QModelIndex & parent) const override; QModelIndex parent(const QModelIndex &child) const override; QModelIndex mapFromSource(const QModelIndex &sourceIndex) const override; QModelIndex mapToSource(const QModelIndex &proxyIndex) const override; private Q_SLOTS: void sourceModelRowsInserted(const QModelIndex &parent, int first, int last); void sourceModelRowsRemoved(); void sourceModelReset(); private: class WindowNode; void populate(); QModelIndex indexForNode(WindowNode *node) const; QVector mNodes; }; } #endif gammaray-2.11.3/plugins/widgetinspector/widgetclientmodel.cpp000066400000000000000000000035401413310443000244710ustar00rootroot00000000000000/* widgetclientmodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "widgetclientmodel.h" #include "widgetmodelroles.h" #include #include using namespace GammaRay; WidgetClientModel::WidgetClientModel(QObject *parent) : ClientDecorationIdentityProxyModel(parent) { } WidgetClientModel::~WidgetClientModel() = default; QVariant WidgetClientModel::data(const QModelIndex &index, int role) const { if (index.isValid() && role == Qt::ForegroundRole) { int flags = ClientDecorationIdentityProxyModel::data(index, WidgetModelRoles::WidgetFlags).value(); if (flags & WidgetModelRoles::Invisible) return qApp->palette().color(QPalette::Disabled, QPalette::Text); } return ClientDecorationIdentityProxyModel::data(index, role); } gammaray-2.11.3/plugins/widgetinspector/widgetclientmodel.h000066400000000000000000000033021413310443000241320ustar00rootroot00000000000000/* widgetclientmodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_WIDGETINSPECTOR_WIDGETCLIENTMODEL_H #define GAMMARAY_WIDGETINSPECTOR_WIDGETCLIENTMODEL_H #include namespace GammaRay { /** UI-dependent (and thus client-side) bits of the widget tree model. */ class WidgetClientModel : public ClientDecorationIdentityProxyModel { Q_OBJECT public: explicit WidgetClientModel(QObject *parent = nullptr); ~WidgetClientModel() override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; }; } #endif // GAMMARAY_WIDGETINSPECTOR_WIDGETCLIENTMODEL_H gammaray-2.11.3/plugins/widgetinspector/widgetinspector.cpp000066400000000000000000000023041413310443000241750ustar00rootroot00000000000000/* widgetinspector.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "widgetinspector.h" #include using namespace GammaRay; gammaray-2.11.3/plugins/widgetinspector/widgetinspector.h000066400000000000000000000033451413310443000236500ustar00rootroot00000000000000/* widgetinspector.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_WIDGETINSPECTOR_WIDGETINSPECTOR_H #define GAMMARAY_WIDGETINSPECTOR_WIDGETINSPECTOR_H #include "widgetinspectorserver.h" #include #include namespace GammaRay { class WidgetInspectorFactory : public QObject, public StandardToolFactory { Q_OBJECT Q_INTERFACES(GammaRay::ToolFactory) Q_PLUGIN_METADATA(IID "com.kdab.GammaRay.ToolFactory" FILE "gammaray_widgetinspector.json") public: explicit WidgetInspectorFactory(QObject *parent = nullptr) : QObject(parent) { } }; } #endif // GAMMARAY_WIDGETINSPECTOR_H gammaray-2.11.3/plugins/widgetinspector/widgetinspector_export_actions.cpp000066400000000000000000000050211413310443000273150ustar00rootroot00000000000000/* widgetinspector_export_actions.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ /** * @file widgetinspector_export_actions.cpp * dlopen hack to avoid dependencies on QtSvg and QtDesigner in the main probe. */ #include #include "uiextractor.h" #ifdef HAVE_QT_SVG #include #endif #ifdef HAVE_QT_PRINTSUPPORT #include #endif #include #include #include extern "C" { #ifdef HAVE_QT_SVG Q_DECL_EXPORT void gammaray_save_widget_to_svg(QWidget *widget, const QString &fileName) { QSvgGenerator svg; svg.setFileName(fileName); svg.setSize(widget->size()); svg.setViewBox(QRect(QPoint(0, 0), widget->size())); QPainter painter(&svg); widget->render(&painter); painter.end(); } #endif #ifdef HAVE_QT_DESIGNER Q_DECL_EXPORT void gammaray_save_widget_to_ui(QWidget *widget, const QString &fileName) { QFile file(fileName); if (file.open(QFile::WriteOnly)) { GammaRay::UiExtractor formBuilder; formBuilder.save(&file, widget); } } #endif #ifdef HAVE_QT_PRINTSUPPORT Q_DECL_EXPORT void gammaray_save_widget_to_pdf(QWidget *widget, const QString &fileName) { QPrinter printer(QPrinter::ScreenResolution); printer.setOutputFileName(fileName); printer.setOutputFormat(QPrinter::PdfFormat); printer.setPageMargins(0, 0, 0, 0, QPrinter::DevicePixel); printer.setPaperSize(widget->size(), QPrinter::DevicePixel); widget->render(&printer); } #endif } gammaray-2.11.3/plugins/widgetinspector/widgetinspectorclient.cpp000066400000000000000000000035761413310443000254100ustar00rootroot00000000000000/* * widgetinspectorclient.cpp * * This file is part of GammaRay, the Qt application inspection and * manipulation tool. * * Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com * Author: Milian Wolff * * Licensees holding valid commercial KDAB GammaRay licenses may use this file in * accordance with GammaRay Commercial License Agreement provided with the Software. * * Contact info@kdab.com if any conditions of this licensing are not clear to you. * * 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, see . * */ #include "widgetinspectorclient.h" #include using namespace GammaRay; WidgetInspectorClient::WidgetInspectorClient(QObject *parent) : WidgetInspectorInterface(parent) { } WidgetInspectorClient::~WidgetInspectorClient() = default; #define WRAP_REMOTE(func, type) \ void WidgetInspectorClient::func(type arg) \ { \ Endpoint::instance()->invokeObject(objectName(), #func, QVariantList() << arg); \ } WRAP_REMOTE(saveAsImage, const QString &) WRAP_REMOTE(saveAsPdf, const QString &) WRAP_REMOTE(saveAsSvg, const QString &) WRAP_REMOTE(saveAsUiFile, const QString &) void WidgetInspectorClient::analyzePainting() { Endpoint::instance()->invokeObject(objectName(), "analyzePainting"); } gammaray-2.11.3/plugins/widgetinspector/widgetinspectorclient.h000066400000000000000000000035601413310443000250460ustar00rootroot00000000000000/* * widgetinspectorclient.h * * This file is part of GammaRay, the Qt application inspection and * manipulation tool. * * Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com * Author: Milian Wolff * * Licensees holding valid commercial KDAB GammaRay licenses may use this file in * accordance with GammaRay Commercial License Agreement provided with the Software. * * Contact info@kdab.com if any conditions of this licensing are not clear to you. * * 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, see . * */ #ifndef GAMMARAY_WIDGETINSPECTOR_WIDGETINSPECTORCLIENT_H #define GAMMARAY_WIDGETINSPECTOR_WIDGETINSPECTORCLIENT_H #include namespace GammaRay { class WidgetInspectorClient : public WidgetInspectorInterface { Q_OBJECT Q_INTERFACES(GammaRay::WidgetInspectorInterface) public: explicit WidgetInspectorClient(QObject *parent); ~WidgetInspectorClient() override; private: void saveAsImage(const QString &fileName) override; void saveAsSvg(const QString &fileName) override; void saveAsPdf(const QString &fileName) override; void saveAsUiFile(const QString &fileName) override; void analyzePainting() override; }; } #endif // GAMMARAY_WIDGETINSPECTORCLIENT_H gammaray-2.11.3/plugins/widgetinspector/widgetinspectorinterface.cpp000066400000000000000000000046721413310443000260700ustar00rootroot00000000000000/* widgetinspectorinterface.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "widgetinspectorinterface.h" #include #include #include namespace GammaRay { QDataStream &operator<<(QDataStream &out, WidgetInspectorInterface::Features value) { out << qint32(value); return out; } QDataStream &operator>>(QDataStream &in, WidgetInspectorInterface::Features &value) { qint32 t; in >> t; value = static_cast(t); return in; } WidgetInspectorInterface::WidgetInspectorInterface(QObject *parent) : QObject(parent) { qRegisterMetaTypeStreamOperators(); qRegisterMetaTypeStreamOperators(); ObjectBroker::registerObject(this); } WidgetInspectorInterface::~WidgetInspectorInterface() = default; WidgetInspectorInterface::Features WidgetInspectorInterface::features() const { return m_features; } void WidgetInspectorInterface::setFeatures(WidgetInspectorInterface::Features features) { if (features == m_features) return; m_features = features; emit featuresChanged(); } QDataStream& operator<<(QDataStream &out, const WidgetFrameData &data) { out << data.tabFocusRects; return out; } QDataStream& operator>>(QDataStream& in, WidgetFrameData &data) { in >> data.tabFocusRects; return in; } } gammaray-2.11.3/plugins/widgetinspector/widgetinspectorinterface.h000066400000000000000000000056471413310443000255400ustar00rootroot00000000000000/* widgetinspectorinterface.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_WIDGETINSPECTOR_WIDGETINSPECTORINTERFACE_H #define GAMMARAY_WIDGETINSPECTOR_WIDGETINSPECTORINTERFACE_H #include #include #include #include #include QT_BEGIN_NAMESPACE class QImage; QT_END_NAMESPACE namespace GammaRay { class WidgetInspectorInterface : public QObject { Q_OBJECT Q_PROPERTY( GammaRay::WidgetInspectorInterface::Features features READ features WRITE setFeatures NOTIFY featuresChanged) public: enum Feature { NoFeature = 0, InputRedirection = 1, AnalyzePainting = 2, SvgExport = 4, PdfExport = 8, UiExport = 16 }; Q_DECLARE_FLAGS(Features, Feature) explicit WidgetInspectorInterface(QObject *parent = nullptr); ~WidgetInspectorInterface() override; Features features() const; void setFeatures(Features features); public slots: virtual void saveAsImage(const QString &fileName) = 0; virtual void saveAsSvg(const QString &fileName) = 0; virtual void saveAsPdf(const QString &fileName) = 0; virtual void saveAsUiFile(const QString &fileName) = 0; virtual void analyzePainting() = 0; signals: void featuresChanged(); private: Features m_features; }; class WidgetFrameData { public: QVector tabFocusRects; }; QDataStream &operator<<(QDataStream &out, const WidgetFrameData &data); QDataStream &operator>>(QDataStream &in, WidgetFrameData &data); } Q_DECLARE_METATYPE(GammaRay::WidgetInspectorInterface::Features) Q_DECLARE_METATYPE(GammaRay::WidgetFrameData) Q_DECLARE_OPERATORS_FOR_FLAGS(GammaRay::WidgetInspectorInterface::Features) QT_BEGIN_NAMESPACE Q_DECLARE_INTERFACE(GammaRay::WidgetInspectorInterface, "com.kdab.GammaRay.WidgetInspector") QT_END_NAMESPACE #endif // GAMMARAY_WIDGETINSPECTORINTERFACE_H gammaray-2.11.3/plugins/widgetinspector/widgetinspectorserver.cpp000066400000000000000000000600761413310443000254360ustar00rootroot00000000000000/* widgetinspectorserver.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include "widgetinspectorserver.h" #include "widgetpaintanalyzerextension.h" #include "waextension/widgetattributeextension.h" #include "overlaywidget.h" #include "widgettreemodel.h" #include "widget3dmodel.h" #include "core/propertycontroller.h" #include "core/metaobject.h" #include "core/metaobjectrepository.h" #include "core/varianthandler.h" #include "core/probesettings.h" #include "core/objecttypefilterproxymodel.h" #include #include "core/probeguard.h" #include #include #include #include "common/objectbroker.h" #include "common/settempvalue.h" #include "common/metatypedeclarations.h" #include "common/modelutils.h" #include "common/objectmodel.h" #include "common/paths.h" #include #include #include <3rdparty/kde/krecursivefilterproxymodel.h> #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include Q_DECLARE_METATYPE(const QStyle*) Q_DECLARE_METATYPE(QSizePolicy::ControlType) Q_DECLARE_METATYPE(QSizePolicy::ControlTypes) using namespace GammaRay; using namespace std; static bool isGoodCandidateWidget(QWidget *widget) { if (!widget->isVisible() || widget->testAttribute(Qt::WA_NoSystemBackground) || widget->metaObject() == &QWidget::staticMetaObject) { return false; } return true; } WidgetInspectorServer::WidgetInspectorServer(Probe *probe, QObject *parent) : WidgetInspectorInterface(parent) , m_externalExportActions(new QLibrary(this)) , m_propertyController(new PropertyController(objectName(), this)) , m_paintAnalyzer(new PaintAnalyzer(QStringLiteral("com.kdab.GammaRay.WidgetPaintAnalyzer"), this)) , m_remoteView(new RemoteViewServer(QStringLiteral("com.kdab.GammaRay.WidgetRemoteView"), this)) , m_probe(probe) { registerWidgetMetaTypes(); registerVariantHandlers(); probe->installGlobalEventFilter(this); PropertyController::registerExtension(); PropertyController::registerExtension(); connect(m_remoteView, &RemoteViewServer::requestUpdate, this, &WidgetInspectorServer::updateWidgetPreview); recreateOverlayWidget(); auto *widgetFilterProxy = new WidgetTreeModel(this); widgetFilterProxy->setSourceModel(probe->objectTreeModel()); auto widgetSearchProxy = new ServerProxyModel(this); widgetSearchProxy->setSourceModel(widgetFilterProxy); widgetSearchProxy->addRole(ObjectModel::ObjectIdRole); probe->registerModel(QStringLiteral("com.kdab.GammaRay.WidgetTree"), widgetSearchProxy); auto widget3dmodel = new Widget3DModel(this); widget3dmodel->setSourceModel(m_probe->objectTreeModel()); probe->registerModel(QStringLiteral("com.kdab.GammaRay.Widget3DModel"), widget3dmodel); m_widgetSelectionModel = ObjectBroker::selectionModel(widgetSearchProxy); connect(m_widgetSelectionModel, &QItemSelectionModel::selectionChanged, this, &WidgetInspectorServer::widgetSelectionChanged); if (m_probe->needsObjectDiscovery()) { connect(m_probe, &Probe::objectCreated, this, &WidgetInspectorServer::objectCreated); discoverObjects(); } connect(probe, &Probe::objectSelected, this, &WidgetInspectorServer::objectSelected); connect(m_remoteView, &RemoteViewServer::elementsAtRequested, this, &WidgetInspectorServer::requestElementsAt); connect(this, &WidgetInspectorServer::elementsAtReceived, m_remoteView, &RemoteViewInterface::elementsAtReceived); connect(m_remoteView, &RemoteViewServer::doPickElementId, this, &WidgetInspectorServer::pickElementId); checkFeatures(); } WidgetInspectorServer::~WidgetInspectorServer() { disconnect(m_overlayWidget.data(), &QObject::destroyed, this, &WidgetInspectorServer::recreateOverlayWidget); delete m_overlayWidget.data(); } void WidgetInspectorServer::widgetSelectionChanged(const QItemSelection &selection) { ProbeGuard guard; if (selection.isEmpty()) { m_propertyController->setObject(nullptr); return; } const QModelIndex index = selection.first().topLeft(); QLayout *layout = nullptr; QWidget *widget = nullptr; if (index.isValid()) { QObject *obj = index.data(ObjectModel::ObjectRole).value(); m_propertyController->setObject(obj); widget = qobject_cast(obj); layout = qobject_cast(obj); if (!widget && layout) widget = layout->parentWidget(); } else { m_propertyController->setObject(nullptr); } if (m_selectedWidget == widget && !layout) return; if (!m_selectedWidget || !widget || m_selectedWidget->window() != widget->window()) m_remoteView->resetView(); m_selectedWidget = widget; m_remoteView->setEventReceiver(m_selectedWidget ? m_selectedWidget->window()->windowHandle() : nullptr); if (m_selectedWidget && (qobject_cast(m_selectedWidget) || m_selectedWidget->inherits("QDesktopScreenWidget"))) { m_overlayWidget->placeOn(WidgetOrLayoutFacade()); return; } if (m_selectedWidget == m_overlayWidget) { // this should not happen, but apparently our object recovery is slightly too good sometimes ;) return; } if (layout) m_overlayWidget->placeOn(layout); else m_overlayWidget->placeOn(widget); if (!m_selectedWidget) return; updateWidgetPreview(); } bool WidgetInspectorServer::eventFilter(QObject *object, QEvent *event) { if (object == m_selectedWidget && event->type() == QEvent::Paint) m_remoteView->sourceChanged(); // make modal dialogs non-modal so that the gammaray window is still reachable // TODO: should only be done in in-process mode if (event->type() == QEvent::Show) { QDialog *dlg = qobject_cast(object); if (dlg) dlg->setWindowModality(Qt::NonModal); } if (event->type() == QEvent::MouseButtonRelease) { QMouseEvent *mouseEv = static_cast(event); if (mouseEv->button() == Qt::LeftButton && mouseEv->modifiers() == (Qt::ControlModifier | Qt::ShiftModifier)) { QWidget *widget = QApplication::widgetAt(mouseEv->globalPos()); if (widget) { // also select the corresponding model if a view was selected if (QAbstractItemView *view = Util::findParentOfType(object)) m_probe->selectObject(view->selectionModel()); else if (QComboBox *box = Util::findParentOfType(object)) m_probe->selectObject(box->model()); else if (auto toolButton = qobject_cast(object)) { if (toolButton->defaultAction()) m_probe->selectObject(toolButton->defaultAction()); } m_probe->selectObject(widget, widget->mapFromGlobal(mouseEv->globalPos())); widgetSelected(widget); } } } return QObject::eventFilter(object, event); } void WidgetInspectorServer::updateWidgetPreview() { if (!m_remoteView->isActive() || !m_selectedWidget) return; RemoteViewFrame frame; frame.setImage(imageForWidget(m_selectedWidget->window())); WidgetFrameData data; data.tabFocusRects = tabFocusChain(m_selectedWidget->window()); frame.setData(QVariant::fromValue(data)); m_remoteView->sendFrame(frame); } QVector WidgetInspectorServer::tabFocusChain(QWidget* window) const { QVector r; QSet widgets; auto w = window; while (w->nextInFocusChain()) { w = w->nextInFocusChain(); if (widgets.contains(w)) break; widgets.insert(w); if (!w->isVisible() || !w->isEnabled()) continue; if ((w->focusPolicy() & Qt::TabFocus) == 0) continue; const auto rect = QRect(w->mapTo(window, QPoint(0, 0)), w->size()); if (!window->rect().contains(rect)) // happens for example for inactive dock widgets continue; r.push_back(rect); } return r; } void WidgetInspectorServer::requestElementsAt(const QPoint &pos, GammaRay::RemoteViewInterface::RequestMode mode) { if (!m_selectedWidget) return; auto window = m_selectedWidget->window(); int bestCandidate; const ObjectIds objects = recursiveWidgetsAt(window, pos, mode, bestCandidate); if (!objects.isEmpty()) { emit elementsAtReceived(objects, bestCandidate); } } void WidgetInspectorServer::pickElementId(const GammaRay::ObjectId &id) { QWidget *widget = id.asQObjectType(); if (widget) widgetSelected(widget); } QImage WidgetInspectorServer::imageForWidget(QWidget *widget) { // prevent "recursion", i.e. infinite update loop, in our eventFilter Util::SetTempValue > guard(m_selectedWidget, nullptr); // We should use hidpi rendering but it's buggy so let stay with // low dpi rendering. See QTBUG-53801 const qreal ratio = 1; // widget->window()->devicePixelRatio(); QImage img(widget->size() * ratio, QImage::Format_ARGB32); img.setDevicePixelRatio(ratio); img.fill(Qt::transparent); widget->render(&img); return img; } void WidgetInspectorServer::recreateOverlayWidget() { ProbeGuard guard; m_overlayWidget = new OverlayWidget; m_overlayWidget->hide(); // the target application might have destroyed the overlay widget // (e.g. because the parent of the overlay got destroyed). // just recreate a new one in this case connect(m_overlayWidget.data(), &QObject::destroyed, this, &WidgetInspectorServer::recreateOverlayWidget); } void WidgetInspectorServer::widgetSelected(QWidget *widget) { if (m_selectedWidget == widget) return; const QAbstractItemModel *model = m_widgetSelectionModel->model(); const QModelIndexList indexList = model->match(model->index(0, 0), ObjectModel::ObjectRole, QVariant::fromValue(widget), 1, Qt::MatchExactly | Qt::MatchRecursive | Qt::MatchWrap); if (indexList.isEmpty()) return; const QModelIndex index = indexList.first(); m_widgetSelectionModel->select( index, QItemSelectionModel::Select | QItemSelectionModel::Clear |QItemSelectionModel::Rows | QItemSelectionModel::Current); } void WidgetInspectorServer::objectSelected(QObject *obj) { if (auto wdg = qobject_cast(obj)) { widgetSelected(wdg); } else if (auto layout = qobject_cast(obj)) { // TODO select the layout directly here if (layout->parentWidget()) widgetSelected(layout->parentWidget()); } } // TODO the following actions should actually store the file on the client! void WidgetInspectorServer::saveAsImage(const QString &fileName) { if (fileName.isEmpty() || !m_selectedWidget) return; m_overlayWidget->hide(); QImage img = imageForWidget(m_selectedWidget); m_overlayWidget->show(); img.save(fileName); } void WidgetInspectorServer::saveAsSvg(const QString &fileName) { if (fileName.isEmpty() || !m_selectedWidget) return; m_overlayWidget->hide(); callExternalExportAction("gammaray_save_widget_to_svg", m_selectedWidget, fileName); m_overlayWidget->show(); } void WidgetInspectorServer::saveAsPdf(const QString &fileName) { if (fileName.isEmpty() || !m_selectedWidget) return; m_overlayWidget->hide(); callExternalExportAction("gammaray_save_widget_to_pdf", m_selectedWidget, fileName); m_overlayWidget->show(); } void WidgetInspectorServer::saveAsUiFile(const QString &fileName) { if (fileName.isEmpty() || !m_selectedWidget) return; callExternalExportAction("gammaray_save_widget_to_ui", m_selectedWidget, fileName); } GammaRay::ObjectIds WidgetInspectorServer::recursiveWidgetsAt(QWidget *parent, const QPoint &pos, GammaRay::RemoteViewInterface::RequestMode mode, int &bestCandidate) const { Q_ASSERT(parent); ObjectIds objects; bestCandidate = -1; const auto childItems = parent->children(); for (int i = childItems.size() - 1; i >= 0; --i) { // backwards to match z order auto c = childItems.at(i); if (!c->isWidgetType() || c->metaObject()->className() == QLatin1String("GammaRay::OverlayWidget")) continue; auto w = qobject_cast(c); const QPoint p = w->mapFromParent(pos); if (w->rect().contains(p, true)) { const bool hasSubChildren = !w->children().isEmpty(); if (hasSubChildren) { const int count = objects.count(); int bc; objects << recursiveWidgetsAt(w, p, mode, bc); if (bestCandidate == -1 && bc != -1) { bestCandidate = count + bc; } } else { if (bestCandidate == -1 && isGoodCandidateWidget(w)) { bestCandidate = objects.count(); } objects << ObjectId(w); } } if (bestCandidate != -1 && mode == RemoteViewInterface::RequestBest) { break; } } if (bestCandidate == -1 && isGoodCandidateWidget(parent)) { bestCandidate = objects.count(); } objects << ObjectId(parent); if (bestCandidate != -1 && mode == RemoteViewInterface::RequestBest) { objects = ObjectIds() << objects[bestCandidate]; bestCandidate = 0; } return objects; } void WidgetInspectorServer::callExternalExportAction(const char *name, QWidget *widget, const QString &fileName) { if (!m_externalExportActions->isLoaded()) { foreach (const auto &path, Paths::pluginPaths(GAMMARAY_PROBE_ABI)) { const QString baseName = path + QLatin1String("/libgammaray_widget_export_actions"); m_externalExportActions->setFileName(baseName + QLatin1Char('-') + QStringLiteral(GAMMARAY_PROBE_ABI)); if (m_externalExportActions->load()) break; m_externalExportActions->setFileName(baseName + QStringLiteral(GAMMARAY_DEBUG_POSTFIX)); if (m_externalExportActions->load()) break; } } void (*function)(QWidget *, const QString &) = reinterpret_cast(m_externalExportActions->resolve(name)); if (!function) { cerr << Q_FUNC_INFO << ' ' << qPrintable(m_externalExportActions->errorString()) << endl; return; } function(widget, fileName); } void WidgetInspectorServer::analyzePainting() { if (!m_selectedWidget || !PaintAnalyzer::isAvailable()) return; m_overlayWidget->hide(); m_paintAnalyzer->beginAnalyzePainting(); m_paintAnalyzer->setBoundingRect(m_selectedWidget->rect()); m_selectedWidget->render(m_paintAnalyzer->paintDevice()); m_paintAnalyzer->endAnalyzePainting(); m_overlayWidget->show(); } void WidgetInspectorServer::checkFeatures() { Features f = NoFeature; #ifdef HAVE_QT_SVG f |= SvgExport; #endif #ifdef HAVE_QT_PRINTSUPPORT f |= PdfExport; #endif #ifdef HAVE_QT_DESIGNER f |= UiExport; #endif if (PaintAnalyzer::isAvailable()) f |= AnalyzePainting; f |= InputRedirection; setFeatures(f); } void WidgetInspectorServer::registerWidgetMetaTypes() { MetaObject *mo = nullptr; MO_ADD_METAOBJECT0(QLayoutItem) MO_ADD_PROPERTY(QLayoutItem, alignment, setAlignment); MO_ADD_PROPERTY_RO(QLayoutItem, controlTypes); MO_ADD_PROPERTY_RO(QLayoutItem, expandingDirections); MO_ADD_PROPERTY(QLayoutItem, geometry, setGeometry); MO_ADD_PROPERTY_RO(QLayoutItem, hasHeightForWidth); MO_ADD_PROPERTY_RO(QLayoutItem, isEmpty); MO_ADD_PROPERTY_RO(QLayoutItem, maximumSize); MO_ADD_PROPERTY_RO(QLayoutItem, minimumSize); MO_ADD_PROPERTY_RO(QLayoutItem, sizeHint); MO_ADD_METAOBJECT2(QLayout, QObject, QLayoutItem); MO_ADD_PROPERTY(QLayout, contentsMargins, setContentsMargins); MO_ADD_PROPERTY_RO(QLayout, contentsRect); MO_ADD_PROPERTY_RO(QLayout, count); MO_ADD_PROPERTY(QLayout, isEnabled, setEnabled); MO_ADD_PROPERTY_RO(QLayout, menuBar); MO_ADD_PROPERTY_RO(QLayout, parentWidget); MO_ADD_METAOBJECT1(QGridLayout, QLayout); MO_ADD_PROPERTY_RO(QGridLayout, columnCount); MO_ADD_PROPERTY_RO(QGridLayout, rowCount); MO_ADD_METAOBJECT2(QWidget, QObject, QPaintDevice); MO_ADD_PROPERTY_RO(QWidget, actions); MO_ADD_PROPERTY (QWidget, backgroundRole, setBackgroundRole); MO_ADD_PROPERTY_RO(QWidget, contentsMargins); MO_ADD_PROPERTY_RO(QWidget, contentsRect); MO_ADD_PROPERTY_RO(QWidget, focusProxy); MO_ADD_PROPERTY_RO(QWidget, focusWidget); MO_ADD_PROPERTY (QWidget, foregroundRole, setForegroundRole); MO_ADD_PROPERTY (QWidget, graphicsEffect, setGraphicsEffect); MO_ADD_PROPERTY_RO(QWidget, graphicsProxyWidget); MO_ADD_PROPERTY_RO(QWidget, hasFocus); MO_ADD_PROPERTY_RO(QWidget, hasMouseTracking); MO_ADD_PROPERTY_RO(QWidget, isWindow); MO_ADD_PROPERTY (QWidget, layout, setLayout); MO_ADD_PROPERTY_O2(QWidget, mask, setMask); MO_ADD_PROPERTY_RO(QWidget, nativeParentWidget); MO_ADD_PROPERTY_RO(QWidget, nextInFocusChain); MO_ADD_PROPERTY_RO(QWidget, parentWidget); MO_ADD_PROPERTY_RO(QWidget, previousInFocusChain); MO_ADD_PROPERTY (QWidget, style, setStyle); MO_ADD_PROPERTY_RO(QWidget, underMouse); MO_ADD_PROPERTY_RO(QWidget, visibleRegion); MO_ADD_PROPERTY_RO(QWidget, window); MO_ADD_PROPERTY_RO(QWidget, windowHandle); MO_ADD_PROPERTY (QWidget, windowFlags, setWindowFlags); MO_ADD_PROPERTY (QWidget, windowRole, setWindowRole); MO_ADD_PROPERTY (QWidget, windowState, setWindowState); MO_ADD_METAOBJECT1(QStyle, QObject); MO_ADD_PROPERTY_RO(QStyle, proxy); MO_ADD_PROPERTY_RO(QStyle, standardPalette); MO_ADD_METAOBJECT1(QApplication, QGuiApplication); MO_ADD_PROPERTY_ST(QApplication, activeModalWidget); MO_ADD_PROPERTY_ST(QApplication, activePopupWidget); MO_ADD_PROPERTY_ST(QApplication, activeWindow); MO_ADD_PROPERTY_ST(QApplication, colorSpec); MO_ADD_PROPERTY_ST(QApplication, desktop); MO_ADD_PROPERTY_ST(QApplication, focusWidget); MO_ADD_PROPERTY_ST(QApplication, style); MO_ADD_PROPERTY_ST(QApplication, topLevelWidgets); MO_ADD_METAOBJECT1(QCompleter, QObject); MO_ADD_PROPERTY_RO(QCompleter, completionCount); MO_ADD_PROPERTY_RO(QCompleter, completionModel); MO_ADD_PROPERTY_RO(QCompleter, currentCompletion); MO_ADD_PROPERTY_RO(QCompleter, currentRow); MO_ADD_PROPERTY (QCompleter, model, setModel); MO_ADD_PROPERTY (QCompleter, popup, setPopup); MO_ADD_PROPERTY_RO(QCompleter, widget); MO_ADD_METAOBJECT1(QFrame, QWidget); MO_ADD_METAOBJECT1(QAbstractScrollArea, QFrame); MO_ADD_PROPERTY_RO(QAbstractScrollArea, cornerWidget); MO_ADD_PROPERTY (QAbstractScrollArea, horizontalScrollBar, setHorizontalScrollBar); MO_ADD_PROPERTY_RO(QAbstractScrollArea, maximumViewportSize); MO_ADD_PROPERTY (QAbstractScrollArea, verticalScrollBar, setVerticalScrollBar); MO_ADD_PROPERTY (QAbstractScrollArea, viewport, setViewport); MO_ADD_METAOBJECT1(QAbstractItemView, QAbstractScrollArea); MO_ADD_PROPERTY_RO(QAbstractItemView, model); MO_ADD_METAOBJECT1(QAbstractButton, QWidget); MO_ADD_PROPERTY_RO(QAbstractButton, group); MO_ADD_METAOBJECT1(QComboBox, QWidget); MO_ADD_PROPERTY_RO(QComboBox, completer); MO_ADD_PROPERTY_RO(QComboBox, itemDelegate); MO_ADD_PROPERTY_RO(QComboBox, lineEdit); MO_ADD_PROPERTY_RO(QComboBox, model); MO_ADD_PROPERTY (QComboBox, validator, setValidator); MO_ADD_PROPERTY (QComboBox, view, setView); MO_ADD_METAOBJECT1(QLineEdit, QWidget); MO_ADD_PROPERTY (QLineEdit, completer, setCompleter); MO_ADD_PROPERTY (QLineEdit, validator, setValidator); MO_ADD_METAOBJECT1(QScrollArea, QAbstractScrollArea); MO_ADD_PROPERTY (QScrollArea, widget, setWidget); MO_ADD_METAOBJECT1(QToolButton, QAbstractButton); MO_ADD_PROPERTY_RO(QToolButton, defaultAction); MO_ADD_PROPERTY_RO(QToolButton, menu); MO_ADD_METAOBJECT0(QSizePolicy); MO_ADD_PROPERTY (QSizePolicy, controlType, setControlType); MO_ADD_PROPERTY_RO(QSizePolicy, expandingDirections); MO_ADD_PROPERTY (QSizePolicy, hasHeightForWidth, setHeightForWidth); MO_ADD_PROPERTY (QSizePolicy, hasWidthForHeight, setWidthForHeight); MO_ADD_PROPERTY (QSizePolicy, horizontalPolicy, setHorizontalPolicy); MO_ADD_PROPERTY (QSizePolicy, horizontalStretch, setHorizontalStretch); MO_ADD_PROPERTY (QSizePolicy, retainSizeWhenHidden, setRetainSizeWhenHidden); MO_ADD_PROPERTY (QSizePolicy, verticalPolicy, setVerticalPolicy); MO_ADD_PROPERTY (QSizePolicy, verticalStretch, setVerticalStretch); MO_ADD_METAOBJECT1(QActionEvent, QEvent); MO_ADD_PROPERTY_RO(QActionEvent, action); MO_ADD_PROPERTY_RO(QActionEvent, before); } static QString sizePolicyPolicyToString(QSizePolicy::Policy policy) { const int index = QSizePolicy::staticMetaObject.indexOfEnumerator("Policy"); const QMetaEnum metaEnum = QSizePolicy::staticMetaObject.enumerator(index); return QString::fromLatin1(metaEnum.valueToKey(policy)); } static QString sizePolicyToString(const QSizePolicy &policy) { return sizePolicyPolicyToString(policy.horizontalPolicy()) + " x " + sizePolicyPolicyToString(policy.verticalPolicy()); } void WidgetInspectorServer::registerVariantHandlers() { VariantHandler::registerStringConverter(sizePolicyToString); VariantHandler::registerStringConverter(Util::displayString); } void WidgetInspectorServer::discoverObjects() { if (qApp) { foreach (QWidget *widget, qApp->topLevelWidgets()) m_probe->discoverObject(widget); } } void WidgetInspectorServer::objectCreated(QObject *object) { if (!object) return; if (qobject_cast(object)) discoverObjects(); if (QAbstractItemView *view = qobject_cast(object)) m_probe->discoverObject(view->model()); } gammaray-2.11.3/plugins/widgetinspector/widgetinspectorserver.h000066400000000000000000000072161413310443000251000ustar00rootroot00000000000000/* widgetinspectorserver.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_WIDGETINSPECTOR_WIDGETINSPECTORSERVER_H #define GAMMARAY_WIDGETINSPECTOR_WIDGETINSPECTORSERVER_H #include #include #include QT_BEGIN_NAMESPACE class QModelIndex; class QItemSelection; class QItemSelectionModel; class QLibrary; class QPoint; QT_END_NAMESPACE namespace GammaRay { class Probe; class PropertyController; class OverlayWidget; class PaintAnalyzer; class RemoteViewServer; class ObjectId; using ObjectIds = QVector; class WidgetInspectorServer : public WidgetInspectorInterface { Q_OBJECT Q_INTERFACES(GammaRay::WidgetInspectorInterface) public: explicit WidgetInspectorServer(Probe *probe, QObject *parent = nullptr); ~WidgetInspectorServer() override; signals: void elementsAtReceived(const GammaRay::ObjectIds &ids, int bestCandidate); protected: bool eventFilter(QObject *object, QEvent *event) override; private: GammaRay::ObjectIds recursiveWidgetsAt(QWidget *parent, const QPoint &pos, GammaRay::RemoteViewInterface::RequestMode mode, int& bestCandidate) const; void callExternalExportAction(const char *name, QWidget *widget, const QString &fileName); QImage imageForWidget(QWidget *widget); void registerWidgetMetaTypes(); void registerVariantHandlers(); void discoverObjects(); void checkFeatures(); QVector tabFocusChain(QWidget *window) const; private slots: void widgetSelectionChanged(const QItemSelection &selection); void widgetSelected(QWidget *widget); void objectSelected(QObject *obj); void objectCreated(QObject *object); void recreateOverlayWidget(); void saveAsImage(const QString &fileName) override; void saveAsSvg(const QString &fileName) override; void saveAsPdf(const QString &fileName) override; void saveAsUiFile(const QString &fileName) override; void analyzePainting() override; void updateWidgetPreview(); void requestElementsAt(const QPoint &pos, GammaRay::RemoteViewInterface::RequestMode mode); void pickElementId(const GammaRay::ObjectId& id); private: QPointer m_overlayWidget; QLibrary *m_externalExportActions; PropertyController *m_propertyController; QItemSelectionModel *m_widgetSelectionModel; QPointer m_selectedWidget; PaintAnalyzer *m_paintAnalyzer; RemoteViewServer *m_remoteView; Probe *m_probe; }; } #endif // GAMMARAY_WIDGETINSPECTORSERVER_H gammaray-2.11.3/plugins/widgetinspector/widgetinspectorwidget.cpp000066400000000000000000000266701413310443000254150ustar00rootroot00000000000000/* widgetinspectorwidget.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include "widgetinspectorwidget.h" #include "widgetinspectorinterface.h" #include "widgetinspectorclient.h" #include "widgetclientmodel.h" #include "ui_widgetinspectorwidget.h" #include "waextension/widgetattributetab.h" #include "widgetmodelroles.h" #include "widgetremoteview.h" #ifdef GAMMARAY_WITH_WIDGET3D #include "widget3dview.h" #include #include #endif #include "common/objectbroker.h" #include "common/objectmodel.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace GammaRay; static QObject *createWidgetInspectorClient(const QString & /*name*/, QObject *parent) { return new WidgetInspectorClient(parent); } WidgetInspectorWidget::WidgetInspectorWidget(QWidget *parent) : QWidget(parent) , ui(new Ui::WidgetInspectorWidget) , m_stateManager(this) , m_inspector(nullptr) , m_remoteView(new WidgetRemoteView(this)) , m_3dView(nullptr) { ObjectBroker::registerClientObjectFactoryCallback( createWidgetInspectorClient); m_inspector = ObjectBroker::object(); ui->setupUi(this); ui->widgetPropertyWidget->setObjectBaseName(m_inspector->objectName()); auto widgetTree = ObjectBroker::model(QStringLiteral("com.kdab.GammaRay.WidgetTree")); auto widgetModel = new WidgetClientModel(this); widgetModel->setSourceModel(widgetTree); ui->widgetTreeView->header()->setObjectName("widgetTreeViewHeader"); ui->widgetTreeView->setDeferredResizeMode(0, QHeaderView::Stretch); ui->widgetTreeView->setDeferredResizeMode(1, QHeaderView::Interactive); ui->widgetTreeView->setModel(widgetModel); ui->widgetTreeView->setSelectionModel(ObjectBroker::selectionModel(widgetModel)); new SearchLineController(ui->widgetSearchLine, widgetModel); connect(ui->widgetTreeView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &WidgetInspectorWidget::widgetSelected); connect(ui->widgetTreeView, &QWidget::customContextMenuRequested, this, &WidgetInspectorWidget::widgetTreeContextMenu); m_remoteView->setName(QStringLiteral("com.kdab.GammaRay.WidgetRemoteView")); m_remoteView->setPickSourceModel(widgetModel); m_remoteView->setFlagRole(WidgetModelRoles::WidgetFlags); m_remoteView->setInvisibleMask(WidgetModelRoles::Invisible); auto layout = new QVBoxLayout; layout->setMargin(0); auto toolbar = new QToolBar(this); // Our icons are 16x16 and support hidpi, so let force iconSize on every styles toolbar->setIconSize(QSize(16, 16)); toolbar->setToolButtonStyle(Qt::ToolButtonIconOnly); layout->setMenuBar(toolbar); ui->widgetPreviewContainer->setLayout(layout); layout->addWidget(m_remoteView); foreach (auto action, m_remoteView->interactionModeActions()->actions()) toolbar->addAction(action); toolbar->addSeparator(); auto action = new QAction(UIResources::themedIcon(QLatin1String("active-focus.png")), tr("Show Tab Focus Chain"), this); action->setCheckable(true); connect(action, &QAction::toggled, m_remoteView, &WidgetRemoteView::setTabFocusOverlayEnabled); toolbar->addAction(action); toolbar->addSeparator(); toolbar->addAction(m_remoteView->zoomOutAction()); auto zoom = new QComboBox; // macOS and some platforms expect to use *small* controls in such small toolbar zoom->setAttribute(Qt::WA_MacSmallSize); zoom->setModel(m_remoteView->zoomLevelModel()); toolbar->addWidget(zoom); connect(zoom, static_cast(&QComboBox::currentIndexChanged), m_remoteView, &RemoteViewWidget::setZoomLevel); connect(m_remoteView, &RemoteViewWidget::zoomLevelChanged, zoom, &QComboBox::setCurrentIndex); zoom->setCurrentIndex(m_remoteView->zoomLevelIndex()); toolbar->addAction(m_remoteView->zoomInAction()); connect(ui->actionSaveAsImage, &QAction::triggered, this, &WidgetInspectorWidget::saveAsImage); connect(ui->actionSaveAsSvg, &QAction::triggered, this, &WidgetInspectorWidget::saveAsSvg); connect(ui->actionSaveAsPdf, &QAction::triggered, this, &WidgetInspectorWidget::saveAsPdf); connect(ui->actionSaveAsUiFile, &QAction::triggered, this, &WidgetInspectorWidget::saveAsUiFile); connect(ui->actionAnalyzePainting, &QAction::triggered, this, &WidgetInspectorWidget::analyzePainting); connect(m_inspector, &WidgetInspectorInterface::featuresChanged, this, &WidgetInspectorWidget::updateActions); addAction(ui->actionSaveAsImage); addAction(ui->actionSaveAsSvg); addAction(ui->actionSaveAsPdf); addAction(ui->actionSaveAsUiFile); addAction(ui->actionAnalyzePainting); updateActions(); m_stateManager.setDefaultSizes(ui->mainSplitter, UISizeVector() << "50%" << "50%"); m_stateManager.setDefaultSizes(ui->previewSplitter, UISizeVector() << "50%" << "50%"); #ifdef GAMMARAY_WITH_WIDGET3D // Check if QQC are available, there's no build-time check for this QQmlEngine engine; QQmlComponent comp(&engine); comp.setData("import QtQuick.Controls 1.2; CheckBox {}", QUrl()); QScopedPointer obj(comp.create()); if (!obj.isNull()) { QWidget *widget3d = new QWidget(this); ui->tabWidget->addTab(widget3d, tr("3D View")); widget3d->setLayout(new QHBoxLayout()); } else { qWarning() << "Disabling 3D Widget inspector: missing QtQuick Controls"; } #else ui->tabWidget->findChild()->hide(); #endif connect(ui->widgetPropertyWidget, &PropertyWidget::tabsUpdated, this, &WidgetInspectorWidget::propertyWidgetTabsChanged); connect(ui->tabWidget, &QTabWidget::currentChanged, this, &WidgetInspectorWidget::onTabChanged); } WidgetInspectorWidget::~WidgetInspectorWidget() = default; void WidgetInspectorWidget::saveTargetState(QSettings *settings) const { settings->setValue("remoteViewState", m_remoteView->saveState()); } void WidgetInspectorWidget::restoreTargetState(QSettings *settings) { m_remoteView->restoreState(settings->value("remoteViewState").toByteArray()); } void WidgetInspectorWidget::onTabChanged(int index) { #ifdef GAMMARAY_WITH_WIDGET3D if (index == 1 && m_3dView == nullptr) { m_3dView = new Widget3DView(this); ui->tabWidget->widget(1)->layout()->addWidget(m_3dView); } #else Q_UNUSED(index) #endif } void WidgetInspectorWidget::updateActions() { const auto model = ui->widgetTreeView->selectionModel()->selectedRows(); const auto selection = !model.isEmpty() && model.first().isValid(); ui->actionSaveAsImage->setEnabled(selection); ui->actionSaveAsSvg->setEnabled( selection && m_inspector->features() & WidgetInspectorInterface::SvgExport); ui->actionSaveAsPdf->setEnabled( selection && m_inspector->features() & WidgetInspectorInterface::PdfExport); ui->actionSaveAsUiFile->setEnabled( selection && m_inspector->features() & WidgetInspectorInterface::UiExport); ui->actionAnalyzePainting->setEnabled( selection && m_inspector->features() & WidgetInspectorInterface::AnalyzePainting); auto f = m_remoteView->supportedInteractionModes() & ~RemoteViewWidget::InputRedirection; if (m_inspector->features() & WidgetInspectorInterface::InputRedirection) f |= RemoteViewWidget::InputRedirection; m_remoteView->setSupportedInteractionModes(f); } void WidgetInspectorWidget::propertyWidgetTabsChanged() { m_stateManager.saveState(); m_stateManager.reset(); } void WidgetInspectorWidget::widgetSelected(const QItemSelection &selection) { ui->tabWidget->setCurrentIndex(0); // select the tree view tab QModelIndex index; if (!selection.isEmpty()) index = selection.first().topLeft(); if (index.isValid()) { // in case selection was triggered remotely ui->widgetTreeView->scrollTo(index); } updateActions(); } void WidgetInspectorWidget::widgetTreeContextMenu(QPoint pos) { const auto index = ui->widgetTreeView->indexAt(pos); if (!index.isValid()) return; const auto objectId = index.data(ObjectModel::ObjectIdRole).value(); QMenu menu(tr("Widget @ %1").arg(QLatin1String("0x") + QString::number(objectId.id(), 16))); ContextMenuExtension ext(objectId); ext.populateMenu(&menu); menu.exec(ui->widgetTreeView->viewport()->mapToGlobal(pos)); } void WidgetInspectorWidget::saveAsImage() { const QString fileName = QFileDialog::getSaveFileName( this, tr("Save As Image"), QString(), tr("Image Files (*.png *.jpg)")); if (fileName.isEmpty()) return; m_inspector->saveAsImage(fileName); } void WidgetInspectorWidget::saveAsSvg() { const QString fileName = QFileDialog::getSaveFileName( this, tr("Save As SVG"), QString(), tr("Scalable Vector Graphics (*.svg)")); if (fileName.isEmpty()) return; m_inspector->saveAsSvg(fileName); } void WidgetInspectorWidget::saveAsPdf() { const QString fileName = QFileDialog::getSaveFileName( this, tr("Save As PDF"), QString(), tr("PDF (*.pdf)")); if (fileName.isEmpty()) return; m_inspector->saveAsPdf(fileName); } void WidgetInspectorWidget::saveAsUiFile() { const QString fileName = QFileDialog::getSaveFileName( this, tr("Save As Qt Designer UI File"), QString(), tr("Qt Designer UI File (*.ui)")); if (fileName.isEmpty()) return; m_inspector->saveAsUiFile(fileName); } void WidgetInspectorWidget::analyzePainting() { m_inspector->analyzePainting(); PaintBufferViewer *viewer = new PaintBufferViewer(QStringLiteral("com.kdab.GammaRay.WidgetPaintAnalyzer"), this); viewer->show(); } void WidgetInspectorUiFactory::initUi() { PropertyWidget::registerTab(QStringLiteral("widgetAttributes"), tr( "Attributes"), PropertyWidgetTabPriority::Advanced); } gammaray-2.11.3/plugins/widgetinspector/widgetinspectorwidget.h000066400000000000000000000052671413310443000250610ustar00rootroot00000000000000/* widgetinspectorwidget.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_WIDGETINSPECTOR_WIDGETINSPECTORWIDGET_H #define GAMMARAY_WIDGETINSPECTOR_WIDGETINSPECTORWIDGET_H #include #include #include QT_BEGIN_NAMESPACE class QItemSelection; class QModelIndex; QT_END_NAMESPACE namespace GammaRay { class WidgetInspectorInterface; class Widget3DView; class WidgetRemoteView; namespace Ui { class WidgetInspectorWidget; } class WidgetInspectorWidget : public QWidget { Q_OBJECT public: explicit WidgetInspectorWidget(QWidget *parent = nullptr); ~WidgetInspectorWidget() override; Q_INVOKABLE void saveTargetState(QSettings *settings) const; Q_INVOKABLE void restoreTargetState(QSettings *settings); private slots: void onTabChanged(int index); void widgetSelected(const QItemSelection &selection); void widgetTreeContextMenu(QPoint pos); void saveAsImage(); void saveAsSvg(); void saveAsPdf(); void saveAsUiFile(); void analyzePainting(); void updateActions(); void propertyWidgetTabsChanged(); private: QScopedPointer ui; UIStateManager m_stateManager; WidgetInspectorInterface *m_inspector; WidgetRemoteView *m_remoteView; Widget3DView *m_3dView; }; class WidgetInspectorUiFactory : public QObject, public StandardToolUiFactory { Q_OBJECT Q_INTERFACES(GammaRay::ToolUiFactory) Q_PLUGIN_METADATA(IID "com.kdab.GammaRay.ToolUiFactory" FILE "gammaray_widgetinspector.json") public: void initUi() override; }; } #endif // GAMMARAY_WIDGETINSPECTOR_H gammaray-2.11.3/plugins/widgetinspector/widgetinspectorwidget.ui000066400000000000000000000074471413310443000252510ustar00rootroot00000000000000 GammaRay::WidgetInspectorWidget 0 0 732 528 Qt::Horizontal 0 Tree Qt::Vertical Qt::CustomContextMenu true Save as &Image... Save currently selected widget as image. Save as &SVG... Renders the currently selected image as SVG vector graphic. Save as &PDF... Renders the currently selected widget in to a PDF file. Save as &UI file... Saves the currently selected widget as a Qt designer UI file. &Analyze Painting... F9 GammaRay::DeferredTreeView QTreeView
    ui/deferredtreeview.h
    GammaRay::PropertyWidget QWidget
    ui/propertywidget.h
    1
    gammaray-2.11.3/plugins/widgetinspector/widgetmodelroles.h000066400000000000000000000031131413310443000240000ustar00rootroot00000000000000/* widgetmodelroles.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_WIDGETINSPECTOR_WIDGETMODELROLES_H #define GAMMARAY_WIDGETINSPECTOR_WIDGETMODELROLES_H #include namespace GammaRay { /** Model roles shared between client and server. */ namespace WidgetModelRoles { enum Roles { WidgetFlags = ObjectModel::UserRole, }; enum WidgetFlag { None = 0, Invisible = 1 }; } } Q_DECLARE_METATYPE(GammaRay::WidgetModelRoles::WidgetFlag) #endif // GAMMARAY_WIDGETINSPECTOR_WIDGETMODELROLES_H gammaray-2.11.3/plugins/widgetinspector/widgetpaintanalyzerextension.cpp000066400000000000000000000053131413310443000270100ustar00rootroot00000000000000/* widgetpaintanalyzerextension.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "widgetpaintanalyzerextension.h" #include #include #include #include using namespace GammaRay; WidgetPaintAnalyzerExtension::WidgetPaintAnalyzerExtension(PropertyController *controller) : PropertyControllerExtension(controller->objectBaseName() + ".painting") , m_paintAnalyzer(nullptr) , m_widget(nullptr) { // check if the paint analyzer already exists before creating it, // as we share the UI with the other plugins. const QString aName = controller->objectBaseName() + QStringLiteral(".painting.analyzer"); if (ObjectBroker::hasObject(aName)) { m_paintAnalyzer = qobject_cast(ObjectBroker::object(aName)); } else { m_paintAnalyzer = new PaintAnalyzer(aName, controller); } QObject::connect(m_paintAnalyzer, &PaintAnalyzer::requestUpdate, [this]() { analyze(); }); } WidgetPaintAnalyzerExtension::~WidgetPaintAnalyzerExtension() = default; bool WidgetPaintAnalyzerExtension::setQObject(QObject *object) { m_widget = qobject_cast(object); if (!PaintAnalyzer::isAvailable() || !m_widget) return false; m_paintAnalyzer->reset(); return true; } void WidgetPaintAnalyzerExtension::analyze() { if (!m_widget) return; m_paintAnalyzer->beginAnalyzePainting(); m_paintAnalyzer->setBoundingRect(m_widget->rect()); m_widget->render(m_paintAnalyzer->paintDevice(), QPoint(), QRegion(), nullptr); m_paintAnalyzer->endAnalyzePainting(); } gammaray-2.11.3/plugins/widgetinspector/widgetpaintanalyzerextension.h000066400000000000000000000034171413310443000264600ustar00rootroot00000000000000/* widgetpaintanalyzerextension.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_WIDGETPAINTANALYZEREXTENSION_H #define GAMMARAY_WIDGETPAINTANALYZEREXTENSION_H #include QT_BEGIN_NAMESPACE class QWidget; QT_END_NAMESPACE namespace GammaRay { class PaintAnalyzer; class PropertyController; class WidgetPaintAnalyzerExtension : public PropertyControllerExtension { public: explicit WidgetPaintAnalyzerExtension(PropertyController *controller); ~WidgetPaintAnalyzerExtension(); bool setQObject(QObject *object) override; private: void analyze(); PaintAnalyzer *m_paintAnalyzer; QWidget *m_widget; }; } #endif // GAMMARAY_WIDGETPAINTANALYZEREXTENSION_H gammaray-2.11.3/plugins/widgetinspector/widgetremoteview.cpp000066400000000000000000000060151413310443000243600ustar00rootroot00000000000000/* widgetremoteview.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "widgetremoteview.h" #include "widgetinspectorinterface.h" #include #include #include using namespace GammaRay; WidgetRemoteView::WidgetRemoteView(QWidget* parent) : RemoteViewWidget(parent) , m_tabFocusEnabled(false) { } WidgetRemoteView::~WidgetRemoteView() = default; void WidgetRemoteView::setTabFocusOverlayEnabled(bool enabled) { m_tabFocusEnabled = enabled; update(); } static void drawArrow(QPainter *p, QPointF first, QPointF second) { p->drawLine(first, second); QPointF vector(second - first); QMatrix m; m.rotate(30); QVector2D v1 = QVector2D(m.map(vector)).normalized() * 10; m.rotate(-60); QVector2D v2 = QVector2D(m.map(vector)).normalized() * 10; p->drawLine(second, second - v1.toPointF()); p->drawLine(second, second - v2.toPointF()); } void WidgetRemoteView::drawDecoration(QPainter* p) { if (!m_tabFocusEnabled) return; const auto data = frame().data().value(); if (data.tabFocusRects.size() < 2) return; p->save(); p->setPen(Qt::darkGreen); for (const auto &rect : data.tabFocusRects) p->drawRect(mapFromSource(rect)); QVector lines; lines.reserve(data.tabFocusRects.size()); for (int i = 0; i < data.tabFocusRects.size() - 1; ++i) { const auto r1 = mapFromSource(data.tabFocusRects.at(i)); const auto r2 = mapFromSource(data.tabFocusRects.at(i + 1)); p->setPen(Qt::green); QLineF l(r1.center(), r2.center()); for (const auto &prevLine : qAsConst(lines)) { QPointF pnt; if (l.intersect(prevLine, &pnt) == QLineF::BoundedIntersection && pnt != l.p1() && pnt != l.p2()) { p->setPen(Qt::red); break; } } lines.push_back(l); drawArrow(p, r1.center(), r2.center()); } p->restore(); } gammaray-2.11.3/plugins/widgetinspector/widgetremoteview.h000066400000000000000000000031511413310443000240230ustar00rootroot00000000000000/* widgetremoteview.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_WIDGETREMOTEVIEW_H #define GAMMARAY_WIDGETREMOTEVIEW_H #include namespace GammaRay { class WidgetRemoteView : public RemoteViewWidget { Q_OBJECT public: explicit WidgetRemoteView(QWidget *parent = nullptr); ~WidgetRemoteView() override; public slots: void setTabFocusOverlayEnabled(bool enabled); protected: void drawDecoration(QPainter *p) override; private: bool m_tabFocusEnabled; }; } #endif // GAMMARAY_WIDGETREMOTEVIEW_H gammaray-2.11.3/plugins/widgetinspector/widgettreemodel.cpp000066400000000000000000000054701413310443000241560ustar00rootroot00000000000000/* widgettreemodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "widgettreemodel.h" #include "common/modelutils.h" #include "widgetmodelroles.h" #include #include using namespace GammaRay; static bool isMainWindowSubclassAcceptor(const QVariant &v) { const QObject *object = v.value(); return object && object->inherits("QMainWindow"); } WidgetTreeModel::WidgetTreeModel(QObject *parent) : ObjectFilterProxyModelBase(parent) { } QPair WidgetTreeModel::defaultSelectedItem() const { // select the first QMainwindow window (if any) in the widget model return QPair(ObjectModel::ObjectRole, QVariant::fromValue(&isMainWindowSubclassAcceptor)); } QVariant WidgetTreeModel::data(const QModelIndex &index, int role) const { if (index.isValid() && role == WidgetModelRoles::WidgetFlags) { QObject *obj = index.data(ObjectModel::ObjectRole).value(); QWidget *widget = qobject_cast(obj); if (!widget) { QLayout *layout = qobject_cast(obj); if (layout) widget = layout->parentWidget(); } if (widget && !widget->isVisible()) return WidgetModelRoles::Invisible; return WidgetModelRoles::None; } return QSortFilterProxyModel::data(index, role); } QMap WidgetTreeModel::itemData(const QModelIndex &index) const { auto d = ObjectFilterProxyModelBase::itemData(index); d.insert(WidgetModelRoles::WidgetFlags, data(index, WidgetModelRoles::WidgetFlags)); return d; } bool WidgetTreeModel::filterAcceptsObject(QObject *object) const { return object->isWidgetType() || qobject_cast(object); } gammaray-2.11.3/plugins/widgetinspector/widgettreemodel.h000066400000000000000000000035331413310443000236210ustar00rootroot00000000000000/* widgettreemodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_WIDGETINSPECTOR_WIDGETTREEMODEL_H #define GAMMARAY_WIDGETINSPECTOR_WIDGETTREEMODEL_H #include namespace GammaRay { /** Widget tree model. * @todo Show layout hierarchy instead of object hierarchy. */ class WidgetTreeModel : public ObjectFilterProxyModelBase { Q_OBJECT public: explicit WidgetTreeModel(QObject *parent = nullptr); QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; QMap itemData(const QModelIndex &index) const override; Q_INVOKABLE QPair defaultSelectedItem() const; protected: bool filterAcceptsObject(QObject *object) const override; }; } #endif // GAMMARAY_WIDGETTREEMODEL_H gammaray-2.11.3/plugins/wlcompositorinspector/000077500000000000000000000000001413310443000215365ustar00rootroot00000000000000gammaray-2.11.3/plugins/wlcompositorinspector/CMakeLists.txt000066400000000000000000000024651413310443000243050ustar00rootroot00000000000000# probe plugin if (NOT GAMMARAY_CLIENT_ONLY_BUILD) find_package(Wayland 1.12 COMPONENTS Server) set_package_properties(Wayland PROPERTIES PURPOSE "Needed for the QtWayland compositor inspector plug-in." TYPE OPTIONAL) if(Qt5WaylandCompositor_FOUND AND Wayland_FOUND) set(gammaray_wlcompositorinspector_srcs wlcompositorinspector.cpp wlcompositorinterface.cpp clientsmodel.cpp resourceinfo.cpp ) gammaray_add_plugin(gammaray_wlcompositorinspector JSON gammaray_wlcompositorinspector.json SOURCES ${gammaray_wlcompositorinspector_srcs} ) target_link_libraries(gammaray_wlcompositorinspector gammaray_core gammaray_kitemmodels Qt5::WaylandCompositor Wayland::Server) endif() endif() # ui part if(GAMMARAY_BUILD_UI AND (NOT MSVC OR NOT MSVC_VERSION LESS 1800)) # at least MSVC 2013 set(gammaray_wlcompositorinspector_ui_srcs inspectorwidget.cpp wlcompositorinterface.cpp wlcompositorclient.cpp logview.cpp ) qt5_wrap_ui(gammaray_wlcompositorinspector_ui_srcs inspectorwidget.ui ) gammaray_add_plugin(gammaray_wlcompositorinspector_ui JSON gammaray_wlcompositorinspector.json SOURCES ${gammaray_wlcompositorinspector_ui_srcs} ) target_link_libraries(gammaray_wlcompositorinspector_ui Qt5::Widgets gammaray_common gammaray_ui ) endif() gammaray-2.11.3/plugins/wlcompositorinspector/clientsmodel.cpp000066400000000000000000000074311413310443000247310ustar00rootroot00000000000000/* wlcompositorinspector.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Giulio Camuffo Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "clientsmodel.h" #include #include #include #include using namespace GammaRay; ClientsModel::ClientsModel(QObject *parent) : QAbstractTableModel(parent) { } ClientsModel::~ClientsModel() = default; QWaylandClient *ClientsModel::client(int index) const { return m_clients.at(index); } void ClientsModel::addClient(QWaylandClient *client) { beginInsertRows(QModelIndex(), m_clients.count(), m_clients.count()); m_clients.append(client); endInsertRows(); } void ClientsModel::removeClient(QWaylandClient *client) { int index = -1; for (int i = 0; i < m_clients.count(); ++i) { if (m_clients.at(i) == client) { index = i; break; } } if (index == -1) { return; } beginRemoveRows(QModelIndex(), index, index); m_clients.removeAt(index); endRemoveRows(); } int ClientsModel::rowCount(const QModelIndex &parent) const { if (parent.isValid()) return 0; return m_clients.count(); } int ClientsModel::columnCount(const QModelIndex &) const { return EndColumn; } QVariant ClientsModel::data(const QModelIndex &index, int role) const { auto client = m_clients.at(index.row()); switch (index.column()) { case PidColumn: if (role == Qt::DisplayRole) return client->processId(); if (role == ObjectIdRole) return QVariant::fromValue(ObjectId(client)); break; case CommandColumn: { if (role != Qt::DisplayRole) return QVariant(); auto pid = client->processId(); QByteArray path; QTextStream(&path) << "/proc/" << pid << "/cmdline"; QFile file(path); if (!file.open(QIODevice::ReadOnly)) { return QStringLiteral("Not available :/"); } QByteArray data = file.readAll(); data.replace('\0', ' '); return data; } } return QVariant(); } QMap ClientsModel::itemData(const QModelIndex &index) const { QMap map; auto insertRole = [&](int role) { map[role] = data(index, role); }; insertRole(Qt::DisplayRole); insertRole(ObjectIdRole); return map; } QVariant ClientsModel::headerData(int section, Qt::Orientation orientation, int) const { if (orientation == Qt::Horizontal) { switch (section) { case PidColumn: return QStringLiteral("pid"); case CommandColumn: return QStringLiteral("command"); } } return QString::number(section + 1); } gammaray-2.11.3/plugins/wlcompositorinspector/clientsmodel.h000066400000000000000000000042341413310443000243740ustar00rootroot00000000000000/* wlcompositorinspector.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Giulio Camuffo Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_CLIENTSMODEL_H #define GAMMARAY_CLIENTSMODEL_H #include #include #include QT_BEGIN_NAMESPACE class QWaylandClient; QT_END_NAMESPACE namespace GammaRay { class ClientsModel : public QAbstractTableModel { public: enum Columns { PidColumn, CommandColumn, EndColumn }; enum Roles { ObjectIdRole = UserRole + 1 }; explicit ClientsModel(QObject *parent); ~ClientsModel(); QWaylandClient *client(int index) const; void addClient(QWaylandClient *client); void removeClient(QWaylandClient *client); int rowCount(const QModelIndex &parent) const override; int columnCount(const QModelIndex &) const override; QVariant data(const QModelIndex &index, int role) const override; QMap itemData(const QModelIndex &index) const override; QVariant headerData(int section, Qt::Orientation orientation, int) const override; private: QVector m_clients; }; } #endif gammaray-2.11.3/plugins/wlcompositorinspector/gammaray_wlcompositorinspector.json000066400000000000000000000002451413310443000310000ustar00rootroot00000000000000{ "id": "gammaray_wlcompositorinspector", "name": "Wayland Compositor", "selectableTypes": [ "QWaylandClient" ], "types": [ "QWaylandCompositor" ] } gammaray-2.11.3/plugins/wlcompositorinspector/inspectorwidget.cpp000066400000000000000000000133121413310443000254540ustar00rootroot00000000000000/* inspectorwidget.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Giulio Camuffo Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "inspectorwidget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ui_inspectorwidget.h" #include "wlcompositorclient.h" #include "logview.h" #include "clientsmodel.h" // only for extra roles using namespace GammaRay; class RemoteSurfaceView : public RemoteViewWidget { public: explicit RemoteSurfaceView(QWidget *parent) : RemoteViewWidget(parent) { setName(QStringLiteral("com.kdab.GammaRay.WaylandCompositorSurfaceView")); } }; static QObject *wlCompositorClientFactory(const QString &/*name*/, QObject *parent) { return new WlCompositorClient(parent); } InspectorWidget::InspectorWidget(QWidget *parent) : QWidget(parent) , m_ui(new Ui::InspectorWidget) { ObjectBroker::registerClientObjectFactoryCallback(wlCompositorClientFactory); m_client = ObjectBroker::object(); m_client->connected(); m_ui->setupUi(this); m_ui->resourceInfo->setVisible(false); auto resourcesModel = ObjectBroker::model(QStringLiteral("com.kdab.GammaRay.WaylandCompositorResourcesModel")); m_ui->resourcesView->setModel(resourcesModel); m_logView = new LogView(this); m_logView->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); m_ui->gridLayout->addWidget(m_logView, 2, 0, 1, 2); connect(m_client, &WlCompositorInterface::logMessage, m_logView, &LogView::logMessage); connect(m_client, &WlCompositorInterface::resetLog, m_logView, &LogView::reset); connect(m_client, &WlCompositorInterface::setLoggingClient, m_logView, &LogView::setLoggingClient); m_model = ObjectBroker::model(QStringLiteral("com.kdab.GammaRay.WaylandCompositorClientsModel")); auto clientSelectionModel = ObjectBroker::selectionModel(m_model); connect(clientSelectionModel, &QItemSelectionModel::selectionChanged, this, &InspectorWidget::clientSelected); m_ui->clientsView->setModel(m_model); m_ui->clientsView->setSelectionModel(clientSelectionModel); m_ui->clientsView->viewport()->installEventFilter(this); connect(m_ui->clientsView, &QTreeView::customContextMenuRequested, this, &InspectorWidget::clientContextMenu); connect(m_ui->resourcesView->selectionModel(), &QItemSelectionModel::currentChanged, this, &InspectorWidget::resourceActivated); m_ui->resourcesView->viewport()->installEventFilter(this); auto *surfaceView = new RemoteSurfaceView(this); m_ui->gridLayout->addWidget(surfaceView, 1, 0, 1, 1); } InspectorWidget::~InspectorWidget() { m_client->disconnected(); } void InspectorWidget::delayedInit() { } void InspectorWidget::clientSelected(const QItemSelection& selection) { if (selection.isEmpty()) { m_client->setSelectedClient(-1); return; } const auto index = selection.at(0).topLeft(); m_client->setSelectedClient(index.row()); } void InspectorWidget::clientContextMenu(QPoint pos) { auto index = m_ui->clientsView->indexAt(pos); if (!index.isValid()) return; index = index.sibling(index.row(), 0); const auto objectId = index.data(ClientsModel::ObjectIdRole).value(); if (objectId.isNull()) return; QMenu menu; ContextMenuExtension ext(objectId); ext.populateMenu(&menu); menu.exec(m_ui->clientsView->viewport()->mapToGlobal(pos)); } void InspectorWidget::resourceActivated(const QModelIndex &index) { QString text = index.data(Qt::ToolTipRole).toString(); m_client->setSelectedResource(index.data(Qt::UserRole + 2).toUInt()); m_ui->resourceInfo->setText(text); m_ui->resourceInfo->setVisible(!text.isEmpty()); } bool InspectorWidget::eventFilter(QObject *o, QEvent *e) { switch (e->type()) { case QEvent::MouseButtonRelease: { auto *me = static_cast(e); if (o == m_ui->clientsView->viewport()) { const auto idx = m_ui->clientsView->indexAt(me->pos()); if (!idx.isValid()) m_ui->clientsView->selectionModel()->clear(); } else { QModelIndex index = m_ui->resourcesView->indexAt(me->pos()); if (!index.isValid()) { m_ui->resourcesView->setCurrentIndex(index); } } return false; } default: break; } return QWidget::eventFilter(o, e); } gammaray-2.11.3/plugins/wlcompositorinspector/inspectorwidget.h000066400000000000000000000045241413310443000251260ustar00rootroot00000000000000/* inspectorwidget.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Giulio Camuffo Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_WLCOMPOSITORINSPECTOR_WIDGET_H #define GAMMARAY_WLCOMPOSITORINSPECTOR_WIDGET_H #include #include QT_BEGIN_NAMESPACE class QAbstractItemModel; class QItemSelection; QT_END_NAMESPACE namespace GammaRay { namespace Ui { class InspectorWidget; } class WlCompositorInterface; class LogView; class InspectorWidget : public QWidget { Q_OBJECT public: explicit InspectorWidget(QWidget *parent = nullptr); ~InspectorWidget() override; protected: bool eventFilter(QObject *o, QEvent *e) override; private slots: void delayedInit(); private: void clientSelected(const QItemSelection &selection); void clientContextMenu(QPoint pos); void resourceActivated(const QModelIndex &index); void logMessage(const QByteArray &msg); QScopedPointer m_ui; QAbstractItemModel *m_model; WlCompositorInterface *m_client; LogView *m_logView; }; class InspectorWidgetFactory : public QObject, public StandardToolUiFactory { Q_OBJECT Q_INTERFACES(GammaRay::ToolUiFactory) Q_PLUGIN_METADATA(IID "com.kdab.GammaRay.ToolUiFactory" FILE "gammaray_wlcompositorinspector.json") }; } #endif // GAMMARAY_FONTBROWSERWIDGET_H gammaray-2.11.3/plugins/wlcompositorinspector/inspectorwidget.ui000066400000000000000000000022051413310443000253060ustar00rootroot00000000000000 GammaRay::InspectorWidget 0 0 802 675 -1 Qt::CustomContextMenu QAbstractItemView::SelectRows false gammaray-2.11.3/plugins/wlcompositorinspector/logview.cpp000066400000000000000000000364231413310443000237260ustar00rootroot00000000000000/* logview.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Giulio Camuffo Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "logview.h" #include #include #include #include #include #include #include #include #include "ringbuffer.h" namespace GammaRay { class View : public QWidget { public: explicit View(QWidget *p) : QWidget(p) , m_lines(5000) , m_metrics(QFont()) , m_lineHeight(m_metrics.height()) , m_client(0) { resize(0, 0); setFocusPolicy(Qt::ClickFocus); setCursor(Qt::IBeamCursor); } QSize sizeHint() const override { return size(); } void drawLine(QPainter &painter, const QRect &rect, const QStaticText &line) { painter.setPen(palette().color(QPalette::Text)); painter.drawStaticText(0, rect.y(), line); } void drawLineSelected(QPainter &painter, const QRect &rect, const QStaticText &line) { painter.fillRect(rect, palette().highlight()); painter.setPen(palette().color(QPalette::HighlightedText)); painter.drawStaticText(0, rect.y(), line); } void drawLinePartialSelected(QPainter &painter, const QRect &rect, const QStaticText &line, int startSelectChar, int endSelectChar) { const QString &text = line.text(); int startX = m_metrics.width(text.left(startSelectChar)); int endX = m_metrics.width(text.left(endSelectChar)); if (startSelectChar > 0) { painter.drawText(QRect(rect.x(), rect.y(), startX, rect.height()), Qt::TextDontClip, text.left(startSelectChar)); } QRect selectRect(rect.x() + startX, rect.y(), endX - startX, rect.height()); painter.fillRect(selectRect, palette().highlight()); painter.setPen(palette().color(QPalette::HighlightedText)); painter.drawText(selectRect, Qt::TextDontClip, text.mid(startSelectChar, endSelectChar - startSelectChar)); if (endSelectChar < text.count()) { painter.setPen(palette().color(QPalette::Text)); painter.drawText(QRect(rect.x() + endX, rect.y(), m_metrics.width(text) - endX, rect.height()), text.mid(endSelectChar)); } } void selectionBoundaries(QPoint &start, QPoint &end) const { bool startBeforeEnd = (m_selectionStart.y() < m_selectionEnd.y()) || (m_selectionStart.y() == m_selectionEnd.y() && m_selectionStart.x() < m_selectionEnd.x()); start = startBeforeEnd ? m_selectionStart : m_selectionEnd; end = startBeforeEnd ? m_selectionEnd : m_selectionStart; } struct LineSelection { bool isNull() const { return start == end; } bool isFull() const { return start == 0 && end < 0; } int start; int end; }; LineSelection lineSelection(int line) const { if (m_selectionStart == m_selectionEnd) { return { 0, 0 }; } QPoint start, end; selectionBoundaries(start, end); if (start.y() < line && line < end.y()) { return { 0, m_lines.at(line).text.text().count() }; } if (start.y() == line || end.y() == line) { int startChar = 0; int endChar = m_lines.at(line).text.text().count(); if (start.y() == line) startChar = start.x(); if (end.y() == line) endChar = end.x() + 1; return { startChar, endChar }; } return { 0, 0 }; } void paintEvent(QPaintEvent *event) override { if (m_lineHeight < 0) { return; } QPainter painter(this); QRectF drawRect = event->rect(); int startingLine = lineAt(drawRect.y()); int y = linePosAt(drawRect.y()); for (int i = startingLine; i < m_lines.count(); ++i) { if (m_client && m_lines.at(i).pid != m_client) { continue; } const QStaticText &text = m_lines.at(i).text; QRect lineRect(QRect(0, y, text.size().width(), m_lineHeight)); painter.fillRect(QRectF(0, y, drawRect.width(), m_lineHeight), i % 2 ? palette().base() : palette().alternateBase()); LineSelection selection = lineSelection(i); if (selection.isNull()) { drawLine(painter, lineRect, text); } else if (selection.isFull()) { drawLineSelected(painter, lineRect, text); } else { drawLinePartialSelected(painter, lineRect, text, selection.start, selection.end); } y += m_lineHeight; if (y >= drawRect.bottom()) break; } } inline int linesCount() const { return m_client ? m_linesCount.value(m_client) : m_lines.count(); } inline int linePosAt(int y) const { int line = qMin(y / m_lineHeight, m_lines.count() - 1); return line * m_lineHeight; } inline int lineAt(int y) const { int line = qMin(y / m_lineHeight, m_lines.count() - 1); if (!m_client) { return line; } for (int i = 0, l = 0; i < m_lines.count(); ++i) { if (m_lines.at(i).pid == m_client) { if (l++ == line) { return i; } } } return line; } inline QPoint charPosAt(const QPointF &p) const { int line = lineAt(p.y()); int lineX = 0; const QString &text = m_lines.at(line).text.text(); for (int x = 0, i = 0; i < text.count(); ++i) { const QChar &c = text.at(i); if (p.x() >= x) { lineX = i; } x += m_metrics.width(c); } return {lineX, line}; } void mousePressEvent(QMouseEvent *e) override { if (e->button() == Qt::LeftButton) { m_selectionStart = m_selectionEnd = charPosAt(e->pos()); e->accept(); update(); } } void mouseMoveEvent(QMouseEvent *e) override { m_selectionEnd = charPosAt(e->pos()); e->accept(); update(); } QString selectedText() const { if (m_selectionStart == m_selectionEnd) { return QString(); } QPoint start, end; selectionBoundaries(start, end); QString string; for (int i = start.y(); i <= end.y(); ++i) { if (m_client && m_lines.at(i).pid != m_client) { continue; } const QStaticText &line = m_lines.at(i).text; LineSelection selection = lineSelection(i); string += line.text().mid(selection.start, selection.end - selection.start); string += QLatin1Char('\n'); } return string; } void keyPressEvent(QKeyEvent *e) override { if (e->key() == Qt::Key_C && e->modifiers() == Qt::ControlModifier) { QApplication::clipboard()->setText(selectedText()); } } void resetSelection() { m_selectionStart = m_selectionEnd = QPoint(); update(); } struct Line { quint64 pid = 0; QStaticText text; int *counter = nullptr; Line() = default; Line(quint64 p, const QStaticText &t, int *cnt) : pid(p), text(t), counter(cnt) { (*counter)++; } Line(const Line &l) : pid(l.pid), text(l.text), counter(l.counter) { (*counter)++; } ~Line() { (*counter)--; } Line &operator=(const Line &l) { (*counter)--; pid = l.pid; text = l.text; counter = l.counter; (*counter)++; return *this; } }; RingBuffer m_lines; QHash m_linesCount; QFontMetricsF m_metrics; int m_lineHeight; QPoint m_selectionStart; QPoint m_selectionEnd; quint64 m_client; }; class Messages : public QScrollArea { public: explicit Messages(QWidget *parent) : QScrollArea(parent) , m_view(new View(this)) { m_view->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); setWidget(m_view); setWidgetResizable(true); } void logMessage(quint64 pid, qint64 time, const QByteArray &msg) { auto scrollbar = verticalScrollBar(); bool scroll = scrollbar->value() >= scrollbar->maximum(); add(pid, time, msg); if (scroll) scrollbar->setValue(scrollbar->maximum()); } void reset() { m_view->m_lines.clear(); m_view->resize(0, 0); } void updateSize() { QSizeF lineSize = m_view->m_lines.last().text.size(); int w = m_view->width(); int h = m_view->linesCount() * m_view->m_lineHeight; if (lineSize.width() > w) { w = lineSize.width(); } m_view->resize(w, h); m_view->update(); } void add(quint64 pid, qint64 time, const QByteArray &m) { m_view->m_lines.append(View::Line(pid, QStaticText(QString("[%1ms] %2").arg(QString::number(time / 1e6), QString(m))), &m_view->m_linesCount[pid])); if (m_view->m_client && pid != m_view->m_client) { return; } updateSize(); } void setLoggingClient(quint64 pid) { m_view->m_client = pid; auto scrollbar = verticalScrollBar(); qreal v = (qreal)scrollbar->value() / (qreal)scrollbar->maximum(); m_view->resetSelection(); updateSize(); // keep the scrollbar at he same percentage scrollbar->setValue(v * (qreal)scrollbar->maximum()); } View *m_view; }; class Timeline : public QScrollArea { public: class View : public QWidget { public: struct DataPoint { qint64 time; quint64 pid; QByteArray msg; }; View() : m_data(5000) { resize(100, 100); setAttribute(Qt::WA_OpaquePaintEvent); setMouseTracking(true); } QSize sizeHint() const override { return size(); } inline qint64 initialTime() const { return m_data.isEmpty() ? 0 : m_data.at(0).time; } void paintEvent(QPaintEvent *event) override { QPainter painter(this); QRectF drawRect = event->rect(); const auto palette = this->palette(); painter.fillRect(drawRect, palette.base()); qreal l = 1; qreal step = l / m_zoom; while (step < 60) { l *= 10; step = l / m_zoom; } int substeps = 5; int mul = 2; while (step / substeps > 60) { substeps *= mul; mul = mul == 2 ? 5 : 2; } auto it = initialTime(); auto rit = round(it, -1); //draw the grid lines qreal linesSpacing = step / substeps; int startLine = drawRect.left() / linesSpacing - (rit - it) / m_zoom; //round the starting position so that we have nice numbers' int s = startLine; for (qreal i = startLine * linesSpacing; i < drawRect.right(); i += linesSpacing, s++) { bool isStep = s % substeps == 0; painter.setPen(isStep ? palette.color(QPalette::Highlight) : palette.color(QPalette::Midlight)); int y = 0; if (isStep) { int stepN = s / substeps; y = 15 * (stepN % 2 + 1); } painter.drawLine(i, y, i, drawRect.bottom()); } //draw the text after having drawn all the lines, so we're sure they don't go over it s = startLine; painter.setPen(palette.color(QPalette::Highlight)); for (qreal i = startLine * linesSpacing; i < drawRect.right(); i += step / substeps, s++) { //krazy:exclude=postfixop bool isStep = s % substeps == 0; if (isStep) { painter.drawText(i-100, ((s / substeps) % 2) * 15, 200, 200, Qt::AlignHCenter, QString("%1ms").arg(QString::number(qreal(it + i * m_zoom) / 1e6, 'g', 6))); } } //finally draw the event lines painter.setPen(palette.color(QPalette::Text)); bool hasDrawn = false; for (int i = 0; i < m_data.count(); ++i) { const auto &point = m_data.at(i); if (m_client && point.pid != m_client) { painter.setPen(palette.color(QPalette::Dark)); } else { painter.setPen(palette.color(QPalette::Text)); } qreal offset = point.time - m_start; qreal x = offset / m_zoom; qreal y = qMax(qreal(40.), drawRect.y()); if (!drawRect.contains(QPoint(x, y))) { if (hasDrawn) break; else continue; } hasDrawn = true; painter.drawLine(x, y, x, drawRect.bottom()); } } void mouseMoveEvent(QMouseEvent *e) override { const QPointF &pos = e->localPos(); for (int i = 0; i < m_data.count(); ++i) { qreal timex = (m_data.at(i).time - m_start) / m_zoom; if (fabs(pos.x() - timex) < 2) { setToolTip(m_data.at(i).msg); return; } } } qint64 round(qint64 time, int direction) { qint64 v = time % 200; return time + direction * v; } void updateSize() { if (m_data.count() == 0) return; m_start = round(m_data.at(0).time, -1); m_timespan = round(m_data.last().time, 1) - m_start; resize(m_timespan / m_zoom, height()); } RingBuffer m_data; qreal m_zoom = 100000; qint64 m_start = 0; qint64 m_timespan = 0; quint64 m_client = 0; }; explicit Timeline(QWidget *parent) : QScrollArea(parent) { m_view.setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); setWidget(&m_view); setWidgetResizable(true); m_view.installEventFilter(this); } void logMessage(quint64 pid, qint64 time, const QByteArray &msg) { m_view.m_data.append({ time, pid, msg }); m_view.updateSize(); } void setLoggingClient(quint64 pid) { m_view.m_client = pid; m_view.update(); } bool eventFilter(QObject *o, QEvent *e) override { if (o == &m_view && e->type() == QEvent::Wheel) { QWheelEvent *we = static_cast(e); qreal pos = we->posF().x() * m_view.m_zoom; auto sb = horizontalScrollBar(); int sbvalue = horizontalScrollBar()->value(); m_view.m_zoom += (1. - qPow( 5. / 4., qreal(we->angleDelta().y()) / 150.)) * m_view.m_zoom; if (m_view.m_zoom < 10) { m_view.m_zoom = 10; } m_view.updateSize(); //keep the point under the mouse still, if possible pos = pos / m_view.m_zoom; sb->setValue(sbvalue + (0.5 + pos - we->posF().x())); } return QScrollArea::eventFilter(o, e); } View m_view; }; LogView::LogView(QWidget *p) : QTabWidget(p) , m_messages(new Messages(this)) , m_timeline(new Timeline(this)) { setTabPosition(QTabWidget::West); addTab(m_messages, tr("Messages")); addTab(m_timeline, tr("Timeline")); } QSize LogView::sizeHint() const { return {200, 200}; } void LogView::logMessage(quint64 pid, qint64 time, const QByteArray &msg) { m_messages->logMessage(pid, time, msg); m_timeline->logMessage(pid, time, msg); } void LogView::setLoggingClient(quint64 pid) { m_messages->setLoggingClient(pid); m_timeline->setLoggingClient(pid); } void LogView::reset() { m_messages->reset(); } } gammaray-2.11.3/plugins/wlcompositorinspector/logview.h000066400000000000000000000031101413310443000233560ustar00rootroot00000000000000/* logview.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Giulio Camuffo Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_LOGVIEW_H #define GAMMARAY_LOGVIEW_H #include #include namespace GammaRay { class Messages; class Timeline; class LogView : public QTabWidget { Q_OBJECT public: explicit LogView(QWidget *p); QSize sizeHint() const override; void logMessage(quint64 pid, qint64 time, const QByteArray &msg); void setLoggingClient(quint64 pid); void reset(); private: Messages *m_messages; Timeline *m_timeline; }; } #endif gammaray-2.11.3/plugins/wlcompositorinspector/resourceinfo.cpp000066400000000000000000000122541413310443000247510ustar00rootroot00000000000000/* resourceinfo.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Giulio Camuffo Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "resourceinfo.h" #include #include #include #include #include #include namespace GammaRay { class ResourceInfoExtractors { Q_DECLARE_TR_FUNCTIONS(GammaRay::ResourceInfoExtractors) public: using Function = std::function; ResourceInfoExtractors() { m_infoExtractors[wl_surface_interface.name] = wlsurfaceInfo; m_infoExtractors[wl_shell_surface_interface.name] = wlshellsurfaceInfo; m_infoExtractors[wl_output_interface.name] = wloutputInfo; } Function extractor(wl_resource *res) const { return m_infoExtractors.value(wl_resource_get_class(res)); } static void wlsurfaceInfo(wl_resource *res, QStringList &lines) { QWaylandSurface *surface = QWaylandSurface::fromResource(res); lines << tr("Role: %1").arg(surface->role() ? QString(surface->role()->name()) : QStringLiteral("none")); lines << tr("Buffer size: (%1x%2)").arg(QString::number(surface->size().width()), QString::number(surface->size().height())); #if QT_VERSION < QT_VERSION_CHECK(5, 8, 0) lines << tr("Is mapped: %1").arg(surface->isMapped() ? QStringLiteral("true") : QStringLiteral("false")); #else lines << tr("Has content: %1").arg(surface->hasContent() ? QStringLiteral("true") : QStringLiteral("false")); #endif } static void wlshellsurfaceInfo(wl_resource *res, QStringList &lines) { QWaylandWlShellSurface *ss = QWaylandWlShellSurface::fromResource(res); if (!ss) { return; } lines << tr("Title: \"%1\"").arg(ss->title()); lines << tr("Class name: \"%1\"").arg(ss->className()); ResourceInfo resinfo(ss->surface()->resource()); lines << tr("Surface: %1").arg(resinfo.name()); foreach (const QString &line, resinfo.infoLines()) { lines << QStringLiteral(" ") + line; } } static void wloutputInfo(wl_resource *res, QStringList &lines) { QWaylandOutput *output = QWaylandOutput::fromResource(res); lines << tr("Manufacturer: %1").arg(output->manufacturer()); lines << tr("Model: %1").arg(output->model()); lines << tr("Physical size: (%1x%2)").arg(QString::number(output->physicalSize().width()), QString::number(output->physicalSize().height())); lines << tr("Position: (%1x%2)").arg(QString::number(output->position().x()), QString::number(output->position().y())); #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) const auto mode = output->currentMode(); lines << tr("Current mode: %1x%2@%3Hz").arg(QString::number(mode.size().width()), QString::number(mode.size().height()), QString::number(mode.refreshRate() / 1000.0f)); #endif lines << tr("Scale factor: %1").arg(QString::number(output->scaleFactor())); lines << tr("Transform: %1").arg(QMetaEnum::fromType().valueToKey(output->transform())); lines << tr("Subpixel: %1").arg(QMetaEnum::fromType().valueToKey(output->subpixel())); } QHash m_infoExtractors; }; Q_GLOBAL_STATIC(ResourceInfoExtractors, s_infoExtractors) ResourceInfo::ResourceInfo(wl_resource *resource) : m_resource(resource) { } uint32_t ResourceInfo::id() const { return wl_resource_get_id(m_resource); } const char *ResourceInfo::interfaceName() const { return wl_resource_get_class(m_resource); } bool ResourceInfo::isInterface(const wl_interface *iface) const { return strcmp(interfaceName(), iface->name) == 0; } QString ResourceInfo::name() const { return QString("%1@%2").arg(interfaceName(), QString::number(id())); } QString ResourceInfo::info() const { const QStringList lines = infoLines(); QString str; for (const QString &line : lines) { if (!str.isEmpty()) { str += QLatin1Char('\n'); } str += line; } return str; } QStringList ResourceInfo::infoLines() const { QStringList lines; lines << tr("Version: %1").arg(QString::number(wl_resource_get_version(m_resource))); if (const auto &func = s_infoExtractors->extractor(m_resource)) { func(m_resource, lines); } return lines; } } gammaray-2.11.3/plugins/wlcompositorinspector/resourceinfo.h000066400000000000000000000032411413310443000244120ustar00rootroot00000000000000/* resourceinfo.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Giulio Camuffo Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_RESOURCEINFO_H #define GAMMARAY_RESOURCEINFO_H #include #include struct wl_resource; struct wl_interface; namespace GammaRay { class ResourceInfo { Q_DECLARE_TR_FUNCTIONS(GammaRay::ResourceInfo) public: explicit ResourceInfo(wl_resource *resource); uint32_t id() const; const char *interfaceName() const; bool isInterface(const wl_interface *iface) const; QString name() const; QString info() const; QStringList infoLines() const; private: wl_resource *m_resource; }; } #endif gammaray-2.11.3/plugins/wlcompositorinspector/ringbuffer.h000066400000000000000000000037761413310443000240550ustar00rootroot00000000000000/* ringbuffer.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Giulio Camuffo Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_RINGBUFFER_H #define GAMMARAY_RINGBUFFER_H #include namespace GammaRay { template struct RingBuffer { RingBuffer(int capacity) : m_head(0) , m_capacity(capacity) { Q_ASSERT(capacity > 0); } int count() const { return qMin(m_capacity, m_vector.count()); } int capacity() const { return m_capacity; } bool isEmpty() const { return count() == 0; } const T &last() const { return at(count() - 1); } void append(const T &t) { if (m_vector.count() == m_capacity) { m_vector[m_head++] = t; if (m_head >= m_capacity) m_head = 0; } else { m_vector.append(t); } } const T &at(int i) const { int index = (i + m_head) % m_capacity; return m_vector.at(index); } void clear() { m_vector.clear(); m_head = 0; } QVector m_vector; int m_head; int m_capacity; }; } #endif gammaray-2.11.3/plugins/wlcompositorinspector/wlcompositorclient.cpp000066400000000000000000000035171413310443000262100ustar00rootroot00000000000000/* wlcompositorclient.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Giulio Camuffo Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "wlcompositorclient.h" #include namespace GammaRay { WlCompositorClient::WlCompositorClient(QObject *p) : WlCompositorInterface(p) { } void WlCompositorClient::connected() { Endpoint::instance()->invokeObject(objectName(), "connected"); } void WlCompositorClient::disconnected() { Endpoint::instance()->invokeObject(objectName(), "disconnected"); } void WlCompositorClient::setSelectedClient(int index) { Endpoint::instance()->invokeObject(objectName(), "setSelectedClient", QVariantList() << index); } void WlCompositorClient::setSelectedResource(uint32_t id) { Endpoint::instance()->invokeObject(objectName(), "setSelectedResource", QVariantList() << id); } } gammaray-2.11.3/plugins/wlcompositorinspector/wlcompositorclient.h000066400000000000000000000031221413310443000256450ustar00rootroot00000000000000/* wlcompositorclient.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Giulio Camuffo Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef WLCOMPOSITORCLIENT_H #define WLCOMPOSITORCLIENT_H #include "wlcompositorinterface.h" namespace GammaRay { class WlCompositorClient : public WlCompositorInterface { Q_OBJECT Q_INTERFACES(GammaRay::WlCompositorInterface) public: explicit WlCompositorClient(QObject *p); public slots: void connected() override; void disconnected() override; void setSelectedClient(int index) override; void setSelectedResource(uint32_t id) override; }; } #endif gammaray-2.11.3/plugins/wlcompositorinspector/wlcompositorinspector.cpp000066400000000000000000000447001413310443000267370ustar00rootroot00000000000000/* wlcompositorinspector.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Giulio Camuffo Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "wlcompositorinspector.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include <3rdparty/kde/krecursivefilterproxymodel.h> #include #include #include "clientsmodel.h" #include "ringbuffer.h" #include "resourceinfo.h" namespace GammaRay { class SurfaceView : public RemoteViewServer { public: SurfaceView(QObject *parent) : RemoteViewServer(QStringLiteral("com.kdab.GammaRay.WaylandCompositorSurfaceView"), parent) , m_surface(nullptr) { connect(this, &RemoteViewServer::requestUpdate, this, &SurfaceView::sendSurfaceFrame); } void setSurface(QWaylandSurface *surface) { if (m_surface == surface) { return; } if (m_surface) { disconnect(m_surface, &QWaylandSurface::redraw, this, &SurfaceView::redraw); } m_surface = surface; if (surface) { connect(surface, &QWaylandSurface::redraw, this, &SurfaceView::redraw); } redraw(); } void redraw() { if (!m_surface) { setNewFrame(QImage()); return; } auto *grabber = new QWaylandSurfaceGrabber(m_surface); connect(grabber, &QWaylandSurfaceGrabber::success, this, [grabber, this](const QImage &img) { setNewFrame(img); grabber->deleteLater(); }); connect(grabber, &QWaylandSurfaceGrabber::failed, this, [grabber, this](QWaylandSurfaceGrabber::Error error) { qWarning() << "Failed to grab surface." << error; grabber->deleteLater(); setNewFrame(QImage()); }); grabber->grab(); } void setNewFrame(const QImage &img) { m_frame = img; sourceChanged(); } void sendSurfaceFrame() { RemoteViewFrame frame; frame.setImage(m_frame); frame.setSceneRect(QRect(0, 0, m_frame.width(), m_frame.height())); frame.setViewRect(QRect(0, 0, m_frame.width(), m_frame.height())); sendFrame(frame); } QWaylandSurface *m_surface; QImage m_frame; }; class Logger : public QObject { public: enum class MessageType { Request = WL_PROTOCOL_LOGGER_REQUEST, Event = WL_PROTOCOL_LOGGER_EVENT, }; Logger(WlCompositorInspector *inspector, QObject *parent) : QObject(parent) , m_lines(5000) , m_connected(false) , m_inspector(inspector) { m_timer.start(); } void add(wl_resource *res, MessageType dir, const QString &line) { pid_t pid; wl_client_get_credentials(wl_resource_get_client(res), &pid, nullptr, nullptr); QString l = QStringLiteral("%1 %2 %3").arg(QString::number(pid), dir == MessageType::Request ? QLatin1String("->") : QLatin1String("<-"), line); // we use QByteArray rather than QString because the log has mostly (only) latin characters // so we save some space using utf8 rather than the utf16 QString uses QByteArray utf8 = l.toUtf8(); qint64 time = m_timer.nsecsElapsed(); m_lines.append({ time, pid, utf8 }); if (m_connected) { emit m_inspector->logMessage(pid, time, utf8); } } void setCurrentClient(QWaylandClient *client) { emit m_inspector->setLoggingClient(client ? client->processId() : 0); } void setConnected(bool c) { m_connected = c; for (int i = 0; i < m_lines.count(); ++i) { const Message &m = m_lines.at(i); emit m_inspector->logMessage(m.pid, m.time, m.line); } } struct Message { qint64 time; pid_t pid; QByteArray line; }; RingBuffer m_lines; bool m_connected; WlCompositorInspector *m_inspector; QElapsedTimer m_timer; }; class ResourcesModel : public QAbstractItemModel { struct ClientListener { wl_listener l; ResourcesModel *m; }; public: struct Resource { wl_listener destroyListener; wl_resource *resource; ResourcesModel *model; QVector children; Resource *parent; int depth; static void destroyed(wl_listener *listener, void *) { Resource *res = reinterpret_cast(listener); res->model->removeResource(res->resource); } static Resource *fromWlResource(wl_resource *res) { wl_listener *listener = wl_resource_get_destroy_listener(res, destroyed); return reinterpret_cast(listener); } }; ResourcesModel() { m_listener.m = nullptr; wl_list_init(&m_listener.l.link); } void setClient(QWaylandClient *client) { beginResetModel(); clear(); endResetModel(); wl_list_remove(&m_listener.l.link); wl_list_init(&m_listener.l.link); m_client = client; if (client) { wl_client_add_resource_created_listener(client->client(), &m_listener.l); m_listener.m = this; m_listener.l.notify = [](wl_listener *listener, void *data) { wl_resource *resource = static_cast(data); ResourcesModel *model = reinterpret_cast(listener)->m; model->addResource(resource); }; wl_client_for_each_resource(client->client(), [](wl_resource *res, void *ud) { ResourcesModel *model = static_cast(ud); model->addResource(res); return WL_ITERATOR_CONTINUE; }, this); } } QWaylandClient *client() const { return m_client; } ~ResourcesModel() override { clear(); } void clear() { for (Resource *res : qAsConst(m_resources)) { destroy(res); } m_resources.clear(); } void destroy(Resource *res) { for (Resource *child : qAsConst(res->children)) { destroy(child); } wl_list_remove(&res->destroyListener.link); delete res; } wl_resource *resource(uint32_t id) { return wl_client_get_object(m_client->client(), id); } QModelIndex index(Resource *res) const { if (res->parent) { return index(res->parent->children.indexOf(res), 0, index(res->parent)); } return index(m_resources.indexOf(res), 0, QModelIndex()); } QModelIndex index(int row, int column, const QModelIndex &parent) const override { Resource *parentres = exists(parent) ? static_cast(parent.internalPointer()) : nullptr; const auto &resources = parentres ? parentres->children : m_resources; if (resources.count() <= row) { return {}; } Resource *res = resources.at(row); return createIndex(row, column, res); } QModelIndex parent(const QModelIndex &idx) const override { Resource *res = static_cast(idx.internalPointer()); if (exists(idx) && res->parent) { return index(res->parent); } return {}; } int row(Resource *res) const { if (res->parent) { return res->parent->children.indexOf(res); } return m_resources.indexOf(res); } void addResource(wl_resource *res) { wl_resource *parentResource = nullptr; // wl_resource_get_parent(res); Resource *parent = parentResource ? Resource::fromWlResource(parentResource) : nullptr; int count = parent ? parent->children.count() : m_resources.count(); beginInsertRows(parent ? index(parent) : QModelIndex(), count, count); auto *r = new Resource; r->parent = parent; r->resource = res; r->depth = parent ? parent->depth + 1 : 0; r->model = this; wl_resource_add_destroy_listener(res, &r->destroyListener); r->destroyListener.notify = Resource::destroyed; if (parent) { parent->children << r; } else { m_resources << r; } m_allResources << r; endInsertRows(); } void removeResource(wl_resource *wlres) { Resource *r = Resource::fromWlResource(wlres); int idx = r->parent ? r->parent->children.indexOf(r) : m_resources.indexOf(r); beginRemoveRows(r->parent ? index(r->parent) : QModelIndex(), idx, idx); if (r->parent) { r->parent->children.removeAt(idx); } else { m_resources.removeAt(idx); } m_allResources.remove(r); wl_list_remove(&r->destroyListener.link); delete r; endRemoveRows(); } bool exists(const QModelIndex &index) const { auto *p = static_cast(index.internalPointer()); return !p || m_allResources.contains(p); } int rowCount(const QModelIndex &index) const override { if (!exists(index)) return 0; Resource *res = static_cast(index.internalPointer()); return res ? res->children.count() : m_resources.count(); } int columnCount(const QModelIndex &) const override { return 1; } QVariant data(const QModelIndex &index, int role) const override { if (!exists(index)) return QVariant(); const Resource *resource = static_cast(index.internalPointer()); wl_resource *res = resource->resource; ResourceInfo info(res); switch (role) { case Qt::DisplayRole: return info.name(); case Qt::ToolTipRole: return info.info(); case Qt::UserRole + 2: return info.id(); } return QVariant(); } QMap itemData(const QModelIndex &index) const override { QMap map; auto insertRole = [&](int role) { map[role] = data(index, role); }; insertRole(Qt::DisplayRole); insertRole(Qt::ToolTipRole); insertRole(Qt::UserRole + 2); return map; } QVariant headerData(int section, Qt::Orientation orientation, int) const override { if (orientation == Qt::Horizontal) { return QStringLiteral("Resources"); } return QString::number(section + 1); } QVector m_resources; QSet m_allResources; ClientListener m_listener; QWaylandClient *m_client = nullptr; }; WlCompositorInspector::WlCompositorInspector(Probe *probe, QObject *parent) : WlCompositorInterface(parent) , m_compositor(nullptr) , m_surfaceView(new SurfaceView(this)) { qWarning() << "init probe" << probe->objectTreeModel() << probe; MetaObject *mo = nullptr; MO_ADD_METAOBJECT1(QWaylandObject, QObject); MO_ADD_METAOBJECT1(QWaylandCompositor, QWaylandObject); m_clientsModel = new ClientsModel(this); probe->registerModel(QStringLiteral("com.kdab.GammaRay.WaylandCompositorClientsModel"), m_clientsModel); m_clientSelectionModel = ObjectBroker::selectionModel(m_clientsModel); m_resourcesModel = new ResourcesModel; probe->registerModel(QStringLiteral("com.kdab.GammaRay.WaylandCompositorResourcesModel"), m_resourcesModel); m_logger = new Logger(this, this); connect(probe, &Probe::objectCreated, this, &WlCompositorInspector::objectAdded); connect(probe, &Probe::objectSelected, this, &WlCompositorInspector::objectSelected); } WlCompositorInspector::~WlCompositorInspector() = default; struct ClientsListener { wl_listener listener; WlCompositorInspector *inspector; }; void WlCompositorInspector::objectAdded(QObject *obj) { if (auto *compositor = qobject_cast(obj)) { init(compositor); } } void WlCompositorInspector::objectSelected(QObject *obj) { if (auto client = qobject_cast(obj)) { const auto indexList = m_clientsModel->match(m_clientsModel->index(0, 0), ClientsModel::ObjectIdRole, QVariant::fromValue(ObjectId(client)), 1, Qt::MatchExactly | Qt::MatchRecursive | Qt::MatchWrap); if (indexList.isEmpty()) return; const auto index = indexList.first(); m_clientSelectionModel->select(index, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows | QItemSelectionModel::Current); } } /* this comes from wayland */ struct argument_details { char type; int nullable; }; static const char * get_next_argument(const char *signature, struct argument_details *details) { details->nullable = 0; for (; *signature; ++signature) { switch(*signature) { case 'i': case 'u': case 'f': case 's': case 'o': case 'n': case 'a': case 'h': details->type = *signature; return signature + 1; case '?': details->nullable = 1; } } details->type = '\0'; return signature; } /* --- */ void WlCompositorInspector::init(QWaylandCompositor *compositor) { qWarning() << "found compositor" << compositor; m_compositor = compositor; wl_display *dpy = compositor->display(); wl_display_add_protocol_logger(dpy, [](void *ud, wl_protocol_logger_type type, const wl_protocol_logger_message *message) { auto *resource = message->resource; QString line = QString("%1.%2(").arg(ResourceInfo(resource).name(), message->message->name); const char *signature = message->message->signature; for (int i = 0; i < message->arguments_count; ++i) { const auto &arg = message->arguments[i]; argument_details details; signature = get_next_argument(signature, &details); if (i > 0) { line += QLatin1String(", "); } switch (details.type) { case 'u': line += QString::number(arg.u); break; case 'i': line += QString::number(arg.i); break; case 'f': line += QString::number(wl_fixed_to_double(arg.f)); break; case 's': line += QString("\"%1\"").arg(arg.s); break; case 'o': { wl_resource *r = (wl_resource *)arg.o; line += r ? ResourceInfo(r).name() : QLatin1String("(nil)"); break; } case 'n': { const auto *type = message->message->types[i]; line += QString("new id %1@%2").arg(type ? type->name : "[unknown]", arg.n ? QString::number(arg.n) : QStringLiteral("nil")); break; } case 'a': line += QStringLiteral("array"); break; case 'h': line += QString::number(arg.h); break; } } line += QLatin1Char(')'); static_cast(ud)->m_logger->add(resource, (Logger::MessageType)type, line); }, this); wl_list *clients = wl_display_get_client_list(dpy); wl_client *client; wl_client_for_each(client, clients) { addClient(client); } auto *listener = new ClientsListener; wl_display_add_client_created_listener(dpy, &listener->listener); listener->listener.notify = [](wl_listener *listener, void *data) { wl_client *client = static_cast(data); reinterpret_cast(listener)->inspector->addClient(client); }; listener->inspector = this; } void WlCompositorInspector::addClient(wl_client *c) { QWaylandClient *client = QWaylandClient::fromWlClient(m_compositor, c); QString pid = QString::number(client->processId()); qWarning() << "client" << client << pid; connect(client, &QObject::destroyed, this, [this, pid, client](QObject *) { if (m_resourcesModel->client() == client) { m_resourcesModel->setClient(nullptr); } m_clientsModel->removeClient(client); }); m_clientsModel->addClient(client); } void WlCompositorInspector::connected() { m_logger->setConnected(true); } void WlCompositorInspector::disconnected() { m_logger->setConnected(false); } void WlCompositorInspector::setSelectedClient(int index) { auto client = index >= 0 ? m_clientsModel->client(index) : nullptr; if (client != m_resourcesModel->client()) { m_resourcesModel->setClient(client); m_logger->setCurrentClient(client); } } void WlCompositorInspector::setSelectedResource(uint id) { wl_resource *res = m_resourcesModel->resource(id); QWaylandSurface *surface = nullptr; if (res && ResourceInfo(res).isInterface(&wl_surface_interface)) { surface = QWaylandSurface::fromResource(res); } m_surfaceView->setSurface(surface); } } gammaray-2.11.3/plugins/wlcompositorinspector/wlcompositorinspector.h000066400000000000000000000053631413310443000264060ustar00rootroot00000000000000/* wlcompositorinspector.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Giulio Camuffo Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_WLCOMPOSITORINSPECTOR_WLCOMPOSITORINSPECTOR_H #define GAMMARAY_WLCOMPOSITORINSPECTOR_WLCOMPOSITORINSPECTOR_H #include #include #include #include "wlcompositorinterface.h" QT_BEGIN_NAMESPACE class QItemSelectionModel; QT_END_NAMESPACE struct wl_client; namespace GammaRay { class ClientsModel; class Logger; class ResourcesModel; class SurfaceView; class WlCompositorInspector: public WlCompositorInterface { Q_OBJECT Q_INTERFACES(GammaRay::WlCompositorInterface) public: explicit WlCompositorInspector(Probe *probe, QObject *parent = nullptr); ~WlCompositorInspector() override; public slots: void connected() override; void disconnected() override; void setSelectedClient(int index) override; void setSelectedResource(uint id) override; private slots: void objectAdded(QObject *obj); void objectSelected(QObject *obj); private: void init(QWaylandCompositor *compositor); void addClient(wl_client *c); QWaylandCompositor *m_compositor; ClientsModel *m_clientsModel; QItemSelectionModel *m_clientSelectionModel; Logger *m_logger; ResourcesModel *m_resourcesModel; SurfaceView *m_surfaceView; }; class WlCompositorInspectorFactory: public QObject, public StandardToolFactory { Q_OBJECT Q_INTERFACES(GammaRay::ToolFactory) Q_PLUGIN_METADATA(IID "com.kdab.GammaRay.ToolFactory" FILE "gammaray_wlcompositorinspector.json") public: explicit WlCompositorInspectorFactory(QObject *parent = nullptr) : QObject(parent) { } }; } #endif gammaray-2.11.3/plugins/wlcompositorinspector/wlcompositorinterface.cpp000066400000000000000000000026611413310443000266710ustar00rootroot00000000000000/* wlcompositorinterface.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Giulio Camuffo Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "wlcompositorinterface.h" #include namespace GammaRay { WlCompositorInterface::WlCompositorInterface(QObject *parent) : QObject(parent) { ObjectBroker::registerObject(this); } WlCompositorInterface::~WlCompositorInterface() = default; } gammaray-2.11.3/plugins/wlcompositorinspector/wlcompositorinterface.h000066400000000000000000000036271413310443000263410ustar00rootroot00000000000000/* wlcompositorinterface.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Giulio Camuffo Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef WLCOMPOSITORINTERFACE_H #define WLCOMPOSITORINTERFACE_H #include #include #ifdef HAVE_STDINT_H #include #endif namespace GammaRay { class WlCompositorInterface : public QObject { Q_OBJECT public: explicit WlCompositorInterface(QObject *parent); ~WlCompositorInterface() override; public slots: virtual void connected() = 0; virtual void disconnected() = 0; virtual void setSelectedClient(int index) = 0; virtual void setSelectedResource(uint id) = 0; signals: void logMessage(quint64 pid, qint64 time, const QByteArray &msg); void setLoggingClient(quint64 pid); void resetLog(); }; } QT_BEGIN_NAMESPACE Q_DECLARE_INTERFACE(GammaRay::WlCompositorInterface, "com.kdab.GammaRay.WlCompositor") QT_END_NAMESPACE #endif gammaray-2.11.3/probe/000077500000000000000000000000001413310443000144745ustar00rootroot00000000000000gammaray-2.11.3/probe/CMakeLists.txt000066400000000000000000000073641413310443000172460ustar00rootroot00000000000000if (NOT GAMMARAY_CLIENT_ONLY_BUILD) set(gammaray_probe_srcs probecreator.cpp hooks.cpp ) list(APPEND gammaray_probe_srcs entry_unix.cpp) # create intermediate gammaray_probe object files which unit tests can use add_library(gammaray_probe_obj OBJECT ${gammaray_probe_srcs}) set_target_properties(gammaray_probe_obj PROPERTIES POSITION_INDEPENDENT_CODE ON) target_compile_features(gammaray_probe_obj PUBLIC ${GAMMARAY_REQUIRED_CXX_FEATURES}) target_compile_definitions(gammaray_probe_obj PRIVATE ${Qt5Core_DEFINITIONS}) # for -DQT_NAMESPACE=... target_include_directories(gammaray_probe_obj PUBLIC $ $ ) target_include_directories(gammaray_probe_obj SYSTEM PRIVATE ${Qt5Core_PRIVATE_INCLUDE_DIRS}) # probe lib # DYLD_INSERT_LIBRARIES only works with real shared libraries, not modules # Windows also needs SHARED rather than MODULE here, to end up in bin/ add_library(gammaray_probe ${GAMMARAY_LIBRARY_TYPE} $) target_link_libraries(gammaray_probe gammaray_core ${CMAKE_DL_LIBS}) set_target_properties(gammaray_probe PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${GAMMARAY_OUTPUT_PREFIX}/${PROBE_INSTALL_DIR}" RUNTIME_OUTPUT_DIRECTORY "${GAMMARAY_OUTPUT_PREFIX}/${PROBE_INSTALL_DIR}" ) target_compile_features(gammaray_probe PUBLIC ${GAMMARAY_REQUIRED_CXX_FEATURES}) if(WIN32) add_library(gammaray_winloader SHARED entry_win.cpp) set_target_properties(gammaray_winloader PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${GAMMARAY_OUTPUT_PREFIX}/${PROBE_INSTALL_DIR}" RUNTIME_OUTPUT_DIRECTORY "${GAMMARAY_OUTPUT_PREFIX}/${PROBE_INSTALL_DIR}" ) target_compile_features(gammaray_winloader PUBLIC ${GAMMARAY_REQUIRED_CXX_FEATURES}) endif() if(NOT GAMMARAY_INSTALL_QT_LAYOUT) set_target_properties(gammaray_probe PROPERTIES PREFIX "") gammaray_set_rpath(gammaray_probe ${PROBE_INSTALL_DIR}) install(TARGETS gammaray_probe DESTINATION ${PROBE_INSTALL_DIR}) if(WIN32) set_target_properties(gammaray_winloader PROPERTIES PREFIX "") install(TARGETS gammaray_winloader DESTINATION ${PROBE_INSTALL_DIR}) endif() else() set_target_properties(gammaray_probe PROPERTIES OUTPUT_NAME gammaray_probe-${GAMMARAY_PROBE_ABI}) gammaray_set_rpath(gammaray_probe ${LIB_INSTALL_DIR}) if(WIN32) set_target_properties(gammaray_winloader PROPERTIES OUTPUT_NAME gammaray_winloader-${GAMMARAY_PROBE_ABI}) install(TARGETS gammaray_winloader ${INSTALL_TARGETS_DEFAULT_ARGS}) endif() if(NOT GAMMARAY_PROBE_ONLY_BUILD) install(TARGETS gammaray_probe EXPORT GammaRayTargets ${INSTALL_TARGETS_DEFAULT_ARGS}) ecm_generate_pri_file(BASE_NAME GammaRayProbe LIB_NAME gammaray_probe-${GAMMARAY_PROBE_ABI} DEPS "core gui network GammaRayCommon GammaRayCore" FILENAME_VAR PRI_FILENAME INCLUDE_INSTALL_DIR ${INCLUDE_INSTALL_DIR}) install(FILES ${PRI_FILENAME} DESTINATION ${ECM_MKSPECS_INSTALL_DIR}) else() install(TARGETS gammaray_probe ${INSTALL_TARGETS_DEFAULT_ARGS}) endif() if (ANDROID) if (Qt5Core_VERSION VERSION_LESS 5.14.0) install(FILES gammaray_probe-android-dependencies.xml RENAME gammaray_probe-${GAMMARAY_PROBE_ABI}-android-dependencies.xml DESTINATION ${LIB_INSTALL_DIR}) else() install(FILES gammaray_probe-android-dependencies.xml RENAME gammaray_probe-${GAMMARAY_PROBE_ABI}_${ANDROID_ABI}-android-dependencies.xml DESTINATION ${LIB_INSTALL_DIR}) endif() endif() endif() if(MSVC) install(FILES "$/$" DESTINATION ${BIN_INSTALL_DIR} CONFIGURATIONS Debug RelWithDebInfo) endif() endif() # GAMMARAY_CLIENT_ONLY_BUILD gammaray-2.11.3/probe/entry_unix.cpp000066400000000000000000000026161413310443000174110ustar00rootroot00000000000000/* entry_unix.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "hooks.h" using namespace GammaRay; // we need a way to execute some code upon load, so let's abuse // static initialization class HitMeBabyOneMoreTime { public: HitMeBabyOneMoreTime() { Hooks::installHooks(); } }; static HitMeBabyOneMoreTime britney; gammaray-2.11.3/probe/entry_win.cpp000066400000000000000000000051671413310443000172270ustar00rootroot00000000000000/* entry_win.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include #include typedef void (*gammaray_probe_inject)(void); namespace { static const std::wstring LOADER_NAME = L"gammaray_winloader"; static const std::string PROBE_NAME = GAMMARAY_PROBE_BASENAME; } extern "C" BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID /*lpvReserved*/) { switch (dwReason) { case DLL_PROCESS_ATTACH: { wchar_t buffer[MAX_PATH * 2]; const int size = GetModuleFileNameW(hInstance, buffer, MAX_PATH * 2); if (!size) { OutputDebugStringW(L"GammaRay: GetModuleFileNameW failed"); break; } const std::wstring probeName(PROBE_NAME.cbegin(), PROBE_NAME.cend()); std::wstring path(buffer, size); path.replace(path.find(LOADER_NAME), LOADER_NAME.length(), probeName); HMODULE probe = GetModuleHandleW(path.c_str()); if (!probe) { probe = LoadLibraryW(path.c_str()); if (!probe) { OutputDebugStringW(L"GammaRay: Failed to load: "); OutputDebugStringW(path.c_str()); break; } } gammaray_probe_inject inject = (gammaray_probe_inject)GetProcAddress(probe, "gammaray_probe_inject"); if (!inject) { OutputDebugStringW(L"GammaRay: Failed to resolve gammaray_probe_inject"); break; } inject(); } break; } // return false to get unloaded return FALSE; // krazy:exclude=captruefalse } gammaray-2.11.3/probe/gammaray_probe-android-dependencies.xml000066400000000000000000000002021413310443000242370ustar00rootroot00000000000000 gammaray-2.11.3/probe/hooks.cpp000066400000000000000000000110601413310443000163210ustar00rootroot00000000000000/* hooks.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2012-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ // krazy:excludeall=cpp due to low-level stuff in here #include #include "hooks.h" #include "probecreator.h" #include #include #include #include //cannot use cstdio on QNX6.6 #include #ifdef Q_OS_MAC #include #include #include #include #elif defined(Q_OS_WIN) #include #endif #define IF_DEBUG(x) using namespace GammaRay; static void log_injection(const char *msg) { #ifdef Q_OS_WIN OutputDebugStringA(msg); #else #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wformat-security" printf(msg); // NOLINT clang-tidy #pragma GCC diagnostic pop #endif } static void gammaray_pre_routine() { #ifdef Q_OS_WIN if (qApp) // DllMain will do a better job at this, we are too early here and might not even have our staticMetaObject properly resolved return; #endif new ProbeCreator(ProbeCreator::Create | ProbeCreator::FindExistingObjects); } Q_COREAPP_STARTUP_FUNCTION(gammaray_pre_routine) // previously installed Qt hooks, for daisy-chaining static void (*gammaray_next_startup_hook)() = nullptr; static void (*gammaray_next_addObject)(QObject *) = nullptr; static void (*gammaray_next_removeObject)(QObject *) = nullptr; extern "C" Q_DECL_EXPORT void gammaray_startup_hook() { Probe::startupHookReceived(); new ProbeCreator(ProbeCreator::Create); if (gammaray_next_startup_hook) gammaray_next_startup_hook(); } extern "C" Q_DECL_EXPORT void gammaray_addObject(QObject *obj) { Probe::objectAdded(obj, true); if (gammaray_next_addObject) gammaray_next_addObject(obj); } extern "C" Q_DECL_EXPORT void gammaray_removeObject(QObject *obj) { Probe::objectRemoved(obj); if (gammaray_next_removeObject) gammaray_next_removeObject(obj); } static void installQHooks() { Q_ASSERT(qtHookData[QHooks::HookDataVersion] >= 1); Q_ASSERT(qtHookData[QHooks::HookDataSize] >= 6); gammaray_next_addObject = reinterpret_cast(qtHookData[QHooks::AddQObject]); gammaray_next_removeObject = reinterpret_cast(qtHookData[QHooks::RemoveQObject]); gammaray_next_startup_hook = reinterpret_cast(qtHookData[QHooks::Startup]); qtHookData[QHooks::AddQObject] = reinterpret_cast(&gammaray_addObject); qtHookData[QHooks::RemoveQObject] = reinterpret_cast(&gammaray_removeObject); qtHookData[QHooks::Startup] = reinterpret_cast(&gammaray_startup_hook); } bool Hooks::hooksInstalled() { return qtHookData[QHooks::AddQObject] == reinterpret_cast(&gammaray_addObject); } void Hooks::installHooks() { if (hooksInstalled()) return; installQHooks(); } extern "C" Q_DECL_EXPORT void gammaray_probe_inject() { if (!qApp) { return; } Hooks::installHooks(); log_injection("gammaray_probe_inject()\n"); new ProbeCreator(ProbeCreator::Create | ProbeCreator::FindExistingObjects); } extern "C" Q_DECL_EXPORT void gammaray_probe_attach() { if (!qApp) { return; } log_injection("gammaray_probe_attach()\n"); new ProbeCreator(ProbeCreator::Create | ProbeCreator::FindExistingObjects | ProbeCreator::ResendServerAddress); } extern "C" Q_DECL_EXPORT void gammaray_install_hooks() { Hooks::installHooks(); } gammaray-2.11.3/probe/hooks.h000066400000000000000000000042751413310443000160000ustar00rootroot00000000000000/* hooks.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_HOOKS_H #define GAMMARAY_HOOKS_H #include QT_BEGIN_NAMESPACE class QObject; QT_END_NAMESPACE extern "C" { extern Q_DECL_EXPORT void gammaray_startup_hook(); extern Q_DECL_EXPORT void gammaray_addObject(QObject *obj); extern Q_DECL_EXPORT void gammaray_removeObject(QObject *obj); /** Entry point for startup injection. */ extern Q_DECL_EXPORT void gammaray_probe_inject(); /** Entry point for runtime attaching. * This differs from the above by also attempting to re-send * the server address to the launcher. So only use this if you * are sure there is a launcher ready to receive this information * on the other side. */ extern Q_DECL_EXPORT void gammaray_probe_attach(); /** Entry point for static injections. */ extern Q_DECL_EXPORT void gammaray_install_hooks(); } namespace GammaRay { namespace Hooks { /** Returns @c true if we have installed the hooks. * This is useful to avoid loops from preloaded hooks for example. */ bool hooksInstalled(); /** Install hooks, either by function overwriting or using qhooks. */ void installHooks(); } } #endif gammaray-2.11.3/probe/probecreator.cpp000066400000000000000000000061001413310443000176640ustar00rootroot00000000000000/* probecreator.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2012-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Kevin Funk Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include "probecreator.h" #include #include #include #include #include #include #include #define IF_DEBUG(x) using namespace GammaRay; using namespace std; ProbeCreator::ProbeCreator(CreateFlags flags) : m_flags(flags) { // push object into the main thread, as windows creates a // different thread where this runs in moveToThread(QCoreApplication::instance()->thread()); // delay to foreground thread QMetaObject::invokeMethod(this, "createProbe", Qt::QueuedConnection); // don't propagate the probe to child processes if (qgetenv("GAMMARAY_UNSET_PRELOAD") == "1") qputenv("LD_PRELOAD", ""); if (qgetenv("GAMMARAY_UNSET_DYLD") == "1") qputenv("DYLD_INSERT_LIBRARIES", ""); // HACK the webinspector plugin does this as well, but if the web view is created // too early the env var from there isn't going to reach the web process qputenv("QTWEBKIT_INSPECTOR_SERVER", QByteArray(GAMMARAY_DEFAULT_ANY_ADDRESS) + ':' + QByteArray::number(Endpoint::defaultPort() + 1)); qputenv("QTWEBENGINE_REMOTE_DEBUGGING", QByteArray(GAMMARAY_DEFAULT_ANY_ADDRESS) + ':' + QByteArray::number(Endpoint::defaultPort() + 1)); } void ProbeCreator::createProbe() { if (!qApp) { deleteLater(); return; } // make sure we are in the ui thread Q_ASSERT(QThread::currentThread() == qApp->thread()); if (Probe::isInitialized()) { // already exists, so we are trying to re-attach to an already injected process if (m_flags & ResendServerAddress) { printf("Resending server address...\n"); Probe::instance()->resendServerAddress(); } deleteLater(); return; } Probe::createProbe(m_flags & FindExistingObjects); Q_ASSERT(Probe::isInitialized()); deleteLater(); } gammaray-2.11.3/probe/probecreator.h000066400000000000000000000033431413310443000173370ustar00rootroot00000000000000/* probecreator.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2012-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Kevin Funk Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_PROBECREATOR_H #define GAMMARAY_PROBECREATOR_H #include namespace GammaRay { /** * Creates Probe instance in main thread and deletes self afterwards. */ class ProbeCreator : public QObject { Q_OBJECT public: enum CreateFlag { Create = 0, FindExistingObjects = 1, ResendServerAddress = 2 }; Q_DECLARE_FLAGS(CreateFlags, CreateFlag) explicit ProbeCreator(CreateFlags flags); private slots: void createProbe(); private: CreateFlags m_flags; }; } Q_DECLARE_OPERATORS_FOR_FLAGS(GammaRay::ProbeCreator::CreateFlags) #endif // GAMMARAY_PROBECREATOR_H gammaray-2.11.3/qt-ci-wrapper/000077500000000000000000000000001413310443000160605ustar00rootroot00000000000000gammaray-2.11.3/qt-ci-wrapper/qt-ci-wrapper.pro000066400000000000000000000037641413310443000213070ustar00rootroot00000000000000# implementation detail of the qmake -> cmake wrapper for the Qt CI system # # DO NOT USE THIS DIRECTLY! TEMPLATE = aux # convert path and command separators to platform format SHELL_INSTALL_PREFIX = $$shell_path($$[QT_INSTALL_PREFIX]) SHELL_PWD = $$shell_path($$PWD/..) SHELL_SEP = ; win32: SHELL_SEP = & # platform specific cmake arguments win32-msvc*: CMAKE_PLATFORM_ARGS = -G\"NMake Makefiles\" win32-g++: CMAKE_PLATFORM_ARGS = -G\"MinGW Makefiles\" gammaray_configure.target = gammaray_configure gammaray_configure.commands = \ ($$sprintf($$QMAKE_MKDIR_CMD, "build")) $$SHELL_SEP \ cd build $$SHELL_SEP \ cmake $$CMAKE_PLATFORM_ARGS \ -DCMAKE_INSTALL_PREFIX=$$SHELL_INSTALL_PREFIX \ -DCMAKE_PREFIX_PATH=$$SHELL_INSTALL_PREFIX \ -DGAMMARAY_INSTALL_QT_LAYOUT=TRUE \ -DGAMMARAY_BUILD_DOCS=FALSE \ -DGAMMARAY_DISABLE_FEEDBACK=TRUE \ -DGAMMARAY_BUILD_UI=FALSE \ $$GAMMARAY_EXTRA_ARGS $$SHELL_PWD gammaray_build.target = gammaray_build gammaray_build.depends += gammaray_configure gammaray_build.commands = cd build $$SHELL_SEP $(MAKE) gammaray_test.target = check gammaray_test.depends += gammaray_configure gammaray_test.commands = cd build $$SHELL_SEP $(MAKE) test gammaray_install.target = install gammaray_install.commands = cd build $$SHELL_SEP $(MAKE) DESTDIR=$(INSTALL_ROOT) install QMAKE_EXTRA_TARGETS += \ gammaray_configure \ gammaray_build \ gammaray_test \ gammaray_install # build documentation directly, the Qt CI can't run CMake at this stage QMAKE_DOCS = $$PWD/../docs/manual/gammaray-manual.qdocconf load(qt_docs) docs.commands = $(MAKE) -f $(MAKEFILE) html_docs && $(MAKE) -f $(MAKEFILE) qch_docs QMAKE_EXTRA_TARGETS += docs DOC_TARGETS = \ install_html_docs uninstall_html_docs \ install_qch_docs uninstall_qch_docs \ install_docs uninstall_docs \ qch_docs prepare_docs generate_docs \ html_docs QMAKE_EXTRA_TARGETS += $$DOC_TARGETS # default target TARGET = gammaray PRE_TARGETDEPS += gammaray_build gammaray-2.11.3/resources/000077500000000000000000000000001413310443000153775ustar00rootroot00000000000000gammaray-2.11.3/resources/gammaray.qrc000066400000000000000000000001371413310443000177050ustar00rootroot00000000000000 gammaray/authors gammaray-2.11.3/resources/gammaray/000077500000000000000000000000001413310443000171755ustar00rootroot00000000000000gammaray-2.11.3/resources/gammaray/authors000066400000000000000000000021571413310443000206120ustar00rootroot00000000000000Allen Winter Andreas Hartmetz Andreas Holzammer Anton Kreuzkamp BogDan Vatra Christoph Sterz Dan Vrátil Filipe Azevedo Frank Osterfeld Giulio Camuffo James Turner Jan Arne Petersen Jan Dalheimer Kevin Funk Laurent Montel Mathias Hasselmann Milian Wolff Nicolas Arnaud-Cormos Patrick Spendrin Peter Kümmel Rafael Roquetto Sergio Martins Stephen Kelly Thomas McGuire Till Adam Tobias Koenig Volker Krause gammaray-2.11.3/resources/update_authors.sh000077500000000000000000000001201413310443000207560ustar00rootroot00000000000000#!/bin/sh git log --pretty=format:"%an <%ae>" | sort | uniq > gammaray/authors gammaray-2.11.3/tests/000077500000000000000000000000001413310443000145275ustar00rootroot00000000000000gammaray-2.11.3/tests/CMakeLists.txt000066400000000000000000000445401413310443000172760ustar00rootroot00000000000000# keep test output locally, in the multibuild case set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/${BIN_INSTALL_DIR}) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/${LIB_INSTALL_DIR}) configure_file(gammaray-test-config.h.in ${CMAKE_CURRENT_BINARY_DIR}/gammaray-test-config.h) # create intermediate modeltestobj object files which unit tests can use add_library(modeltestobj OBJECT ${CMAKE_SOURCE_DIR}/3rdparty/qt/modeltest.cpp) set_target_properties(modeltestobj PROPERTIES POSITION_INDEPENDENT_CODE ON) target_compile_features(modeltestobj PUBLIC ${GAMMARAY_REQUIRED_CXX_FEATURES}) target_compile_definitions(modeltestobj PRIVATE $) # for -DQT_NAMESPACE=... target_include_directories(modeltestobj PUBLIC $) # create intermediate test_helpers_obj object files which unit tests can use add_library(test_helpers_obj OBJECT testhelpers.cpp) set_target_properties(test_helpers_obj PROPERTIES POSITION_INDEPENDENT_CODE ON) target_compile_features(test_helpers_obj PUBLIC ${GAMMARAY_REQUIRED_CXX_FEATURES}) target_compile_definitions(test_helpers_obj PRIVATE $ $ ) # for -DQT_NAMESPACE=... target_include_directories(test_helpers_obj PUBLIC $ $ ) if(NOT GAMMARAY_CLIENT_ONLY_BUILD) # create intermediate base_probe_test_obj object files which unit tests can use add_library(base_probe_test_obj OBJECT baseprobetest.h) set_target_properties(base_probe_test_obj PROPERTIES POSITION_INDEPENDENT_CODE ON) target_compile_features(base_probe_test_obj PUBLIC ${GAMMARAY_REQUIRED_CXX_FEATURES}) target_include_directories(base_probe_test_obj PUBLIC $ $ ) target_compile_definitions(base_probe_test_obj PRIVATE $ $ ) # for -DQT_NAMESPACE=... target_include_directories(base_probe_test_obj PUBLIC $ $ ) if(Qt5Quick_FOUND) # create intermediate base_quick_test_obj object files which unit tests can use add_library(base_quick_test_obj OBJECT basequicktest.h) set_target_properties(base_quick_test_obj PROPERTIES POSITION_INDEPENDENT_CODE ON) target_compile_features(base_quick_test_obj PUBLIC ${GAMMARAY_REQUIRED_CXX_FEATURES}) target_compile_definitions(base_quick_test_obj PRIVATE $ $ $ ) # for -DQT_NAMESPACE=... target_include_directories(base_quick_test_obj PUBLIC $ ) target_include_directories(base_quick_test_obj PUBLIC $ ) endif() endif() add_subdirectory(shared) add_subdirectory(manual) add_subdirectory(targets) # plain unit tests function(gammaray_add_test _name) add_executable(${_name} ${ARGN} $) gammaray_set_rpath(${_name} ${BIN_INSTALL_DIR}) target_link_libraries(${_name} Qt5::Test) add_test(NAME ${_name} COMMAND ${_name}) endfunction() # unit tests requiring probe injection function(gammaray_add_probe_test _name) add_executable(${_name} ${ARGN} $ $ $ ) gammaray_set_rpath(${_name} ${BIN_INSTALL_DIR}) target_link_libraries(${_name} $ Qt5::Test) add_test(NAME ${_name} COMMAND ${_name}) endfunction() # unit tests requiring quick and probe injection function(gammaray_add_quick_test _name) add_executable(${_name} ${ARGN} $ $ $ $ ) gammaray_set_rpath(${_name} ${BIN_INSTALL_DIR}) target_link_libraries(${_name} $ Qt5::Test) add_test(NAME ${_name} COMMAND ${_name}) endfunction() ### BENCH SUITE if(Qt5Widgets_FOUND) add_executable(benchsuite benchsuite.cpp) gammaray_set_rpath(benchsuite ${BIN_INSTALL_DIR}) target_link_libraries(benchsuite Qt5::Core Qt5::Gui Qt5::Widgets Qt5::Test gammaray_common gammaray_core ) # # Full-scale injection tests # add_executable(connectiontest test_connections.cpp) gammaray_set_rpath(connectiontest ${BIN_INSTALL_DIR}) target_link_libraries(connectiontest Qt5::Core Qt5::Gui Qt5::Widgets Qt5::Test ) endif() add_executable(attachhelper attachhelper.cpp) gammaray_set_rpath(attachhelper ${BIN_INSTALL_DIR}) target_link_libraries(attachhelper Qt5::Core) if(UNIX) find_program(GDB_EXECUTABLE gdb) find_program(LLDB_EXECUTABLE lldb) endif() set(INJECTOR_TYPES) if(UNIX) list(APPEND INJECTOR_TYPES preload style) if(GDB_EXECUTABLE) list(APPEND INJECTOR_TYPES gdb) endif() if(LLDB_EXECUTABLE) list(APPEND INJECTOR_TYPES lldb) endif() elseif(WIN32) list(APPEND INJECTOR_TYPES windll style) endif() foreach(injectorType ${INJECTOR_TYPES}) add_test(connectiontest-${injectorType} ${PROJECT_BINARY_DIR}/${BIN_INSTALL_DIR}/gammaray --inprocess --listen tcp://127.0.0.1 -unittest -i ${injectorType} ${PROJECT_BINARY_DIR}/${BIN_INSTALL_DIR}/connectiontest ) add_test(connectiontest-${injectorType}-filter ${PROJECT_BINARY_DIR}/${BIN_INSTALL_DIR}/gammaray --inprocess -unittest --listen tcp://127.0.0.1 -filtertest -i ${injectorType} ${PROJECT_BINARY_DIR}/${BIN_INSTALL_DIR}/connectiontest ) set_tests_properties(connectiontest-${injectorType}-filter PROPERTIES ENVIRONMENT GAMMARAY_TEST_FILTER=1) endforeach() # # common and core unit tests # gammaray_add_test(multisignalmappertest multisignalmappertest.cpp ../core/multisignalmapper.cpp) target_link_libraries(multisignalmappertest Qt5::Gui) gammaray_add_test(sourcelocationtest sourcelocationtest.cpp) target_link_libraries(sourcelocationtest Qt5::Gui gammaray_common) gammaray_add_test(selflocatortest selflocatortest.cpp) target_link_libraries(selflocatortest Qt5::Gui gammaray_common ${CMAKE_DL_LIBS}) gammaray_add_test(executiontest executiontest.cpp) target_link_libraries(executiontest Qt5::Gui gammaray_core) gammaray_add_test(metaobjecttest metaobjecttest.cpp) target_link_libraries(metaobjecttest gammaray_core) gammaray_add_probe_test(problemreportertest problemreportertest.cpp $) target_link_libraries(problemreportertest gammaray_core) if(Qt5Qml_FOUND) target_link_libraries(problemreportertest Qt5::Qml) endif() if(Qt5Widgets_FOUND) target_link_libraries(problemreportertest Qt5::Widgets) endif() gammaray_add_test(objectinstancetest objectinstancetest.cpp) target_link_libraries(objectinstancetest gammaray_core) gammaray_add_test(propertysyncertest propertysyncertest.cpp) target_link_libraries(propertysyncertest gammaray_common Qt5::Gui) gammaray_add_test(propertyadaptortest propertyadaptortest.cpp) target_link_libraries(propertyadaptortest gammaray_core Qt5::Gui gammaray_shared_test_data) if(HAVE_QT_WIDGETS) gammaray_add_test(enumpropertytest enumpropertytest.cpp) target_link_libraries(enumpropertytest gammaray_core Qt5::Gui Qt5::Widgets) endif() gammaray_add_probe_test(propertymodeltest propertymodeltest.cpp $) target_link_libraries(propertymodeltest gammaray_core gammaray_shared_test_data) gammaray_add_test(qmetaobjectvalidatortest qmetaobjectvalidatortest.cpp) target_include_directories(qmetaobjectvalidatortest SYSTEM PRIVATE ${Qt5Core_PRIVATE_INCLUDE_DIRS}) target_link_libraries(qmetaobjectvalidatortest Qt5::Gui gammaray_core) if(GAMMARAY_BUILD_UI) gammaray_add_test(metatypemodeltest metatypemodeltest.cpp ${CMAKE_SOURCE_DIR}/core/tools/metatypebrowser/metatypesmodel.cpp ${CMAKE_SOURCE_DIR}/ui/tools/metatypebrowser/metatypesclientmodel.cpp $ ) target_link_libraries(metatypemodeltest gammaray_core Qt5::Gui Qt5::Widgets) endif() if(NOT GAMMARAY_CLIENT_ONLY_BUILD) gammaray_add_probe_test(signalspycallbacktest signalspycallbacktest.cpp) target_link_libraries(signalspycallbacktest gammaray_core) gammaray_add_probe_test(integrationtest integrationtest.cpp) target_link_libraries(integrationtest gammaray_core) endif() if(NOT GAMMARAY_CLIENT_ONLY_BUILD) gammaray_add_probe_test(multithreadingtest multithreadingtest.cpp) target_link_libraries(multithreadingtest gammaray_core) if(GAMMARAY_BUILD_UI) gammaray_add_probe_test(methodmodeltest methodmodeltest.cpp ${CMAKE_SOURCE_DIR}/core/objectmethodmodel.cpp ${CMAKE_SOURCE_DIR}/core/qmetaobjectvalidator.cpp ${CMAKE_SOURCE_DIR}/core/metaobjectregistry.cpp ${CMAKE_SOURCE_DIR}/ui/tools/objectinspector/clientmethodmodel.cpp $ ) target_include_directories(methodmodeltest SYSTEM PRIVATE ${Qt5Core_PRIVATE_INCLUDE_DIRS}) target_link_libraries(methodmodeltest gammaray_core Qt5::Gui Qt5::Widgets) endif() endif() # # ui unit tests # if(GAMMARAY_BUILD_UI) gammaray_add_test(propertybindertest propertybindertest.cpp) target_link_libraries(propertybindertest gammaray_ui) if(NOT GAMMARAY_CLIENT_ONLY_BUILD) gammaray_add_probe_test(metaobjecttreemodeltest metaobjecttreemodeltest.cpp ${CMAKE_SOURCE_DIR}/ui/tools/metaobjectbrowser/metaobjecttreeclientproxymodel.cpp $ ) target_link_libraries(metaobjecttreemodeltest gammaray_core gammaray_ui Qt5::Gui) gammaray_add_probe_test(toolmanagertest toolmanagertest.cpp ../common/toolmanagerinterface.cpp $ ) target_link_libraries(toolmanagertest gammaray_core gammaray_ui Qt5::Network) endif() endif() # # launcher unit tests # add_executable(sleep sleep.cpp) target_link_libraries(sleep Qt5::Core) if(NOT GAMMARAY_CLIENT_ONLY_BUILD AND NOT GAMMARAY_PROBE_ONLY_BUILD) gammaray_add_test(probeabitest probeabitest.cpp) target_link_libraries(probeabitest gammaray_launcher Qt5::Gui) gammaray_add_test(probeabidetectortest probeabidetectortest.cpp) target_link_libraries(probeabidetectortest gammaray_launcher Qt5::Gui) gammaray_add_test(selftesttest selftesttest.cpp) target_link_libraries(selftesttest gammaray_launcher gammaray_common Qt5::Gui) gammaray_add_test(earlyexittest earlyexittest.cpp) target_include_directories(earlyexittest PRIVATE ${CMAKE_BINARY_DIR}/launcher) target_link_libraries(earlyexittest gammaray_core gammaray_launcher) if(HAVE_QT_WIDGETS) gammaray_add_test(launchertest launchertest.cpp) target_include_directories(launchertest PRIVATE ${CMAKE_BINARY_DIR}/launcher) target_link_libraries(launchertest gammaray_core gammaray_launcher) endif() if(TARGET gammaray_client) gammaray_add_test(clientconnectiontest clientconnectiontest.cpp) target_link_libraries(clientconnectiontest gammaray_core gammaray_launcher gammaray_client) endif() endif() # # client unit tests # if(NOT GAMMARAY_CLIENT_ONLY_BUILD AND NOT GAMMARAY_PROBE_ONLY_BUILD) add_executable(probesettingsclient probesettingsclient.cpp ${CMAKE_SOURCE_DIR}/core/probesettings.cpp) target_compile_definitions(probesettingsclient PRIVATE GAMMARAY_CORE_STATIC_DEFINE) target_include_directories(probesettingsclient PRIVATE ${CMAKE_BINARY_DIR}/core) target_link_libraries(probesettingsclient gammaray_common Qt5::Network) gammaray_set_rpath(probesettingsclient ${BIN_INSTALL_DIR}) gammaray_add_test(probesettingstest probesettingstest.cpp) target_link_libraries(probesettingstest gammaray_launcher gammaray_common Qt5::Core Qt5::Gui) if(GAMMARAY_BUILD_UI) gammaray_add_test(launcheruiiptest launcheruiiptest.cpp) target_link_libraries(launcheruiiptest gammaray_launcher_ui gammaray_common Qt5::Gui Qt5::Widgets Qt5::Network) gammaray_add_test(remotemodeltest remotemodeltest.cpp $ ../core/remote/remotemodelserver.cpp ) target_link_libraries(remotemodeltest gammaray_core gammaray_client Qt5::Gui Qt5::Widgets Qt5::Network) gammaray_add_test(networkselectionmodeltest networkselectionmodeltest.cpp ${CMAKE_SOURCE_DIR}/common/networkselectionmodel.cpp ) target_link_libraries(networkselectionmodeltest gammaray_common Qt5::Gui Qt5::Widgets) endif() endif() # # plugin unit tests # gammaray_add_test(fontdatabasemodeltest fontdatabasemodeltest.cpp ${CMAKE_SOURCE_DIR}/plugins/fontbrowser/fontdatabasemodel.cpp $ ) target_link_libraries(fontdatabasemodeltest Qt5::Gui) gammaray_add_test(codecmodeltest codecmodeltest.cpp ${CMAKE_SOURCE_DIR}/plugins/codecbrowser/codecmodel.cpp $ ) target_link_libraries(codecmodeltest Qt5::Gui) if(NOT GAMMARAY_CLIENT_ONLY_BUILD) #does not work unless the translations are installed in QT_INSTALL_TRANSLATIONS if(EXISTS "${QT_INSTALL_TRANSLATIONS}/qtbase_de.qm") gammaray_add_probe_test(translatortest translatortest.cpp $ ) else() message(STATUS "WARNING: Skipping the translatortest since the translations are not installed.") endif() gammaray_add_probe_test(timertoptest timertoptest.cpp $ ) target_link_libraries(timertoptest gammaray_core Qt5::Gui) if(Qt5Widgets_FOUND) gammaray_add_probe_test(widgettest widgettest.cpp $ ) target_link_libraries(widgettest gammaray_core Qt5::Widgets) gammaray_add_probe_test(actiontest actiontest.cpp ${CMAKE_SOURCE_DIR}/plugins/actioninspector/clientactionmodel.cpp ) target_link_libraries(actiontest gammaray_core Qt5::Widgets) endif() if(GAMMARAY_BUILD_UI) gammaray_add_probe_test(modelinspectortest modelinspectortest.cpp ${CMAKE_SOURCE_DIR}/plugins/modelinspector/modelinspectorinterface.cpp $ ) target_link_libraries(modelinspectortest gammaray_core gammaray_ui Qt5::Gui) endif() if(Qt5Quick_FOUND) gammaray_add_test(qmlsupporttest qmlsupporttest.cpp ${CMAKE_SOURCE_DIR}/plugins/qmlsupport/qmllistpropertyadaptor.cpp ${CMAKE_SOURCE_DIR}/plugins/qmlsupport/qmlattachedpropertyadaptor.cpp ${CMAKE_SOURCE_DIR}/plugins/qmlsupport/qjsvaluepropertyadaptor.cpp ${CMAKE_SOURCE_DIR}/plugins/qmlsupport/qmlcontextpropertyadaptor.cpp ) target_include_directories(qmlsupporttest SYSTEM PRIVATE ${Qt5Qml_PRIVATE_INCLUDE_DIRS}) target_link_libraries(qmlsupporttest gammaray_core Qt5::Quick) endif() if(Qt5Quick_FOUND AND NOT Qt5Quick_VERSION VERSION_LESS 5.7) # requires MSVC 2013 or higher gammaray_add_quick_test(bindinginspectortest bindinginspectortest.cpp $ ) target_include_directories(bindinginspectortest SYSTEM PRIVATE ${Qt5Quick_PRIVATE_INCLUDE_DIRS}) target_link_libraries(bindinginspectortest gammaray_core Qt5::Quick) target_sources(bindinginspectortest PUBLIC ${CMAKE_SOURCE_DIR}/plugins/quickinspector/quickimplicitbindingdependencyprovider.cpp ) if(NOT Qt5Qml_VERSION VERSION_LESS 5.10) target_sources(bindinginspectortest PUBLIC ${CMAKE_SOURCE_DIR}/plugins/qmlsupport/qmlbindingprovider.cpp) endif() endif() if(Qt5Quick_FOUND) gammaray_add_quick_test(quickinspectortest quickinspectortest.cpp quickinspectortest.qrc $ ) target_include_directories(quickinspectortest SYSTEM PRIVATE ${Qt5Quick_PRIVATE_INCLUDE_DIRS}) target_link_libraries(quickinspectortest gammaray_core gammaray_quickinspector_shared Qt5::Quick) gammaray_add_quick_test(quickinspectortest2 quickinspectortest2.cpp quickinspectortest.qrc $ ) target_link_libraries(quickinspectortest2 gammaray_core gammaray_quickinspector_shared Qt5::Quick) # sw renderer support is only available in Qt 5.9.3 or newer if(NOT Qt5Quick_VERSION VERSION_LESS 5.9.3) add_test(NAME quickinspectortest2_softwarecontext COMMAND quickinspectortest2) set_tests_properties(quickinspectortest2_softwarecontext PROPERTIES ENVIRONMENT "QT_QUICK_BACKEND=softwarecontext") add_test(NAME quickinspectortest_softwarecontext COMMAND quickinspectortest) set_tests_properties(quickinspectortest_softwarecontext PROPERTIES ENVIRONMENT "QT_QUICK_BACKEND=softwarecontext") endif() gammaray_add_quick_test(quickinspectorpickingtest quickinspectorpickingtest.cpp quickinspectortest.qrc $ ) target_link_libraries(quickinspectorpickingtest gammaray_core Qt5::Test Qt5::Quick) gammaray_add_quick_test(quickinspectorbench quickinspectorbench.cpp ../plugins/quickinspector/quickitemmodel.cpp ) target_link_libraries(quickinspectorbench gammaray_core Qt5::Test Qt5::Quick) gammaray_add_quick_test(quicktexturetest quicktexturetest.cpp quickinspectortest.qrc ) target_link_libraries(quicktexturetest gammaray_core Qt5::Quick) gammaray_add_quick_test(quickmaterialtest quickmaterialtest.cpp quickinspectortest.qrc $ ) target_include_directories(quickmaterialtest SYSTEM PRIVATE ${Qt5Quick_PRIVATE_INCLUDE_DIRS}) target_link_libraries(quickmaterialtest gammaray_core Qt5::Quick) endif() if(Qt5IviCore_FOUND AND Qt5IviVehicleFunctions_FOUND AND Qt5IviCore_VERSION VERSION_LESS 1.2) gammaray_add_probe_test(qtivipropertymodeltest qtivipropertymodeltest.cpp ../plugins/qtivi/qtivipropertyoverrider.cpp ../plugins/qtivi/qtivipropertymodel.cpp $ ) target_include_directories(qtivipropertymodeltest SYSTEM PRIVATE ${Qt5IviCore_PRIVATE_INCLUDE_DIRS}) target_link_libraries(qtivipropertymodeltest Qt5::Gui Qt5::IviCore Qt5::IviVehicleFunctions) if(Qt5IviMedia_FOUND) target_link_libraries(qtivipropertymodeltest Qt5::IviMedia) endif() endif() endif() gammaray-2.11.3/tests/actiontest.cpp000066400000000000000000000123111413310443000174060ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "baseprobetest.h" #include "testhelpers.h" #include #include #include #include #include using namespace GammaRay; using namespace TestHelpers; class ActionTest : public BaseProbeTest { Q_OBJECT private slots: void testActionCreationDeletion() { createProbe(); QAction *a1 = new QAction(QStringLiteral("Action 1"), this); a1->setObjectName("action1"); QAction *a2 = new QAction(QStringLiteral("Action 2"), this); QTest::qWait(1); // event loop re-entry auto sourceModel = ObjectBroker::model(QStringLiteral("com.kdab.GammaRay.ActionModel")); QVERIFY(sourceModel); auto model = new ClientActionModel(this); model->setSourceModel(sourceModel); QCOMPARE(model->rowCount(), 2); auto idx = searchFixedIndex(model, "action1"); QVERIFY(idx.isValid()); idx = idx.sibling(idx.row(), 5); QVERIFY(idx.data().toString().isEmpty()); QVERIFY(idx.data(Qt::ToolTipRole).isNull()); QVERIFY(idx.data(Qt::DecorationRole).isNull()); delete a1; QTest::qWait(1); // event loop re-entry QCOMPARE(model->rowCount(), 1); delete a2; QTest::qWait(1); // event loop re-entry QCOMPARE(model->rowCount(), 0); } void testConflictDetection() { createProbe(); QAction *a1 = new QAction(QStringLiteral("Action 1"), this); a1->setShortcut(QKeySequence(QStringLiteral("Ctrl+K"))); a1->setShortcutContext(Qt::ApplicationShortcut); QAction *a2 = new QAction(QStringLiteral("Action 2"), this); a2->setShortcut(QKeySequence(QStringLiteral("Ctrl+K"))); a2->setShortcutContext(Qt::WidgetShortcut); QTest::qWait(1); // event loop re-entry auto sourceModel = ObjectBroker::model(QStringLiteral("com.kdab.GammaRay.ActionModel")); QVERIFY(sourceModel); auto model = new ClientActionModel(this); model->setSourceModel(sourceModel); QVERIFY(model); QCOMPARE(model->rowCount(), 2); const auto index = model->index(0, 5); QCOMPARE(index.data(Qt::DisplayRole).toString(), QKeySequence(QStringLiteral("Ctrl+K")).toString( QKeySequence::PortableText)); QVERIFY(!index.data(Qt::DecorationRole).isNull()); QVERIFY(!index.data(Qt::ToolTipRole).toString().isEmpty()); delete a1; delete a2; QTest::qWait(1); } void testActionChanges() { createProbe(); QAction *a1 = new QAction(QStringLiteral("Action 1"), this); QTest::qWait(1); // event loop re-entry auto sourceModel = ObjectBroker::model(QStringLiteral("com.kdab.GammaRay.ActionModel")); auto model = new ClientActionModel(this); model->setSourceModel(sourceModel); QVERIFY(model); QCOMPARE(model->rowCount(), 1); QSignalSpy changeSpy(model, SIGNAL(dataChanged(QModelIndex,QModelIndex))); QVERIFY(changeSpy.isValid()); a1->setCheckable(true); QCOMPARE(changeSpy.size(), 1); a1->setText("Renamed Action"); QCOMPARE(changeSpy.size(), 2); a1->setChecked(true); QCOMPARE(changeSpy.size(), 3); a1->setDisabled(true); QCOMPARE(model->index(0,0).data(Qt::CheckStateRole).toInt(), (int)Qt::Unchecked); a1->setEnabled(true); QCOMPARE(model->index(0,0).data(Qt::CheckStateRole).toInt(), (int)Qt::Checked); QVERIFY(changeSpy.size() >= 4); changeSpy.clear(); QVERIFY(model->setData(model->index(0, 0), Qt::Unchecked, Qt::CheckStateRole)); QCOMPARE(a1->isEnabled(), false); QCOMPARE(changeSpy.size(), 1); a1->setChecked(true); QCOMPARE(changeSpy.size(), 1); QCOMPARE(model->index(0, 3).data(Qt::CheckStateRole).toInt(), (int)Qt::Checked); QVERIFY(model->setData(model->index(0, 3), Qt::Unchecked, Qt::CheckStateRole)); QCOMPARE(a1->isChecked(), false); QCOMPARE(changeSpy.size(), 2); } }; QTEST_MAIN(ActionTest) #include "actiontest.moc" gammaray-2.11.3/tests/attachhelper.cpp000066400000000000000000000104161413310443000177010ustar00rootroot00000000000000/* attachdialog.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "attachhelper.h" #ifdef Q_OS_WIN32 #ifndef NOMINMAX // compile fix for Qt5+VS2010+QDateTime, see: // https://forum.qt.io/topic/21605/solved-qt5-vs2010-qdatetime-not-enough-actual-parameters-for-macro-min-max #define NOMINMAX #endif #include #endif #include #include #include #include #include AttachHelper::AttachHelper(const QString &gammaray, const QString &injector, const QString &debuggee, const QStringList &arguments, QObject *parent) : QObject(parent) , m_timer(new QTimer(this)) , m_proc(new QProcess(this)) , m_gammaray(gammaray) , m_injector(injector) { m_proc->setProcessChannelMode(QProcess::ForwardedChannels); connect(m_proc, &QProcess::started, this, &AttachHelper::processStarted); connect(m_proc, static_cast(&QProcess::finished), this, &AttachHelper::processFinished); m_proc->start(debuggee, arguments); } void AttachHelper::processStarted() { // attach randomly after 1-1500 ms qsrand(QDateTime::currentMSecsSinceEpoch()); const int timeout = qrand() % 1500 + 1; qDebug() << "attaching gammaray in" << timeout << "ms"; m_timer->setSingleShot(true); connect(m_timer, &QTimer::timeout, this, &AttachHelper::attach); m_timer->start(timeout); } void AttachHelper::processFinished(int exitCode) { qApp->exit(exitCode); } void AttachHelper::attach() { if (m_proc->state() != QProcess::Running) return; qDebug() << "attaching gammaray"; QProcess gammaray; gammaray.setProcessChannelMode(QProcess::ForwardedChannels); QStringList args; args << QStringLiteral("--inprocess") << QStringLiteral("-i") << m_injector; #ifdef Q_OS_WIN32 args << QStringLiteral("-p") << QString::number(m_proc->pid()->dwProcessId); #else args << QStringLiteral("-p") << QString::number(m_proc->pid()); #endif args << QStringLiteral("-nodialogs"); args << QStringLiteral("--listen") << QStringLiteral("tcp://127.0.0.1/"); const int ret = gammaray.execute(m_gammaray, args); if (ret != 0) { m_proc->kill(); qFatal("could not attach to debuggee"); } } int main(int argc, char **argv) { QCoreApplication app(argc, argv); if (app.arguments().size() < 4) { qWarning() << "usage: " << app.applicationName() << " GAMMARAY INJECTOR DEBUGGEE [DEBUGGEE_ARGS]"; return 1; } QStringList args = app.arguments(); // remove path to this bin args.removeFirst(); // gammaray const QString gammaray = args.takeFirst(); // injector const QString injector = args.takeFirst(); // app to run const QString debuggee = args.takeFirst(); // run the self-test first, and skip the test if that fails // this prevents failures with Yama ptrace_scope activated for example if (QProcess::execute(gammaray, QStringList() << QStringLiteral("--self-test") << injector) == 1) { qWarning() << "Skipping test due to injector self-test failure!"; return 0; } AttachHelper helper(gammaray, injector, debuggee, args); return app.exec(); } gammaray-2.11.3/tests/attachhelper.h000066400000000000000000000033441413310443000173500ustar00rootroot00000000000000/* attachhelper.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef TEST_GAMMARAY_ATTACHHELPER_H #define TEST_GAMMARAY_ATTACHHELPER_H #include QT_BEGIN_NAMESPACE class QTimer; class QProcess; QT_END_NAMESPACE class AttachHelper : public QObject { Q_OBJECT public: explicit AttachHelper(const QString &gammaray, const QString &injector, const QString &debuggee, const QStringList &arguments, QObject *parent = nullptr); public slots: void attach(); void processStarted(); void processFinished(int); private: QTimer *m_timer; QProcess *m_proc; QString m_gammaray; QString m_injector; }; #endif // ATTACHHELPER_H gammaray-2.11.3/tests/baseprobetest.h000066400000000000000000000037351413310443000175520ustar00rootroot00000000000000/* baseprobetest.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Filipe Azevedo Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef BASEPROBETEST_H #define BASEPROBETEST_H #include #include #include #include #include #include #include using namespace GammaRay; class BaseProbeTest : public QObject { Q_OBJECT public: explicit BaseProbeTest(QObject *parent = nullptr) : QObject(parent) { } protected: virtual void createProbe() { Paths::setRelativeRootPath(GAMMARAY_INVERSE_BIN_DIR); qputenv("GAMMARAY_ProbePath", Paths::probePath(GAMMARAY_PROBE_ABI).toUtf8()); qputenv("GAMMARAY_ServerAddress", GAMMARAY_DEFAULT_LOCAL_TCP_URL); Hooks::installHooks(); Probe::startupHookReceived(); new ProbeCreator(ProbeCreator::Create); QTest::qWait(1); // event loop re-entry } }; #endif // BASEPROBETEST_H gammaray-2.11.3/tests/basequicktest.h000066400000000000000000000057721413310443000175620ustar00rootroot00000000000000/* basequicktest.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Filipe Azevedo Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef BASEQUICKTEST_H #define BASEQUICKTEST_H #include "baseprobetest.h" #include #include #include using namespace GammaRay; class BaseQuickTest : public BaseProbeTest { Q_OBJECT public: explicit BaseQuickTest(QObject *parent = nullptr) : BaseProbeTest(parent) { } private: std::unique_ptr m_view; bool m_exposed = false; protected slots: virtual void init() { createProbe(); m_exposed = false; m_view.reset(new QQuickView); m_view->setResizeMode(QQuickView::SizeViewToRootObject); QTest::qWait(1); // event loop re-entry } virtual void cleanup() { m_exposed = false; m_view.reset(); QTest::qWait(1); } protected: virtual bool ignoreNonExposedView() const { return false; } bool isViewExposed() const { return m_exposed; } bool showSource(const QString &sourceFile) { QSignalSpy renderSpy(m_view.get(), SIGNAL(frameSwapped())); Q_ASSERT(renderSpy.isValid()); m_view->setSource(QUrl(sourceFile)); m_view->show(); m_exposed = QTest::qWaitForWindowExposed(m_view.get()); if (!m_exposed) { qWarning() << "Unable to expose window, probably running tests on a headless system - ignoring all following render failures."; if (!ignoreNonExposedView()) return false; } // wait at least two frames so we have the final window size with all render loop/driver combinations... QTest::qWait(20); renderSpy.wait(); renderSpy.clear(); m_view->update(); return (ignoreNonExposedView() && !m_exposed) || (renderSpy.wait() || !renderSpy.isEmpty()); } QQuickView *view() const { return m_view.get(); } }; #endif // BASEQUICKTEST_H gammaray-2.11.3/tests/benchsuite.cpp000066400000000000000000000041701413310443000173660ustar00rootroot00000000000000/* benchsuite.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "benchsuite.h" #include "core/probe.h" #include "core/util.h" #include #include #include QTEST_MAIN(GammaRay::BenchSuite) using namespace GammaRay; void BenchSuite::iconForObject() { QWidget widget; QLabel label; QTreeView treeView; QBENCHMARK { Util::iconIdForObject(this); Util::iconIdForObject(&widget); Util::iconIdForObject(&label); Util::iconIdForObject(&treeView); } } void BenchSuite::probe_objectAdded() { Probe::createProbe(false); static const int NUM_OBJECTS = 10000; QVector objects; objects.reserve(NUM_OBJECTS + 1); // fill it for (int i = 0; i < NUM_OBJECTS; ++i) { auto *obj = new QObject; objects << obj; } auto it = objects.constBegin(); auto end = objects.constEnd(); QBENCHMARK_ONCE { while (it != end) { Probe::objectAdded(*it); ++it; } } qDeleteAll(objects); delete Probe::instance(); } gammaray-2.11.3/tests/benchsuite.h000066400000000000000000000025651413310443000170410ustar00rootroot00000000000000/* benchsuite.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_BENCHSUITE_H #define GAMMARAY_BENCHSUITE_H #include namespace GammaRay { class BenchSuite : public QObject { Q_OBJECT private slots: void iconForObject(); void probe_objectAdded(); }; } #endif // GAMMARAY_BENCHSUITE_H gammaray-2.11.3/tests/bindinginspectortest.cpp000066400000000000000000001350501413310443000215000ustar00rootroot00000000000000/* bindinginspectortest.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "baseprobetest.h" #include "testhelpers.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include <3rdparty/qt/modeltest.h> #include #include #include #include #include #include #include #include #include #include using namespace GammaRay; template std::vector getSortedChildren(const QModelIndex &index, const QAbstractItemModel *model, CompareFunc compare) { std::vector childIndices; int rowCount = model->rowCount(index); childIndices.reserve(rowCount); for (int i = 0; i < rowCount; ++i) { childIndices.push_back(model->index(i, 0, index)); } std::sort(childIndices.begin(), childIndices.end(), compare); return childIndices; } std::vector getSortedChildren(const QModelIndex &index, const QAbstractItemModel *model = nullptr) { if (!model) { model = index.model(); } return getSortedChildren(index, model, [](const QModelIndex &a, const QModelIndex &b) { return a.data().toString() < b.data().toString(); }); } class MockBindingProvider : public AbstractBindingProvider { public: std::vector> findBindingsFor(QObject * obj) const override { std::vector> nodes; for (auto &&dataItem : data) { if (dataItem.object == obj) { auto binding = new BindingNode(obj, obj->metaObject()->indexOfProperty(dataItem.propertyName)); nodes.push_back(std::unique_ptr(binding)); } } return nodes; } std::vector> findDependenciesFor(GammaRay::BindingNode * binding) const override { std::vector> nodes; for (auto &&dataItem : data) { auto propertyIndex = dataItem.depObject->metaObject()->indexOfProperty(dataItem.propertyName); if (dataItem.object == binding->object() && propertyIndex == binding->propertyIndex()) { auto depPropertyIndex = dataItem.depObject->metaObject()->indexOfProperty(dataItem.depPropertyName); auto dependency = new BindingNode(dataItem.depObject, depPropertyIndex, binding); nodes.push_back(std::unique_ptr(dependency)); } } return nodes; } bool canProvideBindingsFor(QObject *) const override { return true; } struct NodeData { NodeData(QObject *obj, const char *propName, QObject *depObj, const char *depPropName) // required for std::vector::emplace() : object(obj) , propertyName(propName) , depObject(depObj) , depPropertyName(depPropName) {} QObject *object; const char *propertyName; QObject *depObject; const char *depPropertyName; }; std::vector data; }; class MockObject : public QObject { Q_OBJECT Q_PROPERTY(int a READ a WRITE setA NOTIFY aChanged) Q_PROPERTY(bool b READ b WRITE setB NOTIFY bChanged) Q_PROPERTY(char c READ c WRITE setC NOTIFY cChanged) Q_PROPERTY(double d READ d WRITE setD NOTIFY dChanged) Q_PROPERTY(QString e READ e WRITE setE NOTIFY eChanged) public: MockObject(int a, bool b, char c, double d, const QString &e) : QObject(nullptr) , m_a(a) , m_b(b) , m_c(c) , m_d(d) , m_e(e) { } int a() const { return m_a; } bool b() const { return m_b; } char c() const { return m_c; } double d() const { return m_d; } const QString &e() const { return m_e; } void setA(int a) { m_a = a; emit aChanged(); } void setB(bool b) { m_b = b; emit bChanged(); } void setC(char c) { m_c = c; emit cChanged(); } void setD(double d) { m_d = d; emit dChanged(); } void setE(const QString &e) { m_e = e; emit eChanged(); } signals: void aChanged(); void bChanged(); void cChanged(); void dChanged(); void eChanged(); private: int m_a; bool m_b; char m_c; double m_d; QString m_e; }; class BindingInspectorTest : public BaseProbeTest { Q_OBJECT private slots: void initTestCase(); void init(); void cleanup(); void testMockProvider(); #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) void testQmlBindingProvider_data(); void testQmlBindingProvider(); #endif #if QT_VERSION >= QT_VERSION_CHECK(5, 7, 0) void testQtQuickProvider_data(); void testQtQuickProvider(); #endif void testModel(); void testModelDataChanged(); void testModelAdditions(); void testModelInsertions(); void testModelRemovalAtEnd(); void testModelRemovalInside(); #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) void testIntegration(); #endif private: MockBindingProvider *provider; BindingExtension *bindingExtension; std::unique_ptr modelTest; QAbstractItemModel *bindingModel; }; void BindingInspectorTest::initTestCase() { QQmlEngine engine; // Needed to initialize the Qml support plugin provider = new MockBindingProvider; BindingAggregator::registerBindingProvider(std::unique_ptr(provider)); QTest::qWait(1); bindingExtension = ObjectBroker::object("com.kdab.GammaRay.ObjectInspector.bindingsExtension"); QVERIFY(bindingExtension); bindingModel = bindingExtension->model(); QVERIFY(bindingModel); modelTest.reset(new ModelTest(bindingModel)); } void BindingInspectorTest::init() { } void BindingInspectorTest::cleanup() { provider->data.clear(); } void BindingInspectorTest::testMockProvider() { MockObject obj1 { 53, true, 'x', 5.3, "Hello World" }; MockObject obj2 { 35, false, 'y', 3.5, "Bye, World" }; obj2.setObjectName("obj2"); provider->data = {{ {&obj1, "a", &obj1, "e"}, {&obj1, "c", &obj1, "b"}, {&obj1, "c", &obj2, "b"}, {&obj2, "b", &obj2, "a"}, {&obj2, "a", &obj1, "a"}, }}; auto bindings1 = provider->findBindingsFor(&obj1); auto &&bindingNode1 = bindings1.at(0); QVERIFY(bindingNode1 != nullptr); QVERIFY(bindingNode1->parent() == nullptr); QCOMPARE(bindingNode1->object(), &obj1); QCOMPARE(bindingNode1->property().name(), "a"); QCOMPARE(bindingNode1->isPartOfBindingLoop(), false); QCOMPARE(bindingNode1->cachedValue().toInt(), 53); auto dependencies1 = provider->findDependenciesFor(bindingNode1.get()); QCOMPARE(dependencies1.size(), size_t(1)); auto bindings2 = provider->findBindingsFor(&obj1); auto &&bindingNode2 = bindings2.back(); QVERIFY(bindingNode2 != nullptr); QVERIFY(bindingNode2->parent() == nullptr); QCOMPARE(bindingNode2->object(), &obj1); QCOMPARE(bindingNode2->property().name(), "c"); QCOMPARE(bindingNode2->isPartOfBindingLoop(), false); QCOMPARE(bindingNode2->cachedValue().toChar(), QChar('x')); auto dependencies2 = provider->findDependenciesFor(bindingNode2.get()); QCOMPARE(dependencies2.size(), size_t(2)); auto &&dependency2 = dependencies2.at(0); QCOMPARE(dependency2->parent(), bindingNode2.get()); QCOMPARE(dependency2->object(), &obj1); QCOMPARE(dependency2->property().name(), "b"); QCOMPARE(dependency2->isPartOfBindingLoop(), false); QCOMPARE(dependency2->cachedValue().toBool(), true); auto &&dependency3 = dependencies2.back(); QCOMPARE(dependency3->parent(), bindingNode2.get()); QCOMPARE(dependency3->object(), &obj2); QCOMPARE(dependency3->property().name(), "b"); QCOMPARE(dependency3->isPartOfBindingLoop(), false); QCOMPARE(dependency3->cachedValue().toBool(), false); } #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) void BindingInspectorTest::testQmlBindingProvider_data() { QTest::addColumn("code"); QTest::addColumn("referencedObjectName"); QTest::newRow("context property") << QByteArray("import QtQuick 2.0\n" "Rectangle {\n" "id: rect\n" "objectName: 'rect'\n" "property string labelText: \"Hello world!\"\n" "Text { objectName: 'text'; text: labelText }\n" "}") << "rect"; QTest::newRow("scope property") << QByteArray("import QtQuick 2.0\n" "Rectangle {\n" "id: rect\n" "objectName: 'rect'\n" "property string labelText: \"I am wrong!\"\n" "Text {\n" "id: text\n" "objectName: 'text'\n" "property string labelText: \"Hello world!\"\n" "text: labelText\n" "}\n" "}") << "text"; QTest::newRow("id object property") << QByteArray("import QtQuick 2.0\n" "Rectangle {\n" "id: rect\n" "objectName: 'rect'\n" "property string labelText: \"Hello world!\"\n" "Text { objectName: 'text'; text: rect.labelText }\n" "}") << "rect"; QTest::newRow("dynamic context property") << QByteArray("import QtQuick 2.0\n" "Rectangle {\n" "id: rect\n" "objectName: 'rect'\n" "property string labelText: \"Hello world!\"\n" "Text { objectName: 'text'; Component.onCompleted: text = Qt.binding(function() { return labelText; }); }\n" "}") << "rect"; QTest::newRow("dynamic scope property") << QByteArray("import QtQuick 2.0\n" "Rectangle {\n" "id: rect\n" "objectName: 'rect'\n" "property string labelText: \"I am wrong!\"\n" "Text {\n" "id: txt\n" "objectName: 'txt'\n" "property string labelText: \"Hello world!\"\n" "Component.onCompleted: text = Qt.binding(function() { return labelText; });\n" "}\n" "}") << "txt"; QTest::newRow("dynamic id object property") << QByteArray("import QtQuick 2.0\n" "Rectangle {\n" "id: rect\n" "objectName: 'rect'\n" "property string labelText: \"Hello world!\"\n" "Text { objectName: 'text'; Component.onCompleted: text = Qt.binding(function() { return rect.labelText; }); }\n" "}") << "rect"; } void BindingInspectorTest::testQmlBindingProvider() { QFETCH(QByteArray, code); QFETCH(QString, referencedObjectName); QQmlEngine engine; QQmlComponent c(&engine); c.setData(code, QUrl()); QObject *rect = c.create(); QTest::qWait(10); QVERIFY(rect); QObject *text = rect->findChildren().at(0); QObject *referencedObject = rect->objectName() == referencedObjectName ? rect : rect->findChild(referencedObjectName); QVERIFY(referencedObject); QmlBindingProvider provider; auto bindingNodes = provider.findBindingsFor(text); auto &&bindingNode = bindingNodes.front(); QVERIFY(bindingNode); QCOMPARE(bindingNode->object(), text); QCOMPARE(bindingNode->property().name(), "text"); QCOMPARE(bindingNode->isPartOfBindingLoop(), false); QCOMPARE(bindingNode->cachedValue(), QStringLiteral("Hello world!")); auto dependencies = provider.findDependenciesFor(bindingNode.get()); QCOMPARE(dependencies.size(), 1); const std::unique_ptr &dependency = dependencies.front(); QCOMPARE(dependency->object(), referencedObject); QCOMPARE(dependency->property().name(), "labelText"); QCOMPARE(dependency->isPartOfBindingLoop(), false); QCOMPARE(dependency->cachedValue(), QStringLiteral("Hello world!")); QCOMPARE(dependency->dependencies().size(), 0); delete rect; } #endif #if QT_VERSION >= QT_VERSION_CHECK(5, 7, 0) void BindingInspectorTest::testQtQuickProvider_data() { QTest::addColumn("code", nullptr); QTest::addColumn("objName", nullptr); QTest::addColumn("propertyName", nullptr); QTest::addColumn("expectedDependencies", nullptr); QTest::newRow("implicitWidth_determines_width_noLoop") << QByteArray("import QtQuick 2.0\n" "Rectangle {\n" " id: rect\n" " objectName: 'rect'\n" " implicitWidth: 20\n" " Text { objectName: 'text'; width: parent.width }\n" "}\n" ) << "rect" << "width" << QStringList { "rect.implicitWidth" }; QTest::newRow("fill_determines_width") << QByteArray("import QtQuick 2.0\n" "Rectangle {\n" " id: rect\n" " Item {\n" " objectName: 'item'\n" " anchors.fill: parent\n" " }\n" "}\n" ) << "item" << "width" << QStringList {"rect.width", "anchors.leftMargin"}; QTest::newRow("left_and_right_determine_width") << QByteArray("import QtQuick 2.0\n" "Rectangle {\n" " id: rect\n" " Item {\n" " id: item\n" " objectName: 'item'\n" " anchors.left: parent.left\n" " anchors.right: parent.right\n" " }\n" "}\n" ) << "item" << "width" << QStringList {"item.anchors.left", "item.anchors.right"}; QTest::newRow("y_and_height_determine_bottom") << QByteArray("import QtQuick 2.0\n" "Rectangle {\n" " id: rect\n" " Item {\n" " id: item\n" " objectName: 'item'\n" " y: 50\n" " height: 100\n" " }\n" "}\n") << "item" << "bottom" << QStringList {"item.y", "item.height"}; QTest::newRow("childrenRect") << QByteArray("import QtQuick 2.0\n" "Rectangle {\n" " objectName: 'rect'\n" " Text {\n" " id: t\n" " text: 'Hello World!'\n" " }\n" "}\n" ) << "rect" << "childrenRect" << QStringList {"t.height"}; } void BindingInspectorTest::testQtQuickProvider() { QFETCH(QByteArray, code); QFETCH(QString, objName); QFETCH(QString, propertyName); QFETCH(QStringList, expectedDependencies); QQmlEngine engine; QQmlComponent c(&engine); c.setData(code, QUrl()); QObject *rect = c.create(); QTest::qWait(10); QVERIFY(rect); QObject *obj = rect->objectName() == objName ? rect : rect->findChild(objName); QVERIFY(obj); BindingNode parentNode { obj, obj->metaObject()->indexOfProperty(qPrintable(propertyName)) }; QuickImplicitBindingDependencyProvider provider; auto bindingNodes = provider.findDependenciesFor(&parentNode); for (const QString &depName : expectedDependencies) { bool found = false; for (auto &&bindingNode: bindingNodes) { if (bindingNode->canonicalName() == depName) { found = true; break; } } if (!found) { qDebug() << "Dependency" << depName << "not found. Actual dependencies:"; for (auto &&bindingNode: bindingNodes) { qDebug() << "*" << (bindingNode->canonicalName()); } } QVERIFY(found); } delete rect; } #endif void BindingInspectorTest::testModel() { MockObject obj1 { 53, true, 'x', 5.3, "Hello World" }; MockObject obj2 { 35, false, 'y', 3.5, "Bye, World" }; provider->data = {{ {&obj1, "d", &obj1, "e"}, {&obj1, "c", &obj1, "b"}, {&obj1, "c", &obj2, "d"}, {&obj2, "d", &obj2, "a"}, {&obj2, "a", &obj1, "a"}, }}; bindingExtension->setQObject(&obj1); QCOMPARE(bindingModel->rowCount(QModelIndex()), 2); auto topLevelIndices = getSortedChildren(QModelIndex(), bindingModel); QModelIndex obj1cIndex = topLevelIndices[0]; QVERIFY(obj1cIndex.isValid()); QCOMPARE(obj1cIndex.data().toString(), QStringLiteral("c")); QCOMPARE(obj1cIndex.sibling(obj1cIndex.row(), BindingModel::ValueColumn).data().toChar(), QChar('x')); QCOMPARE(obj1cIndex.sibling(obj1cIndex.row(), BindingModel::DepthColumn).data().toString(), QStringLiteral("3")); QCOMPARE(bindingModel->rowCount(obj1cIndex), 2); auto obj1cChildIndices = getSortedChildren(obj1cIndex); QModelIndex obj1bIndex = obj1cChildIndices[0]; QVERIFY(obj1bIndex.isValid()); QCOMPARE(obj1bIndex.data().toString(), QStringLiteral("b")); QCOMPARE(obj1bIndex.sibling(obj1bIndex.row(), BindingModel::ValueColumn).data().toBool(), true); QCOMPARE(obj1bIndex.sibling(obj1bIndex.row(), BindingModel::DepthColumn).data().toString(), QStringLiteral("0")); QCOMPARE(bindingModel->rowCount(obj1bIndex), 0); QModelIndex obj2bIndex = obj1cChildIndices[1]; QVERIFY(obj2bIndex.isValid()); QCOMPARE(obj2bIndex.data().toString(), QStringLiteral("d")); QCOMPARE(obj2bIndex.sibling(obj2bIndex.row(), BindingModel::ValueColumn).data().toDouble(), 3.5); QCOMPARE(obj2bIndex.sibling(obj2bIndex.row(), BindingModel::DepthColumn).data().toString(), QStringLiteral("2")); QCOMPARE(bindingModel->rowCount(obj2bIndex), 1); QModelIndex obj2aIndex = bindingModel->index(0, 0, obj2bIndex); QVERIFY(obj2aIndex.isValid()); QCOMPARE(obj2aIndex.data().toString(), QStringLiteral("a")); QCOMPARE(obj2aIndex.sibling(obj2aIndex.row(), BindingModel::ValueColumn).data().toInt(), 35); QCOMPARE(obj2aIndex.sibling(obj2aIndex.row(), BindingModel::DepthColumn).data().toString(), QStringLiteral("1")); QCOMPARE(bindingModel->rowCount(obj2aIndex), 1); QModelIndex obj1aIndex = bindingModel->index(0, 0, obj2aIndex); QVERIFY(obj1aIndex.isValid()); QCOMPARE(obj1aIndex.data().toString(), QStringLiteral("a")); QCOMPARE(obj1aIndex.sibling(obj1aIndex.row(), BindingModel::ValueColumn).data().toInt(), 53); QCOMPARE(obj1aIndex.sibling(obj1aIndex.row(), BindingModel::DepthColumn).data().toString(), QStringLiteral("0")); QCOMPARE(bindingModel->rowCount(obj1aIndex), 0); } void BindingInspectorTest::testModelDataChanged() { MockObject obj1 { 53, true, 'x', 5.3, "Hello World" }; provider->data = {{ { &obj1, "a", &obj1, "b" }, { &obj1, "a", &obj1, "c" }, { &obj1, "a", &obj1, "d" }, { &obj1, "b", &obj1, "e" }, }}; bindingExtension->setQObject(&obj1); QCOMPARE(bindingModel->rowCount(QModelIndex()), 2); auto topLevelIndices = getSortedChildren(QModelIndex(), bindingModel); QModelIndex obj1aIndex = topLevelIndices[0]; QVERIFY(obj1aIndex.isValid()); QCOMPARE(obj1aIndex.data().toString(), QStringLiteral("a")); QCOMPARE(obj1aIndex.sibling(obj1aIndex.row(), BindingModel::ValueColumn).data().toInt(), 53); QCOMPARE(obj1aIndex.sibling(obj1aIndex.row(), BindingModel::DepthColumn).data().toString(), QStringLiteral("2")); QCOMPARE(bindingModel->rowCount(obj1aIndex), 3); auto obj1aChildren = getSortedChildren(obj1aIndex); QModelIndex obj1dIndex = obj1aChildren[2]; QVERIFY(obj1dIndex.isValid()); QCOMPARE(obj1dIndex.data().toString(), QStringLiteral("d")); QCOMPARE(obj1dIndex.sibling(obj1dIndex.row(), BindingModel::ValueColumn).data().toDouble(), 5.3); QCOMPARE(obj1dIndex.sibling(obj1dIndex.row(), BindingModel::DepthColumn).data().toString(), QStringLiteral("0")); QCOMPARE(bindingModel->rowCount(obj1dIndex), 0); QSignalSpy dataChangedSpy(bindingModel, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector))); QVERIFY(dataChangedSpy.isValid()); obj1.setD(3.1415926535897932); obj1.setA(12); dataChangedSpy.wait(500); QCOMPARE(dataChangedSpy.size(), 2); QCOMPARE(dataChangedSpy.at(0).at(0).toModelIndex(), obj1aIndex.sibling(obj1aIndex.row(), BindingModel::ValueColumn)); QCOMPARE(dataChangedSpy.at(0).at(1).toModelIndex(), obj1aIndex.sibling(obj1aIndex.row(), BindingModel::ValueColumn)); QCOMPARE(dataChangedSpy.at(1).at(0).toModelIndex(), obj1dIndex.sibling(obj1dIndex.row(), BindingModel::ValueColumn)); QCOMPARE(dataChangedSpy.at(1).at(1).toModelIndex(), obj1dIndex.sibling(obj1dIndex.row(), BindingModel::ValueColumn)); QCOMPARE(bindingModel->rowCount(obj1aIndex), 3); QCOMPARE(obj1aIndex.sibling(obj1aIndex.row(), BindingModel::DepthColumn).data().toString(), QStringLiteral("2")); QCOMPARE(obj1aIndex.sibling(obj1aIndex.row(), BindingModel::ValueColumn).data().toInt(), 12); QCOMPARE(obj1dIndex.sibling(obj1dIndex.row(), BindingModel::ValueColumn).data().toDouble(), 3.1415926535897932); QCOMPARE(obj1dIndex.sibling(obj1dIndex.row(), BindingModel::DepthColumn).data().toString(), QStringLiteral("0")); } void BindingInspectorTest::testModelAdditions() { MockObject obj1 { 53, true, 'x', 5.3, "Hello World" }; provider->data = {{ { &obj1, "a", &obj1, "c" } }}; bindingExtension->setQObject(&obj1); QCOMPARE(bindingModel->rowCount(QModelIndex()), 1); QModelIndex obj1aIndex = bindingModel->index(0, 0, QModelIndex()); QVERIFY(obj1aIndex.isValid()); QCOMPARE(obj1aIndex.data().toString(), QStringLiteral("a")); QCOMPARE(obj1aIndex.sibling(obj1aIndex.row(), BindingModel::ValueColumn).data().toInt(), 53); QCOMPARE(obj1aIndex.sibling(obj1aIndex.row(), BindingModel::DepthColumn).data().toString(), QStringLiteral("1")); QCOMPARE(bindingModel->rowCount(obj1aIndex), 1); QModelIndex obj1cIndex = bindingModel->index(0, 0, obj1aIndex); QVERIFY(obj1cIndex.isValid()); QCOMPARE(obj1cIndex.data().toString(), QStringLiteral("c")); QCOMPARE(obj1cIndex.sibling(obj1cIndex.row(), BindingModel::ValueColumn).data().toChar(), QChar('x')); QCOMPARE(obj1cIndex.sibling(obj1cIndex.row(), BindingModel::DepthColumn).data().toString(), QStringLiteral("0")); QCOMPARE(bindingModel->rowCount(obj1cIndex), 0); QSignalSpy rowAddedSpy(bindingModel, SIGNAL(rowsInserted(QModelIndex,int,int))); QSignalSpy dataChangedSpy(bindingModel, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector))); QVERIFY(rowAddedSpy.isValid()); QVERIFY(dataChangedSpy.isValid()); provider->data.emplace_back( &obj1, "c", &obj1, "a" ); provider->data.emplace_back( &obj1, "c", &obj1, "b" ); provider->data.emplace_back( &obj1, "b", &obj1, "d" ); obj1.setA(12); rowAddedSpy.wait(500); QCOMPARE(rowAddedSpy.size(), 1); QCOMPARE(rowAddedSpy.front().at(1).toInt(), 0); QCOMPARE(rowAddedSpy.front().at(2).toInt(), 1); QCOMPARE(rowAddedSpy.front().front().toModelIndex(), obj1cIndex); QCOMPARE(dataChangedSpy.size(), 3); QCOMPARE(dataChangedSpy.at(0).at(0).toModelIndex(), obj1aIndex.sibling(obj1aIndex.row(), BindingModel::ValueColumn)); // Fair enough, we did change the value. QCOMPARE(dataChangedSpy.at(0).at(1).toModelIndex(), obj1aIndex.sibling(obj1aIndex.row(), BindingModel::ValueColumn)); QCOMPARE(dataChangedSpy.at(1).at(0).toModelIndex(), obj1cIndex.sibling(obj1cIndex.row(), BindingModel::DepthColumn)); // depth changed QCOMPARE(dataChangedSpy.at(1).at(1).toModelIndex(), obj1cIndex.sibling(obj1cIndex.row(), BindingModel::DepthColumn)); QCOMPARE(dataChangedSpy.at(2).at(0).toModelIndex(), obj1aIndex.sibling(obj1aIndex.row(), BindingModel::DepthColumn)); // depth changed QCOMPARE(dataChangedSpy.at(2).at(1).toModelIndex(), obj1aIndex.sibling(obj1aIndex.row(), BindingModel::DepthColumn)); QCOMPARE(obj1aIndex.sibling(obj1aIndex.row(), BindingModel::ValueColumn).data().toInt(), 12); QCOMPARE(obj1aIndex.sibling(obj1aIndex.row(), BindingModel::DepthColumn).data().toString(), QStringLiteral("\u221E")); QCOMPARE(obj1cIndex.sibling(obj1cIndex.row(), BindingModel::DepthColumn).data().toString(), QStringLiteral("\u221E")); QCOMPARE(bindingModel->rowCount(obj1cIndex), 2); auto obj1cChildren = getSortedChildren(obj1cIndex); QModelIndex node1aIndex = obj1cChildren[0]; QVERIFY(node1aIndex.isValid()); QCOMPARE(node1aIndex.data().toString(), QStringLiteral("a")); QCOMPARE(node1aIndex.sibling(node1aIndex.row(), BindingModel::ValueColumn).data().toInt(), 12); QCOMPARE(node1aIndex.sibling(node1aIndex.row(), BindingModel::DepthColumn).data().toString(), QStringLiteral("\u221E")); QCOMPARE(bindingModel->rowCount(node1aIndex), 0); QModelIndex obj1bIndex = obj1cChildren[1]; QVERIFY(obj1bIndex.isValid()); QCOMPARE(obj1bIndex.data().toString(), QStringLiteral("b")); QCOMPARE(obj1bIndex.sibling(obj1bIndex.row(), BindingModel::ValueColumn).data().toBool(), true); QCOMPARE(obj1bIndex.sibling(obj1bIndex.row(), BindingModel::DepthColumn).data().toString(), QStringLiteral("1")); QCOMPARE(bindingModel->rowCount(obj1bIndex), 1); QModelIndex obj1dIndex = bindingModel->index(0, 0, obj1bIndex); QVERIFY(obj1dIndex.isValid()); QCOMPARE(obj1dIndex.data().toString(), QStringLiteral("d")); QCOMPARE(obj1dIndex.sibling(obj1dIndex.row(), BindingModel::ValueColumn).data().toDouble(), 5.3); QCOMPARE(obj1dIndex.sibling(obj1dIndex.row(), BindingModel::DepthColumn).data().toString(), QStringLiteral("0")); QCOMPARE(bindingModel->rowCount(obj1dIndex), 0); } void BindingInspectorTest::testModelInsertions() { MockObject obj1 { 53, true, 'x', 5.3, "Hello World" }; MockObject obj2 { 35, false, 'y', 3.5, "Bye, World" }; provider->data = {{ { &obj1, "a", &obj1, "e" } }}; bindingExtension->setQObject(&obj1); QCOMPARE(bindingModel->rowCount(QModelIndex()), 1); QModelIndex obj1aIndex = bindingModel->index(0, 0, QModelIndex()); QVERIFY(obj1aIndex.isValid()); QCOMPARE(obj1aIndex.data().toString(), QStringLiteral("a")); QCOMPARE(obj1aIndex.sibling(obj1aIndex.row(), BindingModel::ValueColumn).data().toInt(), 53); QCOMPARE(obj1aIndex.sibling(obj1aIndex.row(), BindingModel::DepthColumn).data().toString(), QStringLiteral("1")); QCOMPARE(bindingModel->rowCount(obj1aIndex), 1); QModelIndex obj1eIndex = bindingModel->index(0, 0, obj1aIndex); QVERIFY(obj1eIndex.isValid()); QCOMPARE(obj1eIndex.data().toString(), QStringLiteral("e")); QCOMPARE(obj1eIndex.sibling(obj1eIndex.row(), BindingModel::ValueColumn).data().toString(), QStringLiteral("Hello World")); QCOMPARE(obj1eIndex.sibling(obj1eIndex.row(), BindingModel::DepthColumn).data().toString(), QStringLiteral("0")); QCOMPARE(bindingModel->rowCount(obj1eIndex), 0); QSignalSpy rowAddedSpy(bindingModel, SIGNAL(rowsInserted(QModelIndex,int,int))); QSignalSpy dataChangedSpy(bindingModel, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector))); QVERIFY(rowAddedSpy.isValid()); QVERIFY(dataChangedSpy.isValid()); provider->data.emplace(provider->data.end(), &obj1, "a", &obj1, "b"); provider->data.emplace(provider->data.end(), &obj1, "a", &obj1, "c"); provider->data.emplace(provider->data.end(), &obj1, "c", &obj2, "a"); provider->data.emplace(provider->data.end(), &obj1, "e", &obj2, "a"); obj1.setA(12); rowAddedSpy.wait(500); QCOMPARE(rowAddedSpy.size(), 2); QCOMPARE(rowAddedSpy.front().at(0).toModelIndex(), obj1aIndex); QCOMPARE(rowAddedSpy.front().at(1).toInt(), 0); QCOMPARE(rowAddedSpy.front().at(2).toInt(), 1); QCOMPARE(obj1aIndex.sibling(obj1aIndex.row(), BindingModel::DepthColumn).data().toString(), QStringLiteral("2")); QCOMPARE(bindingModel->rowCount(obj1aIndex), 3); auto obj1aChildren = getSortedChildren(obj1aIndex); obj1eIndex = obj1aChildren[2]; QVERIFY(obj1eIndex.isValid()); QCOMPARE(obj1eIndex.data().toString(), QStringLiteral("e")); QCOMPARE(obj1eIndex.sibling(obj1eIndex.row(), BindingModel::ValueColumn).data().toString(), QStringLiteral("Hello World")); QCOMPARE(obj1eIndex.sibling(obj1eIndex.row(), BindingModel::DepthColumn).data().toString(), QStringLiteral("1")); QCOMPARE(bindingModel->rowCount(obj1eIndex), 1); QModelIndex obj1bIndex = obj1aChildren[0]; QVERIFY(obj1bIndex.isValid()); QCOMPARE(obj1bIndex.data().toString(), QStringLiteral("b")); QCOMPARE(obj1bIndex.sibling(obj1bIndex.row(), BindingModel::ValueColumn).data().toBool(), true); QCOMPARE(obj1bIndex.sibling(obj1bIndex.row(), BindingModel::DepthColumn).data().toString(), QStringLiteral("0")); QCOMPARE(bindingModel->rowCount(obj1bIndex), 0); QModelIndex obj1cIndex = obj1aChildren[1]; QVERIFY(obj1cIndex.isValid()); QCOMPARE(obj1cIndex.data().toString(), QStringLiteral("c")); QCOMPARE(obj1cIndex.sibling(obj1cIndex.row(), BindingModel::ValueColumn).data().toChar(), QChar('x')); QCOMPARE(obj1cIndex.sibling(obj1cIndex.row(), BindingModel::DepthColumn).data().toString(), QStringLiteral("1")); QCOMPARE(bindingModel->rowCount(obj1cIndex), 1); QModelIndex obj2aIndex = bindingModel->index(0, 0, obj1cIndex); QVERIFY(obj2aIndex.isValid()); QCOMPARE(obj2aIndex.data().toString(), QStringLiteral("a")); QCOMPARE(obj2aIndex.sibling(obj2aIndex.row(), BindingModel::ValueColumn).data().toInt(), 35); QCOMPARE(obj2aIndex.sibling(obj2aIndex.row(), BindingModel::DepthColumn).data().toString(), QStringLiteral("0")); QCOMPARE(bindingModel->rowCount(obj2aIndex), 0); QCOMPARE(rowAddedSpy.back().at(0).toModelIndex(), obj1eIndex); QCOMPARE(rowAddedSpy.back().at(1).toInt(), 0); QCOMPARE(rowAddedSpy.back().at(2).toInt(), 0); QModelIndex obj2aIndex2 = bindingModel->index(0, 0, obj1eIndex); QVERIFY(obj2aIndex2.isValid()); QCOMPARE(obj2aIndex2.data().toString(), QStringLiteral("a")); QCOMPARE(obj2aIndex2.sibling(obj2aIndex2.row(), BindingModel::ValueColumn).data().toInt(), 35); QCOMPARE(obj2aIndex2.sibling(obj2aIndex2.row(), BindingModel::DepthColumn).data().toString(), QStringLiteral("0")); QCOMPARE(bindingModel->rowCount(obj2aIndex2), 0); QCOMPARE(dataChangedSpy.size(), 3); QCOMPARE(dataChangedSpy.at(0).at(0).toModelIndex(), obj1aIndex.sibling(obj1aIndex.row(), BindingModel::ValueColumn)); // Fair enough, we did change the value. QCOMPARE(dataChangedSpy.at(0).at(1).toModelIndex(), obj1aIndex.sibling(obj1aIndex.row(), BindingModel::ValueColumn)); QCOMPARE(dataChangedSpy.at(1).at(0).toModelIndex(), obj1eIndex.sibling(obj1eIndex.row(), BindingModel::DepthColumn)); // depth changed QCOMPARE(dataChangedSpy.at(1).at(1).toModelIndex(), obj1eIndex.sibling(obj1eIndex.row(), BindingModel::DepthColumn)); QCOMPARE(dataChangedSpy.at(2).at(0).toModelIndex(), obj1aIndex.sibling(obj1aIndex.row(), BindingModel::DepthColumn)); // depth changed QCOMPARE(dataChangedSpy.at(2).at(1).toModelIndex(), obj1aIndex.sibling(obj1aIndex.row(), BindingModel::DepthColumn)); } void BindingInspectorTest::testModelRemovalAtEnd() { MockObject obj1 { 53, true, 'x', 5.3, "Hello World" }; provider->data = {{ { &obj1, "a", &obj1, "b" }, { &obj1, "a", &obj1, "c" }, { &obj1, "a", &obj1, "d" }, { &obj1, "d", &obj1, "e" }, }}; bindingExtension->setQObject(&obj1); QCOMPARE(bindingModel->rowCount(QModelIndex()), 2); auto topLevelIndices = getSortedChildren(QModelIndex(), bindingModel); QModelIndex obj1aIndex = topLevelIndices[0]; QVERIFY(obj1aIndex.isValid()); QCOMPARE(obj1aIndex.data().toString(), QStringLiteral("a")); QCOMPARE(obj1aIndex.sibling(obj1aIndex.row(), BindingModel::ValueColumn).data().toInt(), 53); QCOMPARE(obj1aIndex.sibling(obj1aIndex.row(), BindingModel::DepthColumn).data().toString(), QStringLiteral("2")); QCOMPARE(bindingModel->rowCount(obj1aIndex), 3); QSignalSpy rowRemovedSpy(bindingModel, SIGNAL(rowsRemoved(QModelIndex,int,int))); QSignalSpy dataChangedSpy(bindingModel, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector))); QVERIFY(rowRemovedSpy.isValid()); QVERIFY(dataChangedSpy.isValid()); provider->data.erase(provider->data.end() - 3, provider->data.end()); obj1.setA(12); rowRemovedSpy.wait(500); QCOMPARE(rowRemovedSpy.size(), 1); QCOMPARE(rowRemovedSpy.front().at(1).toInt(), 1); QCOMPARE(rowRemovedSpy.front().at(2).toInt(), 2); QCOMPARE(rowRemovedSpy.front().front().toModelIndex(), obj1aIndex); QCOMPARE(bindingModel->rowCount(obj1aIndex), 1); QCOMPARE(obj1aIndex.sibling(obj1aIndex.row(), BindingModel::DepthColumn).data().toString(), QStringLiteral("1")); QCOMPARE(obj1aIndex.sibling(obj1aIndex.row(), BindingModel::ValueColumn).data().toInt(), 12); QModelIndex obj1bIndex = bindingModel->index(0, 0, obj1aIndex); QVERIFY(obj1bIndex.isValid()); QCOMPARE(obj1bIndex.data().toString(), QStringLiteral("b")); QCOMPARE(obj1bIndex.sibling(obj1bIndex.row(), BindingModel::ValueColumn).data().toBool(), true); QCOMPARE(obj1bIndex.sibling(obj1bIndex.row(), BindingModel::DepthColumn).data().toString(), QStringLiteral("0")); QCOMPARE(bindingModel->rowCount(obj1bIndex), 0); QCOMPARE(dataChangedSpy.size(), 2); QCOMPARE(dataChangedSpy.at(0).at(0).toModelIndex(), obj1aIndex.sibling(obj1aIndex.row(), BindingModel::ValueColumn)); // Fair enough, we did change the value. QCOMPARE(dataChangedSpy.at(0).at(1).toModelIndex(), obj1aIndex.sibling(obj1aIndex.row(), BindingModel::ValueColumn)); QCOMPARE(dataChangedSpy.at(1).at(0).toModelIndex(), obj1aIndex.sibling(obj1aIndex.row(), BindingModel::DepthColumn)); // depth changed QCOMPARE(dataChangedSpy.at(1).at(1).toModelIndex(), obj1aIndex.sibling(obj1aIndex.row(), BindingModel::DepthColumn)); } void BindingInspectorTest::testModelRemovalInside() { MockObject obj1 { 53, true, 'x', 5.3, "Hello World" }; provider->data = {{ { &obj1, "a", &obj1, "b" }, { &obj1, "a", &obj1, "c" }, { &obj1, "a", &obj1, "d" }, { &obj1, "b", &obj1, "e" }, }}; bindingExtension->setQObject(&obj1); QCOMPARE(bindingModel->rowCount(QModelIndex()), 2); auto topLevelIndices = getSortedChildren(QModelIndex(), bindingModel); QModelIndex obj1aIndex = topLevelIndices[0]; QVERIFY(obj1aIndex.isValid()); QCOMPARE(obj1aIndex.data().toString(), QStringLiteral("a")); QCOMPARE(obj1aIndex.sibling(obj1aIndex.row(), BindingModel::ValueColumn).data().toInt(), 53); QCOMPARE(obj1aIndex.sibling(obj1aIndex.row(), BindingModel::DepthColumn).data().toString(), QStringLiteral("2")); QCOMPARE(bindingModel->rowCount(obj1aIndex), 3); QSignalSpy rowRemovedSpy(bindingModel, SIGNAL(rowsRemoved(QModelIndex,int,int))); QSignalSpy dataChangedSpy(bindingModel, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector))); QVERIFY(rowRemovedSpy.isValid()); QVERIFY(dataChangedSpy.isValid()); provider->data.erase(provider->data.begin(), provider->data.begin() + 2); obj1.setA(12); rowRemovedSpy.wait(500); QCOMPARE(rowRemovedSpy.size(), 1); QCOMPARE(rowRemovedSpy.front().front().toModelIndex(), obj1aIndex); QCOMPARE(rowRemovedSpy.front().at(1).toInt(), 0); QCOMPARE(rowRemovedSpy.front().at(2).toInt(), 1); QCOMPARE(bindingModel->rowCount(obj1aIndex), 1); QCOMPARE(obj1aIndex.sibling(obj1aIndex.row(), BindingModel::DepthColumn).data().toString(), QStringLiteral("1")); QCOMPARE(obj1aIndex.sibling(obj1aIndex.row(), BindingModel::ValueColumn).data().toInt(), 12); QModelIndex obj1dIndex = bindingModel->index(0, 0, obj1aIndex); QVERIFY(obj1dIndex.isValid()); QCOMPARE(obj1dIndex.data().toString(), QStringLiteral("d")); QCOMPARE(obj1dIndex.sibling(obj1dIndex.row(), BindingModel::ValueColumn).data().toDouble(), 5.3); QCOMPARE(obj1dIndex.sibling(obj1dIndex.row(), BindingModel::DepthColumn).data().toString(), QStringLiteral("0")); QCOMPARE(bindingModel->rowCount(obj1dIndex), 0); QCOMPARE(dataChangedSpy.size(), 2); QCOMPARE(dataChangedSpy.at(0).at(0).toModelIndex(), obj1aIndex.sibling(obj1aIndex.row(), BindingModel::ValueColumn)); // Fair enough, we did change the value. QCOMPARE(dataChangedSpy.at(0).at(1).toModelIndex(), obj1aIndex.sibling(obj1aIndex.row(), BindingModel::ValueColumn)); QCOMPARE(dataChangedSpy.at(1).at(0).toModelIndex(), obj1aIndex.sibling(obj1aIndex.row(), BindingModel::DepthColumn)); // depth changed QCOMPARE(dataChangedSpy.at(1).at(1).toModelIndex(), obj1aIndex.sibling(obj1aIndex.row(), BindingModel::DepthColumn)); } #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) void BindingInspectorTest::testIntegration() { createProbe(); QByteArray code = "import QtQuick 2.0\n" "Rectangle {\n" " id: a\n" " objectName: 'a'\n" " property string labelText: \"Hello world!\"\n" " implicitWidth: childrenRect.width\n" " height: 200\n" " Text {\n" " id: t\n" " objectName: 'text'\n" " text: labelText\n" " property int foo: width\n" " anchors { left: parent.left; right: parent.right; bottom: parent.bottom; verticalCenter: parent.verticalCenter }\n" " }\n" "}"; QQmlEngine engine; QQmlComponent c(&engine); c.setData(code, QUrl()); QObject *rect = c.create(); QTest::qWait(10); QVERIFY(rect); QObject *text = rect->findChildren().at(0); QAbstractItemModel *bindingModel = ObjectBroker::model(QStringLiteral("com.kdab.GammaRay.ObjectInspector.bindingModel")); QVERIFY(bindingModel); ModelTest modelTest(bindingModel); Probe::instance()->selectObject(text); QCOMPARE(bindingModel->rowCount(), 6); auto topLevelIndices = getSortedChildren(QModelIndex(), bindingModel); QModelIndex textBindingIndex = topLevelIndices[5]; QVERIFY(textBindingIndex.isValid()); QCOMPARE(textBindingIndex.data().toString(), QStringLiteral("t.text")); QCOMPARE(textBindingIndex.sibling(textBindingIndex.row(), BindingModel::ValueColumn).data().toString(), QStringLiteral("Hello world!")); QCOMPARE(textBindingIndex.sibling(textBindingIndex.row(), BindingModel::DepthColumn).data().toString(), QStringLiteral("1")); QCOMPARE(bindingModel->rowCount(textBindingIndex), 1); QModelIndex labelTextIndex = bindingModel->index(0, 0, textBindingIndex); QVERIFY(labelTextIndex.isValid()); QCOMPARE(labelTextIndex.data().toString(), QStringLiteral("a.labelText")); QCOMPARE(labelTextIndex.sibling(labelTextIndex.row(), BindingModel::ValueColumn).data().toString(), QStringLiteral("Hello world!")); QCOMPARE(labelTextIndex.sibling(labelTextIndex.row(), BindingModel::DepthColumn).data().toString(), QStringLiteral("0")); QCOMPARE(bindingModel->rowCount(labelTextIndex), 0); QModelIndex fooIndex = topLevelIndices[4]; QVERIFY(fooIndex.isValid()); QCOMPARE(fooIndex.data().toString(), QStringLiteral("t.foo")); QCOMPARE(fooIndex.sibling(fooIndex.row(), BindingModel::DepthColumn).data().toString(), QStringLiteral("\u221E")); QCOMPARE(bindingModel->rowCount(fooIndex), 1); QModelIndex tWidthIndex = bindingModel->index(0, 0, fooIndex); QVERIFY(tWidthIndex.isValid()); QCOMPARE(tWidthIndex.data().toString(), QStringLiteral("t.width")); QCOMPARE(tWidthIndex.sibling(tWidthIndex.row(), BindingModel::DepthColumn).data().toString(), QStringLiteral("\u221E")); QCOMPARE(bindingModel->rowCount(tWidthIndex), 2); auto tWidthChildren = getSortedChildren(tWidthIndex); QModelIndex tAnchorsRightIndex = tWidthChildren[1]; QVERIFY(tAnchorsRightIndex.isValid()); QCOMPARE(tAnchorsRightIndex.data().toString(), QStringLiteral("t.anchors.right")); QCOMPARE(tAnchorsRightIndex.sibling(tAnchorsRightIndex.row(), BindingModel::DepthColumn).data().toString(), QStringLiteral("\u221E")); QCOMPARE(bindingModel->rowCount(tAnchorsRightIndex), 2); // is `parent` and `parent.right` auto tAnchorsRightChildren = getSortedChildren(tAnchorsRightIndex); QModelIndex aRightIndex = tAnchorsRightChildren[0]; QVERIFY(aRightIndex.isValid()); QCOMPARE(aRightIndex.data().toString(), QStringLiteral("a.right")); QCOMPARE(aRightIndex.sibling(aRightIndex.row(), BindingModel::DepthColumn).data().toString(), QStringLiteral("\u221E")); QCOMPARE(bindingModel->rowCount(aRightIndex), 2); auto aRightChildren = getSortedChildren(aRightIndex); QModelIndex aWidthIndex = aRightChildren[0]; QVERIFY(aWidthIndex.isValid()); QCOMPARE(aWidthIndex.data().toString(), QStringLiteral("a.width")); QCOMPARE(aWidthIndex.sibling(aWidthIndex.row(), BindingModel::DepthColumn).data().toString(), QStringLiteral("\u221E")); QCOMPARE(bindingModel->rowCount(aWidthIndex), 1); QModelIndex aImplicitWidthIndex = bindingModel->index(0, 0, aWidthIndex); QVERIFY(aImplicitWidthIndex.isValid()); QCOMPARE(aImplicitWidthIndex.data().toString(), QStringLiteral("a.implicitWidth")); QCOMPARE(aImplicitWidthIndex.sibling(aImplicitWidthIndex.row(), BindingModel::DepthColumn).data().toString(), QStringLiteral("\u221E")); QCOMPARE(bindingModel->rowCount(aImplicitWidthIndex), 1); QModelIndex aChildrenRectIndex = bindingModel->index(0, 0, aImplicitWidthIndex); QVERIFY(aChildrenRectIndex.isValid()); QCOMPARE(aChildrenRectIndex.data().toString(), QStringLiteral("a.childrenRect")); QCOMPARE(aChildrenRectIndex.sibling(aChildrenRectIndex.row(), BindingModel::DepthColumn).data().toString(), QStringLiteral("\u221E")); QCOMPARE(bindingModel->rowCount(aChildrenRectIndex), 2); auto aChildrenRectChildren = getSortedChildren(aChildrenRectIndex); QModelIndex tWidthIndex2 = aChildrenRectChildren[1]; QVERIFY(tWidthIndex2.isValid()); QCOMPARE(tWidthIndex2.data().toString(), QStringLiteral("t.width")); QCOMPARE(tWidthIndex2.sibling(tWidthIndex2.row(), BindingModel::DepthColumn).data().toString(), QStringLiteral("\u221E")); QCOMPARE(bindingModel->rowCount(tWidthIndex2), 0); Probe::instance()->selectObject(rect); QCOMPARE(bindingModel->rowCount(), 1); QModelIndex aImplicitWidthIndex2 = bindingModel->index(0, 0, QModelIndex()); QVERIFY(aImplicitWidthIndex2.isValid()); QCOMPARE(aImplicitWidthIndex2.data().toString(), QStringLiteral("a.implicitWidth")); QCOMPARE(aImplicitWidthIndex2.sibling(aImplicitWidthIndex2.row(), BindingModel::ValueColumn).data().toDouble(), 0.0); QCOMPARE(aImplicitWidthIndex2.sibling(aImplicitWidthIndex2.row(), BindingModel::DepthColumn).data().toString(), QStringLiteral("\u221E")); QCOMPARE(bindingModel->rowCount(aImplicitWidthIndex2), 1); Probe::instance()->selectObject(text); QCOMPARE(bindingModel->rowCount(), 6); topLevelIndices = getSortedChildren(QModelIndex(), bindingModel); QModelIndex tAnchorsVerticalCenterIndex = topLevelIndices[3]; QVERIFY(tAnchorsVerticalCenterIndex.isValid()); QCOMPARE(tAnchorsVerticalCenterIndex.data().toString(), QStringLiteral("t.anchors.verticalCenter")); QCOMPARE(tAnchorsVerticalCenterIndex.sibling(tAnchorsVerticalCenterIndex.row(), BindingModel::DepthColumn).data().toString(), QStringLiteral("2")); QCOMPARE(bindingModel->rowCount(tAnchorsVerticalCenterIndex), 2); // is `parent` and `parent.verticalCenter` auto tAnchorsVerticalCenterChildren = getSortedChildren(tAnchorsVerticalCenterIndex); QModelIndex aVerticalCenterIndex = tAnchorsVerticalCenterChildren[0]; QCOMPARE(aVerticalCenterIndex.data().toString(), QStringLiteral("a.verticalCenter")); QCOMPARE(aVerticalCenterIndex.sibling(aVerticalCenterIndex.row(), BindingModel::DepthColumn).data().toString(), QStringLiteral("1")); QCOMPARE(bindingModel->rowCount(aVerticalCenterIndex), 2); QVERIFY(aVerticalCenterIndex.isValid()); QModelIndex aYIndex = bindingModel->index(0, 0, aVerticalCenterIndex); QVERIFY(aYIndex.isValid()); QCOMPARE(aYIndex.data().toString(), "a.y"); QCOMPARE(aYIndex.sibling(aYIndex.row(), BindingModel::ValueColumn).data().toString(), QStringLiteral("0")); QCOMPARE(aYIndex.sibling(aYIndex.row(), BindingModel::DepthColumn).data().toString(), QStringLiteral("0")); QCOMPARE(bindingModel->rowCount(aYIndex), 0); QModelIndex aHeightIndex = bindingModel->index(1, 0, aVerticalCenterIndex); QVERIFY(aHeightIndex.isValid()); QCOMPARE(aHeightIndex.data().toString(), QStringLiteral("a.height")); QCOMPARE(aHeightIndex.sibling(aHeightIndex.row(), BindingModel::ValueColumn).data().toString(), QStringLiteral("200")); QCOMPARE(aHeightIndex.sibling(aHeightIndex.row(), BindingModel::DepthColumn).data().toString(), QStringLiteral("0")); QCOMPARE(bindingModel->rowCount(aHeightIndex), 0); delete rect; QCOMPARE(bindingModel->rowCount(), 0); } #endif QTEST_MAIN(BindingInspectorTest) #include "bindinginspectortest.moc" gammaray-2.11.3/tests/clientconnectiontest.cpp000066400000000000000000000115241413310443000214740ustar00rootroot00000000000000/* clientconnectiontest.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Filipe Azevedo Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include #include "testhelpers.h" #include #include #include #include #include #include #include #include #include #ifdef Q_OS_WIN #include #endif using namespace GammaRay; using namespace TestHelpers; class ClientConnectionTest : public QObject { Q_OBJECT public: explicit ClientConnectionTest(QObject *parent = nullptr) : QObject(parent) { } private slots: void initTestCase() { ClientConnectionManager::init(); m_process.setProcessChannelMode(QProcess::ForwardedChannels); m_process.start(QLatin1String(TESTBIN_DIR "/minimalwidgetapplication")); QVERIFY(m_process.waitForStarted()); QTest::qWait(1000); // give the target some time to actually load the QtCore DLL, otherwise ABI detection fails { LaunchOptions options; options.setUiMode(LaunchOptions::NoUi); options.setProbeSetting(QStringLiteral("ServerAddress"), GAMMARAY_DEFAULT_LOCAL_TCP_URL); #ifdef Q_OS_WIN options.setPid(m_process.pid()->dwProcessId); #else options.setPid(m_process.pid()); #endif ProbeABIDetector detector; options.setProbeABI(ProbeFinder::findBestMatchingABI(detector.abiForProcess(options.pid()))); Launcher launcher(options); QSignalSpy spy(&launcher, SIGNAL(attached())); QVERIFY(spy.isValid()); QVERIFY(launcher.start()); spy.wait(30000); QCOMPARE(spy.count(), 1); m_serverUrl = launcher.serverAddress(); QVERIFY(m_serverUrl.isValid()); QVERIFY(m_serverUrl.port() != -1); } } void testMultipleConnection() { ClientConnectionManager connector(nullptr, false); QSignalSpy spyReady(&connector, SIGNAL(ready())); QSignalSpy spyError(&connector, SIGNAL(persistentConnectionError(QString))); QSignalSpy spyDisconnected(&connector, SIGNAL(disconnected())); QVERIFY(spyReady.isValid()); QVERIFY(spyError.isValid()); QVERIFY(spyDisconnected.isValid()); for (int i = 0; i < 5; ++i) { qWarning("Phase: #%i", i); spyReady.clear(); spyError.clear(); spyDisconnected.clear(); connector.connectToHost(m_serverUrl); int loops = 0; while (loops++ < (30000 / 10)) { QTest::qWait(10); if (!spyReady.isEmpty() || !spyError.isEmpty() || !spyDisconnected.isEmpty()) { break; } } QVERIFY(spyReady.count() == 1); QVERIFY(Endpoint::isConnected()); #ifdef Q_OS_WIN QVERIFY(m_process.pid()->dwProcessId == Endpoint::instance()->pid()); #else QVERIFY(m_process.pid() == Endpoint::instance()->pid()); #endif QVERIFY(spyError.isEmpty()); QVERIFY(spyDisconnected.isEmpty()); connector.disconnectFromHost(); QTest::qWait(1); QVERIFY(spyDisconnected.count() == 1); QVERIFY(!Endpoint::isConnected()); } } void cleanupTestCase() { QVERIFY(m_process.state() == QProcess::Running); m_process.terminate(); m_process.kill(); QVERIFY(m_process.waitForFinished(5000)); } private: QProcess m_process; QUrl m_serverUrl; }; QTEST_MAIN(ClientConnectionTest) #include "clientconnectiontest.moc" gammaray-2.11.3/tests/codecmodeltest.cpp000066400000000000000000000037361413310443000202420ustar00rootroot00000000000000/* codecmodeltest.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include <3rdparty/qt/modeltest.h> #include using namespace GammaRay; class CodecModelTest : public QObject { Q_OBJECT private slots: void modelTest() { AllCodecsModel allCodecs; ModelTest tester(&allCodecs); QVERIFY(allCodecs.rowCount() > 0); SelectedCodecsModel codecs; ModelTest tester2(&codecs); QCOMPARE(codecs.rowCount(), 0); codecs.setCodecs(QStringList() << "UTF-8" << "Latin1"); QCOMPARE(codecs.rowCount(), 2); codecs.updateText("Hello World"); auto idx = codecs.index(0, 0); QCOMPARE(idx.data().toString().toUpper(), QLatin1String("UTF-8")); idx = idx.sibling(idx.row(), 1); QVERIFY(!idx.data().toString().isEmpty()); } }; QTEST_MAIN(CodecModelTest) #include "codecmodeltest.moc" gammaray-2.11.3/tests/earlyexittest.cpp000066400000000000000000000132411413310443000201420ustar00rootroot00000000000000/* earlyexittest.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Giulio Camuffo Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include #include #include #include #include #include #include #include #include #include using namespace GammaRay; class EarlyExitTest : public QObject { Q_OBJECT private: bool hasInjector(const char *type) const { auto injector = InjectorFactory::createInjector(type); if (!injector) return false; return injector->selfTest(); } private slots: void testNonExistingTarget() { LaunchOptions options; #ifdef Q_OS_MAC // older OSX versions fall back to lldb by default, which has no synchronous error reporting, see below test options.setInjectorType("preload"); #endif options.setUiMode(LaunchOptions::NoUi); options.setProbeABI(ProbeFinder::listProbeABIs().at(0)); options.setWorkingDirectory(QCoreApplication::applicationDirPath()); options.setLaunchArguments(QStringList() << QStringLiteral("I_DONT_EXIST")); options.setProbeSetting(QStringLiteral("ServerAddress"), GAMMARAY_DEFAULT_LOCAL_TCP_URL); Launcher launcher(options); QVERIFY(!launcher.start()); } void testNonExistingTargetDebugger_data() { QTest::addColumn("injectorType", nullptr); QTest::newRow("dummy") << QString(); // QTestlib fails when the test data is empty... if (hasInjector("gdb")) QTest::newRow("gdb") << QStringLiteral("gdb"); if (hasInjector("lldb")) QTest::newRow("lldb") << QStringLiteral("lldb"); } void testNonExistingTargetDebugger() { QFETCH(QString, injectorType); if (injectorType.isEmpty()) return; LaunchOptions options; options.setUiMode(LaunchOptions::NoUi); // setting the probe is not strictly needed but we silence a runtime warning this way options.setProbeABI(ProbeFinder::listProbeABIs().at(0)); options.setLaunchArguments(QStringList() << QStringLiteral("I_DONT_EXIST")); options.setInjectorType(injectorType); Launcher launcher(options); QSignalSpy spy(&launcher, SIGNAL(finished())); QVERIFY(launcher.start()); spy.wait(10000); QCOMPARE(spy.count(), 1); QEXPECT_FAIL("", "Debug injectors miss error detection for this case.", Continue); QVERIFY(!launcher.errorMessage().isEmpty()); } void test() { LaunchOptions options; options.setUiMode(LaunchOptions::NoUi); // setting the probe is not strictly needed but we silence a runtime warning this way options.setProbeABI(ProbeFinder::listProbeABIs().at(0)); options.setWorkingDirectory(QCoreApplication::applicationDirPath()); options.setLaunchArguments(QStringList() << QCoreApplication::applicationDirPath() + QStringLiteral("/sleep") << QStringLiteral("1")); options.setProbeSetting(QStringLiteral("ServerAddress"), GAMMARAY_DEFAULT_LOCAL_TCP_URL); Launcher launcher(options); QSignalSpy spy(&launcher, SIGNAL(finished())); QVERIFY(launcher.start()); spy.wait(10000); QCOMPARE(spy.count(), 1); } void testStop_data() { QTest::addColumn("injectorType", nullptr); QTest::newRow("default") << QString(); if (hasInjector("gdb")) QTest::newRow("gdb") << QStringLiteral("gdb"); if (hasInjector("lldb")) QTest::newRow("lldb") << QStringLiteral("lldb"); } void testStop() { QFETCH(QString, injectorType); LaunchOptions options; options.setUiMode(LaunchOptions::NoUi); // setting the probe is not strictly needed but we silence a runtime warning this way options.setProbeABI(ProbeFinder::listProbeABIs().at(0)); options.setWorkingDirectory(QCoreApplication::applicationDirPath()); options.setLaunchArguments(QStringList() << QCoreApplication::applicationDirPath() + QStringLiteral("/sleep") << QStringLiteral("1000")); options.setInjectorType(injectorType); options.setProbeSetting(QStringLiteral("ServerAddress"), GAMMARAY_DEFAULT_LOCAL_TCP_URL); Launcher launcher(options); QSignalSpy spy(&launcher, SIGNAL(finished())); QVERIFY(launcher.start()); launcher.stop(); spy.wait(1000); QCOMPARE(spy.count(), 1); } }; QTEST_MAIN(EarlyExitTest) #include "earlyexittest.moc" gammaray-2.11.3/tests/enumpropertytest.cpp000066400000000000000000000406331413310443000207120ustar00rootroot00000000000000/* enumpropertytest.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include #include #include #include Q_DECLARE_METATYPE(const QMetaObject *) Q_DECLARE_METATYPE(QSizePolicy::ControlTypes) using namespace GammaRay; class EnumHolder { public: enum MyEnum { Value0 = 0, Value1 = 1, Value2 = 2, Value3 = 4 }; Q_DECLARE_FLAGS(MyFlags, MyEnum) }; Q_DECLARE_METATYPE(EnumHolder::MyEnum) Q_DECLARE_METATYPE(EnumHolder::MyFlags) Q_DECLARE_OPERATORS_FOR_FLAGS(EnumHolder::MyFlags) static const MetaEnum::Value my_enum_table[] = { { EnumHolder::Value0, "Value0" }, { EnumHolder::Value1, "Value1" }, { EnumHolder::Value2, "Value2" } // Value3 intentionally missing }; #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) namespace EnumNS { Q_NAMESPACE enum UnscopedEnum { AVal, BVal }; Q_ENUM_NS(UnscopedEnum) enum class ScopedEnum { CVal, DVal }; Q_ENUM_NS(ScopedEnum) enum UnscopedFlag { AFlag = 1, BFlag = 2, CFlag = 4 }; Q_DECLARE_FLAGS(UnscopedFlags, UnscopedFlag) Q_FLAG_NS(UnscopedFlags) #if 0 // see QTBUG-47652 enum class ScopedFlag { DFlag = 8, EFlag = 16, FFlag = 32 }; Q_DECLARE_FLAGS(ScopedFlags, ScopedFlag) Q_FLAG_NS(ScopedFlags) #endif } Q_DECLARE_OPERATORS_FOR_FLAGS(EnumNS::UnscopedFlags) // Q_DECLARE_OPERATORS_FOR_FLAGS(EnumNS::ScopedFlags) #endif namespace MyNS { class MyObject : public QObject { Q_OBJECT public: enum MyEnum { MyValue1, MyValue2 }; Q_ENUM(MyEnum) }; class MyOtherObject : public QObject { Q_OBJECT }; } class EnumPropertyTest : public QObject { Q_OBJECT public: explicit EnumPropertyTest(QObject *parent = nullptr) : QObject(parent) { qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); EnumRepositoryServer::create(this); ER_REGISTER_ENUM(EnumHolder, MyEnum, my_enum_table); ER_REGISTER_FLAGS(EnumHolder, MyFlags, my_enum_table); } private slots: void testEnumToString_data() { QTest::addColumn("variant", nullptr); QTest::addColumn("name", nullptr); QTest::addColumn("mo", nullptr); QTest::addColumn("result", nullptr); const QMetaObject *nullObj = nullptr; QTest::newRow("null") << QVariant() << QByteArray() << nullObj << QString(); QTest::newRow("int") << QVariant(42) << QByteArray() << &staticMetaObject << QString(); // global enum QTest::newRow("global enum as int, QMO/name") << QVariant::fromValue(Qt::LeftToRight) << QByteArray("Qt::LayoutDirection") << &QFrame::staticMetaObject << QStringLiteral("LeftToRight"); QTest::newRow("global enum as int, name") << QVariant::fromValue(Qt::LeftToRight) << QByteArray("Qt::LayoutDirection") << nullObj << QStringLiteral("LeftToRight"); QTest::newRow("global enum, QMO/name") << QVariant::fromValue(Qt::LeftToRight) << QByteArray("Qt::LayoutDirection") << &QFrame::staticMetaObject << QStringLiteral("LeftToRight"); QTest::newRow("global enum, QMO") << QVariant::fromValue(Qt::LeftToRight) << QByteArray() << &QFrame::staticMetaObject << QStringLiteral("LeftToRight"); QTest::newRow("global enum, name") << QVariant::fromValue(Qt::LeftToRight) << QByteArray("Qt::LayoutDirection") << nullObj << QStringLiteral("LeftToRight"); QTest::newRow("global enum") << QVariant::fromValue(Qt::LeftToRight) << QByteArray() << nullObj << QStringLiteral("LeftToRight"); // global flag QTest::newRow("global flag as int, name") << QVariant::fromValue(Qt::AlignHCenter|Qt::AlignTop) << QByteArray("Qt::Alignment") << nullObj << QStringLiteral("AlignHCenter|AlignTop"); QTest::newRow("global flag, name") << QVariant::fromValue(Qt::AlignHCenter|Qt::AlignTop) << QByteArray("Qt::Alignment") << nullObj << QStringLiteral("AlignHCenter|AlignTop"); QTest::newRow("global flag") << QVariant::fromValue(Qt::AlignHCenter|Qt::AlignTop) << QByteArray() << nullObj << QStringLiteral("AlignHCenter|AlignTop"); // object-local enum QTest::newRow("local enum as int, QMO/name") << QVariant::fromValue(QFrame::Sunken) << QByteArray("QFrame::Shadow") << &QFrame::staticMetaObject << QStringLiteral("Sunken"); QTest::newRow("local enum as int, name, registered") << QVariant::fromValue(QFrame::Sunken) << QByteArray("QFrame::Shadow") << nullObj << QStringLiteral("Sunken"); QTest::newRow("local enum as int, name, parent registered") << QVariant::fromValue(QFrame::Box) << QByteArray("QFrame::Shape") << nullObj << QStringLiteral("Box"); QTest::newRow("local enum, QMO/name") << QVariant::fromValue(QFrame::Sunken) << QByteArray("QFrame::Shadow") << &QFrame::staticMetaObject << QStringLiteral("Sunken"); QTest::newRow("local enum, QMO") << QVariant::fromValue(QFrame::Sunken) << QByteArray() << &QFrame::staticMetaObject << QStringLiteral("Sunken"); QTest::newRow("local enum, name") << QVariant::fromValue(QFrame::Sunken) << QByteArray("QFrame::Shadow") << nullObj << QStringLiteral("Sunken"); QTest::newRow("local enum") << QVariant::fromValue(QFrame::Sunken) << QByteArray() << nullObj << QStringLiteral("Sunken"); // gadget-local enum QTest::newRow("gadget enum as int, QMO/name") << QVariant::fromValue(QSizePolicy::Maximum) << QByteArray("QSizePolicy::Policy") << &QSizePolicy::staticMetaObject << QStringLiteral("Maximum"); QTest::newRow("gadget enum as int, name") << QVariant::fromValue(QSizePolicy::Maximum) << QByteArray("QSizePolicy::Policy") << nullObj << QStringLiteral("Maximum"); QTest::newRow("gadget enum, QMO/name") << QVariant::fromValue(QSizePolicy::Maximum) << QByteArray("QSizePolicy::Policy") << &QSizePolicy::staticMetaObject << QStringLiteral("Maximum"); QTest::newRow("gadget enum, QMO") << QVariant::fromValue(QSizePolicy::Maximum) << QByteArray() << &QSizePolicy::staticMetaObject << QStringLiteral("Maximum"); QTest::newRow("gadget enum, name") << QVariant::fromValue(QSizePolicy::Maximum) << QByteArray("QSizePolicy::Policy") << nullObj << QStringLiteral("Maximum"); QTest::newRow("gadget enum") << QVariant::fromValue(QSizePolicy::Maximum) << QByteArray() << nullObj << QStringLiteral("Maximum"); // gadget-local flag QTest::newRow("gadget flag as int, QMO/name") << QVariant::fromValue(QSizePolicy::Frame|QSizePolicy::Label) << QByteArray("QSizePolicy::ControlTypes") << &QSizePolicy::staticMetaObject << QStringLiteral("Frame|Label"); QTest::newRow("gadget flag as int, name") << QVariant::fromValue(QSizePolicy::Frame|QSizePolicy::Label) << QByteArray("QSizePolicy::ControlTypes") << nullObj << QStringLiteral("Frame|Label"); QTest::newRow("gadget flag, QMO/name") << QVariant::fromValue(QSizePolicy::Frame|QSizePolicy::Label) << QByteArray("QSizePolicy::ControlTypes") << &QSizePolicy::staticMetaObject << QStringLiteral("Frame|Label"); QTest::newRow("gadget flag, QMO") << QVariant::fromValue(QSizePolicy::Frame|QSizePolicy::Label) << QByteArray() << &QSizePolicy::staticMetaObject << QStringLiteral("Frame|Label"); QTest::newRow("gadget flag, name") << QVariant::fromValue(QSizePolicy::Frame|QSizePolicy::Label) << QByteArray("QSizePolicy::ControlTypes") << nullObj << QStringLiteral("Frame|Label"); QTest::newRow("gadget flag") << QVariant::fromValue(QSizePolicy::Frame|QSizePolicy::Label) << QByteArray() << nullObj << QStringLiteral("Frame|Label"); // non-Qt enum QTest::newRow("plain enum, in map") << QVariant::fromValue(EnumHolder::Value2) << QByteArray() << nullObj << QStringLiteral("Value2"); QTest::newRow("plain enum, not in map") << QVariant::fromValue(EnumHolder::Value3) << QByteArray() << nullObj << QStringLiteral("unknown (4)"); // non-Qt flags QTest::newRow("plain flag, single, in map") << QVariant::fromValue(EnumHolder::Value2) << QByteArray() << nullObj << QStringLiteral("Value2"); QTest::newRow("plain flag, double, in map") << QVariant::fromValue(EnumHolder::Value2 | EnumHolder::Value1) << QByteArray() << nullObj << QStringLiteral("Value1|Value2"); QTest::newRow("plain flag, single, not in map") << QVariant::fromValue(EnumHolder::Value3) << QByteArray() << nullObj << QStringLiteral("flag 0x4"); QTest::newRow("plain flag, double, mixed") << QVariant::fromValue(EnumHolder::Value2|EnumHolder::Value3) << QByteArray() << nullObj << QStringLiteral("Value2|flag 0x4"); QTest::newRow("plain flag, empty") << QVariant::fromValue(EnumHolder::MyFlags()) << QByteArray() << nullObj << QStringLiteral("Value0"); #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) // namespaced unscoped enum QTest::newRow("ns enum as int, QMO/name") << QVariant::fromValue(EnumNS::BVal) << QByteArray("EnumNS::UnscopedEnum") << &EnumNS::staticMetaObject << QStringLiteral("BVal"); QTest::newRow("ns enum as int, name") << QVariant::fromValue(EnumNS::AVal) << QByteArray("EnumNS::UnscopedEnum") << nullObj << QStringLiteral("AVal"); QTest::newRow("ns enum, QMO/name") << QVariant::fromValue(EnumNS::BVal) << QByteArray("EnumNS::UnscopedEnum") << &EnumNS::staticMetaObject << QStringLiteral("BVal"); QTest::newRow("ns enum, QMO") << QVariant::fromValue(EnumNS::BVal) << QByteArray() << &EnumNS::staticMetaObject << QStringLiteral("BVal"); QTest::newRow("ns enum, name") << QVariant::fromValue(EnumNS::BVal) << QByteArray("EnumNS::UnscopedEnum") << nullObj << QStringLiteral("BVal"); QTest::newRow("ns enum") << QVariant::fromValue(EnumNS::BVal) << QByteArray() << nullObj << QStringLiteral("BVal"); // namespaced scoped enum QTest::newRow("ns scoped enum as int, QMO/name") << QVariant::fromValue(static_cast(EnumNS::ScopedEnum::DVal)) << QByteArray("EnumNS::ScopedEnum") << &EnumNS::staticMetaObject << QStringLiteral("DVal"); QTest::newRow("ns scoped enum as int, name") << QVariant::fromValue(static_cast(EnumNS::ScopedEnum::CVal)) << QByteArray("EnumNS::ScopedEnum") << nullObj << QStringLiteral("CVal"); QTest::newRow("ns scoped enum, QMO/name") << QVariant::fromValue(EnumNS::ScopedEnum::DVal) << QByteArray("EnumNS::ScopedEnum") << &EnumNS::staticMetaObject << QStringLiteral("DVal"); QTest::newRow("ns scoped enum, QMO") << QVariant::fromValue(EnumNS::ScopedEnum::DVal) << QByteArray() << &EnumNS::staticMetaObject << QStringLiteral("DVal"); QTest::newRow("ns scoped enum, name") << QVariant::fromValue(EnumNS::ScopedEnum::DVal) << QByteArray("EnumNS::ScopedEnum") << nullObj << QStringLiteral("DVal"); QTest::newRow("ns scoped enum") << QVariant::fromValue(EnumNS::ScopedEnum::DVal) << QByteArray() << nullObj << QStringLiteral("DVal"); // namespaced unscoped flag QTest::newRow("ns flag as int, QMO/name") << QVariant::fromValue(EnumNS::BFlag | EnumNS::CFlag) << QByteArray("EnumNS::UnscopedFlags") << &EnumNS::staticMetaObject << QStringLiteral("BFlag|CFlag"); QTest::newRow("ns flag as int, name") << QVariant::fromValue(EnumNS::AFlag | EnumNS::CFlag) << QByteArray("EnumNS::UnscopedFlags") << nullObj << QStringLiteral("AFlag|CFlag"); QTest::newRow("ns flag, QMO/name") << QVariant::fromValue(EnumNS::BFlag | EnumNS::CFlag) << QByteArray("EnumNS::UnscopedFlags") << &EnumNS::staticMetaObject << QStringLiteral("BFlag|CFlag"); QTest::newRow("ns flag, QMO") << QVariant::fromValue(EnumNS::BFlag | EnumNS::CFlag) << QByteArray() << &EnumNS::staticMetaObject << QStringLiteral("BFlag|CFlag"); QTest::newRow("ns flag, name") << QVariant::fromValue(EnumNS::BFlag | EnumNS::CFlag) << QByteArray("EnumNS::UnscopedFlags") << nullObj << QStringLiteral("BFlag|CFlag"); QTest::newRow("ns flag") << QVariant::fromValue(EnumNS::BFlag | EnumNS::CFlag) << QByteArray() << nullObj << QStringLiteral("BFlag|CFlag"); #if 0 // see QTBUG-47652 // namespaced scoped flag QTest::newRow("ns scoped flag as int, QMO/name") << QVariant::fromValue(static_cast(EnumNS::ScopedFlag::EFlag | EnumNS::ScopedFlag::FFlag)) << QByteArray("EnumNS::ScopedFlags") << &EnumNS::staticMetaObject << QStringLiteral("EFlag|FFlag"); QTest::newRow("ns scoped flag as int, name") << QVariant::fromValue(static_cast(EnumNS::ScopedFlag::DFlag | EnumNS::ScopedFlag::FFlag)) << QByteArray("EnumNS::ScopedFlags") << nullObj << QStringLiteral("DFlag|FFlag"); QTest::newRow("ns scoped flag, QMO/name") << QVariant::fromValue(EnumNS::ScopedFlag::EFlag | EnumNS::ScopedFlag::FFlag) << QByteArray("EnumNS::ScopedFlags") << &EnumNS::staticMetaObject << QStringLiteral("EFlag|FFlag"); QTest::newRow("ns scoped flag, QMO") << QVariant::fromValue(EnumNS::ScopedFlag::EFlag | EnumNS::ScopedFlag::FFlag) << QByteArray() << &EnumNS::staticMetaObject << QStringLiteral("EFlag|FFlag"); QTest::newRow("ns scoped flag, name") << QVariant::fromValue(EnumNS::ScopedFlag::EFlag | EnumNS::ScopedFlag::FFlag) << QByteArray("EnumNS::ScopedFlags") << nullObj << QStringLiteral("EFlag|FFlag"); QTest::newRow("ns scoped flag") << QVariant::fromValue(EnumNS::ScopedFlag::EFlag | EnumNS::ScopedFlag::FFlag) << QByteArray() << nullObj << QStringLiteral("EFlag|FFlag"); #endif #endif // enums from namespace QObjects QTest::newRow("ns object as int, QMO/name") << QVariant::fromValue(MyNS::MyObject::MyValue2) << QByteArray("MyNS::MyObject::MyEnum") << &MyNS::MyObject::staticMetaObject << QStringLiteral("MyValue2"); QTest::newRow("ns object as int, name") << QVariant::fromValue(MyNS::MyObject::MyValue2) << QByteArray("MyNS::MyObject::MyEnum") << nullObj << QStringLiteral("MyValue2"); // semi-qualified namespaced QObjects (ie. Q_PROPERTY missing the namespace but mentioning the class) QTest::newRow("ns object as int, semi-qualified enum") << QVariant::fromValue(MyNS::MyObject::MyValue2) << QByteArray("MyObject::MyEnum") << &MyNS::MyObject::staticMetaObject << QStringLiteral("MyValue2"); QTest::newRow("ns object as int, semi-qualified enum in different object") << QVariant::fromValue(MyNS::MyObject::MyValue2) << QByteArray("MyObject::MyEnum") << &MyNS::MyOtherObject::staticMetaObject << QStringLiteral("MyValue2"); } void testEnumToString() { QFETCH(QVariant, variant); QFETCH(QByteArray, name); QFETCH(const QMetaObject *, mo); QFETCH(QString, result); const auto str = EnumUtil::enumToString(variant, name, mo); QCOMPARE(str, result); } }; QTEST_MAIN(EnumPropertyTest) #include "enumpropertytest.moc" gammaray-2.11.3/tests/executiontest.cpp000066400000000000000000000056051413310443000201440ustar00rootroot00000000000000/* executiontest.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include #include #include #include using namespace GammaRay; class ExecutionTest : public QObject { Q_OBJECT private slots: void testReadOnlyData() { QVERIFY(Execution::isReadOnlyData(&staticMetaObject)); QVERIFY(Execution::isReadOnlyData(&QObject::staticMetaObject)); QVERIFY(!Execution::isReadOnlyData(this)); QVERIFY(Execution::isReadOnlyData(this->metaObject())); QVERIFY(Execution::isReadOnlyData(this->metaObject()->superClass())); } void benchmarkReadOnlyData() { QBENCHMARK { Execution::isReadOnlyData(&QObject::staticMetaObject); } } void testStackTrace() { if (!Execution::stackTracingAvailable()) return; const auto trace = Execution::stackTrace(32); QVERIFY(trace.size() > 0); const auto resolved = Execution::resolveAll(trace); QCOMPARE(resolved.size(), trace.size()); for (const auto &frame : resolved) { qDebug() << frame.name << frame.location.displayString(); } } void benchmarkStackTrace() { if (!Execution::stackTracingAvailable()) return; QBENCHMARK { const auto trace = Execution::stackTrace(255); QVERIFY(trace.size() > 0); } } void benchmarkResolveStackTrace() { if (!Execution::stackTracingAvailable()) return; const auto trace = Execution::stackTrace(255); QVERIFY(trace.size() > 0); QBENCHMARK { const auto frames = Execution::resolveAll(trace); QCOMPARE(frames.size(), trace.size()); } } }; QTEST_MAIN(ExecutionTest) #include "executiontest.moc" gammaray-2.11.3/tests/fontdatabasemodeltest.cpp000066400000000000000000000030101413310443000216010ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include <3rdparty/qt/modeltest.h> #include using namespace GammaRay; class FontDatabaseModelTest : public QObject { Q_OBJECT private slots: void modelTest() { FontDatabaseModel model; ModelTest tester(&model); QVERIFY(model.rowCount() > 0); } }; QTEST_MAIN(FontDatabaseModelTest) #include "fontdatabasemodeltest.moc" gammaray-2.11.3/tests/gammaray-test-config.h.in000066400000000000000000000023321413310443000213230ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_TEST_CONFIG_H #define GAMMARAY_TEST_CONFIG_H #define TESTBIN_DIR "@PROJECT_BINARY_DIR@/testbin" #endif gammaray-2.11.3/tests/hooktest/000077500000000000000000000000001413310443000163675ustar00rootroot00000000000000gammaray-2.11.3/tests/hooktest/hooktest.pro000066400000000000000000000004701413310443000207520ustar00rootroot00000000000000#------------------------------------------------- # # Project created by QtCreator 2011-10-16T17:23:23 # #------------------------------------------------- QT += core gui TARGET = hooktest TEMPLATE = app SOURCES += main.cpp\ mainwindow.cpp HEADERS += mainwindow.h FORMS += mainwindow.ui gammaray-2.11.3/tests/hooktest/main.cpp000066400000000000000000000041671413310443000200270ustar00rootroot00000000000000/* main.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Andreas Holzammer Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include #include "mainwindow.h" #include #include #include #include #include #include static inline void * page_align(void *addr) { assert(addr != 0); return (void *)((size_t)addr & ~(0xFFFF)); } void writeJmp(void *func, void *replacement) { quint8 *cur = (quint8 *)func; quint8 *aligned = (quint8 *)page_align(cur); assert(mprotect(aligned, 0xFFFF, PROT_READ|PROT_WRITE|PROT_EXEC) == 0); *cur = 0xff; *(++cur) = 0x25; *((quint32 *)++cur) = 0; cur += sizeof(quint32); *((quint64 *)cur) = (quint64)replacement; assert(mprotect(aligned, 0xFFFF, PROT_READ|PROT_EXEC) == 0); } void test() { qWarning() << "hook test!"; } int main(int argc, char *argv[]) { void *qt_startup_hook_addr = dlsym(RTLD_NEXT, "qt_startup_hook"); writeJmp(qt_startup_hook_addr, (void *)test); QApplication a(argc, argv); MainWindow w; w.show(); return a.exec(); } gammaray-2.11.3/tests/hooktest/mainwindow.cpp000066400000000000000000000025261413310443000212540ustar00rootroot00000000000000/* mainwindow.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Andreas Holzammer Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "mainwindow.h" #include "ui_mainwindow.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui->setupUi(this); } MainWindow::~MainWindow() { delete ui; } gammaray-2.11.3/tests/hooktest/mainwindow.h000066400000000000000000000026751413310443000207260ustar00rootroot00000000000000/* mainwindow.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Andreas Holzammer Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef TEST_GAMMARAY_MAINWINDOW_H #define TEST_GAMMARAY_MAINWINDOW_H #include namespace Ui { class MainWindow; } class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent = nullptr); ~MainWindow(); private: Ui::MainWindow *ui; }; #endif // MAINWINDOW_H gammaray-2.11.3/tests/hooktest/mainwindow.ui000066400000000000000000000010501413310443000210760ustar00rootroot00000000000000 MainWindow 0 0 400 300 gammaray-2.11.3/tests/integrationtest.cpp000066400000000000000000000036521413310443000204640ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Kevin Funk Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "baseprobetest.h" #include #include #include #include using namespace GammaRay; namespace { int argc = 1; char argv0[] = "integrationtest"; char* argv[] = {argv0}; } class IntegrationTest : public BaseProbeTest { Q_OBJECT private slots: void runQCoreApplication() { { QCoreApplication app(argc, argv); createProbe(); QMetaObject::invokeMethod(&app, "quit", Qt::QueuedConnection); app.exec(); } QVERIFY(!Probe::instance()); } void runQCoreApplicationWithoutExec() { { QCoreApplication app(argc, argv); createProbe(); } QVERIFY(!Probe::instance()); } }; QTEST_APPLESS_MAIN(IntegrationTest) #include "integrationtest.moc" gammaray-2.11.3/tests/launchertest.cpp000066400000000000000000000132531413310443000177400ustar00rootroot00000000000000/* launchertest.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include #include "testhelpers.h" #include #include #include #include #include #include #include #include #include #include #include #ifdef Q_OS_WIN32 #include #endif using namespace GammaRay; using namespace TestHelpers; class LauncherTest : public QObject { Q_OBJECT private: bool hasInjector(const char *type) const { auto injector = InjectorFactory::createInjector(type); if (!injector) return false; return injector->selfTest(); } private slots: void testLauncher_data() { QTest::addColumn("injectorType", nullptr); QTest::newRow("dummy") << QString(); // workaround for QTestlib asserting on empty test data sets if (hasInjector("preload")) QTest::newRow("preload") << QStringLiteral("preload"); if (hasInjector("windll")) QTest::newRow("windll") << QStringLiteral("windll"); } void testLauncher() { QFETCH(QString, injectorType); if (injectorType.isEmpty()) return; LaunchOptions options; options.setUiMode(LaunchOptions::NoUi); QString exePath = QLatin1String(TESTBIN_DIR "/minimalcoreapplication"); #ifdef Q_OS_WIN exePath += QLatin1String(".exe"); #endif ProbeABIDetector detector; options.setProbeABI(ProbeFinder::findBestMatchingABI(detector.abiForExecutable(exePath))); options.setLaunchArguments(QStringList() << exePath); options.setInjectorType(injectorType); options.setProbeSetting(QStringLiteral("ServerAddress"), GAMMARAY_DEFAULT_LOCAL_TCP_URL); Launcher launcher(options); QSignalSpy startSpy(&launcher, SIGNAL(started())); QVERIFY(startSpy.isValid()); QSignalSpy finishSpy(&launcher, SIGNAL(finished())); QVERIFY(finishSpy.isValid()); QVERIFY(launcher.start()); startSpy.wait(60000); QCOMPARE(startSpy.count(), 1); QCOMPARE(finishSpy.count(), 0); launcher.stop(); finishSpy.wait(10000); QCOMPARE(finishSpy.count(), 1); } #ifdef HAVE_QT_WIDGETS void testLauncherStyle() { LaunchOptions options; options.setUiMode(LaunchOptions::NoUi); QString exePath = QLatin1String(TESTBIN_DIR "/minimalwidgetapplication"); #ifdef Q_OS_WIN exePath += QLatin1String(".exe"); #endif ProbeABIDetector detector; options.setProbeABI(ProbeFinder::findBestMatchingABI(detector.abiForExecutable(exePath))); options.setLaunchArguments(QStringList() << exePath); options.setInjectorType(QStringLiteral("style")); options.setProbeSetting(QStringLiteral("ServerAddress"), GAMMARAY_DEFAULT_LOCAL_TCP_URL); Launcher launcher(options); QSignalSpy startSpy(&launcher, SIGNAL(started())); QVERIFY(startSpy.isValid()); QSignalSpy finishSpy(&launcher, SIGNAL(finished())); QVERIFY(finishSpy.isValid()); QVERIFY(launcher.start()); startSpy.wait(60000); QCOMPARE(startSpy.count(), 1); QCOMPARE(finishSpy.count(), 0); launcher.stop(); finishSpy.wait(10000); QCOMPARE(finishSpy.count(), 1); } #endif void testAttach() { QProcess target; target.setProcessChannelMode(QProcess::ForwardedChannels); target.start(QLatin1String(TESTBIN_DIR "/minimalcoreapplication")); QVERIFY(target.waitForStarted()); LaunchOptions options; options.setUiMode(LaunchOptions::NoUi); options.setProbeSetting(QStringLiteral("ServerAddress"), GAMMARAY_DEFAULT_LOCAL_TCP_URL); #ifdef Q_OS_WIN options.setPid(target.pid()->dwProcessId); #else options.setPid(target.pid()); #endif QTest::qWait(5000); // give the target some time to actually load the QtCore DLL, otherwise ABI detection fails ProbeABIDetector detector; options.setProbeABI(ProbeFinder::findBestMatchingABI(detector.abiForProcess(options.pid()))); Launcher launcher(options); QSignalSpy spy(&launcher, SIGNAL(attached())); QVERIFY(spy.isValid()); QVERIFY(launcher.start()); spy.wait(30000); QCOMPARE(spy.count(), 1); target.kill(); target.waitForFinished(); } }; QTEST_MAIN(LauncherTest) #include "launchertest.moc" gammaray-2.11.3/tests/launcheruiiptest.cpp000066400000000000000000000125321413310443000206260ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Christoph Sterz Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include #include #include #include #include #include using namespace GammaRay; class LauncherUiIPTest : public QObject { Q_OBJECT private slots: void initTestCase() { //create socket #ifdef Q_OS_UNIX m_localServer.listen("/tmp/socketfile"); #endif } void cleanupTestCase() { m_localServer.close(); } void testUrl_data() { QTest::addColumn("userInput", nullptr); QTest::addColumn("expectedParsed", nullptr); QTest::addColumn("isValid", nullptr); QTest::addColumn("autoPortWarning", nullptr); #ifdef Q_OS_UNIX QTest::newRow("/tmp/socketfile") << "/tmp/socketfile" << "local:/tmp/socketfile" << true << false; QTest::newRow("local:///tmp/socketfile") << "local:///tmp/socketfile" << "local:/tmp/socketfile" << true << false; #endif QTest::newRow("192.168.42.1") << "192.168.42.1" << "tcp://192.168.42.1:11732" << true << true; QTest::newRow("tcp://192.168.42.1") << "tcp://192.168.42.1" << "tcp://192.168.42.1:11732" << true << true; QTest::newRow("192.168.42") << "192.168.42" << "" << false << true; QTest::newRow("192.168.42.1:2342") << "192.168.42.1:2342" << "tcp://192.168.42.1:2342" << true << false; QTest::newRow("192.168.42.1:66666") << "192.168.42.1:66666" << "" << false << false; QTest::newRow("::1") << "::1" << "tcp://[::1]:11732" << true << true; QTest::newRow("fe80::9c0e:f1f4:d51d:a557") << "fe80::9c0e:f1f4:d51d:a557" << "tcp://[fe80::9c0e:f1f4:d51d:a557]:11732" << true << true; QTest::newRow("fe80::9c0e:f1f4:d51d:a557%enp0s31f6") << "fe80::9c0e:f1f4:d51d:a557%enp0s31f6" << "tcp://[fe80::9c0e:f1f4:d51d:a557]:11732" << true << true; QTest::newRow("fe80::9c0e:f1f4:d51d:a557%enp0s31f6:2342") << "fe80::9c0e:f1f4:d51d:a557%enp0s31f6:2342" << "tcp://[fe80::9c0e:f1f4:d51d:a557]:2342" << true << false; QTest::newRow("[fe80::9c0e:f1f4:d51d:a557]") << "[fe80::9c0e:f1f4:d51d:a557]" << "tcp://[fe80::9c0e:f1f4:d51d:a557]:11732" << true << true; QTest::newRow("tcp://[fe80::9c0e:f1f4:d51d:a557]:2342") << "tcp://[fe80::9c0e:f1f4:d51d:a557]:2342" << "tcp://[fe80::9c0e:f1f4:d51d:a557]:2342" << true << false; QTest::newRow("::ffff:192.168.15.2") << "::ffff:192.168.15.2" << "tcp://[::ffff:192.168.15.2]:11732" << true << true; } void testUrl() { QFETCH(QString, userInput); QFETCH(QString, expectedParsed); QFETCH(bool, isValid); QFETCH(bool, autoPortWarning); ConnectPage connectPage; QSignalSpy addressParsedSpy(&connectPage, SIGNAL(userInputParsed())); auto lineEdit = connectPage.findChild("host"); QVERIFY(lineEdit); lineEdit->setText(userInput); for(size_t i = 0; i<500; i++) { if (addressParsedSpy.count() > 0) break; QTest::qWait(1); } QCOMPARE(connectPage.isValid(), isValid); if (!isValid) return; QCOMPARE(connectPage.m_currentUrl.toString(), expectedParsed); QCOMPARE(lineEdit->actions().contains(connectPage.m_implicitPortWarningSign), autoPortWarning); } void testHostNames_data() { QTest::addColumn("userInput", nullptr); QTest::addColumn("isValid", nullptr); QTest::newRow("localhost") << "localhost" << true; QTest::newRow("tcp://localhost") << "tcp://localhost" << true; QTest::newRow("tcp://localhost:11732") << "tcp://localhost:11732" << true; } void testHostNames() { QFETCH(QString, userInput); QFETCH(bool, isValid); ConnectPage connectPage; QSignalSpy dnsDoneSpy(&connectPage, SIGNAL(dnsResolved())); auto lineEdit = connectPage.findChild("host"); QVERIFY(lineEdit); lineEdit->setText(userInput); for(size_t i = 0; i<1000; i++) { if (dnsDoneSpy.count() > 0) break; QTest::qWait(1); if(i == 999) return; } QCOMPARE(connectPage.isValid(), isValid); } private: QLocalServer m_localServer; }; QTEST_MAIN(LauncherUiIPTest) #include "launcheruiiptest.moc" gammaray-2.11.3/tests/manual/000077500000000000000000000000001413310443000160045ustar00rootroot00000000000000gammaray-2.11.3/tests/manual/CMakeLists.txt000066400000000000000000000047501413310443000205520ustar00rootroot00000000000000if(Qt5Widgets_FOUND) set(gammaray_messagemodeltest_srcs messagemodeltest.cpp ) add_executable(messagemodeltest ${gammaray_messagemodeltest_srcs} ) target_link_libraries(messagemodeltest Qt5::Core Qt5::Gui Qt5::Widgets ) add_executable(modelstest modelstest.cpp) target_link_libraries(modelstest Qt5::Gui Qt5::Widgets gammaray_shared_test_data) endif() add_executable(propertytest propertytest.cpp) target_link_libraries(propertytest Qt5::Core gammaray_shared_test_data) add_executable(connectionstest connectionstest.cpp) target_link_libraries(connectionstest Qt5::Core) if(Qt5Widgets_FOUND) add_executable(translator_test translator_test.cpp) target_link_libraries(translator_test Qt5::Widgets) add_custom_target(translator_test_qm COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/translation.qm ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/translation.qm ) add_dependencies(translator_test translator_test_qm) endif() if(Qt5Webkit_FOUND) add_executable(wk1application wk1application.cpp) target_link_libraries(wk1application Qt5::Core Qt5::Gui Qt5::Widgets Qt5::Webkit Qt5::WebkitWidgets ) endif() if(Qt5WebKit_FOUND AND Qt5Quick_FOUND) set(wk2application_srcs wk2application.cpp wk2application.qrc) add_executable(wk2application ${wk2application_srcs}) target_link_libraries(wk2application Qt5::Quick) endif() add_executable(signalmonitortest signalmonitortest.cpp) target_link_libraries(signalmonitortest Qt5::Core Qt5::Gui ) add_executable(objectreparenttest objectreparenttest.cpp) target_link_libraries(objectreparenttest Qt5::Core) if(Qt5QuickWidgets_FOUND) set(quickwidgettest_srcs quickwidgettest.cpp quickwidgettest.qrc) add_executable(quickwidgettest ${quickwidgettest_srcs}) target_link_libraries(quickwidgettest Qt5::QuickWidgets) endif() if(GAMMARAY_STATIC_PROBE) add_executable(staticinjectiontest staticinjectiontest.cpp) target_link_libraries(staticinjectiontest gammaray_probe Qt5::Widgets gammaray_actioninspector_plugin gammaray_codecbrowser_plugin gammaray_eventmonitor_plugin gammaray_guisupport gammaray_fontbrowser_plugin gammaray_sceneinspector_plugin gammaray_signalmonitor gammaray_statemachineviewer_plugin gammaray_styleinspector_plugin gammaray_timertop_plugin gammaray_translatorinspector gammaray_widgetinspector_plugin # gammaray_bluetooth # gammaray_qmlsupport # gammaray_quickinspector # gammaray_webinspector_plugin ) endif() gammaray-2.11.3/tests/manual/anchorspropertyfiltertest.qml000066400000000000000000000033071413310443000240720ustar00rootroot00000000000000/* anchorspropertyfiltertest.qml This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2018-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ import QtQuick 2.0 Rectangle { id: root objectName: "AnchorsPropertyFilterTest" color: "lightsteelblue" width: 200; height: 100 Rectangle { id: left objectName: "rectWithoutAnchors" width: 50; height: 50 x: 25; y: 25 color: "red" } Rectangle { id: right objectName: "rectWithAnchors" width: 50; height: 50 anchors.verticalCenter: parent.verticalCenter anchors.right: parent.right anchors.rightMargin: 25 // x: 125; y: 25 color: "yellow" } } gammaray-2.11.3/tests/manual/bindingLoopTest.qml000066400000000000000000000046611413310443000216320ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ import QtQuick 2.0 import QtQuick.Controls 2.0 Item { id: a width: 200 height: 200 property int contextPropertyFoo: 5 Item { id: b x: 5 y: 15 width: x + contextPropertyFoo height: x < 10 ? width : y Component.onCompleted: { b.visible = Qt.binding(function(){ return x > 15 ? true : (width < y); }); } } Item { id: d x: b.x y: b.x > 10 ? b.width : 6 width: x + y } Item { id: c property bool wantBindingLoop: false x: b.x y: height width: x + y height: wantBindingLoop ? width : x } Column { anchors.centerIn: parent Button { text: "b.x++" onClicked: b.x++; anchors.horizontalCenter: parent.horizontalCenter } Button { text: "b.x--" onClicked: b.x--; anchors.horizontalCenter: parent.horizontalCenter } Button { text: "b.y++" onClicked: b.y++; anchors.horizontalCenter: parent.horizontalCenter } Button { text: "Toggle binding loop" onClicked: c.wantBindingLoop = !c.wantBindingLoop; anchors.horizontalCenter: parent.horizontalCenter } } } gammaray-2.11.3/tests/manual/connectionstest.cpp000066400000000000000000000071121413310443000217330ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include class MyTestObject : public QObject { Q_OBJECT public slots: void mySlot1() {} void mySlot2() {} void mySlot3() {} void mySlot4() {} signals: void mySignal1(); void mySignal2(); void mySignal3(); void mySignal4(); }; static void connectObjects(QObject *sender, QObject *receiver) { QObject::connect(sender, SIGNAL(mySignal1()), receiver, SLOT(mySlot1())); QObject::connect(sender, SIGNAL(mySignal2()), receiver, SLOT(mySlot2()), Qt::DirectConnection); QObject::connect(sender, SIGNAL(mySignal3()), receiver, SLOT(mySlot3()), Qt::QueuedConnection); QObject::connect(sender, SIGNAL(mySignal4()), receiver, SLOT( mySlot4()), Qt::BlockingQueuedConnection); } static void dummyFunction() { } int main(int argc, char **argv) { QCoreApplication app(argc, argv); MyTestObject sender, receiver; sender.setObjectName(QStringLiteral("sender")); receiver.setObjectName(QStringLiteral("receiver")); connectObjects(&sender, &receiver); MyTestObject selfConnect; selfConnect.setObjectName(QStringLiteral("selfConnect")); connectObjects(&selfConnect, &selfConnect); QThread thread; thread.setObjectName(QStringLiteral("thread")); thread.start(); MyTestObject threadSender, threadReceiver; threadSender.setObjectName(QStringLiteral("threadSender")); threadReceiver.setObjectName(QStringLiteral("threadReceiver")); threadSender.moveToThread(&thread); threadReceiver.moveToThread(&thread); MyTestObject localSender, localReceiver; localSender.setObjectName(QStringLiteral("localSender")); localReceiver.setObjectName(QStringLiteral("localReceiver")); connectObjects(&localSender, &threadReceiver); connectObjects(&threadSender, &localReceiver); MyTestObject doubleSender, doubleReceiver; doubleSender.setObjectName(QStringLiteral("doubleSender")); doubleReceiver.setObjectName(QStringLiteral("doubleReceiver")); connectObjects(&doubleSender, &doubleReceiver); connectObjects(&doubleSender, &doubleReceiver); MyTestObject lambdaSender, lambdaContext; lambdaSender.setObjectName(QStringLiteral("lambdaSender")); lambdaContext.setObjectName(QStringLiteral("lambdaContext")); QObject::connect(&lambdaSender, &MyTestObject::mySignal1, &dummyFunction); QObject::connect(&lambdaSender, &MyTestObject::mySignal2, &lambdaContext, &dummyFunction); return app.exec(); } #include "connectionstest.moc" gammaray-2.11.3/tests/manual/keyhandlingtest.qml000066400000000000000000000027271413310443000217240ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ import QtQuick 2.0 Rectangle { color: "lightsteelblue" width: 640 height: 480 focus: true Text { id: label anchors.centerIn: parent text: "press a key" } // always works Keys.onLeftPressed: { label.text = "left pressed"; } // breaks with signal spy callbacks installed Keys.onPressed: { label.text = "key pressed"; console.log("xxx"); } } gammaray-2.11.3/tests/manual/listmodeltest.qml000066400000000000000000000031311413310443000214110ustar00rootroot00000000000000/* listmodeltest.qml This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ import QtQuick 2.0 Rectangle { width: 200 height: 100 ListModel { id: cakeModel ListElement { name: "Mousse au chocolat"; cost: 1.29 } ListElement { name: "Chocolate cake"; cost: 1.99 } ListElement { name: "Tiramisu"; cost: 2.99 } } ListView { anchors.fill: parent model: cakeModel delegate: Row { spacing: 20 Text { text: name } Text { text: cost + '€' } } } } gammaray-2.11.3/tests/manual/lsd.png000066400000000000000000004637461413310443000173200ustar00rootroot00000000000000PNG  IHDRhh pHYsaa?i IDATx?HPj)8u0j@h```&m```b```b`=`ڠt VUݻ3s@}w}zꝸQ2A&x_^?sW|{݂G=3?0S蟍=CbB욿b3~Q~ۿwßݚ蟹~ ~o fe&ޓg5{_ǧt?)c?#'ȥ~˟~'h3o$?q59OU/9^9dm3Oѿӂ,??>;LfWO9㒾S70ceEy?O?ЯHO33O?Q?Ql8t=(t,7o~7">gMnDI?L~gWZ>jd}xh=B?e~B~B?:Ș<v[Kܾ~;:~x˖7Q0@R"Pty^;W$}}(PPw=)>u80Z)CA܏n=0 Y~p(n?Px_[Ϳ+Ehx7[J_T.b7moCǩ'}ni"l9U#Qq;_R= ٻo.}T9VtE¶e{xErk_q;_?S8BN]%w m~{o(<{.2+^ w"%(%?%\-WJW(\h.JpEjJPW:~Z|w> =헹Rd:Wl(q[C|a2W7<:W :`tk} h\<( MsѲ_R2ܐyg)=yzg_(!:tXNKai\Wd:ius9: ۾&[WߐM-?h+\$}qAkǤ2qGs5:uFjhw^Q;iusaksO:~guC%^;Q|z+z0@֥ܲCm<7}-/:'׾A+ڎҶ)-rRۦ|rRθ-uz[\}S*+mlm~iE羴dSe??Zj[A'Pl yںՒoZ:EZUmB{(͢]{ ܵx֊|{J =oa.fV\{(͢S{r\v/նF݁Y綽>gJ_oU^1,>/B'TMO jdEcUm%|"-j-n(oOZ^|_NeR=B;̧րdۏ%"%]sր;Zvi[Qyn xw]men;B-[dkwUm5 9b|Φ jRޢQƊJ-+m5(?lQ扶2t?+oA[i|"G\}~^tEJdZr#/|'- s%ZN_ iiyu;ZeZ^V[>D_g>9hw;KF/z$y}B=op oDws7J7~8W>7>ZȒ~w|wL+\*<4_G %ҹi|Lp qUL'+4>}<4cJKQK'gso%bN"&/>L"_Lxj3U"~8-?ݔN =ۈViF᡹#c~5|TmHk/{3mFըXcMSaDGeU*5e{0PnxN3UԚS^kF\h(|nF嘽Ow_h5-L}, e6~0Qi4^d~k9fOmWoKE/ϩ/>ܩ0|ЌS3WO=ݴfn odZF骙XB?ht~z  >iyo|lƋFz_3xOBSx"[#˧f%xJ/Oo$Pmy^fq<4SHY޾ ?Ybύ?fzN/_1Ia4V bGL@e4v='bE *VJ/9b@ B[-D)Vj,Ϣj\-1)Fh9K_Y4>3$nĖEUd.U$z;U[Fa6-hYQc}ոk!TQsa7*ꆑ:/*Qя͊h ]PDtÞNen3Vё@zݨhT^jx c(iH{EMVw7#&DMEv'0=$M4('홝H&Wēnn-ȁ AtNM,t"{)Eh'Bh ]r"E*h!vbPy4x;b<$56-Gb>ij_E4x[~meѫ2G!vgߣnNk7gr^jhIQT?ZE?jdJݢy utñXgn'ُMtϡ gޒʠ1`Y{I܆RM]$vd_7=i>4cAf3Iv-ܑA{If6{µ۳O oX/JCpmiO}i;'J &mf?Nݸ6m>&"ug?!rolAؒ}a(h :r-B(]h5yQW ͣc.jQro>:B%fU-SrQro-q&BɣXAKadU-sNb Qro.z D,%&(K|PQrocϋHŤkĬR?8:P(%f(hm{t GɽǴkmGTQro|LF'&c<6=r&DVh+폒{gD@hGVBs=]"i(Elhq)VDU=h9z|,DACh ɢ)tX G,Oł(imG"\$*Ck1k5IV\UU HDk[rM5)>>U:Ḋs%:L,1Zs:QsvK 1b9|۞k>F\J{Iθ2hs/փzIȵ}?$]̀r6sn$j͈F613sM5EQhٍ՞k(G}16C#פi;:H_}'6fDL$WiՒX $b Ut5I4Y:( v슱7F(j5IԐ$:Z:{g/N.Ē(km4N90P- ty q/DzJ@L(!b9Շ#᎙X% -$B]h8"d]l~U2PL>b5`VQNk.v&DUHJ`7.sLw7Ȳ.U8ATcՋ>DX I fc÷(jL]?FIb 6t) 'bN]\Kc~IcZ3IbѦB1IWjk0;a];XͅsNČȶvxkܶl$͡Yt6K]Gu+(o$`u.z[CKh&u*D5 VE;b58N8aʲ(;طfG+H Q}#@El`C1 q&E5փVba@ W`["* Ut`9S`If^#X? nt ˣ ̣k,`I$p̭Ě-L˳봃itIET@ ]}rcr {. -İJhݲoOn,! g'rɃUv>1ԍPhy}4uf8>~ԋ6@'7}E7[B}?Xqҏ5CQaZ ^GZc$Ch[xm k%#hkSg8}?ĹKQϥXar? Nb!pr?)Gpx~}LpDGĬj"YMcNL4h8 eG 8E.b1N88 ̅ĨJh=Ejk/ rB'_rh]v[ o^mr4op.fEFӁ#1zi=ѭЭX>i8}"ZKo~[Rkc"{"ZaRx+8\lysv ݜFK]qF8 ;9h>@/[a@l51":} sc1}Ƞ0Ʈha88F|=$1aG, F !5B̵061cF*1bVSǎ8ڈ1m' _L!"۬h4n =CKh}8jTBkQD@  YtNj7J9ӈFDVcIt&b1qwhv-{6E`b}56N8 >l&DwC}hK1 ZXl h;}:zL9g/oD :}j(;Q8֩Y@|ѯn=>ӍAQtfyIP͡mf¥;/#oՑ &͞kȔf'Iz4БlxP ukLhv_̡K1QOP)tړHfcIf7TV"C30al'bT= 16C+%B(hx+b0oe}d-$zv0VC_TvCֽC-E6=yl{OCwC%ts)}mr>RL`z J`6myB@Q,w5hxϺT`n[vCo8LvF׼C-#VD.]qh}_vTإf ͤv0Ћw!Jz'a Xy,Y̪2:Ht^l<QM`kxj s1e;$n3L>FPr{RbHpys1iy.yoCg񛢜P KNhj nŬf>.yf)< Z%jdB/bQY·qPѮDcCM_X pkَĠ!1-7AC1Ap :`ѯ!t~ y3}A'߇7=ѫQtny3U;[c1wD9Axa[T4.؊ּn%aṰMQ4a#:}3~E͠[c 5of뢠9tZL\L"yjPy3c{U䴀In2ZD/,tac+5EE͡GVb%^ӎxECUG܌*Ϡ{V BbQ9pḽĠ#9[(qqj6x@GtB[eG\>-3v% 8nIx0LէQTs8@Mʠ3%7qNŰ#3);P(8reA#- MK|K1?R 2gG o3UM-/XJAV|,̿%J h& :Z>)pRJAQpF^|*.:T`݌7 Ztr #tWqa{D5}6Ř8 ȷhc1>8*Z",1jlG>Ƙ)Ѫenhɂ#vDRӪCvC D͡tq 5mXP rRBnv0Oȣ-N37zmrREnz0kH#肵(RV\UX&-BLwDQ<ΨEbU0 z4^lf!*BQ|Dk%BFWEć,:g=xymV 5 JcD!!]Q¢B笒7QRPT`b"Xd{AŤ"5C'ڎW5vcʦ :3hpʽMUB{'G9rFVW8vE~/5jhZ֒-]B._5h-Z2HˆODPݰho!7~tF97\vCOQ(h`/r9Db|1ܹͰJo80 1QԒfGCbP 7vbzpX0shmtYa’6.!`z{lCĮ>mo0,!zn/b9Qqbկܹ{Q\Jo zJ&GnLCY߰Fz(T !)_̌ wmpEFѦȠMC 3~Nzmq2.&03c8Σ  .e?m0(=TK\a yTBF(etN`PνIWAl@W9I?#[5j_Q9U1auN/_/:qɛuNLfJfD&ﶩb5] D4JH3*sM3Q@FЍըŘat1(_] Jf5.GWDuS%wMgbE׃}}햨F@7m|jmƴjoax6fɑI$lW1_+M t'<\N$Qqi\JH̰ko {z& ==bN ,a \P#άzbml"a=6@ I*UtbAg6>1I&i.hx1²Jp:'>tkUБ܌Uuq =2%֕C;PNBǮ k9io!AtiD88ϽphA̠-kHnS֜S ѹ)87:h@a1!tcӖ!["c{q$vϼz6`LFSfm\ ?'ms. h.i#hm />M-ƹvX~1c<{mRkpEL$m4 H5izqdW4bx%m4CmāFϹ'^<46,\Jn^&m4I=зY复CCûFH6j*iCC Wsnaw{XA-l+38zq!Х5pl;a |W_3<6>ޣ5ynjG;-vy{"zeeȭ)֋jv~:p[V9 s}qэIԍNXsV|V97CQ؏.#;0v csl[}3ސJ.ʠmXwCHL:wUѩ]]@lyz(JZ,D9֍lrI@Knti,iùhw5[ZDt7b0i5wtjk*o%ݛx/Mhm8)'Dyau-Mz5MP^1fVu(o>P8Clj$[4i ~(?fR:+4 KZg1 n]p-5XHZg1Jh0:>-sn\@0S4ŷ@zaRCЍE>uSB}4 'ڎ{h.$=p* ї4힌ƓE̺IOlc\ TOJFvД{2cB#'ѹm=<͈fO z=@"@TK3艧'ԞWC@:4Pj^W}k*>]*VUF'B\^+S5*QeuMW=%UѹWՐݢzхWK34G7Y?f$4n-h[q5zEj(1T7G}5"(z1j461#jq5$BŰqVÚ1WSA!MsQ6W3aq\_\j>ҧ.bBWǤtUK3nLhG\-h`\\T.kq\JZ(E\}TaZAݸ~ 6c2^h=oϬ bvx5r>=ѹЇ-Gmҫ%NR8N1c(͊#jd/dVa)$VѧE΢[+.W@mfОyuhДѩPE6]7^lqVٯC1˜x(rJl"f4{}s"\ d/ZcnEtl>F{LsdAbPsnhm~h۳c/ftpwJ/"u1ý n;DVAR]<:"ܭ c=!ۋִ[IºeUq/trIЇn (hs4;hE\ |Tv{(yzK(pP{,;$E\$z[s݃5WPb9ѻԭLaoA(|`"nS.hϧB'>ˍ4KDq,,:`Mcz5B;ǑTEnzaF}Z$ l:5hm19.+;ʡC뾂έGn5Q6DVnC6 nNsmZEmoZ6n==Z0,m=ªDu{ѯEN싌]{h_3PpCв{iEMy=ͻA̜vӮTB5'<ڹUtcьhG؁m[tb=0\V9d=I41%ms9tv>h~°|!*Z3UJAN]GƌQMވS$vC-^tˤr/A;ǩ9>^ KM޺HmYTǶwڦ6DڳY 46 CH4b cRwS֬}$:TQ[6pN>hzѹ1=b敵aEt7GZrۆTkuFmKѓpg4ʷ`tn/gV:_ bx'f9L)?ImƞxE`u{r8}tp1BiϋGk:Nhu~Q8aCbCtijsk+I+>Xΰ[x&ڶJQiŧJ4yhw.>uqG>_*506mX q\^Mf0Ӝ<ڻ5fǍѺ9= c$^x@@?{R]_Arth cAPѕŭ8PBCY}Љ=̣,<:GUwnWTsNc܊n[tGEtnsmKl.?~iIl.).,EMUЍ0͹貸7ԍnݶj։ĺ@^s&JmVSr#ꂖ\C0 ZŎL!Vc;dZ'FaTfНZc܆-3s* 0έ.hʹ0D-#" ,g8 >p\].khR]Оh.pSjQ]ЁbFuC-t̲ѯ d=Ӫի5{5NU!뱘=m!뉀{Y[!멘%mgt͸(js+d=#]&d=XkC=2l\B0+?d >܋nuKtk#s,2F!nض7D<6\2f8YJC^n:э]=UvW!V'պUB"#rЫ>U 4U ¢kڅ@ ^D,.t[{&wj,zh*cL'!ѯU븀ΙEA{AW 4U`gH/6MG5- QԮEه^OV&]&dhsDUԲC؁;;HVƽj!V<OXy4,:a8emۮUȐtUs׆F |l* kˠcͦ[QіэUQA7[ӏ8IJ \k͞,[]ѹm0É8$$heQhuE/K;9(JBw.EQ{6 HBV̈́e ȡSv꣋>ƥ貑VP5;Uui-3YBy3~^0C" ?Т=ui/ٷ]a[n/QhཱུW ȊgR(j=z!fP(nS&~HdÃ*L"ڲnC6%ȎW(˲q+Jڳ-=*?Mwhwq@ A@ AABA0AAlIIA AL AOC`A}soy[}k!H8pCQ`O߿ty|w~a1"q#NՃsYgo$;G3>;%uEgyvj`Q\= .׍긪kU$ޮg3=G Z|y8 gk7.hzJL4؆w(dBHc^o zR~PFxz^^E'/*1 s'RTuB,\- _*"'zL`,+? 'y>bЮ:~`ڞߓz_4*23يx7 =|m{VRFdwHO[+Q擷ߞa}3WďIF`:ys=ީ1JG_ulB ]Yリ`!y.Oɗ᙮JzI"k v՝jAU DdDSo+4 7+9hNVjol%=&&cȍx*k'C&= :[!nPEPmU,z :WK %:4P\~(M;CFL)Y=W9vDR \!"~~b%UEϯU-ֿgjRϯT 7?Z\zՒCۀ~~Fxw>n_zSft^ZCLӻ՗ր;Y"SI> E9~7  :ů^5KȁRwe`#QȾ+/*WK>܎1j;ϯQі?ZJ ؚ_ƨgsg~%4djc>;& B-~){֭j|.>TQP|S V]ӗƂGL2OaJxCF;ՊC )'JgvUlϹ*sΑAXR2(IšncSF7jeQ. 2NiEZZ݊y}?%ρ{\}ǟ"Ȳt?BNU8q??&U<@?P5ݣß0} w.4 =]M U# ';jc';7SK0e< khDtt9f9hYͶy>@#vOwͷ?ّR/~#y 3ve0Z5U~(}|><v'X\eVL֋wCOaqO/|LmX:']7h~JwVcO{n'ߪs\ƗPC#::`+є~vXys|fSft% V<݊H|qڍ,^eB9p=?imDR$= Чz S$Mt!s1ZaAd[9ۓs$#[e(|OA@CjF5w.(A ,.P>W 9Bg&x=?&[Vc>nP𜺝*W OڠaKu5O$2Mf<} _2-'YԒaY퉺La*1}'ؤ![a8DK1I^O\dd: yyfhҜy&7 \:vǍ+"s#Q?W#)HD :E~uIɳDkN,Iyh7ttY݄w6R/ex{C\`|SIy d ^Mk Ya_hBMtJ{؝WS NrqX!Z5*y~9g[SbNes=?&DO#h9ϰUm+OehVi]( |MwX^|:L9ZoJ1=UmpnE|fzS;(cJCKl&IiOaE;q>HR}Yg_hגmjL]?|PPrl 6T}mhw@r.ty~=3lmə\@goi?Zݕ$WQuAb ?ϔa:N(Kmw:3vܧ<_*=VZ\Zq=ȉW1P/Q9+<Fq䛒܅60},P-V=K"0+ U"t;RU0/6[IhDmZ7/6/z{<|FXNOCX;Cڱ4ShkkO KRygSs |RtM+\X^EPo\X TijFvNð+_GM\3ݲlTK-w\MK*3=5sMܡHQ®[t k5V< 9I{nPXpIy=~rӤ!"QԬy6+o|ߪE?u- <U@0P/f+)}qQ[/>1 +Uُ$5:;O|Qʞ"HWCY:Gϣ^iڞE߷mOEH'_ao BmQTtMbF1l9ٿev*Q}jhm{'y^aCu,bHp*(vZa//Xg_O6rS<ꍑ8Bu%KڂO$epnjY6y z?k^k;:)CJ:Dy:U)jK~xO/N;`D]s :}'gp٪^uΎܿSF'rpwH~@g9@  i*ymGҷs9ACjMSԪ'@ShYN:"?R^!DWb!N@7 > )_9Ol++wPzT<~}UkOߎ*x.Ɲs 7Z)܏* [ w27=`0Z3HKτV7,KV]@='8@hU wNmL+Cq`n6;bKߑ>AWr:(ܧ]m u*%)j+̚Kߙt>뿈T6<z4pVH[ga4 }wG'_7pa"Pz[F\l1y+=39{尣1Q:28׎?9QJ`tRY{/sX~KFbD(i{Y^rc{^_yVJ>r5YLCW'+4䇎fpi[(CFzrHor0m)Fa⾐R.=*^ Up6V}ZDO]:wkD&֪i_ W9if +wGh~HB";_ A4e PUcuOH$>aH P;u +ZELkK̔N8F?(;QߠKqڛ9e*2?e/Xj gjAa==S}@:ЄZ\9e-Yĵ VN;v4'uFohǟ\0l1_ٞÛWdJ%x&S)|#|6[u9lkbdTh.i.=}: ]c,t̹c!naփGy8BK#Q݈12+ݾܼ8%.N BBgvv/IDNNq84M9YjGwj9my"6.ЇּE_Ԋ` JZҢ\d,\!q*ݫj]gMMSB:!y.5몋]0y})gb<$CGTz&jЍ'`=QꞲLSPg4j*W=*Y \L^2 K@+Ra^޵ I(vwNYzAHqPJG7؀;+ _X,GߨWfW*ƿ b77E,;zW#~/¬T 1ɲa)qԑN(U"^טqu!@-D5Z硳>d'%B0BEuŚ֞o]%-uԐ%}nR8S<&[eq'u+ԇ ̝г/h7)vK}*u դ6u(B%\_BWx%}!(WE9y؈@_p9ʼ֨tj -L騋mXu[TE9QIv ;DtcH`R:y.j sנ܁1LUW~HϊSԃ abbNFؕ,DYso=$\e*z8;egR>GHיt!iä\f23c0mԀߞRE{v`+o)% ,t&Z:ڴJyeGhT\'ٹW4%ܡ5{ H#A{r2k=yjĨ ;( O1?$KvdN נ]}^霩LޣY;P+;9CK Z"MQ7= ` ]q ;L_u{灲Mzǝ^`FY75~b7EjSѾ{p6R^RB:mIU%;(SU(R;9)3f74%k(37.C \eYqKQ?d.tN4[_O-j_"1'oТBGtBGۺTT5aVP xJ픦q߀ )njZ;h kDUƯt>FH#`:c'Zs$^c5_u7.}x keswҝeMy|TeOrIXVk07㖽Nz]v u$kWzg(=^ҍ2:G*PxL2!NVEAJKϊ!; n*ԅt{4%9CmUcLz^sQ& az$y-A6:UѢwxwQԁOjO@:D @)QC ܻ@UjGoo6b‚vF ޓ/ElQKiJ ᣣ6=%+Is"xݝX#RyhH,i9RqRǪb_*l+pKk`3j{+>(vfk(64`Z03+؃o YzVGqeMtcg <\]B;2qU _М{@IFq?$E.FKBShΊڲcHV+Z4rGD|UV.H ]v啧n̑&3e&vggImyn[wpՠ8=)\39kWᠣi(/|)l8>浠!uJ.9+^yWb~{EN9?1w i?KFy kn5=rR;:9U9? Vڞ0<uzSm(z 71'I+jX<ʥr]` V;*ް7El~aMsZMtc].KS=%ڦa'nXF="99b4Rpڑ{e#{4uOU$Oq: 79H䐾xk\:,y]e=LV||]}K![7௢?)j+pˊcSozF)_eP&5rq "l;m3|Ĕ?pj2%} V@5 [!K7ھuAjR'E-T"Ԇ@an7Ë7t)jUCPΊz8AJgpЫ1*לZv=[%}zR 7|Rp<Ŏ`q`2gnѨJ4 6u*K*Y2fWȞngE,=XvmXТry.sX_&b3PM{yQIF1C9wT̖B4cu:d;%|LHCfi4SE3Z'㠘 ~GEC~oo{O- b,]abUv\t>'_+kFc[S¶nNvuxܲ>߻L7zSo:C-)zIa#bS܁)i2 `©~W@|Rҝn\CU(|p}*nNzԩt hB_eGgQcIۺF*DHR{{K3GElݜ!gjY_%u"F~q>|TҍX[va(huo /x*ȎTB >BPȮxt U;x7Emݝh5u,J!*`AE-g&U)5 GaPԡLing,ERm!9=CvnS,kCf,dpaVJ.;.5d*+f`c.YИV|Pҕ%%[z8MZj IS5ZcF[vڗ5 dE%KgB9'8(bOUV^9XIFJ.LsJ<`D*Not 9Z [v5”_Šg$ *j>?K_{$uwjPD+ x NϔUvTG/C:!CvTַ*֜ixKк:5Ի t /k>/qUt߹;})tx;@r\9ޢ }`QI!Kpus쨎XD#3o +6Uc>֑uq1=+8ږn+%N نcڱ%=O+̸n"cEXKڑ/HUYSD Khz+8={֊z8wU9"Pq/:g1$uizQGHޘZ|D[50h-;m!,vŔ },:AY(@vX g/7?tIkomz,h~+ZRgzG|=A9{p9sIFQ[yD4yDO#ܾ?)[|>xy$#8[G!i*bLd^*n-x O8⤪K{l}kl}̕pCvU@oOakI#'C@W}: } ʾHjCs>HPE-]5^oLŀ`hOapiNa['W)J.҄RgٕrNnO%_dǐCTwV'z.'n`1ĩ).&9%sXYrD[vBZ1K_\IKvz1+Hb\^b;S`ŷ:¼-usJ۲38T7 (:ţ4^v4UAvRO-U}}۪,;F:*"~vU8?oDyST}|HY3\}-;K+&J CR@3%m] @s%l\` m] z2oRVUQ5,*luζS bGmRC1 -pf䣃 " 5}vKݓTq֏7 czcO⑧zԫ/z vVҙi{jtt=ArH;ZZQrX[6(_YrTkVVoɯZbWq-o3%'5ÎSbk΁&lO;f34?{ow0cĕ!Ztw1vf!zb/[GMU{ )UY + *i_iO7H1BD$c-=HRK,9Cr%5s톼NnI=| NcD)[w`*wMТ20B.ķdiG檾H~ՂHhuW$4); ]}ıҶ.R1[gg6d|Q`HiE=䘖ƤFD*IJ>Ȏ0|2xW[Ws'@lA8dg+|ttV&+}0=*;[[eNICem9doW? C>>9U ^+bY %GoCS3-}Nvyn8:P΁,C8SekL)ʊ؉Of=tܦeW -iv?oH1S5ve-XZYӥӻ#9ҶT֧\cYwCp3=/'"^I />TE(dk6LLqK k(9%ܣH=V-;I'JںBUa >KE-)o_$5#L"}!.s:ѧ *cA (die$4Fą6}پr8])m_d}+004|jL>IidD; VUv*aU=DPWX:hߒەejy'; ]bNnP vtJTF_n ʎem.pkmb!I?UVl@4ZEvJٺEGY(f鄃u%j#;akWy[vmCԠ^rDK>ȎQڑR" &JX/U-;\sl}"4;x\b#ިu &w ^vus8ce/i,9)V&(VEvXj&і[]rFnAy9V]`JiMrn;(c23қ+Yl$U3ϲ@9U J\1KG YMrZC4ywYb;61B^tfuzGҖn diD [!3Z:A|W)ا؁(&;%KElG Yڲs@]od E<݅n;{ArT z2M~屒]cTq*nL[rRczӌBQ[X{[?K;,:S2n#.I3wGOc;[ WM@dx}*Nt"ye?$Vvuq2\h#9z߲ӜvXQ/7s.)NZvZ|B z ՜&N\m~ g&9ײCfk CW Y@FF>NB[;w >IkBb{*زt^-ѭ@r^dGىn=%$o<*;kL-]o) 4fY[wWsX?8up،ca}J~γty#; Cl}4[KR ^u_dRa[[tGϒUtmt)fme`e?civ#ZkfW|e6! cWi i-9T,9WCȎBa[fB$]*dkΰX2$;IVN#%,hi1 Meppš▎NSMOj|;-;CJ*fiOs7^UvvQ4)jiǖdx]Mvc{-e_HJrH .)[gt zYvb`)YZMۺCKLZ>Nm-"=IvSEK}S۟ಉP6ںA :>N`:&3E,mhil\'AQ{q\k-(e5(RiKaD1K; JQ`*YrJ}oeBV2) \̷u:INj(;4W#-6q%[v;Y{kfoJX:z eAW,y%;N@IK'#CxIc^^1lכ3sU3Ue;1M FK?hNJYڒĞlQ҅#Ɋ#E5|篿dx͓zNm%G4Bv7)lk剢6 I[*mHCuժ%}]e,9xJQMsl-=UҚl+a,HFBnnhCjwEBsс:B}pJi^x6Xw_IKGB9ᜭ*X#l9`0~Eny-9O{H%;ɯy-;Ji! +di53jk9}o(oxe~/ UrN-egؕc%5Fv Vax]HLNdłlm=T>7crbNc˶ =|׻/L2$'4V Ԯ#,RrHs4Q[jiuv[YK_L~F%[؛䂚_,֑cm AAAA ApA0ApAAAAAA'z4=7h2*FqFf*:%\ 0qFC"> 0ίWCCLwB'p;0M"^`fxsfÉG0G#*}CO`'Jx.+%^Зk90w+Լp-$ܬ"\Cʗp1%bpI*^+y;h?攣!݇hf< _KϻwZ#%lCtRUR>B9!qa4/Bc1y(Ot(3 L,*7`" ]PU"(ȇk4fc l=h:(K}Of Lءͅs0B؉S!(s4=JuԽ'`ZUFE+ejp ؕa_]sW~largV9FF3'.iatKoà}&|$a?* ^b+KGĄE=6Qe0 1y,̈́/`uJ9AB(BFc3i$г|AA}6\ ,VpPKJ؄n$"9c Mv!^G0O3|%+GxvU8И2w F@AM*zfiC 꿀KQ9&+MȄtLD:CcAC|?wrE^82vHL;A#a P ' .g`rAQ8rܴ 9Ng |} m?gDZEs PiσhgMe2aW~M/hp ֗N9;iaz F30%tۦÞ<0?*J((?v_ϩu<,0qƃd/0C#Zw)nrp\R>2) ٤p?` 1#E 0A !8Q`NQX:F#!FdizEkSJfD+sg`)w-XPa*E"fP.alrAGe0r¢X,llgX@p >-Qnax ]JY;""L\a] X\|%+| yXN"B؉[ {R 3gh<,9vKԔM!Ij(#=?\،KrfO$4Vg  ohy,)3`h&ǩR4+$U "N:7R1 f)_EhGa}­x啽}98P~ q*;&.T 黄R==)G \Omw Qnl8k x<=* g`h>!.+3#]hp 5nMD+0ś? j܀O=UJw`"DC%v`WNA8悳xPIN Ƨ@,RSXp 2lv>_ IDATFB47ρ XNRX:Ac*l )({$34ϣ4n!9b (KMF:$"]@۩R9c0 ^(c`%,1>ܡ@7JaK(s`{@]1eL-Q.a30e`'‹p NǑ[0/hp $X/0G /<`rEVce2*< 0$DaEiB31O@Mxx#X|Q;G J>L]i^:y+܂YGh4G} VVN8=hp 'PX.tK *c7`7IV:D#aw9ƿ瓲vGTs'%feʏ] (}Xʘ2??^9af*TC \.t[%&IyePѡ.R;$6)aM6PkI< RP>>ܣ%^ch]l$͡>n<*3W-E_I Kgh27Jn#8( Eeh n(K(@RxVNч칆 A#X " ݡ)'1\gPBSmNDTE)@,QV%ܢyj9a*zFiB9m6xR "1aQ. ə2(#yF䄭#4XT&n )#"eV [h"{id-0Tff+gĄ;ȋp/ ?VP [q 13 #0LpAiƕ;@E9‡BA9Ba'οQ+Y2O)sW`r0vǰ|H8Jv)xFiB#_Ye|><59DC8lE?UJpFiC8#X%i.JNX;C4K@PAɯg)%oLz+䕍30EKNLG+䅍S4EKC}&Q^=XlI(7߈ #? UY#Jq+8y@i eX윀Ea}"r>wENpv=*w`Ly&n"%^Qm)8n-Xq޼$wƃ Jj*ɣTX(lRfn<mf<j](:R=aP/`2?0GU9:t=M*Fä(sUvM: +0KM6x.,5> pY.3(ÖO`t f+' Ad 4C]85/oMDpWB)(X2( O'`rB3")Lܡ/䅵s4CM8:@!Kె ($:¡_IeX&]j!1a,]J )pJr,UFɯApG܂,1eXI S0ES9#t< c? 9aRm}~Fy n (4< # W=X!\Y| -ܠE)!P+Hp_A-Y^p@i Gie,RT4 +&!ܣU%" װ:Ѿੁ n(ō,n`2V*KW`w >G%"=MR-Z lOԅo䭍 (م*R?^GgIJKs a "|9FK'6JCDI5஌[ (ŵ"R N(I#^/v)=-GNZV* 7`v (+;g5LTN8mj })a'Z%#ݠ%%$l1!|;@ {h4$ T:$ Xp_E%RpG)m$8 (g~Qz^6qe lTOX&,,PT6.,)`r B+!&|xB[< #h0uV hvxvN'j)i Wh$tP%=d炛"Ja%̡g^pDi~ (`/ U^?$?`2qV(s`r))[`wh(?}0ʛDM\6I chga}!',]9"|9AԅCp@-] ;MEt=4F[G g Z@Sv&iGGhpp {'U)"육Y^s4GIظ@K@AXB䅥kDNXA_ sh0sVy~5=Z'%= #QHO+q3&&~ = }].Mt}0F[ Z!HSq&i(G`rp){U윃Yl\yX,Wn9e,U eXIkNJ{$?`Ge lP?ĕ^61Uz7"J%MAy'؋t.J,Gy= (oJ(8J4$R>Tw Je+8Ϣ7Ji-̣WJa).Jn.-dg Jf*UEyk(7Jj$xl$M੅<6J'xD7&\ 1DEغ@ 䅅[JZ{D[ą{1"trp f(+W`2 RF?Ĕ(J$8LeQJ+u %;FImpi ߎ4UaSn $!|xA@xS~ DEٺ 䕅;JZ+}o{s<4 Jy-*f_5P Ec 'h QV2=QxF;DqZʏc0MUٹ[“26I(o]?(̓\Ps]%=n{w4J[8;@ԅS4KIXBKdh0%t1('G`QTVn2el "?~uQ[EVPǂ&J/x}C#t EظD䄹;FJzcI,Uf:IelU7^ [pB9RpSFL KhtC4MEظB_xm"\)X"LU/$Jm+,d'J]MRv."YamGH]t)3`һ (Oy%?7P=A{hw s? ힿ8M QP~un ufQ ] %!x}Gc4KQXEk$Q(?N %eR^J[pR^ n*(ใFh 'G/5Z!-<:l,=m"YpBnW%X%&\)%Z"#L@]r 3hS&h({`2w6H(}{q^pC)~<o]4NS88Es;Σ-RTIe|'Rp[EI /ohW1$ܠUR}"cvVH+#`IpFe੍w4·4G^y@_trf)(s`vgPs}%&OI# lWaFIt4͋rICps(k`GHTׂ*J/hMR6*I=UԔRK (ോ& ; ia垘8 %XIyc(OE%3<19Z$#=؉gyDFxN<9amV~9#dQ 3C %N,am7g`p6)g3Ji!@ %amo!܁uJ RY nk(=_4EEXAk< }E?$UeT*@mԇେ>P )a>< X& 8Jc',</h-,(Gg`2l> Ns(h h0v?eelQyy x)^h=G`'e 'Q*K&J-E{oT_`9 (േ&+wh_w~2+{@w XGv#/+݁ b۷4 ^UamW̓U^nF ''h0|'&AMٸĕ$ 5a|v){FpSCI gh0tOguH(ݳ88ua̒WJc+iJ׽ࢄ/$,ܣ Q.ieG ܷFKrֈs,Rq%;E(SO?RQv6P%Z%qX$'8ʢF>(g:AUYDP[oX[8@+f=wOkWs x(;#k"zGr1OI@sBI^JPS`%VH܆W!s0H~|77ADu57)WĶ!| .H욂@U%Fbi/* Ꝑ8qPı#H4\8s@+HhB@= R!q xV5EA-ׁU9ZA{(*pHK5 VB!+_D3*Pmc,((e.%Z@ωqmTU$҆*( 8ʺ{W&HVTD5ВPbV(DcJzG$zrh*O,u Z񔘖v\rNiPı+H)oknl%@RBPSXJ\|׬^A3 hDΑ`.ꏋ.1. 8;Oʂ$mABE`-@" *p$>HGNqZ9Į%dӟ$D934;~$F9=/OJy7ۦ $d)QSB17p{ì@>ҙ{T4!iE`!QUX#q z1.@҅xQ4#~DUEZuЫQ IDATz* @ AAI~,O "]٩JU]Q iӋ[V "=ʏǘʠH,khԐ XJ#UgHOV.p2hzGbUt4oO_BޏR4<#q ^A'@.ҁەW!!(*lKLJHk~%\PjEIJ.d-*OlLqAo$vmARyq M  9zHR(/P6H'WzSbV(ZGZ:5 T#,aTĺ!J?9IA  A?%!9RHPЋK&1. o"m{ @ϙ]Ģ&f$ޣUHJ mKP$.KZYW_Uڵ9rJu\"[$ncAI1!1- #xUE@,<-UAX 惍D3&Pitck^T}\w)弎@>'U; q JZ=?--7|I1Psc*?ļ*F]z(G:r5H&1A$}ANIgCbVDOs@ҁSRz[    )1 *7ݴ@.Ҟ?GYQP}dm ^ OHĚPbQ*@Ҏ+QQT!=I@#QXMSщrgJbUUHw|JYPCPR/C$V A\wKb(Bpk*;+G{,({uSP!>sJ4<-]=*Py5~U!q*b$6-AR9Cv"1*o v@O+SU "q R4#m Rz̎\%>ޑ XI4bGHnfNIP{c歊ސuielӁqIYK4HgN9Pksnz*&H컂WO{n@њ}h%8{ +$…'P 4"q JwiE.GA^ؓD'-Pk5H~\jD'҅?bQ>CsnJZ+$XIeA+P4:!qqH?$6mAZi-Oś xGbRDT6W$bu$R-5SL!굨@ҙ-) Mn^@%R nx}Ԙ XWB&W$AA1x ^BeCPΝ9pVQ مĨ(PPK%H+%ZI]wT*x'ʊoa^qPcdcJ:$ /V\y"DT/H| Efn~GbdAb$u%f5A=s, zo$9 +(A01 #WYEUHWm`k_HK/rrEU}8%1dz o*DbA; Ub\F제JL@W]b&H(wK(j Jv9ŝ @P}rAGTH|OELyPcJY#zU\ =عy@)7GURTD:sQ7=.Pi Z)-%d+ x/w@A"_9K|?Ӛ !hʘ HۂW%}]OW }#q JؔLR\9PosiSJ+ 1Gw @ ҕ,K鋫wT#T@҆ K jO' [GaQ_"1*:Gn X6) :Dbd)ZܑHPʠ׉k쑸 ߋpH+j-l=9Ao3RTT#]8JDȏ׫(C7eHkl$: \xl_0%?IJ%xՋ?"q!bʁk[ZPT!9 ͔@uK" ̯TZ 9rDbiQd\ Wi]PƱX=% x,}WަHW(sO_(ZG 37Fý 6Hq> @O/~W#)P[9Jʂ fG(r9Г3שQx+DzwJ4@2%ʕĤ&+k! -I+ j̐99@|iD//Pu&1oRJ 3U"YN1Pmtڼ'ʴLσİ$]XYe + ꬐leQPytN%gqUSΣ 1G$nsdJ)Psth+Q =- 3?7ARi/ah"*;$+oVqP}tbqm/哽Ġ(F)܆HQA҃?}5&P48KLꂤRuWHT#}q xJ"`[uu$FALY!5_5gHW.S#%Pn_$-AV1q@r.C/ "*HwDstyjq2McEb&H)ig$.'ґ -Pb%"A YrT|!xp 8ao$T Ԙ"]fZDI=qX9hsm J}-'1( ʻNy) j/nlw=!PZ^J^ U T!xx`y%۱m[Q[#={*@ %1 JԃR6A:5meW 呇Ī#7^* n^bXY=WPk 'J{.H̛W=9#5)UH{WFnH.b9H|I%}.e>텛IJ#(HHPwt73ʾ?$n~RIPӍ1e|%/HB7q5Y`>ד%k>Pb;01S37W[DH\fn{V%uAJ/>pbWbe뙻Īk "ʃ: o.p++PPDZk 6BÊ bD|۫UbǚH.{JD+ jL _\]RKb&H+nG;$عո@w/ĺgʁs+W_NhH|MiĨ*H(e'G$k׻($\XyELyot&7Z]~(z#y P/H|/"bp,1m^-R޿j RuĦ 'F#HV(Us&v!qb!rX'cJ> 8H 雫?P(q'$a=%P쒬oH\~BD\ӭ;}^@Ձk,1k RԞ!B K>"=9ͬ@тX\rLt C$cUԵk?H,NyWPct td?내bVBqq:ATY3:="ܺT@vysG.x=E:IJF5>'gylŔٍUߎ$ ~UTh#H&nZ>"PݘH~)&PjH|<`դ@Yxl(yz@oQe\ȏ-҃q%ݐ #8 Rys3&*iM 8JL[nfT}pXtq%Aվ5&xRJt)e"qz Q2ueaq=ë+ѬϝkdYGkongi* ] O(Y]G$8 ݲ iOlēIJ7J+ 7 D1aҕO8nHRj>J(QsH!'gWx*أWE@wcm7rn`ta.'-ST:'8ܢrBxLHO_ym~:w~$C8+u۷"qͼ Pʅ/<̴mGz;"5w*Ib7vCzp1sUoCcdo$6#?(S3 m1%=N99~/JmuWSyxtjuAVBʂކFK i3ϽyWbӻ3#$]O]R1PH\(|lnxGoڈ^A؍ t!(׫8!SS$7 9K,z^PYݑJ=#FAC.Jͽ}Pa]nf EUu5 ↦OݮJJh֔ H_ 8K,O.*Pl,;;?qrC^`}Ay}UBzz.?$.+ bzUcW]b7_@AXHgBlѷPو@xGzpu>6(84+ҭҊI=H{N(w&FMb;BNht{ހW :ғ:jX^@ӹFs;eAJuV JYԳGb: cta(g]&([#R߼rݐmݿ 7nP87g 'W`b%yA)vLRJQPn'$8yluU TXU$v6G6S0`Th[z%Z͢ ?_?Fq0 7g"(_{;7Y% paHl'6Dt![#_"qYk+ RTi; 5ERQPfNH<\hr?s4%3砞7}VgTIPiV']JMxt!QJ 8݉)q\FAP6{{C7STxw?iRd%hiCŬSӹyh)Et~nH<.ða:_r5;5!ݸy E}pM݊ZVŻH\7֡SD[{}FlySS2=` ҕb94ń@ACޛZ\ql[Kv#qy6z5{TlxїG'Wc7_#JFctEb(H́?qDr1bYHd{#% V@_Aqzu((SG-|;WqA?YHf~FQRRMy뱩qv,VG7r1toiAT/VgX$BӞC\HMB9ܐxmgsSwʝC{61%RE<9?߭PyXZw*-Q uwuA* )/7õY}XjVS̮:}Bz\RJQ%oDx“*(x&#p#q?QjFJg"qݺ7T.s)usL1PjM+&fɋQT|϶HWB{3} ?8-HwT2@q3TZ)Ꝙ=Ť0I'qC{oBGa7wtUE@ߴ/c?\5'H(xin;0l3@єuVOvWnU|tf6xCyvg)/[t)q8.㕍 ow_$;!FXi-ㅿl\~HD֡ltMb }&q\a(1?)VofSw^cENXiG&K+J\f(/wnѓy|v D9+HHH/\>ìaams ADqS~|q_ZJFg[oP⺷w2E_gh6G+zXA)oLfSF{zP:(Hyk- ;]yGB$nGl׋6=((7dj/!wx1 *݃[w(F#Wz#?z =%Nb%JG8.6nH<.zOk0ŌsQ ;ٍ=]ڵ no<8╼S؈ZD~|q9nܖqXK2(HZrݹ]j<$.{3fz "9n`whV;{ɂ/'bU̧1E\lіz|v($Wz3w-VP*Uxk;ߓ}F >jAKxU6,hj(g;zi&wl2R@S'gspy<%oa}Z5M w B$gCn3VH R)ͮE DI.]Ι@QP2k-oxi<^<o.?$?fݪ+(Ewl./Hw<$GW}5s?VrqATLE7ݮؕX͍=l\@NaLŒ[K9Ӆx8;C!O۲;îsY/;L Ak9뵆:ޏ q>I@wP8^3?',<-bijIALBRYoLY_nH! 1n%*P4ȗ{c3K;H/~Ο}ڨ\xE3] .Vi{#X^˜U 6(8RT:d4kxm!ϛwWssBk+Ew5U{ .O߹w'3[9oQo Kf9_4DqniQ,k<K7m<|ѕ,dVt[Vvpsi#L ԬU}L j. IDAT$?s 377\+QOQ7ܥnNvoڮ7'>;~ W=9x Gd4۞[XYŻDxwS^㡵4]j fS@qOl6ou`gSzk_WoIH(qz[CzT=[ЬRuM)@k{<[sG yz'{]y;&+V)ţhq[I6 ?VTC3:˕ۻq?BBwceGƏks5]zյ/ P6oF1JRuMנs!Ga67R덹<} >ű8UO]ϡu YͤV˾S!Ĥn,*(_Lu*\ZĴƮ?0`t;tvnw'B͕\ q:6þGrk@62v!PQ  D2eryYkNד6'әZێ];zif;5Xnv7^J{zYS o|F#$wߒI3-Y|޸bV>sGi4Nv:t ~$.tr\vnnܜ~Րnᒄ!nlo]Φ"n#mvp;6:92ÁA\nai ^5Y);ǥ^\ ]T;F#EXN&˹+R j&5vy]So͇Ci)&j6t-~2\v:y.m_wR$§qN~b'7x(wWip3tur y)=LpnF>k?ғENXbaGKGg21SXց ~,6xtLN|v/Kw:?Fvswxϧ CDzMݾՅ^ 쮞O6ttsػ`ufm+ji2 ObnM3ljhdlƟުy =cÁpQu=~ݎ봽)l5=͆l{M^֪\)r\*Zb]|Σl{It?RI|DD,f"H:& q\qcoe.-\}ɾ8oFC\Im™M|V1*%x!gͤv(*YLm_.oJe+Vfu{Fg8,X,mfkGru>;ݮZ/ݓeAϱFbQ˦dܡ ~'DT$SރlKZ3ߍt `hm:_8|1s- ٩1,agV6+۱ɧ]n}nnf2XE,'" ƙ>6-/nM}r&2D*k+u{xEfKpvy(9ǭ;3ɽ}^:kqT"2vŊ nu= j{pnߖ@kîxkr)Ѹ{^ֻx =ۥ6XV63u~k :m%iVodݨ5]mߝJwo3wmkRɛPΗȿ tjf7=*1ՆNxn|lT ާDwSע,V޿whk4|귓xkWyk[zG]WV)?x%%E,hvMti~~q| λM,~?vç+[]H$HD/We=8!-9%QX|O&fp4ڂԟ+% U;/OuM^t"0O7k4#ǟ@U:OX鬵d]j\@C9h٠b7=Qd΢5|czno'{33dwG0+N\@د{.qjoR}8ӯV!2̆&▴TwSw:o W.Y#0U= ` &` H &$$A@{  XOsk=8 E*ՃyH pg[eO,t ⏭,۩dʅ56,ͺq\}1 əb=>Xm]A`V |9l|otޝ㛭6X?i/1)~Qx⇳?,g  ZAb: ?gBP7M:^dzBr@~5?֓ʑDAN8ۄ׿[C2OЄ x{"[ebZQ|%X04pok9  MRb RHRi\lk7mWBREZr2^p_)|>Bx:@S#V}ùT./%gIP pG[iY L'd +5La'n; -e\Dr" *Wi]Rr̂;?Zb+.?%V x z>> ɸ6 Qɩ|XWݙgkSک|2`/ E0ʱנ[`Z1a ~$%8|vUېzF[=,djݏag0lXGϩ%ru:㐛Flo".9S .Bn0jtfEr6ҽ:[ IJKW;#:7Q9 Yl}u3R mς#1$~ (}tw:ٯ^pr+<`ahq9whk=}8^Dr2lNڧښ+9[z!"7~56db -|qTBr$umNlk; +R/<ٰQ R2$V 桐f=MJr12l7a{PTs+P7]l~SL~. Z,t>INB{)u ƫݺ%F@6e~2Kbτl}/vrS*UʷgFdk3 a?, !Lww*)px6ӝ]>|~-?!?lF'pQ3|L$7ښvB1^$+ rE? 1/ߚ# >Z#W{~'"NLrN8 "DQq#S2aסrR_T~zPuzPοr4lgjR! A+=j7fwݽy/8 %ZW ;Z CR$>)P,4$8X B\r\nf0;Q#䗷[Ui*z$'yXu2P:PH.I9 ~j5n|`(/ c֬)r;lZxXJr\X~3g[qpfJ}7g;ςE%KaWk}:.5A|׾{ϽڎCޒ0- ߈֨4!G3A<NPz%j!O#腧FyWlKrhݓ,7jk7 /XJ/K M([R~Jtg!gw 1ُб\6%!^'q*㨜(_Fk$OCʯ0%RI+(#4t~סQ݋JTBbw|qI;GߡHj\L~·Ha̭9) l~igkM{ {ӞeZMU'rPfǣ0L0H?kikP x.2X<#C C35~}.u6N"_Ja~;꟭([L`tFr9ieFˆ/>:.,4kV'T:ؚw6gEɟ0K.8)_ORZP˅d6zJ{$wf [߳RWJ &%$[;g^Jhv&̀_9=o9֪HDHgYrwC?lIXuࣞ%IIo A涺%$9|\ָ AɎl-{iYނXm'3 KnBX*`]۔R'D!yQZ6Fб(hk y#%lơ8$ 7 )Hr7h=!"$ƛ 3U E[(?TJ~ʄ V}}1֫(5 ߭"RyeҒ+ɺKo4WFwekP $J7Sz5kUHlnf /6`}.J%aq/ #cvnr3Ϯ\hji_ k-rGia΅ P ?%/+]g-'i`^$|[IQ>8eɽ%ZCm^l0k#?fD\N^hWoxmoH4)rѿPu\>yPJ؂.H?[^YJq=>ؚ6+19Y /4ymkP'z\3[ HH~&WKWfg^ܞnb ZU`͜m-I9&N4aok ET9ll˰kH733Iɥnٺ4$+> <ݭ,lC1;s8fd) s'x!:fnw֓R V1fȃ}cR˽Pel8paK 1)I.jk3ۙ#`h\mws1;?2[[Z`NMz8zlC>RԾ =_lʉR8J'gZ9@IK\H׶%+bޅ(UJnq`kRD$g?}TIRZvKd9ysS[,|d$|DWz\UiCjG; Ir>ZuM9A%wxthLcr`{o[ Xd%7&HwSr8䥭n`:N"Ԭ.yLjQ࢒ɺ´)pIro>'[V3*|w5UOCCNn6rRâ,V:)ɕAn}OYYU"lm}U,&2 [%Xs5k(t-( U{ Ůֽ@pB~~ 0s[<ļH~apȑlX7+. ~a61"yA[F`QˉpԳ;ʆzhR@֨BDJ.CO~Hϥ A* *]P^k^$ƀ{Չ/0?m()Fن7-PV@TN2GVjig3SZOrFd]alJvJK?[V@PksТuSO,5-Bvc啒lU9pMȇji$ml BVrkAl+D6):/M|J.B7lm{Q9 ]80)'*!@?uO̓mE9[S&jI:V<:v kʼ5x'iɍlMkWrl\+"9 <ݭ T+.C,y|u_e m4.yzb*f0WqlJ##l -/NKV󕷨HIV-Guɳ_1UQ卷*FhKW:('?{Lr`hNc IDATrZ:q9 Y~`IuBlm:($$7[&K.`Y&%צ(s5.sa7'[%wDji7lՓt&yzFɱJ6%/3H^?p"u_3rc]fsuN`_ZAbTζk%Ʌ6mZKLr鋪_l`\МI^$7;d%WV/O3 {[l]jgXǒ)Ʀ*yr[ݐz8Y96`]a_J d'aDrDXðI19 Uܭ]//ωbkVҒ[+NS/_i`(w.QVub\֠%wj#NےזW!pi#b|@5ЫVZ&yv!})~'!q9Ӧ[&z&$fio6UqZ=AW9Q wZqg[2f%w6qiu+8h[h:j19a ڒ\V/%ɣ#i*GAK:”2߬]K#dk\l9-f-l5ߐQ.GKuG\ nuHMIn,Xn%O7Ur 6T%T#3&<;"%v V=A՛wPX9TlA'9c05vE6:@HšRٹ,YB~i?M(+W.Hlf$j?{PG[_:P<>DPFᏸX.\Nf%/|UI6jpɭ5Xت?Ef[2YK(V)bCs=#F OCʒ'#JPj:_ɵ9jۆ>)R[:HKno(V-&]|brlMJ$ yMGd'8'ɥ19OE.]fݬm Je")nkQcf2;[4ÚLaQ9;9.J)5;wXVgo]Uɳ+ts-v6-OJn,•fη"PmٚWMmUHg2'0@Qi|<:Az*YY$WlV7`$ofAZ7KH B4Lf&*'g'pjG?zd1v:$OMɟ\muӔɣg!QEEzi<@:uKˋr 9Rx:ǓFRlHZU,{D҄h%t H} O4ΎȎ1R Cu-"[%Yo~kƈȦ}.?ט>Ue9+mn#JcDJ/)V-,f؇qS%cs(_j<7=:ʌU[j&.d')JJ!"4v [ up&4lc<ʈ'޵kFrHpEmt S~@DryƗ_ڪE9̘3od++$]ZjSݳ4@6K>I91!jI^&tdk&P^xЛI2$@ ɭ_#Mq>OoDN68yaw[٪3"(s /ETrmEv$ mqolL(lymT(yr#FUQA՜G*$nkZw俒?jmm:ҢY<:+ypFOz%XPp?I.AdBcu2w5ʁf z}:90]M0eo CeЂ>h۔Xت*C인XR=$rOY;?#hV#\M@UEكͧQqjKJn)^{V' 4k2fyɣ 8a>" %ܩmIqvVMlkaW:|PZrȎYvq~o3.yz p%WpĩV+ΣO)n4xsO܄/JdM}?L u-cc±,5:|PɽmlphNUILI2W $w SpXslR$iP*;\ %{g[WMb>Ư:۴+?& |a?[4INJ[{܈%XE]l}e}95]>%b_!°]mԣ &%OF[vm"d,9Om/H܁Ā7A2}UC$mjfJ7 Y7KyjDY9N Xb#&''yte*X]ːJufԬ/ MԅUGEruqܘsGvxQRu]S[ΡI2J$6iɃ _;צ$@,krљn|iwF-rwRU$o@eXPXZ1n s }ª pl"Uya"%w*mHy6'H4EDom#iM)Ht`ߠe0:#yx%%j͙{Ӻ:<Ցh$G{o8wz^UVԩO#țCs,m54v,_iT5)w h>b ^$I>׷X”.ت೭^I0tnWG ktYɣHi|2xc?ė[l@jǡaa,|uhSV'){bkQ\‘.oT,7d( gd:#RK:o jPFc{Ez 7_a,ּ=bU¯6VyY948UDD.=\!J?x\kwLZ=:@>p秸9"OhODtCdt8ڤB;[uY5*c5>3m@<:cF-L7~DHWo<}(k5./R% Y} IDh,KiH1Yz2q+trK!oM\QumFh.rA`ma\;t Lq95)OHbB|~}R\P.ZVf-*v|VPHtsoڻ@j8n3:dxb=ؑ[ҥ%9a&3PB:j@خh-&dSfQ:qN9{h(5ŞdXG8;cIvngXat'6MCVA݁S6A6.ۼdf33a3cf~)bœ+ے:h[^onp%7Ao?CgֵK+Pm,Px%CsT"ǻaF3LO_Z<N .M֥CDg_^,̽t~E[nÑs3Ly([yu0E2| ,.6ՐnW!]:ϡ Ҷ&^MY &1>jL`"7]\Rn#C.bEtl[βdJ1AÆi :&9HuPcLQYP2xGSh0  Sig8ދSa]~jѴ%J}O Ѧ=&V@{as,ĺٶcvl`S Ҁe +c6jr0,X[]?0e6w,N[U0]gJ[lC ů=`}ȱ$1(=f G>!D<ԺvH|˭0&8ϱǂp!?!UѻRuPY^`MKV/)%p7iغJE%9IJ}% 괮sBxZX&qia"Gf(ѰG{Pq,cBoo5ENQ# a:SY6.4 1Kr໼"ۿcbn@wf&KDnʑ 3D㈹X.P;!v"?jD֘^æK:5(UEggN>MANO،.#1}J[avetQYMu٤ +Zϭz6Q`gfpٍ?ʛ>#Wb#PezpLR?Z#DBXDc5'@_ {E9=B-:߾:et/J'ɹSޝL$abkmY-B0%5WHzmh9zBm( 'ֵM[4s&k!?oȃ:7.Q'~>Ԙ2wL2谲;`0q|􂜙Oڹ2i)괵v%#l0+lLQ?؀gF` 4|I-9 +2+2]v,;u9C!W(Vݩui1lmxlXʊ֏éu=k@=&L6p$>Y*Ԅ"EAc|۳5{c;~RQw ֽCrT?x+]Ƃui=hۖ):Hϐu#8Ir 9ھe2i-˩~"i꽵E1Zu9>6]bݺ$}>XXѥs9IayGy w`=p"4$'?Ж[btk +0GF.n@hάslIKp)GIGk {P_en4b{ gRߑukaYNu#]?S*vQׅu2#hj3#X~BO־ĊO)G{,hGg~uxН)0 _Lf8ڒ;w5_bG2Cw`J(2IĜU.ewv:D(%h:VlJh>!GzbtyalMx}\#ioMTM,ЧLg֩ ؐ_4źG.uqo'kv k[Vsj\arֹFƌAS ~{,Y@Q4R¿rSI6h_en١+Һ% Q_fh'oyjKuެ\\;Qc-Ʌ ZgP[SS֨c`'Ԗz޶O*4.u.1!5999*in}LK.&gWhRY7&Gw|WiT)}\;gyF1|$Q}#$GzܣM֭ɣTC~YNSSχ[%1;dwH rr7|Tk SX 5IMRP6.*R/40IK=[{6ce9B|yn\(yTXnSԜm(2r[kF?o+{LW6#%~icV15["kGzߛ՗Obbl Grq)&5 "Rs}s/Ð3J^r׿8ےKG;$%?OAH-WkZ`SjIj/2x r&wkD*rfͷMW:YX]:ܶ);= 47'?}Q X[4%OrϺչ}7uO1Y$ܤ$ X`F# 99O=~r!pBKrn )Z}923Uvbem22Euo P rCgR~*4c[y_O&!s\Z,J]?vbz:|MS>R,uoER4vA2a(I6jS/q^Uf1˛VHޚF(ƃ5cz Y~q7܁Yӊr׺I0QSkfN4ϢV9&"Ar*)&u-Ӣ4d;'>=OjccVxcV_nm]B[A._,q$)[zH#F}l ]jkk [:Y9:eEl⧷[Kr [ֽ;4#zkGRIZmX/VWN3fe^P1XWdz3PW2<(5`&_CEP1GRXL\8=u.=7E4bݠ59-X9%IÈy`tzX 9GpQkĥveOZi4-9-Ic}a-XcB>#K \=]1{`wAABwAA   ~AALx:紃Q{);ľue-_Eoibᄝ81̐ia9*j3cU2ŻYPQX&]9~ ߤ/lŠ?mdʃCpi1'or&X[t^1>,e: /YiE{JZYNFXFtZ9.3b|'m>=C;-cbx%_sY4@zI,#> Ԗ~]9fk+M$k J_ 9J +ane5+F2GfNc &)aؔB@dztb9mG W.EiPZ*XQL 6/hROvC;v0 /ewʝ΂:!Ãi!#ݠ((S[Ufx_VhE*!:#\os6 GB/8jn`QYa"AћB)>-I圗kQa2"xHM7CLWhFI 'lG* "Qm-|X4̸lw +[e  6>f؍=B'8Ig|]Jݠ[emc)ڎ#ggq Gy&uIDV4h9C= '=cUv<'h]gia 1+42RǗ1ZSz@^f8+/]N;1,v##~Z_ M(vIhY[2ŇķÆ.?# gh lп?XaUGE_n/X4BE} %眴CYFힱ'#)p$#: LgL0Qx0 d=§Ѝ+_7HKWG_5LwVŊԢ/Z?!ע4C4mE'ؖSV:!'E/3ؗ}JG$e B< bb'tbJ-`.[A9!%1f9A+^*u/h;F}%e.`.;؉!~A3$KNFiA8`I ] zr-H+eu'se60 36i-(cSr ugKTxqr(m,\ !", Qǡ{ 8 ?&՘fYd;AX8SXj2!4+j+ŞefAZFc7\|oU@ DI+|`;н@#u*wQYER82Yv ^z=[^ x\3:s%q"Kd`A濰6AU_C%+t#)Ep&s_(ڿ^$-,^U*amD ؖcFᔤNB"N>75VXem r 89>l8C3.Oٔc:i\w'=}a]*㒶/N8G9@'z?X˔aC_؊@Ujy>)>60nOF*Ag2{1/hh%Tƿr { _.j;xHHVGA'RW3z ,kyBd7h#ԐÞ2YHЏ/bYAԓt8)&w _հd [rL{N0kR OXxa0ȏT.tÕI<쳑>!_}NB-ay ;+j-.b[>R2 FFѕOic' nHNdkO72y 6++`;3Eb^([O82.RǪK(.w|:c]q\GlK\N|^g2b'b);섛~ dPz :Y#+[{O8aXm;O 'aOh |<g=t`SNi"T?C_uzAu5%B'ibE~ ҉'t@SZXK >fٽtOVzbU'*dfUjC fNؐJHD2I9Zv 2[ cY7T?PVx~Ha_6wavVve syWKؖCR8 I1%c^wЍb}Kr;.臉._咕~NW0,UK7 n/r+J;L=icey S:y8e'],|cEр`HsɟAA\\LƱU22ypu2 ؓ ^^I\Ƙ9ft AZ"?yśƣ*?-9$dXj=1y7y} d#=,`YZ ](O`]i, c_xnemR>}aMqN`77wNᬛ-Y0y6¼i ?)N)lbz% CmaCN)4zVC9 C p.J#)LÀGibUY2,Nvd,qBUNe#6!ʝt$ UؔLq9ACka,e;lK)lXnW2+ Pu@$ YqC[!"ve{'+G>o_8 OXZ#XƲ ҍ>Qo_H Q35:#Y`b-)o"!:(bIny2caꄯRA<FT%d`)^XN"Yyy0yi3_Xf,KH;E6ؑR|a^<V咑1NcgxFe+Vpukqڞ~-y >:y( |sJ8]o.e,I'h9\ǁ,1qTc(4y?ؐcR8 ƾE=E =\{hi%őuK ӋTd( JڅMxy+,ON|BSk Nd3֤%u[^-;YcWmA A쥅>gԵnMLtE-tMJؕ m-NN}Jk N/i3 OB1eit]-, ؗ>M*',<{:(3E9PV$W uhmP"i$~%6#! ~p%3"Lq`~JrL{R Ps:YLT%;d-:QlGJؓ&>.NBWRÑ0i QZZ!q&#`QzhWÒde8*چm'-WJf8AQ xvkqT-[Tl)0lx"e#J#kj9FűL1sMle N\1Âq>6e.Y8 ]E; kr~9!Г5v} LDWyl~Mu@:OXf3NdOW,Ka45< 2C7g[y\32qp e:u&)) K!!g_o)cWpT'u }-z89XhޝZgp*c~cIz4KcQn9yY ڊ _ klK%6Ki9cAnY8,sa\=Y+uGx7{ib]> I ;?s`Q~e9.4i,[Jؗtp*9AM[ doR;%t R>;#SJ>&1L?Rā1}tqОm>M#;lʟn/\JrM RguPM 8fX./hQû=6ºAKk\Wd})+t `z$S|ƢqCva=I [֥jw|={R +etC-W9?X΢W(kaT-|Wi֣=6V圔%vu-F81nw}XSI;kۈVZXSBVؑsJR2¼N}?P#y0yªp6ç.m`Svx5i:;CEϻH27'g#-=LXkJؕSRZlu9 {KmXR,1uq` Ō&2‚dd})SV$59'[lJ=l԰-kLtq\~#`NR3:Ø)\J+rN[R ڀ} u+[|8bUڸ m}`J 2çǒ !fu C-38>(KH%\ 50ǝ4!{;ૼ&*rv`W,u c͇8w,wZؓrP+h5U>d i.Wm&|z*vd_XC=P!L#7XƘ۳Lq ŐZZ_V |?;CC}l'HQ (q(S0'cI>3h)~q!=|-j=cd-+L] #I!>8+h+bRÎ,1yPᣮug]Y`s2I_m.PAؐ&n7_PL5?\zXú:tvaNŪdUr`Z| Iao(FZ fR6>*\ ~ j)< ºZhüߒǩ"G+éb)]_m&,Nd/rV3Ni]AK\cyDz=֤syt3a'%K ;|6.,: (Y81咐-֥˰ :U> d&e yUr:V3~}b^eMi: yB_+3rBXwJ9huC #y9 K2Gyao>K 2|2ƾTFNU8["NU9d=>LtN5Ǎ/R&{L\0/SJ wa+u]}]l*yI_e+d07t66-9KoRM6`,HƆl0ϲ4p.MX7yƥ)kL0#K|&b a_YaFL K]i]CS[ԧ]Ė,In)YcCZ:hh ɦp# l(K <k. Rǎ1'Itp&XRw]i>ĝ0/ׄ&]J>=ԴS-Dd<Ézaň}`QVc)F-*:")Kr-I)*uGx!?c_‰UaZXNǝ+lc7ZؑNX1 ]CC;0B=ccMvYaC8|<ck v0Yp$/g `U؅cy;PX-喔=%|&:6`PS`S˝ 3l%6򺀿چ]$KibGX#>\YVX.dtڇV`޿IRÁcUveyǾp#ARciK৔q"]l rOX) E CK[0Dža[fX_0+KlH'R7<ڃCG2?ؗVd7L2Tq+0Σ#+l42MA^p,=A>$H :BQwÕԱ/X >NΘ%֥CF2罞!KhhFZf`x.]cEv9bAؔw5$E_D^S$~J g¶L,|rL%-J n:`%s${yṯ(GL`Jv[ؑ&ΥuCWB:ߒTq$=lr+fdUyǞq)9<ѮZ_"Y\_eY _JRõ<ǒ ra5S(ϒǥ4'#30/ lHGRwz.]kvx" ;2cJn M82WO( ZچŠDSicKfXLQX gRď R@G`E8FV$;Xؔߘ-Vd]iBxfCFUAOMxnSyÞ !s, nfeeMa_82n1?x4}mLVpNgV^p&MK2bVΘϲŲ̰!}II'AY ZpgIL" rO0#;,2D*%a dt%]@]L kQ"K Rǡt%rG eEYૼcGZ8*$+dqWHWAG0$;)Rj87K[2WY`Y#7|/L2úc[:8N+)^2xD~ã ;(j:ڇ`2l5GM5ؾIIRTp& J; aEXf r+>Œ,&c| RÅp+Y
    ĪL)}J GRƍCRxX/O^tuA[0&L +-^07KERxgJ9n%;I =Hdu t  ڄL++xKx h>4 cc|J wr[QJkK '/]2Ū,,k,3Ǥ=_ /eWLVFؔ=i4p,55.RgFNCRG?eJwp[ JkCM'!]@`ujZIX79Wq-e\HRǑ@:ؕ>d `Mx-+,#'dgÔ|"Lsy,FfX16;]i@88 . rO_%oq{}s}>AQPTu u7@G \o`%xhz ;Mx8t½q'S|,n7W/g)\AJ+\Ir \8N+NciH8;}`Oؕvm`K7bSFؐ1e2Ś̰*seeYᕬ$XG',3'^+dŬ|`Fv=IĄH&>r~~ ɎMx*wWx5WxWxwxWwxwxwxwwx׏pceX19V+Xm;V"y$"H86RB?!HҺ~CVp+\  E%]ÕKTtUAMp:CK>t]BO;6 zCmH0:LZVaX , +Ax<j6z[›‡f`ik <g$$?_)R& Οq'g7?Ƚ\k('%?OEď5V\qIw nns*ǿQ}͔'!4Ogrw? D'c{? @'};wetߑ (LO2?dq,O]Y;> KOtߐE^Ok9"'e8/(N|[&<|o֟'????'?''N9_2'W8_dWO< &'8yd %pd )mN&p2¿8}N8?䤋cNO8i?'MF?l0?_5f߫2Tw߽f2o_Q#~BW;حWKcw =K?= pD#2Q? VsxZ6`-k::o:N (y}s}!7oK|tYEK%>n;>r~b'n C|T*.q cs+Hvj븴ǑߚIk^5OZaUjFz@W!4t5õPt)%g')ŧYi5^/.)6 T%Wx2ST>nT `#+3;Z|YťD_~4>|C xKx+Xjfz cm½=h=4u :.J>Ån $C.ig\CN!$5\(/.6.O&v>j(WqԈ'ƃb\s$4 Z-\oa-ij+}nLx( e<3k%M;M&<ǽWXƘq S/xӡZ\<ëpAk^KH!J 纃D8˫q߉̄y\ ݉g+V<ɮHHrSxKXiڂC]pOHK<.Cq :*5CKƍu!&-R qK)>5-Z+0;:>…C*l":4V}92֤#? L#"4 [ÃT>4u 򺅴2=i$&R6ǣx=xӋ|Jo$ozZ6`= ~M."Qwh{d<z$.Y*rDN`>4Ozsývc .@"4t%}3 B$rG42%d>#9ב!3zZcHrcEH5r-wrYF_O=-\uE}R#/#f9HY9 Udt#*{yXkFځNZ!;ȍmEϑH9 EvɓS)]JfY`M=.u@;iG*#q"`&tn#{0Gl+PMP'8=XGf;R#n"#4.26#)HLsx2L:[HK䥖"] HE#/ ^Ro^{\3d(H::Ҵ"1Hqg#7 @p+D2%\# ȅ[Hz m@OP}\Fi7􏒌}hMS69~%tt Y$?..cɳPH۟F$*NQOY06t W@}B6EP 4xw*izZvs(R:H¹ɹGMT|~:ȟ4/zSmA['Pg8WU䇶2tȆ-GK+R4_$çfIo`3(+%ʓ#ca6"ZH-HA04td`G>o"ET} >7A[P $aHO2^ԗaxt^EA19Xi::QtxP7eijg(Qt&(J7唃^R\ILQ u-Qa ^:>A&h/'^Gwj5 Ρ`pGaf:̉[(4F[.4sG4;-\[M}pDp%w!EU A:X;L;c(]dF ;vV!*!t>ZLt){-L;Pp7fэk3Y-A064t Y\ v՚S/`-h ΃-"- LESjzt 9 kg-yjE{OaMh.3x5ȗhIՎn!uy=ϵzPɇ>VXh~AAk)D nrٙGNRwc*.W/Z|'AO'PԏmW0KRrM3LVc({ԫD+Rhu6 [tKF#at%u4u+ ʺhs-^Ã&:j_Fx uh u2:~Vhz:y]ʄv-FwkܘgAWg`ي6XdZƭ)x[.ՔRkGC[ WhMpmmF;5ڀ._f7ڍ%•ƎEy km7]A6X}MpK^c$;faw5MGj4,sXj 2lD7C,V`m#0ږnC?EgyȼvV E{hȵvϐ rhZ!:-圃`= $u]?ZĬJ&ڇfW~ƩcnHD׭n=H]Fǵ%=u\=kkK\7J߃q 3"Z~ v[-ýG;Z t Eg-+D* /Z.G=A/apv.ڊ&`V%:oS 0=z7i*utϣa}mpoDtr4/0Ttxwx /`څƳGس0cJ_{a Dۣl6̳b.,, FyC {4-We|3k>B*x~m{GkܚmBS@:>J.I{7QMא ^h3湯&I輾HF/&YoKaLT]FSgYM@* hc"9;N_brh$LP֏ 9]XGMh$1"I579[p/c%~CI!w^Ӱ }Nhi6Z.,X CbFOR?Chg<ڳy K@U7!.r@-t ? ]]!'9Z{@>b!4: ,F|v]*dLitgIPח\:&=뾌A(LPCsA0qL {!2ɖYg1X0c*fڇ+݇tPRr t! {G Zg)^M}r.Y*nUq),e݅1%8|ju \r 9 p\1ci^#$BM=B2pcڅk1mcHILK.BF:zeh Z!*?-da{DSsB9Nc>̧ئIw1d!l m]Cʟ11S̴y1kmCU!z{x ;a&0B1.@UL !#zB-&$v&l r½0!\\`;И7%dBKK~ƈ'wp!}L oev`P7 (cceBdQ% נ8GmAUC1|k!^ } IcYhQH $BH\5|l7SKdBX#J*愖qoF:\j=C=} Y=\1$3mȟMJP}HJu rMwPmð0F唍BSCFkxrvz]C2D:Ō"j:n ׸:בU(:bV;F˝$׺ PBڄ[݄|1/L!m5O$bM\ ͳ2tuwfF&ӖKdC]<s0C_zR3>3DN GmG4;ly5$ia CܛV*qc2l^AOg F2f,PpZ,eǘ9Luȋ0_clދiXj„'?b(!o_*"t^@ٳ"9PBC_|kiIXiJ!e'0Eb|< ug!6l ‡V>A1J!AXk"c|P-1>Tp%u L\pC8TtO!x'µJO-C[H8/դaS84)M!AP PC{5hlLU? ߔ*,@ BWGS{ȇFhcDj##WWG1>*M|5 @Wi`5 Z^BR Vm$9L2™BK7Odp"ؖ+>+Y,#v^-9E5­~~ Y|a KNGrΫ~RvLq"qITRtY c䊽BU?)c#\K98y%à Oi\U=% >HTW8,Oڃ [7.J''|0c]eE;p4B~bU+[ Ж!LTgt)p_'y6ڂ; Wr]1.L\'v-;?#i"U;Z>)L,ug>J/[Rbu\c=BQ $dI{8Y:߱)eZfZpkR8Y%GÅ ݾúTP+PEl|>>z #]LDY㽜](fFI Z\4Lt 2 }«&}E긳-{aT&aSHa>cW lK Y7// RZ&8XsB҅ÅXH2ug> Oؗڃ J'rV[PM wؐ2{õH +TVV@Ja ; P^hsYdpj=-c\.HTf8$ @ʥq(<=d}~hg^ y_|-)wPCXkz ?Հo{Z$wfZPygα)+S4ܧd 09L8KCڕ Ce?dy$0{~bWj nIm•;T=TTU*X 5}-u[(a3Lp%uC;/栯OdqL6#]qLqnf:syıY2{G9Oڇ H)6\[ؔ+vڀ{0kREBM?ei+hE z)mXr'a}@peƺ$02Ù\N%KWf`?8A(sWm|)-|Mqݮ+~ZR Wu)&|P[[lKW… ]CȸQ%N$\g2i އAO_Єi"|JpV:@M@o嚽6ixǖ\)cؗs\0 ǒbSH8 ).LXWqn|GϡZ(/x -}FW7pT`&Z;iaߤVPU:C~bOr<>5kIN \g87 ]Q , s.ZnĻ%%i25(Wʧµ{(aSJi .H; W> %Jǐr!s.H8 uZF,I_CG7(w4tOoVZ{lzE #m=$prZGv)〘 ̄.I}@tqc:'7mCgkƱ$C[K_oPRC[P‡~$B?d/\m\;Ubq(i:#Pg2gwJX7*ڄ~{ɰ)$T@F-ElH6Vڡq t 2Gs 8u-5>WI+TOK!,eJF9(W[(!w^'<RB%ek y%u@]CHА <>ۡ%JUF@-v Oz2 (3ť'r (M[H=;%ϳ̕C{pa9v且;y5?klICֵB_qn F,dV{W2 Sp305X6yuRd](YLtE.[K0ؐ[O2‰T,vå/I%7 -Ɖ α|SPUzMV85 #}$Ppeq&\S3-qf lZ*{2csp.YE*y{s\Pf\mQf%6Ԥ̛v 烌BMqezkrVpZơ'RRb=Ⱥ 1>s RUp2tiRFr@6@E m_nF`W*ltW o v^^@4'g||W8;U.︴}=re8b_,,5]qiG8k:'`!7~+W2_$ǻ.)`?,GCeؑ{ǒMG ܶ49~8 2Ľ x_n(c7T:;AP ?(H fE?} U:oQd9i30=KNeyoN1B3\XgK3K0Y_|#]$ז IDAT~ 8<3]pϱMis\dd[r2E0/}pgO'DT$%:#͖4EEBُY&( I_f`gIι~|ö4 d|(T䁋>LNx ;z>W3;! R` ؋vuΧI0T{ J.2/[t/ðiXYFrL)d[#5Y{Pt!x w2 ';P*<< /ѽ%TO8=HQ!m ?\E;qt[kpY%μg= 8,oa\W|,c K=V$O=T݄W2O$-@_OHWP g/[~wr8Sn喃!4GױRw6Q!g:1]p,0ƺA9H2Lz$/%˫nE8o?J`tƾ\_{R`+~%.$E溤8o)E|t'KSYkE‡~p'U:%aSGjl,Ͷ+aZS'\(s+Mv:|(7喽XCZt'Q!d} j\V'}p"?|oAYF\ Σ]%yD}}Qbn\GPvP>!x^T]ǴdG;PslCm*##<؂W/P`;p^F?m@˃ WZ_2pc :9fkpI1a=K/j6Õex?yq tc% 18Oz!(_ؘ|zWYOtb7fzY_ZA +w8 ^tO3Xxz =Ѝ:Ǖ(x n9yƳp"x#rFCrN'dp +S 2.ؓk+_Fk|O*q~X_2$qo n"y](9j垓 $ޔ&J{)MC=Pt8SD֠AFъ`j?,/:VlB ,9^Fe0"+[I/4ōuh2_@Տ OOP=ȫJ//pKltJ{1 ])1嘥$Dw4#<g o T\Ey=4IbQRc\So)2Q=P2umxo桯<) \Z{O̽Mh'OCe_.ڇo1Eui1W8uHQ')k_ #=+w+vg No7V\ǟ`_rLtO+soޣ #jīKwҌ~ E~#gxWӎ`-:4DKrϗb/2ԗ,/z>[^EpC`uJ!9 <녧! :gXvyx?yFx ߂TYjcpg؃N~2 }sq'uWx%AΗDW_BoJNRf[n/pmj.4%`s+r弑;R<#'}!1ơ{hq:ܻW:I4\ q~ځoQ{)0#xl2!^AEYycUEs!6[S ָuTV;kA(Õ3G::s7KOX]P~\LRa#=S{'BW虧!|YxK/}(q Fs*rZ~؄nKʼNߒcBꀫPG؃A(FiwnZn4ga[~2=b^.} $P/cfLL_Cׯ uJ)άkM4nlA؎x>BQ,\O2 4VJ|lPu±!F(;?C-έBӏ؀Yfi|'E^/1c<Y!`_Y{l{v:^J5YyK('TcIŝuu$x3piO/uH6Do@uKCtǯ8!zƱO* }/! tpFZuE#^;/qW(/>uN5ڸčm(,/AdNty+P=n|/׼wJ8BέAEt-%^cp3#r֏؀Tx=gc'XۨwM,tK;dyţOI^pdeCG3>@Q H=ox#YHsG %Iv:}{MƱEhpO^`'tJ9[;(khYKw=ЍF=s p$o~{WB?pa*Nߒ)P}g2a߭By|/y^?[]҈M'Jtu@XSOe39|\/&nJ7=orpc s.Iyc n| z^_yY,uK'6_8$:T} s^6k1!xU4/N@Exq'׌4=!}Ɖ%h|Dωrc~ ebz“-52cj`FtA#xųrQ-lAї(\v䚡yJrR?uI3 Sָɬ87c^,Ay <嚁(pU9Cڬ'/3&p_npRnNEu{+'%c*Rcy[|ǨcJxl8pj *DC ru~ ]q;.>n[=Duoc$!%FkRf%ЩHW.HSDdH9] wFY떇SNW>]pMp8Nvc.J%:hNb }xO.JOd"+kڅpsk!8R?8 &V$*1 5#K=X[Qyh>;?W& HX\Wڔ"COB]~Th_oD+Iw=䖹M6K'L\p~SG'^gtN3<rwQ719 }@!9~L>6`{{dh7>UZ?d{\چ|/ iwOnJiN+P5`zNc,khR{*m]j>tf2~5: -Aݷ(c+PIo֡~™ԙ?٨>sh^P)@aQF>uE'|ydϕĜYYA9eFjjX2/ )2+xmIhڝd躉G;WQڞyh9 ,Bi2TD^M mB!A!2*d"+'b4aߎ).F-J533]Ҏ~>kJ?䖙ҐΛ^)0ؤz n嚾d6{ ?@4}*8T#mA>AǕwI.WG.X 1?x!++X9;@m.h(>v6陑_t7/-zLTӐK^<M)!-9^ <BgP5f {R/?[iFc{|&}),^n|$!ڿtN+F?Ry]SI_qdx#d[oWPLXp=DXWxp@1}S>;]'43aT2!Ro!Kg!Wp2?6G*v-舟!θ>mB-'-fzNHXdz^Ci.q\EB77!3{_BJ~Կ%1X\O5 m1#PI؝qf ri\, ώP#ei2KZʚx;!^B)rkESuX*/#w^AyDml In|"mF|!ݍT (PHq[{\y^xV75,e"f{$H(l>(ᄳiGY19X ܇;]sp4zfL1s =Vijo-4RW׸w@)/G]&.\ω'8xi!.n I<8g8KW)i3 {*3ېIZ#!_R,+-h5On}PKpqnKܙpeIVnyA+h(LmA&xg_i]VzL ԤH gJQ!{ݤHƋ_a\<4R'ϸL{Eg3J{!8 Hseɳ%p阣Rssd v*s߾ьx`[]G4gdisϠgN*E2vɚ.} ƷW]dsjrIS>C 枽r7(N˗/O1f6 gv Й),nHmpŗ~&JRa>6ԒG\%Yi2b%k tv:ޓp]2E7xZeT[=3L𕓮;#7pŽ#qGVzJה,w΢v.qe t^xYr2R<ǔwNmC6[I4}~:XDI]<قw.:OJǰq3?#}6zH䚮 ;CdWOxk%IO)*V'U)U= D/'ś(ƃytŵ=aO%*Rɏh҆-)B{sHL' 1ZCv,%zO.,R޽Tܙf+Fux#?黉mY |L~3pKC9&X2ncVNhl{7.| 8KiRao`I5~9[B 3N IDATB+}~أqD@+M/xĵ}~F%X9ƶh+ōϔ,;}Yh$pJew栞ҼOT׽7ԨQo{K$kV/$YxpL#a9"nrB3S$axHW!N(}ɷT+eW׷ fCl )K=a/CUʳVNE '=?W[Qש7RU f _I.o1&*R";g*<8yO.]2&y8|5Si'.mv7l!Y:o\H!~f^6%C;qsƕOc?6V%ܦIW"kKruzӊ\s>732mm J)K8O*H8ɻk?Scj[5*# FPcKې _:dtx| wxH''J# &R~HΩw"6zL4B=N1W]&\L8gY]qjo~$f|*5FtGL>Rxbd('?{&/U]GQ쐍2 h%1`jd2Wt*W}Crϻg 9]&tJRⷋh.hE~*ixrI06paħRnf=|b!Zln>Us~cS_֓7PL۰@]$'>zN}gi7x--PV [\}b)p+4-oR2^y? 3 mU|Ty"F ܧw9!x[^J/B;'ݤH%mIxǹµ=ImC zTS>y:OI<$.ϸv@-m1vMl #T%(d8:&YZ =$Rُufbe9PxԷ {ݧ(B-dggI&gijlL(0`TPJٝK[Rw3p@!lzdQUȥFr] rA~;w}*!fWыp=SнIࢫIO_qW`&zuKgihϴaMaGIREG9Lś_&DGlk(Z.嚻DhMFn7ȇ/={d~)rmZ@Sh{EJUGSJR.=&h~OK'⒕*"p@=>zQ=*/n0v)Xxu|]w/Ȇ8=؀̀Yl"_zJz?_!xgJ3źe*r uDkxTLJOI7rM'nR R!{OɔTxJƵÔ+*_ڧ2rh{ aS~c"aO=C\.Ϭ4ӷ+#6zNt ÄM4 StkrnRfd_^5Lyڭi'w4S"`q$ O^Nq(}b ]s(bmN}vϛ0zb|0SYz dUrdZӾ>Y\1zLJ>XB@v3NMݚj%_Z@C_3-p2堇to >ӿl;5o":};& }Dsj-FCI+K8q)qreqƋ%f*=礟) PLNV >CHd'fK(D~1O7JtUa _V*Nqk2(:)P \ڼV,SK߉: ߸%<64mύi%472RLuPn{ {jе ^V6{Q:4]G1Z mW8᧓4)rY"Yi?PVi*GݥYBZ zJ>YLraKߏzO1VmpskGisRf/}Kvagijuz5K*U̯G*!kq4"sOt@&c|7` I|K9+KFmIw!h4yN>㧯9n$G35pY?\"csn=0Azo:IU/IߺM6Y|Jc(F[ )8N,5ݤ u,/;"1mF~Rk_)N]]<-@!/khlsvxq]pP!E:)RXk(%;E>J؉ÔݼK\nU*ۭy(%'l;0r1HLgXG`}ǿ@3AAAA@$AAAABA@s{]3wzv9*>衵(ЄuU$`u+uGDixN讕+(iO씓t7n2)X:ư4gtҵ+ /t#m L<$|t“SkEF@s]QE "G.[Ƭ rQzUIm)1?誥߫$=NskJAĂRS9WU+$D_L8h`ߴRlJZcv6Se8%`Yz͡22ԝ-oFE = DY S;8j9E it TZ'Eџ+n NƒtpvZ2Wg/40% \+Nzܯ5v]JSwFu̚ה#9CńMҽmH".IaBEVŵ7rvGw]V]DH{ڌMtC}I aԚ d<7y( 1oo6kMz.j^q[2-OښTz/)E( G VS7,р,Uo۔AZ ădFtc묵8y\zv<&dY7=u4 IA©u7 ̅l蚅Y6#[C ;7-n))P7S6:Os_pO FRaxի}G=p?UvL"JڭbIOӶ`U-8Ucg Ns J ꂍY6Ot e;ҊBԖ4 9j@UE[ѠoH M] qHYc[v)L\^RH e :#ݽ l4 Ќ.gmpWuۚP➢t<0kNTk / "bdF-8psA(K,؏^>3[^:{'zJ@P<5xڵ|vVmJ)1S^et젥W'&"]+0Td),4psv{pwTq AܭlƏDwzJ gAZ [4dW ?tZ1ۅ煗t>K|uqiH v\q J 0r\ǝ>p[5:DEјsC/]|kUyW4,b]WbAgujx9%'Pr{L\oh:}bD [NYzL7 eey B'*~7@Nz?j4sAQ1+fS+MWCLvLI*-9gax&i!.*M4 IhZ׎1gjF|SQSQRNǃdk@E;Pm^^Ii.c끛2[=f-UƄxhx2';z쉚ia(gYB⍌I{0<'m<}}ً[G*xHZST 끫^Gm:hIYa%4Th,{4%=}0y ɸluڎԌ̏Ve'샼-.9Om_hL7U咊B}R; ↣nAb:ө&MKg_Pȓ:v#u-rY9ּLf'rj9"v$LY^hn4;jpJ˳`]y..RSU˛R Bfn3MkMG63Wn~clwqγ1V8V7Q&nXKf.77)砵 6PST†DwʧuAòo_]5ҜDdߜ~<+M=-Ռ`ZoLKm7+^dby}*{c@˝}2[NvނOԷSU<19"sمh37TqVҊ(Nگ\S [ =tK5sJ1 R}XxgRI;TC]SU 7RLh7|'zfm0|}gYy; ƫINY7;FVS5SUhI#RfzٚM|n`5c**ص"v-BYM\/(Ezihn_rJp3T].CbghH];\ѡyUEcPBn9׬N]3w\R~UYh⾵piJa!,Pqg: \%[KzxZ{,G<5rAUI|z5G<{κ{sɣQYPa=sӿ 7FKi9n~b C؍Jej.jtzS A[M'}I(F(7@ٻEЌ9y[Kzz`N[`OmCV/';} ,V06+n΅^mWs+Pv3 At~u*-r+:ge迹Ӽ]hh[jr6N؂]/ʄqP=0 G<-sMlMW-/VEERAčNA˖aE\3?<`.:Ɓ7  "b#wǯؘonq٦q皺\O5ײQF)_7w2|2Um?ʀ;;=t3@GJ u[QᮩuzB"LKv& |:Mۥo '̌Ԛ8qqHzZNFd=ĕ&4Uw@K*87i%}ΨrRH8@Y 5ݢc:0h Vf+s,5w?G6m}9U2bXGy>x9 zO;c`̭҆8su_襇y:h56ZSݱ:~C5XQyKJ Q޳8ILR7mrĄ-Y['S ,bb/4s!{\[jz*)*xWP0~Q޽Ii ޤeX ݌ڲnzt0k-=]=uU[<'Iŭ|X@DvbIR~,9EJJͥmzt:>wҟake W7SO=\.QbŸK"Q^ IDAT: 5p]j:5UMNYBJI#9+!k%f$ Yg@ѩP4k!30c&.眅kf9g.C47fe.JKWeC9k[u,NYrnQZ)57'#0IX4[`T: 7Eۙs3bmvMÉKxr9gSf%+ޖ囓f.@v󢳅:ho 6Z{{'{~&b&*xųx/JxbzB‚#&D(QTƻ'ne$I)-Me}d90O6(%WLlU*UNojnoEk4/>]-cצ[v8]| ? 3v&L܊)SK1cf= f҈X5kf]ڲ/;v6wޑ:pp;9's|]?\ҍsw<\'O⥗;")~anU?E{wp?t0{=;Z6VrcX,0s=*3ܙvdmki: 541 n_}OQO=7cj{Zj2LM5uC X]uͪj+x*%2gEM|A^^y:*k3xJ)`RICB e\q[7S̛U;QL6!B)_DQ1_+TʿSZY%yEUTU6n3tzx }ב>DSaZixmiPtt^O:;87]K~ݹ~?\3'goX5+da;ʔ32Fة>=֥cڴ Mؠn.SFDțY!m?Hq1/D#LH"ST1G]q0R(Xw * %ݓPMu;Vu&5pɇcMLT31 ,5kmV; xukg]LU7kxP>V>W3}lO߱$lX{-V,=$ ޘSτiУ5=uh{[|z 4uj*/nǸH;' ϒIy$7Aw#$"G '#H(g%U|㺚.|[]r[=ZiuI.O~ށt1uhu\2E?A$Bag=K)@Un՜ڦZ&9N͵t6Mch3Gkc, #GO.h޿E>j'˅qno\ISQ-lkj-ur_Cm]jFt0g6i:o\\oqʹ35#wkRw+YcAfň($nEfJ{r*^,KG=Pc1[_qɃq^B<{ .\=[b&O=׵ͧSu'K t G +aazn eF{S?ն= MtN#5زic%~hNE*jZC{]x ~k7jDׯ\ Epq{}S.gAL9ɶR;r₱ӢgI("v**Mu]>EOēfVwi&#q.sM-x8 qww,}mQ&@(7]htxo6`LM$U=']LZ+2chYT4 E=Y2(-L V79';dAjS7R9 GJyPC4#w07I=>mE2.F(%˓60@{V^?t[gPTIXIubzNlmلGNP& fx ;Ǖ?!~8xUkx2*ZO pJj:pc-݋^;G_tM9٬nC.s&wkc^жEҊ WFLaG#ՙakV.BQu3Z@<9 FAɲ Ŕvjj3mAٹcY縥+֑uٳPh:9O(%ȫQk}:Wb-I(ia+2UB8X)}Z%oaYzFp=}݄fme VBa8Ejl\vDܽfno!H )M-yi?xpzJo go^C3[ʈ'DkG 9|YU/riwrrąP܅̵;3=ɹ- ~Hz snhnݫVwEUmB{~/Sef ( o!!(5-!^}}U&PUsFZ:>ӴbBa%-fMԁo:MJ>y,PR+tG<9)=DZQ;VP!ke!N keI(e+#ǠAF#h˺G=℮X$PSTN6@2]NC=oZ'_16yu |$PZ؞޹;Ԙ.B1#[RLq͞7!,j$a2q3Oͮ-ؿ{12*{Y^{[域ut6](Vq !mSEmGϴ玸9dS:MлR_ wyq&LJ!%mjhOʷPȕ(刯uA8nrݓ$/1Қ3eU dPXiG[!ā^'P Tuw#]ʩn:@\=uڞ4a .@KubH iԞ_BXz {j{h7A<9>uqիs7C2a)i{\2O(n^ kޢdI _s' ЛŦFM 2YO ?/JhquG4/pV }e%B~⊺FҵUD(}jTn~Z$.Q/גet4!T{!W xmJJ-QU=(gh&ZK{Z+.U xR8a3x"u}sF7ĝߢPݫ`^B|3TI+$mM. ]<1Pk.C?HJj:BLa{#TTMꎸqֿ(](ꉪk`VH h=BxsDU2q6LhDحNqt{T=B%=H;=?= M ŌrCCsygΗI Tw5NB,{ D*rCܬO} U RD_U}?^|ʎ\'ċТЛv:N@"bVą{R%%RhfMPw>I3sd!vݢ&|zEbova;HofMPTy7yٸ()wu ={k.UPJU| 4)"F +C817LeBlH#.OUB!/JMq ahX҅Έ';E^QhxNOę鯐 +5j q@<>׺F?Ek_r8]|wS^$e UwKz"NQ*J7K^PLE31`&%Va{oBBi;5_ۦPii(+.3m9!5}]&f д,c=NBi;8Be*L["tC-C B(3-ajf ,P.2BQL'ē  )V8x]?hqA ]ˮ\7'OE oi7iGԕ مރ4$BU ,ĂPI]fS6:qZfO#79B5;L=y"BIUZwą))._.;kTPDy=bgdPiqL+"r憸.% ٗw􀪒BOק/k>'MMF<)](8: M2B!U3wVnuPBUtom[s D. ;Puh?qֈTI}X83is2jC }4ǷTF(d>5ㄸ;=J %|DlVPXyo I*JfIwb(|DLF^}| Tט=^Pj q6-r e'B\]eýqbj ՗tl=[ybBCiҜ3fJv{NJ(wb W "Rݿ+E!Ӳ)4B M8 ŅlYOkkEVe=KS̻P\cc !SoB蚰X]C(b:ZpA\@"B)H{$*ڦn sosPZ [3Cb#\ ~&iZOāxs2/?wB)xʪe/gM"5qMB]{Ng,LFܝymą {]Ԕz3b %Utu glǜ B~ k@|3v18nbBI#ʠ e].2Ba穣wBI Tv|r-PMcRBa ⇹͆ʫݿmA]I _^ɮ.P}>T?#dI ˁh+#uG:!~x{X$$W-jœNmU1Pkx1>iqQ+D2jկ u8\цB +3 Љdhk'/G|#NpPRMvۑBByS'ĊB1:qaz}8KkVYC<\/RBI16u IDATE2vbKW9c$.Ѧb¿q@ AlAABw`0   ^` 6 sWNJ,mz$8}}i1rOȖ^+Yj\GiĆv![ΧXZ䝪Րj}{&z@ Ιb9d$k Xy1k+S7% CKb*JrdS!pD1i$Ų,kL3E1m#2;xV\N=#c/`p a3]]#g&Q#1|S**PJ#6lCj"gå:I;&#}a\91Sd뺈\omEYNk7bFKMZu"4LPh} i[ފ|q s Ţ VF-VdwȑQh)FE.LÜ\>\sEw&tpB_99 _HOo˾FNQbg5D^ a.P(f| "&v]`BƜlŦi_E͊w" ދ3Ւ A>YQwȁALT91) >D"bd-.fsD6J=="&i++]hx]Ĭy%JILیoEzTńȀ(2ьX,1'#3Ȓg MͲ%x@v|Dybxɻe1&=#+smob@> !nVŤu\ 둳%1c+,p-2b76wNBg|E+)ĦcN*(dC9"hS91HU{=Vd3lx(ބSȂ?Ļ,2ѴXA_V䝲1yN~FhXtV GuLZ+2j#%"gQt/@ߚ1ly$f·dI׼.2#b6"C&Z$`])J䅂e BNMRb>VK lblJĖS."~]ȅIt6SB|s0COȊ991~lnra &kGdͳy.4[I51i3]nŒ|"Pϑ+8p⽘|WǸЃ8/DȊE"@ל!2KW?< [^-byQe<-CX rVSN"XO,h,2aB9Bx k;zZK"6pAQRCv[o}B0{މyY!C"TsFa]`}!{zL4ev mM0y DN@| Y͹[1N2-Vb[7b'9jrDV1-{7}b*s6#cv"[;̑K\Xsd]#Gd X~ e1crMJl"\/rb/1yF1+#[۽_*bV,wDEZFbY?.3XY0+2_yHUerS~5]b)^v b7>|iy(B!OY%r 93&V]L،+7Iu\YT UCzFM渌܊e?8 `+W Ȗb!ļ/l}d>,7QQd@ULfCde\[iBG/8yJ9," b#⏷b9:EɉE!>DBvY>.Yhb%qXӑYdă)r  6pEɇikBCFYE_%Ğdó9*"܉wEv1u\XA,XշH9E@k)Ės_b2Z3Lh؈-NC32HW%3b% K#Q{.~y(|fb7#}+b&&dqRS!m=t$~ Pǀr_\&Draɋ9}/i 2!&g@U1+^lV,Ǔ"mRcĺ^O!^E x=X4XR;Ad/1/JYE ю4gEBl8E[/%1 [+,dEǴXj_"Ml3ЪqQ' r%Ȓ)W:i$#gVěf oM4[F{BGz2Z 'l f'ƝX=o*!hBl0AC 9aIE˶XYŶeC/ȌM%]K#vgQ̇~_[rb~e8]CyE4Ą̐sd ?ȁw ]n#LV,, ı"bsYAAy/H[2L'W1b6 /g";^͋pX<9#; ǀ(YWJ6g$}c)Su9d.9_!8aq_D"f}:*2*7ոV| ULx>Fvht^)u1e4KaY"H}1l/kJl@V8?r+>8CkLSD$_A;ҷ 8|(w~pFV|4e#2A2.fBBCVF2)%/V"G^͈ .l!n"$b=.X"ׁH8:ȉ5g/&K+IC\!Gȋ5Ȟ?">C|;_C({dNMGÜ9#&BLG_r dGGLe̬wkh{@\|tXdFUs\,/b2pTeźK䓶2" A܊ S*b!r[SH8AOE*Ewx@Œ䇮]ĒXf|H8݋<%="cb#&}ሌ,Ufζ"*SB| k.OZb7QD).}r#;_>9Ȇxk+aVlB6lm W",Uǭt9ȕw3#d/1Od~#n"yY䍴 YP"g^Mm>U9EaSmLZ@E^HȌXty6!>Gc;ՏCJG ɋuȊr+&| ̉ ҚX`|$&s_$@,DjA)\>YYPN1[-2" {b:Dn~#~D*%NQΐ-1cwb2$V!_>7kn#'" |+㿀#%v< b)f#^G|‌-5ƆHPC\E9)J]Ȉ;5u19,3Gdh^BV"Gb"4K{ѷH7n%5Ꮨ37+6 b1r#b`."&v.R-"N"W/_ȜXrii߹"=3bOdfY,3EvLPlFˈOVwy'-#SJb9ɣxr͔aL,U֦/r]IucbqifgW1s3/&.?{Kdm]3@N=t1n|i!"BZBmE9S 'Ů'd@V|tL)5EVĢSdǣuI߸"'~9Y,cdoK/rYLD.=L0#n"2|##"4bxs+# kdIU,9C>i9ȑ?bwDGrDfG>EYYLb>r+&CH=73rq#" 92^dĶd̝Xw,eȖw+&gNH[XBVS˦c}SQk 85"*r%P@DZ7s+#-vAFņ[dNY@6;ȁ" gÌf묑  o;bwb'H7n"R!""Swqp&y'% ɉM%Y  {Z}̳ 3-v8 #b 2,V##`[C.F#^ER]sO$xt}6@$B< Er#|q7o ~MD uTjHZT*˥HXJCq[=SGD~G{Vqxi E:Q)q{g$"/ ķ+A +NR|H]_OH  Cb2"'>GŦ;d RnE 2$\# b%YQ.55 ![b)ICshyȞG19HGGN~ gbrYL\y!*+Ẅ́y33%v9!'Hߌx+9 Cb=22'>C&{ZlEkdnIB*KdiE@VV2sdmM,1C6Lu!1Fv6<#dclYȷm>r#f|]1;rYL\}[QC^W곘7 b1e],2CV KdaIz/6!#boZrFMI{ș?bCK9Fv4Ă3dCM,DŚdFAlCĖ?Ȁ3#)(@|?tӈovqi#N7"%RRE\DuįH7a rAsAD|C<)?!rQ;A>EȚX_dAI|pLY^wU%;qBz#p%ʕ#m1"[Ē dEEFź_Ȅr+v+BD~#V"v,k#ExCޑb9 >D!Gł dAA|@}. ."߈w1A)Y Ɗ IDAT sۄMs'A IoGED$Ev<3dLd%P b'PEސM xtļsddkr]LGv摦XrLs6Ya-M~QI:@vĚ;d@R|icgb[c͋.Do kJb#D|jH!Y#c3F.cSQ8@Ѕ 6` H v 3d0AAA  A}zyv{hw.9y NV"&ML1@!}n1,ҾG\D볘e, EL9D> wi7# r%X 3sWгNEBnEOz*9k!Jgq X So㳞yKn) R">}ۊ7~cdMQlyAY\$\mČn'b-2$)"5}OL2@&G2Ny!g"gbK,BƦ/4ĊȀ= CdcI|u,;<Sl9(X##S_b%R#iFڢQ:C"{D9lMxk`+Ri _ cdeAls9GDX$m!-{jDnDMӋxYz/vwȊݳHߐb//bQ")ZLy xr+D7C;rXcGgEElxD҈Hxys̐9C箴łC&dףH${ޚ=!HIȖf'KB$#3r\hEȘ#Ҩ ~E@֒q1)6O]1 >es\9I9FV܉'*!nb(;\##Hx~,؁X f"h]'1Y"ˈߟb! viE7Ȑߊ"^;ToOUlxB^+:3N9{iVw=1Yy|%"R  D2wTĦgC\LD2oX`[gȜriTb14}Q,õHh[,F&"_G/sq9I#{b#AYȌ[G^C<&\Xq|$G+BKldKIlyEW#TY.ι@dķϫx5tDCGz)R (tpl(mE:P$|[94wLH{Z"f!3o =Rk4d!F6 _%!k b*."n">LCfc)(V#T$,-G q2ɾ"b3V7 8Z?bc\!#`-RxB68 dBJ܊GŽ3dFF|߉T"^bm|ǔdAV|=':1YG+Y"#q gf*f#S/Jb**# Sdf`=+)"n?cda6痚<#b!2;Q<"/9H$G6 D$C-mDZbK#Ng"7oފȅЉ[D/Dr/ȏ5)2s8\?#'~ ɹD$FA.6*{o#SbKB%"d/s+EMKG, H:Fė:Yc E"kH"X c1vKQ'򷂸o!SӆV(-E%f̽5@UlzA d,wA{ByP2DfWS4kg&"vLI}*>r+x@^sdOEQ$R#;b dLB.Err㕦Xt7b)25_|Ý5->*&aq<yG-F<źg䩈.0G&V׽H\m5~ ʻsSөHxq=R^vq*XƩ%# n/R9[ .2HrE}*OWcrH3r&6PveC1Yxkَ94OObmD1+ r"y"/yu_L1Af zR" K[,jȺDF "7dg \@ 3d?ݯɋ/_D*Aq2CO< /H{,#dIV|ۉņ#AGq6I/UQV"Y:tɈH 9Glp&yA\D"#dn:b&'}+ uiUOb]$0 .b 2֒[A"'jUxn{q̐cdNZDroȷ/}7bc$y#nz j)2%)"7=dc>B${E6:HW_[@~s"iL$GVf#x_AF\#Q5OS q"ޱL޺DID$9Df"lNF\4ĺ]Ay|Es~q>؊)t#G"@L1E&&"T 2Z$,F,T":%V!o9aM8AfR$l((CѦSdyGܾYҔ3dBBE2ڜ;H\ .W="EݫgMy2J N"-w^ANGDgSFZZ$ƒH\MDbCf\"^V kN+dTy.!{E,E[q& ,'fY?A\ ״ dBI2N Nb 2qLIHr';'%"0$#"7[dZ$l%/mE_o jQ_v".hH|)?!? ):R;Gj9Y5$LMlE"DU@!6F^Gv3Ү"b#A(SI,s ]Ȏ Ђ;UcKw-Cܼ9ք`d]#7O%!T i@#$l$&œpL@LD#Αaq)fYF^ go|gxmDKp&'!/ްs#bC i/MH]|<ѲAF˗1"#"2qaκA/b}H#ҩ":W<8_Jbw'RxE :^s+"!23i$gC1".5!n_;J"OSK%BҸ"g;){ m>¿!+~.E2d? MAs2"n;"3Esk +h"k'*RyoӖ[)f$XL2]"gKz=-!t*'5"r!WgbG-lE^|_d?iC$DL0%X>dW=1dž ;wK$V8V#Z7BMlE*O!;ķHz %"${e\#'9y>!{JdTL0Su%x<#;iq4ʳn"O[5b:ȷ8)K < 5kւ8i7!2 +5s)"oIӡaE{乂xnz*)!k3q73guGTE4T;2&}1&/ҩ!^q'bK$C"$S̱H<"DDnėHYAd'"ت iՑ5~)l08zF\w"xF6Umķ8CX`OhL l"g+W"! q*9Y4"'ˆFȋHzDa"n_E-h)EN\ƄƧ\"9XHW*F~,$%+" Dig‰"r`[q0 b>sp=!;sAyv"doސ qs|\S{C$@̲%i XHxÆ`Ҳg9ZI㑘aMw򷉜,E7dXL"ti5(-p"XTiבȊdA\#'gb9!yS њ{\$&<""?ן'obSh{/GR2OevdX̰Ǩ*R{FfdG7O+e-TstR͊887"wd!M~?gbedD\evv"i7KZ|q+F (UE6 fwCH.וHjJmn '/K'%XG#t%y/"ȋbEM R ;)O҈XJHVfDs Z1gCZ ;g+CyCE}!~oJ$uĻ?ѷX=G"[l(o}c7"nl12O"qWBL]QLDw/8ENqvnE2;H5hnC<<t 'Gb]$#rߏ<Ցg)ͳAJ\L0W"Wd)y+#^qk],q&y#S4:ȷH ӌݓGC1Ǟ`ǃH~@P;@GiSLQkB0j6O?䵂h q&DkԌжglMۿG<6 Zq+bC #QYH7DbA ~ ~N4_"5RE =[|+\ }Y$ TxEF;~ ahD#s_`.:e|kG܉aKhX$RrtkŒAE"R_"7Ǚ"ŏ8[H#}/xeqGsNQ;[,>YY[|[䩁3XfG< ڐ\)+Ɏ#CY?䥆-OnqK\_buM(r?4;֔AeD)m'^8w z^$U)͸C~\X4t b}%FK^k9y#GqO#NyKJDLL%ؿyɍԟ ^Ţ ep[q)9 &z8e= ȷaxDIS‘8y~ w#RZ#]iN95dpxcҚ|1BQ7:nDn& Br q48{\#os1&#4jVA UۆȊXDhRw%YE*$|)}<"nxIH/3y"gj<{~KR\v 9ǟbkeo"f9Ļ>4ͽ͔"~: yx`F-T|r2o֠xDg}3b!!~ɊXF$~ݥdz܉dfqճHaO{v"r/\\mx-Oo p :p+N#Wb$9Y,[[_"9h0JE2dc n1kG RT$SԭN8{3BA ;NQ(s1WdI--|8G"wo= 'd-yn o 앑ECl9Im۠iqqNgBfb杍HrIbG$7&8HGhy_La Df |  򈧚w/RЛyq\>z7 ? 8V$X>~4w"EHf/T?~DHH(D«'`Y~Ha]qEEJ[+R RdT{+%jAbݰx-ElFr~G yqy%#.#_$M`U q_7eEN}&H?/FG RRTO~LZGfNdK$tVb]$e&f9F{#5zaiș8}5@Z\>Զqh2xc4x.=,"W U4iJt4(zDjyEJBgPHfAq+8?=@J\[ ʑ~9k~jG"^k,{Drsd; 1˙ВkoDAvHzKG` 5otS݉I~#Y9>#͕Z>Fb(G4;Hߵ{3"U$DvIc1)BR<ŕf gZS >4-"G16 q)Ɋ!F)OHqJĐ#IqW5/HvE0{ `P3ȥAr ;[1͉`Y I 15QZ$xV\7 Z?,f ݱAC$}k"7kB{iǝ.!{g7D^2Zw!qa4Eg O1>k%3\pW^3)Iwu# M HqFp rAfaDžA^gRbQ)^`[Ekoq {?qyl>x7R_YDn͟H[Q$'n4DSE u1a֞E$C)%%9l9~4 rdA"#/Rƽ A҈\4BGptsq/bJ1oTƬm xErwyl*::"XID@<"W6 WtuCFN"!g&eBWœЉ~ps譡>As!<'lsS^<<دʔl?/ G'QvGtR!E ?4V^ɈۚmBۊ?"̢1~oſS#?<V݊7dJO/UǙx(kqӿ S>.яL"46RQEPM$}|G"‚ ϷܳR\@ [{ q"j\U$74YꑴI$y?F{1)G}vF|l=w ~X_HA8">۞F)K6.aa{E7t2x#' 9AjwgE !WEnx =>DZrgԞY+ь~۲&X\#656_IAWKCN$ğ!{bf!i#_D3 W;rhҌ A O 2!9iHvCФQ"!ZӶ!wG~c8e;t/<ǯ[8).qV/3[䐓'$f܉__=]IB1|ΔTo.``6A0A@$A A0ALI AAAĂ f Z-o}w}w×?}خB˭(ǘ; "<(Pm4 K!D\-l$;?R:KچZPzq73pu_tm;a\ oS_? )ծIx[㊫-it=gf< 7Ll]Ym\;܁)J2~C }|=|qM4s]wad"$zMj ⟾ dl2vDcTn& ;j ?F$vӒGhBn-)!g/tP|Ak~RJd=0"D;NY,;kW79r~B Ȑr'o.jRM\uܰH{ʺ3iĿ pq%776jMjK3kч{XBb\ުC .M)]nbyA^(V }\4d؁=ΐ@w Jv$n]<ށ9By'#F]s3j~EEx3xq)h'Ep6IZc6rh=(GjKFV)w sʪ' D @ֆYR[$!=G'Ovwsi~sgy? 6os K7r7vP[ ׯ2(gw)\ ?n[š9lXݪC-Me ž] "y Ylb"=HIB~&nYDNpvcOS wcRX'eQfVvF:@f&5sgr!}NS 0r{u6TH=3@ߘEkC+CK0 Іx<r!>$E]Bvkv6,B.]B ]f)5gf٨Lyrg4'9hk6 3Z Cvwy*Ӑv2>݁%De=nZ$K8*GC {GJghh:%h?kbв pqvHLjm&iR3s`Zp^kf 4[\]|W, +G71oxq!^@׾~]Sn%SUnh7\;,Y)EVrCp"Gp^~z,+A,2LuT Ve`^ 7wVIikD ńGSr LM*8{qKC_4hCvkv(i*F9hsdfkldc{7rH'HG/h SvΠ5)rm]2Jp Ԛ6Y@- ckH RhtԡuXS[vTt3{5_' ;2&>S >9,$A`զQAyHIZ] hw>, .]?4tr 0!8 ?DLÏ~ݻfщ<]^o6 }v9IxɵmN!S_(2mL3 ~WY irA~j/05)ű )ՖFǨJ]y҇ݯPҩ\^p> K~z4wHUlvrke_Z@ -f\6:[\R9 U!-V{zb|+}2}BmHtT J#C?.B/)M niaBiRp~jrJnFy`vn/2В,(q/ӐKl6MjEskj5nJa_Ϯqȹ=A 5> /wM9PkVKHB'j!rZww dpҙ`;W߆`pQ33ejv;_]9(r_~6уMqN~b&,fs1:-H^Rϋ3?)41,Љn]N24Ӭe8}/ >(YoRFnBkX1.+]ALyv oGN*``잡Mx$j,:Erza b? ! ),pEEJ6q 5eiھOJv6ze8LR{4u rq|vӚR"ҀZ Ȥ#e@ ZArK~4{ePI= 3Ye}z2 ;MJUԭx^u%2?li PE9:,):v&޻-P[1acz!O>Y* &FY5| .ج,MI-?M0m7i8p3;m~PS2kHXqVݺv͕.MuQ"6r^0ՎUﻮӌ]Ds.JA04ݖ/ٕ{4c9Fhõn5[=Ñq$v"-J^'YW(s?ݑR6 =?GL7jNkrA{\CAmZi/NjR2yIImqZW- O v H1^gGL?Zfn_M`dYϋhJ3w{Q!?Ͼ\~h]jqbGH0&eCЮ#<ئܙRSk]y.{! 7} ;St\q=q~0)^ 4;׆`xˢ4.S[8qCn͍ܮN8Z EtcM_cFY0 R[So\Y7qU!KvIv D bJkԆ6Z>G!qR3{/RNm] SKkޢ]ynD ,6F9 Ǒ|8+A۴Y|ZQ R=RP hVw9m=@T !WZooh/.þˎ`rNAF蕨EJ7>W9H6w+cȼ R{؅W먎 'ϷSmX=ʸfՖkP;aʻ-iίL ߻ToW e|,^ٟVF|X7#?*oIL8.JbK0=1p%X8Genhn}續kw ƫs<a))wby y: 6Yޤ ^\@='$fqn v>B)BqCi}\+RZL2^Rv4 {sb'gO09$ц$8I[RJh fƔ+ 8 +i'I:Y@ئ!Û1P 9S=RXG٢hݴAv^Vm[(ѵ2џ*Ž7?R9?t;&.?FdGԚo5x6P!a#ۮzqAM(h9f?m̚8w{8^ӆ`n`a6{(nɀ>EŽ`z56λ*; |x4 ^wͲ_~4!ڞ,x4 _iɅ+z#s0l8qɅSN)u-׹-pwFmR5CxԽ&GՉE bjL?/~X[r߰$* Oo)ev*x4֨EM0с<ASsn 5MHe{leu%?-~5h ˆTY7=<Ĭ ^I1s`vH>T  bYcZ{ s'Pl!Ujw~49n"x4] jy6~4R9}d 5vG F8/?h6*ɛN\.z`+![ kO e{gBI@ IDAT1rV.vf')|LM}|w;#2ji ۬3hIo{_U/Z=A %nGs[.@"Є wS> Q'0qmWt=XqD)/ :v) 9vA fq {␴Hz/&#76qKaæQ=A rВhQ &n KK?`tiUu? ͭ#Rd.0qzle~Zc@О@2mV('_mUC;+XA VEh~%@\b6zkS,>(ݷЪ@LOG}ƅi q~v-k1qR +tΎ`jVBD01qkS{D{ze︄K]Y\GA,_-aZEA\0qLBrlT,X,7K0i6*`jDi2>{sdI~|@*G H5!.MļI, bS0%{'mZo%nuj$}ƚ`cbr[w930K'Ъ8;雭];AALkY?XA;]֝z8%lzՍzKo-63s=ۡvc;:s.`taӵn7xE׵G6A jbQ8 bbㄈC {l|Ɯ`A336 ]/M0=6Ay gԾi!jdm;a G Ĭ mI;u v~;{Yey AK.(s@,}Pb UC3iO u~( M?`VHiöݱfAn֜=x:p+K5LOUoL 0b|B:LJ7ż'>%Y{Cv[<%=JR=#^#.Ϯ\l<m\0qHC;S0> 3'P@lW?CtmaJF@^ݥqBXTL4Aj2GnĤ)uQS F-iR}2 pIvnO`hM=,#K{9\ys>qƧ"3ĘZ'%CJʥM0ӷ _ {Z9/B{MԢ6\^,7=5ʶq{J^9 ☂RH-񓁴}qmjPĴ>u/ Fg`e) f?]A[V}%>/ bU6U1X;~7'Ņ`b`_z_Ŗ;&%O!!#bV7ѷ}N.4Lyg%Jkl!WȳӘڒ1j[nc[cKت*mܖ`m~ǔ`^>B1A^\Y09G ݷs~NJ`n:c9i:/ ĦdL@[V r}{^=|];4%֔1#X/0_ߡ=XTElZ!C#ycMA:N\Ah Z?;4 BB; Ʀf*\Hsj DC{/ IJmyA2 (?6`rH-߱!XjZFVoU2ʳG 7 c]Ր'E1-scvKՊS>)4~=S*Jգ 7)FƱ kZ)gQ18sƯMoCnS<:LQ}9eZKp1cAېC`xaUV=bLLjSHك,Z եgO2(q✂ fbt۷e-<{6g⒄Ĭ &> :`“z uhmكhdlrI^`'G{mUќz3nlʓlx UH?=ij'Q3MكXUe0{gO0էRƓl.[ Dg(g=F0{WhUσك={!Μ'7'Xf[o~NyAԖ/"%"@4hGp"ľ`"zaDk [&>Jrn$* r :cEr$8qʸʀjvgO3 .)wi`S }`S!; K + > J$Fu,bܱk`lL97w1mY r,֌` j# wXi+ %fuSވjY܁XW܇5d Jv,W)_wG{Q_:0+Bki93  M{Ccj@^@'`mňc8Dwd@f63qNCF>kA`ge$'1A&v,C ~uu#0{J; &mhZu~J"=5#3jwڜ`#Ģz z@@U7݊.D&X ruՄmzԈgJK ̑`#ĮdVƲurTL}:8q̙YXt1kyG0>t)_(&^@fxK߁X<:9$K4=70}̻kN B İ'8/]0Fd! k. ֋Ĵm7$.iȧ㨊Wќ +IJn lj= : {!D|m xˊ`':%@ *`cD#MU@T|FA͉t3 ✱G&2RDc!mV*IĢ}ѱ5ϒODg"8N-{bSq;\5iy& ?CԾ| Eh/T*9'|Rb ]h]dZ) -iDv./A1oBFL=à S`~CӪ!I<Ǽ\AUR. 6^T'p X@,|{qPWa䏢OpEu ̩G MXSA,v L,SQTؔ@|؜`++ z#`OTUB - &?m(/Y704>QyvF@|[E AnLu]bS"ú'ٵ æ;ihw"e1k鎻!el F&niڇuNreDهMvSlDg ",U,._צ6Y@p>]qɸc|R?eD{&=nVKrlDR̶"6UhZfI✅6\Qa#њKkE3bԓ!OsMĺ-o06H8Uڭ(!eV @ ݑ`aiBi/`)kvSG"uw&#LljL|USy O](+;=)1bjv`thʼn˰G3q()&c؀ቑJCS"9 t\4q:%x7w3`P $&YQp  %8 :Q:$8 AnG*ҏ={4 8 N)nZښ02rUS@,@̻BORе3 kMdHp7Tt`#dAL{-^QR}?j>#jaぴ!(NC43#L+.44Ñ"J0qbĶfMq D"z; u tFi(3BE?)ϸ RQǁ&V-w$XzIB,-YĢ# jlDFĤ-~.X{+@>Դ|!au,A &jHĤ/%Xʶ/ jN%t~&h(K 6%E<`J80[ݢ cW-[[Jћ΁(zIX792AS{\ Dз+CIJ| :AA@Azz`3c g]쩣 F#dAץ=sLtC AD-;~*`$AWb`@prL_ MH(WdAԷ +F L,;{V&*(4i:w }kMT* %Epb ~aT"O`.A|U1aX|!X(h):ٷ V^4rA-ȁT(8[-x= Oud W  ]~-`ܜ5#7Įf@47*{e~ -M"^@TjbM'/FIrR p17g-t)@@*`?)]xsn :KqHpǸ9źq#X pRnq#X pQqc@O]>iWWVQ:Ɗ+Z,P E]Gl7jV~QIJ4$F P1)~D!_Mѳ' A8+fW DKǞ&1 S`bm փZ rsA4v[9ENׄ0LnK[`"\r!/`>!j XUUG#KW`!!jJ=qDYe#$ F6!b0BUgP9}0qa#$~M|#$X_@TU@|0B* ~L-1kX?U[0Begn.U@;=B`&,x ^UUO#qVALALFBp|ӡ? S Ip|*yߟ`/ wI3T? vO`'? Oyꫥ%zV.OS5I|*}ݟ?OS+$X >OS}:ۋDN_J$X >핢? O.7YBSw|Q>e#|hE *fOu]dkrzD}/_ծT jx2ʛMU$|h}-)ިm"|謥t >AtWWWʌ >'%)}?-VG$џKO}9|̤ȁ >A PR>A&Z#Y.1c)11脁b :ͱj3 r:%VzPSv*+;䩱>3cWAlChb.|E-?u}KYeOi.p,z 5%O Z[WǙTrV.9 )/vL4}Yxjgk`.`TB9SGޝJY ^DN+ z5 NT /DuJK}KhU 7)BtGմjo[`Qkg6qJ'R R QmJ>ө'O6?\ O5%H%өT7z'gb1{NE R5Q]%H.f@T] 6ϱj< 6Z^. 6FUQ}aTWn۱.Wwo+TM*O u2F:A,][śiO!Q;PmDo9Xj 2_P#luO,B2"EoRQCmH DMTʰ4a=!cT&JmmG YDAFEo_IA/jMCЯEdS5sKUFTƪdi5qb |6kr.A![kdE65 @hڋ5?hA)iږAkedw7rJ åxW&oi]SbY2գU`*f5O9MMm11tєDk+\A3)Q7E-Fʱ3%.ėCSN $=*+,k}?Y6Ǧ o"y- ][ŋV-b՟}VOK2-UE:4=oy$[4-ꞷ O5톥Z}[T=ozۖxz0oUO4f jg;-ѼMhͼA4<yhz7w ڞhͼAt<yz7񥉾i˾?D 7'@|3oN/fGyn%DRPGh*dYM35˥_zvQ#kEBA.B$p[X;]WeԛggSPRz@S*lER7": O=΄S]/ Dw'Ps3<uyx1) 'NۻT^DDйh0l0 ;Ys>oyx>wnm`!wn78ɒ7?mO*@< p?1>VGtxՈW_utbZ7M&rHS7˱kʳB[M)yxU0ӹp(ƣ5SS| "(;߫6])zaJNXUtÍ&Sy`)B Dee9U-^5a1M~pW*%5uxUC%*Oޜ[nK9!R9\_687P1Kl|YU諫lpnBPT qj >7#ۗ9 z iܲhs[iǺhvǾ͹mņϴsHU־lpTn>KAmǿQ/Z}o_7M7Tކ5e$8es襤4+_Fd[_687.ġu{Sn-K9ޜ{Sn M*RLN5>۷g@fHu [Rug`<k1Usġj5jTxl_5&+~3n?~Ԯ'` _4KSE FZv} _I]t)O$Wތ #'W; ALJY_#M31] |ia=#p3 5dCrFu7"ZkSf- rdnFPVmML܌ +=KV4F#2Z?j̏GninFpYCxᅪyxFf79i%F&s=8:_qn̶[8?W-7b=,tN罋x-b_]F>zvy!{+Q w{myY6E=:^+;y Yff3jѭϫ(O0Lu=Xzk>XY%]uiy \Hڏϫ^be(y7 ~yf,n| C?h|^MfK癡% Їϻa&p3n 5  ZZ><|輀kZ9Mb0T>p; N\W=sVu3UvH/?|j{IDW=7Xןk`櫸DyLՠ"w nr/ Im *F&,|ӵܚwk0ETX_|)ҪW 1#j`p_UάH雯jj&r* L|)Sy-OWZj&ns_c8jXDlGRIO& W5f?˲|UKrJE냏_-CO*OW !M`U>ueC7_sG!w ߹K[2m,m-ےZd߯ \:w=F45Ld 'ԍ:#?vX8ջA85xj+*tQpuO?87Jn ^VyA~=$3^ug?7Jwrmʏgfw5Yߏ7 ڏQRO&`2.bO(">7*ڳ/?7RF{&F~oњyGFQ^5#n~#.keGuwmޏ$ղS?7"ZZVl>7rHn*?¿sI퀿%3#n~NeS퀣q)uvubGFL+c?[hHB77X ZéK ̤tɭ0y >`K3}WmӋO F{%D.)KkSQVZL%MDomMF 5nZ6qduT}_5m*R{m?DK&"`nnU1O`ssBmr"|%<Chɹ$8mqp-ڃMXJvރM\pzh MW+*J~ssVڳm**Ʌss;Cj}y07y~Q'mW;MV^_ 7`MM}/oSoߚ6m)< hVa-{WZWK7&` EGn2ioI.+/`/P`k>;D4>k>~~Ss0,f`SԈNK[;h''>QQ%{!>߳beS&qȃ9%"DG^^Jqz[`"+N;E-7Q8򡳋"~ Z`m}vR}$}lGmn6Ige7joH7j%&j{/I7Q$OtItt_Fm|/IFm6˿$9}C-vC'B!76)Еj@ts_Lg|Wv {#Ṽ;aS53عo{JFI(;_Ǣ]+{JU g{SDKDO~/rsJ\k5k)i6"Rƛk;[):"ϥ8)!)FW_/RQ`>J={JJƯ|)/j߫.H?DC._5)Q*"Ύ`몺/)9yy{DE=MZj.qEnNSQ k{{DG+ݖ#fȿ[woEt٧]i۹HL'Ի y}t647a u~&:cD8qJ:0iRRM4qSYYׄ A/m{'[` X0ՀH0a\H)RH .\؅ n\p.\H…4.\[C< %"- '䱻0As={{5R<)D>g ڶ6iLv:4kaa&ȊFoO5 d whߺEֵ &OɔδV viɨ*Y eUi{vz A&gڶȼ a&ee+IՑeb fD4:-3Q_3kw\A\&sH4Cp- W NS)H$̹LV3x211f2 [d b29@V䦦Q12 [ }8THh7pzH s*eZߗނpV6oSᛶ٢pFEfޔVnIB2o7 BsO'/,+[{mRE"\֒7I[wv8|~[>̛f"| 7۪F+†*~M6ySo-^7nMcڢim *q 7UgdM߄Nct^#QUiýBVk`5M^YaT{'m??"K:77Du>1d Ȃκ)Qc#wp CRm~? :5uCCHgyY5{CU)Y v,:²ARϵrȆUpe8,`+H),yr`b*e9ղokǁ)*u²h ׆SnHQeRqDT&Vmkd]9( :h/4,N:òh, :]YiY::ceqOiVՙ8+~(-`Y: 1ueq< aZa_#*񂰨ל!KHqLP 6R~ R ;dVlG TK>!%aYUu,Vx%ܼVFKCUsZ5BCT:wVCd^; ն~CYN? :z RUK$  dY%H?f̤tRe;+|DVUeE:/΃iEB vgdKWH)TcV5ڂH#g[1al@ ̖yOAmLĐ0Zp1`cHu  R҃HuN/ NT kSX+HCUw!/, Vtl| ) 8,fnm)mläB?#4McP^!Ob،҆2-q̠ oM1h17A{u^8h"yaNU @sd#"123h. GȪt"1VuIpA[a :閅SdYTu:!Hx8nE,ivRt GȢTH1fОX>" :ARt d^q\Љu!ygxg&M-2t"A?[dF1h ٘A'+>2t O#A]Bg1hDR¶~Aގpn BA?:FL0gԾ##3JѸ,#բrl٦a U:DREXRD6l7\?^)o90*/UێkNbWg\ ֢pD6ud]D21(‡KdMG[կ Ȋ{A)e[8*unD'aW=n WȂvSg O®>Af v!+ȨbŔ+!#†v/;0-"Ȕ `n_@1:g7mx9کH>]pkM#vKGd]k'wYxr]G\9/6^)RQ"XVUGȊ՗1;[Fx."iᙰ={ٶ0SktlؿaNuI,PC$%$IN%j{T *) Wn%cKR/NJ-PQDT%/lI*IT r@x_>\!;ȴ2Xfך!LDU+¼' ˪TP }BH޺-lL.>")AUXo%d̹8haVx.li:;'L &L<&,n}d:Wy 4 /m-cd^4 'C:{EI& HYj~.Ҵ*0M& 0Cxzջ?A/V{;PvtBY 8ww*Kſ]Prԭy 2^J3Bе=/!w҇_υ =(iN<=wJ^1JtPNR^QJVSzTJ ^JOI,+ֈE@ B?BE| CD! P#_X X[':A!@=B'vE?"_q!d!M&IDAT~. >#o y~H^wKq ^)"BE]GBPg}"y,s8}Eݯ@Gnꢞwb qi{\Yτp] Wc 0yƗ\!tӤaDw,V6R|'rrf Mn h#^1PiRN[٦gy:吠l+vW0P.i @K.:]^j:)_gtT}EzJhL׈R@T DK..e TXReb$GBJWwb?EtwN"Է_[ \ K^Zi1"ssyAK[ƺ1C +Q ׉G,{@5t S'pl$T8ExVf縨%(Hb\Tlϩ@z\TIIDQ]ZJiF\TvҚN@T]FX.Mi-C3Ry$8kv̷x*KO..[Ps74EHb.U Dqd_ 3쫺Np;֡Ko=K*A>Dn/st OZz|Rf_DϰK.r?r KoC67YNbJ"wP 2 ;4~vylwȉF~ܽ^;47t#7Dn|ȉgAwd_ߡN&tOGkҴCiQhJ$% F_)P0DM^"c3/>#êZ;42tZCig]"J>1J#?P|$`g\ZlGs-hFғO::f9]t#04-kII s4VtGdڂ}G!T+C[j)|T&q/D:Iu0G j#}G!j#o= ؁ϡJ%z`s[@0伅{m_(hvN\BRI=21|c75 Ai0?)mI]Aa  Aat +Yt.0Q^*KaIIʮP)]|Mhy$ _LF Yuo =R.:ẗ܅oLِw?ӽp}e^.iE`/a }x_CE GB2-Yg-Yw%rۀOu#z_Ll /^{_©|y(1akvChz44h(Np鵯{?oͮ^zz|7($}$d @}wwϨ+MDd:_/ઇa Xep濾#!_b}-~!FL/eb>F7Pkȷk@uN n }G 51ڏ_Ky ="B.IENDB`gammaray-2.11.3/tests/manual/messagemodeltest.cpp000066400000000000000000000053461413310443000220650ustar00rootroot00000000000000/* messagemodeltest.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2012-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "messagemodeltest.h" #include #include #include #include using namespace GammaRay; MessageGenerator::MessageGenerator() { auto *layout = new QVBoxLayout; QPushButton *button = new QPushButton(QStringLiteral("debug")); connect(button, &QAbstractButton::clicked, this, &MessageGenerator::generateDebug); layout->addWidget(button); button = new QPushButton(QStringLiteral("warning")); connect(button, &QAbstractButton::clicked, this, &MessageGenerator::generateWarning); layout->addWidget(button); button = new QPushButton(QStringLiteral("critical")); connect(button, &QAbstractButton::clicked, this, &MessageGenerator::generateCritical); layout->addWidget(button); button = new QPushButton(QStringLiteral("fatal")); connect(button, &QAbstractButton::clicked, this, &MessageGenerator::generateFatal); layout->addWidget(button); setLayout(layout); } void MessageGenerator::generateDebug() { qDebug() << "A debug message"; } void MessageGenerator::generateWarning() { qWarning() << "A warning message" << "split into two parts"; } void MessageGenerator::generateCritical() { qCritical() << "A critical message"; } /*some Windows compilers don't like Q_NORETURN*/ /*error C2381: 'GammaRay::MessageGenerator::generateFatal': redefinition; '__declspec(noreturn)' or '[[noreturn]]' differs*/ void MessageGenerator::generateFatal() { qFatal("A fatal message"); } int main(int argc, char **argv) { QApplication app(argc, argv); MessageGenerator generator; generator.show(); return app.exec(); } gammaray-2.11.3/tests/manual/messagemodeltest.h000066400000000000000000000031041413310443000215200ustar00rootroot00000000000000/* messagemodeltest.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2012-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_MESSAGEMODELTEST_H #define GAMMARAY_MESSAGEMODELTEST_H #include #ifndef Q_NORETURN #define Q_NORETURN #endif namespace GammaRay { class MessageGenerator : public QWidget { Q_OBJECT public: MessageGenerator(); private slots: void generateDebug(); void generateWarning(); void generateCritical(); void generateFatal(); //FIXME: Q_RETURN fails on some configurations }; } #endif // GAMMARAY_MESSAGEMODELTEST_H gammaray-2.11.3/tests/manual/modelstest.cpp000066400000000000000000000036601413310443000207000ustar00rootroot00000000000000/* modelstest.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2012-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: David Faure Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include #include #include #include int main(int argc, char **argv) { QApplication app(argc, argv); QStandardItemModel firstModel(3, 3); for (int row = 0; row < 4; ++row) { for (int column = 0; column < 4; ++column) { QStandardItem *item = new QStandardItem(QString("row %0, column %1").arg(row).arg( column)); firstModel.setItem(row, column, item); } } QTableView firstView; firstView.setModel(&firstModel); firstView.show(); QStringListModel stringListModel(QStringList() << "Item1" << "Item2"); QTableView secondView; secondView.setModel(&stringListModel); secondView.show(); return app.exec(); } gammaray-2.11.3/tests/manual/objectreparenttest.cpp000066400000000000000000000041561413310443000224250ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include #include class MyObject : public QObject { Q_OBJECT public: explicit MyObject(QObject *parent = nullptr) : QObject(parent) , c(new QObject(this)) , p1(new QObject(this)) , p2(new QObject(this)) { c->setObjectName(QStringLiteral("MovingSubtree")); auto t = new QTimer(this); t->start(10000); connect(t, &QTimer::timeout, this, &MyObject::reparent); auto gc = new QObject(c); new QObject(gc); c->setParent(p1); } public slots: void reparent() { if (c->parent() == p1) c->setParent(p2); else if (c->parent() == p2) c->setParent(nullptr); else c->setParent(p1); } private: QObject *c, *p1, *p2; }; int main(int argc, char **argv) { QCoreApplication app(argc, argv); MyObject obj; obj.setObjectName(QStringLiteral("MyObject")); return app.exec(); } #include "objectreparenttest.moc" gammaray-2.11.3/tests/manual/picking/000077500000000000000000000000001413310443000174305ustar00rootroot00000000000000gammaray-2.11.3/tests/manual/picking/invisibleoverlay.qml000066400000000000000000000026521413310443000235360ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Christoph Sterz Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ import QtQuick 2.0 Item { width: 100 height: 100 Rectangle{ id: invisibleoverlay color:"#ffffffff" anchors.fill: parent visible: false } Rectangle { id: redrect color: "#ffff0000" anchors.centerIn: parent z: -40 width: parent.width/2 height: parent.height/2 } } gammaray-2.11.3/tests/manual/picking/loader.qml000066400000000000000000000023521413310443000214130ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Christoph Sterz Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ import QtQuick 2.0 Item { width: 100 height: 100 Loader { id: loader source: "stackedrects.qml" active: true } } gammaray-2.11.3/tests/manual/picking/negativezordering.qml000066400000000000000000000034501413310443000236730ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Christoph Sterz Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ import QtQuick 2.0 Item { width: 100 height: 100 Rectangle { id: redrect color: "#ffff0000" anchors.centerIn: parent z: -40 width: parent.width/2 height: parent.height/2 } Rectangle { id: greenrect color: "#ff00ff00" anchors.centerIn: parent z: -4 width: parent.width/2 height: parent.height/2 } Rectangle { id:bluerect x: 40 y: 40 color: "#ff0000ff" anchors.centerIn: parent z: -29 width: parent.width/2 height: parent.height/2 } Rectangle { id: yellowrect x: 70 y: 70 z: -21 color: "#ffffff00" anchors.centerIn: parent width: parent.width/2 height: parent.height/2 } } gammaray-2.11.3/tests/manual/picking/opacityzerooverlay.qml000066400000000000000000000031171413310443000241170ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Christoph Sterz Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ import QtQuick 2.0 import QtQuick 2.0 Item { width: 100 height: 100 Rectangle{ id: invisibleoverlay color:"#ffffffff" anchors.fill: parent opacity: 0 Rectangle { id: visibleChildOfInvisibleParent color: "#ffff0000" anchors.fill: parent opacity: 1 } } Rectangle { id: yellowrect color: "#ffffff00" anchors.centerIn: parent z: -40 width: parent.width/2 height: parent.height/2 } } gammaray-2.11.3/tests/manual/picking/outsideofparent.qml000066400000000000000000000026131413310443000233600ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Christoph Sterz Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ import QtQuick 2.0 Item { width: 100 height: 100 Rectangle { id: blackrectparent color: "#ff000000" width: 10 height: 10 Rectangle { id: redrectchild color: "#ffff0000" //out of parent x: 5 y: 5 width: 70 height: 70 } } } gammaray-2.11.3/tests/manual/picking/stackedrects.qml000066400000000000000000000032611413310443000226240ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Christoph Sterz Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ import QtQuick 2.0 Item { width: 100 height: 100 Rectangle { id: redrect color: "#ffff0000" x: 20 y: 20 width: parent.width/2 height: parent.height/2 } Rectangle { id: greenrect color: "#ff00ff00" x: 30 y: 30 width: parent.width/2 height: parent.height/2 } Rectangle { id:bluerect x: 40 y: 40 color: "#ff0000ff" width: parent.width/2 height: parent.height/2 } Rectangle { id: yellowrect x: 70 y: 70 color: "#ffffff00" width: parent.width/2 height: parent.height/2 } } gammaray-2.11.3/tests/manual/propertytest.cpp000066400000000000000000000030031413310443000212700ustar00rootroot00000000000000/* propertytest.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include #include "../shared/propertytestobject.h" int main(int argc, char **argv) { QCoreApplication app(argc, argv); PropertyTestObject obj; QTimer timer; QObject::connect(&timer, &QTimer::timeout, obj.changingPropertyObject(), &ChangingPropertyObject::changeProperties); timer.start(5000); return app.exec(); } gammaray-2.11.3/tests/manual/quickitemcreatedestroytest.qml000066400000000000000000000031351413310443000242120ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ import QtQuick 2.0 Rectangle { color: "lightsteelblue" width: 240 height: 320 ListView { id: listView anchors { fill: parent; margins: 20 } objectName: "bububu" model: 255 focus: true delegate: Rectangle { color: Qt.rgba(model.index/ListView.view.count, (ListView.view.count-model.index)/ListView.view.count, 0.5, 1.0) implicitHeight: 20 implicitWidth: listView.width Text { text: model.index anchors.fill: parent } } } } gammaray-2.11.3/tests/manual/quickwidgettest.cpp000066400000000000000000000043321413310443000217320ustar00rootroot00000000000000/* quickwidgettest.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include #include #include int main(int argc, char **argv) { QApplication app(argc, argv); QWidget top; top.setWindowTitle(QStringLiteral("Nested Quick Widget")); auto topLayout = new QVBoxLayout; top.setLayout(topLayout); auto topLabel = new QLabel(QStringLiteral("Label outside QQ2 widget")); topLayout->addWidget(topLabel); auto qqw = new QQuickWidget; qqw->setSource(QUrl(QStringLiteral("qrc:/reparenttest.qml"))); topLayout->addWidget(qqw); auto subLayout = new QVBoxLayout; qqw->setLayout(subLayout); auto subLabel = new QLabel(QStringLiteral("Label over QQ2 widget")); subLayout->addWidget(subLabel); top.show(); QQuickWidget top2; top2.setWindowTitle(QStringLiteral("Top Quick Widget")); top2.setSource(QUrl(QStringLiteral("qrc:/reparenttest.qml"))); subLayout = new QVBoxLayout; top2.setLayout(subLayout); subLabel = new QLabel(QStringLiteral("Label over QQ2 top level widget")); subLayout->addWidget(subLabel); top2.show(); return app.exec(); } gammaray-2.11.3/tests/manual/quickwidgettest.qrc000066400000000000000000000001271413310443000217330ustar00rootroot00000000000000 reparenttest.qml gammaray-2.11.3/tests/manual/reparenttest.qml000066400000000000000000000034461413310443000212460ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ import QtQuick 2.0 Rectangle { color: "lightsteelblue" width: 320 height: width/2 Rectangle { id: left anchors { left: parent.left; top: parent.top; margins: 20 } color: "yellow" width: 120 height: 120 Rectangle { color: "red" anchors.centerIn: parent width: 40 height: 40 focus: true Keys.onLeftPressed: { parent = left } Keys.onRightPressed: { parent = right } Rectangle { color: "blue" anchors.centerIn: parent anchors.margins: 5 } } } Rectangle { id: right anchors { right: parent.right; top: parent.top; margins: 20 } color: "green" width: 120 height: 120 } } gammaray-2.11.3/tests/manual/rotationinvariant.qml000066400000000000000000000043631413310443000223000ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Christoph Sterz Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ import QtQuick 2.0 Item { id: root width: 100 height: 100 property alias animated: timer.running property alias interval: timer.interval Rectangle { color: "#ffff0000" anchors.top: parent.top anchors.left: parent.left width: parent.width/2 height: parent.height/2 } Rectangle { color: "#ff00ff00" anchors.top: parent.top anchors.right: parent.right width: parent.width/2 height: parent.height/2 } Rectangle { color: "#ff0000ff" anchors.bottom: parent.bottom anchors.left: parent.left width: parent.width/2 height: parent.height/2 } Rectangle { color: "#ffffff00" anchors.bottom: parent.bottom anchors.right: parent.right width: parent.width/2 height: parent.height/2 } Timer { id: timer running: false repeat: true triggeredOnStart: true onTriggered: { var d = root.rotation; d += 20; if (d >= 360) d = 0; root.rotation = d; } } } gammaray-2.11.3/tests/manual/shadereffect.qml000066400000000000000000000046471413310443000211550ustar00rootroot00000000000000/* shadereffect.qml This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ import QtQuick 2.0 import QtQuick 2.0 Image { id: root source: "lsd.png" Text { anchors.centerIn: parent text: "Drugs are bad, mkay?" } ShaderEffectSource { id: effectSource anchors.fill: parent sourceItem: root recursive: true live: false } ShaderEffect { anchors.fill: parent opacity: 0.5 rotation: 45 scale: 2 property variant src: effectSource property real customUniform: 0.5 vertexShader: " /* TESTVERTEXSHADER */ uniform highp mat4 qt_Matrix; attribute highp vec4 qt_Vertex; attribute highp vec2 qt_MultiTexCoord0; varying highp vec2 coord; void main() { coord = qt_MultiTexCoord0; gl_Position = qt_Matrix * qt_Vertex; }" fragmentShader: " /* TESTFRAGMENTSHADER */ varying highp vec2 coord; uniform sampler2D src; uniform lowp float qt_Opacity; uniform lowp float customUniform; void main() { lowp vec4 tex = texture2D(src, coord); gl_FragColor = vec4(vec3(dot(tex.rgb, vec3(0.344, customUniform, 0.156))), tex.a) * qt_Opacity; }" } } gammaray-2.11.3/tests/manual/signalmonitortest.cpp000066400000000000000000000037741413310443000223100ustar00rootroot00000000000000/* signalmonitortest.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Mathias Hasselmann Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "signalmonitortest.h" #include #include SignalMonitorTest::SignalMonitorTest(QObject *parent) : QObject(parent) , m_timerCount(0) { auto *t1 = new QTimer(this); t1->setObjectName(nextTimerName()); t1->start(250); auto *t2 = new QTimer(this); t2->setObjectName(nextTimerName()); connect(t2, &QTimer::timeout, this, &SignalMonitorTest::onTimeout); t2->start(1500); } QString SignalMonitorTest::nextTimerName() { return QStringLiteral("SignalMonitorTest_t%1").arg(++m_timerCount); } void SignalMonitorTest::onTimeout() { auto *tx = new QTimer(this); tx->setObjectName(nextTimerName()); connect(tx, &QTimer::timeout, tx, &QObject::deleteLater); tx->start(2500); } int main(int argc, char **argv) { QCoreApplication app(argc, argv); new SignalMonitorTest(&app); return app.exec(); } gammaray-2.11.3/tests/manual/signalmonitortest.h000066400000000000000000000027641413310443000217530ustar00rootroot00000000000000/* signalmonitortest.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Mathias Hasselmann Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_SIGNALMONITORTEST_H #define GAMMARAY_SIGNALMONITORTEST_H #include class SignalMonitorTest : public QObject { Q_OBJECT public: explicit SignalMonitorTest(QObject *parent = nullptr); private: QString nextTimerName(); private slots: void onTimeout(); private: int m_timerCount; }; #endif // GAMMARAY_SIGNALMONITORTEST_H gammaray-2.11.3/tests/manual/staticinjectiontest.cpp000066400000000000000000000025551413310443000226110ustar00rootroot00000000000000/* staticinjectiontest.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include #include GAMMARAY_STATIC_INJECT int main(int argc, char** argv) { QApplication app(argc, argv); QLabel label("Hello World"); label.show(); return app.exec(); } gammaray-2.11.3/tests/manual/textures.qml000066400000000000000000000023741413310443000204100ustar00rootroot00000000000000/* textures.qml This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ import QtQuick 2.0 Image { source: "lsd.png" Text { anchors.centerIn: parent text: "Drugs are bad, mkay?" } } gammaray-2.11.3/tests/manual/translation.qm000066400000000000000000000002451413310443000207020ustar00rootroot00000000000000 Widget One En Two Två Three Tre gammaray-2.11.3/tests/manual/translator_test.cpp000066400000000000000000000064211413310443000217430ustar00rootroot00000000000000/* main.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Jan Dalheimer Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include #include #include #include #include #include #include class Widget : public QWidget { Q_OBJECT public: explicit Widget(QWidget *parent = nullptr) : QWidget(parent) { m_label1 = new QLabel(this); m_label2 = new QLabel(this); m_label3 = new QLabel(this); m_button = new QPushButton(this); m_layout = new QVBoxLayout; m_layout->addWidget(m_label1); m_layout->addWidget(m_label2); m_layout->addWidget(m_label3); m_layout->addWidget(m_button); setLayout(m_layout); connect(m_button, &QAbstractButton::clicked, this, &Widget::openFonts); retranslate(); qApp->installEventFilter(this); } void retranslate() { m_label1->setText(tr("One")); m_label2->setText(tr("Two")); m_label3->setText(tr("Three")); m_button->setText(tr("&Fonts")); } private slots: void openFonts() { QFontDialog dialog(this); dialog.exec(); } protected: bool eventFilter(QObject *object, QEvent *event) override { if (event->type() == QEvent::LanguageChange) retranslate(); return QWidget::eventFilter(object, event); } private: QLabel *m_label1; QLabel *m_label2; QLabel *m_label3; QPushButton *m_button; QVBoxLayout *m_layout; }; void loadTranslation(const QString &filename, const QString &name) { Q_ASSERT(QDir().exists(filename)); auto *translator = new QTranslator; translator->setObjectName(name); translator->load(filename); qApp->installTranslator(translator); } int main(int argc, char *argv[]) { QApplication app(argc, argv); loadTranslation(app.applicationDirPath() + "/translation.qm", QStringLiteral("App translator")); loadTranslation(QLibraryInfo::location(QLibraryInfo::TranslationsPath) +QStringLiteral("/qt_sv.qm"), QStringLiteral("Qt translator")); Widget widget; widget.show(); return app.exec(); } #include "translator_test.moc" gammaray-2.11.3/tests/manual/wk1application.cpp000066400000000000000000000025101413310443000214340ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include int main(int argc, char **argv) { QApplication app(argc, argv); QWebView view; view.setUrl(QUrl("https://www.kdab.com/gammaray")); view.show(); return app.exec(); } gammaray-2.11.3/tests/manual/wk2application.cpp000066400000000000000000000025371413310443000214460ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include int main(int argc, char **argv) { QGuiApplication app(argc, argv); QQuickView view; view.setSource(QUrl(QStringLiteral("qrc:/wk2application.qml"))); view.show(); return app.exec(); } gammaray-2.11.3/tests/manual/wk2application.qml000066400000000000000000000023671413310443000214560ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ import QtQuick 2.0 import QtWebKit 3.0 import QtWebKit.experimental 1.0 WebView { width: 800 height: 600 url: "https://www.kdab.com/gammaray" } gammaray-2.11.3/tests/manual/wk2application.qrc000066400000000000000000000001301413310443000214340ustar00rootroot00000000000000 wk2application.qml gammaray-2.11.3/tests/metaobjecttest.cpp000066400000000000000000000066531413310443000202620ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include #include #include #include #include Q_DECLARE_METATYPE(QThread::Priority) using namespace GammaRay; class MetaObjectTest : public QObject { Q_OBJECT private slots: void testMetaObject() { QVERIFY(MetaObjectRepository::instance()->hasMetaObject(QStringLiteral("QThread"))); auto *mo = MetaObjectRepository::instance()->metaObject(QStringLiteral("QThread")); QVERIFY(mo); QCOMPARE(mo->className(), QStringLiteral("QThread")); QVERIFY(mo->inherits(QStringLiteral("QObject"))); auto *superMo = mo->superClass(0); QVERIFY(superMo); QCOMPARE(superMo->className(), QStringLiteral("QObject")); QVERIFY(!mo->superClass(1)); QVERIFY(!superMo->superClass(0)); } void testMemberProperty() { auto *mo = MetaObjectRepository::instance()->metaObject(QStringLiteral("QThread")); QVERIFY(mo->propertyCount() >= 7); // depends on Qt version MetaProperty *prop = nullptr; for (int i = 0; i < mo->propertyCount(); ++i) { prop = mo->propertyAt(i); QVERIFY(prop); if (strcmp(prop->name(), "priority") == 0) break; } QVERIFY(prop); QCOMPARE(prop->name(), "priority"); QCOMPARE(prop->typeName(), "QThread::Priority"); QThread t; QCOMPARE(prop->value(&t).value(), t.priority()); QCOMPARE(prop->isReadOnly(), false); } void testStaticProperty() { auto *mo = MetaObjectRepository::instance()->metaObject(QStringLiteral("QCoreApplication")); QVERIFY(mo); QVERIFY(mo->propertyCount() >= 8); // depends on Qt version MetaProperty *prop = nullptr; for (int i = 0; i < mo->propertyCount(); ++i) { prop = mo->propertyAt(i); QVERIFY(prop); if (strcmp(prop->name(), "libraryPaths") == 0) break; } QVERIFY(prop); QCOMPARE(prop->name(), "libraryPaths"); QCOMPARE(prop->typeName(), "QStringList"); QCOMPARE(prop->isReadOnly(), true); QCOMPARE(prop->value(nullptr).toStringList(), QCoreApplication::libraryPaths()); } }; QTEST_MAIN(MetaObjectTest) #include "metaobjecttest.moc" gammaray-2.11.3/tests/metaobjecttreemodeltest.cpp000066400000000000000000000074351413310443000221620ustar00rootroot00000000000000/* metaobjecttreemodeltest.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "baseprobetest.h" #include "testhelpers.h" #include #include #include #include <3rdparty/qt/modeltest.h> #include using namespace GammaRay; using namespace TestHelpers; class MetaObjectTreeModelTest : public BaseProbeTest { Q_OBJECT signals: void destroyed(); // to trigger the QMO validator private slots: void modelTest() { createProbe(); auto srcModel = ObjectBroker::model("com.kdab.GammaRay.MetaObjectBrowserTreeModel"); QVERIFY(srcModel); MetaObjectTreeClientProxyModel model; model.setSourceModel(srcModel); ModelTest modelTest(&model); Probe::instance()->discoverObject(this); QTest::qWait(150); // wait for compressed change signals } void testModelContent() { createProbe(); auto srcModel = ObjectBroker::model("com.kdab.GammaRay.MetaObjectBrowserTreeModel"); QVERIFY(srcModel); MetaObjectTreeClientProxyModel model; model.setSourceModel(srcModel); Probe::instance()->discoverObject(this); const auto l = searchFixedIndexes(&model, QLatin1String("MetaObjectTreeModelTest"), Qt::MatchRecursive); QCOMPARE(l.size(), 1); auto idx = l.at(0); QVERIFY(idx.isValid()); QVERIFY(!idx.data(Qt::DecorationRole).isNull()); QVERIFY(!idx.data(Qt::ToolTipRole).toString().isEmpty()); idx = idx.sibling(idx.row(), 1); QCOMPARE(idx.data().toInt(), 1); QVERIFY(!idx.data(Qt::BackgroundRole).isNull()); QVERIFY(!idx.data(Qt::ToolTipRole).toString().isEmpty()); idx = idx.sibling(idx.row(), 2); QCOMPARE(idx.data().toInt(), 1); QVERIFY(!idx.data(Qt::BackgroundRole).isNull()); QVERIFY(!idx.data(Qt::ToolTipRole).toString().isEmpty()); idx = idx.parent(); // BaseProbeTest QVERIFY(idx.isValid()); idx = idx.parent(); // QObject QVERIFY(idx.isValid()); QCOMPARE(idx.data(Qt::DisplayRole).toString(), QLatin1String("QObject")); QVERIFY(idx.data(Qt::DecorationRole).isNull()); QVERIFY(idx.data(Qt::ToolTipRole).toString().isEmpty()); idx = idx.sibling(idx.row(), 1); QVERIFY(idx.data().toInt() >= 0); idx = idx.sibling(idx.row(), 2); QVERIFY(idx.data().toInt() > 1); QVERIFY(idx.data(Qt::BackgroundRole).isNull()); QVERIFY(idx.data(Qt::ToolTipRole).toString().isEmpty()); QVERIFY(!idx.parent().isValid()); } }; QTEST_MAIN(MetaObjectTreeModelTest) #include "metaobjecttreemodeltest.moc" gammaray-2.11.3/tests/metatypemodeltest.cpp000066400000000000000000000051361413310443000210110ustar00rootroot00000000000000/* metatypemodeltest.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "testhelpers.h" #include #include #include #include #include #include <3rdparty/qt/modeltest.h> #include #include #include #include #include using namespace GammaRay; using namespace TestHelpers; class MetaTypeModelTest : public QObject { Q_OBJECT private slots: void testModel() { MetaTypesModel srcModel; MetaTypesClientModel model; model.setSourceModel(&srcModel); ModelTest modelTest(&model); QVERIFY(model.rowCount() > 0); QSignalSpy resetSpy(&model, SIGNAL(modelReset())); QVERIFY(resetSpy.isValid()); auto idx = searchFixedIndex(&model, "QObject*"); QVERIFY(idx.isValid()); QVERIFY(!idx.data(MetaTypeRoles::MetaObjectIdRole).value().isNull()); idx = searchFixedIndex(&model, "int"); QVERIFY(idx.isValid()); QVERIFY(idx.data(MetaTypeRoles::MetaObjectIdRole).isNull()); idx = searchFixedIndex(&model, "QThread*"); QVERIFY(!idx.isValid()); qRegisterMetaType(); srcModel.scanMetaTypes(); idx = searchFixedIndex(&model, "QThread*"); QVERIFY(idx.isValid()); QCOMPARE(resetSpy.size(), 0); } }; QTEST_MAIN(MetaTypeModelTest) #include "metatypemodeltest.moc" gammaray-2.11.3/tests/methodmodeltest.cpp000066400000000000000000000073441413310443000204440ustar00rootroot00000000000000/* methodmodeltest.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "baseprobetest.h" #include "testhelpers.h" #include #include #include #include <3rdparty/qt/modeltest.h> #include #ifndef Q_MOC_RUN #define MY_TAG #endif using namespace GammaRay; using namespace TestHelpers; class MethodModelTest : public BaseProbeTest { Q_OBJECT public slots: MY_TAG void taggedSlot() {} Q_REVISION(1407) void revisionedSlot() {} private slots: void modelTest() { createProbe(); ObjectMethodModel srcModel; ClientMethodModel model; model.setSourceModel(&srcModel); ModelTest modelTest(&model); srcModel.setMetaObject(&QObject::staticMetaObject); srcModel.setMetaObject(&staticMetaObject); srcModel.setMetaObject(nullptr); } void testModel() { ObjectMethodModel srcModel; ClientMethodModel model; model.setSourceModel(&srcModel); srcModel.setMetaObject(&staticMetaObject); QVERIFY(model.rowCount() > 0); auto idx = searchContainsIndex(&model, "deleteLater()"); QVERIFY(idx.isValid()); QVERIFY(idx.data(ObjectMethodModelRole::MethodSignature).toString().startsWith(QLatin1String("deleteLater"))); QCOMPARE(idx.sibling(idx.row(), 1).data().toString(), QLatin1String("Slot")); QVERIFY(idx.sibling(idx.row(), 1).data(ObjectMethodModelRole::MethodSignature).isNull()); QCOMPARE(idx.sibling(idx.row(), 2).data().toString(), QLatin1String("Public")); QCOMPARE(idx.sibling(idx.row(), 3).data().toString(), QLatin1String("QObject")); srcModel.setMetaObject(nullptr); QCOMPARE(model.rowCount(), 0); } void testToolTip_data() { QTest::addColumn("name", nullptr); QTest::addColumn("toolTip", nullptr); QTest::newRow("tagged") << "taggedSlot" << "MY_TAG"; QTest::newRow("revision") << "revisionedSlot" << "1407"; } void testToolTip() { QFETCH(QString, name); QFETCH(QString, toolTip); ObjectMethodModel srcModel; ClientMethodModel model; model.setSourceModel(&srcModel); srcModel.setMetaObject(&staticMetaObject); auto idx = searchContainsIndex(&model, name); QVERIFY(idx.isValid()); QCOMPARE(model.columnCount(), 4); for (int i = 0; i < model.columnCount(); ++i) { QVERIFY(idx.sibling(idx.row(), i).data(Qt::ToolTipRole).toString().contains(toolTip)); } } }; QTEST_MAIN(MethodModelTest) #include "methodmodeltest.moc" gammaray-2.11.3/tests/modelinspectortest.cpp000066400000000000000000000334321413310443000211670ustar00rootroot00000000000000/* modelinspectortest.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "baseprobetest.h" #include "testhelpers.h" #include #include #include #include #include #include #include <3rdparty/qt/modeltest.h> #include #include #include #include #include #include using namespace GammaRay; using namespace TestHelpers; class ModelInspectorTest : public BaseProbeTest { Q_OBJECT private slots: void testModelModel() { createProbe(); auto targetModel = new QStandardItemModel; targetModel->setObjectName("targetModel"); QTest::qWait(1); // trigger model inspector plugin loading auto modelModel = ObjectBroker::model("com.kdab.GammaRay.ModelModel"); QVERIFY(modelModel); ModelTest modelModelTester(modelModel); QVERIFY(modelModel->rowCount() >= 1); // can contain the QEmptyModel instance too int topRowCount = modelModel->rowCount(); auto targetModelIdx = searchFixedIndex(modelModel, QLatin1String("targetModel"), Qt::MatchRecursive); QVERIFY(targetModelIdx.isValid()); QCOMPARE(targetModelIdx.data(ObjectModel::ObjectIdRole).value(), ObjectId(targetModel)); QCOMPARE(targetModelIdx.sibling(targetModelIdx.row(), 1).data().toString(), QLatin1String("QStandardItemModel")); QVERIFY(!modelModel->hasChildren(targetModelIdx)); // proxy with source auto targetProxy = new QSortFilterProxyModel; targetProxy->setSourceModel(targetModel); QTest::qWait(1); targetModelIdx = searchFixedIndex(modelModel, QLatin1String("targetModel"), Qt::MatchRecursive); // re-lookup, due to model reset QVERIFY(targetModelIdx.isValid()); QVERIFY(modelModel->hasChildren(targetModelIdx)); QCOMPARE(modelModel->rowCount(), topRowCount); delete targetProxy; QTest::qWait(1); targetModelIdx = searchFixedIndex(modelModel, QLatin1String("targetModel"), Qt::MatchRecursive); // re-lookup, due to model reset QVERIFY(targetModelIdx.isValid()); QVERIFY(!modelModel->hasChildren(targetModelIdx)); QCOMPARE(modelModel->rowCount(), topRowCount); // proxy with source added/reset later targetProxy = new QSortFilterProxyModel; targetProxy->setObjectName("targetProxy"); QTest::qWait(1); QCOMPARE(modelModel->rowCount(), topRowCount + 1); auto proxyIdx = searchFixedIndex(modelModel, "targetProxy", Qt::MatchRecursive); QVERIFY(proxyIdx.isValid()); QVERIFY(!proxyIdx.parent().isValid()); targetProxy->setSourceModel(targetModel); QCOMPARE(modelModel->rowCount(), topRowCount); proxyIdx = searchFixedIndex(modelModel, "targetProxy", Qt::MatchRecursive); QVERIFY(proxyIdx.isValid()); QVERIFY(proxyIdx.parent().isValid()); targetProxy->setSourceModel(nullptr); QCOMPARE(modelModel->rowCount(), topRowCount + 1); proxyIdx = searchFixedIndex(modelModel, "targetProxy", Qt::MatchRecursive); QVERIFY(proxyIdx.isValid()); QVERIFY(!proxyIdx.parent().isValid()); delete targetProxy; QTest::qWait(1); QCOMPARE(modelModel->rowCount(), topRowCount); // 2 element proxy chain targetProxy = new QSortFilterProxyModel; targetProxy->setObjectName("targetProxy"); QTest::qWait(1); auto targetProxy2 = new QSortFilterProxyModel(targetProxy); targetProxy2->setObjectName("targetProxy2"); QTest::qWait(1); targetProxy2->setSourceModel(targetProxy); targetProxy->setSourceModel(targetModel); QCOMPARE(modelModel->rowCount(), topRowCount); proxyIdx = searchFixedIndex(modelModel, "targetProxy2", Qt::MatchRecursive); QVERIFY(proxyIdx.isValid()); auto idx = proxyIdx.parent(); QVERIFY(idx.isValid()); QCOMPARE(modelModel->rowCount(idx), 1); QCOMPARE(idx.data().toString(), QLatin1String("targetProxy")); idx = idx.parent(); QVERIFY(idx.isValid()); QVERIFY(!idx.parent().isValid()); QCOMPARE(modelModel->rowCount(idx), 1); QCOMPARE(idx.data().toString(), QLatin1String("targetModel")); delete targetProxy; QTest::qWait(1); QCOMPARE(modelModel->rowCount(), topRowCount); // QAIM removal delete targetModel; QTest::qWait(1); targetModelIdx = searchFixedIndex(modelModel, QLatin1String("targetModel"), Qt::MatchRecursive); QVERIFY(!targetModelIdx.isValid()); QCOMPARE(modelModel->rowCount(), topRowCount - 1); } void testSelectionModels() { createProbe(); auto targetModel = new QStringListModel; targetModel->setObjectName("targetModel"); targetModel->setStringList(QStringList() << "item1" << "item2" << "item3"); QTest::qWait(1); // trigger model inspector plugin loading auto modelModel = ObjectBroker::model("com.kdab.GammaRay.ModelModel"); QVERIFY(modelModel); auto selectionModels = ObjectBroker::model("com.kdab.GammaRay.SelectionModels"); QVERIFY(selectionModels); ModelTest selModelTester(selectionModels); QCOMPARE(selectionModels->rowCount(), 0); QSignalSpy resetSpy(selectionModels, SIGNAL(modelReset())); QVERIFY(resetSpy.isValid()); auto targetSelModel = new QItemSelectionModel(targetModel); targetSelModel->setObjectName("targetSelModel"); QTest::qWait(1); QCOMPARE(selectionModels->rowCount(), 0); auto modelSelModel = ObjectBroker::selectionModel(modelModel); QVERIFY(modelSelModel); auto idx = searchFixedIndex(modelModel, "targetModel", Qt::MatchRecursive); QVERIFY(idx.isValid()); modelSelModel->select(idx, QItemSelectionModel::ClearAndSelect); QCOMPARE(selectionModels->rowCount(), 1); QSignalSpy dataChangeSpy(selectionModels, SIGNAL(dataChanged(QModelIndex,QModelIndex))); QVERIFY(dataChangeSpy.isValid()); QCOMPARE(selectionModels->index(0, 1).data().toInt(), 0); QCOMPARE(selectionModels->index(0, 2).data().toInt(), 0); QCOMPARE(selectionModels->index(0, 3).data().toInt(), 0); targetSelModel->select(targetModel->index(1, 0), QItemSelectionModel::ClearAndSelect); QCOMPARE(dataChangeSpy.size(), 1); QCOMPARE(selectionModels->index(0, 1).data().toInt(), 1); QCOMPARE(selectionModels->index(0, 2).data().toInt(), 1); // rows QCOMPARE(selectionModels->index(0, 3).data().toInt(), 0); // cols targetSelModel->clear(); QCOMPARE(dataChangeSpy.size(), 2); delete targetSelModel; QTest::qWait(1); QCOMPARE(selectionModels->rowCount(), 0); delete targetModel; QTest::qWait(1); QVERIFY(resetSpy.isEmpty()); } void testModelContent() { createProbe(); auto targetModel = new QStandardItemModel; targetModel->setObjectName("targetModel"); QTest::qWait(1); // trigger model inspector plugin loading auto modelModel = ObjectBroker::model("com.kdab.GammaRay.ModelModel"); QVERIFY(modelModel); auto contentModel = ObjectBroker::model("com.kdab.GammaRay.ModelContent"); QVERIFY(contentModel); ModelTest contentModelTester(contentModel); QCOMPARE(contentModel->rowCount(), 0); auto cellModel = ObjectBroker::model("com.kdab.GammaRay.ModelCellModel"); QVERIFY(cellModel); ModelTest cellModelTester(cellModel); QCOMPARE(cellModel->rowCount(), 0); QSignalSpy cellContentResetSpy(cellModel, SIGNAL(modelReset())); QVERIFY(cellContentResetSpy.isValid()); auto targetModelIdx = searchFixedIndex(modelModel, QLatin1String("targetModel"), Qt::MatchRecursive); QVERIFY(targetModelIdx.isValid()); auto modelSelModel = ObjectBroker::selectionModel(modelModel); QVERIFY(modelSelModel); modelSelModel->select(targetModelIdx, QItemSelectionModel::ClearAndSelect); QCOMPARE(contentModel->rowCount(), 0); auto item = new QStandardItem("item0,0"); item->setFlags(Qt::NoItemFlags); // should nevertheless be selectable for inspection targetModel->appendRow(item); QCOMPARE(contentModel->rowCount(), 1); QCOMPARE(contentModel->columnCount(), 1); auto idx = contentModel->index(0, 0); QVERIFY(idx.isValid()); QCOMPARE(idx.data().toString(), QLatin1String("item0,0")); QVERIFY(idx.flags() & Qt::ItemIsEnabled); QVERIFY(idx.flags() & Qt::ItemIsSelectable); QCOMPARE(idx.data(ModelContentProxyModel::DisabledRole).toBool(), true); auto cellSelModel = ObjectBroker::selectionModel(contentModel); QVERIFY(cellSelModel); qDebug() << "selecting" << cellSelModel->model(); cellSelModel->select(idx, QItemSelectionModel::ClearAndSelect); QVERIFY(cellModel->rowCount() > 0); idx = searchFixedIndex(cellModel, QLatin1String("Qt::DisplayRole"), Qt::MatchRecursive); QVERIFY(idx.isValid()); QCOMPARE(idx.sibling(idx.row(), 1).data().toString(), QLatin1String("item0,0")); auto iface = ObjectBroker::object(); QVERIFY(iface); auto cellData = iface->currentCellData(); QCOMPARE(cellData.row, 0); QCOMPARE(cellData.column, 0); QCOMPARE(cellData.flags, Qt::NoItemFlags); cellSelModel->clear(); QCOMPARE(cellModel->rowCount(), 0); QVERIFY(cellContentResetSpy.isEmpty()); delete targetModel; QTest::qWait(1); } void testSelectionModelSelection() { createProbe(); auto targetModel = new QStringListModel; targetModel->setObjectName("targetModel"); targetModel->setStringList(QStringList() << "item1" << "item2" << "item3"); QTest::qWait(1); // trigger model inspector plugin loading auto modelModel = ObjectBroker::model("com.kdab.GammaRay.ModelModel"); QVERIFY(modelModel); auto targetSelModel = new QItemSelectionModel(targetModel); targetSelModel->setObjectName("targetSelModel"); QTest::qWait(1); auto selectionModels = ObjectBroker::model("com.kdab.GammaRay.SelectionModels"); QVERIFY(selectionModels); auto modelSelModel = ObjectBroker::selectionModel(modelModel); QVERIFY(modelSelModel); auto idx = searchFixedIndex(modelModel, "targetModel", Qt::MatchRecursive); QVERIFY(idx.isValid()); modelSelModel->select(idx, QItemSelectionModel::ClearAndSelect); auto selSelModel = ObjectBroker::selectionModel(selectionModels); QVERIFY(selSelModel); idx = searchFixedIndex(selectionModels, "targetSelModel", Qt::MatchRecursive); QVERIFY(idx.isValid()); selSelModel->select(idx, QItemSelectionModel::ClearAndSelect); auto contentModel = ObjectBroker::model("com.kdab.GammaRay.ModelContent"); QVERIFY(contentModel); QCOMPARE(contentModel->rowCount(), targetModel->rowCount()); for (int i = 0; i < targetModel->rowCount(); ++i) QVERIFY(contentModel->index(i, 0).data(ModelContentProxyModel::SelectedRole).isNull()); QSignalSpy contentSpy(contentModel, SIGNAL(dataChanged(QModelIndex,QModelIndex))); QVERIFY(contentSpy.isValid()); targetSelModel->select(contentModel->index(1, 0), QItemSelectionModel::ClearAndSelect); QVERIFY(contentModel->index(0, 0).data(ModelContentProxyModel::SelectedRole).isNull()); QVERIFY(contentModel->index(1, 0).data(ModelContentProxyModel::SelectedRole).toBool()); QVERIFY(contentModel->index(2, 0).data(ModelContentProxyModel::SelectedRole).isNull()); QVERIFY(!contentSpy.isEmpty()); delete targetSelModel; delete targetModel; } void testWidget() { createProbe(); std::unique_ptr targetModel(new QStringListModel); targetModel->setObjectName("targetModel"); targetModel->setStringList(QStringList() << "item1" << "item2" << "item3"); QTest::qWait(1); // trigger model inspector plugin loading ClientToolManager mgr; mgr.requestAvailableTools(); auto widget = mgr.widgetForId("gammaray_modelinspector"); QVERIFY(widget); widget->show(); const auto views = widget->findChildren(); for (auto view : views) { QVERIFY(view->model()); } } }; QTEST_MAIN(ModelInspectorTest) #include "modelinspectortest.moc" gammaray-2.11.3/tests/multisignalmappertest.cpp000066400000000000000000000062571413310443000217020ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include "core/multisignalmapper.h" #include #include #include #include #include using namespace GammaRay; class Emitter : public QObject { Q_OBJECT signals: void signal1(int); void signal2(const QString &); friend class MultiSignalMapperTest; }; Q_DECLARE_METATYPE(QVector) class MultiSignalMapperTest : public QObject { Q_OBJECT public: explicit MultiSignalMapperTest(QObject *parent = nullptr) : QObject(parent) { qRegisterMetaType >(); } private: QMetaMethod method(QObject *obj, const char *name) { return obj->metaObject()->method(obj->metaObject()->indexOfSignal(name)); } private slots: void testMapper() { Emitter emitter1, emitter2; MultiSignalMapper mapper; mapper.connectToSignal(&emitter1, method(&emitter1, "signal1(int)")); mapper.connectToSignal(&emitter2, method(&emitter1, "signal1(int)")); mapper.connectToSignal(&emitter1, method(&emitter1, "signal2(QString)")); mapper.connectToSignal(&emitter2, method(&emitter1, "signal2(QString)")); QSignalSpy spy(&mapper, SIGNAL(signalEmitted(QObject*,int,QVector))); QVERIFY(spy.isValid()); QVERIFY(spy.isEmpty()); emit emitter1.signal1(42); QCOMPARE(spy.size(), 1); QCOMPARE(spy.at(0).at(0).value(), &emitter1); QCOMPARE(spy.at(0).at(1).toInt(), emitter1.metaObject()->indexOfSignal("signal1(int)")); QCOMPARE(spy.at(0).at(2).value >().first().toInt(), 42); emit emitter2.signal2(QStringLiteral("hello")); QCOMPARE(spy.size(), 2); QCOMPARE(spy.at(1).at(0).value(), &emitter2); QCOMPARE(spy.at(1).at(1).toInt(), emitter1.metaObject()->indexOfSignal("signal2(QString)")); QCOMPARE(spy.at(1).at(2).value >().first().toString(), QStringLiteral("hello")); } }; QTEST_MAIN(MultiSignalMapperTest) #include "multisignalmappertest.moc" gammaray-2.11.3/tests/multithreadingtest.cpp000066400000000000000000000053661413310443000211650ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "baseprobetest.h" #include #include #include using namespace GammaRay; class Thread : public QThread { Q_OBJECT public: Thread() = default; void run() override { objects.reserve(batchSize); for (int i = 0; i < iterations; ++i) { for (int j = 0; j < batchSize; ++j) { objects.push_back(new QObject); } QTest::qWait(delay); qDeleteAll(objects); objects.clear(); } } QVector objects; int batchSize = 1; int delay = 0; int iterations = 100; }; class MultiThreadingTest : public BaseProbeTest { Q_OBJECT private slots: void testCreateDestroy_data() { QTest::addColumn("batchSize", nullptr); QTest::addColumn("delay", nullptr); QTest::addColumn("iterations", nullptr); QTest::newRow("10-0-1000") << 10 << 0 << 1000; QTest::newRow("100-1-100") << 100 << 1 << 100; QTest::newRow("1000-10-100") << 1000 << 10 << 100; } void testCreateDestroy() { QFETCH(int, batchSize); QFETCH(int, delay); QFETCH(int, iterations); createProbe(); Thread t; t.batchSize = batchSize; t.delay = delay; t.iterations = iterations; QTest::qWait(1); QSignalSpy spy(&t, SIGNAL(finished())); QVERIFY(spy.isValid()); // this mainly aims at not triggering any of the sanity checks in the object models or Probe t.start(); QVERIFY(spy.wait(30000)); } }; QTEST_MAIN(MultiThreadingTest) #include "multithreadingtest.moc" gammaray-2.11.3/tests/networkselectionmodeltest.cpp000066400000000000000000000232541413310443000225610ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include <3rdparty/qt/modeltest.h> #include #include #include #include #include #include #include #include #include QT_BEGIN_NAMESPACE namespace QTest { template<> bool qCompare(const QPersistentModelIndex &lhs, const QModelIndex &rhs, char const *actual, char const *expected, char const *file, int line) { return qCompare(lhs, QPersistentModelIndex(rhs), actual, expected, file, line); } } QT_END_NAMESPACE using namespace GammaRay; namespace GammaRay { enum FakeAddress { ServerAddress = 23, ClientAddress = 42, }; class FakeEndpoint : public Endpoint { Q_OBJECT public: explicit FakeEndpoint(QObject *parent = nullptr) : Endpoint(parent) { setDevice(new QBuffer(this)); } protected: void doSendMessage(const Message &msg) override { QByteArray ba; QBuffer buffer(&ba); buffer.open(QIODevice::ReadWrite); msg.write(&buffer); buffer.seek(sizeof(Protocol::PayloadSize)); Protocol::ObjectAddress addr = qToBigEndian(static_cast(msg.address() == ServerAddress ? ClientAddress : ServerAddress)); buffer.write((char *)&addr, sizeof(addr)); buffer.seek(0); emit message(Message::readMessage(&buffer)); } bool isRemoteClient() const override { return true; } void messageReceived(const GammaRay::Message &) override {} QUrl serverAddress() const override { return QUrl(); } void handlerDestroyed(Protocol::ObjectAddress, const QString &) override {} void objectDestroyed(Protocol::ObjectAddress, const QString &, QObject *) override {} signals: void message(const GammaRay::Message &msg); }; class FakeNetworkSelectionModel : public NetworkSelectionModel { Q_OBJECT public: explicit FakeNetworkSelectionModel(Protocol::ObjectAddress address, QAbstractItemModel *model, QObject *parent = nullptr) : NetworkSelectionModel(QStringLiteral("com.kdab.GammaRay.UnitTest.Model"), model, parent) { m_myAddress = address; connect(Endpoint::instance(), SIGNAL(message(GammaRay::Message)), this, SLOT(dispatchMessage(GammaRay::Message))); } void applyPendingSelection() { NetworkSelectionModel::applyPendingSelection(); } void requestSelection() { NetworkSelectionModel::requestSelection(); } private slots: void dispatchMessage(const GammaRay::Message &msg) { if (msg.address() != m_myAddress) return; QMetaObject::invokeMethod(this, "newMessage", Q_ARG(GammaRay::Message, msg)); } }; } class NetworkSelectionModelTest : public QObject { Q_OBJECT private: void fillModel(QStandardItemModel *model) { model->appendRow(new QStandardItem(QStringLiteral("Row 1"))); model->appendRow(new QStandardItem(QStringLiteral("Row 2"))); model->appendRow(new QStandardItem(QStringLiteral("Row 3"))); model->appendRow(new QStandardItem(QStringLiteral("Row 4"))); model->appendRow(new QStandardItem(QStringLiteral("Row 5"))); } private slots: void initTestCase() { qRegisterMetaType(); qRegisterMetaType(); new FakeEndpoint; QVERIFY(Endpoint::instance()); QVERIFY(Endpoint::isConnected()); } void cleanupTestCase() { delete FakeEndpoint::instance(); } void testSelectionUpdate() { QStandardItemModel serverModel; FakeNetworkSelectionModel serverSelection(ServerAddress, &serverModel); fillModel(&serverModel); QSignalSpy serverSpy(&serverSelection, SIGNAL(selectionChanged(QItemSelection,QItemSelection))); QVERIFY(serverSpy.isValid()); QStandardItemModel clientModel; fillModel(&clientModel); FakeNetworkSelectionModel clientSelection(ClientAddress, &clientModel); QSignalSpy clientSpy(&clientSelection, SIGNAL(selectionChanged(QItemSelection,QItemSelection))); QVERIFY(clientSpy.isValid()); serverSelection.select(serverModel.index(2, 0), QItemSelectionModel::ClearAndSelect); QCOMPARE(serverSelection.selection().size(), 1); QCOMPARE(serverSelection.selection().first().topLeft(), serverModel.index(2, 0)); QCOMPARE(clientSelection.selection().size(), 1); QCOMPARE(clientSelection.selection().first().topLeft(), clientModel.index(2, 0)); QCOMPARE(clientSpy.size(), 1); serverSpy.clear(); clientSelection.select(clientModel.index(4, 0), QItemSelectionModel::ClearAndSelect); QCOMPARE(clientSelection.selection().size(), 1); QCOMPARE(clientSelection.selection().first().topLeft(), clientModel.index(4, 0)); QCOMPARE(serverSelection.selection().size(), 1); QCOMPARE(serverSelection.selection().first().topLeft(), serverModel.index(4, 0)); QCOMPARE(serverSpy.size(), 1); } void testDelayedSelectionUpdate() { QStandardItemModel serverModel; FakeNetworkSelectionModel serverSelection(ServerAddress, &serverModel); fillModel(&serverModel); QStandardItemModel clientModel; FakeNetworkSelectionModel clientSelection(ClientAddress, &clientModel); QSignalSpy clientSpy(&clientSelection, SIGNAL(selectionChanged(QItemSelection,QItemSelection))); QVERIFY(clientSpy.isValid()); QVERIFY(!clientSelection.hasSelection()); serverSelection.select(serverModel.index(4, 0), QItemSelectionModel::ClearAndSelect); QTest::qWait(100); fillModel(&clientModel); clientSelection.applyPendingSelection(); // usually triggered by SelectionModelClient QCOMPARE(clientSelection.selection().size(), 1); QCOMPARE(clientSelection.selection().first().topLeft(), clientModel.index(4, 0)); QCOMPARE(clientSpy.size(), 1); } void testInitialSelectionTransfer() { QStandardItemModel serverModel; FakeNetworkSelectionModel serverSelection(ServerAddress, &serverModel); fillModel(&serverModel); serverSelection.select(serverModel.index(3, 0), QItemSelectionModel::ClearAndSelect); QTest::qWait(100); QStandardItemModel clientModel; fillModel(&clientModel); FakeNetworkSelectionModel clientSelection(ClientAddress, &clientModel); QSignalSpy clientSpy(&clientSelection, SIGNAL(selectionChanged(QItemSelection,QItemSelection))); QVERIFY(clientSpy.isValid()); clientSelection.requestSelection(); // usually called by SelectionModelClient QCOMPARE(clientSelection.selection().size(), 1); QCOMPARE(clientSelection.selection().first().topLeft(), clientModel.index(3, 0)); QCOMPARE(clientSpy.size(), 1); } void testCurrent() { QStandardItemModel serverModel; FakeNetworkSelectionModel serverSelection(ServerAddress, &serverModel); fillModel(&serverModel); QSignalSpy serverSpy(&serverSelection, SIGNAL(currentChanged(QModelIndex,QModelIndex))); QVERIFY(serverSpy.isValid()); QStandardItemModel clientModel; fillModel(&clientModel); FakeNetworkSelectionModel clientSelection(ClientAddress, &clientModel); QSignalSpy clientSpy(&clientSelection, SIGNAL(currentChanged(QModelIndex,QModelIndex))); QVERIFY(clientSpy.isValid()); QSignalSpy clientRowSpy(&clientSelection, SIGNAL(currentRowChanged(QModelIndex,QModelIndex))); QVERIFY(clientRowSpy.isValid()); serverSelection.setCurrentIndex(serverModel.index(2, 0), QItemSelectionModel::NoUpdate); QVERIFY(!serverSelection.hasSelection()); QCOMPARE(serverSelection.currentIndex(), serverModel.index(2, 0)); QVERIFY(!clientSelection.hasSelection()); QCOMPARE(clientSelection.currentIndex(), clientModel.index(2, 0)); QCOMPARE(clientSpy.size(), 1); QCOMPARE(clientRowSpy.size(), 1); serverSpy.clear(); clientSelection.setCurrentIndex(clientModel.index(4, 0), QItemSelectionModel::NoUpdate); QVERIFY(!clientSelection.hasSelection()); QCOMPARE(clientSelection.currentIndex(), clientModel.index(4, 0)); QVERIFY(!serverSelection.hasSelection()); QCOMPARE(serverSelection.currentIndex(), serverModel.index(4, 0)); QCOMPARE(serverSpy.size(), 1); } }; QTEST_MAIN(NetworkSelectionModelTest) #include "networkselectionmodeltest.moc" gammaray-2.11.3/tests/objectinstancetest.cpp000066400000000000000000000071561413310443000211370ustar00rootroot00000000000000/* objectinstancetest.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include #include Q_DECLARE_METATYPE(QDateTime *) struct CustomType {}; Q_DECLARE_METATYPE(CustomType) Q_DECLARE_METATYPE(CustomType *) using namespace GammaRay; class ObjectInstanceTest : public QObject { Q_OBJECT private slots: void testQtObject() { QObject obj; ObjectInstance oi(QVariant::fromValue(&obj)); QCOMPARE(oi.type(), ObjectInstance::QtObject); QCOMPARE(oi.typeName(), QByteArray("QObject")); QCOMPARE(oi.object(), &obj); QCOMPARE(oi.qtObject(), &obj); QCOMPARE(oi.metaObject(), &QObject::staticMetaObject); QVERIFY(!oi.isValueType()); auto oi2 = oi; QCOMPARE(oi2.type(), ObjectInstance::QtObject); QCOMPARE(oi.object(), oi2.object()); } void testMetaObjectVariantPointer() { QDateTime dt; ObjectInstance oi(QVariant::fromValue(&dt)); QCOMPARE(oi.type(), ObjectInstance::Object); QCOMPARE(oi.typeName(), QByteArray("QDateTime*")); QVERIFY(oi.object()); QVERIFY(!oi.isValueType()); auto oi2 = oi; QCOMPARE(oi2.type(), ObjectInstance::Object); QCOMPARE(oi.object(), oi2.object()); QDateTime dt2; oi2 = ObjectInstance(QVariant::fromValue(&dt2)); QVERIFY(!(oi == oi2)); } void testMetaObjectVariantValue() { QDateTime dt; ObjectInstance oi(QVariant::fromValue(dt)); QCOMPARE(oi.type(), ObjectInstance::Value); QCOMPARE(oi.typeName(), QByteArray("QDateTime")); QVERIFY(oi.object()); QVERIFY(oi.isValueType()); auto oi2 = oi; QCOMPARE(oi2.type(), ObjectInstance::Value); QVERIFY(oi2.object()); QDateTime dt2; oi2 = ObjectInstance(QVariant::fromValue(dt2)); QCOMPARE(oi2.type(), ObjectInstance::Value); QVERIFY(dt == dt2); QVERIFY(oi.variant() == oi2.variant()); QVERIFY(oi == oi2); } void testUnknownVariantValue() { CustomType t; ObjectInstance oi(QVariant::fromValue(t)); QCOMPARE(oi.type(), ObjectInstance::QtVariant); QCOMPARE(oi.typeName(), QByteArray("CustomType")); } void testUnknownVariantPointer() { CustomType t; ObjectInstance oi(QVariant::fromValue(&t)); QCOMPARE(oi.type(), ObjectInstance::QtVariant); QCOMPARE(oi.typeName(), QByteArray("CustomType*")); } }; QTEST_MAIN(ObjectInstanceTest) #include "objectinstancetest.moc" gammaray-2.11.3/tests/probeabidetectortest.cpp000066400000000000000000000101261413310443000214500ustar00rootroot00000000000000/* probeabidetectortest.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include #include #include #include using namespace GammaRay; class ProbeABIDetectorTest : public QObject { Q_OBJECT private slots: void testDetectExecutable() { ProbeABIDetector detector; QVERIFY(!detector.qtCoreForExecutable(QCoreApplication::applicationFilePath()).isEmpty()); const ProbeABI abi = detector.abiForExecutable(QCoreApplication::applicationFilePath()); QCOMPARE(abi.id(), QStringLiteral(GAMMARAY_PROBE_ABI)); } void testDetectProcess() { ProbeABIDetector detector; QVERIFY(!detector.qtCoreForProcess(QCoreApplication::applicationPid()).isEmpty()); const ProbeABI abi = detector.abiForProcess(QCoreApplication::applicationPid()); QCOMPARE(abi.id(), QStringLiteral(GAMMARAY_PROBE_ABI)); } void testContainsQtCore_data() { QTest::addColumn("line", nullptr); QTest::addColumn("isQtCore", nullptr); QTest::newRow("empty") << QString() << false; QTest::newRow("too short1") << "Qt" << false; QTest::newRow("too short2") << "Qt5" << false; QTest::newRow("unix1") << "libQtCore.so.4.8.6" << true; QTest::newRow("unix2") << "libQt5Core.so" << true; QTest::newRow("unix3") << "/path/to/libQt6Core.so.6.5.4" << true; QTest::newRow("unix4") << "\t libQt5Core.so.5.4.1\n" << true; QTest::newRow("mac1") << "QtCore" << true; QTest::newRow("mac2") << "/framework/5/QtCore" << true; QTest::newRow("mac3") << "Qt5Core.dylib.5.3.2" << true; QTest::newRow("mac4") << "libQt5Core.dylib" << true; QTest::newRow("mac5") << "/path/to/QtCore.dylib" << true; QTest::newRow("mac debug1") << "QtCore_debug" << true; QTest::newRow("mac debug2") << "QtCore_debug.dylib" << true; QTest::newRow("mac qt55 framework") << "QtCore.framework/Versions/5/QtCore" << true; QTest::newRow("win1") << "QtCore.dll" << true; QTest::newRow("win2") << "Qt5Core.dll" << true; QTest::newRow("win3") << R"(c:\path\to\Qt6Core.dll)" << true; QTest::newRow("win debug1") << "QtCored.dll" << true; QTest::newRow("win debug2") << "Qt5Cored.dll" << true; QTest::newRow("complex path") << "/Qt/Core/5/QtCore.dll" << true; QTest::newRow("addon1") << "QtCoreAddon.dll" << false; QTest::newRow("addon2") << "Qt5CoredAddon.so" << false; // QTest::newRow("addon3") << "QtCore_Addon.dll" << false; QTest::newRow("QT") << "QTCore" << false; QTest::newRow("prefix") << "libFooQtCore.so" << false; QTest::newRow("libQt") << "libQt.dylib" << false; } void testContainsQtCore() { QFETCH(QString, line); QFETCH(bool, isQtCore); QCOMPARE(ProbeABIDetector::containsQtCore(line.toUtf8()), isQtCore); } }; QTEST_MAIN(ProbeABIDetectorTest) #include "probeabidetectortest.moc" gammaray-2.11.3/tests/probeabitest.cpp000066400000000000000000000216641413310443000177270ustar00rootroot00000000000000/* probeabitest.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include #include using namespace GammaRay; class ProbeABITest : public QObject { Q_OBJECT private slots: void testIsValid() { ProbeABI abi; QVERIFY(!abi.isValid()); abi.setQtVersion(5, 2); QVERIFY(!abi.isValid()); abi.setArchitecture(QStringLiteral("x86_64")); #ifndef Q_OS_WIN QVERIFY(abi.isValid()); #else QVERIFY(!abi.isValid()); #endif abi.setCompiler(QStringLiteral("MSVC")); abi.setCompilerVersion(QStringLiteral("140")); QVERIFY(abi.isValid()); } void testToString_data() { QTest::addColumn("id", nullptr); QTest::addColumn("majorVersion", nullptr); QTest::addColumn("minorVersion", nullptr); QTest::addColumn("isDebug", nullptr); QTest::addColumn("arch", nullptr); QTest::addColumn("compiler", nullptr); QTest::addColumn("compilerVersion", nullptr); QTest::newRow("invalid") << QString() << -1 << -1 << false << QString() << QString() << QString(); #ifndef Q_OS_WIN #if defined(Q_OS_MAC) QTest::newRow("mac") << "qt5_2-x86_64_debug" << 5 << 2 << true << "x86_64" << "CLANG" << QString(); #else QTest::newRow("unix") << "qt5_2-x86_64" << 5 << 2 << true << "x86_64" << "GCC" << QString(); #endif #else QTest::newRow("msvc") << "qt5_2-MSVC-140-x86_64d" << 5 << 2 << true << "x86_64" << "MSVC" << "140"; QTest::newRow("mingw") << "qt5_2-GNU-i686" << 5 << 2 << false << "i686" << "GNU" << QString(); #endif } void testToString() { QFETCH(QString, id); QFETCH(int, majorVersion); QFETCH(int, minorVersion); QFETCH(bool, isDebug); QFETCH(QString, arch); QFETCH(QString, compiler); QFETCH(QString, compilerVersion); ProbeABI abi; abi.setQtVersion(majorVersion, minorVersion); abi.setIsDebug(isDebug); abi.setArchitecture(arch); abi.setCompiler(compiler); abi.setCompilerVersion(compilerVersion); QCOMPARE(abi.id(), id); } void testFromString_data() { QTest::addColumn("id", nullptr); QTest::addColumn("valid", nullptr); QTest::addColumn("majorVersion", nullptr); QTest::addColumn("minorVersion", nullptr); QTest::addColumn("isDebug", nullptr); QTest::addColumn("arch", nullptr); QTest::addColumn("compiler", nullptr); QTest::addColumn("compilerVersion", nullptr); QTest::newRow("invalid") << QString() << false << -1 << -1 << false << QString() << QString() << QString(); QTest::newRow("only version") << "qt5_2" << false << -1 << -1 << false << QString() << QString() << QString(); QTest::newRow("too many items") << "qt5_2-some-random-stuff-with-too-many-dashs" << false << -1 << -1 << false << QString() << QString() << QString(); QTest::newRow("extra debug/release") << "qt5_2-MSVC-140-release-i686" << false << -1 << -1 << false << QString() << QString() << QString(); QTest::newRow("extra debug/release") << "qt5_2-GNU-debug-arm" << false << -1 << -1 << false << QString() << QString() << QString(); #ifndef Q_OS_WIN #if defined(Q_OS_MAC) QTest::newRow("mac") << "qt5_2-x86_64_debug" << true << 5 << 2 << true << "x86_64" << "CLANG" << QString(); #else QTest::newRow("unix") << "qt5_2-x86_64" << true << 5 << 2 << true << "x86_64" << "GCC" << QString(); #endif #else QTest::newRow("msvc") << "qt5_2-MSVC-140-x86_64d" << true << 5 << 2 << true << "x86_64" << "MSVC" << "140"; QTest::newRow("mingw") << "qt5_2-GNU-i686d" << true << 5 << 2 << true << "i686" << "GNU" << QString(); #endif } void testFromString() { QFETCH(QString, id); QFETCH(bool, valid); QFETCH(int, majorVersion); QFETCH(int, minorVersion); QFETCH(bool, isDebug); QFETCH(QString, arch); QFETCH(QString, compiler); #ifdef Q_OS_WIN QFETCH(QString, compilerVersion); #endif const ProbeABI abi = ProbeABI::fromString(id); QCOMPARE(abi.isValid(), valid); if (!valid) return; QCOMPARE(abi.majorQtVersion(), majorVersion); QCOMPARE(abi.minorQtVersion(), minorVersion); QCOMPARE(abi.architecture(), arch); if (abi.isDebugRelevant()) { QCOMPARE(abi.isDebug(), isDebug); } #ifdef Q_OS_WIN QCOMPARE(abi.compiler(), compiler); QCOMPARE(abi.compilerVersion(), compilerVersion); #else Q_UNUSED(compiler); #endif } void testDisplayString_data() { QTest::addColumn("id", nullptr); QTest::addColumn("display", nullptr); QTest::newRow("invalid") << QString() << QString(); #ifndef Q_OS_WIN #if defined(Q_OS_MAC) QTest::newRow("mac") << "qt5_2-x86_64_debug" << "Qt 5.2 (debug, x86_64)"; #else QTest::newRow("unix") << "qt5_2-x86_64" << "Qt 5.2 (x86_64)"; #endif #else QTest::newRow("msvc") << "qt5_2-MSVC-140-x86_64d" << "Qt 5.2 (MSVC, 140, debug, x86_64)"; QTest::newRow("mingw") << "qt5_2-GNU-i686d" << "Qt 5.2 (GNU, debug, i686)"; #endif } void testDisplayString() { QFETCH(QString, id); QFETCH(QString, display); const ProbeABI abi = ProbeABI::fromString(id); QCOMPARE(abi.displayString(), display); } void testProbeABICompat() { #ifndef Q_OS_WIN const ProbeABI targetABI = ProbeABI::fromString(QStringLiteral("qt5_2-x86_64")); const ProbeABI probeABI = ProbeABI::fromString(QStringLiteral("qt5_1-x86_64")); #if defined(Q_OS_MAC) const bool debugAbiMatters = true; #else const bool debugAbiMatters = false; #endif const bool compilerAbiMatters = false; #else const ProbeABI targetABI = ProbeABI::fromString(QStringLiteral("qt5_2-MSVC-140-x86_64")); const ProbeABI probeABI = ProbeABI::fromString(QStringLiteral("qt5_1-MSVC-140-x86_64")); const bool debugAbiMatters = true; const bool compilerAbiMatters = true; #endif // full match, or same major version and older probe QVERIFY(targetABI.isCompatible(targetABI)); QVERIFY(targetABI.isCompatible(probeABI)); // incompatible // newer minor version probe QVERIFY(!probeABI.isCompatible(targetABI)); // different major version ProbeABI incompatABI(probeABI); incompatABI.setQtVersion(4, 8); QVERIFY(!targetABI.isCompatible(incompatABI)); QVERIFY(!incompatABI.isCompatible(targetABI)); // different architecture incompatABI = targetABI; incompatABI.setArchitecture(QStringLiteral("i686")); QVERIFY(!targetABI.isCompatible(incompatABI)); // different debug/release mode incompatABI = targetABI; incompatABI.setIsDebug(true); QCOMPARE(targetABI.isCompatible(incompatABI), !debugAbiMatters); // different compiler incompatABI = targetABI; incompatABI.setCompiler(QStringLiteral("Clang")); QCOMPARE(targetABI.isCompatible(incompatABI), !compilerAbiMatters); } void testProbeABISort() { ProbeABI qt52; qt52.setQtVersion(5, 2); ProbeABI qt51; qt51.setQtVersion(5, 1); ProbeABI qt48; qt48.setQtVersion(4, 8); QVERIFY(qt51 < qt52); QVERIFY(qt48 < qt52); QVERIFY(!(qt52 < qt51)); QVERIFY(!(qt51 < qt48)); QVERIFY(!(qt52 < qt52)); } }; QTEST_MAIN(ProbeABITest) #include "probeabitest.moc" gammaray-2.11.3/tests/probesettingsclient.cpp000066400000000000000000000031021413310443000213160ustar00rootroot00000000000000/* probesettingsclient.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include #include #include #include using namespace GammaRay; int main(int argc, char **argv) { QCoreApplication app(argc, argv); ProbeSettings::receiveSettings(); QUrl addr = QUrl::fromUserInput(ProbeSettings::value("TestValue").toString()); ProbeSettings::sendServerAddress(addr); QTimer::singleShot(1000, &app, SLOT(quit())); return app.exec(); } gammaray-2.11.3/tests/probesettingstest.cpp000066400000000000000000000053031413310443000210240ustar00rootroot00000000000000/* probesettingstest.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include #include #include #include #include #include using namespace GammaRay; class ProbeSettingsTest : public QObject { Q_OBJECT private slots: void testSettingsRoundtrip() { Paths::setRootPath(QCoreApplication::applicationDirPath() + QStringLiteral("/..")); LaunchOptions options; options.setUiMode(LaunchOptions::NoUi); options.setProbeSetting("TestValue", "https://www.kdab.com/"); options.setLaunchArguments(QStringList(QCoreApplication::applicationDirPath() + QStringLiteral("/probesettingsclient"))); // this will effectively disable injection, so we will just launch the process options.setProbePath(QCoreApplication::applicationDirPath()); options.setInjectorType(QStringLiteral("style")); options.setProbeSetting(QStringLiteral("ServerAddress"), GAMMARAY_DEFAULT_LOCAL_TCP_URL); Launcher launcher(options); QSignalSpy startSpy(&launcher, SIGNAL(started())); QVERIFY(startSpy.isValid()); QSignalSpy finishSpy(&launcher, SIGNAL(finished())); QVERIFY(finishSpy.isValid()); QVERIFY(launcher.start()); QVERIFY(finishSpy.wait()); QCOMPARE(startSpy.size(), 1); QCOMPARE(finishSpy.size(), 1); QCOMPARE(launcher.serverAddress(), QUrl(QStringLiteral("https://www.kdab.com/"))); } }; QTEST_MAIN(ProbeSettingsTest) #include "probesettingstest.moc" gammaray-2.11.3/tests/problemreportertest.cpp000066400000000000000000000456111413310443000213650ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2018-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "baseprobetest.h" #include #include #include #include #include <3rdparty/qt/modeltest.h> #include #include #include #include #include #include #include #include #ifdef QT_QML_LIB #include #include #include #endif #ifdef HAVE_QT_WIDGETS #include #include #endif using namespace GammaRay; class CrossThreadConnectionTask : public QRunnable { public: void run() override { newThreadObj.reset(new QObject()); newThreadObj->setObjectName("newThreadObj"); QObject::connect(newThreadObj.get(), &QObject::destroyed, mainThreadObj.get(), &QObject::deleteLater, Qt::DirectConnection); } std::unique_ptr newThreadObj; std::unique_ptr mainThreadObj; }; struct UnregisteredType {}; class FaultyMetaObjectBaseClass : public QObject { Q_OBJECT Q_PROPERTY(UnregisteredType someProp READ someProp CONSTANT) UnregisteredType someProp() const { return {}; } }; class FaultyMetaObjectClass : public FaultyMetaObjectBaseClass { Q_OBJECT Q_PROPERTY(UnregisteredType someProp READ someProp CONSTANT) public: Q_INVOKABLE void noop(UnregisteredType param) { Q_UNUSED(param) } UnregisteredType someProp() const { return {}; } }; namespace GammaRay { class ProblemReporterTest : public BaseProbeTest { Q_OBJECT static void dummyScan() { Problem p1; p1.problemId = QUuid::createUuid().toString(); p1.findingCategory = Problem::Scan; ProblemCollector::addProblem(p1); Problem p2; p2.problemId = QUuid::createUuid().toString(); p2.findingCategory = Problem::Scan; ProblemCollector::addProblem(p2); } std::unique_ptr problemModelTest; std::unique_ptr availableCheckersModelTest; private slots: void initTestCase() { createProbe(); problemModelTest.reset(new ModelTest(ObjectBroker::model(QStringLiteral("com.kdab.GammaRay.ProblemModel")))); availableCheckersModelTest.reset(new ModelTest(ObjectBroker::model(QStringLiteral("com.kdab.GammaRay.AvailableProblemCheckersModel")))); } void cleanup() { ProblemCollector::instance()->clearScans(); QCOMPARE(ProblemCollector::instance()->problems().size(), 0); } void testDuplicates() { QCOMPARE(ProblemCollector::instance()->problems().size(), 0); Problem p1; p1.problemId = QStringLiteral("9skjlksdjb"); ProblemCollector::addProblem(p1); Problem p2; p2.problemId = QStringLiteral("9skjlksdjb"); ProblemCollector::addProblem(p2); QCOMPARE(ProblemCollector::instance()->problems().size(), 1); ProblemCollector::removeProblem(QStringLiteral("9skjlksdjb")); } void testMultipleSourceLocations() { QCOMPARE(ProblemCollector::instance()->problems().size(), 0); SourceLocation l1 = SourceLocation::fromOneBased(QUrl("../main.qml"), 101, 42); SourceLocation l2 = SourceLocation::fromOneBased(QUrl("A.qml"), 43, 21); SourceLocation l3 = SourceLocation::fromOneBased(QUrl("../../src/Singleton.cpp"), 555, 1); SourceLocation l4 = SourceLocation::fromOneBased(QUrl("../main.qml"), 101, 42); SourceLocation l5 = SourceLocation::fromOneBased(QUrl("A.qml"), 44, 21); Problem p1; p1.problemId = QStringLiteral("abcdefg"); ProblemCollector::addProblem(p1); Problem p2; p2.problemId = QStringLiteral("abcdefg"); p2.locations << l1; ProblemCollector::addProblem(p2); Problem p3; p3.problemId = QStringLiteral("abcdefg"); p3.locations << l2 << l3 << l4; ProblemCollector::addProblem(p3); Problem p4; p4.problemId = QStringLiteral("abcdefg"); p4.locations << l5; ProblemCollector::addProblem(p4); QCOMPARE(ProblemCollector::instance()->problems().size(), 1); QCOMPARE(ProblemCollector::instance()->problems().front().locations.size(), 4); QCOMPARE(ProblemCollector::instance()->problems().front().locations.at(0), SourceLocation::fromOneBased(QUrl("../main.qml"), 101, 42)); QCOMPARE(ProblemCollector::instance()->problems().front().locations.at(1), SourceLocation::fromOneBased(QUrl("A.qml"), 43, 21)); QCOMPARE(ProblemCollector::instance()->problems().front().locations.at(2), SourceLocation::fromOneBased(QUrl("../../src/Singleton.cpp"), 555, 1)); QCOMPARE(ProblemCollector::instance()->problems().front().locations.at(3), SourceLocation::fromOneBased(QUrl("A.qml"), 44, 21)); ProblemCollector::removeProblem(QStringLiteral("abcdefg")); } void testScans() { auto standardCheckersCount = ProblemCollector::instance()->availableCheckers().size(); ProblemCollector::registerProblemChecker(QStringLiteral("Dummy"), QStringLiteral("Dummy"), QStringLiteral("Always reports two dummy problems"), &ProblemReporterTest::dummyScan); QCOMPARE(ProblemCollector::instance()->availableCheckers().size(), standardCheckersCount + 1); QCOMPARE(ProblemCollector::instance()->problems().size(), 0); ProblemCollector::instance()->requestScan(); auto problemsFromScansCount = ProblemCollector::instance()->problems().size(); ProblemCollector::instance()->requestScan(); // scans should always be reproducible if the program didn't change. QCOMPARE(ProblemCollector::instance()->problems().size(), problemsFromScansCount); auto dummyChecker = std::find_if(ProblemCollector::instance()->availableCheckers().begin(), ProblemCollector::instance()->availableCheckers().end(), [](const ProblemCollector::Checker &c) { return c.id == QStringLiteral("Dummy"); } ); dummyChecker->enabled = false; ProblemCollector::instance()->requestScan(); // scans should always be reproducible if the program didn't change. QCOMPARE(ProblemCollector::instance()->problems().size(), problemsFromScansCount - 2); dummyChecker->enabled = true; Problem p1; p1.problemId = QStringLiteral("9skjlksdjb"); p1.findingCategory = Problem::Live; ProblemCollector::addProblem(p1); Problem p2; p2.problemId = QStringLiteral("abcdefg"); p2.findingCategory = Problem::Permanent; ProblemCollector::addProblem(p2); // all problems originating from a scan should be deleted before doing a new scan, but not live- and permanent problems ProblemCollector::instance()->requestScan(); QCOMPARE(ProblemCollector::instance()->problems().size(), problemsFromScansCount + 2); ProblemCollector::instance()->clearScans(); QCOMPARE(ProblemCollector::instance()->problems().size(), 2); ProblemCollector::removeProblem(QStringLiteral("9skjlksdjb")); ProblemCollector::removeProblem(QStringLiteral("abcdefg")); ProblemCollector::instance()->availableCheckers().erase(dummyChecker); } void testAvailableScansModel() { auto model = ObjectBroker::model(QStringLiteral("com.kdab.GammaRay.AvailableProblemCheckersModel")); auto rowCount = model->rowCount(); ProblemCollector::registerProblemChecker(QStringLiteral("Dummy"), QStringLiteral("Dummy"), QStringLiteral("Always reports two dummy problems"), &ProblemReporterTest::dummyScan); QCOMPARE(model->rowCount(), rowCount + 1); auto &checkers = ProblemCollector::instance()->availableCheckers(); checkers.erase(std::remove_if(checkers.begin(), checkers.end(), [](ProblemCollector::Checker &c) { return c.id == "Dummy"; } )); QCOMPARE(model->rowCount(), rowCount); } void testProblemModel() { auto model = ObjectBroker::model(QStringLiteral("com.kdab.GammaRay.ProblemModel")); auto rowCount = model->rowCount(); Problem p1; p1.problemId = QStringLiteral("9skjlksdjb"); p1.description = QStringLiteral("Lorem ipsum dolor sit amet."); p1.severity = Problem::Warning; SourceLocation l1 = SourceLocation::fromOneBased(QUrl("../main.qml"), 101, 42); SourceLocation l2 = SourceLocation::fromOneBased(QUrl("A.qml"), 43, 21); p1.object = ObjectId(this); p1.locations << l1 << l2; ProblemCollector::addProblem(p1); Problem p2; p2.problemId = QStringLiteral("asdf"); ProblemCollector::addProblem(p2); QCOMPARE(model->rowCount(), rowCount + 2); auto index = model->index(rowCount, 0, QModelIndex()); QCOMPARE(index.data(Qt::DisplayRole).toString(), QStringLiteral("Lorem ipsum dolor sit amet.")); QCOMPARE(index.sibling(rowCount, 1).data(Qt::DisplayRole).toString(), l1.displayString()); QCOMPARE(index.data(ObjectModel::ObjectIdRole).value(), ObjectId(this)); QCOMPARE(index.data(ProblemModelRoles::SourceLocationRole).value>(), p1.locations); QCOMPARE(index.data(ProblemModelRoles::SeverityRole).value(), static_cast(Problem::Warning)); QCOMPARE(index.data(ProblemModelRoles::ProblemIdRole).toString(), QStringLiteral("9skjlksdjb")); ProblemCollector::removeProblem(QStringLiteral("9skjlksdjb")); QCOMPARE(model->rowCount(), rowCount + 1); ProblemCollector::removeProblem(QStringLiteral("asdf")); QCOMPARE(model->rowCount(), rowCount); } #ifdef QT_QML_LIB void testBindingLoopChecker() { QQmlEngine engine; QQmlComponent c(&engine); c.setData("import QtQml 2.0\n" "QtObject{id: root\n" " property list children: [\n" " QtObject {id: a; objectName: b.objectName },\n" " QtObject {id: b; objectName: a.objectName }\n" " ]\n" "}", QUrl()); std::unique_ptr obj(c.create()); QTest::qWait(1); QVERIFY(static_cast(obj)); QVERIFY(ProblemCollector::instance()->isCheckerRegistered("com.kdab.GammaRay.ObjectInspector.BindingLoopScan")); ProblemCollector::instance()->requestScan(); #if QT_VERSION < QT_VERSION_CHECK(5, 10, 0) QEXPECT_FAIL("", "Can't find QML bindings with Qt < 5.10.", Abort); #endif const auto &problems = ProblemCollector::instance()->problems(); QVERIFY(std::any_of(problems.begin(), problems.end(), [](const Problem &p){ return p.problemId.startsWith("com.kdab.GammaRay.ObjectInspector.BindingLoopScan"); } )); } #endif void testConnectionIssues() { QVERIFY(ProblemCollector::instance()->isCheckerRegistered("com.kdab.GammaRay.ObjectInspector.ConnectionsCheck")); auto task = std::unique_ptr(new CrossThreadConnectionTask()); task->mainThreadObj.reset(new QObject()); task->mainThreadObj->setObjectName("mainThreadObj"); task->setAutoDelete(false); // QThreadPool takes ownership and deletes 'hello' automatically QThreadPool::globalInstance()->start(task.get()); auto o1 = std::unique_ptr(new QObject()); o1->setObjectName("o1"); auto o2 = std::unique_ptr(new QObject()); o2->setObjectName("o2"); connect(o1.get(), SIGNAL(destroyed(QObject*)), o2.get(), SLOT(deleteLater())); connect(o1.get(), SIGNAL(destroyed(QObject*)), o2.get(), SLOT(deleteLater())); QTest::qWait(10); ProblemCollector::instance()->requestScan(); o1->disconnect(); task->newThreadObj->disconnect(); const auto &problems = ProblemCollector::instance()->problems(); auto crossThreadProblem = std::find_if(problems.begin(), problems.end(), [](const Problem &p){ return p.problemId.startsWith("com.kdab.GammaRay.ObjectInspector.ConnectionsCheck.CrossTread"); } ); QVERIFY(crossThreadProblem != problems.end()); QCOMPARE(crossThreadProblem->object, ObjectId(task->mainThreadObj.get())); QVERIFY(crossThreadProblem->description.contains("direct cross-thread connection")); QVERIFY(crossThreadProblem->description.contains("signal newThreadObj")); QVERIFY(crossThreadProblem->description.contains("slot mainThreadObj")); auto duplicateProblem = std::find_if(problems.begin(), problems.end(), [](const Problem &p){ return p.problemId.startsWith("com.kdab.GammaRay.ObjectInspector.ConnectionsCheck.Duplicate"); } ); QVERIFY(duplicateProblem != problems.end()); QCOMPARE(duplicateProblem->object, ObjectId(o2.get())); QVERIFY(duplicateProblem->description.contains("multiple times")); QVERIFY(duplicateProblem->description.contains("signal o1")); QVERIFY(duplicateProblem->description.contains("slot o2")); disconnect(o1.get(), nullptr, o2.get(), nullptr); connect(o1.get(), &QObject::destroyed, o2.get(), &QObject::deleteLater); connect(o1.get(), &QObject::destroyed, o2.get(), &QObject::deleteLater); QTest::qWait(10); ProblemCollector::instance()->requestScan(); const auto &problems2 = ProblemCollector::instance()->problems(); auto duplicateProblem2 = std::find_if(problems2.begin(), problems2.end(), [&o2](const Problem &p){ return p.problemId.startsWith("com.kdab.GammaRay.ObjectInspector.ConnectionsCheck.Duplicate") && p.object == ObjectId(o2.get()); } ); QEXPECT_FAIL("", "We can't find duplicates with PMF connects, yet.", Abort); QVERIFY(duplicateProblem2 != problems2.end()); QCOMPARE(duplicateProblem2->object, ObjectId(o2.get())); QVERIFY(duplicateProblem2->description.contains("multiple times")); QVERIFY(duplicateProblem2->description.contains("signal o1")); QVERIFY(duplicateProblem2->description.contains("slot o2")); } void testMetaTypeChecks() { std::unique_ptr obj(new FaultyMetaObjectClass); QTest::qWait(1); auto &checkers = ProblemCollector::instance()->availableCheckers(); auto checker = std::find_if(checkers.begin(), checkers.end(), [](ProblemCollector::Checker &c){ return c.id == "com.kdab.GammaRay.MetaObjectBrowser.QMetaObjectValidator"; } ); QVERIFY(checker != checkers.end()); checker->enabled = true; ProblemCollector::instance()->requestScan(); const auto &problems = ProblemCollector::instance()->problems(); QVERIFY(std::any_of(problems.begin(), problems.end(), [&obj](const Problem &p){ return p.problemId.startsWith("com.kdab.GammaRay.MetaObjectBrowser.QMetaObjectValidator") && p.object == ObjectId(const_cast(obj->metaObject()), "const QMetaObject*") && p.description.contains(QLatin1String("overrides base class property")); } )); QVERIFY(std::any_of(problems.begin(), problems.end(), [&obj](const Problem &p){ return p.problemId.startsWith("com.kdab.GammaRay.MetaObjectBrowser.QMetaObjectValidator") && p.object == ObjectId(const_cast(obj->metaObject()), "const QMetaObject*") && p.description.contains(QLatin1String("parameter type not registered")); } )); QVERIFY(std::any_of(problems.begin(), problems.end(), [&obj](const Problem &p){ return p.problemId.startsWith("com.kdab.GammaRay.MetaObjectBrowser.QMetaObjectValidator") && p.object == ObjectId(const_cast(obj->metaObject()), "const QMetaObject*") && p.description.contains(QLatin1String("property with a type not registered")); } )); } #ifdef HAVE_QT_WIDGETS void testActionValidator() { QAction *a1 = new QAction(QStringLiteral("Action 1"), qApp); a1->setShortcut(QKeySequence(QStringLiteral("Ctrl+K"))); a1->setShortcutContext(Qt::ApplicationShortcut); QAction *a2 = new QAction(QStringLiteral("Action 2"), qApp); a2->setShortcut(QKeySequence(QStringLiteral("Ctrl+K"))); a2->setShortcutContext(Qt::WidgetShortcut); QTest::qWait(1); // event loop re-entry QVERIFY(ProblemCollector::instance()->isCheckerRegistered("gammaray_actioninspector.ShortcutDuplicates")); ProblemCollector::instance()->requestScan(); const auto &problems = ProblemCollector::instance()->problems(); QVERIFY(std::any_of(problems.begin(), problems.end(), [=](const Problem &p){ return p.problemId.startsWith("gammaray_actioninspector.ShortcutDuplicates") && (p.object == ObjectId(a1) || p.object == ObjectId(a2)) && p.description.contains("ambiguous") && p.description.contains(QKeySequence(QStringLiteral("Ctrl+K")).toString(QKeySequence::NativeText)) && p.problemId.endsWith(QKeySequence(QStringLiteral("Ctrl+K")).toString(QKeySequence::PortableText)); } )); } #endif }; } QTEST_MAIN(ProblemReporterTest) #include "problemreportertest.moc" gammaray-2.11.3/tests/propertyadaptortest.cpp000066400000000000000000000262421413310443000214000ustar00rootroot00000000000000/* propertyadaptortest.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include Q_DECLARE_METATYPE(QVector) Q_DECLARE_METATYPE(QPen *) using namespace GammaRay; class PropertyAdaptorTest : public QObject { Q_OBJECT private: void testProperty(PropertyAdaptor *adaptor, const char *name, const char *typeName, const char *className, PropertyData::AccessFlags flags) { for (int i = 0; i < adaptor->count(); ++i) { auto prop = adaptor->propertyData(i); if (prop.name() != QLatin1String(name)) continue; QCOMPARE(prop.typeName(), QString(typeName)); QCOMPARE(prop.className(), QString(className)); QCOMPARE(prop.accessFlags(), flags); return; } qDebug() << name; QVERIFY(!"property not found"); } void verifyPropertyData(PropertyAdaptor *adaptor) { for (int i = 0; i < adaptor->count(); ++i) { auto data = adaptor->propertyData(i); QVERIFY(!data.name().isEmpty()); QVERIFY(!data.typeName().isEmpty()); QVERIFY(!data.className().isEmpty()); } } int indexOfProperty(PropertyAdaptor *adaptor, const char *name) { for (int i = 0; i < adaptor->count(); ++i) { auto prop = adaptor->propertyData(i); if (prop.name() == QLatin1String(name)) return i; } return -1; } private slots: void initTestCase() { MetaObject *mo; MO_ADD_METAOBJECT0(QPen); MO_ADD_PROPERTY(QPen, color, setColor); MO_ADD_PROPERTY(QPen, width, setWidth); } void testQtGadget() { Gadget gadget; auto adaptor = PropertyAdaptorFactory::create(ObjectInstance(&gadget, &Gadget::staticMetaObject), this); QVERIFY(adaptor); QCOMPARE(adaptor->count(), 1); verifyPropertyData(adaptor); testProperty(adaptor, "prop1", "int", "Gadget", PropertyData::Writable | PropertyData::Resettable); QVERIFY(!adaptor->canAddProperty()); QSignalSpy spy(adaptor, SIGNAL(propertyChanged(int,int))); QVERIFY(spy.isValid()); QCOMPARE(adaptor->propertyData(0).value(), QVariant(42)); adaptor->writeProperty(0, 23); QCOMPARE(adaptor->propertyData(0).value(), QVariant(23)); QCOMPARE(spy.size(), 1); QCOMPARE(spy.at(0).at(0).toInt(), 0); QCOMPARE(spy.at(0).at(1).toInt(), 0); adaptor->resetProperty(0); QCOMPARE(adaptor->propertyData(0).value(), QVariant(5)); QCOMPARE(spy.size(), 2); } void testROMetaObject() { PropertyTestObject obj; auto adaptor = PropertyAdaptorFactory::create(ObjectInstance(&obj, "QObject"), this); QVERIFY(adaptor); QCOMPARE(adaptor->count(), 4); verifyPropertyData(adaptor); testProperty(adaptor, "parent", "QObject*", "QObject", PropertyData::Readable); testProperty(adaptor, "thread", "QThread*", "QObject", PropertyData::Readable); QVERIFY(!adaptor->canAddProperty()); } void testRWMetaObject() { QThread obj; auto adaptor = PropertyAdaptorFactory::create(&obj, this); QVERIFY(adaptor); QVERIFY(adaptor->count() > 3); verifyPropertyData(adaptor); QSignalSpy spy(adaptor, SIGNAL(propertyChanged(int,int))); QVERIFY(spy.isValid()); auto idx = indexOfProperty(adaptor, "priority"); QCOMPARE(adaptor->propertyData(idx).accessFlags(), PropertyData::Writable); adaptor->writeProperty(idx, QThread::LowPriority); QCOMPARE(spy.size(), 1); QCOMPARE(spy.at(0).at(0).toInt(), idx); QCOMPARE(spy.at(0).at(1).toInt(), idx); adaptor->writeProperty(idx, QThread::NormalPriority); QCOMPARE(spy.size(), 2); QCOMPARE(spy.at(1).at(0).toInt(), idx); QCOMPARE(spy.at(1).at(1).toInt(), idx); } void testSequentialContainer() { auto v = QVector() << 2 << 3 << 5 << 12; auto adaptor = PropertyAdaptorFactory::create(ObjectInstance(QVariant::fromValue(v)), this); QVERIFY(adaptor); QCOMPARE(adaptor->count(), 4); verifyPropertyData(adaptor); testProperty(adaptor, "0", "int", "QVector", PropertyData::Readable); testProperty(adaptor, "3", "int", "QVector", PropertyData::Readable); QVERIFY(!adaptor->canAddProperty()); } void testAssociativeContainer() { QHash h; h["A"] = 2; h["B"] = 3; h["C"] = 5; auto adaptor = PropertyAdaptorFactory::create(ObjectInstance(QVariant::fromValue(h)), this); QVERIFY(adaptor); QCOMPARE(adaptor->count(), 3); verifyPropertyData(adaptor); testProperty(adaptor, "A", "int", "QHash", PropertyData::Readable); testProperty(adaptor, "C", "int", "QHash", PropertyData::Readable); QVERIFY(!adaptor->canAddProperty()); } void testQtObject() { auto obj = new PropertyTestObject; obj->setProperty("dynamicProperty", 5); auto adaptor = PropertyAdaptorFactory::create(ObjectInstance(obj), this); QVERIFY(adaptor); QVERIFY(adaptor->count() > 9); verifyPropertyData(adaptor); testProperty(adaptor, "signalsBlocked", "bool", "QObject", PropertyData::Readable); testProperty(adaptor, "intProp", "int", "PropertyTestObject", PropertyData::Writable); testProperty(adaptor, "readOnlyProp", "int", "PropertyTestObject", PropertyData::Resettable); testProperty(adaptor, "dynamicProperty", "int", "", PropertyData::Writable | PropertyData::Deletable); QSignalSpy changeSpy(adaptor, SIGNAL(propertyChanged(int,int))); QVERIFY(changeSpy.isValid()); auto propIdx = indexOfProperty(adaptor, "intProp"); QVERIFY(propIdx >= 0); QCOMPARE(adaptor->propertyData(propIdx).revision(), 0); QCOMPARE(adaptor->propertyData(propIdx).notifySignal(), QLatin1String("void intPropChanged()")); QCOMPARE(adaptor->propertyData(propIdx).propertyFlags(), PropertyModel::Writable | PropertyModel::Designable | PropertyModel::Stored | PropertyModel::Scriptable); QCOMPARE(adaptor->propertyData(propIdx).value(), QVariant(0)); adaptor->writeProperty(propIdx, 2); QCOMPARE(adaptor->propertyData(propIdx).value(), QVariant(2)); QCOMPARE(changeSpy.size(), 1); QCOMPARE(changeSpy.at(0).at(0).toInt(), propIdx); QCOMPARE(changeSpy.at(0).at(1).toInt(), propIdx); obj->setIntProp(5); QCOMPARE(changeSpy.size(), 2); QCOMPARE(changeSpy.at(1).at(0).toInt(), propIdx); QCOMPARE(changeSpy.at(1).at(1).toInt(), propIdx); QCOMPARE(adaptor->propertyData(propIdx).value(), QVariant(5)); propIdx = indexOfProperty(adaptor, "readOnlyProp"); QVERIFY(propIdx >= 0); QCOMPARE(adaptor->propertyData(propIdx).revision(), 0); QCOMPARE(adaptor->propertyData(propIdx).notifySignal(), QString()); QCOMPARE(adaptor->propertyData(propIdx).propertyFlags(), PropertyModel::Resetable | PropertyModel::Designable | PropertyModel::Stored | PropertyModel::Scriptable); adaptor->resetProperty(propIdx); QCOMPARE(obj->intProp(), 5); QVERIFY(changeSpy.size() >= 3); propIdx = indexOfProperty(adaptor, "dynamicProperty"); QVERIFY(propIdx >= 0); changeSpy.clear(); adaptor->writeProperty(propIdx, 12); QCOMPARE(changeSpy.size(), 1); QCOMPARE(changeSpy.at(0).at(0).toInt(), propIdx); QCOMPARE(changeSpy.at(0).at(1).toInt(), propIdx); QCOMPARE(obj->property("dynamicProperty").toInt(), 12); QSignalSpy addSpy(adaptor, SIGNAL(propertyAdded(int,int))); QVERIFY(addSpy.isValid()); QSignalSpy removeSpy(adaptor, SIGNAL(propertyRemoved(int,int))); QVERIFY(removeSpy.isValid()); QVERIFY(adaptor->canAddProperty()); PropertyData newProp; newProp.setName(QStringLiteral("newProperty")); newProp.setValue(QStringLiteral("value")); auto oldPropCount = adaptor->count(); adaptor->addProperty(newProp); QCOMPARE(oldPropCount + 1, adaptor->count()); QCOMPARE(addSpy.size(), 1); propIdx = indexOfProperty(adaptor, "newProperty"); QVERIFY(propIdx >= 0); adaptor->writeProperty(propIdx, QVariant()); QCOMPARE(oldPropCount, adaptor->count()); QCOMPARE(removeSpy.size(), 1); QCOMPARE(addSpy.size(), 1); QSignalSpy invalidatedSpy(adaptor, SIGNAL(objectInvalidated())); QVERIFY(invalidatedSpy.isValid()); delete obj; QVERIFY(!invalidatedSpy.isEmpty()); } void testQtMetaObject() { auto adaptor = PropertyAdaptorFactory::create(ObjectInstance(nullptr, &PropertyTestObject::staticMetaObject), this); QVERIFY(adaptor); QVERIFY(adaptor->count() >= 5); verifyPropertyData(adaptor); } void testVariant() { QPen pen(Qt::red); auto valuePen = QVariant::fromValue(pen); auto pointerPen = QVariant::fromValue(&pen); auto adaptor = PropertyAdaptorFactory::create(ObjectInstance(valuePen), this); QVERIFY(adaptor); QVERIFY(adaptor->count() >= 2); verifyPropertyData(adaptor); adaptor = PropertyAdaptorFactory::create(ObjectInstance(pointerPen), this); QVERIFY(adaptor); QVERIFY(adaptor->count() >= 2); verifyPropertyData(adaptor); } }; QTEST_MAIN(PropertyAdaptorTest) #include "propertyadaptortest.moc" gammaray-2.11.3/tests/propertybindertest.cpp000066400000000000000000000065541413310443000212150ustar00rootroot00000000000000/* propertybindertest.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include #include class MyObject : public QObject { Q_PROPERTY(int intProp READ intProp WRITE setIntProp NOTIFY intPropChanged) Q_PROPERTY(int intProp2 READ intProp2 WRITE setIntProp2 NOTIFY intProp2Changed) Q_OBJECT public: explicit MyObject(QObject *parent = nullptr) : QObject(parent) {} int intProp() { return p1; } int intProp2() { return p2; } void setIntProp(int i) { if (p1 == i) return; p1 = i; emit intPropChanged(); } void setIntProp2(int i) { if (p2 == i) return; p2 = i; emit intProp2Changed(); } signals: void intPropChanged(); void intProp2Changed(); private: int p1 = 0, p2 = 23; }; using namespace GammaRay; class PropertyBinderTest : public QObject { Q_OBJECT private slots: void testBinding() { auto *obj1 = new MyObject(this); auto *obj2 = new MyObject(this); new PropertyBinder(obj1, "intProp", obj2, "intProp"); obj1->setIntProp(5); QCOMPARE(obj2->intProp(), 5); obj2->setIntProp(9); QCOMPARE(obj1->intProp(), 9); delete obj2; obj1->setIntProp(42); // don't crash } void testInitialBinding() { auto *obj1 = new MyObject(this); obj1->setIntProp(18); auto *obj2 = new MyObject(this); QVERIFY(obj1->intProp() != obj2->intProp()); new PropertyBinder(obj1, "intProp", obj2, "intProp"); QCOMPARE(obj2->intProp(), 18); } void testMultiBinding() { auto *obj1 = new MyObject(this); obj1->setIntProp(18); obj1->setIntProp2(133); auto *obj2 = new MyObject(this); auto binder = new PropertyBinder(obj1, obj2); binder->add("intProp", "intProp"); binder->add("intProp2", "intProp2"); QVERIFY(obj1->intProp() != obj2->intProp()); QVERIFY(obj1->intProp2() != obj2->intProp2()); binder->syncSourceToDestination(); QCOMPARE(obj2->intProp(), 18); QCOMPARE(obj2->intProp2(), 133); obj2->setIntProp(23); QCOMPARE(obj1->intProp(), 23); } }; QTEST_MAIN(PropertyBinderTest) #include "propertybindertest.moc" gammaray-2.11.3/tests/propertymodeltest.cpp000066400000000000000000000131351413310443000210430ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "testhelpers.h" #include "baseprobetest.h" #include #include #include "shared/propertytestobject.h" #include <3rdparty/qt/modeltest.h> #include #include #include #include using namespace GammaRay; using namespace TestHelpers; class PropertyModelTest : public BaseProbeTest { Q_OBJECT private slots: void initTestCase() { createProbe(); } void testPropertyModel() { PropertyTestObject obj; obj.setProperty("dynamicProperty", 5); AggregatedPropertyModel model; ModelTest modelTest(&model); model.setObject(&obj); QVERIFY(model.rowCount() > 9); auto dynRow = searchFixedIndex(&model, "dynamicProperty"); QVERIFY(dynRow.isValid()); QCOMPARE(dynRow.data(Qt::DisplayRole).toString(), QStringLiteral("dynamicProperty")); QVERIFY(dynRow.sibling(dynRow.row(), 1).flags() & Qt::ItemIsEditable); QCOMPARE(dynRow.sibling(dynRow.row(), 1).data(Qt::DisplayRole).toString(), QStringLiteral("5")); QCOMPARE(dynRow.sibling(dynRow.row(), 1).data(Qt::EditRole), QVariant(5)); auto qmoRow = searchFixedIndex(&model, "intProp"); QVERIFY(qmoRow.isValid()); QCOMPARE(qmoRow.data(Qt::DisplayRole).toString(), QStringLiteral("intProp")); auto qmoRow2 = qmoRow.sibling(qmoRow.row(), 1); QVERIFY(qmoRow2.flags() & Qt::ItemIsEditable); QCOMPARE(qmoRow2.data(Qt::DisplayRole).toString(), QStringLiteral("0")); QCOMPARE(qmoRow2.data(Qt::EditRole), QVariant(0)); model.setData(qmoRow2, 12); QCOMPARE(obj.intProp(), 12); auto moRow = searchFixedIndex(&model, "thread"); QVERIFY(moRow.isValid()); QCOMPARE(moRow.data(Qt::DisplayRole).toString(), QStringLiteral("thread")); QVERIFY((moRow.sibling(moRow.row(), 1).flags() & Qt::ItemIsEditable) == 0); QVERIFY(!moRow.sibling(moRow.row(), 1).data(Qt::DisplayRole).toString().isEmpty()); } void testMetaObject() { AggregatedPropertyModel model; model.setObject(ObjectInstance(nullptr, &Gadget::staticMetaObject)); ModelTest modelTest(&model); QCOMPARE(model.rowCount(), 1); auto qmoRow = searchFixedIndex(&model, "prop1"); QVERIFY(qmoRow.isValid()); } void testChangeNotification() { ChangingPropertyObject obj; AggregatedPropertyModel model; // ModelTest modelTest(&model); model.setObject(&obj); QVERIFY(model.rowCount() >= 4); QSignalSpy changeSpy(&model, SIGNAL(dataChanged(QModelIndex,QModelIndex))); QVERIFY(changeSpy.isValid()); QSignalSpy addSpy(&model, SIGNAL(rowsInserted(QModelIndex,int,int))); QVERIFY(addSpy.isValid()); QSignalSpy removeSpy(&model, SIGNAL(rowsRemoved(QModelIndex,int,int))); QVERIFY(removeSpy.isValid()); obj.changeProperties(); QCOMPARE(changeSpy.size(), 1); QCOMPARE(addSpy.size(), 1); obj.changeProperties(); QCOMPARE(changeSpy.size(), 3); obj.setProperty("dynamicChangingProperty", QVariant()); QCOMPARE(changeSpy.size(), 3); QCOMPARE(addSpy.size(), 1); QCOMPARE(removeSpy.size(), 1); } void testGadgetRO() { PropertyTestObject obj; AggregatedPropertyModel model; model.setObject(ObjectInstance(&obj)); auto idx = searchFixedIndex(&model, "gadgetReadOnly"); QVERIFY(idx.isValid()); QCOMPARE(model.rowCount(idx), 1); idx = idx.child(0, 1); QVERIFY((idx.flags() & Qt::ItemIsEditable) == 0); } void testGadgetRW() { PropertyTestObject obj; AggregatedPropertyModel model; model.setObject(ObjectInstance(&obj)); ModelTest modelTest(&model); auto idx = searchFixedIndex(&model, "gadget"); QVERIFY(idx.isValid()); QCOMPARE(model.rowCount(idx), 1); idx = idx.child(0, 1); QVERIFY(idx.flags() & Qt::ItemIsEditable); QVERIFY(model.setData(idx, 1554)); QCOMPARE(obj.gadgetPointer()->prop1(), 1554); idx = searchFixedIndex(&model, "gadgetPointer"); QVERIFY(idx.isValid()); QCOMPARE(model.rowCount(idx), 1); idx = idx.child(0, 1); QVERIFY(idx.flags() & Qt::ItemIsEditable); QVERIFY(model.setData(idx, 1559)); QCOMPARE(obj.gadgetPointer()->prop1(), 1559); } }; QTEST_MAIN(PropertyModelTest) #include "propertymodeltest.moc" gammaray-2.11.3/tests/propertysyncertest.cpp000066400000000000000000000106031413310443000212430ustar00rootroot00000000000000/* propertysyncertest.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include #include #include #include using namespace GammaRay; class MyObject : public QObject { Q_PROPERTY(int intProp READ intProp WRITE setIntProp NOTIFY intPropChanged) Q_OBJECT public: explicit MyObject(QObject *parent = nullptr) : QObject(parent) {} int intProp() { return p1; } void setIntProp(int i) { if (p1 == i) return; p1 = i; emit intPropChanged(); } signals: void intPropChanged(); private: int p1 = 0; }; class PropertySyncerTest : public QObject { Q_OBJECT public: explicit PropertySyncerTest(QObject *parent = nullptr) : QObject(parent) { } public slots: void server2client(const GammaRay::Message &msg) { ++m_server2ClientCount; if (!m_client) return; QByteArray ba; QBuffer buffer(&ba); buffer.open(QIODevice::ReadWrite); msg.write(&buffer); buffer.seek(0); m_client->handleMessage(Message::readMessage(&buffer)); } void client2server(const GammaRay::Message &msg) { ++m_client2ServerCount; Q_ASSERT(m_server); QByteArray ba; QBuffer buffer(&ba); buffer.open(QIODevice::ReadWrite); msg.write(&buffer); buffer.seek(0); m_server->handleMessage(Message::readMessage(&buffer)); } private slots: void testSync() { // server setup MyObject serverObj; serverObj.setIntProp(14); m_server = new PropertySyncer(this); connect(m_server, &PropertySyncer::message, this, &PropertySyncerTest::server2client); m_server->setAddress(1); m_server->addObject(42, &serverObj); // client setup auto *clientObj = new MyObject(this); m_client = new PropertySyncer(this); m_client->setRequestInitialSync(true); connect(m_client, &PropertySyncer::message, this, &PropertySyncerTest::client2server); m_client->setAddress(1); m_client->addObject(42, clientObj); m_server->setObjectEnabled(42, true); QCOMPARE(clientObj->intProp(), 0); QCOMPARE(m_client2ServerCount, 0); QCOMPARE(m_server2ClientCount, 0); // initial sync request m_client->setObjectEnabled(42, true); QCOMPARE(m_client2ServerCount, 1); QCOMPARE(m_server2ClientCount, 1); QCOMPARE(clientObj->intProp(), 14); // regular sync on changes on one side serverObj.setIntProp(42); QCOMPARE(m_server2ClientCount, 2); QCOMPARE(clientObj->intProp(), 42); QCOMPARE(m_client2ServerCount, 1); clientObj->setIntProp(23); QCOMPARE(serverObj.intProp(), 23); QCOMPARE(m_client2ServerCount, 2); QCOMPARE(m_server2ClientCount, 2); // client destroyed m_server->setObjectEnabled(42, false); delete clientObj; serverObj.setIntProp(26); QCOMPARE(m_server2ClientCount, 2); } private: int m_server2ClientCount = 0, m_client2ServerCount = 0; PropertySyncer *m_client = nullptr; PropertySyncer *m_server = nullptr; }; QTEST_MAIN(PropertySyncerTest) #include "propertysyncertest.moc" gammaray-2.11.3/tests/qmetaobjectvalidatortest.cpp000066400000000000000000000104721413310443000223430ustar00rootroot00000000000000/* qmetaobjectvalidatortest.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include #include using namespace GammaRay; struct UnknownCustomType {}; struct KnownCustomType {}; Q_DECLARE_METATYPE(KnownCustomType) class QMetaObjectValidatorTest : public QObject { Q_OBJECT Q_PROPERTY(UnknownCustomType failUnknownType READ failUnknownType) Q_PROPERTY(KnownCustomType knownType READ knownType) public: UnknownCustomType failUnknownType() const { return {}; } KnownCustomType knownType() const { return {}; } signals: void destroyed(); public slots: void unknownParameter(int, UnknownCustomType, int) {} void knownParameter(KnownCustomType) {} private slots: void testSignalOverride() { for (int i = staticMetaObject.methodOffset(); i < staticMetaObject.methodCount(); ++i) { const auto method = staticMetaObject.method(i); if (method.methodType() == QMetaMethod::Signal) QCOMPARE(QMetaObjectValidator::checkMethod(&staticMetaObject, method), QMetaObjectValidatorResult::SignalOverride); else QVERIFY((QMetaObjectValidator::checkMethod(&staticMetaObject, method) & QMetaObjectValidatorResult::SignalOverride) == 0); } for (int i = QObject::staticMetaObject.methodOffset(); i < QObject::staticMetaObject.methodCount(); ++i) { const auto method = QObject::staticMetaObject.method(i); QVERIFY((QMetaObjectValidator::checkMethod(&QObject::staticMetaObject, method) & QMetaObjectValidatorResult::SignalOverride) == 0); } } void testParameterTypes() { for (int i = staticMetaObject.methodOffset(); i < staticMetaObject.methodCount(); ++i) { const auto method = staticMetaObject.method(i); if (method.name().startsWith("unknown")) //krazy:exclude=strings QVERIFY(QMetaObjectValidator::checkMethod(&staticMetaObject, method) & QMetaObjectValidatorResult::UnknownMethodParameterType); else QVERIFY((QMetaObjectValidator::checkMethod(&staticMetaObject, method) & QMetaObjectValidatorResult::UnknownMethodParameterType) == 0); } } void testPropertyType() { for (int i = staticMetaObject.propertyOffset(); i < staticMetaObject.propertyCount(); ++i) { const auto property = staticMetaObject.property(i); if (strstr(property.name(), "fail") == property.name()) QVERIFY(QMetaObjectValidator::checkProperty(&staticMetaObject, property) & QMetaObjectValidatorResult::UnknownPropertyType); else QVERIFY((QMetaObjectValidator::checkProperty(&staticMetaObject, property) & QMetaObjectValidatorResult::UnknownPropertyType) == 0); } } void testObject() { QCOMPARE(QMetaObjectValidator::check(&staticMetaObject), QMetaObjectValidatorResult::SignalOverride | QMetaObjectValidatorResult::UnknownMethodParameterType | QMetaObjectValidatorResult::UnknownPropertyType); QCOMPARE(QMetaObjectValidator::check(&QObject::staticMetaObject), QMetaObjectValidatorResult::NoIssue); } }; QTEST_MAIN(QMetaObjectValidatorTest) #include "qmetaobjectvalidatortest.moc" gammaray-2.11.3/tests/qmlsupporttest.cpp000066400000000000000000000140141413310443000203610ustar00rootroot00000000000000/* qmlsupporttest.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace GammaRay; class QmlSupportTest : public QObject { Q_OBJECT private: int indexOfProperty(PropertyAdaptor *adaptor, const char *name) { for (int i = 0; i < adaptor->count(); ++i) { auto prop = adaptor->propertyData(i); if (prop.name() == QLatin1String(name)) return i; } return -1; } private slots: void initTestCase() { PropertyAdaptorFactory::registerFactory(QmlListPropertyAdaptorFactory::instance()); PropertyAdaptorFactory::registerFactory(QmlAttachedPropertyAdaptorFactory::instance()); PropertyAdaptorFactory::registerFactory(QJSValuePropertyAdaptorFactory::instance()); PropertyAdaptorFactory::registerFactory(QmlContextPropertyAdaptorFactory::instance()); } void testQmlListProperty() { QQmlEngine engine; QQmlComponent component(&engine); component.setData("import QtQuick 2.0\nRectangle { Text { text: \"Hello world!\" } }", QUrl()); auto obj = component.create(); QVERIFY(obj); auto adaptor = PropertyAdaptorFactory::create(obj, this); QVERIFY(adaptor); QVERIFY(adaptor->count() > 20); auto idx = indexOfProperty(adaptor, "data"); QVERIFY(idx >= 0); auto pd = adaptor->propertyData(idx); QCOMPARE(pd.className(), QStringLiteral("QQuickItem")); auto listAdaptor = PropertyAdaptorFactory::create(pd.value(), this); QVERIFY(listAdaptor); QCOMPARE(listAdaptor->count(), 1); auto data = listAdaptor->propertyData(0); QVERIFY(!data.name().isEmpty()); QVERIFY(data.value().canConvert()); QVERIFY(!data.typeName().isEmpty()); QVERIFY(!data.className().isEmpty()); delete obj; } void testAttachedProperty() { QQmlEngine engine; QQmlComponent component(&engine); component.setData("import QtQuick 2.0\nRectangle { Keys.enabled: true }", QUrl()); auto obj = component.create(); QVERIFY(obj); auto adaptor = PropertyAdaptorFactory::create(obj, this); QVERIFY(adaptor); QVERIFY(adaptor->count() > 20); auto idx = indexOfProperty(adaptor, "Keys"); QVERIFY(idx >= 0); auto data = adaptor->propertyData(idx); QCOMPARE(data.name(), QStringLiteral("Keys")); QVERIFY(!data.typeName().isEmpty()); QVERIFY(data.value().isValid()); QVERIFY(data.value().canConvert()); QVERIFY(!data.className().isEmpty()); delete obj; } void testJSValue() { QQmlEngine engine; QQmlComponent component(&engine); component.setData( "import QtQuick 2.0\nRectangle { property var a1: []; property var a2: [\"hello\", \"world\"] }", QUrl()); auto obj = component.create(); QVERIFY(obj); auto adaptor = PropertyAdaptorFactory::create(obj, this); QVERIFY(adaptor); auto idx = indexOfProperty(adaptor, "a1"); QVERIFY(idx >= 0); auto data = adaptor->propertyData(idx); auto jsValueAdaptor = PropertyAdaptorFactory::create(data.value(), this); QVERIFY(jsValueAdaptor); QCOMPARE(jsValueAdaptor->count(), 0); idx = indexOfProperty(adaptor, "a2"); QVERIFY(idx >= 0); data = adaptor->propertyData(idx); jsValueAdaptor = PropertyAdaptorFactory::create(data.value(), this); QVERIFY(jsValueAdaptor); QCOMPARE(jsValueAdaptor->count(), 2); data = jsValueAdaptor->propertyData(1); QCOMPARE(data.name(), QStringLiteral("1")); QCOMPARE(data.value(), QVariant("world")); delete obj; } void testContextProperty() { QQmlEngine engine; engine.rootContext()->setContextProperty("myContextProp", 42); auto adaptor = PropertyAdaptorFactory::create(engine.rootContext(), this); QVERIFY(adaptor); auto idx = indexOfProperty(adaptor, "myContextProp"); QVERIFY(idx >= 0); auto data = adaptor->propertyData(idx); QCOMPARE(data.name(), QStringLiteral("myContextProp")); QCOMPARE(data.value().toInt(), 42); adaptor->writeProperty(idx, 23); QCOMPARE(engine.rootContext()->contextProperty("myContextProp").toInt(), 23); } }; QTEST_MAIN(QmlSupportTest) #include "qmlsupporttest.moc" gammaray-2.11.3/tests/qtivipropertymodeltest.cpp000066400000000000000000000405361413310443000221250ustar00rootroot00000000000000/* qtivipropertymodeltest.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Filipe Azevedo Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "baseprobetest.h" #include "testhelpers.h" #include #include #include #include <3rdparty/qt/modeltest.h> #include #include #include #if defined(QT_IVIMEDIA_LIB) #include #endif #include //#define ENABLE_LOG using namespace GammaRay; using namespace TestHelpers; class QtIviPropertyModelTest : public BaseProbeTest { Q_OBJECT QIviClimateControl *m_climate; #if defined(QT_IVIMEDIA_LIB) QIviAmFmTuner *m_amfm; #endif QAbstractItemModel *m_model; typedef QSharedPointer IviCarrierProperty; public: explicit QtIviPropertyModelTest(QObject *parent = nullptr) : BaseProbeTest(parent) , m_climate(nullptr) #if defined(QT_IVIMEDIA_LIB) , m_amfm(nullptr) #endif , m_model(nullptr) { } private: void createIviProperties() { m_climate = new QIviClimateControl(QString(), this); QVERIFY(m_climate); m_climate->setDiscoveryMode(QIviAbstractZonedFeature::LoadOnlySimulationBackends); QVERIFY(m_climate->startAutoDiscovery() == QIviAbstractFeature::SimulationBackendLoaded); QVERIFY(m_climate->isValid()); #if defined(QT_IVIMEDIA_LIB) m_amfm = new QIviAmFmTuner(this); QVERIFY(m_amfm); m_amfm->setDiscoveryMode(QIviAbstractZonedFeature::LoadOnlySimulationBackends); QVERIFY(m_amfm->startAutoDiscovery() == QIviAbstractFeature::SimulationBackendLoaded); QVERIFY(m_amfm->isValid()); #endif } QModelIndex carrierIndex(QObject *carrier, int column) const { const auto index(m_model->match(m_model->index(0, 0), ObjectModel::ObjectIdRole, QVariant::fromValue(ObjectId(carrier)), 1, Qt::MatchFlags(Qt::MatchExactly | Qt::MatchWrap)).value(0)); return index.sibling(index.row(), column); } QModelIndex carrierPropertyIndex(QObject *carrier, const QString &property, int column) const { const QModelIndex parent(carrierIndex(carrier, 0)); const auto index(parent.isValid() ? m_model->match(m_model->index(0, 0, parent), Qt::DisplayRole, property, 1, Qt::MatchFlags(Qt::MatchExactly | Qt::MatchWrap)).value(0) : QModelIndex()); return index.sibling(index.row(), column); } QVariant carrierPropertyData(QObject *carrier, const QString &property, int column, int role) const { return carrierPropertyIndex(carrier, property, column).data(role); } QVariant carrierPropertyValue(QObject *carrier, const QString &property) const { return carrierPropertyData(carrier, property, QtIviPropertyModel::ValueColumn, QtIviPropertyModel::RawValue); } QModelIndex carrierPropertyIndex(QIviProperty *property, int column) const { const auto index(m_model->match(m_model->index(0, 0), ObjectModel::ObjectIdRole, QVariant::fromValue(ObjectId(property)), 1, Qt::MatchFlags(Qt::MatchExactly | Qt::MatchWrap | Qt::MatchRecursive)) .value(0)); return index.sibling(index.row(), column); } QVariant carrierPropertyData(QIviProperty *property, int column, int role) const { return carrierPropertyIndex(property, column).data(role); } QVariant carrierPropertyValue(QIviProperty *property) const { return carrierPropertyData(property, QtIviPropertyModel::ValueColumn, QtIviPropertyModel::RawValue); } IviCarrierProperty iviCarrierProperty(QObject *carrier, const QString &property) const { if (!carrier) return IviCarrierProperty::create(); const QMetaObject *mo(carrier->metaObject()); const int index(mo->indexOfProperty(qPrintable(property))); if (index == -1) return IviCarrierProperty::create(); const QMetaProperty metaProperty(mo->property(index)); if (metaProperty.userType() == qMetaTypeId()) { return IviCarrierProperty::create(metaProperty.read(carrier).value(), metaProperty); } return IviCarrierProperty::create(metaProperty); } // Check ivi object properties vs model properties bool testIviObject(QObject *iviObject) const { #if defined(ENABLE_LOG) qWarning() << "Testing ivi object: " << iviObject; #endif QVERIFY_RETURN_FALSE(iviObject); QVector iviPropertyNames; QMap iviProperties; const QMetaObject *iviMo(iviObject->metaObject()); int propertyOffset = -1; if (qobject_cast(iviObject)) propertyOffset = QIviServiceObject::staticMetaObject.propertyOffset(); else if (qobject_cast(iviObject)) propertyOffset = QIviAbstractFeature::staticMetaObject.propertyOffset(); QVERIFY_RETURN_FALSE(propertyOffset != -1); for (int i = propertyOffset; i < iviMo->propertyCount(); ++i) { const QMetaProperty property(iviMo->property(i)); iviPropertyNames << QLatin1String(property.name()); QVERIFY_RETURN_FALSE(!iviPropertyNames.last().isEmpty()); if (property.userType() == qMetaTypeId()) { QIviProperty *ivi = property.read(iviObject).value(); QVERIFY_RETURN_FALSE(ivi); iviProperties[iviPropertyNames.last()] = ivi->value(); } else { iviProperties[iviPropertyNames.last()] = property.read(iviObject); } } std::stable_sort(iviPropertyNames.begin(), iviPropertyNames.end()); QVERIFY_RETURN_FALSE(!iviPropertyNames.isEmpty()); QVERIFY_RETURN_FALSE(!iviProperties.isEmpty()); const QModelIndex carrierIndex(this->carrierIndex(iviObject, 0)); const int carrierPropertyCount(m_model->rowCount(carrierIndex)); QVERIFY_RETURN_FALSE(carrierIndex.isValid()); QCOMPARE_RETURN_FALSE(iviProperties.count(), carrierPropertyCount); QVector carrierModelPropertyNames; for (int i = 0; i < carrierPropertyCount; ++i) { const QModelIndex nameIndex(m_model->index(i, QtIviPropertyModel::NameColumn, carrierIndex)); carrierModelPropertyNames << nameIndex.data(Qt::DisplayRole).toString(); QVERIFY_RETURN_FALSE(!carrierModelPropertyNames.last().isEmpty()); QCOMPARE_RETURN_FALSE(iviProperties[carrierModelPropertyNames.last()], carrierPropertyValue(iviObject, carrierModelPropertyNames.last())); } std::stable_sort(carrierModelPropertyNames.begin(), carrierModelPropertyNames.end()); QCOMPARE_RETURN_FALSE(carrierModelPropertyNames.count(), iviPropertyNames.count()); QVERIFY_RETURN_FALSE(carrierModelPropertyNames == iviPropertyNames); if (QIviAbstractZonedFeature *zonedFeature = qobject_cast(iviObject)) { foreach (QIviAbstractZonedFeature *subZone, zonedFeature->zones()) { if (!testIviObject(subZone)) { return false; } } } return true; } private slots: void initTestCase() { createIviProperties(); createProbe(); m_model = ObjectBroker::model("com.kdab.GammaRay.PropertyModel"); QVERIFY(m_model); } void testModelContent() { QVERIFY(new ModelTest(m_model, this)); QVERIFY(testIviObject(m_climate)); #if defined(QT_IVIMEDIA_LIB) QVERIFY(testIviObject(m_amfm)); #endif } void testModelContentChanges_data() { QTest::addColumn("carrier"); QTest::addColumn("property"); QTest::addColumn("override"); QTest::addColumn("value"); QTest::addColumn("expect"); QTest::addColumn("invalidProperty"); // Invalid value mean nothing to set // Invalid expect mean use value QTest::newRow("climate-default-climateMode") << (QObject *)m_climate << "climateMode" << true << QVariant() << QVariant::fromValue(QIviClimateControl::ClimateOn) << false ; QTest::newRow("climate-climateMode-Off") << (QObject *)m_climate << "climateMode" << false << QVariant::fromValue(QIviClimateControl::ClimateOff) << QVariant() << false ; QTest::newRow("climate-airflowDirections-Floor") << (QObject *)m_climate << "airflowDirections" << false << QVariant::fromValue(QIviClimateControl::AirflowDirections(QIviClimateControl::Floor)) << QVariant() << false ; QTest::newRow("climate-airflowDirections-Floor|Dashboard") << (QObject *)m_climate << "airflowDirections" << false << QVariant::fromValue(QIviClimateControl::AirflowDirections(QIviClimateControl::Floor | QIviClimateControl::Dashboard)) << QVariant() << false ; QTest::newRow("climate-FrontLeft-targetTemperature-25") << (QObject *)m_climate->zoneAt("FrontLeft") << "targetTemperature" << false << QVariant(25) << QVariant() << false ; QTest::newRow("climate-FrontRight-seatHeater-8") << (QObject *)m_climate->zoneAt("FrontRight") << "seatHeater" << false << QVariant(8) << QVariant() << false ; QTest::newRow("unknown-property") << (QObject *)nullptr << "hey" << false << QVariant(42) << QVariant() << true ; QTest::newRow("climate-unknown-property-override") << (QObject *)m_climate->zoneAt("FrontLeft") << "hey" << true << QVariant(42) << QVariant() << true ; QTest::newRow("climate-FrontLeft-heater-true(readonly)") << (QObject *)m_climate->zoneAt("FrontLeft") << "heater" << false << QVariant(true) << QVariant(false) << false ; #if defined(QT_IVIMEDIA_LIB) QTest::newRow("amfm-default-discoveryMode") << (QObject *)m_amfm << "discoveryMode" << false << QVariant() << QVariant::fromValue(QIviAbstractFeature::LoadOnlySimulationBackends) << false ; QTest::newRow("amfm-discoveryMode-AutoDiscovery") << (QObject *)m_amfm << "discoveryMode" << false << QVariant::fromValue(QIviAbstractFeature::AutoDiscovery) << QVariant() << false ; QTest::newRow("amfm-band-AMBand") << (QObject *)m_amfm << "band" << false << QVariant::fromValue(QIviAmFmTuner::AMBand) << QVariant() << false ; QTest::newRow("amfm-unknown-property-override") << (QObject *)m_amfm << "hey" << true << QVariant(42) << QVariant() << true ; QTest::newRow("amfm-scanRunning-true(readonly)") << (QObject *)m_amfm << "scanRunning" << false << QVariant(true) << QVariant(false) << false ; #endif } void testModelContentChanges() { QFETCH(QObject *, carrier); QFETCH(QString, property); QFETCH(bool, override); QFETCH(QVariant, value); QFETCH(QVariant, expect); QFETCH(bool, invalidProperty); const QVariant expectValue(expect.isValid() ? expect : value); const IviCarrierProperty iviProperty(iviCarrierProperty(carrier, property)); if (invalidProperty) { QEXPECT_FAIL("", "Expected not found property", Continue); } QVERIFY(iviProperty->isValid()); const QModelIndex valueIndex(carrierPropertyIndex(carrier, property, QtIviPropertyModel::ValueColumn)); if (iviProperty->m_iviProperty) { QCOMPARE(valueIndex, carrierPropertyIndex(iviProperty->m_iviProperty, valueIndex.column())); } QCOMPARE(valueIndex.isValid(), iviProperty->isValid()); QCOMPARE(!invalidProperty, iviProperty->isValid()); #if defined(ENABLE_LOG) qWarning("Original value: %s", invalidProperty ? "" : qPrintable(iviProperty->value().toString())); #endif const QVariant originalValue = iviProperty->cppValue(carrier); const QModelIndex overrideIndex(valueIndex.sibling(valueIndex.row(), QtIviPropertyModel::OverrideColumn)); #if defined(ENABLE_LOG) qWarning("Setting override: %i", (int)override); #endif const bool willOverride(iviProperty->isAvailable() && iviProperty->isOverridable() && (overrideIndex.data(Qt::CheckStateRole).value() == Qt::Checked) != override); QCOMPARE(m_model->setData(overrideIndex, override ? Qt::Checked : Qt::Unchecked, Qt::CheckStateRole), willOverride); QCOMPARE((overrideIndex.data(Qt::CheckStateRole).value() == Qt::Checked), (!iviProperty->isAvailable() ? false : override)); if (value.isValid()) { #if defined(ENABLE_LOG) qWarning("Setting value: %s", qPrintable(value.toString())); #endif const bool willWrite(iviProperty->isAvailable() && iviProperty->isWritable() && valueIndex.data(QtIviPropertyModel::RawValue) != value); QCOMPARE(m_model->setData(valueIndex, value, Qt::EditRole), willWrite); if (willWrite) { QCOMPARE(valueIndex.data(QtIviPropertyModel::RawValue), value); } } if (iviProperty->isValid()) { QCOMPARE(iviProperty->cppValue(carrier), iviProperty->isAvailable() && !override ? expectValue : originalValue); } QCOMPARE(iviProperty->isAvailable(), valueIndex.flags().testFlag(Qt::ItemIsEnabled)); QCOMPARE(iviProperty->isAvailable() && iviProperty->isWritable(), valueIndex.flags().testFlag(Qt::ItemIsEditable)); QCOMPARE(valueIndex.data(QtIviPropertyModel::RawValue), invalidProperty ? QVariant() : expectValue); } }; QTEST_MAIN(QtIviPropertyModelTest) #include "qtivipropertymodeltest.moc" gammaray-2.11.3/tests/quickinspectorbench.cpp000066400000000000000000000051051413310443000212770ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Kevin Funk Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include #include #include #include #include using namespace GammaRay; class QuickInspectorBench : public QObject { Q_OBJECT private slots: void initTestCase() { } void benchModelObjectAdded() { QQuickView view; auto root = view.contentItem(); QuickItemModel model; model.setWindow(&view); const auto items = createItems(root); QBENCHMARK_ONCE { for (auto item : items) { model.objectAdded(item); } } } void benchModelItemUpdated() { QQuickView view; auto root = view.contentItem(); QuickItemModel model; model.setWindow(&view); const auto items = createItems(root); for (auto item : items) { model.objectAdded(item); } QBENCHMARK_ONCE { for (auto item : items) { // trigger item update item->setX(item->x() + 1); } } } private: QVector createItems(QQuickItem* parent) { const int numberOfItems = 10000; QVector items; items.reserve(numberOfItems); for (int i = 0; i < numberOfItems; ++i) { items << new QQuickItem(parent); } return items; } }; QTEST_MAIN(QuickInspectorBench) #include "quickinspectorbench.moc" gammaray-2.11.3/tests/quickinspectorpickingtest.cpp000066400000000000000000000133511413310443000225460ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Christoph Sterz Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include #include #include #include #include #include #include #include #include #include <3rdparty/qt/modeltest.h> #include #include #include #include #include using namespace GammaRay; class QuickInspectorPickingTest : public QObject { Q_OBJECT private: void createProbe() { Paths::setRelativeRootPath(GAMMARAY_INVERSE_BIN_DIR); qputenv("GAMMARAY_ProbePath", Paths::probePath(GAMMARAY_PROBE_ABI).toUtf8()); qputenv("GAMMARAY_ServerAddress", GAMMARAY_DEFAULT_LOCAL_TCP_URL); Hooks::installHooks(); Probe::startupHookReceived(); new ProbeCreator(ProbeCreator::Create); QTest::qWait(1); // event loop re-entry } bool waitForSignal(QSignalSpy *spy, bool keepResult = false) { if (spy->isEmpty()) spy->wait(1000); bool result = !spy->isEmpty(); if (!keepResult) spy->clear(); return result; } bool showSource(const QString &sourceFile) { QSignalSpy renderSpy(view, SIGNAL(frameSwapped())); Q_ASSERT(renderSpy.isValid()); view->setSource(QUrl(sourceFile)); view->show(); exposed = QTest::qWaitForWindowExposed(view); if (!exposed) qWarning() << "Unable to expose window, probably running tests on a headless system - ignoring all following render failures."; // wait at least two frames so we have the final window size with all render loop/driver combinations... QTest::qWait(20); waitForSignal(&renderSpy); view->update(); return !exposed || waitForSignal(&renderSpy); } private slots: void initTestCase() { qRegisterMetaType(); } void init() { createProbe(); // we need one view for the plugin to activate, otherwise the model will not be available view = new QQuickView; view->setResizeMode(QQuickView::SizeViewToRootObject); QTest::qWait(1); // event loop re-entry itemModel = ObjectBroker::model(QStringLiteral("com.kdab.GammaRay.QuickItemModel")); QVERIFY(itemModel); inspector = ObjectBroker::object(); QVERIFY(inspector); inspector->selectWindow(0); QTest::qWait(1); } void cleanup() { delete view; QTest::qWait(1); } void testItemPicking_data() { QTest::addColumn("qmlFile", nullptr); QTest::addColumn("pickedObjectId", nullptr); QTest::newRow("Vanilla Rect-Clicking") << "qrc:/manual/picking/stackedrects.qml" << "bluerect"; QTest::newRow("Negative z-order") << "qrc:/manual/picking/negativezordering.qml" << "greenrect"; QTest::newRow("Invisible overlay") << "qrc:/manual/picking/invisibleoverlay.qml" << "redrect"; QTest::newRow("Opacity:0 overlay") << "qrc:/manual/picking/opacityzerooverlay.qml" << "yellowrect"; QTest::newRow("Loader") << "qrc:/manual/picking/loader.qml" << "bluerect"; QTest::newRow("Outside of parent") << "qrc:/manual/picking/outsideofparent.qml" << "redrectchild"; } // Info: Clickposition is always in Center of View void testItemPicking() { QFETCH(QString, qmlFile); QFETCH(QString, pickedObjectId); QVERIFY(showSource(qmlFile)); auto itemSelectionModel = ObjectBroker::selectionModel(itemModel); QVERIFY(itemSelectionModel); QSignalSpy itemSpy(itemSelectionModel, SIGNAL(selectionChanged(QItemSelection,QItemSelection))); QVERIFY(itemSpy.isValid()); // auto center-click is broken before https://codereview.qt-project.org/141085/ QTest::mouseClick(view, Qt::LeftButton, Qt::ShiftModifier | Qt::ControlModifier, QPoint(view->width()/2, view->height()/2)); if (itemSpy.isEmpty()) QVERIFY(itemSpy.wait()); QCOMPARE(itemSpy.size(), 1); QItemSelection selectedItem = qvariant_cast(itemSpy.at(0).at(0)); QVariant id = itemModel->data(selectedItem.indexes().first()); QCOMPARE(id.toString(), pickedObjectId); } private: QQuickView *view; QAbstractItemModel *itemModel; QuickInspectorInterface *inspector; bool exposed; }; QTEST_MAIN(QuickInspectorPickingTest) #include "quickinspectorpickingtest.moc" gammaray-2.11.3/tests/quickinspectortest.cpp000066400000000000000000000335121413310443000212020ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "basequicktest.h" #include "testhelpers.h" #include #include #include #include #include #include #include #include #include <3rdparty/qt/modeltest.h> #include #include #include #include #include using namespace GammaRay; using namespace TestHelpers; class QuickInspectorTest : public BaseQuickTest { Q_OBJECT protected: bool ignoreNonExposedView() const override { return true; } private: void triggerSceneChange() { QTest::keyClick(view(), Qt::Key_Right); QTest::qWait(20); QTest::keyClick(view(), Qt::Key_Left); QTest::qWait(20); QTest::keyClick(view(), Qt::Key_Right); } private slots: void initTestCase() { qRegisterMetaType(); } void init() override { BaseQuickTest::init(); itemModel = ObjectBroker::model(QStringLiteral("com.kdab.GammaRay.QuickItemModel")); QVERIFY(itemModel); new ModelTest(itemModel, view()); sgModel = ObjectBroker::model(QStringLiteral("com.kdab.GammaRay.QuickSceneGraphModel")); QVERIFY(sgModel); new ModelTest(sgModel, view()); inspector = ObjectBroker::object(); QVERIFY(inspector); inspector->setServerSideDecorationsEnabled(false); inspector->selectWindow(0); QTest::qWait(1); } void testModelsReparent() { QVERIFY(showSource(QStringLiteral("qrc:/manual/reparenttest.qml"))); QTest::keyClick(view(), Qt::Key_Right); QTest::qWait(20); QTest::keyClick(view(), Qt::Key_Left); QTest::qWait(20); QTest::keyClick(view(), Qt::Key_Right); QTest::qWait(20); } void testModelsCreateDestroy() { QVERIFY(showSource(QStringLiteral("qrc:/manual/quickitemcreatedestroytest.qml"))); // scroll through the list, to trigger creations/destructions for (int i = 0; i < 30; ++i) QTest::keyClick(view(), Qt::Key_Down); QTest::qWait(20); for (int i = 0; i < 30; ++i) QTest::keyClick(view(), Qt::Key_Up); QTest::qWait(20); } void testModelsCreateDestroyProxy() { QVERIFY(showSource(QStringLiteral("qrc:/manual/quickitemcreatedestroytest.qml"))); QVERIFY(itemModel->rowCount() > 0); QVERIFY(sgModel->rowCount() > 0); itemModel->setProperty("filterKeyColumn", -1); itemModel->setProperty("filterRegExp", QRegExp("Rect", Qt::CaseInsensitive, QRegExp::FixedString)); sgModel->setProperty("filterKeyColumn", -1); sgModel->setProperty("filterRegExp", QRegExp("Transform", Qt::CaseInsensitive, QRegExp::FixedString)); QVERIFY(itemModel->rowCount() > 0); QVERIFY(sgModel->rowCount() > 0); // scroll through the list, to trigger creations/destructions for (int i = 0; i < 30; ++i) QTest::keyClick(view(), Qt::Key_Down); QTest::qWait(20); itemModel->setProperty("filterRegExp", QRegExp()); sgModel->setProperty("filterRegExp", QRegExp()); QTest::qWait(20); } void testItemPicking() { QVERIFY(showSource(QStringLiteral("qrc:/manual/reparenttest.qml"))); ToolManagerInterface *toolManager = ObjectBroker::object(); QSignalSpy toolSpy(toolManager, SIGNAL(toolSelected(QString))); QVERIFY(toolSpy.isValid()); auto itemSelectionModel = ObjectBroker::selectionModel(itemModel); QVERIFY(itemSelectionModel); QSignalSpy itemSpy(itemSelectionModel, SIGNAL(selectionChanged(QItemSelection,QItemSelection))); QVERIFY(itemSpy.isValid()); auto sgSelectionModel = ObjectBroker::selectionModel(sgModel); QVERIFY(sgModel); QSignalSpy sgSpy(sgSelectionModel, SIGNAL(selectionChanged(QItemSelection,QItemSelection))); QVERIFY(sgSpy.isValid()); // auto center-click is broken before https://codereview.qt-project.org/141085/ QTest::mouseClick(view(), Qt::LeftButton, Qt::ShiftModifier | Qt::ControlModifier, QPoint(view()->width() / 2, view()->height() / 2)); QTest::qWait(20); QCOMPARE(toolSpy.size(), 1); QCOMPARE(itemSpy.size(), 1); if (!isViewExposed()) return; QCOMPARE(sgSpy.size(), 1); } void testFetchingPreview() { auto remoteView = ObjectBroker::object( QStringLiteral("com.kdab.GammaRay.QuickRemoteView")); QVERIFY(remoteView); remoteView->setViewActive(true); QSignalSpy renderSpy(view(), SIGNAL(frameSwapped())); QVERIFY(renderSpy.isValid()); QSignalSpy gotFrameSpy(remoteView, SIGNAL(frameUpdated(GammaRay::RemoteViewFrame))); QVERIFY(gotFrameSpy.isValid()); QVERIFY(showSource(QStringLiteral("qrc:/manual/rotationinvariant.qml"))); remoteView->clientViewUpdated(); if (!isViewExposed()) return; QVERIFY(waitForSignal(&gotFrameSpy, true)); QVERIFY(!renderSpy.isEmpty()); QVERIFY(!gotFrameSpy.isEmpty()); const auto frame = gotFrameSpy.at(0).at(0).value(); QImage img = frame.image(); QTransform transform = frame.transform(); img = img.transformed(transform); QVERIFY(!img.isNull()); QCOMPARE(img.width(), static_cast(view()->width() *view()->devicePixelRatio())); QCOMPARE(img.height(), static_cast(view()->height() *view()->devicePixelRatio())); // Grabbed stuff seems to alter colors depending the monitor color profile, let use plain QColor for comparison. QCOMPARE(QColor(img.pixel(1 * view()->devicePixelRatio(), 1 * view()->devicePixelRatio())), QColor(255, 0, 0)); QCOMPARE(QColor(img.pixel(99 * view()->devicePixelRatio(), 1 * view()->devicePixelRatio())), QColor(0, 255, 0)); QCOMPARE(QColor(img.pixel(1 * view()->devicePixelRatio(), 99 * view()->devicePixelRatio())), QColor(0, 0, 255)); QCOMPARE(QColor(img.pixel(99 * view()->devicePixelRatio(), 99 * view()->devicePixelRatio())), QColor(255, 255, 0)); remoteView->setViewActive(false); } void testCustomRenderModes() { QSignalSpy featureSpy(inspector, SIGNAL(features( GammaRay::QuickInspectorInterface::Features))); QVERIFY(featureSpy.isValid()); inspector->checkFeatures(); QCOMPARE(featureSpy.size(), 1); auto features = featureSpy.at(0).at(0).value(); QSignalSpy renderSpy(view(), SIGNAL(frameSwapped())); QVERIFY(renderSpy.isValid()); QVERIFY(showSource(QStringLiteral("qrc:/manual/reparenttest.qml"))); if (features & QuickInspectorInterface::CustomRenderModeClipping) { // We can't do more than making sure, it doesn't crash. Let's wait some frames inspector->setCustomRenderMode(QuickInspectorInterface::VisualizeClipping); for (int i = 0; i < 3; i++) triggerSceneChange(); if (isViewExposed()) QVERIFY(waitForSignal(&renderSpy)); } if (features & QuickInspectorInterface::CustomRenderModeOverdraw) { inspector->setCustomRenderMode(QuickInspectorInterface::VisualizeOverdraw); for (int i = 0; i < 3; i++) triggerSceneChange(); if (isViewExposed()) QVERIFY(waitForSignal(&renderSpy)); } if (features & QuickInspectorInterface::CustomRenderModeBatches) { inspector->setCustomRenderMode(QuickInspectorInterface::VisualizeBatches); for (int i = 0; i < 3; i++) triggerSceneChange(); if (isViewExposed()) QVERIFY(waitForSignal(&renderSpy)); } if (features & QuickInspectorInterface::CustomRenderModeChanges) { inspector->setCustomRenderMode(QuickInspectorInterface::VisualizeChanges); for (int i = 0; i < 3; i++) triggerSceneChange(); if (isViewExposed()) QVERIFY(waitForSignal(&renderSpy)); } inspector->setCustomRenderMode(QuickInspectorInterface::NormalRendering); for (int i = 0; i < 3; i++) triggerSceneChange(); if (isViewExposed()) QVERIFY(waitForSignal(&renderSpy)); } void testAnchorsPropertyFilter() { PropertyData testData; testData.setName("something"); testData.setClassName("QQuickItem"); testData.setTypeName("QQuickAnchors"); QVERIFY(!PropertyFilters::matches(testData)); testData.setName("anchors"); QVERIFY(PropertyFilters::matches(testData)); QVERIFY(showSource(QStringLiteral("qrc:/manual/anchorspropertyfiltertest.qml"))); auto rectWithoutAnchors = view()->rootObject()->findChild("rectWithoutAnchors"); auto rectWithAnchors = view()->rootObject()->findChild("rectWithAnchors"); auto rectWithoutAnchorsPriv = QQuickItemPrivate::get(rectWithoutAnchors); auto rectWithAnchorsPriv = QQuickItemPrivate::get(rectWithAnchors); QVERIFY(!rectWithoutAnchorsPriv->_anchors); QVERIFY(rectWithAnchorsPriv->_anchors); auto propertyModel = ObjectBroker::model("com.kdab.GammaRay.ObjectInspector.properties"); QVERIFY(propertyModel); QSortFilterProxyModel anchorsFilterModel; anchorsFilterModel.setSourceModel(propertyModel); anchorsFilterModel.setFilterKeyColumn(0); anchorsFilterModel.setFilterFixedString("anchors"); Probe::instance()->selectObject(rectWithoutAnchors); QVERIFY(propertyModel->rowCount()); QCOMPARE(anchorsFilterModel.rowCount(), 1); auto rectWithoutAnchorsAnchorsValue = anchorsFilterModel.data(anchorsFilterModel.index(0, 1), Qt::EditRole); QVERIFY(rectWithoutAnchorsAnchorsValue.canConvert()); QVERIFY(rectWithoutAnchorsAnchorsValue.value() == nullptr); Probe::instance()->selectObject(rectWithAnchors); QCOMPARE(anchorsFilterModel.rowCount(), 1); auto rectWithAnchorsAnchorsValue = anchorsFilterModel.data(anchorsFilterModel.index(0, 1), Qt::EditRole); QVERIFY(rectWithAnchorsAnchorsValue.canConvert()); QVERIFY(rectWithAnchorsAnchorsValue.value() != nullptr); Probe::instance()->selectObject(rectWithoutAnchors); // We want to trigger as much QuiickInspector-code as possible to check that // QQuickItemPrivate::anchors is not called by any GammaRay-code as that // would render the filter useless. auto remoteView = ObjectBroker::object( QStringLiteral("com.kdab.GammaRay.QuickRemoteView")); QVERIFY(remoteView); remoteView->setViewActive(true); remoteView->clientViewUpdated(); QTest::qWait(10); rectWithoutAnchorsAnchorsValue = anchorsFilterModel.data(anchorsFilterModel.index(0, 1), Qt::EditRole); QVERIFY(rectWithoutAnchorsAnchorsValue.canConvert()); QVERIFY(rectWithoutAnchorsAnchorsValue.value() == nullptr); } void testProblemReporting() { //TODO using this qml-file as testcase might stop working if qt decides to be // smarter with out of view items in ListViews QVERIFY(showSource(QStringLiteral("qrc:/manual/quickitemcreatedestroytest.qml"))); QVERIFY(ProblemCollector::instance()->isCheckerRegistered("com.kdab.GammaRay.QuickItemChecker")); ProblemCollector::instance()->requestScan(); if (!isViewExposed()) { // if the CI fails to show the window, this isn't going to succeed return; } const auto &problems = ProblemCollector::instance()->problems(); QVERIFY(std::any_of(problems.begin(), problems.end(), [&](const Problem &p){ return p.problemId.startsWith("com.kdab.GammaRay.QuickItemChecker") && !p.object.isNull() && p.description.contains("out of view") && p.locations.size() > 0; } )); } private: QAbstractItemModel *itemModel; QAbstractItemModel *sgModel; QuickInspectorInterface *inspector; }; QTEST_MAIN(QuickInspectorTest) #include "quickinspectortest.moc" gammaray-2.11.3/tests/quickinspectortest.qrc000066400000000000000000000013211413310443000211760ustar00rootroot00000000000000 manual/reparenttest.qml manual/quickitemcreatedestroytest.qml manual/textures.qml manual/lsd.png manual/rotationinvariant.qml manual/shadereffect.qml manual/anchorspropertyfiltertest.qml manual/picking/invisibleoverlay.qml manual/picking/negativezordering.qml manual/picking/opacityzerooverlay.qml manual/picking/stackedrects.qml manual/picking/loader.qml manual/picking/outsideofparent.qml gammaray-2.11.3/tests/quickinspectortest2.cpp000066400000000000000000000174751413310443000212760ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "basequicktest.h" #include "testhelpers.h" #include #include #include #include #include <3rdparty/qt/modeltest.h> #include #include using namespace GammaRay; using namespace TestHelpers; class QuickInspectorTest2 : public BaseQuickTest { Q_OBJECT protected: bool ignoreNonExposedView() const override { return true; } private slots: void initTestCase() { qRegisterMetaType(); } void init() override { BaseQuickTest::init(); itemModel = ObjectBroker::model(QStringLiteral("com.kdab.GammaRay.QuickItemModel")); QVERIFY(itemModel); new ModelTest(itemModel, view()); sgModel = ObjectBroker::model(QStringLiteral("com.kdab.GammaRay.QuickSceneGraphModel")); QVERIFY(sgModel); new ModelTest(sgModel, view()); inspector = ObjectBroker::object(); QVERIFY(inspector); inspector->setServerSideDecorationsEnabled(false); inspector->selectWindow(0); QTest::qWait(1); } void testPreviewFetchingThrottler_data() { QTest::addColumn("clientIsReplying", nullptr); QTest::newRow("no-reply") << false; QTest::newRow("reply") << true; } void testPreviewFetchingThrottler() { QFETCH(bool, clientIsReplying); auto remoteView = ObjectBroker::object(QStringLiteral( "com.kdab.GammaRay.QuickRemoteView")); QVERIFY(remoteView); QVERIFY(showSource(QStringLiteral("qrc:/manual/rotationinvariant.qml"))); if (!isViewExposed()) return; auto rootItem = view()->rootObject(); QVERIFY(rootItem); Probe::instance()->selectObject(rootItem, QPoint()); // RemoteViewServer throttle the requests with an interval of qRound(1000.0 / 60.0) const qreal throttlerInterval = qRound(1000.0 / 60.0); if (clientIsReplying) { connect(remoteView, &RemoteViewInterface::frameUpdated, remoteView, &RemoteViewInterface::clientViewUpdated, Qt::UniqueConnection); } else { disconnect(remoteView, &RemoteViewInterface::frameUpdated, remoteView, &RemoteViewInterface::clientViewUpdated); } QSignalSpy requestedSpy(remoteView, SIGNAL(requestUpdate())); QVERIFY(requestedSpy.isValid()); QSignalSpy updatedSpy(remoteView, &RemoteViewInterface::frameUpdated); QVERIFY(updatedSpy.isValid()); // Testing static scene only send 1 frame for (int i = 0; i < 3; ++i) { remoteView->setViewActive(true); // Activating the view trigger an update request QVERIFY(waitForSignal(&updatedSpy, true)); QVERIFY(requestedSpy.count() == 1 || requestedSpy.count() == 2); // should be 1, but we might see spurious repaints on windows QVERIFY(updatedSpy.count() == 1 || updatedSpy.count() == 2); if (!clientIsReplying) remoteView->clientViewUpdated(); requestedSpy.clear(); updatedSpy.clear(); if (clientIsReplying) { // The client is answering clientViewUpdated automatically. // For 1 request and multiple client answers the server should only send 1 frame. view()->update(); QVERIFY(waitForSignal(&updatedSpy, true)); for (int j = 0; j < 3; ++j) { // The automatic clientViewUpdated answer is only done for frame sent. // Let manually trigger answers. QTest::qWait(throttlerInterval); remoteView->clientViewUpdated(); } QVERIFY(waitForSignal(&requestedSpy, true)); QVERIFY(requestedSpy.count() == 1 || requestedSpy.count() == 2); QVERIFY(updatedSpy.count() == 1 || updatedSpy.count() == 2); } else { // The client is not answering with clientViewUpdated automatically. // Only 1 request and 1 frame sent should trigger. for (int i = 0; i < 3; ++i) { view()->update(); QTest::qWait(throttlerInterval); QVERIFY(waitForSignal(&updatedSpy, true)); } QVERIFY(requestedSpy.count() == 1 || requestedSpy.count() == 2); QVERIFY(updatedSpy.count() == 1 || updatedSpy.count() == 2); } requestedSpy.clear(); updatedSpy.clear(); remoteView->setViewActive(false); } // Our animation properties const qreal animationInterval = throttlerInterval; const qreal animationDuration = 1400.0; // Qml try to render @ ~60fps const qreal maxPossibleQmlRequests = clientIsReplying ? animationDuration / 1000.0 * 60.0 : 1.0; const qreal maxPossibleThrottledRequests = clientIsReplying ? qMin(maxPossibleQmlRequests, animationDuration / throttlerInterval) : 1.0; // Testing dynamic scene for (int i = 0; i < 3; i++) { remoteView->setViewActive(true); // Activating the view trigger an update request QVERIFY(waitForSignal(&updatedSpy, true)); QCOMPARE(requestedSpy.count(), 1); QCOMPARE(updatedSpy.count(), 1); if (!clientIsReplying) remoteView->clientViewUpdated(); requestedSpy.clear(); updatedSpy.clear(); // We rotate our rendering every animationInterval-ms for animationDuration-ms rootItem->setProperty("interval", qRound(animationInterval)); rootItem->setProperty("animated", true); QTest::qWait(qRound(animationDuration)); // Wait to process pending requests... rootItem->setProperty("animated", false); QTest::qWait(qRound(animationDuration)); QVERIFY(requestedSpy.count() <= qRound(maxPossibleThrottledRequests * 1.05) + 1); QVERIFY(updatedSpy.count() <= qRound(maxPossibleThrottledRequests * 1.05) + 1); QCOMPARE(requestedSpy.count(), updatedSpy.count()); requestedSpy.clear(); updatedSpy.clear(); remoteView->setViewActive(false); } } private: QAbstractItemModel *itemModel; QAbstractItemModel *sgModel; QuickInspectorInterface *inspector; }; QTEST_MAIN(QuickInspectorTest2) #include "quickinspectortest2.moc" gammaray-2.11.3/tests/quickmaterialtest.cpp000066400000000000000000000137571413310443000210030ustar00rootroot00000000000000/* quickmaterialtest.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "basequicktest.h" #include #include #include #include <3rdparty/qt/modeltest.h> #include #include #include using namespace GammaRay; static QSGGeometryNode *findGeometryNode(QSGNode *node) { while(node) { if (node->type() == QSGNode::GeometryNodeType) return static_cast(node); if (node->childCount() == 0) return nullptr; if (node->childCount() > 1 && node->firstChild()->type() != QSGNode::GeometryNodeType) return nullptr; node = node->firstChild(); } return nullptr; } class QuickMaterialTest : public BaseQuickTest { Q_OBJECT private slots: void testStaticShader() { if (!showSource("qrc:/manual/shadereffect.qml")) return; auto iface = ObjectBroker::object("com.kdab.GammaRay.QuickSceneGraph.material"); QVERIFY(iface); QSignalSpy shaderSpy(iface, SIGNAL(gotShader(QString))); QVERIFY(shaderSpy.isValid()); auto propModel = ObjectBroker::model("com.kdab.GammaRay.QuickSceneGraph.materialPropertyModel"); QVERIFY(propModel); ModelTest propModelTest(propModel); QCOMPARE(propModel->rowCount(), 0); auto shaderModel = ObjectBroker::model("com.kdab.GammaRay.QuickSceneGraph.shaderModel"); QVERIFY(shaderModel); ModelTest shaderModelTest(shaderModel); QCOMPARE(shaderModel->rowCount(), 0); auto imageItem = view()->rootObject(); QVERIFY(imageItem); QCOMPARE(imageItem->metaObject()->className(), "QQuickImage"); Probe::instance()->selectObject(imageItem, QPoint()); auto controller = ObjectBroker::object("com.kdab.GammaRay.QuickSceneGraph.controller"); QVERIFY(controller); auto priv = QQuickItemPrivate::get(imageItem); QVERIFY(priv->itemNodeInstance); auto geometryNode = findGeometryNode(priv->itemNodeInstance); QVERIFY(geometryNode); controller->setObject(geometryNode, "QSGGeometryNode"); QVERIFY(propModel->rowCount() > 1); QCOMPARE(shaderModel->rowCount(), 2); iface->getShader(0); QCOMPARE(shaderSpy.size(), 1); QVERIFY(!shaderSpy.at(0).at(0).toString().isEmpty()); shaderSpy.clear(); iface->getShader(1); QCOMPARE(shaderSpy.size(), 1); QVERIFY(!shaderSpy.at(0).at(0).toString().isEmpty()); controller->setObject(nullptr, QString()); } void testDynamicShader() { if (!showSource("qrc:/manual/shadereffect.qml")) return; auto iface = ObjectBroker::object("com.kdab.GammaRay.QuickSceneGraph.material"); QVERIFY(iface); QSignalSpy shaderSpy(iface, SIGNAL(gotShader(QString))); QVERIFY(shaderSpy.isValid()); auto propModel = ObjectBroker::model("com.kdab.GammaRay.QuickSceneGraph.materialPropertyModel"); QVERIFY(propModel); ModelTest propModelTest(propModel); QCOMPARE(propModel->rowCount(), 0); auto shaderModel = ObjectBroker::model("com.kdab.GammaRay.QuickSceneGraph.shaderModel"); QVERIFY(shaderModel); ModelTest shaderModelTest(shaderModel); QCOMPARE(shaderModel->rowCount(), 0); QQuickItem *effectItem = nullptr; foreach (auto item, view()->rootObject()->findChildren()) { if (item->inherits("QQuickShaderEffect")) effectItem = item; } QVERIFY(effectItem); Probe::instance()->selectObject(effectItem, QPoint()); auto controller = ObjectBroker::object("com.kdab.GammaRay.QuickSceneGraph.controller"); QVERIFY(controller); auto priv = QQuickItemPrivate::get(effectItem); QVERIFY(priv->itemNodeInstance); auto geometryNode = findGeometryNode(priv->itemNodeInstance); QVERIFY(geometryNode); controller->setObject(geometryNode, "QSGGeometryNode"); #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) QVERIFY(propModel->rowCount() > 1); #endif QCOMPARE(shaderModel->rowCount(), 2); iface->getShader(0); QCOMPARE(shaderSpy.size(), 1); QVERIFY(!shaderSpy.at(0).at(0).toString().isEmpty()); QVERIFY(shaderSpy.at(0).at(0).toString().contains(QLatin1String("TESTVERTEXSHADER"))); shaderSpy.clear(); iface->getShader(1); QCOMPARE(shaderSpy.size(), 1); QVERIFY(!shaderSpy.at(0).at(0).toString().isEmpty()); QVERIFY(shaderSpy.at(0).at(0).toString().contains(QLatin1String("TESTFRAGMENTSHADER"))); controller->setObject(nullptr, QString()); } }; QTEST_MAIN(QuickMaterialTest) #include "quickmaterialtest.moc" gammaray-2.11.3/tests/quicktexturetest.cpp000066400000000000000000000106461413310443000206770ustar00rootroot00000000000000/* quicktexturetest.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "basequicktest.h" #include #include #include #include using namespace GammaRay; class QuickTextureTest : public BaseQuickTest { Q_OBJECT private slots: void testTextureGrab() { if (!showSource("qrc:/manual/textures.qml")) return; auto remoteView = ObjectBroker::object("com.kdab.GammaRay.ObjectInspector.texture.remoteView"); QVERIFY(remoteView); QSignalSpy frameSpy(remoteView, SIGNAL(frameUpdated(GammaRay::RemoteViewFrame))); QVERIFY(frameSpy.isValid()); remoteView->setViewActive(true); auto imageItem = view()->rootObject(); QVERIFY(imageItem); QCOMPARE(imageItem->metaObject()->className(), "QQuickImage"); Probe::instance()->selectObject(imageItem, QPoint()); QVERIFY(frameSpy.wait()); const auto frame = frameSpy.at(0).at(0).value(); QCOMPARE(frame.viewRect(), QRectF(0, 0, 360, 360)); QCOMPARE(frame.image().pixel(358, 0), QColor(Qt::red).rgb()); QCOMPARE(frame.image().pixel(119, 0), QColor(Qt::green).rgb()); QCOMPARE(frame.image().pixel(239, 0), QColor(Qt::blue).rgb()); } void testDFGrab() { if (!showSource("qrc:/manual/textures.qml")) return; auto remoteView = ObjectBroker::object("com.kdab.GammaRay.ObjectInspector.texture.remoteView"); QVERIFY(remoteView); QSignalSpy frameSpy(remoteView, SIGNAL(frameUpdated(GammaRay::RemoteViewFrame))); QVERIFY(frameSpy.isValid()); remoteView->setViewActive(true); QQuickItem *textItem = nullptr; foreach (auto item, view()->rootObject()->findChildren()) { if (strcmp(item->metaObject()->className(), "QQuickText") == 0) textItem = item; } QVERIFY(textItem); Probe::instance()->selectObject(textItem, QPoint()); QVERIFY(frameSpy.wait()); const auto frame = frameSpy.at(0).at(0).value(); QVERIFY(frame.viewRect().height() > 0); QVERIFY(frame.viewRect().width() > 0); } void testShaderEffectSourceGrab() { if (!showSource("qrc:/manual/shadereffect.qml")) { qWarning() << "Skipping test due to unavailable QtQuick display."; return; } auto remoteView = ObjectBroker::object("com.kdab.GammaRay.ObjectInspector.texture.remoteView"); QVERIFY(remoteView); QSignalSpy frameSpy(remoteView, SIGNAL(frameUpdated(GammaRay::RemoteViewFrame))); QVERIFY(frameSpy.isValid()); remoteView->setViewActive(true); QQuickItem *source = nullptr; foreach (auto item, view()->rootObject()->findChildren()) { if (item->inherits("QQuickShaderEffectSource")) source = item; } QVERIFY(source); Probe::instance()->selectObject(source, QPoint()); QVERIFY(frameSpy.wait()); const auto frame = frameSpy.at(0).at(0).value(); QVERIFY(frame.viewRect().height() > 0); QVERIFY(frame.viewRect().width() > 0); } }; QTEST_MAIN(QuickTextureTest) #include "quicktexturetest.moc" gammaray-2.11.3/tests/remotemodeltest.cpp000066400000000000000000000266331413310443000204610ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include <3rdparty/qt/modeltest.h> #include #include #include #include #include #include #include #include #include #include using namespace GammaRay; static void fakeRegisterServer() {} namespace GammaRay { class FakeRemoteModelServer : public RemoteModelServer { Q_OBJECT public: explicit FakeRemoteModelServer(const QString &objectName, QObject *parent = nullptr) : RemoteModelServer(objectName, parent) { m_myAddress = 42; } static void setup() { FakeRemoteModelServer::s_registerServerCallback = &fakeRegisterServer; } signals: void message(const GammaRay::Message &msg); private slots: void deliverMessage(const QByteArray &ba) { QBuffer buffer(const_cast(&ba)); buffer.open(QIODevice::ReadOnly); emit message(Message::readMessage(&buffer)); } private: bool isConnected() const override { return true; } void sendMessage(const Message &msg) const override { QByteArray ba; QBuffer buffer(&ba); buffer.open(QIODevice::WriteOnly); msg.write(&buffer); buffer.close(); QMetaObject::invokeMethod(const_cast(this), "deliverMessage", Qt::QueuedConnection, Q_ARG(QByteArray, ba)); } }; class FakeRemoteModel : public RemoteModel { Q_OBJECT public: explicit FakeRemoteModel(const QString &serverObject, QObject *parent = nullptr) : RemoteModel(serverObject, parent) { m_myAddress = 42; } static void setup() { FakeRemoteModel::s_registerClientCallback = &fakeRegisterServer; } signals: void message(const GammaRay::Message &msg); private: void sendMessage(const Message &msg) const override { QByteArray ba; QBuffer buffer(&ba); buffer.open(QIODevice::ReadWrite); msg.write(&buffer); buffer.seek(0); emit const_cast(this)->message(Message::readMessage(&buffer)); } }; } class RemoteModelTest : public QObject { Q_OBJECT private: bool waitForData(const QModelIndex &idx) { if (idx.data(RemoteModelRole::LoadingState).value() == RemoteModelNodeState::NoState) return true; // data already present QSignalSpy spy(const_cast(idx.model()), SIGNAL(dataChanged(QModelIndex,QModelIndex))); if (!spy.isValid()) return false; idx.data(); // trigger the request Q_ASSERT(spy.isEmpty()); while (spy.wait()) { for (auto it = spy.constBegin(); it != spy.constEnd(); ++it) { if ((*it).contains(idx)) return true; } spy.clear(); } return false; } private slots: void initTestCase() { FakeRemoteModelServer::setup(); FakeRemoteModel::setup(); } void testEmptyRemoteModel() { QScopedPointer emptyModel(new QStandardItemModel(this)); FakeRemoteModelServer server(QStringLiteral("com.kdab.GammaRay.UnitTest.EmptyModel"), this); server.setModel(emptyModel.data()); server.modelMonitored(true); FakeRemoteModel client(QStringLiteral("com.kdab.GammaRay.UnitTest.EmptyModel"), this); connect(&server, &FakeRemoteModelServer::message, &client, &RemoteModel::newMessage); connect(&client, &FakeRemoteModel::message, &server, &RemoteModelServer::newRequest); ModelTest modelTest(&client); QCOMPARE(client.rowCount(), 0); QTest::qWait(10); QCOMPARE(client.rowCount(), 0); QCOMPARE(client.hasChildren(), false); } void testListRemoteModel() { QScopedPointer listModel(new QStandardItemModel(this)); listModel->appendRow(new QStandardItem(QStringLiteral("entry0"))); listModel->appendRow(new QStandardItem(QStringLiteral("entry2"))); listModel->appendRow(new QStandardItem(QStringLiteral("entry3"))); listModel->appendRow(new QStandardItem(QStringLiteral("entry4"))); FakeRemoteModelServer server(QStringLiteral("com.kdab.GammaRay.UnitTest.ListModel"), this); server.setModel(listModel.data()); server.modelMonitored(true); FakeRemoteModel client(QStringLiteral("com.kdab.GammaRay.UnitTest.ListModel"), this); connect(&server, &FakeRemoteModelServer::message, &client, &RemoteModel::newMessage); connect(&client, &FakeRemoteModel::message, &server, &RemoteModelServer::newRequest); ModelTest modelTest(&client); QTest::qWait(100); // ModelTest is going to fetch stuff for us already QCOMPARE(client.rowCount(), 4); QCOMPARE(client.hasChildren(), true); auto index = client.index(1, 0); QVERIFY(waitForData(index)); QCOMPARE(index.data().toString(), QStringLiteral("entry2")); QCOMPARE(client.rowCount(index), 0); listModel->insertRow(1, new QStandardItem(QStringLiteral("entry1"))); QTest::qWait(10); QCOMPARE(client.rowCount(), 5); index = client.index(1, 0); QVERIFY(waitForData(index)); QCOMPARE(index.data().toString(), QStringLiteral("entry1")); const auto deleteMe = listModel->takeRow(3); qDeleteAll(deleteMe); QTest::qWait(10); QCOMPARE(client.rowCount(), 4); } void testTreeRemoteModel() { QScopedPointer treeModel(new QStandardItemModel(this)); auto e0 = new QStandardItem(QStringLiteral("entry0")); e0->appendRow(new QStandardItem(QStringLiteral("entry00"))); e0->appendRow(new QStandardItem(QStringLiteral("entry01"))); treeModel->appendRow(e0); auto e1 = new QStandardItem(QStringLiteral("entry1")); e1->appendRow(new QStandardItem(QStringLiteral("entry10"))); e1->appendRow(new QStandardItem(QStringLiteral("entry12"))); treeModel->appendRow(e1); FakeRemoteModelServer server(QStringLiteral("com.kdab.GammaRay.UnitTest.TreeModel"), this); server.setModel(treeModel.data()); server.modelMonitored(true); FakeRemoteModel client(QStringLiteral("com.kdab.GammaRay.UnitTest.TreeModel"), this); connect(&server, &FakeRemoteModelServer::message, &client, &RemoteModel::newMessage); connect(&client, &FakeRemoteModel::message, &server, &RemoteModelServer::newRequest); ModelTest modelTest(&client); QTest::qWait(25); // ModelTest is going to fetch stuff for us already QCOMPARE(client.rowCount(), 2); QCOMPARE(client.hasChildren(), true); auto i1 = client.index(1, 0); QVERIFY(waitForData(i1)); QCOMPARE(i1.data().toString(), QStringLiteral("entry1")); QCOMPARE(client.rowCount(i1), 2); auto i12 = client.index(1, 0, i1); QVERIFY(waitForData(i12)); QCOMPARE(i12.data().toString(), QStringLiteral("entry12")); QCOMPARE(client.rowCount(i12), 0); e1->insertRow(1, new QStandardItem(QStringLiteral("entry11"))); QTest::qWait(10); QCOMPARE(client.rowCount(i1), 3); auto i11 = client.index(1, 0, i1); QVERIFY(waitForData(i11)); QCOMPARE(i11.data().toString(), QStringLiteral("entry11")); QCOMPARE(client.rowCount(i11), 0); const auto deleteMe = e1->takeRow(0); qDeleteAll(deleteMe); QTest::qWait(10); QCOMPARE(client.rowCount(i1), 2); i11 = client.index(0, 0, i1); QVERIFY(waitForData(i11)); QCOMPARE(i11.data().toString(), QStringLiteral("entry11")); } // this should not make a difference if the above works, however it broke massively with Qt 5.4... void testSortProxy() { QScopedPointer treeModel(new QStandardItemModel(this)); auto e0 = new QStandardItem(QStringLiteral("entry1")); e0->appendRow(new QStandardItem(QStringLiteral("entry10"))); e0->appendRow(new QStandardItem(QStringLiteral("entry11"))); treeModel->appendRow(e0); auto e1 = new QStandardItem(QStringLiteral("entry0")); e1->appendRow(new QStandardItem(QStringLiteral("entry00"))); e1->appendRow(new QStandardItem(QStringLiteral("entry01"))); e1->appendRow(new QStandardItem(QStringLiteral("entry02"))); e1->appendRow(new QStandardItem(QStringLiteral("entry03"))); treeModel->appendRow(e1); FakeRemoteModelServer server(QStringLiteral("com.kdab.GammaRay.UnitTest.TreeModel2"), this); server.setModel(treeModel.data()); server.modelMonitored(true); FakeRemoteModel client(QStringLiteral("com.kdab.GammaRay.UnitTest.TreeModel2"), this); connect(&server, &FakeRemoteModelServer::message, &client, &RemoteModel::newMessage); connect(&client, &FakeRemoteModel::message, &server, &RemoteModelServer::newRequest); QSortFilterProxyModel proxy; proxy.setDynamicSortFilter(true); proxy.sort(0); proxy.setSourceModel(&client); ModelTest modelTest(&proxy); QTest::qWait(25); // ModelTest is going to fetch stuff for us already QCOMPARE(client.rowCount(), 2); QCOMPARE(proxy.rowCount(), 2); auto pi0 = proxy.index(0, 0); QVERIFY(waitForData(pi0)); QCOMPARE(pi0.data().toString(), QStringLiteral("entry0")); QCOMPARE(proxy.rowCount(pi0), 4); auto pi03 = proxy.index(3, 0, pi0); QVERIFY(waitForData(pi03)); QCOMPARE(pi03.data().toString(), QStringLiteral("entry03")); auto ci0 = client.index(0, 0); QVERIFY(waitForData(ci0)); QCOMPARE(ci0.data().toString(), QStringLiteral("entry1")); QCOMPARE(client.rowCount(ci0), 2); auto pi1 = proxy.index(1, 0); QVERIFY(waitForData(pi1)); QCOMPARE(pi1.data().toString(), QStringLiteral("entry1")); // this fails with data() call batching sizes close to 1 // QEXPECT_FAIL("", "QSFPM misbehavior, no idea yet where this is coming from", Continue); QCOMPARE(proxy.rowCount(pi1), 2); } }; QTEST_MAIN(RemoteModelTest) #include "remotemodeltest.moc" gammaray-2.11.3/tests/selflocatortest.cpp000066400000000000000000000033031413310443000204470ustar00rootroot00000000000000/* selflocatortest.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include #include #include #include #include using namespace GammaRay; class SelfLocatorTest : public QObject { Q_OBJECT private slots: void testFindMe() { qDebug() << SelfLocator::findMe(); QCOMPARE(QCoreApplication::applicationFilePath(), SelfLocator::findMe()); } void testRootPath() { qDebug() << Paths::rootPath(); QVERIFY(!Paths::rootPath().isEmpty()); } }; QTEST_MAIN(SelfLocatorTest) #include "selflocatortest.moc" gammaray-2.11.3/tests/selftesttest.cpp000066400000000000000000000030431413310443000177640ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include #include #include #include using namespace GammaRay; class SelfTestTest : public QObject { Q_OBJECT private slots: void runSelfTest() { Paths::setRelativeRootPath(GAMMARAY_INVERSE_BIN_DIR); SelfTest selfTest; QVERIFY(selfTest.checkEverything()); } }; QTEST_MAIN(SelfTestTest) #include "selftesttest.moc" gammaray-2.11.3/tests/shared/000077500000000000000000000000001413310443000157755ustar00rootroot00000000000000gammaray-2.11.3/tests/shared/CMakeLists.txt000066400000000000000000000010011413310443000205250ustar00rootroot00000000000000#changingpropertyobject doesn't have any symbols. Quiet MSVC warnings. if(MSVC) set(CMAKE_STATIC_LINKER_FLAGS "/ignore:4221 ${CMAKE_SHARED_LINKER_FLAGS}") endif() set(gammaray_shared_test_srcs variantpropertyobject.cpp changingpropertyobject.cpp propertytestobject.cpp ) add_library(gammaray_shared_test_data STATIC ${gammaray_shared_test_srcs}) target_compile_features(gammaray_shared_test_data PUBLIC ${GAMMARAY_REQUIRED_CXX_FEATURES}) target_link_libraries(gammaray_shared_test_data Qt5::Core) gammaray-2.11.3/tests/shared/changingpropertyobject.cpp000066400000000000000000000022421413310443000232530ustar00rootroot00000000000000/* changingpropertyobject.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "changingpropertyobject.h" gammaray-2.11.3/tests/shared/changingpropertyobject.h000066400000000000000000000041341413310443000227220ustar00rootroot00000000000000/* changingpropertyobject.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef CHANGINGPROPERTYOBJECT_H #define CHANGINGPROPERTYOBJECT_H #include #include class ChangingPropertyObject : public QObject { Q_OBJECT Q_PROPERTY( int staticChangingProperty READ staticChangingProperty RESET staticChangingPropertyReset NOTIFY staticChangingPropertyChanged) public: explicit ChangingPropertyObject(QObject *parent = nullptr) : QObject(parent) , m_count(0) { setObjectName(QStringLiteral("changingPropertyObject")); } int staticChangingProperty() { return m_count; } void staticChangingPropertyReset() { m_count = 0; emit staticChangingPropertyChanged(); } signals: void staticChangingPropertyChanged(); public slots: void changeProperties() { ++m_count; setProperty("dynamicChangingProperty", QVariant(m_count)); emit staticChangingPropertyChanged(); } private: int m_count; }; #endif // CHANGINGPROPERTYOBJECT_H gammaray-2.11.3/tests/shared/propertytestobject.cpp000066400000000000000000000023511413310443000224550ustar00rootroot00000000000000/* propertytestobject.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "propertytestobject.h" #include void Gadget::someMethod() { qDebug() << Q_FUNC_INFO; } gammaray-2.11.3/tests/shared/propertytestobject.h000066400000000000000000000057011413310443000221240ustar00rootroot00000000000000/* propertytestobject.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef PROPERTYTESTOBJECT_H #define PROPERTYTESTOBJECT_H #include "changingpropertyobject.h" #include "variantpropertyobject.h" #include class Gadget { Q_GADGET Q_PROPERTY(int prop1 READ prop1 WRITE setProp1 RESET resetProp1) public: Gadget() = default; int prop1() const { return m_prop1; } void setProp1(int v) { m_prop1 = v; } void resetProp1() { m_prop1 = 5; } Q_INVOKABLE void someMethod(); private: int m_prop1 = 42; }; Q_DECLARE_METATYPE(Gadget) Q_DECLARE_METATYPE(Gadget*) class PropertyTestObject : public QObject { Q_OBJECT Q_PROPERTY(int intProp READ intProp WRITE setIntProp NOTIFY intPropChanged) Q_PROPERTY(int readOnlyProp READ intProp RESET resetIntProp) Q_PROPERTY(Gadget gadget READ gadget WRITE setGadget) Q_PROPERTY(Gadget gadgetReadOnly READ gadget) Q_PROPERTY(Gadget* gadgetPointer READ gadgetPointer) Q_PROPERTY(VariantPropertyObject* variantPropertyObject READ variantPropertyObject) Q_PROPERTY(ChangingPropertyObject* changingPropertyObject READ changingPropertyObject) public: explicit PropertyTestObject(QObject *parent = nullptr) : QObject(parent) {} int intProp() { return p1; } void setIntProp(int i) { if (p1 == i) return; p1 = i; emit intPropChanged(); } void resetIntProp() { setIntProp(5); } Gadget gadget() const { return g; } void setGadget(Gadget _g) { g = _g; } Gadget* gadgetPointer() const { return const_cast(&g); } VariantPropertyObject *variantPropertyObject() { return &vpo; } ChangingPropertyObject *changingPropertyObject() { return &cpo; } signals: void intPropChanged(); private: int p1 = 0; Gadget g; VariantPropertyObject vpo; ChangingPropertyObject cpo; }; #endif // PROPERTYTESTOBJECT_H gammaray-2.11.3/tests/shared/variantpropertyobject.cpp000066400000000000000000000036151413310443000231460ustar00rootroot00000000000000/* variantinspector.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Stephen Kelly Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "variantpropertyobject.h" #include #include VariantPropertyObject::VariantPropertyObject(QObject *parent) : QObject(parent) , m_object(new QObject) { QHash mapping; mapping.insert(QStringLiteral("One"), 1); mapping.insert(QStringLiteral("Two"), 2); setProperty("dynamicProperty", QVariant::fromValue(mapping)); } VariantPropertyObject::~VariantPropertyObject() = default; QSharedPointer VariantPropertyObject::sharedObject() const { return m_object; } QPointer VariantPropertyObject::trackingObject() const { return m_object.data(); } QVector VariantPropertyObject::widgetVector() const { QVector vec; vec << 5; vec << 6; vec << 7; vec << 8; return vec; } gammaray-2.11.3/tests/shared/variantpropertyobject.h000066400000000000000000000035311413310443000226100ustar00rootroot00000000000000/* variantinspector.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Stephen Kelly Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef VARIANTPROPERTYOBJECT_H #define VARIANTPROPERTYOBJECT_H #include #include #include #include class VariantPropertyObject : public QObject { Q_OBJECT Q_PROPERTY(QSharedPointer sharedObject READ sharedObject CONSTANT) Q_PROPERTY(QPointer trackingObject READ trackingObject CONSTANT) Q_PROPERTY(QVector intVector READ widgetVector CONSTANT) public: explicit VariantPropertyObject(QObject *parent = nullptr); ~VariantPropertyObject() override; QSharedPointer sharedObject() const; QPointer trackingObject() const; QVector widgetVector() const; private: QSharedPointer m_object; }; #endif gammaray-2.11.3/tests/signalspycallbacktest.cpp000066400000000000000000000050411413310443000216210ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "baseprobetest.h" #include using namespace GammaRay; class Sender : public QObject { Q_OBJECT public: void emitSignal() { emit mySignal(); } signals: void mySignal(); }; class Receiver : public QObject { Q_OBJECT public slots: void senderDeletingSlot() { delete sender(); } }; class SignalSpyCallbackTest : public BaseProbeTest { Q_OBJECT private slots: void testSenderDeletion() { createProbe(); QPointer s1 = new Sender; QPointer s2 = new Sender; Receiver r; connect(s1.data(), &Sender::mySignal, &r, &Receiver::senderDeletingSlot); s1->emitSignal(); // must not crash QVERIFY(s1.isNull()); // give the probe time to process s and r2 (needs one event loop re-entry) QTest::qWait(1); connect(s2.data(), &Sender::mySignal, &r, &Receiver::senderDeletingSlot); s2->emitSignal(); // must not crash QVERIFY(s2.isNull()); } void cleanupTestCase() { // explicitly delete the probe as our usual cleanup doesn't work since we will // not get qApp::aboutToQuit() from QTest::qExec(), and then we end up with // still alive QSFPM when the static deleter kills the source model null object // used by them internally, which then crashes with Qt4... delete Probe::instance(); } }; QTEST_MAIN(SignalSpyCallbackTest) #include "signalspycallbacktest.moc" gammaray-2.11.3/tests/sleep.cpp000066400000000000000000000026511413310443000163470ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Kevin Funk Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include class QThreadAccessViolator : public QThread { public: using QThread::sleep; // protected in Qt4 }; int main(int argc, char *argv[]) { if (argc <= 1) { return 1; } const auto secs = strtoul(argv[1], nullptr, 0); QThreadAccessViolator::sleep(secs); return 0; } gammaray-2.11.3/tests/sourcelocationtest.cpp000066400000000000000000000112311413310443000211620ustar00rootroot00000000000000/* sourcelocationtest.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include #include using namespace GammaRay; class SourceLocationTest : public QObject { Q_OBJECT private slots: void testZeroAndOneBasedNumbering() { SourceLocation loc; loc = SourceLocation::fromZeroBased(QUrl(QStringLiteral("file:///some/file")), 0, 0); QVERIFY(loc.isValid() == true); QCOMPARE(loc.line(), 0); QCOMPARE(loc.column(), 0); QStringLiteral("/some/file:1:1"); loc.setZeroBasedLine(78); loc.setZeroBasedColumn(87); QVERIFY(loc.isValid() == true); QCOMPARE(loc.line(), 78); QCOMPARE(loc.column(), 87); QStringLiteral("/some/file:79:88"); loc = SourceLocation::fromOneBased(QUrl(QStringLiteral("file:///some/file")), 0, 0); QVERIFY(loc.isValid() == true); loc = SourceLocation::fromOneBased(QUrl(QStringLiteral("file:///some/file")), 1, 1); QVERIFY(loc.isValid() == true); QCOMPARE(loc.line(), 0); QCOMPARE(loc.column(), 0); QStringLiteral("/some/file:1:1"); loc.setOneBasedLine(78); loc.setOneBasedColumn(87); QVERIFY(loc.isValid() == true); QCOMPARE(loc.line(), 77); QCOMPARE(loc.column(), 86); QStringLiteral("/some/file:78:87"); } void testDisplayString_data() { QTest::addColumn("url", nullptr); QTest::addColumn("line", nullptr); QTest::addColumn("column", nullptr); QTest::addColumn("displayString", nullptr); QTest::addColumn("valid", nullptr); QTest::newRow("invalid") << QUrl() << -1 << -1 << QString() << false; QTest::newRow("invalid 2") << QUrl() << 42 << 23 << QString() << false; QTest::newRow("url only") << QUrl(QStringLiteral("file:///some/file")) << -1 << -1 << QStringLiteral("/some/file") << true; QTest::newRow("url and line") << QUrl(QStringLiteral("file:///some/file")) << 22 << -1 << QStringLiteral("/some/file:23") << true; QTest::newRow("complete") << QUrl(QStringLiteral("file:///some/file")) << 22 << 41 << QStringLiteral("/some/file:23:42") << true; QTest::newRow("url and column") << QUrl(QStringLiteral("file:///some/file")) << -1 << 42 << QStringLiteral("/some/file") << true; QTest::newRow("complete but 0 column") << QUrl(QStringLiteral("file:///some/file")) << 22 << -1 << QStringLiteral("/some/file:23") << true; QTest::newRow("complete but 1 column") << QUrl(QStringLiteral("file:///some/file")) << 22 << 0 << QStringLiteral("/some/file:23:1") << true; QTest::newRow("url") << QUrl::fromLocalFile(QStringLiteral("/some/file")) << 0 << 0 << QStringLiteral("/some/file:1:1") << true; QTest::newRow("qrc") << QUrl(QStringLiteral("qrc:///main.qml")) << 0 << 0 << QStringLiteral( "qrc:///main.qml:1:1") << true; } void testDisplayString() { QFETCH(QUrl, url); QFETCH(int, line); QFETCH(int, column); QFETCH(QString, displayString); QFETCH(bool, valid); SourceLocation loc = SourceLocation::fromZeroBased(url, line, column); QCOMPARE(loc.displayString(), displayString); QVERIFY(loc.isValid() == valid); } }; QTEST_MAIN(SourceLocationTest) #include "sourcelocationtest.moc" gammaray-2.11.3/tests/targets/000077500000000000000000000000001413310443000162005ustar00rootroot00000000000000gammaray-2.11.3/tests/targets/CMakeLists.txt000066400000000000000000000010331413310443000207350ustar00rootroot00000000000000# put targets in their own prefix, so we can test if GammaRay is properly loaded when installed elsewhere set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/testbin) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/testlib) add_executable(minimalcoreapplication minimalcoreapplication.cpp) target_link_libraries(minimalcoreapplication Qt5::Core) if(Qt5Widgets_FOUND) add_executable(minimalwidgetapplication minimalwidgetapplication.cpp) target_link_libraries(minimalwidgetapplication Qt5::Gui Qt5::Widgets) endif() gammaray-2.11.3/tests/targets/minimalcoreapplication.cpp000066400000000000000000000024501413310443000234300ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include int main(int argc, char **argv) { QCoreApplication app(argc, argv); QObject obj; obj.setObjectName(QStringLiteral("myTestObject")); return app.exec(); } gammaray-2.11.3/tests/targets/minimalwidgetapplication.cpp000066400000000000000000000025641413310443000237710ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include int main(int argc, char **argv) { QApplication app(argc, argv); QPushButton button(QStringLiteral("Hello World!")); button.setObjectName(QStringLiteral("myTestButton")); button.show(); return app.exec(); } gammaray-2.11.3/tests/test_connections.cpp000066400000000000000000000206561413310443000206250ustar00rootroot00000000000000/* test_connections.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "test_connections.h" #include #include #include #include #include #include const int TIMEOUTINTERVAL = 10; const int OBJECTS = 50; const int TIMEOUTS = 100; // BEGIN TestObject TestObject::TestObject(QObject *parent) : QObject(parent) // test object creation in ctor , child(new QObject(this)) { setObjectName(QStringLiteral("TestObject")); child->setObjectName(QStringLiteral("TestObjectChild")); // test connect/disconnect in ctor connect(child, &QObject::destroyed, this, &TestObject::dummySlot); disconnect(child, &QObject::destroyed, this, &TestObject::dummySlot); // now connect again for dtor connect(child, &QObject::destroyed, this, &TestObject::dummySlot); } TestObject::~TestObject() { // test disconnect disconnect(child, &QObject::destroyed, this, &TestObject::dummySlot); // test connect, and leave it around to test disconnect-on-delete connect(child, &QObject::destroyed, this, &TestObject::dummySlot); } // END TestObject // BEGIN TestConnections TestConnections::TestConnections(TestConnections::Type type, int timeOuts, int timeoutInterval) : m_type(type) , m_timeOuts(timeOuts) , m_numTimeout(0) , m_timer(new QTimer(this)) { m_timer = new QTimer(this); connect(m_timer, &QTimer::timeout, this, &TestConnections::timeout); m_timer->start(timeoutInterval == -1 ? TIMEOUTINTERVAL : timeoutInterval); } TestConnections::~TestConnections() = default; void TestConnections::timeout() { if (m_numTimeout == m_timeOuts) { qDeleteAll(m_objects); m_objects.clear(); emit done(); delete m_timer; m_timer = nullptr; return; } m_numTimeout++; if (m_type == NoEventLoop) { // directly create and delete objects without eventloop in between QObject *obj = new TestObject(this); connect(obj, &QObject::destroyed, this, &TestConnections::dummySlot); delete obj; } else if (m_type == Stack) { QObject obj; connect(&obj, &QObject::destroyed, this, &TestConnections::dummySlot); disconnect(&obj, &QObject::destroyed, this, &TestConnections::dummySlot); } else if (m_type == SetParent) { auto *obj = new TestObject; obj->setParent(this); obj->child->setParent(nullptr); obj->child->setParent(obj); obj->deleteLater(); } else { // delete last objects for (int i = 0; i < m_objects.count(); ++i) { QObject *obj = m_objects.at(i); switch (m_type) { case Delete: delete obj; break; case DeleteLater: obj->deleteLater(); break; default: break; } } m_objects.clear(); // create some new objects for (int i = 0; i < OBJECTS; ++i) { QObject *obj = new TestObject(this); m_objects << obj; connect(obj, &QObject::destroyed, this, &TestConnections::dummySlot); } } } // END TestConnections // BEGIN TestThread TestThread::TestThread(TestConnections::Type type, int timeOuts, int timeoutInterval, QObject *parent) : QThread(parent) , m_type(type) , m_timeOuts(timeOuts) , m_timeoutInterval(timeoutInterval) { } TestThread::~TestThread() = default; void TestThread::run() { TestConnections tester(m_type, m_timeOuts, m_timeoutInterval == -1 ? TIMEOUTS : m_timeoutInterval); auto *loop = new QEventLoop; connect(&tester, &TestConnections::done, loop, &QEventLoop::quit); loop->exec(); delete loop; } // END TestThread // BEGIN TestWaiter void TestWaiter::addTester(TestConnections *tester) { connect(tester, &TestConnections::done, this, &TestWaiter::testerDone); m_tester << tester; } void TestWaiter::testerDone() { TestConnections *tester = qobject_cast(sender()); QVERIFY(tester); QVERIFY(m_tester.removeOne(tester)); checkFinished(); } void TestWaiter::addThread(TestThread *thread) { connect(thread, &QThread::finished, this, &TestWaiter::threadFinished); m_threads << thread; } void TestWaiter::threadFinished() { TestThread *thread = qobject_cast(sender()); QVERIFY(thread); QVERIFY(m_threads.removeOne(thread)); checkFinished(); } void TestWaiter::checkFinished() { if (!m_loop) return; if (m_threads.isEmpty() && m_tester.isEmpty()) m_loop->quit(); } void TestWaiter::startThreadsAndWaitForFinished() { if (m_threads.isEmpty() && m_tester.isEmpty()) return; for (TestThread *thread : qAsConst(m_threads)) { thread->start(); } m_loop = new QEventLoop; m_loop->exec(); delete m_loop; m_loop = nullptr; } // END TestWaiter // BEGIN TestMain TestMain::TestMain(int argc, char **argv) : m_argc(argc) , m_argv(argv) { QMetaObject::invokeMethod(this, "startTests", Qt::QueuedConnection); } void TestMain::startTests() { qApp->exit(QTest::qExec(this, m_argc, m_argv)); } void TestMain::run_data() { QTest::addColumn("type", nullptr); QTest::newRow("delete") << static_cast(TestConnections::Delete); QTest::newRow("deleteLater") << static_cast(TestConnections::DeleteLater); QTest::newRow("noEventLoop") << static_cast(TestConnections::NoEventLoop); QTest::newRow("stack") << static_cast(TestConnections::Stack); QTest::newRow("setParent") << static_cast(TestConnections::SetParent); } void TestMain::run() { QFETCH(int, type); bool manual = QProcessEnvironment::systemEnvironment().value(QStringLiteral("GAMMARAY_TEST_MANUAL")). toInt(); TestConnections tester(static_cast(type), manual ? -1 : TIMEOUTS); TestWaiter waiter; waiter.addTester(&tester); waiter.startThreadsAndWaitForFinished(); } void TestMain::threading() { TestWaiter waiter; const int timeouts = 10; // some testers to be run in the main thread // with varying timouts TestConnections tester1(TestConnections::NoEventLoop, timeouts, 10); waiter.addTester(&tester1); TestConnections tester2(TestConnections::Delete, timeouts, 11); waiter.addTester(&tester2); TestConnections tester3(TestConnections::DeleteLater, timeouts, 12); waiter.addTester(&tester3); TestConnections tester4(TestConnections::Stack, timeouts, 13); waiter.addTester(&tester4); TestConnections tester5(TestConnections::SetParent, timeouts, 14); waiter.addTester(&tester5); // now some threads TestThread thread1(TestConnections::NoEventLoop, timeouts, 10); waiter.addThread(&thread1); TestThread thread2(TestConnections::Delete, timeouts, 11); waiter.addThread(&thread2); TestThread thread3(TestConnections::DeleteLater, timeouts, 12); waiter.addThread(&thread3); TestThread thread4(TestConnections::Stack, timeouts, 13); waiter.addThread(&thread4); TestThread thread5(TestConnections::SetParent, timeouts, 13); waiter.addThread(&thread5); waiter.startThreadsAndWaitForFinished(); } // END TestMain int main(int argc, char *argv[]) { QApplication app(argc, argv); TestMain tc(argc, argv); return app.exec(); } gammaray-2.11.3/tests/test_connections.h000066400000000000000000000060111413310443000202570ustar00rootroot00000000000000/* test_connections.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_TEST_CONNECTIONS_H #define GAMMARAY_TEST_CONNECTIONS_H #include #include QT_BEGIN_NAMESPACE class QTimer; class QEventLoop; QT_END_NAMESPACE class TestObject : public QObject { Q_OBJECT public: explicit TestObject(QObject *parent = nullptr); ~TestObject() override; private slots: void dummySlot() { } public: QObject *child; }; class TestConnections : public QObject { Q_OBJECT public: enum Type { DeleteLater, Delete, NoEventLoop, Stack, SetParent }; TestConnections(Type type, int timeOuts, int timeoutInterval = -1); ~TestConnections() override; public slots: void timeout(); private slots: void dummySlot() { } signals: void done(); private: const Type m_type; const int m_timeOuts; int m_numTimeout; QList m_objects; QTimer *m_timer; }; class TestThread : public QThread { Q_OBJECT public: TestThread(TestConnections::Type type, int timeOuts, int timeoutInterval = -1, QObject *parent = nullptr); ~TestThread() override; void run() override; private: TestConnections::Type m_type; int m_timeOuts; int m_timeoutInterval; }; class TestMain : public QObject { Q_OBJECT public: TestMain(int argc, char **argv); public slots: void startTests(); private slots: void run_data(); void run(); void threading(); private: int m_argc; char **m_argv; }; class TestWaiter : public QObject { Q_OBJECT public: void addThread(TestThread *thread); void addTester(TestConnections *tester); void startThreadsAndWaitForFinished(); private slots: void testerDone(); void threadFinished(); private: void checkFinished(); QList m_threads; QList m_tester; QEventLoop *m_loop = nullptr; }; #endif // TEST_CONNECTIONS_H gammaray-2.11.3/tests/testhelpers.cpp000066400000000000000000000064271413310443000176060ustar00rootroot00000000000000/* testhelpers.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Filipe Azevedo Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "testhelpers.h" #include #include #include static QModelIndex searchMatchesOne(QAbstractItemModel *model, const QVariant &value, int role, int column, Qt::MatchFlags flags) { const auto matches = model->match(model->index(0, column), role, value, 1, flags); if (matches.isEmpty()) return {}; const QModelIndex idx = matches.at(0); Q_ASSERT(idx.isValid()); return idx; } static QModelIndexList searchMatchesAll(QAbstractItemModel *model, const QVariant &value, int role, int column, Qt::MatchFlags flags) { const auto matches = model->match(model->index(0, column), role, value, -1, flags); for (const QModelIndex &idx : matches) { Q_UNUSED(idx); Q_ASSERT(idx.isValid()); } return matches; } bool TestHelpers::waitForSignal(QSignalSpy *spy, bool keepResult) { if (spy->isEmpty()) spy->wait(1000); bool result = !spy->isEmpty(); if (!keepResult) spy->clear(); return result; } QModelIndex TestHelpers::searchFixedIndex(QAbstractItemModel *model, const QString &value, Qt::MatchFlags extra, int role, int column) { return searchMatchesOne(model, value, role, column, Qt::MatchFixedString | extra); } QModelIndexList TestHelpers::searchFixedIndexes(QAbstractItemModel *model, const QString &value, Qt::MatchFlags extra, int role, int column) { return searchMatchesAll(model, value, role, column, Qt::MatchFixedString | extra); } QModelIndex TestHelpers::searchContainsIndex(QAbstractItemModel *model, const QString &value, Qt::MatchFlags extra, int role, int column) { return searchMatchesOne(model, value, role, column, Qt::MatchContains | extra); } QModelIndexList TestHelpers::searchContainsIndexes(QAbstractItemModel *model, const QString &value, Qt::MatchFlags extra, int role, int column) { return searchMatchesAll(model, value, role, column, Qt::MatchContains | extra); } gammaray-2.11.3/tests/testhelpers.h000066400000000000000000000053711413310443000172500ustar00rootroot00000000000000/* testhelpers.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Filipe Azevedo Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef TESTHELPERS_H #define TESTHELPERS_H #include QT_BEGIN_NAMESPACE template class QList; class QString; class QSignalSpy; class QAbstractItemModel; class QModelIndex; typedef QList QModelIndexList; QT_END_NAMESPACE namespace TestHelpers { bool waitForSignal(QSignalSpy *spy, bool keepResult = false); QModelIndex searchFixedIndex(QAbstractItemModel *model, const QString &value, Qt::MatchFlags extra = Qt::MatchExactly, int role = Qt::DisplayRole, int column = 0); QModelIndexList searchFixedIndexes(QAbstractItemModel *model, const QString &value, Qt::MatchFlags extra = Qt::MatchExactly, int role = Qt::DisplayRole, int column = 0); QModelIndex searchContainsIndex(QAbstractItemModel *model, const QString &value, Qt::MatchFlags extra = Qt::MatchExactly, int role = Qt::DisplayRole, int column = 0); QModelIndexList searchContainsIndexes(QAbstractItemModel *model, const QString &value, Qt::MatchFlags extra = Qt::MatchExactly, int role = Qt::DisplayRole, int column = 0); } #define QVERIFY_RETURN_FALSE(statement) \ do {\ if (!QTest::qVerify(static_cast(statement), #statement, "", __FILE__, __LINE__))\ return false;\ } while (0) #define QCOMPARE_RETURN_FALSE(actual, expected) \ do {\ if (!QTest::qCompare(actual, expected, #actual, #expected, __FILE__, __LINE__))\ return false;\ } while (0) #endif // TESTHELPERS_H gammaray-2.11.3/tests/tests-qt-ci-only.pro000066400000000000000000000006021413310443000204030ustar00rootroot00000000000000# qmake -> cmake wrapper for the Qt CI system # # DO NOT USE THIS UNLESS YOU KNOW WHAT YOU ARE DOING! # # This only supports a tiny subset of the CMake build system, # hardcoded for the needs of integration with the Qt CI system. # # If you are building GammaRay yourself, please use CMake as # described in Install. txt # empty dummy file to make the CI not choke here TEMPLATE = aux gammaray-2.11.3/tests/timertoptest.cpp000066400000000000000000000165251413310443000200070ustar00rootroot00000000000000/* timertoptest.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "baseprobetest.h" #include "testhelpers.h" #include #include #include #include <3rdparty/qt/modeltest.h> #include #include using namespace GammaRay; using namespace TestHelpers; class Deleter : public QObject { Q_OBJECT public: explicit Deleter(QObject *parent = nullptr) : QObject(parent) { } public slots: void deleteSender() { delete sender(); } }; class TimerTopTest : public BaseProbeTest { Q_OBJECT private: void createProbe() override { BaseProbeTest::createProbe(); auto t = new QTimer; // trigger timer plugin activation QTest::qWait(1); delete t; QTest::qWait(1); } private slots: void testTimerCreateDestroy() { createProbe(); auto *model = ObjectBroker::model(QStringLiteral("com.kdab.GammaRay.TimerModel")); QVERIFY(model); ModelTest modelTest(model); auto baseRowCount = model->rowCount(); QVERIFY(baseRowCount >= 0); QVERIFY(!searchFixedIndex(model, "timer1").isValid()); auto t1 = new QTimer; t1->setObjectName("timer1"); QTest::qWait(1); QCOMPARE(model->rowCount(), baseRowCount + 1); auto idx = searchFixedIndex(model, "timer1"); QVERIFY(idx.isValid()); QCOMPARE(idx.data(ObjectModel::ObjectIdRole).value(), ObjectId(t1)); delete t1; QTest::qWait(1); QCOMPARE(model->rowCount(), baseRowCount); } void testTimerActivation() { createProbe(); auto *model = ObjectBroker::model(QStringLiteral("com.kdab.GammaRay.TimerModel")); QVERIFY(model); ModelTest modelTest(model); auto t1 = new QTimer; t1->setObjectName("timer1"); t1->setInterval(10); t1->setSingleShot(true); QTest::qWait(1); auto idx = searchFixedIndex(model, "timer1"); QVERIFY(idx.isValid()); // TODO verify data QSignalSpy dataChangeSpy(model, SIGNAL(dataChanged(QModelIndex,QModelIndex))); QVERIFY(dataChangeSpy.isValid()); t1->start(); QTest::qWait(10 * 1000); // there's a 5sec throttle on dataChanged // TODO verify data QVERIFY(!dataChangeSpy.isEmpty()); QVERIFY(dataChangeSpy.size() < 5); delete t1; QTest::qWait(1); } void testTimerEvent() { createProbe(); auto *model = ObjectBroker::model(QStringLiteral("com.kdab.GammaRay.TimerModel")); QVERIFY(model); auto idx = searchFixedIndex(model, "testObject"); QVERIFY(!idx.isValid()); const auto prevRowCount = model->rowCount(); setObjectName("testObject"); auto timerId = startTimer(10); // The TimerModel does batch all by a 5000ms timer. QTest::qWait(5000); // Wait for the free timer discovery int i = 0; while (model->rowCount() == prevRowCount && i++ < 10) QTest::qWait(100); idx = searchFixedIndex(model, "testObject"); QVERIFY(idx.isValid()); QCOMPARE(idx.data(ObjectModel::ObjectIdRole).value(), ObjectId(this)); idx = idx.sibling(idx.row(), 6); QVERIFY(idx.isValid()); QCOMPARE(idx.data().toInt(), timerId); killTimer(timerId); // remove free timers from model QMetaObject::invokeMethod(model, "clearHistory"); } void testTimerMultithreading() { createProbe(); { auto *model = ObjectBroker::model(QStringLiteral("com.kdab.GammaRay.TimerModel")); QVERIFY(model); QSharedPointer mainThread(new QThread); mainThread->setObjectName("mainThread"); // main thread free timer operating on "mainThread" object mainThread->startTimer(125); // main thread qtimer QSharedPointer mainTimer(new QTimer); mainTimer->setObjectName("mainTimer"); mainTimer->setInterval(250); mainTimer->start(); // "mainThread" thread "threadTimer" qtimer QSharedPointer threadTimer(new QTimer); threadTimer->setObjectName("threadTimer"); threadTimer->setInterval(100); threadTimer->moveToThread(mainThread.data()); connect(mainThread.data(), &QThread::started, threadTimer.data(), static_cast(&QTimer::start)); int timerId = -1; QTimer::singleShot(500, threadTimer.data(), [&]() { // "threadTimer" thread free timer operating on "threadTimer" object timerId = threadTimer->QObject::startTimer(250); }); QSharedPointer deleter(new Deleter); auto deleteTimer = new QTimer; deleteTimer->setObjectName("deleteTimer"); deleteTimer->setInterval(1500); connect(deleteTimer, &QTimer::timeout, deleter.data(), &Deleter::deleteSender); deleteTimer->start(); mainThread->start(); QTest::qWait(6000); QModelIndex idx; QVERIFY(model->rowCount() >= 4); idx = searchFixedIndex(model, "mainThread"); QVERIFY(idx.isValid()); QCOMPARE(idx.data(ObjectModel::ObjectIdRole).value(), ObjectId(mainThread.data())); idx = searchFixedIndex(model, "mainTimer"); QVERIFY(idx.isValid()); QCOMPARE(idx.data(ObjectModel::ObjectIdRole).value(), ObjectId(mainTimer.data())); const auto idxs = searchFixedIndexes(model, "threadTimer"); QCOMPARE(idxs.count(), 2); for (const QModelIndex &idx : idxs) { QVERIFY(idx.isValid()); } QTimer::singleShot(0, threadTimer.data(), [&]() { threadTimer->QObject::killTimer(timerId); threadTimer->stop(); mainThread->quit(); }); QVERIFY(mainThread->wait()); // remove free timers from model QMetaObject::invokeMethod(model, "clearHistory"); } QTest::qWait(1); } }; QTEST_MAIN(TimerTopTest) #include "timertoptest.moc" gammaray-2.11.3/tests/toolmanagertest.cpp000066400000000000000000000231231413310443000204440ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "baseprobetest.h" #include #include #include #include <3rdparty/qt/modeltest.h> #include #include #include #include using namespace GammaRay; Q_DECLARE_METATYPE(QVector) class ToolManagerTest : public BaseProbeTest { Q_OBJECT private: int visibleRowCount(QAbstractItemModel *model) { int count = 0; for (int i = 0; i < model->rowCount(); ++i) { auto idx = model->index(i, 1); if (!idx.data(Qt::DisplayRole).toString().startsWith(QLatin1String("QDesktop"))) ++count; } return count; } private slots: void initTestCase() { qRegisterMetaType >(); new ClientToolManager; } void init() { delete Probe::instance(); createProbe(); } void testProbeSide() { auto *toolManager = ObjectBroker::object(); QVERIFY(toolManager); QSignalSpy availableToolsSpy(toolManager, &ToolManagerInterface::availableToolsResponse); QSignalSpy toolEnabledSpy(toolManager, &ToolManagerInterface::toolEnabled); QSignalSpy toolSelectedSpy(toolManager, &ToolManagerInterface::toolSelected); QSignalSpy toolsForObjectSpy(toolManager, &ToolManagerInterface::toolsForObjectResponse); toolManager->requestAvailableTools(); availableToolsSpy.wait(500); QCOMPARE(availableToolsSpy.size(), 1); const auto &list = availableToolsSpy[0][0].value >(); QVERIFY(!list.isEmpty()); bool hasBasicTools = false; const ToolData *actionInspector = nullptr; const ToolData *guiSupport = nullptr; for (const auto &tool : list) { if (tool.id == "GammaRay::ObjectInspector") hasBasicTools = true; else if (tool.id == "gammaray_actioninspector") actionInspector = &tool; else if (tool.id == "gammaray_guisupport") guiSupport = &tool; } QVERIFY(hasBasicTools); QVERIFY(actionInspector); QCOMPARE(actionInspector->enabled, false); /* coverity[UNINIT_CTOR] */ QCOMPARE(actionInspector->hasUi, true); /* coverity[UNINIT_CTOR] */ QVERIFY(guiSupport); QCOMPARE(guiSupport->enabled, true); /* coverity[UNINIT_CTOR] */ QCOMPARE(guiSupport->hasUi, false); /* coverity[UNINIT_CTOR] */ // Create QAction to enable action inspector QAction action("Test Action", this); toolEnabledSpy.wait(1000); QVERIFY(!toolEnabledSpy.isEmpty()); QStringList enabledTools; for (auto i = toolEnabledSpy.constBegin(); i != toolEnabledSpy.constEnd(); ++i) enabledTools << i->first().toString(); QVERIFY(enabledTools.contains("gammaray_actioninspector")); toolManager->selectObject(ObjectId(&action), QStringLiteral("gammaray_actioninspector")); toolSelectedSpy.wait(50); QCOMPARE(toolSelectedSpy.size(), 1); QString selectedTool = toolSelectedSpy.first().first().toString(); QCOMPARE(selectedTool, QStringLiteral("gammaray_actioninspector")); toolManager->requestToolsForObject(ObjectId(&action)); toolsForObjectSpy.wait(50); QCOMPARE(toolsForObjectSpy.size(), 1); const ObjectId &actionId = toolsForObjectSpy.first().first().value(); QCOMPARE(actionId.asQObject(), &action); const auto &actionTools = toolsForObjectSpy.first().last().value >(); QStringList supportedToolIds; for (const auto &tool : actionTools) supportedToolIds << tool; QVERIFY(supportedToolIds.contains(QStringLiteral("GammaRay::ObjectInspector"))); QVERIFY(supportedToolIds.contains(QStringLiteral("GammaRay::MetaObjectBrowser"))); QVERIFY(supportedToolIds.contains(QStringLiteral("gammaray_actioninspector"))); } void testClientSide() { ClientToolManager::instance()->requestAvailableTools(); ModelTest modelTest(ClientToolManager::instance()->model()); // we're testing inprocess, thus tool list should be available instantly. QVERIFY(ClientToolManager::instance()->isToolListLoaded()); testHasBasicTools(false); testToolEnabled(); testToolSelected(); testRequestToolsForObject(); testClearance(); ClientToolManager::instance()->requestAvailableTools(); // "reconnect" testHasBasicTools(true); testToolSelected(); testRequestToolsForObject(); } private: void testHasBasicTools(bool actionInspectorEnabled) { bool hasBasicTools = false; const ToolInfo *actionInspector = nullptr; const ToolInfo *guiSupport = nullptr; foreach (const ToolInfo &tool, ClientToolManager::instance()->tools()) { if (tool.id() == "GammaRay::ObjectInspector") hasBasicTools = true; else if (tool.id() == "gammaray_actioninspector") actionInspector = &tool; else if (tool.id() == "gammaray_guisupport") guiSupport = &tool; } QVERIFY(hasBasicTools); QVERIFY(actionInspector); QCOMPARE(actionInspector->isEnabled(), actionInspectorEnabled); QCOMPARE(actionInspector->hasUi(), true); QVERIFY(!guiSupport); // tools without ui are supposed to be filtered out QVERIFY(!ClientToolManager::instance()->widgetForId("inexistantTool")); QVERIFY(actionInspectorEnabled == (bool)ClientToolManager::instance()->widgetForId("gammaray_actioninspector")); // if tool is disabled we explicitly want widgetForId to be null. } void testToolEnabled() { QSignalSpy toolEnabledSpy(ClientToolManager::instance(), &ClientToolManager::toolEnabled); // Create QAction to enable action inspector QAction action("Test Action", this); toolEnabledSpy.wait(50); QVERIFY(!toolEnabledSpy.isEmpty()); QStringList enabledTools; for (auto i = toolEnabledSpy.constBegin(); i != toolEnabledSpy.constEnd(); ++i) enabledTools << i->first().toString(); QVERIFY(enabledTools.contains("gammaray_actioninspector")); QVERIFY(ClientToolManager::instance()->widgetForId("gammaray_actioninspector")); } void testToolSelected() { auto *toolManager = ObjectBroker::object(); QVERIFY(toolManager); QSignalSpy toolSelectedSpy(ClientToolManager::instance(), &ClientToolManager::toolSelected); QAction action("Test Action", this); toolManager->selectObject(ObjectId(&action), QStringLiteral("gammaray_actioninspector")); toolSelectedSpy.wait(50); QCOMPARE(toolSelectedSpy.size(), 1); QString selectedTool = toolSelectedSpy.first().first().toString(); QCOMPARE(selectedTool, QStringLiteral("gammaray_actioninspector")); } void testRequestToolsForObject() { QSignalSpy toolsForObjectSpy(ClientToolManager::instance(), &ClientToolManager::toolsForObjectResponse); QAction action("Test Action", this); ClientToolManager::instance()->requestToolsForObject(ObjectId(&action)); toolsForObjectSpy.wait(50); QCOMPARE(toolsForObjectSpy.size(), 1); const ObjectId &actionId = toolsForObjectSpy.first().first().value(); QCOMPARE(actionId.asQObject(), &action); const auto &actionTools = toolsForObjectSpy.first().last().value >(); QStringList supportedToolIds; for (const auto &tool : actionTools) { QVERIFY(!tool.name().isEmpty()); QVERIFY(tool.name() != tool.id()); supportedToolIds << tool.id(); } QVERIFY(supportedToolIds.contains(QStringLiteral("GammaRay::ObjectInspector"))); QVERIFY(supportedToolIds.contains(QStringLiteral("GammaRay::MetaObjectBrowser"))); QVERIFY(supportedToolIds.contains(QStringLiteral("gammaray_actioninspector"))); } void testClearance() { QSignalSpy resetSpy(ClientToolManager::instance()->model(), &QAbstractItemModel::modelReset); ClientToolManager::instance()->clear(); QVERIFY(ClientToolManager::instance()); QCOMPARE(ClientToolManager::instance()->tools().size(), 0); resetSpy.wait(50); QCOMPARE(resetSpy.size(), 1); } }; QTEST_MAIN(ToolManagerTest) #include "toolmanagertest.moc" gammaray-2.11.3/tests/translatortest.cpp000066400000000000000000000100171413310443000203230ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "baseprobetest.h" #include #include <3rdparty/qt/modeltest.h> #include #include #include using namespace GammaRay; class TranslatorTest : public BaseProbeTest { Q_OBJECT private slots: void testCreateDestroy() { createProbe(); auto t1 = new QTranslator; t1->setObjectName(QStringLiteral("t1")); t1->load(QLibraryInfo::location(QLibraryInfo::TranslationsPath) + QStringLiteral( "/qt_sv.qm")); QVERIFY(!t1->isEmpty()); QCoreApplication::installTranslator(t1); QTest::qWait(1); auto *model = ObjectBroker::model(QStringLiteral("com.kdab.GammaRay.TranslatorsModel")); QVERIFY(model); ModelTest modelTest(model); QCOMPARE(model->rowCount(), 2); auto t2 = new QTranslator; t2->setObjectName(QStringLiteral("t2")); t2->load(QStringLiteral("I_DONT_EXIST_de.qm")); QVERIFY(t2->isEmpty()); QCoreApplication::installTranslator(t2); QTest::qWait(1); QCOMPARE(model->rowCount(), 2); // empty will not get language change events, so we don't notice it yet, FIXME? QCoreApplication::translate("my context", "translate me 1"); QTest::qWait(1); QCoreApplication::removeTranslator(t1); delete t1; QTest::qWait(1); QCOMPARE(model->rowCount(), 2); // see above, we now noticed the empty translator QTest::qWait(1); QCoreApplication::removeTranslator(t2); delete t2; QTest::qWait(1); } void testTranslate() { createProbe(); auto t1 = new QTranslator; t1->setObjectName(QStringLiteral("t1")); t1->load(QLibraryInfo::location(QLibraryInfo::TranslationsPath) + QStringLiteral( "/qt_sv.qm")); QVERIFY(!t1->isEmpty()); QCoreApplication::installTranslator(t1); QTest::qWait(1); auto *translatorModel = ObjectBroker::model(QStringLiteral("com.kdab.GammaRay.TranslatorsModel")); QVERIFY(translatorModel); ModelTest TranslationsModelTest(translatorModel); QCOMPARE(translatorModel->rowCount(), 2); auto translatorSelection = ObjectBroker::selectionModel(translatorModel); QVERIFY(translatorSelection); translatorSelection->select(translatorModel->index(0, 0), QItemSelectionModel::ClearAndSelect); auto *model = ObjectBroker::model(QStringLiteral("com.kdab.GammaRay.TranslationsModel")); QVERIFY(model); ModelTest modelTest(model); QCoreApplication::translate("context", "key", nullptr); QCoreApplication::translate(nullptr, "key", nullptr); QCoreApplication::translate(nullptr, "key", "disambiguation"); QCoreApplication::translate("context", "key", "disambiguation"); delete t1; QTest::qWait(1); } }; QTEST_MAIN(TranslatorTest) #include "translatortest.moc" gammaray-2.11.3/tests/widgettest.cpp000066400000000000000000000057611413310443000174270ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "baseprobetest.h" #include #include <3rdparty/qt/modeltest.h> #include #include using namespace GammaRay; class WidgetTest : public BaseProbeTest { Q_OBJECT private: int visibleRowCount(QAbstractItemModel *model) { int count = 0; for (int i = 0; i < model->rowCount(); ++i) { auto idx = model->index(i, 1); if (!idx.data(Qt::DisplayRole).toString().startsWith(QLatin1String("QDesktop"))) ++count; } return count; } private slots: void testWidgetReparent() { createProbe(); // we need one widget for the plugin to activate, otherwise the model will not be available auto w1 = new QWidget; QTest::qWait(1); // event loop re-entry auto *model = ObjectBroker::model(QStringLiteral("com.kdab.GammaRay.WidgetTree")); QVERIFY(model); ModelTest modelTest(model); auto w2 = new QWidget; QTest::qWait(1); // event loop re-entry QCOMPARE(visibleRowCount(model), 2); w2->setParent(w1); QTest::qWait(1); // event loop re-entry QCOMPARE(visibleRowCount(model), 1); w2->setParent(w1); QTest::qWait(1); // event loop re-entry QCOMPARE(visibleRowCount(model), 1); auto w3 = new QWidget; w2->setParent(w3); // reparent without event loop reentry! QTest::qWait(1); // event loop re-entry QCOMPARE(visibleRowCount(model), 2); delete w2; QTest::qWait(1); // event loop re-entry QCOMPARE(visibleRowCount(model), 2); delete w1; QTest::qWait(1); // event loop re-entry QCOMPARE(visibleRowCount(model), 1); delete w3; QTest::qWait(1); // event loop re-entry QCOMPARE(visibleRowCount(model), 0); } }; QTEST_MAIN(WidgetTest) #include "widgettest.moc" gammaray-2.11.3/translations/000077500000000000000000000000001413310443000161065ustar00rootroot00000000000000gammaray-2.11.3/translations/CMakeLists.txt000066400000000000000000000036661413310443000206610ustar00rootroot00000000000000set(LANGUAGES en de) # translation tools add_executable(ljsonupdate ljsonupdate.cpp) target_link_libraries(ljsonupdate Qt5::Core) # ts file update (manual target, affects source dir) file(GLOB_RECURSE jsons "${CMAKE_SOURCE_DIR}/plugins/*.json") add_custom_command( OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/plugin_meta_data.cpp COMMAND ljsonupdate --extract --out ${CMAKE_CURRENT_SOURCE_DIR}/plugin_meta_data.cpp ${jsons} DEPENDS ${jsons} ) add_custom_target(ts) foreach(LANG ${LANGUAGES}) add_custom_target( ts_${LANG} COMMAND Qt5::lupdate 3rdparty app client common core inprocessui launcher plugins ui ${CMAKE_CURRENT_SOURCE_DIR}/plugin_meta_data.cpp -locations relative -no-ui-lines -no-obsolete -ts ${CMAKE_CURRENT_SOURCE_DIR}/gammaray_${LANG}.ts WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/plugin_meta_data.cpp ) add_dependencies(ts ts_${LANG}) endforeach() # qm generation and installation foreach(LANG ${LANGUAGES}) add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/gammaray_${LANG}.qm COMMAND Qt5::lrelease ${CMAKE_CURRENT_SOURCE_DIR}/gammaray_${LANG}.ts -qm ${CMAKE_CURRENT_BINARY_DIR}/gammaray_${LANG}.qm DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/gammaray_${LANG}.ts ) add_custom_target(gammaray_${LANG}_qm ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/gammaray_${LANG}.qm) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/gammaray_${LANG}.qm DESTINATION ${TRANSLATION_INSTALL_DIR}) endforeach() # message merging (manual target, affects source dir) add_custom_target(tsmerge) foreach(LANG ${LANGUAGES}) add_custom_target( tsmerge_${LANG} COMMAND ljsonupdate --merge --qm ${CMAKE_CURRENT_BINARY_DIR}/gammaray_${LANG}.qm --lang ${LANG} ${jsons} DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/gammaray_${LANG}.qm ) add_dependencies(tsmerge tsmerge_${LANG}) endforeach() gammaray-2.11.3/translations/gammaray_de.ts000066400000000000000000005245441413310443000207420ustar00rootroot00000000000000 GammaRay::AboutDataContext Unable to read the Authors list Autorenliste konnte nicht geladen werden <b>GammaRay %1</b> <b>GammaRay %1</b> <p>The Qt application inspection and manipulation tool.Learn more at <a href="https://www.kdab.com/gammaray">https://www.kdab.com/gammaray/</a>.</p><p>Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, <a href="mailto:info@kdab.com">info@kdab.com</a></p><p>StackWalker code Copyright (c) 2005-2019, Jochen Kalmbach, All rights reserved<br>lz4 fast LZ compression code Copyright (C) 2011-2015, Yann Collet, All rights reserved<br>backward-cpp code Copyright 2013-2017 Google Inc. All rights reserved.</p> <p><u>Authors:</u><br>%1</p> <p><u>Autoren:</u><br>%1</p> <p>GammaRay and the GammaRay logo are registered trademarks of Klarälvdalens Datakonsult AB in the European Union, the United States and/or other countries. Other product and company names and logos may be trademarks or registered trademarks of their respective companies.</p><br> GammaRay::AboutPluginsDialog Loaded Plugins Geladene Plugins Failed Plugins Plugins mit Fehlern GammaRay: Plugin Info GammaRay: Plugin Informationen GammaRay::AbstractAttributeModel Attribute Attribut GammaRay::AbstractConnectionsModel Auto Auto Auto (%1) Auto (%1) Direct Direkt Queued Blocking Blockierend Unknown: %1 Unbekannt: %1 Connections exists multiple times. The connected slot is called multiple times when the signal is emitted. Die Verbindung existiert mehrfach. Der verbundene Slot wird daher mehrfach ausgeführt sobald das Signal ausgesandt wird. Direct cross-thread connection. The connected slot is called in the context of the emitting thread. Direkte Verbindung über Thread-Grenze. Der verbundene Slot wird im Kontext des Threads des ausgesandten Signals ausgeführt. Type Typ <destroyed> <zerstört> <unknown> <unbekannt> GammaRay::AllCodecsModel Codec Aliases GammaRay::AttachDialog <html><head/><body><p align="center"><img src=":/gammaray/GammaRay-48x48.png"/></p><p align="center">Retrieving the list of processes which use Qt ...</p></body></html> <html><head/><body><p align="center"><img src=":/gammaray/GammaRay-48x48.png"/></p><p align="center">Auflisten der Qt-Prozesse ...</p></body></html> Pro&be: &Access mode: &Zugriffsmodus: Out-of-process, local debugging only Out-of-process, remote debugging enabled In-process GammaRay - Attach to Process GammaRay - An Prozess Anhängen GammaRay::BindingModel Property Eigenschaft Value Wert Source Quelle Depth Tiefe GammaRay::Client Unsupported transport protocol. Nicht unterstütztes Transportprotokoll. Protocol violation, first message is not the server version. Protokollfehler, die erste Nachricht enthielt nicht die Serverversion. Gammaray Protocol Mismatch. Probe version is %1, was expecting %2. Gammaray Protokollinkompatibilität Die Anwendung verwendet Version %1, der Client erwartet Version %2. GammaRay::ClientActionModel Warning: Ambiguous shortcut detected. Warnung: Uneindeutiges Tastenkürzel. Object Objekt Name Name Checkable Auswählbar Checked Ausgewählt Priority Priorität Shortcut(s) Tastenkürzel GammaRay::ClientConnectionManager Connection refused. Verbindung abgelehnt. Lost connection to remote host: %1 Verbindung zum Host verloren: %1 Could not establish connection to remote host: %1 Konnte keine Verbindung zum Host aufbauen: %1 GammaRay - Connection Error GammaRay - Verbindungsfehler GammaRay::ClientMethodModel Method Methode Constructor Konstruktor Slot Slot Signal Signal Unknown Unbekannt Public Öffentlich Protected Geschützt Private Privat Tag: %1 Tag: %1 <none> <keins> Revision: %1 Revision: %1 overrides base class signal überschreibt Signal einer Basisklasse uses parameter type not registerd with the meta type system verwendet Parametertypen die nicht beim Metatyp-System registriert sind Issues: %1 Probleme: %1 Signature Signatur Type Typ Access Zugriff Class Klasse GammaRay::ClientNetworkConfigurationModel Name Name Identifier Bearer Timeout Roaming Purpose Zweck State Zustand Type Typ GammaRay::ClientPropertyModel constant konstant designable final final resetable zurücksetzbar scriptable stored user writable schreibbar Attributes: %1 Attribute: %1 Revision: %1 Revision: %1 Notify signal: %1 Benachrichtigungssignal: %1 Property Eigenschaft Value Wert Type Typ Class Klasse GammaRay::ClientTimerModel Invalid Ungültig QQmlTimer QQmlTimer QTimer QTimer Free Timer Object Name Objekt Name State Zustand Total Wakeups Wakeups/Sec Time/Wakeup [uSecs] Max Wakeup Time [uSecs] Timer ID Timer ID None (%1 ms) Inactive (%1 ms) Inaktive (%1 ms) Singleshot (%1 ms) Einmalig (%1 ms) Repeating (%1 ms) Wiederholend (%1 ms) 0 0 N/A GammaRay::ClientToolModel This tool does not work in out-of-process mode. Diese Ansicht funktioniert nicht außerhalb des Zielprozesses. GammaRay::CodeEditor Syntax Highlighting Syntaxhervorhebung GammaRay::CodecBrowserWidget Enter text for preview... Text für Vorschau eingeben... GammaRay::ConnectPage Connect to a running remote instance. Mit laufender Instanz verbinden. Url: URL: No port given, will use 11732 Kein Port angegeben, 11732 wird verwendet File is not a socket GammaRay::ConnectionsTab &Inbound Connections: &Eingehende Verbindungen: &Outbound Connections: &Ausgehende Verbindungen: Go to sender Zu Sender gehen Go to receiver Zu Empfänger gehen GammaRay::ContextMenuExtension Go to: %1 Gehe zu: %1 Show source: %1 Quelltext anzeigen: %1 Go to creation: %1 Zu Erstellung gehen: %1 Go to declaration: %1 Gehe zu Deklaration: %1 Show in "%1" tool In Ansicht "%1" anzeigen GammaRay::CookieJarModel Name Name Domain Domain Path Pfad Value Wert Expiration Date Ablaufdatum Http Only Nur HTTP Secure Sicher Session Cookie Session Cookie GammaRay::DebuggerInjector Process stopped. Prozess beendet. The debugger executable '%1' could not be found Der Debugger '%1' konnte nicht gefunden werden Yama security extension is blocking runtime attaching, see /proc/sys/kernel/yama/ptrace_scope Das Yama Sicherheitsmodul blockiert den Debugger, siehe /proc/sys/kernel/yama/ptrace_scope GammaRay::DynamicPropertyAdaptor <dynamic> <dynamisch> GammaRay::EnvironmentModel Environment Variable Umgebungsvariable Value Wert GammaRay::FontBrowserWidget The quick brown fox jumps over the lazy dog Enter some text here... Point Size: Bold Fett Italic Kursiv Underline Unterstrichen GammaRay::FontDatabaseModel Fonts Schriften Smooth Sizes GammaRay::FontModel <no text> <kein Text> GammaRay::GdbInjector The debuggee application is missing debug symbols which are required for GammaRay's GDB injector. Please recompile the debuggee. GDB error was: %1 Für die Anwendung konnten keine Debug-Informationen gefunden werden. Diese werden für GammaRay's GDB Injektor benötigt. Bitte kompilieren Sie die Anwendung mit Debug-Informationen. GDB Fehler: %1 Your QtCore library is missing debug symbols which are required for GammaRay's GDB injector. Please install the required debug symbols. GDB error was: %1 Für die QtCore Bibliothek konnten keine Debug-Informationen gefunden werden. Bitte installieren Sie die benötigten Debug-Informationen. GDB Fehler: %1 The debuggee application seems to have an invalid stack trace This can be caused by the executable being updated on disk after launching it. GDB error was: %1 Ungülter Stack-Trace der Anwendung. Dies kann durch eine zwischenzeitliche Veränderung der Anwendung seit ihrem Start verursacht worden sein. GDB Fehler: %1 GammaRay::GraphicsSceneView Scene Coordinates: Szenen Koordinaten: 0x0 0x0 Item Coordinates: Item Koordinaten: GammaRay::GridSettingsWidget Grid enabled Gitter aktiviert X Offset: X Abstand: Y Offset: Y Abstand: Cell Width: Zellenbreite: Cell Height: Zellenhöhe: GammaRay::GuiSupport (Injected by GammaRay) variable variabel fixed fest percentage prozentual <empty> <leer> <%1 elements> <%1 Elemente> width: %1 Breite: %1 brush: %1 miter limit: %1 dash pattern: (%1) dash offset: %1 [%1]: %2 [%1]: %2 GammaRay::GuiSupportUiFactory Paint Analyzer Zeichenanalyse GammaRay::InboundConnectionsModel <slot object context> <Slot Objekt Kontext> Sender Sender Signal Signal Slot Slot GammaRay::ItemDelegate (Item %r) (Element %r) GammaRay::KJobModel Job Job Type Typ Status Status Deleted Gelöscht Killed Abgebrochen GammaRay::LaunchPage &Executable: &Programm: ... ... &Program arguments: &Programm Argumente: &Add &Hinzufügen &Remove &Entfernen Pro&be: &Access Mode: &Zugriffsmodus: Out-of-process, local-only Out-of-process, remote debugging enabled In-process Executable to Launch Auszuführendes Programm Executable (*.exe) Programm (*.exe) Working Directory Arbeitsverzeichnis Working Directory: Arbeitsverzeichnis: GammaRay::Launcher Uh-oh, there is no default attach injector on this platform. Uh-oh, there is no default launch injector on this platform. Injector %1 not found. Injektor %1 konnte nicht gefunden werden. Failed to launch target '%1'. Konnte Anwendung nicht starten: '%1'. Failed to attach to target with PID %1. Anhängen an Prozess mit PID %1 fehlgeschlagen. Error: %1 Fehler: %1 GammaRay::LauncherWindow Attach Anhängen Launch Starten Connect Verbinden Self Test Selbsttest About Über GammaRay Launcher GammaRay Launcher GammaRay::LegendModel Bounding rect Geometry rect Children rect Transform origin Transformationsursprung Coordinates (x, y...) Koordinaten Margins/Anchors Ränder/Anker Padding Grid Hilfsgitter GammaRay::LibraryInfoModel Location Quelle Path Pfad GammaRay::LldbInjector The debugger version can't be read (%1) Die Version des Debuggers kann nicht ausgelesen werden (%1) The LLDB version is not compatible: %1 (%2.%3 or higher required) Die LLDB Version ist inkompatibel: %1 (%2.%3 oder neuer wird benötigt) LLDB does not support scripting. Install lldb python support please. LLDB Python Skript-Unterstützung ist nicht installiert. GammaRay::LocaleInspectorWidget Locales Timezones Zeitzonen GammaRay::LogView Messages Nachrichten Timeline Zeitleiste GammaRay::LoggingCategoryModel Category Kategorie Debug Debug Info Info Warning Warnung Critical Kritisch GammaRay::MainWindow &GammaRay &GammaRay &Help &Hilfe &Diagnostics &Diagnose &Actions &Aktionen Setti&ngs &Einstellungen Main Toolbar Werkzeugleiste &Detach Retract probe from application. &Quit &Beenden Quit probed application. Beendet die untersuchte Anwendung. About &GammaRay Über &GammaRay About &Qt Über &Qt About &KDAB Über &KDAB &Plugins... &Plugins... &Message Statistics... &Nachrichtenstatistik... Show GammaRay communication statistics. GammaRay Kommunikationsstatistiken anzeigen. KDevelop KDevelop Kate Kate KWrite KWrite gedit gedit gvim gvim Qt Creator Qt Creator GammaRay (%1) GammaRay (%1) Code Navigation Quellcode Navigation Custom... Benutzerdefiniert... Automatic (No Line numbers) Automatisch (ohne Zeilennummern) Usage ratio of the GammaRay tools. Nutzungsanteile der GammaRay Werkzeuge. About GammaRay Über GammaRay About KDAB Über KDAB Klarälvdalens Datakonsult AB (KDAB) Klarälvdalens Datakonsult AB (KDAB) <qt><p>GammaRay is supported and maintained by KDAB</p>KDAB, the Qt experts, provide consulting and mentoring for developing Qt applications from scratch and in porting from all popular and legacy frameworks to Qt. We continue to help develop parts of Qt and are one of the major contributors to the Qt Project. We can give advanced or standard trainings anywhere around the globe.</p><p>Please visit <a href='https://www.kdab.com'>https://www.kdab.com</a> to meet the people who write code like this.</p></qt> Communication Message Statistics Nachrichtenstatistik Hide inactive tools Nicht aktive Werkzeuge verstecken Transmission rate: RX %1 Mbps, TX %2 Mbps Übertragungsrate: RX %1 Mbps, TX %2 Mbps Custom Code Navigation Benutzerdefinierte Quellcodenavigation Specify command to use for code navigation, '%f' will be replaced by the file name, '%l' by the line number and '%c' by the column number. Befehl zum Anzeigen von Quellcode, '%f' wird durch den Dateinamen ersetzt, '%l' durch die Zeilennummer und '%c' durch die Spaltennummer. Tool %1 failed to load. Werkzeug %1 konnte nicht geladen werden. &Help... &Hilfe... Show help browser. Hilfe anzeigen. &Contribute... &Beitragen... GammaRay::MaterialTab &Shader: GammaRay::MessageDisplayModel Debug Debug Warning Warnung Critical Kritisch Fatal Fatal Info Info Unknown Unbekannt <qt><dl><dt><b>Type:</b></dt><dd>%1</dd><dt><b>Time:</b></dt><dd>%2</dd><dt><b>Message:</b></dt><dd>%3</dd><dt><b>Backtrace:</b></dt><dd><pre>%4</pre></dd></dl></qt> <qt><dl><dt><b>Typ:</b></dt><dd>%1</dd><dt><b>Zeit:</b></dt><dd>%2</dd><dt><b>Nachricht:</b></dt><dd>%3</dd><dt><b>Backtrace:</b></dt><dd><pre>%4</pre></dd></dl></qt> <qt><dl><dt><b>Type:</b></dt><dd>%1</dd><dt><b>Time:</b></dt><dd>%2</dd><dt><b>Message:</b></dt><dd>%3</dd></dl></qt> <qt><dl><dt><b>Typ:</b></dt><dd>%1</dd><dt><b>Zeit:</b></dt><dd>%2</dd><dt><b>Nachricht:</b></dt><dd>%3</dd></dl></qt> GammaRay::MessageHandlerFactory Messages Nachrichten GammaRay::MessageHandlerWidget Messages Nachrichten Categories Kategorien QFatal in %1 at %2 QFatal in %1 um %2 Copy Backtrace Backtrace kopieren GammaRay::MessageModel Message Nachricht Time Zeit Category Kategorie Function Funktion Source Quelle GammaRay::MessageStatisticsModel Message Count: %1 of %2 (%3%) Message Size: %4 of %5 (%6%) Nachrichtenanzahl: %1 of %2 (%3%) Nachrichtengröße: %4 of %5 (%6%) Message Count: %1 of %2 (%3%) Message Size: %4 of %5 (%6%) Object Address: %7 Nachrichtenanzahl: %1 von %2 (%3%) Nachrichtengröße: %4 von %5 (%6%) Objekt-Adresse: %7 Object: %1 Message Type: %2 Message Count: %3 of %4 (%5%) Message Size: %6 of %7 (%8%) Objekt: %1 Nachrichtentyp: %2 Nachrichtenanzahl: %3 of %4 (%5%) Nachrichtengröße: %6 of %7 (%8%) GammaRay::MetaObjectBrowserFactory Meta Objects Meta Objekte GammaRay::MetaObjectModel Class Klasse GammaRay::MetaObjectTreeClientProxyModel overrides base class signal überschreibt Signal einer Basisklasse method uses parameter type not registerd with the meta type system Methode verwendet Parameter deren Typ nicht beim Metatyp-System registriert ist overrides base class property überschreibt eine Eigentschaft der Basisklasse property with type not registered with the meta type system Typ einer Eigentschaft ist nicht beim Metatyp-System registriert Issues:<ul><li>%1</li></ul> Probleme:<ul><li>%1</li></ul> This meta object might have been deleted. Das Meta-Objekt wurde möglicherweise gelöscht. %1% %1% Meta Object Class Meta Objekt Klasse Self Total Incl. Total Self Alive Incl. Alive This column shows the number of objects created and not yet destroyed of a particular type. Diese Spalte enthält die Anzahl an Objekten eines Typs die erzeugt aber noch nicht gelöscht wurden. This column shows the number of objects created and not yet destroyed that inherit from a particular type. Diese Spalte enthält die Anzahl an von einem Typ abgeleiteten Objekten die erzeugt aber noch nicht gelöscht wurden. This column shows the QMetaObject class hierarchy. Diese Spalte enthält die Hierarchie der Meta-Objekte. This column shows the number of objects created of a particular type. Diese Spalte enthält die Anzahl an Objekten eines Typs die bisher insgesamt erzeugt wurden. This column shows the number of objects created that inherit from a particular type. Diese Spalte enthält die Anzahl an von einem Typ abgeleiteten Objekten die bisher insgesamt erzeugt wurden. GammaRay::MetaTypeBrowserFactory Meta Types Meta Typen GammaRay::MetaTypeBrowserWidget &Rescan Meta Types &Nach Metatypen suchen Check for changes to the meta type database. Durchsucht die Metatyp-Datenbank nach Änderungen. GammaRay::MetaTypesClientModel yes ja Type Name Typname Meta Type Id Metatyp Id Size Größe Meta Object Meta Objekt Type Flags Compare Vergleichbar Debug Debug Has equality comparison operators registered. Hat einen Vergleichsoperator registriert. Has debug stream operators registered. Hat einen Debug-Ausgabe Operator registriert. GammaRay::MetaTypesModel N/A GammaRay::MethodArgumentModel <unnamed> (%1) <unbenannt> (%1) Argument Argument Value Wert Type Typ GammaRay::MethodInvocationDialog &Connection type: &Verbindungstyp: Invoke Aufrufen Auto Automatik Direct Direkt Queued GammaRay::MethodsExtension %1: Signal %2 emitted, arguments: %3 %1: Signal %2 ausgesandt, Argumente: %3 %1: Invocation failed: Invalid object, probably got deleted in the meantime. %1: Aufruf fehlgeschlagen: Ungültiges Objekt, möglicherweise zwischenzeitlich gelöscht worden. %1: Invocation failed: Can't invoke constructors. %1: Aufruf fehlgeschlagen: Konstrukturen können nicht aufgerufen werden. %1: Invocation failed.. %1: Aufruf fehlgeschlagen. GammaRay::MethodsTab Invoke Aufrufen Connect to Verbinden zu Emit Aussenden GammaRay::MimeTypesModel Name Name Comment Kommentar Glob Patterns Icons Symbole Suffixes Suffixe Aliases GammaRay::ModelCellModel Role #%1 Rolle #%1 Role Rolle Value Wert Type Typ GammaRay::ModelContentDelegate <unnamed: row %1, column %2> <unbenannt: Zeile %1; Spalte %2> GammaRay::ModelInspectorWidget Model Content Modellinhalt Model Index: Model Index: Invalid Ungültig Internal Id: Interne Id: Internal Pointer: Interner Pointer: Row: %1 Column: %2 Zeile: %1 Spalte: %2 Models Modelle Selection Models Auswahl-Modelle Cell Content Zelleninhalt Flags: GammaRay::ModelPickerDialog Hide invisible items Unsichtbare Elemente verstecken GammaRay::NetworkDiscoveryModel Incompatible GammaRay version. Inkompatible GammaRay version. Name Name Host Host GammaRay::NetworkInterfaceModel Interface Schnittstellen Hardware Address Hardware Adresse Flags GammaRay::NetworkWidget Interfaces Schnittstellen Configurations Konfigurationen GammaRay::NetworkWidgetFactory Cookies Cookies GammaRay::ObjectClassInfoModel Name Name Value Wert GammaRay::ObjectEnumModel %n element(s) 1 Element %n Elemente Name Name Value Wert GammaRay::ObjectInspectorFactory Objects Objekte Properties Eigenschaften Methods Methoden Connections Verbindungen Enums Enumeratoren Class Info Klasseninformation Attributes Attribute Bindings Stack Trace GammaRay::ObjectInspectorWidget Object @ %1 Objekt @ %1 GammaRay::ObjectModelBase Object Objekt Type Typ GammaRay::ObjectTreeModel <deleted> <gelöscht> GammaRay::OutboundConnectionsModel <slot object> <Slot Objekt> Signal Signal Receiver Empfänger Slot Slot GammaRay::PaintAnalyzerWidget Argument Argument Stack Trace Visualize Clip Area Highlight current clipping area. GammaRay::PaintBufferClientModel %1 % %1 % Command Befehl Arguments Argumente Cost Kosten GammaRay::PaintBufferModel <empty> <leer> control rect: %1, elements: %2 %1, brush: %2 %1, pen: %2 position: %1, text: "%2", font: %3 Position: %1, Text: "%2", Schrift: %3 %1 source: %2 %1 Quelle: %2 %1 offset: %2 glyphs: %1, font: %2 GammaRay::PaintBufferViewer Analyze Painting Zeichnen Analysieren GammaRay::PaletteModel Role Rolle GammaRay::PixelMetricModel Metric Metrik Default Value Standardwert GammaRay::PluginManager Failed to load plugin: %1 Plugin konnte nicht geladen werden: %1 GammaRay::PluginMetaData Actions Aktionen Bluetooth Bluetooth Text Codecs Text Codecs Font Browser Schriftarten GUI Support GUI Unterstützung KJobs KJobs Locales Mime Types Mime Typen Models Modelle Network Netzwerk Object Visualization Objektvisualisierung Positioning Positionierung QML Support QML Unterstützung Qt3D Inspector Qt3D Inspektor Qt IVI Qt IVI Quick Scenes Quick Szenen Quick Widgets Support Graphics Scenes Script Engines System System Wayland Compositor Signals Signale State Machines Zustandsautomaten Styles Text Documents Text Dokumente Timers Timer Translations Übersetzungen Web Inspector Web Inspektor Widgets Widgets GammaRay::PositioningWidget Override Überschreiben &Latitude: &Breite: ° ° &Longitude: &Länge: Speed: Geschwindigkeit: m/s m/s Acc&uracy: &Genauigkeit: m m Timestamp: Zeitstempel: yyyy-MM-dd hh:mm:ss.zzz t yyyy-MM-dd hh:mm:ss.zzz t Al&titude: &Höhe: Accurac&y: &Genauigkeit: &Direction: &Richtung: Ma&gnetic variation: &Magnetische Varianz: Load NMEA file... NMEA Datei laden... Load a GPS recording from an NMEA file. GPS Aufzeichnung aus einer NMEA Datei laden. Center on position Zentrieren auf Position Center map on current position. Zentriert die Karte auf die aktuelle Position. F3 F3 Load NMEA file NMEA Datei laden Failed to open NMEA file NMEA Datei laden fehlgeschlagen Could not open '%1': %2. Konnte '%1' nich öffnen: %2. GammaRay::PreloadCheck Cannot find file containing symbol: %1 Konnte Datei nicht finden die Symbol '%1' enthält Invalid shared object: %1 Ungültige Programmdatei: %1 Failed to run 'readelf' (binutils) binary: %1 Ausführen des 'readelf' (binutils) Programms ist fehlgeschlagen: %1 Cannot read shared object: %1 Programmdatei konnte nicht gelesen werden: %1 Symbol is not marked as relocatable: %1 Symbol ist nicht als relokierbar markiert: %1 GammaRay::Probe PID %1 PID %1 GammaRay::ProbeABIContext debug Debug release Release Qt %1.%2 (%3) Qt %1.%2 (%3) GammaRay::ProblemClientModel Problem Description Problembeschreibung Source Location Quelle GammaRay::ProblemReporterFactory Problems Probleme GammaRay::ProblemReporterWidget Available Problem Checkers Start Scan for Problems Problemsuche Starten GammaRay::ProcessModel Process ID Prozess ID Name Name State Zustand User Benutzer Name: %1 PID: %2 Owner: %3 Qt ABI: %4 Probe available: %5 Name: %1 PID: %2 Besitzer: %3 Qt ABI: %4 Probe verfügbar: %5 yes ja no nein GammaRay::PromoLabel Visit KDAB Website KDAB Webseite besuchen GammaRay::PropertiesTab &New Dynamic Property: &Neue dynamische Eigenschaft: Name Name &Type: &Typ: &Value: &Wert: Add Hinzufügen Remove Entfernen Reset Zurücksetzen GammaRay::PropertyDoublePairEditor x x GammaRay::PropertyEnumEditor Loading... Lade... GammaRay::PropertyExtendedEditor ... ... GammaRay::PropertyIntPairEditor x x GammaRay::PropertyMatrixDialog Edit Matrix Matrix Editieren Edit 4x4 Matrix 4x4 Matrix Editieren Edit Transform Transformation Editieren Edit 2D Vector 2D Vektor Editieren Edit 3D Vector 3D Vektor Editieren Edit 4D Vector 4D Vektor Editieren Edit Quaternion Quaternion Editieren Edit Unsupported Type GammaRay::PropertyMatrixModel m_1 m_1 m_2 m_2 m_3 m_3 m_4 m_4 m1_ m1_ m2_ m2_ d x/y d x/y m3_ m3_ m4_ m4_ x x y y z z w w pitch yaw roll GammaRay::PropertyRectEditorDialog TopLeft: Size Größe Size: Größe: GammaRay::PropertyTextEditorDialog Switch to Hex Mode Zu Hex-Modus wechseln Unsaved changes will be lost Nicht gespeicherte Änderungen gehen verloren Switch to Hex mode Zu Hex-Modus wechseln Switch to String mode Zu Text-Modus wechseln. GammaRay::ProxyFactory Plugin does not provide an instance of %1. Plugin stellt keine Instanz von %1 bereit. GammaRay::ProxyToolUiFactory Plugin '%1' could not be loaded. Plugin '%1' konnte nicht geladen werden. GammaRay::QmlContextModel Context Kontext Location Quelle GammaRay::QmlContextPropertyAdaptor QML Context Property QML Kontext Eigenschaft GammaRay::QmlSupport <empty> <leer> <%1 entries> <%1 Elemente> GammaRay::QmlSupportUiFactory QML Context QML Kontext QML Type QML Typ GammaRay::Qt3DGeometryTab B&uffer: P&uffer: Reset Camera Kamera Zurücksetzen Reset camera. Kamera zurücksetzen. Show Normals Normalenvektor anzeigen Visualize normal vectors. Normalenvektoren visualisieren. Show Tangents Tangenten Anzeigen Visualize tangent vectors. Tangentenvektoren visualisieren. View Geometry Geometry Anzeigen Visualize geometry. Geometry visualisieren. View Buffers Puffer anzeigen View raw buffer data. Rohe Pufferdaten anzeigen. Cull Back Enable back face culling. Shading: Flat Flach Wireframe Gitter Phong Phong Normal Normale Texture Coordinate Texturkoodinaten Tangent Tangenten Color Farbe GammaRay::Qt3DInspectorUiFactory Geometry Geometrie GammaRay::Qt3DInspectorWidget &Engine: Scene Szene Search Suchen Render Settings Render Einstellungen Entity @ %1 Entity @ %1 Frame Graph Node @ %1 GammaRay::QtIviObjectModel Name Name Value Wert Writable Schreibbar Override Überschreiben Type Typ GammaRay::QtIviPropertyModel Name Name Value Wert Writable Schreibbar Override Überschreiben Type Typ GammaRay::QuickClientItemModel is invisible ist unsichtbar has a size of zero hat keine Größe is out of view befindet sich außerhalb der Ansicht is partially out of view befindet sich teilweise außerhalb der Ansicht has inactive focus hat inaktiven Fokus has active focus hat aktiven Fokus just received an event hat gerade ein Ereignis empfangen GammaRay::QuickInspectorUiFactory Material Material Geometry Geometrie Texture Textur GammaRay::QuickInspectorWidget Items Elemente Scene Graph Szenengraph Save as &Image... Als &Bild speichern... Save current frame as image. Aktuellen Frame als Bild speichern. Save as &Image (with decoration)... Save current frame with decoration rendered as image. Analyze Painting... Zeichnen Analysieren... Analyzer software renderer paint operations. Zeichenbefehle des Software-Renderers analysieren. F9 F9 Enable slow down mode Animationen verlangsamen Enable slow down mode for animations Aktiviert das Verlangsamen von Animationen Save As Image Als Bild Speichern Image Files (*.png *.jpg) Bilddatei (*.png *.jpg) GammaRay::QuickOverlayLegend Legend Legende Show Legend Legende Anzeigen <b>Show Legend</b><br>This shows a legend explaining the various diagnostic decorations. GammaRay::QuickSceneControlWidget Visualize Clipping <b>Visualize Clipping</b><br/>Items with the property <i>clip</i> set to true, will cut off their and their children's rendering at the items' bounds. While this is a handy feature it comes with quite some cost, like disabling some performance optimizations.<br/>With this tool enabled the QtQuick renderer highlights items, that have clipping enabled, so you can check for items, that have clipping enabled unnecessarily. Visualize Overdraw <b>Visualize Overdraw</b><br/>The QtQuick renderer doesn't detect if an item is obscured by another opaque item, is completely outside the scene or outside a clipped ancestor and thus doesn't need to be rendered. You thus need to take care of setting <i>visible: false</i> for hidden items, yourself.<br/>With this tool enabled the QtQuick renderer draws a 3D-Box visualizing the layers of items that are drawn. Visualize Batches <b>Visualize Batches</b><br/>Where a traditional 2D API, such as QPainter, Cairo or Context2D, is written to handle thousands of individual draw calls per frame, OpenGL is a pure hardware API and performs best when the number of draw calls is very low and state changes are kept to a minimum. Therefore the QtQuick renderer combines the rendering of similar items into single batches.<br/>Some settings (like <i>clip: true</i>) will cause the batching to fail, though, causing items to be rendered separately. With this tool enabled the QtQuick renderer visualizes those batches, by drawing all items that are batched using the same color. The fewer colors you see in this mode the better. Visualize Changes <b>Visualize Changes</b><br>The QtQuick scene is only repainted, if some item changes in a visual manner. Unnecessary repaints can have a bad impact on the performance. With this tool enabled, the QtQuick renderer will thus on each repaint highlight the item(s), that caused the repaint. Visualize Controls <b>Visualize Controls</b><br>The QtQuick scene is rendered normaly, in addition overlays will highlight Qt Quick Controls. Decorate Target <b>Decorate Target</b><br>This enables diagnostic decorations on the target application, such as anchor and layout information, helper grids or control overlays. Layout Grid Layout Gitter <b>Layout Grid</b><br>Configure the layout helper grid. <b>Layout Gitter</b><br>Erlaubt das Konfigurieren eines Hilfsgitters zur Überprüfung des Anwendungslayouts. GammaRay::QuickScenePreviewWidget No remote view available. (This happens e.g. when the window is minimized or the scene is hidden) Remote-Ansicht nicht verfügbar. (Dies kann z.B. durch ein minimiertes Anwendungsfenster verursacht werden) GammaRay::RemoteModel Loading... Lade... GammaRay::RemoteViewWidget No remote view available. Keine Anzeige verfügbar. Pan View Ansicht Verschieben <b>Pan view</b><br>Default mode. Click and drag to move the preview. Won't impact the original application in any way. <b>Ansicht Verschieben</b><br>In diesem Modus kann der Bildschirmausschnitt in der Remote-Ansicht verschoben werden. Measure Pixel Sizes Messen <b>Measure pixel-sizes</b><br>Choose this mode, click somewhere and drag to measure the distance between the point you clicked and the point where your mouse pointer is. (Measured in scene coordinates). <b>Messen</b><br>Mit diesem Modus können Abstände in der Anwendung in Szenenkoordinaten vermessen werden. Pick Element Element auswählen Pick Auswählen <b>Pick Element</b><br>Select an element for inspection by clicking on it. <b>Element Auswählen</b><br>Erlaubt das Auswählen eines Elements zur Untersuchung durch anklicken. Redirect Input Eingabe Umleiten <b>Redirect Input</b><br>In this mode all mouse input is redirected directly to the original application,so you can control the application directly from within GammaRay. <b>Eingabe Umleiten</b><br>In diesem Modus werden alle Maus- und Tastatureingaben an die Anwendung weitergeleitet. Inspect Colors Farbe inspizieren <b>Inspect Colors</b><br>Inspect the RGBA channels of the currently hovered pixel <b>Farben inspizieren</b><br>Zeigt die RGBA Farbkanäle des gerade überfahrenen Pixels an Zoom Out Hinauszoomen Zoom In Hineinzoomen Display FPS Framerate Anzeigen GammaRay::ResourceBrowserFactory Resources Resourcen GammaRay::ResourceBrowserWidget Select a Resource to Preview Resource zur Vorschau auswählen Save As... Speichern unter... Save As Speichern unter GammaRay::ResourceInfo Version: %1 Version: %1 GammaRay::ResourceInfoExtractors Role: %1 Rolle: %1 Buffer size: (%1x%2) Puffergröße: (%1x%2) Is mapped: %1 Has content: %1 Hat Inhalt: %1 Title: "%1" Titel: "%1" Class name: "%1" Klassenname: "%1" Surface: %1 Manufacturer: %1 Hersteller: %1 Model: %1 Modell: %1 Physical size: (%1x%2) Physische Größe: (%1x%2) Position: (%1x%2) Position: (%1x%2) Current mode: %1x%2@%3Hz Aktueller Modus: %1x%2@%3Hz Scale factor: %1 Skalierungsfaktor: %1 Transform: %1 Transformation: %1 Subpixel: %1 Subpixel: %1 GammaRay::ResourceModel Name Name Size Größe Kind Match OS X Finder Typ Type All other platforms Typ Date Modified Datum %1 TB %1 TB %1 GB %1 GB %1 MB %1 MB %1 KB %1 KB %1 byte(s) %1 Byte(s) Root Wurzel Folder Ordner %1 File %1 Datei GammaRay::SGGeometryTab Raw Vertex Data Vertex Daten Preview Vorschau GammaRay::SGWireframeWidget Unknown Unbekannt Drawing mode: %1 Zeichenmodus: %1 GammaRay::SceneInspectorWidget QGraphicsItem @ %1 QGraphicsItem @ %1 GammaRay::SceneModel Item Element Type Typ GammaRay::SearchLineController Search Suchen GammaRay::SelectedCodecsModel Codec Encoded Data GammaRay::SelectionModelModel Object Objekt #Items #Zellen #Rows #Zeilen #Columns #Spalten Type Typ GammaRay::SelfTest No probe found for ABI %1. Probe at %1 is invalid. Found valid probe for ABI %1 at %2. No probes found - GammaRay not functional. No injectors available - GammaRay not functional. Kein Injektor verfügbar - GammaRay ist nicht betriebsbereit. Unable to create instance of injector %1. Konnte keine Instanz des %1 Injektors erzeugen. Injector %1 failed to pass its self-test: %2. Injektor %1 hat seinen Selbsttest nicht bestanden: %2. Injector %1 successfully passed its self-test. Injektor %1 hat seinen Selbsttest erfolgreich abgeschlossen. GammaRay::SignalHistoryDelegate <unknown> <unbekannt> %1 at %2 ms %1 bei %2 ms GammaRay::SignalHistoryModel Address: %1 Adresse: %1 Object Objekt Type Typ Events Ereignisse GammaRay::SignalMonitorWidget Pause Pause Zoom Level: Zoom: GammaRay::StackTraceModel Function Funktion Location Quelle GammaRay::StandardIconModel Name Name Icon Symbol GammaRay::StandardPathsModel Type Typ Display Name Anzeigename Standard Locations Standardpfade Writable Location Beschreibare Pfade GammaRay::StandardPathsProxy Locations Standard / Writable GammaRay::StateMachineViewerServer Clearing filter. Filter zurücksetzen. Setting filter on: %1 Setze Filter auf: %1 State entered: %1 Zustand betreten: %1 State exited: %1 Zustand verlassen: %1 Log [label=%1]: %2 GammaRay::StateMachineViewerWidget S&tate machine: &Zustandsautomaten: Show Log Log anzeigen Hide Log Log verstecken Start/Stop State Machine Automat starten/anhalten Entity @ %1 Entity @ %1 Start State Machine Zustandsautomat starten Stop State Machine Zustandsautomat anhalten GammaRay::StateModel State Zustand Type Typ GammaRay::StyleElementStateTablePage Cell &Width: &Breite: px px Cell &Height: &Höhe: &Zoom: &Zoom: GammaRay::StyleHintModel Style Hint Value Wert Return Data Rückgabedaten GammaRay::StyleInjector Injector style plugin is not found in the Qt style plug-in search path or cannot be loaded Das Injektor Style-Plugin konnte nicht im Suchpfad für Qt Plugins gefunden werden, oder konnte nicht geladen werden GammaRay was compiled without QtWidget support, style injector is not available. GammaRay ist ohne QtWidget Unterstützung übersetzt worden, der Style-Injektor steht nicht zur Verfügung. GammaRay::StyleInspectorWidget &Style: &Style: Primitives Primitiven Controls Complex Controls Pixel Metric Pixel Metrik Standard Icons Standard Palette Standard Palette Style Hints GammaRay::SysInfoModel Key Schlüssel Value Wert GammaRay::SysInfoWidget Form Qt Qt System information: Systeminformationen: Library information: Bibliotheksinformationen: Standard Paths Standardpfade Environment Umgebung GammaRay::TextDocumentFormatModel Property Eigenschaft Value Wert Type Typ GammaRay::TextDocumentInspectorWidget All documents: Alle Dokumente: Document structure: Dokumentenstruktur: Element format: Elementformat: Content Inhalt HTML HTML GammaRay::TextDocumentModel Root Frame Wurzelrahmen Element Element Format Format Table Tabelle Frame Rahmen Block: %1 Block: %1 Cell %1x%2 Zelle %1x%2 Fragment: %1 Fragment: %1 Layout Range: %1 Layoutbereich: %1 no format kein Format Image: %1 Bild: %1 GammaRay::TextureTab <b>Identified Problems:</b> <b>Identifizierte Probleme:</b> GiB GiB MiB MiB KiB KiB B B 0 B 0 B Visualize Texture Problems Texturprobleme Visualisieren Transparency waste: %1% / %2. Unötige Transparenz: %1% / %2. Using a BorderImage for this texture would save %1% / %2. Verwendung eines BorderImage für diese Texture würde %1% / %2 sparen. Texture has only one color, consider using a widget or a rectangle. Diese Textur enthält nur eine Farbe, ggf. Widget oder Rectangle verwenden. Texture is fully transparent, consider using margins or anchoring. Diese Textur is vollständig transparent, ggf. Ränder/Ankerlayouts verwenden. GammaRay::TimerTopWidget ... ... GammaRay::TimezoneClientModel yes ja IANA Id IANA Id Country Land Standard Display Name Standard-Anzeigename DST Sommerzeit Windows Id Windows Id GammaRay::TimezoneOffsetDataClientModel Transition Time (UTC) Übergangszeit (UTC) Offset to UTC Abstand zu UTC Standard Time Offset Standardzeit-Abstand DST Offset Sommerzeit-Abstand Abbreviation Abkürzung GammaRay::ToolPluginErrorModel Plugin Name Plugin Name Plugin File Plugin Datei Error Message Fehlermeldung GammaRay::ToolPluginModel Id Id Supported types Unterstützte Typen GammaRay::TranslatorInspectorWidget Reset Selected Translations Ausgewählte Übersetzungen zurücksetzen Reset manually changed translations back to their original content. Setzt manuell geänderte Übersetzungen auf ihren ursprünglichen Inhalt zurück. Send LanguageChange Event LanguageChange Ereignis senden Sends a QEvent::LanguageChange event to the application to reload changed translations. Versendet ein QEvent::LanguageChange Ereignis an die Anwendung um geänderte Übersetzungen neu zu laden. GammaRay::TranslatorWrapperProxy Context Kontext Source Text Quelltext Disambiguation Translation Übersetzung GammaRay::TranslatorsModel Object Objekt Type Typ Translations Übersetzungen GammaRay::Util <p style='white-space:pre'>Object name: %1 (Address: %2) Type: %3 Parent: %4 (Address: %5) Number of children: %6</p> <p style='white-space:pre'>Objektname: %1 (Adresse: %2) Typ: %3 Elternobjekt: %4 (Adresse: %5) Anzahl der Kindobjekte: %6</p> GammaRay::VariantHandler <no icon> left: %1, top: %2, right: %3, bottom: %4 links: %1, oben: %2, rechts: %3, unten: %4 GammaRay::VtkPanel Layout: Layout: Tree Layout Span Tree Layout Force Directed Layout Force Directed Layout (3D) Simple 2D Layout Stereo: Stereo: Off Aus Crystal Eyes Red/Blue Rot/Blau Interlaced Left Links Right Rechts Dresden Dresden Anaglyph Anaglyph Checkboard Schachbrett GammaRay::WebInspectorWidget about:blank about:blank GammaRay::Widget3DView Window: Fenster: Widget @ %1 Widget @ %1 GammaRay::WidgetInspector::QML Wireframe Gitter Horizontals Class: Klasse: Parent: Elternobjekt: Position: Position: Size: Größe: GammaRay::WidgetInspectorUiFactory Attributes Attribute GammaRay::WidgetInspectorWidget Save as &Image... Als &Bild speichern... Save currently selected widget as image. Speichert das derzeit ausgewählte Widget als Bild. Save as &SVG... Als &SVG speichern... Renders the currently selected image as SVG vector graphic. Speichter das derzeit ausgewählte Widget als SVG Vektorgrafik. Save as &PDF... Als &PDF speichern... Renders the currently selected widget in to a PDF file. Speichert das derzeit ausgewählte Widget als PDF-Datei. Save as &UI file... Als &UI Datei speichern... Saves the currently selected widget as a Qt designer UI file. Speichert das derzeit ausgewählte Widget als Qt Designer UI-Date. &Analyze Painting... &Zeichnen Analysieren... Show Tab Focus Chain Tab Fokus Reihenfolge Anzeigen 3D View 3D Ansicht Widget @ %1 Widget @ %1 Save As Image Als Bild Speichern Image Files (*.png *.jpg) Bilddatei (*.png *.jpg) Save As SVG Als SVG Speichern Scalable Vector Graphics (*.svg) Save As PDF Als PDF Speichern PDF (*.pdf) PDF Datei (*.pdf) Save As Qt Designer UI File Als Qt Designer UI Datei Speichern Qt Designer UI File (*.ui) Qt Designer UI Datei (*.ui) Tree Widgetbaum F9 F9 KUserFeedback::ApplicationVersionSource The version of the application. Die Version der Anwendung. KUserFeedback::AuditLogBrowserDialog Dialog Da&ta Submission: &Datenübertragung: Delete Log Protokoll Löschen KUserFeedback::AuditLogUiController Basic System Information Basic Usage Statistics Detailed System Information Detailed Usage Statistics Unable to open file %1: %2. Category: <i>%1</i><br/> Key: <i>%1</i><br/> Submitted data: <tt>%1</tt><br/><br/> KUserFeedback::CompilerInfoSource The compiler used to build this application. Der zum Übersetzten verwendete Compiler. KUserFeedback::FeedbackConfigDialog Feedback Settings Feedback Einstellungen Contribute! Feedback Geben! No, I do not want to contribute. Nein, ich möchte kein Feedback beitragen. KUserFeedback::FeedbackConfigUiController We make this application for you. You can help us improve it by contributing information on how you use it. This allows us to make sure we focus on things that matter to you. Contributing statistics is of course entirely anonymous, will not use any kind of unique identifier and will not cover any data you process with this application. Share basic system information. No unique identification is included, nor data processed with the application. Share basic system information and basic statistics on how often you use the application. No unique identification is included, nor data processed with the application. Share basic statistics on how often you use the application, as well as detailed information about your system. No unique identification is included, nor data processed with the application. Share detailed system information and statistics on how often individual features of the application are used. No unique identification is included, nor data processed with the application. We make %1 for you. You can help us improve it by contributing information on how you use it. This allows us to make sure we focus on things that matter to you. Contributing statistics is of course entirely anonymous, will not use any kind of unique identifier and will not cover any data you process with %1. Share basic system information. No unique identification is included, nor data processed with %1. Share basic system information and basic statistics on how often you use %1. No unique identification is included, nor data processed with %1. Share basic statistics on how often you use %1, as well as detailed information about your system. No unique identification is included, nor data processed with %1. Share detailed system information and statistics on how often individual features of %1 are used. No unique identification is included, nor data processed with %1. We make this application for you. In order to ensure it actually does what you need it to do we would like to ask you about your use cases and your feedback, in the form of a web survey. I will occasionally participate in web surveys about the application, not more than four times a year though. I will participate in web surveys whenever one is available. Surveys can of course be deferred or skipped. We make %1 for you. In order to ensure it actually does what you need it to do we would like to ask you about your use cases and your feedback, in the form of a web survey. I will occasionally participate in web surveys about %1, not more than four times a year though. I will participate in web surveys about %1 whenever one is available. Surveys can of course be deferred or skipped. KUserFeedback::FeedbackConfigWidget Contribute Statistics Statistiken Beitragen TextLabel Show the raw data that is going to be shared. Übertragene Rohdaten anzeigen. Participate in Surveys An Umfragen Teilnehmen <a href="auditLog">View previously submitted data...</a> <a href="auditLog">Bisher übermittlete Daten anzeigen...</a> KUserFeedback::NotificationPopup Help us make this application better! You can help us improving this application by sharing statistics and participate in surveys. Help us make %1 better! You can help us improving %1 by sharing statistics and participate in surveys. Contribute... Beitragen... We are looking for your feedback! We would like a few minutes of your time to provide feedback about this application in a survey. We would like a few minutes of your time to provide feedback about %1 in a survey. Participate Teilnehmen KUserFeedback::OpenGLInfoSource Information about type, version and vendor of the OpenGL stack. Informationen über den Typ, die Version und den Hersteller des OpenGL Treibers. KUserFeedback::PlatformInfoSource Type and version of the operating system. Typ und Version des Betriebsystems. KUserFeedback::QtVersionSource The Qt version used by this application. Die verwendete Qt Version. KUserFeedback::StartCountSource How often the application has been started. Anzahl der Anwendungsstarts. KUserFeedback::UsageTimeSource The total amount of time the application has been used. Die Nutzungszeit der Anwendung. QFileDialog My Computer gammaray-2.11.3/translations/gammaray_en.ts000066400000000000000000005202671413310443000207520ustar00rootroot00000000000000 GammaRay::AboutDataContext Unable to read the Authors list <b>GammaRay %1</b> <p>The Qt application inspection and manipulation tool.Learn more at <a href="https://www.kdab.com/gammaray">https://www.kdab.com/gammaray/</a>.</p><p>Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, <a href="mailto:info@kdab.com">info@kdab.com</a></p><p>StackWalker code Copyright (c) 2005-2019, Jochen Kalmbach, All rights reserved<br>lz4 fast LZ compression code Copyright (C) 2011-2015, Yann Collet, All rights reserved<br>backward-cpp code Copyright 2013-2017 Google Inc. All rights reserved.</p> <p><u>Authors:</u><br>%1</p> <p>GammaRay and the GammaRay logo are registered trademarks of Klarälvdalens Datakonsult AB in the European Union, the United States and/or other countries. Other product and company names and logos may be trademarks or registered trademarks of their respective companies.</p><br> GammaRay::AboutPluginsDialog Loaded Plugins Failed Plugins GammaRay: Plugin Info GammaRay::AbstractAttributeModel Attribute GammaRay::AbstractConnectionsModel Auto Auto (%1) Direct Queued Blocking Unknown: %1 Connections exists multiple times. The connected slot is called multiple times when the signal is emitted. Direct cross-thread connection. The connected slot is called in the context of the emitting thread. Type <destroyed> <unknown> GammaRay::AllCodecsModel Codec Aliases GammaRay::AttachDialog <html><head/><body><p align="center"><img src=":/gammaray/GammaRay-48x48.png"/></p><p align="center">Retrieving the list of processes which use Qt ...</p></body></html> Pro&be: &Access mode: Out-of-process, local debugging only Out-of-process, remote debugging enabled In-process GammaRay - Attach to Process GammaRay::BindingModel Property Value Source Depth GammaRay::Client Unsupported transport protocol. Protocol violation, first message is not the server version. Gammaray Protocol Mismatch. Probe version is %1, was expecting %2. GammaRay::ClientActionModel Warning: Ambiguous shortcut detected. Object Name Checkable Checked Priority Shortcut(s) GammaRay::ClientConnectionManager Connection refused. Lost connection to remote host: %1 Could not establish connection to remote host: %1 GammaRay - Connection Error GammaRay::ClientMethodModel Method Constructor Slot Signal Unknown Public Protected Private Tag: %1 <none> Revision: %1 overrides base class signal uses parameter type not registerd with the meta type system Issues: %1 Signature Type Access Class GammaRay::ClientNetworkConfigurationModel Name Identifier Bearer Timeout Roaming Purpose State Type GammaRay::ClientPropertyModel constant designable final resetable scriptable stored user writable Attributes: %1 Revision: %1 Notify signal: %1 Property Value Type Class GammaRay::ClientTimerModel Invalid QQmlTimer QTimer Free Timer Object Name State Total Wakeups Wakeups/Sec Time/Wakeup [uSecs] Max Wakeup Time [uSecs] Timer ID None (%1 ms) Inactive (%1 ms) Singleshot (%1 ms) Repeating (%1 ms) 0 N/A GammaRay::ClientToolModel This tool does not work in out-of-process mode. GammaRay::CodeEditor Syntax Highlighting GammaRay::CodecBrowserWidget Enter text for preview... GammaRay::ConnectPage Connect to a running remote instance. Url: No port given, will use 11732 File is not a socket GammaRay::ConnectionsTab &Inbound Connections: &Outbound Connections: Go to sender Go to receiver GammaRay::ContextMenuExtension Go to: %1 Show source: %1 Go to creation: %1 Go to declaration: %1 Show in "%1" tool GammaRay::CookieJarModel Name Domain Path Value Expiration Date Http Only Secure Session Cookie GammaRay::DebuggerInjector Process stopped. The debugger executable '%1' could not be found Yama security extension is blocking runtime attaching, see /proc/sys/kernel/yama/ptrace_scope GammaRay::DynamicPropertyAdaptor <dynamic> GammaRay::EnvironmentModel Environment Variable Value GammaRay::FontBrowserWidget The quick brown fox jumps over the lazy dog Enter some text here... Point Size: Bold Italic Underline GammaRay::FontDatabaseModel Fonts Smooth Sizes GammaRay::FontModel <no text> GammaRay::GdbInjector The debuggee application is missing debug symbols which are required for GammaRay's GDB injector. Please recompile the debuggee. GDB error was: %1 Your QtCore library is missing debug symbols which are required for GammaRay's GDB injector. Please install the required debug symbols. GDB error was: %1 The debuggee application seems to have an invalid stack trace This can be caused by the executable being updated on disk after launching it. GDB error was: %1 GammaRay::GraphicsSceneView Scene Coordinates: 0x0 Item Coordinates: GammaRay::GridSettingsWidget Grid enabled X Offset: Y Offset: Cell Width: Cell Height: GammaRay::GuiSupport (Injected by GammaRay) variable fixed percentage <empty> <%1 elements> width: %1 brush: %1 miter limit: %1 dash pattern: (%1) dash offset: %1 [%1]: %2 GammaRay::GuiSupportUiFactory Paint Analyzer GammaRay::InboundConnectionsModel <slot object context> Sender Signal Slot GammaRay::ItemDelegate (Item %r) GammaRay::KJobModel Job Type Status Deleted Killed GammaRay::LaunchPage &Executable: ... &Program arguments: &Add &Remove Pro&be: &Access Mode: Out-of-process, local-only Out-of-process, remote debugging enabled In-process Executable to Launch Executable (*.exe) Working Directory Working Directory: GammaRay::Launcher Uh-oh, there is no default attach injector on this platform. Uh-oh, there is no default launch injector on this platform. Injector %1 not found. Failed to launch target '%1'. Failed to attach to target with PID %1. Error: %1 GammaRay::LauncherWindow Attach Launch Connect Self Test About GammaRay Launcher GammaRay::LegendModel Bounding rect Geometry rect Children rect Transform origin Coordinates (x, y...) Margins/Anchors Padding Grid GammaRay::LibraryInfoModel Location Path GammaRay::LldbInjector The debugger version can't be read (%1) The LLDB version is not compatible: %1 (%2.%3 or higher required) LLDB does not support scripting. Install lldb python support please. GammaRay::LocaleInspectorWidget Locales Timezones GammaRay::LogView Messages Timeline GammaRay::LoggingCategoryModel Category Debug Info Warning Critical GammaRay::MainWindow &GammaRay &Help &Diagnostics &Actions Setti&ngs Main Toolbar &Detach Retract probe from application. &Quit Quit probed application. About &GammaRay About &Qt About &KDAB &Plugins... &Message Statistics... Show GammaRay communication statistics. &Help... Show help browser. KDevelop Kate KWrite gedit gvim Qt Creator GammaRay (%1) Code Navigation Custom... Automatic (No Line numbers) Usage ratio of the GammaRay tools. About GammaRay About KDAB Klarälvdalens Datakonsult AB (KDAB) <qt><p>GammaRay is supported and maintained by KDAB</p>KDAB, the Qt experts, provide consulting and mentoring for developing Qt applications from scratch and in porting from all popular and legacy frameworks to Qt. We continue to help develop parts of Qt and are one of the major contributors to the Qt Project. We can give advanced or standard trainings anywhere around the globe.</p><p>Please visit <a href='https://www.kdab.com'>https://www.kdab.com</a> to meet the people who write code like this.</p></qt> Communication Message Statistics Hide inactive tools Transmission rate: RX %1 Mbps, TX %2 Mbps Custom Code Navigation Specify command to use for code navigation, '%f' will be replaced by the file name, '%l' by the line number and '%c' by the column number. Tool %1 failed to load. &Contribute... GammaRay::MaterialTab &Shader: GammaRay::MessageDisplayModel Debug Warning Critical Fatal Info Unknown <qt><dl><dt><b>Type:</b></dt><dd>%1</dd><dt><b>Time:</b></dt><dd>%2</dd><dt><b>Message:</b></dt><dd>%3</dd><dt><b>Backtrace:</b></dt><dd><pre>%4</pre></dd></dl></qt> <qt><dl><dt><b>Type:</b></dt><dd>%1</dd><dt><b>Time:</b></dt><dd>%2</dd><dt><b>Message:</b></dt><dd>%3</dd></dl></qt> GammaRay::MessageHandlerFactory Messages GammaRay::MessageHandlerWidget Messages Categories QFatal in %1 at %2 Copy Backtrace GammaRay::MessageModel Message Time Category Function Source GammaRay::MessageStatisticsModel Message Count: %1 of %2 (%3%) Message Size: %4 of %5 (%6%) Message Count: %1 of %2 (%3%) Message Size: %4 of %5 (%6%) Object Address: %7 Object: %1 Message Type: %2 Message Count: %3 of %4 (%5%) Message Size: %6 of %7 (%8%) GammaRay::MetaObjectBrowserFactory Meta Objects GammaRay::MetaObjectModel Class GammaRay::MetaObjectTreeClientProxyModel overrides base class signal method uses parameter type not registerd with the meta type system overrides base class property property with type not registered with the meta type system Issues:<ul><li>%1</li></ul> This meta object might have been deleted. %1% Meta Object Class Self Total Incl. Total Self Alive Incl. Alive This column shows the number of objects created and not yet destroyed of a particular type. This column shows the number of objects created and not yet destroyed that inherit from a particular type. This column shows the QMetaObject class hierarchy. This column shows the number of objects created of a particular type. This column shows the number of objects created that inherit from a particular type. GammaRay::MetaTypeBrowserFactory Meta Types GammaRay::MetaTypeBrowserWidget &Rescan Meta Types Check for changes to the meta type database. GammaRay::MetaTypesClientModel yes Type Name Meta Type Id Size Meta Object Type Flags Compare Debug Has equality comparison operators registered. Has debug stream operators registered. GammaRay::MetaTypesModel N/A GammaRay::MethodArgumentModel <unnamed> (%1) Argument Value Type GammaRay::MethodInvocationDialog &Connection type: Invoke Auto Direct Queued GammaRay::MethodsExtension %1: Signal %2 emitted, arguments: %3 %1: Invocation failed: Invalid object, probably got deleted in the meantime. %1: Invocation failed: Can't invoke constructors. %1: Invocation failed.. GammaRay::MethodsTab Invoke Connect to Emit GammaRay::MimeTypesModel Name Comment Glob Patterns Icons Suffixes Aliases GammaRay::ModelCellModel Role #%1 Role Value Type GammaRay::ModelContentDelegate <unnamed: row %1, column %2> GammaRay::ModelInspectorWidget Model Content Model Index: Invalid Internal Id: Internal Pointer: Row: %1 Column: %2 Models Selection Models Cell Content Flags: GammaRay::ModelPickerDialog Hide invisible items GammaRay::NetworkDiscoveryModel Incompatible GammaRay version. Name Host GammaRay::NetworkInterfaceModel Interface Hardware Address Flags GammaRay::NetworkWidget Interfaces Configurations GammaRay::NetworkWidgetFactory Cookies GammaRay::ObjectClassInfoModel Name Value GammaRay::ObjectEnumModel %n element(s) One element %n elements Name Value GammaRay::ObjectInspectorFactory Objects Properties Methods Connections Enums Class Info Attributes Bindings Stack Trace GammaRay::ObjectInspectorWidget Object @ %1 GammaRay::ObjectModelBase Object Type GammaRay::ObjectTreeModel <deleted> GammaRay::OutboundConnectionsModel <slot object> Signal Receiver Slot GammaRay::PaintAnalyzerWidget Argument Stack Trace Visualize Clip Area Highlight current clipping area. GammaRay::PaintBufferClientModel %1 % Command Arguments Cost GammaRay::PaintBufferModel <empty> control rect: %1, elements: %2 %1, brush: %2 %1, pen: %2 position: %1, text: "%2", font: %3 %1 source: %2 %1 offset: %2 glyphs: %1, font: %2 GammaRay::PaintBufferViewer Analyze Painting GammaRay::PaletteModel Role GammaRay::PixelMetricModel Metric Default Value GammaRay::PluginManager Failed to load plugin: %1 GammaRay::PluginMetaData Actions Bluetooth Text Codecs Font Browser GUI Support KJobs Locales Mime Types Models Network Object Visualization Positioning QML Support Qt3D Inspector Qt IVI Quick Scenes Quick Widgets Support Graphics Scenes Script Engines System Wayland Compositor Signals State Machines Styles Text Documents Timers Translations Web Inspector Widgets GammaRay::PositioningWidget Override &Latitude: ° &Longitude: Speed: m/s Acc&uracy: m Timestamp: yyyy-MM-dd hh:mm:ss.zzz t Al&titude: Accurac&y: &Direction: Ma&gnetic variation: Load NMEA file... Load a GPS recording from an NMEA file. Center on position Center map on current position. F3 Load NMEA file Failed to open NMEA file Could not open '%1': %2. GammaRay::PreloadCheck Cannot find file containing symbol: %1 Invalid shared object: %1 Failed to run 'readelf' (binutils) binary: %1 Cannot read shared object: %1 Symbol is not marked as relocatable: %1 GammaRay::Probe PID %1 GammaRay::ProbeABIContext debug release Qt %1.%2 (%3) GammaRay::ProblemClientModel Problem Description Source Location GammaRay::ProblemReporterFactory Problems GammaRay::ProblemReporterWidget Available Problem Checkers Start Scan for Problems GammaRay::ProcessModel Process ID Name State User Name: %1 PID: %2 Owner: %3 Qt ABI: %4 Probe available: %5 yes no GammaRay::PromoLabel Visit KDAB Website GammaRay::PropertiesTab &New Dynamic Property: Name &Type: &Value: Add Remove Reset GammaRay::PropertyDoublePairEditor x GammaRay::PropertyEnumEditor Loading... GammaRay::PropertyExtendedEditor ... GammaRay::PropertyIntPairEditor x GammaRay::PropertyMatrixDialog Edit Matrix Edit 4x4 Matrix Edit Transform Edit 2D Vector Edit 3D Vector Edit 4D Vector Edit Quaternion Edit Unsupported Type GammaRay::PropertyMatrixModel m_1 m_2 m_3 m_4 m1_ m2_ d x/y m3_ m4_ x y z w pitch yaw roll GammaRay::PropertyRectEditorDialog TopLeft: Size Size: GammaRay::PropertyTextEditorDialog Switch to Hex Mode Unsaved changes will be lost Switch to Hex mode Switch to String mode GammaRay::ProxyFactory Plugin does not provide an instance of %1. GammaRay::ProxyToolUiFactory Plugin '%1' could not be loaded. GammaRay::QmlContextModel Context Location GammaRay::QmlContextPropertyAdaptor QML Context Property GammaRay::QmlSupport <empty> <%1 entries> GammaRay::QmlSupportUiFactory QML Context QML Type GammaRay::Qt3DGeometryTab B&uffer: Reset Camera Reset camera. Show Normals Visualize normal vectors. Show Tangents Visualize tangent vectors. View Geometry Visualize geometry. View Buffers View raw buffer data. Cull Back Enable back face culling. Shading: Flat Wireframe Phong Normal Texture Coordinate Tangent Color GammaRay::Qt3DInspectorUiFactory Geometry GammaRay::Qt3DInspectorWidget &Engine: Scene Search Render Settings Entity @ %1 Frame Graph Node @ %1 GammaRay::QtIviObjectModel Name Value Writable Override Type GammaRay::QtIviPropertyModel Name Value Writable Override Type GammaRay::QuickClientItemModel is invisible has a size of zero is out of view is partially out of view has inactive focus has active focus just received an event GammaRay::QuickInspectorUiFactory Material Geometry Texture GammaRay::QuickInspectorWidget Items Scene Graph Save as &Image... Save current frame as image. Save as &Image (with decoration)... Save current frame with decoration rendered as image. Analyze Painting... Analyzer software renderer paint operations. F9 Enable slow down mode Enable slow down mode for animations Save As Image Image Files (*.png *.jpg) GammaRay::QuickOverlayLegend Legend Show Legend <b>Show Legend</b><br>This shows a legend explaining the various diagnostic decorations. GammaRay::QuickSceneControlWidget Visualize Clipping <b>Visualize Clipping</b><br/>Items with the property <i>clip</i> set to true, will cut off their and their children's rendering at the items' bounds. While this is a handy feature it comes with quite some cost, like disabling some performance optimizations.<br/>With this tool enabled the QtQuick renderer highlights items, that have clipping enabled, so you can check for items, that have clipping enabled unnecessarily. Visualize Overdraw <b>Visualize Overdraw</b><br/>The QtQuick renderer doesn't detect if an item is obscured by another opaque item, is completely outside the scene or outside a clipped ancestor and thus doesn't need to be rendered. You thus need to take care of setting <i>visible: false</i> for hidden items, yourself.<br/>With this tool enabled the QtQuick renderer draws a 3D-Box visualizing the layers of items that are drawn. Visualize Batches <b>Visualize Batches</b><br/>Where a traditional 2D API, such as QPainter, Cairo or Context2D, is written to handle thousands of individual draw calls per frame, OpenGL is a pure hardware API and performs best when the number of draw calls is very low and state changes are kept to a minimum. Therefore the QtQuick renderer combines the rendering of similar items into single batches.<br/>Some settings (like <i>clip: true</i>) will cause the batching to fail, though, causing items to be rendered separately. With this tool enabled the QtQuick renderer visualizes those batches, by drawing all items that are batched using the same color. The fewer colors you see in this mode the better. Visualize Changes <b>Visualize Changes</b><br>The QtQuick scene is only repainted, if some item changes in a visual manner. Unnecessary repaints can have a bad impact on the performance. With this tool enabled, the QtQuick renderer will thus on each repaint highlight the item(s), that caused the repaint. Visualize Controls <b>Visualize Controls</b><br>The QtQuick scene is rendered normaly, in addition overlays will highlight Qt Quick Controls. Decorate Target <b>Decorate Target</b><br>This enables diagnostic decorations on the target application, such as anchor and layout information, helper grids or control overlays. Layout Grid <b>Layout Grid</b><br>Configure the layout helper grid. GammaRay::QuickScenePreviewWidget No remote view available. (This happens e.g. when the window is minimized or the scene is hidden) GammaRay::RemoteModel Loading... GammaRay::RemoteViewWidget No remote view available. Pan View <b>Pan view</b><br>Default mode. Click and drag to move the preview. Won't impact the original application in any way. Measure Pixel Sizes <b>Measure pixel-sizes</b><br>Choose this mode, click somewhere and drag to measure the distance between the point you clicked and the point where your mouse pointer is. (Measured in scene coordinates). Pick Element Pick <b>Pick Element</b><br>Select an element for inspection by clicking on it. Redirect Input <b>Redirect Input</b><br>In this mode all mouse input is redirected directly to the original application,so you can control the application directly from within GammaRay. Inspect Colors <b>Inspect Colors</b><br>Inspect the RGBA channels of the currently hovered pixel Zoom Out Zoom In Display FPS GammaRay::ResourceBrowserFactory Resources GammaRay::ResourceBrowserWidget Select a Resource to Preview Save As... Save As GammaRay::ResourceInfo Version: %1 GammaRay::ResourceInfoExtractors Role: %1 Buffer size: (%1x%2) Is mapped: %1 Has content: %1 Title: "%1" Class name: "%1" Surface: %1 Manufacturer: %1 Model: %1 Physical size: (%1x%2) Position: (%1x%2) Current mode: %1x%2@%3Hz Scale factor: %1 Transform: %1 Subpixel: %1 GammaRay::ResourceModel Name Size Kind Match OS X Finder Type All other platforms Date Modified %1 TB %1 GB %1 MB %1 KB %1 byte(s) Root Folder %1 File GammaRay::SGGeometryTab Raw Vertex Data Preview GammaRay::SGWireframeWidget Unknown Drawing mode: %1 GammaRay::SceneInspectorWidget QGraphicsItem @ %1 GammaRay::SceneModel Item Type GammaRay::SearchLineController Search GammaRay::SelectedCodecsModel Codec Encoded Data GammaRay::SelectionModelModel Object #Items #Rows #Columns Type GammaRay::SelfTest No probe found for ABI %1. Probe at %1 is invalid. Found valid probe for ABI %1 at %2. No probes found - GammaRay not functional. No injectors available - GammaRay not functional. Unable to create instance of injector %1. Injector %1 failed to pass its self-test: %2. Injector %1 successfully passed its self-test. GammaRay::SignalHistoryDelegate <unknown> %1 at %2 ms GammaRay::SignalHistoryModel Address: %1 Object Type Events GammaRay::SignalMonitorWidget Pause Zoom Level: GammaRay::StackTraceModel Function Location GammaRay::StandardIconModel Name Icon GammaRay::StandardPathsModel Type Display Name Standard Locations Writable Location GammaRay::StandardPathsProxy Locations Standard / Writable GammaRay::StateMachineViewerServer Clearing filter. Setting filter on: %1 State entered: %1 State exited: %1 Log [label=%1]: %2 GammaRay::StateMachineViewerWidget S&tate machine: Show Log Hide Log Start/Stop State Machine Entity @ %1 Start State Machine Stop State Machine GammaRay::StateModel State Type GammaRay::StyleElementStateTablePage Cell &Width: px Cell &Height: &Zoom: GammaRay::StyleHintModel Style Hint Value Return Data GammaRay::StyleInjector Injector style plugin is not found in the Qt style plug-in search path or cannot be loaded GammaRay was compiled without QtWidget support, style injector is not available. GammaRay::StyleInspectorWidget &Style: Primitives Controls Complex Controls Pixel Metric Standard Icons Standard Palette Style Hints GammaRay::SysInfoModel Key Value GammaRay::SysInfoWidget Form Qt System information: Library information: Standard Paths Environment GammaRay::TextDocumentFormatModel Property Value Type GammaRay::TextDocumentInspectorWidget All documents: Document structure: Element format: Content HTML GammaRay::TextDocumentModel Root Frame Element Format Table Frame Block: %1 Cell %1x%2 Fragment: %1 Layout Range: %1 no format Image: %1 GammaRay::TextureTab <b>Identified Problems:</b> GiB MiB KiB B 0 B Visualize Texture Problems Transparency waste: %1% / %2. Using a BorderImage for this texture would save %1% / %2. Texture has only one color, consider using a widget or a rectangle. Texture is fully transparent, consider using margins or anchoring. GammaRay::TimerTopWidget ... GammaRay::TimezoneClientModel yes IANA Id Country Standard Display Name DST Windows Id GammaRay::TimezoneOffsetDataClientModel Transition Time (UTC) Offset to UTC Standard Time Offset DST Offset Abbreviation GammaRay::ToolPluginErrorModel Plugin Name Plugin File Error Message GammaRay::ToolPluginModel Id Supported types GammaRay::TranslatorInspectorWidget Reset Selected Translations Reset manually changed translations back to their original content. Send LanguageChange Event Sends a QEvent::LanguageChange event to the application to reload changed translations. GammaRay::TranslatorWrapperProxy Context Source Text Disambiguation Translation GammaRay::TranslatorsModel Object Type Translations GammaRay::Util <p style='white-space:pre'>Object name: %1 (Address: %2) Type: %3 Parent: %4 (Address: %5) Number of children: %6</p> GammaRay::VariantHandler <no icon> left: %1, top: %2, right: %3, bottom: %4 GammaRay::VtkPanel Layout: Tree Layout Span Tree Layout Force Directed Layout Force Directed Layout (3D) Simple 2D Layout Stereo: Off Crystal Eyes Red/Blue Interlaced Left Right Dresden Anaglyph Checkboard GammaRay::WebInspectorWidget about:blank GammaRay::Widget3DView Window: Widget @ %1 GammaRay::WidgetInspector::QML Wireframe Horizontals Class: Parent: Position: Size: GammaRay::WidgetInspectorUiFactory Attributes GammaRay::WidgetInspectorWidget Save as &Image... Save currently selected widget as image. Save as &SVG... Renders the currently selected image as SVG vector graphic. Save as &PDF... Renders the currently selected widget in to a PDF file. Save as &UI file... Saves the currently selected widget as a Qt designer UI file. &Analyze Painting... Show Tab Focus Chain 3D View Widget @ %1 Save As Image Image Files (*.png *.jpg) Save As SVG Scalable Vector Graphics (*.svg) Save As PDF PDF (*.pdf) Save As Qt Designer UI File Qt Designer UI File (*.ui) Tree F9 KUserFeedback::ApplicationVersionSource The version of the application. KUserFeedback::AuditLogBrowserDialog Dialog Da&ta Submission: Delete Log KUserFeedback::AuditLogUiController Basic System Information Basic Usage Statistics Detailed System Information Detailed Usage Statistics Unable to open file %1: %2. Category: <i>%1</i><br/> Key: <i>%1</i><br/> Submitted data: <tt>%1</tt><br/><br/> KUserFeedback::CompilerInfoSource The compiler used to build this application. KUserFeedback::FeedbackConfigDialog Feedback Settings Contribute! No, I do not want to contribute. KUserFeedback::FeedbackConfigUiController We make this application for you. You can help us improve it by contributing information on how you use it. This allows us to make sure we focus on things that matter to you. Contributing statistics is of course entirely anonymous, will not use any kind of unique identifier and will not cover any data you process with this application. Share basic system information. No unique identification is included, nor data processed with the application. Share basic system information and basic statistics on how often you use the application. No unique identification is included, nor data processed with the application. Share basic statistics on how often you use the application, as well as detailed information about your system. No unique identification is included, nor data processed with the application. Share detailed system information and statistics on how often individual features of the application are used. No unique identification is included, nor data processed with the application. We make %1 for you. You can help us improve it by contributing information on how you use it. This allows us to make sure we focus on things that matter to you. Contributing statistics is of course entirely anonymous, will not use any kind of unique identifier and will not cover any data you process with %1. Share basic system information. No unique identification is included, nor data processed with %1. Share basic system information and basic statistics on how often you use %1. No unique identification is included, nor data processed with %1. Share basic statistics on how often you use %1, as well as detailed information about your system. No unique identification is included, nor data processed with %1. Share detailed system information and statistics on how often individual features of %1 are used. No unique identification is included, nor data processed with %1. We make this application for you. In order to ensure it actually does what you need it to do we would like to ask you about your use cases and your feedback, in the form of a web survey. I will occasionally participate in web surveys about the application, not more than four times a year though. I will participate in web surveys whenever one is available. Surveys can of course be deferred or skipped. We make %1 for you. In order to ensure it actually does what you need it to do we would like to ask you about your use cases and your feedback, in the form of a web survey. I will occasionally participate in web surveys about %1, not more than four times a year though. I will participate in web surveys about %1 whenever one is available. Surveys can of course be deferred or skipped. KUserFeedback::FeedbackConfigWidget Contribute Statistics TextLabel Show the raw data that is going to be shared. Participate in Surveys <a href="auditLog">View previously submitted data...</a> KUserFeedback::NotificationPopup Help us make this application better! You can help us improving this application by sharing statistics and participate in surveys. Help us make %1 better! You can help us improving %1 by sharing statistics and participate in surveys. Contribute... We are looking for your feedback! We would like a few minutes of your time to provide feedback about this application in a survey. We would like a few minutes of your time to provide feedback about %1 in a survey. Participate KUserFeedback::OpenGLInfoSource Information about type, version and vendor of the OpenGL stack. KUserFeedback::PlatformInfoSource Type and version of the operating system. KUserFeedback::QtVersionSource The Qt version used by this application. KUserFeedback::StartCountSource How often the application has been started. KUserFeedback::UsageTimeSource The total amount of time the application has been used. QFileDialog My Computer gammaray-2.11.3/translations/ljsonupdate.cpp000066400000000000000000000121371413310443000211460ustar00rootroot00000000000000/* ljsonupdate.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "config-gammaray-version.h" #include #include #include #include #include #include #include static int extractMessages(const QStringList &jsons, const QString &outFileName) { QFile out(outFileName); if (!out.open(QFile::WriteOnly)) { qWarning() << "Can't open output file" << outFileName; return 1; } for (const auto &jsonFile : jsons) { QFile inFile(jsonFile); if (!inFile.open(QFile::ReadOnly)) { qWarning() << "Can't open input file" << jsonFile; return 1; } const auto doc = QJsonDocument::fromJson(inFile.readAll()); const auto obj = doc.object(); const auto str = obj.value("name").toString(); // TODO make the keys configurable too if (str.isEmpty()) continue; out.write(R"(QT_TRANSLATE_NOOP("GammaRay::PluginMetaData", ")"); out.write(str.toUtf8()); out.write("\");\n"); } return 0; } static int mergeMessages(const QStringList &jsons, const QString &qmFile, const QString &lang) { QTranslator t; if (!t.load(qmFile)) { if (QFile::exists(qmFile)) { qDebug() << "Translation catalog empty, skipping."; return 0; } else { qWarning() << "Can't open translation catalog" << qmFile; return 1; } } for (const auto &jsonFile : jsons) { QFile inFile(jsonFile); if (!inFile.open(QFile::ReadOnly)) { qWarning() << "Can't open input file" << jsonFile; return 1; } const auto doc = QJsonDocument::fromJson(inFile.readAll()); auto obj = doc.object(); const auto sourceString = obj.value("name").toString(); // TODO make the keys configurable too if (sourceString.isEmpty()) continue; const auto trString = t.translate("GammaRay::PluginMetaData", sourceString.toUtf8()); if (trString.isEmpty() || trString == sourceString) continue; obj.insert(QLatin1String("name[") + lang + ']', trString); inFile.close(); QFile outFile(jsonFile); if (!outFile.open(QFile::WriteOnly)) { qWarning() << "Can't write to JSON file" << jsonFile; return 1; } outFile.write(QJsonDocument(obj).toJson()); } return 0; } int main(int argc, char **argv) { QCoreApplication app(argc, argv); QCoreApplication::setApplicationName("ljsonupdate"); QCoreApplication::setApplicationVersion(GAMMARAY_VERSION_STRING); QCommandLineParser parser; parser.setApplicationDescription("Translation update tool for JSON plug-in meta data."); parser.addHelpOption(); parser.addVersionOption(); QCommandLineOption extractCommand("extract", "Extract messages from the given JSON files."); parser.addOption(extractCommand); QCommandLineOption outputFile("out", "Output of the extraction.", "output-file"); parser.addOption(outputFile); QCommandLineOption mergeCommand("merge", "Merge messages in the given JSON files."); parser.addOption(mergeCommand); QCommandLineOption qmFile("qm", ".qm file with translated messages.", "qm-file"); parser.addOption(qmFile); QCommandLineOption language("lang", "Language identifier for translated JSON keys.", "lang"); parser.addOption(language); parser.addPositionalArgument("json", "JSON files to process."); parser.process(app); auto jsons = parser.positionalArguments(); if (jsons.isEmpty()) { qWarning() << "No input JSON files specified."; return 1; } if (parser.isSet(extractCommand)) { std::sort(jsons.begin(), jsons.end()); // stabilize the output between different runs return extractMessages(jsons, parser.value(outputFile)); } else if (parser.isSet(mergeCommand)) { return mergeMessages(jsons, parser.value(qmFile), parser.value(language)); } parser.showHelp(1); } gammaray-2.11.3/ui/000077500000000000000000000000001413310443000140025ustar00rootroot00000000000000gammaray-2.11.3/ui/CMakeLists.txt000066400000000000000000000131601413310443000165430ustar00rootroot00000000000000# # Shared code between in-process and out-of-process UI # set(gammaray_ui_srcs aboutdata.cpp aboutwidget.cpp themedimagelabel.cpp clientdecorationidentityproxymodel.cpp clientpropertymodel.cpp clienttoolmanager.cpp clienttoolmodel.cpp contextmenuextension.cpp deferredtreeview.cpp editabletypesmodel.cpp itemdelegate.cpp methodinvocationdialog.cpp modelpickerdialog.cpp palettemodel.cpp propertybinder.cpp propertywidget.cpp propertywidgettab.cpp proxytooluifactory.cpp visibilityfilterproxymodel.cpp splashscreen.cpp searchlinecontroller.cpp tooluifactory.cpp uiintegration.cpp uistatemanager.cpp uiresources.cpp remoteviewwidget.cpp trailingcolorlabel.cpp helpcontroller.cpp paintanalyzerwidget.cpp paintanalyzerreplayview.cpp paintbufferclientmodel.cpp paintbufferviewer.cpp propertyeditor/propertycoloreditor.cpp propertyeditor/propertydoublepaireditor.cpp propertyeditor/propertyeditorfactory.cpp propertyeditor/propertyextendededitor.cpp propertyeditor/propertyfonteditor.cpp propertyeditor/propertyintpaireditor.cpp propertyeditor/propertypaletteeditor.cpp propertyeditor/palettedialog.cpp propertyeditor/propertyeditordelegate.cpp propertyeditor/propertyenumeditor.cpp propertyeditor/propertymatrixdialog.cpp propertyeditor/propertymatrixeditor.cpp propertyeditor/propertymatrixmodel.cpp propertyeditor/propertytexteditor.cpp propertyeditor/propertyrecteditor.cpp codeeditor/codeeditor.cpp codeeditor/codeeditorsidebar.cpp tools/messagehandler/messagedisplaymodel.cpp tools/messagehandler/messagehandlerwidget.cpp tools/messagehandler/messagehandlerclient.cpp tools/metaobjectbrowser/metaobjectbrowserwidget.cpp tools/metaobjectbrowser/metaobjecttreeclientproxymodel.cpp tools/metatypebrowser/metatypebrowserwidget.cpp tools/metatypebrowser/metatypesclientmodel.cpp tools/metatypebrowser/metatypebrowserclient.cpp tools/objectinspector/objectinspectorwidget.cpp tools/objectinspector/propertiestab.cpp tools/objectinspector/propertiesextensionclient.cpp tools/objectinspector/clientmethodmodel.cpp tools/objectinspector/methodsextensionclient.cpp tools/objectinspector/methodstab.cpp tools/objectinspector/connectionstab.cpp tools/objectinspector/connectionsclientproxymodel.cpp tools/objectinspector/connectionsextensionclient.cpp tools/objectinspector/enumstab.cpp tools/objectinspector/classinfotab.cpp tools/objectinspector/methodstab.cpp tools/objectinspector/applicationattributetab.cpp tools/objectinspector/bindingtab.cpp tools/objectinspector/stacktracetab.cpp tools/problemreporter/problemreporterwidget.cpp tools/problemreporter/problemreporterclient.cpp tools/problemreporter/problemclientmodel.cpp tools/resourcebrowser/clientresourcemodel.cpp tools/resourcebrowser/resourcebrowserwidget.cpp tools/resourcebrowser/resourcebrowserclient.cpp resources/ui.qrc ${CMAKE_CURRENT_BINARY_DIR}/resources/classicons.qrc ) add_library(gammaray_ui SHARED ${gammaray_ui_srcs}) generate_export_header(gammaray_ui) set_target_properties(gammaray_ui PROPERTIES ${GAMMARAY_DEFAULT_LIBRARY_PROPERTIES} OUTPUT_NAME gammaray_ui-${GAMMARAY_PROBE_ABI} ) gammaray_set_rpath(gammaray_ui ${LIB_INSTALL_DIR}) target_compile_features(gammaray_ui PUBLIC ${GAMMARAY_REQUIRED_CXX_FEATURES}) gammaray_target_relocatable_interfaces(gammaray_ui_ipaths) target_include_directories(gammaray_ui PUBLIC $ $) target_include_directories(gammaray_ui SYSTEM PRIVATE ${Qt5Widgets_PRIVATE_INCLUDE_DIRS}) target_link_libraries(gammaray_ui LINK_PUBLIC Qt5::Core Qt5::Gui Qt5::Widgets gammaray_common LINK_PRIVATE gammaray_common_internal gammaray_kitemmodels ) if(TARGET KF5::SyntaxHighlighting) target_link_libraries(gammaray_ui LINK_PRIVATE KF5::SyntaxHighlighting) endif() set(gammaray_ui_internal_srcs sidepane.cpp aboutpluginsdialog.cpp mainwindow.cpp clienttoolfilterproxymodel.cpp aboutdialog.cpp ) add_library(gammaray_ui_internal STATIC ${gammaray_ui_internal_srcs}) set_target_properties(gammaray_ui_internal PROPERTIES POSITION_INDEPENDENT_CODE ON) target_compile_features(gammaray_ui_internal PUBLIC ${GAMMARAY_REQUIRED_CXX_FEATURES}) target_include_directories(gammaray_ui_internal SYSTEM PRIVATE ${Qt5Gui_PRIVATE_INCLUDE_DIRS}) target_link_libraries(gammaray_ui_internal Qt5::Core Qt5::Gui Qt5::Widgets gammaray_common_internal gammaray_ui gammaray_kitemmodels ) if(NOT GAMMARAY_DISABLE_FEEDBACK) target_link_libraries(gammaray_ui_internal gammaray_kuserfeedback) endif() if(NOT GAMMARAY_PROBE_ONLY_BUILD) install(TARGETS gammaray_ui EXPORT GammaRayTargets ${INSTALL_TARGETS_DEFAULT_ARGS}) gammaray_install_headers( ${CMAKE_CURRENT_BINARY_DIR}/gammaray_ui_export.h clientdecorationidentityproxymodel.h clienttoolmanager.h propertyeditor/propertyeditordelegate.h propertywidget.h propertywidgettab.h tooluifactory.h uiintegration.h uistatemanager.h uiresources.h ) ecm_generate_pri_file(BASE_NAME GammaRayUi LIB_NAME gammaray_ui-${GAMMARAY_PROBE_ABI} DEPS "core gui widgets GammaRayCommon" FILENAME_VAR PRI_FILENAME INCLUDE_INSTALL_DIR ${INCLUDE_INSTALL_DIR} ) install(FILES ${PRI_FILENAME} DESTINATION ${ECM_MKSPECS_INSTALL_DIR}) else() install(TARGETS gammaray_ui ${INSTALL_TARGETS_DEFAULT_ARGS}) endif() if(MSVC) install(FILES "$/$" DESTINATION ${BIN_INSTALL_DIR} CONFIGURATIONS Debug RelWithDebInfo) endif() add_subdirectory(resources) gammaray-2.11.3/ui/aboutdata.cpp000066400000000000000000000071031413310443000164530ustar00rootroot00000000000000/* aboutdata.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "aboutdata.h" #include #include #include #include #include using namespace GammaRay; namespace GammaRay { class AboutDataContext { Q_DECLARE_TR_FUNCTIONS(GammaRay::AboutDataContext) }; } QStringList AboutData::authors() { QFile f(QStringLiteral(":/gammaray/authors")); if (f.open(QFile::ReadOnly)) { return QString::fromUtf8(f.readAll()).split('\n', QString::SkipEmptyParts); } else { Q_ASSERT_X(false, "AboutData::authors()", "cannot open the authors resource file"); qWarning() << "Failed to open the authors resource file"; return QStringList(AboutDataContext::tr("Unable to read the Authors list")); } } QStringList AboutData::authorsAsHtml() { const auto plainAuthors = authors(); QStringList a; a.reserve(plainAuthors.size()); for (const QString &author : plainAuthors) { a.push_back(author.toHtmlEscaped()); } return a; } QString AboutData::aboutTitle() { return AboutDataContext::tr("GammaRay %1").arg(QStringLiteral(GAMMARAY_VERSION_STRING)); } QString AboutData::aboutHeader() { return AboutDataContext::trUtf8( "

    The Qt application inspection and manipulation tool." "Learn more at https://www.kdab.com/gammaray/.

    " "

    Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, " "a KDAB Group company, info@kdab.com

    " "

    StackWalker code Copyright (c) 2005-2019, Jochen Kalmbach, All rights reserved
    " "lz4 fast LZ compression code Copyright (C) 2011-2015, Yann Collet, All rights reserved
    " "backward-cpp code Copyright 2013-2017 Google Inc. All rights reserved.

    "); } QString AboutData::aboutAuthors() { return AboutDataContext::trUtf8( "

    Authors:
    %1

    " ).arg(authorsAsHtml().join(QStringLiteral("
    "))); } QString AboutData::aboutFooter() { return AboutDataContext::trUtf8( "

    GammaRay and the GammaRay logo are registered trademarks of Klarälvdalens Datakonsult AB " "in the European Union, the United States and/or other countries. Other product and " "company names and logos may be trademarks or registered trademarks of their respective companies.

    " "
    "); } gammaray-2.11.3/ui/aboutdata.h000066400000000000000000000030161413310443000161170ustar00rootroot00000000000000/* aboutdata.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_ABOUTDATA_H #define GAMMARAY_ABOUTDATA_H #include "gammaray_ui_export.h" #include namespace GammaRay { namespace AboutData { QStringList authors(); QStringList authorsAsHtml(); GAMMARAY_UI_EXPORT QString aboutTitle(); GAMMARAY_UI_EXPORT QString aboutHeader(); GAMMARAY_UI_EXPORT QString aboutAuthors(); GAMMARAY_UI_EXPORT QString aboutFooter(); } } #endif // GAMMARAY_ABOUTDATA_H gammaray-2.11.3/ui/aboutdialog.cpp000066400000000000000000000044551413310443000170100ustar00rootroot00000000000000/* aboutdialog.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "aboutdialog.h" #include "aboutwidget.h" #include #include #include using namespace GammaRay; AboutDialog::AboutDialog(QWidget *parent) : QDialog(parent) , ui(new AboutWidget) { auto button = new QDialogButtonBox(this); button->setStandardButtons(QDialogButtonBox::Close); QVBoxLayout *vl = new QVBoxLayout(this); vl->addWidget(ui); vl->addWidget(button); connect(button, &QDialogButtonBox::rejected, this, &QWidget::close); } AboutDialog::~AboutDialog() = default; void AboutDialog::setLogo(const QString &iconFileName) { ui->setLogo(iconFileName); } void AboutDialog::setThemeLogo(const QString &fileName) { ui->setThemeLogo(fileName); } void AboutDialog::setTitle(const QString &title) { ui->setTitle(title); } void AboutDialog::setHeader(const QString &header) { ui->setHeader(header); } void AboutDialog::setAuthors(const QString &authors) { ui->setAuthors(authors); } void AboutDialog::setFooter(const QString &footer) { ui->setFooter(footer); } void AboutDialog::setText(const QString &text) { ui->setText(text); } QSize AboutDialog::sizeHint() const { return {960, 730}; } gammaray-2.11.3/ui/aboutdialog.h000066400000000000000000000034311413310443000164460ustar00rootroot00000000000000/* aboutdialog.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_ABOUTDIALOG_H #define GAMMARAY_ABOUTDIALOG_H #include namespace GammaRay { class AboutWidget; class AboutDialog : public QDialog { Q_OBJECT public: explicit AboutDialog(QWidget *parent = nullptr); ~AboutDialog() override; void setLogo(const QString &iconFileName); void setThemeLogo(const QString &fileName); void setTitle(const QString &title); void setHeader(const QString &header); void setAuthors(const QString &authors); void setFooter(const QString &footer); void setText(const QString &text); QSize sizeHint() const override; private: AboutWidget *ui; }; } #endif // GAMMARAY_ABOUTDIALOG_H gammaray-2.11.3/ui/aboutpluginsdialog.cpp000066400000000000000000000054411413310443000204060ustar00rootroot00000000000000/* aboutpluginsdialog.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2012-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Kevin Funk Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "aboutpluginsdialog.h" #include #include #include #include #include #include #include using namespace GammaRay; AboutPluginsDialog::AboutPluginsDialog(QWidget *parent, Qt::WindowFlags f) : QDialog(parent, f) { QLayout *layout = nullptr; auto *vbox = new QVBoxLayout(this); { auto *toolView = new QTableView(this); toolView->setShowGrid(false); toolView->setSelectionBehavior(QAbstractItemView::SelectRows); toolView->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); toolView->verticalHeader()->hide(); toolView->setModel(ObjectBroker::model(QStringLiteral("com.kdab.GammaRay.ToolPluginModel"))); QGroupBox *toolBox = new QGroupBox(tr("Loaded Plugins"), this); layout = new QHBoxLayout(toolBox); layout->addWidget(toolView); vbox->addWidget(toolBox); } { auto *errorView = new QTableView(this); errorView->setShowGrid(false); errorView->setSelectionBehavior(QAbstractItemView::SelectRows); errorView->setModel(ObjectBroker::model(QStringLiteral( "com.kdab.GammaRay.ToolPluginErrorModel"))); errorView->verticalHeader()->hide(); errorView->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); QGroupBox *errorBox = new QGroupBox(tr("Failed Plugins"), this); layout = new QHBoxLayout(errorBox); layout->addWidget(errorView); vbox->addWidget(errorBox); } setWindowTitle(tr("GammaRay: Plugin Info")); } gammaray-2.11.3/ui/aboutpluginsdialog.h000066400000000000000000000026731413310443000200570ustar00rootroot00000000000000/* aboutpluginsdialog.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2012-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Kevin Funk Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_ABOUTPLUGINSDIALOG_H #define GAMMARAY_ABOUTPLUGINSDIALOG_H #include namespace GammaRay { class AboutPluginsDialog : public QDialog { Q_OBJECT public: explicit AboutPluginsDialog(QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); }; } #endif // GAMMARAY_ABOUTPLUGINSDIALOG_H gammaray-2.11.3/ui/aboutwidget.cpp000066400000000000000000000072241413310443000170310ustar00rootroot00000000000000/* aboutwidget.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "aboutwidget.h" #include "ui_aboutwidget.h" #include #include #include using namespace GammaRay; AboutWidget::AboutWidget(QWidget *parent) : QWidget(parent) , ui(new Ui::AboutWidget) { ui->setupUi(this); // Try to reduce the scrollbar width to hide it a bit... auto vsb = ui->textAuthors->verticalScrollBar(); #if defined(Q_OS_MAC) vsb->setAttribute(Qt::WA_MacSmallSize); #else vsb->setFixedWidth(10); #endif } AboutWidget::~AboutWidget() = default; void AboutWidget::setLogo(const QString &iconFileName) { ui->logoLabel->setPixmap(iconFileName); } void AboutWidget::setThemeLogo(const QString &fileName) { ui->logoLabel->setThemeFileName(fileName); } void AboutWidget::setTitle(const QString &title) { ui->titleLabel->setText(title); } void AboutWidget::setHeader(const QString &header) { ui->textHeader->setText(header); } void AboutWidget::setAuthors(const QString &authors) { ui->textAuthors->setHtml(authors); } void AboutWidget::setFooter(const QString &footer) { ui->textFooter->setText(footer); } void AboutWidget::setText(const QString &text) { setHeader(text); ui->textAuthors->setVisible(false); ui->textFooter->setVisible(false); } void AboutWidget::setBackgroundWindow(QWidget *window) { if (m_backgroundWindow == window) return; if (m_backgroundWindow) { m_backgroundWindow->removeEventFilter(this); m_backgroundWindow->update(); } m_backgroundWindow = window; m_watermark = QPixmap(); if (m_backgroundWindow) { m_backgroundWindow->installEventFilter(this); m_backgroundWindow->update(); } } void AboutWidget::showEvent(QShowEvent *event) { QWidget::showEvent(event); setBackgroundWindow(window()); } bool AboutWidget::eventFilter(QObject *object, QEvent *event) { if (object == m_backgroundWindow) { if (event->type() == QEvent::ScreenChangeInternal) m_watermark = QPixmap(); else if (event->type() == QEvent::Paint) { if (m_watermark.isNull()) m_watermark = UIResources::themedPixmap(QStringLiteral("watermark.png"), this); qreal dpr = 1.0; dpr = m_watermark.devicePixelRatio(); QPainter p(m_backgroundWindow); p.drawPixmap(m_backgroundWindow->width() - (m_watermark.width() / dpr), m_backgroundWindow->height() - (m_watermark.height() / dpr), m_watermark); } } return QWidget::eventFilter(object, event); } gammaray-2.11.3/ui/aboutwidget.h000066400000000000000000000041151413310443000164720ustar00rootroot00000000000000/* aboutwidget.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_ABOUTWIDGET_H #define GAMMARAY_ABOUTWIDGET_H #include "gammaray_ui_export.h" #include #include namespace GammaRay { namespace Ui { class AboutWidget; } class GAMMARAY_UI_EXPORT AboutWidget : public QWidget { Q_OBJECT public: explicit AboutWidget(QWidget *parent = nullptr); ~AboutWidget() override; void setLogo(const QString &iconFileName); void setThemeLogo(const QString &fileName); void setTitle(const QString &title); void setHeader(const QString &header); void setAuthors(const QString &authors); void setFooter(const QString &footer); void setText(const QString &text); void setBackgroundWindow(QWidget *window); protected: void showEvent(QShowEvent *event) override; bool eventFilter(QObject *object, QEvent *event) override; private: QScopedPointer ui; QPointer m_backgroundWindow; QPixmap m_watermark; }; } #endif // GAMMARAY_ABOUTWIDGET_H gammaray-2.11.3/ui/aboutwidget.ui000066400000000000000000000115221413310443000166600ustar00rootroot00000000000000 GammaRay::AboutWidget 0 0 400 300 0 0 0 0 0 0 Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop 0 0 0 0 0 0 75 true 0 0 Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop true true Qt::TextBrowserInteraction 0 0 0 0 0 0 236 236 236 QFrame::NoFrame QFrame::Plain 40 true 0 0 Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop true true Qt::TextBrowserInteraction ThemedImageLabel QLabel
    themedimagelabel.h
    gammaray-2.11.3/ui/clientdecorationidentityproxymodel.cpp000066400000000000000000000050321413310443000237310ustar00rootroot00000000000000/* clientdecorationidentityproxymodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Filipe Azevedo 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, see . */ #include "clientdecorationidentityproxymodel.h" #include #include #include #include #include using namespace GammaRay; ClientDecorationIdentityProxyModel::ClientDecorationIdentityProxyModel(QObject *parent) : QIdentityProxyModel(parent) , m_classesIconsRepository(ObjectBroker::object()) { } QVariant ClientDecorationIdentityProxyModel::data(const QModelIndex &index, int role) const { if (role == Qt::DecorationRole) { const QVariant decoration = QIdentityProxyModel::data(index, role); if (!decoration.isNull()) { return decoration; } if (m_classesIconsRepository) { const auto iconId = QIdentityProxyModel::data(index, ObjectModel::DecorationIdRole); if (iconId.isNull()) return QVariant(); const auto it = m_icons.constFind(iconId.toInt()); if (it != m_icons.constEnd()) return it.value(); const auto icon = QIcon(m_classesIconsRepository->filePath(iconId.toInt())); if (icon.isNull()) return QVariant(); m_icons.insert(iconId.toInt(), icon); return QVariant::fromValue(icon); } return QVariant(); } return QIdentityProxyModel::data(index, role); } gammaray-2.11.3/ui/clientdecorationidentityproxymodel.h000066400000000000000000000036441413310443000234050ustar00rootroot00000000000000/* clientdecorationidentityproxymodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Filipe Azevedo 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, see . */ #ifndef GAMMARAY_CLIENTDECORATIONIDENTITYPROXYMODEL_H #define GAMMARAY_CLIENTDECORATIONIDENTITYPROXYMODEL_H #include "gammaray_ui_export.h" #include #include #include #include namespace GammaRay { class ClassesIconsRepository; /*! Proxy model for client-side resolution of class icons. */ class GAMMARAY_UI_EXPORT ClientDecorationIdentityProxyModel : public QIdentityProxyModel { Q_OBJECT public: explicit ClientDecorationIdentityProxyModel(QObject *parent = nullptr); QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; private: QPointer m_classesIconsRepository; mutable QHash m_icons; }; } #endif // GAMMARAY_CLIENTDECORATIONIDENTITYPROXYMODEL_H gammaray-2.11.3/ui/clientpropertymodel.cpp000066400000000000000000000070231413310443000206140ustar00rootroot00000000000000/* clientpropertymodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2018-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "clientpropertymodel.h" #include #include using namespace GammaRay; ClientPropertyModel::ClientPropertyModel(QObject *parent) : QIdentityProxyModel(parent) { } ClientPropertyModel::~ClientPropertyModel() = default; QVariant ClientPropertyModel::data(const QModelIndex& index, int role) const { if (role == Qt::ToolTipRole && index.isValid()) { const auto idx0 = index.sibling(index.row(), 0); const auto f = idx0.data(PropertyModel::PropertyFlagsRole).value(); const auto revision = idx0.data(PropertyModel::PropertyRevisionRole); const auto notifySignal = idx0.data(PropertyModel::NotifySignalRole).toString(); QStringList tt; if (f != PropertyModel::None) { QStringList fs; if (f & PropertyModel::Constant) fs.push_back(tr("constant")); if (f & PropertyModel::Designable) fs.push_back(tr("designable")); if (f & PropertyModel::Final) fs.push_back(tr("final")); if (f & PropertyModel::Resetable) fs.push_back(tr("resetable")); if (f & PropertyModel::Scriptable) fs.push_back(tr("scriptable")); if (f & PropertyModel::Stored) fs.push_back(tr("stored")); if (f & PropertyModel::User) fs.push_back(tr("user")); if (f & PropertyModel::Writable) fs.push_back(tr("writable")); tt.push_back(tr("Attributes: %1").arg(fs.join(QLatin1String(", ")))); } if (!revision.isNull()) { tt.push_back(tr("Revision: %1").arg(revision.toInt())); } if (!notifySignal.isEmpty()) { tt.push_back(tr("Notify signal: %1").arg(notifySignal)); } return tt.join(QLatin1String("\n")); } return QIdentityProxyModel::data(index, role); } QVariant ClientPropertyModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { switch (section) { case 0: return tr("Property"); case 1: return tr("Value"); case 2: return tr("Type"); case 3: return tr("Class"); } } return QAbstractItemModel::headerData(section, orientation, role); } gammaray-2.11.3/ui/clientpropertymodel.h000066400000000000000000000033651413310443000202660ustar00rootroot00000000000000/* clientpropertymodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2018-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_CLIENTPROPERTYMODEL_H #define GAMMARAY_CLIENTPROPERTYMODEL_H #include "gammaray_ui_export.h" #include namespace GammaRay { /*! Client-side proxy model for the AggregatedPropertyModel. */ class GAMMARAY_UI_EXPORT ClientPropertyModel : public QIdentityProxyModel { Q_OBJECT public: explicit ClientPropertyModel(QObject *parent = nullptr); ~ClientPropertyModel() override; QVariant data(const QModelIndex &index, int role) const override; QVariant headerData(int section, Qt::Orientation orientation, int role) const override; }; } #endif // GAMMARAY_CLIENTPROPERTYMODEL_H gammaray-2.11.3/ui/clienttoolfilterproxymodel.cpp000066400000000000000000000041131413310443000222120ustar00rootroot00000000000000/* clienttoolfilterproxymodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "clienttoolfilterproxymodel.h" #include using namespace GammaRay; ClientToolFilterProxyModel::ClientToolFilterProxyModel(QObject* parent) : QSortFilterProxyModel(parent), m_filterInactiveTools(false) { } ClientToolFilterProxyModel::~ClientToolFilterProxyModel() = default; bool ClientToolFilterProxyModel::filterInactiveTools() const { return m_filterInactiveTools; } void ClientToolFilterProxyModel::setFilterInactiveTools(bool enable) { m_filterInactiveTools = enable; invalidate(); } bool ClientToolFilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const { if (m_filterInactiveTools) { const auto toolEnabled = sourceModel()->data(sourceModel()->index(source_row, 0, source_parent), ToolModelRole::ToolEnabled).toBool(); if (!toolEnabled) return false; } return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent); } gammaray-2.11.3/ui/clienttoolfilterproxymodel.h000066400000000000000000000034111413310443000216570ustar00rootroot00000000000000/* clienttoolfilterproxymodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_CLIENTTOOLFILTERPROXYMODEL_H #define GAMMARAY_CLIENTTOOLFILTERPROXYMODEL_H #include namespace GammaRay { class ClientToolFilterProxyModel : public QSortFilterProxyModel { Q_OBJECT public: explicit ClientToolFilterProxyModel(QObject *parent = nullptr); ~ClientToolFilterProxyModel() override; bool filterInactiveTools() const; public slots: void setFilterInactiveTools(bool enable); protected: bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override; private: bool m_filterInactiveTools; }; } #endif // GAMMARAY_CLIENTTOOLFILTERPROXYMODEL_H gammaray-2.11.3/ui/clienttoolmanager.cpp000066400000000000000000000253011413310443000202160ustar00rootroot00000000000000/* clienttoolmanager.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "clienttoolmanager.h" #include "clienttoolmodel.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace GammaRay; #define MAKE_FACTORY(type, label) \ class type##Factory : public ToolUiFactory { \ public: \ virtual QString id() const override { return "GammaRay::" #type; } \ virtual QString name() const override { return label; } \ virtual QWidget *createWidget(QWidget *parentWidget) override { \ return new type##Widget(parentWidget); \ } \ } MAKE_FACTORY(MessageHandler, qApp->translate("GammaRay::MessageHandlerFactory", "Messages")); MAKE_FACTORY(MetaObjectBrowser, qApp->translate("GammaRay::MetaObjectBrowserFactory", "Meta Objects")); MAKE_FACTORY(MetaTypeBrowser, qApp->translate("GammaRay::MetaTypeBrowserFactory", "Meta Types")); MAKE_FACTORY(ProblemReporter, qApp->translate("GammaRay::ProblemReporterFactory", "Problems")); MAKE_FACTORY(ResourceBrowser, qApp->translate("GammaRay::ResourceBrowserFactory", "Resources")); struct PluginRepository { PluginRepository() = default; Q_DISABLE_COPY(PluginRepository) ~PluginRepository() { qDeleteAll(factories); } // ToolId -> ToolUiFactory QHash factories; // so far unused tools that yet have to be loaded/initialized QSet uninitializedTools; }; Q_GLOBAL_STATIC(PluginRepository, s_pluginRepository) static void insertFactory(ToolUiFactory *factory) { s_pluginRepository()->factories.insert(factory->id(), factory); s_pluginRepository()->uninitializedTools.insert(factory); } static void initPluginRepository() { if (!s_pluginRepository()->factories.isEmpty()) return; insertFactory(new MessageHandlerFactory); insertFactory(new MetaObjectBrowserFactory); insertFactory(new MetaTypeBrowserFactory); insertFactory(new ObjectInspectorFactory); insertFactory(new ProblemReporterFactory); insertFactory(new ResourceBrowserFactory); PluginManager pm; foreach (ToolUiFactory *factory, pm.plugins()) insertFactory(factory); } static bool toolLessThan(const ToolInfo &lhs, const ToolInfo &rhs) { return lhs.name().localeAwareCompare(rhs.name()) < 0; } ToolInfo::ToolInfo(const ToolData &toolData, ToolUiFactory *factory) : m_toolId(toolData.id), m_isEnabled(toolData.enabled), m_hasUi(toolData.hasUi), m_factory(factory) { } ToolInfo::~ToolInfo() = default; QString ToolInfo::id() const { return m_toolId; } bool ToolInfo::isEnabled() const { return m_isEnabled; } void ToolInfo::setEnabled(bool enabled) { m_isEnabled = enabled; } bool ToolInfo::hasUi() const { return m_hasUi; } QString ToolInfo::name() const { if (!m_factory) return m_toolId; return m_factory->name(); } bool ToolInfo::remotingSupported() const { return m_factory && m_factory->remotingSupported(); } bool ToolInfo::isValid() const { return !m_toolId.isEmpty(); } ClientToolManager* ClientToolManager::s_instance = nullptr; ClientToolManager::ClientToolManager(QObject *parent) : QObject(parent) , m_parentWidget(nullptr) , m_model(nullptr) , m_selectionModel(nullptr) { Q_ASSERT(!s_instance); s_instance = this; initPluginRepository(); connect(Endpoint::instance(), &Endpoint::disconnected, this, &ClientToolManager::clear); connect(Endpoint::instance(), &Endpoint::connectionEstablished, this, &ClientToolManager::requestAvailableTools); } ClientToolManager::~ClientToolManager() { for (auto it = m_widgets.constBegin(); it != m_widgets.constEnd(); ++it) delete it.value().data(); s_instance = nullptr; } void ClientToolManager::clear() { emit aboutToReset(); for (auto it = m_widgets.constBegin(); it != m_widgets.constEnd(); ++it) delete it.value().data(); m_tools.clear(); if (m_remote) disconnect(m_remote, nullptr, this, nullptr); m_remote = nullptr; emit reset(); } void ClientToolManager::requestAvailableTools() { m_remote = ObjectBroker::object(); connect(m_remote.data(), &ToolManagerInterface::availableToolsResponse, this, &ClientToolManager::gotTools); connect(m_remote.data(), &ToolManagerInterface::toolEnabled, this, &ClientToolManager::toolGotEnabled); connect(m_remote.data(), &ToolManagerInterface::toolSelected, this, &ClientToolManager::toolGotSelected); connect(m_remote.data(), &ToolManagerInterface::toolsForObjectResponse, this, &ClientToolManager::toolsForObjectReceived); m_remote->requestAvailableTools(); } void ClientToolManager::setToolParentWidget(QWidget *parent) { m_parentWidget = parent; } QWidget *ClientToolManager::widgetForId(const QString &toolId) const { return widgetForIndex(toolIndexForToolId(toolId)); } QWidget *ClientToolManager::widgetForIndex(int index) const { if (index < 0 || index >= m_tools.size()) return nullptr; const ToolInfo &tool = m_tools.at(index); if (!tool.isEnabled()) return nullptr; auto it = m_widgets.constFind(tool.id()); if (it != m_widgets.constEnd() && it.value()) return it.value(); ToolUiFactory *factory = s_pluginRepository()->factories.value(tool.id()); if (!factory) return nullptr; if (s_pluginRepository()->uninitializedTools.contains(factory)) { factory->initUi(); s_pluginRepository()->uninitializedTools.remove(factory); } QWidget *widget = factory->createWidget(m_parentWidget); m_widgets.insert(tool.id(), widget); return widget; } void ClientToolManager::gotTools(const QVector &tools) { emit aboutToReceiveData(); for (const auto &tool : tools) { ToolUiFactory *factory = s_pluginRepository()->factories.value(tool.id); // hide tools we have no UI plugin for if (tool.hasUi && factory) { m_tools.append(ToolInfo(tool, factory)); } if (tool.enabled) { if (factory && (factory->remotingSupported() || !Endpoint::instance()->isRemoteClient()) && s_pluginRepository()->uninitializedTools.contains(factory)) { factory->initUi(); s_pluginRepository()->uninitializedTools.remove(factory); } } } std::sort(m_tools.begin(), m_tools.end(), toolLessThan); emit toolListAvailable(); if (m_remote) { disconnect(m_remote.data(), &ToolManagerInterface::availableToolsResponse, this, &ClientToolManager::gotTools); } } bool ClientToolManager::isToolListLoaded() const { return m_tools.count(); } QAbstractItemModel *ClientToolManager::model() { if (!m_model) m_model = new ClientToolModel(this); return m_model; } QItemSelectionModel *ClientToolManager::selectionModel() { if (!m_selectionModel) m_selectionModel = new ClientToolSelectionModel(this); return m_selectionModel; } void ClientToolManager::requestToolsForObject(const ObjectId &id) { if (!m_remote) { return; } m_remote->requestToolsForObject(id); } void ClientToolManager::selectObject(const ObjectId &id, const ToolInfo &toolInfo) { if (!m_remote) { return; } m_remote->selectObject(id, toolInfo.id()); } void ClientToolManager::toolsForObjectReceived(const ObjectId &id, const QVector &toolIds) { QVector t; t.reserve(toolIds.size()); for (const auto &toolId : toolIds) { const auto i = toolIndexForToolId(toolId); if (i >= 0) t.push_back(m_tools.at(i)); } emit toolsForObjectResponse(id, t); } ClientToolManager *ClientToolManager::instance() { return s_instance; } void ClientToolManager::toolGotEnabled(const QString &toolId) { int i = 0; auto it = m_tools.begin(); for (; it != m_tools.end(); i++, it++) { if (it->id() == toolId) { it->setEnabled(true); ToolUiFactory *factory = s_pluginRepository()->factories.value(it->id()); if (factory && (factory->remotingSupported() || !Endpoint::instance()->isRemoteClient()) && s_pluginRepository()->uninitializedTools.contains(factory)) { factory->initUi(); s_pluginRepository()->uninitializedTools.remove(factory); } emit toolEnabled(toolId); emit toolEnabledByIndex(i); } } } void ClientToolManager::toolGotSelected(const QString &toolId) { emit toolSelected(toolId); emit toolSelectedByIndex(toolIndexForToolId(toolId)); } int ClientToolManager::toolIndexForToolId(const QString &toolId) const { int i = 0; for (auto it = m_tools.constBegin(); it != m_tools.constEnd(); ++i, ++it) { if (it->id() == toolId) return i; } return -1; } ToolInfo ClientToolManager::toolForToolId(const QString &toolId) const { const int index = toolIndexForToolId(toolId); if (index < 0 || index >= m_tools.size()) return ToolInfo(); return m_tools.at(index); } gammaray-2.11.3/ui/clienttoolmanager.h000066400000000000000000000106211413310443000176620ustar00rootroot00000000000000/* clienttoolmanager.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_CLIENTTOOLMANAGER_H #define GAMMARAY_CLIENTTOOLMANAGER_H #include "gammaray_ui_export.h" #include #include #include QT_BEGIN_NAMESPACE class QAbstractItemModel; class QItemSelectionModel; class QWidget; QT_END_NAMESPACE namespace GammaRay { class ObjectId; class ProbeControllerInterface; struct ToolData; class ToolManagerInterface; class ToolUiFactory; /*! Data structure describing a single GammaRay tool. */ class GAMMARAY_UI_EXPORT ToolInfo { public: ToolInfo() = default; ///@cond internal explicit ToolInfo(const ToolData &toolData, ToolUiFactory *factory); ///@endcond ~ToolInfo(); QString id() const; bool isEnabled() const; void setEnabled(bool enabled); bool hasUi() const; QString name() const; bool remotingSupported() const; bool isValid() const; private: QString m_toolId; bool m_isEnabled = false; bool m_hasUi = false; ToolUiFactory *m_factory = nullptr; }; /*! Tool Management API for the client to find out, which tools exist, * get the tool's widgets, etc. * * This is needed when implementing your own client UI embedded into a different application. * If you want to provide a list with all supported tools, you can use * \e ClientToolManager::model() to get all the tool information as a model. * * This class also takes care of loading client-sided tool plugins. */ class GAMMARAY_UI_EXPORT ClientToolManager : public QObject { Q_OBJECT public: explicit ClientToolManager(QObject *parent = nullptr); ~ClientToolManager() override; void setToolParentWidget(QWidget *parent); bool isToolListLoaded() const; QWidget *widgetForId(const QString &toolId) const; QWidget *widgetForIndex(int index) const; int toolIndexForToolId(const QString &toolId) const; ToolInfo toolForToolId(const QString &toolId) const; inline QVector tools() const { return m_tools; } QAbstractItemModel *model(); QItemSelectionModel *selectionModel(); void requestToolsForObject(const ObjectId &id); void selectObject(const ObjectId &id, const ToolInfo &toolInfo); static ClientToolManager* instance(); public slots: void requestAvailableTools(); void clear(); signals: void toolEnabled(const QString &toolId); void toolEnabledByIndex(int toolIndex); void aboutToReceiveData(); void toolListAvailable(); void toolSelected(const QString &toolId); void toolSelectedByIndex(int index); void toolsForObjectResponse(const GammaRay::ObjectId &id, const QVector &toolInfos); void aboutToReset(); void reset(); private slots: void gotTools(const QVector &tools); void toolGotEnabled(const QString &toolId); void toolGotSelected(const QString &toolId); void toolsForObjectReceived(const GammaRay::ObjectId &id, const QVector &toolIds); private: typedef QHash > WidgetsHash; mutable WidgetsHash m_widgets; // ToolId -> Widget QVector m_tools; QPointer m_remote; QWidget *m_parentWidget; QAbstractItemModel *m_model; QItemSelectionModel *m_selectionModel; static ClientToolManager *s_instance; }; } #endif // GAMMARAY_CLIENTTOOLMANAGER_H gammaray-2.11.3/ui/clienttoolmodel.cpp000066400000000000000000000114461413310443000177110ustar00rootroot00000000000000/* clienttoolmodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "clienttoolmodel.h" #include "clienttoolmanager.h" #include #include #include using namespace GammaRay; ClientToolModel::ClientToolModel(ClientToolManager *manager) : QAbstractListModel(manager) , m_toolManager(manager) { connect(m_toolManager, &ClientToolManager::aboutToReceiveData, this, &ClientToolModel::startReset); connect(m_toolManager, &ClientToolManager::toolListAvailable, this, &ClientToolModel::finishReset); connect(m_toolManager, &ClientToolManager::aboutToReset, this, &ClientToolModel::startReset); connect(m_toolManager, &ClientToolManager::reset, this, &ClientToolModel::finishReset); connect(m_toolManager, &ClientToolManager::toolEnabledByIndex, this, &ClientToolModel::toolEnabled); } ClientToolModel::~ClientToolModel() = default; QVariant ClientToolModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); const ToolInfo &tool = m_toolManager->tools().at(index.row()); switch (role) { case Qt::DisplayRole: return tool.name(); case ToolModelRole::ToolId: return tool.id(); case ToolModelRole::ToolWidget: return QVariant::fromValue(m_toolManager->widgetForIndex(index.row())); case Qt::ToolTipRole: if (!tool.remotingSupported() && Endpoint::instance()->isRemoteClient()) return tr("This tool does not work in out-of-process mode."); return QVariant(); case ToolModelRole::ToolEnabled: return tool.isEnabled(); case ToolModelRole::ToolHasUi: return tool.hasUi(); case ToolModelRole::ToolFeedbackId: { auto id = tool.id().toLower(); if (id.startsWith(QLatin1String("gammaray_"))) id = id.mid(9); else if (id.startsWith(QLatin1String("gammaray::"))) id = id.mid(10); return id; } } return QVariant(); } void ClientToolModel::toolEnabled(int toolIndex) { QModelIndex i = index(toolIndex, 0); emit dataChanged(i, i, QVector() << ToolModelRole::ToolEnabled); } void ClientToolModel::startReset() { beginResetModel(); } void ClientToolModel::finishReset() { endResetModel(); } Qt::ItemFlags ClientToolModel::flags(const QModelIndex &index) const { Qt::ItemFlags flags = QAbstractListModel::flags(index); if (!index.isValid()) return flags; const auto &tool = m_toolManager->tools().at(index.row()); if (!tool.isEnabled() || (!tool.remotingSupported() && Endpoint::instance()->isRemoteClient())) flags &= ~(Qt::ItemIsEnabled | Qt::ItemIsSelectable); return flags; } int ClientToolModel::rowCount(const QModelIndex &parent) const { if (parent.isValid()) return 0; return m_toolManager->tools().count(); } ClientToolSelectionModel::ClientToolSelectionModel(ClientToolManager *manager) : QItemSelectionModel(manager->model()) , m_toolManager(manager) { connect(manager, &ClientToolManager::toolSelectedByIndex, this, &ClientToolSelectionModel::selectTool); connect(manager, &ClientToolManager::toolListAvailable, this, &ClientToolSelectionModel::selectDefaultTool); } ClientToolSelectionModel::~ClientToolSelectionModel() = default; void ClientToolSelectionModel::selectTool(int index) { select(model()->index(index, 0), QItemSelectionModel::Select | QItemSelectionModel::Clear | QItemSelectionModel::Rows | QItemSelectionModel::Current); } void ClientToolSelectionModel::selectDefaultTool() { selectTool(m_toolManager->toolIndexForToolId(QStringLiteral("GammaRay::ObjectInspector"))); } gammaray-2.11.3/ui/clienttoolmodel.h000066400000000000000000000044521413310443000173550ustar00rootroot00000000000000/* clienttoolmodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_CLIENTTOOLMODEL_H #define GAMMARAY_CLIENTTOOLMODEL_H #include #include namespace GammaRay { class ClientToolManager; /*! Model of all selectable client tools. */ class ClientToolModel : public QAbstractListModel { Q_OBJECT public: explicit ClientToolModel(ClientToolManager *manager); ~ClientToolModel() override; QVariant data(const QModelIndex &index, int role) const override; Qt::ItemFlags flags(const QModelIndex &index) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; private slots: void startReset(); void finishReset(); void toolEnabled(int toolIndex); private: ClientToolManager *m_toolManager; }; /*! Selection model that automatically syncs ClientToolModel with ClientToolManager. */ class ClientToolSelectionModel : public QItemSelectionModel { Q_OBJECT public: explicit ClientToolSelectionModel(ClientToolManager *manager); ~ClientToolSelectionModel() override; private slots: void selectTool(int index); void selectDefaultTool(); private: ClientToolManager *m_toolManager; }; } #endif // GAMMARAY_CLIENTTOOLMODEL_H gammaray-2.11.3/ui/codeeditor/000077500000000000000000000000001413310443000161235ustar00rootroot00000000000000gammaray-2.11.3/ui/codeeditor/codeeditor.cpp000066400000000000000000000251571413310443000207620ustar00rootroot00000000000000/* codeeditor.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "codeeditor.h" #include "codeeditorsidebar.h" #ifdef HAVE_SYNTAX_HIGHLIGHTING #include #include #include #include #endif #include #include #include #include #include #include #include using namespace GammaRay; #ifdef HAVE_SYNTAX_HIGHLIGHTING KSyntaxHighlighting::Repository *CodeEditor::s_repository = nullptr; #endif CodeEditor::CodeEditor(QWidget *parent) : QPlainTextEdit(parent), m_sideBar(new CodeEditorSidebar(this)), m_highlighter(nullptr) { setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)); connect(this, &QPlainTextEdit::blockCountChanged, this, &CodeEditor::updateSidebarGeometry); connect(this, &QPlainTextEdit::updateRequest, this, &CodeEditor::updateSidebarArea); connect(this, &QPlainTextEdit::cursorPositionChanged, this, &CodeEditor::highlightCurrentLine); updateSidebarGeometry(); highlightCurrentLine(); } CodeEditor::~CodeEditor() = default; void CodeEditor::setFileName(const QString &fileName) { #ifdef HAVE_SYNTAX_HIGHLIGHTING ensureHighlighterExists(); const auto def = s_repository->definitionForFileName(fileName); m_highlighter->setDefinition(def); #else Q_UNUSED(fileName); #endif } void CodeEditor::setSyntaxDefinition(const QString &syntaxName) { #ifdef HAVE_SYNTAX_HIGHLIGHTING ensureHighlighterExists(); const auto def = s_repository->definitionForName(syntaxName); m_highlighter->setDefinition(def); #else Q_UNUSED(syntaxName); #endif } int CodeEditor::sidebarWidth() const { int digits = 1; auto count = blockCount(); while (count >= 10) { ++digits; count /= 10; } return 4 + fontMetrics().width(QLatin1Char('9')) * digits + foldingBarWidth(); } int CodeEditor::foldingBarWidth() const { #ifdef HAVE_SYNTAX_HIGHLIGHTING return fontMetrics().lineSpacing(); #else return 0; #endif } void CodeEditor::contextMenuEvent(QContextMenuEvent *event) { auto menu = createStandardContextMenu(event->pos()); #ifdef HAVE_SYNTAX_HIGHLIGHTING ensureHighlighterExists(); menu->addSeparator(); auto hlActionGroup = new QActionGroup(menu); hlActionGroup->setExclusive(true); auto hlGroupMenu = menu->addMenu(tr("Syntax Highlighting")); auto noHlAction = hlGroupMenu->addAction(QStringLiteral("None")); noHlAction->setCheckable(true); hlActionGroup->addAction(noHlAction); noHlAction->setChecked(!m_highlighter->definition().isValid()); QMenu *hlSubMenu = nullptr; QString currentGroup; foreach (const auto &def, s_repository->definitions()) { if (def.isHidden() || def.section().isEmpty()) continue; if (currentGroup != def.section()) { currentGroup = def.section(); hlSubMenu = hlGroupMenu->addMenu(def.translatedSection()); } Q_ASSERT(hlSubMenu); if (hlSubMenu) { auto action = hlSubMenu->addAction(def.translatedName()); action->setCheckable(true); action->setData(def.name()); hlActionGroup->addAction(action); if (def.name() == m_highlighter->definition().name()) { action->setChecked(true); } } } connect(hlActionGroup, &QActionGroup::triggered, this, &CodeEditor::syntaxSelected); #endif menu->exec(event->globalPos()); delete menu; } void CodeEditor::resizeEvent(QResizeEvent *event) { QPlainTextEdit::resizeEvent(event); updateSidebarGeometry(); } void CodeEditor::updateSidebarGeometry() { setViewportMargins(sidebarWidth(), 0, 0, 0); const auto r = contentsRect(); m_sideBar->setGeometry(QRect(r.left(), r.top(), sidebarWidth(), r.height())); } void CodeEditor::updateSidebarArea(const QRect &rect, int dy) { if (dy) { m_sideBar->scroll(0, dy); } else { m_sideBar->update(0, rect.y(), m_sideBar->width(), rect.height()); } } void CodeEditor::sidebarPaintEvent(QPaintEvent *event) { QPainter painter(m_sideBar); painter.fillRect(event->rect(), palette().color(QPalette::Window)); auto block = firstVisibleBlock(); auto blockNumber = block.blockNumber(); int top = blockBoundingGeometry(block).translated(contentOffset()).top(); int bottom = top + blockBoundingRect(block).height(); const auto foldingMarkerSize = foldingBarWidth(); while (block.isValid() && top <= event->rect().bottom()) { if (block.isVisible() && bottom >= event->rect().top()) { const auto number = QString::number(blockNumber + 1); painter.setPen(palette().color(QPalette::Text)); painter.drawText(0, top, m_sideBar->width() - 2 - foldingMarkerSize, fontMetrics().height(), Qt::AlignRight, number); } // folding marker #ifdef HAVE_SYNTAX_HIGHLIGHTING if (block.isVisible() && isFoldable(block)) { QPolygonF polygon; if (isFolded(block)) { polygon << QPointF(foldingMarkerSize * 0.4, foldingMarkerSize * 0.25); polygon << QPointF(foldingMarkerSize * 0.4, foldingMarkerSize * 0.75); polygon << QPointF(foldingMarkerSize * 0.8, foldingMarkerSize * 0.5); } else { polygon << QPointF(foldingMarkerSize * 0.25, foldingMarkerSize * 0.4); polygon << QPointF(foldingMarkerSize * 0.75, foldingMarkerSize * 0.4); polygon << QPointF(foldingMarkerSize * 0.5, foldingMarkerSize * 0.8); } painter.save(); painter.setRenderHint(QPainter::Antialiasing); painter.setPen(Qt::NoPen); painter.setBrush(palette().color(QPalette::Highlight)); painter.translate(m_sideBar->width() - foldingMarkerSize, top); painter.drawPolygon(polygon); painter.restore(); } #endif block = block.next(); top = bottom; bottom = top + blockBoundingRect(block).height(); ++blockNumber; } } void CodeEditor::highlightCurrentLine() { auto lineColor = palette().color(QPalette::Highlight); lineColor.setAlpha(32); QTextEdit::ExtraSelection selection; selection.format.setBackground(lineColor); selection.format.setProperty(QTextFormat::FullWidthSelection, true); selection.cursor = textCursor(); selection.cursor.clearSelection(); QList extraSelections; extraSelections.append(selection); setExtraSelections(extraSelections); } void CodeEditor::syntaxSelected(QAction *action) { #ifdef HAVE_SYNTAX_HIGHLIGHTING Q_ASSERT(action); Q_ASSERT(s_repository); const auto defName = action->data().toString(); const auto def = s_repository->definitionForName(defName); m_highlighter->setDefinition(def); #else Q_UNUSED(action); #endif } void CodeEditor::ensureHighlighterExists() { #ifdef HAVE_SYNTAX_HIGHLIGHTING if (!s_repository) { s_repository = new KSyntaxHighlighting::Repository; qAddPostRoutine([](){ delete s_repository; }); } if (!m_highlighter) { m_highlighter = new KSyntaxHighlighting::SyntaxHighlighter(document()); m_highlighter->setTheme(palette().color(QPalette::Base).lightness() < 128 ? s_repository->defaultTheme(KSyntaxHighlighting::Repository::DarkTheme) : s_repository->defaultTheme(KSyntaxHighlighting::Repository::LightTheme)); } #endif } QTextBlock CodeEditor::blockAtPosition(int y) const { auto block = firstVisibleBlock(); if (!block.isValid()) return QTextBlock(); int top = blockBoundingGeometry(block).translated(contentOffset()).top(); int bottom = top + blockBoundingRect(block).height(); do { if (top <= y && y <= bottom) { return block; } block = block.next(); top = bottom; bottom = top + blockBoundingRect(block).height(); } while (block.isValid()); return QTextBlock(); } bool CodeEditor::isFoldable(const QTextBlock &block) const { #ifdef HAVE_SYNTAX_HIGHLIGHTING return m_highlighter->startsFoldingRegion(block); #else Q_UNUSED(block); return false; #endif } bool CodeEditor::isFolded(const QTextBlock &block) const { if (!block.isValid()) return false; const auto nextBlock = block.next(); if (!nextBlock.isValid()) return false; return !nextBlock.isVisible(); } void CodeEditor::toggleFold(const QTextBlock &startBlock) { #ifdef HAVE_SYNTAX_HIGHLIGHTING // we also want to fold the last line of the region, therefore the ".next()" const auto endBlock = m_highlighter->findFoldingRegionEnd(startBlock).next(); if (isFolded(startBlock)) { // unfold auto block = startBlock.next(); while (block.isValid() && !block.isVisible()) { block.setVisible(true); block.setLineCount(block.layout()->lineCount()); block = block.next(); } } else { // fold auto block = startBlock.next(); while (block.isValid() && block != endBlock) { block.setVisible(false); block.setLineCount(0); block = block.next(); } } // redraw document document()->markContentsDirty(startBlock.position(), endBlock.position() - startBlock.position() + 1); // update scrollbars emit document()->documentLayout()->documentSizeChanged(document()->documentLayout()->documentSize()); #else Q_UNUSED(startBlock); #endif } gammaray-2.11.3/ui/codeeditor/codeeditor.h000066400000000000000000000050121413310443000204130ustar00rootroot00000000000000/* codeeditor.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_CODEEDITOR_H #define GAMMARAY_CODEEDITOR_H #include "gammaray_ui_export.h" #include #include namespace KSyntaxHighlighting { class Repository; class SyntaxHighlighter; } namespace GammaRay { class CodeEditorSidebar; class GAMMARAY_UI_EXPORT CodeEditor : public QPlainTextEdit { Q_OBJECT public: explicit CodeEditor(QWidget *parent = nullptr); ~CodeEditor() override; void setFileName(const QString &fileName); void setSyntaxDefinition(const QString &syntaxName); protected: void contextMenuEvent(QContextMenuEvent *event) override; void resizeEvent(QResizeEvent *event) override; private slots: void updateSidebarGeometry(); void updateSidebarArea(const QRect &rect, int dy); void highlightCurrentLine(); void syntaxSelected(QAction *action); private: friend class CodeEditorSidebar; int sidebarWidth() const; int foldingBarWidth() const; void sidebarPaintEvent(QPaintEvent *event); void ensureHighlighterExists(); QTextBlock blockAtPosition(int y) const; bool isFoldable(const QTextBlock &block) const; bool isFolded(const QTextBlock &block) const; void toggleFold(const QTextBlock &startBlock); static KSyntaxHighlighting::Repository *s_repository; CodeEditorSidebar *m_sideBar; KSyntaxHighlighting::SyntaxHighlighter *m_highlighter; }; } #endif // GAMMARAY_CODEEDITOR_H gammaray-2.11.3/ui/codeeditor/codeeditorsidebar.cpp000066400000000000000000000037511413310443000223100ustar00rootroot00000000000000/* codeeditorsidebar.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "codeeditorsidebar.h" #include "codeeditor.h" #include #include #include using namespace GammaRay; CodeEditorSidebar::CodeEditorSidebar(CodeEditor *editor) : QWidget(editor), m_codeEditor(editor) { } CodeEditorSidebar::~CodeEditorSidebar() = default; QSize CodeEditorSidebar::sizeHint() const { return {m_codeEditor->sidebarWidth(), 0}; } void CodeEditorSidebar::paintEvent(QPaintEvent *event) { m_codeEditor->sidebarPaintEvent(event); } void CodeEditorSidebar::mouseReleaseEvent(QMouseEvent *event) { if (event->x() >= width() - m_codeEditor->foldingBarWidth()) { auto block = m_codeEditor->blockAtPosition(event->y()); if (!block.isValid() || !m_codeEditor->isFoldable(block)) return; m_codeEditor->toggleFold(block); } QWidget::mouseReleaseEvent(event); } gammaray-2.11.3/ui/codeeditor/codeeditorsidebar.h000066400000000000000000000032101413310443000217430ustar00rootroot00000000000000/* codeeditorsidebar.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_CODEEDITORSIDEBAR_H #define GAMMARAY_CODEEDITORSIDEBAR_H #include namespace GammaRay { class CodeEditor; class CodeEditorSidebar : public QWidget { Q_OBJECT public: explicit CodeEditorSidebar(CodeEditor *editor); ~CodeEditorSidebar() override; QSize sizeHint() const override; protected: void paintEvent(QPaintEvent *event) override; void mouseReleaseEvent(QMouseEvent *event) override; private: CodeEditor *m_codeEditor; }; } #endif // GAMMARAY_CODEEDITORSIDEBAR_H gammaray-2.11.3/ui/contextmenuextension.cpp000066400000000000000000000120551413310443000210170ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Kevin Funk Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "contextmenuextension.h" #include "clienttoolmanager.h" #include "uiintegration.h" #include #include #include #include #include #include using namespace GammaRay; namespace { QString sourceLocationLabel(ContextMenuExtension::Location location, const SourceLocation &sourceLocation) { switch (location) { case ContextMenuExtension::GoTo: return qApp->translate("GammaRay::ContextMenuExtension", "Go to: %1").arg(sourceLocation.displayString()); case ContextMenuExtension::ShowSource: return qApp->translate("GammaRay::ContextMenuExtension", "Show source: %1").arg(sourceLocation.displayString()); case ContextMenuExtension::Creation: return qApp->translate("GammaRay::ContextMenuExtension", "Go to creation: %1").arg(sourceLocation.displayString()); case ContextMenuExtension::Declaration: return qApp->translate("GammaRay::ContextMenuExtension", "Go to declaration: %1").arg(sourceLocation.displayString()); } Q_ASSERT(false); return QString(); } } ContextMenuExtension::ContextMenuExtension(const ObjectId &id) : m_id(id) { } void ContextMenuExtension::setLocation(ContextMenuExtension::Location location, const SourceLocation &sourceLocation) { m_locations.push_back(QPair(location, sourceLocation)); } bool ContextMenuExtension::discoverSourceLocation(ContextMenuExtension::Location location, const QUrl &url) { if (!UiIntegration::instance()) return false; if (url.isEmpty()) return false; setLocation(location, SourceLocation(url)); return true; } bool ContextMenuExtension::discoverPropertySourceLocation(ContextMenuExtension::Location location, const QModelIndex &index) { if (!UiIntegration::instance()) return false; if (!index.isValid()) return false; const bool isUrl = index.sibling(index.row(), PropertyModel::TypeColumn).data().toString() == QStringLiteral( "QUrl"); if (!isUrl) return false; return discoverSourceLocation(location, index.sibling( index.row(), PropertyModel::ValueColumn).data().toUrl()); } void ContextMenuExtension::populateMenu(QMenu *menu) { if (UiIntegration::instance()) { for (auto it = m_locations.constBegin(), end = m_locations.constEnd(); it != end; ++it) { if (it->second.isValid()) { auto action = menu->addAction(sourceLocationLabel(it->first, it->second)); QObject::connect(action, &QAction::triggered, UiIntegration::instance(), [it]() { UiIntegration::requestNavigateToCode(it->second.url(), it->second.line(), it->second.column()); }); } } } if (m_id.isNull()) return; Q_ASSERT(ClientToolManager::instance()); ClientToolManager::instance()->requestToolsForObject(m_id); // delay adding actions until we know the supported tools QObject::connect(ClientToolManager::instance(), &ClientToolManager::toolsForObjectResponse, menu, [menu](const ObjectId &id, const QVector &toolInfos) { for (const auto &toolInfo : toolInfos) { auto action = menu->addAction(qApp->translate("GammaRay::ContextMenuExtension", "Show in \"%1\" tool").arg(toolInfo.name())); QObject::connect(action, &QAction::triggered, [id, toolInfo]() { ClientToolManager::instance()->selectObject(id, toolInfo); }); } }); } gammaray-2.11.3/ui/contextmenuextension.h000066400000000000000000000044501413310443000204640ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Kevin Funk Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_CONTEXTMENUEXTENSION_H #define GAMMARAY_CONTEXTMENUEXTENSION_H #include "gammaray_ui_export.h" #include #include #include #include QT_BEGIN_NAMESPACE class QMenu; class QModelIndex; QT_END_NAMESPACE namespace GammaRay { class GAMMARAY_UI_EXPORT ContextMenuExtension { public: // UI presentation depend the order of this enum enum Location { GoTo, ShowSource, Creation, Declaration }; explicit ContextMenuExtension(const ObjectId &id = ObjectId()); void setLocation(Location location, const SourceLocation &sourceLocation); bool discoverSourceLocation(Location location, const QUrl &url); // Given a model index from a PropertyModel, try to found a valid url and call // setLocation() with the given location. bool discoverPropertySourceLocation(Location location, const QModelIndex &index); /// Populate @p menu with entries related to the captured object id. Only supported on Qt5 void populateMenu(QMenu *menu); private: ObjectId m_id; QVector> m_locations; }; } #endif // GAMMARAY_CONTEXTMENUEXTENSION_H gammaray-2.11.3/ui/deferredtreeview.cpp000066400000000000000000000151751413310443000200520ustar00rootroot00000000000000/* * This file is part of GammaRay, the Qt application inspection and * manipulation tool. * * Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com * Author: Filipe Azevedo * * Licensees holding valid commercial KDAB GammaRay licenses may use this file in * accordance with GammaRay Commercial License Agreement provided with the Software. * * Contact info@kdab.com if any conditions of this licensing are not clear to you. * * 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, see . * */ #include #include "deferredtreeview.h" #include "deferredtreeview_p.h" #include #include using namespace GammaRay; namespace { QHeaderView::ResizeMode sectionResizeMode(QHeaderView *header, int logicalIndex) { return header->sectionResizeMode(logicalIndex); } void setSectionResizeMode(QHeaderView *header, int logicalIndex, QHeaderView::ResizeMode mode) { header->setSectionResizeMode(logicalIndex, mode); } } HeaderView::HeaderView(Qt::Orientation orientation, QWidget *parent) : QHeaderView(orientation, parent) { } bool HeaderView::isState(State state) const { QHeaderViewPrivate *d = reinterpret_cast(d_ptr.data()); return d->state == QHeaderViewPrivate::State(state); } DeferredTreeView::DeferredTreeView(QWidget *parent) : QTreeView(parent) , m_expandNewContent(false) , m_allExpanded(false) , m_timer(new QTimer(this)) { m_timer->setSingleShot(true); m_timer->setInterval(125); setHeader(new HeaderView(header()->orientation(), this)); // Default QTreeView header properties header()->setSectionsMovable(true); header()->setStretchLastSection(true); header()->setDefaultAlignment(Qt::AlignLeft|Qt::AlignVCenter); // Custom header()->setSortIndicatorShown(true); setIndentation(10); setSortingEnabled(true); connect(header(), &QHeaderView::sectionCountChanged, this, &DeferredTreeView::sectionCountChanged); connect(m_timer, &QTimer::timeout, this, &DeferredTreeView::timeout); } void DeferredTreeView::setModel(QAbstractItemModel *model) { QTreeView::setModel(model); if (model) triggerExpansion(QModelIndex()); } QHeaderView::ResizeMode DeferredTreeView::deferredResizeMode(int logicalIndex) const { const auto it = m_sectionsProperties.constFind(logicalIndex); const int resizeMode = it != m_sectionsProperties.constEnd() ? (*it).resizeMode : -1; return resizeMode != -1 ? QHeaderView::ResizeMode(resizeMode) : sectionResizeMode( header(), logicalIndex); } void DeferredTreeView::setDeferredResizeMode(int logicalIndex, QHeaderView::ResizeMode mode) { auto it = m_sectionsProperties.find(logicalIndex); if (it != m_sectionsProperties.end()) { (*it).resizeMode = mode; } else { DeferredHeaderProperties properties; properties.resizeMode = mode; m_sectionsProperties[logicalIndex] = properties; } if (logicalIndex < header()->count()) { setSectionResizeMode(header(), logicalIndex, mode); m_sectionsProperties[logicalIndex].initialized = true; } } bool DeferredTreeView::deferredHidden(int logicalIndex) const { const auto it = m_sectionsProperties.constFind(logicalIndex); const int hidden = it != m_sectionsProperties.constEnd() ? (*it).hidden : -1; return hidden != -1 ? hidden == 1 : header()->isSectionHidden(logicalIndex); } void DeferredTreeView::setDeferredHidden(int logicalIndex, bool hidden) { auto it = m_sectionsProperties.find(logicalIndex); if (it != m_sectionsProperties.end()) { (*it).hidden = hidden; } else { DeferredHeaderProperties properties; properties.hidden = hidden ? 1 : 0; m_sectionsProperties[logicalIndex] = properties; } if (logicalIndex < header()->count()) { header()->setSectionHidden(logicalIndex, hidden); m_sectionsProperties[logicalIndex].initialized = true; } } bool DeferredTreeView::expandNewContent() const { return m_expandNewContent; } void DeferredTreeView::setExpandNewContent(bool expand) { m_expandNewContent = expand; } bool DeferredTreeView::stretchLastSection() const { return header()->stretchLastSection(); } void DeferredTreeView::setStretchLastSection(bool stretch) { header()->setStretchLastSection(stretch); } void DeferredTreeView::resetDeferredInitialized() { for (auto it = m_sectionsProperties.begin(), end = m_sectionsProperties.end(); it != end; ++it) (*it).initialized = false; } void DeferredTreeView::rowsInserted(const QModelIndex &parent, int start, int end) { QTreeView::rowsInserted(parent, start, end); triggerExpansion(parent); } void DeferredTreeView::sectionCountChanged() { const int sections = header()->count(); if (sections == 0) { resetDeferredInitialized(); return; } for (auto it = m_sectionsProperties.begin(), end = m_sectionsProperties.end(); it != end; ++it) { if ((*it).initialized) continue; if (it.key() < sections) { if ((*it).resizeMode != -1) setSectionResizeMode(header(), it.key(), QHeaderView::ResizeMode((*it).resizeMode)); if ((*it).hidden) header()->setSectionHidden(it.key(), (*it).hidden == 1); (*it).initialized = true; } } } void DeferredTreeView::triggerExpansion(const QModelIndex &parent) { if (m_expandNewContent) { m_insertedRows << QPersistentModelIndex(parent); m_timer->start(); } } void DeferredTreeView::timeout() { const QModelIndex selectedRow = selectionModel()->selectedRows().value(0); if (m_allExpanded) { for (auto it = m_insertedRows.constBegin(), end = m_insertedRows.constEnd(); it != end; ++it) { if (it->isValid()) expand(*it); } } else { m_allExpanded = true; expandAll(); } m_insertedRows.clear(); if (selectedRow.isValid()) scrollTo(selectedRow); emit newContentExpanded(); } gammaray-2.11.3/ui/deferredtreeview.h000066400000000000000000000066001413310443000175100ustar00rootroot00000000000000/* * This file is part of GammaRay, the Qt application inspection and * manipulation tool. * * Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com * Author: Filipe Azevedo * * Licensees holding valid commercial KDAB GammaRay licenses may use this file in * accordance with GammaRay Commercial License Agreement provided with the Software. * * Contact info@kdab.com if any conditions of this licensing are not clear to you. * * 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, see . * */ #ifndef DEFERREDTREEVIEW_H #define DEFERREDTREEVIEW_H #include "gammaray_ui_export.h" #include #include #include QT_BEGIN_NAMESPACE class QTimer; QT_END_NAMESPACE namespace GammaRay { /** @brief Base tree view which allow deferred configurations. * * If you starts using some deferred members - stick to them to avoid * synchronization states issues. */ class GAMMARAY_UI_EXPORT DeferredTreeView : public QTreeView { Q_OBJECT public: explicit DeferredTreeView(QWidget *parent = nullptr); void setModel(QAbstractItemModel *model) override; // TODO: Move sections members into HeaderView so it can be reused by QTableView. QHeaderView::ResizeMode deferredResizeMode(int logicalIndex) const; void setDeferredResizeMode(int logicalIndex, QHeaderView::ResizeMode mode); bool deferredHidden(int logicalIndex) const; void setDeferredHidden(int logicalIndex, bool hidden); bool expandNewContent() const; void setExpandNewContent(bool expand); bool stretchLastSection() const; void setStretchLastSection(bool stretch); signals: void newContentExpanded(); protected: void resetDeferredInitialized(); protected slots: void rowsInserted(const QModelIndex &parent, int start, int end) override; private: struct DeferredHeaderProperties { DeferredHeaderProperties() = default; bool initialized = false; // When trying to only play with hidden, we guess the resizeMode using header->resizeMode(). // Though hidden/unexisting columns always return resize mode Fixed, which is bad. // Also when trying to only play with resizeMode, we guess the hidden property, can be wrong too. // Let use int with -1 meaning don't apply the property. int resizeMode = -1; int hidden = -1; }; typedef QMap SectionsProperties; // This use logical indexes SectionsProperties m_sectionsProperties; bool m_expandNewContent; bool m_allExpanded; QVector m_insertedRows; QTimer *m_timer; private slots: void sectionCountChanged(); void triggerExpansion(const QModelIndex &parent); void timeout(); }; } // namespace GammaRay #endif // DEFERREDTREEVIEW_H gammaray-2.11.3/ui/deferredtreeview_p.h000066400000000000000000000032001413310443000200200ustar00rootroot00000000000000/* * This file is part of GammaRay, the Qt application inspection and * manipulation tool. * * Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com * Author: Filipe Azevedo * * Licensees holding valid commercial KDAB GammaRay licenses may use this file in * accordance with GammaRay Commercial License Agreement provided with the Software. * * Contact info@kdab.com if any conditions of this licensing are not clear to you. * * 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, see . * */ #ifndef GAMMARAY_DEFERREDTREEVIEW_P_H #define GAMMARAY_DEFERREDTREEVIEW_P_H #include namespace GammaRay { class HeaderView : public QHeaderView { Q_OBJECT public: enum State { NoState, ResizeSection, MoveSection, SelectSections, NoClear }; // Copied from QHVPrivate explicit HeaderView(Qt::Orientation orientation, QWidget *parent = nullptr); bool isState(State state) const; }; } #endif gammaray-2.11.3/ui/editabletypesmodel.cpp000066400000000000000000000036201413310443000203660ustar00rootroot00000000000000/* editabletypesmodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "editabletypesmodel.h" #include "propertyeditor/propertyeditorfactory.h" using namespace GammaRay; EditableTypesModel::EditableTypesModel(QObject *parent) : QAbstractListModel(parent) { m_types = PropertyEditorFactory::supportedTypes(); } EditableTypesModel::~EditableTypesModel() = default; int EditableTypesModel::rowCount(const QModelIndex &parent) const { if (parent.isValid()) return 0; return m_types.size(); } QVariant EditableTypesModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); const int type = m_types.at(index.row()); if (role == Qt::DisplayRole) return QMetaType::typeName(type); else if (role == Qt::UserRole) return type; return QVariant(); } gammaray-2.11.3/ui/editabletypesmodel.h000066400000000000000000000033321413310443000200330ustar00rootroot00000000000000/* editabletypesmodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_EDITABLETYPESMODEL_H #define GAMMARAY_EDITABLETYPESMODEL_H #include #include namespace GammaRay { /** All types we have edit widgets for. */ class EditableTypesModel : public QAbstractListModel { Q_OBJECT public: explicit EditableTypesModel(QObject *parent = nullptr); ~EditableTypesModel() override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; private: QVector m_types; }; } #endif // GAMMARAY_EDITABLETYPESMODEL_H gammaray-2.11.3/ui/helpcontroller.cpp000066400000000000000000000105041413310443000175420ustar00rootroot00000000000000/* helpcontroller.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include "helpcontroller.h" #include #include #include #include #include #include #include #include #include using namespace GammaRay; namespace GammaRay { struct HelpControllerPrivate { HelpControllerPrivate() = default; void startProcess(); void sendCommand(const QByteArray &cmd); QString assistantPath; QString qhcPath; QProcess *proc = nullptr; }; } void HelpControllerPrivate::startProcess() { if (proc) return; proc = new QProcess(QCoreApplication::instance()); proc->setProcessChannelMode(QProcess::ForwardedChannels); QObject::connect(proc, static_cast(&QProcess::finished), [this](){ proc->deleteLater(); proc = nullptr; }); proc->setProgram(assistantPath); proc->setArguments(QStringList() << QLatin1String("-collectionFile") << qhcPath << QLatin1String("-enableRemoteControl")); proc->start(); proc->waitForStarted(); sendCommand("expandToc 2;"); } void HelpControllerPrivate::sendCommand(const QByteArray &cmd) { if (!proc) { return; } proc->write(cmd); } Q_GLOBAL_STATIC(HelpControllerPrivate, s_helpController) static QString assistantExecutableName() { #ifdef Q_OS_OSX return QStringLiteral("Assistant.app"); #else return QStringLiteral("assistant"); #endif } bool HelpController::isAvailable() { auto d = s_helpController(); if (!d->assistantPath.isEmpty() && !d->qhcPath.isEmpty()) return true; d->assistantPath = QLibraryInfo::location(QLibraryInfo::BinariesPath) + QDir::separator() + assistantExecutableName(); QFileInfo assistFile(d->assistantPath); if (!assistFile.isExecutable()) { qDebug() << "Qt Assistant not found in QT_INSTALL_BINS. Looking in standard Path next."; d->assistantPath = QStandardPaths::findExecutable(assistantExecutableName()); if (d->assistantPath.isEmpty()) { qDebug() << "Qt Assistant not found, help not available."; return false; } } const QString qhcPath = Paths::documentationPath() + QLatin1String("/gammaray.qhc"); if (QFileInfo::exists(qhcPath)) { d->qhcPath = qhcPath; return true; } else { qDebug() << "gammaray.qhc not found in" << Paths::documentationPath() << " - help not available"; } return false; } void HelpController::openContents() { Q_ASSERT(isAvailable()); auto d = s_helpController(); d->startProcess(); d->sendCommand( "setSource qthelp://com.kdab.GammaRay." GAMMARAY_PLUGIN_VERSION "/gammaray/index.html;syncContents\n"); } void HelpController::openPage(const QString &page) { Q_ASSERT(isAvailable()); auto d = s_helpController(); d->startProcess(); d->sendCommand(QByteArray( "setSource qthelp://com.kdab.GammaRay." GAMMARAY_PLUGIN_VERSION "/") + page.toUtf8() + ";syncContents\n"); } gammaray-2.11.3/ui/helpcontroller.h000066400000000000000000000033451413310443000172140ustar00rootroot00000000000000/* helpcontroller.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_HELPCONTROLLER_H #define GAMMARAY_HELPCONTROLLER_H #include "gammaray_ui_export.h" #include QT_BEGIN_NAMESPACE class QString; QT_END_NAMESPACE namespace GammaRay { /*! Controls the Assistant-based help browser. */ namespace HelpController { /*! Returns @c true if Assistant and our help collection are found. */ GAMMARAY_UI_EXPORT bool isAvailable(); /*! Open start page of the help collection. */ GAMMARAY_UI_EXPORT void openContents(); /*! Opens the specified page of the help collection. */ GAMMARAY_UI_EXPORT void openPage(const QString &page); } } #endif // GAMMARAY_HELPCONTROLLER_H gammaray-2.11.3/ui/itemdelegate.cpp000066400000000000000000000063351413310443000171460ustar00rootroot00000000000000/* * This file is part of GammaRay, the Qt application inspection and * manipulation tool. * * Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com * Author: Filipe Azevedo * * Licensees holding valid commercial KDAB GammaRay licenses may use this file in * accordance with GammaRay Commercial License Agreement provided with the Software. * * Contact info@kdab.com if any conditions of this licensing are not clear to you. * * 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, see . * */ #include "itemdelegate.h" #include using namespace GammaRay; ItemDelegateInterface::ItemDelegateInterface() : m_placeholderText(ItemDelegate::tr("(Item %r)")) { } ItemDelegateInterface::ItemDelegateInterface(const QString &placeholderText) : m_placeholderText(placeholderText) { } QString ItemDelegateInterface::placeholderText() const { return m_placeholderText; } void ItemDelegateInterface::setPlaceholderText(const QString &placeholderText) { m_placeholderText = placeholderText; } QSet ItemDelegateInterface::placeholderColumns() const { return m_placeholderColumns; } void ItemDelegateInterface::setPlaceholderColumns(const QSet &placeholderColumns) { m_placeholderColumns = placeholderColumns; } QString ItemDelegateInterface::defaultDisplayText(const QModelIndex &index) const { QString display = index.data().toString(); if (display.isEmpty() && (m_placeholderColumns.isEmpty() || m_placeholderColumns.contains(index.column()))) { display = QString(m_placeholderText) .replace(QStringLiteral("%r"), QString::number(index.row())) .replace(QStringLiteral("%c"), QString::number(index.column())); } return display; } const QWidget *ItemDelegateInterface::widget(const QStyleOptionViewItem &option) const { const QStyleOptionViewItem &opt(option); return opt.widget; } QStyle *ItemDelegateInterface::style(const QStyleOptionViewItem &option) const { const QWidget *widget = this->widget(option); return widget ? widget->style() : QApplication::style(); } ItemDelegate::ItemDelegate(QObject *parent) : QStyledItemDelegate(parent) , ItemDelegateInterface() { } void ItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { QStyleOptionViewItem opt = option; opt.text = defaultDisplayText(index); initStyleOption(&opt, index); const QWidget *widget = this->widget(option); QStyle *style = this->style(option); style->drawControl(QStyle::CE_ItemViewItem, &opt, painter, widget); } gammaray-2.11.3/ui/itemdelegate.h000066400000000000000000000050621413310443000166070ustar00rootroot00000000000000/* * This file is part of GammaRay, the Qt application inspection and * manipulation tool. * * Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com * Author: Filipe Azevedo * * Licensees holding valid commercial KDAB GammaRay licenses may use this file in * accordance with GammaRay Commercial License Agreement provided with the Software. * * Contact info@kdab.com if any conditions of this licensing are not clear to you. * * 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, see . * */ #ifndef GAMMARAY_ITEMDELEGATE_H #define GAMMARAY_ITEMDELEGATE_H #include "gammaray_ui_export.h" #include #include namespace GammaRay { /** @brief A simple interface that avoid empty display role texts. */ class GAMMARAY_UI_EXPORT ItemDelegateInterface { public: ItemDelegateInterface(); explicit ItemDelegateInterface(const QString &placeholderText); // You can put 2 placeholders for row/column using %r and %c QString placeholderText() const; void setPlaceholderText(const QString &placeholderText); // which columns do show place holders, default all (empty) QSet placeholderColumns() const; void setPlaceholderColumns(const QSet &placeholderColumns); protected: QString defaultDisplayText(const QModelIndex &index) const; const QWidget *widget(const QStyleOptionViewItem &option) const; QStyle *style(const QStyleOptionViewItem &option) const; private: QString m_placeholderText; QSet m_placeholderColumns; }; /** @brief A simple delegate that avoid empty display role texts. */ class GAMMARAY_UI_EXPORT ItemDelegate : public QStyledItemDelegate, public ItemDelegateInterface { Q_OBJECT public: explicit ItemDelegate(QObject *parent = nullptr); void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; }; } // Namespace GammaRay #endif // GAMMARAY_ITEMDELEGATE_H gammaray-2.11.3/ui/mainwindow.cpp000066400000000000000000000566541413310443000167020ustar00rootroot00000000000000/* mainwindow.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include "mainwindow.h" #include "ui_mainwindow.h" #include "aboutpluginsdialog.h" #include "aboutdialog.h" #include "clienttoolmanager.h" #include "clienttoolfilterproxymodel.h" #include "aboutdata.h" #include "uiintegration.h" #include "helpcontroller.h" #include "uiresources.h" #include "common/objectbroker.h" #include "common/modelroles.h" #include "common/endpoint.h" #include "common/probecontrollerinterface.h" #include "kde/klinkitemselectionmodel.h" #ifndef GAMMARAY_DISABLE_FEEDBACK #include <3rdparty/kuserfeedback/widgets/feedbackconfigdialog.h> #include <3rdparty/kuserfeedback/widgets/notificationpopup.h> #include <3rdparty/kuserfeedback/core/applicationversionsource.h> #include <3rdparty/kuserfeedback/core/compilerinfosource.h> #include <3rdparty/kuserfeedback/core/openglinfosource.h> #include <3rdparty/kuserfeedback/core/platforminfosource.h> #include <3rdparty/kuserfeedback/core/provider.h> #include <3rdparty/kuserfeedback/core/qtversionsource.h> #include <3rdparty/kuserfeedback/core/selectionratiosource.h> #include <3rdparty/kuserfeedback/core/startcountsource.h> #include <3rdparty/kuserfeedback/core/usagetimesource.h> #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace GammaRay; namespace { struct IdeSettings { const char * const app; const char * const args; const char * const name; const char * const icon; }; static const IdeSettings ideSettings[] = { #if defined(Q_OS_WIN) || defined(Q_OS_OSX) {"", "", "", "" } // Dummy content, because we can't have empty arrays. #else { "kdevelop", "%f:%l:%c", QT_TRANSLATE_NOOP("GammaRay::MainWindow", "KDevelop"), "kdevelop" }, { "kate", "%f --line %l --column %c", QT_TRANSLATE_NOOP("GammaRay::MainWindow", "Kate"), "kate" }, { "kwrite", "%f --line %l --column %c", QT_TRANSLATE_NOOP("GammaRay::MainWindow", "KWrite"), nullptr }, { "gedit", "%f +%l:%c", QT_TRANSLATE_NOOP("GammaRay::MainWindow", "gedit"), nullptr }, { "gvim", "%f +%l", QT_TRANSLATE_NOOP("GammaRay::MainWindow", "gvim"), nullptr }, { "qtcreator", "%f:%l:%c", QT_TRANSLATE_NOOP("GammaRay::MainWindow", "Qt Creator"), nullptr } #endif }; #if defined(Q_OS_WIN) || defined(Q_OS_OSX) // Remove this #if branch when adding real data to ideSettings for Windows/OSX. static const int ideSettingsSize = 0; #else static const int ideSettingsSize = sizeof(ideSettings) / sizeof(IdeSettings); #endif QStyle *gammarayStyleOverride() { const auto styleNameOverride = QString::fromLocal8Bit(qgetenv("GAMMARAY_STYLE")); if (styleNameOverride.isEmpty()) { return nullptr; } if (!QStyleFactory::keys().contains(styleNameOverride)) { qWarning() << "Style" << styleNameOverride << "does not exit (enabled by GAMMARAY_STYLE environment variable)"; qWarning() << "Existing styles: " << QStyleFactory::keys(); } return QStyleFactory::create(styleNameOverride); } QStyle *gammarayDefaultStyle() { foreach (const QString &styleName, QGuiApplicationPrivate::platform_theme->themeHint(QPlatformTheme::StyleNames). toStringList()) { if (auto style = QStyleFactory::create(styleName)) { return style; } } return nullptr; } } MainWindowUIStateManager::MainWindowUIStateManager(QWidget *widget) : UIStateManager(widget) { } QList MainWindowUIStateManager::splitters() const { return QList(); } QList MainWindowUIStateManager::headers() const { return QList(); } MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) , m_stateManager(this) , m_feedbackProvider(nullptr) { const auto styleOverride = gammarayStyleOverride(); if (styleOverride) { applyStyle(styleOverride); } if (!Endpoint::instance()->isRemoteClient()) { // we don't want application styles to propagate to the GammaRay window, // so set the platform default one if needed // check if the style is not already overwritten if (!styleOverride) { if (auto defaultStyle= gammarayDefaultStyle()) { applyStyle(defaultStyle); } } } UIResources::setTheme(UiIntegration::hasDarkUI() ? UIResources::Light : UIResources::Dark); ui->setupUi(this); connect(ui->actionRetractProbe, &QAction::triggered, this, &MainWindow::detachProbe); connect(QApplication::instance(), &QCoreApplication::aboutToQuit, this, &QWidget::close); connect(ui->actionQuit, &QAction::triggered, this, &MainWindow::quitHost); ui->actionQuit->setIcon(QIcon::fromTheme(QStringLiteral("application-exit"))); ui->actionHelp->setShortcut(QKeySequence::HelpContents); ui->actionHelp->setEnabled(HelpController::isAvailable()); connect(ui->actionHelp, &QAction::triggered, this, &MainWindow::help); connect(ui->actionPlugins, &QAction::triggered, this, &MainWindow::aboutPlugins); connect(ui->actionMessageStatistics, &QAction::triggered, this, &MainWindow::showMessageStatistics); connect(ui->actionAboutQt, &QAction::triggered, qobject_cast(QApplication::instance()), &QApplication::aboutQt); connect(ui->actionAboutGammaRay, &QAction::triggered, this, &MainWindow::about); connect(ui->actionAboutKDAB, &QAction::triggered, this, &MainWindow::aboutKDAB); setWindowIcon(QIcon(QStringLiteral(":/gammaray/GammaRay-128x128.png"))); // ClientConnectionManager take care of creating and requesting server tools // but in-process ui need to do it itself. ClientToolManager *toolManager = ClientToolManager::instance(); if (!toolManager) { toolManager = new ClientToolManager(this); toolManager->requestAvailableTools(); } toolManager->setToolParentWidget(this); auto sourceModel = toolManager->model(); auto sourceSelectionModel = toolManager->selectionModel(); m_toolFilterModel = new ClientToolFilterProxyModel(this); m_toolFilterModel->setSourceModel(sourceModel); m_toolFilterModel->setDynamicSortFilter(true); m_toolFilterModel->sort(0); ui->toolSelector->setModel(m_toolFilterModel); ui->toolSelector->setSelectionModel(new KLinkItemSelectionModel(m_toolFilterModel, sourceSelectionModel, sourceModel)); ui->toolSelector->resize(ui->toolSelector->minimumSize()); connect(toolManager->selectionModel(), &QItemSelectionModel::selectionChanged, this, &MainWindow::toolSelected); connect(ui->toolSelector, &QWidget::customContextMenuRequested, this, &MainWindow::toolContextMenu); QSettings settings; settings.beginGroup("Sidebar"); m_toolFilterModel->setFilterInactiveTools(settings.value(QLatin1String("FilterInactive"), false).toBool()); settings.endGroup(); // hide unused tool bar for now ui->mainToolBar->setHidden(true); setWindowTitle(tr("GammaRay (%1)").arg(Endpoint::instance()->label())); // Code Navigation QAction *configAction = new QAction(QIcon::fromTheme(QStringLiteral("applications-development")), tr("Code Navigation"), this); auto menu = new QMenu(this); auto group = new QActionGroup(this); group->setExclusive(true); settings.beginGroup(QStringLiteral("CodeNavigation")); const auto currentIdx = settings.value(QStringLiteral("IDE"), -1).toInt(); for (int i = 0; i < ideSettingsSize; ++i) { auto action = new QAction(menu); action->setText(tr(ideSettings[i].name)); if (ideSettings[i].icon) { action->setIcon(QIcon::fromTheme(ideSettings[i].icon)); } action->setCheckable(true); action->setChecked(currentIdx == i); action->setData(i); action->setEnabled(!QStandardPaths::findExecutable(ideSettings[i].app).isEmpty()); group->addAction(action); menu->addAction(action); } menu->addSeparator(); auto *action = new QAction(menu); action->setText(tr("Custom...")); action->setCheckable(true); action->setChecked(currentIdx == -1); action->setData(-1); group->addAction(action); menu->addAction(action); #if defined(Q_OS_WIN) || defined(Q_OS_OSX) // This is a workaround for the cases, where we can't safely do assumptions // about the install location of the IDE action = new QAction(menu); action->setText(tr("Automatic (No Line numbers)")); action->setCheckable(true); action->setChecked(currentIdx == -2); action->setData(-2); group->addAction(action); menu->addAction(action); #endif QObject::connect(group, &QActionGroup::triggered, this, &MainWindow::setCodeNavigationIDE); configAction->setMenu(menu); ui->menuSettings->addMenu(menu); // Initialize UiIntegration singleton new UiIntegration(this); connect(UiIntegration::instance(), &UiIntegration::navigateToCode, this, &MainWindow::navigateToCode); const bool developerModeEnabled = !qgetenv("GAMMARAY_DEVELOPERMODE").isEmpty(); if (developerModeEnabled) { connect(Endpoint::instance(), &Endpoint::logTransmissionRate, this, &MainWindow::logTransmissionRate); } else { ui->statusBar->hide(); ui->menu_Diagnostics->menuAction()->setVisible(false); } connect(this, &MainWindow::targetQuitRequested, &m_stateManager, &UIStateManager::saveState); } MainWindow::~MainWindow() { QSettings settings; settings.beginGroup("Sidebar"); settings.setValue(QLatin1String("FilterInactive"), m_toolFilterModel->filterInactiveTools()); settings.endGroup(); } void MainWindow::saveTargetState(QSettings *settings) const { // When closing/quitting the client main window, this is triggered but the connection is already closed if (Endpoint::instance()->isConnected()) { const QModelIndex toolIndex = ui->toolSelector->selectionModel()->selectedRows().value(0); const QString toolId = toolIndex.data(ToolModelRole::ToolId).toString(); settings->setValue("selectedToolId", toolId); } } void MainWindow::restoreTargetState(QSettings *settings) { Q_ASSERT(ClientToolManager::instance()->isToolListLoaded()); const QString toolId = settings->value("selectedToolId", QStringLiteral("GammaRay::ObjectInspector")).toString(); selectTool(toolId); } void MainWindow::setupFeedbackProvider() { #ifndef GAMMARAY_DISABLE_FEEDBACK ui->actionContribute->setEnabled(true); connect(ui->actionContribute, &QAction::triggered, this, &MainWindow::configureFeedback); m_feedbackProvider = new KUserFeedback::Provider(this); m_feedbackProvider->setProductIdentifier(QStringLiteral("com.kdab.GammaRay")); m_feedbackProvider->setFeedbackServer(QUrl(QStringLiteral("https://gammaray-userfeedback.kdab.com/"))); m_feedbackProvider->setSubmissionInterval(7); m_feedbackProvider->setApplicationStartsUntilEncouragement(5); m_feedbackProvider->setEncouragementDelay(30); m_feedbackProvider->addDataSource(new KUserFeedback::ApplicationVersionSource); m_feedbackProvider->addDataSource(new KUserFeedback::CompilerInfoSource); m_feedbackProvider->addDataSource(new KUserFeedback::PlatformInfoSource); m_feedbackProvider->addDataSource(new KUserFeedback::QtVersionSource); m_feedbackProvider->addDataSource(new KUserFeedback::StartCountSource); m_feedbackProvider->addDataSource(new KUserFeedback::UsageTimeSource); m_feedbackProvider->addDataSource(new KUserFeedback::OpenGLInfoSource); auto toolRatioSrc = new KUserFeedback::SelectionRatioSource(ui->toolSelector->selectionModel(), QStringLiteral("toolRatio")); toolRatioSrc->setDescription(tr("Usage ratio of the GammaRay tools.")); toolRatioSrc->setRole(ToolModelRole::ToolFeedbackId); toolRatioSrc->setTelemetryMode(KUserFeedback::Provider::DetailedUsageStatistics); m_feedbackProvider->addDataSource(toolRatioSrc); auto popup = new KUserFeedback::NotificationPopup(this); popup->setFeedbackProvider(m_feedbackProvider); #endif } void MainWindow::help() { HelpController::openContents(); } void MainWindow::about() { AboutDialog dialog(this); dialog.setWindowTitle(tr("About GammaRay")); dialog.setWindowIcon(QPixmap(QStringLiteral(":/gammaray/GammaRay-128x128.png"))); dialog.setThemeLogo(QStringLiteral("gammaray-trademark.png")); dialog.setTitle(AboutData::aboutTitle()); dialog.setHeader(AboutData::aboutHeader()); dialog.setAuthors(AboutData::aboutAuthors()); dialog.setFooter(AboutData::aboutFooter()); dialog.adjustSize(); dialog.exec(); } void MainWindow::aboutPlugins() { AboutPluginsDialog dlg(this); dlg.setFixedSize(800, 600); dlg.exec(); } void MainWindow::aboutKDAB() { AboutDialog dialog(this); dialog.setWindowTitle(tr("About KDAB")); dialog.setWindowIcon(UIResources::themedPixmap(QStringLiteral("kdab-logo.png"), this)); dialog.setThemeLogo(QStringLiteral("kdab-logo.png")); dialog.setTitle(trUtf8("Klarälvdalens Datakonsult AB (KDAB)")); dialog.setText( tr("

    GammaRay is supported and maintained by KDAB

    " "

    The KDAB Group is the global No.1 software consultancy for Qt, C++ and " "OpenGL applications across desktop, embedded and mobile platforms.

    " "

    The KDAB Group provides consulting and mentoring for developing legacy " "Qt applications from scratch and in porting from all popular and frameworks " "to Qt. We continue to help develop parts of Qt and are one of the major " "contributors to the Qt Project. We can give advanced or standard trainings " "anywhere around the globe on Qt as well as C++, OpenGL, 3D and more.

    " "

    If you would like to have a custom plugin for GammaRay to visualize, profile " "or debug your own specific components or applications, get in touch with us " "via https://www.kdab.com/contact. " "KDAB engineers know how to write GammaRay plugins and can be contracted to " "help you get yours working and improve your development efficiency.

    " "
    ")); dialog.exec(); } void MainWindow::showMessageStatistics() { auto view = new QTableView; view->setWindowTitle(tr("Communication Message Statistics")); view->setAttribute(Qt::WA_DeleteOnClose); view->setModel(ObjectBroker::model(QStringLiteral("com.kdab.GammaRay.MessageStatisticsModel"))); view->horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeToContents); view->showMaximized(); } bool MainWindow::selectTool(const QString &id) { if (id.isEmpty()) return false; const QItemSelectionModel::SelectionFlags selectionFlags = QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows | QItemSelectionModel::Current; const Qt::MatchFlags matchFlags = Qt::MatchExactly | Qt::MatchRecursive | Qt::MatchWrap; const QAbstractItemModel *model = ui->toolSelector->model(); const QModelIndex toolIndex = model->match(model->index(0, 0), ToolModelRole::ToolId, id, 1, matchFlags).value(0); if (!toolIndex.isValid()) return false; QItemSelectionModel *selectionModel = ui->toolSelector->selectionModel(); selectionModel->setCurrentIndex(toolIndex, selectionFlags); return true; } void MainWindow::toolSelected() { ui->actionsMenu->clear(); QModelIndexList list = ui->toolSelector->selectionModel()->selectedRows(); int row = -1; if (!list.isEmpty()) row = list[0].row(); if (row == -1) return; const QModelIndex mi = ui->toolSelector->model()->index(row, 0); QWidget *toolWidget = mi.data(ToolModelRole::ToolWidget).value(); if (!toolWidget) { toolWidget = createErrorPage(mi); ui->toolSelector->model()->setData(mi, QVariant::fromValue( toolWidget), ToolModelRole::ToolWidget); } Q_ASSERT(toolWidget); if (ui->toolStack->indexOf(toolWidget) < 0) { // newly created if (toolWidget->layout()) { #ifndef Q_OS_MAC toolWidget->layout()->setContentsMargins(11, 0, 0, 0); #else QMargins margins = toolWidget->layout()->contentsMargins(); margins.setLeft(0); toolWidget->layout()->setContentsMargins(margins); #endif } ui->toolStack->addWidget(toolWidget); } ui->toolStack->setCurrentIndex(ui->toolStack->indexOf(toolWidget)); foreach (QAction *action, toolWidget->actions()) { if (auto widgetAction = qobject_cast(action)) { if (auto toolButton = qobject_cast(widgetAction->defaultWidget())) { auto subMenu = ui->actionsMenu->addMenu(toolButton->text()); if (auto defaultAction = toolButton->defaultAction()) { subMenu->addAction(defaultAction); subMenu->addSeparator(); } subMenu->addActions(toolButton->menu()->actions()); } } else { ui->actionsMenu->addAction(action); } } ui->actionsMenu->setEnabled(!ui->actionsMenu->isEmpty()); ui->actionsMenu->setTitle(mi.data().toString()); } void MainWindow::toolContextMenu(QPoint pos) { QMenu menu; auto action = menu.addAction(tr("Hide inactive tools")); action->setCheckable(true); action->setChecked(m_toolFilterModel->filterInactiveTools()); connect(action, &QAction::toggled, m_toolFilterModel, &ClientToolFilterProxyModel::setFilterInactiveTools); menu.exec(ui->toolSelector->viewport()->mapToGlobal(pos)); } void MainWindow::navigateToCode(const QUrl &url, int lineNumber, int columnNumber) { // Show Qt resources in our qrc browser if (url.scheme() == "qrc") { if (selectTool(QStringLiteral("GammaRay::ResourceBrowser"))) { QMetaObject::invokeMethod(ui->toolStack->currentWidget(), "selectResource", Q_ARG(QString, url.toString()), Q_ARG(int, lineNumber), Q_ARG(int, columnNumber)); } } else { QSettings settings; settings.beginGroup(QStringLiteral("CodeNavigation")); const auto ideIdx = settings.value(QStringLiteral("IDE"), -1).toInt(); QString command; #if !defined(Q_OS_WIN) && !defined(Q_OS_OSX) // Remove this #if branch when adding real data to ideSettings for Windows/OSX. if (ideIdx >= 0 && ideIdx < ideSettingsSize) { command += ideSettings[ideIdx].app; command += ' '; command += ideSettings[ideIdx].args; } else #endif if (ideIdx == -1) { command = settings.value(QStringLiteral("CustomCommand")).toString(); } else { QDesktopServices::openUrl(QUrl(url)); } const QString filePath = url.isLocalFile() ? url.toLocalFile() : url.toString(); command.replace(QStringLiteral("%f"), filePath); command.replace(QStringLiteral("%l"), QString::number(std::max(1, lineNumber + 1))); command.replace(QStringLiteral("%c"), QString::number(std::max(1, columnNumber + 1))); if (!command.isEmpty()) { std::cout << "Detaching: " << qPrintable(command) << std::endl; QProcess::startDetached(command); } } } void MainWindow::logTransmissionRate(quint64 bytesRead, quint64 bytesWritten) { const double transmissionRateRX = (bytesRead * 8 / 1024.0 / 1024.0); // in Mpbs const double transmissionRateTX = (bytesWritten * 8 / 1024.0 / 1024.0); // in Mpbs ui->statusBar->showMessage( tr("Transmission rate: RX %1 Mbps, TX %2 Mbps"). arg(transmissionRateRX, 7, 'f', 3). arg(transmissionRateTX, 7, 'f', 3)); } void GammaRay::MainWindow::setCodeNavigationIDE(QAction *action) { QSettings settings; settings.beginGroup(QStringLiteral("CodeNavigation")); if (action->data() == -1) { const auto customCmd = QInputDialog::getText( this, tr("Custom Code Navigation"), tr( "Specify command to use for code navigation, '%f' will be replaced by the file name, '%l' by the line number and '%c' by the column number."), QLineEdit::Normal, settings.value(QStringLiteral("CustomCommand")).toString() ); if (!customCmd.isEmpty()) { settings.setValue(QStringLiteral("CustomCommand"), customCmd); settings.setValue(QStringLiteral("IDE"), -1); } return; } const auto defaultIde = action->data().toInt(); settings.setValue(QStringLiteral("IDE"), defaultIde); } void MainWindow::applyStyle(QStyle *style) { qDebug() << "Using" << style << "style"; // note: do not set this as parent of default style // this will cause the style being deleted too early through ~QObject() // other objects (e.g. the script engine debugger) still might have a // reference on the style during destruction style->setParent(QCoreApplication::instance()); // unfortunately, setting the style is not recursive by default, unless we have a style sheet set setStyleSheet(QStringLiteral("I_DONT_EXIST {}")); setStyle(style); } QWidget *MainWindow::createErrorPage(const QModelIndex &index) { QLabel *page = new QLabel(this); page->setAlignment(Qt::AlignCenter); // TODO show the actual plugin error message as well as any other useful information (eg. file name) we have, once the tool model has those page->setText(tr("Tool %1 failed to load.").arg(index.data(ToolModelRole::ToolId).toString())); return page; } void MainWindow::quitHost() { emit targetQuitRequested(); ObjectBroker::object()->quitHost(); } void MainWindow::detachProbe() { emit targetQuitRequested(); ObjectBroker::object()->detachProbe(); } void MainWindow::configureFeedback() { #ifndef GAMMARAY_DISABLE_FEEDBACK KUserFeedback::FeedbackConfigDialog dlg; dlg.setFeedbackProvider(m_feedbackProvider); dlg.exec(); #endif } gammaray-2.11.3/ui/mainwindow.h000066400000000000000000000056221413310443000163340ustar00rootroot00000000000000/* mainwindow.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_MAINWINDOW_H #define GAMMARAY_MAINWINDOW_H #include #include QT_BEGIN_NAMESPACE class QModelIndex; class QSplitter; class QHeaderView; class QUrl; QT_END_NAMESPACE namespace KUserFeedback { class Provider; } namespace GammaRay { namespace Ui { class MainWindow; } class ClientToolFilterProxyModel; class MainWindowUIStateManager : public UIStateManager { Q_OBJECT public: explicit MainWindowUIStateManager(QWidget *widget); QList splitters() const override; QList headers() const override; }; class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent = nullptr); ~MainWindow() override; void setupFeedbackProvider(); Q_INVOKABLE void saveTargetState(QSettings *settings) const; Q_INVOKABLE void restoreTargetState(QSettings *settings); signals: void targetQuitRequested(); private slots: void help(); void configureFeedback(); void about(); void aboutPlugins(); void aboutKDAB(); void showMessageStatistics(); void toolSelected(); bool selectTool(const QString &id); void toolContextMenu(QPoint pos); void quitHost(); void detachProbe(); void navigateToCode(const QUrl &url, int lineNumber, int columnNumber); void logTransmissionRate(quint64 bytesRead, quint64 bytesWritten); void setCodeNavigationIDE(QAction *action); private: QWidget *createErrorPage(const QModelIndex &index); /// apply custom style for GammaRay's main window void applyStyle(QStyle* style); QScopedPointer ui; MainWindowUIStateManager m_stateManager; ClientToolFilterProxyModel *m_toolFilterModel; KUserFeedback::Provider *m_feedbackProvider; }; } #endif // MAINWINDOW_H gammaray-2.11.3/ui/mainwindow.ui000066400000000000000000000152041413310443000165170ustar00rootroot00000000000000 GammaRay::MainWindow 0 0 829 600 0 0 0 180 0 Qt::CustomContextMenu QFrame::NoFrame QFrame::Plain Qt::ElideRight QListView::TopToBottom false QListView::Fixed QListView::ListMode true false true 0 0 0 0 829 30 &GammaRay &Help &Diagnostics &Actions Setti&ngs Main Toolbar false Qt::BottomToolBarArea|Qt::TopToolBarArea false TopToolBarArea false &Detach Retract probe from application. QAction::ApplicationSpecificRole &Quit Quit probed application. QAction::QuitRole :/gammaray/GammaRay-32x32.png:/gammaray/GammaRay-32x32.png About &GammaRay QAction::AboutRole About &Qt QAction::AboutQtRole About &KDAB QAction::NoRole &Plugins... &Message Statistics... Show GammaRay communication statistics. &Help... Show help browser. false &Contribute... SidePane QListView
    sidepane.h
    gammaray-2.11.3/ui/methodinvocationdialog.cpp000066400000000000000000000055151413310443000212460ustar00rootroot00000000000000/* methodinvocationdialog.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "methodinvocationdialog.h" #include "ui_methodinvocationdialog.h" #include #include using namespace GammaRay; MethodInvocationDialog::MethodInvocationDialog(QWidget *parent) : QDialog(parent) , ui(new Ui::MethodInvocationDialog) , m_stateManager(this) { ui->setupUi(this); ui->argumentView->header()->setObjectName("argumentViewHeader"); ui->argumentView->setDeferredResizeMode(0, QHeaderView::ResizeToContents); ui->argumentView->setDeferredResizeMode(1, QHeaderView::Stretch); ui->argumentView->setDeferredResizeMode(2, QHeaderView::ResizeToContents); ui->buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Invoke")); connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); ui->connectionTypeComboBox->addItem(tr("Auto"), QVariant::fromValue(Qt::AutoConnection)); ui->connectionTypeComboBox->addItem(tr("Direct"), QVariant::fromValue(Qt::DirectConnection)); ui->connectionTypeComboBox->addItem(tr("Queued"), QVariant::fromValue(Qt::QueuedConnection)); } MethodInvocationDialog::~MethodInvocationDialog() = default; Qt::ConnectionType MethodInvocationDialog::connectionType() const { return ui->connectionTypeComboBox->itemData(ui->connectionTypeComboBox->currentIndex()).value(); } void MethodInvocationDialog::setArgumentModel(QAbstractItemModel *model) { ui->argumentView->setModel(model); } gammaray-2.11.3/ui/methodinvocationdialog.h000066400000000000000000000034561413310443000207150ustar00rootroot00000000000000/* methodinvocationdialog.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_METHODINVOCATIONDIALOG_H #define GAMMARAY_METHODINVOCATIONDIALOG_H #include #include QT_BEGIN_NAMESPACE class QAbstractItemModel; QT_END_NAMESPACE namespace GammaRay { namespace Ui { class MethodInvocationDialog; } class MethodInvocationDialog : public QDialog { Q_OBJECT public: explicit MethodInvocationDialog(QWidget *parent = nullptr); ~MethodInvocationDialog() override; Qt::ConnectionType connectionType() const; void setArgumentModel(QAbstractItemModel *model); private: QScopedPointer ui; UIStateManager m_stateManager; }; } #endif // GAMMARAY_METHODINVOCATIONDIALOG_H gammaray-2.11.3/ui/methodinvocationdialog.ui000066400000000000000000000030501413310443000210710ustar00rootroot00000000000000 GammaRay::MethodInvocationDialog 0 0 400 300 &Connection type: connectionTypeComboBox false false QDialogButtonBox::Cancel|QDialogButtonBox::Ok GammaRay::DeferredTreeView QTreeView
    ui/deferredtreeview.h
    gammaray-2.11.3/ui/modelpickerdialog.cpp000066400000000000000000000110221413310443000201600ustar00rootroot00000000000000/* modelpickerdialog.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Filipe Azevedo Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "modelpickerdialog.h" #include "deferredtreeview.h" #include "searchlinecontroller.h" #include #include #include #include #include #include using namespace GammaRay; static QPair qNullSelection() { return qMakePair(-1, QVariant()); } ModelPickerDialog::ModelPickerDialog(QWidget *parent) : QDialog(parent) , m_view(new DeferredTreeView(this)) , m_buttons(new QDialogButtonBox(this)) , m_searchBox (new QLineEdit(this)) , m_showInvisibleItems(new QCheckBox(tr("Hide invisible items"),this)) , m_pendingSelection(qNullSelection()) { setAttribute(Qt::WA_DeleteOnClose); m_view->setUniformRowHeights(true); m_view->setExpandNewContent(true); m_buttons->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); m_showInvisibleItems->setChecked(true); auto *vl = new QVBoxLayout(this); auto *hl = new QHBoxLayout; hl->addWidget(m_searchBox); hl->addWidget(m_showInvisibleItems); vl->addLayout(hl); vl->addWidget(m_view); vl->addWidget(m_buttons); selectionChanged(); resize(640, 480); connect(m_view, &DeferredTreeView::newContentExpanded, this, &ModelPickerDialog::updatePendingSelection); connect(m_view, &DeferredTreeView::activated, this, &ModelPickerDialog::accept); connect(m_buttons, &QDialogButtonBox::accepted, this, &ModelPickerDialog::accept); connect(m_buttons, &QDialogButtonBox::rejected, this, &QDialog::reject); connect(m_showInvisibleItems, &QAbstractButton::toggled, this, &ModelPickerDialog::checkBoxStateChanged); } QAbstractItemModel *ModelPickerDialog::model() const { return m_view->model(); } void ModelPickerDialog::setModel(QAbstractItemModel *model) { m_view->setModel(model); connect(m_view->selectionModel(), &QItemSelectionModel::selectionChanged, this, &ModelPickerDialog::selectionChanged); new SearchLineController(m_searchBox, model); for (int i = 0; i < m_view->model()->columnCount(); ++i) { m_view->setDeferredResizeMode(i, QHeaderView::ResizeToContents); } } void ModelPickerDialog::setRootIndex(const QModelIndex &index) { m_view->setRootIndex(index); } void ModelPickerDialog::setCurrentIndex(const QModelIndex &index) { m_pendingSelection = qNullSelection(); m_view->setCurrentIndex(index); m_view->scrollTo(index); } void ModelPickerDialog::setCurrentIndex(int role, const QVariant &value) { QAbstractItemModel *model = m_view->model(); const QModelIndex index = model->match(model->index(0, 0), role, value, 1, Qt::MatchExactly | Qt::MatchRecursive | Qt::MatchWrap).value(0); if (index.isValid()) { setCurrentIndex(index); } else { m_pendingSelection = qMakePair(role, value); } } void ModelPickerDialog::selectionChanged() { const QModelIndex index = m_view->selectionModel() ? m_view->selectionModel()->selectedRows().value(0) : QModelIndex(); m_buttons->button(QDialogButtonBox::Ok)->setEnabled(index.isValid()); } void ModelPickerDialog::accept() { const QModelIndex index = m_view->selectionModel()->selectedRows().value(0); if (index.isValid()) { emit activated(index); QDialog::accept(); } } void ModelPickerDialog::updatePendingSelection() { if (m_pendingSelection != qNullSelection()) setCurrentIndex(m_pendingSelection.first, m_pendingSelection.second); } gammaray-2.11.3/ui/modelpickerdialog.h000066400000000000000000000045601413310443000176360ustar00rootroot00000000000000/* modelpickerdialog.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Filipe Azevedo Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_MODELPICKERDIALOG_H #define GAMMARAY_MODELPICKERDIALOG_H #include "gammaray_ui_export.h" #include #include QT_BEGIN_NAMESPACE class QModelIndex; class QAbstractItemModel; class QDialogButtonBox; class QLineEdit; class QCheckBox; QT_END_NAMESPACE namespace GammaRay { class DeferredTreeView; /** @brief A simple dialog that allow to pick a model index. */ class GAMMARAY_UI_EXPORT ModelPickerDialog : public QDialog { Q_OBJECT public: explicit ModelPickerDialog(QWidget *parent = nullptr); QAbstractItemModel *model() const; void setModel(QAbstractItemModel *model); void setRootIndex(const QModelIndex &index); void setCurrentIndex(const QModelIndex &index); void setCurrentIndex(int role, const QVariant &value); public slots: void accept() override; signals: void activated(const QModelIndex &index); void checkBoxStateChanged(const bool &checked); private: DeferredTreeView *m_view; QDialogButtonBox *m_buttons; QLineEdit *m_searchBox; QCheckBox *m_showInvisibleItems; QPair m_pendingSelection; private slots: void selectionChanged(); void updatePendingSelection(); }; } // namespace GammaRay #endif // GAMMARAY_MODELPICKERDIALOG_H gammaray-2.11.3/ui/paintanalyzerreplayview.cpp000066400000000000000000000042141413310443000215000ustar00rootroot00000000000000/* paintanalyzerreplayview.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "paintanalyzerreplayview.h" #include #include #include using namespace GammaRay; PaintAnalyzerReplayView::PaintAnalyzerReplayView(QWidget* parent) : RemoteViewWidget(parent) , m_showClipArea(true) { } PaintAnalyzerReplayView::~PaintAnalyzerReplayView() = default; bool PaintAnalyzerReplayView::showClipArea() const { return m_showClipArea; } void PaintAnalyzerReplayView::setShowClipArea(bool show) { m_showClipArea = show; update(); } void PaintAnalyzerReplayView::drawDecoration(QPainter* p) { const auto data = frame().data().value(); if (data.clipPath.isEmpty() || !m_showClipArea) return; QPainterPath invertedClipPath; invertedClipPath.addRect(frame().sceneRect()); invertedClipPath -= data.clipPath; p->save(); p->setTransform(QTransform().scale(zoom(), zoom()), true); QBrush brush(Qt::red, Qt::BDiagPattern); p->fillPath(invertedClipPath, brush); p->restore(); } gammaray-2.11.3/ui/paintanalyzerreplayview.h000066400000000000000000000032501413310443000211440ustar00rootroot00000000000000/* paintanalyzerreplayview.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_PAINTANALYZERREPLAYVIEW_H #define GAMMARAY_PAINTANALYZERREPLAYVIEW_H #include "remoteviewwidget.h" namespace GammaRay { class PaintAnalyzerReplayView : public RemoteViewWidget { Q_OBJECT public: explicit PaintAnalyzerReplayView(QWidget *parent = nullptr); ~PaintAnalyzerReplayView() override; bool showClipArea() const; public slots: void setShowClipArea(bool show); protected: void drawDecoration(QPainter * p) override; private: bool m_showClipArea; }; } #endif // GAMMARAY_PAINTANALYZERREPLAYVIEW_H gammaray-2.11.3/ui/paintanalyzerwidget.cpp000066400000000000000000000155641413310443000206060ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2012-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "paintanalyzerwidget.h" #include "ui_paintanalyzerwidget.h" #include "paintbufferclientmodel.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace GammaRay; PaintAnalyzerWidget::PaintAnalyzerWidget(QWidget *parent) : QWidget(parent) , ui(new Ui::PaintAnalyzerWidget) , m_iface(nullptr) { ui->setupUi(this); ui->commandView->header()->setObjectName("commandViewHeader"); ui->commandView->setItemDelegate(new PropertyEditorDelegate(this)); ui->commandView->setStretchLastSection(false); ui->commandView->setDeferredResizeMode(0, QHeaderView::ResizeToContents); ui->commandView->setDeferredResizeMode(1, QHeaderView::Stretch); ui->commandView->setDeferredResizeMode(2, QHeaderView::ResizeToContents); ui->argumentView->setItemDelegate(new PropertyEditorDelegate(this)); ui->argumentView->header()->setObjectName("argumentViewHeader"); ui->stackTraceView->setItemDelegate(new PropertyEditorDelegate(this)); ui->stackTraceView->header()->setObjectName("stackTraceViewHeader"); auto toolbar = new QToolBar; // Our icons are 16x16 and support hidpi, so let force iconSize on every styles toolbar->setIconSize(QSize(16, 16)); toolbar->setToolButtonStyle(Qt::ToolButtonIconOnly); ui->replayContainer->setMenuBar(toolbar); foreach (auto action, ui->replayWidget->interactionModeActions()->actions()) toolbar->addAction(action); toolbar->addSeparator(); toolbar->addAction(ui->replayWidget->zoomOutAction()); auto zoom = new QComboBox; zoom->setModel(ui->replayWidget->zoomLevelModel()); toolbar->addWidget(zoom); toolbar->addAction(ui->replayWidget->zoomInAction()); toolbar->addSeparator(); toolbar->addAction(ui->actionShowClipArea); ui->replayWidget->setSupportedInteractionModes( RemoteViewWidget::ViewInteraction | RemoteViewWidget::Measuring | RemoteViewWidget::ColorPicking); ui->paintAnalyzerSplitter->setStretchFactor(0, 1); ui->paintAnalyzerSplitter->setStretchFactor(1, 2); connect(zoom, static_cast(&QComboBox::currentIndexChanged), ui->replayWidget, &RemoteViewWidget::setZoomLevel); connect(ui->replayWidget, &RemoteViewWidget::zoomLevelChanged, zoom, &QComboBox::setCurrentIndex); zoom->setCurrentIndex(ui->replayWidget->zoomLevelIndex()); ui->actionShowClipArea->setIcon(UIResources::themedIcon(QLatin1String("visualize-clipping.png"))); connect(ui->actionShowClipArea, &QAction::toggled, ui->replayWidget, &PaintAnalyzerReplayView::setShowClipArea); ui->actionShowClipArea->setChecked(ui->replayWidget->showClipArea()); connect(ui->commandView, &QWidget::customContextMenuRequested, this, &PaintAnalyzerWidget::commandContextMenu); connect(ui->stackTraceView, &QWidget::customContextMenuRequested, this, &PaintAnalyzerWidget::stackTraceContextMenu); } PaintAnalyzerWidget::~PaintAnalyzerWidget() = default; void PaintAnalyzerWidget::setBaseName(const QString &name) { auto model = ObjectBroker::model(name + QStringLiteral(".paintBufferModel")); auto proxy = new PaintBufferClientModel(this); proxy->setSourceModel(model); ui->commandView->setModel(proxy); ui->commandView->setSelectionModel(ObjectBroker::selectionModel(proxy)); new SearchLineController(ui->commandSearchLine, proxy); auto clientPropModel = new ClientPropertyModel(this); clientPropModel->setSourceModel(ObjectBroker::model(name + QStringLiteral(".argumentProperties"))); ui->argumentView->setModel(clientPropModel); ui->stackTraceView->setModel(ObjectBroker::model(name + QStringLiteral(".stackTrace"))); ui->replayWidget->setName(name + QStringLiteral(".remoteView")); m_iface = ObjectBroker::object(name); connect(m_iface, &PaintAnalyzerInterface::hasArgumentDetailsChanged, this, &PaintAnalyzerWidget::detailsChanged); connect(m_iface, &PaintAnalyzerInterface::hasStackTraceChanged, this, &PaintAnalyzerWidget::detailsChanged); detailsChanged(); } void PaintAnalyzerWidget::detailsChanged() { const auto hasAnyDetails = m_iface->hasArgumentDetails() || m_iface->hasStackTrace(); ui->detailsTabWidget->setVisible(hasAnyDetails); if (!hasAnyDetails) return; const auto hasAllDetails = m_iface->hasArgumentDetails() && m_iface->hasStackTrace(); ui->detailsTabWidget->tabBar()->setVisible(hasAllDetails); if (hasAllDetails) return; ui->detailsTabWidget->setCurrentWidget(m_iface->hasArgumentDetails() ? ui->argumentTab : ui->stackTraceTab); } void PaintAnalyzerWidget::commandContextMenu(QPoint pos) { const auto idx = ui->commandView->indexAt(pos); if (!idx.isValid()) return; const auto objectId = idx.data(PaintBufferModelRoles::ObjectIdRole).value(); QMenu contextMenu; ContextMenuExtension cme(objectId); cme.populateMenu(&contextMenu); contextMenu.exec(ui->commandView->viewport()->mapToGlobal(pos)); } void PaintAnalyzerWidget::stackTraceContextMenu(QPoint pos) { const auto idx = ui->stackTraceView->indexAt(pos); if (!idx.isValid()) return; const auto loc = idx.sibling(idx.row(), 1).data().value(); if (!loc.isValid()) return; QMenu contextMenu; ContextMenuExtension cme; cme.setLocation(ContextMenuExtension::ShowSource, loc); cme.populateMenu(&contextMenu); contextMenu.exec(ui->stackTraceView->viewport()->mapToGlobal(pos)); } gammaray-2.11.3/ui/paintanalyzerwidget.h000066400000000000000000000035661413310443000202520ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2012-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_WIDGETINSPECTOR_PAINTANALYZERWIDGET_H #define GAMMARAY_WIDGETINSPECTOR_PAINTANALYZERWIDGET_H #include "gammaray_ui_export.h" #include namespace GammaRay { namespace Ui { class PaintAnalyzerWidget; } class PaintAnalyzerInterface; /** * A widget to look at the command list in a QPaintBuffer. */ class GAMMARAY_UI_EXPORT PaintAnalyzerWidget : public QWidget { Q_OBJECT public: explicit PaintAnalyzerWidget(QWidget *parent = nullptr); ~PaintAnalyzerWidget() override; void setBaseName(const QString &name); private slots: void detailsChanged(); void commandContextMenu(QPoint pos); void stackTraceContextMenu(QPoint pos); private: QScopedPointer ui; PaintAnalyzerInterface *m_iface; }; } #endif gammaray-2.11.3/ui/paintanalyzerwidget.ui000066400000000000000000000071761413310443000204410ustar00rootroot00000000000000 GammaRay::PaintAnalyzerWidget 0 0 591 378 0 Qt::Horizontal Qt::Vertical Qt::CustomContextMenu 10 false 0 Argument 10 Stack Trace Qt::CustomContextMenu false true true Visualize Clip Area Highlight current clipping area. GammaRay::DeferredTreeView QTreeView
    ui/deferredtreeview.h
    GammaRay::PaintAnalyzerReplayView QWidget
    ui/paintanalyzerreplayview.h
    gammaray-2.11.3/ui/paintbufferclientmodel.cpp000066400000000000000000000060201413310443000212310ustar00rootroot00000000000000/* paintbufferclientmodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "paintbufferclientmodel.h" #include #include #include #include using namespace GammaRay; PaintBufferClientModel::PaintBufferClientModel(QObject* parent) : QIdentityProxyModel(parent) { } PaintBufferClientModel::~PaintBufferClientModel() = default; QVariant PaintBufferClientModel::data(const QModelIndex& index, int role) const { if (index.isValid() && !index.parent().isValid() && index.column() == 2) { switch (role) { case Qt::DisplayRole: { const auto cost = QIdentityProxyModel::data(index, Qt::DisplayRole).toDouble(); if (cost < 0.005) return QVariant(); return tr("%1 %").arg(qRound(cost * 100.0) / 100.0); } case Qt::BackgroundRole: { const auto cost = QIdentityProxyModel::data(index, Qt::DisplayRole).toDouble(); if (cost < 0.005) return QVariant(); const auto maxCost = QIdentityProxyModel::data(index.sibling(0, index.column()), PaintBufferModelRoles::MaxCostRole).toDouble(); if (maxCost == 0.0) return QVariant(); return QColor::fromHsv(120.0 * (1.0 - cost / maxCost), UiIntegration::hasDarkUI() ? 255 : 128, UiIntegration::hasDarkUI() ? 128 : 255); } } } return QIdentityProxyModel::data(index, role); } QVariant PaintBufferClientModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { switch (section) { case 0: return tr("Command"); case 1: return tr("Arguments"); case 2: return tr("Cost"); } } return QAbstractItemModel::headerData(section, orientation, role); } gammaray-2.11.3/ui/paintbufferclientmodel.h000066400000000000000000000032261413310443000207030ustar00rootroot00000000000000/* paintbufferclientmodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_PAINTBUFFERCLIENTMODEL_H #define GAMMARAY_PAINTBUFFERCLIENTMODEL_H #include namespace GammaRay { class PaintBufferClientModel : public QIdentityProxyModel { Q_OBJECT public: explicit PaintBufferClientModel(QObject *parent = nullptr); ~PaintBufferClientModel() override; QVariant data(const QModelIndex & index, int role) const override; QVariant headerData(int section, Qt::Orientation orientation, int role) const override; }; } #endif // GAMMARAY_PAINTBUFFERCLIENTMODEL_H gammaray-2.11.3/ui/paintbufferviewer.cpp000066400000000000000000000037551413310443000202470ustar00rootroot00000000000000/* paintbufferviewer.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2012-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "paintbufferviewer.h" #include "ui_paintbufferviewer.h" #include #include #include #include #include #include using namespace GammaRay; PaintBufferViewer::PaintBufferViewer(const QString &name, QWidget *parent) : QDialog(parent) , ui(new Ui::PaintBufferViewer) { ui->setupUi(this); setAttribute(Qt::WA_DeleteOnClose); setModal(true); ui->paintAnalyzerWidget->setBaseName(name); QSettings settings; settings.beginGroup(QStringLiteral("PaintBufferViewer")); restoreGeometry(settings.value(QStringLiteral("Geometry")).toByteArray()); } PaintBufferViewer::~PaintBufferViewer() { QSettings settings; settings.beginGroup(QStringLiteral("PaintBufferViewer")); settings.setValue(QStringLiteral("Geometry"), saveGeometry()); } gammaray-2.11.3/ui/paintbufferviewer.h000066400000000000000000000034051413310443000177040ustar00rootroot00000000000000/* paintbufferviewer.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2012-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_WIDGETINSPECTOR_PAINTBUFFERVIEWER_H #define GAMMARAY_WIDGETINSPECTOR_PAINTBUFFERVIEWER_H #include "gammaray_ui_export.h" #include QT_BEGIN_NAMESPACE class QAbstractItemModel; QT_END_NAMESPACE namespace GammaRay { class WidgetInspectorInterface; namespace Ui { class PaintBufferViewer; } /** * A widget to look at the command list in a QPaintBuffer. */ class GAMMARAY_UI_EXPORT PaintBufferViewer : public QDialog { Q_OBJECT public: explicit PaintBufferViewer(const QString &name, QWidget *parent = nullptr); ~PaintBufferViewer() override; private: QScopedPointer ui; }; } #endif gammaray-2.11.3/ui/paintbufferviewer.ui000066400000000000000000000034161413310443000200740ustar00rootroot00000000000000 GammaRay::PaintBufferViewer 0 0 400 300 Analyze Painting Qt::Horizontal QDialogButtonBox::Close GammaRay::PaintAnalyzerWidget QWidget
    paintanalyzerwidget.h
    1
    buttonBox accepted() GammaRay::PaintBufferViewer accept() 248 254 157 274 buttonBox rejected() GammaRay::PaintBufferViewer reject() 316 260 286 274
    gammaray-2.11.3/ui/palettemodel.cpp000066400000000000000000000125021413310443000171650ustar00rootroot00000000000000/* palettemodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2012-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "palettemodel.h" #include #include using namespace GammaRay; struct role_t { const char *name; QPalette::ColorRole role; }; static const role_t paletteRoles[] = { { "Window", QPalette::Window }, { "WindowText", QPalette::WindowText }, { "Base", QPalette::Base }, { "AlternateBase", QPalette::AlternateBase }, { "Text", QPalette::Text }, { "ToolTipBase", QPalette::ToolTipBase }, { "ToolTipText", QPalette::ToolTipText }, { "Button", QPalette::Button }, { "ButtonText", QPalette::ButtonText }, { "BrightText", QPalette::BrightText }, { "Light", QPalette::Light }, { "Midlight", QPalette::Midlight }, { "Dark", QPalette::Dark }, { "Mid", QPalette::Mid }, { "Shadow", QPalette::Shadow }, { "Highlight", QPalette::Highlight }, { "HighlightedText", QPalette::HighlightedText }, { "Link", QPalette::Link }, { "LinkVisited", QPalette::LinkVisited } }; struct group_t { const char *name; QPalette::ColorGroup group; }; static const group_t paletteGroups[] = { { "Active", QPalette::Active }, { "Inactive", QPalette::Inactive }, { "Disabled", QPalette::Disabled }, }; PaletteModel::PaletteModel(QObject *parent) : QAbstractTableModel(parent) , m_editable(false) { } QPalette PaletteModel::palette() const { return m_palette; } void PaletteModel::setPalette(const QPalette &palette) { beginResetModel(); m_palette = palette; endResetModel(); } void PaletteModel::setEditable(bool editable) { m_editable = editable; } QVariant PaletteModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); if (role == Qt::DisplayRole) { if (index.column() == 0) return paletteRoles[index.row()].name; return m_palette.color(paletteGroups[index.column()-1].group, paletteRoles[index.row()].role).name(); } else if (role == Qt::EditRole && index.column() > 0) { // TODO return QBrush once we have an editor for that return m_palette.color(paletteGroups[index.column()-1].group, paletteRoles[index.row()].role); } else if (role == Qt::DecorationRole && index.column() != 0) { const QBrush brush = m_palette.brush(paletteGroups[index.column()-1].group, paletteRoles[index.row()].role); QPixmap pixmap(32, 32); QPainter painter(&pixmap); painter.fillRect(pixmap.rect(), Qt::black); painter.fillRect(pixmap.rect().adjusted(1, 1, -1, -1), brush); return QIcon(pixmap); } return QVariant(); } bool PaletteModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (!m_editable) return false; if (index.isValid() && role == Qt::EditRole) { if (value.type() == QVariant::Color) { m_palette.setColor(paletteGroups[index.column()-1].group, paletteRoles[index.row()].role, value.value()); } else if (value.type() == QVariant::Brush) { m_palette.setBrush(paletteGroups[index.column()-1].group, paletteRoles[index.row()].role, value.value()); } } return QAbstractItemModel::setData(index, value, role); } int PaletteModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); return 1 + sizeof(paletteGroups) / sizeof(paletteGroups[0]); } int PaletteModel::rowCount(const QModelIndex &parent) const { if (parent.isValid()) return 0; return sizeof(paletteRoles) / sizeof(paletteRoles[0]); } QVariant PaletteModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { if (section == 0) return tr("Role"); return paletteGroups[section-1].name; } return QAbstractItemModel::headerData(section, orientation, role); } Qt::ItemFlags PaletteModel::flags(const QModelIndex &index) const { const Qt::ItemFlags baseFlags = QAbstractTableModel::flags(index); if (m_editable && index.column() > 0) return baseFlags | Qt::ItemIsEditable; return baseFlags; } gammaray-2.11.3/ui/palettemodel.h000066400000000000000000000043411413310443000166340ustar00rootroot00000000000000/* palettemodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2012-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_PALETTEMODEL_H #define GAMMARAY_PALETTEMODEL_H #include #include namespace GammaRay { /** * @brief Model showing the content of a QPalette. */ class PaletteModel : public QAbstractTableModel { Q_OBJECT public: explicit PaletteModel(QObject *parent = nullptr); QPalette palette() const; void setPalette(const QPalette &palette); void setEditable(bool editable); ///@cond override QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; Qt::ItemFlags flags(const QModelIndex &index) const override; bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; ///@endcond private: QPalette m_palette; bool m_editable; }; } #endif // GAMMARAY_PALETTEMODEL_H gammaray-2.11.3/ui/propertybinder.cpp000066400000000000000000000070461413310443000175650ustar00rootroot00000000000000/* propertybinder.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "propertybinder.h" #include #include #include using namespace GammaRay; PropertyBinder::PropertyBinder(QObject *source, QObject *destination) : QObject(source) , m_source(source) , m_destination(destination) , m_lock(false) { Q_ASSERT(source); Q_ASSERT(destination); } PropertyBinder::PropertyBinder(QObject *source, const char *sourceProp, QObject *destination, const char *destProp) : QObject(source) , m_source(source) , m_destination(destination) , m_lock(false) { Q_ASSERT(source); Q_ASSERT(destination); add(sourceProp, destProp); syncSourceToDestination(); } PropertyBinder::~PropertyBinder() = default; void PropertyBinder::add(const char *sourceProp, const char *destProp) { Q_ASSERT(sourceProp); Q_ASSERT(destProp); Binding b; const auto sourceIndex = m_source->metaObject()->indexOfProperty(sourceProp); b.sourceProperty = m_source->metaObject()->property(sourceIndex); Q_ASSERT(b.sourceProperty.isValid()); Q_ASSERT(b.sourceProperty.hasNotifySignal()); connect(m_source, QByteArray("2") + b.sourceProperty.notifySignal().methodSignature(), this, SLOT(syncSourceToDestination())); const auto destIndex = m_destination->metaObject()->indexOfProperty(destProp); b.destinationProperty = m_destination->metaObject()->property(destIndex); Q_ASSERT(b.destinationProperty.isValid()); Q_ASSERT(b.destinationProperty.isWritable()); m_properties.push_back(b); // notification for reverse direction changes, if present if (!b.destinationProperty.hasNotifySignal() || !b.sourceProperty.isWritable()) return; connect(m_destination, QByteArray("2") + b.destinationProperty.notifySignal().methodSignature(), this, SLOT(syncDestinationToSource())); } void PropertyBinder::syncSourceToDestination() { if (!m_destination || m_lock) return; m_lock = true; for (const auto &b : qAsConst(m_properties)) b.destinationProperty.write(m_destination, b.sourceProperty.read(m_source)); m_lock = false; } void PropertyBinder::syncDestinationToSource() { if (m_lock) return; m_lock = true; for (const auto &b : qAsConst(m_properties)) { if (!b.sourceProperty.isWritable()) continue; b.sourceProperty.write(m_source, b.destinationProperty.read(m_destination)); } m_lock = false; } gammaray-2.11.3/ui/propertybinder.h000066400000000000000000000060041413310443000172230ustar00rootroot00000000000000/* propertybinder.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_PROPERTYBINDER_H #define GAMMARAY_PROPERTYBINDER_H #include "gammaray_ui_export.h" #include #include #include #include namespace GammaRay { /** Helper class to bind two properties together, similar to QML. */ class GAMMARAY_UI_EXPORT PropertyBinder : public QObject { Q_OBJECT public: /** Creates a new PropertyBinder for syncing properties between @p source and @p destination. * No properties are synchronized by default, use add() to change this. * No initial synchronization is performed, called syncSourceToDestination() to change that. */ explicit PropertyBinder(QObject *source, QObject *destination); /** Keeps @p sourceProp of @p source in sync with @p destProp of @p destination. * At least the source property must have a change notification signal. * This is a convenience overload for syncing a single property pair, initial synchronization * from source to destination happens automatically. */ explicit PropertyBinder(QObject *source, const char *sourceProp, QObject *destination, const char *destProp); ~PropertyBinder() override; /** Adds another binding between @p sourceProp and @p destProp. * At least the source property must have a change notification signal. */ void add(const char *sourceProp, const char *destProp); public slots: /** Use this for initial/explicit source to destination synchronization. */ void syncSourceToDestination(); private slots: /** Same as the above, for the opposite direction. */ void syncDestinationToSource(); private: QObject *m_source; QPointer m_destination; struct Binding { QMetaProperty sourceProperty; QMetaProperty destinationProperty; }; QVector m_properties; bool m_lock; }; } #endif // GAMMARAY_PROPERTYBINDER_H gammaray-2.11.3/ui/propertyeditor/000077500000000000000000000000001413310443000170755ustar00rootroot00000000000000gammaray-2.11.3/ui/propertyeditor/palettedialog.cpp000066400000000000000000000045631413310443000224270ustar00rootroot00000000000000/* palettedialog.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2012-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "palettedialog.h" #include "ui_palettedialog.h" #include "palettemodel.h" #include "propertyeditor/propertyeditordelegate.h" #include #include using namespace GammaRay; PaletteDialog::PaletteDialog(const QPalette &palette, QWidget *parent) : QDialog(parent) , ui(new Ui::PaletteDialog) , m_stateManager(this) , m_model(new PaletteModel(this)) { ui->setupUi(this); m_model->setPalette(palette); m_model->setEditable(true); ui->paletteView->header()->setObjectName("paletteViewHeader"); ui->paletteView->setDeferredResizeMode(0, QHeaderView::Stretch); ui->paletteView->setDeferredResizeMode(1, QHeaderView::ResizeToContents); ui->paletteView->setDeferredResizeMode(2, QHeaderView::ResizeToContents); ui->paletteView->setDeferredResizeMode(3, QHeaderView::ResizeToContents); ui->paletteView->setModel(m_model); ui->paletteView->setItemDelegate(new PropertyEditorDelegate(this)); } PaletteDialog::~PaletteDialog() { delete ui; } QPalette PaletteDialog::editedPalette() const { return m_model->palette(); } void PaletteDialog::setEditable(bool editable) { m_model->setEditable(editable); ui->buttonBox->button(QDialogButtonBox::Save)->setEnabled(editable); } gammaray-2.11.3/ui/propertyeditor/palettedialog.h000066400000000000000000000033321413310443000220650ustar00rootroot00000000000000/* palettedialog.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2012-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_PALETTEDIALOG_H #define GAMMARAY_PALETTEDIALOG_H #include #include namespace GammaRay { class PropertyEditorFactory; class PaletteModel; namespace Ui { class PaletteDialog; } class PaletteDialog : public QDialog { Q_OBJECT public: explicit PaletteDialog(const QPalette &palette, QWidget *parent = nullptr); ~PaletteDialog() override; void setEditable(bool editable); QPalette editedPalette() const; private: Ui::PaletteDialog *ui; UIStateManager m_stateManager; PaletteModel *m_model; }; } #endif // GAMMARAY_PALETTEDIALOG_H gammaray-2.11.3/ui/propertyeditor/palettedialog.ui000066400000000000000000000036521413310443000222600ustar00rootroot00000000000000 GammaRay::PaletteDialog 0 0 400 300 false true false Qt::Horizontal QDialogButtonBox::Close|QDialogButtonBox::Save GammaRay::DeferredTreeView QTreeView
    ui/deferredtreeview.h
    buttonBox accepted() GammaRay::PaletteDialog accept() 248 254 157 274 buttonBox rejected() GammaRay::PaletteDialog reject() 316 260 286 274
    gammaray-2.11.3/ui/propertyeditor/propertycoloreditor.cpp000066400000000000000000000031701413310443000237340ustar00rootroot00000000000000/* propertycoloreditor.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "propertycoloreditor.h" #include using namespace GammaRay; PropertyColorEditor::PropertyColorEditor(QWidget *parent) : PropertyExtendedEditor(parent) { } void PropertyColorEditor::showEditor(QWidget* parent) { const QColor color = QColorDialog::getColor(value().value(), parent, QString(), QColorDialog::ShowAlphaChannel); if (color.isValid()) save(QVariant::fromValue(color)); emit editorClosed(); } gammaray-2.11.3/ui/propertyeditor/propertycoloreditor.h000066400000000000000000000030221413310443000233750ustar00rootroot00000000000000/* propertycoloreditor.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_PROPERTYCOLOREDITOR_H #define GAMMARAY_PROPERTYCOLOREDITOR_H #include "propertyextendededitor.h" namespace GammaRay { /** Property editor for QColor. */ class PropertyColorEditor : public PropertyExtendedEditor { Q_OBJECT public: explicit PropertyColorEditor(QWidget *parent = nullptr); void showEditor(QWidget * parent) override; }; } #endif // GAMMARAY_PROPERTYCOLOREDITOR_H gammaray-2.11.3/ui/propertyeditor/propertydoublepaireditor.cpp000066400000000000000000000041621413310443000247460ustar00rootroot00000000000000/* propertydoublepaireditor.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "propertydoublepaireditor.h" #include "ui_propertydoublepaireditor.h" using namespace GammaRay; PropertyDoublePairEditor::PropertyDoublePairEditor(QWidget *parent) : QWidget(parent) , ui(new Ui::PropertyDoublePairEditor) { ui->setupUi(this); } PropertyDoublePairEditor::~PropertyDoublePairEditor() = default; PropertyPointFEditor::PropertyPointFEditor(QWidget *parent) : PropertyDoublePairEditor(parent) { } QPointF PropertyPointFEditor::pointF() const { return {ui->xBox->value(), ui->yBox->value()}; } void PropertyPointFEditor::setPointF(const QPointF &point) { ui->xBox->setValue(point.x()); ui->yBox->setValue(point.y()); } PropertySizeFEditor::PropertySizeFEditor(QWidget *parent) : PropertyDoublePairEditor(parent) { } QSizeF PropertySizeFEditor::sizeF() const { return {ui->xBox->value(), ui->yBox->value()}; } void PropertySizeFEditor::setSizeF(const QSizeF &size) { ui->xBox->setValue(size.width()); ui->yBox->setValue(size.height()); } gammaray-2.11.3/ui/propertyeditor/propertydoublepaireditor.h000066400000000000000000000043301413310443000244100ustar00rootroot00000000000000/* propertydoublepaireditor.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_PROPERTYDOUBLEPAIREDITOR_H #define GAMMARAY_PROPERTYDOUBLEPAIREDITOR_H #include namespace GammaRay { namespace Ui { class PropertyDoublePairEditor; } /** Property editor for pairs of doubles, such as PointF and SizeF. */ class PropertyDoublePairEditor : public QWidget { Q_OBJECT public: explicit PropertyDoublePairEditor(QWidget *parent = nullptr); ~PropertyDoublePairEditor() override; protected: QScopedPointer ui; }; class PropertyPointFEditor : public PropertyDoublePairEditor { Q_OBJECT Q_PROPERTY(QPointF pointF READ pointF WRITE setPointF USER true) public: explicit PropertyPointFEditor(QWidget *parent = nullptr); QPointF pointF() const; void setPointF(const QPointF &point); }; class PropertySizeFEditor : public PropertyDoublePairEditor { Q_OBJECT Q_PROPERTY(QSizeF sizeF READ sizeF WRITE setSizeF USER true) public: explicit PropertySizeFEditor(QWidget *parent = nullptr); QSizeF sizeF() const; void setSizeF(const QSizeF &size); }; } #endif // GAMMARAY_PROPERTYDOUBLEPAIREDITOR_H gammaray-2.11.3/ui/propertyeditor/propertydoublepaireditor.ui000066400000000000000000000026361413310443000246050ustar00rootroot00000000000000 GammaRay::PropertyDoublePairEditor 0 0 400 300 0 false -999999999.000000000000000 999999999.000000000000000 x Qt::AlignCenter false -999999999.000000000000000 999999999.000000000000000 gammaray-2.11.3/ui/propertyeditor/propertyeditordelegate.cpp000066400000000000000000000314201413310443000243670ustar00rootroot00000000000000/* propertyeditordelegate.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "propertyeditordelegate.h" #include "propertyeditorfactory.h" #include "propertyextendededitor.h" #include #include #include #include #include #include #include #include #include #include using namespace GammaRay; namespace { template struct matrix_trait {}; template<> struct matrix_trait { static const int rows = 3; static const int columns = 2; static qreal value(const QMatrix &matrix, int r, int c) { switch (r << 4 | c) { case 0x00: return matrix.m11(); case 0x01: return matrix.m12(); case 0x10: return matrix.m21(); case 0x11: return matrix.m22(); case 0x20: return matrix.dx(); case 0x21: return matrix.dy(); } Q_ASSERT(false); return 0.0; } }; template<> struct matrix_trait { static const int rows = 4; static const int columns = 4; static qreal value(const QMatrix4x4 &matrix, int r, int c) { return matrix(r, c); } }; template<> struct matrix_trait { static const int rows = 3; static const int columns = 3; static qreal value(const QTransform &matrix, int r, int c) { switch (r << 4 | c) { case 0x00: return matrix.m11(); case 0x01: return matrix.m12(); case 0x02: return matrix.m13(); case 0x10: return matrix.m21(); case 0x11: return matrix.m22(); case 0x12: return matrix.m23(); case 0x20: return matrix.m31(); case 0x21: return matrix.m32(); case 0x22: return matrix.m33(); } Q_ASSERT(false); return 0.0; } }; template<> struct matrix_trait { static const int rows = 2; static const int columns = 1; static qreal value(const QVector2D &vec, int r, int) { return vec[r]; } }; template<> struct matrix_trait { static const int rows = 3; static const int columns = 1; static qreal value(const QVector3D &vec, int r, int) { return vec[r]; } }; template<> struct matrix_trait { static const int rows = 4; static const int columns = 1; static qreal value(const QVector4D &vec, int r, int) { return vec[r]; } }; template<> struct matrix_trait { static const int rows = 3; static const int columns = 1; static qreal value(const QQuaternion &quaternion, int r, int) { float pitch, yaw, roll; quaternion.getEulerAngles(&pitch, &yaw, &roll); switch (r) { case 0: return pitch; case 1: return yaw; case 2: return roll; } Q_ASSERT(false); return 0.0; } }; } PropertyEditorDelegate::PropertyEditorDelegate(QObject *parent) : QStyledItemDelegate(parent) { setItemEditorFactory(PropertyEditorFactory::instance()); } PropertyEditorDelegate::~PropertyEditorDelegate() = default; void PropertyEditorDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { editor->setProperty("displayString", index.data(Qt::DisplayRole)); QStyledItemDelegate::setEditorData(editor, index); } void PropertyEditorDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { const QVariant value = index.data(Qt::EditRole); if (value.canConvert()) { paint(painter, option, index, value.value()); } else if (value.canConvert()) { paint(painter, option, index, value.value()); } else if (value.type() == QVariant::Transform) { paint(painter, option, index, value.value()); } else if (value.canConvert()) { paint(painter, option, index, value.value()); } else if (value.canConvert()) { paint(painter, option, index, value.value()); } else if (value.canConvert()) { paint(painter, option, index, value.value()); } else if (value.type() == QVariant::Quaternion) { paint(painter, option, index, value.value()); } else { QStyledItemDelegate::paint(painter, option, index); } } QSize PropertyEditorDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const { const QVariant value = index.data(Qt::EditRole); if (value.canConvert()) { return sizeHint(option, index, value.value()); } else if (value.canConvert()) { return sizeHint(option, index, value.value()); } else if (value.type() == QVariant::Transform) { return sizeHint(option, index, value.value()); } else if (value.canConvert()) { return sizeHint(option, index, value.value()); } else if (value.canConvert()) { return sizeHint(option, index, value.value()); } else if (value.canConvert()) { return sizeHint(option, index, value.value()); } else if (value.type() == QVariant::Quaternion) { return sizeHint(option, index, value.value()); } // We don't want multiline texts for String values if (value.type() == QVariant::String || value.type() == QVariant::ByteArray) { QStyleOptionViewItem opt = option; QSize sh = QStyledItemDelegate::sizeHint(opt, index); initStyleOption(&opt, index); return sh.boundedTo(QSize(sh.width(), opt.fontMetrics.height())); } return QStyledItemDelegate::sizeHint(option, index); } template void PropertyEditorDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index, const Matrix &matrix) const { QStyleOptionViewItem opt = option; initStyleOption(&opt, index); opt.text.clear(); QApplication::style()->drawControl(QStyle::CE_ItemViewItem, &opt, painter, opt.widget); QRect textRect = QApplication::style()->subElementRect(QStyle::SE_ItemViewItemText, &opt, opt.widget); const int textHMargin = QApplication::style()->pixelMetric(QStyle::PM_FocusFrameHMargin, nullptr, opt.widget) + 1; static const int textVMargin = 1; textRect = textRect.adjusted(textHMargin, textVMargin, -textHMargin, -textVMargin); static const int parenthesisLineWidth = 1; const int matrixSpacing = opt.fontMetrics.width(QStringLiteral("x")); const int matrixHMargin = matrixSpacing / 2; const int parenthesisWidth = qMax(matrixHMargin, 3); painter->save(); painter->setClipRect(textRect); painter->translate(textRect.topLeft()); painter->setPen(opt.palette.color(opt.state & QStyle::State_Selected ? QPalette::HighlightedText : QPalette::Text)); int xOffset = 0; painter->drawLine(xOffset, 0, xOffset, textRect.height()); painter->drawLine(xOffset, 0, xOffset + parenthesisWidth, 0); painter->drawLine(xOffset, textRect.height() - 1, xOffset + parenthesisWidth, textRect.height() - 1); xOffset += matrixHMargin + parenthesisLineWidth; for (int col = 0; col < matrix_trait::columns; ++col) { const int colWidth = columnWidth(opt, matrix, col); for (int row = 0; row < matrix_trait::rows; ++row) { const QRect r(xOffset, row * opt.fontMetrics.lineSpacing(), colWidth, opt.fontMetrics.lineSpacing()); painter->drawText(r, Qt::AlignHCenter | Qt::AlignRight, QString::number(matrix_trait::value(matrix, row, col))); } xOffset += colWidth + matrixSpacing; } xOffset += -matrixSpacing + matrixHMargin; painter->drawLine(xOffset, 0, xOffset, textRect.height()); painter->drawLine(xOffset, 0, xOffset - parenthesisWidth, 0); painter->drawLine(xOffset, textRect.height() - 1, xOffset - parenthesisWidth, textRect.height() - 1); painter->restore(); } template QSize PropertyEditorDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index, const Matrix &matrix) const { QStyleOptionViewItem opt = option; initStyleOption(&opt, index); static const int parenthesisLineWidth = 1; const int textHMargin = QApplication::style()->pixelMetric(QStyle::PM_FocusFrameHMargin, nullptr, opt.widget) + 1; static const int textVMargin = 1; int width = 0; for (int col = 0; col < matrix_trait::columns; ++col) { width += columnWidth(opt, matrix, col); } width += opt.fontMetrics.width(QStringLiteral("x")) * matrix_trait::columns + 2 * parenthesisLineWidth + 2 * textHMargin; const int height = opt.fontMetrics.lineSpacing() * matrix_trait::rows + 2* textVMargin; return {width, height}; } template int PropertyEditorDelegate::columnWidth(const QStyleOptionViewItem &option, const Matrix &matrix, int column) const { int width = 0; for (int row = 0; row < matrix_trait::rows; ++row) { width = qMax(width, option.fontMetrics.width( QString::number(matrix_trait::value(matrix, row, column)))); } return width; } bool PropertyEditorDelegate::editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index) { // if this is a read-only cell containing a complex type we have a suitable editor for, we'll show that in read-only mode if (index.isValid() && event->type() == QEvent::MouseButtonDblClick && ((index.flags() & Qt::ItemIsEditable) == 0) && (index.flags() & Qt::ItemIsEnabled)) { const auto value = index.data(Qt::EditRole); if (value.isNull() || !PropertyEditorFactory::hasExtendedEditor(value.userType())) return QStyledItemDelegate::editorEvent(event, model, option, index); // special cases for strings, short ones don't benefit from the external editor if (value.type() == QVariant::String && !value.toString().contains(QLatin1Char('\n'))) return QStyledItemDelegate::editorEvent(event, model, option, index); else if (value.type() == QVariant::ByteArray && !value.toByteArray().contains('\n')) return QStyledItemDelegate::editorEvent(event, model, option, index); const auto editor = qobject_cast(PropertyEditorFactory::instance()->createEditor(value.userType(), nullptr)); if (editor) { editor->setReadOnly(true); editor->setValue(value); connect(editor, &PropertyExtendedEditor::editorClosed, editor, &QObject::deleteLater); editor->showEditor(const_cast(option.widget)); } } return QStyledItemDelegate::editorEvent(event, model, option, index); } QString PropertyEditorDelegate::displayText(const QVariant& value, const QLocale& locale) const { if (value.userType() == qMetaTypeId()) return value.value().displayString(); return QStyledItemDelegate::displayText(value, locale); } gammaray-2.11.3/ui/propertyeditor/propertyeditordelegate.h000066400000000000000000000061131413310443000240350ustar00rootroot00000000000000/* propertyeditordelegate.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_PROPERTYEDITORDELEGATE_H #define GAMMARAY_PROPERTYEDITORDELEGATE_H #include "gammaray_ui_export.h" #include QT_BEGIN_NAMESPACE class QMatrix4x4; QT_END_NAMESPACE namespace GammaRay { /*! Delegate for rendering and editing QVariant property values. * Compared to the built-in functionality this provides support for additional types, * such as: * - rendering of vector and matrix types in their mathematical form * - editing of flags and enums resolved via EnumRepository * - editing support mathematical types, palettes, fonts, colors, etc. * * It's recommended to use this on any cell containing target-provided QVariants, * no matter whether it's read-only or read-write. */ class GAMMARAY_UI_EXPORT PropertyEditorDelegate : public QStyledItemDelegate { Q_OBJECT public: explicit PropertyEditorDelegate(QObject *parent); ~PropertyEditorDelegate() override; void setEditorData(QWidget *editor, const QModelIndex &index) const override; void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override; bool editorEvent(QEvent * event, QAbstractItemModel * model, const QStyleOptionViewItem & option, const QModelIndex & index) override; QString displayText(const QVariant & value, const QLocale & locale) const override; private: template void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index, const Matrix &matrix) const; template QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index, const Matrix &matrix) const; template int columnWidth(const QStyleOptionViewItem &option, const Matrix &matrix, int column) const; }; } #endif // GAMMARAY_PROPERTYEDITORDELEGATE_H gammaray-2.11.3/ui/propertyeditor/propertyeditorfactory.cpp000066400000000000000000000115031413310443000242640ustar00rootroot00000000000000/* propertyeditorfactory.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "propertyeditorfactory.h" #include "propertycoloreditor.h" #include "propertyenumeditor.h" #include "propertyfonteditor.h" #include "propertyintpaireditor.h" #include "propertydoublepaireditor.h" #include "propertypaletteeditor.h" #include "propertymatrixeditor.h" #include "propertytexteditor.h" #include "propertyrecteditor.h" #include #include using namespace GammaRay; PropertyEditorFactory::PropertyEditorFactory() { initBuiltInTypes(); addEditor(QVariant::Color, new QStandardItemEditorCreator()); addEditor(QVariant::ByteArray, new QStandardItemEditorCreator(), true); addEditor(QVariant::Font, new QStandardItemEditorCreator()); addEditor(QVariant::Palette, new QStandardItemEditorCreator(), true); addEditor(QVariant::Point, new QStandardItemEditorCreator()); addEditor(QVariant::PointF, new QStandardItemEditorCreator()); addEditor(QVariant::Rect, new QStandardItemEditorCreator()); addEditor(QVariant::RectF, new QStandardItemEditorCreator()); addEditor(QVariant::Size, new QStandardItemEditorCreator()); addEditor(QVariant::SizeF, new QStandardItemEditorCreator()); addEditor(QVariant::String, new QStandardItemEditorCreator(), true); addEditor(QVariant::Transform, new QStandardItemEditorCreator()); addEditor(QVariant::Matrix, new QStandardItemEditorCreator()); addEditor(QVariant::Matrix4x4, new QStandardItemEditorCreator()); addEditor(QVariant::Vector2D, new QStandardItemEditorCreator()); addEditor(QVariant::Vector3D, new QStandardItemEditorCreator()); addEditor(QVariant::Vector4D, new QStandardItemEditorCreator()); addEditor(QVariant::Quaternion, new QStandardItemEditorCreator()); registerEditor(static_cast(qMetaTypeId()), new QStandardItemEditorCreator()); } PropertyEditorFactory *PropertyEditorFactory::instance() { static auto *s_instance = new PropertyEditorFactory; return s_instance; } QWidget *PropertyEditorFactory::createEditor(TypeId type, QWidget *parent) const { if (type == QMetaType::Float) { /* coverity[mixed_enums] */ type = QVariant::Double; } QWidget *w = QItemEditorFactory::createEditor(type, parent); if (!w) return nullptr; // the read-only view is still in the background usually, so transparency is not a good choice here w->setAutoFillBackground(true); return w; } QVector< int > PropertyEditorFactory::supportedTypes() { return instance()->m_supportedTypes; } void PropertyEditorFactory::initBuiltInTypes() { m_supportedTypes << QVariant::Bool << QVariant::Double << QVariant::Int << QVariant::UInt << QVariant::Date << QVariant::DateTime << QVariant::Time; m_supportedTypes << QMetaType::Float; } void PropertyEditorFactory::addEditor(PropertyEditorFactory::TypeId type, QItemEditorCreatorBase *creator, bool extended) { registerEditor(type, creator); m_supportedTypes.push_back(type); if (extended) { auto it = std::lower_bound(m_extendedTypes.begin(), m_extendedTypes.end(), type); m_extendedTypes.insert(it, type); } } bool PropertyEditorFactory::hasExtendedEditor(int typeId) { return std::binary_search(instance()->m_extendedTypes.constBegin(), instance()->m_extendedTypes.constEnd(), typeId); } gammaray-2.11.3/ui/propertyeditor/propertyeditorfactory.h000066400000000000000000000040441413310443000237330ustar00rootroot00000000000000/* propertyeditorfactory.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_PROPERTYEDITORFACTORY_H #define GAMMARAY_PROPERTYEDITORFACTORY_H #include "gammaray_ui_export.h" #include #include namespace GammaRay { /** Item editor factory with support for extra types while keeping support for the built-in ones. */ class GAMMARAY_UI_EXPORT PropertyEditorFactory : public QItemEditorFactory { public: static PropertyEditorFactory *instance(); typedef int TypeId; QWidget *createEditor(TypeId type, QWidget *parent) const override; static QVector supportedTypes(); static bool hasExtendedEditor(int typeId); protected: PropertyEditorFactory(); private: Q_DISABLE_COPY(PropertyEditorFactory) void initBuiltInTypes(); void addEditor(TypeId type, QItemEditorCreatorBase *creator, bool extended = false); QVector m_supportedTypes; QVector m_extendedTypes; }; } #endif // GAMMARAY_PROPERTYEDITORFACTORY_H gammaray-2.11.3/ui/propertyeditor/propertyenumeditor.cpp000066400000000000000000000202561413310443000235660ustar00rootroot00000000000000/* propertyenumeditor.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "propertyenumeditor.h" #include #include using namespace GammaRay; #include #include #include #include #include namespace GammaRay { class PropertyEnumEditorModel : public QAbstractListModel { Q_OBJECT public: explicit PropertyEnumEditorModel(QObject *parent = nullptr); ~PropertyEnumEditorModel() override; EnumValue value() const; void setValue(const EnumValue &value); void updateValue(int value); EnumDefinition definition() const; void setDefinition(const EnumDefinition &def); int rowCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; Qt::ItemFlags flags(const QModelIndex &index) const override; bool setData(const QModelIndex &index, const QVariant &value, int role) override; private: EnumValue m_value; EnumDefinition m_def; }; } PropertyEnumEditorModel::PropertyEnumEditorModel(QObject *parent) : QAbstractListModel(parent) { } PropertyEnumEditorModel::~PropertyEnumEditorModel() = default; EnumValue PropertyEnumEditorModel::value() const { return m_value; } void PropertyEnumEditorModel::setValue(const EnumValue &value) { beginResetModel(); m_value = value; auto repo = ObjectBroker::object(); m_def = repo->definition(value.id()); endResetModel(); } void PropertyEnumEditorModel::updateValue(int value) { Q_ASSERT(m_value.isValid()); Q_ASSERT(m_def.isValid()); m_value.setValue(value); } EnumDefinition PropertyEnumEditorModel::definition() const { return m_def; } void PropertyEnumEditorModel::setDefinition(const EnumDefinition &def) { beginResetModel(); m_def = def; endResetModel(); } int PropertyEnumEditorModel::rowCount(const QModelIndex &parent) const { if (parent.isValid()) return 0; return m_def.elements().size(); } QVariant PropertyEnumEditorModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); if (role == Qt::DisplayRole) { return m_def.elements().at(index.row()).name(); } else if (role == Qt::CheckStateRole && m_def.isFlag()) { const auto elem = m_def.elements().at(index.row()); if (elem.value() == 0) return m_value.value() == 0 ? Qt::Checked : Qt::Unchecked; return (elem.value() & m_value.value()) == elem.value() ? Qt::Checked : Qt::Unchecked; } return QVariant(); } Qt::ItemFlags PropertyEnumEditorModel::flags(const QModelIndex &index) const { const auto f = QAbstractListModel::flags(index); if (index.isValid() && m_def.isFlag() && m_def.elements().at(index.row()).value() != 0) // 0 values can't be toggled return f | Qt::ItemIsUserCheckable; return f; } bool PropertyEnumEditorModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (!index.isValid() || !m_def.isFlag()) return false; if (role == Qt::CheckStateRole) { const auto elem = m_def.elements().at(index.row()); if (value.toInt() == Qt::Checked) m_value.setValue(m_value.value() | elem.value()); else if (value.toInt() == Qt::Unchecked) m_value.setValue(m_value.value() & ~elem.value()); emit dataChanged(this->index(0,0), this->index(rowCount() - 1, 0)); // mask flags can change multiple rows return true; } return QAbstractListModel::setData(index, value, role); } PropertyEnumEditor::PropertyEnumEditor(QWidget* parent) : QComboBox(parent), m_model(new PropertyEnumEditorModel(this)) { setModel(m_model); connect(m_model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(update())); // FIXME: Clazy says 4 overloads for update, but I find not a single one... auto repo = ObjectBroker::object(); connect(repo, &EnumRepository::definitionChanged, this, &PropertyEnumEditor::definitionChanged); setEnabled(false); connect(this, static_cast(&QComboBox::currentIndexChanged), this, &PropertyEnumEditor::slotCurrentIndexChanged); } PropertyEnumEditor::~PropertyEnumEditor() = default; EnumValue PropertyEnumEditor::enumValue() const { return m_model->value(); } void PropertyEnumEditor::setEnumValue(const EnumValue& value) { m_model->setValue(value); updateCurrentIndex(); setupView(); } void PropertyEnumEditor::definitionChanged(int id) { if (!m_model->value().isValid() || id != m_model->value().id()) return; auto repo = ObjectBroker::object(); const auto def = repo->definition(id); m_model->setDefinition(def); updateCurrentIndex(); setupView(); } void PropertyEnumEditor::updateCurrentIndex() { const auto def = m_model->definition(); if (!def.isValid() || !m_model->value().isValid()) return; if (!def.isFlag()) { for (int i = 0; i < def.elements().size(); ++i) { if (def.elements().at(i).value() == m_model->value().value()) { setCurrentIndex(i); break; } } } } void PropertyEnumEditor::setupView() { const auto def = m_model->definition(); if (!def.isValid()) return; setEnabled(true); if (def.isFlag() && view()->metaObject() != &QListView::staticMetaObject) { // the default view doesn't show check boxes (at least with the Fusion style) auto v = new QListView(this); setView(v); v->installEventFilter(this); v->viewport()->installEventFilter(this); } } void PropertyEnumEditor::slotCurrentIndexChanged(int index) { const auto def = m_model->definition(); if (!def.isValid() || def.isFlag() || index < 0) return; m_model->updateValue(def.elements().at(index).value()); } void PropertyEnumEditor::paintEvent(QPaintEvent* event) { const auto def = m_model->definition(); if (def.isValid() && !def.isFlag()) { QComboBox::paintEvent(event); return; } // for flags we paint the combined value name as text QStylePainter painter(this); painter.setPen(palette().color(QPalette::Text)); QStyleOptionComboBox opt; initStyleOption(&opt); if (!def.isValid()) opt.currentText = tr("Loading..."); else opt.currentText = def.valueToString(m_model->value()); painter.drawComplexControl(QStyle::CC_ComboBox, opt); painter.drawControl(QStyle::CE_ComboBoxLabel, opt); } bool PropertyEnumEditor::eventFilter(QObject *watched, QEvent *event) { if (watched == view() || watched == view()->viewport()) { if (event->type() == QEvent::MouseButtonRelease && m_model->definition().isFlag()) { const auto state = view()->currentIndex().data(Qt::CheckStateRole).toInt(); m_model->setData(view()->currentIndex(), state == Qt::Checked ? Qt::Unchecked : Qt::Checked, Qt::CheckStateRole); return true; } } return QComboBox::eventFilter(watched, event); } #include "propertyenumeditor.moc" gammaray-2.11.3/ui/propertyeditor/propertyenumeditor.h000066400000000000000000000037611413310443000232350ustar00rootroot00000000000000/* propertyenumeditor.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_PROPERTYENUMEDITOR_H #define GAMMARAY_PROPERTYENUMEDITOR_H #include #include namespace GammaRay { class PropertyEnumEditorModel; class PropertyEnumEditor : public QComboBox { Q_OBJECT Q_PROPERTY(GammaRay::EnumValue enumValue READ enumValue WRITE setEnumValue USER true) public: explicit PropertyEnumEditor(QWidget *parent = nullptr); ~PropertyEnumEditor() override; EnumValue enumValue() const; void setEnumValue(const EnumValue &value); protected: void paintEvent(QPaintEvent *event) override; bool eventFilter(QObject *watched, QEvent *event) override; private slots: void definitionChanged(int id); void slotCurrentIndexChanged(int index); private: void updateCurrentIndex(); void setupView(); PropertyEnumEditorModel *m_model; }; } #endif // GAMMARAY_PROPERTYENUMEDITOR_H gammaray-2.11.3/ui/propertyeditor/propertyextendededitor.cpp000066400000000000000000000060051413310443000244160ustar00rootroot00000000000000/* propertyextendededitor.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "propertyextendededitor.h" #include "ui_propertyextendededitor.h" #include #include #include using namespace GammaRay; PropertyExtendedEditor::PropertyExtendedEditor(QWidget *parent) : QWidget(parent) , ui(new Ui::PropertyExtendedEditor) { ui->setupUi(this); setInlineEditable(false); setReadOnly(false); // TODO: make button content smaller by using a tiny icon connect(ui->editButton, &QAbstractButton::clicked, this, &PropertyExtendedEditor::slotEdit); } PropertyExtendedEditor::~PropertyExtendedEditor() = default; QVariant PropertyExtendedEditor::value() const { if (isInlineEditable()) return ui->lineEdit->text(); return m_value; } void PropertyExtendedEditor::setValue(const QVariant &value) { m_value = value; const QString displayValue = property("displayString").toString(); ui->lineEdit->setText(displayValue.isEmpty() ? value.toString() : displayValue); } void PropertyExtendedEditor::save(const QVariant &value) { if (isReadOnly()) return; setValue(value); // The user already pressed Apply, don't force her/him to do again QKeyEvent event(QEvent::KeyPress, Qt::Key_Enter, Qt::NoModifier); QApplication::sendEvent(this, &event); } bool PropertyExtendedEditor::isInlineEditable() const { return m_inlineEditable; } void PropertyExtendedEditor::setInlineEditable(bool editable) { m_inlineEditable = editable; ui->lineEdit->setReadOnly(!isInlineEditable()); if (editable) setFocusProxy(ui->lineEdit); else setFocusProxy(ui->editButton); ui->lineEdit->setFrame(editable); } bool PropertyExtendedEditor::isReadOnly() const { return m_readOnly; } void PropertyExtendedEditor::setReadOnly(bool readOnly) { m_readOnly = readOnly; setInlineEditable(false); } void PropertyExtendedEditor::slotEdit() { showEditor(this); } gammaray-2.11.3/ui/propertyeditor/propertyextendededitor.h000066400000000000000000000042721413310443000240670ustar00rootroot00000000000000/* propertyextendededitor.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_PROPERTYEXTENDEDEDITOR_H #define GAMMARAY_PROPERTYEXTENDEDEDITOR_H #include #include #include namespace GammaRay { namespace Ui { class PropertyExtendedEditor; } /** Base class for property editors that open a separate dialog. */ class PropertyExtendedEditor : public QWidget { Q_OBJECT Q_PROPERTY(QVariant value READ value WRITE setValue USER true) public: explicit PropertyExtendedEditor(QWidget *parent = nullptr); ~PropertyExtendedEditor(); QVariant value() const; void setValue(const QVariant &value); bool isReadOnly() const; void setReadOnly(bool readOnly); virtual void showEditor(QWidget *parent) = 0; Q_SIGNALS: void editorClosed(); protected: bool isInlineEditable() const; void setInlineEditable(bool editable); void save(const QVariant &value); private slots: void slotEdit(); private: std::unique_ptr ui; QVariant m_value; bool m_inlineEditable; bool m_readOnly; }; } #endif // GAMMARAY_PROPERTYEXTENDEDEDITOR_H gammaray-2.11.3/ui/propertyeditor/propertyextendededitor.ui000066400000000000000000000016451413310443000242560ustar00rootroot00000000000000 GammaRay::PropertyExtendedEditor 0 0 392 28 0 0 0 0 ... gammaray-2.11.3/ui/propertyeditor/propertyfonteditor.cpp000066400000000000000000000030111413310443000235560ustar00rootroot00000000000000/* propertyfonteditor.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "propertyfonteditor.h" #include using namespace GammaRay; PropertyFontEditor::PropertyFontEditor(QWidget *parent) : PropertyExtendedEditor(parent) { } void PropertyFontEditor::showEditor(QWidget* parent) { bool ok = false; const QFont font = QFontDialog::getFont(&ok, value().value(), parent); if (ok) save(font); emit editorClosed(); } gammaray-2.11.3/ui/propertyeditor/propertyfonteditor.h000066400000000000000000000027511413310443000232350ustar00rootroot00000000000000/* propertyfonteditor.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_PROPERTYFONTEDITOR_H #define GAMMARAY_PROPERTYFONTEDITOR_H #include "propertyextendededitor.h" namespace GammaRay { class PropertyFontEditor : public PropertyExtendedEditor { Q_OBJECT public: explicit PropertyFontEditor(QWidget *parent = nullptr); void showEditor(QWidget * parent) override; }; } #endif // GAMMARAY_PROPERTYFONTEDITOR_H gammaray-2.11.3/ui/propertyeditor/propertyintpaireditor.cpp000066400000000000000000000045371413310443000242740ustar00rootroot00000000000000/* propertyintpaireditor.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "propertyintpaireditor.h" #include "ui_propertyintpaireditor.h" #include #include using namespace GammaRay; PropertyIntPairEditor::PropertyIntPairEditor(QWidget *parent) : QWidget(parent) , ui(new Ui::PropertyIntPairEditor) { ui->setupUi(this); ui->xBox->setMinimum(std::numeric_limits::min()); ui->xBox->setMaximum(std::numeric_limits::max()); ui->yBox->setMinimum(std::numeric_limits::min()); ui->yBox->setMaximum(std::numeric_limits::max()); } PropertyIntPairEditor::~PropertyIntPairEditor() = default; PropertyPointEditor::PropertyPointEditor(QWidget *parent) : PropertyIntPairEditor(parent) { } QPoint PropertyPointEditor::point() const { return {ui->xBox->value(), ui->yBox->value()}; } void PropertyPointEditor::setPoint(const QPoint &point) { ui->xBox->setValue(point.x()); ui->yBox->setValue(point.y()); } PropertySizeEditor::PropertySizeEditor(QWidget *parent) : PropertyIntPairEditor(parent) { } QSize PropertySizeEditor::sizeValue() const { return {ui->xBox->value(), ui->yBox->value()}; } void PropertySizeEditor::setSizeValue(const QSize &size) { ui->xBox->setValue(size.width()); ui->yBox->setValue(size.height()); } gammaray-2.11.3/ui/propertyeditor/propertyintpaireditor.h000066400000000000000000000046771413310443000237460ustar00rootroot00000000000000/* propertyintpaireditor.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_PROPERTYINTPAIREDITOR_H #define GAMMARAY_PROPERTYINTPAIREDITOR_H #include QT_BEGIN_NAMESPACE class QPoint; QT_END_NAMESPACE namespace GammaRay { namespace Ui { class PropertyIntPairEditor; } /** Property editor for QPoint, QSize and anything else consisting of two integer values. */ class PropertyIntPairEditor : public QWidget { Q_OBJECT public: explicit PropertyIntPairEditor(QWidget *parent = nullptr); ~PropertyIntPairEditor() override; protected: QScopedPointer ui; }; /** Property editor for points. Since QStyledItemDelegate ignore valuePropertyName and insists on * USER properties we need one class per type here... */ class PropertyPointEditor : public PropertyIntPairEditor { Q_OBJECT Q_PROPERTY(QPoint point READ point WRITE setPoint USER true) public: explicit PropertyPointEditor(QWidget *parent = nullptr); QPoint point() const; void setPoint(const QPoint &point); }; /** Same again for size. */ class PropertySizeEditor : public PropertyIntPairEditor { Q_OBJECT Q_PROPERTY(QSize sizeValue READ sizeValue WRITE setSizeValue USER true) public: explicit PropertySizeEditor(QWidget *parent = nullptr); QSize sizeValue() const; void setSizeValue(const QSize &size); }; } #endif // GAMMARAY_PROPERTYINTPAIREDITOR_H gammaray-2.11.3/ui/propertyeditor/propertyintpaireditor.ui000066400000000000000000000022401413310443000241140ustar00rootroot00000000000000 GammaRay::PropertyIntPairEditor 0 0 145 22 0 false -99999 99999 x Qt::AlignCenter false gammaray-2.11.3/ui/propertyeditor/propertymatrixdialog.cpp000066400000000000000000000050111413310443000240670ustar00rootroot00000000000000/* propertymatrixdialog.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Tobias Koenig Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "propertymatrixdialog.h" #include "ui_propertymatrixdialog.h" #include "propertymatrixmodel.h" using namespace GammaRay; PropertyMatrixDialog::PropertyMatrixDialog(QWidget *parent) : QDialog(parent) , ui(new Ui::PropertyMatrixDialog) , m_model(new PropertyMatrixModel(this)) { ui->setupUi(this); ui->tableView->setModel(m_model); } PropertyMatrixDialog::~PropertyMatrixDialog() { delete ui; } void PropertyMatrixDialog::setMatrix(const QVariant &matrix) { m_model->setMatrix(matrix); QString windowTitle = tr("Edit Matrix"); switch (matrix.type()) { case QVariant::Matrix: windowTitle = tr("Edit Matrix"); break; case QVariant::Matrix4x4: windowTitle = tr("Edit 4x4 Matrix"); break; case QVariant::Transform: windowTitle = tr("Edit Transform"); break; case QVariant::Vector2D: windowTitle = tr("Edit 2D Vector"); break; case QVariant::Vector3D: windowTitle = tr("Edit 3D Vector"); break; case QVariant::Vector4D: windowTitle = tr("Edit 4D Vector"); break; case QVariant::Quaternion: windowTitle = tr("Edit Quaternion"); break; default: windowTitle = tr("Edit Unsupported Type"); break; } setWindowTitle(windowTitle); } QVariant PropertyMatrixDialog::matrix() const { return m_model->matrix(); } gammaray-2.11.3/ui/propertyeditor/propertymatrixdialog.h000066400000000000000000000032561413310443000235450ustar00rootroot00000000000000/* propertymatrixdialog.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Tobias Koenig Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_PROPERTYMATRIXDIALOG_H #define GAMMARAY_PROPERTYMATRIXDIALOG_H #include namespace GammaRay { namespace Ui { class PropertyMatrixDialog; } class PropertyMatrixModel; class PropertyMatrixDialog : public QDialog { Q_OBJECT public: explicit PropertyMatrixDialog(QWidget *parent = nullptr); ~PropertyMatrixDialog() override; void setMatrix(const QVariant &matrix); QVariant matrix() const; private: Ui::PropertyMatrixDialog *ui; PropertyMatrixModel *m_model; }; } #endif // GAMMARAY_PROPERTYMATRIXDIALOG_H gammaray-2.11.3/ui/propertyeditor/propertymatrixdialog.ui000066400000000000000000000027301413310443000237270ustar00rootroot00000000000000 GammaRay::PropertyMatrixDialog 0 0 400 300 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() GammaRay::PropertyMatrixDialog accept() 248 254 157 274 buttonBox rejected() GammaRay::PropertyMatrixDialog reject() 316 260 286 274 gammaray-2.11.3/ui/propertyeditor/propertymatrixeditor.cpp000066400000000000000000000030121413310443000241150ustar00rootroot00000000000000/* propertymatrixeditor.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Tobias Koenig Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "propertymatrixeditor.h" #include "propertymatrixdialog.h" using namespace GammaRay; PropertyMatrixEditor::PropertyMatrixEditor(QWidget *parent) : PropertyExtendedEditor(parent) { } void PropertyMatrixEditor::showEditor(QWidget* parent) { PropertyMatrixDialog dlg(parent); dlg.setMatrix(value()); if (dlg.exec()) save(dlg.matrix()); emit editorClosed(); } gammaray-2.11.3/ui/propertyeditor/propertymatrixeditor.h000066400000000000000000000027651413310443000236000ustar00rootroot00000000000000/* propertymatrixeditor.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Tobias Koenig Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_PROPERTYMATRIXEDITOR_H #define GAMMARAY_PROPERTYMATRIXEDITOR_H #include "propertyextendededitor.h" namespace GammaRay { class PropertyMatrixEditor : public PropertyExtendedEditor { Q_OBJECT public: explicit PropertyMatrixEditor(QWidget *parent = nullptr); void showEditor(QWidget * parent) override; }; } #endif // GAMMARAY_PROPERTYMATRIXEDITOR_H gammaray-2.11.3/ui/propertyeditor/propertymatrixmodel.cpp000066400000000000000000000342221413310443000237360ustar00rootroot00000000000000/* propertymatrixmodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Tobias Koenig Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "propertymatrixmodel.h" #include #include #include #include #include #include #include using namespace GammaRay; PropertyMatrixModel::PropertyMatrixModel(QObject *parent) : QAbstractTableModel(parent) { } void PropertyMatrixModel::setMatrix(const QVariant &matrix) { beginResetModel(); m_matrix = matrix; endResetModel(); } QVariant PropertyMatrixModel::matrix() const { return m_matrix; } int PropertyMatrixModel::rowCount(const QModelIndex &parent) const { if (parent.isValid()) return 0; switch (m_matrix.type()) { case QVariant::Vector2D: return 2; case QVariant::Matrix: case QVariant::Transform: case QVariant::Vector3D: case QVariant::Quaternion: return 3; case QVariant::Matrix4x4: case QVariant::Vector4D: return 4; default: return 0; } } int PropertyMatrixModel::columnCount(const QModelIndex &parent) const { if (parent.isValid()) return 0; switch (m_matrix.type()) { case QVariant::Vector2D: case QVariant::Vector3D: case QVariant::Vector4D: case QVariant::Quaternion: return 1; case QVariant::Matrix: return 2; case QVariant::Transform: return 3; case QVariant::Matrix4x4: return 4; default: return 0; } } QVariant PropertyMatrixModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); if (role != Qt::DisplayRole && role != Qt::EditRole) return QVariant(); switch (m_matrix.type()) { case QVariant::Matrix: { const QMatrix value = m_matrix.value(); switch (index.row() << 4 | index.column()) { case 0x00: return value.m11(); case 0x01: return value.m12(); case 0x10: return value.m21(); case 0x11: return value.m22(); case 0x20: return value.dx(); case 0x21: return value.dy(); } break; } case QVariant::Transform: { const QTransform value = m_matrix.value(); switch (index.row() << 4 | index.column()) { case 0x00: return value.m11(); case 0x01: return value.m12(); case 0x02: return value.m13(); case 0x10: return value.m21(); case 0x11: return value.m22(); case 0x12: return value.m23(); case 0x20: return value.m31(); case 0x21: return value.m32(); case 0x22: return value.m33(); } break; } case QVariant::Matrix4x4: { const QMatrix4x4 value = m_matrix.value(); return value(index.row(), index.column()); } case QVariant::Vector2D: { const QVector2D value = m_matrix.value(); switch (index.row()) { case 0: return value.x(); case 1: return value.y(); } break; } case QVariant::Vector3D: { const QVector3D value = m_matrix.value(); switch (index.row()) { case 0: return value.x(); case 1: return value.y(); case 2: return value.z(); } break; } case QVariant::Vector4D: { const QVector4D value = m_matrix.value(); switch (index.row()) { case 0: return value.x(); case 1: return value.y(); case 2: return value.z(); case 3: return value.w(); } break; } case QVariant::Quaternion: { float pitch, yaw, roll; const QQuaternion value = m_matrix.value(); value.getEulerAngles(&pitch, &yaw, &roll); switch (index.row()) { case 0: return pitch; case 1: return yaw; case 2: return roll; } break; } default: break; } return QVariant(); } bool PropertyMatrixModel::setData(const QModelIndex &index, const QVariant &data, int role) { if (!index.isValid()) return false; if (role != Qt::EditRole) return false; bool ok = false; float floatData = data.toFloat(&ok); if (!ok) return false; switch (m_matrix.type()) { case QVariant::Vector2D: { QVector2D value = m_matrix.value(); switch (index.row()) { case 0: value.setX(floatData); break; case 1: value.setY(floatData); break; } m_matrix = value; break; } case QVariant::Vector3D: { QVector3D value = m_matrix.value(); switch (index.row()) { case 0: value.setX(floatData); break; case 1: value.setY(floatData); break; case 2: value.setZ(floatData); break; } m_matrix = value; break; } case QVariant::Vector4D: { QVector4D value = m_matrix.value(); switch (index.row()) { case 0: value.setX(floatData); break; case 1: value.setY(floatData); break; case 2: value.setZ(floatData); break; case 3: value.setW(floatData); break; } m_matrix = value; break; } case QVariant::Quaternion: { float pitch, yaw, roll; const QQuaternion value = m_matrix.value(); value.getEulerAngles(&pitch, &yaw, &roll); switch (index.row()) { case 0: pitch = floatData; break; case 1: yaw = floatData; break; case 2: roll = floatData; break; } m_matrix = QQuaternion::fromEulerAngles(pitch, yaw, roll); break; } case QVariant::Matrix: { QMatrix value = m_matrix.value(); switch (index.row() << 4 | index.column()) { case 0x00: value.setMatrix(floatData, value.m12(), value.m21(), value.m22(), value.dx(), value.dy()); break; case 0x01: value.setMatrix(value.m11(), floatData, value.m21(), value.m22(), value.dx(), value.dy()); break; case 0x10: value.setMatrix(value.m11(), value.m12(), floatData, value.m22(), value.dx(), value.dy()); break; case 0x11: value.setMatrix(value.m11(), value.m12(), value.m21(), floatData, value.dx(), value.dy()); break; case 0x20: value.setMatrix(value.m11(), value.m12(), value.m21(), value.m22(), floatData, value.dy()); break; case 0x21: value.setMatrix(value.m11(), value.m12(), value.m21(), value.m22(), value.dx(), floatData); break; } m_matrix = value; break; } case QVariant::Transform: { QTransform value = m_matrix.value(); switch (index.row() << 4 | index.column()) { case 0x00: value.setMatrix(floatData, value.m12(), value.m13(), value.m21(), value.m22(), value.m23(), value.m31(), value.m32(), value.m33()); break; case 0x01: value.setMatrix(value.m11(), floatData, value.m13(), value.m21(), value.m22(), value.m23(), value.m31(), value.m32(), value.m33()); break; case 0x02: value.setMatrix(value.m11(), value.m12(), floatData, value.m21(), value.m22(), value.m23(), value.m31(), value.m32(), value.m33()); break; case 0x10: value.setMatrix(value.m11(), value.m12(), value.m13(), floatData, value.m22(), value.m23(), value.m31(), value.m32(), value.m33()); break; case 0x11: value.setMatrix(value.m11(), value.m12(), value.m13(), value.m21(), floatData, value.m23(), value.m31(), value.m32(), value.m33()); break; case 0x12: value.setMatrix(value.m11(), value.m12(), value.m13(), value.m21(), value.m22(), floatData, value.m31(), value.m32(), value.m33()); break; case 0x20: value.setMatrix(value.m11(), value.m12(), value.m13(), value.m21(), value.m22(), value.m23(), floatData, value.m32(), value.m33()); break; case 0x21: value.setMatrix(value.m11(), value.m12(), value.m13(), value.m21(), value.m22(), value.m23(), value.m31(), floatData, value.m33()); break; case 0x22: value.setMatrix(value.m11(), value.m12(), value.m13(), value.m21(), value.m22(), value.m23(), value.m31(), value.m32(), floatData); break; } m_matrix = value; break; } case QVariant::Matrix4x4: { QMatrix4x4 value = m_matrix.value(); value(index.row(), index.column()) = floatData; m_matrix = value; break; } default: // TODO: should we assert here? break; } emit dataChanged(index, index); return true; } QVariant PropertyMatrixModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role != Qt::DisplayRole) return QAbstractTableModel::headerData(section, orientation, role); if (orientation == Qt::Horizontal) { switch (m_matrix.type()) { case QVariant::Matrix: switch (section) { case 0: return tr("m_1"); case 1: return tr("m_2"); } break; case QVariant::Matrix4x4: switch (section) { case 0: return tr("m_1"); case 1: return tr("m_2"); case 2: return tr("m_3"); case 3: return tr("m_4"); } break; case QVariant::Transform: switch (section) { case 0: return tr("m_1"); case 1: return tr("m_2"); case 2: return tr("m_3"); } break; case QVariant::Vector2D: case QVariant::Vector3D: case QVariant::Vector4D: return QString(); default: break; } } else { switch (m_matrix.type()) { case QVariant::Matrix: switch (section) { case 0: return tr("m1_"); case 1: return tr("m2_"); case 2: return tr("d x/y"); } break; case QVariant::Matrix4x4: switch (section) { case 0: return tr("m1_"); case 1: return tr("m2_"); case 2: return tr("m3_"); case 3: return tr("m4_"); } break; case QVariant::Transform: switch (section) { case 0: return tr("m1_"); case 1: return tr("m2_"); case 2: return tr("m3_"); } break; case QVariant::Vector2D: switch (section) { case 0: return tr("x"); case 1: return tr("y"); } break; case QVariant::Vector3D: switch (section) { case 0: return tr("x"); case 1: return tr("y"); case 2: return tr("z"); } break; case QVariant::Vector4D: switch (section) { case 0: return tr("x"); case 1: return tr("y"); case 2: return tr("z"); case 3: return tr("w"); } break; case QVariant::Quaternion: switch (section) { case 0: return tr("pitch"); case 1: return tr("yaw"); case 2: return tr("roll"); } break; default: break; } } return QString(); } Qt::ItemFlags PropertyMatrixModel::flags(const QModelIndex &index) const { return QAbstractTableModel::flags(index) | Qt::ItemIsEditable; } gammaray-2.11.3/ui/propertyeditor/propertymatrixmodel.h000066400000000000000000000041231413310443000234000ustar00rootroot00000000000000/* propertymatrixmodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Tobias Koenig Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_PROPERTYMATRIXMODEL_H #define GAMMARAY_PROPERTYMATRIXMODEL_H #include namespace GammaRay { class PropertyMatrixModel : public QAbstractTableModel { Q_OBJECT public: explicit PropertyMatrixModel(QObject *parent = nullptr); void setMatrix(const QVariant &matrix); QVariant matrix() const; int rowCount(const QModelIndex &parent = QModelIndex()) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; bool setData(const QModelIndex &index, const QVariant &data, int role = Qt::EditRole) override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; Qt::ItemFlags flags(const QModelIndex &index) const override; private: QVariant m_matrix; }; } #endif // GAMMARAY_PROPERTYMATRIXMODEL_H gammaray-2.11.3/ui/propertyeditor/propertypaletteeditor.cpp000066400000000000000000000030761413310443000242610ustar00rootroot00000000000000/* propertypaletteeditor.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2012-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "propertypaletteeditor.h" #include "palettedialog.h" using namespace GammaRay; PropertyPaletteEditor::PropertyPaletteEditor(QWidget *parent) : PropertyExtendedEditor(parent) { } void PropertyPaletteEditor::showEditor(QWidget* parent) { PaletteDialog dlg(value().value(), parent); dlg.setEditable(!isReadOnly()); if (dlg.exec() == QDialog::Accepted) save(dlg.editedPalette()); emit editorClosed(); } gammaray-2.11.3/ui/propertyeditor/propertypaletteeditor.h000066400000000000000000000027731413310443000237310ustar00rootroot00000000000000/* propertypaletteeditor.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2012-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_PROPERTYPALETTEEDITOR_H #define GAMMARAY_PROPERTYPALETTEEDITOR_H #include "propertyextendededitor.h" namespace GammaRay { class PropertyPaletteEditor : public PropertyExtendedEditor { Q_OBJECT public: explicit PropertyPaletteEditor(QWidget *parent = nullptr); void showEditor(QWidget * parent) override; }; } #endif // GAMMARAY_PROPERTYPALETTEEDITOR_H gammaray-2.11.3/ui/propertyeditor/propertyrecteditor.cpp000066400000000000000000000055071413310443000235610ustar00rootroot00000000000000/* propertyrecteditor.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Hannah von Reth Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "propertyrecteditor.h" #include "ui_propertyrecteditor.h" using namespace GammaRay; PropertyRectEditorDialog::PropertyRectEditorDialog(const QRectF &rect, QWidget *parent) : QDialog(parent) , ui(new Ui::PropertyRectEditorDialog) { ui->setupUi(this); ui->pointFWidget->setPointF(rect.topLeft()); ui->sizeFWidget->setSizeF(rect.size()); ui->stackedWidget->setCurrentWidget(ui->floatPage); } PropertyRectEditorDialog::PropertyRectEditorDialog(const QRect &rect, QWidget *parent) : QDialog(parent) , ui(new Ui::PropertyRectEditorDialog) { ui->setupUi(this); ui->pointWidget->setPoint(rect.topLeft()); ui->sizeWidget->setSizeValue(rect.size()); ui->stackedWidget->setCurrentWidget(ui->intPage); } PropertyRectEditorDialog::~PropertyRectEditorDialog() { delete ui; } QRectF PropertyRectEditorDialog::rectF() const { if (ui->stackedWidget->currentWidget() == ui->intPage) { return QRectF(ui->pointWidget->point(), ui->sizeWidget->sizeValue()); } else { return QRectF(ui->pointFWidget->pointF(), ui->sizeFWidget->sizeF()); } } PropertyRectEditor::PropertyRectEditor(QWidget *parent) : PropertyExtendedEditor(parent) { } void PropertyRectEditor::showEditor(QWidget* parent) { PropertyRectEditorDialog dlg(value().toRect(), parent); if (dlg.exec() == QDialog::Accepted) save(dlg.rectF().toRect()); emit editorClosed(); } PropertyRectFEditor::PropertyRectFEditor(QWidget *parent) : PropertyExtendedEditor(parent) { } void PropertyRectFEditor::showEditor(QWidget* parent) { PropertyRectEditorDialog dlg(value().toRectF(), parent); if (dlg.exec() == QDialog::Accepted) save(dlg.rectF()); emit editorClosed(); } gammaray-2.11.3/ui/propertyeditor/propertyrecteditor.h000066400000000000000000000041241413310443000232200ustar00rootroot00000000000000/* propertyrecteditor.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Hannah von Reth Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef PROPERTYRECTEDITOR_H #define PROPERTYRECTEDITOR_H #include "propertyeditor/propertyextendededitor.h" #include namespace GammaRay { namespace Ui { class PropertyRectEditorDialog; } class PropertyRectEditorDialog : public QDialog { Q_OBJECT public: explicit PropertyRectEditorDialog(const QRectF &rect, QWidget *parent = nullptr); explicit PropertyRectEditorDialog(const QRect &rect, QWidget *parent = nullptr); ~PropertyRectEditorDialog() override; QRectF rectF() const; private: Ui::PropertyRectEditorDialog *ui; }; class PropertyRectEditor : public PropertyExtendedEditor { Q_OBJECT public: explicit PropertyRectEditor(QWidget *parent = nullptr); void showEditor(QWidget * parent) override; }; class PropertyRectFEditor : public PropertyExtendedEditor { Q_OBJECT public: explicit PropertyRectFEditor(QWidget *parent = nullptr); void showEditor(QWidget * parent) override; }; } #endif // PROPERTYRECTEDITOR_H gammaray-2.11.3/ui/propertyeditor/propertyrecteditor.ui000066400000000000000000000106071413310443000234110ustar00rootroot00000000000000 GammaRay::PropertyRectEditorDialog 0 0 400 140 0 0 TopLeft: Size TopLeft: Size: Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok GammaRay::PropertyPointFEditor QWidget
    ui/propertyeditor/propertydoublepaireditor.h
    1
    GammaRay::PropertySizeFEditor QWidget
    ui/propertyeditor/propertydoublepaireditor.h
    1
    GammaRay::PropertyPointEditor QWidget
    ui/propertyeditor/propertyintpaireditor.h
    1
    GammaRay::PropertySizeEditor QWidget
    ui/propertyeditor/propertyintpaireditor.h
    1
    buttonBox accepted() GammaRay::PropertyRectEditorDialog accept() 199 114 199 69 buttonBox rejected() GammaRay::PropertyRectEditorDialog reject() 199 114 199 69
    gammaray-2.11.3/ui/propertyeditor/propertytexteditor.cpp000066400000000000000000000074161413310443000236110ustar00rootroot00000000000000/* propertytexteditor.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Filipe Azevedo Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "propertytexteditor.h" #include "ui_propertytexteditor.h" using namespace GammaRay; PropertyTextEditorDialog::PropertyTextEditorDialog(const QString &text, QWidget *parent) : QDialog(parent) , ui(new Ui::PropertyTextEditorDialog) , m_bytes(text.toUtf8()) , m_mode(NoMode) { ui->setupUi(this); setMode(StringMode); connect(ui->modeButton, &QAbstractButton::clicked, this, &PropertyTextEditorDialog::toggleMode); } PropertyTextEditorDialog::PropertyTextEditorDialog(const QByteArray &bytes, QWidget *parent) : QDialog(parent) , ui(new Ui::PropertyTextEditorDialog) , m_bytes(bytes) , m_mode(NoMode) { ui->setupUi(this); setMode(StringMode); connect(ui->modeButton, &QAbstractButton::clicked, this, &PropertyTextEditorDialog::toggleMode); } PropertyTextEditorDialog::~PropertyTextEditorDialog() = default; void PropertyTextEditorDialog::setReadOnly(bool readOnly) { ui->plainTextEdit->setReadOnly(readOnly); } QString PropertyTextEditorDialog::text() const { if (m_mode == StringMode) return ui->plainTextEdit->toPlainText(); else return QString::fromUtf8(bytes()); } QByteArray PropertyTextEditorDialog::bytes() const { if (m_mode == StringMode) return ui->plainTextEdit->toPlainText().toUtf8(); else return QByteArray::fromHex(ui->plainTextEdit->toPlainText().toUtf8()); } void PropertyTextEditorDialog::setMode(PropertyTextEditorDialog::Mode mode) { if (m_mode != mode) { m_mode = mode; if (m_mode == StringMode) { ui->modeButton->setText(tr("Switch to Hex mode")); ui->plainTextEdit->setPlainText(QString::fromUtf8(m_bytes)); } else { ui->modeButton->setText(tr("Switch to String mode")); ui->plainTextEdit->setPlainText(m_bytes.toHex()); } } } void PropertyTextEditorDialog::toggleMode() { setMode(m_mode == StringMode ? HexMode : StringMode); } PropertyTextEditor::PropertyTextEditor(QWidget *parent) : PropertyExtendedEditor(parent) { setInlineEditable(true); } void PropertyTextEditor::showEditor(QWidget* parent) { PropertyTextEditorDialog dlg(value().toString(), parent); dlg.setReadOnly(isReadOnly()); if (dlg.exec() == QDialog::Accepted) save(dlg.text()); emit editorClosed(); } PropertyByteArrayEditor::PropertyByteArrayEditor(QWidget *parent) : PropertyExtendedEditor(parent) { } void PropertyByteArrayEditor::showEditor(QWidget* parent) { PropertyTextEditorDialog dlg(value().toByteArray(), parent); dlg.setReadOnly(isReadOnly()); if (dlg.exec() == QDialog::Accepted) save(dlg.bytes()); emit editorClosed(); } gammaray-2.11.3/ui/propertyeditor/propertytexteditor.h000066400000000000000000000047651413310443000232620ustar00rootroot00000000000000/* propertytexteditor.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Filipe Azevedo Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_PROPERTYTEXTEDITOR_H #define GAMMARAY_PROPERTYTEXTEDITOR_H #include #include #include namespace GammaRay { namespace Ui { class PropertyTextEditorDialog; } /** Property editor dialog for texts. */ class PropertyTextEditorDialog : public QDialog { Q_OBJECT public: enum Mode { NoMode, StringMode, HexMode }; explicit PropertyTextEditorDialog(const QString &text, QWidget *parent = nullptr); explicit PropertyTextEditorDialog(const QByteArray &bytes, QWidget *parent = nullptr); ~PropertyTextEditorDialog() override; void setReadOnly(bool readOnly); QString text() const; QByteArray bytes() const; void setMode(Mode mode); public Q_SLOTS: void toggleMode(); protected: QScopedPointer ui; const QByteArray m_bytes; Mode m_mode; }; /** Property editor for texts. */ class PropertyTextEditor : public PropertyExtendedEditor { Q_OBJECT public: explicit PropertyTextEditor(QWidget *parent = nullptr); void showEditor(QWidget * parent) override; }; class PropertyByteArrayEditor : public PropertyExtendedEditor { Q_OBJECT public: explicit PropertyByteArrayEditor(QWidget *parent = nullptr); void showEditor(QWidget * parent) override; }; } #endif // GAMMARAY_PROPERTYTEXTEDITOR_H gammaray-2.11.3/ui/propertyeditor/propertytexteditor.ui000066400000000000000000000042751413310443000234440ustar00rootroot00000000000000 GammaRay::PropertyTextEditorDialog 0 0 400 300 QPlainTextEdit::NoWrap 40 Switch to Hex Mode Unsaved changes will be lost QDialogButtonBox::Close|QDialogButtonBox::Save GammaRay::CodeEditor QPlainTextEdit
    ui/codeeditor/codeeditor.h
    buttonBox accepted() GammaRay::PropertyTextEditorDialog accept() 31 278 5 245 buttonBox rejected() GammaRay::PropertyTextEditorDialog reject() 150 284 396 248
    gammaray-2.11.3/ui/propertywidget.cpp000066400000000000000000000142511413310443000176010ustar00rootroot00000000000000/* propertywidget.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "propertywidget.h" #include "common/endpoint.h" #include "common/objectbroker.h" #include "common/propertycontrollerinterface.h" #include #include #include #include using namespace GammaRay; QVector PropertyWidget::s_tabFactories = QVector(); QVector PropertyWidget::s_propertyWidgets; PropertyWidget::PropertyWidget(QWidget *parent) : QTabWidget(parent) , m_tabsUpdatedTimer(new QTimer(this)) , m_lastManuallySelectedWidget(nullptr) , m_controller(nullptr) { m_tabsUpdatedTimer->setInterval(100); m_tabsUpdatedTimer->setSingleShot(true); s_propertyWidgets.push_back(this); connect(this, &QTabWidget::currentChanged, this, &PropertyWidget::slotCurrentTabChanged); connect(m_tabsUpdatedTimer, &QTimer::timeout, this, &PropertyWidget::tabsUpdated); } PropertyWidget::~PropertyWidget() { const int index = s_propertyWidgets.indexOf(this); if (index >= 0) s_propertyWidgets.remove(index); } QString PropertyWidget::objectBaseName() const { Q_ASSERT(!m_objectBaseName.isEmpty()); return m_objectBaseName; } void PropertyWidget::setObjectBaseName(const QString &baseName) { Q_ASSERT(m_objectBaseName.isEmpty()); // ideally the object base name would be a ctor argument, but then this doesn't work in Designer anymore m_objectBaseName = baseName; if (Endpoint::instance()->objectAddress(baseName + ".controller") == Protocol::InvalidObjectAddress) return; // unknown property controller, likely disabled/not supported on the server if (m_controller) disconnect(m_controller, &PropertyControllerInterface::availableExtensionsChanged, this, &PropertyWidget::updateShownTabs); m_controller = ObjectBroker::object( m_objectBaseName + ".controller"); connect(m_controller, &PropertyControllerInterface::availableExtensionsChanged, this, &PropertyWidget::updateShownTabs); updateShownTabs(); } static void propertyWidgetCleanup() { PropertyWidget::cleanupTabs(); } void PropertyWidget::registerTab(PropertyWidgetTabFactoryBase *factory) { if (s_tabFactories.isEmpty()) qAddPostRoutine(propertyWidgetCleanup); s_tabFactories.push_back(factory); for (PropertyWidget *widget : qAsConst(s_propertyWidgets)) widget->updateShownTabs(); } void PropertyWidget::cleanupTabs() { qDeleteAll(s_tabFactories); } void PropertyWidget::createWidgets() { if (m_objectBaseName.isEmpty()) return; for (PropertyWidgetTabFactoryBase *factory : qAsConst(s_tabFactories)) { if (!factoryInUse(factory) && extensionAvailable(factory)) { const PageInfo pi = { factory, factory->createWidget(this) }; m_pages.push_back(pi); } } std::sort(m_pages.begin(), m_pages.end(), [](const PageInfo &lhs, const PageInfo &rhs) -> bool { if (lhs.factory->priority() == rhs.factory->priority()) return s_tabFactories.indexOf(lhs.factory) < s_tabFactories.indexOf(rhs.factory); return lhs.factory->priority() < rhs.factory->priority(); }); } void PropertyWidget::updateShownTabs() { setUpdatesEnabled(false); createWidgets(); // we distinguish between the last selected tab, and the last one that // was explicitly selected. The latter might be temporarily hidden, but // we will try to restore it when it becomes available again. auto prevManuallySelected = m_lastManuallySelectedWidget; auto prevSelectedWidget = currentWidget(); int tabIt = 0; for (const auto &page : qAsConst(m_pages)) { const int index = indexOf(page.widget); if (extensionAvailable(page.factory)) { if (index != tabIt) removeTab(index); insertTab(tabIt, page.widget, page.factory->label()); ++tabIt; } else if (index != -1) { removeTab(index); } } // try to restore selection if (!prevSelectedWidget) // first time setCurrentIndex(0); else if (indexOf(prevManuallySelected) >= 0) setCurrentWidget(prevManuallySelected); else if (indexOf(prevSelectedWidget) >= 0) setCurrentWidget(prevSelectedWidget); // reset to last user selection as this possibly // changed as a result of the reording above m_lastManuallySelectedWidget = prevManuallySelected; setUpdatesEnabled(true); m_tabsUpdatedTimer->start(); // use a timer to group chained registrations. } bool PropertyWidget::extensionAvailable(PropertyWidgetTabFactoryBase *factory) const { return m_controller->availableExtensions().contains(m_objectBaseName + '.' + factory->name()); } bool PropertyWidget::factoryInUse(PropertyWidgetTabFactoryBase *factory) const { return std::find_if(m_pages.begin(), m_pages.end(), [factory](const PageInfo &pi) { return pi.factory == factory; }) != m_pages.end(); } void PropertyWidget::slotCurrentTabChanged() { m_lastManuallySelectedWidget = currentWidget(); } gammaray-2.11.3/ui/propertywidget.h000066400000000000000000000064311413310443000172470ustar00rootroot00000000000000/* propertywidget.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_PROPERTYWIDGET_H #define GAMMARAY_PROPERTYWIDGET_H #include #include #include #include "gammaray_ui_export.h" #include "propertywidgettab.h" QT_BEGIN_NAMESPACE class QAbstractItemModel; class QAbstractItemView; class QModelIndex; class QTimer; QT_END_NAMESPACE namespace GammaRay { class Ui_PropertyWidget; class PropertyControllerInterface; /** @brief Client-side counter-part GammaRay::PropertyController. */ class GAMMARAY_UI_EXPORT PropertyWidget : public QTabWidget { Q_OBJECT public: explicit PropertyWidget(QWidget *parent = nullptr); virtual ~PropertyWidget(); QString objectBaseName() const; void setObjectBaseName(const QString &baseName); /** Register a new tab widget factory. * @tparam T A widget type providing the tab UI * @param name The internal object name of this extension. * @param label The user-visible tab label of this extension. * @param priority This is used to keep tabs in a stable order, tabs are ordered * left to right with increasing priority. */ template static void registerTab(const QString &name, const QString &label, int priority = 1000) { registerTab(new PropertyWidgetTabFactory(name, label, priority)); } //! \internal static void cleanupTabs(); signals: void tabsUpdated(); private: static void registerTab(PropertyWidgetTabFactoryBase *factory); void createWidgets(); bool extensionAvailable(PropertyWidgetTabFactoryBase *factory) const; bool factoryInUse(PropertyWidgetTabFactoryBase *factory) const; private slots: void updateShownTabs(); void slotCurrentTabChanged(); private: QString m_objectBaseName; QTimer *m_tabsUpdatedTimer; QWidget *m_lastManuallySelectedWidget; struct PageInfo { PropertyWidgetTabFactoryBase *factory; QWidget *widget; }; QVector m_pages; PropertyControllerInterface *m_controller; static QVector s_tabFactories; static QVector s_propertyWidgets; }; } #endif // GAMMARAY_PROPERTYWIDGET_H gammaray-2.11.3/ui/propertywidgettab.cpp000066400000000000000000000033331413310443000202670ustar00rootroot00000000000000/* propertywidgettab.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "propertywidgettab.h" using namespace GammaRay; PropertyWidgetTabFactoryBase::PropertyWidgetTabFactoryBase(const QString &name, const QString &label, int priority) : m_name(name) , m_label(label) , m_priority(priority) { } PropertyWidgetTabFactoryBase::~PropertyWidgetTabFactoryBase() = default; QString PropertyWidgetTabFactoryBase::name() const { return m_name; } QString PropertyWidgetTabFactoryBase::label() const { return m_label; } int PropertyWidgetTabFactoryBase::priority() const { return m_priority; } gammaray-2.11.3/ui/propertywidgettab.h000066400000000000000000000055151413310443000177400ustar00rootroot00000000000000/* propertywidgettab.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef PROPERTYWIDGETTAB_H #define PROPERTYWIDGETTAB_H #include "gammaray_ui_export.h" #include #include namespace GammaRay { class PropertyWidget; /*! Priority values for property widget extension tabs. * It is recommended to specify tab priorities relating to these * constants. */ namespace PropertyWidgetTabPriority { /*! Tab priority constants. */ enum Priority { First = 0, ///< The main property inspector, the left-most one. Basic = 100, ///< QObject data that is of common use. Advanced = 200, ///< Tools with common and high value use for a small sub-set of classes. Exotic = 300 ///< Rarely used information, or data that is only valuable to a small amount of users. }; } /*! Interface for tabs in the property widget. */ class GAMMARAY_UI_EXPORT PropertyWidgetTabFactoryBase { public: explicit PropertyWidgetTabFactoryBase(const QString &name, const QString &label, int priority); virtual ~PropertyWidgetTabFactoryBase(); virtual QWidget *createWidget(PropertyWidget *parent) = 0; QString name() const; QString label() const; int priority() const; private: Q_DISABLE_COPY(PropertyWidgetTabFactoryBase) QString m_name; QString m_label; int m_priority; }; /*! Template implementation of PropertyWidgetTabFactoryBase. */ template class PropertyWidgetTabFactory : public PropertyWidgetTabFactoryBase { public: explicit PropertyWidgetTabFactory(const QString &name, const QString &label, int priority) : GammaRay::PropertyWidgetTabFactoryBase(name, label, priority) {} QWidget *createWidget(PropertyWidget *parent) override { return new T(parent); } }; } #endif // PROPERTYWIDGETTAB_H gammaray-2.11.3/ui/proxytooluifactory.cpp000066400000000000000000000040371413310443000205170ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "proxytooluifactory.h" #include using namespace GammaRay; ProxyToolUiFactory::ProxyToolUiFactory(const PluginInfo &pluginInfo, QObject *parent) : ProxyFactory(pluginInfo, parent) { } QString ProxyToolUiFactory::name() const { return pluginInfo().name(); } bool ProxyToolUiFactory::isValid() const { return pluginInfo().isValid(); } bool ProxyToolUiFactory::remotingSupported() const { return pluginInfo().remoteSupport(); } QWidget *ProxyToolUiFactory::createWidget(QWidget *parentWidget) { loadPlugin(); ToolUiFactory *fac = factory(); if (!fac) return new QLabel(tr("Plugin '%1' could not be loaded.").arg( pluginInfo().path()), parentWidget); Q_ASSERT(fac); return fac->createWidget(parentWidget); } void ProxyToolUiFactory::initUi() { loadPlugin(); ToolUiFactory *fac = factory(); if (!fac) return; fac->initUi(); } gammaray-2.11.3/ui/proxytooluifactory.h000066400000000000000000000037521413310443000201670ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2011-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_PROXYTOOLUIFACTORY_H #define GAMMARAY_PROXYTOOLUIFACTORY_H #include #include namespace GammaRay { /** * A wrapper around a plugin ToolUiFactory that only loads the actual plugin once really needed. * Until then, meta-data is provided based on a plugin spec file. */ class ProxyToolUiFactory : public ProxyFactory { Q_OBJECT public: /** * @param pluginInfo Plugin spec file */ explicit ProxyToolUiFactory(const PluginInfo &pluginInfo, QObject *parent = nullptr); QString name() const override; /** Returns @c true if the plugin seems valid from all the information we have so far. */ bool isValid() const; bool remotingSupported() const override; QWidget *createWidget(QWidget *parentWidget) override; void initUi() override; }; } #endif // GAMMARAY_PROXYTOOLUIFACTORY_H gammaray-2.11.3/ui/remoteviewwidget.cpp000066400000000000000000001252201413310443000201020ustar00rootroot00000000000000/* remoteviewwidget.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "remoteviewwidget.h" #include "modelpickerdialog.h" #include "trailingcolorlabel.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace GammaRay; static const qint32 RemoteViewWidgetStateVersion = 1; QT_BEGIN_NAMESPACE GAMMARAY_ENUM_STREAM_OPERATORS(GammaRay::RemoteViewWidget::InteractionMode) QT_END_NAMESPACE RemoteViewWidget::RemoteViewWidget(QWidget *parent) : QWidget(parent) , m_zoomLevelModel(new QStandardItemModel(this)) , m_unavailableText(tr("No remote view available.")) , m_interactionModeActions(new QActionGroup(this)) , m_trailingColorLabel(new TrailingColorLabel(this)) , m_zoom(1.0) , m_x(0) , m_y(0) , m_interactionMode(NoInteraction) , m_supportedInteractionModes(NoInteraction) , m_hasMeasurement(false) , m_pickProxyModel(new ObjectIdsFilterProxyModel(this)) , m_invisibleItemsProxyModel(new VisibilityFilterProxyModel(this)) , m_initialZoomDone(false) , m_extraViewportUpdateNeeded(true) , m_showFps(false) { setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); setMouseTracking(true); setMinimumSize(QSize(400, 300)); setFocusPolicy(Qt::StrongFocus); window()->setAttribute(Qt::WA_AcceptTouchEvents); window()->setAttribute(Qt::WA_TouchPadAcceptSingleTouchEvents); setAttribute(Qt::WA_AcceptTouchEvents); setAttribute(Qt::WA_TouchPadAcceptSingleTouchEvents); // Background textures { QPixmap bgPattern(20, 20); bgPattern.fill(Qt::lightGray); QPainter bgPainter(&bgPattern); bgPainter.fillRect(10, 0, 10, 10, Qt::gray); bgPainter.fillRect(0, 10, 10, 10, Qt::gray); m_activeBackgroundBrush.setTexture(bgPattern); } { QPixmap bgPattern(20, 20); bgPattern.fill(Qt::darkGray); QPainter bgPainter(&bgPattern); bgPainter.fillRect(10, 0, 10, 10, Qt::gray); bgPainter.fillRect(0, 10, 10, 10, Qt::gray); m_inactiveBackgroundBrush.setTexture(bgPattern); } m_zoomLevels.reserve(8); m_zoomLevels << .1 << .25 << .5 << 1.0 << 2.0 << 4.0 << 8.0 << 16.0; for (const auto level : qAsConst(m_zoomLevels)) { auto item = new QStandardItem; item->setText(QString::number(level * 100.0) + locale().percent()); item->setData(level, Qt::UserRole); m_zoomLevelModel->appendRow(item); } setupActions(); connect(m_interactionModeActions, &QActionGroup::triggered, this, &RemoteViewWidget::interactionActionTriggered); setSupportedInteractionModes(ViewInteraction | Measuring | ElementPicking | InputRedirection | ColorPicking); setInteractionMode(ViewInteraction); window()->installEventFilter(this); } RemoteViewWidget::~RemoteViewWidget() { window()->removeEventFilter(this); } void RemoteViewWidget::setName(const QString &name) { m_interface = ObjectBroker::object(name); connect(m_interface.data(), &RemoteViewInterface::reset, this, &RemoteViewWidget::reset); connect(m_interface.data(), &RemoteViewInterface::elementsAtReceived, this, &RemoteViewWidget::elementsAtReceived); connect(m_interface.data(), &RemoteViewInterface::frameUpdated, this, &RemoteViewWidget::frameUpdated); if (isVisible()) { m_interface->setViewActive(true); } m_interface->clientViewUpdated(); } void RemoteViewWidget::setupActions() { m_interactionModeActions->setExclusive(true); auto action = new QAction(UIResources::themedIcon(QLatin1String("move-preview.png")), tr( "Pan View"), this); action->setObjectName("aPanView"); action->setCheckable(true); action->setToolTip(tr("Pan view
    " "Default mode. Click and drag to move the preview. Won't impact the original application in any way.")); action->setData(ViewInteraction); action->setActionGroup(m_interactionModeActions); action = new QAction(UIResources::themedIcon(QLatin1String("measure-pixels.png")), tr("Measure Pixel Sizes"), this); action->setObjectName("aMeasurePixelSizes"); action->setCheckable(true); action->setToolTip(tr("Measure pixel-sizes
    " "Choose this mode, click somewhere and drag to measure the distance between the " "point you clicked and the point where your mouse pointer is. (Measured in scene " "coordinates).")); action->setData(Measuring); action->setActionGroup(m_interactionModeActions); action = new QAction(tr("Pick Element"), this); action->setObjectName("aPickElement"); action->setIconText(tr("Pick")); action->setIcon(UIResources::themedIcon(QLatin1String("pick-element.png"))); action->setCheckable(true); action->setToolTip(tr("Pick Element
    " "Select an element for inspection by clicking on it.")); action->setData(ElementPicking); action->setActionGroup(m_interactionModeActions); action = new QAction(UIResources::themedIcon(QLatin1String("redirect-input.png")), tr("Redirect Input"), this); action->setObjectName("aRedirectInput"); action->setCheckable(true); action->setToolTip(tr("Redirect Input
    " "In this mode all mouse input is redirected directly to the original application," "so you can control the application directly from within GammaRay.")); action->setData(InputRedirection); action->setActionGroup(m_interactionModeActions); action = new QAction(UIResources::themedIcon(QLatin1String("pick-color.png")), tr("Inspect Colors"), this); action->setObjectName("aInspectColors"); action->setCheckable(true); action->setToolTip(tr("Inspect Colors
    " "Inspect the RGBA channels of the currently hovered pixel")); action->setData(ColorPicking); action->setActionGroup(m_interactionModeActions); m_zoomOutAction = new QAction(UIResources::themedIcon(QLatin1String("zoom-out.png")), tr( "Zoom Out"), this); m_zoomOutAction->setObjectName("aZoomOut"); m_zoomOutAction->setShortcutContext(Qt::WidgetShortcut); m_zoomOutAction->setShortcuts(QKeySequence::ZoomOut); connect(m_zoomOutAction, &QAction::triggered, this, &RemoteViewWidget::zoomOut); addAction(m_zoomOutAction); // needed to make the WidgetShortcut context work m_zoomInAction = new QAction(UIResources::themedIcon(QLatin1String("zoom-in.png")), tr( "Zoom In"), this); m_zoomInAction->setObjectName("aZoomIn"); m_zoomInAction->setShortcutContext(Qt::WidgetShortcut); m_zoomInAction->setShortcuts(QKeySequence::ZoomIn); connect(m_zoomInAction, &QAction::triggered, this, &RemoteViewWidget::zoomIn); addAction(m_zoomInAction); m_toggleFPSAction = new QAction(tr("Display FPS"), this); m_toggleFPSAction->setObjectName("aToggleFPS"); m_toggleFPSAction->setCheckable(true); m_toggleFPSAction->setToolTip("Display FPS
    " "Shows rate of received frames from debuggee."); connect(m_toggleFPSAction, &QAction::toggled, this, &RemoteViewWidget::enableFPS); addAction(m_toggleFPSAction); updateActions(); } void RemoteViewWidget::updateActions() { foreach (auto action, m_interactionModeActions->actions()) { action->setEnabled(m_frame.isValid()); } Q_ASSERT(!m_zoomLevels.isEmpty()); const auto zoomLevel = zoomLevelIndex(); m_zoomOutAction->setEnabled(zoomLevel != 0); m_zoomInAction->setEnabled(zoomLevel != m_zoomLevels.size() - 1); } void RemoteViewWidget::enableFPS(const bool showFPS) { m_showFps = showFPS; } void RemoteViewWidget::updateUserViewport() { if (!isVisible()) return; const auto userViewport = QRectF(QPointF(std::floor(-m_x / m_zoom), std::floor(-m_y / m_zoom)), QSizeF(std::ceil(width() / m_zoom) + 1, std::ceil(height() / m_zoom) + 1)); // When We update the viewport by zooming out we need one additional // frameupdate from the application before stopping to send userViewports, // or otherwise the newly appeared area stays empty :( // If we would skip, but need an extra update if (userViewport.contains(frame().viewRect()) && m_extraViewportUpdateNeeded) { m_extraViewportUpdateNeeded = false; m_interface->sendUserViewport(userViewport); return; } // Regular skip if (userViewport.contains(frame().viewRect())) { return; } // Refill extra-update again once we needed a real update m_extraViewportUpdateNeeded = true; m_interface->sendUserViewport(userViewport); } const RemoteViewFrame &RemoteViewWidget::frame() const { return m_frame; } RemoteViewInterface *RemoteViewWidget::remoteViewInterface() const { return m_interface.data(); } void RemoteViewWidget::pickElementId(const QModelIndex &index) { const GammaRay::ObjectId id = index.data(ObjectModel::ObjectIdRole).value(); m_interface->pickElementId(id); } void RemoteViewWidget::elementsAtReceived(const GammaRay::ObjectIds &ids, int bestCandidate) { Q_ASSERT(!ids.isEmpty()); m_pickProxyModel->setIds(ids); if (ids.count() == 1) { m_interface->pickElementId(ids.first()); } else { const int candidate = bestCandidate == -1 ? 0 : bestCandidate; auto *dlg = new ModelPickerDialog(window()); m_invisibleItemsProxyModel->setSourceModel(m_pickProxyModel); m_invisibleItemsProxyModel->setFlagRole(flagRole()); m_invisibleItemsProxyModel->setInvisibleMask(invisibleMask()); dlg->setModel(m_invisibleItemsProxyModel); dlg->setCurrentIndex(ObjectModel::ObjectIdRole, QVariant::fromValue(ids[candidate])); connect(dlg, &ModelPickerDialog::activated, this, &RemoteViewWidget::pickElementId); connect(dlg, &ModelPickerDialog::checkBoxStateChanged, m_invisibleItemsProxyModel, &VisibilityFilterProxyModel::setHideItems); dlg->open(); } } void RemoteViewWidget::frameUpdated(const RemoteViewFrame &frame) { if (!m_frame.isValid()) { m_frame = frame; if (m_initialZoomDone) centerView(); else fitToView(); } else { m_frame = frame; update(); m_fps = 1000.0 / m_fpsTimer.elapsed(); m_fpsTimer.restart(); } updateActions(); if (m_interactionMode == ColorPicking) pickColor(); emit frameChanged(); QMetaObject::invokeMethod(m_interface, "clientViewUpdated", Qt::QueuedConnection); } int RemoteViewWidget::invisibleMask() const { return m_invisibleMask; } void RemoteViewWidget::setInvisibleMask(int invisibleMask) { m_invisibleMask = invisibleMask; } bool RemoteViewWidget::hasValidFrame() const { return m_frame.isValid(); } bool RemoteViewWidget::hasValidCompleteFrame() const { return m_frame.isValid() && (m_frame.image().size() / m_frame.image().devicePixelRatio()) == m_frame.viewRect().size().toSize(); } int RemoteViewWidget::flagRole() const { return m_flagRole; } void RemoteViewWidget::setFlagRole(int flagRole) { m_flagRole = flagRole; } QAbstractItemModel *RemoteViewWidget::pickSourceModel() const { return m_pickProxyModel->sourceModel(); } void RemoteViewWidget::setPickSourceModel(QAbstractItemModel *sourceModel) { if (sourceModel == m_pickProxyModel->sourceModel()) return; m_pickProxyModel->setSourceModel(sourceModel); } void RemoteViewWidget::reset() { m_frame = RemoteViewFrame(); m_hasMeasurement = false; update(); emit frameChanged(); } void RemoteViewWidget::setUnavailableText(const QString &msg) { m_unavailableText = msg; update(); } QActionGroup *RemoteViewWidget::interactionModeActions() const { return m_interactionModeActions; } QAction *RemoteViewWidget::zoomOutAction() const { return m_zoomOutAction; } QAction *RemoteViewWidget::zoomInAction() const { return m_zoomInAction; } void RemoteViewWidget::restoreState(const QByteArray &state) { if (state.isEmpty()) return; QDataStream stream(state); restoreState(stream); } QByteArray RemoteViewWidget::saveState() const { QByteArray data; { QDataStream stream(&data, QIODevice::WriteOnly); saveState(stream); } return data; } double RemoteViewWidget::zoom() const { return m_zoom; } int RemoteViewWidget::zoomLevelIndex() const { const auto it = std::lower_bound(m_zoomLevels.begin(), m_zoomLevels.end(), m_zoom); return std::distance(m_zoomLevels.begin(), it); } void RemoteViewWidget::setZoom(double zoom) { Q_ASSERT(!m_zoomLevels.isEmpty()); const auto oldZoom = m_zoom; // snap to nearest zoom level int index = 0; auto it = std::lower_bound(m_zoomLevels.constBegin(), m_zoomLevels.constEnd(), zoom); if (it == m_zoomLevels.constEnd()) { index = m_zoomLevels.size() - 1; } else if (it != m_zoomLevels.constBegin()) { const auto delta = (*it) - zoom; index = std::distance(m_zoomLevels.constBegin(), it); --it; if (zoom - (*it) < delta) --index; } if (m_zoomLevels.at(index) == oldZoom) return; m_zoom = m_zoomLevels.at(index); emit zoomChanged(); emit zoomLevelChanged(index); emit stateChanged(); m_x = contentWidth() / 2 - (contentWidth() / 2 - m_x) * m_zoom / oldZoom; m_y = contentHeight() / 2 - (contentHeight() / 2 - m_y) * m_zoom / oldZoom; updateActions(); updateUserViewport(); update(); } void RemoteViewWidget::setZoomLevel(int index) { Q_ASSERT(index >= 0 && index < m_zoomLevels.size()); setZoom(m_zoomLevels.at(index)); } void RemoteViewWidget::zoomIn() { Q_ASSERT(!m_zoomLevels.isEmpty()); auto it = std::lower_bound(m_zoomLevels.constBegin(), m_zoomLevels.constEnd(), m_zoom); if (it == m_zoomLevels.constEnd()) return; ++it; if (it == m_zoomLevels.constEnd()) return; setZoom(*it); } void RemoteViewWidget::zoomOut() { Q_ASSERT(!m_zoomLevels.isEmpty()); auto it = std::lower_bound(m_zoomLevels.constBegin(), m_zoomLevels.constEnd(), m_zoom); if (it == m_zoomLevels.constBegin()) return; --it; setZoom(*it); } void RemoteViewWidget::fitToView() { const auto scale = std::min(1.0, std::min((double)contentWidth() / (double)m_frame.sceneRect().width(), (double)contentHeight() / (double)m_frame.sceneRect().height())); setZoom(scale); centerView(); } void RemoteViewWidget::centerView() { m_x = 0.5 * (contentWidth() - m_frame.sceneRect().width() * m_zoom); m_y = 0.5 * (contentHeight() - m_frame.sceneRect().height() * m_zoom); updateUserViewport(); update(); } QAbstractItemModel *RemoteViewWidget::zoomLevelModel() const { return m_zoomLevelModel; } RemoteViewWidget::InteractionMode RemoteViewWidget::interactionMode() const { return m_interactionMode; } void RemoteViewWidget::setInteractionMode(RemoteViewWidget::InteractionMode mode) { if (m_interactionMode == mode || !(mode & m_supportedInteractionModes)) return; switch (mode) { case Measuring: case ElementPicking: setCursor(Qt::CrossCursor); break; case ViewInteraction: setCursor(Qt::OpenHandCursor); break; case NoInteraction: case InputRedirection: setCursor(QCursor()); break; case ColorPicking: setCursor(Qt::CrossCursor); break; } m_interactionMode = mode; foreach (auto action, m_interactionModeActions->actions()) { if (action->data() == mode) action->setChecked(true); } update(); emit interactionModeChanged(); emit stateChanged(); } RemoteViewWidget::InteractionModes RemoteViewWidget::supportedInteractionModes() const { return m_supportedInteractionModes; } void RemoteViewWidget::setSupportedInteractionModes(RemoteViewWidget::InteractionModes modes) { m_supportedInteractionModes = modes; foreach (auto action, m_interactionModeActions->actions()) action->setVisible(action->data().toInt() & modes); } void RemoteViewWidget::paintEvent(QPaintEvent *event) { Q_UNUSED(event); QPainter p(this); if (!m_frame.isValid()) { QWidget::paintEvent(event); p.drawText(rect(), Qt::AlignHCenter | Qt::AlignVCenter, m_unavailableText); return; } drawBackground(&p); p.save(); p.setTransform(QTransform::fromTranslate(m_x, m_y)); if (m_zoom < 1.0) { // We want the preview to look nice when zoomed out, // but need to be able to see single pixels when zoomed in. p.setRenderHint(QPainter::SmoothPixmapTransform); } p.save(); p.setTransform(QTransform().scale(m_zoom, m_zoom), true); p.setTransform(m_frame.transform(), true); p.drawImage(QPoint(), m_frame.image()); p.restore(); drawDecoration(&p); p.restore(); drawRuler(&p); if (m_showFps) drawFPS(&p); if (m_interactionMode == Measuring && m_hasMeasurement) drawMeasureOverlay(&p); } void RemoteViewWidget::drawBackground(QPainter *p) { // not really efficient... p->fillRect(rect(), m_inactiveBackgroundBrush); p->fillRect(m_x, m_y, m_zoom * m_frame.viewRect().width(), m_zoom * m_frame.viewRect().height(), m_activeBackgroundBrush); } void RemoteViewWidget::drawDecoration(QPainter *p) { Q_UNUSED(p); } static int tickLength(int sourcePos, int labelDistance) { int l = 8; if (sourcePos % labelDistance == 0) { return 2 * l; } if (sourcePos % 10 == 0) { return l + 2; } else if (sourcePos % 5 == 0) { return l + 1; } return l; } void RemoteViewWidget::drawRuler(QPainter *p) { p->save(); const int hRulerHeight = horizontalRulerHeight(); const int vRulerWidth = verticalRulerWidth(); const int viewTickStep = std::max(2, m_zoom); const int viewLabelDist = viewTickLabelDistance(); const int sourceLabelDist = sourceTickLabelDistance(viewLabelDist); const auto activePen = QPen(QColor(255, 255, 255, 170)); const auto inactivePen = QPen(QColor(0, 0, 0, 170)); const auto selectedPen = QPen(palette().color(QPalette::Highlight)); // background p->setPen(Qt::NoPen); p->setBrush(QBrush(QColor(51, 51, 51, 170))); p->drawRect(QRect(0, height() - hRulerHeight, width(), hRulerHeight)); p->drawRect(QRect(width() - vRulerWidth, 0, vRulerWidth, height() - hRulerHeight)); // horizontal ruler at the bottom p->save(); p->translate(0, height() - hRulerHeight); for (int i = (m_x % viewTickStep); i < contentWidth(); i += viewTickStep) { const int sourcePos = (i - m_x) / m_zoom; if (sourcePos == m_currentMousePosition.x()) { p->setPen(selectedPen); } else if (sourcePos < 0 || sourcePos > m_frame.viewRect().width()) { p->setPen(inactivePen); } else { p->setPen(activePen); } const int tickSize = tickLength(sourcePos, sourceLabelDist); p->drawLine(i, 0, i, tickSize); if (sourcePos % sourceLabelDist == 0) { if (sourcePos < 0 || sourcePos > m_frame.viewRect().width()) { p->setPen(inactivePen); } else { p->setPen(activePen); } p->drawText(i - viewLabelDist / 2, tickSize, viewLabelDist, hRulerHeight - tickSize, Qt::AlignHCenter | Qt::AlignVCenter, QString::number(sourcePos)); } } p->restore(); // vertical ruler on the right p->save(); p->translate(width() - vRulerWidth, 0); for (int i = (m_y % viewTickStep); i < contentHeight(); i += viewTickStep) { const int sourcePos = (i - m_y) / m_zoom; if (sourcePos == m_currentMousePosition.y()) { p->setPen(selectedPen); } else if (sourcePos < 0 || sourcePos > m_frame.viewRect().height()) { p->setPen(inactivePen); } else { p->setPen(activePen); } const int tickSize = tickLength(sourcePos, sourceLabelDist); p->drawLine(0, i, tickSize, i); if (sourcePos % sourceLabelDist == 0) { if (sourcePos < 0 || sourcePos > m_frame.viewRect().height()) { p->setPen(inactivePen); } else { p->setPen(activePen); } p->drawText(tickSize, i - viewLabelDist / 2, vRulerWidth - tickSize, viewLabelDist, Qt::AlignHCenter | Qt::AlignVCenter, QString::number(sourcePos)); } } p->restore(); p->setPen(activePen); p->drawText(QRect(width() - vRulerWidth, height() - hRulerHeight, vRulerWidth, hRulerHeight), QStringLiteral("%1x\n%2"). arg(std::floor(m_currentMousePosition.x())). arg(std::floor(m_currentMousePosition.y())), Qt::AlignHCenter | Qt::AlignVCenter); p->restore(); } void RemoteViewWidget::drawFPS(QPainter *p) { p->save(); const int hRulerHeight = horizontalRulerHeight(); const int vRulerWidth = verticalRulerWidth(); QFontMetrics metrics(p->font()); const int barWidth = 20; QString fps = QString::number(m_fps, 'g', 3) + " fps"; const QRect textrect(width() - vRulerWidth - metrics.width(fps) - 5, height() - hRulerHeight - metrics.height() - 5, metrics.width(fps) + 2, metrics.height() + 2); p->drawText(textrect, Qt::AlignRight, fps); p->setBrush(QBrush(QColor(51, 51, 51, 170))); p->setPen(Qt::NoPen); p->drawRect(QRect(width() - vRulerWidth - barWidth - 5, height() - hRulerHeight - metrics.height() - 5, barWidth, -10.0 * m_fps)); p->restore(); } int RemoteViewWidget::viewTickLabelDistance() const { const auto maxLabel = std::max(m_frame.viewRect().width(), m_frame.viewRect().height()); return 2 * fontMetrics().width(QString::number(maxLabel)); } int RemoteViewWidget::sourceTickLabelDistance(int viewDistance) { Q_ASSERT(viewDistance > 0); if (m_tickLabelDists.isEmpty()) { m_tickLabelDists.reserve(11); m_tickLabelDists << 5 << 10 << 20 << 25 << 50 << 100 << 200 << 250 << 500 << 1000 << 2000; } const int sourceDist = viewDistance / m_zoom; while (sourceDist > m_tickLabelDists.last()) m_tickLabelDists.push_back(m_tickLabelDists.at(m_tickLabelDists.size() - 4) * 10); const auto it = std::lower_bound(m_tickLabelDists.constBegin(), m_tickLabelDists.constEnd(), sourceDist); return *it; } void RemoteViewWidget::drawMeasureOverlay(QPainter *p) { p->save(); p->setCompositionMode(QPainter::CompositionMode_Difference); auto pen = QPen(QColor(255, 255, 255, 170)); p->setPen(pen); const auto startPos = mapFromSource(m_measurementStartPosition); const auto endPos = mapFromSource(m_measurementEndPosition); const QPoint hOffset(5, 0); const QPoint vOffset(0, 5); p->drawLine(startPos - hOffset, startPos + hOffset); p->drawLine(startPos - vOffset, startPos + vOffset); p->drawLine(endPos - hOffset, endPos + hOffset); p->drawLine(endPos - vOffset, endPos + vOffset); p->drawLine(startPos, endPos); pen.setStyle(Qt::DotLine); p->setPen(pen); p->drawLine(startPos, QPoint(endPos.x(), startPos.y())); p->drawLine(QPoint(endPos.x(), startPos.y()), endPos); p->restore(); // start and end labels const QPoint startLabelDir(startPos.x() < endPos.x() ? -1 : 1, startPos.y() < endPos.y() ? -1 : 1); const QPoint endLabelDir(-startLabelDir.x(), -startLabelDir.y()); drawMeasurementLabel(p, startPos, startLabelDir, QStringLiteral("x: %1 y: %2"). arg(m_measurementStartPosition.x()). arg(m_measurementStartPosition.y())); if (endPos != startPos) { drawMeasurementLabel(p, endPos, endLabelDir, QStringLiteral("x: %1 y: %2"). arg(m_measurementEndPosition.x()). arg(m_measurementEndPosition.y())); } // distance label const auto dPos = QPoint(startPos + endPos) / 2; const auto d = QLineF(m_measurementStartPosition, m_measurementEndPosition).length(); if (d > 0) { const QPoint dDir(startLabelDir.x(), endLabelDir.y()); drawMeasurementLabel(p, dPos, dDir, QStringLiteral("%1px").arg(d, 0, 'f', 2)); } // x/y length labels, if there is enough space const auto xLength = std::abs(m_measurementStartPosition.x() - m_measurementEndPosition.x()); const auto yLength = std::abs(m_measurementStartPosition.y() - m_measurementEndPosition.y()); const auto xDiff = std::abs(endPos.x() - startPos.x()); if (xDiff > fontMetrics().height() * 2 && xLength > 0 && yLength > 0) { const auto xPos = QPoint(dPos.x(), startPos.y()); const QPoint xDir = QPoint(-startLabelDir.x(), startLabelDir.y()); drawMeasurementLabel(p, xPos, xDir, QStringLiteral("x: %1px").arg(xLength)); } const auto yDiff = std::abs(endPos.y() - startPos.y()); if (yDiff > fontMetrics().height() * 2 && xLength > 0 && yLength > 0) { const auto yPos = QPoint(endPos.x(), dPos.y()); const QPoint yDir = QPoint(endLabelDir.x(), -endLabelDir.y()); drawMeasurementLabel(p, yPos, yDir, QStringLiteral("y: %1px").arg(yLength)); } } void RemoteViewWidget::drawMeasurementLabel(QPainter *p, QPoint pos, QPoint dir, const QString &text) { p->save(); static const auto margin = 2; const auto height = fontMetrics().height() + (2 * margin); const auto width = fontMetrics().width(text) + (2 * margin); QRect r(pos.x(), pos.y(), width * dir.x(), height * dir.y()); r = r.normalized(); r = r.translated(dir * 5); p->setPen(palette().color(QPalette::Text)); p->setBrush(palette().background()); p->drawRect(r); p->drawText(r, Qt::AlignCenter, text); p->restore(); } QPoint RemoteViewWidget::mapToSource(QPoint pos) const { return (pos - QPoint(m_x, m_y)) / m_zoom; } QPointF RemoteViewWidget::mapToSource(QPointF pos) const { return (pos - QPointF(m_x, m_y)) / m_zoom; } QRectF RemoteViewWidget::mapToSource(const QRectF &r) const { return QRectF(mapToSource(r.topLeft()), mapToSource(r.bottomRight())); } QPoint RemoteViewWidget::mapFromSource(QPoint pos) const { return pos * m_zoom + QPoint(m_x, m_y); } QPointF RemoteViewWidget::mapFromSource(QPointF pos) const { return pos * m_zoom + QPointF(m_x, m_y); } QRectF RemoteViewWidget::mapFromSource(const QRect& rect) const { return {rect.x() * m_zoom, rect.y() * m_zoom, rect.width() * m_zoom, rect.height() * m_zoom}; } QTouchEvent::TouchPoint RemoteViewWidget::mapToSource(const QTouchEvent::TouchPoint &point) { QTouchEvent::TouchPoint p; p.setFlags(point.flags()); p.setId(point.id()); p.setPressure(point.pressure()); p.setState(point.state()); p.setStartPos(mapToSource(point.startPos())); p.setLastPos(mapToSource(point.lastPos())); p.setPos(mapToSource(point.pos())); // relative p.setRect(mapToSource(point.rect())); p.setStartNormalizedPos(mapToSource(point.startNormalizedPos())); p.setLastNormalizedPos(mapToSource(point.lastNormalizedPos())); p.setNormalizedPos(mapToSource(point.normalizedPos())); p.setStartScenePos(mapToSource(point.startScenePos())); p.setLastScenePos(mapToSource(point.lastScenePos())); p.setScenePos(mapToSource(point.scenePos())); p.setSceneRect(mapToSource(point.sceneRect())); // These may not have the right x,y but i don't think there is a way to get the right value. // On the other hand, it's not a problem, probably p.setStartScreenPos(mapToSource(point.startScreenPos())); p.setLastScreenPos(mapToSource(point.lastScreenPos())); p.setScreenPos(mapToSource(point.screenPos())); p.setScreenRect(mapToSource(point.screenRect())); return p; } void RemoteViewWidget::restoreState(QDataStream &stream) { stream.setVersion(QDataStream::Qt_4_8); qint32 version; InteractionMode interactionMode = m_interactionMode; double zoom = m_zoom; stream >> version; switch (version) { case RemoteViewWidgetStateVersion: { stream >> interactionMode; stream >> zoom; break; } } setInteractionMode(interactionMode); setZoom(zoom); m_initialZoomDone = true; } void RemoteViewWidget::saveState(QDataStream &stream) const { stream.setVersion(QDataStream::Qt_4_8); stream << RemoteViewWidgetStateVersion; switch (RemoteViewWidgetStateVersion) { case 1: { stream << m_interactionMode; stream << m_zoom; break; } } } void RemoteViewWidget::clampPanPosition() { if (m_x > width() / 2) m_x = width() / 2; else if (m_x + m_frame.sceneRect().width() * m_zoom < width() / 2.0) m_x = width() / 2 - m_frame.sceneRect().width() * m_zoom; if (m_y > height() / 2) m_y = height() / 2; else if (m_y + m_frame.sceneRect().height() * m_zoom < height() / 2.0) m_y = height() / 2 - m_frame.sceneRect().height() * m_zoom; } void RemoteViewWidget::resizeEvent(QResizeEvent *event) { m_x += 0.5 * (event->size().width() - event->oldSize().width()); m_y += 0.5 * (event->size().height() - event->oldSize().height()); updateUserViewport(); QWidget::resizeEvent(event); } void RemoteViewWidget::mousePressEvent(QMouseEvent *event) { m_currentMousePosition = mapToSource(QPointF(event->pos())); switch (m_interactionMode) { case NoInteraction: break; case ViewInteraction: m_mouseDownPosition = event->pos() - QPoint(m_x, m_y); if ((m_supportedInteractionModes & ElementPicking)) { if ((event->modifiers() & Qt::ShiftModifier) && (event->modifiers() & Qt::ControlModifier)) { m_interface->requestElementsAt(mapToSource(event->pos()), RemoteViewInterface::RequestAll); } else if ((event->modifiers() & Qt::ControlModifier)) { m_interface->requestElementsAt(mapToSource(event->pos()), RemoteViewInterface::RequestBest); } } if (event->buttons() & Qt::LeftButton) setCursor(Qt::ClosedHandCursor); break; case Measuring: if (event->buttons() & Qt::LeftButton) { m_hasMeasurement = true; m_measurementStartPosition = mapToSource(event->pos()); m_measurementEndPosition = mapToSource(event->pos()); update(); } break; case ElementPicking: if (event->buttons() & Qt::LeftButton) { if ((event->modifiers() & Qt::ShiftModifier) && (event->modifiers() & Qt::ControlModifier)) { m_interface->requestElementsAt(mapToSource(event->pos()), RemoteViewInterface::RequestAll); } else { m_interface->requestElementsAt(mapToSource(event->pos()), RemoteViewInterface::RequestBest); } } break; case InputRedirection: sendMouseEvent(event); break; case ColorPicking: break; } QWidget::mousePressEvent(event); } void RemoteViewWidget::mouseReleaseEvent(QMouseEvent *event) { m_currentMousePosition = mapToSource(QPointF(event->pos())); switch (m_interactionMode) { case NoInteraction: case ElementPicking: break; case ViewInteraction: setCursor(Qt::OpenHandCursor); break; case Measuring: if (event->buttons() & Qt::LeftButton) m_measurementEndPosition = mapToSource(event->pos()); break; case InputRedirection: sendMouseEvent(event); break; case ColorPicking: break; } QWidget::mouseReleaseEvent(event); } void RemoteViewWidget::updatePickerVisibility() const { QPointF sourceCoordinates = frame().transform().inverted().map(QPointF(m_currentMousePosition)); // for quick view, transform is needed QPoint sourceCoordinatesInt = QPoint(std::floor(sourceCoordinates.x()), std::floor(sourceCoordinates.y())); if (frame().image().rect().contains(sourceCoordinatesInt)) { m_trailingColorLabel->show(); } else { m_trailingColorLabel->hide(); } } void RemoteViewWidget::pickColor() const { QPointF sourceCoordinates = frame().transform().inverted().map(QPointF(m_currentMousePosition)); // for quick view, transform is needed QPoint sourceCoordinatesInt = QPoint(std::floor(sourceCoordinates.x()), std::floor(sourceCoordinates.y())); if (frame().image().rect().contains(sourceCoordinatesInt)) { m_trailingColorLabel->setPickedColor(frame().image().pixel(sourceCoordinatesInt)); } else { m_trailingColorLabel->setPickedColor(Qt::transparent); } } void RemoteViewWidget::mouseMoveEvent(QMouseEvent *event) { m_currentMousePosition = mapToSource(QPointF(event->pos())); switch (m_interactionMode) { case NoInteraction: case ElementPicking: break; case ViewInteraction: if (event->buttons() != Qt::LeftButton) { break; } m_x = event->x() - m_mouseDownPosition.x(); m_y = event->y() - m_mouseDownPosition.y(); clampPanPosition(); updateUserViewport(); break; case Measuring: if (event->buttons() & Qt::LeftButton) { m_measurementEndPosition = mapToSource(event->pos()); } break; case InputRedirection: sendMouseEvent(event); break; case ColorPicking: // label should be always fully inside the remoteviewwidget auto labelPosition = event->pos() + QPoint(4, 4); // flip to top if it would stick out bottom end of remoteviewwidget if ((labelPosition.y() + m_trailingColorLabel->height()) > this->height()) labelPosition = labelPosition - QPoint(0, 8) - QPoint(0, m_trailingColorLabel->height()); // flip to left if it would stick out right side of remoteviewwidget if ((labelPosition.x() + m_trailingColorLabel->width()) > this->width()) labelPosition = labelPosition - QPoint(8, 0) - QPoint(m_trailingColorLabel->width(), 0); m_trailingColorLabel->move(labelPosition); updatePickerVisibility(); pickColor(); break; } update(); } void RemoteViewWidget::wheelEvent(QWheelEvent *event) { switch (m_interactionMode) { case NoInteraction: break; case ViewInteraction: case ElementPicking: case Measuring: case ColorPicking: if (event->modifiers() & Qt::ControlModifier && event->orientation() == Qt::Vertical) { if (event->delta() > 0) { zoomIn(); } else { zoomOut(); } } else { if (event->orientation() == Qt::Vertical) { m_y += event->delta(); } else { m_x += event->delta(); } clampPanPosition(); updateUserViewport(); } m_currentMousePosition = mapToSource(QPointF(event->pos())); if (m_interactionMode == ColorPicking) { updatePickerVisibility(); pickColor(); } update(); break; case InputRedirection: sendWheelEvent(event); break; } QWidget::wheelEvent(event); } void RemoteViewWidget::keyPressEvent(QKeyEvent *event) { switch (m_interactionMode) { case NoInteraction: case ViewInteraction: case ElementPicking: case Measuring: break; case InputRedirection: sendKeyEvent(event); break; case ColorPicking: if (event->matches(QKeySequence::Copy)){ QMimeData *data = new QMimeData(); data->setColorData(m_trailingColorLabel->pickedColor()); qApp->clipboard()->setMimeData(data); qApp->clipboard()->setText(m_trailingColorLabel->pickedColor().name()); } } QWidget::keyPressEvent(event); } void RemoteViewWidget::keyReleaseEvent(QKeyEvent *event) { switch (m_interactionMode) { case InputRedirection: sendKeyEvent(event); break; default: break; } QWidget::keyReleaseEvent(event); } void RemoteViewWidget::showEvent(QShowEvent *event) { if (m_interface) { m_interface->setViewActive(true); updateUserViewport(); } QWidget::showEvent(event); } void RemoteViewWidget::hideEvent(QHideEvent *event) { if (Endpoint::isConnected()) { if (m_interface) m_interface->setViewActive(false); } QWidget::hideEvent(event); } void RemoteViewWidget::contextMenuEvent(QContextMenuEvent *event) { switch (m_interactionMode) { case ViewInteraction: case ElementPicking: case Measuring: case ColorPicking: { QMenu menu; menu.addActions(m_interactionModeActions->actions()); menu.addSeparator(); menu.addAction(m_zoomOutAction); menu.addAction(m_zoomInAction); if (!qgetenv("GAMMARAY_DEVELOPERMODE").isEmpty()) { menu.addSeparator(); menu.addAction(m_toggleFPSAction); } menu.exec(event->globalPos()); break; } case NoInteraction: case InputRedirection: QWidget::contextMenuEvent(event); break; } } void RemoteViewWidget::enterEvent(QEvent *event) { Q_UNUSED(event); switch (m_interactionMode) { case ViewInteraction: case ElementPicking: case Measuring: case NoInteraction: case InputRedirection: break; case ColorPicking: m_trailingColorLabel->show(); break; } } void RemoteViewWidget::leaveEvent(QEvent *event) { Q_UNUSED(event); switch (m_interactionMode) { case ViewInteraction: case ElementPicking: case Measuring: case NoInteraction: case InputRedirection: case ColorPicking: m_trailingColorLabel->hide(); break; } } bool RemoteViewWidget::eventFilter(QObject *receiver, QEvent *event) { if (receiver == window()) { if (m_interface) { if (event->type() == QEvent::Show) { m_interface->setViewActive(isVisible()); } else if (event->type() == QEvent::Hide) { m_interface->setViewActive(false); } } } return QWidget::eventFilter(receiver, event); } bool RemoteViewWidget::event(QEvent *event) { if (m_interactionMode == InputRedirection) { switch (event->type()) { case QEvent::TouchBegin: case QEvent::TouchCancel: case QEvent::TouchEnd: case QEvent::TouchUpdate: sendTouchEvent(static_cast(event)); return true; default: break; } } return QWidget::event(event); } int RemoteViewWidget::contentWidth() const { return width() - verticalRulerWidth(); } int RemoteViewWidget::contentHeight() const { return height() - horizontalRulerHeight(); } int RemoteViewWidget::verticalRulerWidth() const { return fontMetrics().width(QString::number(m_frame.sceneRect().height())) + 24; // 2* tick length + some margin } int RemoteViewWidget::horizontalRulerHeight() const { return fontMetrics().height() + 20; // 2 * tick length + some margin } void RemoteViewWidget::interactionActionTriggered(QAction *action) { Q_ASSERT(action); setInteractionMode(static_cast(action->data().toInt())); } void RemoteViewWidget::sendMouseEvent(QMouseEvent *event) { m_interface->sendMouseEvent(event->type(), mapToSource(event->pos()), event->button(), event->buttons(), event->modifiers()); } void RemoteViewWidget::sendKeyEvent(QKeyEvent *event) { m_interface->sendKeyEvent(event->type(), event->key(), event->modifiers(), event->text(), event->isAutoRepeat(), event->count()); } void RemoteViewWidget::sendWheelEvent(QWheelEvent *event) { auto angleDelta = event->angleDelta(); auto pixelDelta = event->pixelDelta(); m_interface->sendWheelEvent(mapToSource(event->pos()), pixelDelta, angleDelta, event->buttons(), event->modifiers()); } void RemoteViewWidget::sendTouchEvent(QTouchEvent *event) { event->accept(); QList touchPoints; foreach (const QTouchEvent::TouchPoint &point, event->touchPoints()) { touchPoints << mapToSource(point); } QTouchDevice::Capabilities caps = event->device()->capabilities(); caps &= ~QTouchDevice::RawPositions; //we don't have a way to meaningfully map the raw positions to the source caps &= ~QTouchDevice::Velocity; //neither for velocity m_interface->sendTouchEvent(event->type(), event->device()->type(), caps, event->device()->maximumTouchPoints(), event->modifiers(), event->touchPointStates(), touchPoints); } gammaray-2.11.3/ui/remoteviewwidget.h000066400000000000000000000207101413310443000175450ustar00rootroot00000000000000/* remoteviewwidget.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_REMOTEVIEWWIDGET_H #define GAMMARAY_REMOTEVIEWWIDGET_H #include "gammaray_ui_export.h" #include #include #include #include #include #include QT_BEGIN_NAMESPACE class QAbstractItemModel; class QActionGroup; class QStandardItemModel; class QModelIndex; class QEvent; class QTouchEvent; QT_END_NAMESPACE namespace GammaRay { class RemoteViewInterface; class ObjectIdsFilterProxyModel; class VisibilityFilterProxyModel; class TrailingColorLabel; /** Widget showing remote screen content and providing both visual inspection * capabilities as well as input redirection. * * Can be sub-classed to support e.g. custom element decoration. */ class GAMMARAY_UI_EXPORT RemoteViewWidget : public QWidget { Q_OBJECT public: explicit RemoteViewWidget(QWidget *parent = nullptr); ~RemoteViewWidget() override; /// Set the object name for connecting to the server. void setName(const QString &name); enum InteractionMode { NoInteraction = 0, ///< disable all ViewInteraction = 1, ///< panning, zooming, etc Measuring = 2, InputRedirection = 4, ElementPicking = 8, ColorPicking = 16 }; InteractionMode interactionMode() const; void setInteractionMode(InteractionMode mode); Q_DECLARE_FLAGS(InteractionModes, InteractionMode) InteractionModes supportedInteractionModes() const; void setSupportedInteractionModes(InteractionModes modes); /// Returns the current zoom level double zoom() const; /// Returns the index of the current zoom level, useful for updating a combo box using the zoomLevelModel. int zoomLevelIndex() const; /// Model containing the supported zoom levels, for use with a combo box QAbstractItemModel *zoomLevelModel() const; /// Set the message that is shown when remote view is unavailable. void setUnavailableText(const QString &msg); /// Action group containing all interaction mode switch actions QActionGroup *interactionModeActions() const; QAction *zoomOutAction() const; QAction *zoomInAction() const; QAbstractItemModel *pickSourceModel() const; void setPickSourceModel(QAbstractItemModel *sourceModel); Q_INVOKABLE virtual void restoreState(const QByteArray &state); virtual QByteArray saveState() const; int flagRole() const; void setFlagRole(int flagRole); int invisibleMask() const; void setInvisibleMask(int invisibleMask); bool hasValidFrame() const; bool hasValidCompleteFrame() const; public slots: /// Clears the current view content. void reset(); /// Sets the zoom level to the closest level to @p zoom. void setZoom(double zoom); /// Sets the zoom level to the level at @p index in the zoom level model. void setZoomLevel(int index); void zoomIn(); void zoomOut(); void fitToView(); void centerView(); signals: void zoomChanged(); void zoomLevelChanged(int zoomLevelIndex); void interactionModeChanged(); void stateChanged(); void frameChanged(); protected: /** Current frame data. */ const RemoteViewFrame &frame() const; RemoteViewInterface *remoteViewInterface() const; /** Override this to draw element decorations. * @P p is translated to that 0,0 is the top left corner of the source image, but not scaled */ virtual void drawDecoration(QPainter *p); // translate from view coordinates to source coordinates QPoint mapToSource(QPoint pos) const; QPointF mapToSource(QPointF pos) const; QRectF mapToSource(const QRectF &r) const; // translates from source coordinates to view coordinates QPoint mapFromSource(QPoint pos) const; QPointF mapFromSource(QPointF pos) const; QRectF mapFromSource(const QRect &rect) const; // translate from view to source coordinates QTouchEvent::TouchPoint mapToSource(const QTouchEvent::TouchPoint &point); void restoreState(QDataStream &stream); void saveState(QDataStream &stream) const; void paintEvent(QPaintEvent *event) override; void resizeEvent(QResizeEvent *event) override; void mousePressEvent(QMouseEvent *event) override; void mouseReleaseEvent(QMouseEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override; void wheelEvent(QWheelEvent *event) override; void keyPressEvent(QKeyEvent *event) override; void keyReleaseEvent(QKeyEvent *event) override; void showEvent(QShowEvent *event) override; void hideEvent(QHideEvent *event) override; void contextMenuEvent(QContextMenuEvent *event) override; void enterEvent(QEvent *event) override; void leaveEvent(QEvent *event) override; bool eventFilter(QObject *receiver, QEvent *event) override; bool event(QEvent *event) override; private: void setupActions(); void updateActions(); void drawBackground(QPainter *p); void drawRuler(QPainter *p); void drawFPS(QPainter *p); int sourceTickLabelDistance(int viewDistance); int viewTickLabelDistance() const; void drawMeasureOverlay(QPainter *p); void drawMeasurementLabel(QPainter *p, QPoint pos, QPoint dir, const QString &text); void clampPanPosition(); void sendMouseEvent(QMouseEvent *event); void sendKeyEvent(QKeyEvent *event); void sendWheelEvent(QWheelEvent *event); void sendTouchEvent(QTouchEvent *event); // size of the content area, ie. excluding the rulers int contentWidth() const; int contentHeight() const; // size of the rulers int horizontalRulerHeight() const; int verticalRulerWidth() const; void updatePickerVisibility() const; void pickColor() const; private slots: void interactionActionTriggered(QAction *action); void pickElementId(const QModelIndex &index); void elementsAtReceived(const GammaRay::ObjectIds &ids, int bestCandidate); void frameUpdated(const GammaRay::RemoteViewFrame &frame); void enableFPS(const bool showFPS); void updateUserViewport(); private: RemoteViewFrame m_frame; QBrush m_activeBackgroundBrush; QBrush m_inactiveBackgroundBrush; QVector m_zoomLevels; QStandardItemModel *m_zoomLevelModel; QString m_unavailableText; QVector m_tickLabelDists; QActionGroup *m_interactionModeActions; QAction *m_zoomInAction; QAction *m_zoomOutAction; QAction *m_toggleFPSAction; QPointer m_interface; TrailingColorLabel *m_trailingColorLabel; double m_zoom; int m_x; // view translation before zoom int m_y; InteractionMode m_interactionMode; InteractionModes m_supportedInteractionModes; QPoint m_mouseDownPosition; // semantics depend on interaction mode QPointF m_currentMousePosition; // in view coordinates QPoint m_measurementStartPosition; // in source coordinates QPoint m_measurementEndPosition; // in source coordinates bool m_hasMeasurement; ObjectIdsFilterProxyModel *m_pickProxyModel; VisibilityFilterProxyModel *m_invisibleItemsProxyModel; bool m_initialZoomDone; bool m_extraViewportUpdateNeeded; int m_flagRole; int m_invisibleMask; QElapsedTimer m_fpsTimer; bool m_showFps; qreal m_fps; }; } Q_DECLARE_OPERATORS_FOR_FLAGS(GammaRay::RemoteViewWidget::InteractionModes) #endif // GAMMARAY_REMOTEVIEWWIDGET_H gammaray-2.11.3/ui/resources/000077500000000000000000000000001413310443000160145ustar00rootroot00000000000000gammaray-2.11.3/ui/resources/CMakeLists.txt000066400000000000000000000027361413310443000205640ustar00rootroot00000000000000# See https://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html if(UNIX AND NOT APPLE AND GAMMARAY_BUILD_UI) foreach(size 16 24 32 48 64 128 256 512) foreach(scale 1 2 3) set(scaled_size ${size}x${size}) set(source gammaray/GammaRay-${scaled_size}.png) if(${scale} GREATER 1) set(scaled_size ${size}x${size}@${scale}) set(source gammaray/GammaRay-${scaled_size}x.png) endif() if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${source}) install( FILES ${CMAKE_CURRENT_SOURCE_DIR}/${source} DESTINATION "${ICON_INSTALL_DIR}/hicolor/${scaled_size}/apps" RENAME GammaRay.png ) endif() endforeach() endforeach() endif() if(APPLE) if(NOT GAMMARAY_INSTALL_QT_LAYOUT) install(FILES gammaray/GammaRay.icns DESTINATION "${RESOURCES_INSTALL_DIR}") endif() endif() # generate the class icon qrc file file(READ "classiconindex.map" _map_content) string(REPLACE "\n" ";" _map_content ${_map_content}) set(QRC_CONTENT "") foreach(_icon_data IN LISTS _map_content) if(_icon_data) string(REPLACE " " ";" _icon_data ${_icon_data}) list(GET _icon_data 0 _icon_name) list(GET _icon_data 1 _icon_file) set(QRC_CONTENT "${QRC_CONTENT}\n ${CMAKE_CURRENT_SOURCE_DIR}/gammaray/ui/icons/classes/${_icon_file}") endif() endforeach() configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/classicons.qrc.in ${CMAKE_CURRENT_BINARY_DIR}/classicons.qrc ) gammaray-2.11.3/ui/resources/classiconindex.map000066400000000000000000000060661413310443000215310ustar00rootroot00000000000000QQuickBorderImage/default.png qtquick2/border-image-icon16.png QQuickFlickable/default.png qtquick2/flickable-icon16.png QQuickFlipable/default.png qtquick2/flipable-icon16.png QQuickFocusScope/default.png qtquick2/focusscope-icon16.png QQuickGridView/default.png qtquick2/gridview-icon16.png QQuickImage/default.png qtquick2/image-icon16.png QQuickItem/default.png qtquick2/item-icon16.png QQuickListView/default.png qtquick2/listview-icon16.png QQuickMouseArea/default.png qtquick2/mouse-area-icon16.png QQuickPathView/default.png qtquick2/pathview-icon16.png QQuickRectangle/default.png qtquick2/rect-icon16.png QQuickTextEdit/default.png qtquick2/text-edit-icon16.png QQuickText/default.png qtquick2/text-icon16.png QQuickTextInput/default.png qtquick2/text-input-icon16.png QQuickWebView/default.png qtquick2/webview-icon16.png QDateTimeEdit/default.png qtwidgets/datetimeedit.png QTextEdit/default.png qtwidgets/textedit.png QCalendarWidget/default.png qtwidgets/calendarwidget.png QProgressBar/default.png qtwidgets/progress.png QAbstractScrollArea/default.png qtwidgets/scrollarea.png QLineEdit/default.png qtwidgets/lineedit.png QDockWidget/default.png qtwidgets/dockwidget.png QScrollBar/default.png qtwidgets/vscrollbar.png QComboBox/default.png qtwidgets/combobox.png QCommandLinkButton/default.png qtwidgets/commandlinkbutton.png QWizard/default.png qtwidgets/wizard.png QWidget/default.png qtwidgets/widget.png QSpinBox/default.png qtwidgets/spinbox.png QScrollBar/orientation=Horizontal.png qtwidgets/hscrollbar.png QCheckBox/default.png qtwidgets/checkbox.png QTabWidget/default.png qtwidgets/tabwidget.png QDateEdit/default.png qtwidgets/dateedit.png QLabel/default.png qtwidgets/label.png QListView/default.png qtwidgets/listbox.png QColumnView/default.png qtwidgets/columnview.png QDial/default.png qtwidgets/dial.png QFrame/default.png qtwidgets/frame.png QTabBar/default.png qtwidgets/tabbar.png QSlider/default.png qtwidgets/hslider.png QGroupBox/default.png qtwidgets/groupbox.png QGraphicsView/default.png qtwidgets/graphicsview.png QMdiArea/default.png qtwidgets/mdiarea.png QFontComboBox/default.png qtwidgets/fontcombobox.png QDialogButtonBox/default.png qtwidgets/dialogbuttonbox.png QToolBox/default.png qtwidgets/toolbox.png QTimeEdit/default.png qtwidgets/timeedit.png QPlainTextEdit/default.png qtwidgets/plaintextedit.png QSlider/orientation=Vertical.png qtwidgets/vslider.png QDoubleSpinBox/default.png qtwidgets/doublespinbox.png QLCDNumber/default.png qtwidgets/lcdnumber.png QToolButton/default.png qtwidgets/toolbutton.png QTableView/default.png qtwidgets/table.png QStackedWidget/default.png qtwidgets/widgetstack.png QRadioButton/default.png qtwidgets/radiobutton.png QPushButton/default.png qtwidgets/pushbutton.png QAbstractItemView/default.png qtwidgets/listview.png QSplitter/orientation=Horizontal.png qtwidgets/edithlayoutsplit.png QHBoxLayout/default.png qtwidgets/edithlayout.png QGridLayout/default.png qtwidgets/editgrid.png QSplitter/default.png qtwidgets/editvlayoutsplit.png QVBoxLayout/default.png qtwidgets/editvlayout.png QFormLayout/default.png qtwidgets/editform.png gammaray-2.11.3/ui/resources/classicons.qrc.in000066400000000000000000000001511413310443000212660ustar00rootroot00000000000000 @QRC_CONTENT@ gammaray-2.11.3/ui/resources/gammaray/000077500000000000000000000000001413310443000176125ustar00rootroot00000000000000gammaray-2.11.3/ui/resources/gammaray/GammaRay-1024x1024.png000066400000000000000000001720401413310443000231050ustar00rootroot00000000000000PNG  IHDR+sBIT|d pHYsetEXtSoftwarewww.inkscape.org< IDATxy\Us^@"(;* "@D@tqǝ,a al` *"*BIwW}~$]U]U~@ߪS|@Qs=%=|}9P*BϬefQz7*NjPЙPh@CQ(NqIRv[G|( 4 Zze:ߥc}] ? %Mv )O\_`( 4uIRoO/PP^h@ ij]QIoqiML*W  *ZO6YI.T)7;~k@ \b $-I\g  ()wКg1hL6鯞y (+"zmc[}!ֱgϛ8sc``ȳW1<g2-zzpq\+2kE y?;^^2ҡsos7HoRc&Y8J)"(\m8s*wx8)ɞ?בcFInSWnܻ"gRh?"k獯zE DX}^IK&Suzf-o}Csr=k=gM)҂^`5MF[RgHz&료pP/o[IQ`ҋ.ddz6mK߬y˭#:m~E(F-Ky~:C)C z>(iLvӷ]vf,{DTqA۹̛P?3t \~G(m%geFwyz.Jcج孏J:;t=h N04Y7&wG%=tL%: n<-s %n[fzb{)i[BMSG޽D_$ )K?7C'N#z`g"Ӱf Ƥ+ڞY{]c.[Ju=p pJHeFT_| C@y heIҪmek?Y8͒pƪ7GXW&?_]ɬ9KSOG(y9(mɵjelsټxN󆑝MjTP*IyA–$O:ZPDڦJ:$t$cf2CS.O]qdtI]sL^Y%G=Sw{(9^U;{=Urufn:0x|,?\gd2``_*H6̵$E+,x UqFS\2PȾF|]:G?ulU*-ubͫN0cٛ$ ܽQS\N_kھ:@T^'iT9xMMGn35 f-m=LnjNA_ʯo{X6kƽ${IRC,zI*/t/,v$kټ +s+?-Wzp糝ɣutL"/piLq?'W+}J_T:.ސCw9něNQ'Q҅{A̮;[s`` *VR,,sͧG `C]RZd}7-IT?zy֙$SWzME,s5vy8.AѣQ곡C`@/ y9VU?!6ݟUqWWy2wPtU.0" fh?67<-Z>l+N4kƽ\чMj Ggңsן:,%G\*1[kcM]qdt֢W!gYzfι  fh'Ӊ;<rJGck~WuۡHґeQ?:'@?55y^m$idْcjWwޖwbΗ/o& CD7q䟩JmsjlĨ֓Mָ^mlV~玫3t ^XK(iЙw8c\Xt `>fDٖem$: +P$sjQl \;_k7}9t P@SWzME,s5vy8.t&ݕ_=g!@X몸Fϖ4$t&dCWy1kƽ\чMjTPЙt*1't G[G+C[kr>_cs|ĚWC䠩ɣmKM~z,(AOK{9꡶}RiCgAIŧ;Cvo[7JJ΂2?vk?}<l/ƐP:LZL%?:@6p s=dh4NRK$6_!鹐?2B)=d*i7JJl=::??qt[6Ɇ6  [d~._;YpЇ72]ӕn|}~`OIiq!07ӕ _GC2Y5[R}/CM%VIRәHDIs8t 4^LmukI&o[7Jz*!t S4^tFS%Xw]36n,X8f.)LY@zWvֶ(YMQ|]$fh{@Rc!2"Ww;yNUߘhCrAkϒtR$;5u?0zDu.,s_0awCrE؍to0DδR6Q\0ysM-ȘZ8q9|a zo~ed/ꊸllf9i[>Y ?<|ysҝUTqI}@r}u.9MQE<+t h`PtZ7*F(So[]#I YR)hҚ@y Ir@%4UYX}8bZ. i3w (r:t&Mg݌ù,M Vܧ/fABAv:$ɋם}8o\W%- hq O JMM9vP)i^$=rIjx-Y ($^>ewαWHℏ8׀gfs^Z@! #Z0yISLܱ :S^w܎ cOte[.>7~n i능2ABA'Tg1q5nQH]B/׳T,^s:GҊOZz$-fIlIkB0\/m1bA:CL:Rw$iĺ2]")8ڀfyu WܽHIcBȊke[tWc3.eBC7 ?.w玭wIKF*w,]:PlebtI9z-$3ߜB掩]:Pl40(TZ:IUs>H/ml~Jn3b/5p2麅S!WFCȃj  ij<&ʼniMM@^*Iusf_erknL߼ K43t|2k9뵺k$<`rR43&i9<7Ki5&tO: XSoJ3]:Gɻ<ƮIRsP@h40`uԬ,魡san>?qomJ+=+tE9 ->7+r- n7ϡs?~\GQ_h$3\q߇UIV!RA+>t"EjI}ִ̨4QnK>m4K p&/^A:GcuWmGD@!p_ B0^珫OҼBYUBJ (S9^3CdsH##]oPܢ}4-{KiLz4Ѥ5ɏ环/t1.GB(o76{BN]&IJUhq§CJ qY5]?}0B.l@Ec(U9`@Wt9J4u㻏揫7LT(.о+s8NRU%u{_{#KCXgf3:BJewm/KTYR],%8lvI=_ʼ6t0{޸BJWUT^%Es=>2Pv.t@YtZs \jZ#~B{l C6lRF&ѱ_Ln3"(΋$^ҿ4*l,͛7'FH >^H-j殏JJ̴R7\0x@YW96hF ˜ofB:9ӌw\}8ol1 {]`w]dW1ϟ|cl)1E3Oq=`g%Ն1 u]|%1Z-6cq`LX ,h]_%:`d۪v$Fi /iSS_ھxzB%EkOt΁>0ǽB掭{>xkۣxwǘђޡ^w}D/o_ lKL:%Ndz*J멖yibW@P-nɿ:GT&vT]mT"2U&HQJqmU5UQT]eQEd&3lK2sW~2-mꊕ]mE^wwj댕w+&//v%؜XsH?(In:Q&E9ֻB4yK1kLe^CbR(ɋD߱xr3g+duk+-j+-ZRlH%vk%r\ȥ"|s2V2"}sזwߵpoH5{eɴ v4ސCO83gdm>)>#K&{MYxm @A\hQdg+HJtTz*Jj*VW"2̪+-QSU)r?"^QJlt^[.O6o-Ի/{ߜmg׻l)޷>KᾹ+V9ޡ+l$ H:72dI[ml۳y.oV]fpۂKϙs[m?g!\G 4U'!4R5Q\Se VɆTZ4"! %TFCJ Q슴u:EzWՙrmNL:>9eԖ1-ǝ)Wg*-,{rO߶tڡC3J>x6B\8͸ (Q4)jGKG$"]/lXTY7H;s^0ls*R/k&yGե9?X_ɭ.2'~(t{F@A1.9&)븝`^pt2ͺAKa(ŷof~*" o{-5_ʗǖN?98SM3'$ȁSXMz@2=ɗ[E8PmVYUɇ^ ]Vߛ~w#wlu: U``9NlN#f;S/@p^Q$sϢ M־+\}#|3 `2_^oOko[?fx__!Ux[<(\ /N"ϛt]/2]G;3JX02r/}}0hLmuM[YҨM:~XǨȿ47 wj鴃{$R@@7"3 va)"]S ܹrV_V}ȍyeSw~WܕJǩqXK,2C@; ӈޠ`wI/Jn~+c,de[~/Hz[&Y駏?k: q9d|盢tL_c}2CI2+vc_Њ^`@W/7ټצO:hAK]k_ X<_X:#B2r,L{ls_RV-LJؗ.xkú_ڱr? .(6gW,z5J!X>j_5.Nt={ыIw`I߿u跫.=i!* wcB;9cfsIsH|p˱daμݶPM5J~+}$:懿؜l"vݘyt!QCn%~ه>: I:NƤg٬Zgq?_;nzd(Y ]MGuϓ{hmo)#w>9 `;鎡'_}73I>"j66gWX~~|ۂ^g~{u1Ax:L>ߓu=nD. %YdCg?4AԫxW.;hy>^TNtv=9o~&P>h;t~}5we)#NZ @O=)Ta}8~rg~o/Pi ]z7j6rwl& ?s[%lvW2vg{Ej]pSCH\Wis"%EkN?UQ5o&׿_ X 8u=: `9q\ o=Yn'i?n@sv~6}t^P08T!IzvMkI'W^N?A ,4A┫8=J$t^@?7P䯱}TcM:>y͏9%RrGSgZ̢GcCryo tENin$Qa Jz@6tHB韜Gl2!XEjbՕv䆆i{EYrry:}3F$0r=M2ULo{xvz|*Bwm e~X3Jo[O?;p o0ɦsRz乶c.-q9 `:cCOyv=~rμ]LmdQ n[~D'_ ?2@Mmu}2XʎW拧S_Z2WCP49#-};?S:E` mp^.p2mAJCk\Ro7{TƷ c_3@^\rqJj/ִ~oө&CGPx42vEGd a*Xzl/ L:c>`@B'/^l/謁 >f$NW:s(2u qeaIG) @u6 Ӭ-e2@\|ǫgK:)tZw]eYGⲸ=ݺWKCP<42tj"VG {|86g`sm?,VΨ `vcn7ЛX?qG5l;oK;Oj@IRNj z\|vl! l TNs~}Ȇ/e4)^]h[ݣΑ_Clr #6%" '(wEvA(qqψt+$I+ šȤןy#|)OK^:G.\Ҋ߶ ^Hκ8tp\d%IC(eMMQ1١;>[ß @F˛"/vs颵]z9s(F',99~^K/luCہ#O:׻PFNrLvoطa5{1@}N{w~4EXQ 댧rl0gYVXֻϙ? W춅R TW? ~JY9GtV&tjC.tceYwlQ3IGNzmZ6o=?i־׷L2gQs 5ӝߟ.o[Ѥ 4'+۪Bg)it軿k ',kl +.}쌛76mҢ5c%:GMzck?z}ؔ9&@`'^ ٭9^ۜ}=dz노ba궅U(T $keXȠ `aՈ O8-vimzJ|Hm8rypDK4s[yv=º  ͥm!em \-ld4!0\r9Mi=&:72tũw<⇡s(]4}rM? sB.قynd=e+@+n{&8Ӎ#W;u]=T_ q9Q:FةUk$+Ta)X,[aͪv=t>}35LJ9!0Mc:GH]ܣCGZ/_ŕCPh$~7 hꊅ<\ۜRXgu }5LDɎ0Pf\M3֕v=6{`a S]?_>BPh2}hI<]/Ta߶%mPu~+;?Cb^}Thz&XvI !ԡ ՠaՉqw Uv2B U_4|rƾHun[h~}uA68o-Ӯc{Jê--6Ġdm~MjiM4Jz;WJϹw@y(4` :i-d{e;Ϳ{7A}[VǞGdgwQNϿ(Y}T!ω{ɀ_=02}4 4aً-~1s(tzr-M^!]h}u7$img#tǩYwLӶi==T:Ѫ%@Y~w֏~mfFeEkGK"tR]zfM&3LC*JYO|AkBP^hEr_w~Y)=Yvr\?%/ɀSWK=\\sjeS/.y?dszs3{D!}2g)\n[(Da5La] qvP-  M}NI_o0sdmzCu[5q29'@0뮽ukBֹ)m  m -PBu+k;QMg#(PJ:golH/nG7hxMد=WAC([4"0uzvu^Ur+ܶ}<\׼T3(3SxyfQN~fݘcjt:z#Y@@(c޻f`S }HۇBYO超^i^}|h IDAT&GҪ>.9\d]z6 c./4*>-~',sÊvP--kºP-zn[(Dffg24'+u]娵#wi"5`:u. `3L3K, Bܶрܶ}(ZaCcϧ]XHq@@mCXAsw鷯&a,Giח8EY F($;^-:H% "&(""*X@_M$Bz )߻eve̙ٙGsJ{ɞgy6? k]AXlR z^c  k>}]7˲nD";*RO$=7@J |QML>&B/}xl\{yߧn²xl[[s9cWjuBTNwWK=JgZ^N5`-Lm̝: &b%:O׃:uosaaԯ _Ўǩ3"ߟY넨<)+K=j/oGϴa`>ВdxJ QMb(|3^X{"c Zݻ/űf.'> 9rĊ`'$'`ℽ; ]u<)Qf=Qb(f8yK  ~6w}\c!IPm-qkY *s6Wdbޜ v~NM v bcm1̟A[.$@Pm!c #WѨaBs: *#޳j_Gzը?hCvlvҧX֠1_:#"JóD>(56P2/{9O'i-}ƈ@ilS*(#@XˁJ(Yݢt 9LTE8Pmi7-P`w~8>5&Rm:a u&&Xh\{o]KWpܞS0 M`J\zo$"¿ |y,>#c1y9͏۵ye~|osU9}I: AUXU =D yWfkjdA8ֻ~G!M `w}Rd:zٿ?3o11a"cc2p= ;?2:L߲.XI9xS#"D>{>ؗug\mXiL8,{-2=9Y,L39 _?y 9X 7XvzbB6暹Bq&R\ .x8l݄W/bGD;|yv>^ww=RxX`^ML4 _5.op<]bH}ɥٱ Os40яj5&*~V;9N&o݈#vj]Om>wM3~}뀦FD_ۉ|YOvKy+z`D捓EB̩A%Y:B :2\g da`76kq*APO.ؐN; 5'݆෋;ʅXsvԈ1@)%4NU`$Q$6Bj~m =/ i|@"-}aէTN(9xر!Q |7K&h.\jMc·+ 7Ųxe~kš"Ljk`w5+wwCᯖ%.GׄYQs99%Ά`bo!K0@e)4/%MLnŻkxo}YK'b"w=ŸJt`?OW+2~cY.9֎gUܾ bd- DU^=d(ȗjHT$i}$_cw}b=~zre%ߜsyjDTˆ5' }ʐgZԘewL+r#D ;YZ}DX۩caLXL/m)/o`aG;\ϱIRK05"Q@T/͖V2NOȿc!'1 <ӓbRj0&7Im %[=#G{L,\Œ϶7d% ~&,Q,3uh-uYO20e.ewO<\t GRGSdg6}fr4%痚*HjmɰQP .G@5>݊ moJ⟈ B):@w}:tVA+mm?r? k?啽Uv kq p.B<5o!Y1%ES# xJYRP9>J߻u!P2ɭDTtL`s9]|K aYq߷e>@$b-?6[}# x;& zD a c)PyP_GCwnE2FDD $3F^}lSo!O~snG%wzz[0 x&WLOt=c A$XJKDrUQ6m aMז8df|qV|3-)`Bf5;\\yQmb@"yOc|, h݋?DQ1:u岟H8s@р9o*chjǿ ^T@GR hm0`@{F-@[Cu!"H$-t&;h{ A kÖM O6qy=DTx^ȣfi&Zgwȵ皣IڥXS씂X;j05?vψqH\]LGYbR=3#d(4)6hJ-[ 6(+kJ-S[gфA=qd$zIt'M/jf!o>x^ˋ $"DM9wjoSpOmk\̾G ͆yŸեCj7 ~N^ac2&#N+/oڛ 7TRh"ξ8z1X>7M?f/Pvf_o"˧.yﹿW?` EGlumm]42p _l(@[*؂ŎD>8RCWPhiPhzrޘZ7-[ڛ @R[{'It $FwderMۺeƍtlHD( rij$[#_zL=4[jUi:Trn>C[ \~Phϣ`:w0LLPNG;[ΑVC'Z)t4)4h~;L = z4SOGCO뀶FJ I -}qGSE Lʧvԙs/ D.)8ntgzs x/~[v3ʱ>u9D= y[=F38m-\:ma#U0e}{¾N=D`^_48x?D_ٖߟw k]OA

    w2yw9_*?]A+=@)p!U =Bv2;Ÿv=i]k02-99"GGH'~&>A=]WI oE b$}ӝQ1Qn$G.<՛88Ը'nt˓Mn9q߰l̺<ӱ]qG#ڨ:x󙯭ш-up}+ڡbR}8_tDv QqOD "MfXbό$@f7l9x_noxȴL[#b|wZ6y~ K$NqJT˔ufRʊnݸA,E; ک̸@-8>NDT*Li|]Z;)>< ͙< e|eӻΈߢỌBp5KdRrD)GowӓT̎8-+I=VZ? `#F_(v%wIPYtp'q9%#Q.r]P^7wIeQiOŢ${lݑvMbMW0qwlle!g,9Es"0@A න$@,8mgbpQn>"SIcAQcx@2?Qb[l{]sԅ[߫A-A[? PM]:DD!F^x1 5D_AVM\n^ [t=o?SSPP'F"KX%B&>X7ݶjϸϔף:ʌ M?gߵm|.?3~yxIgOOy2`=ñ[n r2q^Rm;:FuVΛCߊϾӉ)ۏCP7_Xt]>qh(ijn"(L9#<_$ףZw2,§cTm BcYףn:X=ݟ u.y.+c8>a-ZǶBކA- ؂vPP>t>K}(`L9P_5 x`D)~ujzkaY_A,* ;N|:wjPwϾۉ}w*+M@SEН^BouWDD "'ݕ\@)\g, Rzh Ef~-{ 0>Od؂zoοbOͺ'DD "JaW6 k+(}g"=6}:עBJx {-FZՋ:LȽф<zeSBDT !~3͋yx>] =b|#6nzbUwNDT #~؏Sz1IOp7&P"S9BP6{z\iZ-|Hx~b6ֹu≳BjuLP{5"un^=VZ ?&)vws݋yh<_3XJɛO' fRtݎkL5"Бz?6s-zۊRƷ]t@מDy(% ,zIk"j6/]{zbX7#SuYp]z/߅Ooن:& (-G'?"QSϼQe]K{'>Q8KE-QNTPN[XEM{nv@!NǟzlB1Q`(kggs˲iX/%oQItjkŠTT;E[w.d(B=NJ G^G=&rV@_l|gkPEO | B0Y7Q8>[;}8kۧaSjwMDTa AzZ@~7.{aR,a`)uY w;bVfZU &r5mv%^)_:o#yO?SMR~C;翿hg@}aI5Qt $mCTR=n;1 95p?U5&rv IDATu#znD([?!$/KE(^=Kpʈ2/}Ovb0Ruٶۍmb4%"XLq≳B$7m9}_볖 CT oZBE }*Qś[uvؼU[[`u)HDT- 6l/@9.;' w8HTjX k󴩑z2**@ kݣϿ߉}w:wdPv""dL,cWm]|Kԇ lI&ֆʧܫ^*Dc(%d]Z@NgNhZ)o~?]z2x B}gI kH0e ) X֙")LԎ_fW(b=>3%Q)$g=>LU'C `\K&kp՚NΝAxQebh,/Ǿ(z ;w\s1){Ǯ[=LWdiW=Qr- /2v ھQ%xn8'SM]utG'"L^+z"֟O[Xz!<'ڱ01ĸ}+/ƤMبUS`](.xlMDTј C W׬t鯿}O׵wlR8Zpïݝ9|ܴ=ݸ*D.u0@o \WśQt1 3ؽn ]h?;S?wa -hk ?^}_ FDT JJfmOWz:VX&*ڲg&u'IxG8 )L*QvubI{m;\ @vEe`R=!"*Qamʇmv0;D[?(, zJPI;LED.'7c潷.: M!CTR=MC%]j!D,coCJY@ՋJ(R U7=57;c_sVĞt䎬z(<0X4jDiDVczQz?bPx;>j6]5.MuJ,a& Džsvb;P~[& `7vvy:%".LeW~u^DI`0+nZ;5sB!2ZZ'cE~(tǰV ǟ\͔hU|=8Qc(;r DTՇ< xYXQDG*=}O{|oakqJc<7 9jCܟCD4 tJMM=Ÿ:$,e( ] O>{i]A!\v!Y:[uAGM<-ŮPnU1@NпstMzKYbLU[O2.t|εmU羷(Pzy l7J2"K(D:Cq9Qű~4uupOT KzVσ#AjDCv;V[n](V=0@UmqZ nz3P^D>ۏCJC؂M=aφRAG1@4$u|VSXp{ZE%b@.ӥǽhaM,z?p^G""fL 3;[=G=3gJS}/ZU.Q8E'TfB\5*z=LIKݘ}K_@c:&$ê<>"1U UZ :RuRX^^]ك0q\cAQawMDD1@42ޤm$@IU44HZ@:Nj#ʧ@ܴn]q˾S 32_ &(Il\Ⱦ]fuK: UgnP{|& ڱ0yj>UXlqrr&lܚkw5]9λ_{푈V0@4ĀJK+k=kOHE hpɱzw+!V:YBukwI4%09OU6ʪ%-n'"rKɘ“~PPOhLTB~-1&'[H߱n0uվՏ\s; jDC,Q90$2EOY\- Qz0Jpk8Ft}x= I?ڦCw27h9ADD٘ bBo9_ :vKnUqa{=]zj#.R yPu/zO<~'v٢ U@k#eD ɳ(z/6{zmKj02TolaY P!,._iqW{lm 4Dԍs,ѐ2FA$ : Y`*0sIj9%j@%qD䱝|9Qx.U;vo\@4T{P""Q0@Jmۖ'@0q)4[PzmĆvߪqzC\ nȴIA""ʏ !4YR#˩BKuCT[sW6AαLjuӂ>@N׍z/.g@P (gJ|E"DD4 !]}с0Ҷ8I=VIT&.7XXz, F_'@J$hRhϵx~DIt<GDT& ]J@u!4s$ ]-2^+&J%<yPE-A7~\TBgh<]Q&&$$ޛ*n?ChoO.zR97&6B{9Qz':0iu2r(Zs}ue7Bzf}z߱`)1f,LjMbhȧ'bۓ8D[7\Y@&C,H%yڋ*,9EgTIAzfR=gqZSsw]~>.l0@4dIW2M: @t>,x*Zdz\)Y'(#GFLQY7s5zcF䚩z_מb(S藣Ih1oԂslIK.߅*r=~`$?z${mxĚ9 DDDc1@F=WFz㘴qk$@xGs$ǿ KWA$ʶ_Hߏo# ;z/-Ɩ7co}jUj@DD J#; X֛HP@)&?}UJQb%m۷ -z= {mӮ=5I㉫]tNDD ԕ}I257koށ1G<Dn|P"<Pbѣ5c߅D˻V7]M֩d""*D>}93 'rmۼ ua$@>m c-W+`ZĬq̫$ʼ8>\?=p9W!u!a(@I+n&^ly!u/ILٰ?f^zs͊ߞcI(`ZZD{]Sxayvޢ - asݱ+&2Xk†ެ$L [t!E0?~Z!Qu-=#N8>PWܼw!wu`\j(İNC9Rs]{DDDrxӑL \ىm'6rTճgOOEզ]PRri ]z/.n[!lLVU9.: "" J'vGF2LhǸX>3lLT߅ZK=(#n|H{(5k5魆vx۱ޔЅ^>&(+_r'^[[l܂M;`Hâ / `}qp@I(T?2Aޫ/~Ѝ=@8\%_w&IDD6 JTXr6no㛳uQ >v=7AW޿E!;GE Gs2Ϊ>$]hs1X=|A '"0@.-"+;Tm6kuHp& 秫AQRBAz(Dʊn8jIk=HDDc(͒?_ "(?B8d`ۑ7Ȏ ǿU֚9mL=eErĵM䤼ETdaŽF:NV.a""ah,ZOZ] K]|E$j(v=>8WK.Иyѧ'UTL ^_ك7U"?r!kFDD F> П$5=&3eQ\L扅v]I;\xw>\ׇ՝ӄu\VUD5dm'sQ?Mjh=q Z3 8E@,a᭏{׶ce6n8]ވoL> ?=X9k{q[3&D.BRzATcA:Y!1pFTfǽA76nk&Fw,3ZK۩Q òd]t୏:qF>+9g f&\2Ld25˹'inΑ 벪>&[Ӈ)39{dM"" Dc(Kċ=&.G0[xY.>੟ POe(( IA+yhQޟmVf^XޅI5aJ̻M2/@W K\ƺP#QJY"uu=A!0 @}?[Ek5zXn{nQ X$(LaX0 lԫDkcnG$x{ zJάp0{9:/,vLX셑iY 2eI@/a{lq$w1YrWx&*`5ٷsǶ_ïgzy2tIw {lt,g@""L4N~n]@c]&/c$/[l%oA8}" f揰ϴPЛp\&7%cSPDǤg͸}""Pړu^M( F{SȿۛF[cS9!"" Dc̞==׌_~`[ B߰gHZxq̾u}@T:3-MgՏIdp]cE?*;!la5ա1Z7@f _>OsC 1b6f QC)47>O=okCGsBP1Y>E_Mj_.nZW:N \DpoIz-\W_{NDQ-0~lTFc ``q)5 om Ma5ꇔB ē#Oҏh&0-Ϯ@Z #͚ VdHF_ki;O@[s .< O3"r[{I{z޶CXVo Lԣ/m=Bkc]6[cb }CO'苦glOcAC\w,_ D9X +h,]LōomAMBsC^`<9џx". {z^XByX+'$R E~xdFGsBx] EX5UyiS4C/s4$""b(+,xfDBXىXb͙iY 3yVa3'tbXTb|7 J[q[@<52uadzDh e壯|ڶи@kc pש1}Q} /Zw$pEf'[9D9$2A|nNߌvp87?B<_ @4v[$[xe[. :.g]zF2ְ'&A0톇`B{ZhiݪFGs=BiQb`m?^E{9xM^rP_=vrDDTLʯkƝ= 9;&|ӓ›+K>%ο?:o"i. ޶CXV K&P`|7g #OyG:m~Nn9Lhp~ 8f"k郚׼_n԰>d }cI\mw?sR̿]tHDD%Dy(Qf/sa 7{ IDATp.Ëò$oXe%_sD< jt{[KW jk煍X@Asw2pDD <IJޅG)%iˍŒ|2rӡJ@y÷xyQk imBBLhY^ouVyPxRJ(ʕeHA[m ݼKYwVdvrb&n:*LDy>M d 'ʻSAj—#4+9EUp=uȴ.,1@d㱻gv"%;FA]q=+dfY6IrGE=7Le" 3iv0y-3 *$HDz^"HkTDHD yjSOֹz "r˲{iH$ߙSoy:DT0%˸TeDq@+PʼZڅu%X'?WQYbA27ܶd_<5QWTwl'z(&2]r%Н; kQ`A2\M4牛x6Q~UR@ U{ R%,Q2s)y?:* Dn$-8%"*[+UW~5;j"]˵U@_=~ы&"Jg7S`j $:wr';cXV\^ѡ?9G@kw =&D"VL &=%"*2&P]3gS] I^ z.]bRDDT `&N1VRcN/|p.˼ZIbk{W?8E^f<& d1@LZoWDc+{fwDD,ɾc,z'ԅsV@Ѩ8fߊ NIDD%ihw0j.w)|'85Msvf֦nz\P Hn}\-U&4qkq{UZ1CDsSvwZ&jr`:Č>ZP7.'" UuKI+继o;bω wע(v5+ȗ LDD " qQ,iYcM덇o>R̉ z\`s??* P%1;G@s~Pnz .O}{d ad "&JXo%@ ];\.z|H]ĵpc`O't؂@pdrQ7D1^4[4]R?[7}@}EKS=s?reJ(~zC Pk]m""4LiJgR,k0ӓnad O;G) ܅nG8puLt"6+~b)p) Ή1@i[` 7(|W JS} m&bD@q^q>˨(x;[g""lLi' 4(\_"B.WS#[nnZUM,|+jʻ:CH^q&DDT r7a0r~_ADkuUV=|h.48+Vm߹O(5#G3""TL'+ #L<ߢr(`"'VYNxYN0@DD~n/zu?\q~Dl?r( k!ʴ@R23Xnz!"CŞy=UͅեY<.kx c1@DD7",8`ߜ7Y š }]#""&Lzyp$G *tGy\02K{^DDTu ""!ӯ&ȿ$I _ b=8ŏE>?zDDT ""z;Ll0@DDa@KU@`T'L,hs^w6X1@DDF`-q(z6*; ntQItq#ՐjDDDi2%ԯ@$bp]Hp3 Ie)#" L^Н~|5S/oǴ} vPkl{,¯/h5 "Qٳ'<;zOsvمn<ՠ>3d u/q3HDD "",*0MsMBS;!WnXQP. ("W>~OjDDDcX *@Ul@,"M+XH_3EQahƘ73׽8&|2 Nm9>j헤^%u~$\uEDD5 ""1rzW.t;ʬP;—g_%+'"Ϫ_ADD4 ""_ -(m<kfmؠL7Nf(a=cj,Fo!"ZQ*Oa e a$XZ@pȴunfCDD ""\g 鸴Q9uv̪Ț졡 $&rx''|`/mZXR1u65 `^tDp &Zq]O-UA-\º?z0@DD/4c]/P)xؕ J " PunF%"Q> iV56:9]*21&B& >ŀDDT ""R&;UYz탻8r5jDDDLwv?Ϝ 4AT@,AE/-Qz5 `] ZUޟW) @ H9ؔ-s<ϔS6䜙G2y3JL𺤿7Q^ |I<.9sּK&l(PC<aejW 8?Uhx0lh2|'!kd5%jAcDw{FZ27e:Q5Kz|&l 5Vm{*P@v8N@(@Py#E?~v!s*Z jP,P )n zjuwA RM/k'#莧j0k%G<3y@B&0zc(`yaP N5xôoe&Ҡ@B+FI}y%l7p/香zմk?U -k)??c{Ql1MuamvW|YbzF^ژ:/ `m#x7*?{s= ,OO/FG4_zBLT;f3Wstd RXi丩?ʫRƜXaNktm>1)Ck_@&GQ$[eИ $ TkuU<(k%<(].~p)n^֎?k Ճ!h@`G޶m<ˡe+k =Z:Qk@Q 64A^E0j kx թQ^56(&pgT5fELaIjPVW:,L󃭴㸹2*ljh8HI*IIwķ2H?[0}_]4&껨< Q5<^hfȼH=<.Cdm=A^޵/=P66<0(!+=q,HUH em$SՀmޥG>8 Yvqs:XYpKwcI&XE^(Œ¾Sfxam}ߥ؉"3Gݢ]v!Q C.\Vy6}tdrio̲ L0x$ Ș5ZT\`%?Z<%< q@2V,lT ֭\>ۥkoi֯q~V{) ,XU`bѬӺf9輟Jkڤs5u@hk2V..l FۺfC_dl߯wgwqd(yOX%ImƮJ UAS~6IwR;gw IDAT( 1}ں: xȡ_T>0{YP ʛzJv{c6D ִ]BQy <Z;eL 2|lb[n#u9w3>?́9(^HRyӑ:W}q#ƚ~B\./]@@:m}y2Qںt^'ݱPm 4h^Il}}pG͏=AڳsyqV;[!H7?91";֙.]=JVXخC<}&Fzr?w\qsVvòbfm<cw]6E)hvIH*%y]2= joztQv|+2uFl' dѬȨ;z?]yFT:=6'ȓ=>;YkV'kwy?=BV{t6<"@z{˿7͋91V]74|:mLT giQ Gzgny^kt@o< KV@C ZQI]]sj¯䕶YCj)?f9YiYyX<wI:H9ܱo<o/ޑ/ 93RL֘+cW8Cׁ tǂP(7[i⸥?e9͒hLr,/7٘29Z-]_/9[E룒:n[|Ӆӿux ՗kW SVr /LuرtI FJV\-5`}SuY5;ngc']X<7 DG`Ef7GYi~cfVh<cw]qī0P6pw}EW[|&#yFA;NGXecVtELX7aFz| ~ls~|*+0A~yOt|ƸUn1[.(@Wkilۯ֫@'$vVs~~y͟;D[[21Ct1wǍɳV>4W_Xhtdb/L$Y :XF5u`a@ kWωg)K:Vyo?@<0HZh%ޖNuN,kW9c@꡽=#OXIZQla@FgGQ@=y\E&,~Z;gs;۸yY0}8P^ uNCM2m߿]2e(b;̝]?~;> Ta|8֚kz_<"PN$o+]9M,y۾Yhducdof]pw眶GʣO5例yq ԕ5r)' Kpw?VE 7f]F:2>t1wǵ?;ƞXi5|]~j@#EdJ-LIêJ}yVzf?)uc&iisMZM^Q7Ot8@]+S>yѬӺZn8Zv2 k<#t?4EY3^pa~|L{;orS[I`z}q\F|jU8`M`tXſy'#?@3fjhmkl ǁ';G1{;Gy6u2w͟Oy <{"/HO(GbwXko&\ p~xdRkОs*؟,=@C UՋy*%[ ~!S:e(\E=`oY0}_ QAYOHQeW$qڰTȣ3H hę-5g}(`Gk89Y@h@A/ZiǬ [FR 9 4 #s^ڻwT3x^@"}fq(<v/Cud L90~K\7H doKVXG'GoN* yL͛п}BDŽ h1 *˝I;( -:O<ko!g,dGvWrSqhS[S\J&p4B۠I.k< ?l״miG>#igvlW}˜jc>8#vPg[Q']o<kM~@(PG.iHӶc쁫cR ?Ƭ޾@Fi=cw$ZMO%Sַ 2CzvC#E'_[\{Y@{֭_$o{:ᅎ9@_Isw=V0fc]`3~>-H..rDW%G\{^wk y@{XN͙Jm lj@PƬ J*J٥GSs5tbMQ7% rCgKXV:{yVf|27 >TV/}8*$0VvWˏ'5Fi["i,+z& m3gBeqsJ4j`\{{X{^܅uo 豅 aB PV㗬S7; Cǵ=TAp"@(P L}OyՇ1/\ di iP g$b&~]2N? c@Q gƄ_>WC <^}G)H2b5G}> #[P]yoE?8I22mjo!7i<:nk݆֮*np@Rɾ~ϸv-4RkþwОC[(I&0FY8\{tv[fUiǤ % `So#X^M`Fqm|Ho {<\f5D-)eL⡫}yo@ Q cMcNx "@^yUʄw?=㉷X[9K@ Q crFy^XGK@e؟.q`W݆vh&,pG 66Yu P6a}G1a,jUz<&mcz޳Zn_pOδc0 <jZs^&:f?"D~~8RWX.tu",N6f 3c! % ddi=~_5:vCӏyzZ?ϕ J! dL{vߕ5K)]h 2I7X\'ڊpƵ ݸpƱUm<3 ;6 4uLi} PҚ\unCriKDM'{ucyx^t]- osik` = dN`~vR G|aHWi$-PKHaSoz/J$mMmѕKf:^) \ˈR(Z7H0~V*\Ryݙ>iO{ѩ-2n8 %^,;) $٪3%OO(Cۊ2 qwd1(od&e$ǵ]yt]s,<$?$CVOmifg!޵=3Ȯ8IyvN@P єGlۯv&"@b|䪓t_=چ=\dd[' Q߶\InYOJEpflv8PRJx'c4)3;IZ&_3BD+XĽ d`k '.rm xXp =82$:ΔB4>`d-%c+&Q.t(6{MJgm<Iw?|E~†ˊւZ (6{# :^vfm.YRϟ5%2 ͜5z&wpOqQ}gPpj`6v#%K+O]F,Na5@YVzKcXE7[ס>*f F=Z;,fkSn8PC]i0FJQ}k}?f~Ն  ͖ ̴+c(o/\=}8t?<c(P}NV:"}&B=rkd%@Y`6=Wg=a>~(-JU@UNi}4EpmB++%KSX8q}} D촨t95n<(mㇻW z44&@ PlVq-eRX50^gw;zW ^g*fgfeH4QҨ (.`W;ִ:ø $k>=\@ Pl6N٢О߲Y66b鏎_:zuaw9-+ɘq(6a9㟱Ŗ];  bX8wf`ay7kfcWZwW ^gz0 $@Yxߗ?L֦ V0u#}} P@(6 FM|o<#sY:RP.^(w+͟Yh)3 }'߸G]yk:9ݵGeq)KաP&PYe `3]}y[ixqr%߱qH*$@0}MXk?ۯ6y VGGY-Wi@9έ%J IDATuwa=֎jr*v; ɖÓeoeUa-P gΓ#_]7HmFyc"bѬs]BC6Գ/?A2~+(c;qb3&ٍoz 6WFyYϪ P SJA{yiz Q=޵d){,~֯/_onm]dI8c@(`j/<֡ 9eHc\N>W[+CvS!MKoyd`xϗSҡ}{GuzFÒ,p![%wQ:;tԠB92xZ@(kHryֆ)[#ѳ]5~N=KL '>mdu (8pѬVYXUc'W)==iS[kM6a<R gBP 4Ҡo>EWяTmdMQl{7US?zc J_}zvIr0|qk]tOv>N0y+2\]?pE&ioII/ko/w7 ;c6Z#I@S ķr-4ByqDGvTd&xlWiF Fj-S GMkSnK{Ly֔/y,Cy7VE?/s,X6<@(V!,OjIROQzG\ֺz:]|\ m5T,x¥`P@S Nhtwga yYҌ9i:5໢gM'[pAДLkyֿ6.+jPj#]1'|l! Cg_هSV 0@MgodN}ioڅkf.uګ:,)tyV9w/{NS?渞@(Ψ`VƩqstثSOԗ#(a3q'u[:W)rDd9yn<{Y=;[`dIݻ)Cw\G(# =T37D B{Ό*ZFůGz^5Wăw՞;T9 W Q4)={ʷp÷xe )pϾs>VZY i}{I:$i^]3Uw2A'w2@}5]w޽=,@MÄy5p--y=z3Vnx{7;;ԧ?V2u,rD>m$U||< `ž# vl ;]j.=zm:C(0<@()vM4w^*9>/*byu^S_pEАiIۏ86_|Uḧ'$@^_JW>;6@h@.HGo[ok1@VYܧ-%MֲP4 y3Vy=O5ȷ+Ue`dFN=%I$pPv@ñƞe}iq?/V]ѽ]tJ2絷'Jd@ |~.cF.w$}o1; eSH<لyFZi iGJ`+{wEy[ IGն[%?{r@~h,%sLč^ܱw PQ;7-urfitĪ6]*w=Ti+Ȼ4sZGdzl(ƻ~ }{u r:Efmt\j-ī\F6ct{Lϼ1?eu\P4DqcyY<ke T@埩y#KCk;JiMGWﱶ~U@4gW#]6q7Ѷ|%7Ozqaz7RPL].>=wݷ?_YQqR[9hg;y^\J^3rYYu媓N s•{Zkǥcub:^eŤdhTz1 "[F%}Q=BVx"9;k gkov30)Kl4P@#o:F/znc?rEPW{nc_b;vq(`t)jXXim)R?RPуRm* * :^2#6HzR˾TOx Fˌvtuy&{`QCu辻g^^t@Pԗ1;br[Ws]3bmS];n6Fw=^[VIr@PͻOÒ}wEL>rӤ&UhhcN?R,;R9:Hܘ#ntFS62$cFa|QǟyYO=Պ-e /u񾓮ٺt5h/Z^y#X22:q|Y "<ݶRq+a3ZB*=  (剒imX<E͚eZv^ﱻ[;w˞Ӫ~gl@BPs6g7}w3n<kn)W^JC)h]Vk]ww k jjՄx|ˈ5{N^9|g~!m-:`ZZ{3x a-Y2 ^k X]VxCng7kͨ!z[Ԣ'_ڎRLAI߶qڋIKzM]VvwڪG-U,ZZߩga™Hywu.I^ܡJwq2cw- sK+ڊ5UFp_e< @Xk'&ûxr@-P6H;?Uut*w4B 5q3v*hʧkZJ zkXu?౻b1#U,d=~g|PDN1g0ӻV4b!ЮۏKқUnX`&l BqڋY}6<{ɷ, 2;ՖvNoiiv[/RW.].@^qFzKy8@VˮʱCmG6^q4ݍd(v32_oe{swȣէ2&5e1=#vfVwtWW1\gh[I7vrIW젲>Ϯ;ytmg-vn]Cݖ]*> n-jQC+UCI?rJ%  W&4J*N7n#֍nt:ڴmq}*=kJ<2 r3~사9ٯWGb7A3V1[ ڎ.>^_gr@nfr;gγW.BkbA +( 7\Zl4v  rsݠ^< [/4X ܪf߻>XD:?bCc5; @.<-եOA%q(5fX[uU8ZEv2:QҍHya$Z۲;ZskRrE&Pl_z AiꛫF巓ɣP^ s9=eFt7kI|-^iu{D=~g"rQdZsb>WZӀ=kdC?؄@ƍk/ZcuvyVz_ǽw}|jPW3<v# LÈ$m> Xb5P{{2c=u `@(eriG‚pi5Heyأ.JdXsnD6Sx\QdpgM5_1f*1L6y΋(2clC4nv'ⅵrXzPP@&r`9H,{t[gm6I(d6rD؉3{%ҟ6eq.Kʑڥ"+<@(XI뽎h75ޙrrJowm[P#v' ČQ^oޣ,;}Me,": H*(tZM@@ rqYg9kYUI/#k@2VԢKV\T !$ ߽߽|o.7U^iڳ_wcSYf0tpc_ǂv02zI@G;G1ZϮk/izaט<~RHI#K&^αv!LrYu UkWRБ53'iU:=tVeVycrKKmՔM[>t}Ӏ*űl `v(~ !UitzY)6=NCR+6`n{&ԖRH_8yoݫK0\N;,O?Jg\OuoZ]2M_4)?iƇ:MH~8go]uDD~0++W+Z/#g3-yT?Zr{f5WէfF}e@  X(9"m.T:Io{Sж;~/"[:Gsyy՚m@Kgx`M T][mϹj[oW@)hd+"t=pX:DrFԸ5y)ҎOK@[VgF'єnfrW:wtD<5 l^Y:CRϔЍUnQ9Pc ړҫJGhP! <V@)X7tn%wѝj ٱɚzNZUJhJ?""W:GZ/.y @'vjM9n+/sc]wY< #Z /36)JgZG+=`5ʊ++"b ϹqBDԺ<ƇVD\:FӪ4ϘOOU/oCݷ;ȩ5>Qo;iqp˭Ѝ37κRܱv?cy ';JԉgL.I#p(|eEߩ789U:DV8 <!(X{KFG>u7m")#/8tƥeng67Sjze vtzmwf70rlUƅ(F\+z!G ws< ̶9%@ iR_:C'Vyʈ8>*~nP`EYDNUȩ:ϳU猍2+H)"+rTc3ԗS{[=C'PGGAsBʭZf#c";! P`M,i-)]ZffX6~B~VzM {#4<R<)Fw1K㛖"[x k֝kC0`lW4M0oܲ',"""&'[h|yl;ۿ 4Jg諜..!"b;͟|3-r_ rtBZӟ> Weʵ ":{Σa3>,UgyHڙ6s[g<Ǿ=> 5B=6QV]Eb,ݒc-=:,Sr":"+]=j.?Hsܰڋ^v<MUbmDZUD1)Go~}4:3`4M@ 0=>:zO0#ͳ ǶW_p[\@)F`͸gocO,AI0f> 0ǹ. AA9Ǟ(ml `bG >qݤzC0rtHrt H <qo(0(JV:.Z9>v6 h`UVhJ4O0J'`@u< cR(FPq ?U/RP֒l@A]:<[6i[ Q*rt \ȆPP:yUhRɒm< CK r^:`f >/ Jc[Kg`X9G<}tH0l[7ƍgNԦQ7^@tŌGvWKBa(FXJ&?.i0@M rA#,R:(pum.G0¶^s٭9s0l*R#J`lzW:P`wږ#UDHB 'Ewӎcc@g#n՗ޓ"V:/˘J: [:.r'}t s bblH)b37CL@lЅwDK`@Ɩk1(+`Jo5v-xDtl䋥CS7^@D\]:e*[J`V\{lxӶ9f(\r{D|tAkT뿔N4Gr[nE4Gm9('Em.VXdUJ(a5n-9m۵}ty f5>1t'G|]G/荱LwmOKgaa)yj髶^G8`DY@G\sEJ/Kga~9u;{mVЕ_V_qB,PEu%]:Pw;^:5q}Y筜ܴ/ ~woa q!VDi`.Ukϋ^wM_hԪ^FJg=iWxqyƍgNN [GTiYF?oضᒛKGhO~oOIϋ%3-bo=~דλoY0:+zD~,Mj,o.q,S+׮}8ta#Su|t`x(Sݻ0"yD\:I;Q&nxc(Qw'IU2"y^o۶cTLNVI@1YwL?gA#rzۇ/aկ<>}YxCDR:OaEĵњ~k|K0``xjG8t>Hjҍ7^@@` ZHqFDQ:O/o>1gj׿v>U) "" 'RT&vˆ P:0: ։zJk:^)^O~@7" -JQ0Vycg_ES" FIDڒ#},/MW`Q9~{oYժs#))8G)cil[55}o'S=zqyb갘ENEJ9Ⱪr9ǎVzjxZrswLNVetH8>IENDB`gammaray-2.11.3/ui/resources/gammaray/GammaRay-128x128.png000066400000000000000000000124071413310443000227550ustar00rootroot00000000000000PNG  IHDR>asBIT|d pHYsItEXtSoftwarewww.inkscape.org<IDATxyչTwOL Ƞܐ[&сWQ#:DYE"@t5&1AIc4"(G0,&0tWQ=3ճvuO/?S|y (P@ dU4f?I1CU5 OhTa?~~c'?ЂB|։p ?>K׍K΄ԟHE87Hon.}.YY{ (wJk Q wtn @o_z^7\bʟv`TgkfFm={[SKqUz[Bw$/޳+\0-d L{賑Z߭(5\|BxKPt&aeScU}IJl '^!bx{J=kpcpb(91wAmmC~ħPA}OJ* jo}{eow?r,(gRtr@ D@DM5ןjf-oƧw/hP@cJI&|&-!]FI:#k[/tٛqTUUۇ\(@ D2>پVba~3L>:W+ƞ-_f=z;]0-) ?#FC@0:0"5hj<7f{ >QBC~d"nKF3ȠN× 7b9ؘHf#D*"acM3aLy`P;j!C+vz%̐iY`toI(j}V5W9u j"w9tK>~YF]Х,u؝;6qw6?;{ŇX15?z*kVOՠ.<0ٽZ[=z񰯹mc2ZJʝK|bAD:<z[Z,|Jz$)gVVsZ>H[ P9>S@Q'q洍n=K>*_V Jfpp-˼yWe[WH[ X$Av`Wk4oqOGZ'.enti >Tᜎ:o #8at]T7E|$& e P$Z,VF(i@3X1:h9F߻9mѿFmg=4wKJH'LhDm@ǩ#K#F,Mnv`X|DS,eX}4Ƣzz|Pv B?*10?G~=nnZh?;d)=?/ !]=Ի=M/ bC&4-m$^;0+'-|{UE$fLC)'ܸ8* 0DD;dp3͗~7a':OV%ݮ_08&Ž!bOBZsFMpgrW`ZVv65E?Ft3F65Ik= IGfjLnKk $ >xE#k=M#w;(-2]ߔGqW` ij^C"tG\c \d<:Z@Q5Lefh%~L/''MYC[#Ϙk:S"cE{{P^A/xV/8ܩ@e€@1JK}:+-k_ov;]%j/8{Pd$wO绂'wLˮ[&-vuGpGlAX>44kn9n@0LBG}\gQTW M44o %l!.&Fg51.<*1cLӴ[XfZN7yhkptro;_ VIקMKok fS k@2Im0/!S(Jm;wq]5sMv:NvN2MlE^xƏܗ`ׂ_M.>.7uit_|@%G9SUUm,꼲w[q~}9B%[F.9?}X;Цm;hXW0ϱ+ q|y[rw'dҮ/$l'Q֚<(X׶*sCFϭPu;UM.4J@v+zBYO X^g3'r!+ab[s7TZw+.Ggp%lϠRSsn"m[],Գz#_3bOw[Gv@.QJM%P/b:3Kk^}k`}Kݾ/#5X4&o60 HOM%y#N'AAsC@saywOD !~NjA;$ (9w.}mC`y'Ix%lU&-Թnp"'L9h;~hK!84qOF^ Q;^j!!vXu*qS\^s8|7J 5@V3cѮS=5yh4-["Y/Kv_tb1BQ‚ bT+߾wΦ$7vȭ7]I.iQӖ bd^lv>IV 8`ϊ*$>Y;ݲ/x1~F#L}C_䑑!bv&x-S*Y-R" F5情=vag]2ϒ%w̔[Oh`PȻm4֙/gl$&cs׀xvyM)/ &>?4}\GV @ $"sdJ2Zƾ7 NAT;MY-hJ=HV žb=}(kB~ WW7oHط+e Y/x˷IG[rE#yp[鰯/s'VUUnG-#8? d<_u55U@ (ЖZݺIIENDB`gammaray-2.11.3/ui/resources/gammaray/GammaRay-128x128@2x.png000066400000000000000000000167331413310443000233350ustar00rootroot00000000000000PNG  IHDR>asRGB pHYsediTXtXML:com.adobe.xmp www.inkscape.org ǦmIDATx] Źc6 !}<|ƠDW41ƄQH)y<$[55p/Ye=f9jw{j @ (P@ (P@ (P (9S[$tsJvubZwv/nw}Ie"f>RO+̙WWVZj$@{IYO%?V<#߸Ǫr,sײӟﯛL/qE=V im㼲[KND}9[DT]g*dm f`36c3&r!NU4hh̵M:6E2e7wF-t& 78..X.s(v+SxX`@(cc,o&bo١_T (A>~0STU8\E@uE?q'sU;hu΍sp3*}]R][S ,fJq*e\ }BcaAìvCQtYUç.{i55t Wx_ʙZc)=D`YmPN-D"mx"6ȕPnʹ poH9Q HP]6 ω'c ՠA0ΆSMәxtZ]J EN@mN6,q)&@ƹo5r]v .-$V$\4ðڶ B8em0r#E¥ب9KB#ȈCbcgW3evЧc 8tkOAE/Y^HW k#uCNPy oepcV|}Ur!͖JfY}'rG:+epixOiiaNY6w5}a "]jgU;*G^(:F S-F4P?KZ!]J'gTG*c OAN!s8!ByJu fU.Ӝ;k쮵7X ٗT`7Wq8otTY[3Y^ ? 0tD)I1"A̍`ltc"ޣǕL0YSWl[TQVt喚:X'.#/9wN"}SS>8FzvZS~ ~CUuJc6UNŔ2d\nګ2;+vE!u@HDMߎKb1O6 l"ѝu0f#U` 2P!XxàӗL5n&oӄ b$׮=;DJZ6.I$EUHC?yP8̧6+ DŽc{l*B x}yj 0GQW=be"Loȧ͟Z}]M d$8Lgb*C'S/Ϳ=0E"DI~4O"с4GQJR]fپeZ}ec/_1^p)DΏ^ w 6}\F4t!)p-J?MR/Bz')Fr9Po9%Ώ(/t[*lԛa,V] {wP~(H% #HSG9GN ?sOAY5@z끄CƊB "Rf_t7qܻiyF0j`WxHl ($ E_*Ґ>K~ /^*Uk?Kz:\?#(.[QOiCRVty_0bݠXlj`d:؉kaan]*ȵ413"~̎ňCGWu81>Q#=@|?a@P+y]<$v^gGyLjji?yĿ04Z:y;;0uv_D(dq!A)rb8'[_T^/b/갽(< eΊ \kk߾Q3ә6`a@~@{6UO&PxnXߠu(ugv_~hvHGuteb٫GwwY̽DktEl,-iMOWgMxz&"_!TVwsBѧr=ܟ=1֣^"A ]wczZP}w7l|}%[B1vܠ5vTh=!Csח"iK"fvϣ.̰ϝp }NPs@$dRZ**g\v?=g|>60Ϗ%`FTI0;S?Ls{&giIGʹ΂i_ d31K"GR #~|/'ER ?I6'f|\?h$W{H;;h&a@"ֱIu~1aa/ZsH66/OvܴYʠm랧\s}v"a$v\go"8^|-[1cǘCS+9NT˹և-L-+p QXGKFtse [m6`\#=@~x*e8gF E=-XK Z񑻧}qur<XTQ}YXl4j؅'"t(t Ub1֐YCb&ǑܿX`Mc'OeYv=Hٶo;S4RmEnt7QMRg01#Qtlp,/:tJڥ,|ec3~`?{nI[w<E@@X1 0JATtJ&Q rOsĺ[?x|zQ7;?Đ2z2 /"o+؅E҂/}SҠ8WngFKẄCȓ7^E^J1?8F[OzSE 04y%CL7F;#pKnf 5S٪Oll &'V-QT/d?K-c^Vڻ8_VQ$`g$bDpg P@K{!vܓ4+OsVMF' O &ʾ.X/sތ`c uB'> ~yW ֬9Lo3Q؁0tˑȅ2rUf2zמGLjg~C9$>LC{tA@؜y(83NixHBGwږ}S^wX?r{A=QAW~S/.:g;ɕ`{+7"MG~Amt]o|e /~2E~)\ ?: yL ^" P<[Վ>XVC>B m^=[j75X\M ,1h=_WJ0} ARQq܅|Sf"`􂳹yblc-_UGd9Fwifl~?la0/(%$F PBB,y#<*73 /IQ>޻-[ ݄55`tpo }ٮbr{a!T6K-eOW9JpG"h&g+_e.d?Z?0pp{FNb>W]˜[b.yb[{!@[XxcbtUMf_25}v ?7:ү13ƢM`ǵ~ _Z. Ǽ~aKo_jpﴍ:5P/'}BRǹ5rw^s3Oq/Fۨm\fv[u+DxLKKĥcuo?g+"T^JE3~^E#ѮŌ+GɋC՝~ˑ?[vQ,Nk~%Xa1'ş&|Lcˎf%9Wey@NF{>K.9qQl2;N82 '}axA̝ S~pc08Ԟ7 hF+(oc'&N~S<óZv+ )%]0 H>r\f㝾I[XWr{U`ǞZnN7B<`Ia@0З_"A!|+ݸ|f(Ѻ1f I@tlf>8Ŀ?SlNSY] +LkLlL| 97 2O\~ be޾w&ܰ(2Bcgܱ}" Cȹ|d`PncW8&[?1VMXQ,-v! l޽ VGc 4ih<:q2ND/kLd%.^Ҝ:7ȩ!ç,M)F˼)rst yF[;nPx^c!H-țS+M̧fMmZUG媪jmz8Oɟ3za .:@W<٪pb,@\z P\ȟ3;cs8v CUmR\šo|: ' И'ý$kU`pP.y)8gIxC\nB C@I14lL!Os!;44]6WV}xOȡu\Δc$N]@8vivG<@*iz֭_{yՂt;`sH/=H , ZB7!S}>#`f%<F#E`3 Ya{^?Irx*m־EtEx!V\3O"k_ˀ1=jZ^g?/ZL<x: ~xl֔ƚk-6jB2'fP& @Sm9!QUolYnuAKn>y(:7_ϸuqCMF Y So)[+ТO|yؔUp 쁆< &χ69H-}S2~44svU[XIX2@}?hw1C/L"_[rnË0!P)Ah:r.͇c[˚zpmN7M|?H݇Km'pQ_^BiASȥQו?] 3]7t5lM P R:]D\aڍN# ﬘9ȳs,i7:8wbg=p51Y*tP{YgD]Vã[gcߟ`4$;Ӝp"cOyhAN3>\ 2ewpÜ!Wvn֯[WMBm ɋ## U (P@ (P@ (P@R0>(8IENDB`gammaray-2.11.3/ui/resources/gammaray/GammaRay-128x128@3x.png000066400000000000000000000167331413310443000233360ustar00rootroot00000000000000PNG  IHDR>asRGB pHYsediTXtXML:com.adobe.xmp www.inkscape.org ǦmIDATx] Źc6 !}<|ƠDW41ƄQH)y<$[55p/Ye=f9jw{j @ (P@ (P@ (P (9S[$tsJvubZwv/nw}Ie"f>RO+̙WWVZj$@{IYO%?V<#߸Ǫr,sײӟﯛL/qE=V im㼲[KND}9[DT]g*dm f`36c3&r!NU4hh̵M:6E2e7wF-t& 78..X.s(v+SxX`@(cc,o&bo١_T (A>~0STU8\E@uE?q'sU;hu΍sp3*}]R][S ,fJq*e\ }BcaAìvCQtYUç.{i55t Wx_ʙZc)=D`YmPN-D"mx"6ȕPnʹ poH9Q HP]6 ω'c ՠA0ΆSMәxtZ]J EN@mN6,q)&@ƹo5r]v .-$V$\4ðڶ B8em0r#E¥ب9KB#ȈCbcgW3evЧc 8tkOAE/Y^HW k#uCNPy oepcV|}Ur!͖JfY}'rG:+epixOiiaNY6w5}a "]jgU;*G^(:F S-F4P?KZ!]J'gTG*c OAN!s8!ByJu fU.Ӝ;k쮵7X ٗT`7Wq8otTY[3Y^ ? 0tD)I1"A̍`ltc"ޣǕL0YSWl[TQVt喚:X'.#/9wN"}SS>8FzvZS~ ~CUuJc6UNŔ2d\nګ2;+vE!u@HDMߎKb1O6 l"ѝu0f#U` 2P!XxàӗL5n&oӄ b$׮=;DJZ6.I$EUHC?yP8̧6+ DŽc{l*B x}yj 0GQW=be"Loȧ͟Z}]M d$8Lgb*C'S/Ϳ=0E"DI~4O"с4GQJR]fپeZ}ec/_1^p)DΏ^ w 6}\F4t!)p-J?MR/Bz')Fr9Po9%Ώ(/t[*lԛa,V] {wP~(H% #HSG9GN ?sOAY5@z끄CƊB "Rf_t7qܻiyF0j`WxHl ($ E_*Ґ>K~ /^*Uk?Kz:\?#(.[QOiCRVty_0bݠXlj`d:؉kaan]*ȵ413"~̎ňCGWu81>Q#=@|?a@P+y]<$v^gGyLjji?yĿ04Z:y;;0uv_D(dq!A)rb8'[_T^/b/갽(< eΊ \kk߾Q3ә6`a@~@{6UO&PxnXߠu(ugv_~hvHGuteb٫GwwY̽DktEl,-iMOWgMxz&"_!TVwsBѧr=ܟ=1֣^"A ]wczZP}w7l|}%[B1vܠ5vTh=!Csח"iK"fvϣ.̰ϝp }NPs@$dRZ**g\v?=g|>60Ϗ%`FTI0;S?Ls{&giIGʹ΂i_ d31K"GR #~|/'ER ?I6'f|\?h$W{H;;h&a@"ֱIu~1aa/ZsH66/OvܴYʠm랧\s}v"a$v\go"8^|-[1cǘCS+9NT˹և-L-+p QXGKFtse [m6`\#=@~x*e8gF E=-XK Z񑻧}qur<XTQ}YXl4j؅'"t(t Ub1֐YCb&ǑܿX`Mc'OeYv=Hٶo;S4RmEnt7QMRg01#Qtlp,/:tJڥ,|ec3~`?{nI[w<E@@X1 0JATtJ&Q rOsĺ[?x|zQ7;?Đ2z2 /"o+؅E҂/}SҠ8WngFKẄCȓ7^E^J1?8F[OzSE 04y%CL7F;#pKnf 5S٪Oll &'V-QT/d?K-c^Vڻ8_VQ$`g$bDpg P@K{!vܓ4+OsVMF' O &ʾ.X/sތ`c uB'> ~yW ֬9Lo3Q؁0tˑȅ2rUf2zמGLjg~C9$>LC{tA@؜y(83NixHBGwږ}S^wX?r{A=QAW~S/.:g;ɕ`{+7"MG~Amt]o|e /~2E~)\ ?: yL ^" P<[Վ>XVC>B m^=[j75X\M ,1h=_WJ0} ARQq܅|Sf"`􂳹yblc-_UGd9Fwifl~?la0/(%$F PBB,y#<*73 /IQ>޻-[ ݄55`tpo }ٮbr{a!T6K-eOW9JpG"h&g+_e.d?Z?0pp{FNb>W]˜[b.yb[{!@[XxcbtUMf_25}v ?7:ү13ƢM`ǵ~ _Z. Ǽ~aKo_jpﴍ:5P/'}BRǹ5rw^s3Oq/Fۨm\fv[u+DxLKKĥcuo?g+"T^JE3~^E#ѮŌ+GɋC՝~ˑ?[vQ,Nk~%Xa1'ş&|Lcˎf%9Wey@NF{>K.9qQl2;N82 '}axA̝ S~pc08Ԟ7 hF+(oc'&N~S<óZv+ )%]0 H>r\f㝾I[XWr{U`ǞZnN7B<`Ia@0З_"A!|+ݸ|f(Ѻ1f I@tlf>8Ŀ?SlNSY] +LkLlL| 97 2O\~ be޾w&ܰ(2Bcgܱ}" Cȹ|d`PncW8&[?1VMXQ,-v! l޽ VGc 4ih<:q2ND/kLd%.^Ҝ:7ȩ!ç,M)F˼)rst yF[;nPx^c!H-țS+M̧fMmZUG媪jmz8Oɟ3za .:@W<٪pb,@\z P\ȟ3;cs8v CUmR\šo|: ' И'ý$kU`pP.y)8gIxC\nB C@I14lL!Os!;44]6WV}xOȡu\Δc$N]@8vivG<@*iz֭_{yՂt;`sH/=H , ZB7!S}>#`f%<F#E`3 Ya{^?Irx*m־EtEx!V\3O"k_ˀ1=jZ^g?/ZL<x: ~xl֔ƚk-6jB2'fP& @Sm9!QUolYnuAKn>y(:7_ϸuqCMF Y So)[+ТO|yؔUp 쁆< &χ69H-}S2~44svU[XIX2@}?hw1C/L"_[rnË0!P)Ah:r.͇c[˚zpmN7M|?H݇Km'pQ_^BiASȥQו?] 3]7t5lM P R:]D\aڍN# ﬘9ȳs,i7:8wbg=p51Y*tP{YgD]Vã[gcߟ`4$;Ӝp"cOyhAN3>\ 2ewpÜ!Wvn֯[WMBm ɋ## U (P@ (P@ (P@R0>(8IENDB`gammaray-2.11.3/ui/resources/gammaray/GammaRay-16x16.png000066400000000000000000000011001413310443000225710ustar00rootroot00000000000000PNG  IHDRabKGD pHYs;;̶tIME&( E|IDAT8˕AHAٙݨP4V<zlMHEz(`5x*X &PZCAA-{TtӃΎi1}ȳ H]ٞbQp+P -P?߃c-Qh[qo)A0'F2B+cٷ=>ntnt\P=0cPF)ӊ(S_P5=\ߙ;7s~']2r}=<پnhn᫥< wvے5Z2(t0 ,gL oTogWj~X2cWCU>~ZU4 )M͜cR[ 8P+|QRfTPsD^ ^BJ qr)PVLa o@ճ]Aa9>$Oh!O _IENDB`gammaray-2.11.3/ui/resources/gammaray/GammaRay-16x16@2x.png000066400000000000000000000023351413310443000231560ustar00rootroot00000000000000PNG  IHDR szzbKGD pHYs;;̶tIME :<.jIDATXý[lTU93cu#/؄*BJȵM< - B ^J h RFImxJ*Al0B0hZ.m-Cg3g^ˇa Όs^g}^ 7W/iQ5__;(mrm{#.U+1Lk|iu6l.p6>+O j4}.GA\QBV 1p![ ͺ >RB(sFMR3ň[ Y Z]OO0Gl.p﹥MHOÙf:TRͬ ϸ\kh!tn!dx WAi0!W# B*(eB(tY.~.\bCJ3a=tvńpuۈ 0l Sb U~ D3p;9I)*dxpai8Uk)k2v)$]YސB)2F{]ܝBYY2ktj!D3S>=;zD~NuV/olhfƦObFCyA -mμqrW˩4UJ& ۤ&&MnFRha7'ē1C# A`Mń.u)`Lo0WeBG*|`(. v=ICG#K61\W!#yTJ/썞L#c:YB@:Iwغ+x*% m`>CZˊhm!% %f l0S$!-MZYO @=3"=?z"ݜ!0@-$P u,h#DgPdNRi!#֩!ᑂC ALH{z# eϜ9gz D,iJOyvYKf lf}2[1z!YO%֌N&YO𔰁cy\^e݈!װo7_ (_wуYdec!P[ }ks)͖=SƼzC)eՈ1ē kR+6W6*mS0eF-؜vˍuǐwy@NDԃa94ј 8G߷ǯ nlS~RNf0d8'1f^+D㡐A@ 1l؜rAN~(TzuS$zN ΂:J<2œ4u#T\4ʖ 6Gq?;Pk?Օ͢nXX!- DtfH(k'Ѳ8?~_ +>0@ZCYzV4KJ(K`uh^DDp!0X8oR= )\tal0DlC;gxv,9ԅbbmhQqC\UB@IOlmPlkN]<1H9Eapok7?|{΃t ,@ǶmKiP!ػt,R.`ꟽ~yf$F>}y(I̙Un2?@E@~?5O+2߉$N WM5=2~m[UƎͿyS;AHR !n^-4p'/~[[|#d |;hBIs߼+}~!m`t UP}vw֣YՆt* wD;߷X)=xw,"\ 0w횶"r3@(Q%]Ef \G1˶ 6j|∵2DRaݬc.`h:{me6!=ju蒖f59gq6{gIENDB`gammaray-2.11.3/ui/resources/gammaray/GammaRay-24x24.png000066400000000000000000000016051413310443000226010ustar00rootroot00000000000000PNG  IHDRw=bKGD pHYs;;̶tIME'IDATHǭYlMQ9"RѨEh(ih T<`JPC+"kJ QŋHxE쵼hj=O@gL *^1YkTHeXMlhƦ9ݽrX37-1ّ`3/8ٔc!Q-P֮hw؁ s\ҦsIJ5Je6mܹj:DddWaN=X|Ӈ~6g8%!_J! TaAf-O4`{M}5 nH{mFd)8 7H$ 9'oR -.z,/Ϙ}uisQ 0>80%:G2>ez!=q=fBH[W ”VqG듷\ ∨eGͥ̽-F,H%_Wy4$D:qA12T8DW- %Eͩ HZF n ;\7IcɚA^N>`R ̈́>I~י5=acE՞O/Q~*Snlg~[U|D?$UV rΆ[ӟSh<قlmyp h%rE@g }'8sj}a#nZpO*Iޗ̙mۛ@ŒdW nz[YVT=k2 8 G>xo[eWD8X[AwY<_ tt%I+(vAdŕTۗ\h/C>J!ɿ\*-m^ pEVw՞gELte~CU6Nhkꭶm'qDoycH*;;_}1Uk+>!97ğY^uv_ϾGD+ HAO,:0lFGaripsu+ïN^A̟CZNd >WPvMĂcV؈Wr~lWnMT5WeI/C.RgB۷FIt?zhSja鴪T,n>w(ެPcGwF /yu+ceLl~ѓcv HSyJ6sNUɞҐĪɜ ϋsM#I8p pyn\4Rˢy! 3Pf\q*V`t\oGNFzJk?GΆ`1$;>@& {,RAH\("r8qt&^DRGJ\ZR yF-np +' 0?*v!9b$ 0}]+7ɹtm Wr@x/0!O"2ؠ'g1f7xM;VH`ۗepL0, QѺUHHH,rIENDB`gammaray-2.11.3/ui/resources/gammaray/GammaRay-24x24@3x.png000066400000000000000000000102161413310443000231520ustar00rootroot00000000000000PNG  IHDR``w8sBIT|d pHYs;;̶tEXtSoftwarewww.inkscape.org< IDATx]yT՝~UUW .,èE1A #ظ,@d_!Gq!4QQD'&42j`B7MtuWUΩu]}.w:qUb`Bp-qo"<(%9op MQmI9ol-ͩO!Y!-3 ׭=BkuvV0^lh5%d3aꡝS7v>&B7xrv <7H0N_Y+`>oXh#I\q( - `_keIytmS t#0LApڪ1AI%K#;ge[ @!x)K>f?+%޶4)@ &?~I |OpТ?WmmSP1InBޘX!<7jcTFgR趶)@P6WNW]26)@#Uc ʵfi0׷mwv[,;r>JN_Pחm8ѐ!}nFF&;qKNYl&u 7GIGrS-"SCl>n/N@/N]JR_5T6[y/=Ե2NKHlp( @пQP'w<:Tr ci`*? v*a6v*R0\aZq;Nf)Rۑ"(l,e)& MH6:;!SÛ}a|=Muqvƺk@QƳ@D5?yilgxYiUg܏mh$h]xi'VB; n̊M/;WfŗdG۱E3p?h0X|]Rwsʖ^-7>ݭ;E,tMuh N?5Adldџgs察[T_-N{k{^>.aܳ\^N\VhUnq osYp+xzzO= Z P.ڼH K!"Vi(޹J[''U/ *|]Xg?剴/e1)??>pדc0ŕV$Ag9]Q|9nFrYyn]V#5GYE= &RoAҁYZP%ԝHPi\H};a޼Ĭ0SSPy笭[<c]\#J/NLz Loh 57Yf f}^2M}Z) uBqќҋs\ 6 ,rI @pI̐}I%S4柜9uy1U^e5:Fޙo 5_{OOMNhJ9 MƇnَ&₄h|wv qJp00Ce7@c Q}/?4z ' ^^C.HͿ_|m wO>* ;.G?9GUA{I{Se.L){HJC5 P6 AV */<]2#@B8SCہԘmɵeoI MI5|!WHv[ ԡhƫu\G@Ə\r%׽f7xY;doBvխjv.c?Oq@pD^]PPJNf7x@hˉs77j,<onܘwr:T,l..9YO_DϷ.v,A,̭Qx { eQ4+Z*tvք545LMP%֨>xlY6' ΈzDN'_5*,ż.2}_3M1!Z@DGM+iq2Hh|&#bSJ= _`.e,@*9S<77"o/$jswx&A%< @oJNY p-=L."Z&x ݨTsI0>[Ns,!\noxnmޭ =.]At!~話OQKM<7X@Xm#4:2d nV j&?18^(SP>6\ ߘ@:!q_ ̵UdJTA Qp}dTa테g{צMLdy" y<kײ;t0ոG|+BNpx0ծEMaƩ  5Ab| Sx-~_;^[|?$pyCDm {V;ڂ Ο5Xڡ@ƭ0P_<3Z +,=E@W"Xf!8c͕{{VM,z_(t@ θLr);UJ #]Q~bOlw(eVwmB%x֦Y =8;j "Sb#"5NlwP<`7|]6->-WS,JzG'؉SW;1d/>P8熗 *) i/&+ W.ga?2UD. 7N4߽|^\#ې$Fv%7XpS[y8v(4dƂbچں:*/&'a;? Ϊ74IG׋ǐehp7-}D3z%ʟ7j<~NE~vv Đm4 &K"!(?j@Z|}t4An{MlƉ#-{@G~$!rQsSlEOg Н$SW;(roqgiZG*mSW_=1M^?/6$]jS6! v:kt>Z"P&Tc1{@{;J~t'лϬxoʹGEZ-C@pUE"v_a`j-$mFZP.J6qe~ǒq*rJ!Cۑ^[%*#bO]$8xGCC1W{[ꨥi5 JٷfN>클!֎GDVͧ=VX; oru kRB*H+@+M@f_7/#NDZ!8vQ`5ZYNR>o€*O^/mlҒIENDB`gammaray-2.11.3/ui/resources/gammaray/GammaRay-256x256.png000066400000000000000000000273641413310443000227710ustar00rootroot00000000000000PNG  IHDR\rfsBIT|d pHYsbb8ztEXtSoftwarewww.inkscape.org< IDATxwTϙ^Į`ԀFE؈ tBD4TAWTQQyT4*`l$DEQtܾ3s1٩wvn9sAAAAAAAAAAAAAA*БǶގTt_GTo*=B&.yli?@5fBg~@@rBd0הW6VPgs{|0kE%_*2h_ͮs=*OpiuNt='Ԙtۃ30>cWlU?M'VgoP#Q}v+DKsu*V"n=bƗG>rL\3j+MYglkmź4m,2/{Τ kRee7"|'4ŔŒ|'{0mr/Oի;ٽj؟D;2囼Ug|P6@rk܆qMrbܤIЈfE1lo| ~SdhVzZ2GN`I._5of; 2͟qzq5A8k JL`#'f3l!5hV#7x.k5sx5㞯_ },g~6|?g3l"5r[p%~yw@㞭ONa``>l!Y3W?2A@/da Lτ_KxC#:4CzˡFҬY+L,Ř|/loz. j (XuVö|޳ДU0}ӈjxS 3g%oO zbmw.665W0<:cx"5;P_ ޕ1}t9Hj9kt!:8q24Hg:ra6*"BIrSOD&Sz #뛚cF;MMi~5"BIYohjyҌ \;?_$%Ǩ6nn'`C󇛼Uۑ~  ,D 4i$d(m[ |fPR~z~ !g~V_xDٸNihi 3lkug1#g 7YJgO,]P ag~>5գ/S]{jJQON?rzk- &e/>3A@( .8bng廜] D'<%wGCS һ௏BI (La3" ;hi֬m kie-^Zh2k[Yv޵9~ł#ASDF!;Qᒇ%0Iazasg>Բtغ[~; 1t{us) ߲7eC鉀0",GŹW8"]iqޜut/zڿ]i%)}%o55lvգ@{g\'m7DJ]JolpޜؽcsKǹnsem%}Ou:łt8b?1{sd㈟a9qas2F^oC_P]# @*ۼ=YaqyҍD]8fUz&[[ѥ{t!2lb@l[| {}o[:-Fk6 M Q/ݧ6hpĹ}flJr;Ď UsDϱ|E| =x G}Rn6{޿O~G@A>Ƕ CN =y̍ u8D~nfv)W)a5j%BA:H~]2͌|ϝ? "}ܰhD+yH됢i{{Wƫ6 5{10PKQ7^ĭ$i643Kn^Z/_ƽ|_SPuڈjڛ6]/Oaj]6g'i1=ngC͆r"P~q8Lk167޻vqnkWz C)>=q:B߲+ >c'? '|KDi'7~C.[\Ih5l2} thٶV0%Xm"md pH[XͶo2tYw.6~x}xIcy8rA|mt͇9i6PXC_xJG~Fip0_oݯgz!2,`9e֏ nN8)6u9LkХWo/*v@$؜yƎJt"d|_tRW%Z"O oOkثfYύg%,+@mڞ,c ,cǧvF<D b|c3_mS`hpPǜ` +ʬ=Snf 1FG$ٮw3#&f#.Bإ-iy}}FzVpԥOu%2nPTic^jz=[mjVD#kQ~e+-`5[wj#<.p1&0f3 8f0`| ofblsrso9b_#ӌ2ߟ~^};#. + 5VobwpQGr5ew*qq%fdI(250}?;/l7jjMݰd0_A` J Ketfv*&UMn6t`li6UJ :1t(5;iA5KաGMyXT4zv=|G6&Y2J€>6^g/lLT `Y=8.tqGQ\ظ/9i6fHD**}3P&?|e g<WPRr m0߈6hjb] . ?R3f쨌UgK6kie˦6jg VπSEɛ- щI(w.YA  XŇV__;Wq+ X p襏 1@6ٻot]6~'sC~~f,ў!R;"Nl8 |a'ZkV/꛽hh9YƎlE| zR20G[ ,">qAVd f[+dXȒ[Oc7ωw,{~LCw($ u+Wl2ZF^#?>ݕy23!{&c(W> lnQQ>ɯVt1ʆUn UGIvTݜemX~nΧXzgL|*ʼn@e+{e]o=^ ۚJ"9F|Ѣ Ča`:*}3h2>˲38+WᵏY H]'[2=r)5^c v|x|Ɵϸ˼j~U/1#ߟC3_dܟvW'W:;&lvX|]̷dl~q-`P!{ C36g숰)_7rd~}#z?n̳ub[1Y)0 ]`_=mՕO"c锪;Ot ׈rz@v-*8丩/·Aw8dǧr2 /~aE;T(yv80I/T7``c?"fA^SCشт^]@"s^=hqΌ@lavb6-[,>fgQQ͙1^܍#ӏmG^hjeٰ^,a)ypZ @?`y^ 60o;6[ @+%*ֻݫHXc| K_#R_Y-52Ϧe?s $ݘ?hrm.t1눝~bIlɋNa#O=92CR0ZmahrڑP 1 Q&@.{k•xw[H0I>hsEc%ZR@2V/-;,x#70Hv'afb踵AA75 003Z*w~. u%p]zH:7O?hBb~I j,M*{ES?+;2zكJJQk[1o'.|tʁ?I 'o5.g6wp! fԷJl*eY{F۴?je9?8pnI^jc¿܃-+6,"!ο*pH[ eYnн(7p`}Tx&5O27LNXD!bmkhAu.{SlĔ8,{0CQ l_,е‰z[Iy KqUKa]6¨\޳s\- u L.Bjv^=zVb^س.aS[v\}LorZXcg 9Tku9|76l\/PL/[.G>M_f1. Uev~WW>lo޳!z/Cf XBl>Ѱ=:k |Z3yQu1(|C#6c$MP+dÎ~;w`tn`4{4}AM>#6Ռ1n7|%h6TlZ+߻h KV~8a&{!no@m)7",P[6^z D"p~Y{ma rWW:Z7k m[p5l-50~_0S*-ٰM0%Wu]_ik_8`H Wf$Dew^>c_ 58FQ9u^9ܸ7ƋM?BTj׍ǽ0}0{=MM֭GlZ6f8=j|@xՐ/ٞIcrdsGt\{̰X-ً㧅'_7Đ=m:f^Od.S=u!3GyP}XYǚ3x3x`1e,WtvIXmmdj>,#oM;b~mil-6,wL;j/JDp?͊ ~߅چ|dfm? ]0Ϭ xǃQf!. 2CK Qb= /Uhpԉ3)aS: G$~JlG}Utk33Xc1]zX/ D| Nv+']LC;+ A{9!hke~Dց/vDbveO ` Ml'9r~| E"gS5\tttIH̤)0@͵Wv<%.J ޸!`0XG_%2Lk `x(̦+Q7Ex|O`H РI`ؓeT lL?_F"wFǣCD? Q׾ܕqJ7o|!Xnb?i7@U:7izX V+Jp%jYOٍh̷y2z!X֦Orqv5{$PѹHF?䬘 9GB,}j+#%o#tֵξgl\<1*,KA`(IKǴd2B^XG_$ 6 8gOWN`="AXڞQ ?1?nLVDj0V?gX7xaD8?? 2~Oa6kwtg=?*݈K8,D,ɫ[UZ_f F7"%y{zfRvm`g ..*s_g=#`Y(Fy3߹cf9@zBN69s{VMUa,OVbCsסfH0vOOw#0;kۯD(,O֍G9]֓÷L ~;y!X&@|6l@D~3dG,-j?lZ7@DIo=߹]"Ac G\N57?cZ,Q;{!^A[}ZPڈX]\lн021bFmyZ"Xt-*_5J>7?O}]Y6vW 3eIDAT2pW<PtF?~9Z'"g9|yemyDȶޖ^h?[-N~S!O`xaٴZ"@okV ݢ3v 3@#LwCr D55lkvj@}A?| tc;Qy5X  2L \ŽdgD:2tm##}ηy1R;"/{(;&8*'L>%Of @ژ3ޙq<(3p@pIl6' @ؙ$NA{޽gԦ<) s%O좁p!6ƴ) S< 41w' uU7#`Q;6Zf17i_<,B8OE,Nc&leD,Ht6nݜoz,ބ@ 7^@[.  k`ȍ :.R+ iXpg&"|sa LR2" \j&G^*7_):)L"Fx"ӣn ̰6H" Dؕ{"oi?`uEcn\ܟ:9aXf #P 3 8>ݱf {d#&(qXcqzLispu/R)۰dTW @ ]_bn߱j?8AL|AXSc4v237Ns`/ﯔ]lƏяZ-"% 8os`{Mmo=MVd;r<X堋g``z߶2 #{O´ɢ ED'4yml:c7M2iwɚBQ!P|_F~(x[/^w=^KfZ1%َIDn V+ZoJP1VhY̢~xO0F}QEU}Q O_)î" GL*"%+= { G_xQ{P kh|vi <&Á-@ q3"zS34q1P5㣕bjFu#(pߦD,@ ARb adz.:..i6|tqxЭKeE(]MN ]!;LjX/ŷka7гSY,@P@'?c?K!G?ihO2(VET`qt_ P?NZ_ ?oی{tnDEqDG.]2amʈ =B (ХZY &E," o3یXW0N]aǶ&Z'^TIMbEHpύqltU˗7z⍌1NX`UDe?NK:'v@Xpߔqt|^ OaZjEqQBG'Dz{|FJ ^X `YDu-{H7?yfĢDm*D!щ$h'{V`[(-DcfU8-y;8.3p̮3 _@()D osiRSmW/=L/h6DF}\g&n(2T[2{EDـ6E"E7NN6:Tm߸_MC+ư@ -`QD o8ilkMOQZ|4ab?k%`QD O~!~@dI~ N@"P$q;cFcݛi>s`2oՁ\R("EQڃMx;*疟ӱK(D2zt+ xmP 3@Z>$2Ê#ҵK(D_o|i]>Iyj_h"zq( >Mbz_b`$&"E1OpύUX"o, vp_0?@ `aD _vh4!|btIFEUXFTvUz0&au>>餣BǁitmJXYo6vn]M'>4(0̪_fMoץMK90MGPzg$Db&AupEE: 4ۄA!FOS P 5H(=D Of5P]F> DW"}&ZjVG{ѝ:g,)x&6t( 2?0;MNvQ`bM$~.CBi#P@X1:ikj"8=T߶LjmB) ۃ վ}71|݄FM{z e)xLیcPA͜Zn.tc0~G&n̆aBq#M mABEF3"D| mxO!-#f6OB&uD _?OY6D#"gY 6C"'s ON)cR"(g6s_@Y6fv߲|PC vU9 v!llQ]N `Hob>yl|Z @jEUq󜬏 V>9QwPDz6۩V)V&铓SzB"PD3au0skW>q\#?ThD00q [$^b~hq]j,+("ÈsM.M www.inkscape.org Ǧm@IDATx} Ew13{d !  1#,7&@QBA@!" 0AEM9"W@ I飾߫ٝtwwu^{uzŘM-)GSk8%x6JP JR_PYmiwƴ#uC߶kg5 iD0+[Q 8u㮳nT03&˵C4H&(0LS;G|`eم+gO!WnMoV:ipD <11urO;ᄂ}+ @˭!X6sµU>5g|VI&GZ{:8>ǸIff<€R`C-;xX9QәL @jLgh.4\Xao2A|l>ؼjHrW#]]KN=u#O zCuثT*Lz=n6;`F,gCT#oXʶ}1iK[q-ۻ~6jm1;۶hj돪 Jڔg)pm;Ρ%zf L3ã)ȲM}fu˯b2RLWS{}<JM(mffatZڗ}iNWkݣTCՈ§8owʙG|vW֏ ccw[hie7/A+&vu6;l-וL4rDrW4CJ`\)Q:HU@*htNljoْ;ύt>Y պ8."@*A{ >9町DžXuKKX*kʏ:P :u0ruэ]:dv40^%!Xde?qt@9Ad(EaULq,6黒;3~JġXʽ# t́mr;* Q>2ꍡWX z9b˭Aw}HS3?nji1xCJBb6vQc#NH@*q}*q\u]7>_$亚J]PJB_XD%"V! uSF*ul1􎎶;h]JBޚCL!Xy+Z݋q^GA,0)kC?l$Z\I |8i :(0UI`3羶{}͉0֖n) h3!O5S 5*Iw,c>!%!ljljnY3bȾV;yYs^ JDNFE(T0ptŋI(8Oci֎ܐ%!\K4c+zQX(0^Jf y07:cq֦jշi{{w3r=q/g\~2vGA 2'finn[ٟ f}-S't26Pܢ@IF IwSװD7Tb[% iЇ}w?uTզ 9˼4ȡ'c64Q6  Sԛ:Z|i\gXfa `~)ΙɹK_>fEfRc,oZh1и8egL`#4N!AFâY{^;#QEE|S:|O8|w4a٦h5Ys|q%Xx1Վ%pX\mfhY|%|z>^k{y˘V{:SgL jArwAcZ{q < v89C0Nq5 w0(hH\O:> )[vd!'t(|O[A Ml4ѩY)/%$I8H!U7񏯘 Yp9w2fϞ<]T,?yh[l[\s8NKT qadUP5"-}x!ş:q߼Q4qpWhfƏu6-`Ԃ$$cM2)}}kG&8}T}'Mg2INMC_3fBGG K=]E}K~@ޤYlϿN~'ZGqϋfm9h@02*I:>3Kb_]t Ks7݄dNxg/tD3rǧGF8jm?:|Cojw`c }]r-cuG(2Zt 1LsdA# tI=fwA9I> J dV4M/A/Yp`e\ξ+&DAq4kaL| }lg:ᾱ͠>O~d%~X|Ik]'w PSҕP޻_VDޥa.4 . %ūFxwXM|4őoFzZ:z᧏|5@c=R-khh֣VN2`ڱxDYh}EL3oKP"KL>?慕L.BK^PL4kO!f5\mJ b-m{` 5r X_Of^)[lћ*ON>sxH(PKJHbx$ϥn_6ȹ֊-'>o,N`j,n7X+p^@od=;{^DMD0ԛk:/hTI-ބ?: wiaEP֋d s1'V3Rw|K&Ys 0??]_]E A+UE82;` j꙱CXQ(?]z_j!c{pP\:vAx~|?PX\Pb-S3djꍾۗ)ח ;gki55u _"{ }h(Ზƶ}nh 1VPs}7)q+>~&[Qa& >D\-C_Sp#K8c.]J:n2ctМ~ ~p&gvr%s"cC8LpFu_T `yꐁ*f[O\-%y=yW0ۻP0'/w w!)$ "QM_E! 2Yz0F˸-G2-Paz* P 64 CXXHҒG$!PV` 7rȰtf<n^\=xg0xEiW~1> ! ]0vFo `i8F 650S19 ]eBKd1a <;Vyxy F^fxF}|u _C!``dcbhad(cS"z=_`xHҰMry[Ɏe,TXⰡ l\bK0,HC;_7u{>{lz]_FTYEVY~??v lDLǝkwbw?]7e!{}MQ0Xy$OŐkBxK+\26 /xAph[+ |Ʒө럇s4!f>[.[6O(qsP nfjDI-xH8|87֔zw/N2?ek^UGbԮڈ?@OC h9H̤KD{AsFӎo^w(h''Pd?Ӷ@z J p5t(F>w Ȧ{XnVK9T"[IYP0> LSjGk\w yht* SI1[\CxRE<;ni^XyfqlIY4tTn [hHӘk%c!NC"p\lð"q/1 %N\1Nm7b!FqPL ` ߈D1G` u㫾ڬv_ʴ^!PP4aHN2?A+|}ES'D<- {i9g05pkَ 1Ňnoߟxf_OM^DM:;0L`@ Dw1w9{ٵؔWn:r4uQ>`y/[D Xv󼼤rkG(.#KQ sg/9G4ZksX#vY+&os`rXaԢ1y@Nz!Iy1?i{̾kKhƝZ %PR! cLt2x/\u̼/[h4D KG/PB2"m8+EvŒy_`萃lI=bKƦ NԳށ]_wAH'ζ_<-JƵۢM&'OS.J 꾃mK>߱G>`e'\2)(c 'zL<9B:Fx5mm3-m9ҜËa8Ԣ"C . Cx+Humu[)~+p?|\(v}K7(uqu{]b|Az|%NtѤ"z;ZǁҧXS׏^ UWprG^>!Z{:GzuN $FJ:(lG3̟WIAUG ͸5۲"^}7B+ =}0 ADY[ז%ZS>*x?z@>|@KU\0~~>z@rswVQ~* B u1~QIkW%*ty0hrdBzsO ѯiD/GJsl lz~MJϜ!:|v+}X;K[pOYcF֦5CtBg?&C$!ĩƝb +&d44q=4ܧ Ɩ$R&O<7YOnǞn6%n,&h{T?ZLb*8y~BdOamv&8 ! I8@-;L S=O ^ AyaăaEo;J>TmEXR8Issyέa[HGV `0 aqOĩtg j2W yg0fYrDӰ"ˁؔ GEmb> إL^^UG.o/I/Qcel!B M7ή}6:I x, ?(lk6c{DZNmh[0V+{WG AprkCzʒ|!;6g|?Gb等Se҄ N>J)^n~d{c}%[l=3T?2 @(sd` xЮ>^uΙ}- $A`(lj2?pxwӓ`B>(j &T-6evSi,"H5gzx¢SXz?&sn$5 tmI{~yO кЄ8Lyz*a@sE}:io|16l0h^2%\;}e+4ճŧmc 'pP`l=u/t A$|zwa2?h/<?i_^>n[J \! W@P I:n}q/CY\C K rsyrl7<>ý</?r m?[N-^߸Ùw!a{lUsNLʺˇAJd3dZ, ,).$1~-q[Ba%(/5KRfTc_hdk'Ff O@Vy8wz:p˭s.rҒH+I>ͪ 2ߜ"\hA >IosE,+`;_ic%&gM0c ƒpZrl!r2Plܲ:>H[e ,#"V4hX v%GʬT"fP 53cwP@4H-nX>h_gf/C9uV!?$Z!i]WD^p $B@ &hB*jMXnډ02{dlw[?%ˏxt Fg/@]?^)e̹Qv8+8pE}=lq>i [Oaަ`#`#&ÆWG7/T@"KkR sSldgu)/{^- ⭫TAl-#N_- aʆ`pǴ[656z1]_6ݠma VWmB`P r٧/YX\F4URE(H񍉓nVG!G+2rx-ʤ-+w `# Gv6g`vEGMj%9Joe5فeaއSq8H&} Z%F&l|h4G @(Г?ѻ-Ơu`a5D¦ht ,?u-e/T aB5֌a挅kYvcFQZ/zpޘA mE=hOtUp!,"$噈!2bhm6݀m: Pcَs);{Z3X pm5 LFN˹;yCu״pVtu[-o`kͷ tω뇇-yX@">'>zD2L1R)Q'>,֜?x|"7*qĶrw> 8+@c%0Tʣ'Nk|;p,ϗY3V6xxС0M:x:էUҧhp.锛 f?ke~!;f,q9{?{/Ϻϖ폣ؾ.3I{tO2p {񜯁s[y<   ҧ~zʒr=1ꫠ 4O/H=7.(zhi ر/iē$w8 x{i?b QR 1}m˿`P)X҆/%&DŽRoag+|4oB Id*5#zFc_8s_qq;Ǯyt{,Y C3v7F _wf|e)CZyӟ~o*~)w?&V^,`+@ӝzdklԫoOl#Aaz -5on`(uPS ȩ0,0slIqu,FJĨօ*fGzYC pWn;-jP0{8 ;`S慀/h؏|gߊd*H%*HrfC+73Đ(XPa#jaX Xc XD2}LL6ZmXrTX4PHP@ HTCYb}kxCL$OAviv%G7|-? MsWj]ѠѨ`{|-hz$g9JwagTveI]haU>3uewy(R@ ҿ [*5|g!/9mCt(}``9Avp n˟eWѠѨ`a0#Wc0wrL> wc|c :In4¤bM~n!I{c]0H%İ}VbW :8B7O 裈@"yn2p PezoJ!W7͔6 ݔj?'z*f82$>{!xQ@ UI&ˇ\BRU{с8nŗqDj>,iRz8#p MDJDjfv]GxcO'ٻ?=eAa/EpO{tVjCG]-5;x yB.ag@od8¥~?u,H );xP`h=(9m\ Xt)ǀ=@<Gp+8/Cqw 4eΓ5_<R>~ Cn0%J2͕bܶ^h1rs5T-">SP|6fSHuŃWl%Q Lv;h\uv(Ý~A*n?ʛ I (j?RyF~ -ֵ(tuˑN NyH@!D0g$y56P 6U;DGl7(`; Ϙ I7xHX4 Z۹煫;TQ@ H@0k0HpmsML.~v&1`uȷ] 8"//* R@ l P羲j5 :|A!p'e/{'a)@Sk. fBALi?ށ_|݀r_+%r%etۿf1f3SLg%O-S o4eWzcX W^OAx?"X 9qrng0h}=?zH7c/-ԲN"J$)wlRMlfGØ@D }@8Nq>:fLVhHH7q`L];G9 cK|V #hXoi}lUP@ TT4R繶fk7vor@K^w@<W=CʓS$Ub邻k|lK{e.-,l28+7=X-Ps񮿒#`w[x7xHI̿%gƊJĪPu3} $CܝʵS>[*V)@k X/I3a^AK,T~좔c( *R@ VLt jK)-e,}0>_7N^QF\TRr߃|şߗ0,o5Vn|TzѢѪcUtڿ+8kR>:T!݁#Tu ܳ-tboX |i&s-]3P u4˞şڞOt?PۤYi I0)O`aStGkÝb|MOr(J0񵄒H P c`?B=_\_ xOb5(t8Z]>F%V(ϮgntOYI?^_~̀k6cjfzsYFT$R@ ֪VO~0ɖI d$ERDB;}Z,M% 9B/Z<9fǍt@X-+P ajnp+~ K(' [M>u;\Amp]NTqBP DO9}߳vr"J?O2㧩ϳKbVw(&v>s 3];#=S?jrBc/o-P ) [p/@>,~xz/@*.S OAJ$J:rz'Œ/^ybں{52 tҀ8=CL@BY1ɗ,_ĺm?!O  CB(mS$)V %Q%fnF=g^قwLhk|38+B, H`IQ }AW P+a]|d^I0!'cŚ ź='L} 7}&@+4{G[~%z+ Ȯ=ml)pxz ag'Jļ.u[!~uҏR/L_I߯qM 2TɤJtC88c!VOƙ}ANxK&XY8ncO౳tSԪdTI@q</DTOC߂|t@Q K(Pr^i]0itOaqqE .t׽8}r;r˽&0#H)pGQ->T ׎D]l+9WLL/3,nJ᥽ҙ_& rj? KoJİRiZgb*" j7= qPǟ.?vTM*`{ SЄiyUK1NJAY R0N:lx.;!Ӝ?Ep; 7XmU+2H1wQdQ@ 'ΤM?=ƥC|7p3>3l?2kZ:,悏&C8)#[@0V$2E'Ў?q<¸I E+Z!Yfo1۰b#ꪤ 8Y '9&^`%bV&צb^oae^qpQ5Pwķ 5٨a՞*`a%V FՓnCɟ#4|Łp󺖵u[S2&-R̾ ӹ@&A 8գny_ɿ~tzOYܥ }P82@h1sVG@] %bR㎝9zY,+}o.DcضbE1^BR@1-gmkV-6 42`1"&M Tb sߢ6`b Zt2-߸E2&"K!p*zRj 2j7`"&I ")ߩrw!n}Wٺf+3ѵG yK!hdbv 7/JAfs OBK/Ys/҅6ࠟn |V*Vqy=Iؤaxh/]mJIq2TLivkdSjhq)L})_c:!@B >h:Հ:U9.ES)@+o?*l~.%@ ʟkwW4ՙZS~"5Ҙ8-lj4tZfˤvhww5z aW(P#,PWa&/>_l-VG}m)p-`b_ϥc;6ۃGj2)̏H䌐.œR1@.;`G96 ;z.6oݫ3"ڶ]/G?gH+s*D(qMMް+0z'qX+= S}IrbDS@=d:4(0:S?FP=VkSWXJ<Ң/@p0sJ[]!62%D[i'X ɰ~~L|$0;$R@ N߿wZ%T:];݃.5 )( )Aqp! U>3W7tئPQKJ=ՠ߾=yo짔OӋIo]Ӕ葰 @ S,}1; A^ҟN'Sa1`@4\H@h[xpzƞJD wrhTQ gIDAT_cp[c>Wu6 uIHz ںIN9ǜJDa`h rAXᄍbZ + #2%J@ĪPcb }%A Ms64˿r#uK3H IP|µ x?z h\6 KX j0?z&/!9emIlFxA݌Yxkp39BO\֌fnRqXW^>X A c|""yVL@DsSB5KhqwfU맷օ+Wʂ5OĄ=Hꖶ7Jқ"5m]q 70Eڬ5 $R "wS Gp2b^ݹao, Eha ְZ Qc@*pҤYy`l^N81b A @P_xa[/n Q@(@󝚅cw79\nwlB2SIEv~Z֭G7、?0D㤐OH?y'WQbK%"PuPV %/;= ; lSN[(xBnI8'JD>5m|ou,MQ8\'< (B,0ZmS=g=+F/@9Đ{W!w ؎^Sz<p鶻%,M@h:`4?TeԪa?-(--P恕.^_JeSA u5W=I%*]\ۤ7B>oY P=U7SC`0pA(P cF VسnȀ6*YǕ4s: ;,! :Jנ;<{t)@>V ]wց>H] Fh($x_O)6CdK"@kujn5^[:&Ĥ1ڱQwⵗB;]l4DBx[n)Hn)PgV\+8yhR ~D(k1_]xgc+'M"j?F?uo."^|=V*F(@kձB㦏!:q'\5~٪w<` w5^6H `U91Mh㒌o,)A|B:s7o=[iLs"GI}ׅGw'<I>%rH$`f3l-XW4(k]?<٨4G%*X' nmizP֐Dtv%>|ZalTD%EJ=8)NY0eIN% (Pj{esХv,7'f>It[~ 㒤yvԸ>ښ[0wN]("nLgL T);mR/$0M>0 %}(WTTѢѪ.xl= !auN(Y!~t!骪G.$^Iyom Nb␌0m ,,ug]x死0&Th2d#1'Q5hmrͨc @Xc0wYU\場JCQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q s )IENDB`gammaray-2.11.3/ui/resources/gammaray/GammaRay-256x256@3x.png000066400000000000000000000454761413310443000233500ustar00rootroot00000000000000PNG  IHDR\rfsRGB pHYsediTXtXML:com.adobe.xmp www.inkscape.org Ǧm@IDATx} Ew13{d !  1#,7&@QBA@!" 0AEM9"W@ I飾߫ٝtwwu^{uzŘM-)GSk8%x6JP JR_PYmiwƴ#uC߶kg5 iD0+[Q 8u㮳nT03&˵C4H&(0LS;G|`eم+gO!WnMoV:ipD <11urO;ᄂ}+ @˭!X6sµU>5g|VI&GZ{:8>ǸIff<€R`C-;xX9QәL @jLgh.4\Xao2A|l>ؼjHrW#]]KN=u#O zCuثT*Lz=n6;`F,gCT#oXʶ}1iK[q-ۻ~6jm1;۶hj돪 Jڔg)pm;Ρ%zf L3ã)ȲM}fu˯b2RLWS{}<JM(mffatZڗ}iNWkݣTCՈ§8owʙG|vW֏ ccw[hie7/A+&vu6;l-וL4rDrW4CJ`\)Q:HU@*htNljoْ;ύt>Y պ8."@*A{ >9町DžXuKKX*kʏ:P :u0ruэ]:dv40^%!Xde?qt@9Ad(EaULq,6黒;3~JġXʽ# t́mr;* Q>2ꍡWX z9b˭Aw}HS3?nji1xCJBb6vQc#NH@*q}*q\u]7>_$亚J]PJB_XD%"V! uSF*ul1􎎶;h]JBޚCL!Xy+Z݋q^GA,0)kC?l$Z\I |8i :(0UI`3羶{}͉0֖n) h3!O5S 5*Iw,c>!%!ljljnY3bȾV;yYs^ JDNFE(T0ptŋI(8Oci֎ܐ%!\K4c+zQX(0^Jf y07:cq֦jշi{{w3r=q/g\~2vGA 2'finn[ٟ f}-S't26Pܢ@IF IwSװD7Tb[% iЇ}w?uTզ 9˼4ȡ'c64Q6  Sԛ:Z|i\gXfa `~)ΙɹK_>fEfRc,oZh1и8egL`#4N!AFâY{^;#QEE|S:|O8|w4a٦h5Ys|q%Xx1Վ%pX\mfhY|%|z>^k{y˘V{:SgL jArwAcZ{q < v89C0Nq5 w0(hH\O:> )[vd!'t(|O[A Ml4ѩY)/%$I8H!U7񏯘 Yp9w2fϞ<]T,?yh[l[\s8NKT qadUP5"-}x!ş:q߼Q4qpWhfƏu6-`Ԃ$$cM2)}}kG&8}T}'Mg2INMC_3fBGG K=]E}K~@ޤYlϿN~'ZGqϋfm9h@02*I:>3Kb_]t Ks7݄dNxg/tD3rǧGF8jm?:|Cojw`c }]r-cuG(2Zt 1LsdA# tI=fwA9I> J dV4M/A/Yp`e\ξ+&DAq4kaL| }lg:ᾱ͠>O~d%~X|Ik]'w PSҕP޻_VDޥa.4 . %ūFxwXM|4őoFzZ:z᧏|5@c=R-khh֣VN2`ڱxDYh}EL3oKP"KL>?慕L.BK^PL4kO!f5\mJ b-m{` 5r X_Of^)[lћ*ON>sxH(PKJHbx$ϥn_6ȹ֊-'>o,N`j,n7X+p^@od=;{^DMD0ԛk:/hTI-ބ?: wiaEP֋d s1'V3Rw|K&Ys 0??]_]E A+UE82;` j꙱CXQ(?]z_j!c{pP\:vAx~|?PX\Pb-S3djꍾۗ)ח ;gki55u _"{ }h(Ზƶ}nh 1VPs}7)q+>~&[Qa& >D\-C_Sp#K8c.]J:n2ctМ~ ~p&gvr%s"cC8LpFu_T `yꐁ*f[O\-%y=yW0ۻP0'/w w!)$ "QM_E! 2Yz0F˸-G2-Paz* P 64 CXXHҒG$!PV` 7rȰtf<n^\=xg0xEiW~1> ! ]0vFo `i8F 650S19 ]eBKd1a <;Vyxy F^fxF}|u _C!``dcbhad(cS"z=_`xHҰMry[Ɏe,TXⰡ l\bK0,HC;_7u{>{lz]_FTYEVY~??v lDLǝkwbw?]7e!{}MQ0Xy$OŐkBxK+\26 /xAph[+ |Ʒө럇s4!f>[.[6O(qsP nfjDI-xH8|87֔zw/N2?ek^UGbԮڈ?@OC h9H̤KD{AsFӎo^w(h''Pd?Ӷ@z J p5t(F>w Ȧ{XnVK9T"[IYP0> LSjGk\w yht* SI1[\CxRE<;ni^XyfqlIY4tTn [hHӘk%c!NC"p\lð"q/1 %N\1Nm7b!FqPL ` ߈D1G` u㫾ڬv_ʴ^!PP4aHN2?A+|}ES'D<- {i9g05pkَ 1Ňnoߟxf_OM^DM:;0L`@ Dw1w9{ٵؔWn:r4uQ>`y/[D Xv󼼤rkG(.#KQ sg/9G4ZksX#vY+&os`rXaԢ1y@Nz!Iy1?i{̾kKhƝZ %PR! cLt2x/\u̼/[h4D KG/PB2"m8+EvŒy_`萃lI=bKƦ NԳށ]_wAH'ζ_<-JƵۢM&'OS.J 꾃mK>߱G>`e'\2)(c 'zL<9B:Fx5mm3-m9ҜËa8Ԣ"C . Cx+Humu[)~+p?|\(v}K7(uqu{]b|Az|%NtѤ"z;ZǁҧXS׏^ UWprG^>!Z{:GzuN $FJ:(lG3̟WIAUG ͸5۲"^}7B+ =}0 ADY[ז%ZS>*x?z@>|@KU\0~~>z@rswVQ~* B u1~QIkW%*ty0hrdBzsO ѯiD/GJsl lz~MJϜ!:|v+}X;K[pOYcF֦5CtBg?&C$!ĩƝb +&d44q=4ܧ Ɩ$R&O<7YOnǞn6%n,&h{T?ZLb*8y~BdOamv&8 ! I8@-;L S=O ^ AyaăaEo;J>TmEXR8Issyέa[HGV `0 aqOĩtg j2W yg0fYrDӰ"ˁؔ GEmb> إL^^UG.o/I/Qcel!B M7ή}6:I x, ?(lk6c{DZNmh[0V+{WG AprkCzʒ|!;6g|?Gb等Se҄ N>J)^n~d{c}%[l=3T?2 @(sd` xЮ>^uΙ}- $A`(lj2?pxwӓ`B>(j &T-6evSi,"H5gzx¢SXz?&sn$5 tmI{~yO кЄ8Lyz*a@sE}:io|16l0h^2%\;}e+4ճŧmc 'pP`l=u/t A$|zwa2?h/<?i_^>n[J \! W@P I:n}q/CY\C K rsyrl7<>ý</?r m?[N-^߸Ùw!a{lUsNLʺˇAJd3dZ, ,).$1~-q[Ba%(/5KRfTc_hdk'Ff O@Vy8wz:p˭s.rҒH+I>ͪ 2ߜ"\hA >IosE,+`;_ic%&gM0c ƒpZrl!r2Plܲ:>H[e ,#"V4hX v%GʬT"fP 53cwP@4H-nX>h_gf/C9uV!?$Z!i]WD^p $B@ &hB*jMXnډ02{dlw[?%ˏxt Fg/@]?^)e̹Qv8+8pE}=lq>i [Oaަ`#`#&ÆWG7/T@"KkR sSldgu)/{^- ⭫TAl-#N_- aʆ`pǴ[656z1]_6ݠma VWmB`P r٧/YX\F4URE(H񍉓nVG!G+2rx-ʤ-+w `# Gv6g`vEGMj%9Joe5فeaއSq8H&} Z%F&l|h4G @(Г?ѻ-Ơu`a5D¦ht ,?u-e/T aB5֌a挅kYvcFQZ/zpޘA mE=hOtUp!,"$噈!2bhm6݀m: Pcَs);{Z3X pm5 LFN˹;yCu״pVtu[-o`kͷ tω뇇-yX@">'>zD2L1R)Q'>,֜?x|"7*qĶrw> 8+@c%0Tʣ'Nk|;p,ϗY3V6xxС0M:x:էUҧhp.锛 f?ke~!;f,q9{?{/Ϻϖ폣ؾ.3I{tO2p {񜯁s[y<   ҧ~zʒr=1ꫠ 4O/H=7.(zhi ر/iē$w8 x{i?b QR 1}m˿`P)X҆/%&DŽRoag+|4oB Id*5#zFc_8s_qq;Ǯyt{,Y C3v7F _wf|e)CZyӟ~o*~)w?&V^,`+@ӝzdklԫoOl#Aaz -5on`(uPS ȩ0,0slIqu,FJĨօ*fGzYC pWn;-jP0{8 ;`S慀/h؏|gߊd*H%*HrfC+73Đ(XPa#jaX Xc XD2}LL6ZmXrTX4PHP@ HTCYb}kxCL$OAviv%G7|-? MsWj]ѠѨ`{|-hz$g9JwagTveI]haU>3uewy(R@ ҿ [*5|g!/9mCt(}``9Avp n˟eWѠѨ`a0#Wc0wrL> wc|c :In4¤bM~n!I{c]0H%İ}VbW :8B7O 裈@"yn2p PezoJ!W7͔6 ݔj?'z*f82$>{!xQ@ UI&ˇ\BRU{с8nŗqDj>,iRz8#p MDJDjfv]GxcO'ٻ?=eAa/EpO{tVjCG]-5;x yB.ag@od8¥~?u,H );xP`h=(9m\ Xt)ǀ=@<Gp+8/Cqw 4eΓ5_<R>~ Cn0%J2͕bܶ^h1rs5T-">SP|6fSHuŃWl%Q Lv;h\uv(Ý~A*n?ʛ I (j?RyF~ -ֵ(tuˑN NyH@!D0g$y56P 6U;DGl7(`; Ϙ I7xHX4 Z۹煫;TQ@ H@0k0HpmsML.~v&1`uȷ] 8"//* R@ l P羲j5 :|A!p'e/{'a)@Sk. fBALi?ށ_|݀r_+%r%etۿf1f3SLg%O-S o4eWzcX W^OAx?"X 9qrng0h}=?zH7c/-ԲN"J$)wlRMlfGØ@D }@8Nq>:fLVhHH7q`L];G9 cK|V #hXoi}lUP@ TT4R繶fk7vor@K^w@<W=CʓS$Ub邻k|lK{e.-,l28+7=X-Ps񮿒#`w[x7xHI̿%gƊJĪPu3} $CܝʵS>[*V)@k X/I3a^AK,T~좔c( *R@ VLt jK)-e,}0>_7N^QF\TRr߃|şߗ0,o5Vn|TzѢѪcUtڿ+8kR>:T!݁#Tu ܳ-tboX |i&s-]3P u4˞şڞOt?PۤYi I0)O`aStGkÝb|MOr(J0񵄒H P c`?B=_\_ xOb5(t8Z]>F%V(ϮgntOYI?^_~̀k6cjfzsYFT$R@ ֪VO~0ɖI d$ERDB;}Z,M% 9B/Z<9fǍt@X-+P ajnp+~ K(' [M>u;\Amp]NTqBP DO9}߳vr"J?O2㧩ϳKbVw(&v>s 3];#=S?jrBc/o-P ) [p/@>,~xz/@*.S OAJ$J:rz'Œ/^ybں{52 tҀ8=CL@BY1ɗ,_ĺm?!O  CB(mS$)V %Q%fnF=g^قwLhk|38+B, H`IQ }AW P+a]|d^I0!'cŚ ź='L} 7}&@+4{G[~%z+ Ȯ=ml)pxz ag'Jļ.u[!~uҏR/L_I߯qM 2TɤJtC88c!VOƙ}ANxK&XY8ncO౳tSԪdTI@q</DTOC߂|t@Q K(Pr^i]0itOaqqE .t׽8}r;r˽&0#H)pGQ->T ׎D]l+9WLL/3,nJ᥽ҙ_& rj? KoJİRiZgb*" j7= qPǟ.?vTM*`{ SЄiyUK1NJAY R0N:lx.;!Ӝ?Ep; 7XmU+2H1wQdQ@ 'ΤM?=ƥC|7p3>3l?2kZ:,悏&C8)#[@0V$2E'Ў?q<¸I E+Z!Yfo1۰b#ꪤ 8Y '9&^`%bV&צb^oae^qpQ5Pwķ 5٨a՞*`a%V FՓnCɟ#4|Łp󺖵u[S2&-R̾ ӹ@&A 8գny_ɿ~tzOYܥ }P82@h1sVG@] %bR㎝9zY,+}o.DcضbE1^BR@1-gmkV-6 42`1"&M Tb sߢ6`b Zt2-߸E2&"K!p*zRj 2j7`"&I ")ߩrw!n}Wٺf+3ѵG yK!hdbv 7/JAfs OBK/Ys/҅6ࠟn |V*Vqy=Iؤaxh/]mJIq2TLivkdSjhq)L})_c:!@B >h:Հ:U9.ES)@+o?*l~.%@ ʟkwW4ՙZS~"5Ҙ8-lj4tZfˤvhww5z aW(P#,PWa&/>_l-VG}m)p-`b_ϥc;6ۃGj2)̏H䌐.œR1@.;`G96 ;z.6oݫ3"ڶ]/G?gH+s*D(qMMް+0z'qX+= S}IrbDS@=d:4(0:S?FP=VkSWXJ<Ң/@p0sJ[]!62%D[i'X ɰ~~L|$0;$R@ N߿wZ%T:];݃.5 )( )Aqp! U>3W7tئPQKJ=ՠ߾=yo짔OӋIo]Ӕ葰 @ S,}1; A^ҟN'Sa1`@4\H@h[xpzƞJD wrhTQ gIDAT_cp[c>Wu6 uIHz ںIN9ǜJDa`h rAXᄍbZ + #2%J@ĪPcb }%A Ms64˿r#uK3H IP|µ x?z h\6 KX j0?z&/!9emIlFxA݌Yxkp39BO\֌fnRqXW^>X A c|""yVL@DsSB5KhqwfU맷օ+Wʂ5OĄ=Hꖶ7Jқ"5m]q 70Eڬ5 $R "wS Gp2b^ݹao, Eha ְZ Qc@*pҤYy`l^N81b A @P_xa[/n Q@(@󝚅cw79\nwlB2SIEv~Z֭G7、?0D㤐OH?y'WQbK%"PuPV %/;= ; lSN[(xBnI8'JD>5m|ou,MQ8\'< (B,0ZmS=g=+F/@9Đ{W!w ؎^Sz<p鶻%,M@h:`4?TeԪa?-(--P恕.^_JeSA u5W=I%*]\ۤ7B>oY P=U7SC`0pA(P cF VسnȀ6*YǕ4s: ;,! :Jנ;<{t)@>V ]wց>H] Fh($x_O)6CdK"@kujn5^[:&Ĥ1ڱQwⵗB;]l4DBx[n)Hn)PgV\+8yhR ~D(k1_]xgc+'M"j?F?uo."^|=V*F(@kձB㦏!:q'\5~٪w<` w5^6H `U91Mh㒌o,)A|B:s7o=[iLs"GI}ׅGw'<I>%rH$`f3l-XW4(k]?<٨4G%*X' nmizP֐Dtv%>|ZalTD%EJ=8)NY0eIN% (Pj{esХv,7'f>It[~ 㒤yvԸ>ښ[0wN]("nLgL T);mR/$0M>0 %}(WTTѢѪ.xl= !auN(Y!~t!骪G.$^Iyom Nb␌0m ,,ug]x死0&Th2d#1'Q5hmrͨc @Xc0wYU\場JCQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q s )IENDB`gammaray-2.11.3/ui/resources/gammaray/GammaRay-32x32.png000066400000000000000000000023261413310443000226000ustar00rootroot00000000000000PNG  IHDR szzbKGD pHYs;;̶tIME'/cIDATXýW[lUU]3ܶB!H@~X U4xECC-ZB%)Q"4*cK#/!  >$J`Jǽwdk͚t9weʆ۩ V't.YjJC>XZߨsLA<)qQ`ڒ."/D bY f) ,y(_M"P?h{ D$ *VdGT`a #Ζ^6j@nu Zs 8s rKzl9nzFR@E S/*>ėW&wpqT֚) KF>\M$ami˪'37 揿UH|ao3?@jJ⵾=U.5V+ƒ>@Cb3`R֖深"<[avD`lѱVߟ Z|Y_Q3@L`t %tvD9wz E+Y0j0M~/LUQ#<O̮yNǒ+ql^9ם'rQbd?`tgݧ\[r]Rz6\|LyV c.80̬$I=eD} | *iMGy=E@R0XUG[ }fvQD\3U\M8 [kf8׮*7ŭrfZ %o.|s ZZW@M3MrV/ vt XNC@B] q f.N;oBZ`!70 ~j8IENDB`gammaray-2.11.3/ui/resources/gammaray/GammaRay-32x32@2x.png000066400000000000000000000050441413310443000231520ustar00rootroot00000000000000PNG  IHDR@@iqsBIT|d pHYsetEXtSoftwarewww.inkscape.org< IDATxZ}pT_؄Q X 8PV 5bE0Pڄvd@ BEh Pme0Vĩk\њd_ݰ˒e2s9wϹ |QN$Tv ](0Ӕ [ QA:-zۍ?T䷛ۋ^VyΎ;5nBw5(h<[0x59ˆT{`Մ]͆Eq s D(N/Hf5Hn%ߢh5iMeXfZ (h?[#2p!JhJ@N>mFX?<~v̱MX [M-{5l ?%{>4'lh|&߮~ΊVa3BL*|_|2 h βzP a eXoB]}e遀 AsؠG72)-Aeod_g[MM(dz]𧹑sXɡRks='NE?J l>lm4 :P}}B.qQ 7X5ǎ!{b7 xcP(!D9o<1:UV`iX65Q'(dޛs!hyw`ĥMjZ'&J9/1w@@f9ac|8JPBM Yk ]{,l\*#q$OҜ,Z]ŅHdz?M~+>mpN(hNpV\y б[{ߗ4]H8z?@V| kB$R*rqVC Oug S%K6_2zApr>&~|>& ߏy}j)SI[b,Gc_yK[dpPU>h]MSz_pڎ?œG}uS\[,ҌT|,T64 .!omzzI5~uw`Ng n{&|Ek߶0p4.y}< -( Z@F6z*Tպ q@Qy|fpS^*|<#L(vUǙ@3X]!MlM[%MN9??(_5V%  2-jܵ_6M@~ێVS7iK)4vl%F6Z쀩+M Ba(->V`+8)kȱWgPLS+4@&/^Mт'6L^ىq6?ˎd4/,t5]0#dNw.;6zTTQ-gS:L) vّ 6n@'{?mφe*\pS왚 x ƈԇ*l! 2%"w l'7qgh~Q uw!N ""A`씮wl n PzSU׼&·ȕ"áT"*`8Pj pM;jÉGWl+M3?~y "[5~>=Bb|^.n(뒁l(l?HI(z-$t :U^$@ 7;>)? @X3|;åvK)=z\IwZr@? F8(َ jȫVTR0 ^Q_r|c]m66=u gNY5Un_yEÈ"gTEAf ]1Z'lP {/DHY #~$"s-J#^r~07vL| v}8$@UgozOVLO8ys~ $`MjRC,67HHIEsMOt Dp w%\\;*~)]Xm}xOCNpM߸ ˛ý^o/F5AsQP#7\Zڰ` Coxz4zPE0z .Uu>y6Pp;.(p= ,;K~j]ph1CIXj[]=C,%ČR<} 3O O=L[oPm5]>ݍj%gtO˄-XB9_lE~s$[wjUtn$'#GQ{˳r#{AmO!E;rtUX<#ݦ/evj2 a*VxVL8 $]zOLJ AXę_/p Uc'Wo.恼-6@hJT >}?ܬ!)YvJb|l'֘@9㰼z8&Jw*x t+p =\(r;4Ysj.,vqnp9^;n@ -M1\@*"4Q~_#֏k9'~n7_,@Qoj:P$k}Hrg:5 >#qECf( g^mQa_LY-@xWT(1iTo&'@ފwEI6 pMU]INlHސ " 9tޕHGDGE8 QO Y/T%pEq8bumlTt{&'T&ni .<.q #GLH@ ;,*^(W` +c>iLmrBB>"{iĘ 1Eu#UZas@[6=Se`JvZZ}qO ƛYe>?r5, g!!8o]vdml\z^-܉uW>v3]Ny9 ͇̃; Y? _<%&@grr)Z=@8M<㷜51 srV,;?x= GP(l{YȞm굛M|gbhސ-l!k:N'1*C+p8>e&d>Ů͓Zt!k{4+AqVbGD%lR3X a͋ {PΙGV ) ER@0Q[nlӯS; ĤCB cL"=v!)j"“}Vı ">1V$$E);_C#ry&U*8)tLEh 1C+@֘A)r9bzBħ%L3znhv!k_)喾6DT~F̱%  Y#;:Ih'~ѝI®FodU7eM+C{Tz8\:7A KBcOSG"|SVTZs|: -[ܖB,P3^CӁ`]P9{<cǮ˹yUq˜M@82Aj7f3n}{UIENDB`gammaray-2.11.3/ui/resources/gammaray/GammaRay-48x48.png000066400000000000000000000035501413310443000226160ustar00rootroot00000000000000PNG  IHDR00WbKGD pHYs;;̶tIME(<uIDAThYkpU:&`KTqh-VSPH(E4NI@XPAC4ZPG;)1ȏFyPB0RARôAJ#{ޤ$3sfιgo^k_ E }ϋ5u6FmM/v2JK W.;B4G Dž)4`<c@D!hCN 4Y|_91~։G] !655}="?}s].W/( "SSR~@΍5[u$ksQ`QO0kn}y@ImIvmU\dmtW C8cSN^h!R_~[ݎջ 0Uzϻ(o=!t[["'rҮ{O[`!o$Q"[=?Y3eCwrzn=,rTYGG3g1 ~E8?I=UTvU "<sڒթSPg`ư('ǪXds9ȥA3",L RzdȮV@RRtsni_J44; s돿ڇ_6~hš.>q9$DJNT$7dUOAL 7eʆa&>6${R[|לe%i CN46CՃz ݅o& ]nd#>k@JCW,Bn Y+*ߋ$Ou=Oc1 |"VRUTfp i֪=\*sG^du34c$>z^/\aRBñu VMٔKe,XɈ ZQ֛K3fT9``j)`DW@#[QQ-.ٗ)!ŀd#҇}g^"1nڊ-٩BOVfvW$Cו3Å-q!*o}R1]CoSWu[!7a]-p9s fufjĕΒMw>&Vds^lTgiI` \$DY=}UyCӆ(7O(~LZ{8sSl> prQM(T7˹R8DcIK>- ~ߧsũaՉ' ?>)NM=e'[ks^Lq!po\HkqY>QUHiX@0:^}n@?9Gy,͖kS]7"0faѦ^(ye$i\[6$><ҒE6vC7W fG7=t̙ %ʡ^D:7LTJ ҷ[Z ".-A2 #5҂傖 ]hԼ_ ={€$xsw #X5I gB/Rq 7دC(ٿ8.$9Ay2 $Gio^:wˎ&t G{@#Bƺ@}e'$hE A vb݌Ϟ,@D6b@z^9+ze9Q)Qo+G_YYEWlSIENDB`gammaray-2.11.3/ui/resources/gammaray/GammaRay-48x48@2x.png000066400000000000000000000100551413310443000231660ustar00rootroot00000000000000PNG  IHDR``w8sBIT|d pHYsetEXtSoftwarewww.inkscape.org<IDATx]{Tř=_3`APQdA(Y0"ʰ"즣QVE$<4 $ư %ǀ:t0ӏau7uέЂf47lŘ7$ klU)33nhVc1SE;G! KEt+Ę7v vޘQZ2wU]8Av/P O !BmX#*L.A8-lHYZJRue7勪O+w %6tM51a2Bi_)ϹThXl`i3/dk ^:LK#V#ԧR)M/U=z%SNr':[!U(bJ=ಝ|rxbB(ﻉs 8lu_ټ7z[LP3 g77EG$z# HqDaDpZe˗QBHy_vy.IC9L#c(]`#Z a ED^qV8#/lE|{}+RYV>##\zA6.OhzBRl[R'zYVurE^?O'kzBB/.]H})-4lEcNj=lΕP^)6ȏ%«z%\A'( `P>{?^ _Zم[pLdG#AІ{B/Z];bb=ir!B_t%VSeY8eg? |P(p\y7v+y'Jdp]s;c1s{s݌ፚ~nBY4@U]{s*T%yu{%c/?.TezrdzFn)GAH~~m>F8T`*R,sI&xȯH iN {κQ wǘ*ע]#sHY >5Ib2pFƌQ?xw( ݿɕ'l.+-Ka(>u!Ս1tu$Pȃvjo_l39 GrfӁ.V-xE=ccL,yfT` yek X6\pZკ ]Iօ.l{ۏ [)Rx0(\R@4iń7(q.ϨLi+I)F>u;W4ح2}&/]-Kò C BYso*HKͭյK0ڙ \]y /w lp&0 M19Sd+4o߇_=nJ"-s`XheͶ5>61.wE-M7Yd Kb=%\;AzL4TBM1~Qߎ ` GRY7=Z/\Dy= }_sn rBtlDtQ>['2=8' ! .*nݮCYYApl=8_#r7F 'hڐCw 5lH {.Vh> #'hlzÔ҃ '4=6ρlFƍB@rciۆ}@^hX4msIYI79#ȏ55^xh.׽ߜt IB }@^ozirbsm,rF5} Vn^gzA7=|f͔ҿ67"NIR1. w~|D ,x2| a!Y9rvmi jK>[77dS&hyGHbMlew5>c=ψ_9k"!dݺgln~ ƀB`VZpssK9#@YYb|}+6oW] HT:L=6't3uo{ 0'_ȉAy cE2pmk-97 ʉ'ƙ@nޮEs2H\sp࢑s."qu Rr:F4@8bBA:vgv`ip.#Y[!{w<"eѦ$4/= B+} 4q kY#WayF͉(]"ѧ%B`y}Nx o hF)#kQ ZEܑKC #ݏ2Mm7$)ǞB*O U_] ZƀL)nʗ:UFw0a-o~@ L\v\ vӎGk}0jzٌ`hC@pJ Q љC A"`;^Yde@8-3@@ У|@遦ojQfd>z^_}J.JgԂLFJe46 cO5z GqCBгiΨY$d':OߢɢH S{5 o}:Ŧ7?a|^m҆(ܶMsFKvt€,2A\V75#p4ڑ.5Q'E3aN򲘽AMʬ &a[OR?َuzt*/+d9mdED*7K\+.uLې*u?pӇNNfBEeC9J!(UylB &(@cg*BW3L"+>HpEVBy!2s;vWS9Q|t[_B9?k2B'Y#g/>TU?}O/MՉ,MzBR?$1o˂9;oA Z{5IENDB`gammaray-2.11.3/ui/resources/gammaray/GammaRay-48x48@3x.png000066400000000000000000000151311413310443000231670ustar00rootroot00000000000000PNG  IHDRFsBIT|d pHYs,K,K=tEXtSoftwarewww.inkscape.org<IDATxyUTN';\A03#C +*މ^T\QP^82 *2#஠ k:#K'$ꄤ~S眯*o9EL`Q3c_+b6Mb׮5x+"Ź*>Yrq@) SN|`;@3%mm;4]+E:aAV,R6SN;%ڑTl73soQB##Ɍ Gj1X2$=@a (KЉ` xm9?;!>$lo %ؠ(.@c0y/#pC\7a7.7j@Ͽ0 IuwAz]0&c-!^?tؿ uTJ7}]]_в(P;lllsAHNBq{?޿^O{ ]!@DPP`#"e"E*tE@/ vF'6JDe^O@^nB Hu D!c)d|V@\?o3g|z0][K+%Z)_v) Oe3@1\vP&%k1 YG!AE~"t@ »Db}WeVj⤦ʜ>g\I Vʺ%(C͗SX 'dҦ2<D %o">!6}~hI!/yė9۽ӡYo= m" eO6KF[aLzuWQ4tPhtUxl33.jǐveZxl;+}B˴x ‰WI>@P8zݻ𡦳흾%բ fC)Ւ肉K1p_88$D2oÉM25T 5cUo/=5tg!pf꯻Kv6fWD@RgϸecB} zdeijN G{<Ϊ]C3m+ " Vx:TgA0TG lu9x'vwN_iK;Te^a9mX8u9x`$= m>~رMb0ѵ^,YP6sZU?)pzk'b3L2ޘyA4# uI+BL0 b uEE X7 F=? vB.pR3Nh$m{*/qU./ kn PP27=9tQmhVwvku`L ?8vi*sf0Z)P`#؃.S\B (ǬTuTUWnm} kL@Fp)|n^yy,tK$ަZ LJ3\[ l/I$*U~U?2ï1|*S~MF,^_yGׁFe4z fCEM8婛v7-ˮm泽 Fo0C? bm{;.h^vyzX^%A>{Z}>˫N2D Ʃ^}6\뮃88NDUbq{LLPOhYl:?C8\ m1+'#ub62b8\K4+Gy#ES RF& C^ #)hoGFug/HfrȇvM];9 rOF' V=h ,6+h4E@>ܹw!YJIw"4)'qP Zjjg!1M@,qrڐן-YFqրoyY\HP@@Uzz׵!F2coI%4 1r@x*˜& "n)~m LhY\UCMZbK_d?Z MT1 ԋo<{ ڤXgV?ª'OG׸ƒ;Dzpw&1M@'~LvL|1a[sW(4G1?\.Bz/e+\Aؖ|Z.Gj/蒺?x2'ԥc~l/] A0'e ߮P5Sp4aYゥ@&|T1~MĠJaxS&EyqL.iH)6 Xm)$ &=^Q|4M,fnG:\^\ܠkcarORUMCswa/?fCR ѻ,Jɸ޵2㢪i)p(O Mvn\.PpFū.˜& Mn{ h۬{F xezlQY%$lv  \64)tE#Zxhvi >b_ǡ2?v}ӁD/6-h-xlN}'H1>M's6wD.%tɐCxh G_mg<Ł͋*l6U}5f.w C]Mo#*HU Ceޗ>;.k Ru*|z+{o$Dׁb:9`Iwu3")N =1H3u_~|x]v1xe0 iW6tdS)PQa\B,@LOFߖbYD593JV`~zM7 Ctn4*<<ඡHH)H.As'Ή1f:HXszwD`Ǐ$1s.rq&lN3 w c:QtYA^ H!=g)(P L\GM,!/TSY4.`Su]u{gy)(~M|x$1%+`]ѹ֛䥀7*~.bSW4: Ͻ`߬j}E G:!yF: #E>ՇJ` U:[['As\mS(4: { `J xj _KRMjn}aG֦oM䭀ޚ{ p $*w j0t aBCh>UaZ@@ ۰I =Tl>IҀg - z%cpec MLf<91ǹ䵀2 h@Ch+*=[kmxdd)Y^yWgWIXpqNw"(A+h[,j-^{!Fg [ZzML^z* $0/͉bb )4ًKt)CIGgy/ - fHZɛ\4eq+n6_i6!G *92;uFEئYWfy/ `´-Oݰn HafY]Xɜ_ny)o?vCyF B,8gHIPwˣIM3J 51@}k!dĬ&csg: I Rf̬&:ɀ-f/cry>m3gi|V4<ðjRV(ff=y+ ;J"Moۻ,ynp\aԴ APgKzb}!R@6[\A=1xc9c'TݞAPQ99I!4`l)졚i aŀ8 `E+ j[~;XG'"v#K@Qho\fId;: =+\&_dّ}i:wd>vY#D,oP0$@ykC ŪwhU#oyN iqF~HKj~ wLU W{?~=xeKDT=,B䅀! : ;>?aO~ 8ZV'(BiM(#/$pw{=0=wp9 9@:{ ШD!A$^ġBJS%JJʈ#Qp-$7uMxbMKMceeVnIrV@T@hb(Qn٥z"["Mgm7,9؋āwZ*#'ǻq^~ퟋ&IXKVqS^VGN H!qMUE~)Y\ dg=9) &'`0Z,EZl`- GY#i8-P-6>ku䜀2U^kxK0h+H|rJ@$UsA\6[(9% ]*X)  S}Jn )U2^gxOy eccn3 Ny9Jv)_ek.34_cO32\P7ؙc䌀j8;qM ػ V%:9# "4I㲳 hK;sb)NfWB4Clnΐ3%Y'D4}7ab3( Cl0 gDjOmLysF.wə" mt俾x~bJHVؾtF`D/fn3ھ|l NƶES䌀 N/ZĐS}G 99ä=8t9B?и4L(r@}/%jyOBK33y """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""*.:""R0Sc--w{x:""b0S+Ȉ6Vu\~z1s&h69K6mSȸ{~woQٻh~ H@P5n(Dԃ}3DDT.~| 'DD"BݘQ?:,PW nvrŎ0 "&`l,' >_%㲙;!sA59^O[T" lX BaKŏ7.#G{M蒧 33UCl045s,$"*sf`ds-b 1#a /^+}aO9!S=Bp܅'A@E5PUx |! P-|OC%V/~q_T({tT{gD_[ENh w߉v7 `b+w X;1i^ R  * TUD;-> U1XAkX<)Иj48G=n^Ըn>|Fz f@._xLDU "].9{ʳ>a@n 2O֩:|doO;ln38dϷxw~M(г|]]Ӽϭnu(=gDVWYbbz _*,/% O! ΰNx`?SyxDO/ĸݎ/w]yݴ,01y?*2xeK!?}\9ᕔ\~װ; "2C3ΝbXE=h[; zyGW>Xޫj6FLc>d,KCM 8@D5SF?{m`z"Ag K ξVP=#n8kƊ1UrsA+oF^ћ0 "t2;pُ,? |? 0zy+Q:2X'`1 o 6^p#V%(5][:@p}Mb荘y`e5 /t8&7NP  Ra}?:ވ Cп8g=b✣ >1xDo"3]Cm+?`"Ѳ̃PQ|ʅȻGԿu gJviE074M|,TEWdYyrOEu4HZ#WyDo~d- E>o/kDSrKΛ@'OU8z;Yx6#[B 6==GlD"=cP~ TgV1@$`BDD=<0g}Rk$Y$֩'y"8{éNj"ogD7E1֯kWsR:LJ@U/^1SP O|ɭn\Q>HDԳ]'^ҩٲ-<lz|ӆןRsP8@DTlQ0fX(wf,0SWo 3Cwh?YZ)?LTx=cUwhx [ ݹWE)tT "*f.,@ױaYB^,"A k,,PT` "*X{Pmd^ QKå kb-NF <@Q0 "*{kF*ngD7B{GmS2ؘj];zKrg 7>Np\#B QEV^?(tY3V m,oԺqQ1 "*$UQ_PUke C)Ecb@DT@g< ^Ǒv]n5|YpHЇUS"| 㨚_NB3 *K+ђDF"@"9o q*0bS 20g ^P`"}5ʝ9RFc@DTgX=(:"(ov"A2V{ Q(z۪jgD7Eʸ-NVgqQ^܇W 0(&NȊqw C(p!Da*W _P YkV͛6?^DUDD%2J@ױKT!Amz9cp|a`xNMu[d+-$ [ilnMVECPWAhQ& rKc+*`xsPc͉ Am:9%-VhM%bY &$RQ@uH@F[L־exu01PI ,S*=!]U [m&~L++S|ŧダa,~9SVΛdX+u:G(q]T Ԇ$ZW)5FB̺ Օ"AÐʠHUP ̘i!jA-mnۢlXhX1KQD,4[hl3aZBN[%y24*~rзsN" 6 p,v`@^Q՗T,_`3VJaAc:>ׄHM%ZjCF[m5P@j*RT$2 ,1%mV]+cV0j)Z;꨹iය﨩hXuOa ~;ݒ'P93l' գ ! )$|twOf^' m{([w}UV1T `֨J$m1E[B[HBKT`^raPa093lP%~>~YsH6}5!:Ox|lNNv7<蠟]ꦘj1 0!26w;ώم 3| rc/ 5UlYUHv8o}mv/ @uܬ;uT8Lȵ}3+mUt G"~TMWѦ3e U qzXz?4 [xWXj7{.:*,&ѐ˟1b8@Qi{b6R/ڎ߷nK~*s5-;<~>˽ 9:h# P"@i{. ^ 8oFJB!YkoM4Ï]yJ1p̜Dr@c`$Hub&P煇/JvKޯ&[nAXEa~1{λzG_s脿PY@"ָfKڇ5C7Ms&Li=si@*Fbm]D=Ʃ/1lXb'FZS-?:*&4/{ K2Qda_;5& p4Ģ_C#{rP$km_aM[5ҩ{0 h(ŧ{@jfPdfB3; 飁jY+p-3ag|ߞAJ&ԠK:8f+XȜY=@|[G\]Leu?|XEkbU ex]:*>& ^D ~nȪ m'I]S=@W*?}ÉLj\,oIsw_Q_U>fUu챻uT7/MW)sbw:aEمt]USG\̰́R98(02T6p~KˣJajtDzB @/1$r{<+Lkr{2jv.$ R^WAD"孖nllޒCw~=^: *&+vSrJ, 6moSWf%n[DrĵOJ!yMZuP XON7lH! p]Pi%zwOF'Wl+cu-V;֐+3=q)p07f>׳ O,&)mA58eXU;ڲ}kE=ZviQOBe @;h=~k.OX1(o迺h Opu΋h{N*NTp|cb~/ڞg]7όbY߸}s35V byt8[r!ƛ^ư!^}[f쯳/wLe3e,Vt>!ui:,F=@ʷb=UK SODf^(jؾ_aem46̂vJ=2uиh5IXÇ\3Jv} Z=0a2kx24r̀z %2&p3Rm,,=:wi)^ ]yg4yݞ-Hg/=e-Άbo_]=\iMXjs+cg ]LqC (㈷~UՏZVVȼ'VL1CP.5A(e|nھ-/ʲI !W>YWRzi] иvֿr~4Pay?Lʌ<GŌWߒsNi(qbqv!ogWB|,Nӊ-=8Iǭ@M˜E(|Tԓ0(#RkؖB֜4me]s\D93II[ThIZqVĪ PaPFZ +}Ƿ^];i) 9%yLۻꎵ.3tu d(dqd*Bvڬ1f$vy}â @ .X;2_]gŞw(m.K,)vB *8x6pjTWdumwK+~!{'@ʃ1'nWyD|REWi, c!`bnn=lѽm3O9C@Gem0$|RL_V$Z6k0vW-|q?U %A1(u\]JҘnkGV*l։i X j,9mo'7 ] :REP@'6pmǹ'j* ?q˼0NPS.PV+ZL,Uo -3rZYШ]e`%װ~7?e^~/@%*xRa<m^{]q}$pDG>6HyAv6/^:MY3}QbOu,8~jkc V,vӣL1(^`{q"~S23 2mm,j(C|ް̟z .NY4iEN@Y8>UUHp՘FVgZD"< rU5X_W6{bu̻N=㛴?H:yQܾcvWmID s_露}`cv}Ci3˟44q_qXT5WY?CWO9*c| 7(ɍ;yǩWmxq-(#'~[asAR'9T:I}}(YC7)WL|N 9G񙢸{,) IDATGE9n_ƾth1 MnK|w|frCE:| v}Xۢk:.*_co- 8mmK+R>ʬ'_q'9tLcT9p  VWBj6D%ݨ=:v*_L|l]*;\_ 蔦* qX~)p2j~ܦc,!^;h*} j*U(fnYhjacKkhj35ݑt Tȣs'~T/~91M[U> <\ ׀?-1b'qZ!S[֋9j`h7Y T*$S@m&TPRXhhlamS;fbCk V׭) Tv$C\VzԠYBVҕu^p=m)+V}2߾o|t[l))ݸ]t ,Wݿ)Y'`j+uC@mP*- e-([hjDcۦgUꟿ}`wYpޡWqWi]P% mMGo)h~?CvE_ f܆tWYLۻbߴ埿MrΜID9:<@]bm_b}Yy7QfX dH5P4W'^%{oېB5vi]~q=:8"!zDIZd̓&c'{ Xu˫K36ԁC+@(Fu//t\v׮%r 3FA2,if< o>S[\ OgnIoY֨́N/ zN~mS* 抽;uN,xyOO#QΘ*wٔ}=Y|h^Y ֚ЙnE=ɮN mm$~m3vٲz Q NS0Q9ݞY$~u,UOhA:ԭNm*@={6|]WԚ%:uBgc VovAV4p].zD&uc ,p%z:9MmYjT) kwm'"*&~b~ noe{!{&'7n;q; c zz|WUEck }kBI;9톩5L݉eVz/P({nw@gnq=mo%zk^"*ـO3isᇶ'%*&>bi<Ks46P+zSaNC^T"ٕԶߺė1mؚ â[PQ=6yG]2[K^q|msi]%v yMۻ"2Νs)Q0ݿS$p -mUlww(m=dž1,VCwoZQT^7O_}g"*&>bȜt ̚5TK.Y"P n7%)%P}gE&70dGa`O* s߾\l)_o=i{N-z?NmOٕĶBB抽$QCqDm *2&>!ju-Hb3X8R@yGB JG:`_;*\Gzށk~?f#ޮ0s}0Q13&s y}ʈT5 eWЫgwO\svvkoM B~ ׶h Q0 U񈌷Tw_|~@Kmɪ@knWvVImMFu.$)&;+ J Oh p[=Zhat^È1i{WYhP&]^Ў-TAv/B=ŶPl%S =_) ~Eop.lt7i`rPWGϱ]m+Z5zw]ko3DTL|`ș NA~8kKGR;[&C3Z,B 3mlEM s8O~i蜁L{uCj= YfV w oZ:ee;䒔 yLolm+ݬ:q_W 6gZ6bYi?s=@b`z`+OO.ɺ{1GOP*OoNvblEv۪.e{b7Y$6uN9Q1#f&K[d9Kz@~?$D_W%JvƎ@>SM7cӽ cx| DCF/DyQrk,2]gHr04^ڈ=pJ8ݶ۞v3y ečn;Xx@QNO@iď)=6ڵ=MZV03;i]ӛ#m-. *@Xu5=}%*q ~i(^x{I/وK~Iم,KfMj#@Pа [([GE KBcioY?L,lמM`NQ l*Pܵ& "O1 lpϸuE`EoDwGۊ`ĨC~>yMu$X|VӲ4PSʐ8l oWo:}'X2M K)o݄aB}eO]uhs`Dc ZMKڦ)]=@ǿ C?)N-Cqr[67wdQpM:@$zMp8l$&(LSEߺJH]XO%3My[otoqW (MYx@jm@. \%B_ ,z"aUXbCK:bg zQk<|NBUs pi.]=n^حX0PY`k?*,S[aF0GO(~=.%CvOShҒ9XW"y2.a 0k}!"a2RŪ -зImo=nEqllvۊ] *ӏDu¦9:m;\^|b"a":Uo5c~:˹̚5ʄ%w|sbϷ~vA8 6%߬mvU=P-\:gՎ;P+e > X 7T*d$lb);LʁWԥxȕOn6L? ӷVy@mb!9_ -&~3]~!-_یU &l/@ۆ'NI@1BY빡׿'C8Ւ>be3vP A:u:1 tSټ|m3kBu7vf:$5LlqW`Aˮ@$tn/T=*tm+UhrETӍ ]~g}l^i"H PzUU4j@e3m}igt#ڶ\Շ~(1wt5/,-vPR%0Uߒo=@A2꺦Pz QWfkzp}=6GE˧9wFTl^xMΧ;* ַͫ`6Ï!𯒅Jzc:!/A~MNAWW-<>ImR($~?hUHK6"ꎢ*: ֈ(6ưtm+bP *qse?k(" ߯lـRKٳDf}F/yOLMu`+'t6Uc R$SE@]U !UQ[!BCS[ ŊfDM+>Sa|6L|DT_8zeEcXlji9KX?q¾O|ᰁp*OS8=bj+ :ڪj*1Qlhbf4LX5_Q{II@ܧc-jC-j>. dI})pM;&uC10?Ea zӝ C B *ʐa@Dq5>XR>ȜX$QL,7&n$&>:~' l?H 6tELkEmz0DEt`m?&":"hs5qtK$6ժfM𯎏<¯I7L|DcL޾=v[#XIWO6Rʇq ]Q`+=-j-^݄ֈ z;.5b&ꐨ0J_:;ol^ |Yk{gn:sNX1e#`hɷ CD|(Z/ZBN'ls1cbӡL|!|˺bm5{TWhmx(y} I7nժew^ uDY W[}/Yv_!}D⋿8a1֕I}̸ה(Z"Wx % FlyN,Ì\bwdiXz Db2{[G1Qq4Q/e`O;X9f`CUE4p̢^wn_*C?s>1ӊ}~Ѷ(*Wo;u Mۧu /Xlϟk~vJ-dډ18-==J(d=whTD0H:rӧ) = js X긜$b~-Yb=: >8g6b>"i.E=i7/b5QT5-cq㟮)RiJ[znz&>331Gmg%$=ȼ9m^jȹ )E}B-!$:][)C:$QP̴$|~WPK\z'Ę Q>‹:bsGD= 2\鳨Zmpxh,1Tv˦m8 ij(pj61FMD=bOEOmD~ᶳ:*>K6{"1tA#g_:N,}WG+1%ֵ : *>թ[T ܣf8v?9\ҴeW,^ O{X.CkBuX%Md?ic-F=@7> lL|@EbMmOVI]30gگM ܒ). XsjWU.Sx*c|nK/ږ( 3)Ж1oHYf_sv!̬~8`P}?D[0 g_.Kc=ſ `;&i"X|ރ O`ZGLNlM"a"7}* O% [rp(t#tO+t59_;gW李5B&uD]Mt?dHcӾ"ś8nӕ\t҄n˯K0.BD`@󦜶@ i- tXecrYKu_wڧzֹ3އ &(^QVo{N1C/OjK}! L7c@#0-v[whP*xS( c|G:k&~#:Q/Gۮߐ{=@,щ,&_T瀈z/&D>mL!ykNka8R9t"{߫Ad ,wlw:e $@ M]ً_>~!\^ ߨ㦸mIoʇMYMhN\9{;UH8]-?4(NО}=@@=s1Fh"`>VU=W{ONDL|&f?>E8(tBM:AcM8:}bMEnJ31 w  +-:>C)|QSb*fx"dqXw8fzH$/Hx^ /ou}̵ WPo)zZW쩻gu+ 1.H(&D$m)K IDAT iHxf]qN;m҇oEP{_ͱgN"+8b%î|Hw̽`B/\AD۷jz͹mKe ~5/|jW;&DjfV!<r/ |${\MDq% Pzv:6+pq"?yᡱQ&D~%;Ø^5`i](D=^ϻ:!%`@SVK2 d{ hZoNgqUЪ\6X"rȧj3(7}/^=-40ft= v`Mf`U<褄#a%\(L|iGPu5\htU4_5kR1Q7^X&D>ޱQ1ÎoNvH(e$+vWm{EaWQFLNNML|uwЄm8|̝9YB:kOp -~E=i{ς?q㟮%| q7W-1OTLʀx~K5}-=a+~3ls`ʼҹC"2` ){P@߬kG6T'8I,J`E{UfQesuҐ]U|o?dݷHOo^>~Y(L@yv]rXj?+C o[Zտrꁈ 9! p].:mݣ6 M/]YA˒kw4?:7o&`A N@`+ @ ]s!To2~Af)X2a֜LX2%YB5D,蔗+Q0 *FXa6z޸}gG fbiL1*BcD%Lq~ߖk=0nIݸ6s.f+|bc.`ET@_?eTA La5G$Bc@T^:,ىaLW PCr>GTZLʉB@ݟ[0>ۋ},z=|쏨` i{I+d(*ҟ9 HZMӺ&9U Wհ^M*" |LʍbQrC7{:e^xh,)T кԮKZ>G&DeF u.S/477ZKq:I검E<Q11 *3VܒYLZ|@6/c|BNUx~32DDܨ.͑mș(.M<27PǘSg[mxiۿ66 .z/qk[Q0 *K~N!Y&1>?v5ZMٟJ QYmkt_}y/z?0 *C | H9ؐ+r~/"}.AsKLP\ Jw-З޸Mx什c{@N$%W=$y Qw&U֔UTR{,z=SNNDTjLʗ󊀊o4a涪rJv`D "o1 *W]Wq)yU+6@EVIg`cD>\V @л1h?}]>ƗҐ]=@:Whջc@9m1@^2?ƗO=*nӨ"2Q('M7yo~L)t{qhM-D$"_`@TD^ $[ZOEuݩPl߽)Z=?"?c@T _д нK‡oxs#)y Q%@g}_o?h##.vB$@Uq#7&De,.@)6Lt3mݾ7nV݈sL{Ƕym\w \:_ <:"ʓ`%7aLyv) 6oEqD; *{ ݉iwW,ݝMD=&D=FBK] X?>x䭩?/hO=Jk L If`p]\F}u= H..&1YX}"( $ U +DLH~IfguMUuuuWW=n=goW=k:~*=*1 J$c Vy!{f_,N"&=3%tz_1E Q 48Ngvms޶n9늟{."( eL_`L`+7~F/WJDQ@33.l?: ?1p\7 G ڰaNκJ(]b Ӗ N g~8{|;'^9lj⸑"(L?r\' RO=/f)b(>r񚏈ʙ;p:|1YZi8o`_`Гc !RC^i} ZO!f:~3k&GWBDQ@13.^!5+ޞu=>uTsZNlH("&YJȯr0tYntjtEe Da DrGA yw;)}XW~_չ.$@C D]a@fTOϕjV|wAMwb("N_z(._bx/q.7d針oɀQ 1ED^+5>v >]gS]ołD; DpқY;~Ӎaߎ1mO`Q 1E޺ L(X!ݰKZg{ưlQiCT.{xG۬+g1D0 b 7IOeOkrp [.ߔqjHb(dbr5O!=ۍ(Dq_n?ֱfRPAAE0\w!Hb *./v۾Bs h=rf W Q 1Ns{-.|s!+p9"7(BHߠm%G%GL?6ZX?Fa+b(1}?]~M mW[C߀˳uv{F@žb"*9]sn(|3k{L?40GHe@3 DUvEw`Ie-⁧.u[ ,L Q5Jf Jzp7vXN_`awGß&*.-\[Jv?Eۭ[ڦN0: G*b *2y&S~5rv&9Vk|.g#b ߷eS?䙵;O֋l8!ۆ@3 DUr? i} 7}{Psس!xkf2HJ Q0Ue!Ϭ]Kױqi!~lў"*8um*1 !Wբ@s “[^>2 !x0 t L5W{ls_o97o :f.~У^{g/~r>%'HDӘDM DkwD@fnt?oғ 74=, hnHB D1@0IˠH?ԯ\?7O|aZL&v>YmGu5ٮO Q^ Uˬ{L5( r5iӭ\hv;,~MZqrXQ0(l\H+^+nؾV0m8!d9gϠfwv'b((鴡^^@;,+I͜7ukPxb(~~?wb( ?WV?boP]^j1L4ࠟfF${qʟԿ$ QPDVX&٩~yy]jj_6ؔPe Ltq yT){߿مOv(P=Dq@1>^l k:,.XXtijx >e|b>Q잳2"L{4*n TIuQ*Lr.\Uq_ǚ%R)2:wۉG O%+`  !-݇"M*9\_j}m!vǿxڏˆ$T(f*(Օ[4><ĖoS]Eza \[0[Cjpa}{2ԩqj"P0UHR_s׵`ۖwc@P-j"00UX*@)%a X1-ܱhnHgS^x1P|(5>=Li~T`L =n$fOÿ݉g̞`o]DT}ɰ z?04u D@xE* eNrw_xOߑR`uEp]T?΁ͯ:FD@T\rl؅%:њSJ)J]*}h=x:{c 25ururpa--_oE& ʳő-8VRm\_?,)^Ld?a|T ޾7"D1?X"_T]X:Lc]>^֔W,۷>ǎ+ǁ^oyރw|2oL8}E?޹JԬGAv֑H$?;1L~ " E"5gk+UPK6QmiC8ԓ-2?{h&H#LD%-|qpWaa9_;sQ~K&0{w/z~lad*U+Du_dͱ*ZŠ;7lsZ)nnH!J`.V5)"D*fyv\.f2W6F> }G4Л!F?k3lk(F@Tnl[+xcW+Uw&$@C}]{e bF>  *}HŚq CuTc uY;D1@ѱ _|OoE~շFOSu}doN`V}l-@D1@Ѩܨ/hsy>NW۴$ +qm ,a ȾqoVMҫsg*&0 Ce?|O3 D`.(Vw-^j}$A ` " _/VQ1<1k|et.B@ pS:f|`&]~ %b *bڂO0(?[ {&o;|Q0y8I V$Q+*@3 DŨ_x"I{=UfBLe_aM_]c "S>>~<Tz>ǻzQ00L|g*Vv'}V D@H9za=lv80l|ᇻ;hQ09h[t@ ݱ҃rB]r{`˲^e( DT 5G4?8›z`n8P@T]$N:m@1;APdzr2u]&La x=~`Q00<*)~M5%GNQ0UcSy~¸i}y0Q0YL* 떿 ms}C? coYQ1Yf*O^z ptK {bB: @8 IDAT4R괾nK2OGD1@4w4rF2S_9?0/,= t,X":!zw Cm?*fY1\Bo@TUEŕ;ED@T*@>2;]v#WPH" Qx[sYo z* w0IͲ%P01Y C=e!o8(> !>PO?i}QZmD D}Z MUKe̯(^W"YLXƶ EL07o]fKULDa` U%M fe3 OHD@dNPb;m= EhC#JEu鋿`?`$R D1@$Z ){{!5(vuYOQp)&]AYE-m nKjڱ x&r {ʅ7 8ܶ"Dx(5gG 15ysfMCpP(Q1PMKy518*-\ǃ[JjUqb8m3 {yZRrDQ1PMSQ)ee Wx^XJ(B@U*nٌT' 2xjbRU(ݙH|ǥ̯~l.X"a*򠣧<"3[5{/`L"(Y'n+@݈}t0Unlua Q1PJDž1O;#9ǴZAo{jdžtZ(fI%hT~nJ(6<^DE T2&U]uNM=ƣ0Ǩ<(}l"]Vb<>Y^hjQ=j9O"jBBWͽm϶>mBV!&!ڠH@UisW?l7zG$zZr~ '*7߀]bȢDADab"bUhXl) ك 2 نUD?*u]@xPw] %Q0PR1 'DD@Kx Y M~js `XxCj%Qfe쁰k~޷aÜ\!0PKaNO&W"" լ/+:ñ)p˶{n~=D$i 1ex?y_8Q 䅰kp{í@ !aW[j)d5D߭aWBD`6vdnܾ[î@5Y2:In" ưK}jۺ]0î"JL7®@5o/AtuXUQaSEQnʧ.@`džt^vaR`O]&gD5®#4\'2:ؐʰ<}W?D5hmV< *{PXݓ7 2v%Ui_y2NLTc,|(. P̎=v!DT} D6_sU| @6Zs߷ !p09ؾ~ŏ7Z*uӐ_ !HEԹNv-e-pDE}N`<SN56=ѱf}B" QT:AUNtNdr90xY 6?{ϊ}U)BW5v6&I39t|"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""Rj٢IENDB`gammaray-2.11.3/ui/resources/gammaray/GammaRay-512x512@2x.png000066400000000000000000001375511413310443000233310ustar00rootroot00000000000000PNG  IHDRxsRGB pHYsediTXtXML:com.adobe.xmp www.inkscape.org Ǧm@IDATx] Ekfv73g ׏( G)*P\p[PnAE@AdwfW]]=ݓfwvgf_C_zzu0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#Y;#0#PLxi]ww4Ǎx>R5p.j`F-Bs',_GUWc8ڇ8cfP}t=wf[VF?ǘZ<=lq포5  +p.#4gZn>cX< C FsS"MO,{(xyWwtn7 4=}M[WmZ5u5ǶOSL #4:֩Ve3\ rޱcNA q: xܹ;"Y;W!שqQ#'XZιoXF~j}ϱ5l[(BJo?`APȜEFh^DYwEOy,ݜSC@0t.keo5 >smB)_7&~{XoLqda˥ӯ=~Mלx9˕s0Gw6r3V'O`00h0 c b:0e?/;}ms߲Cс]ϔ?ҬaBf.?Ŵݷ3V=]U$ <$#0mi3nM'y6̜&  / }HٷQ3 Aow_`ʆFoZ3fR'^{пR]S.^#435Wh%kYFb:u9X5pSs#QU;qF>i6a8 _tFis?ʛUa6#0Y+ dd9tzT]~ `A^ G (~ѝw|?OX eky_nF}hR<1@#`s\5ѧyy[A7Z^SNj~ܦ;cEw:J3,6JSOO }ϳ=ü?'y_WY^q8F`鷯3DZz}K c 7O̘< U9~Z[w$?fZlgӡF ӞΙE ]t{GB/l<E`gߵz=S^2͑Ӗ;\t1om_?S {~qաnQ(0  E\6/nt+dF`vʽX#chgK?VU'ZžXw%;aN14Ǯm^H\`L4ĈE|z-,p:Rsp_6x?EBnGEC׶ÜlK"{7&bZ5kFΞ =߇i^&*q:7uO񙶹R8$=t,(#8:TaMc?[/jt@`5fnд,ߋ>ϧűw'E9EǍ/,a}i}2z2=qmk>Opd!oF`1 pKF)|[|ݏ}ٛ 멛sF!F b a-{0ز׀F?*"`j8E=CzK3#Lo3+6mOh1𜧯<ĜqW8oo4c3ۄ {f1< V;cvƱwdZ9nh3@]ڊo +gQR˟]~toOoHh4,E1D$G8 # n_1$lXܰ貝uIg_=8haޗ.l%eF l }r-lGlot}IsF`j@,?X=;x.8vνo~Kyhյ7;0@X`{LY##0B`ʬ76)^M|n-wcy0g|x{z*N! $`F``ۏ7ckSƩwNu9n-Xp5Hh˟딖s:aO"0x}bFVL~^[MȵJƟ:g/*l;dVݓ˵\JZ҉vk~y<8bQΜKFhͼ4lFhlސNAm7;__4k:_Frs9wm}m-;[~_ w]: 4wm.Q+-rYN#0=D8daY[F#j)|pьcF<:o.¡·Nyޡ?GX(xF7;`cxo7W2# 8 3|ϾԿ5=v[G.Ǩ8*xf}\>oX{xF`zVM_T@{<^׼eU/`@i>ƾ!AI0uR, .05{fxCd_"#}&bFKI^O!A|>XG%6ٻڋ޿c_ƿX)n cF8ymjS0|[9X!ADB~*WЯpsd#098 wvӁpN 48yv/.laz`ZEs:~ ,@!0#OL^7C f֍Z9w}:El{D/| j|̂9~?sIă]# ?a}zp* h&_/:rHI n,KfO{Ap 0YixtݴP7nn<|vu  2Lt:?QO p`p?` BiV[Q| ZsNy$:$u~;$V;]VUw3nd\F`GqiYU4%ǛR |-cſ>Z_7 8#Y=WhNA46Эʲmxc/f{M;EŠ nl` `z`[i[@?]¢^޻ㇼy y3p sF3N%z.),8Gބ<}>1~SݬO@JFF˷a[|ͷ|rՒ{iuJVj{q#^2\3xfвbFBk]*qHP 0F1E.Zߛ8l{bk>`D4ro52. ʷVZxŌc>gqwfZ̯P`&C0~3zW84zʭ NqܰN899X[9\r+W92F Gx=mrv>^?.bC6[-cѪ?3Cu5 = &| -x6#w oOHHaЅ4sN=$Nooݷ՗Зh.F`8$(߲S~Xk_E>\_߿oY}nKIp:F)06u3s WKk,c;0P)p G-!F`mͺN<'`zB`RǫGaeߞ̝S(-ơd؆Uac \EaOV~-N#4m[aX{U v5"(9^Z4.[_tm]ʩb6 gp0Yt,C i C˖w]{*zC.^.wʈӘDI<0:!?Эtfy!AŸ\w=M3A4 oY< TڜWF W|}asZˀ-~g|˼)tHFpHPFxvc^:;‹Sis^F&yi8µ4[pr?m[,hlWϚJkb`84TvtHo]Ϟ{ 39l.?N=# 0 ct85ӬD41x AtO@zr*M]֪[O?qG QX+5ǜD`RǬCH4?T׌! ߠUGnV񋚁}Ǟ_c]-]c*ߥOh겄7Q0g°=}ka{&rAhƛ/ӵ<xoAhZߒ3Xȶb#C+p,İAӌУ>Zu8/EO"@ ?@!0Yzn~K?mdt }NƜ ~ӊG8]5P >eW|]tBߛKy/R Fr4 *D=՘ 06=%  _7_h?aw[D?:"07r)Vʣqk JMF̜0. g`v@[Mr91 aCۃ4yuϣOXq5khZс^C^=,=y?5"-/*GWWqe۸/},QqlaP:hh~W)+uTA.*SD<軠\x+Uܘ^+v9O96gx]0Imv4?aq0\d?G['l;Q¤ɀ c #6d ?'ҽUvi/nW" @w˾AڬPQn~B7Q0.Y}մ:Oa %) ZQP\TϘV@:i8&ogR{zcOlrV,^p'X-\0 a5GcM|Dl)!Lma|rʝ5m u ~$G~Ǧf? =I0\n~_$DbKI")B(0|2͗MPC%诡1bSt^~b#"Ƴlк @s3 nDoİT:nY[iF,1{;{o q# 3%a"n$) p)-PŎAk^kֶ|7Ӑo.-xY41羧9]>s;IO!<_jJdei[xc^ԛN|0Uk7*_T@QᅐǞtg5_I=,4;j-,~~G\a/׎VI٦{2G'5#]w}lwtp_gӯ &{k߱vߋzt8t\J: oUU{3%+#TFѫOZnEFM p iA!a lk'yKJfQ[ZEػp.5^I߸+ৡۆ-?=GV04_v@* R:%!nR:J2ŀ£`xjlyϗzffF`bom4n@,mk+;{mE~jmCiwL'@7jf_ n/MfJ *HeɆk=ʵk U]7W[5:c˗טQlxxp2ٷ˷iA0i=pՠw]=QIB@d>EwE2d iAf[a΁ܽ D BC]@g)6Q6_/Ѿm6RbK'7z_C+Mh>CqIm)cUf8%+(">UEu1#ňRxRX*aaoo¥sI]z>]\`U#y$:7j)бһn wKڒ+6>E} g+N@wbw`?J3j( [)B`e)H$_2kPNW@dH")Sg譈dA Y4) M qL52㮋/kX5^_h[m=.뇮4V=uwZA7A!GY+M f̈$c$ JP2Pc2%+xRDBR2")(0#2#F[&^zϑ2yL4,8o+,{ Zʇ%ӄqIl L7=PWSQj[3~BuUKsn'?'N0b-LID Wa dAA ˆgˆ5 ) tcn|i 0h|7ŚI=y r;nfjtj`=tӟ[۵-m" [m0L ň:(H#h*ݥpbщAuW9䀘$q2n#ojX`2d#\4Aڕ𒔄 tV)|L12d^*)" ! \L1GF)u Ez˗a SfK0m Q1`3 mg" &-=ʹ~Kv7gg7m* [Dl•iXL1 Dg/x(~0axEBh+8%bU_pگٱcV>b^8cxhko:s{nf?gs>}E}Gxpl)+3@z,k#/$*tCHNˆrIqhR$Z)EVA[-yMBZubjf1塗rL'瞂"I#m \5߶ѺnZqvw릁C5hRT37}돡?!"$bvh)"BL^I^%B"+\aC9E< S QxL YE< SLnVD+\rQ?Ntϧs&6lqk 㢟%]FfU5 hBI^ֻ8c>nSTFpU5O2$D|Ό#_E< S)Y) BHBW7TKR2҄JL,|B 1QWg.) xv:]2n^42p &*USXD`u*VRL1ө%5C HNˆ5  IĺӨ ]:lB,T= !X3sV-B;l_{z3ak[ Jwpfr" &)2Q3K9BuQBGJ TTQTy2K6䥼$%!%\UM*by̥"%CKH)ip jGtk_MK3g pm3Xq[dh1>\{mކن}KIG_0{d(p >=Ͼi+ǕuJ\Tl $KA_@|%J$/ $SC*UF")$ ~+^bdꬔe8(߸g(Z~,f o3Qh= sC,cqPZg#]]SzvFBTZUҊ9 +'W %]"ɔn@^Pt^URacFu0H?I!𒔄LN3,k8Th@ܪ=FA')jmӆ5(n+w=kL]9*+)%0C9v)<($KQ / S E>&SK /yn䟄JxIJBFZ]%dS)ԣTr64 䧿 t܊gL^QrtpGi}3?Wc (T*w>cXtAtKYlzb*كnF0$%)Sx@* E.W(dF2$AU4}֤h)S}'.l:Wxhk`g{67 wNΛ=ylW7 C ZyS 6P/*+<ۦrL$/R^ CAj _E< ST:IqR ID\ZVHe5 Q ɐ^n4r :nLsk;+eM5K; h?aR5R6KaHR"-•d)i)")Y饄 BHBW7TKR2*!O1_D&w ӵC@7|KE-5$5fqN;kۇ2p> /n珳3 LOS!&$]5Hy) IJBD&\Ix(~bd"0]H Wd>EN#Suk:&v4}[0r-#jq_oKuaoc馿aA{pxEJ (_aX2P2HIYOPH2%!<+ӰV$d.Miev3:741oX_767tKl3tu"z!d‚7lh L1DE z*bd$):1fץ8c<;5D V:e6[* |h-tK*TCՐ3O60`)eXOҳ°)*ș TW@{-aQ.?o擸8f7L^|ݦ\+ kihm[} "nn"F.S<@,W@HBWf# S2ŀFK#<԰A鴉\h+R^ ВwN=p$5y%Z6(hZP҈'n9{V$0L +Z(/7h͙MslE%`LE~R(LPu2?MH1DE.mK|w>~{|q4`JB#iCf iM;rAV#qHHY/dH pLI'h];H߽C33Ѫ1Mk`-MWhjg mZB[хQAcFsJ-?Z0B Yt+ 7{507 PlJdL  bedx*"HHr*t'欽6k2HnTvSD~0pL12u Fv,Ժ"pK5Q8[[i۬ojR!#w[rsږFf@U 2вW4vBB!K1%|NKQs(Og?xG͚t'FJ ԝJ=!O !^'S'yBV#S'b`Vz| )kCCx5em%- "[tҥlD?N+i~Ưv5dV K0.ȘP*`GJEq+][-ws? u=Pmk J+SbYʶX:8[~g:ŋK>J:b: 4- Rqd(ȕR YE< SH @o%z{/Zg ﲫ,v} @1kb/SnNR!#u\Xڴ,3DTw {uCâ/cvLEJq/CB$7L/is-3`)FX2$ MDP |XJxIJ?-lG:OF|0P\ݢ6l =YOS;2P<CoڋOF%2h/^Ɇ/z02zdر}o ޫadſ;hmmvdDr<,oZF7)0A.ۇl~̖[g':YX]J8 R!=뼣GMpƺeY{X T01!b UNfYJYGKw kVl <`U$5#@h^7NvVzdN񁁧!NZ{LijӛqFVN=vwCc_"AT o'M|H]o3<6BiowU\AS3"BX"DwF(Px'\! pS_q?#O?3SZ)L;xVFWBHNS>)Ԅ"bV Ny!2+xqhլOX tQ'qǩ¨7ޅx2.W[UE{m'0{v=$yM_n@.R!^[vg:# u :@J*0 rI:xqLf2 )Nw-0 23+`d+0g 4,txUA;1\sF¯,f͚e=:_3Z nv 1EC򝎶:ɰ| w4R Nez2?ō0 ƻ9m5OIp-_loUr^#Cw?׏a~_Cuɢ$?/dǾ1IYb<_mq!X6bRu &! L1 X ]zo1#QW<^ۮܿ?\ղgTba =SA$5Z@IDATc"H]J2D"%“Tvҡ!3ꥑʟ6E@O!iڞnJ@* V //FXj ~t];+ Ax+I~OzJBH; Q dPsf~r&"Nyw2`j+0|Osd-L[蹋pZEOeB{X,(N鐄.w5-wmw#YvQrȢR 2eCf8'3t 2?>Js.5b0C2@8i~ltf tRjʘN"pOn3~x~{^kE# L%In6֨CnL~4y], ?#kk^ e#GK>*ˑG):uyn9o3N[`:?p= qam ak@<2OWT)`ɀ0[ ǿYXw mP*"V,NgFn3(8t 5QB! xbML1}7>2q#栕ZA!;Q@C pwo+3+L!I>! J$b[eH?.VʔW<8{O_ܥVڝDfMK *Se_P6g `#s6vZ&>D}f_Z[[JU+*$%K$YJRad:\3ל+hE䣏v8fs#N n|ʴ")T(w?nI"`㰝u[-:Uo겶(xR0锄Tf-:I2RV/tٓx/`==} AϚ+ L&/nƣEZ~ˏ4MWye~=s$@)L12IJ)%|Kqrp]>^eB 0i֫8Q_-#K SpD UP1oTJPƴB9Bi71p>?sE8Z!USqRbnG2lz1Bը'rܻX%Ub PPUoA6%^@`l۴|9Z+Kuk'Ebǿ 4W4+0YPeAjЙ@ӽ oFpN4wJȯ2!TxÃгbo/BUxGe-sb2lYVrb)ɐ|L2|֌C긻- 2t/ zM EM1 @gӓI@0&q/;|VW?ciq6~to?@@͠bvЄF\}T\{)<$K&Dls܇V+oL&FWuZ C"Π 1!O Șz٭ jOEgY&'n/+39R$"x'$>io"$#PPXMim.Rdb*%Wٽ9E!(q98,SA`ɮSȝ9HD A@*lQ]6 QHGG&5^6lCsZ ~aStF&ʱlrp !XwjZqt^ 0nk6ucs;TP5QgH ؑEiO_s32<=B.n=6L0$ >, / ؀,O,>lBEAC" 3娼vZF\}TlO.`[!߽|ͩgE)mZߣO_"•i*E_@u;"C:>CsRW(2֩6p(r9e8{9(P1Y{oQ{l{Χq01UŎbI)O\:*ꕋ~9ۇSbK1 `iVJ]*޳3X EC`Ȋ XCފ%H T`p1;MЪQ$ޟ>XS@ yX-M!4R /*D2 Q1{am7qRQ7P*hJE:- |!I wc(`zt(4rwD9(( ``I{^Pd ɊpRϣ;N3==݊X5TV)0qSaݸ//Ot+,#qOk"R)_Ȑ"U!b7MpoL\mu9m]`JNDËU\3s8+d?X1&G~G| ZX""tG`Kc~KKһ7H1$U, c qRY!O _>_5l:/\f=g+3\ˊXjQ2ap]NjImbnkI7VDE^$>G*1F+~plɲ M)7IHNlO[,NbP:$!uR,nBvŕԹt) dn;Ʋ4VB^U!k#s ~6 XIJqR eJ"R $e+v7UyOE$! {Ń.#00p``pOŊ}G=F@dtUzEK 1&p-C jIDuo2KlML%Z:aeR?7X]rz7qB $o8;Pf/sWL37 D:p%j)!<*83 s<_Vt8qI=LUPlJIR2hJ S#ܲjR_ O *7^EeYO&t!^5:{G ܻ;v$9~F]' ET*NDª\!#o5+8}"*x)dBɐD,.svsl sG[)~BRZTx{! .@]ES:$V c驮RgS# /t{FEbߘJ'&Rڏ9FsȴwR9PlYe]^:|2;hݬ8~)eEbҘtW_i'MB !X V;5àVUʰH0q&f1vMLTSz gb _[9s~';מGچ)*b)FEU"Ih9ı}#V3-9+TG%~HCux6P'-)Er%O4a nAiGHRM`? y+o{,@'clڽ2;Jdp /ԒoYL_7W LBpK?IH?_$F rEFK п[.>Fj(y8*URHRqXM(;U4p9[7.I)8Aa*娐dHB 払&[{cd?XRRZwqU棇(0@ES:.}{݌@= :( y'oꪦ*'yS[`L^# J)YVW7xRD"pU4,qhki\CIJ#Taq?@G: B7v=@! :(U˖ۨBJm U~ 8u[AwLm甒WW؋ x(~0bU)+15ܽR 81%Wr=U@hH?IHt=\ lBg(BC[R&:DuP(뭇#qq;Y+4Q`-]Hh+h TJ0i˥ qv0u@fDd+:65Y& QEob?\4?9#ŀ29r\PRat$e}3 cPHBf0{* O5X,Dpe-\^6U9 @¡l5uG;v(_@]# :)Jh=ʮ6-/V$'*ҩH( 6;u-D*e)(0P0MܣBkC9KZX(J22ےPiq)C юwpVCʱ#Pg᫮6}rEYcI.F@KÐ"(YOImz=p:Fr* (F@Xqц)H9%ɝ.罋3= lEOvJT3$T)j hɪ) uɇhlaߵosηbnKx"e?RT6X2hкj=P%wudӡi 2fL8R~YD%HwkpT=/? a{NO4d%!3D^iN 6٢嬠1i b=z|vv9hGG,ZmI91\TkK F 7Lt`ݸ&8<@-P C H|z4<6obC3qN K($!ʡwx !X8o4h}9qol;U>ӌ@# :)%#g|;#5h.H uame*3T`!J5zαSZak")F b@7Z]C]a7y;a$8(7) F@(W!hHQb1ؙE?]z/\"#eh P'mN+pyV ) KB"FDQ!B4V:8<($/+ S +<@ M-ae 5OAz-I# JF=,ﴵ.9`wɣn"DP5RC%2Txɣc2hNGF)kD0s\`'9(R!OR@N2$oåkRW5ysuϱ錳%FD2l+U$tRŏo/\- :w<!J'w"n,<8#J>CT=-"T6bri_+@FѶ߸]yڞ[m G&& FA4Ml!JՐ ~֜W透:[4 {qs 0!'N|҇^!GJ~Z:)Oj۰NQ놌55p3 S#VGa^LF2,V籎vմL%oim8l+=\/a].`YQĥݶ|y}KxěEuTtsR(M"S*7;p,^{6nwb((ٓ]wòMeL.I^`降lVxէ &2gSuhNj(]y1 Nh4uzGƽ |[X_(E oc? D j=/;u #'\M4^!]bUpGj?=|c["Ú_.5DM+zQ7SC2E~юvmVOxT:-1u}rhP?eYd8h7 [_+|-hh<[|ŕN4O# )' >F MST0Ղ?@ z*DW_*ႠJ=1#ڴMGeAc)ۥ;洼NK6+y_ej殴MO>ja 6a(#B$C5mA_^? ~SY)mLF8{+BnCzl~-%ű4f<%U0c\‡.8 P@y pm+3BChô<.YYƿ.4,TLE\]ۑ(&Վ}Λ},خRB.eV a2R B*ֺgt1P50I*ڬᄽy{84=%4tKѓyM F!0F 75$B'B !Oҭ斺~Ǔ463eFbG ^]48 *5Phߨlpb֌n>;`p6bi$) '‰Vc{}.<㺧vnt4T_,°/]]ֺGi苡za<՗d٦ @X%K~ E@S :*b|aV'`$G 7PCsZ\_Q8)@=il {`j.II3X'<ְ XubOg/fkkTfWS=')$|l2 IH CoA*Jw|z #$ȡ&OCgߝx/u G, D;>/5gC|p%Ėx$ZQ2};ZЁrc(r떃|ni^0)ً }ޜ(B߶?xف@S :*Fڳ!V_G 5*J%Ipe6B9E< a !b[qJu4#MWCajd$!.jȐ#+D@SЪuJ fnQIW/^&~5zv|Jtc!`[>Fqh,)I!+sUcۡL`,;5:/rɐLqFDR CuJ* t[\v^dnYQ&3SECXɼBl}W{fl ̒b$) )6tdRc #~nqnx+dO~DFŒI?IHϨaAXHү'@"mi0%sC#3"u_Nz8SN34ALzgY1Na"E 6U CA+7|Ie NX̃LQjve {ѯd(I Lux$jbP]/b'@E̱owީärz3U̻rr-B8t e+ ,_Ww "zȥhB8 ޛweW:$_Wq' ;TTo25dFP*qNȀT븍o؃yoHZ6$/Kך&غuR r_Шy(-`ZOrk/m|ͣ>zE.]IIR ҢQ\]"ש-]YA =iOWs=vt:LwG2 lL_ L:Rs9 W,魕H~}K|Z(G7Yh" gKzb=3m?&O^g#:k_gsA@NY@ɓ)Ŧ 0I (X uׯ EthV:/[bFAe]}&KAmٿCjxz&dkM)s2 G[qT[\]5/ ?s&sfNy"_)pa.ջfG]OR@r袱1pȿb[?".@ɾ' 2YˆF0E8+#' Q_x6֊wR@P۽Oaޜ6taC!,-:,7זt4=R_y+{.ӜcŌ"Ρ 9!^=/ &ż+fЫVXzM,dwhe|^FA]T)= 0f[[xIZ\d'9f.0y,9Y+2 #mG#?ktSdL-&rw9q[ǿi1 SGLD@voެM_`P9yijJ 90e$L*&y'3FD'MەLV:R 33i5=Θ; #i$-ȑ4 ట$' I`S}@YG ڪϪ)Ғo#RզF9Ԣc^4yF0W? .wj $%`x\vܲ#cNqdŌ 9 WI/<ë%@ ة^K;lW'*69Y2 #&d څI^b2 $&`|\z=~;/N0I8ߴQ/SB1h%oop.ޙF3>(#G旞&/#&3ڋF.W34rY_h,Oqy즾U>J'#!6d͘; #A~̿4&9\~qԂ"6HaIGdk<;iM @6K Gf Ώ39]pDݢQP@tI ;D`>M &`r\}Ló+9(eYi1HRi!hn~v:gzf*azͰ>zt&*7;8.ozC @ iC 9H\'u88(ɔNNoK?蓷u*MvA7ɈF0jlNf>OW:(c{vuYgl^V\A^GXm^ 漲o(_3Ov nm<~;@ "+@ X3)tn:+ө.zK=,N'a6_Zx1]xh%C435GԆFa[A Ls-o_Vu.A I @`aڥŴU.M^RŌ"Y*ݓK=ԷИA֤"O"Og2rSĿEg0B HX-W^ F}N;Aӂ!]Ѫ+&gqzg :koKu,yJ+N(U7|o)@ @HhC ̶ԞtnC:9]0" >Fvbh_ΥTK VnYwt鲣:ټJfJAm5gᗢb!  T 0|xPE\1OLe՝cz^M# G׋Hd9.nFŹŽ (h/@,h@`\ 4~A/޲/ױ7i1 <p?ݞNl'Mj}c1\)`U27[ K@.(A`4m+=>KC3O:N0cŌB9K쉀 ~s+Nܕ*q/c#!*c+s?!7@r %6/lt*IXKQ ׿>R>x_trz#re;Z(R*Р TyomÐ@@Ќ 0Mj@!3H)]h%{vo;uO 7֠ 9tĨqMį2?:UJ{פ;wͬt5r Dm;]pD](`n?U [,ɰQ{UϱA #[CǞ$owvNp4ʛjHb ^3X}q#(pSURkLlU*7s-ouOyG>4 @`LA`, ~9Ukܣ]'TAJ$tR%uHG?}W:KLTZ h R߮uTn9Z(Ͷ @+ hEm&@{?D[GG9ӴK:GEGa쒂)߾79/|o@d|BƆ%a }U RZ@{tv iR.I ?9B yl?1! !eɕI;%SQWI)롃uE㤯d-::z ";\dBZupm~v['$v@BVDXeq9j-: S0#rM1h4]B< Qgz`ԕ}&}&e8yZѱ/E/{G: 8ӤNej`kQLʈ+PwWW[@ hY -& t>@S7;*ߙ;5x0; |]|Nv8o {NXrF4jBS͈^=  ZZ@]h^G %΋t л>T.JK=vf}^&3"2܊8օu_;,@H Vڊ@Y#ݏ95;򴿻qY?9fI{ (ӵeubF'~3EʔYXE RVh8\jA*]ʐy􂟖5~'.*N׳:VFckD#M%qݧ~z5@h-*@`4ʰOL:Wj]-P߃{sGwNvuF4jV~X^~˹d@~hZFJv"`ҹt䚃/ 9Ѡw^e:Ǣ-Τ.JTLE!Ű cHFS k^9s>/c"k64J>ǻ0F9*IN!+ɝq Lh@M ovAFE`饋4loTRL{7]qX۰E^9iД32u&u;YZ(i7m6WnE #7ӘcŌI'NC!_ֹU@;Ǝ5ZR2v.Ǯ4Zݖ&k-bL(*GF]1e/,=c-fJ`8/mtiRE4)PuNEF4-T9z8j-: A/^e($;̣ctN[LCOǦwCfAbA8hUJ%UJ(ӢUpP\v9G 0t4 C@Ngi=ЭY-D6*k6TNOU&sUj=uOc`M@Ƈ;Z"\OL:fRy;qÅ8UhQSrsۨ|9b,KqDƋ"vA`sx ڷ?:H=IDATaX1%i;P\Ō$[q: h0KK`|;AO:ϫcl-<-f2t>~s޻>k @ (]@@`~W obθ6:Umw&0(hz7ȱ  F@n#!@r=w6yDFpDeY +AP "'!66 Q]rщ= tFK&WZ @쥶Tr?wAp:ۧ.VRo~dR E#hC@%pWğG-wn\7x՛ܶ.,QŤB>~7~@(&ż/ f^N:B黱ƈFPU*wޤ[A@2v]xdih=t8QW&.J*PJGg@ g O7$DS@P[uGeJxhtdpwL9R(nR&ˌ8Y{gS(((A@`t"<Ѽ^:8^ 7!  ;FA N@o#%y?Ju[!)I}hդJ5@ʀ!@߸;'i_a 0~{ -'pɗMxʀlINEF-t^: 4 E&w}O}/o\Aē?qf~,2M!tbsr'$0o%U連y7>f_nK n{1!;@P[4$Pw*L0TYrЫPy_]u8 @( %Q& J <}3;QrIDd):Ap}eV@D@#Z u"foǏJQft҈FPUz[R#  P(ɍB7A`P\,N^?R'y]:mULqˣ?G]AH ޗ gU{xڧәD#ZS{QRgh` o*(6 М?JOiV^dJT,6ҹ #| =C3== B;osZ(T4ǀmTcH4tbaywlmvs|wl#~Y:7SQD"Y? (!%i2HٜY,WbTJyǿ- @ (]CAұSGGV*TevQO[?.Ar ?MDr};)S-^|? bS%@P[ :>3 8o.K^|LqAC@yz z䘝?I'LrU2SEȽKx7+ҵLnQΞ7@" %!%R 0 2;2Ə; Gt:gʍͻsU+!ܫR6̓  PsS`܏׎;TȓGt+yN+VA)HK>qM4jp8#4@9 (}B/A{mǞf3 ϓCϿǮMT/_At"`E<9 @P›.ONU!㝢+m]%%SJK6WI}F Gxo?⃴1 &зM ^ z?agQ29Z(2ŔqǭK 8vF4QP) ,#iPH@ n n8^6sH/pF42VM>+ӮQ@Oy`.ӶH#px?COC_a+4 | \-e%[Uz8wUoL#'(@ \L ,(@=aZr}v% :7p]H6Y640A7 ]!M敾 (ed @k}/׍ P2Jv݉Mdz'M_͵ӎ..VQ߿s9{5tT1s+r|@JF@n;q GFCDaZ3iu]鲇}?ldjD iH@  (MB'Q:fo޻6sY+yb˜qp[i;֨I#Mu )( %Q&/Eoўև7} @76 (.3 UJ|zw?$N LUFP*6r8B@P;~NhAZk|uVjZXY}oJ>F+ߩ)mFi'* 8c@ ( B&>y {{ȧu.WYH2?L%\4jOvyʀɧcBLϱ @{@(=Jx~?e+U l]E/5~d95]NXB@4VTi%4~+4B*6~62)eQ{f7:t(2J<1U^X@(%I% Wjl)]$EYJBT\T;Wdd:%{S .IA Nh ~н M^<==Gn6mmBI+Ɍdhwq ;Jȱz77YK2eQzNV#.> PJpʼnI`߅Ci/3muRۯ:L9P?_tSB,hZd*t3u{YӖ$I)(<--B'*/8cg;׈FrT:2DN-k:V~WeZ5!6LɄ @k@asO..RY^(y=jJV'=N{+?pW;戀7ڛ .|#h z((1$0~#>^%ȣj3MVY %/-z+Mm9Nڪ֢2 }$"tp8v?syN#jݬ<-+_sɩKӶs]sJgkQ, pN̠֓9xO)@P;M/lzmN4qQ ]\Xc ŷ(1ص{~=tU+Z͂j*F$!'a9|_kck)'LĴjݣ{{6p٣ϲe>Ǝ{+6<m;Z jw/@9 (}B/KN`=Ov $]ˍt ǂEP˝ [Ɛf2M xvDza{mɶڙ:9 봭@(eKc TBΫg7K(ϚX)sK]D(nջrgNJW⚪=Zv]+!b[a0gu*rk)>pAO@zXr 43:<)yd% 4F Yoo5:ԟx5صw`oy*qT["HvRM!vLsA N@oW~8;5w֓*~ bm|/Xẋ}A76 H:`ͺM77|M{ݶ3(H"9:R8^$"',3}Ox+:0hQ~5ǹJOru\Ҿ>ojzR7mB- Wc~,qV6Pl%*~A$_%/ " jzN#8mdS}jٲIM`:=`` `vguhۥTBD"t-Ӷ]0+<)ҿdelZ}~_8?Y&^MiݞԤtK@߾FQ-lQfCp BtH}2:3Sy돞Wn %C|ڱo =S\}}4ЍKfֲC^O[ r%R&tl?㋤'>Jw]X 2*LMr/{]WUZ'7U "YaHAU3vϳ+RM$ i(0-AK/?r"o+e#!zKtvɅOv\B6U>'?θjm@eѿ>EAA[C~3mB@ H@o T~)jQZ*-WkuF2BOE4w˱ڳZB{9ܒM[*)D$+T PT[;~'_dsiǒ&$㱠Q9dz#dmQ֑LZ=:avfӺXOK-($-T ~"6ߍM+8fs4.R\+<:` ;`{oߣaGm0@4V ?+ϐv:#A+tiLa 4s򬉕da&᎕l*KX LP;~ ЁB`{8ӻ9\ "u^nͭER# .6˴1 #UٔdakঁW-^}3oudt#OʤWFHms/_]wHIM>4W2"LTDyt|Gq ΅%@B@|hx# 鼃|s% WQmk%GCP\|o饋#-K;M'+lO1eۙlfKyon[,@ `w}+ }pQa>U?%+iבgh?\poOYal E[{tJBb Kh=aj޿'^x?]̔@֠ce"+^+NG/jˍtBn+unMÕ7vߦU>۪W:k'34?v'Y_U*_E5UI+ti;ށ':,]WI96y8/~C"hdt6q΁>.Z\3 =,/nTp@(.uȀ- @se X_68Vyy~U]v)[+J5Q4 68MFr_ٿ=vz:_~4&6 jIɁ PDxPĻ>T$#z[']"}6' vaUW9n4?ڞ6!4Ldg^o~qPRywBn3W BH@ `w+$'^NkN32S:U"5GOWFLݣSl-h#۪䐿o6ӡ9ODfxЌ PDxWЧxv>Mvq[L*Φ_ѿ?.Ggr2G':Z V3{hC~l3(D` ^Π_&@#r⹓ɔ3+ܿ*3r/rlBf|tu47PwSm~u !  Pd|wзBv&.ӿ;4&0{V7ĺ7W栗=]UI֮hyΪR] _3RH^W& xiӫ*'KKi>qF}}js~5ӟ% S;Tk7$C}?5ȓA &uH"p:Nڕ#dMZOe9 GW |nsjlzj Tkzs vѱk^@ PJpx[r\G;ǤtRF4"GGs[V+^Eh@GisR%-ta@)B-!|cE&SYÀζ3͌:WkuzsAի<ɶnsMaF  P|ÂPz=䠇yAf,PڄN}a!Qu({C_ߖ X?ͧsRikH+6+UI@P{g#ix~N/ܡVҥ /P!Fod$W+] #p*$^/dh|]Έ w q\LNYEjk8)De}Q4Pi@P;^Oߓ&jn/#ɔ{4 ^ RB۫]Uyۚlll D$5  Pr'ЏB]_IN'Fn1;&c[:+wR;cem@\}ZC-:-p C(Fq ]CƁֹ]J*Uj#~#kQfwԆiJC'uϘG>h\~ϏH|`kZVצZ_: g~H)#FtĦ]O@z8 WG>)?#oίbd ܆ uK%kb%8:6-Y2 to| )O69gS. sMnTE1rɶo%);UMoS[)H -&@Q"tzͷZX҉^wϒ3_imó;8n5*iu>E8Oٓ' (e[;=JNr(Cu?TOQV6d1z+-/t\ԩ5(ϚX)S"hɩmH &݄Qi =;S=5M0ڠ.Vf jJJq%}e-*\R  TQ ms͛ vr? w]OkYnŽtQK wAptm>] E"Hw})?㐎 EЖk|c5bp&.NMʈLS%tU}h@P[%!%nʗ5ލɌ֥|-5;pUs7*#@Q5ђ I ץK  Pl}лq"yM65A2;vƫSl"}ZH-R E'w8jx6B-XpE?miKt.7)#Aw:S@C1Ќ PP zcЭ# gQOrm\h$y2~ 2G!<%tm|q3__. _xv]A 3f^~"l(`mAEjy]F, H P Jqɱ$m|TGSn]6R#·<aj"EJ3(VAm=4"A{cOHQA{;5<\嵭%Hml:q#)PΈF ?qNO`!H"H@ 48%3M{z/6]Ӊw<ڹ an/Hɔ.urEIO` #d(Witboy}cs =0'f%mpF~Oje7u$c&yd`ln5dAC!D@P46u'zj~MPdjKhIi ؃.<KR@PۄNAlܮt4DO J"'\52YyLnhMG@֠ccA;t54rўӆD]_2I줌hY%S ps)aI  P"Jt9ۉfwǎ-9 iVi3.褚<밍2Ѥ0vJ0)ǒtZ:+# %#d7 m1/ܞnqqϣXCl;zm׺80ǐr#gnmr(Rk)1! %"D7 ]mo߆ZTIS=MQevv\oD#dluqIKzE ۶!.R  TQgixgD4d;`,Xe7Ѱ»dڑ8)#APIKƿU-Y:3E@1f+=P[t YW9MvN&S}Q&yv4]2 }v_n9(AJC@in:jbk{[{k+ '?އ?vrsq1N &+kt)+V 3E93)ע~fam J[ FGT< Є@t@&~jF@{i=y57>@6-L?:Q0XLKN.o V.Odb1ɉdr@P^pAgڛ"~G @`"|.FM 1SF\y R g_ⲳ^.OS@0\RE}x ?ږG$A&F2FG`]vzF 8:&b)ϧ$7D7^rVIov3Q؋UaS /JH vGq= ^yGP|ٕgj!L4& H_jB'à_pqo7+MV,NmzӦ6R|O!ʳ*KO@0:n(5~W z=W~a 9r@r ȁ#P^y3OQ1s|A`@098K/~@狐NÁVti!~Ҿ\]^+^G+Y0"[dZq`ws)-@秇K?z'~cʟիTE'A 1g1@vBԩMڪU2:=Gi%M@BUG^:Qdќ{-e0 !d_%Ñhz Ʒ_t8re'έ`޸CBrf5 J/V^)u}ڳEN)Q@H+0v7YUЇ f|getJx T`,XX# L46N|_ @%IEJ^(MEc+t,ߍVY- ]@F@|u;/T lճр$dD^׍ͫ$pl!,J ,0ۧ6/AHh@R>K+m*IA G.c1̸5Ot,N2 Ш}Cf/PygY0}I7N`Ij-:j]Mv:O㤄xz߸U3k%.컠֛j;OlꔤUM*5 3JQE"MIXxP5i@A { %R0B-$hdNik=t`lGѮj2IIU IP=-~2QVNMV]ߺ d >ve/SgϽnx9~x6Ol *{AyX"uZ-6|Tܑ,bl@ 2j<+~j\, xL^;v^.'' EAٞ{G)/ܷǧ4,[Z y$Y]Rygđ&鑟;C$U/#D# Ft u7q{ 4_03O 7n21IENDB`gammaray-2.11.3/ui/resources/gammaray/GammaRay-64x64@2x.png000066400000000000000000000062241413310443000231650ustar00rootroot00000000000000PNG  IHDR@@iqsRGB pHYsediTXtXML:com.adobe.xmp www.inkscape.org Ǧm IDATxZ lV?{G[J +Hf[ NCD&8TNFXD؃W&`ea1,&! 58+R(~s~}t+Bw=0]FFET#̎\aif;N~ZbZ.{XzXxh}VpN-pΚf5} Z뇱ᑼxCo~;h.9@1A[R Df ?9kt8:,~Y_u-B@0M0 ; D26\Ŷc߭L.kyp^ʉ]u7nO ]0caPT0t:sO+3\3<  U s`z}@ 9snڎWa+<'ODsW%KxutF8ZcL)k9' _ A\2UF,X",8ׂ7UV]э,60iTafhыO˶>c38rEʈZb/ 3($8&L<192zѶWv{i۩:gJ(*oh t?@c)v>#7F] T Ģ`xR *ڴMH>'^ lDVc73ǣ9Z bt~fqe}u:`ؼׇX;-l lorR8Vd<#KK+Tj2&Vv< 3eRO@I2_ <ÐZ®c7f@l`$`XegF'Sf\YT0 g$3N{m .ߢtpdm^ΛB948 U緷ķ8{H͙ˋrц'mӭRDp*~yʿ = c"81BǙv*JmlRnϑ ʔ7j&ʤNY k0;6,/xŴZ?8FXb=$.]K=Do򽣸Ḯ Ss}Gko*SӐ_M{oÅ}T7$tXX=~ Mlu=I(T(8@j"O%`2qylmbkޅ@?%Z=޺~܁wyLEPkx3yN-u/MKf2)[TkG E<>rx{K ꕔ)WMRk; !?IQwnO==JC+,8RϪg%.e<ΒRXlPW^l"4jFjPr:<^pՙs-p|ms䄭ӽLu.kZB3м1Ƽe0, j^jY9«_R`Mgs#|͆%SJu90k"oI^9j|m DܗzGWS n\<›Ey:o_|P2SVJ0_@uPiR:s%'e6fū~Uv1 ZSZ%I#l(mS"UA?5 eKh=h=@Ϙ&R\YR]MA);bc:OZ@Fqamz@YgD9W3'Yg3`Hpx13_u)`]ʨr.){k߫x`ĤӿK' %ιP4ϏMLdQazek\IoGÍP~ ~6P3y8# 率W쓟FV8E3cս } | '3LNoV9l=Vj\!E @ɌN PEͻPW E qc`H9rä1C:07ֳs!.g/+ $~ RHG_E여G#cq8|3/-~DcZlcHв?dOH 9c~^NycUѥ"3rYλNI;cq(uEJ?h?Z#$j߲Y ϩG/ۃ"9ߗq,A@:-_3dtȩwLMq [7ܵ~dI5ȝ'eNRDP !Jw~pBM8 ¶j:KE@+s/Or@@^<{:; /O٩J01KIi,$r 'y;KVZ!ѸFl"+LTQIENDB`gammaray-2.11.3/ui/resources/gammaray/GammaRay-64x64@3x.png000066400000000000000000000062241413310443000231660ustar00rootroot00000000000000PNG  IHDR@@iqsRGB pHYsediTXtXML:com.adobe.xmp www.inkscape.org Ǧm IDATxZ lV?{G[J +Hf[ NCD&8TNFXD؃W&`ea1,&! 58+R(~s~}t+Bw=0]FFET#̎\aif;N~ZbZ.{XzXxh}VpN-pΚf5} Z뇱ᑼxCo~;h.9@1A[R Df ?9kt8:,~Y_u-B@0M0 ; D26\Ŷc߭L.kyp^ʉ]u7nO ]0caPT0t:sO+3\3<  U s`z}@ 9snڎWa+<'ODsW%KxutF8ZcL)k9' _ A\2UF,X",8ׂ7UV]э,60iTafhыO˶>c38rEʈZb/ 3($8&L<192zѶWv{i۩:gJ(*oh t?@c)v>#7F] T Ģ`xR *ڴMH>'^ lDVc73ǣ9Z bt~fqe}u:`ؼׇX;-l lorR8Vd<#KK+Tj2&Vv< 3eRO@I2_ <ÐZ®c7f@l`$`XegF'Sf\YT0 g$3N{m .ߢtpdm^ΛB948 U緷ķ8{H͙ˋrц'mӭRDp*~yʿ = c"81BǙv*JmlRnϑ ʔ7j&ʤNY k0;6,/xŴZ?8FXb=$.]K=Do򽣸Ḯ Ss}Gko*SӐ_M{oÅ}T7$tXX=~ Mlu=I(T(8@j"O%`2qylmbkޅ@?%Z=޺~܁wyLEPkx3yN-u/MKf2)[TkG E<>rx{K ꕔ)WMRk; !?IQwnO==JC+,8RϪg%.e<ΒRXlPW^l"4jFjPr:<^pՙs-p|ms䄭ӽLu.kZB3м1Ƽe0, j^jY9«_R`Mgs#|͆%SJu90k"oI^9j|m DܗzGWS n\<›Ey:o_|P2SVJ0_@uPiR:s%'e6fū~Uv1 ZSZ%I#l(mS"UA?5 eKh=h=@Ϙ&R\YR]MA);bc:OZ@Fqamz@YgD9W3'Yg3`Hpx13_u)`]ʨr.){k߫x`ĤӿK' %ιP4ϏMLdQazek\IoGÍP~ ~6P3y8# 率W쓟FV8E3cս } | '3LNoV9l=Vj\!E @ɌN PEͻPW E qc`H9rä1C:07ֳs!.g/+ $~ RHG_E여G#cq8|3/-~DcZlcHв?dOH 9c~^NycUѥ"3rYλNI;cq(uEJ?h?Z#$j߲Y ϩG/ۃ"9ߗq,A@:-_3dtȩwLMq [7ܵ~dI5ȝ'eNRDP !Jw~pBM8 ¶j:KE@+s/Or@@^<{:; /O٩J01KIi,$r 'y;KVZ!ѸFl"+LTQIENDB`gammaray-2.11.3/ui/resources/gammaray/GammaRay.icns000066400000000000000000004144441413310443000222010ustar00rootroot00000000000000icns$ic09RPNG  IHDRxsRGB@IDATx$E?^3n$_P2A;ȑ%JVU$A #( pmW]=ngv{f_+ۯ^W:U3F!@!@!@!@!@!@!@!@!@!@!@!@!@!@!@!@!@!@!@!@!@!@!@!@!@!@!@!@!@!@!@!@!@!@!@!@!@!@!@!@!@!@!@!@!@!@!@!@!@!@!@!@!@!@!@!P]xuݓwB B 6GL(4tmd#"?v]Sτ!@@uIsw.~b_&xh,n^^k;1O%!@=OKEIy[l*?/M^n٧!P')?$ðC٣Whۅq( hct~!@5/_U*= 70>([XE!qN9{Eg_L ) ~pjxCnT#B z-yg勒(+WY4l28eNz !̐  ܽ،]*, B(NĿ<+ތM˯(x{[A~-EVU*0'ݵlCVY&"+nC8p$]9Zi@!@ sW!iÉCQx_ l_zI{'ڹ ėXƱpRNOW B>hp!+ˁ5y55i+,Nc2~{  /R.XId̈́g_x?KCϨd䫢H04B j[П{ b< vBo9Q?l6p8n24NBKx_h$_^/E8_lBpG?[W ::D蓝s}q nU_W{U>  3oZ-cpw %^{,W6sMw|&$vC,}QpXWvAWjv(pB@?0_dOxxncs.߯]Y`6[>Vl߄ǔm*|LC@8+~}D0>8/,𻮜Es˔uXeНNs(twI B`(xWpȉs\7~ B>ǒ8 o0$gP #kÓ'qp9,O&_؃`m!r3OC'Ba,#=~»Ow ?N {=^+ɞW]Wr4Naa!068`%{_ \}i''<'_+_S^| MB3W`ӞkSG,N{B.+l>_\zXP}38k3!@KNyȟdD>/̱_ė[<('?ZPЈIؐ L!eZ 2ݍpOfLYn+j7Ã~~ JaT ,R%B`8v/|UoZx!aOFYNɅyV!ѶP !@p\޿r1Ja\c?Վ?8Kph|Vr(:U^ǀ [7]RP7SI!oG sbr8#F4T0"Ș Clg [_k^ꉳS|P0r !@UC'^Ն8\ .PcBB 2[0#Ԙ.B8؅S=g5)EO@!polp~OЫ|ܹ`M9  j׼"J_u@Uj!*Aܹ`M9  jžv4ZO{5b\UxIיq@f}hm]2׏KwMmZg=^ ə :=q匶Tq?>94jB0}-B_FmKaˮw+"A{<%bI+GB V&/|盞O:EG\yp'So{N7â[c ?uNa7/.״zs'W {)\ޜK\ @D+!PĀO7< ߙ"81zz̬ I7=TT[L8 e1ڀo\R }תVyVY B`| s6P7Zt\$MMiu"GUy*L B Gz%Pˡ`K72o_~8|(Ԃ+fb @-Nu@`0CMrgn^buHsMnn&6Ώ-àB]eF^; x5ZׂEI!0W0/nC+u;40BJ\vン-ZsD ]$٨B R8Ek0!Iy7/,S2QC@O0  hgB#2d?;\ypUmSv?z`<> o^6AAW&V(jʷq \2~셳*B $B`h N*e|R!b,Ff| XČXQPJ"#0w[i+'(B3ՒfpXn_"AA> ܇D !PM|I6V7/>[.'\:?pŅSs1  缵Ju_{0 / ^~8cסq=4xB&OYNrA o{-To*F!y BC`5wdP *緽_\ h X,|>c`<> @oKp ?jT1gno\$\k|UfAG?.3riЄ!PMf_#z[ O5|sƏiםt_ '^4"1@%!@7mV_Ί`o^Usk |a3N T-!@)Bkbٯ(B~8 |)^}* 9TT ZrLn^{Zz~ \c^(.nlP-R Tdf*(B y쀰{Ko;~.8g']{,/d'|d0J!@@%8v Jʈ4NkE~ 9SCD (R#B`0?g0É=%\A>  z ǎh1 p B qOݳ݈ Y$q>2ةSB7>0'{[sqBsؕ3ZoDm"k*jh(TB&GzG_2WT6vS΄L|q!PYPb.[ukۿxK!c9US ak pu>a (.ld?`c}@/e6CwJ8+^ZX{iBC4pޔgAs uu(J}{G+Ö< 0;7Kb+½ÍAe#$S~4.SB^uCe(^Be*|.y$ˬj !pV)7*Ԑg컥3y6 ɤ{ZF^jk|B8$p!,_db)dPt0[ %(X16BI`%.7Xm,>OX M" .&64}p{H[áckMğ2ވD`x'x29ŧd_C(4u܎Yt+a7/<QN[&kZ%δ[FV 2|Kgpo7rEr/+.7wƾbI'J2I-NޒIO V쫷ʍ@f팀" 3gɽd;$Bn3%Q*!a#9i%W" C].轓n0Skpz|ٔu'oí)e5r8fyhJ"g]}>H [Y/˽A.<3~dG tX) 7 Yp&th̯VX>_ex֤Lj >pTkxE@Oq1ƶҭ:h#D>m'q?dEq@ݺ Wish_q #hVY;m't3!{?(2!j&k))Rפzʛ3BZ!\M)'~{_|z&̾k;3߾)eƾҔ'=vëWڞ 3@␘( !Eǜ)$lHAh~B.2MCQVj!uMz B!<`>"8{ 肚oxhK 5n6aż>\^a Em~S::.sиG`wL_'@[41"3j pmm.x>eXސ O@l6W.i[-zᝮɯ,q󅥖n?c mKF9e:R nOy6t>EXR פI:چz)}. %݋uR1]Ͽ?8Wo}&|ZVlfk6aX7[M*Y[=̣ nts[F)"\jĊ#=I6{H)51R}&tWE{}}  74zk/lo,|`,s#z@v~X#\ LA9OÐP*岐[ &=ҷkDQ1RPE uMz W)6nv=v5-fwwq?;;p) rnGӇ/,x2> Mx$Lgd@1! U+ 51 gY{b$Q[i#74&*C$8D&΂ߛUˡfyoȞ7vܸ5;t*cL,.`~M)'a$1cz&&s]{#3UTR RIfH7T Dk _v{Lk /Z>_߾*]kNju(zXwlk*joΆOsd UrrU,{ &=6HHH\ۊ JZnb5^fiKdoHC.ASiouoGO7 g@#:ief\瞾nHI@`9sN s utޏVC!tRާH]#" 6Rqo] {CBͮ`8Do7{Ң*1$5?7j{%]krCque÷:rю#Dj Pk36xZgp%i蜝FE"a) ʞ^.RפLjDvtt#7\Vq@ ژaE)7}7%+7YB}41%=]#B !j%+6Exmy{  >J}%"2@W# Mtvdzj[w*BQzr9TowMu g( jU6 kcċ1MaiLۧF "M PvicJ4\Si {< _U+6j_2}`~\#y}#@@g!g?|䭔Q$ft]cÐPc)Rפ0XKkCIWWRc:"uφaQOuBqC!Mm3T~ž o?f˷ɉ?>nYbO 6ùM$ ]\JzqRפUċ SXZ$ӤLj8{OH}LL wbSTs1Q"sթY^d ڏ>AʙcZl)}9[|6BA ils;jebl,5ddCOH\?ic }KzF /AC(J-"=I~/6y_pQQsu >S(t5h_e;l*j |鏅_;z*\~V kI1@@Hh<)!@Rk.Nސ0Zk1*#7~ bG@g(L Kjc=yJlT6G!}ZzԽE\  1 ieZ'I uM"Fz+gdPJ$*hD41-u1|BY"ǰ擦-:$N86g9fU]Ėt uVd@5Sj5 Rt&=FϤ`[;ug>ANtJ,}7Sؔ~5.1sO_73N%T%+wu0P])E@Fn+R5LC(jMka\TdE)v5Zi<#ԄQ1ǯ-! a:M@tG􋅫/02«V@n%ҥor" zG`%~ZIvǷ?k2J"4j?Ak14Hd5,eD}r8T!bHC(rYh dHd%kISpyn {.NF=9C CBYVϨIŤ]4t^7k[X: 03yun]aw"kl4wxpِ Hq"@@L=, gjϴc"ŪTn,es⁝j؛"&=OH׈\ۊ JZCT֖ސ:3ʶm(E.$v:-EdIuZ%g[p8'o*Bn =RgVl8Y٦x9i7eH̷lK٠\ۊ JZ2+ڕs |e!z guwPHpwq˗X)B:`|c.GAomMTf)%Mı JZCD4l٩ H?SÕ*6._1[_c-ڏ <ݰq͗#46{l{~W ,s ˫cCF= ٌGU }´ӤLj-PE@$uwX!FW><v7| @+=$ҤuċF#݁.Rפ(SyZ![sRke8wnel`^O`c9 ysOYbБvON?&Oq S͞DK2mc\Ȫ"2x O]~ gcv:hLF!!p؍+CBHb ;EDCYc+"fLn8{.(~p (B`Jkj8lWseU"qBOI"nؿmX@c 9%_~z9YK2'.='.m F4݈D=!lD*X }0 D {gP/pjަ?)& H֚<5aE"a)!/N8s`E{|'$ WԢmbW S4}!KJ_Ȝu_Rh){S7;G*FW`S&OװToW֪ۜw m0dIa$UjH#%R@Q/ 3HDy@:]&6$?A^g;8k/Ξ$xy^Tg* ;%QU c!:n1 "D Ȭrlu/C ڣ&{\<+ےreV'G_wvZst密~pBpic(:xTz}=|s:FYr㻛J@<,/3u@(eboUp_ ~J}˻YO67s#NBi*yBIyw? #`.GTM9r ꊱxV@5HJ݊U{Ќ?oG2*zy['&=xqP"!ROHS_k*b|o)$VxO%ݧw~P`¥xykO/MՑP7ݼ"M--c2 !#(/lGUDoAb -,:_WO`pˬκ\8_2Bpi1iBYb<IaCn(6E9"2z;lʫO4_*!!aTœLddH{Mu:& F&yg|HB`Db#BF 8lsӅf"JYCn,ˌ6;ɋg;3n[Le[0M7R=rH04P8xH! )kO2@ƓZgPKC8>DۧStX&CNԯA*F`5.v5~bFelDaJ?ֲtx=p=vqX')uy#m4 d}~ޜTZ aȯ1[K:2K()nk$+rǮO^3 zU98NE!2^ð?ӛS7mY@?E&="^(5H!"uMz Sl˷2vK{p p'I\n't6 $^W KVµršyl#!Pt[v':JE\  1{'Gj|p}{ M/bWEZ["{Cjh>-6Mب⧧`-}"]$Ց9.&Lip⑻5 /$*rNDl ޚaR6 ^uE A7RV+atfc dOKáz5m1mE׷=rH0qJF`%P3#'GVoUjNv1nv4)4=ʎXEw1Х3E 9rgpW>8)(ADG L>ݬ? ȼe@-~`d>+ xM64A3,C`i/[kvP5 C2ZBBIYkTdwϻ_ET**爽S[lj:,˒) '!F ֿ{:m OXlZMz 0E@)]{^}x2? lGIfY Hx5 ac߼s>eET*ﰼrf`.ċYO^}1+@Nτ1 ƳҘRZE@d Rx8#3t<-;^䖖zV(j"]G|R0 :r+.mh!@h!=h?B~G8$#YED-r ח!pɬN-1"Gu+=u pZniE!bU;EWwZG0¢Q;v){ .!P ov}I9HAĶRK2ݱK ϟ "+A v `xyt=uvώV)K ::VNC4XP"r \&veAi!w9[?}MTCX(B̈ dVR,֎+ 3v.Ћ؝X8)h@IHT *|}i0Ru SevQ)Kz^1LtB!nL+3kO]jR$ C .c1"姂֐`B=# p!,̱[LD@]qaIPG5Rr\JK* 52d$(Gnߝ^!2$^6_'Z a4MopX-E%bsy6BMB``Ԡ#SHMu CaɮBk15+.; !7`Mǀ//".HTptc8DbH]&o }ا%ԣXki%}L=:@ q),mHfU,%ep>{Z߁* ^ pwќSadU,-E{| !g i~m :>b| (P5cLDE ;d =¤QH)MR'F_ {?s`gv4Q12W" C6)!a|!"Pp^a[}R3La, }J\+cc۳`kZ=R(*zi[Z~ jV +&`DO0'v"1ڸt#^,lm:x1@IDAT{^y<%k#3KNkUBBi!@V _[O1hB``GΜ9^Ffq@bVǚl΢K*ҏ`b =M)Dx]E%stZ bz݇Aq06asqDS)E"%lOG@&2RzE΂#?*'.i',VdGkHh BS觤|0s%cϚa`B-է}Z>{IR] P w?ak2!Fd W劀9{& * yoO M,/B.HIB>ʩUm0w~TDtsbc%*˲EV\_Ҧa\ve@! <+ Wא߼"@>%;1U/-$#@cKyOlCQc6_uZZeTZv}4 m,ه|wL6ESU/?KFf,d~S?XΧ;~+CHq6o]fdB$!0P0`g`CDji{% e2&Ԭ(O^p3G=᭧441 6ċ"MYn n:9~G$/qTUbcutMW"4 CX?ힴLl @@A bHChSXZ*5gTjgl'!)EkHCء ߕ/YY7l:\^J|* ڈ\Gp{^nk,HI4!0P0{=uZ&?j&K[J}2aDBITew'prS0![T~$hu uƃ"(s<4*2ZD6/t뗜N6E@je[ZO~͒ي83m0){!Pb%x}4G)$ԝ""B+?oǍc"!@@&dYOgg&69 8\kh`> Kѣ@_^42U &= ^\!׳GvJQa%so.c_7Rds!h̉O !@@fkgbat7GtC8v|L_qc8=M)+!Q<(R$L'rE tS>m> 9!r)Ă炣s_!c=☻ޱӘAd*21)@(໲"k=*"@Rco_\O5$xHHg*4>gKpOga l8"2o~ȶH}3?pGDE LL<{]Ҷ&=9B&^v5VaNwuf*ZĔDEtgj5o=Aj8Ei%Zs|+رꀨXga kjcc>&.M w؇lJ|LHc b+O!z22HI cAXf*E&O$Nr.@,}j8D0>Py߆E\ X6E݋t29X˜"' ܠpJ)Rפċ8.[C3!uh4UU)NM%/c8&"tn࣍ cw@WxlՉ$4enn}SIKaic鮿Ywq& (peCBi@~L&2 ba>,X6YC$HFώ@ @@F& O5iG3El{ hjl32 #c?y`Ky4u 3% $$b$գ6$ӭgΜ\϶سa5`i WlDO#EJ`E?[}RtY ѰPPB #%J7͑@= \kXV-f V3hTH]ślaLLh۝'H3vjHC>'ETpraU~Cf@-"@@FfC޿ﶇ#K Qj,\ ?]eg( qKz\Vj!uMz `QI7[ m+5~43lC 8ogk2vU>nUYc>xeҦ 4 j8y~Ko/\ml$!s&^CظSXZTj[Y٤ |l"A,%8[gmy^ ,L%}쵅]jZ<5҅SXhe{'v/j*24)<9 tUZObVtwWR # Ja]Wj]> @Tgǟbp Y΀@;ÙzkS|$yH_gRO.*ǵ l WRoW<8-,-6!j*24opR Dvjm.O.WE[(G=P(e/>'عrJ-"=I!Mjۦ9{MW2ɷټP4XY.RdRS S6V!L]vZ+ik_Q+tº%qe+=}2bb: SPUI+m"$g XQFf#L/"=O1 iy3n]0":D Mj(._zKnBxVUDyH1=rȌ\{?ÊF "*3~tk+ ŕ|{WZX"BT"@@&/I/Lb83f #'s$&="^4n|~.c&{|OXC&_b419ȗx"xA@=#@@fwŇ]*?:>BI8;d741@Rbqԁ4mnkyw5 ҍT<ˋ BG.4'P>iOcbB  ˜*K iYd#>2UL2RK^ll'W-JnQ(VDje0qRO1 zzqJ!PP .|B\mK_%z=+tE&@o/= I!U< n[WPҤǰt ٳP(NSFV3v WiXdEV;t]?=^LA1Tdprwy4+e2"T!ÕA7.[KE.hGTO]~:;bHs(S%V,U{#3eM"Z{Qh78ScƄHaŒP|v72  Tdp¥{7e0< pQL7S˳a| ٨.4s buGiGO4-a% =苚x/Pəb32UqWK|ǐd*73ZHhFAEaoCc*05C4tI0pD+t_{ iFa愰$;@yL,/OaIg<^c8R?KH4/+2uZ miDV2-ÐPcT,Q+>w;A0zX<!Lwe.pjI.1D*Ӑ@X ԉOb(é4\jE<R) z9É߄gӕ|>FOkNY?8+1F[SOSJOCg@&=FH=]w>sd|M>^&lC^Sru$]Oc?N )TӉag {/߻"JS峠ߒ>(@T6@cLc0] 5!;E*A!@P@B #rx Jl(c70!j!poN?cja6vI12Jƚ8nHl R 51"%l4>+n OkhoQ( D:*z6HOncYcB YB 6J_e$Rkj$6!P@?B C<>Crj 1 Fώ̸2|W@ sDbY;p!bUb$Ҥ~~kL!@`hMdm-Jl)BReT v͙PN}8gݶMj1!#8{񬨷()H' C 2|oI-:Q$g3#@lM"U7|4nd/X7_+,_8Cb5a„W%Ǿ!!@ 1E"0P @2Mz AEpM!@ *)բ^V/n,4LHqHd aA6ӧ!j-@kcgإ8H$$&T$&!x R 0 d5 6*?i:F!h1lU^7GniT sxΉA"@!06;mG(0[.O+FhA ٢OMz PG8rK{Cy=2b"B`XP0,H=:߯@{%AD:fdi ./jmsʝ:Z{ǥ ך`xW,@ka\<{=!@ *i@G< IPw$(j,7L!h.ƫ"g9D~=#3RQE uMo5/ !@ * )c@t J6Z&HăPUlb)RC1h0s\`;0cZ>'r-I޿!5a#@!#B` @v[X\j-37JzJ۠a?) Aul{tk^[BX)X)Ȉ* $Add<]`z+Ja^[Cn^$H87A4T4tdHTr&="^l"9;=w{P.v'ڥi! .hg2Q!Pȑ!0 onu[NwҟisV-~3l<ק⥰.ic*iÈ _FGю1TBr@ThE@ $F51:<]#S*{u!VyWЊeZaD`Bpo}AvD Rs99uU#br{@2 C gN-,D@ JH>*"л9p_J˙aHC(4? %V7vrӊTt\Ν}曎  @@E`$'@x}1ReRɨBijy=9{|O$eumF0E @2Mz rV1"DB Inj"KA@ &h' *˛zgh Dyol$51\%.KSBPPY<!PJrEG=ąxq t%f8-i{U+5H#2qa_sC{B,TTOFT!^>F]Ϧ(J%셎Y8Nm/lkmm%4/j:^nL "@@E$g@u˵ljqۊ RWs12W0O`p5Zi<֓/?@zؒgBaZuyn+ `a_9 lHM9ӤUċ SXZ$=|߈hOC aK "yKE?@`4c3ĭWghQx1 ieZeXTg&.TT_NT H@|;ԉs0jQQUKbًKJў%B+@Ȝ3؏ʩO̎6쓗V߮ =Hlk.;x9*W{󄄄 JH( B]q]D!+$w $$4₏*D!H 7AB;ӵTuWfvCfNsԷHΙ~LˆF]u-,~BxF@Hژ @[ J& ] 7۫saҲ%2:tq[*7&AV^5-HbkzV);~j=g}֙mV*k2XPgR^R]|eI8h@hdL5%!P8p&ayTَ}Dٔڤ[a{EW{(o/8"@Z ! HSk>msjS]1Wl+;]{(T߸풣9h"y@F|tdK薫Z%'8vLT:`bf9[b2䔯qSc\HbknMh<gA`(EGoQE6vUI̘qs. d}tsܲ>rEK \< @0,1) }B;܄%}xqry&i'n7nU){(7s+x 4 3nj PS5kܲU \?9XQAtj4[L@L@c: ge%Z:nYevϿ#=o2߫"\Jh8 G A` HUZi%NNzbIj6O~ՑojXx:KEW!DXh( ō@6FTڂ#cSCgmp e:#F4[ +˦m+,g  &1ԂWD%+̱v gPR]Jֻ +ii"Otqq"`c^vt"{ϹyR av|,u(Z P-I3h8 G AF~0q$)qa2*|9w)Z d"1\m`n\p&.˦- Æ6|_ͻa{jӝW!L,{閫 [dIxX| "A;a^I~cyI[<;l](3sS4Jr+%SyOHgCRcKg8ے>p@0\1/ԀTWyX~'ώQcd{sn*+,[(%nYD @ݖ /z4p+'/K'YJ:l}7=zZdM0AE@р ų['q: e7aܹK|?UN(>m$гu3(z-nm_6lֻU?gLI/A DC&j雩-e WLsZiۈF떫"e Ŕ"TyVG 9%ЖӸ@ HA'$ۄw.h}ԽܭFu+8@zû"wAo[nMR?6 [| 2=|Z y% ;@`[z'|Jq8mm7%?3X|ߒ^Kt@@ Ptc sWN3,ɚ%:ꖫ2ie E7r s(rAK`TG_NYb.z麿W~w[ G Ps3f\*J$k۔N.I{6ѣ {(rENΠߙA㡘RfP'=ٹj1x mB()LGLˆF薫WOvoZ P( Q#y+ Br|h=neJs@HPi+A@+9LFJH nfE]|- @HPi+T!0Ocl" ؟dr{(vH &@ 4(6%FYӴq7zKaD#h/ gɬߚ@ Gp#`qMVRpvu].r^,q`騛n((8 1JI,O6d_)/Yzl PD(k"~nmVk Ŕ"M  PL(o4R3^&wAtU= )0 n/ @Pt63o$L\t92KQ{߭  oBomv'Qb%J)8ڃv?YV*X!JpxBԩȰVIuܣM~Bg8jqTm&h bC3~aJПTnh:j5ԩw]6 R']R6@1(>!J0|/$qN,6G k~K[0P<̓rL҆6@1(>!Jw4)2q "p6媬ᢕWzRbYt _ؔ B@PmB  rW[X6dR @ P( Q=k޾3c (SRXjS6&&*R~oR m(( ~+DMi7W[p؅fuТ@JK+GzJc "Xh[g^72wVbw ,N;99l*o C@ P(S TxvNZJqub;%z~Z5҇% p D@6 &WB\N0AB%f(V)}eCE 2 mBmM#FJɜ~^n-Wž)DIg7NyiG'.g/:6^(9*@Pbd8MIy̝}_-r^,q=vq+N%;  K(r- B]-+oGXsubJ=#}C܃nđzI卤X2Kr@ gPlCRC mr D{z-̬8Jjl9gӗқ>wY-9 " Wہ`@ 埢+mE\T)P⯣&~A]bFg:zL@a(V!V#@w`͜YboνBI);;*T޺Kڃvy| ,0Ё@ !$ӖO*yDs'ndIư|7$^.cRX쮠HC @@6 !zuڍ+YGRU,8?Da?Vhֲ6> P ( Y5zڲwSlZ˩f"Drsɑ!щUzWv!>~v"molv܀ P(SeTJ̿Q&3K,ZV?J\@ޮ7;nv*@82)Na.ߙr;N[)',)1 Ko_ЉI<ٜy7m86 lBleu/JQtRl蕑c/ݧ2}]Voe:  y& ϻZanOw9n[eݤLRl3N(ڃ_zt6i4ZYV!qŃ Bll#Π]R)ك%Nؖ.U]ZM?oIO^ #p[>cvLbln-RUѕbsGοoĉk lFư,@(ڎ!ަ$]OGp߮tvunh']f+KCUMI{yYFK#^pi@@PBMKӣK震l؅f[^iĕ C񳉞szm [ P (O LyY6K!( |!.7$]Kw ~g_,,W-DA(Pegj]g:c:+!'¦v˒^pʷ2m@=BML`db׭m.RvPW]Yؾe7}ᶒi?:E@ Pakʪ2D6va)mĥߍWBԛ#5Zt 4@h@ P~`zQtq͒R"L_%^})/'J7@<52qC@q 6R* g|\k4/0-[)$:oRyh=*Qv!9](Kpbe)c"ڃbuIkPI )Vod:SW(.,:Bw _XYkb G\(D>J:z!c.D\e(re)߈!ߧB„QL{8oS̸NXAH@w15-]J5] %(fXV:ݴ[4Oީ& b]/s*Z>G{{A"@> &%cii{xM{XcFevzP(T@B|' Åp5K%CdcbM~V;%R6m*nqԝc;R}C5A%),,5D@Ah'0uc蚼]8fI#r5J<_tvnUi:FLH,~??A<> [qY@b@P}B%PjEەtz9ySYrS/Hg/ #R\"1u1b2nݸNJ}eA@ 7Pf+H?t_+YZbjYU;!ٝt%ޛ3P~%z7\;n-A@71= 9" GPw'Z>᪬iD#$4 IDATSxw˨k})== ArM@*gSܩ&UuF2BvW|Wۗ3k`ҥԖ"_q=k?^ ^j}@0{_b4wIXt.YwJV%V{@"'! KmX=mg1nD & +Aǧ$QN,Ŷd?o?<1[1@yRodbXl?}8=; cGHAM@!S@b5N@Zl,GWp֘fW-b׸ޱ ' { B` K;M |:YV_F/dfu{Uz8~Kt"ϗQӲ~Ea}o~iGh@J@^wq]  zʡX,˾t濬Jt)#! Kmv/zo%47-V$SNZ;%1ӲCHVb[؁Z{GlP"AKG`]jۤđ)̫3n. H@ 0&#3K9};+HZ2V[odd)>$z;pVW9o y.-[b0 qkڻvmW>/Y -)9U[:BlO 3a"9YR&FYJ#ۣ&JܗY-@X?ϯ9z @ g(;Fo@'NQF@bM{ZJu?ZoXA[Q7PF%v7FqEo^ "Pۅ`I@HFӲA=ϟ(☌D;nN J!n'g4@F@vԓBO;dE='F#8E/Yy&{-}E'F9ډI)H5I 㓾-lL`(;&m /ΊFĝI܋@Rzf=U(ڃƍ!|x7DGdJc{p m3yĖkӏd!fZbjt3ӫ$](Ʀ1hmxX=V7]&y⑿1cVA@D@v @O{GrPVnTZ0L*Xe-!eQsqbۛŦT2kOp &?AVq_n+ԳHF7Ee_V|_b]2y>xFJRb<øB F b8EG]y+;-.HsWz=r]Zy ۍϿ3E{[bb2Xz>P$ y& ϻrK08N:O&#vNή^^xqh*?G";-F(S~# BRXbeTZ0LS6LrDq{No:FkA&:#8б Oޤm@߽Ad9%лc6:+<7GZ-#S,C^}-3[%mj06d<ůn.%jhV =! P(S37?7 JQntS .K*k9=>D?vTҙPͱ/wwHPALyw[L9ҩBCu_$O+=8(qg?w66鉑#DV:?*YYboɰ@ c8Ah# ZQQv –rylQcw+yRt~xlQ!vX& rO@^s:? 6i7P)=ykN= ~q?CU>1KNt>Q? y# o;xrK`D,2QXgIYoRQuhbk~^ +@ua@I1Jч(H$nt+GOz45 $dU ;t@=B9 pМ{Q\(}Pwlь+G9QӮ @w`YgB' l^~E̢%#ar6qG>+U{-1 &;ZRW't %0D"@W}acGYe0 V+9yt1&:s~[YrCflƇ P( Mr8 8i(_rڴFcӊ/YOV=?FHNA'2ZΖhTB 2 lBf;v#?].+8Tm}R\ J:+%qDA PtcV(92(Zy44R5f{& SHTGA P(Kq0gA4~M=0?ZOqu n4/"u5žn2Xu@rI@.A兀/6Ī㌶/'lNF2ӇYZΖH@ć6 oN}GJ[[k?O<7OYK)(} |FS,:](s)n[{˙3O0 t%`?א+޵ w(Co(俾t%LmWNJ[Xh o%2k%J+u|)g~=s?4?gP0GcJF4ArN@75@GЌ#0Cu#J؁Xb*Ctի>AC@q 6G2w{+Jb:IA @Pac# 43:;{~(ċu3F)J,@8Pgi L9ҝi }|1D?d{g2JQFL쬤^R H E!(;8NkI{;'L胜Y(ڍ}IO)HP@ !}AS_yᜧ9Y?o/dQ y# o;x <,f[H-fD'>O@PBu$5R)RykL'_+ g0IZ0-kVEjRx"@A(F!(+4CC>+s5~ŧ#dLȪdlRlKt JN٘i@ ^ 8  Fxe5 Vω|)$%"ul@ PqWS mФO{xNZ ]lԮmN)  @HPik(O\ê6:;cMւiC)RgYc[!Yk "@PC`'gtwT?s-_xOHFHz! K-EW5  P,(_!6kLt( +oNAX]kD_%IW8x^sMq4v=LC̈́N[H] \R; &ApPnp HՈ=)v QIJ֖,e8#uU۔x}ղc@XPkm(!ðU]U ۬+&kN:#!"DN@@8Pgi53ƼwRat_`'E-Gɍ, =X  G@ גvxuIYqJ_eUW+ߖ\4!KsR(bF@PMCȵ#Pj7Z#IkJS^LMjKa,FӊaHm[W[NA F@6 ֖}j;by ~EGPԕ v.HFHFA}TJILh@(f!PJYQQ֗uH6)R:2Gqf]@Dx(E-ZsRͭU{NXn8Ysbӈ|Bgu D_*4A F@6 ֘{xľf|Q:MXe3T!6(g&vG -񛙊Tw(  BnY󇺆_%ןҶN惺G` KLm@hܙ@pPnp3&jCΝ:{17KwV/`fgyN\HZ[Jz 7(#q \{?5exMb;Jn @q $Ł['H*{%J_Vm΁L{z=cK*]uGk( H/H!,%Y-}kFN(>VJʲo|py:Jq藮Z E&Ȼط8T"(<z@v F(RiskLU("woC(:EA?deqȝ?RP7rJ.$%tg@"&Gz'>A Gbq6ijEXyktX,-Xx@R_@&E@ʿ!  <P4^b%C |rZKSn~]K,#6Mx"iyPJly$MH@n*4p0n%ϰ[iX+Xk޾ɀx?ˏP4MXP yggqP7w?ׇՁ@k@ kVskR6@@<{ @/~hPdKr@P$z~3t[0[n_+3M MI@Sn+5h\>>ws.  i@nY-b-?-D@ m6Z[:{RUh~ ]󿮣BP.#- Dn:w]ˮ #6˭N 8 @Wݣ-^e<k h6&/oZ[*!$ @@*;uTRHί C6瞺q>n\D 0H( O:}>Vݨ|koX   T A"(7).1pM>, @(sxs~F'?ɁMzQk_Z 0(rPp$0eJ)JKߣ˗._\{Z` DG :G_FkC[\4?_]D PC8PC ydgۛ{6ri/jj!ik;{UqbF &ބ1~:{%iaG|q ygg̘-@x _(7,*KKtThzT"@~ muLM@@l"/Sg_asRGBIDATx] |E92I&w;j*M)4D1LhjgИZ>az;U᮲"-amj%C&i {#oBRi]zclC #j &ʺ;{7 am*}AO %13CnF[:ƾ=;n|8;WIOÆ+OC3 FqZ!@HbKEE?|Ny~>,:DGurٳN@-3znxsTՊugQʐ|#UL6 柴~cy1і~!I ^8`rMJ L5 Oo(ɪttH#DIY~I'ļV?Q X2i=7A.WX[vj!2"wo^+Ѿnm?.\LӘU6'auT h(?{Ǝvg. #X蟽P™9n0ٻgXrq+t"^VٔJa~o~g_$Z wFM%tAt;#q4#~9Ub۩kS~]Q(G:,ywL1R5HVKq<<!~F_ν1N"84ZԵҤNN㹴/bd8vrWUiqgl}Yeˆlj@f=y'XHƾ@PP;O{\5@ωO9 !+;Q/{ZR6:JO}tbb{SO@)5RV6mseXⅯ~ͨˆO X}hzj_2x( 9^zҺ<&~>lT' q9Mx[C0=QP0m= >ykpOu κ|x&RX*t!'B(@)sX)iಊ4_.乻\ߴr:uN͙W/w!t3u*в^L.oD7ldWGDK˂ҫkdh\] @FYlβ5c>V $q \9~qwS]x=tڹW,.@gO]<؟ / "5 ~Ki!-8Gy1NJ𳜜ea7 SZP MBQ?0ً >dEXW-l|*:uك߾ o:s.A2%T,_Qv1XSgȤ!lFM(G+O8Z(OWL=?&6|ʂ6yܪA~˩ɜC'T(3kpG=Tu/*T(n*[n8㑖1%@kf<d"vw[ " Cj@CҗŪ--қycErHϓMZ[h@ACX(Mb ߗ} $e- jGn1J1ͰQdW/,,zmHjs:!Ke\ZŐf>I[V?dI4Sp$\ ).^:IK/]ffAI rq֢=MħcmOPlN6VSQU#F gb`{PP)YPe_46)2/5/HѪpzlQ 11ML ` Z(J6 q,d( ,Y<ܲxdU*K,/ys \9-/bJy1X6`LԓE"7x$f]l&@ˆơA@1WY!)ML p`5.&_w#+oB0&k~ :&PZ{j8i1Eu& /@`̥iO!|d%$ *0ɲvoiܾs6Q7؃/Ñh[@ڎ;vZ #pGXSa!`)Dw.FdlI/ \=Gd#&%08`)g.VŠb>#& >K?L{MuSj&ö&ژra|@yx1%I;qWlC R)I/+/I]zH ljzv1򳝬UKטxxI񌃙K쌦mlf\qԘjQW);e74AT?D n!sެvHK_=Ս7 1nS zۏ9fsex[IBgX] Vt,WT|VۻvJ{(da)C7([ua*rc-LސXY{8"gM$Dt]8FQeN|/V'- 1膹"X\ݽHDtU4PR Keח97c-e kZ箯9c{F? $]C~EBR,,Ir9DXkI"E.{mխm/D9^ՇS?$Ont`OR=ފQ㥇$77*{õ9ׯZ5~Řԇ\#|EɳAj5z1f hY({o1e& v@moM}(b!VD2]$֯L]jpdޮ{@l-ujP%hk Cjxǎ+⒟|gqE4Lz`Cѓ[i`OĝaJ1ΒyITiH&erc8x80yI;]^xkœ,AP֠ L `8gj%ϸ0:LNk4!-`3$|IpA[\Baڧ ̂lJ!V%D /;WӴi%%R &Jb_ b(!Bqַg Q,5m/tnPU~Xfg _}S"rK'zɔ!xТ/x )$O4T49w7iCkGPظsoա_·M6s{6zlOtiD٧?3 n m5E T|IwƯJ+cM6x v^{/)iEBM3 8 .=Pu7Rݤ<0z+ep/pd0Y77$zU,k2@<68Mvp:X=Kjh87' Ҏzu zW` s>(ģiEn+gٵ |+>3~A<$ e4gM o`C_w,& JYʿÍθ./Q":_}I&Ҿl^Yz5 /4]Z δ!vʉ>Z}Rf֕GWHХՋxcKr/?S漉 BMo) {<DyC9cb:Q$Sߏ_HOA?`xGi  L7VWq˗zlp_ט*>AN#3jo㕵UyXc8}LJ mLA9`3^; $_!2e̿'_(4k"*]\$>d<pXz ƒ,U$E{ y \i`I!>UqBƾtX|ߡ _/w"Sx%`&i`I 4ZwGp8"C*5l~)A78,@Btgg}\;Lc |hJhw1s jmmx`t{ P[`v>_-jٿWxo7$ 7wԎ8[@!>  _՟)!@8%`=ۡ\%|ex!iR75@sݾ!N5(`݃ W$P.5$z[e9gCq)f3kFDMBǽWw `[ kӡDmC^ d$@F d$@%ZݘHIENDB`ic12 $PNG  IHDR@@iqsRGB IDATx[ tT}  Rʣ5`E0F PZCDt{%z@mHjS"/KxjA)KHlvLwoYq.lf?\=7R#AFy xq\8^!`{V/ˑCt* ^;4svGd!!ܵ:WF"uPPR0ox0AwÄԜhX_uOw:G(8x?2{#`y.`IlHTĴOC ;+F)]s׵Uca L Lg3)?}Pp*~$tɴɿ02G(sMMaet]>8BS*?*VkreĔA#>v"\@~_|5##-e 2a>$" bmmB{Qw77N jЖŖYGCǩ12>B,F[œOfXӇ 1 59+C3Xn9Q ]'81fScȠ&|k`O+MJݡ(4a>!do>t|W^-q xEB_Ӏ 2N c!cmQL/_^0gUe{m%3{ʕ &'8dk\o0*J4v~&ϳ1fnAVXֺ;01B0 N].Ľ:xZF_DH??ӨBXjռ[s{%ܧ&M_6Q>5kgD(@ .zWvN6,X4kM&k2s[qe_[)N<.oPǥx.h6%.ZA $"]A/b47xfL>)sS~%dgؿ tO[?awɵ $+Nr.G;ql.^oDUZ_,Rz}4^^*pxS/5'0OGS3냹CRռ~[  GÞ|JO/L2:RхG*HӮ)}c?fcE"*utp."B'OM.yBX>,SI*}XgS+.5F6iҝ @*xCL(%"j9RQ!Yya:*3D41W(en$op磌U/Sb0 ]~p`ĝ찒${L&[9"Ƽe;۬lzd8$a!M{^v$Xzi[Zfs{wiz#8ɥլ$)4NwZu3dI/wG$%_DMӮuˑYe6wN!͌4o8 "r}qh^Όؐ]9/Ůuқ[hldM[Wk._cz28=~-B9 ـ0%߼T8I@[IP%GxI>ERjkԧni9n\1 @ϵP-qPr[eEFA? ,z/ Ē}`ňO+ g W"62>VF_Z:6r{Rchh^p&^@!؅t ri͗v^i' \MlL. O2@Aǟ<6&)'m"f!i$:_7F:>]DڳݥW=?ISx/.=ډyRވyq%`H=OA?x1ZF4hJL~qrBsNAq#`7cu[V|lڏ*+r'o.VIV& "wĒU@Wڄ噩fk+`'ۣN\ Ly|wnK^H2 = }@k;?> 5Uci1棳aK:ϯRTUVoMq:x}4E7#\!3;3 "KenYk*72;л(mG!/ar)6|$1]j\9q9'5eU? _!ʝG@g֥ D  ٪Dik;A =/ޱi`ETXke q\Fg+e=_P}:QϋwlF(s0ac@xMԇy ~L/w;bSHfLu3wNY. opf%uAĔ{MYs\Z7C  nbŗ1ꭇ R5p.j`F-Bs',_GUWc8ڇ8cfP}t=wf[VF?ǘZ<=lq포5  +p.#4gZn>cX< C FsS"MO,{(xyWwtn7 4=}M[WmZ5u5ǶOSL #4:֩Ve3\ rޱcNA q: xܹ;"Y;W!שqQ#'XZιoXF~j}ϱ5l[(BJo?`APȜEFh^DYwEOy,ݜSC@0t.keo5 >smB)_7&~{XoLqda˥ӯ=~Mלx9˕s0Gw6r3V'O`00h0 c b:0e?/;}ms߲Cс]ϔ?ҬaBf.?Ŵݷ3V=]U$ <$#0mi3nM'y6̜&  / }HٷQ3 Aow_`ʆFoZ3fR'^{пR]S.^#435Wh%kYFb:u9X5pSs#QU;qF>i6a8 _tFis?ʛUa6#0Y+ dd9tzT]~ `A^ G (~ѝw|?OX eky_nF}hR<1@#`s\5ѧyy[A7Z^SNj~ܦ;cEw:J3,6JSOO }ϳ=ü?'y_WY^q8F`鷯3DZz}K c 7O̘< U9~Z[w$?fZlgӡF ӞΙE ]t{GB/l<E`gߵz=S^2͑Ӗ;\t1om_?S {~qաnQ(0  E\6/nt+dF`vʽX#chgK?VU'ZžXw%;aN14Ǯm^H\`L4ĈE|z-,p:Rsp_6x?EBnGEC׶ÜlK"{7&bZ5kFΞ =߇i^&*q:7uO񙶹R8$=t,(#8:TaMc?[/jt@`5fnд,ߋ>ϧűw'E9EǍ/,a}i}2z2=qmk>Opd!oF`1 pKF)|[|ݏ}ٛ 멛sF!F b a-{0ز׀F?*"`j8E=CzK3#Lo3+6mOh1𜧯<ĜqW8oo4c3ۄ {f1< V;cvƱwdZ9nh3@]ڊo +gQR˟]~toOoHh4,E1D$G8 # n_1$lXܰ貝uIg_=8haޗ.l%eF l }r-lGlot}IsF`j@,?X=;x.8vνo~Kyhյ7;0@X`{LY##0B`ʬ76)^M|n-wcy0g|x{z*N! $`F``ۏ7ckSƩwNu9n-Xp5Hh˟딖s:aO"0x}bFVL~^[MȵJƟ:g/*l;dVݓ˵\JZ҉vk~y<8bQΜKFhͼ4lFhlސNAm7;__4k:_Frs9wm}m-;[~_ w]: 4wm.Q+-rYN#0=D8daY[F#j)|pьcF<:o.¡·Nyޡ?GX(xF7;`cxo7W2# 8 3|ϾԿ5=v[G.Ǩ8*xf}\>oX{xF`zVM_T@{<^׼eU/`@i>ƾ!AI0uR, .05{fxCd_"#}&bFKI^O!A|>XG%6ٻڋ޿c_ƿX)n cF8ymjS0|[9X!ADB~*WЯpsd#098 wvӁpN 48yv/.laz`ZEs:~ ,@!0#OL^7C f֍Z9w}:El{D/| j|̂9~?sIă]# ?a}zp* h&_/:rHI n,KfO{Ap 0YixtݴP7nn<|vu  2Lt:?QO p`p?` BiV[Q| ZsNy$:$u~;$V;]VUw3nd\F`GqiYU4%ǛR |-cſ>Z_7 8#Y=WhNA46Эʲmxc/f{M;EŠ nl` `z`[i[@?]¢^޻ㇼy y3p sF3N%z.),8Gބ<}>1~SݬO@JFF˷a[|ͷ|rՒ{iuJVj{q#^2\3xfвbFBk]*qHP 0F1E.Zߛ8l{bk>`D4ro52. ʷVZxŌc>gqwfZ̯P`&C0~3zW84zʭ NqܰN899X[9\r+W92F Gx=mrv>^?.bC6[-cѪ?3Cu5 = &| -x6#w oOHHaЅ4sN=$Nooݷ՗Зh.F`8$(߲S~Xk_E>\_߿oY}nKIp:F)06u3s WKk,c;0P)p G-!F`mͺN<'`zB`RǫGaeߞ̝S(-ơd؆Uac \EaOV~-N#4m[aX{U v5"(9^Z4.[_tm]ʩb6 gp0Yt,C i C˖w]{*zC.^.wʈӘDI<0:!?Эtfy!AŸ\w=M3A4 oY< TڜWF W|}asZˀ-~g|˼)tHFpHPFxvc^:;‹Sis^F&yi8µ4[pr?m[,hlWϚJkb`84TvtHo]Ϟ{ 39l.?N=# 0 ct85ӬD41x AtO@zr*M]֪[O?qG QX+5ǜD`RǬCH4?T׌! ߠUGnV񋚁}Ǟ_c]-]c*ߥOh겄7Q0g°=}ka{&rAhƛ/ӵ<xoAhZߒ3Xȶb#C+p,İAӌУ>Zu8/EO"@ ?@!0Yzn~K?mdt }NƜ ~ӊG8]5P >eW|]tBߛKy/R Fr4 *D=՘ 06=%  _7_h?aw[D?:"07r)Vʣqk JMF̜0. g`v@[Mr91 aCۃ4yuϣOXq5khZс^C^=,=y?5"-/*GWWqe۸/},QqlaP:hh~W)+uTA.*SD<軠\x+Uܘ^+v9O96gx]0Imv4?aq0\d?G['l;Q¤ɀ c #6d ?'ҽUvi/nW" @w˾AڬPQn~B7Q0.Y}մ:Oa %) ZQP\TϘV@:i8&ogR{zcOlrV,^p'X-\0 a5GcM|Dl)!Lma|rʝ5m u ~$G~Ǧf? =I0\n~_$DbKI")B(0|2͗MPC%诡1bSt^~b#"Ƴlк @s3 nDoİT:nY[iF,1{;{o q# 3%a"n$) p)-PŎAk^kֶ|7Ӑo.-xY41羧9]>s;IO!<_jJdei[xc^ԛN|0Uk7*_T@QᅐǞtg5_I=,4;j-,~~G\a/׎VI٦{2G'5#]w}lwtp_gӯ &{k߱vߋzt8t\J: oUU{3%+#TFѫOZnEFM p iA!a lk'yKJfQ[ZEػp.5^I߸+ৡۆ-?=GV04_v@* R:%!nR:J2ŀ£`xjlyϗzffF`bom4n@,mk+;{mE~jmCiwL'@7jf_ n/MfJ *HeɆk=ʵk U]7W[5:c˗טQlxxp2ٷ˷iA0i=pՠw]=QIB@d>EwE2d iAf[a΁ܽ D BC]@g)6Q6_/Ѿm6RbK'7z_C+Mh>CqIm)cUf8%+(">UEu1#ňRxRX*aaoo¥sI]z>]\`U#y$:7j)бһn wKڒ+6>E} g+N@wbw`?J3j( [)B`e)H$_2kPNW@dH")Sg譈dA Y4) M qL52㮋/kX5^_h[m=.뇮4V=uwZA7A!GY+M f̈$c$ JP2Pc2%+xRDBR2")(0#2#F[&^zϑ2yL4,8o+,{ Zʇ%ӄqIl L7=PWSQj[3~BuUKsn'?'N0b-LID Wa dAA ˆgˆ5 ) tcn|i 0h|7ŚI=y r;nfjtj`=tӟ[۵-m" [m0L ň:(H#h*ݥpbщAuW9䀘$q2n#ojX`2d#\4Aڕ𒔄 tV)|L12d^*)" ! \L1GF)u Ez˗a SfK0m Q1`3 mg" &-=ʹ~Kv7gg7m* [Dl•iXL1 Dg/x(~0axEBh+8%bU_pگٱcV>b^8cxhko:s{nf?gs>}E}Gxpl)+3@z,k#/$*tCHNˆrIqhR$Z)EVA[-yMBZubjf1塗rL'瞂"I#m \5߶ѺnZqvw릁C5hRT37}돡?!"$bvh)"BL^I^%B"+\aC9E< S QxL YE< SLnVD+\rQ?Ntϧs&6lqk 㢟%]FfU5 hBI^ֻ8c>nSTFpU5O2$D|Ό#_E< S)Y) BHBW7TKR2҄JL,|B 1QWg.) xv:]2n^42p &*USXD`u*VRL1ө%5C HNˆ5  IĺӨ ]:lB,T= !X3sV-B;l_{z3ak[ Jwpfr" &)2Q3K9BuQBGJ TTQTy2K6䥼$%!%\UM*by̥"%CKH)ip jGtk_MK3g pm3Xq[dh1>\{mކن}KIG_0{d(p >=Ͼi+ǕuJ\Tl $KA_@|%J$/ $SC*UF")$ ~+^bdꬔe8(߸g(Z~,f o3Qh= sC,cqPZg#]]SzvFBTZUҊ9 +'W %]"ɔn@^Pt^URacFu0H?I!𒔄LN3,k8Th@ܪ=FA')jmӆ5(n+w=kL]9*+)%0C9v)<($KQ / S E>&SK /yn䟄JxIJBFZ]%dS)ԣTr64 䧿 t܊gL^QrtpGi}3?Wc (T*w>cXtAtKYlzb*كnF0$%)Sx@* E.W(dF2$AU4}֤h)S}'.l:Wxhk`g{67 wNΛ=ylW7 C ZyS 6P/*+<ۦrL$/R^ CAj _E< ST:IqR ID\ZVHe5 Q ɐ^n4r :nLsk;+eM5K; h?aR5R6KaHR"-•d)i)")Y饄 BHBW7TKR2*!O1_D&w ӵC@7|KE-5$5fqN;kۇ2p> /n珳3 LOS!&$]5Hy) IJBD&\Ix(~bd"0]H Wd>EN#Suk:&v4}[0r-#jq_oKuaoc馿aA{pxEJ (_aX2P2HIYOPH2%!<+ӰV$d.Miev3:741oX_767tKl3tu"z!d‚7lh L1DE z*bd$):1fץ8c<;5D V:e6[* |h-tK*TCՐ3O60`)eXOҳ°)*ș TW@{-aQ.?o擸8f7L^|ݦ\+ kihm[} "nn"F.S<@,W@HBWf# S2ŀFK#<԰A鴉\h+R^ ВwN=p$5y%Z6(hZP҈'n9{V$0L +Z(/7h͙MslE%`LE~R(LPu2?MH1DE.mK|w>~{|q4`JB#iCf iM;rAV#qHHY/dH pLI'h];H߽C33Ѫ1Mk`-MWhjg mZB[хQAcFsJ-?Z0B Yt+ 7{507 PlJdL  bedx*"HHr*t'欽6k2HnTvSD~0pL12u Fv,Ժ"pK5Q8[[i۬ojR!#w[rsږFf@U 2вW4vBB!K1%|NKQs(Og?xG͚t'FJ ԝJ=!O !^'S'yBV#S'b`Vz| )kCCx5em%- "[tҥlD?N+i~Ưv5dV K0.ȘP*`GJEq+][-ws? u=Pmk J+SbYʶX:8[~g:ŋK>J:b: 4- Rqd(ȕR YE< SH @o%z{/Zg ﲫ,v} @1kb/SnNR!#u\Xڴ,3DTw {uCâ/cvLEJq/CB$7L/is-3`)FX2$ MDP |XJxIJ?-lG:OF|0P\ݢ6l =YOS;2P<CoڋOF%2h/^Ɇ/z02zdر}o ޫadſ;hmmvdDr<,oZF7)0A.ۇl~̖[g':YX]J8 R!=뼣GMpƺeY{X T01!b UNfYJYGKw kVl <`U$5#@h^7NvVzdN񁁧!NZ{LijӛqFVN=vwCc_"AT o'M|H]o3<6BiowU\AS3"BX"DwF(Px'\! pS_q?#O?3SZ)L;xVFWBHNS>)Ԅ"bV Ny!2+xqhլOX tQ'qǩ¨7ޅx2.W[UE{m'0{v=$yM_n@.R!^[vg:# u :@J*0 rI:xqLf2 )Nw-0 23+`d+0g 4,txUA;1\sF¯,f͚e=:_3Z nv 1EC򝎶:ɰ| w4R Nez2?ō0 ƻ9m5OIp-_loUr^#Cw?׏a~_Cuɢ$?/dǾ1IYb<_mq!X6bRu &! L1 X ]zo1#QW<^ۮܿ?\ղgTba =SA$5Z@IDATc"H]J2D"%“Tvҡ!3ꥑʟ6E@O!iڞnJ@* V //FXj ~t];+ Ax+I~OzJBH; Q dPsf~r&"Nyw2`j+0|Osd-L[蹋pZEOeB{X,(N鐄.w5-wmw#YvQrȢR 2eCf8'3t 2?>Js.5b0C2@8i~ltf tRjʘN"pOn3~x~{^kE# L%In6֨CnL~4y], ?#kk^ e#GK>*ˑG):uyn9o3N[`:?p= qam ak@<2OWT)`ɀ0[ ǿYXw mP*"V,NgFn3(8t 5QB! xbML1}7>2q#栕ZA!;Q@C pwo+3+L!I>! J$b[eH?.VʔW<8{O_ܥVڝDfMK *Se_P6g `#s6vZ&>D}f_Z[[JU+*$%K$YJRad:\3ל+hE䣏v8fs#N n|ʴ")T(w?nI"`㰝u[-:Uo겶(xR0锄Tf-:I2RV/tٓx/`==} AϚ+ L&/nƣEZ~ˏ4MWye~=s$@)L12IJ)%|Kqrp]>^eB 0i֫8Q_-#K SpD UP1oTJPƴB9Bi71p>?sE8Z!USqRbnG2lz1Bը'rܻX%Ub PPUoA6%^@`l۴|9Z+Kuk'Ebǿ 4W4+0YPeAjЙ@ӽ oFpN4wJȯ2!TxÃгbo/BUxGe-sb2lYVrb)ɐ|L2|֌C긻- 2t/ zM EM1 @gӓI@0&q/;|VW?ciq6~to?@@͠bvЄF\}T\{)<$K&Dls܇V+oL&FWuZ C"Π 1!O Șz٭ jOEgY&'n/+39R$"x'$>io"$#PPXMim.Rdb*%Wٽ9E!(q98,SA`ɮSȝ9HD A@*lQ]6 QHGG&5^6lCsZ ~aStF&ʱlrp !XwjZqt^ 0nk6ucs;TP5QgH ؑEiO_s32<=B.n=6L0$ >, / ؀,O,>lBEAC" 3娼vZF\}TlO.`[!߽|ͩgE)mZߣO_"•i*E_@u;"C:>CsRW(2֩6p(r9e8{9(P1Y{oQ{l{Χq01UŎbI)O\:*ꕋ~9ۇSbK1 `iVJ]*޳3X EC`Ȋ XCފ%H T`p1;MЪQ$ޟ>XS@ yX-M!4R /*D2 Q1{am7qRQ7P*hJE:- |!I wc(`zt(4rwD9(( ``I{^Pd ɊpRϣ;N3==݊X5TV)0qSaݸ//Ot+,#qOk"R)_Ȑ"U!b7MpoL\mu9m]`JNDËU\3s8+d?X1&G~G| ZX""tG`Kc~KKһ7H1$U, c qRY!O _>_5l:/\f=g+3\ˊXjQ2ap]NjImbnkI7VDE^$>G*1F+~plɲ M)7IHNlO[,NbP:$!uR,nBvŕԹt) dn;Ʋ4VB^U!k#s ~6 XIJqR eJ"R $e+v7UyOE$! {Ń.#00p``pOŊ}G=F@dtUzEK 1&p-C jIDuo2KlML%Z:aeR?7X]rz7qB $o8;Pf/sWL37 D:p%j)!<*83 s<_Vt8qI=LUPlJIR2hJ S#ܲjR_ O *7^EeYO&t!^5:{G ܻ;v$9~F]' ET*NDª\!#o5+8}"*x)dBɐD,.svsl sG[)~BRZTx{! .@]ES:$V c驮RgS# /t{FEbߘJ'&Rڏ9FsȴwR9PlYe]^:|2;hݬ8~)eEbҘtW_i'MB !X V;5àVUʰH0q&f1vMLTSz gb _[9s~';מGچ)*b)FEU"Ih9ı}#V3-9+TG%~HCux6P'-)Er%O4a nAiGHRM`? y+o{,@'clڽ2;Jdp /ԒoYL_7W LBpK?IH?_$F rEFK п[.>Fj(y8*URHRqXM(;U4p9[7.I)8Aa*娐dHB 払&[{cd?XRRZwqU棇(0@ES:.}{݌@= :( y'oꪦ*'yS[`L^# J)YVW7xRD"pU4,qhki\CIJ#Taq?@G: B7v=@! :(U˖ۨBJm U~ 8u[AwLm甒WW؋ x(~0bU)+15ܽR 81%Wr=U@hH?IHt=\ lBg(BC[R&:DuP(뭇#qq;Y+4Q`-]Hh+h TJ0i˥ qv0u@fDd+:65Y& QEob?\4?9#ŀ29r\PRat$e}3 cPHBf0{* O5X,Dpe-\^6U9 @¡l5uG;v(_@]# :)Jh=ʮ6-/V$'*ҩH( 6;u-D*e)(0P0MܣBkC9KZX(J22ےPiq)C юwpVCʱ#Pg᫮6}rEYcI.F@KÐ"(YOImz=p:Fr* (F@Xqц)H9%ɝ.罋3= lEOvJT3$T)j hɪ) uɇhlaߵosηbnKx"e?RT6X2hкj=P%wudӡi 2fL8R~YD%HwkpT=/? a{NO4d%!3D^iN 6٢嬠1i b=z|vv9hGG,ZmI91\TkK F 7Lt`ݸ&8<@-P C H|z4<6obC3qN K($!ʡwx !X8o4h}9qol;U>ӌ@# :)%#g|;#5h.H uame*3T`!J5zαSZak")F b@7Z]C]a7y;a$8(7) F@(W!hHQb1ؙE?]z/\"#eh P'mN+pyV ) KB"FDQ!B4V:8<($/+ S +<@ M-ae 5OAz-I# JF=,ﴵ.9`wɣn"DP5RC%2Txɣc2hNGF)kD0s\`'9(R!OR@N2$oåkRW5ysuϱ錳%FD2l+U$tRŏo/\- :w<!J'w"n,<8#J>CT=-"T6bri_+@FѶ߸]yڞ[m G&& FA4Ml!JՐ ~֜W透:[4 {qs 0!'N|҇^!GJ~Z:)Oj۰NQ놌55p3 S#VGa^LF2,V籎vմL%oim8l+=\/a].`YQĥݶ|y}KxěEuTtsR(M"S*7;p,^{6nwb((ٓ]wòMeL.I^`降lVxէ &2gSuhNj(]y1 Nh4uzGƽ |[X_(E oc? D j=/;u #'\M4^!]bUpGj?=|c["Ú_.5DM+zQ7SC2E~юvmVOxT:-1u}rhP?eYd8h7 [_+|-hh<[|ŕN4O# )' >F MST0Ղ?@ z*DW_*ႠJ=1#ڴMGeAc)ۥ;洼NK6+y_ej殴MO>ja 6a(#B$C5mA_^? ~SY)mLF8{+BnCzl~-%ű4f<%U0c\‡.8 P@y pm+3BChô<.YYƿ.4,TLE\]ۑ(&Վ}Λ},خRB.eV a2R B*ֺgt1P50I*ڬᄽy{84=%4tKѓyM F!0F 75$B'B !Oҭ斺~Ǔ463eFbG ^]48 *5Phߨlpb֌n>;`p6bi$) '‰Vc{}.<㺧vnt4T_,°/]]ֺGi苡za<՗d٦ @X%K~ E@S :*b|aV'`$G 7PCsZ\_Q8)@=il {`j.II3X'<ְ XubOg/fkkTfWS=')$|l2 IH CoA*Jw|z #$ȡ&OCgߝx/u G, D;>/5gC|p%Ėx$ZQ2};ZЁrc(r떃|ni^0)ً }ޜ(B߶?xف@S :*Fڳ!V_G 5*J%Ipe6B9E< a !b[qJu4#MWCajd$!.jȐ#+D@SЪuJ fnQIW/^&~5zv|Jtc!`[>Fqh,)I!+sUcۡL`,;5:/rɐLqFDR CuJ* t[\v^dnYQ&3SECXɼBl}W{fl ̒b$) )6tdRc #~nqnx+dO~DFŒI?IHϨaAXHү'@"mi0%sC#3"u_Nz8SN34ALzgY1Na"E 6U CA+7|Ie NX̃LQjve {ѯd(I Lux$jbP]/b'@E̱owީärz3U̻rr-B8t e+ ,_Ww "zȥhB8 ޛweW:$_Wq' ;TTo25dFP*qNȀT븍o؃yoHZ6$/Kך&غuR r_Шy(-`ZOrk/m|ͣ>zE.]IIR ҢQ\]"ש-]YA =iOWs=vt:LwG2 lL_ L:Rs9 W,魕H~}K|Z(G7Yh" gKzb=3m?&O^g#:k_gsA@NY@ɓ)Ŧ 0I (X uׯ EthV:/[bFAe]}&KAmٿCjxz&dkM)s2 G[qT[\]5/ ?s&sfNy"_)pa.ջfG]OR@r袱1pȿb[?".@ɾ' 2YˆF0E8+#' Q_x6֊wR@P۽Oaޜ6taC!,-:,7זt4=R_y+{.ӜcŌ"Ρ 9!^=/ &ż+fЫVXzM,dwhe|^FA]T)= 0f[[xIZ\d'9f.0y,9Y+2 #mG#?ktSdL-&rw9q[ǿi1 SGLD@voެM_`P9yijJ 90e$L*&y'3FD'MەLV:R 33i5=Θ; #i$-ȑ4 ట$' I`S}@YG ڪϪ)Ғo#RզF9Ԣc^4yF0W? .wj $%`x\vܲ#cNqdŌ 9 WI/<ë%@ ة^K;lW'*69Y2 #&d څI^b2 $&`|\z=~;/N0I8ߴQ/SB1h%oop.ޙF3>(#G旞&/#&3ڋF.W34rY_h,Oqy즾U>J'#!6d͘; #A~̿4&9\~qԂ"6HaIGdk<;iM @6K Gf Ώ39]pDݢQP@tI ;D`>M &`r\}Ló+9(eYi1HRi!hn~v:gzf*azͰ>zt&*7;8.ozC @ iC 9H\'u88(ɔNNoK?蓷u*MvA7ɈF0jlNf>OW:(c{vuYgl^V\A^GXm^ 漲o(_3Ov nm<~;@ "+@ X3)tn:+ө.zK=,N'a6_Zx1]xh%C435GԆFa[A Ls-o_Vu.A I @`aڥŴU.M^RŌ"Y*ݓK=ԷИA֤"O"Og2rSĿEg0B HX-W^ F}N;Aӂ!]Ѫ+&gqzg :koKu,yJ+N(U7|o)@ @HhC ̶ԞtnC:9]0" >Fvbh_ΥTK VnYwt鲣:ټJfJAm5gᗢb!  T 0|xPE\1OLe՝cz^M# G׋Hd9.nFŹŽ (h/@,h@`\ 4~A/޲/ױ7i1 <p?ݞNl'Mj}c1\)`U27[ K@.(A`4m+=>KC3O:N0cŌB9K쉀 ~s+Nܕ*q/c#!*c+s?!7@r %6/lt*IXKQ ׿>R>x_trz#re;Z(R*Р TyomÐ@@Ќ 0Mj@!3H)]h%{vo;uO 7֠ 9tĨqMį2?:UJ{פ;wͬt5r Dm;]pD](`n?U [,ɰQ{UϱA #[CǞ$owvNp4ʛjHb ^3X}q#(pSURkLlU*7s-ouOyG>4 @`LA`, ~9Ukܣ]'TAJ$tR%uHG?}W:KLTZ h R߮uTn9Z(Ͷ @+ hEm&@{?D[GG9ӴK:GEGa쒂)߾79/|o@d|BƆ%a }U RZ@{tv iR.I ?9B yl?1! !eɕI;%SQWI)롃uE㤯d-::z ";\dBZupm~v['$v@BVDXeq9j-: S0#rM1h4]B< Qgz`ԕ}&}&e8yZѱ/E/{G: 8ӤNej`kQLʈ+PwWW[@ hY -& t>@S7;*ߙ;5x0; |]|Nv8o {NXrF4jBS͈^=  ZZ@]h^G %΋t л>T.JK=vf}^&3"2܊8օu_;,@H Vڊ@Y#ݏ95;򴿻qY?9fI{ (ӵeubF'~3EʔYXE RVh8\jA*]ʐy􂟖5~'.*N׳:VFckD#M%qݧ~z5@h-*@`4ʰOL:Wj]-P߃{sGwNvuF4jV~X^~˹d@~hZFJv"`ҹt䚃/ 9Ѡw^e:Ǣ-Τ.JTLE!Ű cHFS k^9s>/c"k64J>ǻ0F9*IN!+ɝq Lh@M ovAFE`饋4loTRL{7]qX۰E^9iД32u&u;YZ(i7m6WnE #7ӘcŌI'NC!_ֹU@;Ǝ5ZR2v.Ǯ4Zݖ&k-bL(*GF]1e/,=c-fJ`8/mtiRE4)PuNEF4-T9z8j-: A/^e($;̣ctN[LCOǦwCfAbA8hUJ%UJ(ӢUpP\v9G 0t4 C@Ngi=ЭY-D6*k6TNOU&sUj=uOc`M@Ƈ;Z"\OL:fRy;qÅ8UhQSrsۨ|9b,KqDƋ"vA`sx ڷ?:H=IDATaX1%i;P\Ō$[q: h0KK`|;AO:ϫcl-<-f2t>~s޻>k @ (]@@`~W obθ6:Umw&0(hz7ȱ  F@n#!@r=w6yDFpDeY +AP "'!66 Q]rщ= tFK&WZ @쥶Tr?wAp:ۧ.VRo~dR E#hC@%pWğG-wn\7x՛ܶ.,QŤB>~7~@(&ż/ f^N:B黱ƈFPU*wޤ[A@2v]xdih=t8QW&.J*PJGg@ g O7$DS@P[uGeJxhtdpwL9R(nR&ˌ8Y{gS(((A@`t"<Ѽ^:8^ 7!  ;FA N@o#%y?Ju[!)I}hդJ5@ʀ!@߸;'i_a 0~{ -'pɗMxʀlINEF-t^: 4 E&w}O}/o\Aē?qf~,2M!tbsr'$0o%U連y7>f_nK n{1!;@P[4$Pw*L0TYrЫPy_]u8 @( %Q& J <}3;QrIDd):Ap}eV@D@#Z u"foǏJQft҈FPUz[R#  P(ɍB7A`P\,N^?R'y]:mULqˣ?G]AH ޗ gU{xڧәD#ZS{QRgh` o*(6 М?JOiV^dJT,6ҹ #| =C3== B;osZ(T4ǀmTcH4tbaywlmvs|wl#~Y:7SQD"Y? (!%i2HٜY,WbTJyǿ- @ (]CAұSGGV*TevQO[?.Ar ?MDr};)S-^|? bS%@P[ :>3 8o.K^|LqAC@yz z䘝?I'LrU2SEȽKx7+ҵLnQΞ7@" %!%R 0 2;2Ə; Gt:gʍͻsU+!ܫR6̓  PsS`܏׎;TȓGt+yN+VA)HK>qM4jp8#4@9 (}B/A{mǞf3 ϓCϿǮMT/_At"`E<9 @P›.ONU!㝢+m]%%SJK6WI}F Gxo?⃴1 &зM ^ z?agQ29Z(2ŔqǭK 8vF4QP) ,#iPH@ n n8^6sH/pF42VM>+ӮQ@Oy`.ӶH#px?COC_a+4 | \-e%[Uz8wUoL#'(@ \L ,(@=aZr}v% :7p]H6Y640A7 ]!M敾 (ed @k}/׍ P2Jv݉Mdz'M_͵ӎ..VQ߿s9{5tT1s+r|@JF@n;q GFCDaZ3iu]鲇}?ldjD iH@  (MB'Q:fo޻6sY+yb˜qp[i;֨I#Mu )( %Q&/Eoўև7} @76 (.3 UJ|zw?$N LUFP*6r8B@P;~NhAZk|uVjZXY}oJ>F+ߩ)mFi'* 8c@ ( B&>y {{ȧu.WYH2?L%\4jOvyʀɧcBLϱ @{@(=Jx~?e+U l]E/5~d95]NXB@4VTi%4~+4B*6~62)eQ{f7:t(2J<1U^X@(%I% Wjl)]$EYJBT\T;Wdd:%{S .IA Nh ~н M^<==Gn6mmBI+Ɍdhwq ;Jȱz77YK2eQzNV#.> PJpʼnI`߅Ci/3muRۯ:L9P?_tSB,hZd*t3u{YӖ$I)(<--B'*/8cg;׈FrT:2DN-k:V~WeZ5!6LɄ @k@asO..RY^(y=jJV'=N{+?pW;戀7ڛ .|#h z((1$0~#>^%ȣj3MVY %/-z+Mm9Nڪ֢2 }$"tp8v?syN#jݬ<-+_sɩKӶs]sJgkQ, pN̠֓9xO)@P;M/lzmN4qQ ]\Xc ŷ(1ص{~=tU+Z͂j*F$!'a9|_kck)'LĴjݣ{{6p٣ϲe>Ǝ{+6<m;Z jw/@9 (}B/KN`=Ov $]ˍt ǂEP˝ [Ɛf2M xvDza{mɶڙ:9 봭@(eKc TBΫg7K(ϚX)sK]D(nջrgNJW⚪=Zv]+!b[a0gu*rk)>pAO@zXr 43:<)yd% 4F Yoo5:ԟx5صw`oy*qT["HvRM!vLsA N@oW~8;5w֓*~ bm|/Xẋ}A76 H:`ͺM77|M{ݶ3(H"9:R8^$"',3}Ox+:0hQ~5ǹJOru\Ҿ>ojzR7mB- Wc~,qV6Pl%*~A$_%/ " jzN#8mdS}jٲIM`:=`` `vguhۥTBD"t-Ӷ]0+<)ҿdelZ}~_8?Y&^MiݞԤtK@߾FQ-lQfCp BtH}2:3Sy돞Wn %C|ڱo =S\}}4ЍKfֲC^O[ r%R&tl?㋤'>Jw]X 2*LMr/{]WUZ'7U "YaHAU3vϳ+RM$ i(0-AK/?r"o+e#!zKtvɅOv\B6U>'?θjm@eѿ>EAA[C~3mB@ H@o T~)jQZ*-WkuF2BOE4w˱ڳZB{9ܒM[*)D$+T PT[;~'_dsiǒ&$㱠Q9dz#dmQ֑LZ=:avfӺXOK-($-T ~"6ߍM+8fs4.R\+<:` ;`{oߣaGm0@4V ?+ϐv:#A+tiLa 4s򬉕da&᎕l*KX LP;~ ЁB`{8ӻ9\ "u^nͭER# .6˴1 #UٔdakঁW-^}3oudt#OʤWFHms/_]wHIM>4W2"LTDyt|Gq ΅%@B@|hx# 鼃|s% WQmk%GCP\|o饋#-K;M'+lO1eۙlfKyon[,@ `w}+ }pQa>U?%+iבgh?\poOYal E[{tJBb Kh=aj޿'^x?]̔@֠ce"+^+NG/jˍtBn+unMÕ7vߦU>۪W:k'34?v'Y_U*_E5UI+ti;ށ':,]WI96y8/~C"hdt6q΁>.Z\3 =,/nTp@(.uȀ- @se X_68Vyy~U]v)[+J5Q4 68MFr_ٿ=vz:_~4&6 jIɁ PDxPĻ>T$#z[']"}6' vaUW9n4?ڞ6!4Ldg^o~qPRywBn3W BH@ `w+$'^NkN32S:U"5GOWFLݣSl-h#۪䐿o6ӡ9ODfxЌ PDxWЧxv>Mvq[L*Φ_ѿ?.Ggr2G':Z V3{hC~l3(D` ^Π_&@#r⹓ɔ3+ܿ*3r/rlBf|tu47PwSm~u !  Pd|wзBv&.ӿ;4&0{V7ĺ7W栗=]UI֮hyΪR] _3RH^W& xiӫ*'KKi>qF}}js~5ӟ% S;Tk7$C}?5ȓA &uH"p:Nڕ#dMZOe9 GW |nsjlzj Tkzs vѱk^@ PJpx[r\G;ǤtRF4"GGs[V+^Eh@GisR%-ta@)B-!|cE&SYÀζ3͌:WkuzsAի<ɶnsMaF  P|ÂPz=䠇yAf,PڄN}a!Qu({C_ߖ X?ͧsRikH+6+UI@P{g#ix~N/ܡVҥ /P!Fod$W+] #p*$^/dh|]Έ w q\LNYEjk8)De}Q4Pi@P;^Oߓ&jn/#ɔ{4 ^ RB۫]Uyۚlll D$5  Pr'ЏB]_IN'Fn1;&c[:+wR;cem@\}ZC-:-p C(Fq ]CƁֹ]J*Uj#~#kQfwԆiJC'uϘG>h\~ϏH|`kZVצZ_: g~H)#FtĦ]O@z8 WG>)?#oίbd ܆ uK%kb%8:6-Y2 to| )O69gS. sMnTE1rɶo%);UMoS[)H -&@Q"tzͷZX҉^wϒ3_imó;8n5*iu>E8Oٓ' (e[;=JNr(Cu?TOQV6d1z+-/t\ԩ5(ϚX)S"hɩmH &݄Qi =;S=5M0ڠ.Vf jJJq%}e-*\R  TQ ms͛ vr? w]OkYnŽtQK wAptm>] E"Hw})?㐎 EЖk|c5bp&.NMʈLS%tU}h@P[%!%nʗ5ލɌ֥|-5;pUs7*#@Q5ђ I ץK  Pl}лq"yM65A2;vƫSl"}ZH-R E'w8jx6B-XpE?miKt.7)#Aw:S@C1Ќ PP zcЭ# gQOrm\h$y2~ 2G!<%tm|q3__. _xv]A 3f^~"l(`mAEjy]F, H P Jqɱ$m|TGSn]6R#·<aj"EJ3(VAm=4"A{cOHQA{;5<\嵭%Hml:q#)PΈF ?qNO`!H"H@ 48%3M{z/6]Ӊw<ڹ an/Hɔ.urEIO` #d(Witboy}cs =0'f%mpF~Oje7u$c&yd`ln5dAC!D@P46u'zj~MPdjKhIi ؃.<KR@PۄNAlܮt4DO J"'\52YyLnhMG@֠ccA;t54rўӆD]_2I줌hY%S ps)aI  P"Jt9ۉfwǎ-9 iVi3.褚<밍2Ѥ0vJ0)ǒtZ:+# %#d7 m1/ܞnqqϣXCl;zm׺80ǐr#gnmr(Rk)1! %"D7 ]mo߆ZTIS=MQevv\oD#dluqIKzE ۶!.R  TQgixgD4d;`,Xe7Ѱ»dڑ8)#APIKƿU-Y:3E@1f+=P[t YW9MvN&S}Q&yv4]2 }v_n9(AJC@in:jbk{[{k+ '?އ?vrsq1N &+kt)+V 3E93)ע~fam J[ FGT< Є@t@&~jF@{i=y57>@6-L?:Q0XLKN.o V.Odb1ɉdr@P^pAgڛ"~G @`"|.FM 1SF\y R g_ⲳ^.OS@0\RE}x ?ږG$A&F2FG`]vzF 8:&b)ϧ$7D7^rVIov3Q؋UaS /JH vGq= ^yGP|ٕgj!L4& H_jB'à_pqo7+MV,NmzӦ6R|O!ʳ*KO@0:n(5~W z=W~a 9r@r ȁ#P^y3OQ1s|A`@098K/~@狐NÁVPNG  IHDR\rfsRGB=IDATx] Eկ꙽wsFAA@B4QF@ W8$"p)K !!53]߿׿WU^WՌF!@!@!@!@!@!@!@!@!@!@!@!@!@/oH;B ^!ZBaߎiZV&KY8MlH\/g|,g-c`0Ư,Klg ձFX˷a#Bi"ޯkhJe)@P(*^q_V+_xS j{4Q[?t\L| F?ٓ9|u=5 7V θv\{OՈ. Gd^^s3{DO!:~Y=? MAbvbMc=9XDNO{̷ [`vSѰD~EPƀ!*ɤE98[ BT!Пy{f5 ~-p0W3.n`_?0֖#0]/=&DB<iL6"|uU?8 r 6~1sϔҨ?m@8O>{5z+3ozb{c09g,˒O&4'!78@EXB`X_{鶑7thO?3_;߅;yEI$N_  &@i8ざCp/Cf3]3xFQ*Y P7oҙ]%W`oSxDU,9"rE` c$km3sKPnAE(6Tv$ΆNV/Mk;3x2U%.*B9VDiI$hCFw}y}`^@OT'lHV? &cRW:OV? F_V~gz?-p6L@"JB[gRޗ[ֵפN0N *q*_lXz[&f]ۛ๷7B0^oK@)ѧ\xAl~zxP{*(_<ȍ@E6)-X A@!Q!p=+vlGqPkۺCћq7|˛@yi!2eJU/kډ\|Hpf>>;;(g3lՑ4=+;eS{Jc8; Imcݍ, uMђ!>["5lTlݫrN\4kD%tz"`>)"o,b8ěc) ,uX\4wc`vHC]F!ia6m qVɆnV<1] 2VAdoS j}B9l"QS{ʒF|񴻇ɟZUٌ62)Os.WjVHɠl97!-ȗqR0gG_H'"q\[PΖUֲ kVˁuQ.zx.*/unRiwJfe,jFBP*Bkhiؘ'orCn-j,.7 Ҷ( }R,Y 䄳QJCV}]>ҐzA-_er n3 ;oo+D+=;Y!xŷKV},ܦJ8qfXm=$e1S$vs\؛nv q4W{lxYBν(dƮ 9b@L_2ͅ33HfA:fnxϧa6 ]d_O:!KWJݕPWG).4rRMYo0 JN 'zVZ;PeU\2TU}s`XJfbJ. '%)2λ~~ xGAORp| A,פ_+:|8ne7B e'zfdY,;V=tx;$+-}4YZv #>w*~ͻzNx*y9@,Ju<@ >ҵ'aJ?ޏDSph&TXa—F0|vk~黐rrK|uM0fX:I3z(9)Y/x$oϿq,;\9kd\e>2}t}"O>56?K̓cEX-\f-i&l/oڐ鶲PD ^dmT6U}>u˥5Ϝ9.SL4`׳BgyqO2g23o9p}Wҙ c4 SnU{m[YL,I=׺̈ƶڋ1V ݜÅg5|#kOZC]7]x52uRKm-PxFȿ?U@  |xc| $h^0p]`N܎o8ֺ*h'laq˹%Θ}',WM%S]M`Oz0G:D}=I -ػ,o| G|JƦ)P P? icX "@pR}>zmU>wڼ8u/ryx8η OzYyP>|ڟ"|ATg9h:j,פ n||/~_`aA[]jcӽlu΍ /0_;''2|@SWx *< !ܓ@}# uFk֥~`.Q_v.o#Yꚁ&c\mF%B :?nPpBl' Kpآ. 0S#Ub!v}t8sNquE;PYCy3D΢ /r/DZZ*w5(7cBbb] TOu5HUu#uk+ؗRW}*Jԓa\ہwcEy(, p_/:`u7Qvݨ=pN}EVBe_c 3K1,[K}c$gb{d@W̪o@aOZSl\ Dm3ln!s1Lti%1dTMBgKM{@-ۥ@U3 U vxMUԪ4'N6|xT4%gw/b@;Yyt5[;:ӬF0:'t( ,D*,&V6DE X`kJrY}eAz\35cZ6lcMn=JRWl[@@pHc{+?Z.aHAI}j3wߚME1(}(&@, &)N:ؠjVO?53h 2vd @(ps (W{\SlU{2ꪃ2N$zUXeΏI@ %I/a'֪vք)[RwTe+C֖ͰLq:u8oo\(ee {DLP8-6cZT!BrbyXbQCC|Vfv2~w|%%Ϡױqnq|]g-:ϛsÄ5_=Ö>Qwݿ&\RY0?~ag|-kyH#s}eաa"YZ qB@# оn;[lՖXɈ'0g\3 b)k|[vLYE: H}vL~r Ikkb`GPݝՏKJq < |6մbyeXy{sm|k!,Q c&͖uգq|t(wa!/>9ʩ|(uEE!o4V_^6bdSLYqK{ٿB@<4&mJ KuleLc.6/Gt& ˇ$UFX~ dj+sc"c2T*߮p ǁX/b 1,gSp$@t/!^Vhφm Ƌ۝' ׈r@|`z!R;qp}1llN|(no<𼇶0Dw^˔Ͱ/ZȪ yD 8 {I=5u?xh q\5Æ6]q'oF_Ȏv|5,B˔.XUYg_| !b^:^Ҵwθ`۬{l uHG94ҹ;΅L ADeF|J ӣJ#,im:F 6)',q2UP~J .0;H]u5?26W԰m)F|8 $h̩ZY |+Pܛǵ`A4؟b. ՙqV  |lwfha/:X9W%~?KLgU/ .0;E8qMy47\g@rPnN)S>_8{ #%C@ɠᅡoCRmc֗ x0';쨄]hX .g")r*3m%G@ɛ^r9k8Z2$PEO|g6TAgyq:C0vclDrKs筵akyv^˩eo`+ R" XS{I`((X&B* ?D *x^_|P*=Am/x¦1 JڻNh ^3)0Glc-O7Æ#A}Kv+9h>kBQWئ!p"md1Aa.ũʇ93gnX4ʖ$PMb2ʏ9)(cFDP?)dytإU 8uд)'?4 r}/˚+[|=Fh?>YcVÎ0B6@B@yGߴKk 2JҰpl9*0kcAt'8zBB@yGh/uQfPt\b#p[e{'~ȶbD #BuKe{WO|'ʳ] jԣ[aze$c#q&H\o)Iua)S@`1߿=L$ Pn-G}8e #5rv~6 =?maqR=/f}"@\[P\M6Lcp.v#@|[1M'N`JL{ :ʮI&h 08XUN,ہ2Wtu,69&\U_[%e92luY5I'2E"-%íakl]abYR^fѱ P퓷vn?#tqŞ.Mqa&'7t䱫DOrG@P 1!n]cӒKwޠ$-ҝgP(Bܽu7niWf[ɗI ڈ-aLZ8켻ޓ-k6 PqQo|۪)wK٦kٺB,x0bóedsyc./9o5U/L0yA u u޿I⧒EoȣZGE҉hŧ*rU3[CUcڔjU_K?C̆N#SqoZV:V*ԔdpU]e`75K?rO_]^mecdeռ PymƂͷƮ/ƈen_%Y8t ky#LUa 'u " P͖ajcf=?:b [E0ƉevzS$9JnU'| _e ?Vtt);kt$+rނn*;K|g,rOYV!7Nt٬t|T~r pYIFj]۸6r%( TsQd,jA 1չwؾ^eTm/iw #A(_oHW`9 l|T?zQ5lc8ɀp?tAǧJTl;#o!l';-\/H̔oO&*YG!c_yŷ{K8qN?Sh;ĮIWsȵ*qc.`=s+Ī9Wf"L u_cWRXO˳K8 @ :_ Y ⇕LQ1dƧo?y#mzj0yhGTpʗ~`ҏ݌V,п\rL={W"| &QXpcy$P:LرpUe=|4M2x;yU!wD"c9mN |P}|Iǔd~7$O&!2o)ko/hG!9$Ld1j XiU$!Ǘo=ͮT=߯φTua&y]I@4Z량IaGr c1ܻ*[1{ b Y2F z@A9 iĆ~<$b7PƮ{f%1j* ٓw>\l!BE*nGrҢWJDqhQB'9}j9eA#ݼDrЖ=/BE0PmzazNsz 8Yr Y$T*Ʉ') !b$tk6|DK}IW6ѹ]XjI7l}3c~g0kOZ.篖+j״'@rx2U_N$#EװN8}$ГHPyå3S`UΕN4 &K:>;\oUH?7j6x-ҽx>>B[,NX֮+`'jHtU!HR:! zA&$soҏ?ĕJTۡ( qv}SpꐅV .yfWn;kaL.$70wHC[ҎB}+AGbXbw@7)~xoho\=}6pjlV}`Q^"@Luw@6H'$rǙˆz #aWUkmOF#)dJ92m5/}W󤽆 wK<yP#o:QQ!5-(fmnrBD5dUdFCGu3~2A#SjOa/"I:Iv<@<>?pz$aW= F@6>!Z]Z;[mAbdhyʬ_z+a2֩~bMuM=dDEE1ڷNrdH:^Ec| Pfmj['rv{RV57ٺКAa 93"@Upo\N*@[?LܝS0M۸b8%X,h1;(~oA#rSI{غӹXǷ-hw@8Y p(Oa\7X'gΜ9.u<=e]Z:]9\Q@~`>90g[k:pWQK NhreҚ;m:2 'Kk\9{ |/hN{ Q67.8"@LZ&|U2,?~.(j3.Sy#m^U S&?ʡ`ɯE!_D:ęq8D(q+^88>г$GVņeף~@5xa %PkY]\U=92 ]'`c3arY:r%nSO+_꫷?çaru|?ε<̈́/D!9."uWz "szP"@e>QS! S$7«6omYh5Wr)x*ro Q!2#rɗɰkTư5^y<'G^rчx rozѡZ`a(6.xO=xRJخ>*BT}[.)L{(;g=|Yor%lښ_ cj喃I]'\u5h(GsPVY1(aSd`W);#VcN.ܾqef;͕It<J@{{ߝ"#Eg:aʣ0赜a{_O!?A#@-6?_8Ir䊾+>\bA v9Kח+_ JfE rE9{!^":{Jc_,mJr=LnE U0Ek`KlzR  =3E?$n-fTVi PZeϖ^H_C 8.nxeK(o^ gnls!WUt^5$9γ3m vsq?pPEF@V$~liE{"EŔ9eWgWDxb6W}-T @=555?C٧i8v9O`ʤO=%L$: RC]-RyTL"@ig-*y U}v3]\| ןA̱)| L{% fђcb1ʸqw9ףx*^س0}1IWoNa"(W P7e u哂\qX5>>}=㯀.Jz_sEwmwĻ!|*,25;9ݗ(/!@!@!@!@!@!@!@!@!@!@!@@Y"˸P?ЪIENDB`ic13IPNG  IHDR\rfsRGB@IDATx} Ew13{d !  1#,7&@QBA@!" 0AEM9"W@ I飾߫ٝtwwu^{uzŘM-)GSk8%x6JP JR_PYmiwƴ#uC߶kg5 iD0+[Q 8u㮳nT03&˵C4H&(0LS;G|`eم+gO!WnMoV:ipD <11urO;ᄂ}+ @˭!X6sµU>5g|VI&GZ{:8>ǸIff<€R`C-;xX9QәL @jLgh.4\Xao2A|l>ؼjHrW#]]KN=u#O zCuثT*Lz=n6;`F,gCT#oXʶ}1iK[q-ۻ~6jm1;۶hj돪 Jڔg)pm;Ρ%zf L3ã)ȲM}fu˯b2RLWS{}<JM(mffatZڗ}iNWkݣTCՈ§8owʙG|vW֏ ccw[hie7/A+&vu6;l-וL4rDrW4CJ`\)Q:HU@*htNljoْ;ύt>Y պ8."@*A{ >9町DžXuKKX*kʏ:P :u0ruэ]:dv40^%!Xde?qt@9Ad(EaULq,6黒;3~JġXʽ# t́mr;* Q>2ꍡWX z9b˭Aw}HS3?nji1xCJBb6vQc#NH@*q}*q\u]7>_$亚J]PJB_XD%"V! uSF*ul1􎎶;h]JBޚCL!Xy+Z݋q^GA,0)kC?l$Z\I |8i :(0UI`3羶{}͉0֖n) h3!O5S 5*Iw,c>!%!ljljnY3bȾV;yYs^ JDNFE(T0ptŋI(8Oci֎ܐ%!\K4c+zQX(0^Jf y07:cq֦jշi{{w3r=q/g\~2vGA 2'finn[ٟ f}-S't26Pܢ@IF IwSװD7Tb[% iЇ}w?uTզ 9˼4ȡ'c64Q6  Sԛ:Z|i\gXfa `~)ΙɹK_>fEfRc,oZh1и8egL`#4N!AFâY{^;#QEE|S:|O8|w4a٦h5Ys|q%Xx1Վ%pX\mfhY|%|z>^k{y˘V{:SgL jArwAcZ{q < v89C0Nq5 w0(hH\O:> )[vd!'t(|O[A Ml4ѩY)/%$I8H!U7񏯘 Yp9w2fϞ<]T,?yh[l[\s8NKT qadUP5"-}x!ş:q߼Q4qpWhfƏu6-`Ԃ$$cM2)}}kG&8}T}'Mg2INMC_3fBGG K=]E}K~@ޤYlϿN~'ZGqϋfm9h@02*I:>3Kb_]t Ks7݄dNxg/tD3rǧGF8jm?:|Cojw`c }]r-cuG(2Zt 1LsdA# tI=fwA9I> J dV4M/A/Yp`e\ξ+&DAq4kaL| }lg:ᾱ͠>O~d%~X|Ik]'w PSҕP޻_VDޥa.4 . %ūFxwXM|4őoFzZ:z᧏|5@c=R-khh֣VN2`ڱxDYh}EL3oKP"KL>?慕L.BK^PL4kO!f5\mJ b-m{` 5r X_Of^)[lћ*ON>sxH(PKJHbx$ϥn_6ȹ֊-'>o,N`j,n7X+p^@od=;{^DMD0ԛk:/hTI-ބ?: wiaEP֋d s1'V3Rw|K&Ys 0??]_]E A+UE82;` j꙱CXQ(?]z_j!c{pP\:vAx~|?PX\Pb-S3djꍾۗ)ח ;gki55u _"{ }h(Ზƶ}nh 1VPs}7)q+>~&[Qa& >D\-C_Sp#K8c.]J:n2ctМ~ ~p&gvr%s"cC8LpFu_T `yꐁ*f[O\-%y=yW0ۻP0'/w w!)$ "QM_E! 2Yz0F˸-G2-Paz* P 64 CXXHҒG$!PV` 7rȰtf<n^\=xg0xEiW~1> ! ]0vFo `i8F 650S19 ]eBKd1a <;Vyxy F^fxF}|u _C!``dcbhad(cS"z=_`xHҰMry[Ɏe,TXⰡ l\bK0,HC;_7u{>{lz]_FTYEVY~??v lDLǝkwbw?]7e!{}MQ0Xy$OŐkBxK+\26 /xAph[+ |Ʒө럇s4!f>[.[6O(qsP nfjDI-xH8|87֔zw/N2?ek^UGbԮڈ?@OC h9H̤KD{AsFӎo^w(h''Pd?Ӷ@z J p5t(F>w Ȧ{XnVK9T"[IYP0> LSjGk\w yht* SI1[\CxRE<;ni^XyfqlIY4tTn [hHӘk%c!NC"p\lð"q/1 %N\1Nm7b!FqPL ` ߈D1G` u㫾ڬv_ʴ^!PP4aHN2?A+|}ES'D<- {i9g05pkَ 1Ňnoߟxf_OM^DM:;0L`@ Dw1w9{ٵؔWn:r4uQ>`y/[D Xv󼼤rkG(.#KQ sg/9G4ZksX#vY+&os`rXaԢ1y@Nz!Iy1?i{̾kKhƝZ %PR! cLt2x/\u̼/[h4D KG/PB2"m8+EvŒy_`萃lI=bKƦ NԳށ]_wAH'ζ_<-JƵۢM&'OS.J 꾃mK>߱G>`e'\2)(c 'zL<9B:Fx5mm3-m9ҜËa8Ԣ"C . Cx+Humu[)~+p?|\(v}K7(uqu{]b|Az|%NtѤ"z;ZǁҧXS׏^ UWprG^>!Z{:GzuN $FJ:(lG3̟WIAUG ͸5۲"^}7B+ =}0 ADY[ז%ZS>*x?z@>|@KU\0~~>z@rswVQ~* B u1~QIkW%*ty0hrdBzsO ѯiD/GJsl lz~MJϜ!:|v+}X;K[pOYcF֦5CtBg?&C$!ĩƝb +&d44q=4ܧ Ɩ$R&O<7YOnǞn6%n,&h{T?ZLb*8y~BdOamv&8 ! I8@-;L S=O ^ AyaăaEo;J>TmEXR8Issyέa[HGV `0 aqOĩtg j2W yg0fYrDӰ"ˁؔ GEmb> إL^^UG.o/I/Qcel!B M7ή}6:I x, ?(lk6c{DZNmh[0V+{WG AprkCzʒ|!;6g|?Gb等Se҄ N>J)^n~d{c}%[l=3T?2 @(sd` xЮ>^uΙ}- $A`(lj2?pxwӓ`B>(j &T-6evSi,"H5gzx¢SXz?&sn$5 tmI{~yO кЄ8Lyz*a@sE}:io|16l0h^2%\;}e+4ճŧmc 'pP`l=u/t A$|zwa2?h/<?i_^>n[J \! W@P I:n}q/CY\C K rsyrl7<>ý</?r m?[N-^߸Ùw!a{lUsNLʺˇAJd3dZ, ,).$1~-q[Ba%(/5KRfTc_hdk'Ff O@Vy8wz:p˭s.rҒH+I>ͪ 2ߜ"\hA >IosE,+`;_ic%&gM0c ƒpZrl!r2Plܲ:>H[e ,#"V4hX v%GʬT"fP 53cwP@4H-nX>h_gf/C9uV!?$Z!i]WD^p $B@ &hB*jMXnډ02{dlw[?%ˏxt Fg/@]?^)e̹Qv8+8pE}=lq>i [Oaަ`#`#&ÆWG7/T@"KkR sSldgu)/{^- ⭫TAl-#N_- aʆ`pǴ[656z1]_6ݠma VWmB`P r٧/YX\F4URE(H񍉓nVG!G+2rx-ʤ-+w `# Gv6g`vEGMj%9Joe5فeaއSq8H&} Z%F&l|h4G @(Г?ѻ-Ơu`a5D¦ht ,?u-e/T aB5֌a挅kYvcFQZ/zpޘA mE=hOtUp!,"$噈!2bhm6݀m: Pcَs);{Z3X pm5 LFN˹;yCu״pVtu[-o`kͷ tω뇇-yX@">'>zD2L1R)Q'>,֜?x|"7*qĶrw> 8+@c%0Tʣ'Nk|;p,ϗY3V6xxС0M:x:էUҧhp.锛 f?ke~!;f,q9{?{/Ϻϖ폣ؾ.3I{tO2p {񜯁s[y<   ҧ~zʒr=1ꫠ 4O/H=7.(zhi ر/iē$w8 x{i?b QR 1}m˿`P)X҆/%&DŽRoag+|4oB Id*5#zFc_8s_qq;Ǯyt{,Y C3v7F _wf|e)CZyӟ~o*~)w?&V^,`+@ӝzdklԫoOl#Aaz -5on`(uPS ȩ0,0slIqu,FJĨօ*fGzYC pWn;-jP0{8 ;`S慀/h؏|gߊd*H%*HrfC+73Đ(XPa#jaX Xc XD2}LL6ZmXrTX4PHP@ HTCYb}kxCL$OAviv%G7|-? MsWj]ѠѨ`{|-hz$g9JwagTveI]haU>3uewy(R@ ҿ [*5|g!/9mCt(}``9Avp n˟eWѠѨ`a0#Wc0wrL> wc|c :In4¤bM~n!I{c]0H%İ}VbW :8B7O 裈@"yn2p PezoJ!W7͔6 ݔj?'z*f82$>{!xQ@ UI&ˇ\BRU{с8nŗqDj>,iRz8#p MDJDjfv]GxcO'ٻ?=eAa/EpO{tVjCG]-5;x yB.ag@od8¥~?u,H );xP`h=(9m\ Xt)ǀ=@<Gp+8/Cqw 4eΓ5_<R>~ Cn0%J2͕bܶ^h1rs5T-">SP|6fSHuŃWl%Q Lv;h\uv(Ý~A*n?ʛ I (j?RyF~ -ֵ(tuˑN NyH@!D0g$y56P 6U;DGl7(`; Ϙ I7xHX4 Z۹煫;TQ@ H@0k0HpmsML.~v&1`uȷ] 8"//* R@ l P羲j5 :|A!p'e/{'a)@Sk. fBALi?ށ_|݀r_+%r%etۿf1f3SLg%O-S o4eWzcX W^OAx?"X 9qrng0h}=?zH7c/-ԲN"J$)wlRMlfGØ@D }@8Nq>:fLVhHH7q`L];G9 cK|V #hXoi}lUP@ TT4R繶fk7vor@K^w@<W=CʓS$Ub邻k|lK{e.-,l28+7=X-Ps񮿒#`w[x7xHI̿%gƊJĪPu3} $CܝʵS>[*V)@k X/I3a^AK,T~좔c( *R@ VLt jK)-e,}0>_7N^QF\TRr߃|şߗ0,o5Vn|TzѢѪcUtڿ+8kR>:T!݁#Tu ܳ-tboX |i&s-]3P u4˞şڞOt?PۤYi I0)O`aStGkÝb|MOr(J0񵄒H P c`?B=_\_ xOb5(t8Z]>F%V(ϮgntOYI?^_~̀k6cjfzsYFT$R@ ֪VO~0ɖI d$ERDB;}Z,M% 9B/Z<9fǍt@X-+P ajnp+~ K(' [M>u;\Amp]NTqBP DO9}߳vr"J?O2㧩ϳKbVw(&v>s 3];#=S?jrBc/o-P ) [p/@>,~xz/@*.S OAJ$J:rz'Œ/^ybں{52 tҀ8=CL@BY1ɗ,_ĺm?!O  CB(mS$)V %Q%fnF=g^قwLhk|38+B, H`IQ }AW P+a]|d^I0!'cŚ ź='L} 7}&@+4{G[~%z+ Ȯ=ml)pxz ag'Jļ.u[!~uҏR/L_I߯qM 2TɤJtC88c!VOƙ}ANxK&XY8ncO౳tSԪdTI@q</DTOC߂|t@Q K(Pr^i]0itOaqqE .t׽8}r;r˽&0#H)pGQ->T ׎D]l+9WLL/3,nJ᥽ҙ_& rj? KoJİRiZgb*" j7= qPǟ.?vTM*`{ SЄiyUK1NJAY R0N:lx.;!Ӝ?Ep; 7XmU+2H1wQdQ@ 'ΤM?=ƥC|7p3>3l?2kZ:,悏&C8)#[@0V$2E'Ў?q<¸I E+Z!Yfo1۰b#ꪤ 8Y '9&^`%bV&צb^oae^qpQ5Pwķ 5٨a՞*`a%V FՓnCɟ#4|Łp󺖵u[S2&-R̾ ӹ@&A 8գny_ɿ~tzOYܥ }P82@h1sVG@] %bR㎝9zY,+}o.DcضbE1^BR@1-gmkV-6 42`1"&M Tb sߢ6`b Zt2-߸E2&"K!p*zRj 2j7`"&I ")ߩrw!n}Wٺf+3ѵG yK!hdbv 7/JAfs OBK/Ys/҅6ࠟn |V*Vqy=Iؤaxh/]mJIq2TLivkdSjhq)L})_c:!@B >h:Հ:U9.ES)@+o?*l~.%@ ʟkwW4ՙZS~"5Ҙ8-lj4tZfˤvhww5z aW(P#,PWa&/>_l-VG}m)p-`b_ϥc;6ۃGj2)̏H䌐.œR1@.;`G96 ;z.6oݫ3"ڶ]/G?gH+s*D(qMMް+0z'qX+= S}IrbDS@=d:4(0:S?FP=VkSWXJ<Ң/@p0sJ[]!62%D[i'X ɰ~~L|$0;$R@ N߿wZ%T:];݃.5 )( )Aqp! U>3W7tئPQKJ=ՠ߾=yo짔OӋIo]Ӕ葰 @ S,}1; A^ҟN'Sa1`@4\H@h[xpzƞJD wrhTQ gIDAT_cp[c>Wu6 uIHz ںIN9ǜJDa`h rAXᄍbZ + #2%J@ĪPcb }%A Ms64˿r#uK3H IP|µ x?z h\6 KX j0?z&/!9emIlFxA݌Yxkp39BO\֌fnRqXW^>X A c|""yVL@DsSB5KhqwfU맷օ+Wʂ5OĄ=Hꖶ7Jқ"5m]q 70Eڬ5 $R "wS Gp2b^ݹao, Eha ְZ Qc@*pҤYy`l^N81b A @P_xa[/n Q@(@󝚅cw79\nwlB2SIEv~Z֭G7、?0D㤐OH?y'WQbK%"PuPV %/;= ; lSN[(xBnI8'JD>5m|ou,MQ8\'< (B,0ZmS=g=+F/@9Đ{W!w ؎^Sz<p鶻%,M@h:`4?TeԪa?-(--P恕.^_JeSA u5W=I%*]\ۤ7B>oY P=U7SC`0pA(P cF VسnȀ6*YǕ4s: ;,! :Jנ;<{t)@>V ]wց>H] Fh($x_O)6CdK"@kujn5^[:&Ĥ1ڱQwⵗB;]l4DBx[n)Hn)PgV\+8yhR ~D(k1_]xgc+'M"j?F?uo."^|=V*F(@kձB㦏!:q'\5~٪w<` w5^6H `U91Mh㒌o,)A|B:s7o=[iLs"GI}ׅGw'<I>%rH$`f3l-XW4(k]?<٨4G%*X' nmizP֐Dtv%>|ZalTD%EJ=8)NY0eIN% (Pj{esХv,7'f>It[~ 㒤yvԸ>ښ[0wN]("nLgL T);mR/$0M>0 %}(WTTѢѪ.xl= !auN(Y!~t!骪G.$^Iyom Nb␌0m ,,ug]x死0&Th2d#1'Q5hmrͨc @Xc0wYU\場JCQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q s )IENDB`ic11'PNG  IHDR szzsRGBIDATX W}LUޯ ؤ 7-W,Br*ȩ W,A 49RY3 P 9{ ޽y~<} %UӃ[)pb1ƅ |F"֗~1Y_zeAN?bHS; ICBJ=F,CGs}$pb!MwIl:;AG4Tk6&%.A8ָmXrC$ *k" H/;!8];nfyk(ؔ:f=q{xFH40bc82 _hZ03G.Z |M2/^yW+\E \s'^*o$Xk`3FM"hsySon®iRH~CJ'.Jz꿑 E%84 5](}qL"H+Tux_F=x-'%*j\wEOqNːI\VpzR((XCC4?{ۻ{>PDE6" b)<AoX]e`"{+x3?r@qG80LdU#/ ;0g^^B7hsY|{9e46}hVda͡|C+h$Kg!йjX0 pÁ̓.-+x>+ j R IFQR RՇp TEK@ 3WP0&U ?J}%?ABU9:<=>ABo^PP=789:<=?AC]Ys\W66:==?Boml756>>@FAXEG 44>>?FWFK34==?FCEYBM<>EHMfQ@<=@@BD;;==?B:=@C<:;<>8:;>989673667D44=B021CB/18E@.058.-/23..+01.../0..-,-.;.-..--.'fvy{}Uprtvx{}onpsuxy|lmqxxy|iknyx{phiwx|fhuvzryvzyuw|sutyz}qrtvy|qqsuyqqt|nosmnfikjhhygfebdl}@`bhk`_bfi``jdg``_af]`__^`b`^__^bۉʿ̋ʌ̌řis32|lkvmmsZTn;@GY:><7>I^k7h4`/`/a/a/b0c0c0]. d.!f2f3g3h3qAuDa/Ga/a/b0b0c0d1d1if3f2g3g3h4i5Za/Քb0b0c0c1d1e1܀@UUg3g3h3j6T~Aa.db0c0c1d1d1e2e1Xg3h3k8V}@{?m$c0c0d1d1e2e2f2h3m;R}@~@{?d0{d1d1e1e2f2f3h2LrB߽T}@~@~@{?c+d1e1e2f2f2g3h2Mɺ}@}@~@~@z?d1e2f2f2g3g3h3h4@|@L}@~@~@~A{>d.!e2f2f3g3h3h4h4{@ϻ~@~A~A|?g2f3g3g3h4i4i4k69yAR~@~AA|?}g14g3g3h4h4i4j5j6|@ҼAAz?qg3h3h4i4i5j5j5j55{@WAB|>cj3Fh4i4i4j5j5k6j6@ӾB{BUh4͞i4j5j5k5k6l6k82}=\B~AGj4Yj5j5k5k6l6l6m7Aض{@8UUi5ܠj5k6l6l6m7m7p8@@`|D)j5jk6k6l6m7m7n7n9mIf3 k6l6l7m7n7n8o8p8Il5}l6m7m7n8n8o8p9q9m7m7n7n8o8o9p9r9Uf3m7n7n8o8o8p9p9q:תUUm7~p8 n8o8o8p9p9q9q:r9bp8 m7s3o8o8p9p9q9q:r:r:m7m7Tn7m8|m6/o9p9q9q:r:r:s;r;n7n8o8q9 p9p9q:r:r:s;s;t;u>%n8o8o8q8hq9Hq9q:r:s;s;t;ts;ht;tyCp9p9q9q:r:r;t;q9 tx>x>p9q9q:r:r:s;s;u;ux>x>y?z@,r5p9q:r:r:s;s;u;t:u=v=w=w>x>x>y?y?y?o8Nq:q:r:s;s;t;uw>x>y?y?z?z@zAKp8q:r:r;s;t;tx>x?y?z?z@{@{?٪Uq:r:r:s;s;tx>y?y?z@{@{@|A|@sr:r:s;s;t;ty?y?z?z@{@{A|A}A@n7%r:s;s;t;t@}A}B~B~BCCDDDEEQ`0 i8uJUsF>t;tw?zMLMNJCDDEEE⿀@ d.=c0d1i8xLr;8tx>v?E}A/~BCCHPÊQÊQċSōTƍUȑ[d/Wd1d1h6i7t=ux>x<CCCDDDEEEFɑYI1c2qd1h5g5f2@ux>x>~BICDDDEEEFÇJƌRÄG̙3e1f3f3f2f2d7u;Jux>x>y?BӽDDEEEF„FȏWÅGąGÆH|h5f3e2f2g3h2Gux>x>y?y?iDbDDEEFFÅHǎTąGĆGņHŅF,e2e2f2f3g3i3wt:ux>y?y?|>%ID濂EEEF„FɐXÅGĆGņHƇHƇHʿ@d1f2f3g3g3h4t=v=v=w=w>x>x>y?z?EzEEF„F„GǎUĆGņHŇHƇHLjIɈIvf3xf2g3g3h4h4k6m7v=v=w=w>x>x>y?y?z?ÇKEF„F„FȏUąHņGŇHƇHLjILjIɉIȌI*f3Ug3g3h3h4i4h4lt=v=w=w>x>x>y?y?z?zAZƒFF„FÅGȎTĆGņHƇHLjHLjIȉIɉJɋJd23g3h3h4i4i4j5`@v=6v=v=w>w>x>y?y?z?z@@ÇD"„FÅGǎUćHņHƇHƇHLjIȈIɉIɊJʊJˋKpm$g3h4i4i4j5j5k5yq9 v=ԯv=w=w>x>x?y?z?z@{@ÅFÅGȏUņHŇHƇHLjIȈIȉIɉJʊJˋJˋK̊I#i4Ýh4i4j5j5k5k6n7AUUvx>x>y?y?z@{@{@WŇH5njQƈKŇHƇHLjILjIȉIɉJʊJʊJˋK̋KΌKh4i4i5j5j5k6l6l6m6/UUu>v=w=w>x>x>y?y?z?z@{?q9 њbЕ[ΔZɍOLjIȉIɉJɊJʊJˋK̋ǨK͌L͌Kfg64i4j5j5k6k6l6m7m7m8Rt:u=v=v=w>x>x>y?y?z?z@{@{A\NZZahfdݟcݡf۠e؞dםdՙ^ϏOӍOj6۟j5k5k6l6l7m7n7n8o7m913 vx>x?y?z?z@{@{@{@8ZZZZZZZZZZZ`oxi6wk5k6l6l6m7m7n8n8o8p9p9ȩq:q:Frx>x>y?y?z@{@{@|A[ZZZZZZZZZZZZxV۔Rm7k6l6l6m7m7n7n8o8o9p9q9q:r:r:s;s;t;tx>x>y?y?z?z@{@{A|BFUZZZZZZZZZZpaUߚT٘L/j6l6m7m7n7n8o8o8p9p9q:q:r:s;s;t;tx>x>y?y?z?z@{@{@{A[hZZZZZZZZcmUUUܗT`@l7ڣm7n7n8o8o8p9p9q9q:r:r;s;t;tx>x?y?z?z@{@{@|A}A+ےIZZZZZZZZsUUUUUܗRl8@m7n8n8o8p9p9q9q:r:r:s;s;tx>x>y?y?z@{@{@|A|@w[vZZZZZZzYUUUUUߙUՕU$n8mn8o8o9p9q9q:r:r:s;s;t;tx>x>y?y?z?z@{@|A{@UUb ZZZZZjhUUUUUUUݘSo8o8p9p9q:r:r:s;s;t;tx>x>y?y?z?z@{@{@|AѶIZZZZ]xUUUUUUUUUۘTwo:np9q9q:r:r;s;t;tx>y?y?z?z@{@{@|AȼzC^ZZZ귃VUUUUUUUUUUޛNp82q:Ϫr:r:s;s;tx>x>y?y?z@{@{@|AM ZZqaUUUUUUUUUUUޙTUUq;Vs;ʬs;t;tx>x>y?y?z?z@|@ƶ}A?[crUUUUUUUUUUUUUݘSht=.s=zt<îux>x>y?y?{?е{?{@4츈wy{{~贀~voi`YUUߙUےIs:v;8x@Dyrq9/s;s:v<¶{@iwIʔ^h3+k4w>{@ņG]͓Yȁ9So7s:w={@CKz2oÙߖJޙUu;8w<{?~CqάmIZTvnVm mmokk-k_??Ï(0 ;;^-^-^-^-_-b0_-^-^-`.a/_-^-X^-'c0`._.[De1j4`.b/h4k7^.c0.d1s;If2՘d1A޴z?j-i4Ni4}@Z}@m6n6p9cp8t<t<q:/q:u<r:s;pw>|@.z@v;0vy?|AxH wJt<t;cx=}A~ACawFv={?HGĊPکpƅGk6y>|@DŋSƆFm5l57m9z?by?|BZwAFĀ>.ɏUdžEȃ>Kl6l7q84r:x=Xx?{AĤ;pl赃6ۗQj5p6+p8q:t{@A^\HhbSu<6v<ʶz?}AタCUUɟ紂빇+^u7Z_\ZT3mma^Ulmb%ncklo"kk"kckk??PNG  IHDR\rf-IDATxwTϙ^^0j@A؈&5J$j d=(byQc#F%*6tܾ3s1}vfΞs>=9 AAAAAAAAAAAAA!Bwfzg-S=n"Nf%P tD $z M7hz)ODqktNm;9g岊G zf:ck4/n'  ^ WW~?:[? z: @u;̻{L$h{n/e ^ۭD> hsN\S+Dh5ĕ6㬳Bb|"M(|~L hyBV61d8j !J\YLye nؗ5.~n@ꅵ=A5Z|,=B {LI*ʴP(XdkMT.uiFw # @גe:ORH.xhVoo +sP4_VD.ptr`YcΓtH @p$Ս ~Z5urі= 5_ }0\ X h:?'bS GNef\:xCP [N~tdϴ\}3GS+D =NwZ‹x nj<M8;:fzhQtm?(g`[Sz2!N@1;Z(<@ASV7Vn:?  87A8!`!3L=/@g+@i:z ܞ TE2Gx 妳"P(yxJk}3]BS%ryH)@.:o lf\?=`%F4# "BI\&@;e Ä?56B@gt^PcM^7] s *sOFy/"BRѺ0~`3xojљX]8ME %~Ix_U.0 A@(IxdgJ/al}SSbARvEP(򽗀44<`I.뗕kMCGJq :I?Dow6·@@(9+:!AgXL !Kiׄb\3DG7x4Wp3Ut>t"BIl]Nohi ;}j΃D& %YI[x;9r#+U'S ">{A@( .8qS.E/MA!3q754> !V7Vt q4 ~7`= `VCW܄rBG*۷+ckwS5@ COc|iSc@0!,E'ŹTxqB:yaYVŲu {outS>CCz/)CG\Ԗ]i~ǧ}P.WWܞA+si7AS_~2ǂ͝P|=G\p<,$4]عSƫOMg鴛[F\^ ԍp)?M؜{׏~t~v6߿~wЧ#T.z>]u}OMݦps/j{RȣPND_4tv6L+66;nR|t[(n]-\;Y )6],jn؎3_WC&;q?aoL$BZ#2Qv vfoqrXͶqsh\Y:vko}p*B @etQg-~+|&{=[7^<`6ܤ/X3R޺ *Y!+Mp4U.h/IO]mG S|Zz Muw LH_o[_/t ֛繦],8Vx j{A@v9G|/o: ƷɃO7j . 0tdP` rnNm6 zv~DS:#Bء+c^3b‘py& nw?1'<>Ga_޳oYmhSEZk~ O7bÑkDF|$]9aDL`gLܙ6r` ğ32&% aOg$Ϗ|Op+/pCsˬL8ƂCg +=G6whX]KL(D^6 q(<:[ 썋HhL|&J ` |ŎZ 嚚yW}*aimA3Ċ~R( :;mN*X+IUq?r\:Ccf1H(sv"r_8<5"Ty b0k!( 7dCԈ۾ҺږQG5󙓋Dz1ﳦӑR4EI|#ykLO: ˂4^|pI;ig2쮳>1m{*Kn=Yg,:i)lD&] ̀cnycSpւ=|씰IbY ?G!߯i#?M+f;UeW^,Y&-e4DV_7Rv&v"٠ o6RZX|vgbѭLD MLKͼy."LI=vK]dk"AҦz iy`փvHƌ/wXiccgۚ?pՃI`Ė0X(=?WUC gɥ ]7Z4,>l)%)vhгƃMA([li} g')|?Ќs4J_GG_z X6.o]?l~qu`x!IZv(KM-.9Eeis =oͅ-lfeBT6"˱gXz8Fk^T;C#o_:(G]k__fcfB> ow1$BJueH ( vX~ɴP{m7d ?J]ZMӍݶ Rg7w%uezJ ov*Om'f~og|X8st+ȢU_ԇgm vSk>wRR4*]Uiq:%/^_em}`6VsT2ws}+V{Ɛ ]SwY+!Xa֎_NYhbRli/ (߹ǬvIGd z{҄MYtš,t%/g6[>iێE('/gܭR6L<q5г"U"#쾰w7J^Z!<ЪeۦGJ*,RI @ʶ#I/_q qDU7[ R׹?5mfGY6gB@RH+5!ahjPuFA^1.n36|oRRL;9masP(uOE<®z$nɛt@M,tvW! fԷJl0m`gƭcV飔Uv/+G_ڏ#mt 4|Oᘿ_uuQ'CdNNghEi0#듷3/Ś-Ի sU?%YMq0c][ZP]Adb{44pPgs/kS;N2-jV^ԯ}*=M*hIտ8B0δ~ jR.yϭзڇcu@%vCrafm.Q:Z 3WzW#ضq}&v6ZSiH$Ѱ8x/X[8st8EG\L5C>PUF/Ax~E@Q–=xs5r 3p[Y=*@DiE3ՙQL14{&l.HG ;oBѓ⛬9Q–nG֠fRG@KU;l,W<7:>%U+0t*6['HD`U7ɾY:X~.8r~mԀZTӶ _0]?vV Vw *R`[2i)!g U6#7ZVR_qCKH|{3";HTR#V(M8>׭7mS22/l{g=RP.d,ϖ{Mgcw13W6̯-M?: O3 l< tF6W'4 ֹoVL8FzӴ ]Y `~?vv{<&@0Fކ6O/ytvg% &l{!yc3l΋G}6bVa8ۏ,O=m?@U+@LN`G8lv!\r[94~dH#޸_X@?"pT_ػa6o) ~_&"K _~3{mftMF![C تĠÓя)t^ qDJm^Uq6Qm_'\?oa:bc+l]&i o:dDe=lb+k3@lkwI7'瀱v֍=?[1tp؄HWrs3OI oA`(橋G!3"@Ì/}X~4_0Kx?M燐AXns6` $ٻ3BJ)BhX=d(=#>/?ۍt5wz/]("Cr/L]ͼ"O7Z Ca,gVNaq3k\х5ʗn>{ C0e#揗X8 K7bhk:}Dr۫1 _3J"ccXo<&xB0 μz[Oz Q{}sNztڅp ^d}c+ܾ_q3լIsFV2޿p ͬ@np  +EEcn93rGP|Ít0f_?ғcWrKMVXqu -A |UǂEDC1+^VJBLxD-L`=-~d{ >`:B8&@ۂm1nC;!v2_%CYv*~&֎rXOOOv˘N1D V mXo@t9G˒~"\kGkBi#d,tFJE ދO77mC("~A(oYxO@e֛eoM Y:v WE m>:X\F{# D27ՅHd0h)m۴By.~ଖ=T P)O`J<xzZs"@i?V: } v3@"L!tu< zO (ľ%@였ߘq t>&|=e {27r:71mPD (F>ҏ#ƽo/L/n?tITӏk_g~pt%@n||wִBp8&D?3 Ԧ@B_(,"t)8wμZpR7nBap08M>  ) k{o䘁~F_2p8agl{"luYp:mXf#P ?!ЌO`i85&QfX/"6m|*iMQ("% |nD|1n1m Jш0=J(3Np,LL0{CߺtFm3}o3"סSI-(3Gm) 90NH/KtAC(C(Qw:7~]=Nga3LrܽLW( "%g@0Oh$軣?_Uoܶ3t("%+kzR/6]6g¦hhźCv% he@  4:nOĨf@~?pP9ķ@ub(֌wW|Hup("%5JE>6q!_JOF/O}X8aw<)M>0ݜ.>FZ)͆X Հ=ʢפPDJ:Am-3ôrjm' v=tV U2 ̓3K=ct̫6-1tL'_( "%ξ0?GB{[9VӍB 2G"PI8pG»qxe4^zofy wǣ g:2@@l^tN)?m*k~,Sm) pѸ^;5i0#(rB5@h9g YD1tE͙1@b^,ê[ Kd0H6~sfWk6;X?֖1D؞i#^N^5bB1,ֆh@Bu `"5"g;v#V(󸱥 eҦB( "Eгn #\0 x<.`bmXjNEHQ<{~7kDnPP#hXC(F~̤sifDBV{@EE \D_//{o8EEگU@BLg?3fgXq9`:O Pd ?/2H|cGs{N77޹ +fB| @lnkTLh' &'64י0L^[Ż%Dt!9\D@1x=o/tھ~_ hGi"PDTB'f ?dn[m.* J :9{\k}Omo TD!5~/#sj{%{hqUjEHp< @N{1d|>ιsG&о?@W @@vIKp^T42~tAH`\Qr3ho^c:o!Ph;mM{~.70԰ov6a`'@`A]A.aǶGEW [D 9Cx/CϺm'?km=@(ID9 XKpbQigE34HF0\\ѷ{%a_R `DLSG D5脻3G +LgP8D XYovNMn3GB0 }nuil-*9("!p^@^v^N(b_8tE<{sp4H(=D̓_ nw6ak8uzs/{3Svg.*{Lt E ?0{Lt}q7ۘ H]F -jj=c=lL2?B "U:"-/2PO)8MMPxD Ⲩ;7 B>H_)MPx `hڂ M%t "QV6hpQ& ]A{`66#e6P&uD Cgm.Ęm0R`?f]yrZڻB!`f^ [,Q3n͹Lvfd ɠ z}wqu< pa݀ `71?)EFg 5"竸[]]M(͙t\'XEle<4qi!PD,;yug ֊_ELY0 6@HӾ𵀽?m${ߕ+Ol0J<"E2<|%]3?xܸ2.x(=}Voc>;842e (1]hiCi{(60@a ?UNjCAAAAAAAAAAA:˸P8fIENDB`( @ ;;^-N^-^-^-^- ^/`.^-^-^-_-a. `._-_-c_-ؒ`.e2 `.`.a/Ca/e0U`.ɑ`.b1 d0g2`.o_-b/Ff2i3\/`.c0h5k7b/d1el8~Ea/e1%f2e1CBh4ŝh4z>;Cg2j5=k6j6ĄDOm6ۤn7p: |@j=o8Qo8r:o7Mq:t;x=!v=r9"q9s;_uz?}BzJpGu<ӱw=|>|@BDrA:yMu6vFHÇMΘaUf3Uh4s3v=`v=z?‰XBCEʓZg3Xi4w>ײx>{@iyBƒFȐWƇF̋Kjj53k5m6{x>_x>{@CFŅB;ʑXŅEʈGv'ήl6m6q9slDx>qx=y?|Aoml綅n7n8p9s:u{A~Bo^ߖPPވ7p9r7uߵ{?}AۼBt|@ٸ7ɟɠʢntF_^^["n/bx`^Tksmh`n-klm_nlklm)kmDmmmkkAk*??C??(0` $;;@@^-a.`.`.`.a.s`+`._-^-^-^-_-`.`.^-^-^-^-^-_-c1`._-^-_-`.`._-_.a/y\6_-^-`._.`.c0`.^-`.].b/?a/e1`-^-`.b0b0f1c9`-b^-_-a.*b'[+d0g2`._-^-b/f2i3_-`.c0i6h3^-a/>a/c0f2Si4n;d0c0f2g2v@f2`e2g3a.+yDBf3f3f3ý@Ci5zi4i4p8=‚C4j5j5k5džEl6k6m7t:?t.l7 m6n7q9n7o8q9t;?o:r9Gn8(p9q:s;r9~o8vq:r:u;x=gr9p9s:-s;u{@~B|At;=r:t;w=`z?y?{@}B^mzu;}s;v}B}A~BDUxKFVv=vDDCCCUUd0gi6tC*t<w=Dv{?tEDEɒ[ĄDf3f3j4@w5w>w>z@~@/DÄFƒF„Fʔ\ƈH̋Kyi,g4jg3j5w>w>x>|@ÅE˖aĄELjIʊIόĽLi44j5k6m7ly>;x=x>y?}AFƄE=̘bÃCƅEȈG͊Il6k6m8q8wx>]w=x>y?|A~B4zBsuvxxǡm6l6m7p8r:u;vy?z@|AV\OZYXikVݙSf3j0m7o8p9q:s;tx>z?|AD+|A[YV깊RVk;p9,q9q:r:ty?|ACm]_Y컌SUVWrt:s:u;uz@}ABPpiߖNߖOߖOOxq:~@x>Hy?f|Aa}A3ٷ|•×Ѯ_]]][gdmga___h;klmja`_`Bkkkl`^vkkkklm+k[_ lzkkkkkllEkYkkkkklmiimmmmlm[mmn jpk??????????PNG  IHDRxhIDATxw?{vMP콋EM-`l l `C4bb7|c;1Hr}wg޿?ݝٝ-3{z>|g9yg[wJatG`zDs~oa+@DD ӵӦàҪW0 "^eL w^KVSșr@DDe'W XPFzmx#] ]5HaJ遀Vdؼ΋7DDT6Oolo ۆO0 "&Lp#Y L561JLWsoF#Tu.P#eE~#hpY9 C X 7zq> '_Y 4]o'g?b#ߨ{@Y3>InS0 ̝9}q0$Xh 扅ĐO¼_vdW~Í_r(=qL '-#DD.R}>4?(0TC!B0Y5GT+Q|KYQu)J{7x'%Sʞ}h@ϼ#7"2t#SKp@^љV\:Ǟ+ܥU5Hhokx˟|C`@DT(l/*jH U?7iﹶM{c5to$ "*3g=js@桸_>dz#_Kr<ǦYߕ( "*;r ݦDxojnxx4P-$&]rM۩GR>-*3[Bm=򆽃f7rߎ(' ʉ2<C>=+u񏴬yĒ[DaQ8V!e3δBkni@ pU `tG6wEMEyDD}XrêWȓ#ۨZB^ hU5ѐ>9$Vltl=M`O?Т0]rFE0'MØyۑs6 {ArhekUcҪڦ)8mS7Yߕ(g Pd$#wJ N o/Y)Cp/c@D`y^`ϨJ lHŪ`/."r"O.IQ ypS*&]`{o9~ߵ-Qxc}[ruO&B+ћ0 " Xmk_nY{ ^~ vdbћ1@"{Fj.EoT,gpiu,S"}88@D05qj&zX/[$ ټ" Q.H/o~Uwrm(9#nG=rRfXqE^4IЇkw%ćm>u_>j6UnO,K=o;ߛ~W/Ly\@kCwNtx Qʋ0 "ј <'>hlGּ\wcH\w%DD>҈NPU,<;uA,dt^Qx5%dXnH3IuS>;^|mG{SJ{j/>z`@DT{"J;Eo9~@-Ͼn=u ?F"ćm>.-Kw[j/cO=hM8@D䵸\\0f,Y(x;I*3w)""g:=zج=r \Q5 n,c@D!#lNPw=fK.娆'`xm~71#L?0@uE<_I,S ${ "H"Vq6}Pme^)""(. 4DYXh[8I nem5i  vUpQP 㧍S*5Vs6.)w?γ]uBF~}f ʎ#DDE[r\9^@SO|`ږ3qVKnTEUK<~QUZ,XfU,0IBtQ""lw= !VVWwK,w=DDE$&/jb0Ȇ®"8{8]irIDDErҽ?g("U֊!Od$S?Rn8@DT$VH.E&Yc-0`?IвxD/񻢔;DDE0;@ W9L$O;aߕ1 "*^z[U06tmG{SD.敩Gw(? t}K%}3o9& 5佰 "*w 1/HB%C+v5Ȇ]'_""*,]GBW*W†*"j+>^TP άn%d 1A0H TF *b E4IHZXTk WPb͉΋H<,=T"Q[iՑj""aUa UG$un%lkߴ]WSԈ2u"DMfdE[|/b|2/Ѩww9eXŷ:G$ͻ:" A8$HRZk*Xmu"a1+E†!a#ac B܂6[3 535K(%hYhn7qKf´6J ט1J ze0yf7D*X{; пB6p,7_`@^VT5#: XGQDև w$&bj*R1j+DBRS)*"!4d)O-ilZўqKy/V;n*Zc9|S=zFp hFaCBT.fJsM_v. KmWW X_Պ* `CU n75HsYٞP%-h[h\%,q Vs՚=c?T #GNQhoQ Z~ؾ^4!:KԸ|xf;pn,mJ]kȌq~; ogr #!Xkua{u8vta mWеT>'T1􋪈 O{uٺf3mtՠa@m^1`c=Iy6l,R`!xMW|!3KT{)P6\#ҶHWwb#6zPq1FX8WDdH--NzϿzˋ ~/WMy $H5-?z rB\`]x2}>믊TvQo0f~{ZbiS5glJ3|̚D<@zK`vׁE@"Uݷd~QcZ05!=x~ױX,կ&~׃箦5_Vֶ?h@SOجб}^Ruv*Yms+/as-|cnMzKW~W-Ћ _ȳ_Dm]`KhE_C.}sJQ/"Ͼ,|0aMkh;}] *}Kᶟ qO++(ڰ}kjY!I+C~`PyRАG c j/o֖%[A&:m̫a{ \AJ d( ,*Ԑ?LE$~>BkfPo(LN1qVkBT$#GN_;>%=/I0?@a2?@W 5eYWIUȱ|;qG;[VUъDz9}GzCc7CW_Iu^0lsV'C,t6tVëfhU`ij3}^j\șH߼$IxPy8dkDɨC }ܗh^cM@NV]G0RQzCO|T/"lW `p ߀qO'g߿kisZ_C~Cy# >' ^U7m0bjMu~ ȞLP1l+}AYcaܹߥHCOߢuT~QW>zqn Jn@,i}7/m>!Ks"@FPRkGCmÂpiX#gnǒ@/3_ t3],dl]s%,œ9pxZJ00v\"@e]pPazޗ{;PE@z ̶Z a]/Q[z{GH-k1hn2Ѯk)G_f(qw.@L36I-h„uQ|l ȑCj-&0Y[}>\τS.E@~/pVu ӏ3~))7߮6;vo<š[]K^WRfi]E8vl:~4Payڌ< ʌ<FuZApjn^ +">XܱhM@ONYǭPM˜=w(M\)%~g-{t]D.80Wbڤ6TU:S1(#SXc߮i߃a{w uK$ ]Hyy2zd*"6Xz38NhucP.QCs.EeA/ze>BN%O [Hr<~ن]6FuE_זx:Sy\ y9=>EwͶγLH>2c>6z~וP}v;O|kqxys]Ѩmv$t DG+ICf_BGIn6/^ya29㐭1,kN$[9s-e`3gtX%/t_kܾi2=$2~sz2i*١ҠEU+SiO{c\pު ՙ/%ئ3o F?7v-rzßqrZ-oM9{&= dV|aGF(_w֙D46#[]B?  ^U>pUovzfgO+lTXc.f_w=r I,c|>hw(GN@{(wH_F#Srb3E1͈uEGD_樇~NU'~ѺϳjjY `=/]؋~ס!h2n0~jQؤL"]/*_V`>TBq>}>;ioO9%_{_fwBW.c>!qwp>w=DB*+U@mZ*,5U-ql"2j;/6;M@>:i`B4jyع \ _. 1w@Gq!6"W BwBS[Zb96Zh[ yɉ;wRy<&Wv|Zu1歛(hz~q(6ۖiέ'w_xe?f$n>"% 0F>u[e:O8!CP[!4Pj+PBDžز`Y&hl5L4~F_Y'&!~OK=Ɨrܮձ_yG{m+4or0x^Uԏ#9lj*:W;.!!VװZZhl5qj..=vp5IT(f>9g_w-R9,ai}O`?AUEO(.t5}-.}9:8v[ 7 DU-X{?CkMmeh ?S$o<ݧ{p ="F'aZۜFTnkg"ycпh]R-Dd>`tdsZ6+2}ca7ի\3䤙x]IryͯIї/5G\%IkAr$Hx$~}UBUiHw!k]L;~UiGծ9q{J1 Ctvكyݗ/rQ$ \Md7f,&*OAwpԖ@}UF7/n~u ɟT\ HpZypYw^kM_K#6 "Ј͇e}1]nA݂?nrswG}>vX*6 !x 4k?Tq+<*e򻏂js") Xғx{;\(=@Dνi4Y؉F֚ 榯 D>oZciH/~lkv.8l- KO?y@ʮ1 x1 =1P:lcU؜=>.oSJSr as/n>(~;}YΣ>@t'wɲ$/}Z%nk9xƣZ0`?/jHqЕҎN~Y+TT-O)iQ `9&Zjȉ^7ﶖ׮@/K_cě|Heu׋%h3D!o1ϼk-륖t{L9mZn^p2ϧۥC>#~~MPϳ`XÐ~]"a椝>-yR @L+S |F+vhJc,K_Y@>C6C8 Ӝ?[؈M׬K)[(U>@Z[|}}(Abɰ!+N;7ZT H g }m,Sk55)9|vSfeniSV|o~ 7w ݫ>&R S[/ 'J.~[]eaf^4S?9Mזoc*E ~7DZE򻅽ŬkG`֏|9>S[$(9 >۬Pа[.,Y}%:g޵.sZ[S_w{&xqvP~Q5Dp^waEU$7bwȟ@@$-\\rH [n_o3c(S-9PgK_'~x-aPlڣCe_m֫O+/i>{WԷ1 C6>ƗM> |֓烊nue޿$AN_PI'JrYS[UQK3. Is/cV@NXdʃ~7S Q(+ AP/!i΀Yk~?~Zv\Ż~P# 8fdz>cs6 ^|Էb7i{ѓЇ uH{t8D2vq=mamkU u BUֳvG[*?=IXW(Rാe15$}6~y#.MlhGWc @-yvxB˰o9hP&eƾxe;Wq܃|}ns;bhυO9(j_V+$+MnFl:imS.,=w>w_xǓGWzq36R oEЏZ 0#48]rxeތQb,WDw8cE>@HM\/figI-mzk$P|8C UȌ.nW_1Mrv7-w?xl.H^1?ԊuV'ojtL?kh9t  uن$nW_䣾q 7qнc:/[. UdI|k g'y_`.Ir04/0؅|]CAIzӿ5_;Pː)smO'I 1WCU}ڱ~>n)P[:U89_  ,ږ\2m˖טtI.*uAzq{(^x{)VYZ̘GGd@o @| @cnS_6/:*? =Ɨ{>Ep_\~~C1%,d9vm7apV/f,T>^1/(3ݦ==/C{Ų/#Qn=\>V`.ۊ^[ NjZ&j*#Y|՗HEYVITFJ9PT8-& CP o9@-CO{⢽;L(VRI@Jbag~:RrPM$*zGWϮWl"VbpLE{#ʆ@p T4ѿvwsB @ӞLq*sتEP>az4tGVQSB$ddo0I * Rla@]Ug>@,P7n'r~8BpN>njd] []ꘝ3XQaMX_ C"` Dw\Dw0 `qhD˴ŒUT_ tοxFũ%*l5h{37ֳح:@Tv ?@c~0uv@.Tψr (D$t ?.oƚj ;: 9p̘1ʄ%u,9}c/4~tA8bv6Mi삀jź3Ι9iߥ *# B6]AeMP炥f CyOV!u=/||mːIhWQRY_/nFCjm |w@Ph@׊_H jF*TVf n}Y>nk>@$dwse/n;tGWfl<х w@@`?5&Hwan蒬 _tzr | Zvy6G_|U`OXgP*2KfMe@@XivI ]5PW A fqM2-<x_G' Z<%{w@@M~Xքʈ`pBCe {pG Ŵv8`#n gBa3Q~u0-OM1ٯ MCN0P3]B׊~\CծޠvPFݷJPdnOx3sm$W8:/; ⽻Yn_' `ъM  Yw"ߘٯ)4 E2g~ݍ꺦 FMAaE}ukƖcMbBE _D ~*l|㏔MPI*^[?l~][@ޘO:y@E~U?6 @߫F+3 ]j{ |FTWPݑT[_AEcaCǪԊe?23B1wPl޵"<ـ 6@Y"3 6=^+k}r|\^l^v;T[$[B,I :U UPA]ua"(Mm 4%_-jFܴvLgsrz"i/L:ml5ZZ,(BF4+#s ѨhN217v! *0j"!dV4DZ%nf>fBl "*>S\u֨C{’mysם޸aG{7"~ APcP7}泀On>TvaPSF]UUaUQ12 Z;ůjM`64%w]%HXoLܣ"*o;~\XoH֢O䞊<kQx>p4IaCuU qn%fbyS;]Ҍz0C{>@A}WD @4/$c!5+ZP =+1N迡83Ӱ}T3TcfCk7ܖ@s{-mBkL{|s,32"*6R ۿJAa~{Kn5Nh'"/18-<5o'SY(f>ʶ~7F" g;c=m~b^;3*}om.ﲧVk~:ts؏kQ `ڍb;D,f~T<uͯhdp;mmv*nL^]TW\wuߛfk}[c^dV6wt"Zվm'@=@~,Mx#Ra@ Z0ZxEŧ/v~rqڇ% O1~>/(_՟Ej%FG& 9& BR.n @| >c aQ Dmc^w~׉i8&v-r/E>^ܯ!J| bIq!̊P^$*7IyB'A3g+-oM9~U ȋ%NϲoG>_R1V8@y޶"R SNy-?ןxu!&^L/) @I'9rz7?ߨI}@]wy~ׂJcG`i$T<ś &N; CIZ`Y jڴq뎫 JzA磿}A$ @E^s;oZAZ'ɟ-kY>KSCזQX\NxWA 1>CΊV> *D0@[=i^ifn&E/R>>uQ<=v2oAz2&wLNo6Œ 0 v?z(g!뙩rh"xvr( ĒגKp(t/ۢz&5ycwζj?#7xnr( *kX}N./ JBĻ?U )'kܼ4H [-DD1kKt&s>ۤ@ l9|/Rk`hZ_c z!gh/Qb@$IK Ԋ57+~T`y*q=l_|F$BV(@Tڭ|@mV_CmE\=A@z`!*AJTZx'i} (z%:їi}Jz'+ Rv $޲#׹6 kq' 0f8L;m'W;m\:r s !Ѯ=Ɨc`-fzDA;zi&q>lŨ YWG%i}h 2`@0x'BlQ:X +m#L ˌ~N{ Rxѧzs]swkޙ#-ru"[7e\հ *藹*O t ,Tes\hz;D# l[rauD {p#8;|=WK^u iQ_(̷>d{a-(0h@wWW-qnGED0 $"X| GDPωvuw:?u䄈:0 i::c>o#td1/ϧD}zcX|au;ğ=@Nմn KYCQP|ai@s~+Kй9SO$\}[m %q|6͹nV@%?!Y}c@Tb8\Ӌxa5cңCTخ6Bm 8\ODRlr6sJ::)Rw"(n (W/{]}|M7uYӝϙEf_&n%N *A7)"6@UKd@395|y*ub@hҮnw߮-K#GNu.\$A*z>'*  0l o"dʏFWMs ,rս/_M"Q]ssaU>I{z1aٜp i'Puht hGh ~HOuN%" mqq1d^ǒS]Q2> hr7vSbۗDQ)빐*iK߮>a@{O 9~tA.5 g,5*ŴI%͓;ѝDԁQ.۵$Pz!2&[ٜ\[SLەE$H-cEjaf;&P|ۺΘCg+D7wڡ-,}|">~6"gi\ݶh$8?1{&TYWDV*[04(A>Y=b%u`@TL{^̾}(e@u1SôRX^ƻ^%wsvVLYGt/gt,.~+z*o:j#2r/|6{ڶ=W:{V{o;0 *n=U{c@T&DtP{^=}M8%@k8cD%LX174|LN-4F+q>_}UQ7Desz7@7nE=8˴i ,R5Q0 *o0'@Yo>2M}{6B…]sXHD]9d@=yTxKI>f?ٵ_^$"De$;=2,avb@==xͯ{ Qi1 *'gkyڽ'>(Iݶ(>GDF1' }޻N7ѳ(R?кCT|' ʌ@:ϟp&[kufI5|(!ܤ>v|7ly?ƗԠZQ 0 *Cfx.+wm ǽݳl o聶;T @RD "0 *Cn7v.mG #zkO1 *_3*>}v Qsw8;O&jMJ}QRM-PWթ\T4nv}`Q|(`+Jq]T{]Ԥm]>ƗV[>@R1 *S5Si'0qųճ}EDD@4eؾ4i=>B;c&@@DHpSO\scX{#$Y>? c@T 9#i2%uK1{Kxsu#{2&]SN~({WB @Uq.# 6De,>@6Lt3l5YcF " 4Delq- ŷn>nnu;stN[i}?/D] Qs}88 cvçU#wOsGAǿ;ᨯ@Dg'<ݳdϳ\[Ǭ^1e_ "rQ{_JMK@+elG>7uӱ"rQ/3lҽ= n;n7Nd`ǒ7vA@==nm'""#GNAu|r]Pb-uv" ^!m#!8},Cswۉ(7 zW^y{ӊn7Q/{kRѦ^ ,Tn;Q/!=oʒ& y13n7Q/0컶W={o%|Qb@ U.4\GG|}{vQOg9cɫrbMDa@T&BQa΋|yyvQaco) !|4~ "UgV"ퟶc*pS~&1 *SREvޢqw8ZC;{oѿsq_n"*Deh!d"k~sh6cDT< ׃Y:!@p;1u;}ADa@T ȤB(r"S7/apriϗO^P tSF-I*l`jADa@Tf,C.J.Q7i\ǍRw {!"~Qe?ە^̾i‚X:"@%}'KUWܑQb@T&v9,Hu|t ^#VU *S ʄev_ m-K?F^ /DDe`R^HʹƵ6?C8@Tӊt_lݶ@]-k@7=D2(po@J-}Z_8\siʔ=)DWY;AK^7i7I-Uoloӷj(FDa\ZдȔ EGRko#T/L1 ;2o @W3uhUQ1 0Uc|krƌQfjyK{P1Q2(vw!*ءcɣvzznqﮭaM+"' R+fEMh[UEm)+!Div}7+UO] scu,MDeQ!{m_rM{O1VEDc@06msXr oX[xC8)6>#1 3$\⪹Sƛj;wQȮޱ&4};yezt!Qb@ x/lAUlReq 1 ]<Ɨ1=CH=á9k2)V!Do<]GrzϦP3}_+.WE~DTvo\ 2s-hI_d(NԌ{2(cX;E_>9yޭg5O1FZ q 1 [4j$˹gsUޚZ%2Dd:- *C |6eRvY*uP 4d=]"bL, 3>%u^W:@PD>>Ezi㖦!rϓ-@ >$0 *.6l_|63S}BN=Rr0 *C |2[[1w)?b_YV3 1 I(d\]:'t+q q3d]LjKES:v%0Qb@co?E~^p{SO6uk ەm!0\\ാ+oF¼&uFcdؤ@Tf'q{V2s @ T|0 *jRN%݂ԵNgƩpm6Ւ}J`2="K 1xdN8uͰm׾ 02aH$0 *!J`L)@ ݊Fq9F{wDT\ JF*s7E)@UfsŐh^[v[P#DeQpm>i}W- u5վXp1qh ]>@$" ҙT};ƾϾ;LgHd) a@T?;FŘ7 !Wd?}b~s:C[&Ԛmk]S滷S=F @^-S?ovZa_ Y*ՖK@ <dbX2S]Cf^X >imVwpB "I:HR_]燧2q:_SK|4lU8ɶ&*7 <4kk2^.  Q+S}̈Qm 9{ˁXmuF,8@Tfy(>IڶEeMkw | [ ߮"|%0Qa@hPx7u/@NV%ZC$}R xvi@8@Tfyd8 K+(@,Z7u17.ÎObp0 ?ot:/{KsuA6UPe@Tfy`oK>oƖMٛ=opbPv(`Qa@1N۽M>L;mR:ٟ|krǡSw@0 *G߶g% c9UB\^KOqCՈ]JD`@TdjH !)Pϝ6\f4]g?so|_lmTFBb0 *3 h3։iɥ7^b\' }pES } CEDEiNh5xo߻Obb z d@TfBNϴ&ސ_;mx .vۤ_nT̾$"o1 *Gߴ -´Ȓ{wB~~ k)eb- D"*DEݽGVz@r0tVڪkK|O!y}JDa@T۞4y=(~|-,,z(z*0(Xuc੷?0F{a+@9axE秮P&=6vϐ[fyR[>u>'Pt'IV/mTD_t~]Th@D9c@TM\FԎ+1 at|PMg2d :H{l.>n)-oIgy92@5}Lf:gIl]#T=fү;Y#X!`rӜ[jVH- y Hv3ᐁ?>u5k*sQ0 *n,%ӽ"ޝRWt}mp6%ӪVd2 *# aɹI./$S 1H5{~u`$L{mg.r(OÏn0*s @4՚Z_Kus]:tPB\ds$ "*' DN$}1B+@Io6XlASKsG/uL+BTF?,Q^T=(k~S&IXb_f`5hl$D4 6f/ \T|_:=jBLo PDy0 'w|,^ݛqG,Z(WZYT'L$*#؉r4|_rdwݛO)[b[=+!ѯVt\/V]{ ro8@:|h +>c(EXVEdE3,u",j (G*rMk~]&ogMbչnɚ 7p?&ڛ`71@{XVX TUCǗ~7Sۤ7:.QQi(N>-V׭2ͭ/N5|"S;N2ȥ D96 oŬ!Dj:fo&Oa"* \j00ƅ3+-i3@KMDb@vcv]>pŮW9?x!p0 FcpVQ-z$ڶikU8@Tfe2Mj̋ AʹNх{Qw" s(qDf~vE5cqx 0 ª6DTqS/mVsi۱Y3|]- *3  slѓ?7oYM޵D6[}ʤVSew'"/0 rj)/yD=IOy0O"Nd%*"*"DvQޞDE|һ36:WӦ`b@Tf{yW>8ig7zw6Bf"XDGmmη!G "[Rc8i}׹0,Qa@bo*[Twyy0|wAe%/"*"D) ,/xy_z/v5J2)0𴳈P)!i`!uZL Jp*+K D}RE[Z%3"*"D=ZPYSTdXJ b)G;Q_,=?v<]a ,-DT| $Pt3;P,-D D=5ߚ b%ozyPAQY~| ~(AQ1 d}H NSȮؽ.i/ *C :YbDzSN뛩6@t!bc@Ix"S-~7%h"t̚`RKxl7oVץ7"*.D=Q/o^L뛶q|eyJ!"o0 `XسDZ8ࠎOyMۃ}e[; .%:^@TѲڔ^a%)PDeTٱD۟x68oׇ"%~e{; v-Xsȷ^߂qY?)@_z""YN% _O![lY;D)?fWdU[{ OpL F:3qw| Aj[lG, {q\2 h8|2 *c O lU5^Wt~{ 󺿈; OSشg\G$c>󴾹P24UݠguHҪ͸]qdq-^yi"(i*E}'o6g}Zߤ̓Yw)"|!-wB_6@׈ S^OoeG_ 'fic沬[9%-" 곶=u O3KO* Ѩa7`lZ@26؏ pv:cluS!h=_7#+mQ1>K/~%ߝP\^$f+ R έ:v ~sjiq2*˥1"*6wZIgc'x}:lY Вh\FD,T\*K۝x N6Ӗ\K a:|*]穄lw !aX]{i~QSjqaeǔS~wT$yXc +aZ< P2 ( =wGQqe~W (ɻ8 KD,U/-f]c"*w GrP" i6CiAwMxP~!hzLGDv|wC3Nd!5$b@} bI}tu|Q~kED4|w3hmGBD@{^wPf7P6ەoXw=z{Z7P6kV4!WG0dk@Da@U}{'Q;A"DTz l|x߄U; fwE ||߄G=@!~tėGQm{;#.jI|Լ/8@Mc]kMx'"#D /[&]==&D r4섛Unu'UW4kV&3QEeo<wmR,pCDfd¨w, :w̝vJQQl{M AKtU<"s_tD"vµ0"TKzOӰHuͫs3yJeo*?p~ڀ !Îq 5,kwOXw1 #^Ulprm{@)x@p@ r:r:s;t98tx>z?s;Dr:r;uw?'x=x>y?{@BVu<~r:s;v=y>ɳy?z?{@Bu=s;t|ADz?{@|A~BDyMu[C}DMO‡LnjSʣjÇKg2i8f2u>v{>|ZCDEEŋPŅEi4f2g2x=}v=w>x>{@EEE„FƊPLJIˋKCҀ;i3f3g1x>w=x>y?{AV{>Ǝ9ŅKƒFFƋQąFƇHɉJыFh4g3f2k5ay>uw=x>y?{?@ ÄGƒEƋPŇHLjIʋJ΍Kb;i3_h4i4l6o3wIx>w=w>x>y?z@ăHƉI1ȋPąGƇHȉIɉJ͍LяM<ߞZi5j4j5l7n7YFs=v=w>x>y?y>{=c՘[ГVϒV͑UɋKɊH͌J؝bl6۠k6l6n8o9u9Nx=nw=w=x>y?z@{?׬eZHZZYbebpo7^m7m7n8o8q:s;u<ޱv=ݰv=vy?z?{?|>[ZZZXkSVKHn7ޤn7o8p9q:r:s;tx>z?z@|@rUU.]X[ZZc[UUk7m<o9p9q9r:s;t;ux>y?z@|@B>z^Q\Z[gUUVWs:&q:r;s;s;uy?z@|@CX]gXnSUUUVU;Xr.uy?{?|@̻@(kߙTߘRߘQޗQޗQޗQRz?1z@fz>{{Az~AY~BzAvpqsvxz}~l`_^^^^\kplb____^kklb`___hkklmI`__dkkkkkUߟ`a``kkkkklm`mkkkkkkll"ldrkkkkkkkkllkkklllklnekonnmndnFmmmemmmn#Gncbc oJm^D1Y |*IENDB`gammaray-2.11.3/ui/resources/gammaray/ui/dark/icons/grid-settings@2x.png000077500000000000000000000012321413310443000261270ustar00rootroot00000000000000PNG  IHDR szzsBIT|d pHYsetEXtSoftwarewww.inkscape.org<IDATXŗjAj}BrYu7"9" ՋoQ'Pgw`@Cr6{HgEa k`~t•Vt Q #`8d33$sݪ컙$E~$sNf&@u'2~lEN8+9ǜs5 /g,K גL&RoxGq֢[Vtཷ{oWA"ˇߒKMh[_J/km sત)}Nq`f3kfp`S !?h91ޅ:}Z٠@WUs@TSMktiWA-c7pO058n_gahFEQV6_9Ȁ$ԭޒspt?@GUjI<S`f f8p'|o)Hmf'R՜R?2? X *mAd?<-@Y'ܠ>JoJUc=*uIENDB`gammaray-2.11.3/ui/resources/gammaray/ui/dark/icons/grid-settings@3x.png000077500000000000000000000015331413310443000261340ustar00rootroot00000000000000PNG  IHDR00WsBIT|d pHYs,K,K=tEXtSoftwarewww.inkscape.org<IDATh͊A혎1Aę W/kο 7{]V@HS+?Ute M*_ߺoujanw=a1FM1Hc1vvA$O'yJR4!Y;Do;IҎE푈P)UbD`)"H %P_i*JD%i&iʦ~`'4O@|) w#/Wbw%-$"o~x< wf&(v}<ZyE=Qu󊆼"F0ٰ LJ5Lc0 ?O Z,P281Zb4y$ SZ=8NSDn#۠V1f% ӄC ]ao&,ɬN+SYؿ%` 0 !`{f֐ճi֏YvD9 RjŶf۬?v{AD3s`v/n{P._H){"Ҳ[~ =KIENDB`gammaray-2.11.3/ui/resources/gammaray/ui/dark/icons/lock-open@3x.png000066400000000000000000000016731413310443000252420ustar00rootroot00000000000000PNG  IHDR00WsBIT|d pHYs,K,K=tEXtSoftwarewww.inkscape.org<8IDAThke?FMb3cJ AQԣ(T<ԃxPK B#I qf6`/;.Ҳ36ݙnXs}}yy߅Q"E9""o"d}MD~RA0|28i`1!hŮl^o+g͖l6LZ>2 ض\d[99 }ϩ{;Z A5hYVD ~ّ'm"5'85,zw1뺇UW|qhj`2N9x/+R $V/nX^Z>R Qiy;{Oԕ%Hlnny AwOs"d @w BQ1O` ^&Fac1]{VE䬈,{w++ue`y޵;b9$2w0|ĶDDE_5ͿQh47>&!y^π'T NaxsVݾdYj]oۧy =KυiH+c #u766n s{@e`TFMe`TFMe`Ԕ1 SL/>nW$voI "WӒ Z|ɲpzzt:x? 333' U=~sssEQt 88\O̭;0LqyYwhe3{kX,{U} [yya+I#P!JdMҎ Z7h)X I(q tr3@'bs3XW4@#P%pDJ`YtIolnfǒ-_!8_J]!UE,x CzIENDB`gammaray-2.11.3/ui/resources/gammaray/ui/dark/icons/lock@3x.png000066400000000000000000000013511413310443000242740ustar00rootroot00000000000000PNG  IHDR00WsBIT|d pHYs,K,K=tEXtSoftwarewww.inkscape.org<fIDAThMkQsDbl-f&H,.ąh݊"*+nMq! EZiiB)uQ7 Mp՝9s'0@ qO%U"RUi|Bvӵhlt +;A o@ $.17`_.+"2/"5kk@RW0 `3m]w)vh1]=`ޅ8] ]o0C~苫q,2R*0ڿ$sfρ 7A o@ y*)L< 8t~qj9t^cX,κ: Xk1RP?\G\l67K=ૈ}Cɖ~n[};l ezFIENDB`gammaray-2.11.3/ui/resources/gammaray/ui/dark/icons/measure-pixels.png000077500000000000000000000003421413310443000257360ustar00rootroot00000000000000PNG  IHDRasBIT|d pHYs+tEXtSoftwarewww.inkscape.org<_IDAT8c`T@GGg+qir7! Pl6`* Ī# (Ȯk.pFS!yXIENDB`gammaray-2.11.3/ui/resources/gammaray/ui/dark/icons/measure-pixels@2x.png000077500000000000000000000004171413310443000263130ustar00rootroot00000000000000PNG  IHDR szzsBIT|d pHYsetEXtSoftwarewww.inkscape.org<IDATX E9z:#'( ;|#"7ڷJ]qg 0skqF<ܗ<' 1bo /`?}4@ui7֛Bh BGכ;n `ߵ&zIENDB`gammaray-2.11.3/ui/resources/gammaray/ui/dark/icons/measure-pixels@3x.png000077500000000000000000000005041413310443000263110ustar00rootroot00000000000000PNG  IHDR00WsBIT|d pHYs,K,K=tEXtSoftwarewww.inkscape.org<IDATh10 ϱѡw)N4TR5a & F(J"xۄI>V=Ƹ~Ywl|2ѢOހ;Bsת[kUoT&ӵZ lęQKD>zB$ Bg^?/ 4&d 42FЄ;8C!8?P?DIENDB`gammaray-2.11.3/ui/resources/gammaray/ui/dark/icons/move-preview.png000077500000000000000000000003601413310443000254200ustar00rootroot00000000000000PNG  IHDRasBIT|d pHYs+tEXtSoftwarewww.inkscape.org<mIDAT8œ Iu>ʟI/ض}wVNzX,uh4R:D"κז_#bF@Ӵ#˲Nᣕ^T*G|hY ">'d2y?SUF9wFcl47gdYŮuݎx8NN&]Ƙ / 0Lmj8nZWM<^VѸ^(ޖJc!Dg-_"jx}DZR.BP(f9gnZ{tAD P$\w_bauIENDB`gammaray-2.11.3/ui/resources/gammaray/ui/dark/icons/pick-color@2x.png000066400000000000000000000022051413310443000254040ustar00rootroot00000000000000PNG  IHDR szzsBIT|d pHYsetEXtSoftwarewww.inkscape.org<IDATX_h[e=h8*s :;vMډވtځ´b aĊ@Qn CPG]՚fݍ ]Y9I<^,-u,W<{xO9"i&HnaHpTt0'ybzz̹[+qssssp}ZuC*9.Hy,J: %%%ʓ{)055_E9Ա`0\/l~ǤPJUѱ`u(Lo`0\n9k׮ UJ$m)---ǵsJcаf$Iq~4g?NA[H^"9Mы/>DrdK=@r#߂'ihvvvΥE3PJ]En#G,wޏHHBzSb#$o ZSJvuu΅oZOg$c"Ncc555nLLL x&ch4 kn+"r<eLxᇏ;xېCRt%Deee;H%?e;$yW<$<6m۶vzܹvH|'Nx.D$9x`zDk=VYYygǎfMM #+mUTTjGRGnZ ;IL=?<<ٿ/6oÇ> ޽{~;֍NݹsO'H>b?;X$JKKO?Rԋȥe7bHZ?)..ܜ4h  B)h$ɬS>Y&Cٛ6mYQQ|__ՙPr>P0|+~\4^hH~ ľ}bG"e@yy8K0m|6l! @Ltv#ϓ|NDbYYYɜTw% ' tvOOOH럝ctOp\z5Ms4Mcvv O"; RL2|M4IN޽J_@eY9sLV$֋HdƍU l/%1#"[HVRRR.x rodggsEn~aa4uD2T!zN˲ ay Naa%QoxAD_>܇_666|:˗]PPpt>M"C"ane4$Jw$&+IJ3.*aj~x'ɏ]v=vZeYOYYv˲CCCo@Y5`?.]4B Z0gϞju"hޜΝ;Y&555Y<ɭPay2ӊSie pRZe=//ALiXDIH.,3"2cY֘ b0I' &IENDB`gammaray-2.11.3/ui/resources/gammaray/ui/dark/icons/pick-element.png000066400000000000000000000010301413310443000253400ustar00rootroot00000000000000PNG  IHDRabKGD pHYs;;̶tIME %DIDAT8˝?hZQƿB6Է=zDtOG)[ ͠hHDK]CҥCpp)RC@ Y bO~-~w.L(0(pc eq4HP<+cL_`p+20%"< >Bk\8.G"qFOβD@Dx.xk"ZKdgmfdkH3aǨxnsfIENDB`gammaray-2.11.3/ui/resources/gammaray/ui/dark/icons/pick-element@3x.png000066400000000000000000000033161413310443000257240ustar00rootroot00000000000000PNG  IHDR00WsBIT|d pHYs+tEXtSoftwarewww.inkscape.org<KIDAThYmLSW~s[Z( J+~̶sL&TBD1g4S3,O ? %!?dGtR>6p $b-{HB“4y=>{)-- /猱>"$I0|4ހR"u:"Bn!3`0\SYa²et(VkLccgppЛxqdd$sN1bCX3@IL&Yժ~&wH8j2W}}} q8[P݀`7onVLun#>(jYOZjZdY u.l~~iT[OsM&SCiii/>ezZq6%HDm665:d/_޿uqA vJ4Fh o>p{8#"ә e5\'fy(eY$''hl,//tJQ/IknTUUp3>>}l܀No߾322V+Qoʯ ͼK,E9?p8߄XX~( L!vr݌#G}X"ᜟYbŖ7nNL|ED###kϟ?߫ǂz}!/BLVMsm=yd$ctv2Qo\Y4ι@ R*++d5555 8mYti焜l̚X,ׯ3>8!!ahZQSq̚Ñ 6a cFGGOYԳbf-+3dhuĉ;.Eqd\3n@OA(#ݻdXv/*/((Xn D/6& TWWpF߹szp@;w9B"h}9ţ *W:ʆfs;p @2*ab@ "`<#""-D7fK6mtgݒ nH/6`tdeeyX*%"?n **YIIIojj}"lOCUIII'f/)Nf 33ѣ~F "렊ι^' !pSA ӸhX%ޙ\9ϴjL6IENDB`gammaray-2.11.3/ui/resources/gammaray/ui/dark/icons/redirect-input.png000077500000000000000000000005261413310443000257350ustar00rootroot00000000000000PNG  IHDRasBIT|d pHYs+tEXtSoftwarewww.inkscape.org<IDAT8͒10 ET`v2эpq(ecb t`0(-7c 3XTՓZ{;1f΍2hEDSz`$I_|<'(<IENDB`gammaray-2.11.3/ui/resources/gammaray/ui/dark/icons/redirect-input@2x.png000077500000000000000000000010451413310443000263040ustar00rootroot00000000000000PNG  IHDR szzsBIT|d pHYsetEXtSoftwarewww.inkscape.org<IDATX햿JAƿrI|CnRHZA;% )ԾH`1 66 ܱ 09sRefov*x)[i// ), 33(6WBᐴ܁YӺv8V}v؁ :AtKZNl[g${YkE@> v Jnxy^u]'f!Vl4^WO3d2]Y37\ Çv=Z @s]f~fjTE}8p ǒXqjc"&C"*D;`JNOE@}80vk:3w`Ľ3w 5[IENDB`gammaray-2.11.3/ui/resources/gammaray/ui/dark/icons/redirect-input@3x.png000077500000000000000000000014211413310443000263030ustar00rootroot00000000000000PNG  IHDR00WsBIT|d pHYs,K,K=tEXtSoftwarewww.inkscape.org<IDAThYAa$p`B,NAHh`iҦAiM $A.6˯Y oe"&d޷{ A|g;sC+,8.%NčqDUV$#r]l˲^ym P^TtZeP> X{&jvG@y )W_8!<NRyCJ@Tl۶6<*_RbٕMXy )ď q/՛IENDB`gammaray-2.11.3/ui/resources/gammaray/ui/dark/icons/server-decorations.png000077500000000000000000000012051413310443000266100ustar00rootroot00000000000000PNG  IHDRasBIT|d pHYs+tEXtSoftwarewww.inkscape.org<IDAT8Aqƿ̬I.A'("Ш`DHxnݿ KX 4M_jZ N>@DMATԈD"p8Q `x I_`<`BB!]$6L-ZT*a+ӹ=VLal|FaQ Bo~0KQ%3Y2B"ٖ0wO`&H)0!u2h1|[EQmXbbB,[ e{(J" Rʯ{rssVZ5 `O$% -[6(D4c2ؼ*11*@mHD(..VI `pp0P2+'̜8^ 'ѥÇBIZnwjyyP~{{{sAAACood;r&'n?u&)pKo·<Odll-[\ew9}t*++?ztfǏ LU  &;P(3n;/c3Ckkv3 jkkm33"ZbX&٣lڴAbi6 i־15vܹz&(2^VV毯/LOOO f"E]ouuuy<`p"|" E %KLh&Gi kv?͞evff~_!a4X@T ^.8 `, <3ց;;Ϗ8UIENDB`gammaray-2.11.3/ui/resources/gammaray/ui/dark/icons/server-decorations@3x.png000077500000000000000000000041471413310443000271730ustar00rootroot00000000000000PNG  IHDR00WsBIT|d pHYs,K,K=tEXtSoftwarewww.inkscape.org<IDAThYlS{;P4q^HFUIKI:Di#.56:Vm Udy ?BM-d-%`@$)?wIV#Yzs{ϻgRU(AW,>( |f&[S!DDF+Wl1So&zr'@)O Dy+>}67IJd̼.f[N$Mo僲)S@R!tBEEEp2 6DN3I~nWfTLSOZ2O$I(ÇH}!iZay!eMD*T( j ^o"D`…Ϸ0Ucg͚SUUukҥ,GoK]]]#'Ot\r%Bzw0ƸAdtvv"N&'.+bl:6\WWwsݺuٓYݻgB;u]{MiswE@UէdY]͚5?X,q|`8X{2 &s~7iB21gffڵknvv3li±0@jDB@svvڵ̨-"zYI40w|{=Ϗ$I2ߥK CD_cx +P(ozf8 xQh[J:`^S%hd &^Zhf$IMӣm#i`f@NN۷J$@ m#`.+BӴJM!2BDo(,}=5n'B Vu-'== D42s>0u2 ~V[[{5cI~v0 voDoqKB<dK'!HPQp:y˗/bvJqq f~dY^i'BD _xUUx}(b8EiH4OZח,آ'$0::ZLDriyU---WSS]XXg%N|lCCC2Dty1K'I)RSS3lT%d"xsQ7͋-+'u8s(O_8S'IRIzo72!-[|.(\p!GӴьxz]Ǐn;v]uDjuwwg,ˣ6m꛰]xq~WW;f~Mz$̃6lΝ;}&17!x`kkkUU٨ʲh4 !v\x#ĺc@YYp<9s؄ "Rn#77 }^"Ν;mmNʌ1_#^o_dIa$鍣G]d* X,K览T9pСJs(3>??*7dYQnƱDDD ! !^looVQQ1I+˖-BWEkXN&x" Z[[ f"L̼5 9D_Ç/9r}>_ $}QƍfeeUQ0Y@$k/ f'N8x`aY hjjjO]]v ---6l?`2Ly@%GG.^ӧ{Ξ=v188h P(Ejp8P?##cPivw6#H|070yK1{1s3sCf 3g;ɑ)Y2/jC3etg50Ǝa8&]p%U=2i?|YB|$"Iƚ.U1slv]D4_ jo$%dzkYVh4v!ȲJY"hY@ DTuSB$cͅa9HzNRۣzΡyz{{O$,mmm=עѨK㧗30^F" 4-9>>>8=='qrLDzڶBpeeJ6}ץR)ԑH䖦id|QӴW(f4M{O}>P}( SS'WkX,>p옜G766``ah!sγ\n?N,Km[bn ~cXv_o0QW 8;uKo\◐IENDB`gammaray-2.11.3/ui/resources/gammaray/ui/dark/icons/visualize-batches@2x.png000077500000000000000000000032561413310443000267760ustar00rootroot00000000000000PNG  IHDR szzsBIT|d pHYsetEXtSoftwarewww.inkscape.org<+IDATXV]lS~slqHN榱 ! lHE&H]hjAUDE Lha(SvQ 7 M"iZEmCa!! mY8?''f i |9K,p£ XL~ 4aAni~8A')`0M,˩<_ru ˋ4l1 BsXjb`1 #9Qb4@Dmr>(OG06 o Ø$+0Q0 fҥKũ)b=R]A`Ν;-NCL<`K%%%iڭVu`$os+\p/_<344+bc-h'@0%UUCЌ{+|+J(vEQaf6v]ZZ('z%,--]u-L&ڵkEظqMӺ:uJ 5 'h􋲲2n!рiG",5^ ">~HGGGѣGn9_= 5MiӦ[~FDݹ$h|eeenp@=zf$Izb̜bfjnn~qKYY5d (Wk۩Du(}l"KWW]4o_B~zOOoIX.B77yf'ߺu nɓ'1<<~DA:%;3>8euϞ=͞,[L;ff|Œ3Ν{.o...N$c߻\6_ {!]=3ۙ&?0s+3dfpXܕO| ̍_5kpIENDB`gammaray-2.11.3/ui/resources/gammaray/ui/dark/icons/visualize-batches@3x.png000077500000000000000000000051471413310443000270000ustar00rootroot00000000000000PNG  IHDR00WsBIT|d pHYs,K,K=tEXtSoftwarewww.inkscape.org< IDAThY}PTca+ *o]cCƚDҀؙd&iTc&h1Nj NڌjŤhaR !]P@ee޽}fW;wϽf_kY!Tia|3 mˬ'Bضa A39V2 "cRODa" bwblv4z \J9gg"dɲ٬'0cYA"YYO`ƲPtoi3~om[$d!"s!Z=xrr2ɲL~_h4@ яi&"WVT+vyb5DE@9A1f[Ƙ'RxĄ`SS';wbzXQ7fFEN"˲oΜ9WafA$dsӉ(|ez84ZZZZñ%I ƫT* tV*ӎ`085Xpa(kcN?B `^跂sI.pnv?Vt" ݃dl`0$ %y^EQe{xs#80::'1ى$"cxh߅ڝNG8Xbąfի$%===c+q?鬪?v옪'Wv~:rrri /)X,VEJ(k֬ۼy]!='vj+sCZ"@Dm#X`QŽn5UV߹s=:.Y322vvv^ٸq㗲,CR (u:]V5jԡM65555 sQmpɺl6cmιz`EEEܲe˖*}~.^x޺4޶m۲WݛZQjjj}vu  nOfeWOHH譩Yd4`hhh ~ EqܹsA, Ve=cL_\AAP˲6*$!xgzׯ_  mh&iZeʲ6u@! '^xTWW 塸xrc["9_^^T__o=yusAU*ϧ#OYZZz[%Z=oK,IX%:NDQg}6Krʤ"%|7e… jDmF ر "bSu| !i4jAI$*F;.Ka؁h)lZojvdttUUU+ |ǎRtM~@KJJ:nx9&ۭ9}[[[["Yt2d)))Ǐ_x<ע566FĔ$) 0Vh4֦EC@.++Q9({Ξ=s墢ux2jSN"&& Mj͜tCen!">+++:d2)[@ J$I:0Lh5kָzMnnn$IZֿqη)l7<@#,}%˲<真5ͮ~Ί ,˺t/;ٿne9{Ƿ?#=Xh kqᜫ< `O>Q__rn<Ze|>N Z-3 rLLp||ŋM6*‰SxcUMɈdp9VH~t"QV)&a| /DK0& <cD,L;\Џ' @ $oyJ^IENDB`gammaray-2.11.3/ui/resources/gammaray/ui/dark/icons/visualize-changes.png000077500000000000000000000014111413310443000264120ustar00rootroot00000000000000PNG  IHDRasBIT|d pHYs+tEXtSoftwarewww.inkscape.org<IDAT8Ohqǿ?u N2E!b A],:l+]cX.N[03t#p3Q&+^tINFxxu9@Dv=,VT^ CƘ1Y'5r.u:RԏL&s#4$yjZhqqQj f]LTFoAk,#ȽP(Tڊ$Of=444L& dAhQjޯVrq\`04t0iR4`xxXY(SL&S ۫yu"`ͦRjjj,I;znP(Z8k&&&zeY,Kaff1v^{M&ӷFyPTyn "b<lTTU']ai Ż1jZEDy1mRV{d>vrE2bX3kNEl|__[ʊ4::s_L&Klcppp~ED/~Mͅ%IZ%?;Q C17{IENDB`gammaray-2.11.3/ui/resources/gammaray/ui/dark/icons/visualize-changes@2x.png000077500000000000000000000037571413310443000270030ustar00rootroot00000000000000PNG  IHDR szzsBIT|d pHYsetEXtSoftwarewww.inkscape.org<lIDATXV]L~70 02  , 8aX&XHzѨmB/,5rVTC&5`l![G .ࢰ[gB ]lc7}.|9Zw)d0d"X֯߿YYY٢(ۈhN/FFFb֬YAHp>_i +++1/BuιiXTUMeD3~m!I1"z#"b19\_t (1z\IQIUU=ڵkɜϟ͵ZVEUըϟ?1KZn]oyyfSvwMTA?22Ǘ ]6s8KMHH8˥k555 0jkkSV+V>Ɏ;?tАnw/NWӴo)DmttS1F&&&ne9.nǏ >سg1gmmmrbbbh xҒ}IIIY7==Y__ߕ듓,III`ggg2ID]EEEfϞ=ɽ{bh_1Ʋ^fS/_9005`TWW5;;C5tR>|ݕ?ߴiPWW/33sDdl˕+WbzJ" 7ncPFFFWqq񓡡o]]]>)EEa0111Z$"u"2goݺB^e1@Wں ܼyKEQF`ƍݡ Kv;ǟ9sFX,Zwwwi*a-[455 P$q,.!~ݒVu]O'"$! %=0 ëz ADiveGƔ$F_9.^h߿?j%.ؘv9ƘFBDRpA&bS#m .I^ immJKKiHd$sMDEpN4BaιUU8%%%[n]!#GH۷owɓ' hԩU4KUU;P(ߟ*4699il;===EӴ"]s5M4ɓ'$֋D$G#<0dO-իWRRR;vlK0xCCl{{1$"5//Acc#x<"EqcL{- "@1><`P(T0fLQf f<b|E񗌱9`瘈V ?7 p$1MD`16D| 2=ENN* !",[k` chǏK/y\^,;AD?X,*Y^\x]_Q䖖aum6077m 躮Jha~~[mmm|۶m_($իwnK~HIIy_Uմ=EEEo޼Q ೳNsvzzeO@4 ͝;w~|=P蓸8f20LAL… Ν(tJEQc-ι(vVH.//xĉx]< $Ha?mp:N*,, 14 "9 գ~o Gā7133Smmm0 c h;iQ#9s2Q'UO__Q+O. IENDB`gammaray-2.11.3/ui/resources/gammaray/ui/dark/icons/visualize-changes@3x.png000077500000000000000000000066021413310443000267740ustar00rootroot00000000000000PNG  IHDR00WsBIT|d pHYs,K,K=tEXtSoftwarewww.inkscape.org< IDAThXkpUU>K\BܜhB!zn\EQ62s(q8rpظuV^z=ۭ !0:GW||t4 ^U] s]&aXʣDPUU%ʒcl6[#GJMӬlٲPQQ,1El6%K֯YJ***.,_G2vMW>hjjGD c.z 'XBD{Ģ47^EzviΝ`Æ b/NNNNOVVvСϜ9SLDUV)liQr߂(愄R޸q#q9sE1ڵkѕܼywphhHs\rժU}o悳g*b v/)vmogg睡`BB-[`޽(%%%,Y21|]]]W}>_B̜93%55Gv=k֬q}}b!3577Q6dgg80 nnnvю`0(cBoFݮK1󉪪Hгnݺرc{Gŋ.]f?W_}H$~5* ;;{BxLIzܹ)!flݺ5@`O0 O:afn^z߱Mi2wE`0h^Y7ntE?D@$1b;6J#gZ,IY,YfK.02S٧{B+@J9W^M"c8yf BwфIozD"gM6a۶m3}lqu~ ɆaN03 I)T[[o6v ( B륔3|$I p3sݻ )DgXp8 gF]]chh-[JKKzz(ʏD"x{0r曆ܧ~"šPo#y` IDVfڵkW#<#fPe%:n۶mle[z}vg 6"R^f] .ǥޮu]afEJhDQTT䬪rUVV^/@ND3y3s.+o`EQQ]F!@+qi3 LDއz֭[ Phۧ!Dԛ/ !~O>kѝ;w+״cǎEV{;aGI86. 6߿F\R('L'8)ġׯ.3: D)/xol~M3@!_D S;HKJJ񩧞/[-˄nbK|wS  #`$:(0> G7YHzMQs3fTUU+V-[>۰a%:")&,MS7***~q֭?3ooooo>~wn0~Gܹs'?njô  @VVoƍM>`(''|&G ..N\rÇ͛kUUxR3m)%@bbbo[^'|XUյq6Kzڝ;wcnU,\0СCƨiSU?]I/8b)e 3:thv3L.EQfUWW߸p688h0pd8~ p\%R'Ο?'.++ 9DdJHHpԈnϗ_`Err}}}~"R`0Gi_r%-6>#hkkk^]0Zǻځ?dwyZee}IENDB`gammaray-2.11.3/ui/resources/gammaray/ui/dark/icons/visualize-clipping@2x.png000077500000000000000000000030021413310443000271570ustar00rootroot00000000000000PNG  IHDR szzsBIT|d pHYsetEXtSoftwarewww.inkscape.org<IDATXV]lTU?ӢφB.Y BKB 6 b҇D <Řh HD`h0HJi)XҔBKmv)t|`,H~M3w;3gf V9z.QD! <*Dl^gs-zjfoFG#@BlFؿ8Zre#_@$ױb4m8R~S>Qp ;;x,|GD @}R!/ `kiڳR1F5p "4OљmhhE ,Чr݋ȔR۷og Y\8Ξ{&Y,3J)p8 !fHzc^/R$꥔&MTĉΎUV}݄ːd V,Bt4M$|u !jA!D${hnnX' M>ݽ|g.\89)))@yرV5v'TRRR_UUT$ !Ļ$1o޼ʂ n:wܸqmƍΟ?oRj}xҥCKxbo>JJSǏ='5bcb KuHHѣ8JsNQ lotQJ5jiiqH~I23$'6{Z$V:7H)ϝ;7$EJϖ-[G+**a& )Z?.X#߳fJ޽{]wܹn%cHs𿶑t~M GFqD"$%%EO'o@ׇt`Ϟ=o֎x-LzG[[[  ]re )| 233@ҥK;׮];1B 6QJ޲eK-!D)ӃҺn޼'dee]#9t0IoYf1%j ܷo_g2d26f !6=ɥ>Gzzz.ֶQA8d4 @ZZZ^^aѢEGB/fO2;$qE6ȡ$i0[@*q|7h+ B{i =IENDB`gammaray-2.11.3/ui/resources/gammaray/ui/dark/icons/visualize-clipping@3x.png000077500000000000000000000045031413310443000271670ustar00rootroot00000000000000PNG  IHDR00WsBIT|d pHYs,K,K=tEXtSoftwarewww.inkscape.org<IDAThX}LTf>+3,K7J45 f+_ښ(I]?ڬImcҨ1EeIHuK nښvFYFqy1Hg`k/y}s{<`XTl(1t/x٠HOrG#VӋ' 覢>PUy~3s"dVk>vXy,`Æ co+M%UUu4p@kBRPXX8u6/W~^yt JY< +2SQe"RYEQ (&3L Ă ,EQ|(ۓoaN̙̜Θ6TU&~}Kn'~1==]G $2.m0$I=zԘUT IRE,̜(h0 IR @}Wr6n(޹s'5S^͛'zfvy[ht:HKK%ñzвT׋e˖#X35L#( p H0@V ?ttt|'LفVݻyާQC~ꪩիWg(ijg)r;w Ļw@ ݛ[YY5UUU}>C[ QG444T'jK{__x1 <:yJ$I$#\RWZu{׮]oL*w>y@xa͛;;;;ft]6VXyy`0Yd2>hѢQ*#.hݍ1 IXss;vЅBH5?u:# ;@Dq\f[VV֬0%=Gzgr@tu?000m`_I<|NAAm?&K+++o!O7n}"f^fvvvlڴ 7TBFx6k[n.((5w~O-:`ŋ(9?Se&S__#MDn"o0(++K ng $1vAN4m5G)Ȳ0ݺu+W:4G0*e"{D9Ѽq^JJJh~Qi۶m UUU3.**J˞s@2$  wybz\IIIא*F&fݳg!;zq@]rv9um6dFJ'OgϞ}[ZZi7+ԴJ{q%T˻}><7|EEEm<{K.Mֵ>ci47tuuŴir3ZdffΚԁH}:xQsdH%ϙFܿEuuP(ch4ڶLqCf["JdO>]`Zn~u~~\)/H$IK-rrr@nh47%%%ْ$ml̼C#o6GO8kZg8bf3NEQ?ӋG͔ey||̽e`͚5 >(q2F}cǎ}ӓZMcɒ%AFFF $#| ^#G766dAD\ZZzoEv]?dEsTU ]tǏwuwwg+bYYYƾPCDl40;{A[߯W`0h4ʂ AMKKfoϟȶe ?5#僈_k:&i#ykrrryq'=l$4罽@ *]?rv:r45u@0Ikaa?eRV*(0]pniԓ8 8|0ྈ}NIENDB`gammaray-2.11.3/ui/resources/gammaray/ui/dark/icons/visualize-overdraw@2x.png000077500000000000000000000031651413310443000272150ustar00rootroot00000000000000PNG  IHDR szzsBIT|d pHYsetEXtSoftwarewww.inkscape.org<IDATXolTUƟ ef;i;  m1D>t*qYB4Cmjք[bBL٠슔k #RRt:2L}e0_|sswϹ9m8|ɴSDVj`2 c0^tPUz<+fΜ>>}Zcc#ĠatwwN*u:ɳ$I~Op˖-4tFڊ5M}I2FYՐH4-t:w @Ӵ5MFM$&ґr\kv*])ODi"@0liPU5g޼y>J,"1Y(J tСC-RD>x<Dx|y׮]6Νd"|kX](?8Rw涶K)o0lrxQQ˗/Pŋ֬Y3[Q'WSID>-[$LS;C¬{ 77rmm-B8Ȼht=,qH۷Rooo |cgVǹ@EEaL&D30"D ϧB7%Kĉyyy=VT[[EQς^upoHqq"R^z[nc,I8p gϞ 6~xfM0kڵ=`0H>_TTݻwwo߾p~@pn?',X|Dt?Iʁ7(t7$w/ݸqcӃ#|Le0 VVVZV+EoS&"|H䛓'O~J$YCCCׯ_7flKf^z+Q`@6=,H$eHJ9-_$גW@O|#"7S 4B#SL8IENDB`gammaray-2.11.3/ui/resources/gammaray/ui/dark/icons/visualize-overdraw@3x.png000077500000000000000000000052311413310443000272120ustar00rootroot00000000000000PNG  IHDR00WsBIT|d pHYs,K,K=tEXtSoftwarewww.inkscape.org< IDAThY{PT]^1JH^佗VNUTh4d2dmHdtcL:ʹRL8TZڊi0ZIR5Va!.>eM '9w_@M/ PSy\df߾Qd—HOO'..ق z "DQ|>x<RUU4MctG޻23370q)))Cˑ:J عsr?9ΚG=,?)IҐ$IC6m*G\YY',˗233,6Y/J4}a3S|73+\~xC$eY)H,I8DFc|J-I;##c΍faD䯫p<"G4e"qqqSh/<^ "O C׋~ 1"RDjpS%K#]]Dqgg礳W R[[ۧf[l "TNw?3"O DžXbv@&f^(sMMM͖̼"-93L+:NPUd0!󛮮@ZZEOt:qJ @b[2ZbŴLwBv:f]oo qa2}69$I[":u/NweժUׯ/JNNNv8Ο?߽uV}2ѮN:*z/0s(?𣠈lٲ6o|d: <C F)|ҥ֭kkkkc&Ç'@0=AVJ=hZd2 &Ν;79# R(kMMMTUUGFFCxp\)d_p~oZ&''ijj*4͡@ ŔE"u;"z"yMMӦ0ۏ?(uS׮]KL4Aѡ@LD^ Xl#DŽ(6f~VӴ "03t: :Eql,]"zWTT4!i:ya9N" Re 3o /=23KZ^oҼy󎆂ZU&zfe"/"Ў-jbnn<-"P!?4.B8]ѧ i(^Veʎ;2Az!/WTTd ϜDD7&5k4f#r\QeeA ZRR++++9R'/݊`,QzTyyiiS>/ DQwIII7L%-[s$,YSSdG9)Fh8}wϞ=&cill\{8i;vؘ{<1UUUݻwb}ni4#(m+0űY|^{7պj)IRRReeeFbժUL&MNNjQBM&ӕz+Eox=*FFC>f$ʕ+&jfh4^:zh3"}ϟf۶mwO2%@Q-ep)} ݵ޽z' kfZmܸq(,u1S f4"pPUu@}}ȑ#UU@ڝx(#v`*q~ `#7hݭ].e``r*^h4ADQԒC fΜW\\@gdyH*3of3bǤhK|Zm"Pz":;5; I H{*) _hC8 N"ew7tIENDB`gammaray-2.11.3/ui/resources/gammaray/ui/dark/icons/visualize-traces.png000077500000000000000000000011521413310443000262650ustar00rootroot00000000000000PNG  IHDRasBIT|d pHYs+tEXtSoftwarewww.inkscape.org<IDAT8R=a}E+d-,t|%$!ba-D,)A,6+R)!Br$ "( 66k.c9Rܡp73#0z8:=e2|`hhL~b)}>R$"~Z*cT~Gzܶ ῧ"BIIIdz6(`!|72W}}ܹU@ܲccc `yʫ03Ѳ{Vollr83^C`KDdBĥ۷ov\ +h4zٳιUX@x~?~`uuu?222޹sAJyDJy;yf̅1̜̕|/33OLL"a/}CCx<,u:4:窫SqFf.]9zh{&n99/HܛDD?B͐)}ݎׯ m6ۗ`0SXXRa D__S !`0gk'&&1 ӧO?0Rmmm-K#wJs+z^7d#p:f)BWÉyU̟E"Mͽ+1-32󷭭MX2e߷oODv,fZVVVرcZUU X >o6fYZ__?\{Dт`7|"Zxv,u}%Fc\Ӵd2.**[ΰm۶"ӹYh"O͞3忎+ 3ŏ"96`5x /3" z|3wuXfbMIENDB`gammaray-2.11.3/ui/resources/gammaray/ui/dark/icons/visualize-traces@3x.png000077500000000000000000000037541413310443000266520ustar00rootroot00000000000000PNG  IHDR00WsBIT|d pHYs,K,K=tEXtSoftwarewww.inkscape.org<iIDAThmLayºX3PMZ ݸBti6q5uFCc6&Mi5akbHmX[X 03UF 6 ( ~`LafX)s9;n$===[5g6NtM%" ƍKjkkDO0 <`._|yqqq={>)((0TJ}~aǑ >` +Vmٲeڒi2Ԑ}2 ~z͡gN\|9NL9Ώc6 @~~l6 ú񚚚/X,obbɓommKzq{n "L&߾}lDqCk>fN|cMMMWN>mF>dt]2B۝5xԩ999ֈip(qD @U %lݹs睑I;N&?شiSsssiДfT7Ixx jkk/\p:z v{Ldo?~|ix7?Q@]]owuuΉǀ^zܹs_PJݎ域o4mlV=q';K`0ypm^ҥKe˖-No+RD6t0ncHNٶm0DӴ? {o(ViC]gL)^~eZرc(l2 c'$֭{I)e2Lގ Tvmy+0ryDr}8[N {*:)b17_)"W巵766 ,((FOv⍌%Vu0N% "OJ.6”𸪪*0E)eRJ}#VL8VTTV{sOES|iV@].ד J)s\Gd2M.**磔z1v{/^8_pav{Cå۷o[L&ԑ#G}TVV޺ukĉKD]]SN/fl ZZZÄ"|¡W㺺[I$//--}ru]?zdƍbtH&^#lK^"\jooϊ0"7 ܽ{a$zƌ?lyyy/mJט\ZCgΜ)OKK35qSH)5 "L#MMM]L4T9Pzw#eeeW$~cccAH PC8 f|L 5P---ƣGc{ORk]UUT fNDqݝaFឲX,}555]vmJOOL۫vܺۙ>P3+W]~x M|@ GӴ@RRdZZ7 fffv)//O/++KMM-?^۲#"" "/ ,DlgAwwE$3yL[ d/1#`D-IENDB`gammaray-2.11.3/ui/resources/gammaray/ui/dark/icons/zoom-in.png000077500000000000000000000011011413310443000243550ustar00rootroot00000000000000PNG  IHDRasBIT|d pHYs+tEXtSoftwarewww.inkscape.org<IDAT8R=kA}#ٹNPD6o:\R0&$WAq)pFC |&q+"N@)":qC73%pi!+^X\/NqvR/oB)u8Mݤ]׽舙3(\׽NT !vwcNJyVz-h$JO옜i4-͚8,\-he) lDDijlh`U.{ DID٥hDRgmHd2yy^ 0f̫p%0sJk}ĝBJr07~c0&sF& eWIENDB`gammaray-2.11.3/ui/resources/gammaray/ui/dark/icons/zoom-in@2x.png000077500000000000000000000023041413310443000247350ustar00rootroot00000000000000PNG  IHDR szzsBIT|d pHYsetEXtSoftwarewww.inkscape.org<AIDATX_L[eƟ B\p= Є $D/p1cb"4.1/\Bf-f1& li 0K0z/Aݍ>ww7{wu݀GRԜlbbܿNȃ"`FD~%#9 @Ӵ"2 "?x6 Mp+mYz.4|>t]SD۱c `0(_`t޽/<399T.Vɏjmm&$'0 a\@HAnKɺ(CCCq"2c :RTTsED&πxaeTҥM7? ݋zܴ",c<699`S~~z ?m'!am2<da+O>iiișZ8p`6??˗C*HfiM,--}j,:<ɗnw-Lt"B[$ HuwwgƖU5 cٳ ӺϹ\]V~ҐzGG'h86$]Dd(1Yr/ʒdJk9!x.`bc$nITQ yO:jSSS;VXXX=::z~+ҕx 8nw(vH̯_ ᒒ4PpD2ɜsJI(Uldt5vx@w_I?թT* nl$ac(KLy&5X,e$iw"y===|nۅ 'WD{+>$۞m,mіIENDB`gammaray-2.11.3/ui/resources/gammaray/ui/dark/icons/zoom-in@3x.png000077500000000000000000000037051413310443000247440ustar00rootroot00000000000000PNG  IHDR00WsBIT|d pHYs,K,K=tEXtSoftwarewww.inkscape.org<BIDATh[lgv}i! $ffwŘBF.4H"Wꅊ>$@CJԴ)I* QZ%i 䁦4#]$@8kX]o 4/3||;_ AWauVUyZZ@8d2@#"1`0i,Muܶqf2T^DH&0 ~ 1::`LRU`X,,5qUEUc".L^`PFv؜NȨmKTu>D"40P[[;쀈+ Ia(ϑ_s2"r:JU}EU{UuE1U;CPeYW_Xu4t4{Gn˲XoYURh4iEFD"[UwgQDOBFqZ[ZZEq?k׮=+"~K["}ǻ㲈)urm~6{)S\ם7i,ʲs"rz-o6{Ͷd׎R?yyض0FE0lU0Ms`PQQqADb,MYƋDI@o'"=;v8 @ #"@:>^HR7/'.Nku 03~32_L<C!w]zLm@DDoyȑRK^9vXAyMWDj `08a_{***L>Z0TfxҤIC~Z-Z<\"R/"sn\brň{3iS,XĄ \ajOϊjժˀ3qP謈8prm;bƼp8|X?p8ev8Twߺ˲bmbu 9slٲTuiZZZjjj `ߤ1y`Wgg1y3"4 mhyj]N&Ӻ'E#V=444Ӗe&"8`/577jǶmGr:ԫrcppŶL0=zmsyI;w^5 x[ a{ ɖRVFè8I^I)\sW$wE`Y\R2@`Y֭1vDĆ, 61߂mۿRԫħBQ)ֶퟑF])ƪd/~ߋ c>oJfRSJ*ʱ y[K"&^Tzbyd2?jr2Hi6A<Al{8pV{U7Vp0ܛ I2#W%bIENDB`gammaray-2.11.3/ui/resources/gammaray/ui/dark/icons/zoom-out@2x.png000077500000000000000000000022331413310443000251370ustar00rootroot00000000000000PNG  IHDR szzsBIT|d pHYsetEXtSoftwarewww.inkscape.org<IDATX]HduƟhE[VKϬͶ kA&udFAHhE-H,HnHTKD,ظv8Ӆ&l*[:B5u9cMߔǼ^oi۫iǪbxd 744\пWodeelu|YYYx{ԩ/l}n;t!9w7|C!BRם"]=IENDB`gammaray-2.11.3/ui/resources/gammaray/ui/dark/icons/zoom-out@3x.png000077500000000000000000000036321413310443000251440ustar00rootroot00000000000000PNG  IHDR00WsBIT|d pHYs,K,K=tEXtSoftwarewww.inkscape.org<IDATh[l SCPI,sQH%RJK*FқR*D!JhBEҴpŻ4 !&KE'兟/;9so.w^iCCÃaԺ[!"U]c>l6HU63 ,&;MdٗTNDșl6Q0:0kdd0 *"3X; `Y^qUEUc"l2<^`PFn\ΉȈmUyU} %yfY`p]4= < < 4tQMMM"r.N/0Ms:x|=X #e*0ρׁPEu\d2bBx 0M d!`{Dx": n,>K&Lp:y 8>u*//رcG/E"j crS/ .NOzwwp8IJ8Tuڪ6GqRe]CXu4L4ݕ?U!UMEWLL766M<ئifVZuJUOjoAUWk a_gYp*Rm@UuY58>ڿu[*++uuu/&rl~>wiӦ]miʲk"rn m{ܻKl^~_\ϟMӼi.!X肪6TTT\hm,z"7DI@oODzs###_d21X,t:uQ0@ СCCXFGG50.xi_p -ZBgg'KVXqɓ|˖-+"4X,`08yfO{***Ϝ9SU6>Z[SS34eʔA/ N!ҥK/ݻwn}}}k*z"RWQ0M`ŋȝ'GU.q2n4#m/]tTU=-IqD$ _SScoy4f hoo7NzADV8dd29㠈|⛰Ti,+םD"vCC|ݻwi۶IW5m"a?q?&j<_ ܗ/Oٿ۪z0G? o oD"D)U5T5zzzS__  <T;>jkk{wªZ D_766v˄0nF? ժ:'oxT ]]]ejkkCoZ`L&,DDf00%%%?FBom1`!&|O? WY;'l6;MD]ZZA,̇,{r5.۶[ZZn߾B<jS~fU} &?;E$s-.w-,* IENDB`gammaray-2.11.3/ui/resources/gammaray/ui/dark/pixmaps/000077500000000000000000000000001413310443000226315ustar00rootroot00000000000000gammaray-2.11.3/ui/resources/gammaray/ui/dark/pixmaps/watermark.png000066400000000000000000000556131413310443000253460ustar00rootroot00000000000000PNG  IHDR*sBIT|d pHYsY\tEXtSoftwarewww.inkscape.org< IDATxy|T?̞df/X@@ J .%.$+muբU{.?ZAkm+mjH*KJd!ɜsg~Lf9gl3y^y$9<>0(((((r8 Ä(,  <lƲl@nnnf>|9r DD ÄEQ%ǸE1 bܢEo\;w{{{]DD ÌMR\} peJI:3}۷8rMd)`2D6R8#,Y_"I(3DD$PhԨk ze骯w8psaOoo/?888NDmf<78 Uu #466:>okk'3]DdD!І8\.3޽{n0̸("  wDd-[2VXΆB!$IDG3D/DI9x qpeǾ}K,)8 "Fn4gJ# .V`裏zg̘Qq\9eED"*'%XxMz^8\`QWWWPUUU²l D6!"REϋ \/? Z[[ DQd$I"D4&%BRۣDO&Gβ2Mƣ>'>+If( %('e9ʌ: D<_裏z,AUTTrW.B W7`H)h,o8Z,Uy%),Pv|lc۶mEx 3,=Q )KRC*"x9ڎrRqrNAHeYeyBdn$},:1@b jKʲv!2gfֈ6dI C [ kJ< }@AJD,k `h(Y @NR^R܅e8}I $%XR9mAD# [ґqaIY0, "dŝMYJ'"B0nO"ӋBREx!k/VI 0(X!)OZ)MA$ $Q%ajT9)p V_̠ĵ^AJ)*)qbh< 6PLQ7d_ok4,)FO(<+5 }T "c1}|R<7|PB)#c?HYjFijtvJ H\?XA$J>R|8&Q{.OJ<nʟj>RZɖrp ('mo(7H 0R-Ke@%?f%[ÁF?#%Rx)g4sl B&l#q5+j?L x(cE& B=1 2ҷ!SJ\GÙd~Rv*œlY&8 H`gTm#>R;'$>?5ۢ5p&$d3Wy0 8JIÁ gBrNc_xg8F9E;fRp&NT xp`\g)Kt̓MK3#&whPcDOB CRvYH=ȐҽWK]ECRuU{8ÙT`(qlg8}8@ R̗)%/ J1Y %b8JA"Y}Jijp&r1ۈrR3K%a @fn');dp!eFl3HxtH!d+?Ś({oV>5Ku8R(6l?35Rz8 ¤4As:>9 @J\r$pENq*+X )oI>cWap̿GEۡ'HOz*#3 0`GARp(ݼ>b#m~Rm"; ra7YB P;"6-hPs@O@M}̗E&a& TNڷ=xrē! M$P3W'U*+8|IJx/ Lp80 "-h? H ۟kDkψElusKy Ì+//1c׾5.ӝq5662[lqݻ344Ća'(aRȔQHOܘ#%`dUe."<_q\ 555mۜpaǶmۜ<ϗro?byI9-V"+riGDq1`pYdW H&")Oʉ"e1îx%,M{8&EiiК5kS*--XIHi9r[k݂#%dck/)c+yKKK׬Y#_~JhllT@v DD P("mdHk1Y))#+HGl ^Rtivޝ%QQQpR<@+)wheϛp)7Y]φ3fpjO(V4T=AccmhOOO(+-- "ᬾ&(a""b}.pnRs:h;q#/kkk3ȅ새aIye/ -7)=6&+$e{c+;JȎq\qqqp[[[@J " >7uB)#%[DzIy2Í948x^x'Rzʕ죱\,؇$)S5M4Z9)Kr9yŊQ4c :t- E<&zIB+%eՌ7RZY(ujfN>dwߵcB@/_ijj R˹;NRL|bRvY.aZd8+_{6f.uv׬Y30={FFF0T RBݿ7ZM~R2EˌaY̭z˕RIg>\NRz4uZf`)ʑA(wv^@w2z'U2,Wi.&<948e+;gA0d]\QK)ÜDO,9y䑉nzCbQeg~R2M9#\`D̝;w'6HA(E WLҍBz-&e7o^Cz_UUu֤š\ Dٝ}pW.r'ٷ%5d]l!%٭ Nevء: ̵Gcc#S___F92dlp;9r6燧C,VGYTt0Tv_͒ی-GVUU jZ ʾGaaaQ( != )5lӔnۜΤEaYBK]C\ ٖ}DG)O_)7r %iJ맏OLGojjtթ>8h头z.Rhҧ)[rsKʲNRy428<SMMM_$yeO  {{{] I*E\wkLZ8X߮)opz V$Iu\ЬORCq۷/(}׻;G@?)[\1ۈrrh1^nS,N`X$) u}D:U;HKgQ)=bwyn%RNyV d͚5-[Ҏ}fGPw=y %br 'RH2ZGF,d-x,_Ē +s(R;"ejr/ʘq Gjv <ϗF6E`{.}w|Fms+R86b!H.#=Pv6 xij@ Hl:o#x ڵ˰->RCL R:xDxdm8Hys'}Ih@!Hr8jWSSJvJ\oz۷;yܽN}00/z555_F!X@P{d&wVl'd/RX߾} ᰃa(슈މ&M]DyCҡ5kׯ岌Us{ΝaUIXcIJWhI!UpUTT'w5[ruױ@1@~r~YDsbf|ժU-[n?$u#O{?rReSD#~ײdK.$xItTcQw{z)3gک%D3gHw #@7fiIHZJK/:P(t8p8x믿6lffG!)KP]aSq7W>Kqۢ#u<766$uB! Jt4 ~͛3 35L(RjaRg覄Vo<ϗ555d@gC٦Gcc#S__bb~HyM.`8R; r#NU__ޱcG@=7 X ̝;޲5<A(f .G{[$:ߦ%Jy,[7osNA#=Ah@县AP@S=#_xq6x$_U6UY<_OxafΜ)|DDm։eْ|>P$ym$: hw>R=g,7ppWkM9eF9 G)~ )1cF8jZG0,A5pE2ohnn6teΜ9\{{8V "nooy4v^iU{뭷lՄQF/ereAF1mذA`;XbsAdmr,[V__N>p<'q\9\N:820-p[GXfM,z<1DTvꩧJ΀Gcw Le7d mY͖^zIu44<_"IR٫CъΝ;jNs&GLG m`C;IPF.%np&Į4mͤxbN$+hu:fΜi@&0aJ CDJhsv)/rA/˚Pq?cHi"4>d7x[VL>)jɨ](:zh_?k,_h:a,.mdR,]q*3lJq,S}444dYw}ٳgN=#\eyHR,>FIv$p?|wuuuo~Q$>"cd"'xb-w%>w[8~*1A$DnW> g2䌣`ҥ}\zI ?XpΛ7ϖfwy/0@@ x #҃L<ꆍxW^)$)L꭯X}! vွ?Vk8?l"-3l780s'v(ihhp9Q@2];v444qg >*++HyBQF71GiMP aa&T~FFBbaYh˞Lzjv> lذAFU:8R v?`gEEEы/hyW۪yxBk-]?!".g&@F[D3ouu!k$Z[# (p Yu46#Zs5QPl#V$;%*Ls=q/)w,\g19x\uU"y6l7HJ+Hئtrp'.qŢ(کCJf)Mo DRpI9tj (LsF&F%I3gNVTZ`D]eg:SFaN32_A$"e+nMM!O/:#g7 3G[.=Rڽ{0(ۓKm۶yJdD4<2 ) De"9ic|(n*Hm /4C8iRLGtD?9]VV6TSSk2eYc'DNYwzs 0nr0̴4ifdgϞ=m)Ie*CHlel29'xZI6sZٰCDo|>`7.+<|0v:|Q*)r{p wg޼yV6Iua/?hp6]nL7uFEQ]bEV [ Xڝ>&"OCCC"E| hXyLKA-GmmmPń#(!3,Vرօ9s&ЭdZ`{3$q gur#<EQP0,˽_җl_(g?;m7 XhŶض;>{PL'e*y%egcB?#s8Ћwk&Ʈz 9xg"oԶR[[{)e9q%*pD[ZZY~ IDATLHO?EPL_`;seeeɄa+1O~rhʕӶufiU𨯯wݻ-Ikxx=66梘 ÌB!/# -vuuMY>9sf[1ixI򕯨 ee]U LY1X~Ŀ7440z/_5440/vUWW P"B e<ϗܹDDw :'0@JK cwi<31֭+\zu(O4F FZLkeF!B1&8Lń6f Lcc.Y=cccp8̄a0D8Q3W^=5ihh`6lPFDG|!")Rs%vW^tEҵ^PrJw&{_pw}%! Θ1cСCIGSS FGG]&#gycg)!e~8veڜƎ?t˯+W ]]]k!zzz0>_2J<"]1y:41bV`&Rht:G\.ר=餓d+:p >R~4}B  6 n6f&k9;#9")-^vޭ۹ HC_/5<:Œ?Au`xSN9edʕBV&J޿*իWSN89rDiFfgBqUVJ;p16ltvvݡPhZ/}Q񵵵>n&xGΙ3xzA"R!!Rv3KdY Q;Gyyyɑ#G`*X`A„mJՆ I9, W^yElHoবq\(q>K.$ɡ<Ɨ0k<~_&zҊ&w 8U%K8$ fg8AXrĮ*Y5{o`r;$k+5}`Vjhh`Z[[~9cq\={Tmk7|s0w#<_zwkvˌN𷷷y-ۈ}DH&`r oڴ)Y6>FVE?)k>>Ojz^['g;A#L~vix'-"RZɊ}D Evg)#]T9eGz}w"&Iڵk5u5*0cTm)xP O(+nYMf8$Ir-]]]aVB. 577~5xbt‘_f~D71k.B['ItM7%x|K_y㇧ vmDD] ,F_F& qeZ[he ZLA~6mF7psA"-īq A#_\5v]i}N֭[u</pID&KU8/**!ȉlct 3FRTT$`U~J)Հ'=% K,A\R~Unp?~KEl8+zjjjL]?}AnADU}$I$K8n~Q}Lꫧ6NGqq1/)42΀'=555_\,IQb.AXVg 62 20LUR0!rXQl>vQ2_d/e3/R /&-w>q< Bw4}r]]] `pC*<ܢ;CS43hhhptuubX] "!vV9y/Ғm2:D` dC+VXzFsKMA^pQ A" 9K9bY3f>ODY6Cg>"JHEy QгLF,=x`322ef֒Dv/K~xxز]&q<ڵKz*zҒZZZSOŋB[ =UOi{˨Ek׮5tqGcc#OD #Bʇ'UQ< r%Pȴe -AEQ#ac 2a0V$arl#cҥ^QA@N;-[?03ԧ>%\smJ}YL#+ 33㬳UUVYz;Q|%(r!h@8 h#>rytƻmFG&R@ I$%)kF'ȃuuuj>$r aKwؑtKQGdg1ʝeeQ~?@DV_ 2H&QGqq0l>9uw; ATB/@ss[w.\8DDyq{۶mcǎbB]4@Bبz#2#W2g>k޼y,"Y^2tAo0"r qCV_d Lkkk.!pa9*l21+2?KT$I#<֒Qه#"Wo q]v Z}!-\'Ia8߫sēn={v /[VKAMMMLkkk!Z}-0aaڵm`:::*#~;.[gsp:x 3k,/N?$I}D4f@vBDXZp89sL6&Kk6˗/wB0٪P% `zy8_{J֒ǡ-d,[|ҽ֠&y>'Κ5+N*+3gʲG!P+,IƌN󪲲rjjjYt\L{9Hc͎v4@GbH6(555lYŲl;:x,=AEQ##ZpZJwnSS?ODI73UUW]5,[^^>@DT[[K>zj,` K.ue?,S<uֹDd;lB$i:gseeeq@Z}<Q1g spGdÝ_J:ɬSNa%I !p@:,sf4kt:t'!p@jjjL}H*[Y idTq5dY6 'H$IC_=f{wy;e zg۷owB!"n*p477w^]NsWG/.At02H״:KWa=(c%=tUSSchaL/8.kmNxtd8 m:G

    I `*DFd\r=8 =DMя~>r8d㐑H8)e= uTWW }T]tQ,G 02Ȕ7-=+[mĊ}]e9D`H:G}KDIۜq{ a=<]rݣ1um?liZ⋋$IBC 2pOOOZ7 fK.@cuuu6K=$Ѩ 2=KO:ai?i8+z-o~`1awhiiIh%xxa:A:8 eY#b1Q~>"Lۖ<_裏6Gooti@{{QKK ct|u<>-T? ø(Gd6u狾o577[r~dŊ^Ih̊2*X|;<χ.]:Qhnnf WH( p.$Iڴi;\s( O<GXf3g 8m p^{&2>-]zqM6b}466:=8 EA@#[<_t7:aT׿ q#h9 ׼[)<\.hDc8UC>V^zѣc`C8@7cMw&ŋ )k6b}DNc@& <ӭ}|A-eZX>/^ɲ,NM!p~ȵkѥ'U:yw 30tq\={2>s|$IG3}GYYYK/Geeeȑ#}q`c8fU +ۈ&hhhpI& 9dKNɲO?h0çaʌ6b%>8+e(Yڅ dJ},ZhYUt嗛RW}},˄Y讴ɐ;죥YdI@c<,SZZZsNԄs=D 8@w,46?~X#+’~e]V(2B@1 zPsa_ާUٽ{IhLEq'?1NDXB華{0>dH #DNb8r-lַ9v4AA#z;N;mқ+CV9ǀ&VUZK,ة[cu֮]˿D0M C0 cjp8w}nZmÌ́<Y  !wmJ# qm۶ diz€&v` D?wF͛7eYƀ&j`S Zro~܄O?BY  el4lUm#xp ,4A.@}|2[r9+Pm$8p% h19]w5;ŗ{rj ֭˲80T[rYxl#6lݺ%"4A@#Z OA86>6oG?*%`(3 ɶr7%ۈeTqwx%I$ h2@9w?SR}Ǟ={"M1 efn6,ㄠ9 m۶͜9ӐV]vՐl#YvRABEqp˖-Nu0,ۈ*ԧ>U ˲L8 oψ[ImweY3F/x0o6GDI9 ` 6N"T0{C}\Reg6 繬FdLAAAɶmێ 0mmm̡C\Dy/@A`&xxlE1ن̜9k׮c, 0(C=Hqm {Ly#Db,0+2q<YrQ)ҥKŴ8Mq6 o pY{zzϟVImlܸdwqOe0&Q\K[kqDoX L0L59G$ۘvN8BBӈ8CG$ۈ?sMIЫ 9S^xabIiܹ k---S߿żYѓ*˗;MIyiZ\mmf6BY  8lCݪ:2I iƞ={}<;`duX  ҧa1IDATwy_E ]w], , 2U=ڴi{ppp {,s\Ï?x!b{) S'?ɸEF{ 0]__sΤH1i!˲[l3yM7b-T`Τ={Ι3g"ȤmmmL$[I8vnF_چS;inݺBYM= 8 ٱ .L6]c׮]#G03V_'e=p8x~7{ayfY_N:0a}JKKS]{.Bm`d`_}Z|&̞=[dž P pe&7lִe䉁Pjی !pe& ߵkwl×I+Uqm,^H(9e /9V&^cΝ̡C\Ddjvl8X)+++wtuuŎbtIŽ; ;=9̛7O8JP(b4qA#KWO=A` ,5couus=$J8j'xM /8=JD4bZlSYY96c SQlڴA[8'0֭[c" pmWa(Bm@# 3zid8`  ƶmH؂TG#C4 p+**few{{{cώJ`96nhJq7ϭ8j38Ô"udyX{הS_Ȑ&XSYY9fV[oyTB˘K.eYFA@`Y6ۙChڰ(,q\Ьl$Iwد،5`_uuYF!M"0reYm\{ Wz4Cf̘1bV4)1 Ì5`hδzQrqB T`)wF7x#HXsnذaaqRLòlvψ;,E1ސO}S=DD!HeZKo8:;;ljh4SNz'\.! Ll0^KoNmT-^xq.\pP7<^y,D4)soٲE#8p,^9WNN|'QQygLU5c 4 pѸ ƞDmx]4=`|>?1k@3hPfdw_a([8b@8H'?I];& [nu Qln:g1r@#0LII2}~!= _!pn|/o>gxl{CDgɪ6bi>e3f̘R7c @>A]3n-&x6~ӟƭm x\.ϨaT#mIU~''~ @` c~?|C<}MK^GTχaN!p@+ۈ}lܸ1ѼyMkdL{{;p8 c4A+t6b͚5}GWWW~zۋ,@#v}sϰOi&!AQVUU)ۈr:84B P/97l;vh4s^Wx'jf(28 ]b78bƓ}݂$IfDD^S4BpWrM7yv޽̑#G"26***84Btx***«VV6B$J)W@`@Ќe_ff" D@I'x,Zhl``  d7ndӶk>fΜ9:/Ф$P`::: {﹈hZD럜mR28<ꫯZ$x(^p]ve>>8nɟ3f .Z*Kq 8 .֬YcؐcƍѤ3cٲe^;wnE-|0^,aFTs5x}Y _~t~Ä!p4<}4e$,яc)oiy>w[5w8 gII30eHѓO>q@ pdƍGo'݉Xk@%X` _~9444BD㩾6ax#};ߩ:p@c=6Ib,Ӊ@,UdbSO=ŇB!U8SV{W?ZO phA(ꪫL6q"=PUVVnmm-Kɾv#phQuqJ\u (7|n_%eeecjx@yzf;FD:Pqij{cccD-J qDDv ˷m^3I믿iQRN:il``b  . #=tѐӝiQ2hѢ^ZZ:Nx- nič$)) %} .e˖|>P  jT[[f8a,O'xgs9{3}Yg J8X-[3W{/Y$@ !}y6nx;N$Ii\@Bos#JHVGEBCm:6nXI*NKUywo!ypg0T,]]pc'xirGr2s5N~[p#\3gT}\M_v|. @%,U)AnFӲVGww($UUU#Z/Ql߾[o=jժ xӹ.|9sL ׯEQN\555i}\yࠫ[KAXO;Ɛ"q0yDž^XTVV6K/GH2݂[YYImة駞z3::X__866l۶mZt{^;;ϟ?N;< ۷og{{{M[z7V*Ytޛt6X#@bŊ37*++Hyk骵1888FD. m޼?{ǻ]8#I2m@VC#.Ozgկ~U4RUTUU:yWȑ#"|h/Waaa҃ l޼_q 7pѰd ]]]!-ߓ4aկ~޾}{ƍB _!p \d7tk^"kU^^NرcƴO6X3֤eoc~{ƌSuww3k׮ z<-[9PPG())ΌĪ>x. 9眉SB}u Y_)#< /в~ҽa#Ѱ E^wu1lj9vt7Ċ}DF8V*Yl}݇KCƑ\oOf_#IqQ}7o># F&qVtG&T^^>ht`8rtg꣏>r>''s~Grm1_|5222hC!p(A\siaz)ψQ9s p(6s*rZ}g8Z[[ àGn2e7Uԃ>4?>>40~@=$ժU-S>"ҫWp88TBA^sR蠮q2h_Ƶbz{{8TB=J.$ɐleY("BP #A/7b[l\o:dhH8r*XzY700`%Q 8rK;QwO2pRaa3a zzzdVb\[ fϞmmԞ={DdXsϟ?7^ 8r׾5]]]a2h79[q턥*8rs4q[oooLA"܄&J ,]'&.b:ބۖR@oi Ӝ3Ws ֫W]'6 ud\\ZZ5g/eXBpGgǶVq"D.;"t ۶sA}Q*}-ֽ{νG*1aYV˨>o߾Mewiɓ'v-G\U0U#lN_~0 ^5>i&l$@ 8bv|l߿op] ؘyǏy(vXw;ZV뇖1fz;O*388ߧ]lEW5Ovh9:c<5>>Υ@+.E0 uT\ٶm---q ?ܒn$"8¶w5Cb4j"ϟ?({_ !1˲|>/溯_rÇ!ѧOJ"}qXmlllp_ztJP[[[:_Z7:cY8*!/1&dJĵkׄnīV aoy%o޼*Њ  CRǏc3bfbjjJh! :>>DuTqG~H'RIu  8$%ra7XӧOy?n/%Df!zjcc "!)QgT 褣*N]i 8䤉<_cvٳgBQCNȈQ:5իʍ77&rQcWkkkj*t@ 8$tpp` ?DdFH%Gv۷o "BpPPE "gz^___vff每V P/pLLLqz65 ¹",5wwac/^T^^^@pHWƗkt:1ݻw2LQ?T*%+ ]ǣ %>Bwヘ(cssS'"5C>j*_|Yu!"珪 E/{>p".@x({׳Ţ8 ]8LQ&< |NzLQ,ׯ_a9Q6 W,ZVjYN;#HޯL&XYY)o޼jz~XTCӲ,ѣG@SN )Ãhrrdϟ/_t4BAȾUyt u߿4\ 8sWGK>?=USj  HBIENDB`gammaray-2.11.3/ui/resources/gammaray/ui/dark/pixmaps/watermark@2x.png000066400000000000000000001574541413310443000257260ustar00rootroot00000000000000PNG  IHDR~sBIT|d pHYs**11tEXtSoftwarewww.inkscape.org< IDATxyly}c{Jc)vWY mU)h UDET!+VMh@ ?Tv4PR*Rn ]{رc{~=kkߙ1χd9fxrx#"iEIse;(Y͍~G蒤򥥥D$iX\\l ))).BR%DD'x"d{;uT]zWuGGGSSSS{,t@үH'KRZIQIW/䌌s9WUyʡtR\Y VZo,Zr|\SNE7jWqÇkښxW,#IeY} J [GKKb}ӧOGT EGGG+tRVu[ҼV!C(JI{Ϝ9sѣ6.\ZZSu{Ǵ(6 SWkkИɓ'˟y@D4Tl#z$ _~*(=}}}>%Y333T**EY[}?ߝ @ p9s\k*ɪ-.3"/"@1%u] rV#_ǎxbetrr2T.d-i%IKeA6P =|'>_;w?X3::077tY  Z"vK^kO|_N^r*JU)I~0I{}^F>W;V$y֭呺S&$+\=*I5od6PHh5N_ _yh/M-(D=dFjYɄ35d6PHo~G>D>}:SO%Ο?LRIE+IDRR\$R 6$[:2(X,֘Jz}BȖG'tLҬB佬\X&hdR02(GR=3A~_&'''ssseQI Wy$UʲOKSW2(X,VJ>N}M+ZHy{j"f|Hj~;1AO{t:]hۓqd0$+<lm x<9;;`}o^HNLL$*dB FJeE,f0\mf'7Ij~}lܹs#<|r͛ղnF#j/2pSs<^ km0ޞ "";Ӳl-e_ YkFeܓ(i}l--];) FNxl I >bUilᆰmZoD|oBfnhkjjrFFFF;VsU%׌T8JRB+V/|v\Rݧ?K kwGdeB7rqdWތ mLͷÇ666vJ:(.I-*]~E^I"IT)3g*0N:=x`c<%]lWx,:IݲG<Nt %;B+;0*3mQIwx׻U\uD aDmlQ`G69Z`kB6 x\JMɲbDm8(&FԖrOЋi%Q&^HP(z2(Vx34FfnhDU14JZ4Nf*T%FbE}.J'\QH#j!u-K6n LD+qYcA1+dٮ/l`=Z 0*eiĈZ/"UտHVKJjʈI1A [m}Yd6ڬ,R-۫AQI6S&逬(&)i\h`c~g@@Lʚ;>|Jsc?@ z|>|Ǒԡ-SFLK꒕ʰYmr^td { q;kI<p$T?L@iԣ܃QYUB4&mk @iihnA lrYp^6./I*ۭ¿!iX҂KjY~R%UOH: + +"*nι._, .  =֬-a[(kIm׬/UKԿ'g dMܣf<> £Lt(d6«IeԴV9<.#P-xc/$ 2A6}nYYTXl ٰW~d6©Q]K;]RLݪOHt]u1 qdLʿi^vwI,+/c -i^yFdY\ǟ/xblԐ9~KW&, G>5eT(,5I(#ۣϔ۲ ;h7>ˑMknIm.M-(ēY1VɔiY(5ݲB<9 aF 1d69>nIUٲ8zIs7,%*)5}3eQ>8|6~c'Se;.Y&*VSX f%.Xo)S+hj$<]LEvR>Xv2V#g1 u9>nAaK,+vΓ헷잆|7IګK*Yho2U#]Q 4/i@QYVCe:e_L>(KKҰK &r|, Mu*N 2T-CeVcaYyNsc_e^>aׇX#"; #;ڋ xӞN,ErL=)t},ۿ2'fI7>ҒFd rd៻eWOlKRv ɝmMoq& @cl s| (+ku:6Jv>A!#htSV¿UȂAMm lBU>ݺ{)IׇlEBv:Yզ%\[n*ec^ѣ`_tԚ㢒Q.}l_\"hYdRe`sރ|F閚Y˜ - Tم; V- HG>j]{:%iއnMY7 L@tH%[iYT¦e7Ғn| 됍  k𯨬_w;owM* nd_BK[v&I{¿2 X ,Ũ*Qa"+- ge= ^n˲-ʽTlZ]|Q>ZD1YuM>k _TʋšX-,Œln|ĮHYF,-2&iJҌ)|e7;.ko '/mc^v(QЀ^d;lKTY-K1#(. ¿}lC)ףLdQ+Yƅu`%d5 irRIKR'*]"^lҔ񙔤*MF6{qAuɦMz}|PFୈLT'PmePR\R}[~.Z>Sl6V!+%vY#hdىFyaݱ >w[Ҁ,hȵ `HINm冁X޷z6,ۚz"]6.s1-}HKה&u@Uavs%}#ώ!&\җiI \-o4^sU6=+(2YSr(,¿l2ِq #3 o,E.} ӲE)4GV"6,- 2U&یMjqKHIu հ2(s@PL.ٶZMTB\>Uٿ gl"]_~病=}Cz PFP< f^[/ rk뇅 g@9&}M>ϹfF\?>A>U+0(1R%e۵$M+fDg$ eTWdGmMۥ3#muIPxu ٟS;k{S[`d=(<eˏ53.I-@fje;r4%E1%e'%uqd+xl&$FP8^DM6sɲK5'lɲ$ni[>GԠ p%%lA5YQc^MvPjde<^}n_. 2,bL6v=e3#]#_-]޷ 'x}nPVTv/1x,p}*E3[*"jYVJf`^VSYd_\&I&rX.J_-\ˢj{ *lSB{ )Mݐes|ܜ,3K[_Gw,9.D6^-5 5ec-<6Q%  29>vZe{d#o,u+-%k l!! ٸlFG[o_,Krӵ\#pNA^eG{߭]^n _ewjWFherqS\eIr VM#d}[5dY\vI: .2äYQE҈ 0"A€` ?QYfcue^[Rׇ@D%\y"lj-m%ƞ ܵFz}Ԝ,4Y( 7d5JÐ} "2iY0sde_2I3-xr]XP*%%%] dEdAC;k~ےlmfec5K)En/@aW/Y0$ yyD~qʮsPr6B{k@a``ϥ̓}ikbDVBu346ǯY%@nd EF꟯l5v0 " Kg`'YiL@aAmIIqI^$66W&KҴsF|yȂU LR~" 6UҔq#kjJ !p "jgcV&+!IwɲY!6lEҜlcl[RV^pj a@dzYTuYM''B$I َҪH'+KlܩLRޘȚtCR(9] .%ݎY(++[D"mmm uuuKoqW/Oz/yU^|9XTJ*eҼ,MEG~YTʅ竐Oeqoʴ,%`#<*eMi+%tyyyzϞ=ߙs;+>k[XXƵh,-xyNoɚ5Ze{ ^l.I%ǻ>8sL$ɔۙ3g"NTWW7~Yf6nd*7CB1lGB9!i!H̶̾mo=zS/^Ul|;/b۷t\x>%iRTvk\.s;rY8-."!/GTT˂H<imm(X/\=33S#|O^ ܨ>ICrB/q^j ɡ)6ő5 c>LN}&;tl뫙}34@xUKjT/˞\K[y6 0j%UbTcc;Ω3g,8OZ=55U'ܒ4. lIVqjdEXXH++e,%`; YQ暚&~ᩯ|+wLp3:re9n^@%2-"fP SIcFqK$ɉ__;d9|c[~# =UďM P):_eHj5Dbr?~D{>`7,g;% Hd{d"<إG&wlN,Ə?>㏻rqO{*!Sk\ֿqML=,rN {bh:u(d99~xd,ظ!27=Uɂ md{Snz}l!!t{Ǐ9R vu~jjIr1 IDAT=qIݲ^ZeKz}eOl/.&Qu&2"'O}on5WݲƸYGe3YYY3|`#w1I-b]]]׮]: {yG"7fggke)Qq-&+KV#BֿqY\ TG e2:;;GoN!y+Ξ=ےJbTPvɾq jV ޱR-M6Θk!ZFIUUU{Ňd%UE9ZT{!ڱ㠧2Iw^D:Ix|Ś.USSSS?55,6"f@t˦/‘S?=%A! "{}ܩTh4ַvvv(ȁ$=zԵ%Hu'N\;2^ BR69ٝVٙ= III^ۆΑ~c Y FJ tp >%} nݚ#>u]ҴglIc>襰Y?H9sr2Lɾqy}&JV#[|[:mzY1yeo)7iv4)*i,CqZQ!iW<8tĉ U ,?$FFF:+]d .(쑍1.x}"JHimmu֍ÇsCСCz饗\y^z9t+U8?|B,A oP+7e$e#|Sd, Rl4Hj_~e ,?-77ʦU1V.%oRSȖ]4YR6$%?яk_/Or".٤uBHK65l]~P3bY9'x*θe;700>q%٤.Lb.Y3;#j\RD y}LK.Q61qر]r͛FMR_XO!>L J >ԂA<*g޽C/^$lG.93qf 1G#[V>Y2-Nu[8zda,+9A6*%&9r$8ٳg$%E8pi],0gZe#%!2rI{F]6a9z93J-I<G@̸o$tY߈I-+*LU1؈H6Ϸ${z9!HtREnm3]\_ >[6iMT ZH2//w:,z{{cǎu`Kaprd[e%D0A/A஠lt]7b|S!'rxСCrgٳ 3^ P'B+d%e]=KA)` X,nA1[MSgϞm /-;l+{{a<vG^B((F}4ԧ>ug[Q:pxȑ#ׯO;wu(Cd566+U&oOiOV BFBRG?+GA/=;e߳(\;\p:lK(F_@jd/y}- B-%dF9p$xxB` (Xm'+!}k*<FvFg<w^GWSS055U+c;`J[luԕB#:mvv\,?{%ݐ 7%>Ay}uD%(T!IgϞuΞ=` ˎ=s׿X,V&u? œ %ϼa _ ezArs3:6RSSS?55U'&hIq **YBRRlKwȑs :,G%T*u[܅52Mމ*$G+)1~niiҗ r0sV/?L0PZ:eY kVRl&oSE$!Q}Yߎ$d9 F&J9 !"WalSyYrI F#r/]]]="{bia (ET.őe@dy}A5=444&IgϞ8[#d9 #wiPxQp""])s72 /{6jhͿuttHzzzte5z9^/~1K/u%r @!uJ%(;J܌YldU1}yوH?M_d9tImPQWbWpk*Y8! 6:2###'rI^@(uǼ>HMg"%nLyQF[]Uv!KJjSSp_`Pd5m^UζWƫ5&o׮]cW\2Y,;˩vqꔕ0l/-[lbT*T^c:U.t_ ^o\Rd%T܈늹gLR˯ɓ9rĝ{9;{},yAyICR\^"ZeK 4b^ a,@#dyffB"?]dvtJ6SFǏ:,@#_FeC> @kM"AلmjYs9S qb]ue7-,G]cccrllE6.$\U.il[8Ӌ%&k rL&0b6bv+_xĉ^! ?&Vfq;&QZ5\ݙqYPe[nS`cWGG@Q6H!:,=>I/X+".v1+>/JఁB1I.>|[A:8c% x}*᮸Ed f͑O]U ❝8wqɶy>K/9a>~ٳgd/:]JDvW_,%[-Yln/krl~jj*)g{-Ȯe%lFOWWk& 1n!XoosرT[eȦ,ؾ2I{dcn$Mx{A.?d5"86V___3>>^/g[Ͳ\^Wz+_~v?ے=8#Pkp-sgljɔg;٬EIT$*|$-,VVecpst]!D4LӾI7pUJVkJvLwf־Jp{ݲF7BXn`t:}K6$8 +$6"*fAm]#k&$JI{.Wc+pYsOE (fA<%LzԩSL7x=!n(m $] yZd&Go "nk -_;;n zƘlV㪬OGlD2 qGV>u]ҴgA@lx{30TQL ^R,(촦ƾ-kZoʚUarVQSTPS jq/DYmmw[:-HJ98NlT"{7ғ;/wZ@W!4Z6-1Ie7V@ 6{キ')6?7ӟVR[nU.--Uf2N#7~#ulmQb,[pgv,{>O̓9qW)f9cccM%^JKZYcv{wH+IU/7]}}}66^w"Zrw 8oG?J%t\TLɦ?mjl)S"ONS9q/9(,Goosر}4Mg5HuX6J|W_}g>S=::ZNbRVf rYQ#)f&MؾRr4LMMEdtJzkdvVe=6>cFϽ;%8yd矯KRIY`qkG_^ LxK%wȅ,ɓ'˞x"=]}^)I7==R.믹(UT vlTHw÷kW_uofttann.# 0& h4ijjo~slœ娪j-r eo7n.Y A$hnjjiBbueX;44 Sg.5 txkk뭏\X>`ٳgw.<|q&!M+7:ew˹O5eY /U; 6:r4z|߮5yFԖJIj[gΜ!]0f9xw:56YclYW@F<vn& |IӟtpBjT6o 1O?~|ԩSo lYV+ٻhcFV,.NY_DFGkkИ / / (e:G:6,8$GC g l €5S(<H?e<@<~%] ؞%YkTj{dӓO>Ͽ;zk3/c@@8DdCA6: \a3eƠ4P@\hbT*i|_mIRqY&l%JJ _tiUгBUv]1ɟ1^{mc 6E¢*?V;V~snn.%XNT,Y/➒-58^ً_W $tA۟(ۿtP†%Ekںoo$x su2LFRRg<8| u-訩i e{4rwdeWWnbJʡ(r-K?8ɹssy6ͦXrqM^M<#=o~^ȏܞXړmCЄj7s[]?6)KLB"F{+fX5Ͳ 6GRRgUU#GN%ˑH$:RԬ恠i-'3n 2bhT#&;5555f3ˁFK"iIgff=Ю{QP{@d#y>>#+M&QxZĘ[x 9raroYUkԲw.\0 .u~r,o&귁hPޭ|.]fE`٨T@C ~xGb9и, tŋdٳW(~r$ ‘PXmXd7 V٠LnQؘ6> NGJ҅ʇzɓGnn=иE1p7T/purJ6JƅeG`v_w{_WZ{,rݲtAZ$>|q~,M**If.Y ҵ`ÑtԩSwh߃x<ޕNg0A 8uUbgqW]~Ioع,;/8xh;eTh4:@Cw/G[[[C:vN)7)ŋ[>_=zW'&e/ɑ-+MaYfڷ R&CO) ׅ=Scy;xبFvؘ_D+JM2WU]T43L===ڷo_^Kӑ{]hςKT*Cu>1߀_\;d'Pd2d2x!˜:qY۰3e>s̅{쎰Yx<ޙNSO#IX,?3/αcs:IL Hf9dVTV!1h>Ub?OwgK^w_^h/JIG}کS݂WD{*JT%i ɤMqzWk1mvm-A^hSfXҭӧO>vXׇ;rݻR*_j%(А-ru|^6zjӀll$뮢Y 8>7˾muSpoo.y㏧d|}h651"A[s$uʧW6t|VJVMzMVRѣG9sfA1{9d̢G xFRc&˃\hF輵F+^XkF"yz׿Ys9}}}4#Fit 3˱o߾)Y@[jBcDVT(ѠֳT6H IDAT7g} ne9:;;D5~w9r8_eY 7⫖Ԝd.Ivhac{d7M_4ٙ.IFlppQdF?3d8YD"1% xE.2 1*py}?h6 6bۿ`CĪ??4# 6咪'eBNڳgTwdz<EPf"π6QQ)i/'p>}:rƍz4}c=VaPYUSknjjr53897eO\,C)|mx'?k$<::*Q5GњSN]=qCrv/G?9(*ILB!?H%%$]+qVRbW|nF&Fr3++ (*I7oNmy<#kTo)‘]vM4؇?]/8}tdttF` wtZ{hL̓[Yx<>-@4}6㲞Ҫȗe"0 6ht|g kf9oNݩPIfgg:;;NIlT1666"ˑCjvLՠ| ^ |jܹsNoooQσZtepp*i;,." Z-uuu5$1)vID&iJҬrn`CFp؉ے?w(MozS&Ϩ4/0bKuք6rd^cyX$dXJY/؈566_c=W\upGJ3<'<%WPa%:Y)YֹT8:% @۳C>p񪹹E"IIz&T#o{6p_LR,Ɍ{}(FJõM@1OZ&@___A<БN%"PN$333]eǎ-+#)ҲKt&A8dT= U \[y16bj|{ߋN4ඁ٪Z&r$iNP,QI'x"òand˧@6؈&:ٝFá$]o{b^&Lx[Bྖm e^/~  `OOew*z *%%>O5sòUTR,FuuuǏW.C1Q 6%ija}DR*=-7ze=\ W9UL;__ t/@1HeЇšzeU;*%U} ;qС=Z i`ÑT~ĉ^fb'#hh}/:XM|س*$> f:| aA䮩֏ERgΜY4_e?磉UI)?f\{C񶷽mA> ?Ÿ[`ӱct] O=#pܔK{vOFʩ f.=Dbom޶"r2Gl$-1111Q%mI7yVF}}}scN ɲ +RRӎ2̸fj":ˑ$*|`|copg?+K1qg6NXWxR*`s&K,uCEYOl444MY/IY ͷ'OF>H477l+pСjde2II8y}d2GY1%`*;^/_ߥ%s˩~[{>#N&_|.u ػeUqIu| %&lMo;EŋN:NT*8x`Nܙ3ge1ۚ:2I/ Td2Ӓǩ줬ʑO]ʅFFd߾};_̀ _BLҼOX4+={ERH$fEvXAk6A|`Gٍd9%͉6(AGuQXY/&D gZ7ٻ;}ϕdj"K'Ngs(!)g:))3L;0&)HąHI31db!qny^ȦEt=szMlZz-)V* 9k||<`X</ V(Xa_zC֩.p S[[[[=cz!fq-D=NQ@_AU^'l9idi`D,?'Wx =ܳGITdLD8(Q&r'e2ŋ78qBz1z"B=*?.0?4|DFb`){Px']ޭAF)IC*Z a8Um7&r= E"2 )XFYVkэh{7 9,R޷Am5|՘ĶpuuuA*,@;eɰPd}o޼1O<@6(O>-kZfM*˜_PuYXGʛrڵˆ0?) 3h=uѣTTT(#?( %%%l5f1qammmJڍ?? Y n"iT h2Lطo%[n:ye<ʙ3g$wVȑ#%0l0РD[NfVꫯ~Z3p$gŜ۠L%𕔔tj#8t޺:+B>]|\"# hhh Bl6l?ީȖ#q?eDOI^l"0V#~]&=ڵkmR~F#2 >k^ļj̧3_ 4~V# w}111d^)^6L@X,p0ly~gr̎sarvttwcC\K,'uuu%viX8BP\-Ǟ={Xdnݒ$gk7&1pLo}B~{5a֭:O<H{A  <00馛vU /8S&ۍ.b1=޷A`Suuu/qqHSDvrKK_rNߘʰA[$/~񋸏cd8[iii<8[vnArV"vBo17oNg188(a2О={s~l9'FdVn]wY 9[QF5ah#HZVV63tnᆼD$ՁAe@NH\'S̈́#:aۇZ(E^sbހTz;:;;gm7,]]]%)X"z64_}Qu]Чl9|A;BdRcBwNp[mXj{0\L@ۂ(.#gZ>dZǮʸTJE8r5(uG"nW&" 2@~~~'OU8MlrH愈`TQsj˽yyy#W^y)3GKK 5({+S[nM|{AC={z޳ͩtvvNnN 8YE꥗^+,,oV_mmmBdJ$ TGR[6\yyyŽڍBD 3ݻwI۷oO8po9uby"q7Lk9N'%3qߺu_Cmmm y0q@ #2Og{E n߾_SSD*l---I $S 2@nn֭[1[H۪L( UUUZSD 4ltuu"vK~i߶m>Hhii[(oOGIF&7x#,콽}`0(YEEE\HE ,zzzbD߇/$L- {9!>^{- UWW%īWljQ"r}D-b`߾}R=+^eN"%Ȩ E`p?Ϥ8*++G͖e622b`!3bن].ˇ80 DO,p C;(AadʕCo?+?8AX 8/DlW_&373Y"IYߤݻw۸q?f֓'OMQѬp8<" cy#&H:t(`P^1q?"sM%K={?p֞={ ֭[裏kb Ջ|4'2ػwo[*lc---و&2999ZXF UUUw}ۛ=44$&qL 9bXG<'|Z)⹛zLL46 |>W_8l۶ /x5W1] ~a<6IX򕯤7KXvmDFPK/cjFGG%I vD&7!:=;|- k .} y ^7^KHO?]`p<3s:@AggxS'Ѥ‘{.'3Ka%dxJ=΃2tL(Y###YcccYHĆ֦/0p8VjJg%3OD nQ}Im3l߾x>f388Nt;4I:BYdX,IՈLBmmD'MM 2bZn__ ޽0A "UL n/xwN'@MMM­+((Pp%,V$x TK.]կ~[l8 }`KJRx~U̩cjƦno OEqUWEAw\=[nح[N:`бvZBdVg5 ؽ{ ۷o۶m!zBa&@$I_|1%?sxꩧ6i fX2娯/ |I2S_4/_>K/{Q__qƮ{w*nr}9DsI,\ IR/{rappP74#////<(j6FFFR mۤug;)k!2\1=9992 @ަM4o5i[`cڵBpQM}8?uI?z⊞J߯]>m_H]/-ǪU Br zj5ba!j=Q;OĨdUWWmذ^ؿp8QB.ۍIzi9>?h!Қ@ur*ޥZ2(]m6o~ /p@SS~eҵ"XY]]}+ Z܏t(t:p]˨-GeenSvmm6zͽ;y>i&'<و!@D0sΜ 2'[!sV뮻NWs1jQWWW  Di3fۗ;'ӽZ~_qqqdxxrlikXLeۋeY>9uuuڭ&NZ .mnnvi mZ/$YFi9 u@(5\cg?H/,W^yZXFh9*++P- Di"ȑ#RMMMN>bS%k``666fX,P(ITȄ|D͖l 1xq(ip8#H Q^[AJrjȨby=ةkfTtGK IDATmӶ'NdɲoZ֑9hѢUV ˋ{{{#QtDɰXOַu/up8dYNM`^{b޽Z/Fm3-wwuu8~@vacƍ۫yG}o1lz}^JXTZZ:xan?lv|CD"OCQUUe]?\ @lX|UWl۶mSoSN"HVmttfXFsrrFDQ.--Yt;"/e4ۧ-b@S}Qe˖49f!XYQQt>3"-[ScLUhbFUnIzmj|_www'P믿/<盘L `Z]]Gj?6Q 3d̉hZ@Y n"Hl(Yn[oo՚+ b@vvvƵE(S `k^z7x#JWSD6^kuk\rI(fcND СCn:Ng$IzL8x+ZXh9\.Wq$cHs,G"t= EQAaʖ-[~CEEEˑ6(/Kh)r9#KCR]3LVB|͚5m 7]9P"ݚ#wq08$N/_F ,\prTk!uHQ˷jDAY~-O娨E[HMnO>dP녘Qsss Aۧ `016+Weي~Dq}Ky1H-f 5.]V#<ѐD1[ؐ?裤Rx>6 OVED )"\s54l6H-ŋwi3:yd vp6p'R{nP(Б(Ey睴86oEdlsm+ݻ7T]]]NDhnLSr8Ad/;%z^ %g8qbέTMMM/\h'D'I۴11$mT /K*ZdIɖDdPsz^{man!N'U[R\nN箬\Ll#%Ӣ>/H2DO}]D66p8"ҡ_eǎ;KGKwq0r{J6ϡpTUUٸ}|l<b(r!z8GyD[JG!qSvV^=^[[c@'}Tj6"ȧFSS??nbuLdl}ݺJ+vs+% Z#yy')"'l )$m(2ݝOD eYmZ/$Q (*`AՈ UUUn (6 =SYMMMZS."Jq jTlg?mT4,=3}Gb^ybDOT^ /UVI'O̕e/Dd8nQ$z!㕕+| nsyrVȔ=qE>qD,` 2,FJ5-V*mP,ZXl9fVUUe+  "ӊE`c`IdF%EEEí0a-u]D"28Jg+v\cO=TRٲ8,$IB*"2 D_ 4̩pRd-ŲݴiS-_HXac",gAColXz5OX>{UWkG[lۧ2BFYL*V[&? `bƍ7nUA3u[)@k!c I8j6/}KeR 2Bi ß9U3)tx^OOOq0exQ͸jDf9, o' j_ygjlܸQ'ȔmU`S^GQ2ⶠD9sL IZ[n%̙3v-iYUtpL_#>NDa3$"aADSB!ӜJ5Z+VpH,W^y嘛wI42U  _QQg/^'O`@hi$,CV>tPJåOrp8 k!"1lLGN>56+$x;e-ڵkEz=D=lL$Irg˖-A[zuWlB}}}1i0h ND3੯ַ>}zlD %M䋢h;uTO,[744d )2K"ՕoAX k ϼoY캺'\ D4,:t\Zq:%$z-y(JԬBrL>Ug_DDb ,;^uIh.CH|͚5ƍrx<N9ȸ8 NDs3R:(%rDQ[x*ׯ_500o '0l\2:l\ve<ʼeee6Xub<̀ۨ(+g-ڂ ,0Q $I:B⡷Yܼ<*-LcvCEJe62icYṕiۋeYnz!Za $ɐzxp,e9 <lQ<"dA ъ(7@iia(r$:<^VV+rκ~0lQ~_gV~FeݎO>$B+۪lN* c!q+G_%S/a7YFtҥǏz!jJ*ӹ@$f5(˲Q*vnn[4Է˖-H$T퉈2 %m_[SSLj9Ng$Ia=Zd='Nz!v˱eΝ;8@w ~ D>}52吸X4[g}@4(A Ds3bkղe8$nlŋD8~8%IrsD "C=ztWekkPRR ]녤Ӛ5kf444$ /@ !UGDD :!R'7^FxѢE}MMMZ/D+q|(D"@4l%ۨ(Q}}}s޷amU[ 駟6l0 :"@[VEDfǰAD(gϞ9aqpHx]lOhhh9Rh1Ei1lQ2{9_eQRR I9fr'?0}$D^ D'Oιj*_| :(n t՘tG\pam~SO=5I~>w.zX,28Gз-ze˖eYХZȜ6h>ȏUF)}Ng$IMDBl6h87zj96mfC_lrUV9%Irz-DdnMDvwS6mJzh9AX8x/\7lFWWW'TVV.AACDf0>-$=( }wˣna4( 4eMD#b믿ޢœkr_gg(~"'tvvrK֮]k_Z̏!eGCsڳȻjňnb ` "5P~ӟj[{9VZŻ6s:}۷oz-D96H-ǏOt8v=_Kal5PYYVDDi_D;t(r+v NgWѺ: ⭷*BthXQfa "Hcz!8\.Æ6,UUU [pa,Vl(6HMZ֋*- Wp86o|֬F]]` @i(0l[[[u6&*pXBH7 }k3}G#Sk9&NbyyfF,rK,` "c T wtt8^D*M aC=9(G?^Tz{lSaRi0 ٘j(**bP.J T:p "c T;wfit(..搸:a#~ٿn5b%p| P Q1lQeYv666~Va#~ tzZٶUs=i^6(]F ֚~nG0}'^O=pӷ55ziG IDAT5 EDD/B"Je˖;v^뵤_XQSעcJJJ"gΜIF9jٲe' "J}Z%, R/ rܹXP8 "J'+gݷaƖcގqZEHj5b娪uvvZz-DDj`AD4 `;(r(DJgK-/~<>"2 "J{ΛrSJ^7@QQ'P%Jo'V+E lr~Ոv/Z[ZZ|DDfpժUσP"rkP7jKcE$ Ddrl6H+ᎎy߰d܆@[Xm9&]LffbdY>~iע"'tvvvjTHҗT(J'yzDDFf(b VP-ǥ^͆@]weV#V-7ߜ((4(C "-- >AXO-ڵkwivvvf\\-Nj/hݱc'(Xl---T: it娩A2 "ҒhKeYnJhr\}ՅP( ۉ(((jz!Zm9;?8k( "-)dر#EZ^76,v[ƴb[Cp"H DqO?7嚅L =ذ d AfߵBDa9uԼۈcӦM 2gf3՘_hz-DDZi-_t?q:g9֮]q#i{RmDڴ^8bIp"Pl6HkYņΖCLJ%(ꪫj$Ńa6`:['O,΄u8_~yHqBD "҃Ȼhhhh8ԷfW\V@qqq(P8e8 "҃H(􍸖#UK.1{w83ºo61ުZưADz gHe s1jtk=x'o v2"x DC555ؿ0K˱tq>h0|Q '"ŰADz! Ō[uֱ‰b1l^g}#nx`'裏*f"!p(S D9oתPc[UIIٚ 25pp(hz D(MMMqY5b26rEQڶm<L C^ 0l^Pvarlذa6Ax_tE3ޫ)cPxk!" "ғG}4VXFj9DQ4V\QGqi/ʄCDD3c "=&6.,C.(8,X(8ND4- "ғ,IIzo9 l䈢86Wl-=cmiiCDD3b "=0gArZ aÿf͚0K਩̈́a&oϫݘǖ[nQ`[=vO>dd>bR$ND4+ "KOr,]tn#|F8z6cZH6Ho"pXmFzj9A0ꐸn&(--͕e^ 1l(d{_"rx<mj2Bਭ9sEY.""s񴷷_{#xNgURRVc}2힗^z)3 BkkPRRʧIZEE?(z^  "=?OVu饗\2eF,=\s+@k!"2 ݽ!zC~}}}Z/Xzuo~a9acn_?O^^^BY{=f("r>עEQ`O&)|r\xY-` "Jl۶͖'L4ʉTNݞOh-O|~p((a DWҟ紿OWzF,Y$"&i8N/0)MDdt DWvMތXb†@CM[Xl9:;;̆a*288Tka#jժKb@;V:+R[(1#Gz!@jNa)Fj? Dv}RacTXvZW}}}1g@DGl6H ?8)-ĉTzm7+W !hÇ  7?""=b "=444͸ڳn[a_$BY{E '"$1lEBތ8 6˗/7Lk# :::$"ŰADz&a=WK6z>2i9ynsP6HEQk|aYaBö ֭sJ$Jђ2nM~i}d[իW!Dg$  jĊ7p ^@+8ND "һɓ'u6&%8\.R@e]6B϶ 6)2 @W֦7sJK߫BRi mmmmDD "һ,zx3xG^^^†i[Xӵ۷o0ͪ̉anmlZ/$Q_F&Gee \:5^",Zfݻ^HV^= 0۷[,w0`< "#kr@lrΌj5&m޼#2AD Dd^r+mذaTLccp?8ND2 Ddgr|>†@]wգsk;(0@z-DDfŰADF0`z!j +W6a3عsgVWWW.vBDdf Dd5M1AK=*PWy?Oi5OD6(.SmڸqZ>׿u֌k5le9@F-"tb "MlLCtVzkta(j6牢8V#ց㦛nSep""i'R:-F,Z={X;VhADDc "#)Æ jmEq? 444h2}vEDDa "#RzVD*eeY#|8P8%"2 "2 }n5t *Ǎ7ޘ(T=a@~ n/MS[*{(r!9""1R&"Jk.ի^Ǭ~@|poݺ'2544S VDDaD:~WCo L2f0S&@)HXZ~ۢu)ZA pJ]aEAe<P89X(дiMG6̝;uΞCg~ɽ|ZZZzRj$U7773(KR!-a@|`xo|qIHD" 7;/IS4z@5}~g&Iz[@4+W Z7ި] I՟ԧ]V^XbE}^] Az뭷x/Ҙ E z^{5kƍ*hiiIHz I5s |qmر#&ic5(H$_|N:YqWk[饗[JTx4(rr ը-V/Rs5G IG<o͞=F.Z%mreY/ҖO9昃l&icf@%>#0-[oVCjfϞVOmO ( Ap7 NN?m'G,wmd:?iii1^{*-ioo$l---ƺu*l*@Z>;F#XCT=ݖfKmV²öuATmů7d_\ I>VG6?Nd]vN>>ohoo7DiVi&K+7M??_noKjz,ܣd=ׯw4jNl5];k֬@ IP+ (0 Ahd8Nlu  貶H|>V裏n:uƒ80a@euVHRGMӌ\r38r-`4 gݺuYJ5jlw3f|ρn!l(=6mZXdJ(1M3r7:9×Ge[< jY֤o[aGuT}*ӦM~W3^ǰ~ٳgOأnxBJRYi6[icGH~h9p _Ҿe˖O^xᅡNvmjj8{X}-`? Eϋ/8oHgg)gӧL4KnMO_lu B1E?x&F]ccca?B1lw 6 $>jX:ܔTr?[XPLh`Cjzg~l9mu͓|P{eˊ;;;KՈH*O-mV/ a@!qwڃ>Q1ZdHlٲ⎎e38+[d#ᗿe 9i5j> ?cVL6T*ٸK%;xy|;j5FʗO|r r IDATSn {=ȡD__ߞYfT|ߨ%gO2kVF{{H$2z_MMjjj} k_Zk/$l(<k5,.Tdɒ#yrSԴ38\51Zhii1TE l(D=8ocʕd2Y.g’p\˱dɒ ۶{l_FEPF]$~}ejTWW\xD[?O7&D BԓJ6V\nmm-aI\rI޴#Mw@IJ_$,.S>ﯮڕH+VzJ9ۉ "l(T=C׮]ڼysը<)nS P U֭[MI+ⒺըJ$;ym^+p,[,'Pl۶\vmhIW\s~B vT"l(T=Tj8ym1qWg/6>I閖U~n5Fr ĕQ4K Rh4Ah5F:-^,4 ej|_ L1?^E^,4 YR{.]K,1;;;#X@Vl(dN!ꬳjw{0^Xzum^< 8DeeeO[;/J؍ #lO?@N1ZfMB;^\83*l۶E (׬YɦVSNimER@ [կ~5f4yf@eAk5֮]:3%) l(,!I՟V+K) 6DeeeW_k׆ZZZbz, GP(BO?J  ITa@ᨪVcʕ͛7WH-U’LFBTuYgnwQ)CZ iHk p*BPH$.]dw]L&%pP,9@Ѕ$U}_ Z?!]R÷%.0@ptՉD _iFlٲR9o5B= WĒ%K:$hmm a?pBRRR÷Lؘ.--r Ȫz_lٲ⎎†w}vI 4f ’W_}k5rGuj3g엤P(/ @6Uucc㮏~\O>PvɱXlVCi69A4MMM;3m5Fʷq5X]]]Œ(K}m?8N`Bf T$iwiӲ@IpQG59J%%u9|T4ӯJhxa&rޒ1 ARԴ3AC+JRTX΃):?j80(kǵVc,^w IcML-]t^ 6EmsssW[X<~yESTHNoH*Yhѻcf2 (Tq58f4! k֬Rfd4nu46Hh9>R۶Ӓv9|KvjEpaE$r-/KFi7tSjF"u ~W;eʔΚ϶puXxqm}v;|KYf9i5da:?H*я~cVhmm5֭[TM{8|=a:?)-Ƿr۶Jv;n{˸j5Fohmm5֯__j}8}eYiq 1cFG>#7Ya۶-t #4wyihmm5^}L[ɦiˤՐ$0qGuvX^c4ֵ’ʏ9L[ B!uhefwSLz,_l52 MC9]Hh4J =;;$5p ;PeV#$裏11Irt2Mk5?N2ŷc@@A#Hʕ+>"l\F'u[8jTuQj5$ɲ, 4Mn;T~l9j4D"_~(a:;#; VWt!M8AQ4#zkFX~}\m5$Njg̘>//m{$ӧOON'tu .jfdժUuiH>J2;WAR#<͓ӹ\M@1cF{>,_`O I3gNh(y Y4Vc|h9[j(FZR86䳺>:kH^tF?hO@Z4624ÿկ>-Ǻuʬ՘$#Y 46ٳgj1j)V#1ں,L=r46l7mgr)m#:zIիWu5663mWf- lGsY1[-5\m;-AÐ8Y0 f*|Sn/_;W/KBҶ R')}w9}C_ M9s:ĐlQ۶%筆4V#3h9>pk6 4k6lذ끌f"k9N9ɶm+i÷LXz㾨䮮~I&r o䓺O}Sx[/l5 IJL2e?5aE4E̲[vINOT5Vc-_1`h'fXvevH΃$UeidFi6& cXߝw@2dǪw:Iz:?x< "jΝk5Ʋq]wTXͮ7ӵvei"-Gyyy6d[in{m˖-3gNrah2ʴ* &",)/A8~V#s2˗ [;;;K>Ff0p%y;@6LDMcc㮅 z nzkӔizoW_}}Wsܹs1piT6WM7i1R6'4VC{P]|{o喖t:3gNSO=W{#aڦSN͋]F1i (-{Z H_zͫVʛ{0W6G/Υ畒v(VC6cƌիWmƾ+[ow|Y m5FOӟDk5߭ 6bwѴtRkȦ~l\@)_#e8~_%%5J;a׿uk<OL Jy,f n.2Vc$'-ǃ>XuRIlfڵ&O3ԟviD\d—H {o\ӧ&pXƐ~w˵zj9n Ĺ LicժU-[i` Dy';gΜGDh4گag- Nixc$MPIIIFX=N>䶭[V̜9s 븹p*0Hmmm+ڼys(# esZ| n,G>29 pdp4#lp"p+$풴7 4-[+28∃y"b>6. lp"Ɛ7ƔVCBeee t[ZtxΜ9O=i6 lK[ /¶Iv>2'-Zn2 #}饗6ZʵCjkki6 lK[_=&i{?2 l O^m0]qͷzk$$o6H[o|emJdžÆ486lذ9㎦KZMFpaX~}\m5$)H$3_5~'&_tEl~vMMM`}"l؟ط^-]w]Զ>I\˲Bee~ڵ;7vi܊{ ؟ɓ'9sfq?F!Iɓ'˗ [;;;ˎ>,|$aU #}z=w+;wF8∦񮻠daH .t=S[F%ut\ 1r--thΜ9O=x~6YG0\E, m/Sn6Mo ø,Z;h1 #}饗6Z*D#lcX`[7Z[[%p25mmmF.CǼyW^aNkFZYƍ24ux=|]†a5eਯOoذas,KG?:hҥVo*n,݁J| jHҨ_5~'&_tEQo@qc I%i_y^-]tQm)InG s8֮]m]>ϕ;x @Vqc I5vXWUUUNwQʥW_}5&.FxF.[~w]SO  D7|.뮻.jv=9\( 9:/W'9sٳgWei @6T|;jU!Irǧxw|m[l͜9s(/a7*@qc [XRlɒ%;[VX*.TÎƐ\˗Ypҏ|#ַ4"l-а{̙npG}4r܄3RnZe˖ʷ#8izL' P I xꩧ:ru͒p"Wvr--thΜ9O=A4"l"{^-w_LAcBHӣ)[E=c-t饗6g @᪞7o^`K&Fkkkd.NÓ'OZq3p̘1gm1 o֭"liKx=\}咺%um6)noUӨn/aIDAT\P@a>cjH믿>0<׊f̘ʴ-7ǭYdIc,K)7 HsőH$zEtzQ. O͍1uTֈ  u>s5ӧOO/?4@>w@Ch5bmmmƉ'XW{m?$dPXB_W[[<8oi9;d<-ǃ>Xlٲ:I,ZOҖeTʥ a(,O8f#<@SLLiv`( 0G"+rq˿뿖i7BX"bv|9Hk?p=3gժUܺDDo}kӋx'͗q-r%l ? nr,SSbԩj%[7nZ&ic( e5l ^L&[:묽|q xB*>}o[?,S.N[Ҭ'(ZxqC:Z% x*x'?ɬ>A7nP7BQ,뮋>s5ӦM{u1f˲*Ν)T뢆a4A*N$~瞫җN.F4|z xh6`_W]}x饗ST{. ˲=umڵkVRq&-ǝw90<_wi*F O/ $սe˖Μ9s\Roo/ӨYG-jf^$Sjk80LQIs=L颦>i4~GMz=6$ 9_LX-]wU&iwa$)J}C?'N<7xǕ}l!)vgnz cƍZCE?ݑ.l… =9}"q@6*b{,Xьl9~aṡO66SaFʕ+!}ӓ?j߰_#8"/֘A!4ZjF__ӨYGɐT~gvƁttt[n-X(*))ɻC2ESy,Kp ypz/_^*[Ro+4SDdajH2I^á⊊ ?da🐤ŋjkUypWWWD_֣DlU *xOPPXbl _|nYVCGda˲bGuT`PepIR**>|l'K(JE/@{ضmC_3gf*/eABW"4}jHRww7au _bGy_$uy= D, DHӄ @60$u}lǒgϞX 2eX?eml!4768# 덭[F)TT\UUnT#l`H*>)T{oTR$_dYVdɁ6YE!~øS/v$I&"l0 @/b˒dʶy"lh~q@V6(?3^㮻*SV=DN:JL_`APA7ʇS$azq@+onn'|ȶbI~lm"UUU  @6o@+I˗/,JSvF-G:ҪX//b4Jz HYY٨HR˲2/L&}^CRIccaA#JE򕯌l oΈ0<4}CXr> Ha6. l2Mso,׶P /Dm8&ʶmNr0f _ʚ2j5Fׇwyǒwi<Ȍ`>'FeYeGuOწ1PO>ׯg 5 T:묳0;R{r䇬8\> DmjUY$f ̉1^x}-*I\ 7-GwwwX K@~VUUy2&WƐ\3 JL7jkHx,:Cs6ܵTfs}qXP;^#Ry),l qm56E"c֬Yyssjkaqx4;#<ғC j9?ǐġ~W6EѨHx zϷv˱~zP\EWz衇z ?} z Iɓ'ſQ6[a˲6=n<`qiw\ †oFq(  ai%sL^z饈eYAh5Bm_r%7׎;zzzw öܹs7^R X,Quuuu i~:9ܩ;weFYCCC`glrxé^Def 'B> Jf2 >*D"۶^cm46Xe544Ak8DU}3<3cX a@*2=}k??A9uY'@#l5kVɶ7x0 <Ė͟??acӦMũT*pa@X,zNQb-4,X끸%L- '@YA:::踼)$m۶-(aȽDÆeY3g8a ,2:@NaٳgOHSyA|p{@X*Νo'Jmon Oi  B?,V L49jP*^z饁^IR=_@nE4m~n6x!װ;QrY^^ذ!ɐOĎ<@ObI4 l9TRR2eJ \. |܉*SyP:455BB5U3 RT8F,O8ܶnݺbvaȝ$oKqy%z*IڰaC$J69Cr'b&FWWWw !]]]D);BXax= O;Od2?@n6"H@i??'Iöh69Dr'RYYf0}G=؝^"|i|z @Xihht&V8*D"믿믿nڶ*@N6IӦM t؈F}O8蠃vpIڲeKDR-#m{}^MX/ӨB*/L&~[>.dҔzBr#DұX,ߢ755DYfI:- @n蝨$#﷼ IUgyf?jx#ep@6(,+S$i{m.R~nG Al9֭[gR)r. ).-- |ؐX,fKz=0$՜zh5F Rhoo7a~6( NTC&MT8>u9:yqPZ+ JJJkjj "ltI=i{=Q%U}ٝρcma8txҤI1j…dz=jNNWk_\@6HlHRsssƊD"n)Vc$u֕R8%@ض6mZȕ/}K$U*?~?O&uΉZm۶YOÃPƂ f\(Fu,d=p$I+")w l G" C/vJgH$RtҤ[3-˲a~6QÇ?ކ=j<BXA'|Yf/[Tj.ྡྷB8=|47xciqys_ӴiӺ/䒜/o-;S"k$X,Vz]wMExw8_9P(o!wqG;#I8poJ$А~Gy&V5^Ô8.=\sI&I:L'g`A|ڳ:P"t,_ܲ,+%)/prwPJKK8g(p̚5o֬YyuvvI9?fC&$M:uM7ݴŏ?d2iTUU2_|1Jvdmh ?>8gaq_xҍ7uuui BvOOO$mۦi?+bVnd2-۟ @@)!t 5P/G2Iҡڽp^/醡)U /"mK ? }ie&ЦUtI';餓y=\V6*iW@p_\ mxM6E55ྴ$ԌͅEnoxg¶m-oyΡ IENDB`gammaray-2.11.3/ui/resources/gammaray/ui/dark/pixmaps/watermark@3x.png000066400000000000000000001574541413310443000257270ustar00rootroot00000000000000PNG  IHDR~sBIT|d pHYs**11tEXtSoftwarewww.inkscape.org< IDATxyly}c{Jc)vWY mU)h UDET!+VMh@ ?Tv4PR*Rn ]{رc{~=kkߙ1χd9fxrx#"iEIse;(Y͍~G蒤򥥥D$iX\\l ))).BR%DD'x"d{;uT]zWuGGGSSSS{,t@үH'KRZIQIW/䌌s9WUyʡtR\Y VZo,Zr|\SNE7jWqÇkښxW,#IeY} J [GKKb}ӧOGT EGGG+tRVu[ҼV!C(JI{Ϝ9sѣ6.\ZZSu{Ǵ(6 SWkkИɓ'˟y@D4Tl#z$ _~*(=}}}>%Y333T**EY[}?ߝ @ p9s\k*ɪ-.3"/"@1%u] rV#_ǎxbetrr2T.d-i%IKeA6P =|'>_;w?X3::077tY  Z"vK^kO|_N^r*JU)I~0I{}^F>W;V$y֭呺S&$+\=*I5od6PHh5N_ _yh/M-(D=dFjYɄ35d6PHo~G>D>}:SO%Ο?LRIE+IDRR\$R 6$[:2(X,֘Jz}BȖG'tLҬB佬\X&hdR02(GR=3A~_&'''ssseQI Wy$UʲOKSW2(X,VJ>N}M+ZHy{j"f|Hj~;1AO{t:]hۓqd0$+<lm x<9;;`}o^HNLL$*dB FJeE,f0\mf'7Ij~}lܹs#<|r͛ղnF#j/2pSs<^ km0ޞ "";Ӳl-e_ YkFeܓ(i}l--];) FNxl I >bUilᆰmZoD|oBfnhkjjrFFFF;VsU%׌T8JRB+V/|v\Rݧ?K kwGdeB7rqdWތ mLͷÇ666vJ:(.I-*]~E^I"IT)3g*0N:=x`c<%]lWx,:IݲG<Nt %;B+;0*3mQIwx׻U\uD aDmlQ`G69Z`kB6 x\JMɲbDm8(&FԖrOЋi%Q&^HP(z2(Vx34FfnhDU14JZ4Nf*T%FbE}.J'\QH#j!u-K6n LD+qYcA1+dٮ/l`=Z 0*eiĈZ/"UտHVKJjʈI1A [m}Yd6ڬ,R-۫AQI6S&逬(&)i\h`c~g@@Lʚ;>|Jsc?@ z|>|Ǒԡ-SFLK꒕ʰYmr^td { q;kI<p$T?L@iԣ܃QYUB4&mk @iihnA lrYp^6./I*ۭ¿!iX҂KjY~R%UOH: + +"*nι._, .  =֬-a[(kIm׬/UKԿ'g dMܣf<> £Lt(d6«IeԴV9<.#P-xc/$ 2A6}nYYTXl ٰW~d6©Q]K;]RLݪOHt]u1 qdLʿi^vwI,+/c -i^yFdY\ǟ/xblԐ9~KW&, G>5eT(,5I(#ۣϔ۲ ;h7>ˑMknIm.M-(ēY1VɔiY(5ݲB<9 aF 1d69>nIUٲ8zIs7,%*)5}3eQ>8|6~c'Se;.Y&*VSX f%.Xo)S+hj$<]LEvR>Xv2V#g1 u9>nAaK,+vΓ헷잆|7IګK*Yho2U#]Q 4/i@QYVCe:e_L>(KKҰK &r|, Mu*N 2T-CeVcaYyNsc_e^>aׇX#"; #;ڋ xӞN,ErL=)t},ۿ2'fI7>ҒFd rd៻eWOlKRv ɝmMoq& @cl s| (+ku:6Jv>A!#htSV¿UȂAMm lBU>ݺ{)IׇlEBv:Yզ%\[n*ec^ѣ`_tԚ㢒Q.}l_\"hYdRe`sރ|F閚Y˜ - Tم; V- HG>j]{:%iއnMY7 L@tH%[iYT¦e7Ғn| 됍  k𯨬_w;owM* nd_BK[v&I{¿2 X ,Ũ*Qa"+- ge= ^n˲-ʽTlZ]|Q>ZD1YuM>k _TʋšX-,Œln|ĮHYF,-2&iJҌ)|e7;.ko '/mc^v(QЀ^d;lKTY-K1#(. ¿}lC)ףLdQ+Yƅu`%d5 irRIKR'*]"^lҔ񙔤*MF6{qAuɦMz}|PFୈLT'PmePR\R}[~.Z>Sl6V!+%vY#hdىFyaݱ >w[Ҁ,hȵ `HINm冁X޷z6,ۚz"]6.s1-}HKה&u@Uavs%}#ώ!&\җiI \-o4^sU6=+(2YSr(,¿l2ِq #3 o,E.} ӲE)4GV"6,- 2U&یMjqKHIu հ2(s@PL.ٶZMTB\>Uٿ gl"]_~病=}Cz PFP< f^[/ rk뇅 g@9&}M>ϹfF\?>A>U+0(1R%e۵$M+fDg$ eTWdGmMۥ3#muIPxu ٟS;k{S[`d=(<eˏ53.I-@fje;r4%E1%e'%uqd+xl&$FP8^DM6sɲK5'lɲ$ni[>GԠ p%%lA5YQc^MvPjde<^}n_. 2,bL6v=e3#]#_-]޷ 'x}nPVTv/1x,p}*E3[*"jYVJf`^VSYd_\&I&rX.J_-\ˢj{ *lSB{ )Mݐes|ܜ,3K[_Gw,9.D6^-5 5ec-<6Q%  29>vZe{d#o,u+-%k l!! ٸlFG[o_,Krӵ\#pNA^eG{߭]^n _ewjWFherqS\eIr VM#d}[5dY\vI: .2äYQE҈ 0"A€` ?QYfcue^[Rׇ@D%\y"lj-m%ƞ ܵFz}Ԝ,4Y( 7d5JÐ} "2iY0sde_2I3-xr]XP*%%%] dEdAC;k~ےlmfec5K)En/@aW/Y0$ yyD~qʮsPr6B{k@a``ϥ̓}ikbDVBu346ǯY%@nd EF꟯l5v0 " Kg`'YiL@aAmIIqI^$66W&KҴsF|yȂU LR~" 6UҔq#kjJ !p "jgcV&+!IwɲY!6lEҜlcl[RV^pj a@dzYTuYM''B$I َҪH'+KlܩLRޘȚtCR(9] .%ݎY(++[D"mmm uuuKoqW/Oz/yU^|9XTJ*eҼ,MEG~YTʅ竐Oeqoʴ,%`#<*eMi+%tyyyzϞ=ߙs;+>k[XXƵh,-xyNoɚ5Ze{ ^l.I%ǻ>8sL$ɔۙ3g"NTWW7~Yf6nd*7CB1lGB9!i!H̶̾mo=zS/^Ul|;/b۷t\x>%iRTvk\.s;rY8-."!/GTT˂H<imm(X/\=33S#|O^ ܨ>ICrB/q^j ɡ)6ő5 c>LN}&;tl뫙}34@xUKjT/˞\K[y6 0j%UbTcc;Ω3g,8OZ=55U'ܒ4. lIVqjdEXXH++e,%`; YQ暚&~ᩯ|+wLp3:re9n^@%2-"fP SIcFqK$ɉ__;d9|c[~# =UďM P):_eHj5Dbr?~D{>`7,g;% Hd{d"<إG&wlN,Ə?>㏻rqO{*!Sk\ֿqML=,rN {bh:u(d99~xd,ظ!27=Uɂ md{Snz}l!!t{Ǐ9R vu~jjIr1 IDAT=qIݲ^ZeKz}eOl/.&Qu&2"'O}on5WݲƸYGe3YYY3|`#w1I-b]]]׮]: {yG"7fggke)Qq-&+KV#BֿqY\ TG e2:;;GoN!y+Ξ=ےJbTPvɾq jV ޱR-M6Θk!ZFIUUU{Ňd%UE9ZT{!ڱ㠧2Iw^D:Ix|Ś.USSSS?55,6"f@t˦/‘S?=%A! "{}ܩTh4ַvvv(ȁ$=zԵ%Hu'N\;2^ BR69ٝVٙ= III^ۆΑ~c Y FJ tp >%} nݚ#>u]ҴglIc>襰Y?H9sr2Lɾqy}&JV#[|[:mzY1yeo)7iv4)*i,CqZQ!iW<8tĉ U ,?$FFF:+]d .(쑍1.x}"JHimmu֍ÇsCСCz饗\y^z9t+U8?|B,A oP+7e$e#|Sd, Rl4Hj_~e ,?-77ʦU1V.%oRSȖ]4YR6$%?яk_/Or".٤uBHK65l]~P3bY9'x*θe;700>q%٤.Lb.Y3;#j\RD y}LK.Q61qر]r͛FMR_XO!>L J >ԂA<*g޽C/^$lG.93qf 1G#[V>Y2-Nu[8zda,+9A6*%&9r$8ٳg$%E8pi],0gZe#%!2rI{F]6a9z93J-I<G@̸o$tY߈I-+*LU1؈H6Ϸ${z9!HtREnm3]\_ >[6iMT ZH2//w:,z{{cǎu`Kaprd[e%D0A/A஠lt]7b|S!'rxСCrgٳ 3^ P'B+d%e]=KA)` X,nA1[MSgϞm /-;l+{{a<vG^B((F}4ԧ>ug[Q:pxȑ#ׯO;wu(Cd566+U&oOiOV BFBRG?+GA/=;e߳(\;\p:lK(F_@jd/y}- B-%dF9p$xxB` (Xm'+!}k*<FvFg<w^GWSS055U+c;`J[luԕB#:mvv\,?{%ݐ 7%>Ay}uD%(T!IgϞuΞ=` ˎ=s׿X,V&u? œ %ϼa _ ezArs3:6RSSS?55U'&hIq **YBRRlKwȑs :,G%T*u[܅52Mމ*$G+)1~niiҗ r0sV/?L0PZ:eY kVRl&oSE$!Q}Yߎ$d9 F&J9 !"WalSyYrI F#r/]]]="{bia (ET.őe@dy}A5=444&IgϞ8[#d9 #wiPxQp""])s72 /{6jhͿuttHzzzte5z9^/~1K/u%r @!uJ%(;J܌YldU1}yوH?M_d9tImPQWbWpk*Y8! 6:2###'rI^@(uǼ>HMg"%nLyQF[]Uv!KJjSSp_`Pd5m^UζWƫ5&o׮]cW\2Y,;˩vqꔕ0l/-[lbT*T^c:U.t_ ^o\Rd%T܈늹gLR˯ɓ9rĝ{9;{},yAyICR\^"ZeK 4b^ a,@#dyffB"?]dvtJ6SFǏ:,@#_FeC> @kM"AلmjYs9S qb]ue7-,G]cccrllE6.$\U.il[8Ӌ%&k rL&0b6bv+_xĉ^! ?&Vfq;&QZ5\ݙqYPe[nS`cWGG@Q6H!:,=>I/X+".v1+>/JఁB1I.>|[A:8c% x}*᮸Ed f͑O]U ❝8wqɶy>K/9a>~ٳgd/:]JDvW_,%[-Yln/krl~jj*)g{-Ȯe%lFOWWk& 1n!XoosرT[eȦ,ؾ2I{dcn$Mx{A.?d5"86V___3>>^/g[Ͳ\^Wz+_~v?ے=8#Pkp-sgljɔg;٬EIT$*|$-,VVecpst]!D4LӾI7pUJVkJvLwf־Jp{ݲF7BXn`t:}K6$8 +$6"*fAm]#k&$JI{.Wc+pYsOE (fA<%LzԩSL7x=!n(m $] yZd&Go "nk -_;;n zƘlV㪬OGlD2 qGV>u]ҴgA@lx{30TQL ^R,(촦ƾ-kZoʚUarVQSTPS jq/DYmmw[:-HJ98NlT"{7ғ;/wZ@W!4Z6-1Ie7V@ 6{キ')6?7ӟVR[nU.--Uf2N#7~#ulmQb,[pgv,{>O̓9qW)f9cccM%^JKZYcv{wH+IU/7]}}}66^w"Zrw 8oG?J%t\TLɦ?mjl)S"ONS9q/9(,Goosر}4Mg5HuX6J|W_}g>S=::ZNbRVf rYQ#)f&MؾRr4LMMEdtJzkdvVe=6>cFϽ;%8yd矯KRIY`qkG_^ LxK%wȅ,ɓ'˞x"=]}^)I7==R.믹(UT vlTHw÷kW_uofttann.# 0& h4ijjo~slœ娪j-r eo7n.Y A$hnjjiBbueX;44 Sg.5 txkk뭏\X>`ٳgw.<|q&!M+7:ew˹O5eY /U; 6:r4z|߮5yFԖJIj[gΜ!]0f9xw:56YclYW@F<vn& |IӟtpBjT6o 1O?~|ԩSo lYV+ٻhcFV,.NY_DFGkkИ / / (e:G:6,8$GC g l €5S(<H?e<@<~%] ؞%YkTj{dӓO>Ͽ;zk3/c@@8DdCA6: \a3eƠ4P@\hbT*i|_mIRqY&l%JJ _tiUгBUv]1ɟ1^{mc 6E¢*?V;V~snn.%XNT,Y/➒-58^ً_W $tA۟(ۿtP†%Ekںoo$x su2LFRRg<8| u-訩i e{4rwdeWWnbJʡ(r-K?8ɹssy6ͦXrqM^M<#=o~^ȏܞXړmCЄj7s[]?6)KLB"F{+fX5Ͳ 6GRRgUU#GN%ˑH$:RԬ恠i-'3n 2bhT#&;5555f3ˁFK"iIgff=Ю{QP{@d#y>>#+M&QxZĘ[x 9raroYUkԲw.\0 .u~r,o&귁hPޭ|.]fE`٨T@C ~xGb9и, tŋdٳW(~r$ ‘PXmXd7 V٠LnQؘ6> NGJ҅ʇzɓGnn=иE1p7T/purJ6JƅeG`v_w{_WZ{,rݲtAZ$>|q~,M**If.Y ҵ`ÑtԩSwh߃x<ޕNg0A 8uUbgqW]~Ioع,;/8xh;eTh4:@Cw/G[[[C:vN)7)ŋ[>_=zW'&e/ɑ-+MaYfڷ R&CO) ׅ=Scy;xبFvؘ_D+JM2WU]T43L===ڷo_^Kӑ{]hςKT*Cu>1߀_\;d'Pd2d2x!˜:qY۰3e>s̅{쎰Yx<ޙNSO#IX,?3/αcs:IL Hf9dVTV!1h>Ub?OwgK^w_^h/JIG}کS݂WD{*JT%i ɤMqzWk1mvm-A^hSfXҭӧO>vXׇ;rݻR*_j%(А-ru|^6zjӀll$뮢Y 8>7˾muSpoo.y㏧d|}h651"A[s$uʧW6t|VJVMzMVRѣG9sfA1{9d̢G xFRc&˃\hF輵F+^XkF"yz׿Ys9}}}4#Fit 3˱o߾)Y@[jBcDVT(ѠֳT6H IDAT7g} ne9:;;D5~w9r8_eY 7⫖Ԝd.Ivhac{d7M_4ٙ.IFlppQdF?3d8YD"1% xE.2 1*py}?h6 6bۿ`CĪ??4# 6咪'eBNڳgTwdz<EPf"π6QQ)i/'p>}:rƍz4}c=VaPYUSknjjr53897eO\,C)|mx'?k$<::*Q5GњSN]=qCrv/G?9(*ILB!?H%%$]+qVRbW|nF&Fr3++ (*I7oNmy<#kTo)‘]vM4؇?]/8}tdttF` wtZ{hL̓[Yx<>-@4}6㲞Ҫȗe"0 6ht|g kf9oNݩPIfgg:;;NIlT1666"ˑCjvLՠ| ^ |jܹsNoooQσZtepp*i;,." Z-uuu5$1)vID&iJҬrn`CFp؉ے?w(MozS&Ϩ4/0bKuք6rd^cyX$dXJY/؈566_c=W\upGJ3<'<%WPa%:Y)YֹT8:% @۳C>p񪹹E"IIz&T#o{6p_LR,Ɍ{}(FJõM@1OZ&@___A<БN%"PN$333]eǎ-+#)ҲKt&A8dT= U \[y16bj|{ߋN4ඁ٪Z&r$iNP,QI'x"òand˧@6؈&:ٝFá$]o{b^&Lx[Bྖm e^/~  `OOew*z *%%>O5sòUTR,FuuuǏW.C1Q 6%ija}DR*=-7ze=\ W9UL;__ t/@1HeЇšzeU;*%U} ;qС=Z i`ÑT~ĉ^fb'#hh}/:XM|س*$> f:| aA䮩֏ERgΜY4_e?磉UI)?f\{C񶷽mA> ?Ÿ[`ӱct] O=#pܔK{vOFʩ f.=Dbom޶"r2Gl$-1111Q%mI7yVF}}}scN ɲ +RRӎ2̸fj":ˑ$*|`|copg?+K1qg6NXWxR*`s&K,uCEYOl444MY/IY ͷ'OF>H477l+pСjde2II8y}d2GY1%`*;^/_ߥ%s˩~[{>#N&_|.u ػeUqIu| %&lMo;EŋN:NT*8x`Nܙ3ge1ۚ:2I/ Td2Ӓǩ줬ʑO]ʅFFd߾};_̀ _BLҼOX4+={ERH$fEvXAk6A|`Gٍd9%͉6(AGuQXY/&D gZ7ٻ;}ϕdj"K'Ngs(!)g:))3L;0&)HąHI31db!qny^ȦEt=szMlZz-)V* 9k||<`X</ V(Xa_zC֩.p S[[[[=cz!fq-D=NQ@_AU^'l9idi`D,?'Wx =ܳGITdLD8(Q&r'e2ŋ78qBz1z"B=*?.0?4|DFb`){Px']ޭAF)IC*Z a8Um7&r= E"2 )XFYVkэh{7 9,R޷Am5|՘ĶpuuuA*,@;eɰPd}o޼1O<@6(O>-kZfM*˜_PuYXGʛrڵˆ0?) 3h=uѣTTT(#?( %%%l5f1qammmJڍ?? Y n"iT h2Lطo%[n:ye<ʙ3g$wVȑ#%0l0РD[NfVꫯ~Z3p$gŜ۠L%𕔔tj#8t޺:+B>]|\"# hhh Bl6l?ީȖ#q?eDOI^l"0V#~]&=ڵkmR~F#2 >k^ļj̧3_ 4~V# w}111d^)^6L@X,p0ly~gr̎sarvttwcC\K,'uuu%viX8BP\-Ǟ={Xdnݒ$gk7&1pLo}B~{5a֭:O<H{A  <00馛vU /8S&ۍ.b1=޷A`Suuu/qqHSDvrKK_rNߘʰA[$/~񋸏cd8[iii<8[vnArV"vBo17oNg188(a2О={s~l9'FdVn]wY 9[QF5ah#HZVV63tnᆼD$ՁAe@NH\'S̈́#:aۇZ(E^sbހTz;:;;gm7,]]]%)X"z64_}Qu]Чl9|A;BdRcBwNp[mXj{0\L@ۂ(.#gZ>dZǮʸTJE8r5(uG"nW&" 2@~~~'OU8MlrH愈`TQsj˽yyy#W^y)3GKK 5({+S[nM|{AC={z޳ͩtvvNnN 8YE꥗^+,,oV_mmmBdJ$ TGR[6\yyyŽڍBD 3ݻwI۷oO8po9uby"q7Lk9N'%3qߺu_Cmmm y0q@ #2Og{E n߾_SSD*l---I $S 2@nn֭[1[H۪L( UUUZSD 4ltuu"vK~i߶m>Hhii[(oOGIF&7x#,콽}`0(YEEE\HE ,zzzbD߇/$L- {9!>^{- UWW%īWljQ"r}D-b`߾}R=+^eN"%Ȩ E`p?Ϥ8*++G͖e622b`!3bن].ˇ80 DO,p C;(AadʕCo?+?8AX 8/DlW_&373Y"IYߤݻw۸q?f֓'OMQѬp8<" cy#&H:t(`P^1q?"sM%K={?p֞={ ֭[裏kb Ջ|4'2ػwo[*lc---و&2999ZXF UUUw}ۛ=44$&qL 9bXG<'|Z)⹛zLL46 |>W_8l۶ /x5W1] ~a<6IX򕯤7KXvmDFPK/cjFGG%I vD&7!:=;|- k .} y ^7^KHO?]`p<3s:@AggxS'Ѥ‘{.'3Ka%dxJ=΃2tL(Y###YcccYHĆ֦/0p8VjJg%3OD nQ}Im3l߾x>f388Nt;4I:BYdX,IՈLBmmD'MM 2bZn__ ޽0A "UL n/xwN'@MMM­+((Pp%,V$x TK.]կ~[l8 }`KJRx~U̩cjƦno OEqUWEAw\=[nح[N:`бvZBdVg5 ؽ{ ۷o۶m!zBa&@$I_|1%?sxꩧ6i fX2娯/ |I2S_4/_>K/{Q__qƮ{w*nr}9DsI,\ IR/{rappP74#////<(j6FFFR mۤug;)k!2\1=9992 @ަM4o5i[`cڵBpQM}8?uI?z⊞J߯]>m_H]/-ǪU Br zj5ba!j=Q;OĨdUWWmذ^ؿp8QB.ۍIzi9>?h!Қ@ur*ޥZ2(]m6o~ /p@SS~eҵ"XY]]}+ Z܏t(t:p]˨-GeenSvmm6zͽ;y>i&'<و!@D0sΜ 2'[!sV뮻NWs1jQWWW  Di3fۗ;'ӽZ~_qqqdxxrlikXLeۋeY>9uuuڭ&NZ .mnnvi mZ/$YFi9 u@(5\cg?H/,W^yZXFh9*++P- Di"ȑ#RMMMN>bS%k``666fX,P(ITȄ|D͖l 1xq(ip8#H Q^[AJrjȨby=ةkfTtGK IDATmӶ'NdɲoZ֑9hѢUV ˋ{{{#QtDɰXOַu/up8dYNM`^{b޽Z/Fm3-wwuu8~@vacƍ۫yG}o1lz}^JXTZZ:xan?lv|CD"OCQUUe]?\ @lX|UWl۶mSoSN"HVmttfXFsrrFDQ.--Yt;"/e4ۧ-b@S}Qe˖49f!XYQQt>3"-[ScLUhbFUnIzmj|_www'P믿/<盘L `Z]]Gj?6Q 3d̉hZ@Y n"Hl(Yn[oo՚+ b@vvvƵE(S `k^z7x#JWSD6^kuk\rI(fcND СCn:Ng$IzL8x+ZXh9\.Wq$cHs,G"t= EQAaʖ-[~CEEEˑ6(/Kh)r9#KCR]3LVB|͚5m 7]9P"ݚ#wq08$N/_F ,\prTk!uHQ˷jDAY~-O娨E[HMnO>dP녘Qsss Aۧ `016+Weي~Dq}Ky1H-f 5.]V#<ѐD1[ؐ?裤Rx>6 OVED )"\s54l6H-ŋwi3:yd vp6p'R{nP(Б(Ey睴86oEdlsm+ݻ7T]]]NDhnLSr8Ad/;%z^ %g8qbέTMMM/\h'D'I۴11$mT /K*ZdIɖDdPsz^{man!N'U[R\nN箬\Ll#%Ӣ>/H2DO}]D66p8"ҡ_eǎ;KGKwq0r{J6ϡpTUUٸ}|l<b(r!z8GyD[JG!qSvV^=^[[c@'}Tj6"ȧFSS??nbuLdl}ݺJ+vs+% Z#yy')"'l )$m(2ݝOD eYmZ/$Q (*`AՈ UUUn (6 =SYMMMZS."Jq jTlg?mT4,=3}Gb^ybDOT^ /UVI'O̕e/Dd8nQ$z!㕕+| nsyrVȔ=qE>qD,` 2,FJ5-V*mP,ZXl9fVUUe+  "ӊE`c`IdF%EEEí0a-u]D"28Jg+v\cO=TRٲ8,$IB*"2 D_ 4̩pRd-ŲݴiS-_HXac",gAColXz5OX>{UWkG[lۧ2BFYL*V[&? `bƍ7nUA3u[)@k!c I8j6/}KeR 2Bi ß9U3)tx^OOOq0exQ͸jDf9, o' j_ygjlܸQ'ȔmU`S^GQ2ⶠD9sL IZ[n%̙3v-iYUtpL_#>NDa3$"aADSB!ӜJ5Z+VpH,W^y嘛wI42U  _QQg/^'O`@hi$,CV>tPJåOrp8 k!"1lLGN>56+$x;e-ڵkEz=D=lL$Irg˖-A[zuWlB}}}1i0h ND3੯ַ>}zlD %M䋢h;uTO,[744d )2K"ՕoAX k ϼoY캺'\ D4,:t\Zq:%$z-y(JԬBrL>Ug_DDb ,;^uIh.CH|͚5ƍrx<N9ȸ8 NDs3R:(%rDQ[x*ׯ_500o '0l\2:l\ve<ʼeee6Xub<̀ۨ(+g-ڂ ,0Q $I:B⡷Yܼ<*-LcvCEJe62icYṕiۋeYnz!Za $ɐzxp,e9 <lQ<"dA ъ(7@iia(r$:<^VV+rκ~0lQ~_gV~FeݎO>$B+۪lN* c!q+G_%S/a7YFtҥǏz!jJ*ӹ@$f5(˲Q*vnn[4Է˖-H$T퉈2 %m_[SSLj9Ng$Ia=Zd='Nz!v˱eΝ;8@w ~ D>}52吸X4[g}@4(A Ds3bkղe8$nlŋD8~8%IrsD "C=ztWekkPRR ]녤Ӛ5kf444$ /@ !UGDD :!R'7^FxѢE}MMMZ/D+q|(D"@4l%ۨ(Q}}}s޷amU[ 駟6l0 :"@[VEDfǰAD(gϞ9aqpHx]lOhhh9Rh1Ei1lQ2{9_eQRR I9fr'?0}$D^ D'Oιj*_| :(n t՘tG\pam~SO=5I~>w.zX,28Gз-ze˖eYХZȜ6h>ȏUF)}Ng$IMDBl6h87zj96mfC_lrUV9%Irz-DdnMDvwS6mJzh9AX8x/\7lFWWW'TVV.AACDf0>-$=( }wˣna4( 4eMD#b믿ޢœkr_gg(~"'tvvrK֮]k_Z̏!eGCsڳȻjňnb ` "5P~ӟj[{9VZŻ6s:}۷oz-D96H-ǏOt8v=_Kal5PYYVDDi_D;t(r+v NgWѺ: ⭷*BthXQfa "Hcz!8\.Æ6,UUU [pa,Vl(6HMZ֋*- Wp86o|֬F]]` @i(0l[[[u6&*pXBH7 }k3}G#Sk9&NbyyfF,rK,` "c T wtt8^D*M aC=9(G?^Tz{lSaRi0 ٘j(**bP.J T:p "c T;wfit(..搸:a#~ٿn5b%p| P Q1lQeYv666~Va#~ tzZٶUs=i^6(]F ֚~nG0}'^O=pӷ55ziG IDAT5 EDD/B"Je˖;v^뵤_XQSעcJJJ"gΜIF9jٲe' "J}Z%, R/ rܹXP8 "J'+gݷaƖcގqZEHj5b娪uvvZz-DDj`AD4 `;(r(DJgK-/~<>"2 "J{ΛrSJ^7@QQ'P%Jo'V+E lr~Ոv/Z[ZZ|DDfpժUσP"rkP7jKcE$ Ddrl6H+ᎎy߰d܆@[Xm9&]LffbdY>~iע"'tvvvjTHҗT(J'yzDDFf(b VP-ǥ^͆@]weV#V-7ߜ((4(C "-- >AXO-ڵkwivvvf\\-Nj/hݱc'(Xl---T: it娩A2 "ҒhKeYnJhr\}ՅP( ۉ(((jz!Zm9;?8k( "-)dر#EZ^76,v[ƴb[Cp"H DqO?7嚅L =ذ d AfߵBDa9uԼۈcӦM 2gf3՘_hz-DDZi-_t?q:g9֮]q#i{RmDڴ^8bIp"Pl6HkYņΖCLJ%(ꪫj$Ńa6`:['O,΄u8_~yHqBD "҃Ȼhhhh8ԷfW\V@qqq(P8e8 "҃H(􍸖#UK.1{w83ºo61ުZưADz gHe s1jtk=x'o v2"x DC555ؿ0K˱tq>h0|Q '"ŰADz! Ō[uֱ‰b1l^g}#nx`'裏*f"!p(S D9oתPc[UIIٚ 25pp(hz D(MMMqY5b26rEQڶm<L C^ 0l^Pvarlذa6Ax_tE3ޫ)cPxk!" "ғG}4VXFj9DQ4V\QGqi/ʄCDD3c "=&6.,C.(8,X(8ND4- "ғ,IIzo9 l䈢86Wl-=cmiiCDD3b "=0gArZ aÿf͚0K਩̈́a&oϫݘǖ[nQ`[=vO>dd>bR$ND4+ "KOr,]tn#|F8z6cZH6Ho"pXmFzj9A0ꐸn&(--͕e^ 1l(d{_"rx<mj2Bਭ9sEY.""s񴷷_{#xNgURRVc}2힗^z)3 BkkPRRʧIZEE?(z^  "=?OVu饗\2eF,=\s+@k!"2 ݽ!zC~}}}Z/Xzuo~a9acn_?O^^^BY{=f("r>עEQ`O&)|r\xY-` "Jl۶͖'L4ʉTNݞOh-O|~p((a DWҟ紿OWzF,Y$"&i8N/0)MDdt DWvMތXb†@CM[Xl9:;;̆a*288Tka#jժKb@;V:+R[(1#Gz!@jNa)Fj? Dv}RacTXvZW}}}1g@DGl6H ?8)-ĉTzm7+W !hÇ  7?""=b "=444͸ڳn[a_$BY{E '"$1lEBތ8 6˗/7Lk# :::$"ŰADz&a=WK6z>2i9ynsP6HEQk|aYaBö ֭sJ$Jђ2nM~i}d[իW!Dg$  jĊ7p ^@+8ND "һɓ'u6&%8\.R@e]6B϶ 6)2 @W֦7sJK߫BRi mmmmDD "һ,zx3xG^^^†i[Xӵ۷o0ͪ̉anmlZ/$Q_F&Gee \:5^",Zfݻ^HV^= 0۷[,w0`< "#kr@lrΌj5&m޼#2AD Dd^r+mذaTLccp?8ND2 Ddgr|>†@]wգsk;(0@z-DDfŰADF0`z!j +W6a3عsgVWWW.vBDdf Dd5M1AK=*PWy?Oi5OD6(.SmڸqZ>׿u֌k5le9@F-"tb "MlLCtVzkta(j6牢8V#ց㦛nSep""i'R:-F,Z={X;VhADDc "#)Æ jmEq? 444h2}vEDDa "#RzVD*eeY#|8P8%"2 "2 }n5t *Ǎ7ޘ(T=a@~ n/MS[*{(r!9""1R&"Jk.ի^Ǭ~@|poݺ'2544S VDDaD:~WCo L2f0S&@)HXZ~ۢu)ZA pJ]aEAe<P89X(дiMG6̝;uΞCg~ɽ|ZZZzRj$U7773(KR!-a@|`xo|qIHD" 7;/IS4z@5}~g&Iz[@4+W Z7ި] I՟ԧ]V^XbE}^] Az뭷x/Ҙ E z^{5kƍ*hiiIHz I5s |qmر#&ic5(H$_|N:YqWk[饗[JTx4(rr ը-V/Rs5G IG<o͞=F.Z%mreY/ҖO9昃l&icf@%>#0-[oVCjfϞVOmO ( Ap7 NN?m'G,wmd:?iii1^{*-ioo$l---ƺu*l*@Z>;F#XCT=ݖfKmV²öuATmů7d_\ I>VG6?Nd]vN>>ohoo7DiVi&K+7M??_noKjz,ܣd=ׯw4jNl5];k֬@ IP+ (0 Ahd8Nlu  貶H|>V裏n:uƒ80a@euVHRGMӌ\r38r-`4 gݺuYJ5jlw3f|ρn!l(=6mZXdJ(1M3r7:9×Ge[< jY֤o[aGuT}*ӦM~W3^ǰ~ٳgOأnxBJRYi6[icGH~h9p _Ҿe˖O^xᅡNvmjj8{X}-`? Eϋ/8oHgg)gӧL4KnMO_lu B1E?x&F]ccca?B1lw 6 $>jX:ܔTr?[XPLh`Cjzg~l9mu͓|P{eˊ;;;KՈH*O-mV/ a@!qwڃ>Q1ZdHlٲ⎎e38+[d#ᗿe 9i5j> ?cVL6T*ٸK%;xy|;j5FʗO|r r IDATSn {=ȡD__ߞYfT|ߨ%gO2kVF{{H$2z_MMjjj} k_Zk/$l(<k5,.Tdɒ#yrSԴ38\51Zhii1TE l(D=8ocʕd2Y.g’p\˱dɒ ۶{l_FEPF]$~}ejTWW\xD[?O7&D BԓJ6V\nmm-aI\rI޴#Mw@IJ_$,.S>ﯮڕH+VzJ9ۉ "l(T=C׮]ڼysը<)nS P U֭[MI+ⒺըJ$;ym^+p,[,'Pl۶\vmhIW\s~B vT"l(T=Tj8ym1qWg/6>I閖U~n5Fr ĕQ4K Rh4Ah5F:-^,4 ej|_ L1?^E^,4 YR{.]K,1;;;#X@Vl(dN!ꬳjw{0^Xzum^< 8DeeeO[;/J؍ #lO?@N1ZfMB;^\83*l۶E (׬YɦVSNimER@ [կ~5f4yf@eAk5֮]:3%) l(,!I՟V+K) 6DeeeW_k׆ZZZbz, GP(BO?J  ITa@ᨪVcʕ͛7WH-U’LFBTuYgnwQ)CZ iHk p*BPH$.]dw]L&%pP,9@Ѕ$U}_ Z?!]R÷%.0@ptՉD _iFlٲR9o5B= WĒ%K:$hmm a?pBRRR÷Lؘ.--r Ȫz_lٲ⎎†w}vI 4f ’W_}k5rGuj3g엤P(/ @6Uucc㮏~\O>PvɱXlVCi69A4MMM;3m5Fʷq5X]]]Œ(K}m?8N`Bf T$iwiӲ@IpQG59J%%u9|T4ӯJhxa&rޒ1 ARԴ3AC+JRTX΃):?j80(kǵVc,^w IcML-]t^ 6EmsssW[X<~yESTHNoH*Yhѻcf2 (Tq58f4! k֬Rfd4nu46Hh9>R۶Ӓv9|KvjEpaE$r-/KFi7tSjF"u ~W;eʔΚ϶puXxqm}v;|KYf9i5da:?H*я~cVhmm5֭[TM{8|=a:?)-Ƿr۶Jv;n{˸j5Fohmm5֯__j}8}eYiq 1cFG>#7Ya۶-t #4wyihmm5^}L[ɦiˤՐ$0qGuvX^c4ֵ’ʏ9L[ B!uhefwSLz,_l52 MC9]Hh4J =;;$5p ;PeV#$裏11Irt2Mk5?N2ŷc@@A#Hʕ+>"l\F'u[8jTuQj5$ɲ, 4Mn;T~l9j4D"_~(a:;#; VWt!M8AQ4#zkFX~}\m5$Njg̘>//m{$ӧOON'tu .jfdժUuiH>J2;WAR#<͓ӹ\M@1cF{>,_`O I3gNh(y Y4Vc|h9[j(FZR86䳺>:kH^tF?hO@Z4624ÿկ>-Ǻuʬ՘$#Y 46ٳgj1j)V#1ں,L=r46l7mgr)m#:zIիWu5663mWf- lGsY1[-5\m;-AÐ8Y0 f*|Sn/_;W/KBҶ R')}w9}C_ M9s:ĐlQ۶%筆4V#3h9>pk6 4k6lذ끌f"k9N9ɶm+i÷LXz㾨䮮~I&r o䓺O}Sx[/l5 IJL2e?5aE4E̲[vINOT5Vc-_1`h'fXvevH΃$UeidFi6& cXߝw@2dǪw:Iz:?x< "jΝk5Ʋq]wTXͮ7ӵvei"-Gyyy6d[in{m˖-3gNrah2ʴ* &",)/A8~V#s2˗ [;;;K>Ff0p%y;@6LDMcc㮅 z nzkӔizoW_}}Wsܹs1piT6WM7i1R6'4VC{P]|{o喖t:3gNSO=W{#aڦSN͋]F1i (-{Z H_zͫVʛ{0W6G/Υ畒v(VC6cƌիWmƾ+[ow|Y m5FOӟDk5߭ 6bwѴtRkȦ~l\@)_#e8~_%%5J;a׿uk<OL Jy,f n.2Vc$'-ǃ>XuRIlfڵ&O3ԟviD\d—H {o\ӧ&pXƐ~w˵zj9n Ĺ LicժU-[i` Dy';gΜGDh4گag- Nixc$MPIIIFX=N>䶭[V̜9s 븹p*0Hmmm+ڼys(# esZ| n,G>29 pdp4#lp"p+$풴7 4-[+28∃y"b>6. lp"Ɛ7ƔVCBeee t[ZtxΜ9O=i6 lK[ /¶Iv>2'-Zn2 #}饗6ZʵCjkki6 lK[_=&i{?2 l O^m0]qͷzk$$o6H[o|emJdžÆ486lذ9㎦KZMFpaX~}\m5$)H$3_5~'&_tEl~vMMM`}"l؟ط^-]w]Զ>I\˲Bee~ڵ;7vi܊{ ؟ɓ'9sfq?F!Iɓ'˗ [;;;ˎ>,|$aU #}z=w+;wF8∦񮻠daH .t=S[F%ut\ 1r--thΜ9O=x~6YG0\E, m/Sn6Mo ø,Z;h1 #}饗6Z*D#lcX`[7Z[[%p25mmmF.CǼyW^aNkFZYƍ24ux=|]†a5eਯOoذas,KG?:hҥVo*n,݁J| jHҨ_5~'&_tEQo@qc I%i_y^-]tQm)InG s8֮]m]>ϕ;x @Vqc I5vXWUUUNwQʥW_}5&.FxF.[~w]SO  D7|.뮻.jv=9\( 9:/W'9sٳgWei @6T|;jU!Irǧxw|m[l͜9s(/a7*@qc [XRlɒ%;[VX*.TÎƐ\˗Ypҏ|#ַ4"l-а{̙npG}4r܄3RnZe˖ʷ#8izL' P I xꩧ:ru͒p"Wvr--thΜ9O=A4"l"{^-w_LAcBHӣ)[E=c-t饗6g @᪞7o^`K&Fkkkd.NÓ'OZq3p̘1gm1 o֭"liKx=\}咺%um6)noUӨn/aIDAT\P@a>cjH믿>0<׊f̘ʴ-7ǭYdIc,K)7 HsőH$zEtzQ. O͍1uTֈ  u>s5ӧOO/?4@>w@Ch5bmmmƉ'XW{m?$dPXB_W[[<8oi9;d<-ǃ>Xlٲ:I,ZOҖeTʥ a(,O8f#<@SLLiv`( 0G"+rq˿뿖i7BX"bv|9Hk?p=3gժUܺDDo}kӋx'͗q-r%l ? nr,SSbԩj%[7nZ&ic( e5l ^L&[:묽|q xB*>}o[?,S.N[Ҭ'(ZxqC:Z% x*x'?ɬ>A7nP7BQ,뮋>s5ӦM{u1f˲*Ν)T뢆a4A*N$~瞫җN.F4|z xh6`_W]}x饗ST{. ˲=umڵkVRq&-ǝw90<_wi*F O/ $սe˖Μ9s\Roo/ӨYG-jf^$Sjk80LQIs=L颦>i4~GMz=6$ 9_LX-]wU&iwa$)J}C?'N<7xǕ}l!)vgnz cƍZCE?ݑ.l… =9}"q@6*b{,Xьl9~aṡO66SaFʕ+!}ӓ?j߰_#8"/֘A!4ZjF__ӨYGɐT~gvƁttt[n-X(*))ɻC2ESy,Kp ypz/_^*[Ro+4SDdajH2I^á⊊ ?da🐤ŋjkUypWWWD_֣DlU *xOPPXbl _|nYVCGda˲bGuT`PepIR**>|l'K(JE/@{ضmC_3gf*/eABW"4}jHRww7au _bGy_$uy= D, DHӄ @60$u}lǒgϞX 2eX?eml!4768# 덭[F)TT\UUnT#l`H*>)T{oTR$_dYVdɁ6YE!~øS/v$I&"l0 @/b˒dʶy"lh~q@V6(?3^㮻*SV=DN:JL_`APA7ʇS$azq@+onn'|ȶbI~lm"UUU  @6o@+I˗/,JSvF-G:ҪX//b4Jz HYY٨HR˲2/L&}^CRIccaA#JE򕯌l oΈ0<4}CXr> Ha6. l2Mso,׶P /Dm8&ʶmNr0f _ʚ2j5Fׇwyǒwi<Ȍ`>'FeYeGuOწ1PO>ׯg 5 T:묳0;R{r䇬8\> DmjUY$f ̉1^x}-*I\ 7-GwwwX K@~VUUy2&WƐ\3 JL7jkHx,:Cs6ܵTfs}qXP;^#Ry),l qm56E"c֬Yyssjkaqx4;#<ғC j9?ǐġ~W6EѨHx zϷv˱~zP\EWz衇z ?} z Iɓ'ſQ6[a˲6=n<`qiw\ †oFq(  ai%sL^z饈eYAh5Bm_r%7׎;zzzw öܹs7^R X,Quuuu i~:9ܩ;weFYCCC`glrxé^Def 'B> Jf2 >*D"۶^cm46Xe544Ak8DU}3<3cX a@*2=}k??A9uY'@#l5kVɶ7x0 <Ė͟??acӦMũT*pa@X,zNQb-4,X끸%L- '@YA:::踼)$m۶-(aȽDÆeY3g8a ,2:@NaٳgOHSyA|p{@X*Νo'Jmon Oi  B?,V L49jP*^z饁^IR=_@nE4m~n6x!װ;QrY^^ذ!ɐOĎ<@ObI4 l9TRR2eJ \. |܉*SyP:455BB5U3 RT8F,O8ܶnݺbvaȝ$oKqy%z*IڰaC$J69Cr'b&FWWWw !]]]D);BXax= O;Od2?@n6"H@i??'Iöh69Dr'RYYf0}G=؝^"|i|z @Xihht&V8*D"믿믿nڶ*@N6IӦM t؈F}O8蠃vpIڲeKDR-#m{}^MX/ӨB*/L&~[>.dҔzBr#DұX,ߢ755DYfI:- @n蝨$#﷼ IUgyf?jx#ep@6(,+S$i{m.R~nG Al9֭[gR)r. ).-- |ؐX,fKz=0$՜zh5F Rhoo7a~6( NTC&MT8>u9:yqPZ+ JJJkjj "ltI=i{=Q%U}ٝρcma8txҤI1j…dz=jNNWk_\@6HlHRsssƊD"n)Vc$u֕R8%@ض6mZȕ/}K$U*?~?O&uΉZm۶YOÃPƂ f\(Fu,d=p$I+")w l G" C/vJgH$RtҤ[3-˲a~6QÇ?ކ=j<BXA'|Yf/[Tj.ྡྷB8=|47xciqys_ӴiӺ/䒜/o-;S"k$X,Vz]wMExw8_9P(o!wqG;#I8poJ$А~Gy&V5^Ô8.=\sI&I:L'g`A|ڳ:P"t,_ܲ,+%)/prwPJKK8g(p̚5o֬YyuvvI9?fC&$M:uM7ݴŏ?d2iTUU2_|1Jvdmh ?>8gaq_xҍ7uuui BvOOO$mۦi?+bVnd2-۟ @@)!t 5P/G2Iҡڽp^/醡)U /"mK ? }ie&ЦUtI';餓y=\V6*iW@p_\ mxM6E55ྴ$ԌͅEnoxg¶m-oyΡ IENDB`gammaray-2.11.3/ui/resources/gammaray/ui/icons/000077500000000000000000000000001413310443000213425ustar00rootroot00000000000000gammaray-2.11.3/ui/resources/gammaray/ui/icons/classes/000077500000000000000000000000001413310443000227775ustar00rootroot00000000000000gammaray-2.11.3/ui/resources/gammaray/ui/icons/classes/qtquick2/000077500000000000000000000000001413310443000245425ustar00rootroot00000000000000gammaray-2.11.3/ui/resources/gammaray/ui/icons/classes/qtquick2/README000066400000000000000000000000351413310443000254200ustar00rootroot00000000000000Images taken from QtCreator. gammaray-2.11.3/ui/resources/gammaray/ui/icons/classes/qtquick2/border-image-icon16.png000066400000000000000000000006521413310443000307050ustar00rootroot00000000000000PNG  IHDRatEXtSoftwareAdobe ImageReadyqe<LIDATxڤS=΂@}W5t$D N`g{@h1+@bABm!~~*dvN1&}aC@gƊv[@UU6CQ}lMn/ a \.`X` u-3,l6x94,4 XN6 0 Sc@uo-;=N@UU^0M%Iqt jBY<8Rߍ PQ=(q<#ަyB`Q̓U}O&NҌmq%IzwhR+WAmRIENDB`gammaray-2.11.3/ui/resources/gammaray/ui/icons/classes/qtquick2/flickable-icon16.png000066400000000000000000000005721413310443000302650ustar00rootroot00000000000000PNG  IHDRh6tEXtSoftwareAdobe ImageReadyqe<IDATxڔR=FP/ Ѩ5-(4U Ni *[1˕7|d/q94Mm۾T >@u]UUQ`q|::|"a,4`Y~ ˲d} *]vr_BZa) !́QleJb1kQH _/8Ar<Z-P`0h6$1?z{JC$c^VM&`JafvXu]ZjAncUi0zn eY#6j4 Z!t:F|ׅ(xp w ӂ$IENDB`gammaray-2.11.3/ui/resources/gammaray/ui/icons/classes/qtquick2/focusscope-icon16.png000066400000000000000000000005301413310443000305140ustar00rootroot00000000000000PNG  IHDRatEXtSoftwareAdobe ImageReadyqe<IDATxڔR @F[\}QD/,UUDGs ]Im*\wsQMAmhK?PUe)5Q4X%x<"L~ A[.ҽV9~+ê"xb4ə9f&gAVYT0LdKW&":v\B[MpA<4S-g"vIENDB`gammaray-2.11.3/ui/resources/gammaray/ui/icons/classes/qtquick2/item-icon16.png000066400000000000000000000004701413310443000273040ustar00rootroot00000000000000PNG  IHDRatEXtSoftwareAdobe ImageReadyqe<IDATxڤQ Fq=,;X])9R t4W_9Ltvpt]w0Rjqmo)sIPYm[#PUBzYD: S!PJz4foK]qa;NmT`+Sv]?a;v`B`'l9X4M6|4Nlm ytf3?6 0ʒހIENDB`gammaray-2.11.3/ui/resources/gammaray/ui/icons/classes/qtquick2/listview-icon16.png000066400000000000000000000005121413310443000302110ustar00rootroot00000000000000PNG  IHDRatEXtSoftwareAdobe ImageReadyqe<IDATxڜRK 0MԳtN^¥tUdu9c(^A}&6ayo|y48!έi+LJ2c%x6-sֶ-w/ ޑI`Ƀe0=}U҇A=ѵD*mvԼ >T@9I,SEHjZ5BL,rf u]}H+ng֒f&۶Sι4<)/Zh(IENDB`gammaray-2.11.3/ui/resources/gammaray/ui/icons/classes/qtquick2/mouse-area-icon16.png000066400000000000000000000006521413310443000304060ustar00rootroot00000000000000PNG  IHDRh6tEXtSoftwareAdobe ImageReadyqe<LIDATxڄR@=pA@*-!$ܻ[zS f1 mx"D\.GYq$~P=mP Z^`$ ޱOEIENDB`gammaray-2.11.3/ui/resources/gammaray/ui/icons/classes/qtquick2/pathview-icon16.png000066400000000000000000000005001413310443000301670ustar00rootroot00000000000000PNG  IHDRatEXtSoftwareAdobe ImageReadyqe<IDATxڤRA )?0GSO}MZᆪkNvvvX&i>Ryͺ}<ϽJ)2M @c8&qTEy@۶:oK[`1/:eksQ|U^Uu ϦM>e|z>4 D`O 0c$Ik(990EQTRJu*`taIENDB`gammaray-2.11.3/ui/resources/gammaray/ui/icons/classes/qtquick2/rect-icon16.png000066400000000000000000000003751413310443000273070ustar00rootroot00000000000000PNG  IHDRatEXtSoftwareAdobe ImageReadyqe<IDATxڤ ! EMnCtE{"{FLZCWkHTJ}9J)cbO}jx1$UVhQNyyTs0 03{ `BⰨeO.IENDB`gammaray-2.11.3/ui/resources/gammaray/ui/icons/classes/qtquick2/text-edit-icon16.png000066400000000000000000000005261413310443000302570ustar00rootroot00000000000000PNG  IHDRh6tEXtSoftwareAdobe ImageReadyqe<IDATxڔRK0S`=[A4Bc\!d`kKM_*~fN^gޔy8qsEP#pLVU:${>'\u۶ԗY'uI8 CaE%X.75d_ qcBTQMȇ$>cl7,QբR*@}!4-2[D[))3wFh!hzWtL\y]ŔPNFIENDB`gammaray-2.11.3/ui/resources/gammaray/ui/icons/classes/qtquick2/text-icon16.png000066400000000000000000000004531413310443000273330ustar00rootroot00000000000000PNG  IHDRh6tEXtSoftwareAdobe ImageReadyqe<IDATxڔRA TK'!hl<ƏP=fT- ~AxZ+(!$IR[-4MPtXaQm)Mow)K[i8}2L:D1W^5$joVnc&{wAaGw`h_95N#0պ^ czIENDB`gammaray-2.11.3/ui/resources/gammaray/ui/icons/classes/qtquick2/text-input-icon16.png000066400000000000000000000005141413310443000304660ustar00rootroot00000000000000PNG  IHDRatEXtSoftwareAdobe ImageReadyqe<IDATxڜR[ 0?EJ,BJeԕ&5i†$3;u3 a&f\y"I]0 0v^U84 e hu"M#XeɅL(E۶@p@ l )pcaJ.x<{Ch%U{+[QḰcXJ@$ȫ@˰W? d1qYKΨcv%IENDB`gammaray-2.11.3/ui/resources/gammaray/ui/icons/classes/qtquick2/webview-icon16.png000066400000000000000000000010071413310443000300130ustar00rootroot00000000000000PNG  IHDRh6tEXtSoftwareAdobe ImageReadyqe<IDATxڌKPoބ"$nH3"O12AB"1 B[=gkyEo˲4UU[4;Crnk, iٜgChTj\Fo6j4^Ng36[+rLRzx:[@p8nn7}>trs g^g2 04MUpV+1x1L TPX 2& BM'PRT0 >.Űa8VU yXAlBD?>vFiH$@z<s!0vGA(lR<d:B-24bz-PZMH2@d{|d.g,| !yY@IENDB`gammaray-2.11.3/ui/resources/gammaray/ui/icons/classes/qtwidgets/000077500000000000000000000000001413310443000250125ustar00rootroot00000000000000gammaray-2.11.3/ui/resources/gammaray/ui/icons/classes/qtwidgets/README000066400000000000000000000000361413310443000256710ustar00rootroot00000000000000images taken from Qt Designer gammaray-2.11.3/ui/resources/gammaray/ui/icons/classes/qtwidgets/calendarwidget.png000066400000000000000000000017101413310443000304740ustar00rootroot00000000000000PNG  IHDRĴl;sBIT|dtEXtSoftwarewww.inkscape.org<ZIDAT8˥[kA;4I(Vת}w*=`7 EE-/JmzVSls:gn(̜9;+[RnH/+ H>tg908C?w{ t@b<9U7:7Nɪ;tG-(@Pf,CC;|X K+Wۑ/wJ \.)Q,YI!{<ےYDC㹳X%PM V,f&J≚. sX -1n<}zG̩0{HH>_04ٽg 'JkS9&۶v\d23D$`S#Xe< ^8g(kWQk ˬ7+i+WO$,h~l }{A⇾u55B߾IAWILBhQm"^5 [I0,p.hvZ%鴠DV\q[RTo ͢ HΤ'y{?5Wl+vMx |ol)jM(9_Wh뎹ݞ^wfmm4u7o鷙aNxbt:= 6K{IENDB`gammaray-2.11.3/ui/resources/gammaray/ui/icons/classes/qtwidgets/checkbox.png000066400000000000000000000014611413310443000273100ustar00rootroot00000000000000PNG  IHDRĴl;gAMAOX2tEXtSoftwareAdobe ImageReadyqe<IDATHKSqK4i^ht9o!=4cæm6&EytuPt]|[SaﵳwTPT-aAv]ULƌ;0z%K`N33355==NL&;).N5}L5,rPȣX,T*\.Ik(%xb?c'{vSUtee/WWDDuc1$qQzg]AS,B`q\d2Sa0p8_N/ <^>O:⤉>3@ -/! a>nPdf]©sX"%2~M5:/QOWWWku  6NMIENDB`gammaray-2.11.3/ui/resources/gammaray/ui/icons/classes/qtwidgets/columnview.png000066400000000000000000000010061413310443000277050ustar00rootroot00000000000000PNG  IHDRĴl;gAMAOX2tEXtSoftwareAdobe ImageReadyqe<IDAT8ŕjP?mPRwȢ|}>K_?o T!ILn7!6 GG3|ctńQ Vj4ɶP4\N\.S^!{jl00a$w]V*0[hZ(l|>ZrdrdJ)xy.0 VV_NM&v.Yk%d2hPbn^:sYJHQ-˺v; ~MvRi~Ư? dV _W?K}.h>z}t]d2InlQ6balFj $lɏfIo}H,IENDB`gammaray-2.11.3/ui/resources/gammaray/ui/icons/classes/qtwidgets/combobox.png000066400000000000000000000015251413310443000273330ustar00rootroot00000000000000PNG  IHDRĴl;sBIT|dtEXtSoftwarewww.inkscape.org<IDAT8˥UMOQ=әv mҩ-*5L@bbv7# FtF&JBB`K[kbML^w޹Mt:7::9B RT_ ,2L jm8&Jҹ8dFdmm (q(,666Z `{~}}|LU <0Яr|80eKKP.Iannm I{ _i0QVg)sf(nP=/cy)I'iP]סi'_ ݶ/_*j&mvך:K$5H$8o}“7ē ޼;HDEx"!Cr>.~QSATL2-l`0^;xh'z0RPX`IIa`^I}9n R6,]Lvl|k X70FuOPeq5Z@W SɎY!Gʫc Yȥx[m GsC{{1(02:S{Nq]s10[Hĺ;XhLVKŀSҸ eu %׭9 tT^_p c;<gΰ掏044 )kP~/IENDB`gammaray-2.11.3/ui/resources/gammaray/ui/icons/classes/qtwidgets/commandlinkbutton.png000066400000000000000000000022701413310443000312510ustar00rootroot00000000000000PNG  IHDRĴl;sBIT|d pHYsbb<tEXtSoftwarewww.inkscape.org<5IDAT8Le};ƉcQcQ[mͱYrs5JXIwC_W0k*Ug$SNttp"_?RVk}{~/1.~{kR7/Ay?T&ihHJJjMLL2 qqqzzxq=݇ C~(ط"u}$#""ӄK?vՆ\*í6tb^'\6td[3<<&h'QK. cpҎ ;ǎvZ Jp-GtDzQݴ3a3.D%bnhE-͗E7awi*`{I$~%Cz.mRU`}#1Jy4jt9%'۫BE0pDVQP{ ])'h9 2tC7.蘊RC%PC_9OFρUtKH)EB_ uax.ɊEp y5(m_Y)A[1 ylJ"NIR}QcC= ʽDB'HS}jndRZޤ<*3OcyQfIENDB`gammaray-2.11.3/ui/resources/gammaray/ui/icons/classes/qtwidgets/dateedit.png000066400000000000000000000012401413310443000273000ustar00rootroot00000000000000PNG  IHDRĴl;sBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<IDAT8OK[Asgi0.l .vt#.BJhB*R"J"y&);ܹsUo24@x3pќqg:ˑ"̜0GOeY2L^9ysdUŔF1+½aO`Ei̇-vUbr.*/B T|E2!iI3¤BF|ȟ0'' Ԏ q`-",XQ@18EilO Ȑ H7rAg,81i{ 1Q( hU mv*LK9\ "=:5wsURhXJBX47c]k;YjKk@5=.O_1d[ZEq~|񍫥ZjPK**BRa(j̈|J:hsy1^NS6ƀ- eU 8 UYIENDB`gammaray-2.11.3/ui/resources/gammaray/ui/icons/classes/qtwidgets/datetimeedit.png000066400000000000000000000021541413310443000301640ustar00rootroot00000000000000PNG  IHDRĴl;sBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<IDAT8OlTE?3nw[w%BZ(H\@ j" I HBL Ѥ  ڄ%]I 5aT9G Y@  zi/{w(tuu=XHPMcG ro_ղ1t֛o 1!}1DaCuouυ::P8H4bkW(fbEmVaW< dQ PPŀ,S_Q ͦBc"BcC+OEFFB+dU$kIR(堔Mל<\T:M}C=#3 4*q Þҭ?(&4m$ <8t1_;ڽ{wٗp?v(A*r>=q,dz./P>WW8tfa{k \)55/qG<[OfP{lY%:?X^L!I!cE=# &ؾ&Ʈ]e@r>ŲMjߊXuNhQs ;370 ԉ>kIENDB`gammaray-2.11.3/ui/resources/gammaray/ui/icons/classes/qtwidgets/dial.png000066400000000000000000000017221413310443000264330ustar00rootroot00000000000000PNG  IHDRj PLTEpl}EWf9&*{Rz\17&/3":E#\^wOXi6DO-29%}\h}:,3+1at4^r3/4!l~EqN08*0l6&"+DW`p;uLuND6C+6GCTCVGZz2~:9`>AWD1>% KOZh`ngh7E^;_w&<> w O441fM2wƤ*xVabobTe+Nm:fu9a6oV߬Mzs_rƋ_9^QCHdyyt6Γc7YY=uDК;7'纡`B2,Զn݋#"W3s몏zlc iK;c$<Ȁ Z!QT>[nuߒyS_Tp=ڳ1t6 |+jLtK 'A_@eT(xȧ -0HoF$i<.圗"~ìujIENDB`gammaray-2.11.3/ui/resources/gammaray/ui/icons/classes/qtwidgets/dialogbuttonbox.png000066400000000000000000000017531413310443000307320ustar00rootroot00000000000000PNG  IHDRĴl;sBIT|dtEXtSoftwarewww.inkscape.org<}IDAT8OKQǿgjwu715.DRF] !FPDTPD/ HˮZVVFnk^\wvfNL3Z=sΜ~g{VTT.JrQ8MyN-ϟǏs+\Hj}Sy<{>cZPE'ZG{]"P\hyAc3c|?. ľuԔ(r`D2T1lK1K[.m$/F_/;8s3+G(h`p0)GS/C䀲g^}m|sV`«HKC^l;w@IfMn8+ @?f)* 6F@}Nvt"2e2B{ދй*TjoGF?c0fǽC0AȲO3t/Y &7~uvBx VW#p2|ȚjyOuLG <4o_5jߵ]^HC˩2Nm6طo/C۶\<5֤ϱJi$r-nk3$Ad󑶞$NScDnTҌMJהԬY9Ț3J6ߛr:Θ,]Zvd#i@(ԟQ7ľkyIENDB`gammaray-2.11.3/ui/resources/gammaray/ui/icons/classes/qtwidgets/dockwidget.png000066400000000000000000000011761413310443000276510ustar00rootroot00000000000000PNG  IHDRĴl;sBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<IDAT8ՕnP?;$HH,lH bx+bB@<RgҊ![X*Ѩqrmr/Cb+vHT!eHWֱO?{-l"Pڈdqzzx>])캮t;ЫXki۸Kh Ic}NNa-(.yq( MG&ǙL&,3s cLڊB&s<`&g8AF c RJ|C1ZXt  ?)_!t!Hs QJn=.bǶ 9Z縮C,^MS82)RR5,`&)? B4a% |ߧ(~3Of~ AxpDQTm*M)5oTu_=]VV'_mfWz,:Nm*OG( =@ !6h%!*($(JZdNAX {͟e&6.,w=3k-T)"UEUAصή ڜD~n+M`2 "rp rcPU0O>>>xNCabbU30 D}m|'H8p~?clmm.l6䷠.F0 TD8%ܣq}3+KHjI0]XX`iiYDeIrR6SL...I֦kKc#sssh4^#"!"y{{{mIxssZ-zyyAUiZLԸwcVq*jxxx`||-`acci1Ƥۭ{ظC=cUeddվ-mJ0::JXX,υB|>1A="k-Sa6U2h\Q.P6udb[IENDB`gammaray-2.11.3/ui/resources/gammaray/ui/icons/classes/qtwidgets/editform.png000066400000000000000000000005351413310443000273340ustar00rootroot00000000000000PNG  IHDRשPLTE@X@X/A^gz\wr}YvZvdyfyP\ao|IZaq}V^a ټtRNS#/x %YIDATx^ηQCQ<͙sqaQ Ӝ "0ЗF4b'Msc#=mY,`D9pa;8gS]_`D5pѶ/80 ,IENDB`gammaray-2.11.3/ui/resources/gammaray/ui/icons/classes/qtwidgets/editgrid.png000066400000000000000000000005351413310443000273160ustar00rootroot00000000000000PNG  IHDRשPLTE@X/A^@X\wgzr}ZvIZadyfyP\ao|q}V^aYv5gstRNS #/xC>IDATx^ηQCQ͙sDrӜѐ Rp)0ɲLayWq<+cS={.sQ,` å,pa$!( g 1MOpaܯIᜣv6IENDB`gammaray-2.11.3/ui/resources/gammaray/ui/icons/classes/qtwidgets/edithlayout.png000066400000000000000000000007071413310443000300570ustar00rootroot00000000000000PNG  IHDRw=gAMAOX2tEXtSoftwareAdobe ImageReadyqe<YIDATHN0s}Jy,Ll F$W`@*1 GK;0CRbvU9R$>~sbQUv9NWd8hy*@UQUyPVSxR6 }Vڼׁ8Ȥף߅9@f07 N&,! hy r8Y!qG SP[(d sd@A*s*Qv*d WCS,Cmz/%e&"k u[{53Elb|NIdN77Wu :3;z? o"ңkxWU2+lˌIENDB`gammaray-2.11.3/ui/resources/gammaray/ui/icons/classes/qtwidgets/edithlayoutsplit.png000066400000000000000000000015341413310443000311320ustar00rootroot00000000000000PNG  IHDRשPLTE@Xyyy/A^@XE\I`j}gggfff ,,,,,-,-,-,---,444;;;???]wr}dzo|;;:{~$$$V^aYvZv\w-,,_wͶaxbze{k{l}m{n|q}p~U8~.tRNS ##$+/3GMOU_rxxxzkm.xIDATx^70 DQslcP Up[>BFhȰIM le fxdxͽ`4we?r,>}A(4\W AIENDB`gammaray-2.11.3/ui/resources/gammaray/ui/icons/classes/qtwidgets/editvlayoutsplit.png000066400000000000000000000013441413310443000311470ustar00rootroot00000000000000PNG  IHDRשMPLTE@X/A^@XE\I`j}gggfff"""==='''_zuzк656666'('q}r},,-x~-,,-,-111555556565655 999???IZaV^aYvZv\wo|b+tRNS ##$+/3GMOU_rxxxz6oFIDATx^US@@I!Pڒ m.d&etv윙G@tA|TkU+n8\m\q$rpR\ʓ,p8{RL ;8XHJ ncA0Z4uQmBBG,RUwYA袂2 #R JB@:PNE)ԴĩL23311H;r~?<200!$P(LMLL`L&eY<&'s/& 'q ̴Ӭ};"KAWZ.pI 5tzDT-ej:B*#k៨*9Îcm[Ģ1$zX-uqrZoU^+E Bܬⵛt%|l4{~BclտX*.o^ͳg)}{7x|l$O;p.[HM Vqhy(?p+ܪ'v{N!Rƽ1r\Zöǒ+ E:gTգ={~><<JusܲuIENDB`gammaray-2.11.3/ui/resources/gammaray/ui/icons/classes/qtwidgets/frame.png000066400000000000000000000013211413310443000266070ustar00rootroot00000000000000PNG  IHDRĴl;sBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<NIDAT8Օn@1qJ%&?Rb Oۀ؃X<VXUvП(JR9 77qv]p%/Μg^_+c ֽ;-kc0ưIJ),n8xO\.믉0_:qcL&w0N|zt ˲{BP^16xV'%>;*1z]Kj(w:ukbQy^/Rߜ dl g2*~v-xgHZ<Oh6!m gYYDQKpu] Z`6}"X0lJR6kyl5q_)<2^: f iYm۲'Z\^n7 GQx,x,[E\.)JRqV «J%eF##~S¹\k35aؔWTJ&J) Xߖ8NҀ#gwx/[N?}~+zmLh\JBaYɰi D'-;bIENDB`gammaray-2.11.3/ui/resources/gammaray/ui/icons/classes/qtwidgets/graphicsview.png000066400000000000000000000022361413310443000302160ustar00rootroot00000000000000PNG  IHDRĴl;sBIT|dtEXtSoftwarewww.inkscape.org<0IDAT8kLSgqM7˲,qY쪛n&ev#[ A)r)JA:[.ceҖiZ.[@2E`RV2iKoOA,kMsA?|J_yw;Ruo37u3]q$\Hk̍q: ,tuFDeOB0j~hv*G>J] j@<qE%RTFk5xkT>WEyۈ[P*)3[@Q2*'sקg%.^ cp8OaN\.̖P*=]Mؼ_frba겠g|ZZLH$Ӟ?o{CX # }b22$Jqkn6mXb+wMzs&_|CVXdx'B])''[ms?!uXEE1y"![둟ҾۣƜQo@s$ IW9sb1q)¹ ,qipxaB+0)(O5;oci p` u䍶El|9?!_"^? mH€dqgXNn $XQbԱkjY;b^UT0 X̔d`P[ IENDB`gammaray-2.11.3/ui/resources/gammaray/ui/icons/classes/qtwidgets/groupboxcollapsible.png000066400000000000000000000012761413310443000316050ustar00rootroot00000000000000PNG  IHDRj 2PLTE===XXX555333}}}RRR&&&>>>DDD葑ضEEEIII՝YYYג䳳XXXfffۣïٹݏAAAeeeOOOBBBڌRRRe$tRNS &M K:G 3%g="=ZČ3L4IDATx^n1@asA3CSEꑟm]ye !4LIe][- Ơr \\gc fr{)Fo9ƯoE8TOT:JX Z=k#26V4m;oۦi5;B82=,BrZW#J\݉+w71P4Utc| ry-+"3*R0&'3ap*I0RKhRCx.P8~ |-5gIENDB`gammaray-2.11.3/ui/resources/gammaray/ui/icons/classes/qtwidgets/hscrollbar.png000066400000000000000000000006301413310443000276520ustar00rootroot00000000000000PNG  IHDRĴl;gAMAOX2tEXtSoftwareAdobe ImageReadyqe<*IDATH1K`A.I?P:Aq *A\\榛N. N VhMirp6Ag$eRgˤKPodT%~Iy{%3/(BۆMC{˰kXPTgSqXCC̰wm8P|N=nZHwM[!Cy7BωWv ۗ;ţ9CLsLÎ&~)*aǺ5ip.)R'PZOUҘ#IENDB`gammaray-2.11.3/ui/resources/gammaray/ui/icons/classes/qtwidgets/hslider.png000066400000000000000000000013311413310443000271500ustar00rootroot00000000000000PNG  IHDRĴl;gAMAOX2tEXtSoftwareAdobe ImageReadyqe<kIDATHǽkRa_u3*. ˁAEk]t.`-Ԅr0BF!TBơ5lZAȘ Uտyyw`?x> _ .a& +!?<(% EQx`ۗY$,ȫ`0n9̮1`U!FߏٕAQ\tѼ M%vq]b+/uErO]C|q]\TPÏ7'⣄oe񋲂3c?P!>Ftq]]bڈMOŧU]1b'k"]E.>@;{ q=" zD"dYM<9OŇ84TUE>@P@TB(brcY6+},~VPH|0h,sT~"g&7o+9q}BeC^jQܭjblFA8e`TTۍ̔.^~@cn.S̼5R+G"q*kVYEn8%r}i3=k`E8#IENDB`gammaray-2.11.3/ui/resources/gammaray/ui/icons/classes/qtwidgets/hsplit.png000066400000000000000000000002441413310443000270230ustar00rootroot00000000000000PNG  IHDRm PLTE~OtRNS@fIIDAT[c`@Ŕ9# j QU"VZ*4:@郙as}0vwIENDB`gammaray-2.11.3/ui/resources/gammaray/ui/icons/classes/qtwidgets/label.png000066400000000000000000000016711413310443000266040ustar00rootroot00000000000000PNG  IHDRĴl;gAMAOX2tEXtSoftwareAdobe ImageReadyqe<KIDATHǵmHSaǗX 2SQ$ka~ɲ} ZDQi)&j B/Qh-(**{[ܖjۿl6>t.ws# >Dsrrrj`U*rn5 Jwq$Nq,FwI*{G9,8^gA7 0UT*u(g{b"N 0uf䍴ij3a{]z5(kc@@@6}g1o!'[NX+SS C-a,UzwdM2Cliȃ]STᦿ zzz0<{0u@>5s\"q~J#UZ0^M0h}>ڏ%{g4K].zNK`r(>?iħ|\)܀φS5XI81PUXVnlS|U;A|ΗO{-`9QQ %11t:,6|xzRf{L5NGH dxw}p 9cjJq@d&Bʋ 7D"QF]6:8= 1!LvPx9yV-G占YG ԑX%f D&we\Isr91V^ގjʠtKˉ\b+W'3_O"Y^fy*V-qٲAX+8hb;QKXO!iףB#>ry%qlR~drz-zrJn6^RbG=JW|-e]*촀'GIENDB`gammaray-2.11.3/ui/resources/gammaray/ui/icons/classes/qtwidgets/lcdnumber.png000066400000000000000000000010531413310443000274720ustar00rootroot00000000000000PNG  IHDRj PLTE***DgggXXX󐐐sssռ幹ʦ韟ܙፍYYYكtRNS&M % :K "GgZlIDATx^Ueo1 a9633rkˎ~ $;Mm>)os}bN~{g8=]iHO&] *>>j*L^pCM23؜wBL](QDm&)R$^^#d˱F`r*;zu)D/ꪐ3-2ebNhӄ2m:tҥC,x4iӢGE%uS8IENDB`gammaray-2.11.3/ui/resources/gammaray/ui/icons/classes/qtwidgets/listbox.png000066400000000000000000000014351413310443000272070ustar00rootroot00000000000000PNG  IHDRĴl;gAMA atEXtSoftwarewww.inkscape.org<IDAT8˥TmOA~z-H~I1~>$G[#B{׻ug{m1t3gyfb|^,.. loo<׿?Eo It:h4 ȣGT|%\'b;W8<4$A"@h;@D"'`eYIPPב!%KE%8¼{Z^^$MNN* HkC\C\DJ}JV=z~L\` cvvV---pj_3R$N".t_"Z`?P(U---IIhfTۛ5P@BPu]knnNH$ttSZ=D c@أ8SSSbvn`^Ex\^^u]e2 )d@ qh0Xǖec:::ޮ.UWW m8T)ѠGqP \zK^[F}qBmnY NS .>ϣ86IGw?d_a}= 9(\Y 8k@r9 u{3pLА 8 8bA5}&@6Pl:(_4x^IENDB`gammaray-2.11.3/ui/resources/gammaray/ui/icons/classes/qtwidgets/mdiarea.png000066400000000000000000000012031413310443000271160ustar00rootroot00000000000000PNG  IHDRj PLTElllDDDdddQQQ~~~PPP[[[ZZZ...ن׀ڊډׁ؃^^^~WWW///s؄هܷڌ+++ޗ͹ێ,,,}ߪ`xutRNS M& G:K%"FIDATx^n!F2uw}g)fnzB|J,I^ BЭM\ OlV I#xzm/@)dv;.рjFs7v'b݄>N:4M"+£"qG*\J\\\D8hƲ,lƶmy&ij8.TnAY[[8==eoo z^{Grh4x||@ qyyIym\]]ƯT*auu!̰$!5*vwwiۚW}}}lmmiM_b2? %T$8R(RX$JӃeY$Iɤ6x0niZ"(J)r楔oyJJ\.G]'IENDB`gammaray-2.11.3/ui/resources/gammaray/ui/icons/classes/qtwidgets/progress.png000066400000000000000000000010571413310443000273670ustar00rootroot00000000000000PNG  IHDRĴl;gAMAOX2tEXtSoftwareAdobe ImageReadyqe<IDATH?@I&{wsr. V[ڈ"d+XXn-~[Ȗւ`nV&G&yg,pw(ݽ0 /cᱥ_/jv5O*2Ddcb$s`eٌMPD(dҁjs<~~MͳO~&G8Y~A)$w:{Jyc u]}a2^iuпA p8&M{gxluV֭g+ ;j @ 6eJU]B8u㘲ycRt`1r98Iœwi bѣj9乐Q6G 7uۂki+T΃W-|Oe)11IENDB`gammaray-2.11.3/ui/resources/gammaray/ui/icons/classes/qtwidgets/pushbutton.png000066400000000000000000000006301413310443000277320ustar00rootroot00000000000000PNG  IHDRngAMAOX2tEXtSoftwareAdobe ImageReadyqe<*IDAT(ϕn0EO' R2+* v,,HR2u`Av!.-l k6܆ ec1[ A6| Tt\cFn@!HJ Ф.x=0!<0 Ǔ~ln6k%"Q@VpI_:K 57=""zDEr/o.s揰ݡ+IVtШ|Bf9zѯ`3E/t=[R@.l1HIf"HW [  [0\IENDB`gammaray-2.11.3/ui/resources/gammaray/ui/icons/classes/qtwidgets/radiobutton.png000066400000000000000000000011121413310443000300450ustar00rootroot00000000000000PNG  IHDRngAMAOX2tEXtSoftwareAdobe ImageReadyqe<IDAT(MH Nn6)F1V,Q, 2B((`aul d5"J ꒶KDv)iJ/dEw,:s_bd.\r) V2NvV֚>]J #zsG5e2Ll{R>d2N^/|scZW^r/w:?y[f(3Mۄ;![ #|%%{<2ؾ9f8W_<csUqyeo`gY /Fr S l`WW qw*SI od'A1Ȇ;;Jx= r\c}fc.d8W8QKuAC=.1E.0))*i=Mv8A!uK[T$Cnf\_ n(^)J* _+ /{l8qbIENDB`gammaray-2.11.3/ui/resources/gammaray/ui/icons/classes/qtwidgets/scrollarea.png000066400000000000000000000010441413310443000276460ustar00rootroot00000000000000PNG  IHDRĴl;tEXtSoftwareAdobe ImageReadyqe<IDAT8͕MKQD@-DD1$8` ]).f!+&pFg#h"ye/jfp/\f΀(wdrPeF|5CQ Ra|Ιff Lt:b&P(dA$۾Ka6]֋ԛ@9L\.|5MRX*1\.cVVT`yhŔB|Ń)_9% Vq,>x?5zO7n׳FtES6GĎH$eWb3+v|* NRֆIENDB`gammaray-2.11.3/ui/resources/gammaray/ui/icons/classes/qtwidgets/spacer.png000066400000000000000000000012561413310443000270010ustar00rootroot00000000000000PNG  IHDRj DPLTE Тp fu|f@oSvO{+LZ 9c{>k-Pd4ZnAT\JJJ444LLL---AAA % ???~~~pYYY⃃333撒`rrrMMMM}j[Aex|||mmm EEEV{xxx>k`xCs666N}1IV:jXXX3^taaaggguuuSSSUUU쑯;.tRNS  MD"3W?(:nu\9t7~={>F,IDATx^еn@Eر^ 330333G)J:Ny?,i^췕;JVƁT2N#f.'^%-,ār2'TZ`6q5c`JgLlMLMb:&X,lT66m bdK?47v7ٝ3g.B/ /"8294vu!l6 iBA$)V+y:js.KH$p`@1,zrtxp~~G6.cesN0` ck&Zt]niN@= JDX,0 ef^l*4S^n{7vBYORn\.T*!/PwRQ^ijBxkj;wx{<2b6ٓ' TUzFT[jJŷ#?ih4|>wvlX(( v-2 j 9c f|F1oWج zohzW$8bb  sinl6(_eiD$+J^_]h79'aIENDB`gammaray-2.11.3/ui/resources/gammaray/ui/icons/classes/qtwidgets/tabbar.png000066400000000000000000000011571413310443000267570ustar00rootroot00000000000000PNG  IHDRj 2PLTE%%%}hpjpZx$$$r&&&gMfkqysqc,,,({~-;###uwyeuJcKdOhPiQkRmTnUpVqWsXuYvZwIamh4tRNS &M "D 30:(Q)\^!IDAT!O@kF0ABCzW#84AB&H ,ז]ZLM n fN:(MQO. {'o$EQo1}uESݡQ#lQ{/ e,,̓lm-zDxmƗOYX5 {Ȝ{s6 "_8a gGIxthSw4 l%6HI IENDB`gammaray-2.11.3/ui/resources/gammaray/ui/icons/classes/qtwidgets/table.png000066400000000000000000000007431413310443000266130ustar00rootroot00000000000000PNG  IHDRngAMAOX2tEXtSoftwareAdobe ImageReadyqe<uIDAT(?K!$(&NN77D Egp]$?95Txxq}{z& O LC}ڻug8|Uh{$Z %ٰ6'j@NJwT_ HpWP@x:8w OZ;X `|68? [ST!Jpi{]߀f3 1J%H%*@9`(7߰~RHPK$ 4* @-Mքfxz(#.T2ދ>pȍyHǖ- -P4uVG[injM]&f h4R7jIENDB`gammaray-2.11.3/ui/resources/gammaray/ui/icons/classes/qtwidgets/tabwidget.png000066400000000000000000000010741413310443000274740ustar00rootroot00000000000000PNG  IHDRĴl;gAMAOX2tEXtSoftwareAdobe ImageReadyqe<IDATHՕn@r ֑D"ݹ;Jt !% H "EZZR8;-׾5 qG Fxy<0 (m6@0N<)ٌdADz(ZY@n9y H`@|wWc7~ R 1 _?7}D0څVp ywe9@aLE{g/ǫ݉"ziBS= 3𠢰g IP lA"@ҁ}h)jmK~uY\"omrE/MePk&T!Sô{||5eO[ߋLpb>΍+]hh$@&#fpJcϪ/IENDB`gammaray-2.11.3/ui/resources/gammaray/ui/icons/classes/qtwidgets/textedit.png000066400000000000000000000014671413310443000273620ustar00rootroot00000000000000PNG  IHDRĴl;sBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<IDAT8AKAD<zҠ^DA AЃ_@ E~J(E#I%LeD{q`73oy)xX^^R&1)%RJX}sSJ-d22922D"q/666+"Dhkk{04 )%iR׹Zr}}MR\.S.@JRnnj蔕R knhXQ1Ѡ?1H#&`"JLGKZl]lˏ-tw{7O}8|̙s- Z@_}Aޜ}al/L*_?3]b*aw{n}635Q XՖ|al9v<6>x=>Hg\{,kT5G/a+_rdl<- ovt䞛clɷQi7SgTI711]W=OZ53\0ƖMᅵ ٹc'Ce-oN=v:wQZ^A%qW288kZZJ<5kr뵗l&4(5my򀮃Ao wpW!"XX6%1iݷbtdU--"Tmu12cmnK]L'$1T N\MNݵ0'غR,t : J*'IB\ksBX,o AXx u a%b*"|{ TVcʼn`E0==s)"ϊSkSA)(W*TGLMN\+]ݸ]3V,mG)EF|4Gx6N4#9n2Mⷓjgj^?0Ñk3miI^W}gIe$~k|=w!ۥ =+YӞ5~*&|6VedrC;g>瞐El[74< %Ә qgSYkb F8S66"g@EDL`S6h4ۡVΒJ(j{,RP*Ɓ?>1sd9X\\ "Y i,|drs XDbq;>@4emmmQ(X]}yV3+Thq סa;xB:}xJV @=S;@ WMN 6.133sP۶ l/l"(mƮ>{|~"Vπ9vRS"DN9Bq@ԋXhj\v o_pm `E ?ɽIENDB`gammaray-2.11.3/ui/resources/gammaray/ui/icons/classes/qtwidgets/toolbutton.png000066400000000000000000000022171413310443000277330ustar00rootroot00000000000000PNG  IHDRĴl;VIDATx^UMH\Wޛ޼w 1 q4FmGMLL\Zh.h1]u'qӕlJ (jZBMbt%Fct\[ E>87^s{GC8nbss sKKKXcb[0lF*I5EQ=iCCCa.`bVZUUX,d2y")S4U"RxX1۸x<I111>tu}Buaȡ0XL$22w1;;\Ԃ !XE-`cr%ZZW=ȵFJPUF\ q1#~|0Aj9{Z0̈́ 8z <a~~j$<# pυ Kqzlqq@yy,#}/햱iB؄H3!E%q6 &f[!uvm̜A &r7Kի?]H !22 IL !5 &%ǖ;099ׯƋ/1=+nJwޟ?_}&k! ~vww޽aܼ Mv|=o+`#Uy٤Y[jO ?*ܹ<ҥ%]`ئOȂFʱ[D>2QWWՆ[4MdS:#77+RB#vffOeRHeܸщ ߦD T͚|꺆k׮SRڠd|IBأѺ ي/"m<;]JxL*+8{6pHOq "Hc+dST͛HdbYunjS 矬OZ4~3{s="0D"Y)&((4+1o>|Ts;-eawBD?7 $㡘h.KYMqh!Y.%IENDB`gammaray-2.11.3/ui/resources/gammaray/ui/icons/classes/qtwidgets/vline.png000066400000000000000000000004721413310443000266400ustar00rootroot00000000000000PNG  IHDRĴl;gAMA aIDAT8O픱 0FZ;tp(t,E*TpDzfm>pNH"b " nml( $$ #eV JhACqˬZݯX1`8o 2㼀~bKGUU|!L]Rpdp&I2_-~xb`~ qB}y y h4pֶ{o}s6iMpqbgx^ќ{IENDB`gammaray-2.11.3/ui/resources/gammaray/ui/icons/classes/qtwidgets/vscrollbar.png000066400000000000000000000006371413310443000276770ustar00rootroot00000000000000PNG  IHDRĴl; pHYs  tIME .A=bKGD,IDAT8c````b  > #@RBX˗r Ϝ9ok0'5eȆȆC]I@ 0a0p(XyX}`c c$qb b5 E lj0/G0`Y =cp$tZ#pܺ:0 2rܿ `f.j*a=dYVN BD"oZ[[ۊ7|{54miG-=~gil9K-=K[z>o墥&=p8س@9p+rOxtgʣ5-iMK%RGOzJH@@@A6TuB1t>ѵQ؋Nwѩ{tBߣ41-#\1@cPm*oFߍnz0&Uz8 FQS5_B#H+:چjbׁPwֹ\/+8,}=@wAuvvf @59nðoPᖖeU>Sar ÖHC{{d\mVFFF2>BJv uࠦ2`v^`OOCAn;js亚~\#IENDB`gammaray-2.11.3/ui/resources/gammaray/ui/icons/classes/qtwidgets/vspacer.png000066400000000000000000000012451413310443000271650ustar00rootroot00000000000000PNG  IHDRj DPLTESv fAT\9c{ O{ 4Znpfu|>k-Pd+LZ@oJJJ---444LLLAAA`jpȥ UUU3^tXXXuuuSSSN}YYYaaagggmmmV{}rrr`xxxx~~~ [⊊CsMMM:jAex|||>kM %6661IV333???EEEhT.tRNS MD"3W:(?{t~u=>7n\9F_lIDATx^E{@Ea@\C a=˳ )t!VMrK~p4ì>'g"dQ[=<=ȉ#$3&0W2 ܠ;M<@9!05]O$k!B~mѶN>ݯO5AaFq(co({޳/.Vo_E2b\6IENDB`gammaray-2.11.3/ui/resources/gammaray/ui/icons/classes/qtwidgets/widget.png000066400000000000000000000013141413310443000270020ustar00rootroot00000000000000PNG  IHDRĴl;sBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<IIDAT8ՕN@qpP$Ib 8vǪK*@* tP*۩le,:%{|)%qgQwr!%RJ?C~[_>'Q>W?ɲ49<|iyF^JW8-'0r* 6WqEOJ:Z-e$Ic~6q{{[I&l6NGᛛ)yp\]MnUώ^WQ1NYOFFuŽ2&IBnE=jZU0,rM / tv_(4^ >SF˲vW)!nh6`ggW8s Њ9?^67P.yY( |_pV<^" C)%V0 ITT< GGVE_HY>D")S( FZ⿻~ P IENDB`gammaray-2.11.3/ui/resources/gammaray/ui/icons/classes/qtwidgets/widgetstack.png000066400000000000000000000014741413310443000300370ustar00rootroot00000000000000PNG  IHDRĴl;gAMAOX2tEXtSoftwareAdobe ImageReadyqe<IDAT8KKUQk^h)B_ j 8 4"MY(GA i"J5tܮ{&Y$rkڥv}f- HP Dc w]G`HDn\j=o C/$T**Ja'G BijjZP 34mTkRh\zխbu&f> -qi8U\΅Ϟ Eca=UÜ;`vM>w0.󃠓`ׅ|?_wxqOXeQ YNtx `Ǩxs/Ul!cQ* -90V=~:6|6{`~ef&A52SSSϛgq)1t%C}Vqik؆uvs !\JRZ~) ϺW2A!Be`\ Q!dhDDo@I R0)F@d =HIѮ6C|\n D%$%v,^1P ! IbतSqqq2)sƘYIENDB`gammaray-2.11.3/ui/resources/gammaray/ui/light/000077500000000000000000000000001413310443000213365ustar00rootroot00000000000000gammaray-2.11.3/ui/resources/gammaray/ui/light/icons/000077500000000000000000000000001413310443000224515ustar00rootroot00000000000000gammaray-2.11.3/ui/resources/gammaray/ui/light/icons/active-focus.png000066400000000000000000000013711413310443000255510ustar00rootroot00000000000000PNG  IHDRasBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<vIDAT8RKhQ=eĎ* &4`ZhͮVua!(•;Q.B vnRݘC)JM*QLL?t湐-gs={1FpLMMf/1K\,zAXlrGGn2PKUMŪ h4l6SJ/0Hkf FRt8?fff&q(D!^ Fq qcP^o7e]]-gIENDB`gammaray-2.11.3/ui/resources/gammaray/ui/light/icons/focus.png000066400000000000000000000013261413310443000243000ustar00rootroot00000000000000PNG  IHDRabKGD pHYs B(xtIMEVcIDAT8˝kPӇ`2ԑ1;1ȦB)tHI5S@ҭ-C%SCUMcY&0]#Mx9=w0׈,=}x}}}eٲ,3 Կ’$yd\QMӼ7L$ڶ!}=0&ŭ^on7L9qCDteYVWWm۶]$5 !pyy o (jGuv:~.>w||`:;99(t:myv \7l6b1Vi[fWVV,y^immqvvixjqZi?fEa1NT"z~~v^G磣LfDQB45òl;hBP(Ȫ~jmBb^O4=w7Zν(< ˲(>$Iu=4 ˲~0 mV! W*^2dUU07˲^8 h}\.{^ub**AKӴ9Y텷`0s|U@D7Z@Ӵ3J%( 0(yӉIENDB`gammaray-2.11.3/ui/resources/gammaray/ui/light/icons/grid-settings.png000066400000000000000000000006021413310443000257400ustar00rootroot00000000000000PNG  IHDRasBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<IDAT8=JCAFgfXYB+k {%XJXsRg Tsw,=W.`Ձ/%*i'iוmt!iH:dgh{l+پ67{ۓU_+I!8F ` ,l`[elA'9ؕO샃9:8 x6:vPsp̂)0 h ,ΙJe>^zIՙXԿUnIENDB`gammaray-2.11.3/ui/resources/gammaray/ui/light/icons/grid-settings@2x.png000066400000000000000000000011411413310443000263110ustar00rootroot00000000000000PNG  IHDR szzsBIT|d pHYs^tEXtSoftwarewww.inkscape.org<IDATX͖?NAOlD ‚b$A=h,<Ɗ@bl,!ς:b fطoyYB1Wx#?z|sh)/kv4M` 䌇:t8qGw+& q ^DLD-")#)0}Jc l\B{p "U>VZ0[>pr+5 Qo>Λ=BUIanc xKT4}6DdGf"%g*Y}>eK>.HKo/M肔n3pU\u}{@?Z|G/h >KKD$\Yԧ1 â}]3HR^ݺN5T4_QuIENDB`gammaray-2.11.3/ui/resources/gammaray/ui/light/icons/grid-settings@3x.png000066400000000000000000000015351413310443000263210ustar00rootroot00000000000000PNG  IHDR00WsBIT|d pHYs))"ߌtEXtSoftwarewww.inkscape.org<IDATh=kAs9"5"Z܉AR,I# ?_JAm"&pbݛwvgps{巳dfwyVuX릎RǯN6jC9S?_0`/GiC,r 8k-S0x ~_JDǀVeNhXeeYmגּ87X'9ms- ` :l"m@.|)AӾ7TE1w|M%"ٚTJ_ob`c\D~=ml P k]Rp*/M3PBВ=)4 ?rN*> "x'蝸!"{""o~ה{o`WD܇W=OpBy(ڳ \/t4P#^%؀Hn#Ubj? t U1L߿/O1.> "x)")BEo h/t/T;]ReRBG>QUb޼IBӛibP7f MB y蠚y"fͯR3; |5~ ȍGo`?+\oq}m + v̖>l c3㹤C)>Ô!A#~  LVJu01;#=f Pi\MR\? S,L]`rbR`] Sh I(Ǎ?D]4{ J# Q1GUܵ$Uz7%;C(`nR>_4jݮT*[Zk CiN˜#5NjجV%믃 x~H VOu""= ่j#iɮsG^;]"< 3ؙ/ 'wDWV;Yk`^π=L__85` dEh=?̒,EIENDB`gammaray-2.11.3/ui/resources/gammaray/ui/light/icons/legend@3x.png000066400000000000000000000021451413310443000247720ustar00rootroot00000000000000PNG  IHDR00WsBIT|d pHYs))"ߌtEXtSoftwarewww.inkscape.org<IDAThՙMOQ;#**$bk{.\- M`튴I7?.ܨqk "FEшQgnW?M&mg}9[@H?l,Mp8|OMl5yGQ8$06XO&Bvjd"C)HC @12L@M 6ǭ ȸjYVME]11}nx <>H'R/#Z;`8` X6Rjp^7뼎]@úiv~?s“AoU!rtcmvY۶ L7$͍6NJ,N8} Ql$Aa؝^B2lJsoF3ߝ>+"Ow ՐENJLc"ɅLZ(SX鰶KmX:GIPtȼUyVbF5Mv72am<6('q#bX~H R"Fǰ#knj,k%wr~Z庴vsy+v6Gw>w%c>c 0ӳws܍m􎌌?66v{"agbE VS) ٶ hJr ٻΝrX~ 0sJ^6mry x~аi:!2̙jz+^' bX\tCj #`Ʌ;\>W(gff> 䗐@ vaC@ ׿}'kL.m۾W# B5)!h>_,7AJCWRI'lȃ=<&Y@S3CH/zQ*HYC""2 *U?(%D𞔎y/hP@oy<,zYioc_MIENDB`gammaray-2.11.3/ui/resources/gammaray/ui/light/icons/measure-pixels.png000066400000000000000000000005641413310443000261270ustar00rootroot00000000000000PNG  IHDRasBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<IDAT8Mj0mi %)&=GҮCOㅏ*K]diZI5H'VU/h1MD @v~,9`CD"26t,lNb 圳uPS?-a՛/@nDd{`oQ;t:dF~@DI.Ɇ{oD:笈xβE17EQ,x_)j8G.\uKX6[mcR@%L#rS{7]ئ eC-ڔBޓcaz19 A2Mv ^qCcZ5un4$UVD¹*EZ<1ݭK&fZbڡ6yJyd\ 7v8m0UD1:Q֠<;L{LP$Mۿh;*3cyene էi{RdOe$ۅOÿMVZO<IENDB`gammaray-2.11.3/ui/resources/gammaray/ui/light/icons/measure-pixels@3x.png000066400000000000000000000022561413310443000265020ustar00rootroot00000000000000PNG  IHDR00WsBIT|d pHYs))"ߌtEXtSoftwarewww.inkscape.org<+IDATh]nFb˖-+ŗ(z6y*S?N37(%5$d4Έ#JJB,VrݙNvUMi8{ ,4i4 h=iImbne$1!)0R"s+<{sB B1$@8z!/G: :q]eMEyTӣ:L$@WEp'B@[f_"q\ۃ`+u0tߊ N@.JN "6 j۹S9UʰɌbJ 5a%8#`m2ROa,kX/h`yG$Ա!v.v!:{UܿbFŦyv,Vs(JqΒ9$m#E8ZաrMFNUu%Zsݶ^oiӄiޮ1XS ~:i ^L3YBao Fi"G1[PH7?&>0^1{a13* #߰CTJsy!iYY% N`Q7}$ Dɤf^|_k hĂz#>pS-m-K>*3k~>Mo0{˒w';N/QRIENDB`gammaray-2.11.3/ui/resources/gammaray/ui/light/icons/move-preview.png000066400000000000000000000007231413310443000256060ustar00rootroot00000000000000PNG  IHDRasBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<PIDAT8J@ n|Eu7=|ԅ˘MMS$e\8Z;;p7sܙ{Ry,B(¯#<)ughlqmC;-z)H=6 ,D\XiÈr]~β,:?|(Tm@xe9S9/Δ3 "r<+0<'I5lU(|_DS Oh0G`5́,(dLࠪ*8j.dY:LZN0.~&h{ & g-ԔIENDB`gammaray-2.11.3/ui/resources/gammaray/ui/light/icons/move-preview@2x.png000066400000000000000000000016461413310443000261650ustar00rootroot00000000000000PNG  IHDR szzsBIT|d pHYs^tEXtSoftwarewww.inkscape.org<#IDATXŗnFnoH+]RhHE"< E˃UmwЦ(q ($ε]11Q+ܑ|sf@0[,/ `&6/TG p+˲Hl 8LW]QOֿgYy>Fyf<^w5W< :;Ln?f}OH) T!h.ax%6=ue Zooo,\>j֞ވ8ҙonn6n.^(WVVZ8Z7\TJJɿ:v(+̀B˯ӌn_'@irRAP_niilHm˲x뷹Z1Qeq1ftxx86O 3(M8˲5<qd$Cp_R u횞=:'}$Ifq? B<qsđXkT;\nZ3.au@]Ή@_&" $@ J/eUsz2\-6q38P+<-^e~Ӿdc>LA,!\҂>+`4MS r+7i٦2}@ͥX,WUle c"(eE<MGx<Øf=({΀7<#c (2 3C.+%xMf\)MN~?eB -a?NU߯ ΢j9=@>~|>/ݮrG(d*G..j |'y phJk-Ig_wqA$S "0!eYN a2s̬2DM\>v#9jHTVI݅`PUJ,KvRE}3ޮ~r9LZkVVk?+km+ߣ^DXkt:v<666Mvnjxppp)T]UJ @`L&1JO[k1L&x:PvJ_h4z&@~ޱ$ρե+VJv ~l!րh4&0"("\G(˲t)5%gmlrFC%$%<IAZk2 n$% K!#m%<?M09Ep$a+)ʱ$QDg=b<%š%z մ !@ H*}B]yBS7҇ء!ܓwUפJVk<6}W"ۚZJOOjT1pΩ,=a]߉.kx3Ɣ^b頹2x`#)dDNZ+ʲl,*. ѱq_K=\[L;@_ `(ǯEGtދ ƻ2m-#RGx=+3yiylEPvjL(-+ʏilo4={d?PRr@D*ewfB%;#ˑ btewL5Y,W BJUղ2IleuضIENDB`gammaray-2.11.3/ui/resources/gammaray/ui/light/icons/pick-color.png000066400000000000000000000011501413310443000252160ustar00rootroot00000000000000PNG  IHDRasBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<IDAT8AQ3ㄹ2"Ġ!AO6+]I.$xC4;"`vIӂFl:Kkwxo+pY(a躾L&3hi(j]#`YWAұXlk6ٺ< `]p$hvfemQJ9PU:0!f۶&IsY_/`0jyeY~j^7N?/BR5 ^R(+n*H|`_Zg2}qRTpC]_2l֯1]RMSJTt:!Iқbqi|aw8ZLqܝ~_eY:+_څB"x8&Nl 쬚h4:(5:EQHӏ2<''O iL IENDB`gammaray-2.11.3/ui/resources/gammaray/ui/light/icons/pick-color@2x.png000066400000000000000000000030051413310443000255710ustar00rootroot00000000000000PNG  IHDR szzsBIT|d pHYs踸tEXtSoftwarewww.inkscape.org<IDATX]lUGkW-P)NSЖYƐma 10 1(»0.@Ld51!!ؔŭ[X>:vrhg`n*t<ɛ99y9 1`PۀLrfң//q LR[`̷Xv{mm7oԅOfB5`)W-'N8L&Bcǎ5oBx@qr@ ޹sVU4RuKYY v=t߀iuHܽ{w~QQQ#GVlX,iEQJE._L @ٷoߒcѣ:cXv+E7YQX!*PźvwwYTT4Mܹs###zvg0M_7!S9t:#DbB4|KkkhTBܷgÁ5---=Oe ]\\<4|p!|ԪQCCORJi `/:666TUUu h!TU}mǗQqҥ+im>/(㞄3 cB86pK%%%Dx<^ #c xxބ[rL̗@ۿB_xxx ~P8Y#ل{P?b>-*Ppwv577u]u!˗/{xMl^wU4 1|1e˜H䌔2ԩSa]_fz/ܸq#TZZz0? AVk~GGGR۰aCؘkk\ PķڵkiSSSv`ݻir˖-z؀-˳O^jJ4SUU3//OTUU-q9`ZOtvv|`0g$u]UŪi _#8 SPUVJڵkلb%ɫ8g8클^J={L$7~3 Xȴ[@ 8SCCClR*a+**.4&^?獨irx`` }H$@bdV|5N~{!mFyu]_cǎ>i@(rgRF|^Q|2gyRt(2"dIENDB`gammaray-2.11.3/ui/resources/gammaray/ui/light/icons/pick-color@3x.png000066400000000000000000000046261413310443000256040ustar00rootroot00000000000000PNG  IHDR00WsBIT|d pHYs))"ߌtEXtSoftwarewww.inkscape.org< IDATh͚[lWgΌIc&$@'&b[6Єe+(>!V>J})B@12MI4]:,`P\__|%%%VJqEQ,4 Nwuu}x @Ij4T#9&eB2 cq:;;믭XYYڱtP"| @fch4ׂBy^6;/^ N. ȉ'2L ùsB㔵-K&xEqww̺Xff&#dS%BH@ Pq\&]voΝ9>:QH$B[VM^;wT沦:BtBUU6QB!nee`z^yD$>uԿ!` ePv-MMM9۷o@A૪2DQ\NVę3gt:GR-ub͆l <'-ݻc%IR/rJj|˗/?{K"?CW(zvA+(((4'1NmCYYYʳg暛~urk;>XH)UFGGo=z"L.Kq{{{EBĎ;2r($I",:thhzzZ]X5*<H$!0.Q:('N|V<OS闶^/jllN'R~oooqm###:BH8!ROSSSKP2,񭂷(jhh Rx[&!ݸqx{*Bv?~{)8|?(qXF f2*e4xv!n466u TB6>v[W777;|>Ӝ:75v(I'&xZjkkׂvGA%k5m#jN?Y'ϯx<͵Y~M&gy-dB766)U׆@-Kqqn2Jri%L&Ӿh4Jgff 湹9 X,$ᓶ7M+CCCL&3TsF guuu|x!$|X,?: P(''O8YBMB1c?mZ!MMMï?0"$wɿJf1<W999 Xn\6zY*l٨1BG˺,:[flNQ jf;^3P,oؼ*fffv߻w6m{}7J+1gꟅ B`F!Ef!DxU)Xp4ՐߘOIENDB`gammaray-2.11.3/ui/resources/gammaray/ui/light/icons/pick-element.png000066400000000000000000000010441413310443000255330ustar00rootroot00000000000000PNG  IHDRabKGD pHYs;;̶tIME /IDAT8˕a3Jjd`Oɂ.+D0tN"!vyH t d 03ri!}W}3M$I,뺫t:$EQ` xk?n^럣vH嚪q>.l$"J&t!n1 pmgi/Dn. >fDžBR<<8=wgk;iZWK~l8~l`2|N_AM۶ R׻8PEؒ5 ^4 d8ory.oMh4NjڱAZml6u  4xZ|^G眓$i/`Y/EQb쨃h#>Fe؏FJ773p\IENDB`gammaray-2.11.3/ui/resources/gammaray/ui/light/icons/pick-element@2x.png000066400000000000000000000024671413310443000261170ustar00rootroot00000000000000PNG  IHDR szzsBIT|d pHYsodtEXtSoftwarewww.inkscape.org<IDATX_LSW?^J-:&8f| fIpeN%62]!B@"M6a(837xX!nO& ,2 3YA C[z=>{wΕ(d2vt:@^^^1@rrrrr,4MvZ^^޽{h Bx >Iw޼б8 p'%%m=tЖEQ\.-^NӴ---fɲ{/9rիWogdd85kXt@y$i@ju|lt8ǁmڕ@ĭ0111c]]3 SpZ` :ֵ~<PWWj="?$f3339,9sVPPPJL@mmmUˑF1!-- z"ZVVDU՟*++|>bNNκ@<˘R> X,ȲpXKII9|%I ॲ2zɓ'km"v_$H}}}jkkw[ MFYBh a=`+EݓB-f AD)G3~glڴI_SSsvT `$Q |l.j6+s^m2z'''Bv8πj8`2o!4UUG.ͅZSSS?JTڎ; !+WL[Bmvvvi߾}S~_Bpuu0S>PTTggg\iibIIN{ ߛSSS]j% 𰿰Pۻ^I.fEEx/_/s_Rmmml6[*@OOτfk3<<1Y}[p'UU _1H%r=)Vؔ>|8H:rDŽsί7,"Ҹl&EQ*].׍&U`q\DX'@k02~$(f#% 9H#m"˹=ta:IENDB`gammaray-2.11.3/ui/resources/gammaray/ui/light/icons/pick-element@3x.png000066400000000000000000000041561413310443000261150ustar00rootroot00000000000000PNG  IHDR00WsBIT|d pHYsodtEXtSoftwarewww.inkscape.org<IDAThř{LT?wUXj!@kWnITLHSmMu+688.j&M lGYl\QCMh:DEE TF <,cQ{$s3==9#\.>444\5 2nioN7#44tl3ӧO9,,l~$HhFQeê222fdd̙3O+0jm۷o?t>S&CZN_3D gώk}+V]fU' Iz>F !77č70uaaa`00L-\0k֬_S5c3Ǎ1P4l$I0 ^SS;`0l'py IZtMݫ3LgN @bb|V}A2W7B Ĉ>:KNKK۷ojE2H"CFG);NoHRa? ])Wf)&j$I*((4F`2/1ZA6-"QE, III1O-[Vԡ%wĉ<ZB;oXlt=/^~UmmmM0:_KcUDʖigN:~o[ݾ3M,nheO]!ݠ[Dq @)%, ~hw}xB<Zlͼ'um}(G|b1FG}mh>>q6f>RJNǩY"ښn< DA:~B'r"/8B7Nbd`?{[[[;R@Hy۷ׯ_p'''_0a6Jvp7nF=m !\Ȁnxfkw #R5G@u;\S)>55\`Q>L0M=(UZ|>?$9qF+&r!|TOcccJTI TWO^/<7rb~~hppp7ȡx<.ڈ%av_6433]XXX?88n@'ds-P>òX,&n\X^^P(h)#[Z|zWa)jccó,Z,/e2RZ7XmE^ipwqq1x|i>^8>nnn(ZU[%֏eRI J)u>+ h)Z-R>i+:ȘYx+A3UOIHS?N !@IENDB`gammaray-2.11.3/ui/resources/gammaray/ui/light/icons/redirect-input@3x.png000066400000000000000000000030361413310443000264720ustar00rootroot00000000000000PNG  IHDR00WsBIT|d pHYs))"ߌtEXtSoftwarewww.inkscape.org<IDAThYkG]ևm9ViDHbմM(4J= Ő=TKC1ĆK4ږ?;3=̮Y[c`{o (f>kW0xj6z0DRJ.`RIˠZ NBfB0?8KkZ" m~ȁa$"5?w (`ڶ}[syt4p}l0DHD*@?ԇ^v6"RcD->- ~$eVy'ul6J$ѢdG.dԅڣGO5\K]]гW̺Oj |kuؚƴ8/}` M.#/,x<> -[%l%s;[[IT88ȑX(6.?55>=DbgMʯg:NgI/[{Jmk{O˲/~__gHX4Fɏّpr>kFe#bqߌ}02r ZuO?qǾuww_ƅ B3g )*!D ιbl:4M+&'z{{wJ$"ъ N+ӫJ>wKJ %0\N:0õ(-Y{2Nᜟ,5HJ8_~%VWWנCj²_Rm]vC8 V7<}t?)7Xо &Q}ϣ}hϏjAS ,S.]$X _¥sa5`aawtttX[[[ʕ+;;;)ϛǤQ8n胯rw @ZhvW^\JɊŢBp߉]EB>__3wY `hbnjy2444`&{j ^aDX*|;499ڏRjVm۷oor9F{{ѡ9W19oL&f7o.@[bߊm |9t8B>T*e={gyٶ-GGGrܯP[ @-Z39}dBe1~0`jBD&&&]ed2< P*0[mI NOOC81 tXU:;=z^Xщ24Y'dp)FPebҤP(p"21&C.M..MR( !q 0ƘU}%aXB{`)Ƙw8 _p۶|wm7Vxp(A* %C k_j\'"IENDB`gammaray-2.11.3/ui/resources/gammaray/ui/light/icons/server-decorations.png000066400000000000000000000012121413310443000267710ustar00rootroot00000000000000PNG  IHDRasBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<IDAT8Oha?;wcFn+ .=v /֡Kc \ҼXxI29Z׹.|;.DNy %ƫik㤅 f{ TU P4l'C4-,a xa^!VN 2|/X^Z2i 4 hk#gd,ef8$p>F>!cZb3,Bg@@UUa+?]ln/n _p(H$fʲ|r=rjnz. 13z;==$ɷZm,J;sss-$iړT*H$RnNFwX,zSSSVEQp7=} |`08v9NebbB[ZT|2,8Y*}߁}` *IENDB`gammaray-2.11.3/ui/resources/gammaray/ui/light/icons/server-decorations@2x.png000066400000000000000000000030551413310443000273520ustar00rootroot00000000000000PNG  IHDR szzsBIT|d pHYs^tEXtSoftwarewww.inkscape.org<IDATXLSW?`u&5ѩY !etp&&KLtY 7k\E 9eiVi/w;+VQ8 ~9s 1錯Mό#7n2B8:4 OR&idDu=77n8RLRǂS0\(B҂zB9 t@A1#"P"x @UՏo%BՂKTUHOA!krIbqȈy8$uOcIE@UUJr :*!Fr\Jƈ@n9I ƈg'% =&64OD`4Q0  K`RM&&Oߥ3\`*`M҉r;M0c!poP +((YfҥKV5+;;;= j===SNy4phkhhCQ (a*0,**ضm[ dee)Νn Vմrʜ͛7x<ʦ+w>F~?| e;+Ѓ`+ݛ,f뀦UV]z@p*))z1κp.+㭭nqEȞc4i$lӦMBPehƌ-R[1b=\o/_~7x"eeeǁM~ب,L`ڵk/ X,hΜ9vjjjҶloX~lW[/P0؋}>zl6^ʁUW) "ж@ffv ϟѣGP<>`tOFSQg8q l@]WWdHD4-cǎ_%2yˀ{Bؼyes-[ٳgۻ0z`1͓KKKG&-+s-ŀ@b2(BBvwÄ 6-p,31"Mr~Hb֭3O>$D' -\S7ttt4->449}t8RVVڹs!Ϟ=l6kݞ'}#]: $6#Cd)$gNZ>+*dh r`?b@pRU#2O05X$U"{5M{|EpqLvUd2(hXX:iu</듿+d_N1GѱQ x峂v|PVVQeiנUUeۧd2aGQUEQtv440Ju N4 ک!`ik )~m~1\tI^s\:R0}oMVXpx/bcfe Pc 4ƱtL$׆D"fYV|7K "LC:YD<fAĕׯNN%|?!+,@ ~*nFJ72V*ay+ ҃.984pIwtT3ih,6f?x[p]`'Wi20$6S"S֭֭[8kVv5|"/Ap{$%oHF.k?{7x?i$/xMKLMˆJ/ ͟?W^^*))L<֖ؿGGGGٍA=g1skҍ[`ͺqjWx*-]4GJE)W9}tjϞ=lrCus``ذaCr}HBv/9𖪪566vbL:jf<'?:@аlٲܹr)|Ǐb#ˉPldM$axh|P}~س}z˥FѴ\y͌ 8DU{#:K#9s,_|i#4JDLF/r;… +Fxk4v9#@)‡{zUUv}ǎ>oQb-Ydb, R,Y2]N m۶?=ig4^5ͫJ0H$Rp0^D8fh[ JXRVND"J :th`ƌadb8"ͦbXWXQ PWW7o5'*OOOOzKWWWZ#Xl%+W\]]= [ W:έ#HHoommԩS#0ٳ'nSRw$"s#h4}ݻ444.s FAWhtvXQQ {hrݻΜ8qb LHR#GlٲٳgرR.}M6]. ,Fqԩv]3LHY]׳---UV}6g}A_WPCb1"K*-ZTX>wb{M%-J/^4 Vۍ*p\N#G|/+9Fl rOWW#\ҥK/^\zjkk'l6 Qho ={6۷%& A͝y+WrSxL?'!)A-8~|@d֬Y` `P dgΜ:ydرcSTqČ#Aͽ^2ǚܝ{p9Qo]*9"0*yuM)9۹5LAHAKԪJXd2xqxIsj~Un|q4{} ds@9V3Zf8ɳxEIENDB`gammaray-2.11.3/ui/resources/gammaray/ui/light/icons/visualize-batches.png000066400000000000000000000012551413310443000266040ustar00rootroot00000000000000PNG  IHDRasBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<*IDAT8}=hSQ7>`hH S2)D-m&!. K1S;8ء!BRIA+b1Ѿ4uU^8.{FiTP)eZ<M!8@Z-xa`\.[Й_[@ [[[OE...\.}% tyluuM8bwmm Ӡ}31DhӉ#RʣS?/QU**JX,Ntݳ.O@P6gN7~oTBnllԓdn+@Jy,4H$333Mb|zx{ss`aa-p0<77d2=ZVVV~d2C_Gѽ,Ruzz:LDN#;M`l6?jNe&B3---] OwwwGF_^^ONNVUg. B w}>ߝP(tp v}^SԻl6/8y"{67נ!IENDB`gammaray-2.11.3/ui/resources/gammaray/ui/light/icons/visualize-batches@2x.png000066400000000000000000000026331413310443000271570ustar00rootroot00000000000000PNG  IHDR szzsBIT|d pHYs^tEXtSoftwarewww.inkscape.org<IDATX_LW̰ TD"`6t4i)hBM>PIx7} <Ԇ/414J4i&45$64nJ!b਻gaٝݙ>(Uw&%關=wϹC?Awzصk; ԯ78y&H53rGfE 's"fg\ e`d bص00` >"`1o-[, BlYob{/F37boo}|ǁڵk;5Myjj(**j~D5֭essskh:]]]#b}^~'NmTH$ZwwwE[KKKz3x>C477|~MRTΩ۷eD^=ۃ̎nD[#P~O$ f1q/:;;/1(YiВK)@xb#Rdyּ? .D"y9\%77WViܹsSNM&IDi1Hee`D\QV[1N>Y'"ATR'ҷ`3x,@vYN4R&-Zl"zdq6|' k^=`9۲͆&W_'o5IENDB`gammaray-2.11.3/ui/resources/gammaray/ui/light/icons/visualize-changes.png000066400000000000000000000011271413310443000266010ustar00rootroot00000000000000PNG  IHDRasBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<IDAT8;a/₁u% d 8r,RY؆lS,hRY/lEH%SMaGo.V 9ΫD}kRy6p lۿ;2Jy%cUuufj:izbyj+NpGk]L&am۴W^x\.gvh4?%ǡVl6;B0 f3-˲NAeY3z`6mS,/RQR\.j 8q  y#uSZ]}l6?VNE~}c4M#Vk * j Y )u#ģpx8SW"ښ4MůN N p$IDD$IGxW@xqr~~~h4/k;{k.'u\0HȷwGIENDB`gammaray-2.11.3/ui/resources/gammaray/ui/light/icons/visualize-changes@2x.png000066400000000000000000000025771413310443000271650ustar00rootroot00000000000000PNG  IHDR szzsBIT|d pHYs^tEXtSoftwarewww.inkscape.org<IDATXOTW ̅K & !Mu%֤&ԇ&V^'ۇ6cʐB Rnbf\8Y}382h1`|ko965ɮw3t!*u 8 q~hu0ǁ>'60 (g^aͦp8ͱ>SP/<Ɓ*" }hh<tp_,"bJ.^x8~E^D<"m!]O@PS@ʊn&|d2 @}}=~ٳA6M. L翞>L&]n!@mmRJNx(eX]]Zx<M)E,*ŢJR",RUeR 6&p!cFa؍RZZZy]RE T)׫DJeQ a 8qnkkZ/644N}HD8p{eBAnnn x\70,˒7 УQR(`k0 41M0 ]`aϞ=7niSNr1Ǔϧ|>Ǔ(W$Ա9ƋRl8e)sdwҌڜ;$S<[7#`-Q%vҎ If;~vꯜ[\M"ʲ^댚?[PN{`yoh>. ſBP/%J|y* 'L&-"fsW\;J>P:H(J |cY%XZZ||HI7v36|H$ m`03u$,`Hәؘ)!U*ۀPp¯Rcn(yMZOOp__4>0 Ā[jJ)zعsFS2D d8ݻb¥K~rO'*tR/ivG-IENDB`gammaray-2.11.3/ui/resources/gammaray/ui/light/icons/visualize-changes@3x.png000066400000000000000000000052161413310443000271570ustar00rootroot00000000000000PNG  IHDR00WsBIT|d pHYs))"ߌtEXtSoftwarewww.inkscape.org< IDATh]lTs?S Iq)(  E*VP%ύDClfgy4e2<T5gĠ 0}ˀFA0,)BF/ DHF9ZxX7Jyw `C`|e0Gcp8 G+W^Q f tx$f?j*5_ 2)Q2^O׀ $ -0bigΜm֤B5۶sX "UU`Pe,?, ҐRRJ#q4x<^988r-[D"CoV(N3ZAssf^{-Dw3?L=[EȾ}{ҝ;w8{,ϟgxxh4J"m*822?wEuy^nܸ!FGGj/d2oJ#s``2JW\i|z>׻(5 ɘdz||Ҳ,ҥKŻ ˲f4G-.] @`bƍ| YWJ qBHDud2LLLFFF*mK)+Vaf|9t:- W4jLwBm .wAl6kܼy3꺮. ŋ5)4۶t:s]אRSgN]6T/V4JRֻz.Ff32Wwh܋Y%3Y̔22^%2jzPSJ`Uyy?B(WS{I-`@髟bo6ĿhsDy>Tqt]/Ąoyw;0p+h "AEo.>%i&Μ93wSׯ_cWml.*>Q6>I= KAU +dsLO S?~_ذIENDB`gammaray-2.11.3/ui/resources/gammaray/ui/light/icons/visualize-clipping.png000066400000000000000000000012541413310443000267770ustar00rootroot00000000000000PNG  IHDRasBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<)IDAT8K[Q?ǤCCP >b']3 B(ЪC bAL'6!KpB)H<rD/|{8܋1n"7F0'Y:uR[[[Z zJY|}P(t/eYWH<X\\41f|_m<4jXT=X[[.bA)El@Vqyyeoз}m `nlq15L~`Zۏ .yF:22"d]4ellL|N,K)q:Bfb*`0ۇ@rLc7(h+ @[2vZel{1)k f}Wz0| |h|# 1H]N? \t)( ===U cc!GYPgϞ5‡g{ l6LRJ躎 V,HdP4,tD"!2 RJEfIٌlFQ2 d2VVVHᰈyf3NS466znd2K# `^p!H$Dmm-"vz{{ESSV5x.KܹSL&3 qcbiiIL&qqzX,ABX UU9rrjjQUgA4bՑ:^WDžfxx͛7g~~U4Mnt:蠿!133#_ν{doo/JXJ BtrU^zӴ*555F, v1QUs3 je+?$,c\@ ㉦e@nUU`0wƍp. Y\?11Qg^F/OI kƶh~򧥥܃d~7lVcfοEܝ;woUR'\}4x6S&o/_J򝝝@W(ѣ-Ӊ'O,b@ͶmncD)ػwokыy?(ɓ[)Dkkkjr֭ WNJ5Ms\SSSƉ9 ;<O'~rvI(ɿ|ѣi2L@ 0ſtuuo7JDݻw?z/^xiN@`vE9~̙/<ȡCr FUp8~ g&Bi2@ ]^^*IS5 \.r}y]<OwwguBR ù›7o/VW z}psҷhjSqlH}sdn$" XU__r9dYN_tpbR^Jz^qKl˗/w6,_Iqs^)=0N*l0c/!QLTs&ɆHGg\ 7;0&6 .ǠUu&5"6)I]]]S ѡ( /^G4V++V:~cE(l2׋fCKDdsE… \|]E+W} 0Tvu#3@+vK|cggk. rZa~ 6zꗒd0?t(> Tdn[f(d]v0z~acRqo~YHw֙OQ 飣†T*Ut MPR؞={40p8"_@&VZY,DQZK1XϷ~kXl~}X sˬBl_}*:;;(+`08Dhu} HVAۑ&E9i\MPUEQPUM4m8ZX,l6(""&@`R5N tT*E6EU)vÁrv)@y00}tt4PUL&XLd2\~ID"tl6X,JJJpx<~χf ڜklTj|Z&AQJKK|2ccc$ (@(B|311O*++ A=&r+V @~ .tIbhTH$Bss3[lV^rK/ }~IP(D"&spQ"P s K<G$TUe۶m PUU# IRY~=ׯ'V>zzz$I= QUU[ @D&IRӭPuuu yiooׯ\Ç.\f^cݺu ŐeYrl6\.̎;xعsDww044Ĺsl6Kmm-nG y=sFGG ^t:;COO%%%|>A ( cpA<ϟkꫯȾ}x78s挠inq렼\k׮twwz룵op8LCC555NA~V+eeeC=D$'Op8x뭷O|$7g`SSSرc%v/{nBL犧t% ǏD$&0Z|6UUy'NL}aEc@P_YY9pnW_w^r050jlݻ7,dYodgg00̿\_\zz)C:::4*˲~@%FTmذnǎ[ԔϘ>,XD;N;w^>{lVQ]Q]UU=NO>qƣ(~u\IoJ֭[8p#lVUU5g{}Z_/t/ t>O>ɣ>Q˲b1}ɒ%D"ܒ%K6çx<|Ҿ}9s*0/@aZ ,+**ygDQdYXW^]tł~K]UU/bO>$Iq~2:Fr0d6mڴeddh4JWWW~88~oݺupSQQa+**(2>>MNNjd2ߟ~٘, 0tټƘ.;2w/)pmmmbR)K,=aQ0#RuA cW<MLhفͿk5sc^&875:6eq6uԼÓE>uP1$B0=2o/D>9SnIENDB`gammaray-2.11.3/ui/resources/gammaray/ui/light/icons/visualize-overdraw.png000066400000000000000000000013301413310443000270160ustar00rootroot00000000000000PNG  IHDRasBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<UIDAT8OHa(ub&-1! "F"T3HIdu#e 3ns⟡{L%~=ϋ|y!@3D9:L&B%HBX΂XnmmuttlkMMM\n1`RPJ6zzz4@^#`p3Hd+p9JU OuzoBi.X" Z&'\;\K|~- m 4 /,,|' NLix+`j8oF. Ǔ>Ů.0|k]aX;333 !6瀐iH$1777ǁ9DfnM@1e|>-*򺮮n41==%pgpp<Kx@иRujpl'd{n@;RʑWLц@{CC}p?~ xp뭮)X]]=rT0ѿ"JFw221uֻIENDB`gammaray-2.11.3/ui/resources/gammaray/ui/light/icons/visualize-overdraw@2x.png000066400000000000000000000035021413310443000273730ustar00rootroot00000000000000PNG  IHDR szzsBIT|d pHYs^tEXtSoftwarewww.inkscape.org<IDATXŗmLT |PdV]E/ʮ]PS?HF%}k&i$T7UM]1Qv%~XKln֨F^fp;w}XZw!INsfw)I@3\>/R" zUCCw/_^bZb1{n۷&u>@dˁMEEE<πBUU+H$ !D<WN>؁/lˀ Bj<'5|B CB444(@ "VQQ ! hmmiHKK3`?6px̆&|ԩeee Ѩzc#Sɇ"`ʕ+-ϟ?OxFf#2cTp&f X޽{aAA۶muC&LLvyȽ}utt#K@&t:Vd5j3tH+L>OG0 'H)6dfH 3 z1Rei*//׌$)yVU.YdiFFFf4zN<:C6pF/(mooDDBs\k׮ ?~t"dRoiibZV?fo =HzƱP(8uǫVUUU|hnۻuַR`l LA tonn>(J{n p>P|k}MM͝ ر7z$g>EД}B`P CO$ׯgdd x ƞ={~t:;n߾}ٳ{*`;իW/B%%%^CxVG\.fGFFbw _ #G6*)wެ 2`_EE/_Toܸы#lx9lllḃP#հoHUU=H;v&W?iiibZEE 3Aȁx<۵kWVWWWr}l7edd !>Bn/a__xŴs̀mdNe/.\(yH$@ 噧 ׫__X߿+EEEjRxx PwUU#ĉW6nx;5DB}͚5˗.]j/LjP(:ZYYyxU: ||޽۷oMbѢENEQ@ff&gΜ 48ِJVf6 lRXYY|ڵ֜d" ߟx<'Oݺuk^xWƳyv!eCKLcqd#HIA68}a"z-Hd!6nI@(ƙ)sݨF+IENDB`gammaray-2.11.3/ui/resources/gammaray/ui/light/icons/visualize-overdraw@3x.png000066400000000000000000000062711413310443000274020ustar00rootroot00000000000000PNG  IHDR00WsBIT|d pHYs))"ߌtEXtSoftwarewww.inkscape.org< 6IDATh{p?璜{$k[ZnKEiA7$*T%q,T:]Bw[eLZ:jvh*+2@6B \ pNsy/y搒3}~=]qwiݣ,GބP@^b$ v k HƗM  ,={vEcch42MS^ip8xȑ Y]B[a@ Ν ;vlɓh<7xlƍU`_ ~!cǎu[VN4?? ppr___B) 0***~|s ds˟*i<0b۶h$bJ) zK!M g?...oxl ΈEJGV"/_6R_8ɪWNt]W{BjP|q\?_bO0z98(~ |zX$QJ)e˖bN#(/Ds< `? Lu?">2/{}rH6 )2+V8z)V_B$/'5BxJJJ\^uĉ+Ht!Fc^r?'4Zp… :RBoKgll(fnF&aυ3f`SsM;s%{n_Ɍ r}RD:DGQp{qz'O\CCCP(~N?~3DHamȍxo' ZZ =!,E܂ҚW[[[5Mt]7M􎎎ݻw(**!RCͱQf'0 6ﰀ'0>D*RNOOV]]tUfff&ĉgΜtvv}-.\0cnp8T(}PYY@O\'P z Ӏ:V|{ʕ.\nin޼@ O?4uhkk]jտ"={؛&6o:&ӣ#&3`5p֭[ "CCCڶm.zHs$[? l@+. 6|?iFUUU5o"?Y#@)#]-[|gNiii<?cd(pFwgv (ܵkWoݽ{/yx/>đ{'WH&aRRʡBu|2!AC!'>FN:50o<{9[Q$ 6!z]8:u[oճlٲ\$g32j2qu%)۳#@κuf#7^5[lNBxzzzŋ}=P[[[7b6ظqcz뭷"sL49bĩSvҥKknw޽{G;ol߾=zOs=_y啣gϞH$Zvy˵w~Ջ,$f^VV6G-^lYVzz+xWcxggQTTLM~_z饞ӧO!cHuE(̋Y2(|ywԲIO>t:oCN[[[!o^=r0g=H*OVnXg+Dwۉ3srr-Z4iΜ9Y)nP(dƚXl HҏhǾ#FJ+ɐ$E"wvsbG; Q #y'ȣ?zmFͩ\%>k\ M f OCpsDKp7dq}E݁pR\{+IENDB`gammaray-2.11.3/ui/resources/gammaray/ui/light/icons/visualize-traces.png000066400000000000000000000011051413310443000264460ustar00rootroot00000000000000PNG  IHDRasBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<IDAT8ϋRq?x* " ((`t1-\p g&ڹpJ |ˆP+a`$EL- zXC|{-F!AJgus=O i)B /ȸ&l`v eYcN,cc hWUl~ p2. zޏD"Ou]?zl5]׿wVn`g0pYTޘiiS.[nO3|>jڃp}:dbJ{d2 UU5C-Q*zbhtK4f9~tύF㕔򥓁 @[pع- IENDB`gammaray-2.11.3/ui/resources/gammaray/ui/light/icons/visualize-traces@2x.png000066400000000000000000000026161413310443000270300ustar00rootroot00000000000000PNG  IHDR szzsBIT|d pHYs^tEXtSoftwarewww.inkscape.org< IDATX_H[w?7غVh5 u%N uC+CdL:T@ڭO0h!Ɨ2Z6ZIiEu[X,f717wm3b/s~ Eg OHz[? /zleY4KX.m^K:g@f8QLq )ڿ D)f _='n4d_쯞KɞXk lIHemNMl> QH,HGW,H ,-l" Rݝdm?Ϛ85qTYӂd _G~Yw"'iwٰ 6}mCxxGXP $`Zv=ztCl6-/??Dk/_(j~(O#J1 $DNԩS;;;wInJ|>"6M7?drf@2ׁߧ~XZZ:h!Dۍyp,SN;@Xg@&ǏߍF\QQ1|! ,w_~<].U-3̙`ǎTX,^}cΝ'NY,o Dljjjf}O+vb0Tfs?&P9rH$Zj sssO@U[[;0??t 𮞅'€h_cZWW#6c|||~x<)9s' <N_p!F\Op)<| 7o\&l1;*++A_MӤ;b@%1LR[[KL XvpN(Q  %6zqff&hR{@yy62hBrgWW׍eEQTYׯ_w_\.ٳggK077VUUttt\xFV O2%G 1;TY^׿f&oIENDB`gammaray-2.11.3/ui/resources/gammaray/ui/light/icons/visualize-traces@3x.png000066400000000000000000000047161413310443000270340ustar00rootroot00000000000000PNG  IHDR00WsBIT|d pHYs))"ߌtEXtSoftwarewww.inkscape.org< KIDAThkl^Xnr+R$ E J RTjJ?(rԦH͇JU"4$TQHJ,!X1^ۻL?{zd F|ȑv<3w?g ڷvG~5,H\u 4Y@NBXK; &0tm@?]Wx2?tW"~4'y 0@)2Lg\u%o01m͆hLNMN>%}Usv!S^IXWTn0B6) K<L8hBZOd7)ntd_m}Ϥ,ۊ 9@)mll\uVsGjfɕ+WGꫯ~F3`jQ]"w"dy.ZO~Ӳ,-HXgΜ1ѡE"|>:G]]]Vvv6H$ohjj066\}BlxhUUU}r٘떮ֵk׌ݻw^@zW%@{aaM7 4 #^~ ѳK326wK7^}vRuk`` 3?*\"ρ ACCC'a944ܱc%ďi6p̄sB Xm۶ov ??6e˖#G\H$_7qكN˲@KKKƻp7vwwAUE˘ 0֬YS`NN:#q ,ݾ}kfUUU9{zz###D+y F/^\RSS^~}I0=Add>D<>?w:;; ]׭ӧO{kXdѤa]>X[MVju=CCCF xe;>_a&.] Xi@(|r:Z(aT)K4MV[n~ _" t۷jeg&͋FyuuusN>=L&# LR^o7y+B8GJ&x|ܹs~da)Y@VYYY.@uuuuC" 9Oww1&XWW! "R9{dv@RM0̶{mYh.xUXh4ہuuuOB>:-L g~4ŋ1D"_{wW\yr}455En 6mnn˗C'DRf/5sON>3-R`!PiӦ@CCCիMxdن+L vFM_!UTHIENDB`gammaray-2.11.3/ui/resources/gammaray/ui/light/icons/warning.png000066400000000000000000000010551413310443000246250ustar00rootroot00000000000000PNG  IHDRasBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<IDAT8=Q}E YVl&"n0/!$)E"eA?LNZf`!S8^;!)+ @M&] $ߵ~GV#'-˲RӲZ)Fp]1櫈48IJ(8}?hl2B]p V\n*6=[[ Ȳ I|)(: R@պ@WbBm"r] |fyq+$ض}wUBz_$EkYk}ei8Ha$nk pi<-"6zّ(+)A,a{qHtNgY'"I]XT R]޳KeIENDB`gammaray-2.11.3/ui/resources/gammaray/ui/light/icons/zoom-in.png000066400000000000000000000012071413310443000245470ustar00rootroot00000000000000PNG  IHDRasBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<IDAT8ϋaǿS(ND?`Q=vآ!*D!DBG=Racƒ^WG8,as / xg6Zs~Ըsi<)1f~hlL&s-Ge´5JZ{*g.[Iv%I]ݧR)1&lv.0Z0 >//KB3 cM2ndp2|F^e)[6glZ:Z~tn'{˚=!6|J)1hԙҎb gl ^6dYeYb*Ŏ%ɲiY&,z^ E"R\oIy)}ζIENDB`gammaray-2.11.3/ui/resources/gammaray/ui/light/icons/zoom-in@2x.png000066400000000000000000000030541413310443000251230ustar00rootroot00000000000000PNG  IHDR szzsBIT|d pHYs^tEXtSoftwarewww.inkscape.org<IDATX]lUۥۥkB4ZؐM`oPH[B VCQ)&gP">BbHm|h!I)Kwm30e)Kw+ٙ3{ν8Ce'u4d@&Ч?@8`dk`@@LC{#@ `Ҹ \p)E#-<L0aB֡C ;;;_5 cRԧ[]رcEEEEYct%KoBcxiVVVS>,[nMR7Ϝ9wرkWG5ĈbW@ m]]Ƽ++999/o޼s[/"WII:Q#x+ ,x(eb״7oYYY504 ?77h4zAD^f:J\ED $ǛvB ^Xdp&IO`/0ETY>oeYhѢ7Y$% /0xRcɒ%sH1 gϾ#"N`<`b^ccW: O*X,v\Dd˖-&IF]-3u%,[Ddԩs0233}J)C).L8r%EŹrbXX$mt"n&#* C(!|a].kҌ3555@7ñ}Vv-v\cnwaj8A`0 fxLh̎;`TkED\=0@T GべED$PSLcf嫁gKm.΅]={vR0M(,,ܠ' GXD$^z`2[Qvʶ鑦m9tB.P LL™] ~ioo?xV@vحr:ZZZߚH9//oH & tww_())yX T~]]]?NxވH8u$iBϷOx]]{n{%P T;Ε6mtRn>""q/3gܖylw z+O8ysnYVHD}po0eee[]Hy˰6L켗O8qՁ>8."XJW^ȑ#&MTx*DD,˺bŊ58_rssnH]0x[{Y;['7UJݬqR!I/_طo?y~=<`Y~`t߿g[[ۏϟx: 1 Ne3 >IENDB`gammaray-2.11.3/ui/resources/gammaray/ui/light/icons/zoom-in@3x.png000066400000000000000000000054061413310443000251270ustar00rootroot00000000000000PNG  IHDR00WsBIT|d pHYs))"ߌtEXtSoftwarewww.inkscape.org< IDAThՙ{lTU?wL vmx` H, r v Д 75-@3`  dʕ+nj;^>d2  !`0~Byy;v8@{4WF[ڥz+ `$SRR|}s}j&7o޼g_|yMss iF""w"R `Yqq^oYZB&|f`) E7FZ@Ala2~,z=}{,//olnnYoBO>+Vzĉ3v{V&2eʞFMH Hv ߿3g^t8ֳe%%%bdʕ%%%;urԩo}7>*ҝZd+ q2Ld_p8fjTUvwe{ZUEQܸq&33o.i$[^@!SN}WUՎM@~k"2 -9yn}z~U_ŋ&!/7ʃ{ߔBB B7osO i5Xd:;;:?ӀHŝ)@ 0jN8/ S~\oL ǎ%233s1Xi;mVTT 777g2 EH(tyO.\8s鷀֊ۍ9~/kmi8)hBBzt [BtCML7N #|~]]0w}y1}MEMA'NTmXS.8P4iR62ĝR䎎駱lٲ(&lƍ[[=A1q_bHPUK:СC|7}*bpR C@ p_z"?.T)ړұ p @QqPZ \**ӈS§wf!D\ k~ٜ< s9}%m}nO] iX,YYY555druP8BB愉~ɀ555u !))).vePI@ӷϛ7o`Hhii^&}??ӁTm+)H]~t{vܹSMbŅZdgYdiTAf@60ƍ⑜3;w|`nc4!k/PHBݐ鶽f_nhF&u)!>Dw0,hlldfl(**:R`X=zt"Gd(RzFېn3 Y4h#:;w7كhDd+# Y2lذ@^0uK( MipBcEfCՖ0Y^TTYQƕye?~tUnbB3fF(JvvdUEE"U;U(x<d )۶m{=!!f_XXxEE񆆆XAD3x<%%%ɓF= @ǬY5{Y.((`Hhjj:o߾k֬Y(\\SS\%dM]4 9O?mKKK(2rȇ7l0d2r"/Zf +]-0Ž/rrrnsڵ 9992_O6m(dPPP0O? D01lj|ɔĉݑr…  uȢ܂O>MyLhpywv[m_锪1@!3Z;y1߷EKUUת6Ohe˖`{4; 3Td9l5lvzr}ۃQv_ݳg&LXq/A:txEh &O GޭN[SUUv[[!!~>!ESo_vZ\p8 o=Dvvt$u[mQNJp8jjj6ddd<{`پiӦ/t:#VOO![to& (Y^kHw>苂e0dffN?SUUPU̙3'$''NNN~¨n`2&di?$9GġCwVk:l LsN;vl"//TUU.Bh?ہ`Uŏ˗/k0Z"ADa3g΋;[ +++wwvv !óg^? b?ܺIIINS\"?*TM m(qJrIENDB`gammaray-2.11.3/ui/resources/gammaray/ui/light/icons/zoom-out.png000066400000000000000000000011741413310443000247530ustar00rootroot00000000000000PNG  IHDRasBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<IDAT8RϋQL8"Diaj@ȋ yNBD%NKExu4OӘ{^\||ޓ IP゚";T1׉CBӴt1rc3qk' _T*(s4?&IlzZ KBa< x `@4|>Gۀyfժp4w組,cuI3 \!!6(g ~rF1$T*2fۭF8 8 R0=v l6[M&oh5ΖBgYV=Hx(NݮsΏ'u EVigB9RJCs4Y{<tbXB-ϖ,˷m_.CEQdY޶m{RB^/? E"RBgF>X IENDB`gammaray-2.11.3/ui/resources/gammaray/ui/light/icons/zoom-out@2x.png000066400000000000000000000030311413310443000253170ustar00rootroot00000000000000PNG  IHDR szzsBIT|d pHYs^tEXtSoftwarewww.inkscape.org<IDATX]lUӥKnk `bC6[ BFTmK5<!T3B( |ib@mEmHmR.l=>̴,P] d23w=Fjnv84 t@0lL\ ˆ@b6hX3(2vL4y#tl6N<Mv4 ''h4zVD{[  l/NgeGGǗ""݅^pEDBY[ ,28ޓ3g fQJ˗/_,`Ur+i쒒J)]D+V,"0xN:AD$Úy*C<'Cg,FDq ɹVnݺEDN`啄J3MoYf-)tTd=)"aT Jܗ+I*+ ȋb"b@щULRJ 7(Pm=06Nsܹsۯ`MӶ0N$۝R3 lڴiC ܻcǎ9@z0$٢t .|<L&PL]]]DD?L3g.2 c@DX4QiհU,YpaRJ7 C/**dO$=GHD$z+ ] +d׶j. 9P(tNdgg SpdeeBEDzzzxVxXT;5_/H$ґ:%%%k""gKKK:߿'^mhh944 ÿ,|nHijjzv 5-[4;w.>""yπϟ8OssYInY[uرOGFF^Ri!k-//슇D" TV+LfWJ]CDL/<|iӦU{{쿁IHƋ 9vWkBBЦMHZE2/mOOO 4`${1h S2')?/I68h۶m===1\8d[]]]jNwBΜ9s\ɓw#s#sԜ_<  M̛7P(B@ Ь]q7o! !j@M3n2 Xt8,\@X@pSP1~رZ*pz`.gK3oL4)b.),@RWW׿Lk,]t1%eܸqMwwwB11ob 7 Ebذavݙ~Z'B\TEW'2GN=J5- k***~y5z!s{V!DL k՚L( Xq#M3Ovz6mhBB='''Y\]?lhVooX? &&&@7Y믿>o^i=1,K*@{{7uMMM_|G M@2Ixs7\iR\GZ5 (*++2dȯbߪF!{P6$Hletۺ[ m9iii߅aʛWN$ %JzjGii&ML׊=ZbEK[Eń3f9MӴjDMJVڑIGƺyyО{-[9>>>%|є$D4Xӎ?r){Zp#I}Q7꭬]\\{Œpٳk5Mx3C8VxҽxzzzFTUUxR4M5jׯCZ2T'h'){駭_ /۳{nw\\gӦMizFqq> 0O:O,u}ѱcvFVn㚚_C@/~_q\Gp믿 lRVVŤTŀ_,J-0q 0Ց(--]gggS20@ z@a[Tԡ_~y' Rxl6TM)))%;vxvq f׮]M0aK> q y@LHQDPYN_ֶֶ""U4ok+^[hT@A $<^$0CI||s~uY{V,Ydɒ%K,Ydɒ%K!{YtKkd6ǖJܚmY͒1s+Fx=qM h Cs0(ڛ>O&Z}fɈ+ǰXOǴXhitmF=oXt&y@F@IuA,]C>Ajy:pF,x)6 L O}{nqqqz[<<0i+,@v!%dn4& Rxj-ûCz#@Dg\a6Ki5qR`)гۺ]P} _"艮jnIޞcUhdiňD ٫p1KG{y#tL bKzK뱵w3NyBl*QBX~GBE !QG>r P8%{.V|xK"౵SFy_?;7 we2&.Y%B jJO_Mk~Y$H sG<=WvkiM]0g oI0JNuiڼѯ1C(1m+DЏܲfCU4b7O]Ҝ~n/77B"Υ W>6 " !"ugmg=p=Υ* !ߝcd+H9w 8A X.4:\|U^^^53\+HSh-8%I򣗎<Di`>ڼ[YWPt{mA#Z|(NJ8;-wi7-v;3ns*Thf5*q4i Nj iC5i<IB;vުVEZ=3Iq~$`{c(oT&f9tloK끸H[SRTk7[in3P[uɦMh 'iWPS/x!W_;qm,`0h@Ⳋqr**Xc u\ItU93z!U Q_:azO'Fܤ>e3,J3s<ݶS&[jihPeM=_y=i0fP RRߙXc||D|XPa-,}ɜL{IG8cvս<9]]o,8H;gWT>cu'u^h?3^d,co끴(,.*׭˽KIF;&Rs;YL+[|%Q|/ӱ:+sˤqJ@j5ԬId,֚${IM+ܙT-LH_ʹ2;ˡ xohYngfRwķbG[YBr^-aCt,Kzi#]ڣ]h~F<,jkhM\ &=n蓝sYDY߷M=hOÃ_띭y:l<_4[ZkUpB oۛ1+X Z&0VLNhw1[7&)fwoصm1v h9#7+W"Uٜ,绞:y^&)fBr7(/DK>|C-`m J ֿnǎ,݁ܨ]c|VAs^Npsw> 3#Dx4o0IG]uO MwښqW+0?#G b=4i6"˽h🋿ː6ocdXpc٦c]h#R+%%*iZ\̫xԭ_s -hA(i9]\}6. o:r`9?CqII'S/zvڽyJ"/J Ȇ"8 ~ ,}CZh\+ctjuH -W^ @D0Fް|W5フ>SY@QpĹO̓+8sr;n}}}hXPP:7{kvgPϸWTXZxԖ*9gy1mVo޺t^ʈ1{Ž wsgnbW6+"J̊ʢ9zioY߆] GzR*F78`-.?g}-L5"n(99l VmߧkK( aa׾X}c<)"H;'cz @n5Ek WV:u[ƌPD5W=*Gdz?g;_햁h/0_+^w+7|RosKu9Z}bJJ('<cL n0c[龀 {Zunv>qq -_ &ȫ0B S7TmF_x˱ZJ࡯: rfoYtfCO]*u0rt !<+k^+ &-ߩs\#"AD} gOq 2mhd׽𝉕s/x0eƘBm8wwPbяxE֢dX_)w}ˈ\ ɵ)1zy]UEo V{¦-_\+j~XgLAJWtFJD(wpX#h= `vֹ#6ڈzŌ29ls IY$KL"pP;,dӅB֝C` W>k )!k@.(ʜxpZ[#Z/!%~0vFu|H%&9܊|ELx)?:z4UR,YuOןR=Zav \/P+@$(д;7Or{T ~"q[*uwV|U[A)Q^ Pg.9yX۬zx|Q@ɀPD ē|aen};]o[MaCv|U{HB`ͤv[/'ukBŧew.+9WlkӴs:ymgMi(,ߕ yvLPE8?w'}O:κ 4K: ۾Go3O~檲"LjTZ՗џ &7-9 ח~p~T(m?0gVy[:Xض3V="yNE"e$H1 lpS 쵻j' ,oqQws] 3]o Q]UWu&CbH~k>qYc<mMXlٺb?IQbӆE_PŽVk̻/0b!B}f΍ u/zémv_o'2";#Lz5WOh=uצj8BȜ}%q~g߹63 #l `(yf\Z?|w ۿkZ| #lo"HOV5wozG zI.?@m{G.U$ vB-B"37}Q Z&sr[!zM#ي#)+\}M\@=7:rۮ胀:9䔒*1{鰫EBW7H" QBSvoרF2 섐]:ottF|UU7''׽9ΧOv ._T]inS&t$㿽ճ|uenso qgR! vPu?Ҷ4h & 0V㉘adKvM:ym`sLLyG ۷F g< &0buW;>c.=򅨷*[_8_ƾ3@SCͳHjY4X}޿J MN/̮j|ey]~ 2i3S'B;#& u\m'Hn'p@Iws&:~%l$׵PPؽ"qP , 8B@׭2٢0h'U֢PNU‘FlWQQ t,ӽAhm4^wU~ްӣk bA@1Á1 l nQB;{UB;SJ*ِDz͙ /mt%R2Q -BrR#FPǮh*$ECO6q>56VxD;n6u;tMڡ+ts4)o#cI}BYna iE4H[b=\"u~:cd[wjwFZ7?o~Rqnh᤺!'*.[+7q\y%A"kN׼赦%tKhk\.#\ڌxYi,L`eT;v$N⢭M:Uq_`23hw-Uq7Cj=SoЏ/X_{#IzHTA_$%z1Bͪw\, rHʑ7E@6fVYWB;x3LLDYxǗZjTq<U(G:*of?@/ou|{+Xݷ.qKW$Dx>.c^*4hBDfG{%ԟ&L5qC# Eh)|V,6/HNn£;lcci1sv=lb<U:$.y!E$9V^r9UƘMa'?Q όJ _pDK|c7G]}#o[{/ T*^Z9&Z7[I;/֞Ni%amBmTU ÍF#/,~/5b/7^/m3J5j"7.r51j}$Ez$HX>&\?SKjzE? 랋 ń Z2U_ȮZ~ JJYpѪI[i\GEi4½'(q݋o\*mp ^AEp'I'k V+?ԝHVrgV̩$9EGDj=n&t9q~P-JݭB@KJERg^Dj/th{'H⋪I'tT\_P wUu7_~QzsGRqׇs17hN Ehni[ULYҌ+g5\ZKA$58Mn)ܳƃvC- Ds\S[5? ʃ|9{QDŽ+^Z9͖M 1u E3-1{Uӭ~rFLjZ/v[_V&At{-h;גB)8b_I'h64HMK_&)w1 ,7#©+_i{ ㅘ|?gsl/[ Ay%H6kY-PNTJm(1mK4َ)k8-E@ރtq}i%E$bՓ,iDw!f7צta+h`|P)uBm+C]D})uj|3gd=s6؃H+f~Bzmejz9*<픑lُ "DeFI}+Ϋ(AqK Ԅ,kd?>bcw )wEbn8avf}<w3`_ZB'}7Xdk<yHFJ缼%{ l.g `p@d%8i V%UP(z{^7ґ,!cCנ~:¯WUi5"xk^CIєGP(zƋ DHKifRۚUP^ZkMru.-0"뭯U&Y@ mʜdS`BN"h"zEeoele|7B6j5Ks%c`j֋5嵳hkqjzP\cw@ |@##_Nbp /jb1uljhh`]~.숵i@N&9mϫswhe)3h2  &pHU A!&,yu|bp&L1dƾ`„Mg`W`dNc(`}k8154ˊ, fvFXB"KAzf+|I,͐Έvԙjlpչݱe֎Xm-6NWڵZRJgH@C%0qPHC @ )m7YtTSB1YT#ǀ,gf~3v(k(2k;rD,Z  SָgF l;p[:mHV J @8 'רW H2+U(#iKv -3ѯ޻CP u+ iA/i >BXM7tK[h\XrA.-1k~o3p}7o}4&UYD JAL žeۊ?B#)yq !L01(aӘHz3'J7pP3$䞆5'_n]gg=N9w"e"|"φ {^}jB2ۖWRe7[F="b&v$rݫ^fJz ӘAi2=c\ -v_qrF bkq|`9M 7 >̯+jWks&(#ahW/-h.=[dvF"JeZ7T4i^<"S#K#ٝ5W(:͕$44 /c۳7vF&"Nw}Y簰ĈC j*D# V$~y /fB^N Ow\@6\w ž $Jn9ިfCkYU9GB[[W<=rk@B8p0Qq:G烘^4nvfb_jGͦE% mد+*Lp0{vbY: ]=M(zeU!yq -v[X~skzjt&ʘ'A||_?U(5?KVk7n\ 8\f}uE|I(6f]_9*T1 ⷰ)or3Czjx:|ݽz$3;bĜG/ 1"˒% ž—AʫsWluYZK1vAYVDM}6s!I0>Uek ⯝. ыz}Nb_eO.rݫ/sՑ0IPXZR: -3] f.n߂ʫ^C1Na ĝJp^ }ٻz0Il|L1–ݦCc!2+(kk3VuZ,mdوo(3簩IGV;a4c;w} žNFv&Lǿ,򘀙SXveJpY tcXTj~,0m#ꊘvs\ [\8ݽzeOB+CW|UvvȻw?BemFt G kC-Kz˶LnLu{SsB}l0 Ҳ1~k8(Ҡ%T G}s:'d?yy<X/҃_JdJM!;qi2 3!&t,*>6L}օZY_]_|$ⷖ8$"Y<- k{ziBP^Ś)sgB$ݹ+%H`Q\^veUJt^KN烤ii,ץ5BqSG=w@3=7׶ 𠘼W£G3_2"I dY +lW#7P(G6=֐7kšɼ9]7gg6Ub=Z4.< WWrvE/@~+^0Nų_=6(+n[=f(tP1ϋLMkMyLK.6;[)-u{_ کGecŒu9eu~b?n¤9G$Ŏ-.bf6eޞ'a,vY`XCDp <3[LXscT>{V(f6yi3u6Z̄N+7|I4}zoś}(nJ25> ^I֍iEV(wf?j 鼲.9Fw3Ho}͂!}aDh׬q sƶm}p/7?4lO&/ 7&,| s$+Ӹqn "9?=x#|:IW/vա|rċMጬĠ9~` O*7޻1v"t.DqSXhɐL@l8& 8_ΧPH$jg(6,(~q7|MDxQa&ϷaNqSf@{ܢf?J{<P]ՙw/`>`ͼ^/Ywuh -7 >zOi {9%]q5e6I߱;}Z1L5ծl3R]A}4υe= Pfe;%@HԥNh* Ul7ik-F1Cp!=h1aGZbHM+(&2 -̌10SfY6F)@K{a"ۖ߂3b&LjKwHD bR\eW8|+lQי}[!fIB1G vh1Eh x#.nItǚr.0ϊzQExROF؏J͛¦y_3*fF/B9epO.d0I?my  wDxMH7E_ɴwo!/b[!-fBm7;/5HgfL\}z,4 ?S[_\^ZΠ7j(b2n &Rjˮх1)wTr*ƕ"%gP쿸ػ7Ny)./)&$͘j45e% yc[T حH{d궹5J3Hh FoB9XbOL<5PMIhˮcIҸրsB޽q3IJ}_rfя1ZbfwWy˺-.HX΋H::;+2n[-'*f FЯ& 6,1GyX3] b;קUo#$1$eY &0(*2/|7l.*l Z>"خPdW\},ئT,jw|P׶=4/6c(҄\D!Fq]UOm;nQUNkv< BS3%@!x?}έP5n7w:/Ѩq8IYsڝ:v@z 4k2~h@22pWzxY̜KDAȫe (LoȖ]Y5 B⧹ YL!ØC.8ta1chz"z P~zw6T(eʦ^ͩw <ȑao55 HDnfel]s\uR k>+Hlvvt3 (MeKsK(:UtJPmC(`ٮݞ1n8:~y 4AsD 08lG5,E ۤPdq]NK5t-סs)[`s.g 'F8'W8DNgB28Ґ,9͆昻x^#hl[Z׮:`x.P (A8[$aQZ^ BӭvDUpÏ Dn)]zpj67S5k,ny~캽8'N'  >lgK46SaԚfL7g/;4oY!W~:ZV[y{B:||۔2w- mhq4ZJ FrG9i0UQwtX;S 1_`> 0 ӘiZ4$֩W ]w9WR(NߜiS{nJo-0 <_ckǞ;5o˼a~jꁠ>.+.\# maPX.#.n8tq I/Ia Rʟ|:lp!6" O=NH򂩋V}sV!)#ܭ1>0_.~ͺSkjuO#{6Nܸ%M3e{>YD851 n=嚿 Eof.nM PTYlO+aD{s`E}M>,> 2a@$6$& hW{i8y'^όg"ehQ#'qji—hk)1Gnq}.ZuheĞCV8۞lZ7^_>M$nIđI% ǟ~ωq.Gd:\B$d#;IOGo|:[05R~opJ@H ?yI[?g' Zg察7;91L ? `Lsϒb?MI,{6( W=|~Ao}΀Gk9}Um6ONX?'exͼQSLAK y 6@v3#Gvlan߳ [D0C5ۖN{¼S,RaΓI>xaޟ5&0@B $>c~6XKm0l,ll1ˠ T/F{=wv)?Zu33O1S[Ș}"3؟b,%q ~~3`~Y ĵO#kOݹQYLnf<`t\HR'~lf`dnjqL6-`)2u5ts5Ss' c<.0po1aV8@D>8O2O}c.!0\K-EOI覜b!"[%g/ g.3P^#Ό=#W<Âą?jDYK69JJJRR -yX7s;cGo)}EЏ <|n+;K4- IDATV|Mи2MIEP'< _[322ɖI␇/(ӱ1Z S|[( )R2gܶer01EhId@k8":M vHCF <[0CRRBb CU`j/3 f `$`bX#1ߟ,K{ 11#C~fv%B=((H$^`!UA% :!0on}CKP;q(gP@lX0+{p; Ŏ&zw^`>l|2nޱD6/ S ׷Iy'$i{ y\L:u[?Y0l!<{Z.=jGM a?TJG._:gek~ cM#@Jn80B_+@AAy  yG|T~ǎE乗E̳A$(Rj x~ᡝ!œ n.?&_PEcX/3__lX 9v92S ~!}Oq_4S9L3;B[^ϟ܋L{ ԃ"w92_c=93xm$ِgھ%(ok"WnjlOPhl%G*801ם>>cI d0h`R8V3J%ZJ I?9'Y 53>|l$Xд.': O[{)7Nd|֓obE0I$SZ-w$={֤#ZMStdk9 y{k 1h w Ig" @;;NL FI֣1 1Ąw-nUlYDrTuQrߜNJ^b u݃c quXtPr- Q4M?xuqYW׸g ,E ^xkSlT$?] !iDctnB^}/w7WxHƐ#?0xel,; vgk6};3Oh̗(ު; :Jx O+~8^ W͟5: sF!LϫK$p4E 1`B'I?]l' 6 'o6G/'mP\D/ѺT?l^k>1$JǶpFQ_B)㕄dwR⃷[2]oMa2Xķ!$)cM)xQH .ׯ?T\m@6u(NI2I - Nh+K-ɋlP:())I+Wy3V~2ͦI$n#Z:ltvi0T V; L70kϩsn4[n%{JSВ_p:,LP$bk;F .dP c$;Њ L/AADp`J@'翄JKLs 7(.Ldh1b2rj{Đh_Mu o$3DXBDILeZ1`mm.msaő-/,]Ubˍ2"nUmrx,DZ>ю-mBYŒYmkt4>R-9SRb =&6h# ^yōq=wlv_/rJqn-6IYz-$wHgB/%?q0LIzmi]#s9$摅&Ztꂖݬ9Jhr0]teLh6=֐~D\O̽b:= +797 ~ls=Ck.i(qۚ:0l(,f}}k`grO-zcp@ wMiÀZ`⅖C1^͐;Ē=Ŀ%*Npb41 ۡLr2@OAC ")q$'?wЛL{UncM#v]Vx/r  @8,rк,>c<6CA4p"8ыvΉT77PR>o!t2i?/)+>!fr>R\AKnm[7tɘE! -ұ;Ў(_z& Ɛg˃s=A9eWVɠSA8 &k@Ғu Px;.p7p+hS&; yA=)$B{4C`p6B xhyK8V 1NZ'A^B6|vDp9A3~M o9G _7~mo\U|>(Eѡ\ XD<2 y(99Yٜ-ەMl^!}__qA{ݝ앭W(Y/g@ LAYhq;MA||Osܮ)TV \r1J8(8*m}Z@J cvʾd]O>?,E4D,L.<BbL޻"hMMxLVFc0lah=BvJJo잛!ϨNt~(r~ v0DhP˒= N$TCvmQ[ aY= qݙO׀Ov0B;bi`|3y aK^-YZ4zAk2n~U!L+&~ۅ0L*'C)IKc$f&l@^kk=gY=ԮDKBBD" օoi㈫N h-ABpTvі/wj VLzaETUڿ+?@tmfNۮ۳:H .mbqF# o*rMb3pŹ<)7BNV}9=2il>]^y|j9`ѓRe-1%M8&yV.1bҏb`shgk+dPek$0bΌ#3kYo,VpIW!3um k G=iK"cxa%dfd63""T^Śn |O`͌~QW}y9N}۶m40m-gnC8a0 Q!48(t g=n LsslnɕͿpe91mسɀڡK'oz^$)"%4O ,`II3c=n4|)9CEnA[ Ue g:Z2$Z[$ bZxK;$ct}C0Zf\ ӛ/(\!-6y6ۑcIFmM<,K\1ߺeY>ak=}5;QI=Jt!oS0 \&0͛*-d0#/l\|f\B<,/ &qY"qdww < ͙` el8fBGK3ʵră0㲰Z4`YlOc)MƶGvO}٠إCL <98O@R [6DG[#\3olkkK՞C!s/~D{M ʛYai`=߅vxs i`,{V.G?!MyOiҼO \X~h3ww>әse6f.Vw?4);})2X鎈leG)(#CY؎CdS)$s!c~'% Ц޽$ȂbK4,fnAiiGwVB&'؛V0݉^BlvSB$7u. LpaVmi߬`$spfl0G:zDVkF;5}bvK6I; EM]4 HIpq8v%ͪu645N !P$}4:VOe5fLiMiyugu'Ц͆Cg!7s׬\ D,b`vD=TD!v )͖HpnN]%"6=UPHlLb(rȆ/D]'360n {/L]E+:z9 ~/6,9Gceٜ\}z@V/:0u&N>@1[l=G <>8kզЏBە7K6{ pWC@@K ʧYˏg`'A!B-7S9N|\L 8oIR"j_NWlOEbGEզ9Fft!3TpY/lh1*|qEE)~2LlfBZvґJx~O@ 毈L#!I. O?V7Qqx`@&d?C$~@á3NB7bβG]|dC,B`9懝m:fY#6GLn[LaAT>,E$"tmհA;SZAF/DKf^qlX({lv[bmƫ{7`Wx^wͬwO4I x\+ :}^4֤#$h!IN{Wڇ/^`CZn+;QS-K24Q ?D̀v/TD垑j|C"vPAj{6~ $!6L4 g>ۼRׇi``$mѼ`N*I`Jn%+f+mlnp#'Bz޾̑N[)1OcLԡv0ZrUX4 y7UQo:ۣ̰EȴJ^ Cy RR`] x$6 >Πh?uS6M rWسu^&,ѕ0Bq%S쫿p}z\uɸz-| [Z_=xA{ʖӵLIAWelSkY_/K/N^ԷtvQY}W:GDH+&RMmI|;3N-nqĐwLQ̬|IO7^ed;jkxz0w9J4oIɇڴqG/܋WT9H5#(!t4:Z$xJ0l+f|=y~׈iHk&DbpnOC~ <dNrzc@˻9՗5|fq{l[qɫc8t|k?~7;Oq0B]";$7fΨџ uފg1jGNO86 2N ]2. zDA^Y|ij߾dr)M ~bĔ2/LIׂ04U %V^k7?eloܠȮM6c]ɠg=K].vH64Fe`qjڒ FJ0ON:n@u^ Łm/^IDAT{u4wuz%7udc43u'mOWҧG :L@_6ЁݏΛ1ծ'X}-5fBvo6ݷ'YȀ3^{z?k^f O\y@ĴD36:$ bg4 ,c'{d&@8#+ ӋL6Si[~"|CO>ZYuͼ{)yR7K`o( žƚd7Nű  vA $<1̸g;^> ʼnd@τ6'7q[8dfk#bvqt @ yY{ DB HoRO$>y'΀q$IznO.ؗ) }UPvd˽{|0CgYqN$.k}P0S-W,=F,"& wqYW?'i̾Fl7P(z+fX߯E*1df3^[ ЁB4 =Af(V!,x{_Fy ByU%l*ˠg(V8@ȸ>n潿=Cy F:gT3전V8Hj* 4QͼZ3wVQBP,?ϫfE Bq CwUͼZ'jP({5nf|) BP( BP( BP( BP( @7UjV-IENDB`gammaray-2.11.3/ui/resources/gammaray/ui/light/pixmaps/gammaray-trademark@3x.png000066400000000000000000000706341413310443000276600ustar00rootroot00000000000000PNG  IHDRzsBIT|d pHYs,K,K=tEXtSoftwarewww.inkscape.org< IDATxy`TǿY3#( (V\V ZkjkںPZZ[7$@=?̛L2IL-͛yo{P( BP( BP( BP( BP( BP( BP( BP( BP( BP( BP( BP( BP( BP( BP( BP( BP( BP( BMBP(ϑv B(qP( 7 ү9^W|o BPisu`Y!NݳcQE@P(!N\vֻLAd:G|@sP( ]z}kqc7k-.@E"R 7P( Ő!.>uZYc9P( bsv{ֵ>8p XYSBP *xu GhChsZm@->9΍aÆώH&HIk B4T/\0G<5fA%DeCćt?쵓 0H1‹k7? B1hpF&2IBHH }!6~6; W>U6;^f:y,%z=\Vq9P( ŠzBжQBP("2F-!4 p%tw"Ǎa4N͝t֧}]U @26@P(rճâ}HJ:hHCZkk'Kf=;A}Nq[Gx+vn B~PI#5uH۩Ωڶs<~m.1Bю/NQ B\Lp' "LLlE좯emuV H/a H18P( ŀx J[Ootaū;b``xys)yJ( br3;3xj%$va]U3UÁ4ddɹÚr9WBP X4ƤBGi)mМ[w.nɶ;!ҡtYبilirڞ9@5 BPrd\ Qҿ8V:p6;pjٙQBP @"h,1(uުɒ-TQb[=iZ+ 6oo۳3s Bp\\p I. 8 lnw`V:Hh\ڐi'EW(q08wG >QzSv) EOXl7'J[C?gX bkѢX1tgK(cޅP9wa NNHr@K1L@ʄq 1x '-?!Hw;P(6Ӵ_h7כג1%94cݧt߹ҡC$/$8+J'N`| (y4?ȀHlЧXQ.HH{ ]/_BuEP( ,k߫ó;F{zYZ(l{ÁBPx"y1[۱A=?$ 1`2%). y~[>) E_\xb{}R(R7yx(:`nX\\SUݚF n-$$%5;r %Oy6LSŦGbKS Ry ~QUQ.) EN!Z9 ulWs`uߝӊРG ߶^G~@kDCGM@BdXj@"!_x^+pw 5A8޹E;%;aVp  m˻ȑ9~;rL',ϥ:vM}?ϧ B7u ۞mZsP`XA`vĸ!eA祆,)aP()*FM۫P⠀Xg͑vD2CWK YوZ( JP9dL% $Dh_ M$X:#闯쐮( ZpYdbi8V=)} b(녤 $ġ SMl %0R Y>~X&R( 0579<@".fDo% q/ #A߇8&Sh8y D`x'v_H-yLL'i/lB;u56Pp[nd9Ϝ^co| ̱-ss(!Ǧ~f\NR5/gŦ k⎀x.{*vRͼLW@P Vf,EUdK= awj˵{iՋ^쟈__gt""!ylt.!,=HwK}DE qZFqeƵB3Jo.EUd9y7 #Wt$XL]î `Js"9@&L @r׼tAߺ6pB3Ϣ,c|HE\"oa!P(viK1YZ=ҹG[6HtyiBώg2dޖQ*N"MQ:%G$o\ҥjSJSi _ Ba \^r@wdq}y RTd^fCmX]r˚,&]pS~o ٰ p͋淙y$DAvȾ7ZW( -o=$u{|]6GpN$$ϢP>MTj30L"ԐC<\dr_;_ e$\Uw93w`}`MyU B/z$6"_ЗxᢊH(\PGdoNLt+b;N"!s]?&t2.5XĿ޸ BX>I>߰ZF4DXW8Ei"Q>i3{X3/5tR,޳]-4LduL&go`AJ* @ ~u=ack9 .offP6Xax2/9 2ѥ8t{ʸ q6F@xԶpA;rϗI}$52L\/BٯP(v`EKycI"ڱ5 |E)Bץ8` W,{xLDϏ_nߺ\0jRf?)"!xMBPŚGv5Ex5; t+4ͬ` SdYj,fGH]zH$?Hq`)HHn*t? pJsXVRY.  zI98pܧQdug*4S]$ĞxI`n Ǚ O|ėڗ˲#Ho/ Ba/L}}$[КHDm[ŝ<1",} Bqgt"?o+Ax $^i+Vwf/q$Y)@r-Ǻf`{OLe_9?^KK^ ڱXӑ=@ >0)k'|@ı`P450O]j݆OrلA&VS' LGUJt~z/c#Sor? Bڍ^S8 >K IZ[y}mp=45A$>{զWWG˜NmIxl>2>?m plgxдWP(Y# _l+b('H5.C[ 8rjr|˸5sϾ޿; ?AfΏ+δ[t3FH|dpgo"_# Ba :c㘋l$YxGYTjv˾/ӈ'`~ AI$Ra3;NLز`p$P(iKq>g 5$F3+eKu~&-YTey&X/ PN۵cm70_jƙrr t`rU ŠvKμNܼdvC'AR4h{u& clIy+ /c6U$g){x/{rp"Bd SiZh NJ0XLPkZ  F[%A&:5s UmpeLkHtbZJ"f8+ -5}ak-r5 CwlDād*I8K lc plKGI mW-T@U8DBۮP(v`=.l$,Xđ n 8rfe2wOmBg}./g/ ?f>[0.ȧ\)K L|o;f; b50Tmi˟3$imP4D "b) L|I;>]$XхkZNfJlqo Җ3=nB@ Xa@,AR|/\Tfd#n#BԄݑ_>t*hOi/>M!\gBA!ɢ-yi#uUBڭP(vS?"aЀ)6Oť;Z$aGZX;:A⳷ؒ"̥aGD5.sdVK@+ aBPk` J  փVNL q@@2Xj\|I1FM1) E!/=dmG.$[$oav6AhXTE:)A#>\8oXɜ\j0y[Zʧ&(L:#ܰbɃ¥P(]rDu,.; )ҡ7PN{v1"A02 RRP(Ie-trێp>PI0J+= *q ȼ]ВS@alVivIL5 .*1Ev;mQ( ^N¢;cVKId;_ :qn3s]jN'OmG{cX.HbtO?[SP(r%,eKZIe-ٙ$Hr3o_ Nos$H]jpyqe|H$vMOaBP7k1qt׾<$H~nVY6PrYoHzliKrMr&52.5g @B5q/6?`kVWYS@AgqW8(4tJDھBP '_^$ EtU]W>Ai,*x݆|0(/636w)28 eKCɼ?k B1 `,%%깱 =M!ϸX 80ʐSydJryg[vJZu޶P( $ (D:%HjFmNL Zq X z ~0$ Md$;(̷MB`1_l+"@$5Ih+vkB3hŁH&K %]FgUV T BN^IASslsQN - D+Vma̾Sb8سC[̇tAb6鈑S b-+Fd}~IϫfO8X{Ϝ0tXs eMаJ:BPĹ/=dmGkT<{ !#/[:sdbЊ/N[=R&9TdVlW qߨXLPk]CYe-k$J՛Z0w|ks&̇w5`ԨK,o B_5nc <$ֲIB,`ۗ/8pdt[f!5 X1E$"ܵs>S BTz^+:]յIwۮ|2dVFPW/Ϻ#kh_DBKh5Iݱڇ ^}Voy IDAT½9IBj{KQ)3J @-5I_@>)_VB1xS/[v DhӼGw"~&^ǖ|155Lqa^㢀u!^Ջwr:#?$q4A$0IĬ`~WPA&N;NuqB1Ws`'0S ,XULe!'.PNTWٟ1D -f{v~uO_8댹rt~MOm!JIc잼jQUĎ~tޗ=}(]A^Q?-Ǧ@$ X C5F&q-2aR1@ 0c ͨvʝOnDt $K~abMEC#mEvpe%q[PbЋ<8GAcQU%8͚5@w -0H;aeZ\gV O(ޯ؇DB7^I8B4BQ8S En2${#l.T@4E8)LGK17doV3u4AkKW? bͣ;nRv) WXy<;H QDE<#xN~dLgxo: C'`PIfCʖPDGl˯ >l n 9xp34@%5H؟dMIڇf~L$x #:g="  O %RJ4qq`U$7<'WB1pغ}4G#arCeYYqtlE) nnn6OmcY,7bDYIZ ,:oi};7-{2WBy@f{ v60#⩒)E0ֶ?G&^ %D\:hN@?x^:+]UזF܎: s@4!]$7̥}Z|w1P(?]ޘum]qxyb+&ίoݼwtJO8n_~N~[ %#*_z\yL8D+ */.4\j oEٷ/(Iw'q \Y_1~UI7t 3@$RC1rKR[! ۜKW7uHB-}9KGbb&^m[s``2$Q q4JJ{1a ad"d&D$ 7[}IW<<Rf'S?'jCXW2pȧ' }c!Yv:f2v4=B1p3oިOතc-|Tt^:{B-,`7j}1$.od毐Zt)<3;h}1]+1ƎqEUі`̂$9 U+l{~ԣ̶REǏOϟgg bАш>]W}պ%o IPf;Z>h Ke7bb&8VvX9!4 CIHE&~g?f{V?Vv$@y꿜5kYyj_NED̋@|KlP(O\v"Z8p^;pNm 39hwDB@-!#xe5)HM<>%>;N.-eMY5vm4$mI'sf3)a [E>ʷ- pXy۾7Y˞A[P1 8ȕZ4{E)y~;29/$&&e8 ٙx C%Ff O~ȅ\)n@Y vrz=]IP/6uLboު[v4Q ԠD @x)CF|M`44HYf-1>*21pח]ݶ W'/EX"/g\`o䁱v) eM g1| 8dݹQ T~p[XrקippK}.F+ק/orB! q-fbxR#g;jL55Vn >RVz#\Yu7eӾR1 $'Wp> 2cw@3(&JL-lQsGk9KX!̘*,P ,'AC?n{u{c%Ӆ1R@3xFbeE+_}@!߿݅# b*ioRGnㅫ&j; ZSr:&·x(F2h RccŢl^v̇ H)ڍ@eDg&' 6ZI _|:|q= |8sa/ZF_kӍMJf|lN c ;IPA-/m4,58sZ_+uњQ’Wbf2 ,U0 Ď:&b !84u.҈[uU[_kBHo @RiB ~*w fżG>ep@,J$b·o?$lp>#ҋteAW 7~vgϴ:JL!a%[DE ,bY(4{}zgDkOk%09 3Cg>調n 9{[`cTu{78 y™þ,tZVAx?~?d`kYs l =Yʳ>Җ2Z5]v{fZǡ [}W-~׼w"z-Wȼk"yt4K49oig&V--kڥ1<`ֵ ?ak9MN’%_m @bXYb.d8Ѿבe'-LOĎ ,sAJLa{g ARy"Xѷ<_OER/t9Q>2]>%LMȲgE_W>pkK/y}Ni+;m~^;1-9mk٭O?>$ᬤ<1; $BzQ:=@xGvܳڲXѫ_#'@;f ?o豗nlN:N"Xr7&(;Iώa$%?q>^O\yBN֪SCpʷ4zQ2ㅯLy-ak¦.*GrŲ,'KL5T-$Sf ymrLۡkK" Nbvl6 "ufo'f5Ȕ*xN"Wj}[ZgHro4^2b̴x-=OvzBX|/Jm5xY:&Jt5Vw"Nz|/LN~T$~Q7N޷;, K_fJz5pp9գImaA]Q+}}[X.#-006Ecm[\=*qҗsUQį2{W. `!o,d )qS~_ߣ;F>H>_1O톔v2%P.EfNnLX}1te𱌎33{ddAX驂$e0JK "H_}2i'(2%m`"!徥 ({{t/2VN'}I}Lymp=k..VuYIg1e `b;O=5U''֗L"!Y߫k}d=Mqp9B}upǑ{r~M nlѽzmm=ʙgвbnҠ^ҠѲظx*[3nmwUCYz\`5B%#'F$e0JK'?ϗmtXAy'_)m >HboG:Cwf麶nDJMhxxwg,+/mbWD܂v)_[9 (4;t @k=Cn[ 2/h+V0}l]-O] Lݡ2kZ\O4[j?ʧ&1p8ki$ߩ .y>?}Q!#%vg@b]%Cb@'D߮ݱY.K~R%GbAc>S ySȾ"[ #)s۝HO5&6q$vM-_3 i$x>%Y<rZc+4Cu{Ι҅>5B,hrx:2W.䶅n:󓟉MTejwpQE.1C?_fxC~lۤiu.v& fgA5]߭ChGMq{.0np0q wwyg5I #8yo(ev/k'6_ w54IJwh+t&@ޑﶺcy LgtK"e ]"Yjpw=g|ݟЮؑ'Q2Oq]9 GA{8πe@r64ߗ@-oBsb3~ߍߙ`~]|v! pdK ALXk5|//Q_SN" Rx2̐@{;vZZZ%?qO'3l%sp=k]nHx08sk2l{ @1{XeCsKNHqWm6m5vޣU*kG?{O7 P9AQ!'A _0zTW7Feihz6;L-"?fo'=&'i{jJ$L[z'v/ؔ9!ٖ)H/}ފ=ڗC^}aM)k")|sSyS^gkXգ{Ǔl7/5o\S_4ħݻi )>O\D_Rִ5q{7v ΎG$ĩ\3%g2c] &t/pd++,>GE`gENq#/ 3qX@ٸ}Mag=nA]Qq\a<[^Zjb[Y]N!NdD8@Cvz|Z˙Eo]( ԭt!vK`tį0 22oN]ϥ% $ϗ:(M0Kќē}R84 ,6;J^Ϣ\=u36Y.I$0CKIoXҤIe_Nɘ=ؓl ?nNS?Ýס5wXrG}0bxZo9},*+:+[?$=!I;CϫWlvY+lHT ⁶ŵ%/i@T@Y¢*j#bFm=qf>vN"~2 C/ L@.kFAHFi,&-%1[HUO DCd>PXk0O IDATϋChU5!=-3xr Oo"^0fA"')-q4̭7} DD@@=|DY-c/L\⢈R1!+jzD;?^Ɍ1qb 3^_D2$ЌҖqЕWprKz6g>u坂=!{>Ox~0Sïۺ b<xdÖ^"q0 vhdWrU&͵f_đ碹c {@!-z ;"!2AYf*J{F@{M|(ĂThy`~ϗ>/'nz`tLH 3i_޾KN|Bc. kbI~-\~Df`ǿIxm}фADquI/$si܎ǚ=O>QS\~7e{ݓpy+Y|̓j R܂";Kv RgQS}~F6~)9 Ůylʙ7od^34agZ㳟Olg Iq@@.-jȻ1&xnIKDe"6l8b%ˀ9u6g~Ls{ϨCvˣ D']0Wd%v˾MlL o;"@Уfދ5xScՖmR ~B,'?LgaKW=emMIܫ- .EIa9VJ]Xnm׾2mV"g)obNĢw#W/sJ5ii K>}`'Q˹ŞN#{)pj߭:61~o5`Ht},nJr3~]e` LTXMm l8hT``pd`DN圜W >ӆg 饈 V[Rj$H)e"?B%!-jB%DBmG7;]'<8Okܺ仒,M¦헍__re-ɚG20p&R´;]iv;/}SpxrZ( ;*+ v{*GNdr³DNavC">O2$kg+ΑD9'DZ n/=-x/&9\JNd$0t۪&ҙzfi [dHѳ.5|l ڃr >cxHZiU&{9My%ERhZ;p[h;WWo0|uP;:,okBXЕA_1à~-!+8y0m}ls6m[rHqrt`NEJx-b@b@8;e*A\][c2'I ڝb]"pg|Jt~Z\fNlRm aY2=A,4Ct~o:61/o[*8"$&$MGw#a2u{X}"z/-,IZ6;\ 斲@-9I,[Gd??gE{9sDzNkHr8l}'+ R~l7]KqPr2V4cQP-.\] a8ؿc)^t}'45V<,XX-89PB @/:N."c)&01ւl# ٍ>lc#FcBfQB1wuAz1K{/ )Dt穀/%m/!7ֻk,F` >:)sv޳r`7+xe9>w*i%Qr/XcIb(#SS*H-I(2Q xu]O!rzCe6v@Ѯ/U-nTط'l!J̧q)pjf 8_|"o8:80A%6ެxXpy,EQ*0S(+D, 1&=%b%epm? 7]A*g|Rx-z!g0D@gr7x!7k}SW~u6Ov|ym<`+fx[\\&q;*W|}*!SDeک ,bbAp i+Gؾ`U];0NtjJ)'X\dZA`܉ۖqjFۭpeƞ.o  @F=C#$ u3ȯIwD10$$ʯU8&l{0 _#`"i)q.Uߋt~KLyc^rα@  ?dPa: 3(g"\|Β$ji <Q;N\"ڵ8k=7/0Y$ld,=!"/ğ+#nj[7uT}dPof$;TIbܥO&V>n8#c|,bgƈ*G X s'uuOu]px n?~J8z_WT&!m?G:6$Ǫ`6 t6Dr}uǾUVq^Եߴq.G|Tީ_vYxkD?5V̰HN+VHwxT K劥X'ҝ ʎĎ3xOm{<|PkI0婜 䡙F,FfP^ fN먻<iC솋fL~`L_ɬZcGHs?X &V)wBiZ&g+tHu+n WI, DٛL`q~ ~/bgo>hv l'--73F-)1 7.+@-߲sƄ o7`d[,T9ݏPwRQ xpc<[ .09``/~ i$]"(uI 0l0z.z6]c)H02GQF  !@v!2D~+?} ޼篫;7CMnv'Wq~C#$Fp"l7 !J S#|FA8 `tD h7\_ y{?rzIce O32ɓa9w MPzUOx\  y/9v@v@\bDZYG>HX @iވ7no>EG|~,d0Qt |o,#ȝg(`-@a;n'wYs٦=۪;5a7 4sEMMyStuq@I}v8̳<Գ ~"2.!NĭAbx kX6ɸ T\h"EB"wV/v5s+޹G +;'\kFDf/|4t@9xa5w?  υg8P2{b0 'ϻGnƎ@dLJܓiz왥 PJSD=BLͷEGzg z<{sD(ET\LRo$Z~-l$0ԝ=͔4b\iO$Oqp$t"Ǭܖpt`+mkLPv?EfեE,#׀YvlxM -*M(0q65y1`9HTZnBm@O2RepBG=Cb!-o=<@ ^z=b$6t 0{o<'dIn|)gd1r߲@PNus993*TJHL%/NS8&`c?wu'~] W2 C0f$O6AϿ5oLF?i6 'pdWMM -Z@ 3 FǎOۚW,e&DAs /yyRX$BE)>'08hy\nLTi кkuyG_ۚh}O,Hqh#=#=[7Zt)i&Aj03G? QGA$0x;)[(08;$&'kc;>RDӨVW/kGۥ{|Ggn}ZI$UQj2V V< F'eX#%/ 5) `5Ej's>1WtpУL˶ zRg{W"zE y؇̔c#K~Q ZXM&lB7aHs SPEY{6ZsLT)i]ъH0.TZIaM#- 8L> D-_ V{>*5fߘyQ4.!7Cy+뻿?q~A""ojqTx:Mn|Ӯ.)@~ɵ+M\FuzHH֞G;{1~IE|a+TC&72k]Hf5޴ڊǙI*Dг{TZsyT TύFq3rP0L?6M'+t&()^/ .׳4/ϼ/s,G4glaDBaCtƘKɣqwt*>wAHH1b+GD&jWncq|a.{ t;uqG_„&k=`wA{ܙ1G% °+pṴ -5)|L<00,{ʂf#[x迯=I̴݌FӚ8j@hBb7}`Iְ 84->S9z=x`!^GFZs<ŞsF6pm ncs (юE  :KXIĠkq;X$IxMRn X%#bO};$1-vhu[Ipi``-bmU=q|{nRb2mό {-g'/D}cMvS! @veb@Lo ?!GWoof[qaI-3^oXb5,i%r曏y`j*{βcYh?)֍~o"n6!HLoh=j.Iov a. c&93H ?\x>Bٓ&,ڔ[{+ *^6^c"bZ7]4h[N=g9*f(B;IVՠ*aLwb_ؒ+ڨ _Ȃ?l˶+8.~-=beY,7|=rgoV#v!@R~a?UM{ue1eAM"{޸ӏ.\a!`w/dDY!x =rgo􂍌$@%.f!6h$)}H猭eM#(˕.ı. 'ﰪi=!g9Pp̰2,R(.~PC^3h ;|| tk}^=q%BVV G r漡Pm0c\(M5[zK(Zr[7׋~/&Zm6+j%Oq!D1EtEy KgEnB :|r C P?!x/]뿉Q xbCQPik`*|8\\GY".3Ee%*'5m93b;'FE.lfѥ)B"sY6 }.R }0}#R` 򏥷yJ?0޶X@eT7`3b@e%>m۾-ע|I<([; Lb <~fGݻ'Yl>r˧XwE~QMeTưi8eU݈F@rn`q5@0߷<1S;K3Y'Wof?#;;a9Ԯ󵁗O!1;X-Z<Z^o"xZXC"Af9}sW-D VJ5O^9n%HAt@h&Q(G6dwmfY10C~6@p (Td,ZL$ @Bs 2tXHk %T)xh,ځOPGceR0s/`EAkZJ g1,+HDH.dC1,}{O ~)@"^VQ^ ^3r{&yۼRQru4EzxOa P69%wW4\^Rת׉KOpkp9)i4GV`|q!-L6 P?1ܑ1}wLx:mNطo\ l+Tƕ t2fUkIDAT;p\ ,"|ۇKH`_m甈C/: ]؃bZ51 r78NC_zq$1Ze` 56?b.0%D0X?2C< 7%A*(U2 .:hш0log<5mNX`uP S兺utSX ?/|* oFYE-SYΎwA3Q@210<"d5.0_;PHTA3|*>{w'X$vXMFFw' dEo+~`<Z / ;HW}9y{"\|eH+yh s \rOXX͵ 4.m9r."q/)6D,:>|jGCg?qh@Tт$Qi3+@O,&"vkBa^٭y)f3[f՗Wu_+@G=V?xx{Zv24f~^읹Av,=Uwу=XUݽשּׁ;Tʚ{0rͩ3dÓ^eN4\)L\T,@xp[N_ueDX(c1BKϽ*O[[_8jƧ(A4tbXҽN;fsE\ΠF"s  Ζ{VGͧJfg?2-j P'5`"v 1Yͥ^`0~$8a̮P<&ȤgfA7"bH*_u޻@  #Ę`BE;ܸX}|+G 07 YI`vZrԷHQuBiQqÇ<$+rmsKV|rEubgq]>`OPlA뱁kHH$|Ϸ 1WVՖcVݲޛvT݆e4L1Jo^ #[6< en"NrAЦ9δWѶ3)8ߥ! ]GU ^raT0@D A(S‡>5)r1<G:ط8@<@6/1k/88 Ss۳ ŀzpDŽK_?\n"&{e5a@VzПݰn.X;_̩rndצ#AoU|WSÇ-p4@9n(V5@B)݋r/@$qyyExR!+Ack `ZڰGӫ7dƮWE(!H&&zټ3#'0rs~&VjP`8{&3.~ ]`v(L)l&xLQϒ20oŸQ"HFΑ*3oو]DOW1Ǎ@͍NnwIIMZ6 }z,#!+ t~{\7,0w|*B%Ip6lޱ~59ߦ [0Jl⳶C"9p\p'T;0c%9z,#!-U A" M5E0$tԬ^5@~?AIkN~htP[Ȥ'bFSḳL[a&a? lnDl{ڟgE%( .x,>Qd-nOukۓ|*֝h&tXed8wM ߙbT`8D"d3Gl5ڈ AyB͕* #md*vY>Fm2Z?unRsm7ԖD" դuVfQP_t'd!-@xG، @"U n),, ^% HxtI{D"%5xS}V\d8Mw<&y>8aî L"H "rdrb&S$g/Y!aã*-Wxm[maI$ɨ$KwBCc=Tr8mi7XEq/ #>1J$hQM$7cU͕~i5n2bH$I0f+*z @gsht.`w$ƭ H$! I 4:Kw8@D"dB@9 ҭ H$lil_gih\BcoH$8¹ڨ'Hj˜ )$d\A;]żIENDB`gammaray-2.11.3/ui/resources/gammaray/ui/light/pixmaps/kdab-gammaray-logo.png000066400000000000000000000160671413310443000271720ustar00rootroot00000000000000PNG  IHDR<qsBIT|d pHYs+tEXtSoftwarewww.inkscape.org<IDATxyxUս7Z{8'9'  C  j/Y{ VJުRU!8! a3 HXy^䗵^ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0Cc*+???jk5edGm XPg>2B*![ +`^=$tl-RFL5 ,wTlfHJ VɢbV 0ݿ'K99Գ[*lk֓xR_  K&5()![N`'et08gm]X,d:U;Sy?]{!s]]7(H&Ɂ`Fبx\5לs/tEKXA²|;$R䈚h,n]}Q4mX5nS$ɔ ;|IJrݚK{g(U]UӚ 먴{O3 VxʯbDHD%i- QC6euS5k夆*%]~HXGcieNUԮBtYϘ8XGEu8Sګ aXz: <"),#[̺xƒߞ۹kR5+"18nNwb yΐ=xpOYdXVYjb_(,qRV" $1>5}er]ZuR[ZT}͡!p@bu\?Jkqݴ)S **~r8ضb.W-ȍeT(V34εaXJDY˻=ju=JmZIJK ?:d>lj*DzG;r#;wт8L(ĈC1,\o hb͞ǒIX)l5[n\ ć O>?-RP47u̔DJs UUve +RּrLVCaM-z\gI)hiV*< wi)" 6IOsƫN D`F^`ʂG0$"P\IvRIԠNd8*cjO̯-xJ5 hWа9b]X3n1.maBXŅ/u=X>IR1$a PC"pl˃2d+j#2vV,IguhHXj~un/ bm^}[ ?>ߝd3x}ߧ}rec+$v0sᱱW3vϼ뼹K_/ZQo@ס429=4GRxXs>Vtf HqaѭEaG u<ҕ27r)w&IԬRS IcC{sEW*~i([E墺c&w0,q#Wr+WAUV\Ce^0?ϰQ87=sAFY6.Jk6V,rӿ+|IgЎ協|IXTQfwc972L_u0Gv+)5ԄJ93_XRjzo]+MXq1XRj-q ^rِrY7¬g[B޵lO޼|lk@Eŭn+rLSi>Y2]4ym玝4SG7'?CB?q}bL4 v%2 IgNx2[9Yךr',@8/'}_DG6$4L3J'%([Q+.ɽ壏 KWxBa?[kw_v,]c*XU TZMbmBZ;]8uQP`kJ4LTTz>|g8zEy.}^"v2խxPw7N@$[KlOgr(P[Vo| (Os#"4K+/wŦW#5{sEӼ0ٷt= ,h`1z^!$_Z&.l.8"3]^;'B=$Eցn@HIڿ:YbyH54i! fU :u\Pz+/zE]$-!rY@2?x֪Kb/._Ρ ǁS!oH#LOՙԵgP\-I:(@)3 m'zF&AN=1‡>~O:lHmHqi"xEI~l fR>zqzJڂ2  /Mbfqٽmr:!y*a@SRZ|•:.r(D镫 `ٽ ZeW"¶ZEV])AmB8)bbl>Jϕ҂ZRg,ZXRiѾj+'/Nc*,DoYyt<(g >.Z#J/פ2u& V+(C._LnCYS7XNYSJ%Q;A(Bʑ`5s0D-$7+ f~T3e|.Faayi3.z?8-Ml3hHxZWٵCVNTzZ m[]{, Kz yrL`RXH>|`kPP@]m|<"rИ|'"n|Pko=? =mN}4E(KαHl"/ 7ӏR6AVٜB%ޯU,7-G!x꼻fo~}kKY[gN^]$5}dXh*n咝= )V6\R~ǩ > mnX s5I tK"_QXc h"K-%޴0DZn~BEǕy;i -a岥V_vrmfbጅJJo`y=cʒ-vK(|7444d|OעO8[`w -Bfy)0IDd+K$+W,^i卿۴r=A_uS/stk1΄=5^͕-} VQ^*D^~xMR҃ tjnv1-V.^W}06YoU< /=JTZ_`XN=nun?-͋76;E*z3's% Aw$58ՎuzU7 RTӴ3?|KO[xi/v8Ýzٲ8RєX<*5Ptu'Ox69'h{h޾ z}@Z4 $ og{v5~Utt5,7@3;D -Y`}45H*u햒)"Y@dUWLo}J4y+5W-IIAH÷0k K0-qҞ!F-2;& tb5(]ӢٴꦊMnxcM]pb]ܴ+ML` uPԾznCKc換uZ%c)(o{['/[Y )nn++]_SʧyRH1C@±DmwO]QʽCH*iC&f1XmtGC8 -iBQiOhl,XlYmLo\ csiP;O{#ёzaNch%׶Vg=1fJ\Z@̓I>!cUYvr 6h[{s|$"u7'P!Oܷ-1fnT=JL7h*W޸c#%ͻy,[/XF J;^]vmj¥gxoycZR_};Wt17W}烉/n{OH[NIt":8Ǐ[-ݞJ+**tWoݍ y!:-Sn E4Ʋ:} ~ƟjEƾ4N(+ r 0uD(8Aғ]Qp0L\>gKh*y\rGuPV8dvanص[jh *" k,\1`kHݿJ_GS`W&\ZV8x0Dj1  DžIENDB`gammaray-2.11.3/ui/resources/gammaray/ui/light/pixmaps/kdab-gammaray-logo@2x.png000066400000000000000000000401101413310443000275260ustar00rootroot00000000000000PNG  IHDR,,y}usBIT|d pHYsetEXtSoftwarewww.inkscape.org< IDATxyՕ?ϹSKw͎("*-Fcbf'ʢ qhRM|LQcFN܈K D4"n5{um=Gu5T7MK{sRJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)RJ)F(TW_; jRJ -"tɃvmU$!O&,krpJ3^^Rjp撩rmUy&s w>Ա-,b^:/`#1nRC-tYnyIqиmUZV4V\9E_{@{XJ !7388d= 78X8 ~7M4,X@v^i8!_(x!'`xiBgI8+t0kL"<9e.gpR1j[p؝5:JmN:HZK0ejRjxq 'O#dNXyiY\E@I?@~ehRj-]si"G^:`LawZW@8^[a%^HR->y4u])26 98~ gFUȔ31L㜦O3ٖ :aO*D}7tޖ lHT>sCAMwRh8!.,,|n*V͡d[W@FN*2ij7t ,Hi8>HЫt2OkSaW}Ѓca(*5 T瘺'v{*MRd+ "bϾ&Wn!Rh* %$za)5 /\ntLwE_VhWjb1zkbR<[KEKWO\;"I腨._@9,ҕ0ڗgP_0A2^C! - FaٹZR)}Qܿ&nL%%숟 PΧK~p]y.`#XWJ,ҕ|?uqneQ8,f Mݳ(ј74`)ǜiGܶ8;N[/ݔwdLJnzCЀT ŵd-@_W?RJoϹDR}hה/5nrFZRiw7 XJҲ2zY=yHK*7&"/,ކK>2.qQs{^䠢N*ueRjG}sQ*-7&RNr\';o;c`ubvͨclkݦ;TX w:8tC8kU3]׽ 06d 0M}m䃡 VPH>HiX'ޫ bP7C C̜ڥE`fLSDɶ[Y䕖%699Ѐ2u@N`RUBmz3?"'习|0,R0H*MLk(6Ҁ5 q0WU~ `p HaTWJ!LnN"ޘx+ Jfͳ-[*mkʡ9do@NS}1+}%]z$]JR)7݆1n,&zVĨDawrY;1g^lbd \ٻg0 `ȓW_?87289u9QsAcsҲ{꫓!)ۄpNqm(bDd0D8sO5Hýbyo-&>{iY~O{m,l' I+ƂG醿&c N* %zC2k3M]P&t? -dz<,H+}o}j '=-<RiB>P74` !HN$ְny̰dNu-Y۳RRAy<=,#Qo 慖ܗ=PINcѐ(,kq9OVQ =ԓ,"-_`PCEa7٩;6 XCH6@]xe;DYË=Nm~gj`?7e $ sD*sAѬ[Rin(zpy5x7e$6;pK'xH;'g I4 ǟM[F(|>=cOꧯ U~~UM/+1w&[w}Vhn`y_ݟm6@_ PVIɄ؟Jb}x3RkId Dotm0hG q_ HÇ,_Da[jPt,& v;vaO,}R)[Nu:,sYm]U7?+R?x.s+zT_sɰMޝ;\Dvx^kSn\y._]lw&79d"JTM"Mo//~wxq4<@[lC`j="\6&:$5')a@Fh3Oa0 ֯gV]RN,B3oz⼺6̓@A4NqߟUymnӊ|/?_Ξ\`J^ZGFלd͘š ~ :n9Ȓek.A:Dzyf>#/--sN|v1gVlY[IڶY۞>>m u ^߅B᰸/,H]N[Jlg_ljW59& KW6Zz\C#B0ܷu.Q*|n6ȐhaX;U'3QRnuT%e~H/x_.{F|貧r9h_C@_?쬅k*B >qS^Ɯ+Sm1$:z^0 ('&3A1l"_۝C'D @d;rKwϺ1倁CT+3@1d C:gk{+S B#Zxv^pfȑ!C|6l[ÊOw6)raB`S 74 pQ~;@n2'J"yd!t0;ݔnd4^L2Rtw]| £$2LY(\9$I=kL oLLK&l2!'N8wArpLr8 1M´7)G =>;}uM|ˡnICsr~CQ X#!0`da[ s61 6=|I+ G$Đ1W0L^OV$)&:xTw'4d!bK$ .o<~wvD⟃C$"@4& SLJuM "???&90 @@$0iyٹ[cgĒˈ SF"&;0ܳ6Z?])Hܠf³_/$\oYl]+ܴi5Qq߾5sj2H{S)zh |>/1ʺ|5bD=b[kGEq"8ܙZ@ @$J54A;^hfcv){-ke{u ׋0V>B=C!3!["sŪ `b<󍈈8;Z&  s0 O h"88 mb޽*Ǎ[ C8`ܘ%.d1 YiAi\u L38%[ĞCs{|&`ɡX:bYcrҏ^/T{C, [,Y1=N vh/(i\hmBϟNpZkk^zkǏ6~5|pu?")ğS#k 65 o/@.VّvHLH ,{: s Jak*z6ֳo}, @Ek0bbх-upn00#Ja],\#ꭟM⟁$|fߐEb!aԜ:9ϙq)*Y["'VsUd$ti:W1rS$VvNş?Ip߹bLQx}z6?:|D h:S]=--'u}ܵ{ |=f2.~2)_@>'Ct\-_pL{ X#Ԯ+VH L8LTc KF.$ QTW˰Ͼy7 I趯Za63|-60~#{~ 6vFםW/n@%6sO(d$e136)I1˻g{EfwTCS+c Un0?Ǯh!gG QsZ~mTZ:%qmGB2݀AHc"ƕ65iJIS̥d Ae!q$x1X> S_)$ kEmB3سeI{+w?%o[k)s6,qB#EF:`H#10 8?NW&|&8C{~efhYMv} !3;fXgXdpx?mv8[: XW=V{=\54`dO'3^cKu`tk Y-0DL"3bᰘ}L;57D/nr@ԝ$kw(ΖcKWnEBHnmJ}zRea`<*7xF:YkAX'tqBuMML0L+&XǓL6p+d I~k'7vUMLr?R{|OxdbGSl~ $cs'D$1EbWݷ׎q r\Ѐ5b so{i3MD=^K8N"'uu쇫tnlߌcLu/I.EUT[!';#@&{6u9FG fiuh֤H'!s'=u-Itz8IDAT`Rx0`ݑ`;eMO%=TH}G Pã⌟'߽? 劆9ŏU=/n3Q~է`С(Fk@G_H |/-:NؘU_qWmkM}O}Xvį2O*Uўz [.&B2c<{f KXq!uYm͏/2C"ѡ nIz׹l|m6HX}nqM G͑ ,WUw/@7Z0 _s2ۡKq9lv*2! ZgWHP xUO_6۽5]w$+]_,~g0 Ѐ5"M@ I\-|.Cp)NYz.GtSR6BH roisl̐= Ps/mSÜ-Y?YߤB&vNQ05 VmmcY)dH(aaQn.ڣ_ziIYN7'*}d˿:亡s|)릞O@ !s6C fqփD "Etϕ2>L37#]YD^rW: VĜ?Гw>Ԥy2q0x)n 2N%8,>#2xfdXwFD,^ԝ`ҪQkGӏ>0l+@=P3һ y!i㢿U߮oG,>~M]rL8HցE@^9^ Y㙽;uie#af XN]>r>sZ ;|ݸDX҉PtXx6ZtCd$K|ԡdղ9ڮ[+Ɯ湝6D\cL/*: Nl4f:+F$ 4^m`irїas>e08CP!bM_R㗬 &CEϟ^͔Oz#FBS!3}6upf4ScǎxnTd#5)bO^PPW7o ^ץ1BGDο׭\] tcbK]RME_;՞MFKW&``v,$^@}-㮼Ig Dח}‚{9};s&8a9>3l1Ac,|*$Ϧ iK&3䐐;r`. 1q 1%ܸz/>K+Pt)" nu7S#*>!0HMbZk0rR#X@ܦGƱ#±{|9d'lR_bWp :2L-SMsrB;{6yLSX0u\lxhK${a({3n2"R6^? 5XngAG,|H’RbSs+w{6 }j~sLMZ{VgXƺgTk. ߛiAO?(ϩy-i(rXwaI.@jT?|Bw?<|KyM)[=iʘmztǀXnw$wyE*߂t-znf;Cs~m쳌2ĭ&^m YT$=΋ * cXjc n=^ ϐ'r$SDX쯃.=aȼ ̓Y$U>(c$ =uuɼpq`:vBpܡpL7 B5=#Bw0'!'{N+p)'65v&<|IpO7rjK&\a'yn,ⳮiE%`sd.WB&,n: _?g[h23F C>~lb>Tc;0Z<VK@QQQS|F^o^-qLciY}0+=h\G"wcW%ޞB#njSGo|Vc`<}=Z4ތq;)xUl3 %SDÕi],694`/?Ȍb |ɖ0m(-CҎWn4_ a Y2j\GsMkXYGcLggsWO?r^ ս&3 Se,ѯ*0XuUoE;SoO$︈z4՞FIrD9u]!9=߲t EBUy:rl2X@q9o-W}%H ݺԑxr;ţNZ#|[#QWQ蝹=`?0tϮ%= hK|/ϩ= mhI٭bQCҕ|]ҶD|}4kgXOI~P?i=|}G˦I[=-ynxmr̻>?UёJp]ؙ54Nn`T?Imwur%0Yѓ#q7V?!kD \_I'{*mwmt~2'&}t9Q'UMtҐϹ/>^WQ SG2q.7|\q]xAIbᚊP (ŋْM[hC_s1a8W-K}CjEw*ӧNx rrkN8&ӀFpXLg[$T vxw dی^1!mV4`ekޞA]Ilx\5zP٨f-b豍 T? ԅ*5`38ɀR3NuYo<ҴF]SvQT4yS}/8K(ҀF 2yA%8gsx8 %FMkP#B_'4:oztEQ=,5M>KNc'8ˉ P Xj}a_!oڴiΣƴx(4`jHCk7u):]r.74`a? 6>gժ GXC(`hRU %kIHy5ˇpQK []9V\>61qX-曭T{QVM{|Zc(_OhR@kco+̝^t9v,5, dW'"XU0uj!Y!|['lR}}))\ M$iUhK GêwRhlQS2fh< Vݧ=,5bޟV3L"RKOrYqv{Es%ZMϰ3V =$"DjP@Hbn)9$qq[X3CV-`S԰ṅXH-"U*I-c,Q[Su<*眰zAjXr`җׯWQa!PSSǁH؛<4nm) jx} ?Q(B:ˆC1Ƣ82JKK@aһLDx wmK!g6DsM}3 쥥_/--eyi)7YsZӐ2ܶ9̅w,!gB0B=>YJ|qvpH9m?<4)""8ݺg:N{Xj~Dj o5ok*5 h:AU Tٞ=5EiW/_"oi{'L;'FD8>t\wsICncPLNQd'4 ds3LMa! xm*=Cwxip$㧚 N-kmwh/--sgBsRϔw%K~> Fi<<>[RScch6A.1"ǎ}gvݝwlխv86)s .zw-B<{W5 1E|z; ^n}Ԑp0:U^S\STkn{ZgxC2睬7`}^}ӽi;-߿eͭ_zqKWvy+wJ ($ a EpVC=q6iRCZ8,f &%b(J%z6 s*Jr`ElH;}Ix!P|mHDՀ/xbs!X d CYfѶ`#oemkRCZ8L|ֻZ:dv`ɏԝsZx\Dͫߊ@@b.^qhŸ9Sǟy/rfS(5t&tӀ0WmCy;ٻo$`6I~׷U_/))8s `DGy{_6 oNxG6m/2{O<6D}@ iR|&ڣ!  ?xs{ky楛?U19~z)owisaMT7/Rp9 Z~8Xu%* ;z@Y36qڥeΌw+llc.6V-`3wcl|vqP^\7ʍ xW{n__:ƄH~坒/9"Qm/޼{IL"GYJu yHb&AdwݟiO IYW5I`Ov X9$\Nsqķ];">ozޣ\J (")n'7DqP5?9ӟuyzoM\!h__ A^M&g:[>?u^ycg [L4]r~^$?F8m"nnt9Tuo Pd{gwu~qSXQ&9cAC XjXI%^aQ^Qa`3F8hCqi[dʞ׶)[gbaɞzšҺl̡ǁ!CdSy]w`_ΙUܓkLe0ц#Ȇ./سy%Ժk'e8qn迮l^ Xj)_@x' $9ٵ_#Sa4"1L64Uɇj80~՞}%-߱eg.~:![0AR|9OGByˑϿU1 @D"v9 ,Gp8[}[od'@dŸ8 XjX χ-$5t+Rg ӏţ_Zsdѓ6}>xO5tOնJK: 7?|f,8 boK(//jrMpF}a'o?s^2;oAcayޅ y_tvj8$PH.^ocg yΒJx?'x͖ P,#2dO3-nj8K0f$4{+)yΌw;kɊ }7?-h[vJ+pOS!ң^g#D!2E}R<|EVɢOD?i`}tӚ[͙W3A|q[Ԑn+г Zrׅ| aXUaa^ih%;׆څ1 s1=2-gEn}n_dъsCPNmj I̅w,iac H Qzto8 Xjd x[gls`څHk^m*8.X8>C{ΟӡvT&0W Dbb$DQN˙D4DS!n v}nct$([o骗Ոt_MaVэ ߘ~aL?b6}I }+տFUf{-E*͖׷OcO1w'Xe*ʧ%T#PIGj'ζ^S~ًй}Q>50tHF2q2gu+5p4`m:qH&*%3m|H't(84`oRQaoե,5IIi+fҾ(1hwTA 5thRǴp8mvJ!l^XҲp7PJA1/N Zqz 5IENDB`gammaray-2.11.3/ui/resources/gammaray/ui/light/pixmaps/kdab-gammaray-logo@3x.png000066400000000000000000000644471413310443000275520ustar00rootroot00000000000000PNG  IHDR|EsBIT|d pHYs,K,K=tEXtSoftwarewww.inkscape.org< IDATxy\e?y-!- T$:⸡@ :ϱf~$A,8j(![gtwuwUt^ u}*s< """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""Lw*39&ԅ~{I<@HDDtڝsjC{bAh6syW*֤LĐc $"}FKkkеmX&#ofV *?%=k_n@HDDi hOe#MaX;stug.B""|4vXdi}\š 7}Gᤴ샖zDxd-يk_ .nBtvV=B"IjIyΤ#\w:":mZZ-ugk|.>緸MK_3Mq,Fzs֣p3oQw`A=%ד!\l^se@!$ZbaߓQk(@ޮΛĦNwo~j Y*=1MysY8̺)Թfd;7655\Z ]q_m DDoW{u!L3|g,fJū:_/w_ϙ7 @HD4IvtaY쮜'TMkm)4&E*h{ DDV_ SsQ p3nqs&Q;n DDwu*GLw;&K̽%E%HłSYΙҚ-L'">hlOa:5SN$ȩF;Ķo2RFRI&(C jkv&#we;Nw;91`lܧR;Za]dP&ontb3wgUmݺ*WsL0tugXuGHDT-3]]OD[vuqWk`@o{A` $"ڊ50q&^ڶjSpd`4`K rs&r>JU!6[U5Y!]]z۷ްQSfێ^ܴ{"` ;N=¢eŤͼmfWRE־3L^s_T]z DDHwt 7s&Nqتj۾jYYѕۓA}bmj=vwt.~@HD4-wt: bg"oE-_]zk+ fX<ݏhp{n7WSv}LsGIX"\Z`R2pSlwFfndhZ;'Q灿{e2ɡQ"q*oj6f0GHD4BtgȮ>0is% DD7="hG7~Z 5z97Ya&z  {DDxm9$Ƽ(M.Ey֩:?o浩tcŴk0UXbi3h[XU{guǺ)݁o|9 I/y8er4b?jΞ!Qъj"W\Pz$# uNzPԼT鿣䊕J_{v(6)<$'FJbB""*Cx5ZА^k43Tc1{Id-4cw* ms5Kw5[+V6ؖNWWx20~/muC}/ ^Tzr {u2?^&yMB"=wAJhk[y˛νӺ]K{!ktC+E~+g9U2~ď9/ sV &O6Gۧ D2\djjĥu;hL~ej0~қթ`,Ljc+lMKOf_ ܞu˥w@HDd U+E'WۗlL3\MG@HDOnedm5Xi"LӲr@HDv4FmsicxX=WΛ4B"\~5{c= kaY#E/Έ hO$&+\mڝυ]u}U>L\^4#0hjHbI=i/gldEWΜV D_T?!lb}ub6 h[4V|49vNMŵf3EޫtOw;*0~束x.;8s\VĴV|A=Lp" +Ya#t-}l~;6vLSfJND-߼%p _~8+Nf0ZZ-h?Rɒ=7-;XJo:4^֌|Ծ7~r՜=ݎ8\CT%1u Tp(+`0?w{4PwJ -V\#8of17=S,; @HTGQTB+F= Zzw=̤E N(4FmiK6nޟ탮ڶnp,D<|맱T4Zݩ;l@rkd>0✯<t(ԻTVmݩƳ\bLȢ[wMw;FU4~wDSm`fUQKA*((>BhKu²=ѓgL»S%huc~zBomMnR M4ӻhj>u1FNp wK1솳r=W=EO-/d^5( ^:5CA 3mZspi`zy;K$#9, {C>v=xUrtc4 לy$߽t@!^7g놆ڻץLK7q@>"6U&!& wOvb3iޚłn>:fH g-K<*y+6Kˌ/MO?qn;6o`v2Ԥ0VdE(@q* B߰+ɓ>_0R&r*LK-,B["mB sozCi('ms퉊łpW}l~׾\پ6 A` `G[ j/"0( MqP^oCbvR+Z v7A鉟׊kUuHt K|"#MhTR,o9Ca )uF5R*aX 2?Թ׆^ofZq5hE ?j,\We[q}3پ;h̞YBR s*ZXbpiPǟ{+ 7җ @+rKyo{JhTUxw*D!sۆ($ Jٳn̪^.!MP G?Ȼwsu0jE? 3yj2 NWr_6F8zf}3>p}B2AmfRZmN? bp3\j'nRx4\jL7vxDyןKLz)I6u>lX~{ZfvPiJ,jiФokʽ:a*?.-pq*{OAUb WSKϬMgkgtoR}rY]źr^t7c<iJdOPh0b!)8++=ko1Շam`\^;Um\Pc[ѶOU[hl5Xi1`Vwwn=nFS#/G~ ÈN1}]㒎!t؊7s@C^Z]Zȸ'tޠD-\9߁S 5kS+f.;_NtZtMzQloS?hV,kUv{4%_x/^ua ܓg9կbP!}3mnigmWUE& 4>-xoUꃍ'RNy%>aV@HSB5 _LNUmnѳtSoAҸS1i57i-ZLf.C:k߳# Pj86y '}f;@"Y~`%U~/g3_.;U&fS{Z01ҤӘ*m9̰<>s=m6?tEZsd;a7V_.:X桽{`Ks N(ܶf|ϙ&.gp( 4\%7?NdL[y]ZTCY\j%Y/IuNSݩjĥuNi㑿>30>ٸ}Rձ` 䉉.+k3w܄ N *S};jL5ir|3,;ć^.ËgV}΢.a6/ j1ΗV_a<~o@#|W KYԾJJC K/m+]fnΌ\{\oTޙlt͙9Ca IٯW_e鴨i۵ĥjw;s/̽ʱ}{WSͰ7_?LZ4.i[ٷz[Z-̜d{ev?pd mi~7Ɖ}L»nFޝ *.XԦәlM4ӳoS0bNJ}Q\rO?%EdR=VDUP垀("￶dVlwɅwJ"hJ]֊ Êwf6iV6jIH5s;#1Õ{*=QsIj>M^xwlޔ^k!PLo; ҲϮf Y%~?9rr8CK[ Q f=^:#Sl);msN|˛Bug-wPT_;pR@,ln/?ș»S! mZג;TPiKy[ljϰeS᭰آTbi`'=4aȺKWo-/d{o}r0&f篻x2ڄ?ZA][E'U?~=#8uoR Slof/vNf2B}D` UQ>yˁ Y8jNziB wTBH2aNKkkR$'9Ca Y`kWIf&k+u # =ꢙ~.P/ZL&嚣75'-پ7߽c2c6j 4+Im(w)]m+cb՛iԴƫ5nLI,bIKwTb YiqO&cpߩԞnRaA_@47ܰd_0{[E +K*&lvJپf5kz3{oKS7b|pf鿵"![~=% q N2پ.8d{{ 'u3!^ =AWhd2l <Q ݵ,M^>l>Wxw*~kʘ MmcP8 (zIc+U[05/gBv W| s4t\0x>S{Jް=8*:uXܧlmI $x$c߹uŦ 4{cBt zPo]'g\1 ^=Ѥ?{|e r>}n=cG}#gB݀}5->T"Xoh-|H{!9P.@\[;J]}$ӇY44 o׬Yoe7Ζ@~P{]i+Zk=t1DU:bJ_K} uE|m%9;'WJqX]xW|Kxǡ!>m;(OhVٛ[/ro}'-}_)L5m23oX"~6>86/ k`&Cn֯{*'Ҟ}Ŋ{»S!A:monir}BdPuG\2+xAyτ.Kyma ͰɊd_Rp[^pZY|(D/V{?3zOk#LME.BE 3] =L5YN}ۦ}Eg);\΁l͢qޜ~roޯ(n~1}_|Hw Jz' :*e 0t$8}U8NrZ^ vɟX `F5 N[ngfTVݰ ֮x]Xښ]kʠͺoY6ݶnfux IDATҬVmܥ2UzG^~} "=6Зn=p.Z\}|\dfR̙k~UKG 4Cm)y\qdkYSgsV;!jnXޮ0n_9^xy17^j U5^;M\aӢO|߇o*Umsל>6^s)k\b@!廷rMmWk~0<@HbSx 0=;68vx/NU# .Nc_͙۴ zx[2sF{ݩoN{Z}U2xz >xf?Wj&~9\w(i*+7z[*vSdމ~u% ~bKW';-^qoݢt]MuvŚk˥k-»al+PQg{t(YYO霨 y,ǘYo*{Ti综3&)e+8 =0\ZH2($[ۅ{îu\*UQUW{i%2X10ȹFpg9;5p* $x0q?8 )V~XzɈo~ns"H-wvzmAͫ'9`! ]wYN'nCMu4͙3";kP`ysזDv:<.2lIs {Dx?N VsPTV"sr$<H\618'v9a"s55bII:q5&w飻fΞb&AU^xˮ /p#+LuGT'{Ik7}(9n/kSB|\LrB vj!\Y*'sex3)N(g(y#w'7x7C\ RG У!zt/S5KΫ9z&g~o4xYVޙћ\pYo<,-,JZ/ĉ}&Ι 1=-Xp@\̔)f~hZ~l.#tYՅ{7c 2[?dW`bm0u'uſj$3ϥR&cRgD )N[߽Oc8q5qqx%|GӒ9_a3hh1߉ ߛ5ͨEyo>h檷Qh\H=[ q!L'M5ᜇՂ+~:^ػD~[n[s>:N[`܁0mV]Yc=fmlqПȋiL0j p=ZUǾj]9p(U=Zo~>u5V:9t`&B7MZࣅg¿XnunYE'+[0Ĺn{^w|{ h1h zږͯ,X!?xiqPbܵm93 gUR?V|x+fEvhi ݑ9磥"{* evTGwuPzc:Hs $ ==щJB@ffbf09kS&W5|" PXW"hwㅛ8/L]kJU+/%~]6a\tOZ/Et% U(ݑpLnޓ/޷}ϗZM2Eo%ns4ciKUZUEs(G+KMz?8I@}|-M۞ ?D +քb =@iwԤSTD$)1ws2 'fae8he"vb)NS{FDg)D+D79+fb70RvAIyc#C1 *4r޶aȶAS(vmn{WQnG%|6k #6ګL\}iv[ٽ>ro !@5dм8C+lY̲ȩea 53vtN<\툰>o9di@҅HR h;;[i|Dɸް OBlyon9km;wWWnn/}9lӲۺ6d_iGUJ ?hQ@HmpX)WЕ2)å&ۮ=q_&Ñ7+4-680;8X_!uN{94pH ^Wv9w|Kؓ<+&3 aQyT\u9Ioٰ~7d׷"O0(T8q@BqN/ 5=uKIRvſmoەɟ s@`0"#׼U "5 P|ϟ<|gfM >ڱaK]$yL"8_t=;\@`=E͏~3OvH憮vYzve:o2 %PY޳ȣjxK5.zk+ϫGqũ1+5=1-2i B'8nQB84 kkjBS` (\G4ugI#LY*mL<ѳw6xg%硃`ɚe&Ⱥ "bOY󛏫 ;-Bsm'2޻s3;jo׭29l)@<9㳛KK #K7SJ]JsX+}B`SR@~țo\Qk,#㱑`Y'yLP?{G[ϭJ얎/hGg SW Ώ;VVEy^p{c9c soFhöw{ L[{wjk~8)QA4=}s͗;gP\ڿN*?ݯ)ݘkAq*~páK=DVXo2Xł>g35+-Q(§^&~W`coGTGV|=RL),^nH6vX\ܨ\Mψh1ߡaC7/qphzlj#-l\4`6l{Zډѹ 󪣖`p;pmؠZaB oâja }†|}N*_ӵnX>Y߅8dp|%}Mqo%%? 3ONjab '@a9ksȜŵbzWrf~6SouNv:Bp&]PXA@{ڶt*,kbԶT P~-<8D8̑(P3vˏjjt[ni{dY IjiUN^X{\8 W_v٭{2nof5՜J]#.ѸBڧVGWRG&Y5#1@,ߡjHC'LN}lj.J  R7۱]1R ,zC}mQc{؎(:뒏@M:=s$ qXs3_V. sǵw'uQN`;@*gx_߂ %y|gAr Yx[?xzA$wm{>e7_*q3H(1̤BG^PE liiqvD@HN am[D\`c'-M0sTı8ӭ`AP=%MxR0x~*XiLnW55a"paw- B1Nʅ}֤h*m]TMow懰]^`ږs9 37tl.v+] ij(;گT q>Q=3 5>!СS{ni}5KmItl#Mh6!we{"6 祫t+ewwnu;<o*KGvjz=" 瀨 kqPJ)Īa=zyh{">K'9Y(RZ2t8QWr (|UҬ6|ͱA G v8B\CԮ\X |M߹rc|o ߻j[NԚě=w.hY$!^Sn:{7Cľ=Թ,R%句xcs B'hmlmsʋG 42=zjkV ^[;\}EN4Y @Nn!Y*@`犙+*Bu7~-o_+efrfݹ|>՜KJco]0 E.{hGbrF<˻_/"rW?BNq xX]&q8"+sOm2qAvuEGo7^%oz[ɥZ,qSunXn;6F{Xx9 ^Śݼb;nqrJM~B|+W8MZ z}^ԸҊE 1Ɂ0ތy:`p4EjRr-&Dw8ķbIu.č ۵])/*:lPXbb jFn!>,ן\b>"řQ伜 ˷YJ7(lm v:;nг<`.rMaCaO+v{~\pnPKo趶|sݸ/^y m鑮ࡷ[.J|km/5U={|o>MKG@Hmd=.rNHۭ8+VتƸtxNG -r[.+ԨP8~=83X!//" 9bʣN FXf{"bA0=od& `f9_1 tF+p. in Yh>m 0_ }{ǎ.>.j\|mzd2 ~+NՕO5TvOkl.*ſcfϽ;\GUM,搈%Σ^5Fm4doݽ: (NFWX \vGw]0oLi3oPcuo3aWz?xp"njZ9 Zޕu#Yciz=Thx%W[gWeQmlii Pk;}o>5IBy˟xLCbQs|PQUh1bw` lsF++G㽵fzL'JUAAo`V*-fp=YuyjxN2:v毛ȹ=\?fڽ쎎wW9;'҆~홬mGl4#I&qhDVhRzQKk|!xA=5׬gz<hqᨢ<86-^:C---}>Yrd2^1wW̥`8PXfHSDEh#tuܗ340W0-c4n C>̍9eba'tCArMO.[RU+*_K&1A?L 3Y'"z,6s6b™M(lZb j}aDU_wvѤi]y VNbP:+0H*Wl J+u`XZVY+Ez&"PS툢ud?xZ}wkRM*?}f_Hd_ ^EnV8}N݅%D;vuo=N3S[T?4(↥;z򥫓_}}}t/8 BJL6>xXqxxp}{i KW4ު6cC zdG3ХuA]o^΍TJa" ʋ 't53SY~뎖׷ qj\1^7vC'WҬs9_Iz7Ko! &rj'Z ʅ;n^O F]\h!,E$^;qtj9~T񥹲J1o#fvdMc;= '/$5h4Zfqg˘o;N]m~;K ٪ \SªbE𕄆>WəOٮ% G&blq# ®IhKNJah4t@JI聕mA͈JMS@FH|\Ű2N&x:`G9nw rͬoxSፑGW*25{z=EơSd#ṾyZzy9(;Qz^ߑ IDAT0S 8s]7͛&bı15NV~ZZ-HLuU= 4vY\ϻw]#}QϺ@w>zՊ;U7sULCoiڭ^"Ac?[w?쎎5gjn¥v<FFj߻7B$f"nқ;[.uytgTVI&\d{ Ҭ?@~.S3G8\p~A<:qv"<PaWF腛>v{ 7|khr:,VJL0s]d{xT?{f$\A.h&4*BmsgmCr¶* o.3s朓g>|>ߘ:6`8P@MяhÊkaP$RG+XQ0#жqxbs':LbɌF\e;k>@ێX03NDl*\84_ : tV!jc826Y*fςVd)@eYΩڢhm m> oo3_۴"[~(`1AL4gh9/z9=ݥuw.xĄSǽ9SL1[a拶^0NҖUyꯂdzYh<ٜt.P/Sbfe.+Dam9ͫfjNTrk?͈ &@*K@YU\ Meds Vsgfg@x^0 lu̱LaAK;%Qjtf;N}jlضUc[+񗺊{/RLioT^<0Mso5>6 <U}SR(Ȍv픶8pJgq~Y##Y5ʘ dOULȿ5mbVv3@un2#;:FtZS5Զt,kbMFn!PNQgCuzܻ0 v%j"y^;ċYG3؛t!3ri.Oڙ~hoۇ B?k_;t]chp@HCe{Eܡ nviU>qp ]>.uMAԘ ^u)/×jM^cwqvM:!Pcyb|4b Cu\sZms}S'uq'ٺ䥴;I:F4/:!F;bLs֮.9E<RVW$ƻj}i' L  )k;Rs7>]J #&Ǵ'iOϫ;_?d#:ֺCs]{h473|?_>͎`ڕk'p{x0E/O%AфD~ .t[:Vm ǤՊ(0Vrt#V Wexd7G3Tƌh|gH ~_ vQ ApoPtZ( mfTʥ$SH­NA%x>#.vtJcmĴ睺p<^/l[{]m0F\Q2%:$~׺fh~1cR^Y,̤t vz[I{iM?6?JeG b8'M72$"g*`hvCfdoB >v֎L6yެye0|j_Cw}s;#2-.EG2Z)6V?y6{^fz]Y(Lo9ݤW7:6&9/P6ڪ '#bxJgtc'ӥڂK}Z`SG2i*5Ud=3ßۺ/+VbkDgIPQ!E}_y Y[7}Ʊd~VXГ@(hA#"|fʥK9)OWӝ=hn4܌Bald=o7DRP7l̂6oPUKB!D vt{:=rzx&SBٮl x-+}6؍wwX'cohP7fшF'|el(m]0jf3c,-poaBFB>o8uI/aiLkRFm@(HZYEmtnt2[.OfF4_\D B>waa۞1![i[2 ?F(HϪ)HQۯ3<SrU_V]R(v- B^~m9tB8h*3 2MNi;I Bj87MԴcD+"4JN"ax7ɔH0 ; }eڬ"1O7Իj7 趵H- !bwSMz SL Q#4G\:h 9L@m'{CyBuKb6vs!Hɞd1a f~rd*?hS=Asʍ>PQu3Kmytcٱ^.ö,29Pԏŋs~>:~oXdXaer~ BRk;Oˆ9$ e8:/q0 N00F[ hUdW{WL\9ku0h#Z Iv)T}iFO=aঘ'Ʌ+61HPcpkIy`}?(vپ?%b @IxƳ ~^~{}onJ1nש͎Qep[Mv[k<9hdS@A'Q>mފh2+gʕ)m,Lr^߿~@|5k2l<4ѪLR'0&i8_즟%j,PQȿ]?[]$йyc5N(dhmHǭ={-YV~-:33uʍ>t?ـzj_HnZspڱ p{:ft%~mz3on[ۯkg1s>RX $1r5<^灊]VQٴ-[]mkyvlB]3e 0w@`~y`$~>:$3(/Ud3A V+6c-fVس20䫛 Xw@Lgxގ}fOF4n&{6ԺP/(ӵ$rGU0ڬ%Lm"A6T2O; l;H9-!ĕK& ,+MuϟQ~z}_z}_y9}.`M@@ c_ء-"tU祵$UdF8Z7qFmkaO g-T۶4aܕ½UdKn/Xd [}$~]cïyYzު<%_2S@5KĬA+f(qUQPL+Q^džUsW>V 6AI@UɊ+ ڊ~o}Us TX>W$ʉ:7_x@j*:Ffd.I&Vo4uM}fO$ 1T.%'!$/)%o/A8N_{>}GhPs 4lɼtV7^Dd8qF1AoMg5FbNaK bڶo(`C)Qݏ^OD_CHo^6n 6f;!J?u:i7 ԗf5V13%5 B׶eNmFXUf ѼW?u>`v<8 BJSN}[Z Jw+{t CS2DJ|//? 4rގj ~_EbM3J]H'EC[E0iZ[}?ڵ~m%}BQ벊"d u8OSSzeRf5߿|ޗ ;>UX%:agoMY~Co[{Jw ፫ s AdLC0PjQՏG;[nWP+8XΗv8ou}])kU &H׭L%7-a:j;ZXҥL19/B09)u_xufs=>yWKOv`4k[^A@~A&Og36'b'2Mͺ5)S-Xޭqqf(nVZ Y,̾eagU&Ubێ, 7m~oEW@Mszfi!_%^`F ^ko#Y|^ km{7~a5;w&nD |~X#.ա@JrEf"<9- qt?\rbT{gtOB2n?>Њ__dPmk㱃]N/5@$/r6(G㵠wA3 3 ?"M-7tҚDE~C/,ߴ Ab/78$5͸1%mG.Xb81Zի6xR#ިV .^O`,:#j@ B}eE 2|A+1S,<Ÿ'dv >Ilf}ѷ?]MJAᄿWS?| uw\CG) Y<]4b\&8ߖG Vn0)2:*T͹۞?1(^4n-^)f>B!E)SfI5]-(`#l\O̺)p]ۿ/ S6}KocqTW:e(ށg)%=8_P%g`㌇cӝ|{De_@ًzT|8Hz3c'Pq1"d]쎁k\AdV/LY=C7aQ2v? iC{' |lH!]5hSJ7W80_؈[VDT*1]QfkVc\1*j0C+"<0Gxi:i>y󅨽PSI~nG. bd>:X\rB6 g^;cVnL2& 6ikp[i u!B35wE]_D%0j*Z7]Ȳ&8 !b\J(ᨩ+v,3&2q~d~-vg/hJz%{IWVd' .eV0"ϣϴ.; L7yzCm =dEv#paꚁ*Lpc* Wz qunqG#UE".8I&Ԟ5D4QZ$+cMu32C~CMi"+u#t >C9e@%.V54&ded|o7%_xL̶'#]9tG8Id5bwzf( KԨ"mk.#Dm޿ E&;l_G`36f1Y{[E0ð:6tm[z1IDATM_~(0 Жu)'ݽ?bВ+6G[q9-u tvЌ?zX7ԅGa_r*(_O߹Ю)U92گh(}11***G>ۚmnb"OWo$^U :f =][Gde?Fov3~ o,eyq ͘8~o/{MSxF'*vJmmY ؎qgwcggQ]8xog (vqh2@ؿuwv=FM//\YM 3_Z;y'_;qOf7Qcz.;#ᨇ~u="!D9wnאF*L'\o9N^|9&1&A5UOxdfY Qܮ޹Ru _T9Yf.6yci{=ӹv^{SsW>g囿|L2rqՊ?5f?ttZI_bٺݐe̱c\ӪӁ#Ck& :=~b;^6G}Ox xS+=@0l_{fۢ%+6wk(U&'F{L[9 ;.Sr7+,U;7F]Ww4eq3Ƃh64=xɿDkf WDzH B$}~kj/~͝d[-'?A$sr!X!NH:xvKβΆ=V8&F3js+'c$/~7@kJ7\Ѻh0+dk§h|5w^n P#Ac1Ko$مMĪ=2NTo__ lmt2cJG.~> d1hw g $ !_}Ǜw lJZHQu4c> 5[FG O[g&'i&7Pm9=ˈx@)]xlBv#֚JՀ:ZETg:ԺUN{gX#FbE^9ۗɃ)4$Z/vJ;nsjX}Nmms`.d/ B!D?8w'+j+8߭{SB SB)`c <߰L)B!knN)/ |"O2I@(ŻU W_ W+ }U0xUgzU TL_> BX C++{3(DwF(Hmk0ӑ4" B b_Yj7EovG"eD:P{fGe5" B_b7-(lWf!D1Sٷk(J*ˊ2!#B!DQc[]!b@(H 4P , !\ٮɐQ!DZwȔH !ʿN~CQLԑ@(H3))")%Pv~_5) ^YB !b? ^(vE:H B o(mk){L !Us˔H BEľھ%jD!ROb}(] /ѠH B!lqn߷l8/Ibl[[j6lW  B!84AbH+-v`@(rK uNJ]!RC  ! nObPo_eM>_Kxa{+=#;•%^NxFvt+:YTfe9Kޙ9kkr;MPU{g%SqXA``.8c 7(wY#D5;qQAaUv[`A'gz8ud_xn[K _(NMXtN!0魷w+x{ϨS`\n ;&/ʥt;pB *pYbI>n#Nev?^?6QTufCUp47ןsqB v{pͧYBI`Db^@tDQi~w^{E8]b9~xN_{~a20pӢ#7Py)v}\|[*'ǥ@g sZe' z .ƕ%^J|T㇨D`fsgN49~q8+MSyQ?8Ո|7\kmpR]8,wT8?Eڸrv5*: 0+`?X ;)o *pe0RC=vS#^Lj5'`L?;7._uLWKۇLF 'K iQf_UaS>E#ľ tTJrs94:QV7^bC9_4=yћŀ_ .伙F?4KL^4U5 /Wg#,r89iӧzX=aAGg3SZ-X3*WNdihG ˪ďnb)Hp-H?ʂ).n8c̍vՔo'3h)mg g㴪\Ye͞㭽>^i GS߉],*Ӌp n{wu$W@.v[ҩFjx@+_,!#-ࡥ>Ígw1)>t6'"Fa-F^ 7vw=-laA fRSL6WuAc6iS=p0 KdNZ:wbe''傪 OAV' t[<˦aӔU |1TޯyrpׅSi8r00yy.~Zv]B}3#%nε5Cu|v.`1?n1m7"mj|sɜC_ow6r$'RXDawSW>k筽,ayD KeE߽ېen{N~s4cċ}X* #'|^m̛iYۘMiX^f|=Q@0w!~jBF[d1VOFumR[/U)|"fp,!XTΙ/ Gl3g;St孱5xZb72momxV!GH&6W|AqѴ!>I޲ ;)6 <\<;YYBQ'~KqYX<+sg2'ہ?s-c,V~ o/WUOq%S GO}Д3 Yn \5'xIw`D u<~ۺ8=ycW;e Ѝ; fRS26r *xN }li|86L#`/ɓ],.Jj}a6wYCKԩ:Ƭ('Ogr0.Ç̓_f`,1R赓nvgOSo٭}?\kQVh3r}dU5ݼ53(ڹ`f*8`Z2't_42fg;(Ħ)6 ?`$E Rx|E! VDm=ES /5'G5Hkvƣ@-@9zy ^-mǢVBrЅۦ"T1}WSvwr>.-MO y ;[Dj=(=O[ :? Q[|P;TLnAǦW]#}ʠ70Fzl̨ѡ.E59XOUV 9d[b/MQe/{1FI$`cBH(8Qx5d٦xE (1)TSJLE (1)TSJLE (1)TSJLE (1)TSJLE (1)TSJLE (1)TSJLE (1)TSJLE (1s>zD;!=G?hOw1hIF҄IRcA 3A H!&!#_)1D0 Yw 2Q`w/FLDM=?p˜D01) 5{$Ib"uN f$L(0F{+)qDx1ehS/80"1^(;n0[?exLO;@kDF3\4Kd;no 4K|5)kwzW!哌oj%aK&!Z:^"#$F\| 8/I!n K|H0/I!K|k`ȷ+^" X |)dJO0/I`,Cm)$AF+N >H$`4ƐosD'&#pp=nD'# #ND'!cnxO2 #`ƪ%R>(IT|%R>+_H$cP6bxO2 &` D/K|1f [0}!^"$lxbs)$I.`p+H$IP}mӽ!$^O_I#<,8?x<,ɸ%tEXtdate:create2017-08-23T17:13:53+02:001%tEXtdate:modify2017-08-23T11:30:50+02:00:xtEXtSoftwarewww.inkscape.org<IENDB`gammaray-2.11.3/ui/resources/gammaray/ui/light/pixmaps/kdab-logo@2x.png000066400000000000000000000263471413310443000257520ustar00rootroot00000000000000PNG  IHDR@`;gAMA a cHRMz&u0`:pQ<bKGD pHYs+tIME 2!/BےBؖ¶$%(- @!mI !lKPa[BےBؖ¶$%(- @!mI !lKPa[BےBؖ¶$%(- @!mI !lKPa[BےBؖ¶$%(- @!mI !lKPa[BےBؖ¶$%(- @!mI !lKPa[BےBؖ¶$%(- @!mI !lKPa[BےBؖ¶$%(- @!mI !lKPa[BےBؖ¶$%(- @!mI !lKPa[hN>(ui 睆"}H#hj@'Ap~4W?PjPxS'P~01Pÿj0фL3M HH'r_S,[O#(Cf~&5s 0fS}t]2i!hjVLZ:Bk P5@/HkGmE5s'{bݎﯬAHF*s3;?y#D76uoPЌ!F$c s s\%}hNX&33)t}rBX^=-̍١L?hM[c]~Z!T2F2xY2+3bŇzyӡXXɞ|GdwKk|<>^B/A IsqR j҃׉5Q_8<,fR@HsZ~:B˛*1ya(Ғ%9iytT煭-gc3Դwj89쨻OAqVUƬddf&sTqZL 'LiPAF苇s{c'sK"¯vՃ`qq7\摋gpL\zѤ580z,7gfϱ$⸲tz 8}9 Wmysv19/. n ܿ9;z7 ZC).EAI~T'n~s=N.:4es2ׇMZ-ڇ3\yeݜ;/Hgp|SCS{#W1.eT$|S8ƓVD4Jp.9"_~qACZ[N.亥S/OxxS߉?0g2CY-nAF sS88cKҙܧu[Wgii:>hI3ĩ2cKJ9xR ˣzS{/=퍝8 Hr(SLvsT+Mg>U3.9"}/|D䦺i\zT~5XɵMz65t4]!vd]elK^EҸNzaV^2_Bi_30!5'Ƞ,7搩qR1'}M͎NV+~/.ΚōOfnAJď_}$Vnfصz// egK|Qd}\ˏ}1º-ȳq ~x++>l+ɦ%9)E|ӅXs';?g<>|]}KgE5n'Ca]J 돂SEκ]aqr|Ď>1GZ))]*H J9AN+;X]zbԼU%,[OURr()\(̛}+U5wrlo&vcH;hO;7=S^_0GN\wBO Aʑb7LaSSEezPdp?z|FPL{Vz_7ey~S3_b'm%).BSbECCN3f05nnFMB{Ɵgf2=ǝ4  Gľц;5>7?_T}+M \UdwKg_o-ZmԂީ29w^vlɴfqqLh60VXﳿ4IOR_]]Js8Gu4IeVdarl|~a.^PJY@Hs5\T`Bߡyp]orr(._O'v@P7?~vx䅭-}]O`rc(.>)hF`v$ E/ _. 4mu$~{t&0ዻj:jMm!jgEV4fKrtߞb<%iiV|,%$e~G]g%VWrr{fp0Z{xtǷO]gDI6Ct?_5ƓR{A٨XJYd-aomNR|P5}~.'r(ܑq2 %pK+.3\-uР!mSS$j[\v،O ZuJ)H(y5/oRkG`9cn'0&4䥺ϴ`>B0L)rqw1-jVe+:@ʩ59N>QR+*qǐּu]qCxFljLxzj7k}u'pn њ2#p{c'њf H idD U;\mJSfy%\ytA,癍הvLEs"6yٰd(>nud12МoԬՖХ[Rգ /`{ {{{S33qᡑ;TGwƎk |A6^0Ńsô4Ycz{q[ M`ԛM{G4Gl?iV%'Ԝ97+$ֆTܳu*_[?wd+1u~ڄE2>3;rtNjZrO1ﺕ渭pBGk [kgmވӳ ҉[NO#= VW>lJU ~ܗybE{!fiY:cXQ_ylٶQ³ =tp?gQ`BC&P9IqMBtO=?Zv*ᇧ࠼v/k3pl3\u$n[65j+Ʊ?#qtf~ajӺ.}ۇ@uKA3r /ɶzVB0#/OψoZXSY0;;[G{s;'S1"٥(q`J SkwS4v%/0 W/-JuOVRӽׂXRLM{|)v4Y_>OXi"4L֜zP&Y}[q\oآP%733b؅ r&Њ\45DHr VΎ(9yi{ː^V*T '07NbŗflnVxU>ؘ%@I]k WVs:B!=#<9xaCo>Už{J$*q0)Ų9]UT?1:y~383lk/li0\Lr(Sd$;uNqrμlN_O_Þi{õK&2gfd2j~fezy+wE#47<]5!%)b eiHSɏxns3{f4lnYY8+SfeX]F!<|r4t Ii(v RlKƐ ;8Q) 0;?,ȖW_7k|p; 8 OK05~fqXJr$gGGp-Ny.!v?cxkg;;:g^61r,<>Ɇ]>~#3<:%\rv7'ng ]hjldS];}?nrk ?7kCJ5]VVG\( kNJۘ01CIM/dj:0Z3+/fGZSfuj(ވ]83j^+mog骈'gfpɅ$94~Kj;FU۷uV L~f?zqwDiF3SN;3jn i\i3)7ݓLpKjٯ:+g%ƣRXGy>'+a'}؟7C$ gwq R i~z-?yiOąd(ƒIrJ.{̤%@_'gGW4̠Rw|lmlN3O65_Sώƾ-WFx`45 RӅe,.cgv5YA0n{u~]>& q&N]_{Zv25y8덊Vvtv¶),-Iy޴/bױXX ҇Y85.(Ȏ-!nziY9 \wNҧei(}!O_c".avtY}yQDŽOvsY#Z4uĬ2`u99o~6OnlfNfV/_[\WSFy;@SsDq_i*\kv uwVp8 k<ݰt4}n~@HU5V2X09zn2h(L(Ӎ,)IcIIueĜRlS XЃi$NM45%brv ӲXREr܄L*dz_AGG/P\tr?S _MqHؒ4e%Eź] L5Nά(=IzPL jiYI QƳt84c^_o>U XMqs|d'o [d;gkOv"!ͯV&b̠$ >wHNL *.5]sǶ3/5مb#2=3A`R'n32ɏ~ P5K'`NA2FɃΒN=WP\92g05}.a;##bn}Vk9k qtTz7!&03JsN+MY~ 3sx3 ;N6*VCqQ}v&촩C?8ښ꺥Nwej`蚆es295[c,RQMU.2sNqdh8JB-7> 䈼>n?}SmIpu"?2@2=N3rmjbW"t̟ R 'ɠ0L߄ @CY5_tRE.L4#j`Z?WV` {]*\;3gL#%\יL͙Ii}{Z~=jZ*Fvڹg7`͈CMٰdE52;?O8|Ԕf)T90 Mu.,hxx}̛ rlf#xշxuG+۰;۬i_+^ONeM SU f"Nq򋳊 Z<=N~*gP}*F|fz~K 卣0UR˺6 |}Cf,I~jdl|PNU0Y_j{%%iV!'I[WM]Ʀz?i9.}CELqr 4?hJ45n'7{mu?鯭dŇMhSBVoY2==:ꎳ=>7S3 >2?bW[W7]Iec@ =l}a\(EkiS{t扏wC谩t_C~{WkXeҚ[[;FqexzS3.Gt*̰:domEk2=N~|jQ^݃X/%MVEnng[ cCux-{(E<7eᅶ;.6܌'OeF,>)o-!<9.b.B_XRbjͯWuݧ%%;?;CcbZ,'E>{^m:q_EnnT.>47G:&So?(8z̔C1J*-aٜLf&6z:1wMڂ^j~#tO' =,ʼn33LF#\PTb}ui8OB)v5uw 3\% G.Sg`F2nI,ƲٙWnA{ި֗wD W: Pyc'z>hE~DQ*yxm5`jwgáĩepͱ8rjjԟM<9!4 e8s8`[DqVfS}ljm-h3`v0#ͱ9iV &D]:Sau4Su޸a(v6vUe_?[+*xi/T'fg%iX}ɲRf$Q格dr䧺u(U]n^w?(8cnVJ67S9%bTM\ytA]ngԧ%9Qt&i@)۩NqR!=9-Yvp~_c1q,Pá[:n~놢qS?9kNb/Hg$5Ap1n \  rnR.[^m?;C&{XY}66sǪ6twh"EwGM= i͍}AܝbOf{~ɰf p9Ԡ>{扝T6ug %NjHr*n8~ ?80jo򸬒a3u~X?]XA,qsJtyňc!݈#Īr/mŭ^:џ,.Ne 1ք{cXǪ^ZP~2Ǖe膽 #@kgWP#m@It^h56A/R7GLP1*)Cўv.`yEe|rv`OVF?o[:xw|l wFF,i"vh갺όŬlnc-83Rpo !MS{m{;x6~A? @ ]#2 d!Ӛ>Umx >i'GT6uz~#Gr /JeJ C2vvVU*e&{>ClN&G29Emkwcn4a(ޮlBx+*vɤŔę|X~陪x샦!g!5_ AZvQ-#=AL:kR]]ڪ^xuwPCc.!}caU_;ZG3T)h 9} 2 ja-z2@HZ Zk|4]wx,F>ϣ ~過wFp\k8- !KcB!(- @!mI !lKPa[BےBؖ¶$%(- @!mI !lKPa[BےBؖ¶$%(- @!mI !lKPa[BےBؖ¶$%(- @!mI !lKPa[BےBؖ¶$%(- @!mI !lKPa[BےBؖ¶$%(- @!mI !lKPa[BےBؖ¶$%(- @!mI !lKPa[BےBؖ¶$%(- @!mI !lKPa[BےBؖ¶}6sBa;5.@!$J= `xo/pΣH#hk <ڳEO!8z[ ?!hn=~B k<$'D|6I $c>;$Hn~CsqOp:B Bpd:[ 'D.6$HH -'D #OJ>$ ?j~zK1 g`u{f>.!0tX::[ϻZ# 80 ྅KB<A ZPA ?!& { Lcx"Fxn4\wx"?oaMi(K&[hѳEO1 Vln@[$ÔH%@/p*0$#(%V;hEO1B>{ \B@`;}>$1nL@OS{ 'x .Vn H !FE` #~BQO ۰J~BQ/zOO1!C@bOB$ p3V) !x5;5B!hEbW ! ZpB(htHb\u oX>r?!Me QXS[ wIO1~*W{uxw!͍#𿱖jJ !hpy-`u~~Bn 9j`OBđ*| "~F "'C.\ T:?yBħXWcߎ-~B8\\l"ۄq.3"47`Y% c$ `߇Vk~B2X}JOpo=hWO[Z{߱*%?!DJn zH !`=[$ n0X |xgb(waMlR ?!q \<߳EO1/kfr~gbuA=ٳEO12M1F("''C\={$X(/I ! ~B([%tEXtdate:create2017-08-23T17:13:53+02:001%tEXtdate:modify2017-08-23T11:30:50+02:00:xtEXtSoftwarewww.inkscape.org<IENDB`gammaray-2.11.3/ui/resources/gammaray/ui/light/pixmaps/kdab-logo@3x.png000066400000000000000000000412621413310443000257440ustar00rootroot00000000000000PNG  IHDRoAgAMA a cHRMz&u0`:pQ<bKGD pHYs+tIME 2?<.ffgͤf_ۂ7wvE>ڸ.~x L"<^ 69bOW?Եp 5 jgIK 9/_=Sx:xv~j$1:o6mV8bF ?L{_ lk ϖ?; ddc8wA!88.Gិ[y}GweqBU.T:ÆNxMM~}.Mp0pBncұ>ٙ.y4xRW?6 _X"vY]>LˇEYC6~|iG7acSa1hr|n/.OQsz}Ӈk& mQ4aӲ{nM4wģNQ7u=fY321&1b#~t'}Iq1q[EY<<5:}<7uƮz%1>=fZ{B'ؔ\/r΂o]]}IwC7s; ?lR2p^%𒘻C{z?~j}I}A.9R=ojc;;bb٢B}\qZߖcReqV͊%E|ݬٓC4 i2|":nv|ݓ>|ss|n?y_xo^|/gT>K'yqGrIgr`jOV_TmV1=wɴl~yA'Vܵ'WV5&Z1FZݵu[dz,|>E5wYpHE67Wɇ/go]+}lR18;| S]@ZxLn,߿ dd`B~yAzUyvaMˎk}{?Ξ5{$y,2ey2(y+QUIi'sY:=;/ݬۛ!`qQBUerڼ|77{,7UE9#sܬ\EY=}]Hx21|I ~N&R*kyι9)c/LG_/r-z9vV.\ (&gSU[o?4Y0-?c+s9xjhXc>=1ttԷxv['3)\tp19R5'N嚿iyJ& + #|~2n(~W3ۻSb{l V>~O f3<@cw0*Ӹut22lQ;;g[X]>\Pwj|e qVQ3sRsPROV<o ^OMWR#|Ywr \Hn{o +׵LCkTՓ+$ۨ9,/5J}O??wa!H*.`כle {:cĕx%X}&p3p@V 3|Sw@v>5Lw]km3WCm?w. c.C;5u!ʒ+\um<#K,.Y)ep⑍qoZP@eQFr3IF \8y-vs3)dz:ep |/xkOoܿu39yѭh`zAg/;牭OŊEuz3Ol팹OUQ]Z77Q)S) k(r*>s|y)6P?}sGFgΠ:+uq7b:yz=լ/fUY童6Ślh싹!d*f'j \|h1`aqE6l{j =!>^:m&,˗WeLatGF dΞjOyrk'{/.";3iς/vmFA=8%.6OJ Po>g;hJ"cgsإ%wvkͫMv.(|en}!^8n nssD1 &6ui|;UD kw5)rSQB TawWN*mb԰V:{k;{bqGLw x8?ߺ~g7Ǵ,@][(z%1EA+T+p*0Êe, _cWJ ʅKf3?N7vwoMj{;Fq4c65S虹V}.+hYm\ۥ9cŲvL`@y *ʜrY(C DjAMkc<\T U63>*RB, $KeEU t~uU=}.%DV׼g*7/4'vul[խƓܗ@w~uSNa2x߼ /lk B]P(ª Q[n}jr3]1W GLrә~|eq(bz:)8@Vo6hjiQuqa QG rYpB ésS:e\m7mѻOϜL4=?_+P&8I[_hk+E÷ OϙOenj۩VƲs53Y fŒ\aLԷxf{ N3 38c~ARu%-␊]0}!}WP'^ϖ͊ 1_<7{ye,>{B9@(OWVޛ7)?[:7-kc m>msc^#aê1/_TDFƕssܬܘ ҒS!Ǚ8$7Gⓣ7ìbw^\G<@o==Vv%YPnв}^ie-V,)c[:g|',Wz,NhE s|f~NІvS:5t~%ʼn|籱{:ז [՛խخ/65 h)V9E67zoIw #?-h-Qw hN0 9jf#P;Grf/}|})5,>vd)w\Tխ#wo%Ur;ǟ97>1>ź}wx]z\ 7r=\v\9vd)7_i/C0l=S+|2=W98S;VmhiV`'ی ,5Yf˜G7w1ܹ=˱J3 3aooR~"9p[g{8s9a!Mˎ~z3hx~nD쀏Y L(|}z?|"5P%'#vg˟h~oD%9i"C*96\0mނmWNjN˳1eneZe(05Kea}( 7#Rim3_]]D`8Ϋ;/'OGO&<. Oc? G wķI[*$@_Qd?⼅1oTú=}#i3~ԨK1#ӳY3n ODj q ܰf/}(J4|]Wת"PZ5&8?p㚽9)#-<,vƨh,ù c}gV~eXk/:a_<3;Dx>qtןX<',> zeɚ[o;"V1,+fa3XT}osO(rtxdc{8nXeOG0`b[kYW{훠4|K<,GqyQ${+̼PgeS@w\ 9,̰]9iu EXq]\LOzmgGǷtP28:ђi??ַ׷ZxqG7aptBӐƑ|p=W'n>mn>w\TŬ&`Ue ;FsJ29eNojo\hxo,ќ`<{><||>W\Au+Vְ)+F;qq˜^PjiN wleq=G:yzt?FX勋-͑3rxjKGR|4X}*b:7e8"+jEo:e9^Nx ,x߂I56pRu?Gf{K`l{I,gwQsne c%<\>'14vib#.) ʢ*ngO(?l֣BꏎF<"hWs{ 6[26- ZX-rUo^ /!6|:{u(ˊ@(2p/NcAYcXPh'ԴxJvd?}?@cW_hm䜙,_|.{{O9u~| iiX+/.fє/Ȧ.[Y3f;\u-~>hA|R;,t8ȲH7땏b"{:Sum<;NBVmh'+}^.ڑoN[]3O.jڢru,3,|zJS|ify>4?`yqu܅ WR6Ŗ>.]Yˋu1w,)dhr2ܔyJH.8=?PİzC;%l}sxbu do+xbsC7?@('s򫋪c.0>l':g,6岕5ZoW<~ ?cY V<7~ ڃF(/ò8䷵x{\򱊏y:V^u ǎ,#{k"*D1lqw\T~=nW_îq _ǹ,˥F//۲{*'1TeP3큑?N91w{j[';;mꛛ1e٢B խ]A{5ne= ޏx""n.i3ew$/HxkWW{beӦM&&YqڜX(f`\M. `v~7O 4fsY =K[>PI\ws}y_9`v ir[U '00 .e7N5'N_9./*rbKu{8un\2 -.샇eLI9ڽoruK11%gĹ,^ҕ5Qd-q4BõOZ@Y #}Tb 'c^ g/`fami4'8@vp+X/Q륶uF uA/- ߿e|πbjES9:9s ۻle [[b?X|i|ĩ,)dsJ_hY2מICK&a޾xKr<Ђ+F-q#KJ7[:ʾkc(pBUiYTfR&/-7U#LbͶN._Y˯/:4-wϘN0bs{5c,^PHV]_&8aAY6'V % lɢ-@_0tiEE~H:Q1 e\n%Y{[YPN|X;Zœ[:Z~uqu5a.~p a/_lЍX1PY,_NEs^PF$rYTś;{&<7DO8j]VB tOc Y\v~zNb?n_xh]ApY,QsJ8nVn«$Mţ:j# ?>;_nLF}J21?*m77&)^YPhpZ F.D{F/ϗϢ"bZ~sJ2AIVZΕ:n*f+)/͵$1&<(/G&tsKV2fpشy ip1&4xH55]va11?3_qjrY1?l勋xlsGr*'Eٞ7TGT!? ^DO +&4ACr!CwLj7[C;h ܵ$*$cO2s|1 "f#)sNqk jCs7RŃ䏻SY\uqhJaCSOh8P;mj co6s&Wc;F嚙bŒ"q;9NuZP`V~괘.q|~ҘЖ3sbNavu;}IE`sO[o/4aQy6'TŞᾷ0ac[|]XRȟl!0eS\w38xb\"Ǐ*X\$8d g옻KkߟxY ]k[l^ZU2'C*)u[#ݽ,7>7ށsW L{_`A(l=x) 6ǝW{jx+=Z߬L.9O]Ƃaa^"FGUq&ϋ=+澷[1_ϲx|soo\./.M7pIbnZ61+o_mv~@]R:^彳8lZ63 2tq[D"fwgu{{y뺩oEe/tZ6'9TesqYÆN^#;h Mދ !OڍeS*N΋=y-9xj6+qU]FN#qoԩ.=^MALt1yTe&]+~έ|)\uԄMݕUDN@+bvI&?Z̒,2=Ѻy'cP\ƫ=tC68?-tz,(} _څ7-˂)>Θ_g/ݽ }AROISc*:xgs،ō~wW8:tzvJ4`>(eY(f1vճn89/֖ŃqSbN'.䴹9E}e28B>vd)G$PmuRo t.>.>K)f0z\sK}-#JyZyrk'mȿ]%Nqʜ|Θ_1Hw^ޤj[v`|]x\לX+iC-uGPNqdHn,]ʾ_W-|>79fd0Ǣ,,VM_o&88.a"+8nVV?lJ=um|YbIwm?s=.X~\WEQ,.kSì̄OMM~z;* (1([L20S[LDns|vu^Ǟ (0)YTgױdA_yl'^ŕǗ9#g%\Æ>ϛEW <˲y\dy^}cc;x{ Gy zcmImk wǎ*< UrpE֍_w Y8%kL_EM?>ִo3 '$aN_^PPѰ(8d, D#v_~씘c*s*>qo b`]jS[_h7;Fsp$go?ud`,㑍|Ҩg{8oa!ug0=?~֞^>`kiQ<vyl~wq_t|mu=}%'T񫋪SK)J[[|871?+%[j/5S87^zR_B}Z][Y3{2ZKqQG&8@CU]D  r/̗Wq[|Ҙwvw\TͧşA0}ouřlkNe E {:R?6Nv,18 DKtCKUwߖ/usۭ<=jHBKOUxds˳Xh/4`ذj;颹;h_Y]/aÆ^6כb>C+%>VXk&EOґ{%E1w?oa!_Tײs6ƾz#t4u϶?lnS'D080fW7<0+IW+o ઢL:[f΂aOW+݁=AjYW{xcw/umޓ4a'aHb ?+o`),sdz]†ڶ~^ó5]Ї? gE虹,)bzA=ײlm^ˎ~¡ss1=C*,ʤh8ozC@n.?^+ue/I2 f#K)΃N:p|]P0]!l om 3/WN] F?_~씨˂~|` k`za]Fvx{jL'*8sܻ#ƾ F i п/ I x..1``1P eq F ȿ~XL,k(`obZ}Y2Hob}88tsŸGc xTvAV؋g0bL3sC3?s74V d,R䣍~y)v>Y2;PDD& q8@,"""""P8@,"""""P8@,"""""P8@,"""""P8@,"""""P8@,"""""P8@,"""""P8@,"""""P8@,"""""P8@,"""""P8@,"""""P8@,"""""P8@,"""""P8@,"""""P8@,"""""P8@,"""""P8@,"""""P8@,"""""P8@,"""""P8@,"""""P8@,"""""P8@,"""""P8@,"""""P8@,"""""P8@,""DDd2QK,[K5Ndh1;c4r u0 _AXz}|h8}L""X Ж""A,W- _q*A/ _q&j`ȸSC[""Bڀ/`&%?÷++|ED&S@- nPyIB- ܠW""Zy |5JDdQ 8WLF p)+ XjLb )` QLr `罍oa CDDҀa9]]e[I `lWvI3vf/ mN""Dcc=WDD[m _9 ""xlt_D~ODD$>u_~岛 _C<:=~g$@\0"WDDXarz>&I!j_!p#X!@-_61p-""2" '(|EDd 7?VȨ( 7|QSn=;cI@]%-c %"" +ծk>.$؋,=cID8/8IDD&/C[׈g{  Zi^j^gp?p@ z+""+[> 4 ,""/x5p%T-_(pjg""2qұi`M5 n-ুˁZܨDDdS Y2`Qc%_-=ZvCȆ8lw0FED$)L~;|a,""a:},Bp\H.ve)|ED$L\ bB|ED$)MB[E5YZ۰=7E%ED$M./|ED$ɥz^Xቡ- _I x'ڵ%hgI \ SSDD$b7>E""bR- mQH Jnڢ* |_(|ED$B_F;+|ED$%{w_DޢI  ` WDD&d n~WDD&d ^/ _t-WDD&d `?}f (|EDdJ?nBWDD&d~'@PȤtc1:|H"""7?+=DDDdyag_YDDҊ- `| XZ""v& ]jR+""ik"[r~"""=%vMK~""".軀/G:EDD5-ແC[Sg\ 4,""p㟯;*""4ƣY`/\Ⱦ:>Np=$ ǀO;4ȁU?;"pݱN75 pP3~_"""Imvnj$""LCz;|7c\p$b p) W$j$]k]\.[ _an | xlpa| W"""#h x=vhaQJ$7aC["""/U"""+k boр+1mV-v>e+mQxv>2E+""2 =`V mQn> A{I#"""~Xc{WDDdDCؚ%tEXtdate:create2017-08-23T17:13:53+02:001%tEXtdate:modify2017-08-23T11:30:50+02:00:xtEXtSoftwarewww.inkscape.org<IENDB`gammaray-2.11.3/ui/resources/gammaray/ui/light/pixmaps/kdab-products.png000066400000000000000000000031021413310443000262630ustar00rootroot00000000000000PNG  IHDRWR i%gAMA a cHRMz&u0`:pQ<bKGD̿ pHYs+tIME 2Z=U9Y)x`ܣǃFXW;Zl}i#pI>N8Fw G*߷^Tp`êu |ꚘV`qkݬ )KLMǼb=isiK~FYÀ}:N efV%Þ$ʠ&gI)0G(>p-V |:44-iج7>(]5:nX-V{v,4H'hP&lQ#1YNTasƏu,S^,ƌmm׊zg76mL@UHGyə!l5$b/Y% a?{U[F z\lRMtzzj58+1k@}À 7f=!CAw.-zؐ]CAJ{[yFvh[Ge k;tB,s[Eb-j|d&g9jhlD5Mf75v yr=(%8(jnܖA`ivKpN+SlPU(K.[/r̳ֈ.Q⤝Jsמ^bn} o~YT&ɱMcĚauM ;w <,[QG:&_F&)q(8Pe&ycx62+iK=""{h)hڒfu[ԫv7^F,RimҤ3IblM/xsjkUH9ƛJ98np6Ek. lfQ7k80f"]z 8l#^ct<קO~)V bu8W[i/ls{5"3UOLHyŦxzϬm[1hDEq]6y!E.$᳠ڳ^%tEXtdate:create2017-08-23T12:17:58+02:00@3%tEXtdate:modify2017-08-23T11:30:50+02:00:xtEXtSoftwarewww.inkscape.org<IENDB`gammaray-2.11.3/ui/resources/gammaray/ui/light/pixmaps/kdab-products@2x.png000066400000000000000000000062501413310443000266440ustar00rootroot00000000000000PNG  IHDR( gAMA a cHRMz&u0`:pQ<bKGD̿ pHYs+tIME 2[cif)Y~&GfuvhNRKmݞ wtAbr?L@njNmu @Ja >2 kvVV0Urq$i7Y8bIΌ;e1@W6 I l7d,w?+ŹhF*AW#]^v.t?^l2&QV˽B_km˜|B+SKhߍ\D6[^$u'$hXE[f[)3 wTg$%7;%^$֊D^ #enewՔ٧?K_9;m%(?m`˽O`;Dfp!;=}/KJfX߁zȂp5E[v3l׈r:P_yΈ ѳݪMv6,*\}O"F9=g'i)%'7"t/ZwtC]Ml7y(\ YqV0e#ρAN0Mt WIM@y)v =w*2j;l"Xj4q:pquH9'gV| 6j5Ke<fW M;q f^ڜh8xNAz/durS.va ӽ%/fC- .pկtN-U}iә`F_4)@ۦe(VЃLg WŐ[=m 7u4Jc-4 l@}B%naz m:F.-㥴5@y姆8⥌ku b,Ko2h4#ѾrǠ'Xi\ν án@HḾZjܦ)>43-u?Zv#,]wd%RU$[y"YB>c護CԹ.Gf_,uE &Ɇ,N R;} [27UCXqЧZ^8"}znf84Z2}%F뽜3._?ݍ5w# 5RN3Xse '^tgiV~V_an,=`\nq%7/;/O@Yvu5WU!m *I ";BATc:óȒt@,̛ Wpd)~f|>ÿ]a]"O1~-{ ğJĖ,ƃzK:k<.^pkPl2|fx*OKd!L`AZk[0(CdG24i6)کEdxb%dָ8dApnU銉qq *˅ΙHbF3`U;tR=9r'Xtdo0UvY=4UiWVLNWrѵE2Qn5&`kVcM7]XjzhHDS2OgMuY(P֙oC =|'8%mo=ة}kf-A_9^aa+|'6`fDբ,j}/.G}[3ʛrWds"I+&Debա _{?>Ogm=~lvMkd[Nn5v:>u* r[k6ʷPjn&u{y=dpxCFw*ktV'N<.+Zkh?1=x#޳KR_L5ZJ>*u6LW]_ڣ2>P]$+tWENI=joyʂũ48t1)HxFXxcKbAˆ*wjeݍYr*}BϽ39"7oTgY8ERVka")Ff7_յ Ө6m6;z%tEXtdate:create2017-08-23T12:17:58+02:00@3%tEXtdate:modify2017-08-23T11:30:50+02:00:xtEXtSoftwarewww.inkscape.org<IENDB`gammaray-2.11.3/ui/resources/gammaray/ui/light/pixmaps/kdab-products@3x.png000066400000000000000000000112761413310443000266510ustar00rootroot00000000000000PNG  IHDR<ŝggAMA a cHRMz&u0`:pQ<bKGD̿ pHYs+tIME 2>[Z mԡI+)Ï|#PBG̣%ng4qӪ1RA&O'õtP\R\mt!Wd)q84T|ӓe$+[gJFe*|4)i+_bKcE_x8y%(PPwf)2O ;J>.Lݹt㪐o@卦U[ Vx:znH֏ *bDmM@p0P1l1=|)'Nvf@)ԏxޠ &x8&b ^2!0D3)U'ȷ8TpiB B%VYZ' G?BoJ| No(O?[ k4p (gfPy%ϓkrz!ݷ^"&qƧ)4&,B(5FK (~KsJ)s(Ie!wBHrFqXB!ov) @uV=+_8 ەC<[}y J:S` }H"z 7|*,s ˞%cxLY)7.Z*ع0c UG$i!؀g4짇뿗oB.grq*cD5 VȾ+6vTUrB+i2\A롎0 6T|SQ[c.ˋ|!(`% F1[ b U4޸$m\!]ud(ux}Sf4DF`߽ #8*Ҙ4Δ6cT,24$ʼ+4wFuj =ҹAy%PGP'uP*DЕ]1bI$Iq3ތn /K`8JM8\0= z`M Ȗ-#Y* @߇(~V$sTjv;2TTLWsώY7Օ,IC4NԖO]&n.Ml}E)EIJ@%(A,Kd6/JB7G$6Ϡ ռ2CX\dRa Ld:mL$/T4\aPXN[e  3u8{c o+>8%xqTL- 6U Y;Pp{'c¿^qSFKaRD muEmWpnRQN SLiYD>k G,q 9Lyg'_'5kK* H6HڑcRX'uydwaT| M)l?uL.t_`w _0F^;#TyQL.("c$q2ӲE+G_{pU"LcQ?RK'#cM=TBL`2%QT$A2E`b.$̱hoG%|_TM%<ђ'O.90*gM&0ߓQ@BFp!Ad=\̿,${BNVY5 UXLkUPh) .d9q4,od(Ow)y(:Xܧ[+I*TAZQ\T(be_U /Q[ȹYv x٩ hQJr*HGTM46T;L(E cNVX<@jP)Ǒr4{© IoӉ2dkS,#pX()F[b _"QGMy%Z댒6Ǧ`}Y"\bU(! J;)=kH k6+بsD:fNZД6-7 }r.]tpmAlٲ)W\q*ʪX)DwB$0qHEB=N'F#z% z뭷.ZtAĬ,r j:Fb9OS" I!&pP(!v' x<l{sS X=p8|$J!qBVa`ꢠP(Dww7z= Cю `Z=Zv4/bN4 ľ8 Yb- 1 e&*( ;!  *Hd2@ `F|GIR> BQ@B]\.,KI,6 n9g tZeY$H~=Q!HPn ! p3Z(ep? gJQ vKfePT.  /(!hP( z0e= ʷP(Ň1d@yAEC@*BP^emeh({2?BPz?d2tSBEBP(=(w+@E҃AP ! P@DO~2ה2$QWB޶r`8 txp݅C'8|m/V<kzP( !+H_$VD;U9 #HgS@P(%K[   j I0ʘyXcN+y7P( B\.{)9Aczm>t( `ko B$ {U,4"^\ xqd(J ApIw1Q(*i,C$˲`YhheY@2FQEvmB*^@ǪΚP@z^r=˲ OIY*ST*nquj54 T*4M'T|מ=SG.BV!hiijEGG 8 * :jjT4_S}Z:ܻdD@P&Pèu{يl߆7NtyƦ U Z0цIj1-->wbN(AWBsXPNp\&Dوl{˿<dWF6pay1b@:ryf$gSaQ 1F ͋N)p/ BK(N00LD0Pn=L``F-lKxk4{?*GW7@!Cǝ8%B@ @4p8g0NUqK8P(}$!mmm{ Z=@.*u4=։S8l/x_,!X!֌ŭN3[S3 ҧ BD"DbM.\Vܽhr8 j Amm-:;;Q[[+zL4X0]=C)`?13Pk5e 𢅺,(W' Bfn)Q~H]\555b=UUU\!E$h20 __HDCG?x#Dҧ}q)C0g|cI R $IJp6~#=!QIl$/c S, f`0cQ)BdhNhVU[*qOFzz,BTWWS@q8ENFl.rdN߿]@D%}?TpVTOLz&> {Nl{xZVz\)`0:H)#g+al8=JRṫ;8tY}$b\ |_{e|!e2(łp}:Z}+feE AduQPnGuuu^^bukp`6 hkkC0|{Jy!>Z6 E|-hm?f[ M;Bm-,Hm֨B @}}}io:hZ444(}.l0 j"2MM'ѝiEQƌIcFd myU4 %t.0<Eh~hڜ,EM0\*Θ2$v,Xzx,=s߷p[)_ABOpY,JbT*ىh4ZvQ!h׼v!ATZ4Ѱ5sR+rݿ8gk[ +҆$nTWVs^6Z)9xK^ $+=_4N[z3&JB]]M?݋q8hdB0D(_YB5&Z:<5u!4jת=r8abmH !';.|5P`@*fD 9cW%OHhWzJi`> à"iy)6 # >)&2ASn+K:o__4ѐ\9Λ} : rqxbYb! $ Kn:$7C"鮅x,ŨWIĻ!PSP"<OƧ#{N$VaPSS @)hFDÉNqKZAf4;ϕ=+bf Da 39ƶ,m~n+&i_bnx j&7.|M~?oeH 7Vֶ muj)H$R+V V Gڪ/?B^2 \uԡ\k|_ "zW09قS'[?' x $!|_֙ N!,7 4gO`u`& P(o5ttwwjȱ-2. N2uK~ya嗟66{xPe[&L$w^tO&=q3-!xIr6U $ t՗4ʊ'ڂ XNYT*wg~v8T1B!E.2ڑHHFcf LG@ۗ : Ν1,{Wake|䛣`IbcFmt S8Ł`s.NLع)wY^,K\IC:[`Y$'/sTUU췦cVJ*8 q/⦐Lix2K!Rw? aiP*|łq-s]^>ځ Ck>t Xً(KR_"ieqw$e{sNjmuR)(=3dXK+9yۅJ&K_ {ܯknT۔V J@ P$YpV `2y<fRK%~+a: EH}RY8e^}0 6@kg$'9KX,[ӄq~,')28g0L]hR:7-={R<ȊXV7'Me<_I; ,a 7m2RV(foBw#S7Eyva6!n;)G4E HAzc|"Od٨`Ezapca#[~gm!$k‡ʹ> +h*}̔M'9"q G|3roΤAd5(>\|RRtvvf ~L[CSCR%H8f1vrr"jre!@@`uuu||w(*ldV 6ؗbh4Ym徧ENۤvRDh7 x<`YV,^oYY82NZFQ(8~>X0Ֆ]*C4|sЎra w6~e$=SiE\ [.T @ON5sŧkVbz&Si6+!jHEɅA)|>tSDjp:|8Pu?Vq"i7U|-N=r7qXcISǜ-ܱ'iF,h4ECCCQژs~c&y|#FVlx-e[H#hV\ð+^86&3<%!1w=wܸ}#N(7>(PFBj(É=ݍ͞Z>!QE?F>.bۉ7BkK1]O}bY(+tt)ZJ);A0azؑq%Dv şвՁG:E!p 62)+gBV/+:H<$!H8 uaY^+ޭRy? Mӄ_bCa T IAg-w 6g @V BNјwD,, Z N^՚5DDCCC^C&~rNM)Y? @eޠ6n_N81vpznl[S콯@ 'M.2at 5nΜ220R)ݩ2;Em;2ľd=t%hVtb/E(B @(=ył4c|N$ ENC1*8Pb!x<9! m1V[VʖhM }"܎5xhj9egje;J L{kR?:6Aj'No)WRឫfBJJejC[$[S?_@|3{RK>qo%[3X5Cզ-4PfFX"\.?]d2fx@JbQl TIlJi->r.%+29rCٺ$=k|G?/o9{/}zn \l%FaҰZ*Uܪp. 3[.zu4t )<E@>Br jar2sU9XFww7T*UT5l68NRS2=0-;FH,{1yJeᔡSOJ'l^r +fiB ,1Ž˅ sW0`Y]]] 25!T**++,N,b':NZJbA @8.yl܆U͗~%҈12+pzy_,ӏ϶/XXdh4[H(py׾f*rbwaāY0lw0rv45w%?aH5>}|\ ɊsU4$odP |;.JT瘺Bn7pi4#p,muQ0sc+Eh 0,)gᎌ @c%&KMjq q Cboa!iܾHAUA 8yܬ % l`UUU@[[[Y:T*~%EY!)V+nwwBSRN4I2wy8fwO>2m@"(p@P]"A$Ι1 A,Qc'S!9В ΉA7RYQfY}B!~~6C9Q__΂}Ŵ: %RF|"KdR4:1h]|m>.w0pȤE6_VM\0!6$DZ8+pJa/ƴ>#OIRP__ [wU l˕C%3\Tka`6aeY4I藢aބFhԙ?Σ9c0x}>>D"2s&a`HBz#pB cHW(4(%$Z(8S,p.8Amm-B"&i轫\ok^{-/E٨8Tشvw' 9Ax3?F~t̔\LdaBΔqzC WЎ* z\Rl%Z еX,c& >d^beR4i3k؝A[!B!΄@HyLU7-Y0I!z\ EXXVtpP`0 n|"'TVVd \1Slh96s/ڝn_|i - F!Vz7;nD` 䋜A)!t[*| #F :$rUpN `0}%!E`)+ Z(gkC~zZ"9b?HJ-F$1_Zd+#fmH^\&8IX;(\;lOX^o?%)fh*EVTNɟ`0eϗQ8 `)h0>t 8EF`NL(ǥG .>Ah=&cJ|'r7Ď`9w'GyFBFQS]3\tmUUU"+|B:%r2E \MroEEh.Cm<\QVHL`tDlfH69&l *F*!)9‘kLO)7zRDomYUWWWY%8h>>\s50 :'hE`/NK0d(fp9 Cl9!̱0 3' kb2PRɷMݞeYVJ_aա]B'/%waZ~NC8V:B`Jqk0s̀돴u-"5{+OӪp߷v)AQ>#)"Mv轁Xjy>1I89^=c"j6ZRȵ:p R(v`Yniֳ֟-M AA $[`(k.u4uJ92Y,8v.9S/_MˤH1%&)F[4J(juz=|>~Q+uf:IQ>tVDxZm\z}kKV)#2'yjjq߲5M`9$LI+u|fǤN@21 \c=QKEBP(&I%ܧjhhmkB!EtkjjՕ} aD" Eڤ$Z4]{ɛv7 @rNν x'Ẅ́ͬO2^ɘuS)$eƄ`)}|C `555,ptZ,68EQΪp551 ق!Sv.u#mIl"`5we/ 0ia3`ˤnD"[Bz_Oh`0$K*+;Q(> Yuuu9Ys~46rL9AhVpy|!vǑq `p}%&h8kh@\:S95 E2Rc6.Q$Ͷ@]]ߓJJRDQᲶ{0k8] ËUeH>cΟ8cK g6ݚ-&TX'JI @ %"hxk~W*o856BkG7jذG':?^6]6OVF=,+Ar> ㆦwn'LfhdX[0|0|Z;X /[VN/3ChuX,p:e/jk30PբML0n沿gh0yx-pPr}ӱ.,2Drkk>UŤ,edxW>7__[_~r%Fd(bK,&fu(W̝8&K[NA{{;:H>?}<~(Κ9\{&ƊaS\oz.?@ϼ|xC]ي/(K_• W6v=//-gOAEYEE:;;h 㺳'ywXUcҰy 7c8XϬ?8#U0 FcY^7ҦL&}4`x!ٵ)~zş~xMLrx%CX1y57\)E('NC2?G!V9bx?_fݶ/?|\yXNF6V[#n_0E_0L;J`XҲBz[8ق6g뚘8W>6v±6'~Ulf#^퍸 g@~>mU_|Jϡjrj2mOY ϱX(16҅㒖Z8eDNQfbۡv#kڳOZ~6' \|~zٳ*d ܗ yd76;)k~{Ԫ”>YF: ̠/w_ax}b*T*oVU8|>L4|s&7mY;t`f|vb̠*~ɴh:9v@)du˪7AcL\ZW⹣0oB&aV~&((8,&=2,i7֦4쑽NVVVfYvf:Z.\Mԁ"WŤGCmN"&0p:/ʊϴ) g!e/#A/EDb{[qY.B!N8#Qoߟuzn"Ɠo~.|QE;эX8}v•L3(؆Try+{Q*Z'5ZMw5u(xGSFU>eYt̜4V0^p\Dgg'|>N't:];[]0],(F֣3-~T *F)k֦"j-߹x6Vo9| gۇv4B7?H4 k&V m!i3\qNOp6C0u@ ovdApMGf6bcjL> VINvq8z@Uf#F ¨(+-S 3IBal?p#ea6PSi¸ 3:mq 0-N@P&&Uf a°zKTaM!/I֤ ChmZbcp j˷Ǭq1 VI֋gcnҮ.iaU0 " B/YP !tjn/٠AҢ 3)NUWi.tEyk嗜6mE?aҦalG%)_xgN| 'Zapyq)s2f^XS'󒖭`6<X1u@ܶxΟ;NrӃgۄwBia0gP,9{*.Y01|O$-M`քD`(mAva#PZk൨Nqazh{?/Ϙڜ7g,nX4³;=xon| j0u@\ud\|ꤢ  ݞ@(܆(BRl E,> 1Y2:x}< P#{~EsFG?3KƋ5Vaw9˫ϖuH$@ p8h4$l X,Y9@uuutKpP `r$O.4TU`cYkb,~P$UD]u7_:[:uur v'Z\>؄66a!xK0NhpzPr`qO=+dpqx􇋓ʋB0LrU,!ذ(66gacgQ$kۗ}> w<:˥PT#o {ke1\-xqf<'SrO,[ BXq`n|Oo``͜ `ߠ˃'2g':]˰ٞ1h߾o[Jht:eOKǴ12+ơN[x%8mmM-5VaXc3 [:~o=":dRם7?)~3a.C]ht'>^݈{̆VWa_-C|~-a/̫TNw]~4^]7^&0XrTb3;MAcW_. Η9}}tس)#jkRP**cqo  [3twMiXr4i]>idN2E1> g?? 63Ѱ|m_"$kaX <:+swm%=\\־Ϟ_t.~Gx{uz Amjfttt,zc,z\}&>zuDF~TT>bµOkwxݯiO3LT.;a1eYH*+Xr8,9#q?tvvh$acCC鏳{%bl:Ֆ"&6{)pxwmz> 6,{覬c*tcȬ"ys⍇oʚUy2GhbsĘ9~2u_(ࠢ!q%2ƌ/*MK.q숁$mDrlܞlfsQݮ IDAT@Dq{wm:f e=:w]s:.^%ł)#Dœ/(^>9rp7} bw1Fy+N=;3ۺi3Bঋgcʲ3 ]s(.&w~{ .)˪X֎n9چI M<~XT c'믿`&>޴OTTlmjg7pf`ok8hHaDχQGeKٴT`4͆j31}4j{甄HQc ,j,m K(#/tEwu9jpM/Hn. nCL20PiPk PkPk8{H5wJNlqNWY\R^9}b׌\Z-"w;nw7?E?'t: ɴծ8!i}!NAՄqQ`p PelBlfMD"2tuuT@cRTkŨ]fKD),3g|I`Ax%O?L٘*u"ߧE'`;7Ds̩yeA=X9qũYۡh`2'/RrUgMɫ=w% 2_{9%X,C4$v-Op2vZ۝X4o=t# ٸkX #GT*Y!0į7ap3_+4m6,_w/o3.Trt *R``*|?{9 ͷ'Āa)DeןyPiX4?9oH}q%!ĸ)yZ"iS2_@5 :\8o|F#`RʚʊpВN_|>OHZfTeY/q 5$΃Fs;.q)C1gR;aTTT'\y\tj~ BuyX4O p84S"SDl-OEcW ,#{ԃP8KrBhkB OVakI b29fϔt}M;Hh4p$.0 A 1 eF +ѻcƸ쉖dD'V_mM}*XzNi3/O:nw"Wbt8=AB* :,>}^Z q0rNV5MrB8>I *D?Tl%b)D/1tK }k$$CŔUsPJ=su?!QDQTUܴB8Ŀ݈WVm-h?UjTu`Yfs"f%_8'^_?،?u)L-'uMݍf{}vÔg_|+rV25VfKBErgPh*j62E=KMsz~W9>܆G۱pN$Pb]n)P*ShJkZ|f=%agtnp䄣d<FHlT SF`)_xOs%Ξ)+E!ـWnA̛4 /~œ]Pc[nE2DD[& ;ނ[ TlB|N$s1,̯c7*8#/}ų شāw(pD)'(b;6iƖ}8tGNte I)Rap5g⁧W`nBCu~5t:BpBp{p8Ʊv'v8mZQm5᩟]>c\h}]{YYg۱9_WB!lbZ!D})ǽ*DPWi=tNE)`aҢ[ۻ fo!$S.7^}}X~R|4 "4aNp*]ϻZbCmU,TVVrRr{Bڸ/܊{]({No;=8lGvtcYR٨Gu k?7_2MӌsfAu ķ/cs Urde,Ձ;~b@ETe $F#\[b|$pzkg$XL5s4.?wQj< c UBrX!  )V1ܡAq~gzwbac% ;t6y#kt6+3t TcXc10zH-T V{~)zn^4!F FR!؝E3CQXkj P50G6B?ϭ*vUZuR9wmzd'ϗ΅{egE ! 6q}L$>Np@o@OG^TT0Tڅn4!CpLĢ0 ll=&ɔөr!ٸϡݙuJ[w3OYy<|h@ y1KbbipDV*[u.oƚ-ٞ|:wpQT0 P @t^ n,AA7\8W Z5aP]%:ŧ?|sIz3[Rv72qD&W?Μcփ9 c߈wtt&wWj)ͶCv|Ѭ۹|~>&;웴iv7cX8u1t%K?#w\ ِ Ń(nlmT;eε\v8ȵ}mhQNńy#"l6`Zt:BGbBc<ԆcmN\}]bT*D"D YoX4=14>~$w 36}{<LBkwBF!G^(+GWBKX{& 3Sd㯮5{\ɣOz,A=N1@uuuYER /.S-  &ۖ28p;vh{x>U\8|y7ʲT,b+z&'Ek&e7"lpҫnrA?~\T[S''/_cϑ68n%zsv,?*rYʯΝhW%R鿟.oۖf6]>|WM7UX?7(H28ˇsܞU?^M ;S*kb9|o %ep2{~?>$]}^}>>MUp)' N݈[DŻ]>wnX55σX $x ~zLݰj/S7b8"I\uAʲ539RB)y9l^;/|7^?|_Gki7[9>Mg6T6ZJh4UV~v= DKo~xƲ KFEjܺwwa|hejB~/60M$ٶq,C/=:J ߉gxZϿzꦷǠOy4ߏ-f !7^t`b lߴApp%<- \w'^ 8 X3Sy^7W"N ~,m{ 럽v6=m|~ ;O ?Bu{/η/WO}88߻|8eS ellvbL~<M) Nو|/|KIҌϞރw{} SO/jxe>5nu >a|tNKZ@s^/% uO2a?>A\t7Ǐr~!|x9`䒳674DypjHWvT3Ҷr Rgxb!xڕ3݋،us]m]NV ظfϿzԶ!9s܇i+܂-KT|>q|`?}e˗Kq߿X{9~ٜ)8y|"{8W6tvt^:pXP n,U\Ӎ˜b?ز~wdSwQ8SOE籽EglӎgoZUw~|j |]DO\?7<307Y:zi\OzOLGRVM7LVYԨb{ /Pk`CmYMoY;U6lY;]N^jַs ЯN\'e>v=nobY!N\gߔhiUW+q%c1Zwn7?z^M`|e-pQ‡ى[7?+߆/ )%=a~$675#$ditX#sR ?ֺ/ @H6=?=5s)8y-|ﳶ*ݺ)!Hzߑe<~>Oe gm!Al\=ӷŹar؀|W.b׾cX 'Meh=_=0;Qb$܍C 1}X==sO^߱QLiB0I_Q9p,_=1DW΀WU8q 6;2Y> ^޿`ز-V*mV*-X$+Gql)h2n`ba۱n~)^=?E`!L&T*ڝ2˦Hd2 gz(npTdh3 N uũxrwLhK)WOb|֢ mŶCx%gW@u&`pypy\ګ~,,,4Lp٥뤔XZZdevOM fʵubb('BsF^\w֚:k‰a@LM Hky,0q6T26<Ի]qhHgu<‘׻ S3 c`SNNcepչnf4ţjm0u3g_:Dz! gӉ̄vrhfX*u4P6XA:Go\^^XŒQ73dY UUyj4lkò.:m&_2"&hۥ\7lH^4 aZ<;N^S6N׿"Q4z¢]EpKnbD t׶sm[DD .fwB ^N2|48l6@ ənd&FZjvE[q.Q%R3`KK$^뙆H$Pfˬ4}#T7if^^Ub@jMvmy*3:2։t: MӠj+SK>hO[4p@ !W|vfF122RU:H)F1>>}sCs$v8m~3^/o;]{ƗZ*t]ǡ G9%D6Xd?R%:Y+?L&Sgۻݍg,`ϑbƂ̎qIqp)kѭ`>+u % ;o!V+GCgLS"! h> @fL&]=ӎ4IH! gywiXZZ4ZHБ?=H M)8eK'y^D;6" "4H)d\2m7jn(4<~Qܜ(J夭j:[4uWL FY"+?v">gy 4#  nt \E쨪 0eHˍt: }4 Q3:; L&@ Զ}4p+ YM#YUUm]f2x<WPnU~Qa@D4@FSNVrHd%BQhʶ] sTb8Xp!ml6J5%z! ͵ދnf% K0 DD>H$J1r R)e =hx8 YMy!J! ~_^^nKp8%}AC_`5A O`0XRB$I<񸱋%Lӄa/4 '=#H&%Yp81׳K} OJu@J5IK&PUՕQ{1h #Vd0)QJELLL\Ǎ!VlzW/ CDT(0 &ɒ,C:LCV.? P p\\"(2b1LOOF+)%X! 'ȡf'CiBPdzmJjv,C[#[cwŎLJ 4 17'ͽ" Fqκ0:^/}"etp,}v],0h "P4M׏b+F1::N:XeIJ?1h "P@TQ!NddY- D8>Ơh@ٵ&dͲ:$ B!N~c |[Z^d2%s,^+m钲4 0'YrBD"tǤb@utIRo2B8ˠiw0`Ի4 @ t:mAJh4eVFM\b984u F~d2dqJ rH7 lK,I.v]͹ ]\P-#M“A!Z auݶe+8Xf&S&C]X^^vuuK,.9G:.z]סkXY ˘mv d2x,QuH)r1h f) ieE)^>!pb@D4$5 m&xfPU`JD4: 0 (RhFr1haN&qj'ߏ#G4bi֡v4 `0P(TeB*5:9 Ea@D4)nWI+mP^^Y S~ WqԳXF-P-//1a$ L\`/}h9inP4uiRBӜ3: R`F}AC(24DS.C-:b1{iH0h "i4ͪs) fn DDCNŷFca0h "eea|> {GAp#P̮ > DD^]F)%<+ۣh---n, %c2LKۤX1Qɲ%=+8pbXJAU5F|uPکZAuZexװj* CAQ1r6)PO"zaFKAu28M ۓRرcXz5TU弜!EKJ򬃕e(^2c5!àhX+2\.Se8r֬Yàa0h "0tFJH$R2!J ̚ EFy8&,}-RB\ڍS.p=zaϗ\&i< 8f_:YZ44Mcp04 3 \.JCXY n _p81H$ːdiZa%àhdَeϹAŠhXYverEq6' D=gj,kpguNJ.VI0RJ:tbńEQz{OAQ29'D4:rR:ˠ*r=~Ơbee(W+PQ :tk׮u4*3 CAQe(WoVi(0]DH$ Ykń qxb0h "#RJR)BnoaLNN~4CAQFiiYTye-rxb0huF"8JA4Td2u'@:%@$x2]`4 i_Ի4uZR#K2M"@8pꏯ -j! 4x\f42pO9>LTRIVI)aFIrAZN٫'S0oO㭉 &q*7.DD=Nu׻LeR""bvvoޏ=&>|,2W_ S~'O?U1z6/_?Nُ},|x̙(#xu9Sq|x^tSK%2?t>=6`@5P(Jҡ7Ш0552xeBLLL@B%H巵CRz{AvB>[XmKv/?g1tgRg5`M^?m˟Ɨ~ZA {*<{//'Jsgk)tlE{28/ou!ԡ$x%O9T\= h߫Ѡi*PSf43h4j;!^/8P>h*>͟#am-F{% h QފsuY?{I`g1Ot_1|K>"pӹ3з`|k?_L#pYU^9s29^'աfWFϋ߃P MUꬣp~oj}fА.B`ff###ػwoIfk6ǍW9'/n<* cz,M8Pz&3{Mpoß^ou^ZHW'{ne/pƚ N.{Ӷ12~7{.]Yv̋Dj[px)4KffJ0@R!Oe)#@!K/AUՒ匭XQϊ h={`%Š]q %)B;O/_8xG_ćrN:w?>8jsupˏb2Py[=7{"x.7⡧h ab݌{+A*KOC$CH,_YKdM,t\}~#5d i,Ƙ ixɓᾗ hرjG8aڗaīb_rBecA`Nr֧TiԬyՎi"OPubk6J ] ƍꫯbƍxj`if4 7oơCH_S{K)%G_'/\e/El]? =InUگoAΔc*g)7޶ tgnn˙O|L-, @H*:񽢢B>Dy7ĕ3 y|DzKJ@7 ,_7~ T!pe5Xt÷4[hzq): N*uB!RJ9t!ɖժڍ\] j1 B`ڵ0 iBf3M{ylߪ O8!Jf& Xq)P1U-dRʾn B`vvTdSݠ8Ըjٮ1M,//# bttjn^ Kq _~\9 Ϥ\)zm|kҫV88~~D!)% ìxrF҄Q4L0Bj'ϯD _>p'LJryH$Qfc0̢Ȓ@Du,//cjj +W|N?%/m؏AC P3ZAe?Uci'O'&OmzYTbەw~TKgXUt0j\ݧYT>.0wg* WpQ*C~ʕgulEiJDiF!䬱{V;:ƾa-ku3gY#D"+ݿtRJD)#eruom0 d|G~Be: t Y&H+l8~.eu\s IDATE!joerFaw̪@q949?auB@*U{DmV5*Sz8!r4u3LS,s<Op0==[YOr[LC*Ys0' ; $R9Ä!%)YJUU֜?/"0sيAbX5; `]Ux D:lnv?/iX\\dEYZr~lx~F(M!wvG5=imQUJ`ee0f1:2?aBTj d)Mx<E NTw>c EPU5|xxsw?jǻ'qٱH(ŀa53RS( ; h},F K7:EA$33352*p6! څAQq>Z `rP='ZRa(jV4X۱Rʒlgqqccc{G-0*pǻqVgM> Dm%)!"ubA@QThUU`& B(4^nf-~nnvY^@xTx6 nbGzHY'[RJaB*]8~Gϣ03SSTE2^wAf+Z1;;^oKm{-à*_zzֳ9TA$Y}erc0ZWGOn/(B@(_dtŴh$7-X(I'$>Nwà! u5Dt +tÄ@†KgW!UQᵮ'V6^K>R1 X<G(2B+j02Rpu30 " D$w BD0* T^]a=Hݡzӓd2-M4 0'>ux&rÂAC9 T+/>k!}Fuobi4u&)cQv= -s:~Æ!D"40b-!\>nMhr5>rN`@!B|ҡ i~ 6 \<!DC܍@+V5r 7jx<i20$O.__̀i "L&$X,JUIYI֡'/\2ױ< DDm֎rVЩ~ >y٪b@Dft2夔.gH`\7ם3t徉s&W*5"pU֗~> RTa(.D#-ն3w:=1C71h ja&35\UE^\*davh6I=0Z ٬+f)׶>> D o> !i*T{c*UY(>uhvFΚ߶+z"TUxxZR5PQw(d2zR^V{/5_7|mJa@\n0MJ?Sg$Ɋ!VDµN8ݯ\.׶ zĚPיHn>ŶGIB~ΊJ\.vWRg1hv_,2HDK-_R=L& Ԯ_4P`@E\NȁIjl6t6 .i "jd2@ :%`Е}k5X4I,l֕n}p>paj} a%eprk8+LZ֚"V$uVJ\8L14i;nUw1h "jvͻz\.671h "jL&ԭ:x.qnŠXapk+5٬k,nUWUrV@WOzHen@+4]ٗr|fȭH4Ȉ+CÉAQ8صnT;CMW+h`aWah5it]oL-ڱJ1^AQ2Lr\}Fu=+N]A]%oZ L)eͽX9۞L 6?J78I)]@ef +z-eя @?s}6эM~Rbii %qYtq@Z@{4h;vj;U?:Fȍ5Mk8ېNzmN#]ao?C_MajΥN:G6lXxV !ZJ`$$ P^hBbϴC$q<@JeLNNPgyH$ 8!4X_Wn>@lC͠;F<:fn/}*E@*הڭd ʿ j""PB  U;*?DDDԿ'$UMDDDKVwU OIO`IENDB`gammaray-2.11.3/ui/resources/gammaray/ui/light/pixmaps/splashscreen@2x.png000066400000000000000000002467141413310443000266070ustar00rootroot00000000000000PNG  IHDRsBIT|d pHYsetEXtSoftwarewww.inkscape.org< IDATxy#W}=]znw/}7ǘ%,6& 7̅02a6;Is3̓܁0a1'1!xv~_:TڤTRUIO?W*U:*IG|;!B!B!B!B!B!B!B!B!B!B!B!B!B!BYbĂB!B!r'[@!B! qAexQ B!BWPbD}߸E!B!脉 ޿AN̤0A<\B!B =g"8L2Qn,3>d,B!$HXv7#)4e`7B!BȬ ֈc&"?1^aM=E!\ ٵ!B!$nb.(71#0GG7.(<7@gYDA. B!BM:XYYAXLJC, fFbZ |?j~W[ni/6Fq͐PPؽ{_l޼=㞜 N3l@!BH:ɒA4M4 Z-T*TU/YjD oK)!B!)F:jf :*_Zb &Xq,tBQ!υ@!B!` !+9$ {i1ͪ,%n@:~^-ϹF!BI!˹BX, SRRO9렾2 B!$},AG%Ԅ&uIIt0+RB!B^oi B!B!'7ZnU#B!x <# u"B˲\nj dh )j1!B }bH7ÔPh B!XNzB!BfhJBpi@!B!di!~x !B!,%t3 B!Bf"!B!CJ^vbH7CPh B!tnRD7CJB!BY*f-!,-Rʡ !(7nI kZIWB! |n']B)%0hfDv4RϾGwţϾ}<2ݵO7t5 !B !%]B! z?O<ǟ߇^㹗x5<[?t3S\[B!BfKۥaPh $M;كkoڮ$ TQ;Kmzسž^Zkbs!?%(]E7!D,BXaFPh d~nݡ[$$4G9oX\pD@!BH(7CTJ3ӕ̐BB¹Ԥ7h[_X{Dm{۞x1jD&3w_t5!Bf(  {47 IWa,)8IWT!B8 È&<0J_J ˲G|!D-˅>DB!1}'Fd6JNB!ĊnfPw](G+ (6B J N#S@Hie##|sit5!B˲h4Rz!DdA@xE/D]D|ڡ@H7;IW#2l݄_A!BH옦V˲||5%A`VPh $Fr"+i}q9KtH)v!Ʋ,;LbeeRJyT*ۉpiYAIW#2}aIWB!1MvR R vNbNHfxuٷ rl\^d~txIW#?y%IWB!Q+Mt]\.0 l0  p;&Ͼv>;w<ٍ]{Rvwqpqy'nq jB -iNr@QsB6P(lCTih4r0 \..,Fpwx?,vT[s.ܹcmjq8xQx釡T=l{ܱ餫>z͹IWB!Qn^Zf a\. "fgfڳoڎߴV˸k7sܗt"󑷟\ΒB!, i ϣX,\.r^g; ݌3`U[}qdc3ݜyyh>k΅io1[7%Vଯ+lußG!yJжHQ G=U+ڲnws,/?ZB~DA!d)%&Q&t,BB^GXDVC>\.w`7WD{߹k3t/DŴ$usxo?jEB’}Rd %ȃFYDz6< B`Xavf\.jYz[6H*>{_kMJCg oK䭸#!1R7˲\ay !E}=yuṠC!iBJnn;ҥB(t\.vTG龧Hv7WOBrT·xϿ\O!+OPBbi0M.xϡ]ʮBHP+M  |J%60_C|,p=OC#tU"#%܋mOa :FD֐Dl|zI C;.='?P)AAT\0an%huv=(>BFJN͠BP(R^l0 k?w+ imOW>y-đoL:30 ~3}\at_;2#Az}_aϨL\ 5jJxPu 2ku3(<ۮXAJOp+$]y}/JIW'I:ZU Z3 aFw<mZ ,kKȢd!7ą+qA !V˯ݱG;:Ah4 B` J4 !$n julGa*T*K/Ƞصk[GvGN!.@ *!LtnuRڃoZZCT=q]zE_*\e\BfP8!| ؐNMR ??O&~M3sO'H ݳT { =3ܖI!"揿|TJz̀.,8TBW!jV4M۱P*\ശBH!J,QAC_K C!d\LfP!P,]j4#BW w&]}{P.^%0PݱAClkePJ]taȶ@!pઋEx 8b!DAz.qAޫݱRn.H)Q*l!ll"X2K׆<ӺtT&ϣ\.3_Ä,<ݍIWcn|oo|uIWe&Eszg(Cw`%8؃ Mքf<"p[Eƒށ !ď  ^"Q oW%E^vus_($np!Ew3T*XVr%/"ۤ XXaoVU ߺm'.>P'&]X-2gZ\̀G/*nAm pdvI y Ssy'2kb!8 sA-RɶD,Q A9DbbhFZ-Z- B0K0 %/ B 5^.Vh___XKs>w ?q ڲ_U]doRB ,B p;>7v>'DN0JzE{9Cp?!WKT!$!DϽ jK1EjKu7xUP(nl6Q,Q.)8BQpݶBK^6 W޿~z-ہJda\A+X|B3Ȁ!"@\q ?hc@%`!=]î=! kȱң#6:ۇF0 6Z )w(FzͺáT*Zbuujibmm zv^B'7CneTBB݆ee5ONhسŸ|ᶤwxu dړCӿapP%Q sxznSOG[uٶq߽UC:8",!^a}}V bXYYA\Ch(NQRQVmAa߾}`]CBx(7Cӱ`3 fn,_|;^LW݉{~>jĄ_ PNXlNwdu6hOi| ƕ!Jã G xq2h-y9o2$ J q\BȂ X[[CFP PP.maZ? R˂VURNÙEB˲n%)g @>GZeYhZ0Ms&ZDJh|GL ~NN}6T^WPq?RbooPn.8!_+"q!W_{+}\T<^1!(,BA^ښ=;r !8,q%ZTEmhpv%D_iX,͠PP\RB "?A];Flr4x7( ?|mh;z7x0Dz> 9gkas0!Ab:f" QMUPհ\.F5Z-o dP1fBPE4Mm9XGwㆻOOp^՘@82a?:-  LK?vFmP#D O:=E8)ha laovM !$k0Vuu;cV;?qTyJpXYYAV~NADvm/;ia|FhkPY̋|$d G,Qnz>'wg.q 0)櫩g !}A1Aۻ\tNBȢ:*eY q, a ϣR`uurl6n dI͠PKV*z=4MkB ?tIW#o݅ԓF$|t (t" Gg1>{G ^#$.D!|k.= qPBvK]%&,Š=Yqz3bt70 WH0h4eY@t3( @T5,R[3ߝ>Ͽ!zϓƔ >^U&< D@GHxgǨdӣT-8 fb!TT @`ee+++3 &a2Uuu՞iT"n dq, n?_ڼ :a"uD|;~+6yk7qa|_enOv/؈\*( _U+mZy !$)gh6( vD.LشEtJ)VVVh4AJ^vbAT*![jθɼ>vH5v}IWcjH8 #J׶2c%@t JYo2p-6*~ wvHB4M{E 4m{}R`t7X,a1N`,5!EfRT*%fP!P,]*hJ)tn?μpt3Dp/5:IWclhh!dP&[dpD߸e-Ò,z~ ro#;CX_.ˊ~=cKH^4j֫hh0 j5TX=N ~fnP3 na\.T*jjeRlzhZJ̴&nIW#kt%]1jՆvg _&[_/ tEy^)G7_xJRDNMTi^J$=CwYnPNptd a%gUxTKn ׵q g!$H9& ~ҳLDRAR] iOXd IDATS?)Å,3LլFkkkV%QI}U0R٦E;lLmQ%,BCRt&n#^8uN;le%}O{s{s(x :}A lHwC_A{BFB##j@#'b!<;~}+| (65wKY T'hjZB%F;6p@mk2kVP+RXe۞ !Cf(Jui&Zr\TDAZEXɵl1[77w8hX #ڈ#ڈ7_p >_۷L4|ᮝ'^‰mN3*?K/Yyat͑2,?SqA=Θ߿eǓ@Z%2f>EfQgȠf:0 (d׬%#f_V)!$ʭfYbJ\PN\.74xC&f)dVhggR>3񺳏CcY?|?ݏ=Xo& ėn؆?9p ٻ#|u,)y[zqP 7񡷜(6iI[G!Dz!-2u(*t7`=3*@݆2+B&fP| jؐ6ǔr]H)Q.g*dmZZ ^ejyٸy7~u[p-N_=9#;cg ]ۂ=O9|w;i|`C-8QK7®~MukK!ɠ,&2(hk< qL!,˶3_4LRQ h0S"}A\"=];Wv(w[x .?p+ưw~\9>ޯmXÃ: Uo5K|m z!N ~m $HDW}"=\0 ,ȠÕ)Ĺ2J֦UK<!H :]Qꪥ9F-jP(C";yN<3\z<>ބ7b)o\N=|\z,wPY2`m_s{Zx99#>0G縀KTP[NH,VΗ> Gŕ6 !PP(RdP'NwWlvh4ItTMQ!L_K9To9#ّUB͇^rq>qg.皆΄:7:\BDԗy'f|q{ɩk8ܵ˰*8"z9BH:EOYiO\ g(If( mUJ4l6 PMݞKȄ"B/{a2^Α_+?_U8p\y"pVLjmpKRB@8V 9C@[r _\}ذBeW@w BT*Vb!Ų,tLaT*6,}`H)0\k&?j~~mDo[3?׷Hg} ȀA& hyIx +xt^9 D3!{x8:Y"zq8·a Y:H^T|""?qP.AHU"7B^gȪAG)B!| *eYT*( s;w&G;|{W`Cc7+[_yv<Hle=k-_Kd (G&XۨFCW_}8pj[$6R5GK-d@ҷDQ0k cϣMPQyP*jB0M3BX, g`L(gż'gNhxj XŇvV5GmP*.&Ky:ö;|tMf1Q<meS;+ןf+6'"<u;! ɢfaZAShQgZw.6 4 YFTn0n=,Kͺ=QAz=T*3'4Ә؏\V2?kfVcŽijlݔsaIJq;԰~qbnAo9qEhIH;EwEFw CK_q&,6ZBDo9 nu7(sZE>GH$e`Q :JlrhZ3mOJMVHb) /Ot!K6m߹򌙕 *ϓϝŽy*5"tuR n?j Xy qr{6{8fRLʝ?aMه0w|CJXA ! tV7L0$>!Dߵ ] *jЮr׋}"YzN'ֵ}40P,QV'Ci '6>/5ݞF*r̽{cR)==i_Z9%Ix~zj%C@G܄ ziBs2``*+ABH71 6  pB$kOoٛc/|n/rSɠ[w 6I~>cGd&0H-#"섍v)@)l @G>Rō_%J21u2ߣE`c ѳcO! IIrl1x_(s>OOHzh( K}Rf{&r\,>U4dfw4kMDnoY̹OY{7xeNl;5_s9{zBBQN"GeaZ ϕw8OV?J'X!q3od2LDBכs/nP!Us@[BȼfX&'U!jnk8 wt^ Kc:8h#^u!fo&gx5ɯôw>i[0=nl{En A R?%ZBcojPZۛNwr1r52{9bɬa'6~,1A(Nv۶zΫs$% 㠎]k6A%sT*h60 cHąZi"-AaJjZNthZB&S1[UU7Yw4O:|5'KoLӔ$#?X , *9w`]dPV(*`Պ}X-J4yi~`3x̦Y|g6u]4 ;~KCl/>Y4&gN 0CHqEV[ZNd!ryU$ʄiU'ܵ}A£=΁9~;wľF'2I\?ԝ/bC? ŵ4We #xd_8p%N *.Nrn̽TS'g}-Jߺ$Pٱ{VVVY$N˲25xt7ݠf"@e_kAȬYv7נKuM'*dZ-Th*0؍dN[e?uS|'3~6Wg_~ e@]_]wg÷؉f>v4䏀t%T~1BGfH_}k2C8"¬ O*tlHK<2Rq<3.w]&fT*X,00f4xmVVЗ,\D`TuZ-r%d ju7ӄdkl6Q(R2 \1~olu{ڹxix4A`a_vj)ߛ% ^8jތ`@P$b#$m0|>Ju4 AJ8\I3Р z@=& \OvB@cF>RLF% 1 ?Wʸce6T' 6R` :=\{CO.Dx%  W1d_U}V2&28urY=}is A_&!Xyw|{goq_4fqU*=dNYmq iI亘s"LQ!RN6IxB͠;وjOTVA-˲G42u*N'@ݦF8vɇ} :r߂+ vɱe~ೱ|tAK|r|coKc"JhPs)8pc5JВl5^ |gӦLox=ߔncԏܬo}TGnX,%2XB>W_rЗ u/?TJy4v<2SǁjS5 >G_^!GSiavr9n&c/qWG!c뤘TғAs8;.WN\r8xsN98ܭg7²1{=}ZdW› 2L*-go^Lf׵!Bq󘭟1JBņ~9FfjhZ%K;d.DYr+ "a0 ;B=.kY:*VWW3ՖLSS/7P;.>s|/W9Ȁ!pcS/yKsAl x*'wN@s58UNA‚# N~0@! q @8\+Mà; qG۲ U+5D&hH!1nVB|.7nU֐bs7ȅŸ9.^PFAWY*,B0;7CP=+$jiZ +qMDՂeYV*q 6@Ԣ\cԬ~l.;XOZ d PDg)V~ޟ+'d IDAT,'O.0袅jP, *> az]B{xB%A6۹{~VF>Ra˲ !6fY/sz<Л8I 0OȲ DCFX5"`7Ik0 !.TIA\FXd;8IkCƳg͋IEל~(9iMu~KJܵs^saS3-*nڅԟ6 huzVآ=|Bg‚3PD mS<y}FU_18B˃ a芨Gc]XAhym[v.h_d1G,f#C;OjNz^ݼ3dΰ>64|q\ ^Au7C,>QChMG(üPBMD.,2 6}ӦOg`.LߴZ}٩^uĆO`6y{?}^k'ݤg`b;& !^}˚7C_1TC_?S[:yʤ&SAhC)߰ я-`eenT[3mN TG4MW04z 1ݩV+/ݐ4jjٮ+h4`Yv!xuTbׅEش²,;dbeeB!*M̴N:~'! 30 ?}`%i}&x&"A ~Wʉ_\qxGRA6 ˈ^ 㬑.#q>~iQ72)i !ϏJT(EaqVB۵;Jn IQL)V3zq:qȢ!ϣ\.+Px-ۄ, i^lT*a}}% (̅/ijfN [*22:PlF+Ng?~ݞe /_gpg+ByuW]uu;jR;a!$inC lNJelYq7*SޑuY@%>z=[Ţ-ItX6wLZ-t:B"D,BXC'+#, LU&Ė&p:Nq'7U9n=g{PL5QHrヰcyM`bH'7g]I -7ߦAB@Mhp5_h^2kPB{j2 zPbrnt:Xe2g,3A/GzUAJHCer7!Q,t2sMH*/j"* <Qa&^+++݀iffova!|/q8>.,)wPG+8Oh6o(߼sA9h"bǂ+7 k2a?L׬"p->[ qdO[Pë~jx=TdVVd5x, RvZt:4 [Eg̖ݐP,nv'I#j6VmOVQnVeL,B4k,5:Ssûr rxjeTϻ$`ЀA8\y6TI[l'-2JJ|eap;?ØC"2~bB.~L$~6iwtnmJTZkUj Xwr5 ;zl*jՓfzEq<-а 9t蝇T጗0/ dШ|X t\  Gl8r~xǥ'8aZ&2D1m~ !0@eYv;I $uG.0TU=(AͨJ%[p#C.AOutL|~6*FB!+'ʄrL(PJ]`V>'wj_5س-.KkE!o04*dB NqwD ,-'"`vM !d "}|&3.{+?$ vRi |] ~pW_8N=\bPJLw56 K#hBH2 =!,{ h4˲AxRY T*Vױn$5^DwBrdfBfD凡Ze\.X,.o s4,8u6TLUw<S&5XlKQ28gl~c$T‚D_(&*^Q YҞH. nk΋i;QW.JҬ \.g'+lZzv8PEݠVR$n hۮ\ i&7d\./o""3pIL]=<k6/,ܸ< QgH-Z*o;C'p6/0Z88v<{^;|nMl!$}H)a&LLdRwwղ] Z Zma50VX]]E>G@^GՂit7Ā ]Q+^^r9e[gݑR.dȄb~AN>OþF?r aˀ@upms' p zΆz8hӊ]+0Ddhnp{WppY61_Jdh6h4j &1j%j5Xu4D`1r7([y>Ga:RJ;ѣՔSyIF9A1dBAa l%t''NN})w>=kMZ> W j57U[o~Ԁ=B'ou u5HD/BIH~R_:z,pB B |>5aamWG  *qfC^O,eE*WC.CM2 a[.CXXϻrҩBtB{$6xZHOfUnq (T*j-Mn)2Q*P*淇BqӇO 1$yH|wT}t!_dݛ;d}sC\!'xs-\!Q'$*\%a Ʋ,z=0RyzvǶR,Elo'TMfPqg*Wq5t]4M;,ο8YWJbp#cɸ89|w=|F*DK 4aC~ J\P #t'/C ar"xjM.Gq;&NV3 7k @TCQ&6ݠ!|Ni|Lf>R v{] i9)*҅v84l{E29tسA|!{r< wO>tՙؼZWE‚a{!>A]BQ8 ۏ& E"aYLӴMADP@R02bf35b4ݠOPh i4MہP*r\ @?o΢~jP*"бH,׫]r>dXV}9E-58z@_,|I܀\qjP*<9" u  !XZ[>B!SZ5$,OK =INznP*i$[(7CT o3wCtՠBTDZ]B!p }R ďv& Ls6b][aBm8󎞠ӢPu9w~]'BP(A\$p,IHĆ4Qt7r9 >ARGufMWD\] ")lX,3r%悓N]=3\KPcvMܕpе77bO¡, ^]B: B$ED!˨BcY,B.i w{fzJC& b.w\IZA6!n0 ;oA݆eYj&4M{)e PPhX2{&lZ-OUƶ_iͿ!B'_OOAN~HRM$ǻ\9m;CB ;QۍyR(7ò&%MVĆ4>A҈w8p} 斦a,BBA\^=.8؃*' ;g x߯ל_a8? B %|kO~(jUŽN̺M/lRȂݠ'z'HD0-Ր\ ik"DEղ].Ƚܯ~I9sJ/s9? Ngo|0௜#~~p;c:2x^}Ԉl vlB_r, %6(f*gv7(N}.$D+PtL@B&R '\а{ !/D1{pTKSFh|ְcP\yr} ax}<$.hC !A81*(nS,˲;j\.jbfZHݐdf0Msm"ʄf=dBAa y<_i;ԠڝQY2dv~ؒyP-B;zTxS3xnGPvW5Q_-"!$5j8EL=7 B_P(jjZlA0(4ęen/ n7s!\ȁWa )8S M\7 1p64L=Mx/9$'0xf5r;dn4WWk('dR' T"Hv*7s3eVrh6v2ow>ABw3̫-UmC>GFg_L92@aI9g]{ؽSmF;FVE&"a{.@1# B]t[ DDq$onaOgR!B0T"H0bf4͙Q6Jh6˞!#@anEr5(Qns(4,)q$|b vCP3~8;2@+&2x*:%^xV釆G;)Nˡ0<)c9}:@=^\6ju&aruudzt4M4L`ݠBEۡCȼPΰy]PUiozh6H 7KJ !j2 G݋{~bGAV0'sTxt_PY/߅–@3x!҃my BH0*ulfP(0P.QTvSRg0a&'\I2ύ2LL}˲\!CaI9xs 2Ufho1uZ-*\ts'#HD 7z9vy 1 NO\~'͝cY,˚ȠMvzt3d 0PTP.Sˬ \ae暐l I*jPHu)2Q*P*(2@a9:~eu>:Zw$hT W4*!p)?R9.lZq[uq'XBi$J|'CyCs=)jdڇaʾѺ-P(2Rg=|yv:GCϴqT#?_{0u)po~W7,Dz+σ ?ROT929SXR{'-eo zݮY0*0e)!.".9*"6B @L&j^^ceeen9q2OJw%5K!3E?{'UuN˲MD"_bb5ƮI4F5Tz˲lMf-i{={s xG d2R<8& Cytyb] JF֔]\/Qo͈' 15/$}JH6~8p~ΏmN"RN[!BB(aB J~L&ZFlh rJf' Dbz4ՠR`2`X%W^!@C\}\j#}BOR3_W: =/^k8(%K h X:D:tq{WH\+8tfi`i'0t^֒@!~O'WNqbR$^8ϗ자»BOmfpil\3A IDAT~U&, 2!"4\Lr9Z-r-l7 bULRI\$Ǿ҄/rT*E8~b Hʄ8ș"`)Nr:{:FtK`>!gaYV#>ARb0ߧP#z(6!> pfRE5X,.qZOT'"`dZr}Jb3| 2:Dh tVh'\eQR]AQs+'IuW6XLfAވwbxx fY+1e"((g.B`Dk%.sJ] ˯mb Rn}a٦ߏG3:S\,ƒs ELk1\5tOߞH sgO˧ b ,"889:#+QPQ 8}Kk, ]XpKf oo@Ɏm@Qr͔OG3 kjP(1@W}ZZˈ_ ((2 .\0'4_ w2s˼u"./Ē#x$d0 hv5|8s[[mk!4P0hZТÞz"_,d7`l~ 1' 2 rܖ>D&A8Wʆ65 &<Ecڈ$ŭW й[DvQ 93}# rReB"@h ƅ:]2 4B!w\릴-o( +KݾXp;u&z#y+^(͇FEB&Q|D/bPJe[t]D݌ ɎIQͧb^F~E>]VQ)\UЛ,6<:ZpcYR``[0 86vZh0[U|}OV!ɩ]9H_Nd,_m$wpJvw_aEaP(:_qlmmhgGzR4͸kXv\*Ƒ XҷۑG+ J%t:i%4c~.rcRp$DvL8x ;OcǩB<]܊H:rhG1jhmmy58nS&L&4 j6Η)wJhn%ð|@MOێG"1: Z%* 5m:3[oѡYgᮗW/)i+s=zYΒT*h`4Xyuz:Fz[}. &}j+[ր{܆V,?/>]_+!j,jq8>:w_w Stࣛi2&@1oK畋-zv &;᫴p4ƙ7臩#R1o'E^Q׌Z)ɜ2d啃a]+]// b\t&x$X ?ɐ3 xO)h|J|3߾ClDzH `?3D)J[4p>k~N䡤qGd2( 0 C|f>ܒR;#[l?Ysx}qj`IX/.K rtcTCwo$esJ~E\>atK.cXTـW?ԾxUd( +WF\xEgnwC%o* :6"NO2볳k[#,\eP$mBoI" f<{n[<9+fW`fٻ2xY?yx[$fpRѫbt7!ɠhPF@Qpe +r"Է?P'EzOxf;7/!9kl:M~1X]HM+8x=JBNl.x#Hgkz?Q q#ޘ%Db#Drߕ{l?Ya"Qo0@y]+ی>O uFv_H}_d2j-b3eYL&4 FcVCpM@V1N[&LhX/ctşOV{dTot"q!X $fϼ2\n3q=0 h&Fn:eL&4Ҫ2kZ]\ycVtXJŵcRP]߂K Pn}nRtՠVa4At>`E?w{!gp SwE-kq6|~Z\8d돋}^dIK_#l[3$Jm?8C53 4Mwm'&F1a /Nh'OSdMpG,o`;o5, %( j2zt2 EAP@tjS&fOO3'HƐ\.o¸N?8`݁\wd Ls>8x2'P\ eי71 & ULN)Jj1 z9(ZEQ0L06S-o V[.l6CR h2EQS9(v=uj }X&8OMzm5 G5F3fW4A!/oT7{޿*SwoJR&<y*aPkBC^YCBá2״Ծ=%cdy{DC2Xk·lw繱W l2~0٠V%Gβ,1$BP@R`0bTΉQ|tTI'4r[K_+7HFerL9[Ã7 &eYf0, AE񉢨 6#(xTLAQ2a@>a @mީJl[G!{:K ƍnڨ4,a:fBj|}9x(Oj4 lbFLl7$CW3fk] @c+G("4}#Hvw~;#p,V|<6SyUe G3L&4irDp'D7dpUX,hkkEQ J"!BSDs/Ò."ڌ~Еnu !*.Y{'^R纩7˭'R4p6c&g>~/O=\FD4G5L&jD*'Z5UoqIжO m"BSIu3Ƥt8$u!F,U :?Z'hj68IctCOWSJDا) 0 l:e ]Ft8&tb/ԫY\9m'M=!5V\Ր_N绩7Ag4Ygmy`4댠QvR2YgL{SЪ7Bg0wLTBV7rwقVc;U^g4+,Ao*|Y!32jf9Ƽ1}X!uJ4L&T*eW(Lj%oj6mh^`^G[[Z[[҂fۏNh˲{= !!! Ehh(BBBRiDy3K#H-BhXO_OR%kb Qۇiƹ7)ԚWnpZq.ŵ(jDiMZ--h_[ER"@D`$D .: iH qp '/ry.Ԣ zf;FR ,HH%FbxxLsbTjWri-[ hRQ!(PȐ$ GzRFLj טw;uMm8t%5-E~i ZZZa0P  "6*"& 5.6TTN"qg3tܿ G! QZ݄6uAF C}c1&#I1'К-4rk]P:V7:m:& ̖Ap񑡈 F|T(cB1,- P{ߚiJ F@ `0(44rKjQXYVTշ`o 94Jj}J%@RaIs҅ aCEo1AVcיbHb^s0:Sx{sW-x3`lE@@IDh tΗlћfDXo kq^AFQxl3{nwU&cThTJux4Wp(u Nf f Z)ѸaD*ORt[? GDARGCK)Y&3VT7"!0b@4H8Gb㡋8U(p@q1 m4PV݄WԩrL3p Cm_xv1-q;>CP% ;•Jvz&M:r%"C9(~T*!PЊ~9.![F6uybaW6b\8[39e!f:5HFIR0}TO@T]:r&4X,}C\n0ؽME8Gp6 {N`V!KkӨHWnhE]]cKE5`Y!nEEӰT!ۛ8aMhl2* {ϕ{mKg,^H*"B/GًBBgt|$`R$iE^ NI Z)w:ա^azWi]'.Q)j7hJHbA۳J]_n;sЬ3Xy%+g#5!-ӇA%;rMm=nfVh8; pJbXgrJq&Z,e3G85gX[_Gai IDATrI-8؇F;&m<[g }X9 u[̼ |g;fQh(m‡?/t,pp,=OPb{/`/0k\:bD5M06t͊V7ᓍ IҶi^Cfn[3dvë_œ|Z| ΣT*m!Rt6Q)P76#ߓ3QэQ#,PR`a2fقȺ\ 1Xt`#o/?-T;@^I-n}S3w}&tIixoX:uㅙ&=j4\?~8؈@9XP[B2G"/$bBwhqj!g) PT5-jf^aaiUx|YIxPsӔ޵3*#A<"4U6a+} x/73EYx˥uxӝ8qQw)ʺm~<}xb#B{}+Q%‹Pu+ǜ$?T-4>txDI 'K;"<{\JAFQxfD[ΥFX R{PPф<4h"v_`/8`T^XddV%)^?jG`_yb1zl ]uMm8}dG9M.w}wc'9&܍,>xLTWkX%38|A^<[|/@jB _kM'@R߬w-3[y)\Z߿# Q wy{p2 MxR^?1GSx??@ċ8N^,$5 "ʣ ,;iSI_dX,Ip:'{K&TN9`#j[,д8,1 & Bx2!jF##% XWk%v MtU/}n?9]mռ1xhlc\,ml:x0sLiw=18%,VO *b 1a`R_0 zՈ xES`0X#hxE"Epδ2ALx|)Qr^hXO_w = G.aYu#0*#ѽ QHrLu?\~E{CQe#>}vxTtd3oҥI ^_\'-:q(ϠýƵҲz)5ڮyxňv'NhdMlPeh|A0,e솃ʧ;ݔqMXj| ]_o?׿#7pϫknSa" –>l21*, L8>\IJƨ 1ih_Ɏ axIw8|4 f >*?.坳x0~4 zGckba{PXٌW>-@EUa2؈-ǯokN"9"> -W3V+4ROb!6ES&4 I|cCI\)qYZ pILN)|"ҢUoªS׬oc'%(Z|qUdێ^jx#o(Rdp22|`[a2[Ãݦ6ZT58,vc·/\dGR[&`ӛc@5Ev<<QVR`tQfn 5Eu~w#WBSe:4Z  Vܦ??:Q[a+P#((AAAjh4?5 j5T*J% r^S^8[LFCR&|"4dHJދㆡ =sž7c֘n 0 ͱ ^L+=U ^UGr;O8%&?>V8 }%uĆ[OミKQ2 P؆'ѭҿ| kwuKRbtY3g9z(c]|3Ӷ*f4Ӹ\όx]F^x}gHkh*);Ag0`do EǂcYpb)Ksnißa/Ynixmu4Mi m_)éky3Q9Wi|[W/BZR4,DŽ !>ښo2]#66qdÁlђiÝjC!_OO/-ƀ8F>%&oYl**W+4 (mFym3W*mY|.>mEG Mŀh Bh=[tnhEfn9j?snBzE!%6I1H E\xTJ9( ˡŕȺRV|t%Ae} zwF2B( LAjB$BjrPQ[ (jFNQ hFL{L =S&h]dvvH/>q! ZEoDYM3rKjq&;[ZGkԆFD GJl8B5Ш_}?4P؆Ԣѥcϼ̼rC/uIQ,GªIѧO~Q.{(V6%6<{B0 6?gKp:Cdh Bw0i}xł_5o4d^l{|n2V Vf^g[NM.608Bpe~ 2,HۦÂI0dytq\*^Qیsmb2xNEDɃ0sL$LMc+/w;3'Nt1R&E٢f-\*<#ax83,xѨKm%HlO wbބ l?rPZ݄ai7.E f>*NΤaqX"8U5# -:8!*Ą"-.&zZuSUBAL Dh tˀD04]>"<ؿxwGnJ(3o%hљoxl4 KuJ#12=F|$&[xThx~NQGok'1j@F H]Gl@e]K;cŮ3(9 C&}B xzLs8![eP~F4mppf XEB'VLw͏~>zϮ \.BM`Yorq+fڏ(,=M8t7Jsso]c^VMn"(ƅ!5Z_{a`2@4~OCdB rY[RKy…\ݝ)QR,g%@ [:߼ aA>]A{b"K+o-23*#k_Kʘ4*%^>Vp'FO 'N9}٥cKEQq0cLi|3U=2'kƬGQ2 p1 eP2< [~2vpQ;Ds\K ݒ;7KwOoۨoA`\Eu,4/J, tdDz>yD_)wA*Aɒ=L -Vw%ـD!-ĐTqөK΅KR!Ǜ,³wߕۧbȞTϯ ,˂`i n; ?{{)2~p2^z`}ZR??/JмqT>}vD9gJfW2-|T?Ys)[O M}&d=dRY6=.PXр:^6!j| V/JDɞSm˦idU'|ʄ\.')> VݢVʑ'ǝ3/ OqF̘}̩RQ3ۭ5yon,x{eq̻eVH>1s\3Q V`DkQ R*ݳD[n%ryl1n:D26qt6( OyUr,cִ [ngJ6m8Blj pE0EFQxᅢ"|:Qqտ[Sgb/bƒ0kܵeE@0~q[V8A 5{2%헔kD1=7NeYRhz.(V{] Dh @O(rqHZڑ%KKT'ug>#z{~_ǎ9b8]۞ۣN/[`a"=G>k\zrrX,OĄagjhp} ,2S`bp={3S&Q |;๕P-ps`Q52D9 gFPtX0ap; fH:Iw;ŭ^;ԑiPћ<}ixNG4d:oe0Xˮ'#,@م\LLEAQF|R"ħ62\(]:3$dž#&B3v. ՠP(l^ RQ ~OkQ\و@-^x7Nl8c-~]Bd2(J0 ($ۊ#3>v~}*8.0 c+x=G3اLh42#<9_oR/2u"2}+9EUOi? ^ӫfzXχ}te'A$%F ^t4ĺP 4mF#,T*j1u*(P YgUʰdp݈\. x6&Ovz[D* !ق-/޿gSy(>k$VJ) M=$l2 ZSErmjTT}xLN) <(Wh4 )'%&j&{'1S"qn=/!B@h{r߅߿G}8aS1kFe"BG3Pe.=ck3>:RԽslL$ŒJOOƒ+ ;h4h Lf(=S W05!!A+S7NxZдkEBt\ҍ@o74 Iyg8@3 #3 p|Q㖩qߢ~o&uw >3E8,˞ШS sF⃟lw#[b‚R]aLkS~L\AB(2Ňbq[SVJ-⌎*EzנewQ=ZxG[M3/_AT`ϙc( ɱa+1H/7j}>jׯ}"O|X0{Ӎ-Vw{7"G < M083V -z# mEqu `0mװFĴQ1{l̟<5اL&BV@q'j+Db l>G/ᙻfīPf v,>`xhw 1Ӱ, d;sA &EUhX8! j):*D@&+hۤ>jDzyxYc=>< 3& oq8PN$QDtm(]f:gw' ٝ Up`i 2 u5 (Hq+8#$/Qh gc78kr2y^F%3aT(`'ق!b{xi &(q|8f}ޭDJԠ_ IDATX.ZtFiuO OCHiqp<Me+5`"܇1(L!Dh "#}Y8}cҍb-``LowK iV>)i \ #DF\t,k-QR⣽JQ_h^@zsW8_cBI q0 hiiAHHZm!q(m2,hdvEJ\8~4e۽.L$ycVOG4.IdZhW,afZfi ?=V)pӔA9&q(E3I2; A Qnq fѪŅ\M] &Zl;z]Hϋ b y]wb$J:s\,29 SP5,ݙoۨTPbX#.Onq(~"HOEQ(lSm½EymGݽ'mMqFTJu.brT* NyT 9N^YXpP_뉤} l?'$msѐGO}S:C('.*y*>A@>UHԂ )ZL1$u 8W%I[e-grZX '8hF^q ^8ug$GVpX@ӀLJ&AJ%2/#r%]@_k4C|t(& I1 0>eh4BR ?  L.w+Y{HLz-sH ͠U (jDAE= +PPQ*}WW)i$Dl6pkMdd5E NfDqe#PXـVZ7kZP R JB!YV) -!o}cހ^YNa? ]϶2ϼ@p%B֨'3P6%Yݡ7Zx.`Y'=F38LhZ2 $iR'4*}$m3}ĝf7g-0Ӟ[+j o񤠤jWZ˥\V&XhbI}8qV_R-XXz y%hj3xk~ >0 &`Ƈ?Aߌ^ˣ(l'\:/G XʫBdh >zvBÔ˥uj)1{G&[gCo4(6mۏtfPTN nET} "uDFŝZ7gJƒ-} ~u.\sXXbRvWB:NZ[t&hۄ6z[ihCUC+*QۤT8dxrt w٣;_VSJ1n$?_BCmw[ d1|#:wʊ( 8SG>a0L+p2 m*?˽ևa+͌vUoWaidY 6cƠMhNhi19֦:Z2{Mhi1)]@W7?2,폭8%8{)h!z3 (Q,2JX2Qgh@* ͙2&PUFxD^Y3p]w<7ܼrȒiGfa }%obD6w8Y.$EUُo|j؜L%Es'CUd598QU0&6H<[x͞,q͏-{{}?k8n8b$OaԐftuv ɠ) # ʼn&bkq#ӗI)US._f0 L&qnp]7?e4K&  QTt,ly͝:&}lǁ?,*Q1C1㈑5yFD}m= )*$ u7_liL5'Gœ)c0u9RqE]y N 7Sf WG!<ܚ:?q/޲,φIs ցG^X N>|5 q䑡o7&A%u]r|H?U QWTɌ56ﳭ݊G|d]U߶=q7o1b!4z0&c3|{RIE\-e ˳??z6͠ &1C0a L5GLg|BUUYqV™/WRχmoϏ̤t|q͹ 0udF``Ҝq1[wMz+_z3/P-%#N'>FciA%8vnj1S`Z}fulQ}n߸[wUE1%yG1GI#0yP>zSHl(ls. $Wㇷ1`-{J޶ s0%dl|.{#hEO2gO|9Ÿ"C'V8vj3契t:]I n%ad1@m}w>m4J]S񃛞n,}q=J1|e}W`E_y*X0T?j\"5[d4l6 U dY_;w>0v ,M`F~D )zhwe [ ,~z2^ߓwG[<7q(k mR nm'O=OƗ^銷nZ po۶a=qYhW#ALtwwC4Ns˒o?EVen6,4yPL {hjo몲=mq@Qrb{9 5!J*1b8`$IW=~yK5 ]fLynP.X>pD>E= q/n4-d2Mt/TԛAh҉.Z4 M` n+$ѕga;n{$"sܴ('(Hfpӫ?9}H߮Ds |4^AA xhdOG<8yvWEQ2:hϝشc_(~u=vu@$\~zeIT*v n`Dc_Jzs{xp9u|ث,[K_NA5d 3\{wy'L۞D'le2V}m߷T̅5kͤSUV>e"tf.[!pY=c\a.FNxBFi^!86'[^h[K,1F .qxخ4%2?n{4Hs{}^W.pΥ+apI'?>b:fZ_8Sd_fq͞mݾo[Y_b'=:}o IҒO!V/q𔙘>qxk:ׄ%{QdYVA:랒azVm蹯`ɇݧߗ$I]Y{tWMqřs񳻖Ɩ]m;D 9 < dBUUD>n*ԯn{a~ftfв#\8;w/+kFtD?٪oo;oE(W'|e5EASS^$aQc#QjWAzN-S:]4y'8Nd[]Y_ ֯3'GK{=M 3s#^,4!zԃMO`Dc_zbf<ƶHڄ^8^X.Ĥ~Yò۞aQ4k^ۛlq'u+*Wm?㛻 Y.}--}y]$aܞookuòq3=#D/_<;@fpn S&$"jஷ@W4S&X2Ѹh {`9=m7cd̔k* 7ݵ/£)wwƻ_%^swGwhƯ*yGbJ$ ޑ^lL(c@.~$l#.3l ~\FoϽ}6Wd}yeV\oMkzCuUپ;]Fog52nuIqdtFvJO|, @W}(G\֞S`nۯϙY`,}iUwPe4їzn, l U? MG=;}|Sʺ5pї'^k1>ky v}cҝ*udߴc-ض@Y[co[Q'wuV(-5G]nsO`&%2( dS&`C|~|KծԢ; ޓQ< lNcQ_įy.cq]?~soV=m^|Bd4왲Tj!y̞Ϩ@[Ge ^"VwX>8+!vs'3ocu囗]`OoRYd6rlΗL0h i;Dd9Һ_| ;j\&n\ }F\| bgw>||GV A9mp+0aЏO8hJvn\zq7B酰ygc8O_O :De>_wc[YTeg_#ΐ2V#DVhM4H,kϛ '{g+ "XbvŁ}w>0q9abE$IUg,[䦻\7mٱ\.]+현͐fJJd ϏMZ@Cy~uwnêuOl~b~bFSNƼic0d@smlNjons 7U4b+IiX4ܼ#Mi 2?jutK}~v2|y8=tڎwUKo76޼wckq1B.3%<[X:\t,\pt7sﶎ,^yk y[6?w#M; *usSO` .N$dэ=.Y| ~"X* 4Ԓ 1~` jI9%'gZߙEgww]mؾ@h}lo}ќɸ!,]KW@{?oL5S ÄQ1%q`X6utclݽomڅlѓGCq߲վnf`Ƅș66ڇmM߸c|E(#}',kaٸhJ8jL? 69a@s:]q>l*#@+Kw㆛ k6=Z8 [2{ mÄQOxI! oa@w@0]HꚊk_ 6l_O]|B/rGh <䉗?eCe c܈A[X4pѢWUOE&~ŷq+Gv e0 ޫ WPU5vH@Uޒ-+Ii?sn\,Ԕnf^X)m1~ _ч%N5Fօ}ء9Õ6ybǴ yfOJ$GDDA}So!kו3-xe$Ieg4=dGy0Se@ C[_oݎcO$f7|􂅸Wܸd1s+my ˆ$I'ΉtjC=XJwp3qřsw.)8Мd"m455ϗr݊eԊdBulφי5WVu,$I~457p P_IU)rb+1^3~J n`Zi3j}E|yTZ2>X,ߋC޲t`u]dRtOIiۛ}4Ӑ3,ǯn|LoKxq%3zJ`f}x(FRL T(C˲f!2tY /1~*]}32'p`Sų6s'G洎~|8iA=W2G jU%ք)_|r\uμfO]#JQ>aز"uqU87o=DV@óoE_mQSZO% z9o>ww77'^8;b`>6o6"ä\li6,,B: *v3_-xh .SeFT2F n/z9 g֢ 0}+"3DW|S?_e#b !`S㏪afL8eN㏎vp=Nnm=F m/6-g.;Qߎ^˲/],G}k~zô ñN~yTWp>)W,قXd3hWAҲ K&RK&(>+د|-~u墩od$sᱟ>|еx_% .~ c0rHe gN߾cG ,}̞2K(>}It誂+>fWR/~4ݏ`ءų6*CD$I2) ~3yHş| ~=aO[Gmy3 ioߎ\|i]SO>7^g?-w/o~"1AUd|재}"˸tH',P(`C5K&$Ib+\{=\y6ȁp h>r>vq;Om*D%dI'g/;G$K$2w2<}i7ܴ?j'2v@|撓p@G/8w-} ڻr\('Ϟ߰eg[U2.8iS5 q]˪r\I( t]imܙFb6vlSy/لnmOgKYpڼX8s<&{ێ`öx-xn&<:w)K.8i&p"6{__f5<yyls 4t |1'+Nƪu۰lՆHȲ M۶i@N= =:^|cKYe*G v%|{+ S^}5L;#`4gRP,!gX8Й}~vlӉ`5\| ]9~~ycݖ=p]vǶe#2g/<*?380Me%p6C0`턉% (gëCe48 Ӳ}-2O\t<^X /^U4MUp⬉icT{Id?Ï>3mp˗a kƝmS$L? ?2 S<n8J*8s& Ew1j3n)󊔮Uha>_-/&̛6F5d@E:3[}T%\s቗/CGweZR8n\p pZ}pQd-O^7mp\hq3:8}KJ-X0ox,^0k6}ϮƣϯŶab͆XOulXcPtHДq¬ 88kԊ2g2xc.flݻ]ٞϖLJOΞZ2XbZ8 &lj4IB] Iuw^~knލ[bNtteqy#>[RЬcXk6㘩0gxshJ}]޹?3؎_p,B߇8FGGZ[[d"$T8Յ&4551> @?_0hL_317>|3r{|1l`ru[iزk?m݃{љ3sr"@uhJ> GC"w$  DT)qىn"m@7yF{{;Xcʍw-} '}a#pSFSJ[\.v֚(:a,i0w=6 C&SaÎ4( ұ;?>MUpĘ!kx$IPUq7 Z/.~5ѓGEd7C).lOd2, ߘ@l %'O|rSO'*,LD%A¡tq Q|1²,_c.BT$Ȳԛl7hFZڶ\.UU6}'QAnfa4t]9,@Cۊ X=&מ+[^Q?"""j<@4@|BDIJ 4ܹ'!MJra,Rl(ww]i"B4Nli z`U(Y0u$-+W^>K'Q1==_`xD6m۰:'kuH"۶ 0P(od2ehh*'\{;eN($:bV( dYΏbWEل(qWg k^з/LӬI6C(D\.T*Œ *  "l!i_nlh ""$'Lӌuf@_̲D9!-8aз_r0%"N1RYv/e[W>)Pr ]_j {DDD MO=^?)nڍPB^ -PQ28K&" 44?>f {Yp͙t٣Af%,qI QmupҞj$Sg}͠( t]J\ׅa,PAn~|M(:mx{]F(&"""J"oVC> I:{9=~9(틒 LqUN,twwCQLPsw/{gCVa69]ֶ%""z &8$|Kd4Kek psB߾eYp?Hv8fmLf4~0PP3rP b%we\O-H1u]8o4M͐Exc%lT T*Q≁:jnٴ7m}Pß.ۺߺ 1IY+I/ї_]v:wҺmF.KT6ClfϢ:vSoI>xQE@ |zS}(LgweR i?=H>zn_\NH߶/.J&,B:f:3mܳ|](Z4k&p- |"""zQR,+P 0yG=[{2x>o> huPH2n>wŢP_8i" DɄ,HӱAɢ(w<NĐ4Ξ?/oH@DDDuB ]2,q jIwww ;ᦥXt}bp6m#U68rLDSSS=>==Xzk(iӡkţ{ވ> \EHY z qnd֑wƒD[GA-cڄϛTw bʄi,H0Px-8!}]q>VhK]Y*KNSvwDݴãoXڻxo< gNw|L58cdYe!9d2PU. )||Vա>NYaؾ߸w9""'D\ A|sR5qnY OC-tv6)]i8D6ad2k=S4c  ufݶ6ʶ.9yj?UAzDDDT_D-sg^op$J͙4< *Z2:nđ1y,>T dY5I$ @ww7TUE:fE:soH ${;h4/.PvcJKDDDq#'"*A9l3&]PPXV> q’ CXuB)b~oq灲9>K1~_E,+.۶aFlBI]\.]Y2Aclرon~/m}Dلv {4Qlċ# mض]vIq-.,B.C**y?I+4:Y2AgXwBNJSp΂IfǾNd kMSjyʔ:I'D/,C4YQ)B5DCr0I $3q˲Ni(r\Ց^vY0 ql"""SeY,ܫ ,lوD6au=pDD6HRf೬NמKNl`BA>Z1DDDDaB:Ae(qوA.qnmlNk:U  ubmpBxck8uָ{wg Mh "":&O> a-> 6Hf4x'MRq 6aHlIU@Cx-l猹k\7TP:15]}A"/ҧ(D WfۛZE$BUULPV''pɾnWIa؀L%""";oFCTQ]nlUUC*8,A& sR 4ԁͻ+5)蚂g.ș6V~F j*DD/QD$INr3; b"^6Cjg7 1%T 4ԁg^'F% xs{0?h E[>0ˆ ag3_ Q2NY2A5g]xlg1c}nͻM@9122> T@$)?Q f(uvf,*0P^۰;6{ۭp@DhΈuݪ-+)47A/ȣ 0 Kάo(Ԅ튷*ˠ`;o˲x/g R4B nmlN+IT n=pBx:v(LjbAݟ(ط&24MeY5 6 8{A10 CӴ|PVS)Q2a&4@R1АpmA?e۽Ί$ahk%E4d( !E6uו$ @d ?Âi|UM7§տZi8,EBZ!# IDAT\.MӠia0džQN)xneYضL&UU#:B"KV YpġnVc "6qrW_"ry4,ZZ<˲ EQ:fHR1HBvr,`!\ظF6#|ֆM+Ա+?Q(]a&lZ/~VV契b n,H,31vBδ+Q|ߩx_SQ=!n Eu-!Q ֛8f7l˲N}Nۼ@(7^}M@C~^}($I(}i[v,ːe.' =ڊ=G\ׅif4 )vlL-++cZۼnol%L "= ޮQ> ,;!. :y›͐J*jXoɄd23{7 z`e9 CDQEi4z 4I"Rn0 adb۲#iW;\5'Y}Q/Y^ {3S6їZ,-Bww7E)99VhH=COa;̝2m%M> *BZ0&V4m(1А`F(y}n8}|{- eOpFC5XcGDDTdYiڙqi4M$P 㠻$!Ndb6 ֑ ')t,\X,7ܶ]93O;[ӡl(iEme[ ,CQďf3d2H Ijpu]r9&΋Sk 4$XάI|WOQSgdDD "$IPU5Pb5E)]:QlB^%dˤ'Z4"""j\a>2 2dY8(RKl1V2N$% ѓ&I8~ZQ/MC>mxq?|K 1iBӴ|D-K\ׅarHR,`!ZZ/ "j$Lč( 4M_iCg}!r"'O84aYV| """i">Ⱦ7E;V=߁eYf$ .\'~1I1蚂| qř4ȲF_^mYL& ;8$7XlJ:Ę-sgCS+[.MtN`QI"Ϳ !Yy8r]a *4Q؆f`H'|בEW_<f6 hNwr>Y)DDDD~m5PLYDLӌ]0 J hdIT+ 4ԙsc/„Uߜo~fh(2T ϢlBe@7!JU=s6z@C:j<𑳏ћUkFu:w}b1j`$ "%Q q 4" J'tqf(w-`HGͩS-ɸ/U€&W>y\ imএ.BEDU!U(Pif\%Adt:]l/l( 22,B&aH; 4ԹySFr>Vo܃[x؀mCS0}wd\|4D$o$"DDDi4 a ۶8N,˱ Јl~f&,TUeHKV< zT.Qlrna6n{{HԌ0}̙<󧎬88'|-I=[C+"DTu8.Lˆe;pҔ$@Ud Ez$HnttttӉjHh8)7PA.eYHe?Q1@TJ>,ACDDDB\ @媕xTP^"HiZIDv""""11YjU2D6ae3Vvp%5*jLQ-%ITUJ q d32 @ d@50?<$Slۆm۵>D6%UۛAӴ&5D`6,$5,>뉈jL4Z (DفUmfAd3gI""jHduOɲܫbߓDmۑ뺰,eae7X\.UUJ@ |""""e`(P%f(TIAm 1@DDDDbeY4) J<lj$ 2t]ioߏrDɄ8v6F@Q -F|1qQ ž4T@fT*Uc'5BWω(>p2ODG\INv(T('OI*;c.rLD*b6h """" oCȾصnOu"=EaH* Y"Fȏb !R%ff4ߑq 6$Qq 4QCa؏LeP*uWEFCYAxd2p]W D@DDDDbnl?}Ew JJe3uvlIT""jXޫbnFDb^l!'P2J'Jǁ3j YqDE0@Tbnn"2i;!J&c!t]P4f!IK&+(FEi> :ۧ4TlۆaH DacU@&ض¹ a X2.,˂aPU D}ૂCc(ODT#}5Jwe9 "+ FQe7ضgN9Β 4H !Kݧ?a.#G& 1{Y$Qh "%'D"c> T+۰2D68U c,+2Nd|uŌ(PUjd7>~;PJ%Y4, t J`(fDFC9B $8Lӌ7C)1@DD iDO4,s_l˲zճ 9n1RGa*ԑp@~vCj}7&DI@Q ɲ MB 4ae7x OL,+ bY4 T `(#.SivOF7! v8D6C6,˱  DDDDD1$N(o)հ0!c6 m$Qh """"!5 IREc.?}ψˤd3x٫$YW 5jivb273 )Y&!(h """")2~nk,EQ`Fdc%  DDͿ$ʆlpeAQȲ|}˲:Eݶm, _9D~K$(RBT<4Mgy͠(J"2D*@QLyB" $!I4M;ʿX')ATT Dે(2D7K%( !ѧ4a4M+{A ȲuM+2 4Q KD/,CU՚Y_ * J&4Mc6Q """""12@Je3$a҄Dq@Qm׼K4xf(l@Ⱦru=eDI@QIYful\.+8\.IX2A"b.Y At]P1^I( J bNQ|Clb%qc&YE&""jh7N'DDjT6(A*V48Kh0@DDLY"ZNx5x, P26Axp%Qt"""""Z6*@Ӵ '7KTA- ",@Q}h~؇#h,+Xos]בN[ 6Tx8Β(: 4%,ːeeU!m0M@qd2>{%A8KhEDDDD@Ӵ6͐iZ&jqD@Q܂DDDD}q qfm;$ҏZd7p%Q"""""J} Je3Pu8ΒJ*~a.Q5>݃ z(w[Q4ĉx_.H(NDVeYp'lL&Y{/ e9VT J&rTU7" DDDDD "2TU !lH/o6C-*np]i@2#""""J(J'Mv6C!? gIT{ 4%H !-^u6CqD@5,o(E%̬$T*uXB\ n8Kx+(O:(޼q* 6x'MfHovY DDDDD miv)])$I,,b>"""" t;il$ MbxYe\"D"D RNJ⋯N"J0B1eA i4͢?͠:@ DD=xRJDR$PPlq-Esq8Β(&h """"J†|  Ųhi@ DDDDD _C Ų {/ض\.EQbxC墏jD""j\ e,? &hp,b""jHޓou%s艈MR ޯ-fPU5֍9Β(j$""""J(c.Ev7_6eYf,8Kb(5XkMӊf3x{1p%Q1@DDDDP!mۇ5,$z3l,b?^\8KbQXMDDD"BZUόVPUl\.YcXQvp%Q1(6 g( @_D""" <ߘKw6C&g`1@DDDDh> BSoȒ DW'5 _GDDBf0 $m8K߻Q4coqjDDq 4+o6h(~AqgI 4+|Qɲ ufnqD@QIo 8NO7XA{oW+lM(!h "u'Dl hdoZe7d6Q20@DDDDp<8,˂eY}f l,(h """"JŒ rQhDD DUU$JZꀷGId Dl_DDDDD miYl.PJᔋlMӘ@@|Y!2,r\ n@% D!/QLDDD%oA墓& EgCT* &} D|Q'ƵFDY*EA:>,[T6CXE*yu]a@0(^h """"'Ckk+2gΣ$;%BZX FHl6,,$y`lyDZ=^31`Kb5 !daIֆ$D/]Gčʬ"2#RwWeFތʌfh7l f30#h:1DHDDDD4#l=О+S@K1`͠F^gm)@ьBtI1k6Cmz3<σyf b\:ADDDD4l6Ci݃A&`!PT@4h ""E3"al[,rPEnBlJf3M7htrKD4˲"`if} D3""""e;MhK Zu0h60@DDDD4fhǶRh6R52h:LDD]=ɫhD4;fh7JvC&f  D}?\(/Ef3f6"yf !|7e1GD3X,2 4͖@f$EX6뺨T*f 1|G͐lmu'.r[! Cu<:"* DDDDD3"[`q :=R AH~Ɩfȳ6C/l6m|68k22x^D4;h " ^DDyw6C_Xio6͐ .XL4;h """"*9if3L`1&ͶRRͰ^7 " |wX6[f3LrA{v_E]-l6CD^h@Ql[,tpTw%quIDӍ""""2Ơlf(Khf}l #@QI.ę Auu!X7/ Dor<Fz70}1(OiSsӯ.?'"$c @,ǧl툹98flZk4 AE&c Z.BDaFQ MK6y=k3!PVf3禵.M @@:' lj3DQf@lFF?,h #/ ""rI(Eцf 4f4kfkJ6CV؍"*d 4Ff 5,K4h " Jw" Z6zA.  4D6G6CܔA@Q l0 QT8N f3t^K'f DDDDD%0- \j3ؠ@фi&֎tfW6C'D4n&""""0Ԛl2]ٷyf3dI)!,Up@mX6EL'Dtfp| a A`eeeltn DDDDDB]k3L: fiBJ&Xҏl"~kÑDDDHLgDD`(B^_wn҂l6C^PZQ{w'=*5! 3&vr<n#f6CF N:& @q1ð jul.  4M}y\B NEg3lahfB p[6Rh4Ff @m`O"n d3!rfw]6ht 4Q6W+Id7d;ML"Ak@єch&e3Wv1& 4h4 4M?zbΔ]"*+PT 2Xn uh6t脁@јS"RJxnmje3خZ\GD@f&`ݐ>. Nh ""J5 j34͞ DӍK'Ff}l6R Zc7 5 @Eشi@Y.6<^-߫{G.xX4>m׾C 7 tf#RPu<; bC9Lꁁ""ڐxb @Z-} :f3 9uc`.} ws_{yiCom\g+AwI|?Jv[kNx'o=q7?ݏwcu6|GݟNބz꺷7?|g<6  DDDDD# RJT*RֈJ5]$pСCB`~~> 6>h.{V[Nm. O-Aņnl;89y]xpO#rN} VBZ37\)kÎ9W W ox h " p'DTn6lx,f3Z.:X^^1 pRʁ @cFf_yV|N Gw_y -{}]o{WQ$.>ah~->ݳ|~]. 88eG[|5YGTkہ_ё {4hÒB @䄸"*(p$l(077׳1&m}h4 56mu ( #m3?ۋ '*xͯ8=5>hć>w'ӥݻOy[q}d.;hσ#-)X 5"".߳ƓcWz.\lwe 2,jۣBw%*DsP$\Gsd|ulBٟ?f2 ,h1R4077yaEAl4+}KXB\o+p+K_p~\9swpWv~ܓ0_qm-ë~TN|r}g/~-0;ml#\o=qޛn7_8dovι_88e3zzxG߰lQUpQ{Y۱Pqp48Y מ#1mPP-/@F9(Ѩif "Hk=Zƀ ") ;| )GQ>:e3-'!{55̆-[50ȱu/>6ZgaA_z4#K/: a;X7}~|v~\qmXdνx [+ -/%x`##.>a$g6xM8ak%1a~""ʕJiJTh40F" C" & .iF jmlFS+OD& ٟ%0H6C;Z-[8`1Ⳏ_x6Аޅ=*GZ]8/'5Oo6ÕgnGߴ#o\?X->3p*>}~obp%moyՎ9j۫ / 8q0xi^DDDTY&GJA[~q2(Շ$Rl!'&ߤ-J6(zf0NDQ0 Qf햝8*lقE,..Bk1}v>{;)%(J8w>4"ӎpɅO?u<^rL"7|>xW=A\y6|$ 2`wɫOFs_a_ tOOa\ R"Vv̹^--alpKKKX^^FEkn-2W1W:LJfs3¾+- NH|:n+#|sNWgmGs^g+.{Ve|m{l({~Gu~,'m'v_zaxp`4.܅ x-?oXۺøe/9Ad'qO:;؀C֫O߂]6@{gކ^7?6Y?v/q ^;nyy;~{Ph "B 8RsN|umI 6.+(\8n\q3DJ*i!GDff3t/0bZkav7hu?)%*Jl8|01_lVIB'Bf!r Oy_1Z~vmርs}.ed ˟[7  % _?yH<[^hxe_R”s/~m̭O9v8P\}G]pNuWƞxx_0ؓ0@DDWxg5 $c $X38Hdla4f7l PTgR}l޼21XXX8kvAnvD6p#B}mZ 8;IA=ח>pɱ?xg2XQu%~秎Ɵ_~l^C]ǎM϶.{9jW{{_:yچhpř?},x]%480IZ Cm/_y)")lLHXhFf购 Zk4M8aXRJ!Z vá&Hs[hh-q$.yơ8o<K.<5g:~ɣ<'33!'{IT\'__y"J�vB^da-.{imw k1r3(8s.>a~mQg 4Qh&N"lH DG6CVK)1i\/a~~B>|J),,,R (ijK8騭[? vuM`zŧ|G3/< on9qpCGޏ~zPe}~r:m3^6qD'#wM1~Pq~养pU*OߓOqĪ+7N"O!ʴ|Ip ȟ}홞A;W \sΡ29.8&.(HۡޒhUH%GLسlʹ9,,, ",.."(Z7߃4^ 򟿉5w^o]+`SsBnm5n!=K|{'z8yr-?}4Fz c_Yx?w2.9}c?:n3S ?j/@.L4 m(%7 ~ sܚd' _C7OTTT]ي_UbCK-ދo>qcqYp1sPx~w9oq>/>Zkݧ lq1s3nNk}EAe\qf\cٷn3l&|顃knwX;zm'-]/; UW⡽ ?ip#""""# Kqaj7Nyf<.8z)%8`*|$^~n?,k.>g ,.Ʒ{ _SxE\g/?ի^c'7~\zeJ\{ΖB#x w!TcM0p;eK*َO_{ .59s{ngcK{Da*+O6x8u>ݸo]OJcJF" IDATV mO,GkX۳ W"|0??z yJ)A(:\M~%hqqGJ_붻ï෮WtOn_O=e;󾃕H,3Ȓ{+ZA(: 3h@zED4la Ʋ1f3d'E=ְlfKKKXZZsssp]#qq{U/Cs矚s(Z4~h0@DD3ŞjaL| 6@0(?:n fjHbEաRRÇēf9I' +@<%"j-J !=CIA sC1>$.aUaR 6BkkvĸRؿ? |;+/į]y8HD`r#DRK@&~?u : JiDZdp4N`BHo.:Fd&裦nk3b=@:Iq6Q'dM-[\QJrId7(|߇yf N2oBTUj(M'(WI7Z'xlIj-6 RW]LN& 4Hm$/^'{SBR{+AqaDj^Ŀ * 3}gF#m]W*ҁ8L)X޽~UcY#t, vb# -lj'v@}v{>C?!|IWx}À7乵o.hȤ ]vc㞰xr:=7s3pρLy2n3>HR$imAh 8@!3WlǡTW0PA1k#XǞ%1GA#]6h4`nnJֱ}~a=J),/QZ~>H6a2NJ@^y0c&{Ǿ)BmC9G2R"}e!y>?hzG =8hqDEt 2 tk){?z@䙶mrT_vXֹgN/u=h}z[1bF};63įK!@W_5mk7^с!!|ρ ,&f RH L :zd)=J 0QoWKHA]{&ku7Kpu4 ^3b9Wn[EX 0BBC$^A>.|uV%p~LdkFRZvz.V<@t?>C8{ @i \i@[\O_k3#NG 4}[Cf ab۽kf P@r hJx/|ÞAFDŰf !ml qy^C"qbڥQR5$lIDe@C`՟ZK'9gܾ-K'}Ntbu?^:mŚu6O8V3"D?a3NtBHE(Nu txm*9lwy@gGHxiрͥ?%#B6\Y}]1.cK҉VeX:0*^ƒnϵ cLBԪ>/v!ylɒ3GuZZ 1QV P ‘2.k )*ϸdpt[ӴtBtk#0d0} t"^O60Ra4P52I*lya^m,|&K'ubNoteHu}iM9Nx  e+i,JmLL /Tf9DOm,/ÝwԘNdY Ade /v\P104j U:/]|KHT C8Byp!B|AZmi]iu d h8R@ G xn~'E0r!c]gYōW[V1;f)baFq7Ɩ,v;ǷBY@øb2">ȗ6a!Q)t'eLLaqaHG @_rl1 l2;&.ebAd4PPmi ((DP рҕ=e D$h l6QVPV8NH&8蘬]*n$l[70x#]6r})Z%Zi,fiPoZ.(. RVP$mܛHK8X[ޙ=n2}/B5(; 0x(!r!,?s֩u飶(*:-|ѦL0>OA*rsb4v5nMT,/D®[3T7`K@Ie4B$-4tHBCfR fb'ClȼkzlYf0Ƥ+O4aoEGg+ܵ'aCDe`&jZiMvĘDAZz>`L.lGg= g  DD4ZN:R㖔i!Ȥ`#pD3 Dm!G)ǁ @l0q8 ewi;P$A NlhW˚f4TձC uN} z\Osd)@QAaIc43hc!0ifABBHi$ o/}|%-~) #l爸&NBh׀0@ @@A!$0)8Nih6p]к@8~v,YѰjSEkN9ϩMz(D}aJ +cɽNe 3 H>H$H)"NpɬI'V u4F&naR(>60IyhH{,.xFD a~DfR?\6jSWsb DDTadSi0Rivy)$0 g$4 N!9N\OA5K'iK^QKH(@NZ! \mq9<#FԾll cLtac e 2P%>q8hh0@DDEӿ#cGN$xy! :-h֔k4I!qfNVq8n{+B6lbMLrq|ǔ2i?x\|ܤB40(_B$:/^k'@g`72nіy`BfCt4H3W< I0#F4-&&:\wfc%&|?[I|M'Oz(DCarӲAiQVseR6XtG|"LW Ud#D'cy>#65tAl\G섾]3 '1@Ӌ""ʑHEkDJ#rwidW"m@dm3l )mÈOq,ߏDICYM،  Zm-ԧus>U.Lz(D#a%B) /Gږ]%:_7IHlB MNSq܏DEEh6< 6h3zc&rs#r_w,.9A~h "ٚkƓkYaV3$r\71ڏDvȶRq]ɎƙݰѲd8W< DD4seNRR&'6+Fx'^' AAQ紓7eC!  DD43DP בp'40hCʾl" 0h@f#N(^z4:A-'#*"0Ú(G&Y.8qIL!I"e ֺA5(8+i ݊A6厁"")B--B`̴ >(#ċ@-G vUaB( 2l ""9-]*t ٓmY@]68Ni y8k[n}o0 @M=l>(D(ҌI/KLGϧlIۛ~N% 2@M eڥB83@CpfQj`h%p~0fDDDDLC[2-Fom9A?k x᫞3a DDDD4:,[{Dl_A>*z8Z33k݊?b6ɇSA@om$"=y|vZ;G=gOMh}`'ϓ/(x:e3cş g5nß]v|n剁""*6f.!DKedI!^!40(IԓP;f 3R?>Պ(zu62 ?wVˎާ@4rO~ L&0Z@ifB#'v.!4n  ѐϙh줹WIf7DQ IDAT0 QKQC.5Y($&랷hcc(/sۇJ"8)DC -"DԞ0+m*0Zm!`U%.i~fefV3,jێ;a[MЕe68(war5 Dh {hH 0|AHN H#&h t_#*R f3]Џqf7h!)Ua 0i]:qw?A"q$# _kH>%V2AGg =~)M ee[ixy돃Ls 3(GqGǑp1H'8"8ZW!s~?)\W@$!5*xD fߞ-Ow {g[oӰ]ƕ0M~r>PqkDEbrcB8Rk)t,-): C7MB4 ` Ff1 ϟhd'޻u7hWT-2Z AaQqdW; Q0@DDJ*Kg5I_ϴb#JC+sR2 eD3.Ua<'v|eY6.p]wwvèRd j@*b_E C7 a PAJBiFfQO4kznslAEj 4(PTr 䵿li 4Z 4Q!zhL|2 ܤB~.'Ǖ`RTWKS^ <""@M9֙<%ZY701j)Sa01+CL"DD'ٓpQy@C/K( f(c}a36@ufFcfsIsQ a8rw<ٚEa Ef\x0@DDzml}Wn#tu^k0 s8ѯA46@mL߉Z˟gv-Y &2~ p hdk Fv{e~\Ka |W#QKlN>B72wf_9|DDDDD}WAg'J#Ƥ'1J+˲Nr2eq#""""Cֺt0,Uݤ2U|԰q&~uS(J3aJ)xWlյ MGDDDDԧ2hnZhi` iyDԿrf@?$""^D1:]61 "8h """PI|;;2gN˞^_WnRu$h0|7b:{DT6ZkDQԲ 2j_6Qf+ Df""""*=I2!-[aDQ0 TgDh |7TV5VV٥JdVw 281`$yprc2t h.l%"">$# LFlZOda85&l}$*\ϊyG󴌈vQwB:[g`R0뺥fh *\Mp " _mt.tq*Rcl6yf Q#yGwzP2@/=QYLFǙ` Zkhf#IdK4:>x{`Z/D~Xp}6d plFeWKNxD49ZC6B:ra/zr&fy~򃕈h}vR:ꄴɿ 4!ƺ|Bkf3)kO[<. (BH5^N5lC( """"%rq c &*J"e 6 0@4a7#3G`@QNhn)DDDDD)|Qu" C(r~;&kڕ) JC| HyWOd3ԛ{wݨ!6:M8ŊZǟ[J u*|A T*x&lKa1@4a{5\s/ CFѠ+++DDDĀQe~Ju]4²l nZ}4l&hvWɼݗM lk2Dze]ƽ{]=""<%* AXypll2WJ!DQ6 AIl AMڻw^W;X{FP>o'z\駟;wF !jBr0'=M@|^m Hבp#ErGv'vT(Rǁ0 ӥyf3>|Vq֞wk]t ^ $T*}\1Z/aȾ}>~^|j{a$8~9m_ٟנI4mzQh#VEio| MbiI7h4|Ѵ3?tr첉N΋ d/{89y!*Wa0;5RK4i)@?C>Pk]j)K)Vʳ9A [@X#Lhc0 0_ `dKdd^8?Ɔmawpzw)g{wm˾Uuw o/6]}mkV `>BTŸIENDB`gammaray-2.11.3/ui/resources/gammaray/ui/light/pixmaps/splashscreen@3x.png000066400000000000000000004161771413310443000266120ustar00rootroot00000000000000PNG  IHDR'-msBIT|d pHYs,K,K=tEXtSoftwarewww.inkscape.org< IDATxwV}>Ϯ^M3c Gp/$ro /M^PB1-Bۺk{޾4?9#H#ikh3Ҍ4:_}W ʺ<"SY7 JVLVn  Z 1(ibe 8tTA;0AvcK?*`E<7P4aF4-) !gމI?*CŠy?6`.,K"ק|%n31# d-fDп[k-9x<I'Lq*`IKP O$n<,PWRRt- X:͟ V'",8ϠH,AdOHZȾc~-=AY&0v?("fN LA7wF~?0;[}߶:ݧ]Y߻ݑ Gz}fƄ^?,ԇx'84RP`+~?vj_Z]aY֒1`)§,+i0)J)t:}vi4RVeҥRVT*eݼT9#VKo.v[ժo,۶x~vG?'PgTӛ5ϔ(}*H-88a?7h_ l8#fSo.ܜK\>8#FCض-z]RiӦK/~CZ#mIMDRy[_~ ILdK/Ȣ@謉i4RTdɒ%Rզ>kB@(DDdnnN*gvw|Of¼ A1QNP- EYDk֬;Lzϛqv[DDժC~X%rYj5i6jĶm?˲~?^BATmhwD_?SYDJry'%7>asRJlۖv-mKRqLRpBDT*IRz.jn8f_um&c Là:>7el/ Z|< ۛ޿ܨwrY|jv-fSDD Nh;#NGDDX{+tWnpMԆe 3Q_˖-p̶@ opWg"0(?mȜS5B8jM$fSD)%z]Zz6n˗TD:XwBTB˸[),ͤ?߲epPkCRלb/2&jM'juiZ}'DD-[v>};2RP*+uDT# bo|>|$"vm [Ԛ/O4M7\L H$,McW& HT@!kb0l=SyX,LhĮ: qϭR82`$V}{2iuoHRJq&2Y!,˒j*ͦJ%wU%_{ӽdF N N2 TpCkȬE`,dMgYJ%whiR*`NRY!ԍaU* _Zߍv_Nmu .ДuDߋ3380$uV害rD"}':z5'LA' a{ϋeE4_$t3$fqzҩKbXN 5tܬrLp"@DWX~ڸ ZGMJ)T(@t!l] [ןR)lDx{i0O k"fz.eI OuQs¼'02&z--q' $Qs" 93u"!5a6Y 0Oضm>5Vp28RNcI`SPFg8<̬ T*&`YTU\6Jm y/e ̶ZFD,˒R$ZM+0bXV=dStBpϦicòza `FQkbnfRʍMD >0Rb۶t:7kZ"'afMt:)JRVT*"'&tj'0=ؚfZа"QC =dM\ʚT*dML!( &2YS sdM̖J IŘ!kb0k2 yJ-8' PJI!kberG:Y32!@tDT*VdML9. 3dM&ZXy',RJmn &f[ qnK&kbLY 85tIJ,&f DY51(ϚjdM ʚT*dM6`bjMT*&f DPk[0H!k'0YJ)Ͽa.aY&f @tDv&2C:(: Zd@>tlGXE}fykRCGyͤ;{_{LefMYTɺ_쮬zR6)rqg "7\#k"YaA|eqNy_nz%K>3n((&F & $χw|N@w[Z>0b/+g PPfpBDR6k"Ɍq3|%܏zOmCp&}@6oog݌.}IY7RJǑV%mK\N,k"͡=agZ?s}R9KF0uoȅ^p\v-v[RRT\.8_xTG]~ 豰@B./` f֕=. F>2&ʺ w)J9fRJIՒi4RTT*m۞i?>(àiwr@BNI<^n鬛3G>ာ ̚h4jD)%R-=pDq3F * Z!-'̤^r 4%K>3n(0t:8J%v^w b'`"P\'̜7*f3&RRTZJ\qVKgx'@jwlyd&y|fyd㶆l֒[to֒rɒ櫲㒺*li]mYR{r}wsՌ0O UY7DuZ| YDR˲Z84:sZ%{^~wZg:Yzym;Cg-Kd=v_.r^r䁻Kp˃]?u32uQ9/90fSJmt*y[3e*%سg7e7krN>H^uzFʺ *,̺̬N"̚eI\z.Hٔf)RI:' "' 呵'?$߲F}#""[ZrKT%9}_ysJKY7p=n\~ˣͤCYG/Ϻtք5QT*EuEt"89і_ܴZrk%`hhێ\}r;/s.ͺi+1Yi9L&Y7ܠ eYnq7@Rm'r깍[W+_.4!nv._.yi_\p\sŒZhvܟu32?(?aGED] L܏}P6mmzHq\ܣnbF{$xk˚`0mKrwa6ȉg^&k%ڶ#_.u_\p5GKDҧ/BӥZ@Cgظ)\ @8t:7kZFfMNz88!8Vۖm?]NIƭ 7_|{NY7 S7w=!ޘu32skN\u3\f!MF-0ktPa ~8n =y, 8W~#=!LW}{ѩsv0_ J%ӲnAA3ؠo>ES,+ =6/ΚhZnք.xf)V3Bp"Ͷ-u+2 cّO}:偵%Kլ)S{̺ेʱu3 xWE >5(0hX&a '+<͚tBO('f {bfKn_lMɍ_hzV|#70AC.~8kA!a>am}(,VJ)%m{jM 5a,K*A32?1{NLe7땲euSrgcOPvkʺ9IqN҆h߸!?ivJ1:Lf]wX@$8;u/uW!'2=߯avpRfpB, @FĨY>Vj5qG'f ewɧq8<4䭟۟CH# wyQd"qڛ^R _Zu fOL:a"?4Ȉ[|: JE̴ T+mț&CL7)SJ?Y>ÛnJ!ڶ| +-/?,̤I—40:#{iK,wu*KXw]Ǚ.&:63 @#5=4DTX 30Ae0i:keYcgMNDv dj53Dؗ]s_M)EvT+e9eݜ> ]=;G9]z#v֠9,;Q_gABcurwेMt:}0<_On{} ҰZf(RT"K@1Y$5a}CGJ%xEp"%J|%01"G)BjwY7gi5صgqY,(@Bw,"7<9Ь# 4k3+ sۙHT$t\':a:@AEp"aW|;7f݌u%_ʯ>{,i>Lg(/H)~ =ܛgI3!tJ}1r^x___A!QаAz@¬k`'F3J+hQJIV T茊Vɦ` @jM}1Hr}7}N$hcOkDu_\KRκ9600ftM2<7}m'ִogpc;tN>Cc 6H 8;u˰lE*oB_jfWA s qeMi;j5qǭ?3wHօ| օVMZ7|[7߽Y7%}sSd66Z_Ց\ҒF|d!"^^>|_Y\o af`?ћ _XYgI衜Dēz7"h~Aց ˦WM0,ß5v`B3:l6' _yfS͘z_n9}'uS gISh`— s%JTV;$u/߈|݃–F[{Ü?ed>Eb IDATwhD=vw}(|4}1 d+(K J;쁿.ANNsK2"ljq7PafT$ug9Rn̚q_'iHuf݌/]-GNY7rZ;1 Pp }QAN#RIZ)T;8fԌaatM  Rl߾]A @3k¶mT*Pu7l(>unX-uۣY7cfoO8@oyM)ĢEPyIL/> f֣Mr:ܳ:J82Q޶ +m?z#xA$D|ֵZM岧0d((FaNou'tl۶j*Z3TfR>R+JݖzF' [oD5S߸.f̴Vۖ}w?dQؘ(_bޛaIWu³'g _opѢF8(Aw݊%|'KmvҌa"xΗ(/j?ȟ)}vٶm4MqG*͹SYwBvm9IdQ7]?ܓTgR0YZE:QV^eY;Ǯb#sbD_ɭn1n_|uS #8k;DKOf@p !MsssRGjqV(fa=j5gT*Iv 7Mf0&D$wY&Qץ\.KP<'tuʺʍʺ"h('>h;}d=!-}#k76S6Jn6 Q+w|wN}uEeeנƉ)3JAN#rY,YDݫדE~ML}uZHٔm۶y+JքOj5jbYZ-wXLWCpbH_ɭ<7Qyvv<wt 촶3.;񃇐J.˿D\v||۩RH=`u,f{tҠ?qĶmiZnPB_Xd~~ J1DE~eEa.Wd8#F͢ht*Rք_z]j(zj\~󚬛>nF!͢t\AA{ rݪ'C $լ J$:=ir ]g4N_%m&fSǑj*:udQWYzZܜ,]4t'(`zN5Q.s5a2J-n9N !|'re~nFu3 Y'F>[*<>4$aͦl߾ǟ-Q.+1 (+, sPOK,q ‚t::5j*P+iH:P%ud[w}Y71m֔]/hſUsuJQJ}+Gu*1[×Ȳ)uҝ(!@^l qjD)%Zͭ+QryE"YU^?rYlvz U(3kYYft#;ţY'7=,oZȺ—~ul̺50;m}

    r㪧dp7!/DZ9pNG C Wח!q,*b0O h4P@̬ !=mP,މd%HQT>6uY7c(rIVLvqNvy^vyԪemG58ٖ6lnǟ,K;fLDwNyK*q #zi-˜zAOgΝ> 0o_1Lքa1>Η OZyqM(v1MTo'[a~^c:sM1,,G 8{]pSz0NE=GYd"a[> JL˲hǑz^E(:β,wf`¶mVn*KlUw MۑO“nJN*1!cp_ sgC5BjKog(Y}#vȧyB>FXzxA^}-s0<\:01+'qMK(F3jBv劈 ;tkZ@^,vST2Z~JjfY~N&1X1DG)龧nwuwAn;Z&L呵~ٰݫVG?AKXC=/'1ȰTŘ7SvG1DsO\)W d[ vEރR1ЁF L#"F6IeQz'  ң˚}ɶm7XS*<ǨiTVKlv5yȚ!8_MۚY7CN9l/_"/=z.47mڍ4fLm.޼Fp!Y7%7F-8[B?g4S\~Ny)$yġ*i|“PN'm';\ѹ'_}wrrOIꭧ~s8SFp-=^fvk PJwZmw.򎳏xs3OX?nqk6&1Ŵt(L(&EZyVnhLUL}kMT*& zxZfNi93g{U|*} &}#_|#lK|M'ȯ9t]n@O"ߛm]AA;d%T}?s>ʘβ ~xa@0B q 8FY~OVJ".n s޽Py]}9o)L{/޼D@IZDȢȯIgQ:@t:lO8ndM3Oǟv]=WK6עM28#yR,_ Ⱥ)7d݄\4$%1{qz^s_ؔf<ԋ801̲}! ie:-#jَ @3&q8 CE~eE;V ĶȚϲ,T*Rץ\.{:0pNL׬r˧qD5IKՋ^+1Y7%үoY#V@M)~z]J^?[- ۖ1f%%Koqzbc۱݃,L30h4qO'AE~eE{HF&:[[`Ǭ?Q7yI㗷Ͻ9q{߿eS[s\<'Qˢv [t8<:',TbCd(P%lCҿuj*Jz.eI*D8'}NQSo=vSjgܓVʿ٫sWbv GwJ_ś{i93 nIܤ[Uqomff'\k'@E!2@1v4vXp6 fwuLr5YON Y5, s 9*V`@OX ߇Tj>06VVNȚ3'{_*K窩Ϳ^wA?qkp IDAT]Omͬ8ᅆ/oZ-5%8cB= H7X8]~fV.o8nA'nN*jڸi`^IjD)^7ՄtlOY5, 3@Q*ܚ15Q. 9'I/pk`mP9i|J O>%vHeE{Lp;\z󚬛zQ܀fTIP`B| #Ee=X1n@b[>Ӥo%hL1#B_If0$uE_ifQyh6ҤrY&F<_[ltDvݰ=yZ߾[dϺ߸:&dQ5;頝^[|6l z2%gh$vIAXq%0ɨPړVI/ޫ{pnaD +:Tߥc{2Ȣȯ4( PP v/kHyc֟jbY;lR;:0afT?.Dݰ-蕻'>"+,-,릈>%7/d݌\x~;zJy@zo('{0 KD7{Q7|FXbqYrI>G'C=)I7ݠtߋH߉$A t0Lm0`WDuڑE_ieQ߬:Ih4rQVkhydk8PqwRs8"A'8ѵakCm;~c4yi]vѹ|-Hv?u32)-8Zm[~x-H7+ۡ3#u@#fE^Fmew2hX!G\9MC3!"oi_IHdE_ieQ(tGQ @ZtD!k"R/|zx's8'""&DNI!k蕻˩|!"zg>w5e?/Qyw`$DA}| PZlc{| ?1@7vz=]n48TU=Yt2Ȣȯ4(08ml+̚W+AW:SץT*yOguAf)J)VLz~c&Ț_ywY7Cniټu3R3Jx#0tU L20qҏA H K/:! 3zҚרD'GЛ>(I~~>a֙mM// IdE_ieQqT*I"@`(Y:8Q<;j5""nvn ݧa8'D׆Dbץt/deK2mCvڻȴ 2;tnc.A{d'nܸ $RIzAMޱܛx-DbLV JDVX&#E;^ɳ=J3tQH}6]HYdΠ@6YϚ(ԚHV} NR[ZzWn@^lږlp=9Z~msfڎ+o{L^wAa|1>؂;YceK1ĭXB}܀}D-'KԫE Z̘ |a_xawZ-QJI^pNQa67VZ,OF6awRqfSͦ;6tz^_QV }|zݓ7̚пukZyNLkÖIjrɒ?rIo^Y 9-?Y{.wv&|CA?hAC=uuK/[8{꽃 EpࢷpѠ`9K|kLq:EHqc;}\K*BgJ8#F(*J˞q5=]|\gmi6 Yn\ N$c0ݕaK3eԪeyI'2Ys_sz|}&˿5{"vV q[~(>n3--mwP{KCG`6XgSX"K*gozI3aAUAމUOʇkO߁i85];vJ8Y*2ޞNO{ "͢ЯWB(,5·3Lz% 礥HC,i%3Ы;@vZZkYwedRy:i[zyͲ;"4Wl,!vLo7^tƄH7]ϝ^,}=9Zv[6m^@I?cEI4ٌ72eM~ u>hǚq:CEEa֟n a/6{Qm au‚Z-O퉤nZᜦ%0!npb%7$N6ok%rNQ̘[!p!WgUGfT};\9o mw)aTϛ ϐO|-7?w]l} q3J2. D<gӪC(b{rt;'VKRRץZø-=jvggdQר MיnGΞ`x'EGD't3D8TUjSwLK+81TP"Fke+r sB.zr^a?YWܞ~pbR'vV$j8IyrfKb֏;uN_/03.#`)w%>UzNzC8YFXbWw'ϛwKZˌh`& 5sG8(=">:jc.O&q|c;Wy'"6J֠""k"tzjT6Az}sҒND\-:c#k%'k+W,o<uN|loe'3M IY d2(.4W? L J5S2 E7@vןzw}顣->gxgS5GfqSݶ>;)3]t v#vG,$T*U)}j?Wtlg}0e{LSiFp1dt:nN\'lv d̉qsOpNڄzC:O.MՖqOD^q~Sy.ݍg(?aNklar}e7%d/W!u˕>c̚ T>tWpZ\E$%v6D~lOԎZ& 3_xim}%wGtN}=N0ay-3jENI`fM+a,-F1fm۶v8'-DHߜCLĬնcLi;O=D~r'q9'4T }eY}@ڶ#G g{jK5E:n9'^*tAԃ _v;^'8H"?L4 in*}f(~o֓g?4M?ewj6 Ț?wǝR$z}c8'}<' #RʜX*"n~?6-現Es4)y8Ɍgo:^~qij'm}j**o㰢ׁ'L}gO(%kl53zzf;-c֜޲YrY>| K#L&Ͼzû\OC 7uW/΂AS'.㿷->=)Ms׼lviٜlE~?dR(&7g|1춉i;xLZz}J%mnðRJj>Ӡ\Vh8fwʽy$?aDɊ]NtQs^aYHxSڍ1>ыO=F-/ײ憮1aBadPDP`bgڶe1"iw&}6i-l+ɚͦt:jcǽ{A&# 6fGfC4r-YJ)&rNןp34~|vs ۶æ=Dp·vvŬ +sC&6y x 4v9GϯDo:|f7zZ?EI.l<;ՋG3eQ.+/顜8ݷ&xݱ%OD`֍5%i&%yڂ-"y$.+⯠i1Y~?3QJjcwT.I^J"V#2֕)|,cܫ̤d6L(Y mt =F8Ycf j֟ۻjEХӾ N2{O1((13, 팵zEN9lB |S)CCY.\w Ne;К[Isr|!ADdÖ\z#ϧ;pIAK^Kڣe䚕 %k"j 3zAAd"bvtf2cGxO5d90FR>㯅gH!^hɶHV,<~f|ouGQZu>EgJ2E?j J)il6ߺJ}X'ST`"'q=^pE'+ω)]g)k&}j˛Uz+]4N=,f@ӎ+I}gm<"6t`P60UOА\˚Ʋ Ϙ~/&~[`rCarTsBDO$~}W€I2+Ӡ“43b{9lR$jUl 4w(6&M+zx'}gf%kBdo."zx$eɼxΥ&F]#@ (wc33ןv|7ɦ-#k7A{2eF -D79] *QbQ[3{BwQ@:[N͚`U,f`\߯fpzв,7CxVD3'1aLoEVi㜜~W\7W[^b_9^ӑ<](۲"O͞g'7hr[HF؃O80`<+ F({>]oY|f` #nX/ $O%CL+}nꬉagmY,^%"$벰@qlefMt:wEW'Ǒf7C(mHp"۠t)l!freړWʹ'#MJ3AɃp%wE~n>E~S򎳓gu_v9޲HZk8[0mWU6lхm;( 3짊Dz,w&Mͦɼg&D<^,dCH @eq@qEuu}FǙwqfwqt>@&I/UOS/KzS[Sy'|n ѠX{ՀLqA3'/\kVǢ)u̙<Mc{#ۇGFŴ7GӍJ=Pbo# /)|aAKs+o@@+Vis'_Rw&[܎s* f؋8\ i١?Kq>Ksn0JTLd9ryzovQğn 좈/kɱIH@! a0 J%hBГỺ֩}9gWy2^w"\zƜ گY RqE~N0sG^߳~oEuH>Ϯ=jyZ g'"N6,f0ބyɤs]AC[Ph`E|((J<0Ll0M (LHNZ-N|>|>ߓQʼnhh0M ;X7]5F }Jt/=35DVhVj< ·f u˖bֶ/Gv^VcaD\TDݎ . vQڈ1.|e{XrMR4Bt=)Ӊ'd`Yrgiŧƛ.Y+VcDHZ'yKf`sڭ<.&Q5h/>'6VDwh;Oa텧?w]yp$:!uU$=anqHo$w;皦96 Әv]~FFTBT٪ 1 Xk"xk4R z:HD'@WÇ66X>go׃5OCD݀'6Q d^;3^[,P4pP&^PS9Jw ~cSlɢλqLd ǻwVfBtLch\eJ*2,Yi{aDEZv<좈/$pZl6 ]*`P>vMăfLt&{ZGj6qQh^o^^㪳3yr]wHǹNw}ΨߊM\i݊i5iI=01B45^?NT5 !Sy(M_{sTR]\W7ԹeDWo&lOo5B3]Ƶd(⋮a<0Lנg0lD/ϤFZHtAPb&! -*]F3s]~m_޽Qĸ.9m[-2ܣ'<~'hoڎ5^D.wF \VAvڎXb&!߄cˢCBL9iSIe Ռ^nLn`Ou'kgJ$~7i5Qav ݬ".ד]vQeCX90݂]ЪeDmGa-˲`Y( IDATQFP(D8Z0l}o"DkEג#K^zAℜ?!zBx:s+HfK4vݸ^h,X~cg/ B:KB7Qo[s?5EE9khM _s}QA'DrOx 0AC3\pT70K:* u\ro/&RD3Hlf$jzBUs_aX'7i$Yr ~7yךahTUlѵמ9ޟZvO63W968_wJx; DW{iH'qY U,Rąuwj=*$}c )%ڈ$J%_~$m(wfDRQa=8kfu#A|/OR gem7JP(0QSqBe;`L2$>j:#0ť+fga)aw’9;,[X9<_;ܹfں:e! BRp "u`!N=.h -P4gkRr7YIC_(ۈo=Ҝίb3֯.^xrk qnët߽ige؆Xi:'(-B󜋄 EB{B $& o3O h"Dr ykᜐp~|E D<ڱ^D~ p_lom=XEqa3s%lBu[в= tj\uP8l%Bx5%1qQw͊b-::`/FNQ> 7Z]"Wq9Fwt_ g#&D; V]Vg^&%5%:Yj'8 ~8sCe>\eEW [D֩k0^1K\E?\rlLyb^呔"O :>\k7Ɲ}&ԕ 08hhډćrjo;XFՏ '*˫ʩ'l=ᮏݳ\g0Uڞ% pԛ5թ]r[108iP*׃ȋuqQ9X=ܧ'^%FTBꉰ=Ա-(ḮWtQ؟X߆kvD鄤uk? )ifidP.2es3A4Oҳl~KcIҵ NvΌvO0 5k"χ#<^I%>kp,t3!lCP 49 yF=O":$#%!]ĉvxv?9SFR^8e/Q7iF2~wC}-vr'o/ʇ; z9H@ RQ$'X?3;Q=ɲd姙M D G,N C]rMrD&VzvJ@QG~?#TѼB.!'| 1JN Wxh5R+CIV56 95!NUacqG Qo;U\1#+ݘ3eL]tk|}Z&ob;@~]pU 8(PfwrMЪZ3Kܯf}ޢ/Z(Rښ:1iBuT8z^0QTiAC8¿3>3MӵO 8Rؓ2WY% %<]tOaΘ(v% Ss]_E{m.]9;2q {N*#ŀh~CkΝq< ꡝ. Z=ʓ{>1, x",H#]H"&/߄WzmOqT~@֪3ہV]bG$5$u6x @JZؠYZ<@q@Kd2u!TP^\6Dqo$wNT'yb&( \p@d h[⿍^3upNL95!̰NMb_yԽveTRx'[<IYwØ8EnoarzHDP'4@@ш4If2ׯR3Ms:6tMX$&x ƝƢq:IYB`'fԄslDpE؁<0B/Mp媹to'hLvC JwBh'g 垨Q{oIś.9'Ou&9wpFl:#QĞm&%AGc75y%^mWAq}$^y`D* J8tJlk˜)/䊠KB HSu'?16,PVZ vQDiTGq a:ڸb'N, ~F D!8Ss"I{nCs .Z>^Vk]g*Olm{}n(IPC;8n*H-P@ìɣqåK!4acBtw8jX&KH*&xNϛk-(yB5eL 9Db[wC?3rCP'Y!F~V9 0P! =m\I?O" bѾt=n}@a&(eP?C+jthDXDRA6EP`ׄO.N4/`er~X0On~)J v+OzYq·6b(ޅ[;pGXP,`;>zfzJ<.L89̈́2ۑC:iBP$h)}3m+0q@=HQOIl)/$|. \%QBAQe!˹"kn}VH*a(*u(n-a"&,rNQ^NLpNq88O~(8OnIYk_x g.IY!`> (9 $PV,\߲<0Q=Zzs 5ጙL;ͯ\H 7 0L' 4&LĿ}st`˲왖(FD+/ $R HD vQ V{0 >~SM 56QDm:/-=WP8\.ׯE8z̈/ŁÑ- d9!tӤ܍q[-|g ",;ݙqR TPctخ 7K:L[?ʝMΧk)caN:}H'/⚧Ia8qa@4 cԨQs9&z'HèQ000>navb~ -lʱ3 >r rMEA~I(0M\|ڀ &U\xz>ipp[qzNDUq@)BUkn ar`o稯#qY-u\R'aPoC94)qIvRi%J5 ( F Q)2FFFp 0TQiF =И{a'Xe COD _][rn癈sm3Ƥϊ_8'k!X:y/(jo ϨXVmEBP.\rPHU ׳Evx( hA%na 1_i ]q0cxxCCC(9%Xh/P(E122"vQt(P9sah =e962- 90sIfM􉣱ˡ4΁l_{ gw?6D}mT:!'Ĉھ8dӊ>0vT޵Öejpɰk᝜g wyP}[&Z}aaB̛2nt#(P's%ǪfG"&=Ƚ":$5hM\y'(80 CȮF7')JQԤC!aIxHä Ei 1)!sP1-r"{|}]0} \rG);qe9av}gH:1V;5|xۑA)0 0A t;ב mT*X,a,.CPO4(-(HtKs(C ]uZZaΡAtMru-6pN4: &\|Hy2La?{|K/]cmTԮUPIngL]@Eix%b񬓄b B]GpU얠=-AGkw,,?0 0a &zT*v: &h <;YPOa8?ҐBOivbͮ]~X@s\t1%8l7I%.&m1b#)!4\Ʋ e'lHI1_Ak(Wrr]Nϝ$.JB(F7bnDc:ڟ{./|dA ? 1 0@uЙk-Xe( 얈!4(盺(.8!&Nug&&1;bD 6aR Ͳ5`X`R%ӑÿKo;z9a'lhэ*GMq B2y5g`QR&Hr'%Tn qᝄek}s!Tah0  uIHSbV{Q4^AI+yrQ bdd$ILE9162 8sMh֒kI|Q.&ZKhBq$ݟʅS#)kvxЀvk>4 ̅J{@_tQL87 5ag.lAB%n T= j4!t9*0 .44M躞ʙfIjK)59&( AvQ3\GFF0220OpCrOP$ 0A1(D.['=A4)IEtueD8 GGRN4ԦN H 1BRZJ;} gחuB+. [Azbq݅ ag~V;Xe-; ۻղBM`‚xNL,ċ;!G6a(饨7ʦP@6;D1/Ee?b?>]n5L%v~& 1 0ɇfiҀ< bP(y5^RD(Hu.0Lp 4m!!AF g,J%ᜢb3EӐ3(U̖n?,5hw>('&DpeXnY?"ɶ(ï[\]Lp9.yZyI5ȭ=ol 0@T=;MQ#W)%<BI74hO 5M44AL&31 坠ӕJ #P0\׈*J(uMpI9yeR@_M ͻD1IH"ЦmsO4W{- ;j#E ' 1}NGg v]P/נp% 0]jN׭];Lk{ 0 dN+؅Bfz ׇ>dYG4ގLa,'xF3a&D= tn !|Գ:B/'84gwTJ*l,0(.m]%Xv?9S/,x E~>BstL8g"ݢƒ( 0L눮 wLpޅf ;eS4 2$vbCw HA9T,~=<;""ݽAty'}1ܹdꟙ~ M@QA8WHpg3|So=خ7B^ɟգqɦE1B?pH>~a&04_wC^Ii4M{f1Q `vFIzLsD٢@1< G&7{ba (DqFNy&1)$@wI!("{30CvMdYЍA䅠IsOyG(O%f4zƎ9C/1GC(ܹFU7W#)N5ÈignMF 0LtP{#Puԡ.;1wn rPQ,Q,큑4=BnvYXK],շaeyvu/w ]CGDn(Da4M;wn ˲022Tډ &ȓ sO 'tŢΩP(@Є&?|̅SC/ΉV}E*Wjk AC^ΞP[L&gSk} -+׽ipaaq.ȰN*k  ! ꢐC;b5E߬ ShIv.,Eב|5*0=Ey'm?h=FT-ܹfu9&^y4hsrgy,)A|YhhRv, pc5+NRra^²,;uG-SBD=P@`fO IsQhɡ&t5/Y$r b8'rr8xS,3}P86TĮC-S{hT=ODm%4|vha hD۵ZNElxX3 0~ qpD^I1Hn/xYze;). S$Aän&d{9ĮW)z+L u?mӝmDٴj):Y`IcPۂl:a7tC;q$R9!:)6"֥s~@qUĵ Zu-yK 0 0@hY)N|3I|)˨T*@ϼc„<7zS\w]gub4'ׄ.!]6ii0 as)|5c顗n[wNP5w=QDxv|,']m|-bTN dy\ob:ywN-wqKYw ACTV 0LhLRRvF^l66Ll6(z$(ئi !$zkBF5iQtOhfW%[!9 )7;^LqVImsOoُͻ9?BԆo,=~MB'bkҹp =DnWD.P4j;MX(Qsz%˹H 9:7 0L+0AlGa-Tk0Mۻ)qvQL6{yaD]2b:r8'9S<+NE&F% ߱f#D( $P=+v9'dբin߲ 1GPm~V:>BKlz%@!dcPyȧ`ic 9=z9;v Ι~&#,PHr'~X-gkT3#lWueuaFD7N{6  ׄ` > r9i矠a/'8Yu{o1L{f J-蕺+&8$Qx>ZH'Wh&U8$M*$Tn7][kY|.hIut'Α*Dl< 80 qlCEׄi` ݋B۳D)G'(HRiaHKĉl6 ˲U`[e oX`zU'9A'3;qv!`| 93-(Ϯ]gEw@PE,!!w' (P8 + %\߆7B'W('ՙSܠ3 02bǽt\ԩ',f ͐s. (4MN cD͸O8A|>=vHsnOLO2xLjmsmDNs&;L&oH(e,epպhMk*P(6hC }ι0 4G7i~DY h;ϣP( @XBI&q%J%d2 D_^ 'ѴC; 8r%~^yxnªq<CPu04jL^H99r+7y~3Qx&ۺK] aM]I6WH1 Ul(tEAu!vNua &lb]=( `",˲$_^ g93dqX`˞ aݶXn%P>t}"e5#o=|ֳ0W.gSΑLh}'k@@;1ϽEzbq[mCI5;ܨ3 1`砶8õMC:%q^ފ-B6xL,d2f,Q1AvZgɶJ7]vbrI{ė i\.;9k"Yd/itjѴxn~,zr簸Ovݭ(W-_p\5^_ym3BZ:jk]Y$[^q{T%) <29MQ 0aY$Jr8o&Io3hieTrča8A24PS,m kR]ddkÃf h"sK%6gt]]?r I~?0='#SRb>n)ēE P%ղ h T3nRrNxN 7r. 纮esz9 0 CHׄ +DI?6&k)!^D;7ʼn\fVi4KLObDž }%PH<2P[s 7j$7mޘ7;{%|(kaݐ/ T*862qG?!+Io1lM ;0z)vzr؃+,4MCPHX`z ppeax݊oF%B%T,-לSƶ J-"k?)8ע3ŏ( :0 0>)qtLqNLҠB`ϔ[(}ir&ըd&zT\.ʉ{eW5a0ׯ`qiV8wfz [!RtIΤ׮p#YBP}j|/czaI~{wb?:p5%fh.;e8{`y?Gmqv~wr0 0LcĐ)quQs'嶒I"fi/.p[l| &zd%QNKR=A〉ypN ;DbJN2H7oYYxrV mrLQ&cQa j۝9rpH'&-d2&a|'tqb!t.?||^--w4ゥ3q鳛 ؋;~ V1h m2 0 tN:ympH'&bhqWUaKظpe6b/^ۻ4(Wg1w{#8tƉaаa/h_ϣ1qhL; Ǐı4n4fL\f0 0tƨ;4p&-(fwXs 2Ls<93MGj T*T*X,ڃ~(ȡKeY( ;e80X0QЅط6^?v%Paq) 0;ҩ\.}ߖ64S  ѴPf֗Xɣ/e!N[E}(zxjpghwk_k_P+.^W|ah;Z! :LX. n\Ĥ DRa0 {1-ôF+. T*9B;1 ~Ol8'6?gu,7g:+붙u][$f1*r;Q'| vpN|]) bPaGIwnS!gɷH-/:oY)%dJ n)gW|L0ۻ0 ӳa9ߩBir 4 `i4+@0Q`Xi'cQt)XlNWύ\R'&8&:$ ;ix q~ -805̙BNG@oΉ_? ?t2̛Ƴۃ(W?<a ;](W_<;,nv5wjsܜ1 D @Q vx3IߟyKG۰, R (lFB.ozhR6aY8t¡{89-aρc?~ 3?c"nL1?*WRl)0!&٬cu2add$2η*igGX2g"za_({h`z<a(םHێOϱuO4!ΈQ7n_{Y/XxVwanD:((SRfY`CVI]Q,d1<<\]ם'̚zrEvxbp'K۲ _ov5n̮Mj]$Phf'xaAvMȡT,z1a{hؤpNB|= #| ye{ VW*{9Cټ+nr%a n„ِf1GܛaM&ҌY*Y,7KMōWU'E nۇ{/ĖΐG[|OoW횱MN0i_؏-{`c8>lr՗̉cpʌ|$L7*5Q.&d]@tOP|dJbh X`O[wM} *ffN._H Ͼv%`Z{><}|wa&LSH'~fCle4]W|r"ŝ'Oⓧomًo1( v8? s|EWC=Ye @wI{ mۂg_k|BӀ3㊳ގ)Jb&rȵ^wQ+„+D9d #pڼIn0ؖ &!>}ye9Vj7]I vum|tgF0L/ &Î;bSH'vM0 ^/? _a>4'='w?:,7+H `ƺm/?z?ӿ1w8ȣ/EbP;alyw7~/^-o?d/YkB]uD0 p  kW*P,c XWୗ}xmwM_esZƄI  Kw:.[18-E~se<~7xv'}E ]xx.\rl|b޴q-_ׄ ('dD]/0PTlcz #X3 0N۲,{^a@L],ack#m> g,?]xTǾc|3o3#/;$8?W?YUȵQnl tb|kw<>^|=4 ݮkB]{)<8mp},>ƳIgׁxnjĄz\\4LÒ0K&8; ;Ԧ81bOT1wﻚ aIJ /(P08c\.;t]OŠi {_x@(_?1cRkad:iQ (>biYjnſr6/IY=_}:Z5_a}롍8pl+_$Gd ^E!bRz8$$E+ Gaqa$lXUly~r'[~w_}:fNH, ..oٓUbɓ3̚rfM9 8U{7'6aˮpī`@>y+#+a/|Ad$i=cVvO`]+3'| `gM8xl?-_|h0uS1>%3^Pʚ5y u7gv}K_\}\A-U| y4M}4m 3Ē9;apgps[hҸ~+If/01W[X͸pN;%+=E3/Žw9'Eͷӑ0 $5&F8(f~\AIeıO냫nكH:ifI?Lϟŷ}p9_&4a|/wR.kY'@OC=NS8'0`Yrsq90Ew{=ضo:cލDrth[C4~(k W1V gO§*|쭯=lNxNNj9 0 LZgM0LcDIQH,y3'ojh眂Ol?H$N$&2a}yTLJ@!8pt6z 8yX̘8s9M q"~f|uXd:;0MPz~((I 1S\v]X`͝8qaXVb>9j3/>$R}nlNu՗{;z 9YYڋ'7x(4(W_ w]>ql(e0 IӌG1w Jy5ATi$r幋qyKGr\Ib 80qT#7(ULL׏ʼts*c1xeK'WL4|=CyCp,LWu&HX$D#z-YvΉ]3O m/}xc[pt~>xBeI ߢ%ynkhۿe߃X "z&.^}f|! G??t>? +.0LX Is9Pf74(i=k*Vue;gw#dwҒN0L{ yYݯcqÕu]L!*ny@}&avb(g^EZ( ?g&2 ÀiΉa(X>wRyҷݿ!iW1Ρq_m~*mO7/x媅)\zxKS<83/ƛ^RaE(ހ2(l wFC5#b%L>i4*oO|;đc' O֫ΞWyNϙpap|l}nghx|<;lğJ̛6֮"/lzEAމ0522,N0'ODNϠT Nq!\bNZIfp~ʶW.0e|gW\7co}A܇OYS'0 6hv/ '!/d ҃b `MRJ\GqG>OxFEB&N$Iew[cCEd4 s}h|}-{lO}{ ~T Y>ܨo[c' 돞ėovM3$(Dq"0 zd;Ǝ14%w_/r4āV0vCO־6azH$Pb,w1;w='L\׿t{Jb$'a^l}-85Բrz_~;_q*4?iE! բY=EnbhHB91N`'6>ܶၧw`ׁj=SNյ}Ol?6z_еx+n\9w߀qnk3 ӜND~mx+;ҨM9cv[:Y=C=|ޛ<{BeY`t ٮhF]{Hx b8'1'sbdetP6B;:>_7B;[ǁCm/i`i`L,/5>|Qp\_yEձICG.@9Q8K.n3N` ֎sxvHʥXIHe,`xًERȤqݟ\ֲ,`,GpxȈkB&. 1V\V枠 Su5]L~  Y̟>.oٺ8q皍0J9/Ξו@n4# {qۯjyF|u/tI5,;_sv`ۺ~('a" ĉl6JbwX"P 1;0e!=}ǡ^PqڂYٙLlU\^^:2ax!NK/?s4`рeNľ=OB٬=!E~֣ϋ"j8'r8;>:㟙- $ޤAD==ӳ\wy.&=>TsJm~ 5d3EFSx.ۓy/"]qashd}:Rij_[/D }D|aqBҝ } 2I&òzBqs$}H;STMI_T7t4 t&Vehw7w+Tm(@EPۤsy_*!vΉ`+@ 3CBp$c_)iJ.h3 -. f>e?/?_<;_}ux!c% /ay-<&ܰjZ, V ]~c*0sgAn 0 1ۂ՚ܒ@e]34ZJAiz(NCۘ6Qm~~Ayįf5jڑ_րUsk/%~?"y\Ղy(rxڵG}?j\Vj Ή` D z -^|^Y#X=gu_ .Q⩕toom< _eR°vX|(m |\Q"ii P(Jƹ\*P:k>L'LYNan0oz q51sY+Y1S>MKLm'̣jJ4& b&z!92J EU=WN,DU4.#Wǖq}fdeĨ`,&XRE''܄&ϫrdVZEJF(D#!"1aɑHM.L2p2y%5/ZyARt1k@LPWҦ٫8uWqj-(<,11p(`aM:[# [T9^ ce ˷шԸ2<%q%7ͻh9B\.UhlpL);8FKIϣq>ոVQfp"f+bAaɑ ;6 D=AӴ)1+@!-,8W2'ǖ =!V (iBe]3T O 6<H H3 n1 P),?xO2NFʼnfZwU?e]41YuKWMzНЕ*;Bt\S,izMPeA&B AA bKuju}aa?}xuBt$[^B`θAx9EPkuEԪWq QY"ϣeՍ8rx& >z ƒ$_V7|}~B1.~=5X62NbA(VZJdVbs9(ƥƉxj:U7jy{O؅khYPsuu(,W2LƍR1}T[Z8vЮ* fkӑ8b@QVa3/eaDLLAX?1z؋yc+X 0[| 7V9<1q|p7sƧ"KDq'*Z\$svǠ >sSKl?|Iq"IՄ"=)z߹tW`ĠHV&=-='sey=_C ѹ4cX6h¤a yP̟0XG8vG;uD8Uv0xBqU>wK& GjSؼ<:]^G/G3a1p?âz-v=7<~:#lEctKMS`]E]S+vJW/HYM6dK)X6}(& K 6 r9t:1Y-#5RUQXVN(l])Xf8kzJ 6fޓoZy=c˾s32L[gQɒѾ~}(:gsz:_3n߷xn>ñH.p߂R,dRSפT5!4Z .=] : Z,5ƒ);MIs"=QHpEU6bDRXǿ;pzιM^\38z\/%zR5G;,N2'Jk~"Z׬87_Wܻxmx{>wq<~4>oTTr3䔸z)Vo~m/nƠPQmoN8jx[OGx.Q^ۄ~O9!hfi"P@9=6> osoFYU#pgspjeםu-xd1R gd6__d硋3`hAFD2<'Zn:whlmK¶ wwZڵxo|sXpST}>;4VFf^tj: Vl؏g#f@$9:V/<;l/+]x3bl*P&QI>*SF$"*4D`\Z_\eq&?]Ǯt+i؊/a@x'Ey{}+ݰ)')2.w?ݙ,ݷ`#\,ĵwUG2N&<9s2$`V(ntRD'+K"̛HJ~I-zs E:nHZS@U] ~dX]ز7w̕, |JфNww+N^*r|0 C神@A6@yM^o>6f<ÇV]8q[碊zWxE6OoMl;pWJ?笷F&aGLq"zv>`'HWI:=w= NJizzRn3 7Nljġ84;?[!+l_Nln>zQ3 Ү:{uHt~SdwM3}0c"c2\?`_,l>} cDc&ͭ%1"n۴Q̿p/V{m0ݝ *!Lwg gi[%:1-휔J%s"8 '+I DfQ!Ԧ' D;BN7_qA6z?=l=֩t/Ѯwl㫦]I{*OxS<׿ ME~^U}3}kK`EM%,LH9Ҏ/O.QN_j.]ConByw {8|9 /ݿj?g*:8v7Os;ڱzW/G[xsR${b);z´1+'֬tbd0T ۭV576"ZH1CbȊɘ:"329RpU>\Za~Ţ%X:-: s5[ ?h&/9 zh nEUx⣽sOM13{TCk%,̴Z477 kMKӡMm0;AA*r)F* K;'BA@-JK-S^ ǖùhװ6>W(8';m x>-L/L5x8~񚫗q W_XfNWͶӓo[U:clX7MXj LMi[wjzX{3Ρ]mt2e@7yn1ƒ|sxxSs~qBx=hnUC>0#P\R ///xyyǧ___>* * JhK$ˍ_2 2hUwr´`$Ή D $-NRQVϡո>ú'b崾m"5-9e5,ih؅yk>lڛeƻ yal F\*g6Pܦ;Oc@3 'Gc+bNƒ\.\.7`-bUQzclIhM $Vo4^U u{/I2~W, _8*RF̱ #'wu:;OtG:O:V?gu^h+g:=[\{^]涾XUvNzr(Lל !6`%4EaPt [TN-sE;g IDAT+T >)sCKoĉѤ"x_;Dŀ\Z/~8NtRlSx=^sA,1 ^z4 鎼MMCXb5xf֖SqOCA8=E>gC!Co| yw^W?ƨt+4+5b*}EQon&$᭍'P5,;pR#086 QGv-Zu(hDnY=NdY x8op {msb XG '"84:fE#2V2TrcK0ɶuLhD ***_o% F&FBѮ֡M(ivBn?5 B˖&<D]EBd|V]Tc94Q\ՀFmX4]<ẘ CLX P*x)kXX-mjT5)2j8g̒|.PkY<- 0㥀zFE]]ZPYߎfA 8W(z=9kxŅrKâiw( b :,*x{- =ǣ-(l0K `!|UO6J!G\D B|R@磄B.r:hY m(n@u}lxLJ)yBclӾǹmQ<{W;z+ycX{q܁ڎeC^;ߝmL{3>ֺ}1z c[g+MvFu,K$M{ϙm2ssFfc/p.k[Ъab;[l(<7*+İxyy1Ó(ѓijhE x]>J ˖dlK"}bdurE Ā ?VDtKjύ$G!17$bC3qqr .KfT܆dᮅc%_lX='p%AJ\fMaHVyTv1~-b Db*jpJ)N^.Bfv Kk% *HK d|OEt`YD]:V-lh<rC&z^5nho~ =דi#حiʣLǮ nbsWyc4lgkY={/=6 fv81D8goWGV~\,ÙJ֣^(D")*#0iHiM4VEaj$h }&8N\8A ؀Tĕzhm$߸"Θ~^~L0`hMcAyPL$M ? ㇙c mt2} ũ(2募Nz88mMXg|X5w$;&$(ą!%. wkl^Ԟ% tqcb Ki00&"c13 ˦CZ-GQ n5ZÎq+;_¦gzᦩX`MWLĆa hhi×?ºgЦnWb=Qr<6gqzP<!Pw6r & ƪ#0zp,h;o}1b*OL_ǯMro=tqE׿'^J<}(OIȔhQ6ܐ(i~iѫxǗ?/uܾe˲n /g:,5) A~޸}螟$5 -x\вɘ621!ɌB<&xRȊ)xÝǾ}Fˬ3nW&_2O×4n9#1z5DFˁ)y)<NV[1Vb{J,6DYu#bA.T"=ɱ! 0rP4rKU-ü ؔ!eӇp5Vʁ.B_exgӱ: IT;'O`A>V(ॵS@S]AD[-)qa{:E0(;; /n9.q?Z>YTa”0lzm-f$rEA2T g.r"*9_sF`γI0 tޅVb1wU2hP2u8=8nt&Jޏg]0EFӸwlxy EEjk}ƹwS͓"LT0kXo0>-^qZq}r}Sk'16˲ZX='?i&KNJY%PT^/iZeֿz B^/TVa[z3EדZVsyeYYty Ð a*R9rbD"N6#Ԭ!#;ؓy9EUCS<3E e'djY{VICZ) |gaFb TMt:Pe3I5PIP;'O`#%;!%>*9Y5 VSZ݈V-KL>qBq?o<*Y` m(c׈7`kpmxK1(6isGakk1EU%mvq7 b2Ι9pij7XWm0mTrVQ !p7}V>wKI!xedf2No4i,Ǒ4xt14mN FY2嫎'ELh˲nT߰ q(W5W7cmFFSz|wy(bͮ?աq 6Zz öہ^.˪ \N&\ qFa 6&RC>aH[JY2 AY N6foIQ!/\-)={3.+^yn%ߓ4˸MQxy\wcl}% N< Ĺ8!=w-_IvaԺwt\FKqp csjwDAu'Ch-dt:o% |l9YE%hMKE\d BvYdY8c"U#1oҮelw`VZۋnH䷮&7ɵZ-X%vNB F Qxn{HQ^y`(:^u/FBk%#Eba= qw+JzYU#4Z;'ϮĄt<-Mw}q҉WzCO NR5ŪMb |4+"v|ZٵG~I-N\r:՝yٱZcԐ 9'NN*GszvaADϜ)M彋uI phVgne(كiXF2:5FQso&ؖAE!(drg̔`Y,BaAf"ߓE1e (4cGVJJa;&8d4 EQadk<焜G;ϑ`"NFZ Z FF " 5݋uBnئe( 'K)w"4goXTPN=0uD""֭7XGW9 `]`] c/YO`BI=6`Hc8]bQaĠhlL-,!|{ s<3WJu 5BC˦K!%5fۃm_8 XV9 -j2t˚cN69GVLBw_ZV/Ã}1iXdA.UM!i`MO9:Nd`td4=~qH5ONnʳ&W/)˞ 윺{?g@ rPPtⅻ&C!'R\n-YCDVN) Jk] NvPȥ`dHv*?13Mtdo,bxXs%˄Kk#;e Gc$xP(8!%Vp׾;QDLaq8h(,:d\n KjJKʙ#p'EXfV#m?VY*k]}8_:ݧĉ:aS3p5#X U  URтSIxnd@p1cN:4sG'z}V+TZLFǂ?!>o@'̪#="?1JkT#|ބ:#c.."_ ';'0'T@Xfܙz".$E`AX0) m} s[}yKXcV 'e;h$$Dp: *$ih+iq tzGo TM>;'MD $2F Ijl0~l;p:=_s=tk–$RE!MyR$vND;IqwMXq9=։㋪ Ͱ5cO|O^S ?/W/*,aTj+q*e߷o /]zjrkJtYY}YP l/ކ=pD}8P{ы^B?PiS4@)x{7ح7 5n!kNogl2 4M<)vksV|{xnq"pLzx–獿z)q.\MVM0Z:'U!9i4bDp;@psĹOvNO,e4"{;A P2&U{Mʍb1cto6qž@M{* ^oHnmTr>)onǹ2W/AhCo(ʪ̇;5"1*50>=ήjg1:511Xh,.X;{Ɩw%ߐf>Skuغ<^djW MS@pk&,3m^ UѕZ3pߣć@`֊EO ?$8V S8t솪/3>=K8Lj qE#F3>͹AS['{2y w28pz=85Aш @ݍvw*bI^q5v:hFGC0hnӀy5:Tֵ kl 9  @zr$NM)in%oo6iO^΃>9לw-v8|Y]pMcA{ȹ’M=fn9E+![-S@WvN cM }rUL ؉5.]@9;ͥkoP@:3߾J!PW/j(B޵Ą(NP>C HVoxrcYPZiΉE3 lg7Jj3]  ŭs;8AV 2c3۽3S-o!zsYLmFLko  M{gT5㷯8K0њeY(~3 ,48~}MQ:?]dz1o|*~Tnm`*NNG.;}76L'~9c&쬘Y{u@'UbjIJɓl0 "NsG'`ƈX?R#Nh8A6w"+Kq|]-q A^`\`[`s9h@ ..w94 a^7UsGbܑhUkzw&r"x6f'e B4ud66=Z+ -ǦNwθ]:%U 8:a32oHwjzƴׄ^0VMXB(Ή8bDp[E x' Yy} "N.m|0n|~%L% y߷I$hrnP9aOc;Xpzw$>|Zbh/g7@!hvZ>c W3]UMXc< 9t:4 RIng 7%*>J4j:oh~lS$t3`d4Xc 5Z -iesX\*ٜR)ñkjs2MTb44;9YПinln!+/v" 8ai$btP3OwU-Xz!-qsIIOL;7>u٥`̟.[㑋X>cd [tX:uό&ַIݾ IRAPwPS9N[5 EQE!5 UYB ۣ8U7%5/ENQ5._uL#O Zn$M@yB/͈c\96Js"7Eay= jQPVd_DAi-8bi6f[ b|T+xg~LCK;|}#}u "Cݻ"lpA1p'[VOwջc͂Fq1#ĉ((5n/1 r=4ŬK;'avNܓп! N'CpNoH1E'k08gۨN$x#vl''r& ك --mtЖAZ:2ʆ2!@ lζ<Ժ'EeNw⳥e~QZՀK(n@Ie.URe3 B"@woEA kRNz Lǣ*J:>.T]|GEmh r 1sBD [OB|Oڊエ+<(Jad łvkzidaט!i+/UyCR>X%i KfVi$=sϚr|- ip9`O_B4nXb~TU%M:Oű2~,{:\W݇7ӲuWq Ζ֠ jq ڭ#"18.QΉ9s0zglZҪաe8W^)K/dIcj%H? -'/*WΕXJߊ`5&bϕR).x̬מ^??бI=pX'uI'Ć&pl>p9 \P|ELy5\,v IqHFr|4ғ␝!ɈX|騦 `WǝS0nh#89n F&zN(18!vs%8jRbeꮶ̥Z J8F-\{o.\:(ab s3܁IHKuf<#""3Gv9K~?wM,vS3BGYW&5.Բ( 54[@ "MF'EF-m*-&KPJ|}M.Ȉ:3V( ͡Pb0_LAd$jdҒb+O6k(kB+zoC5b؀d 1CR1i@_'mq8:7Żv~o (Z]6#L3aN 3m2]Z\ՂHgpZذi|ITMĕv]YB)S9'e Z D&} NPY'm`1(/Ε=)83%jk6+S{($Dc@1_<2Sݿ/L,NB:I3'qN.v5Qs} ľ=U?r~6052gًs,V\1-璞=`">xx!=Wwp~Yc6H&8tք`Ϣ9, @Y$hl!2IO×w:i1ٲ'*QJÓ/ŶŪ#%!# j=Bq' N v;x=9iDU}(lPm }005R1(/c@&AO1D|릻SjplN:=p|W[xOuuͮ{cьQ.$ PH{heBUvz[xP\V#g0!7ST6 lg;gdy`˚pYrN6[VeY+\ }x~隣{] CdTzdwpk=NT JK~>& @ ~.ArK\I]qܸV NGq oƒU L0 Yi  #-+Nb愸*[)f#Fdصv+k~~]w.;?p_Ĭ5]\06U-8jghѪ;5.ز(xhrN$$Pp\mݭ3ЬCv~I NP,CV6Iqј9.3dcLNR4T# W} *^<6Wl}glzfwU<018= ƴ,NELIੴ8 Y)Tupy5Zƀ[k3k)vlog L9ɳ:5J;e%l4vqK1 #gʰ?M&ܛbFqXP؂m`嶼f#e^O>Qʦ5%E! LP9'LZӊꀃK2̟`(~su8~y;wϚI@gQx*騜 Y&G/>' F^G;dWr,F:.L9\>WBr z睫ee0xf?ٹJ0sHE OYJѪ@fQs2LtDB= ŪXH"E3F ,O'JP, 7m \}X_쐡5? z:9%h{ju^d "[,6;,{?9IxbTFDH>p8+${dm@l%cuj.b";|YOCѠDzk$hذnqŎx|I?1VB|݅kք;( GoE Ëx6+$<=y6˗&#M39ێyY`U2Y)犳v /o~ 1Aީarw༭>[ =JUbsV^ÅܿS.X}gCwl)=!WoK'߉(y1[xe#ܗGUG+`AP^siy|}* n<nȤP^:k rNv`ac B#qV+v;t:K`w EH9SV/^2kh㰠縗}td25~,j_e_@+ѭ'K.g4dQ5R!FzFViȾ%~cHF"Βa4hn=⁘ Nvr]5sf-hesp8tsL.o/b6͢ݞ@Gߑ&8K* t4a΄WҾ5q=%(xfsfR LMH/Kzni$tHK٣eڇFʾ N_V{:[Z>n9@FJoV{6eY& &Jh~.>2r4'G+$J|e÷Nťu.Fd ~E2OrzZ N\\c]?P1vCs;v\xHY/pa59zQ799   /bk2&zN`؝Sd;lԿ6XiiDbd0-Q{D#z*GBX"""QQQ XӴ |rp<.T\Q{b@"_>Sf^r{M( NV>_-W{^9w9/{vMd,A|YF&kBXBK,]Dz3kPFpBzŕU+yb 7_;Exuײ/VTG9&>F $YlvT{W"so8%wSgcl|YZh;'3 óRdݿxwNSքw~9YFYuEH(g9!^:t _Wou .Y< Qn5k5{_#P"< 5r+0u|}D"6')8-M~}O:vA!v68! A ײ_{J;98g+n>q d b⌤sdٯ\Yr u}u .1xIסK5\\h:f zEeMQ!* j9m>2DǧO= ii'~o==8c6lg$bpB[ ,\8̬KtZjliǦgeĬAۦ er&v3k&{ϛKo 6=$\Pp/*,hjz|{Y<)ݩҌoq%xP5~Zިnh|ZJ0ۄŵ0Y)chm?|C)䮻)='$ L&ͭӛJ-6rk.ƣ~a) C{ք^r? _ZΉs.(8A^^}X!BP k4GɺϢZ/k`B)T_KGS=߫TBrZ8;y񒿥ʢ!o K$I2..6½) PjUZ>>&˹}2Nk/͹}lm5vϚp//@Wu+m:< s"a`䟫Q{h~^}onp>ݷ+N7o'/؅W J7PTZ5;  "DK|-[#OkvGqY oi'w5]?-]a?kq"TO v9oAJ= @K>Q&dl9Tエ? ^77{OLAӳ%grƿ>ً+e]B"qjPeO88?4Y %9K;ẲNη4[ߞc=Cj9uOv|1 sn[lv3}uYa,5J1A_F&<ݏ p8TΉk(8AH8^רkBL6cl6qD" W ́>ڜP"GĞHl9TWu G!t0^xL>?)x)C0p.oĠD$jkLpϢ3'zZs(%oaш2wMt;N@fxY' d9(' Lv0 CHأ!]X*S;]q2dor,8|T}kz,;|)8AXz}B(O mϧNYIjwwp~f%0Y:ײ{Y'5E`s{Ð1!bьΠJE]vЫ}DQis{a0`YǬ }Pgb9'1Ceng(8Av?j.f+eO5^=}cdwu}  f_H5bi]N~ =ߨ-|iF'J؋a+&!D^f#?7zU 2H9o~z/}S}Xxag )9kۊ-zU#0!. z.o]c;;KAEY, <"@J҄?zYC'('m珿~mʗʭV]GHs>!DiYi}x-2F~v߼/~\l R#.W[->7+5[vS {O#ܹ=~X I 68=7 tn)K^jsn6f}!>7v;x)k>)HJ,$fM0 e)0A(8Auv\׻U:llN<[;  Πknspxu׸GcˡdsAVЂ_ASY2~/aS5JZBH=8U>vAI(n?[)z{i'!{[DskG9Nd$l={B\<8gϽ{߽crw~->V.FS[gi ˚oAkB,DM @ Bܼ$[G-mv^_/>v̧md9bK>nۊ]b%e_r (*-77k"@"nXݻ 46(~ XⅫ_%$Βm_/~Rx2ŏWئ1H'Nr0l7㇥?zC9 d A>w\m LEw//|ocT55WeA!llNYc7x񏕻U[`nٛ[[+3D6B=!H#@arTFӛy V { /56_Y&ҲNJO)]=~y3P:;ƹf{Ou{f?Q^6;O{քhTl<\9L&F Lr 'UOs"x։*N+ĉU+V[ɿ p'ʟlni f$q۔\YP1n |t@NyAΣ?Ē_hUXnȑm {qoGoɶ,_|Wt|W+|c̝8q%qL99M΢l[T!{='egAqÔQ]&ncOEl;\SO-uzt2: _~}:`cNŢ#bK.*3~ 0!7sd H:Xx44|DAeWN׊֝GwG@ٵqQP|7QU&f`HF"dݯ }vH3yY=>9)N ́˵WQ\^c(b9'e!Pp oVk`- <~T%6FS8t1HD|L$b` : CՆ6+,v -gꍜ$ee|TuB %Q(ё&DG`hiQQw ->L_yXS6TV &!0xx/k;FņMbӁ -9Iqш4aBL-mV,6Z쨨9AO.ƮcPV}yKmnLF\L$ZQ؊ZY>2'z3q0HFieGjիE߻s269 :2u<`=Hɚ]"uqKlh+?[ѡdxV FNCṎRF[VĚ]Y}c4SnV5t3lmʢ=i?A,s"  Ei*5Q "7$ nF]8eTz#%>o1\mGKΣe_ņ'Kz!!̛2pLY@ ʫQG}3ݐc4mlAe3a+w]VVblvmCcqxCۼv A.0ߊSrCapQs{Ӂh<6wu&@c vbnfK&!ףW {i>ڃ\RMH`'a˱27s]wZIEYŚXAo?>v t.hK. !hIL/>~* .= ;އE\'Q ->ii'?e5ySs]}\k=Wˑ9a),וiƛO>3hj}?e*xKg <5keQx&-%) IDATs2LTΉЫ= L='I9/ߢ9IXø1^gtD]LU{2_"UjlNzGD![ 'nte!'3 csD탔h"Z&fO}ľLq N\|!Bs|o9_iU{}|i+_3'.V>Ous2^kpzE$`ָl%(*Ūǝ"L,nsFiҬ q|o&QE'e`0 Xh!=W [ꫳjW)s"\$O-$/ zy$|^` zLESxtd!#`݋`]fF!D}ANL5PB0xqXr%vy)C/ wW q~eOd9KQ 2~d _6eR͂)|y*}ofNx؜+=,O^X ^'/KisXUp]Wlݝς!:BRreM, XuT#t$l}a9 On/2c.{_. Ť0$3dŖc(,if띥Yt:s" =߄SZӌO> 5` Q5œhg>q6{p4uEjbⅫmsIH[2k4 o_߈GΩ=n=~^ͥ}&,}材^ e"sdV >{ʹ}T =?=??vkg<ؕ#7aHF^+ľ>Z9sOR6vapQxb4^S .5 -xwM\pYGfqyO{ʚK:)16qJ)}חs2TΉ^ ;5m5c7뿖JHăh<<{TO ,Fe!DR¡S늯\!0,5砄('3 <<2(I p/Sx_:Ew``=w^YwmaJ TѠǠ} 9>FQ:@sVBi%JkPqzxl"M, ƻӆ#6ʄ_Ͳ;̬ww>?apFJ:P"k]j17%81-/K,:kK=E!-d۝XuP#xF/ ׾6^h.3TBl}VK`NxBl:r l;to"[3Pz#YHZi'qmfcWyl=T_j&3kX8e8f=6b~6+8"Ylv.p NܬL%ѓf_LW7+>!16 9I>_ N}'f^:&f%^^td\WZՀr?22㐑'XQ:@;3MvaR!Y# IÔq=9z Ͻ/U|oT:%ׯF_r~iVl<+`X,|^%^\V}I񂀣g/c3v!x `F0)h!i["=YϸXmաhmXd0@H3D; RÓ[8%?)G9y `6 whfI|l- l6 "## B˲Y}_ZK8_ a/kX1Eث#!&x.Bsp< /2>]11=u@H٦p}Hhn'va`q+"Ǡak TY8}P^ӈ~S'Ec@j' '= ó1bPlɵбʮ UWP܎Yl94efiB(2S⑚'=]LŸ+|)ACSa;iBEd#;=)VKBH30|n>=CiUP؊+h:mc0Y#Dcƀī[5n0f ą*\GcS;Z 'm-9ꏉR>v [2st*NVK'0v Ndh Ǖ- YbgAY7mdǻ|aA!xտۄBĉP.$dNs Pһ"_u.dwxܳ&t:FcȔR#B|Ll6A`˲!D !dPC7{-fMbB!BHNzDž]@W4Nfϱ ܾq'ǩ6iքkh4:@eQ`ZL& !D>"!͆{vH4-yMGr=>0B!1{BH8.}ŕw7+l5'Ot&It10Et_M˲!!D UEBҊ.`+ɅQIfd/E!B!Fl,f p { ؕa U{M$ AL*DrB݊%֓;As{eYB!BtDf 42'zoIJ-/1f֨xApfMt:g)pDX"K,lX|Q Hz5=P& @J|ǟXNtH!B!=fO4].KgWI\/͚N]w{drN(^]$伹x@M&"蔑B!B#m`=3'Nxx+2Jk9I1(E&8 wrdQ `0,z}X{ 4 NrR2b0uD.~]cR!B!L4 ΉxbF 5m8ಽl6&8 Fw9v*DH+7;{t|]m~Lr'!B!+N9A PSlU]۬Aۦ61&&ķ, 1 na,lMQHhرϠú%:Q!B!xED(DŽzNxuq0 }T?fygք bٮ I(8ABgϣU]3&)qaɜ B! 18!*89 j޳Uk q?M{k\ L&!Dy !c害=szNTNG@ϫ ?k_՛} !B!bd|8(<=h؜e oH*cIv<`0PքzlX6 h4h4R *q*`3z]eTB!B&''mrW|LP`Obi*3kpaH׸<eMX4IBŠgsfBEe(qBH'BJ'G&8!M'iRR:YK5o/y\fƤTeMCZ.Ǔ8 AX8`1 1Yl4z]o%B!o s>ISY뵴֠(3?@1yʚU9I=,:KBf9I~ ;̼ Ov5!B!00 tDOVz&YX@>y:ǻvύQ B!ޑNK t;+,'UbSloP&>{ /- hn6\]5a0@`,h4R`PpګmYP%FbLn[ЦuA!B!0 ̈́ NsUPWԊ(*?+ L=fs>TΉuQpջ`wu̡0z󡦑B!dB1B=쓴mV&jAK m\mzբE%5Z{gۋ&x5AY'yl6vX NWE=ޒiCmUeA~ !B!$Mzs/B%SSPo}d&ߚq2doܳ& eMH (m ZgQ|ٿ90C{}޽HaN]rvW]")ɺ#ɱ$˲%1  H!I`G?$/A $?Mr80 Ȓ%͒(E\JT9'fڷTw]\~?`0=3U眞~O|4l8ED4Kz>%E^}''.ٻK???wM8 QJMs4M*};vOMDD_NIN+%0" 'P~JL&kÉsiWx>k<ȷ'g>mK;SUUt:|~c??g&Vz0ԇ;~~O~x$"9pM[;҂9b]4\.oMMl N) &`x 'MܛO{^O&5sPixz:"1L j&ʲ55y}X,֝Jax '~ tg}!z||D}]IN;QUI^u>d2ytyl` &`x '~wkO~GГ7(!K?8_t+Y8vFC^ M"/ž>Sd]|]T\.(k%dti[:;{Of'msӇӥ("N-?&;.mؔu IDAT$0?ŏ^gǿ+bVe=MrYL8^JSBUU'ϡn'IlԟX,GIMls.N}TjW&~~`-"">Hzr z'"8H[;INMqCif{ɨ˟}2u+'8"[:]7L)=R )EEr?NMls&ZopX_yoyg_OINY(NQ=E7># '(ZzYB"S= ]8/~-{wSĩ Nv$OO}OSnJMta̟t쫫+  F_;} 19د}3G>׽Nv&%'"DD) ɉAP&0E~źH|6LHN0 _xe[qGOwo~Jr ºމK4\Qc9uΩp Fg>nA><׼c#yp3Q+{ϐ~҂d2,vT )5؆ϻ:K%)  VF?;Ŭ%K#;ɱb15k%c~ 'Ovv_÷t׿;QoR pli8S y{ڔH%c+rFv"EDDf0bz"쇻NEho~IoNct/ 'މ쳿1gopx2Veeu]j`"9F" ("NGvSjݕ0Ɵ-Oh_|;%N|3h[w*~#wģymN#-[UUQ($'l[j"wS:ŀS4R*f~>}}?Oby'/<Ɠ>wbɁ,nɉٔHÉ>OcPLTRWWW[0 گvwɉa8x$S's}'Awb]$c*M]$EL&quutx夈w?ُh6{NTU^`k"bvd>&Ƹ;cS v416te8|y[ٴ.˱sӶN-Ώ9Up.v&(b>:1.oIm\*3g8`!?OG>;\M,̏yE{؇Iíf1Net.)׶sd8`}_쾞~b,:O Q_e8شnеw""bZ5|ѷ} b&ZEQ%q>5ɷʷse8`;tW.pg_9\̓⹧Kv;:]"-o+_k"OMz})-`G:513`>Uߓ_Ot4 [މ}ǼS76&i0TX,& t+zϜh 'Nmط""c>.EvjE1(b*&z;SoW8ʃe_:O책rU_?u£ztyBJOeq~)t Rrȥ:VU(RdX:)57_ ￲~#o? >w£zm"iq?m딗bqҵ5/hj"稪*ʲN9N0H/_vz?o?jg?=7!{2leeY590vzb,ݧ!tMl3%EQUL'p _v8{%O~Ͼʃ}_lguIy \:u.z Rl婉T_wW;ʰ?b| FD|7/~c>_}׆d4k)k&P YoR`.̰Gб}~ѿ}JۤLvNisR 8A7_H[;M&Nl2t 1tHÜreY* fAzj!;m};P(Bu5vjb6~^tLj.e86) }=+މM0H{5&zNh`OӸ2 f8 =677>a@^+2#qSt+ '&].6!b[551㡎94h&R W?'}o[;u;I_ yrBu7&n[h;FbSjvN̓}F/&Ea80)9}OeYFD"1]Hb{'?ssӏkMoGD{u7 I0޼w#6OExt_i4Mg }j4Z)x H_=x`vN .³O}e߇kڽCXyUοoyj(.!Eo4MeyΏ!t{! ȇiu ]-lb?wMueY޹yKE,ujb6La8+:΋8. w"m4.#߲ғiKrEQ5t,c6 'oy1oV?_!iB!9v>m6&b1cÆ;+ 'p/7}w>{iKÉ(;ɩ)m''x4M,ˣ&6oSVk"m甾" 'o}O}Y;ƓO2,;)[;]StMrcؕǾ+>N/0A(-5=+⇞95Z֩.ǖH_ZElF 7x> ׯ0)9P ݱmrǔ'JMl:h_|pA+?w}'^|K෽y)w9δyĥS%H)`EΩA0YaDQDv .N:! eJɉ '"'G"JoJMuϷsZ.J &xt`{gW}'oz15R(mtN ۥCN-uܻ(b<}-gW~ җ@Y?榅{'nJMm#%1X%e?wM}V夈5ڟi\J{JMiXݏ9Jw%]Y)e%O+"{C;t'?p)9Qw΍sNS8|;'%6?xW}ڴďѯk*}8QsMN(NM]cOMJc}pWEUB/~7z=w_9֧{;vDĶ疻(7 ÐNE}ҶN$:DZt8D[4w:j8 Ɨ{~o>uGOf$^?_NI s۽8NN˹_Bj"J[~ҶWJ0|;?}6?Nr_/m+u/c6y!9umSi8q?:XʷsR ˾yED=}ko"O"ܦn&]gDQD9)b2)bVQELO==0Mr_~9e\]]?uߏG4Oը\jܿ?ʲ{u4xAܿ?""{챸wwD2"V^vko7םӒٴ? o? oO~T㓟{9~/3-xKy"}xK=o&<1`deYjDRuGsh)ENMK/:c8y{B|^PԻ^\Bm:s̴&&If\(ҟdWWW9KTźw"-p_vDNu@y:Zt(u*%خp {Jd">کfjdbSm휪t5p~c^W:vzblRvfs#( 'G^dH K)mi5֡L|= )5R!&꺖pyBZcz'$OM4MNMDr)j. /`8: |81CcNvj⦅K[O%i˫lqxpQo^?ECeyrb, ̗孩KIQK&S1.mOW^=-ַ)5os yj(J0+,KY`P"/Ns~1vDY1͢,˽n\S)5G9// mZvXV1Lܣpn i*Np@)91LF 8`fRkXsIQrعP] %"JΉogR* cPl*pRJk9]y睺&:51N>ENlf;' ',O]L&"w]WNO Qڲ(&`Td4\. ns p*w4Eckm ѦD锋cIQ%EQ|>tk892wp{J DQ'6CP%Ml6[&N# ]b)v[;E 2t:$56EHl+wbyBv -=wj"7mDDl6l6cΛ"Z-D}uMl3v v xWM㐒;vyj"a2`([h'Jflsq(_Ϸ8i(4:1ҷ9HEyĐ1T=k?:t._Iom+d2Y2vʷJr"-Vk]CHN<4@Jd< p 'hSPO-Py,.MbP °F"bo_8ׄ`ȤmvV}Rm<|hSuLS%@ ' _Ϸuʝ"OODY!N1,:EVk%@_ '$ '"R쾓NOS]ױZFh;14bXmpމ]R2ԵчH eY^KMzux>'Z 7 4:PSz"9E:>DL cwIQ4MϣKw>uH%سvN`Ng'ǺE$c;'m|vCޛu*E4MNM!Nm76OEQDYvmC{S&6bb2Lg8pd}:ƜHǾKZPMrW IDAT)5 iCE>H9E^Psěvpڶ!\Sط}>^4ϷvJrD ! (R(Rj W~4H[;E:A)E),{]{ +pRCG_))_ c,֏!EoeuR<54M]t=ʇ5)51c:F#=r*N|k}K>Z4;'cmNM5M:]T 4l`tqn>DۡzZK'0jbӏF<9U}h'=MsھhMUUKS$]kpBy)vW)wۻ)5q)]MSWWWgy=a8pbi8^uR);l+&VU4MeYUjN,XVJa8pB]bo;}.jeY^LBZ%ثZ 9topڝ} '"NH]z[}1b%&?vNԄ `<PZϷv{@ks{<5q]䩉\ 6p.<XJU}S(ݧ6HVպ{2\Ve08JosE򾹔j=ncZrl&5f'.Rz"9v"ޮ[;r '.M4DRGwDD]ݥc(pbsSbTUEQ|>Rp2\#<@Z}(ҹumju]t:tz[:kRy ] Nt N q[MMQt۶#JjNMΊKNMfxTWUC}(K{[;MQ4MmO )5QE&`#0Zx6Nue+"ʲdN!=sNڒh\F4;-ť&Իk8gI)4Cr"KwDNn3RjRI멉K0@޿/žM! p9 '""yEX7uMHM&mm.\ zTlxv)|*ܔmp '莟ZrQ'&D"VUEa0\ usj"ioϴoj"f\Jov#@t@JNyH UU]۞iĥ%휪,&b8Бp.M]tD\ҢMKe3PQ-urXV뮉4ؖXVtMGشp) ':vwX.&ڷ}P'O68G:m&A۶M) Kp 'E~" 7RCSiQ>m˂6y vLpCPwMm u]_+nwMl3kI:#*_35Rvjb۠i:E}&/k[)$uMu,Kc8]'>8|a:u2 ]5&n9iuZi31E46NuM# @R(F9oMTUյĦBXVEsKQ%M(xGAÉ?E|0QR(Zb2|>_oesHQbv`N&-Ru=Ħm yj"n-d)4,Iɑl&5=hbuY\ϵ&vMMÉ,,˃)T^ue)51hZ:2RC&EcƔh_(}N`,)QUUL&N.]HM%EZ֩mKd8ГbyD(?Nޱ0Efbf'cG`É<5wIMÉc&چȯY4Jn0= *p~6=ƴ.In?z$=+M^)̾K#M?{UU<[9]>{뛮bb) gGd!b)}&$56Oy r(ÉMCd8Гv)v WاDNeSrN5;]Ǻ65KÙ|~/Z#NDz>H }@ENMltE9I'edgz"&R .>7EqVOsW4DQ=ʓi0ѧо\.JMRm] (꺎bNl K~K51Sm/]H y.1^y𧻓C(n&ʲ;51ԞSmr,c> @n*r@)51Gh;V"?TRC>0KtĶ(i3 spgEQ}&B>j5D]Ryj"`e80R<5;&0iQ&MQRcpgs"-nwY)51Ͷ&źb}=?tp`Ҁb2y\U)v;5h55?풢hfU:4`w5ŦRɮ^! C wD;5qS 51L"5v۹\.c OEQvhri%ؔKX& &w~9F=8bSjb[%&ҹW*N @; 'NӾ&Ωkbyj"` p`R|kc(bRɦm N ȩKm5Zu¥&vV0]bߵk뺎ju-5qIS( @NJ~m.MHPH)5qI]i\.׃]wwEF`[)1yjbMK8"X& &p`@mthŦl6uĹ/зKS %;@ 'fRCt:ݚHÉKMM, x 'mEw@)91Lb845Ru]9{[i81Joˀ" IM\jMn0p<Rŵމv)Mn)8,5QE&Rbb\S,&hQ^^L9dm8EQ_mkMC&3&*2 0vrbn$@;5#MMD9KX,(ujO)6Ķvj,ˋHLH%ؗ@ 'hRMnJMԟ/7MeY@RVV~}1CK^MMm.a=IۮwsMF-*Vkb;Hr&R%&]8ܝ59NM7&'&fٍCã,ݥ;ODvj"8g,c>_Pouh)v^}9/uNM󾄡 Ngdsr' Ryk})bjNMe80Pl픧&ʲ)5?"9/o*@w 'LGk%+b=(HےDJAlKM,˨zӹAi.q8N0b&'pu`ĹL0öR|;kjb[ixSϟ9:@g ҥއ,#":5MȫE]0#Ķ$Ωu Ep`ɉ4 Eٷa@Z.Q:Ltp`p"bb\kjb\rlSz`)vDJMk @ ',mJ7mtM쓚tc&Ԅc8@O֯~o -?-鉺c\FUU'MMl1 ) ~N XHɆ먪j+NtC); (m[kjbyHdRj,˾ N \^y "&vX,GKMl!-5a;'~N \^kbDUUqĦʐ"4MH 0{"_p?$5QUIRO7 p&aps""IjEQ &5~w-㈈e80yri1nLlJMt90h%&p8;)}t! '""eDD\]]o)OMʲ>e"/Ž"t*_ 3ۿJɉT}gхSSN@;ʝmU)5q[vS )Vk`;:@Nfw:mFm ]n{.3QUNa80$yE:%VUDlOMmizSF (b]h7&;5ƔHjd 2ؖ6طkb}GY18i~ZE41NOh+E'EԄp ۋNWjEh&fٺkh 0rR}-wHØ[j`< 'E{{6PRm<5c8Glzi«CJM*EQ#]Tj`D.Ѓ]zR PSm3THrEQDY&Fp8_Ho`Rc[%E4ͺku 'F\Rm aLQ&΀SjmS"&RDNЉ'չ&C;51ͤ&΀Hsj(k[UULS 3a80u*eL&fQeL&#9\Rj"`"uMu4Yc.ifEwRRjbXjZ&i߇v27&qp '.&Xw/Eq`if ΃Gt)5q[:e>s\* -5qRjbXD+5p^< .sjJ[UUTUNg;Tt!M4\.cZED?w4MsCc\S7o9;}+E0E4H益o;ibXr(P#a800R)9me~Sjb>oMMVxWmpCSng } i0\.#"b:nM@W_}5/ƫUU|0Cbۋ D)5X,NMl͡Su]*c?/'%4MVuYx) %"^0 mz</穉ԟ04w]OEةkb:5Rd=ȷtʏπ`̥Խ0h;d? &RoD~ cnJMlh0 :/lllSj,&M'Sl1":56P 8<51L5.vjbmfO4nN `\ ':NMe9讉m ]|խ qw=jRm 9LyDF7}\*ƶM$xNt$ODۖN6&X..^IN@G55іwIM,j1eY| @=5іwk551[7r 'g^OMEsMM$iZ,˃RgDzns08v,˘NgHØivJMDĝS}CNm̮wg_yI4e^?6"kcWUzѽ(`\ϭVkiKÌ&]s7 :UswM\Zjf{&|tkj"[~g8pB&nP8qۀuD*Ϟ;]}|`8 'X0^h䩉!NHZO RJ >(+ :r0fRS5}b+5MHN˴8(v^:ɫ'mVE*ؠ51NM$۶`Xvl+5q-[;˴vjĦWav\j( c>W}l 9@kE%5鶆nSj"fGOMD<Lip 'q'}&v)l6;85qDbk'0CXVwJMlKjNSa8<54͵1Ƈ845X,֩& 'p`G&Ңȇ0h&ʲ\j"1 ݔ(=EHC]S2"Nxx^ 'Pvп"X,;wM9EqjZ4}1LRj(ZDMMNRyr"]S'p`CRmcJQMÉ(փ}ReY|>,5a00[&҂.(R]R]tM$p` '4 -OMTU$^!(6&25=6 [4M((Ɩ  2AJ,˨*ʲsjmH)_#]PRc` 'nf-m()4:ʲ<(51NlZoot>SiA? 'RƶS}l锎`` '6h&RbRi;vjIb`"1> 4H]BS)ZbXDQwNM)pNu2eElk)tS0Dɉtg8Ii|~:2HNhwMEeYb۫Mc> LJ !5vEJ-m4^e&mmި4&LQKj"i'. x*&N }91婉,ꚸLjb h yjb65]SyFjG65I )5k`(=ƔH&p;ʷ6Z9sHi51PN= D]$@D|>?W޷Lטf{&&ɠRvƮtkxA:w.TU^?D[h5Rjb[!OM4M|pdrm`b00\y;Pm`L&[砽Uꚸ-kb 'lE8cņߋֿI EsMM$yd2,h&&b7M0  rEV*sg4hI(Bd/쵽^aƀ6`"! $$@ $4409hfPuqTWUշ>]SUsQ#Yք85dz_ 9B!B!B =PT"Chq2 $ 1MD\Ш_' B6@!B!2(6H?NY45$kRRu(kXY! q-X2_Ž֡8A!fnO 5%9B!BOkb\&k4h{( A-?~߻!d}%B!B ieMUD\0< @@!9'!B` oB!$֑ Y-D;V\.׵3LqmݗB! "BI 7n0WYu([qp5MD m(NB!B!dcJ%ʚBiaBDeMRTdX J,BD@A!v!B!BҤ>k" 0Yqv^@m:*}cł4D(5ľv4|<{xj)Lhpmv<m+|1g'(NB!B!dNc&r5as9s".2!:u)ƬVPvn{F_5]ͧ/Y ׻w=_z}x?\']^>98k߾h핫n`Gvq3Ӹ#ka<|c_r?/`'=?ko{R5]o=L۷@B&'!B!2ggMXυNFxքR \~Abr'8(t(91wĂ iѩ~"ܴ5𾛷5&!,rlʼnK'pIfJw>uk>^zSDKOP)Hld+/+/_]g?؁@sIP B!BȜ}3kw&Ѝ88N$rLOO BeQ:n\vn X2wnk*;8>{ގ߼=\,43°R/^7/^7U:HjO$t|݉lZ̚wd26nq򓉴 (NB!B!dNϚή uMքyr];^}LOOCk qgPYN>ķ 0a`n{ƨӑmKW<7?e{)pܴGc|pܒx,p.\7ן,1q_ov 0^ÛN[?~1X:iBqi }(NB!bo@yCnj.ُBzC)rJv\ϚPJ!(kSzrp]Jb333@axE9ʼnR}[w{a?4 'Z8*>4"N"6۵*Aɼ W #LoD{ﴏ>(v8Jgq3/=ńzӞM]d,.ӟ^r AqB!  R  !&B݂!n<0a&y=ܫM9h333(Nj(\*NL+m>3s>=Xk^ 5]xŲ~xe ^C7oԾ7.\n>nyDRY0筙ߵ!ѿ@#xxL"oG??ށ=q_n}$\2#wzvhK!pY|:>tʖ=E|G; 8m$65GwN(!en1B!L 0”ep G:BB w!KkBJ |P5!뺑u'hP-y  (F\FPJEHj=xnħyϔ15ù'+]\/?c N] 9A(mn?宦xn?}e?:}oo׫DH?M qolw8?Ov/*\pW_߼{ q= '[sۿ~#MqLļ\UxmXg.'x<}~"_~crx׷uwS5{(o=z{X6b.9q!s?x|_ W|O. $0sB!dHhàS74d99):2[ɦHoX?!Ͳ&:fMXK6qH(8rJҬ I!|v?47lZǩE>󞃫[/qqFh{-?}t,V-3 [ًGśzθkēyT*FXl(P˿w^\tZ/hHWჿ}U$<|;pHm|({ \勧pI{Ƹwm&>n=ÿoKBsj5_k>ӕ0~o>Z₋Oם nXi"~$z ,‰L[qTU8\ =B!$hmLa W_ JAj<->DsByMʸ0l[q !dW u$ij&(&''kEu$~ 緼u3Oo<pp_ꂍBL ?ɮWzlV7޵S9(\-dJRo<kpE6Y!wJ:S~#<7ϖvϩ+&NͰI BȓYj !$Äz5Ҩ a @`gZt-k @"Da3˦rY'qx Aqr&T3@155\.1334Mgo\U7=B/3,|gcqb8{*쾴 !p^\ o=0Q$nDlol|c=񋧏k"˵h vQH℥Sǎ]NJbb> 4(z '=yqb_;gӱM?<}oCq9.ֻ_QOF y wVr?{B5TYko:=U^j5>]QxӿoM\; + Ad݉i o8N_M&㺳ߺv47Yτ+q</XQ'-ƲĝOTO%sB!C@ ]Ǘ%Dh "OP`J ZWaݹHUN}A;\~zL8'8% F>㒄 xϹG_"ḿy<q#<<9ؼ\ EZåSrlہ2>={)LY*I!14̬@G1 ` I+Vt8+r. rQ@$l6!0廠M))'BgYBxד(fQij&<ϋ2F(ϭ8̤vY v̈́B{NL`Ik†=8턣[;Xҩgr18~IgcDj J3iXE`i+'qyv='%> =ҽ1#>/\f^N8X(?lgA&'!B2ld'wh8 8 m'pu {{t7f%`Rs}| !d47s ) idքݶe[V%r\hEm6BSq"̾84:7?-oO\,몝sn;Uy_kqr|u;WlC; ~K77>$rtBf-JCBU tٸv9nsĉkNYg7r~O]N`QeV`•8,MfQ!BH„u\Guϑ =x皙jњ);F0$v<̘R$7ή+\G)22XʚFƖtYIyMr`rrBX,ȑ#(JJhR]q# +ۀL߻͔*/\s!&/8uI;Ui-LtзƜ]_^w;+‘ bfg3Fo|#E[hnl/Zw'>>PԾל}KF B!LB )בH!#HT}(Ys!'u$\8p] '̀ax3! @6(eMG}O6 \.czz:DX|wN `d6Nqik7=H6tqw/Z]6e\/] L )8m<'.}SciLȸX\p>zן}T'M㷿T.Zse!B2ͪ$KLh_ZIbSIPՖKQP !2Y;{Z(}t]7IrV߶MB)TTp("NnENi&6B6`#{#b#Xxj2g{ IDATaaAc]U52^x4nHgqG7݄pgr<ʁƍܗ~債jrGsCwVTp\|)rt&ҌݻA /<0sB!$C:@h- >L![A!RKI) X]bfB`g5hRR@Jׄ~bP(`rr}LOOcff ˡ2TǡQtϛQ >kpyPZ?:=GtjƟae\/FtB핫񹷝t~_%݌gtW |m'rSm[xp-&L+Uq~CB! tN`yc.ǗBҢQDRVdE}ZT&>ez |FtT]U{v_>ym"*ͻz[? 4^<lZv{tFϕbNŷ޳xJtJ.=;웜#p ʺy5} }9"$xǙ&l~۶3'llZ٬k.܄Ov6oۃvYeJ:M c.95)zE⣛\]\o %+~!l3ԕ>@ؿt_v=^# qs1$I竟Cβ܆B!B)lDRhD=΢ZG}WJEYi;- *\SSS( PJX,vA_>Ku)ʹv"^YYUWV-xdH!7vݖ}vg~xyˎC|ݸ ڦwҏnƾi/x7]7?6-Ϭ?pK/ĚEUߵ0Κ*tjg/d$0sB!B!#0&ڵLR*{{^|cTւF2MnMX8`*'wme{?! ~5>pMK:a={=_ V'-I{::S8sUL{ /SdfNB!B!dd~ I;>kqԼ&!! I h133eAp\.SpDžp'` 2.9x+}A< f_|RWg5?u;_}p[a"i~\Gpd2u8ة6kI uԔsvxuM5~{KxDx xs_i|}- 3'!cG3%B!dz unjݒVP 6k–THZ[l{xo?n64'eM_[{?o޽_aX0KN_YTο߳sX\0^6S8RM ĥN<{?(NB!B!d$Z}T*M*Z'&+LAyM$zhO+PXCbbxrQ^ cW_v:&ެڶo<>ᾭ;\x.z?ol+eD { y{^tNY1OZ1o8q.]uq#8RpI\a!N֎E9:,>~䣸]0J,tl>i٫ׯkޢ G| &4QZ}xO,s%>=m֊'~3WMᴕmE\yG`U s8s=l>?e{_m's^9 !B!y,T ֑( }4,qBbRThr1qGx'n_8cq +l$&.8;g< ge/žy G㸕s>ś3J|';R qن]'i<^-ʵwp{6غo}Nki;۞O/rOoqsu~%2t{'^733D2M%6\j_,!vs!$9uI@ R(WTJ)98RB:H~5IsF7Ǹ\ (sp@sͱv%\|!&(P*033 011ʹ%ie2fff8&&&0110F\4* B4΃F) P*0== 8  i:vÇ\.crrSSSpdW§ny-w-Oگ4$ n!`/Nv2MP8@oQ b B!3BI5RQքrdJ:5,xcׄm߰ |BH0i(vm>%QbKi[x ˴0A!Y!B aD /4 B! a&Cq]w$3&i(„}R(v߇a݈F"ZcrҔީ(lퟝm?}p;v7%pX҉B!dhVo?!J)!"т%!GxKO$ BB!{]?-G}BglzMr95QO}QJ! $kY[h333`y,f$!N| Y҉B!B [ 0@ m )56BHh|ߏ]faqQf#v|(&sp__t"N{!*'!fLZ+NA 4,h9AkdxBȰ/k.5笕J +A5Qa0LnmPJarr@(yƟ}(}\>{@!8A4ɏfqBAhu !{gc6UJAvBD 𘚲$BH#lDPJ<9߄h4`YOYbrr2*T*$ǩ)9R+rkw?OpOp̲tB B!>@JZwd]]~w]VPZҖvhP#](H 5lU,)BEV?2,FvH/e8N51{ER rZk(uݎ}A~y8{h|_pB)TT~MNg2ݹ0v?>/;׽ 8N6K]B(BqB!dЄ1SGs@Ug)H0u1kͅk}">@!yOh@i vRZѶ͐HJ=HPR #P#h5э(gQrNADem7(JT*QYN·V.Ʒxsp#|N;qrv}B!AqB!d)Bqf &hP)HF0„Q'aKv&jX7m2=$tuƪAifHGB@U ;j|!oR H)́nE0xIQ'Y"0BO\|K:-Wp* ^&նB1P B0Q$)gY23$ LvCQe+Th53R2CViZ*] :Bb/a=9W"fMAu=`2JYQR)c$IFM;U7! B!C懲h6jQZC) P01N }B ,U,ffذM[g6G' ;еՒMgMGz"@+q(! )%r\"ȬgQ3FD CJVPF|Rٸie "Қׄl,fQXQ&x5a?½+.jYar\#xBi B!1AY BR࿀ 7u8{Ri y'VQƄYON21xׅYNFdPB{{Ô ZXR#! ʐB`x RE[l +kb2'PTP.QT8rljKF/2P B'RNƠY fXqše !ѵQ^[ 5t`%u5" ZJ BڌaSvkHY|"]d̈ B 3g! J)8eM fhrR .<<ϋm(B!P B# pB(@ 3+Pʺskk: D唬QK)؅u|Ԗt2e2 Ɛ[ں: F K85Q<gT; !eey ĵyz#l12La5Y6rr)"q!dB!cFC"lA'( RBi %TMy$es l9%e,JUX! Q‘A$RؒRA`9b&؀ ! 1R(tG],B!#.N!yG0(֑φeaoO6_x|>7BA~B B!1QÊBa&v DfFXi[>I)@i#LزNu3/>th[@C!P4GoPNJD(HH(FV%Bq#l)P39>.d!k¶fd@Z)U-a1Jj!;YW !sVךfu}}j8q)@R(ZA+-+NظZn#P2=$\'t% *hH!82,߄PrQDqBGzk[9}[Ja02}ghK_ARr9_f4j{T2[DG>ěkB!AqB!fMhh@AoB, X٧0" L;l #(d+P@@TM' 1v:Tp1)B0uݞbEh2ihT>)IfK2NRJyq"1!B1y] }S*)MšT RȘն2Œmf15RҐJCe-MBfо@eF%>Yq𬴿^h$P Bv} ;_ IDAT@T)d%$9. )›!NjD^e !!&+ݨ&HRPIZCC2*dcUa^5Тک$-8h$JiT*>|?}/#Y)~~JŇRqYӿ٬w+s^v[oX,F$!G#xg5w+Y9_#2N hB~ǕDž#x__@:] RJQBh7fMj]2P&`ҟ!wQGN.#Gt0ØR>>RAhv,!e%PWR]>x4eNB寨.͖R(["*%'=g5:K%[ƨ\ P q]@>7lR`K*uPւ&|G\F\ZsS>;>-ԉq+ƧC}}j]8S' D[ZtRDt*4.]_T%<ܛUDF, -a%L|$Ш(m&sdPDR6 0Ht`*oϣ/"&+jݑ$(U`n$NLPB0?t A>*YLl҉^ڙTY錃řۘ;p":Ew տu&>dz覝}N+kvotA} Ry_5\fC씭 %JYB/^ u(LH8RN@)X2ޢ(x& p4 @+X}l/Ykpǹe;CumXwڐ]:KF-p|#HtYZRR5 )#]^ CBYn& 2NJJ )xvӎzC%9.[OݍHsNg'j?MsT!1P,`Љu*|ň)6ԥ5 wL\FyC1Ԡa{@_nYj~QN{v+ё8Hh'~߱͢K(Qk^4oD2lf>:54Q#mfePzPp  )4r#.PZ#1t4Fڲjf݌CSt=*hPH<?3pvHsl-grYI|M>zC^(r ڑ 5!%V[D|hoM虬4kSo>ͫ/%fW+ӧҟ1HB!,jD=QHhߖroA;P%I>q83 lHvą^Ll'# !<< =Fd}B!HQbS,R6 ݨpu9ۨײNNE{b`սg:Ŗhv[USLIF MD S2xΗmqt2'z>n]`:wGWL|jS֩՚a"26`;EM>h,)`BJSHOKt(lFU+D,M4?VR‘PViSǨa1)k).{lCOy,T#O'PV<ׁ:p:ȴKـ]m-TC+GJuMĬSa6c}o>PeLƄZ!u Khھ\}s|VIMovj , krʄeuJG0߉II8(iaNLlG pO.:k:<>g5 ; N;|CI¬ v"kͮ(1z@Zcڊ G<~܏!vk:2Ķ?uXh{T6) rCa!ZyZrO P )0Xk  qG6;>ɞúa"H i bV'2K'<87Xd8vG}g|0${6x_y.r9ָi=)~RACk=8WV}QJG%Gu82ZR212uV #l;GlC2utư0ْ}mY0MvNg8ϺMeNh hlB,_(@;as1ew9*Fĉ"Hl]glTkYvF-00s#Cۘ8gROB0mއb3&HPZB $,HfDA\"OaDgzq1dBJ%e I )}.2'Ɖ|Ia}kHVns!R ֐ʼnBiͬ A솥u&U b"(S+[_klwX@Eβ& HERBD2gܱ3[M9A^c@HLVkuagZ0{Qwҹ.oSgmJ \׉E& pF#"^W(PAG\.鬟PB@9G8ᤐjlPGFrMGץEB\5ɶ9aE{mʼnqf/ 5:HضzyBȨ#l5%l%h뇐lSi%4%R& v`'ԄPBƻLv4B2 fXI'~Kn]'tPźF;LO)edbϨJb  D.C>︌S'Hv}Ƈ_;c15Ǣ+BHf8A!BYuV )BX!iBYZC PBN !caN6~9R ZkJpCQ wQgmAc"l_mUI)H)k%[ًl15(L2Ǡ8Aﲄ9-$4 2=-u6kLЀ1B@иQLI8HHv6k !dDQJ(}+,.gmQe G„:1id!t:dWå2AȜ!B1ep`'cF0AYA+1"Qú:l~e4\+Lg0?z !_6nJ)h nfA}h8\tfLceQJRT*}!2NOi+,,Қçް!sB!@tp @RCJ*P  JN)#a&wBaMƭPdJ!H͐B[)y#9Y<䷞ Dm-A J%Aql%:m@f oYcG\!B5 HHT ZjÀ)( #FJA  ڲMUqBJ>~؂!$I<@~51h4Yf>k 2C 'ߺ!sB!$d"B Hh!Ҿ ?m aY8jkĭ4)bGxK؂S",ThQkX!$yoKͅNAs+?rD\.6fKxzN t@#5S[b[&!d6'!BH qGJ@).JA+̶Ply'Ĉ!ѵBIF~a><||>:vY(>筞W߱S9 '!B,Dh<Gy)D惸P,5׸D?GAO>J0m#$#BBFbT㚂jKclvWa_ÆkF99& %S[,1w6b}Nu0b.JEuº*fktuuEئN.N7.Q? (MkwUiT،DNp`DbG#5] :hF*uPT^u^MWjM(hcڵ]vXD^l;UUNQM왰nvk>'VF9ՁC]L6i:hB [Nk1~ A.Fy«!QKv?Xx`ʥ˲%%|[hB:(y()hsqDcE+IZUDw;uD@1ϼBz=;S  оtAztӦ= (`%M7PH2c 6kQJigo*,жqQF\%5q5‰Bsa &B;;65!]`}Xз^yQ?A0F80`+hlw@=rvhH\o쁨?lR}xI`N`,ηv;'̷"O$_T=i[˷}sk0\Aik cXH'[]UU/1H;'$j`I|[0о?ֶ'],E2 ,[bU{Pav,=1R\[w=|Z8,l6vMA5EXn J}>p9 }UXϝK=mCt /b.lo "vMYrmwXeNjM8J:ہLg`bk c UU{]Xw"&}s(&䣅ʋzG>3D800U/TB;(BV,/BYl|+E.׮bmt]4~ax0{F:D@ H;—]{ϥ 1 NL-ꇨMT5;\E4 |شth-׎h)v/TZZH' !Y4XJ::%x>~N}(fp`lNvH!c? }ݽqH.C?;9ٟێ W(UZ" 'r.wG:-[$]}5[ҨtDH1xgO=M0z0:ʵŰJu& F7=-fS}uQx_h/nk CeNenTbtש㎖vף^=6X ջ{:}ǤkNhٺ&H'v ƸNvK]-7i蘘 s &'W]NcI޻r Rv;‰JM6XZ^q.ٝw>>»> B83DY#o~Et@lQ:(wi8NBlN9wQN N>~G=XBwI*z1Te*b%J.uOl7ߡ؛*}߄tK>:J΂1iǭ,zoWNt k(H'I펍S<>9ѯ> !71js"g!m6vY0!M3d̀ba'>3gT0t1tLZMWR6#l,ra,ž_O j) pt)tZz1rd r.Ck/_>8aX$Y.j ۱asNC&i^]/}8?ԇ s~Vpq?włPYYu8fO{GԿy Û߳ +;/¶v,mrE>OO ?4aX p}Ls"s28U7H9^ ,ٿ'8ޯ7|ZЫzF ʩ`(Eiз5\㠏]@2bl6b}=i`+jo6IRYٌ)x혪8ιH'\~=zSOL$:pNvsN?;1Hx`7o0 +(X}잂m6/{CҼ¦SFPfus叨$0 bTQR \tͯsꎔ&,}EU }؈#ImMX>V:ɺ&xh.IGИctSFSwg &Lp@6NiF'MUbpLur /y9ϳѧ6RQUw7EUG!FmP;s~#s':D,)sK;'=9MuQt:gϟyV<6C80+uׄQUbo~|r*䊦BZZ6Ut5H{Š#!FŦsb=gtmnCWa:i;jRX8QkaSMj}ϼK> uNL9b*Ebl;SJyU.bU5ǹ(KGFkw/7!#tP(NeY,EMҮ Imr۲ۘ}MKg~9 @801zĒw~,YN!m0w%;Fyb(oN.UCZN BfDHy1rkʃ,Po–Ccmcv.#$˰c¦XyG|9=v'{p`"uQKVn^6S)) *`N&)mwnmc87 ^}}ݮ4ϰIbS/?GNC![&wR$?6GINQNVp/:y[Bwha)X-ڌb?Űbu&tN1¶1%Sq;w2ػ~C=/ & D bT)NB] kϡl(1@ V]z10:N:'>Mu5ʏRxE=q`N~뾋RBvqXlPfsfGѹaX|TUU#NCE]m" 6jsCؔ2oG0`'FVo$9REzȝ|ch#b qX#_?6Cwڧ&n. 뚐v=9 &Z}}J?-p`n'yY>0>`Bl҆B?ꢗͽn_HaSkz6,nuEaS0aEcQdk(k 9!H=g[恋uDLl6b- u@YB15=pkBҭ K颰뜸nSsjK0õSTRHw^eP$zu<p:{M\g ]is沈n)cMpQN!DUUh;'b +(o3飋`]i8qʎ%H}e9y@! 6ٹ}9[0?Ns5˧m9v{mX! 8}}Ϲ=d5r)hwGt%x:M ? ՋcTRT`Jmp|X1O(*'kdp `f1ƳM\g.]9H`I0d7+g\p{"CEN L%]]ԇ;'`'9/JyA c'}ט &ld՜}uQ#zFw5&`,StQ&د1%%87H`~v<`b&ꭓ`b]~\;'E6]EpHξ)ꢰv96Y~vj@a]fi kC8v< PtM.{ ^6.t- W|fJsqtNhpSa;鴤Gǘrn |wkwf"GCrڧqSؔvMkpK {; =m{Im>NtiP@^ӜrUN}X|t? asY} (N]l?}s cQUBfn'(g'p9.þɹgF:IN 'TX ߩdv6ceْ΂%1}2>ZpvN,{9tI/|9=k 9"`ź˰2웜R>@1Ɲ}K pt<[Y2cSN&r,p:i\,8KE8!5s 0\S+g st<|s '$]\9!-og U͋T^ gH)y5}wSNAMBֳr@8Y0aK9qZGH X|I눣.;7 &l!v^K۽y#YyNbv]K!ۮcUUm]5{Uˊ1Ƭ`" cr)d۾_'ۙ9#`9Ha, H;ar{S*m6IRQ_G]5ܭ 찢nZ]CA89bv:9׆91lL) cR6HZϬ~ dr:_UUXH'Il\W+dݪV 895a{=`]#5s;6+vkDgc8o,UEMN\B'Xr@ ((xn{sNQͦ鴶sXu=cI %{i/-.Ž1nľK6wN* `skpg[Skwv~0SBhEL#Bl6l6m8J:Ij|M`MK2<@+ )龉s`u :'`'0xcз2l˒1U-65 D8ػ.3i8!iu˰\i8A@˱g-p S^}{\#u (¥lEk+Χ&$~6!`w*PR`b#}LuN7scfia[Ěti̾M:_r1?'kv: X& [NJ>7ޤpntc:N\]])ƸobMlƱ`~ 'xL;o[!H% !t*ru#I*1b^doG|[Ya7 Xؗ)㷮tvY'w,]5l̚-zW81`yp`XV9_Z_{uMl69Tat,ZhsÉtY@Zc$R +{[vM2(V5!mM F:ndGġ_Ƴ;+bA鳨B%1]tqqʎWPM'!vN'1W%VB 0stW효jgu;==K5Nܴ:J!?=>@k3ǂ|sf]WWW{G-!`5,~wWCvNDIqټ}@/5>\YЯ}V4qHV1vY1~٨*EqpӜϣOu_h+SUU՞}pئ}}5~B [e< 'sJUUͮo]WWWl6*讉}!׏z߽wޟ(ȱ&}|_n}XGLPE(Ή9v`sN*\(eS|_*k}}?LH ߯5&`i+E4YvJ`WUvM=sÉu\V4BiLf,SJר7 p8o4v,S|4 *gggשj88:??Xݪj1h'WVV]Nq n{fffꄐzsqF az[WRm2!5Bj˹2(K495J^CX,6h7SՂ%I:4ET0`,Yx ,%-I)m__Vy~7^0jDUU5UUD"qaV_=>>UUB~ׯX@X E"!B驧V1WF&cn8ݻ7o| p1FE6e B BŌo߾cz+H\ޗ` OL&+:Z\,TUU-E![[[@ q\^oX>0h.!#o.A֫WuR)8!T*ŋCeNXC6 KT#Z*Vd6Yh,RJu-#p< MI`mP(FCPUUk4/^q}1_  !d~~b,U܃1e9(IR_SR!DU+6K!xҬ^VlK3NmBcO>h8fHmFA1g2Wo6톝L&%`0؞{;8zty]HmV'`7͚]w@8Z,N'`;޿_1l8 t3X@ 8tvs8tps']`H[[[i7!hV7ԸU0m 9Z3+gN=n7rW^u[5d`uuըvww?~kcurr:l$?d|ÇKUjUzڤ-w\<֖ JBV_%>0c4͆mmmRhuF:cu1 }MvbKT7L d2}|lcuE6 c(w߲Tʙwef|> s!PJZV;::w{󝝝wh&m\-e^MפlI ^l+@|qM3J>cZdR#f{_QOq<N&}wES׸7۸_uYE)eb:NA[ %׳j\cuJGc/J<<|uuLn!!O1WTRkQjo {8`%IX,6V(jZJ&];QMP]v8Ùz`t߷)2;p 3y w߲T>@ g1uoTJpl4L0tI L Γ΄]U0vc|\n*@&ުT*=waY%zxoPJ`;fcrm:q0)c|i~F}Z Ùa:{{~h΄c1j]Z\\x<|FRMӴ}>W nri~{kǒUSTM s=Ho}F0Hv(D'I$='Zq1ƸFѶ:֚n-=̜xrr$ ozCq}_ s:-0ƸB`}t<h`),Uɲ:nK&.//~Ù`l}L}Tv?vwwp99Ùv6n}{h3a=4iÁV gBq1F5McN-A)efkZI塖dYD"n@d2D"aTyhSXZp&>0ƨwuغA:asTCz0 1:O xzÙRX Sv2z=(Xݛk,09(נ9ףiZ[T*z6X"JMqS6 olltw>;!Pv!7OwLc`RǶk&ӧg,O~ t5Ƶ~ɛnkkˍL} nnn2n B1[}6 M|~膇fAlkww7V_bY&uK-Q]_V07WҬ>w-%ɀ$IE9S¥iw<Ϲ\.T*}y㨦iBH$!{w{{{,hpXV}G[||03=>l]ɴ?jp8]^^~Q)oqVK뚢(~m'''\.X__oY}-f B۷*s9aֈm`2moo%I*V*WPeV`\.ZYY)[}h ܔ*!ɶkzW*Gl=F罵ZvLP(} ֚V_ t;dF78WUիoU5: V_1F߾}JD?)MSӞ{۷~;v!BH8fi22N !sh^^^4Mcp8̮?&..r{kk˽$4>|07ߜLI/8ɤ '| zZ' |i~oos}4#muu#eCwrr( q("gYtqqݍv\Ƙ;h'L !fݴϟtBpB M'%J5Jgjk <K;c{{xh\n|?xtZ8CC0N61h& ={2R6 KTv23Օ8Zjx|'iYI$MBHBvvvl6irIj' )L <7J)KR#‚#ݔL&QN 80plnnq4ֈ>, "\BH^gYR`d>7rJcwwW`A,>cG.Q]sL@{ffҠE!*!Ȳ,fٰ(M,c8~_xq#҃lT*;_pmkk˗flLC'Jl6O?yAs1 d !1ooo=ض8~ppk4!.I[,>neY$xjZm$dbzdl#"g|M0d2)tۉ&g@:{JGc̶NNN1ǔbcfG.!v~E2/_$I*g2w岮Ϟ=ٱeƘT*Qce9$IRy~~@X</W*ǏV_CdYtlzԋ/ƪ n 1FeYym#DQta1Fdv¾yzN٬FAbj |2y&nRc5(K'''$Ll>",aWWW2L1j///r9\n Çazfj:1,[x<^e9?_5fJKBP999 Zlmmͦ;cc$-a]/]Y`rmnn̾oooɤ`kL:As6flX5ϊNe _bqӱXlwX|Ŗ3%O`0c\0r`ZOg,=[d fR%ŋ3#_A, x, ` YCVcZV3Gql6a1F 4z,$v-\x0c d֚(r1Cf_ױ:׋a7~ы/l5_a$<u:.tL&#kkk`ORwٽѢ^ 8|ŋA_g ϟ?4 7j`UUUC sv;<Ac*]B;BϟW<L&H$ltC)4Mommz0vj҆38|^EtMs @xygg'ay1]k+++i۷ouyXb~󆱵NR!?P?C0>:1l{!LF|h|I)I`vvve7nɼ1(zz[hONU_.xJqTO1B#bdrFv@ ӊb u%Ƙԇ}rLRbgHFYtq 6AN1Fn8v2I_C1vwwZr;ʲ\^^UyF$eYj~嗊Qk`ߟL&>WUU<7٬'j'(bXeƩxjlj{EQDUUrzOK31,ˡN]Z],kt&!pL@2~Zf2&7cd2gl&.zd2$%i O>տ{SΓ9cf2!Lb q{n*oߎE*YCϞ=$=Gg 򎐲,ͦrfYʺL 5'effd yT*\.7%Č\f1xJWAB$1f0ƨ"3 k( 9==ҧO9zӧgrY{$UV_W\J*˲c,S$![B9e>lYǏ;vëW$wvv}8Un׃$n=rl6yS;jQdY8Rmmm~"MCueYv޳Ɛ-`eTzƩ*v妬T;s:p$ٙeS$a8i$I޼=Z5F7kkkݝ&6z988s(R|>G\'Iֲl12;JMD!_T*%2ƸqsppsS'<,͆}_ &2X(JpeeebN/QQFku>|PJP(dZG* {nzlY㟞6m7STb VVVn;>>-ta7b]-w̞1 ELچ*JIPUSPU*ϏͿgR m\2f ~I &?1&iic<Dl7iX,VORZ=99 |7Xґ%;!EU lSCnRJ&mcU*褮lK>IiƋjY__wp=~v||L=V_˨ApԞ~c+uYmur8ڗiŚFf͠cmmiEwaC8Ba^J)be'8??wl4iɤ b_+fôWm6¸ Bw~~/,ˁD" }l G>af2 @ONaڼ\.X2Gȗb=04e0 nggqH$R===9$`l~HX4f=q1)Of7+++P(Y\.#㰹an -I p&8 ob؅`O1w\ֵ+H2 L&LPJrÁxia6$˲˗r@(,ˆT*%V*ÙcxeٍeѽzJosRoi`7)c ;) ,[[[9t~?$՝ .R3-+IRK&13Y |"De*}WvCjǎ<2dYZ]\\xrdG ,c{_9b~ۮV;2޾}7 KпF:FiX(KKK8Pl~ zf 7o޴"s@$Iϟ[ —{Ϡdkk],'v CgBCdeenq2[4Ƙluqm3Yk_ޞ7b@|M #gڏ؀,Adטz/ ] DE'qrr1h?bMݺSwڸ@޾}?zb==~Mhb O h?D"a'uEQ],l@ϧ"p!Olc׆:ن~': ! |ʈN} Zdwwdl=w~~T*پ3dY1۸&2|8A8|>azfa]&͆c.g?T1X,{'zzǏ:8Bѻk%'I0,ˡH$bl㚪tYn=<KeD"U'rc~Nvh\<<<JSY@ YV_G/?NOOѷ$LƵ?~x"3t/ߤU-# h4 (Ҷ?<9n}qC+ |^z %fh5j|7{{{~b%+0^& 1Ƶ+7@[]E&zEƭE)UuO1J)5d2ONwیH(b33/ Sn}Gө),֙r3&)cRg?#KPyvvK)!wO%EQ<`ao|jKVwN$}t>}?uI-..^eٰ$I3F;0cʲE"]ӟĥi:?H4UUU\^^ƨi///UBWf1HT*ׯ_G Ύkuu[m;0@T J4h?'Nu?BD°\.7%I} *M n" c\.w6F!7(BH4eDU(SӧYtJv$&*BTeYB2Ç|My1VeBfޤfr B_NMElpdpfA]knBt&;K 81n/fR(ԢĄ$IL&$ pH4KeY2aVgrW#Eqj5 Xh[- B0;n," rNb(ت`#9 {T*.c (!ij3tX,֒$]xj,ˣL4$~vww$]g ,$I62io|^d iffjoo u rh"iY&N7]WTr۹ޤSU-놤n4<IFэ-H.zq5_\J>/G rMij5˗-cS+++FwJ[ސ&MgCCdBKKKE`wܸ!!%WVWWHAq:ˑFl!B  o-~ilDl4O!rA@)ef,U/_J%3ZM~-ae2?]Tc#ׯ_ 5RQwR)-l{{3bXwwP(^]]ճᠡ߇t:-7oǀ})%׻ cիWUY|·MAvloo%I2hy/P!3?!~!MOO|)G)GD",cd#N4McRpZV;>>XwnH|>:Go1U/_W|WVVm8Z7oHfH6:r91%p_MMM{xoo[qnj dfV6dBLfi_TAn72Lax<~N'aTȖƒdggj4,Q}uEQxKTcRQpmllz({yC[/^@1en+hhM.uD{t-p\*+h4ϟWFӡ'vc6Glclnu9 5Ⴖ"n#NclWcyy~(n׋"c:=R( JӁ+ pg㸛O~^_ UmS[`!븅yo4r n\p8L||0E^ߐL&dgggqi&5cVWWہ@sBqzz 5pVd2,,,1L&NP߻"Cdwqqq#c/km6??\\\,Y}`;䀧 rc{5Mc1jfK&BV9=ϱD5lqre4MZ~V|fYy l#H6hn-w=zd40BE( bXѣGϟztAPe`@!ǵΐ KMr1; .//]kkk@38lBݳгgлn!pv8 (ID cꪭaAѣͳ ZVyJS8,sEQ[ٙP_QUU#\LZ\̌mCML?KI677]`mWh˶o晨';O6 KdY+>xxoM–_k[>WWWϐmM8Nk}*^|Y_XXXfaq-oʊP AnC;xU0\P< lUU+Je5{F.$ + 9ېeY8??oRJ\ !p@/CzW1,zalyme0cenꯁn|]9\(^n|2ƨ] Y>ޖkMN͑dBbW0 Z}MxJv~Pm-P8`XagU*/Ɣ(CAuE!y\F7}m=֌08ADddM1iOb1$IҨg#b8D)X2&7;;[Oӎ=KR.9B^6tr:(8/6+09 BPl㶑| z@Te%SGV~mC}]9- x(m'\ZZ2׵T*Vf8xLMM G"M|ɇK4"G2s2P1==]nPUUfuw*E@r驞7~,k>(' |mwvv|zu jR1b?dl`1Oq0ǝ%1 %rAIέ/8` ы#3mi "quJԂ!p@~jT2P%4֖[aAqj d0>8` o޼iwhV*ԔmwVmmm8M'Y?3ڊ!8[[c]ZpF3ϑATînF8``Kkfls6p07o޴yg1f㧟~)۸rc ('OxmȲĜ 0 EUU>b͖ܟ~vmq";NF9`J"Z=6KUOن(T?8N4mF"SG. Xmhy FiOfalYL&dV,#T*,˦3sgկynÊi^~mr˗5Bڝ1{^#XM WVV, @Bi1fﯿL$ "!p)_{ !fJRl{mZӧOBVV }_CHdL._P[A]D"驩u# 1T JԃFrx<2d~~>QA2*`q݃k(v8!v1 m%IAϛt6+hAt+Ѝggg UcIgU* -ЍޭG~)I̜sq(0Ѝz9c\0%r/(!ptXm{ gB]Yp0F~=(BQ!p,8X?<<y,SbѲtP𨪊9+J^]]G1qo-8u^`xeYZZZlH?88Ίú6]méy.C݅B!mgg?[L&de N_np'3P'y#e&p mf<7TL&dekV%..."p p!Ls ynuuՒvLƏMT`SThiiɪlB0   i3@>\A\K hGCC(RQJRfi)mѣGxyA\D’s;;;> hCØ]gKnaIqzzA EQćޯYmT*JÊFUUZbwKnP4KM`@S&daRDLYd2gϞ]|>b߼eYŋ8cF/Qtߖ`,Q0N8P n~bc1`@O,j&h4qRUU$t ŢZzNЏ oxK$R#?_G9A;rtt䟟≟rGя 20Ԁ3ȇ@r\[b(BL6E Js! q| tpp#|m=j~T0q}6R@r\띧3遣hV, G)Uk^j666MVd,Џ €4$Isv\~ qt0ˆ1JWFX qg lI;p6*lCQ(MlIH$Sfw6:n蝛ASY rwIՙwLLiX(<~ '4z-Wi޼yUX,"Hyޔ^WWWed0Qqi,e~Xwv}c-L$0M8vQ(C \Tb~ Lf.˲eβP^wF7#EQ7g`[G(N@nv+z$I. @E.,,t+LSR m,--yJ9 {{{^˅ L5L'Iҝl1z*kyyT0ѰT::: Z_^^rgwsV6'LּrLW(dFCbj1%p@} $B Ry[__o|m|EQGm8NDX,fLCl. #vqttw\fml?`Rx@^*JͺjhcNzA0JyRq5ogvsfo@qz杦v3??1F ,j5vM!{{||,LMMzNI8F*pu6!^3V%>~sz@,s{O>罱X̴lT*y<\D8m̘^@8RUHRن(xin3?`0`P(D"3 TIRqt}}CFx<???K%* p%LK1 @'` cZ\._rmz6 Lgt?ijHcM)t ct~nR% IyM0 `8T jfپ=۷( 0$0U'hx%^#acB(LjWmCu$K4H$q }y%tF6^zOOzc2^Q:temc|B(m "Cxggg=py7;;{x>_PUKmn߿&7`M_DQ[1F)T/ǽ^`@`v2}ŝlǏ kIDATXtmVK=99A0 0v?TF—;1F[U'ٖ(8j۞y#ۋ| xH~ !q0GGG~BmlB+bNiZxߞf}䛛*OMMmBB0 EX BLm(T*=kF8@WWWX,fJAztMsJ¤kX(cf6^s=ɓ`88`d 6vww {gC`p0X,fVAyu6CD"fVh04Ex=EQ+++wP t~uªm4ͮaZ͛H$6APߣ0S$IexNJfIjlb8Cb؝fM)ewRO$:pxVqɤۄ;7r#ܗmD"wRDTUUq  fuj <@988nF*$ΡQF)E* 、\\\xi)\.Y܀ BݬsU͸\;8of%s͛;g7\.W$Ώ-ЗNkӲs,wm(Hl^0xЭ"0r\.ׯ_ݼ}> 4: j Y!oC,lcqq1"ymSΐ]8^1yN^7j3;[~Fy5.?(>[@μrNA! phv,GE zti6HZֺ8BX,+PJv{BL=?shiiiBT*yze=}xFcP(_ b1PQ^F7n\.Jg+$U0(*Mm`9== Fˏ=:jsgggޡwmt3uS:5;p󝜟/(:pF7_cjH$R_^^Vah+8+fPBmZǏTx!p\nSzWWW}g5|xuuE:-Jz6!6E񭬬1gϞ<ߒeykSO׫4B>wbGj6w4aqq1vj2WU-"@8#-1U(6FɈϚe^?==ǒl68P(͜G!j:tD"qA)=,3q>H~rwލj*j6w~~~<0 `ɓNm͛7797p$ *,Z cµm!D}uR*J2̖_/ @fgg.SٜfC\B`dR| Bo~-Nv?i}h4\.d}B`@Df^Pa0[ [MӴb٤TJ|cB̐?l3jGa}<q>Q+++Q'CPf73$pzed+.L4 eA{(Et^J,4M{l6/ e'PP̘~0H)uGSbn2qcu71ŅO$[ߟҧO!m2)tIcX₄j,Ue2띚>ﱋ `״'O_ߟ|Bi$A@1FE1^8L&3 sssǣ> dB Y^#sSVgpcb?}{Iɬ#l6zy?D"0n8&D2hVզ|F1Ɲ?h6|W󇣾&1!zzڴ,b8L;]YRI| OgffnA.3;Wfff~iO0N fnDQvwN*}I ~=??˲Q?>}bE>B}uu5 Ϟ=ɐq9+ڧ9pi(GGG!UUivz5qL&},//yzc6Q^C ?4i%|Pn1V)9!8&E}vv&tb{pz$)UPпv^Mn2ϭ P̉qUi<2PEE%cǎ"2՚fJ@a^__-! Ö8Eߏ,PT$G vi~#WP CQUDD5#d:Z(PT# ö5#{dZBۋ f"ي+d2Z-Y(pPUAt:mnt  8ʼ t:XmJ."8s=$ l6-Uu]'"͹b˲ (mpH-zNM^{||<>^%YH6/ SAN(t⧧m!qfcQsuu9%YH6t]8Oe߿QUo8fVN$ǜs:n-su,;s]Dm9LcI^08cl$-|B:99 :s?֫=(D$,--:z>l6 V*8ӻZiڸmc$$U>}z駊 x0VB GGG㌱H(^հNfo-jEQ6M~;Pзl@LjH* KtKNNN&jZnl?]ZA`нtLUx:>cuIUհccceYA+%Iv]Wmۑeӻ[ZQ'9j׿^z^Am4-Jc;Cè$Ici Ű?ɿ j,Z Q@[޼yb$xшM^^^Qw eYBpע:AEUL(j){G1-jmv&''/k^5ȐlX(f////vzmK@!alQ+H61 #8Δ8Ad2y9ĪlEm&8s?ϵظ> Rf3"nТlz8)IRs~עu\ H6Тv0!_2 #\V(Iqru@ E`C~ZA˲jhQH6`ܵmZcs.IR-j x-j D6뺲$IvR^݃dF|ڶmIh\lmmY^WT$fF[ζѢvV ]wעV8hQ;b4MK\.7nJPxZ*wvv{tTf {6C[ԆB!2MzDMV!ca_VUuc֮[0|HE1Nz +QE!rvz>G0PX\UP(<%@yꕨzmOGSRi8`jӏu4.6H9Qb8OD'?v)xh4, ~Ba˧>WոT*M1ɹ1or|>?l b8/>H$.z0pᵳ#Ȳ<~u,0vvvݿ!{ N|~U+"MZ "IӴD'QeGRh0Q07sZurv-IGDz!J|* Op[_xQs]EhzTT*-Fc9=\H6qyhyyLs>fv-!~imu,0Ey%M74MKt3QDe`oZZmO[˧ 2Dt󬭭 T+JC677GTf8j7ϋd`HںƹeYda᥽c?ƻ}۵mw?[(&{qndC$ u{) ctu뺗7H t:}u,0L'1RD:2b6vL8Bׯ1n?هkveӥXBa2XF0޾};-E.S78n7Onmm}:p%u-"~pAUx'`>z2?;e'''cڰeT>l6Re?9oZNnZ777σ`xV[[[;lD$mFU$獃esb {WRhk"\.7>]ٕvLa_]]]ͧR\u=۞0~{,{{{~_ :99cy^ոJPnF]Rq8P%hŋױ ^+a=իW Ngۋ9ۛ[0EyI0Q%@Dyļ1 #8NsN^%sl~dDP/b%//9:Ajܧ꼢(GD<˥Ϡsa>uaUU:vfra_HTU5uaoX$NzG;{Q14M[<:Ɛl!ι9{5>sXQUkkkג$pLȲ|:B{ dR]> "t]Oz}} EQU X:N彽hb/㽢r^xq?W5+ O_~v͛7lvl ŋ`X)E}D"~h4, -"px6ɜx˧l 0O?T_K; {*}^>_:l6+ҡr<9 {au]_:/?  MUX,v8θ(vzYӴXbZL&d`Qm˗ױJ:.Kn~iڂi'~S%I&!@Pxɱ/?EQ^>CD(H*2ibT麞þ`Ԩxrr2{5>gmmZ$*(T*sccc|>aiZbww7qo޼ r(o6ǣ0@ gJU~AD( u}%y adU㎮N0u}T*M{G;Pa,\__^IRM"jvbeeim"]םH$^>CDb\Z^S%fFJwd?׆.|>h4Fq_&yo«WvƁLBvXڅdcD$H$^2(8t~~w]՞Ǵ4;;;(ӱXX޽{(J8ɘi ; J6SlxY%T5>o~~u]Rec>RDDQlmmmY^>0kB'~GD<˥|jggGPU5u~@DN~W,:cLӴ7oE ibuSDQh_Psw,n>W*qQ.[mg燁G[>6D9ױF%QGTUUlfeq5c$}\4ŐG8>>.?QA={vuSD})_T*닝;h\P:nCefGu˲l]ק=v$b8N_y ݧj\Q`ǽy&<-f5MK~z`,C\n8 ہmzS~8HӴ1 l6+w:DQOzJ|P&g Øq8֖EDW_؇#"IUx'&XF%{{{Qqs:54lb[ iˆau%=jrr>~~>ѭ0p'U[I";8H0ǿ2*.Vt8=e]׻6P([^0t]_J%b}:d_T(>f4x/uҦS o Gu=979ױ@c9sr]X,۶S=j "⚦%vvvl7I*EQfe dO@5D+ldYX,.yFX\:>>zߡЁ`0'^L&I^#j W^'{ r{5x7oބX@_g6x$0Vbιu,_H$"aZ. DDC1Oc @ j5I$AEl$( 1f@D.}qW9w mNtApApw +jd{{u,~DD»w$Yy(jz.H$| 2ivT 8mێmn$qFfZEϟ?oqΩӸsDpyy?тxppF! "O0??/LLL@@>M l^dsBX{[Ah4챱1lzщA`pvyyu,~۶O,ckeY看(%_KkYHD$I8KD$˲h4쩩)jɉˡ_NWA8[[[3ŏl|.y,# $I§ }(3$t6OctT*H_]]u,~dbjXm>m[EQfcQP(DZdYvq[ ۶qh8zj/T*U:?Þ x9cfZs9N4$Ih0V/zƈf,!Qò,Zm6ܲ,{|| B1f3ȋ8߽{m;011pGf%i4LŖ8 ˲[[[1u}u]ƜM,˪ollTtWR4^2 l )"޽{[ XEUUY<亮/P(To65<vtqd2Yc!"X'س1$ 71y1k,,,\-,, ݓL&c1,>MӜTU5c㸂 T3 K` IyW'G20ư'`Jh4%n}ʆOE,cQ3 bO{ jɲo.$)dN}nh!t2D>BEQ[2j׌1灧/V5/>~q{{۷KsLȲ,&P}M.[\^^p\.^B1k6 Ǜ&r2<o{bs>8N@˶ 'STH&XUUj ݃=r7_pzzm{^ B~]SQcUs$1?:d٬<>>>j$A;$_&:#b4͛v~'"^,dGDRPXd2X!1"ryڲ1Ax<~A en&9c|>?iZ\43J4 眓ˌ(u}0 =FT*8p]2xSp|C\9g۶stttKLp^T5ߟmZw}דvAxZ!20¦iN>BATUcOcv ۿ^+ OWWW8{eD$jNltM.eyuF:>gy>SW<~yy]bssDD$JD".SU5~yyy#>ED,߬JAUUs>/bNvvvchruuױ0ndvvv"J{˰Peu]]lt|d&\=}tT۩!r,뺍L&sW޹]Tqm7666z>\px$m[.Xxnݻwp8<9JRx(AIz2L9h2|~E31,h}@o޼ /..ηZZ*B濇vcvxx8iZs!J5 `Tp駊q|NX, c O;TT"zdh T6$0:[__pC}u 6J\2<:/999x%ˏƆu,[H6t ATUAXX,vu<JUոaG> X,.ɲ|\:?)K(^ױo!8ϟ?qt|$H(kww70??ӧS7i u=h4?>UQ=zZ__Ǻ~od2UBXJ$Z x|6{qVc0g` bhRSB5y,e>OEHr]i{{%b5yˠ4m!`d|>RV#d0`do!X,\__/FѨa+;;;#BZ݆T*u)BHױ *EQVKZ\\y |HU*id2iyψӈ/(Ab^?ں:?!"`q}}ױtxXL$iT*e`o٧___O%C$B ɤL&Kp8Z,O Xm۷ˢ9dױ J2799yDF. "QӴts}}7F/HR'WWW׹\.EDtD$ lmmaښDRiX>ױÌTaF8?m4G333X2t|w}W?::24M[u}xY>_h4j?ڶ=fec(>227uo߾bc}~r3$3{{D$ L&34cH(JD"QO ąױÍDepyffFbG 7~*glll| B|>']X,:P} =;;u,^!"u $3ΎiZ^WWVV/$bͷo%EQ&`8N`mm:nޮ˲rqcBPXp8"JRmj:ukkkC>S-pίRTXzݻw~TXL@,;:hPV6E x"(#JtܓJNdY X:w]WD1>|^}OD$I3H4k 0¢(.2ぞBqӧ'H:~u}roH_|i7sUU罎R^xtnnNNNƪjXX^Řc(Vq24-N_/j^+{{{h4d2C Me|4I$# (a “jܗL&WMD< #H6NOOkmk[QO:(Je=999:^""9 /)^ Ri1bx}qzz:h48^N8VWWf9Q(&WfGvKj>:nzM(7Ɇ(?~@_1Ux<~dfT*M{ @/Hں:RKAB[mq||eM<{ K!F6O$x!BLґJN8aEQ&OɲgHdO7Hj'OadCӴmeX`DґH$Cx.:vd::A9w%IP*t]_d2X>5|lrVuLc1 G,H4UT"^PBasD+xömt]EsCj 0fut&~*G:~uu5ODcxT*:AdN4I^%D$;H$$n taF^WVV"/ sAFT@>rFR,c^%\\\`Ԑ؛ׯ_K,>{ aP kZq"u~K^:u,Re²vX6H&D"0.d2V$9;<<@(^'''c1T5ڴu#I:;D$ض=yu,{l%4:O`caaND LAtss3FQ0u,1dtu]G&x,˒:ۋB:tssS=^E9֖u,lTVgRԩױt`(d2a;h sKKKXr-k"sLī*(T*Ձ`0p xt*߼y|~) 1lONN^0Ӏù>}ݒׁhVL8E,,---Q_>y& xu}X`tpѝH$$-^FV[<::BWeH:i axFK6EQl[sWR'*ʜiwm%OIϰ| 1惤c}}ضw0 ~'T5 7lvEj3"jZhccF'F6%I1%79`0xT3D$ O2̉ S_n˓dCX,S_6IR#0,^yjn s}(H1 ]קƮ<%DS@/\u_|i{˨;zLUǜH$2H$.[_&ce? 0˩'&''8V>xdkkFEaoo/94M[D)U__%Йr,--5AຮIU RW^(ʄeYV/b[0MSbf 0"x j$Ǐ+r;`y[1==3O?.cT*M{ u OvCQUuuJ/KFMMYґL&\ם$"Okt kkk׌7o}0͎qέ-abb9CʫױaXNy(ٳL&+Y=O6t]ן|I@L&MS%دkysNz[%~d4ϟ_}H:zox$W&޿/[] |춪^\\Do?HP( ض} BPH$j6FaX̒l6;39mjfBP; 蝎_+++cfmSP!"H6MSvG4JQ0t%IժuqqQGs> "bq.J>[WP(4+KK_jKH:z  8Ntkkkݾ~Z )Z,˒i .ᘦ"X,f |> } :ҵd#ʢ(~{f @b'z}15#0P(۪Ƨ5%ZEkzzyssSd2ַ= ۶b"tkɆ,D[{$T9z-k˗v/VVx8>R& X%+@ՒSUqέ@ `ʲ\k:jqu% "P(pؠt΃_DtJ0Ǝz R4mYV/;U~]ݲ&" ˲ܘsKx<>˚EӴjz #azzzi"yTeCEIb0=[?3333c8ښٯw߿2M3JD466qaau\0XcR+jdzz:NM)Ax"{0^l,`U B˾49wc猱s"rtV v<?cd2yiZYr[{{{68w1^z%6ϟ,𯎓JI,c,$u:.F猱sEQO$I]׽d27^✛0`xqNcŋv;RA}˗2r[YY]&bXu]We4sURihLITKl6C.DUUwww#pp IDATCd̶ױ騲AD\4NXOc̲ev E.u=97{xHT,mooo#pZm=1@uT(JӮR2v; R!3سg>Ǘr]wsnR3J(2ױ@zJ?6Uca `9<<\6Mܯ j` 8Ĉ'Oь˂ GuTN ӏ/LZDo賳tnPCT*۶$Km'D$D ?<<,j ojhr\\.Oy].!@D,ڶ0 #ϸ󈈛9J0hzdP(̈ˌbrqKR/^Լ϶n677mb ժ^Q,EQ<׬m9B0<Θ08N6gΚҼR X(ɖ@ agѾHM̍C/n= 41 @HJz c6'/bbxRsPN&rxx8eY>W6=d2o T*Iv?p?A:71q]12L˗aWUDQ[k&ɓ'(>9hkkK|uZE-W.gJ@QJY&Rׯ'ydNFGGy)J3PUO(2p$IR_~05nooOMOOӁL[[[h4:d*hR4fUi侲Z\\]t8t'cƘtxxaض^YY A?0$lBXP(lVEl6{f+b1qppU^,y)3HapRK4yP5cr$Q4`6c1&b8etչ0zVEda}vRz#w-,lݻw/PnBtƻƆoY$iFqaQ4dB-fgksؠRBH`Qũvmn|(޼ysOUUnQ\DqoVS]j|B)uAXb8zzSŝeYi ٙm{pimyy8V],c6l۫{{{vmɻ;񥥥AZ-(۷ @K4a}bi5c \1&X= ta"!*J|2`ѣ.U]J4qO_)\NQ~Pj#z=hgg')Ii[oƞ_V71Fz"}] a4͑J2l6tZ] ϧ>ѭ-<-P(d?yO7\իW Q3wzj"]!w~?VUUYkee](1DQ<]|OŁ3;;PU=(JaJ)TpcLeV1<躞\\\<] V>?:99pR%ɶ uJD8zn2o׽cU\ưV۩ᇇSciZ4fX`⸫ ac׮ Ba6:N n ,G '5 Gz4=)J)ZiTU]|trr2&bߛ¯#I f1ǠE~1l_T*͕e=@-۶#pW.3 >:??wW5>IӚ$INفH$|ku !Aa3,c,_llluJ顪iJRT*ytGGG,_pؚ5nO=zh=׆ Ib?.ض=Nkkl۲_]FӴSJ$:"J:.lq|SSSCЉkoDQ̴Y0PJyu˲l6Mp4 e68È ^ ǖeiϞ=^vs~ Q$i !c0M󝪪pA 4h4VVVQ*&bXͮ 1_p̞iN> !hw˲N%IV*^|3MVĠo0Dq߿?-n2lTH4Ցʎ8&Zl;{nw-NgtttziiwkkkT~X,ΟbޕaC۶.fǙMՀϷ...Z# wEmn?Xm\6J|smZU(:GVVV.ln:˻ 1vZ*MӦs !ZM)ѨZxc eŗypWH_st4MfGfg,j7e21&q!)ˑX,&_o3ś7o$ ۧ3 1j*UUeQMJûT*ub\ƛ,cL]@1H$|j3tPJmEQDޡ/_DQ4/5ƛ7obp54{e]d2jRItV(p*J0T'}+td2zVuUe6}] @' X勰%aF)еJ^OBD޵U2lSJqN;tPJz.07۷s`xwP($e2_nŦ4MêRʪj۷(p7R*‚  LP(j{k\Z-E~⦶manoo޵XyWmd2 #tPJY<'Aye%~G %OF0"|>;sV*fxD\F4`Xvww0,BH_St{{{(Jz$wn}6677C(b ij0!R/9bPJgxPRaO>)u$I"g˾ p8B~j8);']KXzew~$_2Z{z IR2NcxWaCh&Mz՚-T00 ]דo"$6HQ0D={&5bwwwF F) IK !ѣGyB@EQ|Nk ڦiz01d˲lBо3:fggSSS[`Ʉeeew-SB0l6k(j5λ EѼ" 2!q Z*_x _H$«E04ll+»EX,b8@={&;W5k蘟oEh?k\.)ء~9l<}TmoJ">hporfsÑJtLxbii˪Uz $1?yz,͞_>%7ꌱZ-T0TR&T*5ǻ4q0Ƥp8̈ 4 tZSU3ݻ7S+%IR)ODe9!-|^D">NJJiNHRYոJoy6pP(Ԡ.s`!Q?::`JN,%yy ltӧR,ąWkfd2ɭTO<)t3w>ɼc-=1 cŋW UUq1]ױ ?W5uU˲4/fx0u] uX,NDQ4v%1DAѝ |>yjuÇ#RMn㋰7"I߀+\0B8wBy(B @޾};iM{On0Ƅf9= u|6cc?sλOrܻf9G.'C%6;Ƙ`Y ޵Uc}}4;;;I˲p@>$yQ*H<~]'۩>J%l^3o&DQoCG&i\nOrwEeYλA!$J$I2ǧ>::4 B$IbZ1 3%xd|}c)tPJf)2|cTمC޵ @!i*|Сw0i\)NWzx5+++u0зp UUgqd2Ǯ\ޞ$ AA ۶p>t!nllxv ySa""ǂ8!j\i4Ǐ]՟Z.#hiʀ@!IJ,rӱ J%D)  l Ðb W6k蠌1j=x8N}JtBV6 677XոA2$I뮫r9;{dYM䱗͛H8 gggwvv,'ۀD"x$IWU7gϞɺ"m!Jl\(ts\HMBQU3" jrRA zj1vƻ/HRmBH׹*tLNN&2O\DQ!n8%Nömymm 'Qo|}(]M)_H hFvR9p{(Jv1&@ !n選m;= 0Dwe @J BV5:dt˲믿㌭~ t(l۶ơd2V 'SA$4d#СL&ӪV}hܻwoۧ!._V5jYw(4fVu-ܬhh޽`XNNNZcjޘaO}x;O(!_lyy^yu \躎 UwۥVG!PJ'~Ǔ#^ BЯ~'IR}ooow^L۶eu Ѩ uxÇh4*ul]?kAeykkZ!::)N Ø]xE|ee =d27otxD6ۧ!iͅ ZEQ0_=KR_p>]O؆atA/..'P(dj5=+Ji8N$*R:/S¶mm5%jzPJm kAusttT!}ѯ X^* !};?m^|mE3'Io8<pGBٴdYjL&+!]XX@؀@YZZ:XR$$ };v{fqqMp^Zeg*AUU>C8 )6:RA0*NUB"DQ^*rFX0qf7:666pȩ,O666,0<*Hjs0?L&jM 8LӜM$X\.wW$Iu_24E8dA N+J(S($1˲lƘ+O6Pr$@R Z-#:wT$Z-:zSVc׀:<<8q655u#ǙfXƧGٜЅNYx&''6jE~'n Ç?)p [IB)ZzqaBp |jUQE:;;!N$UUMRJqB 쓓6!$6999aB#|!E9qBȷO5:n6Vl۞)L&ûBA6 CFW8AГd#_y UUUEQo \x/s. !L&$As8!tZۛG:>(x" Hd/]$,r!D*Iz}0:/R,@u8d2ǫWX,i-3g: o޼B.x JϟKHD B!H$| $ X,fr9 p[L:~1T@"IB ^Z"p>𳳳у4Bjb!. Kk}v l1oZ_j<\ͦ$Iw87o޴y 8Az$BiD:}8.fÇ8y(r&1G:-Ibqnqq/W#,rAݳgffڄM&C:e%WVV} >lfS"O d2v&Y ~W*N` tD0H1&ecUCPUU j5Bۛ4MݻgYBazzz}^!BHղǕ^^hbb]Ǿhcc*" 9O4M/Jl6] ] D.jAUUYuYEzX,fYe]mfff֖ #޵D!P4 #vfrrYբxBJJ$!|#TՉd2rDF%/޳gp8He^ 266BHK IDAT^ƜZF$I2o.0#L/PܴR$;;;S$=z |C"J]([6V<oG]Fr܍ZJ%J_u@xcL* duuDQ"C#˵fff>~ x,,, vz411Y\\ġ4}6ZpV,//w,fD)6*+dJ2G:oCGp>}*mllV( hU*)J}%q DoqĄSբYL6 C˓߃oHd2# cL$T*&ZOXǃh D"nvԀp3q*BBSr $M~=pp BI&F4TUSţ885|n0%t8Ѝ'O \Xp{{{ggg܎ TT*eBD㦟m;Fo8Q|c=1aFӟfj PQpa?&&&QrÓfܫ 78>>>.>ճ+hTV8 \1&X%3<qt:]]}p%{||YTn xpu4͡P~s7Dm7[u|y>_^R,OOOa# úiqz2p`5@A:{j#Jw_b PH|~S/( mk>0ou]4MWWWT*uQTFׁC\e;fgq.37Enl4=G<A2 H$b1!B~Զ֐4˲l2mqM8ΧY=~Xp'L&ҧϗ dzǻ 6... _>Pe$+G Й51^e2(hcU\r(S|G  #[a4kSBttt>==FmvLD肮 ?wMmQwbq&wM/WzeY8} Ad,AƲ,[ukffF|k𥕕^] \1McJH"g+>_nsxxeZXX/†iݾε"HX[[[s J)B1ktR*C>Xys7EJ>pQ(m@9J8>,,, V6 DEQNBꍯ8Nhee尓zk'aij٬WurS4224 cCwr7w+rB!=:ʰ)TȲ,(B9v7:D\>g󟵽Yuth7E333İ~vvvcZV`CGZXe/,,tۀ}Uؐ$I:==3336!M./PPqJ2&mۭj"9Me9f6> Z蠺b_>8Nn333mBIQX:Er܉(k dM>= 04Smr,!'W}6!IfWa0mabp<R\> @puxx\EQ\.]qVK^[r23! BT<qJ2ƻ>MiV~04q:Au]d2XptXmS  @>_'})d28~m:б899cӨzqgryvll A`,˲ !"!]K|իWj\ųJFduuŻwp>ݨV!M<.$±qu PObEq::R u]]ZZ|>3YvoimllIT*M襠gV:[׏} } 4Ÿ8@99)j4fp{.*R&Z{߅d2E"A<  !⢗/37:MeYwkrrr2&IW5Ac>b߅ J@&JElG}:am7ƛ"zOR^h k>_Ri&c{]ymO:;; B J8]'Ye3ư}QYzm?p *$sss>bW ۶.гT*uҧ,..[԰ɵ7E,7bC  Su A !s Oܕ"jZ_JeaG}wStzzڒeaÇ#p8hO܇:u]?y1p+o> hYTGGGa0|>Zнp8|R,(~7E> f9L&kdP(啕?oQ+F>- Zm&~u7tZNQBġ\/_YU s%IRc`*TU"AESUUw.DA0677{~@!/^ƷQ(u]?.6lx""ܥlPJ ###MBArq+}Q(Bۧڰ!IE87o̥R),qښa6V60==B(:LӜ]\\:t8 [x̵acttq(XpqCn|o L!Xr&`Uvj:eGpm(JӰ!1f6-r\[|a3lwskڒ j>6livZ&jڻAڂ `t]7vwwu7J0T'Xֵ#$7lo:cwh!u``$IC37FGG[p+fYjб3;)Nz~~>9??cwclwn3==m4CvwwQMBznmmɢ(Fyݻ-l臇n+T*) 7hġ+ $A/>q7 ][exrQضm'Ͷ[ cR`uЩ͐(Çͧ*D8>ذx1l4Mf9}}jxH>o`8tJQjz '[ÍaRtrӺ|-<LNcr(_R ۧ|p8|eT*mEA;]ᮊ\:>]kvSqkhZ\HjYV7fjxlC$Xճgd۶BV5i#kkk5޵@D㯚A8ǹ8C+$w1>>XZZ?L...bzk+1rCj߿]i={L]cLbn}X(jPJ} gn TMrrrRE[~!.igg'尪'OkSذm.sX |F S2)kh1X:] $I&R. O>ſ|%L...buObV}SPU] a6R}V*B|Q$`U/Fmְ} 6666,J镓l`+gV%2N?*r̻ Ø\^^ƿ'@`BKeYB!1m9Jk񃃃9q} 6TNB!@1Mt@1(p1AQjm4S| @63 O?I;99 vSQ4ǏƘL&ۼk񺣣訑B0ٟglT*BH.lۖy3h=?Akf|% [[[U@cQBH8H`m+@x( N"p b8322r»Va۶t:PϷ(JE í#4 c iz"AB!@RThI<` ´a5uxcccRƻ'7ZvVVq*!8!E0ƨ(&ZR( X8 ΃aΔmۧ:4g1S6j1Fp˓ dYr:T*hvS@\1v?ʻl^zUޞbd456 l5r܅i#-6c@Q uxYT[ZZ) aD)eRJ*Jw0X jP(Lٶ}J)uxjۍZ-D{htlrr]ڒ!J>azvzz#p իWj $_xS ԹJhX]T0UݧFFF2 wRLPJ=zdan8vtt]OVBJ Q4'fX+!lriзR% =aeß&ݻwλ*JrۧZp#J%IȻXv2kh ,Z6V J%Ļл?$WVV;mctƻp7 eYmO'QMƘ̻ׯ_ 0޵xQXjxmہѶm*}BQSuxmɻp? P(dommwcaa%"F,jjtC8??\^^>]xI^?$iw9a6lIٜr5BXLZ-l;C;Y[[kzF$迾/|2fv`Uc1BfaL4B L#\7=ltttzqqwDONNsޅKLfV r߂俿H4V5:VTE>YV lc555Qi& jthkk+jfۼkA;: JF:W.#6!]PQg,O@W{ݡA&cl6EFr"bta:iکeY'RyOT SJuUTհ8,NkkBveCuT*yw8l& a: la-I$(&FGTUuO@yQ R-//kva#AHcUJEEQxAw-}бن1up(l%,Çm޵xIٜ_} ac$$ t:}Ļ/)ӄǏct%(m۶xcɗEXոB Z?6+秧<׶VP@߆Źcշn8oUy]ذ$IyeY,AYqgϞɶm;V5(Aք(Ga% afYM \reYFR㉥z5:&F".afY ^#t|ݻwsZ:6kkkk/N4 + !'P={B0B)M޵?!l@.jj."rV4o믂mSz=D"#lIE*J4^n}|BQ>=I&gv{ O?ԦrΈ1D]ׅL&ӏ@|~իWP(dg6Z6'R4ͻ8\.'DQp_l&9], [[[w'RÓ'OD0>j|w!YUՙRƻ? 虢(_annnxtӟfEt/޳ Fڛ7oз1d?L4 TpU`B 6oDQL T 61F !D"UoqV*i> l@d2eY#CUn 81Dbq0 /=:iZkkk< 6o( úpj  D[k0߳Třl6aHJW,[Ba CgH ´mۧ߳RiZ} 請8:BT/J[0ƨ(\.]K =+۶-?x/NaWMIx6L=tضZ-4W5lT{6\mch#i$a߀ɲ~~Z{v\G> ;J^VrB0B#"=;]ǐt}h/G;۶DQѷqнp8<]ǐu~L p |>7 wH`:qBHPV5u|tt5 L p|1@0һqJD-4@4d2+m6Әn(JP(x/CMQ/}=իW E # DPam\Ud2iBܺu̳FGGݻW] QBUU5$IV0aÜ$JJe\sJû lxjw=(t`lnne`Uza#nde:Ѣ(^ᇠw{b8n600RG?]gn v[EaG_U%h͛7QB~  B6fM ]16L&=zӹ\356` ha+U HGq3ccc+  Q$~,u IDATV94M#1 i3sssXո$I|w-wE)ut]µBh4ժ*7X$i,c,M)A,˲I@7 h4wattt:HxFc]Gg!lPA 6c8NR 8kqBwBi@t޽iQ8?] Dy mءPH]*()sh C DF$]T!UG !ɻ^!l4͋mhFcAYfj|lNr m說*p+EQX304RE(jvMW`ɶm;6ZDUUٲ,M޵  UpH=o߾myFbqniiwnKr94 EJ2]qƳgdEQBV5ꨮ J)]WEC Ji 6esT _Sw6`?~eކ8c,GcL ÌbMvvv Vǧ~y&8wQ(LĪ*JHQ'Ͷy00t$5΂pR 6U J)d2T˲y0(0t׿.}$JLqBp(X!lC#[\1&(R)w-."1fx0H c6Z 'RR.Bpy0hޅeYkI۷ J)G|AY4@P l$IAǵvȰlۖ$_ٙ$h An.E1D,êYch   cu|| 63EQD"aum)af}@]oK V5~wxx, M8wm˼4Aگ^ c2jNӴ4@!lW6|M-IT*͎xjuqS4@!lW j5ĻAJ$:4̻a`QBHxffF]KEyp/(1u ZPċaXոtpphh B[fO6]à1ƨ Z0lX]h>{Baz||wnqzz:d5 z&''}$exw-nP(ⳳ5J@$Ijoò,ME߆D"qJd :BxC. ureY:E䗫 bqM!lwRG_ڛ=E)h@(&>;xCii(2dX BGBS86l޼yG+܌߰q߆MGS86~qq] X7+RiB^o}:gp pp8'>d~$n'RT{6t|N)5y6VRmHU, !xiMp px<^EW= j4ӧO=?oCɕN;Uj:I9CS865Dj5Ѩ;MSx:vfg p+ p J)geWodYZZZ +CGRmn(C\xc/LƲm۳})_5MsWqMVJd|^] !lضۋ(T.;\С2V5nrttԐeیnխb߿ٶ]1A)ΰ7 lizPJRٻ޶,ϳ$u(Q.EZ`F'3=F2δ4&<;NI"%Riʺe_삓֖ĭ}PŤJkg%n__<6Ď"ݍa:vffX*ոIXs?!l`=::J܆u=1fV1;{^;H̛`ی*MNcqj%tM[o1 v^x'ĦR*fVJ9Bkyyyy9_.ߏ 021Yl6駟b߽qgRCCeYT#l%)eQMm:li#Mq}uu5'q@,6p8vSJIu싡cj {"lj:PJxڣj$~oq# i*]|w"{^q{{i5p eY'R{JuիWVuޫW,u=!u-z}!4R& \흟z!R\.gviggg?hk[KKKݨ #lF}]j+ʵ J)Ӷm_LnWCj9C0 7߸aQ"4jr<B!:NFӴϝk4 lR+F]?|RJ !kbZ}u 6GDZ~/R8*ϗ&vVcoo8{)e.<&Xzm(Ǒ7RƇx'!u]moo_F] a@߮K> !"oZIj:B fTeG]\<3WVVFq_<H;D<rr[?\zuQt:@J9G!lH˲WWWLJ)-XRi"Jz$/0DjuJl4j\=Mab`49^ }nC)%R !zn{{2B`RQA}2$o5MZB:]חfff&r^I6:`mNOO`0oRR)0'-4퉜S6$`0]^^rH|eeedYV[kڢyga>G\ åvu!lHcxL ="J)iZvkkk5i0.~Ե!l+*Σ#"s9Q#lH}]RP:J)iӧO{a<~,ڶ.:`R @zY;;;K吸Rjsb:qA(Os9VWW  RDv:<^|y/޾};fEB}m{49RJ'Z7#lH,ԠNT;HKYX5`0XT*|a@be27'"t(aߦR}1A]V5:RNk$"lHr @hLf8tzygq*Fa Cm}}u-6$p8mk .b\~f4m$&*mmmq-$a@r[Lq(CEУ\Yؘׯ\/t a@ =zhZhQ3~eņm Ruŋ2P(cooORgϞ\Du5|[HӐmB L}kkj`0yu!!lHhZ-㑞ncooO0t5Z֒n o8w$>c m/(jzʲ,qjL!Vka@~?! :hjض]*ˁ8Krn o4MٽG,hVC)eڶ !&qzz ?lDt5n= olH ߪh,888(!WWW늃pW_F]`jBIjH)e\.sa@jyHjJ}h4z;5 eRoq<$^~oJk=ݲ,ٳg`6J̇3A>Pj<}4iUI'\Z]]}u#lHnnj@]_uanncWKnۣȕRN\ Lؽ!C=<<ݐi٥@ 9U(~He8??_X)7.M)e4ͥ }v0m׶ZqJһ8.H՚}_mllE] t6u'V K~.HBqf !鐮4 R'NCy}n6B@qppP| u4$~qq1};t5nh4 u [7c'˲z/;>>[qppPu=P(EKQa@T*U'R ͋ 'Ok]l_j6RQa@aHhVsSSSB.td2bR[ LZ 6\\tZ="tZY)幔26p6eYw\՚d2FV̄4>e萞9& a@*]7H{^>hWCӴ8CAѲ,n CJѨZl6(h~v4k(>%t|v;ЬF*mnnvVcnB0Q2LwnnMRJ9U)CMʮFӱ5M[6Lr>{ x 'u}&VUTJjR;qyy9yud#l8+++}~Іá=RyfZz}^ڣ|`H)`0_5pW.̤#)|WQH>d>t] IDATm\\\$:88X0M3P6 B4ͅ]F8YEG<\__G] &R}6'1t5^x)쯾j%>EYy%>J)YʖeyV[~iffX,1l{{{Vԅ҃$TiARJ !2) b₮`&ieY jŴ@u||l=܉@6L^wdZ)%5M˦!(켍@6LgϞZW|*Ot:p8I)kacWc~~>zbRa  yu4y޵ҏ@:63RR\P0\]]-lnn>낔":pxx8RE] iNU*^uA)5ʣ|AZ]/# y6sarVcoooQюd v5l6+Q,t6@Qq(\jQuK4@0q^~];h4ʎ y&RJZ5=77׋q{]a0D]6L"]^S7H.aLU*u5Ld V&FV[},:Byg.O6Lu=kfiZƯ)o6&BV[MݑFCwǨV ׿~h?aa@)ifnv첔T*a ^/4jRJ= r?XR=s3 a:T{&2zƩV-]\.V.[ax 6oڿۿ-MOO:1p(Ve(ԳgFAt:3p$p=\? tt6RJSJbیHpCӴEMZBaK<j4K:n? r۷o!뺞i @RcW㫯 4<-:wvvw5*|-ru][@f @*Gш t jjQBi6^j( M^ص% KC47 5tt:R4UA6B\^R>fW&cggg9]׻R@?^|i(r|5Z+SJRJh]帼: zV%˽ WVr\]{ph4S*hWӇafee! 6$o !DL7w*pW^~w 677y:= ܡPJimgKٽ  23BxQrBgϞ}݊mہO,K)صa@bﯬmV#φY!yRJi喖~ z=AH^|E7CpgZV2}u P6$R&Yd2IjD !ޜ.RJr{Ɲgww׶,+IjaSƝǣa@hVBOHo] R; 4]4wA(뺩j\\\Looo_^?ױzir T iZigg'MB!gMӼSWC)5[,6lv!lBEz=c+eF]f5r{l<a@b(JnVx.cWcgg's4 @^g4MU*u5]{-<a@"(Jj͈;l 6)ˮm *E{f3iHe\ם˶𽽽u箿SF @buv5wjH)677۷]N"ko޼1)jT*z_~d#uAg@ åųۯLV5 !a󫫫u5~ a@l}vJʮF?{,alNwGG[~Rqxx8555u81<=hK޽VJE 766Ao4+_  tt6R+.--uDNyu Q|uaRVVVw5KaB? 6Ć8˩8;;:ӽKWC)ZWC!<,GBÛ Xjl67Q(lmm~oooI;gfꂆB\^^awww?t5B?u] o2"lvtǕRzA0VuRG~N!lׯiz ;;;׿~hcw5BF# 4 N)%-˚~I/ZBK)ݠ?`cw5a;88X=0jV?UW68HB{ t6B嵵Iy/gj|yg&a {MNӴɉKvtt4cvFVK)G+++nu݇eY8 @(n8.mll*]*֟>}BΞ @(ݻwJH?qqq1zۧ>v5j!D6u4M#lBA0V׋w?iwmRSSSGaxׄ @(Ʀ^Y(Xi1!8?? Gݾ~V4''rJ%_JWx lpXXYYø) {Cz.'|۶S/@`,演3lb8β^gBBHk h4J]qrrb۶ݗRin.sg}x9+{SJiJ){~~~8ejRϖJ%gggW!D~8jz}E5Zu "lf<==(؀b::i]kMrikF߽{y8Ѯn׿^]l6_'O)l)R*6 w2r,J#>ZvBm[>}zi}pD>0a駟#le]z}:oaJ}sGa\ !tvvܵA)rDոIh}hLCkkk{u... VkPl`$M8~/?~w۶hqqԲ#Ԭ83dz}·PJ^/.#!lL)ehŠ{cr-(;!0@aC!֮`0Ŷq0j\jZvjk欔R;>>^]bt] Auna8Rʇˆh4V# ?/z c*8NV4pM! )l\E]G4RvV⟡5MkZظXVm FZ-?;;;MqjRu5^x)>tӧOJ)_uqNCD4M+:l8clooz?v#Y__oZBdi&Z lBիiT?:t WWW'a>‚^(<j[)u8Nŋ~f4 "lBRʋS۵9yK<_JQx/x9;;~z˲$+6ovzzzXT"5x {{{Jwr9FIg6CxiCӴVכ} L()eammQNh81s0; :666LfYC)5͛\X< `}@ۧDΏFӨBjq`0XWJq.`, z0|0L}iMlܡCjb`z+LdŷWM4/;<<qsqf6ǽmh4Z4kfmooe09lT*-mnn?}?{u:fo۶ڮ6A&ȋ/4q !Djot:f>O)^C- !B!R*?6s6*`OQϽguUF\BR˲ޮB (X6 zP(HJ,&)!D0r}}?/ X6 tf<ϻ0ES~4 #.aC]ymMh4T*7@I) "ljZ#L뚏Smmzh+뺆ahO<9B|. 6cPJǤ۷og]׍hdG=!hd뺮eYޓ'Oq9Ggfsiss,:4;Qຮ!>9x4FiZN! EP 0<ϳNu}?(Ƀ)PJM}(^B T ~ǂiWQBZmF]#nSl4MJrNB`-V"l)733_[[;iRȷ_Gan !sܘ+L:bh4JSBl6 G:!YKaǂ[1BRLJ#=)lO>tu:s8.),˺ZZZzfVyޔDIDAT5RJ)%5MJR,b t۽kOEVw5!d2 !(˪3+R x\םTJ~ SV[4drY,D6* tRI`x1)e\f 4C"u:jU4y؃$q#l)l6Dp0?,;::v]wINOOY\\L? =@ IuH)j!b.@ʸ;h4ˇqFC7jaAӴ˧OP6* ERf'"Y~gg'VP 1pXmMA#>SR"_^މhy1:v,ϳB"Sn`@ضmXfY0 #lݷ+t +e+˱|maH_~e&^G]G\5>}:qhZh4*K)sB>77׏؀,l)afT*}t\48븉RJ8ΎRd2qBd"l)~XTJwvvN&?9;;NS8??1 SV+++g@Q)P,SBhRʸ.*\׽0l6<ϛ4mo}}=݌8>>6cER@\܍m۱6Ggɉ5 * >}Zpit6cGnwwv'ջ5Rv:<ϚqnA9˽)g][P @@ٶd.#,gggb}ʖyo_ѐmA>/(naH8R[|nssu_d8阾W=W_Et6cGlfmNމϐ1 BvL[}m1 ;``AX;Fc%=!(zBJIaH0]iCcʖii]ۦ~aH_~e&:==R}-d2:RJb8PiJUu|Zv1MsAJIrB+933CaH RJpc"BJ[^JRΦO6j4L&s4j])Ml]5 ~N$R*ҍ躞[\\L-bSSSmۉB 6QJiLFxD6FQb:cD0NӨiZsBv5FQ^ӴXR aǙ)Quauf.a,1 + J)FZHHNOOmc[S@~Ui F#_,S{ J)n`0޽{ POzղu]O0h?"l aoHI8::R^D]]kϧpI) "l ;f?71+J†R$D.///i^#]i۶/Luaw cEb4")T v{<:mm'0@B6xմRꮟ:~믿0^ l.d2~{LLH7 O64M[YYyIA F#iiuܕRJ*Rq a!l1l6ģNيF\+r0"vFIu}uiT06, xB`0rJ!Y4S6aNm:?zPJFٛ7orハc,u0{ տۖef84M=Fg)u疖s^#t: Ømgc'`0'veY 6r]B֖nk?ɤ[4~Oa/_?֑qС%}J\麮OMM6cGbhuuu4͸B%C۷oyj0D=?VP/˄ 62 #ɓ$t~+I RSJu]=@6J/u9RJI){lR*p8ԤzMx l1k'XO!t\\\d-JtWNOOf8\!X {6'l^6~(j(dӴcC!$ޱH:@\__gSRJ)D躞- QRH0OmGmT@4ͬmۉ@~~?#8J#楔fZD]C|Ұ,+ѷ݄ @X@ضm4Di8.\.4MKt` am2 nuuS׭aoN !RAeumYEuȶk C\.'~W&i<胩Yj8珤AHLt aEvy,E^D./e6x" @0B. S+S5YoN>^g6͔R^}1B}u/A`\ǨD4t:Ry( O>- !lrdly!1kIז8h:Ard m*R]w98 "p+|F#†֢(2qBES+/(dqxss<sgh\Q)qrB24L|!ùy0է9t`u:rw5>B;K l/DZD)UOQ*BnddD8 |2tT*q 3 #N'u޼yRc>:...͡z+d'}p81`PT*gK #ARK_.8c3aq1@N6f3YקX ړ'Otב4MV6,LQWkl6 Bo䐯`U~S_-3oDWC!~O?:FJ9iہ:nrQZz`C8f~L6 4MwvlL&"]mNNNNwYb8a?H)߱PJY]m޽{lnnrnEk2G20NVl&u)յ:t~~y󦯻a@FEXɚGZL/Fq,EQnooꡳd#T*v6^f'VȹVU_Yky! `u]nuX,^_]MJ)WJYz'b8 a05ZRu6^z$_Z .//hA2s t9SRZp6Uګq+yt a0OC,:YTtXM \ x)<ݵ~b%g3Љ,Rʽ _.CG$vY~0IP5Rk!lKnrQ&B5l6R%~I)u"lK$?Ng #T~[R)/Pl6 Dl0 ٸh4rQǩO}h>  `ɤ^X\dg&CG\a8jYvP)ETkL@l0w@s8>|xqjkkk)00aX2Jqc[[[Jw::::z$Sϖ6%s]===zHQׇ΢nۓNgT*766ؔ}6%Fn^5k\(˃:xsooO(j(\__g>JӉ@w ly+_z8tqmٳS!8W8/^xl6?dVu:Ǐ3pl6Ku`OFv{ٳg:n0%t\sz^!MREj0uty83σYV.T;;;t `5PI$_4MUWH\ 3y< b#6fZy՚you KEq/a 9F)nmm10aX+H":ql6[z;2UrpvIENDB`gammaray-2.11.3/ui/resources/gammaray/ui/light/pixmaps/watermark@3x.png000066400000000000000000002760701413310443000261110ustar00rootroot00000000000000PNG  IHDRxsBIT|d pHYs3tEXtSoftwarewww.inkscape.org< IDATxrW<9IQ $bE9[+oy^tG*z[h/Zʎze,9a 4 R0d9\4$DxCOyH5MQu'&&vÇ!Dv>Cl6oa8[v;svv6iZVJi+lm#*:~\.T q٬ёEQj¶m[)4iZWJٙw^G!)>whR0bǹ399]^^~ eϟODQd ۶3a M44RTn_=z(L(:hR0BRm0܍A2vgn;!|kG14!߳,\]]=G0 u!D_ >M*FRJ}P(k6LM hL(Ji8M4c!Dd •Y4ZiZ.1,qlض-0ٶ}uyyٛZ]] !TځTϊ⾦iGGGvϼv][u#nEǃؽ!C 8Ϊaa>?I;ˀA)e&Cɓ'fP\__O;KٙEF5MB0\w8:8hR0<[RvJiYWY)EGG M*Rr]ATSThۙ MӲRJ[)e+m!DW)WLM*XV˛zeeuY{\XX޻w#i hR0hV,JNYnQ~¶LB4Rn=zvФ`H5Rd}}",7)*n7z6J)iZW)ՑRvuilNDQdmllРioy8==<99zNJWJ.hR0V/_dj9)嬔Қ83MeFM*L՚t:P(\u !ĜRj0XӴB!MR&C|֭[qӹOLLL_]]1%az΍N)7~hdYV3i+DeY3sJyTeR333K&"lvurrv(( P*a\52̩a|똤`Hx7b\I5XhLI)t](RK҉"N;8p'c悦iQilll.&RʕZ9Ɣŋ(aRF]760\0 CFQtR*mѤ`T*[a\޿cY\Ez&9BJR'`lNtE۶0u,ImeY~9F]ݞvs3RXッwgg'J;8PJL&tӹd2:N*Rjn+&_eϟ?u}Ҳ+MKzނ듆a\zoxFc-1aO?~YBȧBMN766׾^3 Givpp`Z5syy9!^K)t.u]o?[I5{Y(6oŋ(aRvPx= Q\B ]{yM*yRNT:M;ˠzvJ(L&s*hu6N:::/:igLR0`nwmvvvMӴv=v.//u]R/_*d뚦i\]]hR0@juFJ߾};$ŋ3Q;Jҍytɘ XnG´ ;T P6]l^3_zewY\f6=BJGlNtEMt]GQte%&yޜwΝޠbN)5#e'`'aoo,k4͙^׳mx}}9i&bJ&՜듶m_i|wwW7RJ}F3E;. n`*ʭٍI<880(SJI)-!0 [[[I=G)70 -eT mM?QEQ4T)pC`(Lu øh4~fA.Kk$ kZR/?EјRq)F`J A߳,\]]=efz\6Յ/s]Yof9v5MBj !X4? !onsoۓnwrZHJnZNtYoۛd2Ou:N&i?Bo1I@Bj}MӴ\.//^hOq!l 4I|^oAI0.!G |??=;Όiql4eTeR333#& h6+^oIq.lJc|2]]0 VF&9$wΆC N.K|~޶mMӴӍc!Dv6|:T`h5 B۝3MSq|R*Nqِ,nC˗S mzeY_}KT UJ)7d8R^e2) 4{{#_[u}0^wuv6&O<1,Z4-:fkkk!}4|7ܼa86L<: Å&H84 ~_,}|||ѣ0l^4'k6nwQӴ908OJ҉"N;FM*Q^|9u~~`\=˲?ʴaФE)%3bӆa\H)ۅBB&EJ)h̅ahYV&0 [[[igxIRt]wAyTF)uf:igIJbg2y!ļ4Md{{v6 4ɘ X>==m?z(L;nT fs.Z5q$<_r8lhR0eY i΄aس,x}}0LhR0fvwwu}!i0.z17aѤ` (F1eY(0lӘ¨IRJ.>o_&#|a.(a'ȇQ8NL;ufsV6('<Bp#ƂaLR0D]4mN ðߟ>~Ɗ8wt]IPJL&(QJu,:^__?BffTzF (tgt]_x0^w̍|)<+ϿZ\\|M k\eR333˴)y!Fit͑0nkd2S0|7l@RyfkLR0dL\mknll !´i/d2v.kT$lN(a28BnpgV۠4T|f9vm۞z0 [[[{Bv6`ꚦ{(t&YxZJydBv>`yE?M*~zނ듆a\z㭭jMvݵBs?ɓ'ҜeY eR/:igFRJ4Фg5Mzv?<(_m8~5u`T~u}g Adu]/u4= gggkE;$g^G _p MN=[M; 0|߿^ 4bټv`T*qDd ߰H)DYQf7fffjBF RJR|~mMӺKKKig`uKiRt]wХ {rm!D{igyBB~Ѥ f ju&, `^J)ه,M*>B{iRVya28^}y#,Al)DYaPJ<ң(Z,G[[[Luݕ$绶m<+$YfJ)FEAhRZVBimllu}Sn!9\-/ɺj6'rD=Tq|/H~o I>X,60\V3IM^7*, =dSYq#7Xo[II[\{PJ7͢_lBuKI?p:${f3ѽnq? )t]W|o Y4c4ͥRͲuOJYPJ E4MZOi/TPTn !^'a\.(:}DQ׫jb ځA8<{Tuuq6͉$btB^_H;` 04<ϻiNQ%P\ν+JM<)eD nqf"˵1hRRPJ͎%('\.ZRJBFYi6[inZ8Iu]w~t] 's#)zvxW`>麾o:3t] J2f9>_vΑo֏h%lu'XT*vu1JN.|~C p<ϻc~Nڠ !R{0Ȧaݹs<,B pmR²;ӟrEA ?K!ďA$ RJqxvƂ8wr9F8x /s Tkm^eT˲=ϛK^93뺞ig4TkvO?~e5 4$j>RJBrhRZSThgg',+mA}5㗏= Xx٢8i'{{{kZ>Z~ua>}:f9T3Mse9nWsNuWWW&YO)%&''sN'H;uIJrKzgg'J;˸ܬYu˗SI֭b=ɽWxˆaooorhRʶS@r{qqWJIى.//۶KJ)T]  &`` uσ#('P/?ATM6!D>ިc~oѤ LsL&sJuKҡaFպd]ڶZ~ig+eM{7dzٶVT$뎺 ^o~ccUYM*G3 c2E5oj ȲRJ$Uw ld@ f9Z֤8wwwbiZ={dQs&E~[^^RG.?;s'ɺRq~'&9:O; >nWKW_4MǛ(nqRGGG3i@2RV}˩$:J)#ɺ`oo?xGR J;\RLys=HSJoKN},\;cFFQTOfbx&VIvm!BvƒRra908Z8I}󼹤  iELR[˺?O;gccㅮA'Y^{iV*;ɺD)뺾FYFM*{QJqOų`[? Iى, lN80j뺮ى劣eФ%)嫴s }w޽PJ}_Kn\>,k$80?쳗ig4?K)gáP(2M|buojR;HRYMXVˎI;RJwaٜHn՚ta uݻ㬦c0I;J)}bbbT*v !4͓RoyyjjjV咪9J)˲;J; cVw]n90<ϛ麃8N!RfZn 4M<ϻcBgssn%}3 ~j !DoTM)%,˺?0,]5˲n|2vvv"˲۶KJ)Tqxx8d2kkk'\n,q >hnAd_z}; I*R¶}ݫ` !(N>mnnq_0 <n{{{+ixjYM^>}4?jM6g1I7ZSTH ]' Op7RFRu, U?&`fig;;;\}~E+AdݏbR5Ukv练_F nntpp2O?M(**Aų(.FɄaHM*)* R>ڶ}:?24M[L2 Av^={dJb'O!$31I7iQ1EԭLӼl6'ݟn~뵎 !D>?M44V3B^\BHM<)eͱD,..h,kRbshR !X ðvr܋bu777a8y}?0 _8ѤbEJR\>,'>N499kV5MӔR¶N$$&ONnY_h4|4W+T+)4M\]BOHM*s+LQaDemN>%assl6)JIFhRk6J)_tN>%J˲8@<+ZɴsZǹdMH$f9iNI; !677ke~TR5*Z T0z0r*˹yv\T0 !˴CFQT/qTfIc0nFy&9s]w%,9}3@R<+A0v T0fl^LQal|~E+Adރ8sI 8ζR1ſX#e-N>ڶ99OLLiz5,24Mu}/~wwWL;|7VSrvR?`ڶ-٬~yyiNLLWWWioIDZ)J4M,Ka\)RJ7Lӌ((D2 CPD;xtѣGaC FRl4rigA6fgg ˲S (LwJeiB]C)(8OJSSSQە~_O4,|ٶ}zv$~0<ϻ^SJw?m(}S! PuxLz;dYPJ L&w:E4 #Bl6+(u] PNMMEQ^>| !T`T*iMӲo34`D) ugig%?m(]^^ei?m(iad;q7UU.Rʿi(I)#!TJ6C//6w:7,H`D9EQ>#O??݆O'PIqKm;Jj8ΪaaPxv$&7ST[rvg,ִR؅>C? h6Q|5, _4I>vһ ݟv!oOv䤌8f7biA908LR vB 40 0m*?94ML"oH˲b۶(77I۶0 eדٙ}_>~8NW RӷZig0Iޝ?BRf-F,t"LA``p> 7FE,ɲ6KmC˙N+ VX.U=|)>B0UU(‰0.r!c,r7eń3@w D$lN2,R:> (2ʙLKBx/~qpp9?/ 7a/-e+r_vvvmggg2stiry0`3"`@wgMxNkDt9Ǧ04Ţ~{"ST7U8\.?p]7+;F8]TaWsϲy:_,[(qM~aMj:Rd(z,_1(msf]s""j4 *˲&=a\ @#"}0VJj/t uEv٣˲̽neb988$@k6ZST?%˲bIggg㌱, J*>UVg87?>8KD9" DěflO,kOb`v:Ͳme0IЇB:~(˝b8S)j5x>?CIЇ&''///O>8m(MDZ'rtRYXIr )LL7^,;u8klll%zE1"1b1:a !{yރYƆT*MӼJ*XO>Ud,}?ENDD"7=QRlcQLU0l6[c 8S|ѣFŋu*a4\\\LL&ӵp@IZ&Z8MpD%zEwT}278T*A0(J2 C1b0n0m~j5y^JHT* ܿAmCmxI}~RQId<WZVKQ/˟mɩ 5c#'''V5EQڶ8/7=ndsUU1fOiR *RJa iGk(J0 '9SQ骪 !?j9{_meEr#Yz왚N7L<mi"j sY`pz_bZ(tEQDt7*PV'^*&31Ʈ.{^xE.󍍍[Y~hoooW*F666m~ͦEܿBIAD뺳Ah} o|:)Apa:<<4E( 7 "zo^(Y`𠤂OFDA, @07 J%O!"ͶBPؗ]vwwjz@vecf>? >J*(Dċ⤦isv;.罀 899IBdER5n` ] ,˚B,ihcllN`BIIJ!cls~S/~_eќ뭫SJe-9JD|>&; /۶^(egkggG?x<>EQT*Rvy[ࢬi猱_xbWLRv8*wwwWO.ڶYx2"* Tw(&0B л em[;'87 s-BaOv^kU7Y}BIyF\. CY5XsQ&N⭀ mX,vN)LSl֕y\,; >!;SŶ퇶mv@R.QP,P034mq6TRg)"RhrP *{hNvDZ]CeYVLQEEQ0|mc cU|A\ߘYdJ2lR Y>JyS(ڲ\m9J (FLZM0 U]_eٙ˙0 +r_lsޱZ&Zq?~Ӹ;ZQv(Fϓ)m;(k0dg:<<40=֠?a!eY$c,%hr"c̗i-&dEt1v8R\^.\ ~~Dt9ƈ8<<4M@AjȜ.QT* AY5xB4cjX\,k1v\(w|i{tÓ'OW^JIDGX^^^ ϟ''&&RʩT*0LqyE>FX\`\.w*;K7.՚,]m5LAAI5,ˊ !Vc,HĞSeՀ9::O$in| o!"8ζip8Κ'@wjvjơ,0i>}Xu݌49(g(z}uu1.ۖeqyEQ^BAc"–2C \Y`AyY5/X$S4_ UONEIZNMٙm{+JI/oB,iUT0 x%; 7Tb^9t6 '''vJDM09L,˚P=lN !a߇k w_ fvn^[tC1Pf6=juq6~J}H4i !fjژ,1x<^CqmiAr9lB8:`qh"LGGGsKYdu]/raT,|WGz T}VYED! KdnW_͋+B,jzk{{ۋ'eeegOa86rVA<MWTUWUJڲ3A_dqg^Ӵ< fQVg8cTXv~pxxhpo8Z2|x'; |GjRld(0Y5@L&֥iGD4xxh`*y0,arΫ(@:L 0-˚>A`,..~ŋeYoNAPRΎ(9X<eYOv& (R3(2VVDp}P(G(͞<<<#EYžj `looO4ײHHu+QF/ޯX,aXBIqvwwu49#"Zm<V<.+ Qir,ÄTrܱ,#YRU5%P0PVai6nYVJvaq+;(o]زeY1YEX4 8cJ`vZQP(dg(UsKADEQV'ODs]wZQ4kY%m;bFUv>J"4Y, D:@(JP(2JA\ٶ"; #"% Ôx`vZ!666*9UdX,vQ*֞>}0,\]Bcmmm!...&egTee4M{@v=.GAQ%ʛ(Nu"RetDa2)y*J^qo,˚BL&s); BI)l6 ** ṟmkJۛB<0Mӕ`HꑅUi|| BDodgh4d "$g4M<@BILi`#yʪ/وb| ^k{{Sؐ_/+r)PR;;;(U(lX$;AYe D"Qm{mwwE;hzccST}0+Z6';Kfs2˝PR1\׍dc6c,`T0 +SSSx;TEMN9XYѧ è=V Yr6Hd#_Ra~llPVuz}zzbӧOrCӴK۶WdgbhLOOW} dJ*"R}7TUuc 0PVu@:vUUugJv^#"NDlLvtaBw}@I#SR,011qf=yPV}fvEZ6&; @/ٶ=%Q_R)_7HZ_}`LIi<@ 0PV}1f7&; @/uw}҇ǏfYq]ה*J1V{I(; @DIUEQ0 02PT}|>MNN ;;;#Fۋ/f[ƭiR$;K7X59LRvnj}aX=O0??L&~tbVajȤD4uvv6.;K'PTD$(2k1M(>҅O cZ0Ʈ?~wqq\__gHS\M#_vn꒪Rd[Y*jҡ\8 ueg(+9;=z䫪X,dg鄽qX|ssBvnڒV ʪC\(X,+&; @'r1E5 ø{e96MWvnʒuݸ LB!D1'y󼥫+LQ0*UՄ,ʶ_ooo{t=t LM\AYTH$^U*,(wg4pΉVݪjBU|>7H.9?_ZZjeXYYyh4"۶8KfDv0ZA*J|0 =3T%eYD3M,0PVϟ?O...&eSgssB!fdgPoZ2 02z9_r%Y`([~qzz:ӡy3 0E5VWWKKR˲bd2#ehJ|>b5cW_ ū`N* mY@yt~~2L"{(FQ✓,4%UZ 0LRWPBYuouuJ`pW|߯r=z(ʅ8)Y~mE/dg赁/Hh)Teg1fϻCz*=LQcrSXq)Y~4MM|); _REc||dg1eU$p~`FSTWjDDi"dgbh4 Ϸ0:88}ߟ#id˪T*}uXL.ϟ?OFQ iC?vuݾkZ![YdؒTMӖ,0F2M1,kRv3>>ruu)*;uRiIv"R(Z_ml` lI8NVӴ_}U(; +ZVsSEvV =z!; v{l\f۶Q| oa dI4,?adʪ-?!#; OX똢wu3D$CI3 o7p%0 Sa HUT2suegxuݸy/ۣG|UU_ŞOEDr5ϗ{}n~4p%UXLV ; t:]0%HSeja >S۶퇽<'s<+\׍+odgH^V, @:˲bAx 8|\.W%Z6֋󝝝bE/0bex1F|-VWWq)Y`ъy9'ƘswwgQW7LtyA30%\E,0eie"Jm Hm IDATNDƭ,0x B;N|rm4QR(b.;Æ 0|ѣ 0EYB` >Y:~y?>>kXјg8> T* c,KZ[[ ثWh!"MQDP[P(a`YVy$pcf'gJc0V 0LZ\\\c^VyQ]8""5 äiײpHMEQ.6e|d^uxèKIEQ, lYvޞ.; b*rX'^h4o%U^Ob\`0*bUA`aW1M%4iz ۶AP/: `mIeC!5c,`pi7U533s:]\\Lqz%Tn\}i|>Ǥ{eIS10J_@ ZD"~#; bh6yԍlä/K/rNc?Vwh [[[>)J m{)LQA׭4M?<<Я988圷766n `X]IEDS|} @0ɜA0KD,0&RiMuegED"Y4eg9'컻;湺T* !N?~0ZXXHyWa 0; `" RP)*j{{ۋ'ee(t:O苒Z&c|}}ѥS^?\(Ǚ0 HNNGGGs;;;lfZVYv.A%yɏyߧꇇ3~ z18μ(9,1dBYζ=ٙ*;@ZM/5ES>a<Fa;m{1vs""8l6/; V Pb溮jHDS|ޕ`I/ bꧠ^ϯim{^qα(ms]` ٳgĄ>;;aNDZ*cby'8ۛĄ}>Ԓuݸyڝ e[R<?988Xaax'"JCY`<{L܌m}mz}:#vu.]>u9ٛ^Ԓ*UUeMQU0J]ڶcx/^2ƮBvx/K&0 (4EQTT*<o_*}^^^^LRU*e4/d3G:z=t]ws^6 `+|UYFiq-XzE1E (^^?M?umi"j sYI';3cn,*uzy^-H,3Ɗ8 󖮯vvvo~uvvV|0 U!9V92Ls}JVkI0PtX',˚bZ:>e>zhY缈1\ם&\.WeP]~'GQb11BxkZ>SEDRlFQT7@ؓ0}*}PQb1VH"hF ƳgljH'"]srHD(<ιGDD"qwR. *.$OBdO {t=m{z@08...&onnf (kD$cQQ麮kDSE<1۷ !F{֖?uA莞NRтi^SLV|x4հwX>Ϟ=SbZՊa2X\ 9瞪^j% {{ۓ{aFQFA==,+&rQ {z=JeX,xqDЗ'\6uegPDY;ytEQT~/ muO[u'O`]ma<S0 gT,*2 Ec~=$MR"c 7#s~To7P'"=Ln>(cJq-|9痊xl""q+++ LRpg4̓^gm[Қͦn0 5q/_GQ6HUՈ"MӢ(0 CUU X,b1w=l}T`7͇LKN?ؘEv_E Iv\wuus (8?_c_`M!"j !w AxTbq&|(L&ul7׀x<r9LqcGoq]4j~C!D 799y a/^i0+YFE'0?y ?7o$(2~; ëĞD_~=~ss@U cccWZ9T*u!; tޞH$6}ZvmD$*Ѡ~0:I_jVU7<ǐ*^,0B_%0u7773sssRD+U6'onnu]3PR )!2t]OP,SA AI*˲<ϳ09:==MѤvll\T۶!(KLX<۶ N벳@gV.lVݩBFv.M^J6T.;`umjwww,{|ޟefffFQY=EQ^g*pȜ3ΉH-JsSSSkgff|M1v^R}akkaYFD|dvZBm"uilX ŋeM^c~yRRMƳlq,xRh!prr<|dyy+T*Ӿﯺ8l& ̶ر,rlccs>ӕ][D:d.ƛJ2-#(%9F]Wn=ϛx Sotryq !|ʅBK$) m0JGQlVEƘ"; 99$xSm{{ P'"|@]W,&N7egu_3UQ%Jaoo}2oFyy3B?;;;Qg%l۞m{-x<*t:}^T 1y0Hm{2J7o޼i31| ]a8/; td2Dw#3SVR4?===i{X|cLA;<z/* 9>@VUs@xI%}G{WYONN4E]d}0A ]p||J`ii1M[ϻ FLZpΫ(KG'jם<&|6~||955ujjxq)ϟo6RlT*)ٹ:-J]1Ʀ+o( ?oK"V%0\ם&">Hnj"+4}¶dRY}?( {ؿeDD[[['GvAXd$]߼xbFv0ϘQ_u8ٞ>}a*Yu9DTJ+T׭\.w*;9֊VEa2%,u]>LTzOyNC"Һ F/~t,=~oC{XmZӯ^Ӝ'Y46ai0v=s=q6eg\[[[AEqYvvvb???y_7b >ٸz:~#; uxE\.WӔJ fcNPhrPuJRL?籁:@+ snƫ9/QW*z=zXG&<ϛ K$oDXEQT. ͺgޚ>8 $$j+eRIE!⪷||\Lgr_%){IH@`@,z/8>`֖6HO;[Ng%pWU]y'X,N|pklvѲT֕16$( l%w5[EQjPdgnX<*6)}E)5x- ~!D(L޻(޺wIdtƘE~}p'n:ƺj5JX^^> B{1LU@✟ضwOa I,3mf8 {8ݚ`fիWn& |>?zeii-; \&MR>ޞ2 cԒógd2nc\.7#;-noo?FA5M{MQ]X__0\ޯ^ZZ*7sf?T4V Z.{n? `|~u]R>zȑ 4}L&Ov !D_[[;+ [__aw}'^T 1LQI>NmTKRweeeWUճՓLe52 (Q^~~z0gmT^%(S(T*M !&VVV8%7LZ]ݝ*(X?BHRiP'eYs+++EYv\R !ڷ~f a3(@t:m&ɟ,;;;IB 2p#О?0EO&=VV7|X,l.b sIfCF7 Bȶ퇱X K@JHREػ\.UX,M$z4F)~m~xׯqV ;T)J)C>b>& xbvZ|.HcӲs2M+Jϧ.R)BP(|B,p7w.bl64i'O)Mi3nRJQrSB̵,0^~=k<~ZrX{lmm-zwTRyFcY/2)R >Y(Ro߾@S0@^YY)}i۶ٳ{ kss3FegE\Ӵ Xag{{{ˮ{!&ٶ}1&WbX2!;&STbKG,p?w*c\.u]wmmTvHR50 ÈElZ `vsss3$;(,+n>EuRjʅ1\n4w^f w (Z82. !fbخ,NͿ[iŰ<<8%`@NӦ/^8-J!y0Jw((h"({-W%L&3RӧN"6MG<ifff~?V-ض-mӧOVń\v=!m61۶@w&ZN*~]5ݒH$ èb˗/,V(7@(WI$\{󪪞{=[TWz/Œ\.4M+WdgT*univv6fUٸ4m IDATy`4~8X\^Fs\@B!dt˲,=*̲,*Ш)Jb??dgD"Q{TXeD UU}=ݻ1gRJ=5<(`0XV)V%U6c5zf|;Ϟ=seg趇6t]899 GqSs F躮g.PJa7o4YCUU +`ܪ㊢`_>זڲR0j- h,\ױx#;e~*BUUu__=Wݪj˘bY5DpqNbE) Xhd2iiF^xl.RJ=]R*8f>ݳo{X0n\R={iF!tmV502iu"Z}1~RO)!vJj؏Ꞃࢪ(D"m; hMĒ?.0 ch 4[޽&OADUvTBJ)eN Z4=@1@Q^|ؓ?)lжX__7MTD,{7*^~PUu yE)R,+5/; 5׶m;貃 H$tjBVg2,0/t]?|-; ލJmk\mR)CvIR9ϧgY`x@϶m%N }]SQps!uե,7* as>">&'eM0c iEQ\˿啕,?781tG9Qa|>?+; G9s͛7,&}R1BPYj\n4wRKvkK*!Å޾}MӜbDz d2CƲ,:u~?nIUqJstm{{{s׽z*8N0JumIrQeY%J`PJE<>q|D"qn6 P[x&STMvnHR:IA''',זTe)ZoLvAB)ui ;;;!y`8/^ @ PLQ]hZbZ^p]Fӵ%cX Twæi:@PJj(J/; !$nLn@RJ+++5!t:mZ !nG/gxHvڋ2B>d*lV4KǏ[F#cFX,ph` !']g[b ϕ%U&}>,s>O=i1cF%Zpo6>\^x:b|*J9g=ٙ3 Ԗ乲|A!D_a㜏EsAR{ll,f40554ڇ\V%_,̬r]yg8(nX,>4Md~~(A>OB0xG4-mܔ|BQelyyrRBkbECP'%c,PR5 c*L0lx1V\庮%ra؝6*L---$LfVўa]pmI !N |>@Q3<X,V|dwwwNvlWK>#BH$r+B}gB0HՒ͛7mV? L4R?bMRzi|&͆~>7(SUg\nJvQܣ4y \QR]R[888m~zR*^x5Ms$ ; H$b @!i.*Jud2,SeٳuY;ZR|(n+++מS4LVB0w\VE"s9ϕ@ =Y[ZR1v rR,t:m6^\e\׭PJqC@ g~//URA)5Ova9/>}+P_SED"f? J2pp)*>FX,dgL&dov4;굲*Wrc,=TBj&NBڶ~` @t:]6 ښRg/;LB9ӟ)g*!DիWA9 !*ʮ,MTR/;̠rg6D?侦iBVǩ4M#mwwwRzRT{J$qL@>_,clM0tCG"s9Fn1X(4Y`pVcX#ٳg̶DTv ˪`0DQE޽#)z0.-E !PR@XmN)Hɞ+zaCu,gϞvmyY.m;PR݀eY1,(dgA)noo?9B8L<?*2*]kgggF it:JT1NeRDZD"3Ƙgeg9T0rrܬigăSTTBV~0`YX2ă}%ճgϘ0&*-=˲vvv\+7׼7PgYVJj>C;Bf#$GEI?Q2 JD)z='Xz-k9f(5`dd۶˿u?ʪ7mB1fwww^Us<jNbqRņcK㲳 !*1<- =\[[;z]VZ>nI[5FdgEI |`M5LӜB h4(JR5MӚs|( &`$JNB)J&^P|<m|QR !|B6UT g [[[8Jw`|'; @/ !eYs?Eu^Ugggg|a3gvcii '|QR雛CrEQE)7jvE|9)9z`4o[Gv^fYcKuEq7o޼ǙI&"KA+t*7NMyÇ˲w|2}UVJ%LS]2?OJ*!WeWX\u),p;xjl6@v,Dz,c4F-)^~3 K6iکp7kkkR ~Wո|R)w*HeYLS]ŋ*|>듒jzzZ(t&e>oDzyy3't]m5CgwwwʶƣGF~E]*JlQ!thNrGgc`---9;<<\gvvstiᕕ^r*Lr9LS}S뺩T&; OJNk40^l6pα`fcضmf2]vn&!d䧨.sT#P{k!x X<88ؓ'%T_[[$W!cXUvv4m\~)0!Ds%04(x<)kܴZ\\,yLSBvww 8| POncI7Q.m7q*HӦ|~Qv!DU1!;@7rv&X *Jh֨Of2q۶y:.7trr~CfaaTdgH&}+qE\UVmllԭiTTvegzd(F4BQz]v辕!!9olmma2hLfuN2ľpYQE)Rc/#l;;;NscL@|(~N)5GGNOOdޠڪd2%Y;(UEQF 9 EQt]U\6U+e$˫WRV@W}(5OXPVWV'UU:k!Fcloo!X,PU@``mnn,2cF*qGGGAٳg,v^v>J*۶5UU/~59 e-,BEv4mAUC9EYJjv{dj sO4@}(\:UGcԕzɓ'Y(MLy͘(6zOA[Vcsss苪/^ʱ,0>TBM𑛪ښmKFD4=wG-0T0p~"ST}|c|*DUU2?OJp8|ӓF|9VVVFcd4 eY9nիWAuH$ґehV-ˡ6XJ<ǿW3߀QBuYf!x-; r],Li`0-LQY8nSY}eME#Y`ukJ`(˪P(4AGNgT*dgE:3);M/E8n2Ba⳿6eVJmnBڍj6mra˲s\pj`fh`J_kP|>0?~ܒ#Bٶ}nb˪/_2uT*QFBuY@*1!(O{?EE[ brXm*D"B Pc,. lY5;;;F)r!Dm3G`3 c1 IJ,K%W JY9B!,aUU{9/sg\Q䢔l6$;  !e|>9LQy@\.oooO_s^.2̬뺭,0:!ضjqd2YK$U+cX!$xmۋed瀟=zVU7Z*͛7i&BQUU1 .sqhPP96ZB-˩M􌫪BpJiJ&9J2lǙ&w.*!>/n(8\ !ضh~dbR#N>gdg9,˪ LS!8R?ɪ~X__Ǫ;F!(`%ϕUBqJ)Oۿ[s>[l>kbxƛ7o4UU5lSM?stT_ŋ/^c9WfggeTRVQ!;<{m4@ ; tscC9r@ |^UVMNNF)O>vB8 !+"e_Q`Je4}~Y뺮t Bsr$k뺉D+J@F!e1⽍UJeRn^s^+sB梢(>a۶f`ϟ+Du#Q%wY5\YZZjB:]y* j՛&^x*bzed7_SU˪>UB;N_9n)B]IݹS<0,oZ!mL\EǙX__[[[kLS}jgg'bt:m;ULUa).ܺ,+Z:nPJVPvJi%Wbq9j5lߋ*ByUROR'2*R rqY8Ly`H14M4ՈPUF)7BfjYv}//4D)nʆh\YV} !\J}۶BD UUf^)!9{Y*27Ś&u}TʙK˪1J){IRSG,2<6B04$Na"HAbѧ( 6bSSSt}'7e!؏ RjYUޞ&?9B;)&^N'N)qΙ82ISU`2lHC H|9izu])B0۶$SY'Lq^xjŒ3M`?*JC)=rdg¾TB!c`jhbس)B[5Fz8^cBCi32 *gϞ1qf,;m˲>/O){>LUU% ˲19J*蚧O:`jض DG土LQ X,v=v_MXP~TU%i뚦a_*:!u]w6aj48rB fYqn&@Zt:P('Oزsp~sssm4v|8\.7(!x<~f6Mn<  [J$#TT4M+īWBsBP!l4}'; K!?# rYys~~yXÂ9srrҨV#3Ql6L~TO>ut]4Ր3M^,&~N0E5rҽ޾}:H$A IDAT _X$ǹxl/][U|>G=Tïn66OBPM.--aj4@ B狵=J)JN8Rz1IE!$XmX6p4QOoX[[k1F !đ|> ӔRc}}E` 1B1M'X̎D"M$ kQsH4MLS /Ắ;?D"DzsT0 ;j/_*^赫9HQ,^U,@O(iR4%; mۍX,6&; ~LQ2|63992?d;jFX B"J*d2ydYC97cuu/܋(GGGB!sP(7ٽIEQz !?MoccÍD"b'jL 'dv](Pd 뺘;{4#lGB+B~e-y,!q%}"ԫժNQueObs~(z%!П iڼaO!:RϬ,z0!0ƬfyݾS"6*zzzz붶B}J !&C1(˘[Ɔ%; xJ0Na[Ţy?s #˲l]ׯѣGbrr9gry`۞ 0s Ͽ2sQJv$;xS0l/#&XT*}#{RYi&! jQUVfQ@7]U LJQ''' !DHv,\nn1E_3;;l6_L %]?~w`h0B4VU%!LOO!V(o%;&UUK_ٛ e{C)eB3/()*l6A֘RYf.ncnm˲nURsQ8[Tf/_RB Y`48co߾TUj`)Dltɤ); x[8n~B9zA!]-۶o]RBLWVC B˟h4;;;æ ;EQf !G_TTө!.; x"|Gv^*4˲t˲~0!%uB!Xw*!dvvs||,z(ԉoFsM@d2y҄cr5eQa}!B)?_mۚWMTq !savm)N$U1UU5lCI!Je'7277gJ%qpp0^Vnw"vmAw(;6J:fSd&Idr>닄po4ͪi8/~ BE,..g /w:Lx͇)4J86݋^,|H#;nrMOQVy_'8>kR޽VV24dg+}R@틓E (Ez !4`hyf^B!{UUŦR;Z۞8ʣ8 ۶CTpFdu/_T~N4MK. (J)U V<ee%iݼѱCPT* hb1}g;;;Jmozl333;*Z6G9իWAuLe\~&5r̓p8܇_Ez>T8N74z0 f$~<`FSv }]swwW'򈹹v^466s /_* a8Bexs6??oWUn766<leeC`}(8277׵1d2Bjz=T*ƶiRbq^e(<\y@@wmY< 3MSaяQJ(l6dZ\\[^)J~EQ,𡤲mv]W%twCPVӹ9iO:6ӟt?aҍ*ib1BHEvv GK if۶m)Y]]8R- ,+Fّ_>4BX\\j QQ5gϞbq"|'8Ni~`/U۶5߯|\>]gEQ,Bȵ40J>ReB^T"b1T.t_?٬_QR0cqV!.TUߝ5ǥ  JCfĄjn!?O !Xt9R,}X,,RŲm[H$R B,z%xR:6sk߷~۫'(ѣGN6eBJ)o}iH$|^|,//POB)bضmf2oۮa s' Q vxzR;??NMMúnbrr16K[UU[_BTfbl+dgY?BZ6]&G eUYT#-m+kkk~ZW."cBBu[u#8qBAm{jmm `$}(b=055,ˁb8Dj=~;J)u{iZ9N%reU|!d?{4m4ͻLQ/Z8?/\1jY;jL&pN iz`T}((N&rMOO\j:MN)5ݶP($ !,.H$bzGL&w:muu}(t]WZx眙i[[[Ưڌj8 9BHGFV_'fff}rr"x(C.z@UU,ϣڹ\ (¶m͛7Z:6egc-s۹\C466FϝiӶv,_} r_`uu,2y"h4pF<Ƙ? Uz=. B3` TUm"q%Je?9LӌMLL`<o7&}777gBZGGGlkKmLOOclB""9a ]d  C9r,9??0>)c6!R},m7ƻqJ)N38yT#( L69A9mہ^ .j&YqDH$bu:8D8 *( Ø(nGIO. ` 95]B'f)"X,f׎Bp]K`PJ-uRPV缙f`DxBu'Ūe!D;N7dgOJ*EQl]=QRW!]^R, % ػNsQV]CU=`LMMM<xt/^2?K|qT"v[=??HRQJ~J(wڗpcF7lƒ?!fÝNAYdnn.J)#; }RR9rIE=z$fW*+wݯl@)=6M_eoBPSuTCJ5MX]]-Gz8ʊ8N"ɏ_IISǶm.+mD"ZVbqk[(bU!U׺A1etqk'`0mooϛ2]  ^ƮZ^^A`?Bm$ U׺47giD1jqΧ~}#i?\mK*Bj~T*M>]R'xg n~9k[޷5OeFz8j „B,^wYI%Ȁ}HNOOTU5>U/_-/Jl6}h$n8vG~T"8Kppq";;;Xp_T>*w433Ӟor)Bё [Nt] ;G Y0l:O?48N pd2e0ʛp8Bs@w<{i 0Q3ct&>mVR)3qmp\nVQ3"<,͆c%(m~y6d21.&\uZV2*[[[B6'a(Y>>(^5erP(w~a4@tCBϷeD_TFÞrl6˃ u]so< :Jv]יLLLܸ_4mee嬋<%!XR]ԛ3Ƕm.;]6uL3:á#~(JY /6N6_!D===Jt0 ~u]111߯=|,W&''333mBEj)5Rj;cdgLXN>?;$=\ZZz';%z8lƄz$s;bb*LZBI*F᧧(s[3,//w޾}lll|iccKX,ι{J8kZ3 104u]#tdg)J:Nxg2s=&?կ~h42 /(Jd.&ZFbm3ۡP/eeJD, +R$j6]">v]ВNLӌBPR}o΄iD(v 0EO&|bÀVըiX_lN!vvvoLvu 8ormm _>J 9f[=zucfs 677C!d.3e5M3(ʎ ;in?~Ν0pD"A$rUVE2uUdgѻE봔J$((!m[JU"  >XR?SG)gt:s\!T@ax`%E ?-@rY4qe ]Y3-D RhsssBљniGR;s \TJRݻwarםehNf}%K*!R{{{k !?}9Ǐ0 ]zA]%N|>OIqٶmʈK/{Hq\uNt@}MDNuǏ:s R_\\@npv||KJa? T*q2YVmooOu:ӹ9Ow-r뺮d4_dii4I1W_}GQd k+[%=7h0M?-\uJ*yvv6{rr`P6q!ԕr\^6]`X#ILQu͠U2 )7@}Z]] $mmm0_[[_4gq `/(37eUѸ:[[[sugaђgJ˗/ au j#g$I(>}ZtGMMMwwj490&>7dIe۶Wz49˲8daP;~{ye<Ν;gggLQG*)ls%U$i}+'$ jzhƄ=%BJCY;;;!/8_,dE>l%U/ ]ͦ!pCR(Il:`ZtalLQ铚V9i]^^՝'K톿0 8~h-ܜBײʲ^ 0 w%I2&RgYϟ?wlvY31zVVmnnN !:L@ \7n(alkRʳׯ_΁nKiI0E5x]VB.~M \2677M~`1 #?vsaԻ&8T*gTTۮNWUnofٕTF^sss^7L_%I>z@qEMk@\:;;(8-//3f }DQdj5Kw|XE.ӁPJYmϟ΂hYl60M)t钪X,߸ښgA!am3MbbEш@mnnr5)(VYalp5W.8v(^Fa%I2{h^YYY95M Fq4PؘhqϕKO$ɜrGJ\?08>>JRɩeam4LQ 10Untt>vׯǒ$INo Kw=8;;_Jٶ(svzRoᗥPJ͝pԕ?>~8R~a.//RQ!,$Iܗ/_rLdraaa_w\.{k6hXLo <{8]W9Eё)!SF88Z%U$a9WՊAr;fURyw{lF;ö8y)JR*;0(jڽ8XwW(a kTJ)_KW^ݷmxee^4@?Ap{Kw ӁOPJIqӝupp0O:ԝps[.;boo/o/,,n4@?ϑJ/ݫWApБ'''juKw@w\R*n95~믿$q5&w8v@1;^$ٓRr2$aZmJљnw1rq_ʌ(by:;;;R3!STC`DM1.\,˺WVwzǑ)J";fNډ"]םJ):STCdEQqeee_w@]bQ:dG΁ߊؽ`/;zv/"ҝy޼a@ӑ?nKQELZejlnnΆax DJիWKN.cy:w677oY !BY{{{4GJ*9,#)3ia)!EW Q=FIUZXőQJyV;BNRf!%˲jGw@o Nwwwu) nf/ PJ͚ՐhZy!8`8PR~cP)ő9>>v-ˢ{X.2D emJ*o|^$gϞ3"%iÇۺ|Pg_ӝ?(x;/?OQ/՝=gy1?bTjGBDE({0Lf0djlmm-\\\l?y$ѝ%>|ð(8 | C^04)I˗E9GY$IE [__\ם~)|mGeq/0tmۦBfٶ=s-2./H)w?~HJ*'>SJ!FGG0 )IJ);;wʰfy4hiiTw@PR>ɓ'aA2r$; Fcֶm}^HJ*DQtEGRo5uI)eGQTT*gwvvvMl>z(ҝ>TKώ}ߧJ_Rga*ƸZH'J*H)c˲Fa28vs{J)4͑yveal^TKSJ+n1FFFQQR!3f x;zgccc^c~\ё#J%q#RV$cr#`5fp; (i[ RJfP(0E]Rjc~O\Ν;s ;۶VUНRJ2aQFakmm-ԝ~T+;s ;nKV.t@oj$I?O%JjG){?C45M;p]~)|; 7Ǚt: Y uNNN1̖((0>|8-B(Y}3A[]] tg J*>GJ@RJiYZZX8YYYם0Xp 0Bn0U|rztt`*8 R-A p-Q8n8n,O@QJeMʠW^M[u\TkR*0VӝeEIغC-:B0E1F~$sss,EI$I ;0SJ%J)SwSR2{ 1FÉg}Fw`\RJ#˲ܗ/_ WGQt$XCJm\Zh$&BrB)d$LQe$I,GINsjYָ4M/ C&jZ^_}U; g}}ݲm{riiiWw@6PRndllP(pON.|RJJ)΂bZJN@WPRndff )TJG*ZJl0<םT}ttđ3M&TH0 t@WI-Tr'''uVJ)u]#u~ǻa !ʐ$Iv?~+(7?,I19UǮeYL!u,˚}) i4BQVOtgd%ƞJ)u\,o1R:!xŘg*ZVܪT*{%*ʙ,O@)r9J*hf STB>K),AI*)ebfh4lYMnE,OG߽{n4\.s,,}4x fl[[[ ug 3QGRG3q$yۥ/GY~1㑙Y C+++R*( 9z=o鼔rKw~AI)4/.1iwQQR'(UJuk}x/胝)۶WVV|Y%N?t(T*}j \$E_j69텅^\%*ʙRjԛSO<܌eҮ %޼ys"—(by:F)eYT*纳kZ)Ltg}(=_{B|$pzz +^x18)qKKK!T,]պyӏꫯ)%?qcO>bq~~Lw#={fvww_R)MR}K N+`cc#;Čaou$ ޽{e0Z=tgc(}1??KPV88,Oǵ)8G _j$I#YJ*@_H)B~)/ Xk, Aza/B9꺃pTq۷ݸ25 I*\w}g)gggOtg}/-yePR4 ~TJZe΁T.GFFt袡z/+DQ4C@QRfmm IJ^D(L)Rt;K {m)2 WR0IٳgizLNNvy90RF?, AټmFz; AI;083 JBlooO?ST3hΘT*A.J*@I)OtVVu&T(Ejz;K {˗/˾￑Rƺp]TT*gJ19.a`P?ƶmWJ5HѸo)?J{80d%樂mJ ϡT?~Rt:T !STyrr2qCwnjP LS4>~RE^#s }˲Dw)۶VVV|Y)J*n:({>&5ySwRJ&I2p;^l6sJ oue(ZLOO0#hSTu:X)e΁ܜXi)0rRJ2 a֝bʲ,V z).SJI!>,ѷV5!LOOz%@z>G~/eUX,ˢoj{\){ӧO=~ mY)~ʲ4M0 J*J)%ǹ_. )z~1gǏnmthӝGRUVMNNvy9^`^PFM45H J*Ve sPZ*Ar % ØZZZӝct@ !:v:Tiڑ^VrN^g bgggBJy&X]lN[ut9@Iݻwg CI e!"Ii{Au:[J7@fQRzQOiJ)~&g{{NEˑ,B\ yuЂ ]fsLwMVVϻ!Diqqi{AV>|s& ݭ[N0e\ IDAT֒("4>R^ߎ-`*~_VRʩŦ@ %@{]q<;G |*IX)e5^Rʒ) Vͻ+Lt(I)iQ`'=+R˗/97677o !:J%ԝ5j_@?PRR0(u*8TC44΁KJN;BIH3q(ޯkeɉᳱ1_.Yp9%0WWW; J*@*<|AAwqYOJy(f BKw\Nٶ=-aCIH 0fi1nRV q Rŋc\)m 97J*@jqf6G.ZeU>766r=ȃRڶ.w7Ww eA@IuuW- FzۑBJ%_w|ZV.../--TԘRy9UoH[v)!RH)YЁ *QVѝc}冿FRTGw|ZBSYЅ *RʳNÑ`YUTB˲XPqccc7d哓mAЉ *RSw{*Tߚvvv IOw|akkk,DIHjI7eRo 󠇂 (mvQ fq<; QRR' Coo7B*3ReNA!l@PR|\w,w^4MeL^a.//Szy^Z6u -(+92N90LӜu7 F18ή2֝RΑeQʈyST)_ ?77w; iBIH$IN]e/Ur9BlVPO>(*ϟ΂yyiii[wҌ Zkkka>7u2w~~^5111Erry:)e; iFIH$IGGG(=2>>FQ4;N)eqbfqgZӝꡉ P(pommm R. RJ; iGIHwޝY5;G))%RJdlvvDw|ثWH) Y|(ڣG(KCQϟ?wtmnnFFFtGiN7 J*@Y^RJybPJmJ%R^֣G"YTt:csdU>wMӤZ;!;Rj ,CY$TkgmG ~8έcYaεZm94T[[[ MӴtȪR(rs>|8`*0 ϿkOw %`Pxy!q>R0Mґ,xZf%I2FwF3uK#w0^|9mST)ʦi6, "J*@8>>>㘽T=8nGt) ˲&*SxTJ)*.pMTޤR2M@0EV|vvZw%`t(zxQQRГ'O q2ER^,puu5НAFIgr$ _eҲï=G)l6|>R(``lmm'I8z;RJ&I2p; / BZJQLn 00kO t=;wp/E677'9B0EB/^Ap YJ*@RVѝ#C)%m۞םeض]jZ;Tb{z0 0 Jj 8LQVI)ѣHw 0Pmۦꁩ)}SRJJ)ΘJVKdtee 0P>|;G) իWw_|΂?t:s### 9J*I$BXsd8^dJ|>?yS:)ի;333,d %` VőPJy1~ǻe-//(} 4߾}, |>vvv6;G)<˲XeYӮӝU*oY@oPRd4MT*nE|o5yaLQOʙ9233s; YEI8RJJ,yNU ;;YFIHR]R@ӉR,ﳿow8nW*PwVպc۶Dztz 0΂ ꁱ1VqnݺUR΁?0\םV,K(iyy"cn)%^Ne*}Lo,d%` I)_~uiaT}$,YU (>xeKJyk~9S.Y[͙НaAIXia^.ꫯ p4ͷsjڄRʛtg`XPRVѸ0MIRz=;Gֽxb,LQ!bY:EIX=8tȢ\.繮^rm3E2z}&ϷX@QRms޽{7jfX*|YRB\>ԝaCIhAmRuY\v᯷ˎ0E2Ng޲m9FTfY9{z","~4,?o߾Ͳt jI$;GAR|Vі?fݞzٮ, +>x^Qd89eo޼) !STR,grܛǏ,M(/@.r%UyWÐ]T)_Btg`QR}wk&%U,?α,(D};GAPNBlssnE,K@?J*{Ix20F^.(TJ 8.ߺuC7QViP,g|#LtgT,<"RuR( aħvQQV~!*H J*@&ϟ'IBIeb Ð hVJw?BYc,d22Mҝ#k ,b666reٕJFQt_,d8~^w,R99RjV_K͋ BT\)ѴPJQ\qrל=`dddֶ]>Trﳗ:f k0MsVJy)ߣcYQR2cnnSJQtY.쥺"mfa~~G/AYuE4tGIȔN7 wؘt(^nOQe%ݕRWՎ,(R,mf/Uݾ}ۋ(,4GY_X`dJ9TySwA5c枦b8ް,tdS?ݗ$IwqRV$cPT !sssGdϓLtR+J9fiddD v|xxZwiṮ,Ţ.*8|O?A)eڶ=^*Ntg,jڄR[[[sugFIȜNsE{4M0 JO-9NwʐRNUզ r($ɹeY{utnOOOr /Jb2֝\% sVWW;G(R㦚"STɲj$9Н\% ~բ(iߚYZZ2UVu:9uuWUL}B)5* urqQΒ68EѾ]KQfy0ÇmY0IȤ{ɢ.:??gy{( ԝjz;K dճg gvtgWGIȤw)TkttB=$ITP* ؗRFdғ'O ;rtwRFEw+JVK_ߣfxwZz3J*@fY>::*Α1J)營5et.} }k)0]I|dVv{Tw,mF'PJ8-,,g+Ƙm,(8΅J)|Oz8Gb8'-e4 cvnn f(UV;N';GܚU$sssֲjggRHJ iIčFҝ#+ܹEQ4%d.;;E>}/𶟯 z ibܶ92$rCA)%2ST׷*r$,K C& }8>}jΡKVO,)ӲVH)eZ=kdEǔT]P𧔒Rʩw ^UR1$ J*@I)a?mb;??owwwBq**)8Yh4_'u 40 ryzKKKLQ̍Zfq|\.% (ɟ^CWLDQt.ugɀkOU)f]ݕR2@PR2/ϟGQDI=q_nNdSTujrfPR2T*yRʡ\+aF#;Gloo1MRDdԥ*ui>Ё yRJ%P{]DZgY0*U=!˪ͻQ]TN3>:`(qh4T|>?G($I\!STj}}B7"^ q.,R$ /˲C !dP(ǏYZ@QRB\>K*Jd;Gmnn޲m+ˡ,joo/gF\.z 0eYYJ[Sw^2Ms4ͷs 3֝%`hDQ !(D)}75qccc\J3EOպt:333,(C# ËVő.QJyIdv/RjֶmN};J*09t:T]r-LT/^AT tgVo޼B{*0$(Ca&&&(2R:>tCIR$ CCt=nN9۷oGLӌ|YU^e J*Pmۭj]oQϟ?wt&uLQi( Y@QRJmKR^X̑~aD)LNNvtgFϞ=aΞ 0T/(b虦jtttvll;ǰoZ; ?J*P=۶ssda^(޼ySBh4V>]TtgzPRR* 슩)_)q4EQSJtgz0t8ndb'-R}NA!ݻ2jZQJ)NugPR8qV57&?{mnӧ$k!^TtY٣J)|`z=o9112])ʊ; Ћ 0tVVV|˲9B)Y5%iJ)4X__7$\ZZ՝GIJIčFҝ# *g@T8Ŷ,èP(|KJ 8ۃ<2aסB0E^.r|; HJ*PmBJII%IJL{#U* Y呑 =(Ci~~}J.QJy/_FFFfsST4q0_QR2,֝#+iy9.K)eaX?םw7Kmdi/IoBHdq#c=]D-{R|%1DG*|Ha!l@BRf<(ʅmL)SE X8sGA$ !`l)zNPٮrdBZVLӴb|gRJ?Y@R{3;;KR#RaR2YMVz3Y@RƖnKH :qq+i4͢u){fwwqs!CkTT$!`z=BH)A$I63oeTB*z- $!`mll[#`vOkȐjgg E5df3ʖJgGHkRJJ0B q;SJ9[ThQ uijjj?9h0<5R%ʸge0N:YI^6 ]^^n=  Tn[k^A{+9ZiΕg'd-= TgF*tj?o橠E5TSSSKeI)g 0RLsRʞeYֆ ,.4͹f܃BH{R@Ji!OiԘ3 #!UV[,LТ@J|B*vONNXm=)e!Փ'O iܳ Jr,`R 0MjKR>wmv/KqsAjl.A*9h"@tچa뺱^Z rWWWl= MT!?=cyz,RZk3)aOFZT*qF!BwK`yzhRޏ?^*B,=x$WLNRJA7B*ZwNOOc_= 9qgggQkVAX2$f3ʖJg w `yz 1Jk-y!-!.]__=}Tn۞<=$qb ƂbccC U^6 ؎{0x믿Rr/\.B+?w\onv,n Gqh~djjjɲ3),n W霜 tضh111l-LӜ[__o=;T;Rnh gRu=Crpp mW IDATPRkD _q]Rdzo޼JH8N\. Jr,n!WRLs:4{i<,j !hQ m =T2R\^^ xHd eѢf9~kss,!ͦ#ug m۝R s~hZKJ0YDH,j_]]K%aU?y}}qZTCd J#)e,n"Mpy:am;ZYc{{{Zk- o5͔R*u,"z n<^ʟmiZTCr}}]bY:(B*>72AϟOI){|Cի)4vܳ  !|Oi#RT1JѢy^޽{q>B*n8NýTCXuٮ"ҐxR)ӢEϥA !7}voRGX+=u# ޼yS>L|CAH چa|iHa/)edO>Zr\73q+m>y${0Ǐ=4?s>rGk#LMMQO;99IkSkkkoYnW !L! yF4v^MƱY,Ow]w5Nigݕgㅐ zm!DfGMPe+BTZTbqqPv ㇐ pnM_Ve\-Ңzd橔ҏ{0~͞eY΀?ΆUu㾯UմҘY^on}} `<R Z`ggg_޹dÜ*iiQ AQJy   -^aiF!,N)﷣ 7ٙ<ϬT*q!y^4A.// l6TJQrض]4 UsFH'LLL 5raIZ!n@moo,˲[M)r,`R ^{1BaUsm+a{d߿?l 4B*>q-%:j6))ѣPϞ=s{]E8n,8Rg 3:N< m_KdPj jQMLLIjlq܃AHgYվcy$Ul6SJ)Ç}7ֶaR2{{{{Y 3v$T%"r]wy~~>njJj':q$!__K)v&UFQJaB,>{_#3IZ~C<[}?.l6B rss,FH-8y(~m(aUp֩;PZk+Bpl#F@`Tq$UVkY)*-!-Vԑ2{Rp muG5zoa-LW*PZkS16¹w{ò[[[qpB*n!]{DT/[ECj:NuWq1T»QBТ/_.y*KR[5Ӆa}'эXtzv}}0 . @(aÜֺ)m|}߿_իW+=ĉ /jkΎvg766#?q0!ɀf|߷*Eܳ /y^MHNsֆisr<+Vhƫ!_hcc-7^7WT#ŋe4O-ٙZwr7YEHR*0̸/_ 8 Ӣ,k~}}\!ђ,z܃AH@|w`y|wMe߶"yމR= @TvwbՓ'O >%U!u[k=:&@RRЇ\.B?t]w\.~ۿے8ӻhUSB$$ !}oz;Re4OA?߯J[~ a:??p]Wnnn^= @胔R)~I.jz#~Ut~~^ UsD >I)Fa=GԲeYgaZTAev'L*]mQ}haU))X!xkY{wSt|!׈xXU&Ç9IEH@D HZu]qaRԸn2J_ZӢc ( _,N1lQ$Ұx^q&@uTD뵻n"CZ} Rb:MF0B*"8N0DTBe˲nQeR~.sêjZPJI)g)TDdss{{{J)\.w=>-h8JTD(U<0q-OfV!טu锔R,KTDJ)6 #gRNgLNNkL|2jZÇWC P*J^۷o^}ppBEZT)ִޱ X{!8==MkǏ_{뺫>B0o[khRwӝBB&{|W{;;;Uncc]DH@ĴցӴBiQhQE4eԑ2{$! bJWbKuyyYf}jZJiТVѰR٭ӸgHB*"nRbyyJ)ZTtJJ)܀D,N-˚󯮮 n}Gj5577UA^Vj^= @Ѥ brvSq|IJJi},K-..vee28zD 155U>{;vJ$ATOf-j󼆔R= 099YeRxiٯ,A[T$AF!;q2^zU}09{T$_ivv]T9ZTR{2[<M*u]ZT8??!Ç7# jZJi]=˨;==]q 9qT$R$~!jl&VWW2 ` jZaeI) ^o/9i4H aТ ˗3Zf/YiT$vʲ,޽{gut:vvv<0gffhQl6LӼx,oQU*A>tªqΥKo7۶}4  >UեT*F Eu;V/=0᧾F)<$iAk- yw(#VAh橔2Twrr22s٧q_)E CV˧R7qn2aa=8iyO xH i rR)MF&:<< !ORi|qsgT|8'w%H"*qrZ릔2T S,oueP((!_ŋ333IoQ$1LV3;F뒝NdH@H-i ۶㞥OhUA: ۢ:88!RBVWlT˗/ ikQ}(^[eMmmmnQzawAH|wisj&C<[|Vi)eFs ~T¿.yq[;C eW_}zwoZد= oeVoZO)0&^8 ;EHgwEuêFa}Q뒶m߷m\/k"{ΖޢIdaN$ٳgRjruuZ`ss ; EH'&''#fa*󼉯:t711q_kF٢B4cT CHGh-!L>, WX,0vSJ4RM*!#Vޢɭfx)ˡB9Dz,>!@RpeYTREu[z^0HZTeMݿ YASSS aAZ-:C>V5MutR!"dJ_TѤHB*>MӜ\^^߅U. !BVzq-*!v4 jm͸QR!ONNRJ){ss2{{{R?/M*0 _Z۾g Bpezл{K1.lM$HB*~edcx"žu޽RIJ!/W0&c #4fggeEբzȧ-**1@H[Ң 1M3տAp܏|IdamZkM T?LH)\'YFdRjqff6o .VsB*X+Ѣ 'UV[,S-jUiuѰ~D 0v>}BZTD٢ ty__aeYA!Hj355Ui=(EUVi;[[[ǼmXkR)&`ez~~,raZK\?-ܪYA.!@R4-޷677/Þ* 4dXl&HB*xeYjqq1̵vQۼѯ_7Ui*3HR(}fiADѢ-I)ѢovVٶ 0%T)O*^WJEҢREu)J)>!@RB:.MOO=(k6Jrҍk80 B*!ygggӆa\΍{Qv, ݢB0chQM*!yWWWt:݌{gz7Q.߆=ŋE);77׉b~a <)eU8Z-8N$-*˲ q<_)E AwZ{Ѣ }?&AIhQ !Ą/9 Ax8YjuVkBxq2ʪR:nDqeY)( u]_J)@H|/nnnҢ ^AL=իW+RJw}}:T c=?Bq]7:,Vw8+ZAH 4w+t89F~Y؃jZAJޢ@2Ry˲|U9uSJÞ*ž EHsz:-pbfcc4AZT-*| !N a\ !ge9!qT-*4עm[kKT;2-pd۝T*'azߢw^;y< $f;׋ZT*YF˗/M5 -*!~ RT AHIy۶-Pm۝Ga dAs ̌/Y !x0Z\]]ER*y,Us M*!/_L<Bgeo߾0 =˶QjQ !D* Y !x0~6Msnmm],kT[TB!<ϣI\T*u}|Q+Ylv5Jգ $ׯMlr97YF]E,+X^^bi5m* $Z^__/gufz(zz `'  !Tܳ^7 #tEu(抓eYziiH[ί=˨k4"STB_khQy'G; $z)6:YF][jZ!#ߢBuJ H)'b$0R=:{PY3MN4؎{ Paχ~ VKJ m7Q̕4& q'ѳqrAL=x ^/˲2̝iQ !DyT @HH{T*$Z7z۶-LJ!,RmR$!Io\}3ݡΓ(Δp" UKu:@ ehj[8HHŪ;_[PdynU}?Qz\;Jeooos933ﶶr>(<ϵsNCY1fJJ)!'RJs677Gkf)*!0ƸϟSRxFIU3B# vvvjJ)\pݡ/TqN/d[Sfs.8eUs{{{]wrr.<C6E%9ETc{{{.7 ػjʓi4kmug8炵W/1lNT[w(\LVNjTRŚ\ i蜳ys ;J*o;zYuPT !NNNb{e7yI㊢SO6&<8i㫫=r觪NOOjoUBYQ#sݧ/A<)<ܸO.jxPUǑR޹sZ{yyyyW)Aloo/"g( p>ZCPEl_<lnnpaa?z`˪v='z;cbqqXYYnڙJrs`…aXEAI%c&&&nbU:<< sуzvtZ*ſ⋧Z|!Vu(gMQw"x!܈/_V,Sccc)޾};g._LdR}v`9|j賟ADI88SRL(_IlQQ}Bw*v[ZGfs⋵J)g``QRzӧ#ygө,*Tl7rtnh'Y]]=T*ι(NO?4sn[QGI8a}e-vdaaSTBVqwTϿUJ=˲l9תV텛u ej?HӴ~4==鸔$IO? ׵ s/ J*@O Շx+ojJLR]s'S~޽'B8 ñ(vvv^g Y {Wzb>*M~RJy~nvuuy+klGu!e`q"xtm>Vʪ H)ot_Rzttp?E]\\,Ap~tt,-ȲLlmmtM)5STre|jiiƿSu?mQJ Շ:<<BI)^?}Rnŝ(kZFWųoscx/1@Wt}=JdU)Zk=??߳#kkkRʃV;lz rfvi J*=}tR .F///>z铲,SBxTն+Ƙ7ilooO3zMkm4 pmJ8}C]VeY6k9qiI%sssanXkos^q X%Zȕʪ"ݞŋ4+L |cͲI*(ՕtTV59k[s###N+TފI*J*(Wյ}j4km־L7nff&T*?7PEs.(E1䜳ZRdVT+aBGi7M߁n][[{sEQ ŋlƘBJ{GD)XV_1@\>ESA{=Z_^\\,U*/^Go$GT=}tDkONNsepttc?vI%nmmmSqjV(zխ,B! ș1@Ǣ(ZgUZY܁1IWߕR%I^TX|mG( Б'Oԍ1ST=48s[I%kkkAZzt~ssssdY+x/1@G8[EC3yQ:7[fsEJPJMNf, ލ]TP\MLL}g{{{97W_p~~(|}/ݿ<˲ .,mnnBJII%r_MJ)_K)a0 KQR !rs}}iݞZJ)$na©( M > IO< $ ڑ I({OCƘ@)xbEpQeI*(bU~zt; `lbJ!znnTEѫVf}u||ppp0ګc?'ƌLLgf~4}gy'2%Iw^+vkZf9^<;21::{,\%A0s޽c9R`uuddd䅵vZ{l6g}16͛=AIA\>)C#ݝTJI)K|R)o,[YU8\Ep~~>k !L9 % 8'H}g$YMщ(⶧JSVڵZ[0 w#Ox!e L^RRIeQY*w޽gZfl9֚w0aDIC9g(,..6}g$ILEQ%Sʭmaxeقֺ_ `PR駟8fU5(,}{Yc6W={63!✓Ƙ3YIј9*0CUggguΝ1HF\E1Ƙ,}}:(v)78a 1freeh̶]B_םT#(lZ+J1&;==Bc 1)Cc YKkmfff^;??_Z֎i7@1I97WECqY)1VUWw0_YYB`PRzY$ө,bkkKZkGw|g:<<4+JmEv{j}}}CQ+?8J)59xQ )e_aSf.|˷MRz{{{])m)DR3(JT0R۾s ZK?ccc+;;;U)s8Jr#YPT0`vww+iBA) m ܹ[cccFkݷ?SBq!ĉv㙪 LQw011 7~0 >}:EQ{~~od$*SYzc,+uIurrH)cFFFFfggO}͢Qzn !n/Ƙ7sܔ( nul6 ȭnyyyK}/ʇ DJ97( ˲>o&M))3Ɯ/,,g(WSJإ,boo$IKJ9KsaǷ1I677WB>I*CB39T^&cv}%ih,;\FFFv9Ϣ>̽{v| n<ɴ,ʍ0ޞtI)ELOcTg={Ykb`ݶI!*aԣReuu.YXXhԇ`0QR@RZks v=~|I9zܓlZ(v7N(τa8wA_Z_H)X1EQt7VP(lllYkBO $Iƞ70W7݅(&dңg`HQR@};Ġ8<<4ZÁ^.V+j]T۷oWm>[@I}9wvv,bV EWEeYWǚBޤ+4M}GZT'fW !@k}#y[[[R)u7MYTstttkzz6=EI}"(6}(aa/Ty7ڵ>JwJ$IcQZ]]xߟ0pZ(cL;mMFTsRJ911ltttqiNax;Ǡݝ(nyMIO?M'IT J9ө,97=t%U~jsssUJXVWVV^ͱ qJjccc*39Eш,K~]!SEqhgggkEQșuҠ2L-..1(ϧs}! B$I2/|[v ߹_s.HT!n,˪<%9$X$Ƙy 0faz~z(mwwZk~hTr9GI TPBYMa>iZyq.//' ؋IA$ %'TP2:HVoZ_-_~9,Pc<|ZѴeDC7E??J+C)%LRxBI%#;cP\^^<2ds)%;<kQ9ꁽ1YE)%UR2YDFFFC;kIL&Iw"c̥I*1ƌ_1 "믇 0T*LRu /J*(ܥ`qsO~z(_@o '''):@I%8Gzr",˔`YG... J*( u}ttAa믿N}- CRJEAI L9碻w^RIPRx$x4&~asss(ew (\ 2599w %x V/ŔU1~EY (v J*dsssRk%\V(A"˲ZRR]Ae쌒 J*$ɥUcƘ9Zc( s J*O(G)B7C5Uuzz:͛7ss. (R|MgPR׸:::: 1Ç,e✋( M|֓qr lYxLQ!r9NQR-sΩ(ɟ+ZG9J*e;;;cZ 1:99R=B( W(%I2&1IDATͣ~eUKӔ~RK+Tp˴ֵ˲*ѯ;GٌFJw>swaFI0 VPRߔUa癔b=c TWpxx nxsteUQq( ` TTQR-Jd9{iU|éen#OG-me*#J*%9-sss6QwG~.oEB|'U?OQR-VpcJSVy~;G94 Nk0 nsn"M~.^V9F n?O}TWySRxDI$˲jwRVZ\JIaZkn$ItE￯9ZsܐwEխ@Jd,cnzk4Ms#&TՉzwv+UVkl~~~$IΝ;4w+ֺZ2I%܂0 Gsܒ+8yιHoR*2~<397???l7jۣZ>! C!n QJfII%ܰqc w]=+w%G8炢(8v yxDI7jMTa.骨:88YI)%G>)%Kӯ!s1R(9碉 Tz$(ZkJ*-J*AO__c_ɉ1PRxf|A%b;hDB)Oj%5huT1I7$;w4}wFc,I}FU7]C2YQRxFI79ZA1ɓ39N)UkZ (<5STSIoa -א `QR p΍uQu#q3E9cjRnSxFI7@k]f.i:S9OS% CQ]S4Z 3J*GZKιÇ}QVw}J)h4(<-}T]ڊ,ֺ*Zk~_QRxFI= Ð}T]*bD)QH)+0 ^QR@cjLtS_XX`iz0Bn@@_zq%pic U4 zjddd,uիW,}?cUQSAp(8jT]Jt$sQu ZZx5ygs z*˲8S-]ʲ_Ru 1.//~%@I=#nRTJ^*f (GVWWK9]٬+s5HKԄaHIPT#EQqiko޼A%"v!jQR%snd~~I.iZy!cfMJIIcT*T%@I=aJ!Ww8B_CƘ]SR%*-sGչ<ϫT](!r9@I=1::>xm="J1׽0 7I%vDkw{gϢ<9AI=&''S9ёQJQt(s~Q9&J ɓr'\e#q3uօi4.)F]j۵3JEQ[XX V$J.Iʕ.iZM!w/˲@k$@I0I]6::n(u-kyq52&TEpyy.9.8TNA҅jQM9x= Cκd1BJ*.< k=Q_Xk###T]*"TAI]^T( $I5MSJI)+m96կ~N*.dYVWJQRu( CʂpĴw0A(J*s.\q U~GL{#M(J*&6s;km52u(cJ 䄉hZU!ĹZ;:;;M] (fZRRuIJY/)]:d 1RQR D(Y]uOJ)`ss3b( (ʅ )IH0ҕX4Z<BP@QJ Q(8׹Zv!(((J*ϟ4f\cwŊUf ~_1 P"Tp AXcsFkEYuMRJ;?J*(ʕ.Ea>|4KvvvFGEY9)@PR58I.wG:կCEQFQ.ʪ (J*NStogg'iEQ_XX~U !XP2TpE+sş99zOT%DIWq=iJIՁQE6tε}ߢ+i=vjamIЗUA0IPBTpuv)">cj:ڢZ޿%CIWApu}d|pZ*vJJ@lmmEZ&0h48nա zucUP(km;Tp8Yܥ$I(:XZ=ɪHJ@ QRaXZ n, %U*v lYsAJ PBTpqW渑KJlnnY<^:peE$(!J*$I"!D;G+~RN(! 0<::g(C;,GՉ/$^xAIPBTСX)/!~w,˔7mQo =J*\%IJHS>믿SJi}}7U4()J*萔ZXܥ@)^pbrr >oʪ<#J :$I/]jہPJUeUZɓ'1~r+EUQQQT%FIP!eJ)eTTs.@w(3s97L-7JJ|eDpcnJ$.?(1J*XJ>*(\ssssL@Y%~-(@QRg4Xk΁=%ӫ*Rw(3zl\ܨ<ϧVVVqU'*^zUֲ Pvι8 90vww+BDqO{WT]kmipELRdY6nG4Ynj$@QRIq *#L]]Ges.Z]]Os#X=omnnk)J:眔%>w]_c˿??Όqԯ{:99 LQ~IO("NӔ ѣGZNLLPߔUEQTTpyyY=n fO|PR!;~} >A)aHICZnsASӧs ,*=f?>@IMLLpܯ <jsssyo)|QRIASaq}I靑7s "&!, CN繵;OϞ=1brr)TE~}I*ǏvcB=Aa;OZ;q3Euìc 7 J* 8tZ;#j}6ƌܹswA$Ie}}%|xh|DYI|9!ĉà( #}@g(#QT7@k?~x薧;TeSKKK YёQJPRG84M%8ZSSS59nLE 7Z[5pPRGA.,,TՖ1fJ*t*8w ZV5IJ*vwwKM,kǻ33N 1au,^7x=\'jY@A|>Pv":_J*Ck}UEQ59e:C도m!ST}t %iGQtLUݑ뺧gMo*Hz i)%KF %Q(GYu{Z/>}ZN&_7=$/J4`PR@VUJf?ʪ* a:a:}pDP(2I|JRt %ycLRd2ٮ2㾸7cYZXJ>8v j'u(~CR9,+g:}8<*DI}qlRT(q7MtAZ[nw1|6e_MQR@q۾DY mq\2cvvv*mo޼ai:>VX̑VT:{@ݻwiT:2ivwwBE(qLUPJ}e9Jk-SL,K$QT`eKKK(\Y^/$ɓ\.2,Jw:Q(J*衵BJy޾eY sViJ2eRv0(E6]V3qDј5^|]\\aX%ѣG˹\)e '''I۶;sFx(&&KKEC=4-X]] fZ?B| 0 MQR@˲Q5e7!aq?kʝ&jӧOn;qwaa.RnZߞ?^uٳrl0fB߅YGGGNRqXTCJ)D.3>H1^EBl6f]!h^EsZM)B)EEQRRJ8D"a !mgQ+ʞO tiM`PR@1Guݷ\X !r]v )Z(d2 0o: zhr,oc,'~V,#!D433c: hoo Rr,`LXqgJ*!UeM !ڦs`p(* 3L}Tc zLq~(0 H͝TCYRJ/H†IENDB`gammaray-2.11.3/ui/resources/gammaray/update_icon_sets.sh000077500000000000000000000055541413310443000235120ustar00rootroot00000000000000#!/bin/bash -e # This script will create or update icon sets for macOS (icns) and windows (ico) at the same time (if possible). # This script can be run anywhere bash/imagemagick is installed on (Windows with linux subsystem, unix...) but # it is preferable to run this tool from macOS as nothing else than a mac can create a icns file... # Anything bigger than 64 pixels will be script generated from the biggest image. HAVE_SIPS=0 HAVE_ICONUTIL=0 HAVE_IMAGEMAGICK=0 if [ -x "$(command -v sips)" ]; then HAVE_SIPS=1 fi if [ -x "$(command -v iconutil)" ]; then HAVE_ICONUTIL=1 fi if [ -x "$(command -v convert)" ]; then HAVE_IMAGEMAGICK=1 fi SOURCE_SCALES=(1 2 3) SOURCE_SIZES=(16 24 32 48 64 128 256 512 1024) BIGGEST_SOURCE_SIZE=${SOURCE_SIZES[${#SOURCE_SIZES[@]}-1]} SOURCE_MASK=GammaRay- TARGET_MASK=icon_ SOURCE_ICONSET=GammaRay.iconset TARGET_ICNS=GammaRay.icns TARGET_ICO=GammaRay.ico mkdir -p "${SOURCE_ICONSET}" for source_size in "${SOURCE_SIZES[@]}"; do if [ ${source_size} -eq ${BIGGEST_SOURCE_SIZE} ]; then break; fi for scale in "${SOURCE_SCALES[@]}"; do if [ ${scale} -eq 1 ]; then source="${SOURCE_MASK}${source_size}x${source_size}.png" target="${SOURCE_ICONSET}/${TARGET_MASK}${source_size}x${source_size}.png" else source="${SOURCE_MASK}${source_size}x${source_size}@${scale}x.png" target="${SOURCE_ICONSET}/${TARGET_MASK}${source_size}x${source_size}@${scale}x.png" fi # Downscale biggest image into the requested size if needed and possible if [ ! -f "${source}" ] && [ ${source_size} -lt ${BIGGEST_SOURCE_SIZE} ]; then need=$((source_size*scale)) if [ ${need} -gt ${BIGGEST_SOURCE_SIZE} ]; then echo "- Can not generate '${source}' (${need}x${need}@1x) from ${BIGGEST_SOURCE_SIZE}x${BIGGEST_SOURCE_SIZE}@1x." else if [ ${HAVE_SIPS} -eq 1 ]; then sips -Z ${source_size} "${SOURCE_MASK}${BIGGEST_SOURCE_SIZE}x${BIGGEST_SOURCE_SIZE}.png" --out "${source}" elif [ ${HAVE_IMAGEMAGICK} -eq 1 ]; then convert "${SOURCE_MASK}${BIGGEST_SOURCE_SIZE}x${BIGGEST_SOURCE_SIZE}.png" -resize ${source_size}x${source_size} "${source}" else echo "No tool found to do image conversion to ${source}." fi fi fi # This is mandatory icons if [ -f "${source}" ]; then cp -f "${source}" "${target}" else echo "- Can not find source file ${source}." fi done done if [ ${HAVE_ICONUTIL} -eq 1 ]; then iconutil -c icns -o "${TARGET_ICNS}" "${SOURCE_ICONSET}" fi if [ ${HAVE_IMAGEMAGICK} -eq 1 ]; then find "${SOURCE_ICONSET}" -name '*@*' -exec rm {} \; convert "${SOURCE_ICONSET}/"* "${TARGET_ICO}" fi rm -r "${SOURCE_ICONSET}" gammaray-2.11.3/ui/resources/svg/000077500000000000000000000000001413310443000166135ustar00rootroot00000000000000gammaray-2.11.3/ui/resources/svg/active-focus.svg000066400000000000000000000471671413310443000217430ustar00rootroot00000000000000 image/svg+xml gammaray-2.11.3/ui/resources/svg/focus.svg000066400000000000000000000474531413310443000204700ustar00rootroot00000000000000 image/svg+xml gammaray-2.11.3/ui/resources/svg/lock.svgz000066400000000000000000000034221413310443000204570ustar00rootroot00000000000000X[oF~2/ $~Q$E l] o49YSJR_3û(aX̹oF^tދ,6>")Ӭm?~%PW7qyY_Oۻ?T1wʚ'N?5aE)̺Űv/wwewy-UlnXN0M"):!Q<k={1IߗEvp>֛uBXk!DP_&>㭭!6JMjUCB; a]<>>yM:Qsfu"ޛ'uW'힚%3獏<䱐P5'{,j_:A {X(PL=0v2lLCm՜eYnZܛRUF{fqT GiYSQ\=Tj ʃ)Pn?T5x-n2?̄m$VmeZIi&_.܉Ҭl?QD]]$u*&xؗ3;kȦڦv$|h|'arF~kp>䠽얯`17,||v D0(ŚHZLW,b7U^rf5Oc /3{v||MBZ2  Vag0ώV{ e8#V޳ C${Y(PO j+ a@ci1Au? ḀEȴЅ"3)ţ!nA(+4dBa%X\IPh )`X}ZHOr43o^['\jqBJs!켠K)J`PnZKLl#\a6M52:!K$[xDiKso/H]w0 GFR }% Rv0zF}ZHOr4߫RQpESV/qs=zJAbo"1Js( $&9@T }=%\C:-s$HPLKjI#l?U`'vYSd!n}TL &I>J?àHA𕃸;(7L` .*|ÐMIpܣJR !0'&ޢHa@F, _L[Xk^`=' `nu@2]0zLkUW'p{so2iFR2wa8tVW!4UZ l KF d3"}Iv6uNX,H& oln>5)c`z_t(8Fgor?@1"AFɥņy"WEoTaTPnK A}+bA;H5կ동LG~ziTdIXuH'&cȟWÿ>vۻov+gammaray-2.11.3/ui/resources/ui.qrc000066400000000000000000000222341413310443000171430ustar00rootroot00000000000000 gammaray/GammaRay.icns gammaray/GammaRay.ico gammaray/GammaRay-16x16.png gammaray/GammaRay-16x16@2x.png gammaray/GammaRay-16x16@3x.png gammaray/GammaRay-24x24.png gammaray/GammaRay-24x24@2x.png gammaray/GammaRay-24x24@3x.png gammaray/GammaRay-32x32.png gammaray/GammaRay-32x32@2x.png gammaray/GammaRay-32x32@3x.png gammaray/GammaRay-48x48.png gammaray/GammaRay-48x48@2x.png gammaray/GammaRay-48x48@3x.png gammaray/GammaRay-64x64.png gammaray/GammaRay-64x64@2x.png gammaray/GammaRay-64x64@3x.png gammaray/GammaRay-128x128.png gammaray/GammaRay-128x128@2x.png gammaray/GammaRay-128x128@3x.png gammaray/GammaRay-256x256.png gammaray/GammaRay-256x256@2x.png gammaray/GammaRay-256x256@3x.png gammaray/GammaRay-512x512.png gammaray/GammaRay-512x512@2x.png gammaray/ui/light/pixmaps/watermark.png gammaray/ui/light/pixmaps/watermark@2x.png gammaray/ui/light/pixmaps/watermark@3x.png gammaray/ui/light/pixmaps/gammaray-trademark.png gammaray/ui/light/pixmaps/gammaray-trademark@2x.png gammaray/ui/light/pixmaps/gammaray-trademark@3x.png gammaray/ui/light/pixmaps/splashscreen.png gammaray/ui/light/pixmaps/splashscreen@2x.png gammaray/ui/light/pixmaps/splashscreen@3x.png gammaray/ui/light/pixmaps/kdab-products.png gammaray/ui/light/pixmaps/kdab-products@2x.png gammaray/ui/light/pixmaps/kdab-products@3x.png gammaray/ui/light/pixmaps/kdab-logo.png gammaray/ui/light/pixmaps/kdab-logo@2x.png gammaray/ui/light/pixmaps/kdab-logo@3x.png gammaray/ui/light/pixmaps/kdab-gammaray-logo.png gammaray/ui/light/pixmaps/kdab-gammaray-logo@2x.png gammaray/ui/light/pixmaps/kdab-gammaray-logo@3x.png gammaray/ui/light/icons/active-focus.png gammaray/ui/light/icons/focus.png gammaray/ui/light/icons/grid-settings.png gammaray/ui/light/icons/grid-settings@2x.png gammaray/ui/light/icons/grid-settings@3x.png gammaray/ui/light/icons/legend.png gammaray/ui/light/icons/legend@2x.png gammaray/ui/light/icons/legend@3x.png gammaray/ui/light/icons/measure-pixels.png gammaray/ui/light/icons/measure-pixels@2x.png gammaray/ui/light/icons/measure-pixels@3x.png gammaray/ui/light/icons/move-preview.png gammaray/ui/light/icons/move-preview@2x.png gammaray/ui/light/icons/move-preview@3x.png gammaray/ui/light/icons/pick-element.png gammaray/ui/light/icons/pick-element@2x.png gammaray/ui/light/icons/pick-element@3x.png gammaray/ui/light/icons/redirect-input.png gammaray/ui/light/icons/redirect-input@2x.png gammaray/ui/light/icons/redirect-input@3x.png gammaray/ui/light/icons/server-decorations.png gammaray/ui/light/icons/server-decorations@2x.png gammaray/ui/light/icons/server-decorations@3x.png gammaray/ui/light/icons/visualize-batches.png gammaray/ui/light/icons/visualize-batches@2x.png gammaray/ui/light/icons/visualize-batches@3x.png gammaray/ui/light/icons/visualize-changes.png gammaray/ui/light/icons/visualize-changes@2x.png gammaray/ui/light/icons/visualize-changes@3x.png gammaray/ui/light/icons/visualize-clipping.png gammaray/ui/light/icons/visualize-clipping@2x.png gammaray/ui/light/icons/visualize-clipping@3x.png gammaray/ui/light/icons/visualize-overdraw.png gammaray/ui/light/icons/visualize-overdraw@2x.png gammaray/ui/light/icons/visualize-overdraw@3x.png gammaray/ui/light/icons/visualize-traces.png gammaray/ui/light/icons/visualize-traces@2x.png gammaray/ui/light/icons/visualize-traces@3x.png gammaray/ui/light/icons/warning.png gammaray/ui/light/icons/zoom-in.png gammaray/ui/light/icons/zoom-in@2x.png gammaray/ui/light/icons/zoom-in@3x.png gammaray/ui/light/icons/zoom-out.png gammaray/ui/light/icons/zoom-out@2x.png gammaray/ui/light/icons/zoom-out@3x.png gammaray/ui/dark/pixmaps/watermark.png gammaray/ui/dark/pixmaps/watermark@2x.png gammaray/ui/dark/pixmaps/watermark@3x.png gammaray/ui/dark/icons/grid-settings.png gammaray/ui/dark/icons/grid-settings@2x.png gammaray/ui/dark/icons/grid-settings@3x.png gammaray/ui/dark/icons/legend.png gammaray/ui/dark/icons/legend@2x.png gammaray/ui/dark/icons/legend@3x.png gammaray/ui/dark/icons/lock.png gammaray/ui/dark/icons/lock@2x.png gammaray/ui/dark/icons/lock@3x.png gammaray/ui/dark/icons/lock-open.png gammaray/ui/dark/icons/lock-open@2x.png gammaray/ui/dark/icons/lock-open@3x.png gammaray/ui/dark/icons/measure-pixels.png gammaray/ui/dark/icons/measure-pixels@2x.png gammaray/ui/dark/icons/measure-pixels@3x.png gammaray/ui/dark/icons/move-preview.png gammaray/ui/dark/icons/move-preview@2x.png gammaray/ui/dark/icons/move-preview@3x.png gammaray/ui/dark/icons/pick-element.png gammaray/ui/dark/icons/pick-element@2x.png gammaray/ui/dark/icons/pick-element@3x.png gammaray/ui/dark/icons/redirect-input.png gammaray/ui/dark/icons/redirect-input@2x.png gammaray/ui/dark/icons/redirect-input@3x.png gammaray/ui/dark/icons/server-decorations.png gammaray/ui/dark/icons/server-decorations@2x.png gammaray/ui/dark/icons/server-decorations@3x.png gammaray/ui/dark/icons/visualize-batches.png gammaray/ui/dark/icons/visualize-batches@2x.png gammaray/ui/dark/icons/visualize-batches@3x.png gammaray/ui/dark/icons/visualize-changes.png gammaray/ui/dark/icons/visualize-changes@2x.png gammaray/ui/dark/icons/visualize-changes@3x.png gammaray/ui/dark/icons/visualize-clipping.png gammaray/ui/dark/icons/visualize-clipping@2x.png gammaray/ui/dark/icons/visualize-clipping@3x.png gammaray/ui/dark/icons/visualize-overdraw.png gammaray/ui/dark/icons/visualize-overdraw@2x.png gammaray/ui/dark/icons/visualize-overdraw@3x.png gammaray/ui/dark/icons/visualize-traces.png gammaray/ui/dark/icons/visualize-traces@2x.png gammaray/ui/dark/icons/visualize-traces@3x.png gammaray/ui/dark/icons/zoom-in.png gammaray/ui/dark/icons/zoom-in@2x.png gammaray/ui/dark/icons/zoom-in@3x.png gammaray/ui/dark/icons/zoom-out.png gammaray/ui/dark/icons/zoom-out@2x.png gammaray/ui/dark/icons/zoom-out@3x.png gammaray/ui/light/icons/pick-color@3x.png gammaray/ui/light/icons/pick-color@2x.png gammaray/ui/light/icons/pick-color.png gammaray/ui/dark/icons/pick-color@3x.png gammaray/ui/dark/icons/pick-color@2x.png gammaray/ui/dark/icons/pick-color.png gammaray-2.11.3/ui/searchlinecontroller.cpp000066400000000000000000000056631413310443000207410ustar00rootroot00000000000000/* searchlinecontroller.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "searchlinecontroller.h" #include #include #include #include using namespace GammaRay; namespace { static QAbstractItemModel *findEffectiveFilterModel(QAbstractItemModel *model) { Q_ASSERT(model); if (model->metaObject()->indexOfProperty("filterKeyColumn") != -1) { return model; } QAbstractProxyModel *proxy = qobject_cast(model); if (proxy) { return findEffectiveFilterModel(proxy->sourceModel()); } return nullptr; } } SearchLineController::SearchLineController(QLineEdit *lineEdit, QAbstractItemModel *proxyModel) : QObject(lineEdit) , m_lineEdit(lineEdit) , m_filterModel(findEffectiveFilterModel(proxyModel)) { Q_ASSERT(lineEdit); Q_ASSERT(m_filterModel); if (!m_filterModel) { QMetaObject::invokeMethod(this, "deleteLater", Qt::QueuedConnection); return; } m_filterModel->setProperty("filterKeyColumn", -1); m_filterModel->setProperty("filterCaseSensitivity", Qt::CaseInsensitive); activateSearch(); #if QT_VERSION >= 0x050200 m_lineEdit->setClearButtonEnabled(true); #endif if (m_lineEdit->placeholderText().isEmpty()) m_lineEdit->setPlaceholderText(tr("Search")); auto timer = new QTimer(this); timer->setSingleShot(true); timer->setInterval(300); connect(lineEdit, &QLineEdit::textChanged, timer, [timer]{ timer->start(); }); connect(timer, &QTimer::timeout, this, &SearchLineController::activateSearch); } SearchLineController::~SearchLineController() = default; void SearchLineController::activateSearch() { if (m_filterModel) { m_filterModel->setProperty("filterRegExp", QRegExp(m_lineEdit->text(), Qt::CaseInsensitive, QRegExp::FixedString)); } } gammaray-2.11.3/ui/searchlinecontroller.h000066400000000000000000000044041413310443000203760ustar00rootroot00000000000000/* searchlinecontroller.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_SEARCHLINECONTROLLER_H #define GAMMARAY_SEARCHLINECONTROLLER_H #include "gammaray_ui_export.h" #include #include #include QT_BEGIN_NAMESPACE class QLineEdit; QT_END_NAMESPACE namespace GammaRay { /** Couples a line edit to a QSortFilterProxyModel api-like model for as-you-type filtering. * The model don't really need to be a QSortFilterProxyModel, it can be a plain QAbstractItemModel * implementing QSFPM like properties (ie, filterKeyColumn, filterCaseSensitivity etc...). * If the given proxy is not a QSFPM api-like model, a check is performed recursively in all * sourceModel until a compatible QSFPM api-like one is found. */ class GAMMARAY_UI_EXPORT SearchLineController : public QObject { Q_OBJECT public: /** Establish a connection between @p lineEdit and @p proxyModel. */ explicit SearchLineController(QLineEdit *lineEdit, QAbstractItemModel *proxyModel); ~SearchLineController() override; private slots: void activateSearch(); private: QLineEdit *m_lineEdit; QPointer m_filterModel; }; } #endif // GAMMARAY_SEARCHLINECONTROLLER_H gammaray-2.11.3/ui/sidepane.cpp000066400000000000000000000071571413310443000163100ustar00rootroot00000000000000/* sidepane.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Kevin Funk Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "sidepane.h" #include "uiresources.h" #include #include #include #include using namespace GammaRay; class Delegate : public QStyledItemDelegate { public: explicit Delegate(QObject *parent = nullptr) : QStyledItemDelegate(parent) { } QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override { static const int heightMargin = 10; QSize size = QStyledItemDelegate::sizeHint(option, index); size.setHeight(size.height() + heightMargin); return size; } }; SidePane::SidePane(QWidget *parent) : QListView(parent) { viewport()->installEventFilter(this); viewport()->setAutoFillBackground(false); setAttribute(Qt::WA_MacShowFocusRect, false); setItemDelegate(new Delegate(this)); } SidePane::~SidePane() = default; QSize SidePane::sizeHint() const { static const int widthMargin = 10; if (!model()) return QSize(0, 0); const int width = sizeHintForColumn(0) + widthMargin; const int height = QListView::sizeHint().height(); return {width, height}; } void SidePane::setModel(QAbstractItemModel *model) { if (model) { connect(model, &QAbstractItemModel::rowsInserted, this, &SidePane::updateSizeHint); connect(model, &QAbstractItemModel::rowsRemoved, this, &SidePane::updateSizeHint); connect(model, &QAbstractItemModel::dataChanged, this, &SidePane::updateSizeHint); connect(model, &QAbstractItemModel::modelReset, this, &SidePane::updateSizeHint); } QAbstractItemView::setModel(model); } bool SidePane::eventFilter(QObject *object, QEvent *event) { if (object == viewport()) { if (event->type() == QEvent::Resize) updateSizeHint(); else if (event->type() == QEvent::ScreenChangeInternal) m_background = QPixmap(); else if (event->type() == QEvent::Paint) { if (m_background.isNull()) m_background = UIResources::themedPixmap(QStringLiteral("kdab-gammaray-logo.png"), this); qreal dpr = 1.0; dpr = m_background.devicePixelRatio(); QPainter p(viewport()); p.drawPixmap(viewport()->width() - (m_background.width() / dpr), viewport()->height() - (m_background.height() / dpr), m_background); } } return QListView::eventFilter(object, event); } void SidePane::updateSizeHint() { setMinimumWidth(sizeHint().width()); } gammaray-2.11.3/ui/sidepane.h000066400000000000000000000031541413310443000157460ustar00rootroot00000000000000/* sidepane.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Kevin Funk Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_SIDEPANE_H #define GAMMARAY_SIDEPANE_H #include namespace GammaRay { class SidePane : public QListView { Q_OBJECT public: explicit SidePane(QWidget *parent = nullptr); ~SidePane() override; QSize sizeHint() const override; void setModel(QAbstractItemModel *model) override; protected: bool eventFilter(QObject *object, QEvent *event) override; private slots: void updateSizeHint(); private: QPixmap m_background; }; } #endif // GAMMARAY_SIDEPANE_H gammaray-2.11.3/ui/splashscreen.cpp000066400000000000000000000040301413310443000171750ustar00rootroot00000000000000/* attachdialog.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "splashscreen.h" #include "uiresources.h" #include #include #include #include QSplashScreen *splash = nullptr; namespace GammaRay { void showSplashScreen() { if (!splash) { splash = new QSplashScreen; QPixmap pixmap = UIResources::themedPixmap(QStringLiteral("splashscreen.png"), splash); splash->setPixmap(pixmap); } const QWidget *window = qApp->activeWindow(); if (window && window != splash) { splash->ensurePolished(); const QRect windowRect = qApp->desktop()->availableGeometry(window); QRect splashRect = QRect(QPoint(), splash->size()); splashRect.moveCenter(windowRect.center()); splash->move(splashRect.topLeft()); } splash->show(); } void hideSplashScreen() { if (splash) { splash->hide(); delete splash; splash = nullptr; } } } gammaray-2.11.3/ui/splashscreen.h000066400000000000000000000025331413310443000166500ustar00rootroot00000000000000/* attachdialog.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_SPLASHSCREEN_H #define GAMMARAY_SPLASHSCREEN_H #include "gammaray_ui_export.h" namespace GammaRay { GAMMARAY_UI_EXPORT void showSplashScreen(); GAMMARAY_UI_EXPORT void hideSplashScreen(); } #endif // SPLASHSCREEN_H gammaray-2.11.3/ui/themedimagelabel.cpp000066400000000000000000000036421413310443000177640ustar00rootroot00000000000000/* themedimagelabel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Filipe Azevedo Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "themedimagelabel.h" #include "uiresources.h" #include using namespace GammaRay; ThemedImageLabel::ThemedImageLabel(QWidget *parent, Qt::WindowFlags f) : QLabel(parent, f) { } QString ThemedImageLabel::themeFileName() const { return m_themeFileName; } void ThemedImageLabel::setThemeFileName(const QString &themeFileName) { if (m_themeFileName == themeFileName) return; m_themeFileName = themeFileName; updatePixmap(); } bool ThemedImageLabel::event(QEvent *e) { if (e->type() == QEvent::ScreenChangeInternal) updatePixmap(); return QLabel::event(e); } void ThemedImageLabel::updatePixmap() { if (m_themeFileName.isEmpty()) setPixmap(QPixmap()); else setPixmap(UIResources::themedPixmap(m_themeFileName, this)); } gammaray-2.11.3/ui/themedimagelabel.h000066400000000000000000000032751413310443000174330ustar00rootroot00000000000000/* themedimagelabel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Filipe Azevedo Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_THEMEDIMAGELABEL_H #define GAMMARAY_THEMEDIMAGELABEL_H #include "gammaray_ui_export.h" #include namespace GammaRay { class GAMMARAY_UI_EXPORT ThemedImageLabel : public QLabel { Q_OBJECT public: explicit ThemedImageLabel(QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); QString themeFileName() const; void setThemeFileName(const QString &themeFileName); protected: bool event(QEvent *e) override; virtual void updatePixmap(); private: QString m_themeFileName; }; } #endif // GAMMARAY_THEMEDIMAGELABEL_H gammaray-2.11.3/ui/tools/000077500000000000000000000000001413310443000151425ustar00rootroot00000000000000gammaray-2.11.3/ui/tools/messagehandler/000077500000000000000000000000001413310443000201245ustar00rootroot00000000000000gammaray-2.11.3/ui/tools/messagehandler/messagedisplaymodel.cpp000066400000000000000000000120631413310443000246650ustar00rootroot00000000000000/* messagedisplaymodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "messagedisplaymodel.h" #include #include #include using namespace GammaRay; QString typeToString(int type) { switch (type) { case QtDebugMsg: return MessageDisplayModel::tr("Debug"); case QtWarningMsg: return MessageDisplayModel::tr("Warning"); case QtCriticalMsg: return MessageDisplayModel::tr("Critical"); case QtFatalMsg: return MessageDisplayModel::tr("Fatal"); case QtInfoMsg: return MessageDisplayModel::tr("Info"); default: return MessageDisplayModel::tr("Unknown"); // never reached in theory } } MessageDisplayModel::MessageDisplayModel(QObject *parent) : QIdentityProxyModel(parent) { } MessageDisplayModel::~MessageDisplayModel() = default; QVariant MessageDisplayModel::data(const QModelIndex &proxyIndex, int role) const { if (!proxyIndex.isValid()) return QVariant(); switch (role) { case Qt::DisplayRole: if (proxyIndex.column() == MessageModelColumn::File) { const auto srcIdx = mapToSource(proxyIndex); Q_ASSERT(srcIdx.isValid()); const auto fileName = srcIdx.data().toString(); const auto line = srcIdx.data(MessageModelRole::Line).toInt(); if (line <= 0) return fileName; return static_cast(fileName + ':' + QString::number(line)); } break; case Qt::ToolTipRole: { const auto srcIdx = mapToSource(proxyIndex); Q_ASSERT(srcIdx.isValid()); const auto msgType = typeToString(srcIdx.sibling(srcIdx.row(), 0).data(MessageModelRole::Type).toInt()); const auto msgTime = srcIdx.sibling(srcIdx.row(), MessageModelColumn::Time).data().toString(); const auto msgText = srcIdx.sibling(srcIdx.row(), MessageModelColumn::Message).data().toString(); const auto backtrace = srcIdx.sibling(srcIdx.row(), 0).data(MessageModelRole::Backtrace).toStringList(); if (!backtrace.isEmpty()) { QString bt; int i = 0; for (const auto &frame : backtrace) { bt += QStringLiteral("#%1: %2\n").arg(i, 2).arg(frame.trimmed()); ++i; } return tr("

    " "
    Type:
    %1
    " "
    Time:
    %2
    " "
    Message:
    %3
    " "
    Backtrace:
    %4
    " "
    ").arg(msgType, msgTime, msgText, bt); } else { return tr("
    " "
    Type:
    %1
    " "
    Time:
    %2
    " "
    Message:
    %3
    " "
    ").arg(msgType, msgTime, msgText); } } case Qt::DecorationRole: if (proxyIndex.column() == 0) { const auto srcIdx = mapToSource(proxyIndex); Q_ASSERT(srcIdx.isValid()); const auto msgType = srcIdx.sibling(srcIdx.row(), 0).data(MessageModelRole::Type).toInt(); auto style = qApp->style(); switch (msgType) { case QtDebugMsg: return style->standardIcon(QStyle::SP_MessageBoxInformation); case QtWarningMsg: return style->standardIcon(QStyle::SP_MessageBoxWarning); case QtCriticalMsg: case QtFatalMsg: return style->standardIcon(QStyle::SP_MessageBoxCritical); } } break; case MessageModelRole::File: { const auto srcIdx = mapToSource(proxyIndex); Q_ASSERT(srcIdx.isValid()); return srcIdx.sibling(srcIdx.row(), MessageModelColumn::File).data(); } } return QAbstractProxyModel::data(proxyIndex, role); } gammaray-2.11.3/ui/tools/messagehandler/messagedisplaymodel.h000066400000000000000000000031551413310443000243340ustar00rootroot00000000000000/* messagedisplaymodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2015-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_MESSAGEDISPLAYMODEL_H #define GAMMARAY_MESSAGEDISPLAYMODEL_H #include namespace GammaRay { /** Client side additions to the message model. */ class MessageDisplayModel : public QIdentityProxyModel { Q_OBJECT public: explicit MessageDisplayModel(QObject *parent = nullptr); ~MessageDisplayModel() override; QVariant data(const QModelIndex &proxyIndex, int role = Qt::DisplayRole) const override; }; } #endif // GAMMARAY_MESSAGEDISPLAYMODEL_H gammaray-2.11.3/ui/tools/messagehandler/messagehandlerclient.cpp000066400000000000000000000024361413310443000250160ustar00rootroot00000000000000/* messagehandlerclient.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "messagehandlerclient.h" using namespace GammaRay; MessageHandlerClient::MessageHandlerClient(QObject *parent) : MessageHandlerInterface(parent) { } gammaray-2.11.3/ui/tools/messagehandler/messagehandlerclient.h000066400000000000000000000030251413310443000244560ustar00rootroot00000000000000/* messagehandlerclient.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_MESSAGEHANDLERCLIENT_H #define GAMMARAY_MESSAGEHANDLERCLIENT_H #include namespace GammaRay { class MessageHandlerClient : public MessageHandlerInterface { Q_OBJECT Q_INTERFACES(GammaRay::MessageHandlerInterface) public: explicit MessageHandlerClient(QObject *parent = nullptr); }; } #endif // GAMMARAY_MESSAGEHANDLERCLIENT_H gammaray-2.11.3/ui/tools/messagehandler/messagehandlerwidget.cpp000066400000000000000000000200471413310443000250210ustar00rootroot00000000000000/* messagehandlerwidget.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "messagehandlerwidget.h" #include "ui_messagehandlerwidget.h" #include "messagehandlerclient.h" #include "messagedisplaymodel.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace GammaRay; static QObject *createClientMessageHandler(const QString & /*name*/, QObject *parent) { return new MessageHandlerClient(parent); } MessageHandlerWidget::MessageHandlerWidget(QWidget *parent) : QWidget(parent) , ui(new Ui::MessageHandlerWidget) , m_stateManager(this) { ObjectBroker::registerClientObjectFactoryCallback( createClientMessageHandler); MessageHandlerInterface *handler = ObjectBroker::object(); connect(handler, &MessageHandlerInterface::fatalMessageReceived, this, &MessageHandlerWidget::fatalMessageReceived); ui->setupUi(this); ui->messageView->header()->setObjectName("messageViewHeader"); ui->messageView->setDeferredResizeMode(0, QHeaderView::ResizeToContents); ui->messageView->setDeferredResizeMode(2, QHeaderView::ResizeToContents); ui->backtraceView->header()->setObjectName("backtraceViewHeader"); ui->categoriesView->header()->setObjectName("categoriesViewHeader"); ui->categoriesView->setStretchLastSection(false); ui->categoriesView->setDeferredResizeMode(0, QHeaderView::Stretch); ui->categoriesView->setDeferredResizeMode(1, QHeaderView::ResizeToContents); ui->categoriesView->setDeferredResizeMode(2, QHeaderView::ResizeToContents); ui->categoriesView->setDeferredResizeMode(3, QHeaderView::ResizeToContents); ui->categoriesView->setDeferredResizeMode(4, QHeaderView::ResizeToContents); auto messageModel = ObjectBroker::model(QStringLiteral("com.kdab.GammaRay.MessageModel")); auto displayModel = new MessageDisplayModel(this); displayModel->setSourceModel(messageModel); new SearchLineController(ui->messageSearchLine, displayModel); ui->messageView->setModel(displayModel); ui->messageView->setSelectionModel(ObjectBroker::selectionModel(displayModel)); connect(ui->messageView, &QWidget::customContextMenuRequested, this, &MessageHandlerWidget::messageContextMenu); ui->backtraceView->setModel(ObjectBroker::model(QStringLiteral("com.kdab.GammaRay.MessageStackTraceModel"))); ui->backtraceView->setVisible(handler->stackTraceAvailable()); ui->backtraceView->setItemDelegate(new PropertyEditorDelegate(ui->backtraceView)); connect(handler, &MessageHandlerInterface::stackTraceAvailableChanged, ui->backtraceView, &QWidget::setVisible); connect(ui->backtraceView, &QWidget::customContextMenuRequested, this, &MessageHandlerWidget::stackTraceContextMenu); ui->categoriesView->setModel(ObjectBroker::model(QStringLiteral("com.kdab.GammaRay.LoggingCategoryModel"))); m_stateManager.setDefaultSizes(ui->mainSplitter, UISizeVector() << "50%" << "50%"); m_stateManager.setDefaultSizes(ui->messageView->header(), UISizeVector() << -1 << 300 << -1 << -1 << -1); } MessageHandlerWidget::~MessageHandlerWidget() = default; void MessageHandlerWidget::fatalMessageReceived(const QString &app, const QString &message, const QTime &time, const QStringList &backtrace) { if (Endpoint::isConnected() && !qobject_cast(ObjectBroker::object())) { // only show on remote side return; } QDialog dlg; dlg.setWindowTitle(tr("QFatal in %1 at %2").arg(app, time.toString())); auto *layout = new QGridLayout; QLabel *iconLabel = new QLabel; QIcon icon = dlg.style()->standardIcon(QStyle::SP_MessageBoxCritical, nullptr, &dlg); int iconSize = dlg.style()->pixelMetric(QStyle::PM_MessageBoxIconSize, nullptr, &dlg); iconLabel->setPixmap(icon.pixmap(iconSize, iconSize)); iconLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); layout->addWidget(iconLabel, 0, 0); QLabel *errorLabel = new QLabel; errorLabel->setTextFormat(Qt::PlainText); errorLabel->setWordWrap(true); errorLabel->setText(message); layout->addWidget(errorLabel, 0, 1); auto *buttons = new QDialogButtonBox; if (!backtrace.isEmpty()) { auto *backtraceWidget = new QListWidget; for (const QString &frame : backtrace) backtraceWidget->addItem(frame); layout->addWidget(backtraceWidget, 1, 0, 1, 2); QPushButton *copyBacktraceButton = new QPushButton(tr("Copy Backtrace")); buttons->addButton(copyBacktraceButton, QDialogButtonBox::ActionRole); auto joinedBacktrace = backtrace.join(QStringLiteral("\n")); connect(copyBacktraceButton, &QPushButton::clicked, this, [this, joinedBacktrace] { copyToClipboard(joinedBacktrace); }); } buttons->addButton(QDialogButtonBox::Close); QObject::connect(buttons, &QDialogButtonBox::accepted, &dlg, &QDialog::accept); QObject::connect(buttons, &QDialogButtonBox::rejected, &dlg, &QDialog::reject); layout->addWidget(buttons, 2, 0, 1, 2); dlg.setLayout(layout); dlg.adjustSize(); dlg.exec(); } void MessageHandlerWidget::copyToClipboard(const QString &message) { #ifndef QT_NO_CLIPBOARD QApplication::clipboard()->setText(message); #endif } void MessageHandlerWidget::messageContextMenu(const QPoint &pos) { auto index = ui->messageView->indexAt(pos); if (!index.isValid()) return; index = index.sibling(index.row(), MessageModelColumn::File); if (!index.isValid()) return; const auto fileName = index.data(MessageModelRole::File).toString(); if (fileName.isEmpty()) return; const auto line = index.data(MessageModelRole::Line).toInt(); QMenu contextMenu; ContextMenuExtension cme; cme.setLocation(ContextMenuExtension::ShowSource, SourceLocation::fromOneBased(QUrl(fileName), line)); cme.populateMenu(&contextMenu); contextMenu.exec(ui->messageView->viewport()->mapToGlobal(pos)); } void MessageHandlerWidget::stackTraceContextMenu(QPoint pos) { const auto idx = ui->backtraceView->indexAt(pos); if (!idx.isValid()) return; const auto loc = idx.sibling(idx.row(), 1).data().value(); if (!loc.isValid()) return; QMenu contextMenu; ContextMenuExtension cme; cme.setLocation(ContextMenuExtension::ShowSource, loc); cme.populateMenu(&contextMenu); contextMenu.exec(ui->backtraceView->viewport()->mapToGlobal(pos)); } gammaray-2.11.3/ui/tools/messagehandler/messagehandlerwidget.h000066400000000000000000000037551413310443000244750ustar00rootroot00000000000000/* messagehandlerwidget.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_MESSAGEHANDLERWIDGET_H #define GAMMARAY_MESSAGEHANDLERWIDGET_H #include #include QT_BEGIN_NAMESPACE class QItemSelection; class QTime; QT_END_NAMESPACE namespace GammaRay { namespace Ui { class MessageHandlerWidget; } class MessageHandlerWidget : public QWidget { Q_OBJECT public: explicit MessageHandlerWidget(QWidget *parent = nullptr); ~MessageHandlerWidget() override; private slots: void fatalMessageReceived(const QString &app, const QString &message, const QTime &time, const QStringList &backtrace); void copyToClipboard(const QString &message); void messageContextMenu(const QPoint &pos); void stackTraceContextMenu(QPoint pos); private: QScopedPointer ui; UIStateManager m_stateManager; }; } #endif // MESSAGEHANDLERWIDGET_H gammaray-2.11.3/ui/tools/messagehandler/messagehandlerwidget.ui000066400000000000000000000053141413310443000246540ustar00rootroot00000000000000 GammaRay::MessageHandlerWidget 0 0 772 426 0 Messages Qt::Horizontal Qt::CustomContextMenu false Qt::CustomContextMenu false true Categories false true GammaRay::DeferredTreeView QTreeView
    ui/deferredtreeview.h
    gammaray-2.11.3/ui/tools/metaobjectbrowser/000077500000000000000000000000001413310443000206635ustar00rootroot00000000000000gammaray-2.11.3/ui/tools/metaobjectbrowser/metaobjectbrowserwidget.cpp000066400000000000000000000074161413310443000263240ustar00rootroot00000000000000/* metaobjectbrowserwidget.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Kevin Funk Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "metaobjectbrowserwidget.h" #include "metaobjecttreeclientproxymodel.h" #include #include #include #include #include #include #include #include #include using namespace GammaRay; MetaObjectBrowserWidget::MetaObjectBrowserWidget(QWidget *parent) : QWidget(parent) , m_stateManager(this) { setObjectName("MetaObjectBrowserWidget"); auto model = ObjectBroker::model(QStringLiteral("com.kdab.GammaRay.MetaObjectBrowserTreeModel")); auto proxy = new MetaObjectTreeClientProxyModel(this); proxy->setSourceModel(model); m_treeView = new DeferredTreeView(this); m_treeView->header()->setObjectName("metaObjectViewHeader"); m_treeView->setStretchLastSection(false); m_treeView->setExpandNewContent(true); m_treeView->setDeferredResizeMode(0, QHeaderView::Stretch); m_treeView->setDeferredResizeMode(1, QHeaderView::ResizeToContents); m_treeView->setDeferredResizeMode(2, QHeaderView::ResizeToContents); m_treeView->setDeferredResizeMode(3, QHeaderView::ResizeToContents); m_treeView->setDeferredResizeMode(4, QHeaderView::ResizeToContents); m_treeView->setUniformRowHeights(true); m_treeView->setModel(proxy); m_treeView->setSelectionModel(ObjectBroker::selectionModel(proxy)); m_treeView->sortByColumn(0, Qt::AscendingOrder); connect(m_treeView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &MetaObjectBrowserWidget::selectionChanged); auto objectSearchLine = new QLineEdit(this); new SearchLineController(objectSearchLine, proxy); auto *propertyWidget = new PropertyWidget(this); m_propertyWidget = propertyWidget; m_propertyWidget->setObjectBaseName(QStringLiteral("com.kdab.GammaRay.MetaObjectBrowser")); auto *vbox = new QVBoxLayout; vbox->addWidget(objectSearchLine); vbox->addWidget(m_treeView); auto *hbox = new QHBoxLayout(this); hbox->addLayout(vbox); hbox->addWidget(propertyWidget); connect(m_propertyWidget, &PropertyWidget::tabsUpdated, this, &MetaObjectBrowserWidget::propertyWidgetTabsChanged); Endpoint::instance()->invokeObject(QStringLiteral("com.kdab.GammaRay.MetaObjectBrowser"), "rescanMetaTypes"); } void MetaObjectBrowserWidget::selectionChanged(const QItemSelection &selection) { if (selection.isEmpty()) return; m_treeView->scrollTo(selection.first().topLeft()); // in case of remote changes } void MetaObjectBrowserWidget::propertyWidgetTabsChanged() { m_stateManager.saveState(); m_stateManager.reset(); } gammaray-2.11.3/ui/tools/metaobjectbrowser/metaobjectbrowserwidget.h000066400000000000000000000034431413310443000257650ustar00rootroot00000000000000/* metaobjectbrowserwidget.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Kevin Funk Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_METAOBJECTBROWSERWIDGET_H #define GAMMARAY_METAOBJECTBROWSERWIDGET_H #include #include QT_BEGIN_NAMESPACE class QItemSelection; QT_END_NAMESPACE namespace GammaRay { class PropertyWidget; class DeferredTreeView; class MetaObjectBrowserWidget : public QWidget { Q_OBJECT public: explicit MetaObjectBrowserWidget(QWidget *parent = nullptr); private slots: void selectionChanged(const QItemSelection &selection); void propertyWidgetTabsChanged(); private: UIStateManager m_stateManager; PropertyWidget *m_propertyWidget; DeferredTreeView *m_treeView; }; } #endif // GAMMARAY_METAOBJECTBROWSERWIDGET_H gammaray-2.11.3/ui/tools/metaobjectbrowser/metaobjecttreeclientproxymodel.cpp000066400000000000000000000212511413310443000277070ustar00rootroot00000000000000/* metaobjecttreeclientproxymodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "metaobjecttreeclientproxymodel.h" #include #include #include #include #include #include #include using namespace GammaRay; MetaObjectTreeClientProxyModel::MetaObjectTreeClientProxyModel(QObject *parent) : QIdentityProxyModel(parent) { } MetaObjectTreeClientProxyModel::~MetaObjectTreeClientProxyModel() = default; void MetaObjectTreeClientProxyModel::setSourceModel(QAbstractItemModel *source) { QIdentityProxyModel::setSourceModel(source); connect(source, &QAbstractItemModel::rowsInserted, this, &MetaObjectTreeClientProxyModel::findQObjectIndex); connect(source, &QAbstractItemModel::dataChanged, this, &MetaObjectTreeClientProxyModel::findQObjectIndex); findQObjectIndex(); } // 1 / GRADIENT_SCALE_FACTOR is yellow, 2 / GRADIENT_SCALE_FACTOR and beyond is red static const int GRADIENT_SCALE_FACTOR = 4; static QColor colorForRatio(double ratio) { const auto red = qBound(0.0, ratio * GRADIENT_SCALE_FACTOR, 0.5); const auto green = qBound(0.0, 1 - ratio * GRADIENT_SCALE_FACTOR, 0.5); auto color = QColor(255 * red, 255 * green, 0); if (!UiIntegration::hasDarkUI()) return color.lighter(300); return color; } static QString issuesToString(QMetaObjectValidatorResult::Results r) { QStringList l; if (r & QMetaObjectValidatorResult::SignalOverride) l.push_back(MetaObjectTreeClientProxyModel::tr("overrides base class signal")); if (r & QMetaObjectValidatorResult::UnknownMethodParameterType) l.push_back(MetaObjectTreeClientProxyModel::tr("method uses parameter type not registered with the meta type system")); if (r & QMetaObjectValidatorResult::PropertyOverride) l.push_back(MetaObjectTreeClientProxyModel::tr("overrides base class property")); if (r & QMetaObjectValidatorResult::UnknownPropertyType) l.push_back(MetaObjectTreeClientProxyModel::tr("property with type not registered with the meta type system")); return MetaObjectTreeClientProxyModel::tr("Issues:
    • %1
    ").arg(l.join("
  • ")); } QVariant MetaObjectTreeClientProxyModel::data(const QModelIndex &index, int role) const { if (!sourceModel() || !index.isValid()) return QVariant(); if (index.column() == QMetaObjectModel::ObjectColumn) { const auto issues = QIdentityProxyModel::data(index, QMetaObjectModel::MetaObjectIssues).value(); switch (role) { case Qt::DecorationRole: if (issues != QMetaObjectValidatorResult::NoIssue) return qApp->style()->standardIcon(QStyle::SP_MessageBoxWarning); break; case Qt::ToolTipRole: { if (issues != QMetaObjectValidatorResult::NoIssue) return issuesToString(issues); const auto invalid = index.sibling(index.row(), QMetaObjectModel::ObjectInclusiveAliveCountColumn).data(QMetaObjectModel::MetaObjectInvalid).toBool(); if (invalid) return tr("This meta object might have been deleted."); break; } } return QIdentityProxyModel::data(index, role); } if ((role != Qt::BackgroundRole && role != Qt::ToolTipRole) || !m_qobjIndex.isValid()) return QIdentityProxyModel::data(index, role); if (!needsBackground(index)) return QIdentityProxyModel::data(index, role); // top-level but not QObject, or QObject incl count const auto count = index.data(Qt::DisplayRole).toInt(); if (count <= 0) return QIdentityProxyModel::data(index, role); const auto totalColumn = (index.column() == QMetaObjectModel::ObjectSelfCountColumn || index.column() == QMetaObjectModel::ObjectInclusiveCountColumn)? QMetaObjectModel::ObjectInclusiveCountColumn : QMetaObjectModel::ObjectInclusiveAliveCountColumn; const auto totalCount = m_qobjIndex.sibling(m_qobjIndex.row(), totalColumn).data().toInt(); const auto ratio = (double)count / (double)totalCount; // at this point, role can only be background or tooltip if (role == Qt::BackgroundRole) return colorForRatio(ratio); Q_ASSERT(role == Qt::ToolTipRole); return tr("%1%").arg(ratio * 100.0, 0, 'f', 2); } QVariant MetaObjectTreeClientProxyModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role == Qt::DisplayRole && orientation == Qt::Horizontal) { switch (section) { case QMetaObjectModel::ObjectColumn: return tr("Meta Object Class"); case QMetaObjectModel::ObjectSelfCountColumn: return tr("Self Total"); case QMetaObjectModel::ObjectInclusiveCountColumn: return tr("Incl. Total"); case QMetaObjectModel::ObjectSelfAliveCountColumn: return tr("Self Alive"); case QMetaObjectModel::ObjectInclusiveAliveCountColumn: return tr("Incl. Alive"); default: return QVariant(); } } else if (role == Qt::ToolTipRole) { switch (section) { case QMetaObjectModel::ObjectColumn: return tr("This column shows the QMetaObject class hierarchy."); case QMetaObjectModel::ObjectSelfCountColumn: return tr("This column shows the number of objects created of a particular type."); case QMetaObjectModel::ObjectInclusiveCountColumn: return tr("This column shows the number of objects created that inherit from a particular type."); case QMetaObjectModel::ObjectSelfAliveCountColumn: return tr("This column shows the number of objects created and not yet destroyed of a particular type."); case QMetaObjectModel::ObjectInclusiveAliveCountColumn: return tr("This column shows the number of objects created and not yet destroyed that inherit from a particular type."); default: return QVariant(); } } return QIdentityProxyModel::headerData(section, orientation, role); } Qt::ItemFlags MetaObjectTreeClientProxyModel::flags(const QModelIndex& index) const { auto f = QIdentityProxyModel::flags(index); if (index.isValid()) { const auto idx = index.sibling(index.row(), QMetaObjectModel::ObjectInclusiveAliveCountColumn); const auto invalid = idx.data(QMetaObjectModel::MetaObjectInvalid).toBool(); if (invalid) f &= ~Qt::ItemIsEnabled; } return f; } void MetaObjectTreeClientProxyModel::findQObjectIndex() { auto idxList = match(index(0, 0), Qt::DisplayRole, QStringLiteral("QObject"), 1, Qt::MatchFixedString | Qt::MatchCaseSensitive); if (idxList.isEmpty()) return; m_qobjIndex = idxList.first(); disconnect(sourceModel(), &QAbstractItemModel::rowsInserted, this, &MetaObjectTreeClientProxyModel::findQObjectIndex); disconnect(sourceModel(), &QAbstractItemModel::dataChanged, this, &MetaObjectTreeClientProxyModel::findQObjectIndex); } bool MetaObjectTreeClientProxyModel::needsBackground(const QModelIndex &index) const { if (index.parent().isValid()) return true; if (index.row() != m_qobjIndex.row()) return true; if (index.column() == QMetaObjectModel::ObjectInclusiveCountColumn || index.column() == QMetaObjectModel::ObjectInclusiveAliveCountColumn) return false; return true; } gammaray-2.11.3/ui/tools/metaobjectbrowser/metaobjecttreeclientproxymodel.h000066400000000000000000000041231413310443000273530ustar00rootroot00000000000000/* metaobjecttreeclientproxymodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_METAOBJECTTREECLIENTPROXYMODEL_H #define GAMMARAY_METAOBJECTTREECLIENTPROXYMODEL_H #include #include namespace GammaRay { /** Colors the usage counts based on the global ratio. */ class MetaObjectTreeClientProxyModel : public QIdentityProxyModel { Q_OBJECT public: explicit MetaObjectTreeClientProxyModel(QObject *parent = nullptr); ~MetaObjectTreeClientProxyModel() override; void setSourceModel(QAbstractItemModel *source) override; QVariant data(const QModelIndex &index, int role) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; Qt::ItemFlags flags(const QModelIndex & index) const override; private slots: void findQObjectIndex(); private: bool needsBackground(const QModelIndex &index) const; QPersistentModelIndex m_qobjIndex; }; } #endif // GAMMARAY_METAOBJECTTREECLIENTPROXYMODEL_H gammaray-2.11.3/ui/tools/metatypebrowser/000077500000000000000000000000001413310443000203765ustar00rootroot00000000000000gammaray-2.11.3/ui/tools/metatypebrowser/metatypebrowserclient.cpp000066400000000000000000000027631413310443000255450ustar00rootroot00000000000000/* metatypebrowserclient.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "metatypebrowserclient.h" #include using namespace GammaRay; MetaTypeBrowserClient::MetaTypeBrowserClient(QObject* parent) : MetaTypeBrowserInterface(parent) { } MetaTypeBrowserClient::~MetaTypeBrowserClient() = default; void MetaTypeBrowserClient::rescanTypes() { Endpoint::instance()->invokeObject(objectName(), "rescanTypes"); } gammaray-2.11.3/ui/tools/metatypebrowser/metatypebrowserclient.h000066400000000000000000000031411413310443000252010ustar00rootroot00000000000000/* metatypebrowserclient.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_METATYPEBROWSERCLIENT_H #define GAMMARAY_METATYPEBROWSERCLIENT_H #include namespace GammaRay { class MetaTypeBrowserClient : public MetaTypeBrowserInterface { Q_OBJECT Q_INTERFACES(GammaRay::MetaTypeBrowserInterface) public: explicit MetaTypeBrowserClient(QObject *parent); ~MetaTypeBrowserClient() override; void rescanTypes() override; }; } #endif // GAMMARAY_METATYPEBROWSERCLIENT_H gammaray-2.11.3/ui/tools/metatypebrowser/metatypebrowserwidget.cpp000066400000000000000000000070721413310443000255500ustar00rootroot00000000000000/* metatypebrowserwidget.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "metatypebrowserwidget.h" #include "ui_metatypebrowserwidget.h" #include "metatypesclientmodel.h" #include "metatypebrowserclient.h" #include #include #include #include #include #include using namespace GammaRay; static QObject *createMetaTypeBrowserClient(const QString & /*name*/, QObject *parent) { return new MetaTypeBrowserClient(parent); } MetaTypeBrowserWidget::MetaTypeBrowserWidget(QWidget *parent) : QWidget(parent) , ui(new Ui::MetaTypeBrowserWidget) , m_stateManager(this) { ObjectBroker::registerClientObjectFactoryCallback(createMetaTypeBrowserClient); ui->setupUi(this); auto mtm = new MetaTypesClientModel(this); mtm->setSourceModel(ObjectBroker::model(QStringLiteral("com.kdab.GammaRay.MetaTypeModel"))); ui->metaTypeView->header()->setObjectName("metaTypeViewHeader"); ui->metaTypeView->setDeferredResizeMode(0, QHeaderView::ResizeToContents); ui->metaTypeView->setDeferredResizeMode(1, QHeaderView::ResizeToContents); ui->metaTypeView->setDeferredResizeMode(2, QHeaderView::ResizeToContents); ui->metaTypeView->setDeferredResizeMode(3, QHeaderView::ResizeToContents); ui->metaTypeView->setDeferredResizeMode(4, QHeaderView::ResizeToContents); ui->metaTypeView->setModel(mtm); ui->metaTypeView->sortByColumn(1, Qt::AscendingOrder); // sort by type id connect(ui->metaTypeView, &QWidget::customContextMenuRequested, this, &MetaTypeBrowserWidget::contextMenu); new SearchLineController(ui->metaTypeSearchLine, mtm->sourceModel()); auto iface = ObjectBroker::object(); connect(ui->actionRescanTypes, &QAction::triggered, iface, &MetaTypeBrowserInterface::rescanTypes); iface->rescanTypes(); addAction(ui->actionRescanTypes); } MetaTypeBrowserWidget::~MetaTypeBrowserWidget() = default; void MetaTypeBrowserWidget::contextMenu(QPoint pos) { auto index = ui->metaTypeView->indexAt(pos); if (!index.isValid()) return; index = index.sibling(index.row(), 0); const auto objectId = index.data(MetaTypeRoles::MetaObjectIdRole).value(); if (objectId.isNull()) return; QMenu menu; ContextMenuExtension ext(objectId); ext.populateMenu(&menu); menu.exec(ui->metaTypeView->viewport()->mapToGlobal(pos)); } gammaray-2.11.3/ui/tools/metatypebrowser/metatypebrowserwidget.h000066400000000000000000000032621413310443000252120ustar00rootroot00000000000000/* metatypebrowserwidget.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_METATYPEBROWSERWIDGET_H #define GAMMARAY_METATYPEBROWSERWIDGET_H #include #include namespace GammaRay { namespace Ui { class MetaTypeBrowserWidget; } class MetaTypeBrowserWidget : public QWidget { Q_OBJECT public: explicit MetaTypeBrowserWidget(QWidget *parent = nullptr); ~MetaTypeBrowserWidget() override; private slots: void contextMenu(QPoint pos); private: QScopedPointer ui; UIStateManager m_stateManager; }; } #endif // GAMMARAY_METATYPEBROWSERWIDGET_H gammaray-2.11.3/ui/tools/metatypebrowser/metatypebrowserwidget.ui000066400000000000000000000026621413310443000254030ustar00rootroot00000000000000 GammaRay::MetaTypeBrowserWidget 0 0 400 300 0 Qt::CustomContextMenu false true &Rescan Meta Types Check for changes to the meta type database. GammaRay::DeferredTreeView QTreeView
    ui/deferredtreeview.h
    gammaray-2.11.3/ui/tools/metatypebrowser/metatypesclientmodel.cpp000066400000000000000000000064141413310443000253420ustar00rootroot00000000000000/* metatypesclientmodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "metatypesclientmodel.h" #include #include using namespace GammaRay; MetaTypesClientModel::MetaTypesClientModel(QObject* parent) : QIdentityProxyModel(parent) { } MetaTypesClientModel::~MetaTypesClientModel() = default; QVariant MetaTypesClientModel::data(const QModelIndex& index, int role) const { if (role == Qt::DisplayRole && (index.column() == 5 || index.column() == 6)) { const auto v = QIdentityProxyModel::data(index, Qt::DisplayRole); const auto b = v.type() == QVariant::Bool && v.toBool(); if (b && qApp->style()->standardIcon(QStyle::SP_DialogYesButton).isNull()) return tr("yes"); return QVariant(); } else if (role == Qt::DecorationRole && (index.column() == 5 || index.column() == 6)) { const auto v = QIdentityProxyModel::data(index, Qt::DisplayRole); const auto b = v.type() == QVariant::Bool && v.toBool(); return b ? qApp->style()->standardIcon(QStyle::SP_DialogYesButton) : QVariant(); } return QIdentityProxyModel::data(index, role); } QVariant MetaTypesClientModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal) { if (role == Qt::DisplayRole) { switch (section) { case 0: return tr("Type Name"); case 1: return tr("Meta Type Id"); case 2: return tr("Size"); case 3: return tr("Meta Object"); case 4: return tr("Type Flags"); case 5: return tr("Compare"); case 6: return tr("Debug"); } } else if (role == Qt::ToolTipRole) { switch (section) { case 5: return tr("Has equality comparison operators registered."); case 6: return tr("Has debug stream operators registered."); } } return QVariant(); } return QIdentityProxyModel::headerData(section, orientation, role); } gammaray-2.11.3/ui/tools/metatypebrowser/metatypesclientmodel.h000066400000000000000000000033351413310443000250060ustar00rootroot00000000000000/* metatypesclientmodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_METATYPESCLIENTMODEL_H #define GAMMARAY_METATYPESCLIENTMODEL_H #include namespace GammaRay { /*! Client-side part of the meta types model. */ class MetaTypesClientModel : public QIdentityProxyModel { Q_OBJECT public: explicit MetaTypesClientModel(QObject *parent = nullptr); ~MetaTypesClientModel() override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; }; } #endif // GAMMARAY_METATYPESCLIENTMODEL_H gammaray-2.11.3/ui/tools/objectinspector/000077500000000000000000000000001413310443000203375ustar00rootroot00000000000000gammaray-2.11.3/ui/tools/objectinspector/applicationattributetab.cpp000066400000000000000000000034111413310443000257600ustar00rootroot00000000000000/* applicationattributetab.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "applicationattributetab.h" #include "ui_applicationattributetab.h" #include #include using namespace GammaRay; ApplicationAttributeTab::ApplicationAttributeTab(PropertyWidget *parent) : QWidget(parent) , ui(new Ui::ApplicationAttributeTab) { ui->setupUi(this); ui->attributeView->header()->setObjectName("attributeViewHeader"); ui->attributeView->setModel(ObjectBroker::model(parent->objectBaseName() + QStringLiteral(".applicationAttributeModel"))); } ApplicationAttributeTab::~ApplicationAttributeTab() = default; gammaray-2.11.3/ui/tools/objectinspector/applicationattributetab.h000066400000000000000000000032071413310443000254300ustar00rootroot00000000000000/* applicationattributetab.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_APPLICATIONATTRIBUTETAB_H #define GAMMARAY_APPLICATIONATTRIBUTETAB_H #include #include namespace GammaRay { class PropertyWidget; namespace Ui { class ApplicationAttributeTab; } class ApplicationAttributeTab : public QWidget { Q_OBJECT public: explicit ApplicationAttributeTab(PropertyWidget *parent = nullptr); ~ApplicationAttributeTab() override; private: QScopedPointer ui; }; } #endif // GAMMARAY_APPLICATIONATTRIBUTETAB_H gammaray-2.11.3/ui/tools/objectinspector/applicationattributetab.ui000066400000000000000000000016031413310443000256140ustar00rootroot00000000000000 GammaRay::ApplicationAttributeTab 0 0 399 300 false true GammaRay::DeferredTreeView QTreeView
    ui/deferredtreeview.h
    gammaray-2.11.3/ui/tools/objectinspector/bindingtab.cpp000066400000000000000000000047251413310443000231540ustar00rootroot00000000000000/* bindingtab.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Authors: Volker Krause Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ // Own #include "bindingtab.h" #include "ui_bindingtab.h" #include #include #include #include #include // Qt #include using namespace GammaRay; BindingTab::BindingTab(PropertyWidget* parent) : QWidget(parent) , ui(new Ui::BindingTab) { ui->setupUi(this); ui->bindingView->setObjectName("bindingView"); ui->bindingView->setDeferredResizeMode(0, QHeaderView::ResizeToContents); ui->bindingView->header()->setObjectName("bindingViewHeader"); ui->bindingView->setModel(ObjectBroker::model(parent->objectBaseName() + QStringLiteral(".bindingModel"))); connect(ui->bindingView, &QWidget::customContextMenuRequested, this, &BindingTab::bindingContextMenu); } BindingTab::~BindingTab() = default; void GammaRay::BindingTab::bindingContextMenu(const QPoint &pos) { const auto index = ui->bindingView->indexAt(pos); if (!index.isValid()) return; QMenu menu; ContextMenuExtension ext; ext.setLocation(ContextMenuExtension::ShowSource, index.data(ObjectModel::DeclarationLocationRole).value()); ext.populateMenu(&menu); menu.exec(ui->bindingView->viewport()->mapToGlobal(pos)); } gammaray-2.11.3/ui/tools/objectinspector/bindingtab.h000066400000000000000000000032171413310443000226140ustar00rootroot00000000000000/* bindingtab.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Authors: Volker Krause Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_BINDINGTAB_H #define GAMMARAY_BINDINGTAB_H // Qt #include #include namespace GammaRay { class PropertyWidget; namespace Ui { class BindingTab; } class BindingTab : public QWidget { Q_OBJECT public: explicit BindingTab(PropertyWidget *parent = nullptr); ~BindingTab() override; private slots: void bindingContextMenu(const QPoint &pos); private: QScopedPointer ui; }; } #endif // GAMMARAY_BINDINGTAB_H gammaray-2.11.3/ui/tools/objectinspector/bindingtab.ui000066400000000000000000000017201413310443000227770ustar00rootroot00000000000000 GammaRay::BindingTab 0 0 400 300 Qt::CustomContextMenu Qt::ElideLeft true GammaRay::DeferredTreeView QTreeView
    ui/deferredtreeview.h
    gammaray-2.11.3/ui/tools/objectinspector/classinfotab.cpp000066400000000000000000000041451413310443000235170ustar00rootroot00000000000000/* classinfotab.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "classinfotab.h" #include "ui_classinfotab.h" #include "propertywidget.h" #include #include #include using namespace GammaRay; ClassInfoTab::ClassInfoTab(PropertyWidget *parent) : QWidget(parent) , m_ui(new Ui_ClassInfoTab) { m_ui->setupUi(this); m_ui->classInfoView->header()->setObjectName("classInfoViewHeader"); setObjectBaseName(parent->objectBaseName()); } ClassInfoTab::~ClassInfoTab() = default; void ClassInfoTab::setObjectBaseName(const QString &baseName) { auto *proxy = new QSortFilterProxyModel(this); proxy->setDynamicSortFilter(true); proxy->setSourceModel(ObjectBroker::model(baseName + '.' + "classInfo")); m_ui->classInfoView->setModel(proxy); m_ui->classInfoView->sortByColumn(0, Qt::AscendingOrder); m_ui->classInfoView->header()->setSectionResizeMode(QHeaderView::ResizeToContents); new SearchLineController(m_ui->classInfoSearchLine, proxy); } gammaray-2.11.3/ui/tools/objectinspector/classinfotab.h000066400000000000000000000030551413310443000231630ustar00rootroot00000000000000/* classinfotab.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef CLASSINFOTAB_H #define CLASSINFOTAB_H #include #include namespace GammaRay { class Ui_ClassInfoTab; class PropertyWidget; class ClassInfoTab : public QWidget { Q_OBJECT public: explicit ClassInfoTab(PropertyWidget *parent); ~ClassInfoTab() override; private: void setObjectBaseName(const QString &baseName); private: std::unique_ptr m_ui; }; } #endif // CLASSINFOTAB_H gammaray-2.11.3/ui/tools/objectinspector/classinfotab.ui000066400000000000000000000015551413310443000233540ustar00rootroot00000000000000 GammaRay::ClassInfoTab 0 0 400 300 false GammaRay::DeferredTreeView QTreeView
    ui/deferredtreeview.h
    gammaray-2.11.3/ui/tools/objectinspector/clientmethodmodel.cpp000066400000000000000000000120171413310443000245440ustar00rootroot00000000000000/* clientmethodmodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "clientmethodmodel.h" #include #include #include #include #include #include using namespace GammaRay; ClientMethodModel::ClientMethodModel(QObject *parent) : QIdentityProxyModel(parent) { } ClientMethodModel::~ClientMethodModel() = default; QVariant ClientMethodModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); if (index.column() == 1 && role == Qt::DisplayRole) { const auto methodType = index.data(ObjectMethodModelRole::MetaMethodType).value(); switch (methodType) { case QMetaMethod::Method: return tr("Method"); case QMetaMethod::Constructor: return tr("Constructor"); case QMetaMethod::Slot: return tr("Slot"); case QMetaMethod::Signal: return tr("Signal"); default: return tr("Unknown"); } } if (index.column() == 2 && role == Qt::DisplayRole) { const auto methodAccess = index.data(ObjectMethodModelRole::MethodAccess).value(); switch (methodAccess) { case QMetaMethod::Public: return tr("Public"); case QMetaMethod::Protected: return tr("Protected"); case QMetaMethod::Private: return tr("Private"); default: return tr("Unknown"); } } if (index.column() != 1 && role == ObjectMethodModelRole::MetaMethodType) return index.sibling(index.row(), 1).data(ObjectMethodModelRole::MetaMethodType); if (role == Qt::ToolTipRole) { const auto idx = index.sibling(index.row(), 0); auto tt = idx.data(Qt::DisplayRole).toString(); const auto tag = idx.data(ObjectMethodModelRole::MethodTag).toString(); tt += tr("\nTag: %1").arg(tag.isEmpty() ?tr("") : tag); const auto rev = idx.data(ObjectMethodModelRole::MethodRevision); if (!rev.isNull()) tt += tr("\nRevision: %1").arg(rev.toInt()); const auto r = index.data(ObjectMethodModelRole::MethodIssues).value(); if (r != QMetaObjectValidatorResult::NoIssue) { QStringList l; if (r & QMetaObjectValidatorResult::SignalOverride) l.push_back(tr("overrides base class signal")); if (r & QMetaObjectValidatorResult::UnknownMethodParameterType) l.push_back(tr("uses parameter type not registered with the meta type system")); tt += tr("\nIssues: %1").arg(l.join(", ")); } return tt; } if (role == ObjectMethodModelRole::MethodSortRole) { if (index.column() == 0) return index.data(ObjectMethodModelRole::MethodSignature); return index.data(Qt::DisplayRole); } if (role == Qt::DecorationRole && index.column() == 0) { const auto r = index.data(ObjectMethodModelRole::MethodIssues).value(); if (r != QMetaObjectValidatorResult::NoIssue) return qApp->style()->standardIcon(QStyle::SP_MessageBoxWarning); } return QIdentityProxyModel::data(index, role); } QVariant ClientMethodModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal) { if (role != Qt::DisplayRole) return QVariant(); switch (section) { case 0: return tr("Signature"); case 1: return tr("Type"); case 2: return tr("Access"); case 3: return tr("Class"); } } return QIdentityProxyModel::headerData(section, orientation, role); } gammaray-2.11.3/ui/tools/objectinspector/clientmethodmodel.h000066400000000000000000000033111413310443000242060ustar00rootroot00000000000000/* clientmethodmodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_CLIENTMETHODMODEL_H #define GAMMARAY_CLIENTMETHODMODEL_H #include namespace GammaRay { /*! Client side part of the QMetaMethod model. */ class ClientMethodModel : public QIdentityProxyModel { Q_OBJECT public: explicit ClientMethodModel(QObject *parent = nullptr); ~ClientMethodModel() override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; }; } #endif // GAMMARAY_CLIENTMETHODMODEL_H gammaray-2.11.3/ui/tools/objectinspector/connectionsclientproxymodel.cpp000066400000000000000000000035761413310443000267220ustar00rootroot00000000000000/* connectionsclientproxymodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "connectionsclientproxymodel.h" #include #include #include using namespace GammaRay; ConnectionsClientProxyModel::ConnectionsClientProxyModel(QObject *parent) : QSortFilterProxyModel(parent) { } ConnectionsClientProxyModel::~ConnectionsClientProxyModel() = default; QVariant ConnectionsClientProxyModel::data(const QModelIndex &index, int role) const { if (role == Qt::DecorationRole && index.column() == 0) { const bool warning = data(index, ConnectionsModelRoles::WarningFlagRole).toBool(); if (warning) return qApp->style()->standardIcon(QStyle::SP_MessageBoxWarning); } return QSortFilterProxyModel::data(index, role); } gammaray-2.11.3/ui/tools/objectinspector/connectionsclientproxymodel.h000066400000000000000000000032431413310443000263560ustar00rootroot00000000000000/* connectionsclientproxymodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_CONNECTIONSCLIENTPROXYMODEL_H #define GAMMARAY_CONNECTIONSCLIENTPROXYMODEL_H #include namespace GammaRay { /** Client-side part of the connections model. */ class ConnectionsClientProxyModel : public QSortFilterProxyModel { Q_OBJECT public: explicit ConnectionsClientProxyModel(QObject *parent = nullptr); ~ConnectionsClientProxyModel() override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; }; } #endif // GAMMARAY_CONNECTIONSCLIENTPROXYMODEL_H gammaray-2.11.3/ui/tools/objectinspector/connectionsextensionclient.cpp000066400000000000000000000036461413310443000265320ustar00rootroot00000000000000/* connectionsextensionclient.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "connectionsextensionclient.h" #include #include using namespace GammaRay; ConnectionsExtensionClient::ConnectionsExtensionClient(const QString &name, QObject *parent) : ConnectionsExtensionInterface(name, parent) { } ConnectionsExtensionClient::~ConnectionsExtensionClient() = default; void ConnectionsExtensionClient::navigateToSender(int modelRow) { Endpoint::instance()->invokeObject(name(), "navigateToSender", QVariantList() << QVariant::fromValue(modelRow)); } void ConnectionsExtensionClient::navigateToReceiver(int modelRow) { Endpoint::instance()->invokeObject(name(), "navigateToReceiver", QVariantList() << QVariant::fromValue(modelRow)); } gammaray-2.11.3/ui/tools/objectinspector/connectionsextensionclient.h000066400000000000000000000034041413310443000261670ustar00rootroot00000000000000/* connectionsextensionclient.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_CONNECTIONSEXTENSIONCLIENT_H #define GAMMARAY_CONNECTIONSEXTENSIONCLIENT_H #include namespace GammaRay { class ConnectionsExtensionClient : public ConnectionsExtensionInterface { Q_OBJECT Q_INTERFACES(GammaRay::ConnectionsExtensionInterface) public: explicit ConnectionsExtensionClient(const QString &name, QObject *parent = nullptr); ~ConnectionsExtensionClient() override; public slots: void navigateToReceiver(int modelRow) override; void navigateToSender(int modelRow) override; }; } #endif // GAMMARAY_CONNECTIONSEXTENSIONCLIENT_H gammaray-2.11.3/ui/tools/objectinspector/connectionstab.cpp000066400000000000000000000077121413310443000240630ustar00rootroot00000000000000/* connectionstab.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "connectionstab.h" #include "ui_connectionstab.h" #include "connectionsclientproxymodel.h" #include #include #include #include #include #include #include using namespace GammaRay; ConnectionsTab::ConnectionsTab(PropertyWidget *parent) : QWidget(parent) , ui(new Ui::ConnectionsTab) { m_interface = ObjectBroker::object( parent->objectBaseName() + ".connectionsExtension"); ui->setupUi(this); ui->inboundView->header()->setObjectName("inboundViewHeader"); ui->outboundView->header()->setObjectName("outboundViewHeader"); QSortFilterProxyModel *proxy = new ConnectionsClientProxyModel(this); proxy->setDynamicSortFilter(true); proxy->setSourceModel(ObjectBroker::model(parent->objectBaseName() + ".inboundConnections")); ui->inboundView->setModel(proxy); ui->inboundView->sortByColumn(0, Qt::AscendingOrder); new SearchLineController(ui->inboundSearchLine, proxy); connect(ui->inboundView, &QWidget::customContextMenuRequested, this, &ConnectionsTab::inboundContextMenu); proxy = new ConnectionsClientProxyModel(this); proxy->setDynamicSortFilter(true); proxy->setSourceModel(ObjectBroker::model(parent->objectBaseName() + ".outboundConnections")); ui->outboundView->setModel(proxy); ui->outboundView->sortByColumn(0, Qt::AscendingOrder); new SearchLineController(ui->outboundSearchLine, proxy); connect(ui->outboundView, &QWidget::customContextMenuRequested, this, &ConnectionsTab::outboundContextMenu); } ConnectionsTab::~ConnectionsTab() = default; static int mapToSourceRow(const QModelIndex &index) { QModelIndex i = index; while (const QAbstractProxyModel *proxy = qobject_cast(i.model())) i = proxy->mapToSource(i); return i.row(); } void ConnectionsTab::inboundContextMenu(const QPoint &pos) { const QModelIndex index = ui->inboundView->currentIndex(); if (!index.isValid() || index.data(ConnectionsModelRoles::ActionRole).toInt() == 0) return; QMenu menu; menu.addAction(tr("Go to sender")); if (menu.exec(ui->inboundView->viewport()->mapToGlobal(pos))) m_interface->navigateToSender(mapToSourceRow(index)); } void ConnectionsTab::outboundContextMenu(const QPoint &pos) { const QModelIndex index = ui->outboundView->currentIndex(); if (!index.isValid() || index.data(ConnectionsModelRoles::ActionRole).toInt() == 0) return; QMenu menu; menu.addAction(tr("Go to receiver")); if (menu.exec(ui->outboundView->viewport()->mapToGlobal(pos))) m_interface->navigateToReceiver(mapToSourceRow(index)); } gammaray-2.11.3/ui/tools/objectinspector/connectionstab.h000066400000000000000000000033411413310443000235220ustar00rootroot00000000000000/* connectionstab.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_CONNECTIONSTAB_H #define GAMMARAY_CONNECTIONSTAB_H #include namespace GammaRay { class ConnectionsExtensionInterface; class PropertyWidget; namespace Ui { class ConnectionsTab; } class ConnectionsTab : public QWidget { Q_OBJECT public: explicit ConnectionsTab(PropertyWidget *parent = nullptr); ~ConnectionsTab() override; private slots: void inboundContextMenu(const QPoint &pos); void outboundContextMenu(const QPoint &pos); private: QScopedPointer ui; ConnectionsExtensionInterface *m_interface; }; } #endif // GAMMARAY_CONNECTIONSTAB_H gammaray-2.11.3/ui/tools/objectinspector/connectionstab.ui000066400000000000000000000051651413310443000237160ustar00rootroot00000000000000 GammaRay::ConnectionsTab 0 0 410 506 Qt::Vertical &Inbound Connections: inboundView Qt::CustomContextMenu false true &Outbound Connections: outboundView Qt::CustomContextMenu false true GammaRay::DeferredTreeView QTreeView
    ui/deferredtreeview.h
    gammaray-2.11.3/ui/tools/objectinspector/enumstab.cpp000066400000000000000000000041421413310443000226620ustar00rootroot00000000000000/* enumstab.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "enumstab.h" #include "ui_enumstab.h" #include "propertywidget.h" #include #include #include "kde/krecursivefilterproxymodel.h" #include using namespace GammaRay; EnumsTab::EnumsTab(PropertyWidget *parent) : QWidget(parent) , m_ui(new Ui_EnumsTab) { m_ui->setupUi(this); m_ui->enumView->header()->setObjectName("enumViewHeader"); setObjectBaseName(parent->objectBaseName()); } EnumsTab::~EnumsTab() = default; void EnumsTab::setObjectBaseName(const QString &baseName) { QSortFilterProxyModel *proxy = new KRecursiveFilterProxyModel(this); proxy->setDynamicSortFilter(true); proxy->setSourceModel(ObjectBroker::model(baseName + '.' + "enums")); m_ui->enumView->setModel(proxy); m_ui->enumView->sortByColumn(0, Qt::AscendingOrder); m_ui->enumView->header()->setSectionResizeMode(QHeaderView::ResizeToContents); new SearchLineController(m_ui->enumSearchLine, proxy); } gammaray-2.11.3/ui/tools/objectinspector/enumstab.h000066400000000000000000000030111413310443000223210ustar00rootroot00000000000000/* enumstab.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef ENUMSTAB_H #define ENUMSTAB_H #include #include namespace GammaRay { class Ui_EnumsTab; class PropertyWidget; class EnumsTab : public QWidget { Q_OBJECT public: explicit EnumsTab(PropertyWidget *parent); ~EnumsTab() override; private: void setObjectBaseName(const QString &baseName); private: std::unique_ptr m_ui; }; } #endif // ENUMSTAB_H gammaray-2.11.3/ui/tools/objectinspector/enumstab.ui000066400000000000000000000013751413310443000225220ustar00rootroot00000000000000 GammaRay::EnumsTab 0 0 400 300 GammaRay::DeferredTreeView QTreeView
    ui/deferredtreeview.h
    gammaray-2.11.3/ui/tools/objectinspector/methodsextensionclient.cpp000066400000000000000000000036631413310443000256520ustar00rootroot00000000000000/* methodsextensionclient.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "methodsextensionclient.h" #include #include #include using namespace GammaRay; MethodsExtensionClient::MethodsExtensionClient(const QString &name, QObject *parent) : MethodsExtensionInterface(name, parent) { } MethodsExtensionClient::~MethodsExtensionClient() = default; void MethodsExtensionClient::activateMethod() { Endpoint::instance()->invokeObject(name(), "activateMethod"); } void MethodsExtensionClient::invokeMethod(Qt::ConnectionType type) { Endpoint::instance()->invokeObject(name(), "invokeMethod", QVariantList() << QVariant::fromValue(type)); } void MethodsExtensionClient::connectToSignal() { Endpoint::instance()->invokeObject(name(), "connectToSignal"); } gammaray-2.11.3/ui/tools/objectinspector/methodsextensionclient.h000066400000000000000000000033741413310443000253160ustar00rootroot00000000000000/* methodsextensionclient.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_METHODSEXTENSIONCLIENT_H #define GAMMARAY_METHODSEXTENSIONCLIENT_H #include namespace GammaRay { class MethodsExtensionClient : public MethodsExtensionInterface { Q_OBJECT Q_INTERFACES(GammaRay::MethodsExtensionInterface) public: explicit MethodsExtensionClient(const QString &name, QObject *parent = nullptr); ~MethodsExtensionClient() override; public slots: void activateMethod() override; void invokeMethod(Qt::ConnectionType type) override; void connectToSignal() override; }; } #endif // GAMMARAY_METHODSEXTENSIONCLIENT_H gammaray-2.11.3/ui/tools/objectinspector/methodstab.cpp000066400000000000000000000112621413310443000231770ustar00rootroot00000000000000/* methodstab.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "methodstab.h" #include "ui_methodstab.h" #include "clientmethodmodel.h" #include #include #include #include #include "common/objectbroker.h" #include "common/metatypedeclarations.h" #include "common/tools/objectinspector/methodsextensioninterface.h" #include #include #include #include using namespace GammaRay; MethodsTab::MethodsTab(PropertyWidget *parent) : QWidget(parent) , m_ui(new Ui_MethodsTab) , m_interface(nullptr) { m_ui->setupUi(this); m_ui->methodView->header()->setObjectName("methodViewHeader"); m_ui->methodLog->header()->setObjectName("methodLogHeader"); setObjectBaseName(parent->objectBaseName()); } MethodsTab::~MethodsTab() = default; void MethodsTab::setObjectBaseName(const QString &baseName) { m_objectBaseName = baseName; auto clientModel = new ClientMethodModel(this); clientModel->setSourceModel(ObjectBroker::model(baseName + '.' + "methods")); auto *proxy = new QSortFilterProxyModel(this); proxy->setDynamicSortFilter(true); proxy->setSourceModel(clientModel); proxy->setSortCaseSensitivity(Qt::CaseInsensitive); proxy->setSortRole(ObjectMethodModelRole::MethodSortRole); m_ui->methodView->setModel(proxy); m_ui->methodView->sortByColumn(0, Qt::AscendingOrder); m_ui->methodView->setSelectionModel(ObjectBroker::selectionModel(proxy)); m_ui->methodView->header()->setSectionResizeMode(QHeaderView::ResizeToContents); new SearchLineController(m_ui->methodSearchLine, proxy); connect(m_ui->methodView, &QAbstractItemView::doubleClicked, this, &MethodsTab::methodActivated); connect(m_ui->methodView, &QWidget::customContextMenuRequested, this, &MethodsTab::methodContextMenu); m_ui->methodLog->setModel(ObjectBroker::model(baseName + '.' + "methodLog")); m_interface = ObjectBroker::object(baseName + ".methodsExtension"); new PropertyBinder(m_interface, "hasObject", m_ui->methodLog, "visible"); } void MethodsTab::methodActivated(const QModelIndex &index) { if (!index.isValid() || !m_interface->hasObject()) return; m_interface->activateMethod(); MethodInvocationDialog dlg(this); dlg.setArgumentModel(ObjectBroker::model(m_objectBaseName + '.' + "methodArguments")); if (dlg.exec()) m_interface->invokeMethod(dlg.connectionType()); } void MethodsTab::methodContextMenu(const QPoint &pos) { const QModelIndex index = m_ui->methodView->indexAt(pos); if (!index.isValid() || !m_interface->hasObject()) return; const QMetaMethod::MethodType methodType = index.data(ObjectMethodModelRole::MetaMethodType).value(); QMenu contextMenu; QAction *invokeAction = nullptr, *connectToAction = nullptr; if (methodType == QMetaMethod::Slot || methodType == QMetaMethod::Method) { invokeAction = contextMenu.addAction(tr("Invoke")); } else if (methodType == QMetaMethod::Signal) { connectToAction = contextMenu.addAction(tr("Connect to")); invokeAction = contextMenu.addAction(tr("Emit")); } else { return; // Can't do any action, so don't try to show an empty context menu. } QAction *action = contextMenu.exec(m_ui->methodView->viewport()->mapToGlobal(pos)); if (action == invokeAction) methodActivated(index); else if (action == connectToAction) m_interface->connectToSignal(); } gammaray-2.11.3/ui/tools/objectinspector/methodstab.h000066400000000000000000000034631413310443000226500ustar00rootroot00000000000000/* methodstab.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef METHODSTAB_H #define METHODSTAB_H #include #include QT_BEGIN_NAMESPACE class QModelIndex; QT_END_NAMESPACE namespace GammaRay { class MethodsExtensionInterface; class Ui_MethodsTab; class PropertyWidget; class MethodsTab : public QWidget { Q_OBJECT public: explicit MethodsTab(PropertyWidget *parent); ~MethodsTab() override; private: void setObjectBaseName(const QString &baseName); private slots: void methodActivated(const QModelIndex &index); void methodContextMenu(const QPoint &pos); private: std::unique_ptr m_ui; MethodsExtensionInterface *m_interface; QString m_objectBaseName; }; } #endif // METHODSTAB_H gammaray-2.11.3/ui/tools/objectinspector/methodstab.ui000066400000000000000000000024561413310443000230370ustar00rootroot00000000000000 GammaRay::MethodsTab 0 0 400 300 Qt::CustomContextMenu false false true false GammaRay::DeferredTreeView QTreeView
    ui/deferredtreeview.h
    gammaray-2.11.3/ui/tools/objectinspector/objectinspectorwidget.cpp000066400000000000000000000107261413310443000254520ustar00rootroot00000000000000/* objectinspector.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "objectinspectorwidget.h" #include "ui_objectinspectorwidget.h" #include #include #include #include #include #include #include #include #include #include using namespace GammaRay; ObjectInspectorWidget::ObjectInspectorWidget(QWidget *parent) : QWidget(parent) , ui(new Ui::ObjectInspectorWidget) , m_stateManager(this) { ui->setupUi(this); ui->objectPropertyWidget->setObjectBaseName(QStringLiteral("com.kdab.GammaRay.ObjectInspector")); auto model = ObjectBroker::model(QStringLiteral("com.kdab.GammaRay.ObjectInspectorTree")); auto *clientModel = new ClientDecorationIdentityProxyModel(this); clientModel->setSourceModel(model); ui->objectTreeView->header()->setObjectName("objectTreeViewHeader"); ui->objectTreeView->setModel(clientModel); ui->objectTreeView->setContextMenuPolicy(Qt::CustomContextMenu); ui->objectTreeView->setDeferredResizeMode(0, QHeaderView::Stretch); ui->objectTreeView->setDeferredResizeMode(1, QHeaderView::Interactive); new SearchLineController(ui->objectSearchLine, clientModel); QItemSelectionModel *selectionModel = ObjectBroker::selectionModel(ui->objectTreeView->model()); ui->objectTreeView->setSelectionModel(selectionModel); connect(selectionModel, &QItemSelectionModel::selectionChanged, this, &ObjectInspectorWidget::objectSelectionChanged); if (qgetenv("GAMMARAY_TEST_FILTER") == "1") { QMetaObject::invokeMethod(ui->objectSearchLine, "setText", Qt::QueuedConnection, Q_ARG(QString, QStringLiteral("Object"))); } connect(ui->objectTreeView, &QWidget::customContextMenuRequested, this, &ObjectInspectorWidget::objectContextMenuRequested); m_stateManager.setDefaultSizes(ui->mainSplitter, UISizeVector() << "60%" << "40%"); connect(ui->objectPropertyWidget, &PropertyWidget::tabsUpdated, this, &ObjectInspectorWidget::propertyWidgetTabsChanged); } ObjectInspectorWidget::~ObjectInspectorWidget() = default; void ObjectInspectorWidget::objectSelectionChanged(const QItemSelection &selection) { if (selection.isEmpty()) return; const QModelIndex index = selection.first().topLeft(); ui->objectTreeView->scrollTo(index); } void ObjectInspectorWidget::objectContextMenuRequested(const QPoint &pos) { const auto index = ui->objectTreeView->indexAt(pos); if (!index.isValid()) return; const auto objectId = index.data(ObjectModel::ObjectIdRole).value(); QMenu menu(tr("Object @ %1").arg(QLatin1String("0x") + QString::number(objectId.id(), 16))); ContextMenuExtension ext(objectId); ext.setLocation(ContextMenuExtension::Creation, index.data( ObjectModel::CreationLocationRole).value()); ext.setLocation(ContextMenuExtension::Declaration, index.data(ObjectModel::DeclarationLocationRole).value()); ext.populateMenu(&menu); menu.exec(ui->objectTreeView->viewport()->mapToGlobal(pos)); } void ObjectInspectorWidget::propertyWidgetTabsChanged() { m_stateManager.saveState(); m_stateManager.reset(); } gammaray-2.11.3/ui/tools/objectinspector/objectinspectorwidget.h000066400000000000000000000113471413310443000251170ustar00rootroot00000000000000/* objectinspector.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_OBJECTINSPECTOR_OBJECTINSPECTORWIDGET_H #define GAMMARAY_OBJECTINSPECTOR_OBJECTINSPECTORWIDGET_H #include #include "tooluifactory.h" #include "propertywidget.h" #include "propertiestab.h" #include "methodstab.h" #include "connectionstab.h" #include "enumstab.h" #include "classinfotab.h" #include "propertiesextensionclient.h" #include "methodsextensionclient.h" #include "connectionsextensionclient.h" #include "applicationattributetab.h" #include "bindingtab.h" #include "stacktracetab.h" #include #include QT_BEGIN_NAMESPACE class QItemSelection; QT_END_NAMESPACE namespace GammaRay { namespace Ui { class ObjectInspectorWidget; } template static QObject *createExtension(const QString &name, QObject *parent) { return new T(name, parent); } class ObjectInspectorWidget : public QWidget { Q_OBJECT public: explicit ObjectInspectorWidget(QWidget *parent = nullptr); ~ObjectInspectorWidget() override; private slots: void objectSelectionChanged(const QItemSelection &selection); void objectContextMenuRequested(const QPoint &pos); void propertyWidgetTabsChanged(); private: QScopedPointer ui; UIStateManager m_stateManager; }; class ObjectInspectorFactory : public QObject, public ToolUiFactory { Q_OBJECT public: QString id() const override { return QStringLiteral("GammaRay::ObjectInspector"); } QString name() const override { return tr("Objects"); } QWidget *createWidget(QWidget *parentWidget) override { return new ObjectInspectorWidget( parentWidget); } void initUi() override { PropertyWidget::registerTab(QStringLiteral("properties"), tr("Properties"), PropertyWidgetTabPriority::First); ObjectBroker::registerClientObjectFactoryCallback( createExtension); PropertyWidget::registerTab(QStringLiteral("methods"), tr("Methods"), PropertyWidgetTabPriority::Basic - 1); ObjectBroker::registerClientObjectFactoryCallback( createExtension); PropertyWidget::registerTab(QStringLiteral("connections"), tr("Connections"), PropertyWidgetTabPriority::Basic - 1); ObjectBroker::registerClientObjectFactoryCallback( createExtension); PropertyWidget::registerTab(QStringLiteral("enums"), tr( "Enums"), PropertyWidgetTabPriority::Exotic - 1); PropertyWidget::registerTab(QStringLiteral("classInfo"), tr("Class Info"), PropertyWidgetTabPriority::Exotic - 1); PropertyWidget::registerTab(QStringLiteral("applicationAttributes"), tr("Attributes"), PropertyWidgetTabPriority::Advanced); PropertyWidget::registerTab(QStringLiteral("bindings"), tr("Bindings"), PropertyWidgetTabPriority::Advanced); PropertyWidget::registerTab(QStringLiteral("stackTrace"), tr("Stack Trace"), PropertyWidgetTabPriority::Exotic); } }; } #endif // GAMMARAY_OBJECTINSPECTOR_H gammaray-2.11.3/ui/tools/objectinspector/objectinspectorwidget.ui000066400000000000000000000036221413310443000253020ustar00rootroot00000000000000 GammaRay::ObjectInspectorWidget 0 0 400 300 Qt::Horizontal 0 0 true 1 0 GammaRay::PropertyWidget QWidget
    propertywidget.h
    1
    GammaRay::DeferredTreeView QTreeView
    deferredtreeview.h
    gammaray-2.11.3/ui/tools/objectinspector/propertiesextensionclient.cpp000066400000000000000000000035051413310443000263760ustar00rootroot00000000000000/* propertiesextensionclient.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Milian Wolff Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "propertiesextensionclient.h" #include #include #include using namespace GammaRay; PropertiesExtensionClient::PropertiesExtensionClient(const QString &name, QObject *parent) : PropertiesExtensionInterface(name, parent) { } PropertiesExtensionClient::~PropertiesExtensionClient() = default; void PropertiesExtensionClient::setProperty(const QString &propertyName, const QVariant &value) { Endpoint::instance()->invokeObject(name(), "setProperty", QVariantList() << QVariant::fromValue( propertyName) << VariantWrapper(value)); } gammaray-2.11.3/ui/tools/objectinspector/propertiesextensionclient.h000066400000000000000000000033531413310443000260440ustar00rootroot00000000000000/* propertiesextensionclient.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_PROPERTIESEXTENSIONCLIENT_H #define GAMMARAY_PROPERTIESEXTENSIONCLIENT_H #include namespace GammaRay { class PropertiesExtensionClient : public PropertiesExtensionInterface { Q_OBJECT Q_INTERFACES(GammaRay::PropertiesExtensionInterface) public: explicit PropertiesExtensionClient(const QString &name, QObject *parent = nullptr); ~PropertiesExtensionClient() override; public slots: void setProperty(const QString &propertyName, const QVariant &value) override; }; } #endif // GAMMARAY_PROPERTIESEXTENSIONCLIENT_H gammaray-2.11.3/ui/tools/objectinspector/propertiestab.cpp000066400000000000000000000157461413310443000237430ustar00rootroot00000000000000/* propertiestab.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "propertiestab.h" #include "ui_propertiestab.h" #include "propertywidget.h" #include "editabletypesmodel.h" #include #include #include #include #include #include #include #include #include #include #include #include using namespace GammaRay; PropertiesTab::PropertiesTab(PropertyWidget *parent) : QWidget(parent) , m_ui(new Ui_PropertiesTab) , m_interface(nullptr) , m_newPropertyValue(nullptr) { m_ui->setupUi(this); m_ui->propertyView->header()->setObjectName("propertyViewHeader"); m_ui->newPropertyButton->setIcon(QIcon::fromTheme(QStringLiteral("list-add"))); setObjectBaseName(parent->objectBaseName()); } PropertiesTab::~PropertiesTab() = default; void PropertiesTab::setObjectBaseName(const QString &baseName) { auto model = ObjectBroker::model(baseName + '.' + "properties"); auto clientModel = new ClientPropertyModel(this); clientModel->setSourceModel(model); auto proxy = new QSortFilterProxyModel(this); proxy->setDynamicSortFilter(true); proxy->setSortCaseSensitivity(Qt::CaseInsensitive); proxy->setSourceModel(clientModel); m_ui->propertyView->setModel(proxy); m_ui->propertyView->sortByColumn(0, Qt::AscendingOrder); m_ui->propertyView->setDeferredResizeMode(0, QHeaderView::ResizeToContents); new SearchLineController(m_ui->propertySearchLine, proxy); m_ui->propertyView->setItemDelegate(new PropertyEditorDelegate(this)); connect(m_ui->propertyView, &QWidget::customContextMenuRequested, this, &PropertiesTab::propertyContextMenu); auto typesModel = new EditableTypesModel(this); proxy = new QSortFilterProxyModel(this); proxy->setSortCaseSensitivity(Qt::CaseInsensitive); proxy->setSourceModel(typesModel); proxy->sort(0); m_ui->newPropertyType->setModel(proxy); connect(m_ui->newPropertyType, static_cast(&QComboBox::currentIndexChanged), this, &PropertiesTab::updateNewPropertyValueEditor); updateNewPropertyValueEditor(); connect(m_ui->newPropertyName, &QLineEdit::textChanged, this, &PropertiesTab::validateNewProperty); validateNewProperty(); connect(m_ui->newPropertyButton, &QAbstractButton::clicked, this, &PropertiesTab::addNewProperty); m_interface = ObjectBroker::object( baseName + ".propertiesExtension"); new PropertyBinder(m_interface, "canAddProperty", m_ui->newPropertyBar, "visible"); m_ui->propertyView->setDeferredHidden(1, !m_interface->hasPropertyValues()); m_ui->propertyView->setRootIsDecorated(m_interface->hasPropertyValues()); connect(m_interface, &PropertiesExtensionInterface::hasPropertyValuesChanged, this, &PropertiesTab::hasValuesChanged); } static PropertyEditorFactory::TypeId selectedTypeId(QComboBox *box) { return static_cast( box->itemData(box->currentIndex(), Qt::UserRole).toInt()); } void PropertiesTab::updateNewPropertyValueEditor() { delete m_newPropertyValue; const PropertyEditorFactory::TypeId type = selectedTypeId(m_ui->newPropertyType); m_newPropertyValue = PropertyEditorFactory::instance()->createEditor(type, this); static_cast(m_ui->newPropertyBar->layout())->insertWidget(5, m_newPropertyValue); m_ui->newPropertyValueLabel->setBuddy(m_newPropertyValue); } void PropertiesTab::validateNewProperty() { Q_ASSERT(m_newPropertyValue); m_ui->newPropertyButton->setEnabled(!m_ui->newPropertyName->text().isEmpty()); } void PropertiesTab::propertyContextMenu(const QPoint &pos) { const QModelIndex index = m_ui->propertyView->indexAt(pos); if (!index.isValid()) return; const int actions = index.data(PropertyModel::ActionRole).toInt(); const auto objectId = index.data(PropertyModel::ObjectIdRole).value(); ContextMenuExtension ext(objectId); const bool canShow = actions != PropertyModel::NoAction || ext.discoverPropertySourceLocation(ContextMenuExtension::GoTo, index); if (!canShow) return; QMenu contextMenu; if (actions & PropertyModel::Delete) { QAction *action = contextMenu.addAction(tr("Remove")); action->setData(PropertyModel::Delete); } if (actions & PropertyModel::Reset) { QAction *action = contextMenu.addAction(tr("Reset")); action->setData(PropertyModel::Reset); } ext.populateMenu(&contextMenu); if (QAction *action = contextMenu.exec(m_ui->propertyView->viewport()->mapToGlobal(pos))) { switch (action->data().toInt()) { case PropertyModel::Delete: m_ui->propertyView->model()->setData(index, QVariant(), Qt::EditRole); break; case PropertyModel::Reset: m_ui->propertyView->model()->setData(index, QVariant(), PropertyModel::ResetActionRole); break; } } } void PropertiesTab::addNewProperty() { Q_ASSERT(m_interface->canAddProperty()); const PropertyEditorFactory::TypeId type = selectedTypeId(m_ui->newPropertyType); const QByteArray editorPropertyName = PropertyEditorFactory::instance()->valuePropertyName(type); const QVariant value = m_newPropertyValue->property(editorPropertyName); m_interface->setProperty(m_ui->newPropertyName->text(), value); m_ui->newPropertyName->clear(); updateNewPropertyValueEditor(); } void PropertiesTab::hasValuesChanged() { m_ui->propertyView->setDeferredHidden(1, !m_interface->hasPropertyValues()); m_ui->propertyView->setRootIsDecorated(m_interface->hasPropertyValues()); } gammaray-2.11.3/ui/tools/objectinspector/propertiestab.h000066400000000000000000000036461413310443000234040ustar00rootroot00000000000000/* propertiestab.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef PROPERTIESTAB_H #define PROPERTIESTAB_H #include #include QT_BEGIN_NAMESPACE class QModelIndex; QT_END_NAMESPACE namespace GammaRay { class PropertiesExtensionInterface; class Ui_PropertiesTab; class PropertyWidget; class PropertiesTab : public QWidget { Q_OBJECT public: explicit PropertiesTab(PropertyWidget *parent); ~PropertiesTab() override; private: void setObjectBaseName(const QString &baseName); private slots: void updateNewPropertyValueEditor(); void validateNewProperty(); void propertyContextMenu(const QPoint &pos); void addNewProperty(); void hasValuesChanged(); private: std::unique_ptr m_ui; PropertiesExtensionInterface *m_interface; QWidget *m_newPropertyValue; }; } #endif // PROPERTIESTAB_H gammaray-2.11.3/ui/tools/objectinspector/propertiestab.ui000066400000000000000000000046121413310443000235640ustar00rootroot00000000000000 GammaRay::PropertiesTab 0 0 400 300 Qt::CustomContextMenu false 0 &New Dynamic Property: newPropertyName Name &Type: newPropertyType &Value: Add GammaRay::DeferredTreeView QTreeView
    ui/deferredtreeview.h
    gammaray-2.11.3/ui/tools/objectinspector/stacktracetab.cpp000066400000000000000000000047421413310443000236650ustar00rootroot00000000000000/* stacktracetab.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "stacktracetab.h" #include "ui_stacktracetab.h" #include #include #include #include #include #include using namespace GammaRay; GammaRay::StackTraceTab::StackTraceTab(PropertyWidget *parent) : QWidget(parent) , ui(new Ui::StackTraceTab) { ui->setupUi(this); ui->stackTraceView->setItemDelegate(new PropertyEditorDelegate(ui->stackTraceView)); ui->stackTraceView->setModel(ObjectBroker::model(parent->objectBaseName() + QStringLiteral(".stackTraceModel"))); ui->stackTraceView->header()->setObjectName(QStringLiteral("stackTraceViewHeader")); connect(ui->stackTraceView, &QWidget::customContextMenuRequested, this, &StackTraceTab::contextMenuRequested); } StackTraceTab::~StackTraceTab() = default; void StackTraceTab::contextMenuRequested(QPoint pos) { const auto idx = ui->stackTraceView->indexAt(pos); if (!idx.isValid()) return; const auto loc = idx.sibling(idx.row(), 1).data().value(); if (!loc.isValid()) return; QMenu contextMenu; ContextMenuExtension cme; cme.setLocation(ContextMenuExtension::ShowSource, loc); cme.populateMenu(&contextMenu); contextMenu.exec(ui->stackTraceView->viewport()->mapToGlobal(pos)); } gammaray-2.11.3/ui/tools/objectinspector/stacktracetab.h000066400000000000000000000031431413310443000233240ustar00rootroot00000000000000/* stacktracetab.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_STACKTRACETAB_H #define GAMMARAY_STACKTRACETAB_H #include #include namespace GammaRay { class PropertyWidget; namespace Ui { class StackTraceTab; } class StackTraceTab : public QWidget { Q_OBJECT public: explicit StackTraceTab(PropertyWidget *parent = nullptr); ~StackTraceTab() override; private Q_SLOTS: void contextMenuRequested(QPoint pos); private: std::unique_ptr ui; }; } #endif // GAMMARAY_STACKTRACETAB_H gammaray-2.11.3/ui/tools/objectinspector/stacktracetab.ui000066400000000000000000000013631413310443000235140ustar00rootroot00000000000000 GammaRay::StackTraceTab 0 0 400 300 Qt::CustomContextMenu false true gammaray-2.11.3/ui/tools/problemreporter/000077500000000000000000000000001413310443000203655ustar00rootroot00000000000000gammaray-2.11.3/ui/tools/problemreporter/problemclientmodel.cpp000066400000000000000000000073001413310443000247510ustar00rootroot00000000000000/* problemclientmodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "problemclientmodel.h" #include #include #include #include #include #include using namespace GammaRay; ProblemClientModel::ProblemClientModel(QObject* parent) : QSortFilterProxyModel(parent) { } ProblemClientModel::~ProblemClientModel() = default; QVariant ProblemClientModel::data(const QModelIndex& index, int role) const { if (role == Qt::DecorationRole) { if (index.column() != 0) { return QVariant(); } auto severity = QSortFilterProxyModel::data(index, ProblemModelRoles::SeverityRole).value(); switch (severity) { case Problem::Info: return QVariant::fromValue(QIcon::fromTheme(QStringLiteral("dialog-information"))); case Problem::Warning: return QVariant::fromValue(UIResources::themedIcon(QStringLiteral("warning.png")).pixmap(16, 16)); case Problem::Error: return QVariant::fromValue(QIcon::fromTheme(QStringLiteral("dialog-error"))); } } return QSortFilterProxyModel::data(index, role); } QVariant ProblemClientModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal) { if (role == Qt::DisplayRole) { switch (section) { case 0: return tr("Problem Description"); case 1: return tr("Source Location"); } } return QVariant(); } return QSortFilterProxyModel::headerData(section, orientation, role); } bool ProblemClientModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const { if (!QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent)) { // Implement the usual search-bar filtering return false; } auto index = sourceModel()->index(source_row, 0, source_parent); auto id = index.data(ProblemModelRoles::ProblemIdRole).toString(); return std::none_of(m_disabledCheckers.begin(), m_disabledCheckers.end(), [id](const QString &checkerId) { return id.startsWith(checkerId); }); } void ProblemClientModel::disableChecker(const QString& id) { if (m_disabledCheckers.contains(id)) return; m_disabledCheckers.push_back(id); invalidateFilter(); } void ProblemClientModel::enableChecker(const QString& id) { m_disabledCheckers.erase(std::remove(m_disabledCheckers.begin(), m_disabledCheckers.end(), id), m_disabledCheckers.end()); invalidateFilter(); } gammaray-2.11.3/ui/tools/problemreporter/problemclientmodel.h000066400000000000000000000037731413310443000244300ustar00rootroot00000000000000/* problemclientmodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_PROBLEMCLIENTMODEL_H #define GAMMARAY_PROBLEMCLIENTMODEL_H #include #include namespace GammaRay { /*! Client-side part of the meta types model. */ class ProblemClientModel : public QSortFilterProxyModel { Q_OBJECT public: explicit ProblemClientModel(QObject *parent = nullptr); ~ProblemClientModel() override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; bool filterAcceptsRow(int source_row, const QModelIndex & source_parent) const override; void disableChecker (const QString &id); void enableChecker (const QString &id); private: QVector m_disabledCheckers; // holds the ids of the disabled checkers }; } #endif // GAMMARAY_PROBLEMCLIENTMODEL_H gammaray-2.11.3/ui/tools/problemreporter/problemreporterclient.cpp000066400000000000000000000027671413310443000255270ustar00rootroot00000000000000/* problemreporterclient.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2018-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "problemreporterclient.h" #include using namespace GammaRay; ProblemReporterClient::ProblemReporterClient(QObject* parent) : ProblemReporterInterface(parent) { } ProblemReporterClient::~ProblemReporterClient() = default; void ProblemReporterClient::requestScan() { Endpoint::instance()->invokeObject(objectName(), "requestScan"); } gammaray-2.11.3/ui/tools/problemreporter/problemreporterclient.h000066400000000000000000000031451413310443000251630ustar00rootroot00000000000000/* problemreporterclient.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2018-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_PROBLEMREPORTERCLIENT_H #define GAMMARAY_PROBLEMREPORTERCLIENT_H #include namespace GammaRay { class ProblemReporterClient : public ProblemReporterInterface { Q_OBJECT Q_INTERFACES(GammaRay::ProblemReporterInterface) public: explicit ProblemReporterClient(QObject *parent); ~ProblemReporterClient() override; void requestScan() override; }; } #endif // GAMMARAY_PROBLEMREPORTERCLIENT_H gammaray-2.11.3/ui/tools/problemreporter/problemreporterwidget.cpp000066400000000000000000000160571413310443000255310ustar00rootroot00000000000000/* problemreporterwidget.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2018-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "problemreporterwidget.h" #include "ui_problemreporterwidget.h" #include "problemclientmodel.h" #include "problemreporterclient.h" #include #include #include #include #include #include #include #include #include #include #include using namespace GammaRay; class ProblemFilterDelegate : public QStyledItemDelegate { public: explicit ProblemFilterDelegate(QAbstractItemView *view) : QStyledItemDelegate(view) {} protected: void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override { QStyleOptionViewItem opt = option; initStyleOption(&opt, index); QString title = index.data(Qt::DisplayRole).toString(); QString description = index.data(Qt::ToolTipRole).toString(); QStyle *style = opt.widget ? opt.widget->style() : QApplication::style(); opt.text = index.data(Qt::DisplayRole).toString() + QChar(QChar::LineSeparator) + index.data(Qt::ToolTipRole).toString(); auto textRect = style->subElementRect(QStyle::SE_ItemViewItemText, &opt); // We first paint a normal ItemViewItem but with no text and then draw the title and description ourselves opt.text.clear(); style->drawControl(QStyle::CE_ItemViewItem, &opt, painter, opt.widget); style->drawItemText(painter, textRect, Qt::AlignLeft | Qt::AlignTop, opt.palette, opt.state & QStyle::State_Enabled, title, QPalette::Text); auto oldOpacity = painter->opacity(); painter->setOpacity(0.5); style->drawItemText(painter, textRect, Qt::AlignLeft | Qt::AlignBottom | Qt::TextWordWrap, opt.palette, opt.state & QStyle::State_Enabled, description, QPalette::Text); painter->setOpacity(oldOpacity); } QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override { QStyleOptionViewItem opt = option; initStyleOption(&opt, index); opt.text = index.data(Qt::DisplayRole).toString() + QChar(QChar::LineSeparator) + index.data(Qt::ToolTipRole).toString(); QStyle *style = opt.widget ? opt.widget->style() : QApplication::style(); return style->sizeFromContents(QStyle::CT_ItemViewItem, &opt, QSize(), opt.widget); } }; static QObject *createProblemReporterClient(const QString & /*name*/, QObject *parent) { return new ProblemReporterClient(parent); } ProblemReporterWidget::ProblemReporterWidget(QWidget *parent) : QWidget(parent) , ui(new Ui::ProblemReporterWidget) , m_stateManager(this) { ui->setupUi(this); ObjectBroker::registerClientObjectFactoryCallback( createProblemReporterClient); ProblemReporterInterface *iface = ObjectBroker::object(); connect(ui->scanButton, &QAbstractButton::clicked, iface, &ProblemReporterInterface::requestScan); connect(ui->scanButton, &QAbstractButton::clicked, ui->progressBar, &QWidget::show); connect(iface, &ProblemReporterInterface::problemScansFinished, ui->progressBar, &QWidget::hide); ui->progressBar->setVisible(false); m_problemsModel = new ProblemClientModel(this); m_problemsModel->setSourceModel(ObjectBroker::model(QStringLiteral("com.kdab.GammaRay.ProblemModel"))); ui->problemView->header()->setObjectName("problemViewHeader"); ui->problemView->setDeferredResizeMode(0, QHeaderView::ResizeToContents); ui->problemView->setDeferredResizeMode(1, QHeaderView::ResizeToContents); ui->problemView->setModel(m_problemsModel); ui->problemView->sortByColumn(0, Qt::AscendingOrder); connect(ui->problemView, &QWidget::customContextMenuRequested, this, &ProblemReporterWidget::problemViewContextMenu); new SearchLineController(ui->searchLine, m_problemsModel); m_availableCheckersModel = ObjectBroker::model(QStringLiteral("com.kdab.GammaRay.AvailableProblemCheckersModel")); ui->problemfilterwidget->viewport()->setAutoFillBackground(false); ui->problemfilterwidget->setAttribute(Qt::WA_MacShowFocusRect, false); ui->problemfilterwidget->setItemDelegate(new ProblemFilterDelegate(ui->problemfilterwidget)); ui->problemfilterwidget->setModel(m_availableCheckersModel); connect(m_availableCheckersModel, &QAbstractItemModel::dataChanged, this, &ProblemReporterWidget::updateFilter); } ProblemReporterWidget::~ProblemReporterWidget() = default; void ProblemReporterWidget::problemViewContextMenu(const QPoint &p) { QModelIndex index = ui->problemView->indexAt(p); ObjectId objectId = index.data(ObjectModel::ObjectIdRole).value(); QMenu menu; ContextMenuExtension ext(objectId); const auto sourceLocations = index.data(ProblemModelRoles::SourceLocationRole).value>(); for (const SourceLocation &sourceLocation : sourceLocations) { ext.setLocation(ContextMenuExtension::GoTo, sourceLocation); } ext.populateMenu(&menu); menu.exec(ui->problemView->viewport()->mapToGlobal(p)); } void GammaRay::ProblemReporterWidget::updateFilter(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector &roles) { if (!roles.empty() && !roles.contains(Qt::CheckStateRole)) return; for (int i = topLeft.row(); i <= bottomRight.row(); ++i) { auto index = topLeft.sibling(i, 0); auto checkState = index.data(Qt::CheckStateRole); auto id = index.data(Qt::EditRole).toString(); if (!checkState.canConvert()) continue; if (index.data(Qt::CheckStateRole).value() == Qt::Checked) { m_problemsModel->enableChecker(id); } else { m_problemsModel->disableChecker(id); } } } gammaray-2.11.3/ui/tools/problemreporter/problemreporterwidget.h000066400000000000000000000040551413310443000251710ustar00rootroot00000000000000/* problemreporterwidget.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2018-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Anton Kreuzkamp Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_PROBLEMREPORTERWIDGET_H #define GAMMARAY_PROBLEMREPORTERWIDGET_H #include #include #include #include QT_BEGIN_NAMESPACE class QAbstractItemModel; class QModelIndex; QT_END_NAMESPACE namespace GammaRay { namespace Ui { class ProblemReporterWidget; } class ProblemClientModel; class ProblemReporterWidget : public QWidget { Q_OBJECT public: explicit ProblemReporterWidget(QWidget *parent = nullptr); ~ProblemReporterWidget() override; private slots: void problemViewContextMenu(const QPoint &p); void updateFilter(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector &roles); private: QScopedPointer ui; UIStateManager m_stateManager; QAbstractItemModel *m_availableCheckersModel; ProblemClientModel *m_problemsModel; }; } #endif // GAMMARAY_PROBLEMREPORTERWIDGET_H gammaray-2.11.3/ui/tools/problemreporter/problemreporterwidget.ui000066400000000000000000000057241413310443000253630ustar00rootroot00000000000000 GammaRay::ProblemReporterWidget 0 0 400 300 Qt::Horizontal 0 Available Problem Checkers Qt::AlignCenter QFrame::NoFrame QFrame::Plain QAbstractItemView::NoSelection QListView::Adjust true Start Scan for Problems 0 Qt::CustomContextMenu false 0 0 GammaRay::DeferredTreeView QTreeView
    ui/deferredtreeview.h
    gammaray-2.11.3/ui/tools/resourcebrowser/000077500000000000000000000000001413310443000203755ustar00rootroot00000000000000gammaray-2.11.3/ui/tools/resourcebrowser/clientresourcemodel.cpp000066400000000000000000000044131413310443000251520ustar00rootroot00000000000000/* clientresourcemodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "clientresourcemodel.h" #include using namespace GammaRay; ClientResourceModel::ClientResourceModel(QObject *parent) : QIdentityProxyModel(parent) { } ClientResourceModel::~ClientResourceModel() = default; QVariant ClientResourceModel::data(const QModelIndex &index, int role) const { if (role == Qt::DecorationRole && index.column() == 0) { if (!index.parent().isValid()) return m_iconProvider.icon(QFileIconProvider::Drive); if (hasChildren(index)) return m_iconProvider.icon(QFileIconProvider::Folder); const QList types = m_mimeDb.mimeTypesForFileName(index.data( Qt::DisplayRole).toString()); for (const QMimeType &mt : types) { QIcon icon = QIcon::fromTheme(mt.iconName()); if (!icon.isNull()) return icon; icon = QIcon::fromTheme(mt.genericIconName()); if (!icon.isNull()) return icon; } return m_iconProvider.icon(QFileIconProvider::File); } return QIdentityProxyModel::data(index, role); } gammaray-2.11.3/ui/tools/resourcebrowser/clientresourcemodel.h000066400000000000000000000035201413310443000246150ustar00rootroot00000000000000/* clientresourcemodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_CLIENTRESOURCEMODEL_H #define GAMMARAY_CLIENTRESOURCEMODEL_H #include #include #include namespace GammaRay { /** * Adds file icons for the resource model. * This can't be done server-side since the icon stuff might not exist in a pure QtCore application. */ class ClientResourceModel : public QIdentityProxyModel { Q_OBJECT public: explicit ClientResourceModel(QObject *parent = nullptr); ~ClientResourceModel() override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; private: QFileIconProvider m_iconProvider; QMimeDatabase m_mimeDb; }; } #endif // GAMMARAY_CLIENTRESOURCEMODEL_H gammaray-2.11.3/ui/tools/resourcebrowser/resourcebrowserclient.cpp000066400000000000000000000037311413310443000255370ustar00rootroot00000000000000/* * * * This file is part of GammaRay, the Qt application inspection and * manipulation tool. * * Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com * Author: Milian Wolff * * Licensees holding valid commercial KDAB GammaRay licenses may use this file in * accordance with GammaRay Commercial License Agreement provided with the Software. * * Contact info@kdab.com if any conditions of this licensing are not clear to you. * * 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, see . * */ #include "resourcebrowserclient.h" #include using namespace GammaRay; ResourceBrowserClient::ResourceBrowserClient(QObject *parent) : ResourceBrowserInterface(parent) { } ResourceBrowserClient::~ResourceBrowserClient() = default; void ResourceBrowserClient::downloadResource(const QString &sourceFilePath, const QString &targetFilePath) { Endpoint::instance()->invokeObject(objectName(), "downloadResource", QVariantList() << sourceFilePath << targetFilePath); } void ResourceBrowserClient::selectResource(const QString &sourceFilePath, int line, int column) { Endpoint::instance()->invokeObject(objectName(), "selectResource", QVariantList() << sourceFilePath << line << column); } gammaray-2.11.3/ui/tools/resourcebrowser/resourcebrowserclient.h000066400000000000000000000034521413310443000252040ustar00rootroot00000000000000/* * * * This file is part of GammaRay, the Qt application inspection and * manipulation tool. * * Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com * Author: Milian Wolff * * Licensees holding valid commercial KDAB GammaRay licenses may use this file in * accordance with GammaRay Commercial License Agreement provided with the Software. * * Contact info@kdab.com if any conditions of this licensing are not clear to you. * * 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, see . * */ #ifndef RESOURCEBROWSERCLIENT_H #define RESOURCEBROWSERCLIENT_H #include namespace GammaRay { class ResourceBrowserClient : public ResourceBrowserInterface { Q_OBJECT Q_INTERFACES(GammaRay::ResourceBrowserInterface) public: explicit ResourceBrowserClient(QObject *parent); ~ResourceBrowserClient() override; void downloadResource(const QString &sourceFilePath, const QString &targetFilePath) override; void selectResource(const QString &sourceFilePath, int line = -1, int column = -1) override; }; } #endif // RESOURCEBROWSERCLIENT_H gammaray-2.11.3/ui/tools/resourcebrowser/resourcebrowserwidget.cpp000066400000000000000000000234361413310443000255500ustar00rootroot00000000000000/* resourcebrowserwidget.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Stephen Kelly Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "resourcebrowserwidget.h" #include "ui_resourcebrowserwidget.h" #include "resourcebrowserclient.h" #include "clientresourcemodel.h" #include #include <3rdparty/qt/resourcemodel.h> #include #include #include #include #include #include #include #include #include #include #include using namespace GammaRay; static QObject *createResourceBrowserClient(const QString & /*name*/, QObject *parent) { return new ResourceBrowserClient(parent); } ResourceBrowserWidget::ResourceBrowserWidget(QWidget *parent) : QWidget(parent) , ui(new Ui::ResourceBrowserWidget) , m_stateManager(this) , m_interface(nullptr) { ObjectBroker::registerClientObjectFactoryCallback( createResourceBrowserClient); m_interface = ObjectBroker::object(); connect(m_interface, &ResourceBrowserInterface::resourceDeselected, this, &ResourceBrowserWidget::resourceDeselected); connect(m_interface, &ResourceBrowserInterface::resourceSelected, this, &ResourceBrowserWidget::resourceSelected); connect(m_interface, &ResourceBrowserInterface::resourceDownloaded, this, &ResourceBrowserWidget::resourceDownloaded); ui->setupUi(this); auto resModel = ObjectBroker::model(QStringLiteral("com.kdab.GammaRay.ResourceModel")); auto *model = new ClientResourceModel(this); model->setSourceModel(resModel); ui->treeView->header()->setObjectName("resourceTreeViewHeader"); ui->treeView->setExpandNewContent(true); ui->treeView->setDeferredResizeMode(0, QHeaderView::ResizeToContents); ui->treeView->setDeferredResizeMode(1, QHeaderView::ResizeToContents); ui->treeView->setDeferredResizeMode(2, QHeaderView::ResizeToContents); ui->treeView->setDeferredHidden(3, true); ui->treeView->setModel(model); ui->treeView->setSelectionModel(ObjectBroker::selectionModel(ui->treeView->model())); new SearchLineController(ui->searchLine, model); connect(ui->treeView, &DeferredTreeView::newContentExpanded, this, &ResourceBrowserWidget::setupLayout); ui->treeView->setContextMenuPolicy(Qt::CustomContextMenu); connect(ui->treeView, &QWidget::customContextMenuRequested, this, &ResourceBrowserWidget::handleCustomContextMenu); ui->resourceLabel->setText(tr("Select a Resource to Preview")); ui->stackedWidget->setCurrentWidget(ui->contentLabelPage); ui->textBrowser->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)); } ResourceBrowserWidget::~ResourceBrowserWidget() = default; void ResourceBrowserWidget::selectResource(const QString &sourceFilePath, int line, int column) { m_interface->selectResource(sourceFilePath, line, column); } void ResourceBrowserWidget::setupLayout() { // now the view was setup properly and we can mess with the splitter to resize // the widgets for nicer display int viewWidth = ui->treeView->columnWidth(0) +ui->treeView->columnWidth(1) +ui->treeView->columnWidth(2) +ui->treeView->contentsMargins().left() +ui->treeView->contentsMargins().right() + ui->treeView->verticalScrollBar()->width(); const int totalWidth = ui->mainSplitter->width(); const int minPreviewWidth = 150; if (totalWidth > viewWidth + minPreviewWidth) { m_stateManager.setDefaultSizes(ui->mainSplitter, UISizeVector() << viewWidth << (totalWidth - viewWidth - ui->mainSplitter->handleWidth())); m_stateManager.restoreState(); } } void ResourceBrowserWidget::resourceDeselected() { ui->resourceLabel->setText(tr("Select a Resource to Preview")); ui->stackedWidget->setCurrentWidget(ui->contentLabelPage); } void ResourceBrowserWidget::resourceSelected(const QByteArray &contents, int line, int column) { // try to decode as an image first, fall back to text otherwise auto rawData = contents; QBuffer buffer(&rawData); buffer.open(QBuffer::ReadOnly); QImageReader reader(&buffer); const auto img = reader.read(); if (!img.isNull()) { ui->resourceLabel->setPixmap(QPixmap::fromImage(img)); ui->stackedWidget->setCurrentWidget(ui->contentLabelPage); return; } // avoid re-highlighting the existing content when switching the syntax ui->textBrowser->clear(); // get the file name for syntax highlighting QString fileName; const auto selection = ui->treeView->selectionModel()->selectedRows(); if (!selection.isEmpty()) fileName = selection.at(0).data().toString(); ui->textBrowser->setFileName(fileName); // TODO: make encoding configurable ui->textBrowser->setPlainText(contents); QTextDocument *document = ui->textBrowser->document(); QTextCursor cursor(document->findBlockByLineNumber(line - 1)); if (!cursor.isNull()) { if (column >= 1) cursor.setPosition(cursor.position() + column - 1); ui->textBrowser->setTextCursor(cursor); } ui->textBrowser->setFocus(); ui->stackedWidget->setCurrentWidget(ui->contentTextPage); } void ResourceBrowserWidget::resourceDownloaded(const QString &targetFilePath, const QByteArray &contents) { QFile file(targetFilePath); if (!file.open(QIODevice::WriteOnly)) { qWarning("Unable to write resource content to %s", qPrintable(targetFilePath)); return; } file.write(contents); file.close(); } static QStringList collectDirectories(const QModelIndex &index, const QString &baseDirectory) { QStringList result; const QAbstractItemModel *model = index.model(); const QString directoryPath = index.data(ResourceModel::FilePathRole).toString(); const QString relativeDirectory = directoryPath.mid(baseDirectory.size()); result << relativeDirectory; for (int row = 0; row < model->rowCount(index); ++row) { const QModelIndex childIndex = model->index(row, 0, index); if (model->hasChildren(childIndex)) result += collectDirectories(childIndex, baseDirectory); } return result; } static QStringList collectFiles(const QModelIndex &index, const QString &baseDirectory) { QStringList result; const QAbstractItemModel *model = index.model(); for (int row = 0; row < model->rowCount(index); ++row) { const QModelIndex childIndex = model->index(row, 0, index); if (model->hasChildren(childIndex)) { result += collectFiles(childIndex, baseDirectory); } else { const QString filePath = childIndex.data(ResourceModel::FilePathRole).toString(); const QString relativeFilePath = filePath.mid(baseDirectory.size()); result << relativeFilePath; } } return result; } void ResourceBrowserWidget::handleCustomContextMenu(const QPoint &pos) { const QModelIndex selectedIndex = ui->treeView->indexAt(pos); if (!selectedIndex.isValid()) return; QMenu menu; menu.addAction(style()->standardIcon(QStyle::SP_DialogSaveButton), tr("Save As...")); if (!menu.exec(ui->treeView->viewport()->mapToGlobal(pos))) return; if (selectedIndex.model()->hasChildren(selectedIndex)) { const QString sourceDirectory = selectedIndex.data(ResourceModel::FilePathRole).toString(); const QString targetDirectory = QFileDialog::getExistingDirectory(this, tr("Save As")); // create local target directory tree foreach (const QString &directoryPath, collectDirectories(selectedIndex, sourceDirectory)) { if (directoryPath.isEmpty()) continue; QDir dir(targetDirectory + '/' + directoryPath); dir.mkpath(QStringLiteral(".")); } // request all resource files foreach (const QString &filePath, collectFiles(selectedIndex, sourceDirectory)) m_interface->downloadResource(sourceDirectory + filePath, targetDirectory + filePath); } else { const QString sourceFilePath = selectedIndex.data(ResourceModel::FilePathRole).toString(); const QString sourceFileName = sourceFilePath.mid(sourceFilePath.lastIndexOf('/') + 1); const QString targetFilePath = QFileDialog::getSaveFileName(this, tr( "Save As"), sourceFileName); if (targetFilePath.isEmpty()) return; m_interface->downloadResource(sourceFilePath, targetFilePath); } } gammaray-2.11.3/ui/tools/resourcebrowser/resourcebrowserwidget.h000066400000000000000000000042521413310443000252100ustar00rootroot00000000000000/* resourcebrowserwidget.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Stephen Kelly Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_RESOURCEBROWSER_RESOURCEBROWSERWIDGET_H #define GAMMARAY_RESOURCEBROWSER_RESOURCEBROWSERWIDGET_H #include #include QT_BEGIN_NAMESPACE class QItemSelection; QT_END_NAMESPACE namespace GammaRay { class ResourceBrowserInterface; namespace Ui { class ResourceBrowserWidget; } class ResourceBrowserWidget : public QWidget { Q_OBJECT public: explicit ResourceBrowserWidget(QWidget *parent = nullptr); ~ResourceBrowserWidget() override; public slots: void selectResource(const QString &sourceFilePath, int line = -1, int column = -1); private slots: void setupLayout(); void resourceDeselected(); void resourceSelected(const QByteArray &contents, int line, int column); void resourceDownloaded(const QString &targetFilePath, const QByteArray &contents); void handleCustomContextMenu(const QPoint &pos); private: QScopedPointer ui; UIStateManager m_stateManager; ResourceBrowserInterface *m_interface; }; } #endif // GAMMARAY_RESOURCEBROWSER_H gammaray-2.11.3/ui/tools/resourcebrowser/resourcebrowserwidget.ui000066400000000000000000000100511413310443000253700ustar00rootroot00000000000000 GammaRay::ResourceBrowserWidget 0 0 1036 846 0 0 0 0 Qt::Horizontal 1 0 0 0 0 true QPlainTextEdit::NoWrap true Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse 0 0 0 0 QFrame::NoFrame true Qt::AlignCenter 0 0 527 846 Qt::AlignCenter GammaRay::DeferredTreeView QTreeView
    ui/deferredtreeview.h
    GammaRay::CodeEditor QPlainTextEdit
    ui/codeeditor/codeeditor.h
    gammaray-2.11.3/ui/tooluifactory.cpp000066400000000000000000000027451413310443000174210ustar00rootroot00000000000000/* tooluifactory.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "tooluifactory.h" using namespace GammaRay; ToolUiFactory::ToolUiFactory() = default; ToolUiFactory::~ToolUiFactory() = default; QString ToolUiFactory::name() const { return QString(); // in the common case this is provided via ProxyToolUIFactory } bool ToolUiFactory::remotingSupported() const { return true; } void ToolUiFactory::initUi() { } gammaray-2.11.3/ui/tooluifactory.h000066400000000000000000000064571413310443000170720ustar00rootroot00000000000000/* This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Volker Krause Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_TOOLUIFACTORY_H #define GAMMARAY_TOOLUIFACTORY_H #include "gammaray_ui_export.h" #include #include #include namespace GammaRay { /*! * An abstract interface for creating the UI parts of probe tools. * * The ToolUiFactory class is an abstract base class for creating UIs for probe tools * for GammaRay. The unique identifier used for the UI must match the one of the corresponding * probe tool. */ class GAMMARAY_UI_EXPORT ToolUiFactory { public: ToolUiFactory(); virtual ~ToolUiFactory(); /*! * Unique id of this tool, must match the id of a the corresponding probe tool. * @return a QString containing the tool id. */ virtual QString id() const = 0; /*! * Human readable name of this tool. * You do not need to override this usually, the plugin loader will fill this in. * @return a QString containing the tool name. */ virtual QString name() const; /*! * Return @c true if this tool supports remoting, @c false otherwise. * The default implementation returns @c true. */ virtual bool remotingSupported() const; /*! * Create the UI part of this tool. * @param parentWidget The parent widget for the visual elements of this tool. * @return a pointer to the created QwWidget. */ virtual QWidget *createWidget(QWidget *parentWidget) = 0; /*! * Initialize UI related stuff for this tool. This function is called on loading * the plugin, before the widget itself is needed. Use createWidget to create * the actual widget. */ virtual void initUi(); private: Q_DISABLE_COPY(ToolUiFactory) }; /*! * A templated convenience ToolUiFactory applicable for most use-cases. */ template class StandardToolUiFactory : public ToolUiFactory { public: QString id() const override { return QString(); // TODO is this a problem?? } QWidget *createWidget(QWidget *parentWidget) override { return new ToolUi(parentWidget); } }; } QT_BEGIN_NAMESPACE Q_DECLARE_INTERFACE(GammaRay::ToolUiFactory, "com.kdab.GammaRay.ToolUiFactory/1.0") QT_END_NAMESPACE Q_DECLARE_METATYPE(GammaRay::ToolUiFactory *) #endif gammaray-2.11.3/ui/trailingcolorlabel.cpp000066400000000000000000000075771413310443000203760ustar00rootroot00000000000000/* trailingcolorlabel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Christoph Sterz Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "trailingcolorlabel.h" #include #include #include #include namespace GammaRay { TrailingColorLabel::TrailingColorLabel(QWidget *parent) : QLabel(parent) { setAttribute(Qt::WA_TransparentForMouseEvents); m_pickedColor = qRgba(0,0,0,0); setHidden(true); } void TrailingColorLabel::paintEvent(QPaintEvent *event) { Q_UNUSED(event); QPainter *p = new QPainter(this); QFontMetrics metrics(p->font()); const int margin = 5; const int squaresize = 20; const int height = 30; const int numberGroupWidth = metrics.width(QStringLiteral(" 000")); const int width = margin + squaresize + margin + 4 * numberGroupWidth + margin; setMinimumSize(QSize(width + 1, height + 1)); p->setPen(QPen(Qt::lightGray)); p->setBrush(palette().base()); p->drawRect(0, 0, width, height); p->setPen(QPen(palette().color(QPalette::Normal, QPalette::Text))); p->drawText(QRect(margin + squaresize + margin + 0 * numberGroupWidth, 0, numberGroupWidth, height), Qt::AlignVCenter | Qt::AlignRight, QString::number(qRed(m_pickedColor))); p->drawText(QRect(margin + squaresize + margin + 1 * numberGroupWidth, 0, numberGroupWidth, height), Qt::AlignVCenter | Qt::AlignRight, QString::number(qGreen(m_pickedColor))); p->drawText(QRect(margin + squaresize + margin + 2 * numberGroupWidth, 0, numberGroupWidth, height), Qt::AlignVCenter | Qt::AlignRight, QString::number(qBlue(m_pickedColor))); //alpha p->setPen(QPen(palette().color(QPalette::Disabled, QPalette::Text))); p->drawText(QRect(margin + squaresize + margin + 3 * numberGroupWidth + 2, 0, numberGroupWidth, height), Qt::AlignVCenter | Qt::AlignLeft, "|"); p->drawText(QRect(margin + squaresize + margin + 3 * numberGroupWidth, 0, numberGroupWidth, height), Qt::AlignVCenter | Qt::AlignRight, QString::number(qAlpha(m_pickedColor))); { // Color bg-pattern QBrush brush; QPixmap bgPattern(squaresize, squaresize); bgPattern.fill(Qt::lightGray); QPainter bgPainter(&bgPattern); bgPainter.fillRect(10, 0, 10, 10, Qt::gray); bgPainter.fillRect(0, 10, 10, 10, Qt::gray); brush.setTexture(bgPattern); p->setBrush(brush); p->drawRect(margin, margin, squaresize, squaresize); } QColor color = QColor::fromRgba(m_pickedColor); p->setBrush(QBrush(color)); p->setPen(QPen(Qt::black)); p->drawRect(margin, margin, squaresize, squaresize); } QColor TrailingColorLabel::pickedColor() const { return QColor::fromRgba(m_pickedColor); } void TrailingColorLabel::setPickedColor(QRgb color) { m_pickedColor = color; } } gammaray-2.11.3/ui/trailingcolorlabel.h000066400000000000000000000031071413310443000200240ustar00rootroot00000000000000/* trailingcolorlabel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2017-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Christoph Sterz Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef TRAILINGCOLORLABEL_H #define TRAILINGCOLORLABEL_H #include #include namespace GammaRay { class TrailingColorLabel : public QLabel { Q_OBJECT public: explicit TrailingColorLabel(QWidget *parent = nullptr); void paintEvent(QPaintEvent *event) override; QColor pickedColor() const; signals: public slots: void setPickedColor(QRgb color); private: QRgb m_pickedColor; }; } #endif // TRAILINGCOLORLABEL_H gammaray-2.11.3/ui/uiintegration.cpp000066400000000000000000000036711413310443000173760ustar00rootroot00000000000000/* * This file is part of GammaRay, the Qt application inspection and * manipulation tool. * * Copyright (C) 2014-2021 Klar?lvdalens Datakonsult AB, a KDAB Group company, info@kdab.com * Author: Anton Kreuzkamp * * Licensees holding valid commercial KDAB GammaRay licenses may use this file in * accordance with GammaRay Commercial License Agreement provided with the Software. * * Contact info@kdab.com if any conditions of this licensing are not clear to you. * * 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, see . * */ #include "uiintegration.h" #include #include #include using namespace GammaRay; UiIntegration *UiIntegration::s_uiIntegrationInstance = nullptr; UiIntegration::UiIntegration(QObject *parent) : QObject(parent) { Q_ASSERT(!s_uiIntegrationInstance); s_uiIntegrationInstance = this; } UiIntegration::~UiIntegration() { s_uiIntegrationInstance = nullptr; } UiIntegration *UiIntegration::instance() { return s_uiIntegrationInstance; } void UiIntegration::requestNavigateToCode(const QUrl &url, int lineNumber, int columnNumber) { if (UiIntegration::instance()) emit UiIntegration::instance()->navigateToCode(url, lineNumber, columnNumber); } bool UiIntegration::hasDarkUI() { return QApplication::palette().color(QPalette::Base).lightness() < 128; } gammaray-2.11.3/ui/uiintegration.h000066400000000000000000000043371413310443000170430ustar00rootroot00000000000000/* * This file is part of GammaRay, the Qt application inspection and * manipulation tool. * * Copyright (C) 2014-2021 Klar?lvdalens Datakonsult AB, a KDAB Group company, info@kdab.com * Author: Anton Kreuzkamp * * Licensees holding valid commercial KDAB GammaRay licenses may use this file in * accordance with GammaRay Commercial License Agreement provided with the Software. * * Contact info@kdab.com if any conditions of this licensing are not clear to you. * * 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, see . * */ #ifndef UIINTEGRATION_H #define UIINTEGRATION_H #include "gammaray_ui_export.h" #include QT_BEGIN_NAMESPACE class QUrl; QT_END_NAMESPACE namespace GammaRay { /*! Interface for UI plugins to integrate with the client UI. * This is especially relevant when the client is embedded inside another application * such as an IDE. */ class GAMMARAY_UI_EXPORT UiIntegration : public QObject { Q_OBJECT public: explicit UiIntegration(QObject *parent = nullptr); ~UiIntegration() override; static UiIntegration *instance(); /*! Show the source code at the given position. * The stand-alone client will launch a text editor for this, * if the client is embedded in an IDE it can chose to navigate directly. */ static void requestNavigateToCode(const QUrl &url, int lineNumber, int columnNumber = 0); static bool hasDarkUI(); Q_SIGNALS: void navigateToCode(const QUrl &url, int lineNumber, int columnNumber); private: /*! Singleton instance. */ static UiIntegration *s_uiIntegrationInstance; }; } // namespace GammaRay #endif // UIINTEGRATION_H gammaray-2.11.3/ui/uiresources.cpp000066400000000000000000000135721413310443000170660ustar00rootroot00000000000000/* uiresources.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Filipe Azevedo Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include "uiresources.h" #include #include #include #include #include #include #include #include #include using namespace GammaRay; namespace GammaRay { namespace UIResources { struct PairThemeFileName { bool operator==(const PairThemeFileName &other) const { return devicePixelRatio == other.devicePixelRatio && theme == other.theme && filePath == other.filePath; } bool operator!=(const PairThemeFileName &other) const { return !operator==(other); } qreal devicePixelRatio; UIResources::Theme theme; QString filePath; }; typedef QHash HashedThemeFilePaths; UIResources::Theme s_currentTheme = UIResources::Unknown; QHash s_cachedFilePaths; uint qHash(const PairThemeFileName &entry) { uint h1 = ::qHash(entry.devicePixelRatio); uint h2 = ::qHash(entry.theme); uint h3 = ::qHash(entry.filePath); return h1 + h2 + h3; } qreal devicePixelRatio(QWidget *widget) { qreal pixelRatio = qApp->devicePixelRatio(); if (widget) { const int screenNumber = qMax(0, qApp->desktop()->screenNumber(widget)); const QScreen *screen = qApp->screens().value(screenNumber); pixelRatio = screen->devicePixelRatio(); } return pixelRatio; } UIResources::Theme theme() { return s_currentTheme == UIResources::Unknown ? UIResources::Default : s_currentTheme; } QString themePath(UIResources::Theme theme) { switch (theme) { case UIResources::Unknown: break; case UIResources::Light: return QStringLiteral(":/gammaray/ui/light"); case UIResources::Dark: return QStringLiteral(":/gammaray/ui/dark"); } return QString(); } QString themePath() { return themePath(UIResources::theme()); } QString themedPath(UIResources::Theme theme, const QString &extra, QWidget *widget) { QFileInfo candidate(QString::fromLatin1("%1/%2").arg(UIResources::themePath(theme), extra)); const int dpr = qRound(devicePixelRatio(widget)); if (dpr > 1) { const QString highdpi = QString::fromLatin1("%1/%2@%4x.%3") .arg(candidate.path(), candidate.baseName(), candidate.suffix()) .arg(dpr); if (QFile::exists(highdpi)) candidate.setFile(highdpi); } return candidate.filePath(); } QString themedPath(const QString &extra, QWidget *widget) { return themedPath(UIResources::theme(), extra, widget); } QString themedFilePath(ThemeEntryType type, UIResources::Theme theme, const QString &filePath, QWidget *widget) { const PairThemeFileName pair = { devicePixelRatio(widget), theme, filePath }; HashedThemeFilePaths &hash(s_cachedFilePaths[type]); auto it = hash.find(pair); if (it == hash.end()) { const QString iconFilePath = QString::fromLatin1("%1/%2") .arg(type == Pixmap ? QStringLiteral("pixmaps") : QStringLiteral("icons"), filePath); QString candidate(UIResources::themedPath(theme, iconFilePath, widget)); // Fallback to default theme file if (theme != UIResources::Default && !QFile::exists(candidate)) { const QString fallback = UIResources::themedFilePath(type, UIResources::Default, filePath, widget); if (QFile::exists(fallback)) candidate = fallback; } it = hash.insert(pair, candidate); Q_ASSERT_X(QFile::exists(*it), "themedFilePath", qPrintable(*it)); } return *it; } } } void UIResources::setTheme(UIResources::Theme theme) { s_currentTheme = theme; } QIcon UIResources::themedIcon(const QString &filePath) { return QIcon(themedFilePath(Icon, UIResources::theme(), filePath, nullptr)); } QPixmap UIResources::themedPixmap(const QString &filePath, QWidget *widget) { return QPixmap(themedFilePath(Pixmap, UIResources::theme(), filePath, widget)); } QImage UIResources::themedImage(const QString &filePath, QWidget *widget) { return QImage(themedFilePath(Pixmap, UIResources::theme(), filePath, widget)); } QString UIResources::themedFilePath(UIResources::ThemeEntryType type, const QString &filePath, QWidget *widget) { return themedFilePath(type, UIResources::theme(), filePath, widget); } QImage UIResources::tintedImage(const QImage &image, const QColor &color) { QImage img(image.alphaChannel()); img.setDevicePixelRatio(image.devicePixelRatio()); QColor newColor = color; for (int i = 0; i < img.colorCount(); ++i) { newColor.setAlpha(qGray(img.color(i))); img.setColor(i, newColor.rgba()); } return img; } QPixmap UIResources::tintedPixmap(const QImage &image, const QColor &color) { return QPixmap::fromImage(tintedImage(image, color)); } gammaray-2.11.3/ui/uiresources.h000066400000000000000000000051271413310443000165300ustar00rootroot00000000000000/* uiresources.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Filipe Azevedo Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_UIRESOURCES_H #define GAMMARAY_UIRESOURCES_H #include "gammaray_ui_export.h" #include #include #include namespace GammaRay { namespace UIResources { enum Theme { Unknown, Light, Dark, Default = Light }; enum ThemeEntryType { Icon, Pixmap }; // Set the current theme to use for UI icons used in actions and toolbars. GAMMARAY_UI_EXPORT void setTheme(UIResources::Theme theme); // Relative to UIResources::themePath/icons of the current theme GAMMARAY_UI_EXPORT QIcon themedIcon(const QString &filePath); // Relative to UIResources::themePath/pixmaps of the current theme GAMMARAY_UI_EXPORT QPixmap themedPixmap(const QString &filePath, QWidget *widget); // Relative to UIResources::themePath/pixmaps of the current theme GAMMARAY_UI_EXPORT QImage themedImage(const QString &filePath, QWidget *widget); // Relative to UIResources::themePath/[icons|pixmap] of the current theme GAMMARAY_UI_EXPORT QString themedFilePath(UIResources::ThemeEntryType type, const QString &filePath, QWidget *widget); GAMMARAY_UI_EXPORT QImage tintedImage(const QImage &image, const QColor &color); GAMMARAY_UI_EXPORT QPixmap tintedPixmap(const QImage &image, const QColor &color); // Deprecated, use setTheme typedef Theme IconTheme; inline void setIconTheme(UIResources::IconTheme theme) { setTheme(theme); } } } #endif // GAMMARAY_UIRESOURCES_H gammaray-2.11.3/ui/uistatemanager.cpp000066400000000000000000000453761413310443000175360ustar00rootroot00000000000000/* * This file is part of GammaRay, the Qt application inspection and * manipulation tool. * * Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com * Author: Filipe Azevedo * * Licensees holding valid commercial KDAB GammaRay licenses may use this file in * accordance with GammaRay Commercial License Agreement provided with the Software. * * Contact info@kdab.com if any conditions of this licensing are not clear to you. * * 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, see . * */ #include "uistatemanager.h" #include "deferredtreeview.h" #include "deferredtreeview_p.h" #include "common/endpoint.h" #include "common/settempvalue.h" #include #include #include #include #include #include #include #include #include #include #define WIDGET_CUSTOMIZED "customized" using namespace GammaRay; namespace { QAbstractItemView *headerView(QHeaderView *header) { QWidget *view = header->parentWidget(); while (!qobject_cast(view)) view = view->parentWidget(); return qobject_cast(view); } void distributeSpace(QList &sizes, int size, int handleSize) { std::vector::Iterator> its; int usedSpace = 0; for (auto it = sizes.begin(), end = sizes.end(); it != end; ++it) { if ((*it) == -1) its.push_back(it); else usedSpace += (*it); } if (!its.empty()) { const int freeSpace = size - usedSpace - (sizes.count() * handleSize) - handleSize; const int space = freeSpace / its.size(); for (auto & it : its) (*it) = space; } } using QSplitterList = QList; using QHeaderViewList = QList; } UIStateManager::UIStateManager(QWidget *widget) : QObject(widget) , m_widget(widget) , m_stateSettings(new QSettings(this)) , m_initialized(false) , m_settingsAccess(false) , m_resizing(false) , m_targetStateSource(nullptr) , m_targetRestoreMethodId(-1) , m_targetSaveMethodId(-1) { Q_ASSERT(m_widget); m_widget->installEventFilter(this); } UIStateManager::~UIStateManager() = default; QWidget *UIStateManager::widget() const { return m_widget; } bool UIStateManager::initialized() const { return m_initialized; } QList UIStateManager::splitters() const { return m_widget->findChildren(); } QList UIStateManager::headers() const { return m_widget->findChildren(); } void UIStateManager::setup() { if (!Endpoint::instance()->isConnected()) // in-process mode, don't interfere with QSettings there return; Q_ASSERT(!m_initialized); if (!checkWidget(m_widget)) return; m_initialized = true; m_stateSettings->beginGroup(QString::fromLatin1("UiState/%1").arg(widgetName(m_widget))); // Make sure objects names are unique. // Also make the comparison lower case as some QSettings implementations // are case sensitive. QSet knownNames; knownNames << widgetPath(m_widget); foreach (QSplitter *splitter, splitters()) { if (!checkWidget(splitter)) continue; const QString name = widgetPath(splitter); if (knownNames.contains(name)) { qWarning() << Q_FUNC_INFO << "Duplicate widget name" << name << "in widget" << m_widget; continue; } knownNames << name; connect(splitter, &QSplitter::splitterMoved, this, &UIStateManager::widgetCustomized, Qt::UniqueConnection); } foreach (QHeaderView *header, headers()) { if (!checkWidget(header)) continue; const QString name = widgetPath(header); if (knownNames.contains(name)) { qWarning() << Q_FUNC_INFO << "Duplicate widget name" << name << "in widget" << m_widget; continue; } knownNames << name; connect(header, &QHeaderView::sectionResized, this, &UIStateManager::widgetCustomized, Qt::UniqueConnection); connect(header, &QHeaderView::sectionCountChanged, this, &UIStateManager::headerSectionCountChanged, Qt::UniqueConnection); QAbstractItemView *view = headerView(header); view->removeEventFilter(this); view->installEventFilter(this); } // Try to find methods on widget m_targetStateSource = m_widget->metaObject(); m_targetRestoreMethodId = m_targetStateSource->indexOfMethod("restoreTargetState(QSettings*)"); m_targetSaveMethodId = m_targetStateSource->indexOfMethod("saveTargetState(QSettings*)"); // Here for inherited UIStateManager where widget is not modifiable (ie: QtC) if (m_targetRestoreMethodId == -1 || m_targetSaveMethodId == -1) { m_targetStateSource = metaObject(); m_targetRestoreMethodId = m_targetStateSource->indexOfMethod("restoreTargetState(QSettings*)"); m_targetSaveMethodId = m_targetStateSource->indexOfMethod("saveTargetState(QSettings*)"); } if (m_targetRestoreMethodId == -1 || m_targetSaveMethodId == -1) { m_targetStateSource = nullptr; m_targetRestoreMethodId = -1; m_targetSaveMethodId = -1; } restoreState(); } void UIStateManager::restoreState() { if (!Endpoint::instance()->isConnected()) return; if (!m_initialized) { qWarning() << Q_FUNC_INFO << "Attempting to restoreState for a not yet initialized state manager."; return; } if (m_settingsAccess) { qWarning() << Q_FUNC_INFO << "Recursive restore/save state detected" << widgetPath(m_widget) << m_widget; Q_ASSERT(false); return; } m_settingsAccess = true; restoreWindowState(); restoreSplitterState(); restoreHeaderState(); // Allow restore state per end point if (m_targetStateSource) { Q_ASSERT(!Endpoint::instance()->key().isEmpty()); m_stateSettings->beginGroup(Endpoint::instance()->key()); QMetaMethod method = m_targetStateSource->method(m_targetRestoreMethodId); QObject *target = m_targetStateSource == m_widget->metaObject() ? qobject_cast(m_widget) : this; method.invoke(target, Q_ARG(QSettings*, m_stateSettings)); m_stateSettings->endGroup(); } m_settingsAccess = false; } void UIStateManager::saveState() { if (!Endpoint::instance()->isConnected()) return; if (!m_initialized) { qWarning() << Q_FUNC_INFO << "Attempting to saveState for a not yet initialized state manager."; return; } if (m_settingsAccess) { qWarning() << Q_FUNC_INFO << "Recursive save/restore state detected" << widgetPath(m_widget) << m_widget; return; } m_settingsAccess = true; // Allow save state per end point if (m_targetStateSource) { Q_ASSERT(!Endpoint::instance()->key().isEmpty()); m_stateSettings->beginGroup(Endpoint::instance()->key()); QMetaMethod method = m_targetStateSource->method(m_targetSaveMethodId); QObject *target = m_targetStateSource == m_widget->metaObject() ? qobject_cast(m_widget) : this; method.invoke(target, Q_ARG(QSettings*, m_stateSettings)); m_stateSettings->endGroup(); } saveWindowState(); saveSplitterState(); saveHeaderState(); m_settingsAccess = false; } bool UIStateManager::eventFilter(QObject *object, QEvent *event) { const bool connected = Endpoint::instance()->isConnected(); if (connected && object == m_widget) { if (event->type() == QEvent::Hide) { if (m_initialized) saveState(); } } const bool result = QObject::eventFilter(object, event); if (connected && object == m_widget) { if (event->type() == QEvent::Show) { if (!m_initialized) setup(); } } if (connected && event->type() == QEvent::Resize) { if (m_initialized && !m_resizing) widgetResized(qobject_cast(object)); } return result; } QString UIStateManager::widgetName(QWidget *widget) const { return (widget->objectName().isEmpty() ? QString::fromLatin1(widget->metaObject()->className()) : widget->objectName()).toLower(); } QString UIStateManager::widgetPath(QWidget *widget) const { QStringList path(widgetName(widget)); if (widget != m_widget) { QWidget *parent = widget->parentWidget(); while (parent && parent != m_widget) { path.prepend(widgetName(parent)); parent = parent->parentWidget(); } } return path.join(QLatin1String("-")); } UISizeVector UIStateManager::defaultSizes(QSplitter *splitter) const { return checkWidget(splitter) ? m_defaultSplitterSizes.value(widgetPath(splitter)) : UISizeVector(); } void UIStateManager::setDefaultSizes(QSplitter *splitter, const UISizeVector &defaultSizes) { if (checkWidget(splitter)) m_defaultSplitterSizes[widgetPath(splitter)] = defaultSizes; } UISizeVector UIStateManager::defaultSizes(QHeaderView *header) const { return checkWidget(header) ? m_defaultHeaderSizes.value(widgetPath(header)) : UISizeVector(); } void UIStateManager::setDefaultSizes(QHeaderView *header, const UISizeVector &defaultSizes) { if (checkWidget(header)) m_defaultHeaderSizes[widgetPath(header)] = defaultSizes; } void UIStateManager::reset() { if (m_initialized) { m_initialized = false; m_stateSettings->endGroup(); } setup(); } QString UIStateManager::widgetGeometryKey(QWidget *widget) const { return QString::fromLatin1("%1Geometry").arg(widgetPath(widget)); } QString UIStateManager::widgetStateKey(QWidget *widget) const { return QString::fromLatin1("%1State").arg(widgetPath(widget)); } QString UIStateManager::widgetStateSectionsKey(QWidget *widget) const { return QString::fromLatin1("%1StateSections").arg(widgetPath(widget)); } bool UIStateManager::checkWidget(QWidget *widget) const { if (widget->objectName().isEmpty()) { qWarning() << Q_FUNC_INFO << "Widget with no name" << widget << widgetPath(widget) << "in widget" << m_widget; return false; } return true; } int UIStateManager::percentToInt(const QString &size) const { return size.left(size.length() -1).toInt(); // clazy:exclude=qstring-ref due to Qt4 support } void UIStateManager::restoreWindowState() { QMainWindow *window = qobject_cast(m_widget); if (window) { const QByteArray geometry = m_stateSettings->value(widgetGeometryKey(m_widget)).toByteArray(); const QByteArray state = m_stateSettings->value(widgetStateKey(m_widget)).toByteArray(); if (geometry.isEmpty()) { const QRect area = qApp->desktop()->availableGeometry(QCursor::pos()); QRect rect(QPoint(), QSize(1024, 768)); rect.moveCenter(area.center()); m_widget->setGeometry(rect); } else { if (!m_resizing) { window->restoreGeometry(geometry); window->restoreState(state); } } } } void UIStateManager::saveWindowState() { QMainWindow *window = qobject_cast(m_widget); if (window) { m_stateSettings->setValue(widgetGeometryKey(m_widget), window->saveGeometry()); m_stateSettings->setValue(widgetStateKey(m_widget), window->saveState()); } } void UIStateManager::restoreSplitterState(QSplitter *splitter) { const QSplitterList splitters = splitter ? (QSplitterList() << splitter) : this->splitters(); for (QSplitter *splitter : splitters) { if (!checkWidget(splitter)) continue; const QByteArray state = m_stateSettings->value(widgetStateKey(splitter)).toByteArray(); if (state.isEmpty()) { const UISizeVector defaultSizes = this->defaultSizes(splitter); if (!defaultSizes.isEmpty()) { Q_ASSERT(defaultSizes.count() == splitter->count()); QList sizes; sizes.reserve(defaultSizes.count()); for (const QVariant &size : defaultSizes) { switch (size.type()) { case QVariant::Int: // Pixels sizes << size.toInt(); break; case QVariant::String: { // Percent int value = percentToInt(size.toString()); if (value == -1) sizes << value; else if (splitter->orientation() == Qt::Horizontal) sizes << splitter->width() * value / 100; else sizes << splitter->height() * value / 100; break; } default: Q_ASSERT(false); } } distributeSpace(sizes, splitter->orientation() == Qt::Horizontal ? splitter->width() : splitter->height(), splitter->handleWidth()); splitter->setSizes(sizes); } } else { if (!m_resizing) { splitter->restoreState(state); splitter->setProperty(WIDGET_CUSTOMIZED, true); } } } } void UIStateManager::saveSplitterState(QSplitter *splitter) { const QSplitterList splitters = splitter ? (QSplitterList() << splitter) : this->splitters(); for (QSplitter *splitter : splitters) { if (!checkWidget(splitter) || !splitter->property(WIDGET_CUSTOMIZED).toBool()) continue; m_stateSettings->setValue(widgetStateKey(splitter), splitter->saveState()); } } void UIStateManager::restoreHeaderState(QHeaderView *header) { const QHeaderViewList headers = header ? (QHeaderViewList() << header) : this->headers(); for (QHeaderView *header : headers) { if (header->orientation() == Qt::Vertical || !checkWidget(header) || header->count() == 0) continue; const QByteArray state = m_stateSettings->value(widgetStateKey(header)).toByteArray(); if (state.isEmpty()) { const UISizeVector defaultSizes = this->defaultSizes(header); QAbstractItemView *view = headerView(header); if (!defaultSizes.isEmpty()) { Q_ASSERT(defaultSizes.count() == header->count()); QList sizes; sizes.reserve(defaultSizes.count()); int i = 0; for (auto it = defaultSizes.constBegin(), end = defaultSizes.constEnd(); it != end; ++it) { int size = 0; switch ((*it).type()) { case QVariant::Int: // Pixels size = (*it).toInt(); break; case QVariant::String: // Percent size = percentToInt((*it).toString()); if (size == -1) { } else if (header->orientation() == Qt::Horizontal) { size = view->width() * size / 100; } else { size = view->height() * size / 100; } break; default: Q_ASSERT(false); } switch (header->sectionResizeMode(i)) { case QHeaderView::Interactive: case QHeaderView::Fixed: header->resizeSection(i, size == -1 ? header->sectionSizeHint(i) : size); break; default: break; } i++; } } } else { if (!m_resizing) { const int count = m_stateSettings->value(widgetStateSectionsKey(header), -1).toInt(); if (count == header->count()) { header->restoreState(state); header->setProperty(WIDGET_CUSTOMIZED, true); } else { // QHeaderView is not able to restore state correctly when the column count changed // and lead to crash... let clear the settings. m_stateSettings->remove(widgetStateSectionsKey(header)); m_stateSettings->remove(widgetStateKey(header)); } } } } } void UIStateManager::saveHeaderState(QHeaderView *header) { const QHeaderViewList headers = header ? (QHeaderViewList() << header) : this->headers(); for (QHeaderView *header : headers) { if (header->orientation() == Qt::Vertical || !checkWidget(header) || header->count() == 0 || !header->property(WIDGET_CUSTOMIZED).toBool()) continue; m_stateSettings->setValue(widgetStateSectionsKey(header), header->count()); m_stateSettings->setValue(widgetStateKey(header), header->saveState()); } } void UIStateManager::headerSectionCountChanged() { restoreHeaderState(qobject_cast(sender())); } void UIStateManager::widgetResized(QWidget *widget) { Util::SetTempValue guard(m_resizing, true); if (widget == m_widget) { restoreSplitterState(); restoreHeaderState(); } else { foreach (QHeaderView *header, widget->findChildren()) restoreHeaderState(header); } } void UIStateManager::widgetCustomized() { QSplitter *splitter = qobject_cast(sender()); HeaderView *header = qobject_cast(sender()); if (splitter) { splitter->setProperty(WIDGET_CUSTOMIZED, true); saveSplitterState(splitter); } else if (header) { // There is no way to know if a resize is manual or programamtically... // So only cast against our HeaderView which has a isState() member. if (header->isState(HeaderView::ResizeSection)) { header->setProperty(WIDGET_CUSTOMIZED, true); saveHeaderState(header); } } } gammaray-2.11.3/ui/uistatemanager.h000066400000000000000000000106331413310443000171670ustar00rootroot00000000000000/* * This file is part of GammaRay, the Qt application inspection and * manipulation tool. * * Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com * Author: Filipe Azevedo * * Licensees holding valid commercial KDAB GammaRay licenses may use this file in * accordance with GammaRay Commercial License Agreement provided with the Software. * * Contact info@kdab.com if any conditions of this licensing are not clear to you. * * 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, see . * */ #ifndef UISTATEMANAGER_H #define UISTATEMANAGER_H #include "gammaray_ui_export.h" #include #include #include #include #include #include QT_BEGIN_NAMESPACE class QWidget; class QSplitter; class QSettings; class QHeaderView; QT_END_NAMESPACE namespace GammaRay { using UISizeVector = QVector; /*! Interface for UI plugins to manage their states. * * The manager is able to track if changes appear and only persists them in this case. * That mean any not moved splitter handle will not be persisted, and % based sizes * will works correctly even on resize. * Any QMainwindow is always restored/saved. * * There is now the possibility to implements custom restore/save state per targets. * Just create those 2 public Q_INVOKABLE in the UIStateManager widget: * - Q_INVOKABLE void saveTargetState(QSettings *settings) const; * - Q_INVOKABLE void restoreTargetState(QSettings *settings); * * Usually, button checked, QTabWidget indexes... * Do not store size related things or native Qt save/restore states here, that's the role * of UIStateManager itself. */ class GAMMARAY_UI_EXPORT UIStateManager : public QObject { Q_OBJECT public: explicit UIStateManager(QWidget *widget); ~UIStateManager() override; QWidget *widget() const; bool initialized() const; virtual QList splitters() const; virtual QList headers() const; virtual void setup(); UISizeVector defaultSizes(QSplitter *splitter) const; void setDefaultSizes(QSplitter *splitter, const UISizeVector &defaultSizes); UISizeVector defaultSizes(QHeaderView *header) const; void setDefaultSizes(QHeaderView *header, const UISizeVector &defaultSizes); public slots: void reset(); virtual void restoreState(); virtual void saveState(); protected: ///@cond internal bool eventFilter(QObject *object, QEvent *event) override; QString widgetName(QWidget *widget) const; QString widgetPath(QWidget *widget) const; QString widgetGeometryKey(QWidget *widget) const; QString widgetStateKey(QWidget *widget) const; QString widgetStateSectionsKey(QWidget *widget) const; bool checkWidget(QWidget *widget) const; int percentToInt(const QString &size) const; protected slots: void restoreWindowState(); void saveWindowState(); void restoreSplitterState(QSplitter *splitter = nullptr); void saveSplitterState(QSplitter *splitter = nullptr); void restoreHeaderState(QHeaderView *header = nullptr); void saveHeaderState(QHeaderView *header = nullptr); ///@endcond private: QPointer m_widget; QSettings *m_stateSettings; bool m_initialized; bool m_settingsAccess; bool m_resizing; // Int -> pixels, String -> suffix dependent (% -> percent else pixels) // The UISizeVector is logical index based. QHash m_defaultSplitterSizes; QHash m_defaultHeaderSizes; // target restore/save handling const QMetaObject *m_targetStateSource; int m_targetRestoreMethodId; int m_targetSaveMethodId; private slots: void headerSectionCountChanged(); void widgetResized(QWidget *widget); void widgetCustomized(); }; } // namespace GammaRay #endif // UISTATEMANAGER_H gammaray-2.11.3/ui/visibilityfilterproxymodel.cpp000066400000000000000000000050351413310443000222310ustar00rootroot00000000000000/* objectidfilterproxymodel.cpp This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Manfred Tonch Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #include #include "common/objectmodel.h" #include using namespace GammaRay; VisibilityFilterProxyModel::VisibilityFilterProxyModel(QObject *parent) : KRecursiveFilterProxyModel(parent) , m_hideItems(true) , m_flagRole(0) , m_invisibleMask(0) { } void VisibilityFilterProxyModel::sort(int column, Qt::SortOrder order) { Q_UNUSED(column); Q_UNUSED(order); } bool VisibilityFilterProxyModel::acceptRow(int source_row, const QModelIndex &source_parent) const { const QModelIndex source_index = sourceModel()->index(source_row, 0, source_parent); if (!source_index.isValid()) { return false; } if (m_hideItems) { int flags = source_index.data(m_flagRole).toInt(); if (flags & m_invisibleMask) return false; } return KRecursiveFilterProxyModel::acceptRow(source_row, source_parent); } void VisibilityFilterProxyModel::setHideItems(bool hideItems) { if (m_hideItems != hideItems) { m_hideItems = hideItems; invalidateFilter(); } } void VisibilityFilterProxyModel::setFlagRole(int flagRole) { if (m_flagRole != flagRole) { m_flagRole = flagRole; invalidateFilter(); } } void VisibilityFilterProxyModel::setInvisibleMask(int invisibleMask) { if (m_invisibleMask != invisibleMask) { m_invisibleMask = invisibleMask; invalidateFilter(); } } gammaray-2.11.3/ui/visibilityfilterproxymodel.h000066400000000000000000000047511413310443000217020ustar00rootroot00000000000000/* objectidfilterproxymodel.h This file is part of GammaRay, the Qt application inspection and manipulation tool. Copyright (C) 2010-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Manfred Tonch Licensees holding valid commercial KDAB GammaRay licenses may use this file in accordance with GammaRay Commercial License Agreement provided with the Software. Contact info@kdab.com if any conditions of this licensing are not clear to you. 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, see . */ #ifndef GAMMARAY_VISIBILITYFILTERPROXYMODEL_H #define GAMMARAY_VISIBILITYFILTERPROXYMODEL_H #include "gammaray_common_export.h" #include "3rdparty/kde/krecursivefilterproxymodel.h" namespace GammaRay { /** * @brief A KRecursiveFilterProxyModel for ObjectIds. * * Filter in and sort according to the objects list. */ class VisibilityFilterProxyModel : public KRecursiveFilterProxyModel { Q_OBJECT public: /** * Constructor. * @param parent is the parent object for this instance. */ explicit VisibilityFilterProxyModel(QObject *parent = nullptr); void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override; void setFlagRole(int flagRole); void setInvisibleMask(int invisibleMask); public slots: void setHideItems(bool hideItems); protected: /** * Determines if the item in the specified row can be included in the model. * @param source_row is a non-zero integer representing the row of the item. * @param source_parent is the parent QModelIndex for this model. * @return true if the item in the row can be included in the model; * otherwise returns false. */ bool acceptRow(int source_row, const QModelIndex &source_parent) const override; private: bool m_hideItems; int m_flagRole; int m_invisibleMask; }; } #endif // GAMMARAY_VISIBILITYFILTERPROXYMODEL_H
  • ك1m J||ËXpIu]vc6 IzY|#$t ]743= AFwpEnz<11T֮߀Ѡ2y$%&R˯N^N3O%'g0Ν[Zn$N2) @mW^[!2"LO]xg~"##ťF:L@Uq\ 2N/..-`톍(̴){rQ)T8HZzjUk2}{yؐ!XNGnN6^8ɓ&oRQYa,&/'qcG8v ))P3|k_~FO>MA~HYHΚI\\,5݁lw3{t4a<ӧMſLMESPvpL9{3h4,pN'_ --sS8l()9zl;hcvmx_tAeҫL$ mi,Z0=j0~,XVBCͤ`u26ma՚udS<4>BBLaVAФΛV+NĻQ^J]}=km ';Iǣ\y\ƌOZ"1k촒r^/uuR=SUU::: ehy CBBPUӉBEEowtttb cd4!vϋ^'d:WSnwr0F.j@d2+8A`@;>L&#&W/wݵ~?60<^/v ?ޠd2!Kyc ΟGfFz09p1`0mp:QdSH+z~b0p8p(VAeB fy'Z)zZ]}fF%t/ X$_+{:p\~g5`P$>y*zqӒGhhw% MkG"]Ea>^rfl>O EXye1;X7jԈA'<<>VY-}>?2rD1%#@Rb"$IDDtʲ|- IDAT7BI0L&Fw%EQ wzs`k;V #fZC$鮛Bp(NEwC'Nb$'ketF(^;|ee69٫]p(H݃,P줄. ! 4qMl.PY AlH.XFI5P!|t>V=Z͞5#0P:}DOr^\yGꘐ=(,a] 5}[4<LM6U b^_èV8(! 3/֋\;G9ڨtmpoﮂp>?IYbt ^oF]jxO:I5)q-ucڂ  AA  0"AA`D( 0; |Jm{}Xp ,F\*]^v@{Ce :=R_OH QH UE UUiu8g㹍hT$4h%"Cw;FC߻X*Ğh`x^*vpbq]bqvHbñ/;l~Sy*']LK12Ȣ}*Weu.P/jmN/v DB{^~~?/}`҄q$&$ﲺxuHD;LIPn  RU#GjPQQIɈ,S?vً3?>ɼ1iyts{flۋU I&6_˝lwzT=Ov]=*]_{(KʴLZ+6>QWTj4DzR\S8!"h1w +F .;uJQK L$al.m'f$S>P9%fRw;vnGOݽJm T; CG$#=˗=sFe~=N}Cq /^ϞfGȑtJGG'N ( %<, ՀDA~i(BkkGؘ`yNSPwAЂp8ܽP.56b0=szp$cRS|x8v$ X)1 :NG}%q\>rE۰ fcVWװu8Su.D֭H!\x`6ļ[̠L._¯~;&%<+T3g)Ԕ b3zA.tt'O0xp ϏNCLtZHHDFFttvQMXX׭(R ~sON::: C$~?-X,\rNd2 Zm6mzݫC;PaDDXĒ<NGdDzӁeVaР~˺D,Z Zm;vttvr<;8rմ#GHGՠф }zAՐ=8+xfZ 5\.[Z4af0$޺'Nz!3#LyOz=aaDFFRCyؾs7rsыPAKn{`NN6NTTTJRRϜ|V+vX m8N22Xp>,g>?d4prUULgg'p:=[}sg[رk7/yY̮={!==GD^ϤIawXj _8pU٧rrjj̱'x<:|ѣJY|%zx>IKME1~hVZ9$o8vzd4ktxޛ΋doh4ZrZClJLF#]6[𹼼\?ɟ_xYhnne08+vNZjMc9l~{;$IX",瑜HsK+cnj"NrR 곍AtGG}/9x06!C X|JJh4$&$(+h`0tuؽw/UU(`P=1qXVZ/ӧjAg̨2V\ Ç*WE2udV^9}ڝ4'(|_ 9)G~˗jILH Q._@tt4vO1b3̌ :VCpg"-?LLt4)'0xpnH.''+3AXh(3O#))Vy:OiX(-۝*?/Kx8mmȲLZ Q ͙0c2a}eiq*f{ Ȅާn 3*}E1p{/0OY{aL<ײч % ,#ʵ%)SI1fT'<< wG#oǍeͼR HKMnviok#66Ʀ&"#"|ƌFrL<ӦR[Wǟ_+%#5$w|\qq7defb&餳zӱK8THKx)3#=oxAY=LOK l6s9&ܜUy:FG;( ))M_Ab8P%*ZԄ$ju8!3\'@ KWs v{CA .ht7ۆ>m>2bJPFEB 9rEYK [b=@I6u ӦN@.G\\;w!%%ΎN F3M5 GsnJJ/+-YT:;5d h &K$-57ZNfF:(q L&:;;y7Hqi^yuM>b } z^?>Յ=RaN{R$x&/*luo(’ ݄n2 y/tAtUnO%5DWݼ_59* ACs{j 3N:{R uX=N!UxO d費]cBln15HȲLϡwYWlÞS'U5 $/Vv9{;wc-"z*.lv;1QtttBU9F .:.8NrB҂^'-5PTU\.7K8Y7x<.҂N#=-gďgnUj ` BH%,Y6lI6=!eK(JWm&Yw62C)6vt> ݙ3̜UUJ*+0L`9|fQ\\BCc#f4LF#LiY~XbcbecOt:AђBXC<" ܶD(i}Î#g?6?,_|>v.bXo/xא52Ѹ=dsy(7ohnnfy5Gۗ.m ztZ-55̟7־YAersRRRm۱Z,2~ΙEcS[Nm]7,^DRb?9}*,BLL4_zWAKBEaܷj9Y9zGqcdiWswrٴ]>hoCsY|YZbc;f4iz8ɶ_VTUX|)465p|7ZMm4=k9hZ^yu͝)..O~sfq%Z]ܷjV r h4L:ܜlƣ_An["z*udc03z$ZmA7vLpQ ׋F^~Z|mm\aaaC1s4L&#&k{LnnNW'OqZMmg6l®"I˖j7FC\\,>  >hz}/V~$I"ֶn5 ճiV#p⟻/̐tV ˗.g]ƛAlL  INN ,c6ov[̓KFzR_n瞕Xx &:WI$An{It̳o|^{oĞ I)Y~#I8y .KK/sOmߴT֮@LtϞ}xKTT$gx83\.,'))-cm623 466;f4CssxOw-Z-G!';EE߰͆aXnƎ* [s"+3kٷGyVpIY3p:ijjf9=vc2g:,s11XfϛéSu]9..(bBܳr9 Xx! ]<ܜlF@vV&+MAev; =4vhL&%ehd  X"##Y`>gϟL2#?$>S {|gE8 2O=)yߩN+ҍLnw_#< 30}n( UvxIpFD]"9MG Fzu^l,M\t!&]. `V?c?N51"\s!\pFCbb~hmu1aX/]jILJ `0ȉSټy+551{WwuĉSuz}3_!-q׼F!**:p7WB s[IHKIfEՀn|u?vw,jQ v{M!&^<ʗw6(}^R yn'1~1<[R|FĐcLjs 9xݳdk#/ 7t:O8y4UٻE [ֱY|YqKJJzSk ?O\. NǔIw0v6Lp|w$;Ww.Vla 'f]AB|<>4tZ- {GMll =  P IDATjZ-$%N gٲu?Ӧ2yD?_x4&LAo ywvv}!I,Y!huZ3s/=r}G@AaX҂ʥ&ܺ pb2vBrrU_7,zwSAKDGE(]yE%FGvNbI   # pé $u'Rot7qcxu\:ž|LI pm = !6-D5C lcu6Ew\ }F&lj|U{^:碠%o^:YNUf\WR/[}TŤQP :1 Reƴ[ye9cڀ?z88W> TЅb0kϫMEn_vhET`RaM'dDZ-WW\En)eOycV_rQ,"xP qw$ /6x挋qez( />Ÿh>kۯ߰u n[ƪXnEYȳeSJ0hK :0)q^fVFn^[ ׽݊*6mW}^Ǩ#xŗXpqqy/o_Dk4`]aߥVfSS$Ih4yY_vȴqV5y.j:@E [{ysJ7Gnb0܌C p:h4ZZ[ -f6$QW_^*6YiiiElwkimh4@$52^ LYy%gΝ3|@A[uoSVV$ˌ=sgc29p0[m' tr1^O}9{nchn6#_䩟ʪ*^~uZ,t`߁C|^/]ERR"Iss3mm~2Yr˗ ˏ~~bR.\˯AvV&ӧNp=+Wa1ӧQU]%;+Y(-+ƘѣXnyo\.,INJ;=FIUu ~Çq\nm"p]d+*x长vZm9zϯ`KDDZAbBLeU5/}uuf,.4 7o={ys0$#/}ajwGQRSEz6my]~R ͂sp"Ϝ'N%ddqVvzGinj_?Fɿ~qŗ&&}ϝ247>ˆMywm fm!֬]2y\+_"ܜlJ&{N!<,kS&aX0Lt:. Z,L72"/N1rp:'HKMA1} =D~?"hhj쬮cL:LR%v@3446LZj* X|)ʞ(.-E5FӧNj`6mL< ՊbAio+*t|>jX,f,3f6@( D( dP(# ӵt2 444ۖVa|G %3sH׭ MM(I|>_~ZF}n( Zk?]G=%A{YǿM IjEGd!Ǝյ8,#I:L,{%ςysر㜿xݭ .X`1ќ<}N`Zhll$:*EkƎKYC v_E :ݎ*hdM6z>AAP;h4 INLD3"o(ZKްw߶$IbȲĚuEmgϚhimDF8HOc߁deL[[˗.Xz cĈr19t:./7hzmȲ 4%**}$IîaysXZfNFAeKK8p0YC2G$Ztje r}uV=7OAP,,7Æ 8rf/$&&IXNvg347˅}p.)'Ǚ爋U+s8Zܹ`ȱDDD0sz{ۜƏKӉ`=v9= k=vܜlk:e2Y=݂s{͚̎9@FzS'O̙sz^` ysf햸wL&RS1m*ΜAr]zLx^3gFRb,p8Xx|8#Ljf)̜1 o&ށ[ıcFhyw ǎn3{t&NQ#j0w,8ALL' %`ez߁E'QwAot{R^_tr碘iqzR'%O5%565_ܨGB|g'PJMss IW}=PRRJްkCCc#ee$i˻TWO|JiYO|GHIIG{1rxOb<׈p:?n] E^|AV `RUQ拹k{PTol?n+įa3.̰0ՂOeOPee/3]W v%A^:C-D[ Ol#= duq帋@S lݶB"""x}?^:ovȟ^~Ew. !SpW&f˖ܶy'4}JdIEՀn4L3.|JSf-,a`4ȟεvqsjda 1'Eo$$3&BW?m:x0)|{^^]Ʀ&~_>ukuuX;Vf ߭0xozn>^mlo] UUQUǃw)8$}sGBߏz7z͆Fc؍(ڵ7,cZf8\rQa0ۓ1n ` ҲrTU%1>ԔoG'';^ٷ)&^~V}jUjxVSsh0`}?rz:Ϻ;%I$&wnc GcC8vdIԩӌ5Is~νK"AA7p1ƎŸc$'OqRZ444?B~~9=h4Z#FMm-7oܜ]N(w; gKظe+㷾9Yx! ykhmu1zH.[AAZԄcL&F#(r܄n_ lܴ2\.n6c_ ^k}C0uZZ[Cˏ~=͙|S&fvAAŀP(j:$w=IINs={{9w<bcc9tOdY2fH b18p)))R~>Cq=@AaPPh4$]cS`Je:߬ RUhmiR~Ο)'RuH{tr%ZځKAAˀHxwvjjj鈏Cm1bxϲ[F#F0lh.w-}P֐!|oMMwU 47Б#L0-墵  `2E 6E OBQFɒE s8hiil6qV/af(qf\a+/`ڴ~ts=RXdZ O~KǍ%.. [ #=wDZj 3OAݤTUdbƊ1Tx鼇u}Q03NDz4za)fWH3JQ䤼Rp@yY3g\1#(\B"kQT\BlLLzWsxSXTL|\lTok`w kxu& !|^3gL:B!^yuMBJrZm+*غm;?x(a͋^gZ1Sf⍋.L5#ˢB0|T3Oגi/] 6q.^g& =/_.kf_v>"nßsZ~3\k;GtTԀJi4hnn߄TU(Es@uzwhŘ z.B6x:}1ץi5GZ_s [6QSzuׅnnh-`¼ja4RB(.ldIP] A}t:~{ ϪBAq. Y#hXw͑ϱ'[HkQUuCn!AAktC@*.\ߤĄxG_f"#?>cnz&xs^.PSS˺w6xoh^!]I}>;w{;v1}*މjj5[EQx{ilh7VSRRС|_ĩӼkDF8)k{Hff>ybb9r׬!=o~>s^=;࿈  \"z( o}i'1ilyw@QBLlL ˓HotL+8|(6{W ,,gI3cTŇ{p_?<5L&#o['ڑCҲ2:`݋ s8x7oc={IQ[w97d2af6myG<˄?w%%]0UU%oP/r^"AnbI+UU)++g8v.Ǚ^:e2QQTVVLbB q}C׬%q-((divΙcUsiiE߰7򩪺"]KHQXrYJӂ˗=s:vwرJ87',4 0RSذi+|%_HaQǍ?55D8 s0},3GS  \"^%lz{NY(þOMm-ee]h5:si4R?v wLÔR^Vޭ]mWt:U5lܼpG ';էfLcE c'8ulHzb p;+Yf ko\|uipqQ[ߗC0~lEs2gۃA0W_g%9nšY~bNM.2M%T]",tt=1yn!x.Ng8S&M Hp"##Dn$Ib<him%OUHLL780 s }Nz^q{<(d6  00 ~?5՘L&RSk1 t:I*e$''QS[jd4o#"*,Ess3'5o R^'ڊ$In=^0N'vihh$pcۑ$b, --h50x<444  p In:BJ͆P..AA[̀6 ]Y#hu|h4*@rGѢ騬"`Y;@EAedYֆ^`0tA**+ جVq:x^+*0u:eFqI qqmvn7%%zh0驪nnsݎdՅn (EYyUX,fRSEO z?h%%ect86 DUJH` ZcUÜd+^`nbO (4 Zjjk阍CE8K`S__E,hnnpru4+\.VY1hdUU5Z--3uLFbbch4x1߱ŋA8y 3O֦[Y@x<<̬?tz5Ba,$YFӱE1[n< ~z2xe_kh$ؾ$`0pՉLzi2zh9`Y#~UUUaht:"j4bc)/d,݂P(Dmm-eeevL&Ȉ+*(/b1pp8QT\L( 7t8|}z1\TTV* yY(|{o@7VffM"An  %IhJ ۹$GUUt:=NҺEFD`: 1.6Ih4$$ m6F@mgYnDFFb{鈋%DQZmk#G~Z+GYJUu5/+/ ĩ & i˻d~D(b[I$:* h%.\Ĩ9y4oPOwho`y̟7{YJJJyG?w< <⟩oh`ܳb+x9{FsŇW`jC+%[s_\˯Nt/f0~XV,LfNv~N.ɳϿ??FƦ&lV+ee:s>×t:pؾc'.pEK_Xb'#?<ˊew3f(?뽢 fO|6nފ\.,bm9i뼿s7 g#G ȱx}>~_|mW8Voh$oϱ'ynf:l6+wL3Ͽ7e2 a}AD' |Zhy,ő0ۓ3lpVXc%xo撟_  chllF$+/ =5!$''1oN>Kysq:(+?pOQIU1  }PiԾ{ŗ5WO@Mm-.\d9RVVbjh):WԵ ֆƍ}\<6owxq>~!$$3i>wJrrpݼq3e ud4I^kϹ|>~r<țkޢc. BB$I&%9w7lZťhmm%xKFqI)66udʎ]IIN1cGqq /RV^vw7,wuĄ%2$=2u[dgf2gL@Wt9Z1F 455=Tt,'3s˖,&7'oq9̙56֮ga[ZWJl47{kZZ Q#GE$ֱrs5"ӧRWWO[[+?AaDvPW,W^cmL7+?n,&6n?֖V6lBJhΞEqq YYCn ?&?ɇ0v~˻ x)ysf]t (*O&pW-hhțOeREe}Ua:kwE-A^(VSF~>J#_g/^":*Y @{(BuM-sfͼ!Qwہ(1yhj!))N{RU^/C2n7 sT` Ma 'v*~iQ¬pLYܘu~12wpM'nqt:gMrY;{&U55Xf2Ј]ll ,SUUgkDćn72pWS^\~u 2>kǤ7m2&ҝ&^bqsuZܭVIIktnu"؎7F!#=u$TRoq;Y":*r@%dYZX#$ ^fRU5W9q.ĕXAa   #@AAAF  AA XNQ .S\Ro eRUU*[Fk%V,J2eΊ6.5o2tjG#TUrK*x=J3aԉ9[2p].*|ۯ,Iԝ((!F.RMtI&4/-mo;(oR11F$CAe\L:;.-*ãLr_.6xy/s2~f`4ŪHH*ϸWM{w׼# aXn@n]5uDEE2lh.ZmߗUU$N-*YtPTⱉBoVbD>hrt&gL50*L٫H6KFGj=wրJkZ%>6pn4t6!&N??M|>6mw6c_EUU+*Fӑn#(TUVQYU@Jr2QJuM ȲLRb"N$I9븝?xqh4 Ҳr i8ᨪJEE%UU}In( UUTV%990Gqq ȈzSy1mumEQ.3wάOJUUF>@;d fߜt2fҍu* |TUU -5ZN8dkkc;/( 2]x|\ Fss ;v$sfNFCCo^Ccc`J~R"MUUXa9K,7!?\_qI)K]CcFwq]wǿx23;8s4e\mӼv]0̔c̶LAli0ƒeASy?̽3wg~sw|'xeXV9Ftt$&+3`0Sh{Aw+øn&O)*,#F] vǹhhh2sTF,pys{m;?}446P@rRؘ"##^TZQn3rsDGG` !! ^[.?yvލfc[c펠( ﾿%9[R={dQ,7>t[ZXn=11,](>Ň[tbXn 466íLk O<0-߸G5.Q^Y ăKffUUlCF IG:}VO`1cTNķ54M}|>ZJYd1Os'NafM¨Q#ٺm;U5(ΏwcEaݨdMM͔nF.E5C b j->`0Hvv&lJlq74^}`inN6gϖz:LF#111̜13afkjuY`>(J.f3Nv8 ~6"_ʄNBPQhBוݸݫjPKc[{;WK[[;( vG% )4XA 2$Vx\cd@ J[ ._9y4 Rao#--CTTdr}>_ AEwVCp{ص{{p\* pII̞9#l61kt4Z )),?s 6t0%d1TTV͐/Ԕ-Gd9KÉb>Qc0 ?w6gKJ * } feep/}$ǓB\l,?9ȨٸyK<ٙ~]ȈN>lFR1vhn߁Nez$5.*(`G1{ ]HvV&fQ#3i8 J}}=Μ-eذL0W&U"A>QDGG]>H&;;+83#̌PTX9YIOO#==qqLvW!ו$I$$$t2{֥‚ e{/ͷ,[k֮5%';,&MWt:~x^Ga0X`. 6</99WARQwںmǧ AqL4Çp%//ϣsiFz;v⃏ z=Orr?woX[V>choo~ 钎{1vdԔId__|Lҿ$115u26n/Fll K/ &%o'ͥ5A[z􁵶r:K\XFlL4}--^u磼"<{4( O|Y|%Bή@ ( 766oK~{>4)))HnZH(*n~jux1Yu||廨j/W$$)TfyχKD wuz(u>ߵT*Ux{0z>ΉyEw'h4F Jp?ٳ0߷ _@[+Yd |A-KV[ݯ35QX }BqE-SIv4#d[N-g`_u{Uu z@EVKWՒʒ|]8C]h_,+m:t-zu},IUi˶$IjPuuW{͡{E]F)>3g9rUER-=fdd GʼS ެw+`˝LǨ[cOBu: V"Zdy'QV9Le@ #hSy>1&E 0wD^1Z:d0*3P]v,r$I$%&-t%x ȶJ'>Ы7]#t0%Q |`eFjj3|a|]:FT;`w{:zߨeVNbDklިp׷pY vYQ"p E e8;{H-nO{/j4q ,-T}v 2VBIh4b />PA#@ IDATAA;AA0"AAÈPA# pAk.QWg'3LB":*T¤ EQh9hWHU@Q8Z}܋YVjdY0( A5I$jъnpUUWs 'pJJ_NTsN;?m^V8M{WegynkZTW܋]4{VwP {K<>h- \w5DX=eElI)%%dfg;E8dzL~[G۽~O+\b9.>fRvª Plw9,;4{8`AoViaAJLs w4h1o;?t1o״vEb\r@ lF;Ļ$q|>vPP$h;Y{%"Ep _bF %͏FkuDA4j5Vo㰽%=t=n%[dيwe)7wM|]mx^u)_$RP$I >zO5T.߾u:Ľx˺an'))FO][(ɦqcGs!.\H|KO8{J^^M,?[>h2RRr„qcHJJ$ rJjJ ӦNAsY<Æoo-#3#3gJ0L2D(>G ?/YYnA2mmI |)$ 4 DQ()-ѣ?~ʪ*<O$vC\..\'9xp?-Z^œEQذy HIJb|}"Vm6\TvoL}}IY-n`jhiia4º$';'ei^#G+8u o-[Zd4v.^,kRRHJJdbmKA67@Ų2bcbIJJ"DKK+6[\x^0DeT*U9$ % }~ sAiIpݴ4zQ=9770jnɲL0$ B,:HԣnVh4>Y WJ,H:g]w;Q{?$&$Lj!55\Ə㸦fW!:: /7iSK^OVV&> *DEFU̚9L&^bcctcx1;{,z:$++XfrzL8_0AAP@ Zr<^L\n7DX#pQZRʈ9~ǃJIHH ..%8N;N^^.] I\ծJ z=^GjZVMR|>?:&NOSS3--o'hwlq۩`XHMMAQsZEd6v۵(* &:JEB iy?@+*ĄnPTX-%]nmSp8n:]QTlqn-Ql6eY# wz@EE%jV5ŠdB$***ft[dYErr +VK~~mm\jPTTȅ :dU(J.4TLzz:*v DGEOSS3W-b$'̌ t:UדLuu5iDEEp8n˄$c:d{'zVq=` `}Ш5-)%+3wV{HOHKk Gk_}Dv|u7Y~# p݋Ovj rRzJdd$1+C%˨UU_`0Ȩ#lx^RSSۛj4nǼO655ҫHKOeС̛3^yAODD>_(}ArR?9x~xDAnnjᙑ@ZZ[DRzHkXM eBݻEj(I:X.HtTpN瘽$I B"11!$$:nƎEVF:̡G7v49Yx}?}gjvT ?oEX,j511z:_zq[ZZ,tǿ3JәM+8qF{~˼XVNcvFAӅ'455aZ{-ݞ799sϓ@ss3.s /Mhin ɄDoŲE nÏZIj4݂n^OJJr;v~V+i=ITG RSSZ-=cc;3ņ鴷LQLL4/\jۺ )=dw!KfRT 3 ~2A yV]S?F4)oQwՐ;I-'*Sf8lm3"ZIo1KcSP/@B|<`b6Rj4DEF]>JELLt-.VCrRY%ZmnU[\ݞ+*,`0 wYdZӅm6>?uv;z} 7Q: m6[2lq 'IIvj.]l2qLj. lηiw}zqcCG|}232zt; 7WNN6>#G~ 2dpPIdw9_ﺪQz5Iwg@KӳʳFhT2cԌ`Wq6^@QPv%褦mnI9B~^ YFLQFȥK6/\,#6&z@--^u磼 u5EQ|x~jh4.ՙT*t:m9A x~@Ijjq/^AQ~?^.^Z "\[ϖ`a0\=AsrTLrr%'7A痩Xe峓$ Z}YdYQ$Ih4JvS>ErCfgeN_  nJx#[:gOA!DDE  w  aD( pSAEֶ `2cwP_b[_`ȐiQaԈkUPQh>$%*d$LQZ ܋ZEQܫp#Pn#>t:x)"wXX_\^opɴXmt'lЎJQk tR9i2"/ev }Z~v6-S BD(t}5|ϜOj]RXk&\+1 :ߤ`ⓧ8q "X۫;( $#o뾳Rtl(I =Μ9K}} :FCPQ(/ Zz$Gۈ%FFӴa12x~'^ٳt:q$ DEFRTT` ݎ㡵rstMA"zvb8|(׿wnUWpQ͹59c55ZFVAB"nJ(\v)T\b9=mwrD.Gg@KXn=N\Nǎ}Khoo܂EQ`9v[\,v3eD***yZL&#w~]Ԕd~󉎊vEbj2i". E)*++b'ٶ}))8x{}=#GY~#cnjFe9eA,"zعx,(#0dpyL:qǍeͺ[74`lrٰi F{/@v5uƌxwMj姟 ;+ⓧ>?rpΟ}:͝;O#h!rx_{)&rQbcXh:{lwJb$%%t_{"Ǝ\jMZ, mDEEIm]] @]PmIYgΖN|u;6ms77F׎$IDSRrLR /6?[OpהO0$66K4p8_;2F#ல:PRKq)guֿO$x>Ǐ>O?ҟMuu XV֭^} [ϯ?!" h(>yÙ%bԝucAkh@-`:;Njo@VNA~fTl1yD Sƫٺ}VWCZjƎF$Enc|sdYf)^ZZ[v2)):t|Q^s1L,Z0v7^F|aCsDђKPг{>ΖĈ1Bw$22sg3cTV]G\l,M݂ρhX-k̞5 f͜>d}(b!+3Ç5[W0r[Ǟ9vn;c%>ƶ;ySw;flشCGR__Ϭ?Ab@W!jor Ǝ.۰i3G 3u~3LbcQGlJz:e2 񴵵ϡG7v4Q[e_$dYll6}KQx,^ŋH愮7,\t DnN6ڎoS&O 灛:y2xPT?wa2j|tbu7w6QFxakj0uƌnGV$tbAׇc}ݮc?DI9 6F CB܌$I 2ܜ,Fbu4ycZ7 u? ‚t=#b{ϏbW0zdEg(ȍS nUzghmmjC ;ǃ;,['I&%9[--X.'OSOy?2|P~?=-ĄxN:{VS_Xx!Z_IKKeȲ̼9w3u ))F|۹ɺxb"٢h+>E&=Ȧ*ꜽ;>^TEpvBVր?q/<7BAC}s+^ hj#Y|kthrDx sv3_zfWSeH﵀Rʍ ]#IT*61z٩/@@$JW* {d Y9R`}K 0иn, ҹ`CCj- %n78rz-+ rR-mmLFQմa0M$'%vŭ( n:{NmȲLyE:f$I".6H$Irq׃,]|VaZ:mfgp`ף( Q҂ 66StQRzDsK ĄxZ-m457|$IAkjho$$ģjikkEQp:]9[BIi)'OQ,& |J"_ A $';sl2k^>޽VJ#˽Uz<+}Jhh0vpB/Yy<^>޽CGBݻ,~?,I\nm.@AS0 l2Q_PVVNNN6w%)e}ソcF1qxz&3HCaA>:Ob-=΢dG|1;+z9z V]Odd$&$`a00M̛3l\.O_}\^$3zpƏKSZ #ODD+{}?aⅼj&M9u4֬cرDG}'GYJAA00U5;ٌ`l2F0m¸,l9c4HXxuv;.ǤŶ;9Q|J!-59} z=jχ񐛓Veq|{ */rQWW1 ŒJB1{ x@0^*̸c0 Tċ/1%';/1 T˯@04~+3#~g @P:;>v^/zI1ILL߲lqqWAakxGHOO Wh:i<1|~|]]Lj4j5 uo/#.6~9|^?( MeTPR9} ŋo~ǔIQkx^<55ᲃBEE%a1jhh@}}CxƸud4T;snA ;fZC0ZMjj ZÇ{ʤde-?w{Æ3Vk0͜--ǎ8.&&t6md'ZE zOSAZZZCZj {ҥh[Z!jk8wbc;6NJw#5%ERR"$̑#YvjǻP@0`՚SZzcF'&͆M[2iVkEA/"z%2/]@A~,^@q)1|8=?ÇqbY; RRZ-h4Z޳ⓧژ;{v{=3O#&:ݳfp1dܘ:X#.BӦNfd22o?p6/OSs a߾IJL!1!ٳei4j&NSQT<d1ktN9J@N0~ܘpkAEDFXiw8Xp>9L?@0@uu 3 ZMfz:%\DNDYy9o)Un52LAߓV,Zz-[ G{"rXb`V([#Qkk+.䤤Q'Spؘh  p8hii%cݕ:ӟ TP_Ųrxoj'>UG2oR|J7V7ڋ.>5cm*)PHbTf3HVdt/v^ 7_ /lǦSXewmZOa99V4 孭˧jDsTN0 ;}[ ꟓU5uPIh֎bEu? ז(ܵ ))|_]{^w Əsjyc!hfZ>?Kx ]խ N<9w ^,P/ |g5Y#ب 3tbE.*$jCm8x&#q+' ؀ZEK8dY&::[\,:կ))y?^ro'B D0rrh4b0D7Et:]C`2Dr`Ás?I0Lt:q/^AQ<N>Z-fYLZ{ `KK |H}C,hZƍÐK'EeL&&fW K$t:XOse"#?Ib؀:;.\fQsR/3_" rqvރV%::TƎo'ss9v&s琖#:|@ 13zT-xϞ%.6ӧr}  mn@Zũ3gp8hu:232HLH`}@(mcȐq+By>he̞5ZCrӧ1q8<ӧϾ}TVU1wdefPRZz^  g@-ii515h|}87P$ Cep.v$o>/n-Lm|Nl25Zuh#r }$;~zٻ#+*4p u=n?*P~~9BBL「j8bY7hGi=[HOB~zʪjb2Yleℎ} rs⍥)&t9ٰaNS8g˶w7$PKs1Ǝ(2}S/-3OrQ4h?+9tӦNfɲ7 DZ}Nm._{=q8:_Sd]dedt2H,LANiFMmcFj2m$V}3Q#HL /.o ;;t$`;|wK8p̝^_ZV׿el6&煗óZ5:|F$I& \MYt9>'O1o,L&gϞd2ȃп__jk)'--M΢6`08ټ,v_OڜNkjl@8_Պtٚ>}e"Iu@y{6Ν?" a|hZx``Ԉ7&6Z/Y`d3$gB23 l6 B 4PHҞjR#Y#ٳ#ǎ3v(bbbزBfшnuiSMNv6}! %) $V+}s3uu@$0Jϳe6 ނ.47yMɐb>y}RvC0qcyWٷ/5Cr" rqΞ;@]]=zNS)|ƈx>zp: 'λeeeׯ0Lq!+a ٷ/:--W?#?s?O~]h+ /bP$I;o`-[wЯORSR"m2s]l.:Ih ,lbpRaJdge )d̝3Uױq 8 RB0/7{aw8pw `f,_8\yn֮[ORb"AZj̞5ְq ߏgN ';RfeXF5찝'9R4p >$'_3r=zҢAm4xC% .heL͝lHSusRp'O&1!ǢK>l('Mx_} -+J Z1뻾h*kt6]P]b#3&/;"U$_ȷKLϱF !罘Bە;C'l$Y(s6~V'6dhjK.0xCK! =ĘTn'J@Ie^,^F=FN `D?11"ܜvfu:bN1j$ŢcH/8Q+L:x03*DM3zJi&I|"-2 DrLF#q"pB]chns$S%:q6Xo{,1;ʸd.U‡l"ő z0Aiz=v=zCב$ A1z8%IKIv IvqY$ Q=qrے$ ,k%%z#O  B/c=b$drjn҈:+ AVizx}>`$mgn#G 7Sq"^/׋V+q746AA[[k^wd=465!2}r״)Wٿw^y^oc9 cXQA3 kjعk7,<|>?'O"}NZZXl}2Cinnf1dp1 QV^477Fq f3AHJN l{ܜl^gm$&&RV^NBñ'$>Aϟ-@{d 1@0HYYe(JaA} عkOd-M8q$K1 "==@ (70dpid IDATii7AAaݺ,xbb9#2>riЈi>s 6 8w<~E l{#߰ @-[y B?k/fM(NS^DZ'(+rF#CKSٽg@Ŧ|i)z{ӅQ46OYy9K  RZVx.AAY5 Yn=6u Fg2oH Ɠdbs3ßgn8gUaA>sf$&&w9~,k׭g֬DUU5{ 99Y3g$Iܵ}P2$dxڔ Be2v(ƍC}}|>޲$rsIOKCQoSg7gU(I3Enn. Br(2n<AT#e>ϊA}4M̩-;_d%'V\Pxᴇ= !ܠa`N8#TMPSWpIf c7<=I@4NTUs3m^: / ?@m]>Ip8qq+jj[X` 33#O~&8H[[n'RWo> ̛` 55xb31O=a5=~Ӆ\I((G$b ChFq .67쿞' p}MLaq4M!+SM6Vmvb3I.*ƒN^9"t<9iv( O]CmjLH~xjY*N;TKljsʲ.ϓ)LA%8>X򳑱"1yҭwvSU]MfFV̌t23q\Nz|>fs^g}2ں:~߿|ϒ$CFKr!FιRZ/˖WPQYS_2 }s}KɑcHIIkObB5o8|(~MӮo[$hoR1mϪk;zTws( 77ͷC^~ٛ*ve=]4ͤZy&eiF=J ;כit;\UΏc]8Brue7SfE6Ź(I0-DV6b`e%8ܬryU=#ӭ#j =/w"$i14,#G gє ̎8tl2wݼtG#R"9,f+V`sN̒o4Qhimq --ǎxRdY⢢}۾# Z[Zٰ-B@Q.n/! zM?^B( uFt UX^3`i}}jBf Mt^g9`YvME)_(|&$3{L.\hc2HMI!))|iGϛKiY)B|qc`6).DA~~߂|/ۇ|Vbcb<>/`6IKKeڔ\lnd2۝C,":9AU@^OZj*i|>--dȐwClL.N d}$I\qѠk>n2(ZørcȲL^^.yyr6oنncޜٌ=^3gyWoh`a8t]* h1)<>N<ŢKp%O| cqQV'?1X-[IL'(3gƎaݬYzxK_ -5>bq p[3BH:]dihh\$I<םE:SPOKK+VpQAj}mO4^z5rIJJ" z#3% +~d&M3^Hד=&~sf)NAnbBwaC4_l!99AOff:L}C82;NysHOKEopB#6ʖmR_%:g|V};U5:r=VTU"1Ʀc772y"^0;y4462tQSSuf<A3B$Iܘ3iA .6{KA~$C+yVl>Yp߽X7 x`},zc)onQ#@^ƏOx2Fϳ=(Lx'} HMM$HJJ$GŮ={ٵg/͟ysXp|\ 2sf~ ttcЉ:4qL2,uy i=912P_M@TRsKU\[[^WnҲrX,QۺnZ[:K: RQQIqbs3II}'vuqΝ/%-5 xc2Mkgi2?߂1JURpm/w OFIDS6~6H!6FAQ23QgƲR0({2gN`;qK`< ?KH%08y ))Vl2wun1(0ɪ5kqܴ9<?kj;ޜHʜqi\!';|oGgmgucjERBLHՋf%] ?xG]T^t#o^Yn%.hQ\1'MjF't8ȼٳp\lVhmmc3jvS9~ t$' h91z-RtXȈ`7\Fε%JUH$vf~˶NGVlE 1;JY6U:"jR4"n` ;;c?8>K'Iдz:>=X!'q M'Iȉ1$Ięt8oS"C!8\q\[AAz  2"AAeD( |! w\nZZ깳gEr pUckm= ABRFE2sT M4MƣO[@> @4[DZ(%Q)z&EmU8b]2p%oj=I>4Mj\.,HZZf1e.ɾv)TUUS[W"fsdLϜrqîh22m턏i7=VUy>x;s1%@b˶ oWX[1'"v:bHJ kg /GE7X.u;ڃQt\oq`Cڑ ^vWĈ[墼TF bzN ǃ墵 /U]UUn7mmN\.7PUUq:p:* p=8N|>5J ZZZx<6}>NI 0xq:xmCQnO׋UA<OtlinEl+2`0~ߛ[NQoz=WR)72`hv[O|u+4PYUd"!>XrsNeܜ n󥤦Oa!Ylkm#++Fmm _5Ŋj7cGQ4!HINj ta2!=#xG<'2o4l6S[[GccUk&99L&kx"GUUU$Ǔ㡬$>`Ote+VѷǢ7fzϛLAAźu磬Z>fY /66L|^[#оEo0GQf3oh}횃Iۇ%CzTU5彵))TVVQ[WGeEMYˑEMm-466R]]~jzݱ'jhFAll,--$qi8xpAg d"%%6gAOzz:11vrsrxO vhh%6&|t#ǃ Ҹ܌l2p;N)Nb2mhlj$IlVd{3II@8t;f)j߄ܾ}łnrc0hj$N'!!EfXU-|55ZqqȲnH'p n{}&cziSQd9<,pf3sJ4ڜG.;FAn7 eYሻbsu?:i3L]rJ|Ivv#~z=il6 uk JDBB V^OJr2$'wOCJr ˵Tb691(ܰ/Μ=ǜ3)++g=W?vun2oWyxN4H֋?e^9&=EQ_eXl蚉{Q)vytDPglIӻ$fWϺ5E#}*SR4ep -…&ϰ!a,NhÀY8uV'x3Cet[`eOfE:AYψ\S~62ňIqɍ?ue ,1>̀$Qr9y6xiu]αn_zXaaCK:<Ft: F9(JYYygwo&Irm(9V:.Txlbp>jJ0It\$I̳(q.ަD(‡B$dYz7Rbz1uc$IV*s\wNA  B/#@AA^F  AAzq 8E8d%eȌM5"pSZXӍ$Z]חSTJJԶz$ĹxMӨp)nS1Y&L^"@A>p["/F&ubbUI;gJ!tj^91"Ue8Rb@CMkTvx43%&q.WYyMA%+*kLGĹ؃P蔢(|?׿ŭU|~?&={syy[=$ g[23l(RׁIU"]jp9įFZ1(Z^5Wi`?đlN102Fc`ԁW^"w`@x6ST:?]W ' #77ڤp4M­5=??u IDAT Sп_[<{#T l3^fʋ]}i^/$  ZeN.,0b* ! v 7vN`D :fE zlfƾK=5|3AE`O65]xڜx O?h46^Aᣣ[_gTUP[[g18<ɏcn!EUٷo?;wf1 MM߰ RTDiY9,&3sfb~/]}$iS&3 ݷk>&Ǹ|ŗ".6ǎx,tٗ^糟~X 7'%%%P(;S~M(b"8w^gvvfA;<'%5%I#܂笠h {h7D||xyy44>?ټx <k;G1&M*p p8xEkWA> e򕤧ONv6ii|k_c<P_6xmВ!ļy[|?7o <IOKe߁^ -cZt Ft:J(iCA,p80tR$(2 \hld7\  rԔ;FHꆝcn|^`ǎ(\`& S0]|ٴy KW`0p{߯/:jE$6^`0fEut4S)rd?;  |u 8)1OB7ǎd굜=w)1l(WJ kxk;~Ǎn'?/7l*rsoޞ 7$;;Jii)6'YIJLCn6oن%ow7P][cxo}k ).oH[cXom|OC ILH:J&Ǔ_&Jڜm޳ͶwvзOii;w sYg2p:;WA> ݚt$i9{s=G_>pEwM?nH6׷Y9t, ᅲںzJxWsVx?7,5zFhb]SYj ,'%5]ݕL\lO l?fH~2ax\pUO0bæͤ$wF͟N) zs^VZf;PY]ӿ$1lh ^2wS&qyQ" 7Kʪj\Q#GL]}=U5~qq9s l6+qq +)Б#̞uUy7wwdb F#{%77[ooaȑvX6YyxχNa1 c} tTUWS_u????_a2"?'?f>)<ç~ncQ1>1<^/&c?y&OQT%|YQmh׿H4~rHq1b4×y/ s[ 9r_ߟ'x/*.%Kfv$j= D~AEm<ֿ|j&Y"jeWAE#+N\T5 E8AH;]U&%,aӭ0))~F.^l&!1c0=o<{Eƍ9𥮱cF$ILB3f,hRB cF"6&X,|ӏ ;-t$]llׅ&n{DپgŅ 9w!)1cq~j^3qhllh׋+wez>?W232CM(+ŗ^4)],nXiY9I rݴ} ]ߝ.vSYYEFzӦN|i6uUۙ3o'zu<[=ۆjq貭N F&dZD-K4MM']wLKM2!U*/.e$#,M%Q*$zb1N>CJrJ&+Ub69qKg>fLƔIś  tiB;c0 *`0||dY`0}hn  /e&eYW4 EQQoe(F>=i5k KkG'I,wȪ \?IGM$z4~\RRbF  -#   2"AAeD( ˈPA  B/#@AA^F  AAz  2"AAeD( |@MPUVCANo|뻷z  =>ZP(ě6^xFz:Æ\iksүok>*uu;C\\,#!!#  t[3^5(-+{cgΜ;:}Eo,sq\vAAE,^Jʘ:i" 8Ⱥ7ףiwϘJذ-233\\'ؿ 6eG')1öoxlOjv{Pb;ȼ9SX@Ee%KÇ iwZ̝3A Iu\Ҳr&Y3iii wI {\{  BۻȮsCCn4ف f")f1(ɶeڳ>ޕg03zyflKdxlKV`MD1sh*Tս @7Hth~Auj>yyyh#|#wPVY# Jyh| 6m~4oK/_ZZZ(  z^xElw}/ɏfU|wOc?~sڽ?V:;?zxgxꙟw^~7Ri;w*{1{ehhbĞ={h6oqݵN.%PhntZ3znz-K/`X*2::UeK;?ZͳϽ@Owle8a<}÷@{[9E2_x|<""""J4hlܴ3-ae$%ao.=l۾S>f}</\A0w^<jJz<5֞}Lxu<;曎Syw;U+O 83}jʽok>xsnr,mm= DDDDDޞHQi0 """" FPDDD(4@("""`EDDDHQi0 """" FPDDD(4@("""`ֲmonn.V*V[1 hjnʩ###d2Nf1 ^V< >~sq|w =L&C[[+{.睻0 Z'w֬^Dߊa L/~?sn8tsq?{9s\pyZNNZ۶q&|h###T*~񓟠eֵz{z~$I(ʫ{aZ ͜~iqÏ>Ǝ;K.;wϞe||/UVu'?}imk[R(Kزu+olHsS3_{ m38G=7r6nDss\}ccsqڳ9s ۶1|]z 毾5;\N?T֬^ҥ\tdy]p'(W~qm$]z / ]]]xG{{;l۾%ܱSNYCkk 6ol߾mw{\^{7x=-SﱶORZyfj< """rS|12:E=pu47û塇aʕٻ?ߢcv;}џL6C[k+W]V׊.nfS5W_Ib?}\z_+.Qkk3_o+O㙟= x \vEDD1#Ƙ,T:H\_crrQ8훘XL 09Yf`0?nV>MMha&e s>&˓9sS?}[R]w3}KaT*&b/_FE29Y&ѱ`mm6oJwW'|~c"c .(ضm;gyܛ?^ޞ^ܜܵ\6;Q@yQiLc("""`EDDDHQi0 """" FPDDD(4@("""`EDDDHQi0 """" FPDDDS]+.Խ";R;oʓ C.~?iQDDDɼ0%_?o3N`󖭴R(xqzZZZ" CV^ٷo?LN=fs80Ķ۱2|9=cx`qiXgٴi3qˊ,""""oԏA9,N=k>xaw^  ;vna{ߏk^yu7pKz{en:'Xヌ$Ihyuk|iok[."""Ҙl8뮽عk7_?rҊۿ/?/H6÷ ~plܸ%sy>xՕY Y%~wrڵє/튈4 m-| yǸ(L~TXl?~_XtݲH#ZsYˏûfT*e.BZNYsر{ ℞9M+/`R!cZ[Zf=.@K~Z -twt@HCww˗_r %V^' 1tvRTh)p%dIbddKp\nH2 VLfN=0}U՜f5LRu8|Bs3Jrr\c ### Jc<Ð$˓TU|ϧ)O;TedYyJ׿āCR q1Ilbr91!r 4d2u*d8@c dYZ[Z_={c7\w-?7n""NY[n\z IDAT.~7;v$ W_n:6mラ}W}K/~yu%Oc.sO"_ -Lwwϩa=DqĹknT*3۶ .n8 >OkڳΏA4_y\~<㜳s۶o/7xuƛxw~7 CCE˗-C7^Osߏx'?w~C\nkv˯'˗-6<v>|vů|kG#"""tOG{;KyO/=8{쥽HZ/0dՌpʚ5l߾ҁnYooo:12:+&&&Ȅ!VL\xxu篸2sqʓOg<chDDDDޕ9N`<Ӽq#|* ヌ={RVfUM,"""M?_:JBǴz8QdprZjEttS]rg|rs5{6e]<‹twpu~j}z_d_|!]^nxiJIN;9Sh[yV>9sή]y/[N6(]3G>#ϓV `=F;;;hmm!xzݻP,9GR! C:d2-Y2}L'"B>R[#fYһDODDD~0 r C.#Z -x<'  %}3Βi71si=餓hkk&-[Nv^S!AɄ\X?yzjA'"""?_(zG(\ t)1\6KSSl6K__*bm۶SV`?aQ3{NJ8t ccL?p=T*tww19r,ը0jݻwiV (JQ,EDDD-]8P(BgGANT%Yt)wd-Z)D.^$sI+k455d fL&Cc3!_ZpҊtsرJ@?===,_w@Y:;yVN>i}}Kؼe ;wsϥ$5gٲebybGF-8tKXdժ'V-[S,cppGQĶm96EDDdmx z{z瞙ܹk7lûh/`sSfΑnEDDDp}~""""r8M4@("""`EDDDHQi0 """" FPDDD(4@("""`EDDD̂((Js>K9Y\jksm?[ky F~scl||(۷xG o1t (5(`}V䖛oZs ~^Fw@SS5W|رc'6oaϞ,_ǟx;;wif8?M7x=L\a͚UM7^u1_znZ o2<2ž={u O? O=3 p5dgIDDD=c@9y7T*ʫ,[77nX*+rp0oZǦ[`0vG|onċ/gҥtvvz|- knpڳ֛믹 }U+OW71=b a{$|nHDDDDhoo_ـe˖3?s駑fg_Vrd2|?͙Ib1>r3^W)WxG,' C0P ~nf~s<‹ ._/~4#j Cꢩ)sw3l:Oc|b=<hooc||[22:cYgo_7~_q>$1JbHRw~o'zz> pD#+WZ&Kv|k{Q-`NZ1U-Ibiii)̷=l.}~>ϓdxo~0 C _yVf1Ƽ͑ }^C􍍍1Y.t`s8<H$I!9(Zy8d2J$Itp|O/#I$y}q\Lc q$ 0&ι/>鰶S:Rl|>?b1|El۶7;w&zsG<8/ fԯca7kl߱ذunk 8;We18pHtv~gmgcZpp ._΁!ƹ8ȟƘwI8cpΤ"z)qv/M$Io8aH8>TȑZnjzoe*VJ̜?|M,Ղ#]C8{@.B,4C%qL51u11S?[S-lja0=oZ{(fq7""""FHԦsu/j/i]kҀ{~U$'{Rhvϋ 5SC\˜cHt}6;ƤՎ48M@ҩ8py;u.[B("""5|F}s6mMNLL`Z[ys8ڄ89uy& anKàI,6®O:I:(a*_ikKG$qLh4@9>:j-^1vW`ИzцS-a5z .s$qB[ ֑t.."""::ґ2z?3|l;a46Nߦ[ԥӿ6vStuiWK"""r<4t4'+-(i;g-g> ҵցz+f rvjZ؂3D$!Z8lYc"""r4|Ls$%] ~=gQA$Z1F-qEQ*41}gWO@Bk{B>KV*I#ߧp-bLx~f>X,۽6N۳YmO`yy=MZZ 6f_ZȒ0yk(b$Xk<?|$#t g iki3U9SۇxfPTㄌNA,R9b7יN5^qX v.vp҂sXl$ }6 ZMzzrJ%[(3&?`m4s.=|߫Ԩ]n- Hj("""# xJh)!t-70S}=13MZ1cz\1۸L8ugF^P? DmDPDDDE^pScrt?$&J 8K-x;cf@H[$Iӳ iT<3`@c0 2!G`&#L4f, c 7(W8l2l=P! E';dm1xU&㬭ɴ#HM+vK<|Cd2w 8JcddrӒߤ2m2݀y^p˗ev;@$uL2l0 I_V/0xr= _{>QT(U*$q C2a/IBLkEDD[X '&6<㦫lOrT(ysV2 tߌ6pί5.zK}3Zj}?H_ $Rc$U y4@/n=3IDDDD x!(G}#1Bzj}* o##eP*M26>A{:4imBEuI:Iw N!K#|AZh\-:$&c/]Sc)& 3Yy|vpFvDTN*& ,yW[7{+ s[oY?0Tk<~6ajUa Ӗ.=f{A,.M Gje#MGQDijM B9p#{1P$a0]dzg!x3\!9%LVHv}o,1AHKOqCHj v}M8mceX"ɐf @""" 7ک,Q*Updx M)c v7Y71uS?CL+ۻy`C60GS.G&yη-1o3?8)8k,SvWxm`fLN 9bضoL<>na6KӦGs`i0];u{N SS֦ 2ցygCB/$I8&ɬ{=4}0{֗0"Ē$ (RRVGA[ ;'ֲ,˜bm 3=7]<s$ZbKAWA,1999u!۫_g/t& QT:K>(')47إDDDD[ܽz%(Deh"!d9,y -s8kUj#/xG{?k򰑿_[I T}MPڴWTWKx>MX}EDDd,ja 3Qb90`& 9K!k=8u&qij?=yX~Z$ZFZ1zF]qݜK.-f |fBZK.MHkODDDbMޞcXZˆJ<}uIßȬHaBXX/x>g[ 8 081fI3xA j ښZL,Ima&8|\6C>3Ib1ZȱZ#mmI?%4 CȒJxJIXa mzl93{s\.@_? J2q@lMHGx^}æ.]6p{LJT*tZس= A%%ͦLN,%iODDD"C 4Ɉc?L6 qbS*E2<mYqOַj.IECjs475qښ5K%&O)MN2Y.SV6&IyϞgDQT|dBr0$7 jW+aFWg%+ȂZ60o}z;2xG%)E7iJG'2\b",a.rR\)S*MR.FQ:mk-qn 42;7NO&>0C#~SV*eL6Lg{;455 YMEDDD Z;o -IDAT1lЖ )E4o5B3`3uZ|# b 8r,MMMvj5 hQD$!N8MPzش#qjf | mL>#B?Yl'd `=X1fV|#AMUt<0>C%C9 f}|PDDDl9 =N.Q%0j5Z֢j!$i'mUq-t9b3>asBR(>c>oG!ODDD~-ZL5TZ;T :1#B1E#^CwÞ+JH4yYE~hhsoc csCG(b=OtM70]wm۶#x0Ɛ7ե?iHO9TVZ5d1c'"""Hu 1L&XwZKhj\DDݪE$IR:z^,^S(xsF@i[%qB.%|CoojBSDDD!]^7WQP:S[Oq*("""`EDDDH;k"ߎ$Ip׉{Ub-ٷsVY&K%vލg4Hne%aL&$v٣W0,C.y3<2zX7(R.={e~rApD9(\FwEDDޫ1r903#EDDDK"""" FPDDD(4@pr)IENDB`gammaray-2.11.3/docs/manual/images/gammaray-metaobject-browser.png000066400000000000000000004365671413310443000251640ustar00rootroot00000000000000PNG  IHDR ) pHYs+ IDATxwWof7]U] oHƒ @!9nfwgfgnO: EH!^[77;C‘AmLw4E4 D0̬|~=w?Quu´2k&:a蒎!|0 AAAː$teɐ$ِ$ÐEeYZ)Suuº`AAAi5e AAAa5UW:AAA~d䫯&  pdCGT@AAA-dЯ8ituurvM LLsYun?N3<<}*Ciqx FuUUijnN4#]56Uծ0 B-gN2ysO$Fp8|H$s s|{.Y'NXAAw֏tZeç|v8v$CCCA" H:Td2I" Jdz7dX\ -gaaN;pH婷d<_&:y Q<`0DWwN4E4ԑm6l6[66 #OUU9z.~PUMN>ñ'(L4D"5FZX,FyY).+{ί{IRhiá#G?;R^5{mN[[{6D"A2c!  L~=h٨ì^v|16mB^^.mm|bq̞E,ĉS膎dl6t:y>>dTWWKB Yf<|o_;c+y\zz()."r8{鴊kIdhRCCA.AGg'EE|hc#HB}*$ bX~x"W{&VIw=Z2BUUU,]7|[0Z[rzOOo/>*++hI]ڷHdӧaPUYb%R\\DSs 'Nbʔ]HwhtP(DQTUˉ,C!<9n**q8m6r\.B)R)l6$!I(BuUtu``֎pӋ$ jkj,/rRW[bEe<998*nW^d϶;z<{R_W2^{E<$/tuwr:yF$vVS&_W輍>W o(QVZJo_?us}>,`){|}AAlWL km%0},\8d"uEclV 2)2Ť`PL H(X-L Ȥd5n;ٳf pUk֑H$Z-vTU^OM&&޾~rF+I& FAA|$IuQ:;0[,pA" f@BLi5:EX-TUU$bx<91ʦ:ee8z8CEy,$jeEp`RL躁(LT:K1o}lsZگ?r~?W_tL8]N < ˲&Slʞb}  w6a\>{oɤH:}Ōba#IɌ,m6,3f Yl#OmM&dIˍ"XV|>/f={9y`),( 2L*,KH吝 ݎI1( #2%E|>Ξk%ixۖI8y4%%ȲdܹVUA$$hko'!ǝ3gp|^N:M~~뽮Ea>dYl63\&a&:(+-Yذq35-.W%$ FaA&좡a|MSS3Vy]Yf#,;EVWݸ\.|LP>p$K/NsCq|蚎0A~6]=,ORc͍ ˅fPbAӴ躎c >   6"  m#* pAOOXrX5 3M2}MA:l6gbqRf y,*H̔ 79JAd2ɪkĜY())i7 fjj1[7mn'IR܏$>_}u=ƴS6/gE5c WX,79zEa)vݧsϜΧ7a'STTȂyso(.A+ a 5Q 08vR#[ MM-$ ;$e;)d**g_L{GHH4FAN4㧯Js ̣.Yđe~| JŘM&?Ass 'Oi`CG2{Lv;K/rX0.!K:f}0$L?דé3gHhƾ9|(fN+fΘ,z? 7>Νke//|M ?br$_ڍ̝{/^bֹcy]# :aGhmmNA<0 $b `0q4&JȲDEy5UL&;f~^t],_S;9qp!8rvo'0E16m8A;ΊLcDeϖϷƲ~>v}sc+z>8pҒb,^DEy˖ϙ3-3o8f?ǎjzMq]m M=JQAf斳<|2M(//㗿dYϱ~&t]Õxe8v>ߺg! U6}˖tòAmM5v ۍ# c1ɡ`Jjp1zzzehxN]0G϶lٱ][X,njňbRP ''͊?0hmmv*}}}#B$I;3N6 ),,d;vT*EcS3^EKY.>_.}}AgtwPRR%''ӦR[SM__Nam̟7V< A]]- Xm6EFu;>A0-gdZ0 dYfSQQN߷T*EGg[>NSs V9sfaYz-=Rä;q_}} @03oO*d]ahhfbH& Ӊj0<eY&Ni7&vxCV0Lxb855RZRB("a@OO/^ɓ0][N˽;!PQVFmM5><*+wt 2ETTÔ `X˥ ~JKK1}yy6B0GQa Y0~3p8(,*]h|?^\?c_TXPx`PKڲe˯f͞ qiy$d2J ^TM#aZ#J߇l&04c1&p8n>oTcS3} mLp 8ȑx'q:thot\'wKJ P p3ݰIa.vUn(EOkc2Fh MuO$ dYn:5&HRcu3tp8WiZF$;:lV B<#ퟸ9 PR\L[GdI'@qQUw$=k&_ N_??>VjZ<̛ǖ0 ?Ӆ/Ǟщib6[dj޿eGQUUU25M#Ng__Hu$Ix4"2x<_eimknʨ.(; +2==3{-p0b1_rm&I>߶Gz^6mœٳh41{ 2i& Vȋ'dO7nV\xX,i6}9xl6_#ʕ[(%TU%V$X,qb.ֶv6o=@yyHc6z|$+1 T*l~Fڽ?p{( Ol6F6m]բmNɞ?ON @4 F;KQt]nCe4M&t:ۻe5A኿ PW~º ٷgT*ENNx'30c4*˳_;{bfaYyG eٳN8,D1N:ĉ(--?Yؾ}'aVUvރkL>.z mm۾ۿd=0ŗD+n+ kwog7)?>j򲲫V>tMG-W }׮\[Zۮ>?%먪ʑ#&Ͻ8y4o>w'O]v7oCw 4MKc8|(o=|46]͟]~~࿃wo+dt*u?Zɳr:e2簯5kףUUj:0xW~B8~tK$I멬 /7_x\ƚ林ۘ=k&55 eÚu0 :v;׬%q|z !{[R.wٹ!Yv=T2{/m^\_.Zx՗ضc'6m;%K1iℛZLCC&h,ƿ),,`?HQQQl &^v}}hPdHJX0/E#4,^?ZIgG ͥwZfϚVRS]Ǔc>}Y*:;p:x=sÄ:NiD_a}"2M+/RTXH}]-{2Oe{z{@ea.gy/a$I46|IL2;v?zUz *++B:f`` $IV(Yf֬pp8??ŋG( `;le;ٔpdE qIB0OP3O?I^^;LPϫ/C)/-^xyaf8-_N`(2Ĺs믲dkvsϜYܳfLPϏy ‚sTnʣaB0N 73ud&Mȶ;nY`$+♧SO>ȑ?}ݱ'p8l6<m;3k&%%[Z[AQ&M`B]-gϵ"AmM5|?IZUGlL,|?XGR"A- =tv?+/2ٟ<X`ިp{{Ϩ◿`T?r^{*{^?=sfs)tM#JGII15U>rC78{7lHs,kmebH%|vT2j0 $C8vϛjLo󐑲Nf3nֶvjk1 #;/vK4tbN>[X4.[Sذq3K/bM~{7e!L}݃u})l+dHJi])&B8 /V$?h4͕?I#7]aNs6d2jdR5Ici֬t144UOʴSK4U#O0 dѢ+;DdΞkuSo19c}I#L aۘ{߽\NɰI*&JgHr\y6LǨdR~+tRP_QY^$ t:0J8^Ncc3H(/+fQ\\Dkkf:A__?,rUCe*+9|6 ٜxL&C"w4w I{a݆OYC8̞EGg'v TWG+6e2X<2i+y~}H&Yx!,'Utvu܏Ȳ̊b/p\TWzlw)//߽6/q.?5cۙ8I:b F(d7qH5k#IQ/[`\q Q~+@CG3ero_`Zyh2~ӁwdЎ)NsIN:%}Li`ǫhmo "̚5ӧP\Tȓ? oՍv oo毰Z,\8NV+wC )ĉV.NZTQm9K().d3&E)plY Wi~Qm~o:6LZ,hN*$6+ZX-R4Hj_K*MEwIRXl6}ښjOJ2̎b6QUT*ɤ 2TÎ$I#PG L&a 2dEQT*#lSQe l1c0 $:oiU%J(RtܧR/xjz~T*{-ie\jl&̜cEQFv#z{[3i:k6.łiF0hz\\d]ױZ3-<_L&R4&22zS| uD"ak2|!cZL&:iUq~l:g(H`ZeCb1pX   6"  m#R)]5465S_WGƢ]:  L֯mA NJ1s8M7Zd&]}`QCX7E~^XJ \k+o05,N'_IӼ]f,O=/3M܏ Clڼn?x9jh4JOoU״.j*.{ꪱ醥Uvp)Ln`ىǻ^>x5f']AaaXtCQV^G(fɢLn]Ȉk& T7D yo0yWkrsmw|M,`ww* =IGL4z(*,.K&U ~GYXf~qQ>X1?w=AuU!_݇Ǔb;ha4cdn `痻={OGBbttv瑇( ֬chhb::;5ee,`)5xA f?=}s(6a֮ۀ|^/}TWc2br:ikoieD1l I~~D2Ann.n+P,⣏WxBN8Iäm6JJٺm;e|V|19p0Eb1 X`%T300@Ww7>e%]ǟWIDYi Q4McٮNg(-)Rv[o%I(E .;xa+auv[a68đu)veV B]ˬ)--aY<`׸Sz/dr:^XnWNjk4 ɲc$''Ñ'??ϋ(ȲL}]fښdY0 dY CQL&v`^rFҤ//..c2@udYFQjk2rY]ײ`:rgϚy lGhA~8t]ĩ,]8{ŋxjf3M-%}M eղ->}mNҪʂys՗_0(b[XpL)rsy C<܏qg27~ӧMe2>pk!2f9nJ<Ϝӧ0{gͺ ,#RS]Eee%>ۂ,?R~;X-fLFmM oVY3g0g͉Q4mgxt] ٸ3& yyy<#|z p|~cwE_ma$( łdğgAef1mɒ)2j4*FAMe})84M#Oo~&7LEӴLzK2SO><Ů_!2'MbY:r'P_WK< NL%9Yl)>Ĕ)0mwQfSNijnpp8p9]Wl465f&Mdڴع{W_S^VF,;9kzhni᧯E Ylfȶ;QUDUrK+L&(Į]{鴵xɗד;tHZ}#Ye w7#eKfϢb %|Rڙ6u2l۾0SSou8ȔI-[9j63.o Fc(|IZZ*30C1hkkckv^([Z(ضK\l Aj5ƲhDFFaAy9x04j5^wcXѨ5Am۶G6/qχ<.ԐcGR rwa? : } AW}{֒zxBG%'_VQ<(t:@A$Erzy/_Pe^/%~aȲ,GEru wƦ]]m{[۳o?BaNf٨5pF%X757c 0y<T**sσV I2!!:dYq FZr:?$t!O-DEzZHVAQ4Mgn6*V]BѠhHR%?VNܗuz`UU5IIŏ3 A GMWQR*fl݆`|84M"by|y_++HIWÔW ^Ol\,=ۭ^s%gWvwUPe IDAT@ݺ曅(|#wq,oPt@ ].1Q l6|> AOHHU$IdYFZQKcJ@d(F UӉz:%B jBBTzzq;$g|nֈb/ /9IRYD?^< WSO?}x{6EQظy+E%_wqT{{;55nV8>s56?sضKk{Oоf:&Oʑ-U%oVur{$CYq%!'))r`+>ɬeH>D|O.#:.Sbc)G8>V.ί~hkv3 !,o27ֳj ™TYqX}d g=qw A c!&, ( AXr5-|tE17?)aaa9ze2j00ixcLȊUk_/ZJsK IkY,+&qqTUUCj@O>#%%xOIDD8ɉ:|fl6;=mݰJͤ {ɰܡl߹ {,"ln"w7QUa唈Oԓޠ̉O!.!XP  'g`8͍n}^Ejz(QZƇEQ`ɬ"-}jypD;NDAMeskMI"ĥJ{v@k:JA6 ?73G2̌g A$H A6m"$$ch((hmo'1)A0xphZBCC;3'pa"#‘d𧺗eχ餬_F$8~{g̅"lv;1:|ѣFm6/vF**8u O<ĄLsa .p|tyx_hwA ry@2mvtt' 4EQPR\/~:!Pzw9 xUٮ2&㯴%0rtcֿDQP(%rφ.o'+=h\^NW]\It߿>+UneȐ H AT\1|ƕy@ngŚ^؛GFFϓ#)(O^|>0cu}:G}} B'm/AztDQu=_=Oog߭NT^\̮ {;]x>-ȲLmm]mMM!.cikk~Zu(¥+kZ[[hljv \jI[{;vN񝩭b |S5CN6l䑇|nڲᅬnNo'<u ݮ%oEQ8wC%6vaXشqTL&eܽŀM=fdygv;Mddߪj9$IU>=LHjj MMl߹JͬfmN,+>03p\G-3EE m3έa }UA%{!((wDr"?-[GNGL+MMo¢"V]#/ʪj2  iS&SQQI{GiiZ)A_x# ϱw6e7fEQXaQQ&?z{)-^>C|\|QS>r'g={3`@!IkmIaQ1.|'Y[ -B?|(ysg󗯽WK1wlbQTL8gβg>P._Iq\_ܡTVV q{~Z},j75+.ʿ ޹0F\J 7L8mm픔hhiiȱcw嗞؉5_7zR `4?z8 !fѧ(,_1Yq3oLaa<3o_|Ėm 54h# CTKSU}'Os[`4Zgz^O0p8km~G7Kb74fB 񋟽WKQTl)Ψini# 4J8r8?yy|==3eɗKzM[ P蟓ͅ"T*pٴ \YYϟ3jƎ/bI8NV]#pL2cY|%--K}}[ /\0!c <|>l1餦_e]vZ0yݯnJ'PZV.kj5Cs;: !55(2u$IdD:> ;~qcYr5|j"9s<23;gN!lfʤ;=k{!dB D Ӆ!1!F ޳VQ]};&3#Q#gṷrA $ȷ1lP:d?xTBC *$_zjKHo<_Cc#3gLABb,3v(dYݎ DDÇhhjjH'p8HMI&,̈nh@][ qY͞VKK yÏx!AyKIIi"d">.N֣(9Z`ێ|韙7gV0fzIAbB<:ȈZӉ'2299c.D@@V*ځہ ɟKfQV^ǟ,r2위Q7 F6FN{N!,,̌ q\9vRSIKK!)1Xrrinn(3eI ;$O)7BrܮN!($I=^fEQzT|-l|wCop`fTD=c. 0+*4i3n*& TZw3ܳsmZbKRpC٘fbccPT446rߌDFD`$--*+>,TŒF|>nχ$I۱X70md<~Y2 ZM ZZZd$6&LT!<G#>.!RЀAg DFF",};]mG[h5^O\l,&S$=^OJrkj3j$ideeR^^5j*P$&4SRh4?qR{aBt:HcCmL85oh`ƴB >rL%#=R. ɡBsK 9Y:dE&&DCA»y@nMȲ'())%)1qctl6?Hkk+r2h$I={a.8n?([SsFqyE 2F:rwt̟;z&Oxbobƛۿ қH< Z',$NBBZ&Š׀ȒB:Ơ֫h>A[PbG} [SbЄ)W!#N-[Q:eQؾq)%5 >D:-HB)4nV$iR4 z¤l͂^+yaI,_ɄZ"6&2(F(457ST\O(-+EDD8[m~2QT_Hkkaa J륤 ADDWLd'NbIJLoɽ3aB8/HDG;z0"ٵg/rdfdȲCQ(..%>>~}fZlk7!C-n"iF!{e*6QQ&D ΈjtZNաH ^xcDQVYG\l,[{,>.DI4mRi'yZ,KkB:0͝6`A\Эj F##Ivr!_&(B@z;cnj~D&M$ȕNΞEH\EDGSUUhniaŪ5aqa̛CB|P^QᠭqcǐfwN: El)ƥKu,_Yt¼_(dT"6I,Jʩハ>%,}9{]9C0.o 1( L#DGj:UlvdYfd2i8z8%>s;v!IgXPedv9oddycGa$''bZihhSU 2aa {Fbcb>,ĥK;fK^"#"hkoԙ;p nijUH΁"+4m%zL#jC:\( DPYQTDI\l5NN7ʄS21#Ń.Jgf $I0ׇpaшM)}fǷ!yeD@ *kflO75 )=wC\\x:@źZ,Uvgr#T8E1$l IDATH AT ݸ $Ihuګ3өc굜8u_|^Ol:̲+ 'EQq edge"I lf-tX,DGG3qB>D\l,G` XRSq:htE{A`w8&=-m;vF~vOJ[kJNN6I lZN%6&D"#.Q&qyc63j HNV`ZIJLdİ\L&nHII)i DUYg'Xq )zND7lTX"(P}͘F=,SM8dIDcݝJ>ZAG MSs3sg=gw`ctĎ43<A%voe8d*0upo V AyUn#S谀UI^7[z |쑛 ]n΄ީup mQZqs*<M $ȏU wsgp>L&^}Š$H FePj֣T|3/WVUt:Yrʹ]ʁ. ׋$Itttڊ x<֎޻JΙ|j I2--ލҵ.q>F$VZtzu&dY-|EϹ~V^{װl^E񷿖V;/^/_-]c[}}wRRZFk[ CPNKK+V->.Iry$I.Kq߻obuWA$H AQPdFQm$nIsHnP0AzZj >/bxQv`w8x[56;cڰ;|x W+XbvH).)>>NQV~OCiim~OlZ}s=;:عkOg?X%_qa^-ٴ;;?w 3Ν(۰_|ɪyp8%UT96IztlڲϿ+WgGrs6w,ˬY;wAlv{A6*"=t *.g7i& EYQN Z9vU,7?]{QXTLaQO♧`ѨY~#aauu8ZҲ2f=0D-_Jb=S9q4 0,wWFV3z23?Iv~8N?WryƏҙiy?#,ȡGYi321<#|gPȦ-~jbcijiGqI)jkq:HJJXz- +';'wlv<ͷȠ'](,_ᤰJIi9_p8 5X0o|)QQ&Z[HMIᑇc6GhdAwqT޽fVΙ3z${DV;M[" 0qB>ťeݷyhljbWKW qq̟;={sq< b l߹ skX0oW&)1Y;ۙ4a<1/3z$.m;0EFDD܈IY~#O<)\+?ǎ%z{y6|>YY :?,\/7?)_|TfGGY`4!I}1uvLdfdj9t(׳mN(j CP^QɲxXv=qqhvك"+?^N'o$66ƌrZjxZԠrikk : wwz־ <-sD!+ xjɁ 8?쥮VbYQV^ɖt|^/>>CZ TU_dv/JOoh4ص4L_qV韓K?KaQ 1118t:-A ))CGb$666 nj/fR{1T*&gϾÑe0|X.z>s?. x}ޮCe"#"xgپs7E%T]aά闙AaQ1H:hhlt+/唔aXx/FV;#"f3>_E뗘x?Z$.6FCEE%-"I6maaȲ={y{QdiS&sI~1[ZYv=2Ӹw=7imm#457/6,+o>NuCC 'IN ˥˙7p-[Oc%^F|| fc0 CȠ(x;C^/m45󟾆bC< 3&QQ&v; <8(ī?y& $'23yⱅއ74`;7{v6KK;>qy. `Q$''ٟ?Gd232}: A G(ȄZf$QJc@$Q)BG`0~eN TbyPՠJ|a**ШȲ$IT*>!$ȲLHfy-(( r 륭DIiiDsY|>_0x@NVKxX8:39عiSllܼs(:*^3gvq8n"##QՄtX{PI3,# B矢;fvG ߭Ӷ7n.FB|<Y$0#NQd[5􄆆p8;~IΆߕTXR]k>75S ȲL{[;)I*)4Uh0LTTTMcc8V+FҲrRZmx$Y u"+n{7&:rbccq444;tAQQDbBh0b06nB)2]=SbY6V?D|\ $H ?Zc@sh#!7kBBB:dg~Rq4z}bcbp://bU444mFՑ7/-S3u )އ2l֮@HHsI"}i'}446V:y"|8W^B%8{oNsHOO߇oA'""ׇdB[ 3(V3zx-B3+0EF7f4ܵxQwGVӯ_u$'[qwm2X%,|h>7oER1ضc:aCI׏¢bv.a_S\RJ||fs#wd!9)56QYY! 5l*[ZףZ枩Sx1GyWIjPDQdr2-nV8a<|)hx'/RZVk9~$Ḿ󘣢 iygomb;r4p@j;(,7N9K{G"wGfA|"~wBZ5?WFCzzo.?yyB5ex^/_LQ}zl/{?CM%233\./^up:466S$_UoŊf#)1PXD|\,Rm^['XLn'ջ-2((T%i뚵\\ql]OGK;~\kmm+V姯BA>Ys(j:~1 kr =? \|[EQGYQw] =ut?IU}ΝݽtoKw}}nZmzt/׵-3]V<.xx|e|>f3͟JfΝ "r0q|U ju` m;H[[23l_w$wNa?p }ZhU? ys{ 'gAop#sRXTǟDtZ /F&JʰB\,ht;ֿ@vIђj7:"  Q0~$:g(BG39bC$6c`ϲ%WsI4"s # 7sqgc~%64 4)7wv`vpL-nWsɘ4%Q}+a[g>#mkmVo!!!^NO$ɓ&r14j5I;p },KBAr_Vq6цD c{d?CZҭGMnajd$Pq^{a?͢rC=J{p@E^tK*7Փ9;Q-R9 /S<-g,ct͌% $$*(Aa;fv.ԽFcOaQNEAHXݓX4ȝ!2zJuwV;tmޝt: \"9)Y9|(uurq4 ii\(,DӡhHKKBa!!~ˆFJʩÂF!\r(p[bbZq1V AIINBQ3fH_(%$&&P@Ii䣴EuE;$I闑ngu F&';XSCVg|m)'K ൟւm1o,m܄ɓ bcbtZ{Yb[tJ&䏣B}}E%$'&`n؈륨g{qUc|~7n`፷hnn!--^c F+Y|%SAmm-[ 3v(+hhjBa1VSN3d`ƌY0w)l)6LC83(C̰HI1T?wDH(&Ω^]mQ풓ܾi2kjI?U edi V_;CvoE6{Oʅ*]r"{{n A fC>FhvKLFe~{Gu9 `{H,˒Nu'Lܙ̚wvܛf޻, %^A|EKe[=xZ9O~~< FFGxɋ?do[jpx}>z{0II1CUe9^o/ƛw_߬qS145χNe:ɔ>NOk{{"WqaX,iQՊ^Qd2 ٵ{/ Ka>^!+@ps $%Fܭ,ii$%%F-Z@(bO,^N˄lN>MiIZٳgIMIa{ؾsN&?oӧMe݆^/Zp-ETRZZšu1'%q} ZÉ'ٴe+^@ @QayydDW%l߹@ ^ ?V,Uky(,,`_ĦPY^Fgw7* V>m ug2!' bNNF.z:MRUUD|wރd"-5ήnvXp>ǎQt7,$''hmmБnqqtvuQ\\E.3cTVZCnN6E #ٴe&Vq ?5sFt 6mٳfPW8d9sٶ}˗-ZYeRbkqpWQ)"74nv&g;jﰟ36ٱ̸I*GA ;l%.CCZo˜Rgň#F`H0.6T*ػSjj(/Y ikk>~Q*lv;:Xq3sfdx<[jD%!KZ]=A{Z*t ]Z#+$]dgۇ)фf`@Zj # RVZBgW76X,iff'ޠxRA(">>!rs ôw`0 !BAvf&`߾ar9rs"}P*vXj4\hlB H`h4bJ0hhjn'%"ˉ7&=Bww6;vC"s:wQɛnG!RZINN}\+ `3C JB0V!_ aHۃ6YL12Á0ΞyJ׋gЇ*^>Sp:_޻p0˃!GB .C{GlKa { C?h Ac^X)*XRn dZ c:F1n5HFEb'WWSZR|+p[R*)*, oB.?=I0BxdfdF8FQUY1AVxc[7TF&%-BjJ @DA3VE;']^Vz\.b=I[H1GAR\T=DN.s 7o IDAT F]^W0j!lv;q:)f> `s1^=~\n||Q.3>4I3ɔ" Ev[#88˝Xr"+Hh#noQ ~B䙖d$^ȟ>+b˛n)IʯTe(BqR@6dJŵ$IBHƛL0(uɮb#{Q(7L ܳ`0UmQ6 R/\^b\_fܬA 55 oZ85"1=52Wt|>@h0]QzG_;A6er8ӌQZĪ M5CQ')8Cb,u?/HDcQ8p8H^ Ixf#е >+D݋)t_M@ N1؝N'bޜY_kjFR8pb͔o^fάHĎ]{HMIznr97SrG  ev54`4*[io`hJFFdsdԱ'p{sPKZ*zA\t:e D>~?:M8& D;߀bbU%  1SRR:ee%d2q\$dgۗ_z~VY3iB!4 &S7j(呚ٳ(U**",NqQ)d::;io$/oTnuCZYfj9gC__?KYBvuuSXORb"MAl6e(JVY^YY,Z8locc|1<8G$=%hU!l߹I Bl޺yΞ;'61udLyG?! Ahh@A~pd" .0<< l޲y(Ȯf,iidJ`hJބ\d21 yY8臨TW0x<\N}.6oNYi ugÔpt-b~N|z KM$TJ%M-SR\LR Iٳc7_8tO8GvAGg@]\n7e_Z|՟! 1ib 1H0K/lش\xysgSUYǫ"Qzx.\$++BM6npG.^}MsXD˸ntFarIJLg/ֶ6rsr@FϜE@ M$%&b6'k,^Y3*7!fĈq7?[ݍ1܄<2 @( ;R $/|n;سo?=4+W;?ǫPTP@cc3@Vr%Ѩ5X(ӵTO |=f͜R`FboٳosbǮ=̟;Njɔ:r$ ~}f͜Nĉ\ 455ٳf;ҏȺO"38L Btuwaf͜5ky'ٺm'`(!s^^w8t(kٵ{/<j5a)̆ =le|r5=o> # j:]F٧@Ѱd"6mن(NA~q/>pNPUYASs3 M[CPZRӧHL4agxx'N2mhi l/^Lǫҏo^8{ˍN\&UU2j!"*׬#?o>a,Z85kדCjIaA*delٶQhmjڎ㥶ʊr$`H02;vbJH@Pc:fpp[QVZž}\]{<֬#-5˅/' 1084IfFG##B0ˆ[굟{$jPWW% EL&&ںz[ٲm;*4llNb݁ R>X1O?pAd29*ulg/[1md:V0d"h䓍yGٴe FW o51$FD2_#F I p_HDDB@D.)ph^nnmbc2 JАm3ٺ}'&P3g_ñF=wƦf٨ Ӆgg+Wɧ Y̝=ܜ&MbttnxJ 9y!+ayqxr9,ZH d``lش&MbRU%ctF[{O<CV++Wg$>OO+FFRƦfGhni/ȑXV7\OW9.)fL&UUr FFGQ*̟;xmLH8TDO \ P`0^\FUeo>E x7IMM %>^ͬ3ct妽8]J<a[ tvvnI3g.Dysgt:y7ȥϞ;__مZ p\dѸLuuW*!T*.E0ib˯V7M. w=m<2&VUrӓ  hDT28}mboCZZ*`Q9c:++(9 ᪱q$΍+a41s-F1.q-k })DA ,MW\&Q^Vʚu3{&j Q ׋L&#є@Kkrc$%&qJNN"DQvp8Q*ddSS=b<6:;p]tvwS\TB!9̶">nAd2\lj}”jN'ZVKwO==}Q5~ɔ!ݒh$)1\NZj*EEjj&M$#=ʊrذy ~Ĝ3x>B?aؾc7 ,Njط '"n6 ( .': %6XٻDvvFV nE۸?['Jp`N6#$x<|>.^>^ń~lWGukEzΟG aG|>|>n$q0<<\.CV+zi̟K(_?Yl)ii=w!\n{@$ yǨ+ZA'EE100$IMI!$γO=s0= GБEE<㔗ىjrYps>MiI1OX|>$###QPϒ $x"Νoo?#? ˞}HNJB׳g~~?]z%$q{i(Jk+q=YRBw B!|>?ZZ]Ƹ\ntZ-2`(#F1n#bMBS*8I0c^M{{*B1ހZ?Xd1\7裁##C452Q1>Q7 FcdjQUdeqItZ-q: 1фZeGeܳh!2 R*5˗-?׿=&S?^yz̛KÅȀ)>>AJd'0 xJsfͷCEyqN֢V0ǣP(X:&& p=t7LjużJBs̾)hf3)f~ߓbN&sAw*(lV -]̘6WTmg͸݉#F1*X6 3/t?L_s  qdff-$IڱQ jۈO>ϡ)(&+uxx'u ū|Y31:7~_wbĈ#ƕ|1bĈ͹V/\F0M vixzؿDHfnrû6#"eUٵۈ"qx~-~B̉#|zp pdVj$.ȴK<򝟰w.2y>2 86zB4Ae$S}o:+ggg?b.1bĈqB4Fs@wP(H+riljfbeŸ~3Ll)FAÅ0]մM(ħ6urtۨFwwx mԌ1>>*|Ck c@4:IZlD$AJz&99qRr Kx*I^JBI k?[^tuu#I<ů{LJ+W)^p5l6gӸ(4>”ޘ@"!R8L0dϦդg JMZۅD֌ْ}?LL%+O|/A  !"| l[OĈua#HVw#F XUkֱ`\4j l%ٱkCCL\Caa6oGb4;>S8|(q(eǮ=tvu1Ғb x jI1eJ GJxG8s<-0u8چcq|DŽB!z1X,i,^8 Ia %>ܜ7'Nb7zƸu|>NWzzظy r{/#ٻ's\ΰ$_؅FappٳfAL H|jGFX3PTTB.gmDGzົKM)/+eڔճqVY~#?y$L^ GGD>#Ǩ,gbe%7mד=zY.fxΞXO hie¥ #"S,M ̈́r.GvmF&քR&s+LgʷDZ=?͉;hN\8\n[Zy@ʊhȑQΞ;k| JJφ d&thrI;R[cuV!Iea~?Z빱3-EKúXn=Q"aI)I =?,'>_ģtv;> b̸aYr9QRS̸n45ZC @R1d0 ͂JBRE.Vx]@Grr7xӹ-l%IL]#FFdge222JNh0ja0P"II w0ond&Sѐ[q:#e%R\X\!G9&S`J`JM5ETJXصkPZ\\.CQxع{@c|<))aNNZILd Q)U$'%EMT`4ׇ"11&^7ɱ'yÊ ؄\`r$V]1N1ISs yroKPJr2wb՟%oB.rwr)*,W^P0ib֬#;+I9Ʈ2E ǫ(/+bc3MCR7aoh4R_,ȱnT*U4N%1DM$JKQ*$'%1{ V]GA~:ֶvZZ[ɉ`~ERAM(eU-_^aPHh\;5"1=52[eۯ| s3ZIlゴ0đWǎJc^p8 j u }]N 'q[ݍ1IBcQ87p?n7$ rDQ$/7z=RR ΞA''; sr2yB!MBvV&: PVRLVV&4 ƈb΄lMi$r0!O||<$rVJJ)(GՒ`"R.RgބM1q fcܙ@&)ϧ3f3(J2-LX` 5ŌF`0\(Mqqqdf#sf$+31LLFדAA^f@_1oJBjJ iiiXV.\dHJ%j9fDLބ\d2sfD.sӓ,?‚<$P(L!##FAc[_J[66sbϕ`0hHfF:. .Nux;vaS]=iS&_%ICh@̐w_?\SS3],\0j`B$::)`ǮQR\s uJI\\zk;W/,{+tn% 2' cɝ8beΆ0yfdʛ?Yq# xԇܓB\a~] ={Ij4xW],銢HvVٗɎBjʕx㶥Y9:x "Z鴤}ELH04cgu^/@Ue%jsŸP(QK~ z"ɾbۄq>?9ՎOx]Z\t;xw>LRSjJ>vbĈ$I #==10nU`$o51uNv;?w8q$!/7Xd1Eֻ(r( 2i3'oB.]];)|<]%-Dv݇ᤪQ&r|}$L̘6>)II6@`άCA؅^GaA>ʜٳxhԟ9CWO/3O#єӵx233Xn= ep\B!>fNGsIN'~o WY8>̞Igg :9yu ԟ9˅LƒnԩZ|>IW|o4AwW_-P'(zqlÖbTiB08M":ej4xScܞ( *K`1(+-沂 ҏ~c\c `pJgW]]A'OrPTߏ$ RVZZ%9r.6cb瞽B!x]&TSXH>rflv;P2.^d``k>a鴴rvYa#YG$%&F2ՓhpF.YL]ZZۨ4wG+W`J磪Bhd RRT|a5MEհe6e Jw5pQ͞5ZK8o'3{&Y֬E\1s3ЦiZ4wGeJԀ(㕷K׍,-d!?4͗pNR%WČ1bĈq1nD42uF Mq!`( DFR$($0qR*D4vl(ZFTA   rQTl߹l6P"RSRHt9u%QUYNKk+s8tg5PS= RADWw7d[CAL&*z=@kk;&#=3+2dӖm  =JR,iNjj :5vp0P8=5&2 g}d,Ow!_$Tģ˸ꗐ$ GSd?~s]Gʲ;'F1b]7@ׇl\.g)]ZrizN=Z>  *ݱRh~]SIu{5=\wK%z}Adc.F1b]S*XYcmwp~T*JhpqL-Kg1sTrj5䐓ŁC}d3xQ,r$&& ̈L&dT(DDrss0rq*Kɡ =0<^gβe$$$ ΊhWUpSkQ*̘6>ZZZy`tѸZŤU;߀V%b)!\GYr"2-D9Y(dDQd8rP*\3e lwPveݝFb;6MBcto6#6@GߐC(t1zށ B*X1bĈq*XΝ;_ٹכ*2h"3cf+ Ka8D|Unlei1nO^_eB:U3)?(oTD'9.1c 'VbĈz+'w;HO-8~ "g|hZy#FP(ħ'OvaN:}E!>u_^+rx퍷tuw3<2r]ۈq322 pMSs \O W^B4w`TWTU9g9l06v;viOpcrQ"(U²ep0G=֩󾿷U}zhnia`WZ[۸Xuց:.]rj@w#v<!Bp fvAp`X|EQvc6fJaXՍ(8Nf QqXm6N'6nlp:q8v~V+ t:B%_Jf3.`Ov~?6o/6mޯ[&ǟrf$&c2cҏfP۴lr,Y6mwxxwtwPRZƞ|pۍ륭͎ҏKRbFAb8wʋ|e+mWH鳈Rv,uff fs(x<^f NQEՆbp\[P!B?aŲ%:~ |vc2ɤ,Y~b/۰Lٸi+&˓OfG0uF8y4Z-C@JA.HJr#G O~ym;vb4j/W=[o|ɧ*6;O^IWW7EI&;z]H!B2|xUpIMLyE%Ǝ' ;:݃Z♧0~W-.^zY,Z 8(rs% IOM%=-,rأ+4a.@ @w7V;\kl⹧ ۗR5Wok>r|^m&>Mxx8Z׋DѠPV"3EY3SkX,8. ~YYUExx8VWy'Vb䉌=qcF? w: N3%J`Ti &W?O!B"#*/VQS[Q#PUlv|>?,rnO0DUdx_V͐\^K /[̔hzܞ`T0DDo#HPTXm6lv;j(|>gΞc(J"""晧֐Jnd2@RYh DO2z9D"ATbwqAI{TJ `t]2 ˬztaaa( C ;ځ`&͆D_݋%RIb|<+hr݃r+ ~I\AxX89YNƍ;EtZ.^ZF*˟w¨#XHAA>%993g"Hd,] P|]݌=C(>|kXi "D!Ȉ"#P(JƏæ۹Z]#j4F"/マGʪ+Jƍڏ>&22ȑimkpHNJ &'a4IÏ?p,?@.hoڌQoౕE"tBnɑx}<R)K/MH&Oǩ罵2tHZMЉ c1(>Neɢ,[vh4qo9qJD*2ax}o-o +/<4tf}WNWT2zȁ~?by܏(P>H\.N2gmJH7DO]WnA\|S&M6oڂAg^sߊ`XiTr~ƫۤV[ˢ(zKmzzzywغ[$tTʴ),:'%%y`EFU2 ܽV ·hT)iiid2t$%&R]S(q NR/>ρC|>^z;7?:xݧraY +JN!ː3d IDAT(Jo ()䀈"\ʯ^e 1얽qQٻo?cѣFxXr2o7򕫜>syj3gȗ~5p N"jL6{#ucKT<~vdWoێށ'DL9vaş2E*1y[n#JYz@ǃg7hXmΏ~[>^EvsU/U+1!KYbǏZC#iih4tZ-6K/ƨ}B+H(xghmmT\؞dgg!|4%w&83e"7g6Ç[:$CGx?#*2GQT|p4 Ǎ62qXW~}iH'm;6u q17`0hi e6GVevy ~ ϋFjQ(|q3.ӧM54rr(r˸1X:HIIf<^[XDե+;_nh02v(v܍T&cL,f9,&,,Ԕ BEzz{ٶcC>n[6gZCtT_GEڊ:Ĩѥh1fxv/}bF U<ZF )\}$̍[=<̙5I$PGAdgeys;D"矽<QUZJ_ 8 NᄛZ1h4\W=JGG'/>,QS'Oͷfk/#xwGXv܃N!  r1bӟVFfGZ]CсbZhZkGty<}wW1 rmK i%Oo7WM-521?[! Pq#n9/Î/ }N}!B}X"HO =-SR =33cYYD1_r7w62 j r))Ƀbwrs~hA9 r9tttBRjy8.zD#Hr]|> Jߏ(X:hjR]SKJr e2t:@/m4 TJPT~؉磣s NEI( $ (`0艉/HOGEl6;=nn7Ȉp.WFlL ng3jj #= R jZ /ˏ񻑀T)%nZ4ro_{R!Fb&Glq<(uMW=z!QF(E3Mo~4[mF!Bsʩ+a2Q(l6tfW*X(5"JϗE+G#s! 5{"rq]# UKQK]QE9B"ĝ0ż[V qH$FF#/<̀T\ɓ&T18Fhԃkժ`;͠_j~oe&+هL&A5մ/^D.L?YJ} QrK HnߗV@"|;rh&v|w|s}Z?-߿3[==^̎uq:\~*** Whkj `1 I4R Sx$1qlFMNlb y+21hi!&!)+gIΣ˕f瑒|$R)~˩C,%Lxc;Pzcؽ#V< dr9G^*q3d8t}=%PKC%2rILqSQvKGKn1TASUw|8w<2 Oo~z9Q~:TB{ӵ஖D…&Rٴ\SdCFE8wj᣾-~4T_XlQ58V NhoCP"I(;Ic7: NڏL&e(;Ή]Z;ׯeUDSS*ښ6X,'B ħ Tir8h+@-BHwf E77jQ(\E*9ss]CEq38'W=-L];m6m 4\EFOݛYً0MJ"#6v26#=w('plRrq;>ZDDT,UgO;b W*KBf-]ʼn]h4Z*NT*trڑ]a' ?û6LLB$ YCT[(@Rz6;6`ST.!<*%G`9GnCsRӉON6LT\z$Hp9(?|HOBAdl<W@JF6+Ȓ/Za D~?P{j#26V 2,8 nWʂc9 VGJ3sMLA&EMU9g(xK_c&0S^z~s/1 Ie!@Rz )i\:eAB<.'S,GђGSt53f,>Hkc=gG.S{3(;Llb2Yy_t"H0ED Ln7W0le3hZ`dHR£c2q9mVZ%<*XIjv.W.> %ޱ=RĔ:M GLDLnsOgERT%"*̡c-61my Iʎtyj]Nw@Ξ;ω2$ںzax 8^JE{G'rT`jp ə=s: ٶs7gΞgA|~:,V:::jxΝ/gر5j2 AKsK *g8s1Z z=]]457P*P(w`Xho@pz{{"1!>hcnjBӃO[{;j RIoo؈N%9)kCac2io@VS]Sڏ1tH.@~db5jLF#]ݴ!omȱ'؄\&|E% (>^ (tt?UEfcZimm#;+~(/DGEqaƎȏAH!*.H$"1}-JIE7Q(c W6`J%2TJDBBJ:o_&Raꂄr@DQ"=Ղ&tp8ZZDQd/xjdfǮ={0n,{O`X5Χ62}y  vKNV dI)Id2ombcc8P|D>^MsK+5RQYKCc#.#ˉU.sj5V]DDc2شe*Gʟ$ MMT1bxgϝG*"HZDoH *xhZN&;3vΞ@OOMmvjZyoL0}ư9zS&Q^^Ittĩ2≍ʋU455c0ؼmV}˫Ȣ(mNY3}ꔻ=~p0[,,Z0ںzj qq۬?`˶<ԓP|'Nf}/fQS3O<6Dӣ5E"` >D(*aLa!.!H0C7U䦣 (ٷ,Q #=mf9G.iSo\"9&^˅zupR x^r9rsF9u8].,cǮW~%**%t:iiie0wL\.' |,^0Ǜo=vWh4<v>3V+S&O$&:`hm}=̛;zP*,];}E3Su2 cێ]=~#Sp ~-]ͷޡq8Ǐ_筷^> &:CQbs=.\`2sTJNCFnLF#y9̙=>/tZT*(x<ށJ)<`JDV0%_?)AD))"D68t(64)9UR ..˥Kq\ \.P(=waNbB+/g~"JrDz30[,AJr 9LFEEf 99٤$'~(rr0 ͡( H]]=i9zѣFzX,|>TjN'n@ L.gaDSs3x^zzz 74VQ~cK*FSs i\̕jf͜{N`χaGT"JP\/t%"FJb!::> ? r@ nFEDx846VШZ$hikGV'?n *+yl#BDx8EugrqU;;>m a4#x=twDcSiR0|#C*+Wz(?b9p^ήAG2JĄVx&}oRSd2"2KZJ1"DwۭϹibJ\+*d zJJ IDATO?cH^.[n'%%(J.]fj8},îǿKR$̚9Lƹ3hWX(B鳌3vEr|>555j1vhZ H$RSRtE\POh4c'صg/2Kc}y"#:$K?G@*QtقJb݃Ve1hZ  ڵk1q8JN# "啕̝=0zz{!""1F/F}C ;f4(rNDkkeȑdgttv1bXRLưU*VSL(F qcZ>r NGZZ*&goa ט?w6̐Ï>u ~9QQT"a_a~ f&55͝`}/P?dr{G'~9|AjN*g? ߣPaBq;*X>)j;X!i)~S!Bk36ŝo`Ua$Ƙ(ߨ(Hҥ~J$R>u %~ƫȸX~?._!/7XuK_^OJj2浗>L&cL0= oQ|;N+`{==*=M]]=f宜^8|{^¢ɢ`%'; TJu::)iin234q<_u\n7YA/$n7GN*NDFz9YٔT)Jh`т.+xx j4bbsH$$''dB4BrG!X>+t HT1xcYZLgΞui7z3V4}g9}9Zϧʞ}HTT$aa(>2E[B. :b|w(dZ LC)A^^QIGG'fӖm7V.W x8UvĄ4 wFbZe@{{;Gե+*+V,_g +oJSs@=ʋ9r ^Gvv} iSټm'} RISs3jnj5n 7f4+7f4%Ju;QBTdiH& 1b8Cy՗)zNbLFz}^|iN<^Gryd2)+-d)ILH`֌|s ٙ;%uNfϜO3|X>˱')*>DZJ `wIOl6͜Y3:4HOK5FE**p8(JDQzJPS[Sؼu;Cr)>|@gW7Ǎaqڳt:-Nϒ hmm؉dR)+/'KȤR_\.M-c%pБj_D8ٙT^D"!::j@*:!>Yl)CΟˆ#Ɉ|yÇ`6[xljr1V,[^|Ȉ?㏭"6&ϝG|ʲyo$&$`6["|-v;/< WVkOO/FÇQ0tC'Z#mNKFZuu T|ArR u.&OC=sY3SZvGYFooE kV|QFp~tuw}[fY6/T(Y0o.6fd4""}n.ZV+nEYQbokV|T*%.>ՊfC`2Jdsa&nQ>3~޾> WVyɒ2>Z' }h4`6iljg/wԔdV&^ωS"J>߼*{*$R Ǝl6syr1 (:FΙ3&1 "EQ9S%[k{fg=-[l%9S9g 99[@I 98<]uv}GVfN RE|\]zQ*h.ĄurRZt: LNJ˪4q*56`w8XnWVՍRL؎;FE<^ lv;Pχ^G*ޔ q8“ R)ZP(DVf b6JHRFGjx^AfֆBx0eH8` r~ ClXkFcccy]vA T"!.ΈAvTr9:~2p8:nߏZd~? "Fl>?ZL\&LqH?⠉Æ"@~^.>op\pL&C :tQ#s1$3Zm4 W)) @fj$qSQDIL>[( q?"Ϙ<߾&h2>\fB923_!)!qcJٲ#hjnFR1g,zM&bzz=i,nhhdtC ENjB@Prٻ NрZQy8t$%=wGY̘L>CBM, k*bÕk/~ngc7oE Rذy tw|fRy`Z\DӢT*X-?sgl~P[OE2$W?x\=Hye u:⩫o|k()ź !66ĄDF;@VR ֧{ٰi /]pHAdlJKo_.3cTy2ә?w6115kȠP(;?XJճaini'11AYaIh4j?[t:. ?@RR Z*qcJDžgaX8{,\zٰi aܘ1/34 ̣8N* pZ||{2Q_W ]7 -Rǩop-/o̤F)g/h 7/dx H>ik0jT*4!Wȉ3qrr9f3rY2..\X-|>~}%2J(h4"Nj. $4511HRV+`LJ|\AP8NTjJ D =`gшC D͆(!v1\oXaР6ʋT]K/<(L} R)1 bA* JtTNt+0bq8}Pș5c:^  6u =v'"JILLL|N !`0f\# pS6[ D qU*%M+Gxe;H$R. ^(X,dRqqF~? jp8*ʏ%m6;+V/޴I;6&͎hԳr,f1z=ł\&$BNgx%px1bQTV[جXO;|A7WbUZ#$AF#\[YHP)UdR wGVBDQn͖myg$ bZ)|URiP(\7</vZ 8Nr.@ Bh4 ɰZm@ 8v$)&h4X,V_9/PJ0B. aIJLcJ* @n_5qܽ^?g6))4r1\.y̟;&rA0n|>&X<SZrqH j}+ n'!$sL_ ";qtQ/J\` JF44^!E[(~5us} B4! >91qٟ˵e/Z!\'{X]1)Z\sT]~Aq#q|23T !sW>n&S;vtɒ+.r)-}̌;z.ǃj#--5ׄHjǿ DVVg`olvi_sM{G'Z憒@ @Ss yy(w'q|^-}$uEA3^[۳w?|U.]†[_cͺ <#n&Ξ/h0T*ZTVTDYy-'OEA؉dfdͿ?ƺ )-)Ju 'Of"&>0D¾ikdڔI9*Ν/cA2HOcdf˔9t(m̙94=hHFW}O 888A_sf&0ڏ}LrD UC| ۺB6BHv];ůSX~{mSȝB93.+DNF<&IT~$24ʀGvvtRj$qT,2/6 ݽI31%ŌD̝BѠhn(`JmZ Zwr[̸iB`hQ]mDŽx/]lCWw@0̟;BAQQ!8DQdȐJ%fΠ{zZ]j`0ȪyǙ9c""tJ gl޺bV۶知+WDd=[?`H~.Y̮{IJL`L}T]T*jM-Nr'x#G }OQ>)4rϚɜB+ns +I_Mwn"UHə:!'I(Xƹ_RFf'U`RqGq=ͤ356hͅ6uӾIO+`[aܯ0ys(QD wx23HG.!Jh @P$ v`ѕ$W?5nO RGR 8t(iiU8̟˔Ihljp;}@ŠJGĄCkk2j2FL6>iHeRBBrsﹺ7_3cw0dC#'gn2ZlM{/(t13jy.CSf)#sfkkuQKQ#D cJ=&׽nޗ&Ԉ(hUx.aOdqb^7/J(QMGd2fϜɦ-[6u 55u1ZMVV&&3#CG#0)$99:̟~A%*rX(\n7 8JG3{ d2pŋE.EyE}wyD*pH**J$hjk4!$"}m52Lu~GD B[O W5BH!KҐH%h|Wlz$~7SđHR$r 45-8=u'(QDN [|o"2)5?lo*6| z=WkȳSss,)% ?H%hTRHeb8;m z}7 ET]Q#m] ~SZhb(QDr*Xo%7V^xhh%J(wZ *Tr4_}|-mLN_w_^NjeϾ?x>bvo8 _O(s^MEʪf@pYްC e8DMmݧ^fzpn!j|DQj8lڵ7bEQ$ ckjq}j_i|>G?l#ǰl7 ;U {//lrY8u&,Xi7lkjna574?P(2n.]B rsY>B[{gBy <׿3/C0| k/}Om>5 B47`<[ZZ?u_OCci`ͺ _QD%mE#JdH:`7Nm6;KG˅妮ֶvDQ$FG o u0AhoPHRHSs `g>[8qiTԲ@x]]SKOo/==ijnR[nK(" s^vރ! f;:ٵg/( N'{\ZGWwMM$&&"4PS[4N|>Uomwb4WYG `T]Djj*'N2</Tb룾Aw M͜;WAc83wUk;wc3T" #xY6{좺62QvZv646ԌIMm-m|>~'Nyv>ʾ1z\.7o6ehljWE0&>WVf:p8455S[[7h(AeNCm]=]#}.231-l޲ .ֶvz>Cl,AƦ&n9<}ϐKW8|((UF6nBgW\.DQ+ܯ))Ɉj<}Սf >/|NWw7]]tuwzoh5l(pIuM-mIVff$1m͆jDj t::: %Zx`Y,}d17 gY8].DA@!FAV,Kش!jB"n`0FAR~D/"#/( W|tb̝?( !PJB } 7Iمr{iV^3O?i3{&._!7'F[[;:u ;p‚|֬[o "6ҒbZ,5rY7Ǘa룶.l::0 45PvW^~V!5%N4 ֮g1j.7j\N'ͭLWTÔI8v$_| fΘF f歌9[/+g8tӧSZ2`0oZmTV^䇯h`eӖm+;+W2vL)U/~JG~f̞ɕlhX*}Yd1 Æ2|PZZڨo`Ͼ13gq8x8xӦNaھصQ#Ӌl& ;8Ʒl΂ysض}' ύ| ~9OWw PSSK^n.uu<|FMfF:e媵2L>rdZ4j5s>"-5ѣ"|Ȧ{(QbxDdH rbТ"8Dss P;FGG">.`(ș~뉤$T])=Sgy9v$#G p9rs#뺜iBq\LzZi7NtQS[GsK 6{xg"FcKI#Xm6z13k&+We̙S±'0 ,zp+נj1|͡e|>Vd\.7p)HP(~BB(bhHZ0&~A.^2%%&ihl" AcS3]==,Yeץ?xؘRsIGBgEH%~rq8,|p>P#G3gLmD(3 .w_AnnÆeޜّl^xiAD.Cyym_@RbTt3ƅW!=-`(HM]O=!f¢`k# F`)w``L}C#20`fշ=HMIṧ]R%?/~ @ u|cPO`Z" P bcc9C ̛e- _'Z .E"M[˟6lJQSiim{Q̟7\NGg,\0k733gK0$)19fޑ|f N%??WFvvf81_Ǘ-+n3=t23( 8s\*\NRb"9( %J(Q6TM(fA!Ja\ƨGBT"HX-utL*%66nl6^ǫC,&|>?f L>8^~yr^7`0ȥWHq6f͘V, x>>X) 3#K};V+/#cx>ygPL&Ri*'L\.G"b 1!ɐd|~FɓzHNNrBe JT"BSd rjj5wi%Wkxe9jj,V+`Hg6or|dtvuo_RI~^.N4&3=Cl,^N<^/ObRj5VGku!͆?jYlش5b$"]]ݴPUrj5> wtTJ4 /< O<(XNUk!h$#=ysf+amk5TF| B9-dM$%%O(񐜜\.j0Sgq4xPTzF Fii :ʪ̚1!tPYuѣ"_U7J(Q> DG<&u(/KB6 6h4j\aɢZbbw#?n'!!l߹3:qcPQ%-5ǎS^^IjJ2iii8uLʕ+WI{G11L2[[)\3Q7v Z;NGg'ӦL!$ذq3efg3~8*%#11  +*6e2M͜<}ƤI`;J%SBwO/{`מ}s$'EYy;w不 AxQ#hk`ԈL&G"9)YAj5L8Ғ s̢ÊP*j9}ΰEdEAz{0a,ͭ,ZBzj*ţGqq_(GV˅*N'*1}*M?q'P8c{mB!:;(-)lj4Tq|C,yHRzzz|* Et!##ÇwAVW3 ՄD"!;+.k>x'tdsv܍``yN9ǥW3)) طnBËBAOshniedgeR) ޳3/z[ZR]MFI}c#gϝ2 HLH2X"866FT"%;;^Si))dzg~FRq{ٮdx]n7\ë,7IJG; mN cJKy`8nNO ZErR'N`x<RSS>m*6nᡅٵ{/`cJ;rw*+"NNjR$66TJ(f thtCU#?h^ܸy+>dV ?T*Rysݍ?@!#TDQrx !&FǖI'DQb"FVt7=łD"`0r `5o2_IwDu}?vfP(ĊUk_On^S*w[.^NSO]{̝=+tx,Yyyn'N5k7KϣC(w5k2i"FlonieGyܝ߾#GxxhႻrɭF}@n_5id5dfdbZ8a<o`0k~ݘ}>2߿;% A(lٶG?Lm]8GVks.^p Sʈnfjjض}'z!,?^7kt;CfΕ]رdRM„cumߴ!/o._'vN:㡴nN>bxH ܔ>%J/G{{`{ݔ/ͭ ͎LC"i6yIrs1%>sz.Tsi̘>7 fLgӖm-+׬l^\ysgP(yiiies9^~y;bG;h蠳 TʳO=KWX-d2{ImB0ѨHLX}yGHrsr1}*o=?M[Im]=} /<}H<>F@zzzt|j -ߊP(#xWp8lڲ@Μ;3:_DbccYt>4tZ-fDr{s)Z=1zR)`FZZ*VKP*jH$R@!!$ --m{eofLJVf&>-[#F "  bbiS(**|U BE%nsfk݃F&a4ini?o~!C x;/q9Mw_^NeEH(3n^tr--\KAREpD2x]{ݤB0iqp8A. ņQD%DB@VVH驩!fz=:P0ĀL(ܜ2yz='aAHMK AR߼/>GeŰo/l4d6aPq$ !JC9x9'ZA"7'f}U$9) B %"R A [25}i^'1cZmIHABhJL]$&&׏E&޷=QDK4!JYü*<ŋd̞5?\(Ȥ2^L>VYG}C#}}H$)V%##?ded y٧_ޥe?[G$99?6}}1bp~?1~9KffxmR)/<4TGbDL&c~& `pP:N %M̚9qG$F'6&߾f؄rP̹\%%%}>H$<8.+VE"0q¸/~}@JJ rj5d&R) /}H<>\'OQR<^7'J(QDhm_nw?soP^o`æGnfݠ%J(_=J(Qjz4@ll,{;ܰ}HA>Z6==&(QDp-zzM&>=]]Xck[x<_WDEo0S:x<?oA{Gg(t\P[)5uwE |Ek[wn|~U:;lןֈ bzt5J$$#߾S2%J(Dd.P+՟golae\n7 M8.~ESs (yG ]|z-ط ;va`o|c^]{ٹ{[#"gϝ筷oI3X߯7eikoc7l4DQ+~w9_vQ)PAyEWnO{{mBac֟ߥTD-jͺo-~=>y^m=TTzwl6;k7lo Dg& q)]ܯq+ct|$Z%J(QjٳS'OBR!fϚhmkgdQ^Q)PNyE>ֶv>B#!JHمrM̐|.TThkj9q4̘6`0Zmx}>\soБE;:|tvu{>RS5c:G A'E5jv܍fgdfbj.yEx^8u,fƔ0ijj8zEE<0~,t Yt1j0`6iYwjeK H8t--ΐgXm˘L}Z{hZ~?﾿/z݁ 7'RWᤥ5&PW_ϑc'AT2s4 ڱǖ.alaR.TN]߶}'f:rjص` G!6 Ȏ]IJJer9//̪5먨D*sʘ6u2y\(Vpi1md ؅nPS[SO>ƌiS DW@D%_16jjkZcw8xX~#vʋ w/= ^ hػ R)Yslf*^3=]]T]Ħ-HKKe1eDNȬ3%)1"犏3S^QE_ObI8u,t-elݶ$͝q:ȰErbzDQD3Oev2y|R?С<8o.yܽ'N YavK0~J>kt233ؾs7fa' QWТB.^ĬPդ!"PZR+VqY E,ZÆc\0JIcS3+x'غmP7tø]nkj#e7aCز5|N,ZVRvD%JZJKFsY</]gÓyLF(*)ɨU* DzBIIa$SJF pFCc!>Q#GQ((7$Ϝ>V# "{FDQDӑ` /7Պ祽V++ ~@u`GDѠT*~oj@zz ZÆד秩u0)2Bnv6*<`NNfwAJ*Cb9x7o`X ?8ZZZ?nh4 LFBBǩg)鶤FkϛB@S^Y 2\\.'N_/RRLR1(QTJ~`RnG{ѨhCmƦ&yoή.YB\nhD%JZrظy:Ԕ )+@.RP(?0kj:y8Μ;GKK+cJꦢ"1 )-f( (=CG2rpAׄ/!'<2kj?w6m_{*ȤK;YШդoA D&2I@6-\חH$᧵ ""TTV!N;SUL?!$&$g42`6sqRRinnARѨXL65+W#>YPXHOOEz !ZZ 7(r +ᣄB! H$5P]SKGWF@V3a8FF|\qL}:s륹Q OZg͜΢Ɣ W )ȧI'Dƅj#c..ph4a*\ΐ|9L&rEZ;AI' ܖdR"8r85\|$R)Μ-),¡G!/7綋r&Oz-۶3yD*+/RZ<FCRb"G )1#NsY**/4V)}gٳf IOOx4fİa t`#)11 Ȗ/_$-=aVpLJ B~ܮ o)(7=EWiD0V%7=>%9v87)yC"ABVL%CH'9) Bfc4H';+ ;[ZBNNzDBAA=dg0~ XR13{:s˘3k&y4HOKˍbeHA:BZZ*I -*$;;~&OHfFI`0ĒN#9)Q#GzEӒp8L}!5%RA||Yy`8232'%9 \AbBÇ `l9f0^T23j56m x Ǎ%11FZ-y$''RHLL$;+J%qCWw7RϘҒ} OuzӧNFӑĘr8v$sgϢ ^,f iiN2p2jRS3IIMaH~>#Gѣ"+3#|?Z hkkG&g̙5Fǿ(wx\ygAI*zdYl*8$$xolrM-˶$J{ BA^cHXI!~c)337JMM0udf3I GK|`\a0IJrP<:6h\X233HOW$m1͘FZZۘ6e2Ve9fb4IHARRE q:xRSSQ|V\AQ4zNzzz5sXV wt`='Ofdgs` @Jr #q+ u S===X>S>< `3g1}}UDQN>õ땤2szl"t:+K>H+eSܷ-?~@r45sLnȽ^/=D"p׮#I OLvV֗x^N8h(|~?pLk[IzzT:ឮ߾ٳ p7w6q6ۀw0ɲLk>sI'D϶PU̟7g'ÌD"<|v͙Ñ8!=0cY>z㵯z_?kÕrLطNI]{hjn|~dYfמ}9ru_ZZZqvu1p~ .sy.F# kE'Nn#?,C?膆{rǪG:e2nDaa#Gۯ墢+% 9zlXT@SS[˲K8"+W㕗o[NJǏc՚uHĺT*I׮We)z Kbh68tčV|~*Ԍ7{gQ[WO4ah^ 99TUWhk /7LVZ^㑅35mmtuu))zFc2^YEB^uLFtR0r)I}K5t:rزu;-U{xg Z-.$ q%rP(Dcc.2qeJ%ǕT*Wk,7''ZU֬] >ݸ|>Y2`,ˬ߰TB0[ghnnecOhX <C2#'_d4|@,@->k Jj@9}*3OY^U@NV&`ޖj_g<_K|~ HfC~r**سw~ =:BOO/G m;voc=x<V\ 8r8ɪ5ݞzZ{G' 74 ;qxGbS&MKOO/&Ɩ{d!K_J{>Yah>Ʉ`d._)T*hF[HJJk+CPHl6 1FÇpz+ zp@,YNKӉ+\Gbgϟ¥˨j>Z ˦[f;~CbZÏc(LFmmDeŌbFR@$l6qV;:ٰi =7{$oHLOVrtf/M?xznIB\;SpiS&3rZ[܃48 TݨaISD͍/?!WYU͸1dger,ǀHlB;CpRl2aXh4MVaX0$I oLa\VcX0Mk%|w<,ۃ6Kٻo?,l޲J(0}P8j'M'U2-QU]"fLgݧW2qh{2 v2HOU dH$x[]БhBkYRD#QnMb߯UUdYf¸rs ü1}fhT'4YFhjf#Gp8HOOCVF)?#"0{ JƎ!  V~P**OF#LdYFFQ(( "ྡྷOC* /?;f -tvvT2ʯ^#/7o;Jݗ_bLF#J`$hii!iDyj5ƕev /dBRQV~]{T*?yM6lڂAR`\fϚI]]=NP(DkHZѨ=z<^/. $L\Vhd2r KKVQcݑG"7jE!Ihnee}PQYxt:?O?S:N\.nZӍ<2RRQ՘L&jDѾk$ 4j <26&Bu \ Elxŋ465xҤHL OsK ҀPdefpJUU$%} 7't:bȾ? C펫`XHII+9ci8IINϾF٧bQ\\ɓh褭IJ9wfk^)+Ȋ)U0 @ VNN6[.^gԨ8yUkaYYd1 vWTr))U#1wHdƴ( |> ľihlǖLww7&/7,F.d㦭LWBYUTjcFmNӘ1m*X&ZèQYUd$X-zE%gϞǗp$jعk/v{<@ K,ZR ;; ZRđȍZ*jŚCG]P*${굾Hl6\.7!;NΈ{P8\L{|E$I"/7ScKB>IJaQ 1b:nش)'Rt"Ic߁ttvcˆ-i)Tߨ?z 摗Å8t(p8{ےŖGN-u.Mҷŧ6݃bfȑ]J?8M[qEl6+K'0u?8ٍZTJ%_ʊQHsfd}6xⱥh5k@TUWM*[tv:Yz-` 1koL"BAah$ b 7oQ6u UUlܼGlڲ~ۤu>fCQ2 0d,^\tkm߱qJ8v)ɼ#orٺ}]L8q%cؼu;u s#ǎhx'x<,[(nW^GP(ض}'K}qc)]ȪQ8],[Ukvy[\<O%2;vf٬Y鰨O~&y}?aݷh4;y|`W@8IAHƗfLA1=n7hP(LՍLTJ$#=\.7BAzZ*j (zNhhlR]SC(b=|;frM[a6p$&rz3OCRjQ(bѣl6^|YlN 3,^8Dذi K?BNN6;w`ݧ_Wʐ27nrJZ'ORx٧ٱk~,^E s GcKebrrvHtpVD(̌tR[KK+MF ,vH Џy@, V$ H{G'i4 zP0h4щ=>I @4>A^' Fx2,ZZ`0NfFFx:]-.B7j~ع^x{7i6 cS[W3O=jn2w ZZ[ILLDRHőx}}l$a4xT3 }=x`Z= RNh}3x^͡Wb p!HPlɣ\Bj2yѬt v._.㕗M[{{C\V<lBQsV *g¯D>+VaL&# J8p0u $;Ô_#99t:fh2`XHO .uAnN6ZCbbV zAѠb-HLddz2IY3ذi f#0%$z{]l߹N1GT0cTVY,G);fCoƳk^o! ׺jǓ,^L~^.&<g 1!d #-5#Y*@RH$$, IDAT?GbyMF2BP`HOO#;+L3gLG!Qt:gd2FFՒDzz:f^jl61"?BLF#q$'% x`ZPk4dgK\ Ō`x`A$ G8ID&O*feJf);/Ii)M&237v Jrú/qq$%90̜> F3,ս8djhAHu gx`LP__ψǗ-ӷĨ$Iʯre;Dwzr>h_p,˴wp%;:; )zk+B}Fܨ/ `Ip8Ir81IkYf8ta8I[k?ZAKk+>?pDQF!2@Nzz{iiio[NoWGxe:;B!{t<ţ q=w} K ^d TVUA[{;l޲N|XiY|wtzJ6,s; پc`4 z{]#Z[dZ;!29zǎvH lNee׮V/Aax]HGg'. $$v Vs:ůe\)+1 8صGbg3oV[>BבtъUCFF:dge1}d6l‰qh4^zy~7g6Kr+ͷ_xDC,9vo=,:_.O?`uZZE hmoaX rD#ta1db0P(D"qA!!* $I* $Vӄan(&hlnH(s.FvDhln;J"  B( ϛˡG !WTTVd&##)&{Bn$o?RA$! Q||O?8NDR P(fێCtZ-v;c*)g iiܨh0 vHnP(مlZHV;B}}R) 1kHIJzzN:^DPcKٴe=L&q:Xv=uz456:q߾GFZvE zz^h_Hq%`Xظe+W1", * F,Փ`CGK4*jl6c4d M(JϝCrR?x#ض}'y q6ii*,`X,_(0< $# xj"s <$9IKM~+RB`٣YF@fْG;6mdVY_ Dڳg;J' w۪(?+74== b0md&M,^[[>?OzLe\)Sv8 qqشs=>ZSS9RPl} |jt3~{Fmmiii}ECh4G?m۳2Ah`TVUs%yEAAxH LjCAAx n0{$f?4h0c0A&* 0@6(,5}CAB~I(b\ --Ea6dמt:WɓP*e~Cwu7ށhdޜYdgg13#GeޜYs0,vk} ,~/nFoy>c?~덻~"t2R|N;i?݅ή!si jК~r:"ϥ6L4%mkijjvB5I.O8q:Վ:({LaҟSvW+ OPA;0p' NnNE o;&7'={SU]lf+|xx?̙s|*z5,Y#xhjl6LN9KVV& MMdSW_H}9v;ehjn>6)^Y3jزm;#1M9wW3kt .Q#RVhD_/yx%Q:a}o9?}- V߳Y3P*du YLF!rr;J8fr9O= vro(JRQ($RSRuP(ͥh4`.3t6lGc Pg~ʯCxWdBBD&3z#y7]akig* |GT45ݾ^w0JgHA}]Axjt3~{Nmmiii}EC@P0z=/~A%?7J!ϡW$AAT@E"$xDN<A2QR*O-; ݚ"?;<^/컧X{ {epAas@Q[[;-ZCTY`csQ8ZBR(x{`6T*)0߶H$±Xd1K? ;3|Jn%rY-q?);K/>OvVml߹%uN[='XXK~7h4_z\kk;Yd1t޿ :}GϞcyw{Mm-?ZFf̙50,3/z`5 .] QU ?CYU.^̌S? r2z(:;w%dtuwGK:|@0sel2âڊ$IO{?Q}F޳7_;w =sE˿;/[2! B?b"I IBpVFBnq{PUP*G}}^@oO/`ngϝ# FpJ F =-ZT::;F$$Y`DQ[ho7^PUU[ygX0o.=>s_MTFe{S<1EWoŁC7v,y[8tH4Joo/z,^ġ#GXSϿ$G" _`ص\^?a~Ν\)+ψGo0rd>?xQTTȜٳ8v$U!~uJJƠjyyط Wã, tttw^~EfϜW'tvp`2z}_gAqgCT`2x<ҀB$&؉3!7:PX A:wȲL4%q\vmw;@* =ŌB@RgԌ,x<^t:P0cBRIRFewP(ZFQT)eCo[Yn=6lVHt0 ( t:-~K|\f NR~J$I"Ϲs IDATw=zX,h"Vلl&*$?d:::?ojiS1hmmFyL&6B#R~#`Z Xn, csh )2XvъǂP_7l6(++g՚u4653qx|>-LGł#G`ƴlٶLȑ}{q⥾Iqh~r<^O>#G`3cT>d~yܜltf +V&;;5k?P0JBPDZXj5i1x^L:cxa6:K$/ZUkisߟAJrLa=^Th<~1m`tߦM̪59u,#EEA#ٳ _FY$--@EG{Z֖VHLL rzyܸQèm׮V̌t|~?mmTJLfFN'ZMRruuuh5Z?IIIXfUjR 466T* ~2 G¸\nDFF:|>*5_۪ۛ(?+oh #={zzAo,˸=Z[Xv=?4yD"z].˯r1~[hZz{c^l6qh4jdbz}HABJلJrc2x=ATJ^&>ǃ`@džyyvOgWZD$RBZ΢Vxq܄af3*ǃl&P($$a2taz{](JV >JZr!IF2VYv{<( |~?f˅hVܷGy}^ zhL$a2|x^*5H$6dLV$>k׮GOO/aIII(I0LM&wQ*c#J%q6S&Op 6tZtCb P; )XbgP gkljfD#̛J‘XJEox m+aTTL~~>W E}F76FFckݪݺgukΗ$ %$S(CHA+ JR?@.sgl6qu˹ ӟbJ!##jHOL<"B(HD會Ee[5nC{{=gvg=;LxiZH `Ι b!PTw? "ْޟYnzn> ؝l]D"xGFl6D⑐=ĵ*##jw23o٦( %E1fJKb  0 6$̌ :::0 Arsr1 ىih>Kikk' bOn#xqݨuA8eM" `OIl6`NPVY̤U,f \nqqYhmk#G_  qpf#..OGG'`06m*ܴꔢ(HĬ3uIQmPUY3gF{=dylŪܜl$Iá!ҵ:굱c&106tYIوD"cCrs'D   |S1$ڸxL)@UU$I;-7*ImyG;Q?ד;>TAA&/.\+n7qYOիDpbܶ@ 8n/{>=zOHg8r87ƞ:{*p Nz߼ SQyS7ߊg6*6_ǒEhk}}d3gLR)8ݱG"\¦X|)uu]cE,X0~ 'ivrsEUU4MU7 hF0DU *_ iz͝C8O@UUg;vEdzNOO/5Wj).VV7B!}֭]CnN6bg``Ph77Y)*,>c=@0Ho__ h4M]}m:6 &n7k˧ ƒDyן^@& βL$l d&IyճkO9U_Ŗ@^^Gd]x$*+8w4Oʰs0taXx፸]n ޑN:Mի_"f͜1Vsdd3c4 sO9x^<~$Iq1ytFRXu,f3@]{ʙZVysQB$rsS.ۄs?MfF:k8w"7#+3 >{xHLLi/ %{d[omq_|Eo&75~47:qϻ qKbrg֏1} l揿}kuu9D"76w w'n(T8ms5C2b ½t"7w{,n<f3߹MC?3#_XBX,<AAO$ 8S RP0AAIqNwOVZio7 z  poݱdhh208HNVkV<6Qn"^S} +D">_G b Bݷ?׉CDD"T]E C1<#wtA[{;E\ёQ@yaEarrOz?BAAAxEaܹ3<}hhodC?y `p K/ͷߡ(9n[$Jm]"AA/v!Xٳf| ̛;D~\ys0L$''sh5^{eN>C}C#ӦhndbD̹457cZ?wbKHR5t#GR *ŞBNNVQm̜>+u,?|rwkE2in#GQ8%GވhZI0o Ï@0WoC8F5XpϽEu.US[K?(_~ IFV._ӿr bmD4׎NdP}ie_fDa'aPU|Qrc]x<|=ìZsfc  +EU]SOT=bVwG7 6VZARb"\mm#)))G|\锕084D_?fWlL%tM0 >l.x[~ŋ?>0oXGEe{Ej`68{SJR['Wq1N<M>t;TTUBCc)l1WˣGrqr lߵI~~>Pp9232\smdgsq~7r6s9}-WiĠ|e]EEtuu黖N|vfLƜ3ٵg/˖,uXw(tb]u?̺5Hĩ3̟;'aݕGcxxz~#VZI@D%tAx`00g,^{%V,[АY5k&׭adge1g,E!))V`%LZF\{J SJǎ7\6nX%0LȲLQ6_GA~>qqq,[֒FNv6lfElXll6k<ĚV_2EOo=}D4P8L͕Z6[`A<SR\hdC>jqTM8Kϳv*ttd7JVVL>W9}FMl SKJtt}\a`QSsK<3 PQ}ﻜ8yGw76W^|W!==)tv:8~))ɬ]v{ <+vm*qV+CC./~E De+u)dggb0ϣ86m\fnO!##EQ(*,tKcXYh!99def\_&338"LOYʤ-YLnniv;$&شq=qq@Q2q,_,J DQ.˗.onIDnN6`EY!Ld"//NV._NNv֤hJĔ|=lV._`uXw%--訏7bLke9 eH޸M6o6khn;= $&7ThWH1 qV._F||\_|8yٸ~-KX/ 7oO* j df% ٱ./ikk'''g챸;g- PǬY3{~ޒ 7w6$ՕAA&(aAAnO$ 1*/XSwZ%a H@bL_>0Ah!MD"0ܱ   ½&z@bX>,/gHY  0ш$r^^6_{$LȚIH2GO@T  *BDWn7+$1$*g8=}Ad/ ߣ*AIBBW _r'Td#RZD&9|ExxC:{GMDc3QOh|AC$ k@!g-mD\ ̪Ė0^,P Ϸ;DULˍ"eSm2(Ժ52-z"4yZ (^)*C~_IB̑މ,Nؐ0>Ϣk*ֶ0$ ,8U&A*ľ^9Vs!0$JA4nB!<%!>ŋg߱M*n;ZD$@]HePÄ%Pp] RTDYn Y2 2& CCZRya¿ԅI2˅*N YrC:W$$Gu5vtF \O(Ÿ4 PFt@O#,qOc$Sm<48Rmmag)i{|_#$9aMBDsob5W+|yP HYO:"<'Q?2eD ?tLruGmoX睖0=Q$ak[)s[hWxXeyLd#:\eNr2FvuEx%L~LKr(2(9 IDATQ5Ukq^ FFeT G{Ii_AAx`y,"`B_?E$;dǟvD";p\34pLtgohP} {^:465sqF}>>1~?### &f I6 f_" ~6w&LF?ᖹNY(GcX%z}:NQ,Eʨ2":Jd>7FǣaU$㍍' FC7c\H7%6ĸ΅o79կI4JLR ѹ'5}mCt4@&^I8uCh&¾Ir^a-znvS4$H5It kwǿ1444𰇙3fPQUuz~?ve,[#F Fnڈ\mm/>t;m|o*SKKcŲ_gX7/gqg:d\qix6O%2VNi 14rQp7“ t贍\vjlVXlI7Kh:;èkQx:=>Yr&}t`~26;UƴDyݫ17Y%[΀NUbẻ r7³yjC+;5Dt:G'GVDxiJz%*"|+Oe]59 rĖSO>kp̞2`UUHKlb'DNN6 tuuvbf$BvVFM0F=Kq:ʪU+X?5s澩 O#)߹_D=4JS#a0F^(P6߹1[jD {-7^kw?l^Gep}oSmh?k&οȂlniss> sDUJK$G,\0z7$'a:_9fƷ~c4ydF,f3WλQ\\Hrr2SJegzCb2xG8}o23Y~- ֌͛KKѤv~g4j\x jQ[_MF*  |9ҁ~`7bDfƏ&I^na >'}>hA]g~b?pFjq\A,) D~kF[[;999c'/Y|deʘ&Ocs\A{e9Aao~.p;we8XsOh+p.S>}§w222H$CGx~3AADC|I7/|d|Q;A+tvu @]}:=ݾ``hɑǹXQi\L $p]שT́Ghjn*'=x###vکgg񢅼o{CAbI$ I ttv Ü9{g~{J -Q^/ , 5Σ6ri.U_ph[Grr6 йT}ΏUozFGGp\Hm**6T-[!!ɉ8";v!;; mlGUUVX{ld2QW:sN[XYI84gΝGUUT\"FQEh4zS5W8u,9 {zZ,tttrq*.qD[Mx- *]4]KQZRD__C'!-5t'ya^meɢ̞9ֶXtך[Z;gy9x^  }j+DѡO*K KkH]^Q4]@tu^s& /OCuN 騪C+W02:3HJJ$5Վ͖xGFxGX|)ELFn sMx1,?ʪKx^VXNSs Wj#7hQR\D|\e% PQYEk[;))(¬Y36A ?o.f.OcSNF|>KOwu4MGe$I4?MӐ дq^D,ٚ&OA\D#}nTUאI||<UJ-Sw.^롯`0Hm]֭jzzD"?mw*vC@t`@"81 `4)-)%3#  }).VV/RVZ«/DrR"PUUٵJIqo>?x(RXYE(" ^3gϓϺ5TMjWX8~UUo4LytFFFFX,NmȲLYi Udf`KuHDzZ]~==,_$!<.]! ZQc%*$ _*+;{a*:2M UI`6m/*B[?ʪKc61cgΞ-[IIJ6JSS3Ne9z%tM-#3#eK0TFG}G8{M"* K [gxkS|Eo&75~47zqϻ qKbr](fdge`ܱ.}K/<;=*9~$/n\u ڵ1f 麎"%9w7 *O>-ۓ~?SOCAA& HLRb˲|CunrsN8{J :I&}1,7wAA|>HH- Bw: g$11!'FIp).*=)P(ĕZVi$&&ֆ,ˌxG(RᠨZl6@ܜ\ N$$V YYY465!2V BTT#C&$=2b p6e<#++P8` //P(DyyhDtʤ EUIHx`fTU!++ I#щ(Xrssoּ`pN=DLFnHO ))v%_S)('R2M$ m 꺎l ?=޾ό(%Zƶxh)S 2,-:ߵ~~zj.'y, 9I(Bk[cAAVD"t9!t]#--8hjnč ABBr1 fL&3Mc Pfrss$): h2^/==.`ٌ%pS#  ƒ ߲h vL&JqtNZڍW0*swӦFa42M(R444`41}e$''~. 7MӨoh !8>s+,4M##==f1  {Vl6~V_$% y"CN'nJ d2uSQY񓧈D4yZ-^+Iy׶)?0Bx/>W:G`U|cUHNN"nuKoS-]iOb_'Q:vvk؊{r/GhŬ0\LXuO<84 |(-!_Ν݄l|ۯr).\!++3^ 3st>lzIΝ$Aff&{Cuݴ@0'Y-,]7~K/c1[xǘZZ?OjjF{) kifѪo.xlR7IF_+ PIHD4L)Fiect$ 3ߛB`0->1ī=r:rg2^}?Ŭ33{O?N> T<^ b=!2&p+5/ fiw4VMeÜaɇ g= f%xS1GIHg``ܜFL(@YnOAUq~?B8"i\cȲh/~͝CG|˱'QhSqQ!`%ǝszz' bOI&s9ń#[V4&?=CKG/f+`N3:{Ft fVj=6@w &}/ L<$FdY'[ٵ->a%F̞Ŗm}n;ɥ}Q_8vxL&#sETEo8"M̈́a>Gpp|>l;+/jaZl (R0**YYYl߹?&ee%lSv'|4df;0t:{" Rjf^!0uHLWy/]{{0&p^u8wYWd?o ģOeeg/u YSgYfc!E7KIIp~?/q:l6E$''ӋU+IKK%'; dZY63OàHOMey픔h1ktF#qqv5sӧ97,f3ddj%#=LfϜ̚9,I(*,j2iSKIKIJLDUJJPRTH\DmRޕ(]#y 2I0&0$0'Ao qX2ͨ*& 89_(&6 6FamUxLv#j94aؙ >{xHL_iV1@^jɶ>pnj}cH$2$##-be4)*,$?/ʪKh&q:knI$}lD+i9e޲|q|\tނݞ2XL-+{t]s⟬,f,k7Ť<߁..䘩eJ1bJI 0qs͔TUe1FAAc   H@AA/1K!Lbm(AA "  }MW~]׹R[ʯAAarm:'No&uYG1Uwtr斱@.VV{BbH +m O6X4XQŊJ:̉" lJq8~}c\n7z[= Ʀ[y /~5s/pщ` Co|G} Vp7zZ #階M$ <fPp(£a U y_n0XAm瀄B!N:wURR4m<;wb4 <ر hj{o9.}ԜTtXAoںzϼ9s|lڸO$ ɡGxIJJB%f3f3EN =mD>]wǢG9c:, pH`χX,fNCN'pDpAt]gttwxn6 ÏW^";;P(=Z'$//]gpp]INNfddχ8].0IF _|z1^/M#Аs,fUU;2-!z_kvm#,9h!_^%sU*/ߗX-8G.X2\S]CL}\ac?PLz5/QkO<5! 8M@ ˗-7SVR rR[njIOODKEUNG7i.\…]H$'y٧FUY@$v)絗_$;;!gdU\毰Z)?Hzz*VkkW˺5kزccz=IOK`ph~!O?8r24x핗0ttt2j#ǎDggIIIX[ԓtٵ>֭]=546|?mj!/7GHuM 5hreDY4-A=cH0 )yMdԅɸ=IbQJ ;3IH(mۺb]f[f[;Շ -!IX|gb6^A INJ"Z묬,IINsb9vP8:4CUUȅ  vrsFQ" )תM&‘>рdȯn\w w!W^z$ ' 33@0?1*r|5z1dIͷߥӦil J{G'̓ʋ2vƦ&= *- U9~gϱl钱}R~S*$xx8~|q-&?ŭ IDATCGeph.xGYzmIj‘'uɄ͖@S󍘿nIh{,$9z `ɜxU%YdR U?35hOdoʈu(*%miAn T, {@d͛dMZ!77/¤PN qP8kݝ?#Aӧϲjr;HIN"!!]$';.0ktttpx\ A%  D"(̆7r1S&?n(x<l 6t`e\|ŋc^̼ gEVnX,dfdp|ΙCvV&P dmϘ>.>@L F#fv;.VpD),M}C#F4n^}!;:e`㖭TTV1 ʢ (*K(ԙ3 =,#ظe+N1qxf,!j)q,`c-QI/*r+ Hz IP$H8ܡ}m;;pW{P JewH7]mWy(yHBA;{(yFOg}|~rXЃd|#Z_O>9mjn&+sh+Oo2{LfLko˟9 wWi?psC BQ{͋C5d+ | P⼶<N𭘬*mmdd1AA#a)G7da]` 迫4ٱs2wl&M=~İ{q:,> P"ʐ2ex;OP_b1ÃiF]]=y?pcHKKJC8/oJݩ~?k}D({~( OrfΜt|# =AlAgW7˗.&/7W,D( QD2TPqF|>=Ջl~^~u~`2x7Zpb%YyOLYy9 ߍtYiˮ=p\$'%Gee5,z1 8۽w{/"''r]  & =~,ΙE0d-]lfx|wtpXh!U{=&h$%97|/uvu!~?&-5i(>`0Ėy7ݝБcHĪ`oW  1:a]=457L$rgCSє G ߎIpM 'XWtttARb"G,ӧO Μ-gX@'\8x>?|\[tܻ|)v|^iȲLEU5;vw?XCLF#[L}}#{`Sz }}.yч1>*o.C}}Ϡ6u2Ccn"´SֻL:?(=u ,^ݫ*2i;[,f3)tw wHD8 3 ֶ5462x&fA,"Mdef^xҔ0jď kAԈ3 ^'naFF`08_Mӈ(riuL68L&OIMIFdcKѸ_DCc'M 1I$Ac*ׇ^##=1y455S{ 7sI|~?ML8̌tTVUyv**ʜY3m|$IBQTU5/zEQPTi_INQEQFEY^Wct 7Zi^`d2}Q`0jQ#G1ڤԶ <m3L`Nrxrr.ʪj gtuucZIGqKI3'O1md|>?h8$''1nl!NNdM饽2PU} 9q kss9[~qcl63i[8fu6n-Y$1i=FrRII-D{G-wtp-)7#ǎDnZѸBΞC4lQ _>/mNΞna6}Br ϻ>LSs y9Sini!;;{"]=|#fLvñJg4F~C^ qw'ȱhzy CvA-@"o28a.V͌iذq0q=iSZ:e2>3gˣLnc_Z_O^| qMb - FtO{8xܷ|H4**byDF &dgR Ltogx J>ihh$##cp-TNGFz:͎`$%%D,Ϻ 1;@KkǎRꉏ3zMYt x}>xL&,ddd^ʪjZZZIKNejZ%;wa0H'.=&5l9[L$!5%厯WL _)HLtȪC$Ib|qɇ  pGn,5 224UTP@zZ*'MX;8$UU BL<1nΜ) HFF:)l6 0nl!=\5{֥AAqp"f̠׋xt:,_3HJLw.epDː+Xc#4$X;f0nl!:sc0Xp>`0^A$IHDKx;O97  p*ǎD'UUilh9uwl63yD 3;wk^/[lfY;v `62yv]{ ?tF#SLɓرc'^)&R\\Ďwa00Thhj)L<:   B. <1]ax t1p#ʮ΂? #c>AAA"Nzע?K*d-69l$ ?w-|Dظe+uu t2Xy  o( 3M: )ׄKUͥLYV&㋋G46o7L&`-'N9gӖm<$IK4k0̚9E wH77DV ~i`Esٸy+z[>8>mjmkͷŠ3fL=HBA;H@n׋v)klll49h!:GX2 K,ˬ۰gꦸ ݿ;f~52qxTU ?mh4s6R:::IKMes \AwO{b NOjJ 5(.G[{;qP[A\\g446FGG'+@x;vX;'Fo99ٷ}es^yql6oΨH@;:e70húik>~zٶ?qf„b~>$A;詣ɲzr+= C <uiJg3= }.II z=tvvFA~:ٌh$ vh)@ g,g /|{PWW#Xp>:́{`מ|'g"vދf~& UQ?)FqTTVqE 0Ltwp/\.79g?EQRSSx' g~y S{ڹw=]N*}}}$'%awHD8Gt+>oC%" #F>L&"`i  )DrX,bvb(M Q|~>/(aHMS\aUUQUή.4iHH$`@QT>`#DӢ1G1)2>O8`yg?e9abVv>љ,X,OEKkGdtB0>rKc |򝨪J$!&ƊNys)>?l7u{nYu8p WHgi~4!z+QQVp8p A$J}RqyC㓓OȲ}˗yͷ3HNN"9)WDȒeֻd„L8>\ǔIFf*W_'tbC4tV&`LѸ\X9ؘKOKp }.7D~~)=bbd\NkDey{#G~#$Iͦw/n,8/?p$˂ p @0Lם+  w;EAAQC$   6 xDeUutJH`(tchMMhkoup/ lw/>=\nE$!>ׇ" iP+z'a\nn7r)  mvA說ɓ2i"NrR"H3egx<̜YSdefb68{Y48TUP[[GFzO<It:yчy(;{nZPG~'ֶv<)+'>.@0HEe%qq(BNN)]$rElS|>9;T{yp)©g C!>)۹{/[̹0]kwtk>n><PZk[;lc픝-2Pg+w+(¡#Gٳo#i\g˶7 w8T0dBGG']]ݸ\.illJXEeU5Hz{(.."== u a9[~^"F.^y=8kK4>X6nBGgptKhƶ; =;Vvs/k{AAM@ [nկq)ο,*)/?fNgX;,c.[s޳m6f3㋋HOKCok7Ku IDAT磳8h˫_bE%v;$!dvɄ/I8bcZ,̝3s αiVU%(f"A{xƲh|.TVhimcɢ/.b%=p'K9[~6mypkoMGg'F]}[L `yG_\D||qqM-D":::\u`:;شeO<0&'!>x4M5}#?QJo_^6IО}8t(.oq w JO@tBՂbTTVuhnia݆Mny ᥗXYŊJ6nMtnOM4Xh!%3%-]]0D閈D"\X{sYTVUwH7be3K3edE" q;jأ_7f _|c &I}/Z8^~ϛ @B|<޳g̈i_`=K\'5%9g] D'O lMU :譱lCBnOx<AYp8̺ W/8>r;ww_dɢ>466ŌӘ:y2 M{{MM{p:?Φ>rӦtY,be]]̙5dMP(摇/BNN`6f,Y'3S&s,MM̝;F.]Eϝg%f_ KƂt|S>d2"2H䶾7% |Q3F'0 B^2x C&dIFLϱ  : D3yD>޹P8Lee5I6$IR-`=3st,f ajk/^b1zAz=? |q3pӦd.Y39:Nf1L4}.G,gޚEHJJd2Q8g|㏬"--F XLD"\@"` ';z~ %<أL?\[XQŌiScRSd^$Iz|~m+2yXA{{v7%99V wHh 3#*HIJnZ^nU55. WM BϯLKO1܁dg4FCz: : Wɦۢ;v8xmފhs(-=MzZEEc 0u$L&ڍò{  9c==ڽ3gϒĤc|qzK~adefR2}y9سwHt:Y2xvKYy9iwta&B. %%{ȱ$%:! 1e$$I"#==pI&:LYy9~Ǝ-$`2~|1 sZˡɓ&Qzzz(>SgDȸm0I#T\{LH-MD [hӦLFG Idݴ;“Ɣ]H,_v)ߚ$h{ΡcD$EïJ$[[  |Mknثݻ_>hVSf23v5' kmhXfL6w[ nMA3n^,T5d+4AF-8}HFFxs0L|ٟo~!  #ȯAAa= p4Ѐp!ܠoǨ-d~ ˆ"AEv;iZ; +IȝP+iCAD   H@AAAmD2BJCcӐ}p[-txˮ)  |1 h`-[&.%IOORm-{}˗c2+IVeȾH$k1q}PRSsH$˜1ܷ| wd)QviSUzJP#*”\Ѳw%(٘wӶ M(5  #H@nUUD"t:8O<0.Vᆍ~kcuq L&EƲk^8YzU+WpE;NZj* w c XQIWW{F#@gk,Z[8s, MqQG <RSS8x=uX;+WryBRvUUyl6 rsrp:wsxHOK`0P7>@uCϝ#&&E FV ڕhUb l-iI8ܡr~q,ɣ#w6H:z*Aan inhF]}=/111ǓOsK v{<(tw!99NLFKh%YnsfϢ'Ml2HO,RTVUs8-,=jE_xd[w_e EQc$~~we$%%sTVUbaƎ%5%fdY\Ld6ϝg '{H=߁ُ cr+5Ҷ3qzθH@֏# .~eZzwJ^GLL cJ[[11Vzz{q&$v1 x̠ FgWN' Q[ pOcNjS`$0>]MIM!>A˔-j2nl!` E IJJd2`deNrrcnj „d'?/,{,NFz)$&:Zl18bc4qYdefɬ%dgMJJ2c 󓖖JJJ )IMfdQ{?i4)ZB'$bؐg/BX喑v,l1&AG id?qd6醏?dsߜp P4DU A=!ҭ׶}n7W?x{lh4b0n 62Ñ~NWMWW7 ?ɒBROwwN'ӧ1yD8H ffv 72%%!eeeIUE`0Y2$$/7ܜhɍ#69g y|qc (SվQ((ai/?huqGM s݌ ݍiAv;&vbl6hhlblUx< =R޾>$+c/2ik ݳdAAouN#.EQ`m!+^_Qmh4I8F5bD`   n $Ib6_jN'$IH4FO&`t*#_f=sF/7%0:}n8qq/?PDidKAn?PAAF$   6"FrBww"H.[Zx¸(:}fȾp8o{{~Zhniu>r*JOsghoޯm (Wx}x}_rK4|Ԉix};hȝN4JHe)z0G(!GM692AA Ȱjlj/x͇8ywf :|? .MUUeiS WUEQm*5 {e_DQhEaמ}l1G a6n:={>HEY]O?W ReK9GU4tQ T_W5w_[/LK}o]J{$P^}@o?]OwA1]z\.7@dӉ$I[}I+Ք̘?l1>Sq:ZcXn~,^5;x<22pywxlٶ]6P @wwYzbj.rrX~^z5LF#5jy翰i6.y9t Xz},^M[y L43Kt޻M[aZs)TUeqlܲ̌ "?xY춑$KdޗB{M\atF$cNI@x;i'հhhXMȆ,Cgy(u-adު?V}$H[d# e$% Avf!G#YD-3pϏdb6z:|>?--/pQYd1i)twfkoDW5g$3#)S&QQUMkk;&L(|`0Ⱦ8ddYFQ4MC$"J KщPC+$Q%L+22D#1q\.zb mFϲ# kHĪWw~ ZNc=y # ~>e=ȲAb2LF|QV_`:MVT $0ޠhl6FDQ?^a00,Yeg,_v:Y\'jfIOOMY%3pw5$IZ~dWJF4$Zvv)I͑HF'1@LuC9h[hjD#cI05I i[;jH%qz0&  ޽3J^@Fյ*b! E˞կt"&O8ҥZ]F/s1{zXrprS^_Uc&b!;gnߞqbjdgg e&:ŘA[`%k7= wX<9TPϯ~gX,ap:0  pQ ns$zm!I&Ew  m Ȧ-[inn% bXXЃ$q=t rD$wuuk^|>f )1+ -^|AV&L5Q1R;Q= & +W<@Ccv<\uAEas ˸8ynJOU+1}|O8! {~$ 7[L 5#;; EQ̽kN'-(-%9)1<|=oD!Ϫ w  wW4>ڸxRټu;X8Ⱥ L1qxH'--D6HRb"lc`` 7``m v| YWX QU{Y3K(..SP@   7<$QTKWOu,V+㋋ؽg/˗CKK+&9Z"2px#V8|HNJhX4MeTU߇Gד,ˬZ=S]s]I$!  0"p7n,9Y硩 +klN>CNv}}.|>"1щh$''gʐd$6;.j/_h8 njb0qxHB$ٌ$K>|6b?K4zt]SUUB0f μP5[L ,AA6=+g\E JjJ $1 TTTaPTܜf,hEjj f"Nr]= ̘FeUP{/f!))g2nX26u -\x^OzZ99Y>SF0BUUN:Ea&/Fm3}sRkf  qM]UU;ǻvCfFzwq1tLJr5kFͥK$'% B?% 9vPz ^9H(d!3szZ{\|0ϿCU nJ$a歔#==mTLj/TiVL&Ӑ߻n&kje{~=NJJҨ^C賎0p8 (~U":zA4zB[}vt|G,S&O6,!zٳfl!Y`{7qc {!YC4q>"޻VaN.??ǟV9Yp<~}.VTJ(B4;:8YErR"u 8x>$G _wtd}K3Pι9dI2ESɒ)ښdzgwDziJ)95sh9gTN/`hMhsNz߫Wcdtt"HM319IsSMMžd ~,q907ndbjaشq=LF$`uڂik=nvn߆aKr=!<&~X> |o_E8+!w?mmCEA5t]_ohokWՌ IDATvME,FEEx{Uaddqi/G'pZ\ΊIETW1B˴,zٹ};֭`CȺzzزi#uudYK$I$#EkuA /((R,(KUSA@rE±r3,qz,/ |a8C"' زyw~^}uG@֭Y͚jDUUr6o@:;vgW77lcU;5\s/y.lڰUm4דdEDaC\rх\|:r\]!B''JB8+fɟ?qge@a|$IE& Kuoʃ?JXdq.:L>_'vqх;|OP,8r\9>t:M:!+Wmx ]_,b ~4MCUUGFᖛ>=7رJ%FGݢNJzz,l6ǕW\n?<5Jީe.O ]TW{JǪv0PpC:+U  "!aȁ95r#GF7 J$I: t|Am-LMMg}7qqz^{ Ţxç+ݯs6ZQU5-?8tZ.n\;v`>${gլY݁e[z!_Ѐ`l|CGmf6o@PdEZ[[PK.qz=Sa6MMx ?]r1׭][EQX_xinƹiėzxJ8^LA$IKL+v;\Vv9|wKN@yEIeSOS[SͶ[>Ej={9ٵJ{1?^0_0elXɦ$I慡 ;+k|>p87EE=$I$I!ip8D8[Q$I$IZ2r,㬊BSRGqn n@qL-ugFa͚C$I:d"Ig[nƔ2-O,uƫ[ ~햋>^(.с#IGN+I$I$IҢ- }VJ3(ӿн.0I$I$IZd2YHG9_WtTa+!LNNaZ&r5117;kǑ$I$Iw H"d @Q q8^.T*H$QzE_qhr}>$h AإƺKBnCu)ʳm}( M | J&LϠ*]z1۶nAU C,ѱ1?0 }*b1nMg/ 8K=0q %a={ a S%ѻzG+K{;L9 ֆwLhg4_`L`c8'k˫g-8zk\\8'ǘ>Xz`OTQvy g:όO<76DmskC$\dy斆ZVK|$IJ nECQyx^\.B@X$HRSS&͒L%d||P@EEeQ,1 cuP(`6. !n,ض!a&^UU)JFu "b6zLP,WiY(R)|nA=T^[T])QB3JlajzWvƿw^8$poLG466cO/A2Gc͚ۡ?<0c<\>r~J*+bl߶Wr<_I&S<|ʊ n<.J$uuO\˦zK1mv\bIwD }z2Yx]mnƺ,!xzlOVc#cx4,iVմ\_MI|sK*..QUSn@l/NL5.W: r5 ^bLB& $I9~?@={^crjq&''4Mal|L6K6T*͕xF#055޾>R4 E$## KQ&id|| i(J8q'&%)T,!J' ԰a:|1xk/~믻qbD:N)(L±^295Emm >MǪve󦍸 p8aryJCC$If xoŸ}KTU' /k5q֛֮ocU;O<4-؎Þfӆ456Jgna3-zstGQ}ɞ+lat삽D9CQxhh_siePn=5`SӉn~˕J>5[UY:+O&}b'ǖ:̧Eq\.צf2Ɔpe!'?1.$It!cc|^,arr*B }_|>c;B ,躆8p5J%I466xHdqjkkP\A d`r](uR]b&}D^`MT\ԅvlۊiwo=aqi%()~E4Aؚձ8 I$$fD &&&hmiq,B]΄KŹdFiuM C?סP(04<Ï>@|M}]nSZh)/ hY aPa5?ngu0sE`4ZՆ#1դLFFYn3!d\WSԧrZj{/צ*h =P2ʿF^D^>]ܼEQxV~ۦClJ30q Iz0Gf@ՖRwV]Jc4#9ۢ35U<9:z2Y.-u$I ix<啺Eѱ1I(a1:6F˴]H y/55N,C$&3<e}ztH$IHrP>b>7-x*NU=$I$Iu~vʖ$I$I$iE- KhtlSI[,ug[xRGqn2{t{?O}RGq$I2d \hQZJ+QĎcV1a$I1 `I$I$Ihd"I$I$IҢ]SL0pcwsrQR" bYBrhJ([Yr16> h~/eLBxmXkAwY(7C @c}r+7z8$B(Z`2# Nc&PC$IsIlP2M"\˲BP( LO022# R]r slq(y,Ʋ,R !lή.LӤX*8mrlS*(dsv"':E "R ۶ 8MIJ- ܹbD>_"R,X,?ȱ'sD'D9&==8Stvv! H?.X_%=o?2&pe]TvU8 JHiǦJ``0HMM5 ԃdUm47388Ad @:a 畒mvYPz{hllX>8==LPW[H$Iʮ]~};i8BE) ۶nY8k?rAWb(ݱ?t۞ѣǩ_,8r([l>Kgp,cODccc]洭%KVC7߬%aO;;WiuDaTX2Z$I/ eY}S׿k SO?'a-|Oc*\ow_4FtΆN^xe"G$I$I$ Yn-Oz}KEϗ"?OF#8!/)##< k²,4HM7ʫa]{5m-v]U|~:u{I$I$G( !}m3܂fjj͛7nZyy n?"q+_\J$I$I|)R?>M( &_3==M,qբ*[l.^7Փ$I$I$iy8ks4j](h;y퍷0MUN )I$I$IK9 oC}w~nw|_q~0 F֭]픻[ tu)`}>9>-I$I$IGh6&(fW>^'}"XcƼ%I$I$I  F"gOd2<1*_ {Dg'?0HwO/BӴl6K2N&&'B̋P,M[k mm NgH$LOP,m)$\|UWS[[KguD!)J _X^H P_H&Id2Y\//H9i룶lX*㤪LN[땸<)/qxޥBGH$R(*Mtw.u8$IL@dll|)p\.$bĶmb`ppp(L*Ƕz}~Z[Z@4b(]ضEwwJOOMLMMLŘn.hd* @8$Jfygp0 jkj8v1M!&&'F->S3 %ETWU=4FbmS*BD#R6Eac5%]0/4jԣZ*P~C:+?c9Pl.!}dLh$vG$I RUƆ.W3:6N&etl!Hxv@~Xt&C g~ò-TUA4ҙ,h߇iTήn< btN؎EQg (B$&܄(hJ$r IDATj|^piüEDQyGVqp8l&j+@+؆Ci2s)(!s󟪪8@$I$L:ކmY=v]7FAAUUd0|x^TEtr9¡ |>\r+/E4+>|L"$)ZbZl9'Er-i5Bm4M0 î*IH8B8/I$VF-bBPi\.jka$IVw\.\M]m m31;ndU{+榦rw:4]'ֆrhmif|bĂ1 nkVL&9r81^/5NKS# Nmm pN<IWȇWF,EQhjlq@а!}dA4:I$IZ@;τoZ( x%SSD#P_7m(-g_ɏ+e9tԄ륥j'qx)Ǭ-w^/^]TUjiڼ@ ('d'ϗ'M].S+j)8tUZ1TVfVČ^i?8IQ.$It> 2ڂݕ2Xf99fu}-,&EQhok]cJ$I$IL@SW@RxekI$I$I:D1XyTUTCX0$IL& $},U.u2$IZm.*w4ʘYG$I$I{~R41n}_}&m2Ga-V2`$I$I$wM@nԮƟ}4y臨g{z.B%slڴ|>gn?Myŗ[LpdU{;BoU|{Lz_箻K<G/##yFv{9t/뮽+w/M}}##|G㖛>ཀྵi#N:֛ndꎏϽ[{έp~׿ƳϿo|zy⩧]w=_˯w~ڳ.۶O]yGHTT0M**.B¡}y=vT*M2}Gxnt]U&`$l>/a &ޘ?w?mW} 3e~J3%駠^NM# ?9e?'$In7G?} "efٹceӟO~⺹2^*Dg'[6oWeYH:_`͚t]'bY_/䮧y-7Xjᚮ mǟxoQc6?/|w韉F";$?8B+{=c| w8ٰ~kVwUxjG!>sMg/l|@(_2<<FQ?7!s/_}[D#^xernU.":{ǢW_y6nX,?:BFFxW7'&_k ?i?߫'w="HR,η#\zEg:mrù_;")ԡU~reZ ɥD$INϫeYtupх;F"( s2J#ǎ悝;C5?뎳^||ٲ*8ioJXbrjy8z1(͡*s݆=$`;q022oc! _ǟ.]>GJ".E4.`'m¼f\pUUiomephr1NtvQS]EEQ>q5<EcU;Ox]xç>m9irhyQ, P(?;O'_QݔTM!*"Fy4~me$Ig HE,ƕ_[?nrwN0S` 'UU  }V&&& cG? $u|q?;wP[SCKsoًl޴|0Waݼi#.T*M_?_v -9yR,I%SDi ^<^`00-{U8tjn?!ٱ9senZoΕgX,m٨E TUŶmr<##x^Z[S*dʉP u5bQGx7 8|h9ʟʒ롽۷i"l޴oBg>Ţ|/8?審X-g$I$I{& U@yPM[5ذwnꊹ_WvH$KΥ\<3P@U<Ϊ39eGu2~_PQYJddqx^~?puSpl׶,r. u680ѱ1ϼk`:]r1_v{H|[w /:]z x!n s?p_Ţ|s ~zMvxŗW@Q,ɧp(̺kߵ\I$I$Pv;v޹ԁ,g?r 464{?L‚ٺN-rylvPNLJb!z) nJkx<`|>iZ\nB@b˲>7{FUr<~˲!zULDQ֮YMmM \Gs6VaKUUٸTUS !U(uMSxtraݾ***غyӼ}ds9uo;[~a||삍[9k} ! [EQWVv>۝xؓΎ<<߼g{;S+'?m`6Rަw۷R]GwO=X"EYשsPUH8<Od /8uEQh٠ʲ(woZVRu>$I"BI(NܴTS[s.f$I$IaMi#I$I$IyK& $I$I$-H$I$Ih}"X8M"/I$I$Iҙ[?jV$I$IٲN@8s3I&N&+!kQ( T,طo?c444{+iok}Jbqnq={184fp1Mśfo=2:Lo_?Ul۲qs\q٥BAGK/;066^Ƕmt] pnj^/FUUD5VZEKK3&3\}NYSŴ,8(;w*^m:|޾~.*b깕x};oW}s$Iue4l j+#q/%M&_F8Q# 6S{gJDև4MI$r~Ը?rضe٘iYQ2)J&b T2+o﹗ѱq֯[Gvl,ˢ4Ų,ͷ0:6+_㮻appt:9~qOvj8݌1 L6}Ʊ˟;~uv֮addzL6ˏ~3Ǹ{G7 a:^|lDkk ##+$<{H3[|޾~)lĖ͛ o?HǪv~fs+9v7̭cX % 'E=b SEB8I3!5 _wKGVJ(*}twÑ$IEr@Q('%t6K*%=#ˑIr$R)fII gfI, DZ5 q P,]C{[+Qb/l6d9 33dYl0/ fr@T۶}6^{Mz1$I~y$lYϿ̭tj+.c_GQ,yͷر}^/M >ǹسw?=(^}%@mE(߽uˍږ`\JxW(*wxV̊W8f?gY8cA*E+[!~Qzi$IV\8xȭPqqiYJ&%G` ,UV5 r%d:C2&NHgH&gIΐ+YUUhlh*JT\ ~*!bh4B8D"ajikm!^YK/؉Nyk>Lꫮd }ŹT;p'9BP,Ϯn:쵏DD#a: WŢܾ֭] @gW7 ,uLݯoY׎m[X#G”\kK.z8##}jя}!yf#dl[3m+c!Kh35׬$Ir6)R)|=Ld2$fK6;OS}?Gmqڇ*n7˅fhi(hr86ea[6YX(`&>]B7:m L'xW v~ayn7;V?wm_<nkޱ&'x4]o ͛6Oa&JΦXс:{_lwwKTZqʉ;s7܄(x=>[Da\|lܰUUWVR]]EPı\8s49jYc+0(fƢiɏ9Zok$غ+3c~«T_V"Zu$IEWzsm]ݎS_4MbCCL$Ţ.0 rLl6K0D4$eQ*\\2syMU1 t:M.Cu4M{(O-qEBuA8B mYrYv,Jmyyhq|?{fהR)§ ٤K{m ˯Ƌ/e[|>ZG6-uuX\w456g>Lb<ܳZ,ˢP(8Ͽ'KYn-SS4q{xy4ױz*,7_Ks]]Co2+ǹ\qMQ gt0\ěLf\!sU_֚;v1}:{ ^f̘{>}:==444ݽ184.B084yصk7 Ȼ;9rBX$USC"X+O8YvÃsJ>qǎZT6wagyzR _7>eƌq U<ɹ Gw0 Øg{Y0ή]6#BL$840qpl\(I&ig``40 },bl60tN 3O;^BCC(rt. N ԸTZ P PxRAi ||dĘHMx.픏/Nq 0 0 kjjؿzR55 [R b)mQ*(WwTmٳgq޹JBˬ3D"۷}b0 Ü܎jR4JUc ><ʕ ~uVm0 0 8wl4>&L".]G*BJI&IMMB@WW7ضi<BI,nסVihgNuQס6R0]c " "B DH+T_&AW*C@"_'5 0 0 c @a3:@[k+JNj"M īX֦ s!&h] Q-@+MPj z I8R B *@Z&PM OFaaqt cهWY .M O@`vxn_-E8*_ ²lK)BJ0Zm7$1 0 0G@@6tzbbIOi@-eS*C|_U7r})Ù -4 jsu݇H)|N~3E0 0 }w\Ĵ&q?R/{ ?&k RxO(ʕ2~hM@i&#?ߵҜI ;I.7<0 0 0v\]F>]|_H!F%&bR e JTcg@R(%XWNV+z8;hRDXKu݊]IDATg@ñmzq~E P*ORpp7x'¿/.?]475wL.}477e6<CCٶ}><Euݷ'z.\M?Ζ7rڵ,9\nG8؏RP&O_ysgvjWA`@erhsxc0 ØDZ+\$r]Pmĸ )>Jc*#iYaByuZYmcYZkl۩3 =P\סRP.W%$ZUgdGo|u‹Rή.^˗KaY/J%\nt xgxku:(7 -I"݊%%ʫj n:#xX4F.w1iY̘gof']0 0 8 ៏GEG,IQ 2 F+$RJH!Ü? g#P(rE⥗_a(a7wmmtuu,rSceSOcY6[nӟ$[lD2A&%H_yt:͜ )D"bQhz0JϣP"Qv+mAh ~@,%PH8.rD%8DQ"ȇ>jִ˓,c,e}#m `~ J3{m,ZAO%Q_f^|!-D\edY* mm-܌m:4@PX,EYr9  ‹/1gL-]Bcc#-M̚9NŒT*"MMD"ZZZHׅKLss8z~x !r۱q]\nBF.ץ5g2A.[ :Q,Yw475gv2&^#'rKkm<{ѳjyΒK^,}__fe\.7.@)9~\uX 0؝xd5 ?LBIE#jW]jWr}8\kMv8]Lkm! M.GmY-y,͒Rb;.cWZXEXbT B m65e)#3*S|1FYtɄǓ7؅k.+zḲpX'¤K Vsżj%KF/[75$5,^ ۾>fSOs`pp|-]B2$YݧtvVLxN'ґ-\ɄۙʊXίa0Җ-:~ 0q,:AA00QQ,)IF|\KUë4 3 Hʕ ,55  _J^uB~AT@`H_c.+A)bDln7%ζ#>WS N!W\0 0 Ø:=A |՜2pUı"c0FfWP(;8d5;nBDD̅V?Ʊ%ɺ09%Zi<ϣ1u|&0 0 06UKW#>iG1plR'("Ӹ Hi\yd,MDRw$hT*5FRǗ /<#;4`&kp]leN0 0NUkЀmIj#[D"Q֤#VҪ:k@U+>| X, &cYJe{6k1vflR |yKE5qdst&9)ήRaƈ+ 9S;Vtj]HĸRJ)T(J~@X - K8DqUb?IфZ5 $S(PaaƉu\R*(uN *H|JWS5>r@PTۯVl,+ m e]% rQ"AJ)M*D0Q۲̽70 0 8ю v"́B: @+~ai#QեSzb.ƶ-R5I "e |,vpjsa8< iIXVT}<~!Jb4Jm<455NMYXEMM_?z1T*F.X@$u{}}T*Q,g?s/W~s/ u?f`-H)Gm*E"@k>$Rt:eI goO8%mŨI&q]0A048xtǷK)t{yfPAIR裱FS"@2Jrdi Oj29d G(Ffb5WB iY \^(E\8 HI0tظpH$ŠKٵ{hJ8ztAAnG9gv B=UnRR.3_ihl`wRpy藏0o.X*FdKh7+/ǪN:( ԧ\RO6\{5|l ^[oo_Sjjj=7<ǢE A6W\}/sf澟?ȕ{\u״L&Y^/2bwys9'<7YZk=wmX$f޾] ki>֩Tnj&)0~t?AI&rΜaNЖ,6fQ'iqm+9I~|:уIa_}#,K|h7NM웦aa&ɩGfKAP[Ƴm O<-zdՋYFF\b&Ep>O>\)y}4:vOލD#Rhӵ456~U8 tL΍o`ؠoh(O)%pʕ⦅sfPϯ~(纏\#~||O_z[RT֏ݝwgH.K+l۾E kǙ9}B V>]G67;c\zEOf\Yl) ۃR63g U47/sfU\%p|̞ux(I &3Ɏu of +*L );ҟŬdr>Kّ!NZÒ:ԑ-=TumlnhY3tgާf|^V"Og`S='3 0SeK4Bht^~C ADY3شw?)%/ݗ7|o{̞9֮ͷTkcXl۾{,G stv=ˑ@#]I=Ʈ_C)MǕS3N#;AC4; ӯ8iWXK5l.ay{!aE%W8 yca]R iт)zLA*[&DA.")QJf ĢwtPp(@~ ªRy mc;6mmΆSJ'p|?πX"Q[LKk ˖-?BCCXY|)刃z]>J ?ڰ9mhMF㖒h4Jm*K.c:TgVFy|?Ps lNJJH$k;Ku Ep .O:},[/?]Ƿ3 RJZ[}7?,!(xx>~1 Sd_-Ƀ|e m'x̺;]vܻs0q^/iaN7IQ!6r;M%:C<&y~1sC+BZ4|~=;!m_aSES52-4 MzSؖ’J W.BU˳_xZmGu)ؘ#tzw7B{tu]/Zy/Rc,^uW˲H&l5$xFuwmvj}w>[زm;8R:O{mT䅗~G<kd2 VӽkO=,/d= {L6ǵW]I&L"OQT8J+D\ ẏ\9s`(z&'4t]|D2-\07 /_}Ev>ЋV׷aG-bQ6 hf<T ʊz)x^8Գ'{%lSPEfN .PY1uw%lVzAܤMga]?+W}X_dD"J&B2Q[ezK]4A@TVpd5_CH Mٗĭ"vRA>cQFqz]_.`޽tL>L& jSqi۳ɖmyW'|BPTœٳXwT*q}`6.7m?:2q~U}Co3|aT̘1'9̚2 8=3̪vfڴ7&="}"%D(@@v:Dd@׫I 1WvW՜>q907@ß?Sٺ ׅ B@Zv8D"rq"DU2 0 0&BL$J"`(u⫪ՑNO8^5QT㤈Xw١L@S=d@PB<0SCV eY477gvM8QnS!p/yD<28)\}$|tqwxgqB'i5 xo$0 ӂԇ -ls0 }"t$-:fyaTԉGΏ0 0 0rr9 kn>y۔Rds0 0 Ø [iH>_Gwx'Zvy˘;gxLSO?\}4Ǟ+橧%.hmmR,)X}*jkk=k&{{z޵\.G-[_B eKYj~|(lR=g?OYr$}ÔJ%*S=BzVԩ`v2Ex8õ\E2qf5<}3󖳷ϐaqtqXx{#IqϗJ:@xOo/ %j \֖"o\qzU~kOι7ql~!vyWX{+|<5CW6<THĥTK{)! J5t\_q~P's6 K&o'x<dljlf&O766Xm0LP Ϝ:L&7t:TjdRf}]F Cm K gϜZwi:TWݽwlmo sgNrƛOLNꞛ_ A:u#gϜ{d4?w+ Y DTU3il4U^<42qHeҳO I)fw8j&&"oR鹳 z x</T:m6$ Vaɘޣ0 =x~0%VGF{T8Q*++\.wt j49]b9ݕL&}@@>#i2>};YJd9*GR~P<7 }rhпuMA "3 K ×/]|Px_"NJ ) Ŋ-ޡi(DR,)WO0 [:a`R9+7 lmmOqAp' әN}5* H!d\.{0eهW\.ǰL*ڶvv4bW*2 s2'IP $IHp\$G⽂a/zqyfF"`eKjmi~91,;;إ7/H&pDf2]]P(b1˻nFt*U8hdN[T(aer}/NEPdd2y,$E% GZ&z>T B.+Sϱ1 ~P !T:84 R*#T* Cbxsk;Nk_凰 IRGT~N%YU ƛjGQT U*նJQ.aaBPTb喖&E:bxKSU,[BAuUK;1-Mm:n|rrcs  [w4=44RY8].СPxqii`hxcs+L NLNqnˉ{GV|~H ;}h" 3 ']z=3:>1<:ȽϿDXVkR4?k0Yq 5NkݱQu^8]Y]{FZ[66wvlu>br*ΈbJ8`eu+Ο,ˢ(pǶ݋FcK+wMFc.3 ϱmfSccNZ5>1MLN3յ7oJ̩TaM3׶k!IrrjzphzvCCQT$ϟ=rpP, i}mww>77ڶ K$}htyeT*Ϟ>%J{{#~ۺf08nt|b|b A7oⶶX$jU/ψe2˗{9`oj޼//6@MMuyO^lzQbav巶;:rR\._WW[1 z^*TUU=t&CQƆEwWg_ǎT*%ͭmP׳YfhjkjF @DA*a}8Jz6cB0H47 iD,dYDTEEB!'ri*yVzEb|qn0l2aFEQ%VC"uNK4-ju:YX\uu55B O47 "[,S4 xFшBVv{9+tZ*tuuص{u<]2E`(j*]B.75Օ  W޾֎mDZ!) p8"nUed|y67\JMF^K$55ٞ^QL& x+**5zb1vsg$ﯩ. :Hh5QYY|)JP ˲&qum AӧNVTdT(, zd*qűò0 47N^!j K 664=*A:AQ݁a(UURT)D_}sR,e2jJrmc8}Z)DcRI(B%/^G_|AG._yufڶәS'y 'N+Oz4°7o'?hɫ/e;94LMF㽽/L?x<uwE?N!zA&T*oly@}]lNJ$Y'ky߅o H~^xx {vrNg2) Iy<{p,5˲jG~xH8RBy^xx 0*DRɟ$`E ~H`}oy<;Uiںc; :]յD"IVTzd~emv9$H4d8z}N<1!lK P 76jHdۺMh4maBqnT$I4ă,p_;aXw 2H+>V& ,ˆogoo( K˫6۰\eJ4]*iqv tz,^iyeyuܷB0ò@H4Zef}cscso\ɍOQ壱؃|~xWzx1 b۵Uཚ\8seY q$I>H ,~S ZT(At&?0VgT*f(JVvIJ$$E|czf.HjP(T(F'(ICã"ȉt:jG&(j4?C!0,F'&SP(Iw8_;wT*Hѧu}ѧ ')AX_X$ G/#8˲T*z}X ‚Nd;q ölVӊ‰Ebw8& E"wCQD"QٴQgl>%J4jmn۵ƕյ T*Jǎ1\"ONeV_X04w8,:]. 66+,I0 ( o$)2{>ȩJۮAťJR)G&"Ui15K%ڠޣxL6kttfYXkwD¹Vc;}Gg˲X|۳Mvsumb6?0emMldeL626Q[S}`}n~1ͭmkkiqlvB1>1 "pxtlqiHOZwgܼi~OonY-ԌD"y?p,KQFLQ=.GmlnC-0H<6>/#"I*_ p?$`0 $ $E%\s|>_,a 0(q lP(J |~qgY r EQ8|8IQ\oٳEV5>9=s[:vaic7Ο[Y]d浍T*P+*,xO,Ǎ=}aH(,@TTX"h g4RtThkMgjR&x4?P(z=g?|O:`HO`o+km-/6?8P(0 +J db^u\|SWS3;?eZ=<:07 H<Ш5Pxm}C$3NNNMwaXP80NuU׮vv%IjQTãIe2م%F ) @[۝m0 ,{1^ [[>.]qW&F' oPE~qi)NN͠Ngry**W`\.>B.[ۡp$;}^ I3#c xHug3 \HDQMU~N ucm}b1A"V(@`sgQlOT..ͭh4lw.nkꡑ,kjltm-, ɩH$'0 /-Xw%SEd0^cs ݞkW|X<^,&fN?2b)J:"EEX G"l6HfY TH$G˲4M?X A>xHQL6N3,AIL$O*u:2a0蚛ϝ9_X<ɜ:qyFpf3bPDryptaahA @RQ*X[ۮ`|tZ$z5߆Ͷ_/g+0HH9ۣiD,g6x RY"M'O \ nF=<:j6A X `b2}el|X,r9 Eu:-h, (Bd.QE3 A4C  :P(+J*+,R)JD"DwqL"}H$*4M3]{tpQ1B H%vmMuyBQ&|!Drdw:::L&RP(8 FXa1[,f El7 EӖBu uJPWgydR\VPHdUee[kKewﵵb(j2jkFC]ml(%JA&"J]ylb&TW) ҔJ;:*U]m? GzzNg]]Nlo/_h꺺ƆRqdm^bp\.+ A.nlla8vɶ֖h,NK y__ErtJ,K < T2%4jM.GdL&N*qD1 j4MGQM./ *2J!"fYa$R,AӴV 5| )K>SЧr{u/$w 8#r_h.xqe?r8O~l'˲BA9lEbaq% {zTzcsZ&|R\fh\F-E3IRT[D0.Cw/ q LLMo[w^Z<.ãcߦɩ7ogă)ӳs7nN`Ne?~#Jmm[7u:{~7Up$ط8x}L wCãRifv˯Ño;}j<=xT*JLJZvr3,M@$wun);}\n~qPJ0>oݱ;_|/,> ?~\ZZo4?uzrzϮ 8nxd 961/osbeemYfu`H*$#R)NoYw'U֝`(thc}J$Sӳ'ë*E"teD2QWgZSc,p4Ύfaiee!CպcV7|P*.sg&gJŒlDv ff~qct?%iX$G'&JҥVVtG[k |>`oqi󷷷nnYOuHt|rJ$4ON8~C#cյuA/r_o|~3CRd~aٹ/ql>y /t<BmQ#~7nd2NWUUY]Ur'RiScJCCGkKK>{1t$˲:ıޱ)X0=3bؕ.zeF.ol^8wǵ$aD" vrpdT,8 Im]+?zڮۋ'8]nE~;#ccjQW[;u5HeÇQz_z`A/  wtڽRv*UsSĶukogġ?ԠUTXVWzֶ hxsRtqi?~Ko޺s>w޾ןfrH$h {shyb(rEQJI6}n~QQ/F]K&q _LRo_'"W*2L>w'qX읷3OqTQ,I|?~l1a/3ZMO6\&RUYiq\*1 {Qwx߅'m@ѧ}|FwWg6K|-`8QKt$TK֖U*/~iYEO? X A( 0 A8aEQT*?`d0.|C}sg"Q`2EՁP(,J7nninOy;NS" AvPwׇ}204xrW C!bhA-7"f7^}mf,RI<mmn1 @AvU,BMXP7˰] 47M2˰b]{(J8[wlzn||0dzYP(TTX>V$g9tuva(b Cͭmm-`XP*?;PWT*8n8My~RaX{[+bm0 KR^VueekK@ hnj*a2m-VWUm[w`Fet& G.G\dU&1J ceYV saΎ֖曷ܞmOٶuM ehokA]ՎOoH%>V_\lkmd岞C,v~|o<A"#z{F Ot8/=m!ab&q?DQ$JIĒRDQ U)hL 5U'j: H4آX=.7d11b&}_r{ NRg:]"(UJIL<FZ-宐8g29`XEZ~<ޗ EQNg40 H8n:D"5!{ʽxWq]m3L}nTj}<1X~LGP߾g4Qˉx@Tr9a80 a(B4j|iyuxtlmcSC`ݿbQ$ d20/Kw 47Ïkkj{{B0$ Hat&D r*;EQ?ӯXU*Wk+BTMMS B%ēf}]]- A<WVT>YY[˾0ouEhr,KRF&:[=^ n``ol6!e! #dR&‘D"rY2.D'D"~0FDL%Dy`(, 1 -,Ï>'[|~?b{4]DPjeYAD$ov{A#KtipxT$d*I3@ HR0>ۮ P9DTR&;e7S#Y"NgP)J4MSb<$ffPTqum'YIt&<񧕕wxb6C" rmlB|A"'p$:15zQ #D,>1E>X[KtgG~0H$B?d[kwKaјH$q1bX,b\>F1 0 + ǫLW3>-*l`x󆺺Y_"f rer0=7|d;J2H49\ͭ elbrmOLM Ƒp$ܨP(~0 CpojlLg2}pOScFe~$IZin+猧Dv_Z^tPkW[[\2E"T:2:NQT$G&B1lm}LVQOL?imi^[GK ΗoݩJg3`xieE.O9|";6PMr"~`X][oin$ 8K$‘(q3s WVjARJJKoeD.lm[z|uUqC}LcC{(X]Z^-JDbn~ťx" Vlop' =oCCL" r`($ )]NHlvBJVc_/m%ܶu'(7DM3$I\-ҲRɤL/g*rcsvU},f?t{Wg:\$j;~ں@ 0UU(x1 [^Y-4MML Ύd2yߪ /ѱR:; b ɤ qDrx~00 EIpp8b;:Z?=&&vG"j4m'JewW bg_PN B!KB{DripG{K@EE ;zDTgjðbfb`'T*r!S'&gIt8BA(=p8T(YUPqLTZY]D"ɰl0jinB|>޻FDžd G`8V,3l,hr$ID\__D++,m;zgn' xZ& {~X ]]]- ?~IQt:a?*d4N) KP,St[kd@m-- 1 V_WSgEJtkKs.W)AD,Iew{>u5*@ i~l2C!e%b2dtr!I@G{[S'\nFڵ;޽m$I H$/ ,NJE:܎m7ɤN/zZm8iok5p8dVVEQGz7^ZBn4ns=bZq̯]gG;cM bio? &彚 E6`- %nذA8u8tZ-MәL& 8K$ IRWVX NWaժʊ|.ԸcUUV9T* lcG뭫IeŊ Y~~$EU ykۊX{[+ Oq t:q<:qOչ{`hrj'pPmjlxVT ߋ^SXw:ޗڋx<ދZx<O<gHUy<}sf}HϯX,M-,-3 v`h?*K#cN Irlbrm}T*ݻ?P<A$y`jf0kk8^Y]+OLg2+ /d)Aãc4xgLNT)b9nm}X,\.y8 ,NLMS 'UŠ.aMxxL6{`vnېS+TͭD"q2]lqi%܃[־t&󄥔na;b4MMģ#c $NLfumFwh4 -,UཚDfknj_XZXZr=87o z61׫ʅ8nXv{gj}ޓ˥nneu5L6;37/>t(-k<@Q4 ڱd_lz\>R)].7MS3$Er\.x_'Eky=Ap_Y[gaYTz=Pg9ru~`xkeO?Y]]pBDZX|hxvz?M< 0E"涵b; KJ"OLN펊 +2ѱT: Ԍmn4&&^o0uX,<?PS]57hF&N'oZf1 SV+a*'{34H%ᑱXqP, M̞==q\(&@komwx>4jޞDr˶oh?88`wϺR)O}@Mu<38ĪgNML0t~+ݺswrz6?;Ijk)r"aAjk܃Gs;wD2IR۾Ͷ r>19}̩:jMLvwuY>?T bk2Cp6JG^&)r|risC]>B$I~vvXCaE Z.r8(inJ&S33274-8d2A$ bvn}(͂aX.0k/.8\nS`8NQ=C`vLgGX,.k<8xnHA`@nav#L{=k8-k3x"L$-əD"y© r߾sZ}lbr{g7*r%:hdg?ZzyyuyyF0yw8ZVloEcX ۷;T-yR ;O$8T Hd< )2T?Œy K>C$bFCm<yH΄|QB!r,NE"QNa}( l篩s8 Z9(0#D quZEQP(H`<ܷ;x7:NZ&y EFSH$x>_$4R)1 sxΎH$pxУj_S9͆CaPijqy }ݪ=mzfh0LLMjk~wJٍ[ _ѽ=+& yqI~gI$0], H\H}skO>DJJ Oժ>z~qY!==bWvRyY|ɧmh!Cmޭ.oz}|vmcskm}OAwq\_U.TxQ<[eY[ݡizjzkW\>;o( vzڭ;C\.7w}ǫO?yjlb*;sfrzF"=Q*fLƥDyÏ6{@K"EiJNϗJĖ=ʶo0l6r.j;0 Ξ/KXΟ[\ZvhMɩP( p:aF!D";3;:76^pN( jinaXgO{d406W*h_Ξ K˫Ǐ΁ 48p󛷫tR33C5BZvbFC~PwwΎťGGzX,v{U@ r9}V jjlg2.$}C(T:uk.Ml6d2%q86? { Wv{,@Q[ׯ=)ın A<[C4jU b豞.>Wᗿj4uC н<ij\v(hn_XXmWk+JBzd4V7;ALLNGc1H|>x3s?}.^3tod}޸=t/LJZx'F IQ `C: 2 "St8a24U, `ww?0j;} sgN\t::Q&Jt>_(J}8N$INmBF dz}#w~q @0|@d~8E0 1EFanad!a y=-:L&3 Z $yήR~'?wfjz"uNg7@ @d4`1ϑU4`2ҙFHą|AVP)'ucFRTXt|ZG[@I7߸vBp+BcB RS9kԪbry9%@ZB$j5ǏoJOKO75oݫ*V/7_:`b8A$S^r0{oܺ>:MRCdh0dr1EQTx<1e2)kn2OLl"rAQf8J$}ti?f8a(R.Ie E*?\[47y[kJR*wMZT|yX(d2?jok9<:|pw??Er;ۋŒX,jnnd.Fjk[w8JAL$}'>q{xtރx<d2aLV(Zad2|vV$I> t]V8Ή=L&a(0K+xb$˔JE(Y][o4t>AP2T*eP8T(|r ( 5BK˫Vd0twv0ꪪ5rDRg2F"QE<^:!2_(O>P_/TJR(aأF X$qރ`0tpW#| Wd[k3=q VONφD Ӿjo27d2ٻUʓ}Ejk*ߗ/8O~n__ٵTWs8]Fpl:].w[kK,DX,YmuuX,{CsN}"A윹[N7vO#G0BW{O{2𨵥j0>|4Ƶa0[[ ð흞Ηfeu= ^{˟hs*|Ng׿}bTƆ|>:R*b) lǏo@Tg2X(4wRַl606_r*T8./~y$G\e`*J/Cy Ox Vkٳ"s|6koomok3sX</KB|Plmi(jyet;A@[Ÿ ,4 +}r X_p(lin*JCN]Y]a8J#Ύ`(T*a]BP(ZT` ׯ]ܒ$h4Ͷ.UPB /?ߩj0hoWGKB *T!o7]} E%d*Et*DbH$IiN$Ha/Wr MәL&N78b$矟PPbA`ž]q>_'S5V B] ) NR!p؏GFWVגd0sZIk:n!.7]%K0P,I&S7o B$I>/ |je2tȣP*Zam}Fӹ],2e I^O./OLq\ Ng$#σGRH(W&܇vX(+k|c7n՛, ewvK/ط;v0 7=M$pDQonnA0ͭbt# 1A76vv,;=R!76y2W7?(T)?=݁`( 3baX.x}x ٵdC_=D 4A2l2/[l,>@`}c`۷'S)X\;-Mk,b2m--+-V۾Ko`Jɸr?qa].D"^@REPxxtL\n̲ϯ,jy<[ bo@xl6d6kkV knj3d*NgC719aX<\Y[H; 2QBsuzp`zf6@49=BYGOmMÏ?=>95[g2=qM&CES~ H"|g@z=Rrs`(DTScC ޳ZPF_<;p6H(;-MPxiye}cejzVONrye]+ Q*on242@زgJ%s \~dX_XR*&1 ;{H$Z^^[Y[lQ6 sTȳp8p$ IPAy|"qܵ+P-hL(p8l <~XeCg H$|OTRi$MFarf !02;q>AlN* XaP*yM,&@B, B@$L&d` "0i5jZtس l:IRm-l.N7Qr1 AoqutbC,+ɤFsCmM8*X$*al6[(ŢL*yl.'H(`U(`esu&#(| MgT Eѱh4FP$dXAPN 4 Oz$I,BH& ealr'F'`.VF IDATvux4\әl.bL{#(I|6{ XLd&MӍ 2Ù7IQ}nmcsdt7ҮeZogs~6ojkR<hod2{VH$  JL9߇VW \.GR%I z:{;LF\7{uy}}v3ϧ3o. AþBdofNJtt*N./,Ut'|eb2}( kkk&>_ Fc47a vR0ZMF`Ygfkmn3YzZ"p㩭ho_XWU݇FCmcK+u&Zx<j47f** c]BP.D"P_Wg2BakcX-^Oє*p"#PѮT(JHC}d"0aFㄡ&$sgR +au&#Ev{mMuMuu&3JJUkyeM"Dǚ͙lT@ Z FwwIu&þ!jUMM5 E!R*l6ks{B5 #0+!ȳgiӫzJ *Pi: $ObP,eY?\[߸tA'(dK\9726qmN/C^i*iH*TPe㏘TB *Tv|KQd E`*XX\*Jw74Mt,*TPB ŗR6>9dw*֎RѨՍ kk}2 UѰJDBAO/R4r.[,xPB ^*Ed3 plv~A*DrEj"Nb̩]׷kzsjќNglsL.)J3sCb?|d}$w-O(8{0tm>t?| Nmh R)ǿ(hE-,-ΗEQ[Sӳ]~Fc>.Z=g=-o EQSӳ|>|"4hO,J3s#c0,Ecg+aÎepo?nmFck$g+a/,AGFǟ֐ s >h4661dǟ~>t~XkqbaiX,Ym98߹{ǑH+;4Sw2tAPG"Vx|rjiǻӃFĝ?B/ӐJ%,{*^Y]p u+d2>< vGu~n~qcsK. %-r8[r^{hhm{gW .?'57܇U:+76߽0olm\ngs E?|}q8t {OLc~~> L&;15][Sc4+_֏ONYlA2r9JҋΟwb67\|:Ry}jdљS-͍L?ٻ&sgpNh֛uZkWryMMٻϡ\NRjԪT& U:m2ٵMLj#CP(f(Oaٜ@ hkm>5 ΃rcSTpP&pYmvǁK%SlqǏ }'y95=;H&..[MR,UP*xxX*ONf|R!?qX"x}$Iy ˗^0p319}zpaRQ[S}r-yAolCBz}0Z[ |aqi鮎0I<_qis =$j\& dswS3}+kl6 1$Vֳ,( CήvZ,.a<8w4\$յ#ԩG#ONOt\.gX؁]>R WV0>.aܼJ=q<9O__ /,/;=6>f澲Kŗ rar0 dRP`0RD(ðL& xpM 5&QPu*1kfή2L6$ Vj2A\A(x}Pg2ra۾]Ӡ(JE8 {6[8r074|1Od0,H`.B.olh0ETw-{m8tڙyHAPUUUX;PhrjF,) }UӧtZM&`vn1 &B:J~NY^Y C{Vە˗?27o vaiPA`}}N}>3; ˀ^~;q\>bjkkT ś׮ \ ߄T0EQXރG/]d2RI,(3 , |._`Y ryLJST8AĢ1.]r\.J%|f_gYLh.GwS4-lN&H$!Db3T:% F,iZ*162MC HRT&AD(b1&) 1 (DxayBpD9N.~ Bx|^,CP(ߕ q؜q#2I3\.7Hә xtTX,qdl6ϋDw4 N7^p>p8Bf2yX,(bXAUX0 `<;| ?x b,B:xSׯ^A0L ²z3A\BqpL*Dc1R d qT狅"bh*b@| c>?0,6AP&- q)0eq.`0r<@H@ $ z[C-s=-UlEEZxdtg"GǛ$A HIh4 IbB.g28A$ A$Ib;w5 0 W~  O>c02%T:61p]cfyeuaqycs-iWVוJ $y{ںT"Ju$mjg0EQADt njok-K y>VCR `^q RP$IQTy;A8)J EQ_EC X^FGLR 4 Av(r'|>}pPT"JeJ%We2)M` T*|d2pPD p$\_ (@0\ޝih"$ťex^|84A`y}_%h^\Z;o᱉l&.ãc'K#}Zt:~ c&y-b1 ijTgUաP B~鵋nWIM% 0򥋳 R Eu&SPr鵙9V=ZqgRI>}XRet07>9z;Wz*˵Yw-{Bev~8>tAwgĔP{xt;{fn~A*..-;zf,vWg;w0r%\PpLfv~!HJ֍Bk{HdSuuTʖ=y*x|yeust|T*-8<Ƶ[w yP#uXϞm/4-.-P֙S5P$uxx$qORe0uFC2FQFN$ m-s S3>?qBrkׯ^q8v,D{qQ!i"I$'i p+?>It^B AӃG\.q  O D`0j;[.Ν9rJ%>7_a@DQI 惏 X6.t:h.V $I"b4,{6i"4{`X-͍[\d2y4IQHK yWgPh$iϟ;d20u+L95zeRIG[+þt<E0XOw:QTryH<ҽv!U ,+I0Xm0M&c0 >5zLI GQCV*Z_NI$IK/475@jۿt C8%eKL͞[Xj4ɔVlo#)*!Tka{Oi4AtyrPh1G^۾](Z,b GN 'L&EQ4@{kbjoϗ!Td^KR->T(cq1 %RR?t֑vחCm׾f ,ĽBAP"EA7IQP8>959={ZuTG/M-M+k=z\6;Azv-{Ht/Ym'FsSYyl.APj{ NTW`4Ԯo =W vH(lv-0vz].w_Xo67<pJXI(x:P(DZgc<"wU/V(p;`q̍ju%=Y{_Y}>#3RjR}Sәtqϝ95:қdDczX,zdA7 IDATׯ.qZ=<4z:yBg?Q.>\gVug]Sg~6| x4GB#dJ5ؙ4'~WRJ*Z.hV?5SJ*U^RJ*U^MS0APDL&T%Iys [E[M,f4MrP(D"Q vyqlVZK*xfw"}> R4{M KGRzmvs"So"|l6Qƞ펏3, FE#^|FArdH.af7W76z66V{\3NNNkkK$KxioߠըclgG'I&s(?8p8gv~KRI5nHlW(g_R*V7rh2ә~ZW׮]W.Ν9A>l40GG`>1:<~2ES*h @*eQyRkr((J>_xO-'>̩S[m_l4aa\n4{hvk{h\8{viyfj߸~M!OK˫0 %bQ e2skB_)BFOWg8e(A:b-'S]/|`4 bV6'! /Xt]t?~\Ksf?L756#bq>K$S*`4!D9ΨJðX,o5:-IR?j42# F*TРby9,+W$n޾T]nwcC$}AR:]nҒH$w} Rv: V_zyoޖH&WV׍f˥ ܻT(Od2z17T(g{ibE1 olnjs l3 Rj'/.(jXw]b8IR #dž*JU^ S #=I%4 h7o\ EQE\XZꪩ=z2b1R ńD$ESV mvG\ *"bx fd6q%RJ}=/es/fs>_*2DH.bk4wΟ[X EƖ&EdR10 iHdJ4I4M*%aIy48LTT Ngiʥ h@4MQ R*NE_PWWS@K!z9&YQ!'gf+. 0vrsScG{۽`ZEBH&s<)p'S4 PU$Q Z^W[[EQ<V\.A$EUUQ5 \.Q%HiPz*oin_\D DX,/Mӱx ȉ|cS|T.yX,b0\|@P].FnooݫT.xdF` .0 rKetS>)/ȕKov*JX Fԛ736auvU[[Tx"n2[ + Lr{.W]mN+P(Pf_v_- T*C0ڢmok;ZIj77ɗ HFG1`d^zϜ:Rt1.WժJ}]Jrz}{[dV*}y|dp=N0͝ZZ(Bg6[u>c^TW׺;}9dr3=hinp8"<>pX,`$- \a0ZR[SXo]p00:|h4426}xh[][olmmC{{.J) wuvp܎V\ըktZ)U(Z!Wr榦Ją&?{`2+ @reR) 67Jp8R){:uZfרUw=hGQT.575ƆzJ[‘ACh, _vE8O$1 3-m-XKʤƆbF jk$IoO]m-MNK_WX_/um-|>bݝUj4aD,FQj?wfgo 3'Ovwu^U_"_H Α J1QŬ>V򝇦T:Bk{ԡ`:+rca} 4*U}~iymNOJX$I/ES3X<RxW+\+rRA5(~JVjk>/ԉŧ\?ߤ靝n򇇾յu47X*}O"+D"LFd2v{76W( 3sB|akUTRʷ>d2YB!_][_DSs3ܺ}zfn HJN*/o$S)p66Z]]W*l6%J*UTyE7Z:0+*(2TaI䲮Ύp%)TRٝH4BDi*UTR xz4A< mijZ-1 Seawh4W>D(E/6;>6ZJ"f2o\qKw8]J|skFKg28^bXl6*o$X$bgi!U$Ʌ%<̜ne0`s`4 ‰H4Q_ 띝_ #Y^Yi5L&rdm}ccs[VUϋTJ 3J 4M>g2yåըu d*}J''Y].Alnm=+Nd:D>{{J%YP LR4hL x A>gAP( ós M;{=9 oqX[ߐdSjo PKovܕ[?i|_pS BvCVU֖fn4kk>OWAw?\^]rG j<݈duO|[kt:@"bΜdžZ iz|b3bafn!0 tJ )fweʥcZdl@Ѝ&& 8GwTʵ?Nh=w#H2Ɇa&p\$5HԨUFSScB.liӧN|0-gN_\"ʄVAtܙ+.~Vh0 ,57X˯݋=]lt|[;.`4 TX<>s>kmi[Xdsgf nm.[,[w?<}$]][46V*/;{PP[[3<4E"3sm] dRiWg49dWg/{_"t:m[KGcO_Y] M[ '#CNgOWTV=SV23;W|5LZm8xƩ㣏&((PL0r\jzbj  4=53ǻ:{{7o\jx<d ȍfg糹S'* y}[;rh2ܹw!^g$\xl4](=xV)a I.UT׮nnn2?-JtZڹwqp8nGLLN?{onm7.]pS'߹+eY.]3~U6X,ED"Qe}q\$']:&`HPtF&I$c8 A01 ϝ=#.7fXΝiokmjjTT{C&y|鰘z}\ dd*~Z(EL&@_r氅BA>_hw?lVh__Laa:!I`4-,.ݸ.I< d2 Eѥ+^dxLJb`&rb&i:;{EQ@'GGVVP~ʑ3W=5= ѴDpjjtx`& }}! $=!dёab$IE흽oyĉX,67+T*LvuvT\  G|~WgG<7MfDJfO8. #A KFQ7.7m- zź dݝSΜZ0^*a`4q\$+bl6` X4 $I^x`2tӳs/.\z9Nd2e2[*-APwW"a0ٹyLfZ`(HB.O l6+Nszv yWΓAmGw;;R;NA<o`0lckbQ5jս :EQw?(DQhLf = bo4-L&3qPY^YzBAJU2 \.G.0i[[[<^gGj(JQݞΎvjkt; (RCtݼ}GV&ZO$'&{Qe0`ZZ<|p8{?0k6[q ƎV::Ps [<R_XZhoKg2awJi$=xt|tX*4 A $E$`0*4M$ `0a *(iA4Id P{*UG4 AA2 . a諬SUY( ʎʑ/$TQaIIVёJ&Q@.`|ܓ+$7 )!'.v|R[hbptATN I?6J((s@(t`4<>J$ EWl4ǵA0eUrA.{ɹ=/ 3: RE-,.?w[%ToAB8*'GUCPJOW'Kt* _S4wn?9`nwumo?{OǕQT^"N /ۊ*U|HLVd9FV)l6;*/bE/UªRJ7Ae_;Q:ZJ*Up*UTl6>?MXL&OSʫMv㷧]H&SBJ#bL0\.(>g;AӴ|~U6WVVX{AkKs,[^]N\^Qu򏟌Dߤ719or8>&Éd2!٪jJd1rL6'}<6!|>P(n{\J?˿4- y=Xλ*qLw[W[M3 K˃}_._nmEBA0 _pks{0#HTU6?'S) Êb8r8ecݝ@`ءϯըMQ@Djkm C(  dXXlIJCO"IR.C!0 A f1GL&C$L`0dw8>La h@X,b1J.8}|nޮiqZř,fPD4M@ =56Gcbat< TbCaA F nkm^XZhDT: S'ff:ۂE&ERDb}t{DPD d2U($0 E.>jmi?0z<^>L&l'cFkvnd2C_ IDAT2 L&{;M>~~a ޝݽX,~=RD EQ;{jnaILNy.{`4̖ΎB" EQ hf M4A|> Ȟ.U!'daXoOˡXĭvV޾H(ׯ]Y]x 37H$fy6>9NI%)JҞ\*BaX4޶r7aD4M߼B!O$NkcsdN y<N:f2cSd/~M|tAP$F:M6ck{W&MN%)\`0B.LLNH$ܑH$ Kj@/.775޽%r]"=õ ť`0d2R `o@QͶ-͍S3`8M@EL , M&XH$v*8:|~L6 Tzgwr{q5Q:;cW|pvmѓU7D][߬8\aZLfK>0lsk6ccsԉ pd3 @(B^%mdEs\6p84?zʊ@j(ry F\>C|^Te2-J+z`0XL&d8/dRmE|E'{<7_KB029V CL|Y"4(>6V(gTf0N+HbJ$"ѩ)2M(}ޞn(ζ֖l6+˫k[W[氏3rBpsss{[ZDb㵷@>фqE" 9v&jm WV+lA |~{[FޑEڦz$p/x|7 C0I=]-OtP KlN*64!DcHT(LNxi4d|@QH|~$<^_W[Fv#s>yx2>yĨH$K+7_zpBaME"`02a܊ںD;64tH(}55:?^J@Or9 "H"<<D"ѓ'FGX=Nu/d2fh0:>:9l9u8Eͥ?M&b|>$ɶfBu*reu] p`(\[\XZ NeXƅa0c`I%Ml6{iyEѳOmnm88̀R8ux\i5|l贚ra^_JϜ:Ɣ `_$-h5øuu[;h3 pD&r8lӥVuufU&upJ⮮UBnT*ё@Zr8a0V7StooPT*:ߟH$GG(*4j4M 6֖JET: ðȱ!xC~aq^pHHTnmii Rdh;L6fZ[K2T*k*XTS*hLTt/3N|hauu|$2QI&ZRN+kjt~aKkTqp?DJ_|Ro qT`0pg20 8A,\.$d(Wu8**Ux,R;M6Us\*h_W@pvnݰT&+C__W*-0Ɖёols|JU J*UTy <.WRJ*/\J*Up*UTN ry}skgw$I Hg2G)eq7uvkg7{9c8M&ep@ItrHcrҲ{[جDpV7^JMFST O{}&?I7mT>$g ]rx< Ox<^$=^xޣplvzfnsk LY\JrT'\e2[*wgyk{5HV+-֩ٙL{=^$6{e[VT~[ VWr϶Mӏ&\XLg2~`&5XtHEjҷc2T*쎭H4Fںq=ݝͭmJ915c#IR]\u`4JeE'fNWTxfd6}``4ŢJ]8Bboq0yMsԱ/EQ|x[{HP4}21Ѩ]nwm Ryڞa2@lvѨj.چD,N$s Kfffna0wv @\&ahiybժť>xHQ5-T:7`>ÑHŻɬR}EB!`wn;6861Qf 653H$r`dsvpQ4bF S3sBo%|DB* ϯonlma|Xhl +H E E/ <^SCעH%Dt&<|< EVVV\~<>15={I$].7MBdsmFKB.3M&d}AwWbomH#MMA|~˭j?GV9urjzp$?}gvn>>"^[SCQbJ% 47RwH:ɤbp^_oρL415}Ɔޞ}@*-%B0p8H<۳b i!JCa$V(?p8lK((ٹt&"(A&w2|\m- K#VWQ 3ald}b]]߰;f̙S2A0`xfvnmckh`Eݼ}gjfdN^a`nhfY7oݡ(ރGI&9|6aX06Ñ㣓ӳSt;P;5+\&|~6\|Ch ˫k G)) B96`0, Up4$b1٬P(\*\nI&&IZaSͰ;o~s \.'4jU4K8IR}Nv zh[T47?{bB2A4kYZ 0 I0,Hl6I]8Z.8 :.ŸX<"^DHI$w\.WyT TR)[@T~&MWV׳l2ݓeHT~Q{{=cpX.2Q49]l.bESh@ EB$Jl6ILAvk{G0h.'2b4aX0BDQUJEKSg*e>"H,OQ:G eYW6RF0.#tp` AQU)W/_T% GSl@y2TToy`(T(6˗vv_;viy5˷MLM764ט, gOz{;Kr0B PT*߯ |U?p$"_t <>9'D,HjtښDhZ]]z"ar) w#Zl?S.F3\[Ks(Q*pd:յ^n_Ndsm-F#m-,pr`(H$$Pw=)Vd 55&)  \GVD6U)UXT Bð`(0Mӵ5:&GjU\ן??o?PmMP(,wU_"_T#_/IB80EMlp8FWVkktZt{{֖Ɔ/my*84MJ)H(k\wxh0 XP}Et\~Yb Kˍ Jbgoˆ$MEt:_x\O2R@ alAX,&sA|>_zE*UTRUwPªr\:v{` GH&E8yv9$KqV Br<2 _el hZMq 8#`0H, 6d6* ,6 ~G= n޾sνX,>>9(JG*9dH*ǎ|p'T*=KAw=0[RK$ٹ&>Q,a̖w=xd2>9O=NMϾY#(;bݽH$ds,GO gj]es9hZmbl/vr<3;46}csܺ}~nn$bs5>Qe?˿>Gj~a&_ dfna&_\XXwn}to|;17xtw)ZKJ#{y~qʫɋizzv+bqna1H6 s M $Iڝ;{bHTNC.# B.ٝ.\4S>6߸~T*u|tpރbho]^]dBaxhpqyp'4M d2 q,D"^[lA3NN[*{<@w8\_:RΟ;=%\*>ߥ WW_i:J |?15MQԅsg76BkKs: >{۽iXr;\XZAcvnb]8{ظAѝ=H(JϞ]iΞ>y]ZϞ9H$F޵;\.kvnf0._p|>ښښ;e2mm&F }==d勏UC/- /.0uaqW.V M".w*s!@G{{>CQH$ q|re'?|[[{{7o\b9X,2Q`0޺$AG:@Tv8@p`pݽiC0 C\k/?UTO' z{JR@P{H$<rZr\/,M7^o( b?W_WimvG"0[1 RkxKݹ{ԉ[߻qT*-Kaݵ`0~[o"S C$Gr(P,*#DT*qݽ˗.֔Ke/RS޸~rׯA0dGZ[I "N_zh2Ct|[ӨU\n,}߃Qe L& ?N&SXt8_T"P&InfvE@(h-ɘ-VAۻ{A7o( CR Nj1W.SdzP}]]XT)i\v")әl>vܙ6TL\'w_>(|,EQ+kˆD"N^_W,*J,UE#ѨB.aXT֨GGRB@RT.Xmo޸ubr)  LslX^YloFcJ|/Ym%Ii̩j2 H7ƽ}C80tLlAWWS'vw|?arre 0 YL&[(‘ɼ|{8]n_zyu}AH$skmmDfw$^_W(]S3s|-AјV>=A[;|CQmkmI%S}zmRk0P80毮Jݾ{OTa<}mbYvl|IR~^y32 d2f[ bqmocs,V˲ӳsgO Tlo{V FC݁bX#BwE*|E6Σ쯿/ڜ2B4O-đp.V$}5m#nݹa kc=]͞#{lbioʲ~sÉaXP@4L}u0V`X&x}r,LJ|`w,(s KdRR AБdRyB̜@ LF"{>@pphz}FJH$ C>_*{<+,t5 84ykrdPD`(k5]aEwwBރaI2T `( jX{JZ,\@ (IK~5waZx' CBXr>?P8̲,ð[;*TG.?E ҙL2|X\*aK.u A_jk=|>?r0CZg*E>r/67mrpN,x}bqq8lKsb[kˡx|]]պ޸8EQcX4=3wML0H(xd}98X6;C_,$CR<0%!hdܬ?h4xjiyť7iNg2GjHLg2+kF~bjk9*B<pƆR3( m\wH42,vh4o67Ï:'fkkj::rk5P8VVKcEw-SUXlx< Z$ήbHݼsWTԡjO::: 3P(:\P޾;>d4updzhDba4ͮjwI$zVWjkKby3lh^OWUO]aYhn1}Xz{ Z —_twR_ZY?4fSR{[kჁ ZSsgsܩ˯_bKTWgAD9 BK]x12DBH(``Ash9bQPlm$I^+H2,d*E8봅bJB!qNMMhxy}0\xt:H(d*y,m;wgDSp0$H(LV"|,x:?{fzfP(AdRi0Τ3!GF?{0Li:tZud{0, \CIFcT*R*j#P,8VA0?:5>>fH$ TP(d%55#ni  qDPd4OLTp֖fuZ EѱX,Lr2p d4 BA N=; V  F-JU*L&3 KݞMr\- }[3ٍ-Azնiw-ښ Ѡi˫k<5et:R<ۻ:;~{[ I VUVT rf V P?9=ըy1;7Ҥը12leuV=] 0 ,p8}th,ϝ9hl=54Mղ,[_W˓TmMu}ټl20=xZ[Z66=}VfAyBUJeKsV1LRdn~QRY\DQE**C3!֨U .-/fBAuU%MΜR+b Ѩ? EUEN&tBmM ?hXm^r9m4Z0N/,VUV/1 n(PFm6cD& rLQj=>/A uu)  :XLQTAȲnwBfc)*4Ewq8ښj>ЌlFc4455= g++d>0L]mM>_?80F!VWUJNg4L*\X\ x)Ox<^RYYiD> R*+++bV \e55 V/~ium#tu!.7)ȓG@!'[)S+²럲slr\N.{fq>>o_;(VWUp$r2~ m }6CJXeʔ)S (p2eʔ)Pn˔)SL@.SL2e^ߨ8Gc3sbqg~S +/0 òD"a;n޺FQ_LC#\d%dX ȸ,#{yuČg]em{>a啵|pgLIytrI~MC;O"bxrէas$rXJ,{R bqvnadl `b1<.x9&%Bd$ Xy2¢#[ƭ;w_aնGQz>?8\v{=> E"уÇB]X|v~dfQukoL,y9 7Ww1 ܲl^/O'g=^9<:ܼkYxJBQ h8MLZl6Vq'>%ɼR~9hGQ4 'T*molvw->_*ǙlT55 Kdr{啃Cd\\ZYT [X}rMV C#67E"]/?nin?:rك`(X_wg~P=;0 !IsZ;59=comi?r!k}}Sn1Ǜ[`gAt:35=[a6WWWZuebjd4LM3 {04 LFC U8iNg4NH4AR*OX,[[ۧz=^OcC@`APcC?B JCeكC{ H>475"X=3wp`PX,6=3qf3^oiikr$/7wj*jk*+*wv,=[ۿE-evvR\&S),Fc}z +++FF# Fǹ\WPTB@RFcgg^#|$M&ãc`pai杻Lf|rz~apO&S\N(*.;r{~PTYa|>ORv}OzoVVϞhhG D k7N1`(461uphƆz`0JaZ I?d2\"H$*Tlw:]4M9G3s߸P_i16>yzt~Tb2%KaPܪ Mu:׷% d~fveٶa MSnd2:ffvnskP((rXdݾ;'fK464LMϞ9=4C H$ BCvvV0xSEj~a{r陁>Fewv-P<*=)jk70;\ndt\RHm/䢐/,y赋WFFcăQzQe7x1R*KmdPlpd~aQ$*w߾b4jOLIeAUUfӟѷ5jv(Xmo]ͭL6[W[353[_W PMMuMu-VF8{[Zr g?p~ 8*`0P800 h0 ㉄B'J}~?A`<H0H$ R(FcQ^L ) ۭR*bFig Ng|X,\.W&|~N{2[Ҧi5 HD$d2ߟ*% >bqeB   Nd2,nm*rN4 0pH. /Ht:JxBEG;L&)FJ|~iY1Os"J`F~_ LT4`B\\P$ R* HT:':Jd254: E'Rs B.,h5P82P(I%b%ZU긣*\.CQ4y|DƲx>_&q/J=^PspX;o]"0NxhZfi^`B L!"HH2h0Bwa;:ۂP:St:d޻ p(V(dKTH&y<^P( hJsBA$L&+K>eT*))#2#> H,Ϥ3G,4bQ: 8I$Sʤ2ED,fs~՟G?yӂ? IDAT2 ^@Ք_ _Ucl2E"Q@.h@AWVc@*z}>TL&.56R4 M& A \N.#0,<^H(,L&l_˲Sӳ}cL2/ ? [[Ñ òWSAoqiV$kk_9e 0S"Hi.A C{b2\!A1 6 8?LbAb*,&IR=vAϝyV)S)h_-OϩA}@QTiҸqf K!OcE"QlEy2.SL2e^ _GQ`?]ZYeʔ)SL3sC %\u $ /BLX 6l.S31wrxd2/Wx|gFK5eq48vރ]\6:6P_G4@ik$D`pmo?..-oll_|z++* bkNM|olnmbq)Hr_ݭbgaXɧ7w4j/?8v&0s:dumcv~a{gR`Yƭ;K+:G .* yi?ͦ]+ _[eُ?V3x>6u?S=ӳs]DR&vv-)I\FS]SUxߩC#"hiy<2u\AF *+_t= 55gY߯V)24MgYAۣOS@':OAPfW.>3;#I!o6[o13;}~E8L&Kg2?Μ^Z^D2L&Ǟ)wkyuMm-SA\1)JWjz'96ɵ5Ub6F#W4_(I_~FFHA}kE4*&@>R$N_]"HC}}2JUjqZGXp,kKwwumw?ٍDc<8t8ཫw?P\jۧifmm#󅛷\BQoo\.??}fqxde}M ;w9񱧢, AM K˫ݷ߲id2d0cD :r)~0ƭh,Va6$ֺV,WKho P93 Gj@)EYd2@3,ß4 e"EMFG.\8g6$IdKtンJ=TC}]uU% U,j\D2?8/޿VZ|^,`@`g[Wpz{NEb3ÑJR;/]<ؠi3\&{ymd4\}G/?;,;! B ugN |@8*-òll룱^3}= C3 s||hk=s4 A;_}ABwTω8>PLFén<^Wv{IS3fS<(TI5L&#Jz:[[\LJv NtM#c-=Hwp;˓tNQ̡(H&%b/ix7wvK_!{455#(ZJOaѱqX hbEQeAٵXlko4ݷ-*f 49'}|r+4jEVf(FF'f/;qөhZ[?v=$B0ήAeqr9 AR$IPV)9I-ֶP d jk}+:-<_фÑZ}g> ñx֝AY:yVeO wwux}|D"7 Mef(6:홁~F zmcC[ #4M?w[C#=wrX \.D#Q ^R:lnnlyiyekgX,&S)BN쩞n\ÐT*]X\| څ Ø 3jZp|q<Bp8.^84MD2Y,d\. mmi~6kܙ7o "PR\ف FXl}cJPaLFL*]^[FczV.9ݏ^- H.FW}Pj43s4M2ml u$I +:HQtgG\&p hLNyZ90K@C}ݑPJoޫ0/?DzA+?<x9`YviyBT|RtKszGeGFǏ=SM eʼ",2|S뫯{`>a-'*edo_hs<*a<%,45ɲg2e{|}](yEM o.k "h@C ^̫IICm);cht b(Z,K/))*>A$IbAPɕE( Eq;eʔ)S`1MH4HrZ,rq.",TiR 1 ;rdRi8a(<^PiF! P8R*Yveʔ)Sǣ40LXDQaJK B^,s0,ɠA"fX@($P H$ b:iEQNDO M0Lis2J&S'\lP8<:*dB x^Ùd~)Mx'@nwx<@ yr> D+<˲/V"f,C! @ӌyt0 dG)S(GG a3d>~se@0Ht*~{˲s 'eO(yaFM. h,sfxa#I8q>?=6M4M?.|`(XO9o?ZN·BCRMӉDs%08G|A(:\NέolT*D<;@3 `gpiJӴFOq8nsl>O@ Σ|>`M" 0O*|X<@@Q[;zToi?ztlB,,,%@0.oܺ-[66#R88X,{͍ S3Rdv~qܙٹֵCG>ΎOmTUU/DŸCe bhnP8,‘bApgb)DseNxoHQr\HrB@ x<0PHR_P(&B `& x<(xBEQ>{4H}G/-SbgGŚH$:iQ@paqKϝ[XxU KH(hq|Ύl.wxh_]_ׇFvGss?MF ų\&YZZ1 s W| hXQm*\[o_|`[k| /G<^o]mb`Ts~ˏ++̼T AҺ> & hg"f\.DP`H&2ds9}\672:^pnyu,{|I$S0 C#\F=6뛛m085 %.V395jv{DBA Z\\hCrWV׺:K=Pifum]R:S3߸4<:QxBg2ٽB`d4OSL*۝<#ZY rzovn}LH( B5.Ňy񉩺ښѱC@l6[a6/.E"0=3 rd*Q l,ii ޺3jD"x"195c276z{޼:A#c{(uh-Bqt䪪P4m .;JT\~}aq;].X<37k;X,6{{CJbhx4NtڽҋÐZhc& @0 Ʀ^A`+3Z2^S<ޱo\zµ7cxɟKD_R+c }|,JYhfY{%֖EAkdz iUJwߎD`0T[Sp=T<\.j4B,["w>Aa}=,VViF*47jc( f,  #(RCQeYt:c[2 ~R$Fc`($Wȯ\~]$,[]U =K>әJ q<!9u#>v,˲4Mʊ a|^gG;2@_GML"IQTJU%$HDS޿:<:V(h9^t: C4Eb00, A4Ek5PȲ'*d2iR40>E8 wudl<G4C3ϟs"  z%\"fs4Mc(0EcZr3bd !Z=XYafaE<^) StEK*0I$bq*qÁ 甦iBkٞ@0R) &B[o\V OPT};ZE"!AGc8IJ(]s98q\l2onT6mmi|0d4 tfAbxt&g_4J2[M4sr2 $ǫ*+6G>x*˲+k+@7{`J <5si!ƾzDl8 )wdG~ߙϔ}|UjxYx%GBgETOP.Q] `|VuU|y`0,cU4/7YxۏnӬ= Ux)@tUj>_Mb03)Gb:v!x{~D ?l /=d4_:+hLWcdTjr$=d[};^ܣՈ[!H-E~#K1H߷ ne->$ibF>080=BN4BQqV&P)Ej!(TSIMEUI =hMQRN(:Bz"H1G D1K"l&R|i„h) SC /wp~& :<2 , I)6Hq upR2K*e0 IHAR!0j!@9fֲtB >m ! /i)*aF "6kNhbDD^TS`"I밙!yJKzRRڴrFS-:""hASD 2&p*E1YJt뱿0\M|9aDqx5G"0Ϙ"Z+KN$O5kqvKp@3%H3g̒&;U0$=E!x# prFc 2IKYڡR%DC0rTDMZld $B19'+'鴌m:H1C2YI)P,$2"VT9 /6t'z56@@c%LNHS^5h*ȓVAP`C${$FDԨQ4H,dhJ0d3OupI9Q3fi>M{K 5MǍRIq-diM4JDTXn&I! %*V*PVFy/>eӫL:O CHC=ET0f0KFZY-ZܩǛyzhDst^S6Vq rrC3P\O1 B_pl?'Q@&SI!H2R}QX,E1N;61x/]ww]/^D{WC`n~vMkc+[[ۗ\M&Sk.]Nj5"I&e"+Q9j;Om)#M%4qcY,"% 23)I.N0j)lh V)bQ+v] KzJB$aVF?2}Dr-ywhP+dx۝(a" >b%DAR" h1CDْA(@Ԉ(cSXD9f%F C,P^.`PFa!!cHG}Ҥ$:XEO>FAХÝ:<`22d(LA^J</1YV[[u[,f?`Z;yl$ yjFi"Yz,(I:ܪŭ:ZDd䩒g40+n=.`آfF9j$0;y*|( Y'e(P^c"Sq%T+}aS ړ=?͗mx$yQ7TFq0od9Pt+rCUL RV0("+в}BEKzK(,*P) 䨤O%=Md vNz*e 7*dD*P%آŘfB#"&VC& 62ܒrKHBZiHEdaN**p&Mo}DIBZB]2K"SʽdyEH SAP .FIH^F I PܚHr:`VU*S'KT\Э*%!U[NV@7kqDD"FScQlQ' &N0VtY  ,g @ETG dʭV0`dNJIaFQDPHH@49iL4 (OED0G ;X!@P@8_~m:\R6r;*y 9T:)Pn }Pfi"PD}Lvxdaizgk0][h,ӽr88ƛ Gcxwv8xbPKpefWV.]Z^Yt}ߥ3Fd*P(@3\mm CdeY@wb(n.8}&ըՓS??r*AD1_(Ȓ̲L./ j]_/=3}1D145tmfgYJ\.ܔJ%Ab~payeuaqҕ+˫ uTU*IU,H$4M3\.71yah,66>IywκvP,od++7fκr-/L$~ٷY-|f\6"b.WեR)Mtwuǿ{('Wy? BTRXMMM:\`ШcAr|XTՂ iJum?SM?ዥp-,.?E_۬5[;zlz ɩi1(rP(mok_5bN3}ݱx<{ VVsf?< b(ijhXk<ǥ3@TI&S* ٧c=;w\bZu>_\٬4M?ӿzp8*5ޛ{%QX^ZY?Lkk dumݽimi>SΏ@qvjMMlx (655*O>힙W n^/!ߴ۬uuu'OzgfX]Y]iJӅgzҕZ@_Sc#@D<޹A:r:fW֛l֚+W~S'Olm<ӱX+4M=r8Hxnaqai)?ʊw d*D|ٚ>Fc1C0!$n_|':|ԉ <Lfb:Ƙ9A:]I(i5Z@0:Ύ3Ox갊ei^Y]X-G>mzǾt&j0h*Jbܗ-#IήKdeuu>Y< Nl.wD";uxMu5rP477 Cwv]LFV+rfV6;mVIwWK](J`hZ X̖?F6[[kK8kB| 'P1. %JUtz55չ\P(ڬVfZ[ϝX(6s| 4L,˼Ri}c,at:7v`$eYi{&P_ZV}#VamnZ8!=rfh[Y>X^YeYe^=}@7?Poo?刲YW{BXS]][S1E2inj$a Nd cjuuUձ#Gʹb{g7hp868笫Z-˰dؑ#cκk~a^Z^@Fue2FO8pر\‚ F( j|$u$jK!jeu1Ldw~{p8D<T76|]X,̗,е?NM$ %RcC}89zP6 *ܠ>gz{ZGF Z9`U*sg4BIRkji6M4M,c1zNiZ1 M 9f <`Y5|9qg`YC嵵D"a6<^Vy㼊tP+k륒P,'m/,,{~XU*˽ƌB"Bg26>iwK%!L|>Z,d2n|inL&c ;BېQ?_䥼_}5(Wtj4\>onjpcGxZ\\v啵Wl6/,,.UW9bl}n.[V7~(F=>1r˽&$Xr-|H/JKsc>lkm6oEy79 DA_d23v[F=F]]w7==cSvSSQWVDQbfdt<_,"onoTTOMϸ\`8A{/,,-[񉩭흷yWZ̦ͭ*ǍNs`qyWqȡ.;jGFhjuFj^Y]cYuB|>N_eBo %!{L6k276DFgff l2 J%@ښ_zY$9r,d B&vy2N:t;.a\tZH$GVּښڍͭťj ٬ֿƧjk]u}831u= WU9W7ΎKW49ndtiZuehxvnS }=c.[O6QUU561Q_z@H$-9ק9p~BV6r }#>w[eYeG"}}@8N_׾;Ba|r*H7[[[[[mV\.gV#c =x`x퍷w-BQ8~ Ljk2LUBKJ IDAT0e][+W% b繞@0d^~Usz}wgz`.f\&_\2t Q77)6 ჵ55m{{b`6Wl>p``aacmm0707ܱcGDAwvܩGF'nYukw;o_K$.]f5 VVv;B`{{.Ȳ[l.o[y;r}K˫$//\|M '[X\|esvn~DsK>˭o475E"p$V]7fa!BHcC}4# Ư yvn OLRiB,ͭ7zP, h:z M4=4`mm#e5joMSԻg ![ۑpjrzdmlnǏxʥ+C\ʲo0vbY_X[ۨu!d6:;d2Y=D/2>y8EQ)J*T:-IRG{VYgb2ÑȞkX::Z ES=MQ-w\pIhԹ\~k{g2Y0ƅBqgwnG{|'(2L==]&yIә(=mhxa/,.')JRXBզhx Q3 EQVEV@ R\T*%Q( ITV|Z,* RY#5 ! F?hLFs<銥RT>أS\/oL&^o2^|Ng]_.2,Lv;2BSFI$XQ 6U%YW;;7שׂ(J\.g21,BBDQi "h2vNNe: Je6::P*!m-7L&SUU4(ʙӧں >DHRL=n+pBVcyLF FViQvw]=]]:T~S|ҕmvSO_[Ay$IK+CFS;jB`9ˇadU!(~ppP,۬ښP8Pˢ" 2,&q{gWť*g]¢nw:=H8Z0L{[o;t@__(٬T@;x`v_ᆵY[W[}>A7Z[4fݞNWV1}K+v)EѵjZxhoKg2+k&}euMV˲LilhGL; TWɩ_~.i_t.-/֖啕r[*ޞG:us &[!dqqIV֬67T*cX:{/LZŲxm6[cCBU tjz[S]D) Yfw_ޞn_Xj= PLҬ][hkiq8{oY [[S?hin û.wg{C?ͭV `4˫F,s>_d2} ^Z@,٬&a{eZBKhlin_PTff2 zѨNsՉjkh\^Y9PBU*U tI~/_:08d4}a{Vq!B@^UPK@)Z^,cUS  C.ce 4߆S ښCks*|o*@9ev@ P[[BߊpEezB *TT *TP BA`(t{b5EQIYoL _7y%x0B, qȲ_)A)) %,`(Ngxx|~}cb1L&c2mʔ>7!Ľ1㱉|>x[5.du3sDR ]ho N(ܯxSR٬o "嫄.^а39y᰻{ MߵS7vvvG'}/ѹښ[ |F&fFYEQUmM 0?>tv!_ Ba/_~ Ps_W_6_sK/u:?(IV_/~y`p`aq)HlZY]_\lmiUB˽wХ3_~|mT ",rBFs7׿mv}gwl6)rd2519Cޛ4}ٹݵ:_568пjjk~ Rgyހ鵍MI'n3z~=s9N?JzҲY2Ύѱq^?;7?A(_ 4H$jBbY][6:֊_xg}] k166$Iz}=xzlbJ*]׻}O?Whjj>_ԃiin_fYpTdZh2,MS}=WG* +]x08`F|(ryM BIQQv=pB_~ӹFcѝ]W"2L$zzFy@Kss_OO0v=1 weg||izkoB`ID"Gt:ublbRĖ&e-fsuUՙӧB~oo=4;z}Vv]jJA1ƉdRE jkjЯ^{]Qu{oow2]}xdtcs8XK$S55 /tw<(Jow@;u}>:ͷ$? QUޞLoOboinyͷwO?"J"B_[|v=];7fAu:]ss ?X]776XWº;@f燫Prk2/ԯ[+oo!ڐ L?_+B*TPׂLnNq'J\B *"IRT5Y||?eYQrOxseyk{Em9`o,"Jҭwߤ1R'ʧ5nɲ?ofm}`BȝM$mEEB H&o)x;>N?p,P(y>~((*_bEx*.cl6W>-3)'\|vYEQL7iD(.?VrU >%<#IRo_,>Քff'V)= ǾS4Vwyo?WWUC3lY:wC,K|8F!JyuuRIQ_:^uYT}_4x$A~ӟB ˯t_٭rϏ>4)ۿwO<藻~j1Ees^km}CRYEQfqyahAˇfs9q(.DOwEQp$ZS]+ x`0h,ŧۦ qHTzH!x<< }VaY0=3_~]aYe{IZ_h4_yu_(U]ADQήKT x\Z㈢vv][;*VuB@0 JMӢ(I,Jh,n$i$ˋ+U7VX[,\$Kg2O$~h0r^z;.(/Zy$K@L&"(V@0X( s\<#*jgE;Y-z.!G~e3t\n$9JvvwۿY:kooK$pd4r9gdl=WDQ80},ZoEO=H&}vS}/ӻ.7p~qLF{? &ed2fc(IZ6 RiK/G\5Ǖ\^lnjNӴ$I@ 45=F%Iryz{#hy # U}_ksDds=/q(nm<7tmDEA/]XL>a+(iin~cFіafv.4;ﵶ4xPcCPATjY ,ö|9=}~f(CFX{wX*ݞxlffLTWC᧟zlWgWkRZ Ȳ/G~k{gum%AŒf_ho_d27fg7f bT:\>gV˵ѱC_{MZr9[,E]d)μƆٹ$ɲ,'`0D,pdnal2ML^/JZҕf!s>t*-$pskl[k+126r}M3%A So@, =׻495dz0$9͚vvyK&Sse8vt{bab:.,S04Fs?v86:f}wFF b) _:>鹍kNg^syVC0\nxtaP(rƆ񉩾JZYY-$bS'G"ѫ#`b}>b i!BH662r>B`0LΛL7 huUU9EqP( : 0>1&i{g7>ub//. 0<2mãmp4j ].2XWCj*L%IN2Z^YYmlh^(j2WחWMQfittX*ym6Ѡi{v[&iκh4lnnRnrطwvB6_XdXޙdZ[!q=sh8zХ+W﵇B CjMPdZ{wAw\*r|w,NgtzfM3VUL&{NiS'&&EQxDI>_,۬B縇ΜeyfvNVE1q$mnnDwu76{006UVgFT:LjkkuFA jX<ᬫhoUw]b55hLƒ Xz'{pT$IeоWptwTszn7;kkj$I* f֖!h B`PnN e2':; "Ud<>#x^(Eo:V‘0@:v͖N=o.7~cá 9V{Ɔ\4AUT:Cn{ɔbv㸞KW\nwT2 d2ER+ $y|b?vgaw8^/\res[라>m6)if>MZ7o> !k KY[`T9FWkjo̬mz{?qeiN"ER(3=3===3Mu؈؈oNDѓQ(G&LfhlMRj|~)yޓyͼypbBZ]^Y5uf @T*V';P%JZA$I EQFA&tuRu:-ŮT͖d24^CR!/KD0NKTj|@ęӽ=]tF --*.CAٓH$Jl׾juZ/Q*2' r>b1qռ8%,Zr[[AJr >NOLp]S)Zq" qX:&IZVi4Z(iX;;֭@0tUQkT*&pPub}@! z* q-& Ǣؙjir8MFcR>wf$OJz6׏cFvbȨo油.3Mx3cdj25JB9ϯR*;;VVUJj4OXX\*f,AZ-8^P!d"/O XLPT(DP_譭t3iV+_XLQxxtd0"Q!_0ӳfSS4|>YL&:GQUUrmqq IDAT癚B J `gG{:i4 CQc`2 NUkzA{0jRn3TʕNˤI.3E%ɖfK67[&6]°fllmD&!JY&RV)u2dXnq&yɡA Ó$-4*J[-:y"֖JpjT*eFһׯ9ŢB!;- w DZO0O~<("(5왑D"jشX́`0 W>?VsރgϬ[MMM'O 8~I(=-lXR4h݆(?i_vEJRTJ$}ӯG(YZ^~-˅ "Z-Uu׿~6{P=} f/ۮCYBO H_ bSQVť\J.(^.كIBT*I3sΞT*jjim2ԉ5k$(E鴍ٷA z/*{Km-߅ }V̾tv]y"޼Ҙ}1O WTt^ "rywD9<.L&CpV[^YɥE<d_% ߸h65KbE!D"<.FՈZwwg4 iL4hР.O 0 kԪd*PXd2(p N&SA\@>G$ͮ{p8ld% K&6@Ǚ  RHR^t 4hlD aA_\_yz!V.~.L&}qh_+z 4C;v_Tf旖Wk5bjfsE/>YSũH&)Z^Y{:z>_Z^_yzb819}~4sK{_".'Sn =|4d#ѱH4tg瞾T<lTH6r0*<4Eݾ{AON>59j3bѱ cq{;{\{s _*RtOrͭ:*eW׬d*5[wݹ 5DzS;w=]:o>V , ^/L%D"E--{0Zo}.Rn ťD<9=t>qFC"mnw(}Ab]7nOI%J'&b\&;8lq@ IRVML(B#,~qZsEh4.Jl$I/[76i4T(ML3f*Nˏ>L*/cAT.={ h,.>RY]\SqqyGXP,p$bm8Vcܖe :wvE"Qmu?#P89=88(nW,.! 꺕lNy46AĿOt:Ŵ;;2tv~qqPT꫉p. EϫP(VVd W uc>y[6vv4jpcc/,y}d:@L&|~6{0:ex?m[['sήL*m-˫kwvX,ή;CB!_p[G.z.NK%d2uxtljjr{H\.GP$XկV) L6?|4 W?yP(MN/._|_dҡ58zpnlnkԪS-- ‘h4f25-.x} Ϯ[mku SܽT*ݺsovn!M;!l6aK<.> kj >7otZ,I%'O]V;]s vrfxxrzq(bY*7-;{{fs4p\&յ-6]Tgf(jok-X ATes KM y|w޼u'J՟Igr@/w?p{H|^V[Fcj@Ѵju(`4/.[m߸b bzvnwmnmEcƎ&ffY,V0BQ`"ј\&}0:nݹ'Ξ)W*|޶ Qݳ;cعn;v~;wjjԋKˋK+F;qt씈<.b1u卖fK*b0tSq{{WTXftB(Qh|eR<o‘P8@߽ S7( |N( l@ 888*x ٲҒL77['{ivH%b 0R`gfMJs?f%Xbfi@PTsy ׬֗r+kt:ܙ~1hy-  |>FDi&APrvv}P(x" V(]nD,(J&INk6~eRsSӟb(L#QFOmlm p8V+5j?X$y46b=@ @iBg/\.F>(Lp@5d}=&q{gWTt:rjȽ}@Cf |ldAGDQ|bhd2EQTTRm|rAf1t:A|>k{Y,ljz`XSjA3, JX"s8F@0j5 :;Nmn$d2FR![X0ܾS'8v*ʱӝH&96P"P.MM?x<Dh/cWOwWXrֵ2ljxSj$l[,啮kk]l4^`/v [M wѻ0 \>GQH(BCa.fy\.B AәL&trpd0봙l6pE |  sf|^6ipDՀ XLZrRN32|?=3rZӒ$x<\&{vJ0 #fG"jը\"4upX,,F4 GRt:Y4Jdhп4ɤD,D"0/$S)NWR!h@2H$Ѩ  IR|~b2I!Zbq:TȽ>?T)% lKV$AQL*di5L&+ Rn\`T2%>?R(>(N IxFK"fZ EQFCEB?T( #cx}r>_& >'g0 ֬w~+OP,( yRJ%tX(9rLHaX.ttF$Rə_hX\(ַ-<x"hafRD2ij8Rs9E"!^(N )h<d +jtX\>/ ^r</$IDB_>6-#XPa.l/\L$"+NZ]\Z>u!9 ^ ^sݡp$t $'^JI|ELl&a4-͖oxRaHo^8w16x5yng{MTJJ@&~oakK˭ɷ|)zdeoРA <) PlIZ$P8 @0Jӟ+bQ 4h8^&)]7x'B rL&S`lT8T"D \'_(Xd"p0q&AB!Z&P(CP$D"f`a28\*m"wH$bX 3ժag0u137ɧd!{1T> ǻyhYY]J$+k6X弸:u7.VJd~=0=XnX<Ţx4LރkV \{[+EQ~ |65[7VVKR Z]nA&w:ui /j0 <_}X$=xk"hT-X>WuLcjfb6ML͘_ѱףp\ݳ?-a4XX, ]~!A"?0""F15޻!(7<^^Ykrzb{^uX ?RH$}AnS >>V)}=Ό\ޚm2W2I{Q^~uW0B!BX;;l(joqi%J+S gWJ%^|||l`T! DR#y~olmdrBQԺVTJO4[̃F~zf~3L&,B! BXg{J$E P8 <ںV5 \>J@ 88gzetVVtwuL3ΟsAkK3 [;;,&KRF}p 3Bul9urhv~3#>qˠ ˗^ _(C@0O$VV}~?] +I^wiobu--oٳ;@h_^Y}4Fm['b;oy}BŒ@ kV@mtمB)T6Z[78PN<@tw^%Ο:}7'f-ťe ~2z˯߫oVTZfZDl0蛚@TJbJIL&h`bh0&jrZAPR*PfVT*`dRN[:E0TU4z{k ߼rJ@铑hh_@ H+;çN 1tsbp@T$Ɏko^q{޶MQ$/N`"ai͍(x\A+.38зR֪9V׭^/J}sj `-ukłx*R)Z!Fsbh{JJT*MјLg8]h4615`(tt0Թ#EyjVEA@AI25B0D28h|! u\` B!޵7 a`'@_o6e'i4ڵ"BQT\>v:O|6B|kj'Bį_87L8oBTVX<2rl.Wɮ[mt:eZ-IJ>}JR"Aj۽]gFIyCCCtF[^YkokIRjhpR|@0$J}>$(Fdjc>nb>8:r|>oճ>}FP(r$ErJ_{q ƬVgFNˤkW=}|Nu t6ڢ0GK{}XtڛbjEt:](‘hGGz`AbtLf<,޼pRL"Fb]{C`RF֖ӧHZ(ZX\qRSV նIrY;3\9 ѶEQW/_B$ R)7x|;\޿ :y[w~_d˫k֍Mm7o?~h a>at0yӧ@0C޼H$=/ #m-Vf*V*aNl6߼}"t:`T:sԍ&&z=EQY,:"H.+BΠb[\mem}g׎cx:ryfnaW @rrd0,`R9{f$IpZFQ{}]Es|\axw;gGN;;BѨ 5(&S@i<@Q66\𩓷c26b1tV_5d u Vh\Ξ=q &)ՊX,f0m-< VY^YK3<>&ȤңgG@zhNg Z-_wEQ+Q}'WOGkH4nAQںuhpUu>y>Gtu>Rz>[~??8%LZAAjZ_|)hР7FՃ-פ1}M^0=N |a>6%sA 4ј4hРAwj&I2'I$sR鋒jPJB0a_8^C_M8 CȗT*ͭD" 1 =A~.)J3T H$ pZZ#\./ӧP,Fcu(*JQwm֬Lc$g(WT*]*}ex>_(*TPt:S.ycg: _(;`(T!s|$L6KdPH G"H-Dž?JRTZTTdz'O$`(omBW'JXjJX K>JlOw׍Oo={RyGGN24FQPptL$br'Ƀ#C5:^T66"q{d/s*J.\&;vP(ٵۏ]..H&}@ݳ#g2Y1޺Rv= `~qijp8 rG}J\nH&bl 9@r9z}b(OdP8bptX*MLM (/~JjZJo޾[ro$?NˣTȓD,pjnmi^^]Cع8 )J,nnD !e}Sl6;f9Xx_(C!Jn݀ Eu+HVJAA֬흽]\m6$ 88:r/Pp0+W*b`( #}H$0NgP*AR24 ;rYXW?l6ONTʍmO$wv,ݠik=~:Eho޶d0b Im+=]]+k+k=]{vW,3S3kVɺp>T*wHryPr{Rѱd0uFVx ȳ~cLF=Ӿ񸯈W*Btzvw֨+kJTJʪX,><y b/Htw^.W\.}C櫙jf#`(,::?ӧNb8^±$it:͆JiY,˗x( z.ѱT*].W*`42+Jw?H@9rLSqgwW"X,AP_rf`T*Xr`J%@4:6Nit"')/[m:v%%xp8l.Hxt:Ax67.8_7NJh,.i4 IaX"rϱ_.;3DdXEja0 j5>GFNs \.Sp8(bZSA 0Lӌ R)Ha`F FXl6^UJD"yzŵb'|ðbtbhq z@d0\=15S*L&ðVomi6F4"Zx>0R\& 8^rT-zX$*b CL&]Z^55k(`0b1ˣ(:1=SWgX4 I E"0"(Jt:ΨT*Rf7}w<=;?48 RT<(aصܞcrQ#*J +Y&6 0`jz&KE.Q0 /J'pxltD]?S`tLVP°JK@wa&!:/ϝ?hAa$c8a}=U0. ^v{TJe$6P,aFb{k@ xΫzvs{{|bw"F: z]4(#NE"QB#pWh4Db1:X$2vG]dzT">R) EQL*j5b65<l (4fSӞ}kAJf#"IJž@R`V(O h4E'O T\6lڳۛ-fA }owJ|;rY6j}Lh`X;{yFb*P׬7 O:YTFOJrϝ~)G`h`Y&SL*F&xU*eż AܵS[YVr9b2T*eg{J.--6 'R cY(MM0 R'tL*AdvnHCPk193<9lΉD"IRdG{{B SѾT+_`1 FcXfh+r |>l^ZY5ul6:1n4+ .pQjoka( VX\ 2T!3ٶfO$x\^Kdtw `eDc1S~_$v+@0h?mmih :XLMB\)MMdVtP8ƥuZ^Kl lj253l[KReF p8~mC*͖{}~Jd4|TN^X$jXR0 :EQTBg0.ɡp$ Nk,ADD Zvtg}w~ \^°֖ٹޞ&/'/KyZ#3茺XK8=;5f('/LZٔe/U` pdk{{}~ zdߊs bejf _vu< qjEϝqaN9yb(L(mwod>|'^b~Ao/ "} Bs,PT `o ّL/]|t>kμZ6hРA1ҷl2MY 4hWȑA 4;5G3Ym CQT,)VLᣱrr{hw<ޅ$P8}.lϻA($I6 A K3sA<ymcsem}L=Cfk{q)=ѱg(C IrbjX,:]lQ>~/aӳsS8^bJ+ųնQնwvjcu9J}ɧSB[~U7X,g+a-,Ca$}xb|r[;Gw*1d4,~c%ZxOw)+djeemwgg 9oHrX,ʚugwORnln6 F0ZXZv"pqiᕵusSSwWg>_xh<Cј}!JG`(Q"IҾݳ\s?etlrk5iǛJZo߽_(767]n/s \N{0;|’loynt޾sR$axqi\. 󅉩`6޺zѠ޳ۏT*f> BF;?"|]!RI&|<$r.L&fk4={Vb*V#BFJg2¢\&=yrT, h,vojz7jDdƅ庂w:@yOQms3~dV[XZ_\z9N[(T骟)[[W5&jںB.;uD.x}4l0ȒX,bYP(p `ٲ0>``ho+GB|fHDd9y*!eJS4N1V~>p'B"rMǥ}cYq +%DDx8ݼ~ iٶt&P d2, H$l.Dz,- 0 c?NqPT,,Ng`aX6 8h IDATcX>'K%.pҙ/p.S4E #E9( GeY.Jaq8A@ǰRAPyML JA.EF0ǰ,U0W,QMg2 N0bち_|o|E I%ëB_7/llnUUik^^WX x3}BpX,F_gKCa)DŽ8`pPB!bX,njlU*V#Z277IRSm`0jxϲ:; 00 8Q$P-J8,{Ύ\>8NdEb,,ˆZIoWJ%_f4>95;g炡A{iatWV;K˲ZDQE}tʪL&{>#4Mjas{DEQ  r;tr9}G^_ӕ8N"Eq/TpI$,4(Aa.p U*%J%XDQ '`._[߸{arƆ\.AABaq;BB.#@ w~t(*Ws< r9a2)0dȲ, $I >_, \.7, U^?1*wG{d 0 4],,Ϥ3+ښrnlnG?d3r喕EDP!K%9t;GFǫ"I0\* T5Fb$Gm\R"E|rG ;; BP,}~?'zffg-|>ܼB.gY n߹wνr[NAX0\Y yǾ (?d2QzY@!Y9>2໪\R*(`Yvai6AC#</! hwdk]1 S3x}cEc]~F(fhʤR۳LJǏ}t瞩TcS^AMǛJ& gϜ xo14ᎎ6,a^Pwm{W._|<0jm"b%菄eob0̍kWgfIpLW'fggE*L)B00<":;ږWbD,Ig2ѱt&93;7[;g6ko/>926=3ro=ly蝝[]SӳBjs~G{+M3߸@$iv8]BarzPl{[ή-]|Aa?9~LT?dH$ʴo8=53 A 睮o~[w*JC $Y<~updZe N+ !oJT45jR41Lfbjպ:;># %b+koܼ:8MRbBtˏI} L&ݵadhFQ4 uVkU{?'uuy\xss$Y!E AѾسYq,fG?9) PC JήP$x7qH<o7_{a,X"H[t?|mZT"I$Qmk~ѝV}A x^h4PC]ܬ+ B*fsϞD6Rf-sgN{}~N/]<,M3'uZ#Ѩ@ (O,+JmW1E0T(2lUvC}'ǻ K@0LTBQTkw0 K\vp INWD/^8T*N? A,,:O$K]]L҅sK&Z[,tˡi >:SZ*pTsapl6{dթuTqr'&)9\::Il{QpІzNwpVU5mVe(DQt(plsscgGsg .Q]mTlIُvdڶaCp8Q+kPeǺtL&KSUnߝSSm͡MEXlar֖S'TdsgXY+$#xD'{{JRhT?wfϾ8q.?P_D}^_2.):;F 9}N\.WVX8NįbgO m^,5 UhA(2llrr|b׵ZkWZ0[֭d2 P(CTıTk4 LǺA47JZ,Kl\A:uw`hDRi5j>CQT d2O47I%_aQ( 6wm{hT_ jbX89>6MӶ=B.ojlx`Gn7H {I@b TXFc=Ǐ9]6Bh67I$b'Lrl~qaTF E[kx(I%1eY Ų!-fgEss $I3YÑ;}dɔj48Nj!9}*ꪪ:\&Z-J  TVVL@(Q1 s{bW(6B!?uohdT.몪x<.˃ hskT[Duw Zm2ոX^|As8 ޶ghb)+8QgdHA X"AQ^Ahnh_ZY6|pl:˱d2i(a0n1H=^H($)bQY*몴֑BϞ9nw8 AF@Ngz B% z\<8T*ilcX22Hᑛ7/,&IpxP*ښ+S_7!e7 烦w?ϿO:xx1<2: wwXmC _XٵX \.EM/@04<2oiw jm*JNAsS㗡f|r USwT*TPBW7 *TPBePB ^B *Tx|`a`( @6m~a''S CGZ*vm{CaNfye;pG<^!;k`(۞={yC8Alnm=&H4Ze{wm{DeCDQd >Ye񉩣 øvm{Ei$ɧm%"H9Ɇa‘}յu/#(*gkHy\.Dz*ǎDwb8tzum}kkBG)S(*QoawwF,ˎONdv~X, X,~X(nҙL ˿i80c㓉da|>٬}Fx/ǞJeǻeQd Fh4NjFcz0 ,nwl6*e6G&ٱRi\a Q$!\YY;z] #;`tP(T=~Uvu}cm}S$ 66{=<r8|>rj&)ťͭmAI3̾É ΎdYEѱI?u<|A*0OL-.2L661Y*XY]w:?/(F9qܶg?8ts\6WL6k{d*p8>[XR ߾XwW6c6G"=?NѱժLijbrZ"ťH4<6^[]=0eYrjf+jjqy%Gcښj304l4l{6k375nT_nWgRڵq\.獛7X_eJ'f3] ^x3\ vvtP(q¹.?P4ٵA'houZsUHaAF-+k xᴵFcq.)Tm{K+߸64<?P/N=\>42FL6P(T"Apck B2'_Xu񉩦Ɔ}҅sEl_\ZG-YERX$LR g׶AIFr{b(OlnoH>6j* !0Ng'zc6gu:0'*oQe77:h, Jϟ; J%K˫7_n1cҊr{ h.kllp-)aLYQ5Hb/H@cHR :Vz<޾ss ;]7]x<^*g{{OHLi'}'E `(d4˲$I, 3nO"pB!g$K656*+/r=>EPuyTT*:;zzHINkԨT7N(0Jt{j5\.ð?@(@* #0 ݳ몴Rڕ˩Tӿ|7mVH u bb#G.aO!?|UssZUWD"b>_!bq JmWge.i`/UJ%<^B! ffAsUZM^d\62:P(vvm|A0 F4C~d,A*rbx0aHM" VT<XVFY,Ric7_EǮv(*ӹ\T*;bN&SmkG*-"EAD`>gE3,FM!UU-./T!*+9"T* /4 @,4]V0L=jfPX(| A Jd A>oh/W~fvXWg_g2" zNrL$ 756D Jzz<^=ݙL&ED$˭vי/ϝi+q ',fjnjR*B Z[:76ꢱh|nookjD"pQkE$^0 C֪ը :J$ֆBVIgG[E"Q   "H$ Fb6nFQłV3i5:S -D2lwWg6M$׮\(JӦX|pp`XTheu&qwwuRj5t:SmV.ՙj$ɱNX,jVh4p9H$Jq\ϝ܆a̩-s4-QALABf~λ?EQ$I|ҙ rMS3sd bl|ܙSJh _5XreuWNDQT ^X@08}d `X:0L_s0=3j K='^9~GE B *TxT*TPB/B *TxTEIM>;< IDAT~q //^{^U/?r)<H3n?4MeOJ<OyFtuT*˲(IJ}>ߝ{ ;k=q OT& :]_*}r4Moʿ/U6Ľ~;ϸ)4=26r(jphR404GVn߽dѐ$yD"060S3K<\lgnݹd/ܺsn%0N;x"loWGǃ0UH$ʽ7J?cwxT_|Z۞}~a;_{E{ݞJS'v#<(KԽrW;btlD>PQb(,Oji=hEݾ{'?V—U`Yvxd 8]takgoBa۞}k{G*55陹^pwޞ|>_Vq:{0 ]J\T.-m6M$Yb.iLkۻrܼJ ›;/R) yHotwͭg|N׹3gRIxvf]y틯(k|NLMaS$Ivz*nds9 WVŲs\VG)>o17OLNs8 GFj%r7ZMέ;Ξ9584" ;;ڏ_X,Jϟu\.7u}=Dcy<8E;6Ljq.{Q:nkWV+jcx`TH$ljhXZY=q|4B#cUUZӕH$QT='&&|ASS W_VpR% 66ϝ=0 pt 1{T*jmho[Y]{ .Fƍѱ9_^J<Vjkn߽_WgF4twu0tK?}*r`hkW^{80 qރGjJjmϮ˛G'vvm߸f;~އjҠ/]x~k{X$^J;26qYP7x;w?yνo\Qy:oO`_uǯ1_S$IX$)3v[|>6>1ðuR)ϞFc8{}ݫ S34MrW_[[;L&әmN0x:AރGK$K3˶Zwwm=^\V_gjooAaJ"Nr8%TJ$ART*Q4O$^7ND¾hgNJ& blbJ$MLM#\~APR hHׯ]I k̛[[l6fe8(獛<^/A7_u{?=I8N@H|@PRjj>@ j@QI̠(D"X4  ԙLW(>C E.KCO&`o>7A}߱k7^W+r$IsB.Gdn~pO&-swW j5p~BgY6]/;k%bq<ae>ղ(qׯ  319 #隙g= PİzEO!pL k5꾾s;J4L&77|f{t*}Ƶl.J[-mm^\ 87hmD"QLޖJwvJ"J,˞=ݯ#EQ m5--x<>.kXťer֡sg,/ D2 Am.J)\.D2'W._ A h۳ kW.ON t-,޼qܔJt:c۳m|>YC]'G"UZͣABW= I/V<|}`۬-~nokx+kRZAlgMQMc @cCZWgG?| P]I(jk$bqgG{(Rޥb5kk|ðC^j1'I`ss?u܌x?w4ju$it,Ngb8<3jw$w|\&cYV`(*䴵AP[ w!D8HT X_W'M(yF@ L&{CHAFpfNWMAD"r_3GFE5>GGw677N8N?s j Ba$ -E-fFѝTh455޹ (E!p8\&Y$ҾCOvjzvcs+A@LJGFM( Q$I8NSSc$hghBl+4M#(pErqqovu4FJgkgWROLp2ǃB}t. ˶Z?}TA-wv@bnRX.޹:T" eٽ}(:=3xeR) -s%A@(P-,onfs3mnnڵ1 |~Ō puq{{XM2P:xV$d*xp]EaΎva:[-w;];/|kg|Hu6b 6B\[[o5@:;-L&{CH0L[kKKS?}p2,px~[Vɿ_g/<{pE̲~˿ q?h'l{/Pk">| \0^_"ٵ)2DB*=|4T(`J&S[0LYalEL&[P8"B(xM] _n^"aX,w{}^W~P| C/pJH?eXF*;kՓӳS@pmmöoٱX/ͤh4Ϳڥ۝eٟյ X̲, A{|BbThTkP(APY #US(4E<ǍD"hvn?qW*(BH"D2 ?}3w|P H$hDm(N&SFgEx"9;70Ng$E}~?a,zǺo߽M5~ /.ܹ/~k߯ {P %9bxPFc0p^٩PC#9.j2thxN}],/^J%g$b؄jcsK.~tu F'5j’ rb~x啵d"Τ9(x\ahfdR.6*Z⠜ť|QS]xphieM.e?B` :/N*ήͶgW*w<*îo:XX\r8 (&@CS˲mjlBΜ'{OC!/`4_ITGQcWANkWݞյuB _qmqi,{px8Ijcs{Igye>=|< j ᑱqadoq}wnD3s7_/.h4ᑱt:B.  G1>Ə' ] 044Io>bY6,,.+Y\ ȨvjfmO[E)>e03l,urܵ E]^%߾{̩~/^8ZL& CQ_hqB/V>et&ÑfshjFil<8tpN575*r^}GW}h ˵Xr\ lokU(n&SC!RV%X$277}l<<h}]H(LNIUhop\cC;}7f |@?BA&j55F'd2h݇@ (bxG{RX, P8;sjt|esS#MӇH$*I B!}I5r~sTo17ryy2K=+*P>o- F[֛oI+X\.)=}UܴjD2)Jk1 ݫ3:38ޭQ:;-iZi2hBp x0'8A H(l6*u2AܤQT Ðb"cm}:ۂP"4uss&6Ba <:e=#^2l_ωp$j4%b1;6[k9P^ڊxV4`*}do4?O$B;Ty%SZAQgG6iZ hjlExupR*u*XP_'r|MEյ3OCp8":aT[;<:h'RJH(hPxuۡp҅sMXT"Z.W$U*RhZLZm4Td*egT"t|WGFZWUiii@ O]qa} ,It*>l{'zlgGŕJePɳ߀?&I%CH\^>VŸ8ή'uwI$M8'Si]}~sSc}7js*TR鲖;"JX_]^V *Ty\B *|U *TP+cp t=hSw{rE., ZB *|QATTX,"Ѩ?~ t:Ͳ,0P( =X,ͦR)ߟLBP z}8{}d2r*CJs +4M;].D2Ü\`0H6? N#c/.7,Ⱦ'-rcr4}g(Ѱ,Ed0?%Y}h/Mӫk3s$I>Xqx\.H$ >Iò=l6;08g֠' P/Vq@PjT(u:]4-Q^KR"80:r8?xd[];|nlnuU+k[[d~b6?|(٬C#W޺H$ #ﯮZ\^_XXFo?E>95]e=q|盟_TTz~|Z[kA0hZg6BHscý$I%IȈ(JN<|xnagxtleuH[A/IS' iqA`*ɩj8t¹ٹy65=68N+g+ qeEQa@ @ 4 <# T L&FcL H% 4NӵZO,d7ob9nccadt|oKQ p4[^^Lq]/vwuJ}[߸y"0R|>LIG.!I#͚M*=Hd24J}Ꮔs(f0LIR\x"!‡_J%娩yrdjjSt&fsw? GS|zT*?{<8_Fc O$3d4VWUG?4m<"I2 t: NPI b8$i~( .NgΚeFx6cFP.%IZ\^Q(0 Ec|>2ՀO$IU*UE_,IJ%ptv8[Yngf:;rj}ׯѨzz.[][q\TVWW}MZ*Q߀J$RT$h\.}b bScち:n\y ~N{Z)ܙ}i{[+˖(ꪪC0;{/br0 t:SW9x>.@</K u:eәJX S˕Jlc}atI J2p8re2Y.d$Ku%ՋP*wvFlʙx"QWI&iVhE_RE IR43MhHg2Xq竮q|j5rR88p9 |gwOźZήNU($"0q|(&jk{R(jj WIQG͞'$`Zh.+LD"104{0 xb6m*TuUU張~ ^ #Aͦ-RqDQe2ѸV #l2Bl.QjfDZb1oln:\$AP6}ÑAoS4#**rv93,/:vsk pNH$QX\ðJtp$bXonji:j=.#I`Q_a~*H()\. Z0BPqom@TWdbLF()*pj5$Y-ewvJfږaJj5(#T:MdyP,FN)xaI6+adl2,ӟ??'OmVRT&o?;!J?6$<-y)P8 z; #&W$ '1 o8˷Iq! S05KجO }'Os8iq(q!r!!r!|A3D Q+f\8e s !O,'VxAr\6K%Iғ`Y+9p3e1~/q=a*~8 ?<ï 9c ,C^&ߝxM:Y -AϴZM4dI6P,޹{ LLNz/Xͭ4ONz]NWM?#m{ܿ:{,/r3?vpd2I>vj1NH.vfg@Td2CUU6<[Juݵuqރ ãc{T*ׯ,ܼf}t-$t,d;5lÏ?mmi}$IiQGwvv =hOVFu. d7nB.sK({u}skABe/?t H߯۞d磱x%f 9 [ãc{pxt~a_]eCᏯ^T*wN ,H0"/g_~|AV48ɆGeVd}]m_'R wνvnّ$<4JI ZZ^~vn\."SԨOE] Gmr/?h?[>uaJף]t*>$¢(įw`pƭ;D+ϐ qil1x*&S* g)A~,IRCC7N8O~!Sޣrڍ^Jt\}IykkH"ӧ~ 0QJQ>@p|\\,G KrQQS4q;x. CUJdJ$o_~tZ=53{gdlN^YUݠ]<fwH8y$i nܼ͔#v9jj=GݸݹhsSLkk]\^iݾ{jd$@Qz'injɰ^Teh:VP&q6B aZ6/-44?|܏S'ݺSiΎ+o]I)LEwz(ǎx.` |iyeye-H} \6ErpI^J횙Y=ݝLщ˵8VyPج8Wڵ ~ ɖJ7_|`'tOw'˖ÿ  ;{fvnC|>~@P񬬮?bxkqyaJdQ߀B.dfye50q(^8w`Wg~ۗbL6ۗ+oV7 PxT*}H[EQd2L&$Y]] P(46ԟ?{þƆ[jԪjH[eپa kedmll~+vmk{x"X,!4y n[,^\. GM^@ʅ^EStT~7Ν9UqΎu^'qdS r{޹]j4w=827P(W/=rܛ,aLjh4BX[aT*,[or, H$BキY |1 QQP9 $IEFٹ@ ㉯(Jb2LE5:6Q_WW.cf=Z]U{$C!aR4 3OP(Z "h>;w$/ Jj66rL& G"+(&I~{^1 EQ),K8 KxP(::bM Bl2a7_$IRX>~M*%E?{zied4^FEK¥FEA04-,B0/B:u_/`&I,&ҙlj5%dZN8f6.˂(J]Wn?vIey2/EFatsSc2Z=ݝ%F! C$;}n&+&zV7IZ:Z>:A)JQ,Ah(䕷2[iRtqV-bH\~5ϏH"I76WWUE_F֛_  zoA \X,<J|Py(l6w)Jg24MC|>w`(\_W{1Q&f`hj/e55{R,'ɩYeYtIA~Ik454/,p'] Q .U~3ryx8fTeYx81IeYx^$e4]fY@<88 Q(ORˬ(凊]\EIR,"AX8+t/Xvw}z~ў.B\fYB.dn$Il6_8{`(\_WTR0:yil|ۿq:oinz` p͖/E&ϗeN0rTc}IHTV8AQ $ID"QZT*ycG7E)dBqH$z ^<<5=㨩 `2Z?bՏGS_ 8~NAj=ed$tZk5*re2\.W(HN77j,fPd9 /476"(JzۗkjJ2 Co߹wڥ5P A ȥN<0lmm Oo ONM4Z5+rRce4zb6turEf\^dFDQՉ"t?ű\6 (J?ɧ7=n?v= EE["d2YJ '^O&馆ɩ|>iFS,F:۔J `\\.L&bcя] ;*/..!0L& Aʧ?;qԉcb ,%IJ$chx(y:n 83;Gdў7o0LNA0 E9B8Zd`6|ccnUR 42 JbfQIQ=ݝ]J4jf!xrzv}sh0᨞1'l.HҴ$h*V*)O>'d@.A.`Dcr9Q^BE0tᣎ#r*08#bYt:qN~uۖJOjkm!pth4[wI BEEޣ*J8p_9d,ŊH}]B8u u661G(JQY\fisk߽AQmgo8DQ `EAX,I pPQRu8jh. ()*͙LL&[( 8I"( ro44Dz6-x"P(r$JUdJ&iwַBR%vX8^bNA$_%at:tߕJt:-/ϟ +$I_Xzc6KDx<[i:wje=+M<dH&+!utDEQг|%}6}NL& [;OH&IE՗t:|!]-s|&}VJJ%<$Il6I_$2|%X< DQH<<+<]J_C:Vuiya EV{L #,)p$lR*+( y}PؠWv/Db6ODcX ðRr\4" VV׌ƦV #/?ըU0t&ppi^vvFC\PZEQzd2]CQ:EU {^_.:6˯olhp$ >VQ*d'6 IDATn? "5HRy%Q$qaqY&Dc1d2յٮRĔJP[,))ں $ѣhtsseN8ogN\Z^ hzvvggOoЯ흝jEQc8MBp\V){^oB#87oI2ꥋkkr\!2l}cQSTm9h4H3vmccs~iv/,,/CãS,qdqx$;tAgGK+OaFF ~ 0>Ջf4=2:qk^knjz H8HR&G}߄G횙yN%22:{W4 S30 mlBfCM ٹy54<~ ɤf|C4>1Ye}tl Prlߧe=37j8.kjl$q顑X<C.NGjWW~rgphG>rtJQOesy 'axeيfeh$uT/,۬vclnj #|!ҩtkKSeXqHQAG`+o?,2 dRtsScScx?@Bie/RaѨR) @.Ev&0!  Zr$IU*dz\:!pxB@Ր$(tاUbC+RTR{A: F0W޺t8t:mMu MӒ$9t:B*fYv)Jqa_$eY'p Q`yXUeS{{Zy_Y][][OΝ-9qX $M$a6DQtx}$M0t:]Wy(^0xzȳy3`IJ,ǔ9,/beލ% T*1L\y`RIϙ7ꪹG}{lٿhkiVռ O6675 zd2lye娹pl  CǽhEj:=˱e\񘌆x"qrl6e[55PXRZfðh@fol>y*J0XþX,Τ*Y./,.I7-f3 q5AVY&Î!ݪR)+B1tr睎JAgN0b68#HksZ۬9J556Ǝevcspd2Yq ٹyvdKsn15DAP8"]Ox 8^(yCd* I8A Ip1K'0uTU$x}Z#-oz}\<8'T>MQy3u$Ϳ%E+T%k#oIS?յx"qG}/;|=C % JR2/2%$bI`T\&J$_tZ\.C9C qI(IgPY2#Q_ |>[9AD @"0'@ AH"*꯯M9C97LF% Ţ(*N87(W.xA(1 A "IrIJ˔E)4m6-t*U1ETt( ("Dz#&0"AWB-UmQ|MXr!o $I2  A4M\>ðRL$AiZTV.x0,UJeByZ%ܽp|bPhzfVݍͭ/;$I+kPXɩѱqQ@RНY$QV=~ZZhcsR_EqxdXd|~zI$}|ryxti= ˲ $)IW K+<ϯ?+ܯERk7 }rsԔ S,2WšD$;Ɋw~A*M|%,?lnmGp$>w>?[;K˥RivnIJe{ IRX|8?}E ɧ}~K b1W\}p IRӱ2)'&P 0 P% zQlD,Z,Jq˒Jm\2E1R @0@$@aEQ^2`"I5`D Jҗ̀%I{ޞbbovIT756qR{X NE hk{r޼}̩ZFͭ'jj:gp(P痗Wwv0E)Ν9]Y.WV]F^][:}\OWٳ?z_Ny$I}<8tٯ SӧNC`nJ@8{+}$IgJ%j=|>ʅsۡPX`667>fsSv}QSS(=xh\N AϞ IRa MFdrjıގ#"s龁Aj=-MAνUU KJ%dj=CE8v0}vIUvhp:7n޾phpL:j<=ړJF# AG{ffZ9xrNr|arz&I'O?vÖ桑Q+b#(`D8EQ:XdffCPgG{~=/Ng2AvMgO(k5l68/,zVVo"w!Rt!ټSQ{휛_dgN nuwuX]ZY]ج,{Hj5{{޶V?~\.ٍD"DA`ٲJd3M *xGbx[Kh}]R>l?208T_ E"KmS> wѱǏ;sg*DP?4qj( ѐʼT7'lVD6N{Ridlbjf.I"7Pȓɤq|dlcP8Z{gw7J$a6zvS'#I^R} "h6kmiJѩK/֖f@:& İM2a0},r9Je2Mtwg~@^pIӧX,f2]NǞ[)RIq?PH$:;T)419%IRgj'5'WVzncsރ|~bjaBHQR|D.]+Dj=d>{tfnν SgϜ*,mnnj5h, zvbK\X[U[-~^4r&)\N0 X`*][xK 8e'&wvw{ҙήWT47I $93;faƆͭD"i0<|0LѠ?sTX`f+AT.z;K˫;{ފv!/UKV~?ωt 0ƅ_iXU"C@$QQ&Q dE$%WzFcQRM|.}uXʞiFcZ&'xBӱ'gy_XZFQJ]]mMu՛JQںl$I45 WN?8TWŰNff\uUnr'3cMF?l uDþMJҊr @E?+@d) ]NqcD\u;_ka.?;HdC}]Ǒ# u3j|PjVybrK+VefveYE(EQZW$ y~ArV&f pNt L&jY "H4ZyQ&1HzV{Wު٬hC{^$JMM˫Bqw?r9$Ie8AP}tNd2LJFFMF B1c(V*6+I(B,*h4Ҭi$ƲIALɩH,V]UPťR RJyqw:qs9/RI sm 0`~_t'KDSŸ$w7p3]n<;{SU&)Zx$EQtٮSYUYZRrȡ߷GZZV]U{v-*-)֔Go;#`Q+ͱ &&nizkkvxa^@|pP) [2iCıBm%V2K MPIQ 2 CV"d"2 Ϲm@0m2>`P"8qq,//(M(Nvpi|N t:r9ID"i,$&a6s,ÚAe),V((NcH$ys݉d#_2urMBǥry9錢*j&J|2 I$p$qn+Hb~yOlb&ehpbq|>8OP4eۓ-IQt6pd2YEU^>Hl.' b6Ʋ2{dY͖x>͙PQ xi3$I:UU)JnnMcƉ{{"h47z|>dÇ )'9Tv* ÞI<%IcEAdPPD*ӹ\NS$EUy Ad4M _eݞ.>xaL&kt]G1 x\6i>8Ir9CH3<9D"f +44t ]d/ mPu0 c=mYXXXXyaD`kK : E LaL XQ]7Ui$4$AML 0 0 ]5]Gr s<2kS^S }m,ܒ D(2許1B`[SS,IrAU7} &)De c̰4K"ϳ,tkkDEE1ƙlrNgc~&k /P!4 Ô~s{$v9LMTwEv~;$YOv8c(.*y??96yϽCUepqiIycS:xl|2a M}:MCp&yX]Ybf }7F4)7hnPScuuS$95=n:}!Ʀ'I!s-[uwuŇbnnQa` Aì&M)4; Lo n0?PUmdJ>_E"|fi_W>-BX}ij雮Ahʪi(jNQL&SW[KB!aYbqSFzGeol"EUU(bUNCs.}~$e`(`@G$IBTQZ麦"ǥnf!$(.aY}_v'?~GQ3.pyZ.] zs ^'^1zȡϿB(B8~۟q1X!<f{TMSU=Ws\}}ϿѲg&GF}^ϟz g(ɪy~R<v#6YUQ;;/Dێ8Eܙsjkݟ]y]y@WMM,<PPYW2֖i]SطϡZ[~_wwgiII{[`/>(ͭ 7z{N;/G\=k}}Go>`0(r<7oW<ǿ+5UhU׭j&,4g2cKSkGF!DGcX8XcCffR)ͶJR䑃ou5#WE)* w3$A} ӯ|ScC89k~eyɅ۷g$I m>3guøxy?Ň}vsGGϜp'2l!]I"M#AF#MSY+KjJ*[k[{Z{:[;Zk~/E=A!I)--y:m61Z[JKKJ]Ng*VU;GtO9Àk;ȲzvqwqΎnll}=i4mN$1FYH3 DyJ(_z}um=B'K4EO(vfn``{}LxokiukJյucIc@owx` /ܾ7XTuop/N{W(|=]](ط44M_Y]ڵ~Sqwq|=Gv~s ܼY\ @)L& u۶ PP(lZ63; I4VUVvcP(mI[}`^"u]6דܺ ͲW^oinJee=]P8D~B,ؾ<ɘ:6\(nWWWG{k=e475H$JngطG7 @faBw^RRoϛw@l6۾=oL!IRC}}eEiϽw.P2.TUV/"h,$E&I0 UUWVW9弦iofkkDBDO$ C&&t]{hg:eteu!IJ, IREY]7bhdY7^{p8L& +~x֖Zۣf6Mp$RYY!r*~VVW3̡f7DA .SQ6U8V1QNVs8$kt:G I((2݇<ޓ@s:/ _9tpziI10h鰓$jaɭ-Qn7KTUYQ_W;|keFtzhhѻK]hteumhI*J//+ڲ` q( ϸ_0$Y&rjz&2kcs3VWUNIf>JKK:UMz66BsvnwoymdA8=37k)e<G۷-twIKshUUv9n@X 9M3 44?KϣSMO>p:#W>5=va, ! v\ h$766[kkM[ҋ5Vggwx|VeEy R8yaڹ쥑+Pw~\緢ia155",IDUp3g~t]a0u֖yᓷ^Oݟ)̳Ŝa57OH&79d&*IR"f\o   %K ECG+p,{%,Ájin2$pX߆&67>=_q`eᴰxL**q͉z+fw-/tRg@I}nScsˑw }g6Ty)R鴦iEgf3z؛B!H8js.jn߽P+mRaVaP *|=2 OR ?,^fb 5FIENDB`gammaray-2.11.3/docs/manual/images/gammaray-method-invocation.png000066400000000000000000000206651413310443000250010ustar00rootroot00000000000000PNG  IHDR,br pHYs+ IDATxw\%@vкځ֊,8Q܋JUZ=@pQ**궎:['˽ A@$ GhO |||{]½JI%ʻB`3X 0@`3X 0@`3ʻ̼sJ*D5WXP܋RSy7LWRROjrTl8$RP"@?'''RRφn> FX 0PxpH`ei0 8$CB(L^~i 0 avbT`݋9g_מ}8qt1[V4k, J]ëCf+ Sg|ּٖ;kz Ku&C\*4kQէϜr93C˦UP^q 2|+dMo^=w>SAjjʐ5׮_773wws?H('|}}lݾ#5-j5lРr"R*kןx?ofa"^ [ו+jeeeu!% xHԻ6CrB4ejUxM;ūn~7лg=nӪe1b''6svx" *POjؠ~-nִ 'xO#4)xzVVV^DgC®ݸaffuB,Fޤڵ"40JMM>Zoإ-[ׅێ>jxJVNMKXi 勗.P*#ևٵ}sfN'{vسs?6d2Yںwu(pɓ \ٳ^>`ȢfF􊦰NڷowƍfϘJDvG)-Yfnn/֬^q?;wNá4q5bxuO*oi9"rtUTpqvv~}9:D"n=SVqbKD"}Y"RTϜ0R":[fŵ}/_?mV;__333{;~}}O>VVV3Mѿ2+'V->Hh6(C<Ҡ,,,lm>|X6JRғVDF2pHhmmED ErJ-D( 2L$:99nfh'%y5."y/LњH} u8q2p$[[]Uɯ^i0 *:5-DVgX[wʽ#rydڧ2ӯOrTq\bbRʕ()I(/\8+NNmڰV*[ sV h_[.&N֡}ffffMu EvpeAO('O/_P=0h_W:;9F133#?J9KLJ\IGWie)DTnc]€l}R$"ErrZZuV-#6mQ3SRSޡ//\C־NO' 2!>YJ$[\1BTykkV-[lڼUcnl{Pխ?t~v"wX* 3M:9hU>HĒ'(p_63!MһgڵkQաa jۦմ)N &KѢ5Cࠫ-b}D"ڥO#Yԫ['`Ę7[_O5'Om C=u[a铃ؼeP'GAnݾT*3e D>,drPU~%!!*%€?q% OFr?[lQޭSw:kUTIgn9+V\>ܥWY㼻wҹdᢥ`_e3tyQNNRس\UaSg^zT2BE=ݎ;{Vg>{ís`{v._1iE 'O5lРkFkK}FOD b{ qY[Aq ߳w>~p14lm}_vfRt*|._Vu :4`p@מ}F {B=.0++O7otx\o߹ s|GDÉ{y*le;!hc&LmᄌCٻp}.ZR3X:{tN^_^~]-|ߣBV$ʥ۳`O>{g6~]ۻu:4\;òSR#ևoڸ611qC&݂7o^zo;>+e2ٜ)>98H5mڧGp=dvJGwwd]VYaa](];G:}Dg/^lXvͿj)Mp\|\!oղDϣіgee8͛7\8֧15wڙ㸦Mh4͚>{j?N`)H$>O>[p D"<\>zT0%%kA[[F]bb1U2@qoظ8":[j'kfffogׯg46Ţt?p0*55ͧ@"R3ml뫝Tr%I/+wfggGDBlll()Iq.ggkt :9offh|ަe+޻wU\Y[~^xu!<ݢ|5'Ndkk}ƦXR*OZlL-]w6?r_LHĎy󆣓#q6JOͿ`yuiaaѾgϝ-*-- ̚E61c']uԭKuhټYS]Bj|(sp xHxĩujׯWAt?p0٬|y˶7o޳O[XXX8yɑ;w"54lt"P*ޞbK==N>Z|srr9w2;YFքtw{wYYY)wldb(/O%==.^֜ӂ'%|UcOb8xrЦ-[=9xwƿtPx~Ԙ { ו+zf98νɷOqAGk֤yw2ڥ;w;zyJD"zu3tڵk-/MgoIP -.~owdf mx/mܻ'O/_P4.>aǏ}Tbb. Rrپcn@.WDۧWy7(Ž5Ǡ?-\faaǍ.W# zY۶iUm1JpH& T J  f ,`Q5(ndR0n2)`Vq:Evy7?˻ E.<8$f ~kNếdN.+zKabʻ#,f\|eD4urPʻ9K,_2ur-_Մ0퐰${gRj FVڐjѼW._r鲾seY a$&}d k3n_7.W`9[򦕶ĘJZMU -}S͊[\G7pgUA3(t$__@mfNVL{O>ȴOՂqӈrh}\VFiW2U٤q$<lkJL!\-XX̫Ɯ_>}MDC)/*was?IwcF84ŢJ񕓘ГGo֜rPI:wF8U侄n.Rp9t 7dff޹{O(7Dի6tX`*W\ )#/=zq4;bTT +III?Y!! RL2p@6m\;cOL:C\⊋Opbx>̬sD|I1qs} Xں̣#y oݹw6qj٢g7Fl?/qֽͿn}NVA:t_{,] 5}\P֯WofцMR`!uf~]+8n윜ч \🛷LټYM9{ƴ E@|(Rii11\?A*0䷝ۺw&\[鳛o ]wFtJme+ ޱuP"Z*$5-mú5OLJfalX`* n6cSZvĈaC,%Dۻǩ3g;F4eŒkTWTg;HsfMvtr>037h4<]V-=>hߑǧL%D$rr%&u㫟/sxKk+Ĥ$FEy&|F֩YFLLl˖i|:kzw̚޽5Ҋ E?|k҉SwebvFw ez̄$e2]50fD]5] FtpX`: ܣk _OoCegLpyp\mhiNNgMOR%&&V\e#• _ 8'''n ZVt=="wgte/9-x˶흼թD%''GWesEN ,"?mU*UDZV<_z#GҖI-6DlR*U_é_\ϗ_|>uʤ>7hʴϞ9:9a⤑c'4ibnn{^T`RPPh0o[NǍ=+>~G|J5#{Zč D]r֭ʻe ժV!U^g٪ [IDATe #y>GiQ{'/On͹pRڵ \:8S28sGJ6SRJ?c%hX`202j{˘.#T_V]F`騈hy71,0%9X`2r/Hp+SFtB,%;;;wCˤDX\صb(1)I9A\.6tp;W>zRʻ!eD";;;K$b{{i^%W*e߰r!VRA`IKKUd3qbNA yam< XOkf ,` f ,` f,8dpIENDB`gammaray-2.11.3/docs/manual/images/gammaray-methods.png000066400000000000000000003704701413310443000230170ustar00rootroot00000000000000PNG  IHDRJ{ pHYs+ IDATxg\I':V!`GD:aE;b Q `V:!j.Bh33fIh9B! FB!Tp5Bi:\ BW!B!@(r22bqUǣB033[$.140X*Q> 4z s:|}N:UEui--h4]]] ssmm嗡 ªD}22ZjanjZՁT̗jXZTu !>bQfͪACb&,L$*(B hK[@C> @!9\eBHj!t@!4B!MBHu: e$۵iԴq&WUW㯹3˪)55mzzzӢy3~>&& ^ѣ{WcV.!9Iu9W( }_KqlԹ/OʏQm׫mGQ纔N7nϷo=++1"jXZ._PP(5{nAA!w嬒֖TtA7LSVF 15uǮ߾7_oi,ٞ|>pxi7jHT:H$NeʨRNH#'vן3+?*CG+oR/[jIܽw8<"`䰶mZn޾.xӜٳjת%w5$?z̴̜UJz72RYYM?vEgff@ hڤq%-*X1f䷡w8mHMM۵g?'tuΟr9km-Ν驩iRnuۡ0G?3Xz/ɔΖ`DBXQ1s嫉Uc  xL&ӡGwo/4Kxj &&%)9xZXX}͛EbQ݂&r1[ {5 lTtء|Uݙӧh rss7mv#Cۥ#v/_loOI$O>~t^~N_oQ#UvG#:;9r Zd't=.}p ,kX:ad@~x<^ϟ7oruS#io ЫgOQ1Ə!v[ []v1U mbl6x6-/Y8۷e+;tyNgGJ`0utÇm*m%I>vHڧϺC'^N:poMO$T,_Kgގ[x5]5mR׎TI(uD"ѷ-[ R9Wya K̛uRLl<X|l9"=.tKffe>U* M?l\e::8~ؔ6oX;uRٵnW>W 33+vmZKoܒ{pá{{lтy2&$FO>ۿg繳'ZjfUkLƱ=;B>z|Hb:Ա!߸%i[\(;C$"\:n";S޼]M6i(at}ࡢ"7_=d?ٹ.W*99vmH'iuRiv?iڤq9~j+:]n ngӾ]pXw[V-JD(oP+mXÿwo\шP#])`m ^HqFkѼ٤ `uӦyQNu~ _~x[zZ'{NPTqeO S19۶ﲲ;~("Q(6ңhEEE<`0n.7~|uuuutt\z߻@6[ooddxjvvmjXZ6iҘxU>w~V]41p7_+r37fG]<^Fd2 @䂂d2lDlQNI=yQtگNܹ/_)N_|#_]]]:nJ C%}WlCCD6^VVu=~ 7nݶAٟશ CiЀii7nvuvR*h?[^=h4Z˖-JJJzNZj@etYdL>~˖-"T"2(:?N333 7o߱n@_TTq7 œx FnjQvvd7$7/sb嫾!:l";`̛;;6e+6?x`5,ˬ\cb{_|:!], KD_ ffDYAGGg-G ԲyS&Zխ肀\ ڜ|M';Y999P C%}@yyyk''[YFFl011h˗,d 7QkblCW;016_244ҥ|>?''W>```޺ݾ0@P8u?//gBd֭S[Uu7|>r ONu'OboruyPU>}&'B'^zCAGiz߁.esH)s[[(Ҿ;r#U5NU^PvZjF?٥-Jv vѱd})d0ի`2к][ow:٨%V y:{j ###wuttt&=D(<~/_tz?U7狗{Y*}֌i{0-`}}7jhff:|KOOwY7l0TOOOoaCsf݆~ƍu.m;vQNp*W'ێ #|#Zt!pܘQ NhyÏD{B@hcnruPUdu.fמ}łu̜1Vvڥ;v!ht:::cXxNHȘ4q< :dem۴nۦ􁒨T¦5d0G8}6PQ^)o*eR2iiŪuF~^~,B?ڳVZ;SOUT:?5w=0vHIJE^;rh9o߽&Wݫ: 5|Z3SeO"n=q|k*s߃-QgOH|ރA)@wQoPοbo'G* TJ\ XYխ &S6_;~RBi:\ BW!B!@,qe@oC/ aYҴ?yR!T/,^F)-;'';ب?,BMVffMnO B!`jfZXX(wB!MBHj!t@!4B!MBHj!teH$JJJ. B!jִoVj ))N:JB!5IO$%hެ|(p)BU7Bi:\ BW!B!p5Bi:\ BW!̬֫]{Wu!o"$ܽ ++DjݪE֭TB!C@IIILL\g{fnM 77O$40B^@P^]FM{>XܠA}{;[mmmξ~VVv6ݵ}33.%^ ׇߺs7-P([Ǻ][:uG|q׮*9B!J jլwmk$7n 4\J|gשœn޾ӠA}_ ^Ajkkx|>_$}߷֭w^룊#B;%"hn ׿v#<|, @(&ԩ#`0mڴ~=qn4vZ\TDݺvf24M {ֶɠ>4i(';5F!ϔPKK˺]vm~v҇ӧ#} o=z4?+tMk<ءQϞϝh耹b޾{goVu\j(*((ڥ3XgeER\4dff۰cG,^R\tʛK4#yyuԞ>mr7{^7eĖ͛s@ XY9a4"gR޼]j͘waXo-,,ܾs۷"qn]'ѩxK!Tk.}z&MخmEK9u62//kgiStuuJ-7>\d5n߱"?sf˦E{z{>,}~zO7jDTMC9{.*~ZJj֨aݮKƍnV6?rheO={GN7lٵ}+ݲe#Gl~pHy,ϟ}}lIXbA+WMPqb5͛/Id|ׯ{wnςM?hhGF :}{dfe h4=;uut222ȍ{vnkؠ~vv r+[nnsn*5'wڥďTNɻi4ښztnæB;ܻ `0&idWzzzzt:ǏD"nժ%F&=x0`ƴ)4_8v KOOOO+ה#BR9۷x[l64t@mmmc#!^r dhyގ׳{qPTTtcFtl YlCs cCz-''(OH唿` ЀO=ٶKIffe\X OBx|)ш_~e Xm k"p8bxi?vBr8q&Mlb YPEC;\tnnހ!/6446d_2l Y::m[7=v<0hJfͦM[ʮ>dffժUl9ďTNaضMqc:::}E +,|ruDUׯg}n>NtQ# '7Hı=CU K 4nԬiSSեOh^/,,SSeSȢ{P(d2uj.**"KT1G>WXXYYo*~ZJ~Ri_hcu3{ֺM~yݹ{"CK &Oann>vH93SӡΚ=GOWooM[B,,G Y͘6e߁r ,=o?F*WMPet_zݜM ::̦M-vl0 jڑ%@psq1hFFY3B>jlq[s%"7ZPc>tu &CO&~4bmZZZ&s`0rj?R-ZvN'O:v!#lo K9q fMgl ^C~m(**wRV-}}O>+IaF ,]<]<^N"'5EYȭlI?=rDCzxz߽qۓ`ك`8;w!])NFưEr9&nQjNmOGFA}^4\l[l"`:o`/g^|EK'251~* d.zK?9٣ {N8G6J Vĩ3,r=rG;8y{qveQn5 Ǫ-,g1 ?[ !d^1>y X`2#OF}аR.\ˆmyr߮W(նK !.zúU~Jt Wxǜ?x"зow푔r~^.}z߻ ~K0sTb[-2Y)*:˕Ya5+.ŝ ۿ۹#D;w'#ݻv>kgZKWJOWQCrđG}LM۹gٞ C1ܪ ]vHȦ`cc#e8JMMƝ;3FgoZV@(qO @t-q O,"q X(&Y 8?o}>А]v#p(&ill4r飸܂d2Æ "b`Ο h4Zaaѥ˓&X,:^F'X,ސ.\J$LJNIpwbz;:5EY@]<: FFls3'OsLMM׫Yy 4|02LMM󹥲20_p,)ȡE%m۴l~K'˙N?G]]޽_*TU%4nHrUf͛6r:us#NMGtIZGOs7oڔfF`0؆Ç "C="{غi9uԽϷ/1Bo_:پSڧOĆʤ]b.'F-14^RE @LzQ׮Ə9|l 077#177KOHEFK2ΰnsbU B[&O>bds(Gh>ʭNDςNޝ‡۪eYZCC`  ++Kz211:.h x<lɸ C¿|* s3u7!}$$=J"144*4YW O}Yt/M2%&kN\Jss,(8]T*^yT yWY/ WlC"TA6 *,rEY:J^ ^}z=!ۏ?g%"}'[055~zKH33SgNnL!M~m--h4ډ#VWXa>đ0nݾ+D&+KȖ.daa>ksׯWo_sb[uԾvt7n޶`CQ)2w@ޒy-0<>[6PLa)J"r Y F~{9Μ ܰA}T>BxK7zoD#k5,4j|Бclm YS&Grν.vE^nE|~ޥoVd#V*D8 mZjܨaYխ;~(Ld'[)Z@,=I.ヸ3+,T|VDE}I6d{x$sK r-P/:{2x}8~Ӗ!5$T|uSErʅzpZmZOؼYSHJN0ːRR & 4`1%vUZtg"Uewl7߷A?UݓGI_^^чɜ>%݇Ռ.X,m---ΐfoۺQu VY孠4[gIF8*,脸]۷JS,y+.н;չxk7Ǝ+V0<|d=M{Lt>ԥب5+:ktLd7>xUmQ;ҬS#''W٢ zkתI?[_;]"蟅w"FJM䌣rtttGxwd""=''w%^|lURRB.1}_]<(/yy.MTwq22F ,]<]l 1g|7nsΝoFl3|7m%vY]V =~Dġoҷ"Ν{vs]c)j`~<Dt33SNc≧1qnqInҖ\9;9(PNeK!]vHȦ`cc#l; V KXd{9xkNӋ )6mVV%% 0bDܷkk7*H$JMM ?jש#?d=Ymg U 3N69 R F`0؆Ç <cF1LccÇFFPr nݹ;~hbϡCJ6 seh`@۶iM;~N?߾DqB߷/Nl)'blOlp>Yٷ1YQ XRAX,NYF {d[ ܽwKȎ[X)5R%MTA5kh޴ɕ\LKE2{DЄqĩwwuy=_.F3L##0i]<\<}fϝ߼ie旹"YOV'FC uQ)s1,33 9SIzz:"]VFf&qN KKuԖL @y*^A= R@VV6 ##&t:ۏtœ"])!4'W`y-0<>[6PGEA!҂F8r&}-!X,p`an^NkoIrm6d1n;u҄OLf޽\x)Ar E[z:uD;K/ *E=\Žml.Y.Ysp,B+W؆z%;UpQ0!(2duaRgJ>sίu/GЃA^~#G{а{.;v99an l;wX r D[QQH$jԠAIIIex`CV]]]wW [B% s{b}5@+,<~ńQ#KקϞ wltwpҫ}znPf-&wddfg̴OC} Ejc՞nd^'%S\X"lܻX (((8vR!&Ef3 V Uig9UrڴnոQ [wQDBwoui0b(uE禧s\=}F h#pÇ 5>h1vMQQy AěujdeΟLMsఀ[NH<Wgg_W>Μ ܰA}rs֬sȈ cɂyۉn]:KI,Nysv}Q,m9ھ8y .HSFvxR$V.[w_'OΝ{](^nE|~ގ.s3Q#M;@X$&Er8WOǻ9$0Q|!D=Plh EjiV ˜sP%e>~#he\xW{5wj.2(R=ٓ^>ƯߴeGȦF WMr=1r#5}uSbPJ0ڊ-dz.d[?xhݮlz5Gr8円ء}UGŦun]?X*yPIf~𱤤{͛6qNK'aRSʻfѵc'Na2m[1sIiE-!4rj!BRB*BHj!t@!4B!MBHj!t@!4B!MBHӕo5ҩCKUU>P[I }P +C^^^Д镔zU T~Rf)t Nοr9xD+,40 qdNJ`Ǐ-[4lh`@V^gbb8.%^>pZmZOHRQRrʰwo\/^lߕX 055oȠCG߯__/IҢ=[mnHڐfoۺ"%>%} TN:Vawl7W%A}ʪɣ$///d*Ų`3ybVGŠ(sVPv3FUFDUB\tB\[%)мYӕK@ޝ\ 2I݃LSruv:uRlTvl5:&VOH(N=1ڰx<8K*̚1u;rrrUwO=<|zZ5T1WܷK$p1sV$C=(2TPiqTՀΡ},_UXXD[իJJJeq22% HHБcw9zyLr:* 7hc'{{_vc'7o/7n&ܺp>~m(*gxqoO&v ׫g&k222܂=t:ޠ~ƍg?Bv56۰u;8%Ք',`+V{;z_ idLᲥH8ufБdaIֆ՟..=\iNFƈ1qH$lV?QaԮmmtd)SkT' IDATys^neVP$1Sg" 0v‹c/Y\s^751~* ( #'EOVb+Y (XACU휊LŨʽ(**˙GN=15m}DLfɈ}N.kɲUFFQl9qB)Ӊg>t`7M4!>欿O-QQP0Դ|nܹ3͞;߈m}ƿOr!998[]{@$ݹs8>޵t ];P:VlϚ=ރ"HnffjשcL\<4:&͕x"72 Xky<1g'*^){l)  lll$[ucM{Ğݻ WfXoztҊRש][Ul;rQKX*XA:^,<|HKQm۴Zr-q,]t(&]ۯ\QE"QjjZXQNT|!jC=((wʞqYT*H)5b`2bB"xnd2FC.-u=(T,sfdnۦ5Nd[:ET | B߾t:}Oե(&6~,7d  33+/?a&&sSh4ږz,_o|>-|deg߾{dF,`¢K'M`Xt:f*H)=\n)p-!;nZoaa.7ƤhRǥy;mr"mR֐-T^ƍp57mru8GrQ] 08.޿ܽw?ph&id&57ml2Wd!D=((=T%{T*HMLfY J9223Ə[ujXZJo[q܅a_| b"* &l6x,2)Y]9bxqSB!p\0446g̬,I$ ƈaCF M[^|v%c_|E-֡n7YwdKp8(u=e-VY/ Io(1P_6m7ds0TMJ!k I.oo)EU044*4+-*|'s1kVxrU`Rii70!(2du? P*A+bTAʬ󝑑I HSS:-Cq9E,33S"Zj@Nss/ZbBG/_5n"ET (oY],--h4ډ#L^zX,r uԾvV?7n޶`CC{S'M9{.d+ʵGKh7P ө 9G3YP)LᲥvlKoXJ9R1KH$x٘Q#4n`HHkuFT-g/@Y{9߰9U#a:SÐ ^&UpQ0!(2du[[ ׺['B yurlCݺؽg䄆{R240`{߁b-8q\nEEE"Q%%%Q?_ۗQY]tuu]7l ,,Y,&۾k3G.]/_>{^,@>ZK^';]f@Lf܅Ky%X,V]CD>RT){l)D3>}=Dz3 EcR!aKRlx,iU^׳"kL:8X|t"$˓ltisb IVlzQ0[[g*OWmZjܨaYխ;~("}?sBQ:ش1:]֢s9>#vVF C4t;[[&|( uRK` .L!˜?g 5an:ngۑx NscϾP'7^}O9)aYӧ ƒ$[[ݺt$Y==݅޻ߡ3Xڂs} qrF]4RH2a\pOGsR`IݘmHس/GΎ.Ye٨ÃL`6T5[ᨶ wboGvʔVX* IJh\3bxw7g&?d=Yg- P-5Jq^aas$Ot$Wз *wÿ.x>RVE/:{2 ԨaCxS'WnĿԴ!#D$RY=Ua"‰HP_b Yg3]ɶк];j;?p2 ;ܱCHy孋M{ݺTcII7mRYA➽xJQLMMSg`e&=?xIq)QE22TkN ?dڴc4s3u_PǏA,,[B/w*ܵj@!P BJj!t@!4B!MBHj!t@!4B!MBHj!t 2]6=)9SWrV:CU*!*C\H!A*WC')Hf۶nn9>կWws@hff:qܘ(LYzqr.Vu8)!w)LMM2h@M 2,`>X,6'ZZZ(a5~]޶M zVuR?~Lm٢y`CR2I[^Ift;wnղEmڴzk7Ǝ5cwVuDxYNN] ^ҮmUU q qD,\L3FSOT*quǟ>sҡGwqM\]?L [uVZ (* "K_[[kw**O*REe)*VFAI@&iL2E{=s]NN\EK>,ѥM4p@{`JXF.H /CŽ;55+#ܦyM޽^#tbu,&xjDG~Мy7n"I>.ޛ JU5 K~όB\]@($MrOr8c|0ߣ{7c<|3oᙳ%OexN1ёvMz4zDEG=]YL;={6D@U"Q'baX߯/xi֔lo̼2ŝsn^g{VBl%3+x]ϣ89:ڍ,V/^OTin#J?osꚛd]M?x*'c萏Gn!7o=i}?4\~8)4 ,-o޺䨻1Jھ(,--rZb\̩ްi+aTi"ϟ/eCh;8O18DGX_lE8kZ#TkJl?p/--O}N0۷N0~C޾jllT_k `{0ɉlŪA_J|,&11 9=PD"=Uxza<"t֭ցz037S.477i /޳k6+xKB<ї>^e[.!)BSS으@으o*B d,--*+ʭUU1VL&S.@e@P̚J+@Siֵ+BeM@ ӳ.@Ċ'r\P¤G0aTQuH$cĸ^ӲD ^;,&sY3r-j7oEm$kf lx/,--56К1j\V-*#߆+p.Zͣf```xMNk;k?YMM\B XqcYMMl|"ߙx6d GxΝ;]W_b*mFX9$D IG;wIz &6-#S,T*q_>/? Q0*ʒf?xS޽o@ TT<& EÔ]z-G,+)VU%$)5du I&MԨfg{X"qGlu懅޷"L&HK/(  ZK@e_=.MP=R/e1~ yN]ˆorwÃfZj@ zΚ=QoWK-C?urO3tW\=焍fiah4D8ӵWHMN _\ZVFն[3 a12#]aHR1@ }m;S'יf3̵߭Tk#+?ppd?>՛2߻W!TJ[SN^f:8vstX?:8Mr<5-zGT>i*FF#^z+#"QJXYM핫WG\܆_oŗ.m*1_WY:Xrjz@DbxxcMvN޶;D׷7IiUN]j/^<>וkE_F-zN[f=|1vZ0? 7vvyӧ,R ۢvfW{"m3d^EiCþ]U@!CԲZ[@ N}R@ l@ l@ l@ l@ l@ N;fuuuዖ߹{~.CQGAiM۶MVːMT6h|#E]V5NeLp;w͹p4)^҆֒%y}]&[X yw[ظyݻnjAg~f}oͦyO9}Z.v]fی^8?kW+|]>UxP/%%`!4p;wW|ѣ?cJ`D:XekX*ʵ}>fĂ_7_=dWo=W}ܜAK?_{jjj_EMMM,9:dg?}6J r3 r3b8mzajjr7n:;( /;=3؄-yD6aV@쁽&Ư+ #T/ s"- ;zԮXs5-zfxyH e<7o_UԒwHdw٢swF;8yM;״f&DQׯ۸s[zQ% g5d8+H"qq޼TPdן:/($YLto߉9nܼ~ߨ;gNel܅` xݻilK|a؂xn^gΒu"-sꌉ7n#JL>b2sذ'}J$jdX 0 ޻ͬV_퍹[w@m[0!1XOPz.Ӟa?b2:xy:9:9tt0x_' HuGl%3+x]ϣ89:ڍ,V/^OTin#J?os4(--;{يU\7/3ggԎ]@Iq1G*U"⣉qyYi ÈB+KK"-%r [jjj&ƥ$'<}Z{*kor9}89,o.u5)ੜC>ZEuu7ovvrTz[byyYi>ޞQwuĸS9aVzè<8@8?_~rQ/]Z T̏%)zG)WY^= S(o V_6x͊'O 3;g„X|\XH025gf)._ b\L_x{y`6F.xy`6zmǤ@,&ifj_PQD"=Uxza<6aXnhHBTx|6629cS/K$jsfWP.<%%*w|thƶ\.gD@{0ɉlŪA_J<8&&3IPj'Lܬ^V+T^(3zϮ۬(" }րh "45ۏ /R KK BҢRJXU^j<)!^O\Z@:)$_gRP(f% 4~uU,g2* B;={ۣmĊ'r\PdTQyD5@"LLKzNA9,]c獛DG}VhcFT6j}k@O]rN^w3ss3 Þ* V5ZNi=# O<%r~zՊ`$%[XyE~?Pe^l6д[z저^0*l6D ĥHХ [ Ç ]`ޅ ]>4KЩoNO+_ڜ~F Z^VYʅ~fZjD8:b9r`Ӛ9ڏ~VS#V,d1{hll|VSw)7116a=|M2YCCCc4&YMMlBäelqѻ%KyDV;wE IDAT?^tمzwWfCM2HԐ|lMK$ j+JqקOsssbԡ2#d^>()ywo[#O}I&z(+'o0e}^K=ѽ=y UUq Iʵ1qM2Y]}}BW>[$cZk{\싲 G5">"w Yr\:zx,?VW{Ղ*9:˟cb&Nv9#o l#bb\vݕk`pA%0jvի#GP.E~nC捯W֯z__!ư]fcUªAǓ ^mycMvN޶;D׷7IiUN]j/^<;!]&^$eۢ紕aÇks0.jǮP*hCqp9N/JK5lL"2;w݋XΐŲ>/>{= vWmhA.EhHPhHPjo'@ h@O @ A@  A@  A@  A@  il.|;wOERwH4Z|*A_MAhbWMT6h|#E]V5NeLp[lG͙M _~E\&I=' y}]&[X ywuձqV33ӻwm7urݻ$M2s6\]fی^8?kW+|]>UxP/%%`!4p;wW|ѣ?cѮt'bx˶sw뺺{ܻYM Qrh;[וk >|(ɉnZzȐ-uzEZ]SSS-B'ijjZd)!;#lm^QzPYo# 3WcUqAy险&hÇ%Զ \b5161~"9'2N@MMʈ<)n^Ӣwknn-]s;XE-)y=Lvq{-:}g״sIxMyHό.k6lHU@]}>s๹{9Kω ϩ3&:C®߸0pү?ur_PH,H"qq޼TJ?"G=]YL;={6DÍ@PX%5|2~,ayJX4ii&ho̼`O!K[ xdfKyt'GXqJ?K* #mrYb>Ste_S}z/[eL|q򊤸#q?s [bĸaD%~}*):7vcǞ>[7nR(`萏QQaXLvIOd -PSS{mg'Gu;5|=$kq1r2>:xæD\NvwGNN!oUSS{41.%9~ϟ/e3h;8O3X=zta=|ŗ.8QjRMr+q,>HzITZ^Qr8QiAU" \h?zK'IejUWT!K[ 8;9^qSΙ0a^e[1)<˟djT"HO^>fcֽ[7ۍP|Q.~2B%ۑ# ZHP.<%%3je<pC]i%){KN|42L&S.1wzErO'VT< &K ֭kWEe@P̚J\*QT(Ę411uXL,guZԎ7nފIRMZI+=(ͤP033%^XZZ>( TWW+"UՄ*oP96\w1jU"րh z"t䝼zfekf=}T *-,@(".kjLz$G /#VVl6%%MkB#O}I&z(+'o0eIK^+=ѽ=y UUq Iʵ1qM2Y]}}BW>[$cZk{\싲 GI>"w Yr\:zx,?VW՛2߻WW a_6qԴQ}HdE8{wX 熋D **d!Ƴ+WA 7^=\8wuFS|+NoA]fcUªAǓ]]iͤE으m;vѯo_}ko,Ҫ2P^tywoz+5@"Ql/#G oƏ9cׂa(@m5i-ƙ8}ςϾ(-հOI_xho&FJ޻nCmm!e=|ؗ_|bU. C hIh1!A!A-EMǞ =)@ P6@ DGe@ttP6@ DGe@ttP6@ DGe@ttP6@ DGG"db*||"@Rj3h{e@'IBP(_ĖU WWWhzl'^Vrқ<.TsIGR64 VΨW3!Br8& j\RA'cy3C&%Vp~ڵzdc9?;EMMmlB٢ߪ0pJF6njff O:da %ku/0w7d͆Meb+#ON}~"-0sI$jɠsΦ\K+KL&K֬3nW+{!w^'2ݻGQ{-(OE6m3z]Zu16;wW|ѣ?cqA -P34c % n,hlRIa*|Ƭ`H}-ۏcs]H>[tn ߸y저cƏ _BVG.̬򲱱 OYH'* ۶bm§ O;//|mOgm2jq1p6b pVDz n5kbl8Bo&lLĐ7]$\&^E78DZyv ]6kZ}.@ xn޾~/stGR&=}۾3kڹ$d<|3oᙳD+i3I0}͆MTw*(<9u_q ǭ޽{aaX> 5CCÄd#c)|ݕdO{Nd<42/!ISw.NWN>[ f]q3Ea{s=F0DZ!ao.ޛ JB8b2{xNt͙w--bnf~@*%Zi^hMZ8rQaĺ\7+X̾gΩk.--;{يU\7/3ggԎ]|=$ ?.H9w?Pqʉ1ތzB|v3~TZ^Qr8QiiFp?x ( ؏uetsc,lGfYŒӨ;o=y|7mg[fՅwTaSSJ+'DMKINxr,-UFI&դr2h}- j xiiY|a;ۑTR)6n'ZB`_wDrG\}V:Di)O @,V(pW4L)rE6x͊'O 3;g„X|\XH025gf)._ b\L_ʽ<0 ek#}<0 =ʶc 2xl<˟dD:ɉ_@L& `3Bb4׬IN&+;B0cb0s:i0wPUU]W_߷O3s3esszZ]:č Ϟy1֬TOi77mda aI H$ScuMcCUW ]> ڿ_>}+ޭ۠94L RiVNl6hi'OjT>/SrPQ@ NV4`իk\*8EەGP|qg];YYYk:Dh)BY,3`.)BSS으@으o*B d,--*+ʭUUAVOp85sO'VT4Aݕ411";8|~ʉ>R,啕B1kv(qP* A1PTԈʄԸ*Uq+$*=F~ W] e7KĵMZ\RmpIq⦧rUT.9y'^GaO_J)9uj>Esn*PHx)_7 ssOhWW?#^U4,-{ӳ?kZSbMtbᙢS۔}<)5tdo,xZ҆C+5ZJ߷]ta(ܑtÅ.!0a0w`eef{gd2X"9z,5FZpu?u]ɟ) ~9tC煿:y=X,VS KH?>z^Dx?15c̃w{nAÎQ Ư۰Ys.<vFX,{>h5 *Qmh8h]TZYY._EaS +ĥP.o6X300N-r绐..#Siz @s5{Ibn]fNbgcCCCA |!:*j0Ç'ɓ'?qpvr]=|P2߻W/`*0 ǟϏV|1n'OMշ{a 1MN ZXs:FF#V.. >};~byNtpv;oiah4d2ANnjkAqTʗ, 8S'W_ bR T@Ä́Ը*52E6hqeIR962\q &k]f G\,hWYM핫WG*mm(а(ϖKb۫W*FA<"]fc*BvN޶;D׷z+ڎLEœAǓR?o%Ҫ2P^ty|o@~? t(?>` IDATJ3qT}QZZފ#cׂa(@AA򽁷7@ «x 8} ѡ@ @ (@ @ ::(@ @ ::(@ @ ::mŨULPE&!x ^grhi>^4#Cx޽N e2w]]բhsfی^8?kW+|]q :~aZqBÀedd(apeMd9@dLMR4p@{`X,޲ k-:7gvoܼuvP@^vzf Dž/Za !I_#|^fVyXPxՅ,s$xymۥm{ O;//|mr\:zN/V4.fFf0o DNaJ faVD yE"eU{~sNe!aGڕkynSܼE /2 [́wB;wyO?|$e;lѹ;ݼOBÇa >s๹{9K6]l$H5}=_3SgΡ1X$pVHXL6ɉ?ɉX"~{R:zLiЉg1FF}<"$iju7}g87n&=(}͔5׋e93;=B1cWY7ҥ4̛620̅$ l úw릱!Ш. G~߯_vLw6hgf8;M&F LPbQe.Lr;z.[jЀW*OIaDpA/޳k6++Kz -S X,f00f 0\ S\{9y9y|%T `iiAXZZTV [ ۋ<]4135p8k2̏Ox"˕jL;=Kz͠RP(f%. =IS$1p8&P]]hLeOoܼ׮zz(9&dFջ_03Lh=E?4jA?4<]IIzNA+9#3{ä_rD9W\QBFTf,,--È2:v!D ܸAD;%Urq- DF\.6`( 00M'Iն;{֌lm ðR r iaaBaUV.rL_R)innx1āqM2ٳ؄DIuѦ`133w*Ν;]W_b(d^{ ݽoE2L,=_PK^N柺uOԔC:ˡ_ؼG,+)VU%$ߎfg{X"qGʽG<()yw4D`dy„6lvuqV.UQn}~5du IˆnCt!B뚥rr/&6 ӊR(7d`,yN]ˆorwÃfZj@ zΚ=Q$[3 !᠁f߿ܐ`j5X#]f ,-,-q&H, 8S'W_?av6#IaÊ9&mdi;yOZ>c\;fiA s‹kuX|&,XZ݊:u6`lr Z64*?_s๹{9K6]l$HvFS`Ə[{0 ð}8}&$&=|k,Q7[䣞,&~ڳ?<,t'YLQg/O'G}1IZ]z}g8q7n&=(<U6KŒiSgLt]j]}>fŋbUFpҏ}蜺ҲzQSXp2S}xFEX[,^0//+3jNq򊤸#q?shjHtҌi>-kR"5%)p£2f8~|(;ُQ=ZkuXَ#.3r]yںUk֯Xill4RZZvĸS9aV|CX|41./+%; T}K;o=y|mg[f?BWSMMM *-{;MӶopܟ>tG#Tc5P㥥elGKR-^ugBڤhe 'IejUTYΛDMKINxr IcŴ'fh+B+LW "pvr~fœ3axX,>[t.,$br3[D /fX\.F^>^e[1QrLfcֽ[7 V]-466wQz/DjllԷi*< Dl`0i]l c5Re.'tϝ?'{~`/^b;Of;O^`;OVڂu;g +|l?vP0B%'Yx+\٭k<̔6n[__/DshvxQGGA>@ྈ_YZۨ4ss3 8I?-Sc:h:p9}5kZVFjؗOװ,kԁˉZ3zdEeeL\#,&sQFWTVFs9j9d6hrS ĢWƴw_CDi**+mm&C",9t<|o/_~ JT&.& `ff`0E"Xrʙ|s#{&MSq8Ñ؋?G*޹Sg:}:xee1q 1qcGG/H$O}fdd:V&>ѽ{3*";~89:ȦN@R1442Q]]]mmm;\k(H [ū%xlGr)hffrŲ/rudQ]SV ;h^R)Hč_0T8l]8xbȏ+bo@P!3gZfe;͜5Abǎӽmme~}LgsglHT!ի_:;m ۥg|=}wIB$6J?olW}QѶ&9OIݫ:Mb$RQ#2ML? n#`7S(ؗ|\~5kS̬E[X<=+o_D EJ CWWOOJJ:|ͿzdtͿ(>IlR;^zmW_)7I=?PrqcFGKw`yO\dYqqb-LXP(P( B*++z#0>$5 Ge h34̶1l^t mÇDٷ h H% /n@]N1aX٥y;ƞFN_?ehO??{VE~1OMvu\M[@n1<}/U:h탗$U~xȊZTV[.+ /?Cg24Y|m|~H$~:jTOw C3ݻ%E"Guu5i&`0Z.dnnߣ#e=ISl=oЄfxmT*OLjEөԋt#yy?Яok :r?S__ lfᭂ3ly2=%y>y+I&GvzF~Y__ɑ-[h/_Uć(/;]PEF/7-yo~X2%.ou_ee#ԧmق?tD^֎`u@"3yJ!MwR H?C*Sĵ F:X$~E70oΉ4ovбPYY:dyԈ]&.+l^3.^,샢>G&Mrtykw yIЂ%lgwsX-ӏM;獛*@pyD"{=G"By:xlپS()s|17':;8qtMOO@a$I:a8ݼuW?[pbo_޾s7X /11NM67;7Qkjoqe;aݻoH6;ae5JeX0on.]tdЈɑ_ѣvnf~2916+Wc*d28u`[...gJbANzd=Xc dzzڍ%<˄1={P))[UVVIJ{tՑY"\xypy/^=J#G\zMe:fL.3sOUU7 YrR\\rfa|LTnVWlܼKM H]tՉF_ݽk<.2z__l>P\; "ѵ7bf mX]"cP(G">P(|eJRBґg%$nDFխD%D f>GBDxli)·o @T-i@*HiFҒJ"T,%n߹kH?~/醆~3Ngk/]8N|{)mݕJn%=]TK?r X z] Cь |哫RTEBaFV COosD沲Ꚛ޽z@uu ɶlllTSW8s3K9o_H$5?E~:bq=gɲGc ==*ȶ) s. `0TjV$yf>3ދώYX'N;?TF$<$_ l\"cPQ=mŪ5ٰva1ӽ܈ުhIH R+# affJB%BsPJD@% BcQH O=23+''3+R.LMM2&\Z2 =F, OX,lYY9*EUR+Jg .R;I"3&SX,&zr##C ΄mڲ|EݺbGE3S<ܔ({AC,KFar@)-Wlۿ__psqK25|\Ep8gN95T `B|Kd j: u ?' 5SS"7*{bCH%T)6["4рL,--N_P/P@"hQZT a2m (nFT*u)[.P嚿BWֹs'R~ ,OUU6{Y\3W^f %)!݈[ Zi]_X \B'M7d:v c;OJ;؂bwi_*1Ƽ»C>]k;t`Y9'߱s,f]`h](ξyvyyvʈ܈=xbE}!6ܾYDhNP.OF|Mj#БP )6*jm5 /̶]GE:8Ox<%5"G@ :@ (@ @ ;(@ @ ;(@ @ ;(@ @ ;[@ IKz4jJ^TiփGo3IB_xf=C4$hhƁfO{u ʅr$a]佺l9{gT-ꠁ׷XRVg_KS_DjdWf(jkkD"Ir m*-P?hq&3Jm"޺-MLٳմM[=5z"Z Һw|$OD.r5`0Z.dnnڀOχ]y_2Z&rlڲ}oN/ZSur[~}_ ё`MWZnY[jii:dK\‘UY9[C6 e \Q`7FXO>yځx/7 o0gou_eeUwЄ[g ҹyYǐ S(-[|>P.^{!M`pPi4==]Ow7{;QUmᗲ557myڲ׬]/o~r͜ sl9DPWucBѤI.7ςv펰upqvzՙw:j(B573L;`\YQ=}NM67;Xv\QY ϞstYNWAZ<}jJh h)';q\=F1>ӽzwP(}>s@юt`3{XO-Xbvvq?W g$D'ssqh]tiq]qs]P5]\Ip4-x_;zl#\2W6ncr2R٧fhh [1-*+$%ƽ~]X⒚~)+V1`䤧xNv ߃xšݻ=Nqqɍ1QYi_ qv,]iw_1I$Bg1JNPAMYi3N?uʕѱ!Gi)w_vlݔ}*AI6PYYu}{; "ѵ7bf mXr2R==vVGDrVX"ɑ}:7xee%χ M]q9Y цd28u`4^">P(|eJRBґg%"*j=>aАE5`dl$hllTSW:anم JԱ#~033%oA(fd,^8`0Oz:7pwR#[bOwW*:rϱ?,z/ ?g2olJud?ye[\i>ӽQ_sԑ m9K}' _@ |gLPejbr,Di)بgPQQSh-Mtر_ 2'.-_{Yt:3ݫْsVZӯO kר,0ӇFLg+j=>aА"dP^^.keFF*s-rxZ'tTAŻp(-JҙKb1kdhZZZb7'Fa͒t5c_wlmҋyMTPGft vijjRZgPGG':rt\O/{.׵Kź͕}Ltl˗bT*UG>L`2kj-DqHKϜdks*#+lk@ M]Ek)@d d׈R<LMMʕVQڀO؇#4i{fݺv9߲j\mm-uU-˾_YZۨ!{|܌B$%aN9eJjtGN]yyE)Z__/'gN bllDR_R.{W433]tzU`fjڽ[W=E׬07'#;լ`0ϯ.Z-E8a[ Xn] L]hML++ZH)+/>xex"kKڳGS8xH$KN92st:=t.bZf0Ǝػ_PW'HgV033]bYqERTWWבmswDuM tnfݤwϞD앓QXL18At`UUbNwֵNUpP`dTtjZRUUM$P(H$z566g))x]O{t́@zzǏ[ql:"e?PHT[[x4!`Q1 "QuMM\[ J[ rqm 49`ٮߣm'Lr<ֻWOzz!W<4~xOPlU+ݧNup>3B7DFE߸YxAu0kn0_kjbhTA]Nw=Sc'x(8VXv:7_yV 7,M"Ǖb7 p3t AXKovW[?Mn)'N'qбt3f[[Yi4u$RV!xM8;:'MKWjA(*kh׮YrNn3gutkI6pYXLޭDpR(U7n6Ԓ'6UWfVΎ"-znj."\~5kSxe޾b?.lRRޝmR;(c\zmW_)k3l;) ,+.VG@C3:PLXP(@@ @ O A@  A@  A@  A@  A@  TWW/Z4AǷ$Bp/^l!MSаfrՓ4m>4TmF9M dٴe,ߜ̓)Ǎ ^ɓ ]#3lKau$#- r rmNZ/~poF>}jh^n*8a,hW Fik7'nAxϯ*+ޯjRx6l]:w"/"Asu@"3y 6O҇#4w  8:fog{ \-#R6f-7O[eM:5OryD"{=G"By:xlپS(b]xx94хYp®.S/:sNgXY Rffi'̛++ω4)&qfݾsRݹ?N+*+ٳb{?.*A""UCֱ';O{Qt $M8bWH$t~?XFR+d28u`pFZs|I89O744+."k!k$6a% O5T p4-#c#Dcc\ s. `0TjKط'|)y B0#+g 7}ӹx+J1J,{Rԑ#<~f{Q<ψfQTGOɻ[Eo :3[5WA10ƞdwt:w E X&&ǒOrz`N0efe30` 8SRu)++ݫ^&`Fc1ӽD*! n9S_B~Wc̨/WG&W>+Atg.g-yJtر_ 2'.MɈ5{ۊUka OT p4 (rY;.+/722T+n9:[wH*ޅ@i)W*Μ]J \#CC}o߯/8)m`4 k;==]);vck^k:2ce㟟 LMMKSSR.O>:::ё}|E[ܣGw}>py](hDgc_|%R:|`2 LMMS QJ+vF?`ܹwlcF /{~3֭CV苷+++\}LZqHKϜdks*#+lk@ M]Ek!k*kD)rLQڀO؇#4i{fݺv9߲j\mm-u=_YZۨ!{|܌B$%aN9eJjtGN]yyE)Z__/'gN bllDR_R.{W433]tzU`fjڽ[W=E׬07'#;լ`0ϯS+<^D*Qk׮ya-sݺu377SG_cL첲|~m u}L0fs,&+ڸMɈiS4^|<1G 6er)J6e1\65K+y#E Z!Ǎu(} vY0?A>@ @  @ ;(@ @ ;(@ @ ;(@ @ ;(@ @ ;(@ ӊ@uuu𢥊=^J|2L}Ӳ!/%mZ1`X|"5 txV/[usoVH6ms捷u,YExw.X̞6(gD|,&of?(ZKۛDSfƞccq<%dR& ~*qh "m;ޱ3y?hI~poF>}jhN[/̙Y9g6m>7'dzJqc-})444,Zmgv<7+-lkp+P6mGm˿Y}U{Z>cH$C~&/R6qE3t~%ʪ!ΓݧF=>Tq-_avqeŖ:::qWO;뺍r- By:xlپS(b557myڲWz% ̟;g]RTNO-Xbvvq?Wush$Gg vG:8O?b=]_B;u:v<夫;yw#Dj۔i8~n߹fTy˿h9L^1!cn.Nt ~w08(p4큨_CRTj=>d^>yܦL7fhiF.]t'xZ@-(zSQK${5k׷ qEΎY>{豽-۴FLN:xx!7 2N&G{ŖZ /^LIJH:dQrl :ucC c19˖.]T*<&lMM/k7X\\RS>5cŪ5,na{HĨ{テ "ѵ7bf m߸Yՠ7o3h62ӎ7Hb1Q: #=bQ9kйsCXu5|ʍI'Rb;|PjI4Pj-rM=(C">JSđA--j8y.Cw6 v}5gf?d ftCC?'3dk._ E X>ӽ%o!`Fc1ӽV Y9g0 =Ӧ:anم JԱͩԕ-mmmlkT*upWKtwW,Q,{Rԑ#<N"FEE*yxl'O>olla>Ι ڌJ$ ,W kHVNcc>P(ݻی1tz5D+[WTT_ O63 |CO6{'g5*˓":ڐqܪj⽣ݤ҆G oSSܡ^Yt47.%oO/++b)W*Μ]J |Gr|EqcG<`'f>+X,WldhZZZbخ5L}x]tQ'4ܨv47'YdrZV^OF3}7 w߹{/l[jݺ*ѷIZ|L~ O.G Oy6^ fݻu=;CIFH Zi]7ⅿj)/l* [uTn|~m ւC>MZmbmmmx])(^CƉrxV]tH_،=2&.ɑX9v̨}#+*+c9l*L}CGjjkk&˵IBTtLHT]SpflE]]]G55 >1n舽uuό"G kjbhT+3fǎӽmmeW"1&M\vl8AKJijk׬*-岝 /x'F,PTkax=*y$)a{;v&;M9Fi5:iŋGV"kEo)tCV<yh$g4E9k  YrWs۳w^Xnk,:I ޡTTVݸysV0'6UAC9.}߂|@\zU?<ٌ׮aD9#|p޽[HLhb/_?m#4zD3h5,O2wgԲtD\zmW_)|!HKJ¶C(ryPG1lc9d1ȥ;x.Xp?CsZJ l#,|ςA<hG4G4E"p<%% HÇ!7VUUClIqwJgWRmlۼ}iAF4Mǵ|o ho7@ Z A@  A@  A@  A@  ih:xREz@}L\|U!"UDajҊZb~۳o'R&:{CnDb{b6M[=5z {<|A$2661yT>~`0VC-277kvʹgT-ꠁ׷4UVg_KS__hmś@m c~poF>}jhfᭂ_=z!ihhXt963xnVZPV[&u@"3ya 6RY9g6m>7'dzJqc-}:-B}BׯL}}u3 hZ40oΉ4ovбPYY:dyԈ=".+l^3.^2Q S99w7 < ZĖKYi ӖfzL.7svPHdcϱK'RܦL`t7xK?s||Xuu[wp<]񘛋FڥNo. ϯz*JR{Eos :R[c##w7¦쥎N\|"ӎNM)Qm";8qtMOO@aD#ISf=gD+bVqh$Gg vG:8O?9Z툦EΎY>{豽-۴FLN:xx!7 2N&G{ŖZ(y 1QGc=z(,dy9navcC c19 D3SS,ł}O)'OaYϞ=|hoWYYu}{;EDko&J4 H(..q0>&*7+A6nF Xiq$1 o|˶ʪc1Iq_=;pI$/~5z__$,=ҹsCXu5D Ѻ ZGJ]!HKbXF^R(x2%)!HܳM)G%\cGM=rĕT٧fhh [^?ST"X\\RS>5cŪ5,na{< e4-p}W =3kq,&S 4;N8![ϯtJP,:n`%,y 3}h4aϙ|,5h*:bիץPW'?0xR:vT˗Y9_c`s3SUbm:<}J$Ƽzzڿ/^;g݌ Œ 3 iSO WYYyuMM^,,ZvhA(]mƏ`zDCqk l9Nn+VקφkT)9QJuu &U檄HKط'|!bOwW*:rՑ "444=zdfVNOfV \`eLMMJKxee1s38-eegc_|%R)p\PyGWWhrT~}3s[vXFb" ˩֥\T:sV v)x|>L}ԯ᫵DF3}7 w߹{/l[(K.0/ /;܆!wSrZ 岱HYy9&?y[>D n!J;+bX,3Z9M>Eq9}VD2bQoߥ\C1xeee7<իXPUUfY9މbYrɮ9gro,oh9b8vU6J%&R܌B$%a[d9I3 kK>CR!-^0ҕnIm͉ W?A 3Sn]o c5Re."~finN?yŷ%?ӚMƌYQYV,;fԾѱlP!܋ IDAT#5DG$o!PLHTQYok3BD"ի1;1n舽uu"-zݫg_p.^YYUU5 (P˃QD">6!ȶ۹;AG$eff`05}iɻF /_*uA$>?#+g`D낈&-֠st:=t.bdscDꚚ#RG%8xH$KN92|XA33ӕ+<' D^49w8!?Ŏ.C-Ϝ!WkU\.6s\G;~}Lgsg@ǎӽmmeEѰ?Z>uXqS?EKݧb7{ɮ:ݤ֮>r4y'(xqqI  @5JKl'79A,--716 h?IJZ[ \xyk~EUuNn흦ϜEZ ѺPJS]zC'9.XV?yEDkCq氡K]|㫵҅-Ѥ9sǒ|~:yu]Úɉe2o_v+ɺ@4̬E[\DcZ&lZn߁+W +vCrpxm\.j? Ҽv, 7ftԡ= PQRlvS<,YV\d+<ъLa2knǍmN> YZN2eKԏ+kAH D 8ۿyOM@  @ D+@ ;(@ )Bw@ Z?hh@ hh@ hh@ N+FLe%]1B:Q՜A6hR -HQO׮$ >䥊 AhB+jQH㵆kw~ш_~ΤN߀MT4Hx"~^+})eaZU4Fiyށ*½*Ic ^a8cIeЋ-G?XӝƦS;H :I_S޴CЇ"9X >=h,I-㮴 'G@wp03 HQa-ލT&{He |"¦m!^|:^rܛfmP?N]]i?\jP7PToOZߑ3:QeuGrQ&̽1UP/8-& ܡ35TbH3*_T4h{jcyY\CkH^le>iPלT6ȳ"2̃AhQi ]—m!ini1͢_6ptlǗC:]?/$'ju*Ť.Ciǚ$LCDR$e J\.+e ow$5RPaLG{:eZ,i br{.2V=@:}5@LX◑La^2cPX4 1LǦT6Gኆ6Đ #,/~.mٕWEoT {"j HF{Y{hRm`F|o˥TN7/ FЏPox#T~$m8 $ BԔ"nK^fe. a_G5cP@1 ,~3 妌҈BE< `&)e8Jq]35R35e%G@*V :&%&PURSE9pP!]kY<뜏˾ҎsQ \\{_@V8MhSA,k&F\*@JJPT s [,d>_/1 \.rེ (*WCyR8$cNf//AKCJytw7ܐ>dcl4^.}AJ3~[Rhz3}ԅɬ&SLNQ%z:94`9W^+J^FK.5~@SʫX?u "]0iP+EvL\#574qPS4&4+=ˠ^yP9ZCG. q:xu>@T~^OIc``Bxu>)\}{0iMs 2e`@ :i|AsY4!҈ =XrŊ^엣{оD5p6tluA9>n&Su2P ? a4 ԅ%gT,oTLgN<!(|yDa;NFvQO}υN'yQk<2q^,4ȥ _^h 4:,ţ0* c29=M{=n-IXھu2L}yFVJUTۇ _'=@,?X@X//$~G2'm-ØuRH/HǤ)u2s YL>mGt*%7$R9CG/$?Ǎ4hg?)&SXc/rZ&6nT =i}1*<KHV,$hbyRճ-IHoڮ rN K{2ez7+z?&Ʋ+ *׹@3e]Z?m N&1hhq)\1즯59ZPrbnu"RZS̘|!쌸8g$ǬkosHFѣF[aQcjπqVv֕`8 M9x***ZuOԎ2 ; _h5ij *-7/07SsR?48Pf_&LgQudoO˘BC"9eR{Mc8c[ yuw8-c17nz@ |8)'rխݰk_bILNpsa]:w0#;hά4o/-وFJKu5'r,TTT޺}g|Drʵ'tڍDFK^+H9Ym"WZgb9~Q(_12%1@r‹;v4/ !69{.V#ZfͳoCKϞ=qYtDFl>p/..Oo;dvIXٳI'<..G kkkSr2憅6ʺ O>9braC/^1}DIQ[#MMMBarCM ]F0 k଀L6~;oN;d=zͮ=uLځ `O{D5/\8+ ӡ1 1XVNP߱}|x/@KK{ Bj8]^%gϞKR\F(>c``gWlB_ЌO?8VVXl.5 8 NJd*hVQ%񣴔/˧M!6r(hP$2CbYբF$3Oh4Nlr3 |5%~xBa]|>̈́ķa@YYr.",+#^ \a3.Y,B/500>8+ ӡ=,B7g p|03bUPy.lk]{6H$555~Xx=d2YzFf#Ա>~Ba\BQ^YYujT*e0ݺv{5%$,4dOLlZzFm]Kee^*flYGkHDmQUP[[[x,,xL&AQȪ[7F *z`s@abb$ꄤvchld9"jǮں:Nj=nՙcwߧNK$ں'ruj 7‚x%O&N)6{cV\N!՛rpn\.o଀LJuvti7g'EKR'7/AMU+b>1mL!CKA3æ04bdd{io&O"%RɯQ;ۍwVq>Ez=ٯ[׮3Yd1F76%Vnɢ:KCiQ[#{讳VK( 3idv=1W2=GyTign6on଀L6jQsEK8v |K#& .բkq1K%bբXVvFmճm բ+W |TD4ڱ8`#bEn>gv(JD2~D9_}[\adT{{JSG3Lܞ)woՕU }7P1P&$80$8jG081P6,x{o WnA KPp&eĻs_;?Q*plE*~4I<@4 #:ᖌں_~Bh,BQxԀR]'xOsQr8qB$p(@ Z%%OEi<ݗ._=jBgnl d9y Dw p.2xT*pw(!6<}D m4oxh7`jbT*8l6[&nVGcC!&e{  D=N IDAT5}D{)@ %Nn?r4UjZ077:EFF{A;^@nRrahА-/h4㛂y 2btWߘQ@ZG(oXb`k8AFҌc[i]#Gu={ ߐHfCwz֚57qE+o#M{~3X,c`26x#%'gQ#w>nd:xne??~\}~dX:;xfnܬ)H\-7ÇJN>nUD~}߾٦ZS̘|!m쌸8gwdX~@cG2R52lނS$ڕgjxK8 M9x***ZuOԎ?F+9zMJw M c┩3466H$c:889_9yN甊;wG'SktS;޸yܺw'eLPzaAJ=5isY7o&U48_ Xhy %kn~/bX{%&x0.;xyxffLw uGkn̸\O[ڠ򦁁ABbʍ(KVUW\ui0AwFbN722pOg-Y4R'M PynQ#!QuK'7gSle7eN/g0=z|{;Xf8r09>&K*;._Ƅcy`>'NA H'0s3sfdy{yDn٦.hL⒫ bNfֿu;ՕlH._dVz~Z(WDKb:αCڟ|@)q^(ݱ-]x~60!g:{goܼ7zGV>]~3QњNjK]R,?},@ҁ KvC&zڔĸ̴aMnOG\8bЋ.m df?gRHSSeyM KEGS'MZd $'#{GzBeoܼ 85z(][[{ ajj?HF^"Q CC3 $$߇Y, AN~}1 :R26kf00'GEEP^^< a2Z4Qo\$9~2S_uX,ʙ?w6dN9~2OK eU={ 6?իiM1LXhBR6њ. ,ӻ:僦t;DnF:ɼ"bHXFPUU \3rZV'dpq[[7[Udӝ8RðaCziШ+~hXZXP( Bt:Zs]@廑\)3d~|>fBL9kUA˙dݰ,cN"!9@+v7)z"tsv9~ LJ33.a/^bK|kss3fEnd *ԩZjzNfo~! 5NѮ,xn]('ʨkԄL+,--(ʁb,/'tny& "Q )35*xP(*I~q-yF9.kZR/~x~Hh^\YQSBŋ L&`P(:aa zZeѨoN/J38QVVNgr6-׵K;\x6uj 7pgԯX1Sd4[Cl+*\{+*b;*fYܵgoDRSSϴ(C ;w rX,qW2,ȿִ'&6-=BaeeFM`X6c5H$"QMR˵ UU] k: |jAQUnx'P^aNg0FFF&&&mDY,SSS:q~}?555es8T*ԴsN=?,aR4cccCC 2 I9Ա>~Ba\BrmLl\DRU]n-0Q#GDU[Wxѣ-:CCviDR[Wr0Dn^PZ=ZX/)e߸M - PTgG_ S.qڍNn^FL#G/MU+b68xpMMgs ̓-0b;9hfoάiG2T)TO/1 b6FnRf\b0;tTD KZŃNpsUL]}?BD"1sD~@u >o ?ڏx8WJmhhh#aY/P8+dbXTۯ~sLLǍq62P0*JRu&pOwc_:XZ zzX-| F-Yy6O߆ѣFF,]B+hoG]R,Q[#{讳VK(P1pꔗSFFkW-5og 7>3l/T;Y$D47ʫ׮ dM&]y \tyђe'y}/_҈BpD"qq\*d2D"_g\!5%NSTټuΨzԷMh;Mf85r}^S\ZХs' ^HA߬F}>"foܽE@+xF ۃ ntԑ3<|d kCCCw+ZxG'O|߶uUZDe-M{W2`0ۯ޸߄! "1 o @ѣGk7n&|aϾ]0N+fZ5"Y--}Sd>%(@ (@ ZjjA=@P6@4膇@ Zh@  A@  A@  A@  il*lTGy֚V75@$dah DiMnh15 D#iƵ9۷4_zq{\!HfwnReM\ialѷ ;7nފڱ޽ kdV4r_ L 빳C---޸VhɼYTb;;GY޹[dϏq_#, on)ho \-7ÇJN>nUD~}ߵR\+~ÿZ#ihh XYyJAnvFnvF\n\vk鬜kosHFѣF[aQcj>^2bbX7l}_605`tf@phPQQ\=}vd1">_uB;GWIS_Po9&spqv޳(^f҈9_9yNJ#uuk7lvvrZXLGn;ۓ2P(?Sq49pƬnG[xktS;޸yiE\SL YW$&x0.;@wBQcaXV *aAh5qnnAoMdgwo{gՉQTi-;BC|1Az{z8٧iC<i X8kU_:9{x:}f˶(n>gۜՎ/pwu"~=z|{;Xf8r09>&K*;._Ƅcy`>R[Z˯79}4~ock7֦$d s3sfdy{yDn٦)q^(ݱux{us$W%=޿ߧn$5굂ĸYz&|Ūu&&c?veȺ Cm_>< r\Ӂ@Z:u8z`%.]q\ vorp/..Oo;dvIXٳI'<..G zLkaOG\8bЋ.m df?gRHSSey ' c0 #%բ죩&z-\Ԅc=#rv{ 7nzd=zͮ=uLhp055 z$#Sy/! wJ&&yå|33nw++{n,&aXb6tʍ̬sg3L',-x- BHƣ̚42Rj域9c:aW)CBaYUuu=zjڡ9P(۶l;zܼ&EW__,@;Zݿp>{XS>hNOiZjq ̸":!3ۢvnߺق$+ ^ٗJޞ :ɓЉ~MMMFv,+'(XVRx{\*rR\.6=ؔA*@ХsguZtB^Q+UL`i]DfM6U-j!>w[ݼu;rZE-hu&'7;CʀkJ̅2hi- 2\DXVF诽V!2n،q1Kzdkj T*8x1x{E씕sZubff\ ^J|qss3fEqNneh J/ʪd,--(ʁb)/'tn6XxݺvQN0c?Z.Lf2dL&DbS$16fjTx89_\=,-Fx}szQr@ T<Z kΧwNmY@nΨ_[[/_Zc<,r|fkw |>bXyEE\B#6{wF﩯/Ot屮 d=pמ IMMM_Uڬr@*2n]ԉd2GcWm]Eb{!2 UU] Ua!{bb3j@ VVV(*Y,A1$&)9E2d]aac2^>(*M̳g hHZ$d=@Yw1N;1'@ %$)5H$U IƎ؂zLkՙcwߧNK$ں'ruj 7‚x%O&N). wr-a PTgGOvsvR.qT*ur d=0pT"..ӦtoI_~wvy,{ˈeKh4O_{c;t43lj@ NW9͸A!\&DZu5%~~DfJ;zSP[[qKՂ9_}3|VE׷ӋkO>3cz P(&l9YQQلm"! ;K?t2+=rZ!6Z)=Έٍ.ܴ>dX~@cG2R52lނS$ڕgjddF47eSfNKWl< ?-_:'jnL#_/st45:0a8gw=ݽ&NnާNٲ-nϙ{iޞ,1Bᇝ:v2sR?NK8es@p荛^@ |8)'rS[Wvfgo'7~-19ͅAABbvҹI-"Q>0 ðݭ>T‚&w>ܘqnnAoM2T^f҈-`: (ndd`o;fZhN5o~ـSFf85 Ƒ1/^OTq5&#cvR_~ݼa{KTWNpRINFȭ 㪗[xD6J5H$\K{2+OWn`nFy073?gVNfGm ͯ^+H9Ymzتu&&c?#+**S$'xQcZ Cm_>iϞH:?qqɮ?bK9{._өSA.p2e܁}4ZxqqI|~!K:nMIL*/A^M;0hP(m[6=j n^쫯W sur_k:8;x,\Oޫ"WAu'{V IDAT6#;thbAUU5p͸ʅffjZxmQ;olAZC-ۣ,BSS# ;â/ϜKK{ Bj|=bf|_ϷFTqMMJ!~dթJt:]*@Хs@Ϟ=Jr\!0S!,-|\>mz)FyE"lX\X-;fUt4?i~WDnvȍkd@>@kAnvvuTLH|{86)"²2B*vf\Y `Hvx{E씕sZubff\ ^*f ͊J 3,,x =*|E9d%+Wo TͷujZjzNfo~.L @R1Vq:7[<FrYY9C wz};Gؘ7`:r 4E'}Pev| ϜfZ=u5pʑ,|fkw |>bXyEE\B#6{wF﩯/Ot屮 d=pמ IMMM_Uڬr@*2n]ԉȎKVrBOa!{bb3j@ VVV b{!IG( } HTB:9-jkk ~1O &6A"NHOϡ̃"nݴۂ@hٳo4H$P-ef ,@dkA F  k}P&9j䈨jp/zVgvhȎ:-HjR Y@ ߖβ%i4x׽-vK}GLx1_:JMٳGwZz@L#zŸ3D\[WG  ksvM/pEK8v |K#&7yL|dCɉ@.2; ޘcY9nWϞ"ZiBwi cF.^@ w@ ZM@  A@  A@  A@  A@  il*l|m G@l͎(o>pSp~߾mZ8>mnݾ{ނ7$ݭF`M\iXh{_ܟEE0*ֿ_߹C|ԛqVԎoH̸>^|'yWYE#ۇn=,10LAsgZZZqd-Fg:xne??~\}~dXf)ݸYSՂ9_}3|VE׷l\?ǔsbMsH=8zȰy >,y 펥:ahtWE7iEȮ쌸8g ejDMBz]{vY_7@l`jìД+]'zD-cD|v^柿r_&Lg_  upC|e9)Suuk7lvvrZXLudoO˘BN;N?zk{n޺Mȫ0ktS;޸y]E\SK Y?$&x0t}gtXhȘ/F1t##CoO{1@ D5_0 ztWO}$ܼ<&N0CqK?|F kT‚ O˅͍v]PV400HHLvvwv_z]]Xeɪk;{x9.X&%h%FxPꤩ*U_:9{x:}f˶(n>gۜՎ/pwu"~=z|{;Xf8r09>&K*;._Ƅcy`>R[Z˯79}4~oc-x<"y$2t ϙe)q^(ݱux{u[$W%=޿ߧn$UD\Vs2+}WDXĄs,;'P4Q~q9b# x2tqK.\8QOJ87:a_r;Z1vs]@D㵪مGvjCkO=K=t`]ĐI^6%1.'3mnXhSF{mc 30?F(72@qqIu(h꤉^ ,5dzOܺ]^@hBl`=DzFakkkO>`0LMMTK$9bhtabҬ gnrpi)̌JK}N~}1 :RZX3l&42sdO9aBL&aNh`HTs. çL~ ˪{UU5*ifƭBٶeѣV5)zϾzu$߇Y,[մ#kU˅.T. ,ӻ:僦t;DnF:ɼ"NXF=h /\s$XY$t'ξT*t0l!%O4F7NEhjj2bİcY9A~Dzr~X2mz)T*|K(T鄼)VR2d f2H!,+rM :}4?+W "nyvƵu^ T5* hV\xB]Ai.2P\(,Ӹ h0:2C>dݰ,cNȂ'T*p87b=)+2:̸x*f ͊J ,,x =*|P~ZjzNfo~! ,--(ʁb)/'tn6XxݺvQ.cy.lk]{6H$555TiT`0uR';w rX,qW2,keCCC'G_EUUW@mmmXXhȞشں:Udi<lfut̾D$IJN!ɔ! |j8tvhȎ:-HjR gϞ\ @H3zJWh׹V"n4Dc Vh4o6:vb0O@(KHRkHۍ&9j䈨jp/zVGK^@ ߖ1mL*)D*5w{gwnDzW xAaz9lA3æsjnn 2/ܿ/9nEOS8Lc6̺,KJK.3B[~|'SFc?`fk3X!hodwڍwӡԴ={tʪ 8xsp6NXJW(YaP[}[C ZUӈ.?{w!(!ﳵU @p  זV-U^xR)ҪPPTTr`*7T"  IH[ӘdC(!}fvgLVX.>̹vW}9U5a{aK*_~.^Rz"n fŹ z@ d - IWU޹[>|, rɑkSd6N7Ie+ֆm<V\톈cRs\~AўcF0; nӧ<}ga;ҭd xQlv6[*EO\kMJ/WL3ۇ WV2,pI)N1ܹz 'O1"PT|_|US"*zeh)t>H)azfAz 7F~nZ {qC\ QV9~ 'jgJ͖' ^q]>v7dܱ>9jQ(={#JR}5 ]OM,sdfK _7w[N@CCM6\cǵLL&k,m\=^T&Y'Z϶f̶fܾsۯ ./ţsXͮ's/e qG#˜7k=->0]]_~NANq̹v gsZ::o\?  12. ku9n |N` BhH4<@x\8DVʡjҢl7W稽1Q55WU%')lw@]s?ٹL)c#6c{h[SSp &l6U(7msT>`X OI6_*+ HTo/A բPEy? E i>=tO9bD0Fz`Æ)dbd%#4}:} mU7 2SmBd2pră.۬IVP[1yU˗^*,*ݐ=_?u~8={msbבRt:= 4620Wq~80|Vk87c*.=Qy5@JWAWnN/$|Du[ ,40!i/Sl1OҚ>m/_hhHJIcHEN>ponnT4 ܭZ[[BQs^BVccӕD" 6UL*oYpJ|>豬Sg)8DB162O}VyF.7pdUU &HNNMMLJn8#gɭL&Ϙn E(V–%{O..0Wq~|UIRa7c = NHBA;@R8uuug O IDATs ;qbwdE d2m,Z$}}BVvpqo}}~8IB@Scccb8Y:n~bF$+˰_dipr\=JQ6ξ*)jMOd7ˊJ u߄|X{x]x}^P`j<傊]b˦v.ԮԴs^{9^D⦍ʨLUo.1 |C3-_|Wqք~0fo?7OÆ- ǫdcغ~{Y:JJ鉸eHޓʎ=jd z@ d - IWU޹[>|, r%go^K ތt7 W]Su(l.v%zϿN~Pҵ.W x:5oUŕn>}|g>}xF nw{/hOt́1G_<;kWII}&y$Z WV2,pI)N1#躨 'O1"PrF\gFE[,Z ;Ojjl*E3@ޖ2illO"NW_QHgf"HWֽ@Aw@/AAZ  H_V ס uh5  }Z  H_V ס uh5  }Z  H_׍eV3م' &hk8o&|o(hwcj*WNdsrضi4SM\z 0yݸy+vܽ{|]]z444d0=v\{{;GdcKeRJ&ޭuL>sߺUq> ^G,֢6>5c5C(ZZ"wa89Ckeuv'C 4i≓9 HgXl.@5rcFK7d[ ^LFsqvu.OSпԴ i4%[ټmaCjk~>4$"QCce\sL40xB_ZJNwzz-s#\1.;{Qm5`W,`ӶD䱌I)iRoަM;y,1n?c'^? / UA$ OSFxGӒWwucXͮW\aI)iO>"H:H$Z@ -- EpC"y{-r2*:Q"ŹoWqaRiJ7y QZP__/ac+Ε"ȝ{"!EaxxS4@RțSy#ΑaWPtZvtui\dmh~@_?u>3Y,ųgϱƦ [|K|@_MM-3=ە*)--.2 \n3Vm$35yU˗^*,7oOS/,[đE>>l?/H&oc#s7@S 7mMѯJ֛㽠j`t{;[=iS'|ECCRJF- pCm|~sssoǤECCRjY *lmm cFjooyH*H4`;bb8xU>L&?yP]=bpU{AOU^+(22$Yoސo%r<Ĥ6>IM?MSdZZBa?z lY˂ş=Wy--Ge..0Wq~|UIR#eW Jv ;GW #~J!d;/ Xbgk%~]|¡s햯s8>cY诠 |<, 456&@ YŝҲP=]k{o _`j<{ /M53Ugkljڹgk{ED"qReRm.ijO?3faӔaeip()'ace_,mf͵;5zs^[^sj}&X6+3E#=@ECkצ`6;nدtlo7Dd`؞>~3Ұ#AWE{cF-?;EzlTܟγٹ֚x_goRI3Y$J޷|Y0Z ޳jjjUE39߭m ң:OzGc?왯 A/իAAzZ  H_V ס uh5  }Z  H_V ס uh5  }Z  H_׍eV3årjS,]x` 1 HwP|Sֹ\\ŗSLg}KĄ 67wB\ o;"X[[ա6V9D451~A00791N(~ފv)?ul!+W?|XLnpPBxRߤj`9ⷾGl9kooLBX8z,xLj%O-s|9 'R?5-fpڴu{KKTȝ{nv;voo/Ow7 ESMMm4HDN~޼u+_X88xq Dv7n,еs..G ^euv'C 4i≓uZ,m|4@ F 5-# Ɣn4wAf[)HdyÆ=Y?  12.px=d3gy, OI6_*+ HTo/ejkSzzGcF"o"H<]\"ښWPj22LZ~L x r[4L&;H&NO L=^$!YmpҒX,|d`ͮopF`̘ݲCnw3FttO8Kxc#;v k{5aƍ%bCLAb--gJήY-⠕.M2QWp;^/GgÛ!1.Nn.N̤cebC:S::EE߮,2t^]S( _=c/Ey?0^+O&mr͚d#H5yU˗^*,xZPi~ S.aÆ)d6620Wq~80|Vk8G0fW%ߥ59*YhhHNMF,UKk^|!)%ak#ybd8`¡6>9cT+tj@ HÇ mi~H&'5YY !&Wu>ki؄VP8fԨ׿ E'n􌣊"}}:L~jAu" =}6>8\n^A$xcJ; 4D":,6;95]2W$F&gL" yaZ?.R>ki9z,tqO{O_OR)wVD"5T^ 3lϞ+ud؉ÿ[']} '-Z(uTĆ0&ic(`OncV 'K[Ǽ­o*0a{aK &TO4>g5wΦ'NϛeYUS[ |<, 456&;*bCX]wqCExŋeSLAzƦ{;ϱZ@$7m\/UoȥrׯO84sU_zwgu8m [ׯ_?w/K[CI)= +˰_di0kѣFvHЈtZ坻U7@.r;oDڋƫ׮M1b'_.W x:d'r 2,4lA'o2FN/(s 6zѪ"=ov6[*EO\kMJ/WL3NWV2,pI)N1ܹz 'O1"PrT˂RyotA:s/-8yto& ߼?d8y_}骾[M Dӂ:Oj) 1 AA:@AACAjAA:@AACAjAA:@AWMMM!+W˦߭gb1ӇKզY?NS_,(pz.-ڶc7SUu)7ow~KswV[LhL662\,@&)9{8ꄉƯX<JRwVmѣO>cr";gCۃtu+W+fK6m\Iw>p6)0I*+=u\(r -V]jžQMm{{JŅP >} wz z+ Nڻ5S!+Wr vQvq8) x]{[7 } >z444d0=v6%LBX8z,xLj%O-s|9 'R?5-fpڴu{KKTȝ{nv;voo/Ow7 ESMMm4HDN~޼upn=o?/X/U\=Od8_0ˊ|N> ^GWdVcmtqQxw2:;ړġCL4 Xl.@5rcFԴ VTpMFsqvu.OA%8v0,m6Dl&@|HpЬgD n.Vq;%yp<^PSS{ZeZr♂&ߺ}w[|iQ^s-۵9{$#Sq/Q(^Xffj575E^BN]VvB(-w+ݺr# kjjSҎLQ\)Hlk.w4-(/{EHpW1&ɓS%-/Wt :kzlt>9 s s ْ1uMmvQn8ПD"h,<'y|<8(D"ikS;]/UWD"hQb 2DhD"V^v :DqRGOԉ ONJI{@ D *y r@KҢpJHA"y{-r2*:Q"Źx"F;` ( ` c2&$9*U %"z,~ŹRd|sP$hR: OlIuuuRwyLaȰ+(:uzPhfb 4 ɔ\ ,{2dO]GG6L -^` 2SBЧӇ*D t3ʴKAx'R5Ʀ [|Aqa+O&mr͚d#IW-_zd}3S6'LAbXԇO: Ȱ\= <CM[$T<[[OksT^ L0АnogXiS'|ECCRJF- pCm|~sssoǔVJ@@"* $&%MNjٳ$ `gcCLl<},+$8(!1);',6IP]R`Bp̨Q9G#7fc#m|>ۜqTqxmקpTW.aeO>~WPdd8IޭG; 4D":,6;95]2W$F&gL" yaZ?.R>ki9z,tqO{O_OR)wVDN̖HWQy5ΰ>{ԑa'N n@ st22hQ˜L%vVKwy-P4L&Xl` {aϔ=ku?u W, X%se;w6~Q'1JBQrGM۵{_|9xFZq۷DL0SɵJ/ rվ4443V9~ 'jgJŅŅɉqBVSS뙨DAm;v:6s>V&KhlHJ8E(9#MB'so}; 78spq1qpXxRTVk::l g7떶UPZ϶f̶fBx<뤓,+7oƎtwsP4~AaiQ9E's/e qֿXCαw[sD0%r?*Ύ$"q!&MA"548[[Y%0l`,Rpd'ps&vW-s#\1.;{Qm5`W,`ӶD䱌I)iRoަM;y,1nrL+_*+hj޾s.L! ѧӱe&'m|~ŕk)L]x[k+F-zz/-vsu%^Vx 糉nߍoڲ][sG22ŕx4-93#q(PxbMMpQK ' +\! 5xL>B6%툩%[[[<}y$QM_Jn%e ;d?y2bdYsSS~Mі,/;͙55,k6hSr9GESyT[ Z[ݸy_0s ;Wz>8ПD"h,<'y|<8(D"ikS<575zbӦ+=.O53UAd2@ X?/^>]M8a<@031~N\x@da.KK`=U`yEV,#d Δ(h]5 {1]A =FMM-f3glp˗F Fvk6;vKĆ/&T--/%r˴)9"d)6 8VBSh4D]]0Cx TV{`_}X oĸ8aW_ 8s3Ǐ jOh[Xz}kcNzuuLɣXl6 |8~0FLysӧM0{76ohlT1q"(E&5te@I)iO>"H\X|^qa`/"hQ@V$r\Ң`o(RAz!kוQ17oݎ)5QC;*. A`/t=6^!L&7Gj@}}Z]_ů8W^#wMJ͙J^c_L#oNvEU^o L@WF Z1Rtbco?%_rsf~n8Ǐ)cذa7^=OH~ѧӇ*T\iCĖ?b~P\/0}55Tl(PAy2 \n3m$ A!'M\|˒dON^ۜ`FRtK=LN6tH$a{x )ᛶ]INxs櫒ӚW,_44$`#5} /_|Аư8\n^A$xJC; 4D":,6;95]271)opRӏXΞ%2~*H>͙Fwk?t T.uuus ;qbwdE d2m,]♙p-?xhfjL=t==_\˲;gSY'mͲb+ʡC9\nRʯS&wɓfLH<|˂RyuըA:ƪ_|STEzSOy275~t o:X|JG[j*z/qz6yw TzTrCo==eA#G:mnM'$8Δ=ku?u W, XqIŹT'KBV@eJ'#ܯttt>hkS9ovoܼ?޽m|.u[*[uo1 d2pŲ`NW v;w6~Q'1JB93ɡA@+W+T3iŅo1akϕ_ES27CV~+8K\*+^755ݻ/Ke&]x:e)9kij߮k똚~cPR藫tmҕV9~ 'jgJŅŅɉqBVS7{>]hlHJ8Ex]{zjoRm5w[N@CCM6\cǵ{DL&k,m\=^T&U_= {}򺥭;wzVs><,+o`/70ŧ Q1^n.?B6%툩%[[[<}y$QM_J⍦ t:Om1uMmvQn<}*"j[k7o=}r fΜAxJH$m_'s$r/H$mmT&WW1bTsRfR. $kh; L^xt=4L&K''f&Ϟ)KK`!-|jkkk^AѪd2YCk3% f7q8G&tiq8njii=SrvERՃ/GUG_*+8cϧ[\Tz#uuu:˗/}}>3zeR3X1cv3SSSٻ{[#w:z'~dsha5yM؆qcnayED"yDnԓzto&ĸ8aW_ 8s3Ǐ jm "H_PhouLzX:])y1З~h7o9{tӦM=o~,>H$ t- 0Y,= >Px괏òDʇ:P"hQ@]ͺ~^Ӹ\.hiQJz84NRd?1L}*rHHѤSLM._Vy7lQ#G\,+T~LC}q,gG{w-- Ԅ!?Jv}d.X9P\{*dZr%d2Y`(:]ͮ-'LтJbH#îԵB1W+:&Sꃵ.XlmCL/W\993?7OΔ1l0KrWq Sp:}:} Rgoll%ϧ(/:Zfz*]VR[Z\<6=qO: ':Fy-80|Vlfb| /͘> K|ƴ7:ʞQJmԔ#0yU˗^*,xZPi ŃbRFSO%Ӆ$G0fW%ߥ59*YhhHNMF,UKk^|!)%ak#ybd8`¡6>9c՚kj!@b lW['NC CŪQ(EB@PADPj-**T$$@Bx5oLr/aX7gssϽ$ٹ6[yy3ǟ>L^DGkCvw^1WuuMML޹\@ 1=G$RFǞj H)z02b8NIk 455H$қ;+q㴴\HM_iA Bε<̄3IbI$9紴 l޸*̿PFIϸ؋%6>cXgHmm5}fBUUUW%hgBgр[f_7|SK[w/=)n˖ Hi]_y[jm-fn=UWWyCcsg.0 Mן*,CndlBӋQ#G 4HT ޲9:&nBS?um%n0`E\ia:H Z@^ IDATI2s4>?yDwR70ӋMr$Wib2[/06wZm+Յ`, oY9⧋Joxe5]]|W紴 !ƦOLC]f9V7 =ރ_ԍ=ekHFjijvnXD䱨#%kaB5.nIOÌ+r?x8y$tCGtfǟ>;UoJѝ2yYq b&4b;5˥g`BVzPDܜOx:\SS@Н2k4ԡ{! OA>@bogFz@;0@ @;0@ @;0@ @;0@ @;0@  &zdiwVk=JD L] 'ee#/O=}Vz->|XEv=RRǙC ϞG=QZSSS]h+OʫRg7O2y D"Z魥y7N%-+'8aƎ1WUI`жwʿn\oJdrZF"+ G֬qɉ9 0a%Γ7op XcM_Zikk[6F5y1/;3|_i_ZTϹs%!@ 5xva99ϣWν{7ܫΟ;wڷodƍ#s\Lp8V?Z43-#Su4@ccӖ4E6QGOtOhK k546޽+746{UR tvr!ɍ=z m~jΛo,,m hutNȴ^Āj%1k6-06[r=@(/v\.ۄ ֖<~!'OL@ n7w6p#3A5+?5j6֖/^Moя gͬfVwii6"ZbؽΈf5dg0A#Ǣ} %D1DNs< )/e˽x-5+ENN.swu9*Zm "HY?f3fgN˻/sJJ.N iiX냜ikh`2YU5UD55UinxȨc#4QB6V|;+7sƴJYA:koPf͚y5;jv/~ uzm-]5-M)mJd:ddyakx<#9׮ǟN$iki!􎎎e^ǎ A6 PR"#,LC9;-svz(<"y\5 f\. ᡪhhKB=y!(++EczD?v[>h tɝC;#1`siv'EOjj8NWWW0u&)7ĞJpXl{=Ʒ  ?ҍW%7+ȃ؅$iiiɥ$A r(O"lv3n8$U0o2yDO ]Z}G Q'5IYhjh|3tȭ?M}o}=Ns= <T]p`?r40{̆Ƅ3I4d*+)͙ֆӉf&4*Jd1qm<^sssTfC.ܰÇuIȼii0Ꚛh¸\@ 1=G(zѱx<69)hJbXSRMM N曲a~۩!<5EOx;YDK S0x`s-K8$1Y3I @"Ιu8E 3Ȗd7nx ]]:@7PSScUg_)^*@;C>G rEV^^ތf|-K3Sab|eK Hif+wgE'SP" p?S:@ =DQ^.~>N'O$[jC O!@  h@  h@  h@ N7󋊞 :&)@ >N7q@hcFiitK@,o>ӝ2@C@>GHش, h@  h@  h`2VkJJ͚[} Ϧcp?\Y󫪪?E5hrfVv4Ew. ҝhҲ5d@KG(z|ϛ/4\ tCCc3/*/^l 432_mYO®xÕ272)z gϋ֬766::';PVV>rZLя/^ ʀjf%ZޗF ny72146[~W%%s%|Ubeڬ˽Yl6g7`\ ECS551o|,DLQ=)L洴 ;s̴ӦCB>]޾-s!2 y z9=%+#u m Su\A V1^ht1l崱cGo ѓ0*{0h%s.\Rmkk[6F5y1/;3|_ihR8_=.[/&'\޳3d„_PT=y붧 ;<\r^:kq$vn0n!Jd?G^ #ڟLҹu? E+.>555mmmS&Ommmͫ.Gº5dIwvDhT#"QǛSK_444k5%`>j //?o*l BQȱV̛K p8xn7w6p#3|UHg0lZ`lnz~9-#zrgϋo޾[cDq(Ts^;;be]{ZZh_:8axRWO%t_\.$:lwlHg'G{;2yܘѣ闬l fn>ϋ_`A]tOhK '19G`\O$*XSNĞ4Z~h:ڱ{b!bRWk?1fv7o>edbiac+ kYz894?C ']2+((茔,| 47" {RkUTVEǜ4 a_h[ZO=p8Nˌͬ774H1x =)=xC(_ͼxhԹazyy'E yٙ&ߵ瓛agk~(͊ʪʪ꤄ _9~2zeEeJZ3{766]HLHI>}'$ijh V *~Ę*{I鸼̉~ChBc7Eu)5?nFAatjάEΚ9®BbBnV*_oazε&EEPDˋaιvcV2%J"Hʪӑ\^3OCnw!& 7+t6YK2]f`ԅ-l˧㢩 9 /}=ݽw F.6%:5<|ڹH&E,˳IlY&&f]J=qajed14w"+}*@/4jg~-% ۻ_\\ gisUWAEA4grLioʐD Ey8n4:ݗ9xUwWxd999.j%D"NK`KҔԾ>^$IFs$;B`6^BQvPb1dj:TY,v+z6~Ȩc#4Q%{,lm|;+7sƴJ$eվ>H(={E*NW]=ԴZa9 EWA樇={qmAcFp8mm-Aڞ/XdeA3"_ v_'ǀMhx2geNGD>/~?T˨hki"Dji|>rsz>сBQSWW]CZzGG2 M@g0"Gh,Hv+4b l^u*!CZ &%{,TUTeeeXxnƍw{Gt:]W'_AA!>xDgw.'aþŰ/Y»ڹ-h\~{;DZQSSzmKm--_]~(@iS&OYA]] ` <Sd]&֐wΟW⥻J ry.cu--M993jIƷ WTz$ M0Zy`)eM o_mo[_O1#wyG~ }> Z҅p,ߏou9Q>'XH6y@hJϻ-"x9/,5''&ui_h]@ }Ob9/#d[_ 8G.uL&bS2&OؓPTT4Q#s8g k6ѯF*zѱx<69)v\.W ޞy% pL\B&@ꊊ4(&p8%1zQUU|Uܺz{gd^ᴴuuMM^1Whd=)cxHyMM 6Jo'oܼ8--Rӯt;x`s-13L`4wά9--wB7?ϮE)+Gw=BT,EYڬttojb>z\ · mrBW[bH x~k ss{Wg8ŦPMiTĄ ۱{o K"g̘/K_u7)P%8nޜV5d ;TQY)HI=H bh֪,-/6KE[[}ϊK"ƍٍfϝ=krw٫op,& $9.3zZG.I ԅ  SgG˫lނ+4![wG3VUQY睿8nԿ.ԝ< @No 嶎=**"|HDbG{࠶  ic6K}M"Cw{2i"+|#gƢS 8Cv!&XR@5Zܔ|qzHݞGGumY'?ovm N ]K$ Q3#ɓzh%?x(6DF=x)0K=lUI7zEUߤ'z/[C2R2r5;7,"XTH)/c}k].&'"} 'YRȝQFo _qkI$2 ͎?}VGsgϊkB:.[\vn'nnJF(WE{551)^FC]땺uoS gC7@;'@ h@  h@  h@  h@  hw`2VkJJ͚[} Ϧcp?\Y󫪪?E5tI"H;ekٌP7/06_hj6`SeUUI9f_~U_ hfmgdb2eKvb/b5ͬ fΉ>ԩLя/^ ʀjf%ZޗF ny72146[~W%%s%|Ubeڬ˽Yl6g7`\ ECS551o|,DLQ=C{r6ކ }[C dLSrzJVFAAvNϹR]ScbٜicǎCƾOXU]+V Qf^ :}~2.8=.[/&'\޳3d„_PT=y붧 ;<\r^:k$vn0n!Jd?G^ #ڟLҹu? E+.ېCaݚՎv;y"N4M5TXk|HAAA^^~ޜUx8Bj_^я`Աp8natFޚ IDAT ڀM 햸\/@_::edZ/^b@5s]y1R۷~kh6ܔj˫R{sS언,4ܶkOK Rg?/Y ഴ 33{ EDg0-n@hogC&3zA1y =hK`)`ai{B$&_4'#Ǣ} %D1D)$Fh:ڱ{b!bRWk?1fv7o>edbiac+ kYz894?C &kv8nyP[K?q2F](ZL&feL4Qƾ$9uTS1ȣbz6Q;Y{c/`wrΈW %x g dhJbXSRUUUW%ȭWLl|FNK QWs|&XLӝr<&knnN> Ho>쵮:z"[<r!5z~K &93$ &Hs̊:z"y'yʪ3Xtʛa~t.D"m\$ez͊NG&E|>@ |.H;-ճ҃@>'6-MM~k ]]UzSV{T3ǥn@RWWQ(!H~&&s_!csex<~{ĐtT]].\W.u]>]_y+^* ƎYu(eyCcsgL uuwWk-l9--ԅ lIKD3_d@5IA@t:fnc #p ШF~>odbabzFTD;%lb[IA `cddbZN"Cw?~RUɖlv?4RXxo Q$IR@5-lY!uV2:ht{3YߢPͬL,0@l+546=~d4;ٹW/W%nwoU}ǿl HM\ <3R`}k].&'v/ԔW8.ϺD 4zkK@2nO4I2Kqg$A-_NS.N.BNKKWsZ:Bѳȯq~aCOC yURcDDn64'Ret/[[ƍaO#G@kJ/&{ρ;**(Z{{z f2v[»֮C:⨣'JK_xjj핕>%e22YY?xy"&3kþ<{@UU WpTٲpƯZ=naI\I:/_m{ED&+**P445x<Ӳmg3e!f Oa2e_ءȃ'N/'' tyo2wO c'OШS2R ڶ P0UwJ~O4T/4|6rرhJe]J9~5;,$]Q~ߕ+͙ hnn^&[kYStFkTV Qf^ ڋ Gp8{oͺgLsyΐ AyRyR۞npsɽzJzs|W} )+;&tG >,WpÈfgyy_/4j //?o*_ϿӝD$jkk9ed^B $;t=iiMՓ#S2Y,1 FURjct|viiUUR_%K]=g/v\.MXܻ346346Nv6d@991G /Y:P<}ЃfT_ 錟6,l޽'ObkKs8r,`\O$*XSNĞBJb ?3YY,~MR.;,u6Kc,ΝOYhjifmwCQF&6 0˿iij^LY1tȐɓ']-1STtNJNc$LW6:vp'OYi]t) Щ=x1`dbjz.0|N 2ǙeLWPP)YÇhnn;4E6`֪9emiє[[[G |5roj]+(+N puqZS@v̈߃EN xCe} EjœG%+?)JLΜ4q=tu5u?ܬ ;[CRD17ObTLIK?}T4`Ʀ )g޿=z$$M >a2Y/^S%x=I:9q;CcAWhw(S.ƞ8ǭH@ (,7c>`TVU3?xi, p.$&fe\~lRTD E?q8XXm7n(Sr-8?y|a5s;?vb rBwnEq,mVѩ]ݞ F]˹|:.0ݻ{lobSS!çd[_^$IFs$;B`6^BQvPb1dj:TY,v+z6~Ȩc#4Q%{,lm|;+7sƴJ$eվ>H(={E*NW]=ԴZa9 EWEg7n޲-(p ?<lo/:%dǎ*U?_/ suum-M䣶h]-xk4Px~];M՛"oo'MM ;dij mT?7mو߁O8ho7`WVU<%''SFGBL~ch'F$'cM oz-e,u1HF`$ f7S(ʚ rϿZ +޾󷾞.bF>L4kkK±"?}{D؟c!YSI)=B,(LWְGîFZ?Gl &kv8^oݾ}-~dP;3LNM˘zlcm)L8p`ze@zjwcNJG)zC$eeN-D&FǞj r@gĈ+Y؅cx3F2VWTT4QFF!Fr8RVUU|Uܺz{gd^A~:QWs|&XLӝr<&knnN> Ho>l:z"[<r!5z~K &93$ &Hs̊:z"y'yʪ3Xtʛa~t.D"m\$ez͊NG&E|>@ |Pd%O ]KlA }O x&5됗is M|WO[)Z=GgwRx|HP FS}ڏL&Y9.;GN8dnf$L!v=jfmacش-xXML澰C4sNodbabzFTD;%lb[IA `cddbZN"Cw#߁Uɖlv?4RXx Q$IR@5-lY!uV2:ht{3YߢPͬL,ڗؔ"ɓz׭ {n ~RyUR);3W=ekHFjjvnXD䱨R^TW8]LNDvN˳.*;ZA'RVxKu5}I[zGtfǟ>+ BoJw乳gƉ?5QY_^.f;qow O?77u#+"|)SRZcWSS@Н2kK/{-wZV ;/- S)͸ @0elzㅌ3?S gEO h@  h@  h@  h@  h@ z ]Z{UR:mjS~6 r͚_UU)9ƗSOBA,NKKWs}y>Z:Bѳq~aCOC yURcDDn64'Ret/[[ƍaO#G@kZf2v[&{ρ;**(Z{{zyqx<55U{ENG,i,_<WWWW[|a_PTv=+q*lY?8y W7v$v$]ܗJ{Ww~ W" UT(<iٶ3Ǚ{Es_=4'g~mH{oߖ{Ӆ+2yĴKb{] f 6.XѩxqD銶Fۮ);-0679Ll-ɮ1|" 2ǙeLWPP)YÇhnn;4E6`֪9emiє$hPuLԼ:bp$k_aAzS6{mn߹BQyѨs)Ob3'Mk$]]M'7+:P6C*b'&~X)iϜt!1!%GOD"'L&Kc6'I3'Nag~ =h MpeJ֥GuIfLʪ9~:kv+ Bs8 Y|9׮>B-/O1,;rbۍ[)ʔ+v##*+OG:p-/_{͜>_b"݅ܬНd/u\"tEhtjW?Qv}r..4('41"|$JC Χvd[_K[S **N>+DgJ3~[V$R('p3׼2g<|=zyC,''rsW$H$"ilIBZRxH$͑Wh{{y ECz&5rTSSe؝vJK 7*_AH{#Dq챰Blp8**+VwWQ:#W#ttsGt:]WvdS(RjkjjÇ ('4QضH]>ȗ;og7n޲-(p/L! $K6"Xȱݸߍ RR"2={E*NW$Dlt0WPP9& ذoL |hvn 7E2N$IkjjX $cSh g AI$R?|8e#dxg~~>7&>z,s`_tZ#@.c5554e)''tF{b4I"6@SCo^+psY*K] =|& ZyXo ޾= c!YSI)=B,(LWְGîFZ?Gl <֑5O ,֯Aۑ?Bv*zuZq4BZd2,6;5-cMuZ mGm-)8ΔF=q2R[KONhB)G)zCZӝ,Z@LӍ=IrΈW#61LMiԃQL pJJ_c xuȼii0Ꚛb+zkbifiK5\D"liOTR)r-}!DR"֦PBȾ{kˮBTJ2K5-?7wfgF%13y93gE'X |\|{DuxSN]1M;hWaL8$04;z\!GA~+}?Hܟ:R?V_p7.dxBi|Fu܉W]$|@@\_@`Ey}?x_0~\ ugW &3uk !p$\EYY8X,KYsƌrrSQY}$S+TꆵW׭/,,2=sttڹ8{x̘5g!vWDt:o]ݝfkV,Pw;=΄iijzrYޑLa]g̬&Xz,铦5Wx[_TTdfe;s L&7*ѱ&[<;"GnR2~Q1q'[.Zw9xq۶l!0303mB_+RJ#X V `5;X V `5;X V-gᒖ*-#3k-U"lz>[jdl3,m̬4Y=4qyୡ/_3xg/^bL!b:v̚9ۋd`8a ˫obzoo~ѣ;^QuM4now:Bj;+Sm=gҕ4 {de54ڹ8;!~RX ޾z@Lܳ/:MMst'歡j>sBnu0Hv2wO݀uAE xy^Ǝҷשּׂp&%>fKWΜ ؉ _[[pr3gO^t6,$x0CF\.w{wQ#LN~ܖ$z[=g#.]Iۼ5tەgΟNo4gﳱ=}zCWfDizђ?('O9ttE+/h"\S jkkR֫N[x,%%kW'3cCJd}*++)Jk҄f&t:B;&7AQ544$=6c#B>_^LCGCs;x*xBocKl81d2L&wֵW迧 ,_5a[XzFfTgSgmM`b9k߽zӍoJlHFfc'M~/{FfN>c'Nۄlu2ukhxuu5^HE3xM-'Z644EruqvtgH$Ro]uB4*36S&X{{5AObq)V4*!g_ hT*`okjb| 'V󽂷UUAZ:Y<Ŝ8yi,f$NLbipvOw}B C$r;mgOԱSgΊ̖U5S8 &3g{I `\2]>AxCܮfc'즹L2q9$vGG[/XL |jSǎF1NC|燏 pxsqP6#1WnާS( Uϟkjjwmգ۷{VTTYqC`"CC /_c[TUU.=wb]:;hM[BtM E\Hujc^[r>}OJ=ѱrN:}(.(maN$phߵؙʪWߘS?~4PܵKg ^AUUT/I=[Ćn**.Iop9RbS6.{<1ʅB WI*Hdb_>wiԕkTUT?`g퐗).ЮW]FvE&W!+7%x",oVڮ&;lvܡ(a.![7a&2%6V}'" #Iǖ,EM{Lʁ=srnu*؉.,evI($pv^c?[%%%nhhX>hSO"v8hSpȔNQ1kjjw`9w9Fpq:k fﳳDUUzda ڂJRd2.^A@YYBx|e226G" 4MUU%%UU=wGUVV!5ԅPbK*W}_ |an"#ktXLކL&1,7/ZSS1˭W }czќ*<޸H$u9_UUEKSxʩ" n]"q5.xJk{"dN;P:Vƺ8"[c;^Q>zR\R2x"` k Ea0ܽR6V&Шԙ.3]]? m%%!v[vNRחJ/kll$ZUUEPNII)qΝYYXX8sTc#TK**.Ok뾡9^" d!eeBHEE!TZZ*FI*BOQQBC746XHPWSCQ( Z[>>I̪bg)Ӝ*<޸H$uDF]͙t:=>fDW~[].ڲV] aMׯ:dֺzׇ Dhkk9.YJƢ0 }{&v_o=b̬BS?pe+555B%:GW*Y56n 8~ܫo<'+()cvI$҉Mtt w%G|g o&b9*ZZ;u]; `!ت@Aassy _]}ws_M[H݇ox^lS8U"Ks^,4!hWs?BH[[kE|Eٿ|օWmRPYUz۸9l[w>.D33,{*6;TE ׅz>t{)2lafr :%8inr'IJ>NE =w?{^1q |~YyyDd̮hafkweUBff%E]]!&#=7&6>y.*.)l^,cj||,_[[dzwܽysy)Ӯ&ukh4װf&N d{~.А r>N$o egw_ͯB8`YqLY!ut4gWTT>IVWWGѺtīF ZG ^*P( ^|ǜ-ªت*f&b\<ڼW]d0Fn?AQu!$YY(+ B6 e#RAHʐ;rHTb5rZ+o 1UWWb-Y"vn.^>|y9'NۧtW*6hh9g[|_T\vt6n/45=f,\`2%&LDAP46=J*{s0x}5Y٪]Ln8Bf&TBtluA5u{ݣ[ HkGwto4v/W F4PQQ{`ϮPF޽*D%x2YhV!qV2:xuߣfV dovnFwrpnwy@jwˮ\/o췿;5uo5m!V`p[jkj~w2-),n sǒړE-k)ֵ˲c3_]>qݛd2o,鐗6o]˟vY?b ǷNyɯܽÜ;w(\>/U>tL^E9)q{rgQ4#svI$Vzs61Ӈwu|X)2Y 5ۦ;iF[~|q*j:vItttPWO%H&o2s][}:csa~n^}|0X"<.BQJʪuu˃#X6M[ǢvDPb7f>2wCFWRVmo-6Y{A߼RiT4^$5tطDkAѤi!ve_ga$H3u;AxC.:o^JI.F62&~XiGGUCj2.N~ɘN;t+BJ @C[G!/%̑?CU'-w_ ң±te5uW1'ƚZX&̒ãs/$'d/+gWlsEб]eŅᅤt5BhK-q'yo3(O3)KK^vocT6OZiw7O4!TQVR9¡7ԭH^][)sq-5[D~'L& .)8s J*5>yK_=`}HMB$O^ KE"Y+ެ#]Me)z\%6eEȟCFO@8G3&E#>U6<Si"?ďo3n;a;!4/t_o+;K$םM ZPba׆155!*dxQi+߻qi\!k;%I&H> 7| <.uCsG7*:zVKgw"L&~UO" RV\HЍ) ,vOMwZPbHښ7l< ]1񝿉C(GjhIj!ks++y\NƳN3T$SeY n߹+BˮBTԄTVUrn:)3`Vjg<{z87 J 1&XaZ9ʯl?C6_Ouҽ CKUѴE"ӕ]͙J*.)/)VVUnx4n -?>pM93_Ew[;L"4B:fSgĄ?reEiQa%&b5 j.{WQU^[װ}$RRmm߽~7ޫ?ʲR A uGHKM.-,;߀Qb|)GC8NGEyiQcccAbH %BDSxk Pb:>u^UO~mMccc]ߐ@)G|veE#dN OrӇw(!rga?Ͼ|jyʲRNWx3X]e+T \se<{w{mu5]P++}ty.d eq!!n_=hВC0\vձȒD+2 pHcLRU5 zFMS{iaihYM}E=4z˽xHsǙgwZ|m+}x\En=*RQ0a%]Q?<`x*ꩣ'b"d KUm"?4Wx.X/?T$U,K!h?{x쭌C⬐et4y/ =}(sܮCg%Ha7 Dz^ߢКHeObG]?2_ls<^KjN_},.|SeʃR,^^#-[۾Ң˼SQ}h-(ۺb~оDiOP[+ѝDR<=wuwA4KyyEi1BaN-io#.I744vw*?户})q{giKЇOnRWJmr)}xw(2SUIz0{j>otinromXC bhdlhd܄'Wj ph[JZy@t 5@jwy@jwy@jw~ EEL GWm޽Zpj~C BoB{޶X ? 2|aߣpR;X V `5;2TXX 92(Η@=g_dde5t 7  7h@jwypIENDB`gammaray-2.11.3/docs/manual/images/gammaray-mime-types.png000066400000000000000000002662561413310443000234530ustar00rootroot00000000000000PNG  IHDRXHMsBIT|d pHYs+ IDATxWpdKz/T`o}{g9K16B/\) zP(R`,Er\js]{ tUr  u._3?䟲@@ ;|@ .lV{]^7tlh4iDQ&el3:f|@ ^ş_''bna:t_N?WDx:9B J3fGP03;G"y ܼu㔗V@ `_c.]@<'x{>r&nܺgd~aH$Bkd,,:8N,558].YY]S[SZRP(YCt:bPł!NL& a,/HK*bvҔ pXCF)*ҢT*,_pP*ODc1PCoU@ `*ϿG*bjz֟TZ,flH&1M:,f3*Y‘m=s!Y)j24j jz}NVEQc47P(H&Nh5J %FZ- >d2Ja1)//hW'h‚tlFVjx<8Luu~g۸zL& Ep; @ )Z_@lFTr-aUH9z飳7\|i$@ ؚ] @ " @ ; @ c@ ;%JR&͢PTwdX,L&C@ 'rz,fiJ0M|mGC@ /%t, Z@ ]a-1C6}+F"Q.2Ɔ~=^z[Q?@ 7a  ɐd&,tl6K&!N#d(/+#)fz}LNMp:]<"v{k@ vM3cv;rT*Ň}BK޻OLF#Lt&C,Cv[0$fxF@o_?`6L\sLF&>JeևrLF:FP1f֊Ry`&zR7nFPކ}|}bɈB@&12:d2F:A34<\.Uzϑ'y{y1Z[kBN*BT1nܺ ,mM9|G.>L.l2NgP*sv*ɑ L&GB,/?Zm#Mtrj\%\0r mҵzz|~]̍r"rj0LJ(cqr\NhZ췘FTJ#{Y%pIc| y=_y F={[T*_X!&#/O?ʆh$Ju&9|>?})--G|DQZO>l6,gϜ1?)؟}~j1ܺ}d15=# ,Vfuko(M 066NE55q-Ν=d__g tEM7 կ\DkK 5ܾscG;*rP3#QQa%LQW[CEB!9}$--l6:PըjNwBlf۱Jd2uu18<%4J>x*TTTJ(F34HP(D HK6ᵫinjWp{tutP__˗P(8]K2,TVVbZih 6b6'O B<x3gN1;7O8&(v޻:;1L\rREK+FVYI<UvlJXS24p\Q3[}xv+;4X},5U  cD[l20ٱZ,XWJ0`G:q{r7_N'F:ctuu t,,:8rWJPF/sZ. Kp{X4L&vu_^P\G:QTVgm<"4xdzR4ssX-2L ! a^ł/ix<,f z-`#b]Cͼp֔{q  &AAd?&? COڂP\0